master to upcoming merge

This commit is contained in:
AlexOn1ine 2025-11-22 10:39:52 +01:00
commit aa20b2bc0f
21 changed files with 441 additions and 139 deletions

View File

@ -1892,8 +1892,8 @@
1:
.endm
.macro jumpifabilitycantbesuppressed battler:req, jumpInstr:req
callnative BS_JumpIfAbilityCantBeSuppressed
.macro jumpifabilitycantbereactivated battler:req, jumpInstr:req
callnative BS_JumpIfAbilityCantBeReactivated
.byte \battler
.4byte \jumpInstr
.endm

View File

@ -11933,7 +11933,6 @@ gBattleAnimMove_PrismaticLaser::
loadspritegfx ANIM_TAG_CIRCLE_OF_LIGHT @charge animation
loadspritegfx ANIM_TAG_TEAL_ALERT @straight lines
loadspritegfx ANIM_TAG_GREEN_SPIKE @needle arm animation
loadspritegfx ANIM_TAG_NEEDLE @sting
monbg ANIM_ATTACKER
setalpha 14, 8
createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_BG, 1, 0, 16, RGB_BLACK
@ -11962,6 +11961,7 @@ gBattleAnimMove_PrismaticLaser::
unloadspritegfx ANIM_TAG_GREEN_SPIKE
unloadspritegfx ANIM_TAG_ICE_CHUNK
unloadspritegfx ANIM_TAG_CIRCLE_OF_LIGHT
loadspritegfx ANIM_TAG_NEEDLE @sting
delay 30
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 10, 0x32
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 10, 0x32
@ -15060,6 +15060,7 @@ gBattleAnimMove_Poltergeist::
waitbgfadein
clearmonbg 0x3
blendoff
unloadspritegfx ANIM_TAG_ITEM_BAG
end
@Credits to Skeli
@ -32857,7 +32858,6 @@ gBattleAnimMove_SavageSpinOut::
blendoff
waitforvisualfinish
unloadspritegfx ANIM_TAG_STRING
unloadspritegfx ANIM_TAG_CIRCLE_OF_LIGHT
loadspritegfx ANIM_TAG_COCOON
loadspritegfx ANIM_TAG_IMPACT @hit
delay 1
@ -33327,7 +33327,6 @@ FinishInfernoOverdrive:
delay 16
createvisualtask AnimTask_ShakeMon2, 2, ANIM_TARGET, 8, 0, 16, 1
playsewithpan SE_M_MEGA_KICK2, SOUND_PAN_TARGET
unloadspritegfx ANIM_TAG_CIRCLE_OF_LIGHT
createvisualtask AnimTask_ShakeMon, 5, ANIM_TARGET, 0, 2, 79, 1
call InfernoOverdriveExplosion
delay 6
@ -34507,7 +34506,6 @@ gBattleAnimMove_BlackHoleEclipse::
unloadspritegfx ANIM_TAG_VERTICAL_HEX @red
unloadspritegfx ANIM_TAG_SHADOW_BALL
unloadspritegfx ANIM_TAG_BLACK_BALL_2
unloadspritegfx ANIM_TAG_FOCUS_ENERGY
loadspritegfx ANIM_TAG_EXPLOSION_2
call BlackHoleEclipseExplosion
createvisualtask AnimTask_BlendBattleAnimPal, 10, (F_PAL_BG | F_PAL_BATTLERS_2), 1, 0, 16, RGB_WHITE @ bg to white pal
@ -36739,8 +36737,6 @@ gBattleAnimMove_ClangorousSoulblaze::
playsewithpan SE_SHINY, SOUND_PAN_ATTACKER
createsprite gClangorousSoulRedRingTemplate, ANIM_ATTACKER, 3, 0x0, 0x0, 0x0, 0x0
waitforvisualfinish
unloadspritegfx ANIM_TAG_HORSESHOE_SIDE_FIST
unloadspritegfx ANIM_TAG_SPARKLE_2 @stars
loadspritegfx ANIM_TAG_ROUND_SHADOW @ fly
playsewithpan SE_M_FLY, SOUND_PAN_ATTACKER
createsprite gClangoorousSoulblazeWhiteFlySpriteTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0xd, 0x150
@ -37218,8 +37214,6 @@ SearingSunrazeSmashImpact:
loadspritegfx ANIM_TAG_CROSS_IMPACT @x
delay 0
unloadspritegfx ANIM_TAG_METEOR @superpower
unloadspritegfx ANIM_TAG_DRAGON_ASCENT @dragon ascent 1
unloadspritegfx ANIM_TAG_DRAGON_ASCENT_FOE @dragon ascent 2
createsprite gSearingSunrazeSmashCrossImpactSpriteTemplate, ANIM_TARGET, 2, 0x0, 0x0, 0x1, 0x24
playsewithpan SE_M_LEER, SOUND_PAN_TARGET
visible ANIM_ATTACKER
@ -37781,7 +37775,6 @@ gBattleAnimMove_SoulStealing7StarStrike::
call SoulStealingSevenStarStrikeBlueParalysis
waitforvisualfinish
visible ANIM_ATTACKER
unloadspritegfx ANIM_TAG_ROUND_SHADOW
loadspritegfx ANIM_TAG_SPARKLE_4 @ detect
loadspritegfx ANIM_TAG_EXPLOSION @ explosion
playsewithpan SE_M_DETECT, SOUND_PAN_ATTACKER

View File

