From dcaf025e63d00c349a07f8df84629360d4077097 Mon Sep 17 00:00:00 2001 From: theturtlemafia Date: Sat, 2 Aug 2025 00:27:35 -0400 Subject: [PATCH 01/13] allows multiple joker-effect popup texts --- source/joker.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/source/joker.c b/source/joker.c index c302faf..8dc6573 100644 --- a/source/joker.c +++ b/source/joker.c @@ -235,33 +235,44 @@ bool joker_object_score(JokerObject *joker_object, Card* scored_card, int *chips *money += joker_effect.money; // TODO: Retrigger - tte_set_pos(fx2int(joker_object->sprite_object->x) + 8, JOKER_SCORE_TEXT_Y); // Offset of 16 pixels to center the text on the card - - char score_buffer[12]; - + int cursorPosX = fx2int(joker_object->sprite_object->x) + 8; // Offset of 16 pixels to center the text on the card if (joker_effect.chips > 0) { + char score_buffer[12]; + tte_set_pos(cursorPosX, JOKER_SCORE_TEXT_Y); tte_set_special(0xD000); // Blue snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.chips); + tte_write(score_buffer); + cursorPosX += 30; } - else if (joker_effect.mult > 0) + if (joker_effect.mult > 0) { + char score_buffer[12]; + tte_set_pos(cursorPosX, JOKER_SCORE_TEXT_Y); tte_set_special(0xE000); // Red snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.mult); + tte_write(score_buffer); + cursorPosX += 30; } - else if (joker_effect.xmult > 0) + if (joker_effect.xmult > 0) { + char score_buffer[12]; + tte_set_pos(cursorPosX, JOKER_SCORE_TEXT_Y); tte_set_special(0xE000); // Red snprintf(score_buffer, sizeof(score_buffer), "X%d", joker_effect.xmult); + tte_write(score_buffer); + cursorPosX += 30; } - else if (joker_effect.money > 0) + if (joker_effect.money > 0) { + char score_buffer[12]; + tte_set_pos(cursorPosX, JOKER_SCORE_TEXT_Y); tte_set_special(0xC000); // Yellow snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.money); + tte_write(score_buffer); + cursorPosX += 30; } - 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 From ee0591562f6a73e717d6c4473a04c2369b8d54a0 Mon Sep 17 00:00:00 2001 From: theturtlemafia Date: Sat, 2 Aug 2025 01:14:41 -0400 Subject: [PATCH 02/13] add blue joker --- include/game.h | 2 ++ source/game.c | 4 ++++ source/joker_effects.c | 10 ++++++++++ 3 files changed, 16 insertions(+) diff --git a/include/game.h b/include/game.h index 02c3b01..b0ae27a 100644 --- a/include/game.h +++ b/include/game.h @@ -82,6 +82,8 @@ int get_played_top(void); JokerObject** get_jokers(void); int get_jokers_top(void); +int get_deck_top(void); + int get_num_discards_remaining(void); #endif // GAME_H \ No newline at end of file diff --git a/source/game.c b/source/game.c index 5e4ffe1..c69ce9c 100644 --- a/source/game.c +++ b/source/game.c @@ -264,6 +264,10 @@ int get_jokers_top(void) { return jokers_top; } +int get_deck_top(void) { + return deck_top; +} + int get_num_discards_remaining(void) { return discards; } diff --git a/source/joker_effects.c b/source/joker_effects.c index 7a2eaf3..d070bd7 100644 --- a/source/joker_effects.c +++ b/source/joker_effects.c @@ -287,6 +287,15 @@ static JokerEffect blackboard_joker_effect(Joker *joker, Card *scored_card) { return effect; } +static JokerEffect blue_joker_effect(Joker *joker, Card *scored_card) { + JokerEffect effect = {0}; + if (scored_card != NULL) + return effect; // if card != null, we are not at the end-phase of scoring yet + effect.chips = (get_deck_top() + 1) * 2; + + return effect; +} + const JokerInfo joker_registry[] = { { COMMON_JOKER, 2, default_joker_effect }, // DEFAULT_JOKER_ID = 0 { COMMON_JOKER, 5, greedy_joker_effect }, // GREEDY_JOKER_ID = 1 @@ -311,6 +320,7 @@ const JokerInfo joker_registry[] = { { COMMON_JOKER, 5, banner_joker_effect }, { COMMON_JOKER, 5, mystic_summit_joker_effect }, { UNCOMMON_JOKER, 6, blackboard_joker_effect }, + { COMMON_JOKER, 5, blue_joker_effect }, }; static const size_t joker_registry_size = NUM_ELEM_IN_ARR(joker_registry); From 174050d81347ab6e68ce44f060b452038f36cd40 Mon Sep 17 00:00:00 2001 From: theturtlemafia Date: Sat, 2 Aug 2025 12:18:55 -0400 Subject: [PATCH 03/13] add fist, parking, business card, scholar, scary face, abstact, bull, smiley, even, odd jokers --- include/game.h | 2 +- source/game.c | 4 + source/joker_effects.c | 166 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+), 1 deletion(-) diff --git a/include/game.h b/include/game.h index b0ae27a..681780e 100644 --- a/include/game.h +++ b/include/game.h @@ -83,7 +83,7 @@ JokerObject** get_jokers(void); int get_jokers_top(void); int get_deck_top(void); - int get_num_discards_remaining(void); +int get_money(void); #endif // GAME_H \ No newline at end of file diff --git a/source/game.c b/source/game.c index c69ce9c..5691f75 100644 --- a/source/game.c +++ b/source/game.c @@ -272,6 +272,10 @@ int get_num_discards_remaining(void) { return discards; } +int get_money(void) { + return money; +} + // General functions void set_seed(int seed) { diff --git a/source/joker_effects.c b/source/joker_effects.c index d070bd7..c9aa15b 100644 --- a/source/joker_effects.c +++ b/source/joker_effects.c @@ -291,11 +291,167 @@ static JokerEffect blue_joker_effect(Joker *joker, Card *scored_card) { JokerEffect effect = {0}; if (scored_card != NULL) return effect; // if card != null, we are not at the end-phase of scoring yet + effect.chips = (get_deck_top() + 1) * 2; return effect; } +static JokerEffect raised_fist_joker_effect(Joker *joker, Card *scored_card) { + JokerEffect effect = {0}; + if (scored_card != NULL) + return effect; // if card != null, we are not at the end-phase of scoring yet + + // Find the lowest rank card in hand + // Aces are always considered high value, even in an ace-low straight + u8 lowest_value = 99; + CardObject** hand = get_hand_array(); + int hand_top = get_hand_top(); + for (int i = 0; i < hand_top; i++ ) + { + u8 value = card_get_value(hand[i]->card); + if (lowest_value > value) + lowest_value = value; + } + + if (lowest_value != 99) + effect.mult = lowest_value * 2; + + return effect; +} + +static JokerEffect reserved_parking_joker_effect(Joker *joker, Card *scored_card) { + JokerEffect effect = {0}; + if (scored_card != NULL) + return effect; // if card != null, we are not at the end-phase of scoring yet + + CardObject** hand = get_hand_array(); + int hand_top = get_hand_top(); + for (int i = 0; i < hand_top; i++ ) + { + switch (hand[i]->card->rank) { + case KING: case QUEEN: case JACK: + if (random() % 2 == 0) + effect.money += 1; + default: + break; + } + } + + return effect; +} + +static JokerEffect business_card_joker_effect(Joker *joker, Card *scored_card) { + JokerEffect effect = {0}; + if (scored_card == NULL) + return effect; + + switch (scored_card->rank) { + case KING: case QUEEN: case JACK: + if (random() % 2 == 0) + effect.money = 1; + default: + break; + } + + effect.chips = 1; + + return effect; +} + +static JokerEffect scholar_joker_effect(Joker *joker, Card *scored_card) { + JokerEffect effect = {0}; + if (scored_card == NULL) + return effect; + + if (scored_card->rank == ACE) { + effect.chips = 20; + effect.mult = 4; + } + + return effect; +} + +static JokerEffect scary_face_joker_effect(Joker *joker, Card *scored_card) { + JokerEffect effect = {0}; + if (scored_card == NULL) + return effect; + + switch (scored_card->rank) { + case KING: case QUEEN: case JACK: + effect.chips = 30; + default: + break; + } + + return effect; +} + +static JokerEffect abstract_joker_effect(Joker *joker, Card *scored_card) { + JokerEffect effect = {0}; + if (scored_card != NULL) + return effect; // if card != null, we are not at the end-phase of scoring yet + + // +1 xmult per occupied joker slot + int jokers_top = get_jokers_top(); + effect.mult = (jokers_top + 1) * 3; + + return effect; +} + +static JokerEffect bull_joker_effect(Joker *joker, Card *scored_card) { + JokerEffect effect = {0}; + if (scored_card != NULL) + return effect; // if card != null, we are not at the end-phase of scoring yet + + effect.chips = get_money() * 2; + + return effect; +} + +static JokerEffect smiley_face_joker_effect(Joker *joker, Card *scored_card) { + JokerEffect effect = {0}; + if (scored_card == NULL) + return effect; + + switch (scored_card->rank) { + case KING: case QUEEN: case JACK: + effect.mult = 5; + default: + break; + } + + return effect; +} + +static JokerEffect even_steven_joker_effect(Joker *joker, Card *scored_card) { + JokerEffect effect = {0}; + if (scored_card == NULL) + return effect; + + if (card_get_value(scored_card) % 2 == 0) { + switch (scored_card->rank) { + case KING: case QUEEN: case JACK: + break; + default: + effect.mult = 4; + } + } + + return effect; +} + +static JokerEffect odd_todd_joker_effect(Joker *joker, Card *scored_card) { + JokerEffect effect = {0}; + if (scored_card == NULL) + return effect; + + if (card_get_value(scored_card) % 2 == 1) // todo test ace + effect.chips = 31; + + return effect; +} + const JokerInfo joker_registry[] = { { COMMON_JOKER, 2, default_joker_effect }, // DEFAULT_JOKER_ID = 0 { COMMON_JOKER, 5, greedy_joker_effect }, // GREEDY_JOKER_ID = 1 @@ -321,6 +477,16 @@ const JokerInfo joker_registry[] = { { COMMON_JOKER, 5, mystic_summit_joker_effect }, { UNCOMMON_JOKER, 6, blackboard_joker_effect }, { COMMON_JOKER, 5, blue_joker_effect }, + { COMMON_JOKER, 5, raised_fist_joker_effect }, + { COMMON_JOKER, 6, reserved_parking_joker_effect }, + { COMMON_JOKER, 4, business_card_joker_effect }, + { COMMON_JOKER, 4, scholar_joker_effect }, + { COMMON_JOKER, 4, scary_face_joker_effect }, + { COMMON_JOKER, 4, abstract_joker_effect }, + { UNCOMMON_JOKER, 6, bull_joker_effect}, + { COMMON_JOKER, 4, smiley_face_joker_effect }, + { COMMON_JOKER, 4, even_steven_joker_effect }, + { COMMON_JOKER, 4, odd_todd_joker_effect }, }; static const size_t joker_registry_size = NUM_ELEM_IN_ARR(joker_registry); From 445ea788a435482f5cd2df2620378608c5417945 Mon Sep 17 00:00:00 2001 From: theturtlemafia Date: Sat, 2 Aug 2025 13:28:28 -0400 Subject: [PATCH 04/13] fix error from rebase --- source/game.c | 49 ++++++++++++------------------------------------- 1 file changed, 12 insertions(+), 37 deletions(-) diff --git a/source/game.c b/source/game.c index 5691f75..a4cdce2 100644 --- a/source/game.c +++ b/source/game.c @@ -164,6 +164,18 @@ int get_jokers_top(void) { return jokers_top; } +int get_deck_top(void) { + return deck_top; +} + +int get_num_discards_remaining(void) { + return discards; +} + +int get_money(void) { + return money; +} + // Consts // Rects left top right bottom @@ -239,43 +251,6 @@ static const Rect SHOP_REROLL_RECT = {88, 96, UNDEFINED, UNDEFI #define PITCH_STEP_UNDISCARD_SFX 2*PITCH_STEP_DRAW_SFX // Naming the stage where cards return from the discard pile to the deck "undiscard" -// get-functions, for other files to view game state (mainly for jokers) -CardObject **get_hand_array(void) { - return hand; -} - -int get_hand_top(void) { - return hand_top; -} - -CardObject **get_played_array(void) { - return played; -} - -int get_played_top(void) { - return played_top; -} - -JokerObject **get_jokers(void) { - return jokers; -} - -int get_jokers_top(void) { - return jokers_top; -} - -int get_deck_top(void) { - return deck_top; -} - -int get_num_discards_remaining(void) { - return discards; -} - -int get_money(void) { - return money; -} - // General functions void set_seed(int seed) { From 254efd7841bc8f127694642c49b204fdb5add9a5 Mon Sep 17 00:00:00 2001 From: theturtlemafia Date: Sat, 9 Aug 2025 19:23:47 -0400 Subject: [PATCH 05/13] implement feedback --- include/card.h | 2 ++ include/graphic_utils.h | 1 + source/joker.c | 8 ++++---- source/joker_effects.c | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/include/card.h b/include/card.h index 2b003b6..9831013 100644 --- a/include/card.h +++ b/include/card.h @@ -35,6 +35,8 @@ #define NUM_RANKS 13 #define RANK_OFFSET 2 // Because the first rank is 2 and ranks start at 0 +#define IMPOSSIBLY_HIGH_CARD_VALUE 99 + // Card types typedef struct { diff --git a/include/graphic_utils.h b/include/graphic_utils.h index 6c169c7..be20b1a 100644 --- a/include/graphic_utils.h +++ b/include/graphic_utils.h @@ -42,6 +42,7 @@ // Tile size in pixels, both height and width as tiles are square #define TILE_SIZE 8 +#define EFFECT_TEXT_SEPARATION_AMOUNT 32; // If we need to show multiple effects at once typedef struct { diff --git a/source/joker.c b/source/joker.c index 8dc6573..3ae8a7a 100644 --- a/source/joker.c +++ b/source/joker.c @@ -243,7 +243,7 @@ bool joker_object_score(JokerObject *joker_object, Card* scored_card, int *chips tte_set_special(0xD000); // Blue snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.chips); tte_write(score_buffer); - cursorPosX += 30; + cursorPosX += EFFECT_TEXT_SEPARATION_AMOUNT; } if (joker_effect.mult > 0) { @@ -252,7 +252,7 @@ bool joker_object_score(JokerObject *joker_object, Card* scored_card, int *chips tte_set_special(0xE000); // Red snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.mult); tte_write(score_buffer); - cursorPosX += 30; + cursorPosX += EFFECT_TEXT_SEPARATION_AMOUNT; } if (joker_effect.xmult > 0) { @@ -261,7 +261,7 @@ bool joker_object_score(JokerObject *joker_object, Card* scored_card, int *chips tte_set_special(0xE000); // Red snprintf(score_buffer, sizeof(score_buffer), "X%d", joker_effect.xmult); tte_write(score_buffer); - cursorPosX += 30; + cursorPosX += EFFECT_TEXT_SEPARATION_AMOUNT; } if (joker_effect.money > 0) { @@ -270,7 +270,7 @@ bool joker_object_score(JokerObject *joker_object, Card* scored_card, int *chips tte_set_special(0xC000); // Yellow snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.money); tte_write(score_buffer); - cursorPosX += 30; + cursorPosX += EFFECT_TEXT_SEPARATION_AMOUNT; } joker_object->joker->processed = true; // Mark the joker as processed diff --git a/source/joker_effects.c b/source/joker_effects.c index c9aa15b..df434f4 100644 --- a/source/joker_effects.c +++ b/source/joker_effects.c @@ -304,7 +304,7 @@ static JokerEffect raised_fist_joker_effect(Joker *joker, Card *scored_card) { // Find the lowest rank card in hand // Aces are always considered high value, even in an ace-low straight - u8 lowest_value = 99; + u8 lowest_value = IMPOSSIBLY_HIGH_CARD_VALUE; CardObject** hand = get_hand_array(); int hand_top = get_hand_top(); for (int i = 0; i < hand_top; i++ ) From b63559862f714b88fbbdb7fa79df88d9be5efdf9 Mon Sep 17 00:00:00 2001 From: MeirGavish Date: Sat, 16 Aug 2025 12:41:11 +0300 Subject: [PATCH 06/13] renamed int_list.c to list.c --- source/{int_list.c => list.c} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename source/{int_list.c => list.c} (100%) diff --git a/source/int_list.c b/source/list.c similarity index 100% rename from source/int_list.c rename to source/list.c From 92fa27905996e760e0f21b2ee1f1ad01df8ba0bb Mon Sep 17 00:00:00 2001 From: MeirGavish Date: Sat, 16 Aug 2025 13:18:44 +0300 Subject: [PATCH 07/13] Refactored jokers stack to be a list (AI used) --- include/game.h | 7 +++-- source/game.c | 63 +++++++++++++++++++++--------------------- source/joker_effects.c | 10 +++++-- source/list.c | 1 + 4 files changed, 44 insertions(+), 37 deletions(-) diff --git a/include/game.h b/include/game.h index 55e8730..ef2a827 100644 --- a/include/game.h +++ b/include/game.h @@ -74,6 +74,10 @@ enum HandType void game_init(); void game_update(); +// Forward declaration +struct List; +typedef struct List List; + // Utility functions for other files typedef struct CardObject CardObject; // forward declaration, actually declared in card.h typedef struct JokerObject JokerObject; @@ -82,8 +86,7 @@ int get_hand_top(void); int hand_get_size(void); CardObject** get_played_array(void); int get_played_top(void); -JokerObject** get_jokers(void); -int get_jokers_top(void); +List* get_jokers(void); int get_num_discards_remaining(void); diff --git a/source/game.c b/source/game.c index 0c3f9f0..9588453 100644 --- a/source/game.c +++ b/source/game.c @@ -76,10 +76,9 @@ static int selection_y = 0; static bool sort_by_suit = false; -// Stacks -static JokerObject *jokers[MAX_JOKERS_HELD_SIZE] = {NULL}; -static int jokers_top = -1; +static List *jokers = NULL; +// Stacks static CardObject *played[MAX_SELECTION_SIZE] = {NULL}; static int played_top = -1; @@ -92,17 +91,11 @@ static int deck_top = -1; static Card *discard_pile[MAX_DECK_SIZE] = {NULL}; static int discard_top = -1; -// Joker stack +// Joker stack using List 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--]; + if (list_get_size(jokers) >= MAX_JOKERS_HELD_SIZE) return; + list_append(jokers, joker); } // Played stack @@ -166,15 +159,11 @@ int get_played_top(void) { return played_top; } -JokerObject **get_jokers(void) { +List *get_jokers(void) { return jokers; } -int get_jokers_top(void) { - return jokers_top; -} - -List* jokers_available_to_shop; // List of joker IDs +List *jokers_available_to_shop; // List of joker IDs // Consts @@ -1021,6 +1010,10 @@ void game_init() { jokers_available_to_shop_init(); + // Initialize jokers list + if (jokers) list_destroy(&jokers); + jokers = list_new(MAX_JOKERS_HELD_SIZE); + hands = max_hands; discards = max_discards; @@ -1614,9 +1607,10 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection if (*played_selections > 0) { - for (int k = 0; k <= jokers_top; k++) + for (int k = 0; k < list_get_size(jokers); k++) { - if (joker_object_score(jokers[k], played[*played_selections - 1]->card, &chips, &mult, NULL, &money, NULL)) // NULLs aren't implemented yet + JokerObject *joker = list_get(jokers, k); + if (joker_object_score(joker, played[*played_selections - 1]->card, &chips, &mult, NULL, &money, NULL)) // NULLs aren't implemented yet { display_chips(chips); display_mult(mult); @@ -1632,11 +1626,12 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection scored_cards = j + 1; // Count the number of cards that have been scored if (scored_cards > *played_selections) { - for (int k = 0; k <= jokers_top; k++) + for (int k = 0; k < list_get_size(jokers); k++) { - if (jokers[k] != NULL) + JokerObject *joker = list_get(jokers, k); + if (joker != NULL) { - jokers[k]->joker->processed = false; // Reset the joker's processed state for the next score + joker->joker->processed = false; // Reset the joker's processed state for the next score } } @@ -1663,9 +1658,10 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection { tte_erase_rect_wrapper(PLAYED_CARDS_SCORES_RECT); - for (int k = 0; k <= jokers_top; k++) // Independent joker scoring loop + for (int k = 0; k <= list_get_size(jokers); k++) // Independent joker scoring loop { - if (joker_object_score(jokers[k], NULL, &chips, &mult, NULL, &money, NULL)) // NULLs aren't implemented yet + JokerObject *joker = list_get(jokers, k); + if (joker_object_score(joker, NULL, &chips, &mult, NULL, &money, NULL)) // NULLs aren't implemented yet { display_chips(chips); display_mult(mult); @@ -1675,11 +1671,12 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection } } - for (int k = 0; k <= jokers_top; k++) + for (int k = 0; k <= list_get_size(jokers); k++) { - if (jokers[k] != NULL) + JokerObject *joker = list_get(jokers, k); + if (joker != NULL) { - jokers[k]->joker->processed = false; // Reset the joker's processed state for the next round + joker->joker->processed = false; // Reset the joker's processed state for the next round } } @@ -1788,12 +1785,14 @@ static void held_jokers_update_loop() FIXED hand_x = int2fx(108); FIXED hand_y = int2fx(10); + int jokers_top = list_get_size(jokers) - 1; for (int i = jokers_top; i >= 0; i--) { - jokers[i]->sprite_object->tx = hand_x - int2fx(spacing_lut[jokers_top][i]); - jokers[i]->sprite_object->ty = hand_y; + JokerObject *joker = list_get(jokers, i); + joker->sprite_object->tx = hand_x - int2fx(spacing_lut[jokers_top][i]); + joker->sprite_object->ty = hand_y; - joker_object_update(jokers[i]); + joker_object_update(joker); } } @@ -2297,7 +2296,7 @@ static void shop_top_row_on_key_hit(SelectionGrid* selection_grid, Selection* se int shop_joker_idx = selection->x - 1; // - 1 to account for next round button JokerObject *joker_object = list_get(shop_jokers, shop_joker_idx); if (joker_object == NULL - || jokers_top >= MAX_JOKERS_HELD_SIZE - 1 + || list_get_size(jokers) >= MAX_JOKERS_HELD_SIZE || money < joker_object->joker->value) { return; diff --git a/source/joker_effects.c b/source/joker_effects.c index bab4512..84d3046 100644 --- a/source/joker_effects.c +++ b/source/joker_effects.c @@ -2,6 +2,7 @@ #include "joker.h" #include "util.h" #include "hand_analysis.h" +#include "list.h" #include static JokerEffect default_joker_effect(Joker *joker, Card *scored_card) { @@ -191,16 +192,19 @@ static JokerEffect joker_stencil_effect(Joker *joker, Card *scored_card) { if (scored_card != NULL) return effect; // if card != null, we are not at the end-phase of scoring yet + List* jokers = get_jokers(); + // +1 xmult per empty joker slot... - int num_jokers = get_jokers_top() + 1; + int num_jokers = list_get_size(jokers); effect.xmult = (MAX_JOKERS_HELD_SIZE) - num_jokers; // ...and also each stencil_joker adds +1 xmult - JokerObject** jokers = get_jokers(); + for (int i = 0; i < num_jokers; i++ ) { - if (jokers[i]->joker->id == JOKER_STENCIL_ID) + JokerObject* joker_object = list_get(jokers, i); + if (joker_object->joker->id == JOKER_STENCIL_ID) effect.xmult++; } diff --git a/source/list.c b/source/list.c index 1a39dc4..c57324a 100644 --- a/source/list.c +++ b/source/list.c @@ -1,5 +1,6 @@ #include #include +#include #include "list.h" #include "util.h" From 64274ae465911af1dcd920b9293858959fa99c06 Mon Sep 17 00:00:00 2001 From: MeirGavish Date: Sat, 16 Aug 2025 15:38:34 +0300 Subject: [PATCH 08/13] Added joker selling (still no animation) + fixed SpriteObject constructor focused bug --- include/game.h | 3 +- include/graphic_utils.h | 13 +++-- include/joker.h | 1 + source/game.c | 126 ++++++++++++++++++++++++++++++---------- source/joker.c | 11 +++- source/selection_grid.c | 3 + source/sprite.c | 1 + 7 files changed, 120 insertions(+), 38 deletions(-) diff --git a/include/game.h b/include/game.h index ef2a827..c4789ac 100644 --- a/include/game.h +++ b/include/game.h @@ -23,6 +23,7 @@ #define PEEK_DECK KEY_L // Not implemented #define SORT_HAND KEY_R #define PAUSE_GAME KEY_START // Not implemented +#define SELL_KEY KEY_L enum GameState { @@ -90,4 +91,4 @@ List* get_jokers(void); int get_num_discards_remaining(void); -#endif // GAME_H \ No newline at end of file +#endif // GAME_H diff --git a/include/graphic_utils.h b/include/graphic_utils.h index 2e80828..5162b16 100644 --- a/include/graphic_utils.h +++ b/include/graphic_utils.h @@ -34,11 +34,16 @@ #define SE_COL_LEN 32 // Since y direction goes from the top of the screen to the bottom -#define SE_UP -1 -#define SE_DOWN 1 +#define SCREEN_UP -1 +#define SCREEN_DOWN 1 +#define SCREEN_LEFT -1 +#define SCREEN_RIGHT 1 -#define OVERFLOW_LEFT -1 -#define OVERFLOW_RIGHT 1 +#define SE_UP SCREEN_UP +#define SE_DOWN SCREEN_DOWN + +#define OVERFLOW_LEFT SCREEN_LEFT +#define OVERFLOW_RIGHT SCREEN_RIGHT // Tile size in pixels, both height and width as tiles are square #define TILE_SIZE 8 diff --git a/include/joker.h b/include/joker.h index c959883..3ee358b 100644 --- a/include/joker.h +++ b/include/joker.h @@ -78,6 +78,7 @@ 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 JokerEffect joker_get_score_effect(Joker *joker, Card *scored_card); +int joker_get_sell_value(const Joker* joker); JokerObject *joker_object_new(Joker *joker); void joker_object_destroy(JokerObject **joker_object); diff --git a/source/game.c b/source/game.c index 9588453..fb37eac 100644 --- a/source/game.c +++ b/source/game.c @@ -91,13 +91,6 @@ static int deck_top = -1; static Card *discard_pile[MAX_DECK_SIZE] = {NULL}; static int discard_top = -1; -// Joker stack using List -static inline void joker_push(JokerObject *joker) -{ - if (list_get_size(jokers) >= MAX_JOKERS_HELD_SIZE) return; - list_append(jokers, joker); -} - // Played stack static inline void played_push(CardObject *card_object) { @@ -229,6 +222,9 @@ static const Rect ROUND_END_NUM_HANDS_RECT = {88, 116, UNDEFINED, UNDEF 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 }; + +static const BG_POINT HELD_JOKERS_POS = {108, 10}; + #define ITEM_SHOP_Y 71 // TODO: Needs to be a rect? //TODO: Properly define and use @@ -1782,15 +1778,13 @@ static void held_jokers_update_loop() {40, 20, 0, -20, -40} }; - FIXED hand_x = int2fx(108); - FIXED hand_y = int2fx(10); + FIXED hand_x = int2fx(HELD_JOKERS_POS.x); int jokers_top = list_get_size(jokers) - 1; for (int i = jokers_top; i >= 0; i--) { JokerObject *joker = list_get(jokers, i); joker->sprite_object->tx = hand_x - int2fx(spacing_lut[jokers_top][i]); - joker->sprite_object->ty = hand_y; joker_object_update(joker); } @@ -2147,6 +2141,25 @@ static int reroll_cost = REROLL_BASE_COST; #define REROLL_BTN_FRAME_PAL_IDX 7 #define REROLL_BTN_PAL_IDX 3 +void print_price_under_sprite_object(SpriteObject *sprite_object, int price) +{ + int x = fx2int(sprite_object->tx) + TILE_SIZE - (get_digits_even(price) - 1) * TILE_SIZE; + int y = fx2int(sprite_object->ty) + CARD_SPRITE_SIZE + TILE_SIZE; // TODO: Should probably extract the sprite size + tte_printf("#{P:%d,%d; cx:0xC000}$%d", x, y, price); +} + +void erase_price_under_sprite_object(SpriteObject *sprite_object) +{ + Rect price_rect; + price_rect.left = fx2int(sprite_object->tx); + price_rect.top = fx2int(sprite_object->ty) + CARD_SPRITE_SIZE + TILE_SIZE; + // TODO: Should probably extract the size from the sprite + + price_rect.right = price_rect.left + TILE_SIZE * 3; + price_rect.bottom = price_rect.top + 2*TILE_SIZE; // Taking 2 tiles down so the highlighted case is also covered + tte_erase_rect_wrapper(price_rect); +} + static void game_shop_create_items() { tte_erase_rect_wrapper(SHOP_PRICES_TEXT_RECT); @@ -2172,9 +2185,7 @@ static void game_shop_create_items() joker_object->sprite_object->tx = joker_object->sprite_object->x; joker_object->sprite_object->ty = int2fx(ITEM_SHOP_Y); - int x = fx2int(joker_object->sprite_object->tx) + TILE_SIZE - (get_digits_even(joker_object->joker->value) - 1) * TILE_SIZE; - int y = fx2int(joker_object->sprite_object->ty) + CARD_SPRITE_SIZE + TILE_SIZE; - tte_printf("#{P:%d,%d; cx:0xC000}$%d", x, y, joker_object->joker->value); + print_price_under_sprite_object(joker_object->sprite_object, joker_object->joker->value); sprite_position(joker_object_get_sprite(joker_object), fx2int(joker_object->sprite_object->x), fx2int(joker_object->sprite_object->y)); list_append(shop_jokers, joker_object); @@ -2247,28 +2258,80 @@ static void game_shop_reroll(int *reroll_cost) tte_printf("#{P:%d,%d; cx:0xF000}$%d", SHOP_REROLL_RECT.left, SHOP_REROLL_RECT.top, *reroll_cost); } +static int jokers_sel_row_get_size() +{ + return list_get_size(jokers); +} + +static void jokers_sel_row_on_selection_changed(SelectionGrid *selection_grid, + int row_idx, + const Selection *prev_selection, + const Selection *new_selection) +{ + if (prev_selection->y == row_idx) + { + JokerObject* joker_object = list_get(jokers, prev_selection->x); + erase_price_under_sprite_object(joker_object->sprite_object); + sprite_object_set_focus(joker_object->sprite_object, false); + } + + if (new_selection->y == row_idx) + { + JokerObject* joker_object = list_get(jokers, new_selection->x); + sprite_object_set_focus(joker_object->sprite_object, true); + print_price_under_sprite_object(joker_object->sprite_object, joker_get_sell_value(joker_object->joker)); + } +} + +void game_sell_joker(int joker_idx) +{ + if (joker_idx < 0 || joker_idx > list_get_size(jokers)) + return; + + JokerObject *joker_object = list_get(jokers, joker_idx); + money += joker_get_sell_value(joker_object->joker); + display_money(money); + erase_price_under_sprite_object(joker_object->sprite_object); + + list_remove_by_idx(jokers, joker_idx); + int_list_append(jokers_available_to_shop, (intptr_t)joker_object->joker->id); + + // TODO: Discard animation + joker_object_destroy(&joker_object); +} + +static void jokers_sel_row_on_key_hit(SelectionGrid* selection_grid, Selection* selection) +{ + if (!key_hit(SELL_KEY)) + return; + + game_sell_joker(selection->x); + // Move the selection away from the jokers so it doesn't point to an invalid place + selection_grid_move_selection_vert(selection_grid, SCREEN_DOWN); +} + // Shop input static int shop_top_row_get_size() { return list_get_size(shop_jokers) + 1; // + 1 to account for next round button } +void add_joker(JokerObject *joker_object) +{ + list_append(jokers, joker_object); + joker_object->sprite_object->ty = int2fx(HELD_JOKERS_POS.y); +} + static void game_shop_buy_joker(int shop_joker_idx) { JokerObject *joker_object = list_get(shop_jokers, shop_joker_idx); - joker_push(joker_object); + money -= joker_object->joker->value; // Deduct the money spent on the joker display_money(money); // Update the money display + erase_price_under_sprite_object(joker_object->sprite_object); + sprite_object_set_focus(joker_object->sprite_object, false); - Rect joker_price_rect; - joker_price_rect.left = fx2int(joker_object->sprite_object->tx); - // 2*TILE_SIZE because the item is highlighted and raised by 1 additional tile - joker_price_rect.top = fx2int(joker_object->sprite_object->ty) + CARD_SPRITE_SIZE + 2 * 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); - + add_joker(joker_object); list_remove_by_idx(shop_jokers, shop_joker_idx); // Remove the joker from the shop } @@ -2347,12 +2410,12 @@ static void shop_top_row_on_selection_changed(SelectionGrid* selection_grid, int } } -static int shop_bottom_row_get_size() +static int shop_reroll_row_get_size() { - return 1; // Currently only the reroll button + return 1; // Only the reroll button } -static void shop_bottom_row_on_selection_changed(SelectionGrid* selection_grid, int row_idx, const Selection* prev_selection, const Selection* new_selection) +static void shop_reroll_row_on_selection_changed(SelectionGrid* selection_grid, int row_idx, const Selection* prev_selection, const Selection* new_selection) { if (row_idx == prev_selection->y) { @@ -2365,7 +2428,7 @@ static void shop_bottom_row_on_selection_changed(SelectionGrid* selection_grid, } } -static void shop_bottom_row_on_key_hit(SelectionGrid* selection_grid, Selection* selection) +static void shop_reroll_row_on_key_hit(SelectionGrid* selection_grid, Selection* selection) { if (money >= reroll_cost) { @@ -2375,11 +2438,12 @@ static void shop_bottom_row_on_key_hit(SelectionGrid* selection_grid, Selection* SelectionGridRow shop_selection_rows[] = { - {0, shop_top_row_get_size, shop_top_row_on_selection_changed, shop_top_row_on_key_hit}, - {1, shop_bottom_row_get_size, shop_bottom_row_on_selection_changed, shop_bottom_row_on_key_hit} + {0, jokers_sel_row_get_size, jokers_sel_row_on_selection_changed, jokers_sel_row_on_key_hit}, + {1, shop_top_row_get_size, shop_top_row_on_selection_changed, shop_top_row_on_key_hit}, + {2, shop_reroll_row_get_size, shop_reroll_row_on_selection_changed, shop_reroll_row_on_key_hit} }; -static const Selection SHOP_INIT_SEL = {-1, 0}; +static const Selection SHOP_INIT_SEL = {-1, 1}; SelectionGrid shop_selection_grid = {shop_selection_rows, NUM_ELEM_IN_ARR(shop_selection_rows), SHOP_INIT_SEL}; @@ -2530,8 +2594,6 @@ void game_shop() } } - - void game_blind_select() { 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 diff --git a/source/joker.c b/source/joker.c index 6b30061..867d608 100644 --- a/source/joker.c +++ b/source/joker.c @@ -181,6 +181,16 @@ JokerEffect joker_get_score_effect(Joker *joker, Card *scored_card) return jinfo->effect(joker, scored_card); } +int joker_get_sell_value(const Joker* joker) +{ + if (joker == NULL) + { + return UNDEFINED; + } + + return joker->value/2; +} + // JokerObject methods JokerObject *joker_object_new(Joker *joker) { @@ -320,7 +330,6 @@ bool joker_object_score(JokerObject *joker_object, Card* scored_card, int *chips return false; } - void joker_object_set_selected(JokerObject* joker_object, bool selected) { if (joker_object == NULL) diff --git a/source/selection_grid.c b/source/selection_grid.c index 8acc017..1cb1012 100644 --- a/source/selection_grid.c +++ b/source/selection_grid.c @@ -49,6 +49,9 @@ void selection_grid_move_selection_vert(SelectionGrid *selection_grid, int direc && new_selection.y < selection_grid->num_rows) { int new_row_size = selection_grid->rows[new_selection.y].get_size(); + if (new_row_size <= 0) + return; + if (selection.x >= new_row_size) { // TODO: Maintain relative horizontal position diff --git a/source/sprite.c b/source/sprite.c index 90cb7b5..e69f2a2 100644 --- a/source/sprite.c +++ b/source/sprite.c @@ -119,6 +119,7 @@ SpriteObject* sprite_object_new() sprite_object->sprite = NULL; sprite_object_reset_transform(sprite_object); sprite_object->selected = false; + sprite_object->focused = false; return sprite_object; } From 947fac73585c91f8b08229bd195117e8bb7e2308 Mon Sep 17 00:00:00 2001 From: MeirGavish Date: Sat, 16 Aug 2025 21:33:23 +0300 Subject: [PATCH 09/13] Added joker selling animation --- source/game.c | 43 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/source/game.c b/source/game.c index fb37eac..dac13ea 100644 --- a/source/game.c +++ b/source/game.c @@ -77,6 +77,7 @@ static int selection_y = 0; static bool sort_by_suit = false; static List *jokers = NULL; +static List *discarded_jokers = NULL; // Stacks static CardObject *played[MAX_SELECTION_SIZE] = {NULL}; @@ -224,6 +225,7 @@ static const Rect CASHOUT_RECT = {88, 72, UNDEFINED, UNDEF static const Rect SHOP_REROLL_RECT = {88, 96, UNDEFINED, UNDEFINED }; static const BG_POINT HELD_JOKERS_POS = {108, 10}; +static const BG_POINT JOKER_DISCARD_TARGET = {240, 30}; #define ITEM_SHOP_Y 71 // TODO: Needs to be a rect? @@ -1010,6 +1012,9 @@ void game_init() if (jokers) list_destroy(&jokers); jokers = list_new(MAX_JOKERS_HELD_SIZE); + if (discarded_jokers != NULL) list_destroy(&discarded_jokers); + discarded_jokers = list_new(MAX_JOKERS_HELD_SIZE); + hands = max_hands; discards = max_discards; @@ -1767,6 +1772,26 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection } } +static void discarded_jokers_update_loop() +{ + if (discarded_jokers == NULL) + return; + + // Iterating backwards because of removal within loop + for (int i = list_get_size(discarded_jokers) - 1; i >= 0; i--) + { + JokerObject* joker_object = list_get(discarded_jokers, i); + joker_object_update(joker_object); + if (joker_object->sprite_object->x == joker_object->sprite_object->tx + && joker_object->sprite_object->y == joker_object->sprite_object->ty) + { + list_remove_by_idx(discarded_jokers, i); + joker_object_destroy(&joker_object); + } + } + +} + static void held_jokers_update_loop() { const int spacing_lut[MAX_JOKERS_HELD_SIZE][MAX_JOKERS_HELD_SIZE] = @@ -1790,6 +1815,12 @@ static void held_jokers_update_loop() } } +static void jokers_update_loop() +{ + held_jokers_update_loop(); + discarded_jokers_update_loop(); +} + static void game_playing_ui_text_update() { static int last_hand_size = 0; @@ -2283,6 +2314,13 @@ static void jokers_sel_row_on_selection_changed(SelectionGrid *selection_grid, } } +void joker_start_discard_animation(JokerObject *joker_object) +{ + joker_object->sprite_object->tx = int2fx(JOKER_DISCARD_TARGET.x); + joker_object->sprite_object->ty = int2fx(JOKER_DISCARD_TARGET.y); + list_append(discarded_jokers, joker_object); +} + void game_sell_joker(int joker_idx) { if (joker_idx < 0 || joker_idx > list_get_size(jokers)) @@ -2296,8 +2334,7 @@ void game_sell_joker(int joker_idx) list_remove_by_idx(jokers, joker_idx); int_list_append(jokers_available_to_shop, (intptr_t)joker_object->joker->id); - // TODO: Discard animation - joker_object_destroy(&joker_object); + joker_start_discard_animation(joker_object); } static void jokers_sel_row_on_key_hit(SelectionGrid* selection_grid, Selection* selection) @@ -2771,7 +2808,7 @@ void game_update() } } - held_jokers_update_loop(); + jokers_update_loop(); switch (game_state) { From 2539954deacc57242d2a37adbf6b5d5c6a404c5e Mon Sep 17 00:00:00 2001 From: MeirGavish Date: Sun, 17 Aug 2025 11:10:00 +0300 Subject: [PATCH 10/13] Fixed build error from previous merge and applied review comments --- include/card.h | 2 +- source/joker_effects.c | 18 ++++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/include/card.h b/include/card.h index 9831013..e403b33 100644 --- a/include/card.h +++ b/include/card.h @@ -35,7 +35,7 @@ #define NUM_RANKS 13 #define RANK_OFFSET 2 // Because the first rank is 2 and ranks start at 0 -#define IMPOSSIBLY_HIGH_CARD_VALUE 99 +#define IMPOSSIBLY_HIGH_CARD_VALUE 100 // Card types typedef struct diff --git a/source/joker_effects.c b/source/joker_effects.c index 36b6197..7cbb0d3 100644 --- a/source/joker_effects.c +++ b/source/joker_effects.c @@ -313,15 +313,15 @@ static JokerEffect raised_fist_joker_effect(Joker *joker, Card *scored_card) { // Aces are always considered high value, even in an ace-low straight u8 lowest_value = IMPOSSIBLY_HIGH_CARD_VALUE; CardObject** hand = get_hand_array(); - int hand_top = get_hand_top(); - for (int i = 0; i < hand_top; i++ ) + int hand_size = hand_get_size(); + for (int i = 0; i < hand_size; i++ ) { u8 value = card_get_value(hand[i]->card); if (lowest_value > value) lowest_value = value; } - if (lowest_value != 99) + if (lowest_value != IMPOSSIBLY_HIGH_CARD_VALUE) effect.mult = lowest_value * 2; return effect; @@ -333,8 +333,8 @@ static JokerEffect reserved_parking_joker_effect(Joker *joker, Card *scored_card return effect; // if card != null, we are not at the end-phase of scoring yet CardObject** hand = get_hand_array(); - int hand_top = get_hand_top(); - for (int i = 0; i < hand_top; i++ ) + int hand_size = hand_get_size(); + for (int i = 0; i < hand_size; i++ ) { switch (hand[i]->card->rank) { case KING: case QUEEN: case JACK: @@ -356,13 +356,11 @@ static JokerEffect business_card_joker_effect(Joker *joker, Card *scored_card) { switch (scored_card->rank) { case KING: case QUEEN: case JACK: if (random() % 2 == 0) - effect.money = 1; + effect.money = 2; default: break; } - effect.chips = 1; - return effect; } @@ -400,8 +398,8 @@ static JokerEffect abstract_joker_effect(Joker *joker, Card *scored_card) { return effect; // if card != null, we are not at the end-phase of scoring yet // +1 xmult per occupied joker slot - int jokers_top = get_jokers_top(); - effect.mult = (jokers_top + 1) * 3; + int num_jokers = list_get_size(get_jokers()); + effect.mult = num_jokers * 3; return effect; } From 46141090a05c6a395a98fe5f2e3b79d58ba5112e Mon Sep 17 00:00:00 2001 From: MeirGavish Date: Sun, 17 Aug 2025 12:36:20 +0300 Subject: [PATCH 11/13] Added joker sprites by @GreenNinja2525 for Blue Joker, Even Steven, Odd Todd, Scholar and Business Card. Disabled jokers with no sprites. --- graphics/joker_gfx11.png | Bin 855 -> 1199 bytes graphics/joker_gfx12.grit | 1 + graphics/joker_gfx12.png | Bin 0 -> 1200 bytes graphics/joker_gfx13.grit | 1 + graphics/joker_gfx13.png | Bin 0 -> 1135 bytes include/joker_gfx.h | 2 + source/joker.c | 4 ++ source/joker_effects.c | 86 ++++++++++++++++++++++++-------------- 8 files changed, 62 insertions(+), 32 deletions(-) create mode 100644 graphics/joker_gfx12.grit create mode 100644 graphics/joker_gfx12.png create mode 100644 graphics/joker_gfx13.grit create mode 100644 graphics/joker_gfx13.png diff --git a/graphics/joker_gfx11.png b/graphics/joker_gfx11.png index 07b537c4b3f16e80ba8decafaea4179ef21a4375..2bc36d4ff9d82089853fd3120ba47efec6723bbe 100644 GIT binary patch delta 1180 zcmV;N1Y`Tx2CoT_BYy;TNklDgpT-OzIPK=?}#OdY)Y`|$4Xi#=Jc))Ur5EAP+U5hV+3 zcqrH?EhDoBbPf+tuGY~xJU|$Ra9kG-fN>CD&huh#Uan{H029xwU^GDR+2@nmb!6gu zl3QDTTe{oJZ~jHb#ijAc?Ycb6ftl+JkWsGIalG>8T7Ph|sxC%q>*L$$cr@d3#Vf&; zBKK3}PE~tWOL*F(H-L-oB9Wk^@P~n@|a^ z5)V=tMg#B|PI-Xn)7w@jZMNYy+ZM~r20fCv%{CtWe5A-)-mOe?CK@0uobc1x?`>Zz zKY7El;(yZ`LGTb^RGynMLd&})B1tB62SXMFfhgUMk1j2n~gkea!YVdSb$mDvG3sU@fd3c*XCXL@J40?Zlh{+r&Gvo(N z?{oyW*+#gzhH!IjCXpa3Pq|uG#+c{VKwXFc5cdH(%GEl4`||ti@rA#X_G16G(q_64 z>3c``cB7~Y?wFO>$6VKr(<#mO3AKt~z$)zZHz?E+~^R&W1 zRDbtY)LYKHFoFrzS7KBcl#RfZ*A<=C1AjPACAdQC{RkEIcckQdHKbN1Mrb+n0Ft+X z4%!2@)`4F9;|YYlS$^EXpE-^1@eL?0gfSvucC>3mPfc*z6q$~?OR0RO`u0AQ5O z%mXOM7Zx&E>3mO-<-HIw@$gia%cy=f9uNbW2Lu5bS9S0>JRXk~883PAfGj*FSbzQ3 zkPT3Jww-7II@O4hkSvLa$pCqH7-M41vSBhMLi|>RUo7aK!%4y1aU3Oeofdg{T-Q~$ zMq)qH0{GR^tpC*fmq`7uU^_|Lg~+E2`9A>#(g57;v;NKbl)&BIuQgJQK!!p6Mzwzy uB`|t2bW@lGkM8zK9!#Dh|EJ&M{}lpc{>X@hdrcbv0000oJ# delta 833 zcmV-H1HSyP3D*XYBYy)PNklUweTjTg}Y?GS=1{+j^LpqfTe0jB=NCvQ1Z`_)< zfeQwRrJ$~Lo%U1503F5h4jN0}Ji+CPuU&1k5x-3^WH(c=#(*`3tDAP9DWH4;5t#u? z3v<3_qw?C<56EVQn%hJ$kO5Yu3ae5zGC=mZ+V&56dVdajdOrH>CV)@|P)b9c9B~R1 z%Zkwb(yHS0^c;Ni{nN~JQqW_8uo$4Qwe733jTMKTAt*nCEQyT;0O0DT?b`=T&JbO9 z&|HUHF8w}AzXz7lb77zXa@kp+v}qu>@`2Fo+c*2>d;8|i zn^^`IaL&1#d{{8Xn2O&1uYZoj{_OYPIP5zFME=eDaE(8j=^7m11S0<+c_&o+4dW1i z(+Styl!^SS*RO?a{>hV}2Jq&6&k2L@AbAI-X(F>w5VE%~Hv-w!G)+8{oM5@{)tN{S zOFjrRO%tT+RozoBvrs^0p#V+O9KV5f{0wTVk`F~k6@3XXCJ3gpdF*q_cUL zrYU|(Hu~L=!yo7nd;4+&Ta~;kNs`yAdMHjvXWgH{7=zYLBSanmQtt|=S9Qy@QGsg` zlyiU_{#%tilEo4Lz$mZb@l)MXZpWh;wIoOhFrDo<)HF>nO#=h)Z@Z0ak%)ynft)~Z z-yT5?yXWFMSu9z|H+Rnw zjpuw1B5bH6KcTClv{lLbA{LG3LQRG;+}=I_kpu~kl`+QylpYKtyM}Z1lD?tJ+Yw|-Aa}%Ax{P>+k_w=8?keHi*QSLc` z`u1BDhJj3+&ud_>)Yg4FJR{)o(;fktMCtA{YA3%kA8+8< z?=7pad%c~RhT(e%-r$@=uATPpeE?e@>t1#L4?6bwVhrhb05}KKcHZhqkTUvdZ3)|% zm@vo}JMHaEOiYVWW&e6rM|txXVN=TATW^7_w}eBe zC<=24WD*^k_YLC^e6eSroIpCvhYLLS0zi-aMXQPNSOQnan@AQ*`1J9HrQhV#C5-Z# zLs!lNe)j@0(%C$AZa?qo{qVQt{_Me+Xs3==UCUzMdjWe9bkS-e8qdKfui><|gvqH( zOio?mw6h2d8rN@O_FzoN0bu9$bLD!#S0O}*#&g0E zM0`|x06TdASl&AmZIUnVomq5LoiMNtxH{fMVr~NCu>{6r30$@w&)jbD|)@EgL7`#^V`YOvDlzQI8q1Dnc^@`=oc=GF?a#@6g-{q)qfmy9n9DCdHg>tb6_2j_bfvI O0000es za@qkYJx!ug5S+#fwDt~(EyuAFr}ehOJ}HW0JO2Lv_wPTyof0%Sy9k)Bep)91(Z_M_6|f{@7?P}A3}&I)7@=7>&GvmUwLgs z2$2V!@|04mcU2VTKf4GR$9Ozum3mW@`6Xk#v&Bpp?`$R7ZCqRCS9v)%m#HXp{6r^& zYCZW`o%!0#eNh(ml!8(Ua?}Mm>Y6>o0)!CM_6}0qGOCSM;$CQcQ-eAfAtd33ynS|Y z6?Atyp(r5)c>c7oFt+G-+Nd^K;xfU;Aleuplk_|7Xf_D}KHo#tYXXQA$JM#$pYVXV z%ndUw`kl6QT~RME8bBr?gn%BGQ1hCR;u_SK*YV-%JQ85$K6;~HPLu~ACc^Zrli3!T z2H<1>&?`eIe-{R#{VoimI1mP?FRx=ZuCIjwkRfOhsAFV}$>p`>%``x@(MrwuT^L~T zK^XvW2%z`lM6}hyK=kK#7t0`P#!dZf-^pD9vJqtEISwCgigNGANg^%)aJCF&n3R)a zfD;ek1JUCW>OWrrh+IFs^t62sA8sPN^f0@)in}my;{IHQEXW3N9s@Y|x(_csZGfY@ z+o;8caj86zo^eyO=lzO$K?12v>=VRQsW+qSE-rIa>dmOn&Q@lYHc2K}1eJPI@ayC# zoCyK|dBzfVUha3=@caQhe}I0cooIIr(`-+Y`Khp40qBobAbNqtS?sMk7O>Xvc4rigHq!%;uod zRVr*UG&hK9m?Hcs0jNO;N-3k%)XKCcxUM`P3zd2^b#k4>Z;=QY>T^XTn6g!D%t+9~~K{%}`dfx8?m~29Vv+g%M=P-+Pmr zn|`9G(8d7IA58soQkMqX!Uex>OdYh5?0@<_{u@o1Cuj3mVU7R*002ovPDHLkV1mX@ B9rgeK literal 0 HcmV?d00001 diff --git a/include/joker_gfx.h b/include/joker_gfx.h index 75cfbbc..ce1c916 100644 --- a/include/joker_gfx.h +++ b/include/joker_gfx.h @@ -13,5 +13,7 @@ #include "joker_gfx9.h" #include "joker_gfx10.h" #include "joker_gfx11.h" +#include "joker_gfx12.h" +#include "joker_gfx13.h" #endif \ No newline at end of file diff --git a/source/joker.c b/source/joker.c index 867d608..6ec320b 100644 --- a/source/joker.c +++ b/source/joker.c @@ -130,6 +130,8 @@ void joker_init() joker_gfxTiles[9] = joker_gfx9Tiles; joker_gfxTiles[10] = joker_gfx10Tiles; joker_gfxTiles[11] = joker_gfx11Tiles; + joker_gfxTiles[12] = joker_gfx12Tiles; + joker_gfxTiles[13] = joker_gfx13Tiles; joker_gfxPal[0] = joker_gfx0Pal; joker_gfxPal[1] = joker_gfx1Pal; @@ -143,6 +145,8 @@ void joker_init() joker_gfxPal[9] = joker_gfx9Pal; joker_gfxPal[10] = joker_gfx10Pal; joker_gfxPal[11] = joker_gfx11Pal; + joker_gfxPal[12] = joker_gfx12Pal; + joker_gfxPal[13] = joker_gfx13Pal; for (int i = 0; i < num_spritesheets; i++) { diff --git a/source/joker_effects.c b/source/joker_effects.c index 7cbb0d3..155c850 100644 --- a/source/joker_effects.c +++ b/source/joker_effects.c @@ -304,7 +304,11 @@ static JokerEffect blue_joker_effect(Joker *joker, Card *scored_card) { return effect; } -static JokerEffect raised_fist_joker_effect(Joker *joker, Card *scored_card) { +// Using __attribute__((unused)) for jokers with no sprites yet to avoid warning +// Remove the attribute once they have sprites +__attribute__((unused)) +static JokerEffect raised_fist_joker_effect(Joker *joker, Card *scored_card) +{ JokerEffect effect = {0}; if (scored_card != NULL) return effect; // if card != null, we are not at the end-phase of scoring yet @@ -325,8 +329,9 @@ static JokerEffect raised_fist_joker_effect(Joker *joker, Card *scored_card) { effect.mult = lowest_value * 2; return effect; -} +} +__attribute__((unused)) static JokerEffect reserved_parking_joker_effect(Joker *joker, Card *scored_card) { JokerEffect effect = {0}; if (scored_card != NULL) @@ -346,7 +351,7 @@ static JokerEffect reserved_parking_joker_effect(Joker *joker, Card *scored_card } return effect; -} +}; static JokerEffect business_card_joker_effect(Joker *joker, Card *scored_card) { JokerEffect effect = {0}; @@ -377,6 +382,7 @@ static JokerEffect scholar_joker_effect(Joker *joker, Card *scored_card) { return effect; } +__attribute__((unused)) static JokerEffect scary_face_joker_effect(Joker *joker, Card *scored_card) { JokerEffect effect = {0}; if (scored_card == NULL) @@ -392,6 +398,7 @@ static JokerEffect scary_face_joker_effect(Joker *joker, Card *scored_card) { return effect; } + __attribute__((unused)) static JokerEffect abstract_joker_effect(Joker *joker, Card *scored_card) { JokerEffect effect = {0}; if (scored_card != NULL) @@ -404,6 +411,7 @@ static JokerEffect abstract_joker_effect(Joker *joker, Card *scored_card) { return effect; } +__attribute__((unused)) static JokerEffect bull_joker_effect(Joker *joker, Card *scored_card) { JokerEffect effect = {0}; if (scored_card != NULL) @@ -414,6 +422,7 @@ static JokerEffect bull_joker_effect(Joker *joker, Card *scored_card) { return effect; } +__attribute__((unused)) static JokerEffect smiley_face_joker_effect(Joker *joker, Card *scored_card) { JokerEffect effect = {0}; if (scored_card == NULL) @@ -457,43 +466,56 @@ static JokerEffect odd_todd_joker_effect(Joker *joker, Card *scored_card) { return effect; } +/* The index of a joker in the registry matches its ID. + * The joker sprites are matched by ID so the position in the registry + * determines the joker's sprite. + * Each consecutive NUM_JOKERS_PER_SPRITESHEET (defined in joker.c) jokers + * share a spritesheet and thus a color palette. + * To make better use of color palettes jokers may be rearranged here + * (and put together in the matching spritesheet) to share a color palette. + * Otherwise the order is similar to the wiki. + */ const JokerInfo joker_registry[] = { { COMMON_JOKER, 2, default_joker_effect }, // DEFAULT_JOKER_ID = 0 - { COMMON_JOKER, 5, greedy_joker_effect }, // GREEDY_JOKER_ID = 1 - { COMMON_JOKER, 5, lusty_joker_effect }, // etc... - { COMMON_JOKER, 5, wrathful_joker_effect }, - { COMMON_JOKER, 5, gluttonous_joker_effect }, - { COMMON_JOKER, 3, jolly_joker_effect }, - { COMMON_JOKER, 4, zany_joker_effect }, - { COMMON_JOKER, 4, mad_joker_effect }, - { COMMON_JOKER, 4, crazy_joker_effect }, - { COMMON_JOKER, 4, droll_joker_effect }, - { COMMON_JOKER, 3, sly_joker_effect }, - { COMMON_JOKER, 4, wily_joker_effect }, - { COMMON_JOKER, 4, clever_joker_effect }, - { COMMON_JOKER, 4, devious_joker_effect }, - { COMMON_JOKER, 4, crafty_joker_effect }, - { COMMON_JOKER, 5, half_joker_effect }, - { UNCOMMON_JOKER, 8, joker_stencil_effect }, - { COMMON_JOKER, 5, banner_joker_effect }, - { COMMON_JOKER, 4, walkie_talkie_joker_effect }, - { UNCOMMON_JOKER, 8, fibonnaci_joker_effect }, - { UNCOMMON_JOKER, 6, blackboard_joker_effect }, - { COMMON_JOKER, 5, mystic_summit_joker_effect }, - { COMMON_JOKER, 4, misprint_joker_effect }, - { UNCOMMON_JOKER, 6, blackboard_joker_effect }, - { COMMON_JOKER, 5, blue_joker_effect }, + { COMMON_JOKER, 5, greedy_joker_effect }, // GREEDY_JOKER_ID = 1 + { COMMON_JOKER, 5, lusty_joker_effect }, // etc... 2 + { COMMON_JOKER, 5, wrathful_joker_effect }, // 3 + { COMMON_JOKER, 5, gluttonous_joker_effect }, // 4 + { COMMON_JOKER, 3, jolly_joker_effect }, // 5 + { COMMON_JOKER, 4, zany_joker_effect }, // 6 + { COMMON_JOKER, 4, mad_joker_effect }, // 7 + { COMMON_JOKER, 4, crazy_joker_effect }, // 8 + { COMMON_JOKER, 4, droll_joker_effect }, // 9 + { COMMON_JOKER, 3, sly_joker_effect }, // 10 + { COMMON_JOKER, 4, wily_joker_effect }, // 11 + { COMMON_JOKER, 4, clever_joker_effect }, // 12 + { COMMON_JOKER, 4, devious_joker_effect }, // 13 + { COMMON_JOKER, 4, crafty_joker_effect }, // 14 + { COMMON_JOKER, 5, half_joker_effect }, // 15 + { UNCOMMON_JOKER, 8, joker_stencil_effect }, // 16 + { COMMON_JOKER, 5, banner_joker_effect }, // 17 + { COMMON_JOKER, 4, walkie_talkie_joker_effect }, // 18 + { UNCOMMON_JOKER, 8, fibonnaci_joker_effect }, // 19 + { UNCOMMON_JOKER, 6, blackboard_joker_effect }, // 20 + { COMMON_JOKER, 5, mystic_summit_joker_effect }, // 21 + { COMMON_JOKER, 4, misprint_joker_effect }, // 22 + { COMMON_JOKER, 4, even_steven_joker_effect }, // 23 + { COMMON_JOKER, 5, blue_joker_effect }, // 24 + { COMMON_JOKER, 4, odd_todd_joker_effect }, // 25 + { COMMON_JOKER, 4, scholar_joker_effect }, // 26 + { COMMON_JOKER, 4, business_card_joker_effect }, // 27 + // Business card should be paired with Shortcut for palette optimization when it's added + + // The following jokers don't have sprites yet, + // uncomment them when their sprites are added. +#if 0 { COMMON_JOKER, 5, raised_fist_joker_effect }, { COMMON_JOKER, 6, reserved_parking_joker_effect }, - { COMMON_JOKER, 4, business_card_joker_effect }, - { COMMON_JOKER, 4, scholar_joker_effect }, { COMMON_JOKER, 4, scary_face_joker_effect }, { COMMON_JOKER, 4, abstract_joker_effect }, { UNCOMMON_JOKER, 6, bull_joker_effect}, { COMMON_JOKER, 4, smiley_face_joker_effect }, - { COMMON_JOKER, 4, even_steven_joker_effect }, - { COMMON_JOKER, 4, odd_todd_joker_effect }, - +#endif }; static const size_t joker_registry_size = NUM_ELEM_IN_ARR(joker_registry); From 1b9785bc1f4a581dae677e7b099076e13b6a1a96 Mon Sep 17 00:00:00 2001 From: MeirGavish Date: Sun, 17 Aug 2025 13:42:56 +0300 Subject: [PATCH 12/13] Sly Joker card corner fix --- graphics/joker_gfx5.png | Bin 1053 -> 1027 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/graphics/joker_gfx5.png b/graphics/joker_gfx5.png index bc4e1125a567e3cb6616ddc0acee1ec1bd98be3f..a3dbac820faa10b31beeea3ee5dcda32bc323d6b 100644 GIT binary patch delta 1005 zcmV{mwPXF@7;TO zi9`P&-hJ=xe)oOe-TU6DfTPm>3E|nB)zI^2icGt9@*4~k9)FMZ1`6LfI2BIfF8TL( zjpMOi7>0rI=~>hE?aOChU{zJkG3g}S)K0$Y)R97qYk5b2(~n=Zi_cvF?BHA}3RH!^ zk`2Rf?=Pu#@P#e3WJKh&t9%No5 za^6i#^Vd>*Pse5XYuZv@?C-$B#Hht~w6)=7NKXpwm47Yu&BTrm*c_7k-8dj9)qTK9*|$ z2q93I%ztY{y@Zl|a9g|m{j*wYn;wO>wE++UIAm*HWanqX2`JgBfL{QRYL3rlVuTQ4 z7>1=zvokDTnl@Ud&+L8tW`gbDyPg3G>V4?G+c`J|Nq0iYR-t68An8s^8@5lgYBisG zA;PLZEbK5p`C+MsS6PipMOny*h3EhJ{)hG<P08CqM=+JyNhp05)H2|#hJ>jROy>fhpnXwlD zfFH(Vs298DGox!TupqF`TbwLKkqIY|2?LKFF57~0C>;dpM997Z)<3>6%W(kjQC|V- z#ec4F8+aAv=jIPBPO^y*P(Tj#L};!Q6>bB&+_wNBD6XzRd-d94cFVJit1H304jFz3 z^;B5fv-9gIa0q2-zIy-^r8Wr~O+u{CoNPhl0xjmteGIV!(XygI*YyS)w@0FCoht|5pt^Y+P5PJcBY>>`1%}X z%M>iy3hX_-A8E-HH{hZRQu`b{^X(raL5N;a`}r7ZHJ`fzWdDaJD^*oJjoD5<_g(Qn b{d@c$c9s>r6Wtw_00000NkvXXu0mjfXdL_t(&f#q30NLxV||J|c>(T*JocFd9}oO=P6m zwG~$aw@*;ii(k3}c;!+M0)xnRvSnG`^EFWqe&vXGBB|h3lQYt44Zm0ZT)sdVUQSF= zN+~r>lUgO&-+#M1>IKs@scD)Oa-Hm6Z+_PhV6n$MD*h_$xhM~sui6lBE zG8Hla=o}nz$VVrqLvsSpb<@`&%MJ7Bb@j;HIym z!g!$Q5M6#x2dIqqLg(O!GLiX#%6Km<%-(A8`%0I`Uw>1M{(NsE=EjB{vZL)yKPr8p zVgK0C-x{ArYsdE=uM!P*XHjKHA?{=R#!L5MNg^GV`yu8I* zTh?)QF>d#n6Lk2sc0hJ*#i4A-xN;|uJ#-k!7ec@~mQXH)W^!H1 zFc9G#0+b@Vx{Ua%*AB5)ot<4>4(D~q@i(Ac3di?F{&ET2fMacbZwC-kTma(^? z76w$h+}7j=+BZUOecAYWI6>qLvW7s%VZP0XRiVJ&(+5F3k(4@IbwMhw!8gBn7zqRH ziaN+)sMh?_9iaOko-Pc-@TIe!{IbIk@E`p>{tpuM7aR++mVf{N002ovPDHLkU;% Date: Sun, 17 Aug 2025 14:51:04 +0300 Subject: [PATCH 13/13] Even Steven card corner fix --- graphics/joker_gfx11.png | Bin 1199 -> 1182 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/graphics/joker_gfx11.png b/graphics/joker_gfx11.png index 2bc36d4ff9d82089853fd3120ba47efec6723bbe..f67828fff67f23c7d7050ee3f14be0f394f39f4c 100644 GIT binary patch delta 1162 zcmV;51a)h6uwQNQKe;0R{;*GJ#7c+f@<*hBRpo%n>xZDONL@ znSHx6yOKlTJ{UB-`SHzr`)1zLi~|Cl{vgz4i?^;jlspR-Z+~5PZ2C>glZ8Jf==29+ zVSY}L_1>I{`ohg?xfjqqIyM@CD?bTO5;qVx!i>$}!u%Y1Z%(mr^BRI6fM2bPwu4vC zr@2l5m@1F!x?)al1f|_L!}>~8qzU3xD9z5d?vGyv$+h;N94PcVkEF$x@}3uqIl0f9A3%SzyCM!A5BrnLVJr zw~JDxhW6erf*^q7x^Mst8N-a{#lE~;&*A|lo@v2o0QTe6QEeQV_@3m}mcN$n_VSm1 zlyPxsJaW4(&vIbqIs;^sDm5G|yc`P-m(;~bZ9P1kjDJTnE?2zF&K0>|ty>$xykYJ# z;4ULWNKE+)0;N0R6u`dSXHyxv!2P47~sITH<#7Ebu->~}ZD%8$OVtoWox5IjT} zmFK36(0}wUiAa(Oo&F$XjEU0e55i7=5b7x2-%#r8-crc+5tr{n6q`ReIv2_F^jTc~ z^jY}V+Gc7J z0AozJuZ$oFNLOX|mV`{MC%GWSpO=TX>0{FPt-_%9>0?ahNSPr&Xu3zoa2qWIYs(1M zmZuU4vhtKFHD!!>eht)x2mo;(prcf&;qRZHjK>%LRoWMOKPYWB7a~bK%Kr*&I~87R zihsI4bD7-#)P;=IA?R1@-H6F9-fJk2VD84GfOKBMD=%XMZM|F3L}_ceI-VP zLD>jgd0o*-J%IC+**Ti;k5LZ)i!;2XE&Lezi{S zfBt2PC)@bvkLZsF|1F+uqj<6{WW3}7cV(X40f7JU0{|FhQ}X}{@`Z&=Ry^5OWO*+{ zOguc*`sYaB9WPeFS zOa{oq6NaH!vuv15i4eb4;TH?~-{GWS?l_K;x=xC`Jg)01TO+ZbX#xCdY1%I}-x8_s z3bvD^U5I?oklzU?kOtsxpY=88djfZRzgkZ<0vQJN8&&=|swYDig=z5UZlC1A~6UZf(LZFA9+?ZT?F!4oLkUv0T z!IndC;yS9V=u&bl6dZ%e(YH2EF;o~@kPb%u0}K+n$QYWNWS^1}F!Yj3!IYxi>0veF znc25Hvn#m-`oXZnn;+l2w{PaXopnHQ=jf&2mn*@q`)aI zFDimLVganMw4liPXBWaKD-Z7FX73J$%)$s$^HAuWj`-XPf6M?B3QG&InCkC!=29lZPd@b2%6Jz1{S6V^m4@6X*4B@1kL zDA*`1BeMr|4i8YS*3mgUKp2K_To(?2aS&k6^I~sau4nN86VI$*G(hm#=abrXWa4|0 zTU&lxy4%Zd{zb;crSZt^x;)E)nd=OYQLff;yz=H+aDTF@E=FqWf14+u8PzkOQ z4^kOM1MnD5d4TBC+g2xSw&6D07R$^AJ(9T1HXi+aq{v#{txR(!8Xzs4@YC7vZC@)t zdBd{e(|;O4@DO2Co|`g4%ey5aNhWj$Lly*qDBZ!3bq7PHqx5WBsRvJ2S#X57{0O4h zd`#(FB+tuNarw(v?AOhWm|Of`ODI@DY-+x76b1fP(k%h0d$+S6akcH<_vyE1@ z4lo9!WQ(cLw?GgC!hL0gVMw}a@N`wkZjo&H^dVhY1$s8#&_W?S})jEFr^84%Yg};>cV*j?%X1WmR z4u6I$iAVXa(6&?I^^U0fb9c!7-#Z<}_^l2>zt$ipl$GCocozUrJh>G09jOig_~*ig zj^q)_fAJN7iNcdQ-n~`o$K#J8gqmQr1zvm;&*3TMb%l2y-o?(zr6_s8m2Wxow8B7C zfA?0@Th6>Nf(h1FVpJHEjlh-H6`j@tIDbzixI*jw2o?5sq~v=wq*f+IXgTu$lDB~l z+5@)MfnPj$`}N6z}?=jHBya0hC%&CwSN{R tFnTg{Q