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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+32
-23
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user