[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
@@ -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