Centralizes non volatile status effect checks (#6533)

Co-authored-by: PhallenTree <168426989+PhallenTree@users.noreply.github.com>
This commit is contained in:
Alex 2025-04-30 17:34:19 +02:00 committed by GitHub
parent 2412666b01
commit 4ff2f3de42
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 942 additions and 742 deletions

View File

@ -799,7 +799,8 @@
2:
.endm
.macro unused_0x8d
.macro trynonvolatilestatus
.byte 0x8d
.endm
.macro initmultihitstring
@ -1335,9 +1336,8 @@
.4byte \jumpInstr
.endm
.macro unused ptr:req
.macro setnonvolatilestatus
.byte 0xfd
.4byte \ptr
.endm
.macro tryworryseed failInstr:req
@ -2169,18 +2169,6 @@
.4byte \jumpInstr
.endm
.macro trypoisontype attacker:req, target:req, failInstr:req
various \attacker, VARIOUS_POISON_TYPE_IMMUNITY
.byte \target
.4byte \failInstr
.endm
.macro tryparalyzetype attacker:req, target:req, failInstr:req
various \attacker, VARIOUS_PARALYZE_TYPE_IMMUNITY
.byte \target
.4byte \failInstr
.endm
.macro trysetfairylock failInstr:req
various BS_ATTACKER, VARIOUS_TRY_FAIRY_LOCK
.4byte \failInstr

View File

@ -2863,34 +2863,9 @@ BattleScript_MoveMissed::
BattleScript_EffectDarkVoid::
.if B_DARK_VOID_FAIL >= GEN_7
jumpifspecies BS_ATTACKER, SPECIES_DARKRAI, BattleScript_EffectSleep
jumpifspecies BS_ATTACKER, SPECIES_DARKRAI, BattleScript_EffectNonVolatileStatus
goto BattleScript_PokemonCantUseTheMove
.endif
BattleScript_EffectSleep::
attackcanceler
attackstring
ppreduce
jumpifsubstituteblocks BattleScript_ButItFailed
jumpifstatus BS_TARGET, STATUS1_SLEEP, BattleScript_AlreadyAsleep
jumpifuproarwakes BattleScript_CantMakeAsleep
jumpifability BS_TARGET, ABILITY_INSOMNIA, BattleScript_InsomniaProtects
jumpifability BS_TARGET, ABILITY_VITAL_SPIRIT, BattleScript_InsomniaProtects
jumpifability BS_TARGET, ABILITY_COMATOSE, BattleScript_AbilityProtectsDoesntAffect
jumpifability BS_TARGET, ABILITY_PURIFYING_SALT, BattleScript_AbilityProtectsDoesntAffect
jumpifflowerveil BattleScript_FlowerVeilProtects
jumpifability BS_TARGET_SIDE, ABILITY_SWEET_VEIL, BattleScript_SweetVeilProtects
jumpifleafguardprotected BS_TARGET, BattleScript_AbilityProtectsDoesntAffect
jumpifshieldsdown BS_TARGET, BattleScript_AbilityProtectsDoesntAffect
jumpifstatus BS_TARGET, STATUS1_ANY, BattleScript_ButItFailed
jumpifsleepclause BattleScript_SleepClauseBlocked
jumpifterrainaffected BS_TARGET, STATUS_FIELD_ELECTRIC_TERRAIN, BattleScript_ElectricTerrainPrevents
jumpifterrainaffected BS_TARGET, STATUS_FIELD_MISTY_TERRAIN, BattleScript_MistyTerrainPrevents
accuracycheck BattleScript_ButItFailed, ACC_CURR_MOVE
jumpifsafeguard BattleScript_SafeguardProtected
attackanimation
waitanimation
seteffectprimary MOVE_EFFECT_SLEEP
goto BattleScript_MoveEnd
BattleScript_TerrainPreventsEnd2::
pause B_WAIT_TIME_SHORT
@ -2919,7 +2894,7 @@ BattleScript_FlowerVeilProtectsRet::
waitmessage B_WAIT_TIME_LONG
return
BattleScript_FlowerVeilProtects:
BattleScript_FlowerVeilProtects::
call BattleScript_FlowerVeilProtectsRet
setmoveresultflags MOVE_RESULT_FAILED
goto BattleScript_MoveEnd
@ -2948,15 +2923,11 @@ BattleScript_AromaVeilProtects:
setmoveresultflags MOVE_RESULT_FAILED
goto BattleScript_MoveEnd
BattleScript_PastelVeilProtectsRet::
BattleScript_PastelVeilProtects:
pause B_WAIT_TIME_SHORT
call BattleScript_AbilityPopUp
printstring STRINGID_PASTELVEILPROTECTED
waitmessage B_WAIT_TIME_LONG
return
BattleScript_PastelVeilProtects:
call BattleScript_PastelVeilProtectsRet
setmoveresultflags MOVE_RESULT_FAILED
goto BattleScript_MoveEnd
@ -2967,7 +2938,7 @@ BattleScript_AbilityProtectsDoesntAffectRet::
waitmessage B_WAIT_TIME_LONG
return
BattleScript_AbilityProtectsDoesntAffect:
BattleScript_AbilityProtectsDoesntAffect::
call BattleScript_AbilityProtectsDoesntAffectRet
setmoveresultflags MOVE_RESULT_FAILED
goto BattleScript_MoveEnd
@ -3315,31 +3286,6 @@ BattleScript_RestoreHp:
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_EffectToxic::
attackcanceler
attackstring
ppreduce
jumpifability BS_TARGET, ABILITY_IMMUNITY, BattleScript_ImmunityProtected
jumpifability BS_TARGET, ABILITY_COMATOSE, BattleScript_AbilityProtectsDoesntAffect
jumpifability BS_TARGET, ABILITY_PURIFYING_SALT, BattleScript_AbilityProtectsDoesntAffect
jumpifability BS_TARGET_SIDE, ABILITY_PASTEL_VEIL, BattleScript_PastelVeilProtects
jumpifflowerveil BattleScript_FlowerVeilProtects
jumpifleafguardprotected BS_TARGET, BattleScript_AbilityProtectsDoesntAffect
jumpifshieldsdown BS_TARGET, BattleScript_AbilityProtectsDoesntAffect
jumpifsubstituteblocks BattleScript_ButItFailed
jumpifstatus BS_TARGET, STATUS1_POISON | STATUS1_TOXIC_POISON, BattleScript_AlreadyPoisoned
jumpifstatus BS_TARGET, STATUS1_ANY, BattleScript_ButItFailed
jumpifterrainaffected BS_TARGET, STATUS_FIELD_MISTY_TERRAIN, BattleScript_MistyTerrainPrevents
trypoisontype BS_ATTACKER, BS_TARGET, BattleScript_NotAffected
accuracycheck BattleScript_ButItFailed, ACC_CURR_MOVE
jumpifsafeguard BattleScript_SafeguardProtected
attackanimation
waitanimation
seteffectprimary MOVE_EFFECT_TOXIC
resultmessage
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_AlreadyPoisoned::
setalreadystatusedmoveattempt BS_ATTACKER
pause B_WAIT_TIME_LONG
@ -3348,10 +3294,10 @@ BattleScript_AlreadyPoisoned::
goto BattleScript_MoveEnd
BattleScript_ImmunityProtected::
copybyte gEffectBattler, gBattlerTarget
call BattleScript_AbilityPopUp
setbyte cMULTISTRING_CHOOSER, B_MSG_ABILITY_PREVENTS_MOVE_STATUS
call BattleScript_PSNPrevention
pause B_WAIT_TIME_SHORT
printfromtable gStatusPreventionStringIds
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_EffectAuroraVeil::
@ -3571,78 +3517,18 @@ BattleScript_EffectAuroraVeilSuccess::
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_EffectPoison::
attackcanceler
attackstring
ppreduce
jumpifability BS_TARGET, ABILITY_IMMUNITY, BattleScript_ImmunityProtected
jumpifability BS_TARGET, ABILITY_COMATOSE, BattleScript_AbilityProtectsDoesntAffect
jumpifability BS_TARGET, ABILITY_PURIFYING_SALT, BattleScript_AbilityProtectsDoesntAffect
jumpifability BS_TARGET_SIDE, ABILITY_PASTEL_VEIL, BattleScript_PastelVeilProtects
jumpifflowerveil BattleScript_FlowerVeilProtects
jumpifleafguardprotected BS_TARGET, BattleScript_AbilityProtectsDoesntAffect
jumpifshieldsdown BS_TARGET, BattleScript_AbilityProtectsDoesntAffect
jumpifsubstituteblocks BattleScript_ButItFailed
jumpifstatus BS_TARGET, STATUS1_POISON, BattleScript_AlreadyPoisoned
jumpifstatus BS_TARGET, STATUS1_TOXIC_POISON, BattleScript_AlreadyPoisoned
trypoisontype BS_ATTACKER, BS_TARGET, BattleScript_NotAffected
jumpifstatus BS_TARGET, STATUS1_ANY, BattleScript_ButItFailed
jumpifterrainaffected BS_TARGET, STATUS_FIELD_MISTY_TERRAIN, BattleScript_MistyTerrainPrevents
accuracycheck BattleScript_ButItFailed, ACC_CURR_MOVE
jumpifsafeguard BattleScript_SafeguardProtected
attackanimation
waitanimation
seteffectprimary MOVE_EFFECT_POISON
resultmessage
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_EffectParalyze::
attackcanceler
attackstring
ppreduce
jumpifability BS_TARGET, ABILITY_LIMBER, BattleScript_LimberProtected
jumpifability BS_TARGET, ABILITY_COMATOSE, BattleScript_AbilityProtectsDoesntAffect
jumpifability BS_TARGET, ABILITY_PURIFYING_SALT, BattleScript_AbilityProtectsDoesntAffect
jumpifflowerveil BattleScript_FlowerVeilProtects
jumpifleafguardprotected BS_TARGET, BattleScript_AbilityProtectsDoesntAffect
jumpifshieldsdown BS_TARGET, BattleScript_AbilityProtectsDoesntAffect
jumpifsubstituteblocks BattleScript_ButItFailed
typecalc
jumpifmovehadnoeffect BattleScript_ButItFailed
jumpifstatus BS_TARGET, STATUS1_PARALYSIS, BattleScript_AlreadyParalyzed
jumpifelectricabilityaffected BS_TARGET, ABILITY_VOLT_ABSORB, BattleScript_VoltAbsorbHeal
clearmoveresultflags MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE
tryparalyzetype BS_ATTACKER, BS_TARGET, BattleScript_NotAffected
jumpifstatus BS_TARGET, STATUS1_ANY, BattleScript_ButItFailed
jumpifterrainaffected BS_TARGET, STATUS_FIELD_MISTY_TERRAIN, BattleScript_MistyTerrainPrevents
accuracycheck BattleScript_ButItFailed, ACC_CURR_MOVE
jumpifsafeguard BattleScript_SafeguardProtected
attackanimation
waitanimation
seteffectprimary MOVE_EFFECT_PARALYSIS
resultmessage
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_VoltAbsorbHeal:
copybyte gBattlerAbility, gBattlerTarget
tryhealquarterhealth BS_TARGET BattleScript_MonMadeMoveUseless @ Check if max hp
goto BattleScript_MoveHPDrain
BattleScript_AlreadyParalyzed:
BattleScript_AlreadyParalyzed::
setalreadystatusedmoveattempt BS_ATTACKER
pause B_WAIT_TIME_SHORT
printstring STRINGID_PKMNISALREADYPARALYZED
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_LimberProtected::
copybyte gEffectBattler, gBattlerTarget
setbyte cMULTISTRING_CHOOSER, B_MSG_ABILITY_PREVENTS_MOVE_STATUS
call BattleScript_PRLZPrevention
goto BattleScript_MoveEnd
BattleScript_PowerHerbActivation:
playanimation BS_ATTACKER, B_ANIM_HELD_ITEM_EFFECT
printstring STRINGID_POWERHERB
@ -4786,35 +4672,17 @@ BattleScript_FlatterTryConfuse::
seteffectprimary MOVE_EFFECT_CONFUSION
goto BattleScript_MoveEnd
BattleScript_EffectWillOWisp::
BattleScript_EffectNonVolatileStatus::
attackcanceler
attackstring
ppreduce
jumpifsubstituteblocks BattleScript_ButItFailed
jumpifstatus BS_TARGET, STATUS1_BURN, BattleScript_AlreadyBurned
jumpiftype BS_TARGET, TYPE_FIRE, BattleScript_NotAffected
jumpifability BS_TARGET, ABILITY_WATER_VEIL, BattleScript_WaterVeilPrevents
jumpifability BS_TARGET, ABILITY_WATER_BUBBLE, BattleScript_WaterVeilPrevents
jumpifability BS_TARGET, ABILITY_COMATOSE, BattleScript_AbilityProtectsDoesntAffect
jumpifability BS_TARGET, ABILITY_PURIFYING_SALT, BattleScript_AbilityProtectsDoesntAffect
jumpifability BS_TARGET, ABILITY_THERMAL_EXCHANGE, BattleScript_AbilityProtectsDoesntAffect
jumpifflowerveil BattleScript_FlowerVeilProtects
jumpifleafguardprotected BS_TARGET, BattleScript_AbilityProtectsDoesntAffect
jumpifshieldsdown BS_TARGET, BattleScript_AbilityProtectsDoesntAffect
jumpifstatus BS_TARGET, STATUS1_ANY, BattleScript_ButItFailed
jumpifterrainaffected BS_TARGET, STATUS_FIELD_MISTY_TERRAIN, BattleScript_MistyTerrainPrevents
trynonvolatilestatus
accuracycheck BattleScript_ButItFailed, ACC_CURR_MOVE
jumpifsafeguard BattleScript_SafeguardProtected
attackanimation
waitanimation
seteffectprimary MOVE_EFFECT_BURN
goto BattleScript_MoveEnd
BattleScript_WaterVeilPrevents::
call BattleScript_AbilityPopUp
copybyte gEffectBattler, gBattlerTarget
setbyte cMULTISTRING_CHOOSER, B_MSG_ABILITY_PREVENTS_MOVE_STATUS
call BattleScript_BRNPrevention
setnonvolatilestatus
resultmessage
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_AlreadyBurned::
@ -5068,18 +4936,8 @@ BattleScript_EffectYawn::
attackcanceler
attackstring
ppreduce
jumpifability BS_TARGET, ABILITY_VITAL_SPIRIT, BattleScript_PrintBattlerAbilityMadeIneffective
jumpifability BS_TARGET, ABILITY_INSOMNIA, BattleScript_PrintBattlerAbilityMadeIneffective
jumpifability BS_TARGET, ABILITY_COMATOSE, BattleScript_PrintBattlerAbilityMadeIneffective
jumpifability BS_TARGET, ABILITY_PURIFYING_SALT, BattleScript_AbilityProtectsDoesntAffect
jumpifflowerveil BattleScript_FlowerVeilProtects
jumpifleafguardprotected BS_TARGET, BattleScript_AbilityProtectsDoesntAffect
jumpifshieldsdown BS_TARGET, BattleScript_AbilityProtectsDoesntAffect
jumpifsleepclause BattleScript_SleepClauseBlocked
jumpifsubstituteblocks BattleScript_ButItFailed
jumpifsafeguard BattleScript_SafeguardProtected
trynonvolatilestatus
accuracycheck BattleScript_ButItFailed, NO_ACC_CALC_CHECK_LOCK_ON
jumpifuproarwakes BattleScript_ButItFailed
setyawn BattleScript_ButItFailed
attackanimation
waitanimation
@ -5087,8 +4945,7 @@ BattleScript_EffectYawnSuccess::
printstring STRINGID_PKMNWASMADEDROWSY
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_PrintBattlerAbilityMadeIneffective::
copybyte sBATTLER, gBattlerAbility
BattleScript_PrintAbilityMadeIneffective::
pause B_WAIT_TIME_SHORT
call BattleScript_AbilityPopUp
@ -7456,9 +7313,9 @@ BattleScript_AbilityRaisesDefenderStat::
waitmessage B_WAIT_TIME_LONG
return
BattleScript_AbilityPopUpTarget:
BattleScript_AbilityPopUpTarget::
copybyte gBattlerAbility, gBattlerTarget
BattleScript_AbilityPopUp:
BattleScript_AbilityPopUp::
.if B_ABILITY_POP_UP == TRUE
showabilitypopup BS_ABILITY_BATTLER
pause 40
@ -8198,24 +8055,6 @@ BattleScript_ItemNoStatLoss::
waitmessage B_WAIT_TIME_LONG
return
BattleScript_BRNPrevention::
pause B_WAIT_TIME_SHORT
printfromtable gBRNPreventionStringIds
waitmessage B_WAIT_TIME_LONG
return
BattleScript_PRLZPrevention::
pause B_WAIT_TIME_SHORT
printfromtable gPRLZPreventionStringIds
waitmessage B_WAIT_TIME_LONG
return
BattleScript_PSNPrevention::
pause B_WAIT_TIME_SHORT
printfromtable gPSNPreventionStringIds
waitmessage B_WAIT_TIME_LONG
return
BattleScript_ObliviousPreventsAttraction::
pause B_WAIT_TIME_SHORT
call BattleScript_AbilityPopUp

View File

@ -175,7 +175,6 @@ bool32 IsHazardClearingMove(u32 move);
bool32 IsSubstituteEffect(enum BattleMoveEffects effect);
// status checks
bool32 AI_CanGetFrostbite(u32 battler, u32 ability);
bool32 AI_CanBeConfused(u32 battlerAtk, u32 battlerDef, u32 move, u32 ability);
bool32 IsBattlerIncapacitated(u32 battler, u32 ability);
bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove);
@ -183,9 +182,9 @@ bool32 ShouldPoison(u32 battlerAtk, u32 battlerDef);
bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove);
bool32 AI_CanParalyze(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove);
bool32 AI_CanConfuse(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove);
bool32 ShouldBurn(u32 battlerAtk, u32 battlerDef);
bool32 ShouldFreezeOrFrostbite(u32 battlerAtk, u32 battlerDef);
bool32 ShouldParalyze(u32 battlerAtk, u32 battlerDef);
bool32 ShouldBurn(u32 battlerAtk, u32 battlerDef, u32 abilityDef);
bool32 ShouldFreezeOrFrostbite(u32 battlerAtk, u32 battlerDef, u32 abilityDef);
bool32 ShouldParalyze(u32 battlerAtk, u32 battlerDef, u32 abilityDef);
bool32 AI_CanBurn(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove);
bool32 AI_CanGiveFrostbite(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove);
bool32 AI_CanBeInfatuated(u32 battlerAtk, u32 battlerDef, u32 defAbility);