@ -288,10 +288,10 @@ BattleScript_MoveSwitch:
waitmessage B_WAIT_TIME_SHORT
BattleScript_MoveSwitchOpenPartyScreen::
openpartyscreen BS_ATTACKER, BattleScript_MoveSwitchEnd
switchoutabilities BS_ATTACKER
waitstate
switchhandleorder BS_ATTACKER, 2
returntoball BS_ATTACKER, FALSE
switchoutabilities BS_ATTACKER
switchhandleorder BS_ATTACKER, 2
getswitchedmondata BS_ATTACKER
switchindataupdate BS_ATTACKER
hpthresholds BS_ATTACKER
@ -3570,10 +3570,10 @@ BattleScript_EffectBatonPass::
attackanimation
waitanimation
openpartyscreen BS_ATTACKER, BattleScript_ButItFailed
switchoutabilities BS_ATTACKER
waitstate
switchhandleorder BS_ATTACKER, 2
returntoball BS_ATTACKER, FALSE
switchoutabilities BS_ATTACKER
switchhandleorder BS_ATTACKER, 2
getswitchedmondata BS_ATTACKER
switchindataupdate BS_ATTACKER
hpthresholds BS_ATTACKER
@ -4721,11 +4721,11 @@ BattleScript_ActionSwitch::
end2
BattleScript_DoSwitchOut::
switchoutabilities BS_ATTACKER
undodynamax BS_ATTACKER
waitstate
returnatktoball
waitstate
switchoutabilities BS_ATTACKER
drawpartystatussummary BS_ATTACKER
switchhandleorder BS_ATTACKER, 1
getswitchedmondata BS_ATTACKER
@ -5026,9 +5026,9 @@ BattleScript_RoarSuccessRet:
attackanimation
waitanimation
BattleScript_RoarSuccessRet_Ret:
switchoutabilities BS_TARGET
returntoball BS_TARGET, FALSE
waitstate
switchoutabilities BS_TARGET
return
BattleScript_WeaknessPolicy::
@ -6482,10 +6482,10 @@ BattleScript_EmergencyExit::
playanimation BS_SCRIPTING, B_ANIM_SLIDE_OFFSCREEN
waitanimation
openpartyscreen BS_SCRIPTING, BattleScript_EmergencyExitRet
switchoutabilities BS_SCRIPTING
waitstate
switchhandleorder BS_SCRIPTING, 2
returntoball BS_SCRIPTING, FALSE
switchoutabilities BS_SCRIPTING
switchhandleorder BS_SCRIPTING, 2
getswitchedmondata BS_SCRIPTING
switchindataupdate BS_SCRIPTING
hpthresholds BS_SCRIPTING
@ -6513,10 +6513,10 @@ BattleScript_EmergencyExitEnd2::
playanimation BS_ATTACKER, B_ANIM_SLIDE_OFFSCREEN
waitanimation
openpartyscreen BS_ATTACKER, BattleScript_EmergencyExitRetEnd2
switchoutabilities BS_ATTACKER
waitstate
switchhandleorder BS_ATTACKER, 2
returntoball BS_ATTACKER, FALSE
switchoutabilities BS_ATTACKER
switchhandleorder BS_ATTACKER, 2
getswitchedmondata BS_ATTACKER
switchindataupdate BS_ATTACKER
hpthresholds BS_ATTACKER
@ -8298,12 +8298,12 @@ BattleScript_EjectButtonActivates::
undodynamax BS_SCRIPTING
makeinvisible BS_SCRIPTING
openpartyscreen BS_SCRIPTING, BattleScript_EjectButtonEnd
waitstate
returntoball BS_SCRIPTING, FALSE
copybyte sSAVED_BATTLER, sBATTLER
switchoutabilities BS_SCRIPTING
copybyte sBATTLER, sSAVED_BATTLER
waitstate
switchhandleorder BS_SCRIPTING, 0x2
returntoball BS_SCRIPTING, FALSE
getswitchedmondata BS_SCRIPTING
switchindataupdate BS_SCRIPTING
hpthresholds BS_SCRIPTING
@ -8395,8 +8395,7 @@ BattleScript_NeutralizingGasExits::
setbyte gBattlerAttacker, 0
BattleScript_NeutralizingGasExitsLoop:
copyarraywithindex gBattlerTarget, gBattlerByTurnOrder, gBattlerAttacker, 1
jumpifabilitycantbesuppressed BS_TARGET, BattleScript_NeutralizingGasExitsLoopIncrement
jumpifability BS_TARGET, ABILITY_IMPOSTER, BattleScript_NeutralizingGasExitsLoopIncrement @ Imposter only activates when first entering the field
jumpifabilitycantbereactivated BS_TARGET, BattleScript_NeutralizingGasExitsLoopIncrement
saveattacker
switchinabilities BS_TARGET
restoreattacker
@ -8432,13 +8431,15 @@ BattleScript_TargetAbilityStatRaiseRet_End:
BattleScript_EffectMaxMove::
attackcanceler
accuracycheck BattleScript_ButItFailed, NO_ACC_CALC_CHECK_LOCK_ON
goto BattleScript_HitFromAtkString
goto BattleScript_HitFromCritCalc
BattleScript_EffectRaiseStatAllies::
savetarget
copybyte gBattlerTarget, gBattlerAttacker
copybyte sSAVED_STAT_CHANGER, sSTATCHANGER
BattleScript_RaiseSideStatsLoop:
jumpifabsent BS_TARGET, BattleScript_RaiseSideStatsIncrement
copybyte sSTATCHANGER, sSAVED_STAT_CHANGER
statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_RaiseSideStatsIncrement
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_CHANGE, BattleScript_RaiseSideStatsIncrement
printfromtable gStatUpStringIds
@ -8453,8 +8454,10 @@ BattleScript_RaiseSideStatsEnd:
BattleScript_EffectLowerStatFoes::
savetarget
copybyte sBATTLER, gBattlerTarget
copybyte sSAVED_STAT_CHANGER, sSTATCHANGER
BattleScript_LowerSideStatsLoop:
jumpifabsent BS_TARGET, BattleScript_LowerSideStatsIncrement
copybyte sSTATCHANGER, sSAVED_STAT_CHANGER
statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_LowerSideStatsIncrement
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_CHANGE, BattleScript_LowerSideStatsIncrement
printfromtable gStatDownStringIds

View File

@ -1186,6 +1186,8 @@ struct MapPosition
#if T_SHOULD_RUN_MOVE_ANIM
extern bool32 gLoadFail;
extern bool32 gCountAllocs;
extern s32 gSpriteAllocs;
#endif // T_SHOULD_RUN_MOVE_ANIM
#endif // GUARD_GLOBAL_H

View File

@ -2,7 +2,11 @@
#define GUARD_TEST_RUNNER_H
extern const bool8 gTestRunnerEnabled;
#if TESTING
extern const bool8 gTestRunnerHeadless;
#else
#define gTestRunnerHeadless FALSE
#endif
extern const bool8 gTestRunnerSkipIsFail;
#if TESTING

View File

@ -14,6 +14,7 @@
#include "sound.h"
#include "sprite.h"
#include "task.h"
#include "test_runner.h"
#include "trig.h"
#include "util.h"
#include "data.h"
@ -2435,7 +2436,7 @@ void TryShinyAnimation(u8 battler, struct Pokemon *mon)
if (illusionMon != NULL)
mon = illusionMon;
if (IsBattlerSpriteVisible(battler) && IsValidForBattle(mon))
if (IsBattlerSpriteVisible(battler) && IsValidForBattle(mon) && !gTestRunnerHeadless)
{
if (isShiny)
{
@ -2768,4 +2769,3 @@ static void CB_CriticalCaptureThrownBallMovement(struct Sprite *sprite)
sprite->callback = SpriteCB_Ball_Bounce_Step;
}
}

View File

