kind of fixed how game_speed behaves

This commit is contained in:
alex
2025-09-21 23:16:50 -07:00
parent 9dc5f7ccc3
commit 6e60cf5970
7 changed files with 856 additions and 811 deletions
+1 -1
View File
@@ -72,7 +72,7 @@ LIBDIRS := $(LIBGBA) $(LIBTONC)
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(BUILD)/$(TARGET)
export OUTPUT := $(CURDIR)/$(TARGET)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir)) \
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
View File
+7 -3
View File
@@ -87,15 +87,19 @@ typedef struct List List;
// Utility functions for other files
typedef struct CardObject CardObject; // forward declaration, actually declared in card.h
typedef struct JokerObject JokerObject;
CardObject** get_hand_array(void);
CardObject **get_hand_array(void);
int get_hand_top(void);
int hand_get_size(void);
CardObject** get_played_array(void);
CardObject **get_played_array(void);
int get_played_top(void);
List* get_jokers(void);
List *get_jokers(void);
int get_deck_top(void);
int get_num_discards_remaining(void);
int get_money(void);
extern int game_speed; // had to do this use this it the sprites animations
// that were not affected by game_speed, like discard
// and draw-to-deck animations.
#endif // GAME_H
+165 -129
View File
@@ -32,7 +32,7 @@
static uint rng_seed = 0;
static uint timer = 0; // This might already exist in libtonc but idk so i'm just making my own
static int game_speed = 1;
int game_speed = 1; // BY DEFAULT IS ON 1, but if changed to 2, should speed up all (or most) of the game aspects that should be sped up by speed, as in the original game.
static int background = 0;
static enum GameState game_state = GAME_SPLASH_SCREEN; // The current game state, this is used to determine what the game is doing at any given time
@@ -99,48 +99,56 @@ static int discard_top = -1;
// Played stack
static inline void played_push(CardObject *card_object)
{
if (played_top >= MAX_SELECTION_SIZE - 1) return;
if (played_top >= MAX_SELECTION_SIZE - 1)
return;
played[++played_top] = card_object;
}
static inline CardObject *played_pop()
{
if (played_top < 0) return NULL;
if (played_top < 0)
return NULL;
return played[played_top--];
}
// Deck stack
static inline void deck_push(Card *card)
{
if (deck_top >= MAX_DECK_SIZE - 1) return;
if (deck_top >= MAX_DECK_SIZE - 1)
return;
deck[++deck_top] = card;
}
static inline Card *deck_pop()
{
if (deck_top < 0) return NULL;
if (deck_top < 0)
return NULL;
return deck[deck_top--];
}
// Discard stack
static inline void discard_push(Card *card)
{
if (discard_top >= MAX_DECK_SIZE - 1) return;
if (discard_top >= MAX_DECK_SIZE - 1)
return;
discard_pile[++discard_top] = card;
}
static inline Card *discard_pop()
{
if (discard_top < 0) return NULL;
if (discard_top < 0)
return NULL;
return discard_pile[discard_top--];
}
// get-functions, for other files to view game state (mainly for jokers)
CardObject **get_hand_array(void) {
CardObject **get_hand_array(void)
{
return hand;
}
int get_hand_top(void) {
int get_hand_top(void)
{
return hand_top;
}
@@ -149,15 +157,18 @@ int hand_get_size(void)
return hand_top + 1;
}
CardObject **get_played_array(void) {
CardObject **get_played_array(void)
{
return played;
}
int get_played_top(void) {
int get_played_top(void)
{
return played_top;
}
List *get_jokers(void) {
List *get_jokers(void)
{
return jokers;
}
@@ -171,41 +182,46 @@ void remove_held_joker(int joker_idx)
list_remove_by_idx(jokers, joker_idx);
}
int get_deck_top(void) {
int get_deck_top(void)
{
return deck_top;
}
int get_num_discards_remaining(void) {
int get_num_discards_remaining(void)
{
return discards;
}
int get_money(void) {
int get_money(void)
{
return money;
}
// Consts
// Rects left top right bottom
// Screenblock rects
static const Rect ROUND_END_MENU_RECT = {9, 7, 24, 20 };
static const Rect ROUND_END_MENU_RECT = {9, 7, 24, 20};
static const Rect POP_MENU_ANIM_RECT = {9, 7, 24, 31 };
static const Rect POP_MENU_ANIM_RECT = {9, 7, 24, 31};
// The rect for popping menu animations (round end, shop, blinds)
// - extends beyond the visible screen to the end of the screenblock
// It includes both the target and source position rects.
// This is because when popping, the target position is blank so we just animate
// the whole rect so we don't have to track its position
static const Rect SINGLE_BLIND_SELECT_RECT = {9, 7, 13, 32 };
static const Rect SINGLE_BLIND_SELECT_RECT = {9, 7, 13, 32};
static const Rect HAND_BG_RECT_SELECTING = {9, 11, 24, 17 };
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 };
// static const Rect HAND_BG_RECT_PLAYING = {9, 14, 24, 18 };
static const Rect TOP_LEFT_ITEM_SRC_RECT = {0, 20, 8, 25 };
static const BG_POINT TOP_LEFT_PANEL_POINT = {0, 0, };
static const Rect TOP_LEFT_PANEL_ANIM_RECT = {0, 0, 8, 4 };
static const Rect TOP_LEFT_ITEM_SRC_RECT = {0, 20, 8, 25};
static const BG_POINT TOP_LEFT_PANEL_POINT = {
0,
0,
};
static const Rect TOP_LEFT_PANEL_ANIM_RECT = {0, 0, 8, 4};
/* Contains the shop icon/current blind etc.
* The difference between TOP_LEFT_PANEL_ANIM_RECT and TOP_LEFT_PANEL_RECT
* is due to an overlap between the bottom of the top left panel
@@ -213,44 +229,47 @@ static const Rect TOP_LEFT_PANEL_ANIM_RECT = {0, 0, 8, 4 };
* TOP_LEFT_PANEL_ANIM_RECT should be used for animations,
* TOP_LEFT_PANEL_RECT for copies etc. but mind the overlap
*/
static const BG_POINT TOP_LEFT_BLIND_TITLE_POINT = {0, 21, };
static const Rect BIG_BLIND_TITLE_SRC_RECT = {0, 26, 8, 26 };
static const Rect BOSS_BLIND_TITLE_SRC_RECT = {0, 27, 8, 27 };
static const BG_POINT TOP_LEFT_BLIND_TITLE_POINT = {
0,
21,
};
static const Rect BIG_BLIND_TITLE_SRC_RECT = {0, 26, 8, 26};
static const Rect BOSS_BLIND_TITLE_SRC_RECT = {0, 27, 8, 27};
static const BG_POINT GAME_OVER_SRC_RECT_3X3_POS = {25, 29};
static const Rect GAME_OVER_DIALOG_DEST_RECT= {11, 21, 23, 26};
static const Rect GAME_OVER_DIALOG_DEST_RECT = {11, 21, 23, 26};
static const Rect GAME_OVER_ANIM_RECT = {11, 8, 23, 26};
// Rects for TTE (in pixels)
static const Rect HAND_SIZE_RECT = {128, 128, 152, 160 }; // Seems to include both SELECT and PLAYING
static const Rect HAND_SIZE_RECT_SELECT = {128, 128, 152, 136 };
static const Rect HAND_SIZE_RECT_PLAYING = {128, 152, 152, 160 };
static const Rect HAND_TYPE_RECT = {8, 64, 64, 72 };
static const Rect HAND_SIZE_RECT = {128, 128, 152, 160}; // Seems to include both SELECT and PLAYING
static const Rect HAND_SIZE_RECT_SELECT = {128, 128, 152, 136};
static const Rect HAND_SIZE_RECT_PLAYING = {128, 152, 152, 160};
static const Rect HAND_TYPE_RECT = {8, 64, 64, 72};
// Score displayed in the same place as the hand type
static const Rect TEMP_SCORE_RECT = {8, 64, 64, 72 };
static const Rect SCORE_RECT = {32, 48, 64, 56 };
static const Rect TEMP_SCORE_RECT = {8, 64, 64, 72};
static const Rect SCORE_RECT = {32, 48, 64, 56};
static const Rect PLAYED_CARDS_SCORES_RECT = {72, 48, 240, 56 };
static const Rect BLIND_TOKEN_TEXT_RECT = {80, 72, 200, 160 };
static const Rect MONEY_TEXT_RECT = {8, 120, 64, 128 };
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 };
static const Rect PLAYED_CARDS_SCORES_RECT = {72, 48, 240, 56};
static const Rect BLIND_TOKEN_TEXT_RECT = {80, 72, 200, 160};
static const Rect MONEY_TEXT_RECT = {8, 120, 64, 128};
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()
static const Rect HANDS_TEXT_RECT = {16, 104, UNDEFINED, UNDEFINED };
static const Rect DISCARDS_TEXT_RECT = {48, 104, UNDEFINED, UNDEFINED };
static const Rect DECK_SIZE_RECT = {200, 152, UNDEFINED, UNDEFINED };
static const Rect ROUND_TEXT_RECT = {48, 144, UNDEFINED, UNDEFINED };
static const Rect ANTE_TEXT_RECT = {8, 144, UNDEFINED, UNDEFINED };
static const Rect ROUND_END_BLIND_REQ_RECT = {104, 96, 136, UNDEFINED };
static const Rect ROUND_END_BLIND_REWARD_RECT = { 168, 96, UNDEFINED, UNDEFINED };
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 };
static const Rect HANDS_TEXT_RECT = {16, 104, UNDEFINED, UNDEFINED};
static const Rect DISCARDS_TEXT_RECT = {48, 104, UNDEFINED, UNDEFINED};
static const Rect DECK_SIZE_RECT = {200, 152, UNDEFINED, UNDEFINED};
static const Rect ROUND_TEXT_RECT = {48, 144, UNDEFINED, UNDEFINED};
static const Rect ANTE_TEXT_RECT = {8, 144, UNDEFINED, UNDEFINED};
static const Rect ROUND_END_BLIND_REQ_RECT = {104, 96, 136, UNDEFINED};
static const Rect ROUND_END_BLIND_REWARD_RECT = {168, 96, UNDEFINED, UNDEFINED};
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};
static const Rect GAME_LOSE_MSG_TEXT_RECT = {104, 72, UNDEFINED, UNDEFINED};
// 1 character to the right oF GAME_LOSE
static const Rect GAME_WIN_MSG_TEXT_RECT = {112, 72, UNDEFINED, UNDEFINED};
@@ -265,7 +284,7 @@ static const BG_POINT JOKER_DISCARD_TARGET = {240, 30};
#define MAIN_MENU_PLAY_BUTTON_MAIN_COLOR_PID 5
#define MAIN_MENU_PLAY_BUTTON_OUTLINE_COLOR_PID 2
//TODO: Properly define and use
// TODO: Properly define and use
#define MENU_POP_OUT_ANIM_FRAMES 20
#define GAME_OVER_ANIM_FRAMES 15
@@ -275,7 +294,7 @@ static const BG_POINT JOKER_DISCARD_TARGET = {240, 30};
#define PITCH_STEP_DISCARD_SFX (-64)
#define PITCH_STEP_DRAW_SFX 24
#define PITCH_STEP_UNDISCARD_SFX 2*PITCH_STEP_DRAW_SFX
#define PITCH_STEP_UNDISCARD_SFX 2 * PITCH_STEP_DRAW_SFX
// Naming the stage where cards return from the discard pile to the deck "undiscard"
// General functions
@@ -293,7 +312,7 @@ void sort_hand_by_suit()
{
if (hand[a] == NULL || (hand[b] != NULL && (hand[a]->card->suit > hand[b]->card->suit || (hand[a]->card->suit == hand[b]->card->suit && hand[a]->card->rank > hand[b]->card->rank))))
{
CardObject* temp = hand[a];
CardObject *temp = hand[a];
hand[a] = hand[b];
hand[b] = temp;
}
@@ -309,7 +328,7 @@ void sort_hand_by_rank()
{
if (hand[a] == NULL || (hand[b] != NULL && hand[a]->card->rank > hand[b]->card->rank))
{
CardObject* temp = hand[a];
CardObject *temp = hand[a];
hand[a] = hand[b];
hand[b] = temp;
}
@@ -343,7 +362,7 @@ void sort_cards()
{
if (hand[i] != NULL)
{
//hand[i]->sprite = sprite_new(ATTR0_SQUARE | ATTR0_4BPP | ATTR0_AFF, ATTR1_SIZE_32, card_sprite_lut[hand[i]->card->suit][hand[i]->card->rank], 0, i);
// hand[i]->sprite = sprite_new(ATTR0_SQUARE | ATTR0_4BPP | ATTR0_AFF, ATTR1_SIZE_32, card_sprite_lut[hand[i]->card->suit][hand[i]->card->rank], 0, i);
card_object_set_sprite(hand[i], i); // Set the sprite for the card object
sprite_position(card_object_get_sprite(hand[i]), fx2int(hand[i]->sprite_object->x), fx2int(hand[i]->sprite_object->y));
}
@@ -372,7 +391,8 @@ enum HandType hand_get_type()
res_hand_type = FLUSH;
// Check for straight
if (hand_contains_straight(ranks)) {
if (hand_contains_straight(ranks))
{
if (res_hand_type == FLUSH)
res_hand_type = STRAIGHT_FLUSH;
else
@@ -380,7 +400,8 @@ enum HandType hand_get_type()
}
// Check for royal flush vs regular straight flush
if (res_hand_type == STRAIGHT_FLUSH) {
if (res_hand_type == STRAIGHT_FLUSH)
{
if (ranks[TEN] && ranks[JACK] && ranks[QUEEN] && ranks[KING] && ranks[ACE])
return ROYAL_FLUSH;
return STRAIGHT_FLUSH;
@@ -389,32 +410,40 @@ enum HandType hand_get_type()
// The following can be optimized better but not sure how much it matters
u8 n_of_a_kind = hand_contains_n_of_a_kind(ranks);
if (n_of_a_kind >= 5) {
if (res_hand_type == FLUSH) {
if (n_of_a_kind >= 5)
{
if (res_hand_type == FLUSH)
{
return FLUSH_FIVE;
}
return FIVE_OF_A_KIND;
}
if (n_of_a_kind == 4) {
if (n_of_a_kind == 4)
{
return FOUR_OF_A_KIND;
}
if (n_of_a_kind == 3 && hand_contains_full_house(ranks)) {
if (n_of_a_kind == 3 && hand_contains_full_house(ranks))
{
return FULL_HOUSE;
}
// Flush is more valuable than the remaining hand types, so return now
if (res_hand_type == FLUSH) {
if (res_hand_type == FLUSH)
{
return FLUSH;
}
if (n_of_a_kind == 3) {
if (n_of_a_kind == 3)
{
return THREE_OF_A_KIND;
}
if (n_of_a_kind == 2) {
if (hand_contains_two_pair(ranks)) {
if (n_of_a_kind == 2)
{
if (hand_contains_two_pair(ranks))
{
return TWO_PAIR;
}
return PAIR;
@@ -676,7 +705,7 @@ void display_score(int value)
char score_suffix = ' ';
int display_value = value;
if(value >= 10000)
if (value >= 10000)
{
score_suffix = 'k';
display_value = value / 1000; // 12,986 = 12k
@@ -685,7 +714,7 @@ void display_score(int value)
// Calculate text width: digits + suffix character (if 'k')
int num_digits = get_digits(display_value);
int text_width = num_digits * TILE_SIZE;
if(score_suffix == 'k')
if (score_suffix == 'k')
{
text_width += TILE_SIZE; // Add width for 'k' suffix
}
@@ -720,7 +749,7 @@ void display_mult(int value)
void display_round(int value)
{
//tte_erase_rect_wrapper(ROUND_TEXT_RECT);
// tte_erase_rect_wrapper(ROUND_TEXT_RECT);
tte_printf("#{P:%d,%d; cx:0x%X000}%d", ROUND_TEXT_RECT.left, ROUND_TEXT_RECT.top, TTE_YELLOW_PB, round);
}
@@ -731,17 +760,17 @@ void display_ante(int value)
void display_hands(int value)
{
//tte_erase_rect_wrapper(HANDS_TEXT_RECT);
// tte_erase_rect_wrapper(HANDS_TEXT_RECT);
tte_printf("#{P:%d,%d; cx:0xD000}%d", HANDS_TEXT_RECT.left, HANDS_TEXT_RECT.top, hands); // Hand
}
void display_discards(int value)
{
//tte_erase_rect_wrapper(DISCARDS_TEXT_RECT);
// tte_erase_rect_wrapper(DISCARDS_TEXT_RECT);
tte_printf("#{P:%d,%d; cx:0xE000}%d", DISCARDS_TEXT_RECT.left, DISCARDS_TEXT_RECT.top, discards); // Discard
}
static void print_hand_type(const char* hand_type_str)
static void print_hand_type(const char *hand_type_str)
{
if (hand_type_str == NULL)
return; // NULL-checking paranoia
@@ -831,7 +860,8 @@ void set_hand()
void card_draw()
{
if (deck_top < 0 || hand_top >= hand_size - 1 || hand_top >= MAX_HAND_SIZE - 1) return;
if (deck_top < 0 || hand_top >= hand_size - 1 || hand_top >= MAX_HAND_SIZE - 1)
return;
CardObject *card_object = card_object_new(deck_pop());
@@ -846,12 +876,13 @@ void card_draw()
// Sort the hand after drawing a card
sort_cards();
play_sfx(SFX_CARD_DRAW, MM_BASE_PITCH_RATE + cards_drawn*PITCH_STEP_DRAW_SFX);
play_sfx(SFX_CARD_DRAW, MM_BASE_PITCH_RATE + cards_drawn * PITCH_STEP_DRAW_SFX);
}
void hand_set_focus(int index)
{
if (index < 0 || index > hand_top || hand_state != HAND_SELECT) return;
if (index < 0 || index > hand_top || hand_state != HAND_SELECT)
return;
selection_x = index;
play_sfx(SFX_CARD_FOCUS, MM_BASE_PITCH_RATE + rand() % 512);
@@ -859,7 +890,8 @@ void hand_set_focus(int index)
void hand_toggle_card_selection()
{
if (hand_state != HAND_SELECT || hand[selection_x] == NULL) return;
if (hand_state != HAND_SELECT || hand[selection_x] == NULL)
return;
if (card_object_is_selected(hand[selection_x]))
{
@@ -907,13 +939,15 @@ int hand_get_max_size()
bool hand_discard()
{
if (hand_state != HAND_SELECT || hand_selections == 0) return false;
if (hand_state != HAND_SELECT || hand_selections == 0)
return false;
return true;
}
bool hand_play()
{
if (hand_state != HAND_SELECT || hand_selections == 0) return false;
if (hand_state != HAND_SELECT || hand_selections == 0)
return false;
return true;
}
@@ -963,7 +997,7 @@ static void game_round_init()
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)
// if (playing_blind_token != NULL)
//{
// obj_hide(playing_blind_token->obj); // Hide the blind token sprite for now
//}
@@ -980,7 +1014,7 @@ static void game_round_init()
// TODO: Address Copilot review at
// https://github.com/cellos51/balatro-gba/pull/46#pullrequestreview-3045772903
char score_suffix = ' ';
if(blind_requirement >= 10000)
if (blind_requirement >= 10000)
{
// clear existing text
tte_erase_rect_wrapper(blind_req_text_rect);
@@ -993,7 +1027,7 @@ static void game_round_init()
update_text_rect_to_right_align_num(&blind_req_text_rect, blind_requirement, OVERFLOW_RIGHT);
// If we added a suffix, adjust position to account for the extra character
if(score_suffix == 'k')
if (score_suffix == 'k')
{
blind_req_text_rect.left -= TILE_SIZE; // Move left by one character width to make room for 'k'
}
@@ -1030,7 +1064,6 @@ static void game_lose_init()
game_over_init();
// Using the text color to match the "Game Over" text
affine_background_set_color(TEXT_CLR_RED);
}
static void game_win_init()
@@ -1095,10 +1128,12 @@ void game_init()
jokers_available_to_shop_init();
// Initialize jokers list
if (jokers) list_destroy(&jokers);
if (jokers)
list_destroy(&jokers);
jokers = list_new(MAX_JOKERS_HELD_SIZE);
if (discarded_jokers != NULL) list_destroy(&discarded_jokers);
if (discarded_jokers != NULL)
list_destroy(&discarded_jokers);
discarded_jokers = list_new(MAX_JOKERS_HELD_SIZE);
hands = max_hands;
@@ -1269,7 +1304,7 @@ static void game_playing_process_input_and_state()
if (mult > 0)
{
temp_score = chips * mult;
lerped_temp_score = int2fx(temp_score);
lerped_temp_score = (float)(1.0f / game_speed) * int2fx(temp_score);
lerped_score = int2fx(score);
display_temp_score(temp_score);
@@ -1368,11 +1403,11 @@ static void game_playing_discarded_cards_loop()
change_background(BG_ID_ROUND_END); // Change the background to the round end background. This is how it works in Balatro, so I'm doing it this way too.
// We take each discarded card and put it back into the deck with a short animation
static CardObject* discarded_card_object = NULL;
static CardObject *discarded_card_object = NULL;
if (discarded_card_object == NULL)
{
discarded_card_object = card_object_new(discard_pop());
//discarded_card_object->sprite = sprite_new(ATTR0_SQUARE | ATTR0_4BPP | ATTR0_AFF, ATTR1_SIZE_32, card_sprite_lut[discarded_card_object->card->suit][discarded_card_object->card->rank], 0, 0);
// discarded_card_object->sprite = sprite_new(ATTR0_SQUARE | ATTR0_4BPP | ATTR0_AFF, ATTR1_SIZE_32, card_sprite_lut[discarded_card_object->card->suit][discarded_card_object->card->rank], 0, 0);
card_object_set_sprite(discarded_card_object, 0); // Set the sprite for the discarded card object
sprite_object_reset_transform(discarded_card_object->sprite_object);
@@ -1404,9 +1439,9 @@ static void game_playing_discarded_cards_loop()
}
}
static const int HAND_SPACING_LUT[MAX_HAND_SIZE] = { 28, 28, 28, 28, 27, 21, 18, 15, 13, 12, 10, 9, 9, 8, 8, 7 }; // This is a stupid way to do this but I don't care
static const int HAND_SPACING_LUT[MAX_HAND_SIZE] = {28, 28, 28, 28, 27, 21, 18, 15, 13, 12, 10, 9, 9, 8, 8, 7}; // This is a stupid way to do this but I don't care
void card_in_hand_loop_handle_discard_and_shuffling(int card_idx, bool* discarded_card, FIXED* hand_x, FIXED* hand_y, bool* sound_played, bool* break_loop)
void card_in_hand_loop_handle_discard_and_shuffling(int card_idx, bool *discarded_card, FIXED *hand_x, FIXED *hand_y, bool *sound_played, bool *break_loop)
{
if (hand_state != HAND_DISCARD && hand_state != HAND_SHUFFLING)
{
@@ -1476,7 +1511,7 @@ void card_in_hand_loop_handle_discard_and_shuffling(int card_idx, bool* discarde
};
}
static void cards_in_hand_update_loop(bool* discarded_card, int* played_selections, bool* sound_played)
static void cards_in_hand_update_loop(bool *discarded_card, int *played_selections, bool *sound_played)
{
for (int i = hand_top + 1; i >= 0; i--) // Start from the end of the hand and work backwards because that's how Balatro does it
{
@@ -1519,7 +1554,8 @@ static void cards_in_hand_update_loop(bool* discarded_card, int* played_selectio
case HAND_DISCARD: // TODO: Add sound
bool break_loop;
card_in_hand_loop_handle_discard_and_shuffling(i, discarded_card, &hand_x, &hand_y, sound_played, &break_loop);
if (break_loop) break;
if (break_loop)
break;
break;
case HAND_PLAY:
@@ -1534,7 +1570,7 @@ static void cards_in_hand_update_loop(bool* discarded_card, int* played_selectio
hand[i] = NULL;
sort_cards();
play_sfx(SFX_CARD_DRAW, MM_BASE_PITCH_RATE + cards_drawn*PITCH_STEP_DISCARD_SFX);
play_sfx(SFX_CARD_DRAW, MM_BASE_PITCH_RATE + cards_drawn * PITCH_STEP_DISCARD_SFX);
hand_top--;
hand_selections--;
@@ -1581,7 +1617,8 @@ static void cards_in_hand_update_loop(bool* discarded_card, int* played_selectio
}
}
if (card_object_is_selected(played[i])) break;
if (card_object_is_selected(played[i]))
break;
}
break;
case TWO_PAIR: // find two pairs of cards with the same rank (Requires recursion)
@@ -1600,7 +1637,8 @@ static void cards_in_hand_update_loop(bool* discarded_card, int* played_selectio
}
}
if (card_object_is_selected(played[i])) break;
if (card_object_is_selected(played[i]))
break;
}
for (; i <= played_top - 1; i++) // Find second pair
@@ -1639,7 +1677,8 @@ static void cards_in_hand_update_loop(bool* discarded_card, int* played_selectio
}
}
if (card_object_is_selected(played[i])) break;
if (card_object_is_selected(played[i]))
break;
}
break;
case FOUR_OF_A_KIND: // find four cards with the same rank (requires recursion)
@@ -1709,7 +1748,7 @@ static void cards_in_hand_update_loop(bool* discarded_card, int* played_selectio
}
}
static void played_cards_update_loop(bool* discarded_card, int* played_selections, bool* sound_played)
static void played_cards_update_loop(bool *discarded_card, int *played_selections, bool *sound_played)
{
// So this one is a bit fucking weird because I have to work kinda backwards for everything because of the order of the pushed cards from the hand to the play stack
// (also crazy that the company that published Balatro is called "Playstack" and this is a play stack, but I digress)
@@ -1719,7 +1758,7 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection
{
if (card_object_get_sprite(played[i]) == NULL)
{
//played[i]->sprite = sprite_new(ATTR0_SQUARE | ATTR0_4BPP | ATTR0_AFF, ATTR1_SIZE_32, card_sprite_lut[played[i]->card->suit][played[i]->card->rank], 0, i + MAX_HAND_SIZE);
// played[i]->sprite = sprite_new(ATTR0_SQUARE | ATTR0_4BPP | ATTR0_AFF, ATTR1_SIZE_32, card_sprite_lut[played[i]->card->suit][played[i]->card->rank], 0, i + MAX_HAND_SIZE);
card_object_set_sprite(played[i], i + MAX_HAND_SIZE); // Set the sprite for the played card object
}
@@ -1870,7 +1909,7 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection
if (!*sound_played)
{
play_sfx(SFX_CARD_DRAW, MM_BASE_PITCH_RATE + cards_drawn*PITCH_STEP_DISCARD_SFX);
play_sfx(SFX_CARD_DRAW, MM_BASE_PITCH_RATE + cards_drawn * PITCH_STEP_DISCARD_SFX);
*sound_played = true;
}
@@ -1879,7 +1918,7 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection
discard_push(played[i]->card); // Push the card to the discard pile
card_object_destroy(&played[i]);
//played_top--;
// played_top--;
cards_drawn++; // This technically isn't drawing cards, I'm just reusing the variable
*sound_played = false;
@@ -2030,7 +2069,8 @@ void game_round_end()
obj_unhide(round_end_blind_token->obj, 0);
int current_ante = ante;
if (current_blind == BOSS_BLIND) current_ante--; // Beating the boss blind increases the ante, so we need to display the previous ante value
if (current_blind == BOSS_BLIND)
current_ante--; // Beating the boss blind increases the ante, so we need to display the previous ante value
Rect blind_req_rect = ROUND_END_BLIND_REQ_RECT;
int blind_req = blind_get_requirement(current_blind, current_ante);
@@ -2074,14 +2114,15 @@ void game_round_end()
}
case 4: // Every 20 frames, display the blind reward and update the text until it reaches 0
{
if (timer % FRAMES(20) != 0) break;
if (timer % FRAMES(20) != 0)
break;
// TODO: Add sound effect here
if (blind_reward > 0)
{
blind_reward--;
tte_printf("#{P:%d,%d; cx:0x%X000}$%d", BLIND_REWARD_RECT.left , BLIND_REWARD_RECT.top, TTE_YELLOW_PB, blind_reward);
tte_printf("#{P:%d,%d; cx:0x%X000}$%d", BLIND_REWARD_RECT.left, BLIND_REWARD_RECT.top, TTE_YELLOW_PB, blind_reward);
tte_printf("#{P:%d,%d; cx:0x%X000}$%d", ROUND_END_BLIND_REWARD_RECT.left, ROUND_END_BLIND_REWARD_RECT.top, TTE_YELLOW_PB, blind_get_reward(current_blind) - blind_reward);
}
else if (timer > FRAMES(20))
@@ -2132,7 +2173,7 @@ void game_round_end()
int hand_y = 0;
// TODO: Implement interest
//int interest_y = 0;
// int interest_y = 0;
if (hands > 0)
{
@@ -2277,7 +2318,7 @@ void erase_price_under_sprite_object(SpriteObject *sprite_object)
// 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
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);
}
@@ -2333,8 +2374,8 @@ static void game_shop_intro()
int y_from = 26 + y - timer_offset;
int y_to = 0 + y;
Rect from = { 0, y_from, 8, y_from };
BG_POINT to = { 0, y_to };
Rect from = {0, y_from, 8, y_from};
BG_POINT to = {0, y_to};
main_bg_se_copy_rect(from, to);
}
@@ -2391,14 +2432,14 @@ static void jokers_sel_row_on_selection_changed(SelectionGrid *selection_grid,
{
if (prev_selection->y == row_idx)
{
JokerObject* joker_object = list_get(jokers, prev_selection->x);
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);
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));
}
@@ -2427,7 +2468,7 @@ void game_sell_joker(int joker_idx)
joker_start_discard_animation(joker_object);
}
static void jokers_sel_row_on_key_hit(SelectionGrid* selection_grid, Selection* selection)
static void jokers_sel_row_on_key_hit(SelectionGrid *selection_grid, Selection *selection)
{
if (!key_hit(SELL_KEY))
return;
@@ -2461,7 +2502,7 @@ static void game_shop_buy_joker(int shop_joker_idx)
list_remove_by_idx(shop_jokers, shop_joker_idx); // Remove the joker from the shop
}
static void shop_top_row_on_key_hit(SelectionGrid* selection_grid, Selection* selection)
static void shop_top_row_on_key_hit(SelectionGrid *selection_grid, Selection *selection)
{
if (!key_hit(SELECT_CARD))
return;
@@ -2484,9 +2525,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
|| list_get_size(jokers) >= MAX_JOKERS_HELD_SIZE
|| money < joker_object->joker->value)
if (joker_object == NULL || list_get_size(jokers) >= MAX_JOKERS_HELD_SIZE || money < joker_object->joker->value)
{
return;
}
@@ -2498,9 +2537,9 @@ static void shop_top_row_on_key_hit(SelectionGrid* selection_grid, Selection* se
}
}
static void shop_top_row_on_selection_changed(SelectionGrid* selection_grid, int row_idx,
const Selection* prev_selection,
const Selection* new_selection)
static void shop_top_row_on_selection_changed(SelectionGrid *selection_grid, int row_idx,
const Selection *prev_selection,
const Selection *new_selection)
{
// The selection grid system only guarantees that the new selection is within bounds
if (prev_selection->y == row_idx && prev_selection->x >= 0 && prev_selection->x < shop_top_row_get_size())
@@ -2539,7 +2578,7 @@ static int shop_reroll_row_get_size()
return 1; // Only the reroll button
}
static void shop_reroll_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)
{
@@ -2552,7 +2591,7 @@ static void shop_reroll_row_on_selection_changed(SelectionGrid* selection_grid,
}
}
static void shop_reroll_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)
{
@@ -2561,11 +2600,10 @@ static void shop_reroll_row_on_key_hit(SelectionGrid* selection_grid, Selection*
}
SelectionGridRow shop_selection_rows[] =
{
{
{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}
};
{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, 1};
@@ -2699,7 +2737,7 @@ void game_shop()
for (int i = 0; i < list_get_size(shop_jokers); i++)
{
JokerObject* joker_object = list_get(shop_jokers, i);
JokerObject *joker_object = list_get(shop_jokers, i);
if (joker_object != NULL)
{
// Make the joker available back to shop
@@ -2937,10 +2975,9 @@ static void discarded_jokers_update_loop()
// 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);
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)
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);
@@ -2956,8 +2993,7 @@ static void held_jokers_update_loop()
{13, -13, 0, 0, 0},
{26, 0, -26, 0, 0},
{39, 13, -13, -39, 0},
{40, 20, 0, -20, -40}
};
{40, 20, 0, -20, -40}};
FIXED hand_x = int2fx(HELD_JOKERS_POS.x);
+29 -24
View File
@@ -1,3 +1,4 @@
#include "game.h"
#include "sprite.h"
#include "util.h"
#include "audio_utils.h"
@@ -12,7 +13,7 @@
#define SPRITE_FOCUS_RAISE_PX 10
OBJ_ATTR obj_buffer[MAX_SPRITES];
OBJ_AFFINE *obj_aff_buffer = (OBJ_AFFINE*)obj_buffer;
OBJ_AFFINE *obj_aff_buffer = (OBJ_AFFINE *)obj_buffer;
static Sprite *free_sprites[MAX_SPRITES] = {NULL};
static bool free_affines[MAX_AFFINES] = {false};
@@ -25,7 +26,7 @@ Sprite *sprite_new(u16 a0, u16 a1, u32 tid, u32 pb, int sprite_index)
sprite->obj = NULL;
sprite->aff = NULL;
if(!free_sprites[sprite_index])
if (!free_sprites[sprite_index])
{
free_sprites[sprite_index] = sprite;
}
@@ -39,9 +40,9 @@ Sprite *sprite_new(u16 a0, u16 a1, u32 tid, u32 pb, int sprite_index)
{
int aff_index = MAX_AFFINES;
for(int i = 0; i < MAX_AFFINES; i++)
for (int i = 0; i < MAX_AFFINES; i++)
{
if(!free_affines[i])
if (!free_affines[i])
{
free_affines[i] = true;
aff_index = i;
@@ -73,7 +74,8 @@ Sprite *sprite_new(u16 a0, u16 a1, u32 tid, u32 pb, int sprite_index)
void sprite_destroy(Sprite **sprite)
{
if (*sprite == NULL) return;
if (*sprite == NULL)
return;
obj_hide((*sprite)->obj);
free_sprites[(*sprite)->obj - obj_buffer] = NULL;
if ((*sprite)->aff != NULL)
@@ -86,7 +88,8 @@ void sprite_destroy(Sprite **sprite)
int sprite_get_layer(Sprite *sprite)
{
if (sprite == NULL || sprite->obj == NULL) return -1;
if (sprite == NULL || sprite->obj == NULL)
return -1;
return sprite->obj - obj_buffer;
}
@@ -112,9 +115,9 @@ int sprite_get_pb(const Sprite *sprite)
}
// SpriteObject methods
SpriteObject* sprite_object_new()
SpriteObject *sprite_object_new()
{
SpriteObject* sprite_object = (SpriteObject*)malloc(sizeof(SpriteObject));
SpriteObject *sprite_object = (SpriteObject *)malloc(sizeof(SpriteObject));
sprite_object->sprite = NULL;
sprite_object_reset_transform(sprite_object);
sprite_object->selected = false;
@@ -123,15 +126,16 @@ SpriteObject* sprite_object_new()
return sprite_object;
}
void sprite_object_destroy(SpriteObject** sprite_object)
void sprite_object_destroy(SpriteObject **sprite_object)
{
if (*sprite_object == NULL) return;
if (*sprite_object == NULL)
return;
sprite_destroy(&((*sprite_object)->sprite));
free(*sprite_object);
*sprite_object = NULL;
}
void sprite_object_set_sprite(SpriteObject* sprite_object, Sprite* sprite)
void sprite_object_set_sprite(SpriteObject *sprite_object, Sprite *sprite)
{
if (sprite_object == NULL)
return;
@@ -139,7 +143,7 @@ void sprite_object_set_sprite(SpriteObject* sprite_object, Sprite* sprite)
sprite_object->sprite = sprite;
}
void sprite_object_reset_transform(SpriteObject* sprite_object)
void sprite_object_reset_transform(SpriteObject *sprite_object)
{
sprite_object->tx = 0; // Target position
sprite_object->ty = 0;
@@ -155,10 +159,10 @@ void sprite_object_reset_transform(SpriteObject* sprite_object)
sprite_object->vrotation = 0;
}
void sprite_object_update(SpriteObject* sprite_object)
void sprite_object_update(SpriteObject *sprite_object)
{
sprite_object->vx += (sprite_object->tx - sprite_object->x) / 8;
sprite_object->vy += (sprite_object->ty - sprite_object->y) / 8;
sprite_object->vx += (sprite_object->tx - sprite_object->x) / (8 / game_speed); // SPEED
sprite_object->vy += (sprite_object->ty - sprite_object->y) / (8 / game_speed);
sprite_object->vscale += (sprite_object->tscale - sprite_object->scale) / 8; // Scale up the card when it's played
@@ -211,38 +215,39 @@ void sprite_object_update(SpriteObject* sprite_object)
sprite_position(sprite_object->sprite, fx2int(sprite_object->x), fx2int(sprite_object->y));
}
void sprite_object_shake(SpriteObject* sprite_object, mm_word sound_id)
void sprite_object_shake(SpriteObject *sprite_object, mm_word sound_id)
{
sprite_object->vscale = float2fx(0.3f);
sprite_object->vrotation = float2fx(8.0f); //Rotate the card when it's scored
sprite_object->vrotation = float2fx(8.0f); // Rotate the card when it's scored
if (sound_id == UNDEFINED) return; // If no sound ID is provided, do nothing
if (sound_id == UNDEFINED)
return; // If no sound ID is provided, do nothing
play_sfx(sound_id, MM_BASE_PITCH_RATE);
}
void sprite_object_set_selected(SpriteObject* sprite_object, bool selected)
void sprite_object_set_selected(SpriteObject *sprite_object, bool selected)
{
if (sprite_object == NULL)
return;
sprite_object->selected = selected;
}
bool sprite_object_is_selected(SpriteObject* sprite_object)
bool sprite_object_is_selected(SpriteObject *sprite_object)
{
if (sprite_object == NULL)
return false;
return sprite_object->selected;
}
Sprite* sprite_object_get_sprite(SpriteObject* sprite_object)
Sprite *sprite_object_get_sprite(SpriteObject *sprite_object)
{
if (sprite_object == NULL)
return NULL;
return sprite_object->sprite;
}
void sprite_object_set_focus(SpriteObject* sprite_object, bool focus)
void sprite_object_set_focus(SpriteObject *sprite_object, bool focus)
{
if (sprite_object->focused == focus)
{
@@ -250,11 +255,11 @@ void sprite_object_set_focus(SpriteObject* sprite_object, bool focus)
}
sprite_object->focused = focus;
play_sfx(SFX_CARD_FOCUS , MM_BASE_PITCH_RATE + rand() % 512);
play_sfx(SFX_CARD_FOCUS, MM_BASE_PITCH_RATE + rand() % 512);
sprite_object->ty = sprite_object->ty + int2fx((focus ? -1 : 1) * SPRITE_FOCUS_RAISE_PX);
}
bool sprite_object_is_focused(SpriteObject* sprite_object)
bool sprite_object_is_focused(SpriteObject *sprite_object)
{
return sprite_object->focused;
}