View File

@ -39,8 +39,6 @@ void HandleBattleWindow(u8 xStart, u8 yStart, u8 xEnd, u8 yEnd, u8 flags);
bool8 UproarWakeUpCheck(u8 battlerId);
bool32 DoesSubstituteBlockMove(u32 battlerAtk, u32 battlerDef, u32 move);
bool32 DoesDisguiseBlockMove(u32 battler, u32 move);
bool32 CanPoisonType(u8 battlerAttacker, u8 battlerTarget);
bool32 CanParalyzeType(u8 battlerAttacker, u8 battlerTarget);
bool32 CanUseLastResort(u8 battlerId);
u32 IsFlowerVeilProtected(u32 battler);
u32 IsLeafGuardProtected(u32 battler, u32 ability);
@ -60,6 +58,7 @@ bool32 IsMoveAffectedByParentalBond(u32 move, u32 battler);
void SaveBattlerTarget(u32 battler);
void SaveBattlerAttacker(u32 battler);
bool32 CanBurnHitThaw(u16 move);
void SetNonVolatileStatusCondition(u32 target, enum MoveEffects effect);
extern void (* const gBattleScriptingCommandsTable[])(void);
extern const struct StatFractions gAccuracyStageRatios[];

View File

@ -186,9 +186,6 @@ extern const u8 BattleScript_FlashFireBoost[];
extern const u8 BattleScript_AbilityNoStatLoss[];
extern const u8 BattleScript_ItemNoStatLoss[];
extern const u8 BattleScript_ItemNoStatLossSpicyExtract[];
extern const u8 BattleScript_BRNPrevention[];
extern const u8 BattleScript_PRLZPrevention[];
extern const u8 BattleScript_PSNPrevention[];
extern const u8 BattleScript_ObliviousPreventsAttraction[];
extern const u8 BattleScript_FlinchPrevention[];
extern const u8 BattleScript_OwnTempoPrevents[];
@ -519,6 +516,18 @@ extern const u8 BattleScript_TeraShellDistortingTypeMatchups[];
extern const u8 BattleScript_TeraFormChange[];
extern const u8 BattleScript_SleepClausePreventsEnd[];
extern const u8 BattleScript_AbilityProtectsDoesntAffect[];
extern const u8 BattleScript_ImmunityProtected[];
extern const u8 BattleScript_SafeguardProtected[];
extern const u8 BattleScript_FlowerVeilProtects[];
extern const u8 BattleScript_SleepClauseBlocked[];
extern const u8 BattleScript_AlreadyAsleep[];
extern const u8 BattleScript_CantMakeAsleep[];
extern const u8 BattleScript_AlreadyPoisoned[];
extern const u8 BattleScript_AlreadyParalyzed[];
extern const u8 BattleScript_AlreadyBurned[];
extern const u8 BattleScript_PrintAbilityMadeIneffective[];
// zmoves
extern const u8 BattleScript_ZMoveActivateDamaging[];
extern const u8 BattleScript_ZMoveActivateStatus[];
@ -565,7 +574,6 @@ extern const u8 BattleScript_DynamaxEnds_Ret[];
extern const u8 BattleScript_MoveBlockedByDynamax[];
// Battle move scripts
extern const u8 BattleScript_EffectSleep[];
extern const u8 BattleScript_EffectAbsorb[];
extern const u8 BattleScript_EffectAbsorbLiquidOoze[];
extern const u8 BattleScript_EffectExplosion[];
@ -592,7 +600,6 @@ extern const u8 BattleScript_EffectRoar[];
extern const u8 BattleScript_EffectHit[];
extern const u8 BattleScript_EffectConversion[];
extern const u8 BattleScript_EffectRestoreHp[];
extern const u8 BattleScript_EffectToxic[];
extern const u8 BattleScript_EffectLightScreen[];
extern const u8 BattleScript_EffectRest[];
extern const u8 BattleScript_EffectOHKO[];
@ -617,8 +624,6 @@ extern const u8 BattleScript_EffectSpecialDefenseDown2[];
extern const u8 BattleScript_EffectAccuracyDown2[];
extern const u8 BattleScript_EffectEvasionDown2[];
extern const u8 BattleScript_EffectReflect[];
extern const u8 BattleScript_EffectPoison[];
extern const u8 BattleScript_EffectParalyze[];
extern const u8 BattleScript_EffectTwoTurnsAttack[];
extern const u8 BattleScript_EffectSubstitute[];
extern const u8 BattleScript_EffectRage[];
@ -688,7 +693,7 @@ extern const u8 BattleScript_EffectWorrySeed[];
extern const u8 BattleScript_EffectHail[];
extern const u8 BattleScript_EffectTorment[];
extern const u8 BattleScript_EffectFlatter[];
extern const u8 BattleScript_EffectWillOWisp[];
extern const u8 BattleScript_EffectNonVolatileStatus[];
extern const u8 BattleScript_EffectMemento[];
extern const u8 BattleScript_EffectFocusPunch[];
extern const u8 BattleScript_EffectFollowMe[];
@ -858,5 +863,7 @@ extern const u8 BattleScript_EffectFickleBeam[];
extern const u8 BattleScript_FickleBeamDoubled[];
extern const u8 BattleScript_QuestionForfeitBattle[];
extern const u8 BattleScript_ForfeitBattleGaveMoney[];
extern const u8 BattleScript_EffectNonVolatileStatus[];
extern const u8 BattleScript_AbilityPopUp[];
#endif // GUARD_BATTLE_SCRIPTS_H

View File

@ -23,6 +23,12 @@
#define MOVE_LIMITATION_PLACEHOLDER (1 << 15)
#define MOVE_LIMITATIONS_ALL 0xFFFF
enum NonVolatileStatus
{
STATUS_CHECK_TRIGGER,
STATUS_RUN_SCRIPT,
};
enum AbilityEffectOptions
{
ABILITY_CHECK_TRIGGER,
@ -331,12 +337,13 @@ bool32 MoveHasChargeTurnAdditionalEffect(u32 move);
bool32 CanTargetPartner(u32 battlerAtk, u32 battlerDef);
bool32 TargetFullyImmuneToCurrMove(u32 battlerAtk, u32 battlerDef);
bool32 CanBeSlept(u32 battler, u32 ability, enum SleepClauseBlock isBlockedBySleepClause);
bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 defAbility);
bool32 CanBeBurned(u32 battler, u32 ability);
bool32 CanBeParalyzed(u32 battler, u32 ability);
bool32 CanBeFrozen(u32 battler);
bool32 CanGetFrostbite(u32 battler);
bool32 CanBeSlept(u32 battlerAtk, u32 battlerDef, u32 abilityDef, enum SleepClauseBlock isBlockedBySleepClause);
bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 abilityDef);
bool32 CanBeBurned(u32 battlerAtk, u32 battlerDef, u32 ability);
bool32 CanBeParalyzed(u32 battlerAtk, u32 battlerDef, u32 abilityDef);
bool32 CanBeFrozen(u32 battlerAtk, u32 battlerDef, u32 abilityDef);
bool32 CanGetFrostbite(u32 battlerAtk, u32 battlerDef, u32 abilityDef);
bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 abilityDef, enum MoveEffects secondaryMoveEffect, enum NonVolatileStatus option);
bool32 CanBeConfused(u32 battler);
bool32 IsBattlerTerrainAffected(u32 battler, u32 terrainFlag);
u32 GetBattlerAffectionHearts(u32 battler);

View File

@ -206,7 +206,7 @@
#define HITMARKER_NO_PPDEDUCT (1 << 11)
#define HITMARKER_UNUSED_2 (1 << 12)
#define HITMARKER_STATUS_ABILITY_EFFECT (1 << 13)
#define HITMARKER_SYNCHRONISE_EFFECT (1 << 14)
#define HITMARKER_SYNCHRONIZE_EFFECT (1 << 14)
#define HITMARKER_RUN (1 << 15)
#define HITMARKER_IGNORE_DISGUISE (1 << 16)
#define HITMARKER_DISABLE_ANIMATION (1 << 17) // disable animations during battle scripts, e.g. for Bug Bite

View File

@ -170,11 +170,9 @@ enum CmdVarious
VARIOUS_JUMP_IF_SHIELDS_DOWN_PROTECTED,
VARIOUS_TRY_FAIRY_LOCK,
VARIOUS_JUMP_IF_NO_ALLY,
VARIOUS_POISON_TYPE_IMMUNITY,
VARIOUS_JUMP_IF_HOLD_EFFECT,
VARIOUS_INFATUATE_WITH_BATTLER,
VARIOUS_SET_LAST_USED_ITEM,
VARIOUS_PARALYZE_TYPE_IMMUNITY,
VARIOUS_JUMP_IF_ABSENT,
VARIOUS_DESTROY_ABILITY_POPUP,
VARIOUS_TOTEM_BOOST,

View File

@ -1046,12 +1046,15 @@ enum GotStatusedStringID
B_MSG_STATUSED_BY_ABILITY,
};
// gBRNPreventionStringIds / gPRLZPreventionStringIds / gPSNPreventionStringIds
// gStatusPreventionStringIds
enum StatusPreventionStringID
{
B_MSG_ABILITY_PREVENTS_MOVE_STATUS,
B_MSG_ABILITY_PREVENTS_MOVE_BURN,
B_MSG_ABILITY_PREVENTS_MOVE_PARALYSIS,
B_MSG_ABILITY_PREVENTS_MOVE_POISON,
B_MSG_ABILITY_PREVENTS_ABILITY_STATUS,
B_MSG_STATUS_HAD_NO_EFFECT,
B_MSG_ABILITY_PASTEL_VEIL,
};
// gGotDefrostedStringIds

View File

