Clang format joker files and def files (#257)

* Clang format joker files and def files

* Fix blind usage of GBLA_UNUSED macro

* Add final files for clang-format (main.c and selection_grid.h)

---------

Co-authored-by: MeirGavish <meir.gavish@gmail.com>
This commit is contained in:
Rickey
2025-12-09 23:41:24 -08:00
committed by GitHub
parent 35ecbf33ae
commit 2fb9e6d633
12 changed files with 2014 additions and 1403 deletions
+1 -18
View File
@@ -17,21 +17,4 @@ jobs:
uses: actions/checkout@v5
- name: Run clang-format
run: >
clang-format --dry-run -Werror
include/bitset.h source/bitset.c
include/affine_background.h source/affine_background.c
include/audio_utils.h source/audio_utils.c
include/graphic_utils.h source/graphic_utils.c
include/util.h source/util.c
include/pool.h source/pool.c
include/card.h source/card.c
include/game.h source/game.c
include/hand_analysis.h source/hand_analysis.c
include/list.h source/list.c
include/sprite.h source/sprite.c
include/splash_screen.h source/splash_screen.c
include/blind.h source/blind.c
# Currently contains only the code that has been formatted to conform
# When all existing code conforms, change to this:
# run: clang-format --dry-run -Werror include/*.h source/*.c
run: clang-format --dry-run -Werror include/*.h source/*.c
+2 -2
View File
@@ -1,7 +1,7 @@
#include "sprite.h"
#include "joker.h"
#include "card.h"
#include "joker.h"
#include "list.h"
#include "sprite.h"
POOL_ENTRY(Sprite, MAX_SPRITES);
POOL_ENTRY(SpriteObject, MAX_SPRITE_OBJECTS);
+1 -1
View File
@@ -23,4 +23,4 @@ DEF_JOKER_GFX(21)
DEF_JOKER_GFX(22)
DEF_JOKER_GFX(23)
DEF_JOKER_GFX(24)
DEF_JOKER_GFX(25)
DEF_JOKER_GFX(25)
+2
View File
@@ -1,3 +1,4 @@
// clang-format off
// (stateEnum, on_init, on_update, 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, noop)
@@ -7,3 +8,4 @@ DEF_STATE_INFO(GAME_STATE_SHOP, noop, game_shop_on_update, game_shop_on_exit)
DEF_STATE_INFO(GAME_STATE_BLIND_SELECT, noop, game_blind_select_on_update, game_blind_select_on_exit)
DEF_STATE_INFO(GAME_STATE_LOSE, game_lose_on_init, game_lose_on_update, game_over_on_exit)
DEF_STATE_INFO(GAME_STATE_WIN, game_win_on_init, game_win_on_update, game_over_on_exit)
// clang-format on
+84 -54
View File
@@ -1,12 +1,12 @@
#ifndef JOKER_H
#define JOKER_H
#include <maxmod.h>
#include "sprite.h"
#include "card.h"
#include "game.h"
#include "graphic_utils.h"
#include "sprite.h"
#include <maxmod.h>
// This won't be more than the number of jokers in your current deck
// plus the amount that can fit in the shop, 8 should be fine. For now...
@@ -14,51 +14,59 @@
#define MAX_DEFINABLE_JOKERS 150
#define JOKER_TID (MAX_HAND_SIZE + MAX_SELECTION_SIZE) * JOKER_SPRITE_OFFSET // Tile ID for the starting index in the tile memory
// Tile ID for the starting index in the tile memory
#define JOKER_TID (MAX_HAND_SIZE + MAX_SELECTION_SIZE) * JOKER_SPRITE_OFFSET
#define JOKER_SPRITE_OFFSET 16 // Offset for the joker sprites
#define JOKER_BASE_PB 4 // The starting palette index for the jokers
#define JOKER_LAST_PB (NUM_PALETTES - 1)
#define JOKER_BASE_PB 4 // The starting palette index for the jokers
#define JOKER_LAST_PB (NUM_PALETTES - 1)
// 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
// such as planet cards etc.
#define JOKER_STARTING_LAYER 27
#define BASE_EDITION 0
#define FOIL_EDITION 1
#define HOLO_EDITION 2
#define POLY_EDITION 3
#define BASE_EDITION 0
#define FOIL_EDITION 1
#define HOLO_EDITION 2
#define POLY_EDITION 3
#define NEGATIVE_EDITION 4
#define MAX_EDITIONS 5
#define COMMON_JOKER 0
#define UNCOMMON_JOKER 1
#define RARE_JOKER 2
#define COMMON_JOKER 0
#define UNCOMMON_JOKER 1
#define RARE_JOKER 2
#define LEGENDARY_JOKER 3
// 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
#define COMMON_JOKER_CHANCE 70
#define UNCOMMON_JOKER_CHANCE 25
#define RARE_JOKER_CHANCE 5
#define COMMON_JOKER_CHANCE 70
#define UNCOMMON_JOKER_CHANCE 25
#define RARE_JOKER_CHANCE 5
#define LEGENDARY_JOKER_CHANCE 0
// These are the common Joker Events. Special Joker behaviour will be checked on a
// Joker per Joker basis (see if it's there, then do something, e.g. Pareidolia, Baseball Card)
enum JokerEvent
{
JOKER_EVENT_ON_JOKER_CREATED, // Triggers only once when the Joker is created, mainly used for data initialization
JOKER_EVENT_ON_HAND_PLAYED, // Triggers only once when the hand is played and before the cards are scored
JOKER_EVENT_ON_CARD_SCORED, // Triggers when a played card scores (e.g. Walkie Talkie, Fibonnacci...)
JOKER_EVENT_ON_CARD_SCORED_END, // Triggers after the card has finishd scoring (e.g. retrigger Jokers)
JOKER_EVENT_ON_JOKER_CREATED, // Triggers only once when the Joker is created, mainly used for
// data initialization
JOKER_EVENT_ON_HAND_PLAYED, // Triggers only once when the hand is played and before the cards
// are scored
JOKER_EVENT_ON_CARD_SCORED, // Triggers when a played card scores (e.g. Walkie Talkie,
// Fibonnacci...)
JOKER_EVENT_ON_CARD_SCORED_END, // Triggers after the card has finishd scoring (e.g. retrigger
// Jokers)
JOKER_EVENT_ON_CARD_HELD, // Triggers when going through held cards
JOKER_EVENT_INDEPENDENT, // Joker will trigger normally, when Jokers are scored (e.g. base Joker)
JOKER_EVENT_ON_HAND_SCORED_END, // Triggers when entire hand has finished scoring (e.g. food Jokers)
JOKER_EVENT_INDEPENDENT, // Joker will trigger normally, when Jokers are scored (e.g. base
// Joker)
JOKER_EVENT_ON_HAND_SCORED_END, // Triggers when entire hand has finished scoring (e.g. food
// Jokers)
JOKER_EVENT_ON_HAND_DISCARDED, // Triggers when discarding a hand
JOKER_EVENT_ON_ROUND_END, // Triggers at the end of the round (e.g. Rocket)
JOKER_EVENT_ON_BLIND_SELECTED, // Triggers when selecting a blind (e.g. Dagger, Riff Raff, Madness..)
JOKER_EVENT_ON_BLIND_SELECTED, // Triggers when selecting a blind (e.g. Dagger, Riff Raff,
// Madness..)
};
// These are flags that can be combined into a single u32 and returned by
@@ -76,49 +84,59 @@ enum JokerEvent
#define MAX_JOKER_OBJECTS 32 // The maximum number of joker objects that can be created at once
// Jokers in the game
#define DEFAULT_JOKER_ID 0
#define GREEDY_JOKER_ID 1
#define STENCIL_JOKER_ID 16
#define SHORTCUT_JOKER_ID 26
#define PAREIDOLIA_JOKER_ID 30
#define BLUEPRINT_JOKER_ID 39
#define BRAINSTORM_JOKER_ID 40
#define DEFAULT_JOKER_ID 0
#define GREEDY_JOKER_ID 1
#define STENCIL_JOKER_ID 16
#define SHORTCUT_JOKER_ID 26
#define PAREIDOLIA_JOKER_ID 30
#define BLUEPRINT_JOKER_ID 39
#define BRAINSTORM_JOKER_ID 40
#define FOUR_FINGERS_JOKER_ID 48
typedef struct
typedef struct
{
u8 id; // Unique ID for the joker, used to identify different jokers
u8 id; // Unique ID for the joker, used to identify different jokers
u8 modifier; // base, foil, holo, poly, negative
u8 value;
u8 rarity;
// General purpose values that are interpreted differently for each Joker (scaling, last retriggered card, etc...)
// General purpose values that are interpreted differently for each Joker (scaling, last
// retriggered card, etc...)
s32 scoring_state;
s32 persistent_state;
} Joker;
typedef struct JokerObject
{
Joker *joker;
Joker* joker;
SpriteObject* sprite_object;
} JokerObject;
typedef struct // These jokers are triggered after the played hand has finished scoring.
typedef struct // These jokers are triggered after the played hand has finished scoring.
{
u32 chips;
u32 mult;
u32 xmult;
int money;
bool retrigger; // Retrigger played hand (e.g. "Dusk" joker, even though on the wiki it says "On Scored" it makes more sense to have it here)
bool expire; // Will make the Joker expire/destry itself if true (i.e. Bananas and fully consumed Food Jokers)
char* message; // Used to send custom messages e.g. "Extinct!" or "Again!"
bool retrigger; // Retrigger played hand (e.g. "Dusk" joker, even though on the wiki it says "On
// Scored" it makes more sense to have it here)
bool expire; // Will make the Joker expire/destry itself if true (i.e. Bananas and fully
// consumed Food Jokers)
char* message; // Used to send custom messages e.g. "Extinct!" or "Again!"
} JokerEffect;
// JokerEffectFuncs take in a joker that will be scored, a scored_card that is not NULL when related to the given joker_event, and output a joker_effect storing the effects of the scored joker
// They return a set of flags indicating what fields of the joker_effect are valid to access
typedef u32 (*JokerEffectFunc)(Joker *joker, Card *scored_card, enum JokerEvent joker_event, JokerEffect **joker_effect);
// JokerEffectFuncs take in a joker that will be scored, a scored_card that is not NULL when related
// to the given joker_event, and output a joker_effect storing the effects of the scored joker They
// return a set of flags indicating what fields of the joker_effect are valid to access
typedef u32 (*JokerEffectFunc)(
Joker* joker,
Card* scored_card,
enum JokerEvent joker_event,
JokerEffect** joker_effect
);
typedef struct {
typedef struct
{
u8 rarity;
u8 base_value;
JokerEffectFunc joker_effect_func;
@@ -128,23 +146,35 @@ size_t get_joker_registry_size(void);
void joker_init();
Joker *joker_new(u8 id);
void joker_destroy(Joker **joker);
Joker* joker_new(u8 id);
void joker_destroy(Joker** joker);
// Unique effects like "Four Fingers" or "Credit Card" will be hard coded into game.c with a conditional check for the joker ID from the players owned jokers
// game.c should probably be restructured so most of the variables in it are moved to some sort of global variable header file so they can be easily accessed and modified for the jokers
u32 joker_get_score_effect(Joker *joker, Card *scored_card, enum JokerEvent joker_event, JokerEffect **joker_effect);
// Unique effects like "Four Fingers" or "Credit Card" will be hard coded into game.c with a
// conditional check for the joker ID from the players owned jokers game.c should probably be
// restructured so most of the variables in it are moved to some sort of global variable header file
// so they can be easily accessed and modified for the jokers
u32 joker_get_score_effect(
Joker* joker,
Card* scored_card,
enum JokerEvent joker_event,
JokerEffect** joker_effect
);
int joker_get_sell_value(const Joker* joker);
JokerObject *joker_object_new(Joker *joker);
void joker_object_destroy(JokerObject **joker_object);
void joker_object_update(JokerObject *joker_object);
JokerObject* joker_object_new(Joker* joker);
void joker_object_destroy(JokerObject** joker_object);
void joker_object_update(JokerObject* joker_object);
// This doesn't actually score anything, it just performs an animation and plays a sound effect
void joker_object_shake(JokerObject *joker_object, mm_word sound_id);
void joker_object_shake(JokerObject* joker_object, mm_word sound_id);
// This scores the joker and returns true if it was scored successfully
// card_object = NULL means the joker_event does not concern a particular Card, i.e. Independend or On_Blind_Selected
// as opposed to events that concern a particular card, i.e. On_Card_Scored or On_Card_Held
bool joker_object_score(JokerObject *joker_object, CardObject* card_object, enum JokerEvent joker_event);
// card_object = NULL means the joker_event does not concern a particular Card, i.e. Independend or
// On_Blind_Selected as opposed to events that concern a particular card, i.e. On_Card_Scored or
// On_Card_Held
bool joker_object_score(
JokerObject* joker_object,
CardObject* card_object,
enum JokerEvent joker_event
);
void joker_object_set_selected(JokerObject* joker_object, bool selected);
bool joker_object_is_selected(JokerObject* joker_object);
+3 -1
View File
@@ -1,3 +1,4 @@
// clang-format off
#ifndef JOKER_GFX_H
#define JOKER_GFX_H
@@ -28,4 +29,5 @@
#include "joker_gfx24.h"
#include "joker_gfx25.h"
#endif
#endif
// clang-format on
+13 -7
View File
@@ -10,18 +10,25 @@ typedef struct SelectionGridRow SelectionGridRow;
typedef struct SelectionGrid SelectionGrid;
// Called whenever there is a change in the selection cursor
// row_idx is the index of the row whose function is invoked - can be used to identify whether it is the previous or new selection row.
typedef void (*RowOnSelectionChangedFunc)(SelectionGrid* selection_grid, int row_idx, const Selection* prev_selection, const Selection* new_selection);
// row_idx is the index of the row whose function is invoked - can be used to identify whether it is
// the previous or new selection row.
typedef void (*RowOnSelectionChangedFunc)(
SelectionGrid* selection_grid,
int row_idx,
const Selection* prev_selection,
const Selection* new_selection
);
typedef int (*RowGetSizeFunc)();
// Called for any non-directional key hit
// The key will not be passed, the function will have to check key_hit() etc. for the key it wants to check
// The key will not be passed, the function will have to check key_hit() etc. for the key it wants
// to check
typedef void (*RowOnKeyTransitFunc)(SelectionGrid* selection_grid, Selection* selection);
struct SelectionGridRow
{
int row_idx;
RowGetSizeFunc get_size;
RowOnSelectionChangedFunc on_selection_changed;
RowGetSizeFunc get_size;
RowOnSelectionChangedFunc on_selection_changed;
RowOnKeyTransitFunc on_key_transit;
};
@@ -30,8 +37,7 @@ struct SelectionGrid
const SelectionGridRow* rows;
const int num_rows;
Selection selection;
};
};
void selection_grid_process_input(SelectionGrid* selection_grid);
+2 -1
View File
@@ -45,7 +45,8 @@ static Blind _blind_type_map[BLIND_TYPE_MAX] = {
static void s_blind_gfx_init(enum BlindType type);
GBLA_UNUSED void blind_set_boss_graphics(const unsigned int* tiles, const u16* palette)
GBLA_UNUSED
void blind_set_boss_graphics(const unsigned int* tiles, const u16* palette)
{
// TODO: This function is unused and not fully fleshed out.
// We need to support more boss blind graphics in the future.
+166 -142
View File
@@ -1,38 +1,35 @@
#include <tonc.h>
#include "joker.h"
#include "joker_gfx.h"
#include "graphic_utils.h"
#include "card.h"
#include "graphic_utils.h"
#include "joker_gfx.h"
#include "pool.h"
#include "soundbank.h"
#include "util.h"
#include <maxmod.h>
#include <stdlib.h>
#include <string.h>
#include <tonc.h>
#include "pool.h"
#define JOKER_SCORE_TEXT_Y 48
#define HELD_CARD_SCORE_TEXT_Y 108
#define MAX_CARD_SCORE_STR_LEN 2
#define JOKER_SCORE_TEXT_Y 48
#define HELD_CARD_SCORE_TEXT_Y 108
#define MAX_CARD_SCORE_STR_LEN 2
#define NUM_JOKERS_PER_SPRITESHEET 2
static const unsigned int *joker_gfxTiles[] =
{
static const unsigned int* joker_gfxTiles[] = {
#define DEF_JOKER_GFX(idx) joker_gfx##idx##Tiles,
#include "../include/def_joker_gfx_table.h"
#undef DEF_JOKER_GFX
};
static const unsigned short *joker_gfxPal[] =
{
static const unsigned short* joker_gfxPal[] = {
#define DEF_JOKER_GFX(idx) joker_gfx##idx##Pal,
#include "../include/def_joker_gfx_table.h"
#include "def_joker_gfx_table.h"
#undef DEF_JOKER_GFX
};
const static u8 edition_price_lut[MAX_EDITIONS] =
{
const static u8 edition_price_lut[MAX_EDITIONS] = {
0, // BASE_EDITION
2, // FOIL_EDITION
3, // HOLO_EDITION
@@ -42,104 +39,45 @@ const static u8 edition_price_lut[MAX_EDITIONS] =
/* So for the card objects, I needed them to be properly sorted
which is why they let you specify the layer index when creating a new card object.
Since the cards would overlap a lot in your hand, If they weren't sorted properly, it would look like a mess.
The joker objects are functionally identical to card objects, so they use the same logic.
But I'm going to use a simpler approach for the joker objects
since I'm lazy and sorting them wouldn't look good enough to warrant the effort.
Since the cards would overlap a lot in your hand, If they weren't sorted properly, it would look
like a mess. The joker objects are functionally identical to card objects, so they use the same
logic. But I'm going to use a simpler approach for the joker objects since I'm lazy and sorting
them wouldn't look good enough to warrant the effort.
*/
static bool used_layers[MAX_JOKER_OBJECTS] = {false}; // Track used layers for joker sprites
static bool _used_layers[MAX_JOKER_OBJECTS] = {false}; // Track used layers for joker sprites
// TODO: Refactor sorting into SpriteObject?
// Maps the spritesheet index to the palette bank index allocated to it.
// Spritesheets that were not allocated are
static int joker_spritesheet_pb_map[(MAX_DEFINABLE_JOKERS + 1) / NUM_JOKERS_PER_SPRITESHEET];
static int joker_pb_num_sprite_users[JOKER_LAST_PB - JOKER_BASE_PB + 1] = { 0 };
static int _joker_spritesheet_pb_map[(MAX_DEFINABLE_JOKERS + 1) / NUM_JOKERS_PER_SPRITESHEET];
static int _joker_pb_num_sprite_users[JOKER_LAST_PB - JOKER_BASE_PB + 1] = {0};
static int get_num_spritesheets()
{
return (get_joker_registry_size() + NUM_JOKERS_PER_SPRITESHEET - 1) / NUM_JOKERS_PER_SPRITESHEET;
}
static int joker_get_spritesheet_idx(u8 joker_id)
{
return joker_id / NUM_JOKERS_PER_SPRITESHEET;
}
// TODO: This should be generalized so any sprite can have dynamic swapping
static void joker_pb_add_sprite_user(int pb)
{
joker_pb_num_sprite_users[pb - JOKER_BASE_PB]++;
}
static void joker_pb_remove_sprite_user(int pb)
{
int num_sprite_users = joker_pb_num_sprite_users[pb - JOKER_BASE_PB];
joker_pb_num_sprite_users[pb - JOKER_BASE_PB] = max(0, num_sprite_users - 1);
}
static int joker_pb_get_num_sprite_users(int joker_pb)
{
return joker_pb_num_sprite_users[joker_pb - JOKER_BASE_PB];
}
static int get_unused_joker_pb()
{
for (int i = 0; i < NUM_ELEM_IN_ARR(joker_pb_num_sprite_users); i++)
{
if (joker_pb_num_sprite_users[i] == 0)
{
return (i + JOKER_BASE_PB);
}
}
return UNDEFINED;
}
static int allocate_pb_if_needed(u8 joker_id)
{
int joker_spritesheet_idx = joker_get_spritesheet_idx(joker_id);
int joker_pb = joker_spritesheet_pb_map[joker_spritesheet_idx];
if (joker_pb != UNDEFINED)
{
// Already allocated
return joker_pb;
}
// Allocate a new palette
joker_pb = get_unused_joker_pb();
if (joker_pb == UNDEFINED)
{
// Ran out of palettes, default to base and pray
joker_pb = JOKER_BASE_PB;
}
else
{
joker_spritesheet_pb_map[joker_spritesheet_idx] = joker_pb;
memcpy16(&pal_obj_mem[PAL_ROW_LEN * joker_pb], joker_gfxPal[joker_spritesheet_idx], NUM_ELEM_IN_ARR(joker_gfx0Pal));
}
return joker_pb;
}
static int s_get_num_spritesheets(void);
static int s_joker_get_spritesheet_idx(u8 joker_id);
static void s_joker_pb_add_sprite_user(int pb);
static void s_joker_pb_remove_sprite_user(int pb);
static int s_joker_pb_get_num_sprite_users(int joker_pb);
static int s_get_unused_joker_pb(void);
static int s_allocate_pb_if_needed(u8 joker_id);
void joker_init()
{
// This should init once only so no need to free
int num_spritesheets = get_num_spritesheets();
int num_spritesheets = s_get_num_spritesheets();
for (int i = 0; i < num_spritesheets; i++)
{
joker_spritesheet_pb_map[i] = UNDEFINED;
_joker_spritesheet_pb_map[i] = UNDEFINED;
}
}
Joker *joker_new(u8 id)
Joker* joker_new(u8 id)
{
if (id >= get_joker_registry_size()) return NULL;
if (id >= get_joker_registry_size())
return NULL;
Joker *joker = POOL_GET(Joker);
const JokerInfo *jinfo = get_joker_registry_entry(id);
Joker* joker = POOL_GET(Joker);
const JokerInfo* jinfo = get_joker_registry_entry(id);
joker->id = id;
joker->modifier = BASE_EDITION; // TODO: Make this a parameter
@@ -149,22 +87,28 @@ Joker *joker_new(u8 id)
joker->persistent_state = 0;
// initialize persistent Joker data if needed
JokerEffect *joker_effect = NULL;
JokerEffect* joker_effect = NULL;
jinfo->joker_effect_func(joker, NULL, JOKER_EVENT_ON_JOKER_CREATED, &joker_effect);
return joker;
}
void joker_destroy(Joker **joker)
void joker_destroy(Joker** joker)
{
POOL_FREE(Joker, *joker);
*joker = NULL;
}
u32 joker_get_score_effect(Joker *joker, Card *scored_card, enum JokerEvent joker_event, JokerEffect **joker_effect)
u32 joker_get_score_effect(
Joker* joker,
Card* scored_card,
enum JokerEvent joker_event,
JokerEffect** joker_effect
)
{
const JokerInfo *jinfo = get_joker_registry_entry(joker->id);
if (!jinfo) return JOKER_EFFECT_FLAG_NONE;
const JokerInfo* jinfo = get_joker_registry_entry(joker->id);
if (!jinfo)
return JOKER_EFFECT_FLAG_NONE;
return jinfo->joker_effect_func(joker, scored_card, joker_event, joker_effect);
}
@@ -176,21 +120,21 @@ int joker_get_sell_value(const Joker* joker)
return UNDEFINED;
}
return joker->value/2;
return joker->value / 2;
}
// JokerObject methods
JokerObject *joker_object_new(Joker *joker)
JokerObject* joker_object_new(Joker* joker)
{
JokerObject *joker_object = POOL_GET(JokerObject);
JokerObject* joker_object = POOL_GET(JokerObject);
int layer = 0;
for (int i = 0; i < MAX_JOKER_OBJECTS; i++)
{
if (!used_layers[i])
if (!_used_layers[i])
{
layer = i;
used_layers[i] = true; // Mark this layer as used
_used_layers[i] = true; // Mark this layer as used
break;
}
}
@@ -199,57 +143,60 @@ JokerObject *joker_object_new(Joker *joker)
joker_object->sprite_object = sprite_object_new();
int tile_index = JOKER_TID + (layer * JOKER_SPRITE_OFFSET);
int joker_spritesheet_idx = joker_get_spritesheet_idx(joker->id);
int joker_spritesheet_idx = s_joker_get_spritesheet_idx(joker->id);
int joker_idx = joker->id % NUM_JOKERS_PER_SPRITESHEET;
int joker_pb = allocate_pb_if_needed(joker->id);
joker_pb_add_sprite_user(joker_pb);
int joker_pb = s_allocate_pb_if_needed(joker->id);
s_joker_pb_add_sprite_user(joker_pb);
memcpy32(&tile_mem[4][tile_index], &joker_gfxTiles[joker_spritesheet_idx][joker_idx * TILE_SIZE * JOKER_SPRITE_OFFSET], TILE_SIZE * JOKER_SPRITE_OFFSET);
memcpy32(
&tile_mem[4][tile_index],
&joker_gfxTiles[joker_spritesheet_idx][joker_idx * TILE_SIZE * JOKER_SPRITE_OFFSET],
TILE_SIZE * JOKER_SPRITE_OFFSET
);
sprite_object_set_sprite
(
joker_object->sprite_object,
sprite_new
(
ATTR0_SQUARE | ATTR0_4BPP | ATTR0_AFF,
ATTR1_SIZE_32,
tile_index,
sprite_object_set_sprite(
joker_object->sprite_object,
sprite_new(
ATTR0_SQUARE | ATTR0_4BPP | ATTR0_AFF,
ATTR1_SIZE_32,
tile_index,
joker_pb,
JOKER_STARTING_LAYER + layer
)
);
return joker_object;
}
void joker_object_destroy(JokerObject **joker_object)
void joker_object_destroy(JokerObject** joker_object)
{
if (joker_object == NULL || *joker_object == NULL) return;
if (joker_object == NULL || *joker_object == NULL)
return;
int layer = sprite_get_layer(joker_object_get_sprite(*joker_object)) - JOKER_STARTING_LAYER;
used_layers[layer] = false;
joker_pb_remove_sprite_user(sprite_get_pb(joker_object_get_sprite(*joker_object)));
if (joker_pb_get_num_sprite_users((sprite_get_pb(joker_object_get_sprite(*joker_object)))) == 0)
_used_layers[layer] = false;
s_joker_pb_remove_sprite_user(sprite_get_pb(joker_object_get_sprite(*joker_object)));
if (s_joker_pb_get_num_sprite_users((sprite_get_pb(joker_object_get_sprite(*joker_object)))) ==
0)
{
joker_spritesheet_pb_map[joker_get_spritesheet_idx((*joker_object)->joker->id)] = UNDEFINED;
_joker_spritesheet_pb_map[s_joker_get_spritesheet_idx((*joker_object)->joker->id)] =
UNDEFINED;
}
sprite_object_destroy(&(*joker_object)->sprite_object); // Destroy the sprite
joker_destroy(&(*joker_object)->joker); // Destroy the joker
joker_destroy(&(*joker_object)->joker); // Destroy the joker
POOL_FREE(JokerObject, *joker_object);
*joker_object = NULL;
}
void joker_object_update(JokerObject *joker_object)
void joker_object_update(JokerObject* joker_object)
{
CardObject *card_object = (CardObject *)joker_object;
CardObject* card_object = (CardObject*)joker_object;
card_object_update(card_object);
}
void joker_object_shake(JokerObject *joker_object, mm_word sound_id)
void joker_object_shake(JokerObject* joker_object, mm_word sound_id)
{
sprite_object_shake(joker_object->sprite_object, sound_id);
}
@@ -265,7 +212,11 @@ void set_and_shift_text(char* str, int* cursor_pos_x, int* cursor_pos_y, int col
*cursor_pos_x += joker_score_display_offset_px;
}
bool joker_object_score(JokerObject *joker_object, CardObject* card_object, enum JokerEvent joker_event)
bool joker_object_score(
JokerObject* joker_object,
CardObject* card_object,
enum JokerEvent joker_event
)
{
if (joker_object == NULL)
{
@@ -273,16 +224,17 @@ bool joker_object_score(JokerObject *joker_object, CardObject* card_object, enum
}
JokerEffect* joker_effect = NULL;
u32 effect_flags_ret = joker_get_score_effect(joker_object->joker, card_object->card, joker_event, &joker_effect);
u32 effect_flags_ret =
joker_get_score_effect(joker_object->joker, card_object->card, joker_event, &joker_effect);
if (effect_flags_ret == JOKER_EFFECT_FLAG_NONE)
{
return false;
}
u32 chips = get_chips();
u32 mult = get_mult();
int money = get_money();
u32 chips = get_chips();
u32 mult = get_mult();
int money = get_money();
if (effect_flags_ret & JOKER_EFFECT_FLAG_RETRIGGER)
{
@@ -317,7 +269,7 @@ bool joker_object_score(JokerObject *joker_object, CardObject* card_object, enum
}
if (effect_flags_ret & JOKER_EFFECT_FLAG_MULT)
{
mult = u32_protected_add(mult, joker_effect->mult);
mult = u32_protected_add(mult, joker_effect->mult);
char score_buffer[INT_MAX_DIGITS + 2];
snprintf(score_buffer, sizeof(score_buffer), "+%lu", joker_effect->mult);
set_and_shift_text(score_buffer, &cursorPosX, &cursorPosY, TTE_RED_PB);
@@ -326,7 +278,7 @@ bool joker_object_score(JokerObject *joker_object, CardObject* card_object, enum
// if xmult is zero, DO NOT multiply by it
if (effect_flags_ret & JOKER_EFFECT_FLAG_XMULT && joker_effect->xmult > 0)
{
mult = u32_protected_mult(mult, joker_effect->xmult);
mult = u32_protected_mult(mult, joker_effect->xmult);
char score_buffer[INT_MAX_DIGITS + 2];
snprintf(score_buffer, sizeof(score_buffer), "X%lu", joker_effect->xmult);
set_and_shift_text(score_buffer, &cursorPosX, &cursorPosY, TTE_RED_PB);
@@ -392,12 +344,12 @@ Sprite* joker_object_get_sprite(JokerObject* joker_object)
int joker_get_random_rarity()
{
int joker_rarity = 0;
int rarity_roll = random() % 100;
if (rarity_roll < COMMON_JOKER_CHANCE)
int rarity_roll = random() % 100;
if (rarity_roll < COMMON_JOKER_CHANCE)
{
joker_rarity = COMMON_JOKER;
}
else if (rarity_roll < COMMON_JOKER_CHANCE + UNCOMMON_JOKER_CHANCE)
else if (rarity_roll < COMMON_JOKER_CHANCE + UNCOMMON_JOKER_CHANCE)
{
joker_rarity = UNCOMMON_JOKER;
}
@@ -405,10 +357,82 @@ int joker_get_random_rarity()
{
joker_rarity = RARE_JOKER;
}
else if (rarity_roll < COMMON_JOKER_CHANCE + UNCOMMON_JOKER_CHANCE + RARE_JOKER_CHANCE + LEGENDARY_JOKER_CHANCE)
else if (rarity_roll < COMMON_JOKER_CHANCE + UNCOMMON_JOKER_CHANCE + RARE_JOKER_CHANCE +
LEGENDARY_JOKER_CHANCE)
{
joker_rarity = LEGENDARY_JOKER;
}
return joker_rarity;
}
static int s_get_num_spritesheets()
{
return (get_joker_registry_size() + NUM_JOKERS_PER_SPRITESHEET - 1) /
NUM_JOKERS_PER_SPRITESHEET;
}
static int s_joker_get_spritesheet_idx(u8 joker_id)
{
return joker_id / NUM_JOKERS_PER_SPRITESHEET;
}
static void s_joker_pb_add_sprite_user(int pb)
{
_joker_pb_num_sprite_users[pb - JOKER_BASE_PB]++;
}
static void s_joker_pb_remove_sprite_user(int pb)
{
int num_sprite_users = _joker_pb_num_sprite_users[pb - JOKER_BASE_PB];
_joker_pb_num_sprite_users[pb - JOKER_BASE_PB] = max(0, num_sprite_users - 1);
}
static int s_joker_pb_get_num_sprite_users(int joker_pb)
{
return _joker_pb_num_sprite_users[joker_pb - JOKER_BASE_PB];
}
static int s_get_unused_joker_pb()
{
for (int i = 0; i < NUM_ELEM_IN_ARR(_joker_pb_num_sprite_users); i++)
{
if (_joker_pb_num_sprite_users[i] == 0)
{
return (i + JOKER_BASE_PB);
}
}
return UNDEFINED;
}
static int s_allocate_pb_if_needed(u8 joker_id)
{
int joker_spritesheet_idx = s_joker_get_spritesheet_idx(joker_id);
int joker_pb = _joker_spritesheet_pb_map[joker_spritesheet_idx];
if (joker_pb != UNDEFINED)
{
// Already allocated
return joker_pb;
}
// Allocate a new palette
joker_pb = s_get_unused_joker_pb();
if (joker_pb == UNDEFINED)
{
// Ran out of palettes, default to base and pray
joker_pb = JOKER_BASE_PB;
}
else
{
_joker_spritesheet_pb_map[joker_spritesheet_idx] = joker_pb;
memcpy16(
&pal_obj_mem[PAL_ROW_LEN * joker_pb],
joker_gfxPal[joker_spritesheet_idx],
NUM_ELEM_IN_ARR(joker_gfx0Pal)
);
}
return joker_pb;
}
+1706 -1153
View File
File diff suppressed because it is too large Load Diff
+32 -23
View File
@@ -1,18 +1,18 @@
#include <tonc.h>
#include <maxmod.h>
#include <string.h>
#include "sprite.h"
#include "affine_background.h"
#include "blind.h"
#include "card.h"
#include "game.h"
#include "blind.h"
#include "joker.h"
#include "affine_background.h"
#include "graphic_utils.h"
#include "joker.h"
#include "sprite.h"
#include <maxmod.h>
#include <string.h>
#include <tonc.h>
// Graphics
#include "background_gfx.h"
#include "affine_background_gfx.h"
#include "background_gfx.h"
// Audio
#include "soundbank.h"
@@ -25,14 +25,22 @@ void init()
irq_add(II_HBLANK, affine_background_hblank);
// Initialize text engine
tte_init_se(0, BG_CBB(TTE_CBB) | BG_SBB(TTE_SBB), 0, CLR_WHITE, TTE_BIT_UNPACK_OFFSET, NULL, NULL);
tte_init_se(
0,
BG_CBB(TTE_CBB) | BG_SBB(TTE_SBB),
0,
CLR_WHITE,
TTE_BIT_UNPACK_OFFSET,
NULL,
NULL
);
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_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;
// Set up the video mode
@@ -40,7 +48,7 @@ void init()
REG_BG0CNT = BG_PRIO(0) | BG_CBB(TTE_CBB) | BG_SBB(TTE_SBB) | BG_4BPP;
// BG1 is the main background layer
REG_BG1CNT = BG_PRIO(1) | BG_CBB(MAIN_BG_CBB) | BG_SBB(MAIN_BG_SBB) | BG_8BPP;
// BG2 is the affine background layer
// BG2 is the affine background layer
REG_BG2CNT = BG_PRIO(2) | BG_CBB(AFFINE_BG_CBB) | BG_SBB(AFFINE_BG_SBB) | BG_8BPP | BG_WRAP;
int win1_left = 72;
@@ -53,20 +61,21 @@ void init()
int win2_right = 232;
int win2_bottom = 44;
REG_WIN0H = win1_left<<8 | win1_right;
REG_WIN0V = win1_top<<8 | win1_bottom;
REG_WIN0CNT = WIN_ALL | WIN_BLD;
REG_WINOUTCNT = WIN_ALL;
REG_WIN0H = win1_left << 8 | win1_right;
REG_WIN0V = win1_top << 8 | win1_bottom;
REG_WIN0CNT = WIN_ALL | WIN_BLD;
REG_WINOUTCNT = WIN_ALL;
REG_WIN1H = win2_left<<8 | win2_right;
REG_WIN1V = win2_top<<8 | win2_bottom;
REG_WIN1H = win2_left << 8 | win2_right;
REG_WIN1V = win2_top << 8 | win2_bottom;
REG_WIN1CNT = WIN_ALL | WIN_BLD;
REG_BLDCNT = BLD_BUILD(BLD_BG1, BLD_BG2, 1);
REG_BLDALPHA = BLDA_BUILD(0, 13);
REG_DISPCNT = DCNT_MODE1 | DCNT_OBJ_1D | DCNT_BG0 | DCNT_BG1 | DCNT_BG2 | DCNT_OBJ | DCNT_WIN0 | DCNT_WIN1;
REG_DISPCNT = DCNT_MODE1 | DCNT_OBJ_1D | DCNT_BG0 | DCNT_BG1 | DCNT_BG2 | DCNT_OBJ | DCNT_WIN0 |
DCNT_WIN1;
// Initialize subsystems
mmInitDefault((mm_addr)soundbank_bin, 12);
@@ -94,14 +103,14 @@ int main()
{
init();
while(true)
while (true)
{
VBlankIntrWait();
mmFrame();
key_poll();
key_poll();
update();
draw();
}
return 0;
return 0;
}
+2 -1
View File
@@ -6,4 +6,5 @@
#define GIT_DIRTY "-dirty"
#endif
__attribute__((section(".version"), used)) const char balatro_version[] = "GBALATRO_VERSION:" GIT_HASH GIT_DIRTY;
__attribute__((section(".version"), used)) const char balatro_version[] =
"GBALATRO_VERSION:" GIT_HASH GIT_DIRTY;