From 111a95951cdb78a5d9f8464ede89368e83f834c6 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Tue, 22 Oct 2024 13:25:46 +0100 Subject: [PATCH 1/6] Fallback memmem implementation --- tools/mgba-rom-test-hydra/Makefile | 2 +- tools/mgba-rom-test-hydra/main.c | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/tools/mgba-rom-test-hydra/Makefile b/tools/mgba-rom-test-hydra/Makefile index f93f991d9b..ef0523adc9 100644 --- a/tools/mgba-rom-test-hydra/Makefile +++ b/tools/mgba-rom-test-hydra/Makefile @@ -12,7 +12,7 @@ all: mgba-rom-test-hydra$(EXE) @: mgba-rom-test-hydra$(EXE): $(SRCS) - $(CC) $(SRCS) -o $@ -lm $(LDFLAGS) + $(CC) $(SRCS) -Werror=implicit-function-declaration -o $@ -lm $(LDFLAGS) clean: $(RM) mgba-rom-test-hydra$(EXE) diff --git a/tools/mgba-rom-test-hydra/main.c b/tools/mgba-rom-test-hydra/main.c index 841b7328e5..d4c0f4e080 100644 --- a/tools/mgba-rom-test-hydra/main.c +++ b/tools/mgba-rom-test-hydra/main.c @@ -106,6 +106,28 @@ static const struct Symbol *lookup_address(uint32_t address) return NULL; } +#ifndef _GNU_SOURCE +// Very naive implementation of 'memmem' for systems which don't make it +// available by default. +void *memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) +{ + const char *haystack_ = haystack; + const char *needle_ = needle; + for (size_t i = 0; i < haystacklen - needlelen; i++) + { + size_t j; + for (j = 0; j < needlelen; j++) + { + if (haystack_[i+j] != needle_[j]) + break; + } + if (j == needlelen) + return (void *)&haystack_[i]; + } + return NULL; +} +#endif + // Similar to 'fwrite(buffer, 1, size, f)' except that anything which // looks like the output of '%p' (i.e. '<0x\d{7}>') is translated into // the name of a symbol (if it represents one). From be98de71d519f9db00c52290889d3a67116d4cf4 Mon Sep 17 00:00:00 2001 From: GhoulMage Date: Thu, 24 Oct 2024 18:12:31 +0200 Subject: [PATCH 2/6] Fixed damage test (#5574) --- test/battle/move_effect/fixed_damage_arg.c | 41 ++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 test/battle/move_effect/fixed_damage_arg.c diff --git a/test/battle/move_effect/fixed_damage_arg.c b/test/battle/move_effect/fixed_damage_arg.c new file mode 100644 index 0000000000..6d96b435db --- /dev/null +++ b/test/battle/move_effect/fixed_damage_arg.c @@ -0,0 +1,41 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_SONIC_BOOM].effect == EFFECT_FIXED_DAMAGE_ARG); +} + +SINGLE_BATTLE_TEST("Sonic Boom deals fixed damage", s16 damage) +{ + u16 mon; + PARAMETRIZE { mon = SPECIES_RATTATA; } + PARAMETRIZE { mon = SPECIES_ARON; } + + GIVEN { + ASSUME(gMovesInfo[MOVE_SONIC_BOOM].argument == 20); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(mon); + } WHEN { + TURN { MOVE(player, MOVE_SONIC_BOOM); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SONIC_BOOM, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT(results[0].damage == 20); + EXPECT(results[1].damage == 20); + } +} + +SINGLE_BATTLE_TEST("Sonic Boom doesn't affect ghost types") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GASTLY); + } WHEN { + TURN { MOVE(player, MOVE_SONIC_BOOM); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SONIC_BOOM, player); + MESSAGE("It doesn't affect Foe Gastly…"); + } +} From 5dcf3e766444a1260f1e5db9f2bce8acba548717 Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Fri, 25 Oct 2024 14:42:06 +0200 Subject: [PATCH 3/6] Fixed Shiny Pokemon not being shiny after transforming with a gimmick (#5573) Co-authored-by: Hedara --- src/battle_gfx_sfx_util.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index cb3349ee32..aa47a73540 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -897,7 +897,7 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo if (GetBattlerSide(battlerAtk) == B_SIDE_PLAYER) { - if (B_TRANSFORM_SHINY >= GEN_4 && trackEnemyPersonality) + if (B_TRANSFORM_SHINY >= GEN_4 && trackEnemyPersonality && !megaEvo) { personalityValue = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerDef]], MON_DATA_PERSONALITY); isShiny = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerDef]], MON_DATA_IS_SHINY); @@ -915,7 +915,7 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo } else { - if (B_TRANSFORM_SHINY >= GEN_4 && trackEnemyPersonality) + if (B_TRANSFORM_SHINY >= GEN_4 && trackEnemyPersonality && !megaEvo) { personalityValue = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerDef]], MON_DATA_PERSONALITY); isShiny = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerDef]], MON_DATA_IS_SHINY); From ad16fa76f20ed2507ca9cc2a3bffcf282e2dbd21 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sat, 26 Oct 2024 10:11:45 +0200 Subject: [PATCH 4/6] Fixes Magic Guard not preventing Salt Cure (#5583) --- src/battle_util.c | 55 +++++++++++++++-------------- test/battle/move_effect/salt_cure.c | 18 ++++++++++ 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index 079a9dd436..5284909ddd 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -2318,14 +2318,14 @@ s32 GetDrainedBigRootHp(u32 battler, s32 hp) return hp * -1; } -#define MAGIC_GUARD_CHECK \ -if (ability == ABILITY_MAGIC_GUARD) \ -{\ - RecordAbilityBattle(battler, ability);\ - gBattleStruct->turnEffectsTracker++;\ - break;\ -} +static inline bool32 IsBattlerProtectedByMagicGuard(u32 battler, u32 ability) +{ + if (ability != ABILITY_MAGIC_GUARD) + return FALSE; + RecordAbilityBattle(battler, ability); + return TRUE; +} u8 DoBattlerEndTurnEffects(void) { @@ -2453,10 +2453,9 @@ u8 DoBattlerEndTurnEffects(void) case ENDTURN_LEECH_SEED: // leech seed if ((gStatuses3[battler] & STATUS3_LEECHSEED) && IsBattlerAlive(gStatuses3[battler] & STATUS3_LEECHSEED_BATTLER) - && IsBattlerAlive(battler)) + && IsBattlerAlive(battler) + && !IsBattlerProtectedByMagicGuard(battler, ability)) { - MAGIC_GUARD_CHECK; - gBattlerTarget = gStatuses3[battler] & STATUS3_LEECHSEED_BATTLER; // Notice gBattlerTarget is actually the HP receiver. gBattleMoveDamage = GetNonDynamaxMaxHP(battler) / 8; if (gBattleMoveDamage == 0) @@ -2470,10 +2469,9 @@ u8 DoBattlerEndTurnEffects(void) break; case ENDTURN_POISON: // poison if ((gBattleMons[battler].status1 & STATUS1_POISON) - && IsBattlerAlive(battler)) + && IsBattlerAlive(battler) + && !IsBattlerProtectedByMagicGuard(battler, ability)) { - MAGIC_GUARD_CHECK; - if (ability == ABILITY_POISON_HEAL) { if (!BATTLER_MAX_HP(battler) && !(gStatuses3[battler] & STATUS3_HEAL_BLOCK)) @@ -2499,10 +2497,9 @@ u8 DoBattlerEndTurnEffects(void) break; case ENDTURN_BAD_POISON: // toxic poison if ((gBattleMons[battler].status1 & STATUS1_TOXIC_POISON) - && IsBattlerAlive(battler)) + && IsBattlerAlive(battler) + && !IsBattlerProtectedByMagicGuard(battler, ability)) { - MAGIC_GUARD_CHECK; - if (ability == ABILITY_POISON_HEAL) { if (!BATTLER_MAX_HP(battler) && !(gStatuses3[battler] & STATUS3_HEAL_BLOCK)) @@ -2531,9 +2528,9 @@ u8 DoBattlerEndTurnEffects(void) break; case ENDTURN_BURN: // burn if ((gBattleMons[battler].status1 & STATUS1_BURN) - && IsBattlerAlive(battler)) + && IsBattlerAlive(battler) + && !IsBattlerProtectedByMagicGuard(battler, ability)) { - MAGIC_GUARD_CHECK; gBattleMoveDamage = GetNonDynamaxMaxHP(battler) / (B_BURN_DAMAGE >= GEN_7 ? 16 : 8); if (ability == ABILITY_HEATPROOF) { @@ -2550,9 +2547,9 @@ u8 DoBattlerEndTurnEffects(void) break; case ENDTURN_FROSTBITE: // burn if ((gBattleMons[battler].status1 & STATUS1_FROSTBITE) - && IsBattlerAlive(battler)) + && IsBattlerAlive(battler) + && !IsBattlerProtectedByMagicGuard(battler, ability)) { - MAGIC_GUARD_CHECK; gBattleMoveDamage = GetNonDynamaxMaxHP(battler) / (B_BURN_DAMAGE >= GEN_7 ? 16 : 8); if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; @@ -2563,9 +2560,9 @@ u8 DoBattlerEndTurnEffects(void) break; case ENDTURN_NIGHTMARES: // spooky nightmares if ((gBattleMons[battler].status2 & STATUS2_NIGHTMARE) - && IsBattlerAlive(battler)) + && IsBattlerAlive(battler) + && !IsBattlerProtectedByMagicGuard(battler, ability)) { - MAGIC_GUARD_CHECK; // R/S does not perform this sleep check, which causes the nightmare effect to // persist even after the affected Pokémon has been awakened by Shed Skin. if (gBattleMons[battler].status1 & STATUS1_SLEEP) @@ -2585,9 +2582,9 @@ u8 DoBattlerEndTurnEffects(void) break; case ENDTURN_CURSE: // curse if ((gBattleMons[battler].status2 & STATUS2_CURSED) - && IsBattlerAlive(battler)) + && IsBattlerAlive(battler) + && !IsBattlerProtectedByMagicGuard(battler, ability)) { - MAGIC_GUARD_CHECK; gBattleMoveDamage = GetNonDynamaxMaxHP(battler) / 4; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; @@ -2601,7 +2598,11 @@ u8 DoBattlerEndTurnEffects(void) { if (--gDisableStructs[battler].wrapTurns != 0) // damaged by wrap { - MAGIC_GUARD_CHECK; + if (IsBattlerProtectedByMagicGuard(battler, ability)) + { + gBattleStruct->turnEffectsTracker++; + break; + } gBattleScripting.animArg1 = gBattleStruct->wrappedMove[battler]; gBattleScripting.animArg2 = gBattleStruct->wrappedMove[battler] >> 8; @@ -2905,7 +2906,9 @@ u8 DoBattlerEndTurnEffects(void) gBattleStruct->turnEffectsTracker++; break; case ENDTURN_SALT_CURE: - if (gStatuses4[battler] & STATUS4_SALT_CURE && IsBattlerAlive(battler)) + if (gStatuses4[battler] & STATUS4_SALT_CURE + && IsBattlerAlive(battler) + && !IsBattlerProtectedByMagicGuard(battler, ability)) { gBattlerTarget = battler; if (IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_STEEL) || IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_WATER)) diff --git a/test/battle/move_effect/salt_cure.c b/test/battle/move_effect/salt_cure.c index 939e32f0a6..7385bb16e8 100644 --- a/test/battle/move_effect/salt_cure.c +++ b/test/battle/move_effect/salt_cure.c @@ -99,3 +99,21 @@ SINGLE_BATTLE_TEST("Salt Cure does not get applied if hitting a Substitute") NOT MESSAGE("Foe Wobbuffet is being salt cured!"); } } + +SINGLE_BATTLE_TEST("Salt Cure residual damage does not inflict any damage against Magic Guard") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_CLEFABLE) { Ability(ABILITY_MAGIC_GUARD); }; + } WHEN { + TURN { MOVE(player, MOVE_SALT_CURE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SALT_CURE, player); + HP_BAR(opponent); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SALT_CURE_DAMAGE, opponent); + HP_BAR(opponent); + MESSAGE("Foe Clefable is hurt by Salt Cure!"); + } + } +} From a2c64b0ed06d5180adca7418b83ace1bbc3377a2 Mon Sep 17 00:00:00 2001 From: RavePossum Date: Sat, 26 Oct 2024 18:13:04 -0400 Subject: [PATCH 5/6] Fix unnecessary dependency scanning for test build and test rom names --- Makefile | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index a807cbf8bf..4052e81a2e 100644 --- a/Makefile +++ b/Makefile @@ -86,8 +86,8 @@ ELF_NAME := $(ROM_NAME:.gba=.elf) MAP_NAME := $(ROM_NAME:.gba=.map) MODERN_ELF_NAME := $(MODERN_ROM_NAME:.gba=.elf) MODERN_MAP_NAME := $(MODERN_ROM_NAME:.gba=.map) -TESTELF = $(ROM_NAME:.gba=-test.elf) -HEADLESSELF = $(ROM_NAME:.gba=-test-headless.elf) +TESTELF = $(MODERN_ROM_NAME:.gba=-test.elf) +HEADLESSELF = $(MODERN_ROM_NAME:.gba=-test-headless.elf) # Pick our active variables ifeq ($(MODERN),0) @@ -407,6 +407,13 @@ ifneq ($(NODEP),1) -include $(addprefix $(OBJ_DIR)/,$(C_SRCS:.c=.d)) endif +$(TEST_BUILDDIR)/%.o: $(TEST_SUBDIR)/%.c + @echo "$(CC1) -o $@ $<" + @$(CPP) $(CPPFLAGS) $< | $(PREPROC) -i $< charmap.txt | $(CC1) $(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $(AS) $(ASFLAGS) -o $@ - + +$(TEST_BUILDDIR)/%.d: $(TEST_SUBDIR)/%.c + $(SCANINC) -M $@ $(INCLUDE_SCANINC_ARGS) -I tools/agbcc/include $< + $(ASM_BUILDDIR)/%.o: $(ASM_SUBDIR)/%.s $(AS) $(ASFLAGS) -o $@ $< @@ -450,14 +457,6 @@ $(OBJ_DIR)/sym_ewram.ld: sym_ewram.txt $(DATA_SRC_SUBDIR)/pokemon/teachable_learnsets.h: $(DATA_ASM_BUILDDIR)/event_scripts.o python3 $(TOOLS_DIR)/learnset_helpers/teachable.py -# NOTE: Based on C_DEP above, but without NODEP and KEEP_TEMPS handling. -define TEST_DEP -$1: $2 $$(shell $(SCANINC) -I include -I $(TOOLS_DIR)/agbcc/include $2) - @echo "$$(CC1) -o $$@ $$<" - @$$(CPP) $$(CPPFLAGS) $$< | $$(PREPROC) -i $$< charmap.txt | $$(CC1) $$(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $$(AS) $$(ASFLAGS) -o $$@ - -endef -$(foreach src, $(TEST_SRCS), $(eval $(call TEST_DEP,$(patsubst $(TEST_SUBDIR)/%.c,$(TEST_BUILDDIR)/%.o,$(src)),$(src),$(patsubst $(TEST_SUBDIR)/%.c,%,$(src))))) - # Linker script ifeq ($(MODERN),0) LD_SCRIPT := ld_script.ld From 2ccc91e0915720f7c52216886a6be4cab6032708 Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Sun, 27 Oct 2024 10:36:31 +0100 Subject: [PATCH 6/6] Fixed broken Starting Terrain test (#5582) Co-authored-by: Hedara --- test/battle/terrain/starting_terrain.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/battle/terrain/starting_terrain.c b/test/battle/terrain/starting_terrain.c index 37caa20bc7..f6c4412563 100644 --- a/test/battle/terrain/starting_terrain.c +++ b/test/battle/terrain/starting_terrain.c @@ -67,7 +67,7 @@ SINGLE_BATTLE_TEST("Terrain started after the one which started the battle lasts VarSet(B_VAR_STARTING_STATUS_TIMER, 0); GIVEN { - PLAYER(SPECIES_WOBBUFFET) { Ability(viaMove == TRUE ? ABILITY_SHADOW_TAG : ABILITY_GRASSY_SURGE); } + PLAYER(SPECIES_TAPU_BULU) { Ability(viaMove == TRUE ? ABILITY_TELEPATHY : ABILITY_GRASSY_SURGE); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { // More than 5 turns @@ -84,7 +84,7 @@ SINGLE_BATTLE_TEST("Terrain started after the one which started the battle lasts ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_RESTORE_BG); // Player uses Grassy Terrain if (viaMove) { - MESSAGE("Wobbuffet used GrssyTerrain!"); + MESSAGE("Tapu Bulu used Grassy Terrain!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_GRASSY_TERRAIN, player); MESSAGE("Grass grew to cover the battlefield!"); } else { @@ -94,13 +94,13 @@ SINGLE_BATTLE_TEST("Terrain started after the one which started the battle lasts } // 5 turns - MESSAGE("Wobbuffet used Celebrate!"); + MESSAGE("Tapu Bulu used Celebrate!"); MESSAGE("Foe Wobbuffet used Celebrate!"); - MESSAGE("Wobbuffet used Celebrate!"); + MESSAGE("Tapu Bulu used Celebrate!"); MESSAGE("Foe Wobbuffet used Celebrate!"); - MESSAGE("Wobbuffet used Celebrate!"); + MESSAGE("Tapu Bulu used Celebrate!"); MESSAGE("Foe Wobbuffet used Celebrate!"); MESSAGE("The grass disappeared from the battlefield.");