@ -145,6 +145,7 @@ struct MoveInfo
u32 fixedDamage;
u32 absorbPercentage;
u32 recoilPercentage;
u32 nonVolatileStatus;
} argument;
// primary/secondary effects
@ -516,6 +517,11 @@ static inline u32 GetMoveRecoil(u32 moveId)
return gMovesInfo[SanitizeMoveId(moveId)].argument.recoilPercentage;
}
static inline u32 GetMoveNonVolatileStatus(u32 moveId)
{
return gMovesInfo[SanitizeMoveId(moveId)].argument.nonVolatileStatus;
}
static inline const struct AdditionalEffect *GetMoveAdditionalEffectById(u32 moveId, u32 effect)
{
return &gMovesInfo[SanitizeMoveId(moveId)].additionalEffects[effect];

View File

@ -1518,7 +1518,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_PARALYZE:
if (!AI_CanParalyze(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, aiData->partnerMove))
ADJUST_SCORE(-10);
if (!ShouldParalyze(battlerAtk, battlerDef))
if (!ShouldParalyze(battlerAtk, battlerDef, aiData->abilities[battlerDef]))
ADJUST_SCORE(-5);
break;
case EFFECT_SUBSTITUTE:
@ -1812,7 +1812,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_WILL_O_WISP:
if (!AI_CanBurn(battlerAtk, battlerDef, aiData->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove))
ADJUST_SCORE(-10);
if (!ShouldBurn(battlerAtk, battlerDef))
if (!ShouldBurn(battlerAtk, battlerDef, aiData->abilities[battlerDef]))
ADJUST_SCORE(-5);
break;
case EFFECT_MEMENTO:
@ -1926,7 +1926,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-10);
break;
case EFFECT_REST:
if (!CanBeSlept(battlerAtk, aiData->abilities[battlerAtk], NOT_BLOCKED_BY_SLEEP_CLAUSE))
if (!CanBeSlept(battlerAtk, battlerAtk, aiData->abilities[battlerAtk], NOT_BLOCKED_BY_SLEEP_CLAUSE))
ADJUST_SCORE(-10);
//fallthrough
case EFFECT_RESTORE_HP:
@ -2434,25 +2434,14 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
switch (effect)
{
case MOVE_EFFECT_BURN:
if (!AI_CanBurn(battlerAtk, battlerDef, BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove))
ADJUST_SCORE(-10);
break;
case MOVE_EFFECT_PARALYSIS:
if (!AI_CanParalyze(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, aiData->partnerMove))
ADJUST_SCORE(-10);
break;
case MOVE_EFFECT_POISON:
if (!AI_CanPoison(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, aiData->partnerMove))
ADJUST_SCORE(-10);
break;
case MOVE_EFFECT_TOXIC:
if (!AI_CanPoison(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, aiData->partnerMove))
ADJUST_SCORE(-10);
break;
case MOVE_EFFECT_FREEZE:
if (!CanBeFrozen(battlerDef, TRUE)
|| MoveBlockedBySubstitute(move, battlerAtk, battlerDef))
ADJUST_SCORE(-10);
break;
}*/
}
@ -3629,7 +3618,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
}
break;
case EFFECT_REST:
if (!(CanBeSlept(battlerAtk, aiData->abilities[battlerAtk], NOT_BLOCKED_BY_SLEEP_CLAUSE)))
if (!(CanBeSlept(battlerAtk, battlerAtk, aiData->abilities[battlerAtk], NOT_BLOCKED_BY_SLEEP_CLAUSE)))
{
break;
}
@ -4096,7 +4085,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_FLAME_ORB:
if (!ShouldBurn(battlerAtk, battlerAtk) && CanBeBurned(battlerAtk, aiData->abilities[battlerDef]))
if (!ShouldBurn(battlerAtk, battlerAtk, aiData->abilities[battlerAtk]))
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_BLACK_SLUDGE:
@ -4146,7 +4135,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_FLAME_ORB:
if (ShouldBurn(battlerAtk, battlerAtk))
if (ShouldBurn(battlerAtk, battlerAtk, aiData->abilities[battlerAtk]))
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_BLACK_SLUDGE:
@ -4802,7 +4791,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_FLAME_ORB:
if (ShouldBurn(battlerAtk, battlerAtk))
if (ShouldBurn(battlerAtk, battlerAtk, aiData->abilities[battlerAtk]))
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_BLACK_SLUDGE:

View File

@ -646,7 +646,7 @@ static bool32 ShouldSwitchIfBadlyStatused(u32 battler)
{
//Yawn
if (gStatuses3[battler] & STATUS3_YAWN
&& CanBeSlept(battler, monAbility, BLOCKED_BY_SLEEP_CLAUSE)
&& CanBeSlept(battler, battler, monAbility, BLOCKED_BY_SLEEP_CLAUSE) // TODO: ask for help from pawwkie
&& gBattleMons[battler].hp > gBattleMons[battler].maxHP / 3
&& RandomPercentage(RNG_AI_SWITCH_YAWN, GetSwitchChance(SHOULD_SWITCH_YAWN)))
{

View File

@ -917,7 +917,7 @@ static bool32 AI_IsMoveEffectInPlus(u32 battlerAtk, u32 battlerDef, u32 move, s3
return TRUE;
break;
case MOVE_EFFECT_FREEZE_OR_FROSTBITE:
if (AI_CanGetFrostbite(battlerDef, abilityDef))
if (CanBeFrozen(battlerAtk, battlerDef, abilityDef))
return TRUE;
break;
case MOVE_EFFECT_PARALYSIS:
@ -3109,7 +3109,7 @@ bool32 IsBattlerIncapacitated(u32 battler, u32 ability)
bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove)
{
if (!CanBeSlept(battlerDef, defAbility, BLOCKED_BY_SLEEP_CLAUSE)
if (!CanBeSlept(battlerAtk, battlerDef, defAbility, BLOCKED_BY_SLEEP_CLAUSE)
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|| PartnerMoveEffectIsStatusSameTarget(BATTLE_PARTNER(battlerAtk), battlerDef, partnerMove)) // shouldn't try to sleep mon that partner is trying to make sleep
return FALSE;
@ -3130,12 +3130,12 @@ static inline bool32 DoesBattlerBenefitFromAllVolatileStatus(u32 battler, u32 ab
bool32 ShouldPoison(u32 battlerAtk, u32 battlerDef)
{
u32 defAbility = AI_DATA->abilities[battlerDef];
u32 abilityDef = AI_DATA->abilities[battlerDef];
// Battler can be poisoned and has move/ability that synergizes with being poisoned
if (CanBePoisoned(battlerAtk, battlerDef, defAbility) && (
DoesBattlerBenefitFromAllVolatileStatus(battlerDef, defAbility)
|| defAbility == ABILITY_POISON_HEAL
|| (defAbility == ABILITY_TOXIC_BOOST && HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL))))
if (CanBePoisoned(battlerAtk, battlerDef, AI_DATA->abilities[battlerAtk], abilityDef) && (
DoesBattlerBenefitFromAllVolatileStatus(battlerDef, abilityDef)
|| abilityDef == ABILITY_POISON_HEAL
|| (abilityDef == ABILITY_TOXIC_BOOST && HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL))))
{
if (battlerAtk == battlerDef) // Targeting self
return TRUE;
@ -3148,14 +3148,13 @@ bool32 ShouldPoison(u32 battlerAtk, u32 battlerDef)
return TRUE;
}
bool32 ShouldBurn(u32 battlerAtk, u32 battlerDef)
bool32 ShouldBurn(u32 battlerAtk, u32 battlerDef, u32 abilityDef)
{
u32 defAbility = AI_DATA->abilities[battlerDef];
// Battler can be burned and has move/ability that synergizes with being burned
if (CanBeBurned(battlerDef, defAbility) && (
DoesBattlerBenefitFromAllVolatileStatus(battlerDef, defAbility)
|| defAbility == ABILITY_HEATPROOF
|| (defAbility == ABILITY_FLARE_BOOST && HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL))))
if (CanBeBurned(battlerAtk, battlerDef, abilityDef) && (
DoesBattlerBenefitFromAllVolatileStatus(battlerDef, abilityDef)
|| abilityDef == ABILITY_HEATPROOF
|| (abilityDef == ABILITY_FLARE_BOOST && HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL))))
{
if (battlerAtk == battlerDef) // Targeting self
return TRUE;
@ -3169,11 +3168,11 @@ bool32 ShouldBurn(u32 battlerAtk, u32 battlerDef)
return TRUE;
}
bool32 ShouldFreezeOrFrostbite(u32 battlerAtk, u32 battlerDef)
bool32 ShouldFreezeOrFrostbite(u32 battlerAtk, u32 battlerDef, u32 abilityDef)
{
if (!B_USE_FROSTBITE)
{
if (CanBeFrozen(battlerDef))
if (CanBeFrozen(battlerAtk, battlerDef, abilityDef))
{
if (battlerAtk == battlerDef) // Targeting self
return FALSE;
@ -3184,17 +3183,16 @@ bool32 ShouldFreezeOrFrostbite(u32 battlerAtk, u32 battlerDef)
}
else
{
u32 defAbility = AI_DATA->abilities[battlerDef];
// Battler can be frostbitten and has move/ability that synergizes with being frostbitten
if (CanBeFrozen(battlerDef) &&
DoesBattlerBenefitFromAllVolatileStatus(battlerDef, defAbility))
if (CanBeFrozen(battlerAtk, battlerDef, abilityDef) &&
DoesBattlerBenefitFromAllVolatileStatus(battlerDef, abilityDef))
{
if (battlerAtk == battlerDef) // Targeting self
return TRUE;
else
return FALSE;
}
if (battlerAtk == battlerDef)
return FALSE;
else
@ -3202,12 +3200,11 @@ bool32 ShouldFreezeOrFrostbite(u32 battlerAtk, u32 battlerDef)
}
}
bool32 ShouldParalyze(u32 battlerAtk, u32 battlerDef)
bool32 ShouldParalyze(u32 battlerAtk, u32 battlerDef, u32 abilityDef)
{
u32 defAbility = AI_DATA->abilities[battlerDef];
// Battler can be paralyzed and has move/ability that synergizes with being paralyzed
if (CanBeParalyzed(battlerDef, defAbility) && (
DoesBattlerBenefitFromAllVolatileStatus(battlerDef, defAbility)))
if (CanBeParalyzed(battlerAtk, battlerDef, abilityDef) && (
DoesBattlerBenefitFromAllVolatileStatus(battlerDef, abilityDef)))
{
if (battlerAtk == battlerDef) // Targeting self
return TRUE;
@ -3222,24 +3219,19 @@ bool32 ShouldParalyze(u32 battlerAtk, u32 battlerDef)
bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove)
{
if (!CanBePoisoned(battlerAtk, battlerDef, GetBattlerAbility(battlerDef))
if (!CanBePoisoned(battlerAtk, battlerDef, AI_DATA->abilities[battlerAtk], defAbility)
|| AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == UQ_4_12(0.0)
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|| PartnerMoveEffectIsStatusSameTarget(BATTLE_PARTNER(battlerAtk), battlerDef, partnerMove))
return FALSE;
else if (defAbility != ABILITY_CORROSION && IS_BATTLER_ANY_TYPE(battlerDef, TYPE_POISON, TYPE_STEEL))
return FALSE;
else if (IsValidDoubleBattle(battlerAtk) && AI_DATA->abilities[BATTLE_PARTNER(battlerDef)] == ABILITY_PASTEL_VEIL)
return FALSE;
return TRUE;
}
bool32 AI_CanParalyze(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove)
{
if (!CanBeParalyzed(battlerDef, defAbility)
if (!CanBeParalyzed(battlerAtk, battlerDef, defAbility)
|| AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == UQ_4_12(0.0)
|| gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|| PartnerMoveEffectIsStatusSameTarget(BATTLE_PARTNER(battlerAtk), battlerDef, partnerMove))
return FALSE;
@ -3271,21 +3263,9 @@ bool32 AI_CanConfuse(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battler
return TRUE;
}
bool32 AI_CanGetFrostbite(u32 battler, u32 ability)
{
if (ability == ABILITY_MAGMA_ARMOR
|| ability == ABILITY_COMATOSE
|| IS_BATTLER_OF_TYPE(battler, TYPE_ICE)
|| gBattleMons[battler].status1 & STATUS1_ANY
|| IsAbilityStatusProtected(battler, ability)
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD)
return FALSE;
return TRUE;
}
bool32 AI_CanBurn(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove)
{
if (!CanBeBurned(battlerDef, defAbility)
if (!CanBeBurned(battlerAtk, battlerDef, defAbility)
|| AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == UQ_4_12(0.0)
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|| PartnerMoveEffectIsStatusSameTarget(battlerAtkPartner, battlerDef, partnerMove))
@ -3297,7 +3277,7 @@ bool32 AI_CanBurn(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtk
bool32 AI_CanGiveFrostbite(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove)
{
if (!AI_CanGetFrostbite(battlerDef, defAbility)
if (!CanBeFrozen(battlerAtk, battlerDef, defAbility)
|| AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == UQ_4_12(0.0)
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|| PartnerMoveEffectIsStatusSameTarget(battlerAtkPartner, battlerDef, partnerMove))

View File

@ -362,9 +362,9 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] =
[STRINGID_PKMNPREVENTSUSAGE] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} prevents {B_ATK_NAME_WITH_PREFIX2} from using {B_CURRENT_MOVE}!"), //I don't see this in SV text
[STRINGID_PKMNRESTOREDHPUSING] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} restored HP using its {B_DEF_ABILITY}!"), //not in gen 5+, ability popup
[STRINGID_PKMNCHANGEDTYPEWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} made it the {B_BUFF1} type!"), //not in gen 5+, ability popup
[STRINGID_PKMNPREVENTSPARALYSISWITH] = COMPOUND_STRING("{B_EFF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} prevents paralysis!"), //not in gen 5+, ability popup
[STRINGID_PKMNPREVENTSPARALYSISWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_LAST_ABILITY} prevents paralysis!"), //not in gen 5+, ability popup
[STRINGID_PKMNPREVENTSROMANCEWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} prevents romance!"), //not in gen 5+, ability popup
[STRINGID_PKMNPREVENTSPOISONINGWITH] = COMPOUND_STRING("{B_EFF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} prevents poisoning!"), //not in gen 5+, ability popup
[STRINGID_PKMNPREVENTSPOISONINGWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_LAST_ABILITY} prevents poisoning!"), //not in gen 5+, ability popup
[STRINGID_PKMNPREVENTSCONFUSIONWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} prevents confusion!"), //not in gen 5+, ability popup
[STRINGID_PKMNRAISEDFIREPOWERWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} raised the power of Fire-type moves!"), //not in gen 5+, ability popup
[STRINGID_PKMNANCHORSITSELFWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} anchors itself with {B_DEF_ABILITY}!"), //not in gen 5+, ability popup
@ -468,7 +468,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] =
[STRINGID_ITEMALLOWSONLYYMOVE] = COMPOUND_STRING("{B_LAST_ITEM} only allows the use of {B_CURRENT_MOVE}!\p"),
[STRINGID_PKMNHUNGONWITHX] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} hung on using its {B_LAST_ITEM}!"),
[STRINGID_EMPTYSTRING3] = gText_EmptyString3,
[STRINGID_PKMNSXPREVENTSBURNS] = COMPOUND_STRING("{B_EFF_NAME_WITH_PREFIX}'s {B_EFF_ABILITY} prevents burns!"), //not in gen 5+, ability popup
[STRINGID_PKMNSXPREVENTSBURNS] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_LAST_ABILITY} prevents burns!"), //not in gen 5+, ability popup
[STRINGID_PKMNSXBLOCKSY] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} blocks {B_CURRENT_MOVE}!"), //not in gen 5+, ability popup
[STRINGID_PKMNSXRESTOREDHPALITTLE2] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}'s {B_ATK_ABILITY} restored its HP a little!"), //not in gen 5+, ability popup
[STRINGID_PKMNSXWHIPPEDUPSANDSTORM] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ACTIVE_ABILITY} whipped up a sandstorm!"), //not in gen 5+, ability popup
@ -525,7 +525,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] =
[STRINGID_PKMNOBTAINEDX2] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} obtained {B_BUFF2}."),
[STRINGID_PKMNOBTAINEDXYOBTAINEDZ] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} obtained {B_BUFF1}.\p{B_DEF_NAME_WITH_PREFIX} obtained {B_BUFF2}."),
[STRINGID_BUTNOEFFECT] = COMPOUND_STRING("But it had no effect!"),
[STRINGID_PKMNSXHADNOEFFECTONY] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ACTIVE_ABILITY} had no effect on {B_EFF_NAME_WITH_PREFIX2}!"), //not in gen 5+, ability popup
[STRINGID_PKMNSXHADNOEFFECTONY] = COMPOUND_STRING("Target protected by {B_LAST_ABILITY}!"), //not in gen 5+, ability popup
[STRINGID_TWOENEMIESDEFEATED] = sText_TwoInGameTrainersDefeated,
[STRINGID_TRAINER2LOSETEXT] = COMPOUND_STRING("{B_TRAINER2_LOSE_TEXT}"),
[STRINGID_PKMNINCAPABLEOFPOWER] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} appears incapable of using its power!"),
@ -1333,25 +1333,14 @@ const u16 gBerryEffectStringIds[] =
[B_MSG_NORMALIZED_STATUS] = STRINGID_PKMNSITEMNORMALIZEDSTATUS
};
const u16 gBRNPreventionStringIds[] =
const u16 gStatusPreventionStringIds[] =
{
[B_MSG_ABILITY_PREVENTS_MOVE_STATUS] = STRINGID_PKMNSXPREVENTSBURNS,
[B_MSG_ABILITY_PREVENTS_MOVE_BURN] = STRINGID_PKMNSXPREVENTSBURNS,
[B_MSG_ABILITY_PREVENTS_MOVE_PARALYSIS] = STRINGID_PKMNPREVENTSPARALYSISWITH,
[B_MSG_ABILITY_PREVENTS_MOVE_POISON] = STRINGID_PKMNPREVENTSPOISONINGWITH,
[B_MSG_ABILITY_PREVENTS_ABILITY_STATUS] = STRINGID_PKMNSXPREVENTSYSZ,
[B_MSG_STATUS_HAD_NO_EFFECT] = STRINGID_PKMNSXHADNOEFFECTONY
};
const u16 gPRLZPreventionStringIds[] =
{
[B_MSG_ABILITY_PREVENTS_MOVE_STATUS] = STRINGID_PKMNPREVENTSPARALYSISWITH,
[B_MSG_ABILITY_PREVENTS_ABILITY_STATUS] = STRINGID_PKMNSXPREVENTSYSZ,
[B_MSG_STATUS_HAD_NO_EFFECT] = STRINGID_PKMNSXHADNOEFFECTONY
};
const u16 gPSNPreventionStringIds[] =
{
[B_MSG_ABILITY_PREVENTS_MOVE_STATUS] = STRINGID_PKMNPREVENTSPOISONINGWITH,
[B_MSG_ABILITY_PREVENTS_ABILITY_STATUS] = STRINGID_PKMNSXPREVENTSYSZ,
[B_MSG_STATUS_HAD_NO_EFFECT] = STRINGID_PKMNSXHADNOEFFECTONY
[B_MSG_STATUS_HAD_NO_EFFECT] = STRINGID_PKMNSXHADNOEFFECTONY,
[B_MSG_ABILITY_PASTEL_VEIL] = STRINGID_PASTELVEILPROTECTED
};
const u16 gItemSwapStringIds[] =

