9#include "background_gfx.h"
33#define PLAY_HAND_BTN_PAL_IDX 6
34#define PLAY_HAND_BTN_BORDER_PAL_IDX 7
35#define DISCARD_BTN_PAL_IDX 13
36#define DISCARD_BTN_BORDER_PAL_IDX 8
37#define SORT_BTNS_PAL_IDX 9
38#define SORT_BY_RANK_BTN_BORDER_PAL_IDX 22
39#define SORT_BY_SUIT_BTN_BORDER_PAL_IDX 23
40#define BLIND_BG_SHADOW_PAL_IDX 5
41#define BLIND_BG_SECONDARY_PAL_IDX 18
42#define BLIND_BG_PRIMARY_PAL_IDX 19
45#define PITCH_STEP_DISCARD_SFX (-64)
46#define PITCH_STEP_DRAW_SFX 24
47#define PITCH_STEP_UNDISCARD_SFX (2 * PITCH_STEP_DRAW_SFX)
52#define NUM_SCORE_LERP_STEPS 16
53#define TM_SCORE_LERP_INTERVAL 2
55#define GAME_PLAYING_HAND_SEL_Y 1
58#define CARD_FOCUSED_UNSEL_Y 10
59#define CARD_UNFOCUSED_SEL_Y 15
60#define CARD_FOCUSED_SEL_Y 20
61#define SCORED_CARD_TEXT_Y 48
62#define JOKER_SCORE_TEXT_Y 48
63#define HELD_CARD_SCORE_TEXT_Y 108
64#define MAX_CARD_SCORE_STR_LEN 2
69#define SCORE_FLAMES_ANIM_FREQ 5
70#define NUM_SCORE_FLAMES_FRAMES 8
71#define SCORE_FLAME_FRAME_WIDTH 3
72static const Rect SCORE_FLAME_RESET = {26, 20, 28, 20};
73static const Rect SCORE_FLAME_FRAMES_START = {26, 21, 28, 21};
74static const BG_POINT SCORE_FLAME_CHIPS_POS = {1, 9};
75static const BG_POINT SCORE_FLAME_MULT_POS = {5, 9};
84static const BG_POINT TOP_LEFT_BLIND_TITLE_POINT = {0, 21};
85static const Rect BIG_BLIND_TITLE_SRC_RECT = {0, 26, 8, 26 };
86static const Rect BOSS_BLIND_TITLE_SRC_RECT = {0, 27, 8, 27 };
95static const Rect HAND_BG_RECT_SELECTING = {9, 11, 24, 17 };
96static const Rect HAND_SIZE_RECT_SELECT = {120, 128, 160, 136 };
97static const Rect HAND_SIZE_RECT_PLAYING = {120, 152, 160, 160 };
98static const Rect PLAYED_CARDS_SCORES_RECT = {72, 48, 240, 56 };
99static const Rect HELD_CARDS_SCORES_RECT = {72, 108, 240, 116 };
101static const BG_POINT CARD_DRAW_POS = {208, 110};
102static const BG_POINT CARD_DISCARD_PNT = {240, 70};
103static const BG_POINT HAND_START_POS = {120, 90};
104static const BG_POINT HAND_PLAY_POS = {120, 70};
111static int round_button_row_get_size(
void);
112static bool round_button_row_on_selection_changed(
115 const Selection* prev_selection,
116 const Selection* new_selection
118static void round_button_row_on_key_hit(
SelectionGrid* selection_grid, Selection* selection);
120static void round_hand_row_on_key_transit(
SelectionGrid* selection_grid, Selection* selection);
122static bool round_hand_row_on_selection_changed(
125 const Selection* prev_selection,
126 const Selection* new_selection
129static int round_hand_row_get_size(
void);
144 round_hand_row_get_size,
145 round_hand_row_on_selection_changed,
146 round_hand_row_on_key_transit,
151 round_button_row_get_size,
152 round_button_row_on_selection_changed,
153 round_button_row_on_key_hit,
159static const Selection GAME_PLAYING_INIT_SEL = {0, 1};
162 game_round_selection_rows,
164 GAME_PLAYING_INIT_SEL
181static Button game_round_buttons[] = {
198 PLAY_SCORING_CARD_JOKERS,
199 PLAY_SCORING_HELD_CARDS,
200 PLAY_SCORING_INDEPENDENT_JOKERS,
201 PLAY_SCORING_HAND_SCORED_END,
206static enum PlayState play_state = PLAY_STARTING;
213static const int HAND_SPACING_LUT[MAX_HAND_SIZE] =
214 {28, 28, 28, 28, 27, 21, 18, 15, 13, 12, 10, 9, 9, 8, 8, 7};
217static CardObject* s_played_hand[MAX_SELECTION_SIZE] = {NULL};
218static int s_played_top = -1;
220static Card* s_discard_pile[MAX_DECK_SIZE] = {NULL};
221static int s_discard_top = -1;
224static int s_scored_card_index = 0;
225static bool s_retrigger =
false;
228static ListItr s_joker_scored_itr;
229static ListItr s_joker_card_scored_end_itr;
230static ListItr s_joker_round_end_itr;
232static u32 s_temp_score = 0;
233static bool s_score_flames_active =
false;
234static FIXED s_lerped_score = 0;
235static FIXED s_lerped_temp_score = 0;
238static bool s_sound_played =
false;
239static bool s_discarded_card =
false;
241static int s_cards_drawn = 0;
242static int s_cards_discarded = 0;
255 return s_played_top + 1;
265 if (s_played_top >= MAX_SELECTION_SIZE - 1)
267 s_played_hand[++s_played_top] = card_object;
272 return s_discard_top;
284 if (s_discard_top >= MAX_DECK_SIZE - 1)
286 s_discard_pile[++s_discard_top] = card;
296 if (s_discard_top < 0)
298 return s_discard_pile[s_discard_top--];
303 return s_scored_card_index;
308 s_retrigger = new_retrigger;
321 for (
int i = 0; i < NUM_RANKS; i++)
323 for (
int i = 0; i < NUM_SUITS; i++)
326 for (
int i = 0; i <= s_played_top; i++)
332 if (!s_played_hand[i])
334 ranks_out[s_played_hand[i]->card->rank]++;
335 suits_out[s_played_hand[i]->card->suit]++;
346 REG_WIN0V = (REG_WIN0V << 8) | 0x80;
348 if (get_current_background() == BG_CARD_PLAYING)
352 &se_mem[MAIN_BG_SBB][SE_ROW_LEN * offset],
353 &background_gfxMap[SE_ROW_LEN * offset],
363 GRIT_CPY(pal_bg_mem, background_gfxPal);
364 GRIT_CPY(&tile8_mem[MAIN_BG_CBB], background_gfxTiles);
365 GRIT_CPY(&se_mem[MAIN_BG_SBB], background_gfxMap);
367 if (g_game_vars.current_blind ==
372 else if (g_game_vars.current_blind >= BLIND_TYPE_BOSS)
383 &pal_bg_mem[BLIND_BG_PRIMARY_PAL_IDX],
384 blind_get_color(g_game_vars.current_blind, BLIND_BACKGROUND_MAIN_COLOR_INDEX),
388 &pal_bg_mem[BLIND_BG_SECONDARY_PAL_IDX],
389 blind_get_color(g_game_vars.current_blind, BLIND_BACKGROUND_SECONDARY_COLOR_INDEX),
393 &pal_bg_mem[BLIND_BG_SHADOW_PAL_IDX],
394 blind_get_color(g_game_vars.current_blind, BLIND_BACKGROUND_SHADOW_COLOR_INDEX),
407 if (get_current_background() != BG_CARD_SELECTING)
412 REG_WIN0V = (REG_WIN0V << 8) | 0xA0;
415 for (
int i = 0; i <= 2; i++)
461 --g_game_vars.discards;
526static int round_hand_row_get_size(
void)
536static bool s_moving_card =
false;
540static bool s_card_moved_too_fast =
false;
541static bool s_card_selected_instead_of_moved =
false;
546static const int CARD_SWAP_TIME_THRESHOLD = 6;
547static int s_selection_hit_timer = UNDEFINED;
549static bool round_hand_row_on_selection_changed(
552 const Selection* prev_selection,
553 const Selection* new_selection
556 int prev_card_idx = UNDEFINED;
557 int next_card_idx = UNDEFINED;
560 s_card_moved_too_fast = (s_selection_hit_timer != UNDEFINED) &&
561 (g_game_vars.timer - s_selection_hit_timer) < CARD_SWAP_TIME_THRESHOLD;
563 if (prev_selection->y == GAME_PLAYING_HAND_SEL_Y)
568 if (new_selection->y == GAME_PLAYING_HAND_SEL_Y)
573 bool on_the_same_row = new_selection->y == prev_selection->y;
575 if (on_the_same_row && key_is_down(SELECT_CARD) && !s_card_moved_too_fast &&
576 !s_card_selected_instead_of_moved)
578 bool moved_by_one_tile = abs(new_selection->x - prev_selection->x) == 1;
581 if (!moved_by_one_tile)
589 s_moving_card =
true;
605 if (key_released(SELECT_CARD) || (s_card_moved_too_fast && !s_moving_card))
608 s_card_selected_instead_of_moved =
true;
610 if (next_card_idx != UNDEFINED)
626static void round_hand_row_on_key_transit(
SelectionGrid* selection_grid, Selection* selection)
628 if (key_hit(SELECT_CARD))
630 s_selection_hit_timer = g_game_vars.timer;
632 else if (key_released(SELECT_CARD))
634 if (!s_moving_card && !s_card_selected_instead_of_moved)
638 s_moving_card =
false;
639 s_card_moved_too_fast =
false;
640 s_card_selected_instead_of_moved =
false;
641 s_selection_hit_timer = UNDEFINED;
643 else if (key_hit(DESELECT_CARDS))
648 else if (key_hit(PLAY_HAND_KEY))
652 else if (key_hit(DISCARD_HAND_KEY))
662static int round_button_row_get_size(
void)
667static inline void round_button_set_highlight(
int btn_idx,
bool highlight)
672static bool round_button_row_on_selection_changed(
675 const Selection* prev_selection,
676 const Selection* new_selection
683 if (prev_selection->y == row_idx && prev_selection->x >= 0 &&
684 prev_selection->x < round_button_row_get_size())
686 round_button_set_highlight(prev_selection->x,
false);
689 if (new_selection->y == row_idx)
691 round_button_set_highlight(new_selection->x,
true);
697static void round_button_row_on_key_hit(
SelectionGrid* selection_grid, Selection* selection)
699 if (key_hit(SELECT_CARD))
719static inline void round_process_hand_select_input(
void)
730 enum GameState next_state = GAME_STATE_ROUND_END;
734 if (g_game_vars.current_blind > BLIND_TYPE_BIG)
736 if (g_game_vars.ante < MAX_ANTE)
746 next_state = GAME_STATE_WIN;
750 else if (g_game_vars.hands == 0)
752 next_state = GAME_STATE_LOSE;
755 game_change_state(next_state);
758static inline void card_in_hand_loop_handle_discard_and_shuffling(
774 if (card_object_is_selected(hand[card_idx]) ||
get_hand_state() == HAND_SHUFFLING)
776 if (!s_discarded_card)
778 *hand_x = int2fx(CARD_DISCARD_PNT.x);
779 *hand_y = int2fx(CARD_DISCARD_PNT.y);
788 s_sound_played =
true;
791 if (hand[card_idx]->x >= *hand_x)
796 card_object_destroy(&hand[card_idx]);
801 s_sound_played =
false;
802 g_game_vars.timer = TM_ZERO;
807 if (hand[card_idx] != NULL)
809 *hand_y = hand[card_idx]->y;
810 *hand_x = hand[card_idx]->x;
814 s_discarded_card =
true;
821 *hand_y -= int2fx(15);
825 *hand_y += int2fx(24);
827 *hand_x = *hand_x + (int2fx(card_idx) - int2fx(
get_hand_top()) / 2) *
833 *hand_x = *hand_x + (int2fx(card_idx) - int2fx(
get_hand_top()) / 2) *
837 if (card_idx == 0 && s_discarded_card ==
false && g_game_vars.timer % FRAMES(10) == 0)
842 s_sound_played =
false;
843 s_cards_discarded = 0;
845 g_game_vars.timer = TM_ZERO;
851static inline void select_flush_and_straight_cards_in_played_hand(
void)
854 bool final_selection[MAX_SELECTION_SIZE] = {
false};
857 int min_len = get_straight_and_flush_size();
863 bool flush_selection[MAX_HAND_SIZE] = {
false};
866 for (
int i = 0; i <= s_played_top; i++)
868 final_selection[i] = flush_selection[i];
876 bool straight_selection[MAX_HAND_SIZE] = {
false};
879 for (
int i = 0; i <= s_played_top; i++)
881 final_selection[i] = final_selection[i] || straight_selection[i];
889 for (
int i = 0; i <= s_played_top; i++)
891 if (final_selection[i])
893 card_object_set_selected(s_played_hand[i],
true);
898static inline void select_all_five_cards_in_played_hand(
void)
900 for (
int i = 0; i <= s_played_top; i++)
902 card_object_set_selected(s_played_hand[i],
true);
906static inline void select_four_of_a_kind_cards_in_played_hand(
void)
913 if (played_size >= 5)
915 int unmatched_index = -1;
917 for (
int i = 0; i <= played_size; i++)
919 if (s_played_hand[i]->card->rank != s_played_hand[(i + 1) % played_size]->card->rank &&
920 s_played_hand[i]->card->rank != s_played_hand[(i + 2) % played_size]->card->rank)
927 for (
int i = 0; i <= played_size; i++)
929 if (i != unmatched_index)
931 card_object_set_selected(s_played_hand[i],
true);
937 for (
int i = 0; i <= played_size; i++)
939 card_object_set_selected(s_played_hand[i],
true);
944static inline void select_three_of_a_kind_cards_in_played_hand(
void)
947 for (
int i = 0; i <= s_played_top - 1; i++)
949 for (
int j = i + 1; j <= s_played_top; j++)
951 if (s_played_hand[i]->card->rank == s_played_hand[j]->card->rank)
953 card_object_set_selected(s_played_hand[i],
true);
954 card_object_set_selected(s_played_hand[j],
true);
956 for (
int k = j + 1; k <= s_played_top; k++)
958 if (s_played_hand[i]->card->rank == s_played_hand[k]->card->rank &&
959 !card_object_is_selected(s_played_hand[k]))
961 card_object_set_selected(s_played_hand[k],
true);
970 if (card_object_is_selected(s_played_hand[i]))
975static inline void select_two_pair_cards_in_played_hand(
void)
980 for (i = 0; i <= s_played_top - 1; i++)
982 for (
int j = i + 1; j <= s_played_top; j++)
984 if (s_played_hand[i]->card->rank == s_played_hand[j]->card->rank)
986 card_object_set_selected(s_played_hand[i],
true);
987 card_object_set_selected(s_played_hand[j],
true);
993 if (card_object_is_selected(s_played_hand[i]))
997 for (; i <= s_played_top - 1; i++)
999 for (
int j = i + 1; j <= s_played_top; j++)
1001 if (s_played_hand[i]->card->rank == s_played_hand[j]->card->rank &&
1002 !card_object_is_selected(s_played_hand[i]) &&
1003 !card_object_is_selected(s_played_hand[j]))
1005 card_object_set_selected(s_played_hand[i],
true);
1006 card_object_set_selected(s_played_hand[j],
true);
1013static inline void select_pair_cards_in_played_hand(
void)
1016 for (
int i = 0; i <= s_played_top - 1; i++)
1018 for (
int j = i + 1; j <= s_played_top; j++)
1020 if (s_played_hand[i]->card->rank == s_played_hand[j]->card->rank)
1022 card_object_set_selected(s_played_hand[i],
true);
1023 card_object_set_selected(s_played_hand[j],
true);
1028 if (card_object_is_selected(s_played_hand[i]))
1033static inline void select_highcard_cards_in_played_hand(
void)
1036 int highest_rank_index = 0;
1038 for (
int i = 0; i <= s_played_top; i++)
1040 if (s_played_hand[i]->card->rank > s_played_hand[highest_rank_index]->card->rank)
1042 highest_rank_index = i;
1046 card_object_set_selected(s_played_hand[highest_rank_index],
true);
1056 return g_game_vars.hands == 0 ||
1060static inline void round_process_input_and_state(
void)
1069 g_game_vars.timer = TM_ZERO;
1074 round_process_hand_select_input();
1076 else if (play_state == PLAY_ENDING)
1078 if (g_game_vars.mult > 0)
1082 s_lerped_temp_score = int2fx(s_temp_score);
1083 s_lerped_score = int2fx(g_game_vars.score);
1085 if (s_temp_score > g_game_vars.best_hand_score)
1086 g_game_vars.best_hand_score = s_temp_score;
1088 display_temp_score(s_temp_score);
1090 g_game_vars.chips = 0;
1091 g_game_vars.mult = 0;
1095 static const int SCORE_CALC_SFX_PITCH_SHIFT = -102;
1096 static const int SCORE_CALC_SFX_VOLUME = 204;
1102 SCORE_CALC_SFX_VOLUME
1106 else if (play_state == PLAY_ENDED && g_game_vars.timer % FRAMES(TM_SCORE_LERP_INTERVAL) == 0)
1113 s_lerped_temp_score -= int2fx(s_temp_score * g_game_vars.game_speed) / NUM_SCORE_LERP_STEPS;
1114 s_lerped_score += int2fx(s_temp_score * g_game_vars.game_speed) / NUM_SCORE_LERP_STEPS;
1116 if (s_lerped_temp_score > 0)
1120 display_score(fx2uint(s_lerped_score));
1121 display_temp_score(fx2uint(s_lerped_temp_score));
1127 s_lerped_temp_score = 0;
1131 display_score(g_game_vars.score);
1142 if (get_deck_top() < 0 ||
get_hand_top() >= g_game_vars.hand_size - 1 ||
1146 CardObject* card_object = card_object_new(deck_pop());
1148 const FIXED deck_x = int2fx(CARD_DRAW_POS.x);
1149 const FIXED deck_y = int2fx(CARD_DRAW_POS.y);
1151 card_object->x = deck_x;
1152 card_object->y = deck_y;
1167static inline void round_process_card_draw(
void)
1169 if (
get_hand_state() == HAND_DRAW && s_cards_drawn < g_game_vars.hand_size)
1171 if (g_game_vars.timer % FRAMES(10) == 0)
1181 g_game_vars.timer = TM_ZERO;
1185static inline void round_discarded_cards_loop(
void)
1189 g_game_vars.timer > FRAMES(10))
1196 static CardObject* discarded_card_object = NULL;
1197 if (s_discard_top >= 0 && discarded_card_object == NULL)
1199 discarded_card_object = card_object_new(
discard_pop());
1202 card_object_set_sprite(discarded_card_object, 0);
1205 discarded_card_object->tx = int2fx(204);
1206 discarded_card_object->ty = int2fx(112);
1207 discarded_card_object->x = int2fx(240);
1208 discarded_card_object->y = int2fx(80);
1212 if (discarded_card_object->y >= discarded_card_object->ty)
1214 deck_push(discarded_card_object->card);
1215 card_object_destroy(&discarded_card_object);
1226 if (s_discard_top == -1 && discarded_card_object == NULL)
1234static inline void select_cards_in_played_hand(
void)
1241 select_highcard_cards_in_played_hand();
1244 select_pair_cards_in_played_hand();
1247 select_two_pair_cards_in_played_hand();
1249 case THREE_OF_A_KIND:
1250 select_three_of_a_kind_cards_in_played_hand();
1252 case FOUR_OF_A_KIND:
1253 select_four_of_a_kind_cards_in_played_hand();
1259 case STRAIGHT_FLUSH:
1262 select_flush_and_straight_cards_in_played_hand();
1266 case FIVE_OF_A_KIND:
1271 select_all_five_cards_in_played_hand();
1276static inline void cards_in_hand_update_loop(
void)
1286 if (hand[i] != NULL)
1288 FIXED hand_x = int2fx(HAND_START_POS.x);
1289 FIXED hand_y = int2fx(HAND_START_POS.y);
1297 hand_x = hand_x + (int2fx(i) - int2fx(
get_hand_top()) / 2) *
1302 (i == selected_card_idx &&
1303 game_round_selection_grid.selection.y == GAME_PLAYING_HAND_SEL_Y);
1305 if (is_focused && !card_object_is_selected(hand[i]))
1307 hand_y -= int2fx(CARD_FOCUSED_UNSEL_Y);
1309 else if (!is_focused && card_object_is_selected(hand[i]))
1311 hand_y -= int2fx(CARD_UNFOCUSED_SEL_Y);
1313 else if (is_focused && card_object_is_selected(hand[i]))
1315 hand_y -= int2fx(CARD_FOCUSED_SEL_Y);
1317 if (i != selected_card_idx && hand[i]->y > hand_y)
1319 hand[i]->y = hand_y;
1322 hand[i]->ty = hand_y;
1326 hand_x = hand_x + (int2fx(i) - int2fx(
get_hand_top()) / 2) *
1331 case HAND_SHUFFLING:
1335 card_in_hand_loop_handle_discard_and_shuffling(
1346 hand_x = hand_x + (int2fx(i) - int2fx(
get_hand_top()) / 2) *
1348 hand_y += int2fx(24);
1350 if (card_object_is_selected(hand[i]) && s_discarded_card ==
false &&
1351 g_game_vars.timer % FRAMES(10) == 0)
1353 card_object_set_selected(hand[i],
false);
1369 s_discarded_card =
true;
1372 if (i == 0 && s_discarded_card ==
false && g_game_vars.timer % FRAMES(10) == 0)
1377 g_game_vars.timer = TM_ZERO;
1380 select_cards_in_played_hand();
1386 hand_x = hand_x + (int2fx(i) - int2fx(
get_hand_top()) / 2) *
1388 hand_y += int2fx(24);
1392 hand[i]->tx = hand_x;
1393 hand[i]->ty = hand_y;
1398static inline void round_ui_text_update(
void)
1400 static int s_last_hand_size = UNDEFINED;
1401 static int s_last_hand_max_size = UNDEFINED;
1402 static int s_last_deck_size = UNDEFINED;
1405 s_last_hand_max_size != g_game_vars.hand_size || s_last_deck_size != deck_get_size())
1409 switch (get_current_background())
1411 case BG_CARD_SELECTING:
1413 "#{P:%d,%d; cx:0x%X000}%2d/%-2ld",
1414 HAND_SIZE_RECT_SELECT.left,
1415 HAND_SIZE_RECT_SELECT.top,
1418 g_game_vars.hand_size
1422 case BG_CARD_PLAYING:
1424 "#{P:%d,%d; cx:0x%X000}%2d/%-2ld",
1425 HAND_SIZE_RECT_PLAYING.left,
1426 HAND_SIZE_RECT_PLAYING.top,
1429 g_game_vars.hand_size
1438 display_deck_size_max();
1441 s_last_hand_max_size = g_game_vars.hand_size;
1442 s_last_deck_size = deck_get_size();
1450 if (curr_score >= required_score && !s_score_flames_active)
1453 s_score_flames_active =
true;
1456 if (curr_score < required_score && s_score_flames_active)
1459 s_score_flames_active =
false;
1461 Rect reset_rect = SCORE_FLAME_RESET;
1463 reset_rect.left += SCORE_FLAME_FRAME_WIDTH;
1464 reset_rect.right += SCORE_FLAME_FRAME_WIDTH;
1469static inline void round_process_flaming_score(
void)
1471 static u8 flame_score_frame = 0;
1473 if (s_score_flames_active)
1475 if (g_game_vars.timer % SCORE_FLAMES_ANIM_FREQ == 0)
1477 Rect frame_rect = SCORE_FLAME_FRAMES_START;
1478 flame_score_frame = (flame_score_frame + 1) % NUM_SCORE_FLAMES_FRAMES;
1481 frame_rect.top += flame_score_frame;
1482 frame_rect.bottom += flame_score_frame;
1486 frame_rect.left += SCORE_FLAME_FRAME_WIDTH;
1487 frame_rect.right += SCORE_FLAME_FRAME_WIDTH;
1497static void joker_scoring_increment_text_cursor(
int* cursor_pos_x)
1502 const int joker_score_display_offset_px = (MAX_CARD_SCORE_STR_LEN + 1) *
TTE_CHAR_SIZE;
1503 *cursor_pos_x += joker_score_display_offset_px;
1510static bool joker_object_score(
1513 enum JokerEvent joker_event
1519 u32 effect_flags_ret =
1520 joker_get_score_effect(joker_object->joker, card_object->card, joker_event, &joker_effect);
1522 if (effect_flags_ret == JOKER_EFFECT_FLAG_NONE)
1527 if (effect_flags_ret & JOKER_EFFECT_FLAG_RETRIGGER)
1534 if (joker_event == JOKER_EVENT_ON_CARD_HELD)
1538 cursorPosX += fx2int(card_object->x);
1539 cursorPosY = HELD_CARD_SCORE_TEXT_Y;
1543 cursorPosX += fx2int(joker_object->x);
1544 cursorPosY = JOKER_SCORE_TEXT_Y;
1548 if (effect_flags_ret & JOKER_EFFECT_FLAG_CHIPS)
1552 "#{P:%d,%d; cx:0x%X000;}+%lu",
1558 joker_scoring_increment_text_cursor(&cursorPosX);
1559 sfx_id = SFX_CHIPS_GENERIC;
1561 if (effect_flags_ret & JOKER_EFFECT_FLAG_MULT)
1565 "#{P:%d,%d; cx:0x%X000;}+%lu",
1571 joker_scoring_increment_text_cursor(&cursorPosX);
1575 if (effect_flags_ret & JOKER_EFFECT_FLAG_XMULT && joker_effect->xmult > 0)
1579 "#{P:%d,%d; cx:0x%X000;}X%lu",
1585 joker_scoring_increment_text_cursor(&cursorPosX);
1588 if (effect_flags_ret & JOKER_EFFECT_FLAG_MONEY)
1590 g_game_vars.money += joker_effect->money;
1592 "#{P:%d,%d; cx:0x%X000;}%d$",
1598 joker_scoring_increment_text_cursor(&cursorPosX);
1603 if (effect_flags_ret & JOKER_EFFECT_FLAG_MESSAGE)
1606 "#{P:%d,%d; cx:0x%X000;}%s",
1610 joker_effect->message
1614 if (effect_flags_ret & JOKER_EFFECT_FLAG_EXPIRE && joker_effect->expire)
1616 joker_object_shake(joker_object, UNDEFINED);
1625 joker_object_shake(joker_object, sfx_id);
1643 enum JokerEvent joker_event
1650 if (joker_object_score(joker, card_object, joker_event))
1658static inline void play_starting_played_cards_update(
int played_idx)
1661 bool card_selected =
1662 (s_played_top < s_scored_card_index)
1664 : card_object_is_selected(s_played_hand[s_played_top - s_scored_card_index]);
1666 if (played_idx == s_played_top && (g_game_vars.timer % FRAMES(10) == 0 || !card_selected) &&
1667 g_game_vars.timer > FRAMES(40))
1669 s_scored_card_index--;
1671 if (s_scored_card_index == 0)
1674 g_game_vars.timer = TM_ZERO;
1675 play_state = PLAY_BEFORE_SCORING;
1679 s_played_hand[played_idx]->tx =
1680 int2fx(HAND_PLAY_POS.x) +
1681 (int2fx(s_played_top - played_idx) - int2fx(s_played_top) / 2) * -27;
1682 s_played_hand[played_idx]->ty = int2fx(HAND_PLAY_POS.y);
1684 card_selected = card_object_is_selected(s_played_hand[played_idx]);
1685 if (card_selected && s_played_top - played_idx >= s_scored_card_index)
1687 s_played_hand[played_idx]->ty -= int2fx(10);
1706 play_state = PLAY_SCORING_CARDS;
1717 if (g_game_vars.timer % FRAMES(30) == 0 && g_game_vars.timer > FRAMES(40))
1722 while (s_scored_card_index <= s_played_top &&
1723 !card_object_is_selected(s_played_hand[s_scored_card_index]))
1725 s_scored_card_index++;
1729 if (s_scored_card_index > s_played_top)
1735 play_state = PLAY_SCORING_HELD_CARDS;
1742 CardObject* scored_card_object = s_played_hand[s_scored_card_index];
1744 if (card_object_is_selected(scored_card_object))
1747 tte_set_pos(fx2int(scored_card_object->x) +
TILE_SIZE, SCORED_CARD_TEXT_Y);
1750 tte_set_special(TTE_BLUE_PB * TTE_SPECIAL_PB_MULT_OFFSET);
1752 u8 card_value = card_get_value(scored_card_object->card);
1755 char score_buffer[INT_MAX_DIGITS + 2];
1756 snprintf(score_buffer,
sizeof(score_buffer),
"+%hhu", card_value);
1757 tte_write(score_buffer);
1759 card_object_shake(scored_card_object, SFX_CHIPS_CARD);
1770 play_state = PLAY_SCORING_CARD_JOKERS;
1786 if (g_game_vars.timer % FRAMES(30) == 0 && g_game_vars.timer > FRAMES(40))
1793 &s_joker_scored_itr,
1794 s_played_hand[s_scored_card_index],
1795 JOKER_EVENT_ON_CARD_SCORED
1804 &s_joker_card_scored_end_itr,
1805 s_played_hand[s_scored_card_index],
1806 JOKER_EVENT_ON_CARD_SCORED_END
1814 s_retrigger =
false;
1815 play_state = PLAY_SCORING_CARDS;
1821 s_scored_card_index++;
1822 play_state = PLAY_SCORING_CARDS;
1836 if (played_idx == 0 && (g_game_vars.timer % FRAMES(30) == 0) && g_game_vars.timer > FRAMES(40))
1843 for (; s_scored_card_index >= 0; s_scored_card_index--)
1846 &s_joker_scored_itr,
1847 hand[s_scored_card_index],
1848 JOKER_EVENT_ON_CARD_HELD
1851 card_object_shake(hand[s_scored_card_index], SFX_CARD_SELECT);
1857 s_scored_card_index = 0;
1859 play_state = PLAY_SCORING_INDEPENDENT_JOKERS;
1873 if (played_idx == 0 && (g_game_vars.timer % FRAMES(30) == 0) && g_game_vars.timer > FRAMES(40))
1886 play_state = PLAY_SCORING_HAND_SCORED_END;
1900 if (played_idx == 0 && (g_game_vars.timer % FRAMES(30) == 0) && g_game_vars.timer > FRAMES(40))
1906 &s_joker_round_end_itr,
1908 JOKER_EVENT_ON_HAND_SCORED_END
1916 g_game_vars.timer = TM_ZERO;
1917 play_state = PLAY_ENDING;
1932 bool card_selected =
1933 (s_played_top < s_scored_card_index)
1935 : card_object_is_selected(s_played_hand[s_played_top - s_scored_card_index]);
1937 if (played_idx == s_played_top && (g_game_vars.timer % FRAMES(10) == 0 || !card_selected) &&
1938 g_game_vars.timer > FRAMES(40))
1940 s_scored_card_index--;
1945 int static const CHIPS_ACCUM_SFX_PITCH_RATIO = 2;
1947 if (s_scored_card_index == 0)
1954 g_game_vars.timer = TM_ZERO;
1955 play_state = PLAY_ENDED;
1959 if (card_object_is_selected(s_played_hand[played_idx]) &&
1960 s_played_top - played_idx >= s_scored_card_index)
1962 s_played_hand[played_idx]->ty = int2fx(HAND_PLAY_POS.y);
1976 if (!s_discarded_card && g_game_vars.timer > FRAMES(40))
1979 if (!s_sound_played)
1986 s_sound_played =
true;
1990 if (s_played_hand[played_idx]->x >= int2fx(CARD_DISCARD_PNT.x))
1993 card_object_destroy(&s_played_hand[played_idx]);
1996 s_sound_played =
false;
1999 if (played_idx == s_played_top)
2010 play_state = PLAY_STARTING;
2014 s_scored_card_index = 0;
2016 g_game_vars.timer = TM_ZERO;
2023 s_played_hand[played_idx]->tx = int2fx(CARD_DISCARD_PNT.x);
2024 s_discarded_card =
true;
2030static inline void played_cards_update_loop(
void)
2035 for (
int played_idx = 0; played_idx <= s_played_top; played_idx++)
2037 if (s_played_hand[played_idx] == NULL)
2042 if (card_object_get_sprite(s_played_hand[played_idx]) == NULL)
2046 card_object_set_sprite(s_played_hand[played_idx], MAX_HAND_SIZE + played_idx);
2053 play_starting_played_cards_update(played_idx);
2056 case PLAY_BEFORE_SCORING:
2064 case PLAY_SCORING_CARDS:
2072 case PLAY_SCORING_CARD_JOKERS:
2080 case PLAY_SCORING_HELD_CARDS:
2088 case PLAY_SCORING_INDEPENDENT_JOKERS:
2096 case PLAY_SCORING_HAND_SCORED_END:
2122 s_played_hand[played_idx]->tscale = FIX_ONE;
2141 g_game_vars.current_blind,
2142 CUR_BLIND_TOKEN_POS.x,
2143 CUR_BLIND_TOKEN_POS.y,
2144 PLAYING_BLIND_TOKEN_LAYER
2153 g_game_vars.current_blind,
2156 ROUND_END_BLIND_TOKEN_LAYER
2159 if (g_game_vars.round_end_blind_token != NULL)
2164 Rect blind_req_text_rect = BLIND_REQ_TEXT_RECT;
2167 char blind_req_str_buff[UINT_MAX_DIGITS + 1];
2179 "#{P:%d,%d; cx:0x%X000}%s",
2180 blind_req_text_rect.left,
2181 blind_req_text_rect.top,
2186 "#{P:%d,%d; cx:0x%X000}$%d",
2187 BLIND_REWARD_RECT.left,
2188 BLIND_REWARD_RECT.top,
2201 game_round_selection_grid.selection = GAME_PLAYING_INIT_SEL;
2217 round_process_input_and_state();
2221 round_process_card_draw();
2223 round_discarded_cards_loop();
2225 s_discarded_card =
false;
2227 cards_in_hand_update_loop();
2228 played_cards_update_loop();
2230 round_ui_text_update();
2233 round_process_flaming_score();
Utilities for using maxmod to play sound effects.
#define MM_BASE_PITCH_RATE
The default pitch rate for the sound effect played.
void play_sfx(mm_word id, mm_word rate, mm_byte volume)
Play a sound effect, wrapper for mmEffectEx() See https://maxmod.org/ref/functions/mm_sound_effect....
#define SFX_DEFAULT_VOLUME
Default volume for sound effects.
u16 blind_get_color(enum BlindType type, enum BlindColorIndex index)
Get the color associated with a given Blind.
u32 blind_get_requirement(enum BlindType type, int ante)
Get the score required to beat a certain blind (either the Small, Big, or any Boss/Showdown blind) at...
Sprite * blind_token_new(enum BlindType type, int x, int y, enum BlindTokenLayers sprite_index)
Create a new BlindToken sprite.
void set_blind_beaten(enum BlindType type)
Remove the given Blind from the corresponding List so that we don't roll it again in the future.
int blind_get_reward(enum BlindType type)
Get the amount of money gained from beating a certain Blind.
Common functions to render UI elements.
void change_background(enum BackgroundId id, bool force_redraw)
Graphic utility functions.
void main_bg_se_move_rect_1_tile_vert(Rect se_rect, enum ScreenVertDir direction)
Moves a rect in the main background vertically in direction by a single tile.
void main_bg_se_copy_rect(Rect se_rect, BG_POINT dest_pos)
Copies a rect in the main background from se_rect to the position (x, y).
void toggle_windows(bool win0, bool win1)
Toggles the visibility of the window layers.
void update_text_rect_to_right_align_str(Rect *rect, const char *str, enum OverflowDir overflow_direction)
Changes rect->left so it fits the digits of num exactly when right aligned to rect->right....
#define TILE_SIZE
Tile size in pixels, both height and width as tiles are square.
#define TTE_CHAR_SIZE
By default TTE characters occupy a single tile.
void tte_erase_rect_wrapper(Rect rect)
A wrapper for tte_erase_rect that would use the rect struct.
INLINE int rect_width(const Rect *rect)
Get the width of a rectangle.
Functions relative to manipulating and analyzing the contents of the Hand, a.k.a. the Cards we hold a...
void set_hand_state(enum HandState)
Set the hand state. Primarily used by the GAME_PLAYING game state.
void hand_set_nb_selected_cards(int new_selections)
Set the current number of selected Cards.
int find_flush_in_played_cards(CardObject **played, int top, int min_len, bool *out_selection)
Finds the best flush (set of cards with the same suit) in the given array of played cards.
enum HandType get_hand_type(void)
Get the current hand type.
int get_hand_top(void)
Get the position in hand array of the last card obtained.
enum HandState get_hand_state(void)
Get the hand state.
void swap_cards_in_hand(int idx_a, int idx_b)
Swaps the order of two cards in hand.
int find_straight_in_played_cards(CardObject **played, int top, int min_len, bool *out_selection)
Finds the best straight in the given array of played cards.
CardObject ** get_hand_array(void)
Get the hand array of Cards currently held in hand.
int hand_get_nb_selected_cards(void)
Get the current number of selected Cards.
void select_paired_cards_in_hand(CardObject **played, int top, bool *selection)
This is used for the special case in "Four Fingers" where you can add a pair into a straight (e....
void reorder_card_sprites_layers(void)
Destroy the sprites of the Cards held in hand and recreate then in the same order as the Cards in the...
void set_hand_top(int new_hand_top)
Set the position in hand array of the last card obtained.
void hand_select_card(int index)
Set the card at a given index in Hand as selected.
void hand_change_sort(bool to_sort_by_suit)
Switch to the given sort method. Can sort playing cards in two ways: by rank and suit....
void compute_hand_value_info(void)
Determine the HandType and ContainedHandTypes of the currently selected Cards, then print the Hand's ...
void sort_cards(void)
Sort the hand array according to the selected method to do that.
void hand_deselect_all_cards(void)
Deselect all cards in hand.
int hand_nb_held_cards(void)
Get the current number of Cards in hand.
Functions relative to the handling of Jokers.
All the functions used specifically to interact with Jokers. This includes browsing,...
bool jokers_sel_row_on_selection_changed(SelectionGrid *selection_grid, int row_idx, const Selection *prev_selection, const Selection *new_selection)
Handle directional inputs on the Jokers' row at the top of the game screen.
void jokers_sel_row_on_key_transit(SelectionGrid *selection_grid, Selection *selection)
Handle button inputs on the Jokers' row at the top of the game screen.
int jokers_sel_row_get_size(void)
Returns the size of the Jokers' row at the top of the game screen. Is equal to the number of Jokers o...
Header of shared rects and points needed for ui.
void * list_itr_next(ListItr *itr)
void list_push_back(List *list, void *data)
ListItr list_itr_create(List *list)
u32 rng_get_u32(enum RngSequence key)
Get the next "randomly" generated number in the sequence corresponding to the given type.
static void card_draw(void)
Draw the next card at the top of the Deck and play a little Sprite animation to position it in our ha...
static void discard_push(Card *card)
Push a new card to discard at the end of the associated array.
static bool play_scoring_independent_jokers_update(int played_idx)
Score Jokers normally for event JOKER_EVENT_INDEPENDENT.
void round_change_background_playing(void)
Change to the round card playing background.
void round_on_init(void)
Round state initialization.
int get_scored_card_index(void)
Get the index of the card that is currently being scored.
static void played_push(CardObject *card_object)
Push a new card to play at the end of the associated array.
void round_on_update(void)
Round state update.
int get_discard_top(void)
Get the index of the last discarded card.
static bool play_before_scoring_cards_update(void)
Returns true if the Jokers scoring loop has returned early for event JOKER_EVENT_ON_HAND_PLAYED.
int get_played_size(void)
Get the number of cards played.
void round_change_background_selecting(void)
Change to the round card selection background.
static bool round_is_over(void)
Determines if the round is over, be it because we lost or won the round.
void set_retrigger(bool new_retrigger)
Set whether the current card should get triggered again.
static bool check_and_score_joker_for_event(ListItr *starting_joker_itr, CardObject *card_object, enum JokerEvent joker_event)
Iterate over the Jokers List until we encounter one that scores for the specified event.
static void play_ending_played_cards_update(int played_idx)
This is the reverse of PLAY_STARTING. The cards get reset back to their neutral position sequentially...
static void round_sort_by_suit_on_pressed(void)
"Suit" sorting button implementation.
static void round_play_hand_on_pressed(void)
"Play" button implementation.
static void round_execute_discard(void)
Triggers the discard of the currently selected cards in hand.
static Card * discard_pop()
Remove and recover the last discarded card.
void toggle_flaming_score(void)
Checks whether the score that would result from the current Chips and Mult exceeds the current Blind'...
static void round_execute_play_hand(void)
Triggers the playing of the currently selected cards in hand.
static bool can_play_hand(void)
Determines whether the "Play" button can be pressed or not.
static bool play_scoring_card_jokers_update(void)
Activate Jokers for event JOKER_EVENT_ON_CARD_SCORED_END for the previous scored card,...
static bool can_discard_hand(void)
Determines whether the "Discard" button can be pressed or not.
static bool play_scoring_held_cards_update(int played_idx)
Activate Jokers for event JOKER_EVENT_ON_CARD_HELD for the previous scored card, if any.
int get_played_top(void)
Get the index of the last played card.
static int hand_sel_idx_to_card_idx(int selection_index)
Converts a selection index from the selection grid into a card index within the hand array.
static bool play_scoring_hand_scored_end_update(int played_idx)
Trigger all Jokers for event JOKER_EVENT_ON_HAND_SCORED_END once they are done scoring.
static GBAL_UNUSED void get_played_distribution(u8 ranks_out[NUM_RANKS], u8 suits_out[NUM_SUITS])
Outputs the distribution of ranks and suits in the played stack.
static void round_sort_by_rank_on_pressed(void)
"Rank" sorting button implementation.
static void round_discard_on_pressed(void)
"Discard" button implementation.
static void round_handle_round_over(void)
Evaluates if we have won or lost when we can no longer play so that we land on the correct Game Over ...
static bool play_scoring_cards_update(void)
Score all played cards in order, then Jokers after each one.
static bool play_ended_played_cards_update(int played_idx)
Returns true if the card at index played_idx has been discarded. Basically a copy of HAND_DISCARD.
API relative to the Rounds we play.
An implementation for a selection grid that handles directional selection.
void selection_grid_move_selection_vert(SelectionGrid *selection_grid, int direction_tribool)
Moves the selection vertically within the selection grid.
void selection_grid_process_input(SelectionGrid *selection_grid)
Processes user input for the selection grid.
Data structures and functions related to the handling of Skip Tags.
void skip_tag_process_start(enum SkipTagEvent checked_tag_event)
Start Skip Tag processing for the specified event.
enum SkipTagProcStage skip_tag_process_get_proc_stage(void)
Recover the current stage of the Tag processing.
void sprite_object_reset_transform(SpriteObject *sprite_object)
Reset SpriteObject's transform back to default values.
void sprite_destroy(Sprite **sprite)
Destroy Sprite.
void sprite_hide(Sprite *sprite)
Hides the sprite by manipulating ATTR0_HIDE in OAM.
The core selection grid struct, represents the grid itself and its state.
A sprite object is a sprite that is focusable and movable in animation.
Utilities relating around number string representation and protected arithmatic helper functions.
uint32_t u32_protected_add(uint32_t a, uint32_t b)
Avoid overflow when adding two u32 integers.
#define NUM_ELEM_IN_ARR(arr)
Get the number of elements in an array.
#define GBAL_RETURN_IF_NULL(param, ret_val)
Returns ret_val and prints a default error message if param is equal to NULL. Useful for checking arg...
void truncate_uint_to_suffixed_str(uint32_t num, int num_req_chars, char out_str_buff[UINT_MAX_DIGITS+1])
Truncate an unsigned number into a suffixed string representation e.g. 12000 -> "12K" The least signi...
#define GBAL_UNUSED
Wrapper around the "unused" attribute.
#define RET_NONE
An empty return value for RETURN_IF macros when used in void functions Expands to nothing because mac...
uint32_t u32_protected_mult(uint32_t a, uint32_t b)
Avoid overflow when multiplying two u32 integers.