Merge branch 'master' into master-to-upcoming

Conflicts:
	data/battle_scripts_1.s
	include/battle_util.h
	include/constants/battle.h
	src/battle_script_commands.c
	src/battle_util.c
This commit is contained in:
Hedara 2025-10-29 18:42:59 +01:00
commit a2ef3284dd
24 changed files with 203 additions and 30 deletions

View File

@ -441,6 +441,15 @@
"contributions": [
"code"
]
},
{
"login": "MandL27",
"name": "MandL27",
"avatar_url": "https://avatars.githubusercontent.com/u/10366615?v=4",
"profile": "https://github.com/MandL27",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,

View File

@ -74,6 +74,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ghostyboyy97"><img src="https://avatars.githubusercontent.com/u/106448956?v=4?s=100" width="100px;" alt="ghostyboyy97"/><br /><sub><b>ghostyboyy97</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=ghostyboyy97" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://hashtagmarky.github.io"><img src="https://avatars.githubusercontent.com/u/143505183?v=4?s=100" width="100px;" alt="Marky"/><br /><sub><b>Marky</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=HashtagMarky" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MandL27"><img src="https://avatars.githubusercontent.com/u/10366615?v=4?s=100" width="100px;" alt="MandL27"/><br /><sub><b>MandL27</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=MandL27" title="Code">💻</a></td>
</tr>
</tbody>
<tfoot>

View File

@ -2536,6 +2536,7 @@ BattleScript_CantMakeAsleep::
BattleScript_EffectAbsorbLiquidOoze::
call BattleScript_AbilityPopUpTarget
jumpifability BS_ATTACKER, ABILITY_MAGIC_GUARD, BattleScript_EffectAbsorbRet
goto BattleScript_EffectAbsorb
BattleScript_EffectAbsorb::
@ -2544,6 +2545,7 @@ BattleScript_EffectAbsorb::
printfromtable gAbsorbDrainStringIds
waitmessage B_WAIT_TIME_LONG
tryfaintmon BS_ATTACKER
BattleScript_EffectAbsorbRet:
return
BattleScript_EffectExplosion::
@ -4945,10 +4947,12 @@ BattleScript_LeechSeedTurnDrainLiquidOoze::
copybyte gBattlerAbility, gBattlerAttacker
call BattleScript_AbilityPopUp
copybyte gBattlerAttacker, gBattlerTarget @ needed to get liquid ooze message correct
jumpifability BS_TARGET, ABILITY_MAGIC_GUARD, BattleScript_LeechSeedTurnDrainHealBlockEnd2
goto BattleScript_LeechSeedTurnDrainGainHp
BattleScript_LeechSeedTurnDrainHealBlock::
call BattleScript_LeechSeedTurnDrain
BattleScript_LeechSeedTurnDrainHealBlockEnd2:
end2
BattleScript_LeechSeedTurnDrainRecovery::

Binary file not shown.

Before

Width:  |  Height:  |  Size: 914 B

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 728 B

After

Width:  |  Height:  |  Size: 567 B

View File

@ -35,6 +35,7 @@ void SetGimmickAsActivated(u32 battler, enum Gimmick gimmick);
void ChangeGimmickTriggerSprite(u32 spriteId, u32 animId);
void CreateGimmickTriggerSprite(u32 battler);
bool32 IsGimmickTriggerSpriteActive(void);
bool32 IsGimmickTriggerSpriteMatchingBattler(u32 battler);
void HideGimmickTriggerSprite(void);
void DestroyGimmickTriggerSprite(void);

View File

@ -423,6 +423,7 @@ bool32 CanMoveSkipAccuracyCalc(u32 battlerAtk, u32 battlerDef, enum Ability abil
u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, enum Ability atkAbility, enum Ability defAbility, enum HoldEffect atkHoldEffect, enum HoldEffect defHoldEffect);
bool32 IsSemiInvulnerable(u32 battler, enum SemiInvulnerableExclusion excludeCommander);
bool32 BreaksThroughSemiInvulnerablity(u32 battler, u32 move);
bool32 HasPartnerTrainer(u32 battler);
u32 GetNaturePowerMove(u32 battler);
u32 GetNaturePowerMove(u32 battler);
void RemoveAbilityFlags(u32 battler);

View File

@ -8,6 +8,7 @@
#define P_SUMMARY_SCREEN_RENAME TRUE // If TRUE, an option to change Pokémon nicknames replaces the cancel prompt on the summary screen info page.
#define P_SUMMARY_SCREEN_IV_EV_INFO FALSE // If TRUE, will allow player to cycle through the Stats, IVs, and EVs in the summary screen skills page.
#define P_SUMMARY_SCREEN_IV_EV_BOX_ONLY FALSE // If TRUE, will allow player to cycle through the Stats, IVs, and EVs in the summary screen skills page, but only in the PC storage box.
#define P_SUMMARY_SCREEN_IV_HYPERTRAIN TRUE // If TRUE, stats that have been hyper trained will show as 31/S when viewing them in the summary screen
#define P_SUMMARY_SCREEN_IV_EV_TILESET FALSE // If TRUE, loads an alternate tileset to allow changing the "STATS" label in the summary screen skills page. Note: if it's still loading the alternate tileset after changing this and recompiling, you may need a `make clean` before compilation.
#define P_SUMMARY_SCREEN_IV_EV_VALUES FALSE // If TRUE, will show the actual IV value instead of the letter grade.
/*

View File

@ -103,6 +103,7 @@ enum BattleSide
#define BATTLE_TWO_VS_ONE_OPPONENT ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && TRAINER_BATTLE_PARAM.opponentB == 0xFFFF))
#define BATTLE_TYPE_HAS_AI (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER | BATTLE_TYPE_INGAME_PARTNER)
#define BATTLE_TYPE_MORE_THAN_TWO_BATTLERS (BATTLE_TYPE_DOUBLE | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_TWO_OPPONENTS)
#define BATTLE_TYPE_PLAYER_HAS_PARTNER (BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_TOWER_LINK_MULTI)
// Multibattle test composite flags
#define BATTLE_MULTI_TEST (BATTLE_TYPE_IS_MASTER | BATTLE_TYPE_TRAINER | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_MULTI | BATTLE_TYPE_TWO_OPPONENTS)

View File

@ -1119,7 +1119,7 @@ bool32 ShouldSwitch(u32 battler)
if (IsDoubleBattle())
{
u32 partner = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerAtPosition(battler)));
u32 partner = BATTLE_PARTNER(battler);
battlerIn1 = battler;
if (gAbsentBattlerFlags & (1u << partner))
battlerIn2 = battler;
@ -1270,7 +1270,7 @@ void ModifySwitchAfterMoveScoring(u32 battler)
if (IsDoubleBattle())
{
u32 partner = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerAtPosition(battler)));
u32 partner = BATTLE_PARTNER(battler);
battlerIn1 = battler;
if (gAbsentBattlerFlags & (1u << partner))
battlerIn2 = battler;
@ -1318,7 +1318,7 @@ bool32 IsSwitchinValid(u32 battler)
// Edge case: See if partner already chose to switch into the same mon
if (IsDoubleBattle())
{
u32 partner = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerAtPosition(battler)));
u32 partner = BATTLE_PARTNER(battler);
if (gBattleStruct->AI_monToSwitchIntoId[battler] == PARTY_SIZE) // Generic switch
{
if ((gAiLogicData->shouldSwitch & (1u << partner)) && gAiLogicData->monToSwitchInId[partner] == gAiLogicData->mostSuitableMonId[battler])

View File

@ -891,7 +891,7 @@ static inline bool32 ShouldCalcCritDamage(u32 battlerAtk, u32 battlerDef, u32 mo
struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, enum AIConsiderGimmick considerGimmickAtk, enum AIConsiderGimmick considerGimmickDef, u32 weather)
{
struct SimulatedDamage simDamage;
struct SimulatedDamage simDamage = {0};
enum BattleMoveEffects moveEffect = GetMoveEffect(move);
bool32 isDamageMoveUnusable = FALSE;
bool32 toggledGimmickAtk = FALSE;
@ -924,7 +924,7 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
gBattleStruct->magnitudeBasePower = 70;
gBattleStruct->presentBasePower = 80;
struct DamageContext ctx;
struct DamageContext ctx = {0};
ctx.battlerAtk = battlerAtk;
ctx.battlerDef = battlerDef;
ctx.move = move;

View File

@ -455,6 +455,12 @@ void HandleInputChooseTarget(u32 battler)
PlaySE(SE_SELECT);
gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCB_HideAsMoveTarget;
gBattlerControllerFuncs[battler] = HandleInputChooseMove;
if (gBattleStruct->gimmick.playerSelect == 1 && gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE)
{
gBattleStruct->gimmick.playerSelect = 0;
gBattleStruct->zmove.viewing = TRUE;
ReloadMoveNames(battler);
}
DoBounceEffect(battler, BOUNCE_HEALTHBOX, 7, 1);
DoBounceEffect(battler, BOUNCE_MON, 7, 1);
EndBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX);
@ -2100,6 +2106,8 @@ void PlayerHandleChooseMove(u32 battler)
if (!IsGimmickTriggerSpriteActive())
gBattleStruct->gimmick.triggerSpriteId = 0xFF;
else if (!IsGimmickTriggerSpriteMatchingBattler(battler))
DestroyGimmickTriggerSprite();
if (!(gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE && !gBattleStruct->zmove.viable))
CreateGimmickTriggerSprite(battler);

View File

@ -298,13 +298,12 @@ static bool32 HandleEndTurnEmergencyExit(u32 battler)
&& IsBattlerAlive(battler)
&& (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER))
&& !(gBattleTypeFlags & BATTLE_TYPE_ARENA)
&& CountUsablePartyMons(battler) > 0
&& gBattleMons[battler].volatiles.semiInvulnerable != STATE_SKY_DROP) // Not currently held by Sky Drop
{
gBattlerAbility = battler;
gLastUsedAbility = ability;
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER || IsOnPlayerSide(battler))
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
BattleScriptExecute(BattleScript_EmergencyExitEnd2);
else
BattleScriptExecute(BattleScript_EmergencyExitWildEnd2);

View File

@ -178,6 +178,13 @@ bool32 IsGimmickTriggerSpriteActive(void)
return FALSE;
}
bool32 IsGimmickTriggerSpriteMatchingBattler(u32 battler)
{
if (battler == gSprites[gBattleStruct->gimmick.triggerSpriteId].tBattler)
return TRUE;
return FALSE;
}
void HideGimmickTriggerSprite(void)
{
if (gBattleStruct->gimmick.triggerSpriteId != 0xFF)

View File

@ -3237,6 +3237,9 @@ void SwitchInClearSetData(u32 battler, struct Volatiles *volatilesCopy)
gBattleStruct->battlerState[battler].stompingTantrumTimer = 0;
gBattleStruct->palaceFlags &= ~(1u << battler);
gBattleStruct->battlerState[battler].canPickupItem = FALSE;
gBattleStruct->hazardsCounter = 0;
gDisableStructs[battler].hazardsDone = FALSE;
gSpecialStatuses[battler].switchInItemDone = FALSE;
ClearPursuitValuesIfSet(battler);

View File

@ -1098,7 +1098,6 @@ bool32 EmergencyExitCanBeTriggered(u32 battler)
&& HadMoreThanHalfHpNowDoesnt(battler)
&& (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER))
&& !(gBattleTypeFlags & BATTLE_TYPE_ARENA)
&& CountUsablePartyMons(battler) > 0
&& gBattleMons[battler].volatiles.semiInvulnerable != STATE_SKY_DROP)
return TRUE;
@ -6696,7 +6695,7 @@ static void Cmd_moveend(void)
effect = TRUE;
gBattleScripting.battler = battler;
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER || IsOnPlayerSide(battler))
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
BattleScriptCall(BattleScript_EmergencyExit);
else
BattleScriptCall(BattleScript_EmergencyExitWild);
@ -15386,9 +15385,7 @@ void BS_TryAllySwitch(void)
{
NATIVE_ARGS(const u8 *failInstr);
if (!IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker))
|| (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
|| (GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT && gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS))
if (!IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker)) || HasPartnerTrainer(gBattlerAttacker))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -16831,11 +16828,14 @@ void BS_ArenaJudgmentWindow(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void SetArenMonLostValues(u32 battler)
static void SetArenMonLostValues(u32 battler, u32 side)
{
gBattleMons[battler].hp = 0;
gHitMarker |= HITMARKER_FAINTED(battler);
gBattleStruct->arenaLostOpponentMons |= 1u << gBattlerPartyIndexes[battler];
if (side == B_SIDE_PLAYER)
gBattleStruct->arenaLostPlayerMons |= 1u << gBattlerPartyIndexes[battler];
else
gBattleStruct->arenaLostOpponentMons |= 1u << gBattlerPartyIndexes[battler];
gDisableStructs[battler].truantSwitchInHack = TRUE;
}
@ -16844,22 +16844,22 @@ static void SetArenMonLostValues(u32 battler)
void BS_ArenaOpponentMonLost(void)
{
NATIVE_ARGS();
SetArenMonLostValues(opponentMon);
SetArenMonLostValues(opponentMon, B_SIDE_OPPONENT);
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_ArenaPlayerMonLost(void)
{
NATIVE_ARGS();
SetArenMonLostValues(playerMon);
SetArenMonLostValues(playerMon, B_SIDE_PLAYER);
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_ArenaBothMonsLost(void)
{
NATIVE_ARGS();
SetArenMonLostValues(playerMon);
SetArenMonLostValues(opponentMon);
SetArenMonLostValues(playerMon, B_SIDE_PLAYER);
SetArenMonLostValues(opponentMon, B_SIDE_OPPONENT);
gBattlescriptCurrInstr = cmd->nextInstr;
}
#undef playerMon

View File

@ -1990,7 +1990,7 @@ void DoSpecialTrainerBattle(void)
gBattleTypeFlags |= BATTLE_TYPE_DOUBLE | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_MULTI | BATTLE_TYPE_TWO_OPPONENTS;
break;
case FRONTIER_MODE_LINK_MULTIS:
gBattleTypeFlags |= BATTLE_TYPE_DOUBLE | BATTLE_TYPE_LINK | BATTLE_TYPE_MULTI | BATTLE_TYPE_TOWER_LINK_MULTI;
gBattleTypeFlags |= BATTLE_TYPE_DOUBLE | BATTLE_TYPE_LINK | BATTLE_TYPE_MULTI | BATTLE_TYPE_TOWER_LINK_MULTI | BATTLE_TYPE_TWO_OPPONENTS;
FillFrontierTrainersParties(FRONTIER_MULTI_PARTY_SIZE);
break;
}

View File

@ -10771,6 +10771,15 @@ bool32 BreaksThroughSemiInvulnerablity(u32 battler, u32 move)
return FALSE;
}
bool32 HasPartnerTrainer(u32 battler)
{
if ((GetBattlerSide(battler) == B_SIDE_PLAYER && gBattleTypeFlags & BATTLE_TYPE_PLAYER_HAS_PARTNER)
|| (GetBattlerSide(battler) == B_SIDE_OPPONENT && gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS))
return TRUE;
else
return FALSE;
}
static u32 GetMirrorMoveMove(void)
{
s32 i, validMovesCount;

View File

@ -12522,7 +12522,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
SIZE_32x32,
SHADOW_SIZE_M,
TRACKS_FOOT,
sAnimTable_Following,
sAnimTable_Following_Asym,
gOverworldPalette_Krabby,
gShinyOverworldPalette_Krabby
)
@ -12592,7 +12592,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
SIZE_32x32,
SHADOW_SIZE_M,
TRACKS_FOOT,
sAnimTable_Following,
sAnimTable_Following_Asym,
gOverworldPalette_Kingler,
gShinyOverworldPalette_Kingler
)

View File

@ -22,12 +22,13 @@
#include "item.h"
#include "item_icon.h"
#include "link.h"
#include "load_save.h"
#include "list_menu.h"
#include "load_save.h"
#include "main.h"
#include "mystery_gift.h"
#include "match_call.h"
#include "menu.h"
#include "metatile_behavior.h"
#include "mystery_gift.h"
#include "overworld.h"
#include "party_menu.h"
#include "pokeblock.h"
@ -985,7 +986,7 @@ void FieldShowRegionMap(void)
static bool32 IsBuildingPCTile(u32 tileId)
{
return gMapHeader.mapLayout->primaryTileset == &gTileset_Building && (tileId == METATILE_Building_PC_On || tileId == METATILE_Building_PC_Off);
return (MetatileBehavior_IsPC(UNPACK_BEHAVIOR(GetMetatileAttributesById(tileId))));
}
static bool32 IsPlayerHousePCTile(u32 tileId)

View File

@ -333,6 +333,7 @@ static const u8 *GetLetterGrade(u32 stat);
static u8 AddWindowFromTemplateList(const struct WindowTemplate *template, u8 templateId);
static u8 IncrementSkillsStatsMode(u8 mode);
static void ClearStatLabel(u32 length, u32 statsCoordX, u32 statsCoordY);
u32 GetAdjustedIvData(struct Pokemon *mon, u32 stat);
static const struct BgTemplate sBgTemplates[] =
{
@ -1172,6 +1173,13 @@ static void DestroyCategoryIcon(void)
sMonSummaryScreen->categoryIconSpriteId = 0xFF;
}
u32 GetAdjustedIvData(struct Pokemon *mon, u32 stat)
{
if (P_SUMMARY_SCREEN_IV_HYPERTRAIN && GetMonData(mon, MON_DATA_HYPER_TRAINED_HP + stat))
return MAX_PER_STAT_IVS;
return GetMonData(mon, MON_DATA_HP_IV + stat);
}
void ShowPokemonSummaryScreen(u8 mode, void *mons, u8 monIndex, u8 maxMonIndex, void (*callback)(void))
{
sMonSummaryScreen = AllocZeroed(sizeof(*sMonSummaryScreen));
@ -1863,12 +1871,12 @@ void ExtractMonSkillStatsData(struct Pokemon *mon, struct PokeSummary *sum)
void ExtractMonSkillIvData(struct Pokemon *mon, struct PokeSummary *sum)
{
sum->currentHP = GetMonData(mon, MON_DATA_HP_IV);
sum->atk = GetMonData(mon, MON_DATA_ATK_IV);
sum->def = GetMonData(mon, MON_DATA_DEF_IV);
sum->spatk = GetMonData(mon, MON_DATA_SPATK_IV);
sum->spdef = GetMonData(mon, MON_DATA_SPDEF_IV);
sum->speed = GetMonData(mon, MON_DATA_SPEED_IV);
sum->currentHP = GetAdjustedIvData(mon, STAT_HP);
sum->atk = GetAdjustedIvData(mon, STAT_ATK);
sum->def = GetAdjustedIvData(mon, STAT_DEF);
sum->spatk = GetAdjustedIvData(mon, STAT_SPATK);
sum->spdef = GetAdjustedIvData(mon, STAT_SPDEF);
sum->speed = GetAdjustedIvData(mon, STAT_SPEED);
}
void ExtractMonSkillEvData(struct Pokemon *mon, struct PokeSummary *sum)

View File

@ -108,3 +108,69 @@ SINGLE_BATTLE_TEST("Emergency Exit activates when taking residual damage and fal
ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT);
}
}
WILD_BATTLE_TEST("Emergency Exit makes the pokemon flee during wild battle")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(262); };
} WHEN {
TURN { MOVE(player, MOVE_SUPER_FANG);}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUPER_FANG, player);
HP_BAR(opponent);
ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT);
} THEN {
EXPECT_EQ(gBattleOutcome, B_OUTCOME_MON_TELEPORTED);
}
}
WILD_BATTLE_TEST("Emergency Exit activates when taking residual damage and falling under 50% max-hp (wild battle)")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(134); Status1(STATUS1_BURN); };
} WHEN {
TURN { }
} SCENE {
HP_BAR(opponent);
ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT);
} THEN {
EXPECT_EQ(gBattleOutcome, B_OUTCOME_MON_TELEPORTED);
}
}
WILD_BATTLE_TEST("Emergency Exit makes the player ran during wild battle")
{
GIVEN {
PLAYER(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(262); };
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SUPER_FANG);}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUPER_FANG, opponent);
HP_BAR(player);
ABILITY_POPUP(player, ABILITY_EMERGENCY_EXIT);
} THEN {
EXPECT_EQ(gBattleOutcome, B_OUTCOME_PLAYER_TELEPORTED);
}
}
WILD_BATTLE_TEST("Emergency Exit activates when taking residual damage and falling under 50% max-hp (wild battle player side)")
{
GIVEN {
PLAYER(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(134); };
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SANDSTORM);}
} SCENE {
HP_BAR(player);
ABILITY_POPUP(player, ABILITY_EMERGENCY_EXIT);
} THEN {
EXPECT_EQ(gBattleOutcome, B_OUTCOME_PLAYER_TELEPORTED);
}
}

View File

@ -187,3 +187,34 @@ SINGLE_BATTLE_TEST("Liquid Ooze does not cause Dream Eater users to lose HP inst
EXPECT_LT(damage, 0); // Negative damage = Heal
}
}
SINGLE_BATTLE_TEST("Liquid Ooze HP loss from Absorb is blocked by Magic Guard")
{
GIVEN {
PLAYER(SPECIES_CLEFFA) { Ability(ABILITY_MAGIC_GUARD); }
OPPONENT(SPECIES_TENTACOOL) { Ability(ABILITY_LIQUID_OOZE); }
} WHEN {
TURN { MOVE(player, MOVE_ABSORB); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ABSORB, player);
HP_BAR(opponent);
NONE_OF {
HP_BAR(player);
MESSAGE("Wobbuffet sucked up the liquid ooze!");
}
}
}
SINGLE_BATTLE_TEST("Liquid Ooze HP loss from Leech Seed is blocked by Magic Guard")
{
GIVEN {
PLAYER(SPECIES_CLEFFA) { Ability(ABILITY_MAGIC_GUARD); }
OPPONENT(SPECIES_TENTACOOL) { Ability(ABILITY_LIQUID_OOZE); }
} WHEN {
TURN { MOVE(player, MOVE_LEECH_SEED); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_LEECH_SEED, player);
HP_BAR(opponent);
NOT HP_BAR(player);
}
}

View File

@ -38,3 +38,26 @@ SINGLE_BATTLE_TEST("Hazards are applied based on order of set up")
EXPECT_EQ(gBattleStruct->hazardsQueue[0][5], HAZARDS_NONE);
}
}
SINGLE_BATTLE_TEST("Hazards are applied correctly after a battler faints")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_FINAL_GAMBIT) == EFFECT_FINAL_GAMBIT);
PLAYER(SPECIES_WYNAUT);
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_STEALTH_ROCK);
MOVE(player, MOVE_FINAL_GAMBIT);
SEND_OUT(player, 1);
SEND_OUT(player, 2); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FINAL_GAMBIT, player);
MESSAGE("Wynaut fainted!");
MESSAGE("Pointed stones dug into Wobbuffet!");
MESSAGE("Wobbuffet fainted!");
MESSAGE("Pointed stones dug into Wynaut!");
}
}