View File

@ -487,7 +487,7 @@ static void Cmd_statbuffchange(void);
static void Cmd_normalisebuffs(void);
static void Cmd_setbide(void);
static void Cmd_twoturnmoveschargestringandanimation(void);
static void Cmd_unused_0x8d(void);
static void Cmd_trynonvolatilestatus(void);
static void Cmd_initmultihitstring(void);
static void Cmd_forcerandomswitch(void);
static void Cmd_tryconversiontypechange(void);
@ -599,7 +599,7 @@ static void Cmd_settelekinesis(void);
static void Cmd_swapstatstages(void);
static void Cmd_averagestats(void);
static void Cmd_jumpifcaptivateaffected(void);
static void Cmd_unused(void);
static void Cmd_setnonvolatilestatus(void);
static void Cmd_tryworryseed(void);
static void Cmd_callnative(void);
@ -746,7 +746,7 @@ void (* const gBattleScriptingCommandsTable[])(void) =
Cmd_normalisebuffs, //0x8A
Cmd_setbide, //0x8B
Cmd_twoturnmoveschargestringandanimation, //0x8C
Cmd_unused_0x8d, //0x8D
Cmd_trynonvolatilestatus, //0x8D
Cmd_initmultihitstring, //0x8E
Cmd_forcerandomswitch, //0x8F
Cmd_tryconversiontypechange, //0x90
@ -858,7 +858,7 @@ void (* const gBattleScriptingCommandsTable[])(void) =
Cmd_swapstatstages, //0xFA
Cmd_averagestats, //0xFB
Cmd_jumpifcaptivateaffected, //0xFC
Cmd_unused, //0xFD
Cmd_setnonvolatilestatus, //0xFD
Cmd_tryworryseed, //0xFE
Cmd_callnative, //0xFF
};
@ -3190,6 +3190,64 @@ static inline bool32 TrySetLightScreen(u32 battler)
return FALSE;
}
void SetNonVolatileStatusCondition(u32 effectBattler, enum MoveEffects effect)
{
if (effect == MOVE_EFFECT_SLEEP
|| effect == MOVE_EFFECT_FREEZE)
{
const u8 *cancelMultiTurnMovesResult = NULL;
cancelMultiTurnMovesResult = CancelMultiTurnMoves(effectBattler, SKY_DROP_STATUS_FREEZE_SLEEP);
if (cancelMultiTurnMovesResult)
gBattlescriptCurrInstr = cancelMultiTurnMovesResult;
}
BattleScriptPush(gBattlescriptCurrInstr + 1);
if (sStatusFlagsForMoveEffects[effect] == STATUS1_SLEEP)
{
if (B_SLEEP_TURNS >= GEN_5)
gBattleMons[effectBattler].status1 |= STATUS1_SLEEP_TURN(1 + RandomUniform(RNG_SLEEP_TURNS, 1, 3));
else
gBattleMons[effectBattler].status1 |= STATUS1_SLEEP_TURN(1 + RandomUniform(RNG_SLEEP_TURNS, 2, 5));
TryActivateSleepClause(effectBattler, gBattlerPartyIndexes[effectBattler]);
}
else
{
gBattleMons[effectBattler].status1 |= sStatusFlagsForMoveEffects[effect];
}
gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[effect];
BtlController_EmitSetMonData(effectBattler, BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[effectBattler].status1), &gBattleMons[effectBattler].status1);
MarkBattlerForControllerExec(effectBattler);
if (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUSED_BY_ABILITY;
gHitMarker &= ~HITMARKER_STATUS_ABILITY_EFFECT;
}
else
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUSED;
}
gBattleScripting.moveEffect = MOVE_EFFECT_NONE;
// for synchronize
if (effect == MOVE_EFFECT_POISON
|| effect == MOVE_EFFECT_TOXIC
|| effect == MOVE_EFFECT_PARALYSIS
|| effect == MOVE_EFFECT_BURN)
{
gBattleStruct->synchronizeMoveEffect = effect;
gHitMarker |= HITMARKER_SYNCHRONIZE_EFFECT;
}
if (effect == MOVE_EFFECT_POISON || effect == MOVE_EFFECT_TOXIC)
gBattleStruct->poisonPuppeteerConfusion = TRUE;
}
#define INCREMENT_RESET_RETURN \
{ \
gBattlescriptCurrInstr++; \
@ -3197,12 +3255,6 @@ static inline bool32 TrySetLightScreen(u32 battler)
return; \
}
#define RESET_RETURN \
{ \
gBattleScripting.moveEffect = 0; \
return; \
}
void SetMoveEffect(bool32 primary, bool32 certain)
{
s32 i, affectsUser = 0;
@ -3210,7 +3262,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
bool32 mirrorArmorReflected = (GetBattlerAbility(gBattlerTarget) == ABILITY_MIRROR_ARMOR);
u32 flags = 0;
u32 battlerAbility;
bool8 activateAfterFaint = FALSE;
bool32 activateAfterFaint = FALSE;
// NULL move effect
if (gBattleScripting.moveEffect == 0)
@ -3284,287 +3336,24 @@ void SetMoveEffect(bool32 primary, bool32 certain)
if (gBattleScripting.moveEffect <= PRIMARY_STATUS_MOVE_EFFECT) // status change
{
const u8 *cancelMultiTurnMovesResult = NULL;
switch (sStatusFlagsForMoveEffects[gBattleScripting.moveEffect])
if (!(gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)) // Calcs already done
{
case STATUS1_SLEEP:
// check active uproar
if (battlerAbility != ABILITY_SOUNDPROOF || B_UPROAR_IGNORE_SOUNDPROOF >= GEN_5)
{
for (i = 0; i < gBattlersCount && !(gBattleMons[i].status2 & STATUS2_UPROAR); i++)
;
}
else
{
i = gBattlersCount;
}
if (i != gBattlersCount)
break;
if (!CanBeSlept(gEffectBattler, GetBattlerAbility(gEffectBattler), BLOCKED_BY_SLEEP_CLAUSE) && !gBattleStruct->battlerState[gEffectBattler].sleepClauseEffectExempt)
break;
cancelMultiTurnMovesResult = CancelMultiTurnMoves(gEffectBattler, SKY_DROP_STATUS_FREEZE_SLEEP);
if (cancelMultiTurnMovesResult)
gBattlescriptCurrInstr = cancelMultiTurnMovesResult;
statusChanged = TRUE;
break;
case STATUS1_POISON:
if ((battlerAbility == ABILITY_IMMUNITY || battlerAbility == ABILITY_PASTEL_VEIL)
&& (primary == TRUE || certain == TRUE))
{
gLastUsedAbility = battlerAbility;
RecordAbilityBattle(gEffectBattler, battlerAbility);
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_PSNPrevention;
if (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_ABILITY_STATUS;
gHitMarker &= ~HITMARKER_STATUS_ABILITY_EFFECT;
}
else
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_MOVE_STATUS;
}
RESET_RETURN
}
if (!CanPoisonType(gBattleScripting.battler, gEffectBattler)
&& (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
&& (primary == TRUE || certain == TRUE))
{
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_PSNPrevention;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUS_HAD_NO_EFFECT;
RESET_RETURN
}
if (!CanBePoisoned(gBattleScripting.battler, gEffectBattler, GetBattlerAbility(gEffectBattler)))
break;
statusChanged = TRUE;
break;
case STATUS1_BURN:
if ((battlerAbility == ABILITY_WATER_VEIL || battlerAbility == ABILITY_WATER_BUBBLE)
&& (primary == TRUE || certain == TRUE))
{
gLastUsedAbility = battlerAbility;
RecordAbilityBattle(gEffectBattler, battlerAbility);
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_BRNPrevention;
if (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_ABILITY_STATUS;
gHitMarker &= ~HITMARKER_STATUS_ABILITY_EFFECT;
}
else
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_MOVE_STATUS;
}
RESET_RETURN
}
if (IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_FIRE)
&& (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
&& (primary == TRUE || certain == TRUE))
{
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_BRNPrevention;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUS_HAD_NO_EFFECT;
RESET_RETURN
}
if (B_STATUS_TYPE_IMMUNITY == GEN_1)
{
u32 moveType = GetBattleMoveType(gCurrentMove);
if (primary == FALSE && certain == FALSE && IS_BATTLER_OF_TYPE(gEffectBattler, moveType))
break;
}
if (!CanBeBurned(gEffectBattler, GetBattlerAbility(gEffectBattler)))
break;
statusChanged = TRUE;
break;
case STATUS1_FREEZE:
if (B_STATUS_TYPE_IMMUNITY == GEN_1)
{
u32 moveType = GetBattleMoveType(gCurrentMove);
if (primary == FALSE && certain == FALSE && IS_BATTLER_OF_TYPE(gEffectBattler, moveType))
break;
}
if (!CanBeFrozen(gEffectBattler))
break;
cancelMultiTurnMovesResult = CancelMultiTurnMoves(gEffectBattler, SKY_DROP_STATUS_FREEZE_SLEEP);
if (cancelMultiTurnMovesResult)
gBattlescriptCurrInstr = cancelMultiTurnMovesResult;
statusChanged = TRUE;
break;
case STATUS1_PARALYSIS:
if (battlerAbility == ABILITY_LIMBER)
{
if (primary == TRUE || certain == TRUE)
{
gLastUsedAbility = ABILITY_LIMBER;
RecordAbilityBattle(gEffectBattler, ABILITY_LIMBER);
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_PRLZPrevention;
if (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_ABILITY_STATUS;
gHitMarker &= ~HITMARKER_STATUS_ABILITY_EFFECT;
}
else
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_MOVE_STATUS;
}
RESET_RETURN
}
else
{
break;
}
}
if (B_STATUS_TYPE_IMMUNITY == GEN_1)
{
u32 moveType = GetBattleMoveType(gCurrentMove);
if (primary == FALSE && certain == FALSE && IS_BATTLER_OF_TYPE(gEffectBattler, moveType))
break;
}
if (!CanParalyzeType(gBattleScripting.battler, gEffectBattler)
&& (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
&& (primary == TRUE || certain == TRUE))
{
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_PRLZPrevention;
gBattleCommunication[MULTISTRING_CHOOSER] = 2;
RESET_RETURN
}
if (!CanParalyzeType(gBattleScripting.battler, gEffectBattler))
break;
if (!CanBeParalyzed(gEffectBattler, GetBattlerAbility(gEffectBattler)))
break;
statusChanged = TRUE;
break;
case STATUS1_TOXIC_POISON:
if ((battlerAbility == ABILITY_IMMUNITY || battlerAbility == ABILITY_PASTEL_VEIL)
&& (primary == TRUE || certain == TRUE))
{
gLastUsedAbility = battlerAbility;
RecordAbilityBattle(gEffectBattler, battlerAbility);
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_PSNPrevention;
if (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_ABILITY_STATUS;
gHitMarker &= ~HITMARKER_STATUS_ABILITY_EFFECT;
}
else
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_MOVE_STATUS;
}
RESET_RETURN
}
if (!CanPoisonType(gBattleScripting.battler, gEffectBattler)
&& (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
&& (primary == TRUE || certain == TRUE))
{
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_PSNPrevention;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUS_HAD_NO_EFFECT;
RESET_RETURN
}
if (gBattleMons[gEffectBattler].status1)
break;
if (CanBePoisoned(gBattleScripting.battler, gEffectBattler, GetBattlerAbility(gEffectBattler)))
{
// It's redundant, because at this point we know the status1 value is 0.
gBattleMons[gEffectBattler].status1 &= ~STATUS1_TOXIC_POISON;
gBattleMons[gEffectBattler].status1 &= ~STATUS1_POISON;
statusChanged = TRUE;
break;
}
else
{
gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_DOESNT_AFFECT_FOE;
}
break;
case STATUS1_FROSTBITE:
if (B_STATUS_TYPE_IMMUNITY == GEN_1)
{
u32 moveType = GetBattleMoveType(gCurrentMove);
if (primary == FALSE && certain == FALSE && IS_BATTLER_OF_TYPE(gEffectBattler, moveType))
break;
}
if (!CanGetFrostbite(gEffectBattler))
break;
statusChanged = TRUE;
break;
statusChanged = CanSetNonVolatileStatus(gBattlerAttacker,
gEffectBattler,
GetBattlerAbility(gBattlerAttacker),
battlerAbility,
gBattleScripting.moveEffect,
STATUS_CHECK_TRIGGER);
}
if (statusChanged == TRUE)
if (statusChanged || gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
{
BattleScriptPush(gBattlescriptCurrInstr + 1);
if (sStatusFlagsForMoveEffects[gBattleScripting.moveEffect] == STATUS1_SLEEP)
{
if (B_SLEEP_TURNS >= GEN_5)
gBattleMons[gEffectBattler].status1 |= STATUS1_SLEEP_TURN(1 + RandomUniform(RNG_SLEEP_TURNS, 1, 3));
else
gBattleMons[gEffectBattler].status1 |= STATUS1_SLEEP_TURN(1 + RandomUniform(RNG_SLEEP_TURNS, 2, 5));
TryActivateSleepClause(gEffectBattler, gBattlerPartyIndexes[gEffectBattler]);
}
else
{
gBattleMons[gEffectBattler].status1 |= sStatusFlagsForMoveEffects[gBattleScripting.moveEffect];
}
gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleScripting.moveEffect];
BtlController_EmitSetMonData(gEffectBattler, BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gEffectBattler].status1), &gBattleMons[gEffectBattler].status1);
MarkBattlerForControllerExec(gEffectBattler);
if (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUSED_BY_ABILITY;
gHitMarker &= ~HITMARKER_STATUS_ABILITY_EFFECT;
}
else
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUSED;
}
// for synchronize
if (gBattleScripting.moveEffect == MOVE_EFFECT_POISON
|| gBattleScripting.moveEffect == MOVE_EFFECT_TOXIC
|| gBattleScripting.moveEffect == MOVE_EFFECT_PARALYSIS
|| gBattleScripting.moveEffect == MOVE_EFFECT_BURN)
{
gBattleStruct->synchronizeMoveEffect = gBattleScripting.moveEffect;
gHitMarker |= HITMARKER_SYNCHRONISE_EFFECT;
}
if (gBattleScripting.moveEffect == MOVE_EFFECT_POISON || gBattleScripting.moveEffect == MOVE_EFFECT_TOXIC)
gBattleStruct->poisonPuppeteerConfusion = TRUE;
return;
SetNonVolatileStatusCondition(gEffectBattler, gBattleScripting.moveEffect);
}
else if (statusChanged == FALSE)
else
{
gBattleScripting.moveEffect = 0;
gBattlescriptCurrInstr++;
return;
}
return;
}
@ -4574,7 +4363,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
case MOVE_EFFECT_YAWN_FOE:
{
if (!(gStatuses3[gBattlerTarget] & STATUS3_YAWN)
&& CanBeSlept(gBattlerTarget, GetBattlerAbility(gBattlerTarget), BLOCKED_BY_SLEEP_CLAUSE)
&& CanBeSlept(gBattlerTarget, gBattlerTarget, GetBattlerAbility(gBattlerTarget), BLOCKED_BY_SLEEP_CLAUSE)
&& RandomPercentage(RNG_G_MAX_SNOOZE, 50))
{
gStatuses3[gBattlerTarget] |= STATUS3_YAWN_TURN(2);
@ -5560,7 +5349,7 @@ static void MoveValuesCleanUp(void)
gBattleCommunication[MISS_TYPE] = 0;
if (!gMultiHitCounter)
gHitMarker &= ~HITMARKER_DESTINYBOND;
gHitMarker &= ~HITMARKER_SYNCHRONISE_EFFECT;
gHitMarker &= ~HITMARKER_SYNCHRONIZE_EFFECT;
}
static void Cmd_movevaluescleanup(void)
@ -6606,7 +6395,7 @@ static void Cmd_moveend(void)
// Not strictly a protect effect, but works the same way
if (gProtectStructs[gBattlerTarget].beakBlastCharge
&& CanBeBurned(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))
&& CanBeBurned(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker))
&& !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT))
{
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
@ -8455,8 +8244,7 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler)
}
else if (IsBattlerAffectedByHazards(battler, TRUE))
{
i = GetBattlerAbility(battler);
if (CanBePoisoned(gBattlerAttacker, battler, i))
if (CanBePoisoned(gBattlerAttacker, battler, GetBattlerAbility(gBattlerAttacker), GetBattlerAbility(battler)))
{
if (gSideTimers[GetBattlerSide(battler)].toxicSpikesAmount >= 2)
gBattleMons[battler].status1 |= STATUS1_TOXIC_POISON;
@ -9808,17 +9596,6 @@ static bool32 HasAttackerFaintedTarget(void)
return FALSE;
}
bool32 CanPoisonType(u8 battlerAttacker, u8 battlerTarget)
{
return GetBattlerAbility(battlerAttacker) == ABILITY_CORROSION
|| !IS_BATTLER_ANY_TYPE(battlerTarget, TYPE_POISON, TYPE_STEEL);
}
bool32 CanParalyzeType(u8 battlerAttacker, u8 battlerTarget)
{
return !(B_PARALYZE_ELECTRIC >= GEN_6 && IS_BATTLER_OF_TYPE(battlerTarget, TYPE_ELECTRIC));
}
bool32 CanUseLastResort(u8 battler)
{
u32 i;
@ -10396,24 +10173,6 @@ static void Cmd_various(void)
gBattleStruct->friskedAbility = FALSE;
break;
}
case VARIOUS_POISON_TYPE_IMMUNITY:
{
VARIOUS_ARGS(u8 target, const u8 *failInstr);
if (!CanPoisonType(battler, GetBattlerForBattleScript(cmd->target)))
gBattlescriptCurrInstr = cmd->failInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
return;
}
case VARIOUS_PARALYZE_TYPE_IMMUNITY:
{
VARIOUS_ARGS(u8 target, const u8 *failInstr);
if (!CanParalyzeType(battler, GetBattlerForBattleScript(cmd->target)))
gBattlescriptCurrInstr = cmd->failInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
return;
}
case VARIOUS_TRACE_ABILITY:
{
VARIOUS_ARGS();
@ -11182,17 +10941,17 @@ static void Cmd_various(void)
VARIOUS_ARGS(const u8 *failInstr, const u8 *sleepClauseFailInstr);
u32 targetAbility = GetBattlerAbility(gBattlerTarget);
// Psycho shift works
if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget, targetAbility))
if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), targetAbility))
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_TOXIC_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget, targetAbility))
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_TOXIC_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), targetAbility))
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_BURN) && CanBeBurned(gBattlerTarget, targetAbility))
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_BURN) && CanBeBurned(gBattlerAttacker, gBattlerTarget, targetAbility))
gBattleCommunication[MULTISTRING_CHOOSER] = 2;
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && CanBeParalyzed(gBattlerTarget, targetAbility))
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && CanBeParalyzed(gBattlerAttacker, gBattlerTarget, targetAbility))
gBattleCommunication[MULTISTRING_CHOOSER] = 3;
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) && CanBeSlept(gBattlerTarget, targetAbility, BLOCKED_BY_SLEEP_CLAUSE))
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) && CanBeSlept(gBattlerAttacker, gBattlerTarget, targetAbility, BLOCKED_BY_SLEEP_CLAUSE))
gBattleCommunication[MULTISTRING_CHOOSER] = 4;
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_FROSTBITE) && CanGetFrostbite(gBattlerTarget))
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_FROSTBITE) && CanBeFrozen(gBattlerAttacker, gBattlerTarget, targetAbility))
gBattleCommunication[MULTISTRING_CHOOSER] = 5;
else if (IsSleepClauseActiveForSide(GetBattlerSide(battler)))
{
@ -12818,8 +12577,27 @@ static void Cmd_twoturnmoveschargestringandanimation(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_unused_0x8d(void)
static void Cmd_trynonvolatilestatus(void)
{
CMD_ARGS();
bool32 canInflictStatus = TRUE;
if (!CanSetNonVolatileStatus(gBattlerAttacker,
gBattlerTarget,
GetBattlerAbility(gBattlerAttacker),
GetBattlerAbility(gBattlerTarget),
GetMoveNonVolatileStatus(gCurrentMove),
STATUS_RUN_SCRIPT))
canInflictStatus = FALSE;
if (canInflictStatus && DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove))
{
canInflictStatus = FALSE;
gBattlescriptCurrInstr = BattleScript_ButItFailed;
}
if (canInflictStatus)
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_initmultihitstring(void)
@ -16747,8 +16525,11 @@ static void Cmd_jumpifcaptivateaffected(void)
}
}
static void Cmd_unused(void)
static void Cmd_setnonvolatilestatus(void)
{
CMD_ARGS();
gEffectBattler = gBattlerTarget;
SetNonVolatileStatusCondition(gBattlerTarget, GetMoveNonVolatileStatus(gCurrentMove));
}
static void Cmd_tryworryseed(void)
@ -18622,7 +18403,7 @@ void BS_SwapStats(void)
static void TrySetParalysis(const u8 *nextInstr, const u8 *failInstr)
{
if (CanBeParalyzed(gBattlerTarget, GetBattlerAbility(gBattlerTarget)))
if (CanBeParalyzed(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget)))
{
gBattleMons[gBattlerTarget].status1 |= STATUS1_PARALYSIS;
gBattleCommunication[MULTISTRING_CHOOSER] = 3;
@ -18639,7 +18420,7 @@ static void TrySetParalysis(const u8 *nextInstr, const u8 *failInstr)
static void TrySetPoison(const u8 *nextInstr, const u8 *failInstr)
{
if (CanBePoisoned(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget)))
if (CanBePoisoned(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerAbility(gBattlerTarget)))
{
gBattleMons[gBattlerTarget].status1 |= STATUS1_POISON;
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
@ -18656,7 +18437,7 @@ static void TrySetPoison(const u8 *nextInstr, const u8 *failInstr)
static void TrySetSleep(const u8 *nextInstr, const u8 *failInstr)
{
if (CanBeSlept(gBattlerTarget, GetBattlerAbility(gBattlerTarget), BLOCKED_BY_SLEEP_CLAUSE))
if (CanBeSlept(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget), BLOCKED_BY_SLEEP_CLAUSE))
{
if (B_SLEEP_TURNS >= GEN_5)
gBattleMons[gBattlerTarget].status1 |= STATUS1_SLEEP_TURN((Random() % 3) + 2);

View File

@ -66,6 +66,8 @@ static void SetRandomMultiHitCounter();
static u32 GetBattlerItemHoldEffectParam(u32 battler, u32 item);
static bool32 CanBeInfinitelyConfused(u32 battler);
static bool32 IsAnyTargetAffected(u32 battlerAtk);
static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, u32 abilityDef, u32 abilityAffected, const u8 *battleScript, enum NonVolatileStatus option);
static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum NonVolatileStatus option);
ARM_FUNC NOINLINE static uq4_12_t PercentToUQ4_12(u32 percent);
ARM_FUNC NOINLINE static uq4_12_t PercentToUQ4_12_Floored(u32 percent);
@ -831,8 +833,7 @@ void HandleAction_NothingIsFainted(void)
gCurrentActionFuncId = gActionsByTurnOrder[gCurrentTurnActionNumber];
gHitMarker &= ~(HITMARKER_DESTINYBOND | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_ATTACKSTRING_PRINTED
| HITMARKER_NO_PPDEDUCT | HITMARKER_STATUS_ABILITY_EFFECT | HITMARKER_PASSIVE_DAMAGE
| HITMARKER_OBEYS | HITMARKER_SYNCHRONISE_EFFECT
| HITMARKER_CHARGING);
| HITMARKER_OBEYS | HITMARKER_SYNCHRONIZE_EFFECT | HITMARKER_CHARGING);
}
void HandleAction_ActionFinished(void)
@ -845,7 +846,7 @@ void HandleAction_ActionFinished(void)
SpecialStatusesClear();
gHitMarker &= ~(HITMARKER_DESTINYBOND | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_ATTACKSTRING_PRINTED
| HITMARKER_NO_PPDEDUCT | HITMARKER_STATUS_ABILITY_EFFECT | HITMARKER_PASSIVE_DAMAGE
| HITMARKER_OBEYS | HITMARKER_SYNCHRONISE_EFFECT
| HITMARKER_OBEYS | HITMARKER_SYNCHRONIZE_EFFECT
| HITMARKER_CHARGING | HITMARKER_IGNORE_DISGUISE);
// check if Stellar type boost should be used up
@ -4635,7 +4636,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& IsBattlerAlive(gBattlerAttacker)
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
&& IsBattlerTurnDamaged(gBattlerTarget)
&& CanBeSlept(gBattlerAttacker, ability, NOT_BLOCKED_BY_SLEEP_CLAUSE)
&& CanBeSlept(gBattlerAttacker, gBattlerTarget, ability, NOT_BLOCKED_BY_SLEEP_CLAUSE)
&& GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS
&& IsMoveMakingContact(move, gBattlerAttacker))
{
@ -4659,7 +4660,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& IsBattlerAlive(gBattlerAttacker)
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
&& IsBattlerTurnDamaged(gBattlerTarget)
&& CanBePoisoned(gBattlerTarget, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))
&& CanBePoisoned(gBattlerTarget, gBattlerAttacker, gLastUsedAbility, GetBattlerAbility(gBattlerAttacker))
&& GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS
&& IsMoveMakingContact(move, gBattlerAttacker))
{
@ -4680,7 +4681,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& IsBattlerAlive(gBattlerAttacker)
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
&& IsBattlerTurnDamaged(gBattlerTarget)
&& CanBeParalyzed(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))
&& CanBeParalyzed(gBattlerTarget, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))
&& GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS
&& IsMoveMakingContact(move, gBattlerAttacker))
{
@ -4700,7 +4701,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS
&& (IsMoveMakingContact(move, gBattlerAttacker))
&& IsBattlerTurnDamaged(gBattlerTarget)
&& CanBeBurned(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))
&& CanBeBurned(gBattlerTarget, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))
&& (B_ABILITY_TRIGGER_CHANCE >= GEN_4 ? RandomPercentage(RNG_FLAME_BODY, 30) : RandomChance(RNG_FLAME_BODY, 1, 3)))
{
gBattleScripting.moveEffect = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_BURN;
@ -4917,7 +4918,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (!(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)
&& IsBattlerAlive(gBattlerTarget)
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
&& CanBePoisoned(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget))
&& CanBePoisoned(gBattlerAttacker, gBattlerTarget, gLastUsedAbility, GetBattlerAbility(gBattlerTarget))
&& GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS
&& IsMoveMakingContact(move, gBattlerAttacker)
&& IsBattlerTurnDamaged(gBattlerTarget) // Need to actually hit the target
@ -4935,7 +4936,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (!(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)
&& IsBattlerAlive(gBattlerTarget)
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
&& CanBePoisoned(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget))
&& CanBePoisoned(gBattlerAttacker, gBattlerTarget, gLastUsedAbility, GetBattlerAbility(gBattlerTarget))
&& IsBattlerTurnDamaged(gBattlerTarget) // Need to actually hit the target
&& RandomWeighted(RNG_TOXIC_CHAIN, 7, 3))
{
@ -5137,45 +5138,71 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
gBattleStruct->bypassMoldBreakerChecks = FALSE;
break;
case ABILITYEFFECT_SYNCHRONIZE:
if (gLastUsedAbility == ABILITY_SYNCHRONIZE && (gHitMarker & HITMARKER_SYNCHRONISE_EFFECT))
if (gLastUsedAbility == ABILITY_SYNCHRONIZE && (gHitMarker & HITMARKER_SYNCHRONIZE_EFFECT))
{
gHitMarker &= ~HITMARKER_SYNCHRONISE_EFFECT;
gHitMarker &= ~HITMARKER_SYNCHRONIZE_EFFECT;
if (!(gBattleMons[gBattlerAttacker].status1 & STATUS1_ANY))
bool32 statusChanged = CanSetNonVolatileStatus(gBattlerTarget,
gBattlerAttacker,
gLastUsedAbility,
GetBattlerAbility(gBattlerAttacker),
gBattleStruct->synchronizeMoveEffect,
STATUS_CHECK_TRIGGER);
BattleScriptPushCursor();
gBattleScripting.battler = gBattlerAbility = gBattlerTarget;
RecordAbilityBattle(gBattlerTarget, ABILITY_SYNCHRONIZE);
if (statusChanged)
{
gBattleStruct->synchronizeMoveEffect &= ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN);
if (B_SYNCHRONIZE_TOXIC < GEN_5 && gBattleStruct->synchronizeMoveEffect == MOVE_EFFECT_TOXIC)
gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_POISON;
gBattleScripting.moveEffect = gBattleStruct->synchronizeMoveEffect + MOVE_EFFECT_AFFECTS_USER;
gBattleScripting.battler = gBattlerAbility = gBattlerTarget;
PREPARE_ABILITY_BUFFER(gBattleTextBuff1, ABILITY_SYNCHRONIZE);
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_SynchronizeActivates;
gHitMarker |= HITMARKER_STATUS_ABILITY_EFFECT;
effect++;
}
else // Synchronize ability pop up still shows up even if status fails
{
gBattlescriptCurrInstr = BattleScript_AbilityPopUp;
}
}
break;
case ABILITYEFFECT_ATK_SYNCHRONIZE:
if (gLastUsedAbility == ABILITY_SYNCHRONIZE && (gHitMarker & HITMARKER_SYNCHRONISE_EFFECT))
if (gLastUsedAbility == ABILITY_SYNCHRONIZE && (gHitMarker & HITMARKER_SYNCHRONIZE_EFFECT))
{
gHitMarker &= ~HITMARKER_SYNCHRONISE_EFFECT;
gHitMarker &= ~HITMARKER_SYNCHRONIZE_EFFECT;
if (!(gBattleMons[gBattlerTarget].status1 & STATUS1_ANY))
bool32 statusChanged = CanSetNonVolatileStatus(gBattlerAttacker,
gBattlerTarget,
gLastUsedAbility,
GetBattlerAbility(gBattlerAttacker),
gBattleStruct->synchronizeMoveEffect,
STATUS_CHECK_TRIGGER);
BattleScriptPushCursor();
gBattleScripting.battler = gBattlerAbility = gBattlerAttacker;
RecordAbilityBattle(gBattlerAttacker, ABILITY_SYNCHRONIZE);
if (statusChanged)
{
gBattleStruct->synchronizeMoveEffect &= ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN);
if (gBattleStruct->synchronizeMoveEffect == MOVE_EFFECT_TOXIC)
gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_POISON;
gBattleScripting.moveEffect = gBattleStruct->synchronizeMoveEffect;
gBattleScripting.battler = gBattlerAbility = gBattlerAttacker;
PREPARE_ABILITY_BUFFER(gBattleTextBuff1, ABILITY_SYNCHRONIZE);
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_SynchronizeActivates;
gHitMarker |= HITMARKER_STATUS_ABILITY_EFFECT;
effect++;
}
else // Synchronize ability pop up still shows up even if status fails
{
gBattlescriptCurrInstr = BattleScript_AbilityPopUp;
}
}
break;
@ -5509,98 +5536,295 @@ bool32 IsBattlerTerrainAffected(u32 battler, u32 terrainFlag)
return IsBattlerGrounded(battler);
}
bool32 CanBeSlept(u32 battler, u32 ability, enum SleepClauseBlock isBlockedBySleepClause)
bool32 CanBeSlept(u32 battlerAtk, u32 battlerDef, u32 abilityDef, enum SleepClauseBlock isBlockedBySleepClause)
{
if(IsSleepClauseActiveForSide(GetBattlerSide(battler)) && isBlockedBySleepClause)
if (IsSleepClauseActiveForSide(GetBattlerSide(battlerDef)) && isBlockedBySleepClause)
return FALSE;
if (ability == ABILITY_INSOMNIA
|| ability == ABILITY_VITAL_SPIRIT
|| ability == ABILITY_COMATOSE
|| ability == ABILITY_PURIFYING_SALT
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD
|| gBattleMons[battler].status1 & STATUS1_ANY
|| IsAbilityOnSide(battler, ABILITY_SWEET_VEIL)
|| IsAbilityStatusProtected(battler, ability)
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_MISTY_TERRAIN))
if (CanSetNonVolatileStatus(battlerAtk,
battlerDef,
ABILITY_NONE, // attacker ability does not matter
abilityDef,
MOVE_EFFECT_SLEEP, // also covers yawn
STATUS_CHECK_TRIGGER))
return TRUE;
return FALSE;
}
bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 abilityDef)
{
if (CanSetNonVolatileStatus(battlerAtk,
battlerDef,
abilityAtk,
abilityDef,
MOVE_EFFECT_TOXIC, // also covers poison
STATUS_CHECK_TRIGGER))
return TRUE;
return FALSE;
}
// TODO: check order of battlerAtk and battlerDef
bool32 CanBeBurned(u32 battlerAtk, u32 battlerDef, u32 abilityDef)
{
if (CanSetNonVolatileStatus(battlerAtk,
battlerDef,
ABILITY_NONE, // attacker ability does not matter
abilityDef,
MOVE_EFFECT_BURN,
STATUS_CHECK_TRIGGER))
return TRUE;
return FALSE;
}
bool32 CanBeParalyzed(u32 battlerAtk, u32 battlerDef, u32 abilityDef)
{
if (CanSetNonVolatileStatus(battlerAtk,
battlerDef,
ABILITY_NONE, // attacker ability does not matter
abilityDef,
MOVE_EFFECT_PARALYSIS,
STATUS_CHECK_TRIGGER))
return TRUE;
return FALSE;
}
bool32 CanBeFrozen(u32 battlerAtk, u32 battlerDef, u32 abilityDef)
{
if (CanSetNonVolatileStatus(battlerAtk,
battlerDef,
ABILITY_NONE, // attacker ability does not matter
abilityDef,
MOVE_EFFECT_FREEZE,
STATUS_CHECK_TRIGGER))
return TRUE;
return FALSE;
}
// Unused, technically also redundant
bool32 CanGetFrostbite(u32 battlerAtk, u32 battlerDef, u32 abilityDef)
{
if (CanSetNonVolatileStatus(battlerAtk,
battlerDef,
ABILITY_NONE, // attacker ability does not matter
abilityDef,
MOVE_EFFECT_FREEZE_OR_FROSTBITE, // also covers frostbite
STATUS_CHECK_TRIGGER))
return TRUE;
return FALSE;
}
bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 abilityDef, enum MoveEffects effect, enum NonVolatileStatus option)
{
const u8 *battleScript = NULL;
u32 sideBattler = ABILITY_NONE;
bool32 abilityAffected = FALSE;
// Move specific checks
switch (effect)
{
case MOVE_EFFECT_POISON:
case MOVE_EFFECT_TOXIC:
if (gBattleMons[battlerDef].status1 & (STATUS1_POISON | STATUS1_TOXIC_POISON))
{
battleScript = BattleScript_AlreadyPoisoned;
}
else if (abilityAtk != ABILITY_CORROSION && IS_BATTLER_ANY_TYPE(battlerDef, TYPE_POISON, TYPE_STEEL))
{
battleScript = BattleScript_NotAffected;
}
else if ((sideBattler = IsAbilityOnSide(battlerDef, ABILITY_PASTEL_VEIL)))
{
abilityAffected = TRUE;
battlerDef = sideBattler - 1;
abilityDef = ABILITY_PASTEL_VEIL;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PASTEL_VEIL;
battleScript = BattleScript_ImmunityProtected;
}
else if (abilityDef == ABILITY_IMMUNITY)
{
abilityAffected = TRUE;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_MOVE_POISON;
battleScript = BattleScript_ImmunityProtected;
}
break;
case MOVE_EFFECT_PARALYSIS:
if (gBattleMons[battlerDef].status1 & STATUS1_PARALYSIS)
{
battleScript = BattleScript_AlreadyParalyzed;
}
else if (B_PARALYZE_ELECTRIC >= GEN_6 && IS_BATTLER_OF_TYPE(battlerDef, TYPE_ELECTRIC))
{
battleScript = BattleScript_NotAffected;
}
else if (option == STATUS_RUN_SCRIPT // Check only important during battle execution for moves
&& CalcTypeEffectivenessMultiplier(gCurrentMove, GetBattleMoveType(gCurrentMove), battlerAtk, battlerDef, abilityDef, TRUE)
&& gBattleStruct->moveResultFlags[battlerDef] & MOVE_RESULT_NO_EFFECT)
{
battleScript = BattleScript_ButItFailed;
}
else if (abilityDef == ABILITY_LIMBER)
{
abilityAffected = TRUE;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_MOVE_PARALYSIS;
battleScript = BattleScript_ImmunityProtected;
}
break;
case MOVE_EFFECT_BURN:
if (gBattleMons[battlerDef].status1 & STATUS1_BURN)
{
battleScript = BattleScript_AlreadyBurned;
}
else if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_FIRE))
{
battleScript = BattleScript_NotAffected;
}
else if (abilityDef == ABILITY_WATER_VEIL || abilityDef == ABILITY_WATER_BUBBLE)
{
abilityAffected = TRUE;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_MOVE_BURN;
battleScript = BattleScript_ImmunityProtected;
}
else if (abilityDef == ABILITY_THERMAL_EXCHANGE)
{
abilityAffected = TRUE;
battleScript = BattleScript_AbilityProtectsDoesntAffect;
}
break;
case MOVE_EFFECT_SLEEP:
if (gBattleMons[battlerDef].status1 & STATUS1_SLEEP)
{
battleScript = BattleScript_AlreadyAsleep;
}
else if (UproarWakeUpCheck(battlerDef))
{
battleScript = BattleScript_CantMakeAsleep;
}
else if (CanSleepDueToSleepClause(battlerAtk, battlerDef, option))
{
battleScript = BattleScript_SleepClauseBlocked;
}
else if (IsBattlerTerrainAffected(battlerDef, STATUS_FIELD_ELECTRIC_TERRAIN))
{
battleScript = BattleScript_ElectricTerrainPrevents;
}
else if ((sideBattler = IsAbilityOnSide(battlerDef, ABILITY_SWEET_VEIL)))
{
abilityAffected = TRUE;
battlerDef = sideBattler - 1;
abilityDef = ABILITY_SWEET_VEIL;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUS_HAD_NO_EFFECT;
battleScript = BattleScript_ImmunityProtected;
}
else if (abilityDef == ABILITY_VITAL_SPIRIT || abilityDef == ABILITY_INSOMNIA)
{
abilityAffected = TRUE;
battleScript = BattleScript_PrintAbilityMadeIneffective;
}
break;
case MOVE_EFFECT_FREEZE:
case MOVE_EFFECT_FROSTBITE:
if (gBattleMons[battlerDef].status1 & (STATUS1_FREEZE | STATUS1_FROSTBITE))
{
battleScript = BattleScript_AlreadyBurned;
}
else if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE) || IsBattlerWeatherAffected(battlerDef, B_WEATHER_SUN))
{
battleScript = BattleScript_NotAffected;
}
else if (abilityDef == ABILITY_MAGMA_ARMOR)
{
abilityAffected = TRUE;
battleScript = BattleScript_NotAffected;
}
break;
default:
break;
}
if (IsNonVolatileStatusBlocked(battlerDef, abilityDef, abilityAffected, battleScript, option))
return FALSE;
// Checks that apply to all non volatile statuses
if (abilityDef == ABILITY_COMATOSE
|| abilityDef == ABILITY_SHIELDS_DOWN
|| abilityDef == ABILITY_PURIFYING_SALT)
{
abilityAffected = TRUE;
battleScript = BattleScript_AbilityProtectsDoesntAffect;
}
else if (IsBattlerTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN))
{
battleScript = BattleScript_MistyTerrainPrevents;
}
else if (IsLeafGuardProtected(battlerDef, abilityDef))
{
abilityAffected = TRUE;
battleScript = BattleScript_AbilityProtectsDoesntAffect;
}
else if ((sideBattler = IsFlowerVeilProtected(battlerDef)))
{
abilityAffected = TRUE;
battlerDef = sideBattler - 1;
abilityDef = ABILITY_FLOWER_VEIL;
battleScript = BattleScript_FlowerVeilProtects;
}
else if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD)
{
battleScript = BattleScript_SafeguardProtected;
}
else if (gBattleMons[battlerDef].status1 & STATUS1_ANY)
{
battleScript = BattleScript_ButItFailed;
}
if (IsNonVolatileStatusBlocked(battlerDef, abilityDef, abilityAffected, battleScript, option))
return FALSE;
return TRUE;
}
bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 defAbility)
static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, u32 abilityDef, u32 abilityAffected, const u8 *battleScript, enum NonVolatileStatus option)
{
if (!(CanPoisonType(battlerAtk, battlerDef))
|| gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD
|| gBattleMons[battlerDef].status1 & STATUS1_ANY
|| defAbility == ABILITY_IMMUNITY
|| defAbility == ABILITY_COMATOSE
|| defAbility == ABILITY_PURIFYING_SALT
|| IsAbilityOnSide(battlerDef, ABILITY_PASTEL_VEIL)
|| IsAbilityStatusProtected(battlerDef, defAbility)
|| IsBattlerTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
if (battleScript != NULL)
{
if (option == STATUS_RUN_SCRIPT)
{
if (battleScript != BattleScript_NotAffected)
gBattleStruct->moveResultFlags[battlerDef] |= MOVE_RESULT_FAILED;
if (abilityAffected)
{
gLastUsedAbility = abilityDef;
gBattlerAbility = battlerDef;
RecordAbilityBattle(battlerDef, abilityDef);
}
gBattlescriptCurrInstr = battleScript;
}
return TRUE;
}
return FALSE;
}
bool32 CanBeBurned(u32 battler, u32 ability)
static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum NonVolatileStatus option)
{
if (IS_BATTLER_OF_TYPE(battler, TYPE_FIRE)
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD
|| gBattleMons[battler].status1 & STATUS1_ANY
|| ability == ABILITY_WATER_VEIL
|| ability == ABILITY_WATER_BUBBLE
|| ability == ABILITY_COMATOSE
|| ability == ABILITY_THERMAL_EXCHANGE
|| ability == ABILITY_PURIFYING_SALT
|| IsAbilityStatusProtected(battler, ability)
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN))
// Can freely sleep own partner
if (IsDoubleBattle() && IsSleepClauseEnabled() && IsBattlerAlly(battlerAtk, battlerDef))
{
if (option == STATUS_RUN_SCRIPT)
gBattleStruct->battlerState[battlerDef].sleepClauseEffectExempt = TRUE;
return FALSE;
return TRUE;
}
}
bool32 CanBeParalyzed(u32 battler, u32 ability)
{
if ((B_PARALYZE_ELECTRIC >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_ELECTRIC))
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD
|| ability == ABILITY_LIMBER
|| ability == ABILITY_COMATOSE
|| ability == ABILITY_PURIFYING_SALT
|| gBattleMons[battler].status1 & STATUS1_ANY
|| IsAbilityStatusProtected(battler, ability)
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
}
if (option == STATUS_RUN_SCRIPT)
gBattleStruct->battlerState[battlerDef].sleepClauseEffectExempt = FALSE;
// Can't sleep if clause is active otherwise
if (IsSleepClauseActiveForSide(GetBattlerSide(battlerDef)))
return TRUE;
bool32 CanBeFrozen(u32 battler)
{
u16 ability = GetBattlerAbility(battler);
if (IS_BATTLER_OF_TYPE(battler, TYPE_ICE)
|| IsBattlerWeatherAffected(battler, B_WEATHER_SUN)
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD
|| ability == ABILITY_MAGMA_ARMOR
|| ability == ABILITY_COMATOSE
|| ability == ABILITY_PURIFYING_SALT
|| gBattleMons[battler].status1 & STATUS1_ANY
|| IsAbilityStatusProtected(battler, ability)
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
}
bool32 CanGetFrostbite(u32 battler)
{
u16 ability = GetBattlerAbility(battler);
if (IS_BATTLER_OF_TYPE(battler, TYPE_ICE)
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD
|| ability == ABILITY_MAGMA_ARMOR
|| ability == ABILITY_COMATOSE
|| ability == ABILITY_PURIFYING_SALT
|| gBattleMons[battler].status1 & STATUS1_ANY
|| IsAbilityStatusProtected(battler, ability)
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
return FALSE;
}
bool32 CanBeConfused(u32 battler)
@ -7132,7 +7356,7 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler, bool32 moveTurn)
switch (battlerHoldEffect)
{
case HOLD_EFFECT_TOXIC_ORB:
if (CanBePoisoned(battler, battler, GetBattlerAbility(battler)))
if (CanBePoisoned(battler, battler, battlerAbility, battlerAbility)) // Can corrosion trigger toxic orb on itself?
{
effect = ITEM_STATUS_CHANGE;
gBattleMons[battler].status1 = STATUS1_TOXIC_POISON;
@ -7141,7 +7365,7 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler, bool32 moveTurn)
}
break;
case HOLD_EFFECT_FLAME_ORB:
if (CanBeBurned(battler, battlerAbility))
if (CanBeBurned(battler, battler, battlerAbility))
{
effect = ITEM_STATUS_CHANGE;
gBattleMons[battler].status1 = STATUS1_BURN;
@ -7409,7 +7633,7 @@ u8 GetAttackerObedienceForAction()
obedienceLevel = levelReferenced - obedienceLevel;
calc = ((rnd >> 16) & 255);
if (calc < obedienceLevel && CanBeSlept(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), NOT_BLOCKED_BY_SLEEP_CLAUSE))
if (calc < obedienceLevel && CanBeSlept(gBattlerAttacker, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), NOT_BLOCKED_BY_SLEEP_CLAUSE))
{
// try putting asleep
int i;

View File

@ -18,7 +18,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_SLEEP] =
{
.battleScript = BattleScript_EffectSleep,
.battleScript = BattleScript_EffectNonVolatileStatus,
.battleTvScore = 1,
},
@ -185,7 +185,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_TOXIC] =
{
.battleScript = BattleScript_EffectToxic,
.battleScript = BattleScript_EffectNonVolatileStatus,
.battleTvScore = 5,
.encourageEncore = TRUE,
},
@ -368,14 +368,14 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_POISON] =
{
.battleScript = BattleScript_EffectPoison,
.battleScript = BattleScript_EffectNonVolatileStatus,
.battleTvScore = 4,
.encourageEncore = TRUE,
},
[EFFECT_PARALYZE] =
{
.battleScript = BattleScript_EffectParalyze,
.battleScript = BattleScript_EffectNonVolatileStatus,
.battleTvScore = 4,
.encourageEncore = TRUE,
},
@ -895,7 +895,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_WILL_O_WISP] =
{
.battleScript = BattleScript_EffectWillOWisp,
.battleScript = BattleScript_EffectNonVolatileStatus,
.battleTvScore = 5,
.encourageEncore = TRUE,
},

