diff --git a/README.md b/README.md
index 1e8f868..59b4fde 100644
--- a/README.md
+++ b/README.md
@@ -1,55 +1,78 @@
-# 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.
+# Balatro-GBA
-This port is strictly limited in content to a minimal version of Balatro and will not recreate the full game.
-### **Disclaimer: This project is NOT endorsed by or affiliated with Playstack or LocalThunk**
-#### This is a non-profit community fan project solely aimed to recreate a minimal version of Balatro on the Game Boy Advance as a tribute to the full Balatro 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.
+[](https://github.com/cellos51/balatro-gba/actions)
+[](https://github.com/cellos51/balatro-gba/issues)
+[](https://github.com/cellos51/balatro-gba/pulls)
+
+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 **tech-demo/proof of concept** is strictly limited in content to a minimal version of Balatro and will **NOT** recreate the full game. **This version is intended for people who already own and know how the official full game works.** Please refer to the Balatro Wiki if you need help understanding certain mechanics or abilities.
+
+
+
+
+
+### Disclaimer: This project is NOT endorsed by or affiliated with Playstack or LocalThunk
+#### This is a non-profit community fan project solely aimed to recreate a minimal version of Balatro on the Game Boy Advance as a tribute to the full Balatro and is not intended 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.
-This version is meant for people who already own and know the official full game.
-Buy the official full version
+### Please buy the official full version from these sources below:
+[](https://store.steampowered.com/app/2379780/Balatro/)
+[](https://play.google.com/store/apps/details?id=com.playstack.balatro.android)
+[](https://apps.apple.com/us/app/balatro/id6502453075)
+[](https://www.nintendo.com/us/store/products/balatro-switch/)
+[](https://store.playstation.com/en-us/concept/10010334)
+[](https://www.xbox.com/en-US/games/store/balatro/9PK087LNGJC5)
+[](https://www.humblebundle.com/store/balatro?srsltid=AfmBOoqS2De8T4kizzWxJS1pbvQosJ_bYCl4qvC6LA1YLPAh4sZ8vJqO)
-[Balatro on Steam](https://store.steampowered.com/app/2379780/Balatro/)
+
+
-[Balatro on Google Play](https://play.google.com/store/apps/details?id=com.playstack.balatro.android)
-[Balatro on Apple App Store](https://apps.apple.com/us/app/balatro/id6502453075)
+### Controls:
+(A: Pick Card/Make Selections)
-[Balatro on Switch](https://www.nintendo.com/us/store/products/balatro-switch/)
+(B: Deselect All Cards)
-[Balatro on PlayStation Store](https://store.playstation.com/en-us/concept/10010334)
+(L: Sell Joker)
-[Balatro on Xbox](https://www.xbox.com/games/store/balatro/9PK087LNGJC5)
-
-
+(R: Sort Suit/Rank)
+(D-Pad: Navigation)
# **Build Instructions:**
## **-Windows-**
1. Install `devkitPro` from https://github.com/devkitPro/installer by downloading the latest release.
-2. Execute it and follow the installation instructions, you only need to install the tools for the GBA development so ensure you have it selected and install it to the default directory.
+
+2. Open it and follow the installation instructions, you only need to install the tools for the GBA development so ensure you have it selected and install it to the default directory.
+
3. Go to the Start Menu and search for `MSys2` and open it.
+
4. Install `Git` by typing this command: `pacman -S git` if you don't have it already installed.
+
5. Navigate to where you want to clone the project such as your desktop or Documents folder.
The easiest way to properly `cd` to the directory is to type `cd` and drag and drop the folder into the terminal window and press Enter.
-Example: `cd '/home/user/OneDrive/Documents/balatro-gba` or something like this.
-6. Clone the project with `git clone --recursive https://github.com/cellos51/balatro-gba.git` in the terminal window.
+- Example: `cd /home/user/OneDrive/Desktop/balatro-gba` or something like this.
+
+6. Clone the project with `git clone https://github.com/cellos51/balatro-gba.git` in the terminal window.
+
7. Use `cd` and drag the newly generated folder into the window to add the file path and press Enter.
+
8. Type `make` into the window and press Enter to start building the game.
+
9. After it completes, navigate through the `build` directory in the project folder and look for `balatro-gba.gba` and load it on an emulator or flashcart.
-## **-Linux-**
-[No Instructions Available]
+## **Common Issues:**
+#### 1. **When I drag the folder into the window and press enter, it doesn't work!**
+- After dragging the folder, nake sure to click back into the window and press enter
-### **Common Issues:**
-1. **When I drag the folder into the window and press enter, it doesn't work!**
--After dragging the folder, press the right or left arrow key until the text is no longer highlighted and then press enter.
-2. **When I run `make` it errors out and won't compile!**
--Move the project folder to your desktop and then `cd` to it by dragging the folder into the terminal window after typing `cd` first.
-3. **I can't find the build folder!**
--Look for a folder called `build` inside the project folder and then find `balatro-gba.gba`.
-4. **The Game won't start!**
--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.
+#### 2. **When I run `make` it errors out and won't compile!**
+- Move the project folder to your desktop and then `cd` to it by dragging the folder into the terminal window after typing `cd` first.
+#### 3. **I can't find the compiled rom!**
+- Look for a folder called `build` inside the project folder and then find `balatro-gba.gba`.
+
+#### 4. **The Game won't start!**
+- 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.
diff --git a/example.gif b/example.gif
index b83c0e1..e2d598d 100644
Binary files a/example.gif and b/example.gif differ
diff --git a/graphics/affine_background_gfx.grit b/graphics/affine_background_gfx.grit
index 3205289..1bc6094 100644
--- a/graphics/affine_background_gfx.grit
+++ b/graphics/affine_background_gfx.grit
@@ -1 +1 @@
--gt -gB8 -mLa -mRa
\ No newline at end of file
+-gt -gB8 -mLa -mRa -pe 16
\ No newline at end of file
diff --git a/graphics/affine_main_menu_background_gfx.grit b/graphics/affine_main_menu_background_gfx.grit
new file mode 100644
index 0000000..1bc6094
--- /dev/null
+++ b/graphics/affine_main_menu_background_gfx.grit
@@ -0,0 +1 @@
+-gt -gB8 -mLa -mRa -pe 16
\ No newline at end of file
diff --git a/graphics/affine_main_menu_background_gfx.png b/graphics/affine_main_menu_background_gfx.png
new file mode 100644
index 0000000..74b938a
Binary files /dev/null and b/graphics/affine_main_menu_background_gfx.png differ
diff --git a/graphics/background_blind_select_gfx.grit b/graphics/background_blind_select_gfx.grit
index f57bef6..2d86f5c 100644
--- a/graphics/background_blind_select_gfx.grit
+++ b/graphics/background_blind_select_gfx.grit
@@ -1 +1 @@
--gB8 -mR8 -mLs -mRtf
\ No newline at end of file
+-gB8 -mR8 -mLs -mRtf -pe 160
\ No newline at end of file
diff --git a/graphics/background_gfx.grit b/graphics/background_gfx.grit
index f57bef6..2d86f5c 100644
--- a/graphics/background_gfx.grit
+++ b/graphics/background_gfx.grit
@@ -1 +1 @@
--gB8 -mR8 -mLs -mRtf
\ No newline at end of file
+-gB8 -mR8 -mLs -mRtf -pe 160
\ No newline at end of file
diff --git a/graphics/background_main_menu_gfx.grit b/graphics/background_main_menu_gfx.grit
new file mode 100644
index 0000000..2d86f5c
--- /dev/null
+++ b/graphics/background_main_menu_gfx.grit
@@ -0,0 +1 @@
+-gB8 -mR8 -mLs -mRtf -pe 160
\ No newline at end of file
diff --git a/graphics/background_main_menu_gfx.png b/graphics/background_main_menu_gfx.png
new file mode 100644
index 0000000..9ea9aff
Binary files /dev/null and b/graphics/background_main_menu_gfx.png differ
diff --git a/graphics/background_shop_gfx.grit b/graphics/background_shop_gfx.grit
index f57bef6..2d86f5c 100644
--- a/graphics/background_shop_gfx.grit
+++ b/graphics/background_shop_gfx.grit
@@ -1 +1 @@
--gB8 -mR8 -mLs -mRtf
\ No newline at end of file
+-gB8 -mR8 -mLs -mRtf -pe 160
\ No newline at end of file
diff --git a/include/affine_background.h b/include/affine_background.h
index 2b599f2..d1ee08d 100644
--- a/include/affine_background.h
+++ b/include/affine_background.h
@@ -5,9 +5,15 @@
#include "graphic_utils.h"
-#define AFFINE_BG_PAL_LEN 5
-#define AFFINE_BG_PB (PAL_ROW_LEN * 15) // This isn't really a palette bank, just the starting index of the palette
#define AFFINE_BG_IDX 2 // The index of the affine background BGCNT register etc.
+#define AFFINE_BG_PAL_LEN 16
+#define AFFINE_BG_PB (PAL_ROW_LEN * 10) // This isn't really a palette bank, just the starting index of the palette
+
+enum AffineBackgroundID
+{
+ AFFINE_BG_MAIN_MENU,
+ AFFINE_BG_GAME,
+};
void affine_background_init();
IWRAM_CODE void affine_background_hblank();
@@ -15,5 +21,6 @@ IWRAM_CODE void affine_background_update();
void affine_background_set_color(COLOR color);
// Must be called with an array of size at least AFFINE_BG_PAL_LEN
void affine_background_load_palette(const u16 *src);
+void affine_background_change_background(enum AffineBackgroundID new_bg);
#endif // AFFINE_BACKGROUND_H
diff --git a/include/game.h b/include/game.h
index ed65d3d..f995216 100644
--- a/include/game.h
+++ b/include/game.h
@@ -16,6 +16,7 @@
#define BG_ID_ROUND_END 3
#define BG_ID_SHOP 4
#define BG_ID_BLIND_SELECT 5
+#define BG_ID_MAIN_MENU 6
// Input bindings
#define SELECT_CARD KEY_A
@@ -27,6 +28,8 @@
enum GameState
{
+ GAME_SPLASH_SCREEN,
+ GAME_MAIN_MENU,
GAME_PLAYING,
GAME_ROUND_END,
GAME_SHOP,
@@ -75,6 +78,7 @@ enum HandType
// Game functions
void game_init();
void game_update();
+void game_set_state(enum GameState new_game_state);
// Forward declaration
struct List;
diff --git a/include/graphic_utils.h b/include/graphic_utils.h
index 1df8e49..7fb6607 100644
--- a/include/graphic_utils.h
+++ b/include/graphic_utils.h
@@ -173,4 +173,10 @@ void memcpy16_tile8_with_palette_offset(u16* dst, const u16* src, uint hwcount,
*/
void memcpy32_tile8_with_palette_offset(u32* dst, const u32* src, uint wcount, u8 palette_offset);
+/* Toggles the visibility of the window layers.
+ * win0 and win1 are the visibility states for the two windows.
+ * These windows are primarily used for the shadows on held jokers, consumables and cards.
+ */
+void toggle_windows(bool win0, bool win1);
+
#endif //GRAPHIC_UTILS_H
diff --git a/include/splash_screen.h b/include/splash_screen.h
new file mode 100644
index 0000000..3b23d1d
--- /dev/null
+++ b/include/splash_screen.h
@@ -0,0 +1,13 @@
+#ifndef SPLASH_SCREEN_H
+#define SPLASH_SCREEN_H
+
+#include
+
+#define SPLASH_FPS 60
+#define SPLASH_DURATION_SECONDS 10
+#define SPLASH_DURATION_FRAMES (SPLASH_FPS * SPLASH_DURATION_SECONDS)
+
+void splash_screen_init();
+void splash_screen_update(uint timer);
+
+#endif // SPLASH_SCREEN_H
\ No newline at end of file
diff --git a/source/affine_background.c b/source/affine_background.c
index f1b629d..7374a10 100644
--- a/source/affine_background.c
+++ b/source/affine_background.c
@@ -1,20 +1,22 @@
#include "affine_background.h"
#include "affine_background_gfx.h"
+#include "affine_main_menu_background_gfx.h"
#include "graphic_utils.h"
+#define ANIMATION_SPEED_DIVISOR 16
+
BG_AFFINE bgaff_arr[SCREEN_HEIGHT + 1];
+AFF_SRC_EX asx = {0};
+
+enum AffineBackgroundID background = AFFINE_BG_MAIN_MENU;
+
static uint timer = 0;
-#define ANIMATION_SPEED_DIVISOR 16
-
-
void affine_background_init()
-{
- memcpy32_tile8_with_palette_offset((u32*)&tile8_mem[AFFINE_BG_CBB], (const u32*)affine_background_gfxTiles, affine_background_gfxTilesLen/4, AFFINE_BG_PB);
- GRIT_CPY(&se_mem[AFFINE_BG_SBB], affine_background_gfxMap);
- affine_background_load_palette(affine_background_gfxPal);
+{
+ affine_background_update();
REG_BG_AFFINE[AFFINE_BG_IDX] = bg_aff_default;
}
@@ -27,18 +29,17 @@ IWRAM_CODE void affine_background_prep_bgaff_arr()
{
for (u16 vcount = 0; vcount < SCREEN_HEIGHT; vcount++)
{
- AFF_SRC_EX asx = {32<<8, 64<<8, 120, 80, 0x0100, 0x0100, 0};
const s32 timer_s32 = timer << 8;
const s32 vcount_s32 = vcount << 8;
const s16 vcount_s16 = vcount;
const s32 vcount_sine = lu_sin(vcount_s32 + timer_s32 / ANIMATION_SPEED_DIVISOR); // dividing the timer by 16 to make the animation slower
-
+
asx.scr_x = (SCREEN_WIDTH / 2); // 128 on x and y is an offset used to center the rotation
asx.scr_y = vcount_s16 - (SCREEN_HEIGHT / 2); // scr_y must equal vcount otherwise the background will have no vertical difference
- asx.tex_x = vcount_sine;
- asx.tex_y = vcount_sine / 64;
- asx.sx = vcount_sine / 32;
- asx.sy = vcount_sine / 16;
+ asx.tex_x = (1000 * 1000) + (vcount_sine);
+ asx.tex_y = (1000 * 1000);
+ asx.sx = 128;
+ asx.sy = 128;
asx.alpha = vcount_sine + (timer_s32 / ANIMATION_SPEED_DIVISOR);
bg_rotscale_ex(&bgaff_arr[vcount], &asx);
@@ -66,13 +67,30 @@ IWRAM_CODE void affine_background_hblank()
void affine_background_update()
{
- affine_background_prep_bgaff_arr();
+ if (REG_IE & IRQ_HBLANK) // High quality mode with HBLANK interrupt
+ {
+ affine_background_prep_bgaff_arr();
+ }
+ else // Low quality mode without HBLANK interrupt
+ {
+ asx.scr_x = 0;
+ asx.scr_y = 0;
+ asx.tex_x += 5;
+ asx.tex_y += 12;
+ asx.sx = ((lu_sin(timer * 100)) >> 8) + 256; // Scale the sine value to fit in a s16
+ asx.sy = ((lu_sin(timer * 100 + 0x4000)) >> 8) + 256; // Scale the sine value to fit in a s16
+ asx.alpha = 0;
+
+ bg_rotscale_ex(&bgaff_arr[0], &asx);
+ REG_BG_AFFINE[AFFINE_BG_IDX] = bgaff_arr[0];
+ }
+
timer++;
}
void affine_background_set_color(COLOR color)
{
- memcpy16(&pal_bg_mem[AFFINE_BG_PB], affine_background_gfxPal, AFFINE_BG_PAL_LEN);
+ affine_background_change_background(background); // Reload the palette to reset any previous color scaling
for (int i = 0; i < AFFINE_BG_PAL_LEN; i++)
{
clr_rgbscale(&pal_bg_mem[AFFINE_BG_PB] + i, &pal_bg_mem[AFFINE_BG_PB] + i, 1, color);
@@ -82,4 +100,31 @@ void affine_background_set_color(COLOR color)
void affine_background_load_palette(const u16 *src)
{
memcpy16(&pal_bg_mem[AFFINE_BG_PB], src, AFFINE_BG_PAL_LEN);
+}
+
+void affine_background_change_background(enum AffineBackgroundID new_bg)
+{
+ background = new_bg;
+
+ switch (background)
+ {
+ case AFFINE_BG_MAIN_MENU:
+ REG_BG2CNT &= ~BG_AFF_32x32;
+ REG_BG2CNT |= BG_AFF_16x16;
+ REG_IE |= IRQ_HBLANK; // Enable HBLANK
+
+ memcpy32_tile8_with_palette_offset((u32*)&tile8_mem[AFFINE_BG_CBB], (const u32*)affine_main_menu_background_gfxTiles, affine_main_menu_background_gfxTilesLen/4, AFFINE_BG_PB);
+ GRIT_CPY(&se_mem[AFFINE_BG_SBB], affine_main_menu_background_gfxMap);
+ affine_background_load_palette(affine_main_menu_background_gfxPal);
+ break;
+ case AFFINE_BG_GAME:
+ REG_BG2CNT &= ~BG_AFF_16x16;
+ REG_BG2CNT |= BG_AFF_32x32;
+ REG_IE &= ~IRQ_HBLANK; // Disable HBLANK
+
+ memcpy32_tile8_with_palette_offset((u32*)&tile8_mem[AFFINE_BG_CBB], (const u32*)affine_background_gfxTiles, affine_background_gfxTilesLen/4, AFFINE_BG_PB);
+ GRIT_CPY(&se_mem[AFFINE_BG_SBB], affine_background_gfxMap);
+ affine_background_load_palette(affine_background_gfxPal);
+ break;
+ }
}
\ No newline at end of file
diff --git a/source/game.c b/source/game.c
index ce165ca..62f6bd5 100644
--- a/source/game.c
+++ b/source/game.c
@@ -16,31 +16,34 @@
#include "tonc_video.h"
#include "audio_utils.h"
#include "selection_grid.h"
+#include "splash_screen.h"
#include "background_gfx.h"
#include "background_shop_gfx.h"
#include "background_blind_select_gfx.h"
#include "affine_background_gfx.h"
+#include "background_main_menu_gfx.h"
+#include "affine_main_menu_background_gfx.h"
#include "soundbank.h"
#include "list.h"
-// This seed system is temporary.
-static uint rng_seed = 2048;
-static bool seeded = false;
+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;
static int background = 0;
-static enum GameState game_state = GAME_BLIND_SELECT; // The current game state, this is used to determine what the game is doing at any given time
+static enum GameState game_state = GAME_SPLASH_SCREEN; // The current game state, this is used to determine what the game is doing at any given time
static enum HandState hand_state = HAND_DRAW;
static enum PlayState play_state = PLAY_PLAYING;
static int state = 0; // General state variable, used for switch statements in each game state related function
static enum HandType hand_type = NONE;
+static CardObject *main_menu_ace = NULL;
+
static Sprite *playing_blind_token = NULL; // The sprite that displays the blind when in "GAME_PLAYING/GAME_ROUND_END" state
static Sprite *round_end_blind_token = NULL; // The sprite that displays the blind when in "GAME_ROUND_END" state
@@ -249,6 +252,11 @@ static const BG_POINT JOKER_DISCARD_TARGET = {240, 30};
#define ITEM_SHOP_Y 71 // TODO: Needs to be a rect?
+#define MAIN_MENU_BUTTONS 2
+#define MAIN_MENU_IMPLEMENTED_BUTTONS 1 // Remove this once all buttons are implemented
+#define MAIN_MENU_PLAY_BUTTON_MAIN_COLOR_PID 5
+#define MAIN_MENU_PLAY_BUTTON_OUTLINE_COLOR_PID 2
+
//TODO: Properly define and use
#define MENU_POP_OUT_ANIM_FRAMES 20
#define GAME_OVER_ANIM_FRAMES 15
@@ -265,12 +273,8 @@ static const BG_POINT JOKER_DISCARD_TARGET = {240, 30};
// General functions
void set_seed(int seed)
{
- if (!seeded)
- {
- seeded = true;
- rng_seed = seed;
- srand(rng_seed);
- }
+ rng_seed = seed;
+ srand(rng_seed);
}
void sort_hand_by_suit()
@@ -437,11 +441,12 @@ void change_background(int id)
}
else
{
- REG_DISPCNT |= DCNT_WIN0; // Enable window 0 to make hand background transparent
+ toggle_windows(true, true); // Enable window 0 for the hand shadow
+
// Load the tiles and palette
// Background
- memcpy(pal_bg_mem, background_gfxPal, 64); // This '64" isn't a specific number, I'm just using it to prevent the text colors from being overridden
- GRIT_CPY(&tile8_mem[MAIN_BG_CBB], background_gfxTiles); // Deadass i have no clue how any of these memory things work but I just messed with them until stuff worked
+ GRIT_CPY(pal_bg_mem, background_gfxPal);
+ GRIT_CPY(&tile8_mem[MAIN_BG_CBB], background_gfxTiles);
GRIT_CPY(&se_mem[MAIN_BG_SBB], background_gfxMap);
if (current_blind == BIG_BLIND) // Change text and palette depending on blind type
@@ -476,6 +481,7 @@ void change_background(int id)
}
REG_WIN0V = (REG_WIN0V << 8) | 0xA0; // Set window 0 bottom to 160
+ toggle_windows(true, true);
for (int i = 0; i <= 2; i++)
{
@@ -492,16 +498,16 @@ void change_background(int id)
background = BG_ID_ROUND_END;
}
- REG_DISPCNT &= ~DCNT_WIN0; // Disable window 0 so it doesn't make the cashout menu transparent
+ toggle_windows(false, true); // Disable window 0 so it doesn't make the cashout menu transparent
main_bg_se_clear_rect(ROUND_END_MENU_RECT);
tte_erase_rect_wrapper(HAND_SIZE_RECT);
}
else if (id == BG_ID_SHOP)
{
- REG_DISPCNT &= ~DCNT_WIN0;
+ toggle_windows(false, true);
- memcpy(pal_bg_mem, background_shop_gfxPal, 64);
+ GRIT_CPY(pal_bg_mem, background_shop_gfxPal);
GRIT_CPY(&tile_mem[MAIN_BG_CBB], background_shop_gfxTiles);
GRIT_CPY(&se_mem[MAIN_BG_SBB], background_shop_gfxMap);
@@ -528,9 +534,9 @@ void change_background(int id)
sprite_position(blind_select_tokens[BIG_BLIND], 120, default_y);
sprite_position(blind_select_tokens[BOSS_BLIND], 160, default_y);
- REG_DISPCNT &= ~DCNT_WIN0;
+ toggle_windows(false, true);
- memcpy16(pal_bg_mem, background_blind_select_gfxPal, 64);
+ GRIT_CPY(pal_bg_mem, background_blind_select_gfxPal);
GRIT_CPY(&tile_mem[MAIN_BG_CBB], background_blind_select_gfxTiles);
GRIT_CPY(&se_mem[MAIN_BG_SBB], background_blind_select_gfxMap);
@@ -542,7 +548,7 @@ void change_background(int id)
// Select button PID is 15 and the outline is 18
memcpy16(&pal_bg_mem[18], &pal_bg_mem[15], 1);
// Skip button PID is 10 and the outline is 5
- memcpy16(&pal_bg_mem[10 ], &pal_bg_mem[5], 1);
+ memcpy16(&pal_bg_mem[10], &pal_bg_mem[5], 1);
for (int i = 0; i < MAX_BLINDS; i++)
{
@@ -627,6 +633,18 @@ void change_background(int id)
}
}
}
+ else if (id == BG_ID_MAIN_MENU)
+ {
+ toggle_windows(false, false);
+
+ tte_erase_screen();
+ GRIT_CPY(pal_bg_mem, background_main_menu_gfxPal);
+ GRIT_CPY(&tile_mem[MAIN_BG_CBB], background_main_menu_gfxTiles);
+ GRIT_CPY(&se_mem[MAIN_BG_SBB], background_main_menu_gfxMap);
+
+ // Disable the button highlight colors
+ memcpy16(&pal_bg_mem[MAIN_MENU_PLAY_BUTTON_OUTLINE_COLOR_PID], &pal_bg_mem[MAIN_MENU_PLAY_BUTTON_MAIN_COLOR_PID], 1);
+ }
else
{
return; // Invalid background ID
@@ -931,9 +949,6 @@ void increment_blind(enum BlindState increment_reason)
static void game_round_init()
{
- // once we have a main menu, move the seed set there. this is only here now because there isn't a way to set the seed to a psudorandom value unless there is something to wait on
- set_seed(rng_seed);
-
hand_state = HAND_DRAW;
cards_drawn = 0;
hand_selections = 0;
@@ -981,6 +996,20 @@ static void game_round_init()
deck_shuffle(); // Shuffle the deck at the start of the round
}
+static void game_main_menu_init()
+{
+ affine_background_change_background(AFFINE_BG_MAIN_MENU);
+ change_background(BG_ID_MAIN_MENU);
+ main_menu_ace = card_object_new(card_new(SPADES, ACE));
+ card_object_set_sprite(main_menu_ace, 0); // Set the sprite for the ace of spades
+ main_menu_ace->sprite_object->sprite->obj->attr0 |= ATTR0_AFF_DBL; // Make the sprite double sized
+ main_menu_ace->sprite_object->tx = int2fx(88);
+ main_menu_ace->sprite_object->x = main_menu_ace->sprite_object->tx;
+ main_menu_ace->sprite_object->ty = int2fx(26);
+ main_menu_ace->sprite_object->y = main_menu_ace->sprite_object->ty;
+ main_menu_ace->sprite_object->tscale = float2fx(0.8f);
+}
+
static void game_over_init()
{
// Clears the round end menu
@@ -1008,6 +1037,12 @@ static void init_game_state(enum GameState game_state_to_init)
// Switch written out, add init for states as needed
switch (game_state_to_init)
{
+ case GAME_SPLASH_SCREEN:
+ splash_screen_init();
+ break;
+ case GAME_MAIN_MENU:
+ game_main_menu_init();
+ break;
case GAME_PLAYING:
game_round_init();
break;
@@ -1029,7 +1064,7 @@ static void init_game_state(enum GameState game_state_to_init)
}
// Game functions
-static void game_set_state(enum GameState new_game_state)
+void game_set_state(enum GameState new_game_state)
{
timer = 0; // Reset the timer
init_game_state(new_game_state);
@@ -1069,6 +1104,22 @@ void game_init()
obj_hide(blind_select_tokens[BIG_BLIND]->obj);
obj_hide(blind_select_tokens[BOSS_BLIND]->obj);
+ game_set_state(game_state);
+}
+
+void game_start()
+{
+ set_seed(rng_seed);
+
+ affine_background_change_background(AFFINE_BG_GAME);
+
+ // Normally I would just cache these and hide/unhide but I didn't feel like dealing with defining a layer for it
+ card_destroy(&main_menu_ace->card);
+ card_object_destroy(&main_menu_ace);
+
+ hands = max_hands;
+ discards = max_discards;
+
// Fill the deck with all the cards. Later on this can be replaced with a more dynamic system that allows for different decks and card types.
for (int suit = 0; suit < NUM_SUITS; suit++)
{
@@ -1095,6 +1146,8 @@ void game_init()
display_money(money); // Set the money display
tte_printf("#{P:%d,%d; cx:0x%X000}%d#{cx:0x%X000}/%d", ANTE_TEXT_RECT.left, ANTE_TEXT_RECT.top, TTE_YELLOW_PB, ante, TTE_WHITE_PB, MAX_ANTE); // Ante
+
+ game_set_state(GAME_BLIND_SELECT);
}
static void game_playing_process_hand_select_input()
@@ -2613,7 +2666,6 @@ void game_shop()
}
}
}
-
if (timer % 20 == 0)
{
@@ -2824,6 +2876,54 @@ void game_blind_select()
}
}
+void game_main_menu()
+{
+ change_background(BG_ID_MAIN_MENU);
+
+ card_object_update(main_menu_ace);
+ main_menu_ace->sprite_object->trotation = lu_sin((timer << 8) / 2) / 3;
+ main_menu_ace->sprite_object->rotation = main_menu_ace->sprite_object->trotation;
+
+ // Seed randomization
+ rng_seed++;
+ if (key_curr_state() != key_prev_state()) // If the keys have changed, make it more pseudo-random
+ {
+ rng_seed *= 2;
+ }
+
+ if (key_hit(KEY_LEFT))
+ {
+ if (selection_x > 0)
+ {
+ selection_x--;
+ }
+ }
+ else if (key_hit(KEY_RIGHT))
+ {
+ if (selection_x < MAIN_MENU_IMPLEMENTED_BUTTONS - 1)
+ {
+ selection_x++;
+ }
+ }
+
+ if (selection_x == 0) // Play button
+ {
+ // Select button PID is 5 and the outline is 3
+ memset16(&pal_bg_mem[MAIN_MENU_PLAY_BUTTON_OUTLINE_COLOR_PID], HIGHLIGHT_COLOR, 1);
+
+ if (key_hit(KEY_A))
+ {
+ // Start the game
+ game_start();
+ }
+ }
+ else
+ {
+ // Select button PID is 5 and the outline is 3
+ memcpy16(&pal_bg_mem[MAIN_MENU_PLAY_BUTTON_OUTLINE_COLOR_PID], &pal_bg_mem[MAIN_MENU_PLAY_BUTTON_MAIN_COLOR_PID], 1);
+ }
+}
+
static void discarded_jokers_update_loop()
{
if (discarded_jokers == NULL)
@@ -2841,7 +2941,6 @@ static void discarded_jokers_update_loop()
joker_object_destroy(&joker_object);
}
}
-
}
static void held_jokers_update_loop()
@@ -2906,19 +3005,16 @@ void game_update()
{
timer++;
- if (seeded == false)
- {
- rng_seed++;
- if (key_curr_state() != key_prev_state()) // If the keys have changed, make it more pseudo-random
- {
- rng_seed *= 2;
- }
- }
-
jokers_update_loop();
switch (game_state)
{
+ case GAME_SPLASH_SCREEN:
+ splash_screen_update(timer);
+ break;
+ case GAME_MAIN_MENU:
+ game_main_menu();
+ break;
case GAME_PLAYING:
game_playing();
break;
diff --git a/source/graphic_utils.c b/source/graphic_utils.c
index 2e76aae..71207ab 100644
--- a/source/graphic_utils.c
+++ b/source/graphic_utils.c
@@ -269,4 +269,34 @@ void memcpy32_tile8_with_palette_offset(u32* dst, const u32* src, uint wcount, u
// Copying u8 data 4 times across u32 data
dst[i] = src[i] + offset;
}
+}
+
+void toggle_windows(bool win0, bool win1)
+{
+ if (win0)
+ {
+ REG_DISPCNT |= DCNT_WIN0;
+ }
+ else
+ {
+ REG_DISPCNT &= ~DCNT_WIN0;
+ }
+
+ if (win1)
+ {
+ REG_DISPCNT |= DCNT_WIN1;
+ }
+ else
+ {
+ REG_DISPCNT &= ~DCNT_WIN1;
+ }
+
+ if (win0 || win1)
+ {
+ REG_BLDCNT = BLD_BUILD(BLD_BG1, BLD_BG2, 1);
+ }
+ else
+ {
+ REG_BLDCNT = 0;
+ }
}
\ No newline at end of file
diff --git a/source/main.c b/source/main.c
index 0d9a596..a9ca8af 100644
--- a/source/main.c
+++ b/source/main.c
@@ -24,10 +24,6 @@ void init()
irq_add(II_VBLANK, mmVBlank);
irq_add(II_HBLANK, affine_background_hblank);
- // Initialize maxmod
- mmInitDefault((mm_addr)soundbank_bin, 12);
- mmStart(MOD_MAIN_THEME, MM_PLAY_LOOP);
-
// Initialize text engine
tte_init_se(0, BG_CBB(TTE_CBB) | BG_SBB(TTE_SBB), 0, CLR_WHITE, TTE_BIT_UNPACK_OFFSET, NULL, NULL);
tte_erase_screen();
@@ -45,7 +41,7 @@ void init()
// BG1 is the main background layer
REG_BG1CNT = BG_PRIO(1) | BG_CBB(MAIN_BG_CBB) | BG_SBB(MAIN_BG_SBB) | BG_8BPP;
// BG2 is the affine background layer
- REG_BG2CNT = BG_PRIO(2) | BG_CBB(AFFINE_BG_CBB) | BG_SBB(AFFINE_BG_SBB) | BG_8BPP | BG_WRAP | BG_AFF_32x32;
+ REG_BG2CNT = BG_PRIO(2) | BG_CBB(AFFINE_BG_CBB) | BG_SBB(AFFINE_BG_SBB) | BG_8BPP | BG_WRAP;
int win1_left = 72;
int win1_top = 44;
@@ -73,6 +69,8 @@ void init()
REG_DISPCNT = DCNT_MODE1 | DCNT_OBJ_1D | DCNT_BG0 | DCNT_BG1 | DCNT_BG2 | DCNT_OBJ | DCNT_WIN0 | DCNT_WIN1;
// Initialize subsystems
+ mmInitDefault((mm_addr)soundbank_bin, 12);
+ mmStart(MOD_MAIN_THEME, MM_PLAY_LOOP);
affine_background_init();
sprite_init();
card_init();
diff --git a/source/splash_screen.c b/source/splash_screen.c
new file mode 100644
index 0000000..0c3c535
--- /dev/null
+++ b/source/splash_screen.c
@@ -0,0 +1,32 @@
+#include "splash_screen.h"
+
+#include
+
+#include "graphic_utils.h"
+#include "game.h"
+
+static const Rect COUNTDOWN_TIMER_RECT = {208, 144, 240, 152};
+
+void splash_screen_init()
+{
+ tte_printf("#{P:72,8; cx:0xF000}DISCLAIMER");
+ tte_printf("#{P:8,24; cx:0xF000}This project is NOT endorsed \n by or affiliated with \n Playstack or LocalThunk.\n\n If you have paid for this, \n you have been scammed \n and should request a refund \n IMMEDIATELY. \n\n The only official place \n to obtain this is from: \n\n 'github.com/\n cellos51/balatro-gba'");
+ tte_printf("#{P:8,144; cx:0xF000}(Press any key to skip)");
+}
+
+void splash_screen_update(uint timer)
+{
+ if (timer < SPLASH_DURATION_FRAMES)
+ {
+ tte_erase_rect_wrapper(COUNTDOWN_TIMER_RECT);
+ tte_printf("#{P:%d,%d; cx:0xF000}%d", COUNTDOWN_TIMER_RECT.left, COUNTDOWN_TIMER_RECT.top, 1 + (SPLASH_DURATION_FRAMES - timer) / SPLASH_FPS);
+
+ if (!key_hit(KEY_ANY))
+ {
+ return;
+ }
+ }
+
+ game_set_state(GAME_MAIN_MENU);
+ tte_erase_screen();
+}
\ No newline at end of file