EndTurnEffectOrder Refactor (#6224)

This commit is contained in:
ghoulslash 2025-04-14 10:57:34 -04:00 committed by GitHub
commit 81ed774c00
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 2022 additions and 1514 deletions

View File

@ -1007,7 +1007,7 @@
.4byte \jumpInstr
.endm
.macro unused_bb
.macro tryhealingitem
.byte 0xbb
.endm
@ -1140,9 +1140,8 @@
.4byte \failInstr
.endm
.macro trywish turnNumber:req, failInstr:req
.macro trywish failInstr:req
.byte 0xd4
.byte \turnNumber
.4byte \failInstr
.endm

View File

@ -414,12 +414,12 @@ BattleScript_MoveEffectSaltCure::
return
BattleScript_SaltCureExtraDamage::
playanimation BS_TARGET, B_ANIM_SALT_CURE_DAMAGE, NULL
playanimation BS_ATTACKER, B_ANIM_SALT_CURE_DAMAGE, NULL
waitanimation
call BattleScript_HurtTarget_NoString
printstring STRINGID_TARGETISHURTBYSALTCURE
waitmessage B_WAIT_TIME_LONG
tryfaintmon BS_TARGET
tryfaintmon BS_ATTACKER
end2
BattleScript_HurtTarget_NoString:
@ -4977,7 +4977,7 @@ BattleScript_EffectWish::
attackcanceler
attackstring
ppreduce
trywish 0, BattleScript_ButItFailed
trywish BattleScript_ButItFailed
attackanimation
waitanimation
goto BattleScript_MoveEnd
@ -5770,13 +5770,13 @@ BattleScript_FogEnded_Ret::
return
BattleScript_IceBodyHeal::
call BattleScript_AbilityPopUpScripting
playanimation BS_SCRIPTING, B_ANIM_SIMPLE_HEAL
healthbarupdate BS_SCRIPTING
datahpupdate BS_SCRIPTING
call BattleScript_AbilityPopUp
playanimation BS_ATTACKER, B_ANIM_SIMPLE_HEAL
healthbarupdate BS_ATTACKER
datahpupdate BS_ATTACKER
printstring STRINGID_ICEBODYHPGAIN
waitmessage B_WAIT_TIME_LONG
end2
end3
BattleScript_OverworldStatusStarts::
printfromtable gStartingStatusStringIds
@ -5837,12 +5837,14 @@ BattleScript_WonderRoomEnds::
BattleScript_MagicRoomEnds::
printstring STRINGID_MAGICROOMENDS
waitmessage B_WAIT_TIME_LONG
setbyte gBattlerTarget, 0
BattleScript_MagicRoomHealingItemsLoop:
copyarraywithindex gBattlerAttacker, gBattlerByTurnOrder, gBattlerTarget, 1
tryhealingitem
addbyte gBattlerTarget, 1
jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_MagicRoomHealingItemsLoop
end2
BattleScript_GrassyTerrainEnds::
call BattleScript_GrassyTerrainHeals_Ret
goto BattleScript_TerrainEnds
BattleScript_TerrainEnds_Ret::
printfromtable gTerrainStringIds
waitmessage B_WAIT_TIME_LONG
@ -6608,7 +6610,6 @@ BattleScript_SelectingNotAllowedCurrentMoveInPalace::
goto BattleScript_SelectingUnusableMoveInPalace
BattleScript_WishComesTrue::
trywish 1, BattleScript_WishButFullHp
playanimation BS_TARGET, B_ANIM_WISH_HEAL
printstring STRINGID_PKMNWISHCAMETRUE
waitmessage B_WAIT_TIME_LONG
@ -6927,7 +6928,7 @@ BattleScript_CudChewActivates::
pause B_WAIT_TIME_SHORTEST
call BattleScript_AbilityPopUp
setbyte sBERRY_OVERRIDE, 1 @ override the requirements for eating berries
consumeberry BS_SCRIPTING, FALSE
consumeberry BS_ATTACKER, FALSE
setbyte sBERRY_OVERRIDE, 0
end3
@ -7079,6 +7080,7 @@ BattleScript_DoTurnDmg:
datahpupdate BS_ATTACKER
tryfaintmon BS_ATTACKER
checkteamslost BattleScript_DoTurnDmgEnd
tryhealingitem
BattleScript_DoTurnDmgEnd:
end2
@ -7326,6 +7328,7 @@ BattleScript_YawnEnd:
BattleScript_EmbargoEndTurn::
printstring STRINGID_EMBARGOENDS
waitmessage B_WAIT_TIME_LONG
tryhealingitem
end2
BattleScript_TelekinesisEndTurn::
@ -7544,6 +7547,37 @@ BattleScript_EmergencyExitWild::
finishaction
return
BattleScript_EmergencyExitEnd2::
pause 5
call BattleScript_AbilityPopUp
pause B_WAIT_TIME_LONG
playanimation BS_ATTACKER, B_ANIM_SLIDE_OFFSCREEN
waitanimation
openpartyscreen BS_ATTACKER, BattleScript_EmergencyExitRetEnd2
switchoutabilities BS_ATTACKER
waitstate
switchhandleorder BS_ATTACKER, 2
returntoball BS_TARGET, FALSE
getswitchedmondata BS_ATTACKER
switchindataupdate BS_ATTACKER
hpthresholds BS_ATTACKER
printstring STRINGID_SWITCHINMON
switchinanim BS_ATTACKER, FALSE, TRUE
waitstate
switchineffects BS_ATTACKER
BattleScript_EmergencyExitRetEnd2:
end2
BattleScript_EmergencyExitWildEnd2::
pause 5
call BattleScript_AbilityPopUp
pause B_WAIT_TIME_LONG
playanimation BS_ATTACKER, B_ANIM_SLIDE_OFFSCREEN
waitanimation
setoutcomeonteleport BS_ATTACKER
finishaction
end2
BattleScript_TraceActivates::
pause B_WAIT_TIME_SHORT
call BattleScript_AbilityPopUpScripting
@ -7586,6 +7620,7 @@ BattleScript_PickupActivates::
call BattleScript_AbilityPopUp
printstring STRINGID_XFOUNDONEY
waitmessage B_WAIT_TIME_LONG
tryhealingitem
BattleScript_PickupActivatesEnd:
end3
@ -7595,6 +7630,7 @@ BattleScript_HarvestActivates::
call BattleScript_AbilityPopUp
printstring STRINGID_HARVESTBERRY
waitmessage B_WAIT_TIME_LONG
tryhealingitem
BattleScript_HarvestActivatesEnd:
end3
@ -8229,25 +8265,13 @@ BattleScript_MoveUsedPsychicTerrainPrevents::
goto BattleScript_MoveEnd
BattleScript_GrassyTerrainHeals::
call BattleScript_GrassyTerrainHeals_Ret
end2
BattleScript_GrassyTerrainHeals_Ret::
setbyte gBattleCommunication, 0
BattleScript_GrassyTerrainLoop:
copyarraywithindex gBattlerAttacker, gBattlerByTurnOrder, gBattleCommunication, 1
checkgrassyterrainheal BS_ATTACKER, BattleScript_GrassyTerrainLoopIncrement
printstring STRINGID_GRASSYTERRAINHEALS
waitmessage B_WAIT_TIME_LONG
orword gHitMarker, HITMARKER_IGNORE_BIDE | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE
healthbarupdate BS_ATTACKER
datahpupdate BS_ATTACKER
BattleScript_GrassyTerrainLoopIncrement::
addbyte gBattleCommunication, 1
jumpifbytenotequal gBattleCommunication, gBattlersCount, BattleScript_GrassyTerrainLoop
bicword gHitMarker, HITMARKER_IGNORE_BIDE | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE
BattleScript_GrassyTerrainHealEnd:
return
end2
BattleScript_AbilityNoSpecificStatLoss::
pause B_WAIT_TIME_SHORT

View File

@ -85,12 +85,12 @@ struct DisableStruct
s8 stockpileBeforeSpDef;
u8 substituteHP;
u8 encoredMovePos;
u8 disableTimer:4;
u8 encoreTimer:4;
u8 perishSongTimer:4;
u8 rolloutTimer:4;
u8 rolloutTimerStartValue:4;
u8 tauntTimer:4;
u16 disableTimer;
u16 encoreTimer;
u16 perishSongTimer;
u16 rolloutTimer;
u16 rolloutTimerStartValue;
u16 tauntTimer;
u8 furyCutterCounter;
u8 battlerPreventingEscape;
u8 battlerWithSureHit;
@ -99,16 +99,16 @@ struct DisableStruct
u8 chargeTimer:4;
u8 rechargeTimer;
u8 autotomizeCount;
u8 slowStartTimer;
u8 embargoTimer;
u8 magnetRiseTimer;
u8 telekinesisTimer;
u8 healBlockTimer;
u8 laserFocusTimer;
u8 throatChopTimer;
u16 slowStartTimer;
u16 embargoTimer;
u16 magnetRiseTimer;
u16 telekinesisTimer;
u16 healBlockTimer;
u16 laserFocusTimer;
u16 throatChopTimer;
u8 wrapTurns;
u8 syrupBombTimer;
u8 tormentTimer:4; // used for G-Max Meltdown
u16 syrupBombTimer;
u16 tormentTimer; // used for G-Max Meltdown
u8 usedMoves:4;
u8 truantCounter:1;
u8 truantSwitchInHack:1;
@ -270,7 +270,7 @@ struct FieldTimer
struct WishFutureKnock
{
u8 futureSightCounter[MAX_BATTLERS_COUNT];
u16 futureSightCounter[MAX_BATTLERS_COUNT];
u8 futureSightBattlerIndex[MAX_BATTLERS_COUNT];
u8 futureSightPartyIndex[MAX_BATTLERS_COUNT];
u16 futureSightMove[MAX_BATTLERS_COUNT];
@ -557,7 +557,7 @@ struct ZMoveData
struct DynamaxData
{
u8 dynamaxTurns[MAX_BATTLERS_COUNT];
u16 dynamaxTurns[MAX_BATTLERS_COUNT];
u16 baseMoves[MAX_BATTLERS_COUNT]; // base move of Max Move
u16 lastUsedBaseMove;
};
@ -634,9 +634,9 @@ struct BattlerState
struct BattleStruct
{
struct BattlerState battlerState[MAX_BATTLERS_COUNT];
u8 turnEffectsTracker;
u8 eventBlockCounter;
u8 turnEffectsBattlerId;
u8 turnCountersTracker;
u8 endTurnEventsCounter;
u16 wrappedMove[MAX_BATTLERS_COUNT];
u16 moveTarget[MAX_BATTLERS_COUNT];
u32 expShareExpValue;
@ -707,8 +707,6 @@ struct BattleStruct
struct LinkBattlerHeader linkBattlerHeader;
struct BattleVideo battleVideo;
} multiBuffer;
u8 wishPerishSongState;
u8 wishPerishSongBattlerId;
u8 startingStatus:6; // status to apply at battle start. defined in constants/battle.h
u8 startingStatusDone:1;
u8 terrainDone:1;
@ -718,7 +716,7 @@ struct BattleStruct
u8 friskedAbility:1; // If identifies two mons, show the ability pop-up only once.
u8 fickleBeamBoosted:1;
u8 poisonPuppeteerConfusion:1;
u8 startingStatusTimer;
u16 startingStatusTimer;
u8 atkCancellerTracker;
struct BattleTvMovePoints tvMovePoints;
struct BattleTv tv;

View File

@ -0,0 +1,7 @@
#ifndef GUARD_BATTLE_END_TURN
#define GUARD_BATTLE_END_TURN
u32 DoEndTurnEffects(void);
#endif // GUARD_BATTLE_END_TURN

View File

@ -101,6 +101,7 @@ extern const u8 BattleScript_SelectingTormentedMoveInPalace[];
extern const u8 BattleScript_SelectingNotAllowedMoveTaunt[];
extern const u8 BattleScript_MoveUsedIsTaunted[];
extern const u8 BattleScript_SelectingNotAllowedMoveTauntInPalace[];
extern const u8 BattleScript_WishButFullHp[];
extern const u8 BattleScript_WishComesTrue[];
extern const u8 BattleScript_IngrainTurnHeal[];
extern const u8 BattleScript_AtkDefDown[];
@ -269,7 +270,6 @@ extern const u8 BattleScript_WonderRoomEnds[];
extern const u8 BattleScript_MagicRoomEnds[];
extern const u8 BattleScript_TerrainEnds[];
extern const u8 BattleScript_TerrainEnds_Ret[];
extern const u8 BattleScript_GrassyTerrainEnds[];
extern const u8 BattleScript_MudSportEnds[];
extern const u8 BattleScript_WaterSportEnds[];
extern const u8 BattleScript_SturdiedMsg[];
@ -385,6 +385,8 @@ extern const u8 BattleScript_FriskMsgWithPopup[];
extern const u8 BattleScript_MoodyActivates[];
extern const u8 BattleScript_EmergencyExit[];
extern const u8 BattleScript_EmergencyExitWild[];
extern const u8 BattleScript_EmergencyExitEnd2[];
extern const u8 BattleScript_EmergencyExitWildEnd2[];
extern const u8 BattleScript_CheekPouchActivates[];
extern const u8 BattleScript_TotemVar[];
extern const u8 BattleScript_TotemFlaredToLife[];

View File

@ -68,6 +68,7 @@ enum ItemCaseId
ITEMEFFECT_ON_SWITCH_IN,
ITEMEFFECT_ON_SWITCH_IN_FIRST_TURN,
ITEMEFFECT_NORMAL,
ITEMEFFECT_TRY_HEALING,
ITEMEFFECT_MOVE_END,
ITEMEFFECT_KINGSROCK,
ITEMEFFECT_TARGET,
@ -172,6 +173,8 @@ enum SleepClauseBlock
};
void HandleAction_ThrowBall(void);
u32 GetCurrentBattleWeather(void);
bool32 EndOrContinueWeather(void);
bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move);
bool32 HandleMoveTargetRedirection(void);
void HandleAction_UseMove(void);
@ -205,11 +208,9 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler);
u8 CheckMoveLimitations(u32 battler, u8 unusableMoves, u16 check);
bool32 AreAllMovesUnusable(u32 battler);
u8 GetImprisonedMovesCount(u32 battler, u16 move);
u8 DoFieldEndTurnEffects(void);
s32 GetDrainedBigRootHp(u32 battler, s32 hp);
bool32 IsMagicGuardProtected(u32 battler, u32 ability);
u8 DoBattlerEndTurnEffects(void);
bool32 HandleWishPerishSongOnTurnEnd(void);
u32 DoEndTurnEffects(void);
bool32 HandleFaintedMonActions(void);
void TryClearRageAndFuryCutter(void);
u32 AtkCanceller_MoveSuccessOrder(void);
@ -327,6 +328,7 @@ bool32 CanBeConfused(u32 battler);
bool32 IsBattlerTerrainAffected(u32 battler, u32 terrainFlag);
u32 GetBattlerAffectionHearts(u32 battler);
void TryToRevertMimicryAndFlags(void);
bool32 BattleArenaTurnEnd(void);
u32 CountBattlerStatIncreases(u32 battler, bool32 countEvasionAcc);
bool32 ChangeTypeBasedOnTerrain(u32 battler);
void RemoveConfusionStatus(u32 battler);