View File

@ -1305,6 +1305,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.argument = { .nonVolatileStatus = MOVE_EFFECT_SLEEP },
.zMove = { .effect = Z_EFFECT_SPD_UP_1 },
.ignoresSubstitute = B_UPDATED_MOVE_FLAGS >= GEN_6,
.magicCoatAffected = TRUE,
@ -2066,6 +2067,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.argument = { .nonVolatileStatus = MOVE_EFFECT_POISON },
.zMove = { .effect = Z_EFFECT_DEF_UP_1 },
.magicCoatAffected = TRUE,
.powderMove = TRUE,
@ -2093,6 +2095,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.category = DAMAGE_CATEGORY_STATUS,
.zMove = { .effect = Z_EFFECT_SPDEF_UP_1 },
.magicCoatAffected = TRUE,
.argument = { .nonVolatileStatus = MOVE_EFFECT_PARALYSIS },
.powderMove = TRUE,
.contestEffect = CONTEST_EFFECT_BADLY_STARTLE_MONS_WITH_GOOD_APPEALS,
.contestCategory = CONTEST_CATEGORY_SMART,
@ -2116,6 +2119,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.argument = { .nonVolatileStatus = MOVE_EFFECT_SLEEP },
.zMove = { .effect = Z_EFFECT_SPD_UP_1 },
.magicCoatAffected = TRUE,
.powderMove = TRUE,
@ -2300,6 +2304,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.argument = { .nonVolatileStatus = MOVE_EFFECT_PARALYSIS },
.zMove = { .effect = Z_EFFECT_SPDEF_UP_1 },
.magicCoatAffected = TRUE,
.contestEffect = CONTEST_EFFECT_BADLY_STARTLE_MONS_WITH_GOOD_APPEALS,
@ -2449,6 +2454,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.argument = { .nonVolatileStatus = MOVE_EFFECT_TOXIC },
.zMove = { .effect = Z_EFFECT_DEF_UP_1 },
.magicCoatAffected = TRUE,
.contestEffect = CONTEST_EFFECT_WORSEN_CONDITION_OF_PREV_MONS,
@ -2524,6 +2530,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.argument = { .nonVolatileStatus = MOVE_EFFECT_SLEEP },
.zMove = { .effect = Z_EFFECT_SPD_UP_1 },
.magicCoatAffected = TRUE,
.contestEffect = CONTEST_EFFECT_BADLY_STARTLE_PREV_MONS,
@ -3619,6 +3626,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.argument = { .nonVolatileStatus = MOVE_EFFECT_PARALYSIS },
.zMove = { .effect = Z_EFFECT_SPDEF_UP_1 },
.magicCoatAffected = TRUE,
.contestEffect = CONTEST_EFFECT_BADLY_STARTLE_PREV_MONS,
@ -3673,6 +3681,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.target = B_UPDATED_MOVE_DATA >= GEN_5 ? MOVE_TARGET_BOTH : MOVE_TARGET_SELECTED,
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.argument = { .nonVolatileStatus = MOVE_EFFECT_POISON },
.zMove = { .effect = Z_EFFECT_DEF_UP_1 },
.magicCoatAffected = TRUE,
.contestEffect = CONTEST_EFFECT_WORSEN_CONDITION_OF_PREV_MONS,
@ -3743,6 +3752,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.argument = { .nonVolatileStatus = MOVE_EFFECT_SLEEP },
.zMove = { .effect = Z_EFFECT_SPD_UP_1 },
.magicCoatAffected = TRUE,
.contestEffect = CONTEST_EFFECT_BADLY_STARTLE_PREV_MONS,
@ -3883,6 +3893,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.argument = { .nonVolatileStatus = MOVE_EFFECT_SLEEP },
.zMove = { .effect = Z_EFFECT_RESET_STATS },
.magicCoatAffected = TRUE,
.powderMove = TRUE,
@ -6850,6 +6861,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.argument = { .nonVolatileStatus = MOVE_EFFECT_BURN },
.zMove = { .effect = Z_EFFECT_ATK_UP_1 },
.magicCoatAffected = TRUE,
.contestEffect = CONTEST_EFFECT_BADLY_STARTLE_FRONT_MON,
@ -7374,6 +7386,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.argument = { .nonVolatileStatus = MOVE_EFFECT_SLEEP },
.zMove = { .effect = Z_EFFECT_SPD_UP_1 },
.magicCoatAffected = TRUE,
.contestEffect = CONTEST_EFFECT_MAKE_FOLLOWING_MONS_NERVOUS,
@ -8385,6 +8398,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.argument = { .nonVolatileStatus = MOVE_EFFECT_SLEEP },
.zMove = { .effect = Z_EFFECT_SPD_UP_1 },
.ignoresSubstitute = B_UPDATED_MOVE_FLAGS >= GEN_6,
.magicCoatAffected = TRUE,
@ -11921,6 +11935,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.target = MOVE_TARGET_BOTH,
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.argument = { .nonVolatileStatus = MOVE_EFFECT_SLEEP },
.zMove = { .effect = Z_EFFECT_RESET_STATS },
.magicCoatAffected = TRUE,
.sketchBanned = (B_SKETCH_BANS >= GEN_9),

