From d787a918b66b553529433310f92c961cad48ce66 Mon Sep 17 00:00:00 2001 From: MeirGavish Date: Wed, 9 Jul 2025 17:36:19 +0300 Subject: [PATCH 1/3] Refactored so only set_hand() sets the global variable and not hand_get_type() so as to conform to set/get semantics --- source/game.c | 74 ++++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/source/game.c b/source/game.c index b5c7078..aa86b40 100644 --- a/source/game.c +++ b/source/game.c @@ -173,14 +173,16 @@ void sort_cards() enum HandType hand_get_type() { + enum HandType res_hand_type = NONE; + // Idk if this is how Balatro does it but this is how I'm doing it if (hand_selections == 0 || hand_state == HAND_DISCARD) { - hand_type = NONE; - return hand_type; + res_hand_type = NONE; + return res_hand_type; } - hand_type = HIGH_CARD; + res_hand_type = HIGH_CARD; u8 suits[NUM_SUITS] = {0}; u8 ranks[NUM_RANKS] = {0}; @@ -199,23 +201,23 @@ enum HandType hand_get_type() { if (suits[i] >= MAX_SELECTION_SIZE) // if i add jokers just MAX_SELECTION_SIZE - 1 for four fingers { - hand_type = FLUSH; + res_hand_type = FLUSH; break; } } // Check for straight - for (int i = 0; i < 9; i++) + for (int i = 0; i < NUM_RANKS - 4; i++) { if (ranks[i] && ranks[i + 1] && ranks[i + 2] && ranks[i + 3] && ranks[i + 4]) { - if (hand_type == FLUSH) + if (res_hand_type == FLUSH) { - hand_type = STRAIGHT_FLUSH; + res_hand_type = STRAIGHT_FLUSH; } else { - hand_type = STRAIGHT; + res_hand_type = STRAIGHT; } break; } @@ -224,75 +226,75 @@ enum HandType hand_get_type() // Check for ace low straight if (ranks[ACE] && ranks[TWO] && ranks[THREE] && ranks[FOUR] && ranks[FIVE]) { - hand_type = STRAIGHT; + res_hand_type = STRAIGHT; } // Check for royal flush - if (hand_type == STRAIGHT_FLUSH && ranks[TEN] && ranks[JACK] && ranks[QUEEN] && ranks[KING] && ranks[ACE]) + if (res_hand_type == STRAIGHT_FLUSH && ranks[TEN] && ranks[JACK] && ranks[QUEEN] && ranks[KING] && ranks[ACE]) { - hand_type = ROYAL_FLUSH; - return hand_type; + res_hand_type = ROYAL_FLUSH; + return res_hand_type; } // Check for straight flush - if (hand_type == STRAIGHT_FLUSH) + if (res_hand_type == STRAIGHT_FLUSH) { - hand_type = STRAIGHT_FLUSH; - return hand_type; + res_hand_type = STRAIGHT_FLUSH; + return res_hand_type; } for (int i = 0; i < NUM_RANKS; i++) { if (ranks[i] >= 5) { - if (hand_type == FLUSH) + if (res_hand_type == FLUSH) { - hand_type = FLUSH_FIVE; - return hand_type; + res_hand_type = FLUSH_FIVE; + return res_hand_type; } else { - hand_type = FIVE_OF_A_KIND; - return hand_type; + res_hand_type = FIVE_OF_A_KIND; + return res_hand_type; } } else if (ranks[i] == 4) { - hand_type = FOUR_OF_A_KIND; - return hand_type; + res_hand_type = FOUR_OF_A_KIND; + return res_hand_type; } else if (ranks[i] == 3) { - if (hand_type == PAIR) + if (res_hand_type == PAIR) { - hand_type = FULL_HOUSE; - return hand_type; + res_hand_type = FULL_HOUSE; + return res_hand_type; } else { - hand_type = THREE_OF_A_KIND; + res_hand_type = THREE_OF_A_KIND; } } else if (ranks[i] == 2) { - if (hand_type == THREE_OF_A_KIND) + if (res_hand_type == THREE_OF_A_KIND) { - hand_type = FULL_HOUSE; - return hand_type; + res_hand_type = FULL_HOUSE; + return res_hand_type; } - else if (hand_type == PAIR) + else if (res_hand_type == PAIR) { - hand_type = TWO_PAIR; - return hand_type; + res_hand_type = TWO_PAIR; + return res_hand_type; } else { - hand_type = PAIR; + res_hand_type = PAIR; } } } - return hand_type; + return res_hand_type; } void change_background(int id) @@ -400,8 +402,8 @@ void set_mult(int value) void set_hand() { tte_erase_rect(8, 64, 64, 72); // Hand type - - switch (hand_get_type()) + hand_type = hand_get_type(); + switch (hand_type) { case HIGH_CARD: tte_printf("#{P:8,64;}HIGH C"); From fb7b60431c07030c71de3f456d46265d08978492 Mon Sep 17 00:00:00 2001 From: MeirGavish Date: Thu, 10 Jul 2025 03:23:42 +0300 Subject: [PATCH 2/3] Extracted pop menu animation to a rect copying function for shop --- include/graphic_utils.h | 25 +++++++++--- source/game.c | 73 ++++++++++++++++++---------------- source/graphic_utils.c | 87 ++++++++++++++++++++++++++++++----------- 3 files changed, 123 insertions(+), 62 deletions(-) diff --git a/include/graphic_utils.h b/include/graphic_utils.h index 121b83b..796c773 100644 --- a/include/graphic_utils.h +++ b/include/graphic_utils.h @@ -4,6 +4,11 @@ /* This file contains general utils and wrappers that relate to * graphics/video/vram and generally displaying things on the screen. * Mostly wrappers and defines for using tonc. + * + * Note: the code here assumes we're working with a single screenblock + * which should be true for this entire game since a screenblock + * is enough to contain a full screen (and more) + * and there isn't any scrolling etc. */ /* Reminder: @@ -17,13 +22,14 @@ #define AFFINE_BG_SBB 2 #define AFFINE_BG_CBB 2 -/* A 1024 size screenblock is arranged in a grid of 32x32 block entries +/* Dimensions for a screenblock. + * A 1024 size screenblock is arranged in a grid of 32x32 screen entries * Interestingly since each block is 8x8 pixels, the 240x160 GBA screen * is smaller than the screenblock, only the top left part of the screenblock * is displayed on the screen. */ -#define SCREENBLOCK_ROW_LEN 32 - +#define SE_ROW_LEN 32 +#define SE_COL_LEN 32 typedef struct { @@ -35,10 +41,17 @@ typedef struct #define UNDEFINED -1 -/* Clears a rect in the main background screenblock. - * The rect needs to be in screenblocks +/* Clears a rect in the main background. + * The se_rect dimensions need to be in number of tiles. */ -void main_bg_se_clear_rect(Rect rect); +void main_bg_se_clear_rect(Rect se_rect); + +/* Copies a rect in the main background a single tile up. + * The se_rect dimensions need to be in number of tiles. + * This is for the pop menu animation, can later be generalized to more tiles + * or other directions + */ +void main_bg_se_copy_rect_1_tile_up(Rect se_rect); // A wrapper for tte_erase_rect that would use the rect struct void tte_erase_rect_wrapper(Rect rect); diff --git a/source/game.c b/source/game.c index 44b2a4a..df4ed41 100644 --- a/source/game.c +++ b/source/game.c @@ -108,36 +108,46 @@ static inline Card *discard_pop() // Consts -static const Rect ROUND_END_MENU_RECT = { 9, 7, 25, 21 }; // In screenblocks -// Rects for TTE -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 }; +// Rects left top right bottom +// Screenblock rects +static const Rect ROUND_END_MENU_RECT = {9, 7, 25, 21 }; + +static const Rect POP_MENU_ANIM_RECT = {9, 7, 25, 32 }; +// The rect for popping menu animations - includes both the target and source position +// rects. The source position extends beyond the visible screen to the end +// of the screenblock +// The target position is blank so we just animate the whole thing so we don't +// have to track its position + +// 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 }; // Score displayed in the same place as the hand type -static const Rect TEMP_SCORE_RECT = { 8, 64, 64, 72 }; +static const Rect TEMP_SCORE_RECT = {8, 64, 64, 72 }; -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, 120 }; -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 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, 120 }; +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 }; // 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 BLIND_REQUIREMENT_TEXT_RECT = { 40, 24, 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_REQUIREMENT_RECT = { 112, 96, UNDEFINED, 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 HANDS_TEXT_RECT = {16, 104, UNDEFINED, UNDEFINED }; +static const Rect DISCARDS_TEXT_RECT = {48, 104, UNDEFINED, UNDEFINED }; +static const Rect BLIND_REQ_TEXT_RECT = {40, 24, 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 = {112, 96, UNDEFINED, 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 }; // General functions void sort_hand_by_suit() @@ -172,7 +182,6 @@ void sort_hand_by_rank() } } - void sort_cards() { if (sort_by_suit) @@ -656,7 +665,7 @@ void game_init() tte_printf("#{P:%d,%d; cx:0xF000}%d/%d", HAND_SIZE_RECT.left, HAND_SIZE_RECT.top, hand_get_size(), hand_get_max_size()); // Hand size/max size tte_printf("#{P:%d,%d; cx:0xF000}%d/%d", DECK_SIZE_RECT.left, DECK_SIZE_RECT.top, deck_get_size(), deck_get_max_size()); // Deck size/max size - tte_printf("#{P:%d,%d; cx:0xE000}%d", BLIND_REQUIREMENT_TEXT_RECT.left, BLIND_REQUIREMENT_TEXT_RECT.top, blind_get_requirement(current_blind, ante)); // Blind requirement + tte_printf("#{P:%d,%d; cx:0xE000}%d", BLIND_REQ_TEXT_RECT.left, BLIND_REQ_TEXT_RECT.top, blind_get_requirement(current_blind, ante)); // Blind requirement tte_printf("#{P:%d,%d; cx:0xC000}$%d", BLIND_REWARD_RECT.left, BLIND_REWARD_RECT.top, blind_get_reward(current_blind)); // Blind reward set_score(score); // Set the score display @@ -1427,7 +1436,7 @@ void game_round_end() // Writing this kind a made me want to kms. If somewone wa { obj_unhide(round_end_blind_token->obj, 0); - tte_printf("#{P:%d,%d; cx:0xE000}%d", ROUND_END_BLIND_REQUIREMENT_RECT.left, ROUND_END_BLIND_REQUIREMENT_RECT.top, blind_get_requirement(current_blind, ante)); + tte_printf("#{P:%d,%d; cx:0xE000}%d", ROUND_END_BLIND_REQ_RECT.left, ROUND_END_BLIND_REQ_RECT.top, blind_get_requirement(current_blind, ante)); int y = 13; @@ -1469,7 +1478,7 @@ void game_round_end() // Writing this kind a made me want to kms. If somewone wa blind_reward--; tte_printf("#{P:%d,%d; cx:0xC000}$%d", BLIND_REWARD_RECT.left , BLIND_REWARD_RECT.top, blind_reward); - tte_printf("#{P:%d,%d; cx:0xC000}$%d", ROUND_END_BLIND_REWARD_RECT.left, ROUND_END_BLIND_REQUIREMENT_RECT.top, blind_get_reward(current_blind) - blind_reward); + tte_printf("#{P:%d,%d; cx:0xC000}$%d", ROUND_END_BLIND_REWARD_RECT.left, ROUND_END_BLIND_REQ_RECT.top, blind_get_reward(current_blind) - blind_reward); if (blind_reward <= 0) { @@ -1741,11 +1750,7 @@ void game_shop() if (timer < 13) { - for (int y = 7; y < 40; y++) - { - int x = 9; - memcpy16(&se_mem[MAIN_BG_SBB][x + 32 * (y - 1)], &se_mem[MAIN_BG_SBB][x + 32 * y], 16); - } + main_bg_se_copy_rect_1_tile_up(POP_MENU_ANIM_RECT); if (timer >= 7) { @@ -1763,7 +1768,7 @@ void game_shop() // { // int y_from = 26; // int y_to = 0; - // memcpy16(&se_mem[MAIN_BG_SE_BASE][32 * y_to], &se_mem[MAIN_BG_SE_BASE][32 * y_from], 9); + // memcpy16(&se_mem[MAIN_BG_SBB][32 * y_to], &se_mem[MAIN_BG_SBB][32 * y_from], 9); // } } } diff --git a/source/graphic_utils.c b/source/graphic_utils.c index a42a877..21925ef 100644 --- a/source/graphic_utils.c +++ b/source/graphic_utils.c @@ -1,22 +1,65 @@ -#include -#include - -#include "graphic_utils.h" - -void main_bg_se_clear_rect(Rect rect) -{ - for (int y = rect.top; y < rect.bottom; y++) - { - for (int x = rect.left; x < rect.right; x++) - { - // I also tried to use se_mat and it didn't work - memset16(&se_mem[MAIN_BG_SBB][x + SCREENBLOCK_ROW_LEN * y], 0x000, 1); - } - } -} - -void tte_erase_rect_wrapper(Rect rect) -{ - tte_erase_rect(rect.left, rect.top, rect.right, rect.bottom); -} - +#include +#include +#include + +#include "graphic_utils.h" + +/* + * + */ +const Rect FULL_SCREENBLOCK_RECT = { 0, 0, SE_ROW_LEN, SE_COL_LEN }; + + +// Clips a rect of screenblock entries to a specified rect +static void clip_se_rect_to_bounding_rect(Rect* rect, const Rect* bounding_rect) +{ + rect->right = min(rect->right, SE_ROW_LEN); + rect->bottom = min(rect->bottom, SE_COL_LEN); + rect->left = max(rect->left, 0); + rect->top = max(rect->top, 0); +} + +// Can be unstaticed if needed +// Clips a rect of screenblock entries to screenblock boundaries +// The bounding rect is not required to be within screenblock boundaries +static void clip_se_rect_to_screenblock(Rect* rect) +{ + clip_se_rect_to_bounding_rect(rect, &FULL_SCREENBLOCK_RECT); +} + + +void main_bg_se_clear_rect(Rect se_rect) +{ + if (se_rect.left > se_rect.right) + return; + // Just to make sure we're not overflowing the boundaries + clip_se_rect_to_screenblock(&se_rect); + + for (int y = se_rect.top; y < se_rect.bottom; y++) + { + memset16(&(se_mem[MAIN_BG_SBB][se_rect.left + SE_ROW_LEN * y]), 0x0000, se_rect.right - se_rect.left); + } +} + +void main_bg_se_copy_rect_1_tile_up(Rect se_rect) +{ + if (se_rect.left > se_rect.right) + return; + + Rect bounding_rect = FULL_SCREENBLOCK_RECT; + bounding_rect.top = 1; // Since we're going up, need to clip to 1 to not overflow + clip_se_rect_to_bounding_rect(&se_rect, &bounding_rect); + + for (int y = se_rect.top; y < se_rect.bottom; y++) + { + memcpy16(&se_mem[MAIN_BG_SBB][se_rect.left + SE_ROW_LEN * (y - 1)], + &se_mem[MAIN_BG_SBB][se_rect.left + SE_ROW_LEN * y], + se_rect.right - se_rect.left); + } +} + +void tte_erase_rect_wrapper(Rect rect) +{ + tte_erase_rect(rect.left, rect.top, rect.right, rect.bottom); +} + From 05f4cc2abb2096964ec8efdf0a2f5c098ec0e46b Mon Sep 17 00:00:00 2001 From: MeirGavish Date: Thu, 10 Jul 2025 20:59:04 +0300 Subject: [PATCH 3/3] Fixed generic main_bg_se_copy_rect_1_tile_vert() function and replaced some code with it --- include/graphic_utils.h | 13 +++++---- source/game.c | 63 ++++++++++++++++------------------------- source/graphic_utils.c | 36 ++++++++++++----------- 3 files changed, 51 insertions(+), 61 deletions(-) diff --git a/include/graphic_utils.h b/include/graphic_utils.h index 796c773..e505f74 100644 --- a/include/graphic_utils.h +++ b/include/graphic_utils.h @@ -31,6 +31,10 @@ #define SE_ROW_LEN 32 #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 + typedef struct { int left; @@ -46,12 +50,11 @@ typedef struct */ void main_bg_se_clear_rect(Rect se_rect); -/* Copies a rect in the main background a single tile up. - * The se_rect dimensions need to be in number of tiles. - * This is for the pop menu animation, can later be generalized to more tiles - * or other directions +/* Copies a rect in the main background vertically in direction by a single tile. + * direction must be either SE_UP or SE_DOWN. + * se_rect dimensions are in number of tiles. */ -void main_bg_se_copy_rect_1_tile_up(Rect se_rect); +void main_bg_se_copy_rect_1_tile_vert(Rect se_rect, int direction); // A wrapper for tte_erase_rect that would use the rect struct void tte_erase_rect_wrapper(Rect rect); diff --git a/source/game.c b/source/game.c index d43bdba..9195bab 100644 --- a/source/game.c +++ b/source/game.c @@ -113,14 +113,18 @@ static inline Card *discard_pop() // Rects left top right bottom // Screenblock rects -static const Rect ROUND_END_MENU_RECT = {9, 7, 25, 21 }; +static const Rect ROUND_END_MENU_RECT = {9, 7, 24, 21 }; -static const Rect POP_MENU_ANIM_RECT = {9, 7, 25, 32 }; -// The rect for popping menu animations - includes both the target and source position -// rects. The source position extends beyond the visible screen to the end -// of the screenblock -// The target position is blank so we just animate the whole thing so we don't -// have to track its position +static const Rect POP_MENU_ANIM_RECT = {9, 6, 24, 32 }; +// The rect for popping menu animations (round end, shop, blinds) - includes both the +// target and source position rects. +// Extends beyond the visible screen to the end of the screenblock +// This is because when popping, the target position is blank so we just animate the whole thing +// so we don't have to track its position +// +// When unpopping we include another row above the menu assuming it's blank so it's copied into it + +static const Rect SHOP_ICON_RECT = { 0, 0, 8, 4 }; // Rects for TTE (in pixels) static const Rect HAND_SIZE_RECT = {128, 128, 152, 160 }; // Seems to include both SELECT and PLAYING @@ -373,7 +377,7 @@ void change_background(int id) // Incoming hack! Clear the round end menu so that we can slowly display it with an animation later. The reason this isn't optimal is because the full background is already loaded into the vram at this point. // I'm just doing it this way because it's easier than doing some weird shit with Grit in order to get a proper tilemap. I'm not the biggest fan of Grit. - // TODO: See TODO comment in game_round_end(), once that is properly done, remove this. + // TODO: Remove this if game_round_end() is fixed to use main_bg_se_copy_rect_1_tile_vert() for the pop menu main_bg_se_clear_rect(ROUND_END_MENU_RECT); //tte_erase_rect(0, 0, 64, 48); // Clear top left corner where the blind stats are displayed @@ -1460,9 +1464,11 @@ void game_playing() void game_round_end() // Writing this kind a made me want to kms. If somewone wants to rewrite this, please do so. { - /* TODO: The correct way to do this is the same as game_shop(), - * put the menu in VRAM outside the screen and copy it from there. - * That code needs to be extracted to a function and reused here. + /* TODO: I could use main_bg_se_copy_rect_1_tile_vert() to replace the menu pop up here + * But there are a bunch of other manual hard-coded tilemap animations in here that + * are very hard to understand, and if I change the background image for the pop up + * it will change the tile charblock layout and screw them up so it's all or nothing... + * - Meir */ static int state = 0; static int sequence_step = 0; // Reusable variable for the animations in states @@ -1799,12 +1805,7 @@ void game_round_end() // Writing this kind a made me want to kms. If somewone wa case 8: { sequence_step++; - int x = 9; - - for (int y = 19; y > 5; y--) - { - memcpy16(&se_mem[MAIN_BG_SBB][x + 32 * (y + 1)], &se_mem[MAIN_BG_SBB][x + 32 * y], 16); - } + main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT, SE_DOWN); if (sequence_step >= 20) { @@ -1868,16 +1869,13 @@ void game_shop() { case 0: // Intro sequence (menu and shop icon coming into frame) { - for (int y = 7; y < 40; y++) // Shift the shop panel - { - int x = 9; - memcpy16(&se_mem[MAIN_BG_SBB][x + 32 * (y - 1)], &se_mem[MAIN_BG_SBB][x + 32 * y], 16); - } + main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT, SE_UP); if (timer >= 7) // Shift the shop icon { int timer_offset = timer - 6; + // TODO: Extract to generic function? for (int y = 0; y < timer_offset; y++) { int y_from = 26 + y - timer_offset; @@ -1969,19 +1967,10 @@ void game_shop() } case 2: // Outro sequence (menu and shop icon going out of frame) { - // This is reused from game_round_end() // Shift the shop panel - int x = 9; - for (int y = 19; y > 5; y--) - { - memcpy16(&se_mem[MAIN_BG_SBB][x + 32 * (y + 1)], &se_mem[MAIN_BG_SBB][x + 32 * y], 16); - } + main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT, SE_DOWN); - // Shift the shop icon - for (int y = 0; y < 5; y++) - { - memcpy16(&se_mem[MAIN_BG_SBB][32 * (y - 1)], &se_mem[MAIN_BG_SBB][32 * y], 9); - } + main_bg_se_copy_rect_1_tile_vert(SHOP_ICON_RECT, SE_UP); if (timer == 1) { @@ -2033,13 +2022,9 @@ 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 { - case 0: // Intro sequence (menu and shop icon coming into frame) + case 0: // Intro sequence (menu coming into frame) { - for (int y = 7; y < 40; y++) - { - int x = 9; - memcpy16(&se_mem[MAIN_BG_SBB][x + 32 * (y - 1)], &se_mem[MAIN_BG_SBB][x + 32 * y], 16); - } + main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT, SE_UP); if (timer == 12) { diff --git a/source/graphic_utils.c b/source/graphic_utils.c index 21925ef..48fa243 100644 --- a/source/graphic_utils.c +++ b/source/graphic_utils.c @@ -4,19 +4,15 @@ #include "graphic_utils.h" -/* - * - */ const Rect FULL_SCREENBLOCK_RECT = { 0, 0, SE_ROW_LEN, SE_COL_LEN }; - // Clips a rect of screenblock entries to a specified rect static void clip_se_rect_to_bounding_rect(Rect* rect, const Rect* bounding_rect) { - rect->right = min(rect->right, SE_ROW_LEN); - rect->bottom = min(rect->bottom, SE_COL_LEN); - rect->left = max(rect->left, 0); - rect->top = max(rect->top, 0); + rect->right = min(rect->right, bounding_rect->right); + rect->bottom = min(rect->bottom, bounding_rect->bottom); + rect->left = max(rect->left, bounding_rect->left); + rect->top = max(rect->top, bounding_rect->top); } // Can be unstaticed if needed @@ -32,29 +28,35 @@ void main_bg_se_clear_rect(Rect se_rect) { if (se_rect.left > se_rect.right) return; - // Just to make sure we're not overflowing the boundaries + // Clip to avoid screenblock overflow clip_se_rect_to_screenblock(&se_rect); for (int y = se_rect.top; y < se_rect.bottom; y++) { - memset16(&(se_mem[MAIN_BG_SBB][se_rect.left + SE_ROW_LEN * y]), 0x0000, se_rect.right - se_rect.left); + memset16(&(se_mem[MAIN_BG_SBB][se_rect.left + SE_ROW_LEN * y]), 0x0000, se_rect.right - se_rect.left + 1); } } -void main_bg_se_copy_rect_1_tile_up(Rect se_rect) +void main_bg_se_copy_rect_1_tile_vert(Rect se_rect, int direction) { - if (se_rect.left > se_rect.right) + if (se_rect.left > se_rect.right + || (direction != SE_UP && direction != SE_DOWN)) return; - + + // Clip to avoid read/write overflow of the screenblock Rect bounding_rect = FULL_SCREENBLOCK_RECT; - bounding_rect.top = 1; // Since we're going up, need to clip to 1 to not overflow + bounding_rect.top = 1; + bounding_rect.bottom = SE_COL_LEN - 1; clip_se_rect_to_bounding_rect(&se_rect, &bounding_rect); - for (int y = se_rect.top; y < se_rect.bottom; y++) + int start = (direction == SE_UP) ? se_rect.top : se_rect.bottom; + int end = (direction == SE_UP) ? se_rect.bottom : se_rect.top; + + for (int y = start; y != end - direction; y -= direction) { - memcpy16(&se_mem[MAIN_BG_SBB][se_rect.left + SE_ROW_LEN * (y - 1)], + memcpy16(&se_mem[MAIN_BG_SBB][se_rect.left + SE_ROW_LEN * (y + direction)], &se_mem[MAIN_BG_SBB][se_rect.left + SE_ROW_LEN * y], - se_rect.right - se_rect.left); + se_rect.right - se_rect.left + 1); } }