From b1c2b7f35e167be0bf96d9755aa2903210ed4984 Mon Sep 17 00:00:00 2001 From: Rickey Date: Wed, 13 May 2026 18:22:02 -0700 Subject: [PATCH] Refactor/round end (#479) * Initial round_end extraction * move hands to game vars * Checkpoint * Finish removing all variables for state fns * Move over round_end substate functions * Move over round_end with compile * working in general * Fix round start state issue * clang-format * Fix clang format issue * PR fixes and standardize types in game vars struct * Clang-format --- include/def_state_info_table.h | 2 +- include/game.h | 3 + include/game/options_menu.h | 4 +- include/game/round_end.h | 29 ++ include/game_variables.h | 15 +- include/layout.h | 18 +- include/timer.h | 8 + source/game.c | 598 ++++----------------------------- source/game/common_ui.c | 3 +- source/game/round_end.c | 473 ++++++++++++++++++++++++++ 10 files changed, 606 insertions(+), 547 deletions(-) create mode 100644 include/game/round_end.h create mode 100644 source/game/round_end.c diff --git a/include/def_state_info_table.h b/include/def_state_info_table.h index 1577790..f4628cb 100644 --- a/include/def_state_info_table.h +++ b/include/def_state_info_table.h @@ -5,7 +5,7 @@ DEF_STATE_INFO(GAME_STATE_MAIN_MENU, game_main_menu_on_init, game_main_me DEF_STATE_INFO(GAME_STATE_OPTIONS_MENU, game_options_menu_on_init, game_options_menu_on_update, game_options_menu_on_exit) DEF_STATE_INFO(GAME_STATE_GAME_START, noop, game_start, noop ) DEF_STATE_INFO(GAME_STATE_PLAYING, game_round_on_init, game_playing_on_update, noop ) -DEF_STATE_INFO(GAME_STATE_ROUND_END, noop, game_round_end_on_update, game_round_end_on_exit ) +DEF_STATE_INFO(GAME_STATE_ROUND_END, game_round_end_on_init, game_round_end_on_update, game_round_end_on_exit ) DEF_STATE_INFO(GAME_STATE_SHOP, noop, game_shop_on_update, game_shop_on_exit ) DEF_STATE_INFO(GAME_STATE_BLIND_SELECT, game_blind_select_on_init, game_blind_select_on_update, game_blind_select_on_exit) DEF_STATE_INFO(GAME_STATE_LOSE, game_lose_on_init, game_over_on_update, game_over_on_exit ) diff --git a/include/game.h b/include/game.h index e6dfc76..0f397da 100644 --- a/include/game.h +++ b/include/game.h @@ -192,5 +192,8 @@ void display_round(void); void reset_top_left_panel_bottom_row(void); void reset_background(void); +void display_hands(void); +void display_discards(void); +void display_score(u32 value); #endif // GAME_H diff --git a/include/game/options_menu.h b/include/game/options_menu.h index 5d44324..6d01c3b 100644 --- a/include/game/options_menu.h +++ b/include/game/options_menu.h @@ -6,8 +6,6 @@ #ifndef GAME_OPTIONS_MENU_H #define GAME_OPTIONS_MENU_H -#include "game_variables.h" - /** * @brief Change the options menu background */ @@ -28,4 +26,4 @@ void game_options_menu_on_update(void); */ void game_options_menu_on_exit(void); -#endif // GAME_OPTIONS_MENU_H \ No newline at end of file +#endif // GAME_OPTIONS_MENU_H diff --git a/include/game/round_end.h b/include/game/round_end.h new file mode 100644 index 0000000..2c4c467 --- /dev/null +++ b/include/game/round_end.h @@ -0,0 +1,29 @@ +/** + * @file round_end.h + * + * @brief Round end game state functions + */ +#ifndef GAME_ROUND_END_H +#define GAME_ROUND_END_H + +/** + * @brief Change to the round end background + */ +void game_round_end_change_background(void); + +/** + * @brief Round end state initialization + */ +void game_round_end_on_init(void); + +/** + * @brief Round end state update + */ +void game_round_end_on_update(void); + +/** + * @brief Round end cleanup + */ +void game_round_end_on_exit(void); + +#endif // GAME_ROUND_END_H diff --git a/include/game_variables.h b/include/game_variables.h index 9fb1e95..f8ddd95 100644 --- a/include/game_variables.h +++ b/include/game_variables.h @@ -23,6 +23,9 @@ #define DEFAULT_MUSIC_VOLUME VOLUME_OPTION_MAX #define DEFAULT_SOUND_VOLUME VOLUME_OPTION_MAX +#define MAX_HANDS 4 +#define MAX_DISCARDS 4 + /** * @brief A central location for all game variables. * @@ -39,9 +42,9 @@ typedef struct // Variables visible by the player - int round; - int ante; - int money; + s32 round; + s32 ante; + s32 money; // Blind variables @@ -49,6 +52,12 @@ typedef struct enum BlindType next_boss_blind; enum BlindState blinds_states[NUM_BLINDS_PER_ANTE]; + s32 hands; + s32 discards; + u32 score; + + Sprite* playing_blind_token; + Sprite* round_end_blind_token; // Options variables // BY DEFAULT IS SET TO 1, but if changed to 2 or more, should speed up all (or most) of the diff --git a/include/layout.h b/include/layout.h index a414ba9..b79f4fe 100644 --- a/include/layout.h +++ b/include/layout.h @@ -9,13 +9,17 @@ #include "graphic_utils.h" // clang-format off -// Points x y -static const BG_POINT CUR_BLIND_TOKEN_POS = {8, 18}; -static const BG_POINT TOP_LEFT_PANEL_POINT = {0, 0, }; -// Rects left top right bottom -static const Rect TOP_LEFT_PANEL_ANIM_RECT = {0, 0, 8, 4}; -static const Rect POP_MENU_ANIM_RECT = {9, 7, 24, 31}; -static const Rect TOP_LEFT_ITEM_SRC_RECT = {0, 20, 8, 25}; +// Points x y +static const BG_POINT CUR_BLIND_TOKEN_POS = {8, 18}; +static const BG_POINT TOP_LEFT_PANEL_POINT = {0, 0}; +static const BG_POINT ROUND_END_REWARDS_ELLIPSIS_POS = {10, 13}; +// Rects left top right bottom +static const Rect TOP_LEFT_PANEL_ANIM_RECT = {0, 0, 8, 4}; +static const Rect POP_MENU_ANIM_RECT = {9, 7, 24, 31}; +static const Rect TOP_LEFT_ITEM_SRC_RECT = {0, 20, 8, 25}; +static const Rect BLIND_REWARD_RECT = {40, 32, 64, 40}; +static const Rect BLIND_REQ_TEXT_RECT = {32, 24, 64, 32}; +static const Rect HAND_SIZE_RECT = {128, 128, 152, 160}; // Seems to include both SELECT and PLAYING // clang-format on #endif // LAYOUT_H diff --git a/include/timer.h b/include/timer.h index a736a6a..b30e834 100644 --- a/include/timer.h +++ b/include/timer.h @@ -9,6 +9,14 @@ #define TM_ZERO 0 +#define TM_REWARDS_ELLIPSIS_PRINT_START 2 +#define TM_REWARDS_ELLIPSIS_PRINT_END 16 +#define TM_REWARD_DISPLAY_INTERVAL 15 +#define TM_REWARD_INCREMENT_INTERVAL 20 + +#define TM_DISPLAY_REWARDS_CONT_WAIT (TM_REWARDS_ELLIPSIS_PRINT_END + TM_REWARD_DISPLAY_INTERVAL) +#define TM_HAND_REWARD_INCR_WAIT (TM_DISPLAY_REWARDS_CONT_WAIT + TM_REWARD_DISPLAY_INTERVAL) + #define MENU_POP_OUT_ANIM_FRAMES 20 #endif // TIMER_H diff --git a/source/game.c b/source/game.c index da84029..c8036c8 100644 --- a/source/game.c +++ b/source/game.c @@ -15,6 +15,7 @@ #include "game/game_over.h" #include "game/main_menu.h" #include "game/options_menu.h" +#include "game/round_end.h" #include "game_variables.h" #include "graphic_utils.h" #include "hand_analysis.h" @@ -38,13 +39,10 @@ #define STRAIGHT_AND_FLUSH_SIZE_DEFAULT 5 // Pixel sizes -#define ITEM_SHOP_Y 71 -#define ROUND_END_REWARD_AMOUNT_X 168 -#define ROUND_END_REWARD_TEXT_X 88 -#define SCORED_CARD_TEXT_Y 48 +#define ITEM_SHOP_Y 71 +#define SCORED_CARD_TEXT_Y 48 // SE sizes -#define ROUND_END_BLACK_PANEL_INIT_BOTTOM_SE 12 #define SHOP_LIGHTS_1_CLR 0xFFFF #define SHOP_LIGHTS_2_CLR 0x32BE @@ -65,22 +63,10 @@ #define CARD_FOCUSED_SEL_Y 20 // Timer defs -#define TM_RESET_STATIC_VARS 30 -#define TM_END_POP_MENU_ANIM 13 -#define TM_START_ROUND_END_REWARDS_ANIM 1 -#define TM_END_DISPLAY_FIN_BLIND 30 -#define TM_END_DISPLAY_SCORE_MIN 4 -#define TM_REWARDS_ELLIPSIS_PRINT_START 2 -#define TM_REWARDS_ELLIPSIS_PRINT_END 16 -#define TM_REWARD_DISPLAY_INTERVAL 15 -#define TM_DISPLAY_REWARDS_CONT_WAIT (TM_REWARDS_ELLIPSIS_PRINT_END + TM_REWARD_DISPLAY_INTERVAL) -#define TM_HAND_REWARD_INCR_WAIT (TM_DISPLAY_REWARDS_CONT_WAIT + TM_REWARD_DISPLAY_INTERVAL) -#define TM_REWARD_INCREMENT_INTERVAL 20 -#define TM_DISMISS_ROUND_END_TM 20 -#define TM_CREATE_SHOP_ITEMS_WAIT 1 -#define TM_SHIFT_SHOP_ICON_WAIT 7 -#define TM_END_GAME_SHOP_INTRO 12 -#define TM_SHOP_PRC_INPUT_START 1 +#define TM_CREATE_SHOP_ITEMS_WAIT 1 +#define TM_SHIFT_SHOP_ICON_WAIT 7 +#define TM_END_GAME_SHOP_INTRO 12 +#define TM_SHOP_PRC_INPUT_START 1 // TODO: Rename "PID" to "PAL_IDX" // Palette IDs @@ -98,7 +84,6 @@ #define SHOP_LIGHTS_3_PID 17 #define BLIND_BG_SECONDARY_PID 18 #define BLIND_BG_PRIMARY_PID 19 -#define REWARD_PANEL_BORDER_PID 19 #define SHOP_LIGHTS_4_PID 22 #define SHOP_BOTTOM_PANEL_BORDER_PID 26 @@ -140,20 +125,6 @@ enum GameShopStates GAME_SHOP_MAX }; -enum GameRoundEndStates -{ - ROUND_END_START, - START_EXPAND_POPUP, - DISPLAY_FINISHED_BLIND, - DISPLAY_SCORE_MIN, - UPDATE_BLIND_REWARD, - BLIND_PANEL_EXIT, - DISPLAY_REWARDS, - DISPLAY_CASHOUT, - DISMISS_ROUND_END_PANEL, - ROUND_END_EXIT -}; - typedef struct { u32 chips; @@ -178,29 +149,15 @@ static void noop(void) // reviewer(s). static void game_round_on_init(void); static void game_playing_on_update(void); -static void game_round_end_on_update(void); -static void game_round_end_on_exit(void); static void game_shop_on_update(void); static void game_shop_on_exit(void); static void game_shop_intro(void); static void game_shop_process_user_input(void); static void game_shop_outro(void); -static void game_round_end_start(void); -static void game_round_end_start_expand_popup(void); -static void game_round_end_display_finished_blind(void); -static void game_round_end_display_score_min(void); -static void game_round_end_update_blind_reward(void); -static void game_round_end_panel_exit(void); -static void game_round_end_display_rewards(void); -static void game_round_end_display_cashout(void); -static void game_round_end_dismiss_round_end_panel(void); static void sort_cards(void); static void display_temp_score(u32 value); -static void display_score(u32 value); static void check_flaming_score(void); -static void display_hands(int value); -static void display_discards(int value); static void set_hand(void); static int deck_get_size(void); static int deck_get_max_size(void); @@ -209,7 +166,6 @@ static bool check_and_score_joker_for_event( CardObject* card_object, enum JokerEvent joker_event ); -static int calculate_interest_reward(void); static void game_playing_discard_on_pressed(void); static void game_playing_execute_discard(void); @@ -269,7 +225,6 @@ static void game_shop_create_items(void); static void erase_price_under_sprite_object(SpriteObject* sprite_object); static void print_price_under_sprite_object(SpriteObject* sprite_object, int price); -static void game_round_end_extend_black_panel_down(int black_panel_bottom); static void remove_owned_joker(int owned_joker_idx); @@ -292,8 +247,6 @@ static bool can_discard_hand(void); // This is because when popping, the target position is blank so we just animate // the whole rect so we don't have to track its position -static const Rect ROUND_END_MENU_RECT = {9, 7, 24, 20 }; - static const Rect HAND_BG_RECT_SELECTING = {9, 11, 24, 17 }; /* Contains the shop icon/current blind etc. @@ -306,9 +259,6 @@ static const Rect HAND_BG_RECT_SELECTING = {9, 11, 24, 17 }; static const BG_POINT TOP_LEFT_BLIND_TITLE_POINT = {0, 21, }; static const Rect BIG_BLIND_TITLE_SRC_RECT = {0, 26, 8, 26 }; static const Rect BOSS_BLIND_TITLE_SRC_RECT = {0, 27, 8, 27 }; -static const Rect CASHOUT_DEST_RECT = {10, 8, 23, 10 }; -static const BG_POINT CASHOUT_SRC_3X3_RECT_POS = {5, 29}; -static const BG_POINT ROUND_END_REWARDS_ELLIPSIS_POS = {10, 13}; // Flaming score animation frames #define SCORE_FLAMES_ANIM_FREQ 5 // animation will run at 12FPS @@ -320,7 +270,6 @@ static const BG_POINT SCORE_FLAME_CHIPS_POS = {1, 9}; static const BG_POINT SCORE_FLAME_MULT_POS = {5, 9}; // Rects for TTE (in pixels) -static const Rect HAND_SIZE_RECT = {128, 128, 152, 160 }; // Seems to include both SELECT and PLAYING static const Rect HAND_SIZE_RECT_SELECT = {128, 128, 152, 136 }; static const Rect HAND_SIZE_RECT_PLAYING = {128, 152, 152, 160 }; static const Rect HAND_TYPE_RECT = {8, 64, 64, 72 }; @@ -330,12 +279,9 @@ static const Rect SCORE_RECT = {24, 48, 64, 56 }; static const Rect PLAYED_CARDS_SCORES_RECT = {72, 48, 240, 56 }; static const Rect HELD_CARDS_SCORES_RECT = {72, 108, 240, 116 }; -static const Rect BLIND_TOKEN_TEXT_RECT = {80, 72, 200, 160 }; static const Rect MONEY_TEXT_RECT = {8, 120, 64, 128 }; static const Rect CHIPS_TEXT_RECT = {8, 80, 32, 88 }; static const Rect MULT_TEXT_RECT = {40, 80, 64, 88 }; -static const Rect BLIND_REWARD_RECT = {40, 32, 64, 40 }; -static const Rect BLIND_REQ_TEXT_RECT = {32, 24, 64, 32 }; static const Rect SHOP_PRICES_TEXT_RECT = {72, 56, 192, 160 }; // Rects with UNDEFINED are only used in tte_printf, they need to be fully defined @@ -345,9 +291,6 @@ static const Rect DISCARDS_TEXT_RECT = {48, 104, UNDEFINED, UNDEF static const Rect DECK_SIZE_RECT = {200, 152, 240, 160 }; static const Rect ROUND_TEXT_RECT = {48, 144, UNDEFINED, UNDEFINED }; static const Rect ANTE_TEXT_RECT = {8, 144, UNDEFINED, UNDEFINED }; -static const Rect ROUND_END_BLIND_REQ_RECT = {104, 96, 136, UNDEFINED }; -static const Rect ROUND_END_BLIND_REWARD_RECT = { 168, 96, UNDEFINED, UNDEFINED }; -static const Rect CASHOUT_TEXT_RECT = {88, 72, UNDEFINED, UNDEFINED }; static const Rect SHOP_REROLL_RECT = {88, 96, UNDEFINED, UNDEFINED }; static const BG_POINT HELD_JOKERS_POS = {108, 10}; @@ -452,18 +395,6 @@ static const SubStateActionFn shop_state_actions[] = { game_shop_outro }; -static const SubStateActionFn round_end_state_actions[] = { - game_round_end_start, - game_round_end_start_expand_popup, - game_round_end_display_finished_blind, - game_round_end_display_score_min, - game_round_end_update_blind_reward, - game_round_end_panel_exit, - game_round_end_display_rewards, - game_round_end_display_cashout, - game_round_end_dismiss_round_end_panel -}; - static int reroll_cost = REROLL_BASE_COST; // The current game state, this is used to determine what the game is doing at any given time @@ -490,6 +421,13 @@ GameVariables g_game_vars = { BLIND_STATE_UPCOMING }, + .hands = 0, + .discards = 0, + .score = 0, + + .playing_blind_token = NULL, + .round_end_blind_token = NULL, + .game_speed = DEFAULT_GAME_SPEED, .high_contrast = DEFAULT_HIGH_CONTRAST, .music_volume = DEFAULT_MUSIC_VOLUME, @@ -498,27 +436,12 @@ GameVariables g_game_vars = { // clang-format on // The sprite that displays the blind when in "GAME_PLAYING/GAME_ROUND_END" state -static Sprite* playing_blind_token = NULL; // The sprite that displays the blind when in "GAME_ROUND_END" state -static Sprite* round_end_blind_token = NULL; - -// Will be rolled later, just giving it a valid value - -static int blind_reward = 0; -static int hand_reward = 0; -static int interest_reward = 0; -static int interest_to_count = 0; -static int interest_start_time = UNDEFINED; // Red deck default (can later be moved to a deck.h file or something) -static int max_hands = 4; -static int max_discards = 4; // Set in game_init and game_round_init -static int hands = 0; -static int discards = 0; -static u32 score = 0; static u32 temp_score = 0; // This is the score that shows in the same spot as the hand type. static bool score_flames_active = false; static FIXED lerped_score = 0; @@ -661,8 +584,8 @@ void game_init() jokers_available_to_shop_init(); - hands = max_hands; - discards = max_discards; + g_game_vars.hands = MAX_HANDS; + g_game_vars.discards = MAX_DISCARDS; g_game_vars.timer = TM_ZERO; g_game_vars.current_blind = BLIND_TYPE_SMALL; g_game_vars.blinds_states[0] = BLIND_STATE_CURRENT; @@ -670,7 +593,7 @@ void game_init() g_game_vars.blinds_states[2] = BLIND_STATE_UPCOMING; g_game_vars.ante = STARTING_ANTE; g_game_vars.money = STARTING_MONEY; - score = STARTING_SCORE; + g_game_vars.score = STARTING_SCORE; g_game_vars.round = 0; // Initialize/reset unbeaten Boss/Showdown Blinds so they are all available @@ -692,8 +615,8 @@ void game_reset() // For some reason that I haven't figured out yet, // if I don't destroy the blind tokens they won't // show up on the next run. - sprite_destroy(&playing_blind_token); - sprite_destroy(&round_end_blind_token); + sprite_destroy(&g_game_vars.playing_blind_token); + sprite_destroy(&g_game_vars.round_end_blind_token); list_clear(&_owned_jokers_list); list_clear(&_discarded_jokers_list); @@ -703,15 +626,15 @@ void game_reset() game_init(); display_round(); - display_score(score); + display_score(g_game_vars.score); display_chips(); display_mult(); - display_hands(hands); - display_discards(discards); + display_hands(); + display_discards(); display_money(); // Ante tte_printf( - "#{P:%d,%d; cx:0x%X000}%d#{cx:0x%X000}/%d", + "#{P:%d,%d; cx:0x%X000}%ld#{cx:0x%X000}/%d", ANTE_TEXT_RECT.left, ANTE_TEXT_RECT.top, TTE_YELLOW_PB, @@ -957,12 +880,12 @@ int get_deck_top(void) int get_num_discards_remaining(void) { - return discards; + return g_game_vars.discards; } int get_num_hands_remaining(void) { - return hands; + return g_game_vars.hands; } u32 get_chips(void) @@ -996,7 +919,7 @@ void display_money() tte_erase_rect_wrapper(MONEY_TEXT_RECT); char money_str_buff[INT_MAX_DIGITS + 2]; // + 2 for null terminator and "$" sign - snprintf(money_str_buff, sizeof(money_str_buff), "$%d", g_game_vars.money); + snprintf(money_str_buff, sizeof(money_str_buff), "$%ld", g_game_vars.money); // Bias left so the number is centered and the "$" sign is on the left update_text_rect_to_center_str(&money_text_rect, money_str_buff, SCREEN_LEFT); @@ -1431,20 +1354,6 @@ void change_background_legacy(enum BackgroundId id) tte_erase_rect_wrapper(HAND_SIZE_RECT_SELECT); } - else if (id == BG_ROUND_END) - { - if (background_legacy != BG_CARD_SELECTING && background_legacy != BG_CARD_PLAYING) - { - change_background(BG_CARD_SELECTING, false); - background_legacy = BG_ROUND_END; - } - - // Disable window 0 so it doesn't make the cashout menu transparent - toggle_windows(false, true); - - main_bg_se_clear_rect(ROUND_END_MENU_RECT); - tte_erase_rect_wrapper(HAND_SIZE_RECT); - } else if (id == BG_SHOP) { toggle_windows(false, true); @@ -1472,7 +1381,7 @@ void change_background_legacy(enum BackgroundId id) 1 ); } - else if (id == BG_BLIND_SELECT || id == BG_MAIN_MENU) + else if (id == BG_BLIND_SELECT || id == BG_MAIN_MENU || id == BG_ROUND_END) { // do nothing, just don't return early! } @@ -1510,7 +1419,7 @@ static void display_temp_score(u32 value) ); } -static void display_score(u32 value) +void display_score(u32 value) { Rect score_rect = SCORE_RECT; // Clear the existing text before redrawing @@ -1559,7 +1468,7 @@ void display_round(void) { // tte_erase_rect_wrapper(ROUND_TEXT_RECT); tte_printf( - "#{P:%d,%d; cx:0x%X000}%d", + "#{P:%d,%d; cx:0x%X000}%ld", ROUND_TEXT_RECT.left, ROUND_TEXT_RECT.top, TTE_YELLOW_PB, @@ -1567,21 +1476,23 @@ void display_round(void) ); } -static void display_hands(int value) +void display_hands(void) { - // tte_erase_rect_wrapper(HANDS_TEXT_RECT); - tte_printf("#{P:%d,%d; cx:0xD000}%d", HANDS_TEXT_RECT.left, HANDS_TEXT_RECT.top, hands); // Hand + tte_printf( + "#{P:%d,%d; cx:0xD000}%ld", + HANDS_TEXT_RECT.left, + HANDS_TEXT_RECT.top, + g_game_vars.hands + ); } -static void display_discards(int value) +void display_discards(void) { - // tte_erase_rect_wrapper(DISCARDS_TEXT_RECT); - // Discard tte_printf( - "#{P:%d,%d; cx:0xE000}%d", + "#{P:%d,%d; cx:0xE000}%ld", DISCARDS_TEXT_RECT.left, DISCARDS_TEXT_RECT.top, - discards + g_game_vars.discards ); } @@ -1619,7 +1530,7 @@ static void set_hand(void) static bool can_discard_hand(void) { - return (discards > 0 && hand_state == HAND_SELECT && hand_selections > 0); + return (g_game_vars.discards > 0 && hand_state == HAND_SELECT && hand_selections > 0); } static int deck_get_size(void) @@ -1650,29 +1561,29 @@ static void game_round_on_init(void) cards_drawn = 0; hand_selections = 0; - sprite_destroy(&playing_blind_token); - playing_blind_token = blind_token_new( + sprite_destroy(&g_game_vars.playing_blind_token); + g_game_vars.playing_blind_token = blind_token_new( g_game_vars.current_blind, CUR_BLIND_TOKEN_POS.x, CUR_BLIND_TOKEN_POS.y, PLAYING_BLIND_TOKEN_LAYER ); // Create the blind token sprite at the top left corner // TODO: Hide blind token and display it after sliding blind rect animation - // if (playing_blind_token != NULL) + // if (g_game_vars.playing_blind_token != NULL) //{ - // obj_hide(playing_blind_token->obj); // Hide the blind token sprite for now + // obj_hide(g_game_vars.playing_blind_token->obj); // Hide the blind token sprite for now //} - sprite_destroy(&round_end_blind_token); - round_end_blind_token = blind_token_new( + sprite_destroy(&g_game_vars.round_end_blind_token); + g_game_vars.round_end_blind_token = blind_token_new( g_game_vars.current_blind, 81, 86, ROUND_END_BLIND_TOKEN_LAYER ); // Create the blind token sprite for round end - if (round_end_blind_token != NULL) + if (g_game_vars.round_end_blind_token != NULL) { - obj_hide(round_end_blind_token->obj); // Hide the blind token sprite for now + obj_hide(g_game_vars.round_end_blind_token->obj); // Hide the blind token sprite for now } Rect blind_req_text_rect = BLIND_REQ_TEXT_RECT; @@ -1739,7 +1650,8 @@ static void game_playing_execute_discard(void) return; hand_state = HAND_DISCARD; - display_discards(--discards); + --g_game_vars.discards; + display_discards(); set_hand(); } @@ -1804,7 +1716,8 @@ static void game_playing_execute_play_hand(void) return; hand_state = HAND_PLAY; - display_hands(--hands); + --g_game_vars.hands; + display_hands(); } static int game_playing_hand_row_get_size(void) @@ -2058,7 +1971,7 @@ static inline void game_playing_handle_round_over(void) { enum GameState next_state = GAME_STATE_ROUND_END; - if (score >= blind_get_requirement(g_game_vars.current_blind, g_game_vars.ante)) + if (g_game_vars.score >= blind_get_requirement(g_game_vars.current_blind, g_game_vars.ante)) { if (g_game_vars.current_blind > BLIND_TYPE_BIG) { @@ -2075,7 +1988,7 @@ static inline void game_playing_handle_round_over(void) } } } - else if (hands == 0) + else if (g_game_vars.hands == 0) { next_state = GAME_STATE_LOSE; } @@ -2386,8 +2299,8 @@ static bool check_and_score_joker_for_event( static inline bool game_round_is_over(void) { - return hands == 0 || - score >= blind_get_requirement(g_game_vars.current_blind, g_game_vars.ante); + return g_game_vars.hands == 0 || + g_game_vars.score >= blind_get_requirement(g_game_vars.current_blind, g_game_vars.ante); } // Basically a copy of HAND_DISCARD @@ -2827,7 +2740,7 @@ static inline void game_playing_process_input_and_state(void) // protect against score overflow temp_score = u32_protected_mult(chips, mult); lerped_temp_score = int2fx(temp_score); - lerped_score = int2fx(score); + lerped_score = int2fx(g_game_vars.score); display_temp_score(temp_score); @@ -2867,14 +2780,14 @@ static inline void game_playing_process_input_and_state(void) } else { - score = u32_protected_add(score, temp_score); + g_game_vars.score = u32_protected_add(g_game_vars.score, temp_score); temp_score = 0; lerped_temp_score = 0; lerped_score = 0; tte_erase_rect_wrapper(TEMP_SCORE_RECT); // Just erase the temp score - display_score(score); + display_score(g_game_vars.score); } } } @@ -3216,385 +3129,6 @@ static void game_playing_on_update(void) game_playing_process_flaming_score(); } -static int calculate_interest_reward(void) -{ - int reward = (g_game_vars.money / 5) * INTEREST_PER_5; - if (reward > MAX_INTEREST) - reward = MAX_INTEREST; - return reward; -} - -static void game_round_end_on_exit(void) -{ - // Cleanup blind tokens from this round to avoid accumulating - // allocated blind sprites each round - blind_reward = 0; - hand_reward = 0; - interest_reward = 0; - sprite_destroy(&playing_blind_token); - sprite_destroy(&round_end_blind_token); - // TODO: Reuse sprites for blind selection? -} - -static void game_round_end_on_update(void) -{ - if (state_info[game_state].substate == ROUND_END_EXIT) - { - game_change_state(GAME_STATE_SHOP); - return; - } - - int substate = state_info[game_state].substate; - round_end_state_actions[substate](); -} - -static void game_round_end_start() -{ - // Reset static variables to default values upon re-entering the round end state - if (g_game_vars.timer == TM_RESET_STATIC_VARS) - { - change_background(BG_ROUND_END, false); // Change the background to the round end background - state_info[game_state].substate = START_EXPAND_POPUP; // Change the state to the next one - g_game_vars.timer = TM_ZERO; // Reset the timer - blind_reward = blind_get_reward(g_game_vars.current_blind); - hand_reward = hands; - interest_reward = calculate_interest_reward(); - interest_to_count = interest_reward; - interest_start_time = UNDEFINED; - } -} - -static void game_round_end_start_expand_popup() -{ - main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT, SCREEN_UP); - - if (g_game_vars.timer == TM_END_POP_MENU_ANIM) - { - state_info[game_state].substate = DISPLAY_FINISHED_BLIND; - g_game_vars.timer = TM_ZERO; - } -} - -static void game_round_end_extend_black_panel_down(int black_panel_bottom) -{ - Rect single_line_rect = ROUND_END_MENU_RECT; - single_line_rect.bottom = black_panel_bottom; - single_line_rect.top = single_line_rect.bottom - 1; - main_bg_se_copy_rect_1_tile_vert(single_line_rect, SCREEN_DOWN); -} - -static void game_round_end_display_finished_blind() -{ - obj_unhide(round_end_blind_token->obj, 0); - - int current_ante = g_game_vars.ante; - - // Beating the boss blind increases the ante, so we need to display the previous ante value - if (g_game_vars.current_blind > BLIND_TYPE_BIG) - current_ante--; - - Rect blind_req_rect = ROUND_END_BLIND_REQ_RECT; - u32 blind_req = blind_get_requirement(g_game_vars.current_blind, current_ante); - - /* Not bothering to truncate here because there are 8 tiles - * and the blind requirement will not increase past ante 8 - * so there's enough room for sure. - */ - char blind_req_str_buff[UINT_MAX_DIGITS + 1]; - snprintf(blind_req_str_buff, sizeof(blind_req_str_buff), "%lu", blind_req); - - update_text_rect_to_right_align_str(&blind_req_rect, blind_req_str_buff, OVERFLOW_RIGHT); - - tte_printf( - "#{P:%d,%d; cx:0x%X000}%s", - blind_req_rect.left, - blind_req_rect.top, - TTE_RED_PB, - blind_req_str_buff - ); - - if (g_game_vars.timer == TM_START_ROUND_END_REWARDS_ANIM) - { - game_round_end_extend_black_panel_down(ROUND_END_BLACK_PANEL_INIT_BOTTOM_SE); - } - - if (g_game_vars.timer >= TM_END_DISPLAY_FIN_BLIND) - { - state_info[game_state].substate = DISPLAY_SCORE_MIN; - g_game_vars.timer = TM_ZERO; - } -} - -static void game_round_end_display_score_min() -{ - const int timer_offset = g_game_vars.timer - 1; - const int x_from = 0; - const int y_from = 29; - const int x_to = 13; - const int y_to = 11; - - memcpy16( - &se_mem[MAIN_BG_SBB][x_to + timer_offset + 32 * y_to], - &se_mem[MAIN_BG_SBB][x_from + timer_offset + 32 * y_from], - 1 - ); - - if (g_game_vars.timer >= TM_END_DISPLAY_SCORE_MIN) - { - state_info[game_state].substate = UPDATE_BLIND_REWARD; - g_game_vars.timer = TM_ZERO; - } -} - -static void game_round_end_update_blind_reward() -{ - if (g_game_vars.timer % FRAMES(20) != 0) - return; - - // TODO: Add sound effect here - - if (blind_reward > 0) - { - blind_reward--; - tte_printf( - "#{P:%d,%d; cx:0x%X000}$%d", - BLIND_REWARD_RECT.left, - BLIND_REWARD_RECT.top, - TTE_YELLOW_PB, - blind_reward - ); - tte_printf( - "#{P:%d,%d; cx:0x%X000}$%d", - ROUND_END_BLIND_REWARD_RECT.left, - ROUND_END_BLIND_REWARD_RECT.top, - TTE_YELLOW_PB, - blind_get_reward(g_game_vars.current_blind) - blind_reward - ); - } - else if (g_game_vars.timer > FRAMES(20)) - { - tte_erase_rect_wrapper(BLIND_REWARD_RECT); - tte_erase_rect_wrapper(BLIND_REQ_TEXT_RECT); - obj_hide(playing_blind_token->obj); - affine_background_load_palette(affine_background_gfxPal); - state_info[game_state].substate = BLIND_PANEL_EXIT; - g_game_vars.timer = TM_ZERO; - } -} - -static void game_round_end_panel_exit() -{ - // TODO: make heads or tails of what's going on here and replace - // magic numbers. - if (g_game_vars.timer < 8) - { - main_bg_se_copy_rect_1_tile_vert(TOP_LEFT_PANEL_ANIM_RECT, SCREEN_UP); - - if (g_game_vars.timer == 1) - { - reset_top_left_panel_bottom_row(); - } - else if (g_game_vars.timer == 2) - { - int y = 5; - memset16(&se_mem[MAIN_BG_SBB][32 * (y - 1)], 0x0001, 1); - memset16(&se_mem[MAIN_BG_SBB][1 + 32 * (y - 1)], 0x0002, 7); - memset16(&se_mem[MAIN_BG_SBB][8 + 32 * (y - 1)], 0x0401, 1); - } - } - else if (g_game_vars.timer > FRAMES(20)) - { - memset16(&pal_bg_mem[REWARD_PANEL_BORDER_PID], 0x1483, 1); - state_info[game_state].substate = DISPLAY_REWARDS; - g_game_vars.timer = TM_ZERO; - } -} - -static inline void game_round_end_print_separator_ellipsis(void) -{ - int x = - (ROUND_END_REWARDS_ELLIPSIS_POS.x + g_game_vars.timer - TM_REWARDS_ELLIPSIS_PRINT_START) * - TILE_SIZE; - int y = (ROUND_END_REWARDS_ELLIPSIS_POS.y) * TILE_SIZE; - - tte_printf("#{P:%d,%d; cx:0x%X000}.", x, y, TTE_WHITE_PB); -} - -// TODO: Allow for more generic rewards and consolidate with game_round_end_print_interest_reward() -static inline void game_round_end_print_hand_reward(int hand_y_offset) -{ - int hand_y = ROUND_END_REWARDS_ELLIPSIS_POS.y + hand_y_offset; - if (g_game_vars.timer == TM_DISPLAY_REWARDS_CONT_WAIT) - { - game_round_end_extend_black_panel_down(hand_y); - - tte_printf( - "#{P:%d,%d; cx:0x%X000}%d #{cx:0x%X000}Hands", - ROUND_END_REWARD_TEXT_X, - hand_y * TILE_SIZE, - TTE_BLUE_PB, - hand_reward, - TTE_WHITE_PB - ); - } - // Increment the hand reward text until the hand reward variable is depleted - else if (g_game_vars.timer > TM_HAND_REWARD_INCR_WAIT && - g_game_vars.timer % FRAMES(TM_REWARD_INCREMENT_INTERVAL) == 0) - { - hand_reward--; - tte_printf( - "#{P:%d, %d; cx:0x%X000}$%d", - ROUND_END_REWARD_AMOUNT_X, - hand_y * TILE_SIZE, - TTE_YELLOW_PB, - hands - hand_reward - ); - if (hand_reward == 0) - { - interest_start_time = g_game_vars.timer + TM_REWARD_DISPLAY_INTERVAL; - } - } -} - -static inline void game_round_end_print_interest_reward(int interest_y_offset) -{ - int interest_y = ROUND_END_REWARDS_ELLIPSIS_POS.y + interest_y_offset; - - if (g_game_vars.timer == interest_start_time) - { - game_round_end_extend_black_panel_down(interest_y); - - tte_printf( - "#{P:%d,%d; cx:0x%X000}%d #{cx:0x%X000}Interest", - ROUND_END_REWARD_TEXT_X, - interest_y * TILE_SIZE, - TTE_YELLOW_PB, - interest_reward, - TTE_WHITE_PB - ); - } - // Increment the interest reward text until the interest reward variable is depleted - else if (g_game_vars.timer > interest_start_time + TM_REWARD_DISPLAY_INTERVAL && - g_game_vars.timer % FRAMES(TM_REWARD_INCREMENT_INTERVAL) == 0) - { - interest_to_count--; - tte_printf( - "#{P:%d, %d; cx:0x%X000}$%d", - ROUND_END_REWARD_AMOUNT_X, - interest_y * TILE_SIZE, - TTE_YELLOW_PB, - interest_reward - interest_to_count - ); - } -} - -static void game_round_end_display_rewards() -{ - int hand_y_offset = 0; - int interest_y_offset = 0; - - if (hands > 0) - { - hand_y_offset = 1; - } - else - { - interest_start_time = TM_DISPLAY_REWARDS_CONT_WAIT; - } - - if (interest_reward > 0) - { - interest_y_offset = hand_y_offset + 1; - } - - // Once all rewards are accounted for go to the next state - if (hand_reward <= 0 && interest_to_count <= 0) - { - g_game_vars.timer = TM_ZERO; - state_info[game_state].substate = DISPLAY_CASHOUT; - } - else if (g_game_vars.timer == TM_START_ROUND_END_REWARDS_ANIM) - { - game_round_end_extend_black_panel_down(ROUND_END_REWARDS_ELLIPSIS_POS.y); - } - else if (g_game_vars.timer < TM_REWARDS_ELLIPSIS_PRINT_END) - { - game_round_end_print_separator_ellipsis(); - } - else if (g_game_vars.timer >= TM_DISPLAY_REWARDS_CONT_WAIT && hand_reward > 0) - { - game_round_end_print_hand_reward(hand_y_offset); - } - else if (interest_start_time != UNDEFINED && g_game_vars.timer >= interest_start_time && - interest_to_count > 0) - { - game_round_end_print_interest_reward(interest_y_offset); - } -} - -static inline void game_round_end_cashout(void) -{ - // Reward the player - g_game_vars.money += - hands + blind_get_reward(g_game_vars.current_blind) + calculate_interest_reward(); - display_money(); - - hands = max_hands; // Reset the hands to the maximum - discards = max_discards; // Reset the discards to the maximum - display_hands(hands); // Set the hands display - display_discards(discards); // Set the discards display - - score = 0; - display_score(score); // Set the score display -} - -static void game_round_end_display_cashout() -{ - if (g_game_vars.timer == FRAMES(40)) - { - // Put the "cash out" button onto the round end panel - main_bg_se_copy_expand_3x3_rect(CASHOUT_DEST_RECT, CASHOUT_SRC_3X3_RECT_POS); - - int cashout_amount = - hands + blind_get_reward(g_game_vars.current_blind) + calculate_interest_reward(); - - bool omit_space = cashout_amount >= 10; - tte_printf( - "#{P:%d, %d; cx:0x%X000}Cash Out:%s$%d", - CASHOUT_TEXT_RECT.left, - CASHOUT_TEXT_RECT.top, - TTE_WHITE_PB, - omit_space ? "" : " ", - cashout_amount - ); - } - - // Wait until the player presses A to cash out - else if (g_game_vars.timer > FRAMES(40) && key_hit(SELECT_CARD)) - { - game_round_end_cashout(); - - state_info[game_state].substate = DISMISS_ROUND_END_PANEL; // Go to the next state - g_game_vars.timer = TM_ZERO; - - obj_hide(round_end_blind_token->obj); // Hide the blind token object - tte_erase_rect_wrapper(BLIND_TOKEN_TEXT_RECT); // Erase the blind token text - } -} - -static void game_round_end_dismiss_round_end_panel() -{ - Rect round_end_down = ROUND_END_MENU_RECT; - round_end_down.top--; - main_bg_se_copy_rect_1_tile_vert(round_end_down, SCREEN_DOWN); - - if (g_game_vars.timer >= TM_DISMISS_ROUND_END_TM) - { - g_game_vars.timer = TM_ZERO; - state_info[game_state].substate = ROUND_END_EXIT; - } -} - static Rect get_text_rect_under_sprite_object(SpriteObject* sprite_object) { int height = 0; @@ -4248,8 +3782,8 @@ void game_start(void) affine_background_change_background(AFFINE_BG_GAME); - hands = max_hands; - discards = max_discards; + g_game_vars.hands = MAX_HANDS; + g_game_vars.discards = MAX_DISCARDS; // Activate high contrast palette for cards if loaded settings tell us to toggle_high_contrast_cards(g_game_vars.high_contrast); @@ -4278,19 +3812,19 @@ void game_start(void) deck_get_max_size() ); - display_round(); // Set the round display - display_score(score); // Set the score display + display_round(); // Set the round display + display_score(g_game_vars.score); // Set the score display display_chips(); // Set the chips display display_mult(); // Set the multiplier display - display_hands(hands); // Hand - display_discards(discards); // Discard + display_hands(); // Hand + display_discards(); // Discard display_money(); // Set the money display tte_printf( - "#{P:%d,%d; cx:0x%X000}%d#{cx:0x%X000}/%d", + "#{P:%d,%d; cx:0x%X000}%ld#{cx:0x%X000}/%d", ANTE_TEXT_RECT.left, ANTE_TEXT_RECT.top, TTE_YELLOW_PB, diff --git a/source/game/common_ui.c b/source/game/common_ui.c index ef0894d..bb5b44d 100644 --- a/source/game/common_ui.c +++ b/source/game/common_ui.c @@ -4,6 +4,7 @@ #include "game.h" #include "game/main_menu.h" #include "game/options_menu.h" +#include "game/round_end.h" typedef void (*BackgroundRenderCallback)(void); @@ -14,7 +15,7 @@ static const BackgroundRenderCallback bgCallbacks[] = { [BG_NONE] = NULL, [BG_CARD_SELECTING] = NULL, [BG_CARD_PLAYING] = NULL, - [BG_ROUND_END] = NULL, + [BG_ROUND_END] = game_round_end_change_background, [BG_SHOP] = NULL, [BG_BLIND_SELECT] = game_blind_select_change_background, [BG_OPTIONS_MENU] = game_options_menu_change_background, diff --git a/source/game/round_end.c b/source/game/round_end.c new file mode 100644 index 0000000..8f4dbab --- /dev/null +++ b/source/game/round_end.c @@ -0,0 +1,473 @@ +#include "round_end.h" + +#include "affine_background.h" +#include "affine_background_gfx.h" +#include "game_variables.h" +#include "layout.h" +#include "timer.h" +#include "util.h" + +enum GameRoundEndStates +{ + ROUND_END_START, + START_EXPAND_POPUP, + DISPLAY_FINISHED_BLIND, + DISPLAY_SCORE_MIN, + UPDATE_BLIND_REWARD, + BLIND_PANEL_EXIT, + DISPLAY_REWARDS, + DISPLAY_CASHOUT, + DISMISS_ROUND_END_PANEL, + ROUND_END_EXIT +}; + +static const u32 TM_RESET_STATIC_VARS = 30; +static const u32 TM_END_POP_MENU_ANIM = 13; +static const u32 TM_START_ROUND_END_REWARDS_ANIM = 1; +static const u32 TM_END_DISPLAY_FIN_BLIND = 30; +static const u32 TM_END_DISPLAY_SCORE_MIN = 4; +static const u32 TM_DISMISS_ROUND_END_TM = 20; + +static const u32 REWARD_PANEL_BORDER_PID = 19; + +static const u32 ROUND_END_BLACK_PANEL_INIT_BOTTOM_SE = 12; +static const u32 ROUND_END_REWARD_TEXT_X = 88; +static const u32 ROUND_END_REWARD_AMOUNT_X = 168; + +// clang-format off +static const Rect ROUND_END_BLIND_REQ_RECT = {104, 96, 136, UNDEFINED}; +static const Rect ROUND_END_BLIND_REWARD_RECT = {168, 96, UNDEFINED, UNDEFINED}; +static const Rect CASHOUT_DEST_RECT = {10, 8, 23, 10 }; +static const Rect CASHOUT_TEXT_RECT = {88, 72, UNDEFINED, UNDEFINED}; +static const Rect BLIND_TOKEN_TEXT_RECT = {80, 72, 200, 160 }; +static const Rect ROUND_END_MENU_RECT = {9, 7, 24, 20 }; + +static const BG_POINT CASHOUT_SRC_3X3_RECT_POS = {5, 29}; +// clang-format on + +static int substate; +static int blind_reward = 0; +static int hand_reward = 0; +static int interest_reward = 0; +static int interest_to_count = 0; +static int interest_start_time = UNDEFINED; + +static int calculate_interest_reward(void); + +static void game_round_end_start(void); +static void game_round_end_start_expand_popup(void); +static void game_round_end_display_finished_blind(void); +static void game_round_end_display_score_min(void); +static void game_round_end_update_blind_reward(void); +static void game_round_end_panel_exit(void); +static void game_round_end_display_rewards(void); +static void game_round_end_display_cashout(void); +static void game_round_end_dismiss_round_end_panel(void); + +static void game_round_end_extend_black_panel_down(int black_panel_bottom); + +static const SubStateActionFn round_end_state_actions[] = { + game_round_end_start, + game_round_end_start_expand_popup, + game_round_end_display_finished_blind, + game_round_end_display_score_min, + game_round_end_update_blind_reward, + game_round_end_panel_exit, + game_round_end_display_rewards, + game_round_end_display_cashout, + game_round_end_dismiss_round_end_panel +}; + +static int calculate_interest_reward(void) +{ + int reward = (g_game_vars.money / 5) * INTEREST_PER_5; + if (reward > MAX_INTEREST) + reward = MAX_INTEREST; + return reward; +} + +static void game_round_end_start(void) +{ + // Reset static variables to default values upon re-entering the round end state + if (g_game_vars.timer == TM_RESET_STATIC_VARS) + { + change_background(BG_ROUND_END, false); // Change the background to the round end background + substate = START_EXPAND_POPUP; // Change the state to the next one + g_game_vars.timer = TM_ZERO; // Reset the timer + blind_reward = blind_get_reward(g_game_vars.current_blind); + hand_reward = g_game_vars.hands; + interest_reward = calculate_interest_reward(); + interest_to_count = interest_reward; + interest_start_time = UNDEFINED; + } +} + +static void game_round_end_start_expand_popup(void) +{ + main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT, SCREEN_UP); + + if (g_game_vars.timer == TM_END_POP_MENU_ANIM) + { + substate = DISPLAY_FINISHED_BLIND; + g_game_vars.timer = TM_ZERO; + } +} + +static void game_round_end_display_finished_blind(void) +{ + obj_unhide(g_game_vars.round_end_blind_token->obj, 0); + + int current_ante = g_game_vars.ante; + + // Beating the boss blind increases the ante, so we need to display the previous ante value + if (g_game_vars.current_blind > BLIND_TYPE_BIG) + current_ante--; + + Rect blind_req_rect = ROUND_END_BLIND_REQ_RECT; + u32 blind_req = blind_get_requirement(g_game_vars.current_blind, current_ante); + + /* Not bothering to truncate here because there are 8 tiles + * and the blind requirement will not increase past ante 8 + * so there's enough room for sure. + */ + char blind_req_str_buff[UINT_MAX_DIGITS + 1]; + snprintf(blind_req_str_buff, sizeof(blind_req_str_buff), "%lu", blind_req); + + update_text_rect_to_right_align_str(&blind_req_rect, blind_req_str_buff, OVERFLOW_RIGHT); + + tte_printf( + "#{P:%d,%d; cx:0x%X000}%s", + blind_req_rect.left, + blind_req_rect.top, + TTE_RED_PB, + blind_req_str_buff + ); + + if (g_game_vars.timer == TM_START_ROUND_END_REWARDS_ANIM) + { + game_round_end_extend_black_panel_down(ROUND_END_BLACK_PANEL_INIT_BOTTOM_SE); + } + + if (g_game_vars.timer >= TM_END_DISPLAY_FIN_BLIND) + { + substate = DISPLAY_SCORE_MIN; + g_game_vars.timer = TM_ZERO; + } +} + +static void game_round_end_display_score_min(void) +{ + const int timer_offset = g_game_vars.timer - 1; + const int x_from = 0; + const int y_from = 29; + const int x_to = 13; + const int y_to = 11; + + memcpy16( + &se_mem[MAIN_BG_SBB][x_to + timer_offset + 32 * y_to], + &se_mem[MAIN_BG_SBB][x_from + timer_offset + 32 * y_from], + 1 + ); + + if (g_game_vars.timer >= TM_END_DISPLAY_SCORE_MIN) + { + substate = UPDATE_BLIND_REWARD; + g_game_vars.timer = TM_ZERO; + } +} + +static void game_round_end_update_blind_reward(void) +{ + if (g_game_vars.timer % FRAMES(20) != 0) + return; + + // TODO: Add sound effect here + + if (blind_reward > 0) + { + blind_reward--; + tte_printf( + "#{P:%d,%d; cx:0x%X000}$%d", + BLIND_REWARD_RECT.left, + BLIND_REWARD_RECT.top, + TTE_YELLOW_PB, + blind_reward + ); + tte_printf( + "#{P:%d,%d; cx:0x%X000}$%d", + ROUND_END_BLIND_REWARD_RECT.left, + ROUND_END_BLIND_REWARD_RECT.top, + TTE_YELLOW_PB, + blind_get_reward(g_game_vars.current_blind) - blind_reward + ); + } + else if (g_game_vars.timer > FRAMES(20)) + { + tte_erase_rect_wrapper(BLIND_REWARD_RECT); + tte_erase_rect_wrapper(BLIND_REQ_TEXT_RECT); + obj_hide(g_game_vars.playing_blind_token->obj); + affine_background_load_palette(affine_background_gfxPal); + substate = BLIND_PANEL_EXIT; + g_game_vars.timer = TM_ZERO; + } +} + +static void game_round_end_panel_exit(void) +{ + // TODO: make heads or tails of what's going on here and replace + // magic numbers. + if (g_game_vars.timer < 8) + { + main_bg_se_copy_rect_1_tile_vert(TOP_LEFT_PANEL_ANIM_RECT, SCREEN_UP); + + if (g_game_vars.timer == 1) + { + reset_top_left_panel_bottom_row(); + } + else if (g_game_vars.timer == 2) + { + int y = 5; + memset16(&se_mem[MAIN_BG_SBB][32 * (y - 1)], 0x0001, 1); + memset16(&se_mem[MAIN_BG_SBB][1 + 32 * (y - 1)], 0x0002, 7); + memset16(&se_mem[MAIN_BG_SBB][8 + 32 * (y - 1)], 0x0401, 1); + } + } + else if (g_game_vars.timer > FRAMES(20)) + { + memset16(&pal_bg_mem[REWARD_PANEL_BORDER_PID], 0x1483, 1); + substate = DISPLAY_REWARDS; + g_game_vars.timer = TM_ZERO; + } +} + +static inline void game_round_end_print_separator_ellipsis(void) +{ + int x = + (ROUND_END_REWARDS_ELLIPSIS_POS.x + g_game_vars.timer - TM_REWARDS_ELLIPSIS_PRINT_START) * + TILE_SIZE; + int y = (ROUND_END_REWARDS_ELLIPSIS_POS.y) * TILE_SIZE; + + tte_printf("#{P:%d,%d; cx:0x%X000}.", x, y, TTE_WHITE_PB); +} + +// TODO: Allow for more generic rewards and consolidate with game_round_end_print_interest_reward() +static inline void game_round_end_print_hand_reward(int hand_y_offset) +{ + int hand_y = ROUND_END_REWARDS_ELLIPSIS_POS.y + hand_y_offset; + if (g_game_vars.timer == TM_DISPLAY_REWARDS_CONT_WAIT) + { + game_round_end_extend_black_panel_down(hand_y); + + tte_printf( + "#{P:%lu,%d; cx:0x%X000}%d #{cx:0x%X000}Hands", + ROUND_END_REWARD_TEXT_X, + hand_y * TILE_SIZE, + TTE_BLUE_PB, + hand_reward, + TTE_WHITE_PB + ); + } + // Increment the hand reward text until the hand reward variable is depleted + else if (g_game_vars.timer > TM_HAND_REWARD_INCR_WAIT && + g_game_vars.timer % FRAMES(TM_REWARD_INCREMENT_INTERVAL) == 0) + { + hand_reward--; + tte_printf( + "#{P:%lu, %d; cx:0x%X000}$%ld", + ROUND_END_REWARD_AMOUNT_X, + hand_y * TILE_SIZE, + TTE_YELLOW_PB, + g_game_vars.hands - hand_reward + ); + if (hand_reward == 0) + { + interest_start_time = g_game_vars.timer + TM_REWARD_DISPLAY_INTERVAL; + } + } +} + +static inline void game_round_end_print_interest_reward(int interest_y_offset) +{ + int interest_y = ROUND_END_REWARDS_ELLIPSIS_POS.y + interest_y_offset; + + if (g_game_vars.timer == interest_start_time) + { + game_round_end_extend_black_panel_down(interest_y); + + tte_printf( + "#{P:%lu,%d; cx:0x%X000}%d #{cx:0x%X000}Interest", + ROUND_END_REWARD_TEXT_X, + interest_y * TILE_SIZE, + TTE_YELLOW_PB, + interest_reward, + TTE_WHITE_PB + ); + } + // Increment the interest reward text until the interest reward variable is depleted + else if (g_game_vars.timer > interest_start_time + TM_REWARD_DISPLAY_INTERVAL && + g_game_vars.timer % FRAMES(TM_REWARD_INCREMENT_INTERVAL) == 0) + { + interest_to_count--; + tte_printf( + "#{P:%lu, %d; cx:0x%X000}$%d", + ROUND_END_REWARD_AMOUNT_X, + interest_y * TILE_SIZE, + TTE_YELLOW_PB, + interest_reward - interest_to_count + ); + } +} + +static void game_round_end_display_rewards(void) +{ + int hand_y_offset = 0; + int interest_y_offset = 0; + + if (g_game_vars.hands > 0) + { + hand_y_offset = 1; + } + else + { + interest_start_time = TM_DISPLAY_REWARDS_CONT_WAIT; + } + + if (interest_reward > 0) + { + interest_y_offset = hand_y_offset + 1; + } + + // Once all rewards are accounted for go to the next state + if (hand_reward <= 0 && interest_to_count <= 0) + { + g_game_vars.timer = TM_ZERO; + substate = DISPLAY_CASHOUT; + } + else if (g_game_vars.timer == TM_START_ROUND_END_REWARDS_ANIM) + { + game_round_end_extend_black_panel_down(ROUND_END_REWARDS_ELLIPSIS_POS.y); + } + else if (g_game_vars.timer < TM_REWARDS_ELLIPSIS_PRINT_END) + { + game_round_end_print_separator_ellipsis(); + } + else if (g_game_vars.timer >= TM_DISPLAY_REWARDS_CONT_WAIT && hand_reward > 0) + { + game_round_end_print_hand_reward(hand_y_offset); + } + else if (interest_start_time != UNDEFINED && g_game_vars.timer >= interest_start_time && + interest_to_count > 0) + { + game_round_end_print_interest_reward(interest_y_offset); + } +} + +static inline void game_round_end_cashout(void) +{ + // Reward the player + g_game_vars.money += g_game_vars.hands + blind_get_reward(g_game_vars.current_blind) + + calculate_interest_reward(); + display_money(); + + g_game_vars.hands = MAX_HANDS; // Reset the hands to the maximum + g_game_vars.discards = MAX_DISCARDS; // Reset the discards to the maximum + // TODO: these can just be in one spot, passing global to global + display_hands(); // Set the hands display + display_discards(); // Set the discards display + + g_game_vars.score = 0; + display_score(g_game_vars.score); // Set the score display +} + +static void game_round_end_display_cashout() +{ + if (g_game_vars.timer == FRAMES(40)) + { + // Put the "cash out" button onto the round end panel + main_bg_se_copy_expand_3x3_rect(CASHOUT_DEST_RECT, CASHOUT_SRC_3X3_RECT_POS); + + int cashout_amount = g_game_vars.hands + blind_get_reward(g_game_vars.current_blind) + + calculate_interest_reward(); + + bool omit_space = cashout_amount >= 10; + tte_printf( + "#{P:%d, %d; cx:0x%X000}Cash Out:%s$%d", + CASHOUT_TEXT_RECT.left, + CASHOUT_TEXT_RECT.top, + TTE_WHITE_PB, + omit_space ? "" : " ", + cashout_amount + ); + } + + // Wait until the player presses A to cash out + else if (g_game_vars.timer > FRAMES(40) && key_hit(SELECT_CARD)) + { + game_round_end_cashout(); + + substate = DISMISS_ROUND_END_PANEL; // Go to the next state + g_game_vars.timer = TM_ZERO; + + obj_hide(g_game_vars.round_end_blind_token->obj); // Hide the blind token object + tte_erase_rect_wrapper(BLIND_TOKEN_TEXT_RECT); // Erase the blind token text + } +} + +static void game_round_end_dismiss_round_end_panel(void) +{ + Rect round_end_down = ROUND_END_MENU_RECT; + round_end_down.top--; + main_bg_se_copy_rect_1_tile_vert(round_end_down, SCREEN_DOWN); + + if (g_game_vars.timer >= TM_DISMISS_ROUND_END_TM) + { + g_game_vars.timer = TM_ZERO; + substate = ROUND_END_EXIT; + } +} + +static void game_round_end_extend_black_panel_down(int black_panel_bottom) +{ + Rect single_line_rect = ROUND_END_MENU_RECT; + single_line_rect.bottom = black_panel_bottom; + single_line_rect.top = single_line_rect.bottom - 1; + main_bg_se_copy_rect_1_tile_vert(single_line_rect, SCREEN_DOWN); +} + +void game_round_end_change_background(void) +{ + // Disable window 0 so it doesn't make the cashout menu transparent + toggle_windows(false, true); + + main_bg_se_clear_rect(ROUND_END_MENU_RECT); + tte_erase_rect_wrapper(HAND_SIZE_RECT); +} + +void game_round_end_on_init(void) +{ + substate = ROUND_END_START; + g_game_vars.timer = 0; +} + +void game_round_end_on_update(void) +{ + if (substate == ROUND_END_EXIT) + { + game_change_state(GAME_STATE_SHOP); + return; + } + + round_end_state_actions[substate](); +} + +void game_round_end_on_exit(void) +{ + // Cleanup blind tokens from this round to avoid accumulating + // allocated blind sprites each round + blind_reward = 0; + hand_reward = 0; + interest_reward = 0; + sprite_destroy(&g_game_vars.playing_blind_token); + sprite_destroy(&g_game_vars.round_end_blind_token); + // TODO: Reuse sprites for blind selection? +}