View File

@ -1040,7 +1040,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
// the following checks apply to any target (including user)
// throat chop check
if (gDisableStructs[battlerAtk].throatChopTimer && IsSoundMove(move))
if (gDisableStructs[battlerAtk].throatChopTimer > gBattleTurnCounter && IsSoundMove(move))
return 0; // Can't even select move at all
// heal block check
if (gStatuses3[battlerAtk] & STATUS3_HEAL_BLOCK && IsHealBlockPreventingMove(battlerAtk, move))
@ -2447,7 +2447,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
break;
case EFFECT_EMBARGO:
if (!IsBattlerItemEnabled(battlerAtk)
|| gDisableStructs[battlerDef].embargoTimer != 0
|| gStatuses3[battlerDef] & STATUS3_EMBARGO
|| PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove))
ADJUST_SCORE(-10);
break;
@ -2465,7 +2465,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-10);
break;
case EFFECT_HEAL_BLOCK:
if (gDisableStructs[battlerDef].healBlockTimer != 0
if (gStatuses3[battlerDef] & STATUS3_HEAL_BLOCK
|| PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove))
ADJUST_SCORE(-10);
break;

View File

@ -178,7 +178,7 @@ void ActivateDynamax(u32 battler)
// Set appropriate use flags.
SetActiveGimmick(battler, GIMMICK_DYNAMAX);
SetGimmickAsActivated(battler, GIMMICK_DYNAMAX);
gBattleStruct->dynamax.dynamaxTurns[battler] = DYNAMAX_TURNS_COUNT;
gBattleStruct->dynamax.dynamaxTurns[battler] = gBattleTurnCounter + DYNAMAX_TURNS_COUNT;
// Substitute is removed upon Dynamaxing.
gBattleMons[battler].status2 &= ~STATUS2_SUBSTITUTE;
@ -209,7 +209,6 @@ void UndoDynamax(u32 battler)
// Makes sure there are no Dynamax flags set, including on switch / faint.
SetActiveGimmick(battler, GIMMICK_NONE);
gBattleStruct->dynamax.dynamaxTurns[battler] = 0;
// Undo form change if needed.
if (IsGigantamaxed(battler))

