From 4ff2f3de4207b3702d2ea0de39a2729feccb1a0b Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Wed, 30 Apr 2025 17:34:19 +0200 Subject: [PATCH] Centralizes non volatile status effect checks (#6533) Co-authored-by: PhallenTree <168426989+PhallenTree@users.noreply.github.com> --- asm/macros/battle_script.inc | 18 +- data/battle_scripts_1.s | 195 +-------- include/battle_ai_util.h | 7 +- include/battle_script_commands.h | 3 +- include/battle_scripts.h | 23 +- include/battle_util.h | 19 +- include/constants/battle.h | 2 +- include/constants/battle_script_commands.h | 2 - include/constants/battle_string_ids.h | 7 +- include/move.h | 6 + src/battle_ai_main.c | 25 +- src/battle_ai_switch_items.c | 2 +- src/battle_ai_util.c | 68 ++-- src/battle_message.c | 31 +- src/battle_script_commands.c | 441 ++++++--------------- src/battle_util.c | 424 +++++++++++++++----- src/data/battle_move_effects.h | 10 +- src/data/moves_info.h | 15 + test/battle/ability/flower_veil.c | 64 +++ test/battle/ability/limber.c | 20 + test/battle/ability/pastel_veil.c | 29 +- test/battle/ability/poison_point.c | 19 + test/battle/ability/purifying_salt.c | 14 + test/battle/ability/static.c | 15 + test/battle/ability/sweet_veil.c | 40 ++ test/battle/ability/synchronize.c | 68 ++++ test/battle/ability/water_bubble.c | 21 + test/battle/move_effect/toxic.c | 22 + test/battle/status1/burn.c | 37 ++ test/battle/status1/poison.c | 23 ++ test/battle/status1/sleep.c | 14 + 31 files changed, 942 insertions(+), 742 deletions(-) create mode 100644 test/battle/ability/flower_veil.c create mode 100644 test/battle/ability/sweet_veil.c create mode 100644 test/battle/ability/synchronize.c create mode 100644 test/battle/ability/water_bubble.c diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 16f4a567c4..038056248c 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -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 diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index cfa0f9e3ff..63f3d7a85b 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -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 diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index e7672c7ae3..c21fc9f837 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -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); diff --git a/include/battle_script_commands.h b/include/battle_script_commands.h index 6bd25e8647..7f5d2caa08 100644 --- a/include/battle_script_commands.h +++ b/include/battle_script_commands.h @@ -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[]; diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 10ebf7e020..1a60f9b559 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -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 diff --git a/include/battle_util.h b/include/battle_util.h index c18ac5cbaf..6f5bfc5418 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -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); diff --git a/include/constants/battle.h b/include/constants/battle.h index 88224cf5b3..bd8c56c2a0 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -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 diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 42a654471d..81cc24f910 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -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, diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 1e54932674..cf3b927e91 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -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 diff --git a/include/move.h b/include/move.h index b13878a58a..c49ba84f9a 100644 --- a/include/move.h +++ b/include/move.h @@ -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]; diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index beec380c5a..6dd43982be 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -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: diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index f144e5998d..f10cbdfd45 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -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))) { diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index b9645b2ba3..98ce0840e7 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -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)) diff --git a/src/battle_message.c b/src/battle_message.c index 206ac18904..0635aef7a5 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -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[] = diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 0ccbb86b02..914ffe7fc1 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -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); diff --git a/src/battle_util.c b/src/battle_util.c index 53e3499aec..43d507707c 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -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; diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index d0481b4a95..9e29b064d3 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -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, }, diff --git a/src/data/moves_info.h b/src/data/moves_info.h index fee2dcccc4..3676cbc955 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -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), diff --git a/test/battle/ability/flower_veil.c b/test/battle/ability/flower_veil.c new file mode 100644 index 0000000000..97349b6ba3 --- /dev/null +++ b/test/battle/ability/flower_veil.c @@ -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!"); + } +} diff --git a/test/battle/ability/limber.c b/test/battle/ability/limber.c index 9ba2760c87..fa18069946 100644 --- a/test/battle/ability/limber.c +++ b/test/battle/ability/limber.c @@ -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); + } + } +} diff --git a/test/battle/ability/pastel_veil.c b/test/battle/ability/pastel_veil.c index 5f6fa6b058..756cabe604 100644 --- a/test/battle/ability/pastel_veil.c +++ b/test/battle/ability/pastel_veil.c @@ -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 { diff --git a/test/battle/ability/poison_point.c b/test/battle/ability/poison_point.c index 635698379c..3a766e041f 100644 --- a/test/battle/ability/poison_point.c +++ b/test/battle/ability/poison_point.c @@ -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); + } + } +} diff --git a/test/battle/ability/purifying_salt.c b/test/battle/ability/purifying_salt.c index 85d2e9c009..1c98466aef 100644 --- a/test/battle/ability/purifying_salt.c +++ b/test/battle/ability/purifying_salt.c @@ -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); + } +} diff --git a/test/battle/ability/static.c b/test/battle/ability/static.c index 8d5a27c6b5..07db9eeabb 100644 --- a/test/battle/ability/static.c +++ b/test/battle/ability/static.c @@ -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); + } +} diff --git a/test/battle/ability/sweet_veil.c b/test/battle/ability/sweet_veil.c new file mode 100644 index 0000000000..0e1322925f --- /dev/null +++ b/test/battle/ability/sweet_veil.c @@ -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); + } +} diff --git a/test/battle/ability/synchronize.c b/test/battle/ability/synchronize.c new file mode 100644 index 0000000000..83f960973c --- /dev/null +++ b/test/battle/ability/synchronize.c @@ -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); + } +} diff --git a/test/battle/ability/water_bubble.c b/test/battle/ability/water_bubble.c new file mode 100644 index 0000000000..f6d838ae31 --- /dev/null +++ b/test/battle/ability/water_bubble.c @@ -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); + } + } +} diff --git a/test/battle/move_effect/toxic.c b/test/battle/move_effect/toxic.c index 7b8274a441..26f54e5d57 100644 --- a/test/battle/move_effect/toxic.c +++ b/test/battle/move_effect/toxic.c @@ -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; diff --git a/test/battle/status1/burn.c b/test/battle/status1/burn.c index c37768cedf..80c3fb0ecd 100644 --- a/test/battle/status1/burn.c +++ b/test/battle/status1/burn.c @@ -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; diff --git a/test/battle/status1/poison.c b/test/battle/status1/poison.c index 12f6ffa95f..90d6fa032c 100644 --- a/test/battle/status1/poison.c +++ b/test/battle/status1/poison.c @@ -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); + } + } +} diff --git a/test/battle/status1/sleep.c b/test/battle/status1/sleep.c index f42f4bd714..401c0d0bcd 100644 --- a/test/battle/status1/sleep.c +++ b/test/battle/status1/sleep.c @@ -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;