[Feature] Implement seeded runs by adding a Seed selection menu (#482)

* 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>
This commit is contained in:
Geralt
2026-06-02 00:06:37 -07:00
committed by GitHub
co-authored by MathisMartin31
parent 21d8d912ab
commit 1df8402900
32 changed files with 2161 additions and 123 deletions
+1
View File
@@ -0,0 +1 @@
-gB8 -mR8 -mLs -mRtf -pn 160
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

+1
View File
@@ -0,0 +1 @@
-gB4 -Mw4 -Mh4 -m! -pn 16
Binary file not shown.

After

Width:  |  Height:  |  Size: 872 B

+4
View File
@@ -1,6 +1,7 @@
#ifndef CARD_H #ifndef CARD_H
#define CARD_H #define CARD_H
#include "deck_types.h"
#include "sprite.h" #include "sprite.h"
#include <maxmod.h> #include <maxmod.h>
@@ -58,6 +59,8 @@ typedef struct CardObject
bool selected; bool selected;
} CardObject; } CardObject;
void card_init(void);
// Card sprites accessibility functions // Card sprites accessibility functions
void set_cards_high_contrast(bool enable); void set_cards_high_contrast(bool enable);
void set_cards_more_readable(bool enable); void set_cards_more_readable(bool enable);
@@ -74,6 +77,7 @@ CardObject* card_object_new(Card* card);
void card_object_destroy(CardObject** card_object); void card_object_destroy(CardObject** card_object);
void card_object_update(CardObject* card_object); // Update the card object position and scale void card_object_update(CardObject* card_object); // Update the card object position and scale
void card_object_set_sprite(CardObject* card_object, int layer); void card_object_set_sprite(CardObject* card_object, int layer);
void card_object_set_sprite_face_down(CardObject* card_object, enum DeckType deck, int layer);
void card_object_shake(CardObject* card_object, mm_word sound_id); void card_object_shake(CardObject* card_object, mm_word sound_id);
void card_object_set_selected(CardObject* card_object, bool selected); void card_object_set_selected(CardObject* card_object, bool selected);
+45
View File
@@ -0,0 +1,45 @@
/**
* @file deck_types.h
*
* @brief Functions related to the different types of Decks.
*/
#ifndef DECK_TYPES_H
#define DECK_TYPES_H
#include "graphic_utils.h"
#define DECK_SPRITES_PB 1
#define DECK_NAME_LENGTH 14 // 13 chars + '\0'
#define DECK_DESC_WIDTH DECK_NAME_LENGTH
#define DECK_DESC_HEIGHT 5
enum DeckType
{
DECK_TYPE_RED,
DECK_TYPE_BLUE,
DECK_TYPE_YELLOW,
DECK_TYPE_GREEN,
DECK_TYPE_BLACK,
DECK_TYPE_PAINTED,
DECK_TYPE_MAX
};
/**
* @brief Prints the Deck name string to the screen at the given position.
*
* @param deck Decktype we want the name of.
* @param pos screen position in pixels to print the name at.
*/
void print_deck_name(enum DeckType deck, BG_POINT pos);
/**
* @brief Prints the Deck description string to the screen at the given position.
*
* @param deck DeckType we want the description of.
* @param pos screen position in pixels of the top-left corner of the rectangle
* we'll print the Deck description into
*/
void print_deck_description(enum DeckType deck, BG_POINT pos);
#endif // DECK_TYPES_H
+1
View File
@@ -3,6 +3,7 @@
DEF_STATE_INFO(GAME_STATE_SPLASH_SCREEN, splash_screen_on_init, splash_screen_on_update, splash_screen_on_exit ) DEF_STATE_INFO(GAME_STATE_SPLASH_SCREEN, splash_screen_on_init, splash_screen_on_update, splash_screen_on_exit )
DEF_STATE_INFO(GAME_STATE_MAIN_MENU, game_main_menu_on_init, game_main_menu_on_update, game_main_menu_on_exit ) DEF_STATE_INFO(GAME_STATE_MAIN_MENU, game_main_menu_on_init, game_main_menu_on_update, game_main_menu_on_exit )
DEF_STATE_INFO(GAME_STATE_OPTIONS_MENU, game_options_menu_on_init, game_options_menu_on_update, game_options_menu_on_exit) DEF_STATE_INFO(GAME_STATE_OPTIONS_MENU, game_options_menu_on_init, game_options_menu_on_update, game_options_menu_on_exit)
DEF_STATE_INFO(GAME_STATE_RUN_SETUP , game_run_setup_on_init, game_run_setup_on_update, game_run_setup_on_exit )
DEF_STATE_INFO(GAME_STATE_GAME_START, noop, game_start, noop ) DEF_STATE_INFO(GAME_STATE_GAME_START, noop, game_start, noop )
DEF_STATE_INFO(GAME_STATE_PLAYING, game_round_on_init, game_playing_on_update, noop ) DEF_STATE_INFO(GAME_STATE_PLAYING, game_round_on_init, game_playing_on_update, noop )
DEF_STATE_INFO(GAME_STATE_ROUND_END, game_round_end_on_init, game_round_end_on_update, game_round_end_on_exit ) DEF_STATE_INFO(GAME_STATE_ROUND_END, game_round_end_on_init, game_round_end_on_update, game_round_end_on_exit )
+2
View File
@@ -24,6 +24,8 @@
#define SORT_HAND KEY_R #define SORT_HAND KEY_R
#define PAUSE_GAME KEY_START // Not implemented #define PAUSE_GAME KEY_START // Not implemented
#define SELL_KEY KEY_L #define SELL_KEY KEY_L
#define TAB_LEFT KEY_L
#define TAB_RIGHT KEY_R
// Matching the position of the on-screen buttons // Matching the position of the on-screen buttons
#define PLAY_HAND_KEY KEY_L #define PLAY_HAND_KEY KEY_L
+1
View File
@@ -19,6 +19,7 @@ enum BackgroundId
BG_ROUND_END, BG_ROUND_END,
BG_SHOP, BG_SHOP,
BG_BLIND_SELECT, BG_BLIND_SELECT,
BG_RUN_SETUP,
BG_OPTIONS_MENU, BG_OPTIONS_MENU,
BG_MAIN_MENU BG_MAIN_MENU
}; };
+29
View File
@@ -0,0 +1,29 @@
/**
* @file run_setup.h
*
* @brief Setup Run menu state functions.
*/
#ifndef GAME_RUN_SETUP_H
#define GAME_RUN_SETUP_H
/**
* @brief Change to the Run Setup menu background
*/
void game_run_setup_change_background(void);
/**
* @brief Run Setup menu state initialization
*/
void game_run_setup_on_init(void);
/**
* @brief Run Setup menu state update
*/
void game_run_setup_on_update(void);
/**
* @brief Run Setup menu cleanup
*/
void game_run_setup_on_exit(void);
#endif // GAME_RUN_SETUP_H
+1
View File
@@ -47,6 +47,7 @@ typedef struct
s32 ante; s32 ante;
s32 money; s32 money;
s32 hand_size; s32 hand_size;
s32 deck;
// Blind variables // Blind variables
+61 -1
View File
@@ -63,6 +63,9 @@
/** @def TTE_BIT_ON_CLR_IDX */ /** @def TTE_BIT_ON_CLR_IDX */
#define TTE_BIT_ON_CLR_IDX TTE_BIT_UNPACK_OFFSET + 1 #define TTE_BIT_ON_CLR_IDX TTE_BIT_UNPACK_OFFSET + 1
/** @def TTE_BLACK_PB */
#define TTE_BLACK_PB 0 // 0x0
/** @def TTE_YELLOW_PB */ /** @def TTE_YELLOW_PB */
#define TTE_YELLOW_PB 12 // 0xC #define TTE_YELLOW_PB 12 // 0xC
@@ -149,6 +152,37 @@ enum OverflowDir
// When making this, missed that it already exists in tonc_math.h // When making this, missed that it already exists in tonc_math.h
typedef RECT Rect; typedef RECT Rect;
// clang-format off
/**
* @brief Structure to represent arbitrary-sized 9-patches.
*
* While expanding a 3x3 rect is simple enough and has its purpose, this is an attempt
* to generalize the idea to a struct representing a 9-patch like this, such that areas
* 1, 2, 3 and 4 have no null dimensions and don't overlap:
* ```
* margins.left margins.right │ Such as:
* ╭───┴───╮ ╭─┴─╮ │
* ╔═══════╤═════╤═══╗ ╮ │ 1) margins.left + margins.right <= width (patch_rect)
* ║ 1 │ 6 │ 2 ║ ├─ margins.top │
* ╟───────┼─────┼───╢ ╯ │ 2) margins.top + margins.bottom <= height(patch_rect)
* ║ 5 │ 0 │ 7 ║ │
* ║ │ │ ║ │ 3) margins values are inclusive, in number of tiles
* ╟───────┼─────┼───╢ ╮ │
* ║ │ │ ║ │ │
* ║ 4 │ 8 │ 3 ║ ├─ margins.bottom │
* ║ │ │ ║ │ │
* ╚═══════╧═════╧═══╝ ╯ │
* ╰────────┬────────╯ │
* patch_rect │
* ```
*/
// clang-format on
typedef struct NinePatchRect
{
Rect patch_rect;
Rect margins;
} NinePatchRect;
/** /**
* @brief Get the width of a rectangle * @brief Get the width of a rectangle
* *
@@ -218,7 +252,33 @@ void main_bg_se_copy_rect_1_tile_vert(Rect se_rect, enum ScreenVertDir direction
void main_bg_se_copy_rect(Rect se_rect, BG_POINT dest_pos); void main_bg_se_copy_rect(Rect se_rect, BG_POINT dest_pos);
/** /**
* @brief Copies a 3x3 rect and expands it to fit a passed rect. * @brief Copies a tile in the main background at se_tile_src to fill se_rect_dest.
*
* @param se_rect_dest dimensions are in number of tiles.
* @param se_tile_src x and y are the coordinates in number of tiles.
*/
void main_bg_se_copy_expand_tile(Rect se_rect_dest, BG_POINT se_tile_src);
/**
* @brief Copies a 9-patch and expands it to fit a passed rect.
*
* Performs the following operation:
*
* 1. The corners are copied
* 2. The sides are stretched
* 3. The center is stretched to fill `se_rect_dest` minus the patch's margins.
*
* @param se_rect_dest Destination for the 9-patch copy. Needs to be at least large enough to fit
* the corners and sides of the patch, (`margins.left + margins.right` by
* `margins.top + margins.bottom`) in which case only the corners are copied.
* @param src_9_ptch Pointer to the properties of the 9-patch we want to stretch.
*
* @sa NinePatchRect
*/
void main_bg_se_copy_expand_9_patch(Rect se_dest_rect, const NinePatchRect* src_9_ptch);
/**
* @brief Copies a 3x3 rect and expands it to fit a passed rect. Special case of the 9-patch.
* *
* Performs the following operation: * Performs the following operation:
* *
+1 -1
View File
@@ -18,7 +18,7 @@
#define JOKER_STARTING_LAYER 26 #define JOKER_STARTING_LAYER 26
// Tile ID for the starting index in the tile memory // Tile ID for the starting index in the tile memory
#define JOKER_TID (JOKER_STARTING_LAYER * JOKER_SPRITE_OFFSET) #define JOKER_TID (JOKER_STARTING_LAYER * JOKER_SPRITE_OFFSET)
#define JOKER_BASE_PB 3 // The starting palette index for the jokers #define JOKER_BASE_PB 4 // The starting palette index for the jokers, after the boss blind tokens
#define JOKER_LAST_PB (NUM_PALETTES - 1) #define JOKER_LAST_PB (NUM_PALETTES - 1)
// Currently allocating the rest of the palettes for the jokers. // Currently allocating the rest of the palettes for the jokers.
// This number needs to be decreased once we need to allocated palettes for other sprites // This number needs to be decreased once we need to allocated palettes for other sprites
+1 -6
View File
@@ -12,11 +12,6 @@
#include <tonc.h> #include <tonc.h>
/**
* @brief Hex value of "ZZZZZZ" in base 36
*/
#define MAX_SEED 0x81BF0FFF
/** /**
* @brief Information to track and restore RNG state * @brief Information to track and restore RNG state
*/ */
@@ -41,7 +36,7 @@ void rng_update(void);
/** /**
* @brief Set the rng seed to the chosen value, and reset the step counter to 0. * @brief Set the rng seed to the chosen value, and reset the step counter to 0.
* The seed will be capped at `MAX_SEED` for compatibility with the Seed Input screen * The seed will be capped at `MAX_BASE36` for compatibility with the Seed Input screen
* used to choose a seed for seeded runs. * used to choose a seed for seeded runs.
* *
* @param seed the new RNG seed * @param seed the new RNG seed
+7
View File
@@ -31,6 +31,13 @@
#include <tonc.h> #include <tonc.h>
/**
* @brief Checks whether the SaveGame section is present and valid.
*
* @returns true if it is, false if not.
*/
bool is_game_data_valid(void);
/** /**
* @brief Save current run data to SRAM. * @brief Save current run data to SRAM.
*/ */
+1 -2
View File
@@ -107,14 +107,13 @@ void noop(void);
// clang-format off // clang-format off
#define STATE_INFO_UPDATE_FN_ONLY(fn) {.on_init = noop, .on_update = fn, .on_exit = noop} #define STATE_INFO_UPDATE_FN_ONLY(fn) {.on_init = noop, .on_update = fn, .on_exit = noop}
#define STATE_INFO_INIT_UPDATE_FN(init_fn, update_fn) {.on_init = init_fn, .on_update = update_fn, .on_exit = noop}
#define STATE_MACHINE_DEFINE(infos, num) \ #define STATE_MACHINE_DEFINE(infos, num) \
{ \ { \
.state_infos = &infos[0], \ .state_infos = &infos[0], \
.num_infos = num, \ .num_infos = num, \
.registered = false, \ .registered = false, \
}; };
// clang-format on // clang-format on
#endif // STATE_MACHINE_H #endif // STATE_MACHINE_H
+49
View File
@@ -17,6 +17,12 @@
#define UNDEFINED -1 #define UNDEFINED -1
/**
* @def MAX_BASE36
* @brief Hex value of "ZZZZZZ" in base 36
*/
#define MAX_BASE36 0x81BF0FFF
/** /**
* @def SIGN * @def SIGN
* @brief Get the sign (signum) of an integer * @brief Get the sign (signum) of an integer
@@ -36,6 +42,7 @@
#define INT_MAX_DIGITS 11 // strlen(str(INT_MAX)) = strlen("-2147483647") #define INT_MAX_DIGITS 11 // strlen(str(INT_MAX)) = strlen("-2147483647")
#define UINT_MAX_DIGITS 10 // strlen(str(UINT32_MAX)) = strlen("4294967295") #define UINT_MAX_DIGITS 10 // strlen(str(UINT32_MAX)) = strlen("4294967295")
#define UINT8_MAX_DIGITS 3 // strlen(str(UINT8_MAX)) = strlen("255") #define UINT8_MAX_DIGITS 3 // strlen(str(UINT8_MAX)) = strlen("255")
#define BASE36_MAX_DIGITS 6 // strlen("ZZZZZZ")
#define ONE_K 1000 #define ONE_K 1000
#define ONE_M 1000000 #define ONE_M 1000000
@@ -150,4 +157,46 @@ static inline int u32_get_digits(uint32_t n)
return 10; return 10;
} }
/**
* @brief Convert a base-36 string representation to a 32-bit unsigned integer.
* Since we are dealing with base-36 instead of decimal, the 32-bit decimal
* value of a base-36 string representation `b36` is equal to:
*
* \f( b36[0] * 36^0 + b36[1] * 36^1 + b36[2] * 36^2 ... \f)
*
* @param b36_str input char[] to convert to decimal, must be of size `BASE36_MAX_DIGITS+1`
*
* @returns the 32-bit unsigned value of `b36_str`
*/
uint32_t base36_to_u32(const char b36_str[]);
/**
* @brief Convert a 32-bit unsigned integer to its base-36 string representation.
* This will perform 6 divisions, so it will be significantly more expensive
* than its `base36_to_u32` counterpart.
*
* We will iterate over all digits from `BASE36_MAX_DIGITS-1` to 0 and determine
* their values in base-36, to then construct the string representation `b36_str`
* in base-36 or the integer `n`
*
* Initially set to `n`, the variable `acc` will contain any given stage `i`:
* ```
* b32[i] * 36^i + b32[i-1] * 36^(i-1) + ... + b32[0]
* ```
*
* And we can thus extract the two following values:
* ```
* b32[i] = acc / 36^i
* acc = acc mod 36^i = b32[i-1] * 36^(i-1) + ... + b32[0]
* ```
*
* So that acc can now be used for the following step, until `i` hits 0
*
* @param n integer value to convert to a base-36 representation
* @param b36_str output char[], representation of `n` in base-36
*
* @sa base36_to_u32
*/
void u32_to_base36(uint32_t n, char b36_str[]);
#endif // UTIL_H #endif // UTIL_H
+2 -2
View File
@@ -12,8 +12,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <tonc.h> #include <tonc.h>
#define NORMAL_BLIND_PB 1 #define NORMAL_BLIND_PB 2
#define BOSS_BLIND_PB 2 #define BOSS_BLIND_PB 3
#define BLIND_BASE_LAYER (MAX_HAND_SIZE + MAX_SELECTION_SIZE) #define BLIND_BASE_LAYER (MAX_HAND_SIZE + MAX_SELECTION_SIZE)
+29 -3
View File
@@ -12,6 +12,7 @@
// Card Sprites and Palettes // Card Sprites and Palettes
#include "deck_big_gfx.h" #include "deck_big_gfx.h"
#include "deck_gfx.h" #include "deck_gfx.h"
#include "decks_face_down_gfx.h"
#include "high_contrast_deck_pal_gfx.h" #include "high_contrast_deck_pal_gfx.h"
// Card sprites lookup table. First index is the suit, second index is the rank. The value is the // Card sprites lookup table. First index is the suit, second index is the rank. The value is the
@@ -22,20 +23,27 @@ const static u16 _card_sprite_lut[NUM_SUITS][NUM_RANKS] = {
{416, 432, 448, 464, 480, 496, 512, 528, 544, 560, 576, 592, 608}, {416, 432, 448, 464, 480, 496, 512, 528, 544, 560, 576, 592, 608},
{624, 640, 656, 672, 688, 704, 720, 736, 752, 768, 784, 800, 816} {624, 640, 656, 672, 688, 704, 720, 736, 752, 768, 784, 800, 816}
}; };
// Deck sprites lookup table. Index is the deck Id. The value is the tile index.
const static u16 _deck_sprite_lut[DECK_TYPE_MAX] = {0, 16, 32, 48, 64, 80};
bool high_contrast = DEFAULT_HIGH_CONTRAST; bool high_contrast = DEFAULT_HIGH_CONTRAST;
bool more_readable = DEFAULT_MORE_READABLE; bool more_readable = DEFAULT_MORE_READABLE;
void card_init()
{
GRIT_CPY(&pal_obj_mem[DECK_SPRITES_PB * PAL_ROW_LEN], decks_face_down_gfxPal);
}
void set_cards_high_contrast(bool enable) void set_cards_high_contrast(bool enable)
{ {
high_contrast = enable; high_contrast = enable;
if (high_contrast) if (high_contrast)
{ {
GRIT_CPY(&pal_obj_mem[CARD_PB], high_contrast_deck_pal_gfxPal); GRIT_CPY(&pal_obj_mem[CARD_PB * PAL_ROW_LEN], high_contrast_deck_pal_gfxPal);
} }
else else
{ {
GRIT_CPY(&pal_obj_mem[CARD_PB], deck_gfxPal); GRIT_CPY(&pal_obj_mem[CARD_PB * PAL_ROW_LEN], deck_gfxPal);
} }
} }
@@ -130,7 +138,25 @@ void card_object_set_sprite(CardObject* card_object, int layer)
ATTR0_SQUARE | ATTR0_4BPP | ATTR0_AFF, ATTR0_SQUARE | ATTR0_4BPP | ATTR0_AFF,
ATTR1_SIZE_32, ATTR1_SIZE_32,
tile_index, tile_index,
0, CARD_PB,
layer + CARD_STARTING_LAYER
);
sprite_object_set_sprite(card_object->sprite_object, sprite);
}
void card_object_set_sprite_face_down(CardObject* card_object, enum DeckType deck, int layer)
{
int tile_index = CARD_TID + (layer * CARD_SPRITE_OFFSET);
memcpy32(
&tile_mem[TILE_MEM_OBJ_CHARBLOCK0_IDX][tile_index],
&decks_face_down_gfxTiles[_deck_sprite_lut[deck] * TILE_SIZE],
TILE_SIZE * CARD_SPRITE_OFFSET
);
Sprite* sprite = sprite_new(
ATTR0_SQUARE | ATTR0_4BPP | ATTR0_AFF,
ATTR1_SIZE_32,
tile_index,
DECK_SPRITES_PB,
layer + CARD_STARTING_LAYER layer + CARD_STARTING_LAYER
); );
sprite_object_set_sprite(card_object->sprite_object, sprite); sprite_object_set_sprite(card_object->sprite_object, sprite);
+300
View File
@@ -0,0 +1,300 @@
/**
* @file deck_types.c
*
* @brief Implementation of functions related to the different types of Decks.
*/
#include "deck_types.h"
#include "game.h"
#include <tonc.h>
#define TTE_COLOR_TEXT_FORMAT "#{cx:0x%X000}%s"
/**
* @brief List of DeckType names, all formatted to be centered in a string of 13 characters.
*/
static const char deck_names[DECK_TYPE_MAX][DECK_NAME_LENGTH] = {
" Red Deck ",
" Blue Deck ",
" Yellow Deck ",
" Green Deck ",
" Black Deck ",
"Painted Deck "
};
static void print_desc_red_deck(BG_POINT pos);
static void print_desc_blue_deck(BG_POINT pos);
static void print_desc_yellow_deck(BG_POINT pos);
static void print_desc_green_deck(BG_POINT pos);
static void print_desc_black_deck(BG_POINT pos);
static void print_desc_painted_deck(BG_POINT pos);
typedef void (*PrintDescCallback)(BG_POINT);
static const PrintDescCallback deck_description_functions[DECK_TYPE_MAX] = {
print_desc_red_deck,
print_desc_blue_deck,
print_desc_yellow_deck,
print_desc_green_deck,
print_desc_black_deck,
print_desc_painted_deck
};
void print_deck_name(enum DeckType deck, BG_POINT pos)
{
tte_printf("#{P:%d,%d; cx:0x%X000}%s", pos.x, pos.y, TTE_WHITE_PB, deck_names[deck]);
}
void print_deck_description(enum DeckType deck, BG_POINT pos)
{
if (deck_description_functions[deck] == NULL)
return;
deck_description_functions[deck](pos);
}
/**
* @brief Print an empty string of 13 characters long at given coordinates.
* Reduces boilerplate in deck descriptions functions.
*
* @param pos_x
* @param pos_y
*/
static inline void print_empty_desc_line(int pos_x, int pos_y)
{
tte_printf("#{P:%d,%d}" TTE_COLOR_TEXT_FORMAT, pos_x, pos_y, TTE_BLACK_PB, " ");
}
/**
* @brief Print the decription of the Red Deck
* ```
* " "
* " +1 discard "
* " every Round "
* " "
* " "
* ```
* @param pos position of the rectagle to print the description to
*/
static void print_desc_red_deck(BG_POINT pos)
{
print_empty_desc_line(pos.x, pos.y);
tte_printf(
"#{P:%d,%d}" TTE_COLOR_TEXT_FORMAT TTE_COLOR_TEXT_FORMAT,
pos.x,
pos.y + TILE_SIZE,
TTE_RED_PB,
" +1 ",
TTE_BLACK_PB,
"discard "
);
tte_printf(
"#{P:%d,%d}" TTE_COLOR_TEXT_FORMAT,
pos.x,
pos.y + 2 * TILE_SIZE,
TTE_BLACK_PB,
" every Round "
);
print_empty_desc_line(pos.x, pos.y + 3 * TILE_SIZE);
print_empty_desc_line(pos.x, pos.y + 4 * TILE_SIZE);
}
/**
* @brief Print the decription of the Blue Deck
* ```
* " "
* " +1 hand "
* " every Round "
* " "
* " "
* ```
* @param pos position of the rectagle to print the description to
*/
static void print_desc_blue_deck(BG_POINT pos)
{
print_empty_desc_line(pos.x, pos.y);
tte_printf(
"#{P:%d,%d}" TTE_COLOR_TEXT_FORMAT TTE_COLOR_TEXT_FORMAT,
pos.x,
pos.y + TILE_SIZE,
TTE_BLUE_PB,
" +1 ",
TTE_BLACK_PB,
"hand "
);
tte_printf(
"#{P:%d,%d}" TTE_COLOR_TEXT_FORMAT,
pos.x,
pos.y + 2 * TILE_SIZE,
TTE_BLACK_PB,
" every Round "
);
print_empty_desc_line(pos.x, pos.y + 3 * TILE_SIZE);
print_empty_desc_line(pos.x, pos.y + 4 * TILE_SIZE);
}
/**
* @brief Print the decription of the Yellow Deck
* ```
* " "
* " Start with "
* "an extra $10 "
* " "
* " "
* ```
* @param pos position of the rectagle to print the description to
*/
static void print_desc_yellow_deck(BG_POINT pos)
{
print_empty_desc_line(pos.x, pos.y);
tte_printf(
"#{P:%d,%d}" TTE_COLOR_TEXT_FORMAT,
pos.x,
pos.y + TILE_SIZE,
TTE_BLACK_PB,
" Start with "
);
tte_printf(
"#{P:%d,%d}" TTE_COLOR_TEXT_FORMAT TTE_COLOR_TEXT_FORMAT,
pos.x,
pos.y + 2 * TILE_SIZE,
TTE_BLACK_PB,
"an extra",
TTE_YELLOW_PB,
" $10 "
);
print_empty_desc_line(pos.x, pos.y + 3 * TILE_SIZE);
print_empty_desc_line(pos.x, pos.y + 4 * TILE_SIZE);
}
/**
* @brief Print the decription of the Green Deck
* ```
* "End of Round:"
* " "
* "-$2 per Hand "
* "-$2 per Disc."
* "-No Interest "
* ```
* @param pos position of the rectagle to print the description to
*/
static void print_desc_green_deck(BG_POINT pos)
{
tte_printf("#{P:%d,%d; cx:0x%X000}%s", pos.x, pos.y, TTE_BLACK_PB, "End of Round:");
print_empty_desc_line(pos.x, pos.y + TILE_SIZE);
tte_printf(
"#{P:%d,%d}" TTE_COLOR_TEXT_FORMAT TTE_COLOR_TEXT_FORMAT TTE_COLOR_TEXT_FORMAT
TTE_COLOR_TEXT_FORMAT,
pos.x,
pos.y + 2 * TILE_SIZE,
TTE_BLACK_PB,
"-",
TTE_YELLOW_PB,
"$2 ",
TTE_BLACK_PB,
"per ",
TTE_RED_PB,
"Hand "
);
tte_printf(
"#{P:%d,%d}" TTE_COLOR_TEXT_FORMAT TTE_COLOR_TEXT_FORMAT TTE_COLOR_TEXT_FORMAT
TTE_COLOR_TEXT_FORMAT,
pos.x,
pos.y + 3 * TILE_SIZE,
TTE_BLACK_PB,
"-",
TTE_YELLOW_PB,
"$1 ",
TTE_BLACK_PB,
"per ",
TTE_BLUE_PB,
"Disc."
);
tte_printf(
"#{P:%d,%d}" TTE_COLOR_TEXT_FORMAT TTE_COLOR_TEXT_FORMAT,
pos.x,
pos.y + 4 * TILE_SIZE,
TTE_BLACK_PB,
"-No ",
TTE_YELLOW_PB,
"Interest "
);
}
/**
* @brief Print the decription of the Black Deck
* ```
* "+1 Joker slot"
* " "
* " -1 hand "
* " every Round "
* " "
* ```
* @param pos position of the rectagle to print the description to
*/
static void print_desc_black_deck(BG_POINT pos)
{
tte_printf(
"#{P:%d,%d}" TTE_COLOR_TEXT_FORMAT TTE_COLOR_TEXT_FORMAT,
pos.x,
pos.y,
TTE_YELLOW_PB,
"+1 ",
TTE_BLACK_PB,
"Joker slot"
);
print_empty_desc_line(pos.x, pos.y + TILE_SIZE);
tte_printf(
"#{P:%d,%d}" TTE_COLOR_TEXT_FORMAT TTE_COLOR_TEXT_FORMAT,
pos.x,
pos.y + 2 * TILE_SIZE,
TTE_BLUE_PB,
" -1 ",
TTE_BLACK_PB,
"hand "
);
tte_printf(
"#{P:%d,%d}" TTE_COLOR_TEXT_FORMAT,
pos.x,
pos.y + 3 * TILE_SIZE,
TTE_BLACK_PB,
" every Round "
);
print_empty_desc_line(pos.x, pos.y + 4 * TILE_SIZE);
}
/**
* @brief Print the decription of the Painted Deck
* ```
* " "
* "+2 Hand size "
* "-1 Joker slot"
* " "
* " "
* ```
* @param pos position of the rectagle to print the description to
*/
static void print_desc_painted_deck(BG_POINT pos)
{
print_empty_desc_line(pos.x, pos.y);
tte_printf(
"#{P:%d,%d}" TTE_COLOR_TEXT_FORMAT TTE_COLOR_TEXT_FORMAT,
pos.x,
pos.y + TILE_SIZE,
TTE_YELLOW_PB,
"+2 ",
TTE_BLACK_PB,
"Hand size "
);
tte_printf(
"#{P:%d,%d}" TTE_COLOR_TEXT_FORMAT TTE_COLOR_TEXT_FORMAT,
pos.x,
pos.y + 2 * TILE_SIZE,
TTE_RED_PB,
"-1 ",
TTE_BLACK_PB,
"Joker slot"
);
print_empty_desc_line(pos.x, pos.y + 3 * TILE_SIZE);
print_empty_desc_line(pos.x, pos.y + 4 * TILE_SIZE);
}
+3 -4
View File
@@ -14,6 +14,7 @@
#include "game/main_menu.h" #include "game/main_menu.h"
#include "game/options_menu.h" #include "game/options_menu.h"
#include "game/round_end.h" #include "game/round_end.h"
#include "game/run_setup.h"
#include "game/shop.h" #include "game/shop.h"
#include "game_variables.h" #include "game_variables.h"
#include "graphic_utils.h" #include "graphic_utils.h"
@@ -266,8 +267,8 @@ static enum PlayState play_state = PLAY_STARTING;
GameVariables g_game_vars = { GameVariables g_game_vars = {
.timer = 0, .rng_info = {0, 0}, .timer = 0, .rng_info = {0, 0},
.round = 0, .ante = 0, .money = 0, .round = 0, .ante = 0, .money = 0, .hand_size = DEFAULT_HAND_SIZE,
.hand_size = DEFAULT_HAND_SIZE, .deck = DECK_TYPE_RED,
.current_blind = BLIND_TYPE_SMALL, .current_blind = BLIND_TYPE_SMALL,
.next_boss_blind = BLIND_TYPE_BIG, .next_boss_blind = BLIND_TYPE_BIG,
@@ -2547,8 +2548,6 @@ static void game_playing_on_update(void)
void game_start(void) void game_start(void)
{ {
rng_shuffle_seed();
affine_background_change_background(AFFINE_BG_GAME); affine_background_change_background(AFFINE_BG_GAME);
g_game_vars.hands = MAX_HANDS; g_game_vars.hands = MAX_HANDS;
+2
View File
@@ -5,6 +5,7 @@
#include "game/main_menu.h" #include "game/main_menu.h"
#include "game/options_menu.h" #include "game/options_menu.h"
#include "game/round_end.h" #include "game/round_end.h"
#include "game/run_setup.h"
#include "game/shop.h" #include "game/shop.h"
typedef void (*BackgroundRenderCallback)(void); typedef void (*BackgroundRenderCallback)(void);
@@ -19,6 +20,7 @@ static const BackgroundRenderCallback bgCallbacks[] = {
[BG_ROUND_END] = game_round_end_change_background, [BG_ROUND_END] = game_round_end_change_background,
[BG_SHOP] = game_shop_change_background, [BG_SHOP] = game_shop_change_background,
[BG_BLIND_SELECT] = game_blind_select_change_background, [BG_BLIND_SELECT] = game_blind_select_change_background,
[BG_RUN_SETUP] = game_run_setup_change_background,
[BG_OPTIONS_MENU] = game_options_menu_change_background, [BG_OPTIONS_MENU] = game_options_menu_change_background,
[BG_MAIN_MENU] = game_main_menu_change_background, [BG_MAIN_MENU] = game_main_menu_change_background,
}; };
+2 -2
View File
@@ -102,7 +102,7 @@ void game_main_menu_change_background(void)
void game_main_menu_on_init(void) void game_main_menu_on_init(void)
{ {
affine_background_change_background(AFFINE_BG_MAIN_MENU); affine_background_change_background(AFFINE_BG_MAIN_MENU);
change_background(BG_MAIN_MENU, false); change_background(BG_MAIN_MENU, true);
main_menu_ace = card_object_new(card_new(SPADES, ACE)); main_menu_ace = card_object_new(card_new(SPADES, ACE));
card_object_set_sprite(main_menu_ace, 0); card_object_set_sprite(main_menu_ace, 0);
main_menu_ace->sprite_object->sprite->obj->attr0 |= ATTR0_AFF_DBL; main_menu_ace->sprite_object->sprite->obj->attr0 |= ATTR0_AFF_DBL;
@@ -208,7 +208,7 @@ static void main_menu_on_key_transit(SelectionGrid* selection_grid, Selection* s
static void play_on_pressed(void) static void play_on_pressed(void)
{ {
game_change_state(GAME_STATE_GAME_START); game_change_state(GAME_STATE_RUN_SETUP);
} }
static void options_on_pressed(void) static void options_on_pressed(void)
-1
View File
@@ -359,7 +359,6 @@ static void update_volume_slider_graphics(enum OptionButtonRows sel_row)
void game_options_menu_change_background(void) void game_options_menu_change_background(void)
{ {
tte_erase_screen(); tte_erase_screen();
CBB_CLEAR(MAIN_BG_CBB);
GRIT_CPY(pal_bg_mem, background_options_menu_gfxPal); GRIT_CPY(pal_bg_mem, background_options_menu_gfxPal);
GRIT_CPY(&tile_mem[MAIN_BG_CBB], background_options_menu_gfxTiles); GRIT_CPY(&tile_mem[MAIN_BG_CBB], background_options_menu_gfxTiles);
File diff suppressed because it is too large Load Diff
+182 -94
View File
@@ -69,9 +69,7 @@ static void bg_se_copy_or_move_rect_1_tile_vert(
) )
{ {
if (se_rect.left > se_rect.right || (direction != SCREEN_UP && direction != SCREEN_DOWN)) if (se_rect.left > se_rect.right || (direction != SCREEN_UP && direction != SCREEN_DOWN))
{
return; return;
}
// Clip to avoid read/write overflow of the screenblock // Clip to avoid read/write overflow of the screenblock
clip_se_rect_within_step_of_full_screen_vert(&se_rect, direction); clip_se_rect_within_step_of_full_screen_vert(&se_rect, direction);
@@ -166,51 +164,27 @@ static inline void main_bg_se_fill_rect_with_se(SE se, Rect se_rect)
} }
} }
// Helper: Copy the corners of a 3x3 tile block void main_bg_se_copy_expand_tile(Rect se_rect_dest, BG_POINT se_tile_src)
static inline void main_bg_se_expand_3x3_copy_corners(
const Rect* se_dest_rect,
const BG_POINT* src_top_left_pnt,
int dest_rect_width,
int dest_rect_height
)
{ {
SE top_left_se = se_mat[MAIN_BG_SBB][src_top_left_pnt->y][src_top_left_pnt->x]; main_bg_se_fill_rect_with_se(se_mat[MAIN_BG_SBB][se_tile_src.y][se_tile_src.x], se_rect_dest);
se_mat[MAIN_BG_SBB][se_dest_rect->top][se_dest_rect->left] = top_left_se;
SE top_right_se = se_mat[MAIN_BG_SBB][src_top_left_pnt->y][src_top_left_pnt->x + 2];
se_mat[MAIN_BG_SBB][se_dest_rect->top][se_dest_rect->left + dest_rect_width - 1] = top_right_se;
SE bottom_left_se = se_mat[MAIN_BG_SBB][src_top_left_pnt->y + 2][src_top_left_pnt->x];
se_mat[MAIN_BG_SBB][se_dest_rect->top + dest_rect_height - 1][se_dest_rect->left] =
bottom_left_se;
SE bottom_right_se = se_mat[MAIN_BG_SBB][src_top_left_pnt->y + 2][src_top_left_pnt->x + 2];
se_mat[MAIN_BG_SBB][se_dest_rect->top + dest_rect_height - 1]
[se_dest_rect->left + dest_rect_width - 1] = bottom_right_se;
} }
// Helper: Copy the top and bottom sides of a 3x3 tile block void main_bg_se_copy_expand_3x3_rect(Rect se_dest_rect, BG_POINT src_top_left_pnt)
static inline void main_bg_se_expand_3x3_copy_top_bottom(
const Rect* se_dest_rect,
const BG_POINT* src_top_left_pnt,
int dest_rect_width
)
{ {
if (dest_rect_width > 2) // New implementation: uses a 9-patch to factorize as much code as we can
// clang-format off
NinePatchRect src_9_ptch = {
.patch_rect =
{ {
SE top_middle_se = se_mat[MAIN_BG_SBB][src_top_left_pnt->y][src_top_left_pnt->x + 1]; src_top_left_pnt.x,
SE bottom_middle_se = se_mat[MAIN_BG_SBB][src_top_left_pnt->y + 2][src_top_left_pnt->x + 1]; src_top_left_pnt.y,
memset16( src_top_left_pnt.x + 2,
&se_mat[MAIN_BG_SBB][se_dest_rect->top][se_dest_rect->left + 1], src_top_left_pnt.y + 2
top_middle_se, },
dest_rect_width - 2 .margins = {1, 1, 1, 1}
); };
memset16( // clang-format on
&se_mat[MAIN_BG_SBB][se_dest_rect->bottom][se_dest_rect->left + 1], main_bg_se_copy_expand_9_patch(se_dest_rect, &src_9_ptch);
bottom_middle_se,
dest_rect_width - 2
);
}
} }
// Helper: Copy the left and right sides of a 3x3 tile block // Helper: Copy the left and right sides of a 3x3 tile block
@@ -232,62 +206,12 @@ static inline void main_bg_se_3w_copy_expand_left_right_sides(
} }
} }
void main_bg_se_copy_expand_3x3_rect(Rect se_dest_rect, BG_POINT src_top_left_pnt)
{
clip_se_rect_to_screenblock(&se_dest_rect);
int dest_rect_width = rect_width(&se_dest_rect);
int dest_rect_height = rect_height(&se_dest_rect);
// Verify the dest rect is at least 2x2
if (dest_rect_width < 2 || dest_rect_height < 2)
{
return;
}
// Copy the corners
main_bg_se_expand_3x3_copy_corners(
&se_dest_rect,
&src_top_left_pnt,
dest_rect_width,
dest_rect_height
);
// Copy top and bottom sides
main_bg_se_expand_3x3_copy_top_bottom(&se_dest_rect, &src_top_left_pnt, dest_rect_width);
BG_POINT src_middle_left_pnt = {src_top_left_pnt.x, src_top_left_pnt.y + 1};
// Avoid the corners when copying the sides
Rect dest_sides_rect = se_dest_rect;
dest_sides_rect.top += 1;
dest_sides_rect.bottom -= 1;
// Copy left and right sides
main_bg_se_3w_copy_expand_left_right_sides(&dest_sides_rect, &src_middle_left_pnt);
// Fill the center if needed
if (dest_rect_width > 2 && dest_rect_height > 2)
{
SE middle_fill_se = se_mat[MAIN_BG_SBB][src_top_left_pnt.y + 1][src_top_left_pnt.x + 1];
Rect dest_inner_fill_rect = {
se_dest_rect.left + 1,
se_dest_rect.top + 1,
se_dest_rect.right - 1,
se_dest_rect.bottom - 1
};
main_bg_se_fill_rect_with_se(middle_fill_se, dest_inner_fill_rect);
}
}
void main_bg_se_copy_expand_3w_row(Rect se_dest_rect, BG_POINT src_row_left_pnt) void main_bg_se_copy_expand_3w_row(Rect se_dest_rect, BG_POINT src_row_left_pnt)
{ {
clip_se_rect_to_screenblock(&se_dest_rect); clip_se_rect_to_screenblock(&se_dest_rect);
int dest_rect_width = rect_width(&se_dest_rect); int dest_rect_width = rect_width(&se_dest_rect);
if (dest_rect_width < 2) if (dest_rect_width < 2)
{
return; return;
}
// Copy left and right sides // Copy left and right sides
main_bg_se_3w_copy_expand_left_right_sides(&se_dest_rect, &src_row_left_pnt); main_bg_se_3w_copy_expand_left_right_sides(&se_dest_rect, &src_row_left_pnt);
@@ -304,6 +228,172 @@ void main_bg_se_copy_expand_3w_row(Rect se_dest_rect, BG_POINT src_row_left_pnt)
} }
} }
// Helper: Copy the four corners of a 9-patch
static inline void main_bg_se_expand_9_patch_copy_corners(
Rect se_dest_rect,
const NinePatchRect* src_9_ptch
)
{
BG_POINT top_left_dest_pos = {se_dest_rect.left, se_dest_rect.top};
Rect top_left_src = {
src_9_ptch->patch_rect.left,
src_9_ptch->patch_rect.top,
src_9_ptch->patch_rect.left + src_9_ptch->margins.left - 1,
src_9_ptch->patch_rect.top + src_9_ptch->margins.top - 1
};
BG_POINT top_right_dest_pos = {
se_dest_rect.right - src_9_ptch->margins.right + 1,
se_dest_rect.top
};
Rect top_right_src = {
src_9_ptch->patch_rect.right - src_9_ptch->margins.right + 1,
src_9_ptch->patch_rect.top,
src_9_ptch->patch_rect.right,
src_9_ptch->patch_rect.top + src_9_ptch->margins.top - 1
};
BG_POINT bottom_right_dest_pos = {
se_dest_rect.right - src_9_ptch->margins.right + 1,
se_dest_rect.bottom - src_9_ptch->margins.bottom + 1
};
Rect bottom_right_src = {
src_9_ptch->patch_rect.right - src_9_ptch->margins.right + 1,
src_9_ptch->patch_rect.bottom - src_9_ptch->margins.bottom + 1,
src_9_ptch->patch_rect.right,
src_9_ptch->patch_rect.bottom
};
BG_POINT bottom_left_dest_pos = {
se_dest_rect.left,
se_dest_rect.bottom - src_9_ptch->margins.bottom + 1
};
Rect bottom_left_src = {
src_9_ptch->patch_rect.left,
src_9_ptch->patch_rect.bottom - src_9_ptch->margins.bottom + 1,
src_9_ptch->patch_rect.left + src_9_ptch->margins.left - 1,
src_9_ptch->patch_rect.bottom
};
main_bg_se_copy_rect(top_left_src, top_left_dest_pos);
main_bg_se_copy_rect(top_right_src, top_right_dest_pos);
main_bg_se_copy_rect(bottom_right_src, bottom_right_dest_pos);
main_bg_se_copy_rect(bottom_left_src, bottom_left_dest_pos);
}
// Helper: Copy the four sides of a 9-patch
static inline void main_bg_se_expand_9_patch_stretch_sides(
Rect se_dest_rect,
const NinePatchRect* src_9_ptch,
int top_bottom_width,
int left_right_height
)
{
// We can take the Top and Bottom sides' tiles 1 by 1 and stretch them horizontally
// Top side
for (int i = 0; i < src_9_ptch->margins.top; i++)
{
SE top_side_tile = se_mat[MAIN_BG_SBB][src_9_ptch->patch_rect.top + i]
[src_9_ptch->patch_rect.left + src_9_ptch->margins.left];
memset16(
&se_mat[MAIN_BG_SBB][se_dest_rect.top + i]
[se_dest_rect.left + src_9_ptch->margins.left],
top_side_tile,
top_bottom_width
);
}
// Bottom side side
for (int i = 0; i < src_9_ptch->margins.bottom; i++)
{
SE bottom_side_tile = se_mat[MAIN_BG_SBB][src_9_ptch->patch_rect.bottom - i]
[src_9_ptch->patch_rect.left + src_9_ptch->margins.left];
memset16(
&se_mat[MAIN_BG_SBB][se_dest_rect.bottom - i]
[se_dest_rect.left + src_9_ptch->margins.left],
bottom_side_tile,
top_bottom_width
);
}
// Can't stretch the Left and Right sides with a wide memset16, and
// main_bg_se_fill_rect_with_se` does it rows-wise too. We'll have to copy rects by hand.
Rect left_side_tiles_src = {
src_9_ptch->patch_rect.left,
src_9_ptch->patch_rect.top + src_9_ptch->margins.top,
src_9_ptch->patch_rect.left + src_9_ptch->margins.left - 1,
src_9_ptch->patch_rect.top + src_9_ptch->margins.top
};
BG_POINT left_side_tiles_dest_pos = {
se_dest_rect.left,
se_dest_rect.top + src_9_ptch->margins.top
};
Rect right_side_tiles_src = {
src_9_ptch->patch_rect.right - src_9_ptch->margins.right + 1,
src_9_ptch->patch_rect.top + src_9_ptch->margins.top,
src_9_ptch->patch_rect.right,
src_9_ptch->patch_rect.top + src_9_ptch->margins.top
};
BG_POINT right_side_tiles_dest_pos = {
se_dest_rect.right - src_9_ptch->margins.right + 1,
se_dest_rect.top + src_9_ptch->margins.top
};
for (int i = 0; i < left_right_height; i++)
{
main_bg_se_copy_rect(left_side_tiles_src, left_side_tiles_dest_pos);
left_side_tiles_dest_pos.y++;
main_bg_se_copy_rect(right_side_tiles_src, right_side_tiles_dest_pos);
right_side_tiles_dest_pos.y++;
}
}
void main_bg_se_copy_expand_9_patch(Rect se_dest_rect, const NinePatchRect* src_9_ptch)
{
clip_se_rect_to_screenblock(&se_dest_rect);
int dest_rect_width = rect_width(&se_dest_rect);
int dest_rect_height = rect_height(&se_dest_rect);
int src_9ptch_min_width = src_9_ptch->margins.left + src_9_ptch->margins.right;
int src_9ptch_min_height = src_9_ptch->margins.top + src_9_ptch->margins.bottom;
// Verify the dest rect is big enough to fit at least the 9-patch's corners
if (dest_rect_width < src_9ptch_min_width || dest_rect_height < src_9ptch_min_height)
return;
// Copy the corners
main_bg_se_expand_9_patch_copy_corners(se_dest_rect, src_9_ptch);
// Fill the sides and center if needed
if (dest_rect_width > src_9ptch_min_width && dest_rect_height > src_9ptch_min_height)
{
// Stretch sides
main_bg_se_expand_9_patch_stretch_sides(
se_dest_rect,
src_9_ptch,
dest_rect_width - src_9ptch_min_width,
dest_rect_height - src_9ptch_min_height
);
// Fill center
SE middle_fill_se =
se_mat[MAIN_BG_SBB][src_9_ptch->patch_rect.top + src_9_ptch->margins.top]
[src_9_ptch->patch_rect.left + src_9_ptch->margins.left];
Rect dest_inner_fill_rect = {
se_dest_rect.left + src_9_ptch->margins.left,
se_dest_rect.top + src_9_ptch->margins.top,
se_dest_rect.right - src_9_ptch->margins.right,
se_dest_rect.bottom - src_9_ptch->margins.bottom
};
main_bg_se_fill_rect_with_se(middle_fill_se, dest_inner_fill_rect);
}
}
void tte_erase_rect_wrapper(Rect rect) void tte_erase_rect_wrapper(Rect rect)
{ {
tte_erase_rect(rect.left, rect.top, rect.right, rect.bottom); tte_erase_rect(rect.left, rect.top, rect.right, rect.bottom);
@@ -333,9 +423,7 @@ void update_text_rect_to_right_align_str(
void update_text_rect_to_center_str(Rect* rect, const char* str, enum ScreenHorzDir bias_direction) void update_text_rect_to_center_str(Rect* rect, const char* str, enum ScreenHorzDir bias_direction)
{ {
if (rect == NULL || str == NULL) if (rect == NULL || str == NULL)
{
return; return;
}
int text_width_chars = strlen(str); int text_width_chars = strlen(str);
int rect_width_chars = rect_width(rect) / TTE_CHAR_SIZE; int rect_width_chars = rect_width(rect) / TTE_CHAR_SIZE;
+1
View File
@@ -96,6 +96,7 @@ void init()
load_options(); load_options();
affine_background_init(); affine_background_init();
sprite_init(); sprite_init();
card_init();
blind_init(); blind_init();
joker_init(); joker_init();
game_init(); game_init();
+2 -1
View File
@@ -1,6 +1,7 @@
#include "random.h" #include "random.h"
#include "game_variables.h" #include "game_variables.h"
#include "util.h"
#include <stdlib.h> #include <stdlib.h>
#include <tonc.h> #include <tonc.h>
@@ -22,7 +23,7 @@ void rng_update(void)
void rng_set_seed(u32 seed) void rng_set_seed(u32 seed)
{ {
g_game_vars.rng_info.seed = seed % (MAX_SEED + 1); g_game_vars.rng_info.seed = seed % (MAX_BASE36 + 1);
g_game_vars.rng_info.step = 0; g_game_vars.rng_info.step = 0;
srand(g_game_vars.rng_info.seed); srand(g_game_vars.rng_info.seed);
} }
+6
View File
@@ -332,6 +332,12 @@ void load_options(void)
set_cards_more_readable(options.cards_more_readable); set_cards_more_readable(options.cards_more_readable);
} }
bool is_game_data_valid(void)
{
SaveHeader header;
return get_save_header(&header) && (header.valid_sections & SAVE_SECTION_FLAG_GAME);
}
void save_game(void) void save_game(void)
{ {
SaveGame game = SaveGame_default; SaveGame game = SaveGame_default;
+91
View File
@@ -191,3 +191,94 @@ uint16_t u16_protected_mult(uint16_t a, uint16_t b)
{ {
return (a == 0 || b == 0) ? 0 : (a > (UINT16_MAX / b) ? UINT16_MAX : a * b); return (a == 0 || b == 0) ? 0 : (a > (UINT16_MAX / b) ? UINT16_MAX : a * b);
} }
/**
* @brief Get the numerical value of a base-36 digit.
* Allowed values are [0-9, A-Z], with letters mapped to values of 10-35.
* Lowercase letters are mapped to the same values as the uppercase ones.
* Any other char is invalid and will be attributed a value of 0.
*
* @param c the char representing a digit in base-36
* @return u32
*/
static inline uint32_t base36_digit_value(char c)
{
switch (c)
{
case '0' ... '9':
return c - '0';
case 'A' ... 'Z':
return 10 + c - 'A';
case 'a' ... 'z':
return 10 + c - 'a';
default:
return 0;
}
}
/**
* @brief Get the char corresponding to a base-36 digit's numerical value.
* Inverse operation of base36_digit_value
*
* @param n decimal value of the base-36 char we want to get
* @return char
*
* @sa base36_digit_value
*/
static inline char base36_digit_char(uint32_t n)
{
switch (n)
{
case 0 ... 9:
return n + '0';
case 10 ... 35:
return n - 10 + 'A';
default:
return '\0';
}
}
/**
* @brief Get 36 to the power `i`
*
* @param i power of 36 we want, between 0 and `BASE36_MAX_DIGITS - 1`
* @return 36 to the power `i`
*
* @sa base36_digit_value
*/
static inline uint32_t get_base36_power(uint8_t i)
{
if (i >= BASE36_MAX_DIGITS)
{
return 0;
}
static const uint32_t powers_of_36[BASE36_MAX_DIGITS] = {1, 36, 1296, 46656, 1679616, 60466176};
return powers_of_36[i];
}
uint32_t base36_to_u32(const char b36_str[])
{
uint32_t res = 0;
for (uint8_t i = 0; i < BASE36_MAX_DIGITS; i++)
{
res += base36_digit_value(b36_str[i]) * get_base36_power(BASE36_MAX_DIGITS - i - 1);
}
return res;
}
void u32_to_base36(const uint32_t n, char b36_str[])
{
uint32_t power;
uint32_t acc = (n > MAX_BASE36) ? MAX_BASE36 : n;
for (int i = 0; i < BASE36_MAX_DIGITS; i++)
{
power = get_base36_power(BASE36_MAX_DIGITS - i - 1);
b36_str[i] = base36_digit_char(acc / power);
acc = acc % power;
}
// Properly end the string
b36_str[BASE36_MAX_DIGITS] = '\0';
}
+1 -1
View File
@@ -17,6 +17,6 @@ run_test() {
} }
run_test bitset run_test bitset
run_test pool
run_test list run_test list
run_test pool
run_test util run_test util
+80
View File
@@ -1,4 +1,5 @@
#include <font.h> #include <font.h>
#include <stdint.h>
#include <util.h> #include <util.h>
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
@@ -213,8 +214,87 @@ void test_truncate_uint_to_suffixed_str()
assert(strcmp(suffixed_str_buff, max_uint_str_buff) == 0); assert(strcmp(suffixed_str_buff, max_uint_str_buff) == 0);
} }
void test_base36_conversions(void)
{
char base36_str[BASE36_MAX_DIGITS + 1] = {'\0'};
uint32_t base10_int;
// Base-36 Str => Int
snprintf(base36_str, sizeof(base36_str), "%s", "ZZZZZZ");
assert(base36_to_u32(base36_str) == MAX_BASE36);
snprintf(base36_str, sizeof(base36_str), "%s", "000000");
assert(base36_to_u32(base36_str) == 0);
snprintf(base36_str, sizeof(base36_str), "%s", "000001");
assert(base36_to_u32(base36_str) == 1);
snprintf(base36_str, sizeof(base36_str), "%s", "000010");
assert(base36_to_u32(base36_str) == 36);
snprintf(base36_str, sizeof(base36_str), "%s", "000100");
assert(base36_to_u32(base36_str) == 1296);
snprintf(base36_str, sizeof(base36_str), "%s", "001000");
assert(base36_to_u32(base36_str) == 46656);
snprintf(base36_str, sizeof(base36_str), "%s", "010000");
assert(base36_to_u32(base36_str) == 1679616);
snprintf(base36_str, sizeof(base36_str), "%s", "100000");
assert(base36_to_u32(base36_str) == 60466176);
// Base-36 Str => Int
base10_int = 0;
u32_to_base36(base10_int, base36_str);
assert(strcmp(base36_str, "000000") == 0);
base10_int = 1;
u32_to_base36(base10_int, base36_str);
assert(strcmp(base36_str, "000001") == 0);
base10_int = 36;
u32_to_base36(base10_int, base36_str);
assert(strcmp(base36_str, "000010") == 0);
base10_int = 1296;
u32_to_base36(base10_int, base36_str);
assert(strcmp(base36_str, "000100") == 0);
base10_int = 46656;
u32_to_base36(base10_int, base36_str);
assert(strcmp(base36_str, "001000") == 0);
base10_int = 1679616;
u32_to_base36(base10_int, base36_str);
assert(strcmp(base36_str, "010000") == 0);
base10_int = 60466176;
u32_to_base36(base10_int, base36_str);
assert(strcmp(base36_str, "100000") == 0);
base10_int = MAX_BASE36;
u32_to_base36(base10_int, base36_str);
assert(strcmp(base36_str, "ZZZZZZ") == 0);
base10_int = UINT32_MAX;
u32_to_base36(base10_int, base36_str);
assert(strcmp(base36_str, "ZZZZZZ") == 0);
}
int main() int main()
{ {
printf("Testing Truncating UINT32.\n");
test_truncate_uint_to_suffixed_str(); test_truncate_uint_to_suffixed_str();
printf("Testing Base-36 conversions.\n");
test_base36_conversions();
printf("-------------------------------------------------------------------------------\n");
printf("Util Tests Passed :)\n");
printf("-------------------------------------------------------------------------------\n");
return 0; return 0;
} }