diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index a2d940737..fa89c29b9 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1764,7 +1764,7 @@ various BS_ATTACKER, VARIOUS_TOTEM_BOOST .4byte \ptr .endm - + .macro tryactivategrimneigh, battler:req various \battler, VARIOUS_TRY_ACTIVATE_GRIM_NEIGH .endm @@ -1777,6 +1777,50 @@ various 0, VARIOUS_PICKPOCKET .endm + .macro doterrainseed battler:req, ptr:req + various \battler, VARIOUS_TERRAIN_SEED + .4byte \ptr + .endm + + .macro makeinvisible battler:req + various \battler, VARIOUS_MAKE_INVISIBLE + .endm + + .macro tryroomservice battler:req, ptr:req + various \battler, VARIOUS_ROOM_SERVICE + .4byte \ptr + .endm + + .macro jumpifterrainaffected battler:req, terrainFlags:req, ptr:req + various \battler, VARIOUS_JUMP_IF_TERRAIN_AFFECTED + .4byte \terrainFlags + .4byte \ptr + .endm + + .macro jumpifpranksterblocked battler:req, ptr:req + various \battler, VARIOUS_JUMP_IF_PRANKSTER_BLOCKED + .4byte \ptr + .endm + + .macro eeriespellppreduce ptr:req + various BS_TARGET, VARIOUS_EERIE_SPELL_PP_REDUCE + .4byte \ptr + .endm + + .macro jumpifteamhealthy battler:req, ptr:req + various \battler, VARIOUS_JUMP_IF_TEAM_HEALTHY + .4byte \ptr + .endm + + .macro tryhealquarterhealth battler:req, ptr:req + various \battler, VARIOUS_TRY_HEAL_QUARTER_HP + .4byte \ptr + .endm + + .macro removeterrain + various BS_ATTACKER, VARIOUS_REMOVE_TERRAIN + .endm + .macro getrototillertargets ptr:req various BS_ATTACKER, VARIOUS_GET_ROTOTILLER_TARGETS .4byte \ptr @@ -1786,7 +1830,7 @@ various \battler, VARIOUS_JUMP_IF_NOT_ROTOTILLER_AFFECTED .4byte \ptr .endm - + @ helpful macros .macro setstatchanger stat:req, stages:req, down:req setbyte sSTATCHANGER \stat | \stages << 3 | \down << 7 @@ -1891,6 +1935,12 @@ 1: .endm + .macro jumpifflowerveilattacker jumpptr:req + jumpifnottype BS_ATTACKER, TYPE_GRASS, 1f + jumpifability BS_ATTACKER_SIDE, ABILITY_FLOWER_VEIL, \jumpptr + 1: + .endm + .macro setallytonexttarget jumpptr:req jumpifbyte CMP_GREATER_THAN, gBattlerTarget, 0x1, 1f addbyte gBattlerTarget, 0x2 diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 33182e878..d7de92096 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -532,7 +532,7 @@ gBattleAnims_Moves:: .4byte Move_QUASH .4byte Move_ACROBATICS .4byte Move_REFLECT_TYPE - .4byte Move_RETALITATE + .4byte Move_RETALIATE .4byte Move_FINAL_GAMBIT .4byte Move_BESTOW .4byte Move_INFERNO @@ -822,6 +822,7 @@ gBattleAnims_General:: .4byte General_SlideOffScreen @ B_ANIM_SLIDE_OFFSCREEN .4byte General_RestoreBg @ B_ANIM_RESTORE_BG .4byte General_TotemFlare @ B_ANIM_TOTEM_FLARE + .4byte General_GulpMissile @ B_ANIM_GULP_MISSILE .align 2 gBattleAnims_Special:: @@ -5610,7 +5611,7 @@ Move_REFLECT_TYPE: blendoff end -Move_RETALITATE: +Move_RETALIATE: loadspritegfx ANIM_TAG_CUT @Cut monbg ANIM_DEF_PARTNER setalpha 9, 8 @@ -13481,7 +13482,7 @@ Move_BODY_PRESS:: end Move_DECORATE:: - end @to do: + goto Move_FLOWER_SHIELD Move_DRUM_BEATING:: loadspritegfx ANIM_TAG_MUSIC_NOTES @@ -13957,7 +13958,7 @@ Move_EXPANDING_FORCE:: end @to do: Move_STEEL_ROLLER:: - end @to do: + goto Move_GYRO_BALL Move_SCALE_SHOT:: end @to do: @@ -13984,7 +13985,7 @@ Move_SKITTER_SMACK:: end @to do: Move_BURNING_JEALOUSY:: - end @to do: + goto Move_OVERHEAT Move_LASH_OUT:: end @to do: @@ -14011,7 +14012,7 @@ Move_SCORCHING_SANDS:: end @to do: Move_JUNGLE_HEALING:: - end @to do: + goto Move_AROMATHERAPY Move_WICKED_BLOW:: end @to do: @@ -24397,6 +24398,20 @@ General_TotemFlare:: clearmonbg ANIM_ATTACKER end +General_GulpMissile: @ Tackle anim (placeholder) + loadspritegfx ANIM_TAG_IMPACT + monbg ANIM_ATTACKER + setalpha 12, 8 + createsprite gHorizontalLungeSpriteTemplate, ANIM_ATTACKER, 2, 4, 4 + delay 6 + createsprite gBasicHitSplatSpriteTemplate, ANIM_ATTACKER, 2, 0, 0, ANIM_ATTACKER, 2 + createvisualtask AnimTask_ShakeMon, 2, ANIM_ATTACKER, 3, 0, 6, 1 + playsewithpan SE_M_COMET_PUNCH, SOUND_PAN_TARGET + waitforvisualfinish + clearmonbg ANIM_ATTACKER + blendoff + end + RainbowEndureEffect: launchtemplate gBlueEndureEnergySpriteTemplate 0x2 0x4 0x0 0xffe8 0x1a 0x2 delay 0x3 diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 177c657ce..3d9c66bd6 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -252,7 +252,7 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectHit @ EFFECT_ROUND .4byte BattleScript_EffectHit @ EFFECT_BRINE .4byte BattleScript_EffectHit @ EFFECT_VENOSHOCK - .4byte BattleScript_EffectHit @ EFFECT_RETALITATE + .4byte BattleScript_EffectHit @ EFFECT_RETALIATE .4byte BattleScript_EffectBulldoze @ EFFECT_BULLDOZE .4byte BattleScript_EffectHit @ EFFECT_FOUL_PLAY .4byte BattleScript_EffectHit @ EFFECT_PSYSHOCK @@ -369,6 +369,133 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectSleepHit @ EFFECT_SLEEP_HIT .4byte BattleScript_EffectAttackerDefenseDownHit @ EFFECT_ATTACKER_DEFENSE_DOWN_HIT .4byte BattleScript_EffectHit @ EFFECT_BODY_PRESS + .4byte BattleScript_EffectEerieSpell @ EFFECT_EERIE_SPELL + .4byte BattleScript_EffectJungleHealing @ EFFECT_JUNGLE_HEALING + .4byte BattleScript_EffectCoaching @ EFFECT_COACHING + .4byte BattleScript_EffectHit @ EFFECT_LASH_OUT + .4byte BattleScript_EffectHit @ EFFECT_GRASSY_GLIDE + .4byte BattleScript_EffectRemoveTerrain @ EFFECT_REMOVE_TERRAIN + .4byte BattleScript_EffectHit @ EFFECT_DYNAMAX_DOUBLE_DMG + .4byte BattleScript_EffectDecorate @ EFFECT_DECORATE + .4byte BattleScript_EffectHit @ EFFECT_SNIPE_SHOT + .4byte BattleScript_EffectTripleHit @ EFFECT_TRIPLE_HIT + .4byte BattleScript_EffectRecoilHP25 @ EFFECT_RECOIL_HP_25 + +BattleScript_EffectDecorate: + attackcanceler + accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE + attackstring + ppreduce + jumpifstat BS_TARGET, CMP_NOT_EQUAL, STAT_ATK, 12, BattleScript_DecorateBoost + jumpifstat BS_TARGET, CMP_NOT_EQUAL, STAT_SPATK, 12, BattleScript_DecorateBoost + goto BattleScript_ButItFailed +BattleScript_DecorateBoost: + attackanimation + waitanimation + setbyte sSTAT_ANIM_PLAYED, FALSE + playstatchangeanimation BS_TARGET, BIT_ATK | BIT_SPATK, 0x0 + setstatchanger STAT_ATK, 2, FALSE + statbuffchange STAT_BUFF_ALLOW_PTR | STAT_BUFF_NOT_PROTECT_AFFECTED, BattleScript_DecorateBoostSpAtk + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, 0x2, BattleScript_DecorateBoostSpAtk + printfromtable gStatUpStringIds + waitmessage B_WAIT_TIME_LONG +BattleScript_DecorateBoostSpAtk: + setstatchanger STAT_SPATK, 2, FALSE + statbuffchange STAT_BUFF_ALLOW_PTR | STAT_BUFF_NOT_PROTECT_AFFECTED, BattleScript_MoveEnd + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, 0x2, BattleScript_MoveEnd + printfromtable gStatUpStringIds + waitmessage B_WAIT_TIME_LONG + goto BattleScript_MoveEnd + +BattleScript_EffectRemoveTerrain: + attackcanceler + attackstring + ppreduce + jumpifword CMP_NO_COMMON_BITS, gFieldStatuses, STATUS_FIELD_TERRAIN_ANY, BattleScript_ButItFailed + critcalc + damagecalc + adjustdamage + attackanimation + waitanimation + effectivenesssound + hitanimation BS_TARGET + waitstate + healthbarupdate BS_TARGET + datahpupdate BS_TARGET + critmessage + waitmessage B_WAIT_TIME_LONG + resultmessage + waitmessage B_WAIT_TIME_LONG + removeterrain + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, 4, BattleScript_MoveEnd + printfromtable gTerrainEndingStringIds + waitmessage B_WAIT_TIME_LONG + playanimation BS_ATTACKER, B_ANIM_RESTORE_BG, NULL + tryfaintmon BS_TARGET, FALSE, NULL + goto BattleScript_MoveEnd + +BattleScript_EffectCoaching: + attackcanceler + attackstring + ppreduce + jumpifnoally BS_ATTACKER, BattleScript_ButItFailed + copybyte gBattlerTarget, gBattlerAttacker + setallytonexttarget EffectCoaching_CheckAllyStats + goto BattleScript_ButItFailed +EffectCoaching_CheckAllyStats: + jumpifstat BS_TARGET, CMP_NOT_EQUAL, STAT_ATK, MAX_STAT_STAGE, BattleScript_CoachingWorks + jumpifstat BS_TARGET, CMP_NOT_EQUAL, STAT_DEF, MAX_STAT_STAGE, BattleScript_CoachingWorks + goto BattleScript_ButItFailed @ ally at max atk, def +BattleScript_CoachingWorks: + attackanimation + waitanimation + setbyte sSTAT_ANIM_PLAYED, FALSE + playstatchangeanimation BS_TARGET, BIT_ATK | BIT_DEF, 0x0 + setstatchanger STAT_ATK, 1, FALSE + statbuffchange STAT_BUFF_ALLOW_PTR | STAT_BUFF_NOT_PROTECT_AFFECTED, BattleScript_CoachingBoostDef + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, 0x2, BattleScript_CoachingBoostDef + printfromtable gStatUpStringIds + waitmessage B_WAIT_TIME_LONG +BattleScript_CoachingBoostDef: + setstatchanger STAT_DEF, 1, FALSE + statbuffchange STAT_BUFF_ALLOW_PTR | STAT_BUFF_NOT_PROTECT_AFFECTED, BattleScript_MoveEnd + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, 0x2, BattleScript_MoveEnd + printfromtable gStatUpStringIds + waitmessage B_WAIT_TIME_LONG + goto BattleScript_MoveEnd + +BattleScript_EffectJungleHealing: + attackcanceler + attackstring + ppreduce + jumpifteamhealthy BS_ATTACKER, BattleScript_ButItFailed + attackanimation + waitanimation + copybyte gBattlerTarget, gBattlerAttacker + setbyte gBattleCommunication, 0 +JungleHealing_RestoreTargetHealth: + copybyte gBattlerAttacker, gBattlerTarget + tryhealquarterhealth BS_TARGET, BattleScript_JungleHealing_TryCureStatus + orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE + healthbarupdate BS_TARGET + datahpupdate BS_TARGET + printstring STRINGID_PKMNREGAINEDHEALTH + waitmessage B_WAIT_TIME_LONG +BattleScript_JungleHealing_TryCureStatus: + jumpifmove MOVE_LIFE_DEW, BattleScript_JungleHealingTryRestoreAlly @ life dew only heals + jumpifstatus BS_TARGET, STATUS1_ANY, BattleScript_JungleHealingCureStatus + goto BattleScript_JungleHealingTryRestoreAlly +BattleScript_JungleHealingCureStatus: + curestatus BS_TARGET + updatestatusicon BS_TARGET + printstring STRINGID_PKMNSTATUSNORMAL + waitmessage B_WAIT_TIME_LONG +BattleScript_JungleHealingTryRestoreAlly: + jumpifbyte CMP_NOT_EQUAL, gBattleCommunication, 0x0, BattleScript_MoveEnd + addbyte gBattleCommunication, 1 + jumpifnoally BS_TARGET, BattleScript_MoveEnd + setallytonexttarget JungleHealing_RestoreTargetHealth + goto BattleScript_MoveEnd BattleScript_EffectAttackerDefenseDownHit: setmoveeffect MOVE_EFFECT_DEF_MINUS_1 | MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN @@ -1540,6 +1667,7 @@ BattleScript_EffectPsychicTerrain: printfromtable gTerrainStringIds waitmessage B_WAIT_TIME_LONG playanimation BS_SCRIPTING, B_ANIM_RESTORE_BG, NULL + call BattleScript_TerrainSeedLoop goto BattleScript_MoveEnd BattleScript_EffectTopsyTurvy: @@ -1862,6 +1990,26 @@ BattleScript_EffectMagnetRise: goto BattleScript_MoveEnd BattleScript_EffectTrickRoom: + attackcanceler + attackstring + ppreduce + setroom + attackanimation + waitanimation + printfromtable gRoomsStringIds + waitmessage B_WAIT_TIME_LONG + savetarget + setbyte gBattlerTarget, 0 +BattleScript_RoomServiceLoop: + copybyte sBATTLER, gBattlerTarget + tryroomservice BS_TARGET, BattleScript_RoomServiceLoop_NextBattler + removeitem BS_TARGET +BattleScript_RoomServiceLoop_NextBattler: + addbyte gBattlerTarget, 0x1 + jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_RoomServiceLoop + restoretarget + goto BattleScript_MoveEnd + BattleScript_EffectWonderRoom: BattleScript_EffectMagicRoom: attackcanceler @@ -2112,6 +2260,8 @@ BattleScript_EffectSleep:: jumpifleafguard BattleScript_LeafGuardProtects jumpifshieldsdown BS_TARGET, BattleScript_LeafGuardProtects jumpifstatus BS_TARGET, STATUS1_ANY, BattleScript_ButItFailed + 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 @@ -2119,6 +2269,26 @@ BattleScript_EffectSleep:: setmoveeffect MOVE_EFFECT_SLEEP seteffectprimary goto BattleScript_MoveEnd + +BattleScript_TerrainPreventsEnd2:: + pause B_WAIT_TIME_SHORT + printfromtable gTerrainPreventsStringIds + waitmessage B_WAIT_TIME_LONG + end2 + +BattleScript_ElectricTerrainPrevents:: + pause B_WAIT_TIME_SHORT + printstring STRINGID_ELECTRICTERRAINPREVENTS + waitmessage B_WAIT_TIME_LONG + orhalfword gMoveResultFlags, MOVE_RESULT_FAILED + goto BattleScript_MoveEnd + +BattleScript_MistyTerrainPrevents:: + pause B_WAIT_TIME_SHORT + printstring STRINGID_MISTYTERRAINPREVENTS + waitmessage B_WAIT_TIME_LONG + orhalfword gMoveResultFlags, MOVE_RESULT_FAILED + goto BattleScript_MoveEnd BattleScript_FlowerVeilProtectsRet:: pause B_WAIT_TIME_SHORT @@ -2173,18 +2343,21 @@ BattleScript_AlreadyAsleep:: pause B_WAIT_TIME_SHORT printstring STRINGID_PKMNALREADYASLEEP waitmessage B_WAIT_TIME_LONG + orhalfword gMoveResultFlags, MOVE_RESULT_FAILED goto BattleScript_MoveEnd BattleScript_WasntAffected:: pause B_WAIT_TIME_SHORT printstring STRINGID_PKMNWASNTAFFECTED waitmessage B_WAIT_TIME_LONG + orhalfword gMoveResultFlags, MOVE_RESULT_FAILED goto BattleScript_MoveEnd BattleScript_CantMakeAsleep:: pause B_WAIT_TIME_SHORT printfromtable gUproarAwakeStringIds waitmessage B_WAIT_TIME_LONG + orhalfword gMoveResultFlags, MOVE_RESULT_FAILED goto BattleScript_MoveEnd BattleScript_EffectPoisonHit: @@ -2631,6 +2804,7 @@ BattleScript_EffectToxic:: 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 @@ -2771,6 +2945,16 @@ BattleScript_EffectTrap:: setmoveeffect MOVE_EFFECT_WRAP goto BattleScript_EffectHit +BattleScript_EffectTripleHit:: + attackcanceler + accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE + attackstring + ppreduce + setmultihitcounter 3 + initmultihitstring + sethword sMULTIHIT_EFFECT, 0 + goto BattleScript_MultiHitLoop + BattleScript_EffectDoubleHit:: attackcanceler accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE @@ -2855,6 +3039,7 @@ BattleScript_EffectConfuse: jumpifability BS_TARGET, ABILITY_OWN_TEMPO, BattleScript_OwnTempoPrevents jumpifsubstituteblocks BattleScript_ButItFailed jumpifstatus2 BS_TARGET, STATUS2_CONFUSION, BattleScript_AlreadyConfused + jumpifterrainaffected BS_TARGET, STATUS_FIELD_MISTY_TERRAIN, BattleScript_MistyTerrainPrevents accuracycheck BattleScript_ButItFailed, ACC_CURR_MOVE jumpifsafeguard BattleScript_SafeguardProtected attackanimation @@ -2973,6 +3158,7 @@ BattleScript_EffectPoison:: 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 @@ -2998,6 +3184,7 @@ BattleScript_EffectParalyze: jumpifstatus BS_TARGET, STATUS1_PARALYSIS, BattleScript_AlreadyParalyzed 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 bichalfword gMoveResultFlags, MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE @@ -3420,6 +3607,33 @@ BattleScript_EffectDestinyBond:: waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd +BattleScript_EffectEerieSpell:: + attackcanceler + attackstring + ppreduce + accuracycheck BattleScript_ButItFailed, ACC_CURR_MOVE + attackstring + ppreduce + critcalc + damagecalc + adjustdamage + attackanimation + waitanimation + effectivenesssound + hitanimation BS_TARGET + waitstate + healthbarupdate BS_TARGET + datahpupdate BS_TARGET + critmessage + waitmessage B_WAIT_TIME_LONG + resultmessage + waitmessage B_WAIT_TIME_LONG + tryfaintmon BS_TARGET, FALSE, NULL + eeriespellppreduce BattleScript_MoveEnd + printstring STRINGID_PKMNREDUCEDPP + waitmessage B_WAIT_TIME_LONG + goto BattleScript_MoveEnd + BattleScript_EffectSpite:: attackcanceler attackstring @@ -3470,6 +3684,13 @@ BattleScript_TripleKickLoop:: BattleScript_DoTripleKickAttack:: accuracycheck BattleScript_TripleKickNoMoreHits, ACC_CURR_MOVE movevaluescleanup + jumpifmove MOVE_SURGING_STRIKES, EffectTripleKick_DoDmgCalcs @ no power boost each hit + jumpifmove MOVE_TRIPLE_AXEL, EffectTripleKick_TripleAxelBoost @ triple axel gets +20 power + addbyte sTRIPLE_KICK_POWER, 10 @ triple kick gets +10 power + goto EffectTripleKick_DoDmgCalcs +EffectTripleKick_TripleAxelBoost: + addbyte sTRIPLE_KICK_POWER, 20 +EffectTripleKick_DoDmgCalcs: addbyte sTRIPLE_KICK_POWER, 10 addbyte sMULTIHIT_STRING + 4, 1 critcalc @@ -3657,19 +3878,25 @@ BattleScript_EffectPerishSong:: waitanimation printstring STRINGID_FAINTINTHREE waitmessage B_WAIT_TIME_LONG - setbyte sBATTLER, 0 + setbyte gBattlerTarget, 0 BattleScript_PerishSongLoop:: - jumpifability BS_SCRIPTING, ABILITY_SOUNDPROOF, BattleScript_PerishSongNotAffected + jumpifability BS_TARGET, ABILITY_SOUNDPROOF, BattleScript_PerishSongBlocked + jumpifpranksterblocked BS_TARGET, BattleScript_PerishSongNotAffected BattleScript_PerishSongLoopIncrement:: - addbyte sBATTLER, 1 - jumpifbytenotequal sBATTLER, gBattlersCount, BattleScript_PerishSongLoop + addbyte gBattlerTarget, 1 + jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_PerishSongLoop goto BattleScript_MoveEnd -BattleScript_PerishSongNotAffected:: +BattleScript_PerishSongBlocked:: printstring STRINGID_PKMNSXBLOCKSY2 waitmessage B_WAIT_TIME_LONG goto BattleScript_PerishSongLoopIncrement +BattleScript_PerishSongNotAffected: + printstring STRINGID_ITDOESNTAFFECT + waitmessage B_WAIT_TIME_LONG + goto BattleScript_PerishSongLoopIncrement + BattleScript_EffectSandstorm:: attackcanceler attackstring @@ -4302,6 +4529,7 @@ BattleScript_EffectWillOWisp:: jumpifleafguard BattleScript_LeafGuardProtects jumpifshieldsdown BS_TARGET, BattleScript_LeafGuardProtects 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 @@ -4694,6 +4922,12 @@ BattleScript_EffectRecoil50: setmoveeffect MOVE_EFFECT_RECOIL_50 | MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN goto BattleScript_EffectHit +BattleScript_EffectRecoilHP25: + setmoveeffect MOVE_EFFECT_RECOIL_HP_25 | MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN + jumpifnotmove MOVE_STRUGGLE, BattleScript_EffectHit + incrementgamestat GAME_STAT_USED_STRUGGLE + goto BattleScript_EffectHit + BattleScript_EffectTeeterDance:: attackcanceler attackstring @@ -5557,16 +5791,28 @@ BattleScript_RoarSuccessSwitch:: waitstate printstring STRINGID_PKMNWASDRAGGEDOUT switchineffects BS_TARGET + jumpifbyte CMP_EQUAL, sSWITCH_CASE, B_SWITCH_RED_CARD, BattleScript_RoarSuccessSwitch_Ret + setbyte sSWITCH_CASE, B_SWITCH_NORMAL goto BattleScript_MoveEnd +BattleScript_RoarSuccessSwitch_Ret: + swapattackerwithtarget @ continuation of RedCardActivates + restoretarget + removeitem BS_TARGET + setbyte sSWITCH_CASE, B_SWITCH_NORMAL + return BattleScript_RoarSuccessEndBattle:: call BattleScript_RoarSuccessRet + setbyte sSWITCH_CASE, B_SWITCH_NORMAL setoutcomeonteleport BS_ATTACKER finishaction BattleScript_RoarSuccessRet: + jumpifbyte CMP_EQUAL, sSWITCH_CASE, B_SWITCH_HIT, BattleScript_RoarSuccessRet_Ret + jumpifbyte CMP_EQUAL, sSWITCH_CASE, B_SWITCH_RED_CARD, BattleScript_RoarSuccessRet_Ret attackanimation waitanimation +BattleScript_RoarSuccessRet_Ret: switchoutabilities BS_TARGET returntoball BS_TARGET waitstate @@ -5611,6 +5857,21 @@ BattleScript_TargetItemStatRaise:: removeitem BS_TARGET BattleScript_TargetItemStatRaiseRemoveItemRet: return + +BattleScript_AttackerItemStatRaise:: + copybyte sBATTLER, gBattlerAttacker + statbuffchange MOVE_EFFECT_AFFECTS_USER, BattleScript_AttackerItemStatRaiseRet + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, 0x2, BattleScript_AttackerItemStatRaiseRet + playanimation BS_ATTACKER, B_ANIM_HELD_ITEM_EFFECT, NULL + waitanimation + setgraphicalstatchangevalues + playanimation BS_ATTACKER, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1 + waitanimation + printstring STRINGID_USINGITEMSTATOFPKMNROSE + waitmessage 0x40 + removeitem BS_ATTACKER +BattleScript_AttackerItemStatRaiseRet: + return BattleScript_MistProtected:: pause B_WAIT_TIME_SHORT @@ -5756,6 +6017,76 @@ BattleScript_PerishBodyActivates:: orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_x100000 return +BattleScript_GulpMissileGorging:: + call BattleScript_AbilityPopUp + playanimation BS_ATTACKER, B_ANIM_GULP_MISSILE, NULL + waitanimation + orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_x100000 + effectivenesssound + hitanimation BS_ATTACKER + waitstate + jumpifability BS_ATTACKER, ABILITY_MAGIC_GUARD, BattleScript_GulpMissileNoDmgGorging + healthbarupdate BS_ATTACKER + datahpupdate BS_ATTACKER + tryfaintmon BS_ATTACKER, FALSE, NULL + getbattlerfainted BS_ATTACKER + jumpifbyte CMP_EQUAL, gBattleCommunication, TRUE, BattleScript_GulpMissileNoSecondEffectGorging +BattleScript_GulpMissileNoDmgGorging: + handleformchange BS_TARGET, 0 + playanimation BS_TARGET, B_ANIM_FORM_CHANGE, NULL + waitanimation + swapattackerwithtarget + setmoveeffect MOVE_EFFECT_PARALYSIS + seteffectprimary + swapattackerwithtarget + return +BattleScript_GulpMissileNoSecondEffectGorging: + handleformchange BS_TARGET, 0 + playanimation BS_TARGET, B_ANIM_FORM_CHANGE, NULL + waitanimation + return + +BattleScript_GulpMissileGulping:: + call BattleScript_AbilityPopUp + playanimation BS_ATTACKER, B_ANIM_GULP_MISSILE, NULL + waitanimation + orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_x100000 + effectivenesssound + hitanimation BS_ATTACKER + waitstate + jumpifability BS_ATTACKER, ABILITY_MAGIC_GUARD, BattleScript_GulpMissileNoDmgGulping + healthbarupdate BS_ATTACKER + datahpupdate BS_ATTACKER + tryfaintmon BS_ATTACKER, FALSE, NULL + getbattlerfainted BS_ATTACKER + jumpifbyte CMP_EQUAL, gBattleCommunication, TRUE, BattleScript_GulpMissileNoSecondEffectGulping + jumpifability BS_ATTACKER, ABILITY_CLEAR_BODY, BattleScript_GulpMissileNoSecondEffectGulping + jumpifability BS_ATTACKER, ABILITY_FULL_METAL_BODY, BattleScript_GulpMissileNoSecondEffectGulping + jumpifability BS_ATTACKER, ABILITY_WHITE_SMOKE, BattleScript_GulpMissileNoSecondEffectGulping + jumpifflowerveilattacker BattleScript_GulpMissileNoSecondEffectGulping +BattleScript_GulpMissileNoDmgGulping: + handleformchange BS_TARGET, 0 + playanimation BS_TARGET, B_ANIM_FORM_CHANGE, NULL + waitanimation + swapattackerwithtarget @ to make gStatDownStringIds down below print the right battler + setstatchanger STAT_DEF, 1, TRUE + statbuffchange STAT_BUFF_NOT_PROTECT_AFFECTED, BattleScript_GulpMissileGorgingTargetDefenseCantGoLower + setgraphicalstatchangevalues + playanimation BS_TARGET, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1 + printfromtable gStatDownStringIds + waitmessage B_WAIT_TIME_LONG + swapattackerwithtarget @ restore the battlers, just in case + return +BattleScript_GulpMissileNoSecondEffectGulping: + handleformchange BS_TARGET, 0 + playanimation BS_TARGET, B_ANIM_FORM_CHANGE, NULL + waitanimation + return +BattleScript_GulpMissileGorgingTargetDefenseCantGoLower: + printstring STRINGID_STATSWONTDECREASE + waitmessage B_WAIT_TIME_LONG + return + BattleScript_PerishSongCountGoesDown:: printstring STRINGID_PKMNPERISHCOUNTFELL waitmessage B_WAIT_TIME_LONG @@ -6090,6 +6421,17 @@ BattleScript_MagicCoatBounce:: setmagiccoattarget BS_ATTACKER return +BattleScript_MagicCoatBouncePrankster:: + attackstring + ppreduce + pause B_WAIT_TIME_SHORT + printfromtable gMagicCoatBounceStringIds + waitmessage B_WAIT_TIME_LONG + printstring STRINGID_ITDOESNTAFFECT + waitmessage B_WAIT_TIME_LONG + orhalfword gMoveResultFlags, MOVE_RESULT_NO_EFFECT + goto BattleScript_MoveEnd + BattleScript_SnatchedMove:: attackstring ppreduce @@ -6894,6 +7236,19 @@ BattleScript_SnowWarningActivates:: playanimation BS_BATTLER_0, B_ANIM_HAIL_CONTINUES, NULL call BattleScript_WeatherFormChanges end3 + +BattleScript_TerrainSeedLoop: + savetarget + setbyte gBattlerTarget, 0 +BattleScript_TerrainSeedLoopIter: + copybyte sBATTLER, gBattlerTarget + doterrainseed BS_TARGET, BattleScript_TerrainSeedLoop_NextBattler + removeitem BS_TARGET +BattleScript_TerrainSeedLoop_NextBattler: + addbyte gBattlerTarget, 0x1 + jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_TerrainSeedLoopIter + restoretarget + return BattleScript_ElectricSurgeActivates:: pause B_WAIT_TIME_SHORT @@ -6901,6 +7256,7 @@ BattleScript_ElectricSurgeActivates:: printstring STRINGID_TERRAINBECOMESELECTRIC waitmessage B_WAIT_TIME_LONG playanimation BS_SCRIPTING, B_ANIM_RESTORE_BG, NULL + call BattleScript_TerrainSeedLoop end3 BattleScript_MistySurgeActivates:: @@ -6909,6 +7265,7 @@ BattleScript_MistySurgeActivates:: printstring STRINGID_TERRAINBECOMESMISTY waitmessage B_WAIT_TIME_LONG playanimation BS_SCRIPTING, B_ANIM_RESTORE_BG, NULL + call BattleScript_TerrainSeedLoop end3 BattleScript_GrassySurgeActivates:: @@ -6917,6 +7274,7 @@ BattleScript_GrassySurgeActivates:: printstring STRINGID_TERRAINBECOMESGRASSY waitmessage B_WAIT_TIME_LONG playanimation BS_SCRIPTING, B_ANIM_RESTORE_BG, NULL + call BattleScript_TerrainSeedLoop end3 BattleScript_PsychicSurgeActivates:: @@ -6925,6 +7283,7 @@ BattleScript_PsychicSurgeActivates:: printstring STRINGID_TERRAINBECOMESPSYCHIC waitmessage B_WAIT_TIME_LONG playanimation BS_SCRIPTING, B_ANIM_RESTORE_BG, NULL + call BattleScript_TerrainSeedLoop end3 BattleScript_BadDreamsActivates:: @@ -7076,6 +7435,7 @@ BattleScript_SoundproofProtected:: call BattleScript_AbilityPopUp printstring STRINGID_PKMNSXBLOCKSY waitmessage B_WAIT_TIME_LONG + orhalfword gMoveResultFlags, MOVE_RESULT_DOESNT_AFFECT_FOE goto BattleScript_MoveEnd BattleScript_DazzlingProtected:: @@ -7119,6 +7479,7 @@ BattleScript_AbilityNoSpecificStatLoss:: printstring STRINGID_PKMNSXPREVENTSYLOSS waitmessage B_WAIT_TIME_LONG setbyte cMULTISTRING_CHOOSER, B_MSG_STAT_FELL_EMPTY + orhalfword gMoveResultFlags, MOVE_RESULT_NO_EFFECT return BattleScript_StickyHoldActivates:: @@ -7367,6 +7728,19 @@ BattleScript_AbilityStatusEffect:: seteffectsecondary return +BattleScript_BattleBondActivatesOnMoveEndAttacker:: + pause 5 + copybyte gBattlerAbility, gBattlerAttacker + call BattleScript_AbilityPopUp + printstring STRINGID_ATTACKERBECAMEFULLYCHARGED + handleformchange BS_ATTACKER, 0 + handleformchange BS_ATTACKER, 1 + playanimation BS_ATTACKER, B_ANIM_FORM_CHANGE, NULL + waitanimation + handleformchange BS_ATTACKER, 2 + printstring STRINGID_ATTACKERBECAMEASHSPECIES + return + BattleScript_DancerActivates:: call BattleScript_AbilityPopUp waitmessage B_WAIT_TIME_SHORT @@ -7529,6 +7903,7 @@ BattleScript_BerryReduceDmg:: BattleScript_PrintBerryReduceString:: waitmessage B_WAIT_TIME_LONG printstring STRINGID_BERRYDMGREDUCES + waitmessage B_WAIT_TIME_LONG return BattleScript_BerryCureConfusionEnd2:: @@ -7554,6 +7929,19 @@ BattleScript_BerryCureChosenStatusRet:: removeitem BS_SCRIPTING return +BattleScript_MentalHerbCureRet:: + playanimation BS_ATTACKER, B_ANIM_HELD_ITEM_EFFECT, NULL + printfromtable gMentalHerbCureStringIds + waitmessage B_WAIT_TIME_LONG + updatestatusicon BS_SCRIPTING + removeitem BS_SCRIPTING + copybyte gBattlerAttacker, sSAVED_BATTLER @ restore the original attacker just to be safe + return + +BattleScript_MentalHerbCureEnd2:: + call BattleScript_MentalHerbCureRet + end2 + BattleScript_WhiteHerbEnd2:: call BattleScript_WhiteHerbRet end2 @@ -7707,12 +8095,13 @@ BattleScript_BerryStatRaiseEnd2:: BattleScript_BerryStatRaiseEnd2_AbilityPopup: call BattleScript_AbilityPopUp BattleScript_BerryStatRaiseEnd2_Anim: - playanimation BS_ATTACKER, B_ANIM_HELD_ITEM_EFFECT, NULL - statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_BUFF_ALLOW_PTR, BattleScript_BerryStatRaiseDoStatUp -BattleScript_BerryStatRaiseDoStatUp:: + statbuffchange STAT_BUFF_ALLOW_PTR, BattleScript_BerryStatRaiseEnd2_End + setgraphicalstatchangevalues + playanimation BS_ATTACKER, B_ANIM_HELD_ITEM_EFFECT, sB_ANIM_ARG1 setbyte cMULTISTRING_CHOOSER, B_MSG_STAT_ROSE_ITEM call BattleScript_StatUp removeitem BS_ATTACKER +BattleScript_BerryStatRaiseEnd2_End:: end2 BattleScript_BerryStatRaiseRet:: @@ -7721,12 +8110,13 @@ BattleScript_BerryStatRaiseRet:: BattleScript_BerryStatRaiseRet_AbilityPopup: call BattleScript_AbilityPopUp BattleScript_BerryStatRaiseRet_Anim: - playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT, NULL statbuffchange STAT_BUFF_ALLOW_PTR, BattleScript_BerryStatRaiseRet_End -BattleScript_BerryStatRaiseRet_End: + setgraphicalstatchangevalues + playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT, sB_ANIM_ARG1 setbyte cMULTISTRING_CHOOSER, B_MSG_STAT_ROSE_ITEM call BattleScript_StatUp removeitem BS_SCRIPTING +BattleScript_BerryStatRaiseRet_End: return BattleScript_BerryFocusEnergyEnd2:: @@ -7980,3 +8370,69 @@ BattleScript_StickyBarbTransfer:: removeitem BS_TARGET return +BattleScript_RedCardActivates:: + playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT, NULL + printstring STRINGID_REDCARDACTIVATE + waitmessage 0x40 + swapattackerwithtarget + jumpifstatus3 BS_EFFECT_BATTLER, STATUS3_ROOTED, BattleScript_RedCardIngrain + jumpifability BS_EFFECT_BATTLER, ABILITY_SUCTION_CUPS, BattleScript_RedCardSuctionCups + setbyte sSWITCH_CASE, B_SWITCH_RED_CARD + forcerandomswitch BattleScript_RedCardEnd + @ changes the current battle script. the rest happens in BattleScript_RoarSuccessSwitch_Ret, if switch is successful +BattleScript_RedCardEnd: + return +BattleScript_RedCardIngrain: + printstring STRINGID_PKMNANCHOREDITSELF + waitmessage 0x40 + removeitem BS_SCRIPTING + swapattackerwithtarget + return +BattleScript_RedCardSuctionCups: + printstring STRINGID_PKMNANCHORSITSELFWITH + waitmessage 0x40 + removeitem BS_SCRIPTING + swapattackerwithtarget + return + +BattleScript_EjectButtonActivates:: + makevisible BS_ATTACKER + playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT, NULL + printstring STRINGID_EJECTBUTTONACTIVATE + waitmessage 0x40 + removeitem BS_SCRIPTING + makeinvisible BS_SCRIPTING + openpartyscreen BS_SCRIPTING, BattleScript_EjectButtonEnd + switchoutabilities BS_SCRIPTING + waitstate + switchhandleorder BS_SCRIPTING 0x2 + returntoball BS_SCRIPTING + getswitchedmondata BS_SCRIPTING + switchindataupdate BS_SCRIPTING + hpthresholds BS_SCRIPTING + printstring 0x3 + switchinanim BS_SCRIPTING 0x1 + waitstate + switchineffects BS_SCRIPTING +BattleScript_EjectButtonEnd: + return + +BattleScript_EjectPackActivate_Ret:: + goto BattleScript_EjectButtonActivates + +BattleScript_EjectPackActivate_End2:: + call BattleScript_EjectPackActivate_Ret + end2 + +BattleScript_EjectPackActivates:: + jumpifcantswitch BS_SCRIPTING, BattleScript_EjectButtonEnd + goto BattleScript_EjectPackActivate_Ret + +BattleScript_DarkTypePreventsPrankster:: + attackstring + ppreduce + pause B_WAIT_TIME_SHORT + printstring STRINGID_ITDOESNTAFFECT + waitmessage B_WAIT_TIME_LONG + orhalfword gMoveResultFlags, MOVE_RESULT_NO_EFFECT + goto BattleScript_MoveEnd diff --git a/include/battle.h b/include/battle.h index 576884d81..571df66c8 100644 --- a/include/battle.h +++ b/include/battle.h @@ -119,6 +119,7 @@ struct ProtectStruct u32 spikyShielded:1; u32 kingsShielded:1; u32 banefulBunkered:1; + u32 obstructed:1; u32 endured:1; u32 noValidMoves:1; u32 helpingHand:1; @@ -126,7 +127,7 @@ struct ProtectStruct u32 stealMove:1; u32 prlzImmobility:1; u32 confusionSelfDmg:1; - u32 targetNotAffected:1; + u32 targetAffected:1; u32 chargingTurn:1; u32 fleeFlag:2; // For RunAway and Smoke Ball. u32 usedImprisonedMove:1; @@ -142,9 +143,13 @@ struct ProtectStruct u32 usedGravityPreventedMove:1; u32 powderSelfDmg:1; u32 usedThroatChopPreventedMove:1; + u32 statRaised:1; u32 micle:1; u32 custap:1; // also quick claw u32 touchedProtectLike:1; + u32 disableEjectPack:1; + u32 statFell:1; + u32 pranksterElevated:1; u32 physicalDmg; u32 specialDmg; u8 physicalBattlerId; @@ -597,6 +602,7 @@ struct BattleStruct u16 changedSpecies[PARTY_SIZE]; // For Zygarde or future forms when multiple mons can change into the same pokemon. u8 quickClawBattlerId; struct StolenItem itemStolen[PARTY_SIZE]; // Player's team that had items stolen (two bytes per party member) + u8 blunderPolicy:1; // should blunder policy activate }; #define GET_MOVE_TYPE(move, typeArg) \ @@ -668,8 +674,9 @@ struct BattleScripting u16 moveEffect; u16 multihitMoveEffect; u8 illusionNickHack; // To properly display nick in STRINGID_ENEMYABOUTTOSWITCHPKMN. - bool8 fixedPopup; // force ability popup to stick until manually called back + bool8 fixedPopup; // Force ability popup to stick until manually called back u16 abilityPopupOverwrite; + u8 switchCase; // Special switching conditions, eg. red card }; // rom_80A5C6C diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index d4df682c6..b8cd53c07 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -119,10 +119,10 @@ bool32 IsUngroundingEffect(u16 effect); bool32 IsSemiInvulnerable(u8 battlerDef, u16 move); // status checks -bool32 CanBeBurned(u8 battler, u16 ability); -bool32 CanBePoisoned(u8 battler, u16 ability); -bool32 CanBeConfused(u8 battler, u16 ability); -bool32 CanSleep(u8 battler, u16 ability); +bool32 AI_CanBeBurned(u8 battler, u16 ability); +bool32 AI_CanBePoisoned(u8 battler, u16 ability); +bool32 AI_CanBeConfused(u8 battler, u16 ability); +bool32 AI_CanSleep(u8 battler, u16 ability); bool32 IsBattlerIncapacitated(u8 battler, u16 ability); bool32 AI_CanPutToSleep(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u16 partnerMove); bool32 ShouldPoisonSelf(u8 battler, u16 ability); diff --git a/include/battle_controllers.h b/include/battle_controllers.h index 1cedf81fa..71ca64097 100644 --- a/include/battle_controllers.h +++ b/include/battle_controllers.h @@ -185,6 +185,7 @@ extern struct UnusedControllerStruct gUnusedControllerStruct; void HandleLinkBattleSetup(void); void SetUpBattleVarsAndBirchZigzagoon(void); void InitBattleControllers(void); +bool32 IsValidForBattle(struct Pokemon *mon); void TryReceiveLinkBattleData(void); void PrepareBufferDataTransferLink(u8 bufferId, u16 size, u8 *data); diff --git a/include/battle_script_commands.h b/include/battle_script_commands.h index 2e6a0aa4e..dec1596f2 100644 --- a/include/battle_script_commands.h +++ b/include/battle_script_commands.h @@ -13,6 +13,7 @@ struct StatFractions }; s32 CalcCritChanceStage(u8 battlerAtk, u8 battlerDef, u32 move, bool32 recordAbility); +s8 GetInverseCritChance(u8 battlerAtk, u8 battlerDef, u32 move); u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move); u8 GetBattlerTurnOrderNum(u8 battlerId); bool32 NoAliveMonsForEitherParty(void); diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 232a40aa5..203be6519 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -97,6 +97,7 @@ extern const u8 BattleScript_SelectingImprisonedMove[]; extern const u8 BattleScript_SelectingImprisonedMoveInPalace[]; extern const u8 BattleScript_GrudgeTakesPp[]; extern const u8 BattleScript_MagicCoatBounce[]; +extern const u8 BattleScript_MagicCoatBouncePrankster[]; extern const u8 BattleScript_SnatchedMove[]; extern const u8 BattleScript_EnduredMsg[]; extern const u8 BattleScript_OneHitKOMsg[]; @@ -373,6 +374,20 @@ extern const u8 BattleScript_BattlerShookOffTaunt[]; extern const u8 BattleScript_BattlerGotOverItsInfatuation[]; extern const u8 BattleScript_Pickpocket[]; extern const u8 BattleScript_StickyBarbTransfer[]; - +extern const u8 BattleScript_AttackerItemStatRaise[]; +extern const u8 BattleScript_RedCardActivates[]; +extern const u8 BattleScript_EjectButtonActivates[]; +extern const u8 BattleScript_EjectPackActivate_Ret[]; +extern const u8 BattleScript_EjectPackActivate_End2[]; +extern const u8 BattleScript_EjectPackActivates[]; +extern const u8 BattleScript_MentalHerbCureRet[]; +extern const u8 BattleScript_MentalHerbCureEnd2[]; +extern const u8 BattleScript_TerrainPreventsEnd2[]; +extern const u8 BattleScript_MistyTerrainPrevents[]; +extern const u8 BattleScript_ElectricTerrainPrevents[]; +extern const u8 BattleScript_DarkTypePreventsPrankster[]; +extern const u8 BattleScript_GulpMissileGorging[]; +extern const u8 BattleScript_GulpMissileGulping[]; +extern const u8 BattleScript_BattleBondActivatesOnMoveEndAttacker[]; #endif // GUARD_BATTLE_SCRIPTS_H diff --git a/include/battle_util.h b/include/battle_util.h index f46db8898..612a2e6d9 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -28,9 +28,10 @@ #define ITEMEFFECT_ON_SWITCH_IN 0x0 #define ITEMEFFECT_MOVE_END 0x3 -#define ITEMEFFECT_KINGSROCK_SHELLBELL 0x4 +#define ITEMEFFECT_KINGSROCK 0x4 #define ITEMEFFECT_TARGET 0x5 #define ITEMEFFECT_ORBS 0x6 +#define ITEMEFFECT_LIFEORB_SHELLBELL 0x7 #define WEATHER_HAS_EFFECT ((!IsAbilityOnField(ABILITY_CLOUD_NINE) && !IsAbilityOnField(ABILITY_AIR_LOCK))) @@ -46,7 +47,7 @@ struct TypePower extern const struct TypePower gNaturalGiftTable[]; -bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide); +bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move); void HandleAction_UseMove(void); void HandleAction_Switch(void); void HandleAction_UseItem(void); @@ -144,7 +145,14 @@ void TryRestoreStolenItems(void); bool32 CanStealItem(u8 battlerStealing, u8 battlerItem, u16 item); void TrySaveExchangedItem(u8 battlerId, u16 stolenItem); bool32 IsPartnerMonFromSameTrainer(u8 battlerId); - +u8 TryHandleSeed(u8 battler, u32 terrainFlag, u8 statId, u16 itemId, bool32 execute); +bool32 IsBattlerAffectedByHazards(u8 battlerId, bool32 toxicSpikes); +void SortBattlersBySpeed(u8 *battlers, bool8 slowToFast); +bool32 CompareStat(u8 battlerId, u8 statId, u8 cmpTo, u8 cmpKind); +bool32 TryRoomService(u8 battlerId); +void BufferStatChange(u8 battlerId, u8 statId, u8 stringId); +void DoBurmyFormChange(u32 monId); +bool32 BlocksPrankster(u16 move, u8 battlerPrankster, u8 battlerDef); // ability checks bool32 IsRolePlayBannedAbilityAtk(u16 ability); @@ -155,4 +163,12 @@ bool32 IsGastroAcidBannedAbility(u16 ability); bool32 IsEntrainmentBannedAbilityAttacker(u16 ability); bool32 IsEntrainmentTargetOrSimpleBeamBannedAbility(u16 ability); +bool32 CanSleep(u8 battlerId); +bool32 CanBePoisoned(u8 battlerId); +bool32 CanBeBurned(u8 battlerId); +bool32 CanBeParalyzed(u8 battlerId); +bool32 CanBeFrozen(u8 battlerId); +bool32 CanBeConfused(u8 battlerId); +bool32 IsBattlerTerrainAffected(u8 battlerId, u32 terrainFlag); + #endif // GUARD_BATTLE_UTIL_H diff --git a/include/constants/battle.h b/include/constants/battle.h index 53e933944..ec6f57d70 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -239,7 +239,7 @@ #define STATUS_FIELD_FAIRY_LOCK (1 << 11) #define STATUS_FIELD_TERRAIN_PERMANENT (1 << 12) // Overworld thunderstorm generates electric terrain -#define STATUS_TERRAIN_ANY (STATUS_FIELD_GRASSY_TERRAIN | STATUS_FIELD_MISTY_TERRAIN | STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_PSYCHIC_TERRAIN) +#define STATUS_FIELD_TERRAIN_ANY (STATUS_FIELD_GRASSY_TERRAIN | STATUS_FIELD_MISTY_TERRAIN | STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_PSYCHIC_TERRAIN) // Flags describing move's result #define MOVE_RESULT_MISSED (1 << 0) @@ -347,7 +347,8 @@ #define MOVE_EFFECT_THROAT_CHOP 0x43 #define MOVE_EFFECT_INCINERATE 0x44 #define MOVE_EFFECT_BUG_BITE 0x45 -#define NUM_MOVE_EFFECTS 0x46 +#define MOVE_EFFECT_RECOIL_HP_25 0x46 +#define NUM_MOVE_EFFECTS 0x47 #define MOVE_EFFECT_AFFECTS_USER 0x4000 #define MOVE_EFFECT_CERTAIN 0x8000 diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index c04bc14f7..39e1feda9 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -523,6 +523,7 @@ #define B_ANIM_SLIDE_OFFSCREEN 26 // for Emergency Exit #define B_ANIM_RESTORE_BG 27 // for Terrain Endings #define B_ANIM_TOTEM_FLARE 28 // Totem boosts aura flare +#define B_ANIM_GULP_MISSILE 29 // special animations table (gBattleAnims_Special) #define B_ANIM_LVL_UP 0 diff --git a/include/constants/battle_config.h b/include/constants/battle_config.h index b87af3122..10669e36b 100644 --- a/include/constants/battle_config.h +++ b/include/constants/battle_config.h @@ -37,6 +37,14 @@ #define SPECIES_ZYGARDE 0 // 50% #define SPECIES_ZYGARDE_10 10011 // 10 % #define SPECIES_ZYGARDE_COMPLETE 10012 // 100 % + #define SPECIES_BURMY 0 + #define SPECIES_BURMY_SANDY_CLOAK 10013 + #define SPECIES_BURMY_TRASH_CLOAK 10014 + #define SPECIES_CRAMORANT 0 + #define SPECIES_CRAMORANT_GORGING 10015 + #define SPECIES_CRAMORANT_GULPING 10016 + #define SPECIES_GRENINJA_BATTLE_BOND 0 + #define SPECIES_GRENINJA_ASH 10017 #endif // Items with peculiar battle effects. @@ -121,16 +129,18 @@ #define B_GALE_WINGS GEN_7 // In Gen7+ requires full HP to trigger. #define B_STANCE_CHANGE_FAIL GEN_7 // In Gen7+, Stance Change fails if the Pokémon is unable to use a move because of confusion, paralysis, etc. In Gen6, it doesn't. #define B_GHOSTS_ESCAPE GEN_7 // In Gen6+, Ghost-type Pokémon can escape even when blocked by abilities such as Shadow Tag. +#define B_SHADOW_TAG_ESCAPE GEN_7 // In Gen4+, if both sides have a Pokémon with Shadow Tag, all battlers can escape. Before, neither side could escape this situation. #define B_MOODY_ACC_EVASION GEN_8 // In Gen8, Moody CANNOT raise Accuracy and Evasion anymore. #define B_FLASH_FIRE_FROZEN GEN_7 // In Gen5+, Flash Fire can trigger even when frozen, when it couldn't before. #define B_SYNCHRONIZE_NATURE GEN_8 // In Gen8, if the Pokémon with Synchronize is leading the party, it's 100% guaranteed that wild Pokémon will have the same ability, as opposed to 50% previously. #define B_UPDATED_INTIMIDATE GEN_8 // In Gen8, Intimidate doesn't work on opponents with the Inner Focus, Scrappy, Own Tempo or Oblivious abilities. +#define B_PRANKSTER_DARK_TYPES GEN_7 // In Gen7+, Prankster-elevated status moves do not affect Dark type Pokémon. // Item settings #define B_HP_BERRIES GEN_7 // In Gen4+, berries which restore hp activate immediately after HP drops to half. In Gen3, the effect occurs at the end of the turn. #define B_BERRIES_INSTANT GEN_7 // In Gen4+, most berries activate on battle start/switch-in if applicable. In Gen3, they only activate either at the move end or turn end. #define B_X_ITEMS_BUFF GEN_7 // In Gen7+, the X Items raise a stat by 2 stages instead of 1. -#define B_MENTAL_HERB GEN_5 // In Gen5+, mental herb cures Taunt, Encore, Heal Block, and Disable +#define B_MENTAL_HERB GEN_5 // In Gen5+, the Mental Herb cures Infatuation, Taunt, Encore, Torment, Heal Block, and Disable // Flag settings // To use the following features in scripting, replace the 0s with the flag ID you're assigning it to. diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index ec4978dd4..5038b52c3 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -236,7 +236,7 @@ #define EFFECT_ROUND 230 #define EFFECT_BRINE 231 #define EFFECT_VENOSHOCK 232 -#define EFFECT_RETALITATE 233 +#define EFFECT_RETALIATE 233 #define EFFECT_BULLDOZE 234 #define EFFECT_FOUL_PLAY 235 #define EFFECT_PSYSHOCK 236 @@ -353,7 +353,18 @@ #define EFFECT_SLEEP_HIT 347 #define EFFECT_ATTACKER_DEFENSE_DOWN_HIT 348 #define EFFECT_BODY_PRESS 349 +#define EFFECT_EERIE_SPELL 350 +#define EFFECT_JUNGLE_HEALING 351 +#define EFFECT_COACHING 352 +#define EFFECT_LASH_OUT 353 +#define EFFECT_GRASSY_GLIDE 354 +#define EFFECT_REMOVE_TERRAIN 355 +#define EFFECT_DYNAMAX_DOUBLE_DMG 356 +#define EFFECT_DECORATE 357 +#define EFFECT_SNIPE_SHOT 358 +#define EFFECT_TRIPLE_HIT 359 +#define EFFECT_RECOIL_HP_25 360 -#define NUM_BATTLE_MOVE_EFFECTS 350 +#define NUM_BATTLE_MOVE_EFFECTS 361 #endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 6c5e3872a..961498e9f 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -37,6 +37,7 @@ #define sILLUSION_NICK_HACK gBattleScripting + 0x32 #define sFIXED_ABILITY_POPUP gBattleScripting + 0x33 #define sABILITY_OVERWRITE gBattleScripting + 0x34 +#define sSWITCH_CASE gBattleScripting + 0x36 #define cMULTISTRING_CHOOSER gBattleCommunication + 5 #define cMISS_TYPE gBattleCommunication + 6 @@ -174,8 +175,17 @@ #define VARIOUS_TOTEM_BOOST 103 #define VARIOUS_TRY_ACTIVATE_GRIM_NEIGH 104 #define VARIOUS_MOVEEND_ITEM_EFFECTS 105 -#define VARIOUS_GET_ROTOTILLER_TARGETS 106 -#define VARIOUS_JUMP_IF_NOT_ROTOTILLER_AFFECTED 107 +#define VARIOUS_TERRAIN_SEED 106 +#define VARIOUS_MAKE_INVISIBLE 107 +#define VARIOUS_ROOM_SERVICE 108 +#define VARIOUS_JUMP_IF_TERRAIN_AFFECTED 109 +#define VARIOUS_EERIE_SPELL_PP_REDUCE 110 +#define VARIOUS_JUMP_IF_TEAM_HEALTHY 111 +#define VARIOUS_TRY_HEAL_QUARTER_HP 112 +#define VARIOUS_REMOVE_TERRAIN 113 +#define VARIOUS_JUMP_IF_PRANKSTER_BLOCKED 114 +#define VARIOUS_GET_ROTOTILLER_TARGETS 115 +#define VARIOUS_JUMP_IF_NOT_ROTOTILLER_AFFECTED 116 // Cmd_manipulatedamage #define DMG_CHANGE_SIGN 0 @@ -230,16 +240,24 @@ #define MOVEEND_ITEM_EFFECTS_TARGET 13 #define MOVEEND_MOVE_EFFECTS2 14 #define MOVEEND_ITEM_EFFECTS_ALL 15 -#define MOVEEND_KINGSROCK_SHELLBELL 16 +#define MOVEEND_KINGSROCK 16 // These item effects will occur each strike of a multi-hit move #define MOVEEND_SUBSTITUTE 17 #define MOVEEND_UPDATE_LAST_MOVES 18 #define MOVEEND_MIRROR_MOVE 19 -#define MOVEEND_NEXT_TARGET 20 -#define MOVEEND_LIFE_ORB 21 -#define MOVEEND_PICKPOCKET 22 -#define MOVEEND_DANCER 23 -#define MOVEEND_EMERGENCY_EXIT 24 -#define MOVEEND_CLEAR_BITS 25 -#define MOVEEND_COUNT 26 +#define MOVEEND_NEXT_TARGET 20 // Everything up until here is handled for each strike of a multi-hit move +#define MOVEEND_EJECT_BUTTON 21 +#define MOVEEND_RED_CARD 22 +#define MOVEEND_EJECT_PACK 23 +#define MOVEEND_LIFEORB_SHELLBELL 24 // Includes shell bell, throat spray, etc +#define MOVEEND_PICKPOCKET 25 +#define MOVEEND_DANCER 26 +#define MOVEEND_EMERGENCY_EXIT 27 +#define MOVEEND_CLEAR_BITS 28 +#define MOVEEND_COUNT 29 + +// switch cases +#define B_SWITCH_NORMAL 0 +#define B_SWITCH_HIT 1 // dragon tail, circle throw +#define B_SWITCH_RED_CARD 2 #endif // GUARD_CONSTANTS_BATTLE_SCRIPT_COMMANDS_H diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 0558e4feb..ff5eb5366 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -573,8 +573,15 @@ #define STRINGID_ITEMCANNOTBEREMOVED 569 #define STRINGID_STICKYBARBTRANSFER 570 #define STRINGID_PKMNBURNHEALED 571 +#define STRINGID_REDCARDACTIVATE 572 +#define STRINGID_EJECTBUTTONACTIVATE 573 +#define STRINGID_ATKGOTOVERINFATUATION 574 +#define STRINGID_TORMENTEDNOMORE 575 +#define STRINGID_HEALBLOCKEDNOMORE 576 +#define STRINGID_ATTACKERBECAMEFULLYCHARGED 577 +#define STRINGID_ATTACKERBECAMEASHSPECIES 578 -#define BATTLESTRINGS_COUNT 572 +#define BATTLESTRINGS_COUNT 579 // The below IDs are all indexes into battle message tables, // used to determine which of a set of messages to print. @@ -810,4 +817,17 @@ #define B_MSG_SWITCHIN_ASONE 13 #define B_MSG_SWITCHIN_CURIOUS_MEDICINE 14 +// gMentalHerbCureStringIds +#define B_MSG_MENTALHERBCURE_INFATUATION 0 +#define B_MSG_MENTALHERBCURE_TAUNT 1 +#define B_MSG_MENTALHERBCURE_ENCORE 2 +#define B_MSG_MENTALHERBCURE_TORMENT 3 +#define B_MSG_MENTALHERBCURE_HEALBLOCK 4 +#define B_MSG_MENTALHERBCURE_DISABLE 5 + +// gTerrainPreventsStringIds +#define B_MSG_TERRAINPREVENTS_MISTY 0 +#define B_MSG_TERRAINPREVENTS_ELECTRIC 1 +#define B_MSG_TERRAINPREVENTS_PSYCHIC 2 + #endif // GUARD_CONSTANTS_BATTLE_STRING_IDS_H diff --git a/include/constants/hold_effects.h b/include/constants/hold_effects.h index 13e886944..3dc4a1389 100644 --- a/include/constants/hold_effects.h +++ b/include/constants/hold_effects.h @@ -29,7 +29,7 @@ #define HOLD_EFFECT_EXP_SHARE 25 #define HOLD_EFFECT_QUICK_CLAW 26 #define HOLD_EFFECT_FRIENDSHIP_UP 27 -#define HOLD_EFFECT_CURE_ATTRACT 28 +#define HOLD_EFFECT_MENTAL_HERB 28 #define HOLD_EFFECT_CHOICE_BAND 29 #define HOLD_EFFECT_FLINCH 30 #define HOLD_EFFECT_BUG_POWER 31 @@ -154,4 +154,10 @@ #define HOLD_EFFECT_CHOICE(holdEffect)((holdEffect == HOLD_EFFECT_CHOICE_BAND || holdEffect == HOLD_EFFECT_CHOICE_SCARF || holdEffect == HOLD_EFFECT_CHOICE_SPECS)) +// Terrain seed params +#define HOLD_EFFECT_PARAM_ELECTRIC_TERRAIN 0 +#define HOLD_EFFECT_PARAM_GRASSY_TERRAIN 1 +#define HOLD_EFFECT_PARAM_MISTY_TERRAIN 2 +#define HOLD_EFFECT_PARAM_PSYCHIC_TERRAIN 3 + #endif // GUARD_HOLD_EFFECTS_H diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 07f5e68fc..42b8b9d40 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -520,6 +520,9 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) u32 i; u16 predictedMove = gLastMoves[battlerDef]; // TODO better move prediction + SetTypeBeforeUsingMove(move, battlerAtk); + GET_MOVE_TYPE(move, moveType); + if (IsTargetingPartner(battlerAtk, battlerDef)) return score; @@ -1204,7 +1207,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_DISABLE: if (gDisableStructs[battlerDef].disableTimer == 0 - && (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect != HOLD_EFFECT_CURE_ATTRACT) + && (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect != HOLD_EFFECT_MENTAL_HERB) && !PartnerHasSameMoveEffectWithoutTarget(AI_DATA->battlerAtkPartner, move, AI_DATA->partnerMove)) { if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // attacker should go first @@ -1224,7 +1227,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_ENCORE: if (gDisableStructs[battlerDef].encoreTimer == 0 - && (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect != HOLD_EFFECT_CURE_ATTRACT) + && (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect != HOLD_EFFECT_MENTAL_HERB) && !DoesPartnerHaveSameMoveEffect(AI_DATA->battlerAtkPartner, battlerDef, move, AI_DATA->partnerMove)) { if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // attacker should go first @@ -1458,7 +1461,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; } - if (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect == HOLD_EFFECT_CURE_ATTRACT) + if (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect == HOLD_EFFECT_MENTAL_HERB) score -= 6; break; case EFFECT_WILL_O_WISP: @@ -1570,7 +1573,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score -= 10; break; case EFFECT_REST: - if (!CanSleep(battlerAtk, AI_DATA->atkAbility)) + if (!AI_CanSleep(battlerAtk, AI_DATA->atkAbility)) score -= 10; //fallthrough case EFFECT_RESTORE_HP: @@ -2472,7 +2475,10 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) bool32 attackerHasBadAbility = (GetAbilityRating(AI_DATA->atkAbility) < 0); bool32 partnerHasBadAbility = (GetAbilityRating(atkPartnerAbility) < 0); u16 predictedMove = gLastMoves[battlerDef]; //for now - + + SetTypeBeforeUsingMove(move, battlerAtk); + GET_MOVE_TYPE(move, moveType); + // check what effect partner is using if (AI_DATA->partnerMove != 0) { @@ -2686,7 +2692,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_SWAGGER: if (gBattleMons[battlerAtkPartner].statStages[STAT_ATK] < MAX_STAT_STAGE && HasMoveWithSplit(battlerAtkPartner, SPLIT_PHYSICAL) - && (!CanBeConfused(battlerAtkPartner, TRUE) + && (!AI_CanBeConfused(battlerAtkPartner, TRUE) || atkPartnerHoldEffect == HOLD_EFFECT_CURE_CONFUSION || atkPartnerHoldEffect == HOLD_EFFECT_CURE_STATUS)) { @@ -2696,7 +2702,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_FLATTER: if (gBattleMons[battlerAtkPartner].statStages[STAT_SPATK] < MAX_STAT_STAGE && HasMoveWithSplit(battlerAtkPartner, SPLIT_SPECIAL) - && (!CanBeConfused(battlerAtkPartner, TRUE) + && (!AI_CanBeConfused(battlerAtkPartner, TRUE) || atkPartnerHoldEffect == HOLD_EFFECT_CURE_CONFUSION || atkPartnerHoldEffect == HOLD_EFFECT_CURE_STATUS)) { @@ -3248,7 +3254,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case EFFECT_REST: - if (!(CanSleep(battlerAtk, AI_DATA->atkAbility))) + if (!(AI_CanSleep(battlerAtk, AI_DATA->atkAbility))) { break; } @@ -3408,7 +3414,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_DISABLE: if (gDisableStructs[battlerDef].disableTimer == 0 - && (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect != HOLD_EFFECT_CURE_ATTRACT)) // mental herb + && (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect != HOLD_EFFECT_MENTAL_HERB)) // mental herb { if (GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0) // AI goes first { @@ -3430,7 +3436,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) break; case EFFECT_ENCORE: if (gDisableStructs[battlerDef].encoreTimer == 0 - && (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect != HOLD_EFFECT_CURE_ATTRACT)) // mental herb + && (B_MENTAL_HERB >= GEN_5 && AI_DATA->defHoldEffect != HOLD_EFFECT_MENTAL_HERB)) // mental herb { if (IsEncoreEncouragedEffect(gBattleMoves[gLastMoves[battlerDef]].effect)) score += 3; @@ -3937,11 +3943,11 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score += 2; break; case HOLD_EFFECT_TOXIC_ORB: - if (!ShouldPoisonSelf(battlerAtk, AI_DATA->atkAbility) && CanBePoisoned(battlerDef, AI_DATA->defAbility)) + if (!ShouldPoisonSelf(battlerAtk, AI_DATA->atkAbility) && AI_CanBePoisoned(battlerDef, AI_DATA->defAbility)) score += 2; break; case HOLD_EFFECT_FLAME_ORB: - if (!ShouldBurnSelf(battlerAtk, AI_DATA->atkAbility) && CanBeBurned(battlerAtk, AI_DATA->defAbility)) + if (!ShouldBurnSelf(battlerAtk, AI_DATA->atkAbility) && AI_CanBeBurned(battlerAtk, AI_DATA->defAbility)) score += 2; break; case HOLD_EFFECT_BLACK_SLUDGE: @@ -4751,7 +4757,10 @@ static s16 AI_HPAware(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) { u16 effect = gBattleMoves[move].effect; u8 moveType = gBattleMoves[move].type; - + + SetTypeBeforeUsingMove(move, battlerAtk); + GET_MOVE_TYPE(move, moveType); + if (IsTargetingPartner(battlerAtk, battlerDef)) { if ((effect == EFFECT_HEAL_PULSE || effect == EFFECT_HIT_ENEMY_HEAL_ALLY) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 3fac2ac10..95d979e94 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -711,7 +711,8 @@ static bool32 AI_GetIfCrit(u32 move, u8 battlerAtk, u8 battlerDef) s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef) { - s32 dmg, moveType; + s32 dmg, moveType, critDmg, normalDmg; + s8 critChance; SaveBattlerData(battlerAtk); SaveBattlerData(battlerDef); @@ -722,12 +723,22 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef) gBattleStruct->dynamicMoveType = 0; SetTypeBeforeUsingMove(move, battlerAtk); GET_MOVE_TYPE(move, moveType); - dmg = CalculateMoveDamage(move, battlerAtk, battlerDef, moveType, 0, AI_GetIfCrit(move, battlerAtk, battlerDef), FALSE, FALSE); + + critChance = GetInverseCritChance(battlerAtk, battlerDef, move); + normalDmg = CalculateMoveDamage(move, battlerAtk, battlerDef, moveType, 0, FALSE, FALSE, FALSE); + critDmg = CalculateMoveDamage(move, battlerAtk, battlerDef, moveType, 0, TRUE, FALSE, FALSE); + + if(critChance == -1) + dmg = normalDmg; + else + dmg = (critDmg + normalDmg * (critChance - 1)) / critChance; // handle dynamic move damage switch (gBattleMoves[move].effect) { case EFFECT_LEVEL_DAMAGE: + case EFFECT_PSYWAVE: + //psywave's expected damage is equal to the user's level dmg = gBattleMons[battlerAtk].level; break; case EFFECT_DRAGON_RAGE: @@ -736,20 +747,11 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef) case EFFECT_SONICBOOM: dmg = 20; break; - case EFFECT_PSYWAVE: - { - u32 randDamage; - if (B_PSYWAVE_DMG >= GEN_6) - randDamage = (Random() % 101); - else - randDamage = (Random() % 11) * 10; - dmg = gBattleMons[battlerAtk].level * (randDamage + 50) / 100; - } - break; //case EFFECT_METAL_BURST: //case EFFECT_COUNTER: default: - dmg *= (100 - (Random() % 10)) / 100; // add random factor + //do not add the random factor, it's an average case analysis + //dmg *= (100 - (Random() % 10)) / 100; // add random factor break; } @@ -2589,7 +2591,7 @@ bool32 IsBattlerIncapacitated(u8 battler, u16 ability) return FALSE; } -bool32 CanSleep(u8 battler, u16 ability) +bool32 AI_CanSleep(u8 battler, u16 ability) { if (ability == ABILITY_INSOMNIA || ability == ABILITY_VITAL_SPIRIT @@ -2603,7 +2605,7 @@ bool32 CanSleep(u8 battler, u16 ability) bool32 AI_CanPutToSleep(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u16 partnerMove) { - if (!CanSleep(battlerDef, defAbility) + if (!AI_CanSleep(battlerDef, defAbility) || AI_GetMoveEffectiveness(move, battlerAtk, battlerDef) == AI_EFFECTIVENESS_x0 || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) || PartnerMoveEffectIsStatusSameTarget(BATTLE_PARTNER(battlerAtk), battlerDef, partnerMove)) // shouldn't try to sleep mon that partner is trying to make sleep @@ -2611,7 +2613,7 @@ bool32 AI_CanPutToSleep(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, return TRUE; } -bool32 CanBePoisoned(u8 battler, u16 ability) +bool32 AI_CanBePoisoned(u8 battler, u16 ability) { if (ability == ABILITY_IMMUNITY || ability == ABILITY_PASTEL_VEIL @@ -2624,7 +2626,7 @@ bool32 CanBePoisoned(u8 battler, u16 ability) bool32 ShouldPoisonSelf(u8 battler, u16 ability) { - if (CanBePoisoned(battler, ability) && ( + if (AI_CanBePoisoned(battler, ability) && ( ability == ABILITY_MARVEL_SCALE || ability == ABILITY_POISON_HEAL || ability == ABILITY_QUICK_FEET @@ -2639,7 +2641,7 @@ bool32 ShouldPoisonSelf(u8 battler, u16 ability) bool32 AI_CanPoison(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u16 partnerMove) { - if (!CanBePoisoned(battlerDef, defAbility) + if (!AI_CanBePoisoned(battlerDef, defAbility) || AI_GetMoveEffectiveness(move, battlerAtk, battlerDef) == AI_EFFECTIVENESS_x0 || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) || PartnerMoveEffectIsStatusSameTarget(BATTLE_PARTNER(battlerAtk), battlerDef, partnerMove)) @@ -2673,7 +2675,7 @@ bool32 AI_CanParalyze(u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 move, u1 return TRUE; } -bool32 CanBeConfused(u8 battler, u16 ability) +bool32 AI_CanBeConfused(u8 battler, u16 ability) { if ((gBattleMons[battler].status2 & STATUS2_CONFUSION) || (ability == ABILITY_OWN_TEMPO) @@ -2684,7 +2686,7 @@ bool32 CanBeConfused(u8 battler, u16 ability) bool32 AI_CanConfuse(u8 battlerAtk, u8 battlerDef, u16 defAbility, u8 battlerAtkPartner, u16 move, u16 partnerMove) { - if (!CanBeConfused(battlerDef, defAbility) + if (!AI_CanBeConfused(battlerDef, defAbility) || AI_GetMoveEffectiveness(move, battlerAtk, battlerDef) == AI_EFFECTIVENESS_x0 || gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) @@ -2696,7 +2698,7 @@ bool32 AI_CanConfuse(u8 battlerAtk, u8 battlerDef, u16 defAbility, u8 battlerAtk return TRUE; } -bool32 CanBeBurned(u8 battler, u16 ability) +bool32 AI_CanBeBurned(u8 battler, u16 ability) { if (ability == ABILITY_WATER_VEIL || ability == ABILITY_WATER_BUBBLE @@ -2710,7 +2712,7 @@ bool32 CanBeBurned(u8 battler, u16 ability) bool32 ShouldBurnSelf(u8 battler, u16 ability) { - if (CanBeBurned(battler, ability) && ( + if (AI_CanBeBurned(battler, ability) && ( ability == ABILITY_QUICK_FEET || ability == ABILITY_HEATPROOF || ability == ABILITY_MAGIC_GUARD @@ -2724,7 +2726,7 @@ bool32 ShouldBurnSelf(u8 battler, u16 ability) bool32 AI_CanBurn(u8 battlerAtk, u8 battlerDef, u16 defAbility, u8 battlerAtkPartner, u16 move, u16 partnerMove) { - if (!CanBeBurned(battlerDef, defAbility) + if (!AI_CanBeBurned(battlerDef, defAbility) || AI_GetMoveEffectiveness(move, battlerAtk, battlerDef) == AI_EFFECTIVENESS_x0 || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) || PartnerMoveEffectIsStatusSameTarget(battlerAtkPartner, battlerDef, partnerMove)) diff --git a/src/battle_anim.c b/src/battle_anim.c index ef896c93f..d1a69bcde 100644 --- a/src/battle_anim.c +++ b/src/battle_anim.c @@ -2213,6 +2213,7 @@ void LaunchBattleAnimation(const u8 *const animsTable[], u16 tableId, bool8 isMo case B_ANIM_DOOM_DESIRE_HIT: case B_ANIM_WISH_HEAL: case B_ANIM_MEGA_EVOLUTION: + case B_ANIM_GULP_MISSILE: hideHpBoxes = TRUE; break; default: @@ -3227,7 +3228,7 @@ static void LoadDefaultBg(void) if (IsContest()) LoadContestBgAfterMoveAnim(); #if B_TERRAIN_BG_CHANGE == TRUE - else if (gFieldStatuses & STATUS_TERRAIN_ANY) + else if (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY) DrawTerrainTypeBattleBackground(); #endif else diff --git a/src/battle_bg.c b/src/battle_bg.c index aaa1c60f9..8ef2fd4ca 100644 --- a/src/battle_bg.c +++ b/src/battle_bg.c @@ -1422,7 +1422,7 @@ bool8 LoadChosenBattleElement(u8 caseId) void DrawTerrainTypeBattleBackground(void) { - switch (gFieldStatuses & STATUS_TERRAIN_ANY) + switch (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY) { case STATUS_FIELD_GRASSY_TERRAIN: LoadMoveBg(BG_GRASSY_TERRAIN); diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 2698956d6..b43e80225 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -225,12 +225,18 @@ static void Intro_DelayAndEnd(void) } } +static bool32 TwoIntroMons(u32 battlerId) // Double battle with both player pokemon active. +{ + return (IsDoubleBattle() && IsValidForBattle(&gEnemyParty[gBattlerPartyIndexes[battlerId ^ BIT_FLANK]])); +} + static void Intro_WaitForShinyAnimAndHealthbox(void) { bool8 healthboxAnimDone = FALSE; bool8 twoMons; - if (!IsDoubleBattle() || ((IsDoubleBattle() && (gBattleTypeFlags & BATTLE_TYPE_MULTI) && !BATTLE_TWO_VS_ONE_OPPONENT) || (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS))) + twoMons = TwoIntroMons(gActiveBattler); + if (!twoMons || ((twoMons && (gBattleTypeFlags & BATTLE_TYPE_MULTI) && !BATTLE_TWO_VS_ONE_OPPONENT) || (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS))) { if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) healthboxAnimDone = TRUE; @@ -292,15 +298,17 @@ static void Intro_TryShinyAnimShowHealthbox(void) { bool32 bgmRestored = FALSE; bool32 battlerAnimsDone = FALSE; + bool32 twoMons; if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].triedShinyMonAnim && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].finishedShinyMonAnim) TryShinyAnimation(gActiveBattler, &gEnemyParty[gBattlerPartyIndexes[gActiveBattler]]); + twoMons = TwoIntroMons(gActiveBattler); if (!(gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) || BATTLE_TWO_VS_ONE_OPPONENT) - && IsDoubleBattle() + && twoMons && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].triedShinyMonAnim && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].ballAnimActive && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].finishedShinyMonAnim) @@ -310,7 +318,7 @@ static void Intro_TryShinyAnimShowHealthbox(void) { if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].healthboxSlideInStarted) { - if (IsDoubleBattle() && (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) || BATTLE_TWO_VS_ONE_OPPONENT)) + if (twoMons && (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) || BATTLE_TWO_VS_ONE_OPPONENT)) { UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler ^ BIT_FLANK], &gEnemyParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]], HEALTHBOX_ALL); StartHealthboxSlideIn(gActiveBattler ^ BIT_FLANK); @@ -342,7 +350,7 @@ static void Intro_TryShinyAnimShowHealthbox(void) bgmRestored = TRUE; } - if (!IsDoubleBattle() || (IsDoubleBattle() && gBattleTypeFlags & BATTLE_TYPE_MULTI && !BATTLE_TWO_VS_ONE_OPPONENT)) + if (!twoMons || (twoMons && gBattleTypeFlags & BATTLE_TYPE_MULTI && !BATTLE_TWO_VS_ONE_OPPONENT)) { if (gSprites[gBattleControllerData[gActiveBattler]].callback == SpriteCallbackDummy && gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) @@ -363,7 +371,7 @@ static void Intro_TryShinyAnimShowHealthbox(void) if (bgmRestored && battlerAnimsDone) { - if (IsDoubleBattle() && (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) || BATTLE_TWO_VS_ONE_OPPONENT)) + if (twoMons && (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) || BATTLE_TWO_VS_ONE_OPPONENT)) { DestroySprite(&gSprites[gBattleControllerData[gActiveBattler ^ BIT_FLANK]]); SetBattlerShadowSpriteCallback(gActiveBattler ^ BIT_FLANK, GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]], MON_DATA_SPECIES)); @@ -469,7 +477,7 @@ static void SwitchIn_HandleSoundAndEnd(void) { if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive && !IsCryPlayingOrClearCrySongs()) { - if (gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy + if (gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy || gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy_2) { m4aMPlayVolumeControl(&gMPlayInfo_BGM, 0xFFFF, 0x100); @@ -1892,7 +1900,7 @@ static void Task_StartSendOutAnim(u8 taskId) u8 savedActiveBank = gActiveBattler; gActiveBattler = gTasks[taskId].data[0]; - if ((!IsDoubleBattle() || (gBattleTypeFlags & BATTLE_TYPE_MULTI)) && !BATTLE_TWO_VS_ONE_OPPONENT) + if ((!TwoIntroMons(gActiveBattler) || (gBattleTypeFlags & BATTLE_TYPE_MULTI)) && !BATTLE_TWO_VS_ONE_OPPONENT) { gBattleResources->bufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler]; StartSendOutAnim(gActiveBattler, FALSE); diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index 70a8f51e9..fa53b222f 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -1090,20 +1090,25 @@ static void Intro_DelayAndEnd(void) } } +static bool32 TwoIntroMons(u32 battlerId) // Double battle with both player pokemon active. +{ + return (IsDoubleBattle() && IsValidForBattle(&gPlayerParty[gBattlerPartyIndexes[battlerId ^ BIT_FLANK]])); +} + static void Intro_WaitForShinyAnimAndHealthbox(void) { bool8 healthboxAnimDone = FALSE; // Check if healthbox has finished sliding in - if (!IsDoubleBattle() || (IsDoubleBattle() && (gBattleTypeFlags & BATTLE_TYPE_MULTI))) + if (TwoIntroMons(gActiveBattler) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) { - if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy + && gSprites[gHealthboxSpriteIds[gActiveBattler ^ BIT_FLANK]].callback == SpriteCallbackDummy) healthboxAnimDone = TRUE; } else { - if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy - && gSprites[gHealthboxSpriteIds[gActiveBattler ^ BIT_FLANK]].callback == SpriteCallbackDummy) + if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) healthboxAnimDone = TRUE; } @@ -1121,7 +1126,7 @@ static void Intro_WaitForShinyAnimAndHealthbox(void) HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); - if (IsDoubleBattle()) + if (TwoIntroMons(gActiveBattler)) HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]], gActiveBattler ^ BIT_FLANK); gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].introEndDelay = 3; @@ -1150,7 +1155,7 @@ static void Intro_TryShinyAnimShowHealthbox(void) { if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].healthboxSlideInStarted) { - if (IsDoubleBattle() && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + if (TwoIntroMons(gActiveBattler) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) { UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler ^ BIT_FLANK], &gPlayerParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]], HEALTHBOX_ALL); StartHealthboxSlideIn(gActiveBattler ^ BIT_FLANK); @@ -1181,15 +1186,7 @@ static void Intro_TryShinyAnimShowHealthbox(void) } // Wait for battler anims - if (!IsDoubleBattle() || (IsDoubleBattle() && (gBattleTypeFlags & BATTLE_TYPE_MULTI))) - { - if (gSprites[gBattleControllerData[gActiveBattler]].callback == SpriteCallbackDummy - && gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) - { - battlerAnimsDone = TRUE; - } - } - else + if (TwoIntroMons(gActiveBattler) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) { if (gSprites[gBattleControllerData[gActiveBattler]].callback == SpriteCallbackDummy && gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy @@ -1199,11 +1196,19 @@ static void Intro_TryShinyAnimShowHealthbox(void) battlerAnimsDone = TRUE; } } + else + { + if (gSprites[gBattleControllerData[gActiveBattler]].callback == SpriteCallbackDummy + && gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + { + battlerAnimsDone = TRUE; + } + } // Clean up if (bgmRestored && battlerAnimsDone) { - if (IsDoubleBattle() && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + if (TwoIntroMons(gActiveBattler) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) DestroySprite(&gSprites[gBattleControllerData[gActiveBattler ^ BIT_FLANK]]); DestroySprite(&gSprites[gBattleControllerData[gActiveBattler]]); @@ -3170,12 +3175,7 @@ static void Task_StartSendOutAnim(u8 taskId) u8 savedActiveBattler = gActiveBattler; gActiveBattler = gTasks[taskId].tBattlerId; - if (!IsDoubleBattle() || (gBattleTypeFlags & BATTLE_TYPE_MULTI)) - { - gBattleResources->bufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler]; - StartSendOutAnim(gActiveBattler, FALSE); - } - else + if (TwoIntroMons(gActiveBattler) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) { gBattleResources->bufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler]; StartSendOutAnim(gActiveBattler, FALSE); @@ -3185,6 +3185,11 @@ static void Task_StartSendOutAnim(u8 taskId) StartSendOutAnim(gActiveBattler, FALSE); gActiveBattler ^= BIT_FLANK; } + else + { + gBattleResources->bufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler]; + StartSendOutAnim(gActiveBattler, FALSE); + } gBattlerControllerFuncs[gActiveBattler] = Intro_TryShinyAnimShowHealthbox; gActiveBattler = savedActiveBattler; DestroyTask(taskId); diff --git a/src/battle_controllers.c b/src/battle_controllers.c index 2fc7cac07..033a12122 100644 --- a/src/battle_controllers.c +++ b/src/battle_controllers.c @@ -588,6 +588,14 @@ static void InitLinkBtlControllers(void) } } +bool32 IsValidForBattle(struct Pokemon *mon) +{ + u32 species = GetMonData(mon, MON_DATA_SPECIES2); + return (species != SPECIES_NONE && species != SPECIES_EGG + && GetMonData(mon, MON_DATA_HP) != 0 + && GetMonData(mon, MON_DATA_IS_EGG) == 0); +} + static void SetBattlePartyIds(void) { s32 i, j; @@ -602,10 +610,7 @@ static void SetBattlePartyIds(void) { if (GET_BATTLER_SIDE2(i) == B_SIDE_PLAYER) { - if (GetMonData(&gPlayerParty[j], MON_DATA_HP) != 0 - && GetMonData(&gPlayerParty[j], MON_DATA_SPECIES2) != SPECIES_NONE - && GetMonData(&gPlayerParty[j], MON_DATA_SPECIES2) != SPECIES_EGG - && GetMonData(&gPlayerParty[j], MON_DATA_IS_EGG) == 0) + if (IsValidForBattle(&gPlayerParty[j])) { gBattlerPartyIndexes[i] = j; break; @@ -613,10 +618,7 @@ static void SetBattlePartyIds(void) } else { - if (GetMonData(&gEnemyParty[j], MON_DATA_HP) != 0 - && GetMonData(&gEnemyParty[j], MON_DATA_SPECIES2) != SPECIES_NONE - && GetMonData(&gEnemyParty[j], MON_DATA_SPECIES2) != SPECIES_EGG - && GetMonData(&gEnemyParty[j], MON_DATA_IS_EGG) == 0) + if (IsValidForBattle(&gEnemyParty[j])) { gBattlerPartyIndexes[i] = j; break; @@ -627,11 +629,7 @@ static void SetBattlePartyIds(void) { if (GET_BATTLER_SIDE2(i) == B_SIDE_PLAYER) { - if (GetMonData(&gPlayerParty[j], MON_DATA_HP) != 0 - && GetMonData(&gPlayerParty[j], MON_DATA_SPECIES) != SPECIES_NONE // Probably a typo by Game Freak. The rest use SPECIES2. - && GetMonData(&gPlayerParty[j], MON_DATA_SPECIES2) != SPECIES_EGG - && GetMonData(&gPlayerParty[j], MON_DATA_IS_EGG) == 0 - && gBattlerPartyIndexes[i - 2] != j) + if (IsValidForBattle(&gPlayerParty[j]) && gBattlerPartyIndexes[i - 2] != j) { gBattlerPartyIndexes[i] = j; break; @@ -639,16 +637,18 @@ static void SetBattlePartyIds(void) } else { - if (GetMonData(&gEnemyParty[j], MON_DATA_HP) != 0 - && GetMonData(&gEnemyParty[j], MON_DATA_SPECIES2) != SPECIES_NONE - && GetMonData(&gEnemyParty[j], MON_DATA_SPECIES2) != SPECIES_EGG - && GetMonData(&gEnemyParty[j], MON_DATA_IS_EGG) == 0 - && gBattlerPartyIndexes[i - 2] != j) + if (IsValidForBattle(&gEnemyParty[j]) && gBattlerPartyIndexes[i - 2] != j) { gBattlerPartyIndexes[i] = j; break; } } + + // No valid mons were found. Add the empty slot. + if (gBattlerPartyIndexes[i - 2] == 0) + gBattlerPartyIndexes[i] = 1; + else + gBattlerPartyIndexes[i] = 0; } } } diff --git a/src/battle_interface.c b/src/battle_interface.c index c4fcfe331..26d6fb27f 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -1037,6 +1037,7 @@ static void UpdateLvlInHealthbox(u8 healthboxSpriteId, u8 lvl) if (gBattleStruct->mega.evolvedPartyIds[GetBattlerSide(battler)] & gBitTable[gBattlerPartyIndexes[battler]]) { objVram = ConvertIntToDecimalStringN(text, lvl, STR_CONV_MODE_LEFT_ALIGN, 3); + xPos = 5 * (3 - (objVram - (text + 2))) - 1; } else { @@ -1044,6 +1045,7 @@ static void UpdateLvlInHealthbox(u8 healthboxSpriteId, u8 lvl) text[1] = CHAR_LV_2; objVram = ConvertIntToDecimalStringN(text + 2, lvl, STR_CONV_MODE_LEFT_ALIGN, 3); + xPos = 5 * (3 - (objVram - (text + 2))); } xPos = 5 * (3 - (objVram - (text + 2))); @@ -1491,18 +1493,12 @@ void DestroyMegaTriggerSprite(void) gBattleStruct->mega.triggerSpriteId = 0xFF; } -static const s8 sIndicatorPosSingles[][2] = +static const s8 sIndicatorPositions[][2] = { - [B_POSITION_PLAYER_LEFT] = {53, -8}, - [B_POSITION_OPPONENT_LEFT] = {45, -8}, -}; - -static const s8 sIndicatorPosDoubles[][2] = -{ - [B_POSITION_PLAYER_LEFT] = {53, -8}, - [B_POSITION_OPPONENT_LEFT] = {45, -8}, - [B_POSITION_PLAYER_RIGHT] = {53, -8}, - [B_POSITION_OPPONENT_RIGHT] = {45, -8}, + [B_POSITION_PLAYER_LEFT] = {52, -9}, + [B_POSITION_OPPONENT_LEFT] = {44, -9}, + [B_POSITION_PLAYER_RIGHT] = {52, -9}, + [B_POSITION_OPPONENT_RIGHT] = {44, -9}, }; u32 CreateMegaIndicatorSprite(u32 battlerId, u32 which) @@ -1515,16 +1511,15 @@ u32 CreateMegaIndicatorSprite(u32 battlerId, u32 which) position = GetBattlerPosition(battlerId); GetBattlerHealthboxCoords(battlerId, &x, &y); - if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) - { - x += sIndicatorPosDoubles[position][0]; - y += sIndicatorPosDoubles[position][1]; - } - else - { - x += sIndicatorPosSingles[position][0]; - y += sIndicatorPosSingles[position][1]; - } + + x += sIndicatorPositions[position][0]; + y += sIndicatorPositions[position][1]; + + if (gBattleMons[battlerId].level >= 100) + x -= 4; + else if (gBattleMons[battlerId].level < 10) + x += 5; + spriteId = CreateSpriteAtEnd(&sSpriteTemplate_MegaIndicator, x, y, 0); gSprites[gSprites[gHealthboxSpriteIds[battlerId]].oam.affineParam].hOther_IndicatorSpriteId = spriteId; diff --git a/src/battle_main.c b/src/battle_main.c index 77e71c5b7..62662d1e4 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3032,6 +3032,10 @@ void SwitchInClearSetData(void) gBattleResources->flags->flags[gActiveBattler] = 0; gCurrentMove = 0; gBattleStruct->arenaTurnCounter = 0xFF; + + // Reset damage to prevent things like red card activating if the switched-in mon is holding it + gSpecialStatuses[gActiveBattler].physicalDmg = 0; + gSpecialStatuses[gActiveBattler].specialDmg = 0; ClearBattlerMoveHistory(gActiveBattler); ClearBattlerAbilityHistory(gActiveBattler); @@ -3067,6 +3071,7 @@ void FaintClearSetData(void) gProtectStructs[gActiveBattler].spikyShielded = 0; gProtectStructs[gActiveBattler].kingsShielded = 0; gProtectStructs[gActiveBattler].banefulBunkered = 0; + gProtectStructs[gActiveBattler].obstructed = 0; gProtectStructs[gActiveBattler].endured = 0; gProtectStructs[gActiveBattler].noValidMoves = 0; gProtectStructs[gActiveBattler].helpingHand = 0; @@ -3074,7 +3079,7 @@ void FaintClearSetData(void) gProtectStructs[gActiveBattler].stealMove = 0; gProtectStructs[gActiveBattler].prlzImmobility = 0; gProtectStructs[gActiveBattler].confusionSelfDmg = 0; - gProtectStructs[gActiveBattler].targetNotAffected = 0; + gProtectStructs[gActiveBattler].targetAffected = 0; gProtectStructs[gActiveBattler].chargingTurn = 0; gProtectStructs[gActiveBattler].fleeFlag = 0; gProtectStructs[gActiveBattler].usedImprisonedMove = 0; @@ -3088,6 +3093,8 @@ void FaintClearSetData(void) gProtectStructs[gActiveBattler].usesBouncedMove = 0; gProtectStructs[gActiveBattler].usedGravityPreventedMove = 0; gProtectStructs[gActiveBattler].usedThroatChopPreventedMove = 0; + gProtectStructs[gActiveBattler].statRaised = 0; + gProtectStructs[gActiveBattler].statFell = 0; gDisableStructs[gActiveBattler].isFirstTurn = 2; @@ -3457,6 +3464,16 @@ static void TryDoEventsBeforeFirstTurn(void) if (gBattleControllerExecFlags) return; + // Set invalid mons as absent(for example when starting a double battle with only one pokemon). + if (!(gBattleTypeFlags & BATTLE_TYPE_SAFARI)) + { + for (i = 0; i < gBattlersCount; i++) + { + if (gBattleMons[i].hp == 0 || gBattleMons[i].species == SPECIES_NONE) + gAbsentBattlerFlags |= gBitTable[i]; + } + } + if (gBattleStruct->switchInAbilitiesCounter == 0) { for (i = 0; i < gBattlersCount; i++) @@ -3668,7 +3685,7 @@ u8 IsRunningFromBattleImpossible(void) return 0; if (gBattleTypeFlags & BATTLE_TYPE_LINK) return 0; - if (gBattleMons[gActiveBattler].ability == ABILITY_RUN_AWAY) + if (GetBattlerAbility(gActiveBattler) == ABILITY_RUN_AWAY) return 0; if ((i = IsAbilityPreventingEscape(gActiveBattler))) @@ -4083,10 +4100,10 @@ static void HandleTurnActionSelectionState(void) } break; case STATE_WAIT_ACTION_CONFIRMED_STANDBY: - if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) + if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) | (0xF << 28) - | (gBitTable[gActiveBattler] << 4) - | (gBitTable[gActiveBattler] << 8) + | (gBitTable[gActiveBattler] << 4) + | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 12)))) { if (AllAtActionConfirmed()) @@ -4289,6 +4306,7 @@ s8 GetChosenMovePriority(u32 battlerId) { u16 move; + gProtectStructs[battlerId].pranksterElevated = 0; if (gProtectStructs[battlerId].noValidMoves) move = MOVE_STRUGGLE; else @@ -4309,6 +4327,11 @@ s8 GetMovePriority(u32 battlerId, u16 move) priority++; } else if (GetBattlerAbility(battlerId) == ABILITY_PRANKSTER && IS_MOVE_STATUS(move)) + { + gProtectStructs[battlerId].pranksterElevated = 1; + priority++; + } + else if (gBattleMoves[move].effect == EFFECT_GRASSY_GLIDE && gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerGrounded(battlerId)) { priority++; } @@ -4649,7 +4672,7 @@ static void CheckQuickClaw_CustapBerryActivation(void) } } } - + // setup stuff before turns/actions TryClearRageAndFuryCutter(); gCurrentTurnActionNumber = 0; @@ -4886,6 +4909,7 @@ static void HandleEndTurn_FinishBattle(void) { UndoMegaEvolution(i); UndoFormChange(i, B_SIDE_PLAYER, FALSE); + DoBurmyFormChange(i); } gBattleMainFunc = FreeResetData_ReturnToOvOrDoEvolutions; gCB2_AfterEvolution = BattleMainCB2; diff --git a/src/battle_message.c b/src/battle_message.c index 19468f168..69829a731 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -699,9 +699,23 @@ static const u8 sText_PkmnGotOverItsInfatuation[] = _("{B_SCR_ACTIVE_NAME_WITH_P static const u8 sText_ItemCannotBeRemoved[] = _("{B_ATK_NAME_WITH_PREFIX}'s item cannot be removed!"); static const u8 sText_StickyBarbTransfer[] = _("The {B_LAST_ITEM} attached itself to\n{B_ATK_NAME_WITH_PREFIX}!"); static const u8 sText_PkmnBurnHealed[] = _("{B_DEF_NAME_WITH_PREFIX}'s\nburn was healed."); +static const u8 sText_RedCardActivate[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} held up its {B_LAST_ITEM}\nagainst {B_ATK_NAME_WITH_PREFIX}!"); +static const u8 sText_EjectButtonActivate[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} is switched\nout with the {B_LAST_ITEM}!"); +static const u8 sText_AttackerGotOverInfatuation[] =_("{B_ATK_NAME_WITH_PREFIX} got over\nits infatuation!"); +static const u8 sText_TormentedNoMore[] = _("{B_ATK_NAME_WITH_PREFIX} is\ntormented no more!"); +static const u8 sText_HealBlockedNoMore[] = _("{B_ATK_NAME_WITH_PREFIX} is cured of\nits heal block!"); +static const u8 sText_AttackerBecameFullyCharged[] = _("{B_ATK_NAME_WITH_PREFIX} became fully charged\ndue to its bond with its trainer!\p"); +static const u8 sText_AttackerBecameAshSpecies[] = _("{B_ATK_NAME_WITH_PREFIX} became Ash-{B_BUFF1}!\p"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { + [STRINGID_ATTACKERBECAMEASHSPECIES - 12] = sText_AttackerBecameAshSpecies, + [STRINGID_ATTACKERBECAMEFULLYCHARGED - 12] = sText_AttackerBecameFullyCharged, + [STRINGID_HEALBLOCKEDNOMORE - 12] = sText_HealBlockedNoMore, + [STRINGID_TORMENTEDNOMORE - 12] = sText_TormentedNoMore, + [STRINGID_ATKGOTOVERINFATUATION - 12] = sText_AttackerGotOverInfatuation, + [STRINGID_EJECTBUTTONACTIVATE - 12] = sText_EjectButtonActivate, + [STRINGID_REDCARDACTIVATE - 12] = sText_RedCardActivate, [STRINGID_PKMNBURNHEALED - 12] = sText_PkmnBurnHealed, [STRINGID_STICKYBARBTRANSFER - 12] = sText_StickyBarbTransfer, [STRINGID_ITEMCANNOTBEREMOVED - 12] = sText_ItemCannotBeRemoved, @@ -1264,14 +1278,31 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_SCREENCLEANERENTERS - 12] = sText_ScreenCleanerActivates, }; +const u16 gMentalHerbCureStringIds[] = +{ + [B_MSG_MENTALHERBCURE_INFATUATION] = STRINGID_ATKGOTOVERINFATUATION, + [B_MSG_MENTALHERBCURE_TAUNT] = STRINGID_BUFFERENDS, + [B_MSG_MENTALHERBCURE_ENCORE] = STRINGID_PKMNENCOREENDED, + [B_MSG_MENTALHERBCURE_TORMENT] = STRINGID_TORMENTEDNOMORE, + [B_MSG_MENTALHERBCURE_HEALBLOCK] = STRINGID_HEALBLOCKEDNOMORE, + [B_MSG_MENTALHERBCURE_DISABLE] = STRINGID_PKMNMOVEDISABLEDNOMORE, +}; + const u16 gTerrainStringIds[] = { STRINGID_TERRAINBECOMESMISTY, STRINGID_TERRAINBECOMESGRASSY, STRINGID_TERRAINBECOMESELECTRIC, STRINGID_TERRAINBECOMESPSYCHIC }; +const u16 gTerrainEndingStringIds[] = +{ + STRINGID_MISTYTERRAINENDS, STRINGID_GRASSYTERRAINENDS, STRINGID_ELECTRICTERRAINENDS, STRINGID_PSYCHICTERRAINENDS +}; + const u16 gTerrainPreventsStringIds[] = { - STRINGID_MISTYTERRAINPREVENTS, STRINGID_ELECTRICTERRAINPREVENTS, STRINGID_PSYCHICTERRAINPREVENTS + [B_MSG_TERRAINPREVENTS_MISTY] = STRINGID_MISTYTERRAINPREVENTS, + [B_MSG_TERRAINPREVENTS_ELECTRIC] = STRINGID_ELECTRICTERRAINPREVENTS, + [B_MSG_TERRAINPREVENTS_PSYCHIC] = STRINGID_PSYCHICTERRAINPREVENTS }; const u16 gMagicCoatBounceStringIds[] = @@ -2556,7 +2587,7 @@ void BufferStringBattle(u16 stringID) { if (gBattleTypeFlags & BATTLE_TYPE_LEGENDARY) stringPtr = sText_LegendaryPkmnAppeared; - else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) // interesting, looks like they had something planned for wild double battles + else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsValidForBattle(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]])) // interesting, looks like they had something planned for wild double battles stringPtr = sText_TwoWildPkmnAppeared; else if (gBattleTypeFlags & BATTLE_TYPE_WALLY_TUTORIAL) stringPtr = sText_WildPkmnAppearedPause; @@ -2567,7 +2598,7 @@ void BufferStringBattle(u16 stringID) case STRINGID_INTROSENDOUT: // poke first send-out if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) { - if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsValidForBattle(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]])) { if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) stringPtr = sText_InGamePartnerSentOutZGoN; @@ -2585,7 +2616,7 @@ void BufferStringBattle(u16 stringID) } else { - if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsValidForBattle(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]])) { if (BATTLE_TWO_VS_ONE_OPPONENT) stringPtr = sText_Trainer1SentOutTwoPkmn; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index a719db550..339e5bd99 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1237,6 +1237,15 @@ static const u8 sBattlePalaceNatureToFlavorTextId[NUM_NATURES] = bool32 IsBattlerProtected(u8 battlerId, u16 move) { + // Decorate bypasses protect and detect, but not crafty shield + if (move == MOVE_DECORATE) + { + if (gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_CRAFTY_SHIELD) + return TRUE; + else if (gProtectStructs[battlerId].protected) + return FALSE; + } + if (!(gBattleMoves[move].flags & FLAG_PROTECT_AFFECTED)) return FALSE; else if (gBattleMoves[move].effect == MOVE_EFFECT_FEINT) @@ -1248,6 +1257,8 @@ bool32 IsBattlerProtected(u8 battlerId, u16 move) return TRUE; else if (gProtectStructs[battlerId].banefulBunkered) return TRUE; + else if (gProtectStructs[battlerId].obstructed && !IS_MOVE_STATUS(move)) + return TRUE; else if (gProtectStructs[battlerId].spikyShielded) return TRUE; else if (gProtectStructs[battlerId].kingsShielded && gBattleMoves[move].power != 0) @@ -1256,10 +1267,10 @@ bool32 IsBattlerProtected(u8 battlerId, u16 move) && GetChosenMovePriority(gBattlerAttacker) > 0) return TRUE; else if (gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_CRAFTY_SHIELD - && gBattleMoves[move].power == 0) + && IS_MOVE_STATUS(move)) return TRUE; else if (gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_MAT_BLOCK - && gBattleMoves[move].power != 0) + && !IS_MOVE_STATUS(move)) return TRUE; else return FALSE; @@ -1405,8 +1416,17 @@ static void Cmd_attackcanceler(void) gProtectStructs[gBattlerTarget].bounceMove = 0; gProtectStructs[gBattlerTarget].usesBouncedMove = 1; gBattleCommunication[MULTISTRING_CHOOSER] = 0; - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_MagicCoatBounce; + if (BlocksPrankster(gCurrentMove, gBattlerTarget, gBattlerAttacker)) + { + // Opponent used a prankster'd magic coat -> reflected status move should fail against a dark-type attacker + gBattlerTarget = gBattlerAttacker; + gBattlescriptCurrInstr = BattleScript_MagicCoatBouncePrankster; + } + else + { + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_MagicCoatBounce; + } return; } else if (GetBattlerAbility(gBattlerTarget) == ABILITY_MAGIC_BOUNCE @@ -1681,6 +1701,9 @@ static void Cmd_accuracycheck(void) if ((Random() % 100 + 1) > GetTotalAccuracy(gBattlerAttacker, gBattlerTarget, move)) { gMoveResultFlags |= MOVE_RESULT_MISSED; + if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_BLUNDER_POLICY) + gBattleStruct->blunderPolicy = TRUE; // Only activates from missing through acc/evasion checks + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && (gBattleMoves[move].target == MOVE_TARGET_BOTH || gBattleMoves[move].target == MOVE_TARGET_FOES_AND_ALLY)) gBattleCommunication[MISS_TYPE] = B_MSG_AVOIDED_ATK; @@ -1797,7 +1820,8 @@ s32 CalcCritChanceStage(u8 battlerAtk, u8 battlerDef, u32 move, bool32 recordAbi } else if (gStatuses3[battlerAtk] & STATUS3_LASER_FOCUS || gBattleMoves[move].effect == EFFECT_ALWAYS_CRIT - || (abilityAtk == ABILITY_MERCILESS && gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY)) + || (abilityAtk == ABILITY_MERCILESS && gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY) + || move == MOVE_SURGING_STRIKES) { critChance = -2; } @@ -1819,6 +1843,15 @@ s32 CalcCritChanceStage(u8 battlerAtk, u8 battlerDef, u32 move, bool32 recordAbi return critChance; } +s8 GetInverseCritChance(u8 battlerAtk, u8 battlerDef, u32 move) +{ + s32 critChanceIndex = CalcCritChanceStage(battlerAtk, battlerDef, move, FALSE); + if(critChanceIndex < 0) + return -1; + else + return sCriticalHitChance[critChanceIndex]; +} + static void Cmd_critcalc(void) { s32 critChance = CalcCritChanceStage(gBattlerAttacker, gBattlerTarget, gCurrentMove, TRUE); @@ -1967,6 +2000,15 @@ static void Cmd_multihitresultmessage(void) } } gBattlescriptCurrInstr++; + + // Print berry reducing message after result message. + if (gSpecialStatuses[gBattlerTarget].berryReduced + && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) + { + gSpecialStatuses[gBattlerTarget].berryReduced = 0; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_PrintBerryReduceString; + } } static void Cmd_attackanimation(void) @@ -2499,8 +2541,7 @@ void SetMoveEffect(bool32 primary, u32 certain) { s32 i, byTwo, affectsUser = 0; bool32 statusChanged = FALSE; - bool32 noSunCanFreeze = TRUE; - + switch (gBattleScripting.moveEffect) // Set move effects which happen later on { case MOVE_EFFECT_KNOCK_OFF: @@ -2559,15 +2600,9 @@ void SetMoveEffect(bool32 primary, u32 certain) else gActiveBattler = gBattlersCount; - if (gBattleMons[gEffectBattler].status1) - break; if (gActiveBattler != gBattlersCount) break; - if (GetBattlerAbility(gEffectBattler) == ABILITY_VITAL_SPIRIT - || GetBattlerAbility(gEffectBattler) == ABILITY_INSOMNIA - || GetBattlerAbility(gEffectBattler) == ABILITY_COMATOSE - || IsAbilityOnSide(gEffectBattler, ABILITY_SWEET_VEIL) - || IsAbilityStatusProtected(gEffectBattler)) + if (!CanSleep(gEffectBattler)) break; CancelMultiTurnMoves(gEffectBattler); @@ -2606,18 +2641,17 @@ void SetMoveEffect(bool32 primary, u32 certain) } if (!CanPoisonType(gBattleScripting.battler, gEffectBattler)) break; - if (gBattleMons[gEffectBattler].status1) - break; - if (GetBattlerAbility(gEffectBattler) == ABILITY_IMMUNITY - || GetBattlerAbility(gEffectBattler) == ABILITY_COMATOSE - || IsAbilityStatusProtected(gEffectBattler)) + if (!CanBePoisoned(gEffectBattler)) break; statusChanged = TRUE; break; case STATUS1_BURN: + if (gCurrentMove == MOVE_BURNING_JEALOUSY && gProtectStructs[gEffectBattler].statRaised == 0) + break; + if ((GetBattlerAbility(gEffectBattler) == ABILITY_WATER_VEIL || GetBattlerAbility(gEffectBattler) == ABILITY_WATER_BUBBLE) - && (primary == TRUE || certain == MOVE_EFFECT_CERTAIN)) + && (primary == TRUE || certain == MOVE_EFFECT_CERTAIN)) { gLastUsedAbility = GetBattlerAbility(gEffectBattler); RecordAbilityBattle(gEffectBattler, GetBattlerAbility(gEffectBattler)); @@ -2645,30 +2679,14 @@ void SetMoveEffect(bool32 primary, u32 certain) gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUS_HAD_NO_EFFECT; RESET_RETURN } - if (IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_FIRE)) - break; - if (GetBattlerAbility(gEffectBattler) == ABILITY_WATER_VEIL - || GetBattlerAbility(gEffectBattler) == ABILITY_COMATOSE - || GetBattlerAbility(gEffectBattler) == ABILITY_WATER_BUBBLE - || IsAbilityStatusProtected(gEffectBattler)) - break; - if (gBattleMons[gEffectBattler].status1) + + if (!CanBeBurned(gEffectBattler)) break; statusChanged = TRUE; break; case STATUS1_FREEZE: - if (WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_SUN_ANY) - noSunCanFreeze = FALSE; - if (IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_ICE)) - break; - if (gBattleMons[gEffectBattler].status1) - break; - if (noSunCanFreeze == 0) - break; - if (GetBattlerAbility(gEffectBattler) == ABILITY_MAGMA_ARMOR - || GetBattlerAbility(gEffectBattler) == ABILITY_COMATOSE - || IsAbilityStatusProtected(gEffectBattler)) + if (!CanBeFrozen(gEffectBattler)) break; CancelMultiTurnMoves(gEffectBattler); @@ -2711,11 +2729,7 @@ void SetMoveEffect(bool32 primary, u32 certain) } if (!CanParalyzeType(gBattleScripting.battler, gEffectBattler)) break; - if (GetBattlerAbility(gEffectBattler) == ABILITY_LIMBER - || GetBattlerAbility(gEffectBattler) == ABILITY_COMATOSE - || IsAbilityStatusProtected(gEffectBattler)) - break; - if (gBattleMons[gEffectBattler].status1) + if (!CanBeParalyzed(gEffectBattler)) break; statusChanged = TRUE; @@ -2754,9 +2768,7 @@ void SetMoveEffect(bool32 primary, u32 certain) break; if (CanPoisonType(gBattleScripting.battler, gEffectBattler)) { - if (GetBattlerAbility(gEffectBattler) == ABILITY_IMMUNITY - || GetBattlerAbility(gEffectBattler) == ABILITY_COMATOSE - || IsAbilityStatusProtected(gEffectBattler)) + if (!CanBePoisoned(gEffectBattler)) break; // It's redundant, because at this point we know the status1 value is 0. @@ -2828,8 +2840,7 @@ void SetMoveEffect(bool32 primary, u32 certain) switch (gBattleScripting.moveEffect) { case MOVE_EFFECT_CONFUSION: - if (GetBattlerAbility(gEffectBattler) == ABILITY_OWN_TEMPO - || gBattleMons[gEffectBattler].status2 & STATUS2_CONFUSION) + if (!CanBeConfused(gEffectBattler)) { gBattlescriptCurrInstr++; } @@ -3056,7 +3067,7 @@ void SetMoveEffect(bool32 primary, u32 certain) gBattlescriptCurrInstr++; } else if (gBattleMons[gBattlerTarget].item - && gBattleMons[gBattlerTarget].ability == ABILITY_STICKY_HOLD) + && GetBattlerAbility(gBattlerTarget) == ABILITY_STICKY_HOLD) { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_NoItemSteal; @@ -3166,6 +3177,14 @@ void SetMoveEffect(bool32 primary, u32 certain) BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_MoveEffectRecoilWithStatus; break; + case MOVE_EFFECT_RECOIL_HP_25: // Struggle + gBattleMoveDamage = (gBattleMons[gEffectBattler].maxHP) / 4; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_MoveEffectRecoil; + break; case MOVE_EFFECT_THRASH: if (gBattleMons[gEffectBattler].status2 & STATUS2_LOCK_CONFUSE) { @@ -3223,7 +3242,8 @@ void SetMoveEffect(bool32 primary, u32 certain) || gSideStatuses[GetBattlerSide(gBattlerTarget)] & SIDE_STATUS_MAT_BLOCK || gProtectStructs[gBattlerTarget].spikyShielded || gProtectStructs[gBattlerTarget].kingsShielded - || gProtectStructs[gBattlerTarget].banefulBunkered) + || gProtectStructs[gBattlerTarget].banefulBunkered + || gProtectStructs[gBattlerTarget].obstructed) { gProtectStructs[gBattlerTarget].protected = 0; gSideStatuses[GetBattlerSide(gBattlerTarget)] &= ~(SIDE_STATUS_WIDE_GUARD); @@ -3233,6 +3253,7 @@ void SetMoveEffect(bool32 primary, u32 certain) gProtectStructs[gBattlerTarget].spikyShielded = 0; gProtectStructs[gBattlerTarget].kingsShielded = 0; gProtectStructs[gBattlerTarget].banefulBunkered = 0; + gProtectStructs[gBattlerTarget].obstructed = 0; if (gCurrentMove == MOVE_FEINT) { BattleScriptPush(gBattlescriptCurrInstr + 1); @@ -3597,52 +3618,11 @@ static void Cmd_jumpifstat(void) { bool32 ret = 0; u8 battlerId = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); - u8 statValue = gBattleMons[battlerId].statStages[gBattlescriptCurrInstr[3]]; + u8 statId = gBattlescriptCurrInstr[3]; u8 cmpTo = gBattlescriptCurrInstr[4]; u8 cmpKind = gBattlescriptCurrInstr[2]; - // Because this command is used as a way of checking if a stat can be lowered/raised, - // we need to do some modification at run-time. - if (GetBattlerAbility(battlerId) == ABILITY_CONTRARY) - { - if (cmpKind == CMP_GREATER_THAN) - cmpKind = CMP_LESS_THAN; - else if (cmpKind == CMP_LESS_THAN) - cmpKind = CMP_GREATER_THAN; - - if (cmpTo == 0) - cmpTo = 0xC; - else if (cmpTo == 0xC) - cmpTo = 0; - } - - switch (cmpKind) - { - case CMP_EQUAL: - if (statValue == cmpTo) - ret++; - break; - case CMP_NOT_EQUAL: - if (statValue != cmpTo) - ret++; - break; - case CMP_GREATER_THAN: - if (statValue > cmpTo) - ret++; - break; - case CMP_LESS_THAN: - if (statValue < cmpTo) - ret++; - break; - case CMP_COMMON_BITS: - if (statValue & cmpTo) - ret++; - break; - case CMP_NO_COMMON_BITS: - if (!(statValue & cmpTo)) - ret++; - break; - } + ret = CompareStat(battlerId, statId, cmpTo, cmpKind); if (ret) gBattlescriptCurrInstr = T2_READ_PTR(gBattlescriptCurrInstr + 5); @@ -4836,6 +4816,17 @@ static void Cmd_moveend(void) gBattlescriptCurrInstr = BattleScript_BanefulBunkerEffect; effect = 1; } + else if (gProtectStructs[gBattlerTarget].obstructed && gCurrentMove != MOVE_SUCKER_PUNCH) + { + gProtectStructs[gBattlerAttacker].touchedProtectLike = 0; + i = gBattlerAttacker; + gBattlerAttacker = gBattlerTarget; + gBattlerTarget = i; // gBattlerTarget and gBattlerAttacker are swapped in order to activate Defiant, if applicable + gBattleScripting.moveEffect = MOVE_EFFECT_DEF_MINUS_2; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_KingsShieldEffect; + effect = 1; + } } gBattleScripting.moveendState++; break; @@ -4844,7 +4835,7 @@ static void Cmd_moveend(void) && gBattleMons[gBattlerTarget].hp != 0 && gBattlerAttacker != gBattlerTarget && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget) && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED - && gBattleMoves[gCurrentMove].power && gBattleMons[gBattlerTarget].statStages[STAT_ATK] < MAX_STAT_STAGE) + && gBattleMoves[gCurrentMove].power && CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) { gBattleMons[gBattlerTarget].statStages[STAT_ATK]++; BattleScriptPushCursor(); @@ -4951,8 +4942,9 @@ static void Cmd_moveend(void) else gBattleScripting.moveendState++; break; - case MOVEEND_KINGSROCK_SHELLBELL: // king's rock and shell bell - if (ItemBattleEffects(ITEMEFFECT_KINGSROCK_SHELLBELL, 0, FALSE)) + case MOVEEND_KINGSROCK: // King's rock + // These effects will occur at each hit in a multi-strike move + if (ItemBattleEffects(ITEMEFFECT_KINGSROCK, 0, FALSE)) effect = TRUE; gBattleScripting.moveendState++; break; @@ -5080,6 +5072,11 @@ static void Cmd_moveend(void) gBattleScripting.moveendState++; break; case MOVEEND_NEXT_TARGET: // For moves hitting two opposing Pokemon. + // Set a flag if move hits either target (for throat spray that can't check damage) + if (!(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) + && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) + gProtectStructs[gBattlerAttacker].targetAffected = 1; + if (!(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) && gBattleTypeFlags & BATTLE_TYPE_DOUBLE && !gProtectStructs[gBattlerAttacker].chargingTurn @@ -5112,6 +5109,7 @@ static void Cmd_moveend(void) MoveValuesCleanUp(); gBattleScripting.moveEffect = gBattleScripting.savedMoveEffect; BattleScriptPush(gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]); + gBattleStruct->atkCancellerTracker = 0; // Run all cancellers on next target gBattlescriptCurrInstr = BattleScript_FlushMessageBox; return; } @@ -5124,23 +5122,107 @@ static void Cmd_moveend(void) RecordLastUsedMoveBy(gBattlerAttacker, gCurrentMove); gBattleScripting.moveendState++; break; - case MOVEEND_LIFE_ORB: - if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_LIFE_ORB - && IsBattlerAlive(gBattlerAttacker) - && !(TestSheerForceFlag(gBattlerAttacker, gCurrentMove)) - && GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD - && gSpecialStatuses[gBattlerAttacker].damagedMons) + case MOVEEND_EJECT_BUTTON: + if (gCurrentMove != MOVE_DRAGON_TAIL + && gCurrentMove != MOVE_CIRCLE_THROW + && IsBattlerAlive(gBattlerAttacker) + && !TestSheerForceFlag(gBattlerAttacker, gCurrentMove) + && (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER || (gBattleTypeFlags & BATTLE_TYPE_TRAINER))) { - gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 10; - if (gBattleMoveDamage == 0) - gBattleMoveDamage = 1; - effect = TRUE; - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_ItemHurtRet; - gLastUsedItem = gBattleMons[gBattlerAttacker].item; + // Since we check if battler was damaged, we don't need to check move result. + // In fact, doing so actually prevents multi-target moves from activating eject button properly + u8 battlers[4] = {0, 1, 2, 3}; + SortBattlersBySpeed(battlers, FALSE); + for (i = 0; i < gBattlersCount; i++) + { + u8 battler = battlers[i]; + // Attacker is the damage-dealer, battler is mon to be switched out + if (IsBattlerAlive(battler) + && GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_EJECT_BUTTON + && !DoesSubstituteBlockMove(gCurrentMove, gBattlerAttacker, battler) + && (gSpecialStatuses[battler].physicalDmg != 0 || gSpecialStatuses[battler].specialDmg != 0) + && CountUsablePartyMons(battler) > 0) // Has mon to switch into + { + gActiveBattler = gBattleScripting.battler = battler; + gLastUsedItem = gBattleMons[battler].item; + if (gBattleMoves[gCurrentMove].effect == EFFECT_HIT_ESCAPE) + gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_EjectButtonActivates; + effect = TRUE; + break; // Only the fastest Eject Button activates + } + } } gBattleScripting.moveendState++; break; + case MOVEEND_RED_CARD: + if (gCurrentMove != MOVE_DRAGON_TAIL + && gCurrentMove != MOVE_CIRCLE_THROW + && IsBattlerAlive(gBattlerAttacker) + && !TestSheerForceFlag(gBattlerAttacker, gCurrentMove)) + { + // Since we check if battler was damaged, we don't need to check move result. + // In fact, doing so actually prevents multi-target moves from activating red card properly + u8 battlers[4] = {0, 1, 2, 3}; + SortBattlersBySpeed(battlers, FALSE); + for (i = 0; i < gBattlersCount; i++) + { + u8 battler = battlers[i]; + // Search for fastest hit pokemon with a red card + // Attacker is the one to be switched out, battler is one with red card + if (battler != gBattlerAttacker + && IsBattlerAlive(battler) + && !DoesSubstituteBlockMove(gCurrentMove, gBattlerAttacker, battler) + && GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_RED_CARD + && (gSpecialStatuses[battler].physicalDmg != 0 || gSpecialStatuses[battler].specialDmg != 0) + && CanBattlerSwitch(gBattlerAttacker)) + { + gLastUsedItem = gBattleMons[battler].item; + gActiveBattler = gBattleStruct->savedBattlerTarget = gBattleScripting.battler = battler; // Battler with red card + gEffectBattler = gBattlerAttacker; + if (gBattleMoves[gCurrentMove].effect == EFFECT_HIT_ESCAPE) + gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_RedCardActivates; + effect = TRUE; + break; // Only fastest red card activates + } + } + } + gBattleScripting.moveendState++; + break; + case MOVEEND_EJECT_PACK: + { + u8 battlers[4] = {0, 1, 2, 3}; + SortBattlersBySpeed(battlers, FALSE); + for (i = 0; i < gBattlersCount; i++) + { + u8 battler = battlers[i]; + if (IsBattlerAlive(battler) + && gProtectStructs[battler].statFell + && gProtectStructs[battler].disableEjectPack == 0 + && GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_EJECT_PACK + && !(gCurrentMove == MOVE_PARTING_SHOT && CanBattlerSwitch(gBattlerAttacker)) // Does not activate if attacker used Parting Shot and can switch out + && CountUsablePartyMons(battler) > 0) // Has mon to switch into + { + gProtectStructs[battler].statFell = FALSE; + gActiveBattler = gBattleScripting.battler = battler; + gLastUsedItem = gBattleMons[battler].item; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_EjectPackActivates; + effect = TRUE; + break; // Only fastest eject pack activates + } + } + } + gBattleScripting.moveendState++; + break; + case MOVEEND_LIFEORB_SHELLBELL: + if (ItemBattleEffects(ITEMEFFECT_LIFEORB_SHELLBELL, 0, FALSE)) + effect = TRUE; + gBattleScripting.moveendState++; + break; case MOVEEND_PICKPOCKET: if (IsBattlerAlive(gBattlerAttacker) && gBattleMons[gBattlerAttacker].item != ITEM_NONE // Attacker must be holding an item @@ -5241,6 +5323,7 @@ static void Cmd_moveend(void) if (gSpecialStatuses[gBattlerAttacker].dancerOriginalTarget) *(gBattleStruct->moveTarget + gBattlerAttacker) = gSpecialStatuses[gBattlerAttacker].dancerOriginalTarget & 0x3; gProtectStructs[gBattlerAttacker].usesBouncedMove = 0; + gProtectStructs[gBattlerAttacker].targetAffected = 0; gBattleStruct->ateBoost[gBattlerAttacker] = 0; gStatuses3[gBattlerAttacker] &= ~(STATUS3_ME_FIRST); gSpecialStatuses[gBattlerAttacker].gemBoost = 0; @@ -5510,6 +5593,7 @@ bool32 CanBattlerSwitch(u32 battlerId) } else { + // Check if attacker side has mon to switch into battlerIn1 = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) @@ -5944,6 +6028,7 @@ static void Cmd_switchineffects(void) if (!(gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_SPIKES_DAMAGED) && (gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_SPIKES) && GetBattlerAbility(gActiveBattler) != ABILITY_MAGIC_GUARD + && IsBattlerAffectedByHazards(gActiveBattler, FALSE) && IsBattlerGrounded(gActiveBattler)) { u8 spikesDmg = (5 - gSideTimers[GetBattlerSide(gActiveBattler)].spikesAmount) * 2; @@ -5956,6 +6041,7 @@ static void Cmd_switchineffects(void) } else if (!(gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_STEALTH_ROCK_DAMAGED) && (gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_STEALTH_ROCK) + && IsBattlerAffectedByHazards(gActiveBattler, FALSE) && GetBattlerAbility(gActiveBattler) != ABILITY_MAGIC_GUARD) { gSideStatuses[GetBattlerSide(gActiveBattler)] |= SIDE_STATUS_STEALTH_ROCK_DAMAGED; @@ -5977,12 +6063,13 @@ static void Cmd_switchineffects(void) BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ToxicSpikesAbsorbed; } - else + else if (IsBattlerAffectedByHazards(gActiveBattler, TRUE)) { if (!(gBattleMons[gActiveBattler].status1 & STATUS1_ANY) && !IS_BATTLER_OF_TYPE(gActiveBattler, TYPE_STEEL) && GetBattlerAbility(gActiveBattler) != ABILITY_IMMUNITY - && !(gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_SAFEGUARD)) + && !(gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_SAFEGUARD) + && !(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)) { if (gSideTimers[GetBattlerSide(gActiveBattler)].toxicSpikesAmount >= 2) gBattleMons[gActiveBattler].status1 |= STATUS1_TOXIC_POISON; @@ -5999,6 +6086,7 @@ static void Cmd_switchineffects(void) } else if (!(gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_STICKY_WEB_DAMAGED) && (gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_STICKY_WEB) + && IsBattlerAffectedByHazards(gActiveBattler, FALSE) && IsBattlerGrounded(gActiveBattler)) { gSideStatuses[GetBattlerSide(gActiveBattler)] |= SIDE_STATUS_STICKY_WEB_DAMAGED; @@ -6504,6 +6592,7 @@ static void Cmd_jumptocalledmove(void) else gChosenMove = gCurrentMove = gCalledMove; + gBattleStruct->atkCancellerTracker = 0; gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]; } @@ -7104,7 +7193,7 @@ static void HandleTerrainMove(u32 moveEffect) } else { - gFieldStatuses &= ~STATUS_TERRAIN_ANY; + gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY; gFieldStatuses |= statusFlag; if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_TERRAIN_EXTENDER) *timer = 8; @@ -7204,7 +7293,7 @@ u32 IsLeafGuardProtected(u32 battler) bool32 IsShieldsDownProtected(u32 battler) { return (gBattleMons[battler].ability == ABILITY_SHIELDS_DOWN - && gBattleMons[battler].species == SPECIES_MINIOR); + && GetFormIdFromFormSpeciesId(gBattleMons[battler].species) < GetFormIdFromFormSpeciesId(SPECIES_MINIOR_CORE_RED)); // Minior is not in core form } u32 IsAbilityStatusProtected(u32 battler) @@ -7469,7 +7558,7 @@ static void Cmd_various(void) bits = 0; for (i = STAT_ATK; i < NUM_BATTLE_STATS; i++) { - if (gBattleMons[gActiveBattler].statStages[i] != 12) + if (CompareStat(gActiveBattler, i, MAX_STAT_STAGE, CMP_LESS_THAN)) bits |= gBitTable[i]; } if (bits) @@ -7494,7 +7583,7 @@ static void Cmd_various(void) case VARIOUS_SET_MAGIC_COAT_TARGET: gBattlerAttacker = gBattlerTarget; side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE; - if (IsAffectedByFollowMe(gBattlerAttacker, side)) + if (IsAffectedByFollowMe(gBattlerAttacker, side, gCurrentMove)) gBattlerTarget = gSideTimers[side].followmeTarget; else gBattlerTarget = gActiveBattler; @@ -7706,7 +7795,7 @@ static void Cmd_various(void) || GetBattlerAbility(gActiveBattler) == ABILITY_AS_ONE_ICE_RIDER) && HasAttackerFaintedTarget() && !NoAliveMonsForEitherParty() - && gBattleMons[gBattlerAttacker].statStages[STAT_ATK] != 12) + && CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) { gBattleMons[gBattlerAttacker].statStages[STAT_ATK]++; SET_STATCHANGER(STAT_ATK, 1, FALSE); @@ -7724,7 +7813,7 @@ static void Cmd_various(void) || GetBattlerAbility(gActiveBattler) == ABILITY_AS_ONE_SHADOW_RIDER) && HasAttackerFaintedTarget() && !NoAliveMonsForEitherParty() - && gBattleMons[gBattlerAttacker].statStages[STAT_SPATK] != 12) + && CompareStat(gBattlerAttacker, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN)) { gBattleMons[gBattlerAttacker].statStages[STAT_SPATK]++; SET_STATCHANGER(STAT_SPATK, 1, FALSE); @@ -7769,7 +7858,7 @@ static void Cmd_various(void) if (GetBattlerAbility(gActiveBattler) == ABILITY_BEAST_BOOST && HasAttackerFaintedTarget() && !NoAliveMonsForEitherParty() - && gBattleMons[gBattlerAttacker].statStages[i] != 12) + && CompareStat(gBattlerAttacker, i, MAX_STAT_STAGE, CMP_LESS_THAN)) { gBattleMons[gBattlerAttacker].statStages[i]++; SET_STATCHANGER(i, 1, FALSE); @@ -7786,7 +7875,7 @@ static void Cmd_various(void) if (GetBattlerAbility(gBattleScripting.battler) == ABILITY_SOUL_HEART && IsBattlerAlive(gBattleScripting.battler) && !NoAliveMonsForEitherParty() - && gBattleMons[gBattleScripting.battler].statStages[STAT_SPATK] != 12) + && CompareStat(gBattleScripting.battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN)) { gBattleMons[gBattleScripting.battler].statStages[STAT_SPATK]++; SET_STATCHANGER(STAT_SPATK, 1, FALSE); @@ -7802,7 +7891,7 @@ static void Cmd_various(void) if (gBattleMoves[gCurrentMove].effect == EFFECT_FELL_STINGER && HasAttackerFaintedTarget() && !NoAliveMonsForEitherParty() - && gBattleMons[gBattlerAttacker].statStages[STAT_ATK] != 12) + && CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) { if (B_FELL_STINGER_STAT_RAISE >= GEN_7) SET_STATCHANGER(STAT_ATK, 3, FALSE); @@ -7834,7 +7923,9 @@ static void Cmd_various(void) } return; case VARIOUS_SUCKER_PUNCH_CHECK: - if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget)) + if (gProtectStructs[gBattlerTarget].obstructed) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); + else if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget)) gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); else if (gBattleMoves[gBattleMons[gBattlerTarget].moves[gBattleStruct->chosenMovePositions[gBattlerTarget]]].power == 0) gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); @@ -7842,15 +7933,15 @@ static void Cmd_various(void) gBattlescriptCurrInstr += 7; return; case VARIOUS_SET_SIMPLE_BEAM: - if (IsEntrainmentTargetOrSimpleBeamBannedAbility(gBattleMons[gActiveBattler].ability)) + if (IsEntrainmentTargetOrSimpleBeamBannedAbility(gBattleMons[gBattlerTarget].ability) + || gBattleMons[gBattlerTarget].ability == ABILITY_SIMPLE) { gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); } else { - gBattleMons[gActiveBattler].ability = ABILITY_SIMPLE; + gBattleMons[gBattlerTarget].ability = ABILITY_SIMPLE; gBattlescriptCurrInstr += 7; - break; } return; case VARIOUS_TRY_ENTRAINMENT: @@ -8071,7 +8162,8 @@ static void Cmd_various(void) // Change species. if (gBattlescriptCurrInstr[3] == 0) { - PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[gActiveBattler].species); + if (!gBattleTextBuff1) + PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[gActiveBattler].species); BtlController_EmitSetMonData(0, REQUEST_SPECIES_BATTLE, gBitTable[gBattlerPartyIndexes[gActiveBattler]], 2, &gBattleMons[gActiveBattler].species); MarkBattlerForControllerExec(gActiveBattler); } @@ -8128,6 +8220,7 @@ static void Cmd_various(void) && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED) { + gBattleScripting.switchCase = B_SWITCH_HIT; gBattlescriptCurrInstr = BattleScript_ForceRandomSwitch; } else @@ -8513,7 +8606,164 @@ static void Cmd_various(void) if (ItemBattleEffects(1, gActiveBattler, FALSE)) return; break; - case VARIOUS_GET_ROTOTILLER_TARGETS: + case VARIOUS_ROOM_SERVICE: + if (GetBattlerHoldEffect(gActiveBattler, TRUE) == HOLD_EFFECT_ROOM_SERVICE && TryRoomService(gActiveBattler)) + { + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_BerryStatRaiseRet; + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); + } + return; + case VARIOUS_TERRAIN_SEED: + if (GetBattlerHoldEffect(gActiveBattler, TRUE) == HOLD_EFFECT_SEEDS) + { + u8 effect = 0; + u16 item = gBattleMons[gActiveBattler].item; + switch (GetBattlerHoldEffectParam(gActiveBattler)) + { + case HOLD_EFFECT_PARAM_ELECTRIC_TERRAIN: + effect = TryHandleSeed(gActiveBattler, STATUS_FIELD_ELECTRIC_TERRAIN, STAT_DEF, item, FALSE); + break; + case HOLD_EFFECT_PARAM_GRASSY_TERRAIN: + effect = TryHandleSeed(gActiveBattler, STATUS_FIELD_GRASSY_TERRAIN, STAT_DEF, item, FALSE); + break; + case HOLD_EFFECT_PARAM_MISTY_TERRAIN: + effect = TryHandleSeed(gActiveBattler, STATUS_FIELD_MISTY_TERRAIN, STAT_SPDEF, item, FALSE); + break; + case HOLD_EFFECT_PARAM_PSYCHIC_TERRAIN: + effect = TryHandleSeed(gActiveBattler, STATUS_FIELD_PSYCHIC_TERRAIN, STAT_SPDEF, item, FALSE); + break; + } + + if (effect) + return; + } + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); + return; + case VARIOUS_MAKE_INVISIBLE: + if (gBattleControllerExecFlags) + break; + + BtlController_EmitSpriteInvisibility(0, TRUE); + MarkBattlerForControllerExec(gActiveBattler); + break; + case VARIOUS_JUMP_IF_TERRAIN_AFFECTED: + { + u32 flags = T1_READ_32(gBattlescriptCurrInstr + 3); + if (IsBattlerTerrainAffected(gActiveBattler, flags)) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 7); + else + gBattlescriptCurrInstr += 11; + } + return; + case VARIOUS_EERIE_SPELL_PP_REDUCE: + if (gLastMoves[gActiveBattler] != 0 && gLastMoves[gActiveBattler] != 0xFFFF) + { + s32 i; + + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (gLastMoves[gActiveBattler] == gBattleMons[gActiveBattler].moves[i]) + break; + } + + if (i != MAX_MON_MOVES && gBattleMons[gActiveBattler].pp[i] != 0) + { + s32 ppToDeduct = 3; + + if (gBattleMons[gActiveBattler].pp[i] < ppToDeduct) + ppToDeduct = gBattleMons[gActiveBattler].pp[i]; + + PREPARE_MOVE_BUFFER(gBattleTextBuff1, gLastMoves[gActiveBattler]) + ConvertIntToDecimalStringN(gBattleTextBuff2, ppToDeduct, STR_CONV_MODE_LEFT_ALIGN, 1); + PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff2, 1, ppToDeduct) + gBattleMons[gActiveBattler].pp[i] -= ppToDeduct; + if (!(gDisableStructs[gActiveBattler].mimickedMoves & gBitTable[i]) + && !(gBattleMons[gActiveBattler].status2 & STATUS2_TRANSFORMED)) + { + BtlController_EmitSetMonData(0, REQUEST_PPMOVE1_BATTLE + i, 0, 1, &gBattleMons[gActiveBattler].pp[i]); + MarkBattlerForControllerExec(gActiveBattler); + } + + if (gBattleMons[gActiveBattler].pp[i] == 0) + CancelMultiTurnMoves(gActiveBattler); + + gBattlescriptCurrInstr += 7; // continue + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); // cant reduce pp + } + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); // cant reduce pp + } + return; + case VARIOUS_JUMP_IF_TEAM_HEALTHY: + if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && IsBattlerAlive(BATTLE_PARTNER(gActiveBattler))) + { + u8 partner = BATTLE_PARTNER(gActiveBattler); + if ((gBattleMons[gActiveBattler].hp == gBattleMons[gActiveBattler].maxHP && !(gBattleMons[gActiveBattler].status1 & STATUS1_ANY)) + && (gBattleMons[partner].hp == gBattleMons[partner].maxHP && !(gBattleMons[partner].status1 & STATUS1_ANY))) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); // fail + else + gBattlescriptCurrInstr += 7; + } + else // single battle + { + if (gBattleMons[gActiveBattler].hp == gBattleMons[gActiveBattler].maxHP && !(gBattleMons[gActiveBattler].status1 & STATUS1_ANY)) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); // fail + else + gBattlescriptCurrInstr += 7; + } + return; + case VARIOUS_TRY_HEAL_QUARTER_HP: + gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 4; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + gBattleMoveDamage *= -1; + + if (gBattleMons[gActiveBattler].hp == gBattleMons[gActiveBattler].maxHP) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); // fail + else + gBattlescriptCurrInstr += 7; // can heal + return; + case VARIOUS_REMOVE_TERRAIN: + switch (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY) + { + case STATUS_FIELD_MISTY_TERRAIN: + gFieldTimers.mistyTerrainTimer = 0; + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + break; + case STATUS_FIELD_GRASSY_TERRAIN: + gFieldTimers.grassyTerrainTimer = 0; + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + break; + case STATUS_FIELD_ELECTRIC_TERRAIN: + gFieldTimers.electricTerrainTimer = 0; + gBattleCommunication[MULTISTRING_CHOOSER] = 2; + break; + case STATUS_FIELD_PSYCHIC_TERRAIN: + gFieldTimers.psychicTerrainTimer = 0; + gBattleCommunication[MULTISTRING_CHOOSER] = 3; + break; + default: + gBattleCommunication[MULTISTRING_CHOOSER] = 4; // failsafe + break; + } + gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY; // remove the terrain + break; + case VARIOUS_JUMP_IF_PRANKSTER_BLOCKED: + if (BlocksPrankster(gCurrentMove, gBattlerAttacker, gActiveBattler)) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); + else + gBattlescriptCurrInstr += 7; + return; + case VARIOUS_GET_ROTOTILLER_TARGETS: // Gets the battlers to be affected by rototiller. If there are none, print 'But it failed!' { u32 count = 0; @@ -8588,6 +8838,11 @@ static void Cmd_setprotectlike(void) gProtectStructs[gBattlerAttacker].banefulBunkered = 1; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_ITSELF; } + else if (gCurrentMove == MOVE_OBSTRUCT) + { + gProtectStructs[gBattlerAttacker].obstructed = 1; + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + } gDisableStructs[gBattlerAttacker].protectUses++; fail = FALSE; @@ -8869,6 +9124,14 @@ static void Cmd_trysetrest(void) { gBattlescriptCurrInstr = failJump; } + else if (IsBattlerTerrainAffected(gBattlerTarget, STATUS_FIELD_ELECTRIC_TERRAIN)) + { + gBattlescriptCurrInstr = BattleScript_ElectricTerrainPrevents; + } + else if (IsBattlerTerrainAffected(gBattlerTarget, STATUS_FIELD_MISTY_TERRAIN)) + { + gBattlescriptCurrInstr = BattleScript_MistyTerrainPrevents; + } else { if (gBattleMons[gBattlerTarget].status1 & ((u8)(~STATUS1_SLEEP))) @@ -8912,7 +9175,7 @@ bool8 UproarWakeUpCheck(u8 battlerId) for (i = 0; i < gBattlersCount; i++) { - if (!(gBattleMons[i].status2 & STATUS2_UPROAR) || gBattleMons[battlerId].ability == ABILITY_SOUNDPROOF) + if (!(gBattleMons[i].status2 & STATUS2_UPROAR) || GetBattlerAbility(battlerId) == ABILITY_SOUNDPROOF) continue; gBattleScripting.battler = i; @@ -9227,11 +9490,16 @@ static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr gBattleTextBuff2[index] = STRINGID_STATFELL >> 8; index++; gBattleTextBuff2[index] = B_BUFF_EOS; - + if (gBattleMons[gActiveBattler].statStages[statId] == MIN_STAT_STAGE) + { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STAT_WONT_DECREASE; + } else + { + gProtectStructs[gActiveBattler].statFell = 1; // Eject pack, lash out gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == gActiveBattler); // B_MSG_ATTACKER_STAT_FELL or B_MSG_DEFENDER_STAT_FELL + } } } else // stat increase @@ -9266,9 +9534,14 @@ static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr gBattleTextBuff2[index] = B_BUFF_EOS; if (gBattleMons[gActiveBattler].statStages[statId] == MAX_STAT_STAGE) + { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STAT_WONT_INCREASE; + } else - gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == gActiveBattler); // B_MSG_ATTACKER_STAT_ROSE or B_MSG_DEFENDER_STAT_ROSE + { + gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == gActiveBattler); + gProtectStructs[gActiveBattler].statRaised = 1; + } } gBattleMons[gActiveBattler].statStages[statId] += statValue; @@ -9406,6 +9679,36 @@ static void Cmd_forcerandomswitch(void) struct Pokemon* party = NULL; s32 validMons = 0; s32 minNeeded; + + bool32 redCardForcedSwitch = FALSE; + + // Red card checks against wild pokemon. If we have reached here, the player has a mon to switch into + // Red card swaps attacker with target to get the animation correct, so here we check attacker which is really the target. Thanks GF... + if (gBattleScripting.switchCase == B_SWITCH_RED_CARD + && !(gBattleTypeFlags & BATTLE_TYPE_TRAINER) + && GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT) // Check opponent's red card activating + { + if (!WILD_DOUBLE_BATTLE) + { + // Wild mon with red card will end single battle + gBattlescriptCurrInstr = BattleScript_RoarSuccessEndBattle; + return; + } + else + { + // Wild double battle, wild mon red card activation on player + if (IS_WHOLE_SIDE_ALIVE(gBattlerTarget)) + { + // Both player's battlers are alive + redCardForcedSwitch = FALSE; + } + else + { + // Player has only one mon alive -> force red card switch before manually switching to other mon + redCardForcedSwitch = TRUE; + } + } + } // Swapping pokemon happens in: // trainer battles @@ -9419,8 +9722,9 @@ static void Cmd_forcerandomswitch(void) || (WILD_DOUBLE_BATTLE && GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER && GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER) + || redCardForcedSwitch ) - { + { if (GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER) party = gPlayerParty; else @@ -9528,7 +9832,7 @@ static void Cmd_forcerandomswitch(void) } } - if (validMons <= minNeeded) + if (!redCardForcedSwitch && validMons <= minNeeded) { gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); } @@ -10101,7 +10405,7 @@ static void Cmd_counterdamagecalculator(void) { gBattleMoveDamage = gProtectStructs[gBattlerAttacker].physicalDmg * 2; - if (IsAffectedByFollowMe(gBattlerAttacker, sideTarget)) + if (IsAffectedByFollowMe(gBattlerAttacker, sideTarget, gCurrentMove)) gBattlerTarget = gSideTimers[sideTarget].followmeTarget; else gBattlerTarget = gProtectStructs[gBattlerAttacker].physicalBattlerId; @@ -10126,7 +10430,7 @@ static void Cmd_mirrorcoatdamagecalculator(void) // a copy of atkA1 with the phy { gBattleMoveDamage = gProtectStructs[gBattlerAttacker].specialDmg * 2; - if (IsAffectedByFollowMe(gBattlerAttacker, sideTarget)) + if (IsAffectedByFollowMe(gBattlerAttacker, sideTarget, gCurrentMove)) gBattlerTarget = gSideTimers[sideTarget].followmeTarget; else gBattlerTarget = gProtectStructs[gBattlerAttacker].specialBattlerId; @@ -10520,7 +10824,7 @@ static void Cmd_healpartystatus(void) else party = gEnemyParty; - if (gBattleMons[gBattlerAttacker].ability != ABILITY_SOUNDPROOF) + if (GetBattlerAbility(gBattlerAttacker) != ABILITY_SOUNDPROOF) { gBattleMons[gBattlerAttacker].status1 = 0; gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_NIGHTMARE); @@ -10536,7 +10840,7 @@ static void Cmd_healpartystatus(void) if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && !(gAbsentBattlerFlags & gBitTable[gActiveBattler])) { - if (gBattleMons[gActiveBattler].ability != ABILITY_SOUNDPROOF) + if (GetBattlerAbility(gActiveBattler) != ABILITY_SOUNDPROOF) { gBattleMons[gActiveBattler].status1 = 0; gBattleMons[gActiveBattler].status2 &= ~(STATUS2_NIGHTMARE); @@ -10649,7 +10953,8 @@ static void Cmd_trysetperishsong(void) for (i = 0; i < gBattlersCount; i++) { if (gStatuses3[i] & STATUS3_PERISH_SONG - || gBattleMons[i].ability == ABILITY_SOUNDPROOF) + || GetBattlerAbility(i) == ABILITY_SOUNDPROOF + || BlocksPrankster(gCurrentMove, gBattlerAttacker, i)) { notAffectedCount++; } @@ -10697,8 +11002,8 @@ static void Cmd_handlerollout(void) static void Cmd_jumpifconfusedandstatmaxed(void) { if (gBattleMons[gBattlerTarget].status2 & STATUS2_CONFUSION - && gBattleMons[gBattlerTarget].statStages[gBattlescriptCurrInstr[1]] == MAX_STAT_STAGE) - gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 2); + && !CompareStat(gBattlerTarget, gBattlescriptCurrInstr[1], MAX_STAT_STAGE, CMP_LESS_THAN)) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 2); // Fails if we're confused AND stat cannot be raised else gBattlescriptCurrInstr += 6; } @@ -10907,7 +11212,8 @@ static void Cmd_maxattackhalvehp(void) // belly drum if (!(gBattleMons[gBattlerAttacker].maxHP / 2)) halfHp = 1; - + + // Belly Drum fails if the user's current HP is less than half its maximum, or if the user's Attack is already at +6 (even if the user has Contrary). if (gBattleMons[gBattlerAttacker].statStages[STAT_ATK] < MAX_STAT_STAGE && gBattleMons[gBattlerAttacker].hp > halfHp) { @@ -11222,7 +11528,7 @@ static void Cmd_setcharge(void) static void Cmd_callterrainattack(void) // nature power { gHitMarker &= ~(HITMARKER_ATTACKSTRING_PRINTED); - gCurrentMove = sNaturePowerMoves[gBattleTerrain]; + gCurrentMove = GetNaturePowerMove(); gBattlerTarget = GetMoveTarget(gCurrentMove, 0); BattleScriptPush(gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]); gBattlescriptCurrInstr++; @@ -11230,7 +11536,14 @@ static void Cmd_callterrainattack(void) // nature power u16 GetNaturePowerMove(void) { - //TODO terrain + if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN) + return MOVE_MOONBLAST; + else if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) + return MOVE_THUNDERBOLT; + else if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN) + return MOVE_ENERGY_BALL; + else if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN) + return MOVE_PSYCHIC; return sNaturePowerMoves[gBattleTerrain]; } @@ -11356,7 +11669,7 @@ static void Cmd_tryswapitems(void) // trick gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); } // check if ability prevents swapping - else if (gBattleMons[gBattlerTarget].ability == ABILITY_STICKY_HOLD) + else if (GetBattlerAbility(gBattlerTarget) == ABILITY_STICKY_HOLD) { gBattlescriptCurrInstr = BattleScript_StickyHoldActivates; gLastUsedAbility = gBattleMons[gBattlerTarget].ability; @@ -11507,6 +11820,18 @@ static void Cmd_setyawn(void) { gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); } + else if (IsBattlerTerrainAffected(gBattlerTarget, STATUS_FIELD_ELECTRIC_TERRAIN)) + { + // When Yawn is used while Electric Terrain is set and drowsiness is set from Yawn being used against target in the previous turn: + // "But it failed" will display first. + gBattlescriptCurrInstr = BattleScript_ElectricTerrainPrevents; + } + else if (IsBattlerTerrainAffected(gBattlerTarget, STATUS_FIELD_MISTY_TERRAIN)) + { + // When Yawn is used while Misty Terrain is set and drowsiness is set from Yawn being used against target in the previous turn: + // "But it failed" will display first. + gBattlescriptCurrInstr = BattleScript_MistyTerrainPrevents; + } else { gStatuses3[gBattlerTarget] |= STATUS3_YAWN_TURN(2); @@ -12580,14 +12905,14 @@ static void Cmd_trainerslideout(void) static const u16 sTelekinesisBanList[] = { - SPECIES_DIGLETT, - SPECIES_DUGTRIO, + SPECIES_DIGLETT, + SPECIES_DUGTRIO, #ifdef POKEMON_EXPANSION - SPECIES_DIGLETT_ALOLAN, - SPECIES_DUGTRIO_ALOLAN, - SPECIES_SANDYGAST, - SPECIES_PALOSSAND, - SPECIES_GENGAR_MEGA, + SPECIES_DIGLETT_ALOLAN, + SPECIES_DUGTRIO_ALOLAN, + SPECIES_SANDYGAST, + SPECIES_PALOSSAND, + SPECIES_GENGAR_MEGA, #endif }; @@ -12697,7 +13022,7 @@ static void Cmd_metalburstdamagecalculator(void) { gBattleMoveDamage = gProtectStructs[gBattlerAttacker].physicalDmg * 150 / 100; - if (IsAffectedByFollowMe(gBattlerAttacker, sideTarget)) + if (IsAffectedByFollowMe(gBattlerAttacker, sideTarget, gCurrentMove)) gBattlerTarget = gSideTimers[sideTarget].followmeTarget; else gBattlerTarget = gProtectStructs[gBattlerAttacker].physicalBattlerId; @@ -12710,7 +13035,7 @@ static void Cmd_metalburstdamagecalculator(void) { gBattleMoveDamage = gProtectStructs[gBattlerAttacker].specialDmg * 150 / 100; - if (IsAffectedByFollowMe(gBattlerAttacker, sideTarget)) + if (IsAffectedByFollowMe(gBattlerAttacker, sideTarget, gCurrentMove)) gBattlerTarget = gSideTimers[sideTarget].followmeTarget; else gBattlerTarget = gProtectStructs[gBattlerAttacker].specialBattlerId; diff --git a/src/battle_util.c b/src/battle_util.c index 2215fea48..a709aaf9e 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -217,12 +217,13 @@ static const u16 sEntrainmentTargetSimpleBeamBannedAbilities[] = ABILITY_GULP_MISSILE, }; -bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide) +bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move) { u32 ability = GetBattlerAbility(battlerAtk); if (gSideTimers[defSide].followmeTimer == 0 || gBattleMons[gSideTimers[defSide].followmeTarget].hp == 0 + || gBattleMoves[move].effect == EFFECT_SNIPE_SHOT || ability == ABILITY_PROPELLER_TAIL || ability == ABILITY_STALWART) return FALSE; @@ -307,7 +308,7 @@ void HandleAction_UseMove(void) // choose target side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE; - if (IsAffectedByFollowMe(gBattlerAttacker, side) + if (IsAffectedByFollowMe(gBattlerAttacker, side, gCurrentMove) && gBattleMoves[gCurrentMove].target == MOVE_TARGET_SELECTED && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gSideTimers[side].followmeTarget)) { @@ -327,6 +328,7 @@ void HandleAction_UseMove(void) && ((GetBattlerAbility(gActiveBattler) == ABILITY_LIGHTNING_ROD && moveType == TYPE_ELECTRIC) || (GetBattlerAbility(gActiveBattler) == ABILITY_STORM_DRAIN && moveType == TYPE_WATER)) && GetBattlerTurnOrderNum(gActiveBattler) < var + && gBattleMoves[gCurrentMove].effect != EFFECT_SNIPE_SHOT && (GetBattlerAbility(gBattlerAttacker) != ABILITY_PROPELLER_TAIL || GetBattlerAbility(gBattlerAttacker) != ABILITY_STALWART)) { @@ -981,6 +983,11 @@ static const u8 sAbilitiesAffectedByMoldBreaker[] = [ABILITY_FLUFFY] = 1, [ABILITY_QUEENLY_MAJESTY] = 1, [ABILITY_WATER_BUBBLE] = 1, + [ABILITY_MIRROR_ARMOR] = 1, + [ABILITY_PUNK_ROCK] = 1, + [ABILITY_ICE_SCALES] = 1, + [ABILITY_ICE_FACE] = 1, + [ABILITY_PASTEL_VEIL] = 1, }; static const u8 sAbilitiesNotTraced[ABILITIES_COUNT] = @@ -1261,7 +1268,7 @@ void PressurePPLose(u8 target, u8 attacker, u16 move) { int moveIndex; - if (gBattleMons[target].ability != ABILITY_PRESSURE) + if (GetBattlerAbility(target) != ABILITY_PRESSURE) return; for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) @@ -1293,7 +1300,7 @@ void PressurePPLoseOnUsingImprison(u8 attacker) for (i = 0; i < gBattlersCount; i++) { - if (atkSide != GetBattlerSide(i) && gBattleMons[i].ability == ABILITY_PRESSURE) + if (atkSide != GetBattlerSide(i) && GetBattlerAbility(i) == ABILITY_PRESSURE) { for (j = 0; j < MAX_MON_MOVES; j++) { @@ -1326,7 +1333,7 @@ void PressurePPLoseOnUsingPerishSong(u8 attacker) for (i = 0; i < gBattlersCount; i++) { - if (gBattleMons[i].ability == ABILITY_PRESSURE && i != attacker) + if (GetBattlerAbility(i) == ABILITY_PRESSURE && i != attacker) { for (j = 0; j < MAX_MON_MOVES; j++) { @@ -1410,7 +1417,6 @@ void CancelMultiTurnMoves(u8 battler) bool8 WasUnableToUseMove(u8 battler) { if (gProtectStructs[battler].prlzImmobility - || gProtectStructs[battler].targetNotAffected || gProtectStructs[battler].usedImprisonedMove || gProtectStructs[battler].loveImmobility || gProtectStructs[battler].usedDisabledMove @@ -1444,8 +1450,8 @@ void PrepareStringBattle(u16 stringId, u8 battler) // Check Defiant and Competitive stat raise whenever a stat is lowered. else if ((stringId == STRINGID_DEFENDERSSTATFELL || stringId == STRINGID_PKMNCUTSATTACKWITH) - && ((GetBattlerAbility(gBattlerTarget) == ABILITY_DEFIANT && gBattleMons[gBattlerTarget].statStages[STAT_ATK] != MAX_STAT_STAGE) - || (GetBattlerAbility(gBattlerTarget) == ABILITY_COMPETITIVE && gBattleMons[gBattlerTarget].statStages[STAT_SPATK] != MAX_STAT_STAGE)) + && ((GetBattlerAbility(gBattlerTarget) == ABILITY_DEFIANT && CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) + || (GetBattlerAbility(gBattlerTarget) == ABILITY_COMPETITIVE && CompareStat(gBattlerTarget, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN))) && gSpecialStatuses[gBattlerTarget].changedStatsBattlerId != BATTLE_PARTNER(gBattlerTarget) && gSpecialStatuses[gBattlerTarget].changedStatsBattlerId != gBattlerTarget) { @@ -2722,16 +2728,29 @@ u8 DoBattlerEndTurnEffects(void) { gStatuses3[gActiveBattler] -= STATUS3_YAWN_TURN(1); if (!(gStatuses3[gActiveBattler] & STATUS3_YAWN) && !(gBattleMons[gActiveBattler].status1 & STATUS1_ANY) - && gBattleMons[gActiveBattler].ability != ABILITY_VITAL_SPIRIT - && gBattleMons[gActiveBattler].ability != ABILITY_INSOMNIA && !UproarWakeUpCheck(gActiveBattler) + && GetBattlerAbility(gActiveBattler) != ABILITY_VITAL_SPIRIT + && GetBattlerAbility(gActiveBattler) != ABILITY_INSOMNIA && !UproarWakeUpCheck(gActiveBattler) && !IsLeafGuardProtected(gActiveBattler)) { CancelMultiTurnMoves(gActiveBattler); - gBattleMons[gActiveBattler].status1 |= (Random() & 3) + 2; - BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); - MarkBattlerForControllerExec(gActiveBattler); gEffectBattler = gActiveBattler; - BattleScriptExecute(BattleScript_YawnMakesAsleep); + if (IsBattlerTerrainAffected(gActiveBattler, STATUS_FIELD_ELECTRIC_TERRAIN)) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAINPREVENTS_ELECTRIC; + BattleScriptExecute(BattleScript_TerrainPreventsEnd2); + } + else if (IsBattlerTerrainAffected(gActiveBattler, STATUS_FIELD_MISTY_TERRAIN)) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAINPREVENTS_MISTY; + BattleScriptExecute(BattleScript_TerrainPreventsEnd2); + } + else + { + gBattleMons[gActiveBattler].status1 |= (Random() & 3) + 2; + BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); + MarkBattlerForControllerExec(gActiveBattler); + BattleScriptExecute(BattleScript_YawnMakesAsleep); + } effect++; } } @@ -3073,6 +3092,7 @@ enum CANCELLER_POWDER_MOVE, CANCELLER_POWDER_STATUS, CANCELLER_THROAT_CHOP, + CANCELLER_PRANKSTER, CANCELLER_END, CANCELLER_PSYCHIC_TERRAIN, CANCELLER_END2, @@ -3106,7 +3126,7 @@ u8 AtkCanceller_UnableToUseMove(void) else { u8 toSub; - if (gBattleMons[gBattlerAttacker].ability == ABILITY_EARLY_BIRD) + if (GetBattlerAbility(gBattlerAttacker) == ABILITY_EARLY_BIRD) toSub = 2; else toSub = 1; @@ -3116,7 +3136,7 @@ u8 AtkCanceller_UnableToUseMove(void) gBattleMons[gBattlerAttacker].status1 -= toSub; if (gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) { - if (gCurrentMove != MOVE_SNORE && gCurrentMove != MOVE_SLEEP_TALK) + if (gChosenMove != MOVE_SNORE && gChosenMove != MOVE_SLEEP_TALK) { gBattlescriptCurrInstr = BattleScript_MoveUsedIsAsleep; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; @@ -3400,6 +3420,18 @@ u8 AtkCanceller_UnableToUseMove(void) } gBattleStruct->atkCancellerTracker++; break; + case CANCELLER_PRANKSTER: + if (BlocksPrankster(gCurrentMove, gBattlerAttacker, gBattlerTarget) + && !(IS_MOVE_STATUS(gCurrentMove) && GetBattlerAbility(gBattlerTarget) == ABILITY_MAGIC_BOUNCE)) + { + if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE) || !(gBattleMoves[gCurrentMove].target & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))) + CancelMultiTurnMoves(gBattlerAttacker); // Don't cancel moves that can hit two targets bc one target might not be protected + gBattleScripting.battler = gBattlerAbility = gBattlerTarget; + gBattlescriptCurrInstr = BattleScript_DarkTypePreventsPrankster; + effect = 1; + } + gBattleStruct->atkCancellerTracker++; + break; case CANCELLER_END: break; } @@ -3703,6 +3735,8 @@ static bool32 ShouldChangeFormHpBased(u32 battler) {ABILITY_SHIELDS_DOWN, SPECIES_MINIOR_METEOR_VIOLET, SPECIES_MINIOR_CORE_VIOLET, 2}, {ABILITY_SHIELDS_DOWN, SPECIES_MINIOR_METEOR_YELLOW, SPECIES_MINIOR_CORE_YELLOW, 2}, {ABILITY_SCHOOLING, SPECIES_WISHIWASHI_SCHOOL, SPECIES_WISHIWASHI, 4}, + {ABILITY_GULP_MISSILE, SPECIES_CRAMORANT, SPECIES_CRAMORANT_GORGING, 2}, + {ABILITY_GULP_MISSILE, SPECIES_CRAMORANT, SPECIES_CRAMORANT_GULPING, 1}, }; u32 i; @@ -3824,11 +3858,11 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move switch (gLastUsedAbility) { case ABILITYEFFECT_SWITCH_IN_TERRAIN: - if (VarGet(VAR_TERRAIN) & STATUS_TERRAIN_ANY) + if (VarGet(VAR_TERRAIN) & STATUS_FIELD_TERRAIN_ANY) { - u16 terrainFlags = VarGet(VAR_TERRAIN) & STATUS_TERRAIN_ANY; // only works for status flag (1 << 15) + u16 terrainFlags = VarGet(VAR_TERRAIN) & STATUS_FIELD_TERRAIN_ANY; // only works for status flag (1 << 15) gFieldStatuses = terrainFlags | STATUS_FIELD_TERRAIN_PERMANENT; // terrain is permanent - switch (VarGet(VAR_TERRAIN) & STATUS_TERRAIN_ANY) + switch (VarGet(VAR_TERRAIN) & STATUS_FIELD_TERRAIN_ANY) { case STATUS_FIELD_ELECTRIC_TERRAIN: gBattleCommunication[MULTISTRING_CHOOSER] = 2; @@ -4033,7 +4067,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move u32 opposingDef = 0, opposingSpDef = 0; opposingBattler = BATTLE_OPPOSITE(battler); - for (i = 0; i < 2; opposingBattler ^= BIT_SIDE, i++) + for (i = 0; i < 2; opposingBattler ^= BIT_FLANK, i++) { if (IsBattlerAlive(opposingBattler)) { @@ -4053,10 +4087,11 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move gSpecialStatuses[battler].switchInAbilityDone = 1; - if (gBattleMons[battler].statStages[statId] != MAX_STAT_STAGE) + if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN)) { gBattleMons[battler].statStages[statId]++; SET_STATCHANGER(statId, 1, FALSE); + gBattlerAttacker = battler; PREPARE_STAT_BUFFER(gBattleTextBuff1, statId); BattleScriptPushCursorAndCallback(BattleScript_AttackerAbilityStatRaiseEnd3); effect++; @@ -4304,7 +4339,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move } break; case ABILITY_SPEED_BOOST: - if (gBattleMons[battler].statStages[STAT_SPEED] < MAX_STAT_STAGE && gDisableStructs[battler].isFirstTurn != 2) + if (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN) && gDisableStructs[battler].isFirstTurn != 2) { gBattleMons[battler].statStages[STAT_SPEED]++; gBattleScripting.animArg1 = 14 + STAT_SPEED; @@ -4322,9 +4357,9 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move for (i = STAT_ATK; i < statsNum; i++) { - if (gBattleMons[battler].statStages[i] != MIN_STAT_STAGE) + if (CompareStat(battler, i, MIN_STAT_STAGE, CMP_GREATER_THAN)) validToLower |= gBitTable[i]; - if (gBattleMons[battler].statStages[i] != MAX_STAT_STAGE) + if (CompareStat(battler, i, MAX_STAT_STAGE, CMP_LESS_THAN)) validToRaise |= gBitTable[i]; } @@ -4527,7 +4562,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move } else if (effect == 2) // Boost Stat ability; { - if (gBattleMons[battler].statStages[statId] == MAX_STAT_STAGE) + if (!CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN)) { if ((gProtectStructs[gBattlerAttacker].notFirstStrike)) gBattlescriptCurrInstr = BattleScript_MonMadeMoveUseless; @@ -4556,7 +4591,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) && moveType == TYPE_DARK - && gBattleMons[battler].statStages[STAT_ATK] != MAX_STAT_STAGE) + && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) { SET_STATCHANGER(STAT_ATK, 1, FALSE); BattleScriptPushCursor(); @@ -4569,7 +4604,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) && (moveType == TYPE_DARK || moveType == TYPE_BUG || moveType == TYPE_GHOST) - && gBattleMons[battler].statStages[STAT_SPEED] != MAX_STAT_STAGE) + && CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN)) { SET_STATCHANGER(STAT_SPEED, 1, FALSE); BattleScriptPushCursor(); @@ -4582,7 +4617,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) && moveType == TYPE_WATER - && gBattleMons[battler].statStages[STAT_DEF] != MAX_STAT_STAGE) + && CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN)) { SET_STATCHANGER(STAT_DEF, 2, FALSE); BattleScriptPushCursor(); @@ -4594,7 +4629,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) - && gBattleMons[battler].statStages[STAT_DEF] != MAX_STAT_STAGE) + && CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN)) { SET_STATCHANGER(STAT_DEF, 1, FALSE); BattleScriptPushCursor(); @@ -4611,7 +4646,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move && gBattleMons[battler].hp < gBattleMons[battler].maxHP / 2 && (gMultiHitCounter == 0 || gMultiHitCounter == 1) && !(TestSheerForceFlag(gBattlerAttacker, gCurrentMove)) - && gBattleMons[battler].statStages[STAT_SPATK] != MAX_STAT_STAGE) + && CompareStat(battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN)) { SET_STATCHANGER(STAT_SPATK, 1, FALSE); BattleScriptPushCursor(); @@ -4642,8 +4677,12 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) && IS_MOVE_PHYSICAL(gCurrentMove) - && (gBattleMons[battler].statStages[STAT_SPEED] != MAX_STAT_STAGE || gBattleMons[battler].statStages[STAT_DEF] != MIN_STAT_STAGE)) + && (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN) // Don't activate if speed cannot be raised + || CompareStat(battler, STAT_DEF, MIN_STAT_STAGE, CMP_GREATER_THAN))) // Don't activate if defense cannot be lowered { + if (gBattleMoves[gCurrentMove].effect == EFFECT_HIT_ESCAPE && CanBattlerSwitch(gBattlerAttacker)) + gProtectStructs[battler].disableEjectPack = 1; // Set flag for target + BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_WeakArmorActivates; effect++; @@ -4699,7 +4738,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move && gIsCriticalHit && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) - && gBattleMons[battler].statStages[STAT_ATK] != MAX_STAT_STAGE) + && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) { SET_STATCHANGER(STAT_ATK, MAX_STAT_STAGE - gBattleMons[battler].statStages[STAT_ATK], FALSE); BattleScriptPushCursor(); @@ -4726,7 +4765,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move case ABILITY_TANGLING_HAIR: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 - && gBattleMons[gBattlerAttacker].statStages[STAT_SPEED] != MIN_STAT_STAGE + && CompareStat(gBattlerAttacker, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED && IsMoveMakingContact(move, gBattlerAttacker)) @@ -4797,10 +4836,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED - && GetBattlerAbility(gBattlerAttacker) != ABILITY_INSOMNIA - && GetBattlerAbility(gBattlerAttacker) != ABILITY_VITAL_SPIRIT - && !(gBattleMons[gBattlerAttacker].status1 & STATUS1_ANY) - && !IsAbilityStatusProtected(gBattlerAttacker) + && CanSleep(gBattlerAttacker) && IsMoveMakingContact(move, gBattlerAttacker) && (Random() % 3) == 0) { @@ -4819,11 +4855,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED - && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_POISON) - && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_STEEL) - && GetBattlerAbility(gBattlerAttacker) != ABILITY_IMMUNITY - && !(gBattleMons[gBattlerAttacker].status1 & STATUS1_ANY) - && !IsAbilityStatusProtected(gBattlerAttacker) + && CanBePoisoned(gBattlerAttacker) && IsMoveMakingContact(move, gBattlerAttacker) && (Random() % 3) == 0) { @@ -4841,10 +4873,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED - && CanParalyzeType(gBattlerTarget, gBattlerAttacker) - && GetBattlerAbility(gBattlerAttacker) != ABILITY_LIMBER - && !(gBattleMons[gBattlerAttacker].status1 & STATUS1_ANY) - && !IsAbilityStatusProtected(gBattlerAttacker) + && CanBeParalyzed(gBattlerAttacker) && IsMoveMakingContact(move, gBattlerAttacker) && (Random() % 3) == 0) { @@ -4861,11 +4890,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && (gBattleMoves[move].flags & FLAG_MAKES_CONTACT) && TARGET_TURN_DAMAGED - && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_FIRE) - && GetBattlerAbility(gBattlerAttacker) != ABILITY_WATER_VEIL - && GetBattlerAbility(gBattlerAttacker) != ABILITY_WATER_BUBBLE - && !(gBattleMons[gBattlerAttacker].status1 & STATUS1_ANY) - && !IsAbilityStatusProtected(gBattlerAttacker) + && CanBeBurned(gBattlerAttacker) && (Random() % 3) == 0) { gBattleScripting.moveEffect = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_BURN; @@ -4920,7 +4945,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) - && gBattleMons[battler].statStages[STAT_SPEED] != MAX_STAT_STAGE + && CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN) && (moveType == TYPE_FIRE || moveType == TYPE_WATER)) { SET_STATCHANGER(STAT_SPEED, 6, FALSE); @@ -4964,6 +4989,42 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move effect++; } break; + case ABILITY_GULP_MISSILE: + if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && !gProtectStructs[gBattlerAttacker].confusionSelfDmg + && TARGET_TURN_DAMAGED + && IsBattlerAlive(battler)) + { + if (gBattleMons[gBattlerTarget].species == SPECIES_CRAMORANT_GORGING) + { + gBattleStruct->changedSpecies[gBattlerPartyIndexes[gBattlerTarget]] = gBattleMons[gBattlerTarget].species; + gBattleMons[gBattlerTarget].species = SPECIES_CRAMORANT; + if (GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) + { + gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 4; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + } + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_GulpMissileGorging; + effect++; + } + else if (gBattleMons[gBattlerTarget].species == SPECIES_CRAMORANT_GULPING) + { + gBattleStruct->changedSpecies[gBattlerPartyIndexes[gBattlerTarget]] = gBattleMons[gBattlerTarget].species; + gBattleMons[gBattlerTarget].species = SPECIES_CRAMORANT; + if (GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) + { + gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 4; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + } + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_GulpMissileGulping; + effect++; + } + } + break; } break; case ABILITYEFFECT_MOVE_END_ATTACKER: // Same as above, but for attacker @@ -4973,11 +5034,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerTarget].hp != 0 && !gProtectStructs[gBattlerTarget].confusionSelfDmg - && !IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_POISON) - && !IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_STEEL) - && GetBattlerAbility(gBattlerTarget) != ABILITY_IMMUNITY - && !(gBattleMons[gBattlerTarget].status1 & STATUS1_ANY) - && !IsAbilityStatusProtected(gBattlerTarget) + && CanBePoisoned(gBattlerTarget) && IsMoveMakingContact(move, gBattlerAttacker) && (Random() % 3) == 0) { @@ -5004,6 +5061,27 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move effect++; } break; + case ABILITY_GULP_MISSILE: + if (((gCurrentMove == MOVE_SURF && TARGET_TURN_DAMAGED) || gStatuses3[gBattlerAttacker] & STATUS3_UNDERWATER) + && (effect = ShouldChangeFormHpBased(gBattlerAttacker))) + { + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_AttackerFormChange; + effect++; + } + break; + case ABILITY_BATTLE_BOND: + if (gBattleMons[gBattlerAttacker].species == SPECIES_GRENINJA_BATTLE_BOND + && gBattleResults.opponentFaintCounter != 0 + && CalculateEnemyPartyCount() > 1) + { + PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerAttacker].species); + gBattleStruct->changedSpecies[gBattlerPartyIndexes[gBattlerAttacker]] = gBattleMons[gBattlerAttacker].species; + gBattleMons[gBattlerAttacker].species = SPECIES_GRENINJA_ASH; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_BattleBondActivatesOnMoveEndAttacker; + } + break; } break; case ABILITYEFFECT_MOVE_END_OTHER: // Abilities that activate on *another* battler's moveend: Dancer, Soul-Heart, Receiver, Symbiosis @@ -5326,11 +5404,15 @@ u32 IsAbilityOnFieldExcept(u32 battlerId, u32 ability) u32 IsAbilityPreventingEscape(u32 battlerId) { u32 id; - - if (B_GHOSTS_ESCAPE >= GEN_6 && IS_BATTLER_OF_TYPE(battlerId, TYPE_GHOST)) - return 0; - - if ((id = IsAbilityOnOpposingSide(battlerId, ABILITY_SHADOW_TAG)) && gBattleMons[battlerId].ability != ABILITY_SHADOW_TAG) + #if B_GHOSTS_ESCAPE >= GEN_6 + if (IS_BATTLER_OF_TYPE(battlerId, TYPE_GHOST)) + return 0; + #endif + #if B_SHADOW_TAG_ESCAPE >= GEN_4 + if ((id = IsAbilityOnOpposingSide(battlerId, ABILITY_SHADOW_TAG)) && gBattleMons[battlerId].ability != ABILITY_SHADOW_TAG) + #else + if (id = IsAbilityOnOpposingSide(battlerId, ABILITY_SHADOW_TAG)) + #endif return id; if ((id = IsAbilityOnOpposingSide(battlerId, ABILITY_ARENA_TRAP)) && IsBattlerGrounded(battlerId)) return id; @@ -5380,6 +5462,100 @@ enum ITEM_STATS_CHANGE, // 5 }; +bool32 IsBattlerTerrainAffected(u8 battlerId, u32 terrainFlag) +{ + if (!(gFieldStatuses & terrainFlag)) + return FALSE; + else if (gStatuses3[battlerId] & STATUS3_SEMI_INVULNERABLE) + return FALSE; + + return IsBattlerGrounded(battlerId); +} + +bool32 CanSleep(u8 battlerId) +{ + u16 ability = GetBattlerAbility(battlerId); + if (ability == ABILITY_INSOMNIA + || ability == ABILITY_VITAL_SPIRIT + || ability == ABILITY_COMATOSE + || gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_SAFEGUARD + || gBattleMons[battlerId].status1 & STATUS1_ANY + || IsAbilityOnSide(battlerId, ABILITY_SWEET_VEIL) + || IsAbilityStatusProtected(battlerId) + || IsBattlerTerrainAffected(battlerId, STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_MISTY_TERRAIN)) + return FALSE; + return TRUE; +} + +bool32 CanBePoisoned(u8 battlerId) +{ + u16 ability = GetBattlerAbility(battlerId); + if (IS_BATTLER_OF_TYPE(battlerId, TYPE_POISON) + || IS_BATTLER_OF_TYPE(battlerId, TYPE_STEEL) + || gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_SAFEGUARD + || gBattleMons[battlerId].status1 & STATUS1_ANY + || ability == ABILITY_IMMUNITY + || ability == ABILITY_COMATOSE + || gBattleMons[battlerId].status1 & STATUS1_ANY + || IsAbilityStatusProtected(battlerId) + || IsBattlerTerrainAffected(battlerId, STATUS_FIELD_MISTY_TERRAIN)) + return FALSE; + return TRUE; +} + +bool32 CanBeBurned(u8 battlerId) +{ + u16 ability = GetBattlerAbility(battlerId); + if (IS_BATTLER_OF_TYPE(battlerId, TYPE_FIRE) + || gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_SAFEGUARD + || gBattleMons[battlerId].status1 & STATUS1_ANY + || ability == ABILITY_WATER_VEIL + || ability == ABILITY_WATER_BUBBLE + || ability == ABILITY_COMATOSE + || IsAbilityStatusProtected(battlerId) + || IsBattlerTerrainAffected(battlerId, STATUS_FIELD_MISTY_TERRAIN)) + return FALSE; + return TRUE; +} + +bool32 CanBeParalyzed(u8 battlerId) +{ + u16 ability = GetBattlerAbility(battlerId); + if ((B_PARALYZE_ELECTRIC >= GEN_6 && IS_BATTLER_OF_TYPE(battlerId, TYPE_ELECTRIC)) + || gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_SAFEGUARD + || ability == ABILITY_LIMBER + || ability == ABILITY_COMATOSE + || gBattleMons[battlerId].status1 & STATUS1_ANY + || IsAbilityStatusProtected(battlerId) + || IsBattlerTerrainAffected(battlerId, STATUS_FIELD_MISTY_TERRAIN)) + return FALSE; + return TRUE; +} + +bool32 CanBeFrozen(u8 battlerId) +{ + u16 ability = GetBattlerAbility(battlerId); + if (IS_BATTLER_OF_TYPE(battlerId, TYPE_ICE) + || (WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_SUN_ANY) + || gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_SAFEGUARD + || ability == ABILITY_MAGMA_ARMOR + || ability == ABILITY_COMATOSE + || gBattleMons[battlerId].status1 & STATUS1_ANY + || IsAbilityStatusProtected(battlerId) + || IsBattlerTerrainAffected(battlerId, STATUS_FIELD_MISTY_TERRAIN)) + return FALSE; + return TRUE; +} + +bool32 CanBeConfused(u8 battlerId) +{ + if (GetBattlerAbility(gEffectBattler) == ABILITY_OWN_TEMPO + || gBattleMons[gEffectBattler].status2 & STATUS2_CONFUSION + || IsBattlerTerrainAffected(battlerId, STATUS_FIELD_MISTY_TERRAIN)) + return FALSE; + return TRUE; +} + // second argument is 1/X of current hp compared to max hp bool32 HasEnoughHpToEatBerry(u32 battlerId, u32 hpFraction, u32 itemId) { @@ -5443,11 +5619,9 @@ static u8 HealConfuseBerry(u32 battlerId, u32 itemId, u8 flavorId, bool32 end2) static u8 StatRaiseBerry(u32 battlerId, u32 itemId, u32 statId, bool32 end2) { - if (gBattleMons[battlerId].statStages[statId] < MAX_STAT_STAGE && HasEnoughHpToEatBerry(battlerId, GetBattlerHoldEffectParam(battlerId), itemId)) + if (CompareStat(battlerId, statId, MAX_STAT_STAGE, CMP_LESS_THAN) && HasEnoughHpToEatBerry(battlerId, GetBattlerHoldEffectParam(battlerId), itemId)) { - PREPARE_STAT_BUFFER(gBattleTextBuff1, statId); - PREPARE_STRING_BUFFER(gBattleTextBuff2, STRINGID_STATROSE); - + BufferStatChange(battlerId, statId, STRINGID_STATROSE); gEffectBattler = battlerId; if (GetBattlerAbility(battlerId) == ABILITY_RIPEN) SET_STATCHANGER(statId, 2, FALSE); @@ -5474,10 +5648,11 @@ static u8 StatRaiseBerry(u32 battlerId, u32 itemId, u32 statId, bool32 end2) static u8 RandomStatRaiseBerry(u32 battlerId, u32 itemId, bool32 end2) { s32 i; + u16 stringId; for (i = 0; i < 5; i++) { - if (gBattleMons[battlerId].statStages[STAT_ATK + i] < MAX_STAT_STAGE) + if (CompareStat(battlerId, STAT_ATK + i, MAX_STAT_STAGE, CMP_LESS_THAN)) break; } if (i != 5 && HasEnoughHpToEatBerry(battlerId, GetBattlerHoldEffectParam(battlerId), itemId)) @@ -5485,21 +5660,19 @@ static u8 RandomStatRaiseBerry(u32 battlerId, u32 itemId, bool32 end2) do { i = Random() % 5; - } while (gBattleMons[battlerId].statStages[STAT_ATK + i] == MAX_STAT_STAGE); + } while (!CompareStat(battlerId, STAT_ATK + i, MAX_STAT_STAGE, CMP_LESS_THAN)); PREPARE_STAT_BUFFER(gBattleTextBuff1, i + 1); - + stringId = (GetBattlerAbility(battlerId) == ABILITY_CONTRARY) ? STRINGID_STATFELL : STRINGID_STATROSE; gBattleTextBuff2[0] = B_BUFF_PLACEHOLDER_BEGIN; gBattleTextBuff2[1] = B_BUFF_STRING; gBattleTextBuff2[2] = STRINGID_STATSHARPLY; gBattleTextBuff2[3] = STRINGID_STATSHARPLY >> 8; gBattleTextBuff2[4] = B_BUFF_STRING; - gBattleTextBuff2[5] = STRINGID_STATROSE; - gBattleTextBuff2[6] = STRINGID_STATROSE >> 8; + gBattleTextBuff2[5] = stringId; + gBattleTextBuff2[6] = stringId >> 8; gBattleTextBuff2[7] = EOS; - gEffectBattler = battlerId; - if (GetBattlerAbility(battlerId) == ABILITY_RIPEN) SET_STATCHANGER(i + 1, 4, FALSE); else @@ -5507,7 +5680,6 @@ static u8 RandomStatRaiseBerry(u32 battlerId, u32 itemId, bool32 end2) gBattleScripting.animArg1 = 0x21 + i + 6; gBattleScripting.animArg2 = 0; - if (end2) { BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); @@ -5517,6 +5689,7 @@ static u8 RandomStatRaiseBerry(u32 battlerId, u32 itemId, bool32 end2) BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BerryStatRaiseRet; } + return ITEM_STATS_CHANGE; } return 0; @@ -5546,13 +5719,11 @@ static u8 DamagedStatBoostBerryEffect(u8 battlerId, u8 statId, u8 split) { if (IsBattlerAlive(battlerId) && TARGET_TURN_DAMAGED - && gBattleMons[battlerId].statStages[statId] < MAX_STAT_STAGE + && CompareStat(battlerId, statId, MAX_STAT_STAGE, CMP_LESS_THAN) && !DoesSubstituteBlockMove(gBattlerAttacker, battlerId, gCurrentMove) && GetBattleMoveSplit(gCurrentMove) == split) { - - PREPARE_STAT_BUFFER(gBattleTextBuff1, statId); - PREPARE_STRING_BUFFER(gBattleTextBuff2, STRINGID_STATROSE); + BufferStatChange(battlerId, statId, STRINGID_STATROSE); gEffectBattler = battlerId; if (GetBattlerAbility(battlerId) == ABILITY_RIPEN) @@ -5569,6 +5740,30 @@ static u8 DamagedStatBoostBerryEffect(u8 battlerId, u8 statId, u8 split) return 0; } +u8 TryHandleSeed(u8 battler, u32 terrainFlag, u8 statId, u16 itemId, bool32 execute) +{ + if (gFieldStatuses & terrainFlag && CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN)) + { + BufferStatChange(battler, statId, STRINGID_STATROSE); + gLastUsedItem = itemId; // For surge abilities + gEffectBattler = gBattleScripting.battler = battler; + SET_STATCHANGER(statId, 1, FALSE); + gBattleScripting.animArg1 = 0xE + statId; + gBattleScripting.animArg2 = 0; + if (execute) + { + BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); + } + else + { + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_BerryStatRaiseRet; + } + return ITEM_STATS_CHANGE; + } + return 0; +} + static u8 ItemHealHp(u32 battlerId, u32 itemId, bool32 end2, bool32 percentHeal) { if (HasEnoughHpToEatBerry(battlerId, 2, itemId)) @@ -5604,6 +5799,61 @@ static bool32 UnnerveOn(u32 battlerId, u32 itemId) return FALSE; } +static bool32 GetMentalHerbEffect(u8 battlerId) +{ + bool32 ret = FALSE; + + // Check infatuation + if (gBattleMons[battlerId].status2 & STATUS2_INFATUATION) + { + gBattleMons[battlerId].status2 &= ~(STATUS2_INFATUATION); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_INFATUATION; // STRINGID_TARGETGOTOVERINFATUATION + StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn); + ret = TRUE; + } + #if B_MENTAL_HERB >= GEN_5 + // Check taunt + if (gDisableStructs[battlerId].tauntTimer != 0) + { + gDisableStructs[battlerId].tauntTimer = gDisableStructs[battlerId].tauntTimer2 = 0; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_TAUNT; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_TAUNT); + ret = TRUE; + } + // Check encore + if (gDisableStructs[battlerId].encoreTimer != 0) + { + gDisableStructs[battlerId].encoredMove = 0; + gDisableStructs[battlerId].encoreTimerStartValue = gDisableStructs[battlerId].encoreTimer = 0; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_ENCORE; // STRINGID_PKMNENCOREENDED + ret = TRUE; + } + // Check torment + if (gBattleMons[battlerId].status2 & STATUS2_TORMENT) + { + gBattleMons[battlerId].status2 &= ~(STATUS2_TORMENT); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_TORMENT; + ret = TRUE; + } + // Check heal block + if (gStatuses3[battlerId] & STATUS3_HEAL_BLOCK) + { + gStatuses3[battlerId] &= ~(STATUS3_HEAL_BLOCK); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_HEALBLOCK; + ret = TRUE; + } + // Check disable + if (gDisableStructs[battlerId].disableTimer != 0) + { + gDisableStructs[battlerId].disableTimer = gDisableStructs[battlerId].disableTimerStartValue = 0; + gDisableStructs[battlerId].disabledMove = 0; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_DISABLE; + ret = TRUE; + } + #endif + return ret; +} + u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) { int i = 0, moveType; @@ -5803,6 +6053,49 @@ u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) BattleScriptPushCursorAndCallback(BattleScript_AirBaloonMsgIn); RecordItemEffectBattle(battlerId, HOLD_EFFECT_AIR_BALLOON); break; + case HOLD_EFFECT_ROOM_SERVICE: + if (TryRoomService(battlerId)) + { + BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); + effect = ITEM_STATS_CHANGE; + } + break; + case HOLD_EFFECT_SEEDS: + switch (GetBattlerHoldEffectParam(battlerId)) + { + case HOLD_EFFECT_PARAM_ELECTRIC_TERRAIN: + effect = TryHandleSeed(battlerId, STATUS_FIELD_ELECTRIC_TERRAIN, STAT_DEF, gLastUsedItem, TRUE); + break; + case HOLD_EFFECT_PARAM_GRASSY_TERRAIN: + effect = TryHandleSeed(battlerId, STATUS_FIELD_GRASSY_TERRAIN, STAT_DEF, gLastUsedItem, TRUE); + break; + case HOLD_EFFECT_PARAM_MISTY_TERRAIN: + effect = TryHandleSeed(battlerId, STATUS_FIELD_MISTY_TERRAIN, STAT_SPDEF, gLastUsedItem, TRUE); + break; + case HOLD_EFFECT_PARAM_PSYCHIC_TERRAIN: + effect = TryHandleSeed(battlerId, STATUS_FIELD_PSYCHIC_TERRAIN, STAT_SPDEF, gLastUsedItem, TRUE); + break; + } + break; + case HOLD_EFFECT_EJECT_PACK: + if (gProtectStructs[battlerId].statFell + && gProtectStructs[battlerId].disableEjectPack == 0 + && !(gCurrentMove == MOVE_PARTING_SHOT && CanBattlerSwitch(gBattlerAttacker))) // Does not activate if attacker used Parting Shot and can switch out + { + gProtectStructs[battlerId].statFell = FALSE; + gActiveBattler = gBattleScripting.battler = battlerId; + effect = ITEM_STATS_CHANGE; + if (moveTurn) + { + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_EjectPackActivate_Ret; + } + else + { + BattleScriptExecute(BattleScript_EjectPackActivate_End2); + } + } + break; } if (effect) @@ -5895,7 +6188,20 @@ u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) break; case HOLD_EFFECT_BLACK_SLUDGE: if (IS_BATTLER_OF_TYPE(battlerId, TYPE_POISON)) + { goto LEFTOVERS; + } + else if (GetBattlerAbility(battlerId) != ABILITY_MAGIC_GUARD && !moveTurn) + { + gBattleMoveDamage = gBattleMons[battlerId].maxHP / 8; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + BattleScriptExecute(BattleScript_ItemHurtEnd2); + effect = ITEM_HP_CHANGE; + RecordItemEffectBattle(battlerId, battlerHoldEffect); + PREPARE_ITEM_BUFFER(gBattleTextBuff1, gLastUsedItem); + } + break; case HOLD_EFFECT_LEFTOVERS: LEFTOVERS: if (gBattleMons[battlerId].hp < gBattleMons[battlerId].maxHP && !moveTurn) @@ -6055,13 +6361,12 @@ u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) effect = ITEM_STATUS_CHANGE; } break; - case HOLD_EFFECT_CURE_ATTRACT: - if (gBattleMons[battlerId].status2 & STATUS2_INFATUATION) + case HOLD_EFFECT_MENTAL_HERB: + if (GetMentalHerbEffect(battlerId)) { - gBattleMons[battlerId].status2 &= ~(STATUS2_INFATUATION); - StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn); - BattleScriptExecute(BattleScript_BerryCureChosenStatusEnd2); - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CURED_PROBLEM; + gBattleScripting.savedBattler = gBattlerAttacker; + gBattlerAttacker = battlerId; + BattleScriptExecute(BattleScript_MentalHerbCureEnd2); effect = ITEM_EFFECT_OTHER; } break; @@ -6206,14 +6511,13 @@ u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) effect = ITEM_EFFECT_OTHER; } break; - case HOLD_EFFECT_CURE_ATTRACT: - if (gBattleMons[battlerId].status2 & STATUS2_INFATUATION) + case HOLD_EFFECT_MENTAL_HERB: + if (GetMentalHerbEffect(battlerId)) { - gBattleMons[battlerId].status2 &= ~(STATUS2_INFATUATION); - StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn); + gBattleScripting.savedBattler = gBattlerAttacker; + gBattlerAttacker = battlerId; BattleScriptPushCursor(); - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CURED_PROBLEM; - gBattlescriptCurrInstr = BattleScript_BerryCureChosenStatusRet; + gBattlescriptCurrInstr = BattleScript_MentalHerbCureRet; effect = ITEM_EFFECT_OTHER; } break; @@ -6285,45 +6589,91 @@ u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) } } break; - case ITEMEFFECT_KINGSROCK_SHELLBELL: - if (gBattleMoveDamage) + case ITEMEFFECT_KINGSROCK: + // Occur on each hit of a multi-strike move + switch (atkHoldEffect) { - switch (atkHoldEffect) + case HOLD_EFFECT_FLINCH: + if (gBattleMoveDamage != 0 // Need to have done damage + && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && TARGET_TURN_DAMAGED + && (Random() % 100) < atkHoldEffectParam + && gBattleMoves[gCurrentMove].flags & FLAG_KINGS_ROCK_AFFECTED + && gBattleMons[gBattlerTarget].hp) { - case HOLD_EFFECT_FLINCH: - if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) - && TARGET_TURN_DAMAGED - && (Random() % 100) < atkHoldEffectParam - && gBattleMoves[gCurrentMove].flags & FLAG_KINGS_ROCK_AFFECTED - && gBattleMons[gBattlerTarget].hp) - { - gBattleScripting.moveEffect = MOVE_EFFECT_FLINCH; - BattleScriptPushCursor(); - SetMoveEffect(FALSE, 0); - BattleScriptPop(); - } - break; - case HOLD_EFFECT_SHELL_BELL: - if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) - && gSpecialStatuses[gBattlerTarget].dmg != 0 - && gSpecialStatuses[gBattlerTarget].dmg != 0xFFFF - && gBattlerAttacker != gBattlerTarget - && gBattleMons[gBattlerAttacker].hp != gBattleMons[gBattlerAttacker].maxHP - && gBattleMons[gBattlerAttacker].hp != 0) - { - gLastUsedItem = atkItem; - gPotentialItemEffectBattler = gBattlerAttacker; - gBattleScripting.battler = gBattlerAttacker; - gBattleMoveDamage = (gSpecialStatuses[gBattlerTarget].dmg / atkHoldEffectParam) * -1; - if (gBattleMoveDamage == 0) - gBattleMoveDamage = -1; - gSpecialStatuses[gBattlerTarget].dmg = 0; - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_ItemHealHP_Ret; - effect++; - } - break; + gBattleScripting.moveEffect = MOVE_EFFECT_FLINCH; + BattleScriptPushCursor(); + SetMoveEffect(FALSE, 0); + BattleScriptPop(); } + break; + case HOLD_EFFECT_BLUNDER_POLICY: + if (gBattleStruct->blunderPolicy + && gBattleMons[gBattlerAttacker].hp != 0 + && CompareStat(gBattlerAttacker, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN)) + { + gBattleStruct->blunderPolicy = FALSE; + gLastUsedItem = atkItem; + gBattleScripting.statChanger = SET_STATCHANGER(STAT_SPEED, 2, FALSE); + effect = ITEM_STATS_CHANGE; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_AttackerItemStatRaise; + } + break; + } + break; + case ITEMEFFECT_LIFEORB_SHELLBELL: + // Occur after the final hit of a multi-strike move + switch (atkHoldEffect) + { + case HOLD_EFFECT_SHELL_BELL: + if (gSpecialStatuses[gBattlerAttacker].damagedMons // Need to have done damage + && gBattlerAttacker != gBattlerTarget + && gBattleMons[gBattlerAttacker].hp != gBattleMons[gBattlerAttacker].maxHP + && gBattleMons[gBattlerAttacker].hp != 0) + { + gLastUsedItem = atkItem; + gPotentialItemEffectBattler = gBattlerAttacker; + gBattleScripting.battler = gBattlerAttacker; + gBattleMoveDamage = (gSpecialStatuses[gBattlerTarget].dmg / atkHoldEffectParam) * -1; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = -1; + gSpecialStatuses[gBattlerTarget].dmg = 0; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_ItemHealHP_Ret; + effect = ITEM_HP_CHANGE; + } + break; + case HOLD_EFFECT_LIFE_ORB: + if (IsBattlerAlive(gBattlerAttacker) + && !(TestSheerForceFlag(gBattlerAttacker, gCurrentMove)) + && GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD + && gSpecialStatuses[gBattlerAttacker].damagedMons) + { + gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 10; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + effect = ITEM_HP_CHANGE; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_ItemHurtRet; + gLastUsedItem = gBattleMons[gBattlerAttacker].item; + } + break; + case HOLD_EFFECT_THROAT_SPRAY: // Does NOT need to be a damaging move + if (gProtectStructs[gBattlerAttacker].targetAffected + && gBattleMons[gBattlerAttacker].hp != 0 + && gBattleMoves[gCurrentMove].flags & FLAG_SOUND + && CompareStat(gBattlerAttacker, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN) + && !NoAliveMonsForEitherParty()) // Don't activate if battle will end + { + gLastUsedItem = atkItem; + gBattleScripting.battler = gBattlerAttacker; + gBattleScripting.statChanger = SET_STATCHANGER(STAT_SPATK, 1, FALSE); + effect = ITEM_STATS_CHANGE; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_AttackerItemStatRaise; + } + break; } break; case ITEMEFFECT_TARGET: @@ -6581,12 +6931,16 @@ u8 GetMoveTarget(u16 move, u8 setTarget) moveTarget = setTarget - 1; else moveTarget = gBattleMoves[move].target; - + + // Special cases + if (move == MOVE_CURSE && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST)) + moveTarget = MOVE_TARGET_USER; + switch (moveTarget) { case MOVE_TARGET_SELECTED: side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE; - if (IsAffectedByFollowMe(gBattlerAttacker, side)) + if (IsAffectedByFollowMe(gBattlerAttacker, side, move)) { targetBattler = gSideTimers[side].followmeTarget; } @@ -6621,7 +6975,7 @@ u8 GetMoveTarget(u16 move, u8 setTarget) break; case MOVE_TARGET_RANDOM: side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE; - if (IsAffectedByFollowMe(gBattlerAttacker, side)) + if (IsAffectedByFollowMe(gBattlerAttacker, side, move)) targetBattler = gSideTimers[side].followmeTarget; else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && moveTarget & MOVE_TARGET_RANDOM) targetBattler = SetRandomTarget(gBattlerAttacker); @@ -6738,7 +7092,7 @@ u8 IsMonDisobedient(void) obedienceLevel = gBattleMons[gBattlerAttacker].level - obedienceLevel; calc = (Random() & 255); - if (calc < obedienceLevel && !(gBattleMons[gBattlerAttacker].status1 & STATUS1_ANY) && gBattleMons[gBattlerAttacker].ability != ABILITY_VITAL_SPIRIT && gBattleMons[gBattlerAttacker].ability != ABILITY_INSOMNIA) + if (calc < obedienceLevel && CanSleep(gBattlerAttacker)) { // try putting asleep int i; @@ -6972,7 +7326,7 @@ static const u16 sWeightToDamageTable[] = }; static const u8 sSpeedDiffPowerTable[] = {40, 60, 80, 120, 150}; -static const u8 sHeatCrushPowerTable[] = {40, 40, 60, 80, 100, 120}; +static const u8 sHeatCrashPowerTable[] = {40, 40, 60, 80, 100, 120}; static const u8 sTrumpCardPowerTable[] = {200, 80, 60, 50, 40}; const struct TypePower gNaturalGiftTable[] = @@ -7144,7 +7498,7 @@ static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef) if (gBattleMons[battlerAtk].pp[i] >= ARRAY_COUNT(sTrumpCardPowerTable)) basePower = sTrumpCardPowerTable[ARRAY_COUNT(sTrumpCardPowerTable) - 1]; else - basePower = sTrumpCardPowerTable[i]; + basePower = sTrumpCardPowerTable[gBattleMons[battlerAtk].pp[i]]; } break; case EFFECT_ACROBATICS: @@ -7167,10 +7521,10 @@ static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef) break; case EFFECT_HEAT_CRASH: weight = GetBattlerWeight(battlerAtk) / GetBattlerWeight(battlerDef); - if (weight >= ARRAY_COUNT(sHeatCrushPowerTable)) - basePower = sHeatCrushPowerTable[ARRAY_COUNT(sHeatCrushPowerTable) - 1]; + if (weight >= ARRAY_COUNT(sHeatCrashPowerTable)) + basePower = sHeatCrashPowerTable[ARRAY_COUNT(sHeatCrashPowerTable) - 1]; else - basePower = sHeatCrushPowerTable[i]; + basePower = sHeatCrashPowerTable[i]; break; case EFFECT_PUNISHMENT: basePower = 60 + (CountBattlerStatIncreases(battlerDef, FALSE) * 20); @@ -7213,6 +7567,29 @@ static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef) if (gBattleMoves[gLastUsedMove].effect == EFFECT_FUSION_COMBO && move != gLastUsedMove) basePower *= 2; break; + case EFFECT_LASH_OUT: + if (gProtectStructs[battlerAtk].statFell) + basePower *= 2; + break; + case EFFECT_EXPLOSION: + if (move == MOVE_MISTY_EXPLOSION && gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN && IsBattlerGrounded(battlerAtk)) + MulModifier(&basePower, UQ_4_12(1.5)); + break; + case EFFECT_DYNAMAX_DOUBLE_DMG: + #ifdef B_DYNAMAX + if (IsDynamaxed(battlerDef)) + basePower *= 2; + #endif + break; + } + + // move-specific base power changes + switch (move) + { + case MOVE_WATER_SHURIKEN: + if (gBattleMons[battlerAtk].species == SPECIES_GRENINJA_ASH) + basePower = 20; + break; } if (basePower == 0) @@ -7478,7 +7855,7 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe if (gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY) MulModifier(&modifier, UQ_4_12(2.0)); break; - case EFFECT_RETALITATE: + case EFFECT_RETALIATE: // todo break; case EFFECT_SOLARBEAM: @@ -7539,7 +7916,7 @@ static u32 CalcAttackStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, b atkStage = gBattleMons[battlerDef].statStages[STAT_SPATK]; } } - if (gBattleMoves[move].effect == EFFECT_BODY_PRESS) + else if (gBattleMoves[move].effect == EFFECT_BODY_PRESS) { atkStat = gBattleMons[battlerAtk].defense; atkStage = gBattleMons[battlerAtk].statStages[STAT_DEF]; @@ -7856,6 +8233,7 @@ static u32 CalcFinalDmg(u32 dmg, u16 move, u8 battlerAtk, u8 battlerDef, u8 move u32 abilityDef = GetBattlerAbility(battlerDef); u32 defSide = GET_BATTLER_SIDE(battlerDef); u16 finalModifier = UQ_4_12(1.0); + u16 itemDef = gBattleMons[battlerDef].item; // check multiple targets in double battle if (GetMoveTargetCount(move, battlerAtk, battlerDef) >= 2) @@ -7976,7 +8354,8 @@ static u32 CalcFinalDmg(u32 dmg, u16 move, u8 battlerAtk, u8 battlerDef, u8 move // berries reducing dmg case HOLD_EFFECT_RESIST_BERRY: if (moveType == GetBattlerHoldEffectParam(battlerDef) - && (moveType == TYPE_NORMAL || typeEffectivenessModifier >= UQ_4_12(2.0))) + && (moveType == TYPE_NORMAL || typeEffectivenessModifier >= UQ_4_12(2.0)) + && !UnnerveOn(battlerDef, itemDef)) { if (abilityDef == ABILITY_RIPEN) MulModifier(&finalModifier, UQ_4_12(0.25)); @@ -8129,16 +8508,18 @@ static u16 CalcTypeEffectivenessMultiplierInternal(u16 move, u8 moveType, u8 bat modifier = UQ_4_12(1.0); } - if (GetBattlerAbility(battlerDef) == ABILITY_WONDER_GUARD && modifier <= UQ_4_12(1.0) && gBattleMoves[move].power) + if (((GetBattlerAbility(battlerDef) == ABILITY_WONDER_GUARD && modifier <= UQ_4_12(1.0)) + || (GetBattlerAbility(battlerDef) == ABILITY_TELEPATHY && battlerDef == BATTLE_PARTNER(battlerAtk))) + && gBattleMoves[move].power) { modifier = UQ_4_12(0.0); if (recordAbilities) { - gLastUsedAbility = ABILITY_WONDER_GUARD; + gLastUsedAbility = gBattleMons[battlerDef].ability; gMoveResultFlags |= MOVE_RESULT_MISSED; gLastLandedMoves[battlerDef] = 0; gBattleCommunication[MISS_TYPE] = B_MSG_AVOIDED_DMG; - RecordAbilityBattle(battlerDef, ABILITY_WONDER_GUARD); + RecordAbilityBattle(battlerDef, gBattleMons[battlerDef].ability); } } @@ -8375,6 +8756,9 @@ void UndoFormChange(u32 monId, u32 side, bool32 isSwitchingOut) {SPECIES_MINIOR_METEOR_VIOLET, SPECIES_MINIOR_CORE_VIOLET}, {SPECIES_MINIOR_METEOR_YELLOW, SPECIES_MINIOR_CORE_YELLOW}, {SPECIES_WISHIWASHI_SCHOOL, SPECIES_WISHIWASHI}, + {SPECIES_CRAMORANT_GORGING, SPECIES_CRAMORANT}, + {SPECIES_CRAMORANT_GULPING, SPECIES_CRAMORANT}, + {SPECIES_GRENINJA_ASH, SPECIES_GRENINJA_BATTLE_BOND}, }; if (isSwitchingOut) // Don't revert Mimikyu Busted when switching out @@ -8492,6 +8876,7 @@ bool32 SetIllusionMon(struct Pokemon *mon, u32 battlerId) id = i; if (GetMonData(&party[id], MON_DATA_SANITY_HAS_SPECIES) && GetMonData(&party[id], MON_DATA_HP) + && !GetMonData(&party[id], MON_DATA_IS_EGG) && &party[id] != mon && &party[id] != partnerMon) { @@ -8729,14 +9114,6 @@ void SortBattlersBySpeed(u8 *battlers, bool8 slowToFast) } } -bool32 TestSheerForceFlag(u8 battler, u16 move) -{ - if (GetBattlerAbility(battler) == ABILITY_SHEER_FORCE && gBattleMoves[move].flags & FLAG_SHEER_FORCE_BOOST) - return TRUE; - else - return FALSE; -} - void TryRestoreStolenItems(void) { u32 i; @@ -8807,3 +9184,176 @@ void TrySaveExchangedItem(u8 battlerId, u16 stolenItem) #endif } +bool32 IsBattlerAffectedByHazards(u8 battlerId, bool32 toxicSpikes) +{ + bool32 ret = TRUE; + u32 holdEffect = GetBattlerHoldEffect(gActiveBattler, TRUE); + if (toxicSpikes && holdEffect == HOLD_EFFECT_HEAVY_DUTY_BOOTS && !IS_BATTLER_OF_TYPE(battlerId, TYPE_POISON)) + { + ret = FALSE; + RecordItemEffectBattle(battlerId, holdEffect); + } + else if (holdEffect == HOLD_EFFECT_HEAVY_DUTY_BOOTS) + { + ret = FALSE; + RecordItemEffectBattle(battlerId, holdEffect); + } + return ret; +} + +bool32 TestSheerForceFlag(u8 battler, u16 move) +{ + if (GetBattlerAbility(battler) == ABILITY_SHEER_FORCE && gBattleMoves[move].flags & FLAG_SHEER_FORCE_BOOST) + return TRUE; + else + return FALSE; +} + +// This function is the body of "jumpifstat", but can be used dynamically in a function +bool32 CompareStat(u8 battlerId, u8 statId, u8 cmpTo, u8 cmpKind) +{ + bool8 ret = FALSE; + u8 statValue = gBattleMons[battlerId].statStages[statId]; + + // Because this command is used as a way of checking if a stat can be lowered/raised, + // we need to do some modification at run-time. + if (GetBattlerAbility(battlerId) == ABILITY_CONTRARY) + { + if (cmpKind == CMP_GREATER_THAN) + cmpKind = CMP_LESS_THAN; + else if (cmpKind == CMP_LESS_THAN) + cmpKind = CMP_GREATER_THAN; + + if (cmpTo == MIN_STAT_STAGE) + cmpTo = MAX_STAT_STAGE; + else if (cmpTo == MAX_STAT_STAGE) + cmpTo = MIN_STAT_STAGE; + } + + switch (cmpKind) + { + case CMP_EQUAL: + if (statValue == cmpTo) + ret = TRUE; + break; + case CMP_NOT_EQUAL: + if (statValue != cmpTo) + ret = TRUE; + break; + case CMP_GREATER_THAN: + if (statValue > cmpTo) + ret = TRUE; + break; + case CMP_LESS_THAN: + if (statValue < cmpTo) + ret = TRUE; + break; + case CMP_COMMON_BITS: + if (statValue & cmpTo) + ret = TRUE; + break; + case CMP_NO_COMMON_BITS: + if (!(statValue & cmpTo)) + ret = TRUE; + break; + } + + return ret; +} + +void BufferStatChange(u8 battlerId, u8 statId, u8 stringId) +{ + bool8 hasContrary = (GetBattlerAbility(battlerId) == ABILITY_CONTRARY); + + PREPARE_STAT_BUFFER(gBattleTextBuff1, statId); + if (stringId == STRINGID_STATFELL) + { + if (hasContrary) + PREPARE_STRING_BUFFER(gBattleTextBuff2, STRINGID_STATROSE) + else + PREPARE_STRING_BUFFER(gBattleTextBuff2, STRINGID_STATFELL) + } + else if (stringId == STRINGID_STATROSE) + { + if (hasContrary) + PREPARE_STRING_BUFFER(gBattleTextBuff2, STRINGID_STATFELL) + else + PREPARE_STRING_BUFFER(gBattleTextBuff2, STRINGID_STATROSE) + } + else + { + PREPARE_STRING_BUFFER(gBattleTextBuff2, stringId) + } +} + +bool32 TryRoomService(u8 battlerId) +{ + if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && CompareStat(battlerId, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN)) + { + BufferStatChange(battlerId, STAT_SPEED, STRINGID_STATFELL); + gEffectBattler = gBattleScripting.battler = battlerId; + SET_STATCHANGER(STAT_SPEED, 1, TRUE); + gBattleScripting.animArg1 = 0xE + STAT_SPEED; + gBattleScripting.animArg2 = 0; + gLastUsedItem = gBattleMons[battlerId].item; + return TRUE; + } + else + { + return FALSE; + } +} + +void DoBurmyFormChange(u32 monId) +{ + u16 newSpecies, currSpecies; + s32 sentIn; + struct Pokemon *party = gPlayerParty; + + sentIn = gSentPokesToOpponent[(gBattlerFainted & 2) >> 1]; + currSpecies = GetMonData(&party[monId], MON_DATA_SPECIES, NULL); + + if ((GET_BASE_SPECIES_ID(currSpecies) == SPECIES_BURMY) && (gBitTable[monId] & sentIn)) + { + switch (gBattleTerrain) + { + case BATTLE_TERRAIN_GRASS: + case BATTLE_TERRAIN_LONG_GRASS: + case BATTLE_TERRAIN_POND: + case BATTLE_TERRAIN_MOUNTAIN: + case BATTLE_TERRAIN_PLAIN: + newSpecies = SPECIES_BURMY; + break; + case BATTLE_TERRAIN_CAVE: + case BATTLE_TERRAIN_SAND: + newSpecies = SPECIES_BURMY_SANDY_CLOAK; + break; + case BATTLE_TERRAIN_BUILDING: + newSpecies = SPECIES_BURMY_TRASH_CLOAK; + break; + default: // Don't change form if last battle was water-related + newSpecies = SPECIES_NONE; + break; + } + + if (newSpecies != SPECIES_NONE) + { + SetMonData(&party[monId], MON_DATA_SPECIES, &newSpecies); + CalculateMonStats(&party[monId]); + } + } +} + +bool32 BlocksPrankster(u16 move, u8 battlerPrankster, u8 battlerDef) +{ + #if B_PRANKSTER_DARK_TYPES >= GEN_7 + if (gProtectStructs[battlerPrankster].pranksterElevated + && GetBattlerSide(battlerPrankster) != GetBattlerSide(battlerDef) + && !(gBattleMoves[gCurrentMove].target & (MOVE_TARGET_OPPONENTS_FIELD | MOVE_TARGET_DEPENDS)) // Don't block hazards, assist-type moves + && IS_BATTLER_OF_TYPE(battlerDef, TYPE_DARK) // Only Dark-types can block Prankster'd + && !(gStatuses3[battlerDef] & STATUS3_SEMI_INVULNERABLE)) + return TRUE; + else + #endif + return FALSE; +} diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index 98b30951e..7e745fb30 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -2599,11 +2599,12 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = #if B_UPDATED_MOVE_DATA >= GEN_4 .accuracy = 0, .flags = FLAG_MAKES_CONTACT | FLAG_PROTECT_AFFECTED | FLAG_KINGS_ROCK_AFFECTED, + .effect = EFFECT_RECOIL_HP_25, #else .accuracy = 100, .flags = FLAG_MAKES_CONTACT | FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED, + .effect = EFFECT_RECOIL_25, #endif - .effect = EFFECT_RECOIL_25, .power = 50, .type = TYPE_NORMAL, .pp = 1, @@ -3424,6 +3425,11 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_HEAL_BELL] = { + #if B_UPDATED_MOVE_DATA != GEN_5 + .flags = FLAG_SNATCH_AFFECTED | FLAG_SOUND, + #else + .flags = FLAG_SNATCH_AFFECTED, + #endif .effect = EFFECT_HEAL_BELL, .power = 0, .type = TYPE_NORMAL, @@ -3432,7 +3438,6 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = .secondaryEffectChance = 0, .target = MOVE_TARGET_USER, .priority = 0, - .flags = FLAG_SNATCH_AFFECTED | FLAG_SOUND, .split = SPLIT_STATUS, }, @@ -5338,6 +5343,11 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_HOWL] = { + #if B_UPDATED_MOVE_DATA >= GEN_8 + .flags = FLAG_SNATCH_AFFECTED | FLAG_SOUND, + #else + .flags = FLAG_SNATCH_AFFECTED, + #endif .effect = EFFECT_ATTACK_UP, .power = 0, .type = TYPE_NORMAL, @@ -5346,7 +5356,6 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = .secondaryEffectChance = 0, .target = MOVE_TARGET_USER, .priority = 0, - .flags = FLAG_SNATCH_AFFECTED | FLAG_SOUND, .split = SPLIT_STATUS, }, @@ -8097,7 +8106,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_RETALIATE] = { - .effect = EFFECT_RETALITATE, + .effect = EFFECT_RETALIATE, .power = 70, .type = TYPE_NORMAL, .accuracy = 100, @@ -10226,7 +10235,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_PSYCHIC_FANGS] = { .effect = EFFECT_BRICK_BREAK, - .power = 75, + .power = 85, .type = TYPE_PSYCHIC, .accuracy = 100, .pp = 15, @@ -10743,7 +10752,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_SNIPE_SHOT] = { - .effect = EFFECT_PLACEHOLDER, //TODO + .effect = EFFECT_SNIPE_SHOT, .power = 80, .type = TYPE_WATER, .accuracy = 100, @@ -10751,7 +10760,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = .secondaryEffectChance = 0, .target = MOVE_TARGET_SELECTED, .priority = 0, - .flags = FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED, + .flags = FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_HIGH_CRIT, .split = SPLIT_SPECIAL, }, @@ -10940,7 +10949,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_DECORATE] = { - .effect = EFFECT_PLACEHOLDER, // TODO .. EFFECT_DECORATE + .effect = EFFECT_DECORATE, .power = 0, .type = TYPE_FAIRY, .accuracy = 0, @@ -10996,7 +11005,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_BEHEMOTH_BLADE] = { - .effect = EFFECT_HIT, //TODO: 2x damage if dynamaxed? meh... + .effect = EFFECT_DYNAMAX_DOUBLE_DMG, .power = 100, .type = TYPE_STEEL, .accuracy = 100, @@ -11010,7 +11019,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_BEHEMOTH_BASH] = { - .effect = EFFECT_HIT, //TODO: 2x damage if dynamaxed? meh... + .effect = EFFECT_DYNAMAX_DOUBLE_DMG, .power = 100, .type = TYPE_STEEL, .accuracy = 100, @@ -11136,7 +11145,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_LIFE_DEW] = { - .effect = EFFECT_RESTORE_HP, + .effect = EFFECT_JUNGLE_HEALING, .power = 0, .type = TYPE_WATER, .accuracy = 0, @@ -11150,7 +11159,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_OBSTRUCT] = { - .effect = EFFECT_PLACEHOLDER, //TODO. EFFECT_PROTECT? + .effect = EFFECT_PROTECT, .power = 0, .type = TYPE_DARK, .accuracy = 100, @@ -11158,7 +11167,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = .secondaryEffectChance = 0, .target = MOVE_TARGET_USER, .priority = 4, - .flags = 0, + .flags = FLAG_PROTECTION_MOVE, .split = SPLIT_STATUS, }, @@ -11234,7 +11243,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_STEEL_ROLLER] = { - .effect = EFFECT_PLACEHOLDER, //TODO + .effect = EFFECT_REMOVE_TERRAIN, .power = 130, .type = TYPE_STEEL, .accuracy = 100, @@ -11290,7 +11299,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_MISTY_EXPLOSION] = { - .effect = EFFECT_PLACEHOLDER, //TODO + .effect = EFFECT_EXPLOSION, .power = 100, .type = TYPE_FAIRY, .accuracy = 100, @@ -11304,7 +11313,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_GRASSY_GLIDE] = { - .effect = EFFECT_PLACEHOLDER, //TODO + .effect = EFFECT_GRASSY_GLIDE, .power = 70, .type = TYPE_GRASS, .accuracy = 100, @@ -11360,12 +11369,12 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_BURNING_JEALOUSY] = { - .effect = EFFECT_PLACEHOLDER, //TODO + .effect = EFFECT_BURN_HIT, .power = 70, .type = TYPE_FIRE, .accuracy = 100, .pp = 5, - .secondaryEffectChance = 0, + .secondaryEffectChance = 100, .target = MOVE_TARGET_BOTH, .priority = 0, .flags = FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST, @@ -11374,7 +11383,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_LASH_OUT] = { - .effect = EFFECT_PLACEHOLDER, //TODO + .effect = EFFECT_LASH_OUT, .power = 75, .type = TYPE_DARK, .accuracy = 100, @@ -11416,7 +11425,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_COACHING] = { - .effect = EFFECT_PLACEHOLDER, //TODO + .effect = EFFECT_COACHING, .power = 0, .type = TYPE_FIGHTING, .accuracy = 0, @@ -11444,7 +11453,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_TRIPLE_AXEL] = { - .effect = EFFECT_TRIPLE_KICK, //TODO: Increase damage by 20 instead of 10 + .effect = EFFECT_TRIPLE_KICK, .power = 20, .type = TYPE_ICE, .accuracy = 90, @@ -11486,7 +11495,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_JUNGLE_HEALING] = { - .effect = EFFECT_PLACEHOLDER, //TODO + .effect = EFFECT_JUNGLE_HEALING, .power = 0, .type = TYPE_GRASS, .accuracy = 0, @@ -11513,7 +11522,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_SURGING_STRIKES] = { - .effect = EFFECT_PLACEHOLDER, //TODO (Multi hit + Always Crit) + .effect = EFFECT_TRIPLE_HIT, .power = 25, .type = TYPE_WATER, .accuracy = 100, @@ -11625,7 +11634,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_EERIE_SPELL] = { - .effect = EFFECT_HIT, // To do. It's a copy of Spite that inflicts damage and reduced the target's last move's PP by 3 instead of 4. + .effect = EFFECT_EERIE_SPELL, .power = 80, .type = TYPE_PSYCHIC, .accuracy = 100, diff --git a/src/data/items.h b/src/data/items.h index 370cd2022..ef5c8825d 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -2262,7 +2262,7 @@ const struct Item gItems[] = .name = _("MENTAL HERB"), .itemId = ITEM_MENTAL_HERB, .price = 100, - .holdEffect = HOLD_EFFECT_CURE_ATTRACT, + .holdEffect = HOLD_EFFECT_MENTAL_HERB, .description = sMentalHerbDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, diff --git a/src/pokedex.c b/src/pokedex.c index cc469b32f..9de5e0c74 100644 --- a/src/pokedex.c +++ b/src/pokedex.c @@ -1393,6 +1393,7 @@ static const struct SearchOptionText sDexSearchTypeOptions[NUMBER_OF_MON_TYPES + {gText_DexEmptyString, gTypeNames[TYPE_ICE]}, {gText_DexEmptyString, gTypeNames[TYPE_DRAGON]}, {gText_DexEmptyString, gTypeNames[TYPE_DARK]}, + {gText_DexEmptyString, gTypeNames[TYPE_FAIRY]}, {}, }; @@ -1427,6 +1428,7 @@ static const u8 sDexSearchTypeIds[NUMBER_OF_MON_TYPES] = TYPE_ICE, TYPE_DRAGON, TYPE_DARK, + TYPE_FAIRY, }; // Number pairs are the task data for tracking the cursor pos and scroll offset of each option list