From 2b60340747cd81c39757ea65f83d6841092459f7 Mon Sep 17 00:00:00 2001 From: MeirGavish Date: Fri, 11 Jul 2025 03:05:34 +0300 Subject: [PATCH 1/8] Created function main_bg_se_copy_rect --- source/graphic_utils.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/source/graphic_utils.c b/source/graphic_utils.c index 3fb85d6..d6a7afc 100644 --- a/source/graphic_utils.c +++ b/source/graphic_utils.c @@ -41,7 +41,9 @@ void main_bg_se_copy_rect_1_tile_vert(Rect se_rect, int direction) { 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; @@ -60,6 +62,27 @@ void main_bg_se_copy_rect_1_tile_vert(Rect se_rect, int direction) } } +void main_bg_se_copy_rect(Rect rect_from, Rect rect_to) +{ + if (rect_from.left > rect_from.right + || rect_from.top < rect_from.bottom + || rect_to.left > rect_to.right + || rect_from.top < rect_from.bottom + // Check equal dimensions + || rect_from.bottom - rect_from.top != rect_to.bottom - rect_to.top + || rect_from.left - rect_from.right != rect_to.left - rect_to.right) + { + return; + } + + for (int y = rect_from.top; y < rect_from.bottom; y++) + { + memcpy16(&(se_mem[MAIN_BG_SBB][rect_to.left + SE_ROW_LEN * y]), + &(se_mem[MAIN_BG_SBB][rect_from.left + SE_ROW_LEN * y]), + rect_from.right - rect_from.left + 1); + } +} + void tte_erase_rect_wrapper(Rect rect) { tte_erase_rect(rect.left, rect.top, rect.right, rect.bottom); From 297aa22ca13e3d583d20b2d1802678e634c0d631 Mon Sep 17 00:00:00 2001 From: GreenNinja2525 <118409873+GreenNinja2525@users.noreply.github.com> Date: Sat, 12 Jul 2025 23:48:49 -0400 Subject: [PATCH 2/8] Add copyright and disclaimer to address potential Copyright and Legal Action --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a143751..0270f47 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # Balatro GBA This is an attempt to recreate the game 'Balatro' as accurately as possible, including all of the visual effects that make Balatro feel satisfying to play. -This port is strictly limited to the content that was available in the Balatro Steam demo. - +This port is strictly limited to the content that was available in the January 2024 Balatro Steam demo. +### **Disclaimer: This project is NOT endorsed or supported by Playstack or LocalThunk** +#### This project is solely aimed to recreate Balatro's old demo on the Game Boy Advance and is not meant to infringe or draw sales away from the full game's release or any of the established works by Playstack and LocalThunk. All rights are reserved to their respective holders. ![Example](https://github.com/cellos51/balatro-gba/blob/main/example.gif) # **Build Instructions:** @@ -34,3 +35,6 @@ Example: `cd '/home/user/OneDrive/Documents/balatro-gba` or something like this. -Try a different emulator or if you are using original hardware, make sure the rom is not corrupted and try a different flashcart or SD Card. If this does not work, open an issue on the Github page because a recent commit may have broke the game. 5. **It says I don't have `Git` or `Make` installed!** -Use `pacman -S git` or `pacman -S make` although it should already be installed if you followed the instructions correctly. + +#### Purchasing the official game is highly recommended and almost mandatory in order for this project to have a chance of staying up due to copyright issues and potential legal action. +**Purchase the Steam Version here: https://store.steampowered.com/app/2379780/Balatro/** From e65d15654f8a434c39592e44343e131a8a51130a Mon Sep 17 00:00:00 2001 From: MeirGavish Date: Sun, 13 Jul 2025 11:15:30 +0300 Subject: [PATCH 3/8] Fixed compilation error --- source/main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/source/main.c b/source/main.c index 30400f0..06b09be 100644 --- a/source/main.c +++ b/source/main.c @@ -11,7 +11,6 @@ // Graphics #include "deck_gfx.h" #include "background_gfx.h" -#include "background_play_gfx.h" #include "affine_background_gfx.h" // Audio From 4f8930510e5b180df19ea13a6ac1f45bc41a1c26 Mon Sep 17 00:00:00 2001 From: GreenNinja2525 <118409873+GreenNinja2525@users.noreply.github.com> Date: Sun, 13 Jul 2025 12:21:56 -0400 Subject: [PATCH 4/8] Update main.c and remove #include "background_play_gfx.h" --- source/main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/main.c b/source/main.c index 30400f0..e44c227 100644 --- a/source/main.c +++ b/source/main.c @@ -11,7 +11,6 @@ // Graphics #include "deck_gfx.h" #include "background_gfx.h" -#include "background_play_gfx.h" #include "affine_background_gfx.h" // Audio @@ -147,4 +146,4 @@ int main() } return 0; -} \ No newline at end of file +} From f34444b3db0300f4a2934c74f228606d5012e0c6 Mon Sep 17 00:00:00 2001 From: MeirGavish Date: Sun, 13 Jul 2025 17:01:49 +0300 Subject: [PATCH 5/8] Fixed main_bg_se_copy_rect(), exposed it, and used it in a few places --- include/graphic_utils.h | 22 ++++++++++++ source/game.c | 75 ++++++++++++++++++++++++++--------------- source/graphic_utils.c | 26 +++++++------- 3 files changed, 83 insertions(+), 40 deletions(-) diff --git a/include/graphic_utils.h b/include/graphic_utils.h index e505f74..43b59b0 100644 --- a/include/graphic_utils.h +++ b/include/graphic_utils.h @@ -35,6 +35,9 @@ #define SE_UP -1 #define SE_DOWN 1 +// Tile size in pixels, both height and width as tiles are square +#define TILE_SIZE 8 + typedef struct { int left; @@ -45,6 +48,19 @@ typedef struct #define UNDEFINED -1 +INLINE int rect_width(const Rect* rect) +{ + /* Extra parens to avoid issues in case compiler turns INLINE into macro + * Not sure if necessary, could be just paranoia + */ + return (((rect)->right) - ((rect)->left) + 1); +} + +INLINE int rect_height(const Rect* rect) +{ + return (((rect)->bottom) - ((rect)->top) + 1); +} + /* Clears a rect in the main background. * The se_rect dimensions need to be in number of tiles. */ @@ -56,6 +72,12 @@ void main_bg_se_clear_rect(Rect se_rect); */ void main_bg_se_copy_rect_1_tile_vert(Rect se_rect, int direction); +/* Copies the rect in rect_from into rect_to. + * Notice there may be overlaps and overwrites due to them. + * Both ects must be the same size + */ +void main_bg_se_copy_rect(Rect rect_to, Rect rect_from); + // 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 65fa248..eef79e5 100644 --- a/source/game.c +++ b/source/game.c @@ -116,8 +116,12 @@ static inline Card *discard_pop() // Screenblock rects static const Rect ROUND_END_MENU_RECT = {9, 7, 24, 21 }; -static const Rect POP_MENU_ANIM_RECT_DOWN = {9, 6, 24, 32 }; -static const Rect POP_MENU_ANIM_RECT_UP = {9, 7, 24, 32 }; +// TODO: Currently unused, remove? +//static const Rect POP_MENU_ANIM_RECT_SRC = {9, 19, 24, 31 }; +//static const Rect POP_MENU_ANIM_RECT_DEST = {9, 7, 24, 19 }; + +static const Rect POP_MENU_ANIM_RECT_DOWN = {9, 6, 24, 31 }; +static const Rect POP_MENU_ANIM_RECT_UP = {9, 7, 24, 31 }; // The rects 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. @@ -126,9 +130,22 @@ static const Rect POP_MENU_ANIM_RECT_UP = {9, 7, 24, 32 }; // // In the down version we include another row above the menu assuming it's blank so it's copied into it -static const Rect SINGLE_BLIND_SELECT_RECT = { 9, 7, 13, 32 }; +static const Rect SINGLE_BLIND_SELECT_RECT = {9, 7, 13, 31 }; + +static const Rect TOP_LEFT_ITEM_SRC_RECT = {0, 20, 8, 25 }; +static const Rect TOP_LEFT_PANEL_RECT = {0, 0, 8, 5 }; +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 + * and the top of the score panel in the tiles connecting them. + * TOP_LEFT_PANEL_ANIM_RECT should be used for animations, + * TOP_LEFT_PANEL_RECT for copies etc. but mind the overlap + */ +static const Rect TOP_LEFT_BLIND_TITLE_RECT = {0, 1, 8, 1 }; +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 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 @@ -159,7 +176,7 @@ 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 }; -//TODO: Different number ? 11 ? +//TODO: Properly define and use #define MENU_POP_OUT_ANIM_FRAMES 20 // General functions @@ -353,6 +370,14 @@ enum HandType hand_get_type() return res_hand_type; } +/* Copies the appropriate item into the top left panel (blind/shop icon) + * from where it was put outside the screenview + */ +void bg_copy_current_item_to_top_left_panel() +{ + main_bg_se_copy_rect(TOP_LEFT_PANEL_RECT, TOP_LEFT_ITEM_SRC_RECT); +} + void change_background(int id) { if (background == id) @@ -370,24 +395,19 @@ void change_background(int id) tte_erase_rect_wrapper(HAND_SIZE_RECT_PLAYING); - for (int y = 0; y < 6; y++) // Copies the blind panel from the bottom of the screen to the top. This code is identical to the one used in game_shop() - { - int y_from = 20 + y; - int y_to = 0 + y; - memcpy16(&se_mem[MAIN_BG_SBB][32 * y_to], &se_mem[MAIN_BG_SBB][32 * y_from], 9); - } - + /* TODO: The blind panel should come in with an animation when it's selected, + * probably should happen elsewhere + */ + bg_copy_current_item_to_top_left_panel(); + + // TODO: These cases are still untested if (current_blind == BIG_BLIND) // Change text and palette depending on blind type { - int y_from = 26; - int y_to = 1; - memcpy16(&se_mem[MAIN_BG_SBB][32 * y_to], &se_mem[MAIN_BG_SBB][32 * y_from], 9); + main_bg_se_copy_rect(TOP_LEFT_BLIND_TITLE_RECT, BIG_BLIND_TITLE_SRC_RECT); } else if (current_blind == BOSS_BLIND) { - int y_from = 27; - int y_to = 1; - memcpy16(&se_mem[MAIN_BG_SBB][32 * y_to], &se_mem[MAIN_BG_SBB][32 * y_from], 9); + main_bg_se_copy_rect(TOP_LEFT_BLIND_TITLE_RECT, BOSS_BLIND_TITLE_SRC_RECT); } // This would change the palette of the background to match the blind, but the backgroun doesn't use the blind token's exact colors so a different approach is required @@ -461,7 +481,7 @@ void change_background(int id) obj_unhide(blind_select_tokens[BIG_BLIND]->obj, 0); obj_unhide(blind_select_tokens[BOSS_BLIND]->obj, 0); - const int default_y = 89 + (8 * 12); // Default y position for the blind select tokens. 8 is the size of a tile and 12 is the amound of tiles the background is shifted down by + const int default_y = 89 + (TILE_SIZE * 12); // Default y position for the blind select tokens. 8 is the size of a tile and 12 is the amound of tiles the background is shifted down by sprite_position(blind_select_tokens[SMALL_BLIND], 80, default_y); sprite_position(blind_select_tokens[BIG_BLIND], 120, default_y); sprite_position(blind_select_tokens[BOSS_BLIND], 160, default_y); @@ -503,9 +523,9 @@ void change_background(int id) Rect blind_rect = SINGLE_BLIND_SELECT_RECT; - // + 1 to get to the start of the next blind, no gap between them - blind_rect.left += i * (SINGLE_BLIND_SELECT_RECT.right - SINGLE_BLIND_SELECT_RECT.left + 1); - blind_rect.right += i * (SINGLE_BLIND_SELECT_RECT.right - SINGLE_BLIND_SELECT_RECT.left + 1); + // There's no gap between them + blind_rect.left += i * rect_width(&SINGLE_BLIND_SELECT_RECT); + blind_rect.right += i * rect_width(&SINGLE_BLIND_SELECT_RECT); main_bg_se_copy_rect_1_tile_vert(blind_rect, SE_UP); int x_to = blind_rect.left; @@ -521,16 +541,17 @@ void change_background(int id) y_from = 30; } + // TODO: What is this doing? I couldn't figure out and it seems unnecessary memcpy16(&se_mem[MAIN_BG_SBB][x_to + 32 * y_to], &se_mem[MAIN_BG_SBB][x_from + 32 * y_from], 5); - sprite_position(blind_select_tokens[i], blind_select_tokens[i]->pos.x, blind_select_tokens[i]->pos.y - 8); // Move token up by a tile + sprite_position(blind_select_tokens[i], blind_select_tokens[i]->pos.x, blind_select_tokens[i]->pos.y - TILE_SIZE); // Move token up by a tile } else if (blinds[i] == BLIND_UPCOMING) // Change the select icon to "NEXT" { int x_from = 0; int y_from = 20; - int x_to = 10 + (i * 5); + int x_to = 10 + (i * rect_width(&SINGLE_BLIND_SELECT_RECT)); int y_to = 20; memcpy16(&se_mem[MAIN_BG_SBB][x_to + 32 * y_to], &se_mem[MAIN_BG_SBB][x_from + 32 * y_from], 3); @@ -2093,7 +2114,7 @@ void game_shop() // Shift the shop panel main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT_DOWN, SE_DOWN); - main_bg_se_copy_rect_1_tile_vert(SHOP_ICON_RECT, SE_UP); + main_bg_se_copy_rect_1_tile_vert(TOP_LEFT_PANEL_ANIM_RECT, SE_UP); if (timer == 1) { @@ -2193,7 +2214,7 @@ void game_blind_select() for (int i = 0; i < MAX_BLINDS; i++) { - sprite_position(blind_select_tokens[i], blind_select_tokens[i]->pos.x, blind_select_tokens[i]->pos.y - (8 * 12)); + sprite_position(blind_select_tokens[i], blind_select_tokens[i]->pos.x, blind_select_tokens[i]->pos.y - (TILE_SIZE * 12)); } timer = 0; @@ -2221,7 +2242,7 @@ void game_blind_select() for (int i = 0; i < MAX_BLINDS; i++) { - sprite_position(blind_select_tokens[i], blind_select_tokens[i]->pos.x, blind_select_tokens[i]->pos.y + 8); + sprite_position(blind_select_tokens[i], blind_select_tokens[i]->pos.x, blind_select_tokens[i]->pos.y + TILE_SIZE); } } // TODO: Currently selecting other blinds crashes, remove this condition once fixed diff --git a/source/graphic_utils.c b/source/graphic_utils.c index 6b6344c..050b853 100644 --- a/source/graphic_utils.c +++ b/source/graphic_utils.c @@ -4,7 +4,7 @@ #include "graphic_utils.h" -const Rect FULL_SCREENBLOCK_RECT = { 0, 0, SE_ROW_LEN, SE_COL_LEN }; +const Rect FULL_SCREENBLOCK_RECT = { 0, 0, SE_ROW_LEN, SE_COL_LEN }; // TODO: LEN - 1 ... // Clips a rect of screenblock entries to a specified rect static void clip_se_rect_to_bounding_rect(Rect* rect, const Rect* bounding_rect) @@ -33,7 +33,7 @@ void main_bg_se_clear_rect(Rect 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 + 1); + memset16(&(se_mem[MAIN_BG_SBB][se_rect.left + SE_ROW_LEN * y]), 0x0000, rect_width(&se_rect)); } } @@ -58,33 +58,33 @@ void main_bg_se_copy_rect_1_tile_vert(Rect se_rect, int direction) { 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 + 1); + rect_width(&se_rect)); } if (direction == SE_DOWN) { - memset16(&se_mem[MAIN_BG_SBB][se_rect.left + SE_ROW_LEN * (end)], 0x0000, se_rect.right - se_rect.left + 1); // This clears the top row when going down, or the bottom row when going up. + memset16(&se_mem[MAIN_BG_SBB][se_rect.left + SE_ROW_LEN * (end)], 0x0000, rect_width(&se_rect)); // This clears the top row when going down, or the bottom row when going up. } } -void main_bg_se_copy_rect(Rect rect_from, Rect rect_to) +void main_bg_se_copy_rect(Rect rect_to, Rect rect_from) { if (rect_from.left > rect_from.right - || rect_from.top < rect_from.bottom + || rect_from.top > rect_from.bottom || rect_to.left > rect_to.right - || rect_from.top < rect_from.bottom + || rect_from.top > rect_from.bottom // Check equal dimensions - || rect_from.bottom - rect_from.top != rect_to.bottom - rect_to.top - || rect_from.left - rect_from.right != rect_to.left - rect_to.right) + || rect_width(&rect_from) != rect_width(&rect_to) + || rect_height(&rect_from) != rect_height(&rect_to)) { return; } - for (int y = rect_from.top; y < rect_from.bottom; y++) + for (int y = 0; y < rect_height(&rect_from); y++) { - memcpy16(&(se_mem[MAIN_BG_SBB][rect_to.left + SE_ROW_LEN * y]), - &(se_mem[MAIN_BG_SBB][rect_from.left + SE_ROW_LEN * y]), - rect_from.right - rect_from.left + 1); + memcpy16(&(se_mem[MAIN_BG_SBB][rect_to.left + SE_ROW_LEN * (rect_to.top + y)]), + &(se_mem[MAIN_BG_SBB][rect_from.left + SE_ROW_LEN * (rect_from.top + y)]), + rect_width(&rect_from)); } } From e7589f27dc4dc29c6da48a74b755a005b9c85563 Mon Sep 17 00:00:00 2001 From: MeirGavish Date: Sun, 13 Jul 2025 21:20:57 +0300 Subject: [PATCH 6/8] Added move version (...)se_move_rect_1_tile_vert(), switched to use it in down pop-out animations, updated copy version, and removed my POP_MENU_ANIM_RECT UP/DOWN hack --- include/graphic_utils.h | 7 +++++++ source/game.c | 23 +++++++++++------------ source/graphic_utils.c | 37 +++++++++++++++++++++++++++++-------- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/include/graphic_utils.h b/include/graphic_utils.h index 43b59b0..8ffeac4 100644 --- a/include/graphic_utils.h +++ b/include/graphic_utils.h @@ -72,6 +72,13 @@ void main_bg_se_clear_rect(Rect se_rect); */ void main_bg_se_copy_rect_1_tile_vert(Rect se_rect, int direction); +/* Moves a rect in the main background vertically in direction by a single tile. + * Note that tiles in the previous location will be transparent (0x000) + * direction must be either SE_UP or SE_DOWN. + * se_rect dimensions are in number of tiles. + */ +void main_bg_se_move_rect_1_tile_vert(Rect se_rect, int direction); + /* Copies the rect in rect_from into rect_to. * Notice there may be overlaps and overwrites due to them. * Both ects must be the same size diff --git a/source/game.c b/source/game.c index eef79e5..64d9f25 100644 --- a/source/game.c +++ b/source/game.c @@ -120,15 +120,12 @@ static const Rect ROUND_END_MENU_RECT = {9, 7, 24, 21 }; //static const Rect POP_MENU_ANIM_RECT_SRC = {9, 19, 24, 31 }; //static const Rect POP_MENU_ANIM_RECT_DEST = {9, 7, 24, 19 }; -static const Rect POP_MENU_ANIM_RECT_DOWN = {9, 6, 24, 31 }; -static const Rect POP_MENU_ANIM_RECT_UP = {9, 7, 24, 31 }; -// The rects for popping menu animations (round end, shop, blinds) +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 -// -// In the down version we include another row above the menu assuming it's blank so it's copied into it static const Rect SINGLE_BLIND_SELECT_RECT = {9, 7, 13, 31 }; @@ -430,7 +427,7 @@ void change_background(int id) for (int i = 0; i <= 2; i++) { - main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT_UP, SE_DOWN); + main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT, SE_DOWN); } tte_erase_rect_wrapper(HAND_SIZE_RECT_SELECT); @@ -1949,7 +1946,7 @@ void game_round_end() // Writing this kind a made me want to kms. If somewone wa case 8: // Shift the round end panel back out of view and go to the next state { sequence_step++; - main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT_DOWN, SE_DOWN); + main_bg_se_move_rect_1_tile_vert(POP_MENU_ANIM_RECT, SE_DOWN); if (sequence_step >= 20) { @@ -2013,7 +2010,7 @@ void game_shop() { case 0: // Intro sequence (menu and shop icon coming into frame) { - main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT_UP, SE_UP); + main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT, SE_UP); if (timer >= 7) // Shift the shop icon { @@ -2112,7 +2109,7 @@ void game_shop() case 2: // Outro sequence (menu and shop icon going out of frame) { // Shift the shop panel - main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT_DOWN, SE_DOWN); + main_bg_se_move_rect_1_tile_vert(POP_MENU_ANIM_RECT, SE_DOWN); main_bg_se_copy_rect_1_tile_vert(TOP_LEFT_PANEL_ANIM_RECT, SE_UP); @@ -2167,7 +2164,7 @@ void game_blind_select() { case 0: // Intro sequence (menu coming into frame) { - main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT_UP, SE_UP); + main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT, SE_UP); for (int i = 0; i < MAX_BLINDS; i++) { @@ -2209,7 +2206,7 @@ void game_blind_select() for (int i = 0; i < 12; i++) { - main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT_UP, SE_UP); + main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT, SE_UP); } for (int i = 0; i < MAX_BLINDS; i++) @@ -2238,7 +2235,9 @@ void game_blind_select() { if (timer < 15) { - main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT_DOWN, SE_DOWN); + Rect blinds_rect = POP_MENU_ANIM_RECT; + blinds_rect.top -= 1; // Because of the raised blind + main_bg_se_move_rect_1_tile_vert(blinds_rect, SE_DOWN); for (int i = 0; i < MAX_BLINDS; i++) { diff --git a/source/graphic_utils.c b/source/graphic_utils.c index 050b853..0096d7c 100644 --- a/source/graphic_utils.c +++ b/source/graphic_utils.c @@ -4,9 +4,10 @@ #include "graphic_utils.h" -const Rect FULL_SCREENBLOCK_RECT = { 0, 0, SE_ROW_LEN, SE_COL_LEN }; // TODO: LEN - 1 ... +const Rect FULL_SCREENBLOCK_RECT = { 0, 0, SE_ROW_LEN, SE_COL_LEN }; // Clips a rect of screenblock entries to a specified rect +// The bounding rect is not required to be within screenblock boundaries static void clip_se_rect_to_bounding_rect(Rect* rect, const Rect* bounding_rect) { rect->right = min(rect->right, bounding_rect->right); @@ -17,12 +18,20 @@ static void clip_se_rect_to_bounding_rect(Rect* rect, const Rect* bounding_rect) // 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); } +// Clips a rect of screenblock entries to be within one step of +// screenblock boundaries vertically (1 step from top, 1 step from bottom. +static void clip_se_rect_within_step_of_full_screen_vert(Rect* se_rect) +{ + Rect bounding_rect = FULL_SCREENBLOCK_RECT; + bounding_rect.top += 1; + bounding_rect.bottom -= -1; + clip_se_rect_to_bounding_rect(se_rect, &bounding_rect); +} void main_bg_se_clear_rect(Rect se_rect) { @@ -37,6 +46,8 @@ void main_bg_se_clear_rect(Rect se_rect) } } + + void main_bg_se_copy_rect_1_tile_vert(Rect se_rect, int direction) { if (se_rect.left > se_rect.right @@ -46,10 +57,7 @@ void main_bg_se_copy_rect_1_tile_vert(Rect se_rect, int direction) } // Clip to avoid read/write overflow of the screenblock - Rect bounding_rect = FULL_SCREENBLOCK_RECT; - bounding_rect.top = 1; - bounding_rect.bottom = SE_COL_LEN - 1; - clip_se_rect_to_bounding_rect(&se_rect, &bounding_rect); + clip_se_rect_within_step_of_full_screen_vert(&se_rect); int start = (direction == SE_UP) ? se_rect.top : se_rect.bottom; int end = (direction == SE_UP) ? se_rect.bottom : se_rect.top; @@ -60,11 +68,24 @@ void main_bg_se_copy_rect_1_tile_vert(Rect se_rect, int direction) &se_mem[MAIN_BG_SBB][se_rect.left + SE_ROW_LEN * y], rect_width(&se_rect)); } +} - if (direction == SE_DOWN) +void main_bg_se_move_rect_1_tile_vert(Rect se_rect, int direction) +{ + if (se_rect.left > se_rect.right + || (direction != SE_UP && direction != SE_DOWN)) { - memset16(&se_mem[MAIN_BG_SBB][se_rect.left + SE_ROW_LEN * (end)], 0x0000, rect_width(&se_rect)); // This clears the top row when going down, or the bottom row when going up. + return; } + + // Clip to avoid read/write overflow of the screenblock + clip_se_rect_within_step_of_full_screen_vert(&se_rect); + + int end = (direction == SE_UP) ? se_rect.bottom : se_rect.top; + + main_bg_se_copy_rect_1_tile_vert(se_rect, direction); + + memset16(&se_mem[MAIN_BG_SBB][se_rect.left + SE_ROW_LEN * (end)], 0x0000, rect_width(&se_rect)); } void main_bg_se_copy_rect(Rect rect_to, Rect rect_from) From 8cbfa705906d77b83ea9d6a260c35be0adc14777 Mon Sep 17 00:00:00 2001 From: MeirGavish Date: Sun, 13 Jul 2025 23:17:37 +0300 Subject: [PATCH 7/8] Switched hand background playing/selecting switch to use small moves instead of big copies --- include/graphic_utils.h | 3 ++- source/game.c | 20 ++++++++++++++------ source/graphic_utils.c | 6 ++---- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/include/graphic_utils.h b/include/graphic_utils.h index 8ffeac4..2e497fd 100644 --- a/include/graphic_utils.h +++ b/include/graphic_utils.h @@ -74,6 +74,7 @@ void main_bg_se_copy_rect_1_tile_vert(Rect se_rect, int direction); /* Moves a rect in the main background vertically in direction by a single tile. * Note that tiles in the previous location will be transparent (0x000) + * so maybe copy would be a better choice if you don't want to delete things * direction must be either SE_UP or SE_DOWN. * se_rect dimensions are in number of tiles. */ @@ -81,7 +82,7 @@ void main_bg_se_move_rect_1_tile_vert(Rect se_rect, int direction); /* Copies the rect in rect_from into rect_to. * Notice there may be overlaps and overwrites due to them. - * Both ects must be the same size + * Both rects must be the same size */ void main_bg_se_copy_rect(Rect rect_to, Rect rect_from); diff --git a/source/game.c b/source/game.c index 64d9f25..8c0a619 100644 --- a/source/game.c +++ b/source/game.c @@ -129,6 +129,10 @@ static const Rect POP_MENU_ANIM_RECT = {9, 7, 24, 31 }; static const Rect SINGLE_BLIND_SELECT_RECT = {9, 7, 13, 31 }; +static const Rect HAND_BG_RECT_SELECTING = {9, 11, 24, 15 }; +// TODO: Currently unused, remove? +//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 Rect TOP_LEFT_PANEL_RECT = {0, 0, 8, 5 }; static const Rect TOP_LEFT_PANEL_ANIM_RECT = {0, 0, 8, 4 }; @@ -143,7 +147,6 @@ static const Rect TOP_LEFT_BLIND_TITLE_RECT = {0, 1, 8, 1 }; static const Rect BIG_BLIND_TITLE_SRC_RECT = {0, 26, 8, 26 }; static const Rect BOSS_BLIND_TITLE_SRC_RECT = {0, 27, 8, 27 }; - // 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 }; @@ -425,9 +428,12 @@ void change_background(int id) background = BG_ID_CARD_PLAYING; } + Rect curr_hand_bg_rect = HAND_BG_RECT_SELECTING; for (int i = 0; i <= 2; i++) { - main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT, SE_DOWN); + main_bg_se_move_rect_1_tile_vert(POP_MENU_ANIM_RECT, SE_DOWN); + curr_hand_bg_rect.top++; + curr_hand_bg_rect.bottom++; } tte_erase_rect_wrapper(HAND_SIZE_RECT_SELECT); @@ -445,7 +451,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: Remove this if game_round_end() is fixed to use main_bg_se_copy_rect_1_tile_vert() for the pop menu + // TODO: Remove this if game_round_end() is fixed to use main_bg_se_copy/move_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 @@ -499,12 +505,13 @@ void change_background(int id) { if (blinds[i] != BLIND_CURRENT && (i == SMALL_BLIND || i == BIG_BLIND)) // Make the skip button gray { + // TODO: Switch all the copies here to use main_bg_se_copy_rect() int x_from = 0; int y_from = 24 + (i * 4); int x_to = 9 + (i * 5); int y_to = 29; - + for (int j = 0; j < 3; j++) { memcpy16(&se_mem[MAIN_BG_SBB][x_to + 32 * y_to], &se_mem[MAIN_BG_SBB][x_from + 32 * y_from], 5); @@ -538,7 +545,7 @@ void change_background(int id) y_from = 30; } - // TODO: What is this doing? I couldn't figure out and it seems unnecessary + // TODO: What is this doing? More importantly, why? memcpy16(&se_mem[MAIN_BG_SBB][x_to + 32 * y_to], &se_mem[MAIN_BG_SBB][x_from + 32 * y_from], 5); sprite_position(blind_select_tokens[i], blind_select_tokens[i]->pos.x, blind_select_tokens[i]->pos.y - TILE_SIZE); // Move token up by a tile @@ -548,7 +555,7 @@ void change_background(int id) int x_from = 0; int y_from = 20; - int x_to = 10 + (i * rect_width(&SINGLE_BLIND_SELECT_RECT)); + int x_to = 10 + (i * 5); int y_to = 20; memcpy16(&se_mem[MAIN_BG_SBB][x_to + 32 * y_to], &se_mem[MAIN_BG_SBB][x_from + 32 * y_from], 3); @@ -2204,6 +2211,7 @@ void game_blind_select() background = -1; // Force refresh of the background change_background(BG_ID_BLIND_SELECT); + // TODO: Create a generic vertical move by any number of tiles to avoid for loops? for (int i = 0; i < 12; i++) { main_bg_se_copy_rect_1_tile_vert(POP_MENU_ANIM_RECT, SE_UP); diff --git a/source/graphic_utils.c b/source/graphic_utils.c index 0096d7c..9f8079f 100644 --- a/source/graphic_utils.c +++ b/source/graphic_utils.c @@ -46,8 +46,6 @@ void main_bg_se_clear_rect(Rect se_rect) } } - - void main_bg_se_copy_rect_1_tile_vert(Rect se_rect, int direction) { if (se_rect.left > se_rect.right @@ -81,11 +79,11 @@ void main_bg_se_move_rect_1_tile_vert(Rect se_rect, int direction) // Clip to avoid read/write overflow of the screenblock clip_se_rect_within_step_of_full_screen_vert(&se_rect); - int end = (direction == SE_UP) ? se_rect.bottom : se_rect.top; + int deleted_y = (direction == SE_UP) ? se_rect.bottom : se_rect.top; main_bg_se_copy_rect_1_tile_vert(se_rect, direction); - memset16(&se_mem[MAIN_BG_SBB][se_rect.left + SE_ROW_LEN * (end)], 0x0000, rect_width(&se_rect)); + memset16(&se_mem[MAIN_BG_SBB][se_rect.left + SE_ROW_LEN * (deleted_y)], 0x0000, rect_width(&se_rect)); } void main_bg_se_copy_rect(Rect rect_to, Rect rect_from) From 5d5a8a2d4b935118d8d42467695d7f682ebb14a1 Mon Sep 17 00:00:00 2001 From: MeirGavish Date: Sun, 13 Jul 2025 23:42:03 +0300 Subject: [PATCH 8/8] Fixed an error in previous hand background commit --- source/game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/game.c b/source/game.c index 8c0a619..90cbf21 100644 --- a/source/game.c +++ b/source/game.c @@ -431,7 +431,7 @@ void change_background(int id) Rect curr_hand_bg_rect = HAND_BG_RECT_SELECTING; for (int i = 0; i <= 2; i++) { - main_bg_se_move_rect_1_tile_vert(POP_MENU_ANIM_RECT, SE_DOWN); + main_bg_se_move_rect_1_tile_vert(curr_hand_bg_rect, SE_DOWN); curr_hand_bg_rect.top++; curr_hand_bg_rect.bottom++; }