Refactor/game over screen (#468)

* Move game over (win/lose) state functions to their own file

* remove unused enum

* clang format

* Implement suggestions from @ricfehr3

* Fixing lack of highlighted button after skipping  ablind

* factorize as much game over state code as possible

---------

Co-authored-by: MathisMartin31 <mathis.martin31@gmail.com>
This commit is contained in:
Geralt
2026-05-07 21:36:12 -07:00
committed by GitHub
co-authored by MathisMartin31
parent 9561e0e738
commit 6373fee144
6 changed files with 225 additions and 147 deletions
+51 -145
View File
@@ -12,6 +12,7 @@
#include "card.h"
#include "game/blind_select.h"
#include "game/common_ui.h"
#include "game/game_over.h"
#include "game/main_menu.h"
#include "game/options_menu.h"
#include "game_variables.h"
@@ -45,9 +46,6 @@
// SE sizes
#define ROUND_END_BLACK_PANEL_INIT_BOTTOM_SE 12
// TODO: Properly define and use
#define GAME_OVER_ANIM_FRAMES 15
#define SHOP_LIGHTS_1_CLR 0xFFFF
#define SHOP_LIGHTS_2_CLR 0x32BE
#define SHOP_LIGHTS_3_CLR 0x4B5F
@@ -184,11 +182,6 @@ 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_lose_on_init(void);
static void game_lose_on_update(void);
static void game_over_on_exit(void);
static void game_win_on_init(void);
static void game_win_on_update(void);
static void game_shop_intro(void);
static void game_shop_process_user_input(void);
static void game_shop_outro(void);
@@ -211,14 +204,12 @@ static void display_discards(int value);
static void set_hand(void);
static int deck_get_size(void);
static int deck_get_max_size(void);
static void game_over_init(void);
static bool check_and_score_joker_for_event(
ListItr* starting_joker_itr,
CardObject* card_object,
enum JokerEvent joker_event
);
static int calculate_interest_reward(void);
static void game_over_anim_frame(void);
static void game_playing_discard_on_pressed(void);
static void game_playing_execute_discard(void);
@@ -317,11 +308,6 @@ 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 GAME_OVER_SRC_RECT_3X3_POS = {25, 29};
static const Rect GAME_OVER_DIALOG_DEST_RECT= {11, 21, 23, 28};
static const Rect GAME_OVER_ANIM_RECT = {11, 8, 23, 28};
static const BG_POINT NEW_RUN_BTN_DEST_POS = {15, 26};
static const Rect NEW_RUN_BTN_SRC_RECT = {0, 30, 4, 31};
static const BG_POINT ROUND_END_REWARDS_ELLIPSIS_POS = {10, 13};
// Flaming score animation frames
@@ -363,9 +349,6 @@ static const Rect ROUND_END_BLIND_REQ_RECT = {104, 96, 136, UNDEF
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 Rect GAME_LOSE_MSG_TEXT_RECT = {104, 72, UNDEFINED, UNDEFINED};
// 1 character to the right of GAME_LOSE
static const Rect GAME_WIN_MSG_TEXT_RECT = {112, 72, UNDEFINED, UNDEFINED};
static const BG_POINT HELD_JOKERS_POS = {108, 10};
static const BG_POINT JOKER_DISCARD_TARGET = {240, 30};
@@ -697,6 +680,51 @@ void game_init()
init_unbeaten_blinds_list(true);
}
void game_reset()
{
while (list_get_len(&_owned_jokers_list) > 0)
{
JokerObject* joker_object = list_get_at_idx(&_owned_jokers_list, 0);
remove_owned_joker(0);
joker_object_destroy(&joker_object);
}
tte_erase_screen();
// 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);
list_clear(&_owned_jokers_list);
list_clear(&_discarded_jokers_list);
list_clear(&_expired_jokers_list);
list_clear(&_shop_jokers_list);
game_init();
display_round();
display_score(score);
display_chips();
display_mult();
display_hands(hands);
display_discards(discards);
display_money();
// Ante
tte_printf(
"#{P:%d,%d; cx:0x%X000}%d#{cx:0x%X000}/%d",
ANTE_TEXT_RECT.left,
ANTE_TEXT_RECT.top,
TTE_YELLOW_PB,
g_game_vars.ante,
TTE_WHITE_PB,
MAX_ANTE
);
affine_background_load_palette(affine_background_gfxPal);
}
static inline void discarded_jokers_update_loop(void)
{
if (list_is_empty(&_discarded_jokers_list))
@@ -1458,6 +1486,11 @@ void change_background_legacy(enum BackgroundId id)
background_legacy = id;
}
void reset_background(void)
{
background_legacy = BG_NONE;
}
static void display_temp_score(u32 value)
{
char temp_score_str_buff[UINT_MAX_DIGITS + 1];
@@ -1682,28 +1715,6 @@ static void game_round_on_init(void)
game_playing_selection_grid.selection = GAME_PLAYING_INIT_SEL;
}
static void game_over_init(void)
{
// Clears the round end menu
main_bg_se_clear_rect(POP_MENU_ANIM_RECT);
main_bg_se_copy_expand_3x3_rect(GAME_OVER_DIALOG_DEST_RECT, GAME_OVER_SRC_RECT_3X3_POS);
main_bg_se_copy_rect(NEW_RUN_BTN_SRC_RECT, NEW_RUN_BTN_DEST_POS);
}
static void game_lose_on_init(void)
{
game_over_init();
// Using the text color to match the "Game Over" text
affine_background_set_color(TEXT_CLR_RED);
}
static void game_win_on_init(void)
{
game_over_init();
// Using the text color to match the "You Win" text
affine_background_set_color(TEXT_CLR_BLUE);
}
// General functions
static inline void set_seed(int seed)
{
@@ -4292,108 +4303,3 @@ void game_start(void)
game_change_state(GAME_STATE_BLIND_SELECT);
}
static void game_over_anim_frame(void)
{
main_bg_se_move_rect_1_tile_vert(GAME_OVER_ANIM_RECT, SCREEN_UP);
}
static inline void game_over_process_user_input()
{
if (key_hit(SELECT_CARD))
{
play_sfx(SFX_BUTTON, MM_BASE_PITCH_RATE, BUTTON_SFX_VOLUME);
game_change_state(GAME_STATE_BLIND_SELECT);
}
}
static void game_lose_on_update(void)
{
if (g_game_vars.timer < GAME_OVER_ANIM_FRAMES)
{
game_over_anim_frame();
}
else if (g_game_vars.timer == GAME_OVER_ANIM_FRAMES)
{
tte_printf(
"#{P:%d,%d; cx:0x%X000}GAME OVER",
GAME_LOSE_MSG_TEXT_RECT.left,
GAME_LOSE_MSG_TEXT_RECT.top,
TTE_RED_PB
);
}
game_over_process_user_input();
}
// This function isn't set in stone. This is just a placeholder
// allowing the player to restart the game. Thought it would be nice to have
// util we decide what we want to do after a game over.
static void game_over_on_exit(void)
{
while (list_get_len(&_owned_jokers_list) > 0)
{
JokerObject* joker_object = list_get_at_idx(&_owned_jokers_list, 0);
remove_owned_joker(0);
joker_object_destroy(&joker_object);
}
tte_erase_screen();
// 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);
list_clear(&_owned_jokers_list);
list_clear(&_discarded_jokers_list);
list_clear(&_expired_jokers_list);
list_clear(&_shop_jokers_list);
game_init();
display_round();
display_score(score);
display_chips();
display_mult();
display_hands(hands);
display_discards(discards);
display_money();
// Ante
tte_printf(
"#{P:%d,%d; cx:0x%X000}%d#{cx:0x%X000}/%d",
ANTE_TEXT_RECT.left,
ANTE_TEXT_RECT.top,
TTE_YELLOW_PB,
g_game_vars.ante,
TTE_WHITE_PB,
MAX_ANTE
);
affine_background_load_palette(affine_background_gfxPal);
}
static void game_win_on_update(void)
{
if (g_game_vars.timer < GAME_OVER_ANIM_FRAMES)
{
game_over_anim_frame();
}
else if (g_game_vars.timer == GAME_OVER_ANIM_FRAMES)
{
tte_printf(
"#{P:%d,%d; cx:0x%X000}YOU WIN",
GAME_WIN_MSG_TEXT_RECT.left,
GAME_WIN_MSG_TEXT_RECT.top,
TTE_BLUE_PB
);
}
game_over_process_user_input();
}
void reset_background(void)
{
background_legacy = BG_NONE;
}
+1
View File
@@ -222,6 +222,7 @@ static void game_blind_select_handle_input()
}
game_blind_select_print_blinds_reqs_and_rewards();
highlight_select_button();
timer = TM_ZERO;
}
+131
View File
@@ -0,0 +1,131 @@
#include "game/game_over.h"
#include "affine_background.h"
#include "audio_utils.h"
#include "button.h"
#include "game.h"
#include "game/main_menu.h"
#include "graphic_utils.h"
#include "layout.h"
#include "soundbank.h"
#include "timer.h"
#include "util.h"
#include <tonc.h>
enum EndCondition
{
END_CONDITION_NONE,
END_CONDITION_WIN,
END_CONDITION_LOSS
};
// clang-format off
static const BG_POINT GAME_OVER_SRC_RECT_3X3_POS = { 25, 29};
static const Rect GAME_OVER_DIALOG_DEST_RECT = { 11, 21, 23, 28};
static const Rect GAME_OVER_ANIM_RECT = { 11, 8, 23, 28};
static const Rect GAME_LOSE_MSG_TEXT_RECT = {104, 72, UNDEFINED, UNDEFINED};
// 1 character to the right of GAME_LOSE
static const Rect GAME_WIN_MSG_TEXT_RECT = {112, 72, UNDEFINED, UNDEFINED};
static const BG_POINT NEW_RUN_BTN_DEST_POS = { 15, 26};
static const Rect NEW_RUN_BTN_SRC_RECT = { 0, 30, 4, 31};
// clang-format on
static const u32 GAME_OVER_ANIM_FRAMES = 15;
static enum EndCondition condition = END_CONDITION_NONE;
static u32 timer = TM_ZERO;
static void game_over_init(enum EndCondition init_condition)
{
condition = init_condition;
// Clears the round end menu
main_bg_se_clear_rect(POP_MENU_ANIM_RECT);
main_bg_se_copy_expand_3x3_rect(GAME_OVER_DIALOG_DEST_RECT, GAME_OVER_SRC_RECT_3X3_POS);
main_bg_se_copy_rect(NEW_RUN_BTN_SRC_RECT, NEW_RUN_BTN_DEST_POS);
// Using the text color to match the Win/Loss condition
switch (condition)
{
case END_CONDITION_WIN:
affine_background_set_color(TEXT_CLR_BLUE);
break;
case END_CONDITION_LOSS:
affine_background_set_color(TEXT_CLR_RED);
break;
default:
break;
}
timer = TM_ZERO;
}
void game_win_on_init(void)
{
game_over_init(END_CONDITION_WIN);
}
void game_lose_on_init(void)
{
game_over_init(END_CONDITION_LOSS);
}
/**
* @brief Makes the message background frame move 1 tile up, called until it is in its place.
*/
static inline void game_over_anim_frame(void)
{
main_bg_se_move_rect_1_tile_vert(GAME_OVER_ANIM_RECT, SCREEN_UP);
}
/**
* @brief Polls user input to detect "Retry" button press.
*/
static inline void game_over_process_user_input()
{
if (key_hit(SELECT_CARD))
{
play_sfx(SFX_BUTTON, MM_BASE_PITCH_RATE, BUTTON_SFX_VOLUME);
game_change_state(GAME_STATE_BLIND_SELECT);
}
}
void game_over_on_update(void)
{
timer++;
if (timer < GAME_OVER_ANIM_FRAMES)
{
game_over_anim_frame();
}
else if (timer == GAME_OVER_ANIM_FRAMES)
{
switch (condition)
{
case END_CONDITION_WIN:
tte_printf(
"#{P:%d,%d; cx:0x%X000}YOU WIN",
GAME_WIN_MSG_TEXT_RECT.left,
GAME_WIN_MSG_TEXT_RECT.top,
TTE_BLUE_PB
);
break;
case END_CONDITION_LOSS:
tte_printf(
"#{P:%d,%d; cx:0x%X000}GAME OVER",
GAME_LOSE_MSG_TEXT_RECT.left,
GAME_LOSE_MSG_TEXT_RECT.top,
TTE_RED_PB
);
break;
default:
break;
}
}
game_over_process_user_input();
}
void game_over_on_exit(void)
{
condition = END_CONDITION_NONE;
game_reset();
}