View File

@ -0,0 +1,64 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_TOXIC) == EFFECT_TOXIC);
ASSUME(GetMoveNonVolatileStatus(MOVE_TOXIC) == MOVE_EFFECT_TOXIC);
ASSUME(GetMoveEffect(MOVE_POISON_GAS) == EFFECT_POISON);
ASSUME(GetMoveNonVolatileStatus(MOVE_POISON_GAS) == MOVE_EFFECT_POISON);
ASSUME(GetMoveEffect(MOVE_WILL_O_WISP) == EFFECT_WILL_O_WISP);
ASSUME(GetMoveNonVolatileStatus(MOVE_WILL_O_WISP) == MOVE_EFFECT_BURN);
ASSUME(GetMoveEffect(MOVE_THUNDER_WAVE) == EFFECT_PARALYZE);
ASSUME(GetMoveNonVolatileStatus(MOVE_THUNDER_WAVE) == MOVE_EFFECT_PARALYSIS);
ASSUME(GetMoveEffect(MOVE_HYPNOSIS) == EFFECT_SLEEP);
ASSUME(GetMoveNonVolatileStatus(MOVE_HYPNOSIS) == MOVE_EFFECT_SLEEP);
}
DOUBLE_BATTLE_TEST("Flower Veil prevents Toxic bad poison on partner - right target")
{
u32 move;
PARAMETRIZE { move = MOVE_TOXIC; }
PARAMETRIZE { move = MOVE_POISON_GAS; }
PARAMETRIZE { move = MOVE_WILL_O_WISP; }
PARAMETRIZE { move = MOVE_THUNDER_WAVE; }
PARAMETRIZE { move = MOVE_HYPNOSIS; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_COMFEY) { Ability(ABILITY_FLOWER_VEIL); }
OPPONENT(SPECIES_CHIKORITA);
} WHEN {
TURN { MOVE(playerLeft, move, target: opponentRight); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, move, playerLeft);
ABILITY_POPUP(opponentLeft, ABILITY_FLOWER_VEIL);
MESSAGE("The opposing Chikorita surrounded itself with a veil of petals!");
}
}
DOUBLE_BATTLE_TEST("Flower Veil prevents Toxic bad poison on partner - left target")
{
u32 move;
PARAMETRIZE { move = MOVE_TOXIC; }
PARAMETRIZE { move = MOVE_POISON_GAS; }
PARAMETRIZE { move = MOVE_WILL_O_WISP; }
PARAMETRIZE { move = MOVE_THUNDER_WAVE; }
PARAMETRIZE { move = MOVE_HYPNOSIS; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_CHIKORITA);
OPPONENT(SPECIES_COMFEY) { Ability(ABILITY_FLOWER_VEIL); }
} WHEN {
TURN { MOVE(playerLeft, move, target: opponentLeft); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, move, playerLeft);
ABILITY_POPUP(opponentRight, ABILITY_FLOWER_VEIL);
MESSAGE("The opposing Chikorita surrounded itself with a veil of petals!");
}
}