@ -2607,7 +2607,7 @@ void BtlController_HandleStatusAnimation(u32 battler)
void BtlController_HandleHitAnimation(u32 battler)
{
if (gSprites[gBattlerSpriteIds[battler]].invisible == TRUE)
if (gSprites[gBattlerSpriteIds[battler]].invisible == TRUE || gTestRunnerHeadless)
{
BtlController_Complete(battler);
}
@ -2622,6 +2622,11 @@ void BtlController_HandleHitAnimation(u32 battler)
void BtlController_HandlePlaySE(u32 battler)
{
if (gTestRunnerHeadless)
{
BtlController_Complete(battler);
return;
}
s32 pan = IsOnPlayerSide(battler) ? SOUND_PAN_ATTACKER : SOUND_PAN_TARGET;
PlaySE12WithPanning(gBattleResources->bufferA[battler][1] | (gBattleResources->bufferA[battler][2] << 8), pan);
@ -2630,6 +2635,11 @@ void BtlController_HandlePlaySE(u32 battler)
void BtlController_HandlePlayFanfareOrBGM(u32 battler)
{
if (gTestRunnerHeadless)
{
BtlController_Complete(battler);
return;
}
if (gBattleResources->bufferA[battler][3])
{
BattleStopLowHpSound();
@ -2896,7 +2906,7 @@ void AnimateMonAfterPokeBallFail(u32 battler)
{
if (B_ANIMATE_MON_AFTER_FAILED_POKEBALL == FALSE)
return;
LaunchKOAnimation(battler, ReturnAnimIdForBattler(TRUE, battler), TRUE);
TryShinyAnimation(gBattlerTarget, GetBattlerMon(gBattlerTarget));
}

View File

@ -3861,9 +3861,7 @@ static void TryDoEventsBeforeFirstTurn(void)
while (gBattleStruct->switchInBattlerCounter < gBattlersCount) // From fastest to slowest
{
i = gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++];
if (AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS_FIRST_TURN, i, gBattleMons[i].ability, 0, 0) != 0)
return;
if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES, i, 0, 0, 0) != 0)
if (AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS_FIRST_TURN, i, 0, 0, 0) != 0)
return;
}
gBattleStruct->switchInBattlerCounter = 0;
@ -3880,6 +3878,8 @@ static void TryDoEventsBeforeFirstTurn(void)
return;
if (TryClearIllusion(battler, ABILITYEFFECT_ON_SWITCHIN))
return;
if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN_IMMUNITIES, battler, 0, 0, 0) != 0)
return;
}
gBattleStruct->switchInBattlerCounter = 0;
gBattleStruct->eventState.beforeFristTurn++;
@ -4342,7 +4342,7 @@ static void HandleTurnActionSelectionState(void)
case B_ACTION_SWITCH:
gBattleStruct->battlerPartyIndexes[battler] = gBattlerPartyIndexes[battler];
if (gBattleTypeFlags & BATTLE_TYPE_ARENA
|| !CanBattlerEscape(battler))
|| (!CanBattlerEscape(battler) && GetBattlerHoldEffect(battler) != HOLD_EFFECT_SHED_SHELL))
{
BtlController_EmitChoosePokemon(battler, B_COMM_TO_CONTROLLER, PARTY_ACTION_CANT_SWITCH, PARTY_SIZE, ABILITY_NONE, 0, gBattleStruct->battlerPartyOrders[battler]);
}

View File