1596
src/battle_end_turn.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@
#include "battle_ai_util.h"
#include "battle_arena.h"
#include "battle_controllers.h"
#include "battle_end_turn.h"
#include "battle_interface.h"
#include "battle_main.h"
#include "battle_message.h"
@ -3878,13 +3879,11 @@ static void TryDoEventsBeforeFirstTurn(void)
gBattleStruct->appearedInBattle |= 1u << gBattlerPartyIndexes[i];
}
*(&gBattleStruct->turnEffectsTracker) = 0;
*(&gBattleStruct->eventBlockCounter) = 0;
*(&gBattleStruct->turnEffectsBattlerId) = 0;
*(&gBattleStruct->wishPerishSongState) = 0;
*(&gBattleStruct->wishPerishSongBattlerId) = 0;
gBattleScripting.moveendState = 0;
gBattleStruct->faintedActionsState = 0;
gBattleStruct->turnCountersTracker = 0;
gBattleStruct->endTurnEventsCounter = 0;
memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts));
SetShellSideArmCategory();
@ -3918,11 +3917,9 @@ static void HandleEndTurn_ContinueBattle(void)
if ((gBattleMons[i].status1 & STATUS1_SLEEP) && (gBattleMons[i].status2 & STATUS2_MULTIPLETURNS))
CancelMultiTurnMoves(i);
}
gBattleStruct->turnEffectsTracker = 0;
gBattleStruct->eventBlockCounter = 0;
gBattleStruct->turnEffectsBattlerId = 0;
gBattleStruct->wishPerishSongState = 0;
gBattleStruct->wishPerishSongBattlerId = 0;
gBattleStruct->turnCountersTracker = 0;
gBattleStruct->endTurnEventsCounter = 0;
}
}
@ -3933,18 +3930,14 @@ void BattleTurnPassed(void)
gBattleStruct->speedTieBreaks = RandomUniform(RNG_SPEED_TIE, 0, Factorial(MAX_BATTLERS_COUNT) - 1);
TurnValuesCleanUp(TRUE);
if (gBattleOutcome == 0)
{
if (DoFieldEndTurnEffects())
return;
if (DoBattlerEndTurnEffects())
return;
if (HandleWishPerishSongOnTurnEnd())
return;
}
if (gBattleOutcome == 0 && DoEndTurnEffects())
return;
if (BattleArenaTurnEnd())
return;
if (HandleFaintedMonActions())
return;
gBattleStruct->faintedActionsState = 0;
TurnValuesCleanUp(FALSE);
@ -3979,6 +3972,8 @@ void BattleTurnPassed(void)
gBattleStruct->battlerState[i].absent = (gAbsentBattlerFlags & (1u << i) ? TRUE : FALSE);
gBattleStruct->monToSwitchIntoId[i] = PARTY_SIZE;
gStatuses4[i] &= ~STATUS4_ELECTRIFIED;
gBattleMons[i].status2 &= ~STATUS2_FLINCHED;
gBattleMons[i].status2 &= ~STATUS2_POWDER;
}
for (i = 0; i < NUM_BATTLE_SIDES; i++)
@ -3987,6 +3982,8 @@ void BattleTurnPassed(void)
gSideTimers[i].retaliateTimer--;
}
gFieldStatuses &= ~STATUS_FIELD_ION_DELUGE;
BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG);
AssignUsableGimmicks();
SetShellSideArmCategory();