View File

@ -4,6 +4,7 @@
SINGLE_BATTLE_TEST("Limber prevents paralysis")
{
GIVEN {
ASSUME(MoveHasAdditionalEffect(MOVE_THUNDER_SHOCK, MOVE_EFFECT_PARALYSIS) == TRUE);
PLAYER(SPECIES_PERSIAN) { Ability(ABILITY_LIMBER); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
@ -16,3 +17,22 @@ SINGLE_BATTLE_TEST("Limber prevents paralysis")
}
}
}
SINGLE_BATTLE_TEST("Limber prevents paralysis from Thunder Wave")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_THUNDER_WAVE) == EFFECT_PARALYZE);
ASSUME(GetMoveNonVolatileStatus(MOVE_THUNDER_WAVE) == MOVE_EFFECT_PARALYSIS);
PLAYER(SPECIES_PERSIAN) { Ability(ABILITY_LIMBER); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_THUNDER_WAVE); }
} SCENE {
MESSAGE("Persian's Limber prevents paralysis!");
NONE_OF {
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, player);
STATUS_ICON(player, paralysis: TRUE);
}
}
}

View File

@ -1,6 +1,12 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_TOXIC) == EFFECT_TOXIC);
ASSUME(GetMoveNonVolatileStatus(MOVE_TOXIC) == MOVE_EFFECT_TOXIC);
}
SINGLE_BATTLE_TEST("Pastel Veil prevents Poison Sting poison")
{
GIVEN {
@ -34,7 +40,6 @@ DOUBLE_BATTLE_TEST("Pastel Veil prevents Poison Sting poison on partner")
SINGLE_BATTLE_TEST("Pastel Veil immediately cures Mold Breaker poison")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TOXIC) == EFFECT_TOXIC);
PLAYER(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); }
OPPONENT(SPECIES_PONYTA_GALAR) { Ability(ABILITY_PASTEL_VEIL); }
} WHEN {
@ -52,7 +57,6 @@ SINGLE_BATTLE_TEST("Pastel Veil immediately cures Mold Breaker poison")
DOUBLE_BATTLE_TEST("Pastel Veil does not cure Mold Breaker poison on partner")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TOXIC) == EFFECT_TOXIC);
PLAYER(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_PONYTA_GALAR) { Ability(ABILITY_PASTEL_VEIL); }
@ -69,7 +73,6 @@ DOUBLE_BATTLE_TEST("Pastel Veil does not cure Mold Breaker poison on partner")
SINGLE_BATTLE_TEST("Pastel Veil prevents Toxic bad poison")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TOXIC) == EFFECT_TOXIC);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PONYTA_GALAR) { Ability(ABILITY_PASTEL_VEIL); }
} WHEN {
@ -82,10 +85,9 @@ SINGLE_BATTLE_TEST("Pastel Veil prevents Toxic bad poison")
}
}
DOUBLE_BATTLE_TEST("Pastel Veil prevents Toxic bad poison on partner")
DOUBLE_BATTLE_TEST("Pastel Veil prevents Toxic bad poison on partner - right target")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TOXIC) == EFFECT_TOXIC);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_PONYTA_GALAR) { Ability(ABILITY_PASTEL_VEIL); }
@ -100,6 +102,23 @@ DOUBLE_BATTLE_TEST("Pastel Veil prevents Toxic bad poison on partner")
}
}
DOUBLE_BATTLE_TEST("Pastel Veil prevents Toxic bad poison on partner - left target")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_PONYTA_GALAR) { Ability(ABILITY_PASTEL_VEIL); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_TOXIC, target: opponentLeft); }
} SCENE {
MESSAGE("Wobbuffet used Toxic!");
ABILITY_POPUP(opponentRight, ABILITY_PASTEL_VEIL);
MESSAGE("The opposing Wynaut is protected by a pastel veil!");
NOT STATUS_ICON(opponentLeft, badPoison: TRUE);
}
}
SINGLE_BATTLE_TEST("Pastel Veil prevents Toxic Spikes poison")
{
GIVEN {

View File

@ -49,3 +49,22 @@ SINGLE_BATTLE_TEST("Poison Point triggers 30% of the time")
STATUS_ICON(player, poison: TRUE);
}
}
SINGLE_BATTLE_TEST("Poison Point will not poison Poison-Type targets with corrosion")
{
GIVEN {
ASSUME(MoveMakesContact(MOVE_TACKLE));
PLAYER(SPECIES_SALANDIT) { Ability(ABILITY_CORROSION); }
OPPONENT(SPECIES_NIDORAN_M) { Ability(ABILITY_POISON_POINT); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); }
TURN {}
} SCENE {
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_POISON_POINT);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, player);
MESSAGE("Salandit was poisoned by the opposing Nidoran♂'s Poison Point!");
STATUS_ICON(player, poison: TRUE);
}
}
}

