From 4a5644eddbcb0bf8a2d11d2d4aa1ae000900448d Mon Sep 17 00:00:00 2001 From: cellos51 Date: Thu, 17 Jul 2025 21:47:50 -0400 Subject: [PATCH 01/19] Defining a basic system for jokers --- graphics/joker_gfx.grit | 1 + graphics/joker_gfx.png | Bin 0 -> 862 bytes include/blind.h | 2 +- include/card.h | 3 ++ include/joker.h | 79 ++++++++++++++++++++++++++++++++++++++++ source/blind.c | 14 +++---- source/card.c | 8 ++++ source/game.c | 9 +---- source/joker.c | 36 ++++++++++++++++++ source/main.c | 10 ++--- 10 files changed, 139 insertions(+), 23 deletions(-) create mode 100644 graphics/joker_gfx.grit create mode 100644 graphics/joker_gfx.png create mode 100644 include/joker.h create mode 100644 source/joker.c diff --git a/graphics/joker_gfx.grit b/graphics/joker_gfx.grit new file mode 100644 index 0000000..f677be5 --- /dev/null +++ b/graphics/joker_gfx.grit @@ -0,0 +1 @@ +-gB4 -Mw4 -Mh4 -m! \ No newline at end of file diff --git a/graphics/joker_gfx.png b/graphics/joker_gfx.png new file mode 100644 index 0000000000000000000000000000000000000000..5623e6089c218348eb80fed3f95d7f6f36197e9f GIT binary patch literal 862 zcmV-k1EKthP)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy24YJ`L;wH)0002_L%V+f000SaNLh0L01FcU01FcV0GgZ_00007 zbV*G`2k8e976~X@#=vm^00N>(L_t(o!^Ky}4o)ujt^?0|0RJ`O8KLYndwAu7{v@wqi`0F}W^ty5?0wOM%OLU<^n}wxD)qoC~xJ zIJtHJQh}&cfMs~dwUaO+E0H($fO75h1OOJmf`Oi2f}UTlc$&^uazIvr?IIC-2`tCF z(^|-^l@WR+8Pm<}z0j%g=_EF0?WSNCNb)ZrSi70v>y*~y3acQ^dU`An9s^MnX*xdm zq*XX~V*`W`AY|3R{(Sw0{muetf63!oDBya45C9?N+U9x)IOnM$VeO{k$+48J&&dlx zVHFUFM1P2)NF^Tcv|9|)uHL9|Z(S>K-fR~U2m+w+L;WjAuUa@QNqWi(l o43Np_bbGl*y!z#oy!Q3{KfH-~4wy$>$p8QV07*qoM6N<$f;4P$8vp #include +#include "deck_gfx.h" + // Audio #include "soundbank.h" @@ -14,6 +16,12 @@ const static u16 card_sprite_lut[NUM_SUITS][NUM_RANKS] = { {624, 640, 656, 672, 688, 704, 720, 736, 752, 768, 784, 800, 816} }; +void card_init() +{ + GRIT_CPY(&tile_mem[4], deck_gfxTiles); + GRIT_CPY(pal_obj_mem, deck_gfxPal); +} + // Card methods Card *card_new(u8 suit, u8 rank) { diff --git a/source/game.c b/source/game.c index e7811a6..ccfb2f2 100644 --- a/source/game.c +++ b/source/game.c @@ -776,11 +776,6 @@ void hand_set_focus(int index) mmEffectEx(&sfx_focus); } -int hand_get_focus() -{ - return card_focused; -} - void hand_select() { if (hand_state != HAND_SELECT || hand[card_focused] == NULL) return; @@ -978,11 +973,11 @@ static void game_playing_process_input_and_state() { if (key_hit(KEY_LEFT)) { - hand_set_focus(hand_get_focus() + 1); // The reason why this adds 1 is because the hand is drawn from right to left. There is no particular reason for this, it's just how I did it. + hand_set_focus(card_focused + 1); // The reason why this adds 1 is because the hand is drawn from right to left. There is no particular reason for this, it's just how I did it. } else if (key_hit(KEY_RIGHT)) { - hand_set_focus(hand_get_focus() - 1); + hand_set_focus(card_focused - 1); } if (key_hit(KEY_A)) diff --git a/source/joker.c b/source/joker.c new file mode 100644 index 0000000..3f07e42 --- /dev/null +++ b/source/joker.c @@ -0,0 +1,36 @@ +#include + +#include "joker.h" +#include "joker_gfx.h" +#include "graphic_utils.h" + +const static u8 joker_data_lut[MAX_JOKERS][2] = // Rarity, Value +{ + {COMMON_JOKER, 2}, // Default Joker + {COMMON_JOKER, 5}, // Greedy Joker +}; + +void joker_init() +{ + GRIT_CPY(&tile_mem[4][JOKER_TID], joker_gfxTiles); + memcpy16(&pal_obj_mem[PAL_ROW_LEN * JOKER_PB], joker_gfxPal, sizeof(joker_gfxPal) / 2); +} + +Joker *joker_new(u8 id) +{ + Joker *joker = malloc(sizeof(Joker)); + + joker->id = id; // TODO: Make this random later + joker->modifier = BASE_EDITION; // TODO: Make this random later + joker->value = joker_data_lut[id][1]; + joker->rarity = joker_data_lut[id][0]; + + return joker; +} + +void joker_destroy(Joker **joker) +{ + if (*joker == NULL) return; + free(*joker); + *joker = NULL; +} \ No newline at end of file diff --git a/source/main.c b/source/main.c index ad10ee1..1cb00e2 100644 --- a/source/main.c +++ b/source/main.c @@ -6,10 +6,10 @@ #include "card.h" #include "game.h" #include "blind.h" +#include "joker.h" #include "graphic_utils.h" // Graphics -#include "deck_gfx.h" #include "background_gfx.h" #include "affine_background_gfx.h" @@ -60,10 +60,6 @@ void init() pal_bg_bank[14][15] = 0x213F; // Red pal_bg_bank[15][15] = CLR_WHITE; - // Deck graphics - GRIT_CPY(&tile_mem[4], deck_gfxTiles); - GRIT_CPY(pal_obj_mem, deck_gfxPal); - // Set up the video mode // BG0 is the TTE text layer REG_BG0CNT = BG_PRIO(0) | BG_CBB(TTE_CBB) | BG_SBB(TTE_SBB) | BG_4BPP; @@ -101,7 +97,9 @@ void init() // Initialize subsystems sprite_init(); - blinds_init(); + card_init(); + blind_init(); + joker_init(); game_init(); } From 176910c48324624dcde3ee185938481a2f8df237 Mon Sep 17 00:00:00 2001 From: cellos51 Date: Fri, 18 Jul 2025 19:29:17 -0400 Subject: [PATCH 02/19] Jokers appear in the shop but don't do anything yet --- Makefile | 9 ++--- include/card.h | 2 ++ include/game.h | 2 ++ include/joker.h | 11 +++++- include/sprite.h | 1 + source/card.c | 13 ++++++- source/game.c | 92 ++++++++++++++++++++++++++++++++++++++++-------- source/joker.c | 82 +++++++++++++++++++++++++++++++++++++++++- source/sprite.c | 6 ++++ 9 files changed, 197 insertions(+), 21 deletions(-) diff --git a/Makefile b/Makefile index 596c0a9..d5c7ebc 100644 --- a/Makefile +++ b/Makefile @@ -38,16 +38,17 @@ GRAPHICS := graphics #--------------------------------------------------------------------------------- ARCH := -mthumb -mthumb-interwork -CFLAGS := -g -Wall -O2\ - -mcpu=arm7tdmi -mtune=arm7tdmi\ - $(ARCH) +CFLAGS := -O3 -Wall \ + -mcpu=arm7tdmi -mtune=arm7tdmi \ + -ffast-math -fomit-frame-pointer -funroll-loops -flto \ + $(ARCH) CFLAGS += $(INCLUDE) CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions ASFLAGS := -g $(ARCH) -LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $*.map) +LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $*.map) -flto #--------------------------------------------------------------------------------- # any extra libraries we wish to link with the project diff --git a/include/card.h b/include/card.h index 314866c..ecd724d 100644 --- a/include/card.h +++ b/include/card.h @@ -2,6 +2,7 @@ #define CARD_H #include +#include #include "sprite.h" @@ -65,5 +66,6 @@ CardObject *card_object_new(Card *card); void card_object_destroy(CardObject **card_object); void card_object_update(CardObject *card_object); // Update the card object position and scale void card_object_set_sprite(CardObject *card_object, int layer); +void card_object_score(CardObject *card_object, mm_word sound_id); // This doesn't actually score anything, it just performs an animation and plays a sound effect #endif // CARD_H \ No newline at end of file diff --git a/include/game.h b/include/game.h index f9fed6f..ab6be3b 100644 --- a/include/game.h +++ b/include/game.h @@ -3,6 +3,8 @@ #define MAX_HAND_SIZE 16 #define MAX_DECK_SIZE 52 +#define MAX_JOKERS_SIZE 5 +#define MAX_SHOP_JOKERS 2 // TODO: Make this dynamic and allow for other items besides jokers #define MAX_SELECTION_SIZE 5 #define FRAMES(x) (((x) + game_speed - 1) / game_speed) diff --git a/include/joker.h b/include/joker.h index 4488380..4f60e76 100644 --- a/include/joker.h +++ b/include/joker.h @@ -2,7 +2,11 @@ #define JOKER_H #define JOKER_TID 880 // Tile ID for the starting index in the tile memory +#define JOKER_SPRITE_OFFSET 16 // Offset for the joker sprites #define JOKER_PB 4 // Palette bank for the default jokers +#define JOKER_STARTING_LAYER 27 + +#include #include "sprite.h" #include "card.h" @@ -19,6 +23,7 @@ #define LEGENDARY_JOKER 3 #define MAX_JOKERS 2 // The current maximum jokers added +#define MAX_JOKER_OBJECTS 32 // The maximum number of joker objects that can be created at once #define DEFAULT_JOKER_ID 0 #define GREEDY_JOKER_ID 1 // This is just an example to show the patern of making joker IDs @@ -44,6 +49,7 @@ typedef struct // copy of CardObject in card.h FIXED trotation; FIXED rotation; FIXED vrotation; + bool selected; } JokerObject; typedef struct // These jokers are triggered after the played hand has finished scoring. @@ -74,6 +80,9 @@ void joker_destroy(Joker **joker); IndependentEffect joker_independent_effect(Joker *joker); OnScoredEffect joker_on_scored(Joker *joker, Card *scored_card); -void joker_object_score(JokerObject *joker_object); // This doesn't actually score anything, it just performs an animation and plays a sound effect +JokerObject *joker_object_new(Joker *joker); +void joker_object_destroy(JokerObject **joker_object); +void joker_object_update(JokerObject *joker_object); +void joker_object_score(JokerObject *joker_object, mm_word sound_id); // This doesn't actually score anything, it just performs an animation and plays a sound effect #endif // JOKER_H \ No newline at end of file diff --git a/include/sprite.h b/include/sprite.h index fdcf7f0..f2b6083 100644 --- a/include/sprite.h +++ b/include/sprite.h @@ -15,6 +15,7 @@ typedef struct Sprite *sprite_new(u16 a0, u16 a1, u32 tid, u32 pb, int sprite_index); Sprite *affine_sprite_new(u16 a0, u16 a1, u32 tid, u32 pb); void sprite_destroy(Sprite **sprite); +int sprite_get_layer(Sprite *sprite); INLINE void sprite_position(Sprite *sprite, int x, int y) { sprite->pos.x = x; diff --git a/source/card.c b/source/card.c index 4848c20..7b9dcfd 100644 --- a/source/card.c +++ b/source/card.c @@ -151,4 +151,15 @@ void card_object_set_sprite(CardObject *card_object, int layer) { sprite_destroy(&card_object->sprite); // Destroy the old sprite if it exists card_object->sprite = sprite_new(ATTR0_SQUARE | ATTR0_4BPP | ATTR0_AFF, ATTR1_SIZE_32, card_sprite_lut[card_object->card->suit][card_object->card->rank], 0, layer); -} \ No newline at end of file +} + +void card_object_score(CardObject *card_object, mm_word sound_id) +{ + card_object->vscale = float2fx(0.3f); // Scale down the card when it's scored + card_object->vrotation = float2fx(8.0f); // Rotate the card when it's scored + + if (sound_id == -1) return; // If no sound ID is provided, do nothing + + mm_sound_effect sfx_select = {{sound_id}, 1024, 0, 255, 128,}; + mmEffectEx(&sfx_select); +} diff --git a/source/game.c b/source/game.c index ccfb2f2..e26de39 100644 --- a/source/game.c +++ b/source/game.c @@ -9,6 +9,7 @@ #include "sprite.h" #include "card.h" #include "blind.h" +#include "joker.h" #include "graphic_utils.h" #include "tonc_video.h" @@ -79,6 +80,9 @@ static int deck_top = -1; static Card *discard_pile[MAX_DECK_SIZE] = {NULL}; static int discard_top = -1; +static JokerObject *jokers[MAX_JOKERS] = {NULL}; +static int jokers_top = -1; + // Played stack static inline void played_push(CardObject *card_object) { @@ -118,6 +122,18 @@ static inline Card *discard_pop() return discard_pile[discard_top--]; } +static inline void joker_push(JokerObject *joker) +{ + if (jokers_top >= MAX_JOKERS_SIZE - 1) return; + jokers[++jokers_top] = joker; +} + +static inline JokerObject *joker_pop() +{ + if (jokers_top < 0) return NULL; + return jokers[jokers_top--]; +} + // Consts // Rects left top right bottom @@ -877,13 +893,13 @@ void game_round_init() cards_drawn = 0; hand_selections = 0; - playing_blind_token = blind_token_new(current_blind, 8, 18, 33); // Create the blind token sprite at the top left corner + playing_blind_token = blind_token_new(current_blind, 8, 18, MAX_SELECTION_SIZE + MAX_HAND_SIZE + 1); // Create the blind token sprite at the top left corner // TODO: Hide blind token and display it after sliding blind rect animation //if (playing_blind_token != NULL) //{ // obj_hide(playing_blind_token->obj); // Hide the blind token sprite for now //} - round_end_blind_token = blind_token_new(current_blind, 82, 86, 34); // Create the blind token sprite for round end + round_end_blind_token = blind_token_new(current_blind, 82, 86, MAX_SELECTION_SIZE + MAX_HAND_SIZE + 2); // Create the blind token sprite for round end if (round_end_blind_token != NULL) { @@ -930,9 +946,9 @@ void game_init() hands = max_hands; discards = max_discards; - blind_select_tokens[SMALL_BLIND] = blind_token_new(SMALL_BLIND, 8, 18, 35); - blind_select_tokens[BIG_BLIND] = blind_token_new(BIG_BLIND, 8, 18, 36); - blind_select_tokens[BOSS_BLIND] = blind_token_new(BOSS_BLIND, 8, 18, 37); + blind_select_tokens[SMALL_BLIND] = blind_token_new(SMALL_BLIND, 8, 18, MAX_SELECTION_SIZE + MAX_HAND_SIZE + 3); + blind_select_tokens[BIG_BLIND] = blind_token_new(BIG_BLIND, 8, 18, MAX_SELECTION_SIZE + MAX_HAND_SIZE + 4); + blind_select_tokens[BOSS_BLIND] = blind_token_new(BOSS_BLIND, 8, 18, MAX_SELECTION_SIZE + MAX_HAND_SIZE + 5); obj_hide(blind_select_tokens[SMALL_BLIND]->obj); obj_hide(blind_select_tokens[BIG_BLIND]->obj); @@ -1471,12 +1487,7 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection tte_write(score_buffer); *played_selections = scored_cards; - //played[j]->vy += (3 << FIX_SHIFT); - played[played_top - j]->vscale = float2fx(0.3f); // Scale down the card when it's scored - played[played_top - j]->vrotation = float2fx(8.0f); // Rotate the card when it's scored - - mm_sound_effect sfx_select = {{SFX_CARD_SELECT}, 1024, 0, 255, 128,}; - mmEffectEx(&sfx_select); + card_object_score(played[played_top - j], SFX_CARD_SELECT); // Relocated card scoring logic here chips += card_get_value(played[played_top - j]->card); @@ -1618,6 +1629,31 @@ static void game_round_end_cashout() set_score(score); // Set the score display } +static void game_shop_create_items(JokerObject *shop_jokers[], bool first_time) +{ + for (int i = 0; i < MAX_SHOP_JOKERS; i++) + { + if (shop_jokers[i] != NULL) + { + joker_object_destroy(&shop_jokers[i]); // Destroy the joker object if it exists + } + + shop_jokers[i] = joker_object_new(joker_new(DEFAULT_JOKER_ID)); + shop_jokers[i]->x = int2fx(120 + i * 32); + shop_jokers[i]->y = int2fx(160); + shop_jokers[i]->tx = shop_jokers[i]->x; + shop_jokers[i]->ty = int2fx(71); + + if (first_time == false) + { + shop_jokers[i]->y = shop_jokers[i]->ty; // If it's not the first time, set the y position to the target position + joker_object_score(shop_jokers[i], UNDEFINED); // Give the joker a little wiggle animation + } + + sprite_position(shop_jokers[i]->sprite, fx2int(shop_jokers[i]->x), fx2int(shop_jokers[i]->y)); + } +} + void game_playing() { // TODO: Blind rect sliding into view animation... @@ -1923,6 +1959,16 @@ void game_shop() // TODO: Later move these static variables somewhere else so they can be reused for each game state static int state = 0; + static JokerObject *shop_jokers[MAX_SHOP_JOKERS] = {NULL}; + + for (int i = 0; i < MAX_SHOP_JOKERS; i++) + { + if (shop_jokers[i] != NULL) + { + joker_object_update(shop_jokers[i]); + } + } + // these are for controlling the shop menu static bool top_row = true; static ushort selection_x = 0; @@ -1960,6 +2006,11 @@ void game_shop() { main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT_UP, SE_UP); + if (timer == 1) + { + game_shop_create_items(shop_jokers, true); + } + if (timer >= 7) // Shift the shop icon { int timer_offset = timer - 6; @@ -2052,7 +2103,7 @@ void game_shop() if (key_hit(KEY_A)) { - // Do shop re-roll for money + game_shop_create_items(shop_jokers, false); } } @@ -2067,20 +2118,28 @@ void game_shop() if (timer == 1) { + for (int i = 0; i < MAX_SHOP_JOKERS; i++) + { + if (shop_jokers[i] != NULL) + { + shop_jokers[i]->ty = int2fx(160); + } + } + int y = 6; memset16(&se_mem[MAIN_BG_SBB][32 * (y - 1)], 0x0006, 1); memset16(&se_mem[MAIN_BG_SBB][1 + 32 * (y - 1)], 0x0007, 2); memset16(&se_mem[MAIN_BG_SBB][3 + 32 * (y - 1)], 0x0008, 1); memset16(&se_mem[MAIN_BG_SBB][4 + 32 * (y - 1)], 0x0009, 4); memset16(&se_mem[MAIN_BG_SBB][7 + 32 * (y - 1)], 0x000A, 1); - memset16(&se_mem[MAIN_BG_SBB][8 + 32 * (y - 1)], 0x0406, 1); + memset16(&se_mem[MAIN_BG_SBB][8 + 32 * (y - 1)], 0x0406, 1); } else if (timer == 2) { int y = 5; memset16(&se_mem[MAIN_BG_SBB][32 * (y - 1)], 0x0001, 1); memset16(&se_mem[MAIN_BG_SBB][1 + 32 * (y - 1)], 0x0002, 7); - memset16(&se_mem[MAIN_BG_SBB][8 + 32 * (y - 1)], 0x0401, 1); + memset16(&se_mem[MAIN_BG_SBB][8 + 32 * (y - 1)], 0x0401, 1); } if (timer >= MENU_POP_OUT_ANIM_FRAMES) @@ -2097,6 +2156,11 @@ void game_shop() selection_x = 0; // Reset the selection top_row = true; // Reset the top row selection + for (int i = 0; i < MAX_SHOP_JOKERS; i++) + { + joker_object_destroy(&shop_jokers[i]); // Destroy the joker objects + } + increment_blind(BLIND_DEFEATED); game_set_state(GAME_BLIND_SELECT); // If we reach here, we should go to the blind select state diff --git a/source/joker.c b/source/joker.c index 3f07e42..8e6f392 100644 --- a/source/joker.c +++ b/source/joker.c @@ -3,6 +3,10 @@ #include "joker.h" #include "joker_gfx.h" #include "graphic_utils.h" +#include "card.h" + +#include +#include const static u8 joker_data_lut[MAX_JOKERS][2] = // Rarity, Value { @@ -10,6 +14,15 @@ const static u8 joker_data_lut[MAX_JOKERS][2] = // Rarity, Value {COMMON_JOKER, 5}, // Greedy Joker }; +/* 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. +*/ +static bool used_layers[MAX_JOKER_OBJECTS] = {false}; // Track used layers for joker sprites + void joker_init() { GRIT_CPY(&tile_mem[4][JOKER_TID], joker_gfxTiles); @@ -33,4 +46,71 @@ void joker_destroy(Joker **joker) if (*joker == NULL) return; free(*joker); *joker = NULL; -} \ No newline at end of file +} + +// JokerObject methods +JokerObject *joker_object_new(Joker *joker) +{ + JokerObject *joker_object = malloc(sizeof(JokerObject)); + + int layer = 0; + for (int i = 0; i < MAX_JOKER_OBJECTS; i++) + { + if (!used_layers[i]) + { + layer = i; + used_layers[i] = true; // Mark this layer as used + break; + } + } + + joker_object->joker = joker; + joker_object->sprite = sprite_new + ( + ATTR0_SQUARE | ATTR0_4BPP | ATTR0_AFF, + ATTR1_SIZE_32, + JOKER_TID + (JOKER_SPRITE_OFFSET * joker->id), + JOKER_PB, + JOKER_STARTING_LAYER + layer + ); + joker_object->tx = 0; // Target position + joker_object->ty = 0; + joker_object->x = 0; + joker_object->y = 0; + joker_object->vx = 0; + joker_object->vy = 0; + joker_object->tscale = float2fx(1.0f); // Target scale + joker_object->scale = float2fx(1.0f); + joker_object->vscale = float2fx(0.0f); + joker_object->trotation = float2fx(0.0f); // Target rotation + joker_object->rotation = float2fx(0.0f); + joker_object->vrotation = float2fx(0.0f); + + return joker_object; +} + +void joker_object_destroy(JokerObject **joker_object) +{ + if (*joker_object == NULL) return; + + int layer = sprite_get_layer((*joker_object)->sprite) - JOKER_STARTING_LAYER; + used_layers[layer] = false; + sprite_destroy(&(*joker_object)->sprite); // Destroy the sprite + joker_destroy(&(*joker_object)->joker); // Destroy the joker + free(*joker_object); + *joker_object = NULL; +} + +void joker_object_update(JokerObject *joker_object) +{ + // Re-use the card object update logic by casting the JokerObject to a CardObject temporarily + // This works because card_object_update doesn't use any card data, which is why CardObject should've been generic. my bad + CardObject *card_object = (CardObject *)joker_object; + card_object_update(card_object); +} + +void joker_object_score(JokerObject *joker_object, mm_word sound_id) // Another derived function of card_object +{ + CardObject *card_object = (CardObject *)joker_object; + card_object_score(card_object, sound_id); +} diff --git a/source/sprite.c b/source/sprite.c index 57c98a9..ca2ace1 100644 --- a/source/sprite.c +++ b/source/sprite.c @@ -79,6 +79,12 @@ void sprite_destroy(Sprite **sprite) *sprite = NULL; } +int sprite_get_layer(Sprite *sprite) +{ + if (sprite == NULL || sprite->obj == NULL) return -1; + return sprite->obj - obj_buffer; +} + // Sprite functions void sprite_init() { From c06b1797643ac69306e3657532fc85d677f6431d Mon Sep 17 00:00:00 2001 From: cellos51 Date: Fri, 18 Jul 2025 20:25:12 -0400 Subject: [PATCH 03/19] really bad purchasing system for jokers --- include/game.h | 2 +- source/game.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/include/game.h b/include/game.h index ab6be3b..818ae41 100644 --- a/include/game.h +++ b/include/game.h @@ -3,7 +3,7 @@ #define MAX_HAND_SIZE 16 #define MAX_DECK_SIZE 52 -#define MAX_JOKERS_SIZE 5 +#define MAX_JOKERS_SIZE 5 // This doesn't account for negatives right now. #define MAX_SHOP_JOKERS 2 // TODO: Make this dynamic and allow for other items besides jokers #define MAX_SELECTION_SIZE 5 #define FRAMES(x) (((x) + game_speed - 1) / game_speed) diff --git a/source/game.c b/source/game.c index e26de39..14451a7 100644 --- a/source/game.c +++ b/source/game.c @@ -27,7 +27,7 @@ static uint timer = 0; // This might already exist in libtonc but idk so i'm jus static int game_speed = 1; static int background = 0; -static enum GameState game_state = GAME_BLIND_SELECT; // The current game state, this is used to determine what the game is doing at any given time +static enum GameState game_state = GAME_SHOP; // The current game state, this is used to determine what the game is doing at any given time static enum HandState hand_state = HAND_DRAW; static enum PlayState play_state = PLAY_PLAYING; @@ -1592,6 +1592,29 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection } } +static void held_jokers_update_loop() +{ + const int spacing_lut[MAX_HAND_SIZE] = { 26, 26, 26, 26, 20 }; + + FIXED hand_x = int2fx(108); + FIXED hand_y = int2fx(10); + + for (int i = 0; i < jokers_top + 1; i++) + { + if (jokers[i] == NULL) continue; + + jokers[i]->tx = hand_x + (int2fx(i) - int2fx(jokers_top) / 2) * -spacing_lut[jokers_top]; + jokers[i]->ty = hand_y; + + if (jokers[i]->selected) + { + jokers[i]->ty -= (10 << FIX_SHIFT); + } + + joker_object_update(jokers[i]); + } +} + static void game_playing_ui_text_update() { static int last_hand_size = 0; @@ -1974,7 +1997,7 @@ void game_shop() static ushort selection_x = 0; // temp variables for future implementation - const ushort max_items_top = 0; + const ushort max_items_top = MAX_SHOP_JOKERS; const ushort max_items_bottom = 0; if (timer % 20 == 0) // Shift palette around the border of the shop icon @@ -2076,10 +2099,13 @@ void game_shop() } } + memcpy16(&pal_bg_mem[7], &pal_bg_mem[3], 1); + memcpy16(&pal_bg_mem[5], &pal_bg_mem[16], 1); + // Shop selection logic if (selection_x == 0 && top_row) { - memcpy16(&pal_bg_mem[7], &pal_bg_mem[3], 1); + memset16(&pal_bg_mem[5], 0xFFFF, 1); if (key_hit(KEY_A)) @@ -2099,7 +2125,6 @@ void game_shop() else if (selection_x == 0 && !top_row) { memset16(&pal_bg_mem[7], 0xFFFF, 1); - memcpy16(&pal_bg_mem[5], &pal_bg_mem[16], 1); if (key_hit(KEY_A)) { @@ -2107,6 +2132,43 @@ void game_shop() } } + for (int i = 0; i < MAX_SHOP_JOKERS; i++) + { + if (shop_jokers[i] != NULL) + { + if (i == selection_x - 1 && top_row) + { + shop_jokers[i]->selected = true; + + if (key_hit(KEY_A) && jokers_top < MAX_JOKERS_SIZE - 1) + { + joker_push(shop_jokers[i]); + shop_jokers[i]->selected = false; + shop_jokers[i] = NULL; // Remove the joker from the shop + } + } + else + { + shop_jokers[i]->selected = false; + } + } + } + + for (int i = 0; i < MAX_SHOP_JOKERS; i++) + { + if (shop_jokers[i] != NULL) + { + if (shop_jokers[i]->selected) + { + shop_jokers[i]->ty = int2fx(61); + } + else + { + shop_jokers[i]->ty = int2fx(71); // Reset the y position to the default position + } + } + } + break; } case 2: // Outro sequence (menu and shop icon going out of frame) @@ -2342,6 +2404,8 @@ void game_update() } } + held_jokers_update_loop(); + switch (game_state) { case GAME_PLAYING: From 52fa45e426ef0ac5847b38c8b03adacb95f06bff Mon Sep 17 00:00:00 2001 From: cellos51 Date: Fri, 18 Jul 2025 20:38:34 -0400 Subject: [PATCH 04/19] Refactor joker selection logic in game shop --- Makefile | 6 +++--- source/game.c | 20 ++------------------ 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/Makefile b/Makefile index d5c7ebc..cafa2aa 100644 --- a/Makefile +++ b/Makefile @@ -38,9 +38,9 @@ GRAPHICS := graphics #--------------------------------------------------------------------------------- ARCH := -mthumb -mthumb-interwork -CFLAGS := -O3 -Wall \ +CFLAGS := -O3 -Wall \ -mcpu=arm7tdmi -mtune=arm7tdmi \ - -ffast-math -fomit-frame-pointer -funroll-loops -flto \ + -ffast-math -fomit-frame-pointer -funroll-loops \ $(ARCH) CFLAGS += $(INCLUDE) @@ -48,7 +48,7 @@ CFLAGS += $(INCLUDE) CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions ASFLAGS := -g $(ARCH) -LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $*.map) -flto +LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $*.map) #--------------------------------------------------------------------------------- # any extra libraries we wish to link with the project diff --git a/source/game.c b/source/game.c index 14451a7..e699015 100644 --- a/source/game.c +++ b/source/game.c @@ -2138,33 +2138,17 @@ void game_shop() { if (i == selection_x - 1 && top_row) { - shop_jokers[i]->selected = true; + shop_jokers[i]->ty = int2fx(61); if (key_hit(KEY_A) && jokers_top < MAX_JOKERS_SIZE - 1) { joker_push(shop_jokers[i]); - shop_jokers[i]->selected = false; shop_jokers[i] = NULL; // Remove the joker from the shop } } else { - shop_jokers[i]->selected = false; - } - } - } - - for (int i = 0; i < MAX_SHOP_JOKERS; i++) - { - if (shop_jokers[i] != NULL) - { - if (shop_jokers[i]->selected) - { - shop_jokers[i]->ty = int2fx(61); - } - else - { - shop_jokers[i]->ty = int2fx(71); // Reset the y position to the default position + shop_jokers[i]->ty = int2fx(71); } } } From 414bfa2a328f27f515a0b57c91d462e9bc2691f8 Mon Sep 17 00:00:00 2001 From: cellos51 Date: Sun, 20 Jul 2025 20:55:11 -0400 Subject: [PATCH 05/19] Semi functional jokers --- include/game.h | 2 +- include/graphic_utils.h | 2 +- include/joker.h | 1 + source/game.c | 126 ++++++++++++++++++++++++++++++---------- source/joker.c | 21 +++++++ 5 files changed, 119 insertions(+), 33 deletions(-) diff --git a/include/game.h b/include/game.h index 818ae41..e830ecd 100644 --- a/include/game.h +++ b/include/game.h @@ -3,7 +3,7 @@ #define MAX_HAND_SIZE 16 #define MAX_DECK_SIZE 52 -#define MAX_JOKERS_SIZE 5 // This doesn't account for negatives right now. +#define MAX_JOKERS_HELD_SIZE 5 // This doesn't account for negatives right now. #define MAX_SHOP_JOKERS 2 // TODO: Make this dynamic and allow for other items besides jokers #define MAX_SELECTION_SIZE 5 #define FRAMES(x) (((x) + game_speed - 1) / game_speed) diff --git a/include/graphic_utils.h b/include/graphic_utils.h index 06683ea..1ef76c4 100644 --- a/include/graphic_utils.h +++ b/include/graphic_utils.h @@ -15,7 +15,7 @@ * Screen Base Block is the base for the screenblock entries i.e. tilemap * Character Base Block is the base for the tiles themselves */ -#define MAIN_BG_SBB 29 +#define MAIN_BG_SBB 31 #define MAIN_BG_CBB 1 #define TTE_SBB 30 #define TTE_CBB 0 diff --git a/include/joker.h b/include/joker.h index 4f60e76..38948db 100644 --- a/include/joker.h +++ b/include/joker.h @@ -34,6 +34,7 @@ typedef struct u8 modifier; // base, foil, holo, poly, negative u8 value; u8 rarity; + bool proccessed; } Joker; typedef struct // copy of CardObject in card.h diff --git a/source/game.c b/source/game.c index 9a39749..73234e4 100644 --- a/source/game.c +++ b/source/game.c @@ -68,6 +68,9 @@ static int card_focused = 0; static bool sort_by_suit = false; // Stacks +static JokerObject *jokers[MAX_JOKERS_HELD_SIZE] = {NULL}; +static int jokers_top = -1; + static CardObject *played[MAX_SELECTION_SIZE] = {NULL}; static int played_top = -1; @@ -80,8 +83,18 @@ static int deck_top = -1; static Card *discard_pile[MAX_DECK_SIZE] = {NULL}; static int discard_top = -1; -static JokerObject *jokers[MAX_JOKERS] = {NULL}; -static int jokers_top = -1; +// Joker stack +static inline void joker_push(JokerObject *joker) +{ + if (jokers_top >= MAX_JOKERS_HELD_SIZE - 1) return; + jokers[++jokers_top] = joker; +} + +static inline JokerObject *joker_pop() +{ + if (jokers_top < 0) return NULL; + return jokers[jokers_top--]; +} // Played stack static inline void played_push(CardObject *card_object) @@ -122,18 +135,6 @@ static inline Card *discard_pop() return discard_pile[discard_top--]; } -static inline void joker_push(JokerObject *joker) -{ - if (jokers_top >= MAX_JOKERS_SIZE - 1) return; - jokers[++jokers_top] = joker; -} - -static inline JokerObject *joker_pop() -{ - if (jokers_top < 0) return NULL; - return jokers[jokers_top--]; -} - // Consts // Rects left top right bottom @@ -1156,15 +1157,15 @@ static void cards_in_hand_update_loop(bool* discarded_card, int* played_selectio case HAND_SELECT: if (i == card_focused && !hand[i]->selected) { - hand_y -= (10 << FIX_SHIFT); + hand_y -= int2fx(10); } else if (i != card_focused && hand[i]->selected) { - hand_y -= (15 << FIX_SHIFT); + hand_y -= int2fx(15); } else if (i == card_focused && hand[i]->selected) { - hand_y -= (20 << FIX_SHIFT); + hand_y -= int2fx(20); } if (i != card_focused && hand[i]->y > hand_y) @@ -1214,11 +1215,11 @@ static void cards_in_hand_update_loop(bool* discarded_card, int* played_selectio { if (hand_state == HAND_DISCARD) { - hand_y -= (15 << FIX_SHIFT); // Don't raise the card if we're mass discarding, it looks stupid. + hand_y -= int2fx(15); // Don't raise the card if we're mass discarding, it looks stupid. } else { - hand_y += (24 << FIX_SHIFT); + hand_y += int2fx(24); } hand_x = hand_x + (int2fx(i) - int2fx(hand_top) / 2) * -spacing_lut[hand_top]; } @@ -1241,7 +1242,7 @@ static void cards_in_hand_update_loop(bool* discarded_card, int* played_selectio break; case HAND_PLAY: hand_x = hand_x + (int2fx(i) - int2fx(hand_top) / 2) * -spacing_lut[hand_top]; - hand_y += (24 << FIX_SHIFT); + hand_y += int2fx(24); if (hand[i]->selected && *discarded_card == false && timer % FRAMES(10) == 0) { @@ -1417,7 +1418,7 @@ static void cards_in_hand_update_loop(bool* discarded_card, int* played_selectio break; case HAND_PLAYING: // Don't need to do anything here, just wait for the player to select cards hand_x = hand_x + (int2fx(i) - int2fx(hand_top) / 2) * -spacing_lut[hand_top]; - hand_y += (24 << FIX_SHIFT); + hand_y += int2fx(24); break; } @@ -1464,7 +1465,7 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection if (played[i]->selected && played_top - i >= *played_selections) { - played_y -= (10 << FIX_SHIFT); + played_y -= int2fx(10); } break; case PLAY_SCORING: @@ -1501,6 +1502,64 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection if (j == 0 && scored_cards == *played_selections) // Check if it's the last card { tte_erase_rect_wrapper(PLAYED_CARDS_SCORES_RECT); + + for (int k = 0; k <= jokers_top; k++) + { + if (jokers[k]->joker->proccessed == false) + { + IndependentEffect joker_effect = joker_independent_effect(jokers[k]->joker); + IndependentEffect zero_effect = {0}; + + if (memcmp(&joker_effect, &zero_effect, sizeof(IndependentEffect)) != 0) + { + chips += joker_effect.chips; + set_chips(chips); + mult += joker_effect.mult; + set_mult(mult); + // TODO: XMult + money += joker_effect.money; + set_money(money); + // TODO: Retrigger + + + tte_set_pos(fx2int(jokers[k]->x) + 8, 48); // Offset of 16 pixels to center the text on the card + + char score_buffer[12]; + + if (joker_effect.chips > 0) + { + tte_set_special(0xD000); // Blue + snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.chips); + } + else if (joker_effect.mult > 0) + { + tte_set_special(0xE000); // Red + snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.mult); + } + else if (joker_effect.money > 0) + { + tte_set_special(0xC000); // Yellow + snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.money); + } + + tte_write(score_buffer); + + jokers[k]->joker->proccessed = true; // Mark the joker as processed + joker_object_score(jokers[k], SFX_CARD_SELECT); + + return; // Returning was just the easiest way to break out of the loop + } + } + } + + for (int k = 0; k <= jokers_top; k++) + { + if (jokers[k] != NULL) + { + jokers[k]->joker->proccessed = false; // Reset the joker's processed state for the next round + } + } + play_state = PLAY_ENDING; timer = 0; *played_selections = played_top + 1; // Reset the played selections to the top of the played stack @@ -1511,7 +1570,7 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection if (played[i]->selected) { - played_y -= (10 << FIX_SHIFT); + played_y -= int2fx(10); } break; case PLAY_ENDING: // This is the reverse of PLAY_PLAYING. The cards get reset back to their neutral position sequentially @@ -1528,7 +1587,7 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection if (played[i]->selected && played_top - i <= *played_selections - 1) { - played_y -= (10 << FIX_SHIFT); + played_y -= int2fx(10); } break; case PLAY_ENDED: // Basically a copy of HAND_DISCARD @@ -1596,21 +1655,26 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection static void held_jokers_update_loop() { - const int spacing_lut[MAX_HAND_SIZE] = { 26, 26, 26, 26, 20 }; + const int spacing_lut[MAX_JOKERS_HELD_SIZE][MAX_JOKERS_HELD_SIZE] = + { + {0, 0, 0, 0, 0}, + {13, -13, 0, 0, 0}, + {26, 0, -26, 0, 0}, + {39, 13, -13, -39, 0}, + {40, 20, 0, -20, -40} + }; FIXED hand_x = int2fx(108); FIXED hand_y = int2fx(10); - for (int i = 0; i < jokers_top + 1; i++) + for (int i = jokers_top; i >= 0; i--) { - if (jokers[i] == NULL) continue; - - jokers[i]->tx = hand_x + (int2fx(i) - int2fx(jokers_top) / 2) * -spacing_lut[jokers_top]; + jokers[i]->tx = hand_x - int2fx(spacing_lut[jokers_top][i]); jokers[i]->ty = hand_y; if (jokers[i]->selected) { - jokers[i]->ty -= (10 << FIX_SHIFT); + jokers[i]->ty -= int2fx(10); } joker_object_update(jokers[i]); @@ -2146,7 +2210,7 @@ void game_shop() { shop_jokers[i]->ty = int2fx(61); - if (key_hit(KEY_A) && jokers_top < MAX_JOKERS_SIZE - 1) + if (key_hit(KEY_A) && jokers_top < MAX_JOKERS_HELD_SIZE - 1) { joker_push(shop_jokers[i]); shop_jokers[i] = NULL; // Remove the joker from the shop diff --git a/source/joker.c b/source/joker.c index 8e6f392..e5eabd1 100644 --- a/source/joker.c +++ b/source/joker.c @@ -37,6 +37,7 @@ Joker *joker_new(u8 id) joker->modifier = BASE_EDITION; // TODO: Make this random later joker->value = joker_data_lut[id][1]; joker->rarity = joker_data_lut[id][0]; + joker->proccessed = false; return joker; } @@ -48,6 +49,25 @@ void joker_destroy(Joker **joker) *joker = NULL; } +IndependentEffect joker_independent_effect(Joker *joker) +{ + IndependentEffect effect = {0}; + + switch (joker->id) + { + case DEFAULT_JOKER_ID: // Default Joker + effect.mult = 4; + break; + case GREEDY_JOKER_ID: // Greedy Joker + // Nothing because this joker is a scored type + break; + default: + break; + } + + return effect; +} + // JokerObject methods JokerObject *joker_object_new(Joker *joker) { @@ -85,6 +105,7 @@ JokerObject *joker_object_new(Joker *joker) joker_object->trotation = float2fx(0.0f); // Target rotation joker_object->rotation = float2fx(0.0f); joker_object->vrotation = float2fx(0.0f); + joker_object->selected = false; return joker_object; } From a3a4d065b57bcf1b8bcb52e03a27fd126327ae7d Mon Sep 17 00:00:00 2001 From: cellos51 Date: Sun, 20 Jul 2025 22:19:57 -0400 Subject: [PATCH 06/19] Jokers have prices in shop --- source/game.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/source/game.c b/source/game.c index 73234e4..8e218c7 100644 --- a/source/game.c +++ b/source/game.c @@ -184,6 +184,7 @@ static const Rect CHIPS_TEXT_RECT = {8, 80, 32, 88 }; static const Rect MULT_TEXT_RECT = {40, 80, 64, 88 }; static const Rect BLIND_REWARD_RECT = {40, 32, 64, 40 }; static const Rect BLIND_REQ_TEXT_RECT = {32, 24, 64, 32 }; +static const Rect SHOP_PRICES_TEXT_RECT = {72, 56, 192, 160 }; // Rects with UNDEFINED are only used in tte_printf, they need to be fully defined // to be used with tte_erase_rect_wrapper() @@ -1720,6 +1721,8 @@ static void game_round_end_cashout() static void game_shop_create_items(JokerObject *shop_jokers[], bool first_time) { + tte_erase_rect_wrapper(SHOP_PRICES_TEXT_RECT); + for (int i = 0; i < MAX_SHOP_JOKERS; i++) { if (shop_jokers[i] != NULL) @@ -1733,6 +1736,10 @@ static void game_shop_create_items(JokerObject *shop_jokers[], bool first_time) shop_jokers[i]->tx = shop_jokers[i]->x; shop_jokers[i]->ty = int2fx(71); + int x = fx2int(shop_jokers[i]->tx) + TILE_SIZE - (get_digits_even(shop_jokers[i]->joker->value) - 1) * TILE_SIZE; + int y = fx2int(shop_jokers[i]->ty); + tte_printf("#{P:%d,%d; cx:0xC000}$%d", x, y, shop_jokers[i]->joker->value); + if (first_time == false) { shop_jokers[i]->y = shop_jokers[i]->ty; // If it's not the first time, set the y position to the target position @@ -2210,9 +2217,20 @@ void game_shop() { shop_jokers[i]->ty = int2fx(61); - if (key_hit(KEY_A) && jokers_top < MAX_JOKERS_HELD_SIZE - 1) + if (key_hit(KEY_A) && jokers_top < MAX_JOKERS_HELD_SIZE - 1 && money >= shop_jokers[i]->joker->value) { joker_push(shop_jokers[i]); + money -= shop_jokers[i]->joker->value; // Deduct the money spent on the joker + set_money(money); // Update the money display + + Rect joker_price_rect; + joker_price_rect.left = fx2int(shop_jokers[i]->tx); + joker_price_rect.top = fx2int(shop_jokers[i]->ty) + TILE_SIZE; + joker_price_rect.right = joker_price_rect.left + TILE_SIZE * 3; + joker_price_rect.bottom = joker_price_rect.top + TILE_SIZE; + + tte_erase_rect_wrapper(joker_price_rect); + shop_jokers[i] = NULL; // Remove the joker from the shop } } @@ -2234,6 +2252,8 @@ void game_shop() if (timer == 1) { + tte_erase_rect_wrapper(SHOP_PRICES_TEXT_RECT); // Erase the shop prices text + for (int i = 0; i < MAX_SHOP_JOKERS; i++) { if (shop_jokers[i] != NULL) From 99907ec90a2908451672739b16a152fa5413c5d1 Mon Sep 17 00:00:00 2001 From: cellos51 Date: Sun, 20 Jul 2025 22:42:30 -0400 Subject: [PATCH 07/19] Change initial game state from GAME_SHOP to GAME_BLIND_SELECT --- source/game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/game.c b/source/game.c index 8e218c7..db1e109 100644 --- a/source/game.c +++ b/source/game.c @@ -27,7 +27,7 @@ static uint timer = 0; // This might already exist in libtonc but idk so i'm jus static int game_speed = 1; static int background = 0; -static enum GameState game_state = GAME_SHOP; // The current game state, this is used to determine what the game is doing at any given time +static enum GameState game_state = GAME_BLIND_SELECT; // The current game state, this is used to determine what the game is doing at any given time static enum HandState hand_state = HAND_DRAW; static enum PlayState play_state = PLAY_PLAYING; From 1b53a1b077984f0f65f19ca137a840a99a201499 Mon Sep 17 00:00:00 2001 From: cellos51 Date: Mon, 21 Jul 2025 20:09:35 -0400 Subject: [PATCH 08/19] Updated to joker sprite to use the new one by @GreenNinja2525 and changed the way switching backgrounds works to prevent flicker --- graphics/joker_gfx.png | Bin 862 -> 731 bytes source/game.c | 61 +++++++++++++++++++++++------------------ 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/graphics/joker_gfx.png b/graphics/joker_gfx.png index 5623e6089c218348eb80fed3f95d7f6f36197e9f..6fa5d48300f8fde3a1246321d0d42cdf3e81078a 100644 GIT binary patch literal 731 zcmV<10wn#3P)Px%l}SWFR7i=nRxxWLK@|RG6zs1|Cs^d}58QPg*i&*0s1A^V@FKS*vP*r^ezKuoQDty>-^YAh3b6M;~AX$U*@~)4(2mgTy8jhb|NI z=NABw0LvJBFa#eAcRpo}%d{Ghio8H^rnx6znu}H=i4}Jw$=*z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy24YJ`L;wH)0002_L%V+f000SaNLh0L01FcU01FcV0GgZ_00007 zbV*G`2k8e976~X@#=vm^00N>(L_t(o!^Ky}4o)ujt^?0|0RJ`O8KLYndwAu7{v@wqi`0F}W^ty5?0wOM%OLU<^n}wxD)qoC~xJ zIJtHJQh}&cfMs~dwUaO+E0H($fO75h1OOJmf`Oi2f}UTlc$&^uazIvr?IIC-2`tCF z(^|-^l@WR+8Pm<}z0j%g=_EF0?WSNCNb)ZrSi70v>y*~y3acQ^dU`An9s^MnX*xdm zq*XX~V*`W`AY|3R{(Sw0{muetf63!oDBya45C9?N+U9x)IOnM$VeO{k$+48J&&dlx zVHFUFM1P2)NF^Tcv|9|)uHL9|Z(S>K-fR~U2m+w+L;WjAuUa@QNqWi(l o43Np_bbGl*y!z#oy!Q3{KfH-~4wy$>$p8QV07*qoM6N<$f;4P$8vp Date: Mon, 21 Jul 2025 20:27:30 -0400 Subject: [PATCH 09/19] Corrected joker sprite canvas size and fixed money not being erased --- graphics/joker_gfx.png | Bin 731 -> 864 bytes source/game.c | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/graphics/joker_gfx.png b/graphics/joker_gfx.png index 6fa5d48300f8fde3a1246321d0d42cdf3e81078a..8933e0a41671eafa9adced3be5075d669f2392a2 100644 GIT binary patch literal 864 zcmV-m1E2hfP)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007 zbV*G`2k8eE02(x_R65rH00N{*L_t(o!^KyzY9lcaeUc6CuS{ofk=zgDIv?Z%ytiO? z!G>F$#ov%41Pu2DIh?~q2&74|cG`d;?vf^D;v&m+DP5#HSV>pdlC@s#uDs`v0bzMY z(tA5I8c75Oxx0T7cnA6Vo7{@zx>rw`9Y5@pli`u-j`=Wx3{r3-%lDg;a^ zRclHTF?-fT2|6AQG6CFf4{o;yN@=3y60~8T5JD7ypX$Hsh zQ?LmrA!e*z`4h&&K`dtZ_Oa(97=Jv|f{9|B<*YGVfAlUDBB zjR^=LK*;I<`}5-`s+|Q;b*bZ8DB$^k5C9>?j?MKDaL$b(VeO{U$-cC$&&diw?k*q@ ziSr>0LzQ^E(~2de#+uxYIHmaz8W7apufBNv8xR6*^ zLJ59)u!GZs9n?k@T)kCs^;Sh~R8e{WxY<3nDnizkPzMKqlgX>;Y;nNI-^Q$u#;hOHJ)ZTH&Nd90+t5@EWm~V1SyeD? z8(I-aBNRnZCg4^G@RY?k=cxrp2q73_31S5c5mGOwB%|EQ8?#K}t=tESO6I%by}|R| q2k%Bey4~&eGD&>+Px%l}SWFR7i=nRxxWLK@|RG6zs1|Cs^d}58QPg*i&*0s1A^V@FKS*vP*r^ezKuoQDty>-^YAh3b6M;~AX$U*@~)4(2mgTy8jhb|NI z=NABw0LvJBFa#eAcRpo}%d{Ghio8H^rnx6znu}H=i4}Jw$=* Date: Mon, 21 Jul 2025 22:05:31 -0400 Subject: [PATCH 10/19] added shop rerolling and edition pricing for jokers --- include/joker.h | 2 ++ source/game.c | 22 +++++++++++++++++++--- source/joker.c | 11 ++++++++++- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/include/joker.h b/include/joker.h index 38948db..e791792 100644 --- a/include/joker.h +++ b/include/joker.h @@ -17,6 +17,8 @@ #define POLY_EDITION 3 #define NEGATIVE_EDITION 4 +#define MAX_EDITIONS 5 + #define COMMON_JOKER 0 #define UNCOMMON_JOKER 1 #define RARE_JOKER 2 diff --git a/source/game.c b/source/game.c index 6c390b3..c9c3288 100644 --- a/source/game.c +++ b/source/game.c @@ -27,7 +27,7 @@ static uint timer = 0; // This might already exist in libtonc but idk so i'm jus static int game_speed = 1; static int background = 0; -static enum GameState game_state = GAME_BLIND_SELECT; // The current game state, this is used to determine what the game is doing at any given time +static enum GameState game_state = GAME_SHOP; // The current game state, this is used to determine what the game is doing at any given time static enum HandState hand_state = HAND_DRAW; static enum PlayState play_state = PLAY_PLAYING; @@ -50,7 +50,7 @@ static int discards = 0; static int round = 0; static int ante = 1; -static int money = 4; +static int money = 10; static int score = 0; static int temp_score = 0; // This is the score that shows in the same spot as the hand type. static FIXED lerped_score = 0; @@ -198,6 +198,7 @@ static const Rect ROUND_END_BLIND_REWARD_RECT = { 168, 96, UNDEFINED, UNDEF static const Rect ROUND_END_NUM_HANDS_RECT = {88, 116, UNDEFINED, UNDEFINED }; static const Rect HAND_REWARD_RECT = {168, UNDEFINED, UNDEFINED, UNDEFINED }; static const Rect CASHOUT_RECT = {88, 72, UNDEFINED, UNDEFINED }; +static const Rect SHOP_REROLL_RECT = {88, 96, UNDEFINED, UNDEFINED }; //TODO: Properly define and use #define MENU_POP_OUT_ANIM_FRAMES 20 @@ -2082,6 +2083,9 @@ void game_shop() } } + const int reroll_base_cost = 5; // Base cost for rerolling the shop items + static int reroll_cost = reroll_base_cost; + // these are for controlling the shop menu static bool top_row = true; static ushort selection_x = 0; @@ -2151,6 +2155,11 @@ void game_shop() } case 1: // Shop menu input and selection { + if (timer == 1) + { + tte_printf("#{P:%d,%d; cx:0xF000}$%d", SHOP_REROLL_RECT.left, SHOP_REROLL_RECT.top, reroll_cost); + } + // Shop input logic if (key_hit(KEY_UP)) { @@ -2216,9 +2225,15 @@ void game_shop() { memset16(&pal_bg_mem[7], 0xFFFF, 1); - if (key_hit(KEY_A)) + if (key_hit(KEY_A) && money >= reroll_cost) { + money -= reroll_cost; + set_money(money); // Update the money display + game_shop_create_items(shop_jokers, false); + + reroll_cost++; + tte_printf("#{P:%d,%d; cx:0xF000}$%d", SHOP_REROLL_RECT.left, SHOP_REROLL_RECT.top, reroll_cost); } } @@ -2301,6 +2316,7 @@ void game_shop() } default: state = 0; // Reset the state + reroll_cost = reroll_base_cost; selection_x = 0; // Reset the selection top_row = true; // Reset the top row selection diff --git a/source/joker.c b/source/joker.c index e5eabd1..a056ea8 100644 --- a/source/joker.c +++ b/source/joker.c @@ -14,6 +14,15 @@ const static u8 joker_data_lut[MAX_JOKERS][2] = // Rarity, Value {COMMON_JOKER, 5}, // Greedy Joker }; +const static u8 edition_price_lut[MAX_EDITIONS] = +{ + 0, // BASE_EDITION + 2, // FOIL_EDITION + 3, // HOLO_EDITION + 5, // POLY_EDITION + 5, // NEGATIVE_EDITION +}; + /* 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. @@ -35,7 +44,7 @@ Joker *joker_new(u8 id) joker->id = id; // TODO: Make this random later joker->modifier = BASE_EDITION; // TODO: Make this random later - joker->value = joker_data_lut[id][1]; + joker->value = joker_data_lut[id][1] + edition_price_lut[joker->modifier]; // Base value + edition price joker->rarity = joker_data_lut[id][0]; joker->proccessed = false; From c8893c806c3fdaeed06340d496ca10fdba66641b Mon Sep 17 00:00:00 2001 From: cellos51 Date: Tue, 22 Jul 2025 01:42:31 -0400 Subject: [PATCH 11/19] Added @GreenNinja2525's replacement deck icon. Added play hand and discard buttons and changed keybindings. --- graphics/background_blind_select_gfx.png | Bin 3653 -> 3848 bytes graphics/background_gfx.png | Bin 2924 -> 3561 bytes graphics/background_shop_gfx.png | Bin 3429 -> 3623 bytes include/game.h | 7 ++ source/game.c | 123 ++++++++++++++++------- source/main.c | 2 +- 6 files changed, 97 insertions(+), 35 deletions(-) diff --git a/graphics/background_blind_select_gfx.png b/graphics/background_blind_select_gfx.png index 9f5487434fbf585bd475dda4eb3cdcb8bf15b100..d7335e565486ac8bf67a80ce52450d10917a6e47 100644 GIT binary patch delta 3632 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 delta 3460 zcmY+Gc|4SB|Ho%xM&iuajuAyDB9tE4%@m@JCCZiuBT>ihNQKF5iXvGvmXMt?WoLvL z6P1#1$j}Vo*s@F`hRHJHx$F7;ey`W>{^z>y`?{|0^}4>x=lf1L@J1mmA3#Hul>?g* zSrCYL;#rGR7jH4y;~@#sy#3wZIS+^t@v#)j;*`X@yKC*G>eQ_=mut~;XU+c}R4cc5 zeh~Ez^v?Wg zI{R)oRWcAO>@5uI+M)gKHPGCtLsiHQwF7Uso zZt^cI7()>l_@e@-+@m>|;-g&gy}+-H5Fyw2#Y0EE=b|6Y@a)Su|5nUFuL&H8(65v3 zL^65!(=Plk!*tr^bUo;TXz1va)<&Le#-hK|zT|axscj_J1mSrpUsfXh)s(}?LeVBT z_;fie>-b&PgfEo;3!%*=!782Rpw~n~y`D)MywFAA%wySk-KY8+02hzp5om%sy&w=F zyLGPKnOwU^JV$cU?&RIiaR{-oI7He2#JIa|W3%YEwhx{jd%RXJD$6{Obi#Zb=Kp{BU9K>>!N~;#lC$0FgLxl4Nz&^i6dh_Y@Tpfoo(>*L?}$*1EwW(x*_YFSgfcR zdeY!6ozaqgy8(xR3Pu!5I7EB->~A|dt8>%W;0X^nlmS*S?Vy8K1D^F!-$c>ac|u7t zjGi(qXQ>nqjd6`u>gNT07k5?oM`W~%39n9#yDprwmjK^05$E>?(fbyH27X-%yzG_u z`E1wZGv@xomyQ;wZ^$MGC|X|^-Vp5{{D<)21*>tQJ#W!}xZyTs){w)CvZL^!x($1% zjRrb*yGpf+MILi9N0yNHtymRp3#lxmNE#Dj+*MXuvUyv*WxfBv4W%LIje7^x6{v_Y z_Iiw>axj05`5KXN6u4UWets?apS?Qg>N+sne&uUPs{7e4E@VgkKPjJ<))mNz47`v2 zlxUK_bp!>adPB;n$K$BR2gbNrTI=zjQ33<=zfp>lnpEeJs*bwf+>U*3f|_~iq%r8C z@ut3HM1$0PyS|l$ylC6st4|XQr=QP`T*LaxDFX81CdvbA=g&zbvY%&qs`V$e-TtjS z?g3bFwG}nC{nn#XY}MCi3P~9ampf-x?Ck8!_HqP?q4KS|<=j{FY*(D@ZwJg~(DA($<1=^5Xqiw#wfj>d?TwTGD2urt45^bp^QM57V6u3d2ny!v_|SqiBXA(JB` zZ~Y2`!JXDT25IP~U&MpiMSmK88PMA&{>&+$Zf88WE%Z9*;8qwi{_3A$o{0Nac|*#B zCqu^P^KUvDs*%pg_qkK%+nP{U{Kb~V5>GsJw$ArnP&ElwRMJpHoa(3av#_{Wn`Gkn z9kg_X=V!7R9n*3j676(i*mlyBd7bspdGq`N*BtrvmJ~CnH80ZgiL8DS5Zy3nh+zC} zCaZ6bUSe7!QHmLZStSZTUZwU@TXJ1mI!pFBj1KC1C;nl5o~u(I5@}wKINAvFBCS{jyc1$FLQa*Y07fSYq1n(t5t$Z?sTB z*E?nr3@&kRG?TXCgHQrq{VHjU+@$)=hNu6{oQDRQ@Khw>K9`=MZ$exJ=b4PMN zUdlP}jxg5%@lz6BQ}>A9WE5WyyI=}S5Uew>u>92(h8MM{k{yneKc(qyRv26 zhliD4<`%DoR#gtSgMCykm7||fK>>C)?F}g_Sx>%|rupr#x z7^c{Q?}0-o{?ZE|QM8uT)A8~=S|Z&lARuThZ*P3Fd(t`~$*cBGmRw`t{OaAU<�C z{hRxiz7vVi+`T`LeNPmmHaR+stA`7Nd-MC6>fGUk&?MPX{^zg!JZT;v@Z*_Y(ns}j$Vih;THe%wp2q<=7O@~rAt0Kx2ogap;dO~y9>u^p_st^b(f zC(ugWjZ)3n(5DidZ=|qg(yvaGR4tzw{EQ`yTN`a>w~kwbJ(owp#z_CEa@LDhSl|=w zJM`hIiu+0a5DPmE?^RdMm+=keeLtTqH}|)varZ{gY^nWewpWf-G+TkC*)Zf{&MvGj zrHX{YKC;cU9ckLi%0P0eeNCz@&GZQPLK(Z1inMkAAg1{`JJl>b>4IhuuR|aRl8}hp zZYrMJVbzH1aVS^@t@lN7{4FrV3fWR0H!mcgP*(oSgg2cPxn#CI-8aw7&W%T}g?Fmc zTx*5@7Hu>w+t|Jyj$p)exZIk?qbl8T?6tn``Z$Oc+7pPzvAu5~m_p9O>rpn@psgpb zxK*7(m^$aHk2Gt8-B5?2jItrorLorx2cj*R))!V2{w2UD>4&7I*zA(B%7pY0uaIZ8 zjOznI?!Gat&ujmke<|JNaC7tR^x3iN&2x9(()zQSO!NIU)n+o=hjx@dgtU`&1x-3h zq7QM#S-@^ER~Lw!emSr$JEx0fMrv^dmUT9;e8-zSe!H)J#)w~?9(yA{>*M_D`nvDE znYVfFkB52dybwy@fi~MV({3{$%-=G&{pF!Xf!6p|ncH!Dy*{B}?KacOS@{6NgMU!W z4${A-^i(TTLdetm;ABGBY%|uNI;^fo)-|{d$gMXxaF{L;dLYw#A6Th}S6J~vus{o8 z#`{kmR&F&FLZd~Z%lb8K{>EKo$vFdZ^Mtl)RdcB;T3^o-t0pax8~rU{sV%1SB2Bxk zW2e!O#qQr@X%S;Uu=mUz-RIBKwtMl8BTJ#I*9;X(JxEy2`=U}gQxHmS$xjQIZU_SS ze~kCn>C({D8rLTk+ z59DvOHepe*r+g+saVR!H;(DPmjm->4H+@;rYVYzb*ga=_2ad(56on&Fh|t$6+y+M` zxk`^6v6sGLdku0A*)jq@4odVu`<{X(w6hDjuGt09Abs_E$p5g6G1&Wi&)#|vD3p~! zq^KSWPEE9CO$YO|wE8I28^!HPZv@Kxq+;M9@kHk(;!ef5vkhhbN7EERi=R{p2pP;y zIe8p^{$P@_`m8#QtMf!4F_{@&P1uN}_)if`7OQZ(^~I5MR@-hzB6QcEXKw7;k9pBu zfd_oc@+J7va$(OpF=9g}XardS`#+%gf7pj9nH{k%OzluZ&YiqQHCvgV=De>CZ$FOf zhBk-Z#w)#o9zG8N0pJ&nfB^aE=g%b>gMbla^^512@x%NRr5`Pv@JSIts!Jt)o|G(A zPe0%dmp{X;p$L49=vBqsR)6n|(>A*z7d1{6^Y6T0ClceF@D!Y}c|KQD`t5DMH+D4| z_^4g{HQ~O~zQbXcKG`wwM-b%9oFXU9@*>c6PR5nu@wY zhw;SFQmI(t0^eLrCa3h7v`6lxj&W=qV8)IJ_>4WZJDKQH9C6-lAp+6G6%Zi-;`ga{ z+?+m=B?qzSNg-ioOvfIlKCVCle@)J35E~Z@YGQXbqj5xw50}9d!Ag;LL#P2Rhg9}D4~?zW#d`f~_0Y5UdYUJi~3LhVl%;p_#dY}4BsXr6_O90YvMTH0Au IpT^($547{LYXATM diff --git a/graphics/background_gfx.png b/graphics/background_gfx.png index 3517421859e326ec2673be4f66a31365c0ef12cb..31045dbb9beba94136f864ea2d79291288d10edb 100644 GIT binary patch literal 3561 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 literal 2924 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 delta 3238 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 J%S>?h{|y_UC|&>n diff --git a/include/game.h b/include/game.h index e830ecd..5e96206 100644 --- a/include/game.h +++ b/include/game.h @@ -15,6 +15,13 @@ #define BG_ID_SHOP 4 #define BG_ID_BLIND_SELECT 5 +// Input bindings +#define SELECT_CARD KEY_A +#define DESELECT_CARDS KEY_B +#define PEEK_DECK KEY_L // Not implemented +#define SORT_HAND KEY_R +#define PAUSE_GAME KEY_START // Not implemented + enum GameState { GAME_PLAYING, diff --git a/source/game.c b/source/game.c index c9c3288..67956d8 100644 --- a/source/game.c +++ b/source/game.c @@ -27,7 +27,7 @@ static uint timer = 0; // This might already exist in libtonc but idk so i'm jus static int game_speed = 1; static int background = 0; -static enum GameState game_state = GAME_SHOP; // The current game state, this is used to determine what the game is doing at any given time +static enum GameState game_state = GAME_BLIND_SELECT; // The current game state, this is used to determine what the game is doing at any given time static enum HandState hand_state = HAND_DRAW; static enum PlayState play_state = PLAY_PLAYING; @@ -64,6 +64,7 @@ static int cards_drawn = 0; static int hand_selections = 0; static int card_focused = 0; +static int selection_y = 0; static bool sort_by_suit = false; @@ -150,7 +151,7 @@ static const Rect POP_MENU_ANIM_RECT = {9, 7, 24, 31 }; static const Rect SINGLE_BLIND_SELECT_RECT = { 9, 7, 13, 32 }; -static const Rect HAND_BG_RECT_SELECTING = {9, 11, 24, 18 }; +static const Rect HAND_BG_RECT_SELECTING = {9, 11, 24, 17 }; // TODO: Currently unused, remove? //static const Rect HAND_BG_RECT_PLAYING = {9, 14, 24, 18 }; @@ -422,12 +423,10 @@ void change_background(int id) { if (background == BG_ID_CARD_PLAYING) { - for (int i = 0; i <= 2; i++) - { - main_bg_se_move_rect_1_tile_vert(HAND_BG_RECT_SELECTING, SE_UP); - } - + int offset = 12; + memcpy16(&se_mem[MAIN_BG_SBB][SE_ROW_LEN * offset], &background_gfxMap[SE_ROW_LEN * offset], SE_ROW_LEN * 8); tte_erase_rect_wrapper(HAND_SIZE_RECT_PLAYING); + REG_WIN0V = (REG_WIN0V << 8) | 0x80; } else { @@ -452,9 +451,13 @@ void change_background(int id) bg_copy_current_item_to_top_left_panel(); // This would change the palette of the background to match the blind, but the backgroun doesn't use the blind token's exact colors so a different approach is required - memset16(&pal_bg_mem[17], blind_get_color(current_blind, BLIND_BACKGROUND_MAIN_COLOR_INDEX), 1); - memset16(&pal_bg_mem[16], blind_get_color(current_blind, BLIND_BACKGROUND_SECONDARY_COLOR_INDEX), 1); - memset16(&pal_bg_mem[5], blind_get_color(current_blind, BLIND_BACKGROUND_SHADOW_COLOR_INDEX), 1); + memset16(&pal_bg_mem[19], blind_get_color(current_blind, BLIND_BACKGROUND_MAIN_COLOR_INDEX), 1); + memset16(&pal_bg_mem[5], blind_get_color(current_blind, BLIND_BACKGROUND_SECONDARY_COLOR_INDEX), 1); + memset16(&pal_bg_mem[2], blind_get_color(current_blind, BLIND_BACKGROUND_SHADOW_COLOR_INDEX), 1); + + // Copy the Play Hand and Discard button colors to their selection highlights + memcpy16(&pal_bg_mem[1], &pal_bg_mem[7], 1); + memcpy16(&pal_bg_mem[9], &pal_bg_mem[12], 1); } } else if (id == BG_ID_CARD_PLAYING) @@ -465,6 +468,8 @@ void change_background(int id) background = BG_ID_CARD_PLAYING; } + REG_WIN0V = (REG_WIN0V << 8) | 0xA0; + for (int i = 0; i <= 2; i++) { main_bg_se_move_rect_1_tile_vert(HAND_BG_RECT_SELECTING, SE_DOWN); @@ -849,6 +854,7 @@ bool hand_discard() if (hand_state != HAND_SELECT || hand_selections == 0) return false; hand_state = HAND_DISCARD; card_focused = 0; + selection_y = 0; return true; } @@ -857,6 +863,7 @@ bool hand_play() if (hand_state != HAND_SELECT || hand_selections == 0) return false; hand_state = HAND_PLAY; card_focused = 0; + selection_y = 0; return true; } @@ -1007,36 +1014,85 @@ static void game_playing_process_input_and_state() { if (key_hit(KEY_LEFT)) { - hand_set_focus(card_focused + 1); // The reason why this adds 1 is because the hand is drawn from right to left. There is no particular reason for this, it's just how I did it. + if (selection_y == 0) + { + hand_set_focus(card_focused + 1); // The reason why this adds 1 is because the hand is drawn from right to left. There is no particular reason for this, it's just how I did it. + } + else + { + card_focused = -2; // Play button + } } else if (key_hit(KEY_RIGHT)) { - hand_set_focus(card_focused - 1); + if (selection_y == 0) + { + hand_set_focus(card_focused - 1); + } + else + { + card_focused = -3; // Discard button + } + } + else if (key_hit(KEY_UP) && selection_y != 0) + { + selection_y = 0; + card_focused = 0; + } + else if (key_hit(KEY_DOWN) && selection_y != 1) + { + selection_y = 1; + + if (card_focused > hand_top / 2) + { + card_focused = -2; // Play button + } + else + { + card_focused = -3; // Discard button + } } - if (key_hit(KEY_A)) + if (card_focused == -2) // Play button logic + { + memset16(&pal_bg_mem[1], 0xFFFF, 1); + memcpy16(&pal_bg_mem[9], &pal_bg_mem[12], 1); + + if (key_hit(SELECT_CARD) && hands > 0 && hand_play() && card_focused == -2) + { + hands--; + tte_printf("#{P:%d,%d; cx:0xD000}%d", HANDS_TEXT_RECT.left, HANDS_TEXT_RECT.top, hands); + } + } + else if (card_focused == -3) // Discard button logic + { + memcpy16(&pal_bg_mem[1], &pal_bg_mem[7], 1); + memset16(&pal_bg_mem[9], 0xFFFF, 1); + + if (key_hit(SELECT_CARD) && discards > 0 && hand_discard() && card_focused == -3) + { + set_hand(); + discards--; + tte_printf("#{P:%d,%d; cx:0xE000}%d", DISCARDS_TEXT_RECT.left, DISCARDS_TEXT_RECT.top, discards); + } + } + + if (card_focused >= 0) + { + memcpy16(&pal_bg_mem[1], &pal_bg_mem[7], 1); // Play button highlight color + memcpy16(&pal_bg_mem[9], &pal_bg_mem[12], 1); // Discard button highlight color + } + + if (key_hit(SELECT_CARD)) { hand_select(); set_hand(); } - if (key_hit(KEY_B)) + if (key_hit(SORT_HAND)) { hand_change_sort(); } - - if (key_hit(KEY_SELECT) && discards > 0 && hand_discard()) - { - set_hand(); - discards--; - tte_printf("#{P:%d,%d; cx:0xE000}%d", DISCARDS_TEXT_RECT.left, DISCARDS_TEXT_RECT.top, discards); - } - - if (key_hit(KEY_START) && hands > 0 && hand_play()) - { - hands--; - tte_printf("#{P:%d,%d; cx:0xD000}%d", HANDS_TEXT_RECT.left, HANDS_TEXT_RECT.top, hands); - } } else if (play_state == PLAY_ENDING) { @@ -1938,7 +1994,7 @@ void game_round_end() } else if (timer > FRAMES(20)) { - memset16(&pal_bg_mem[17], 0x1483, 1); + memset16(&pal_bg_mem[19], 0x1483, 1); state = 6; timer = 0; } @@ -2027,7 +2083,7 @@ void game_round_end() tte_printf("#{P:%d, %d; cx:0xF000}Cash Out: $%d", CASHOUT_RECT.left, CASHOUT_RECT.top, hands + blind_get_reward(current_blind)); // Print the cash out amount } - else if (timer > FRAMES(40) && key_hit(KEY_A)) // Wait until the player presses A to cash out + else if (timer > FRAMES(40) && key_hit(SELECT_CARD)) // Wait until the player presses A to cash out { game_round_end_cashout(); @@ -2204,10 +2260,9 @@ void game_shop() // Shop selection logic if (selection_x == 0 && top_row) { - memset16(&pal_bg_mem[5], 0xFFFF, 1); - if (key_hit(KEY_A)) + if (key_hit(SELECT_CARD)) { // Go to next blind selection game state state = 2; // Go to the outro sequence state @@ -2225,7 +2280,7 @@ void game_shop() { memset16(&pal_bg_mem[7], 0xFFFF, 1); - if (key_hit(KEY_A) && money >= reroll_cost) + if (key_hit(SELECT_CARD) && money >= reroll_cost) { money -= reroll_cost; set_money(money); // Update the money display @@ -2245,7 +2300,7 @@ void game_shop() { shop_jokers[i]->ty = int2fx(61); - if (key_hit(KEY_A) && jokers_top < MAX_JOKERS_HELD_SIZE - 1 && money >= shop_jokers[i]->joker->value) + if (key_hit(SELECT_CARD) && jokers_top < MAX_JOKERS_HELD_SIZE - 1 && money >= shop_jokers[i]->joker->value) { joker_push(shop_jokers[i]); money -= shop_jokers[i]->joker->value; // Deduct the money spent on the joker @@ -2370,7 +2425,7 @@ void game_blind_select() { top_row = false; } - else if (key_hit(KEY_A)) + else if (key_hit(SELECT_CARD)) { if (top_row) // Blind selected { diff --git a/source/main.c b/source/main.c index 1cb00e2..bd7b046 100644 --- a/source/main.c +++ b/source/main.c @@ -71,7 +71,7 @@ void init() int win1_left = 72; int win1_top = 44; int win1_right = 200; - int win1_bottom = 160; + int win1_bottom = 128; int win2_left = 72; int win2_top = 0; From 9086aa0f59acb264329e93c92dd52cca296f847f Mon Sep 17 00:00:00 2001 From: cellos51 Date: Tue, 22 Jul 2025 02:26:22 -0400 Subject: [PATCH 12/19] Fixed hands and discards being infinite and palette issue --- source/game.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/source/game.c b/source/game.c index 67956d8..c247a47 100644 --- a/source/game.c +++ b/source/game.c @@ -50,7 +50,7 @@ static int discards = 0; static int round = 0; static int ante = 1; -static int money = 10; +static int money = 4; static int score = 0; static int temp_score = 0; // This is the score that shows in the same spot as the hand type. static FIXED lerped_score = 0; @@ -64,6 +64,7 @@ static int cards_drawn = 0; static int hand_selections = 0; static int card_focused = 0; +static int previous_card_focused = 0; static int selection_y = 0; static bool sort_by_suit = false; @@ -423,7 +424,7 @@ void change_background(int id) { if (background == BG_ID_CARD_PLAYING) { - int offset = 12; + int offset = 11; memcpy16(&se_mem[MAIN_BG_SBB][SE_ROW_LEN * offset], &background_gfxMap[SE_ROW_LEN * offset], SE_ROW_LEN * 8); tte_erase_rect_wrapper(HAND_SIZE_RECT_PLAYING); REG_WIN0V = (REG_WIN0V << 8) | 0x80; @@ -696,7 +697,7 @@ static void print_hand_type(const char* hand_type_str) { if (hand_type_str == NULL) return; // NULL-checking paranoia - tte_printf("#{P:%d,%d;}%s", HAND_TYPE_RECT.left, HAND_TYPE_RECT.top, hand_type_str); + tte_printf("#{P:%d,%d; cx:0xF000}%s", HAND_TYPE_RECT.left, HAND_TYPE_RECT.top, hand_type_str); } void set_hand() @@ -852,18 +853,12 @@ int hand_get_max_size() bool hand_discard() { if (hand_state != HAND_SELECT || hand_selections == 0) return false; - hand_state = HAND_DISCARD; - card_focused = 0; - selection_y = 0; return true; } bool hand_play() { if (hand_state != HAND_SELECT || hand_selections == 0) return false; - hand_state = HAND_PLAY; - card_focused = 0; - selection_y = 0; return true; } @@ -1037,11 +1032,12 @@ static void game_playing_process_input_and_state() else if (key_hit(KEY_UP) && selection_y != 0) { selection_y = 0; - card_focused = 0; + card_focused = previous_card_focused; } else if (key_hit(KEY_DOWN) && selection_y != 1) { selection_y = 1; + previous_card_focused = card_focused; if (card_focused > hand_top / 2) { @@ -1058,10 +1054,13 @@ static void game_playing_process_input_and_state() memset16(&pal_bg_mem[1], 0xFFFF, 1); memcpy16(&pal_bg_mem[9], &pal_bg_mem[12], 1); - if (key_hit(SELECT_CARD) && hands > 0 && hand_play() && card_focused == -2) + if (key_hit(SELECT_CARD) && hands > 0 && hand_play()) { - hands--; - tte_printf("#{P:%d,%d; cx:0xD000}%d", HANDS_TEXT_RECT.left, HANDS_TEXT_RECT.top, hands); + hand_state = HAND_PLAY; + card_focused = 0; + selection_y = 0; + previous_card_focused = 0; + set_hands(--hands); } } else if (card_focused == -3) // Discard button logic @@ -1069,10 +1068,14 @@ static void game_playing_process_input_and_state() memcpy16(&pal_bg_mem[1], &pal_bg_mem[7], 1); memset16(&pal_bg_mem[9], 0xFFFF, 1); - if (key_hit(SELECT_CARD) && discards > 0 && hand_discard() && card_focused == -3) + if (key_hit(SELECT_CARD) && discards > 0 && hand_discard()) { + hand_state = HAND_DISCARD; + card_focused = 0; + selection_y = 0; + previous_card_focused = 0; + set_hands(--discards); set_hand(); - discards--; tte_printf("#{P:%d,%d; cx:0xE000}%d", DISCARDS_TEXT_RECT.left, DISCARDS_TEXT_RECT.top, discards); } } @@ -2090,7 +2093,6 @@ void game_round_end() state = 8; // Go to the next state timer = 0; // Reset the timer - memset16(&pal_bg_mem[6], 0x0174, 1); obj_hide(round_end_blind_token->obj); // Hide the blind token object tte_erase_rect_wrapper(BLIND_TOKEN_TEXT_RECT); // Erase the blind token text } From 793da72465f7f4831e226cdc0332ee7fc9b48ca7 Mon Sep 17 00:00:00 2001 From: cellos51 Date: Tue, 22 Jul 2025 15:45:29 -0400 Subject: [PATCH 13/19] added co-pilot's fixes --- include/joker.h | 2 +- source/game.c | 11 +++++------ source/joker.c | 2 +- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/include/joker.h b/include/joker.h index e791792..3ae1906 100644 --- a/include/joker.h +++ b/include/joker.h @@ -36,7 +36,7 @@ typedef struct u8 modifier; // base, foil, holo, poly, negative u8 value; u8 rarity; - bool proccessed; + bool processed; } Joker; typedef struct // copy of CardObject in card.h diff --git a/source/game.c b/source/game.c index c247a47..1c7c0be 100644 --- a/source/game.c +++ b/source/game.c @@ -30,6 +30,7 @@ static int background = 0; static enum GameState game_state = GAME_BLIND_SELECT; // The current game state, this is used to determine what the game is doing at any given time static enum HandState hand_state = HAND_DRAW; static enum PlayState play_state = PLAY_PLAYING; +static int state = 0; // General state variable, used for switch statements in each game state related function static enum HandType hand_type = NONE; @@ -480,7 +481,7 @@ void change_background(int id) } else if (id == BG_ID_ROUND_END) { - if (background != BG_ID_CARD_SELECTING || background != BG_ID_CARD_PLAYING) + if (background != BG_ID_CARD_SELECTING && background != BG_ID_CARD_PLAYING) { change_background(BG_ID_CARD_SELECTING); background = BG_ID_ROUND_END; @@ -1579,7 +1580,7 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection for (int k = 0; k <= jokers_top; k++) { - if (jokers[k]->joker->proccessed == false) + if (jokers[k]->joker->processed == false) { IndependentEffect joker_effect = joker_independent_effect(jokers[k]->joker); IndependentEffect zero_effect = {0}; @@ -1618,7 +1619,7 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection tte_write(score_buffer); - jokers[k]->joker->proccessed = true; // Mark the joker as processed + jokers[k]->joker->processed = true; // Mark the joker as processed joker_object_score(jokers[k], SFX_CARD_SELECT); return; // Returning was just the easiest way to break out of the loop @@ -1630,7 +1631,7 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection { if (jokers[k] != NULL) { - jokers[k]->joker->proccessed = false; // Reset the joker's processed state for the next round + jokers[k]->joker->processed = false; // Reset the joker's processed state for the next round } } @@ -1871,8 +1872,6 @@ void game_round_end_cleanup() void game_round_end() { - static int state = 0; - static int blind_reward = 0; static int hand_reward = 0; static int interest_reward = 0; diff --git a/source/joker.c b/source/joker.c index a056ea8..888721a 100644 --- a/source/joker.c +++ b/source/joker.c @@ -46,7 +46,7 @@ Joker *joker_new(u8 id) joker->modifier = BASE_EDITION; // TODO: Make this random later joker->value = joker_data_lut[id][1] + edition_price_lut[joker->modifier]; // Base value + edition price joker->rarity = joker_data_lut[id][0]; - joker->proccessed = false; + joker->processed = false; return joker; } From b583755985eb69e1be61cc0bc25bae3261877e1d Mon Sep 17 00:00:00 2001 From: cellos51 Date: Tue, 22 Jul 2025 15:53:49 -0400 Subject: [PATCH 14/19] Refactored background change logic and removed unused static state variables in game functions --- source/game.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/source/game.c b/source/game.c index 1c7c0be..7ecf45d 100644 --- a/source/game.c +++ b/source/game.c @@ -423,12 +423,13 @@ void change_background(int id) } else if (id == BG_ID_CARD_SELECTING) { + tte_erase_rect_wrapper(HAND_SIZE_RECT_PLAYING); + REG_WIN0V = (REG_WIN0V << 8) | 0x80; // Set window 0 top to 128 + if (background == BG_ID_CARD_PLAYING) { int offset = 11; memcpy16(&se_mem[MAIN_BG_SBB][SE_ROW_LEN * offset], &background_gfxMap[SE_ROW_LEN * offset], SE_ROW_LEN * 8); - tte_erase_rect_wrapper(HAND_SIZE_RECT_PLAYING); - REG_WIN0V = (REG_WIN0V << 8) | 0x80; } else { @@ -439,8 +440,6 @@ void change_background(int id) GRIT_CPY(&tile8_mem[MAIN_BG_CBB], background_gfxTiles); // Deadass i have no clue how any of these memory things work but I just messed with them until stuff worked GRIT_CPY(&se_mem[MAIN_BG_SBB], background_gfxMap); - tte_erase_rect_wrapper(HAND_SIZE_RECT_PLAYING); - if (current_blind == BIG_BLIND) // Change text and palette depending on blind type { main_bg_se_copy_rect(BIG_BLIND_TITLE_SRC_RECT, TOP_LEFT_BLIND_TITLE_POINT); @@ -470,7 +469,7 @@ void change_background(int id) background = BG_ID_CARD_PLAYING; } - REG_WIN0V = (REG_WIN0V << 8) | 0xA0; + REG_WIN0V = (REG_WIN0V << 8) | 0xA0; // Set window 0 bottom to 160 for (int i = 0; i <= 2; i++) { @@ -2126,9 +2125,6 @@ void game_round_end() void game_shop() { change_background(BG_ID_SHOP); - - // TODO: Later move these static variables somewhere else so they can be reused for each game state - static int state = 0; static JokerObject *shop_jokers[MAX_SHOP_JOKERS] = {NULL}; @@ -2391,8 +2387,6 @@ void game_shop() void game_blind_select() { - static int state = 0; - static bool top_row = true; // There's only one row in this game state, but this is here for consistency with the shop state if we make these variables global or something switch (state) // I'm only using magic numbers here for the sake of simplicity since it's just sequential, but you can replace them with named constants or enums if it makes it clearer From c536d78976068963ce9e9596fd16207aa619eaae Mon Sep 17 00:00:00 2001 From: cellos51 Date: Tue, 22 Jul 2025 17:35:52 -0400 Subject: [PATCH 15/19] Minor refactor to avoid the use of variable --- source/game.c | 74 +++++++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/source/game.c b/source/game.c index 7ecf45d..b881fdf 100644 --- a/source/game.c +++ b/source/game.c @@ -65,7 +65,6 @@ static int cards_drawn = 0; static int hand_selections = 0; static int card_focused = 0; -static int previous_card_focused = 0; static int selection_y = 0; static bool sort_by_suit = false; @@ -1007,6 +1006,8 @@ static void game_playing_process_input_and_state() { if (hand_state == HAND_SELECT) { + static bool discard_button_highlighted = false; // true = play button highlighted, false = discard button highlighted + if (key_hit(KEY_LEFT)) { if (selection_y == 0) @@ -1015,7 +1016,7 @@ static void game_playing_process_input_and_state() } else { - card_focused = -2; // Play button + discard_button_highlighted = false; // Play button } } else if (key_hit(KEY_RIGHT)) @@ -1026,61 +1027,62 @@ static void game_playing_process_input_and_state() } else { - card_focused = -3; // Discard button + discard_button_highlighted = true; // Discard button } } else if (key_hit(KEY_UP) && selection_y != 0) { selection_y = 0; - card_focused = previous_card_focused; } else if (key_hit(KEY_DOWN) && selection_y != 1) { selection_y = 1; - previous_card_focused = card_focused; if (card_focused > hand_top / 2) { - card_focused = -2; // Play button + discard_button_highlighted = false; // Play button } else { - card_focused = -3; // Discard button + discard_button_highlighted = true; // Discard button } } - if (card_focused == -2) // Play button logic + if (selection_y == 1) // On row of play/discard buttons { - memset16(&pal_bg_mem[1], 0xFFFF, 1); - memcpy16(&pal_bg_mem[9], &pal_bg_mem[12], 1); - - if (key_hit(SELECT_CARD) && hands > 0 && hand_play()) + if (discard_button_highlighted == false) // Play button logic { - hand_state = HAND_PLAY; - card_focused = 0; - selection_y = 0; - previous_card_focused = 0; - set_hands(--hands); + memset16(&pal_bg_mem[1], 0xFFFF, 1); + memcpy16(&pal_bg_mem[9], &pal_bg_mem[12], 1); + + if (key_hit(SELECT_CARD) && hands > 0 && hand_play()) + { + hand_state = HAND_PLAY; + card_focused = 0; + selection_y = 0; + previous_card_focused = 0; + set_hands(--hands); + } } - } - else if (card_focused == -3) // Discard button logic - { - memcpy16(&pal_bg_mem[1], &pal_bg_mem[7], 1); - memset16(&pal_bg_mem[9], 0xFFFF, 1); - - if (key_hit(SELECT_CARD) && discards > 0 && hand_discard()) + else // Discard button logic { - hand_state = HAND_DISCARD; - card_focused = 0; - selection_y = 0; - previous_card_focused = 0; - set_hands(--discards); - set_hand(); - tte_printf("#{P:%d,%d; cx:0xE000}%d", DISCARDS_TEXT_RECT.left, DISCARDS_TEXT_RECT.top, discards); + memcpy16(&pal_bg_mem[1], &pal_bg_mem[7], 1); + memset16(&pal_bg_mem[9], 0xFFFF, 1); + + if (key_hit(SELECT_CARD) && discards > 0 && hand_discard()) + { + hand_state = HAND_DISCARD; + card_focused = 0; + selection_y = 0; + previous_card_focused = 0; + set_hands(--discards); + set_hand(); + tte_printf("#{P:%d,%d; cx:0xE000}%d", DISCARDS_TEXT_RECT.left, DISCARDS_TEXT_RECT.top, discards); + } } } - if (card_focused >= 0) + if (selection_y == 0) // On row of cards { memcpy16(&pal_bg_mem[1], &pal_bg_mem[7], 1); // Play button highlight color memcpy16(&pal_bg_mem[9], &pal_bg_mem[12], 1); // Discard button highlight color @@ -1229,15 +1231,17 @@ static void cards_in_hand_update_loop(bool* discarded_card, int* played_selectio hand_x = hand_x + (int2fx(i) - int2fx(hand_top) / 2) * -spacing_lut[hand_top]; break; case HAND_SELECT: - if (i == card_focused && !hand[i]->selected) + bool is_focused = (i == card_focused && selection_y == 0); + + if (is_focused && !hand[i]->selected) { hand_y -= int2fx(10); } - else if (i != card_focused && hand[i]->selected) + else if (!is_focused && hand[i]->selected) { hand_y -= int2fx(15); } - else if (i == card_focused && hand[i]->selected) + else if (is_focused && hand[i]->selected) { hand_y -= int2fx(20); } From e51e08322fb4f16f1b5703fd6e402d6f5c7cc56e Mon Sep 17 00:00:00 2001 From: cellos51 Date: Tue, 22 Jul 2025 17:39:33 -0400 Subject: [PATCH 16/19] Fixed compilation error and removed another static variable --- source/game.c | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/source/game.c b/source/game.c index b881fdf..6e2b5dd 100644 --- a/source/game.c +++ b/source/game.c @@ -64,7 +64,7 @@ static int hand_size = 8; // Default hand size is 8 static int cards_drawn = 0; static int hand_selections = 0; -static int card_focused = 0; +static int selection_x = 0; static int selection_y = 0; static bool sort_by_suit = false; @@ -805,7 +805,7 @@ void card_draw() void hand_set_focus(int index) { if (index < 0 || index > hand_top || hand_state != HAND_SELECT) return; - card_focused = index; + selection_x = index; mm_sound_effect sfx_focus = {{SFX_CARD_FOCUS}, 1024 + rand() % 512, 0, 255, 128,}; mmEffectEx(&sfx_focus); @@ -813,11 +813,11 @@ void hand_set_focus(int index) void hand_select() { - if (hand_state != HAND_SELECT || hand[card_focused] == NULL) return; + if (hand_state != HAND_SELECT || hand[selection_x] == NULL) return; - if (hand[card_focused]->selected) + if (hand[selection_x]->selected) { - hand[card_focused]->selected = false; + hand[selection_x]->selected = false; hand_selections--; mm_sound_effect sfx_select = {{SFX_CARD_SELECT}, 1024, 0, 255, 128,}; @@ -825,7 +825,7 @@ void hand_select() } else if (hand_selections < MAX_SELECTION_SIZE) { - hand[card_focused]->selected = true; + hand[selection_x]->selected = true; hand_selections++; mm_sound_effect sfx_deselect = {{SFX_CARD_DESELECT}, 1024, 0, 255, 128,}; @@ -1012,7 +1012,7 @@ static void game_playing_process_input_and_state() { if (selection_y == 0) { - hand_set_focus(card_focused + 1); // The reason why this adds 1 is because the hand is drawn from right to left. There is no particular reason for this, it's just how I did it. + hand_set_focus(selection_x + 1); // The reason why this adds 1 is because the hand is drawn from right to left. There is no particular reason for this, it's just how I did it. } else { @@ -1023,7 +1023,7 @@ static void game_playing_process_input_and_state() { if (selection_y == 0) { - hand_set_focus(card_focused - 1); + hand_set_focus(selection_x - 1); } else { @@ -1038,7 +1038,7 @@ static void game_playing_process_input_and_state() { selection_y = 1; - if (card_focused > hand_top / 2) + if (selection_x > hand_top / 2) { discard_button_highlighted = false; // Play button } @@ -1058,9 +1058,8 @@ static void game_playing_process_input_and_state() if (key_hit(SELECT_CARD) && hands > 0 && hand_play()) { hand_state = HAND_PLAY; - card_focused = 0; + selection_x = 0; selection_y = 0; - previous_card_focused = 0; set_hands(--hands); } } @@ -1072,9 +1071,8 @@ static void game_playing_process_input_and_state() if (key_hit(SELECT_CARD) && discards > 0 && hand_discard()) { hand_state = HAND_DISCARD; - card_focused = 0; + selection_x = 0; selection_y = 0; - previous_card_focused = 0; set_hands(--discards); set_hand(); tte_printf("#{P:%d,%d; cx:0xE000}%d", DISCARDS_TEXT_RECT.left, DISCARDS_TEXT_RECT.top, discards); @@ -1231,7 +1229,7 @@ static void cards_in_hand_update_loop(bool* discarded_card, int* played_selectio hand_x = hand_x + (int2fx(i) - int2fx(hand_top) / 2) * -spacing_lut[hand_top]; break; case HAND_SELECT: - bool is_focused = (i == card_focused && selection_y == 0); + bool is_focused = (i == selection_x && selection_y == 0); if (is_focused && !hand[i]->selected) { @@ -1246,7 +1244,7 @@ static void cards_in_hand_update_loop(bool* discarded_card, int* played_selectio hand_y -= int2fx(20); } - if (i != card_focused && hand[i]->y > hand_y) + if (i != selection_x && hand[i]->y > hand_y) { hand[i]->y = hand_y; hand[i]->vy = 0; @@ -2145,7 +2143,6 @@ void game_shop() // these are for controlling the shop menu static bool top_row = true; - static ushort selection_x = 0; // temp variables for future implementation const ushort max_items_top = MAX_SHOP_JOKERS; From 62db2815dd720dcf468a5286ce61bd61a5105887 Mon Sep 17 00:00:00 2001 From: cellos51 Date: Tue, 22 Jul 2025 17:43:52 -0400 Subject: [PATCH 17/19] removed another static variable and improved boss blind selection --- source/game.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/source/game.c b/source/game.c index 6e2b5dd..72d0045 100644 --- a/source/game.c +++ b/source/game.c @@ -2141,9 +2141,6 @@ void game_shop() const int reroll_base_cost = 5; // Base cost for rerolling the shop items static int reroll_cost = reroll_base_cost; - // these are for controlling the shop menu - static bool top_row = true; - // temp variables for future implementation const ushort max_items_top = MAX_SHOP_JOKERS; const ushort max_items_bottom = 0; @@ -2217,7 +2214,7 @@ void game_shop() // Shop input logic if (key_hit(KEY_UP)) { - top_row = true; + selection_y = 0; if (selection_x > max_items_top) { @@ -2226,7 +2223,7 @@ void game_shop() } else if (key_hit(KEY_DOWN)) { - top_row = false; + selection_y = 1; if (selection_x > max_items_bottom) { @@ -2242,11 +2239,11 @@ void game_shop() } else if (key_hit(KEY_RIGHT)) { - if (top_row && selection_x < max_items_top) + if (selection_y == 0 && selection_x < max_items_top) { selection_x++; } - else if (!top_row && selection_x < max_items_bottom) + else if (selection_y == 1 && selection_x < max_items_bottom) { selection_x++; } @@ -2256,7 +2253,7 @@ void game_shop() memcpy16(&pal_bg_mem[5], &pal_bg_mem[16], 1); // Shop selection logic - if (selection_x == 0 && top_row) + if (selection_x == 0 && selection_y == 0) { memset16(&pal_bg_mem[5], 0xFFFF, 1); @@ -2274,7 +2271,7 @@ void game_shop() // And I don't care enough to fix it right now. } } - else if (selection_x == 0 && !top_row) + else if (selection_x == 0 && selection_y == 1) { memset16(&pal_bg_mem[7], 0xFFFF, 1); @@ -2294,7 +2291,7 @@ void game_shop() { if (shop_jokers[i] != NULL) { - if (i == selection_x - 1 && top_row) + if (i == selection_x - 1 && selection_y == 0) { shop_jokers[i]->ty = int2fx(61); @@ -2372,7 +2369,7 @@ void game_shop() reroll_cost = reroll_base_cost; selection_x = 0; // Reset the selection - top_row = true; // Reset the top row selection + selection_y = 0; // Reset the selection for (int i = 0; i < MAX_SHOP_JOKERS; i++) { @@ -2388,8 +2385,6 @@ void game_shop() void game_blind_select() { - static bool top_row = true; // There's only one row in this game state, but this is here for consistency with the shop state if we make these variables global or something - switch (state) // I'm only using magic numbers here for the sake of simplicity since it's just sequential, but you can replace them with named constants or enums if it makes it clearer { case 0: // Intro sequence (menu coming into frame) @@ -2412,18 +2407,23 @@ void game_blind_select() } case 1: // Blind select input and selection { + if (timer == 1 && current_blind == BOSS_BLIND) + { + selection_y = 0; + } + // Blind select input logic if (key_hit(KEY_UP)) { - top_row = true; + selection_y = 0; } - else if (key_hit(KEY_DOWN)) + else if (key_hit(KEY_DOWN) && current_blind != BOSS_BLIND) { - top_row = false; + selection_y = 1; } else if (key_hit(SELECT_CARD)) { - if (top_row) // Blind selected + if (selection_y == 0) // Blind selected { state++; timer = 0; @@ -2451,7 +2451,7 @@ void game_blind_select() } } - if (top_row) + if (selection_y == 0) { memset16(&pal_bg_mem[18], 0xFFFF, 1); memcpy16(&pal_bg_mem[10], &pal_bg_mem[5], 1); @@ -2540,7 +2540,7 @@ void game_blind_select() default: state = 0; timer = 0; - top_row = true; + selection_y = 0; background = UNDEFINED; game_set_state(GAME_PLAYING); break; From 1b4267893d2146bd02d79eba19935b284c3b1075 Mon Sep 17 00:00:00 2001 From: cellos51 Date: Tue, 22 Jul 2025 20:00:05 -0400 Subject: [PATCH 18/19] Added greedy joker and refactor joker system --- graphics/joker_gfx.png | Bin 864 -> 1551 bytes include/card.h | 6 +-- include/joker.h | 17 ++----- source/card.c | 2 +- source/game.c | 100 +++++++++++++++++++---------------------- source/joker.c | 61 ++++++++++++++++++++++--- 6 files changed, 109 insertions(+), 77 deletions(-) diff --git a/graphics/joker_gfx.png b/graphics/joker_gfx.png index 8933e0a41671eafa9adced3be5075d669f2392a2..3fa9161ca241e5257bf1a27916b5f21ac96b7d77 100644 GIT binary patch delta 1459 zcmV;k1x)(j29FFOiBL{Q4GJ0x0000DNk~Le0000$0000W2nGNE0HU3Kgpnaq2><{9 z008+zyMK{dHY5V9F`GebV$yfM z*sE3@xqbRLjGR(pL=5h~U?f= z=#J*P%>`7~dAV?97`LCk!b!D+lWGaKpT5F{E5l80Po&u;!j@%F*L6rz;{dhu>wg3E zj*S2SDD7x`C?r#Wt^<s%<@d1TlI`>q1!75(|aZ!G5d zBq%Q|Gne;!n729$Vf?RezIN0QF*id9{L=M4X7|~b#=2iS++pK*zAW4c?k8S4Ylzr^zdv*l z_lGW`Z>0lE?|QNHt`~hP9ZU}Z&y~BL%b&;-c2_{JK)fFM4d)Noi^yU z9#1k|yUqeRrG$9Cj2jyP0DoZL@q8KaeA%G8u@OK#Uv_*JaO?rJngohpVInDJ(s}g& zzJ#6a=*~#ktDbqo;QkB5^Nr?5JdZj_jm=M5&>Zo+58#2&xR}kLR;#fwwZz)B3*-|Q zfv4S_x${HYUTTui4$?_Ss3ifq1tGew8=9tOz#7*&^Gkwx1w|{%?|k`ry)wJ&uL@Wd zZAp?0TVzp3nZm$tE^yQT=k?T6BocgYg}sWGaIk7@dePJSh5v;BFYgE3yce+3^WPtx z0CM}-#H4Ey{J1?I`~0_64)~;Bj#?dQz38{DJ`vtuvU`Yj%cuBy{9lTM&KP~}v_k*@ N002ovPDHLkV1hc9vPu8| delta 767 zcmVP_XRne!$k7wa&woY|Wz*pL{vJ5zaJxOF3x5A9 z1WYMaYf2I^d)7n=Ivx%(0o-m6Znp_g@5i99b9@q5!)bP%eDg@&qq){UnwTdm|eHIUHe@_3xUggU<@!MTTnl@&IMWo zoZPwqDM6GHqcAwbCL0Q>XfC#szVP<5%}S}5T8fDiy7#g5JO5OB_oAz|&N(#gKGt-B)|F5P2Y{2wtLjVr$>cRQ z#+HQDHq=Q-a0$3hLJ~*|L$R|Unxir6sprite = sprite_new(ATTR0_SQUARE | ATTR0_4BPP | ATTR0_AFF, ATTR1_SIZE_32, card_sprite_lut[card_object->card->suit][card_object->card->rank], 0, layer); } -void card_object_score(CardObject *card_object, mm_word sound_id) +void card_object_shake(CardObject *card_object, mm_word sound_id) { card_object->vscale = float2fx(0.3f); // Scale down the card when it's scored card_object->vrotation = float2fx(8.0f); // Rotate the card when it's scored diff --git a/source/game.c b/source/game.c index 72d0045..02577f9 100644 --- a/source/game.c +++ b/source/game.c @@ -1549,82 +1549,70 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection { // So pretend "played_selections" is now called "scored_cards" and it counts the number of cards that have been scored int scored_cards = 0; - for (int j = played_top; j >= 0; j--) + for (int j = 0; j <= played_top; j++) { - if (played[played_top - j]->selected) + tte_erase_rect_wrapper(PLAYED_CARDS_SCORES_RECT); + + if (*played_selections > 0) { - scored_cards++; + for (int k = 0; k <= jokers_top; k++) + { + if (joker_object_score(jokers[k], played[*played_selections - 1]->card, &chips, &mult, NULL, &money, NULL)) // NULLs aren't implemented yet + { + set_chips(chips); + set_mult(mult); + set_money(money); + + return; + } + } + } + + if (played[j]->selected) + { + scored_cards = j + 1; // Count the number of cards that have been scored if (scored_cards > *played_selections) { - tte_erase_rect_wrapper(PLAYED_CARDS_SCORES_RECT); - tte_set_pos(fx2int(played[played_top - j]->x) + 8, 48); // Offset of 16 pixels to center the text on the card + for (int k = 0; k <= jokers_top; k++) + { + if (jokers[k] != NULL) + { + jokers[k]->joker->processed = false; // Reset the joker's processed state for the next score + } + } + + tte_set_pos(fx2int(played[j]->x) + 8, 48); // Offset of 16 pixels to center the text on the card tte_set_special(0xD000); // Set text color to blue from background memory // Write the score to a character buffer variable char score_buffer[5]; // Assuming the maximum score is 99, we need 4 characters (2 digits + null terminator) - snprintf(score_buffer, sizeof(score_buffer), "+%d", card_get_value(played[played_top - j]->card)); + snprintf(score_buffer, sizeof(score_buffer), "+%d", card_get_value(played[j]->card)); tte_write(score_buffer); *played_selections = scored_cards; - card_object_score(played[played_top - j], SFX_CARD_SELECT); + card_object_shake(played[j], SFX_CARD_SELECT); // Relocated card scoring logic here - chips += card_get_value(played[played_top - j]->card); + chips += card_get_value(played[j]->card); set_chips(chips); + break; } } - if (j == 0 && scored_cards == *played_selections) // Check if it's the last card + if (j == played_top && scored_cards == *played_selections) // Check if it's the last card { tte_erase_rect_wrapper(PLAYED_CARDS_SCORES_RECT); - for (int k = 0; k <= jokers_top; k++) + for (int k = 0; k <= jokers_top; k++) // Independent joker scoring loop { - if (jokers[k]->joker->processed == false) - { - IndependentEffect joker_effect = joker_independent_effect(jokers[k]->joker); - IndependentEffect zero_effect = {0}; + if (joker_object_score(jokers[k], NULL, &chips, &mult, NULL, &money, NULL)) // NULLs aren't implemented yet + { + set_chips(chips); + set_mult(mult); + set_money(money); - if (memcmp(&joker_effect, &zero_effect, sizeof(IndependentEffect)) != 0) - { - chips += joker_effect.chips; - set_chips(chips); - mult += joker_effect.mult; - set_mult(mult); - // TODO: XMult - money += joker_effect.money; - set_money(money); - // TODO: Retrigger - - - tte_set_pos(fx2int(jokers[k]->x) + 8, 48); // Offset of 16 pixels to center the text on the card - - char score_buffer[12]; - - if (joker_effect.chips > 0) - { - tte_set_special(0xD000); // Blue - snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.chips); - } - else if (joker_effect.mult > 0) - { - tte_set_special(0xE000); // Red - snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.mult); - } - else if (joker_effect.money > 0) - { - tte_set_special(0xC000); // Yellow - snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.money); - } - - tte_write(score_buffer); - - jokers[k]->joker->processed = true; // Mark the joker as processed - joker_object_score(jokers[k], SFX_CARD_SELECT); - - return; // Returning was just the easiest way to break out of the loop - } + return; // Returning was just the easiest way to break out of the loop } } @@ -1805,7 +1793,9 @@ static void game_shop_create_items(JokerObject *shop_jokers[], bool first_time) joker_object_destroy(&shop_jokers[i]); // Destroy the joker object if it exists } - shop_jokers[i] = joker_object_new(joker_new(DEFAULT_JOKER_ID)); + u8 joker_id = random() % MAX_JOKERS; + + shop_jokers[i] = joker_object_new(joker_new(joker_id)); shop_jokers[i]->x = int2fx(120 + i * 32); shop_jokers[i]->y = int2fx(160); shop_jokers[i]->tx = shop_jokers[i]->x; @@ -1818,7 +1808,7 @@ static void game_shop_create_items(JokerObject *shop_jokers[], bool first_time) if (first_time == false) { shop_jokers[i]->y = shop_jokers[i]->ty; // If it's not the first time, set the y position to the target position - joker_object_score(shop_jokers[i], UNDEFINED); // Give the joker a little wiggle animation + joker_object_shake(shop_jokers[i], UNDEFINED); // Give the joker a little wiggle animation } sprite_position(shop_jokers[i]->sprite, fx2int(shop_jokers[i]->x), fx2int(shop_jokers[i]->y)); diff --git a/source/joker.c b/source/joker.c index 888721a..8af79c0 100644 --- a/source/joker.c +++ b/source/joker.c @@ -4,9 +4,11 @@ #include "joker_gfx.h" #include "graphic_utils.h" #include "card.h" +#include "soundbank.h" #include #include +#include const static u8 joker_data_lut[MAX_JOKERS][2] = // Rarity, Value { @@ -58,17 +60,21 @@ void joker_destroy(Joker **joker) *joker = NULL; } -IndependentEffect joker_independent_effect(Joker *joker) +JokerEffect joker_get_score_effect(Joker *joker, Card *scored_card) { - IndependentEffect effect = {0}; + JokerEffect effect = {0}; switch (joker->id) { case DEFAULT_JOKER_ID: // Default Joker + if (scored_card != NULL) break; // Joker is independent, no effect effect.mult = 4; break; case GREEDY_JOKER_ID: // Greedy Joker - // Nothing because this joker is a scored type + if (scored_card->suit == DIAMONDS) // If the scored card is a diamond + { + effect.mult = 3; + } break; default: break; @@ -139,8 +145,53 @@ void joker_object_update(JokerObject *joker_object) card_object_update(card_object); } -void joker_object_score(JokerObject *joker_object, mm_word sound_id) // Another derived function of card_object +void joker_object_shake(JokerObject *joker_object, mm_word sound_id) { CardObject *card_object = (CardObject *)joker_object; - card_object_score(card_object, sound_id); + card_object_shake(card_object, sound_id); } + +bool joker_object_score(JokerObject *joker_object, Card* scored_card, int *chips, int *mult, int *xmult, int *money, bool *retrigger) +{ + if (joker_object->joker->processed == true) return false; // If the joker has already been processed, return false + + JokerEffect joker_effect = joker_get_score_effect(joker_object->joker, scored_card); + + if (memcmp(&joker_effect, &(JokerEffect){0}, sizeof(JokerEffect)) != 0) + { + *chips += joker_effect.chips; + *mult += joker_effect.mult; + // TODO: XMult + *money += joker_effect.money; + // TODO: Retrigger + + tte_set_pos(fx2int(joker_object->x) + 8, 48); // Offset of 16 pixels to center the text on the card + + char score_buffer[12]; + + if (joker_effect.chips > 0) + { + tte_set_special(0xD000); // Blue + snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.chips); + } + else if (joker_effect.mult > 0) + { + tte_set_special(0xE000); // Red + snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.mult); + } + else if (joker_effect.money > 0) + { + tte_set_special(0xC000); // Yellow + snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.money); + } + + tte_write(score_buffer); + + joker_object->joker->processed = true; // Mark the joker as processed + joker_object_shake(joker_object, SFX_CARD_SELECT); // TODO: Add a sound effect for scoring the joker + + return true; + } + + return false; +} \ No newline at end of file From c025d354680248062ae7574dbfa8ae184658a54b Mon Sep 17 00:00:00 2001 From: cellos51 Date: Tue, 22 Jul 2025 20:07:51 -0400 Subject: [PATCH 19/19] null check on greedy joker --- source/joker.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/joker.c b/source/joker.c index 8af79c0..b765690 100644 --- a/source/joker.c +++ b/source/joker.c @@ -71,7 +71,7 @@ JokerEffect joker_get_score_effect(Joker *joker, Card *scored_card) effect.mult = 4; break; case GREEDY_JOKER_ID: // Greedy Joker - if (scored_card->suit == DIAMONDS) // If the scored card is a diamond + if (scored_card != NULL && scored_card->suit == DIAMONDS) // If the scored card is a diamond { effect.mult = 3; }