View File

@ -962,7 +962,8 @@ const u16 gTerrainPreventsStringIds[] =
const u16 gHealingWishStringIds[] =
{
STRINGID_HEALINGWISHCAMETRUE, STRINGID_LUNARDANCECAMETRUE
STRINGID_HEALINGWISHCAMETRUE,
STRINGID_LUNARDANCECAMETRUE
};
const u16 gDmgHazardsStringIds[] =

View File

@ -533,7 +533,7 @@ static void Cmd_presentdamagecalculation(void);
static void Cmd_setsafeguard(void);
static void Cmd_magnitudedamagecalculation(void);
static void Cmd_jumpifnopursuitswitchdmg(void);
static void Cmd_unused_bb(void);
static void Cmd_tryhealingitem(void);
static void Cmd_halvehp(void);
static void Cmd_copyfoestats(void);
static void Cmd_rapidspinfree(void);
@ -792,7 +792,7 @@ void (* const gBattleScriptingCommandsTable[])(void) =
Cmd_setsafeguard, //0xB8
Cmd_magnitudedamagecalculation, //0xB9
Cmd_jumpifnopursuitswitchdmg, //0xBA
Cmd_unused_bb, //0xBB
Cmd_tryhealingitem, //0xBB
Cmd_halvehp, //0xBC
Cmd_copyfoestats, //0xBD
Cmd_rapidspinfree, //0xBE
@ -3987,7 +3987,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
}
break;
case MOVE_EFFECT_THROAT_CHOP:
gDisableStructs[gEffectBattler].throatChopTimer = 2;
gDisableStructs[gEffectBattler].throatChopTimer = gBattleTurnCounter + 2;
gBattlescriptCurrInstr++;
break;
case MOVE_EFFECT_INCINERATE:
@ -4194,7 +4194,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
else if (!(gStatuses3[gEffectBattler] & STATUS3_HEAL_BLOCK))
{
gStatuses3[gEffectBattler] |= STATUS3_HEAL_BLOCK;
gDisableStructs[gEffectBattler].healBlockTimer = 2;
gDisableStructs[gEffectBattler].healBlockTimer = gBattleTurnCounter + 2;
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_EffectPsychicNoise;
}
@ -7691,7 +7691,7 @@ static void Cmd_sethealblock(void)
else
{
gStatuses3[gBattlerTarget] |= STATUS3_HEAL_BLOCK;
gDisableStructs[gBattlerTarget].healBlockTimer = 5;
gDisableStructs[gBattlerTarget].healBlockTimer = gBattleTurnCounter + 5;
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
@ -8345,6 +8345,7 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler)
else if ((gBattleStruct->battlerState[battler].storedHealingWish || gBattleStruct->battlerState[battler].storedLunarDance)
&& (gBattleMons[battler].hp != gBattleMons[battler].maxHP || gBattleMons[battler].status1 != 0 || B_HEALING_WISH_SWITCH < GEN_8))
{
gBattlerAttacker = battler;
if (gBattleStruct->battlerState[battler].storedHealingWish)
{
BattleScriptPushCursor();
@ -14304,7 +14305,7 @@ static void Cmd_setembargo(void)
else
{
gStatuses3[gBattlerTarget] |= STATUS3_EMBARGO;
gDisableStructs[gBattlerTarget].embargoTimer = 5;
gDisableStructs[gBattlerTarget].embargoTimer = gBattleTurnCounter + 5;
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
@ -14480,8 +14481,18 @@ static void Cmd_jumpifnopursuitswitchdmg(void)
}
}
static void Cmd_unused_bb(void)
static void Cmd_tryhealingitem(void)
{
CMD_ARGS();
if (gItemsInfo[gBattleMons[gBattlerAttacker].item].pocket == POCKET_BERRIES
|| GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_RESTORE_HP) // Edge case for Berry Juice
{
if (ItemBattleEffects(ITEMEFFECT_TRY_HEALING, gBattlerAttacker, FALSE))
return;
}
gBattlescriptCurrInstr = cmd->nextInstr;
}
// Belly Drum, Fillet Away
@ -14935,7 +14946,6 @@ static void Cmd_settorment(void)
else
{
gBattleMons[gBattlerTarget].status2 |= STATUS2_TORMENT;
gDisableStructs[gBattlerTarget].tormentTimer = PERMANENT_TORMENT; // permanent
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
@ -15143,36 +15153,17 @@ static void Cmd_trycopyability(void)
static void Cmd_trywish(void)
{
CMD_ARGS(u8 turnNumber, const u8 *failInstr);
CMD_ARGS(const u8 *failInstr);
switch (cmd->turnNumber)
if (gWishFutureKnock.wishCounter[gBattlerAttacker] <= gBattleTurnCounter)
{
case 0: // use wish
if (gWishFutureKnock.wishCounter[gBattlerAttacker] <= gBattleTurnCounter)
{
gWishFutureKnock.wishCounter[gBattlerAttacker] = gBattleTurnCounter + 2;
gWishFutureKnock.wishPartyId[gBattlerAttacker] = gBattlerPartyIndexes[gBattlerAttacker];
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
{
gBattlescriptCurrInstr = cmd->failInstr;
}
break;
case 1: // heal effect
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gBattlerTarget, gWishFutureKnock.wishPartyId[gBattlerTarget])
if (B_WISH_HP_SOURCE >= GEN_5)
gBattleStruct->moveDamage[gBattlerTarget] = max(1, GetMonData(&GetBattlerParty(gBattlerTarget)[gWishFutureKnock.wishPartyId[gBattlerTarget]], MON_DATA_MAX_HP) / 2);
else
gBattleStruct->moveDamage[gBattlerTarget] = max(1, GetNonDynamaxMaxHP(gBattlerAttacker) / 2);
gBattleStruct->moveDamage[gBattlerTarget] *= -1;
if (gBattleMons[gBattlerTarget].hp == gBattleMons[gBattlerTarget].maxHP)
gBattlescriptCurrInstr = cmd->failInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
break;
gWishFutureKnock.wishCounter[gBattlerAttacker] = gBattleTurnCounter + 2;
gWishFutureKnock.wishPartyId[gBattlerAttacker] = gBattlerPartyIndexes[gBattlerAttacker];
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
{
gBattlescriptCurrInstr = cmd->failInstr;
}
}
@ -15405,9 +15396,9 @@ static void Cmd_setuserstatus3(void)
{
gStatuses3[gBattlerAttacker] |= flags;
if (flags & STATUS3_MAGNET_RISE)
gDisableStructs[gBattlerAttacker].magnetRiseTimer = 5;
gDisableStructs[gBattlerAttacker].magnetRiseTimer = gBattleTurnCounter + 5;
if (flags & STATUS3_LASER_FOCUS)
gDisableStructs[gBattlerAttacker].laserFocusTimer = 2;
gDisableStructs[gBattlerAttacker].laserFocusTimer = gBattleTurnCounter + 2;
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
@ -16647,7 +16638,7 @@ static void Cmd_settelekinesis(void)
else
{
gStatuses3[gBattlerTarget] |= STATUS3_TELEKINESIS;
gDisableStructs[gBattlerTarget].telekinesisTimer = 3;
gDisableStructs[gBattlerTarget].telekinesisTimer = gBattleTurnCounter + 3;
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
@ -18748,7 +18739,7 @@ void BS_TrySetTorment(void)
&& !IsAbilityOnSide(gBattlerTarget, ABILITY_AROMA_VEIL))
{
gBattleMons[gBattlerTarget].status2 |= STATUS2_TORMENT;
gDisableStructs[gBattlerTarget].tormentTimer = 3; // 3 turns excluding current turn
gDisableStructs[gBattlerTarget].tormentTimer = gBattleTurnCounter + 3; // 3 turns excluding current turn
gBattleCommunication[MULTISTRING_CHOOSER] = 3;
gEffectBattler = gBattlerTarget;
gBattlescriptCurrInstr = cmd->nextInstr;

File diff suppressed because it is too large Load Diff

View File

@ -66,3 +66,45 @@ DOUBLE_BATTLE_TEST("Only the fastest Wimp Out (Emergency Exit) user switches out
ABILITY_POPUP(opponentRight, ABILITY_WIMP_OUT);
}
}
SINGLE_BATTLE_TEST("Emergency Exit activates when taking residual damage and falling under 50% max-hp - Burn")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(134); Status1(STATUS1_BURN); };
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { SEND_OUT(opponent, 1); }
} SCENE {
HP_BAR(opponent);
ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT);
}
}
SINGLE_BATTLE_TEST("Emergency Exit activates when taking residual damage and falling under 50% max-hp - Weather")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(134); };
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SANDSTORM); SEND_OUT(opponent, 1); }
} SCENE {
HP_BAR(opponent);
ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT);
}
}
SINGLE_BATTLE_TEST("Emergency Exit activates when taking residual damage and falling under 50% max-hp - Salt Cure")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(160); };
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SALT_CURE); SEND_OUT(opponent, 1); }
} SCENE {
HP_BAR(opponent);
ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT);
}
}