@ -1355,11 +1355,31 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u
if (move == NO_ACC_CALC_CHECK_LOCK_ON)
{
if (gBattleMons[gBattlerTarget].volatiles.lockOn && gDisableStructs[gBattlerTarget].battlerWithSureHit == gBattlerAttacker)
{
gBattlescriptCurrInstr = nextInstr;
}
else if (IsSemiInvulnerable(gBattlerTarget, CHECK_ALL))
{
if (gBattlerTarget != BATTLE_PARTNER(gBattlerAttacker))
{
gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED;
gBattleStruct->missStringId[gBattlerTarget] = gBattleCommunication[MISS_TYPE] = B_MSG_AVOIDED_ATK;
}
gBattlescriptCurrInstr = failInstr;
else if (!JumpIfMoveAffectedByProtect(gCurrentMove, gBattlerTarget, TRUE, failInstr))
}
else if (IsBattlerProtected(gBattlerAttacker, gBattlerTarget, gCurrentMove))
{
gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED;
gBattleStruct->missStringId[gBattlerTarget] = gBattleCommunication[MISS_TYPE] = B_MSG_PROTECTED;
gLastLandedMoves[gBattlerTarget] = 0;
gLastHitByType[gBattlerTarget] = 0;
gBattlescriptCurrInstr = failInstr;
}
else
{
gBattlescriptCurrInstr = nextInstr;
}
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX)
{
if (gProtectStructs[gBattlerTarget].protected == PROTECT_MAX_GUARD)
@ -2168,6 +2188,10 @@ static void Cmd_attackanimation(void)
gBattleMons[gBattlerAttacker].friendship,
&gDisableStructs[gBattlerAttacker],
multihit);
#if T_SHOULD_RUN_MOVE_ANIM
gCountAllocs = TRUE;
gSpriteAllocs = 0;
#endif
gBattleScripting.animTurn++;
gBattleScripting.animTargetsHit++;
MarkBattlerForControllerExec(gBattlerAttacker);
@ -2186,7 +2210,12 @@ static void Cmd_waitanimation(void)
CMD_ARGS();
if (gBattleControllerExecFlags == 0 && gBattleStruct->battlerKOAnimsRunning == 0)
{
#if T_SHOULD_RUN_MOVE_ANIM
gCountAllocs = FALSE;
#endif
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
static void DoublesHPBarReduction(void)
@ -4197,14 +4226,17 @@ static void Cmd_tryfaintmon(void)
}
else
{
if (gBattleMons[battler].ability == ABILITY_NEUTRALIZING_GAS
if (gDisableStructs[battler].neutralizingGas
&& !(gAbsentBattlerFlags & (1u << battler))
&& !IsBattlerAlive(battler))
{
gBattleMons[battler].ability = ABILITY_NONE;
BattleScriptPush(gBattlescriptCurrInstr);
gBattlescriptCurrInstr = BattleScript_NeutralizingGasExits;
return;
gDisableStructs[battler].neutralizingGas = FALSE;
if (!IsNeutralizingGasOnField())
{
BattleScriptPush(gBattlescriptCurrInstr);
gBattlescriptCurrInstr = BattleScript_NeutralizingGasExits;
return;
}
}
if (cmd->battler == BS_ATTACKER)
@ -7466,7 +7498,7 @@ static void Cmd_jumpifcantswitch(void)
CMD_ARGS(u8 battler:7, u8 ignoreEscapePrevention:1, const u8 *jumpInstr);
u32 battler = GetBattlerForBattleScript(cmd->battler);
if (!cmd->ignoreEscapePrevention && !CanBattlerEscape(battler))
if (!cmd->ignoreEscapePrevention && !CanBattlerEscape(battler) && GetBattlerHoldEffect(battler) != HOLD_EFFECT_SHED_SHELL)
{
gBattlescriptCurrInstr = cmd->jumpInstr;
}
@ -13159,46 +13191,48 @@ static void Cmd_switchoutabilities(void)
CMD_ARGS(u8 battler);
u32 battler = GetBattlerForBattleScript(cmd->battler);
if (gBattleMons[battler].ability == ABILITY_NEUTRALIZING_GAS)
if (gDisableStructs[battler].neutralizingGas)
{
gBattleMons[battler].ability = ABILITY_NONE;
BattleScriptPush(gBattlescriptCurrInstr);
gBattlescriptCurrInstr = BattleScript_NeutralizingGasExits;
gDisableStructs[battler].neutralizingGas = FALSE;
if (!IsNeutralizingGasOnField())
{
BattleScriptPush(gBattlescriptCurrInstr);
gBattlescriptCurrInstr = BattleScript_NeutralizingGasExits;
return;
}
}
else
switch (GetBattlerAbility(battler))
{
switch (GetBattlerAbility(battler))
{
case ABILITY_NATURAL_CURE:
if (gBattleMons[battler].status1 & STATUS1_SLEEP)
TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]);
case ABILITY_NATURAL_CURE:
if (gBattleMons[battler].status1 & STATUS1_SLEEP)
TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]);
gBattleMons[battler].status1 = 0;
BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE,
1u << gBattleStruct->battlerPartyIndexes[battler],
sizeof(gBattleMons[battler].status1),
&gBattleMons[battler].status1);
MarkBattlerForControllerExec(battler);
break;
case ABILITY_REGENERATOR:
{
u32 regenerate = GetNonDynamaxMaxHP(battler) / 3;
regenerate += gBattleMons[battler].hp;
if (regenerate > gBattleMons[battler].maxHP)
regenerate = gBattleMons[battler].maxHP;
BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_HP_BATTLE,
1u << gBattleStruct->battlerPartyIndexes[battler],
sizeof(regenerate),
&regenerate);
MarkBattlerForControllerExec(battler);
break;
default:
break;
}
}
gBattlescriptCurrInstr = cmd->nextInstr;
gBattleMons[battler].status1 = 0;
BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE,
1u << gBattleStruct->battlerPartyIndexes[battler],
sizeof(gBattleMons[battler].status1),
&gBattleMons[battler].status1);
MarkBattlerForControllerExec(battler);
break;
case ABILITY_REGENERATOR:
{
u32 regenerate = GetNonDynamaxMaxHP(battler) / 3;
regenerate += gBattleMons[battler].hp;
if (regenerate > gBattleMons[battler].maxHP)
regenerate = gBattleMons[battler].maxHP;
BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_HP_BATTLE,
1u << gBattleStruct->battlerPartyIndexes[battler],
sizeof(regenerate),
&regenerate);
MarkBattlerForControllerExec(battler);
break;
}
default:
break;
}
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_jumpifhasnohp(void)
@ -16486,15 +16520,27 @@ void BS_TryBoosterEnergy(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_JumpIfAbilityCantBeSuppressed(void)
void BS_JumpIfAbilityCantBeReactivated(void)
{
NATIVE_ARGS(u8 battler, const u8 *jumpInstr);
u32 battler = GetBattlerForBattleScript(cmd->battler);
u32 ability = gBattleMons[battler].ability;
if (gAbilitiesInfo[gBattleMons[battler].ability].cantBeSuppressed)
switch (ability)
{
case ABILITY_IMPOSTER:
case ABILITY_NEUTRALIZING_GAS:
case ABILITY_AIR_LOCK:
case ABILITY_CLOUD_NINE:
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
break;
default:
if (gAbilitiesInfo[ability].cantBeSuppressed)
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
break;
}
}
void BS_TryActivateAbilityShield(void)
@ -17839,13 +17885,16 @@ void BS_TryEndNeutralizingGas(void)
if (gSpecialStatuses[gBattlerTarget].neutralizingGasRemoved)
{
gSpecialStatuses[gBattlerTarget].neutralizingGasRemoved = FALSE;
BattleScriptPush(cmd->nextInstr);
gBattlescriptCurrInstr = BattleScript_NeutralizingGasExits;
}
else
{
gBattlescriptCurrInstr = cmd->nextInstr;
gDisableStructs[gBattlerTarget].neutralizingGas = FALSE;
if (!IsNeutralizingGasOnField())
{
BattleScriptPush(cmd->nextInstr);
gBattlescriptCurrInstr = BattleScript_NeutralizingGasExits;
return;
}
}
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_GetRototillerTargets(void)

View File

@ -776,7 +776,9 @@ void HandleAction_Run(void)
}
else
{
if (!CanBattlerEscape(gBattlerAttacker))
if (GetBattlerHoldEffect(gBattlerAttacker) != HOLD_EFFECT_CAN_ALWAYS_RUN
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_RUN_AWAY
&& !CanBattlerEscape(gBattlerAttacker))
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ATTACKER_CANT_ESCAPE;
gBattlescriptCurrInstr = BattleScript_PrintFailedToRunString;
@ -5479,7 +5481,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
case ABILITYEFFECT_NEUTRALIZINGGAS:
case ABILITYEFFECT_NEUTRALIZINGGAS_FIRST_TURN:
// Prints message only. separate from ABILITYEFFECT_ON_SWITCHIN bc activates before entry hazards
if (ability == ABILITY_NEUTRALIZING_GAS && !gDisableStructs[battler].neutralizingGas)
if (gBattleMons[battler].ability == ABILITY_NEUTRALIZING_GAS && !gDisableStructs[battler].neutralizingGas)
{
gDisableStructs[battler].neutralizingGas = TRUE;
gBattlerAbility = battler;
@ -5594,7 +5596,7 @@ bool32 IsNeutralizingGasOnField(void)
for (i = 0; i < gBattlersCount; i++)
{
if (IsBattlerAlive(i) && gBattleMons[i].ability == ABILITY_NEUTRALIZING_GAS && !gBattleMons[i].volatiles.gastroAcid)
if (gDisableStructs[i].neutralizingGas && !gBattleMons[i].volatiles.gastroAcid)
return TRUE;
}
@ -5670,7 +5672,7 @@ u32 GetBattlerAbilityInternal(u32 battler, u32 ignoreMoldBreaker, u32 noAbilityS
if (!hasAbilityShield
&& IsNeutralizingGasOnField()
&& gBattleMons[battler].ability != ABILITY_NEUTRALIZING_GAS)
&& !gDisableStructs[battler].neutralizingGas)
return ABILITY_NONE;
if (CanBreakThroughAbility(gBattlerAttacker, battler, gBattleMons[gBattlerAttacker].ability, hasAbilityShield, ignoreMoldBreaker))
@ -5750,8 +5752,6 @@ bool32 CanBattlerEscape(u32 battler) // no ability check
{
if (gBattleStruct->battlerState[battler].commanderSpecies != SPECIES_NONE)
return FALSE;
else if (GetBattlerHoldEffect(battler) == HOLD_EFFECT_SHED_SHELL)
return TRUE;
else if (B_GHOSTS_ESCAPE >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_GHOST))
return TRUE;
else if (gBattleMons[battler].volatiles.escapePrevention)

View File

@ -1269,8 +1269,7 @@ bool32 CannotUseItemsInBattle(u16 itemId, struct Pokemon *mon)
switch (battleUsage)
{
case EFFECT_ITEM_INCREASE_STAT:
u32 ability = GetBattlerAbility(gBattlerInMenuId);
if (CompareStat(gBattlerInMenuId, GetItemEffect(itemId)[1], MAX_STAT_STAGE, CMP_EQUAL, ability))
if (CompareStat(gBattlerInMenuId, GetItemEffect(itemId)[1], MAX_STAT_STAGE, CMP_EQUAL, GetBattlerAbility(gBattlerInMenuId)))
cannotUse = TRUE;
break;
case EFFECT_ITEM_SET_FOCUS_ENERGY:

View File

@ -6372,6 +6372,7 @@ static void DeleteInvalidFusionMoves(struct Pokemon *mon, u32 species)
}
}
#if P_FUSION_FORMS
static void SwapFusionMonMoves(struct Pokemon *mon, const u16 moveTable[][2], u32 mode)
{
u32 oldMoveIndex, newMoveIndex;
@ -6400,6 +6401,8 @@ static void SwapFusionMonMoves(struct Pokemon *mon, const u16 moveTable[][2], u3
}
}
#endif //P_FUSION_FORMS
static void Task_TryItemUseFusionChange(u8 taskId)
{
struct Pokemon *mon = &gPlayerParty[gTasks[taskId].firstFusionSlot];
@ -6493,6 +6496,7 @@ static void Task_TryItemUseFusionChange(u8 taskId)
{
if (gTasks[taskId].fusionType == FUSE_MON)
{
#if P_FUSION_FORMS
#if P_FAMILY_KYUREM
#if P_FAMILY_RESHIRAM
if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_WHITE)
@ -6503,11 +6507,13 @@ static void Task_TryItemUseFusionChange(u8 taskId)
SwapFusionMonMoves(mon, gKyuremBlackSwapMoveTable, FUSE_MON);
#endif //P_FAMILY_ZEKROM
#endif //P_FAMILY_KYUREM
#endif //P_FUSION_FORMS
if (gTasks[taskId].moveToLearn != 0)
FormChangeTeachMove(taskId, gTasks[taskId].moveToLearn, gTasks[taskId].firstFusionSlot);
}
else //(gTasks[taskId].fusionType == UNFUSE_MON)
{
#if P_FUSION_FORMS
#if P_FAMILY_KYUREM
#if P_FAMILY_RESHIRAM
if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_WHITE)
@ -6518,6 +6524,7 @@ static void Task_TryItemUseFusionChange(u8 taskId)
SwapFusionMonMoves(mon, gKyuremBlackSwapMoveTable, UNFUSE_MON);
#endif //P_FAMILY_ZEKROM
#endif //P_FAMILY_KYUREM
#endif //P_FUSION_FORMS
if ( gTasks[taskId].tExtraMoveHandling == FORGET_EXTRA_MOVES)
{
DeleteInvalidFusionMoves(mon, gTasks[taskId].fusionResult);

View File

@ -11,6 +11,7 @@
#include "sprite.h"
#include "task.h"
#include "trig.h"
#include "test_runner.h"
#include "util.h"
#include "data.h"
#include "item.h"
@ -597,8 +598,8 @@ static void Task_DoPokeballSendOutAnim(u8 taskId)
{
u32 throwCaseId, ballId, battler, ballSpriteId;
bool32 notSendOut = FALSE;
u32 throwXoffset = (B_ENEMY_THROW_BALLS >= GEN_6) ? 24 : 0;
s32 throwYoffset = (B_ENEMY_THROW_BALLS >= GEN_6) ? -16 : 24;
u32 throwXoffset = (B_ENEMY_THROW_BALLS >= GEN_6 && !gTestRunnerHeadless) ? 24 : 0;
s32 throwYoffset = (B_ENEMY_THROW_BALLS >= GEN_6 && !gTestRunnerHeadless) ? -16 : 24;
if (gTasks[taskId].tFrames == 0)
{
@ -675,7 +676,7 @@ static inline void DoPokeballSendOutSoundEffect(u32 battler)
static inline void *GetOpponentMonSendOutCallback(void)
{
return (B_ENEMY_THROW_BALLS >= GEN_6) ? SpriteCB_MonSendOut_1 : SpriteCB_OpponentMonSendOut;
return (B_ENEMY_THROW_BALLS >= GEN_6 && !gTestRunnerHeadless) ? SpriteCB_MonSendOut_1 : SpriteCB_OpponentMonSendOut;
}
// This sequence of functions is very similar to those that get run when
@ -1205,7 +1206,7 @@ static void SpriteCB_MonSendOut_2(struct Sprite *sprite)
u32 r7;
bool32 rightPosition = (IsBattlerPlayer(sprite->sBattler)) ? B_POSITION_PLAYER_RIGHT : B_POSITION_OPPONENT_RIGHT;
if (HIBYTE(sprite->data[7]) >= 35 && HIBYTE(sprite->data[7]) < 80)
if (HIBYTE(sprite->data[7]) >= 35 && HIBYTE(sprite->data[7]) < 80 && !gTestRunnerHeadless)
{
s16 r4;
@ -1246,7 +1247,8 @@ static void SpriteCB_MonSendOut_2(struct Sprite *sprite)
sprite->data[0] = 0;
if (IsDoubleBattle() && gBattleSpritesDataPtr->animationData->introAnimActive
&& sprite->sBattler == GetBattlerAtPosition(rightPosition))
&& sprite->sBattler == GetBattlerAtPosition(rightPosition)
&& !gTestRunnerHeadless)
sprite->callback = SpriteCB_ReleaseMon2FromBall;
else
sprite->callback = SpriteCB_ReleaseMonFromBall;
@ -1269,12 +1271,15 @@ static void SpriteCB_ReleaseMon2FromBall(struct Sprite *sprite)
static void SpriteCB_OpponentMonSendOut(struct Sprite *sprite)
{
if (gTestRunnerHeadless)
sprite->data[0] = 15;
sprite->data[0]++;
if (sprite->data[0] > 15)
{
sprite->data[0] = 0;
if (IsDoubleBattle() && gBattleSpritesDataPtr->animationData->introAnimActive
&& sprite->sBattler == GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT))
&& sprite->sBattler == GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT)
&& !gTestRunnerHeadless)
sprite->callback = SpriteCB_ReleaseMon2FromBall;
else
sprite->callback = SpriteCB_ReleaseMonFromBall;
@ -1534,7 +1539,7 @@ void StartHealthboxSlideIn(u8 battler)
healthboxSprite->y2 = -healthboxSprite->y2;
}
gSprites[healthboxSprite->data[5]].callback(&gSprites[healthboxSprite->data[5]]);
if (GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT)
if (GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT && !gTestRunnerHeadless)
healthboxSprite->callback = SpriteCB_HealthboxSlideInDelayed;
}

View File

@ -2437,7 +2437,7 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data)
data[retVal++] = substruct0->nickname12;
}
}
else if (POKEMON_NAME_LENGTH >= 11)
else if (field != MON_DATA_NICKNAME10 && POKEMON_NAME_LENGTH >= 11)
{
if (substruct0->nickname11 == 0)
{

View File

@ -28,6 +28,8 @@
#if T_SHOULD_RUN_MOVE_ANIM
EWRAM_DATA bool32 gLoadFail = FALSE;
EWRAM_DATA bool32 gCountAllocs = FALSE;
EWRAM_DATA s32 gSpriteAllocs = 0;
#endif // T_SHOULD_RUN_MOVE_ANIM
struct SpriteCopyRequest
@ -1501,6 +1503,10 @@ void LoadSpriteSheets(const struct SpriteSheet *sheets)
void FreeSpriteTilesByTag(u16 tag)
{
#if T_SHOULD_RUN_MOVE_ANIM
if (gCountAllocs)
gSpriteAllocs--;
#endif
u8 index = IndexOfSpriteTileTag(tag);
if (index != 0xFF)
{
@ -1566,6 +1572,10 @@ u16 GetSpriteTileTagByTileStart(u16 start)
void AllocSpriteTileRange(u16 tag, u16 start, u16 count)
{
#if T_SHOULD_RUN_MOVE_ANIM
if (gCountAllocs)
gSpriteAllocs++;
#endif
u8 freeIndex = IndexOfSpriteTileTag(TAG_NONE);
sSpriteTileRangeTags[freeIndex] = tag;
SET_SPRITE_TILE_RANGE(freeIndex, start, count);

View File

@ -7,5 +7,7 @@ const bool8 gTestRunnerEnabled = FALSE;
// The Makefile patches gTestRunnerHeadless as part of make test.
// This allows us to open the ROM in an mgba with a UI and see the
// animations and messages play, which helps when debugging a test.
#if TESTING
const bool8 gTestRunnerHeadless = FALSE;
#endif
const bool8 gTestRunnerSkipIsFail = FALSE;

View File

@ -310,3 +310,46 @@ SINGLE_BATTLE_TEST("Neutralizing Gas exiting the field does not activate Imposte
NOT ABILITY_POPUP(player, ABILITY_IMPOSTER);
}
}
SINGLE_BATTLE_TEST("Neutralizing Gas exiting the field does not activate Air Lock/Cloud Nine but their effects are kept")
{
u32 species, ability;
PARAMETRIZE { species = SPECIES_GOLDUCK; ability = ABILITY_CLOUD_NINE; }
PARAMETRIZE { species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_RAIN_DANCE) == EFFECT_RAIN_DANCE);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(species) { Ability(ability); }
OPPONENT(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); }
OPPONENT(SPECIES_LUDICOLO) { Ability(ABILITY_RAIN_DISH); }
} WHEN {
TURN { SWITCH(player, 1); SWITCH(opponent, 1); }
TURN { MOVE(player, MOVE_RAIN_DANCE); }
} SCENE {
NOT ABILITY_POPUP(player, ABILITY_AIR_LOCK);
MESSAGE("The effects of the neutralizing gas wore off!");
NOT ABILITY_POPUP(player, ABILITY_AIR_LOCK);
ANIMATION(ANIM_TYPE_MOVE, MOVE_RAIN_DANCE, player);
NOT ABILITY_POPUP(opponent, ABILITY_RAIN_DISH);
}
}
SINGLE_BATTLE_TEST("Neutralizing Gas only displays exiting message for the last user leaving the field")
{
GIVEN {
PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { SWITCH(player, 1); SWITCH(opponent, 1); }
} SCENE {
ABILITY_POPUP(player, ABILITY_NEUTRALIZING_GAS);
ABILITY_POPUP(opponent, ABILITY_NEUTRALIZING_GAS);
SEND_IN_MESSAGE("Wobbuffet");
MESSAGE("The effects of the neutralizing gas wore off!");
NOT MESSAGE("The effects of the neutralizing gas wore off!");
}
}

View File

@ -9,7 +9,6 @@
AI_DOUBLE_BATTLE_TEST("AI uses Final Gambit")
{
KNOWN_FAILING;
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET);
@ -20,13 +19,12 @@ AI_DOUBLE_BATTLE_TEST("AI uses Final Gambit")
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { EXPECT_MOVE(opponentLeft, MOVE_FINAL_GAMBIT); }
TURN { EXPECT_MOVE(opponentLeft, MOVE_FINAL_GAMBIT); SEND_OUT(playerLeft, 2); }
}
}
AI_DOUBLE_BATTLE_TEST("AI uses Guillotine")
{
KNOWN_FAILING;
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET);
@ -37,13 +35,12 @@ AI_DOUBLE_BATTLE_TEST("AI uses Guillotine")
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { EXPECT_MOVE(opponentLeft, MOVE_GUILLOTINE); }
TURN { EXPECT_MOVE(opponentLeft, MOVE_GUILLOTINE); SEND_OUT(playerLeft, 2); }
}
}
AI_DOUBLE_BATTLE_TEST("AI uses Sheer Cold")
{
KNOWN_FAILING;
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET);
@ -54,7 +51,7 @@ AI_DOUBLE_BATTLE_TEST("AI uses Sheer Cold")
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { EXPECT_MOVE(opponentLeft, MOVE_SHEER_COLD); }
TURN { EXPECT_MOVE(opponentLeft, MOVE_SHEER_COLD); SEND_OUT(playerLeft, 2); }
}
}

