15#include "card_rarity_pal_gfx.h"
24#define JOKER_SCORE_TEXT_Y 48
25#define HELD_CARD_SCORE_TEXT_Y 108
26#define MAX_CARD_SCORE_STR_LEN 2
28#define MAX_NUM_JOKERS_SPRITESHEETS 75
30static const unsigned int* joker_gfxTiles[] = {
31#define DEF_JOKER_GFX(idx) joker_gfx##idx##Tiles,
32#include "../include/def_joker_gfx_table.h"
35static const unsigned short* joker_gfxPal[] = {
36#define DEF_JOKER_GFX(idx) joker_gfx##idx##Pal,
37#include "def_joker_gfx_table.h"
42const static u8 EDITION_PRICE_LUT[MAX_EDITIONS] = {
57static bool s_used_layers[MAX_JOKER_OBJECTS] = {
false};
62static int s_joker_spritesheet_pb_map[MAX_NUM_JOKERS_SPRITESHEETS];
63static int s_joker_pb_num_sprite_users[JOKER_LAST_PB - JOKER_BASE_PB + 1] = {0};
72static const int JOKER_ID_TO_SPRITE_MAP[] = {
73 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
97static const int SPRITESHEET_IDX_TO_STARTING_JOKER_ID[] = {
98 0, 18, 20, 22, 27, 32, 36, 40, 42, 44,
99 45, 46, 47, 48, 49, 50, 51, 52
103static const char* JOKER_RARITY_STRINGS_LUT[MAX_RARITIES] = {
104 "Common",
"Uncommon",
"Rare",
"Legendary"
108static int s_get_num_spritesheets(
void);
109static int s_joker_get_spritesheet_idx(u8 joker_id);
110static int s_joker_get_sprite_idx_in_sheet(u8 joker_id,
int spritesheet_idx);
111static void s_joker_pb_add_sprite_user(
int pb);
112static void s_joker_pb_remove_sprite_user(
int pb);
113static int s_joker_pb_get_num_sprite_users(
int joker_pb);
114static int s_get_unused_joker_pb(
void);
115static int s_allocate_pb_if_needed(u8 joker_id);
116static int joker_get_random_rarity(
enum RngSequence key);
121 int num_spritesheets = s_get_num_spritesheets();
123 for (
int i = 0; i < num_spritesheets; i++)
125 s_joker_spritesheet_pb_map[i] = UNDEFINED;
129Joker* joker_new(u8
id)
131 if (
id >= get_joker_registry_size())
135 const JokerInfo* jinfo = get_joker_registry_entry(
id);
138 joker->modifier = BASE_EDITION;
139 joker->value = jinfo->base_value + EDITION_PRICE_LUT[joker->modifier];
140 joker->rarity = jinfo->rarity;
141 joker->scoring_state = 0;
142 joker->persistent_state = 0;
146 jinfo->joker_effect_func(joker, NULL, JOKER_EVENT_ON_JOKER_CREATED, &joker_effect);
151void joker_destroy(
Joker** joker)
153 POOL_FREE(
Joker, *joker);
157u32 joker_get_score_effect(
160 enum JokerEvent joker_event,
164 const JokerInfo* jinfo = get_joker_registry_entry(joker->id);
166 return JOKER_EFFECT_FLAG_NONE;
168 return jinfo->joker_effect_func(joker, scored_card, joker_event, joker_effect);
171const char* joker_get_rarity_string(u8 rarity)
173 if (rarity >= MAX_RARITIES)
176 return JOKER_RARITY_STRINGS_LUT[rarity];
181 if (rarity >= MAX_RARITIES)
186 return card_rarity_pal_gfxPal[1 + 2 * rarity + (main_color ? 0 : 1)];
189int joker_get_buy_price(
const Joker* joker)
196int joker_get_sell_value(
const Joker* joker)
200 return joker->value / 2;
211 for (
int i = 0; i < MAX_JOKER_OBJECTS; i++)
213 if (!s_used_layers[i])
216 s_used_layers[i] =
true;
221 joker_object->joker = joker;
223 joker_object->type = ITEM_TYPE_JOKER;
225 int tile_index = JOKER_TID + layer * JOKER_SPRITE_OFFSET;
227 int joker_spritesheet_idx = s_joker_get_spritesheet_idx(joker->id);
228 int joker_idx = s_joker_get_sprite_idx_in_sheet(joker->id, joker_spritesheet_idx);
229 int joker_pb = s_allocate_pb_if_needed(joker->id);
230 s_joker_pb_add_sprite_user(joker_pb);
234 &joker_gfxTiles[joker_spritesheet_idx][joker_idx *
TILE_SIZE * JOKER_SPRITE_OFFSET],
241 ATTR0_SQUARE | ATTR0_4BPP | ATTR0_AFF,
245 JOKER_STARTING_LAYER + layer
252void joker_object_destroy(
JokerObject** joker_object)
254 if (joker_object == NULL || *joker_object == NULL)
257 s16 layer =
sprite_get_layer(joker_object_get_sprite(*joker_object)) - JOKER_STARTING_LAYER;
258 s_used_layers[layer] =
false;
259 s_joker_pb_remove_sprite_user(
sprite_get_pb(joker_object_get_sprite(*joker_object)));
260 if (s_joker_pb_get_num_sprite_users((
sprite_get_pb(joker_object_get_sprite(*joker_object)))) ==
263 s_joker_spritesheet_pb_map[s_joker_get_spritesheet_idx((*joker_object)->joker->id)] =
268 joker_destroy(&(*joker_object)->joker);
270 *joker_object = NULL;
284 joker_object_destroy(&joker_object);
285 *joker_object_item = NULL;
288void joker_object_shake(
JokerObject* joker_object, mm_word sound_id)
306 joker_object->ty = int2fx(HELD_JOKERS_POS.y);
319static inline int get_num_rollable_jokers(
void)
327static inline bool no_rollable_jokers(
void)
333static inline bool joker_is_rollable(
int joker_id)
340 int num_jokers = get_joker_registry_size();
343 for (
int i = 0; i < num_jokers; i++)
355 int jokers_avail_size = get_num_rollable_jokers();
357 if (jokers_avail_size == 0)
361 int joker_rarity = joker_get_random_rarity(key);
363 int matching_joker_ids[jokers_avail_size];
364 int fallback_random_idx =
rng_get_u32(key) % jokers_avail_size;
365 int fallback_random_joker_id = UNDEFINED;
371 int joker_id = UNDEFINED;
374 if (i++ == fallback_random_idx)
375 fallback_random_joker_id = joker_id;
376 const JokerInfo* info = get_joker_registry_entry(joker_id);
377 if (info->rarity == joker_rarity)
379 matching_joker_ids[match_count++] = joker_id;
383 int selected_joker_id = (match_count > 0) ? matching_joker_ids[
rng_get_u32(key) % match_count]
384 : fallback_random_joker_id;
386 return selected_joker_id;
391 if (no_rollable_jokers())
396 if (joker_is_rollable(TEST_JOKER_ID0))
398 joker_id = TEST_JOKER_ID0;
403 if (joker_is_rollable(TEST_JOKER_ID1))
405 joker_id = TEST_JOKER_ID1;
410 joker_id = joker_roll_id(key);
414 if (joker_id == UNDEFINED)
419 return (
Item*)joker_object_new(joker_new(joker_id));
428static inline int joker_get_random_rarity(
enum RngSequence key)
430 int joker_rarity = 0;
432 if (rarity_roll < COMMON_JOKER_CHANCE)
434 joker_rarity = COMMON_JOKER;
436 else if (rarity_roll < COMMON_JOKER_CHANCE + UNCOMMON_JOKER_CHANCE)
438 joker_rarity = UNCOMMON_JOKER;
440 else if (rarity_roll < COMMON_JOKER_CHANCE + UNCOMMON_JOKER_CHANCE + RARE_JOKER_CHANCE)
442 joker_rarity = RARE_JOKER;
444 else if (rarity_roll < COMMON_JOKER_CHANCE + UNCOMMON_JOKER_CHANCE + RARE_JOKER_CHANCE +
445 LEGENDARY_JOKER_CHANCE)
447 joker_rarity = LEGENDARY_JOKER;
453static void set_and_shift_text(
char* str,
int* cursor_pos_x,
int* cursor_pos_y,
int color_pb)
455 tte_set_pos(*cursor_pos_x, *cursor_pos_y);
456 tte_set_special(color_pb * TTE_SPECIAL_PB_MULT_OFFSET);
460 const int joker_score_display_offset_px = (MAX_CARD_SCORE_STR_LEN + 1) *
TTE_CHAR_SIZE;
461 *cursor_pos_x += joker_score_display_offset_px;
464bool joker_object_score(
467 enum JokerEvent joker_event
470 if (joker_object == NULL)
476 u32 effect_flags_ret =
477 joker_get_score_effect(joker_object->joker, card_object->card, joker_event, &joker_effect);
479 if (effect_flags_ret == JOKER_EFFECT_FLAG_NONE)
484 if (effect_flags_ret & JOKER_EFFECT_FLAG_RETRIGGER)
493 if (joker_event == JOKER_EVENT_ON_CARD_HELD)
497 cursorPosX += fx2int(card_object->x);
498 cursorPosY = HELD_CARD_SCORE_TEXT_Y;
502 cursorPosX += fx2int(joker_object->x);
503 cursorPosY = JOKER_SCORE_TEXT_Y;
507 if (effect_flags_ret & JOKER_EFFECT_FLAG_CHIPS)
510 char score_buffer[INT_MAX_DIGITS + 2];
511 snprintf(score_buffer,
sizeof(score_buffer),
"+%lu", joker_effect->chips);
512 set_and_shift_text(score_buffer, &cursorPosX, &cursorPosY, TTE_BLUE_PB);
513 sfx_id = SFX_CHIPS_GENERIC;
515 if (effect_flags_ret & JOKER_EFFECT_FLAG_MULT)
518 char score_buffer[INT_MAX_DIGITS + 2];
519 snprintf(score_buffer,
sizeof(score_buffer),
"+%lu", joker_effect->mult);
520 set_and_shift_text(score_buffer, &cursorPosX, &cursorPosY, TTE_RED_PB);
524 if (effect_flags_ret & JOKER_EFFECT_FLAG_XMULT && joker_effect->xmult > 0)
527 char score_buffer[INT_MAX_DIGITS + 2];
528 snprintf(score_buffer,
sizeof(score_buffer),
"X%lu", joker_effect->xmult);
529 set_and_shift_text(score_buffer, &cursorPosX, &cursorPosY, TTE_RED_PB);
532 if (effect_flags_ret & JOKER_EFFECT_FLAG_MONEY)
534 g_game_vars.money += joker_effect->money;
535 char score_buffer[INT_MAX_DIGITS + 2];
536 snprintf(score_buffer,
sizeof(score_buffer),
"%d$", joker_effect->money);
537 set_and_shift_text(score_buffer, &cursorPosX, &cursorPosY, TTE_YELLOW_PB);
542 if (effect_flags_ret & JOKER_EFFECT_FLAG_MESSAGE)
544 set_and_shift_text(joker_effect->message, &cursorPosX, &cursorPosY, TTE_WHITE_PB);
547 if (effect_flags_ret & JOKER_EFFECT_FLAG_EXPIRE && joker_effect->expire)
549 joker_object_shake(joker_object, UNDEFINED);
558 joker_object_shake(joker_object, sfx_id);
565 if (joker_object == NULL)
570static int s_get_num_spritesheets()
572 return MAX_NUM_JOKERS_SPRITESHEETS;
575static int s_joker_get_spritesheet_idx(u8 joker_id)
577 return JOKER_ID_TO_SPRITE_MAP[joker_id];
580static int s_joker_get_sprite_idx_in_sheet(u8 joker_id,
int spritesheet_idx)
582 return joker_id - SPRITESHEET_IDX_TO_STARTING_JOKER_ID[JOKER_ID_TO_SPRITE_MAP[joker_id]];
585static void s_joker_pb_add_sprite_user(
int pb)
587 s_joker_pb_num_sprite_users[pb - JOKER_BASE_PB]++;
590static void s_joker_pb_remove_sprite_user(
int pb)
592 int num_sprite_users = s_joker_pb_num_sprite_users[pb - JOKER_BASE_PB];
593 s_joker_pb_num_sprite_users[pb - JOKER_BASE_PB] = max(0, num_sprite_users - 1);
596static int s_joker_pb_get_num_sprite_users(
int joker_pb)
598 return s_joker_pb_num_sprite_users[joker_pb - JOKER_BASE_PB];
601static int s_get_unused_joker_pb()
605 if (s_joker_pb_num_sprite_users[i] == 0)
607 return (i + JOKER_BASE_PB);
614static int s_allocate_pb_if_needed(u8 joker_id)
616 int joker_spritesheet_idx = s_joker_get_spritesheet_idx(joker_id);
617 int joker_pb = s_joker_spritesheet_pb_map[joker_spritesheet_idx];
618 if (joker_pb != UNDEFINED)
625 joker_pb = s_get_unused_joker_pb();
627 if (joker_pb == UNDEFINED)
630 joker_pb = JOKER_BASE_PB;
634 s_joker_spritesheet_pb_map[joker_spritesheet_idx] = joker_pb;
636 &pal_obj_mem[PAL_ROW_LEN * joker_pb],
637 joker_gfxPal[joker_spritesheet_idx],
bool bitset_get_idx(Bitset *bitset, int idx)
Get the value of a flag.
bool bitset_is_empty(Bitset *bitset)
Check if a bitset is empty (all 0's)
void bitset_clear(Bitset *bitset)
Clear the bitset, all to 0.
int bitset_num_set_bits(Bitset *bitset)
Count how many bits are set to 1 in a bitset.
#define BITSET_DEFINE(name, capacity)
Make a standard bitset.
BitsetItr bitset_itr_create(const Bitset *bitset)
Declare a BitsetItr.
int bitset_itr_next(BitsetItr *itr)
Get the index of the next set bit in the bitset from a BitsetItr.
void bitset_set_idx(Bitset *bitset, int idx, bool on)
Set a flag in a bitset to a value.
Game global game variables struct definition.
Graphic utility functions.
#define TILE_SIZE
Tile size in pixels, both height and width as tiles are square.
#define TILE_MEM_OBJ_CHARBLOCK0_IDX
The index for the first charblock of the object memory in tonc's tile_mem.
#define TTE_CHAR_SIZE
By default TTE characters occupy a single tile.
The core structure for items in the shop and inventory. Provides a common API for the shop and invent...
#define ITEM_RETURN_IF_UNEXPECTED_TYPE_RET(item, expected_type, ret_val)
Checks if item is of type expected_type - logs error message and returns otherwise.
#define ITEM_RETURN_IF_UNEXPECTED_TYPE_VOID(item, expected_type)
Checks if item is of type expected_type - logs error message and returns otherwise.
Functions relative to the handling of Jokers.
void joker_object_dispose(Item **joker_object)
Destroy a JokerObject item, free its resources, and make it available to be rolled.
u16 joker_get_rarity_color(u8 rarity, bool main_color)
Get Joker rarity panel color.
Item * joker_object_roll_new(enum RngSequence key)
Roll and create a new JokerObject item.
int joker_object_get_buy_price(Item *joker_object)
Returns the buy price of the joker object.
void joker_object_add_to_owned(Item *joker_object)
Add a Joker to the list of owned Jokers and place it in the joker row.
void joker_reset_rollable_jokers(void)
Reset rollable jokers to include all jokers in the registry.
void joker_set_rollable(int joker_id, bool rollable)
Set whether a Joker is available to be rolled for the shop, packs, etc.
Header of shared rects and points needed for ui.
void list_push_back(List *list, void *data)
Interface to interact with the mgba logger.
Common functions to handle RNG manipulation. Using this interface has three goals:
u32 rng_get_u32(enum RngSequence key)
Get the next "randomly" generated number in the sequence corresponding to the given type.
RngSequence
Keys to different independent RNG sequences.
API relative to the Rounds we play.
void set_retrigger(bool new_retrigger)
Set whether the current card should get triggered again.
void sprite_object_init(SpriteObject *sprite_object)
Initialize a SpriteObject to a default state. Must be called only once per SpriteObject when it is cr...
void sprite_object_destroy(SpriteObject *sprite_object)
Destroy SpriteObject.
s16 sprite_get_layer(Sprite *sprite)
Get index of Sprite in the GBA object buffer.
int sprite_get_pb(const Sprite *sprite)
Get the palette bank of a Sprite.
void sprite_object_set_sprite(SpriteObject *sprite_object, Sprite *sprite)
Register a Sprite to an associated SpriteObject.
void sprite_object_shake(SpriteObject *sprite_object, mm_word sound_id)
Shake SpriteObject on screen and play a sound.
Sprite * sprite_new(u16 a0, u16 a1, u32 tid, u32 pb, s16 sprite_index)
Allocate and retrieve a pointer to a valid Sprite.
Sprite * sprite_object_get_sprite(SpriteObject *sprite_object)
Get a SpriteObject's registered Sprite.
An iterator into a Bitset.
A generic interface for all items that can appear in the shop or be in the inventory....
A sprite object is a sprite that is focusable and movable in animation.
Sprite struct for GBA hardware specifics.
Utilities relating around number string representation and protected arithmatic helper functions.
uint32_t u32_protected_add(uint32_t a, uint32_t b)
Avoid overflow when adding two u32 integers.
#define NUM_ELEM_IN_ARR(arr)
Get the number of elements in an array.
#define GBAL_RETURN_IF_NULL_RET(param, ret_val)
Checks if param is equal to NULL and prints error message and returns in case it is....
#define GBAL_RETURN_IF_NULL_VOID(param)
Checks if param is NULL and prints error message and returns in case it is. Useful for checking argum...
#define GBAL_UNUSED
A friendly wrapper around the not so friendly looking attribute syntax for ((unused))
uint32_t u32_protected_mult(uint32_t a, uint32_t b)
Avoid overflow when multiplying two u32 integers.