Merge branch 'main' into joker_pooling
@@ -1,9 +1,26 @@
|
|||||||
# Balatro GBA
|
# 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 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 January 2024 Balatro Steam demo.
|
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 or supported by Playstack or LocalThunk**
|
### **Disclaimer: This project is NOT endorsed by or affiliated with 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.
|
#### 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.
|
||||||
|
#### 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
|
||||||
|
|
||||||
|
[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)
|
||||||
|
|
||||||
|
[Balatro on Switch](https://www.nintendo.com/us/store/products/balatro-switch/)
|
||||||
|
|
||||||
|
[Balatro on PlayStation Store](https://store.playstation.com/en-us/concept/10010334)
|
||||||
|
|
||||||
|
[Balatro on Xbox](https://www.xbox.com/games/store/balatro/9PK087LNGJC5)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
# **Build Instructions:**
|
# **Build Instructions:**
|
||||||
@@ -22,7 +39,7 @@ Example: `cd '/home/user/OneDrive/Documents/balatro-gba` or something like this.
|
|||||||
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.
|
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-**
|
## **-Linux-**
|
||||||
[Not Available]
|
[No Instructions Available]
|
||||||
|
|
||||||
### **Common Issues:**
|
### **Common Issues:**
|
||||||
1. **When I drag the folder into the window and press enter, it doesn't work!**
|
1. **When I drag the folder into the window and press enter, it doesn't work!**
|
||||||
@@ -36,5 +53,3 @@ Example: `cd '/home/user/OneDrive/Documents/balatro-gba` or something like this.
|
|||||||
5. **It says I don't have `Git` or `Make` installed!**
|
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.
|
-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/**
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
@@ -0,0 +1 @@
|
|||||||
|
-gB4 -Mw4 -Mh4 -m! -pn 16
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1 @@
|
|||||||
|
-gB4 -Mw4 -Mh4 -m! -pn 16
|
||||||
|
After Width: | Height: | Size: 855 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 658 B |
|
Before Width: | Height: | Size: 958 B After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 620 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 927 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1 @@
|
|||||||
|
-gB4 -Mw4 -Mh4 -m! -pn 16
|
||||||
|
After Width: | Height: | Size: 961 B |
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef AFFINE_BACKGROUND_H
|
||||||
|
#define AFFINE_BACKGROUND_H
|
||||||
|
|
||||||
|
#include <tonc.h>
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
void affine_background_init();
|
||||||
|
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);
|
||||||
|
|
||||||
|
#endif // AFFINE_BACKGROUND_H
|
||||||
@@ -6,6 +6,8 @@
|
|||||||
#define MAX_JOKERS_HELD_SIZE 5 // This doesn't account for negatives right now.
|
#define MAX_JOKERS_HELD_SIZE 5 // This doesn't account for negatives right now.
|
||||||
#define MAX_SHOP_JOKERS 2 // TODO: Make this dynamic and allow for other items besides jokers
|
#define MAX_SHOP_JOKERS 2 // TODO: Make this dynamic and allow for other items besides jokers
|
||||||
#define MAX_SELECTION_SIZE 5
|
#define MAX_SELECTION_SIZE 5
|
||||||
|
#define MAX_CARD_SCORE_DIGITS 2 // Current digit limit for score received from cards including mult etc. from jokers
|
||||||
|
#define MAX_CARD_SCORE_STR_LEN (MAX_CARD_SCORE_DIGITS + 1) // For the '+' or 'X'
|
||||||
#define FRAMES(x) (((x) + game_speed - 1) / game_speed)
|
#define FRAMES(x) (((x) + game_speed - 1) / game_speed)
|
||||||
|
|
||||||
// TODO: Turn into enum?
|
// TODO: Turn into enum?
|
||||||
@@ -77,9 +79,12 @@ typedef struct CardObject CardObject; // forward declaration, actually declared
|
|||||||
typedef struct JokerObject JokerObject;
|
typedef struct JokerObject JokerObject;
|
||||||
CardObject** get_hand_array(void);
|
CardObject** get_hand_array(void);
|
||||||
int get_hand_top(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);
|
int get_played_top(void);
|
||||||
JokerObject** get_jokers(void);
|
JokerObject** get_jokers(void);
|
||||||
int get_jokers_top(void);
|
int get_jokers_top(void);
|
||||||
|
|
||||||
|
int get_num_discards_remaining(void);
|
||||||
|
|
||||||
#endif // GAME_H
|
#endif // GAME_H
|
||||||
@@ -43,6 +43,9 @@
|
|||||||
// Tile size in pixels, both height and width as tiles are square
|
// Tile size in pixels, both height and width as tiles are square
|
||||||
#define TILE_SIZE 8
|
#define TILE_SIZE 8
|
||||||
|
|
||||||
|
// By default TTE characters occupy a single tile
|
||||||
|
#define TTE_CHAR_SIZE TILE_SIZE
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
int left;
|
int left;
|
||||||
@@ -117,4 +120,22 @@ void tte_erase_rect_wrapper(Rect rect);
|
|||||||
*/
|
*/
|
||||||
void update_text_rect_to_right_align_num(Rect* rect, int num, int overflow_direction);
|
void update_text_rect_to_right_align_num(Rect* rect, int num, int overflow_direction);
|
||||||
|
|
||||||
|
/*Copies 16 bit data from src to dst, applying a palette offset to the data.
|
||||||
|
* This is intended solely for use with tile8/8bpp data for dst and src.
|
||||||
|
* The palette offset allows the tiles to use a different location in the palette
|
||||||
|
* memory
|
||||||
|
* This is useful because grit always loads the palette to the beginning of
|
||||||
|
* pal_bg_mem[]
|
||||||
|
*/
|
||||||
|
void memcpy16_tile8_with_palette_offset(u16* dst, const u16* src, uint hwcount, u8 palette_offset);
|
||||||
|
|
||||||
|
/*Copies 32 bit data from src to dst, applying a palette offset to the data.
|
||||||
|
* This is intended solely for use with tile8/8bpp data for dst and src.
|
||||||
|
* The palette offset allows the tiles to use a different location in the palette
|
||||||
|
* memory
|
||||||
|
* This is useful because grit always loads the palette to the beginning of
|
||||||
|
* pal_bg_mem[]
|
||||||
|
*/
|
||||||
|
void memcpy32_tile8_with_palette_offset(u32* dst, const u32* src, uint wcount, u8 palette_offset);
|
||||||
|
|
||||||
#endif //GRAPHIC_UTILS_H
|
#endif //GRAPHIC_UTILS_H
|
||||||
@@ -10,5 +10,8 @@
|
|||||||
#include "joker_gfx6.h"
|
#include "joker_gfx6.h"
|
||||||
#include "joker_gfx7.h"
|
#include "joker_gfx7.h"
|
||||||
#include "joker_gfx8.h"
|
#include "joker_gfx8.h"
|
||||||
|
#include "joker_gfx9.h"
|
||||||
|
#include "joker_gfx10.h"
|
||||||
|
#include "joker_gfx11.h"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -37,6 +37,8 @@ static inline int get_digits_even(int n)
|
|||||||
|
|
||||||
#define NUM_ELEM_IN_ARR(arr) (sizeof(arr) / sizeof((arr)[0]))
|
#define NUM_ELEM_IN_ARR(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||||
|
|
||||||
|
#define INT_MAX_DIGITS 10 // strlen(str(INT_MAX)) = strlen("2147483647")
|
||||||
|
|
||||||
int int_arr_max(int int_arr[], int size);
|
int int_arr_max(int int_arr[], int size);
|
||||||
|
|
||||||
#endif // UTIL_H
|
#endif // UTIL_H
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
#include "affine_background.h"
|
||||||
|
#include "affine_background_gfx.h"
|
||||||
|
|
||||||
|
#include "graphic_utils.h"
|
||||||
|
|
||||||
|
BG_AFFINE bgaff;
|
||||||
|
AFF_SRC_EX asx = {32<<8, 64<<8, 120, 80, 0x0100, 0x0100, 0};
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
bgaff = bg_aff_default;
|
||||||
|
}
|
||||||
|
|
||||||
|
void affine_background_update()
|
||||||
|
{
|
||||||
|
static uint timer = 0;
|
||||||
|
timer++;
|
||||||
|
|
||||||
|
// These values are not permament, just the current configuration for the affine background
|
||||||
|
asx.tex_x += 5;
|
||||||
|
asx.tex_y += 12;
|
||||||
|
asx.sx = (lu_sin(timer * 100)) >> 8; // Scale the sine value to fit in a s16
|
||||||
|
asx.sx += 256; // Add 256 to the sine value to make it positive
|
||||||
|
asx.sy = (lu_sin(timer * 100 + 0x4000)) >> 8; // Scale the sine value to fit in a s16
|
||||||
|
asx.sy += 256; // Add 256 to the sine value to make it positive
|
||||||
|
bg_rotscale_ex(&bgaff, &asx);
|
||||||
|
REG_BG_AFFINE[2] = bgaff;
|
||||||
|
}
|
||||||
|
|
||||||
|
void affine_background_set_color(COLOR color)
|
||||||
|
{
|
||||||
|
memcpy16(&pal_bg_mem[AFFINE_BG_PB], affine_background_gfxPal, AFFINE_BG_PAL_LEN);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void affine_background_load_palette(const u16 *src)
|
||||||
|
{
|
||||||
|
memcpy16(&pal_bg_mem[AFFINE_BG_PB], src, AFFINE_BG_PAL_LEN);
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "hand_analysis.h"
|
#include "hand_analysis.h"
|
||||||
#include "blind.h"
|
#include "blind.h"
|
||||||
#include "joker.h"
|
#include "joker.h"
|
||||||
|
#include "affine_background.h"
|
||||||
#include "graphic_utils.h"
|
#include "graphic_utils.h"
|
||||||
#include "tonc_video.h"
|
#include "tonc_video.h"
|
||||||
#include "audio_utils.h"
|
#include "audio_utils.h"
|
||||||
@@ -18,6 +19,7 @@
|
|||||||
#include "background_gfx.h"
|
#include "background_gfx.h"
|
||||||
#include "background_shop_gfx.h"
|
#include "background_shop_gfx.h"
|
||||||
#include "background_blind_select_gfx.h"
|
#include "background_blind_select_gfx.h"
|
||||||
|
#include "affine_background_gfx.h"
|
||||||
|
|
||||||
#include "soundbank.h"
|
#include "soundbank.h"
|
||||||
|
|
||||||
@@ -148,6 +150,11 @@ int get_hand_top(void) {
|
|||||||
return hand_top;
|
return hand_top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int hand_get_size(void)
|
||||||
|
{
|
||||||
|
return hand_top + 1;
|
||||||
|
}
|
||||||
|
|
||||||
CardObject **get_played_array(void) {
|
CardObject **get_played_array(void) {
|
||||||
return played;
|
return played;
|
||||||
}
|
}
|
||||||
@@ -239,6 +246,10 @@ static const Rect SHOP_REROLL_RECT = {88, 96, UNDEFINED, UNDEFI
|
|||||||
#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"
|
// Naming the stage where cards return from the discard pile to the deck "undiscard"
|
||||||
|
|
||||||
|
int get_num_discards_remaining(void) {
|
||||||
|
return discards;
|
||||||
|
}
|
||||||
|
|
||||||
// General functions
|
// General functions
|
||||||
void set_seed(int seed)
|
void set_seed(int seed)
|
||||||
{
|
{
|
||||||
@@ -428,6 +439,8 @@ void change_background(int id)
|
|||||||
else if (current_blind == BOSS_BLIND)
|
else if (current_blind == BOSS_BLIND)
|
||||||
{
|
{
|
||||||
main_bg_se_copy_rect(BOSS_BLIND_TITLE_SRC_RECT, TOP_LEFT_BLIND_TITLE_POINT);
|
main_bg_se_copy_rect(BOSS_BLIND_TITLE_SRC_RECT, TOP_LEFT_BLIND_TITLE_POINT);
|
||||||
|
|
||||||
|
affine_background_set_color(blind_get_color(BOSS_BLIND, BLIND_SHADOW_COLOR_INDEX));
|
||||||
}
|
}
|
||||||
|
|
||||||
bg_copy_current_item_to_top_left_panel();
|
bg_copy_current_item_to_top_left_panel();
|
||||||
@@ -849,11 +862,6 @@ void hand_change_sort()
|
|||||||
sort_cards();
|
sort_cards();
|
||||||
}
|
}
|
||||||
|
|
||||||
int hand_get_size()
|
|
||||||
{
|
|
||||||
return hand_top + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int hand_get_max_size()
|
int hand_get_max_size()
|
||||||
{
|
{
|
||||||
return hand_size;
|
return hand_size;
|
||||||
@@ -1615,7 +1623,7 @@ static void played_cards_update_loop(bool* discarded_card, int* played_selection
|
|||||||
tte_set_special(0xD000); // Set text color to blue from background memory
|
tte_set_special(0xD000); // Set text color to blue from background memory
|
||||||
|
|
||||||
// Write the score to a character buffer variable
|
// Write the score to a character buffer variable
|
||||||
char score_buffer[5]; // Assuming the maximum score is 99, we need 4 characters (2 digits + null terminator)
|
char score_buffer[INT_MAX_DIGITS + 2]; // for '+' and null terminator
|
||||||
snprintf(score_buffer, sizeof(score_buffer), "+%d", card_get_value(played[j]->card));
|
snprintf(score_buffer, sizeof(score_buffer), "+%d", card_get_value(played[j]->card));
|
||||||
tte_write(score_buffer);
|
tte_write(score_buffer);
|
||||||
|
|
||||||
@@ -1986,6 +1994,7 @@ void game_round_end()
|
|||||||
tte_erase_rect_wrapper(BLIND_REWARD_RECT);
|
tte_erase_rect_wrapper(BLIND_REWARD_RECT);
|
||||||
tte_erase_rect_wrapper(BLIND_REQ_TEXT_RECT);
|
tte_erase_rect_wrapper(BLIND_REQ_TEXT_RECT);
|
||||||
obj_hide(playing_blind_token->obj);
|
obj_hide(playing_blind_token->obj);
|
||||||
|
affine_background_load_palette(affine_background_gfxPal);
|
||||||
state = 5;
|
state = 5;
|
||||||
timer = 0;
|
timer = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,3 +168,22 @@ void update_text_rect_to_right_align_num(Rect* rect, int num, int overflow_direc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void memcpy16_tile8_with_palette_offset(u16* dst, const u16* src, uint hwcount, u8 palette_offset)
|
||||||
|
{
|
||||||
|
const u16 offset = (((palette_offset) << 8) | (palette_offset));
|
||||||
|
for (int i = 0; i < hwcount; i++)
|
||||||
|
{
|
||||||
|
// Copying u8 data twice across u16 data
|
||||||
|
dst[i] = src[i] + offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void memcpy32_tile8_with_palette_offset(u32* dst, const u32* src, uint wcount, u8 palette_offset)
|
||||||
|
{
|
||||||
|
const u32 offset = (palette_offset << 24) | (palette_offset << 16) | (palette_offset << 8) | palette_offset;
|
||||||
|
for (int i = 0; i < wcount; i++)
|
||||||
|
{
|
||||||
|
// Copying u8 data 4 times across u32 data
|
||||||
|
dst[i] = src[i] + offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -127,6 +127,9 @@ void joker_init()
|
|||||||
joker_gfxTiles[6] = joker_gfx6Tiles;
|
joker_gfxTiles[6] = joker_gfx6Tiles;
|
||||||
joker_gfxTiles[7] = joker_gfx7Tiles;
|
joker_gfxTiles[7] = joker_gfx7Tiles;
|
||||||
joker_gfxTiles[8] = joker_gfx8Tiles;
|
joker_gfxTiles[8] = joker_gfx8Tiles;
|
||||||
|
joker_gfxTiles[9] = joker_gfx9Tiles;
|
||||||
|
joker_gfxTiles[10] = joker_gfx10Tiles;
|
||||||
|
joker_gfxTiles[11] = joker_gfx11Tiles;
|
||||||
|
|
||||||
joker_gfxPal[0] = joker_gfx0Pal;
|
joker_gfxPal[0] = joker_gfx0Pal;
|
||||||
joker_gfxPal[1] = joker_gfx1Pal;
|
joker_gfxPal[1] = joker_gfx1Pal;
|
||||||
@@ -137,6 +140,9 @@ void joker_init()
|
|||||||
joker_gfxPal[6] = joker_gfx6Pal;
|
joker_gfxPal[6] = joker_gfx6Pal;
|
||||||
joker_gfxPal[7] = joker_gfx7Pal;
|
joker_gfxPal[7] = joker_gfx7Pal;
|
||||||
joker_gfxPal[8] = joker_gfx8Pal;
|
joker_gfxPal[8] = joker_gfx8Pal;
|
||||||
|
joker_gfxPal[9] = joker_gfx9Pal;
|
||||||
|
joker_gfxPal[10] = joker_gfx10Pal;
|
||||||
|
joker_gfxPal[11] = joker_gfx11Pal;
|
||||||
|
|
||||||
for (int i = 0; i < num_spritesheets; i++)
|
for (int i = 0; i < num_spritesheets; i++)
|
||||||
{
|
{
|
||||||
@@ -264,32 +270,46 @@ bool joker_object_score(JokerObject *joker_object, Card* scored_card, int *chips
|
|||||||
*money += joker_effect.money;
|
*money += joker_effect.money;
|
||||||
// TODO: Retrigger
|
// TODO: Retrigger
|
||||||
|
|
||||||
tte_set_pos(fx2int(joker_object->sprite_object->x) + 8, JOKER_SCORE_TEXT_Y); // Offset of 16 pixels to center the text on the card
|
const int joker_score_display_offset_px = (MAX_CARD_SCORE_STR_LEN + 1)*TTE_CHAR_SIZE;
|
||||||
|
// + 1 For space
|
||||||
char score_buffer[12];
|
|
||||||
|
|
||||||
|
int cursorPosX = fx2int(joker_object->sprite_object->x) + 8; // Offset of 16 pixels to center the text on the card
|
||||||
if (joker_effect.chips > 0)
|
if (joker_effect.chips > 0)
|
||||||
{
|
{
|
||||||
|
char score_buffer[INT_MAX_DIGITS + 2]; // For '+' and null terminator
|
||||||
|
tte_set_pos(cursorPosX, JOKER_SCORE_TEXT_Y);
|
||||||
tte_set_special(0xD000); // Blue
|
tte_set_special(0xD000); // Blue
|
||||||
snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.chips);
|
snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.chips);
|
||||||
|
tte_write(score_buffer);
|
||||||
|
cursorPosX += joker_score_display_offset_px;
|
||||||
}
|
}
|
||||||
else if (joker_effect.mult > 0)
|
if (joker_effect.mult > 0)
|
||||||
{
|
{
|
||||||
|
char score_buffer[INT_MAX_DIGITS + 2];
|
||||||
|
tte_set_pos(cursorPosX, JOKER_SCORE_TEXT_Y);
|
||||||
tte_set_special(0xE000); // Red
|
tte_set_special(0xE000); // Red
|
||||||
snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.mult);
|
snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.mult);
|
||||||
|
tte_write(score_buffer);
|
||||||
|
cursorPosX += joker_score_display_offset_px;
|
||||||
}
|
}
|
||||||
else if (joker_effect.xmult > 0)
|
if (joker_effect.xmult > 0)
|
||||||
{
|
{
|
||||||
|
char score_buffer[INT_MAX_DIGITS + 2];
|
||||||
|
tte_set_pos(cursorPosX, JOKER_SCORE_TEXT_Y);
|
||||||
tte_set_special(0xE000); // Red
|
tte_set_special(0xE000); // Red
|
||||||
snprintf(score_buffer, sizeof(score_buffer), "X%d", joker_effect.xmult);
|
snprintf(score_buffer, sizeof(score_buffer), "X%d", joker_effect.xmult);
|
||||||
|
tte_write(score_buffer);
|
||||||
|
cursorPosX += joker_score_display_offset_px;
|
||||||
}
|
}
|
||||||
else if (joker_effect.money > 0)
|
if (joker_effect.money > 0)
|
||||||
{
|
{
|
||||||
|
char score_buffer[INT_MAX_DIGITS + 2];
|
||||||
|
tte_set_pos(cursorPosX, JOKER_SCORE_TEXT_Y);
|
||||||
tte_set_special(0xC000); // Yellow
|
tte_set_special(0xC000); // Yellow
|
||||||
snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.money);
|
snprintf(score_buffer, sizeof(score_buffer), "+%d", joker_effect.money);
|
||||||
}
|
|
||||||
|
|
||||||
tte_write(score_buffer);
|
tte_write(score_buffer);
|
||||||
|
cursorPosX += joker_score_display_offset_px;
|
||||||
|
}
|
||||||
|
|
||||||
joker_object->joker->processed = true; // Mark the joker as processed
|
joker_object->joker->processed = true; // Mark the joker as processed
|
||||||
joker_object_shake(joker_object, SFX_CARD_SELECT); // TODO: Add a sound effect for scoring the joker
|
joker_object_shake(joker_object, SFX_CARD_SELECT); // TODO: Add a sound effect for scoring the joker
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#include "game.h"
|
||||||
#include "joker.h"
|
#include "joker.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "hand_analysis.h"
|
#include "hand_analysis.h"
|
||||||
@@ -178,7 +179,8 @@ static JokerEffect half_joker_effect(Joker *joker, Card *scored_card) {
|
|||||||
if (scored_card != NULL)
|
if (scored_card != NULL)
|
||||||
return effect; // if card != null, we are not at the end-phase of scoring yet
|
return effect; // if card != null, we are not at the end-phase of scoring yet
|
||||||
|
|
||||||
if (get_played_top() + 1 <= 3) // game.c has hand_get_size() but it's not accesible here
|
int played_size = get_played_top() + 1;
|
||||||
|
if (played_size <= 3)
|
||||||
effect.mult = 20;
|
effect.mult = 20;
|
||||||
|
|
||||||
return effect;
|
return effect;
|
||||||
@@ -190,12 +192,13 @@ static JokerEffect joker_stencil_effect(Joker *joker, Card *scored_card) {
|
|||||||
return effect; // if card != null, we are not at the end-phase of scoring yet
|
return effect; // if card != null, we are not at the end-phase of scoring yet
|
||||||
|
|
||||||
// +1 xmult per empty joker slot...
|
// +1 xmult per empty joker slot...
|
||||||
int jokers_top = get_jokers_top();
|
int num_jokers = get_jokers_top() + 1;
|
||||||
effect.xmult = (MAX_JOKERS_HELD_SIZE-1) - jokers_top;
|
|
||||||
|
effect.xmult = (MAX_JOKERS_HELD_SIZE) - num_jokers;
|
||||||
|
|
||||||
// ...and also each stencil_joker adds +1 xmult
|
// ...and also each stencil_joker adds +1 xmult
|
||||||
JokerObject** jokers = get_jokers();
|
JokerObject** jokers = get_jokers();
|
||||||
for (int i = 0; i < jokers_top; i++ )
|
for (int i = 0; i < num_jokers; i++ )
|
||||||
{
|
{
|
||||||
if (jokers[i]->joker->id == JOKER_STENCIL_ID)
|
if (jokers[i]->joker->id == JOKER_STENCIL_ID)
|
||||||
effect.xmult++;
|
effect.xmult++;
|
||||||
@@ -215,6 +218,78 @@ static JokerEffect misprint_joker_effect(Joker *joker, Card *scored_card) {
|
|||||||
return effect;
|
return effect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static JokerEffect walkie_talkie_joker_effect(Joker *joker, Card *scored_card) {
|
||||||
|
JokerEffect effect = {0};
|
||||||
|
if (scored_card == NULL)
|
||||||
|
return effect;
|
||||||
|
|
||||||
|
if (scored_card->rank == TEN || scored_card->rank == FOUR) {
|
||||||
|
effect.chips = 10;
|
||||||
|
effect.mult = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return effect;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JokerEffect fibonnaci_joker_effect(Joker *joker, Card *scored_card) {
|
||||||
|
JokerEffect effect = {0};
|
||||||
|
if (scored_card == NULL)
|
||||||
|
return effect;
|
||||||
|
|
||||||
|
switch (scored_card->rank) {
|
||||||
|
case ACE: case TWO: case THREE: case FIVE: case EIGHT:
|
||||||
|
effect.mult = 8;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return effect;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JokerEffect banner_joker_effect(Joker *joker, Card *scored_card) {
|
||||||
|
JokerEffect effect = {0};
|
||||||
|
if (scored_card != NULL)
|
||||||
|
return effect; // if card != null, we are not at the end-phase of scoring yet
|
||||||
|
|
||||||
|
effect.chips = 30 * get_num_discards_remaining();
|
||||||
|
|
||||||
|
return effect;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JokerEffect mystic_summit_joker_effect(Joker *joker, Card *scored_card) {
|
||||||
|
JokerEffect effect = {0};
|
||||||
|
if (scored_card != NULL)
|
||||||
|
return effect; // if card != null, we are not at the end-phase of scoring yet
|
||||||
|
|
||||||
|
if (get_num_discards_remaining() == 0)
|
||||||
|
effect.mult = 15;
|
||||||
|
|
||||||
|
return effect;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JokerEffect blackboard_joker_effect(Joker *joker, Card *scored_card) {
|
||||||
|
JokerEffect effect = {0};
|
||||||
|
if (scored_card != NULL)
|
||||||
|
return effect; // if card != null, we are not at the end-phase of scoring yet
|
||||||
|
|
||||||
|
bool all_cards_are_spades_or_clubs = true;
|
||||||
|
CardObject** hand = get_hand_array();
|
||||||
|
int hand_size = hand_get_size();
|
||||||
|
for (int i = 0; i < hand_size; i++ )
|
||||||
|
{
|
||||||
|
u8 suit = hand[i]->card->suit;
|
||||||
|
if (suit == HEARTS || suit == DIAMONDS) {
|
||||||
|
all_cards_are_spades_or_clubs = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (all_cards_are_spades_or_clubs)
|
||||||
|
effect.xmult = 3;
|
||||||
|
|
||||||
|
return effect;
|
||||||
|
}
|
||||||
|
|
||||||
const JokerInfo joker_registry[] = {
|
const JokerInfo joker_registry[] = {
|
||||||
{ COMMON_JOKER, 2, default_joker_effect }, // DEFAULT_JOKER_ID = 0
|
{ COMMON_JOKER, 2, default_joker_effect }, // DEFAULT_JOKER_ID = 0
|
||||||
{ COMMON_JOKER, 5, greedy_joker_effect }, // GREEDY_JOKER_ID = 1
|
{ COMMON_JOKER, 5, greedy_joker_effect }, // GREEDY_JOKER_ID = 1
|
||||||
@@ -233,6 +308,11 @@ const JokerInfo joker_registry[] = {
|
|||||||
{ COMMON_JOKER, 4, crafty_joker_effect },
|
{ COMMON_JOKER, 4, crafty_joker_effect },
|
||||||
{ COMMON_JOKER, 5, half_joker_effect },
|
{ COMMON_JOKER, 5, half_joker_effect },
|
||||||
{ UNCOMMON_JOKER, 8, joker_stencil_effect },
|
{ UNCOMMON_JOKER, 8, joker_stencil_effect },
|
||||||
|
{ COMMON_JOKER, 5, banner_joker_effect },
|
||||||
|
{ COMMON_JOKER, 4, walkie_talkie_joker_effect },
|
||||||
|
{ UNCOMMON_JOKER, 8, fibonnaci_joker_effect },
|
||||||
|
{ UNCOMMON_JOKER, 6, blackboard_joker_effect },
|
||||||
|
{ COMMON_JOKER, 5, mystic_summit_joker_effect },
|
||||||
{ COMMON_JOKER, 4, misprint_joker_effect },
|
{ COMMON_JOKER, 4, misprint_joker_effect },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "game.h"
|
#include "game.h"
|
||||||
#include "blind.h"
|
#include "blind.h"
|
||||||
#include "joker.h"
|
#include "joker.h"
|
||||||
|
#include "affine_background.h"
|
||||||
#include "graphic_utils.h"
|
#include "graphic_utils.h"
|
||||||
|
|
||||||
// Graphics
|
// Graphics
|
||||||
@@ -17,29 +18,6 @@
|
|||||||
#include "soundbank.h"
|
#include "soundbank.h"
|
||||||
#include "soundbank_bin.h"
|
#include "soundbank_bin.h"
|
||||||
|
|
||||||
#define MAP_AFF_SIZE 0x0100
|
|
||||||
BG_AFFINE bgaff;
|
|
||||||
AFF_SRC_EX asx = {32<<8, 64<<8, 120, 80, 0x0100, 0x0100, 0};
|
|
||||||
|
|
||||||
|
|
||||||
void init_map() // Temp function derived from libtonc example
|
|
||||||
{
|
|
||||||
// Grit is fucking garbage and doesn't allow you to have a palette offset, they all get loaded at 0x0000. I have to manually change each tile's palette bank
|
|
||||||
|
|
||||||
unsigned int correctedTiles[affine_background_gfxTilesLen / 4];
|
|
||||||
for (int i = 0; i < affine_background_gfxTilesLen / 4; i++)
|
|
||||||
{
|
|
||||||
correctedTiles[i] = affine_background_gfxTiles[i] | 0xF0F0F0F0; // I brute forced this. I have no idea how this would and does work, but it does. We'll just leave this here for now.
|
|
||||||
// Actually for future reference, each number seems to equate to an XY on the palette, so 0xF0F0F0F0 would just push everything down 15 colors, since the second number isn't set.
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(&tile8_mem[AFFINE_BG_CBB], correctedTiles, affine_background_gfxTilesLen);
|
|
||||||
GRIT_CPY(&se_mem[AFFINE_BG_SBB], affine_background_gfxMap);
|
|
||||||
memcpy16(&pal_bg_mem[16 * 15], affine_background_gfxPal, 5);
|
|
||||||
|
|
||||||
bgaff = bg_aff_default;
|
|
||||||
}
|
|
||||||
|
|
||||||
void init()
|
void init()
|
||||||
{
|
{
|
||||||
irq_init(NULL);
|
irq_init(NULL);
|
||||||
@@ -93,9 +71,8 @@ void init()
|
|||||||
|
|
||||||
REG_DISPCNT = DCNT_MODE1 | DCNT_OBJ_1D | DCNT_BG0 | DCNT_BG1 | DCNT_BG2 | DCNT_OBJ | DCNT_WIN0 | DCNT_WIN1;
|
REG_DISPCNT = DCNT_MODE1 | DCNT_OBJ_1D | DCNT_BG0 | DCNT_BG1 | DCNT_BG2 | DCNT_OBJ | DCNT_WIN0 | DCNT_WIN1;
|
||||||
|
|
||||||
init_map(); // Initialize the map for the background
|
|
||||||
|
|
||||||
// Initialize subsystems
|
// Initialize subsystems
|
||||||
|
affine_background_init();
|
||||||
sprite_init();
|
sprite_init();
|
||||||
card_init();
|
card_init();
|
||||||
blind_init();
|
blind_init();
|
||||||
@@ -103,24 +80,9 @@ void init()
|
|||||||
game_init();
|
game_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void background_update() // This needs to be called before update() and draw() because setting REG_BG_AFFINE during an HBlank can cause issues
|
|
||||||
{
|
|
||||||
static uint timer = 0;
|
|
||||||
timer++;
|
|
||||||
|
|
||||||
// These values are not permament, just the current configuration for the affine background
|
|
||||||
asx.tex_x += 5;
|
|
||||||
asx.tex_y += 12;
|
|
||||||
asx.sx = (lu_sin(timer * 100)) >> 8; // Scale the sine value to fit in a s16
|
|
||||||
asx.sx += 256; // Add 256 to the sine value to make it positive
|
|
||||||
asx.sy = (lu_sin(timer * 100 + 0x4000)) >> 8; // Scale the sine value to fit in a s16
|
|
||||||
asx.sy += 256; // Add 256 to the sine value to make it positive
|
|
||||||
bg_rotscale_ex(&bgaff, &asx);
|
|
||||||
REG_BG_AFFINE[2]= bgaff;
|
|
||||||
}
|
|
||||||
|
|
||||||
void update()
|
void update()
|
||||||
{
|
{
|
||||||
|
affine_background_update();
|
||||||
game_update();
|
game_update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +100,6 @@ int main()
|
|||||||
VBlankIntrWait();
|
VBlankIntrWait();
|
||||||
mmFrame();
|
mmFrame();
|
||||||
key_poll();
|
key_poll();
|
||||||
background_update();
|
|
||||||
update();
|
update();
|
||||||
draw();
|
draw();
|
||||||
}
|
}
|
||||||
|
|||||||