[Feature] Implement Joker descriptions in the Shop when holding B (#530)

* WIP

* WIP

* WIP

* WIP

* Tags are bugged but it's looking good

* Centering is bugged, almost there

* WIP

* Fixed centeriiiing, yaaaay

* good progress

* add proper '\n' support

* WIP descriptions

* All descriptions are there ^^

* missing 9-patch for pretty display

* WIP

* fix rebase issues

* Fix minor Run Setup screen issues

* Almost done, only one fix to go

* Aaaalmooost doooneeeee

* make desc frame size adaptative

* add rarity color and text

* final cleanup

* clang format

* clang format

* clang format

* clang format

* clang format

* Fix Joker update loop index

* Fix const and Bull description

* Fix unwanted button SFX

* fix tte colors

* Add misprint description

* Fix justified line length computation

* Add safeguards to tag parsing loop

* clang format

* Fix Ante bugged display at boss blind end

* Tweak suit colors

* Final fixes

* Simplified the way we print descriptions

* simplify further

* get rid of weird macro

* Fix Onwed joker price when holding A after showing its description

---------

Co-authored-by: MathisMartin31 <mathis.martin31@gmail.com>
This commit is contained in:
Geralt
2026-06-08 18:16:37 -07:00
committed by GitHub
co-authored by MathisMartin31
parent 917354f057
commit e26558e1a5
23 changed files with 1295 additions and 135 deletions
+1 -1
View File
@@ -1 +1 @@
-gB8 -mR8 -mLs -mRtf -pe 160
-gB8 -mR8 -mLs -mRtf -pe 64
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

+1 -1
View File
@@ -1 +1 @@
-gB8 -mR8 -mLs -mRtf -pe 160
-gB8 -mR8 -mLs -mRtf -pe 64
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

+1 -1
View File
@@ -1 +1 @@
-gB8 -mR8 -mLs -mRtf -pn 160
-gB8 -mR8 -mLs -mRtf -pn 64
+1 -1
View File
@@ -1 +1 @@
-gB8 -mR8 -mLs -mRtf -pe 160
-gB8 -mR8 -mLs -mRtf -pe 64
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

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

After

Width:  |  Height:  |  Size: 566 B

+1 -1
View File
@@ -26,7 +26,7 @@
* @def AFFINE_BG_PB
* @brief The starting index of the background palette.
*/
#define AFFINE_BG_PB (PAL_ROW_LEN * 10)
#define AFFINE_BG_PB (PAL_ROW_LEN * 15)
/**
* @brief An ID to specify background rendering types.
+1
View File
@@ -79,6 +79,7 @@ void game_reset(void);
void game_update(void);
void game_change_state(enum GameState new_game_state);
enum GameState game_get_state(void);
CardObject** get_played_array(void);
int get_played_top(void);
+9
View File
@@ -6,6 +6,8 @@
#ifndef GAME_SHOP_H
#define GAME_SHOP_H
#include "joker.h"
#include <stdbool.h>
/**
@@ -14,6 +16,13 @@
*/
void game_shop_reset(void);
/**
* @brief Get a pointer to the Card we are currently showing the description of.
*
* @return JokerObject*
*/
JokerObject* game_shop_get_description_card(void);
/**
* @brief Set whether a Joker can appear in the shop.
*
+130 -5
View File
@@ -63,20 +63,32 @@
/** @def TTE_BIT_ON_CLR_IDX */
#define TTE_BIT_ON_CLR_IDX TTE_BIT_UNPACK_OFFSET + 1
/** @def TTE_GREEN_PB */
#define TTE_GREEN_PB 6 // 0x6
/** @def TTE_PURPLE_PB */
#define TTE_PURPLE_PB 7 // 0x7
/** @def TTE_DARK_GREEN_PB */
#define TTE_DARK_GREEN_PB 8 // 0x8
/** @def TTE_DARK_BLUE_PB */
#define TTE_DARK_BLUE_PB 9 // 0x9
/** @def TTE_BLACK_PB */
#define TTE_BLACK_PB 0 // 0x0
#define TTE_BLACK_PB 10 // 0xA
/** @def TTE_YELLOW_PB */
#define TTE_YELLOW_PB 12 // 0xC
#define TTE_YELLOW_PB 11 // 0xB
/** @def TTE_BLUE_PB */
#define TTE_BLUE_PB 13 // 0xD
#define TTE_BLUE_PB 12 // 0xC
/** @def TTE_RED_PB */
#define TTE_RED_PB 14 // 0xE
#define TTE_RED_PB 13 // 0xD
/** @def TTE_WHITE_PB */
#define TTE_WHITE_PB 15 // 0xF
#define TTE_WHITE_PB 14 // 0xE
/** @def TTE_SPECIAL_PB_MULT_OFFSET */
#define TTE_SPECIAL_PB_MULT_OFFSET 0x1000
@@ -90,12 +102,74 @@
/** @} */
/**
* @name TTE Text formatting Tags
*
* @{
*/
/** @def TTE_GREEN_TAG */
#define TTE_GREEN_TAG "#{cx:0x6000}"
/** @def TTE_PURPLE_TAG */
#define TTE_PURPLE_TAG "#{cx:0x7000}"
/** @def TTE_GREEN_TAG */
#define TTE_DARK_GREEN_TAG "#{cx:0x8000}"
/** @def TTE_PURPLE_TAG */
#define TTE_DARK_BLUE_TAG "#{cx:0x9000}"
/** @def TTE_BLACK_TAG */
#define TTE_BLACK_TAG "#{cx:0xA000}"
/** @def TTE_YELLOW_TAG */
#define TTE_YELLOW_TAG "#{cx:0xB000}"
/** @def TTE_BLUE_TAG */
#define TTE_BLUE_TAG "#{cx:0xC000}"
/** @def TTE_RED_TAG */
#define TTE_RED_TAG "#{cx:0xD000}"
/** @def TTE_WHITE_TAG */
#define TTE_WHITE_TAG "#{cx:0xE000}"
/** @def TTE_DIAMOND_TAG */
#define TTE_DIAMOND_TAG TTE_YELLOW_TAG "Diamond "
/** @def TTE_HEART_TAG */
#define TTE_HEART_TAG TTE_RED_TAG "Hearts "
/** @def TTE_SPADE_TAG */
#define TTE_SPADE_TAG TTE_DARK_BLUE_TAG "Spade "
/** @def TTE_CLUB_TAG */
#define TTE_CLUB_TAG TTE_DARK_GREEN_TAG "Club "
/** @} */
/**
* @name Text colors
*
* @{
*/
/** @def TEXT_CLR_GREEN */
#define TEXT_CLR_GREEN RGB15(10, 27, 16)
/** @def TEXT_CLR_PURPLE */
#define TEXT_CLR_PURPLE RGB15(25, 15, 31)
/** @def TEXT_CLR_DARK_GREEN */
#define TEXT_CLR_DARK_GREEN RGB15(0, 14, 8)
/** @def TEXT_CLR_DARK_BLUE */
#define TEXT_CLR_DARK_BLUE RGB15(10, 5, 15)
/** @def TEXT_CLR_BLACK */
#define TEXT_CLR_BLACK RGB15(3, 5, 5) // ~ 0x1483
/** @def TEXT_CLR_YELLOW */
#define TEXT_CLR_YELLOW RGB15(31, 20, 0) // 0x029F
@@ -147,6 +221,12 @@ enum OverflowDir
OVERFLOW_RIGHT = SCREEN_RIGHT
};
enum TextJustifyFlag
{
JUSTIFY_LEFT,
JUSTIFY_CENTER
};
/** @} */
// When making this, missed that it already exists in tonc_math.h
@@ -183,6 +263,19 @@ typedef struct NinePatchRect
Rect margins;
} NinePatchRect;
INLINE void tte_colors_setup(void)
{
pal_bg_bank[TTE_GREEN_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_GREEN;
pal_bg_bank[TTE_PURPLE_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_PURPLE;
pal_bg_bank[TTE_DARK_GREEN_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_DARK_GREEN;
pal_bg_bank[TTE_DARK_BLUE_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_DARK_BLUE;
pal_bg_bank[TTE_BLACK_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_BLACK;
pal_bg_bank[TTE_YELLOW_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_YELLOW;
pal_bg_bank[TTE_BLUE_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_BLUE;
pal_bg_bank[TTE_RED_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_RED;
pal_bg_bank[TTE_WHITE_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_WHITE;
}
/**
* @brief Get the width of a rectangle
*
@@ -422,4 +515,36 @@ void toggle_windows(bool win0, bool win1);
*/
void reset_top_left_panel_bottom_row(void);
/**
* @brief Justify a text with custom formatting tags according to the given
* justification and bias direction tags
*
* - The raw_text will be divided into Words, separated by 1 space, and 1 space only.
* - Each Word can have one or more formatting `{TAGS}` attached to it, and they MUST be right
* next to each other. It might not apply correctly otherwise if the line wraps.
* - `{TAGS}` must be one of TTE_BLACK_TAG, TTE_YELLOW_TAG, etc.
*
* @param raw_text The unformatted text. The '\n' character and custom formatting `{TAGS}` will
* not count towards the computed line widths, as they won't appear on screen.
* @param dst_rect Rectangle the justified text must fit into. Will overflow at the bottom if
* text is too long. Size in tiles
* @param justify_direction Align the text either to the left or center.
* @param bias_direction Used with `JUSTIFY_CENTER` only. Determines if lines that cannot be
* centered are to be slightly to the left or to the right.
* @param do_print does not actually print anything, only does the calculations to determine
* the number of lines taken by the paragraph
*
* @returns the number of lines the wrapped paragraph takes
*
* @sa update_text_rect_to_center_str
* @sa TTE_BLACK_TAG
*/
int tte_printf_justified_in_rect(
const char* raw_text,
Rect dst_rect,
enum TextJustifyFlag justify_direction,
enum ScreenHorzDir bias_direction,
bool do_print
);
#endif // GRAPHIC_UTILS_H
+35
View File
@@ -1,3 +1,8 @@
/**
* @file joker.h
*
* @brief Functions relative to the handling of Jokers
*/
#ifndef JOKER_H
#define JOKER_H
@@ -37,6 +42,8 @@
#define RARE_JOKER 2
#define LEGENDARY_JOKER 3
#define MAX_RARITIES (LEGENDARY_JOKER + 1)
// Percent chance to get a joker of each rarity
// Note that this deviates slightly from the Balatro wiki to allow legendary
// jokers to appear without spectral cards implemented
@@ -132,10 +139,16 @@ typedef u32 (*JokerEffectFunc)(
JokerEffect** joker_effect
);
typedef int (*JokerDescFunc)(Joker* joker, Rect dest_rect);
typedef struct
{
const char* name;
u8 rarity;
u8 base_value;
bool is_desc_dynamic; // Is the little variable description at the bottom dynamic?
// Only used by the Misprint joker for now
JokerDescFunc joker_print_desc;
JokerEffectFunc joker_effect_func;
} JokerInfo;
const JokerInfo* get_joker_registry_entry(int joker_id);
@@ -156,6 +169,28 @@ u32 joker_get_score_effect(
enum JokerEvent joker_event,
JokerEffect** joker_effect
);
const char* joker_get_rarity_string(u8 rarity);
/**
* @brief Get Joker rarity panel color.
*
* The colors are organized in the `card_rarity_pal_gfx.png` file which is organized like this:
* - 0 -> transparency
* - 1,2 -> Common Joker
* - 3,4 -> Uncommon Joker
* - 5,6 -> Rare Joker
* - 7,8 -> Legendary Joker / Tarot Card
* - 9,10 -> Planet Card
* - 11,12 -> Spectral Card
* - 13,14 -> Voucher
*
* @param rarity Value of the rarity (Common, Rare...)
* @param main_color Whether we want the main or shadow color
* @return u16 value of the color, not a pointer
*/
u16 joker_get_rarity_color(u8 rarity, bool main_color);
int joker_get_sell_value(const Joker* joker);
JokerObject* joker_object_new(Joker* joker);
+2
View File
@@ -18,9 +18,11 @@ static const BG_POINT HELD_JOKERS_POS = {108, 10};
// Rects left top right bottom
static const Rect TOP_LEFT_PANEL_ANIM_RECT = {0, 0, 8, 4};
static const Rect POP_MENU_ANIM_RECT = {9, 7, 24, 31};
static const Rect DECK_ANIM_RECT = {25, 14, 28, 23}; // Can be used for the Shop and Blind Select screen
static const Rect TOP_LEFT_ITEM_SRC_RECT = {0, 20, 8, 25};
static const Rect BLIND_REWARD_RECT = {40, 32, 64, 40};
static const Rect BLIND_REQ_TEXT_RECT = {32, 24, 64, 32};
static const Rect PLAYING_SCREEN_RECT = {72, 0, 240, 160};
static const Rect HAND_SIZE_RECT = {128, 128, 152, 160}; // Seems to include both SELECT and PLAYING
// clang-format on
+31 -35
View File
@@ -376,6 +376,19 @@ static inline Card* discard_pop()
return discard_pile[discard_top--];
}
static inline void display_ante(void)
{
tte_printf(
"#{P:%d,%d; cx:0x%X000}%ld#{cx:0x%X000}/%d",
ANTE_TEXT_RECT.left,
ANTE_TEXT_RECT.top,
TTE_YELLOW_PB,
g_game_vars.ante,
TTE_WHITE_PB,
MAX_ANTE
);
}
void game_init()
{
state_machine_remove(&game_sm);
@@ -437,15 +450,7 @@ void game_reset()
display_discards();
display_money();
// Ante
tte_printf(
"#{P:%d,%d; cx:0x%X000}%ld#{cx:0x%X000}/%d",
ANTE_TEXT_RECT.left,
ANTE_TEXT_RECT.top,
TTE_YELLOW_PB,
g_game_vars.ante,
TTE_WHITE_PB,
MAX_ANTE
);
display_ante();
affine_background_load_palette(affine_background_gfxPal);
}
@@ -490,8 +495,10 @@ static inline void held_jokers_update_loop(void)
int i = 0;
while ((joker = list_itr_next(&itr)))
{
joker->sprite_object->tx = hand_x - int2fx(spacing_lut[jokers_top][i++]);
// Let the Shop handle the position of this Joker
if (joker != game_shop_get_description_card())
joker->sprite_object->tx = hand_x - int2fx(spacing_lut[jokers_top][i]);
i++;
joker_object_update(joker);
}
}
@@ -558,6 +565,11 @@ void game_change_state(enum GameState new_game_state)
state_machine_change_state(&game_sm, new_game_state);
}
enum GameState game_get_state(void)
{
return game_sm.state;
}
CardObject** get_played_array(void)
{
return played;
@@ -761,17 +773,6 @@ void display_mult(void)
check_flaming_score();
}
static inline void display_ante(int value)
{
tte_printf(
"#{P:%d,%d; cx:0xC000}%d#{cx:0xF000}/%d",
ANTE_TEXT_RECT.left,
ANTE_TEXT_RECT.top,
value,
MAX_ANTE
);
}
// Returns true if the card is *considered* a face card
bool card_is_face(Card* card)
{
@@ -972,9 +973,10 @@ void display_round(void)
void display_hands(void)
{
tte_printf(
"#{P:%d,%d; cx:0xD000}%ld",
"#{P:%d,%d; cx:0x%X000}%ld",
HANDS_TEXT_RECT.left,
HANDS_TEXT_RECT.top,
TTE_BLUE_PB,
g_game_vars.hands
);
}
@@ -982,9 +984,10 @@ void display_hands(void)
void display_discards(void)
{
tte_printf(
"#{P:%d,%d; cx:0xE000}%ld",
"#{P:%d,%d; cx:0x%X000}%ld",
DISCARDS_TEXT_RECT.left,
DISCARDS_TEXT_RECT.top,
TTE_RED_PB,
g_game_vars.discards
);
}
@@ -1382,7 +1385,8 @@ static inline void game_playing_handle_round_over(void)
{
if (g_game_vars.ante < MAX_ANTE)
{
display_ante(++g_game_vars.ante);
g_game_vars.ante++;
display_ante();
// mark current boss blind as beaten and allow for reroll
set_blind_beaten(g_game_vars.next_boss_blind);
@@ -2549,6 +2553,7 @@ static void game_playing_on_update(void)
void game_start(void)
{
affine_background_change_background(AFFINE_BG_GAME);
tte_colors_setup();
g_game_vars.hands = MAX_HANDS;
g_game_vars.discards = MAX_DISCARDS;
@@ -2587,16 +2592,7 @@ void game_start(void)
display_discards(); // Discard
display_money(); // Set the money display
tte_printf(
"#{P:%d,%d; cx:0x%X000}%ld#{cx:0x%X000}/%d",
ANTE_TEXT_RECT.left,
ANTE_TEXT_RECT.top,
TTE_YELLOW_PB,
g_game_vars.ante,
TTE_WHITE_PB,
MAX_ANTE
); // Ante
display_ante();
game_change_state(GAME_STATE_BLIND_SELECT);
}
+8 -3
View File
@@ -768,6 +768,7 @@ static int seed_play_get_row_size(void)
*/
static void seed_play_row_on_key_transit(SelectionGrid* selection_grid, Selection* selection)
{
if (key_hit(SELECT_CARD))
button_press(&choose_deck_bottom_buttons[selection->x]);
}
@@ -1004,14 +1005,19 @@ static inline Button* choose_seed_get_button_from_sel(const Selection* sel)
/**
* @brief Handle button imputs in `choose_seed_selection_grid`
*
* Always register A press, but only B press when we are on a keyboard row
*
* @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))
if (key_hit(SELECT_CARD) ||
(selection->y <= RUN_SETUP_SEED_ROW_KEY3 && key_hit(DESELECT_CARDS)))
{
button_press(choose_seed_get_button_from_sel(selection));
}
}
/**
* @brief Handle directional imputs in `choose_seed_selection_grid`
@@ -1133,7 +1139,6 @@ static inline void toggle_seed_enabled(bool enable)
*/
static void use_seed_on_pressed(void)
{
if (key_hit(SELECT_CARD))
toggle_seed_enabled(!use_seed);
}
@@ -1142,7 +1147,7 @@ static void use_seed_on_pressed(void)
*/
static void seed_on_pressed(void)
{
if (key_hit(SELECT_CARD) && use_seed)
if (use_seed)
state_machine_change_state(&run_setup_sm, RUN_SETUP_SUBSTATE_CHOOSE_SEED);
}
+305 -10
View File
@@ -24,13 +24,17 @@
#include "timer.h"
#include "util.h"
#include <string.h>
// Timer defs
#define TM_END_GAME_SHOP_INTRO 12
#define TM_CREATE_SHOP_ITEMS_WAIT 1
#define TM_SHIFT_SHOP_ICON_WAIT 7
#define TM_SHOW_CARD_DESC_WAIT 12
// Pixel sized
#define ITEM_SHOP_Y 71
#define OWNED_CARDS_HIDE_Y_OFFSET 50
// Shop
#define REROLL_BASE_COST 5 // Base cost for rerolling the shop items
@@ -47,6 +51,8 @@
#define SHOP_LIGHTS_3_PAL_IDX 17
#define SHOP_LIGHTS_4_PAL_IDX 22
#define SHOP_BOTTOM_PANEL_BORDER_PAL_IDX 26
#define SHOP_DESC_RARITY_MAIN_COLOR_PAL_IDX 27
#define SHOP_DESC_RARITY_SHADOW_COLOR_PAL_IDX 28
#define SHOP_LIGHTS_1_CLR 0xFFFF
#define SHOP_LIGHTS_2_CLR 0x32BE
@@ -54,13 +60,31 @@
#define SHOP_LIGHTS_4_CLR 0x5F9F
// clang-format off
// Positions in tiles
static const BG_POINT SHOP_CLEAR_3X3_SRC_POS = { 29, 0};
static const Rect SHOP_ICON_FROM_RECT = { 0, 26, 8, 26};
static const BG_POINT SHOP_ICON_TO_POS = { 0, 0};
static const Rect SHOP_PRICES_TEXT_RECT = { 72, 56, 192, 160 };
static const Rect SHOP_REROLL_RECT = { 88, 96, UNDEFINED, UNDEFINED };
static const BG_POINT OWNED_CARDS_PANEL_3X3_SRC_POS = { 29, 21};
static const Rect OWNED_JOKERS_PANEL_RECT = { 9, 1, 21, 5};
static const Rect OWNED_CONSUMABLES_PANEL_RECT = { 23, 1, 28, 5};
static const Rect OWNED_CARDS_PANEL_RECT = { 9, 1, 28, 5};
static const Rect CARD_DESC_9_PTCH_TO_RECT = { 9, 6, 28, 18};
static const NinePatchRect CARD_DESC_9_PTCH_SRC = {
.patch_rect = { 27, 25, 31, 31},
.margins = { 2, 3, 2, 3}
};
static const int CARD_DESC_MAX_TEXT_HEIGHT = CARD_DESC_9_PTCH_TO_RECT.bottom -
CARD_DESC_9_PTCH_TO_RECT.top + 1 -
CARD_DESC_9_PTCH_SRC.margins.top -
CARD_DESC_9_PTCH_SRC.margins.bottom;
static const Rect CARD_DESC_TEXT_RECT = { 11, 9, 26, 18};
static const Rect CARD_NAME_TEXT_RECT = { 10, 7, 27, 7};
// Positions in pixels
static const BG_POINT SHOP_JOKER_SPRITES_INIT_POS = {120, 160};
static const BG_POINT CARD_DESCRIPTION_SPRITE_POS = {135, 9};
static const Rect SHOP_PRICES_TEXT_RECT = { 72, 56, 192, 160 };
static const Rect SHOP_REROLL_RECT = { 88, 96, UNDEFINED, UNDEFINED };
// clang-format on
static List s_shop_jokers_list = LIST_DEFAULT;
@@ -70,17 +94,23 @@ enum GameShopStates
{
GAME_SHOP_INTRO,
GAME_SHOP_ACTIVE,
GAME_SHOP_SHOW_CARD_DESC,
GAME_SHOP_HIDE_CARD_DESC,
GAME_SHOP_EXIT,
GAME_SHOP_MAX
};
static void game_shop_intro(void);
static void game_shop_process_user_input(void);
static void game_shop_show_card_desc(void);
static void game_shop_hide_card_desc(void);
static void game_shop_outro(void);
static StateInfo shop_state_actions[] = {
static StateInfo shop_state_actions[GAME_SHOP_MAX] = {
STATE_INFO_UPDATE_FN_ONLY(game_shop_intro),
STATE_INFO_UPDATE_FN_ONLY(game_shop_process_user_input),
STATE_INFO_UPDATE_FN_ONLY(game_shop_show_card_desc),
STATE_INFO_UPDATE_FN_ONLY(game_shop_hide_card_desc),
STATE_INFO_UPDATE_FN_ONLY(game_shop_outro),
};
@@ -139,6 +169,17 @@ static int timer;
static int reroll_cost = REROLL_BASE_COST;
// Variables relative to the Card we are showing the description of
static JokerObject* description_card = NULL;
static FIXED description_card_original_x_pos = UNDEFINED;
static FIXED description_card_original_y_pos = UNDEFINED;
static List* description_card_original_list = NULL;
JokerObject* game_shop_get_description_card(void)
{
return description_card;
}
static inline void reset_shop_jokers(void)
{
int num_jokers = get_joker_registry_size();
@@ -533,9 +574,9 @@ static bool shop_reroll_row_on_selection_changed(
* @brief Reroll items up for sale in the Shop.
* Reroll cost will go up by a rate that increases by 1 each reroll.
*/
static inline void game_shop_reroll(int* reroll_cost)
static inline void game_shop_reroll(void)
{
g_game_vars.money -= *reroll_cost;
g_game_vars.money -= reroll_cost;
display_money(); // Update the money display
List* shop_jokers_list = &s_shop_jokers_list;
@@ -570,13 +611,13 @@ static inline void game_shop_reroll(int* reroll_cost)
}
}
(*reroll_cost)++;
reroll_cost++;
tte_printf(
"#{P:%d,%d; cx:0x%X000}$%d",
SHOP_REROLL_RECT.left,
SHOP_REROLL_RECT.top,
TTE_WHITE_PB,
*reroll_cost
reroll_cost
);
}
@@ -606,7 +647,7 @@ static void next_round_on_pressed(void)
static void reroll_on_pressed(void)
{
// TODO: Add money sound effect
game_shop_reroll(&reroll_cost);
game_shop_reroll();
}
static bool reroll_can_be_pressed(void)
@@ -617,16 +658,270 @@ static bool reroll_can_be_pressed(void)
/**
* @brief Handle user inputs logic in the Shop though a SelectionGrid.
*/
static void game_shop_process_user_input()
static void game_shop_process_user_input(void)
{
selection_grid_process_input(&shop_selection_grid);
static JokerObject* tmp_card = NULL;
// Determine the Joker we would show the description of
switch (shop_selection_grid.selection.y)
{
// Owned Joker
case 0:
{
description_card_original_list = get_jokers_list();
tmp_card = list_get_at_idx(get_jokers_list(), shop_selection_grid.selection.x);
break;
}
// Jokers for sale
case 1:
{
description_card_original_list = &s_shop_jokers_list;
tmp_card = (shop_selection_grid.selection.x > 0)
? list_get_at_idx(&s_shop_jokers_list, shop_selection_grid.selection.x - 1)
: NULL;
break;
}
// TODO: handle Consumables and Vouchers when implemented
default:
{
description_card_original_list = NULL;
tmp_card = NULL;
break;
}
}
// Show description of selected card when pressing B.
// Always wait for the card in question to be immobile to avoid accumulating
// errors when pressing and releasing B in quick succession.
if (tmp_card != NULL && tmp_card->sprite_object->vx == 0 && tmp_card->sprite_object->vy == 0 &&
key_held(DESELECT_CARDS))
{
description_card = tmp_card;
description_card_original_x_pos = description_card->sprite_object->x;
description_card_original_y_pos = description_card->sprite_object->y;
timer = TM_ZERO;
state_machine_change_state(&shop_sm, GAME_SHOP_SHOW_CARD_DESC);
}
}
static void game_shop_show_card_desc(void)
{
// Anim start
if (timer == 1)
{
// Erase shop text and disable transparency window
tte_erase_rect_wrapper(PLAYING_SCREEN_RECT);
toggle_windows(false, true);
// Move all other Jokers offscreen
JokerObject* joker_object = NULL;
// Owned Jokers
ListItr itr = list_itr_create(get_jokers_list());
while ((joker_object = list_itr_next(&itr)))
{
if (joker_object != description_card)
joker_object->sprite_object->ty -= int2fx(OWNED_CARDS_HIDE_Y_OFFSET);
}
// Shop Jokers
itr = list_itr_create(&s_shop_jokers_list);
while ((joker_object = list_itr_next(&itr)))
{
if (joker_object != description_card)
joker_object->sprite_object->ty = int2fx(SHOP_JOKER_SPRITES_INIT_POS.y + TILE_SIZE);
}
// Set description_card new target position
description_card->sprite_object->tx = int2fx(CARD_DESCRIPTION_SPRITE_POS.x);
description_card->sprite_object->ty = int2fx(CARD_DESCRIPTION_SPRITE_POS.y);
}
// First 12 anim frames
if (timer <= TM_SHOW_CARD_DESC_WAIT)
{
// Hide Deck (last 5 frames only)
if (TM_SHOW_CARD_DESC_WAIT - timer < 5)
main_bg_se_move_rect_1_tile_vert(DECK_ANIM_RECT, SCREEN_DOWN);
// Hide shop panel
main_bg_se_move_rect_1_tile_vert(POP_MENU_ANIM_RECT, SCREEN_DOWN);
// Hide Owned Cards panels
main_bg_se_move_rect_1_tile_vert(OWNED_CARDS_PANEL_RECT, SCREEN_UP);
}
// Anim end
else if (timer == TM_SHOW_CARD_DESC_WAIT + 1)
{
// Compute needed space for the description
const JokerInfo* info = get_joker_registry_entry(description_card->joker->id);
int desc_bottom_offset =
CARD_DESC_MAX_TEXT_HEIGHT -
info->joker_print_desc(description_card->joker, CARD_DESC_TEXT_RECT);
// Print Rarity and change color or the panel
// Do it before drawing the panel so the color is already set
const char* rarity_str = joker_get_rarity_string(info->rarity);
tte_printf(
TTE_WHITE_TAG "#{P:%d,%d}%*s%s",
CARD_DESC_TEXT_RECT.left * TILE_SIZE,
(CARD_DESC_TEXT_RECT.bottom - desc_bottom_offset - 1) * TILE_SIZE,
(rect_width(&CARD_DESC_TEXT_RECT) - strlen(rarity_str)) / 2,
"",
rarity_str
);
pal_bg_mem[SHOP_DESC_RARITY_MAIN_COLOR_PAL_IDX] =
joker_get_rarity_color(info->rarity, true);
pal_bg_mem[SHOP_DESC_RARITY_SHADOW_COLOR_PAL_IDX] =
joker_get_rarity_color(info->rarity, false);
// Draw description panel
Rect actual_dest_rect = CARD_DESC_9_PTCH_TO_RECT;
actual_dest_rect.bottom -= desc_bottom_offset;
main_bg_se_copy_expand_9_patch(actual_dest_rect, &CARD_DESC_9_PTCH_SRC);
// Print joker name
tte_printf(
TTE_WHITE_TAG "#{P:%d,%d}%*s%s",
CARD_NAME_TEXT_RECT.left * TILE_SIZE,
CARD_NAME_TEXT_RECT.top * TILE_SIZE,
(rect_width(&CARD_NAME_TEXT_RECT) - strlen(info->name)) / 2,
"",
info->name
);
}
// Actively wait for the B button to be released, but only if the described card has stopped
// moving
else if (description_card->sprite_object->vx == 0 && description_card->sprite_object->vy == 0 &&
!key_held(DESELECT_CARDS))
{
timer = TM_ZERO;
state_machine_change_state(&shop_sm, GAME_SHOP_HIDE_CARD_DESC);
}
}
static void game_shop_hide_card_desc(void)
{
// just so we don't print the price of an owned Joker too many times
static bool owned_joker_price_printed = false;
// Anim start
if (timer == 1)
{
// Erase shop text and Joker Description frame
tte_erase_rect_wrapper(PLAYING_SCREEN_RECT);
main_bg_se_copy_expand_3x3_rect(CARD_DESC_9_PTCH_TO_RECT, SHOP_CLEAR_3X3_SRC_POS);
// Enable transparency window
toggle_windows(false, true);
// Redraw Jokers/Consumables frames
main_bg_se_copy_expand_3x3_rect(OWNED_JOKERS_PANEL_RECT, OWNED_CARDS_PANEL_3X3_SRC_POS);
main_bg_se_copy_expand_3x3_rect(
OWNED_CONSUMABLES_PANEL_RECT,
OWNED_CARDS_PANEL_3X3_SRC_POS
);
// Move Jokers back to their positions
JokerObject* joker_object = NULL;
// Owned Jokers
ListItr itr = list_itr_create(get_jokers_list());
while ((joker_object = list_itr_next(&itr)))
{
if (joker_object != description_card)
joker_object->sprite_object->ty = int2fx(HELD_JOKERS_POS.y);
}
// Shop Jokers
itr = list_itr_create(&s_shop_jokers_list);
while ((joker_object = list_itr_next(&itr)))
{
if (joker_object != description_card)
joker_object->sprite_object->ty = int2fx(ITEM_SHOP_Y);
}
description_card->sprite_object->tx = description_card_original_x_pos;
description_card->sprite_object->ty = description_card_original_y_pos;
}
// First 12 anim frames
if (timer <= TM_SHOW_CARD_DESC_WAIT)
{
// Show Deck (last 5 frames only)
if (TM_SHOW_CARD_DESC_WAIT - timer < 5)
main_bg_se_move_rect_1_tile_vert(DECK_ANIM_RECT, SCREEN_UP);
// Show shop panel
main_bg_se_move_rect_1_tile_vert(POP_MENU_ANIM_RECT, SCREEN_UP);
}
// Last anim frame (no need to wait for the Joker to have stopped for this):
else if (timer == TM_SHOW_CARD_DESC_WAIT + 1)
{
// Need to account for the description_card being selected if it came from the shop.
if (description_card_original_list == &s_shop_jokers_list)
description_card->sprite_object->ty += int2fx(TILE_SIZE);
// Print price under shop Jokers
JokerObject* joker_object = NULL;
ListItr itr = list_itr_create(&s_shop_jokers_list);
while ((joker_object = list_itr_next(&itr)))
{
sprite_object_print_price_under(
joker_object->sprite_object,
joker_object->joker->value
);
}
if (description_card_original_list == &s_shop_jokers_list)
description_card->sprite_object->ty -= int2fx(TILE_SIZE);
// Print Reroll prince
tte_printf(
"#{P:%d,%d; cx:0x%X000}$%d",
SHOP_REROLL_RECT.left,
SHOP_REROLL_RECT.top,
TTE_WHITE_PB,
reroll_cost
);
}
// Cleanup and change state
else if (description_card->sprite_object->vx == 0 && description_card->sprite_object->vy == 0)
{
owned_joker_price_printed = false;
description_card = NULL;
timer = TM_ZERO;
state_machine_change_state(&shop_sm, GAME_SHOP_ACTIVE);
}
// At any point after the other prices have been printed, and while the card is still moving,
// if we are NOT pressing A, print the price under it.
else if (!owned_joker_price_printed && !key_held(SELECT_CARD) &&
description_card_original_list == get_jokers_list())
{
owned_joker_price_printed = true;
sprite_object_print_price_under(
description_card->sprite_object,
description_card->joker->value
);
}
}
/**
* @brief Outro sequence substate update.
* This makes the menu and shop icon go out of frame.
*/
static void game_shop_outro()
static void game_shop_outro(void)
{
// Shift the shop panel
main_bg_se_move_rect_1_tile_vert(POP_MENU_ANIM_RECT, SCREEN_DOWN);
+136
View File
@@ -2,6 +2,7 @@
#include "layout.h"
#include <stdio.h>
#include <string.h>
#include <tonc_core.h>
#include <tonc_math.h>
@@ -507,3 +508,138 @@ void reset_top_left_panel_bottom_row(void)
top_left_panel_bottom_row_pos.y += rect_height(&TOP_LEFT_ITEM_SRC_RECT) - 1;
main_bg_se_copy_rect(TOP_LEFT_PANEL_BOTTOM_ROW_RESET_RECT, top_left_panel_bottom_row_pos);
}
// Width of the screen, in nb of tiles
#define MAX_LINE_TEXT_LENGTH 30
int tte_printf_justified_in_rect(
const char* raw_text,
Rect dst_rect,
enum TextJustifyFlag justify_direction,
enum ScreenHorzDir bias_direction,
bool do_print
)
{
// These are the actual lengths of the line/token, which take the {TAGS} into account
int raw_text_len = strlen(raw_text);
int line_start = 0;
int token_start = 0;
int token_len = 0;
// These lengths correspond to what will be visible on screen
int max_line_text_len = rect_width(&dst_rect);
int line_text_len = 0;
int token_text_len = 0;
int line_x = 0;
int line_y = 0;
// Will exit when there are no more words
while (line_start < raw_text_len)
{
// Need to do everything by hand, as it seems tte_printf does NOT stop at \0
token_len = 0;
token_text_len = 0;
// Parse the `raw_text` and
while (line_text_len <= max_line_text_len && token_start < raw_text_len)
{
int current_char = token_start + token_len;
// Handle tags
if (raw_text[current_char] == '#' && (current_char + 1) < raw_text_len &&
raw_text[current_char + 1] == '{')
{
while (current_char < raw_text_len && raw_text[current_char] != '}')
{
token_len++;
current_char++;
}
if (current_char < raw_text_len)
{
token_len++;
current_char++;
}
}
// Handle special cases
// End of word/text detected
if (raw_text[current_char] == ' ' || raw_text[current_char] == '\0')
{
token_start = current_char + 1;
// Set these to -1 so that it becomes 0 when incremented for the
// next iteration since we need to continue.
token_len = -1;
token_text_len = -1;
}
// Early end of line detected, interrupt parsing immediately
else if (raw_text[current_char] == '\n')
{
token_start = current_char + 1;
// Set to 0, no need to compensate for continuing the loops since
// we're breaking early from it.
token_len = 0;
token_text_len = 0;
line_text_len++;
break;
}
token_len++;
token_text_len++;
line_text_len++;
};
// Do not print anything if we only need to compute the paragraph's total height
if (do_print)
{
// Length of the raw slice for this line (includes formatting tags)
int line_len = token_start - line_start;
// Trim trailing whitespace/newlines from the raw slice
while (line_len > 0)
{
char c = raw_text[line_start + line_len - 1];
if (c == ' ' || c == '\n' || c == '\0')
line_len--;
else
break;
}
// Remove the overflowing token's visible length from this line
line_text_len = line_text_len - token_text_len - 1;
if (line_text_len < 0)
line_text_len = 0;
// useful DEBUG
// tte_printf("#{P:0,%d}line %d - %d", 40 + (dst_rect.top + line_y) * TILE_SIZE, line_y,
// line_text_len);
// Now, we can print the chars from line_start to token_start
line_x = 0;
if (justify_direction == JUSTIFY_CENTER)
{
line_x = (max_line_text_len - line_text_len) / 2;
}
tte_printf(
"#{P:%d,%d}%.*s",
(dst_rect.left + line_x) * TILE_SIZE,
(dst_rect.top + line_y) * TILE_SIZE,
line_len,
raw_text + line_start
);
}
line_start = token_start;
line_text_len = 0;
line_y++;
}
return line_y;
}
+27 -1
View File
@@ -3,12 +3,15 @@
#include "card.h"
#include "game_variables.h"
#include "graphic_utils.h"
#include "joker_gfx.h"
#include "pool.h"
#include "random.h"
#include "soundbank.h"
#include "util.h"
// Tiles and palettes
#include "card_rarity_pal_gfx.h"
#include "joker_gfx.h"
#include <maxmod.h>
#include <stdlib.h>
#include <string.h>
@@ -88,6 +91,11 @@ static int spritesheet_idx_to_starting_joker_id[] = {
0, 18, 20, 22, 27, 32, 36, 40, 42, 44,
45, 46, 47, 48, 49, 50, 51, 52
};
// Lookup table of Joker Rarity strings. Used to display at the bottom of the description screen.
static const char* joker_rarity_strings_lut[MAX_RARITIES] = {
"Common", "Uncommon", "Rare", "Legendary"
};
// clang-format on
static int s_get_num_spritesheets(void);
@@ -152,6 +160,24 @@ u32 joker_get_score_effect(
return jinfo->joker_effect_func(joker, scored_card, joker_event, joker_effect);
}
const char* joker_get_rarity_string(u8 rarity)
{
if (rarity >= MAX_RARITIES)
return NULL;
return joker_rarity_strings_lut[rarity];
}
u16 joker_get_rarity_color(u8 rarity, bool main_color)
{
if (rarity >= MAX_RARITIES)
return 0x0;
// +1 to account for the transparency
// odd indices are the main colors, even ones are the shadows
return card_rarity_pal_gfxPal[1 + 2 * rarity + (main_color ? 0 : 1)];
}
int joker_get_sell_value(const Joker* joker)
{
if (joker == NULL)
+588 -61
View File
@@ -7,10 +7,23 @@
#include "random.h"
#include "util.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MISPRINT_MAX_MULT 23
#define REGISTER_JOKER_DESC_FUNC(joker_desc_name) \
static int joker_desc_name(Joker* joker, Rect dest_rect);
#define REGISTER_JOKER_EFFECT_FUNC(joker_effect_name) \
static u32 joker_effect_name( \
Joker* joker, \
Card* scored_card, \
enum JokerEvent joker_event, \
JokerEffect** joker_effect \
);
#define SCORE_ON_EVENT_ONLY_WITH_CARD(scored_card, restricted_event, checked_event) \
if (checked_event != restricted_event || scored_card == NULL) \
{ \
@@ -24,15 +37,63 @@
static JokerEffect shared_joker_effect = {0};
// Joker Effect functions
// Joker Descriptions
#define REGISTER_JOKER_EFFECT_FUNC(joker_effect_name) \
static u32 joker_effect_name( \
Joker* joker, \
Card* scored_card, \
enum JokerEvent joker_event, \
JokerEffect** joker_effect \
);
REGISTER_JOKER_DESC_FUNC(default_joker_desc)
REGISTER_JOKER_DESC_FUNC(greedy_joker_desc)
REGISTER_JOKER_DESC_FUNC(lusty_joker_desc)
REGISTER_JOKER_DESC_FUNC(wrathful_joker_desc)
REGISTER_JOKER_DESC_FUNC(gluttonous_joker_desc)
REGISTER_JOKER_DESC_FUNC(jolly_joker_desc)
REGISTER_JOKER_DESC_FUNC(zany_joker_desc)
REGISTER_JOKER_DESC_FUNC(mad_joker_desc)
REGISTER_JOKER_DESC_FUNC(crazy_joker_desc)
REGISTER_JOKER_DESC_FUNC(droll_joker_desc)
REGISTER_JOKER_DESC_FUNC(sly_joker_desc)
REGISTER_JOKER_DESC_FUNC(wily_joker_desc)
REGISTER_JOKER_DESC_FUNC(clever_joker_desc)
REGISTER_JOKER_DESC_FUNC(devious_joker_desc)
REGISTER_JOKER_DESC_FUNC(crafty_joker_desc)
REGISTER_JOKER_DESC_FUNC(half_joker_desc)
REGISTER_JOKER_DESC_FUNC(stencil_joker_desc)
REGISTER_JOKER_DESC_FUNC(misprint_joker_desc)
REGISTER_JOKER_DESC_FUNC(walkie_talkie_joker_desc)
REGISTER_JOKER_DESC_FUNC(fibonnaci_joker_desc)
REGISTER_JOKER_DESC_FUNC(banner_joker_desc)
REGISTER_JOKER_DESC_FUNC(mystic_summit_joker_desc)
REGISTER_JOKER_DESC_FUNC(blackboard_joker_desc)
REGISTER_JOKER_DESC_FUNC(blue_joker_desc)
REGISTER_JOKER_DESC_FUNC(raised_fist_joker_desc)
REGISTER_JOKER_DESC_FUNC(reserved_parking_joker_desc)
REGISTER_JOKER_DESC_FUNC(business_card_joker_desc)
REGISTER_JOKER_DESC_FUNC(scholar_joker_desc)
REGISTER_JOKER_DESC_FUNC(scary_face_joker_desc)
REGISTER_JOKER_DESC_FUNC(abstract_joker_desc)
REGISTER_JOKER_DESC_FUNC(bull_joker_desc)
REGISTER_JOKER_DESC_FUNC(smiley_face_joker_desc)
REGISTER_JOKER_DESC_FUNC(even_steven_joker_desc)
REGISTER_JOKER_DESC_FUNC(odd_todd_joker_desc)
REGISTER_JOKER_DESC_FUNC(acrobat_joker_desc)
REGISTER_JOKER_DESC_FUNC(hanging_chad_joker_desc)
REGISTER_JOKER_DESC_FUNC(the_duo_joker_desc)
REGISTER_JOKER_DESC_FUNC(the_trio_joker_desc)
REGISTER_JOKER_DESC_FUNC(the_family_joker_desc)
REGISTER_JOKER_DESC_FUNC(the_order_joker_desc)
REGISTER_JOKER_DESC_FUNC(the_tribe_joker_desc)
REGISTER_JOKER_DESC_FUNC(bootstraps_joker_desc)
REGISTER_JOKER_DESC_FUNC(shoot_the_moon_joker_desc)
REGISTER_JOKER_DESC_FUNC(pareidolia_joker_desc)
REGISTER_JOKER_DESC_FUNC(photograph_joker_desc)
REGISTER_JOKER_DESC_FUNC(dusk_joker_desc)
REGISTER_JOKER_DESC_FUNC(shortcut_joker_desc)
REGISTER_JOKER_DESC_FUNC(blueprint_joker_desc)
REGISTER_JOKER_DESC_FUNC(brainstorm_joker_desc)
REGISTER_JOKER_DESC_FUNC(hack_joker_desc)
REGISTER_JOKER_DESC_FUNC(four_fingers_joker_desc)
REGISTER_JOKER_DESC_FUNC(seltzer_joker_desc)
REGISTER_JOKER_DESC_FUNC(sock_and_buskin_joker_desc)
// Joker Effect functions
static u32 sinful_joker_effect(
Card* scored_card,
@@ -94,80 +155,85 @@ REGISTER_JOKER_EFFECT_FUNC(sock_and_buskin_joker_effect)
// clang-format off
/* The index of a joker in the registry matches its ID.
*
* The joker sprites are matched by ID so the position in the registry
* determines the joker's sprite.
*
* Each consecutive NUM_JOKERS_PER_SPRITESHEET (defined in joker.c) jokers
* share a spritesheet and thus a color palette.
*
* To make better use of color palettes jokers may be rearranged here
* (and put together in the matching spritesheet) to share a color palette.
* Otherwise the order is similar to the wiki.
*
* TODO: move Name and Description printing out of this when the CardInstance is implemented.
*/
// clang-format off
const JokerInfo joker_registry[] =
{
// Spritesheet 0
{ COMMON_JOKER, 2, default_joker_effect }, // DEFAULT_JOKER_ID = 0
{ COMMON_JOKER, 4, abstract_joker_effect }, // 1
{ COMMON_JOKER, 5, half_joker_effect }, // 2
{ COMMON_JOKER, 4, misprint_joker_effect }, // 3
{ COMMON_JOKER, 4, scary_face_joker_effect }, // 4
{ UNCOMMON_JOKER, 6, sock_and_buskin_joker_effect }, // 5
{ UNCOMMON_JOKER, 6, acrobat_joker_effect }, // 6
{ UNCOMMON_JOKER, 8, fibonnaci_joker_effect }, // 7
{ COMMON_JOKER, 4, scholar_joker_effect }, // 8
{ COMMON_JOKER, 4, crafty_joker_effect }, // 9
{ COMMON_JOKER, 4, droll_joker_effect }, // 10
{ COMMON_JOKER, 5, raised_fist_joker_effect }, // 11
{ COMMON_JOKER, 6, reserved_parking_joker_effect }, // 12
{ COMMON_JOKER, 4, business_card_joker_effect }, // 13
{ COMMON_JOKER, 4, hanging_chad_joker_effect }, // 14
{ UNCOMMON_JOKER, 8, stencil_joker_effect }, // 15
{ COMMON_JOKER, 5, banner_joker_effect }, // 16
{ COMMON_JOKER, 5, shoot_the_moon_joker_effect, }, // 17
{ "Joker", COMMON_JOKER, 2, false, default_joker_desc, default_joker_effect }, // DEFAULT_JOKER_ID = 0
{ "Abstract Joker", COMMON_JOKER, 4, false, abstract_joker_desc, abstract_joker_effect }, // 1
{ "Half Joker", COMMON_JOKER, 5, false, half_joker_desc, half_joker_effect }, // 2
{ "Misprint", COMMON_JOKER, 4, true, misprint_joker_desc, misprint_joker_effect }, // 3
{ "Scary Face", COMMON_JOKER, 4, false, scary_face_joker_desc, scary_face_joker_effect }, // 4
{ "Sock and Buskin", UNCOMMON_JOKER, 6, false, sock_and_buskin_joker_desc, sock_and_buskin_joker_effect }, // 5
{ "Acrobat", UNCOMMON_JOKER, 6, false, acrobat_joker_desc, acrobat_joker_effect }, // 6
{ "Fibonacci", UNCOMMON_JOKER, 8, false, fibonnaci_joker_desc, fibonnaci_joker_effect }, // 7
{ "Scholar", COMMON_JOKER, 4, false, scholar_joker_desc, scholar_joker_effect }, // 8
{ "Crafty Joker", COMMON_JOKER, 4, false, crafty_joker_desc, crafty_joker_effect }, // 9
{ "Droll Joker", COMMON_JOKER, 4, false, droll_joker_desc, droll_joker_effect }, // 10
{ "Raised Fist", COMMON_JOKER, 5, false, raised_fist_joker_desc, raised_fist_joker_effect }, // 11
{ "Reserved Parking", COMMON_JOKER, 6, false, reserved_parking_joker_desc, reserved_parking_joker_effect }, // 12
{ "Business Card", COMMON_JOKER, 4, false, business_card_joker_desc, business_card_joker_effect }, // 13
{ "Hanging Chad", COMMON_JOKER, 4, false, hanging_chad_joker_desc, hanging_chad_joker_effect }, // 14
{ "Joker Stencil", UNCOMMON_JOKER, 8, false, stencil_joker_desc, stencil_joker_effect }, // 15
{ "Banner", COMMON_JOKER, 5, false, banner_joker_desc, banner_joker_effect }, // 16
{ "Shoot the Moon", COMMON_JOKER, 5, false, shoot_the_moon_joker_desc, shoot_the_moon_joker_effect, }, // 17
// Spritesheet 1
{ COMMON_JOKER, 5, greedy_joker_effect }, // 18
{ COMMON_JOKER, 5, lusty_joker_effect }, // 19
{ "Greedy Joker", COMMON_JOKER, 5, false, greedy_joker_desc, greedy_joker_effect }, // 18
{ "Lusty Joker", COMMON_JOKER, 5, false, lusty_joker_desc, lusty_joker_effect }, // 19
// Spritesheet 2
{ COMMON_JOKER, 5, wrathful_joker_effect }, // 20
{ COMMON_JOKER, 5, gluttonous_joker_effect }, // 21
{ "Wrathful Joker", COMMON_JOKER, 5, false, wrathful_joker_desc, wrathful_joker_effect }, // 20
{ "Gluttonous Joker", COMMON_JOKER, 5, false, gluttonous_joker_desc, gluttonous_joker_effect }, // 21
// Spritesheet 3
{ COMMON_JOKER, 4, crazy_joker_effect }, // 22
{ COMMON_JOKER, 4, mad_joker_effect }, // 23
{ COMMON_JOKER, 4, clever_joker_effect }, // 24
{ COMMON_JOKER, 4, devious_joker_effect }, // 25
{ COMMON_JOKER, 4, even_steven_joker_effect }, // 26
{ "Crazy Joker", COMMON_JOKER, 4, false, crazy_joker_desc, crazy_joker_effect }, // 22
{ "Mad Joker", COMMON_JOKER, 4, false, mad_joker_desc, mad_joker_effect }, // 23
{ "Clever Joker", COMMON_JOKER, 4, false, clever_joker_desc, clever_joker_effect }, // 24
{ "Devious Joker", COMMON_JOKER, 4, false, devious_joker_desc, devious_joker_effect }, // 25
{ "Even Steven", COMMON_JOKER, 4, false, even_steven_joker_desc, even_steven_joker_effect }, // 26
// Spritesheet 4
{ UNCOMMON_JOKER, 6, blackboard_joker_effect }, // 27
{ COMMON_JOKER, 5, mystic_summit_joker_effect }, // 28
{ COMMON_JOKER, 4, walkie_talkie_joker_effect }, // 29
{ COMMON_JOKER, 4, zany_joker_effect }, // 30
{ COMMON_JOKER, 4, wily_joker_effect }, // 31
{ "Blackboard", UNCOMMON_JOKER, 6, false, blackboard_joker_desc, blackboard_joker_effect }, // 27
{ "Mystic Summit", COMMON_JOKER, 5, false, mystic_summit_joker_desc, mystic_summit_joker_effect }, // 28
{ "Walkie Talkie", COMMON_JOKER, 4, false, walkie_talkie_joker_desc, walkie_talkie_joker_effect }, // 29
{ "Zany Joker", COMMON_JOKER, 4, false, zany_joker_desc, zany_joker_effect }, // 30
{ "Wily Joker", COMMON_JOKER, 4, false, wily_joker_desc, wily_joker_effect }, // 31
// Spritesheet 5
{ COMMON_JOKER, 3, sly_joker_effect }, // 32
{ COMMON_JOKER, 3, jolly_joker_effect }, // 33
{ COMMON_JOKER, 5, blue_joker_effect }, // 34
{ COMMON_JOKER, 4, odd_todd_joker_effect }, // 35
{ "Sly Joker", COMMON_JOKER, 3, false, sly_joker_desc, sly_joker_effect }, // 32
{ "Jolly Joker", COMMON_JOKER, 3, false, jolly_joker_desc, jolly_joker_effect }, // 33
{ "Blue Joker", COMMON_JOKER, 5, false, blue_joker_desc, blue_joker_effect }, // 34
{ "Odd Todd", COMMON_JOKER, 4, false, odd_todd_joker_desc, odd_todd_joker_effect }, // 35
// Spritesheet 6
{ RARE_JOKER, 8, the_duo_joker_effect }, // 36
{ RARE_JOKER, 8, the_trio_joker_effect }, // 37
{ RARE_JOKER, 8, the_order_joker_effect }, // 38
{ RARE_JOKER, 8, the_tribe_joker_effect }, // 39
{ "The Duo", RARE_JOKER, 8, false, the_duo_joker_desc, the_duo_joker_effect }, // 36
{ "The Trio", RARE_JOKER, 8, false, the_trio_joker_desc, the_trio_joker_effect }, // 37
{ "The Order", RARE_JOKER, 8, false, the_order_joker_desc, the_order_joker_effect }, // 38
{ "The Tribe", RARE_JOKER, 8, false, the_tribe_joker_desc, the_tribe_joker_effect }, // 39
// Spritesheet 7
{ RARE_JOKER, 8, the_family_joker_effect }, // 40
{ RARE_JOKER, 10, blueprint_brainstorm_joker_effect }, // 41 Brainstorm
{ "The Family", RARE_JOKER, 8, false, the_family_joker_desc, the_family_joker_effect }, // 40
{ "Brainstorm", RARE_JOKER, 10, false, brainstorm_joker_desc, blueprint_brainstorm_joker_effect }, // 41 Brainstorm
// Spritesheet 8
{ COMMON_JOKER, 4, smiley_face_joker_effect }, // 42
{ UNCOMMON_JOKER, 6, bull_joker_effect }, // 43
{ "Smiley Face", COMMON_JOKER, 4, false, smiley_face_joker_desc, smiley_face_joker_effect }, // 42
{ "Bull", UNCOMMON_JOKER, 6, false, bull_joker_desc, bull_joker_effect }, // 43
// Individual Jokers (for now :3)
{ COMMON_JOKER, 5, photograph_joker_effect, }, // 44
{ UNCOMMON_JOKER, 6, hack_joker_effect }, // 45
{ UNCOMMON_JOKER, 5, joker_effect_noop }, // 46 Pareidolia
{ UNCOMMON_JOKER, 7, bootstraps_joker_effect }, // 47
{ UNCOMMON_JOKER, 7, joker_effect_noop, }, // 48 Shortcut
{ UNCOMMON_JOKER, 5, dusk_joker_effect }, // 49
{ UNCOMMON_JOKER, 7, joker_effect_noop, }, // 50 Four Fingers
{ UNCOMMON_JOKER, 6, seltzer_joker_effect, }, // 51
{ RARE_JOKER, 10, blueprint_brainstorm_joker_effect }, // 52 Blueprint
{ "Photograph", COMMON_JOKER, 5, false, photograph_joker_desc, photograph_joker_effect, }, // 44
{ "Hack", UNCOMMON_JOKER, 6, false, hack_joker_desc, hack_joker_effect }, // 45
{ "Pareidolia", UNCOMMON_JOKER, 5, false, pareidolia_joker_desc, joker_effect_noop }, // 46 Pareidolia
{ "Bootstraps", UNCOMMON_JOKER, 7, false, bootstraps_joker_desc, bootstraps_joker_effect }, // 47
{ "Shortcut", UNCOMMON_JOKER, 7, false, shortcut_joker_desc, joker_effect_noop, }, // 48 Shortcut
{ "Dusk", UNCOMMON_JOKER, 5, false, dusk_joker_desc, dusk_joker_effect }, // 49
{ "Four Fingers", UNCOMMON_JOKER, 7, false, four_fingers_joker_desc, joker_effect_noop, }, // 50 Four Fingers
{ "Seltzer", UNCOMMON_JOKER, 6, false, seltzer_joker_desc, seltzer_joker_effect, }, // 51
{ "Blueprint", RARE_JOKER, 10, false, blueprint_joker_desc, blueprint_brainstorm_joker_effect }, // 52 Blueprint
// The following jokers don't have sprites yet,
// uncomment them when their sprites are added.
@@ -192,6 +258,465 @@ size_t get_joker_registry_size(void)
return joker_registry_size;
}
#pragma region JOKER DESCRIPTIONS
static int default_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] = TTE_RED_TAG "+4 " TTE_BLACK_TAG "Mult";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int greedy_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLACK_TAG "Played cards with " TTE_DIAMOND_TAG TTE_BLACK_TAG "suit give " TTE_RED_TAG
"+3 " TTE_BLACK_TAG "Mult when scored";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int lusty_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLACK_TAG "Played cards with " TTE_HEART_TAG TTE_BLACK_TAG "suit give " TTE_RED_TAG
"+3 " TTE_BLACK_TAG "Mult when scored";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int wrathful_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLACK_TAG "Played cards with " TTE_SPADE_TAG TTE_BLACK_TAG "suit give " TTE_RED_TAG
"+3 " TTE_BLACK_TAG "Mult when scored";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int gluttonous_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLACK_TAG "Played cards with " TTE_CLUB_TAG TTE_BLACK_TAG "suit give " TTE_RED_TAG
"+3 " TTE_BLACK_TAG "Mult when scored";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int jolly_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_RED_TAG "+8 " TTE_BLACK_TAG "Mult if played hand contains a " TTE_YELLOW_TAG "Pair";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int zany_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] = TTE_RED_TAG
"+12 " TTE_BLACK_TAG "Mult if played hand contains a " TTE_YELLOW_TAG "Three of a Kind";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int mad_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] = TTE_RED_TAG
"+10 " TTE_BLACK_TAG "Mult if played hand contains a " TTE_YELLOW_TAG "Two Pair";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int crazy_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] = TTE_RED_TAG
"+12 " TTE_BLACK_TAG "Mult if played hand contains a " TTE_YELLOW_TAG "Straight";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int droll_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_RED_TAG "+10 " TTE_BLACK_TAG "Mult if played hand contains a " TTE_YELLOW_TAG "Flush";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int sly_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLUE_TAG "+50 " TTE_BLACK_TAG "Chips if played hand contains a " TTE_YELLOW_TAG "Pair";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int wily_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] = TTE_BLUE_TAG
"+100 " TTE_BLACK_TAG "Chips if played hand contains a " TTE_YELLOW_TAG "Three of a Kind";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int clever_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] = TTE_BLUE_TAG
"+80 " TTE_BLACK_TAG "Chips if played hand contains a " TTE_YELLOW_TAG "Two Pair";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int devious_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] = TTE_BLUE_TAG
"+100 " TTE_BLACK_TAG "Chips if played hand contains a " TTE_YELLOW_TAG "Straight";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int crafty_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLUE_TAG "+80 " TTE_BLACK_TAG "Chips if played hand contains a " TTE_YELLOW_TAG "Flush";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int half_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_RED_TAG "+20 " TTE_BLACK_TAG "Mult if played hand contains " TTE_YELLOW_TAG
"3 " TTE_BLACK_TAG "or fewer cards";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int stencil_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc_format[] =
TTE_RED_TAG "X1 " TTE_BLACK_TAG
"Mult for each empty Joker slot Joker Stencil included\n\n(Now " TTE_RED_TAG
"X%ld " TTE_BLACK_TAG "Mult)";
const u32 desc_max_size = 130;
List* jokers = get_jokers_list();
u32 stencil_bonus = MAX_JOKERS_HELD_SIZE - list_get_len(jokers);
ListItr itr = list_itr_create(jokers);
JokerObject* joker_object;
while ((joker_object = list_itr_next(&itr)))
{
if (joker_object->joker->id == STENCIL_JOKER_ID)
stencil_bonus++;
}
char desc[desc_max_size];
snprintf(desc, desc_max_size, desc_format, stencil_bonus);
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int misprint_joker_desc(Joker* joker, Rect dest_rect)
{
// TODO: print glitchy desc with occasional next card reveal
char desc[] = TTE_YELLOW_TAG "Random" TTE_BLACK_TAG " Mult between " TTE_RED_TAG
"+0" TTE_BLACK_TAG " and " TTE_RED_TAG "+23";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int walkie_talkie_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLACK_TAG "Each played " TTE_YELLOW_TAG "10 " TTE_BLACK_TAG "or " TTE_YELLOW_TAG
"4 " TTE_BLACK_TAG "gives " TTE_BLUE_TAG "+10 " TTE_BLACK_TAG
"Chips and " TTE_RED_TAG "+4 " TTE_BLACK_TAG "Mult when scored";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int fibonnaci_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLACK_TAG "Each played\n" TTE_YELLOW_TAG "Ace" TTE_BLACK_TAG ", " TTE_YELLOW_TAG
"2" TTE_BLACK_TAG ", " TTE_YELLOW_TAG "3" TTE_BLACK_TAG ", " TTE_YELLOW_TAG
"5" TTE_BLACK_TAG ", " TTE_YELLOW_TAG "8\n" TTE_BLACK_TAG "gives " TTE_RED_TAG
"+8 " TTE_BLACK_TAG "Mult when scored";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int banner_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLUE_TAG "+30 " TTE_BLACK_TAG "Chips for each remaining " TTE_YELLOW_TAG "discard";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int mystic_summit_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] = TTE_RED_TAG "+15 " TTE_BLACK_TAG "Mult when " TTE_YELLOW_TAG
"0 " TTE_BLACK_TAG "discards remaining ";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int blackboard_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] = TTE_RED_TAG
"X3 " TTE_BLACK_TAG "Mult if all cards held in hand are " TTE_SPADE_TAG TTE_BLACK_TAG
"or " TTE_CLUB_TAG;
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int blue_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc_format[] =
TTE_BLUE_TAG "+2 " TTE_BLACK_TAG "Chips for each remaining card in " TTE_YELLOW_TAG
"deck" TTE_BLACK_TAG "\n\n(Now " TTE_BLUE_TAG "+%ld" TTE_BLACK_TAG " Chips)";
const u32 desc_max_size = 139;
u32 blue_bonus = (get_deck_top() + 1) * 2;
char desc[desc_max_size];
snprintf(desc, desc_max_size, desc_format, blue_bonus);
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int raised_fist_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLACK_TAG "Adds " TTE_YELLOW_TAG "double" TTE_BLACK_TAG " the rank of " TTE_YELLOW_TAG
"lowest" TTE_BLACK_TAG " ranked card held in hand to Mult";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int reserved_parking_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] = TTE_BLACK_TAG
"Each " TTE_YELLOW_TAG "face" TTE_BLACK_TAG " card held in hand has a " TTE_GREEN_TAG
"1 in 2" TTE_BLACK_TAG " chance to give " TTE_YELLOW_TAG "$1";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int business_card_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] = TTE_BLACK_TAG
"Played " TTE_YELLOW_TAG "face" TTE_BLACK_TAG " cards have a " TTE_GREEN_TAG
"1 in 2" TTE_BLACK_TAG " chance to give " TTE_YELLOW_TAG "$2" TTE_BLACK_TAG " when scored";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int scholar_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] = TTE_BLACK_TAG
"Played " TTE_YELLOW_TAG "Aces" TTE_BLACK_TAG " give " TTE_BLUE_TAG "+20" TTE_BLACK_TAG
" Chips and " TTE_RED_TAG "+4" TTE_BLACK_TAG " Mult when scored";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int scary_face_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLACK_TAG "Played " TTE_YELLOW_TAG "face" TTE_BLACK_TAG " cards give " TTE_BLUE_TAG
"+30" TTE_BLACK_TAG " Chips when scored";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int abstract_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc_format[] =
TTE_RED_TAG "+3" TTE_BLACK_TAG " Mult for each " TTE_YELLOW_TAG "Joker" TTE_BLACK_TAG
" card\n\n(Now " TTE_RED_TAG "+%ld" TTE_BLACK_TAG " Mult)";
const u32 desc_max_size = 125;
u32 abstract_bonus = list_get_len(get_jokers_list()) * 3;
char desc[desc_max_size];
snprintf(desc, desc_max_size, desc_format, abstract_bonus);
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int bull_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc_format[] =
TTE_BLUE_TAG "+2" TTE_BLACK_TAG " Chips for each " TTE_YELLOW_TAG "$1" TTE_BLACK_TAG
" you have\n\n(Now " TTE_BLUE_TAG "+%ld" TTE_BLACK_TAG " Chips)";
const u32 desc_max_size = 127;
u32 bull_bonus = (g_game_vars.money > 0) ? g_game_vars.money * 2 : 0;
char desc[desc_max_size];
snprintf(desc, desc_max_size, desc_format, bull_bonus);
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int smiley_face_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLACK_TAG "Played " TTE_YELLOW_TAG "face" TTE_BLACK_TAG " cards give " TTE_RED_TAG
"+5" TTE_BLACK_TAG " Mult when scored";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int even_steven_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLACK_TAG "Played cards with " TTE_YELLOW_TAG "even rank give " TTE_RED_TAG
"+4" TTE_BLACK_TAG " Mult when scored\n(10, 8, 6, 4, 2)";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int odd_todd_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLACK_TAG "Played cards with " TTE_YELLOW_TAG "odd rank give " TTE_BLUE_TAG
"+31" TTE_BLACK_TAG " Chips when scored\n(A, 9, 7, 5, 3)";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int acrobat_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] = TTE_RED_TAG "X3" TTE_BLACK_TAG " Mult on " TTE_YELLOW_TAG
"final hand" TTE_BLACK_TAG " of round";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int hanging_chad_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] = TTE_BLACK_TAG "Retrigger " TTE_YELLOW_TAG "first" TTE_BLACK_TAG
" played card used in scoring " TTE_YELLOW_TAG
"2" TTE_BLACK_TAG " additional times ";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int the_duo_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_RED_TAG "X2" TTE_BLACK_TAG " Mult if played hand contains a " TTE_YELLOW_TAG "Pair";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int the_trio_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] = TTE_RED_TAG
"X3" TTE_BLACK_TAG " Mult if played hand contains a " TTE_YELLOW_TAG "Three of a Kind";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int the_family_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] = TTE_RED_TAG
"X4" TTE_BLACK_TAG " Mult if played hand contains a " TTE_YELLOW_TAG "Four of a Kind";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int the_order_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_RED_TAG "X3" TTE_BLACK_TAG " Mult if played hand contains a " TTE_YELLOW_TAG "Straight";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int the_tribe_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_RED_TAG "X2" TTE_BLACK_TAG " Mult if played hand contains a " TTE_YELLOW_TAG "Flush";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int bootstraps_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc_format[] =
TTE_RED_TAG "+2" TTE_BLACK_TAG " Mult for every " TTE_YELLOW_TAG "$5" TTE_BLACK_TAG
" you have\n\n(Now " TTE_RED_TAG "+%ld" TTE_BLACK_TAG " Mult)";
const u32 desc_max_size = 125;
u32 bootstrap_bonus = (g_game_vars.money > 0) ? (g_game_vars.money / 5) * 2 : 0;
char desc[desc_max_size];
snprintf(desc, desc_max_size, desc_format, bootstrap_bonus);
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int shoot_the_moon_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLACK_TAG "Each " TTE_YELLOW_TAG "Queen" TTE_BLACK_TAG
" held in hand gives " TTE_RED_TAG "+13" TTE_BLACK_TAG " Mult";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int photograph_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLACK_TAG "First played " TTE_YELLOW_TAG "face" TTE_BLACK_TAG " card gives " TTE_RED_TAG
"X2" TTE_BLACK_TAG " Mult when scored";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int dusk_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] = TTE_BLACK_TAG "Retrigger all played cards in " TTE_YELLOW_TAG
"final hand" TTE_BLACK_TAG " of the round";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int blueprint_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLACK_TAG "Copies ability of the leftmost " TTE_YELLOW_TAG "Joker";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int brainstorm_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLACK_TAG "Copies ability of " TTE_YELLOW_TAG "Joker" TTE_BLACK_TAG " to the right";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int hack_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] = TTE_BLACK_TAG
"Retrigger each played " TTE_YELLOW_TAG "2" TTE_BLACK_TAG ", " TTE_YELLOW_TAG
"3" TTE_BLACK_TAG ", " TTE_YELLOW_TAG "4" TTE_BLACK_TAG ", or " TTE_YELLOW_TAG "5";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int seltzer_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc_format[] = TTE_BLACK_TAG
"Retrigger all cards played for the next " TTE_YELLOW_TAG "%ld" TTE_BLACK_TAG " hands";
const u32 desc_max_size = 94;
char desc[desc_max_size];
snprintf(desc, desc_max_size, desc_format, joker->persistent_state);
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int sock_and_buskin_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLACK_TAG "Retrigger all played " TTE_YELLOW_TAG "face" TTE_BLACK_TAG " cards";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int pareidolia_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLACK_TAG "All cards are considered " TTE_YELLOW_TAG "face" TTE_BLACK_TAG " cards";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int shortcut_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLACK_TAG "Allows " TTE_YELLOW_TAG "Straights" TTE_BLACK_TAG
" to be made with gaps of " TTE_YELLOW_TAG "1 rank" TTE_BLACK_TAG
"\n\n(ex: " TTE_YELLOW_TAG "10 8 6 5 3" TTE_BLACK_TAG ")";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
static int four_fingers_joker_desc(Joker* joker, Rect dest_rect)
{
static const char desc[] =
TTE_BLACK_TAG "All " TTE_YELLOW_TAG "Flushes" TTE_BLACK_TAG " and " TTE_YELLOW_TAG
"Straights" TTE_BLACK_TAG " can be made with 4 cards";
return tte_printf_justified_in_rect(desc, dest_rect, JUSTIFY_CENTER, SCREEN_LEFT, true);
}
#pragma endregion
#pragma region JOKER EFFECTS
static u32 joker_effect_noop(
Joker* joker,
Card* scored_card,
@@ -1505,3 +2030,5 @@ static u32 sock_and_buskin_joker_effect(
return effect_flags_ret;
}
#pragma endregion
+4 -5
View File
@@ -17,6 +17,7 @@
// Graphics
#include "affine_background_gfx.h"
#include "background_gfx.h"
#include "graphic_utils.h"
// Audio
#include "audio_utils.h"
@@ -52,11 +53,9 @@ void init()
tte_erase_screen();
tte_init_con();
// TTE palette setup
pal_bg_bank[TTE_YELLOW_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_YELLOW;
pal_bg_bank[TTE_BLUE_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_BLUE;
pal_bg_bank[TTE_RED_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_RED;
pal_bg_bank[TTE_WHITE_PB][TTE_BIT_ON_CLR_IDX] = TEXT_CLR_WHITE;
// Will need to be called when starting the game, as some colors will have
// been overwritten by the logo and its whopping 10 palettes
tte_colors_setup();
// Set up the video mode
// BG0 is the TTE text layer
+8 -5
View File
@@ -15,14 +15,16 @@ void splash_screen_on_init(void)
{
timer = 0;
tte_printf("#{P:72,8; cx:0xF000}DISCLAIMER");
tte_printf("#{P:72,8; cx:0x%X000}DISCLAIMER", TTE_WHITE_PB);
tte_printf(
"#{P:8,24; cx:0xF000}This project is NOT endorsed \n by or affiliated with \n Playstack or "
"#{P:8,24; cx:0x%X000}This project is NOT endorsed \n by or affiliated with \n Playstack "
"or "
"LocalThunk.\n\n If you have paid for this, \n you have been scammed \n and should request "
"a refund \n IMMEDIATELY. \n\n The only official place \n to obtain this is from: \n\n "
"'github.com/\n GBALATRO/balatro-gba'"
"'github.com/\n GBALATRO/balatro-gba'",
TTE_WHITE_PB
);
tte_printf("#{P:8,144; cx:0xF000}(Press any key to skip)");
tte_printf("#{P:8,144; cx:0x%X000}(Press any key to skip)", TTE_WHITE_PB);
}
void splash_screen_on_update(void)
@@ -33,9 +35,10 @@ void splash_screen_on_update(void)
{
tte_erase_rect_wrapper(COUNTDOWN_TIMER_RECT);
tte_printf(
"#{P:%d,%d; cx:0xF000}%d",
"#{P:%d,%d; cx:0x%X000}%d",
COUNTDOWN_TIMER_RECT.left,
COUNTDOWN_TIMER_RECT.top,
TTE_WHITE_PB,
1 + (SPLASH_DURATION_FRAMES - timer) / SPLASH_FPS
);