Merge branch '_RHH/master' into _RHH/upcoming

This commit is contained in:
Eduardo Quezada 2024-10-27 08:02:45 -03:00
commit 5d4ab8cc7e
8 changed files with 125 additions and 42 deletions

View file

@ -372,6 +372,13 @@ ifneq ($(NODEP),1)
-include $(addprefix $(OBJ_DIR)/,$(C_SRCS:.c=.d))
endif
$(TEST_BUILDDIR)/%.o: $(TEST_SUBDIR)/%.c
@echo "$(CC1) <flags> -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 $@ $<
@ -415,14 +422,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) <flags> -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
LD_SCRIPT := ld_script_modern.ld
LD_SCRIPT_DEPS :=

View file

@ -931,7 +931,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);
@ -949,7 +949,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);

View file

@ -2297,14 +2297,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)
{
@ -2432,10 +2432,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)
@ -2449,10 +2448,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))
@ -2478,10 +2476,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))
@ -2510,9 +2507,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)
{
@ -2529,9 +2526,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;
@ -2542,9 +2539,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)
@ -2564,9 +2561,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;
@ -2580,7 +2577,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;
@ -2884,7 +2885,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))

View file

@ -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 the opposing Gastly…");
}
}

View file

@ -99,3 +99,21 @@ SINGLE_BATTLE_TEST("Salt Cure does not get applied if hitting a Substitute")
NOT MESSAGE("The opposing 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("The opposing Clefable is hurt by Salt Cure!");
}
}
}

View file

@ -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("The opposing Wobbuffet used Celebrate!");
MESSAGE("Wobbuffet used Celebrate!");
MESSAGE("Tapu Bulu used Celebrate!");
MESSAGE("The opposing Wobbuffet used Celebrate!");
MESSAGE("Wobbuffet used Celebrate!");
MESSAGE("Tapu Bulu used Celebrate!");
MESSAGE("The opposing Wobbuffet used Celebrate!");
MESSAGE("The grass disappeared from the battlefield.");

View file

@ -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)

View file

@ -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).