View File

@ -26,8 +26,6 @@ DOUBLE_BATTLE_TEST("Healer cures adjacent ally's status condition 30% of the tim
DOUBLE_BATTLE_TEST("Healer cures status condition before burn or poison damage is dealt")
{
KNOWN_FAILING; // According to Bulbapedia, Healer should trigger before status damage and Wobbuffet should live
// Source: https://bulbapedia.bulbagarden.net/wiki/Healer_(Ability)#Effect
u16 status;
PARAMETRIZE { status = STATUS1_POISON; }
PARAMETRIZE { status = STATUS1_BURN; }
@ -42,10 +40,8 @@ DOUBLE_BATTLE_TEST("Healer cures status condition before burn or poison damage i
} WHEN {
TURN {}
} SCENE {
NOT {
MESSAGE("The opposing Wobbuffet fainted!");
}
MESSAGE("The opposing Chansey's Healer cured Foe Wobbuffet's problem!");
NOT MESSAGE("The opposing Wobbuffet fainted!");
MESSAGE("The opposing Chansey's Healer cured the opposing Wobbuffet's problem!");
}
}

View File

@ -0,0 +1,31 @@
#include "global.h"
#include "test/battle.h"
DOUBLE_BATTLE_TEST("End Turn Effects: First Event Block is executed correctly")
{
s16 healed;
s16 damage;
GIVEN {
PLAYER(SPECIES_WYNAUT) { HP(100); Speed(1); }
PLAYER(SPECIES_EKANS) { HP(100); Ability(ABILITY_SHED_SKIN); Status1(STATUS1_BURN); Speed(2); }
OPPONENT(SPECIES_WYNAUT) { HP(100); Item(ITEM_LEFTOVERS); Speed(3); }
OPPONENT(SPECIES_WOBBUFFET) { HP(100); Item(ITEM_BLACK_SLUDGE); Speed(4); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_GRASSY_TERRAIN); }
} SCENE {
MESSAGE("The opposing Wobbuffet is healed by the grassy terrain!");
HP_BAR(opponentRight, captureDamage: &healed);
HP_BAR(opponentRight, captureDamage: &damage);
MESSAGE("The opposing Wobbuffet was hurt by the Black Sludge!");
MESSAGE("The opposing Wynaut is healed by the grassy terrain!");
MESSAGE("The opposing Wynaut restored a little HP using its Leftovers!");
MESSAGE("Ekans is healed by the grassy terrain!");
MESSAGE("Ekans's Shed Skin cured its burn problem!");
MESSAGE("Wynaut is healed by the grassy terrain!");
} THEN {
EXPECT_GT(0, healed);
EXPECT_GT(damage, 0);
}
}

