9#include "background_gfx.h"
31#define PLAY_HAND_BTN_PAL_IDX 6
32#define PLAY_HAND_BTN_BORDER_PAL_IDX 7
33#define DISCARD_BTN_PAL_IDX 13
34#define DISCARD_BTN_BORDER_PAL_IDX 8
35#define SORT_BTNS_PAL_IDX 9
36#define SORT_BY_RANK_BTN_BORDER_PAL_IDX 22
37#define SORT_BY_SUIT_BTN_BORDER_PAL_IDX 23
38#define BLIND_BG_SHADOW_PAL_IDX 5
39#define BLIND_BG_SECONDARY_PAL_IDX 18
40#define BLIND_BG_PRIMARY_PAL_IDX 19
43#define PITCH_STEP_DISCARD_SFX (-64)
44#define PITCH_STEP_DRAW_SFX 24
45#define PITCH_STEP_UNDISCARD_SFX (2 * PITCH_STEP_DRAW_SFX)
50#define NUM_SCORE_LERP_STEPS 16
51#define TM_SCORE_LERP_INTERVAL 2
53#define GAME_PLAYING_HAND_SEL_Y 1
56#define CARD_FOCUSED_UNSEL_Y 10
57#define CARD_UNFOCUSED_SEL_Y 15
58#define CARD_FOCUSED_SEL_Y 20
59#define SCORED_CARD_TEXT_Y 48
64#define SCORE_FLAMES_ANIM_FREQ 5
65#define NUM_SCORE_FLAMES_FRAMES 8
66#define SCORE_FLAME_FRAME_WIDTH 3
67static const Rect SCORE_FLAME_RESET = {26, 20, 28, 20};
68static const Rect SCORE_FLAME_FRAMES_START = {26, 21, 28, 21};
69static const BG_POINT SCORE_FLAME_CHIPS_POS = {1, 9};
70static const BG_POINT SCORE_FLAME_MULT_POS = {5, 9};
79static const BG_POINT TOP_LEFT_BLIND_TITLE_POINT = {0, 21};
80static const Rect BIG_BLIND_TITLE_SRC_RECT = {0, 26, 8, 26 };
81static const Rect BOSS_BLIND_TITLE_SRC_RECT = {0, 27, 8, 27 };
90static const Rect HAND_BG_RECT_SELECTING = {9, 11, 24, 17 };
91static const Rect HAND_SIZE_RECT_SELECT = {120, 128, 160, 136 };
92static const Rect HAND_SIZE_RECT_PLAYING = {120, 152, 160, 160 };
93static const Rect PLAYED_CARDS_SCORES_RECT = {72, 48, 240, 56 };
94static const Rect HELD_CARDS_SCORES_RECT = {72, 108, 240, 116 };
96static const BG_POINT CARD_DRAW_POS = {208, 110};
97static const BG_POINT CARD_DISCARD_PNT = {240, 70};
98static const BG_POINT HAND_START_POS = {120, 90};
99static const BG_POINT HAND_PLAY_POS = {120, 70};
106static int game_round_button_row_get_size(
void);
107static bool game_round_button_row_on_selection_changed(
110 const Selection* prev_selection,
111 const Selection* new_selection
113static void game_round_button_row_on_key_hit(
SelectionGrid* selection_grid, Selection* selection);
115static void game_round_hand_row_on_key_transit(
SelectionGrid* selection_grid, Selection* selection);
117static bool game_round_hand_row_on_selection_changed(
120 const Selection* prev_selection,
121 const Selection* new_selection
124static int game_round_hand_row_get_size(
void);
139 game_round_hand_row_get_size,
140 game_round_hand_row_on_selection_changed,
141 game_round_hand_row_on_key_transit,
146 game_round_button_row_get_size,
147 game_round_button_row_on_selection_changed,
148 game_round_button_row_on_key_hit,
154static const Selection GAME_PLAYING_INIT_SEL = {0, 1};
157 game_round_selection_rows,
159 GAME_PLAYING_INIT_SEL
176static Button game_round_buttons[] = {
193 PLAY_SCORING_CARD_JOKERS,
194 PLAY_SCORING_HELD_CARDS,
195 PLAY_SCORING_INDEPENDENT_JOKERS,
196 PLAY_SCORING_HAND_SCORED_END,
201static enum PlayState play_state = PLAY_STARTING;
208static const int HAND_SPACING_LUT[MAX_HAND_SIZE] =
209 {28, 28, 28, 28, 27, 21, 18, 15, 13, 12, 10, 9, 9, 8, 8, 7};
212static CardObject* s_played_hand[MAX_SELECTION_SIZE] = {NULL};
213static int s_played_top = -1;
215static Card* s_discard_pile[MAX_DECK_SIZE] = {NULL};
216static int s_discard_top = -1;
219static int s_scored_card_index = 0;
220static bool s_retrigger =
false;
223static ListItr s_joker_scored_itr;
224static ListItr s_joker_card_scored_end_itr;
225static ListItr s_joker_round_end_itr;
227static u32 s_temp_score = 0;
228static bool s_score_flames_active =
false;
229static FIXED s_lerped_score = 0;
230static FIXED s_lerped_temp_score = 0;
233static bool s_sound_played =
false;
234static bool s_discarded_card =
false;
236static int s_cards_drawn = 0;
237static int s_cards_discarded = 0;
250 return s_played_top + 1;
260 if (s_played_top >= MAX_SELECTION_SIZE - 1)
262 s_played_hand[++s_played_top] = card_object;
267 return s_discard_top;
279 if (s_discard_top >= MAX_DECK_SIZE - 1)
281 s_discard_pile[++s_discard_top] = card;
291 if (s_discard_top < 0)
293 return s_discard_pile[s_discard_top--];
298 return s_scored_card_index;
303 s_retrigger = new_retrigger;
316 for (
int i = 0; i < NUM_RANKS; i++)
318 for (
int i = 0; i < NUM_SUITS; i++)
321 for (
int i = 0; i <= s_played_top; i++)
327 if (!s_played_hand[i])
329 ranks_out[s_played_hand[i]->card->rank]++;
330 suits_out[s_played_hand[i]->card->suit]++;
341 REG_WIN0V = (REG_WIN0V << 8) | 0x80;
343 if (get_current_background() == BG_CARD_PLAYING)
347 &se_mem[MAIN_BG_SBB][SE_ROW_LEN * offset],
348 &background_gfxMap[SE_ROW_LEN * offset],
358 GRIT_CPY(pal_bg_mem, background_gfxPal);
359 GRIT_CPY(&tile8_mem[MAIN_BG_CBB], background_gfxTiles);
360 GRIT_CPY(&se_mem[MAIN_BG_SBB], background_gfxMap);
362 if (g_game_vars.current_blind ==
367 else if (g_game_vars.current_blind >= BLIND_TYPE_BOSS)
378 &pal_bg_mem[BLIND_BG_PRIMARY_PAL_IDX],
379 blind_get_color(g_game_vars.current_blind, BLIND_BACKGROUND_MAIN_COLOR_INDEX),
383 &pal_bg_mem[BLIND_BG_SECONDARY_PAL_IDX],
384 blind_get_color(g_game_vars.current_blind, BLIND_BACKGROUND_SECONDARY_COLOR_INDEX),
388 &pal_bg_mem[BLIND_BG_SHADOW_PAL_IDX],
389 blind_get_color(g_game_vars.current_blind, BLIND_BACKGROUND_SHADOW_COLOR_INDEX),
402 if (get_current_background() != BG_CARD_SELECTING)
407 REG_WIN0V = (REG_WIN0V << 8) | 0xA0;
410 for (
int i = 0; i <= 2; i++)
456 --g_game_vars.discards;
521static int game_round_hand_row_get_size(
void)
531static bool s_moving_card =
false;
535static bool s_card_moved_too_fast =
false;
536static bool s_card_selected_instead_of_moved =
false;
541static const int CARD_SWAP_TIME_THRESHOLD = 6;
542static int s_selection_hit_timer = UNDEFINED;
544static bool game_round_hand_row_on_selection_changed(
547 const Selection* prev_selection,
548 const Selection* new_selection
551 int prev_card_idx = UNDEFINED;
552 int next_card_idx = UNDEFINED;
555 s_card_moved_too_fast = (s_selection_hit_timer != UNDEFINED) &&
556 (g_game_vars.timer - s_selection_hit_timer) < CARD_SWAP_TIME_THRESHOLD;
558 if (prev_selection->y == GAME_PLAYING_HAND_SEL_Y)
563 if (new_selection->y == GAME_PLAYING_HAND_SEL_Y)
568 bool on_the_same_row = new_selection->y == prev_selection->y;
570 if (on_the_same_row && key_is_down(SELECT_CARD) && !s_card_moved_too_fast &&
571 !s_card_selected_instead_of_moved)
573 bool moved_by_one_tile = abs(new_selection->x - prev_selection->x) == 1;
576 if (!moved_by_one_tile)
584 s_moving_card =
true;
600 if (key_released(SELECT_CARD) || (s_card_moved_too_fast && !s_moving_card))
603 s_card_selected_instead_of_moved =
true;
605 if (next_card_idx != UNDEFINED)
621static void game_round_hand_row_on_key_transit(
SelectionGrid* selection_grid, Selection* selection)
623 if (key_hit(SELECT_CARD))
625 s_selection_hit_timer = g_game_vars.timer;
627 else if (key_released(SELECT_CARD))
629 if (!s_moving_card && !s_card_selected_instead_of_moved)
633 s_moving_card =
false;
634 s_card_moved_too_fast =
false;
635 s_card_selected_instead_of_moved =
false;
636 s_selection_hit_timer = UNDEFINED;
638 else if (key_hit(DESELECT_CARDS))
643 else if (key_hit(PLAY_HAND_KEY))
647 else if (key_hit(DISCARD_HAND_KEY))
657static int game_round_button_row_get_size(
void)
662static inline void game_round_button_set_highlight(
int btn_idx,
bool highlight)
667static bool game_round_button_row_on_selection_changed(
670 const Selection* prev_selection,
671 const Selection* new_selection
678 if (prev_selection->y == row_idx && prev_selection->x >= 0 &&
679 prev_selection->x < game_round_button_row_get_size())
681 game_round_button_set_highlight(prev_selection->x,
false);
684 if (new_selection->y == row_idx)
686 game_round_button_set_highlight(new_selection->x,
true);
692static void game_round_button_row_on_key_hit(
SelectionGrid* selection_grid, Selection* selection)
694 if (key_hit(SELECT_CARD))
714static inline void game_round_process_hand_select_input(
void)
725 enum GameState next_state = GAME_STATE_ROUND_END;
729 if (g_game_vars.current_blind > BLIND_TYPE_BIG)
731 if (g_game_vars.ante < MAX_ANTE)
741 next_state = GAME_STATE_WIN;
745 else if (g_game_vars.hands == 0)
747 next_state = GAME_STATE_LOSE;
750 game_change_state(next_state);
753static inline void card_in_hand_loop_handle_discard_and_shuffling(
769 if (card_object_is_selected(hand[card_idx]) ||
get_hand_state() == HAND_SHUFFLING)
771 if (!s_discarded_card)
773 *hand_x = int2fx(CARD_DISCARD_PNT.x);
774 *hand_y = int2fx(CARD_DISCARD_PNT.y);
783 s_sound_played =
true;
786 if (hand[card_idx]->x >= *hand_x)
791 card_object_destroy(&hand[card_idx]);
796 s_sound_played =
false;
797 g_game_vars.timer = TM_ZERO;
802 if (hand[card_idx] != NULL)
804 *hand_y = hand[card_idx]->y;
805 *hand_x = hand[card_idx]->x;
809 s_discarded_card =
true;
816 *hand_y -= int2fx(15);
820 *hand_y += int2fx(24);
822 *hand_x = *hand_x + (int2fx(card_idx) - int2fx(
get_hand_top()) / 2) *
828 *hand_x = *hand_x + (int2fx(card_idx) - int2fx(
get_hand_top()) / 2) *
832 if (card_idx == 0 && s_discarded_card ==
false && g_game_vars.timer % FRAMES(10) == 0)
837 s_sound_played =
false;
838 s_cards_discarded = 0;
840 g_game_vars.timer = TM_ZERO;
846static inline void select_flush_and_straight_cards_in_played_hand(
void)
849 bool final_selection[MAX_SELECTION_SIZE] = {
false};
852 int min_len = get_straight_and_flush_size();
858 bool flush_selection[MAX_HAND_SIZE] = {
false};
861 for (
int i = 0; i <= s_played_top; i++)
863 final_selection[i] = flush_selection[i];
871 bool straight_selection[MAX_HAND_SIZE] = {
false};
874 for (
int i = 0; i <= s_played_top; i++)
876 final_selection[i] = final_selection[i] || straight_selection[i];
884 for (
int i = 0; i <= s_played_top; i++)
886 if (final_selection[i])
888 card_object_set_selected(s_played_hand[i],
true);
893static inline void select_all_five_cards_in_played_hand(
void)
895 for (
int i = 0; i <= s_played_top; i++)
897 card_object_set_selected(s_played_hand[i],
true);
901static inline void select_four_of_a_kind_cards_in_played_hand(
void)
908 if (played_size >= 5)
910 int unmatched_index = -1;
912 for (
int i = 0; i <= played_size; i++)
914 if (s_played_hand[i]->card->rank != s_played_hand[(i + 1) % played_size]->card->rank &&
915 s_played_hand[i]->card->rank != s_played_hand[(i + 2) % played_size]->card->rank)
922 for (
int i = 0; i <= played_size; i++)
924 if (i != unmatched_index)
926 card_object_set_selected(s_played_hand[i],
true);
932 for (
int i = 0; i <= played_size; i++)
934 card_object_set_selected(s_played_hand[i],
true);
939static inline void select_three_of_a_kind_cards_in_played_hand(
void)
942 for (
int i = 0; i <= s_played_top - 1; i++)
944 for (
int j = i + 1; j <= s_played_top; j++)
946 if (s_played_hand[i]->card->rank == s_played_hand[j]->card->rank)
948 card_object_set_selected(s_played_hand[i],
true);
949 card_object_set_selected(s_played_hand[j],
true);
951 for (
int k = j + 1; k <= s_played_top; k++)
953 if (s_played_hand[i]->card->rank == s_played_hand[k]->card->rank &&
954 !card_object_is_selected(s_played_hand[k]))
956 card_object_set_selected(s_played_hand[k],
true);
965 if (card_object_is_selected(s_played_hand[i]))
970static inline void select_two_pair_cards_in_played_hand(
void)
975 for (i = 0; i <= s_played_top - 1; i++)
977 for (
int j = i + 1; j <= s_played_top; j++)
979 if (s_played_hand[i]->card->rank == s_played_hand[j]->card->rank)
981 card_object_set_selected(s_played_hand[i],
true);
982 card_object_set_selected(s_played_hand[j],
true);
988 if (card_object_is_selected(s_played_hand[i]))
992 for (; i <= s_played_top - 1; i++)
994 for (
int j = i + 1; j <= s_played_top; j++)
996 if (s_played_hand[i]->card->rank == s_played_hand[j]->card->rank &&
997 !card_object_is_selected(s_played_hand[i]) &&
998 !card_object_is_selected(s_played_hand[j]))
1000 card_object_set_selected(s_played_hand[i],
true);
1001 card_object_set_selected(s_played_hand[j],
true);
1008static inline void select_pair_cards_in_played_hand(
void)
1011 for (
int i = 0; i <= s_played_top - 1; i++)
1013 for (
int j = i + 1; j <= s_played_top; j++)
1015 if (s_played_hand[i]->card->rank == s_played_hand[j]->card->rank)
1017 card_object_set_selected(s_played_hand[i],
true);
1018 card_object_set_selected(s_played_hand[j],
true);
1023 if (card_object_is_selected(s_played_hand[i]))
1028static inline void select_highcard_cards_in_played_hand(
void)
1031 int highest_rank_index = 0;
1033 for (
int i = 0; i <= s_played_top; i++)
1035 if (s_played_hand[i]->card->rank > s_played_hand[highest_rank_index]->card->rank)
1037 highest_rank_index = i;
1041 card_object_set_selected(s_played_hand[highest_rank_index],
true);
1051 return g_game_vars.hands == 0 ||
1055static inline void game_round_process_input_and_state(
void)
1059 game_round_process_hand_select_input();
1061 else if (play_state == PLAY_ENDING)
1063 if (g_game_vars.mult > 0)
1067 s_lerped_temp_score = int2fx(s_temp_score);
1068 s_lerped_score = int2fx(g_game_vars.score);
1070 if (s_temp_score > g_game_vars.best_hand_score)
1071 g_game_vars.best_hand_score = s_temp_score;
1073 display_temp_score(s_temp_score);
1075 g_game_vars.chips = 0;
1076 g_game_vars.mult = 0;
1080 static const int SCORE_CALC_SFX_PITCH_SHIFT = -102;
1081 static const int SCORE_CALC_SFX_VOLUME = 204;
1087 SCORE_CALC_SFX_VOLUME
1091 else if (play_state == PLAY_ENDED && g_game_vars.timer % FRAMES(TM_SCORE_LERP_INTERVAL) == 0)
1098 s_lerped_temp_score -= int2fx(s_temp_score * g_game_vars.game_speed) / NUM_SCORE_LERP_STEPS;
1099 s_lerped_score += int2fx(s_temp_score * g_game_vars.game_speed) / NUM_SCORE_LERP_STEPS;
1101 if (s_lerped_temp_score > 0)
1105 display_score(fx2uint(s_lerped_score));
1106 display_temp_score(fx2uint(s_lerped_temp_score));
1112 s_lerped_temp_score = 0;
1116 display_score(g_game_vars.score);
1127 if (get_deck_top() < 0 ||
get_hand_top() >= g_game_vars.hand_size - 1 ||
1131 CardObject* card_object = card_object_new(deck_pop());
1133 const FIXED deck_x = int2fx(CARD_DRAW_POS.x);
1134 const FIXED deck_y = int2fx(CARD_DRAW_POS.y);
1136 card_object->x = deck_x;
1137 card_object->y = deck_y;
1152static inline void game_round_process_card_draw(
void)
1154 if (
get_hand_state() == HAND_DRAW && s_cards_drawn < g_game_vars.hand_size)
1156 if (g_game_vars.timer % FRAMES(10) == 0)
1166 g_game_vars.timer = TM_ZERO;
1170static inline void game_round_discarded_cards_loop(
void)
1174 g_game_vars.timer > FRAMES(10))
1181 static CardObject* discarded_card_object = NULL;
1182 if (s_discard_top >= 0 && discarded_card_object == NULL)
1184 discarded_card_object = card_object_new(
discard_pop());
1187 card_object_set_sprite(discarded_card_object, 0);
1190 discarded_card_object->tx = int2fx(204);
1191 discarded_card_object->ty = int2fx(112);
1192 discarded_card_object->x = int2fx(240);
1193 discarded_card_object->y = int2fx(80);
1197 if (discarded_card_object->y >= discarded_card_object->ty)
1199 deck_push(discarded_card_object->card);
1200 card_object_destroy(&discarded_card_object);
1211 if (s_discard_top == -1 && discarded_card_object == NULL)
1219static inline void select_cards_in_played_hand(
void)
1226 select_highcard_cards_in_played_hand();
1229 select_pair_cards_in_played_hand();
1232 select_two_pair_cards_in_played_hand();
1234 case THREE_OF_A_KIND:
1235 select_three_of_a_kind_cards_in_played_hand();
1237 case FOUR_OF_A_KIND:
1238 select_four_of_a_kind_cards_in_played_hand();
1244 case STRAIGHT_FLUSH:
1247 select_flush_and_straight_cards_in_played_hand();
1251 case FIVE_OF_A_KIND:
1256 select_all_five_cards_in_played_hand();
1261static inline void cards_in_hand_update_loop(
void)
1271 if (hand[i] != NULL)
1273 FIXED hand_x = int2fx(HAND_START_POS.x);
1274 FIXED hand_y = int2fx(HAND_START_POS.y);
1279 hand_x = hand_x + (int2fx(i) - int2fx(
get_hand_top()) / 2) *
1284 (i == selected_card_idx &&
1285 game_round_selection_grid.selection.y == GAME_PLAYING_HAND_SEL_Y);
1287 if (is_focused && !card_object_is_selected(hand[i]))
1289 hand_y -= int2fx(CARD_FOCUSED_UNSEL_Y);
1291 else if (!is_focused && card_object_is_selected(hand[i]))
1293 hand_y -= int2fx(CARD_UNFOCUSED_SEL_Y);
1295 else if (is_focused && card_object_is_selected(hand[i]))
1297 hand_y -= int2fx(CARD_FOCUSED_SEL_Y);
1299 if (i != selected_card_idx && hand[i]->y > hand_y)
1301 hand[i]->y = hand_y;
1304 hand[i]->ty = hand_y;
1308 hand_x = hand_x + (int2fx(i) - int2fx(
get_hand_top()) / 2) *
1313 case HAND_SHUFFLING:
1317 card_in_hand_loop_handle_discard_and_shuffling(
1328 hand_x = hand_x + (int2fx(i) - int2fx(
get_hand_top()) / 2) *
1330 hand_y += int2fx(24);
1332 if (card_object_is_selected(hand[i]) && s_discarded_card ==
false &&
1333 g_game_vars.timer % FRAMES(10) == 0)
1335 card_object_set_selected(hand[i],
false);
1351 s_discarded_card =
true;
1354 if (i == 0 && s_discarded_card ==
false && g_game_vars.timer % FRAMES(10) == 0)
1359 g_game_vars.timer = TM_ZERO;
1362 select_cards_in_played_hand();
1368 hand_x = hand_x + (int2fx(i) - int2fx(
get_hand_top()) / 2) *
1370 hand_y += int2fx(24);
1374 hand[i]->tx = hand_x;
1375 hand[i]->ty = hand_y;
1380static inline void game_round_ui_text_update(
void)
1382 static int s_last_hand_size = 0;
1383 static int s_last_deck_size = 0;
1387 switch (get_current_background())
1389 case BG_CARD_SELECTING:
1392 "#{P:%d,%d; cx:0x%X000}%2d/%-2ld",
1393 HAND_SIZE_RECT_SELECT.left,
1394 HAND_SIZE_RECT_SELECT.top,
1397 g_game_vars.hand_size
1401 case BG_CARD_PLAYING:
1404 "#{P:%d,%d; cx:0x%X000}%2d/%-2ld",
1405 HAND_SIZE_RECT_PLAYING.left,
1406 HAND_SIZE_RECT_PLAYING.top,
1409 g_game_vars.hand_size
1418 display_deck_size_max();
1421 s_last_deck_size = deck_get_size();
1429 if (curr_score >= required_score && !s_score_flames_active)
1432 s_score_flames_active =
true;
1435 if (curr_score < required_score && s_score_flames_active)
1438 s_score_flames_active =
false;
1440 Rect reset_rect = SCORE_FLAME_RESET;
1442 reset_rect.left += SCORE_FLAME_FRAME_WIDTH;
1443 reset_rect.right += SCORE_FLAME_FRAME_WIDTH;
1448static inline void game_round_process_flaming_score(
void)
1450 static u8 flame_score_frame = 0;
1452 if (s_score_flames_active)
1454 if (g_game_vars.timer % SCORE_FLAMES_ANIM_FREQ == 0)
1456 Rect frame_rect = SCORE_FLAME_FRAMES_START;
1457 flame_score_frame = (flame_score_frame + 1) % NUM_SCORE_FLAMES_FRAMES;
1460 frame_rect.top += flame_score_frame;
1461 frame_rect.bottom += flame_score_frame;
1465 frame_rect.left += SCORE_FLAME_FRAME_WIDTH;
1466 frame_rect.right += SCORE_FLAME_FRAME_WIDTH;
1489 enum JokerEvent joker_event
1496 if (joker_object_score(joker, card_object, joker_event))
1504static inline void play_starting_played_cards_update(
int played_idx)
1507 bool card_selected =
1508 (s_played_top < s_scored_card_index)
1510 : card_object_is_selected(s_played_hand[s_played_top - s_scored_card_index]);
1512 if (played_idx == s_played_top && (g_game_vars.timer % FRAMES(10) == 0 || !card_selected) &&
1513 g_game_vars.timer > FRAMES(40))
1515 s_scored_card_index--;
1517 if (s_scored_card_index == 0)
1520 g_game_vars.timer = TM_ZERO;
1521 play_state = PLAY_BEFORE_SCORING;
1525 s_played_hand[played_idx]->tx =
1526 int2fx(HAND_PLAY_POS.x) +
1527 (int2fx(s_played_top - played_idx) - int2fx(s_played_top) / 2) * -27;
1528 s_played_hand[played_idx]->ty = int2fx(HAND_PLAY_POS.y);
1530 card_selected = card_object_is_selected(s_played_hand[played_idx]);
1531 if (card_selected && s_played_top - played_idx >= s_scored_card_index)
1533 s_played_hand[played_idx]->ty -= int2fx(10);
1552 play_state = PLAY_SCORING_CARDS;
1563 if (g_game_vars.timer % FRAMES(30) == 0 && g_game_vars.timer > FRAMES(40))
1568 while (s_scored_card_index <= s_played_top &&
1569 !card_object_is_selected(s_played_hand[s_scored_card_index]))
1571 s_scored_card_index++;
1575 if (s_scored_card_index > s_played_top)
1581 play_state = PLAY_SCORING_HELD_CARDS;
1588 CardObject* scored_card_object = s_played_hand[s_scored_card_index];
1590 if (card_object_is_selected(scored_card_object))
1593 tte_set_pos(fx2int(scored_card_object->x) +
TILE_SIZE, SCORED_CARD_TEXT_Y);
1596 tte_set_special(TTE_BLUE_PB * TTE_SPECIAL_PB_MULT_OFFSET);
1598 u8 card_value = card_get_value(scored_card_object->card);
1601 char score_buffer[INT_MAX_DIGITS + 2];
1602 snprintf(score_buffer,
sizeof(score_buffer),
"+%hhu", card_value);
1603 tte_write(score_buffer);
1605 card_object_shake(scored_card_object, SFX_CHIPS_CARD);
1616 play_state = PLAY_SCORING_CARD_JOKERS;
1632 if (g_game_vars.timer % FRAMES(30) == 0 && g_game_vars.timer > FRAMES(40))
1639 &s_joker_scored_itr,
1640 s_played_hand[s_scored_card_index],
1641 JOKER_EVENT_ON_CARD_SCORED
1650 &s_joker_card_scored_end_itr,
1651 s_played_hand[s_scored_card_index],
1652 JOKER_EVENT_ON_CARD_SCORED_END
1660 s_retrigger =
false;
1661 play_state = PLAY_SCORING_CARDS;
1667 s_scored_card_index++;
1668 play_state = PLAY_SCORING_CARDS;
1682 if (played_idx == 0 && (g_game_vars.timer % FRAMES(30) == 0) && g_game_vars.timer > FRAMES(40))
1689 for (; s_scored_card_index >= 0; s_scored_card_index--)
1692 &s_joker_scored_itr,
1693 hand[s_scored_card_index],
1694 JOKER_EVENT_ON_CARD_HELD
1697 card_object_shake(hand[s_scored_card_index], SFX_CARD_SELECT);
1703 s_scored_card_index = 0;
1705 play_state = PLAY_SCORING_INDEPENDENT_JOKERS;
1719 if (played_idx == 0 && (g_game_vars.timer % FRAMES(30) == 0) && g_game_vars.timer > FRAMES(40))
1732 play_state = PLAY_SCORING_HAND_SCORED_END;
1746 if (played_idx == 0 && (g_game_vars.timer % FRAMES(30) == 0) && g_game_vars.timer > FRAMES(40))
1752 &s_joker_round_end_itr,
1754 JOKER_EVENT_ON_HAND_SCORED_END
1762 g_game_vars.timer = TM_ZERO;
1763 play_state = PLAY_ENDING;
1778 bool card_selected =
1779 (s_played_top < s_scored_card_index)
1781 : card_object_is_selected(s_played_hand[s_played_top - s_scored_card_index]);
1783 if (played_idx == s_played_top && (g_game_vars.timer % FRAMES(10) == 0 || !card_selected) &&
1784 g_game_vars.timer > FRAMES(40))
1786 s_scored_card_index--;
1791 int static const CHIPS_ACCUM_SFX_PITCH_RATIO = 2;
1793 if (s_scored_card_index == 0)
1800 g_game_vars.timer = TM_ZERO;
1801 play_state = PLAY_ENDED;
1805 if (card_object_is_selected(s_played_hand[played_idx]) &&
1806 s_played_top - played_idx >= s_scored_card_index)
1808 s_played_hand[played_idx]->ty = int2fx(HAND_PLAY_POS.y);
1822 if (!s_discarded_card && g_game_vars.timer > FRAMES(40))
1825 if (!s_sound_played)
1832 s_sound_played =
true;
1836 if (s_played_hand[played_idx]->x >= int2fx(CARD_DISCARD_PNT.x))
1839 card_object_destroy(&s_played_hand[played_idx]);
1842 s_sound_played =
false;
1845 if (played_idx == s_played_top)
1856 play_state = PLAY_STARTING;
1860 s_scored_card_index = 0;
1862 g_game_vars.timer = TM_ZERO;
1869 s_played_hand[played_idx]->tx = int2fx(CARD_DISCARD_PNT.x);
1870 s_discarded_card =
true;
1876static inline void played_cards_update_loop(
void)
1881 for (
int played_idx = 0; played_idx <= s_played_top; played_idx++)
1883 if (s_played_hand[played_idx] == NULL)
1888 if (card_object_get_sprite(s_played_hand[played_idx]) == NULL)
1891 card_object_set_sprite(s_played_hand[played_idx], played_idx + MAX_HAND_SIZE);
1898 play_starting_played_cards_update(played_idx);
1901 case PLAY_BEFORE_SCORING:
1909 case PLAY_SCORING_CARDS:
1917 case PLAY_SCORING_CARD_JOKERS:
1925 case PLAY_SCORING_HELD_CARDS:
1933 case PLAY_SCORING_INDEPENDENT_JOKERS:
1941 case PLAY_SCORING_HAND_SCORED_END:
1967 s_played_hand[played_idx]->tscale = FIX_ONE;
1985 g_game_vars.current_blind,
1986 CUR_BLIND_TOKEN_POS.x,
1987 CUR_BLIND_TOKEN_POS.y,
1988 PLAYING_BLIND_TOKEN_LAYER
1997 g_game_vars.current_blind,
2000 ROUND_END_BLIND_TOKEN_LAYER
2003 if (g_game_vars.round_end_blind_token != NULL)
2008 Rect blind_req_text_rect = BLIND_REQ_TEXT_RECT;
2011 char blind_req_str_buff[UINT_MAX_DIGITS + 1];
2023 "#{P:%d,%d; cx:0x%X000}%s",
2024 blind_req_text_rect.left,
2025 blind_req_text_rect.top,
2030 "#{P:%d,%d; cx:0x%X000}$%d",
2031 BLIND_REWARD_RECT.left,
2032 BLIND_REWARD_RECT.top,
2043 game_round_selection_grid.selection = GAME_PLAYING_INIT_SEL;
2059 game_round_process_input_and_state();
2063 game_round_process_card_draw();
2065 game_round_discarded_cards_loop();
2067 s_discarded_card =
false;
2069 cards_in_hand_update_loop();
2070 played_cards_update_loop();
2072 game_round_ui_text_update();
2075 game_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.
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)
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 bool game_round_is_over(void)
Determines if the round is over, be it because we lost or won the round.
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 void game_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_independent_jokers_update(int played_idx)
Score Jokers normally for event JOKER_EVENT_INDEPENDENT.
static void game_round_play_hand_on_pressed(void)
"Play" button implementation.
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.
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 set_retrigger(bool new_retrigger)
Set whether the current card should get triggered again.
static void game_round_sort_by_suit_on_pressed(void)
"Suit" sorting button implementation.
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.
void game_round_on_init(void)
Round state initialization.
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 game_round_execute_discard(void)
Triggers the discard of the currently selected cards in hand.
static void game_round_execute_play_hand(void)
Triggers the playing of the currently selected cards in hand.
void game_round_on_update(void)
Round state update.
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 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,...
void game_round_change_background_playing(void)
Change to the round card playing background.
static bool can_discard_hand(void)
Determines whether the "Discard" button can be pressed or not.
static void game_round_discard_on_pressed(void)
"Discard" button implementation.
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 game_round_sort_by_rank_on_pressed(void)
"Rank" sorting button implementation.
void game_round_change_background_selecting(void)
Change to the round card selection background.
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.
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.
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
A friendly wrapper around the not so friendly looking attribute syntax for ((unused))
uint32_t u32_protected_mult(uint32_t a, uint32_t b)
Avoid overflow when multiplying two u32 integers.