View File

@ -1659,5 +1659,85 @@ SINGLE_BATTLE_TEST("Dynamax: Dynamax is reverted before switch out")
}
}
SINGLE_BATTLE_TEST("Dynamax: max move against semi-invulnerable target prints the correct message")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) {Speed(1);};
OPPONENT(SPECIES_WOBBUFFET) {Speed(2);};
} WHEN {
TURN { MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_FLY); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_DYNAMAX_GROWTH, player);
MESSAGE("Wobbuffet used Max Strike!");
MESSAGE("The opposing Wobbuffet avoided the attack!");
}
}
DOUBLE_BATTLE_TEST("Dynamax stat lowering moves don't make stat-changing abilities apply to partner")
{
u32 move, stat, ability;
move = 0; stat = 0; ability = 0;
u32 abilityList[] = {ABILITY_COMPETITIVE, ABILITY_DEFIANT, ABILITY_CONTRARY, ABILITY_SIMPLE};
for (u32 j = 0; j < 4; j++)
{
PARAMETRIZE { move = MOVE_SCRATCH; stat = STAT_SPEED; ability = abilityList[j]; }
PARAMETRIZE { move = MOVE_FURY_CUTTER; stat = STAT_SPATK; ability = abilityList[j]; }
PARAMETRIZE { move = MOVE_LICK; stat = STAT_DEF; ability = abilityList[j]; ;}
PARAMETRIZE { move = MOVE_DRAGON_CLAW; stat = STAT_ATK; ability = abilityList[j]; }
PARAMETRIZE { move = MOVE_CRUNCH; stat = STAT_SPDEF; ability = abilityList[j]; }
}
GIVEN {
ASSUME(MoveHasAdditionalEffect(MOVE_MAX_STRIKE, MOVE_EFFECT_LOWER_SPEED_SIDE));
ASSUME(MoveHasAdditionalEffect(MOVE_MAX_FLUTTERBY, MOVE_EFFECT_LOWER_SP_ATK_SIDE));
ASSUME(MoveHasAdditionalEffect(MOVE_MAX_PHANTASM, MOVE_EFFECT_LOWER_DEFENSE_SIDE));
ASSUME(MoveHasAdditionalEffect(MOVE_MAX_WYRMWIND, MOVE_EFFECT_LOWER_ATTACK_SIDE));
ASSUME(MoveHasAdditionalEffect(MOVE_MAX_DARKNESS, MOVE_EFFECT_LOWER_SP_DEF_SIDE));
PLAYER(SPECIES_WOBBUFFET) { }
PLAYER(SPECIES_WOBBUFFET) { }
OPPONENT(SPECIES_WOBBUFFET) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_SHADOW_TAG); }
} WHEN {
TURN { MOVE(playerLeft, move, target: opponentLeft, gimmick: GIMMICK_DYNAMAX);}
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
} THEN {
EXPECT_EQ(opponentRight->statStages[stat], DEFAULT_STAT_STAGE - 1);
}
}
DOUBLE_BATTLE_TEST("Dynamax stat raising moves don't make stat-changing abilities apply to partner")
{
u32 move, stat, ability;
move = 0; stat = 0; ability = 0;
u32 abilityList[] = {ABILITY_CONTRARY, ABILITY_SIMPLE};
for (u32 j = 0; j < 2; j++)
{
PARAMETRIZE { move = MOVE_PECK; stat = STAT_SPEED; ability = abilityList[j]; }
PARAMETRIZE { move = MOVE_POISON_JAB; stat = STAT_SPATK; ability = abilityList[j]; }
PARAMETRIZE { move = MOVE_BULLET_PUNCH; stat = STAT_DEF; ability = abilityList[j]; ;}
PARAMETRIZE { move = MOVE_DOUBLE_KICK; stat = STAT_ATK; ability = abilityList[j]; }
PARAMETRIZE { move = MOVE_MUD_SLAP; stat = STAT_SPDEF; ability = abilityList[j]; }
}
GIVEN {
ASSUME(MoveHasAdditionalEffect(MOVE_MAX_STRIKE, MOVE_EFFECT_LOWER_SPEED_SIDE));
ASSUME(MoveHasAdditionalEffect(MOVE_MAX_FLUTTERBY, MOVE_EFFECT_LOWER_SP_ATK_SIDE));
ASSUME(MoveHasAdditionalEffect(MOVE_MAX_PHANTASM, MOVE_EFFECT_LOWER_DEFENSE_SIDE));
ASSUME(MoveHasAdditionalEffect(MOVE_MAX_WYRMWIND, MOVE_EFFECT_LOWER_ATTACK_SIDE));
ASSUME(MoveHasAdditionalEffect(MOVE_MAX_DARKNESS, MOVE_EFFECT_LOWER_SP_DEF_SIDE));
PLAYER(SPECIES_WOBBUFFET) { Ability(ability); }
PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_SHADOW_TAG); }
OPPONENT(SPECIES_WOBBUFFET) {}
OPPONENT(SPECIES_WOBBUFFET) {}
} WHEN {
TURN { MOVE(playerLeft, move, target: opponentLeft, gimmick: GIMMICK_DYNAMAX);}
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
} THEN {
EXPECT_EQ(playerRight->statStages[stat], DEFAULT_STAT_STAGE + 1);
}
}
TO_DO_BATTLE_TEST("Dynamax: Contrary inverts stat-lowering Max Moves, without showing a message")
TO_DO_BATTLE_TEST("Dynamax: Contrary inverts stat-increasing Max Moves, without showing a message")