View File

@ -107,11 +107,11 @@ DOUBLE_BATTLE_TEST("Court Change used by the player swaps Mist, Safeguard, Auror
MESSAGE("Wynaut used Court Change!");
MESSAGE("Wynaut swapped the battle effects affecting each side of the field!");
// The effects now end for the player side.
MESSAGE("Your team's Mist wore off!");
MESSAGE("Your team is no longer protected by Safeguard!");
MESSAGE("Your team's Mist wore off!");
MESSAGE("Your team's Reflect wore off!");
MESSAGE("Your team's Aurora Veil wore off!");
MESSAGE("Your team's Tailwind petered out!");
MESSAGE("Your team's Aurora Veil wore off!");
MESSAGE("Your team's Light Screen wore off!");
}
}
@ -144,11 +144,12 @@ DOUBLE_BATTLE_TEST("Court Change used by the opponent swaps Mist, Safeguard, Aur
MESSAGE("The opposing Wynaut used Court Change!");
MESSAGE("The opposing Wynaut swapped the battle effects affecting each side of the field!");
// The effects now end for the player side.
MESSAGE("The opposing team's Mist wore off!");
MESSAGE("The snow stopped.");
MESSAGE("The opposing team is no longer protected by Safeguard!");
MESSAGE("The opposing team's Mist wore off!");
MESSAGE("The opposing team's Reflect wore off!");
MESSAGE("The opposing team's Aurora Veil wore off!");
MESSAGE("The opposing team's Tailwind petered out!");
MESSAGE("The opposing team's Aurora Veil wore off!");
MESSAGE("The opposing team's Light Screen wore off!");
}
}

