Refactor/state machine (#470)
* initial additions * Move current_blind to game vars * tmp * tmp * tmp * tmp * tmp * tmp * tmp * Compile but is broken * temp fix for token location * tmp * Working, lets clean up * remove todo * Remove odd reroll blind variable * fix skipping blind screen * fix reroll * remove magic for blind token locations * clang format * Cleanup button highlight in blind menu * some cleanup * Add layout file for global UI rects * remove dead code * Clang format * clang format it up * Move reset top left funct to gfx utils * make note of soon-to-be removed vars * Cleanup gamevars declaration * Cleanup layout file * Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Add include for stdbool.h in common_ui.h * Cleanup rect declaration * Start moving state machine stuff to new file * quick tmp * oops, add new files * working call list * fixed pointer stuff * working as per norm * working replacement * move defines to static const * fix compile issue * Add some missing "void"s * Working new substates * add statemachine to blind select * Fix game restart statemachine leak * clang-format * Clang format * Rework shop exit logic * Use new state machine in shop * Remove substates entirely * Remove debug stuff * Clang format * document state machine better * update list tests * clang-format * Update for PR * Claaaaang format * another clang-format * Remove extra state in round_end * cleanup docs for removals in lists * clang-format... again * update doxygen format * Clang format * Update list docs * Update lists tests * Fix bad conflict resolution --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
Copilot Autofix powered by AI
parent
2329c4c681
commit
92cb9f9883
+3
-6
@@ -40,9 +40,6 @@ typedef struct CardObject CardObject;
|
|||||||
typedef struct Card Card;
|
typedef struct Card Card;
|
||||||
typedef struct JokerObject JokerObject;
|
typedef struct JokerObject JokerObject;
|
||||||
|
|
||||||
typedef void (*GameStateCallback)(void);
|
|
||||||
typedef void (*SubStateActionFn)(void);
|
|
||||||
|
|
||||||
// Enum value names in ../include/def_state_info_table.h
|
// Enum value names in ../include/def_state_info_table.h
|
||||||
enum GameState
|
enum GameState
|
||||||
{
|
{
|
||||||
@@ -67,7 +64,7 @@ enum PlayState
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Game functions
|
// Game functions
|
||||||
void game_init();
|
void game_init(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Called when exiting the Game Over screen (both win or lose) to reset game variables
|
* @brief Called when exiting the Game Over screen (both win or lose) to reset game variables
|
||||||
@@ -77,9 +74,9 @@ void game_init();
|
|||||||
* and shouldn't be called from other states, otherwise some data such as shop jokers
|
* and shouldn't be called from other states, otherwise some data such as shop jokers
|
||||||
* may not be properly reset.
|
* may not be properly reset.
|
||||||
*/
|
*/
|
||||||
void game_reset();
|
void game_reset(void);
|
||||||
|
|
||||||
void game_update();
|
void game_update(void);
|
||||||
void game_change_state(enum GameState new_game_state);
|
void game_change_state(enum GameState new_game_state);
|
||||||
|
|
||||||
CardObject** get_played_array(void);
|
CardObject** get_played_array(void);
|
||||||
|
|||||||
+30
-5
@@ -23,6 +23,13 @@
|
|||||||
*/
|
*/
|
||||||
#define MAX_LIST_NODES 128
|
#define MAX_LIST_NODES 128
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default list declaration for empty lists
|
||||||
|
*/
|
||||||
|
// clang-format off
|
||||||
|
#define LIST_DEFAULT { .head = NULL, .tail = NULL, .len = 0 }
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
typedef struct ListNode ListNode;
|
typedef struct ListNode ListNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -106,15 +113,16 @@ typedef struct
|
|||||||
} ListItr;
|
} ListItr;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a list.
|
* Initialize a list.
|
||||||
*
|
*
|
||||||
* While this function does not allocate memory for the list itself, the list does allocate memory
|
* Set the values of a list to default.
|
||||||
* for each element. So every created list must be freed with @ref list_clear to ensure the list's
|
*
|
||||||
* nodes are deleted properly.
|
* If using this function to reset a list, the list must be freed with @ref list_clear to ensure the
|
||||||
|
* list's nodes are deleted properly.
|
||||||
*
|
*
|
||||||
* @return A @ref List with head and tail reset.
|
* @return A @ref List with head and tail reset.
|
||||||
*/
|
*/
|
||||||
List list_create(void);
|
List list_init(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear a list.
|
* Clear a list.
|
||||||
@@ -122,6 +130,8 @@ List list_create(void);
|
|||||||
* Go through the list and free each node and set the `head` and `tail` to `NULL`.
|
* Go through the list and free each node and set the `head` and `tail` to `NULL`.
|
||||||
* Note, it doesn't "free" the data at the node.
|
* Note, it doesn't "free" the data at the node.
|
||||||
*
|
*
|
||||||
|
* @note To reset an existing list to default values, first call `list_clear` then @ref list_init
|
||||||
|
*
|
||||||
* @param list pointer to a @ref List to clear
|
* @param list pointer to a @ref List to clear
|
||||||
*/
|
*/
|
||||||
void list_clear(List* list);
|
void list_clear(List* list);
|
||||||
@@ -222,6 +232,18 @@ void* list_get_at_idx(List* list, unsigned int idx);
|
|||||||
*/
|
*/
|
||||||
bool list_remove_at_idx(List* list, unsigned int idx);
|
bool list_remove_at_idx(List* list, unsigned int idx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a List's node with the matching pointer
|
||||||
|
*
|
||||||
|
* @param list pointer to a @ref List
|
||||||
|
* @param data pointer to data in node in list
|
||||||
|
*
|
||||||
|
* @return `true` if successfully removed, `false` otherwise
|
||||||
|
*
|
||||||
|
* @note When working with @ref ListItr, use @ref list_itr_remove_current_node()
|
||||||
|
*/
|
||||||
|
bool list_remove_data(List* list, void* data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the number of elements in a @ref List
|
* Get the number of elements in a @ref List
|
||||||
*
|
*
|
||||||
@@ -265,6 +287,9 @@ void* list_itr_next(ListItr* itr);
|
|||||||
* most recently returned valu from @ref list_itr_next()
|
* most recently returned valu from @ref list_itr_next()
|
||||||
*
|
*
|
||||||
* @param itr pointer to the @ref ListItr
|
* @param itr pointer to the @ref ListItr
|
||||||
|
*
|
||||||
|
* @note When working with @ref ListItr, use this and not @ref list_remove_at() as it will
|
||||||
|
* "break" the iterator.
|
||||||
*/
|
*/
|
||||||
void list_itr_remove_current_node(ListItr* itr);
|
void list_itr_remove_current_node(ListItr* itr);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
/**
|
||||||
|
* @file state_machine.h
|
||||||
|
*
|
||||||
|
* @brief State Machine
|
||||||
|
*
|
||||||
|
* This file is the interface into a generic state machine system.
|
||||||
|
*
|
||||||
|
* State machines are defined as an array of function callbacks where each state
|
||||||
|
* is an index in the array with three functions per state: `on_init()`,
|
||||||
|
* `on_update()`, and `on_exit()`.
|
||||||
|
*
|
||||||
|
* **`on_init()`** Ran once when transitioning into the new state
|
||||||
|
* **`on_update()`** Ran once every frame
|
||||||
|
* **`on_exit()`** Ran once when exiting a state, for cleanup.
|
||||||
|
*
|
||||||
|
* The state machine `on_update()` function is "registered" to a linked-list of
|
||||||
|
* other state machine update functions. This list will call the state machines
|
||||||
|
* active `on_update()` function. This allows much of the complexity of state
|
||||||
|
* transitions to remain central to `state_machine.c`. Also, multiple state
|
||||||
|
* machines can be registered to this list. This allows using substates within
|
||||||
|
* states, or have one off state machines like animation controllers.
|
||||||
|
*
|
||||||
|
* When a state machine is finished, it can "remove" itself from the main update
|
||||||
|
* callback list. This can be done within the update method of its own state
|
||||||
|
* machine. This can be used to start a self destructing state machine.
|
||||||
|
*/
|
||||||
|
#ifndef STATE_MACHINE_H
|
||||||
|
#define STATE_MACHINE_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief State machine callback function pointer type
|
||||||
|
*/
|
||||||
|
typedef void (*StateCallback)(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief State machine callbacks
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
StateCallback on_init;
|
||||||
|
StateCallback on_update;
|
||||||
|
StateCallback on_exit;
|
||||||
|
} StateInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief State machine instance
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief Pointer to the active update function in `state_infos`
|
||||||
|
*/
|
||||||
|
StateCallback active_update;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Array of @ref StateCallbacks , one entry per state
|
||||||
|
*/
|
||||||
|
StateInfo* state_infos;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Number of elements in the `state_infos` array
|
||||||
|
*/
|
||||||
|
unsigned int num_infos;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The current state of the state machine, the offset into state_infos
|
||||||
|
*/
|
||||||
|
int state;
|
||||||
|
} StateMachine;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Register a statemachine to run it's update function once per frame
|
||||||
|
*
|
||||||
|
* @param state_machine pointer to @ref StateMachine to register, cannot be NULL
|
||||||
|
*/
|
||||||
|
void state_machine_register(StateMachine* state_machine);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove a statemachine's update function
|
||||||
|
*
|
||||||
|
* @param state_machine pointer to @ref StateMachine to remove, cannot be NULL
|
||||||
|
*/
|
||||||
|
void state_machine_remove(StateMachine* state_machine);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update registered state machines' update functions
|
||||||
|
*/
|
||||||
|
void state_machine_update(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calls the current state's on_exit, the new state's on_init, and sets the active update fn
|
||||||
|
*
|
||||||
|
* @param state_machine pointer to @ref StateMachine, cannot be NULL
|
||||||
|
* @param new_state offset into `state_infos` array to transition to
|
||||||
|
*/
|
||||||
|
void state_machine_change_state(StateMachine* state_machine, int new_state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief no operation
|
||||||
|
*/
|
||||||
|
void noop(void);
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
#define STATE_INFO_UPDATE_FN_ONLY(fn) {.on_init = noop, .on_update = fn, .on_exit = noop}
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
#endif // STATE_MACHINE_H
|
||||||
+2
-2
@@ -111,8 +111,8 @@ void init_unbeaten_blinds_list(bool showdown)
|
|||||||
if (!init)
|
if (!init)
|
||||||
{
|
{
|
||||||
init = true;
|
init = true;
|
||||||
unbeaten_showdown_blinds = list_create();
|
unbeaten_showdown_blinds = list_init();
|
||||||
unbeaten_boss_blinds = list_create();
|
unbeaten_boss_blinds = list_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
List* p_unbeaten_blinds = showdown ? &unbeaten_showdown_blinds : &unbeaten_boss_blinds;
|
List* p_unbeaten_blinds = showdown ? &unbeaten_showdown_blinds : &unbeaten_boss_blinds;
|
||||||
|
|||||||
+24
-53
@@ -27,6 +27,7 @@
|
|||||||
#include "soundbank.h"
|
#include "soundbank.h"
|
||||||
#include "splash_screen.h"
|
#include "splash_screen.h"
|
||||||
#include "sprite.h"
|
#include "sprite.h"
|
||||||
|
#include "state_machine.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "tonc_memdef.h"
|
#include "tonc_memdef.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
@@ -86,15 +87,6 @@
|
|||||||
|
|
||||||
#define EXPIRE_ANIMATION_FRAME_COUNT 3
|
#define EXPIRE_ANIMATION_FRAME_COUNT 3
|
||||||
|
|
||||||
// Used as a No Operation for game states that have no init and/or exit function.
|
|
||||||
// ricfehr3 did the work of determining whether a noop or a NULL check was more
|
|
||||||
// efficient. Well, this is the answer.
|
|
||||||
// Thanks!
|
|
||||||
// https://github.com/cellos51/balatro-gba/issues/137#issuecomment-3322485129
|
|
||||||
static void noop(void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// These functions need to be forward declared
|
// These functions need to be forward declared
|
||||||
// so they're visible to the state_info array,
|
// so they're visible to the state_info array,
|
||||||
// and the sub-state function tables.
|
// and the sub-state function tables.
|
||||||
@@ -212,8 +204,20 @@ static const BG_POINT HAND_PLAY_POS = {120, 70};
|
|||||||
// variable and handling in common.c once the related refactor is finished
|
// variable and handling in common.c once the related refactor is finished
|
||||||
static enum BackgroundId background_legacy = BG_NONE;
|
static enum BackgroundId background_legacy = BG_NONE;
|
||||||
|
|
||||||
|
static StateInfo state_info[] = {
|
||||||
|
#define DEF_STATE_INFO(stateEnum, init_fn, update_fn, exit_fn) \
|
||||||
|
{.on_init = init_fn, .on_update = update_fn, .on_exit = exit_fn},
|
||||||
|
#include "../include/def_state_info_table.h"
|
||||||
|
#undef DEF_STATE_INFO
|
||||||
|
};
|
||||||
|
|
||||||
|
static StateMachine game_sm = {
|
||||||
|
.state_infos = &state_info[0],
|
||||||
|
.num_infos = GAME_STATE_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
SelectionGridRow game_playing_selection_rows[] = {
|
static SelectionGridRow game_playing_selection_rows[] = {
|
||||||
{
|
{
|
||||||
0,
|
0,
|
||||||
jokers_sel_row_get_size,
|
jokers_sel_row_get_size,
|
||||||
@@ -240,14 +244,14 @@ SelectionGridRow game_playing_selection_rows[] = {
|
|||||||
|
|
||||||
static const Selection GAME_PLAYING_INIT_SEL = {0, 1};
|
static const Selection GAME_PLAYING_INIT_SEL = {0, 1};
|
||||||
|
|
||||||
SelectionGrid game_playing_selection_grid = {
|
static SelectionGrid game_playing_selection_grid = {
|
||||||
game_playing_selection_rows,
|
game_playing_selection_rows,
|
||||||
NUM_ELEM_IN_ARR(game_playing_selection_rows),
|
NUM_ELEM_IN_ARR(game_playing_selection_rows),
|
||||||
GAME_PLAYING_INIT_SEL
|
GAME_PLAYING_INIT_SEL
|
||||||
};
|
};
|
||||||
|
|
||||||
// Array of buttons by horizontal selection index (x)
|
// Array of buttons by horizontal selection index (x)
|
||||||
Button game_playing_buttons[] = {
|
static Button game_playing_buttons[] = {
|
||||||
{PLAY_HAND_BTN_BORDER_PAL_IDX, PLAY_HAND_BTN_PAL_IDX, game_playing_play_hand_on_pressed, can_play_hand },
|
{PLAY_HAND_BTN_BORDER_PAL_IDX, PLAY_HAND_BTN_PAL_IDX, game_playing_play_hand_on_pressed, can_play_hand },
|
||||||
{SORT_BY_RANK_BTN_BORDER_PAL_IDX, SORT_BTNS_PAL_IDX, game_playing_sort_by_rank_on_pressed, NULL },
|
{SORT_BY_RANK_BTN_BORDER_PAL_IDX, SORT_BTNS_PAL_IDX, game_playing_sort_by_rank_on_pressed, NULL },
|
||||||
{SORT_BY_SUIT_BTN_BORDER_PAL_IDX, SORT_BTNS_PAL_IDX, game_playing_sort_by_suit_on_pressed, NULL },
|
{SORT_BY_SUIT_BTN_BORDER_PAL_IDX, SORT_BTNS_PAL_IDX, game_playing_sort_by_suit_on_pressed, NULL },
|
||||||
@@ -258,8 +262,6 @@ Button game_playing_buttons[] = {
|
|||||||
static const int HAND_SPACING_LUT[MAX_HAND_SIZE] =
|
static const int HAND_SPACING_LUT[MAX_HAND_SIZE] =
|
||||||
{28, 28, 28, 28, 27, 21, 18, 15, 13, 12, 10, 9, 9, 8, 8, 7};
|
{28, 28, 28, 28, 27, 21, 18, 15, 13, 12, 10, 9, 9, 8, 8, 7};
|
||||||
|
|
||||||
// The current game state, this is used to determine what the game is doing at any given time
|
|
||||||
static enum GameState game_state = GAME_STATE_UNDEFINED;
|
|
||||||
static enum PlayState play_state = PLAY_STARTING;
|
static enum PlayState play_state = PLAY_STARTING;
|
||||||
|
|
||||||
// Initialization of the global vars
|
// Initialization of the global vars
|
||||||
@@ -293,28 +295,6 @@ GameVariables g_game_vars = {
|
|||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
int substate;
|
|
||||||
GameStateCallback on_init;
|
|
||||||
GameStateCallback on_update;
|
|
||||||
GameStateCallback on_exit;
|
|
||||||
} StateInfo;
|
|
||||||
|
|
||||||
StateInfo state_info[] = {
|
|
||||||
#define DEF_STATE_INFO(stateEnum, init_fn, update_fn, exit_fn) \
|
|
||||||
{.on_init = init_fn, .on_update = update_fn, .on_exit = exit_fn, .substate = 0},
|
|
||||||
#include "../include/def_state_info_table.h"
|
|
||||||
#undef DEF_STATE_INFO
|
|
||||||
};
|
|
||||||
|
|
||||||
// The sprite that displays the blind when in "GAME_PLAYING/GAME_ROUND_END" state
|
|
||||||
|
|
||||||
// The sprite that displays the blind when in "GAME_ROUND_END" state
|
|
||||||
|
|
||||||
// Red deck default (can later be moved to a deck.h file or something)
|
|
||||||
// Set in game_init and game_round_init
|
|
||||||
|
|
||||||
static u32 temp_score = 0; // This is the score that shows in the same spot as the hand type.
|
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 bool score_flames_active = false;
|
||||||
static FIXED lerped_score = 0;
|
static FIXED lerped_score = 0;
|
||||||
@@ -425,11 +405,13 @@ static inline void jokers_available_to_shop_init(void)
|
|||||||
|
|
||||||
void game_init()
|
void game_init()
|
||||||
{
|
{
|
||||||
|
state_machine_remove(&game_sm);
|
||||||
|
state_machine_register(&game_sm);
|
||||||
// Initialize all jokers list once
|
// Initialize all jokers list once
|
||||||
_owned_jokers_list = list_create();
|
_owned_jokers_list = list_init();
|
||||||
_discarded_jokers_list = list_create();
|
_discarded_jokers_list = list_init();
|
||||||
_expired_jokers_list = list_create();
|
_expired_jokers_list = list_init();
|
||||||
_shop_jokers_list = list_create();
|
_shop_jokers_list = list_init();
|
||||||
// TODO: Move this to an initialization of the play scoring states
|
// TODO: Move this to an initialization of the play scoring states
|
||||||
_joker_scored_itr = list_itr_create(&_owned_jokers_list);
|
_joker_scored_itr = list_itr_create(&_owned_jokers_list);
|
||||||
|
|
||||||
@@ -595,25 +577,14 @@ void game_update()
|
|||||||
|
|
||||||
jokers_update_loop();
|
jokers_update_loop();
|
||||||
|
|
||||||
state_info[game_state].on_update();
|
state_machine_update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_change_state(enum GameState new_game_state)
|
void game_change_state(enum GameState new_game_state)
|
||||||
{
|
{
|
||||||
g_game_vars.timer = TM_ZERO; // Reset the timer
|
g_game_vars.timer = TM_ZERO; // Reset the timer
|
||||||
|
|
||||||
if (game_state >= 0 && game_state < GAME_STATE_MAX)
|
state_machine_change_state(&game_sm, new_game_state);
|
||||||
{
|
|
||||||
state_info[game_state].substate = 0;
|
|
||||||
state_info[game_state].on_exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (new_game_state >= 0 && new_game_state < GAME_STATE_MAX)
|
|
||||||
{
|
|
||||||
state_info[new_game_state].on_init();
|
|
||||||
|
|
||||||
game_state = new_game_state;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CardObject** get_played_array(void)
|
CardObject** get_played_array(void)
|
||||||
|
|||||||
+36
-29
@@ -11,17 +11,19 @@
|
|||||||
#include "layout.h"
|
#include "layout.h"
|
||||||
#include "soundbank.h"
|
#include "soundbank.h"
|
||||||
#include "sprite.h"
|
#include "sprite.h"
|
||||||
|
#include "state_machine.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#include <maxmod.h>
|
#include <maxmod.h>
|
||||||
|
|
||||||
#define BLIND_SELECT_BTN_PID 15
|
static const u32 BLIND_SELECT_BTN_PID = 15;
|
||||||
#define TM_DISP_BLIND_PANEL_FINISH 7
|
static const u32 BLIND_SKIP_BTN_PID = 5;
|
||||||
#define TM_DISP_BLIND_PANEL_START 1
|
static const u32 BLIND_SKIP_BTN_SELECTED_BORDER_PID = 10;
|
||||||
#define BLIND_SKIP_BTN_PID 5
|
static const u32 BLIND_SELECT_BTN_SELECTED_BORDER_PID = 18;
|
||||||
#define BLIND_SKIP_BTN_SELECTED_BORDER_PID 10
|
|
||||||
#define BLIND_SELECT_BTN_SELECTED_BORDER_PID 18
|
static const u32 TM_DISP_BLIND_PANEL_FINISH = 7;
|
||||||
|
static const u32 TM_DISP_BLIND_PANEL_START = 1;
|
||||||
|
|
||||||
static int timer;
|
static int timer;
|
||||||
|
|
||||||
@@ -29,6 +31,7 @@ static void game_blind_select_start_anim_seq(void);
|
|||||||
static void game_blind_select_handle_input(void);
|
static void game_blind_select_handle_input(void);
|
||||||
static void game_blind_select_selected_anim_seq(void);
|
static void game_blind_select_selected_anim_seq(void);
|
||||||
static void game_blind_select_display_blind_panel(void);
|
static void game_blind_select_display_blind_panel(void);
|
||||||
|
static void game_blind_select_exit(void);
|
||||||
static Rect game_blind_select_get_req_score_rect(enum BlindTokens blind);
|
static Rect game_blind_select_get_req_score_rect(enum BlindTokens blind);
|
||||||
static void game_blind_select_print_blinds_reqs_and_rewards(void);
|
static void game_blind_select_print_blinds_reqs_and_rewards(void);
|
||||||
static enum BlindType get_blind_type_from_token(enum BlindTokens blind);
|
static enum BlindType get_blind_type_from_token(enum BlindTokens blind);
|
||||||
@@ -40,15 +43,22 @@ enum BlindSelectState
|
|||||||
BLIND_SELECT,
|
BLIND_SELECT,
|
||||||
BLIND_SELECTED_ANIM_SEQ,
|
BLIND_SELECTED_ANIM_SEQ,
|
||||||
DISPLAY_BLIND_PANEL,
|
DISPLAY_BLIND_PANEL,
|
||||||
BLIND_SELECT_MAX
|
BLIND_SELECT_EXIT,
|
||||||
|
BLIND_SELECT_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: this will be refactored into common state machine
|
// TODO: this will be refactored into common state machine
|
||||||
static const SubStateActionFn blind_select_state_actions[] = {
|
static StateInfo state_info[] = {
|
||||||
game_blind_select_start_anim_seq,
|
STATE_INFO_UPDATE_FN_ONLY(game_blind_select_start_anim_seq),
|
||||||
game_blind_select_handle_input,
|
STATE_INFO_UPDATE_FN_ONLY(game_blind_select_handle_input),
|
||||||
game_blind_select_selected_anim_seq,
|
STATE_INFO_UPDATE_FN_ONLY(game_blind_select_selected_anim_seq),
|
||||||
game_blind_select_display_blind_panel
|
STATE_INFO_UPDATE_FN_ONLY(game_blind_select_display_blind_panel),
|
||||||
|
STATE_INFO_UPDATE_FN_ONLY(game_blind_select_exit),
|
||||||
|
};
|
||||||
|
|
||||||
|
static StateMachine blind_select_sm = {
|
||||||
|
.state_infos = &state_info[0],
|
||||||
|
.num_infos = BLIND_SELECT_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
@@ -74,8 +84,6 @@ static const u32 SKIP_ROW = 1;
|
|||||||
static int selection_x = 0;
|
static int selection_x = 0;
|
||||||
static int selection_y = 0;
|
static int selection_y = 0;
|
||||||
|
|
||||||
static enum BlindSelectState substate;
|
|
||||||
|
|
||||||
static Sprite* blind_select_tokens[NUM_BLINDS_PER_ANTE] = {NULL};
|
static Sprite* blind_select_tokens[NUM_BLINDS_PER_ANTE] = {NULL};
|
||||||
|
|
||||||
static void game_blind_select_start_anim_seq()
|
static void game_blind_select_start_anim_seq()
|
||||||
@@ -95,7 +103,7 @@ static void game_blind_select_start_anim_seq()
|
|||||||
if (timer == TM_END_ANIM_SEQ)
|
if (timer == TM_END_ANIM_SEQ)
|
||||||
{
|
{
|
||||||
game_blind_select_print_blinds_reqs_and_rewards();
|
game_blind_select_print_blinds_reqs_and_rewards();
|
||||||
substate = BLIND_SELECT;
|
state_machine_change_state(&blind_select_sm, BLIND_SELECT);
|
||||||
timer = TM_ZERO; // Reset the timer
|
timer = TM_ZERO; // Reset the timer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,7 +199,7 @@ static void game_blind_select_handle_input()
|
|||||||
{
|
{
|
||||||
case BLIND_ROW:
|
case BLIND_ROW:
|
||||||
play_sfx(SFX_BUTTON, MM_BASE_PITCH_RATE, BUTTON_SFX_VOLUME);
|
play_sfx(SFX_BUTTON, MM_BASE_PITCH_RATE, BUTTON_SFX_VOLUME);
|
||||||
substate = BLIND_SELECTED_ANIM_SEQ;
|
state_machine_change_state(&blind_select_sm, BLIND_SELECTED_ANIM_SEQ);
|
||||||
timer = TM_ZERO;
|
timer = TM_ZERO;
|
||||||
++g_game_vars.round;
|
++g_game_vars.round;
|
||||||
display_round();
|
display_round();
|
||||||
@@ -258,8 +266,8 @@ static void game_blind_select_selected_anim_seq()
|
|||||||
obj_hide(blind_select_tokens[i]->obj);
|
obj_hide(blind_select_tokens[i]->obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
substate = DISPLAY_BLIND_PANEL; // Reset the state
|
timer = TM_ZERO;
|
||||||
timer = TM_ZERO; // Reset the timer
|
state_machine_change_state(&blind_select_sm, DISPLAY_BLIND_PANEL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,7 +275,7 @@ static void game_blind_select_display_blind_panel()
|
|||||||
{
|
{
|
||||||
if (timer >= TM_DISP_BLIND_PANEL_FINISH)
|
if (timer >= TM_DISP_BLIND_PANEL_FINISH)
|
||||||
{
|
{
|
||||||
substate = BLIND_SELECT_MAX;
|
state_machine_change_state(&blind_select_sm, BLIND_SELECT_EXIT);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,6 +310,12 @@ static void game_blind_select_display_blind_panel()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void game_blind_select_exit(void)
|
||||||
|
{
|
||||||
|
reset_background();
|
||||||
|
game_change_state(GAME_STATE_PLAYING);
|
||||||
|
}
|
||||||
|
|
||||||
static Rect game_blind_select_get_req_score_rect(enum BlindTokens blind)
|
static Rect game_blind_select_get_req_score_rect(enum BlindTokens blind)
|
||||||
{
|
{
|
||||||
Rect blind_req_score_rect = SINGLE_BLIND_SEL_REQ_SCORE_RECT;
|
Rect blind_req_score_rect = SINGLE_BLIND_SEL_REQ_SCORE_RECT;
|
||||||
@@ -441,7 +455,8 @@ static void blind_tokens_init()
|
|||||||
void game_blind_select_on_init(void)
|
void game_blind_select_on_init(void)
|
||||||
{
|
{
|
||||||
timer = TM_ZERO;
|
timer = TM_ZERO;
|
||||||
substate = START_ANIM_SEQ;
|
state_machine_register(&blind_select_sm);
|
||||||
|
state_machine_change_state(&blind_select_sm, START_ANIM_SEQ);
|
||||||
|
|
||||||
selection_x = 0;
|
selection_x = 0;
|
||||||
selection_y = 0;
|
selection_y = 0;
|
||||||
@@ -461,14 +476,6 @@ void game_blind_select_on_init(void)
|
|||||||
void game_blind_select_on_update(void)
|
void game_blind_select_on_update(void)
|
||||||
{
|
{
|
||||||
timer++;
|
timer++;
|
||||||
if (substate == BLIND_SELECT_MAX)
|
|
||||||
{
|
|
||||||
reset_background();
|
|
||||||
game_change_state(GAME_STATE_PLAYING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
blind_select_state_actions[substate]();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_blind_select_on_exit(void)
|
void game_blind_select_on_exit(void)
|
||||||
@@ -483,7 +490,7 @@ void game_blind_select_on_exit(void)
|
|||||||
reset_background();
|
reset_background();
|
||||||
selection_y = 0;
|
selection_y = 0;
|
||||||
|
|
||||||
g_game_vars.timer = TM_ZERO;
|
state_machine_remove(&blind_select_sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_blind_select_change_background(void)
|
void game_blind_select_change_background(void)
|
||||||
|
|||||||
+31
-30
@@ -5,6 +5,7 @@
|
|||||||
#include "game.h"
|
#include "game.h"
|
||||||
#include "game_variables.h"
|
#include "game_variables.h"
|
||||||
#include "layout.h"
|
#include "layout.h"
|
||||||
|
#include "state_machine.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
@@ -19,7 +20,7 @@ enum GameRoundEndStates
|
|||||||
DISPLAY_REWARDS,
|
DISPLAY_REWARDS,
|
||||||
DISPLAY_CASHOUT,
|
DISPLAY_CASHOUT,
|
||||||
DISMISS_ROUND_END_PANEL,
|
DISMISS_ROUND_END_PANEL,
|
||||||
ROUND_END_EXIT
|
ROUND_END_STATES_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
static const u32 TM_RESET_STATIC_VARS = 30;
|
static const u32 TM_RESET_STATIC_VARS = 30;
|
||||||
@@ -46,7 +47,6 @@ static const Rect ROUND_END_MENU_RECT = {9, 7, 24, 20
|
|||||||
static const BG_POINT CASHOUT_SRC_3X3_RECT_POS = {5, 29};
|
static const BG_POINT CASHOUT_SRC_3X3_RECT_POS = {5, 29};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
static int substate;
|
|
||||||
static int blind_reward = 0;
|
static int blind_reward = 0;
|
||||||
static int hand_reward = 0;
|
static int hand_reward = 0;
|
||||||
static int interest_reward = 0;
|
static int interest_reward = 0;
|
||||||
@@ -67,16 +67,21 @@ static void game_round_end_dismiss_round_end_panel(void);
|
|||||||
|
|
||||||
static void game_round_end_extend_black_panel_down(int black_panel_bottom);
|
static void game_round_end_extend_black_panel_down(int black_panel_bottom);
|
||||||
|
|
||||||
static const SubStateActionFn round_end_state_actions[] = {
|
static StateInfo state_info[] = {
|
||||||
game_round_end_start,
|
STATE_INFO_UPDATE_FN_ONLY(game_round_end_start),
|
||||||
game_round_end_start_expand_popup,
|
STATE_INFO_UPDATE_FN_ONLY(game_round_end_start_expand_popup),
|
||||||
game_round_end_display_finished_blind,
|
STATE_INFO_UPDATE_FN_ONLY(game_round_end_display_finished_blind),
|
||||||
game_round_end_display_score_min,
|
STATE_INFO_UPDATE_FN_ONLY(game_round_end_display_score_min),
|
||||||
game_round_end_update_blind_reward,
|
STATE_INFO_UPDATE_FN_ONLY(game_round_end_update_blind_reward),
|
||||||
game_round_end_panel_exit,
|
STATE_INFO_UPDATE_FN_ONLY(game_round_end_panel_exit),
|
||||||
game_round_end_display_rewards,
|
STATE_INFO_UPDATE_FN_ONLY(game_round_end_display_rewards),
|
||||||
game_round_end_display_cashout,
|
STATE_INFO_UPDATE_FN_ONLY(game_round_end_display_cashout),
|
||||||
game_round_end_dismiss_round_end_panel
|
STATE_INFO_UPDATE_FN_ONLY(game_round_end_dismiss_round_end_panel),
|
||||||
|
};
|
||||||
|
|
||||||
|
static StateMachine round_end_sm = {
|
||||||
|
.state_infos = &state_info[0],
|
||||||
|
.num_infos = ROUND_END_STATES_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int calculate_interest_reward(void)
|
static int calculate_interest_reward(void)
|
||||||
@@ -93,8 +98,8 @@ static void game_round_end_start(void)
|
|||||||
if (g_game_vars.timer == TM_RESET_STATIC_VARS)
|
if (g_game_vars.timer == TM_RESET_STATIC_VARS)
|
||||||
{
|
{
|
||||||
change_background(BG_ROUND_END, false); // Change the background to the round end background
|
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
|
state_machine_change_state(&round_end_sm, START_EXPAND_POPUP);
|
||||||
g_game_vars.timer = TM_ZERO; // Reset the timer
|
g_game_vars.timer = TM_ZERO; // Reset the timer
|
||||||
blind_reward = blind_get_reward(g_game_vars.current_blind);
|
blind_reward = blind_get_reward(g_game_vars.current_blind);
|
||||||
hand_reward = g_game_vars.hands;
|
hand_reward = g_game_vars.hands;
|
||||||
interest_reward = calculate_interest_reward();
|
interest_reward = calculate_interest_reward();
|
||||||
@@ -109,7 +114,7 @@ static void game_round_end_start_expand_popup(void)
|
|||||||
|
|
||||||
if (g_game_vars.timer == TM_END_POP_MENU_ANIM)
|
if (g_game_vars.timer == TM_END_POP_MENU_ANIM)
|
||||||
{
|
{
|
||||||
substate = DISPLAY_FINISHED_BLIND;
|
state_machine_change_state(&round_end_sm, DISPLAY_FINISHED_BLIND);
|
||||||
g_game_vars.timer = TM_ZERO;
|
g_game_vars.timer = TM_ZERO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,7 +156,7 @@ static void game_round_end_display_finished_blind(void)
|
|||||||
|
|
||||||
if (g_game_vars.timer >= TM_END_DISPLAY_FIN_BLIND)
|
if (g_game_vars.timer >= TM_END_DISPLAY_FIN_BLIND)
|
||||||
{
|
{
|
||||||
substate = DISPLAY_SCORE_MIN;
|
state_machine_change_state(&round_end_sm, DISPLAY_SCORE_MIN);
|
||||||
g_game_vars.timer = TM_ZERO;
|
g_game_vars.timer = TM_ZERO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,7 +177,7 @@ static void game_round_end_display_score_min(void)
|
|||||||
|
|
||||||
if (g_game_vars.timer >= TM_END_DISPLAY_SCORE_MIN)
|
if (g_game_vars.timer >= TM_END_DISPLAY_SCORE_MIN)
|
||||||
{
|
{
|
||||||
substate = UPDATE_BLIND_REWARD;
|
state_machine_change_state(&round_end_sm, UPDATE_BLIND_REWARD);
|
||||||
g_game_vars.timer = TM_ZERO;
|
g_game_vars.timer = TM_ZERO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,7 +213,7 @@ static void game_round_end_update_blind_reward(void)
|
|||||||
tte_erase_rect_wrapper(BLIND_REQ_TEXT_RECT);
|
tte_erase_rect_wrapper(BLIND_REQ_TEXT_RECT);
|
||||||
obj_hide(g_game_vars.playing_blind_token->obj);
|
obj_hide(g_game_vars.playing_blind_token->obj);
|
||||||
affine_background_load_palette(affine_background_gfxPal);
|
affine_background_load_palette(affine_background_gfxPal);
|
||||||
substate = BLIND_PANEL_EXIT;
|
state_machine_change_state(&round_end_sm, BLIND_PANEL_EXIT);
|
||||||
g_game_vars.timer = TM_ZERO;
|
g_game_vars.timer = TM_ZERO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,7 +241,7 @@ static void game_round_end_panel_exit(void)
|
|||||||
else if (g_game_vars.timer > FRAMES(20))
|
else if (g_game_vars.timer > FRAMES(20))
|
||||||
{
|
{
|
||||||
memset16(&pal_bg_mem[REWARD_PANEL_BORDER_PID], 0x1483, 1);
|
memset16(&pal_bg_mem[REWARD_PANEL_BORDER_PID], 0x1483, 1);
|
||||||
substate = DISPLAY_REWARDS;
|
state_machine_change_state(&round_end_sm, DISPLAY_REWARDS);
|
||||||
g_game_vars.timer = TM_ZERO;
|
g_game_vars.timer = TM_ZERO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -342,7 +347,7 @@ static void game_round_end_display_rewards(void)
|
|||||||
if (hand_reward <= 0 && interest_to_count <= 0)
|
if (hand_reward <= 0 && interest_to_count <= 0)
|
||||||
{
|
{
|
||||||
g_game_vars.timer = TM_ZERO;
|
g_game_vars.timer = TM_ZERO;
|
||||||
substate = DISPLAY_CASHOUT;
|
state_machine_change_state(&round_end_sm, DISPLAY_CASHOUT);
|
||||||
}
|
}
|
||||||
else if (g_game_vars.timer == TM_START_ROUND_END_REWARDS_ANIM)
|
else if (g_game_vars.timer == TM_START_ROUND_END_REWARDS_ANIM)
|
||||||
{
|
{
|
||||||
@@ -406,7 +411,7 @@ static void game_round_end_display_cashout()
|
|||||||
{
|
{
|
||||||
game_round_end_cashout();
|
game_round_end_cashout();
|
||||||
|
|
||||||
substate = DISMISS_ROUND_END_PANEL; // Go to the next state
|
state_machine_change_state(&round_end_sm, DISMISS_ROUND_END_PANEL);
|
||||||
g_game_vars.timer = TM_ZERO;
|
g_game_vars.timer = TM_ZERO;
|
||||||
|
|
||||||
obj_hide(g_game_vars.round_end_blind_token->obj); // Hide the blind token object
|
obj_hide(g_game_vars.round_end_blind_token->obj); // Hide the blind token object
|
||||||
@@ -423,7 +428,7 @@ static void game_round_end_dismiss_round_end_panel(void)
|
|||||||
if (g_game_vars.timer >= TM_DISMISS_ROUND_END_TM)
|
if (g_game_vars.timer >= TM_DISMISS_ROUND_END_TM)
|
||||||
{
|
{
|
||||||
g_game_vars.timer = TM_ZERO;
|
g_game_vars.timer = TM_ZERO;
|
||||||
substate = ROUND_END_EXIT;
|
game_change_state(GAME_STATE_SHOP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -446,19 +451,14 @@ void game_round_end_change_background(void)
|
|||||||
|
|
||||||
void game_round_end_on_init(void)
|
void game_round_end_on_init(void)
|
||||||
{
|
{
|
||||||
substate = ROUND_END_START;
|
|
||||||
g_game_vars.timer = 0;
|
g_game_vars.timer = 0;
|
||||||
|
state_machine_register(&round_end_sm);
|
||||||
|
state_machine_change_state(&round_end_sm, ROUND_END_START);
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_round_end_on_update(void)
|
void game_round_end_on_update(void)
|
||||||
{
|
{
|
||||||
if (substate == ROUND_END_EXIT)
|
// Substate logic only
|
||||||
{
|
|
||||||
game_change_state(GAME_STATE_SHOP);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
round_end_state_actions[substate]();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_round_end_on_exit(void)
|
void game_round_end_on_exit(void)
|
||||||
@@ -470,5 +470,6 @@ void game_round_end_on_exit(void)
|
|||||||
interest_reward = 0;
|
interest_reward = 0;
|
||||||
sprite_destroy(&g_game_vars.playing_blind_token);
|
sprite_destroy(&g_game_vars.playing_blind_token);
|
||||||
sprite_destroy(&g_game_vars.round_end_blind_token);
|
sprite_destroy(&g_game_vars.round_end_blind_token);
|
||||||
|
state_machine_remove(&round_end_sm);
|
||||||
// TODO: Reuse sprites for blind selection?
|
// TODO: Reuse sprites for blind selection?
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-26
@@ -20,6 +20,7 @@
|
|||||||
#include "random.h"
|
#include "random.h"
|
||||||
#include "save.h"
|
#include "save.h"
|
||||||
#include "soundbank.h"
|
#include "soundbank.h"
|
||||||
|
#include "state_machine.h"
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
@@ -62,8 +63,6 @@ static const Rect SHOP_REROLL_RECT = { 88, 96, UNDEFINED, UNDEFINED };
|
|||||||
static const BG_POINT SHOP_JOKER_SPRITES_INIT_POS = {120, 160};
|
static const BG_POINT SHOP_JOKER_SPRITES_INIT_POS = {120, 160};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
// Shop Substates
|
|
||||||
|
|
||||||
enum GameShopStates
|
enum GameShopStates
|
||||||
{
|
{
|
||||||
GAME_SHOP_INTRO,
|
GAME_SHOP_INTRO,
|
||||||
@@ -72,14 +71,19 @@ enum GameShopStates
|
|||||||
GAME_SHOP_MAX
|
GAME_SHOP_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
static void game_shop_intro();
|
static void game_shop_intro(void);
|
||||||
static void game_shop_process_user_input();
|
static void game_shop_process_user_input(void);
|
||||||
static void game_shop_outro();
|
static void game_shop_outro(void);
|
||||||
|
|
||||||
static const SubStateActionFn shop_state_actions[] = {
|
static StateInfo shop_state_actions[] = {
|
||||||
game_shop_intro,
|
STATE_INFO_UPDATE_FN_ONLY(game_shop_intro),
|
||||||
game_shop_process_user_input,
|
STATE_INFO_UPDATE_FN_ONLY(game_shop_process_user_input),
|
||||||
game_shop_outro
|
STATE_INFO_UPDATE_FN_ONLY(game_shop_outro),
|
||||||
|
};
|
||||||
|
|
||||||
|
static StateMachine shop_sm = {
|
||||||
|
.state_infos = &shop_state_actions[0],
|
||||||
|
.num_infos = GAME_SHOP_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Shop SelectionGrid
|
// Shop SelectionGrid
|
||||||
@@ -132,7 +136,6 @@ static Button reroll_button = {
|
|||||||
// Shop internal variables
|
// Shop internal variables
|
||||||
|
|
||||||
static int timer;
|
static int timer;
|
||||||
static enum GameShopStates substate;
|
|
||||||
|
|
||||||
static int reroll_cost = REROLL_BASE_COST;
|
static int reroll_cost = REROLL_BASE_COST;
|
||||||
|
|
||||||
@@ -165,7 +168,9 @@ void game_shop_on_init(void)
|
|||||||
game_shop_change_background();
|
game_shop_change_background();
|
||||||
|
|
||||||
timer = TM_ZERO;
|
timer = TM_ZERO;
|
||||||
substate = GAME_SHOP_INTRO;
|
|
||||||
|
state_machine_register(&shop_sm);
|
||||||
|
state_machine_change_state(&shop_sm, GAME_SHOP_INTRO);
|
||||||
|
|
||||||
// The selection grid is initialized outside of bounds and moved
|
// The selection grid is initialized outside of bounds and moved
|
||||||
// to trigger the selection change so the initial selection is visible
|
// to trigger the selection change so the initial selection is visible
|
||||||
@@ -245,7 +250,7 @@ static void game_shop_create_items(void)
|
|||||||
List* shop_jokers_list = get_shop_jokers_list();
|
List* shop_jokers_list = get_shop_jokers_list();
|
||||||
|
|
||||||
list_clear(shop_jokers_list);
|
list_clear(shop_jokers_list);
|
||||||
*shop_jokers_list = list_create();
|
*shop_jokers_list = list_init();
|
||||||
|
|
||||||
for (int i = 0; i < MAX_SHOP_JOKERS; i++)
|
for (int i = 0; i < MAX_SHOP_JOKERS; i++)
|
||||||
{
|
{
|
||||||
@@ -326,7 +331,7 @@ static void game_shop_intro()
|
|||||||
|
|
||||||
if (timer == TM_END_GAME_SHOP_INTRO)
|
if (timer == TM_END_GAME_SHOP_INTRO)
|
||||||
{
|
{
|
||||||
substate = GAME_SHOP_ACTIVE;
|
state_machine_change_state(&shop_sm, GAME_SHOP_ACTIVE);
|
||||||
timer = TM_ZERO; // Reset the timer
|
timer = TM_ZERO; // Reset the timer
|
||||||
|
|
||||||
// print initial reroll cost only when the panel is in place
|
// print initial reroll cost only when the panel is in place
|
||||||
@@ -515,7 +520,7 @@ static inline void game_shop_reroll(int* reroll_cost)
|
|||||||
}
|
}
|
||||||
|
|
||||||
list_clear(shop_jokers_list);
|
list_clear(shop_jokers_list);
|
||||||
*shop_jokers_list = list_create();
|
*shop_jokers_list = list_init();
|
||||||
|
|
||||||
game_shop_create_items();
|
game_shop_create_items();
|
||||||
|
|
||||||
@@ -559,7 +564,7 @@ static void shop_reroll_row_on_key_transit(SelectionGrid* selection_grid, Select
|
|||||||
static void next_round_on_pressed(void)
|
static void next_round_on_pressed(void)
|
||||||
{
|
{
|
||||||
// Go to next blind selection game state
|
// Go to next blind selection game state
|
||||||
substate = GAME_SHOP_EXIT; // Go to the outro sequence state
|
state_machine_change_state(&shop_sm, GAME_SHOP_EXIT);
|
||||||
timer = TM_ZERO;
|
timer = TM_ZERO;
|
||||||
reroll_cost = REROLL_BASE_COST;
|
reroll_cost = REROLL_BASE_COST;
|
||||||
|
|
||||||
@@ -624,8 +629,7 @@ static void game_shop_outro()
|
|||||||
|
|
||||||
if (timer >= MENU_POP_OUT_ANIM_FRAMES)
|
if (timer >= MENU_POP_OUT_ANIM_FRAMES)
|
||||||
{
|
{
|
||||||
substate = GAME_SHOP_MAX; // Go to the next state
|
game_change_state(GAME_STATE_BLIND_SELECT);
|
||||||
timer = TM_ZERO; // Reset the timer
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -681,14 +685,6 @@ void game_shop_on_update(void)
|
|||||||
{
|
{
|
||||||
game_shop_lights_anim_frame();
|
game_shop_lights_anim_frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (substate == GAME_SHOP_MAX)
|
|
||||||
{
|
|
||||||
game_change_state(GAME_STATE_BLIND_SELECT);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
shop_state_actions[substate]();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void game_shop_on_exit(void)
|
void game_shop_on_exit(void)
|
||||||
@@ -711,5 +707,7 @@ void game_shop_on_exit(void)
|
|||||||
|
|
||||||
increment_blind(BLIND_STATE_DEFEATED); // TODO: Move to game_round_end()?
|
increment_blind(BLIND_STATE_DEFEATED); // TODO: Move to game_round_end()?
|
||||||
|
|
||||||
|
state_machine_remove(&shop_sm);
|
||||||
|
|
||||||
save_game();
|
save_game();
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-2
@@ -1,3 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* @file list.c
|
||||||
|
*
|
||||||
|
* @brief List functions implementation.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
|
|
||||||
#include "pool.h"
|
#include "pool.h"
|
||||||
@@ -29,9 +35,9 @@ static void s_list_remove_node(List* list, ListNode* node);
|
|||||||
*/
|
*/
|
||||||
static ListNode* s_list_itr_node_next(ListItr* itr);
|
static ListNode* s_list_itr_node_next(ListItr* itr);
|
||||||
|
|
||||||
List list_create(void)
|
List list_init(void)
|
||||||
{
|
{
|
||||||
List list = {.head = NULL, .tail = NULL, .len = 0};
|
List list = LIST_DEFAULT;
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,3 +312,20 @@ void list_itr_remove_current_node(ListItr* itr)
|
|||||||
s_list_remove_node(itr->list, itr->current_node);
|
s_list_remove_node(itr->list, itr->current_node);
|
||||||
itr->current_node = tmp_prev;
|
itr->current_node = tmp_prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool list_remove_data(List* list, void* data)
|
||||||
|
{
|
||||||
|
ListItr itr = list_itr_create(list);
|
||||||
|
ListNode* ln;
|
||||||
|
|
||||||
|
while ((ln = s_list_itr_node_next(&itr)))
|
||||||
|
{
|
||||||
|
if (ln->data == data)
|
||||||
|
{
|
||||||
|
s_list_remove_node(list, ln);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
#include "state_machine.h"
|
||||||
|
|
||||||
|
#include "game.h"
|
||||||
|
#include "list.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
static List update_cbs = LIST_DEFAULT;
|
||||||
|
|
||||||
|
// Used as a No Operation for game states that have no init and/or exit function.
|
||||||
|
// ricfehr3 did the work of determining whether a noop or a NULL check was more
|
||||||
|
// efficient. Well, this is the answer.
|
||||||
|
// Thanks!
|
||||||
|
// https://github.com/cellos51/balatro-gba/issues/137#issuecomment-3322485129
|
||||||
|
void noop(void) {};
|
||||||
|
|
||||||
|
void state_machine_register(StateMachine* state_machine)
|
||||||
|
{
|
||||||
|
// Always try to remove the state machine first. Only one can exist at a time
|
||||||
|
// So ensure that calling this function doesn't add two update functions
|
||||||
|
state_machine_remove(state_machine);
|
||||||
|
|
||||||
|
state_machine->active_update = noop;
|
||||||
|
state_machine->state = UNDEFINED;
|
||||||
|
|
||||||
|
list_push_back(&update_cbs, &state_machine->active_update);
|
||||||
|
}
|
||||||
|
|
||||||
|
void state_machine_remove(StateMachine* state_machine)
|
||||||
|
{
|
||||||
|
list_remove_data(&update_cbs, &state_machine->active_update);
|
||||||
|
}
|
||||||
|
|
||||||
|
void state_machine_update(void)
|
||||||
|
{
|
||||||
|
ListItr itr = list_itr_create(&update_cbs);
|
||||||
|
StateCallback* cb;
|
||||||
|
while ((cb = list_itr_next(&itr)))
|
||||||
|
{
|
||||||
|
(*cb)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void state_machine_change_state(StateMachine* state_machine, int new_state)
|
||||||
|
{
|
||||||
|
if (state_machine->state >= 0 && state_machine->state < state_machine->num_infos)
|
||||||
|
{
|
||||||
|
state_machine->state_infos[state_machine->state].on_exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_state >= 0 && new_state < state_machine->num_infos)
|
||||||
|
{
|
||||||
|
state_machine->state_infos[new_state].on_init();
|
||||||
|
|
||||||
|
state_machine->active_update = state_machine->state_infos[new_state].on_update;
|
||||||
|
|
||||||
|
state_machine->state = new_state;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state_machine->active_update = noop;
|
||||||
|
state_machine->state = UNDEFINED;
|
||||||
|
}
|
||||||
|
}
|
||||||
+83
-12
@@ -5,13 +5,13 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
// As simple as it gets, just needs to be initialized correctly
|
// As simple as it gets, just needs to be initialized correctly
|
||||||
// - list_create
|
// - list_init
|
||||||
// - list_is_empty
|
// - list_is_empty
|
||||||
// - list_get_len
|
// - list_get_len
|
||||||
// - list_clear
|
// - list_clear
|
||||||
void create_and_clear_list(void)
|
void create_and_clear_list(void)
|
||||||
{
|
{
|
||||||
List my_cool_list = list_create();
|
List my_cool_list = list_init();
|
||||||
|
|
||||||
// verify no data
|
// verify no data
|
||||||
assert(my_cool_list.head == NULL);
|
assert(my_cool_list.head == NULL);
|
||||||
@@ -26,7 +26,7 @@ void create_and_clear_list(void)
|
|||||||
|
|
||||||
// Push back one entry, make sure it looks as expected
|
// Push back one entry, make sure it looks as expected
|
||||||
// tests:
|
// tests:
|
||||||
// - list_create
|
// - list_init
|
||||||
// - list_push_back
|
// - list_push_back
|
||||||
// - list_is_empty
|
// - list_is_empty
|
||||||
// - list_get_len
|
// - list_get_len
|
||||||
@@ -34,7 +34,7 @@ void create_and_clear_list(void)
|
|||||||
// - list_clear
|
// - list_clear
|
||||||
void push_back_one_entry(void)
|
void push_back_one_entry(void)
|
||||||
{
|
{
|
||||||
List my_cool_list = list_create();
|
List my_cool_list = list_init();
|
||||||
|
|
||||||
// verify no data
|
// verify no data
|
||||||
assert(my_cool_list.head == NULL);
|
assert(my_cool_list.head == NULL);
|
||||||
@@ -69,7 +69,7 @@ void push_back_one_entry(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Push front one entry, make sure it looks as expected
|
// Push front one entry, make sure it looks as expected
|
||||||
// - list_create
|
// - list_init
|
||||||
// - list_push_front
|
// - list_push_front
|
||||||
// - list_is_empty
|
// - list_is_empty
|
||||||
// - list_get_len
|
// - list_get_len
|
||||||
@@ -77,7 +77,7 @@ void push_back_one_entry(void)
|
|||||||
// - list_clear
|
// - list_clear
|
||||||
void push_front_one_entry(void)
|
void push_front_one_entry(void)
|
||||||
{
|
{
|
||||||
List my_cool_list = list_create();
|
List my_cool_list = list_init();
|
||||||
|
|
||||||
// verify no data
|
// verify no data
|
||||||
assert(my_cool_list.head == NULL);
|
assert(my_cool_list.head == NULL);
|
||||||
@@ -123,7 +123,7 @@ void push_front_one_entry(void)
|
|||||||
// Lastly, push to the front the same 3 entries and verify the state of the list
|
// Lastly, push to the front the same 3 entries and verify the state of the list
|
||||||
// before detroying.
|
// before detroying.
|
||||||
//
|
//
|
||||||
// - list_create
|
// - list_init
|
||||||
// - list_push_front
|
// - list_push_front
|
||||||
// - list_is_empty
|
// - list_is_empty
|
||||||
// - list_get_len
|
// - list_get_len
|
||||||
@@ -135,7 +135,7 @@ void push_front_one_entry(void)
|
|||||||
// - list_itr_remove_node_current;
|
// - list_itr_remove_node_current;
|
||||||
void push_back_three_remove_push_front_three_entries(void)
|
void push_back_three_remove_push_front_three_entries(void)
|
||||||
{
|
{
|
||||||
List my_cool_list = list_create();
|
List my_cool_list = list_init();
|
||||||
|
|
||||||
// verify no data
|
// verify no data
|
||||||
assert(my_cool_list.head == NULL);
|
assert(my_cool_list.head == NULL);
|
||||||
@@ -287,7 +287,7 @@ void push_back_three_remove_push_front_three_entries(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Test inserting at head, middle, and tail of list
|
// Test inserting at head, middle, and tail of list
|
||||||
// - list_create
|
// - list_init
|
||||||
// - list_push_back
|
// - list_push_back
|
||||||
// - list_insert
|
// - list_insert
|
||||||
// - list_is_empty
|
// - list_is_empty
|
||||||
@@ -299,7 +299,7 @@ void push_back_three_remove_push_front_three_entries(void)
|
|||||||
// - list_get_at_idx
|
// - list_get_at_idx
|
||||||
void test_list_insertion(void)
|
void test_list_insertion(void)
|
||||||
{
|
{
|
||||||
List my_cool_list = list_create();
|
List my_cool_list = list_init();
|
||||||
|
|
||||||
// verify no data
|
// verify no data
|
||||||
assert(my_cool_list.head == NULL);
|
assert(my_cool_list.head == NULL);
|
||||||
@@ -515,7 +515,7 @@ void test_list_insertion(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Test inserting at head, middle, and tail of list
|
// Test inserting at head, middle, and tail of list
|
||||||
// - list_create
|
// - list_init
|
||||||
// - list_push_back
|
// - list_push_back
|
||||||
// - list_swap
|
// - list_swap
|
||||||
// - list_is_empty
|
// - list_is_empty
|
||||||
@@ -526,7 +526,7 @@ void test_list_insertion(void)
|
|||||||
// - list_get_at_idx
|
// - list_get_at_idx
|
||||||
void test_list_swap(void)
|
void test_list_swap(void)
|
||||||
{
|
{
|
||||||
List my_cool_list = list_create();
|
List my_cool_list = list_init();
|
||||||
|
|
||||||
// verify no data
|
// verify no data
|
||||||
assert(my_cool_list.head == NULL);
|
assert(my_cool_list.head == NULL);
|
||||||
@@ -662,6 +662,74 @@ void test_list_swap(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Test the "list_remove_data" function
|
||||||
|
// - list_init
|
||||||
|
// - list_is_empty
|
||||||
|
// - list_get_len
|
||||||
|
// - list_clear
|
||||||
|
void test_remove_data(void)
|
||||||
|
{
|
||||||
|
// this value MUST be 5
|
||||||
|
const int initial_list_size = 5;
|
||||||
|
const int midpoint = initial_list_size / 2;
|
||||||
|
List my_cool_list = list_init();
|
||||||
|
|
||||||
|
// verify no data
|
||||||
|
assert(my_cool_list.head == NULL);
|
||||||
|
assert(my_cool_list.tail == NULL);
|
||||||
|
assert(list_get_len(&my_cool_list) == 0);
|
||||||
|
assert(list_is_empty(&my_cool_list));
|
||||||
|
|
||||||
|
int test_data[initial_list_size];
|
||||||
|
|
||||||
|
for(int i = 0; i < initial_list_size; i++)
|
||||||
|
{
|
||||||
|
// 0 -> 1 -> 2 -> 3 -> 4
|
||||||
|
test_data[i] = i;
|
||||||
|
list_push_back(&my_cool_list, &test_data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(list_get_len(&my_cool_list) == initial_list_size);
|
||||||
|
|
||||||
|
// remove from the front
|
||||||
|
assert(list_remove_data(&my_cool_list, &test_data[0]));
|
||||||
|
|
||||||
|
assert(my_cool_list.head->data == &test_data[1]);
|
||||||
|
assert(list_get_len(&my_cool_list) == initial_list_size - 1);
|
||||||
|
|
||||||
|
// remove from the back
|
||||||
|
assert(list_remove_data(&my_cool_list, &test_data[initial_list_size - 1]));
|
||||||
|
|
||||||
|
assert(my_cool_list.head->data == &test_data[1]);
|
||||||
|
assert(my_cool_list.tail->data == &test_data[initial_list_size - 2]);
|
||||||
|
assert(list_get_len(&my_cool_list) == initial_list_size - 2);
|
||||||
|
|
||||||
|
// remove from the middle
|
||||||
|
assert(list_remove_data(&my_cool_list, &test_data[midpoint]));
|
||||||
|
|
||||||
|
assert(my_cool_list.head->data == &test_data[1]);
|
||||||
|
assert(my_cool_list.tail->data == &test_data[initial_list_size - 2]);
|
||||||
|
assert(list_get_len(&my_cool_list) == initial_list_size - 3);
|
||||||
|
|
||||||
|
// fail on removal of pointer not in list
|
||||||
|
assert(!list_remove_data(&my_cool_list, NULL));
|
||||||
|
|
||||||
|
assert(my_cool_list.head->data == &test_data[1]);
|
||||||
|
assert(my_cool_list.tail->data == &test_data[initial_list_size - 2]);
|
||||||
|
assert(list_get_len(&my_cool_list) == initial_list_size - 3);
|
||||||
|
|
||||||
|
// remove last two elements, make sure list can be emptied
|
||||||
|
assert(!list_is_empty(&my_cool_list));
|
||||||
|
assert(list_remove_data(&my_cool_list, &test_data[1]));
|
||||||
|
assert(!list_is_empty(&my_cool_list));
|
||||||
|
assert(list_remove_data(&my_cool_list, &test_data[3]));
|
||||||
|
assert(list_is_empty(&my_cool_list));
|
||||||
|
|
||||||
|
list_clear(&my_cool_list);
|
||||||
|
|
||||||
|
assert(list_is_empty(&my_cool_list));
|
||||||
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
printf("Testing List Create and Clear.\n");
|
printf("Testing List Create and Clear.\n");
|
||||||
@@ -682,6 +750,9 @@ int main(void)
|
|||||||
printf("Testing List Swap.\n");
|
printf("Testing List Swap.\n");
|
||||||
test_list_swap();
|
test_list_swap();
|
||||||
|
|
||||||
|
printf("Testing List Remove Data.\n");
|
||||||
|
test_remove_data();
|
||||||
|
|
||||||
printf("-------------------------------------------------------------------------------\n");
|
printf("-------------------------------------------------------------------------------\n");
|
||||||
printf("List Tests Passed :)\n");
|
printf("List Tests Passed :)\n");
|
||||||
printf("-------------------------------------------------------------------------------\n");
|
printf("-------------------------------------------------------------------------------\n");
|
||||||
|
|||||||
Reference in New Issue
Block a user