Merge remote-tracking branch 'upstream/main'
And add NULL Acrobat Joker effect
This commit is contained in:
@@ -38,17 +38,23 @@ GRAPHICS := graphics
|
||||
#---------------------------------------------------------------------------------
|
||||
ARCH := -mthumb -mthumb-interwork
|
||||
|
||||
GIT_DIRTY := $(shell git diff-index --quiet HEAD -- || echo "-dirty")
|
||||
GIT_HASH := $(shell git rev-parse --short HEAD || echo "undef")
|
||||
GIT_C_FLAGS := -DGIT_HASH=\"$(GIT_HASH)\" -DGIT_DIRTY=\"$(GIT_DIRTY)\"
|
||||
|
||||
CFLAGS := -g -O3 -Wall -Werror\
|
||||
-mcpu=arm7tdmi -mtune=arm7tdmi \
|
||||
-ffast-math -fomit-frame-pointer -funroll-loops \
|
||||
$(ARCH)
|
||||
|
||||
CFLAGS += $(GIT_C_FLAGS)
|
||||
|
||||
CFLAGS += $(INCLUDE)
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
|
||||
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $*.map)
|
||||
LDFLAGS = -g $(ARCH) -Wl,-Map,$(notdir $*.map),--undefined=balatro_version
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# any extra libraries we wish to link with the project
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# Balatro-GBA
|
||||
|
||||
[](https://github.com/cellos51/balatro-gba/actions)
|
||||
[](https://github.com/cellos51/balatro-gba/issues)
|
||||
[](https://github.com/cellos51/balatro-gba/pulls)
|
||||
[](https://github.com/cellos51/balatro-gba/actions)
|
||||
[](https://github.com/cellos51/balatro-gba/issues)
|
||||
[](https://github.com/cellos51/balatro-gba/pulls)
|
||||
[](https://github.com/cellos51/balatro-gba/discussions)
|
||||
|
||||
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.
|
||||
@@ -45,10 +46,10 @@ A docker compose file is provided to build this project.
|
||||
1.) Install [docker desktop](https://docs.docker.com/compose/install/).
|
||||
|
||||
2.) Open a terminal to this projects directory:
|
||||
- On **linux** run `UID=$(id -u) GID=$(id -g) docker compose up`
|
||||
- On **windows** run `docker compose up`
|
||||
- On **Linux** run `UID=$(id -u) GID=$(id -g) docker compose up`
|
||||
- On **Windows** run `docker compose up`
|
||||
|
||||
Docker will build the project and the ROM will be in the same location as step 7 describes below.
|
||||
Docker will build the project and the ROM will be in the same location as how Step 7 describes below.
|
||||
|
||||
## **-Windows-**
|
||||
Video Tutorial: https://youtu.be/72Zzo1VDYzQ?si=UDmEdbST1Cx1zZV2
|
||||
@@ -71,13 +72,31 @@ Disregard Steps 3-4 and instead click the green code button on the main reposito
|
||||
|
||||
## **-Linux-**
|
||||
|
||||
1.) Add the devkitPro repo using these instructions https://devkitpro.org/wiki/devkitPro_pacman
|
||||
1.) Add the devkitPro repository using these instructions https://devkitpro.org/wiki/devkitPro_pacman
|
||||
|
||||
2.) Install devkitPro by running `sudo pacman -S gba-dev` and accepting all packages
|
||||
2.) Install devkitPro by running `sudo pacman -S gba-dev` and accepting all packages.
|
||||
|
||||
3.) Activate the devkitPro environment by running `source /etc/profile.d/devkit-env.sh` or opening a new shell
|
||||
3.) Activate the devkitPro environment by running `source /etc/profile.d/devkit-env.sh` or opening a new shell.
|
||||
|
||||
4.) Follow instructions from windows tutorial step 3
|
||||
4.) Follow instructions from the Windows tutorial starting from Step 3
|
||||
|
||||
## **-macOS-**
|
||||
1.) Install devkitPro installer using: https://github.com/devkitPro/installer and following https://devkitpro.org/wiki/devkitPro_pacman#macOS.
|
||||
> Note: You may have to install the installers directly from their url in a browser, as the installer script may not install correctly due to Cloudflare checks on their server. You can use one of the following urls:
|
||||
|
||||
> Apple Silicon: https://pkg.devkitpro.org/packages/macos-installers/devkitpro-pacman-installer.arm64.pkg
|
||||
|
||||
> Intel: https://pkg.devkitpro.org/packages/macos-installers/devkitpro-pacman-installer.x86_64.pkg
|
||||
|
||||
|
||||
2.) Verify that devkitPro is installed in '/opt/devkitpro'
|
||||
|
||||
3.) Add the following to your .bashrc or .zshrc (or export the variables in your shell session):
|
||||
- export DEVKITPRO=/opt/devkitpro
|
||||
- export DEVKITARM=$DEVKITPRO/devkitARM
|
||||
- export PATH=$PATH:$DEVKITPRO/tools/bin:$DEVKITPRO/pacman/bin
|
||||
|
||||
4.) Follow instructions from Windows tutorial step 4
|
||||
|
||||
## **Common Issues:**
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import re
|
||||
|
||||
def grep_binary_offsets(path, pattern):
|
||||
with open(path, "rb") as f:
|
||||
data = f.read()
|
||||
|
||||
results = []
|
||||
for match in re.finditer(rb"[ -~]{4,}", data):
|
||||
s = match.group()
|
||||
if re.search(pattern.encode(), s):
|
||||
results.append((match.start(), s.decode("utf-8", errors="ignore")))
|
||||
return results
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python script.py <file>")
|
||||
sys.exit(1)
|
||||
|
||||
file_path = sys.argv[1]
|
||||
pattern = "GBALATRO_VERSION"
|
||||
|
||||
for offset, text in grep_binary_offsets(file_path, pattern):
|
||||
version = text.split(":")[1]
|
||||
print(f"git hash: {version}")
|
||||
+15
-2
@@ -878,8 +878,21 @@ void card_draw()
|
||||
|
||||
void hand_set_focus(int index)
|
||||
{
|
||||
if (index < 0 || index > hand_top || hand_state != HAND_SELECT) return;
|
||||
selection_x = index;
|
||||
if (hand_state != HAND_SELECT) return;
|
||||
|
||||
// Wrap around to the other side of the hand when going out of bounds on either side
|
||||
if (index < 0)
|
||||
{
|
||||
selection_x = hand_top;
|
||||
}
|
||||
else if (index > hand_top)
|
||||
{
|
||||
selection_x = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
selection_x = index;
|
||||
}
|
||||
|
||||
play_sfx(SFX_CARD_FOCUS, MM_BASE_PITCH_RATE + rand() % 512);
|
||||
}
|
||||
|
||||
+1
-1
@@ -157,7 +157,7 @@ void joker_destroy(Joker **joker)
|
||||
JokerEffect joker_get_score_effect(Joker *joker, Card *scored_card)
|
||||
{
|
||||
const JokerInfo *jinfo = get_joker_registry_entry(joker->id);
|
||||
if (!jinfo) return (JokerEffect){0};
|
||||
if (!jinfo || jinfo->effect == NULL) return (JokerEffect){0};
|
||||
|
||||
return jinfo->effect(joker, scored_card);
|
||||
}
|
||||
|
||||
+143
-1
@@ -464,6 +464,140 @@ static JokerEffect acrobat_joker_effect(Joker *joker, Card *scored_card) {
|
||||
return effect;
|
||||
}
|
||||
|
||||
__attribute__((unused))
|
||||
static JokerEffect the_duo_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
|
||||
|
||||
// This is really inefficient but the only way at the moment to check for whole-hand conditions
|
||||
u8 suits[NUM_SUITS];
|
||||
u8 ranks[NUM_RANKS];
|
||||
get_played_distribution(ranks, suits);
|
||||
|
||||
if (hand_contains_n_of_a_kind(ranks) >= 2)
|
||||
effect.xmult = 2;
|
||||
return effect;
|
||||
}
|
||||
|
||||
|
||||
// graphics available from @MathisMartin31
|
||||
__attribute__((unused))
|
||||
static JokerEffect the_trio_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
|
||||
|
||||
// This is really inefficient but the only way at the moment to check for whole-hand conditions
|
||||
u8 suits[NUM_SUITS];
|
||||
u8 ranks[NUM_RANKS];
|
||||
get_played_distribution(ranks, suits);
|
||||
|
||||
if (hand_contains_n_of_a_kind(ranks) >= 3)
|
||||
effect.xmult = 3;
|
||||
return effect;
|
||||
}
|
||||
|
||||
// graphics available from @MathisMartin31
|
||||
__attribute__((unused))
|
||||
static JokerEffect the_family_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
|
||||
|
||||
// This is really inefficient but the only way at the moment to check for whole-hand conditions
|
||||
u8 suits[NUM_SUITS];
|
||||
u8 ranks[NUM_RANKS];
|
||||
get_played_distribution(ranks, suits);
|
||||
|
||||
if (hand_contains_n_of_a_kind(ranks) >= 4)
|
||||
effect.xmult = 4;
|
||||
return effect;
|
||||
}
|
||||
|
||||
// graphics available from @MathisMartin31
|
||||
__attribute__((unused))
|
||||
static JokerEffect the_order_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
|
||||
|
||||
u8 suits[NUM_SUITS];
|
||||
u8 ranks[NUM_RANKS];
|
||||
get_played_distribution(ranks, suits);
|
||||
|
||||
if (hand_contains_straight(ranks))
|
||||
effect.xmult = 3;
|
||||
return effect;
|
||||
}
|
||||
|
||||
// graphics available from @MathisMartin31
|
||||
__attribute__((unused))
|
||||
static JokerEffect the_tribe_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
|
||||
|
||||
u8 suits[NUM_SUITS];
|
||||
u8 ranks[NUM_RANKS];
|
||||
get_played_distribution(ranks, suits);
|
||||
|
||||
if (hand_contains_flush(suits))
|
||||
effect.xmult = 2;
|
||||
return effect;
|
||||
}
|
||||
|
||||
|
||||
// graphics available from @MathisMartin31
|
||||
__attribute__((unused))
|
||||
static JokerEffect bootstraps_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.mult = (get_money() / 5) * 2;
|
||||
|
||||
return effect;
|
||||
}
|
||||
|
||||
// no graphics available but ready to be used if wanted when graphics available
|
||||
__attribute__((unused))
|
||||
static JokerEffect shoot_the_moon_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
|
||||
|
||||
CardObject** hand = get_hand_array();
|
||||
int hand_size = hand_get_size();
|
||||
for (int i = 0; i < hand_size; i++ )
|
||||
{
|
||||
if (hand[i]->card->rank == QUEEN)
|
||||
{
|
||||
effect.mult += 13;
|
||||
}
|
||||
}
|
||||
|
||||
return effect;
|
||||
}
|
||||
|
||||
// no graphics available but ready to be used if wanted when graphics available
|
||||
__attribute__((unused))
|
||||
static JokerEffect triboulet_joker_effect(Joker *joker, Card *scored_card) {
|
||||
JokerEffect effect = {0};
|
||||
if (scored_card == NULL)
|
||||
return effect;
|
||||
|
||||
switch (scored_card->rank) {
|
||||
case KING: case QUEEN:
|
||||
effect.xmult = 2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return effect;
|
||||
}
|
||||
|
||||
|
||||
/* The index of a joker in the registry matches its ID.
|
||||
* The joker sprites are matched by ID so the position in the registry
|
||||
* determines the joker's sprite.
|
||||
@@ -508,13 +642,21 @@ const JokerInfo joker_registry[] = {
|
||||
|
||||
// The following jokers don't have sprites yet,
|
||||
// uncomment them when their sprites are added.
|
||||
|
||||
#if 0
|
||||
{ COMMON_JOKER, 5, raised_fist_joker_effect },
|
||||
{ COMMON_JOKER, 6, reserved_parking_joker_effect },
|
||||
|
||||
{ COMMON_JOKER, 4, abstract_joker_effect },
|
||||
{ UNCOMMON_JOKER, 6, bull_joker_effect},
|
||||
|
||||
{ UNCOMMON_JOKER, 6, acrobat_joker_effect },
|
||||
{ RARE_JOKER, 8, the_duo_joker_effect},
|
||||
{ RARE_JOKER, 8, the_trio_joker_effect},
|
||||
{ RARE_JOKER, 8, the_family_joker_effect},
|
||||
{ RARE_JOKER, 8, the_order_joker_effect},
|
||||
{ RARE_JOKER, 8, the_tribe_joker_effect},
|
||||
{ UNCOMMON_JOKER, 7, bootstraps_joker_effect},
|
||||
{ COMMON_JOKER, 5, shoot_the_moon_joker_effect},
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#ifndef GIT_HASH
|
||||
#define GIT_HASH "undef"
|
||||
#endif
|
||||
|
||||
#ifndef GIT_DIRTY
|
||||
#define GIT_DIRTY "-dirty"
|
||||
#endif
|
||||
|
||||
__attribute__((section(".version"), used))
|
||||
const char balatro_version[] = "GBALATRO_VERSION:" GIT_HASH GIT_DIRTY;
|
||||
Reference in New Issue
Block a user