1df8402900
* initial commit * wip * WIP * WIP * clang format * Choose Seed navigation mostly done * seed input navigation done * seed input done * cleanup and clang format * Implement base 10 to 36 conversions * Enable seeded runs * clang format * show default deck sprite, name and description * Rename Deck enum to DeckType * Implement arbitrary 9-patch expand system * Make 9-patch example smaller in the docs * Improve NinePatchRect docs * fix Deck back side sprite palette * clang format * Add last deck description * clang format * Fix 'Z' in seed input keyboard * Fix rebase issue * fix deck sprite not updating * Documents static funcs in run_setup.c * more documentation * clang format * Move Play button to the left in its row * Tests WIP * removing tests for random.h * use the new StateMachine struct * First wave of fixes * Fix the 9-patch again * Changed the base 36 logic so it's in big endian * properly set end of base 36 string to '\0' * typos * fix cursor position after rolling a seed + fix random seed generation * Make keyboard more intuitive to use * static const var * Address more comment from @ricfehr3 * clang format * clang format * clang format * clang format * clang format * clang format * clang format * fix deck palette * create the `DEF_SEED_KEYBOARD_BUTTON_OBJECT` macro * Replace `expand_3x3` by a call to `expand_9-patch` * clang format * clang format * clang format * fix rebase iussue in state_machine.h --------- Co-authored-by: MathisMartin31 <mathis.martin31@gmail.com>
1251 lines
38 KiB
C
1251 lines
38 KiB
C
/**
|
|
* @file run_setup.c
|
|
*
|
|
* @brief Setup Run menu state functions implementation.
|
|
*/
|
|
|
|
#include "game/run_setup.h"
|
|
|
|
#include "background_run_setup_gfx.h"
|
|
#include "button.h"
|
|
#include "card.h"
|
|
#include "game.h"
|
|
#include "game_variables.h"
|
|
#include "graphic_utils.h"
|
|
#include "save.h"
|
|
#include "selection_grid.h"
|
|
#include "sprite.h"
|
|
#include "state_machine.h"
|
|
#include "util.h"
|
|
|
|
#include <tonc.h>
|
|
|
|
// Palette Indices
|
|
|
|
#define CHANGE_DECK_BTN_MAIN_COLOR_PAL_IDX 1
|
|
#define RED_BTN_MAIN_COLOR_PAL_IDX 10
|
|
#define RED_DISABLED_BTN_MAIN_COLOR_PAL_IDX 11
|
|
#define BLUE_BTN_MAIN_COLOR_PAL_IDX 12
|
|
#define BLUE_DISABLED_BTN_MAIN_COLOR_PAL_IDX 13
|
|
#define BACK_BTN_MAIN_COLOR_PAL_IDX 14
|
|
|
|
#define NEW_RUN_BTN_OUTLINE_COLOR_PAL_IDX 30
|
|
#define RESUME_BTN_OUTLINE_COLOR_PAL_IDX 31
|
|
#define CHANGE_DECK_BTN_OUTINE_COLOR_PAL_IDX 32
|
|
#define SEED_CHECK_BTN_OUTLINE_COLOR_PAL_IDX 33
|
|
#define SEED_DECK_BTN_OUTLINE_COLOR_PAL_IDX 34
|
|
#define PLAY_BTN_OUTLINE_COLOR_PAL_IDX 35
|
|
#define BACK_BTN_OUTLINE_COLOR_PAL_IDX 36
|
|
|
|
#define KEYBOARD_1_BTN_OUTLINE_COLOR_PAL_IDX 40
|
|
#define KEYBOARD_2_BTN_OUTLINE_COLOR_PAL_IDX 41
|
|
#define KEYBOARD_3_BTN_OUTLINE_COLOR_PAL_IDX 42
|
|
#define KEYBOARD_4_BTN_OUTLINE_COLOR_PAL_IDX 43
|
|
#define KEYBOARD_5_BTN_OUTLINE_COLOR_PAL_IDX 44
|
|
#define KEYBOARD_6_BTN_OUTLINE_COLOR_PAL_IDX 45
|
|
#define KEYBOARD_7_BTN_OUTLINE_COLOR_PAL_IDX 46
|
|
#define KEYBOARD_8_BTN_OUTLINE_COLOR_PAL_IDX 47
|
|
#define KEYBOARD_9_BTN_OUTLINE_COLOR_PAL_IDX 48
|
|
#define KEYBOARD_0_BTN_OUTLINE_COLOR_PAL_IDX 49
|
|
|
|
#define KEYBOARD_A_BTN_OUTLINE_COLOR_PAL_IDX 50
|
|
#define KEYBOARD_B_BTN_OUTLINE_COLOR_PAL_IDX 51
|
|
#define KEYBOARD_C_BTN_OUTLINE_COLOR_PAL_IDX 52
|
|
#define KEYBOARD_D_BTN_OUTLINE_COLOR_PAL_IDX 53
|
|
#define KEYBOARD_E_BTN_OUTLINE_COLOR_PAL_IDX 54
|
|
#define KEYBOARD_F_BTN_OUTLINE_COLOR_PAL_IDX 55
|
|
#define KEYBOARD_G_BTN_OUTLINE_COLOR_PAL_IDX 56
|
|
#define KEYBOARD_H_BTN_OUTLINE_COLOR_PAL_IDX 57
|
|
#define KEYBOARD_I_BTN_OUTLINE_COLOR_PAL_IDX 58
|
|
#define KEYBOARD_J_BTN_OUTLINE_COLOR_PAL_IDX 59
|
|
|
|
#define KEYBOARD_K_BTN_OUTLINE_COLOR_PAL_IDX 60
|
|
#define KEYBOARD_L_BTN_OUTLINE_COLOR_PAL_IDX 61
|
|
#define KEYBOARD_M_BTN_OUTLINE_COLOR_PAL_IDX 62
|
|
#define KEYBOARD_N_BTN_OUTLINE_COLOR_PAL_IDX 63
|
|
#define KEYBOARD_O_BTN_OUTLINE_COLOR_PAL_IDX 64
|
|
#define KEYBOARD_P_BTN_OUTLINE_COLOR_PAL_IDX 65
|
|
#define KEYBOARD_Q_BTN_OUTLINE_COLOR_PAL_IDX 66
|
|
#define KEYBOARD_R_BTN_OUTLINE_COLOR_PAL_IDX 67
|
|
#define KEYBOARD_S_BTN_OUTLINE_COLOR_PAL_IDX 68
|
|
#define KEYBOARD_T_BTN_OUTLINE_COLOR_PAL_IDX 69
|
|
|
|
#define KEYBOARD_RAND_BTN_OUTLINE_COLOR_PAL_IDX 70
|
|
#define KEYBOARD_U_BTN_OUTLINE_COLOR_PAL_IDX 71
|
|
#define KEYBOARD_V_BTN_OUTLINE_COLOR_PAL_IDX 72
|
|
#define KEYBOARD_W_BTN_OUTLINE_COLOR_PAL_IDX 73
|
|
#define KEYBOARD_X_BTN_OUTLINE_COLOR_PAL_IDX 74
|
|
#define KEYBOARD_Y_BTN_OUTLINE_COLOR_PAL_IDX 75
|
|
#define KEYBOARD_Z_BTN_OUTLINE_COLOR_PAL_IDX 76
|
|
#define KEYBOARD_DEL_BTN_OUTLINE_COLOR_PAL_IDX 77
|
|
|
|
enum RunSetupSubstate
|
|
{
|
|
RUN_SETUP_SUBSTATE_CHOOSE_DECK,
|
|
RUN_SETUP_SUBSTATE_CHOOSE_SEED,
|
|
RUN_SETUP_SUBSTATE_RESUME,
|
|
RUN_SETUP_SUBSTATE_MAX
|
|
};
|
|
|
|
static void choose_deck_substate_init(void);
|
|
static void choose_deck_substate_update(void);
|
|
|
|
static void seed_keyboard_substate_init(void);
|
|
static void seed_keyboard_substate_update(void);
|
|
|
|
static void resume_substate_init(void);
|
|
|
|
static StateInfo state_info[] = {
|
|
STATE_INFO_INIT_UPDATE_FN(choose_deck_substate_init, choose_deck_substate_update),
|
|
STATE_INFO_INIT_UPDATE_FN(seed_keyboard_substate_init, seed_keyboard_substate_update),
|
|
STATE_INFO_INIT_UPDATE_FN(resume_substate_init, noop)
|
|
};
|
|
|
|
static StateMachine run_setup_sm = {
|
|
.state_infos = &state_info[0],
|
|
.num_infos = RUN_SETUP_SUBSTATE_MAX,
|
|
};
|
|
|
|
#pragma region LAYOUT
|
|
/*******************************************************************************
|
|
* TILES LAYOUT
|
|
******************************************************************************/
|
|
|
|
// clang-format off
|
|
static const Rect RUN_SETUP_RESUME_TAB_DISABLED_SRC = {19, 22, 23, 23};
|
|
static const BG_POINT RUN_SETUP_RESUME_TAB_DISABLED_DEST_POS = {15, 1};
|
|
|
|
static const BG_POINT RUN_SETUP_FRAME_BG_SE_FILL_SRC_POS = {9 , 31};
|
|
|
|
static const Rect RUN_SETUP_CHOOSE_DECK_CHOICE_BG_9_PTCH_DEST = {6 , 4 , 23, 13};
|
|
static const NinePatchRect RUN_SETUP_CHOOSE_DECK_CHOICE_BG_9_PTCH_SRC = {
|
|
{ 0 , 21, 7 , 28},
|
|
{ 5 , 3 , 2 , 4 }
|
|
};
|
|
|
|
static const Rect RUN_SETUP_CHOOSE_DECK_CLEAN_LEFT_DEST = {4 , 6 , 5 , 13};
|
|
static const Rect RUN_SETUP_CHOOSE_DECK_CLEAN_RIGHT_DEST = {24, 6 ,25 , 13};
|
|
|
|
static const Rect RUN_SETUP_CHOOSE_DECK_USE_SEED_BTN_OFF_SRC = {30, 8 , 31, 10};
|
|
static const Rect RUN_SETUP_CHOOSE_DECK_USE_SEED_BTN_ON_SRC = {30, 11, 31, 13};
|
|
static const BG_POINT RUN_SETUP_CHOOSE_DECK_USE_SEED_BTN_DEST_POS = {17, 14};
|
|
|
|
static const BG_POINT RUN_SETUP_CHOOSE_DECK_SEED_BTN_3X3_SRC_POS = {0 , 29};
|
|
static const BG_POINT RUN_SETUP_CHOOSE_DECK_SEED_BTN_DISABLED_3X3_SRC_POS = {3 , 29};
|
|
static const Rect RUN_SETUP_CHOOSE_DECK_SEED_BTN_DEST = {19, 14, 24, 16};
|
|
|
|
static const Rect RUN_SETUP_CHOOSE_SEED_FRAME_CLEAN_DEST = {4 , 3 , 25, 5 };
|
|
static const Rect RUN_SETUP_CHOOSE_SEED_KEYBOARD_SRC = {10, 24, 31, 31};
|
|
static const Rect RUN_SETUP_CHOOSE_SEED_FIELD_SRC = {24, 22, 31, 23};
|
|
static const BG_POINT RUN_SETUP_CHOOSE_SEED_KEYBOARD_DEST_POS = {4 , 6};
|
|
static const BG_POINT RUN_SETUP_CHOOSE_SEED_FIELD_DEST_POS = {11, 4};
|
|
|
|
static const BG_POINT RUN_SETUP_CHOOSE_SEED_DECK_BTN_3X3_SRC_POS = RUN_SETUP_CHOOSE_DECK_SEED_BTN_3X3_SRC_POS;
|
|
static const Rect RUN_SETUP_CHOOSE_SEED_DECK_BTN_DEST = {17, 14, 24, 16};
|
|
|
|
// Pixel sizes
|
|
#define RUN_SETUP_DECK_SPRITE_T_X 48
|
|
#define RUN_SETUP_DECK_SPRITE_T_Y 54
|
|
static const BG_POINT RUN_SETUP_DECK_NAME_TEXT_POS = {80 , 40 };
|
|
static const BG_POINT RUN_SETUP_DECK_DESC_TEXT_POS = {80 , 56 };
|
|
static const Rect RUN_SETUP_DECK_NAME_DESC_RECT = {80 , 40 ,176, 96 };
|
|
static const BG_POINT RUN_SETUP_SEED_FIELD_TEXT_POS = {96 , 40 };
|
|
static const BG_POINT RUN_SETUP_SEED_DECK_TEXT_POS = {152, 120};
|
|
static const BG_POINT RUN_SETUP_PLAY_TEXT_POS = {76 , 120};
|
|
static const BG_POINT RUN_SETUP_BACK_TEXT_POS = {104, 136};
|
|
// clang-format on
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region TAB BAR
|
|
/*******************************************************************************
|
|
* TAB BAR BUTTONS
|
|
*
|
|
* Part of all substate but not state-dependent, they are always updated the
|
|
* same way, thus `run_setup_tabs_update()` will always be called.
|
|
******************************************************************************/
|
|
|
|
enum RunSetupTab
|
|
{
|
|
RUN_SETUP_TAB_NEW_RUN,
|
|
RUN_SETUP_TAB_RESUME,
|
|
RUN_SETUP_TAB_MAX
|
|
};
|
|
|
|
static bool is_saved_game_valid = false;
|
|
|
|
static void tab_set_highlight(enum RunSetupTab tab_sel);
|
|
static void run_setup_tabs_update(void);
|
|
|
|
// clang-format off
|
|
static Button tabs_buttons[RUN_SETUP_TAB_MAX] = {
|
|
{NEW_RUN_BTN_OUTLINE_COLOR_PAL_IDX, RED_BTN_MAIN_COLOR_PAL_IDX, NULL, NULL},
|
|
{RESUME_BTN_OUTLINE_COLOR_PAL_IDX, RED_BTN_MAIN_COLOR_PAL_IDX, NULL, NULL},
|
|
};
|
|
// clang-format on
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region BOTTOM BUTTONS
|
|
/*******************************************************************************
|
|
* BOTTOM BUTTONS
|
|
*
|
|
* Declared before any SubState because they are common to all of them.
|
|
*
|
|
* The behaviour and appearance of the Seed/Deck button will change from state
|
|
* to state so these Buttons can't be defined here. functions related to their
|
|
* behaviour can, though.
|
|
*
|
|
* The Play and Back buttons will always be the same, but Play needs to be
|
|
* duplicated anyway to be integrated to each SubState's SelectionGrid, since
|
|
* the row it is on doesn't always have the same number of buttons.
|
|
******************************************************************************/
|
|
|
|
static int back_row_get_size(void);
|
|
static void back_row_on_key_transit(SelectionGrid* selection_grid, Selection* selection);
|
|
|
|
static inline void toggle_seed_enabled(bool enable);
|
|
static void use_seed_on_pressed(void);
|
|
static void seed_on_pressed(void);
|
|
static void deck_on_pressed(void);
|
|
static void play_on_pressed(void);
|
|
static void back_on_pressed(void);
|
|
|
|
// clang-format off
|
|
static Button back_button = {
|
|
BACK_BTN_OUTLINE_COLOR_PAL_IDX, BACK_BTN_MAIN_COLOR_PAL_IDX,
|
|
back_on_pressed, NULL
|
|
};
|
|
// clang-format on
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region CHOOSE DECK
|
|
/*******************************************************************************
|
|
* CHOOSE DECK SUBSTATE
|
|
******************************************************************************/
|
|
|
|
enum RunSetupDeckRows
|
|
{
|
|
RUN_SETUP_DECK_ROW_CHANGE_DECK,
|
|
RUN_SETUP_DECK_ROW_SEED_PLAY,
|
|
RUN_SETUP_DECK_ROW_BACK,
|
|
RUN_SETUP_DECK_ROW_MAX
|
|
};
|
|
|
|
enum RunSetupDeckBottomButtons
|
|
{
|
|
RUN_SETUP_DECK_BB_PLAY,
|
|
RUN_SETUP_DECK_BB_USE_SEED,
|
|
RUN_SETUP_DECK_BB_SEED,
|
|
RUN_SETUP_DECK_BB_MAX
|
|
};
|
|
|
|
// CHOOSE DECK SELECTION GRID
|
|
|
|
static int change_deck_get_row_size(void);
|
|
static bool choose_deck_row_on_selection_changed(
|
|
SelectionGrid* selection_grid,
|
|
int row_idx,
|
|
const Selection* prev_selection,
|
|
const Selection* new_selection
|
|
);
|
|
static int seed_play_get_row_size(void);
|
|
static void seed_play_row_on_key_transit(SelectionGrid* selection_grid, Selection* selection);
|
|
|
|
// clang-format off
|
|
static SelectionGridRow choose_deck_rows[RUN_SETUP_DECK_ROW_MAX] = {
|
|
{
|
|
RUN_SETUP_DECK_ROW_CHANGE_DECK,
|
|
change_deck_get_row_size,
|
|
choose_deck_row_on_selection_changed,
|
|
NULL,
|
|
{.wrap = true}
|
|
}, {
|
|
RUN_SETUP_DECK_ROW_SEED_PLAY,
|
|
seed_play_get_row_size,
|
|
choose_deck_row_on_selection_changed,
|
|
seed_play_row_on_key_transit,
|
|
{.wrap = false}
|
|
}, {
|
|
RUN_SETUP_DECK_ROW_BACK,
|
|
back_row_get_size,
|
|
choose_deck_row_on_selection_changed,
|
|
back_row_on_key_transit,
|
|
{.wrap = false}
|
|
}
|
|
};
|
|
// clang-format on
|
|
|
|
static const Selection RUN_SETUP_CHOOSE_DECK_INIT_SEL = {0, 0};
|
|
static const Selection RUN_SETUP_CHOOSE_DECK_SEL_FROM_SEED = {2, 1};
|
|
|
|
static SelectionGrid choose_deck_selection_grid = {
|
|
choose_deck_rows,
|
|
RUN_SETUP_DECK_ROW_MAX,
|
|
RUN_SETUP_CHOOSE_DECK_INIT_SEL
|
|
};
|
|
|
|
// CHOOSE DECK BUTTONS
|
|
|
|
static void change_deck_on_pressed(void);
|
|
|
|
static Button change_deck_button = {
|
|
CHANGE_DECK_BTN_OUTINE_COLOR_PAL_IDX,
|
|
CHANGE_DECK_BTN_MAIN_COLOR_PAL_IDX,
|
|
change_deck_on_pressed,
|
|
NULL
|
|
};
|
|
|
|
// clang-format off
|
|
// Seed button is disabled by default but can be enabled by clicking the toggle just next to it
|
|
static Button choose_deck_bottom_buttons[RUN_SETUP_DECK_BB_MAX] = {
|
|
{
|
|
PLAY_BTN_OUTLINE_COLOR_PAL_IDX, BLUE_BTN_MAIN_COLOR_PAL_IDX,
|
|
play_on_pressed, NULL
|
|
}, {
|
|
SEED_CHECK_BTN_OUTLINE_COLOR_PAL_IDX, RED_BTN_MAIN_COLOR_PAL_IDX,
|
|
use_seed_on_pressed, NULL
|
|
}, {
|
|
SEED_DECK_BTN_OUTLINE_COLOR_PAL_IDX, BLUE_DISABLED_BTN_MAIN_COLOR_PAL_IDX,
|
|
seed_on_pressed, NULL
|
|
}
|
|
};
|
|
// clang-format on
|
|
|
|
static bool use_seed = false;
|
|
static CardObject* run_setup_deck = NULL;
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region SEED KEYBOARD
|
|
/*******************************************************************************
|
|
* SEED KEYBOARD SUBSTATE
|
|
******************************************************************************/
|
|
|
|
#define KEYBOARD_WIDTH 10
|
|
#define KEYBOARD_HEIGHT 4
|
|
|
|
enum RunSetupSeedRows
|
|
{
|
|
RUN_SETUP_SEED_ROW_KEY0,
|
|
RUN_SETUP_SEED_ROW_KEY1,
|
|
RUN_SETUP_SEED_ROW_KEY2,
|
|
RUN_SETUP_SEED_ROW_KEY3,
|
|
RUN_SETUP_SEED_ROW_DECK_PLAY,
|
|
RUN_SETUP_SEED_ROW_BACK,
|
|
RUN_SETUP_SEED_ROW_MAX
|
|
};
|
|
|
|
enum RunSetupKeyboardButtons
|
|
{
|
|
RUN_SETUP_KEYBOARD_BEGIN,
|
|
// Row 0
|
|
RUN_SETUP_KEYBOARD_1 = RUN_SETUP_KEYBOARD_BEGIN,
|
|
RUN_SETUP_KEYBOARD_2,
|
|
RUN_SETUP_KEYBOARD_3,
|
|
RUN_SETUP_KEYBOARD_4,
|
|
RUN_SETUP_KEYBOARD_5,
|
|
RUN_SETUP_KEYBOARD_6,
|
|
RUN_SETUP_KEYBOARD_7,
|
|
RUN_SETUP_KEYBOARD_8,
|
|
RUN_SETUP_KEYBOARD_9,
|
|
RUN_SETUP_KEYBOARD_0,
|
|
// Row 1
|
|
RUN_SETUP_KEYBOARD_A,
|
|
RUN_SETUP_KEYBOARD_B,
|
|
RUN_SETUP_KEYBOARD_C,
|
|
RUN_SETUP_KEYBOARD_D,
|
|
RUN_SETUP_KEYBOARD_E,
|
|
RUN_SETUP_KEYBOARD_F,
|
|
RUN_SETUP_KEYBOARD_G,
|
|
RUN_SETUP_KEYBOARD_H,
|
|
RUN_SETUP_KEYBOARD_I,
|
|
RUN_SETUP_KEYBOARD_J,
|
|
// Row 2
|
|
RUN_SETUP_KEYBOARD_K,
|
|
RUN_SETUP_KEYBOARD_L,
|
|
RUN_SETUP_KEYBOARD_M,
|
|
RUN_SETUP_KEYBOARD_N,
|
|
RUN_SETUP_KEYBOARD_O,
|
|
RUN_SETUP_KEYBOARD_P,
|
|
RUN_SETUP_KEYBOARD_Q,
|
|
RUN_SETUP_KEYBOARD_R,
|
|
RUN_SETUP_KEYBOARD_S,
|
|
RUN_SETUP_KEYBOARD_T,
|
|
// Row 3
|
|
RUN_SETUP_KEYBOARD_RAND,
|
|
RUN_SETUP_KEYBOARD_U,
|
|
RUN_SETUP_KEYBOARD_V,
|
|
RUN_SETUP_KEYBOARD_W,
|
|
RUN_SETUP_KEYBOARD_X,
|
|
RUN_SETUP_KEYBOARD_Y,
|
|
RUN_SETUP_KEYBOARD_Z,
|
|
RUN_SETUP_KEYBOARD_DEL,
|
|
RUN_SETUP_KEYBOARD_MAX
|
|
};
|
|
|
|
enum RunSetupSeedBottomButtons
|
|
{
|
|
RUN_SETUP_SEED_BB_PLAY,
|
|
RUN_SETUP_SEED_BB_DECK,
|
|
RUN_SETUP_SEED_BB_MAX
|
|
};
|
|
|
|
// CHOOSE SEED SELECTION GRID
|
|
|
|
static int choose_seed_get_keyboard_row_size(void);
|
|
static int choose_seed_get_keyboard_short_row_size(void);
|
|
static int choose_seed_get_bottom_row_size(void);
|
|
static void choose_seed_row_on_key_transit(SelectionGrid* selection_grid, Selection* selection);
|
|
static bool choose_seed_row_on_selection_changed(
|
|
SelectionGrid* selection_grid,
|
|
int row_idx,
|
|
const Selection* prev_selection,
|
|
const Selection* new_selection
|
|
);
|
|
|
|
// clang-format off
|
|
static SelectionGridRow choose_seed_selection_rows[] = {
|
|
// First 4 rows are the seed keyboard itself.
|
|
{
|
|
RUN_SETUP_SEED_ROW_KEY0,
|
|
choose_seed_get_keyboard_row_size,
|
|
choose_seed_row_on_selection_changed,
|
|
choose_seed_row_on_key_transit,
|
|
{.wrap = true}
|
|
}, {
|
|
RUN_SETUP_SEED_ROW_KEY1,
|
|
choose_seed_get_keyboard_row_size,
|
|
choose_seed_row_on_selection_changed,
|
|
choose_seed_row_on_key_transit,
|
|
{.wrap = true}
|
|
}, {
|
|
RUN_SETUP_SEED_ROW_KEY2,
|
|
choose_seed_get_keyboard_row_size,
|
|
choose_seed_row_on_selection_changed,
|
|
choose_seed_row_on_key_transit,
|
|
{.wrap = true}
|
|
}, {
|
|
RUN_SETUP_SEED_ROW_KEY3,
|
|
choose_seed_get_keyboard_short_row_size,
|
|
choose_seed_row_on_selection_changed,
|
|
choose_seed_row_on_key_transit,
|
|
{.wrap = true}
|
|
},
|
|
// Then we have the Deck/PLAY and Back rows
|
|
{
|
|
RUN_SETUP_SEED_ROW_DECK_PLAY,
|
|
choose_seed_get_bottom_row_size,
|
|
choose_seed_row_on_selection_changed,
|
|
choose_seed_row_on_key_transit,
|
|
{.wrap = false}
|
|
}, {
|
|
RUN_SETUP_SEED_ROW_BACK,
|
|
back_row_get_size,
|
|
choose_seed_row_on_selection_changed,
|
|
back_row_on_key_transit,
|
|
{.wrap = false}
|
|
}
|
|
};
|
|
// clang-format on
|
|
|
|
static const Selection RUN_SETUP_CHOOSE_SEED_INIT_SEL = {1, 4};
|
|
|
|
static SelectionGrid choose_seed_selection_grid = {
|
|
choose_seed_selection_rows,
|
|
RUN_SETUP_SEED_ROW_MAX,
|
|
RUN_SETUP_CHOOSE_SEED_INIT_SEL
|
|
};
|
|
|
|
// CHOOSE SEED BUTTONS
|
|
|
|
static void keyboard_button_on_pressed(void);
|
|
|
|
// clang-format off
|
|
|
|
#define DEF_SEED_KEYBOARD_BUTTON_OBJECT(key) \
|
|
{ \
|
|
KEYBOARD_##key##_BTN_OUTLINE_COLOR_PAL_IDX, RED_BTN_MAIN_COLOR_PAL_IDX, \
|
|
keyboard_button_on_pressed, NULL \
|
|
} \
|
|
|
|
// Button at [3][0] is there for consistency but is not used since it corresponds to the "Random" key
|
|
static Button keyboard_buttons[KEYBOARD_HEIGHT * KEYBOARD_WIDTH] = {
|
|
// Row 0
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(1),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(2),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(3),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(4),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(5),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(6),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(7),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(8),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(9),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(0),
|
|
// Row 1
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(A),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(B),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(C),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(D),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(E),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(F),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(G),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(H),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(I),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(J),
|
|
// Row 2
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(K),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(L),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(M),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(N),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(O),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(P),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(Q),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(R),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(S),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(T),
|
|
// Row 3
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(RAND),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(U),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(V),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(W),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(X),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(Y),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(Z),
|
|
DEF_SEED_KEYBOARD_BUTTON_OBJECT(DEL)
|
|
};
|
|
|
|
static Button choose_seed_bottom_buttons[2] = {
|
|
{
|
|
PLAY_BTN_OUTLINE_COLOR_PAL_IDX, BLUE_BTN_MAIN_COLOR_PAL_IDX,
|
|
play_on_pressed, NULL
|
|
}, {
|
|
SEED_DECK_BTN_OUTLINE_COLOR_PAL_IDX, BLUE_BTN_MAIN_COLOR_PAL_IDX,
|
|
deck_on_pressed, NULL
|
|
}
|
|
};
|
|
|
|
static const char keyboard_buttons_to_char[KEYBOARD_HEIGHT * KEYBOARD_WIDTH] = {
|
|
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
|
|
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
|
|
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
|
|
' ', 'U', 'V', 'W', 'X', 'Y', 'Z'
|
|
};
|
|
// clang-format on
|
|
|
|
// Size BASE36_MAX_DIGITS + 1 to always have '\0' at the end
|
|
static char seed_str[BASE36_MAX_DIGITS + 1] = {'\0'};
|
|
static u8 seed_cursor_pos = 0;
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region RESUME
|
|
/*******************************************************************************
|
|
* RESUME SUBSTATE
|
|
******************************************************************************/
|
|
|
|
enum RunSetupResumeRows
|
|
{
|
|
RUN_SETUP_RESUME_ROW_PLAY,
|
|
RUN_SETUP_RESUME_ROW_BACK,
|
|
RUN_SETUP_RESUME_ROW_MAX
|
|
};
|
|
|
|
// RESUME SELECTION GRID
|
|
|
|
// RESUME BUTTONS
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region STATE FUNCTIONS
|
|
/*******************************************************************************
|
|
* STATE FUNCTIONS
|
|
******************************************************************************/
|
|
|
|
void game_run_setup_change_background(void)
|
|
{
|
|
GRIT_CPY(pal_bg_mem, background_run_setup_gfxPal);
|
|
GRIT_CPY(&tile_mem[MAIN_BG_CBB], background_run_setup_gfxTiles);
|
|
GRIT_CPY(&se_mem[MAIN_BG_SBB], background_run_setup_gfxMap);
|
|
}
|
|
|
|
void game_run_setup_on_init(void)
|
|
{
|
|
state_machine_register(&run_setup_sm);
|
|
game_run_setup_change_background();
|
|
|
|
// Rank doesn't matter, won't see it
|
|
run_setup_deck = card_object_new(card_new(SPADES, ACE));
|
|
|
|
// We put it at the same layer as the main menu Ace card, but it's okay because
|
|
// both cards do not exist at the same time, one is destroyed before the other is created.
|
|
card_object_set_sprite_face_down(run_setup_deck, g_game_vars.deck, 0);
|
|
|
|
run_setup_deck->sprite_object->tx = int2fx(RUN_SETUP_DECK_SPRITE_T_X);
|
|
run_setup_deck->sprite_object->ty = int2fx(RUN_SETUP_DECK_SPRITE_T_Y);
|
|
|
|
// Need to do -1 here so that the sprite isn't at the target position and gets updated
|
|
run_setup_deck->sprite_object->x = run_setup_deck->sprite_object->tx - 1;
|
|
run_setup_deck->sprite_object->y = run_setup_deck->sprite_object->ty - 1;
|
|
|
|
card_object_update(run_setup_deck);
|
|
|
|
/* Uncomment these lines when we figure out how to properly restore a game save
|
|
is_saved_game_valid = is_game_data_valid();
|
|
if (is_saved_game_valid)
|
|
{
|
|
state_machine_change_state(&run_setup_sm, RUN_SETUP_SUBSTATE_RESUME);
|
|
}
|
|
*/
|
|
|
|
// Land on the deck swapping button when landing on this state from the Main Menu
|
|
choose_deck_selection_grid.selection = RUN_SETUP_CHOOSE_DECK_INIT_SEL;
|
|
state_machine_change_state(&run_setup_sm, RUN_SETUP_SUBSTATE_CHOOSE_DECK);
|
|
}
|
|
|
|
void game_run_setup_on_update(void)
|
|
{
|
|
run_setup_tabs_update();
|
|
}
|
|
|
|
void game_run_setup_on_exit(void)
|
|
{
|
|
state_machine_remove(&run_setup_sm);
|
|
|
|
card_destroy(&run_setup_deck->card);
|
|
card_object_destroy(&run_setup_deck);
|
|
|
|
tte_erase_screen();
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region IMPLEMENTATION
|
|
/*******************************************************************************
|
|
* STATIC FUNCTIONS IMPLEMENTATION
|
|
******************************************************************************/
|
|
|
|
// CHOOSE DECK
|
|
|
|
/**
|
|
* @brief Initializes the Deck changing substate.
|
|
*/
|
|
static void choose_deck_substate_init(void)
|
|
{
|
|
// Show Deck sprite, name and TODO: description
|
|
obj_unhide(run_setup_deck->sprite_object->sprite->obj, 0);
|
|
print_deck_name(g_game_vars.deck, RUN_SETUP_DECK_NAME_TEXT_POS);
|
|
print_deck_description(g_game_vars.deck, RUN_SETUP_DECK_DESC_TEXT_POS);
|
|
|
|
// Clean frame and expand 9-patch for the Deck choice background
|
|
main_bg_se_copy_expand_tile(
|
|
RUN_SETUP_CHOOSE_DECK_CLEAN_LEFT_DEST,
|
|
RUN_SETUP_FRAME_BG_SE_FILL_SRC_POS
|
|
);
|
|
main_bg_se_copy_expand_tile(
|
|
RUN_SETUP_CHOOSE_DECK_CLEAN_RIGHT_DEST,
|
|
RUN_SETUP_FRAME_BG_SE_FILL_SRC_POS
|
|
);
|
|
main_bg_se_copy_expand_9_patch(
|
|
RUN_SETUP_CHOOSE_DECK_CHOICE_BG_9_PTCH_DEST,
|
|
&RUN_SETUP_CHOOSE_DECK_CHOICE_BG_9_PTCH_SRC
|
|
);
|
|
|
|
// TODO: add left/right navigation arrows once more decks have been implemented
|
|
|
|
// Set Tab to "New Run"
|
|
main_bg_se_copy_rect(RUN_SETUP_RESUME_TAB_DISABLED_SRC, RUN_SETUP_RESUME_TAB_DISABLED_DEST_POS);
|
|
tab_set_highlight(RUN_SETUP_TAB_NEW_RUN);
|
|
|
|
// Set button highlights
|
|
button_set_highlight(&change_deck_button, true);
|
|
button_set_highlight(&choose_deck_bottom_buttons[RUN_SETUP_DECK_BB_USE_SEED], false);
|
|
toggle_seed_enabled(use_seed); // This just re-applies the current value
|
|
button_set_highlight(&choose_deck_bottom_buttons[RUN_SETUP_DECK_BB_PLAY], false);
|
|
button_set_highlight(&back_button, false);
|
|
|
|
// Print button text
|
|
tte_printf(
|
|
"#{P:%d,%d; cx:0x%X000}%s",
|
|
RUN_SETUP_PLAY_TEXT_POS.x,
|
|
RUN_SETUP_PLAY_TEXT_POS.y,
|
|
TTE_WHITE_PB,
|
|
"PLAY"
|
|
);
|
|
tte_printf(
|
|
"#{P:%d,%d; cx:0x%X000}%s",
|
|
RUN_SETUP_BACK_TEXT_POS.x,
|
|
RUN_SETUP_BACK_TEXT_POS.y,
|
|
TTE_WHITE_PB,
|
|
"Back"
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @brief Update the deck changing substate by handling user input though its
|
|
* corresponding SelectionGrid.
|
|
*/
|
|
static void choose_deck_substate_update(void)
|
|
{
|
|
selection_grid_process_input(&choose_deck_selection_grid);
|
|
}
|
|
|
|
/**
|
|
* @brief Returns the width of the Change Deck Button's row
|
|
*
|
|
* @return 1
|
|
*/
|
|
static int change_deck_get_row_size(void)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* @brief Gets the Button corresponding to the current Selection in `choose_deck_selection_grid`
|
|
*
|
|
* @param sel Selection within `choose_deck_selection_grid`
|
|
* @return a pointer to the currently hovered Button
|
|
*/
|
|
static inline Button* change_deck_get_button_from_sel(const Selection* sel)
|
|
{
|
|
switch (sel->y)
|
|
{
|
|
case RUN_SETUP_DECK_ROW_CHANGE_DECK:
|
|
return &change_deck_button;
|
|
|
|
case RUN_SETUP_DECK_ROW_SEED_PLAY:
|
|
if (sel->x >= RUN_SETUP_DECK_BB_MAX)
|
|
return NULL;
|
|
return &choose_deck_bottom_buttons[sel->x];
|
|
|
|
case RUN_SETUP_DECK_ROW_BACK:
|
|
return &back_button;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief Handle directional input in `choose_deck_selection_grid`
|
|
*
|
|
* @param selection_grid pointer to `choose_deck_selection_grid`
|
|
* @param row_idx row index in `choose_deck_selection_grid` this function is being called for
|
|
* @param prev_selection previous selection in selection_grid
|
|
* @param new_selection new selection in selection_grid
|
|
* @return bool
|
|
*/
|
|
static bool choose_deck_row_on_selection_changed(
|
|
SelectionGrid* selection_grid,
|
|
int row_idx,
|
|
const Selection* prev_selection,
|
|
const Selection* new_selection
|
|
)
|
|
{
|
|
button_set_highlight(change_deck_get_button_from_sel(prev_selection), false);
|
|
button_set_highlight(change_deck_get_button_from_sel(new_selection), true);
|
|
|
|
// TODO: detect left/right press on Change Deck row to allow swapping
|
|
// Decks need to be implemented for this
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns the width of the Seed checkbox/Seed/Play buttons' row
|
|
*
|
|
* @return 3
|
|
*/
|
|
static int seed_play_get_row_size(void)
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
/**
|
|
* @brief Handle button inputs for `choose_deck_selection_grid`
|
|
*
|
|
* @param selection_grid pointer to `choose_deck_selection_grid`
|
|
* @param selection current Selection in selection_grid
|
|
*/
|
|
static void seed_play_row_on_key_transit(SelectionGrid* selection_grid, Selection* selection)
|
|
{
|
|
button_press(&choose_deck_bottom_buttons[selection->x]);
|
|
}
|
|
|
|
// CHOOSE SEED
|
|
|
|
/**
|
|
* @brief Prints the string representing the chosen seed in base-36
|
|
*/
|
|
static inline void update_seed_text(void)
|
|
{
|
|
tte_printf(
|
|
"#{P:%d,%d; cx:0x%X000}%-*s",
|
|
RUN_SETUP_SEED_FIELD_TEXT_POS.x,
|
|
RUN_SETUP_SEED_FIELD_TEXT_POS.y,
|
|
TTE_BLACK_PB,
|
|
BASE36_MAX_DIGITS + 1,
|
|
seed_str
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize the Choose Seed substate
|
|
*/
|
|
static void seed_keyboard_substate_init(void)
|
|
{
|
|
choose_seed_selection_grid.selection = RUN_SETUP_CHOOSE_SEED_INIT_SEL;
|
|
tte_erase_rect_wrapper(RUN_SETUP_DECK_NAME_DESC_RECT);
|
|
|
|
// Hide Deck card sprite
|
|
obj_hide(run_setup_deck->sprite_object->sprite->obj);
|
|
|
|
// Clean deck swap screen with frame BG color
|
|
main_bg_se_copy_expand_tile(
|
|
RUN_SETUP_CHOOSE_SEED_FRAME_CLEAN_DEST,
|
|
RUN_SETUP_FRAME_BG_SE_FILL_SRC_POS
|
|
);
|
|
|
|
// Copy keyboard tiles
|
|
main_bg_se_copy_rect(
|
|
RUN_SETUP_CHOOSE_SEED_KEYBOARD_SRC,
|
|
RUN_SETUP_CHOOSE_SEED_KEYBOARD_DEST_POS
|
|
);
|
|
main_bg_se_copy_rect(RUN_SETUP_CHOOSE_SEED_FIELD_SRC, RUN_SETUP_CHOOSE_SEED_FIELD_DEST_POS);
|
|
|
|
// Replace seed toggle and button by "Deck" button that allows going back to deck substate
|
|
main_bg_se_copy_expand_3x3_rect(
|
|
RUN_SETUP_CHOOSE_SEED_DECK_BTN_DEST,
|
|
RUN_SETUP_CHOOSE_SEED_DECK_BTN_3X3_SRC_POS
|
|
);
|
|
tte_printf(
|
|
"#{P:%d,%d; cx:0x%X000}%s",
|
|
RUN_SETUP_SEED_DECK_TEXT_POS.x,
|
|
RUN_SETUP_SEED_DECK_TEXT_POS.y,
|
|
TTE_WHITE_PB,
|
|
"Deck " // extra space after to clean potential "Seed" text
|
|
);
|
|
|
|
// Set buttons highlight
|
|
for (enum RunSetupKeyboardButtons key = RUN_SETUP_KEYBOARD_BEGIN; key < RUN_SETUP_KEYBOARD_MAX;
|
|
key++)
|
|
{
|
|
button_set_highlight(&keyboard_buttons[key], false);
|
|
}
|
|
button_set_highlight(&choose_seed_bottom_buttons[RUN_SETUP_SEED_BB_PLAY], false);
|
|
button_set_highlight(&choose_seed_bottom_buttons[RUN_SETUP_SEED_BB_DECK], true);
|
|
button_set_highlight(&back_button, false);
|
|
|
|
// Print seed text
|
|
update_seed_text();
|
|
}
|
|
|
|
/**
|
|
* @brief Update the Choose Seed substate by handling user inputs though the
|
|
* corresponding `choose_seed_selection_grid` SelectionGrid
|
|
*/
|
|
static void seed_keyboard_substate_update(void)
|
|
{
|
|
selection_grid_process_input(&choose_seed_selection_grid);
|
|
}
|
|
|
|
/**
|
|
* @brief Returns the width of the 3 top keyboard rows
|
|
*
|
|
* @return `KEYBOARD_WIDTH = 10`
|
|
*/
|
|
static int choose_seed_get_keyboard_row_size(void)
|
|
{
|
|
return KEYBOARD_WIDTH;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns the width of the bottom keyboard row, the shortest one
|
|
*
|
|
* @return `KEYBOARD_WIDTH - 2 = 8`
|
|
*/
|
|
static int choose_seed_get_keyboard_short_row_size(void)
|
|
{
|
|
return KEYBOARD_WIDTH - 2;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns the width of the Deck/Play buttons' row
|
|
*
|
|
* @return 2
|
|
*/
|
|
static int choose_seed_get_bottom_row_size(void)
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
/**
|
|
* @brief Deletes the last char in the seed string base-36 representation
|
|
*/
|
|
static inline void reroll_seed_str(void)
|
|
{
|
|
// Need to roll a new seed and get a "real" random number. Otherwise the seed is still stuck at
|
|
// 0 and we'll have the same sequence of generated seeds each time.
|
|
// Also, don't use the shuffled seed as is, or we'll just end up with sequential seeds when
|
|
// rolling multiple times.
|
|
rng_shuffle_seed();
|
|
u32 new_seed = rng_get_u32();
|
|
rng_set_seed(new_seed);
|
|
u32_to_base36(new_seed, seed_str);
|
|
update_seed_text();
|
|
seed_cursor_pos = BASE36_MAX_DIGITS;
|
|
}
|
|
|
|
/**
|
|
* @brief Deletes the last char in the seed string base-36 representation
|
|
*/
|
|
static inline void delete_seed_char(void)
|
|
{
|
|
if (seed_cursor_pos == 0)
|
|
return;
|
|
|
|
seed_str[--seed_cursor_pos] = '\0';
|
|
update_seed_text();
|
|
}
|
|
|
|
/**
|
|
* @brief Add a new char at the end of the seed string
|
|
*
|
|
* @param key index of the keyboard key corresponding to the char we want to add
|
|
*/
|
|
static inline void type_seed_char(enum RunSetupKeyboardButtons key)
|
|
{
|
|
if (seed_cursor_pos >= BASE36_MAX_DIGITS)
|
|
return;
|
|
|
|
seed_str[seed_cursor_pos++] = keyboard_buttons_to_char[key];
|
|
update_seed_text();
|
|
}
|
|
|
|
/**
|
|
* @brief Get the keyboard key index from a SelectionGrid's Selection. The returned
|
|
* value is valid only if the Selection is within the keyboard Rows.
|
|
*
|
|
* @param sel the Selection within the `choose_seed_selection_grid`
|
|
* @return the key index
|
|
*/
|
|
static inline enum RunSetupKeyboardButtons get_keyboard_index_from_sel(const Selection* sel)
|
|
{
|
|
return sel->x + KEYBOARD_WIDTH * sel->y;
|
|
}
|
|
|
|
/**
|
|
* @brief Keyboard key Button callback
|
|
*/
|
|
static void keyboard_button_on_pressed(void)
|
|
{
|
|
// Type something only if the button pressed in on the keyboard
|
|
if (choose_seed_selection_grid.selection.y > RUN_SETUP_SEED_ROW_KEY3)
|
|
return;
|
|
|
|
// Type only if the seed string is not full.
|
|
// The cursor position is unsigned so always positive, but we still need to
|
|
// ensure it doersn't go out of bounds by more than 1 so that we can always
|
|
// substract 1 from it when erasing a character from the seed string.
|
|
if (seed_cursor_pos > BASE36_MAX_DIGITS)
|
|
seed_cursor_pos = BASE36_MAX_DIGITS;
|
|
|
|
if (key_hit(DESELECT_CARDS))
|
|
delete_seed_char();
|
|
|
|
else if (key_hit(SELECT_CARD))
|
|
{
|
|
// Get keyboard button index from selection
|
|
enum RunSetupKeyboardButtons key =
|
|
get_keyboard_index_from_sel(&choose_seed_selection_grid.selection);
|
|
|
|
switch (key)
|
|
{
|
|
case RUN_SETUP_KEYBOARD_RAND:
|
|
reroll_seed_str();
|
|
break;
|
|
|
|
// Erase last character
|
|
case RUN_SETUP_KEYBOARD_DEL:
|
|
delete_seed_char();
|
|
break;
|
|
|
|
default:
|
|
type_seed_char(key);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Get the Button corresponding to a Selection within `choose_seed_selection_grid`
|
|
*
|
|
* @param sel Selection in `choose_seed_selection_grid`
|
|
* @return pointer to the Button at coordinates `sel` in grid
|
|
*/
|
|
static inline Button* choose_seed_get_button_from_sel(const Selection* sel)
|
|
{
|
|
switch (sel->y)
|
|
{
|
|
case RUN_SETUP_SEED_ROW_KEY0:
|
|
case RUN_SETUP_SEED_ROW_KEY1:
|
|
case RUN_SETUP_SEED_ROW_KEY2:
|
|
case RUN_SETUP_SEED_ROW_KEY3:
|
|
return &keyboard_buttons[get_keyboard_index_from_sel(sel)];
|
|
|
|
case RUN_SETUP_SEED_ROW_DECK_PLAY:
|
|
return &choose_seed_bottom_buttons[sel->x];
|
|
|
|
case RUN_SETUP_SEED_ROW_BACK:
|
|
return &back_button;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief Handle button imputs in `choose_seed_selection_grid`
|
|
*
|
|
* @param selection_grid pointer to `choose_seed_selection_grid`
|
|
* @param selection Selected button in `choose_seed_selection_grid`
|
|
*/
|
|
static void choose_seed_row_on_key_transit(SelectionGrid* selection_grid, Selection* selection)
|
|
{
|
|
if (key_hit(SELECT_CARD) || key_hit(DESELECT_CARDS))
|
|
button_press(choose_seed_get_button_from_sel(selection));
|
|
}
|
|
|
|
/**
|
|
* @brief Handle directional imputs in `choose_seed_selection_grid`
|
|
*
|
|
* @param selection_grid pointer to `choose_seed_selection_grid`
|
|
* @param row_idx index of the row in selection_grid the function is being called for
|
|
* @param prev_selection previous Selection in `choose_seed_selection_grid`
|
|
* @param new_selection new selection in `choose_seed_selection_grid`
|
|
* @return bool
|
|
*/
|
|
static bool choose_seed_row_on_selection_changed(
|
|
SelectionGrid* selection_grid,
|
|
int row_idx,
|
|
const Selection* prev_selection,
|
|
const Selection* new_selection
|
|
)
|
|
{
|
|
// TODO: since some rows don't have the same number of buttons but their widths line up,
|
|
// need to find a way to shift selection.x to left or right to keep navigation consistent
|
|
bool proceed_selection = true;
|
|
|
|
if (row_idx == prev_selection->y)
|
|
button_set_highlight(choose_seed_get_button_from_sel(prev_selection), false);
|
|
|
|
if (row_idx == new_selection->y)
|
|
{
|
|
Selection shifted_selection = *new_selection;
|
|
|
|
// From row 3 to 4
|
|
if (prev_selection->y == RUN_SETUP_SEED_ROW_KEY2 &&
|
|
new_selection->y == RUN_SETUP_SEED_ROW_KEY3 && prev_selection->x >= 5 &&
|
|
prev_selection->x <= 8)
|
|
{
|
|
shifted_selection.x++;
|
|
selection_grid->selection = shifted_selection;
|
|
proceed_selection = false;
|
|
}
|
|
|
|
// From row 4 to 3
|
|
else if (prev_selection->y == RUN_SETUP_SEED_ROW_KEY3 &&
|
|
new_selection->y == RUN_SETUP_SEED_ROW_KEY2 && prev_selection->x >= 1 &&
|
|
prev_selection->x <= 3)
|
|
{
|
|
shifted_selection.x++;
|
|
selection_grid->selection = shifted_selection;
|
|
proceed_selection = false;
|
|
}
|
|
|
|
button_set_highlight(choose_seed_get_button_from_sel(&shifted_selection), true);
|
|
}
|
|
|
|
return proceed_selection;
|
|
}
|
|
|
|
/**
|
|
* @brief Deck Button Callback
|
|
*/
|
|
static void deck_on_pressed(void)
|
|
{
|
|
state_machine_change_state(&run_setup_sm, RUN_SETUP_SUBSTATE_CHOOSE_DECK);
|
|
choose_deck_selection_grid.selection = RUN_SETUP_CHOOSE_DECK_SEL_FROM_SEED;
|
|
button_set_highlight(&change_deck_button, false);
|
|
button_set_highlight(&choose_deck_bottom_buttons[RUN_SETUP_DECK_BB_SEED], true);
|
|
}
|
|
|
|
// RESUME GAME
|
|
|
|
/**
|
|
* @brief Initialize the "Resume Previous Run" substate
|
|
*/
|
|
static void resume_substate_init(void)
|
|
{
|
|
tab_set_highlight(RUN_SETUP_TAB_RESUME);
|
|
|
|
// Show Deck card sprite
|
|
obj_unhide(run_setup_deck->sprite_object->sprite->obj, 0);
|
|
}
|
|
|
|
// COMMON BUTTONS
|
|
|
|
/**
|
|
* @brief Toggle whether or not we will use the seed chosen by the user.
|
|
* Changes the looks of the Seed button to signal if it is clickable or not,
|
|
* and that of the checkbox next to it.
|
|
*
|
|
* @param enable whether the seed is enabled or not
|
|
*/
|
|
static inline void toggle_seed_enabled(bool enable)
|
|
{
|
|
use_seed = enable;
|
|
|
|
// Apply right main color to the Button
|
|
choose_deck_bottom_buttons[RUN_SETUP_DECK_BB_SEED].button_pal_idx =
|
|
use_seed ? BLUE_BTN_MAIN_COLOR_PAL_IDX : BLUE_DISABLED_BTN_MAIN_COLOR_PAL_IDX;
|
|
button_set_highlight(&choose_deck_bottom_buttons[RUN_SETUP_DECK_BB_SEED], false);
|
|
|
|
// Replace Seed button tiles with the disabled one's
|
|
BG_POINT button_tiles = use_seed ? RUN_SETUP_CHOOSE_DECK_SEED_BTN_3X3_SRC_POS
|
|
: RUN_SETUP_CHOOSE_DECK_SEED_BTN_DISABLED_3X3_SRC_POS;
|
|
main_bg_se_copy_expand_3x3_rect(RUN_SETUP_CHOOSE_DECK_SEED_BTN_DEST, button_tiles);
|
|
|
|
// Print Seed button text with the right color
|
|
tte_printf(
|
|
"#{P:%d,%d; cx:0x%X000}%s",
|
|
RUN_SETUP_SEED_DECK_TEXT_POS.x,
|
|
RUN_SETUP_SEED_DECK_TEXT_POS.y,
|
|
use_seed ? TTE_WHITE_PB : TTE_BLACK_PB,
|
|
" Seed" // Extra space before to clean potential "Deck" text
|
|
);
|
|
|
|
// Replace toggle button tiles with either checkmark of empty circle
|
|
Rect toggle_tiles = use_seed ? RUN_SETUP_CHOOSE_DECK_USE_SEED_BTN_ON_SRC
|
|
: RUN_SETUP_CHOOSE_DECK_USE_SEED_BTN_OFF_SRC;
|
|
main_bg_se_copy_rect(toggle_tiles, RUN_SETUP_CHOOSE_DECK_USE_SEED_BTN_DEST_POS);
|
|
}
|
|
|
|
/**
|
|
* @brief Seed checkbox Button Callback. Toggles custom seed usage.
|
|
*/
|
|
static void use_seed_on_pressed(void)
|
|
{
|
|
if (key_hit(SELECT_CARD))
|
|
toggle_seed_enabled(!use_seed);
|
|
}
|
|
|
|
/**
|
|
* @brief Seed Button Callback. Opens the seed input keyboard.
|
|
*/
|
|
static void seed_on_pressed(void)
|
|
{
|
|
if (key_hit(SELECT_CARD) && use_seed)
|
|
state_machine_change_state(&run_setup_sm, RUN_SETUP_SUBSTATE_CHOOSE_SEED);
|
|
}
|
|
|
|
/**
|
|
* @brief Play Button Callback. Applies the chosen seed if enabled and starts a new run.
|
|
*/
|
|
static void play_on_pressed(void)
|
|
{
|
|
// Apply provided Seed if enabled
|
|
if (use_seed)
|
|
rng_set_seed(base36_to_u32(seed_str));
|
|
else
|
|
rng_shuffle_seed();
|
|
|
|
game_change_state(GAME_STATE_GAME_START);
|
|
}
|
|
|
|
/**
|
|
* @brief Back Button Callback. Goes back to the Main Menu.
|
|
*/
|
|
static void back_on_pressed(void)
|
|
{
|
|
game_change_state(GAME_STATE_MAIN_MENU);
|
|
}
|
|
|
|
/**
|
|
* @brief Button Callback used for cycling through the Decks.
|
|
*/
|
|
static void change_deck_on_pressed(void)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* @brief Highlights the `tab_sel` Tab at the top of the screen,
|
|
* and removes highlight from all the others.
|
|
*
|
|
* @param tab_sel index of the selected tab
|
|
*/
|
|
static void tab_set_highlight(enum RunSetupTab tab_sel)
|
|
{
|
|
for (enum RunSetupTab tab = 0; tab < RUN_SETUP_TAB_MAX; tab++)
|
|
{
|
|
button_set_highlight(&tabs_buttons[tab], tab == tab_sel);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Handle user input in regards to the Tabs. Tabs are cycled through using the L/R buttons.
|
|
*/
|
|
static void run_setup_tabs_update(void)
|
|
{
|
|
static enum RunSetupTab current_tab = RUN_SETUP_TAB_RESUME;
|
|
|
|
// Either not pressed anything or there is no saved data, "Resume" tab is left grayed out and
|
|
// only one tab is available. Return early to not spend time on tab-changing logic
|
|
if (!key_hit(KEY_ANY) || !is_saved_game_valid)
|
|
return;
|
|
|
|
// Not all the way to the right and pressed R
|
|
if (key_hit(TAB_RIGHT) && current_tab < RUN_SETUP_TAB_MAX - 1)
|
|
{
|
|
button_set_highlight(&tabs_buttons[current_tab], false);
|
|
current_tab++;
|
|
}
|
|
// Not all the way to the left and pressed L
|
|
else if (key_hit(TAB_LEFT) && current_tab > 0)
|
|
{
|
|
button_set_highlight(&tabs_buttons[current_tab], false);
|
|
current_tab--;
|
|
}
|
|
|
|
button_set_highlight(&tabs_buttons[current_tab], true);
|
|
|
|
switch (current_tab)
|
|
{
|
|
case RUN_SETUP_TAB_NEW_RUN:
|
|
state_machine_change_state(&run_setup_sm, RUN_SETUP_SUBSTATE_CHOOSE_DECK);
|
|
break;
|
|
|
|
case RUN_SETUP_TAB_RESUME:
|
|
state_machine_change_state(&run_setup_sm, RUN_SETUP_SUBSTATE_RESUME);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Retuns the width of the Back button's row.
|
|
*
|
|
* @return 1, as there is only the Back button on that row.
|
|
*/
|
|
static int back_row_get_size()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static void back_row_on_key_transit(SelectionGrid* selection_grid, Selection* selection)
|
|
{
|
|
if (key_hit(SELECT_CARD))
|
|
button_press(&back_button);
|
|
}
|
|
|
|
#pragma endregion
|