View File

@ -8,9 +8,20 @@ SINGLE_BATTLE_TEST("Grassy Terrain recovers 1/16th HP at end of turn")
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(1); };
} WHEN {
TURN { MOVE(player, MOVE_GRASSY_TERRAIN); }
TURN {}
} SCENE {
s32 maxHPPlayer = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
s32 maxHPOpponent = GetMonData(&OPPONENT_PARTY[0], MON_DATA_MAX_HP);
ANIMATION(ANIM_TYPE_MOVE, MOVE_GRASSY_TERRAIN, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
MESSAGE("Wobbuffet is healed by the grassy terrain!");
HP_BAR(player, damage: -maxHPPlayer / 16);
MESSAGE("The opposing Wobbuffet is healed by the grassy terrain!");
HP_BAR(opponent, damage: -maxHPOpponent / 16);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
MESSAGE("Wobbuffet is healed by the grassy terrain!");
HP_BAR(player, damage: -maxHPPlayer / 16);
MESSAGE("The opposing Wobbuffet is healed by the grassy terrain!");

View File

@ -58,6 +58,36 @@ SINGLE_BATTLE_TEST("Leech Seed recovery is prevented by Heal Block")
}
}
DOUBLE_BATTLE_TEST("Leech Seed will drain HP based on speed of the drained mon")
{
GIVEN {
PLAYER(SPECIES_WYNAUT) { Speed(1); }
PLAYER(SPECIES_WOBBUFFET) { Speed(2); }
OPPONENT(SPECIES_WYNAUT) { Speed(3); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(4); }
} WHEN {
TURN {
MOVE(playerLeft, MOVE_LEECH_SEED, target: opponentLeft);
MOVE(playerRight, MOVE_LEECH_SEED, target: opponentRight);
MOVE(opponentLeft, MOVE_LEECH_SEED, target: playerLeft);
MOVE(opponentRight, MOVE_LEECH_SEED, target: playerRight);
}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_LEECH_SEED, opponentRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_LEECH_SEED, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_LEECH_SEED, playerRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_LEECH_SEED, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_LEECH_SEED_DRAIN, opponentRight);
HP_BAR(opponentRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_LEECH_SEED_DRAIN, opponentLeft);
HP_BAR(opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_LEECH_SEED_DRAIN, playerRight);
HP_BAR(playerRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_LEECH_SEED_DRAIN, playerLeft);
HP_BAR(playerLeft);
}
}
TO_DO_BATTLE_TEST("Leech Seed doesn't affect already seeded targets")
TO_DO_BATTLE_TEST("Leech Seed's effect is paused until a new battler replaces the original user's position") // Faint, can't be replaced, then revived.
TO_DO_BATTLE_TEST("Leech Seed's effect pause still prevents it from being seeded again")