View File

@ -0,0 +1,67 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gItemsInfo[ITEM_SHED_SHELL].holdEffect == HOLD_EFFECT_SHED_SHELL);
};
SINGLE_BATTLE_TEST("Shed Shell allows switching out even when trapped by Mean Look")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_SHED_SHELL); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_GASTLY);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_MEAN_LOOK); }
TURN { SWITCH(player, 1); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MEAN_LOOK, opponent);
SWITCH_OUT_MESSAGE("Wobbuffet");
SEND_IN_MESSAGE("Wynaut");
}
}
SINGLE_BATTLE_TEST("Shed Shell allows switching out even when trapped by Shadow Tag")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_SHED_SHELL); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_SHADOW_TAG); }
} WHEN {
TURN { SWITCH(player, 1); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
SWITCH_OUT_MESSAGE("Wobbuffet");
SEND_IN_MESSAGE("Wynaut");
}
}
SINGLE_BATTLE_TEST("Shed Shell allows switching out even when trapped by Arena Trap")
{
GIVEN {
PLAYER(SPECIES_DIGLETT) { Item(ITEM_SHED_SHELL); } // Grounded
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_DIGLETT) { Ability(ABILITY_ARENA_TRAP); }
} WHEN {
TURN { SWITCH(player, 1); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
SWITCH_OUT_MESSAGE("Diglett");
SEND_IN_MESSAGE("Wynaut");
}
}
SINGLE_BATTLE_TEST("Shed Shell does not allow Teleport when trapped")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TELEPORT) == EFFECT_TELEPORT);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_SHED_SHELL); Moves(MOVE_TELEPORT, MOVE_SPLASH, MOVE_CELEBRATE); }
OPPONENT(SPECIES_GASTLY);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_MEAN_LOOK); }
TURN { MOVE(player, MOVE_TELEPORT); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MEAN_LOOK, opponent);
MESSAGE("Wobbuffet used Teleport!");
MESSAGE("But it failed!");
}
}