View File

@ -117,3 +117,17 @@ SINGLE_BATTLE_TEST("Purifying Salt doesn't prevent pokemon from being poisoned b
HP_BAR(player);
}
}
SINGLE_BATTLE_TEST("Purifying Salt protects from secondary effect burn")
{
GIVEN {
ASSUME(MoveHasAdditionalEffect(MOVE_EMBER, MOVE_EFFECT_BURN));
PLAYER(SPECIES_GARGANACL) { Ability(ABILITY_PURIFYING_SALT); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_EMBER); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent);
NOT STATUS_ICON(player, STATUS1_BURN);
}
}

View File

@ -47,3 +47,18 @@ SINGLE_BATTLE_TEST("Static triggers 30% of the time")
STATUS_ICON(player, paralysis: TRUE);
}
}
SINGLE_BATTLE_TEST("Static triggers even if attacker is under substitute")
{
GIVEN {
ASSUME(MoveMakesContact(MOVE_TACKLE));
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PIKACHU) { Ability(ABILITY_STATIC); }
} WHEN {
TURN { MOVE(player, MOVE_SUBSTITUTE); }
TURN { MOVE(player, MOVE_TACKLE); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_STATIC);
STATUS_ICON(player, paralysis: TRUE);
}
}

View File

@ -0,0 +1,40 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_HYPNOSIS) == EFFECT_SLEEP);
ASSUME(GetMoveNonVolatileStatus(MOVE_HYPNOSIS) == MOVE_EFFECT_SLEEP);
}
DOUBLE_BATTLE_TEST("Sweet Veil prevents Sleep on partner - right target")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_BOUNSWEET) { Ability(ABILITY_SWEET_VEIL); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(playerLeft, MOVE_HYPNOSIS, target: opponentRight); }
} SCENE {
MESSAGE("Wobbuffet used Hypnosis!");
ABILITY_POPUP(opponentLeft, ABILITY_SWEET_VEIL);
NOT STATUS_ICON(opponentRight, sleep: TRUE);
}
}
DOUBLE_BATTLE_TEST("Sweet Veil prevents Sleep on partner - left target")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_BOUNSWEET) { Ability(ABILITY_SWEET_VEIL); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_HYPNOSIS, target: opponentLeft); }
} SCENE {
MESSAGE("Wobbuffet used Hypnosis!");
ABILITY_POPUP(opponentRight, ABILITY_SWEET_VEIL);
NOT STATUS_ICON(opponentLeft, sleep: TRUE);
}
}

View File

@ -0,0 +1,68 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Synchronize will mirror back non volatile status back at opposing mon")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TOXIC) == EFFECT_TOXIC);
ASSUME(GetMoveNonVolatileStatus(MOVE_TOXIC) == MOVE_EFFECT_TOXIC);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ABRA) { Ability(ABILITY_SYNCHRONIZE); }
} WHEN {
TURN { MOVE(player, MOVE_TOXIC); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
STATUS_ICON(opponent, badPoison: TRUE);
ABILITY_POPUP(opponent, ABILITY_SYNCHRONIZE);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, player);
STATUS_ICON(player, badPoison: TRUE);
}
}
SINGLE_BATTLE_TEST("Synchronize will still show up the ability pop up even if it fails")
{
GIVEN {
ASSUME(MoveMakesContact(MOVE_TACKLE));
PLAYER(SPECIES_PIKACHU) { Ability(ABILITY_STATIC); }
OPPONENT(SPECIES_ABRA) { Ability(ABILITY_SYNCHRONIZE); }
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); MOVE(player, MOVE_CELEBRATE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
ABILITY_POPUP(player, ABILITY_STATIC);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponent);
STATUS_ICON(opponent, paralysis: TRUE);
ABILITY_POPUP(opponent, ABILITY_SYNCHRONIZE);
NONE_OF {
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, player);
STATUS_ICON(player, paralysis: TRUE);
}
}
}
SINGLE_BATTLE_TEST("Synchronize will mirror back static activation")
{
GIVEN {
ASSUME(MoveMakesContact(MOVE_TACKLE));
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PIKACHU) { Ability(ABILITY_STATIC); }
OPPONENT(SPECIES_ABRA) { Ability(ABILITY_SYNCHRONIZE); }
} WHEN {
TURN { MOVE(player, MOVE_SKILL_SWAP); }
TURN { SWITCH(opponent, 1); }
TURN { MOVE(opponent, MOVE_TACKLE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
ABILITY_POPUP(player, ABILITY_STATIC);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponent);
STATUS_ICON(opponent, paralysis: TRUE);
ABILITY_POPUP(opponent, ABILITY_SYNCHRONIZE);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, player);
STATUS_ICON(player, paralysis: TRUE);
}
}

View File

@ -0,0 +1,21 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Water Bubble prevents burn from Will-o-Wisp")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_WILL_O_WISP) == EFFECT_WILL_O_WISP);
ASSUME(GetMoveNonVolatileStatus(MOVE_WILL_O_WISP) == MOVE_EFFECT_BURN);
PLAYER(SPECIES_DEWPIDER) { Ability(ABILITY_WATER_BUBBLE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_WILL_O_WISP); }
} SCENE {
ABILITY_POPUP(player, ABILITY_WATER_BUBBLE);
MESSAGE("Dewpider's Water Bubble prevents burns!");
NONE_OF {
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, player);
STATUS_ICON(player, burn: TRUE);
}
}
}

View File

@ -4,6 +4,7 @@
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_TOXIC) == EFFECT_TOXIC);
ASSUME(GetMoveNonVolatileStatus(MOVE_TOXIC) == MOVE_EFFECT_TOXIC);
}
SINGLE_BATTLE_TEST("Toxic inflicts bad poison")
@ -21,6 +22,27 @@ SINGLE_BATTLE_TEST("Toxic inflicts bad poison")
}
}
SINGLE_BATTLE_TEST("Toxic can't bad poison a poison or steel type")
{
u32 species;
PARAMETRIZE { species = SPECIES_BELDUM; }
PARAMETRIZE { species = SPECIES_BULBASAUR; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species);
} WHEN {
TURN { MOVE(player, MOVE_TOXIC); }
} SCENE {
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
STATUS_ICON(opponent, badPoison: TRUE);
}
}
}
SINGLE_BATTLE_TEST("Toxic cannot miss if used by a Poison-type")
{
u32 species;

View File

@ -1,6 +1,12 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_WILL_O_WISP) == EFFECT_WILL_O_WISP);
ASSUME(GetMoveNonVolatileStatus(MOVE_WILL_O_WISP) == MOVE_EFFECT_BURN);
}
SINGLE_BATTLE_TEST("Burn deals 1/16th (Gen7+) or 1/8th damage per turn")
{
u32 j;
@ -35,6 +41,37 @@ SINGLE_BATTLE_TEST("Burn reduces Attack by 50%", s16 damage)
}
}
SINGLE_BATTLE_TEST("Will-O-Wisp burns target")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_WILL_O_WISP); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_WILL_O_WISP, player);
MESSAGE("The opposing Wobbuffet was burned!");
STATUS_ICON(opponent, burn: TRUE);
}
}
SINGLE_BATTLE_TEST("Will-O-Wisp can't burn a fire type")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_CHARMANDER);
} WHEN {
TURN { MOVE(player, MOVE_WILL_O_WISP); }
} SCENE {
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_WILL_O_WISP, player);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponent);
STATUS_ICON(opponent, burn: TRUE);
}
}
}
AI_SINGLE_BATTLE_TEST("AI avoids Will-o-Wisp when it can not burn target")
{
u32 species, ability;

View File

@ -16,3 +16,26 @@ SINGLE_BATTLE_TEST("Poison deals 1/8th damage per turn")
HP_BAR(player, damage: maxHP / 8);
}
}
SINGLE_BATTLE_TEST("Poison can't bad poison a poison or steel type")
{
u32 species;
PARAMETRIZE { species = SPECIES_BELDUM; }
PARAMETRIZE { species = SPECIES_BULBASAUR; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_POISON_GAS) == EFFECT_POISON);
ASSUME(GetMoveNonVolatileStatus(MOVE_POISON_GAS) == MOVE_EFFECT_POISON);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species);
} WHEN {
TURN { MOVE(player, MOVE_POISON_GAS); }
} SCENE {
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_POISON_GAS, player);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
STATUS_ICON(opponent, poison: TRUE);
}
}
}

View File

@ -22,6 +22,20 @@ SINGLE_BATTLE_TEST("Sleep prevents the battler from using a move")
}
}
SINGLE_BATTLE_TEST("Sleep: Spore doesn't affect grass types (Gen 6+)")
{
GIVEN {
ASSUME(IsPowderMove(MOVE_SPORE));
ASSUME(B_POWDER_GRASS >= GEN_6);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_CHIKORITA);
} WHEN {
TURN { MOVE(player, MOVE_SPORE); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SPORE, player);
}
}
AI_SINGLE_BATTLE_TEST("AI avoids hypnosis when it can not put target to sleep")
{
u32 species, ability;