View File

@ -0,0 +1,37 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_MAGIC_ROOM) == EFFECT_MAGIC_ROOM);
}
DOUBLE_BATTLE_TEST("Magic Room prevents item hold effects")
{
GIVEN {
ASSUME(gItemsInfo[ITEM_BERRY_JUICE].holdEffect == HOLD_EFFECT_RESTORE_HP);
ASSUME(GetMoveEffect(MOVE_DRAGON_RAGE) == EFFECT_FIXED_DAMAGE_ARG);
ASSUME(GetMoveFixedDamage(MOVE_DRAGON_RAGE) == 40);
PLAYER(SPECIES_WOBBUFFET) { MaxHP(100); HP(60); Item(ITEM_BERRY_JUICE); }
PLAYER(SPECIES_WOBBUFFET) { MaxHP(100); HP(60); Item(ITEM_BERRY_JUICE); }
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(60); Item(ITEM_BERRY_JUICE); }
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(60); Item(ITEM_BERRY_JUICE); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_MAGIC_ROOM); }
TURN {
MOVE(playerLeft, MOVE_DRAGON_RAGE, target: opponentLeft);
MOVE(opponentLeft, MOVE_DRAGON_RAGE, target: playerLeft);
MOVE(playerRight, MOVE_DRAGON_RAGE, target: opponentRight);
MOVE(opponentRight, MOVE_DRAGON_RAGE, target: playerRight);
}
TURN { MOVE(playerLeft, MOVE_MAGIC_ROOM); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGIC_ROOM, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGIC_ROOM, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponentRight);
}
}

View File

@ -0,0 +1,25 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_WISH) == EFFECT_WISH);
}
SINGLE_BATTLE_TEST("Wish restores 50% of max player HP")
{
GIVEN {
PLAYER(SPECIES_WYNAUT) { HP(50); MaxHP(100); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_WISH); }
TURN { }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_WISH, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
MESSAGE("Wynaut's wish came true!");
HP_BAR(player, hp: 100);
MESSAGE("Wynaut's HP was restored.");
}
}

View File

@ -24,22 +24,24 @@ SINGLE_BATTLE_TEST("Throat Chop prevents the usage of sound moves")
}
}
SINGLE_BATTLE_TEST("Throat Chop won't work through a substitute")
SINGLE_BATTLE_TEST("Throat Chop prevents sound base moves for 2 turns")
{
GIVEN {
PLAYER(SPECIES_INCINEROAR) { Speed(100); };
OPPONENT(SPECIES_WOBBUFFET) { Speed(50); };
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_HYPER_VOICE, MOVE_ALLURING_VOICE, MOVE_OVERDRIVE, MOVE_ROUND); }
} WHEN {
TURN { MOVE(opponent, MOVE_SUBSTITUTE); }
TURN { MOVE(player, MOVE_THROAT_CHOP); MOVE(opponent, MOVE_HYPER_VOICE); }
TURN {}
TURN { MOVE(opponent, MOVE_HYPER_VOICE); MOVE(player, MOVE_THROAT_CHOP); }
TURN { FORCED_MOVE(opponent); }
TURN { MOVE(opponent, MOVE_HYPER_VOICE); MOVE(player, MOVE_THROAT_CHOP); }
TURN { FORCED_MOVE(opponent); }
TURN { MOVE(opponent, MOVE_HYPER_VOICE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, opponent);
HP_BAR(opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPER_VOICE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_THROAT_CHOP, player);
NONE_OF {
MESSAGE("The effects of Throat Chop prevent the opposing Wobbuffet from using certain moves!");
}
ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPER_VOICE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_THROAT_CHOP, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPER_VOICE, opponent);
}
}