View File

@ -675,9 +675,10 @@ SINGLE_BATTLE_TEST("Move Animations don't leak when used - Singles (player to op
SceneSingles(move, player);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -722,9 +723,10 @@ SINGLE_BATTLE_TEST("Move Animations don't leak when used - Singles (opponent to
SceneSingles(move, opponent);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -792,9 +794,10 @@ DOUBLE_BATTLE_TEST("Move Animations don't leak when used - Doubles (playerLeft t
DoublesScene(move, attacker);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -863,9 +866,10 @@ DOUBLE_BATTLE_TEST("Move Animations don't leak when used - Doubles (opponentLeft
DoublesScene(move, attacker);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -934,9 +938,10 @@ DOUBLE_BATTLE_TEST("Move Animations don't leak when used - Doubles (playerLeft t
DoublesScene(move, attacker);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -1005,9 +1010,10 @@ DOUBLE_BATTLE_TEST("Move Animations don't leak when used - Doubles (opponentRigh
DoublesScene(move, attacker);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -1076,9 +1082,10 @@ DOUBLE_BATTLE_TEST("Move Animations don't leak when used - Doubles (playerRight
DoublesScene(move, attacker);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -1147,9 +1154,10 @@ DOUBLE_BATTLE_TEST("Move Animations don't leak when used - Doubles (opponentLeft
DoublesScene(move, attacker);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -1218,9 +1226,10 @@ DOUBLE_BATTLE_TEST("Move Animations don't leak when used - Doubles (playerRight
DoublesScene(move, attacker);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -1289,9 +1298,10 @@ DOUBLE_BATTLE_TEST("Move Animations don't leak when used - Doubles (opponentRigh
DoublesScene(move, attacker);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -1585,9 +1595,10 @@ SINGLE_BATTLE_TEST("Move Animations occur before their stat change animations -
SceneSingles(move, player);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -1653,9 +1664,10 @@ SINGLE_BATTLE_TEST("Z-Moves don't leak when used - Singles (player to opponent)"
ANIMATION(ANIM_TYPE_MOVE, zmove, player);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -1682,9 +1694,10 @@ SINGLE_BATTLE_TEST("Z-Moves don't leak when used - Singles (opponent to player)"
ANIMATION(ANIM_TYPE_MOVE, zmove, opponent);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -1713,9 +1726,10 @@ DOUBLE_BATTLE_TEST("Z-Moves don't leak when used - Doubles (playerLeft to oppone
ANIMATION(ANIM_TYPE_MOVE, zmove, playerLeft);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -1744,9 +1758,10 @@ DOUBLE_BATTLE_TEST("Z-Moves don't leak when used - Doubles (playerLeft to oppone
ANIMATION(ANIM_TYPE_MOVE, zmove, playerLeft);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -1775,9 +1790,10 @@ DOUBLE_BATTLE_TEST("Z-Moves don't leak when used - Doubles (playerRight to oppon
ANIMATION(ANIM_TYPE_MOVE, zmove, playerRight);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -1806,9 +1822,10 @@ DOUBLE_BATTLE_TEST("Z-Moves don't leak when used - Doubles (playerRight to oppon
ANIMATION(ANIM_TYPE_MOVE, zmove, playerRight);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -1837,9 +1854,10 @@ DOUBLE_BATTLE_TEST("Z-Moves don't leak when used - Doubles (opponentLeft to play
ANIMATION(ANIM_TYPE_MOVE, zmove, opponentLeft);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -1868,9 +1886,10 @@ DOUBLE_BATTLE_TEST("Z-Moves don't leak when used - Doubles (opponentLeft to play
ANIMATION(ANIM_TYPE_MOVE, zmove, opponentLeft);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -1899,9 +1918,10 @@ DOUBLE_BATTLE_TEST("Z-Moves don't leak when used - Doubles (opponentRight to pla
ANIMATION(ANIM_TYPE_MOVE, zmove, opponentRight);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -1930,9 +1950,10 @@ DOUBLE_BATTLE_TEST("Z-Moves don't leak when used - Doubles (opponentRight to pla
ANIMATION(ANIM_TYPE_MOVE, zmove, opponentRight);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -1975,9 +1996,10 @@ SINGLE_BATTLE_TEST("Tera Blast doesn't leak when used - Singles (player to oppon
ANIMATION(ANIM_TYPE_MOVE, move, player);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -1997,9 +2019,10 @@ SINGLE_BATTLE_TEST("Tera Blast doesn't leak when used - Singles (opponent to pla
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -2021,9 +2044,10 @@ DOUBLE_BATTLE_TEST("Tera Blast doesn't leak when used - Doubles (playerLeft to o
ANIMATION(ANIM_TYPE_MOVE, move, playerLeft);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -2045,9 +2069,10 @@ DOUBLE_BATTLE_TEST("Tera Blast doesn't leak when used - Doubles (playerLeft to o
ANIMATION(ANIM_TYPE_MOVE, move, playerLeft);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -2069,9 +2094,10 @@ DOUBLE_BATTLE_TEST("Tera Blast doesn't leak when used - Doubles (playerRight to
ANIMATION(ANIM_TYPE_MOVE, move, playerRight);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -2093,9 +2119,10 @@ DOUBLE_BATTLE_TEST("Tera Blast doesn't leak when used - Doubles (playerRight to
ANIMATION(ANIM_TYPE_MOVE, move, playerRight);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -2117,9 +2144,10 @@ DOUBLE_BATTLE_TEST("Tera Blast doesn't leak when used - Doubles (opponentLeft to
ANIMATION(ANIM_TYPE_MOVE, move, opponentLeft);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -2141,9 +2169,10 @@ DOUBLE_BATTLE_TEST("Tera Blast doesn't leak when used - Doubles (opponentLeft to
ANIMATION(ANIM_TYPE_MOVE, move, opponentLeft);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -2165,9 +2194,10 @@ DOUBLE_BATTLE_TEST("Tera Blast doesn't leak when used - Doubles (opponentRight t
ANIMATION(ANIM_TYPE_MOVE, move, opponentRight);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}
@ -2189,9 +2219,10 @@ DOUBLE_BATTLE_TEST("Tera Blast doesn't leak when used - Doubles (opponentRight t
ANIMATION(ANIM_TYPE_MOVE, move, opponentRight);
} THEN {
FORCE_MOVE_ANIM(FALSE);
if (gLoadFail)
if (gLoadFail || gSpriteAllocs != 0)
DebugPrintf("Move failed: %S (%u)", GetMoveName(move), move);
EXPECT_EQ(gLoadFail, FALSE);
EXPECT_EQ(gSpriteAllocs, 0);
}
}