From 46e2a87e2e0cc5d8d98dcd435d0211f64d81da8d Mon Sep 17 00:00:00 2001 From: cawtds <38510667+cawtds@users.noreply.github.com> Date: Sat, 14 Feb 2026 12:54:29 +0100 Subject: [PATCH] Update/battle engine c43dd0c3ca (#111) --- asm/macros/battle_script.inc | 177 +- data/battle_anim_scripts.s | 24 +- data/battle_scripts_1.s | 681 ++-- data/event_scripts.s | 1 + data/scripts/test.inc | 27 +- data/scripts/trainer_battle.inc | 12 +- data/scripts/white_out.inc | 3 +- data/specials.inc | 6 +- include/battle.h | 88 +- include/battle_ai_main.h | 14 +- include/battle_ai_switch.h | 12 +- include/battle_ai_util.h | 394 +- include/battle_anim.h | 2 +- include/battle_anim_scripts.h | 2 + include/battle_bg.h | 1 + include/battle_controllers.h | 331 +- include/battle_dynamax.h | 14 +- include/battle_end_turn.h | 3 +- include/battle_gfx_sfx_util.h | 3 +- include/battle_gimmick.h | 26 +- include/battle_interface.h | 2 +- include/battle_main.h | 24 +- include/battle_message.h | 3 + include/battle_move_resolution.h | 3 +- include/battle_script_commands.h | 41 +- include/battle_scripts.h | 16 +- include/battle_setup.h | 11 +- include/battle_terastal.h | 12 +- include/battle_transition.h | 46 +- include/battle_util.h | 382 +- include/battle_util2.h | 6 +- include/battle_z_move.h | 30 +- include/config/battle.h | 4 + include/constants/battle.h | 21 +- include/constants/battle_anim.h | 19 +- include/constants/battle_end_turn.h | 2 +- include/constants/battle_move_effects.h | 1 - include/constants/battle_move_resolution.h | 74 +- include/constants/battle_script_commands.h | 66 +- include/constants/battle_setup.h | 2 +- include/constants/battle_string_ids.h | 15 +- include/constants/battle_switch_in.h | 1 + include/constants/battle_z_move_effects.h | 62 +- include/constants/berry.h | 15 +- include/constants/field_poison.h | 14 + include/constants/form_change_types.h | 40 +- include/constants/generational_changes.h | 4 + include/constants/global.h | 28 +- include/constants/songs.h | 10 +- include/constants/trainers.h | 15 +- include/data.h | 2 +- include/event_scripts.h | 1 + include/field_message_box.h | 1 + include/field_poison.h | 6 - include/field_specials.h | 1 + include/main.h | 2 +- include/move.h | 42 +- include/party_menu.h | 2 +- include/pokemon.h | 48 +- include/pokemon_icon.h | 3 + include/pokemon_summary_screen.h | 2 +- include/random.h | 2 + include/strings.h | 1 - include/test/battle.h | 24 +- include/test_runner.h | 22 +- include/trainer_slide.h | 14 +- include/type_icons.h | 2 +- src/battle_ai_main.c | 344 +- src/battle_ai_switch.c | 328 +- src/battle_ai_util.c | 642 +-- src/battle_anim.c | 2 + src/battle_anim_effects_3.c | 32 +- src/battle_arena.c | 84 +- src/battle_bg.c | 2 +- src/battle_controller_link_opponent.c | 108 +- src/battle_controller_link_partner.c | 47 +- src/battle_controller_oak_old_man.c | 165 +- src/battle_controller_opponent.c | 103 +- src/battle_controller_player.c | 310 +- src/battle_controller_player_partner.c | 70 +- src/battle_controller_pokedude.c | 120 +- src/battle_controller_recorded_opponent.c | 77 +- src/battle_controller_recorded_partner.c | 55 +- src/battle_controller_recorded_player.c | 80 +- src/battle_controller_safari.c | 52 +- src/battle_controllers.c | 381 +- src/battle_dynamax.c | 64 +- src/battle_end_turn.c | 130 +- src/battle_frontier.c | 9 - src/battle_gfx_sfx_util.c | 300 +- src/battle_gimmick.c | 55 +- src/battle_interface.c | 166 +- src/battle_intro.c | 80 +- src/battle_main.c | 1058 +++-- src/battle_message.c | 311 +- src/battle_move_resolution.c | 2583 ++++++++++-- src/battle_script_commands.c | 1673 +++----- src/battle_setup.c | 430 +- src/battle_special.c | 2 +- src/battle_switch_in.c | 32 +- src/battle_terastal.c | 14 +- src/battle_transition.c | 2 +- src/battle_util.c | 3529 +++++------------ src/battle_util2.c | 174 +- src/battle_z_move.c | 62 +- src/data/battle_move_effects.h | 22 +- src/data/moves_info.h | 63 +- src/data/party_menu.h | 17 +- src/data/pokemon/form_change_tables.h | 362 +- .../pokemon/species_info/gen_9_families.h | 2 +- src/evolution_scene.c | 8 +- src/field_message_box.c | 18 + src/field_poison.c | 32 +- src/field_specials.c | 9 + src/item_menu.c | 16 +- src/item_use.c | 98 +- src/learn_move.c | 2 +- src/party_menu.c | 190 +- src/party_menu_specials.c | 2 +- src/pokemon.c | 483 ++- src/pokemon_icon.c | 56 +- src/pokemon_summary_screen.c | 4 +- src/recorded_battle.c | 29 +- src/region_map.c | 1 - src/reshow_battle_screen.c | 17 +- src/script_pokemon_util.c | 5 +- src/trainer_slide.c | 66 +- src/trainer_tower.c | 2 +- src/type_icons.c | 74 +- src/wild_encounter.c | 2 + test/battle/ability/aerilate.c | 120 +- test/battle/ability/anger_shell.c | 4 +- test/battle/ability/arena_trap.c | 18 +- test/battle/ability/battle_bond.c | 2 +- test/battle/ability/berserk.c | 6 +- test/battle/ability/cheek_pouch.c | 2 +- test/battle/ability/commander.c | 19 + test/battle/ability/corrosion.c | 31 +- test/battle/ability/dancer.c | 1 - test/battle/ability/dazzling.c | 1 - test/battle/ability/desolate_land.c | 4 +- test/battle/ability/disguise.c | 76 +- test/battle/ability/early_bird.c | 47 +- test/battle/ability/electromorphosis.c | 4 +- test/battle/ability/emergency_exit.c | 70 + test/battle/ability/forewarn.c | 82 +- test/battle/ability/frisk.c | 4 +- test/battle/ability/galvanize.c | 69 +- test/battle/ability/gulp_missile.c | 98 +- test/battle/ability/innards_out.c | 5 +- test/battle/ability/magician.c | 2 +- test/battle/ability/mold_breaker.c | 25 + test/battle/ability/neuroforce.c | 3 +- test/battle/ability/normalize.c | 73 +- test/battle/ability/pickpocket.c | 310 +- test/battle/ability/pixilate.c | 105 +- test/battle/ability/primordial_sea.c | 4 +- test/battle/ability/rattled.c | 8 +- test/battle/ability/refrigerate.c | 105 +- test/battle/ability/stamina.c | 2 - test/battle/ability/weak_armor.c | 2 - test/battle/ability/wind_power.c | 8 +- test/battle/ai/ai_check_viability.c | 64 + test/battle/ai/ai_choice.c | 27 + test/battle/ai/ai_doubles.c | 107 +- test/battle/ai/ai_multi.c | 8 +- test/battle/ai/ai_switching.c | 35 +- test/battle/ai/can_use_all_moves.c | 2 +- test/battle/ai/check_bad_move.c | 13 + test/battle/ai/gimmick_z_move.c | 1 - test/battle/form_change/end_battle.c | 41 +- test/battle/form_change/faint.c | 43 + test/battle/form_change/gigantamax.c | 19 + test/battle/form_change/mega_evolution.c | 38 + test/battle/form_change/primal_reversion.c | 56 +- test/battle/form_change/ultra_burst.c | 22 + test/battle/gimmick/dynamax.c | 2 +- test/battle/gimmick/terastal.c | 16 +- test/battle/gimmick/zmove.c | 1 - test/battle/hold_effect/booster_energy.c | 14 - test/battle/hold_effect/restore_hp.c | 24 + test/battle/move_animations/all_anims.c | 4 +- test/battle/move_effect/beak_blast.c | 30 +- test/battle/move_effect/bide.c | 1 + test/battle/move_effect/charge.c | 4 +- test/battle/move_effect/fling.c | 52 +- test/battle/move_effect/foresight.c | 117 +- test/battle/move_effect/gear_up.c | 46 +- test/battle/move_effect/hyperspace_fury.c | 4 - test/battle/move_effect/ivy_cudgel.c | 6 +- test/battle/move_effect/last_resort.c | 33 + test/battle/move_effect/magnetic_flux.c | 46 +- test/battle/move_effect/magnitude.c | 19 + test/battle/move_effect/miracle_eye.c | 104 +- test/battle/move_effect/ohko.c | 45 + test/battle/move_effect/parting_shot.c | 383 +- test/battle/move_effect/protect.c | 211 +- test/battle/move_effect/psych_up.c | 120 +- test/battle/move_effect/role_play.c | 1 - test/battle/move_effect/round.c | 21 + test/battle/move_effect/sheer_cold.c | 86 - test/battle/move_effect/snore.c | 1 + test/battle/move_effect/stockpile.c | 39 + test/battle/move_effect/stomping_tantrum.c | 26 + test/battle/move_effect/synchronoise.c | 42 + test/battle/move_effect/trick.c | 12 + .../move_effects_combined/hyperspace_fury.c | 97 + .../move_has_no_effect_on_same_type.c | 39 + test/battle/volatiles/confusion.c | 31 + test/species.c | 29 +- test/test_runner_battle.c | 71 +- 211 files changed, 12975 insertions(+), 8809 deletions(-) create mode 100644 include/constants/field_poison.h delete mode 100644 test/battle/move_effect/hyperspace_fury.c delete mode 100644 test/battle/move_effect/sheer_cold.c create mode 100644 test/battle/move_effects_combined/hyperspace_fury.c create mode 100644 test/battle/move_flags/move_has_no_effect_on_same_type.c diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index b368c9bd4..7c6dd4899 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -30,10 +30,6 @@ .byte B_SCR_OP_TYPECALC .endm - .macro adjustdamage - .byte B_SCR_OP_ADJUSTDAMAGE - .endm - .macro multihitresultmessage .byte B_SCR_OP_MULTIHITRESULTMESSAGE .endm @@ -363,8 +359,8 @@ .byte B_SCR_OP_WAITSTATE .endm - .macro isdmgblockedbydisguise - .byte B_SCR_OP_ISDMGBLOCKEDBYDISGUISE + .macro tryselfconfusiondmgformchange + .byte B_SCR_OP_TRYSELFCONFUSIONDMGFORMCHANGE .endm .macro return @@ -687,10 +683,6 @@ .byte B_SCR_OP_USEITEMONOPPONENT .endm - .macro unused_0x78 - .byte B_SCR_OP_UNUSED_0X78 - .endm - .macro setprotectlike .byte B_SCR_OP_SETPROTECTLIKE .endm @@ -714,10 +706,6 @@ .4byte \failInstr .endm - .macro trymirrormove - .byte B_SCR_OP_UNUSED_0X7E - .endm - .macro setfieldweather .byte B_SCR_OP_SETFIELDWEATHER .endm @@ -739,14 +727,6 @@ .byte B_SCR_OP_TRYSETREST .endm - .macro unused_0x82 - .byte B_SCR_OP_UNUSED_0X82 - .endm - - .macro unused_0x83 - .byte B_SCR_OP_UNUSED_0X83 - .endm - .macro jumpifuproarwakes jumpInstr:req .byte B_SCR_OP_JUMPIFUPROARWAKES .4byte \jumpInstr @@ -765,14 +745,6 @@ .4byte \failInstr .endm - .macro removestockpilecounters - callnative BS_RemoveStockpileCounters - .endm - - .macro unused_0x88 - .byte B_SCR_OP_UNUSED_0X88 - .endm - .macro statbuffchange battler:req, flags:req, failInstr:req, stats=0 .byte B_SCR_OP_STATBUFFCHANGE .byte \battler @@ -888,22 +860,6 @@ .byte B_SCR_OP_SETCALLEDMOVE .endm - .macro unused_0x9f - .byte B_SCR_OP_UNUSED_0X9F - .endm - - .macro unused_0xA0 - .byte B_SCR_OP_UNUSED_0XA0 - .endm - - .macro unused_0xA1 - .byte B_SCR_OP_UNUSED_0XA1 - .endm - - .macro unused_0xA2 - .byte B_SCR_OP_UNUSED_0XA2 - .endm - .macro disablelastusedattack failInstr:req .byte B_SCR_OP_DISABLELASTUSEDATTACK .4byte \failInstr @@ -933,20 +889,6 @@ .4byte \failInstr .endm - .macro trychoosesleeptalkmove failInstr:req - .byte B_SCR_OP_UNUSED_0XA9 - .4byte \failInstr - .endm - - .macro trysetdestinybond failInstr:req - .byte B_SCR_OP_UNUSED_AA - .4byte \failInstr - .endm - - .macro unused_0xab - .byte B_SCR_OP_UNUSED_0XAB - .endm - .macro settailwind failInstr:req .byte B_SCR_OP_SETTAILWIND .4byte \failInstr @@ -983,20 +925,12 @@ .4byte \failInstr .endm - .macro unused_0xb3 - .byte B_SCR_OP_UNUSED_0XB3 - .endm - .macro jumpifconfusedandstatmaxed stat:req, jumpInstr:req .byte B_SCR_OP_JUMPIFCONFUSEDANDSTATMAXED .byte \stat .4byte \jumpInstr .endm - .macro unused_0xb5 - .byte B_SCR_OP_UNUSED_0XB5 - .endm - .macro setembargo failInstr:req .byte B_SCR_OP_SETEMBARGO .4byte \failInstr @@ -1010,10 +944,6 @@ .byte B_SCR_OP_SETSAFEGUARD .endm - .macro magnitudedamagecalculation - .byte B_SCR_OP_MAGNITUDEDAMAGECALCULATION - .endm - .macro jumpifnopursuitswitchdmg jumpInstr:req .byte B_SCR_OP_JUMPIFNOPURSUITSWITCHDMG .4byte \jumpInstr @@ -1071,18 +1001,6 @@ setsemiinvulnerablebit TRUE .endm - .macro unused_0xC6 - .byte B_SCR_OP_UNUSED_0XC6 - .endm - - .macro unused_0xC7 - .byte B_SCR_OP_UNUSED_0XC7 - .endm - - .macro unused_c8 - .byte B_SCR_OP_UNUSED_C8 - .endm - .macro trymemento failInstr:req .byte B_SCR_OP_TRYMEMENTO .4byte \failInstr @@ -1092,15 +1010,6 @@ .byte B_SCR_OP_SETFORCEDTARGET .endm - .macro unused_0xcb battler:req - .byte B_SCR_OP_UNUSED_0XCB - .byte \battler - .endm - - .macro unused_0xCC - .byte B_SCR_OP_UNUSED_0XCC - .endm - .macro curestatuswithmove failInstr:req .byte B_SCR_OP_CURESTATUSWITHMOVE .4byte \failInstr @@ -1111,10 +1020,6 @@ .4byte \failInstr .endm - .macro unused_0xcf - .byte B_SCR_OP_UNUSED_0XCF - .endm - .macro settaunt failInstr:req .byte B_SCR_OP_SETTAUNT .4byte \failInstr @@ -1156,10 +1061,6 @@ .4byte \failInstr .endm - .macro Cmd_unused0xd8 - .byte B_SCR_OP_UNUSED0XD8 - .endm - .macro setroom .byte B_SCR_OP_SETROOM .endm @@ -1186,11 +1087,6 @@ .4byte \failInstr .endm - .macro assistattackselect failInstr:req - .byte B_SCR_OP_UNUSED_0XDE - .4byte \failInstr - .endm - .macro trysetmagiccoat failInstr:req .byte B_SCR_OP_TRYSETMAGICCOAT .4byte \failInstr @@ -1201,11 +1097,6 @@ .4byte \failInstr .endm - .macro unused2 ptr:req - .byte B_SCR_OP_UNUSED2 - .4byte \ptr - .endm - .macro switchoutabilities battler:req .byte B_SCR_OP_SWITCHOUTABILITIES .byte \battler @@ -1217,22 +1108,10 @@ .4byte \jumpInstr .endm - .macro unused_0xE4 - .byte B_SCR_OP_UNUSED_0XE4 - .endm - .macro pickup .byte B_SCR_OP_PICKUP .endm - .macro unused_0xE6 - .byte B_SCR_OP_UNUSED_0XE6 - .endm - - .macro unused_0xE7 - .byte B_SCR_OP_UNUSED_0XE7 - .endm - .macro settypebasedhalvers failInstr:req .byte B_SCR_OP_SETTYPEBASEDHALVERS .4byte \failInstr @@ -1262,10 +1141,6 @@ .byte B_SCR_OP_SNATCHSETBATTLERS .endm - .macro unused_0xee - .byte B_SCR_OP_UNUSED_0XEE - .endm - .macro handleballthrow .byte B_SCR_OP_HANDLEBALLTHROW .endm @@ -1452,24 +1327,6 @@ .4byte \ptr .endm - .macro handlemegaevo battler:req, case:req - callnative BS_HandleMegaEvolution - .byte \battler - .byte \case - .endm - - .macro handleprimalreversion battler:req, case:req - callnative BS_HandlePrimalReversion - .byte \battler - .byte \case - .endm - - .macro handleultraburst battler:req, case:req - callnative BS_HandleUltraBurst - .byte \battler - .byte \case - .endm - .macro jumpifshelltrap battler:req, jumpInstr:req callnative BS_JumpIfShellTrap .byte \battler @@ -1605,12 +1462,8 @@ .4byte \jumpInstr .endm - .macro trygulpmissile - callnative BS_TryGulpMissile - .endm - - .macro tryactivategulpmissile - callnative BS_TryActivateGulpMissile + .macro trytwoturnmovespowerherbformchange + callnative BS_TryTwoTurnMovesPowerHerbFormChange .endm .macro tryquash failInstr:req @@ -1637,10 +1490,6 @@ .4byte \jumpInstr .endm - .macro ficklebeamdamagecalculation - callnative BS_FickleBeamDamageCalculation - .endm - .macro trytarshot failInstr:req callnative BS_TryTarShot .4byte \failInstr @@ -2130,10 +1979,11 @@ .4byte \failInstr .endm - .macro handleformchange battler:req, case_:req + .macro handleformchange battler:req, caseId:req, bufferSpeciesName=TRUE callnative BS_HandleFormChange .byte \battler - .byte \case_ + .byte \caseId + .byte \bufferSpeciesName .endm .macro tryautotomize failInstr:req @@ -2319,9 +2169,8 @@ .4byte \failInstr .endm - .macro setpoltergeistmessage failInstr:req + .macro setpoltergeistmessage callnative BS_SetPoltergeistMessage - .4byte \failInstr .endm .macro tryresetnegativestatstages @@ -2340,11 +2189,6 @@ callnative BS_BattlerItemToLastUsedItem .endm -@ various remaining from pokefirered - .macro getbattlersforrecall - callnative BS_GetBattlersForRecall - .endm - .macro setallytonexttarget jumpInstr:req jumpifbyte CMP_GREATER_THAN, gBattlerTarget, 0x1, 1f addbyte gBattlerTarget, 0x2 @@ -2426,3 +2270,8 @@ .byte \battler .byte \switchedItems .endm + +@ various remaining from pokefirered + .macro getbattlersforrecall + callnative BS_GetBattlersForRecall + .endm diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 2d79c8aac..854dd04a2 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -61,7 +61,7 @@ gBattleAnimGeneral_SilphScoped:: monbg ANIM_ATTACKER playsewithpan SE_M_TELEPORT, SOUND_PAN_ATTACKER waitplaysewithpan SE_M_MINIMIZE, SOUND_PAN_ATTACKER, 48 - createvisualtask AnimTask_TransformMon, 3, FALSE, FALSE, TRUE + createvisualtask AnimTask_TransformMon, 3, SPECIES_GFX_CHANGE_GHOST_UNVEIL waitsound waitforvisualfinish clearmonbg ANIM_ATTACKER @@ -26246,7 +26246,7 @@ gBattleAnimMove_Transform:: monbg ANIM_ATTACKER playsewithpan SE_M_TELEPORT, SOUND_PAN_ATTACKER waitplaysewithpan SE_M_MINIMIZE, SOUND_PAN_ATTACKER, 48 - createvisualtask AnimTask_TransformMon, 2, 0, 1 + createvisualtask AnimTask_TransformMon, 2, SPECIES_GFX_CHANGE_TRANSFORM waitforvisualfinish clearmonbg ANIM_ATTACKER end @@ -28768,14 +28768,28 @@ gBattleAnimGeneral_SimpleHeal:: gBattleAnimGeneral_IllusionOff:: monbg ANIM_TARGET - createvisualtask AnimTask_TransformMon, 2, 1, 0 + createvisualtask AnimTask_TransformMon, 2, SPECIES_GFX_CHANGE_ILLUSION_OFF waitforvisualfinish clearmonbg ANIM_TARGET end gBattleAnimGeneral_FormChange:: monbg ANIM_ATTACKER - createvisualtask AnimTask_TransformMon, 2, 1, 0 + createvisualtask AnimTask_TransformMon, 2, SPECIES_GFX_CHANGE_FORM_CHANGE + waitforvisualfinish + clearmonbg ANIM_ATTACKER + end + +gBattleAnimGeneral_FormChangeDisguise:: + playsewithpan SE_CONTEST_CONDITION_LOSE, SOUND_PAN_TARGET + createvisualtask AnimTask_ShakeMon2, 2, ANIM_TARGET, 2, 0, 10, 1 + waitforvisualfinish + playsewithpan SE_CONTEST_CURTAIN_FALL, SOUND_PAN_TARGET + goto gBattleAnimGeneral_FormChangeInstant + +gBattleAnimGeneral_FormChangeInstant:: + monbg ANIM_ATTACKER + createvisualtask AnimTask_TransformMon, 2, SPECIES_GFX_CHANGE_FORM_CHANGE_INSTANT waitforvisualfinish clearmonbg ANIM_ATTACKER end @@ -28851,6 +28865,7 @@ MegaEvolutionSpinEffect: return gBattleAnimGeneral_TeraCharge:: + createvisualtask AnimTask_HideOpponentShadows, 2 @ Hide opponent shadows so they don't flicker between battle anims loadspritegfx ANIM_TAG_TERA_CRYSTAL loadspritegfx ANIM_TAG_TERA_SHATTER loadspritegfx ANIM_TAG_FOCUS_ENERGY @@ -28872,7 +28887,6 @@ gBattleAnimGeneral_TeraCharge:: delay 20 createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 0, 16, RGB_WHITEALPHA waitforvisualfinish - createvisualtask AnimTask_TransformMon, 2, 1, 0 call TeraChargeParticles playsewithpan SE_M_BRICK_BREAK, SOUND_PAN_ATTACKER clearmonbg ANIM_ATK_PARTNER diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 0c03e2e3b..cbfd4856b 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -3,7 +3,7 @@ #include "constants/global.h" #include "constants/battle.h" #include "constants/pokemon.h" -@#include "constants/battle_arena.h" +#include "constants/battle_arena.h" #include "constants/battle_move_resolution.h" #include "constants/battle_script_commands.h" #include "constants/battle_anim.h" @@ -22,32 +22,25 @@ .section script_data, "aw", %progbits -BattleScript_BattleTowerLost:: +BattleScript_TrainerTowerLost:: getbattlersforrecall - jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, 0, BattleScript_BattleTowerLostLostSkipMonRecall + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, 0, BattleScript_TrainerTowerLostLostSkipMonRecall printfromtable gDoubleBattleRecallStrings waitmessage B_WAIT_TIME_LONG returnopponentmon1toball waitstate returnopponentmon2toball waitstate -BattleScript_BattleTowerLostLostSkipMonRecall:: +BattleScript_TrainerTowerLostLostSkipMonRecall:: trainerslidein BS_ATTACKER waitstate printstring STRINGID_TRAINER1WINTEXT - jumpifnotbattletype BATTLE_TYPE_DOUBLE, BattleScript_BattleTowerLostLostSkipDouble + jumpifnotbattletype BATTLE_TYPE_DOUBLE, BattleScript_TrainerTowerLostLostSkipDouble printstring STRINGID_TRAINER2WINTEXT -BattleScript_BattleTowerLostLostSkipDouble:: +BattleScript_TrainerTowerLostLostSkipDouble:: end2 -BattleScript_LinkBattleWonOrLost:: - printstring STRINGID_BATTLEEND - waitmessage B_WAIT_TIME_LONG - endlinkbattle - waitmessage B_WAIT_TIME_LONG - end2 - -BattleScript_BattleTowerTrainerBattleWon:: +BattleScript_TrainerTowerTrainerBattleWon:: printstring STRINGID_PLAYERDEFEATEDTRAINER1 trainerslidein BS_ATTACKER waitstate @@ -59,22 +52,18 @@ BattleScript_BattleTowerEtcTrainerBattleWonSkipText:: pickup end2 -BattleScript_LeftoverBirchString:: - printstring STRINGID_DONTLEAVEBIRCH - end2 - @ pokeemerald - -BattleScript_EffectFickleBeam:: - attackcanceler - accuracycheck BattleScript_MoveMissedPause - ficklebeamdamagecalculation - goto BattleScript_HitFromDamageCalc -BattleScript_FickleBeamDoubled:: +BattleScript_FickleBeamMessage:: pause B_WAIT_TIME_SHORTEST printstring STRINGID_FICKLEBEAMDOUBLED waitmessage B_WAIT_TIME_LONG - goto BattleScript_HitFromDamageCalc + return + +BattleScript_MagnitudeMessage:: + pause B_WAIT_TIME_SHORT + printstring STRINGID_MAGNITUDESTRENGTH + waitmessage B_WAIT_TIME_LONG + return BattleScript_Terastallization:: @ TODO: no string prints in S/V, but right now this helps with clarity @@ -91,14 +80,13 @@ BattleScript_Terastallization:: BattleScript_TeraFormChange:: @ TODO: no string prints in S/V, but right now this helps with clarity printstring STRINGID_PKMNSTORINGENERGY - handleformchange BS_ATTACKER, 0 + handleformchange BS_ATTACKER, 0, FALSE @ Prevent species name from overriting type name handleformchange BS_ATTACKER, 1 playanimation BS_ATTACKER, B_ANIM_TERA_CHARGE waitanimation applyterastallization playanimation BS_ATTACKER, B_ANIM_TERA_ACTIVATE waitanimation - handleformchange BS_ATTACKER, 2 printstring STRINGID_PKMNTERASTALLIZEDINTO waitmessage B_WAIT_TIME_LONG switchinabilities BS_ATTACKER @@ -606,7 +594,6 @@ BattleScript_EffectFling:: printstring STRINGID_PKMNFLUNG waitmessage B_WAIT_TIME_SHORT damagecalc - adjustdamage removeitem BS_ATTACKER attackanimation waitanimation @@ -692,7 +679,7 @@ BattleScript_OctlockTurnDmgEnd: BattleScript_EffectPoltergeist:: attackcanceler accuracycheck BattleScript_MoveMissedPause - setpoltergeistmessage BattleScript_ButItFailed + setpoltergeistmessage printstring STRINGID_ABOUTTOUSEPOLTERGEIST waitmessage B_WAIT_TIME_LONG goto BattleScript_HitFromDamageCalc @@ -1068,24 +1055,59 @@ BattleScript_VCreateStatLossRet: BattleScript_EffectPartingShot:: attackcanceler jumpifstat BS_TARGET, CMP_GREATER_THAN, STAT_ATK, MIN_STAT_STAGE, BattleScript_EffectPartingShotTryAtk - jumpifstat BS_TARGET, CMP_EQUAL, STAT_SPATK, MIN_STAT_STAGE, BattleScript_CantLowerMultipleStats + jumpifstat BS_TARGET, CMP_EQUAL, STAT_SPATK, MIN_STAT_STAGE, BattleScript_EffectPartingShotCantLowerMultipleStats BattleScript_EffectPartingShotTryAtk: accuracycheck BattleScript_MoveMissedPause attackanimation waitanimation + setbyte sB_ANIM_TARGETS_HIT, 0 setstatchanger STAT_ATK, 1, TRUE statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_EffectPartingShotTrySpAtk, BIT_SPATK - printfromtable gStatDownStringIds - waitmessage B_WAIT_TIME_LONG + call BattleScript_EffectPartingShotMaybePrintStat BattleScript_EffectPartingShotTrySpAtk: setstatchanger STAT_SPATK, 1, TRUE - statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_EffectPartingShotSwitch - printfromtable gStatDownStringIds - waitmessage B_WAIT_TIME_LONG + statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_EffectPartingShotMaybeSwitch + call BattleScript_EffectPartingShotMaybePrintStat +BattleScript_EffectPartingShotMaybeSwitch: + jumpifgenconfiglowerthan CONFIG_PARTING_SHOT_SWITCH, GEN_7, BattleScript_EffectPartingShotSwitch + jumpifbyte CMP_NOT_EQUAL, sB_ANIM_TARGETS_HIT, 0, BattleScript_EffectPartingShotSwitch + goto BattleScript_MoveEnd + BattleScript_EffectPartingShotSwitch: moveendall goto BattleScript_MoveSwitchPursuitEnd +BattleScript_EffectPartingShotCantLowerMultipleStats: + pause B_WAIT_TIME_SHORT + setmoveresultflags MOVE_RESULT_FAILED + call BattleScript_EffectPartingShotPrintWontDecrease + setbyte sB_ANIM_TARGETS_HIT, 0 + goto BattleScript_EffectPartingShotMaybeSwitch + +BattleScript_EffectPartingShotMaybePrintStat: + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_ATTACKER_STAT_CHANGED, BattleScript_EffectPartingShotPrintStat + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_DEFENDER_STAT_CHANGED, BattleScript_EffectPartingShotPrintStat + return + +BattleScript_EffectPartingShotPrintStat: + setbyte sB_ANIM_TARGETS_HIT, 1 + printfromtable gStatDownStringIds + waitmessage B_WAIT_TIME_LONG + return + +BattleScript_EffectPartingShotPrintWontDecrease: + jumpifability BS_TARGET, ABILITY_CONTRARY, BattleScript_EffectPartingShotPrintWontDecreaseContrary + printstring STRINGID_STATSWONTDECREASE2 + waitmessage B_WAIT_TIME_LONG + return + +BattleScript_EffectPartingShotPrintWontDecreaseContrary: + swapattackerwithtarget + printstring STRINGID_STATSWONTDECREASE2 + waitmessage B_WAIT_TIME_LONG + swapattackerwithtarget + return + BattleScript_EffectPowder:: attackcanceler jumpifvolatile BS_TARGET, VOLATILE_POWDER, BattleScript_ButItFailed @@ -1120,6 +1142,7 @@ BattleScript_EffectAromaticMistWontGoHigher: BattleScript_EffectMagneticFlux:: attackcanceler + savetarget setbyte gBattleCommunication, 0 BattleScript_EffectMagneticFluxStart: jumpifability BS_TARGET, ABILITY_MINUS, BattleScript_EffectMagneticFluxCheckStats @@ -1148,8 +1171,10 @@ BattleScript_EffectMagneticFluxTrySpDef: waitmessage B_WAIT_TIME_LONG BattleScript_EffectMagneticFluxLoop: jumpifbytenotequal gBattlerTarget, gBattlerAttacker, BattleScript_EffectMagneticFluxEnd + jumpifnoally BS_ATTACKER, BattleScript_EffectMagneticFluxEnd setallytonexttarget BattleScript_EffectMagneticFluxStart BattleScript_EffectMagneticFluxEnd: + restoretarget jumpifbyte CMP_NOT_EQUAL, gBattleCommunication, 0, BattleScript_MoveEnd goto BattleScript_ButItFailed @@ -1184,6 +1209,7 @@ BattleScript_EffectGearUpTrySpAtk: waitmessage B_WAIT_TIME_LONG BattleScript_EffectGearUpLoop: jumpifbytenotequal gBattlerTarget, gBattlerAttacker, BattleScript_EffectGearUpEnd + jumpifnoally BS_ATTACKER, BattleScript_EffectGearUpEnd setallytonexttarget BattleScript_EffectGearUpStart BattleScript_EffectGearUpEnd: restoretarget @@ -2192,6 +2218,9 @@ BattleScript_TryTailwindAbilitiesLoop_WindPower: BattleScript_EffectMiracleEye:: attackcanceler accuracycheck BattleScript_MoveMissedPause + jumpifgenconfiglowerthan CONFIG_MIRACLE_EYE_FAIL, GEN_5, BattleScript_MiracleEyeSet + jumpifvolatile BS_TARGET, VOLATILE_MIRACLE_EYE, BattleScript_ButItFailed +BattleScript_MiracleEyeSet: setvolatile BS_TARGET, VOLATILE_MIRACLE_EYE goto BattleScript_IdentifiedFoe @@ -2273,19 +2302,15 @@ BattleScript_HitFromAccCheck:: setpreattackadditionaleffect BattleScript_HitFromDamageCalc:: damagecalc - adjustdamage -BattleScript_HitFromAtkAnimation:: call BattleScript_Hit_RetFromAtkAnimation BattleScript_MoveEnd:: moveendall end -BattleScript_EffectHit_Ret:: - attackcanceler BattleScript_EffectHit_RetFromAccCheck:: accuracycheck BattleScript_MoveMissedPause + setpreattackadditionaleffect damagecalc - adjustdamage BattleScript_Hit_RetFromAtkAnimation:: attackanimation waitanimation @@ -2301,15 +2326,6 @@ BattleScript_Hit_RetFromAtkAnimation:: setadditionaleffects return -BattleScript_EffectNaturalGift:: - attackcanceler - jumpifnotberry BS_ATTACKER, BattleScript_ButItFailed - jumpifword CMP_COMMON_BITS, gFieldStatuses, STATUS_FIELD_MAGIC_ROOM, BattleScript_ButItFailed - jumpifability BS_ATTACKER, ABILITY_KLUTZ, BattleScript_ButItFailed - jumpifvolatile BS_ATTACKER, VOLATILE_EMBARGO, BattleScript_ButItFailed - accuracycheck BattleScript_MoveMissedPause - call BattleScript_HitFromDamageCalc - BattleScript_MakeMoveMissed:: setmoveresultflags MOVE_RESULT_MISSED BattleScript_MoveMissedPause:: @@ -2721,18 +2737,6 @@ BattleScript_AbilityPreventsRest:: waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd -BattleScript_EffectOHKO:: - attackcanceler - typecalc - jumpifmovehadnoeffect BattleScript_HitFromAtkAnimation - tryKO BattleScript_KOFail - goto BattleScript_HitFromAtkAnimation -BattleScript_KOFail:: - pause B_WAIT_TIME_LONG - printfromtable gKOFailedStringIds - waitmessage B_WAIT_TIME_LONG - goto BattleScript_MoveEnd - BattleScript_RecoilIfMiss:: printstring STRINGID_PKMNCRASHED waitmessage B_WAIT_TIME_LONG @@ -2895,7 +2899,7 @@ BattleScript_PowerHerbActivation:: printstring STRINGID_POWERHERB waitmessage B_WAIT_TIME_LONG removeitem BS_ATTACKER - trygulpmissile @ Edge case for Cramorant ability Gulp Missile + trytwoturnmovespowerherbformchange @ Edge case for Cramorant ability Gulp Missile return BattleScript_EffectGeomancy:: @@ -2932,9 +2936,6 @@ BattleScript_TwoTurnMoveCharging:: setadditionaleffects @ only onChargeTurnOnly effects will work here return -BattleScript_TwoTurnMovesSecondPowerHerbActivates: - call BattleScript_PowerHerbActivation - trygulpmissile @ Edge case for Cramorant ability Gulp Missile BattleScript_FromTwoTurnMovesSecondTurnRet: call BattleScript_TwoTurnMovesSecondTurnRet accuracycheck BattleScript_MoveMissedPause @@ -3068,16 +3069,6 @@ BattleScript_EffectPainSplit:: waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd -BattleScript_EffectSnore:: - attackcanceler - jumpifhalfword CMP_EQUAL, gChosenMove, MOVE_SLEEP_TALK, BattleScript_DoSnore - printstring STRINGID_PKMNFASTASLEEP - waitmessage B_WAIT_TIME_LONG - statusanimation BS_ATTACKER -BattleScript_DoSnore:: - accuracycheck BattleScript_MoveMissedPause - goto BattleScript_HitFromDamageCalc - BattleScript_EffectConversion2:: attackcanceler settypetorandomresistance BattleScript_ButItFailed @@ -3181,6 +3172,7 @@ BattleScript_EffectMeanLookGen5: BattleScript_EffectNightmare:: attackcanceler jumpifsubstituteblocks BattleScript_ButItFailed + accuracycheck BattleScript_MoveMissedPause jumpifvolatile BS_TARGET, VOLATILE_NIGHTMARE, BattleScript_ButItFailed jumpifstatus BS_TARGET, STATUS1_SLEEP, BattleScript_NightmareWorked jumpifability BS_TARGET, ABILITY_COMATOSE, BattleScript_NightmareWorked @@ -3268,7 +3260,12 @@ BattleScript_EffectSpikes:: BattleScript_EffectForesight:: attackcanceler + accuracycheck BattleScript_ButItFailed + jumpifgenconfiglowerthan CONFIG_FORESIGHT_FAIL, GEN_3, BattleScript_ForesightFailCheck + jumpifgenconfiglowerthan CONFIG_FORESIGHT_FAIL, GEN_5, BattleScript_ForesightSet +BattleScript_ForesightFailCheck: jumpifvolatile BS_TARGET, VOLATILE_FORESIGHT, BattleScript_ButItFailed +BattleScript_ForesightSet: setvolatile BS_TARGET, VOLATILE_FORESIGHT BattleScript_IdentifiedFoe: attackanimation @@ -3357,15 +3354,6 @@ BattleScript_EffectSafeguard:: setsafeguard goto BattleScript_PrintReflectLightScreenSafeguardString -BattleScript_EffectMagnitude:: - attackcanceler - magnitudedamagecalculation - pause B_WAIT_TIME_SHORT - printstring STRINGID_MAGNITUDESTRENGTH - waitmessage B_WAIT_TIME_LONG - accuracycheck BattleScript_MoveMissedPause - goto BattleScript_HitFromDamageCalc - BattleScript_EffectBatonPass:: attackcanceler jumpifbattletype BATTLE_TYPE_ARENA, BattleScript_ButItFailed @@ -3476,6 +3464,7 @@ BattleScript_EffectBellyDrum:: BattleScript_EffectPsychUp:: attackcanceler + accuracycheck BattleScript_MoveMissedPause copyfoestats attackanimation waitanimation @@ -3511,17 +3500,9 @@ BattleScript_DoEffectTeleport:: setteleportoutcome BS_ATTACKER goto BattleScript_MoveEnd -BattleScript_EffectBeatUp:: - jumpifgenconfiglowerthan CONFIG_BEAT_UP, GEN_5, BattleScript_EffectBeatUpGen3 - goto BattleScript_EffectHit - -BattleScript_EffectBeatUpGen3: - attackcanceler - accuracycheck BattleScript_MoveMissedPause - pause B_WAIT_TIME_SHORT - trydobeatup BattleScript_MoveEnd, BattleScript_ButItFailed +BattleScript_BeatUpAttackMessage:: printstring STRINGID_PKMNATTACK - goto BattleScript_HitFromDamageCalc + return BattleScript_EffectDefenseCurl:: attackcanceler @@ -3612,11 +3593,9 @@ BattleScript_EffectStockpileSpDef:: goto BattleScript_MoveEnd BattleScript_MoveEffectStockpileWoreOff:: - .if B_STOCKPILE_RAISES_DEFS >= GEN_4 dostockpilestatchangeswearoff BS_ATTACKER, BattleScript_StockpileStatChangeDown printstring STRINGID_STOCKPILEDEFFECTWOREOFF waitmessage B_WAIT_TIME_SHORT - .endif return BattleScript_StockpileStatChangeDown: @@ -3626,24 +3605,6 @@ BattleScript_StockpileStatChangeDown: BattleScript_StockpileStatChangeDown_Ret: return -BattleScript_EffectSpitUp:: - attackcanceler - jumpifbyte CMP_EQUAL, cMISS_TYPE, B_MSG_PROTECTED, BattleScript_SpitUpFailProtect - accuracycheck BattleScript_MoveMissedPause - damagecalc - adjustdamage - stockpiletobasedamage - call BattleScript_Hit_RetFromAtkAnimation - removestockpilecounters - goto BattleScript_MoveEnd - -BattleScript_SpitUpFailProtect:: - pause B_WAIT_TIME_LONG - stockpiletobasedamage - resultmessage - waitmessage B_WAIT_TIME_LONG - goto BattleScript_MoveEnd - BattleScript_EffectSwallow:: attackcanceler stockpiletohpheal BattleScript_ButItFailed @@ -3653,7 +3614,6 @@ BattleScript_EffectSwallow:: datahpupdate BS_TARGET, PASSIVE_HP_UPDATE printstring STRINGID_PKMNREGAINEDHEALTH waitmessage B_WAIT_TIME_LONG - removestockpilecounters goto BattleScript_MoveEnd BattleScript_EffectTorment:: @@ -4110,7 +4070,6 @@ BattleScript_EffectCamouflage:: BattleScript_FaintBattler:: tryillusionoff BS_FAINTED - tryactivategulpmissile playfaintcry BS_FAINTED pause B_WAIT_TIME_LONG dofaintanimation BS_FAINTED @@ -4284,7 +4243,10 @@ BattleScript_RivalBattleLostSkipMonRecall:: end2 BattleScript_LocalBattleLost:: - jumpifbattletype BATTLE_TYPE_TRAINER_TOWER, BattleScript_BattleTowerLost + jumpifbattletype BATTLE_TYPE_DOME, BattleScript_CheckDomeDrew + jumpifbattletype BATTLE_TYPE_FRONTIER, BattleScript_LocalBattleLostPrintTrainersWinText + jumpifbattletype BATTLE_TYPE_TRAINER_HILL, BattleScript_LocalBattleLostPrintTrainersWinText + jumpifbattletype BATTLE_TYPE_TRAINER_TOWER, BattleScript_TrainerTowerLost jumpifbattletype BATTLE_TYPE_EREADER_TRAINER, BattleScript_LocalBattleLostEnd jumpifhalfword CMP_EQUAL, gTrainerBattleParameter + 2, TRAINER_SECRET_BASE, BattleScript_LocalBattleLostEnd jumpifbyte CMP_NOT_EQUAL, cMULTISTRING_CHOOSER, 0, BattleScript_RivalBattleLost @@ -4314,97 +4276,97 @@ BattleScript_LocalBattleLostEnd:: end2 .endif -@BattleScript_CheckDomeDrew:: -@ jumpifbyte CMP_EQUAL, gBattleOutcome, B_OUTCOME_DREW, BattleScript_LocalBattleLostEnd_ -@BattleScript_LocalBattleLostPrintTrainersWinText:: -@ jumpifnotbattletype BATTLE_TYPE_TRAINER, BattleScript_LocalBattleLostPrintWhiteOut -@ returnopponentmon1toball -@ waitstate -@ returnopponentmon2toball -@ waitstate -@ trainerslidein BS_OPPONENT1 -@ waitstate -@ printstring STRINGID_TRAINER1WINTEXT -@ jumpifbattletype BATTLE_TYPE_TOWER_LINK_MULTI, BattleScript_LocalBattleLostDoTrainer2WinText -@ jumpifnotbattletype BATTLE_TYPE_TWO_OPPONENTS, BattleScript_LocalBattleLostEnd_ -@BattleScript_LocalBattleLostDoTrainer2WinText:: -@ trainerslideout BS_OPPONENT1 -@ waitstate -@ trainerslidein BS_OPPONENT2 -@ waitstate -@ printstring STRINGID_TRAINER2WINTEXT +BattleScript_CheckDomeDrew:: + jumpifbyte CMP_EQUAL, gBattleOutcome, B_OUTCOME_DREW, BattleScript_LocalBattleLostEnd_ +BattleScript_LocalBattleLostPrintTrainersWinText:: + jumpifnotbattletype BATTLE_TYPE_TRAINER, BattleScript_LocalBattleLostPrintWhiteOut + returnopponentmon1toball + waitstate + returnopponentmon2toball + waitstate + trainerslidein BS_OPPONENT1 + waitstate + printstring STRINGID_TRAINER1WINTEXT + jumpifbattletype BATTLE_TYPE_TOWER_LINK_MULTI, BattleScript_LocalBattleLostDoTrainer2WinText + jumpifnotbattletype BATTLE_TYPE_TWO_OPPONENTS, BattleScript_LocalBattleLostEnd_ +BattleScript_LocalBattleLostDoTrainer2WinText:: + trainerslideout BS_OPPONENT1 + waitstate + trainerslidein BS_OPPONENT2 + waitstate + printstring STRINGID_TRAINER2WINTEXT BattleScript_LocalBattleLostEnd_:: end2 -@BattleScript_FrontierLinkBattleLost:: -@ returnopponentmon1toball -@ waitstate -@ returnopponentmon2toball -@ waitstate -@ trainerslidein BS_OPPONENT1 -@ waitstate -@ printstring STRINGID_TRAINER1WINTEXT -@ trainerslideout BS_OPPONENT1 -@ waitstate -@ trainerslidein BS_OPPONENT2 -@ waitstate -@ printstring STRINGID_TRAINER2WINTEXT -@ jumpifbattletype BATTLE_TYPE_RECORDED, BattleScript_FrontierLinkBattleLostEnd -@ endlinkbattle -@BattleScript_FrontierLinkBattleLostEnd:: -@ waitmessage B_WAIT_TIME_LONG -@ end2 -@ -@BattleScript_LinkBattleWonOrLost:: -@ jumpifbattletype BATTLE_TYPE_BATTLE_TOWER, BattleScript_TowerLinkBattleWon -@ printstring STRINGID_BATTLEEND -@ waitmessage B_WAIT_TIME_LONG -@ jumpifbattletype BATTLE_TYPE_RECORDED, BattleScript_LinkBattleWonOrLostWaitEnd -@ endlinkbattle -@BattleScript_LinkBattleWonOrLostWaitEnd:: -@ waitmessage B_WAIT_TIME_LONG -@ end2 -@ -@BattleScript_TowerLinkBattleWon:: -@ playtrainerdefeatedmusic -@ printstring STRINGID_BATTLEEND -@ waitmessage B_WAIT_TIME_LONG -@ trainerslidein BS_OPPONENT1 -@ waitstate -@ printstring STRINGID_TRAINER1LOSETEXT -@ trainerslideout BS_OPPONENT1 -@ waitstate -@ trainerslidein BS_OPPONENT2 -@ waitstate -@ printstring STRINGID_TRAINER2LOSETEXT -@ jumpifbattletype BATTLE_TYPE_RECORDED, BattleScript_TowerLinkBattleWonEnd -@ endlinkbattle -@BattleScript_TowerLinkBattleWonEnd:: -@ waitmessage B_WAIT_TIME_LONG -@ end2 -@ -@BattleScript_FrontierTrainerBattleWon:: -@ jumpifnotbattletype BATTLE_TYPE_TRAINER, BattleScript_PayDayMoneyAndPickUpItems -@ jumpifbattletype BATTLE_TYPE_TWO_OPPONENTS, BattleScript_FrontierTrainerBattleWon_TwoDefeated -@ printstring STRINGID_PLAYERDEFEATEDTRAINER1 -@ goto BattleScript_FrontierTrainerBattleWon_LoseTexts -@BattleScript_FrontierTrainerBattleWon_TwoDefeated: -@ printstring STRINGID_TWOENEMIESDEFEATED -@BattleScript_FrontierTrainerBattleWon_LoseTexts: -@ trainerslidein BS_OPPONENT1 -@ waitstate -@ printstring STRINGID_TRAINER1LOSETEXT -@ jumpifnotbattletype BATTLE_TYPE_TWO_OPPONENTS, BattleScript_TryPickUpItems -@ trainerslideout BS_OPPONENT1 -@ waitstate -@ trainerslidein BS_OPPONENT2 -@ waitstate -@ printstring STRINGID_TRAINER2LOSETEXT -@BattleScript_TryPickUpItems: -@ jumpifnotbattletype BATTLE_TYPE_PYRAMID, BattleScript_FrontierTrainerBattleWon_End -@ pickup -@BattleScript_FrontierTrainerBattleWon_End: -@ end2 +BattleScript_FrontierLinkBattleLost:: + returnopponentmon1toball + waitstate + returnopponentmon2toball + waitstate + trainerslidein BS_OPPONENT1 + waitstate + printstring STRINGID_TRAINER1WINTEXT + trainerslideout BS_OPPONENT1 + waitstate + trainerslidein BS_OPPONENT2 + waitstate + printstring STRINGID_TRAINER2WINTEXT + jumpifbattletype BATTLE_TYPE_RECORDED, BattleScript_FrontierLinkBattleLostEnd + endlinkbattle +BattleScript_FrontierLinkBattleLostEnd:: + waitmessage B_WAIT_TIME_LONG + end2 + +BattleScript_LinkBattleWonOrLost:: + jumpifbattletype BATTLE_TYPE_BATTLE_TOWER, BattleScript_TowerLinkBattleWon + printstring STRINGID_BATTLEEND + waitmessage B_WAIT_TIME_LONG + jumpifbattletype BATTLE_TYPE_RECORDED, BattleScript_LinkBattleWonOrLostWaitEnd + endlinkbattle +BattleScript_LinkBattleWonOrLostWaitEnd:: + waitmessage B_WAIT_TIME_LONG + end2 + +BattleScript_TowerLinkBattleWon:: + playtrainerdefeatedmusic + printstring STRINGID_BATTLEEND + waitmessage B_WAIT_TIME_LONG + trainerslidein BS_OPPONENT1 + waitstate + printstring STRINGID_TRAINER1LOSETEXT + trainerslideout BS_OPPONENT1 + waitstate + trainerslidein BS_OPPONENT2 + waitstate + printstring STRINGID_TRAINER2LOSETEXT + jumpifbattletype BATTLE_TYPE_RECORDED, BattleScript_TowerLinkBattleWonEnd + endlinkbattle +BattleScript_TowerLinkBattleWonEnd:: + waitmessage B_WAIT_TIME_LONG + end2 + +BattleScript_FrontierTrainerBattleWon:: + jumpifnotbattletype BATTLE_TYPE_TRAINER, BattleScript_PayDayMoneyAndPickUpItems + jumpifbattletype BATTLE_TYPE_TWO_OPPONENTS, BattleScript_FrontierTrainerBattleWon_TwoDefeated + printstring STRINGID_PLAYERDEFEATEDTRAINER1 + goto BattleScript_FrontierTrainerBattleWon_LoseTexts +BattleScript_FrontierTrainerBattleWon_TwoDefeated: + printstring STRINGID_TWOENEMIESDEFEATED +BattleScript_FrontierTrainerBattleWon_LoseTexts: + trainerslidein BS_OPPONENT1 + waitstate + printstring STRINGID_TRAINER1LOSETEXT + jumpifnotbattletype BATTLE_TYPE_TWO_OPPONENTS, BattleScript_TryPickUpItems + trainerslideout BS_OPPONENT1 + waitstate + trainerslidein BS_OPPONENT2 + waitstate + printstring STRINGID_TRAINER2LOSETEXT +BattleScript_TryPickUpItems: + jumpifnotbattletype BATTLE_TYPE_PYRAMID, BattleScript_FrontierTrainerBattleWon_End + pickup +BattleScript_FrontierTrainerBattleWon_End: + end2 BattleScript_SmokeBallEscape:: playanimation BS_ATTACKER, B_ANIM_SMOKEBALL_ESCAPE @@ -4726,7 +4688,6 @@ BattleScript_BideAttack:: typecalc clearmoveresultflags MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE copybidedmg - adjustdamage setbyte sB_ANIM_TURN, 1 attackanimation waitanimation @@ -4954,6 +4915,9 @@ BattleScript_PerishBodyActivates:: BattleScript_GulpMissileGorging:: call BattleScript_AbilityPopUp + handleformchange BS_TARGET, 0 + playanimation BS_TARGET, B_ANIM_FORM_CHANGE_INSTANT + waitanimation playanimation BS_ATTACKER, B_ANIM_GULP_MISSILE waitanimation effectivenesssound @@ -4965,21 +4929,17 @@ BattleScript_GulpMissileGorging:: tryfaintmon BS_ATTACKER jumpiffainted BS_ATTACKER, TRUE, BattleScript_GulpMissileNoSecondEffectGorging BattleScript_GulpMissileNoDmgGorging: - handleformchange BS_TARGET, 0 - playanimation BS_TARGET, B_ANIM_FORM_CHANGE - waitanimation swapattackerwithtarget seteffectprimary BS_ATTACKER, BS_TARGET, MOVE_EFFECT_PARALYSIS swapattackerwithtarget - return BattleScript_GulpMissileNoSecondEffectGorging: - handleformchange BS_TARGET, 0 - playanimation BS_TARGET, B_ANIM_FORM_CHANGE - waitanimation return BattleScript_GulpMissileGulping:: call BattleScript_AbilityPopUp + handleformchange BS_TARGET, 0 + playanimation BS_TARGET, B_ANIM_FORM_CHANGE_INSTANT + waitanimation playanimation BS_ATTACKER, B_ANIM_GULP_MISSILE waitanimation effectivenesssound @@ -4991,9 +4951,6 @@ BattleScript_GulpMissileGulping:: tryfaintmon BS_ATTACKER jumpiffainted BS_ATTACKER, TRUE, BattleScript_GulpMissileNoSecondEffectGulping BattleScript_GulpMissileNoDmgGulping: - handleformchange BS_TARGET, 0 - playanimation BS_TARGET, B_ANIM_FORM_CHANGE - waitanimation swapattackerwithtarget @ to make gStatDownStringIds down below print the right battler setstatchanger STAT_DEF, 1, TRUE statbuffchange BS_TARGET, STAT_CHANGE_NOT_PROTECT_AFFECTED | STAT_CHANGE_ALLOW_PTR, BattleScript_GulpMissileGulpingEnd @@ -5001,11 +4958,7 @@ BattleScript_GulpMissileNoDmgGulping: waitmessage B_WAIT_TIME_LONG BattleScript_GulpMissileGulpingEnd: swapattackerwithtarget @ restore the battlers, just in case - return BattleScript_GulpMissileNoSecondEffectGulping: - handleformchange BS_TARGET, 0 - playanimation BS_TARGET, B_ANIM_FORM_CHANGE - waitanimation return BattleScript_SeedSowerActivates:: @@ -5150,7 +5103,6 @@ BattleScript_MonTookFutureAttack:: jumpifmovehadnoeffect BattleScript_FutureAttackEnd accuracycheck BattleScript_MoveMissedPause damagecalc - adjustdamage jumpifmovehadnoeffect BattleScript_DoFutureAttackResult jumpifbyte CMP_NOT_EQUAL, cMULTISTRING_CHOOSER, B_MSG_FUTURE_SIGHT, BattleScript_FutureHitAnimDoomDesire playanimation BS_ATTACKER, B_ANIM_FUTURE_SIGHT_HIT @@ -5490,10 +5442,10 @@ BattleScript_MegaEvolution:: printstring STRINGID_MEGAEVOREACTING BattleScript_MegaEvolutionAfterString: waitmessage B_WAIT_TIME_LONG - handlemegaevo BS_SCRIPTING, 0 + handleformchange BS_SCRIPTING, 0 playanimation BS_SCRIPTING, B_ANIM_MEGA_EVOLUTION waitanimation - handlemegaevo BS_SCRIPTING, 1 + handleformchange BS_SCRIPTING, 1 printstring STRINGID_MEGAEVOEVOLVED waitmessage B_WAIT_TIME_LONG switchinabilities BS_SCRIPTING @@ -5507,11 +5459,10 @@ BattleScript_WishMegaEvolution:: BattleScript_PrimalReversion:: flushtextbox - handleprimalreversion BS_SCRIPTING, 0 - handleprimalreversion BS_SCRIPTING, 1 + handleformchange BS_SCRIPTING, 0 playanimation BS_SCRIPTING, B_ANIM_PRIMAL_REVERSION waitanimation - handleprimalreversion BS_SCRIPTING, 2 + handleformchange BS_SCRIPTING, 1 printstring STRINGID_PKMNREVERTEDTOPRIMAL waitmessage B_WAIT_TIME_LONG switchinabilities BS_SCRIPTING @@ -5523,10 +5474,9 @@ BattleScript_PowerConstruct:: waitmessage B_WAIT_TIME_SHORT call BattleScript_AbilityPopUpScripting handleformchange BS_SCRIPTING, 0 - handleformchange BS_SCRIPTING, 1 playanimation BS_SCRIPTING, B_ANIM_POWER_CONSTRUCT waitanimation - handleformchange BS_SCRIPTING, 2 + handleformchange BS_SCRIPTING, 1 printstring STRINGID_POWERCONSTRUCTTRANSFORM waitmessage B_WAIT_TIME_SHORT end2 @@ -5536,31 +5486,49 @@ BattleScript_UltraBurst:: trytrainerslidezmovemsg printstring STRINGID_ULTRABURSTREACTING waitmessage B_WAIT_TIME_LONG - handleultraburst BS_SCRIPTING, 0 + handleformchange BS_SCRIPTING, 0 playanimation BS_SCRIPTING, B_ANIM_ULTRA_BURST waitanimation - handleultraburst BS_SCRIPTING, 1 + handleformchange BS_SCRIPTING, 1 printstring STRINGID_ULTRABURSTCOMPLETED waitmessage B_WAIT_TIME_LONG switchinabilities BS_SCRIPTING end3 -BattleScript_GulpMissileFormChange:: - call BattleScript_BattlerFormChange +BattleScript_TwoTurnMovesSecondTurnFormChange:: + call BattleScript_BattlerFormChangeInstant goto BattleScript_FromTwoTurnMovesSecondTurnRet BattleScript_BattlerFormChange:: pause 5 call BattleScript_AbilityPopUpScripting flushtextbox -BattleScript_BattlerFormChangeNoPopup: +BattleScript_BattlerFormChangeNoPopup:: handleformchange BS_SCRIPTING, 0 - handleformchange BS_SCRIPTING, 1 playanimation BS_SCRIPTING, B_ANIM_FORM_CHANGE waitanimation - handleformchange BS_SCRIPTING, 2 +BattleScript_BattlerFormChangeFromAfterAnimation:: + handleformchange BS_SCRIPTING, 1 + switchinabilities BS_SCRIPTING + jumpifability BS_TARGET, ABILITY_DISGUISE, BattleScript_ApplyDisguiseFormChangeHPLoss return +BattleScript_BattlerFormChangeInstant:: + handleformchange BS_SCRIPTING, 0 + playanimation BS_SCRIPTING, B_ANIM_FORM_CHANGE_INSTANT + waitanimation + goto BattleScript_BattlerFormChangeFromAfterAnimation + +BattleScript_BattlerFormChangeDisguise:: + call BattleScript_AbilityPopUpScripting + pause B_WAIT_TIME_LONG + handleformchange BS_SCRIPTING, 0 + playanimation BS_SCRIPTING, B_ANIM_FORM_CHANGE_DISGUISE + waitanimation + printstring STRINGID_PKMNDISGUISEWASBUSTED + waitmessage B_WAIT_TIME_SHORT + goto BattleScript_BattlerFormChangeFromAfterAnimation + BattleScript_BattlerFormChangeEnd3NoPopup:: call BattleScript_BattlerFormChangeNoPopup end2 @@ -5574,24 +5542,23 @@ BattleScript_BattlerFormChangeWithString:: call BattleScript_AbilityPopUpScripting flushtextbox handleformchange BS_SCRIPTING, 0 - handleformchange BS_SCRIPTING, 1 playanimation BS_SCRIPTING, B_ANIM_FORM_CHANGE waitanimation - handleformchange BS_SCRIPTING, 2 + handleformchange BS_SCRIPTING, 1 printstring STRINGID_PKMNTRANSFORMED waitmessage B_WAIT_TIME_LONG + switchinabilities BS_SCRIPTING return BattleScript_AttackerFormChangeMoveEffect:: waitmessage 1 handleformchange BS_ATTACKER, 0 - handleformchange BS_ATTACKER, 1 playanimation BS_ATTACKER, B_ANIM_FORM_CHANGE waitanimation + handleformchange BS_ATTACKER, 1 copybyte sBATTLER, gBattlerAttacker printstring STRINGID_PKMNTRANSFORMED waitmessage B_WAIT_TIME_LONG - handleformchange BS_ATTACKER, 2 return BattleScript_BallFetch:: @@ -5618,12 +5585,9 @@ BattleScript_ApplyDisguiseFormChangeHPLossReturn: BattleScript_TargetFormChangeNoPopup: flushtextbox handleformchange BS_SCRIPTING, 0 - handleformchange BS_SCRIPTING, 1 playanimation BS_TARGET, B_ANIM_FORM_CHANGE waitanimation - handleformchange BS_SCRIPTING, 2 - jumpifability BS_TARGET, ABILITY_DISGUISE, BattleScript_ApplyDisguiseFormChangeHPLoss - return + goto BattleScript_BattlerFormChangeFromAfterAnimation BattleScript_TargetFormChange:: pause 5 @@ -5634,11 +5598,6 @@ BattleScript_TargetFormChange:: BattleScript_TargetFormChangeWithString:: pause 5 call BattleScript_AbilityPopUpTarget - call BattleScript_TargetFormChangeNoPopup - printstring STRINGID_PKMNTRANSFORMED - waitmessage B_WAIT_TIME_LONG - return - BattleScript_TargetFormChangeWithStringNoPopup:: call BattleScript_TargetFormChangeNoPopup printstring STRINGID_PKMNTRANSFORMED @@ -5722,6 +5681,12 @@ BattleScript_MoveUsedIsAsleep:: statusanimation BS_ATTACKER goto BattleScript_MoveEnd +BattleScript_BeforeSnoreMessage:: + printstring STRINGID_PKMNFASTASLEEP + waitmessage B_WAIT_TIME_LONG + statusanimation BS_ATTACKER + return + BattleScript_MoveUsedWokeUp:: printfromtable gWokeUpStringIds waitmessage B_WAIT_TIME_LONG @@ -5864,13 +5829,12 @@ BattleScript_MoveUsedIsConfused:: jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, FALSE, BattleScript_MoveUsedIsConfusedRet BattleScript_DoSelfConfusionDmg:: cancelmultiturnmoves - adjustdamage printstring STRINGID_ITHURTCONFUSION waitmessage B_WAIT_TIME_LONG effectivenesssound hitanimation BS_ATTACKER waitstate - isdmgblockedbydisguise + tryselfconfusiondmgformchange healthbarupdate BS_ATTACKER, PASSIVE_HP_UPDATE datahpupdate BS_ATTACKER, PASSIVE_HP_UPDATE resultmessage @@ -7039,10 +7003,9 @@ BattleScript_BattleBondActivatesOnMoveEndAttacker:: call BattleScript_AbilityPopUp printstring STRINGID_ATTACKERBECAMEFULLYCHARGED handleformchange BS_ATTACKER, 0 - handleformchange BS_ATTACKER, 1 playanimation BS_ATTACKER, B_ANIM_FORM_CHANGE waitanimation - handleformchange BS_ATTACKER, 2 + handleformchange BS_ATTACKER, 1 printstring STRINGID_ATTACKERBECAMEASHSPECIES return @@ -7356,112 +7319,112 @@ BattleScript_FlushMessageBox:: flushtextbox return -@BattleScript_PalacePrintFlavorText:: -@ setbyte gBattleCommunication + 1, 0 -@BattleScript_PalaceTryBattlerFlavorText:: -@ palaceflavortext -@ jumpifbyte CMP_NOT_EQUAL, gBattleCommunication, TRUE, BattleScript_PalaceEndFlavorText -@ printfromtable gBattlePalaceFlavorTextTable -@ waitmessage B_WAIT_TIME_LONG -@BattleScript_PalaceEndFlavorText:: -@ addbyte gBattleCommunication + 1, 1 -@ jumpifbytenotequal gBattleCommunication + 1, gBattlersCount, BattleScript_PalaceTryBattlerFlavorText -@ setbyte gBattleCommunication, 0 -@ setbyte gBattleCommunication + 1, 0 -@ end2 -@ -@BattleScript_ArenaTurnBeginning:: -@ waitcry -@ volumedown -@ playse SE_ARENA_TIMEUP1 -@ pause 8 -@ playse SE_ARENA_TIMEUP1 -@ drawarenareftextbox -@ arenajudgmentstring B_MSG_REF_COMMENCE_BATTLE -@ arenawaitmessage -@ pause B_WAIT_TIME_LONG -@ erasearenareftextbox -@ volumeup -@ end2 -@ -@BattleScript_ArenaDoJudgment:: -@ makevisible BS_PLAYER1 -@ waitstate -@ makevisible BS_OPPONENT1 -@ waitstate -@ volumedown -@ playse SE_ARENA_TIMEUP1 -@ pause 8 -@ playse SE_ARENA_TIMEUP1 -@ pause B_WAIT_TIME_LONG -@ drawarenareftextbox -@ arenajudgmentstring B_MSG_REF_THATS_IT -@ arenawaitmessage -@ pause B_WAIT_TIME_LONG -@ setbyte gBattleCommunication, 0 @ Reset state for arenajudgmentwindow -@ arenajudgmentwindow -@ pause B_WAIT_TIME_LONG -@ arenajudgmentwindow -@ arenajudgmentstring B_MSG_REF_JUDGE_MIND -@ arenawaitmessage -@ arenajudgmentwindow -@ arenajudgmentstring B_MSG_REF_JUDGE_SKILL -@ arenawaitmessage -@ arenajudgmentwindow -@ arenajudgmentstring B_MSG_REF_JUDGE_BODY -@ arenawaitmessage -@ arenajudgmentwindow -@ jumpifbyte CMP_EQUAL, gBattleCommunication + 1, ARENA_RESULT_PLAYER_LOST, BattleScript_ArenaJudgmentPlayerLoses -@ jumpifbyte CMP_EQUAL, gBattleCommunication + 1, ARENA_RESULT_TIE, BattleScript_ArenaJudgmentDraw -@@ ARENA_RESULT_PLAYER_WON -@ arenajudgmentstring B_MSG_REF_PLAYER_WON -@ arenawaitmessage -@ arenajudgmentwindow -@ erasearenareftextbox -@ printstring STRINGID_DEFEATEDOPPONENTBYREFEREE -@ waitmessage B_WAIT_TIME_LONG -@ playfaintcry BS_OPPONENT1 -@ waitcry -@ dofaintanimation BS_OPPONENT1 -@ cleareffectsonfaint BS_OPPONENT1 -@ waitanimation -@ arenaopponentmonlost -@ end2 -@ -@BattleScript_ArenaJudgmentPlayerLoses: -@ arenajudgmentstring B_MSG_REF_OPPONENT_WON -@ arenawaitmessage -@ arenajudgmentwindow -@ erasearenareftextbox -@ printstring STRINGID_LOSTTOOPPONENTBYREFEREE -@ waitmessage B_WAIT_TIME_LONG -@ playfaintcry BS_PLAYER1 -@ waitcry -@ dofaintanimation BS_PLAYER1 -@ cleareffectsonfaint BS_PLAYER1 -@ waitanimation -@ arenaplayermonlost -@ end2 -@ -@BattleScript_ArenaJudgmentDraw: -@ arenajudgmentstring B_MSG_REF_DRAW -@ arenawaitmessage -@ arenajudgmentwindow -@ erasearenareftextbox -@ printstring STRINGID_TIEDOPPONENTBYREFEREE -@ waitmessage B_WAIT_TIME_LONG -@ arenabothmonslost -@ playfaintcry BS_PLAYER1 -@ waitcry -@ dofaintanimation BS_PLAYER1 -@ cleareffectsonfaint BS_PLAYER1 -@ waitanimation -@ playfaintcry BS_OPPONENT1 -@ waitcry -@ dofaintanimation BS_OPPONENT1 -@ cleareffectsonfaint BS_OPPONENT1 -@ waitanimation -@ end2 +BattleScript_PalacePrintFlavorText:: + setbyte gBattleCommunication + 1, 0 +BattleScript_PalaceTryBattlerFlavorText:: + palaceflavortext + jumpifbyte CMP_NOT_EQUAL, gBattleCommunication, TRUE, BattleScript_PalaceEndFlavorText + printfromtable gBattlePalaceFlavorTextTable + waitmessage B_WAIT_TIME_LONG +BattleScript_PalaceEndFlavorText:: + addbyte gBattleCommunication + 1, 1 + jumpifbytenotequal gBattleCommunication + 1, gBattlersCount, BattleScript_PalaceTryBattlerFlavorText + setbyte gBattleCommunication, 0 + setbyte gBattleCommunication + 1, 0 + end2 + +BattleScript_ArenaTurnBeginning:: + waitcry + volumedown + playse SE_ARENA_TIMEUP1 + pause 8 + playse SE_ARENA_TIMEUP1 + drawarenareftextbox + arenajudgmentstring B_MSG_REF_COMMENCE_BATTLE + arenawaitmessage + pause B_WAIT_TIME_LONG + erasearenareftextbox + volumeup + end2 + +BattleScript_ArenaDoJudgment:: + makevisible BS_PLAYER1 + waitstate + makevisible BS_OPPONENT1 + waitstate + volumedown + playse SE_ARENA_TIMEUP1 + pause 8 + playse SE_ARENA_TIMEUP1 + pause B_WAIT_TIME_LONG + drawarenareftextbox + arenajudgmentstring B_MSG_REF_THATS_IT + arenawaitmessage + pause B_WAIT_TIME_LONG + setbyte gBattleCommunication, 0 @ Reset state for arenajudgmentwindow + arenajudgmentwindow + pause B_WAIT_TIME_LONG + arenajudgmentwindow + arenajudgmentstring B_MSG_REF_JUDGE_MIND + arenawaitmessage + arenajudgmentwindow + arenajudgmentstring B_MSG_REF_JUDGE_SKILL + arenawaitmessage + arenajudgmentwindow + arenajudgmentstring B_MSG_REF_JUDGE_BODY + arenawaitmessage + arenajudgmentwindow + jumpifbyte CMP_EQUAL, gBattleCommunication + 1, ARENA_RESULT_PLAYER_LOST, BattleScript_ArenaJudgmentPlayerLoses + jumpifbyte CMP_EQUAL, gBattleCommunication + 1, ARENA_RESULT_TIE, BattleScript_ArenaJudgmentDraw +@ ARENA_RESULT_PLAYER_WON + arenajudgmentstring B_MSG_REF_PLAYER_WON + arenawaitmessage + arenajudgmentwindow + erasearenareftextbox + printstring STRINGID_DEFEATEDOPPONENTBYREFEREE + waitmessage B_WAIT_TIME_LONG + playfaintcry BS_OPPONENT1 + waitcry + dofaintanimation BS_OPPONENT1 + cleareffectsonfaint BS_OPPONENT1 + waitanimation + arenaopponentmonlost + end2 + +BattleScript_ArenaJudgmentPlayerLoses: + arenajudgmentstring B_MSG_REF_OPPONENT_WON + arenawaitmessage + arenajudgmentwindow + erasearenareftextbox + printstring STRINGID_LOSTTOOPPONENTBYREFEREE + waitmessage B_WAIT_TIME_LONG + playfaintcry BS_PLAYER1 + waitcry + dofaintanimation BS_PLAYER1 + cleareffectsonfaint BS_PLAYER1 + waitanimation + arenaplayermonlost + end2 + +BattleScript_ArenaJudgmentDraw: + arenajudgmentstring B_MSG_REF_DRAW + arenawaitmessage + arenajudgmentwindow + erasearenareftextbox + printstring STRINGID_TIEDOPPONENTBYREFEREE + waitmessage B_WAIT_TIME_LONG + arenabothmonslost + playfaintcry BS_PLAYER1 + waitcry + dofaintanimation BS_PLAYER1 + cleareffectsonfaint BS_PLAYER1 + waitanimation + playfaintcry BS_OPPONENT1 + waitcry + dofaintanimation BS_OPPONENT1 + cleareffectsonfaint BS_OPPONENT1 + waitanimation + end2 BattleScript_AskIfWantsToForfeitMatch:: printselectionstring STRINGID_QUESTIONFORFEITMATCH diff --git a/data/event_scripts.s b/data/event_scripts.s index cf981aafb..7dfb4f6d7 100644 --- a/data/event_scripts.s +++ b/data/event_scripts.s @@ -16,6 +16,7 @@ #include "constants/event_objects.h" #include "constants/event_object_movement.h" #include "constants/field_move.h" +#include "constants/field_poison.h" #include "constants/frontier_util.h" #include "constants/decorations.h" #include "constants/item.h" diff --git a/data/scripts/test.inc b/data/scripts/test.inc index d95cf7274..be6850481 100644 --- a/data/scripts/test.inc +++ b/data/scripts/test.inc @@ -1,30 +1,21 @@ -Test_EventScript_NPC:: - msgbox Test_Text_WelcomeToWorldOfPokemon, MSGBOX_NPC - end - EventScript_TestSignpostMsg:: msgbox Test_Text_ThisIsASignpost, MSGBOX_SIGN end -Test_EventScript_CoordEvent:: - msgbox Test_Text_ThisIsACoordEvent, MSGBOX_SIGN - end - -Test_Text_WelcomeToWorldOfPokemon:: - .string "テストよう メッセージです!\n" - .string "ポケモンの せかいへ ようこそ!$" - Test_Text_ThisIsASignpost:: .string "テストよう メッセージです!\n" .string "かんばん です$" -Test_Text_ThisIsACoordEvent:: - .string "テストよう メッセージです!\n" - .string "ざひょう チェックの イベントです$" +EventScript_TryGetTrainerScript:: + special ShouldTryGetTrainerScript + goto_if_eq VAR_RESULT, TRUE, EventScript_GotoTrainerScript + releaseall + end - .align 2 -Test_Text_Empty:: - .string "$" +EventScript_GotoTrainerScript:: + gotobeatenscript + releaseall + end EventScript_ObjectApproachPlayer:: lock diff --git a/data/scripts/trainer_battle.inc b/data/scripts/trainer_battle.inc index eed8df8ac..f5a534e3b 100644 --- a/data/scripts/trainer_battle.inc +++ b/data/scripts/trainer_battle.inc @@ -14,10 +14,10 @@ EventScript_TryDoNormalTrainerBattle:: applymovement VAR_LAST_TALKED, Movement_RevealTrainer waitmovement 0 clearflag FLAG_SAFE_FOLLOWER_MOVEMENT - specialvar VAR_RESULT, Script_HasTrainerBeenFought + specialvar VAR_RESULT, GetTrainerFlag goto_if_ne VAR_RESULT, FALSE, EventScript_NoTrainerBattle special PlayTrainerEncounterMusic - special SetUpTrainerMovement + special SetTrainerFacingDirection goto EventScript_ShowTrainerIntroMsg EventScript_NoTrainerBattle:: @@ -27,12 +27,12 @@ EventScript_TryDoDoubleTrainerBattle:: lock faceplayer call EventScript_RevealTrainer - specialvar VAR_RESULT, Script_HasTrainerBeenFought + specialvar VAR_RESULT, GetTrainerFlag goto_if_ne VAR_RESULT, FALSE, EventScript_NoDoubleTrainerBattle special HasEnoughMonsForDoubleBattle goto_if_ne VAR_RESULT, PLAYER_HAS_TWO_USABLE_MONS, EventScript_NotEnoughMonsForDoubleBattle special PlayTrainerEncounterMusic - special SetUpTrainerMovement + special SetTrainerFacingDirection goto EventScript_ShowTrainerIntroMsg EventScript_NotEnoughMonsForDoubleBattle:: @@ -60,7 +60,7 @@ EventScript_TryDoRematchBattle:: specialvar VAR_RESULT, IsTrainerReadyForRematch goto_if_eq VAR_RESULT, FALSE, EventScript_NoRematchBattle special PlayTrainerEncounterMusic - special SetUpTrainerMovement + special SetTrainerFacingDirection special ShowTrainerIntroSpeech waitmessage waitbuttonpress @@ -79,7 +79,7 @@ EventScript_TryDoDoubleRematchBattle:: special HasEnoughMonsForDoubleBattle goto_if_ne VAR_RESULT, PLAYER_HAS_TWO_USABLE_MONS, EventScript_NotEnoughMonsForDoubleRematchBattle special PlayTrainerEncounterMusic - special SetUpTrainerMovement + special SetTrainerFacingDirection special ShowTrainerIntroSpeech waitmessage waitbuttonpress diff --git a/data/scripts/white_out.inc b/data/scripts/white_out.inc index 4aa6f9f19..2e8e24777 100644 --- a/data/scripts/white_out.inc +++ b/data/scripts/white_out.inc @@ -36,7 +36,8 @@ EventScript_FieldPoison:: textcolor NPC_TEXT_COLOR_NEUTRAL special TryFieldPoisonWhiteOut waitstate - goto_if_eq VAR_RESULT, TRUE, EventScript_FieldWhiteOut + goto_if_eq VAR_RESULT, FLDPSN_WHITEOUT, EventScript_FieldWhiteOut + goto_if_eq VAR_RESULT, FLDPSN_FRONTIER_WHITEOUT, EventScript_FrontierFieldWhiteOut releaseall end diff --git a/data/specials.inc b/data/specials.inc index f192467d8..694accd7f 100644 --- a/data/specials.inc +++ b/data/specials.inc @@ -69,7 +69,7 @@ gSpecials:: def_special GetTrainerBattleMode def_special ShowTrainerIntroSpeech def_special ShowTrainerCantBattleSpeech - def_special Script_HasTrainerBeenFought + def_special GetTrainerFlag def_special DoTrainerApproach def_special PlayTrainerEncounterMusic def_special ShouldTryRematchBattle @@ -315,8 +315,8 @@ gSpecials:: def_special StartGroudonKyogreBattle def_special BattleSetup_StartLegendaryBattle def_special StartRegiBattle - def_special SetUpTrainerMovement - def_special NullFieldSpecial + def_special SetTrainerFacingDirection + def_special ShouldTryGetTrainerScript def_special NullFieldSpecial def_special StartDroughtWeatherBlend def_special DoDiveWarp diff --git a/include/battle.h b/include/battle.h index c4768a856..4c6fc5d6b 100644 --- a/include/battle.h +++ b/include/battle.h @@ -6,6 +6,7 @@ #include "constants/battle_switch_in.h" #include "constants/abilities.h" #include "constants/battle.h" +#include "constants/battle_move_resolution.h" #include "constants/form_change_types.h" #include "constants/hold_effects.h" #include "constants/moves.h" @@ -28,26 +29,6 @@ #include "random.h" // for rng_value_t #include "trainer_slide.h" -// Helper for accessing command arguments and advancing gBattlescriptCurrInstr. -// -// For example accuracycheck is defined as: -// -// .macro accuracycheck failInstr:req, move:req -// .byte 0x1 -// .4byte \failInstr -// .2byte \move -// .endm -// -// Which corresponds to: -// -// CMD_ARGS(const u8 *failInstr, u16 move); -// -// The arguments can be accessed as cmd->failInstr and cmd->move. -// gBattlescriptCurrInstr = cmd->nextInstr; advances to the next instruction. -#define CMD_ARGS(...) const struct __attribute__((packed)) { u8 opcode; RECURSIVELY(R_FOR_EACH(APPEND_SEMICOLON, __VA_ARGS__)) const u8 nextInstr[0]; } *const cmd UNUSED = (const void *)gBattlescriptCurrInstr -#define VARIOUS_ARGS(...) CMD_ARGS(u8 battler, u8 id, ##__VA_ARGS__) -#define NATIVE_ARGS(...) CMD_ARGS(void (*func)(void), ##__VA_ARGS__) - // Used to exclude moves learned temporarily by Transform or Mimic #define MOVE_IS_PERMANENT(battler, moveSlot) \ (!(gBattleMons[battler].volatiles.transformed) \ @@ -555,7 +536,7 @@ struct EventStates enum FirstTurnEventsStates beforeFirstTurn:8; enum FaintedActions faintedAction:8; enum BattlerId faintedActionBattler:4; - enum MoveSuccessOrder atkCanceler:8; + enum CancelerState atkCanceler:8; enum BattlerId atkCancelerBattler:4; enum BattleIntroStates battleIntro:8; enum SwitchInEvents switchIn:8; @@ -584,7 +565,7 @@ struct BattleStruct u8 expSentInMons; // As bits for player party mons - not including exp share mons. u8 wildVictorySong; enum Type dynamicMoveType; - u8 battlerPreventingSwitchout; + enum BattlerId battlerPreventingSwitchout; u8 moneyMultiplier:6; u8 moneyMultiplierItem:1; u8 moneyMultiplierMove:1; @@ -621,7 +602,6 @@ struct BattleStruct void (*savedCallback)(void); u16 chosenItem[MAX_BATTLERS_COUNT]; u16 choicedMove[MAX_BATTLERS_COUNT]; - u16 changedItems[MAX_BATTLERS_COUNT]; u8 switchInBattlerCounter; u16 lastTakenMoveFrom[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT]; // a 2-D array [target][attacker] union { @@ -657,9 +637,9 @@ struct BattleStruct u8 stolenStats[NUM_BATTLE_STATS]; // hp byte is used for which stats to raise, other inform about by how many stages enum Ability tracedAbility[MAX_BATTLERS_COUNT]; struct Illusion illusion[MAX_BATTLERS_COUNT]; - u8 soulheartBattlerId; - u8 friskedBattler; // Frisk needs to identify 2 battlers in double battles. - u8 quickClawBattlerId; + enum BattlerId soulheartBattlerId; + enum BattlerId friskedBattler; // Frisk needs to identify 2 battlers in double battles. + enum BattlerId quickClawBattlerId; struct LostItem itemLost[NUM_BATTLE_SIDES][PARTY_SIZE]; // Pokemon that had items consumed or stolen (two bytes per party member per side) u8 blunderPolicy:1; // should blunder policy activate u8 swapDamageCategory:1; // Photon Geyser, Shell Side Arm, Light That Burns the Sky @@ -825,7 +805,7 @@ struct BattleScripting u8 moveendState; u8 savedStatChanger; // For further use, if attempting to change stat two times(ex. Moody) u8 shiftSwitched; // When the game tells you the next enemy's pokemon and you switch. Option for noobs but oh well. - u8 battler; + enum BattlerId battler; u8 animTurn; u8 animTargetsHit; u8 statChanger; @@ -989,8 +969,8 @@ extern u8 gBattlersCount; extern u16 gBattlerPartyIndexes[MAX_BATTLERS_COUNT]; extern u8 gBattlerPositions[MAX_BATTLERS_COUNT]; extern u8 gActionsByTurnOrder[MAX_BATTLERS_COUNT]; -extern u8 gBattlerByTurnOrder[MAX_BATTLERS_COUNT]; -extern u8 gBattlersBySpeed[MAX_BATTLERS_COUNT]; +extern enum BattlerId gBattlerByTurnOrder[MAX_BATTLERS_COUNT]; +extern enum BattlerId gBattlersBySpeed[MAX_BATTLERS_COUNT]; extern u8 gCurrentTurnActionNumber; extern u8 gCurrentActionFuncId; extern struct BattlePokemon gBattleMons[MAX_BATTLERS_COUNT]; @@ -1003,11 +983,11 @@ extern u16 gCalledMove; extern s32 gBideDmg[MAX_BATTLERS_COUNT]; extern u16 gLastUsedItem; extern enum Ability gLastUsedAbility; -extern u8 gBattlerAttacker; -extern u8 gBattlerTarget; -extern u8 gBattlerFainted; -extern u8 gEffectBattler; -extern u8 gPotentialItemEffectBattler; +extern enum BattlerId gBattlerAttacker; +extern enum BattlerId gBattlerTarget; +extern enum BattlerId gBattlerFainted; +extern enum BattlerId gEffectBattler; +extern enum BattlerId gPotentialItemEffectBattler; extern u8 gAbsentBattlerFlags; extern u8 gMultiHitCounter; extern const u8 *gBattlescriptCurrInstr; @@ -1064,7 +1044,7 @@ extern u16 gMoveToLearn; extern u32 gFieldStatuses; extern struct FieldTimer gFieldTimers; extern u16 gBattleTurnCounter; -extern u8 gBattlerAbility; +extern enum BattlerId gBattlerAbility; extern struct QueuedStatBoost gQueuedStatBoosts[MAX_BATTLERS_COUNT]; extern MainCallback gPreBattleCallback1; @@ -1083,7 +1063,7 @@ extern u8 gPartyCriticalHits[PARTY_SIZE]; extern u8 gCategoryIconSpriteId; extern struct PokedudeBattlerState *gPokedudeBattlerStates[MAX_BATTLERS_COUNT]; -static inline bool32 IsBattlerAlive(u32 battler) +static inline bool32 IsBattlerAlive(enum BattlerId battler) { if (battler >= gBattlersCount) return FALSE; @@ -1095,24 +1075,24 @@ static inline bool32 IsBattlerAlive(u32 battler) return TRUE; } -static inline bool32 IsBattlerTurnDamaged(u32 battler) +static inline bool32 IsBattlerTurnDamaged(enum BattlerId battler) { return gSpecialStatuses[battler].damagedByAttack; } -static inline bool32 IsBattlerAtMaxHp(u32 battler) +static inline bool32 IsBattlerAtMaxHp(enum BattlerId battler) { return gBattleMons[battler].hp == gBattleMons[battler].maxHP; } -static inline enum BattlerPosition GetBattlerPosition(u32 battler) +static inline enum BattlerPosition GetBattlerPosition(enum BattlerId battler) { return gBattlerPositions[battler]; } -static inline u32 GetBattlerAtPosition(enum BattlerPosition position) +static inline enum BattlerId GetBattlerAtPosition(enum BattlerPosition position) { - u32 battler; + enum BattlerId battler; for (battler = 0; battler < gBattlersCount; battler++) { if (GetBattlerPosition(battler) == position) @@ -1121,37 +1101,37 @@ static inline u32 GetBattlerAtPosition(enum BattlerPosition position) return battler; } -static inline u32 GetPartnerBattler(u32 battler) +static inline u32 GetPartnerBattler(enum BattlerId battler) { return GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler))); } -static inline u32 GetOppositeBattler(u32 battler) +static inline u32 GetOppositeBattler(enum BattlerId battler) { return GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerPosition(battler))); } -static inline u32 GetBattlerSide(u32 battler) +static inline u32 GetBattlerSide(enum BattlerId battler) { return GetBattlerPosition(battler) & BIT_SIDE; } -static inline bool32 IsOnPlayerSide(u32 battler) +static inline bool32 IsOnPlayerSide(enum BattlerId battler) { return GetBattlerSide(battler) == B_SIDE_PLAYER; } -static inline bool32 IsBattlerAlly(u32 battlerAtk, u32 battlerDef) +static inline bool32 IsBattlerAlly(enum BattlerId battlerAtk, enum BattlerId battlerDef) { return GetBattlerSide(battlerAtk) == GetBattlerSide(battlerDef); } -static inline u32 GetOpposingSideBattler(u32 battler) +static inline u32 GetOpposingSideBattler(enum BattlerId battler) { return GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerSide(battler))); } -static inline struct Pokemon* GetBattlerMon(u32 battler) +static inline struct Pokemon* GetBattlerMon(enum BattlerId battler) { u32 index = gBattlerPartyIndexes[battler]; return !IsOnPlayerSide(battler) ? &gEnemyParty[index] : &gPlayerParty[index]; @@ -1162,12 +1142,12 @@ static inline struct Pokemon *GetSideParty(enum BattleSide side) return side == B_SIDE_PLAYER ? gPlayerParty : gEnemyParty; } -static inline struct Pokemon *GetBattlerParty(u32 battler) +static inline struct Pokemon *GetBattlerParty(enum BattlerId battler) { return GetSideParty(GetBattlerSide(battler)); } -static inline struct PartyState *GetBattlerPartyState(u32 battler) +static inline struct PartyState *GetBattlerPartyState(enum BattlerId battler) { return &gBattleStruct->partyState[GetBattlerSide(battler)][gBattlerPartyIndexes[battler]]; } @@ -1184,26 +1164,26 @@ static inline bool32 IsSpreadMove(enum MoveTarget moveTarget) return moveTarget == TARGET_BOTH || moveTarget == TARGET_FOES_AND_ALLY; } -static inline u32 GetChosenMoveFromPosition(u32 battler) +static inline u32 GetBattlerChosenMove(enum BattlerId battler) { return gBattleMons[battler].moves[gBattleStruct->chosenMovePositions[battler]]; } -static inline void SetPassiveDamageAmount(u32 battler, s32 value) +static inline void SetPassiveDamageAmount(enum BattlerId battler, s32 value) { if (value == 0) value = 1; gBattleStruct->passiveHpUpdate[battler] = value; } -static inline void SetHealAmount(u32 battler, s32 value) +static inline void SetHealAmount(enum BattlerId battler, s32 value) { if (value == 0) value = 1; gBattleStruct->passiveHpUpdate[battler] = -1 * value; } -static inline bool32 IsGhostBattleWithoutScope() +static inline bool32 IsGhostBattleWithoutScope(void) { return (gBattleTypeFlags & BATTLE_TYPE_GHOST) && !CheckBagHasItem(ITEM_SILPH_SCOPE, 1); } diff --git a/include/battle_ai_main.h b/include/battle_ai_main.h index a69069288..a5b7ed8e3 100644 --- a/include/battle_ai_main.h +++ b/include/battle_ai_main.h @@ -126,16 +126,16 @@ enum MoveComparisonResult void BattleAI_SetupItems(void); void BattleAI_SetupFlags(void); -void BattleAI_SetupAIData(u8 defaultScoreMoves, u32 battler); -void ComputeBattlerDecisions(u32 battler); -u32 BattleAI_ChooseMoveIndex(u32 battler); +void BattleAI_SetupAIData(u8 defaultScoreMoves, enum BattlerId battler); +void ComputeBattlerDecisions(enum BattlerId battler); +u32 BattleAI_ChooseMoveIndex(enum BattlerId battler); void Ai_InitPartyStruct(void); -void Ai_UpdateSwitchInData(u32 battler); -void Ai_UpdateFaintData(u32 battler); +void Ai_UpdateSwitchInData(enum BattlerId battler); +void Ai_UpdateFaintData(enum BattlerId battler); void SetAiLogicDataForTurn(struct AiLogicData *aiData); void ResetDynamicAiFunctions(void); -void AI_TrySwitchOrUseItem(u32 battler); -void CalcBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u32 battlerDef, u32 weather, u32 fieldStatus); +void AI_TrySwitchOrUseItem(enum BattlerId battler); +void CalcBattlerAiMovesData(struct AiLogicData *aiData, enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 weather, u32 fieldStatus); extern AiSwitchFunc gDynamicAiSwitchFunc; diff --git a/include/battle_ai_switch.h b/include/battle_ai_switch.h index d37ebf090..16b75c735 100644 --- a/include/battle_ai_switch.h +++ b/include/battle_ai_switch.h @@ -43,11 +43,11 @@ enum SwitchType SWITCH_MID_BATTLE_OPTIONAL, }; -u32 GetMostSuitableMonToSwitchInto(u32 battler, enum SwitchType switchType); -bool32 ShouldSwitch(u32 battler); -void ModifySwitchAfterMoveScoring(u32 battler); -u32 AI_SelectRevivalBlessingMon(u32 battler); -bool32 IsSwitchinValid(u32 battler); -bool32 IsAceMon(u32 battler, u32 monPartyId); +u32 GetMostSuitableMonToSwitchInto(enum BattlerId battler, enum SwitchType switchType); +bool32 ShouldSwitch(enum BattlerId battler); +void ModifySwitchAfterMoveScoring(enum BattlerId battler); +u32 AI_SelectRevivalBlessingMon(enum BattlerId battler); +bool32 IsSwitchinValid(enum BattlerId battler); +bool32 IsAceMon(enum BattlerId battler, u32 monPartyId); #endif // GUARD_BATTLE_AI_SWITCH_H diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index e79a2ff35..aeaccab19 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -77,154 +77,154 @@ static inline bool32 IsMoveUnusable(u32 moveIndex, enum Move move, u32 moveLimit typedef bool32 (*MoveFlag)(enum Move move); -bool32 AI_IsFaster(u32 battlerAi, u32 battlerDef, enum Move aiMove, enum Move playerMove, enum ConsiderPriority considerPriority); -bool32 AI_IsSlower(u32 battlerAi, u32 battlerDef, enum Move aiMove, enum Move playerMove, enum ConsiderPriority considerPriority); +bool32 AI_IsFaster(enum BattlerId battlerAi, enum BattlerId battlerDef, enum Move aiMove, enum Move playerMove, enum ConsiderPriority considerPriority); +bool32 AI_IsSlower(enum BattlerId battlerAi, enum BattlerId battlerDef, enum Move aiMove, enum Move playerMove, enum ConsiderPriority considerPriority); bool32 AI_RandLessThan(u32 val); -bool32 AI_IsBattlerGrounded(u32 battler); -enum MoveTarget AI_GetBattlerMoveTargetType(u32 battler, enum Move move); -enum Ability AI_GetMoldBreakerSanitizedAbility(u32 battlerAtk, enum Ability abilityAtk, enum Ability abilityDef, enum HoldEffect holdEffectDef, enum Move move); -u32 AI_GetDamage(u32 battlerAtk, u32 battlerDef, u32 moveIndex, enum DamageCalcContext calcContext, struct AiLogicData *aiData); +bool32 AI_IsBattlerGrounded(enum BattlerId battler); +enum MoveTarget AI_GetBattlerMoveTargetType(enum BattlerId battler, enum Move move); +enum Ability AI_GetMoldBreakerSanitizedAbility(enum BattlerId battlerAtk, enum Ability abilityAtk, enum Ability abilityDef, enum HoldEffect holdEffectDef, enum Move move); +u32 AI_GetDamage(enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 moveIndex, enum DamageCalcContext calcContext, struct AiLogicData *aiData); bool32 IsAiFlagPresent(u64 flag); -bool32 IsAiBattlerAware(u32 battlerId); -bool32 CanAiPredictMove(u32 battlerId); -bool32 IsAiBattlerAssumingStab(u32 battlerId); -bool32 IsAiBattlerAssumingStatusMoves(u32 battlerId); -bool32 IsAiBattlerPredictingAbility(u32 battlerId); +bool32 IsAiBattlerAware(enum BattlerId battlerId); +bool32 CanAiPredictMove(enum BattlerId battlerId); +bool32 IsAiBattlerAssumingStab(enum BattlerId battlerId); +bool32 IsAiBattlerAssumingStatusMoves(enum BattlerId battlerId); +bool32 IsAiBattlerPredictingAbility(enum BattlerId battlerId); bool32 ShouldRecordStatusMove(enum Move move); -void ClearBattlerMoveHistory(u32 battlerId); -void RecordLastUsedMoveBy(u32 battlerId, enum Move move); -void RecordAllMoves(u32 battler); -void RecordKnownMove(u32 battlerId, enum Move move); -void RecordAbilityBattle(u32 battlerId, enum Ability abilityId); -void ClearBattlerAbilityHistory(u32 battlerId); -void RecordItemEffectBattle(u32 battlerId, enum HoldEffect itemEffect); -void ClearBattlerItemEffectHistory(u32 battlerId); -void SaveBattlerData(u32 battlerId); -void SetBattlerData(u32 battlerId); -void SetBattlerAiData(u32 battlerId, struct AiLogicData *aiData); -void RestoreBattlerData(u32 battlerId); -enum Move GetAIChosenMove(u32 battlerId); +void ClearBattlerMoveHistory(enum BattlerId battlerId); +void RecordLastUsedMoveBy(enum BattlerId battlerId, enum Move move); +void RecordAllMoves(enum BattlerId battler); +void RecordKnownMove(enum BattlerId battlerId, enum Move move); +void RecordAbilityBattle(enum BattlerId battlerId, enum Ability abilityId); +void ClearBattlerAbilityHistory(enum BattlerId battlerId); +void RecordItemEffectBattle(enum BattlerId battlerId, enum HoldEffect itemEffect); +void ClearBattlerItemEffectHistory(enum BattlerId battlerId); +void SaveBattlerData(enum BattlerId battlerId); +void SetBattlerData(enum BattlerId battlerId); +void SetBattlerAiData(enum BattlerId battler, struct AiLogicData *aiData); +void RestoreBattlerData(enum BattlerId battlerId); +enum Move GetAIChosenMove(enum BattlerId battlerId); u32 GetTotalBaseStat(u32 species); -bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler); -bool32 AI_BattlerAtMaxHp(u32 battler); -u32 GetHealthPercentage(u32 battler); -bool32 AI_CanBattlerEscape(u32 battler); -bool32 IsBattlerTrapped(u32 battlerAtk, u32 battlerDef); -s32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler2, enum Move aiMoveConsidered, enum Move playerMoveConsidered, enum ConsiderPriority considerPriority); -bool32 CanTargetFaintAi(u32 battlerDef, u32 battlerAtk); -u32 NoOfHitsForTargetToFaintBattler(u32 battlerDef, u32 battlerAtk, enum AiConsiderEndure considerEndure); -void GetBestDmgMovesFromBattler(u32 battlerAtk, u32 battlerDef, enum DamageCalcContext calcContext, enum Move *bestMoves); -u32 GetMoveIndex(u32 battler, enum Move move); -bool32 IsBestDmgMove(u32 battlerAtk, u32 battlerDef, enum DamageCalcContext calcContext, enum Move move); -bool32 BestDmgMoveHasEffect(u32 battlerAtk, u32 battlerDef, enum DamageCalcContext calcContext, enum BattleMoveEffects moveEffect); -u32 GetBestDmgFromBattler(u32 battler, u32 battlerTarget, enum DamageCalcContext calcContext); -bool32 CanTargetMoveFaintAi(enum Move move, u32 battlerDef, u32 battlerAtk, u32 nHits); -bool32 CanTargetFaintAiWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod, s32 dmgMod); -enum Ability AI_DecideKnownAbilityForTurn(u32 battlerId); -enum HoldEffect AI_DecideHoldEffectForTurn(u32 battlerId); -bool32 DoesBattlerIgnoreAbilityChecks(u32 battlerAtk, enum Ability atkAbility, enum Move move); +bool32 IsTruantMonVulnerable(enum BattlerId battlerAI, enum BattlerId opposingBattler); +bool32 AI_BattlerAtMaxHp(enum BattlerId battler); +u32 GetHealthPercentage(enum BattlerId battler); +bool32 AI_CanBattlerEscape(enum BattlerId battler); +bool32 IsBattlerTrapped(enum BattlerId battlerAtk, enum BattlerId battlerDef); +s32 AI_WhoStrikesFirst(enum BattlerId battlerAI, enum BattlerId battler, enum Move aiMoveConsidered, enum Move playerMoveConsidered, enum ConsiderPriority considerPriority); +bool32 CanTargetFaintAi(enum BattlerId battlerDef, enum BattlerId battlerAtk); +u32 NoOfHitsForTargetToFaintBattler(enum BattlerId battlerDef, enum BattlerId battlerAtk, enum AiConsiderEndure considerEndure); +void GetBestDmgMovesFromBattler(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum DamageCalcContext calcContext, enum Move *bestMoves); +u32 GetMoveIndex(enum BattlerId battler, enum Move move); +bool32 IsBestDmgMove(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum DamageCalcContext calcContext, enum Move move); +bool32 BestDmgMoveHasEffect(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum DamageCalcContext calcContext, enum BattleMoveEffects moveEffect); +u32 GetBestDmgFromBattler(enum BattlerId battler, enum BattlerId battlerTarget, enum DamageCalcContext calcContext); +bool32 CanTargetMoveFaintAi(enum Move move, enum BattlerId battlerDef, enum BattlerId battlerAtk, u32 nHits); +bool32 CanTargetFaintAiWithMod(enum BattlerId battlerDef, enum BattlerId battlerAtk, s32 hpMod, s32 dmgMod); +enum Ability AI_DecideKnownAbilityForTurn(enum BattlerId battlerId); +enum HoldEffect AI_DecideHoldEffectForTurn(enum BattlerId battlerId); +bool32 DoesBattlerIgnoreAbilityChecks(enum BattlerId battlerAtk, enum Ability atkAbility, enum Move move); u32 AI_GetWeather(void); -u32 AI_GetSwitchinWeather(u32 battler); -u32 AI_GetSwitchinFieldStatus(u32 battler); +u32 AI_GetSwitchinWeather(enum BattlerId battler); +u32 AI_GetSwitchinFieldStatus(enum BattlerId battler); enum WeatherState IsWeatherActive(u32 flags); -bool32 CanAIFaintTarget(u32 battlerAtk, u32 battlerDef, u32 numHits); -bool32 CanIndexMoveFaintTarget(u32 battlerAtk, u32 battlerDef, u32 index, enum DamageCalcContext calcContext); -bool32 HasDamagingMove(u32 battler); -bool32 HasDamagingMoveOfType(u32 battler, enum Type type); -u32 GetBattlerSecondaryDamage(u32 battlerId); -bool32 BattlerWillFaintFromWeather(u32 battler, enum Ability ability); -bool32 BattlerWillFaintFromSecondaryDamage(u32 battler, enum Ability ability); -bool32 ShouldTryOHKO(u32 battlerAtk, u32 battlerDef, enum Ability atkAbility, enum Ability defAbility, enum Move move); -bool32 ShouldUseRecoilMove(u32 battlerAtk, u32 battlerDef, u32 recoilDmg, u32 moveIndex); -bool32 ShouldAbsorb(u32 battlerAtk, u32 battlerDef, enum Move move); -bool32 ShouldRecover(u32 battlerAtk, u32 battlerDef, enum Move move, u32 healPercent); -bool32 ShouldSetScreen(u32 battlerAtk, u32 battlerDef, enum BattleMoveEffects moveEffect); -bool32 ShouldCureStatus(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData); -bool32 ShouldCureStatusWithItem(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData); -enum AIPivot ShouldPivot(u32 battlerAtk, u32 battlerDef, enum Move move); +bool32 CanAIFaintTarget(enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 numHits); +bool32 CanIndexMoveFaintTarget(enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 index, enum DamageCalcContext calcContext); +bool32 HasDamagingMove(enum BattlerId battler); +bool32 HasDamagingMoveOfType(enum BattlerId battler, enum Type type); +u32 GetBattlerSecondaryDamage(enum BattlerId battlerId); +bool32 BattlerWillFaintFromWeather(enum BattlerId battler, enum Ability ability); +bool32 BattlerWillFaintFromSecondaryDamage(enum BattlerId battler, enum Ability ability); +bool32 ShouldTryOHKO(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability atkAbility, enum Ability defAbility, enum Move move); +bool32 ShouldUseRecoilMove(enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 recoilDmg, u32 moveIndex); +bool32 ShouldAbsorb(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move); +bool32 ShouldRecover(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, u32 healPercent); +bool32 ShouldSetScreen(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum BattleMoveEffects moveEffect); +bool32 ShouldCureStatus(enum BattlerId battlerAtk, enum BattlerId battlerDef, struct AiLogicData *aiData); +bool32 ShouldCureStatusWithItem(enum BattlerId battlerAtk, enum BattlerId battlerDef, struct AiLogicData *aiData); +enum AIPivot ShouldPivot(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move); bool32 IsRecycleEncouragedItem(enum Item item); -bool32 ShouldRestoreHpBerry(u32 battlerAtk, enum Item item); +bool32 ShouldRestoreHpBerry(enum BattlerId battlerAtk, enum Item item); bool32 IsStatBoostingBerry(enum Item item); -bool32 CanKnockOffItem(u32 fromBattler, u32 battler, enum Item item); +bool32 CanKnockOffItem(enum BattlerId fromBattler, enum BattlerId battler, enum Item item); bool32 IsAbilityOfRating(enum Ability ability, s32 rating); -bool32 AI_IsAbilityOnSide(u32 battlerId, enum Ability ability); -bool32 AI_MoveMakesContact(u32 battlerAtk, u32 battlerDef, enum Ability ability, enum HoldEffect holdEffect, enum Move move); -bool32 IsUnseenFistContactMove(u32 battlerAtk, u32 battlerDef, enum Move move); -bool32 IsConsideringZMove(u32 battlerAtk, u32 battlerDef, enum Move move); -bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, enum Move chosenMove); -void SetAIUsingGimmick(u32 battler, enum AIConsiderGimmick use); -bool32 IsAIUsingGimmick(u32 battler); -void DecideTerastal(u32 battler); -bool32 CanEndureHit(u32 battler, u32 battlerTarget, enum Move move); -bool32 ShouldFinalGambit(u32 battlerAtk, u32 battlerDef, bool32 aiIsFaster); -bool32 ShouldConsiderSelfSacrificeDamageEffect(u32 battlerAtk, u32 battlerDef, enum Move move, bool32 aiIsFaster); +bool32 AI_IsAbilityOnSide(enum BattlerId battlerId, enum Ability ability); +bool32 AI_MoveMakesContact(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability ability, enum HoldEffect holdEffect, enum Move move); +bool32 IsUnseenFistContactMove(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move); +bool32 IsConsideringZMove(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move); +bool32 ShouldUseZMove(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move chosenMove); +void SetAIUsingGimmick(enum BattlerId battler, enum AIConsiderGimmick use); +bool32 IsAIUsingGimmick(enum BattlerId battler); +void DecideTerastal(enum BattlerId battler); +bool32 CanEndureHit(enum BattlerId battler, enum BattlerId battlerTarget, enum Move move); +bool32 ShouldFinalGambit(enum BattlerId battlerAtk, enum BattlerId battlerDef, bool32 aiIsFaster); +bool32 ShouldConsiderSelfSacrificeDamageEffect(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, bool32 aiIsFaster); // stat stage checks -bool32 AnyStatIsRaised(u32 battlerId); -bool32 AnyUsefulStatIsRaised(u32 battlerId); -bool32 CanLowerStat(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData, enum Stat stat); -bool32 BattlerStatCanRise(u32 battler, enum Ability battlerAbility, enum Stat stat); -bool32 AreBattlersStatsMaxed(u32 battler); -u32 CountPositiveStatStages(u32 battlerId); -u32 CountNegativeStatStages(u32 battlerId); +bool32 AnyStatIsRaised(enum BattlerId battlerId); +bool32 AnyUsefulStatIsRaised(enum BattlerId battlerId); +bool32 CanLowerStat(enum BattlerId battlerAtk, enum BattlerId battlerDef, struct AiLogicData *aiData, enum Stat stat); +bool32 BattlerStatCanRise(enum BattlerId battler, enum Ability battlerAbility, enum Stat stat); +bool32 AreBattlersStatsMaxed(enum BattlerId battler); +u32 CountPositiveStatStages(enum BattlerId battlerId); +u32 CountNegativeStatStages(enum BattlerId battlerId); // move checks -bool32 Ai_IsPriorityBlocked(u32 battlerAtk, u32 battlerDef, enum Move move, struct AiLogicData *aiData); +bool32 Ai_IsPriorityBlocked(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, struct AiLogicData *aiData); bool32 AI_CanMoveBeBlockedByTarget(struct BattleContext *ctx); bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, enum DamageCategory category); -enum MoveComparisonResult CompareMoveEffects(enum Move move1, enum Move move2, u32 battlerAtk, u32 battlerDef, s32 noOfHitsToKo); -struct SimulatedDamage AI_CalcDamageSaveBattlers(enum Move move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, enum AIConsiderGimmick considerGimmickAtk, enum AIConsiderGimmick considerGimmickDef); -bool32 IsAdditionalEffectBlocked(u32 battlerAtk, u32 abilityAtk, u32 battlerDef, enum Ability abilityDef); -struct SimulatedDamage AI_CalcDamage(enum Move move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, enum AIConsiderGimmick considerGimmickAtk, enum AIConsiderGimmick considerGimmickDef, u32 weather, u32 fieldStatuses); -bool32 AI_IsDamagedByRecoil(u32 battler); +enum MoveComparisonResult CompareMoveEffects(enum Move move1, enum Move move2, enum BattlerId battlerAtk, enum BattlerId battlerDef, s32 noOfHitsToKo); +struct SimulatedDamage AI_CalcDamageSaveBattlers(enum Move move, enum BattlerId battlerAtk, enum BattlerId battlerDef, uq4_12_t *typeEffectiveness, enum AIConsiderGimmick considerGimmickAtk, enum AIConsiderGimmick considerGimmickDef); +bool32 IsAdditionalEffectBlocked(enum BattlerId battlerAtk, u32 abilityAtk, enum BattlerId battlerDef, enum Ability abilityDef); +struct SimulatedDamage AI_CalcDamage(enum Move move, enum BattlerId battlerAtk, enum BattlerId battlerDef, uq4_12_t *typeEffectiveness, enum AIConsiderGimmick considerGimmickAtk, enum AIConsiderGimmick considerGimmickDef, u32 weather, u32 fieldStatuses); +bool32 AI_IsDamagedByRecoil(enum BattlerId battler); u32 GetNoOfHitsToKO(u32 dmg, s32 hp); -u32 GetNoOfHitsToKOBattlerDmg(u32 dmg, u32 battlerDef); -u32 GetNoOfHitsToKOBattler(u32 battlerAtk, u32 battlerDef, u32 moveIndex, enum DamageCalcContext calcContext, enum AiConsiderEndure considerEndure); -u32 GetBestNoOfHitsToKO(u32 battlerAtk, u32 battlerDef, enum DamageCalcContext calcContext); -u32 GetCurrDamageHpPercent(u32 battlerAtk, u32 battlerDef, enum DamageCalcContext calcContext); -uq4_12_t AI_GetMoveEffectiveness(enum Move move, u32 battlerAtk, u32 battlerDef); -enum Move *GetMovesArray(u32 battler); +u32 GetNoOfHitsToKOBattlerDmg(u32 dmg, enum BattlerId battlerDef); +u32 GetNoOfHitsToKOBattler(enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 moveIndex, enum DamageCalcContext calcContext, enum AiConsiderEndure considerEndure); +u32 GetBestNoOfHitsToKO(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum DamageCalcContext calcContext); +u32 GetCurrDamageHpPercent(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum DamageCalcContext calcContext); +uq4_12_t AI_GetMoveEffectiveness(enum Move move, enum BattlerId battlerAtk, enum BattlerId battlerDef); +enum Move *GetMovesArray(enum BattlerId battler); bool32 IsConfusionMoveEffect(enum BattleMoveEffects moveEffect); -bool32 HasMove(u32 battlerId, enum Move move); -u32 GetBattlerMoveIndexWithEffect(u32 battler, enum BattleMoveEffects effect); -bool32 HasPhysicalBestMove(u32 battlerAtk, u32 battlerDef, enum DamageCalcContext calcContext); -bool32 HasOnlyMovesWithCategory(u32 battlerId, enum DamageCategory category, bool32 onlyOffensive); -bool32 HasMoveWithCategory(u32 battler, enum DamageCategory category); -bool32 HasMoveWithType(u32 battler, enum Type type); -bool32 HasMoveWithEffect(u32 battler, enum BattleMoveEffects moveEffect); -bool32 HasMoveWithAIEffect(u32 battler, u32 aiEffect); -bool32 HasBattlerSideMoveWithEffect(u32 battler, enum BattleMoveEffects effect); -bool32 HasBattlerSideMoveWithAIEffect(u32 battler, u32 effect); -bool32 HasBattlerSideUsedMoveWithEffect(u32 battler, enum BattleMoveEffects effect); -bool32 HasNonVolatileMoveEffect(u32 battlerId, enum MoveEffect effect); -bool32 IsPowerBasedOnStatus(u32 battlerId, enum BattleMoveEffects effect, u32 argument); -bool32 HasMoveWithAdditionalEffect(u32 battlerId, enum MoveEffect moveEffect); -bool32 HasBattlerSideMoveWithAdditionalEffect(u32 battler, enum MoveEffect moveEffect); -bool32 HasMoveWithCriticalHitChance(u32 battlerId); -bool32 HasMoveWithMoveEffectExcept(u32 battlerId, enum MoveEffect moveEffect, enum BattleMoveEffects exception); -bool32 HasMoveThatLowersOwnStats(u32 battlerId); -bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool32 ignoreStatus); -bool32 HasAnyKnownMove(u32 battlerId); +bool32 HasMove(enum BattlerId battlerId, enum Move move); +u32 GetBattlerMoveIndexWithEffect(enum BattlerId battler, enum BattleMoveEffects effect); +bool32 HasPhysicalBestMove(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum DamageCalcContext calcContext); +bool32 HasOnlyMovesWithCategory(enum BattlerId battlerId, enum DamageCategory category, bool32 onlyOffensive); +bool32 HasMoveWithCategory(enum BattlerId battler, enum DamageCategory category); +bool32 HasMoveWithType(enum BattlerId battler, enum Type type); +bool32 HasMoveWithEffect(enum BattlerId battler, enum BattleMoveEffects moveEffect); +bool32 HasMoveWithAIEffect(enum BattlerId battler, u32 aiEffect); +bool32 HasBattlerSideMoveWithEffect(enum BattlerId battler, enum BattleMoveEffects effect); +bool32 HasBattlerSideMoveWithAIEffect(enum BattlerId battler, u32 effect); +bool32 HasBattlerSideUsedMoveWithEffect(enum BattlerId battler, enum BattleMoveEffects effect); +bool32 HasNonVolatileMoveEffect(enum BattlerId battlerId, enum MoveEffect effect); +bool32 IsPowerBasedOnStatus(enum BattlerId battlerId, enum BattleMoveEffects effect, u32 argument); +bool32 HasMoveWithAdditionalEffect(enum BattlerId battlerId, enum MoveEffect moveEffect); +bool32 HasBattlerSideMoveWithAdditionalEffect(enum BattlerId battler, enum MoveEffect moveEffect); +bool32 HasMoveWithCriticalHitChance(enum BattlerId battlerId); +bool32 HasMoveWithMoveEffectExcept(enum BattlerId battlerId, enum MoveEffect moveEffect, enum BattleMoveEffects exception); +bool32 HasMoveThatLowersOwnStats(enum BattlerId battlerId); +bool32 HasMoveWithLowAccuracy(enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 accCheck, bool32 ignoreStatus); +bool32 HasAnyKnownMove(enum BattlerId battlerId); bool32 IsAromaVeilProtectedEffect(enum BattleMoveEffects moveEffect); bool32 IsNonVolatileStatusMove(enum Move move); -bool32 IsMoveRedirectionPrevented(u32 battlerAtk, enum Move move, enum Ability atkAbility); +bool32 IsMoveRedirectionPrevented(enum BattlerId battlerAtk, enum Move move, enum Ability atkAbility); bool32 IsHazardMove(enum Move move); -bool32 IsTwoTurnNotSemiInvulnerableMove(u32 battlerAtk, enum Move move); -bool32 IsBattlerDamagedByStatus(u32 battler); -s32 ProtectChecks(u32 battlerAtk, u32 battlerDef, enum Move move, enum Move predictedMove); -bool32 ShouldRaiseAnyStat(u32 battlerAtk, u32 battlerDef); -bool32 ShouldSetWeather(u32 battler, u32 weather); -bool32 ShouldClearWeather(u32 battler, u32 weather); -bool32 ShouldSetFieldStatus(u32 battler, u32 fieldStatus); -bool32 ShouldClearFieldStatus(u32 battler, u32 fieldStatus); -bool32 HasSleepMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef); -bool32 HasHealingEffect(u32 battler); +bool32 IsTwoTurnNotSemiInvulnerableMove(enum BattlerId battlerAtk, enum Move move); +bool32 IsBattlerDamagedByStatus(enum BattlerId battler); +s32 ProtectChecks(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, enum Move predictedMove); +bool32 ShouldRaiseAnyStat(enum BattlerId battlerAtk, enum BattlerId battlerDef); +bool32 ShouldSetWeather(enum BattlerId battler, u32 weather); +bool32 ShouldClearWeather(enum BattlerId battler, u32 weather); +bool32 ShouldSetFieldStatus(enum BattlerId battler, u32 fieldStatus); +bool32 ShouldClearFieldStatus(enum BattlerId battler, u32 fieldStatus); +bool32 HasSleepMoveWithLowAccuracy(enum BattlerId battlerAtk, enum BattlerId battlerDef); +bool32 HasHealingEffect(enum BattlerId battler); bool32 IsTrappingMove(enum Move move); -bool32 HasTrappingMoveEffect(u32 battler); -bool32 IsFlinchGuaranteed(u32 battlerAtk, u32 battlerDef, enum Move move); -bool32 HasChoiceEffect(u32 battler); -bool32 HasThawingMove(u32 battler); -bool32 HasUsableWhileAsleepMove(u32 battler); +bool32 HasTrappingMoveEffect(enum BattlerId battler); +bool32 IsFlinchGuaranteed(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move); +bool32 HasChoiceEffect(enum BattlerId battler); +bool32 HasThawingMove(enum BattlerId battler); +bool32 HasUsableWhileAsleepMove(enum BattlerId battler); bool32 IsStatRaisingEffect(enum BattleMoveEffects effect); bool32 IsStatLoweringEffect(enum BattleMoveEffects effect); bool32 IsSelfStatLoweringEffect(enum MoveEffect effect); @@ -233,107 +233,107 @@ bool32 IsSwitchOutEffect(enum BattleMoveEffects effect); bool32 IsChaseEffect(enum BattleMoveEffects effect); bool32 IsAttackBoostMoveEffect(enum BattleMoveEffects effect); bool32 IsUngroundingEffect(enum BattleMoveEffects effect); -bool32 HasMoveWithFlag(u32 battler, MoveFlag getFlag); +bool32 HasMoveWithFlag(enum BattlerId battler, MoveFlag getFlag); bool32 IsHazardClearingMove(enum Move move); bool32 IsSubstituteEffect(enum BattleMoveEffects effect); bool32 IsSelfSacrificeEffect(enum Move move); u32 GetAIExplosionChanceFromHP(u32 hpPercent); // status checks -bool32 AI_CanBeConfused(u32 battlerAtk, u32 battlerDef, enum Move move, enum Ability ability); -bool32 IsBattlerIncapacitated(u32 battler, enum Ability ability); -bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, enum Ability defAbility, enum Move move, enum Move partnerMove); -bool32 ShouldPoison(u32 battlerAtk, u32 battlerDef); -bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, enum Ability defAbility, enum Move move, enum Move partnerMove); -bool32 AI_CanParalyze(u32 battlerAtk, u32 battlerDef, enum Ability defAbility, enum Move move, enum Move partnerMove); -bool32 AI_CanConfuse(u32 battlerAtk, u32 battlerDef, enum Ability defAbility, u32 battlerAtkPartner, enum Move move, enum Move partnerMove); -bool32 ShouldBurn(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef); -bool32 ShouldFreezeOrFrostbite(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef); -bool32 ShouldParalyze(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef); -bool32 AI_CanBurn(u32 battlerAtk, u32 battlerDef, enum Ability defAbility, u32 battlerAtkPartner, enum Move move, enum Move partnerMove); -bool32 AI_CanGiveFrostbite(u32 battlerAtk, u32 battlerDef, enum Ability defAbility, u32 battlerAtkPartner, enum Move move, enum Move partnerMove); -bool32 AI_CanBeInfatuated(u32 battlerAtk, u32 battlerDef, enum Ability defAbility); -bool32 AnyPartyMemberStatused(u32 battlerId, bool32 checkSoundproof); -bool32 ShouldTryToFlinch(u32 battlerAtk, u32 battlerDef, enum Ability atkAbility, enum Ability defAbility, enum Move move); -bool32 ShouldTrap(u32 battlerAtk, u32 battlerDef, enum Move move); -bool32 IsWakeupTurn(u32 battler); -bool32 AI_IsBattlerAsleepOrComatose(u32 battlerId); +bool32 AI_CanBeConfused(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, enum Ability ability); +bool32 IsBattlerIncapacitated(enum BattlerId battler, enum Ability ability); +bool32 AI_CanPutToSleep(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability defAbility, enum Move move, enum Move partnerMove); +bool32 ShouldPoison(enum BattlerId battlerAtk, enum BattlerId battlerDef); +bool32 AI_CanPoison(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability defAbility, enum Move move, enum Move partnerMove); +bool32 AI_CanParalyze(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability defAbility, enum Move move, enum Move partnerMove); +bool32 AI_CanConfuse(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability defAbility, enum BattlerId battlerAtkPartner, enum Move move, enum Move partnerMove); +bool32 ShouldBurn(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityDef); +bool32 ShouldFreezeOrFrostbite(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityDef); +bool32 ShouldParalyze(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityDef); +bool32 AI_CanBurn(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability defAbility, enum BattlerId battlerAtkPartner, enum Move move, enum Move partnerMove); +bool32 AI_CanGiveFrostbite(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability defAbility, enum BattlerId battlerAtkPartner, enum Move move, enum Move partnerMove); +bool32 AI_CanBeInfatuated(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability defAbility); +bool32 AnyPartyMemberStatused(enum BattlerId battlerId, bool32 checkSoundproof); +bool32 ShouldTryToFlinch(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability atkAbility, enum Ability defAbility, enum Move move); +bool32 ShouldTrap(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move); +bool32 IsWakeupTurn(enum BattlerId battler); +bool32 AI_IsBattlerAsleepOrComatose(enum BattlerId battlerId); // ability logic bool32 IsMoxieTypeAbility(enum Ability ability); bool32 DoesAbilityRaiseStatsWhenLowered(enum Ability ability); -bool32 ShouldTriggerAbility(u32 battlerAtk, u32 battlerDef, enum Ability ability); -bool32 CanEffectChangeAbility(u32 battlerAtk, u32 battlerDef, enum Move move, struct AiLogicData *aiData); -void AbilityChangeScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *score, struct AiLogicData *aiData); -s32 BattlerBenefitsFromAbilityScore(u32 battler, enum Ability ability, struct AiLogicData *aiData); +bool32 ShouldTriggerAbility(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability ability); +bool32 CanEffectChangeAbility(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, struct AiLogicData *aiData); +void AbilityChangeScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 *score, struct AiLogicData *aiData); +enum AIScore BattlerBenefitsFromAbilityScore(enum BattlerId battler, enum Ability ability, struct AiLogicData *aiData); // partner logic -bool32 IsTargetingPartner(u32 battlerAtk, u32 battlerDef); +bool32 IsTargetingPartner(enum BattlerId battlerAtk, enum BattlerId battlerDef); // IsTargetingPartner includes a check to make sure the adjacent pokemon is truly a partner. -enum Move GetAllyChosenMove(u32 battlerId); +enum Move GetAllyChosenMove(enum BattlerId battlerId); bool32 IsBattle1v1(void); // IsBattle1v1 is distinct from !IsDoubleBattle. If the player is fighting Maxie and Tabitha, with Steven as their partner, and both Tabitha and Steven have run out of Pokemon, the battle is 1v1, even though mechanically it is a Double Battle for how battlers and flags are set. // Most AI checks should be using IsBattle1v1; most engine checks should be using !IsDoubleBattle -bool32 HasTwoOpponents(u32 battler); +bool32 HasTwoOpponents(enum BattlerId battler); // HasTwoOpponents checks if the opposing side has two pokemon. Partner state is irrelevant. e.g., Dragon Darts hits one time with two opponents and twice with one opponent. -bool32 HasPartner(u32 battler); -bool32 HasPartnerIgnoreFlags(u32 battler); +bool32 HasPartner(enum BattlerId battler); +bool32 HasPartnerIgnoreFlags(enum BattlerId battler); // HasPartner respects the Attacks Partner AI flag; HasPartnerIgnoreFlags checks only if a live pokemon is adjacent. -bool32 AreMovesEquivalent(u32 battlerAtk, u32 battlerAtkPartner, enum Move move, enum Move partnerMove); -bool32 DoesPartnerHaveSameMoveEffect(u32 battlerAtkPartner, u32 battlerDef, enum Move move, enum Move partnerMove); -bool32 PartnerMoveEffectIsStatusSameTarget(u32 battlerAtkPartner, u32 battlerDef, enum Move partnerMove); -bool32 PartnerMoveEffectIs(u32 battlerAtkPartner, enum Move partnerMove, enum BattleMoveEffects effectCheck); -bool32 PartnerMoveIs(u32 battlerAtkPartner, enum Move partnerMove, enum Move moveCheck); -bool32 PartnerMoveIsSameAsAttacker(u32 battlerAtkPartner, u32 battlerDef, enum Move move, enum Move partnerMove); -bool32 PartnerMoveIsSameNoTarget(u32 battlerAtkPartner, enum Move move, enum Move partnerMove); +bool32 AreMovesEquivalent(enum BattlerId battlerAtk, enum BattlerId battlerAtkPartner, enum Move move, enum Move partnerMove); +bool32 DoesPartnerHaveSameMoveEffect(enum BattlerId battlerAtkPartner, enum BattlerId battlerDef, enum Move move, enum Move partnerMove); +bool32 PartnerMoveEffectIsStatusSameTarget(enum BattlerId battlerAtkPartner, enum BattlerId battlerDef, enum Move partnerMove); +bool32 PartnerMoveEffectIs(enum BattlerId battlerAtkPartner, enum Move partnerMove, enum BattleMoveEffects effectCheck); +bool32 PartnerMoveIs(enum BattlerId battlerAtkPartner, enum Move partnerMove, enum Move moveCheck); +bool32 PartnerMoveIsSameAsAttacker(enum BattlerId battlerAtkPartner, enum BattlerId battlerDef, enum Move move, enum Move partnerMove); +bool32 PartnerMoveIsSameNoTarget(enum BattlerId battlerAtkPartner, enum Move move, enum Move partnerMove); bool32 PartnerMoveActivatesSleepClause(enum Move partnerMove); -bool32 ShouldUseWishAromatherapy(u32 battlerAtk, u32 battlerDef, enum Move move); -u32 GetFriendlyFireKOThreshold(u32 battler); -bool32 IsAllyProtectingFromMove(u32 battlerAtk, enum Move attackerMove, enum Move allyMove); +bool32 ShouldUseWishAromatherapy(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move); +u32 GetFriendlyFireKOThreshold(enum BattlerId battler); +bool32 IsAllyProtectingFromMove(enum BattlerId battlerAtk, enum Move attackerMove, enum Move allyMove); // party logic struct BattlePokemon *AllocSaveBattleMons(void); void FreeRestoreBattleMons(struct BattlePokemon *savedBattleMons); struct AiLogicData *AllocSaveAiLogicData(void); void FreeRestoreAiLogicData(struct AiLogicData *savedAiLogicData); -s32 CountUsablePartyMons(u32 battlerId); -bool32 IsPartyFullyHealedExceptBattler(u32 battler); -bool32 PartyHasMoveCategory(u32 battlerId, enum DamageCategory category); -bool32 SideHasMoveCategory(u32 battlerId, enum DamageCategory category); -void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId); -u32 GetActiveBattlerIds(u32 battler, u32 *battlerIn1, u32 *battlerIn2); -bool32 IsPartyMonOnFieldOrChosenToSwitch(u32 partyIndex, u32 battlerIn1, u32 battlerIn2); +s32 CountUsablePartyMons(enum BattlerId battlerId); +bool32 IsPartyFullyHealedExceptBattler(enum BattlerId battler); +bool32 PartyHasMoveCategory(enum BattlerId battlerId, enum DamageCategory category); +bool32 SideHasMoveCategory(enum BattlerId battlerId, enum DamageCategory category); +void GetAIPartyIndexes(enum BattlerId battlerId, s32 *firstId, s32 *lastId); +u32 GetActiveBattlerIds(enum BattlerId battler, enum BattlerId *battlerIn1, enum BattlerId *battlerIn2); +bool32 IsPartyMonOnFieldOrChosenToSwitch(u32 partyIndex, enum BattlerId battlerIn1, enum BattlerId battlerIn2); // score increases -enum AIScore IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, enum StatChange statId); -enum AIScore IncreaseStatUpScoreContrary(u32 battlerAtk, u32 battlerDef, enum StatChange statId); -enum AIScore IncreaseStatDownScore(u32 battlerAtk, u32 battlerDef, enum Stat stat); -void IncreasePoisonScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *score); -void IncreaseBurnScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *score); -void IncreaseParalyzeScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *score); -void IncreaseSleepScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *score); -void IncreaseConfusionScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *score); -void IncreaseFrostbiteScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *score); -bool32 HasHPForDamagingSetup(u32 battlerAtk, u32 battlerDef, u32 hpThreshold); +enum AIScore IncreaseStatUpScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum StatChange statId); +enum AIScore IncreaseStatUpScoreContrary(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum StatChange statId); +enum AIScore IncreaseStatDownScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Stat stat); +void IncreasePoisonScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 *score); +void IncreaseBurnScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 *score); +void IncreaseParalyzeScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 *score); +void IncreaseSleepScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 *score); +void IncreaseConfusionScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 *score); +void IncreaseFrostbiteScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 *score); +bool32 HasHPForDamagingSetup(enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 hpThreshold); -s32 AI_TryToClearStats(u32 battlerAtk, u32 battlerDef, bool32 isDoubleBattle); -bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef); -bool32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, enum Move move, struct AiLogicData *aiData); -void IncreaseTidyUpScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *score); -bool32 AI_ShouldSpicyExtract(u32 battlerAtk, u32 battlerAtkPartner, enum Move move, struct AiLogicData *aiData); -u32 IncreaseSubstituteMoveScore(u32 battlerAtk, u32 battlerDef, enum Move move); -bool32 IsBattlerItemEnabled(u32 battler); -bool32 IsBattlerPredictedToSwitch(u32 battler); -enum Move GetIncomingMove(u32 battler, u32 opposingBattler, struct AiLogicData *aiData); -enum Move GetIncomingMoveSpeedCheck(u32 battler, u32 opposingBattler, struct AiLogicData *aiData); +s32 AI_TryToClearStats(enum BattlerId battlerAtk, enum BattlerId battlerDef, bool32 isDoubleBattle); +bool32 AI_ShouldCopyStatChanges(enum BattlerId battlerAtk, enum BattlerId battlerDef); +bool32 AI_ShouldSetUpHazards(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, struct AiLogicData *aiData); +void IncreaseTidyUpScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 *score); +bool32 AI_ShouldSpicyExtract(enum BattlerId battlerAtk, enum BattlerId battlerAtkPartner, enum Move move, struct AiLogicData *aiData); +u32 IncreaseSubstituteMoveScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move); +bool32 IsBattlerItemEnabled(enum BattlerId battler); +bool32 IsBattlerPredictedToSwitch(enum BattlerId battler); +enum Move GetIncomingMove(enum BattlerId battler, enum BattlerId opposingBattler, struct AiLogicData *aiData); +enum Move GetIncomingMoveSpeedCheck(enum BattlerId battler, enum BattlerId opposingBattler, struct AiLogicData *aiData); bool32 IsNaturalEnemy(u32 speciesAttacker, u32 speciesTarget); -bool32 AI_OpponentCanFaintAiWithMod(u32 battler, u32 healAmount); -void SetBattlerFieldStatusForSwitchin(u32 battler); -bool32 ShouldInstructPartner(u32 battlerDef, enum Move move); -bool32 CanMoveBeBouncedBack(u32 battler, enum Move move); +bool32 AI_OpponentCanFaintAiWithMod(enum BattlerId battler, u32 healAmount); +void SetBattlerFieldStatusForSwitchin(enum BattlerId battler); +bool32 ShouldInstructPartner(enum BattlerId partner, enum Move move); +bool32 CanMoveBeBouncedBack(enum BattlerId battler, enum Move move); // Switching and item helpers -bool32 AiExpectsToFaintPlayer(u32 battler); +bool32 AiExpectsToFaintPlayer(enum BattlerId battler); // These are for the purpose of not doubling up on moves during double battles. // Used in GetAIEffectGroup for move effects and GetAIEffectGroupFromMove for additional effects diff --git a/include/battle_anim.h b/include/battle_anim.h index d050c0be1..15b9b561b 100644 --- a/include/battle_anim.h +++ b/include/battle_anim.h @@ -98,7 +98,7 @@ struct LinkBattleAnim u8 furyCutterCounter; u8 syrupBombIsShiny:1; u8 isTransformedMonShiny:1; - u8 padding:4; + u8 stockpileCounter:4; }; #define ANIM_ARGS_COUNT 8 diff --git a/include/battle_anim_scripts.h b/include/battle_anim_scripts.h index d614f20fa..b7d49bdd3 100644 --- a/include/battle_anim_scripts.h +++ b/include/battle_anim_scripts.h @@ -1013,6 +1013,8 @@ extern const u8 gBattleAnimGeneral_GhostGetOut[]; extern const u8 gBattleAnimGeneral_SilphScoped[]; extern const u8 gBattleAnimGeneral_SafariRockThrow[]; extern const u8 gBattleAnimGeneral_SafariReaction[]; +extern const u8 gBattleAnimGeneral_FormChangeInstant[]; +extern const u8 gBattleAnimGeneral_FormChangeDisguise[]; // special animations extern const u8 gBattleAnimSpecial_LevelUp[]; diff --git a/include/battle_bg.h b/include/battle_bg.h index a460989f1..002a2cf8c 100644 --- a/include/battle_bg.h +++ b/include/battle_bg.h @@ -19,6 +19,7 @@ void LoadBattleMenuWindowGfx(void); void LoadBattleTextboxAndBackground(void); void BattleInitBgsAndWindows(void); void DrawMainBattleBackground(void); +bool8 LoadChosenBattleElement(u8 caseId); void DrawTerrainTypeBattleBackground(void); const void* GetBattleBackgroundPalette(u16 terrain); diff --git a/include/battle_controllers.h b/include/battle_controllers.h index 47f9dde14..ce8031ccd 100644 --- a/include/battle_controllers.h +++ b/include/battle_controllers.h @@ -1,7 +1,7 @@ #ifndef GUARD_BATTLE_CONTROLLERS_H #define GUARD_BATTLE_CONTROLLERS_H -typedef void (*BattleControllerFunc)(u32 battler); +typedef void (*BattleControllerFunc)(enum BattlerId battler); enum { REQUEST_ALL_BATTLE, @@ -75,10 +75,11 @@ enum BattleController BATTLE_CONTROLLER_LINK_PARTNER, BATTLE_CONTROLLER_LINK_OPPONENT, BATTLE_CONTROLLER_SAFARI, - BATTLE_CONTROLLER_WALLY, BATTLE_CONTROLLER_RECORDED_PLAYER, BATTLE_CONTROLLER_RECORDED_PARTNER, BATTLE_CONTROLLER_RECORDED_OPPONENT, + BATTLE_CONTROLLER_OAK_OLD_MAN, + BATTLE_CONTROLLER_POKEDUDE, BATTLE_CONTROLLERS_COUNT, }; @@ -90,47 +91,47 @@ enum BattleController // (e.g. MarkBattlerForControllerExec) instead of using these macros // directly. -static inline void MarkBattleControllerActiveOnLocal(u32 battler) +static inline void MarkBattleControllerActiveOnLocal(enum BattlerId battler) { gBattleControllerExecFlags |= (1u << battler); } -static inline void MarkBattleControllerIdleOnLocal(u32 battler) +static inline void MarkBattleControllerIdleOnLocal(enum BattlerId battler) { gBattleControllerExecFlags &= ~(1u << battler); } -static inline bool32 IsBattleControllerActiveOnLocal(u32 battler) +static inline bool32 IsBattleControllerActiveOnLocal(enum BattlerId battler) { return gBattleControllerExecFlags & (1u << battler); } -static inline void MarkBattleControllerMessageOutboundOverLink(u32 battler) +static inline void MarkBattleControllerMessageOutboundOverLink(enum BattlerId battler) { gBattleControllerExecFlags |= ((1u << battler) << (32 - MAX_BATTLERS_COUNT)); } -static inline void MarkBattleControllerMessageSynchronizedOverLink(u32 battler) +static inline void MarkBattleControllerMessageSynchronizedOverLink(enum BattlerId battler) { gBattleControllerExecFlags &= ~((1 << 28) << (battler)); } -static inline bool32 IsBattleControllerMessageSynchronizedOverLink(u32 battler) +static inline bool32 IsBattleControllerMessageSynchronizedOverLink(enum BattlerId battler) { return gBattleControllerExecFlags & (1u << (battler + 28)); } -static inline void MarkBattleControllerActiveForPlayer(u32 battler, u32 playerId) +static inline void MarkBattleControllerActiveForPlayer(enum BattlerId battler, u32 playerId) { gBattleControllerExecFlags |= ((1u << battler) << ((playerId) << 2)); } -static inline void MarkBattleControllerIdleForPlayer(u32 battler, u32 playerId) +static inline void MarkBattleControllerIdleForPlayer(enum BattlerId battler, u32 playerId) { gBattleControllerExecFlags &= ~((1u << battler) << ((playerId) * 4)); } -static inline bool32 IsBattleControllerActiveForPlayer(u32 battler, u32 playerId) +static inline bool32 IsBattleControllerActiveForPlayer(enum BattlerId battler, u32 playerId) { return gBattleControllerExecFlags & ((1u << battler) << ((playerId) * 4)); } @@ -138,7 +139,7 @@ static inline bool32 IsBattleControllerActiveForPlayer(u32 battler, u32 playerId // This actually checks if a specific controller is active on any player or if // *any* controller is pending sync over link communications, but the macro name // can only be so specific before it just gets ridiculous. -static inline bool32 IsBattleControllerActiveOrPendingSyncAnywhere(u32 battler) +static inline bool32 IsBattleControllerActiveOrPendingSyncAnywhere(enum BattlerId battler) { return gBattleControllerExecFlags & ( (1u << battler) @@ -284,8 +285,8 @@ enum }; extern struct UnusedControllerStruct gUnusedControllerStruct; -extern void (*gBattlerControllerFuncs[MAX_BATTLERS_COUNT])(u32 battler); -extern void (*gBattlerControllerEndFuncs[MAX_BATTLERS_COUNT])(u32 battler); +extern void (*gBattlerControllerFuncs[MAX_BATTLERS_COUNT])(enum BattlerId battler); +extern void (*gBattlerControllerEndFuncs[MAX_BATTLERS_COUNT])(enum BattlerId battler); extern u8 gBattleControllerData[MAX_BATTLERS_COUNT]; extern u8 gBattlerBattleController[MAX_BATTLERS_COUNT]; @@ -295,178 +296,178 @@ void SetUpBattleVars(void); void InitBattleControllers(void); bool32 IsValidForBattle(struct Pokemon *mon); void TryReceiveLinkBattleData(void); -void PrepareBufferDataTransferLink(u32 battler, u32 bufferId, u16 size, u8 *data); -void UpdateFriendshipFromXItem(u32 battler); +void PrepareBufferDataTransferLink(enum BattlerId battler, u32 bufferId, u16 size, u8 *data); +void UpdateFriendshipFromXItem(enum BattlerId battler); bool32 IsAiVsAiBattle(void); -bool32 BattlerIsPlayer(u32 battlerId); -bool32 BattlerIsPartner(u32 battlerId); -bool32 BattlerIsOpponent(u32 battlerId); -bool32 BattlerIsRecorded(u32 battlerId); -bool32 BattlerIsLink(u32 battlerId); -bool32 BattlerIsWally(u32 battlerId); -bool32 BattlerHasAi(u32 battlerId); +bool32 BattlerIsPlayer(enum BattlerId battlerId); +bool32 BattlerIsPartner(enum BattlerId battlerId); +bool32 BattlerIsOpponent(enum BattlerId battlerId); +bool32 BattlerIsRecorded(enum BattlerId battlerId); +bool32 BattlerIsLink(enum BattlerId battlerId); +bool32 BattlerIsOldMan(enum BattlerId battlerId); +bool32 BattlerHasAi(enum BattlerId battlerId); // emitters -void BtlController_EmitGetMonData(u32 battler, u32 bufferId, u8 requestId, u8 monToCheck); -void BtlController_EmitSetMonData(u32 battler, u32 bufferId, u8 requestId, u8 monToCheck, u8 bytes, void *data); -void BtlController_EmitLoadMonSprite(u32 battler, u32 bufferId); -void BtlController_EmitSwitchInAnim(u32 battler, u32 bufferId, u8 partyId, bool8 dontClearTransform, bool8 dontClearSubstituteBit); -void BtlController_EmitReturnMonToBall(u32 battler, u32 bufferId, bool8 skipAnim); -void BtlController_EmitDrawTrainerPic(u32 battler, u32 bufferId); -void BtlController_EmitTrainerSlide(u32 battler, u32 bufferId); -void BtlController_EmitTrainerSlideBack(u32 battler, u32 bufferId); -void BtlController_EmitFaintAnimation(u32 battler, u32 bufferId); -void BtlController_EmitBallThrowAnim(u32 battler, u32 bufferId, u8 caseId); -void BtlController_EmitMoveAnimation(u32 battler, u32 bufferId, enum Move move, u8 turnOfMove, u16 movePower, s32 dmg, u8 friendship, u8 multihit); -void BtlController_EmitPrintString(u32 battler, u32 bufferId, enum StringID stringId); -void BtlController_EmitPrintSelectionString(u32 battler, u32 bufferId, enum StringID stringId); -void BtlController_EmitChooseAction(u32 battler, u32 bufferId, u8 action, enum Item itemId); -void BtlController_EmitYesNoBox(u32 battler, u32 bufferId); -void BtlController_EmitChooseMove(u32 battler, u32 bufferId, bool8 isDoubleBattle, bool8 NoPpNumber, struct ChooseMoveStruct *movePpData); -void BtlController_EmitChooseItem(u32 battler, u32 bufferId, u8 *battlePartyOrder); -void BtlController_EmitChoosePokemon(u32 battler, u32 bufferId, u8 caseId, u8 slotId, u16 abilityId, u8 battlerPreventingSwitchout, u8 *data); -void BtlController_EmitHealthBarUpdate(u32 battler, u32 bufferId, u16 hpValue); -void BtlController_EmitExpUpdate(u32 battler, u32 bufferId, u8 partyId, s32 expPoints); -void BtlController_EmitStatusIconUpdate(u32 battler, u32 bufferId, u32 status); -void BtlController_EmitStatusAnimation(u32 battler, u32 bufferId, bool8 isVolatile, u32 status); -void BtlController_EmitDataTransfer(u32 battler, u32 bufferId, u16 size, void *data); -void BtlController_EmitTwoReturnValues(u32 battler, u32 bufferId, u8 ret8, u32 ret32); -void BtlController_EmitChosenMonReturnValue(u32 battler, u32 bufferId, u8 partyId, u8 *battlePartyOrder); -void BtlController_EmitOneReturnValue(u32 battler, u32 bufferId, u16 ret); -void BtlController_EmitOneReturnValue_Duplicate(u32 battler, u32 bufferId, u16 ret); -void BtlController_EmitHitAnimation(u32 battler, u32 bufferId); -void BtlController_EmitCantSwitch(u32 battler, u32 bufferId); -void BtlController_EmitPlaySE(u32 battler, u32 bufferId, u16 songId); -void BtlController_EmitPlayFanfareOrBGM(u32 battler, u32 bufferId, u16 songId, bool8 playBGM); -void BtlController_EmitFaintingCry(u32 battler, u32 bufferId); -void BtlController_EmitIntroSlide(u32 battler, u32 bufferId, u8 terrainId); -void BtlController_EmitIntroTrainerBallThrow(u32 battler, u32 bufferId); -void BtlController_EmitDrawPartyStatusSummary(u32 battler, u32 bufferId, struct HpAndStatus *hpAndStatus, u8 flags); -void BtlController_EmitHidePartyStatusSummary(u32 battler, u32 bufferId); -void BtlController_EmitEndBounceEffect(u32 battler, u32 bufferId); -void BtlController_EmitSpriteInvisibility(u32 battler, u32 bufferId, bool8 isInvisible); -void BtlController_EmitBattleAnimation(u32 battler, u32 bufferId, u8 animationId, u16 argument); -void BtlController_EmitLinkStandbyMsg(u32 battler, u32 bufferId, u8 mode, bool32 record); -void BtlController_EmitResetActionMoveSelection(u32 battler, u32 bufferId, u8 caseId); -void BtlController_EmitEndLinkBattle(u32 battler, u32 bufferId, u8 battleOutcome); -void BtlController_EmitDebugMenu(u32 battler, u32 bufferId); +void BtlController_EmitGetMonData(enum BattlerId battler, u32 bufferId, u8 requestId, u8 monToCheck); +void BtlController_EmitSetMonData(enum BattlerId battler, u32 bufferId, u8 requestId, u8 monToCheck, u8 bytes, void *data); +void BtlController_EmitLoadMonSprite(enum BattlerId battler, u32 bufferId); +void BtlController_EmitSwitchInAnim(enum BattlerId battler, u32 bufferId, u8 partyId, bool8 dontClearTransform, bool8 dontClearSubstituteBit); +void BtlController_EmitReturnMonToBall(enum BattlerId battler, u32 bufferId, bool8 skipAnim); +void BtlController_EmitDrawTrainerPic(enum BattlerId battler, u32 bufferId); +void BtlController_EmitTrainerSlide(enum BattlerId battler, u32 bufferId); +void BtlController_EmitTrainerSlideBack(enum BattlerId battler, u32 bufferId); +void BtlController_EmitFaintAnimation(enum BattlerId battler, u32 bufferId); +void BtlController_EmitBallThrowAnim(enum BattlerId battler, u32 bufferId, u8 caseId); +void BtlController_EmitMoveAnimation(enum BattlerId battler, u32 bufferId, enum Move move, u8 turnOfMove, u16 movePower, s32 dmg, u8 friendship, u8 multihit); +void BtlController_EmitPrintString(enum BattlerId battler, u32 bufferId, enum StringID stringId); +void BtlController_EmitPrintSelectionString(enum BattlerId battler, u32 bufferId, enum StringID stringId); +void BtlController_EmitChooseAction(enum BattlerId battler, u32 bufferId, u8 action, enum Item itemId); +void BtlController_EmitYesNoBox(enum BattlerId battler, u32 bufferId); +void BtlController_EmitChooseMove(enum BattlerId battler, u32 bufferId, bool8 isDoubleBattle, bool8 NoPpNumber, struct ChooseMoveStruct *movePpData); +void BtlController_EmitChooseItem(enum BattlerId battler, u32 bufferId, u8 *battlePartyOrder); +void BtlController_EmitChoosePokemon(enum BattlerId battler, u32 bufferId, u8 caseId, u8 slotId, u16 abilityId, enum BattlerId battlerPreventingSwitchout, u8 *data); +void BtlController_EmitHealthBarUpdate(enum BattlerId battler, u32 bufferId, u16 hpValue); +void BtlController_EmitExpUpdate(enum BattlerId battler, u32 bufferId, u8 partyId, s32 expPoints); +void BtlController_EmitStatusIconUpdate(enum BattlerId battler, u32 bufferId, u32 status); +void BtlController_EmitStatusAnimation(enum BattlerId battler, u32 bufferId, bool8 isVolatile, u32 status); +void BtlController_EmitDataTransfer(enum BattlerId battler, u32 bufferId, u16 size, void *data); +void BtlController_EmitTwoReturnValues(enum BattlerId battler, u32 bufferId, u8 ret8, u32 ret32); +void BtlController_EmitChosenMonReturnValue(enum BattlerId battler, u32 bufferId, u8 partyId, u8 *battlePartyOrder); +void BtlController_EmitOneReturnValue(enum BattlerId battler, u32 bufferId, u16 ret); +void BtlController_EmitOneReturnValue_Duplicate(enum BattlerId battler, u32 bufferId, u16 ret); +void BtlController_EmitHitAnimation(enum BattlerId battler, u32 bufferId); +void BtlController_EmitCantSwitch(enum BattlerId battler, u32 bufferId); +void BtlController_EmitPlaySE(enum BattlerId battler, u32 bufferId, u16 songId); +void BtlController_EmitPlayFanfareOrBGM(enum BattlerId battler, u32 bufferId, u16 songId, bool8 playBGM); +void BtlController_EmitFaintingCry(enum BattlerId battler, u32 bufferId); +void BtlController_EmitIntroSlide(enum BattlerId battler, u32 bufferId, u8 terrainId); +void BtlController_EmitIntroTrainerBallThrow(enum BattlerId battler, u32 bufferId); +void BtlController_EmitDrawPartyStatusSummary(enum BattlerId battler, u32 bufferId, struct HpAndStatus *hpAndStatus, u8 flags); +void BtlController_EmitHidePartyStatusSummary(enum BattlerId battler, u32 bufferId); +void BtlController_EmitEndBounceEffect(enum BattlerId battler, u32 bufferId); +void BtlController_EmitSpriteInvisibility(enum BattlerId battler, u32 bufferId, bool8 isInvisible); +void BtlController_EmitBattleAnimation(enum BattlerId battler, u32 bufferId, u8 animationId, u16 argument); +void BtlController_EmitLinkStandbyMsg(enum BattlerId battler, u32 bufferId, u8 mode, bool32 record); +void BtlController_EmitResetActionMoveSelection(enum BattlerId battler, u32 bufferId, u8 caseId); +void BtlController_EmitEndLinkBattle(enum BattlerId battler, u32 bufferId, u8 battleOutcome); +void BtlController_EmitDebugMenu(enum BattlerId battler, u32 bufferId); -void BtlController_Complete(u32 battler); // Can be used for all the controllers. -void BtlController_Empty(u32 battler); // Empty command, does nothing, only completes the execution. -void BtlController_TerminatorNop(u32 battler); // Dummy function at the end of the table. -void BattleControllerDummy(u32 battler); -void StartSendOutAnim(u32 battler, bool32 dontClearTransform, bool32 dontClearSubstituteBit, bool32 doSlideIn); -void Controller_WaitForString(u32 battler); -void Controller_WaitForHealthBar(u32 battler); +void BtlController_Complete(enum BattlerId battler); // Can be used for all the controllers. +void BtlController_Empty(enum BattlerId battler); // Empty command, does nothing, only completes the execution. +void BtlController_TerminatorNop(enum BattlerId battler); // Dummy function at the end of the table. +void BattleControllerDummy(enum BattlerId battler); +void StartSendOutAnim(enum BattlerId battler, bool32 dontClearTransform, bool32 dontClearSubstituteBit, bool32 doSlideIn); +void Controller_WaitForString(enum BattlerId battler); +void Controller_WaitForHealthBar(enum BattlerId battler); // handlers -void BtlController_HandleGetMonData(u32 battler); -void BtlController_HandleGetRawMonData(u32 battler); -void BtlController_HandleSetMonData(u32 battler); -void BtlController_HandleSetRawMonData(u32 battler); -void BtlController_HandleLoadMonSprite(u32 battler); -void BtlController_HandleSwitchInAnim(u32 battler); -void BtlController_HandleReturnMonToBall(u32 battler); -void BtlController_HandleDrawTrainerPic(u32 battlerId, enum TrainerPicID trainerPicId, bool32 isFrontPic, s16 xPos, s16 yPos, s32 subpriority); -void BtlController_HandleTrainerSlide(u32 battler, enum TrainerPicID trainerPicId); -void BtlController_HandleTrainerSlideBack(u32 battlerId, s16 data0, bool32 startAnim); -void BtlController_HandleFaintAnimation(u32 battler); -void BtlController_HandleBallThrowAnim(u32 battler); -void BtlController_HandleMoveAnimation(u32 battler); -void BtlController_HandlePrintString(u32 battler); -void BtlController_HandlePrintStringPlayerOnly(u32 battler); -void BtlController_HandleHealthBarUpdate(u32 battler); -void BtlController_HandleStatusIconUpdate(u32 battler); -void BtlController_HandleStatusAnimation(u32 battler); -void BtlController_HandleHitAnimation(u32 battler); -void BtlController_HandlePlaySE(u32 battler); -void BtlController_HandlePlayFanfareOrBGM(u32 battler); -void BtlController_HandleFaintingCry(u32 battler); -void BtlController_HandleIntroSlide(u32 battler); -void BtlController_HandleSpriteInvisibility(u32 battler); -bool32 TwoPlayerIntroMons(u32 battlerId); // Double battle with both player pokemon active. -bool32 TwoOpponentIntroMons(u32 battlerId); // Double battle with both opponent pokemon active. -void BtlController_HandleIntroTrainerBallThrow(u32 battler, u16 tagTrainerPal, const u16 *trainerPal, s16 framesToWait, void (*controllerCallback)(u32 battler)); -void BtlController_HandleDrawPartyStatusSummary(u32 battler, enum BattleSide side, bool32 considerDelay); -void BtlController_HandleHidePartyStatusSummary(u32 battler); -void BtlController_HandleBattleAnimation(u32 battler); +void BtlController_HandleGetMonData(enum BattlerId battler); +void BtlController_HandleGetRawMonData(enum BattlerId battler); +void BtlController_HandleSetMonData(enum BattlerId battler); +void BtlController_HandleSetRawMonData(enum BattlerId battler); +void BtlController_HandleLoadMonSprite(enum BattlerId battler); +void BtlController_HandleSwitchInAnim(enum BattlerId battler); +void BtlController_HandleReturnMonToBall(enum BattlerId battler); +void BtlController_HandleDrawTrainerPic(enum BattlerId battlerId, enum TrainerPicID trainerPicId, bool32 isFrontPic, s16 xPos, s16 yPos, s32 subpriority); +void BtlController_HandleTrainerSlide(enum BattlerId battler, enum TrainerPicID trainerPicId); +void BtlController_HandleTrainerSlideBack(enum BattlerId battlerId, s16 data0, bool32 startAnim); +void BtlController_HandleFaintAnimation(enum BattlerId battler); +void BtlController_HandleBallThrowAnim(enum BattlerId battler); +void BtlController_HandleMoveAnimation(enum BattlerId battler); +void BtlController_HandlePrintString(enum BattlerId battler); +void BtlController_HandlePrintStringPlayerOnly(enum BattlerId battler); +void BtlController_HandleHealthBarUpdate(enum BattlerId battler); +void BtlController_HandleStatusIconUpdate(enum BattlerId battler); +void BtlController_HandleStatusAnimation(enum BattlerId battler); +void BtlController_HandleHitAnimation(enum BattlerId battler); +void BtlController_HandlePlaySE(enum BattlerId battler); +void BtlController_HandlePlayFanfareOrBGM(enum BattlerId battler); +void BtlController_HandleFaintingCry(enum BattlerId battler); +void BtlController_HandleIntroSlide(enum BattlerId battler); +void BtlController_HandleSpriteInvisibility(enum BattlerId battler); +bool32 TwoPlayerIntroMons(enum BattlerId battlerId); // Double battle with both player pokemon active. +bool32 TwoOpponentIntroMons(enum BattlerId battlerId); // Double battle with both opponent pokemon active. +void BtlController_HandleIntroTrainerBallThrow(enum BattlerId battler, u16 tagTrainerPal, const u16 *trainerPal, s16 framesToWait, void (*controllerCallback)(enum BattlerId battler)); +void BtlController_HandleDrawPartyStatusSummary(enum BattlerId battler, enum BattleSide side, bool32 considerDelay); +void BtlController_HandleHidePartyStatusSummary(enum BattlerId battler); +void BtlController_HandleBattleAnimation(enum BattlerId battler); // player controller -void SetControllerToPlayer(u32 battler); -void PlayerBufferExecCompleted(u32 battler); -void SetBattleEndCallbacks(u32 battler); -void PlayerHandleExpUpdate(u32 battler); +void SetControllerToPlayer(enum BattlerId battler); +void PlayerBufferExecCompleted(enum BattlerId battler); +void SetBattleEndCallbacks(enum BattlerId battler); +void PlayerHandleExpUpdate(enum BattlerId battler); enum TrainerPicID LinkPlayerGetTrainerPicId(u32 multiplayerId); void CB2_SetUpReshowBattleScreenAfterMenu(void); void CB2_SetUpReshowBattleScreenAfterMenu2(void); void Task_PlayerController_RestoreBgmAfterCry(u8 taskId); void ActionSelectionCreateCursorAt(u8 cursorPosition, u8 baseTileNum); void ActionSelectionDestroyCursorAt(u8 cursorPosition); -void InitMoveSelectionsVarsAndStrings(u32 battler); +void InitMoveSelectionsVarsAndStrings(enum BattlerId battler); void MoveSelectionCreateCursorAt(u8 cursorPos, u8 arg1); void MoveSelectionDestroyCursorAt(u8 cursorPosition); -void PlayerHandleChooseMove(u32 battler); -void HandleInputChooseMove(u32 battler); -void HandleInputChooseTarget(u32 battler); -void HandleInputShowEntireFieldTargets(u32 battler); -void HandleInputShowTargets(u32 battler); -void HandleMoveSwitching(u32 battler); -void HandleChooseMoveAfterDma3(u32 battler); +void PlayerHandleChooseMove(enum BattlerId battler); +void HandleInputChooseMove(enum BattlerId battler); +void HandleInputChooseTarget(enum BattlerId battler); +void HandleInputShowEntireFieldTargets(enum BattlerId battler); +void HandleInputShowTargets(enum BattlerId battler); +void HandleMoveSwitching(enum BattlerId battler); +void HandleChooseMoveAfterDma3(enum BattlerId battler); // recorded player controller -void SetControllerToRecordedPlayer(u32 battler); -void RecordedPlayerBufferExecCompleted(u32 battler); +void SetControllerToRecordedPlayer(enum BattlerId battler); +void RecordedPlayerBufferExecCompleted(enum BattlerId battler); // recorded partner controller -void SetControllerToRecordedPartner(u32 battler); -void RecordedPartnerBufferExecCompleted(u32 battler); +void SetControllerToRecordedPartner(enum BattlerId battler); +void RecordedPartnerBufferExecCompleted(enum BattlerId battler); // opponent controller -void SetControllerToOpponent(u32 battler); -void OpponentBufferExecCompleted(u32 battler); -void OpponentHandleTrainerSlide(u32 battler); +void SetControllerToOpponent(enum BattlerId battler); +void OpponentBufferExecCompleted(enum BattlerId battler); +void OpponentHandleTrainerSlide(enum BattlerId battler); // player partner controller -void Controller_PlayerPartnerShowIntroHealthbox(u32 battler); // Also used by the link partner. -void SetControllerToPlayerPartner(u32 battler); -void PlayerPartnerBufferExecCompleted(u32 battler); +void Controller_PlayerPartnerShowIntroHealthbox(enum BattlerId battler); // Also used by the link partner. +void SetControllerToPlayerPartner(enum BattlerId battler); +void PlayerPartnerBufferExecCompleted(enum BattlerId battler); // safari controller -void SetControllerToSafari(u32 battler); -void SafariBufferExecCompleted(u32 battler); +void SetControllerToSafari(enum BattlerId battler); +void SafariBufferExecCompleted(enum BattlerId battler); // recorded opponent controller -void SetControllerToRecordedOpponent(u32 battler); -void RecordedOpponentBufferExecCompleted(u32 battler); +void SetControllerToRecordedOpponent(enum BattlerId battler); +void RecordedOpponentBufferExecCompleted(enum BattlerId battler); // link opponent -void SetControllerToLinkOpponent(u32 battler); -void LinkOpponentBufferExecCompleted(u32 battler); +void SetControllerToLinkOpponent(enum BattlerId battler); +void LinkOpponentBufferExecCompleted(enum BattlerId battler); // link partner -void SetControllerToLinkPartner(u32 battler); -void LinkPartnerBufferExecCompleted(u32 battler); +void SetControllerToLinkPartner(enum BattlerId battler); +void LinkPartnerBufferExecCompleted(enum BattlerId battler); -void TrySetBattlerShadowSpriteCallback(u32 battler); +void TrySetBattlerShadowSpriteCallback(enum BattlerId battler); -void AnimateMonAfterPokeBallFail(u32 battler); -void TryShinyAnimAfterMonAnim(u32 battler); -void WaitForMonAnimAfterLoad(u32 battler); -void BtlController_HandleSwitchInWaitAndEnd(u32 battler); -void BtlController_Intro_DelayAndEnd(u32 battler); -void BtlController_HandleSwitchInShowHealthbox(u32 battler); -void BtlController_HandleSwitchInTryShinyAnim(u32 battler); -void BtlController_HandleSwitchInSoundAndEnd(u32 battler); -void BtlController_HandleSwitchInShowSubstitute(u32 battler); +void AnimateMonAfterPokeBallFail(enum BattlerId battler); +void TryShinyAnimAfterMonAnim(enum BattlerId battler); +void WaitForMonAnimAfterLoad(enum BattlerId battler); +void BtlController_HandleSwitchInWaitAndEnd(enum BattlerId battler); +void BtlController_Intro_DelayAndEnd(enum BattlerId battler); +void BtlController_HandleSwitchInShowHealthbox(enum BattlerId battler); +void BtlController_HandleSwitchInTryShinyAnim(enum BattlerId battler); +void BtlController_HandleSwitchInSoundAndEnd(enum BattlerId battler); +void BtlController_HandleSwitchInShowSubstitute(enum BattlerId battler); // oak and old man controller -void SetControllerToOakOrOldMan(u32 battler); -void OakOldManBufferExecCompleted(u32 battler); +void SetControllerToOakOrOldMan(enum BattlerId battler); +void OakOldManBufferExecCompleted(enum BattlerId battler); // pokedude controller -void SetControllerToPokedude(u32 battler); +void SetControllerToPokedude(enum BattlerId battler); void InitPokedudePartyAndOpponent(void); -void PokedudeBufferExecCompleted(u32 battler); +void PokedudeBufferExecCompleted(enum BattlerId battler); // These flags are set to signal that the indicated message // was already emitted @@ -482,41 +483,15 @@ void PokedudeBufferExecCompleted(u32 battler); bool8 BtlCtrl_OakOldMan_TestState2Flag(u8 mask); void BtlCtrl_OakOldMan_SetState2Flag(u8 mask); -void PrintOakText_InflictingDamageIsKey(u32 battler); -void PrintOakText_HowDisappointing(u32 battler); -void PrintOakText_OakNoRunningFromATrainer(u32 battler); -void OakOldManHandleInputChooseMove(u32 battler); +void PrintOakText_InflictingDamageIsKey(enum BattlerId battler); +void PrintOakText_HowDisappointing(enum BattlerId battler); +void PrintOakText_OakNoRunningFromATrainer(enum BattlerId battler); +void OakOldManHandleInputChooseMove(enum BattlerId battler); void BtlCtrl_DrawVoiceoverMessageFrame(void); void BtlCtrl_RemoveVoiceoverMessageFrame(void); -bool32 ShouldBattleRestrictionsApply(u32 battler); +bool32 ShouldBattleRestrictionsApply(enum BattlerId battler); void FreeShinyStars(void); enum BattleTrainer GetBattlerTrainer(enum BattlerId battler); - -// oak and old man controller -void SetControllerToOakOrOldMan(u32 battler); -void OakOldManBufferExecCompleted(u32 battler); - -// These flags are set to signal that the indicated message -// was already emitted - -// Inflicting damage is key -#define FIRST_BATTLE_MSG_FLAG_INFLICT_DMG 0x1 -// Lowering stats is advantageous -#define FIRST_BATTLE_MSG_FLAG_STAT_CHG 0x2 -// Keep an eye on your HP -#define FIRST_BATTLE_MSG_FLAG_HP_RESTORE 0x4 -// -#define FIRST_BATTLE_MSG_FLAG_PARTY_MENU 0x8 - -bool8 BtlCtrl_OakOldMan_TestState2Flag(u8 mask); -void BtlCtrl_OakOldMan_SetState2Flag(u8 mask); -void PrintOakText_InflictingDamageIsKey(u32 battler); -void PrintOakText_HowDisappointing(u32 battler); -void PrintOakText_OakNoRunningFromATrainer(u32 battler); -void OakOldManHandleInputChooseMove(u32 battler); -void BtlCtrl_DrawVoiceoverMessageFrame(void); -void BtlCtrl_RemoveVoiceoverMessageFrame(void); - #endif // GUARD_BATTLE_CONTROLLERS_H diff --git a/include/battle_dynamax.h b/include/battle_dynamax.h index 516d7db21..8ba4a99d6 100644 --- a/include/battle_dynamax.h +++ b/include/battle_dynamax.h @@ -3,16 +3,16 @@ #define DYNAMAX_TURNS_COUNT 3 -bool32 CanDynamax(u32 battler); -bool32 IsGigantamaxed(u32 battler); +bool32 CanDynamax(enum BattlerId battler); +bool32 IsGigantamaxed(enum BattlerId battler); void ApplyDynamaxHPMultiplier(struct Pokemon *mon); -void ActivateDynamax(u32 battler); -u32 GetNonDynamaxHP(u32 battler); -u32 GetNonDynamaxMaxHP(u32 battler); -void UndoDynamax(u32 battler); +void ActivateDynamax(enum BattlerId battler); +u32 GetNonDynamaxHP(enum BattlerId battler); +u32 GetNonDynamaxMaxHP(enum BattlerId battler); +void UndoDynamax(enum BattlerId battler); bool32 IsMoveBlockedByMaxGuard(enum Move move); -enum Move GetMaxMove(u32 battler, enum Move baseMove); +enum Move GetMaxMove(enum BattlerId battler, enum Move baseMove); u32 GetMaxMovePower(enum Move move); bool32 IsMaxMove(enum Move move); void ChooseDamageNonTypesString(enum Type type); diff --git a/include/battle_end_turn.h b/include/battle_end_turn.h index 468544cf2..d660000cb 100644 --- a/include/battle_end_turn.h +++ b/include/battle_end_turn.h @@ -1,7 +1,6 @@ #ifndef GUARD_BATTLE_END_TURN #define GUARD_BATTLE_END_TURN -u32 DoEndTurnEffects(void); +bool32 DoEndTurnEffects(void); #endif // GUARD_BATTLE_END_TURN - diff --git a/include/battle_gfx_sfx_util.h b/include/battle_gfx_sfx_util.h index 9b9c72d94..949c84a55 100644 --- a/include/battle_gfx_sfx_util.h +++ b/include/battle_gfx_sfx_util.h @@ -3,6 +3,7 @@ void AllocateBattleSpritesData(void); void FreeBattleSpritesData(void); +u16 ChooseMoveAndTargetInBattlePalace(enum BattlerId battler); void SpriteCB_WaitForBattlerBallReleaseAnim(struct Sprite *sprite); void SpriteCB_TrainerSlideIn(struct Sprite *sprite); void SpriteCB_TrainerSpawn(struct Sprite *sprite); @@ -22,7 +23,7 @@ bool8 BattleInitAllSprites(u8 *state, u8 *battlerId); void ClearSpritesHealthboxAnimData(void); void CopyAllBattleSpritesInvisibilities(void); void CopyBattleSpriteInvisibility(u8 battlerId); -void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bool32 trackEnemyPersonality, bool32 ghostUnveil); +void HandleSpeciesGfxDataChange(enum BattlerId battlerAtk, enum BattlerId battlerDef, u8 changeType); void BattleLoadSubstituteOrMonSpriteGfx(u8 battlerId, bool8 loadMonSprite); void LoadBattleMonGfxAndAnimate(u8 battlerId, bool8 loadMonSprite, u8 spriteId); void TrySetBehindSubstituteSpriteBit(u8 battlerId, u16 move); diff --git a/include/battle_gimmick.h b/include/battle_gimmick.h index 84b1d6b26..6322df2bd 100644 --- a/include/battle_gimmick.h +++ b/include/battle_gimmick.h @@ -19,32 +19,32 @@ struct GimmickInfo const struct SpriteTemplate *triggerTemplate; const u32 indicatorPalTag; const u8 *indicatorData; - bool32 (*CanActivate)(u32 battler); - void (*ActivateGimmick)(u32 battler); + bool32 (*CanActivate)(enum BattlerId battler); + void (*ActivateGimmick)(enum BattlerId battler); }; void AssignUsableGimmicks(void); -bool32 CanActivateGimmick(u32 battler, enum Gimmick gimmick); -bool32 IsGimmickSelected(u32 battler, enum Gimmick gimmick); -void SetActiveGimmick(u32 battler, enum Gimmick gimmick); -enum Gimmick GetActiveGimmick(u32 battler); -bool32 ShouldTrainerBattlerUseGimmick(u32 battler, enum Gimmick gimmick); -bool32 HasTrainerUsedGimmick(u32 battler, enum Gimmick gimmick); -void SetGimmickAsActivated(u32 battler, enum Gimmick gimmick); +bool32 CanActivateGimmick(enum BattlerId battler, enum Gimmick gimmick); +bool32 IsGimmickSelected(enum BattlerId battler, enum Gimmick gimmick); +void SetActiveGimmick(enum BattlerId battler, enum Gimmick gimmick); +enum Gimmick GetActiveGimmick(enum BattlerId battler); +bool32 ShouldTrainerBattlerUseGimmick(enum BattlerId battler, enum Gimmick gimmick); +bool32 HasTrainerUsedGimmick(enum BattlerId battler, enum Gimmick gimmick); +void SetGimmickAsActivated(enum BattlerId battler, enum Gimmick gimmick); void ChangeGimmickTriggerSprite(u32 spriteId, u32 animId); -void CreateGimmickTriggerSprite(u32 battler); +void CreateGimmickTriggerSprite(enum BattlerId battler); bool32 IsGimmickTriggerSpriteActive(void); -bool32 IsGimmickTriggerSpriteMatchingBattler(u32 battler); +bool32 IsGimmickTriggerSpriteMatchingBattler(enum BattlerId battler); void HideGimmickTriggerSprite(void); void DestroyGimmickTriggerSprite(void); void LoadIndicatorSpritesGfx(void); -u32 GetIndicatorPalTag(u32 battler); +u32 GetIndicatorPalTag(enum BattlerId battler); void UpdateIndicatorVisibilityAndType(u32 healthboxId, bool32 invisible); void UpdateIndicatorOamPriority(u32 healthboxId, u32 oamPriority); void UpdateIndicatorLevelData(u32 healthboxId, u32 level); -void CreateIndicatorSprite(u32 battler); +void CreateIndicatorSprite(enum BattlerId battler); extern const struct GimmickInfo gGimmicksInfo[]; diff --git a/include/battle_interface.h b/include/battle_interface.h index de91849db..0ae1f2433 100644 --- a/include/battle_interface.h +++ b/include/battle_interface.h @@ -119,7 +119,7 @@ void InitBattlerHealthboxCoords(u8 battler); void GetBattlerHealthboxCoords(u8 battler, s16 *x, s16 *y); void UpdateHpTextInHealthbox(u32 healthboxSpriteId, u32 maxOrCurrent, s16 currHp, s16 maxHp); void SwapHpBarsWithHpText(void); -u8 CreatePartyStatusSummarySprites(u8 battler, struct HpAndStatus *partyInfo, bool8 skipPlayer, bool8 isBattleStart); +u8 CreatePartyStatusSummarySprites(enum BattlerId battler, struct HpAndStatus *partyInfo, bool8 skipPlayer, bool8 isBattleStart); void Task_HidePartyStatusSummary(u8 taskId); void UpdateHealthboxAttribute(u8 healthboxSpriteId, struct Pokemon *mon, u8 elementId); s32 MoveBattleBar(u8 battler, u8 healthboxSpriteId, u8 whichBar, u8 unused); diff --git a/include/battle_main.h b/include/battle_main.h index e05d883c6..2257085da 100644 --- a/include/battle_main.h +++ b/include/battle_main.h @@ -82,31 +82,31 @@ void SpriteCB_HideAsMoveTarget(struct Sprite *sprite); void SpriteCB_OpponentMonFromBall(struct Sprite *sprite); void SpriteCB_BattleSpriteStartSlideLeft(struct Sprite *sprite); void SpriteCB_FaintSlideAnim(struct Sprite *sprite); -void DoBounceEffect(u8 battler, u8 which, s8 delta, s8 amplitude); -void EndBounceEffect(u8 battler, u8 which); +void DoBounceEffect(enum BattlerId battler, u8 which, s8 delta, s8 amplitude); +void EndBounceEffect(enum BattlerId battler, u8 which); void SpriteCB_PlayerMonFromBall(struct Sprite *sprite); void SpriteCB_PlayerMonSlideIn(struct Sprite *sprite); void SpriteCB_TrainerThrowObject(struct Sprite *sprite); void AnimSetCenterToCornerVecX(struct Sprite *sprite); void BeginBattleIntroDummy(void); void BeginBattleIntro(void); -void SwitchInClearSetData(u32 battler, struct Volatiles *volatilesCopy); -const u8 *FaintClearSetData(u32 battler); +void SwitchInClearSetData(enum BattlerId battler, struct Volatiles *volatilesCopy); +const u8 *FaintClearSetData(enum BattlerId battler); void BattleTurnPassed(void); -u8 IsRunningFromBattleImpossible(u32 battler); -void SwitchTwoBattlersInParty(u32 battler, u32 battler2); -void SwitchPartyOrder(u32 battler); +u8 IsRunningFromBattleImpossible(enum BattlerId battler); +void SwitchTwoBattlersInParty(enum BattlerId battler, enum BattlerId battler2); +void SwitchPartyOrder(enum BattlerId battler); void SwapTurnOrder(u8 id1, u8 id2); -u32 GetBattlerTotalSpeedStat(u32 battler, enum Ability ability, enum HoldEffect holdEffect); -s32 GetChosenMovePriority(u32 battler, enum Ability ability); -s32 GetBattleMovePriority(u32 battler, enum Ability ability, enum Move move); +u32 GetBattlerTotalSpeedStat(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect); +s32 GetChosenMovePriority(enum BattlerId battler, enum Ability ability); +s32 GetBattleMovePriority(enum BattlerId battler, enum Ability ability, enum Move move); s32 GetWhichBattlerFasterArgs(struct BattleCalcValues *calcValues, bool32 ignoreChosenMoves, u32 speedBattler1, u32 speedBattler2, s32 priority1, s32 priority2); s32 GetWhichBattlerFasterOrTies(struct BattleCalcValues *calcValues, bool32 ignoreChosenMoves); s32 GetWhichBattlerFaster(struct BattleCalcValues *calcValues, bool32 ignoreChosenMoves); void RunBattleScriptCommands_PopCallbacksStack(void); void RunBattleScriptCommands(void); -enum Type GetDynamicMoveType(struct Pokemon *mon, enum Move move, u32 battler, enum MonState monInBattle); -void SetTypeBeforeUsingMove(enum Move move, u32 battlerAtk); +enum Type GetDynamicMoveType(struct Pokemon *mon, enum Move move, enum BattlerId battler, enum MonState monInBattle); +void SetTypeBeforeUsingMove(enum Move move, enum BattlerId battler); bool32 IsWildMonSmart(void); u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer, u32 battleTypeFlags); void ModifyPersonalityForNature(u32 *personality, u32 newNature); diff --git a/include/battle_message.h b/include/battle_message.h index 2ad30a65f..21aae045b 100644 --- a/include/battle_message.h +++ b/include/battle_message.h @@ -271,6 +271,9 @@ extern const u8 *const gStatNamesTable2[]; extern const u16 gMissStringIds[]; +// battle main +extern const u8 gText_LinkStandby3[]; +extern const u8 BattleFrontier_BattleTowerBattleRoom_Text_RecordCouldntBeSaved[]; extern const u8 gText_Sleep[]; extern const u8 gText_Poison[]; extern const u8 gText_Burn[]; diff --git a/include/battle_move_resolution.h b/include/battle_move_resolution.h index 34c63f3c2..01ce81314 100644 --- a/include/battle_move_resolution.h +++ b/include/battle_move_resolution.h @@ -3,7 +3,8 @@ #include "constants/battle_move_resolution.h" -bool32 DoMoveEnd(enum MoveEndState endMode, enum MoveEndState endState); +enum CancelerResult DoAttackCanceler(void); +enum MoveEndResult DoMoveEnd(enum MoveEndState endMode, enum MoveEndState endState); void MoveValuesCleanUp(void); #endif // GUARD_BATTLE_MOVE_RESOLUTION_H diff --git a/include/battle_script_commands.h b/include/battle_script_commands.h index 23d0c0be6..d28f51105 100644 --- a/include/battle_script_commands.h +++ b/include/battle_script_commands.h @@ -40,33 +40,34 @@ union TRANSPARENT StatChangeFlags }; }; -bool32 HasBattlerActedThisTurn(u32 battler); -u32 GetBattlerTurnOrderNum(u32 battler); -bool32 NoAliveMonsForBattlerSide(u32 battler); +bool32 HasBattlerActedThisTurn(enum BattlerId battler); +u32 GetBattlerTurnOrderNum(enum BattlerId battler); +bool32 NoAliveMonsForBattlerSide(enum BattlerId battler); bool32 NoAliveMonsForPlayer(void); bool32 NoAliveMonsForEitherParty(void); -void SetMoveEffect(u32 battler, u32 effectBattler, enum MoveEffect moveEffect, const u8 *battleScript, enum SetMoveEffectFlags effectFlags); -bool32 CanBattlerSwitch(u32 battlerId); +void SetMoveEffect(enum BattlerId battlerAtk, enum BattlerId effectBattler, enum MoveEffect moveEffect, const u8 *battleScript, enum SetMoveEffectFlags effectFlags); +bool32 CanBattlerSwitch(enum BattlerId battlerId); void BattleDestroyYesNoCursorAt(u8 cursorPosition); void BattleCreateYesNoCursorAt(u8 cursorPosition); void BufferMoveToLearnIntoBattleTextBuff2(void); void HandleBattleWindow(u8 xStart, u8 yStart, u8 xEnd, u8 yEnd, u8 flags); -bool8 UproarWakeUpCheck(u8 battler); -bool32 DoesSubstituteBlockMove(u32 battlerAtk, u32 battlerDef, enum Move move); -bool32 DoesDisguiseBlockMove(u32 battler, enum Move move); -bool32 CanUseLastResort(u8 battlerId); -u32 IsFlowerVeilProtected(u32 battler); -u32 IsLeafGuardProtected(u32 battler, enum Ability ability); -bool32 IsShieldsDownProtected(u32 battler, enum Ability ability); -u32 IsAbilityStatusProtected(u32 battler, enum Ability ability); -bool32 TryResetBattlerStatChanges(u8 battler); -bool32 CanCamouflage(u8 battlerId); -void StealTargetItem(u8 battlerStealer, u8 battlerItem); +bool8 UproarWakeUpCheck(enum BattlerId battler); +bool32 DoesSubstituteBlockMove(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move); +bool32 DoesDisguiseBlockMove(enum BattlerId battler, enum Move move); +bool32 DoesIceFaceBlockMove(enum BattlerId battler, enum Move move); +bool32 CanUseLastResort(enum BattlerId battlerId); +u32 IsFlowerVeilProtected(enum BattlerId battler); +u32 IsLeafGuardProtected(enum BattlerId battler, enum Ability ability); +bool32 IsShieldsDownProtected(enum BattlerId battler, enum Ability ability); +u32 IsAbilityStatusProtected(enum BattlerId battler, enum Ability ability); +bool32 TryResetBattlerStatChanges(enum BattlerId battler); +bool32 CanCamouflage(enum BattlerId battler); +void StealTargetItem(enum BattlerId battlerStealer, enum BattlerId battlerItem); u8 GetCatchingBattler(void); -bool32 ProteanTryChangeType(u32 battler, enum Ability ability, enum Move move, enum Type moveType); -u8 GetFirstFaintedPartyIndex(u8 battlerId); -void SaveBattlerTarget(u32 battler); -void SaveBattlerAttacker(u32 battler); +bool32 ProteanTryChangeType(enum BattlerId battler, enum Ability ability, enum Move move, enum Type moveType); +u8 GetFirstFaintedPartyIndex(enum BattlerId battler); +void SaveBattlerTarget(enum BattlerId battler); +void SaveBattlerAttacker(enum BattlerId battler); bool32 CanBurnHitThaw(enum Move move); bool32 IsMonGettingExpSentOut(void); diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 9eb4b0df3..127fe7633 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -345,6 +345,8 @@ extern const u8 BattleScript_DancerActivates[]; extern const u8 BattleScript_AftermathDmg[]; extern const u8 BattleScript_BattlerFormChange[]; extern const u8 BattleScript_BattlerFormChangeEnd2[]; +extern const u8 BattleScript_BattlerFormChangeNoPopup[]; +extern const u8 BattleScript_BattlerFormChangeDisguise[]; extern const u8 BattleScript_AttackerFormChangeWithString[]; extern const u8 BattleScript_TargetFormChange[]; extern const u8 BattleScript_AnticipationActivates[]; @@ -397,7 +399,7 @@ extern const u8 BattleScript_DoesntAffectTargetAtkString[]; extern const u8 BattleScript_DoesntAffectScripting[]; extern const u8 BattleScript_GulpMissileGorging[]; extern const u8 BattleScript_GulpMissileGulping[]; -extern const u8 BattleScript_GulpMissileFormChange[]; +extern const u8 BattleScript_TwoTurnMovesSecondTurnFormChange[]; extern const u8 BattleScript_BattleBondActivatesOnMoveEndAttacker[]; extern const u8 BattleScript_EffectBattleBondStatIncrease[]; extern const u8 BattleScript_PrimalWeatherBlocksMove[]; @@ -514,6 +516,10 @@ extern const u8 BattleScript_Explosion[]; extern const u8 BattleScript_ActivateSwitchInAbility[]; extern const u8 BattleScript_BreakScreens[]; extern const u8 BattleScript_StealStats[]; +extern const u8 BattleScript_BeatUpAttackMessage[]; +extern const u8 BattleScript_MagnitudeMessage[]; +extern const u8 BattleScript_FickleBeamMessage[]; +extern const u8 BattleScript_BeforeSnoreMessage[]; // zmoves extern const u8 BattleScript_ZMoveActivateDamaging[]; @@ -587,7 +593,6 @@ extern const u8 BattleScript_EffectLightScreen[]; extern const u8 BattleScript_EffectRest[]; extern const u8 BattleScript_RestIsAlreadyAsleep[]; extern const u8 BattleScript_InsomniaProtects[]; -extern const u8 BattleScript_EffectOHKO[]; extern const u8 BattleScript_EffectHealBlock[]; extern const u8 BattleScript_RecoilIfMiss[]; extern const u8 BattleScript_EffectMist[]; @@ -619,7 +624,6 @@ extern const u8 BattleScript_EffectHappyHour[]; extern const u8 BattleScript_EffectDisable[]; extern const u8 BattleScript_EffectEncore[]; extern const u8 BattleScript_EffectPainSplit[]; -extern const u8 BattleScript_EffectSnore[]; extern const u8 BattleScript_EffectConversion2[]; extern const u8 BattleScript_EffectLockOn[]; extern const u8 BattleScript_EffectSketch[]; @@ -641,7 +645,6 @@ extern const u8 BattleScript_EffectSwagger[]; extern const u8 BattleScript_EffectAttract[]; extern const u8 BattleScript_EffectPresent[]; extern const u8 BattleScript_EffectSafeguard[]; -extern const u8 BattleScript_EffectMagnitude[]; extern const u8 BattleScript_EffectBatonPass[]; extern const u8 BattleScript_EffectCaptivate[]; extern const u8 BattleScript_EffectMorningSun[]; @@ -660,7 +663,6 @@ extern const u8 BattleScript_EffectSemiInvulnerable[]; extern const u8 BattleScript_EffectDefenseCurl[]; extern const u8 BattleScript_EffectSoftboiled[]; extern const u8 BattleScript_EffectStockpile[]; -extern const u8 BattleScript_EffectSpitUp[]; extern const u8 BattleScript_EffectSwallow[]; extern const u8 BattleScript_EffectOverwriteAbility[]; extern const u8 BattleScript_EffectTorment[]; @@ -696,7 +698,6 @@ extern const u8 BattleScript_EffectDragonDance[]; extern const u8 BattleScript_EffectCamouflage[]; extern const u8 BattleScript_EffectPledge[]; extern const u8 BattleScript_EffectFling[]; -extern const u8 BattleScript_EffectNaturalGift[]; extern const u8 BattleScript_EffectRoost[]; extern const u8 BattleScript_EffectGravity[]; extern const u8 BattleScript_EffectMiracleEye[]; @@ -809,7 +810,6 @@ extern const u8 BattleScript_EffectFilletAway[]; extern const u8 BattleScript_EffectShedTail[]; extern const u8 BattleScript_EffectTidyUp[]; extern const u8 BattleScript_EffectSpicyExtract[]; -extern const u8 BattleScript_EffectFickleBeam[]; extern const u8 BattleScript_GhostBallDodge[]; extern const u8 BattleScript_GhostGetOutGetOut[]; extern const u8 BattleScript_TooScaredToMove[]; @@ -818,7 +818,7 @@ extern const u8 BattleScript_SilphScopeUnveiled[]; // pokefirered -extern const u8 BattleScript_BattleTowerTrainerBattleWon[]; +extern const u8 BattleScript_TrainerTowerTrainerBattleWon[]; extern const u8 BattleScript_MoveUsedLoafingAroundMsg[]; extern const u8 BattleScript_OldMan_Pokedude_CaughtMessage[]; diff --git a/include/battle_setup.h b/include/battle_setup.h index 87fd47ef6..8a6a7ffea 100644 --- a/include/battle_setup.h +++ b/include/battle_setup.h @@ -1,6 +1,7 @@ #ifndef GUARD_BATTLE_SETUP_H #define GUARD_BATTLE_SETUP_H +#include "battle_transition.h" #include "vs_seeker.h" /* the layout of the first byte can be confusing here @@ -48,11 +49,11 @@ void BattleSetup_StartLatiBattle(void); void BattleSetup_StartLegendaryBattle(void); void StartGroudonKyogreBattle(void); void StartRegiBattle(void); -u8 BattleSetup_GetEnvironmentId(void); -u8 BattleSetup_GetBattleTowerBattleTransition(void); -u8 GetWildBattleTransition(void); -u8 GetTrainerBattleTransition(void); -u8 GetSpecialBattleTransition(s32 id); +enum BattleEnvironments BattleSetup_GetEnvironmentId(void); +enum BattleTransition GetWildBattleTransition(void); +enum BattleTransition GetTrainerBattleTransition(void); +enum BattleTransition GetSpecialBattleTransition(enum BattleTransitionGroup id); +void ChooseStarter(void); void ChooseStarter(void); void ResetTrainerOpponentIds(void); void SetMapVarsToTrainerA(void); diff --git a/include/battle_terastal.h b/include/battle_terastal.h index 823759169..3320eddd5 100644 --- a/include/battle_terastal.h +++ b/include/battle_terastal.h @@ -1,12 +1,12 @@ #ifndef GUARD_BATTLE_TERASTAL_H #define GUARD_BATTLE_TERASTAL_H -void ActivateTera(u32 battler); -void ApplyBattlerVisualsForTeraAnim(u32 battler); -bool32 CanTerastallize(u32 battler); -enum Type GetBattlerTeraType(u32 battler); -void ExpendTypeStellarBoost(u32 battler, enum Type type); -bool32 IsTypeStellarBoosted(u32 battler, enum Type type); +void ActivateTera(enum BattlerId battler); +void ApplyBattlerVisualsForTeraAnim(enum BattlerId battler); +bool32 CanTerastallize(enum BattlerId battler); +enum Type GetBattlerTeraType(enum BattlerId battler); +void ExpendTypeStellarBoost(enum BattlerId battler, enum Type type); +bool32 IsTypeStellarBoosted(enum BattlerId battler, enum Type type); uq4_12_t GetTeraMultiplier(struct BattleContext *ctx); u16 GetTeraTypeRGB(enum Type type); diff --git a/include/battle_transition.h b/include/battle_transition.h index 80215741c..6c6c77caa 100644 --- a/include/battle_transition.h +++ b/include/battle_transition.h @@ -1,7 +1,8 @@ #ifndef GUARD_BATTLE_TRANSITION_H #define GUARD_BATTLE_TRANSITION_H -enum { +enum MugshotColor +{ MUGSHOT_COLOR_PURPLE, MUGSHOT_COLOR_GREEN, MUGSHOT_COLOR_PINK, @@ -10,7 +11,8 @@ enum { MUGSHOT_COLOR_COUNT }; -enum { +enum BattleTransition +{ B_TRANSITION_BLUR, B_TRANSITION_SWIRL, B_TRANSITION_SHUFFLE, @@ -28,10 +30,48 @@ enum { B_TRANSITION_COUNT }; +#define B_TRANSITION_FRONTIER_LOGO_WIGGLE B_TRANSITION_BLUR +#define B_TRANSITION_FRONTIER_LOGO_WAVE B_TRANSITION_BLUR +#define B_TRANSITION_FRONTIER_SQUARES B_TRANSITION_BLUR +#define B_TRANSITION_FRONTIER_SQUARES_SCROLL B_TRANSITION_BLUR +#define B_TRANSITION_FRONTIER_SQUARES_SPIRAL B_TRANSITION_BLUR +#define B_TRANSITION_FRONTIER_CIRCLES_MEET B_TRANSITION_BLUR +#define B_TRANSITION_FRONTIER_CIRCLES_CROSS B_TRANSITION_BLUR +#define B_TRANSITION_FRONTIER_CIRCLES_ASYMMETRIC_SPIRAL B_TRANSITION_BLUR +#define B_TRANSITION_FRONTIER_CIRCLES_SYMMETRIC_SPIRAL B_TRANSITION_BLUR +#define B_TRANSITION_FRONTIER_CIRCLES_MEET_IN_SEQ B_TRANSITION_BLUR +#define B_TRANSITION_FRONTIER_CIRCLES_CROSS_IN_SEQ B_TRANSITION_BLUR +#define B_TRANSITION_FRONTIER_CIRCLES_ASYMMETRIC_SPIRAL_IN_SEQ B_TRANSITION_BLUR +#define B_TRANSITION_FRONTIER_CIRCLES_SYMMETRIC_SPIRAL_IN_SEQ B_TRANSITION_BLUR +#define B_TRANSITION_REGICE B_TRANSITION_BLUR +#define B_TRANSITION_REGISTEEL B_TRANSITION_BLUR +#define B_TRANSITION_REGIROCK B_TRANSITION_BLUR +#define B_TRANSITION_KYOGRE B_TRANSITION_BLUR +#define B_TRANSITION_GROUDON B_TRANSITION_BLUR +#define B_TRANSITION_RAYQUAZA B_TRANSITION_BLUR +#define B_TRANSITION_AQUA B_TRANSITION_BLUR +#define B_TRANSITION_MAGMA B_TRANSITION_BLUR + + +// IDs for GetSpecialBattleTransition +enum BattleTransitionGroup +{ + B_TRANSITION_GROUP_B_TOWER, + B_TRANSITION_GROUP_B_DOME = 3, + B_TRANSITION_GROUP_B_PALACE, + B_TRANSITION_GROUP_B_ARENA, + B_TRANSITION_GROUP_B_FACTORY, + B_TRANSITION_GROUP_B_PIKE, + B_TRANSITION_GROUP_B_PYRAMID = 10, + B_TRANSITION_GROUP_TRAINER_TOWER, + B_TRANSITION_GROUP_SECRET_BASE, + B_TRANSITION_GROUP_E_READER, +}; + extern const struct SpritePalette gSpritePalette_Pokeball; bool8 IsBattleTransitionDone(void); -void BattleTransition_StartOnField(u8 transitionId); +void BattleTransition_StartOnField(enum BattleTransition transitionId); void Task_BattleTransition_Intro(u8 taskId); #endif // GUARD_BATTLE_TRANSITION_H diff --git a/include/battle_util.h b/include/battle_util.h index b6bc681f1..4dd04ccb6 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -41,7 +41,6 @@ enum FieldEffectCases enum AbilityEffect { - ABILITYEFFECT_ON_SWITCHIN, ABILITYEFFECT_ENDTURN, ABILITYEFFECT_MOVE_END_ATTACKER, ABILITYEFFECT_COLOR_CHANGE, // Color Change / Berserk / Anger Shell @@ -49,6 +48,7 @@ enum AbilityEffect ABILITYEFFECT_IMMUNITY, ABILITYEFFECT_SYNCHRONIZE, ABILITYEFFECT_ATK_SYNCHRONIZE, + ABILITYEFFECT_FORM_CHANGE_ON_HIT, ABILITYEFFECT_MOVE_END_OTHER, ABILITYEFFECT_MOVE_END_FOES_FAINTED, // Moxie-like abilities / Battle Bond / Magician @@ -56,6 +56,8 @@ enum AbilityEffect ABILITYEFFECT_TERA_SHIFT, ABILITYEFFECT_NEUTRALIZINGGAS, ABILITYEFFECT_UNNERVE, + ABILITYEFFECT_ON_SWITCHIN, + ABILITYEFFECT_SWITCH_IN_FORM_CHANGE, ABILITYEFFECT_COMMANDER, // Commander / Hospitality / Costar ABILITYEFFECT_ON_WEATHER, ABILITYEFFECT_ON_TERRAIN, @@ -86,76 +88,11 @@ enum ItemEffect // for Natural Gift and Fling struct TypePower { - enum Type type; + enum Type type:8; u8 power; u16 effect; }; -enum MoveSuccessOrder -{ - CANCELER_STANCE_CHANGE_1, - CANCELER_CLEAR_FLAGS, - CANCELER_SKY_DROP, - CANCELER_RECHARGE, - CANCELER_CHILLY_RECEPTION, - CANCELER_ASLEEP_OR_FROZEN, - CANCELER_POWER_POINTS, - CANCELER_OBEDIENCE, - CANCELER_TRUANT, - CANCELER_FOCUS_GEN5, - CANCELER_FLINCH, - CANCELER_DISABLED, - CANCELER_VOLATILE_BLOCKED, // Gravity / Heal Block / Throat Chop - CANCELER_TAUNTED, - CANCELER_IMPRISONED, - CANCELER_CONFUSED, - CANCELER_GHOST, - CANCELER_PARALYZED, - CANCELER_INFATUATION, - CANCELER_BIDE, - CANCELER_Z_MOVES, - CANCELER_CHOICE_LOCK, - CANCELER_CALLSUBMOVE, - CANCELER_THAW, - CANCELER_STANCE_CHANGE_2, - CANCELER_ATTACKSTRING, - CANCELER_PPDEDUCTION, - CANCELER_SKY_BATTLE, - CANCELER_WEATHER_PRIMAL, - CANCELER_FOCUS_PRE_GEN5, - CANCELER_MOVE_FAILURE, - CANCELER_POWDER_STATUS, - CANCELER_PRIORITY_BLOCK, - CANCELER_PROTEAN, - CANCELER_EXPLODING_DAMP, - CANCELER_EXPLOSION, - CANCELER_CHARGING, - CANCELER_NO_TARGET, - CANCELER_TOOK_ATTACK, - CANCELER_TARGET_FAILURE, - CANCELER_NOT_FULLY_PROTECTED, - CANCELER_MULTIHIT_MOVES, - CANCELER_END, -}; - -enum Obedience -{ - OBEYS, - DISOBEYS_LOAFS, - DISOBEYS_HITS_SELF, - DISOBEYS_FALL_ASLEEP, - DISOBEYS_WHILE_ASLEEP, - DISOBEYS_RANDOM_MOVE, -}; - -enum MoveCanceler -{ - MOVE_STEP_SUCCESS, - MOVE_STEP_BREAK, // Runs script. Increments state - MOVE_STEP_PAUSE, // Runs script. Does not increment state - MOVE_STEP_FAILURE, // Move failed, jump to script that handles the failure -}; - enum ImmunityHealStatusOutcome { IMMUNITY_NO_EFFECT, @@ -169,8 +106,8 @@ extern const struct TypePower gNaturalGiftTable[]; struct BattleContext { - u32 battlerAtk:3; - u32 battlerDef:3; + enum BattlerId battlerAtk:3; + enum BattlerId battlerDef:3; u32 fixedBasePower:8; u32 weather:16; u32 unused:2; @@ -203,8 +140,8 @@ struct BattleContext // Helper struct to keep the arg list small and prevent constant recalculations of abilities/hold effects. struct BattleCalcValues { - u32 battlerAtk:3; - u32 battlerDef:3; + enum BattlerId battlerAtk:3; + enum BattlerId battlerDef:3; enum Move move:16; u32 padding:10; enum Ability abilities[MAX_BATTLERS_COUNT]; @@ -238,16 +175,17 @@ enum EjectPackTiming }; void HandleAction_ThrowBall(void); -uq4_12_t CalcTypeEffectivenessMultiplierHelper(enum Move move, enum Type moveType, u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum Ability abilityDef, bool32 recordAbilities); +uq4_12_t CalcTypeEffectivenessMultiplierHelper(enum Move move, enum Type moveType, enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum Ability abilityDef, bool32 recordAbilities); u32 GetCurrentBattleWeather(void); bool32 EndOrContinueWeather(void); -bool32 IsUnnerveBlocked(u32 battler, enum Item itemId); -bool32 IsAffectedByFollowMe(u32 battlerAtk, enum BattleSide defSide, enum Move move); +enum DamageCategory GetReflectDamageMoveDamageCategory(enum BattlerId battler, enum Move move); +bool32 IsUnnerveBlocked(enum BattlerId battler, enum Item itemId); +bool32 IsAffectedByFollowMe(enum BattlerId battlerAtk, enum BattleSide defSide, enum Move move); void DetermineTarget(enum MoveTarget moveTarget, bool32 overwriteTarget); void HandleAction_UseMove(void); void HandleAction_Switch(void); void HandleAction_UseItem(void); -bool32 TryRunFromBattle(u32 battler); +bool32 TryRunFromBattle(enum BattlerId battler); void HandleAction_Run(void); void HandleAction_WatchesCarefully(void); void HandleAction_SafariZoneBallThrow(void); @@ -258,72 +196,71 @@ void HandleAction_OldManBallThrow(void); void HandleAction_TryFinish(void); void HandleAction_NothingIsFainted(void); void HandleAction_ActionFinished(void); -u8 GetBattlerForBattleScript(u8 caseId); -bool32 IsBattlerMarkedForControllerExec(u32 battler); -void MarkBattlerForControllerExec(u32 battler); -void MarkBattlerReceivedLinkData(u32 battler); -const u8 *CancelMultiTurnMoves(u32 battler, enum SkyDropState skyDropState); -bool32 IsLastMonToMove(u32 battler); -bool32 ShouldDefiantCompetitiveActivate(u32 battler, enum Ability ability); -void PrepareStringBattle(enum StringID stringId, u32 battler); +enum BattlerId GetBattlerForBattleScript(u8 caseId); +bool32 IsBattlerMarkedForControllerExec(enum BattlerId battler); +void MarkBattlerForControllerExec(enum BattlerId battler); +void MarkBattlerReceivedLinkData(enum BattlerId battler); +const u8 *CancelMultiTurnMoves(enum BattlerId battler, enum SkyDropState skyDropState); +bool32 IsLastMonToMove(enum BattlerId battler); +bool32 ShouldDefiantCompetitiveActivate(enum BattlerId battler, enum Ability ability); +void PrepareStringBattle(enum StringID stringId, enum BattlerId battler); void ResetSentPokesToOpponentValue(void); -void OpponentSwitchInResetSentPokesToOpponentValue(u32 battler); -void UpdateSentPokesToOpponentValue(u32 battler); +void OpponentSwitchInResetSentPokesToOpponentValue(enum BattlerId battler); +void UpdateSentPokesToOpponentValue(enum BattlerId battler); void BattleScriptPush(const u8 *bsPtr); void BattleScriptPushCursor(void); void BattleScriptCall(const u8 *bsPtr); void BattleScriptPop(void); -u32 TrySetCantSelectMoveBattleScript(u32 battler); -u32 CheckMoveLimitations(u32 battler, u8 unusableMoves, u16 check); -bool32 AreAllMovesUnusable(u32 battler); -u8 GetImprisonedMovesCount(u32 battler, enum Move move); -s32 GetDrainedBigRootHp(u32 battler, s32 hp); -bool32 IsAbilityAndRecord(u32 battler, enum Ability battlerAbility, enum Ability abilityToCheck); +u32 TrySetCantSelectMoveBattleScript(enum BattlerId battler); +u32 CheckMoveLimitations(enum BattlerId battler, u8 unusableMoves, u16 check); +bool32 AreAllMovesUnusable(enum BattlerId battler); +u8 GetImprisonedMovesCount(enum BattlerId battler, enum Move move); +s32 GetDrainedBigRootHp(enum BattlerId battler, s32 hp); +bool32 IsAbilityAndRecord(enum BattlerId battler, enum Ability battlerAbility, enum Ability abilityToCheck); bool32 HandleFaintedMonActions(void); void TryClearRageAndFuryCutter(void); -enum MoveCanceler AtkCanceler_MoveSuccessOrder(void); -bool32 HasNoMonsToSwitch(u32 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2); -bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, enum Ability ability); -bool32 TryChangeBattleTerrain(u32 battler, u32 statusFlag); -bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum Ability abilityDef, u32 move, enum ResultOption option); +bool32 HasNoMonsToSwitch(enum BattlerId battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2); +bool32 TryChangeBattleWeather(enum BattlerId battler, u32 battleWeatherId, enum Ability ability); +bool32 TryChangeBattleTerrain(enum BattlerId battler, u32 statusFlag); +bool32 CanAbilityBlockMove(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum Ability abilityDef, u32 move, enum ResultOption option); bool32 CanTargetBlockPranksterMove(struct BattleContext *ctx, s32 movePriority); bool32 CanPsychicTerrainProtectTarget(struct BattleContext *ctx, s32 movePriority); bool32 CanMoveBeBlockedByTarget(struct BattleContext *ctx, s32 movePriority); bool32 CanAbilityAbsorbMove(struct BattleContext *ctx); bool32 TryFieldEffects(enum FieldEffectCases caseId); -u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ability, enum Move move, bool32 shouldAbilityTrigger); -bool32 TryPrimalReversion(u32 battler); +u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum Ability ability, enum Move move, bool32 shouldAbilityTrigger); +bool32 TryPrimalReversion(enum BattlerId battler); bool32 IsNeutralizingGasOnField(void); -bool32 IsMoldBreakerTypeAbility(u32 battler, enum Ability ability); -enum Ability GetBattlerAbilityIgnoreMoldBreaker(u32 battler); -enum Ability GetBattlerAbilityNoAbilityShield(u32 battler); -enum Ability GetBattlerAbilityInternal(u32 battler, bool32 ignoreMoldBreaker, bool32 noAbilityShield); -enum Ability GetBattlerAbility(u32 battler); -u32 IsAbilityOnSide(u32 battler, enum Ability ability); -u32 IsAbilityOnOpposingSide(u32 battler, enum Ability ability); +bool32 IsMoldBreakerTypeAbility(enum BattlerId battler, enum Ability ability); +enum Ability GetBattlerAbilityIgnoreMoldBreaker(enum BattlerId battler); +enum Ability GetBattlerAbilityNoAbilityShield(enum BattlerId battler); +enum Ability GetBattlerAbilityInternal(enum BattlerId battler, bool32 ignoreMoldBreaker, bool32 noAbilityShield); +enum Ability GetBattlerAbility(enum BattlerId battler); +u32 IsAbilityOnSide(enum BattlerId battler, enum Ability ability); +u32 IsAbilityOnOpposingSide(enum BattlerId battler, enum Ability ability); u32 IsAbilityOnField(enum Ability ability); -u32 IsAbilityOnFieldExcept(u32 battler, enum Ability ability); -u32 IsAbilityPreventingEscape(u32 battler); +u32 IsAbilityOnFieldExcept(enum BattlerId battler, enum Ability ability); +u32 IsAbilityPreventingEscape(enum BattlerId battler); bool32 IsBattlerProtected(struct BattleContext *ctx); enum ProtectType GetProtectType(enum ProtectMethod method); -bool32 CanBattlerEscape(u32 battler); // no ability check +bool32 CanBattlerEscape(enum BattlerId battler); // no ability check void BattleScriptExecute(const u8 *BS_ptr); void BattleScriptPushCursorAndCallback(const u8 *BS_ptr); -void ClearVariousBattlerFlags(u32 battler); +void ClearVariousBattlerFlags(enum BattlerId battler); void HandleAction_RunBattleScript(void); -u32 SetRandomTarget(u32 battler); +u32 SetRandomTarget(enum BattlerId battlerAtk); u32 GetBattleMoveTarget(enum Move move, enum MoveTarget moveTarget); enum Obedience GetAttackerObedienceForAction(void); -enum HoldEffect GetBattlerHoldEffect(u32 battler); -enum HoldEffect GetBattlerHoldEffectIgnoreAbility(u32 battler); -enum HoldEffect GetBattlerHoldEffectIgnoreNegation(u32 battler); -enum HoldEffect GetBattlerHoldEffectInternal(u32 battler, enum Ability ability); -u32 GetBattlerHoldEffectParam(u32 battler); -bool32 CanBattlerAvoidContactEffects(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum HoldEffect holdEffectAtk, enum Move move); -bool32 IsMoveMakingContact(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum HoldEffect holdEffectAtk, enum Move move); -bool32 IsBattlerGrounded(u32 battler, enum Ability ability, enum HoldEffect holdEffect); +enum HoldEffect GetBattlerHoldEffect(enum BattlerId battler); +enum HoldEffect GetBattlerHoldEffectIgnoreAbility(enum BattlerId battler); +enum HoldEffect GetBattlerHoldEffectIgnoreNegation(enum BattlerId battler); +enum HoldEffect GetBattlerHoldEffectInternal(enum BattlerId battler, enum Ability ability); +u32 GetBattlerHoldEffectParam(enum BattlerId battler); +bool32 CanBattlerAvoidContactEffects(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum HoldEffect holdEffectAtk, enum Move move); +bool32 IsMoveMakingContact(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum HoldEffect holdEffectAtk, enum Move move); +bool32 IsBattlerGrounded(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect); u32 GetMoveSlot(u16 *moves, enum Move move); -u32 GetBattlerWeight(u32 battler); +u32 GetBattlerWeight(enum BattlerId battler); s32 CalcCritChanceStage(struct BattleContext *ctx); s32 CalcCritChanceStageGen1(struct BattleContext *ctx); s32 CalculateMoveDamage(struct BattleContext *ctx); @@ -335,157 +272,160 @@ uq4_12_t CalcPartyMonTypeEffectivenessMultiplier(enum Move move, u16 speciesDef, uq4_12_t GetTypeModifier(enum Type atkType, enum Type defType); uq4_12_t GetOverworldTypeEffectiveness(struct Pokemon *mon, enum Type moveType); void UpdateMoveResultFlags(uq4_12_t modifier, u16 *resultFlags); -s32 GetStealthHazardDamage(enum TypeSideHazard hazardType, u32 battler); +s32 GetStealthHazardDamage(enum TypeSideHazard hazardType, enum BattlerId battler); s32 GetStealthHazardDamageByTypesAndHP(enum TypeSideHazard hazardType, enum Type type1, enum Type type2, u32 maxHp); -bool32 CanMegaEvolve(u32 battler); -bool32 CanUltraBurst(u32 battler); -void ActivateMegaEvolution(u32 battler); -void ActivateUltraBurst(u32 battler); -bool32 IsBattlerMegaEvolved(u32 battler); -bool32 IsBattlerPrimalReverted(u32 battler); -bool32 IsBattlerUltraBursted(u32 battler); -u32 GetBattleFormChangeTargetSpecies(u32 battler, enum FormChanges method); +bool32 CanMegaEvolve(enum BattlerId battler); +bool32 CanUltraBurst(enum BattlerId battler); +void ActivateMegaEvolution(enum BattlerId battler); +void ActivateUltraBurst(enum BattlerId battler); +bool32 IsBattlerMegaEvolved(enum BattlerId battler); +bool32 IsBattlerPrimalReverted(enum BattlerId battler); +bool32 IsBattlerUltraBursted(enum BattlerId battler); +u32 GetBattleFormChangeTargetSpecies(enum BattlerId battler, enum FormChanges method, enum Ability ability); bool32 TryRevertPartyMonFormChange(u32 partyIndex); -bool32 TryBattleFormChange(u32 battler, enum FormChanges method); -bool32 DoBattlersShareType(u32 battler1, u32 battler2); -bool32 CanBattlerGetOrLoseItem(u32 fromBattler, u32 battler, enum Item itemId); -u32 GetBattlerVisualSpecies(u32 battler); -bool32 TryClearIllusion(u32 battler, enum Ability ability); -u32 GetIllusionMonSpecies(u32 battler); -struct Pokemon *GetIllusionMonPtr(u32 battler); -void ClearIllusionMon(u32 battler); -u32 GetIllusionMonPartyId(struct Pokemon *party, struct Pokemon *mon, struct Pokemon *partnerMon, u32 battler); -void SetIllusionMon(struct Pokemon *mon, u32 battler); -enum ImmunityHealStatusOutcome TryImmunityAbilityHealStatus(u32 battler); -bool32 ShouldGetStatBadgeBoost(u16 flagId, u32 battler); +bool32 TryBattleFormChange(enum BattlerId battler, enum FormChanges method, enum Ability ability); +bool32 DoBattlersShareType(enum BattlerId battler1, enum BattlerId battler2); +bool32 CanBattlerGetOrLoseItem(enum BattlerId fromBattler, enum BattlerId battler, enum Item itemId); +u32 GetBattlerVisualSpecies(enum BattlerId battler); +bool32 TryClearIllusion(enum BattlerId battler, enum Ability ability); +u32 GetIllusionMonSpecies(enum BattlerId battler); +struct Pokemon *GetIllusionMonPtr(enum BattlerId battler); +void ClearIllusionMon(enum BattlerId battler); +u32 GetIllusionMonPartyId(struct Pokemon *party, struct Pokemon *mon, struct Pokemon *partnerMon, enum BattlerId battler); +void SetIllusionMon(struct Pokemon *mon, enum BattlerId battler); +enum ImmunityHealStatusOutcome TryImmunityAbilityHealStatus(enum BattlerId battler); +bool32 ShouldGetStatBadgeBoost(u16 flagId, enum BattlerId battler); uq4_12_t GetBadgeBoostModifier(void); enum DamageCategory GetBattleMoveCategory(enum Move move); -void SetDynamicMoveCategory(u32 battlerAtk, u32 battlerDef, enum Move move); -bool32 CanFling(u32 battlerAtk, u32 battlerDef); +void SetDynamicMoveCategory(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move); +bool32 CanFling(enum BattlerId battlerAtk); bool32 IsTelekinesisBannedSpecies(u16 species); -bool32 IsHealBlockPreventingMove(u32 battler, enum Move move); -bool32 IsBelchPreventingMove(u32 battler, enum Move move); -bool32 HasEnoughHpToEatBerry(u32 battler, enum Ability ability, u32 hpFraction, enum Item itemId); -bool32 IsPartnerMonFromSameTrainer(u32 battler); -enum DamageCategory GetCategoryBasedOnStats(u32 battler); +bool32 IsHealBlockPreventingMove(enum BattlerId battler, enum Move move); +bool32 IsGravityPreventingMove(enum Move move); +bool32 IsBelchPreventingMove(enum BattlerId battler, enum Move move); +bool32 HasEnoughHpToEatBerry(enum BattlerId battler, enum Ability ability, u32 hpFraction, enum Item itemId); +bool32 IsPartnerMonFromSameTrainer(enum BattlerId battler); +enum DamageCategory GetCategoryBasedOnStats(enum BattlerId battler); void SetShellSideArmCategory(void); bool32 MoveIsAffectedBySheerForce(enum Move move); bool32 IsSheerForceAffected(enum Move move, enum Ability ability); void TryRestoreHeldItems(void); -bool32 CanStealItem(u32 battlerStealing, u32 battlerItem, enum Item item); -void TrySaveExchangedItem(u32 battler, enum Item stolenItem); -bool32 IsBattlerAffectedByHazards(u32 battler, enum HoldEffect holdEffect, bool32 toxicSpikes); -void SortBattlersBySpeed(u8 *battlers, bool32 slowToFast); -bool32 CompareStat(u32 battler, enum Stat statId, u32 cmpTo, u32 cmpKind, enum Ability ability); -bool32 BlocksPrankster(enum Move move, u32 battlerPrankster, u32 battlerDef, bool32 checkTarget); -bool32 PickupHasValidTarget(u32 battler); +bool32 CanStealItem(enum BattlerId battlerStealing, enum BattlerId battlerItem, enum Item item); +void TrySaveExchangedItem(enum BattlerId battler, enum Item stolenItem); +bool32 IsBattlerAffectedByHazards(enum BattlerId battler, enum HoldEffect holdEffect, bool32 toxicSpikes); +void SortBattlersBySpeed(enum BattlerId *battlers, bool32 slowToFast); +bool32 CompareStat(enum BattlerId battler, enum Stat statId, u32 cmpTo, u32 cmpKind, enum Ability ability); +bool32 BlocksPrankster(enum Move move, enum BattlerId battlerPrankster, enum BattlerId battlerDef, bool32 checkTarget); +bool32 PickupHasValidTarget(enum BattlerId battler); bool32 CantPickupItem(u32 battler); -bool32 IsBattlerWeatherAffected(u32 battler, u32 weatherFlags); -enum MoveTarget GetBattlerMoveTargetType(u32 battler, enum Move move); -bool32 CanTargetBattler(u32 battlerAtk, u32 battlerDef, enum Move move); +bool32 IsBattlerWeatherAffected(enum BattlerId battler, u32 weatherFlags); +enum MoveTarget GetBattlerMoveTargetType(enum BattlerId battler, enum Move move); +bool32 CanTargetBattler(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move); u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent); -void CopyMonLevelAndBaseStatsToBattleMon(u32 battler, struct Pokemon *mon); -void CopyMonAbilityAndTypesToBattleMon(u32 battler, struct Pokemon *mon); -void RecalcBattlerStats(u32 battler, struct Pokemon *mon, bool32 isDynamaxing); +void CopyMonLevelAndBaseStatsToBattleMon(enum BattlerId battler, struct Pokemon *mon); +void CopyMonAbilityAndTypesToBattleMon(enum BattlerId battler, struct Pokemon *mon); +void RecalcBattlerStats(enum BattlerId battler, struct Pokemon *mon, bool32 isDynamaxing); bool32 IsGen6ExpShareEnabled(void); bool32 MoveHasAdditionalEffect(enum Move move, enum MoveEffect moveEffect); bool32 MoveHasAdditionalEffectWithChance(enum Move move, enum MoveEffect moveEffect, u32 chance); bool32 MoveHasAdditionalEffectSelf(enum Move move, enum MoveEffect moveEffect); bool32 IsMoveEffectRemoveSpeciesType(enum Move move, enum MoveEffect moveEffect, u32 argument); bool32 MoveHasChargeTurnAdditionalEffect(enum Move move); -bool32 CanTargetPartner(u32 battlerAtk, u32 battlerDef); -bool32 IsBattlerUnaffectedByMove(u32 battler); +bool32 CanTargetPartner(enum BattlerId battlerAtk, enum BattlerId battlerDef); +bool32 IsBattlerUnaffectedByMove(enum BattlerId battler); bool32 MoodyCantRaiseStat(u32 stat); bool32 MoodyCantLowerStat(u32 stat); -bool32 IsPsychicTerrainAffected(u32 battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses); -bool32 IsMistyTerrainAffected(u32 battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses); -bool32 IsGrassyTerrainAffected(u32 battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses); -bool32 IsElectricTerrainAffected(u32 battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses); -bool32 IsAnyTerrainAffected(u32 battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses); -bool32 IsBattlerTerrainAffected(u32 battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses, u32 terrainFlag); -enum Stat GetHighestStatId(u32 battler); -enum Stat GetParadoxHighestStatId(u32 battler); -enum Stat GetParadoxBoostedStatId(u32 battler); +bool32 IsPsychicTerrainAffected(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses); +bool32 IsMistyTerrainAffected(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses); +bool32 IsGrassyTerrainAffected(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses); +bool32 IsElectricTerrainAffected(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses); +bool32 IsAnyTerrainAffected(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses); +bool32 IsBattlerTerrainAffected(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses, u32 terrainFlag); +enum Stat GetHighestStatId(enum BattlerId battler); +enum Stat GetParadoxHighestStatId(enum BattlerId battler); +enum Stat GetParadoxBoostedStatId(enum BattlerId battler); -bool32 CanBeSlept(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef, enum SleepClauseBlock isBlockedBySleepClause); -bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum Ability abilityDef); -bool32 CanBeBurned(u32 battlerAtk, u32 battlerDef, enum Ability ability); -bool32 CanBeParalyzed(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef); -bool32 CanBeFrozen(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef); -bool32 CanGetFrostbite(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef); -bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum MoveEffect secondaryMoveEffect, enum ResultOption option); -bool32 CanBeConfused(u32 battler); -u32 GetBattlerAffectionHearts(u32 battler); +bool32 CanBeSlept(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityDef, enum SleepClauseBlock isBlockedBySleepClause); +bool32 CanBePoisoned(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum Ability abilityDef); +bool32 CanBeBurned(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability ability); +bool32 CanBeParalyzed(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityDef); +bool32 CanBeFrozen(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityDef); +bool32 CanGetFrostbite(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityDef); +bool32 CanSetNonVolatileStatus(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum MoveEffect secondaryMoveEffect, enum ResultOption option); +bool32 CanBeConfused(enum BattlerId battler); +u32 GetBattlerAffectionHearts(enum BattlerId battler); void TryToRevertMimicryAndFlags(void); bool32 BattleArenaTurnEnd(void); -u32 CountBattlerStatIncreases(u32 battler, bool32 countEvasionAcc); -bool32 BattlerHasCopyableChanges(u32 battler); -bool32 ChangeTypeBasedOnTerrain(u32 battler); -void RemoveConfusionStatus(u32 battler); -u32 GetBattlerGender(u32 battler); -bool32 AreBattlersOfOppositeGender(u32 battler1, u32 battler2); -bool32 AreBattlersOfSameGender(u32 battler1, u32 battler2); -u32 CalcSecondaryEffectChance(u32 battler, enum Ability battlerAbility, const struct AdditionalEffect *additionalEffect); -bool32 MoveEffectIsGuaranteed(u32 battler, enum Ability battlerAbility, const struct AdditionalEffect *additionalEffect); -void GetBattlerTypes(u32 battler, bool32 ignoreTera, enum Type types[static 3]); -enum Type GetBattlerType(u32 battler, u32 typeIndex, bool32 ignoreTera); +u32 CountBattlerStatIncreases(enum BattlerId battler, bool32 countEvasionAcc); +bool32 BattlerHasCopyableChanges(enum BattlerId battler); +bool32 ChangeTypeBasedOnTerrain(enum BattlerId battler); +void RemoveConfusionStatus(enum BattlerId battler); +u32 GetBattlerGender(enum BattlerId battler); +bool32 AreBattlersOfOppositeGender(enum BattlerId battler1, enum BattlerId battler2); +bool32 AreBattlersOfSameGender(enum BattlerId battler1, enum BattlerId battler2); +u32 CalcSecondaryEffectChance(enum BattlerId battler, enum Ability battlerAbility, const struct AdditionalEffect *additionalEffect); +bool32 MoveEffectIsGuaranteed(enum BattlerId battler, enum Ability battlerAbility, const struct AdditionalEffect *additionalEffect); +void GetBattlerTypes(enum BattlerId battler, bool32 ignoreTera, enum Type types[static 3]); +enum Type GetBattlerType(enum BattlerId battler, u32 typeIndex, bool32 ignoreTera); bool32 CanMonParticipateInSkyBattle(struct Pokemon *mon); -void RemoveBattlerType(u32 battler, enum Type type); +void RemoveBattlerType(enum BattlerId battler, enum Type type); enum Type GetBattleMoveType(enum Move move); -void TryActivateSleepClause(u32 battler, u32 indexInParty); +void TryActivateSleepClause(enum BattlerId battler, u32 indexInParty); void TryDeactivateSleepClause(enum BattleSide battlerSide, u32 indexInParty); bool32 IsSleepClauseActiveForSide(enum BattleSide battlerSide); bool32 IsSleepClauseEnabled(void); void ClearDamageCalcResults(void); -u32 DoesDestinyBondFail(u32 battler); +u32 DoesDestinyBondFail(enum BattlerId battler); bool32 IsMoveEffectBlockedByTarget(enum Ability ability); -bool32 SetTargetToNextPursuiter(u32 battlerDef); +bool32 SetTargetToNextPursuiter(enum BattlerId battlerDef); bool32 IsPursuitTargetSet(void); -void ClearPursuitValuesIfSet(u32 battler); +void ClearPursuitValuesIfSet(enum BattlerId battler); void ClearPursuitValues(void); bool32 HasWeatherEffect(void); -bool32 IsFutureSightAttackerInParty(u32 battlerAtk, u32 battlerDef, enum Move move); -bool32 HadMoreThanHalfHpNowDoesnt(u32 battler); -void ChooseStatBoostAnimation(u32 battler); +bool32 IsFutureSightAttackerInParty(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move); +bool32 HadMoreThanHalfHpNowDoesnt(enum BattlerId battler); +void ChooseStatBoostAnimation(enum BattlerId battler); void UpdateStallMons(void); bool32 TrySwitchInEjectPack(enum EjectPackTiming timing); -bool32 EmergencyExitCanBeTriggered(u32 battler); -bool32 TryTriggerSymbiosis(u32 battler, u32 ally); -bool32 TrySymbiosis(u32 battler, enum Item itemId, bool32 moveEnd); -void BestowItem(u32 battlerAtk, u32 battlerDef); -ARM_FUNC u32 GetBattlerVolatile(u32 battler, enum Volatile _volatile); -void SetMonVolatile(u32 battler, enum Volatile _volatile, u32 newValue); -bool32 ItemHealMonVolatile(u32 battler, enum Item itemId); +bool32 EmergencyExitCanBeTriggered(enum BattlerId battler); +bool32 TryTriggerSymbiosis(enum BattlerId battler, u32 ally); +bool32 TrySymbiosis(enum BattlerId battler, enum Item itemId, bool32 moveEnd); +void BestowItem(enum BattlerId battlerAtk, enum BattlerId battlerDef); +ARM_FUNC u32 GetBattlerVolatile(enum BattlerId battler, enum Volatile _volatile); +void SetMonVolatile(enum BattlerId battler, enum Volatile _volatile, u32 newValue); +bool32 ItemHealMonVolatile(enum BattlerId battler, enum Item itemId); void PushHazardTypeToQueue(enum BattleSide side, enum Hazards hazardType); bool32 IsHazardOnSide(enum BattleSide side, enum Hazards hazardType); bool32 AreAnyHazardsOnSide(enum BattleSide side); void RemoveAllHazardsFromField(enum BattleSide side); bool32 IsHazardOnSideAndClear(enum BattleSide side, enum Hazards hazardType); void RemoveHazardFromField(enum BattleSide side, enum Hazards hazardType); -bool32 CanMoveSkipAccuracyCalc(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum Move move, enum ResultOption option); -u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, enum Move move, enum Ability atkAbility, enum Ability defAbility, enum HoldEffect atkHoldEffect, enum HoldEffect defHoldEffect); -bool32 IsSemiInvulnerable(u32 battler, enum SemiInvulnerableExclusion excludeCommander); -bool32 BreaksThroughSemiInvulnerablity(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum Move move); -bool32 HasPartnerTrainer(u32 battler); -bool32 IsAffectedByPowderMove(u32 battler, enum Ability ability, enum HoldEffect holdEffect); -enum Move GetNaturePowerMove(u32 battler); -void RemoveAbilityFlags(u32 battler); -void CheckSetUnburden(u32 battler); +bool32 CanMoveSkipAccuracyCalc(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum Move move, enum ResultOption option); +u32 GetTotalAccuracy(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, enum Ability atkAbility, enum Ability defAbility, enum HoldEffect atkHoldEffect, enum HoldEffect defHoldEffect); +bool32 DoesOHKOMoveMissTarget(struct BattleCalcValues *cv); +bool32 DoesMoveMissTarget(struct BattleCalcValues *cv); +bool32 IsSemiInvulnerable(enum BattlerId battler, enum SemiInvulnerableExclusion excludeCommander); +bool32 BreaksThroughSemiInvulnerablity(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum Move move); +bool32 HasPartnerTrainer(enum BattlerId battler); +bool32 IsAffectedByPowderMove(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect); +enum Move GetNaturePowerMove(void); +void RemoveAbilityFlags(enum BattlerId battler); +void CheckSetUnburden(enum BattlerId battler); bool32 IsDazzlingAbility(enum Ability ability); bool32 IsAllowedToUseBag(void); -bool32 IsAnyTargetTurnDamaged(u32 battlerAtk); +bool32 IsAnyTargetTurnDamaged(enum BattlerId battlerAtk); bool32 IsAnyTargetAffected(void); -bool32 IsMimikyuDisguised(u32 battler); +bool32 IsMimikyuDisguised(enum BattlerId battler); bool32 IsDoubleSpreadMove(void); -bool32 IsBattlerInvalidForSpreadMove(u32 battlerAtk, u32 battlerDef); +bool32 IsBattlerInvalidForSpreadMove(enum BattlerId battlerAtk, enum BattlerId battlerDef); void SetStartingStatus(enum StartingStatus status); void ResetStartingStatuses(void); bool32 IsUsableWhileAsleepEffect(enum BattleMoveEffects effect); -void SetWrapTurns(u32 battler, enum HoldEffect holdEffect); +void SetWrapTurns(enum BattlerId battler, enum HoldEffect holdEffect); bool32 ChangeOrderTargetAfterAttacker(void); void TryUpdateEvolutionTracker(enum EvolutionConditions evolutionCondition, u32 upAmount, enum Move usedMove); -bool32 CanUseMoveConsecutively(u32 battler); -void TryResetConsecutiveUseCounter(u32 battler); +bool32 CanUseMoveConsecutively(enum BattlerId battler); +void TryResetConsecutiveUseCounter(enum BattlerId battler); void SetOrClearRageVolatile(void); #endif // GUARD_BATTLE_UTIL_H diff --git a/include/battle_util2.h b/include/battle_util2.h index 51c9c0363..5f91ba6dd 100644 --- a/include/battle_util2.h +++ b/include/battle_util2.h @@ -3,8 +3,8 @@ void AllocateBattleResources(void); void FreeBattleResources(void); -void AdjustFriendshipOnBattleFaint(u8 battler); -void SwitchPartyOrderInGameMulti(u8 battler, u8 arg1); -u32 BattlePalace_TryEscapeStatus(u8 battler); +void AdjustFriendshipOnBattleFaint(enum BattlerId battler); +void SwitchPartyOrderInGameMulti(enum BattlerId battler, u8 arg1); +u32 BattlePalace_TryEscapeStatus(enum BattlerId battler); #endif // GUARD_BATTLE_UTIL_H diff --git a/include/battle_z_move.h b/include/battle_z_move.h index bfa4c8495..6a039c21f 100644 --- a/include/battle_z_move.h +++ b/include/battle_z_move.h @@ -1,29 +1,27 @@ #ifndef GUARD_BATTLE_Z_MOVE_H #define GUARD_BATTLE_Z_MOVE_H -#include "constants/battle_z_move_effects.h" - #define MOVE_Z_STATUS 0xFFFF struct SignatureZMove { u16 species; - u16 item; - u16 move; - u16 zmove; + enum Item item; + enum Move move; + enum Move zmove; }; -bool32 IsZMove(u32 move); -bool32 CanUseZMove(u32 battler); -u32 GetUsableZMove(u32 battler, u32 move); -void ActivateZMove(u32 battler); -bool32 IsViableZMove(u32 battler, u32 move); -bool32 TryChangeZTrigger(u32 battler, u32 moveIndex); -u32 GetTypeBasedZMove(u32 move); -u32 GetSignatureZMove(u32 move, u32 species, u32 item); -bool32 MoveSelectionDisplayZMove(u16 zmove, u32 battler); +bool32 IsZMove(enum Move move); +bool32 CanUseZMove(enum BattlerId battler); +enum Move GetUsableZMove(enum BattlerId battler, enum Move move); +void ActivateZMove(enum BattlerId battler); +bool32 IsViableZMove(enum BattlerId battler, enum Move move); +bool32 TryChangeZTrigger(enum BattlerId battler, u32 moveIndex); +enum Move GetTypeBasedZMove(enum Move move); +enum Move GetSignatureZMove(enum Move move, u32 species, enum Item item); +bool32 MoveSelectionDisplayZMove(enum Move zmove, enum BattlerId battler); void SetZEffect(void); -void AssignUsableZMoves(u32 battler, u16 *moves); -u32 GetZMovePower(u32 move); +void AssignUsableZMoves(enum BattlerId battler, enum Move *moves); +u32 GetZMovePower(enum Move move); #endif // GUARD_BATTLE_Z_MOVE_H diff --git a/include/config/battle.h b/include/config/battle.h index 2bf3343ae..dfa48b842 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -93,12 +93,14 @@ #define B_MINIMIZE_EVASION GEN_LATEST // In Gen5+, Minimize raises evasion by 2 stages instead of 1. #define B_GROWTH_STAT_RAISE GEN_LATEST // In Gen5+, Growth raises Attack in addition to Special Attack by 1 stage each. Under the effects of the sun, it raises them by 2 stages each instead. #define B_FOCUS_ENERGY_CRIT_RATIO GEN_LATEST // In Gen3+, Focus Energy increases critical hit ratio by 2 instead of 1. +#define B_PSYCH_UP_CRIT_RATIO GEN_LATEST // In Gen6+, Psych Up also copies the target's critical hit ratio. // Other move settings #define B_INCINERATE_GEMS GEN_LATEST // In Gen6+, Incinerate can destroy Gems. #define B_CAN_SPITE_FAIL GEN_LATEST // In Gen4+, Spite can no longer fail if the foe's last move only has 1 remaining PP. #define B_CRASH_IF_TARGET_IMMUNE GEN_LATEST // In Gen4+, moves with crash damage will crash if the user attacks a target that is immune due to their typing. #define B_MEMENTO_FAIL GEN_LATEST // In Gen4+, Memento no longer fails if the target already has -6 Attack and Special Attack. Additionally, in Gen5+, it fails if there is no target, or if the target is protected or behind a Substitute. +#define B_PARTING_SHOT_SWITCH GEN_LATEST // In Gen7+, the user won't switch out if Parting Shot fails to lower the target's stats. #define B_GLARE_GHOST GEN_LATEST // In Gen4+, Glare can hit Ghost-type Pokémon normally. #define B_SKILL_SWAP GEN_LATEST // In Gen4+, Skill Swap triggers switch-in abilities after use. #define B_BRICK_BREAK GEN_LATEST // In Gen4+, you can destroy your own side's screens. In Gen 5+, screens are not removed if the target is immune. @@ -130,6 +132,8 @@ #define B_AFTER_YOU_TURN_ORDER GEN_LATEST // In Gen8+, After You doesn't fail if the turn order wouldn't change after use. #define B_QUASH_TURN_ORDER GEN_LATEST // In Gen8+, Quash-affected battlers move according to speed order. Before Gen8, Quash-affected battlers move in the order they were affected by Quash. #define B_DESTINY_BOND_FAIL GEN_LATEST // In Gen7+, Destiny Bond fails if used repeatedly. +#define B_FORESIGHT_FAIL GEN_LATEST // In Gen2 and Gen5+, Foresight fails if used against a target already under its effect. +#define B_MIRACLE_EYE_FAIL GEN_LATEST // In Gen5+, Miracle Eye fails if used against a target already under its effect. #define B_PURSUIT_TARGET GEN_LATEST // In Gen4+, Pursuit attacks a switching opponent even if they weren't targeting them. Before Gen4, Pursuit only attacks a switching opponent that it originally targeted. #define B_SKIP_RECHARGE GEN_LATEST // In Gen1, recharging moves such as Hyper Beam skip the recharge if the target gets KO'd #define B_ENCORE_TARGET GEN_LATEST // In Gen5+, encored moves are allowed to choose a target diff --git a/include/constants/battle.h b/include/constants/battle.h index 4c87a74b7..4b66ca2d6 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -35,7 +35,7 @@ enum BattlerPosition B_POSITION_ABSENT = 0xFF, }; -enum BattlerId +enum __attribute__((packed)) BattlerId { B_BATTLER_0, B_BATTLER_1, @@ -284,7 +284,6 @@ enum VolatileFlags F(VOLATILE_ROOST_ACTIVE, roostActive, (u32, 1)) \ F(VOLATILE_UNBURDEN_ACTIVE, unburdenActive, (u32, 1)) \ F(VOLATILE_NEUTRALIZING_GAS, neutralizingGas, (u32, 1)) \ - F(VOLATILE_TRIGGER_ICE_FACE, triggerIceFace, (u32, 1)) \ F(VOLATILE_UNNERVE_ACTIVATED, unnerveActivated, (u32, 1)) \ F(VOLATILE_ENDURED, endured, (u32, 1)) \ F(VOLATILE_TRY_EJECT_PACK, tryEjectPack, (u32, 1)) \ @@ -410,11 +409,13 @@ enum TypeSideHazard #define MOVE_RESULT_NOT_VERY_EFFECTIVE (1 << 2) #define MOVE_RESULT_DOESNT_AFFECT_FOE (1 << 3) #define MOVE_RESULT_ONE_HIT_KO (1 << 4) -#define MOVE_RESULT_FAILED (1 << 5) -#define MOVE_RESULT_FOE_ENDURED (1 << 6) -#define MOVE_RESULT_FOE_HUNG_ON (1 << 7) -#define MOVE_RESULT_STURDIED (1 << 8) -#define MOVE_RESULT_FOE_ENDURED_AFFECTION (1 << 9) +#define MOVE_RESULT_ONE_HIT_KO_NO_AFFECT (1 << 5) +#define MOVE_RESULT_ONE_HIT_KO_STURDY (1 << 6) +#define MOVE_RESULT_FAILED (1 << 7) +#define MOVE_RESULT_FOE_ENDURED (1 << 8) +#define MOVE_RESULT_FOE_HUNG_ON (1 << 9) +#define MOVE_RESULT_STURDIED (1 << 10) +#define MOVE_RESULT_FOE_ENDURED_AFFECTION (1 << 11) #define MOVE_RESULT_AVOIDED_ATTACK (MOVE_RESULT_MISSED | MOVE_RESULT_FAILED) #define MOVE_RESULT_NO_EFFECT (MOVE_RESULT_MISSED | MOVE_RESULT_FAILED | MOVE_RESULT_DOESNT_AFFECT_FOE) @@ -594,6 +595,7 @@ enum __attribute__((packed)) MoveEffect // Move effects that happen before the move hits. Set in SetPreAttackMoveEffect MOVE_EFFECT_BREAK_SCREEN, MOVE_EFFECT_STEAL_STATS, + MOVE_EFFECT_BEAT_UP_MESSAGE, // Handles the message printing for gen2,3 and 4 NUM_MOVE_EFFECTS }; @@ -731,11 +733,6 @@ enum MoveTarget #define PARENTAL_BOND_2ND_HIT 1 #define PARENTAL_BOND_OFF 0 -// Constants for if HandleScriptMegaPrimalBurst should handle Mega Evolution, Primal Reversion, or Ultra Burst. -#define HANDLE_TYPE_MEGA_EVOLUTION 0 -#define HANDLE_TYPE_PRIMAL_REVERSION 1 -#define HANDLE_TYPE_ULTRA_BURST 2 - // Constants for Torment #define PERMANENT_TORMENT 0xF diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index 17d2487a5..84936bb1b 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -597,10 +597,11 @@ #define B_ANIM_MON_SCARED 56 #define B_ANIM_GHOST_GET_OUT 57 #define B_ANIM_SILPH_SCOPED 58 -#define B_ANIM_ROCK_THROW (B_ANIM_SILPH_SCOPED + 1) -#define B_ANIM_SAFARI_REACTION (B_ANIM_ROCK_THROW + 1) - -#define NUM_B_ANIMS_GENERAL (B_ANIM_SAFARI_REACTION + 1) +#define B_ANIM_ROCK_THROW 59 +#define B_ANIM_SAFARI_REACTION 60 +#define B_ANIM_FORM_CHANGE_INSTANT 61 +#define B_ANIM_FORM_CHANGE_DISGUISE 62 +#define NUM_B_ANIMS_GENERAL 63 // special animations table (sBattleAnims_Special) #define B_ANIM_LVL_UP 0 @@ -676,6 +677,16 @@ #define ANIM_ORDER_UP_DROOPY 2 #define ANIM_ORDER_UP_STRETCHY 3 +// AnimTask_TransformMon variations +enum SpeciesGfxChange +{ + SPECIES_GFX_CHANGE_TRANSFORM, + SPECIES_GFX_CHANGE_FORM_CHANGE, + SPECIES_GFX_CHANGE_FORM_CHANGE_INSTANT, + SPECIES_GFX_CHANGE_ILLUSION_OFF, + SPECIES_GFX_CHANGE_GHOST_UNVEIL, +}; + // Flags given to various functions to indicate which palettes to consider. // Handled by UnpackSelectedBattlePalettes #define F_PAL_BG (1 << 0) diff --git a/include/constants/battle_end_turn.h b/include/constants/battle_end_turn.h index 5ff2d5cb3..ff8cf4640 100644 --- a/include/constants/battle_end_turn.h +++ b/include/constants/battle_end_turn.h @@ -49,7 +49,7 @@ enum EndTurnResolutionOrder ENDTURN_TERRAIN, ENDTURN_THIRD_EVENT_BLOCK, ENDTURN_EMERGENCY_EXIT_4, - ENDTURN_FORM_CHANGE_ABILITIES, + ENDTURN_FORM_CHANGE, ENDTURN_EJECT_PACK, ENDTURN_DYNAMAX, ENDTURN_TRAINER_A_SLIDES, diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index f7f345af2..3ebf4ab8e 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -33,7 +33,6 @@ enum __attribute__((packed)) BattleMoveEffects EFFECT_LIGHT_SCREEN, EFFECT_REST, EFFECT_OHKO, - EFFECT_SHEER_COLD, // Same as EFFECT_OHKO but Ice-types are immune to it and has decreased accuracy for non Ice-type users. EFFECT_FUSION_COMBO, EFFECT_FIXED_PERCENT_DAMAGE, EFFECT_FIXED_HP_DAMAGE, diff --git a/include/constants/battle_move_resolution.h b/include/constants/battle_move_resolution.h index 6645ff1af..738677c97 100644 --- a/include/constants/battle_move_resolution.h +++ b/include/constants/battle_move_resolution.h @@ -1,11 +1,77 @@ #ifndef GUARD_CONSTANTS_BATTLE_MOVE_RESOLUTION_H #define GUARD_CONSTANTS_BATTLE_MOVE_RESOLUTION_H +enum Obedience +{ + OBEYS, + DISOBEYS_LOAFS, + DISOBEYS_HITS_SELF, + DISOBEYS_FALL_ASLEEP, + DISOBEYS_WHILE_ASLEEP, + DISOBEYS_RANDOM_MOVE, +}; + +enum CancelerResult +{ + CANCELER_RESULT_SUCCESS, + CANCELER_RESULT_BREAK, // Runs script. Increments state + CANCELER_RESULT_PAUSE, // Runs script. Does not increment state + CANCELER_RESULT_FAILURE, // Move failed, jump to script that handles the failure +}; + +enum CancelerState +{ + CANCELER_STANCE_CHANGE_1, + CANCELER_CLEAR_FLAGS, + CANCELER_SKY_DROP, + CANCELER_RECHARGE, + CANCELER_CHILLY_RECEPTION, + CANCELER_ASLEEP_OR_FROZEN, + CANCELER_POWER_POINTS, + CANCELER_OBEDIENCE, + CANCELER_TRUANT, + CANCELER_FOCUS_GEN5, + CANCELER_FLINCH, + CANCELER_DISABLED, + CANCELER_VOLATILE_BLOCKED, // Gravity / Heal Block / Throat Chop + CANCELER_TAUNTED, + CANCELER_IMPRISONED, + CANCELER_CONFUSED, + CANCELER_GHOST, + CANCELER_PARALYZED, + CANCELER_INFATUATION, + CANCELER_BIDE, + CANCELER_Z_MOVES, + CANCELER_CHOICE_LOCK, + CANCELER_CALLSUBMOVE, + CANCELER_THAW, + CANCELER_STANCE_CHANGE_2, + CANCELER_ATTACKSTRING, + CANCELER_PPDEDUCTION, + CANCELER_MOVE_SPECIFIC_MESSAGE, + CANCELER_SKY_BATTLE, + CANCELER_WEATHER_PRIMAL, + CANCELER_FOCUS_PRE_GEN5, + CANCELER_MOVE_FAILURE, + CANCELER_POWDER_STATUS, + CANCELER_PRIORITY_BLOCK, + CANCELER_PROTEAN, + CANCELER_EXPLODING_DAMP, + CANCELER_EXPLOSION, + CANCELER_CHARGING, + CANCELER_NO_TARGET, + CANCELER_TOOK_ATTACK, + CANCELER_TARGET_FAILURE, + CANCELER_NOT_FULLY_PROTECTED, + CANCELER_MULTIHIT_MOVES, + CANCELER_END, +}; + enum MoveEndResult { - MOVEEND_STEP_CONTINUE, - MOVEEND_STEP_RUN_SCRIPT, - MOVEEND_STEP_BREAK, + MOVEEND_RESULT_CONTINUE, + MOVEEND_RESULT_RUN_SCRIPT, + MOVEEND_RESULT_BREAK, }; // cases for Cmd_moveend - Order matters! @@ -17,6 +83,7 @@ enum MoveEndState MOVEEND_RAGE, MOVEEND_SYNCHRONIZE_TARGET, MOVEEND_ABILITIES, + MOVEEND_FORM_CHANGE_ON_HIT, // Disguise / Gulp Missile MOVEEND_ABILITIES_ATTACKER, MOVEEND_STATUS_IMMUNITY_ABILITIES, // TODO: Do berries come before???? MOVEEND_SYNCHRONIZE_ATTACKER, @@ -54,7 +121,6 @@ enum MoveEndState MOVEEND_MIRROR_HERB, MOVEEND_PICKPOCKET, MOVEEND_THIRD_MOVE_BLOCK, - MOVEEND_CHANGED_ITEMS, MOVEEND_CLEAR_BITS, MOVEEND_DANCER, MOVEEND_PURSUIT_NEXT_ACTION, diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index b8aab46b3..12ca2e503 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -10,7 +10,6 @@ enum BattleScriptOpcode B_SCR_OP_CRITCALC, B_SCR_OP_DAMAGECALC, B_SCR_OP_TYPECALC, - B_SCR_OP_ADJUSTDAMAGE, B_SCR_OP_MULTIHITRESULTMESSAGE, B_SCR_OP_ATTACKANIMATION, B_SCR_OP_WAITANIMATION, @@ -62,7 +61,7 @@ enum BattleScriptOpcode B_SCR_OP_BICWORD, B_SCR_OP_PAUSE, B_SCR_OP_WAITSTATE, - B_SCR_OP_ISDMGBLOCKEDBYDISGUISE, + B_SCR_OP_TRYSELFCONFUSIONDMGFORMCHANGE, B_SCR_OP_RETURN, B_SCR_OP_END, B_SCR_OP_END2, @@ -121,25 +120,20 @@ enum BattleScriptOpcode B_SCR_OP_HPTHRESHOLDS, B_SCR_OP_HPTHRESHOLDS2, B_SCR_OP_USEITEMONOPPONENT, - B_SCR_OP_UNUSED_0X78, B_SCR_OP_SETPROTECTLIKE, B_SCR_OP_TRYEXPLOSION, B_SCR_OP_SETATKHPTOZERO, B_SCR_OP_JUMPIFNEXTTARGETVALID, B_SCR_OP_TRYHEALHALFHEALTH, - B_SCR_OP_UNUSED_0X7E, B_SCR_OP_SETFIELDWEATHER, B_SCR_OP_SETREFLECT, B_SCR_OP_SETSEEDED, B_SCR_OP_MANIPULATEDAMAGE, B_SCR_OP_TRYSETREST, - B_SCR_OP_UNUSED_0X82, - B_SCR_OP_UNUSED_0X83, B_SCR_OP_JUMPIFUPROARWAKES, B_SCR_OP_STOCKPILE, B_SCR_OP_STOCKPILETOBASEDAMAGE, B_SCR_OP_STOCKPILETOHPHEAL, - B_SCR_OP_UNUSED_0X88, B_SCR_OP_STATBUFFCHANGE, B_SCR_OP_NORMALISEBUFFS, B_SCR_OP_SETBIDE, @@ -162,19 +156,12 @@ enum BattleScriptOpcode B_SCR_OP_SETSUBSTITUTE, B_SCR_OP_MIMICATTACKCOPY, B_SCR_OP_SETCALLEDMOVE, - B_SCR_OP_UNUSED_0X9F, - B_SCR_OP_UNUSED_0XA0, - B_SCR_OP_UNUSED_0XA1, - B_SCR_OP_UNUSED_0XA2, B_SCR_OP_DISABLELASTUSEDATTACK, B_SCR_OP_TRYSETENCORE, B_SCR_OP_PAINSPLITDMGCALC, B_SCR_OP_SETTYPETORANDOMRESISTANCE, B_SCR_OP_SETALWAYSHITFLAG, B_SCR_OP_COPYMOVEPERMANENTLY, - B_SCR_OP_UNUSED_0XA9, - B_SCR_OP_UNUSED_AA, - B_SCR_OP_UNUSED_0XAB, B_SCR_OP_SETTAILWIND, B_SCR_OP_TRYSPITEPPREDUCE, B_SCR_OP_HEALPARTYSTATUS, @@ -182,35 +169,25 @@ enum BattleScriptOpcode B_SCR_OP_TRYSETSPIKES, B_SCR_OP_SETVOLATILE, B_SCR_OP_TRYSETPERISHSONG, - B_SCR_OP_UNUSED_0XB3, B_SCR_OP_JUMPIFCONFUSEDANDSTATMAXED, - B_SCR_OP_UNUSED_0XB5, B_SCR_OP_SETEMBARGO, B_SCR_OP_PRESENTDAMAGECALCULATION, B_SCR_OP_SETSAFEGUARD, - B_SCR_OP_MAGNITUDEDAMAGECALCULATION, B_SCR_OP_JUMPIFNOPURSUITSWITCHDMG, B_SCR_OP_TRYACTIVATEITEM, B_SCR_OP_HALVEHP, B_SCR_OP_COPYFOESTATS, B_SCR_OP_RAPIDSPINFREE, - B_SCR_OP_UNUSED_0XBF, B_SCR_OP_RECOVERBASEDONSUNLIGHT, B_SCR_OP_SETSTICKYWEB, B_SCR_OP_SELECTFIRSTVALIDTARGET, B_SCR_OP_SETFUTUREATTACK, B_SCR_OP_TRYDOBEATUP, B_SCR_OP_SETSEMIINVULNERABLEBIT, - B_SCR_OP_UNUSED_0XC6, - B_SCR_OP_UNUSED_0XC7, - B_SCR_OP_UNUSED_C8, B_SCR_OP_TRYMEMENTO, B_SCR_OP_SETFORCEDTARGET, - B_SCR_OP_UNUSED_0XCB, - B_SCR_OP_UNUSED_0XCC, B_SCR_OP_CURESTATUSWITHMOVE, B_SCR_OP_SETTORMENT, - B_SCR_OP_UNUSED_0XCF, B_SCR_OP_SETTAUNT, B_SCR_OP_TRYSETHELPINGHAND, B_SCR_OP_TRYSWAPITEMS, @@ -219,29 +196,22 @@ enum BattleScriptOpcode B_SCR_OP_SETTOXICSPIKES, B_SCR_OP_SETGASTROACID, B_SCR_OP_SETYAWN, - B_SCR_OP_UNUSED0XD8, B_SCR_OP_SETROOM, B_SCR_OP_TRYSWAPABILITIES, B_SCR_OP_TRYIMPRISON, B_SCR_OP_SETSTEALTHROCK, B_SCR_OP_TRYSETVOLATILE, - B_SCR_OP_UNUSED_0XDE, B_SCR_OP_TRYSETMAGICCOAT, B_SCR_OP_TRYSETSNATCH, - B_SCR_OP_UNUSED2, B_SCR_OP_SWITCHOUTABILITIES, B_SCR_OP_JUMPIFHASNOHP, - B_SCR_OP_UNUSED_0XE4, B_SCR_OP_PICKUP, - B_SCR_OP_UNUSED_0XE6, - B_SCR_OP_UNUSED_0XE7, B_SCR_OP_SETTYPEBASEDHALVERS, B_SCR_OP_JUMPIFSUBSTITUTEBLOCKS, B_SCR_OP_TRYRECYCLEITEM, B_SCR_OP_SETTYPETOENVIRONMENT, B_SCR_OP_PURSUITDOUBLES, B_SCR_OP_SNATCHSETBATTLERS, - B_SCR_OP_UNUSED_0XEE, B_SCR_OP_HANDLEBALLTHROW, B_SCR_OP_GIVECAUGHTMON, B_SCR_OP_TRYSETCAUGHTMONDEXFLAGS, @@ -258,6 +228,40 @@ enum BattleScriptOpcode B_SCR_OP_JUMPIFCAPTIVATEAFFECTED, B_SCR_OP_SETNONVOLATILESTATUS, B_SCR_OP_TRYOVERWRITEABILITY, + + // Expansion users, please don't use any of the unused commands. + // They are reserved for expansion usage. + // Use callnatives instead. + B_SCR_OP_UNUSED_1, + B_SCR_OP_UNUSED_2, + B_SCR_OP_UNUSED_3, + B_SCR_OP_UNUSED_4, + B_SCR_OP_UNUSED_5, + B_SCR_OP_UNUSED_6, + B_SCR_OP_UNUSED_7, + B_SCR_OP_UNUSED_8, + B_SCR_OP_UNUSED_9, + B_SCR_OP_UNUSED_10, + B_SCR_OP_UNUSED_11, + B_SCR_OP_UNUSED_12, + B_SCR_OP_UNUSED_13, + B_SCR_OP_UNUSED_14, + B_SCR_OP_UNUSED_15, + B_SCR_OP_UNUSED_16, + B_SCR_OP_UNUSED_17, + B_SCR_OP_UNUSED_18, + B_SCR_OP_UNUSED_19, + B_SCR_OP_UNUSED_20, + B_SCR_OP_UNUSED_21, + B_SCR_OP_UNUSED_22, + B_SCR_OP_UNUSED_23, + B_SCR_OP_UNUSED_24, + B_SCR_OP_UNUSED_25, + B_SCR_OP_UNUSED_26, + B_SCR_OP_UNUSED_27, + B_SCR_OP_UNUSED_28, + B_SCR_OP_UNUSED_29, + B_SCR_OP_UNUSED_30, B_SCR_OP_CALLNATIVE, }; diff --git a/include/constants/battle_setup.h b/include/constants/battle_setup.h index 9492280d6..094d054e6 100644 --- a/include/constants/battle_setup.h +++ b/include/constants/battle_setup.h @@ -11,6 +11,6 @@ #define TRAINER_BATTLE_REMATCH_DOUBLE 7 #define TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE_NO_MUSIC 8 #define TRAINER_BATTLE_EARLY_RIVAL 9 -#define TRAINER_BATTLE_SET_TRAINERS_FOR_MULTI_BATTLE 10 +#define TRAINER_BATTLE_TWO_TRAINERS_NO_INTRO 10 #endif // GUARD_CONSTANTS_BATTLE_SETUP_H diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 5234c2783..2942af5a2 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -26,7 +26,6 @@ enum StringID STRINGID_ATTACKMISSED, STRINGID_PKMNPROTECTEDITSELF, STRINGID_STATSWONTINCREASE2, - STRINGID_AVOIDEDDAMAGE, STRINGID_ITDOESNTAFFECT, STRINGID_SCR_ITDOESNTAFFECT, STRINGID_BATTLERFAINTED, @@ -295,7 +294,6 @@ enum StringID STRINGID_ITEMALLOWSONLYYMOVE, STRINGID_PKMNHUNGONWITHX, STRINGID_EMPTYSTRING3, - STRINGID_PKMNSXBLOCKSY, STRINGID_PKMNSXRESTOREDHPALITTLE2, STRINGID_PKMNSXWHIPPEDUPSANDSTORM, STRINGID_PKMNSXPREVENTSYLOSS, @@ -321,7 +319,6 @@ enum StringID STRINGID_EMPTYSTRING4, STRINGID_ABOOSTED, STRINGID_PKMNSXINTENSIFIEDSUN, - STRINGID_PKMNMAKESGROUNDMISS, STRINGID_YOUTHROWABALLNOWRIGHT, STRINGID_PKMNSXTOOKATTACK, STRINGID_PKMNCHOSEXASDESTINY, @@ -338,6 +335,7 @@ enum StringID STRINGID_PKMNSXPREVENTSFLINCHING, STRINGID_PKMNALREADYHASBURN, STRINGID_STATSWONTDECREASE2, + STRINGID_PKMNSXBLOCKSY, STRINGID_PKMNSXWOREOFF, STRINGID_THEWALLSHATTERED, STRINGID_PKMNSXCUREDITSYPROBLEM, @@ -455,6 +453,7 @@ enum StringID STRINGID_STEALTHROCKDMG, STRINGID_TOXICSPIKESABSORBED, STRINGID_TOXICSPIKESPOISONED, + STRINGID_TOXICSPIKESBADLYPOISONED, STRINGID_STICKYWEBSWITCHIN, STRINGID_HEALINGWISHCAMETRUE, STRINGID_HEALINGWISHHEALED, @@ -710,7 +709,6 @@ enum StringID STRINGID_TIMETODYNAMAX, STRINGID_TIMETOGIGANTAMAX, STRINGID_QUESTIONFORFEITBATTLE, - STRINGID_TOXICSPIKESBADLYPOISONED, STRINGID_POWERCONSTRUCTPRESENCEOFMANY, STRINGID_POWERCONSTRUCTTRANSFORM, STRINGID_ABILITYSHIELDPROTECTS, @@ -719,14 +717,15 @@ enum StringID STRINGID_SILPHSCOPEUNVEILED, STRINGID_GHOSTWASMAROWAK, STRINGID_TRAINER1MON1COMEBACK, - - // pokefirered specific - STRINGID_OLDMANUSEDITEM, - STRINGID_GOTCHAPKMNCAUGHTOLDMAN, STRINGID_THREWROCK, STRINGID_THREWBAIT, STRINGID_PKMNANGRY, STRINGID_PKMNEATING, + STRINGID_PKMNDISGUISEWASBUSTED, + + // pokefirered specific + STRINGID_OLDMANUSEDITEM, + STRINGID_GOTCHAPKMNCAUGHTOLDMAN, STRINGID_PKMNTRANSFERREDBILLSPC, STRINGID_PKMNBOXBILLSPCFULL, STRINGID_POKEDUDEUSED, diff --git a/include/constants/battle_switch_in.h b/include/constants/battle_switch_in.h index 3a3084472..5a75b3c1f 100644 --- a/include/constants/battle_switch_in.h +++ b/include/constants/battle_switch_in.h @@ -8,6 +8,7 @@ enum SwitchInEvents SWITCH_IN_EVENTS_NEUTRALIZING_GAS, SWITCH_IN_EVENTS_UNNERVE, SWITCH_IN_EVENTS_FIRST_BLOCK, + SWITCH_IN_EVENTS_FORM_CHANGE, SWITCH_IN_EVENTS_SECOND_BLOCK, SWITCH_IN_EVENTS_WHITE_HERB, SWITCH_IN_EVENTS_OPPORTUNIST, diff --git a/include/constants/battle_z_move_effects.h b/include/constants/battle_z_move_effects.h index ce4d5c4ad..b62fd3d88 100644 --- a/include/constants/battle_z_move_effects.h +++ b/include/constants/battle_z_move_effects.h @@ -1,38 +1,40 @@ #ifndef GUARD_Z_MOVE_EFFECTS_H #define GUARD_Z_MOVE_EFFECTS_H -#define Z_EFFECT_NONE 0 -#define Z_EFFECT_RESET_STATS 1 -#define Z_EFFECT_ALL_STATS_UP_1 2 -#define Z_EFFECT_BOOST_CRITS 3 -#define Z_EFFECT_FOLLOW_ME 4 -#define Z_EFFECT_CURSE 5 -#define Z_EFFECT_RECOVER_HP 6 -#define Z_EFFECT_RESTORE_REPLACEMENT_HP 7 +enum ZEffect +{ + Z_EFFECT_NONE, + Z_EFFECT_RESET_STATS, + Z_EFFECT_ALL_STATS_UP_1, + Z_EFFECT_BOOST_CRITS, + Z_EFFECT_FOLLOW_ME, + Z_EFFECT_CURSE, + Z_EFFECT_RECOVER_HP, + Z_EFFECT_RESTORE_REPLACEMENT_HP, -#define Z_EFFECT_ATK_UP_1 8 -#define Z_EFFECT_DEF_UP_1 9 -#define Z_EFFECT_SPD_UP_1 10 -#define Z_EFFECT_SPATK_UP_1 11 -#define Z_EFFECT_SPDEF_UP_1 12 -#define Z_EFFECT_ACC_UP_1 13 -#define Z_EFFECT_EVSN_UP_1 14 + Z_EFFECT_ATK_UP_1, + Z_EFFECT_DEF_UP_1, + Z_EFFECT_SPD_UP_1, + Z_EFFECT_SPATK_UP_1, + Z_EFFECT_SPDEF_UP_1, + Z_EFFECT_ACC_UP_1, + Z_EFFECT_EVSN_UP_1, -#define Z_EFFECT_ATK_UP_2 15 -#define Z_EFFECT_DEF_UP_2 16 -#define Z_EFFECT_SPD_UP_2 17 -#define Z_EFFECT_SPATK_UP_2 18 -#define Z_EFFECT_SPDEF_UP_2 19 -#define Z_EFFECT_ACC_UP_2 20 -#define Z_EFFECT_EVSN_UP_2 21 - -#define Z_EFFECT_ATK_UP_3 22 -#define Z_EFFECT_DEF_UP_3 23 -#define Z_EFFECT_SPD_UP_3 24 -#define Z_EFFECT_SPATK_UP_3 25 -#define Z_EFFECT_SPDEF_UP_3 26 -#define Z_EFFECT_ACC_UP_3 27 -#define Z_EFFECT_EVSN_UP_3 28 + Z_EFFECT_ATK_UP_2, + Z_EFFECT_DEF_UP_2, + Z_EFFECT_SPD_UP_2, + Z_EFFECT_SPATK_UP_2, + Z_EFFECT_SPDEF_UP_2, + Z_EFFECT_ACC_UP_2, + Z_EFFECT_EVSN_UP_2, + Z_EFFECT_ATK_UP_3, + Z_EFFECT_DEF_UP_3, + Z_EFFECT_SPD_UP_3, + Z_EFFECT_SPATK_UP_3, + Z_EFFECT_SPDEF_UP_3, + Z_EFFECT_ACC_UP_3, + Z_EFFECT_EVSN_UP_3, +}; #endif // GUARD_Z_MOVE_EFFECTS_H diff --git a/include/constants/berry.h b/include/constants/berry.h index 90292181d..f4ecc74bb 100644 --- a/include/constants/berry.h +++ b/include/constants/berry.h @@ -17,12 +17,15 @@ #define BERRY_COLOR_YELLOW 4 #define BERRY_COLOR_PINK 5 -#define FLAVOR_SPICY 0 -#define FLAVOR_DRY 1 -#define FLAVOR_SWEET 2 -#define FLAVOR_BITTER 3 -#define FLAVOR_SOUR 4 -#define FLAVOR_COUNT 5 +enum __attribute__((__packed__)) Flavor +{ + FLAVOR_SPICY, + FLAVOR_DRY, + FLAVOR_SWEET, + FLAVOR_BITTER, + FLAVOR_SOUR, + FLAVOR_COUNT, +}; #define BERRY_STAGE_NO_BERRY 0 // there is no tree planted and the soil is completely flat. #define BERRY_STAGE_PLANTED 1 diff --git a/include/constants/field_poison.h b/include/constants/field_poison.h new file mode 100644 index 000000000..a65e853b2 --- /dev/null +++ b/include/constants/field_poison.h @@ -0,0 +1,14 @@ +#ifndef GUARD_CONSTANTS_FIELD_POISON_H +#define GUARD_CONSTANTS_FIELD_POISON_H + +enum { + FLDPSN_NONE, + FLDPSN_PSN, + FLDPSN_FNT +}; + +#define FLDPSN_NO_WHITEOUT 0 +#define FLDPSN_WHITEOUT 1 +#define FLDPSN_FRONTIER_WHITEOUT 2 + +#endif //GUARD_CONSTANTS_FIELD_POISON_H \ No newline at end of file diff --git a/include/constants/form_change_types.h b/include/constants/form_change_types.h index f43f3bddc..1cdd15049 100644 --- a/include/constants/form_change_types.h +++ b/include/constants/form_change_types.h @@ -1,7 +1,7 @@ #ifndef GUARD_CONSTANTS_FORM_CHANGE_TYPES_H #define GUARD_CONSTANTS_FORM_CHANGE_TYPES_H -// FORM_CHANGE_BATTLE_HP_PERCENT param2 arguments +// FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END / FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT param2 arguments enum FormChangeBattleHPPercentArguments { HP_HIGHER_THAN = 1, @@ -47,8 +47,6 @@ enum FormChanges // - No parameters. FORM_CHANGE_WITHDRAW, // Form change that activates when the Pokémon faints, either in battle or in the overworld by poison. - // If species is not specified and it's on the player's side, it will try to use the value - // saved in gBattleStruct->partyState[x][y].changedSpecies from a previous form change. // - No parameters. FORM_CHANGE_FAINT, // Form change that activates when the Pokémon is sent out at the beginning of a battle @@ -65,15 +63,36 @@ enum FormChanges // param1: battle environment to check. FORM_CHANGE_END_BATTLE_ENVIRONMENT, // Form change that activates when the Pokémon is switched out in battle. - // param1: ability to check, optional - FORM_CHANGE_BATTLE_SWITCH, + // param1: (optional) ability to check + FORM_CHANGE_BATTLE_SWITCH_OUT, + // Form change that activates when the Pokémon is switched-in in battle. + // This form change happens before other abilities like Neutralizing Gas. + // param1: ability to check + FORM_CHANGE_BATTLE_SWITCH_IN, // Form change that activates when the Pokémon's HP % passes a certain threshold. // param1: Ability to check. // param2: HP comparer // - HP_HIGHER_THAN if the form triggers when the current HP is higher than the specified threshold. // - HP_LOWER_EQ_THAN if the form triggers when the current HP is lower or equal than the specified threshold. // param3: HP percentage threshold. - FORM_CHANGE_BATTLE_HP_PERCENT, + // param4: (optional) Minimum level to do form change + FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, + // Form change that activates when the Pokémon's HP % passes a certain threshold when sent out into battle. + // param1: Ability to check. + // param2: HP comparer + // - HP_HIGHER_THAN if the form triggers when the current HP is higher than the specified threshold. + // - HP_LOWER_EQ_THAN if the form triggers when the current HP is lower or equal than the specified threshold. + // param3: HP percentage threshold. + // param4: (optional) Minimum level to do form change + FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, + // Form change that activates when the Pokémon's HP % passes a certain threshold when using a move. + // param1: Ability to check. + // param2: HP comparer + // - HP_HIGHER_THAN if the form triggers when the current HP is higher than the specified threshold. + // - HP_LOWER_EQ_THAN if the form triggers when the current HP is lower or equal than the specified threshold. + // param3: HP percentage threshold. + // param4: Move used. + FORM_CHANGE_BATTLE_HP_PERCENT_DURING_MOVE, // Form change that activates when the mon has the defined item. // If it's on the player's side, it also requires ITEM_MEGA_RING in the user's bag and for the player to trigger it by pressing START before selecting a move. // param1: item to hold. @@ -112,8 +131,13 @@ enum FormChanges // Form change that activates when inflicted with a specific status // param1: status FORM_CHANGE_STATUS, - // Form change that activates after move is used. Currently only used for activating Gulp Missile. - FORM_CHANGE_HIT_BY_MOVE, + // Form change that activates after receiving an attack. + // param1: ability + // param2: move category to check + FORM_CHANGE_BATTLE_HIT_BY_MOVE_CATEGORY, + // Form change that activates after hitting itself in confusion. + // param1: ability + FORM_CHANGE_BATTLE_HIT_BY_CONFUSION_SELF_DMG, // Form change that activates when terastallized as as a specific type // param1: tera type FORM_CHANGE_BATTLE_TERASTALLIZATION, diff --git a/include/constants/generational_changes.h b/include/constants/generational_changes.h index c764712ba..4709a2d7e 100644 --- a/include/constants/generational_changes.h +++ b/include/constants/generational_changes.h @@ -86,11 +86,13 @@ F(MINIMIZE_EVASION, minimizeEvasion, (u32, GEN_COUNT - 1)) \ F(GROWTH_STAT_RAISE, growthStatRaise, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(FOCUS_ENERGY_CRIT_RATIO, focusEnergyCritRatio, (u32, GEN_COUNT - 1)) \ + F(PSYCH_UP_CRIT_RATIO, psychUpCritRatio, (u32, GEN_COUNT - 1)) \ /* Other move settings */ \ F(INCINERATE_GEMS, incinerateGems, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(CAN_SPITE_FAIL, canSpiteFail, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(CRASH_IF_TARGET_IMMUNE, crashIfTargetImmune, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(MEMENTO_FAIL, mementoFail, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ + F(PARTING_SHOT_SWITCH, partingShotSwitch, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(GLARE_GHOST, glareGhost, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(SKILL_SWAP, skillSwap, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(BRICK_BREAK, brickBreak, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ @@ -120,6 +122,8 @@ F(AFTER_YOU_TURN_ORDER, afterYouTurnOrder, (u32, GEN_COUNT - 1)) \ F(QUASH_TURN_ORDER, quashTurnOrder, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(DESTINY_BOND_FAIL, destinyBondFail, (u32, GEN_COUNT - 1)) \ + F(FORESIGHT_FAIL, foresightFail, (u32, GEN_COUNT - 1)) \ + F(MIRACLE_EYE_FAIL, miracleEyeFail, (u32, GEN_COUNT - 1)) \ F(PURSUIT_TARGET, pursuitTarget, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(SKIP_RECHARGE, skipRecharge, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(ENCORE_TARGET, encoreTarget, (u32, GEN_COUNT - 1)) \ diff --git a/include/constants/global.h b/include/constants/global.h index 6e9cd805a..22c6a077a 100644 --- a/include/constants/global.h +++ b/include/constants/global.h @@ -33,14 +33,17 @@ #define NUM_VERSIONS 15 -#define LANGUAGE_JAPANESE 1 -#define LANGUAGE_ENGLISH 2 -#define LANGUAGE_FRENCH 3 -#define LANGUAGE_ITALIAN 4 -#define LANGUAGE_GERMAN 5 -#define LANGUAGE_KOREAN 6 // 6 goes unused but the theory is it was meant to be Korean -#define LANGUAGE_SPANISH 7 -#define NUM_LANGUAGES 7 +enum Language +{ + LANGUAGE_JAPANESE = 1, + LANGUAGE_ENGLISH = 2, + LANGUAGE_FRENCH = 3, + LANGUAGE_ITALIAN = 4, + LANGUAGE_GERMAN = 5, + LANGUAGE_KOREAN = 6, // 6 goes unused but the theory is it was meant to be Korean + LANGUAGE_SPANISH = 7, + NUM_LANGUAGES +}; #define GAME_LANGUAGE (LANGUAGE_ENGLISH) @@ -134,9 +137,12 @@ enum FrontierLevelMode #define NUM_TOWER_CHALLENGE_TYPES 4 -#define MALE 0 -#define FEMALE 1 -#define GENDER_COUNT 2 +enum Gender +{ + MALE, + FEMALE, + GENDER_COUNT, +}; #define BARD_SONG_LENGTH 6 #define NUM_STORYTELLER_TALES 4 diff --git a/include/constants/songs.h b/include/constants/songs.h index 06ebcd708..017299d15 100644 --- a/include/constants/songs.h +++ b/include/constants/songs.h @@ -360,8 +360,6 @@ #define MUS_NONE 0xFFFF // placeholders -#define SE_SUDOWOODO_SHAKE SE_USE_ITEM -#define SE_POKENAV_CALL SE_USE_ITEM #define MUS_B_TOWER MUS_ROUTE1 #define MUS_B_FRONTIER MUS_ROUTE1 #define MUS_B_ARENA MUS_ROUTE1 @@ -375,12 +373,20 @@ #define MUS_B_DOME MUS_ROUTE1 #define MUS_B_FACTORY MUS_ROUTE1 #define MUS_B_PYRAMID_TOP MUS_ROUTE1 +#define MUS_VS_FRONTIER_BRAIN MUS_VS_CHAMPION +#define MUS_VS_KYOGRE_GROUDON MUS_VS_LEGEND +#define MUS_VS_RAYQUAZA MUS_VS_LEGEND +#define MUS_VS_MEW MUS_VS_LEGEND +#define MUS_VS_REGI MUS_VS_LEGEND + #define MUS_OBTAIN_SYMBOL SE_USE_ITEM #define MUS_OBTAIN_B_POINTS SE_USE_ITEM #define SE_ARENA_TIMEUP1 SE_USE_ITEM #define SE_ARENA_TIMEUP2 SE_USE_ITEM #define SE_PIKE_CURTAIN_CLOSE SE_USE_ITEM #define SE_PIKE_CURTAIN_OPEN SE_USE_ITEM +#define SE_SUDOWOODO_SHAKE SE_USE_ITEM +#define SE_POKENAV_CALL SE_USE_ITEM #endif // GUARD_CONSTANTS_SONGS_H diff --git a/include/constants/trainers.h b/include/constants/trainers.h index 508a7578b..5db61a229 100644 --- a/include/constants/trainers.h +++ b/include/constants/trainers.h @@ -19,7 +19,7 @@ #define TRAINER_ENCOUNTER_MUSIC_INTERVIEWER 12 #define TRAINER_ENCOUNTER_MUSIC_RICH 13 // Rich Boys and Gentlemen - +// TODO: replace with actual trainer classes // placeholders #define FACILITY_CLASS_TUBER_M FACILITY_CLASS_RUIN_MANIAC #define FACILITY_CLASS_PKMN_BREEDER_M FACILITY_CLASS_RUIN_MANIAC @@ -27,9 +27,16 @@ #define FACILITY_CLASS_TUBER_F FACILITY_CLASS_AROMA_LADY #define FACILITY_CLASS_PKMN_BREEDER_F FACILITY_CLASS_AROMA_LADY -#define TRAINER_CLASS_RIVAL TRAINER_CLASS_RIVAL_LATE -#define TRAINER_CLASS_TUBER_M TRAINER_CLASS_RS_TUBER_M -#define TRAINER_CLASS_TUBER_F TRAINER_CLASS_RS_TUBER_F +#define TRAINER_CLASS_RIVAL TRAINER_CLASS_RIVAL_LATE +#define TRAINER_CLASS_TUBER_M TRAINER_CLASS_RS_TUBER_M +#define TRAINER_CLASS_TUBER_F TRAINER_CLASS_RS_TUBER_F +#define TRAINER_CLASS_SALON_MAIDEN TRAINER_CLASS_TUBER +#define TRAINER_CLASS_DOME_ACE TRAINER_CLASS_PKMN_BREEDER +#define TRAINER_CLASS_PALACE_MAVEN TRAINER_CLASS_PKMN_RANGER +#define TRAINER_CLASS_ARENA_TYCOON TRAINER_CLASS_AROMA_LADY +#define TRAINER_CLASS_FACTORY_HEAD TRAINER_CLASS_RUIN_MANIAC +#define TRAINER_CLASS_PIKE_QUEEN TRAINER_CLASS_LADY +#define TRAINER_CLASS_PYRAMID_KING TRAINER_CLASS_PAINTER diff --git a/include/data.h b/include/data.h index e2a597b06..733ae6dff 100644 --- a/include/data.h +++ b/include/data.h @@ -232,7 +232,7 @@ static inline u16 SanitizeTrainerId(u16 trainerId) { switch (trainerId) { - case TRAINER_RECORD_MIXING_FRIEND: + // case TRAINER_RECORD_MIXING_FRIEND: case TRAINER_RECORD_MIXING_APPRENTICE: case TRAINER_EREADER: case TRAINER_FRONTIER_BRAIN: diff --git a/include/event_scripts.h b/include/event_scripts.h index ca11d67a4..528c9481c 100644 --- a/include/event_scripts.h +++ b/include/event_scripts.h @@ -844,6 +844,7 @@ extern const u8 EventScript_TryDoDoubleRematchBattle[]; extern const u8 EventScript_TryDoRematchBattle[]; extern const u8 EventScript_StartTrainerApproach[]; extern const u8 EventScript_TestSignpostMsg[]; +extern const u8 EventScript_TryGetTrainerScript[]; extern const u8 EventScript_ObjectApproachPlayer[]; extern const u8 BerryTreeScript[]; diff --git a/include/field_message_box.h b/include/field_message_box.h index 430f2baf0..fcceaff99 100644 --- a/include/field_message_box.h +++ b/include/field_message_box.h @@ -11,6 +11,7 @@ enum { }; bool8 ShowFieldMessage(const u8 *message); +bool8 ShowFieldMessageFromBuffer(void); bool8 ShowFieldAutoScrollMessage(const u8 *message); void HideFieldMessageBox(void); bool8 IsFieldMessageBoxHidden(void); diff --git a/include/field_poison.h b/include/field_poison.h index a29cf1fd6..36eb67728 100644 --- a/include/field_poison.h +++ b/include/field_poison.h @@ -3,12 +3,6 @@ #include "global.h" -enum { - FLDPSN_NONE, - FLDPSN_PSN, - FLDPSN_FNT -}; - s32 DoPoisonFieldEffect(void); #endif //GUARD_FIELD_POISON_H diff --git a/include/field_specials.h b/include/field_specials.h index b03799f84..cbeccd4a6 100644 --- a/include/field_specials.h +++ b/include/field_specials.h @@ -33,5 +33,6 @@ bool8 InPokemonCenter(void); void UpdateFrontierManiac(u16 daysSince); void UpdateFrontierGambler(u16 daysSince); void FrontierGamblerSetWonOrLost(bool8 won); +bool8 InMultiPartnerRoom(void); #endif // GUARD_FIELD_SPECIALS_H diff --git a/include/main.h b/include/main.h index fcbf4cbe0..5e709272c 100644 --- a/include/main.h +++ b/include/main.h @@ -37,7 +37,7 @@ struct Main /*0x439*/ u8 oamLoadDisabled:1; /*0x439*/ u8 inBattle:1; - /*0x439*/ u8 field_439_x4:1; + /*0x439*/ u8 anyLinkBattlerHasFrontierPass:1; }; #define GAME_CODE_LENGTH 4 diff --git a/include/move.h b/include/move.h index 1d63abbc1..a7cac78aa 100644 --- a/include/move.h +++ b/include/move.h @@ -1,11 +1,13 @@ #ifndef GUARD_MOVES_H #define GUARD_MOVES_H +#include "generational_changes.h" #include "contest_effect.h" #include "constants/battle.h" #include "constants/battle_factory.h" #include "constants/battle_move_effects.h" #include "constants/battle_string_ids.h" +#include "constants/battle_z_move_effects.h" #include "constants/moves.h" // For defining EFFECT_HIT etc. with battle TV scores and flags etc. @@ -94,6 +96,7 @@ struct MoveInfo u32 criticalHitStage:2; bool32 alwaysCriticalHit:1; u32 numAdditionalEffects:3; // limited to 7 + // Flags bool32 makesContact:1; bool32 ignoresProtect:1; @@ -126,6 +129,12 @@ struct MoveInfo bool32 alwaysHitsInRain:1; bool32 accuracy50InSun:1; bool32 alwaysHitsInHailSnow:1; + bool32 alwaysHitsOnSameType:1; // Always hits if user is of same type as move + bool32 noAffectOnSameTypeTarget:1; // Fails if target is of same type as move + bool32 accIncreaseByTenOnSameType:1; // Accuracy is increased by 10% if user is of same type as move + bool32 padding1:15; + // end of word + // Ban flags bool32 gravityBanned:1; bool32 mirrorMoveBanned:1; @@ -143,7 +152,7 @@ struct MoveInfo bool32 dampBanned:1; //Other bool32 validApprenticeMove:1; - u32 padding:3; + u32 padding2:17; // end of word union { @@ -165,7 +174,7 @@ struct MoveInfo } reflectDamage; struct { u16 terrain; - u16 percent:14; + u16 percent:13; enum TerrainGroundCheck groundCheck:2; u16 hitsBothFoes:1; } terrainBoost; @@ -259,7 +268,7 @@ static inline u32 GetMovePP(enum Move moveId) return gMovesInfo[SanitizeMoveId(moveId)].pp; } -static inline u32 GetMoveZEffect(enum Move moveId) +static inline enum ZEffect GetMoveZEffect(enum Move moveId) { moveId = SanitizeMoveId(moveId); assertf(GetMoveCategory(moveId) == DAMAGE_CATEGORY_STATUS, "not a status move: %S", gMovesInfo[moveId].name); @@ -458,6 +467,29 @@ static inline bool32 MoveAlwaysHitsInHailSnow(enum Move moveId) return gMovesInfo[SanitizeMoveId(moveId)].alwaysHitsInHailSnow; } +static inline bool32 MoveAlwaysHitsOnSameType(enum Move moveId) +{ + #if TESTING + if (moveId == MOVE_TOXIC && GetConfig(CONFIG_TOXIC_NEVER_MISS) < GEN_6) + return FALSE; + #endif + return gMovesInfo[SanitizeMoveId(moveId)].alwaysHitsOnSameType; +} + +static inline bool32 MoveHasNoEffectOnSameType(enum Move moveId) +{ + #if TESTING + if (moveId == MOVE_SHEER_COLD && GetConfig(CONFIG_SHEER_COLD_IMMUNITY) < GEN_7) + return FALSE; + #endif + return gMovesInfo[SanitizeMoveId(moveId)].noAffectOnSameTypeTarget; +} + +static inline bool32 MoveHasIncreasedAccByTenOnSameType(enum Move moveId) +{ + return gMovesInfo[SanitizeMoveId(moveId)].accIncreaseByTenOnSameType; +} + static inline bool32 IsMoveGravityBanned(enum Move moveId) { return gMovesInfo[SanitizeMoveId(moveId)].gravityBanned; @@ -613,7 +645,7 @@ static inline u32 GetMoveTerrainBoost_GroundCheck(enum Move moveId) return gMovesInfo[moveId].argument.terrainBoost.groundCheck; } -static inline u32 GetMoveTerrainBoost_HitsBothFoes(enum Move moveId) +static inline bool32 GetMoveTerrainBoost_HitsBothFoes(enum Move moveId) { moveId = SanitizeMoveId(moveId); assertf(gMovesInfo[moveId].effect == EFFECT_TERRAIN_BOOST, "not a terrain boosted move: %S", GetMoveName(moveId)); @@ -688,7 +720,7 @@ static inline u32 GetMoveRecoil(enum Move moveId) return gMovesInfo[moveId].argument.recoilPercentage; } -static inline u32 GetMoveNonVolatileStatus(enum Move move) +static inline enum MoveEffect GetMoveNonVolatileStatus(enum Move move) { move = SanitizeMoveId(move); switch (GetMoveEffect(move)) diff --git a/include/party_menu.h b/include/party_menu.h index 7ae17630d..f7f383460 100644 --- a/include/party_menu.h +++ b/include/party_menu.h @@ -84,7 +84,7 @@ void ChooseMonForWirelessMinigame(void); void OpenPartyMenuInBattle(u8 partyAction); void Pokedude_OpenPartyMenuInBattle(void); void Pokedude_ChooseMonForInBattleItem(void); -void EnterPartyFromItemMenuInBattle(void); +void ChooseMonForInBattleItem(void); void BufferBattlePartyCurrentOrder(void); void BufferBattlePartyCurrentOrderBySide(u8 battlerId, u8 flankId); void SwitchPartyOrderLinkMulti(u8 battlerId, u8 slot, u8 slot2); diff --git a/include/pokemon.h b/include/pokemon.h index ae972bf05..c8e08cff1 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -653,6 +653,26 @@ struct FormChange u16 param1; u16 param2; u16 param3; + u16 param4; +}; + +struct FormChangeContext +{ + enum FormChanges method:16; + u16 currentSpecies; + u16 partyItemUsed; + u16 multichoiceSelection; + u16 heldItem; + u16 ability; + u16 learnedMove; + u32 status; + u16 moves[MAX_MON_MOVES]; + u16 hp; + u16 maxHP; + u32 gmaxFactor:1; + enum Type teraType; + u32 level:7; + u32 padding:8; }; enum FusionExtraMoveHandling @@ -760,6 +780,7 @@ u16 GiveMoveToMon(struct Pokemon *mon, enum Move move); u16 GiveMoveToBoxMon(struct BoxPokemon *boxMon, enum Move move); u16 GiveMoveToBattleMon(struct BattlePokemon *mon, enum Move move); void SetMonMoveSlot(struct Pokemon *mon, enum Move move, u8 slot); +void SetBoxMonMoveSlot(struct BoxPokemon *mon, enum Move move, u8 slot); void SetBattleMonMoveSlot(struct BattlePokemon *mon, enum Move move, u8 slot); void GiveMonInitialMoveset(struct Pokemon *mon); void GiveBoxMonInitialMoveset(struct BoxPokemon *boxMon); @@ -769,8 +790,8 @@ enum Move MonTryLearningNewMoveAtLevel(struct Pokemon *mon, bool32 firstMove, u3 enum Move MonTryLearningNewMove(struct Pokemon *mon, bool8 firstMove); void DeleteFirstMoveAndGiveMoveToMon(struct Pokemon *mon, enum Move move); void DeleteFirstMoveAndGiveMoveToBoxMon(struct BoxPokemon *boxMon, enum Move move); -u8 CountAliveMonsInBattle(u8 caseId, u32 battler); -u8 GetDefaultMoveTarget(u8 battler); +u8 CountAliveMonsInBattle(u8 caseId, enum BattlerId battler); +u8 GetDefaultMoveTarget(enum BattlerId battler); u8 GetMonGender(struct Pokemon *mon); u8 GetBoxMonGender(struct BoxPokemon *boxMon); u8 GetGenderFromSpeciesAndPersonality(u16 species, u32 personality); @@ -799,7 +820,7 @@ u8 GiveCapturedMonToPlayer(struct Pokemon *mon); u8 CopyMonToPC(struct Pokemon *mon); u8 CalculatePlayerPartyCount(void); u8 CalculateEnemyPartyCount(void); -u8 CalculateEnemyPartyCountInSide(u32 battler); +u8 CalculateEnemyPartyCountInSide(enum BattlerId battler); u8 GetMonsStateToDoubles(void); u8 GetMonsStateToDoubles_2(void); enum Ability GetAbilityBySpecies(u16 species, u8 abilityNum); @@ -834,11 +855,11 @@ void RemoveMonPPBonus(struct Pokemon *mon, u8 moveIndex); void RemoveBoxMonPPBonus(struct BoxPokemon *mon, u8 moveIndex); void RemoveBattleMonPPBonus(struct BattlePokemon *mon, u8 moveIndex); void PokemonToBattleMon(struct Pokemon *src, struct BattlePokemon *dst); -void CopyPartyMonToBattleData(u32 battler, u32 partyIndex); +void CopyPartyMonToBattleData(enum BattlerId battler, u32 partyIndex); bool8 ExecuteTableBasedItemEffect(struct Pokemon *mon, enum Item item, u8 partyIndex, u8 moveIndex); bool8 PokemonUseItemEffects(struct Pokemon *mon, enum Item item, u8 partyIndex, u8 moveIndex, u8 usedByAI); -bool8 HealStatusConditions(struct Pokemon *mon, u32 healMask, u8 battler); -u8 GetItemEffectParamOffset(u32 battler, enum Item itemId, u8 effectByte, u8 effectBit); +bool8 HealStatusConditions(struct Pokemon *mon, u32 healMask, enum BattlerId battler); +u8 GetItemEffectParamOffset(enum BattlerId battler, enum Item itemId, u8 effectByte, u8 effectBit); u8 *UseStatIncreaseItem(enum Item itemId); u8 GetNature(struct Pokemon *mon); u8 GetNatureFromPersonality(u32 personality); @@ -910,8 +931,8 @@ const u16 *GetMonSpritePalFromSpeciesIsEgg(u16 species, bool32 isShiny, bool32 i bool32 IsMoveHM(enum Move move); bool32 CannotForgetMove(enum Move move); bool8 IsMonSpriteNotFlipped(u16 species); -s8 GetMonFlavorRelation(struct Pokemon *mon, u8 flavor); -s8 GetFlavorRelationByPersonality(u32 personality, u8 flavor); +s8 GetMonFlavorRelation(struct Pokemon *mon, enum Flavor flavor); +s8 GetFlavorRelationByPersonality(u32 personality, enum Flavor flavor); bool8 IsTradedMon(struct Pokemon *mon); bool8 IsOtherTrainer(u32 otId, u8 *otName); void MonRestorePP(struct Pokemon *mon); @@ -927,7 +948,7 @@ void StopPokemonAnimationDelayTask(void); void BattleAnimateBackSprite(struct Sprite *sprite, u16 species); u8 GetOpposingLinkMultiBattlerId(bool8 rightSide, u8 multiplayerId); enum TrainerPicID FacilityClassToPicIndex(u16 facilityClass); -enum TrainerPicID PlayerGenderToFrontTrainerPicId(u8 playerGender); +enum TrainerPicID PlayerGenderToFrontTrainerPicId(enum Gender playerGender); void HandleSetPokedexFlag(enum NationalDexOrder nationalNum, u8 caseId, u32 personality); void HandleSetPokedexFlagFromMon(struct Pokemon *mon, u32 caseId); bool8 HasTwoFramesAnimation(u16 species); @@ -936,14 +957,14 @@ void DestroyMonSpritesGfxManager(u8 managerId); u8 *MonSpritesGfxManager_GetSpritePtr(u8 managerId, u8 spriteNum); u16 GetFormSpeciesId(u16 speciesId, u8 formId); u8 GetFormIdFromFormSpeciesId(u16 formSpeciesId); -u32 GetFormChangeTargetSpecies(struct Pokemon *mon, enum FormChanges method, u32 arg); -u32 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *boxMon, enum FormChanges method, u32 arg); +u32 GetFormChangeTargetSpecies_Internal(struct FormChangeContext ctx); bool32 DoesSpeciesHaveFormChangeMethod(u16 species, enum FormChanges method); u16 MonTryLearningNewMoveEvolution(struct Pokemon *mon, bool8 firstMove); void RemoveIVIndexFromList(u8 *ivs, u8 selectedIv); void TrySpecialOverworldEvo(void); bool32 SpeciesHasGenderDifferences(u16 species); -bool32 TryFormChange(u32 monId, enum BattleSide side, enum FormChanges method); +bool32 TryFormChange(struct Pokemon *mon, enum FormChanges method); +bool32 TryBoxMonFormChange(struct BoxPokemon *boxMon, enum FormChanges method); void TryToSetBattleFormChangeMoves(struct Pokemon *mon, enum FormChanges method); u32 GetMonFriendshipScore(struct Pokemon *pokemon); u32 GetMonAffectionHearts(struct Pokemon *pokemon); @@ -957,7 +978,7 @@ void HealPokemon(struct Pokemon *mon); void HealBoxPokemon(struct BoxPokemon *boxMon); void UpdateDaysPassedSinceFormChange(u16 days); void TrySetDayLimitToFormChange(struct Pokemon *mon); -enum Type CheckDynamicMoveType(struct Pokemon *mon, enum Move move, u32 battler, enum MonState state); +enum Type CheckDynamicMoveType(struct Pokemon *mon, enum Move move, enum BattlerId battler, enum MonState state); uq4_12_t GetDynamaxLevelHPMultiplier(u32 dynamaxLevel, bool32 inverseMultiplier); u32 GetRegionalFormByRegion(u32 species, u32 region); bool32 IsSpeciesForeignRegionalForm(u32 species, u32 currentRegion); @@ -969,6 +990,7 @@ void SavePlayerPartyMon(u32 index, struct Pokemon *mon); bool32 IsSpeciesOfType(u32 species, enum Type type); struct BoxPokemon *GetSelectedBoxMonFromPcOrParty(void); u32 GiveScriptedMonToPlayer(struct Pokemon *mon, u8 slot); +void ChangePokemonNicknameWithCallback(void (*callback)(void)); // pokefirered u16 GetFirstPartnerMove(u16 species); diff --git a/include/pokemon_icon.h b/include/pokemon_icon.h index 702ef6dab..459fb7dec 100644 --- a/include/pokemon_icon.h +++ b/include/pokemon_icon.h @@ -9,7 +9,9 @@ extern const u16 gMonIconPalettes[][16]; // extern const u8 gMonIconPaletteIndices[]; const u8 *GetMonIconPtr(u16 speciesId, u32 personality); +const u8 *GetMonIconPtrIsEgg(u16 species, u32 personality, bool32 isEgg); const u8 *GetMonIconTiles(u16 iconSpecies, bool32 extra); +const u8 *GetMonIconTilesIsEgg(u16 species, u32 personality, bool32 isEgg); const u16 *GetValidMonIconPalettePtr(u16 speciesId); void LoadMonIconPalettes(void); void FreeMonIconPalettes(void); @@ -19,6 +21,7 @@ void FreeAndDestroyMonIconSprite(struct Sprite *); u16 GetUnownLetterByPersonality(u32 personality); void SpriteCB_MonIcon(struct Sprite *); u8 CreateMonIcon(u16 species, SpriteCallback callback, s16 x, s16 y, u8 subpriority, u32 personality); +u8 CreateMonIconIsEgg(u16 species, void (*callback)(struct Sprite *), s16 x, s16 y, u8 subpriority, u32 personality, bool32 isEgg); u8 UpdateMonIconFrame(struct Sprite *sprite); void LoadMonIconPalette(u16 iconId); void LoadMonIconPalettePersonality(u16 species, u32 personality); diff --git a/include/pokemon_summary_screen.h b/include/pokemon_summary_screen.h index 68ee95dd8..08287f108 100644 --- a/include/pokemon_summary_screen.h +++ b/include/pokemon_summary_screen.h @@ -11,7 +11,7 @@ extern const struct CompressedSpriteSheet gSpriteSheet_CategoryIcons; extern const struct SpritePalette gSpritePal_CategoryIcons; extern const struct SpriteTemplate gSpriteTemplate_CategoryIcons; -void ShowSelectMovePokemonSummaryScreen(struct Pokemon *, u8, u8, MainCallback, u16); +void ShowSelectMovePokemonSummaryScreen(struct Pokemon *mons, u8 monIndex, void (*callback)(void), u16 newMove); u8 GetMoveSlotToReplace(void); void SummaryScreen_SetUnknownTaskId(u8 a0); void SummaryScreen_DestroyUnknownTask(void); diff --git a/include/random.h b/include/random.h index 0e44f805e..9d17e7733 100644 --- a/include/random.h +++ b/include/random.h @@ -169,6 +169,7 @@ enum RandomTag RNG_QUICK_DRAW, RNG_QUICK_CLAW, RNG_TRACE, + RNG_FOREWARN, RNG_FICKLE_BEAM, RNG_AI_ABILITY, RNG_AI_SCORE_TIE_DOUBLES_MOVE, @@ -240,6 +241,7 @@ enum RandomTag RNG_MAGNITUDE, RNG_FISHING_BITE, RNG_FISHING_GEN3_STICKY, + RNG_WILD_MON_TARGET, RNG_TAUNT, }; diff --git a/include/strings.h b/include/strings.h index 83a9dd28e..f5d5bbe61 100644 --- a/include/strings.h +++ b/include/strings.h @@ -2400,7 +2400,6 @@ extern const u8 gText_BattleArenaGutsSymbol[]; extern const u8 gText_BattleFactoryKnowledgeSymbol[]; extern const u8 gText_BattlePikeLuckSymbol[]; extern const u8 gText_BattlePyramidBraveSymbol[]; -extern const u8 gText_EmptyString7[]; extern const u8 gText_BattleTower3[]; extern const u8 gText_BattleDome2[]; extern const u8 gText_BattlePalace2[]; diff --git a/include/test/battle.h b/include/test/battle.h index d925185b1..711e7ab92 100644 --- a/include/test/battle.h +++ b/include/test/battle.h @@ -621,7 +621,7 @@ enum struct QueuedAbilityEvent { - u8 battlerId; + enum BattlerId battlerId; enum Ability ability; }; @@ -638,14 +638,14 @@ enum { EXP_EVENT_NEW_EXP, EXP_EVENT_DELTA_EXP }; struct QueuedHPEvent { - u32 battlerId:3; + enum BattlerId battlerId:3; u32 type:1; u32 address:28; }; struct QueuedSubHitEvent { - u32 battlerId:3; + enum BattlerId battlerId:3; u32 checkBreak:1; u32 breakSub:1; u32 address:27; @@ -653,7 +653,7 @@ struct QueuedSubHitEvent struct QueuedExpEvent { - u32 battlerId:3; + enum BattlerId battlerId:3; u32 type:1; u32 address:28; }; @@ -665,7 +665,7 @@ struct QueuedMessageEvent struct QueuedStatusEvent { - u32 battlerId:3; + enum BattlerId battlerId:3; u32 mask:29; }; @@ -1064,7 +1064,7 @@ void ClosePokemon(u32 sourceLine); void RNGSeed_(u32 sourceLine, rng_value_t seed); void AIFlags_(u32 sourceLine, u64 flags); -void BattlerAIFlags_(u32 sourceLine, u32 battler, u64 flags); +void BattlerAIFlags_(u32 sourceLine, enum BattlerId battler, u64 flags); void AILogScores(u32 sourceLine); void Gender_(u32 sourceLine, u32 gender); void Nature_(u32 sourceLine, u32 nature); @@ -1098,18 +1098,14 @@ void Environment_(u32 sourceLine, u32 environment); static inline bool8 IsMultibattleTest(void) { - if (TESTING) + #if TESTING { if (((gBattleTypeFlags & BATTLE_MULTI_TEST) == BATTLE_MULTI_TEST) - || ((gBattleTypeFlags & BATTLE_TWO_VS_ONE_TEST) == BATTLE_TWO_VS_ONE_TEST)) + || ((gBattleTypeFlags & BATTLE_TWO_VS_ONE_TEST) == BATTLE_TWO_VS_ONE_TEST)) return TRUE; - else - return FALSE; - } - else - { - return FALSE; } + #endif + return FALSE; } // Created for easy use of EXPECT_MOVES, so the user can provide 1, 2, 3 or 4 moves for AI which can pass the test. diff --git a/include/test_runner.h b/include/test_runner.h index 539086b87..13e75e2d7 100644 --- a/include/test_runner.h +++ b/include/test_runner.h @@ -13,23 +13,23 @@ extern const bool8 gTestRunnerSkipIsFail; enum Gimmick; -void TestRunner_Battle_RecordAbilityPopUp(u32 battlerId, enum Ability ability); +void TestRunner_Battle_RecordAbilityPopUp(enum BattlerId battlerId, enum Ability ability); void TestRunner_Battle_RecordAnimation(u32 animType, u32 animId); -void TestRunner_Battle_RecordHP(u32 battlerId, u32 oldHP, u32 newHP); -void TestRunner_Battle_RecordSubHit(u32 battlerId, u32 damage, bool32 broke); -void TestRunner_Battle_RecordExp(u32 battlerId, u32 oldExp, u32 newExp); +void TestRunner_Battle_RecordHP(enum BattlerId battlerId, u32 oldHP, u32 newHP); +void TestRunner_Battle_RecordSubHit(enum BattlerId battlerId, u32 damage, bool32 broke); +void TestRunner_Battle_RecordExp(enum BattlerId battlerId, u32 oldExp, u32 newExp); void TestRunner_Battle_RecordMessage(const u8 *message); -void TestRunner_Battle_RecordStatus1(u32 battlerId, u32 status1); +void TestRunner_Battle_RecordStatus1(enum BattlerId battlerId, u32 status1); void TestRunner_Battle_RecordCatchChance(u32 catchChance); void TestRunner_Battle_AfterLastTurn(void); -void TestRunner_Battle_CheckChosenMove(u32 battlerId, enum Move moveId, u32 target, enum Gimmick gimmick); -void TestRunner_Battle_CheckSwitch(u32 battlerId, u32 partyIndex); -void TestRunner_Battle_CheckAiMoveScores(u32 battlerId); -void TestRunner_Battle_AISetScore(const char *file, u32 line, u32 battlerId, u32 moveIndex, s32 score); -void TestRunner_Battle_AIAdjustScore(const char *file, u32 line, u32 battlerId, u32 moveIndex, s32 score); +void TestRunner_Battle_CheckChosenMove(enum BattlerId battlerId, enum Move moveId, u32 target, enum Gimmick gimmick); +void TestRunner_Battle_CheckSwitch(enum BattlerId battlerId, u32 partyIndex); +void TestRunner_Battle_CheckAiMoveScores(enum BattlerId battlerId); +void TestRunner_Battle_AISetScore(const char *file, u32 line, enum BattlerId battlerId, u32 moveIndex, s32 score); +void TestRunner_Battle_AIAdjustScore(const char *file, u32 line, enum BattlerId battlerId, u32 moveIndex, s32 score); void TestRunner_CheckMemory(void); -void TestRunner_Battle_CheckBattleRecordActionType(u32 battlerId, u32 recordIndex, u32 actionType); +void TestRunner_Battle_CheckBattleRecordActionType(enum BattlerId battlerId, u32 recordIndex, u32 actionType); u32 TestRunner_Battle_GetForcedAbility(enum BattleTrainer trainer, u32 partyIndex); u32 TestRunner_Battle_GetChosenGimmick(enum BattleTrainer trainer, u32 partyIndex); diff --git a/include/trainer_slide.h b/include/trainer_slide.h index 7ff0bca66..3a00e1155 100644 --- a/include/trainer_slide.h +++ b/include/trainer_slide.h @@ -11,16 +11,16 @@ struct MessageStatus }; void SetTrainerSlideMessage(enum DifficultyLevel difficulty, u32 trainerId, u32 slideId); -enum TrainerSlideTargets ShouldDoTrainerSlide(u32 battler, enum TrainerSlideType slideId); -void TryInitializeFirstSTABMoveTrainerSlide(u32 battlerDef, u32 battlerAtk, enum Type moveType); +enum TrainerSlideTargets ShouldDoTrainerSlide(enum BattlerId battler, enum TrainerSlideType slideId); +void TryInitializeFirstSTABMoveTrainerSlide(enum BattlerId battlerDef, enum BattlerId battlerAtk, enum Type moveType); void TryInitializeTrainerSlidePlayerLandsFirstCriticalHit(u32 target); void TryInitializeTrainerSlideEnemyLandsFirstCriticalHit(u32 target); void TryInitializeTrainerSlidePlayerLandsFirstSuperEffectiveHit(u32 target); void TryInitializeTrainerSlideEnemyMonUnaffected(u32 target); -bool32 IsTrainerSlideInitialized(u32 battler, enum TrainerSlideType slideId); -bool32 IsTrainerSlidePlayed(u32 battler, enum TrainerSlideType slideId); -void InitalizeTrainerSlide(u32 battler, enum TrainerSlideType slideId); -void MarkTrainerSlideAsPlayed(u32 battler, enum TrainerSlideType slideId); -void MarkInitializedTrainerSlidesAsPlayed(u32 battler, enum TrainerSlideType slideId); +bool32 IsTrainerSlideInitialized(enum BattlerId battler, enum TrainerSlideType slideId); +bool32 IsTrainerSlidePlayed(enum BattlerId battler, enum TrainerSlideType slideId); +void InitalizeTrainerSlide(enum BattlerId battler, enum TrainerSlideType slideId); +void MarkTrainerSlideAsPlayed(enum BattlerId battler, enum TrainerSlideType slideId); +void MarkInitializedTrainerSlidesAsPlayed(enum BattlerId battler, enum TrainerSlideType slideId); #endif // GUARD_TRAINER_SLIDE_H diff --git a/include/type_icons.h b/include/type_icons.h index 1d6a312f0..2a69def76 100644 --- a/include/type_icons.h +++ b/include/type_icons.h @@ -1,7 +1,7 @@ #ifndef GUARD_TYPE_ICONS_H #define GUARD_TYPE_ICONS_H -void LoadTypeIcons(u32); +void LoadTypeIcons(enum BattlerId battler); #define TYPE_ICON_TAG 0x2720 #define TYPE_ICON_TAG_2 0x2721 diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 502694d03..36e604ea5 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -38,14 +38,15 @@ #define AI_ACTION_WATCH (1 << 2) #define AI_ACTION_DO_NOT_ATTACK (1 << 3) -static u32 ChooseMoveOrAction(u32 battler); -static u32 ChooseMoveOrAction_Singles(u32 battler); -static u32 ChooseMoveOrAction_Doubles(u32 battler); -static inline void BattleAI_DoAIProcessing(struct AiThinkingStruct *aiThink, u32 battlerAtk, u32 battlerDef); -static inline void BattleAI_DoAIProcessing_PredictedSwitchin(struct AiThinkingStruct *aiThink, struct AiLogicData *aiData, u32 battlerAtk, u32 battlerDef); +static u32 ChooseMoveOrAction(enum BattlerId battler); +static u32 ChooseMoveOrAction_Singles(enum BattlerId battler); +static u32 ChooseMoveOrAction_Doubles(enum BattlerId battler); +static inline void BattleAI_DoAIProcessing(struct AiThinkingStruct *aiThink, enum BattlerId battlerAtk, enum BattlerId battlerDef); +static inline void BattleAI_DoAIProcessing_PredictedSwitchin(struct AiThinkingStruct *aiThink, struct AiLogicData *aiData, enum BattlerId battlerAtk, enum BattlerId battlerDef); static bool32 IsPinchBerryItemEffect(enum HoldEffect holdEffect); -static bool32 DoesAbilityBenefitFromSunOrRain(u32 battler, enum Ability ability, u32 weather); -static void AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef); +static bool32 DoesAbilityBenefitFromSunOrRain(enum BattlerId battler, enum Ability ability, u32 weather); +static void AI_CompareDamagingMoves(enum BattlerId battlerAtk, enum BattlerId battlerDef); +static u32 GetWindAbilityScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, struct AiLogicData *aiData); // ewram EWRAM_DATA const u8 *gAIScriptPtr = NULL; // Still used in contests @@ -53,25 +54,25 @@ EWRAM_DATA AiScoreFunc sDynamicAiFunc = NULL; EWRAM_DATA AiSwitchFunc gDynamicAiSwitchFunc = NULL; // const rom data -static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score); -static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score); -static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score); -static s32 AI_ForceSetupFirstTurn(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score); -static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score); -static s32 AI_TryTo2HKO(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score); -static s32 AI_AttacksPartner(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score); -static s32 AI_PreferBatonPass(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score); -static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score); -static s32 AI_Roaming(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score); -static s32 AI_Safari(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score); -static s32 AI_FirstBattle(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score); -static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score); -static s32 AI_PowerfulStatus(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score); -static s32 AI_DynamicFunc(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score); -static s32 AI_PredictSwitch(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score); -static s32 AI_CheckPpStall(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score); +static s32 AI_CheckBadMove(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score); +static s32 AI_TryToFaint(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score); +static s32 AI_CheckViability(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score); +static s32 AI_ForceSetupFirstTurn(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score); +static s32 AI_Risky(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score); +static s32 AI_TryTo2HKO(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score); +static s32 AI_AttacksPartner(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score); +static s32 AI_PreferBatonPass(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score); +static s32 AI_HPAware(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score); +static s32 AI_Roaming(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score); +static s32 AI_Safari(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score); +static s32 AI_FirstBattle(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score); +static s32 AI_DoubleBattle(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score); +static s32 AI_PowerfulStatus(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score); +static s32 AI_DynamicFunc(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score); +static s32 AI_PredictSwitch(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score); +static s32 AI_CheckPpStall(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score); -static s32 (*const sBattleAiFuncTable[])(u32, u32, enum Move, s32) = +static s32 (*const sBattleAiFuncTable[])(enum BattlerId, enum BattlerId, enum Move, s32) = { [0] = AI_CheckBadMove, // AI_FLAG_CHECK_BAD_MOVE [1] = AI_TryToFaint, // AI_FLAG_TRY_TO_FAINT @@ -143,7 +144,7 @@ static s32 (*const sBattleAiFuncTable[])(u32, u32, enum Move, s32) = void BattleAI_SetupItems(void) { u8 *data = (u8 *)gBattleHistory; - const u16 *items = GetTrainerItemsFromId(TRAINER_BATTLE_PARAM.opponentA); + const enum Item *items = GetTrainerItemsFromId(TRAINER_BATTLE_PARAM.opponentA); for (u32 i = 0; i < sizeof(struct BattleHistory); i++) data[i] = 0; @@ -189,7 +190,7 @@ static u64 GetWildAiFlags(void) return flags; } -static u64 GetAiFlags(u16 trainerId, u32 battler) +static u64 GetAiFlags(u16 trainerId, enum BattlerId battler) { u64 flags = 0; @@ -207,10 +208,8 @@ static u64 GetAiFlags(u16 trainerId, u32 battler) flags = AI_FLAG_SAFARI; else if (gBattleTypeFlags & BATTLE_TYPE_ROAMER) flags = AI_FLAG_ROAMING; - else if (gBattleTypeFlags & BATTLE_TYPE_WILD_SCRIPTED) - flags = AI_FLAG_CHECK_BAD_MOVE; - // else if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) - // flags = AI_FLAG_FIRST_BATTLE; + else if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) + flags = AI_FLAG_FIRST_BATTLE; else if (gBattleTypeFlags & BATTLE_TYPE_FACTORY) flags = GetAiScriptsInBattleFactory(); else if (gBattleTypeFlags & (BATTLE_TYPE_FRONTIER | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_TRAINER_HILL | BATTLE_TYPE_SECRET_BASE)) @@ -241,22 +240,22 @@ static u64 GetAiFlags(u16 trainerId, u32 battler) void BattleAI_SetupFlags(void) { if (IsAiVsAiBattle()) - gAiThinkingStruct->aiFlags[B_POSITION_PLAYER_LEFT] = GetAiFlags(gPartnerTrainerId, B_POSITION_PLAYER_LEFT); + gAiThinkingStruct->aiFlags[B_BATTLER_0] = GetAiFlags(gPartnerTrainerId, B_BATTLER_0); else - gAiThinkingStruct->aiFlags[B_POSITION_PLAYER_LEFT] = 0; // player has no AI + gAiThinkingStruct->aiFlags[B_BATTLER_0] = 0; // player has no AI if (DEBUG_OVERWORLD_MENU && gIsDebugBattle) { - gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_LEFT] = gDebugAIFlags; - gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_RIGHT] = gDebugAIFlags; + gAiThinkingStruct->aiFlags[B_BATTLER_1] = gDebugAIFlags; + gAiThinkingStruct->aiFlags[B_BATTLER_3] = gDebugAIFlags; return; } if (IsWildMonSmart() && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_TRAINER))) { // smart wild AI - gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_LEFT] = GetAiFlags(0xFFFF, B_POSITION_OPPONENT_LEFT); - gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_RIGHT] = GetAiFlags(0xFFFF, B_POSITION_OPPONENT_RIGHT); + gAiThinkingStruct->aiFlags[B_BATTLER_1] = GetAiFlags(0xFFFF, B_BATTLER_1); + gAiThinkingStruct->aiFlags[B_BATTLER_3] = GetAiFlags(0xFFFF, B_BATTLER_3); // The check is here because wild natural enemies are not symmetrical. if (B_WILD_NATURAL_ENEMIES && IsDoubleBattle()) @@ -264,38 +263,38 @@ void BattleAI_SetupFlags(void) u32 speciesLeft = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); u32 speciesRight = GetMonData(&gEnemyParty[1], MON_DATA_SPECIES); if (IsNaturalEnemy(speciesLeft, speciesRight)) - gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_LEFT] |= AI_FLAG_ATTACKS_PARTNER; + gAiThinkingStruct->aiFlags[B_BATTLER_1] |= AI_FLAG_ATTACKS_PARTNER; if (IsNaturalEnemy(speciesRight, speciesLeft)) - gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_RIGHT] |= AI_FLAG_ATTACKS_PARTNER; + gAiThinkingStruct->aiFlags[B_BATTLER_3] |= AI_FLAG_ATTACKS_PARTNER; } } else { - gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_LEFT] = GetAiFlags(TRAINER_BATTLE_PARAM.opponentA, B_POSITION_OPPONENT_LEFT); + gAiThinkingStruct->aiFlags[B_BATTLER_1] = GetAiFlags(TRAINER_BATTLE_PARAM.opponentA, B_BATTLER_1); if ((TRAINER_BATTLE_PARAM.opponentB != 0) && (TRAINER_BATTLE_PARAM.opponentB != 0xFFFF)) - gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_RIGHT] = GetAiFlags(TRAINER_BATTLE_PARAM.opponentB, B_POSITION_OPPONENT_RIGHT); + gAiThinkingStruct->aiFlags[B_BATTLER_3] = GetAiFlags(TRAINER_BATTLE_PARAM.opponentB, B_BATTLER_3); else - gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_RIGHT] = gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_LEFT]; + gAiThinkingStruct->aiFlags[B_BATTLER_3] = gAiThinkingStruct->aiFlags[B_BATTLER_1]; } if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) { - gAiThinkingStruct->aiFlags[B_POSITION_PLAYER_RIGHT] = GetAiFlags(gPartnerTrainerId, B_POSITION_PLAYER_RIGHT); + gAiThinkingStruct->aiFlags[B_BATTLER_2] = GetAiFlags(gPartnerTrainerId, B_BATTLER_1); } else if (IsDoubleBattle() && IsAiVsAiBattle()) { - gAiThinkingStruct->aiFlags[B_POSITION_PLAYER_RIGHT] = gAiThinkingStruct->aiFlags[B_POSITION_PLAYER_LEFT]; + gAiThinkingStruct->aiFlags[B_BATTLER_2] = gAiThinkingStruct->aiFlags[B_BATTLER_0]; } else // Assign ai flags for player for prediction { - u64 aiFlags = GetAiFlags(TRAINER_BATTLE_PARAM.opponentA, B_POSITION_OPPONENT_LEFT) - | GetAiFlags(TRAINER_BATTLE_PARAM.opponentB, B_POSITION_OPPONENT_RIGHT); - gAiThinkingStruct->aiFlags[B_POSITION_PLAYER_RIGHT] = aiFlags; - gAiThinkingStruct->aiFlags[B_POSITION_PLAYER_LEFT] = aiFlags; + u64 aiFlags = GetAiFlags(TRAINER_BATTLE_PARAM.opponentA, B_BATTLER_1) + | GetAiFlags(TRAINER_BATTLE_PARAM.opponentB, B_BATTLER_3); + gAiThinkingStruct->aiFlags[B_BATTLER_2] = aiFlags; + gAiThinkingStruct->aiFlags[B_BATTLER_0] = aiFlags; } } -void BattleAI_SetupAIData(u8 defaultScoreMoves, u32 battler) +void BattleAI_SetupAIData(u8 defaultScoreMoves, enum BattlerId battler) { u32 moveLimitations; u64 flags[MAX_BATTLERS_COUNT]; @@ -341,7 +340,7 @@ bool32 BattlerChooseNonMoveAction(void) return FALSE; } -void SetupAIPredictionData(u32 battler, enum SwitchType switchType) +void SetupAIPredictionData(enum BattlerId battler, enum SwitchType switchType) { s32 opposingBattler = GetOppositeBattler(battler); gAiLogicData->aiPredictionInProgress = TRUE; @@ -361,7 +360,7 @@ void SetupAIPredictionData(u32 battler, enum SwitchType switchType) gAiLogicData->aiPredictionInProgress = FALSE; } -void ComputeBattlerDecisions(u32 battler) +void ComputeBattlerDecisions(enum BattlerId battler) { bool32 isAiBattler = (gBattleTypeFlags & BATTLE_TYPE_HAS_AI || IsWildMonSmart()) && (BattlerHasAi(battler) && !(gBattleTypeFlags & BATTLE_TYPE_PALACE)); if (isAiBattler || CanAiPredictMove(battler)) @@ -394,7 +393,7 @@ void ComputeBattlerDecisions(u32 battler) } } -void ReconsiderGimmick(u32 battlerAtk, u32 battlerDef, enum Move move) +void ReconsiderGimmick(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move) { // After choosing a move for battlerAtk assuming that a gimmick will be used, reconsider whether the gimmick is necessary. @@ -405,23 +404,23 @@ void ReconsiderGimmick(u32 battlerAtk, u32 battlerDef, enum Move move) SetAIUsingGimmick(battlerAtk, NO_GIMMICK); } -static u32 ChooseMoveOrAction(u32 battler) +static u32 ChooseMoveOrAction(enum BattlerId battler) { if (IsDoubleBattle()) return ChooseMoveOrAction_Doubles(battler); return ChooseMoveOrAction_Singles(battler); } -static void SetupRandomRollsForAIMoveSelection(u32 battler) +static void SetupRandomRollsForAIMoveSelection(enum BattlerId battler) { gAiLogicData->shouldConsiderExplosion = RandomPercentage(RNG_AI_CONSIDER_EXPLOSION, GetAIExplosionChanceFromHP(gAiLogicData->hpPercents[battler])); gAiLogicData->shouldConsiderFinalGambit = RandomPercentage(RNG_AI_FINAL_GAMBIT, FINAL_GAMBIT_CHANCE); } -void AI_TrySwitchOrUseItem(u32 battler) +void AI_TrySwitchOrUseItem(enum BattlerId battler) { struct Pokemon *party; - u32 battlerIn1, battlerIn2; + enum BattlerId battlerIn1, battlerIn2; s32 firstId; s32 lastId; // + 1 party = GetBattlerParty(battler); @@ -470,7 +469,7 @@ void AI_TrySwitchOrUseItem(u32 battler) BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_USE_MOVE, BATTLE_OPPOSITE(battler) << 8); } -u32 BattleAI_ChooseMoveIndex(u32 battler) +u32 BattleAI_ChooseMoveIndex(enum BattlerId battler) { u32 chosenMoveIndex; @@ -497,7 +496,7 @@ u32 BattleAI_ChooseMoveIndex(u32 battler) static void CopyBattlerDataToAIParty(u32 bPosition, enum BattleSide side) { - u32 battler = GetBattlerAtPosition(bPosition); + enum BattlerId battler = GetBattlerAtPosition(bPosition); struct AiPartyMon *aiMon = &gAiPartyData->mons[side][gBattlerPartyIndexes[battler]]; struct BattlePokemon *bMon = &gBattleMons[battler]; @@ -555,7 +554,7 @@ void Ai_InitPartyStruct(void) } } -void Ai_UpdateSwitchInData(u32 battler) +void Ai_UpdateSwitchInData(enum BattlerId battler) { enum BattleSide side = GetBattlerSide(battler); struct AiPartyMon *aiMon = &gAiPartyData->mons[side][gBattlerPartyIndexes[battler]]; @@ -584,7 +583,7 @@ void Ai_UpdateSwitchInData(u32 battler) } } -void Ai_UpdateFaintData(u32 battler) +void Ai_UpdateFaintData(enum BattlerId battler) { struct AiPartyMon *aiMon = &gAiPartyData->mons[GetBattlerSide(battler)][gBattlerPartyIndexes[battler]]; ClearBattlerMoveHistory(battler); @@ -593,7 +592,7 @@ void Ai_UpdateFaintData(u32 battler) aiMon->isFainted = TRUE; } -void RecordMovesBasedOnStab(u32 battler) +void RecordMovesBasedOnStab(enum BattlerId battler) { for (u32 moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) { @@ -603,7 +602,7 @@ void RecordMovesBasedOnStab(u32 battler) } } -void RecordStatusMoves(u32 battler) +void RecordStatusMoves(enum BattlerId battler) { for (u32 moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) { @@ -613,10 +612,10 @@ void RecordStatusMoves(u32 battler) } } -void SetBattlerAiData(u32 battler, struct AiLogicData *aiData) +void SetBattlerAiData(enum BattlerId battler, struct AiLogicData *aiData) { enum Ability ability; - u32 holdEffect; + enum HoldEffect holdEffect; ability = aiData->abilities[battler] = AI_DecideKnownAbilityForTurn(battler); aiData->items[battler] = gBattleMons[battler].item; @@ -635,7 +634,7 @@ void SetBattlerAiData(u32 battler, struct AiLogicData *aiData) } #define BYPASSES_ACCURACY_CALC 101 // 101 indicates for ai that the move will always hit -static u32 Ai_SetMoveAccuracy(struct AiLogicData *aiData, u32 battlerAtk, u32 battlerDef, enum Move move) +static u32 Ai_SetMoveAccuracy(struct AiLogicData *aiData, enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move) { u32 accuracy; enum Ability abilityAtk = aiData->abilities[battlerAtk]; @@ -656,7 +655,7 @@ static u32 Ai_SetMoveAccuracy(struct AiLogicData *aiData, u32 battlerAtk, u32 ba } #undef BYPASSES_ACCURACY_CALC -void CalcBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u32 battlerDef, u32 weather, u32 fieldStatus) +void CalcBattlerAiMovesData(struct AiLogicData *aiData, enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 weather, u32 fieldStatus) { enum Move move; enum Move *moves = GetMovesArray(battlerAtk); @@ -680,13 +679,13 @@ void CalcBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u32 batt } } -static void SetBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u32 battlersCount, u32 weather) +static void SetBattlerAiMovesData(struct AiLogicData *aiData, enum BattlerId battlerAtk, u32 battlersCount, u32 weather) { SaveBattlerData(battlerAtk); SetBattlerData(battlerAtk); // Simulate dmg for both ai controlled mons and for player controlled mons. - for (u32 battlerDef = 0; battlerDef < battlersCount; battlerDef++) + for (enum BattlerId battlerDef = 0; battlerDef < battlersCount; battlerDef++) { if (battlerAtk == battlerDef || !IsBattlerAlive(battlerDef)) continue; @@ -720,7 +719,7 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData) gAiLogicData->aiCalcInProgress = TRUE; if (DEBUG_AI_DELAY_TIMER) CycleCountStart(); - for (u32 battlerAtk = 0; battlerAtk < battlersCount; battlerAtk++) + for (enum BattlerId battlerAtk = 0; battlerAtk < battlersCount; battlerAtk++) { if (!IsBattlerAlive(battlerAtk)) continue; @@ -728,7 +727,7 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData) SetBattlerAiData(battlerAtk, aiData); } - for (u32 battlerAtk = 0; battlerAtk < battlersCount; battlerAtk++) + for (enum BattlerId battlerAtk = 0; battlerAtk < battlersCount; battlerAtk++) { if (!IsBattlerAlive(battlerAtk)) continue; @@ -736,7 +735,7 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData) SetBattlerAiMovesData(aiData, battlerAtk, battlersCount, weather); } - for (u32 battlerAtk = 0; battlerAtk < battlersCount; battlerAtk++) + for (enum BattlerId battlerAtk = 0; battlerAtk < battlersCount; battlerAtk++) { // Prediction limited to player side but can be expanded to read partners move in the future if (!IsOnPlayerSide(battlerAtk) || !CanAiPredictMove(battlerAtk)) @@ -755,15 +754,15 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData) gAiLogicData->aiCalcInProgress = FALSE; } -u32 GetPartyMonAbility(struct Pokemon *mon) +enum Ability GetPartyMonAbility(struct Pokemon *mon) { - // Doesn't have any special handling yet + // Doesn't have any special handling yet u32 species = GetMonData(mon, MON_DATA_SPECIES); enum Ability ability = GetSpeciesAbility(species, GetMonData(mon, MON_DATA_ABILITY_NUM)); return ability; } -static u32 PpStallReduction(enum Move move, u32 battlerAtk) +static u32 PpStallReduction(enum Move move, enum BattlerId battlerAtk) { if (move == MOVE_NONE) return 0; @@ -802,13 +801,13 @@ static u32 PpStallReduction(enum Move move, u32 battlerAtk) return returnValue; } -static u32 ChooseMoveOrAction_Singles(u32 battler) +static u32 ChooseMoveOrAction_Singles(enum BattlerId battler) { u8 currentMoveArray[MAX_MON_MOVES]; u8 consideredMoveArray[MAX_MON_MOVES]; u32 numOfBestMoves; u64 flags = gAiThinkingStruct->aiFlags[battler]; - u32 opposingBattler = GetOppositeBattler(battler); + enum BattlerId opposingBattler = GetOppositeBattler(battler); gAiThinkingStruct->aiLogicId = 0; gAiThinkingStruct->movesetIndex = 0; @@ -864,7 +863,7 @@ static u32 ChooseMoveOrAction_Singles(u32 battler) return consideredMoveArray[RandomUniform(RNG_AI_SCORE_TIE_SINGLES, 0, numOfBestMoves - 1)]; } -static u32 ChooseMoveOrAction_Doubles(u32 battler) +static u32 ChooseMoveOrAction_Doubles(enum BattlerId battler) { u64 flags; s32 bestMovePointsForTarget[MAX_BATTLERS_COUNT]; @@ -876,7 +875,7 @@ static u32 ChooseMoveOrAction_Doubles(u32 battler) u32 mostViableMovesNo; s32 mostMovePoints; - for (u32 battlerIndex = 0; battlerIndex < MAX_BATTLERS_COUNT; battlerIndex++) + for (enum BattlerId battlerIndex = 0; battlerIndex < MAX_BATTLERS_COUNT; battlerIndex++) { if (battlerIndex == battler || gBattleMons[battlerIndex].hp == 0) { @@ -961,7 +960,7 @@ static u32 ChooseMoveOrAction_Doubles(u32 battler) mostViableTargetsArray[0] = 0; mostViableTargetsNo = 1; - for (u32 battlerIndex = 1; battlerIndex < MAX_BATTLERS_COUNT; battlerIndex++) + for (enum BattlerId battlerIndex = 1; battlerIndex < MAX_BATTLERS_COUNT; battlerIndex++) { if (mostMovePoints == bestMovePointsForTarget[battlerIndex]) { @@ -986,7 +985,7 @@ static u32 ChooseMoveOrAction_Doubles(u32 battler) return actionOrMoveIndex[gBattlerTarget]; } -static inline bool32 ShouldConsiderMoveForBattler(u32 battlerAi, u32 battlerDef, enum Move move) +static inline bool32 ShouldConsiderMoveForBattler(enum BattlerId battlerAi, enum BattlerId battlerDef, enum Move move) { if (battlerAi == BATTLE_PARTNER(battlerDef)) { @@ -997,7 +996,7 @@ static inline bool32 ShouldConsiderMoveForBattler(u32 battlerAi, u32 battlerDef, return TRUE; } -static inline void BattleAI_DoAIProcessing(struct AiThinkingStruct *aiThink, u32 battlerAtk, u32 battlerDef) +static inline void BattleAI_DoAIProcessing(struct AiThinkingStruct *aiThink, enum BattlerId battlerAtk, enum BattlerId battlerDef) { do { @@ -1032,7 +1031,7 @@ static inline void BattleAI_DoAIProcessing(struct AiThinkingStruct *aiThink, u32 aiThink->movesetIndex = 0; } -void BattleAI_DoAIProcessing_PredictedSwitchin(struct AiThinkingStruct *aiThink, struct AiLogicData *aiData, u32 battlerAtk, u32 battlerDef) +void BattleAI_DoAIProcessing_PredictedSwitchin(struct AiThinkingStruct *aiThink, struct AiLogicData *aiData, enum BattlerId battlerAtk, enum BattlerId battlerDef) { struct BattlePokemon switchoutCandidate = gBattleMons[battlerDef]; struct SimulatedDamage simulatedDamageSwitchout[MAX_MON_MOVES]; @@ -1145,14 +1144,14 @@ void BattleAI_DoAIProcessing_PredictedSwitchin(struct AiThinkingStruct *aiThink, // AI Score Functions // AI_FLAG_CHECK_BAD_MOVE - decreases move scores -static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) +static s32 AI_CheckBadMove(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score) { if (IsTargetingPartner(battlerAtk, battlerDef)) return score; // move data enum BattleMoveEffects moveEffect = GetMoveEffect(move); - u32 nonVolatileStatus = GetMoveNonVolatileStatus(move); + enum MoveEffect nonVolatileStatus = GetMoveNonVolatileStatus(move); enum Type moveType; enum MoveTarget moveTarget = AI_GetBattlerMoveTargetType(battlerAtk, move); struct AiLogicData *aiData = gAiLogicData; @@ -1170,18 +1169,28 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, enum Move move, s32 s SetTypeBeforeUsingMove(move, battlerAtk); moveType = GetBattleMoveType(move); + if (gBattleStruct->battlerState[battlerDef].commandingDondozo) + RETURN_SCORE_MINUS(20); + if (IsPowderMove(move) && !IsAffectedByPowderMove(battlerDef, aiData->abilities[battlerDef], aiData->holdEffects[battlerDef])) RETURN_SCORE_MINUS(10); if (!BreaksThroughSemiInvulnerablity(battlerAtk, battlerDef, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], move) - && moveEffect != EFFECT_SEMI_INVULNERABLE && AI_IsFaster(battlerAtk, battlerDef, move, predictedMoveSpeedCheck, CONSIDER_PRIORITY)) + && moveEffect != EFFECT_SEMI_INVULNERABLE && AI_IsFaster(battlerAtk, battlerDef, move, predictedMoveSpeedCheck, CONSIDER_PRIORITY) + && abilityAtk != ABILITY_NO_GUARD && abilityDef != ABILITY_NO_GUARD) RETURN_SCORE_MINUS(10); - if (IsTwoTurnNotSemiInvulnerableMove(battlerAtk, move) && CanTargetFaintAi(battlerDef, battlerAtk)) - RETURN_SCORE_MINUS(10); + if (CanTargetFaintAi(battlerDef, battlerAtk)) + { + if (IsTwoTurnNotSemiInvulnerableMove(battlerAtk, move)) + RETURN_SCORE_MINUS(10); - if (gBattleStruct->battlerState[battlerDef].commandingDondozo) - RETURN_SCORE_MINUS(20); + if (moveEffect == EFFECT_SEMI_INVULNERABLE && aiData->holdEffects[battlerAtk] != HOLD_EFFECT_POWER_HERB) + { + if (abilityAtk == ABILITY_NO_GUARD || abilityDef == ABILITY_NO_GUARD) + RETURN_SCORE_MINUS(10); + } + } // Don't setup into expected Focus Punch. if (GetMoveCategory(move) == DAMAGE_CATEGORY_STATUS @@ -1196,7 +1205,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, enum Move move, s32 s // Don't use anything but super effective thawing moves if target is frozen if any other attack available if (((GetMoveType(move) == TYPE_FIRE && GetMovePower(move) != 0) || CanBurnHitThaw(move)) && effectiveness < UQ_4_12(2.0) && (gBattleMons[battlerDef].status1 & STATUS1_ICY_ANY)) { - u32 aiMove; + enum Move aiMove; for (u32 moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) { aiMove = gBattleMons[battlerAtk].moves[moveIndex]; @@ -1797,10 +1806,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, enum Move move, s32 s || !(weather & (B_WEATHER_ICY_ANY))) ADJUST_SCORE(-10); break; - case EFFECT_SHEER_COLD: - if (GetConfig(CONFIG_SHEER_COLD_IMMUNITY) >= GEN_7 && IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE)) - RETURN_SCORE_MINUS(20); - // fallthrough case EFFECT_OHKO: if (!ShouldTryOHKO(battlerAtk, battlerDef, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], move)) ADJUST_SCORE(-10); @@ -2535,9 +2540,9 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, enum Move move, s32 s ADJUST_SCORE(-10); break; case EFFECT_NATURE_POWER: - predictedMove = GetNaturePowerMove(battlerAtk); + predictedMove = GetNaturePowerMove(); if (GetMoveEffect(predictedMove) != GetMoveEffect(move)) - return AI_CheckBadMove(battlerAtk, battlerDef, GetNaturePowerMove(battlerAtk), score); + return AI_CheckBadMove(battlerAtk, battlerDef, GetNaturePowerMove(), score); break; case EFFECT_TAUNT: if (gBattleMons[battlerDef].volatiles.tauntTimer > 0 @@ -2759,7 +2764,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, enum Move move, s32 s ADJUST_SCORE(-10); break; case EFFECT_FLING: - if (!CanFling(battlerAtk, battlerDef)) + if (!CanFling(battlerAtk)) { ADJUST_SCORE(-10); } @@ -3082,6 +3087,8 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, enum Move move, s32 s if (!ShouldBurn(battlerAtk, battlerDef, aiData->abilities[battlerDef])) ADJUST_SCORE(-5); break; + default: + break; } // Choice items @@ -3106,7 +3113,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, enum Move move, s32 s return score; } -static s32 AI_GetWhichBattlerFasterOrTies(u32 battlerAtk, u32 battlerDef, bool32 ignoreChosenMoves) +static s32 AI_GetWhichBattlerFasterOrTies(enum BattlerId battlerAtk, enum BattlerId battlerDef, bool32 ignoreChosenMoves) { struct BattleCalcValues calcValues = {0}; calcValues.battlerAtk = battlerAtk; @@ -3119,7 +3126,7 @@ static s32 AI_GetWhichBattlerFasterOrTies(u32 battlerAtk, u32 battlerDef, bool32 return GetWhichBattlerFasterOrTies(&calcValues, ignoreChosenMoves); } -static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) +static s32 AI_TryToFaint(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score) { u32 movesetIndex = gAiThinkingStruct->movesetIndex; enum Move predictedMoveSpeedCheck = GetIncomingMoveSpeedCheck(battlerAtk, battlerDef, gAiLogicData); @@ -3153,17 +3160,17 @@ static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, enum Move move, s32 sco } // double battle logic -static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) +static s32 AI_DoubleBattle(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score) { // move data enum Type moveType = GetMoveType(move); enum BattleMoveEffects effect = GetMoveEffect(move); enum MoveTarget moveTarget = AI_GetBattlerMoveTargetType(battlerAtk, move); // ally data - u32 battlerAtkPartner = BATTLE_PARTNER(battlerAtk); + enum BattlerId battlerAtkPartner = BATTLE_PARTNER(battlerAtk); struct AiLogicData *aiData = gAiLogicData; enum Ability atkPartnerAbility = aiData->abilities[BATTLE_PARTNER(battlerAtk)]; - u32 atkPartnerHoldEffect = aiData->holdEffects[BATTLE_PARTNER(battlerAtk)]; + enum HoldEffect atkPartnerHoldEffect = aiData->holdEffects[BATTLE_PARTNER(battlerAtk)]; enum BattleMoveEffects partnerEffect = GetMoveEffect(aiData->partnerMove); bool32 partnerProtecting = IsAllyProtectingFromMove(battlerAtk, move, aiData->partnerMove) && !MoveIgnoresProtect(move); bool32 partnerHasBadAbility = (gAbilitiesInfo[atkPartnerAbility].aiRating < 0); @@ -3177,12 +3184,11 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, enum Move move, s32 s bool32 hasPartner = HasPartner(battlerAtk); u32 friendlyFireThreshold = GetFriendlyFireKOThreshold(battlerAtk); u32 noOfHitsToKOPartner = GetNoOfHitsToKOBattler(battlerAtk, battlerAtkPartner, gAiThinkingStruct->movesetIndex, AI_ATTACKING, CONSIDER_ENDURE); - bool32 wouldPartnerFaint = hasPartner && CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, gAiThinkingStruct->movesetIndex, AI_ATTACKING) - && !partnerProtecting; + bool32 wouldPartnerFaint = hasPartner && CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, gAiThinkingStruct->movesetIndex, AI_ATTACKING) && !partnerProtecting; bool32 isFriendlyFireOK = !wouldPartnerFaint && (noOfHitsToKOPartner == 0 || noOfHitsToKOPartner > friendlyFireThreshold); // check what effect partner is using - if (aiData->partnerMove != 0 && hasPartner) + if (aiData->partnerMove != MOVE_NONE && hasPartner) { // This catches weather, terrain, screens, etc if (AreMovesEquivalent(battlerAtk, battlerAtkPartner, move, aiData->partnerMove)) @@ -3912,7 +3918,7 @@ static bool32 IsPinchBerryItemEffect(enum HoldEffect holdEffect) } } -static bool32 DoesAbilityBenefitFromSunOrRain(u32 battler, enum Ability ability, u32 weather) +static bool32 DoesAbilityBenefitFromSunOrRain(enum BattlerId battler, enum Ability ability, u32 weather) { switch (ability) { @@ -3923,7 +3929,6 @@ static bool32 DoesAbilityBenefitFromSunOrRain(u32 battler, enum Ability ability, return (weather & B_WEATHER_RAIN); case ABILITY_HARVEST: if (GetItemPocket(gAiLogicData->items[battler]) != POCKET_BERRIES - && GetItemPocket(gBattleStruct->changedItems[battler]) != POCKET_BERRIES && GetItemPocket(GetBattlerPartyState(battler)->usedHeldItem) != POCKET_BERRIES) { return FALSE; @@ -3940,7 +3945,27 @@ static bool32 DoesAbilityBenefitFromSunOrRain(u32 battler, enum Ability ability, return FALSE; } -static enum MoveComparisonResult CompareMoveAccuracies(u32 battlerAtk, u32 battlerDef, u32 moveSlot1, u32 moveSlot2) +static u32 GetWindAbilityScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, struct AiLogicData *aiData) +{ + u32 score = 0; + + if (aiData->abilities[battlerAtk] == ABILITY_WIND_RIDER) + { + score = IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_ATK); + } + else if (aiData->abilities[battlerAtk] == ABILITY_WIND_POWER) + { + if (gBattleMons[battlerAtk].volatiles.chargeTimer == 0 + && HasDamagingMoveOfType(battlerAtk, TYPE_ELECTRIC)) + { + score = DECENT_EFFECT; + } + } + + return score; +} + +static enum MoveComparisonResult CompareMoveAccuracies(enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 moveSlot1, u32 moveSlot2) { u32 acc1 = gAiLogicData->moveAccuracy[battlerAtk][battlerDef][moveSlot1]; u32 acc2 = gAiLogicData->moveAccuracy[battlerAtk][battlerDef][moveSlot2]; @@ -3952,7 +3977,7 @@ static enum MoveComparisonResult CompareMoveAccuracies(u32 battlerAtk, u32 battl return MOVE_NEUTRAL_COMPARISON; } -static enum MoveComparisonResult CompareMoveSpeeds(u32 battlerAtk, u32 battlerDef, bool32 move1Faster, bool32 move2Faster) +static enum MoveComparisonResult CompareMoveSpeeds(enum BattlerId battlerAtk, enum BattlerId battlerDef, bool32 move1Faster, bool32 move2Faster) { if (move1Faster && !move2Faster) return MOVE_WON_COMPARISON; @@ -3961,7 +3986,7 @@ static enum MoveComparisonResult CompareMoveSpeeds(u32 battlerAtk, u32 battlerDe return MOVE_NEUTRAL_COMPARISON; } -static enum MoveComparisonResult CompareGuaranteeFaintTarget(u32 battlerAtk, u32 battlerDef, u16 moveSlot1, u16 moveSlot2, u16 *moves) +static enum MoveComparisonResult CompareGuaranteeFaintTarget(enum BattlerId battlerAtk, enum BattlerId battlerDef, u16 moveSlot1, u16 moveSlot2, u16 *moves) { s32 dmg1, dmg2; bool32 guarantee1, guarantee2; @@ -3982,7 +4007,7 @@ static enum MoveComparisonResult CompareGuaranteeFaintTarget(u32 battlerAtk, u32 return MOVE_NEUTRAL_COMPARISON; } -static enum MoveComparisonResult CompareResistBerryEffects(u32 battlerAtk, u32 battlerDef, u32 moveSlot1, u32 moveSlot2) +static enum MoveComparisonResult CompareResistBerryEffects(enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 moveSlot1, u32 moveSlot2) { // Check for resist berries in OHKOs if (gAiLogicData->holdEffects[battlerDef] == HOLD_EFFECT_RESIST_BERRY) @@ -3997,7 +4022,7 @@ static enum MoveComparisonResult CompareResistBerryEffects(u32 battlerAtk, u32 b return MOVE_NEUTRAL_COMPARISON; } -static enum MoveComparisonResult CompareMoveSelfSacrifice(u32 battlerAtk, u32 battlerDef, enum Move move1, enum Move move2) +static enum MoveComparisonResult CompareMoveSelfSacrifice(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move1, enum Move move2) { bool32 selfSacrifice1 = IsSelfSacrificeEffect(move1); bool32 selfSacrifice2 = IsSelfSacrificeEffect(move2); @@ -4009,7 +4034,7 @@ static enum MoveComparisonResult CompareMoveSelfSacrifice(u32 battlerAtk, u32 ba return MOVE_NEUTRAL_COMPARISON; } -static enum MoveComparisonResult CompareMoveTwoTurnEffect(u32 battlerAtk, enum Move move1, enum Move move2) +static enum MoveComparisonResult CompareMoveTwoTurnEffect(enum BattlerId battlerAtk, enum Move move1, enum Move move2) { bool32 twoTurn1 = IsTwoTurnNotSemiInvulnerableMove(battlerAtk, move1); bool32 twoTurn2 = IsTwoTurnNotSemiInvulnerableMove(battlerAtk, move2); @@ -4021,9 +4046,9 @@ static enum MoveComparisonResult CompareMoveTwoTurnEffect(u32 battlerAtk, enum M return MOVE_NEUTRAL_COMPARISON; } -static inline bool32 ShouldUseSpreadDamageMove(u32 battlerAtk, enum Move move, u32 moveIndex, u32 hitsToFaintOpposingBattler) +static inline bool32 ShouldUseSpreadDamageMove(enum BattlerId battlerAtk, enum Move move, u32 moveIndex, u32 hitsToFaintOpposingBattler) { - u32 partnerBattler = BATTLE_PARTNER(battlerAtk); + enum BattlerId partnerBattler = BATTLE_PARTNER(battlerAtk); u32 noOfHitsToFaintPartner = GetNoOfHitsToKOBattler(battlerAtk, partnerBattler, moveIndex, AI_ATTACKING, CONSIDER_ENDURE); u32 friendlyFireThreshold = GetFriendlyFireKOThreshold(battlerAtk); return (HasPartnerIgnoreFlags(battlerAtk) @@ -4033,7 +4058,7 @@ static inline bool32 ShouldUseSpreadDamageMove(u32 battlerAtk, enum Move move, u && noOfHitsToFaintPartner < (friendlyFireThreshold * 2)); } -static bool32 ShouldCompareMove(u32 battlerAtk, u32 battlerDef, u32 moveIndex, enum Move move) +static bool32 ShouldCompareMove(enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 moveIndex, enum Move move) { if (IsTargetingPartner(battlerAtk, battlerDef)) return FALSE; @@ -4046,7 +4071,7 @@ static bool32 ShouldCompareMove(u32 battlerAtk, u32 battlerDef, u32 moveIndex, e return TRUE; } -static void AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef) +static void AI_CompareDamagingMoves(enum BattlerId battlerAtk, enum BattlerId battlerDef) { u32 tempMoveScores[MAX_MON_MOVES]; u32 moveComparisonScores[MAX_MON_MOVES]; @@ -4220,7 +4245,7 @@ static void AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef) } } -static s32 AI_CalcHoldEffectMoveScore(u32 battlerAtk, u32 battlerDef, enum Move move, struct AiLogicData *aiData) +static s32 AI_CalcHoldEffectMoveScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, struct AiLogicData *aiData) { enum HoldEffect holdEffect = aiData->holdEffects[battlerAtk]; @@ -4249,7 +4274,7 @@ static s32 AI_CalcHoldEffectMoveScore(u32 battlerAtk, u32 battlerDef, enum Move return score; } -static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, enum Move move, struct AiLogicData *aiData) +static s32 AI_CalcMoveEffectScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, struct AiLogicData *aiData) { // move data enum BattleMoveEffects moveEffect = GetMoveEffect(move); @@ -4326,6 +4351,8 @@ static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, enum Move move case MOVE_EFFECT_BURN: IncreaseBurnScore(battlerAtk, battlerDef, move, &score); break; + default: + break; } // move effect checks switch (moveEffect) @@ -4630,7 +4657,6 @@ static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, enum Move move } break; case EFFECT_OHKO: - case EFFECT_SHEER_COLD: if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX) break; else if (gBattleMons[battlerAtk].volatiles.lockOn) @@ -4737,10 +4763,12 @@ static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, enum Move move case MOVE_EFFECT_PARALYSIS: encourage = TRUE; break; + default: + break; } if (gBattleMons[battlerDef].volatiles.encoreTimer == 0 - && (B_MENTAL_HERB < GEN_5 || aiData->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB) - && (encourage)) + && (B_MENTAL_HERB < GEN_5 || aiData->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB) + && (encourage)) ADJUST_SCORE(BEST_EFFECT); break; } @@ -4750,7 +4778,7 @@ static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, enum Move move ADJUST_SCORE(BEST_EFFECT); break; case EFFECT_LOCK_ON: - if (HasMoveWithEffect(battlerAtk, EFFECT_OHKO) || HasMoveWithEffect(battlerAtk, EFFECT_SHEER_COLD)) + if (HasMoveWithEffect(battlerAtk, EFFECT_OHKO)) ADJUST_SCORE(GOOD_EFFECT); else if (HasMoveWithLowAccuracy(battlerAtk, battlerDef, 85, TRUE)) ADJUST_SCORE(GOOD_EFFECT); @@ -4919,6 +4947,9 @@ static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, enum Move move case EFFECT_SEMI_INVULNERABLE: if (predictedMove != MOVE_NONE && isBattle1v1) { + if (aiData->abilities[battlerAtk] == ABILITY_NO_GUARD || aiData->abilities[battlerDef] == ABILITY_NO_GUARD) + break; + enum BattleMoveEffects predictedEffect = GetMoveEffect(predictedMove); if ((AI_IsFaster(battlerAtk, battlerDef, move, predictedMoveSpeedCheck, CONSIDER_PRIORITY)) && (IsExplosionMove(predictedMove) || predictedEffect == EFFECT_PROTECT)) @@ -5593,6 +5624,14 @@ static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, enum Move move if (CountUsablePartyMons(battlerAtk) != 0) ADJUST_SCORE(WEAK_EFFECT); + + if (!(gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_TAILWIND)) + { + u32 windAbilityScore = GetWindAbilityScore(battlerAtk, battlerDef, aiData); + + if (windAbilityScore > 0) + ADJUST_SCORE(windAbilityScore); + } } else { @@ -5606,15 +5645,26 @@ static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, enum Move move tailwindScore += 1; if (speed <= foe2Speed && (speed * 2) > foe2Speed) tailwindScore += 1; - if (partnerSpeed <= foe1Speed && (speed * 2) > foe1Speed) + if (partnerSpeed <= foe1Speed && (partnerSpeed * 2) > foe1Speed) tailwindScore += 1; - if (partnerSpeed <= foe1Speed && (speed * 2) > foe1Speed) + if (partnerSpeed <= foe2Speed && (partnerSpeed * 2) > foe2Speed) tailwindScore += 1; if (tailwindScore > 0) tailwindScore += 1; ADJUST_SCORE(tailwindScore); + + if (!(gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_TAILWIND)) + { + u32 windAbilityScore = GetWindAbilityScore(battlerAtk, battlerDef, aiData); + + if (IsBattlerAlive(BATTLE_PARTNER(battlerAtk))) + windAbilityScore += GetWindAbilityScore(BATTLE_PARTNER(battlerAtk), battlerDef, aiData); + + if (windAbilityScore > 0) + ADJUST_SCORE(windAbilityScore); + } } break; } @@ -5796,7 +5846,7 @@ static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, enum Move move return score; } -static s32 AI_CalcAdditionalEffectScore(u32 battlerAtk, u32 battlerDef, enum Move move, struct AiLogicData *aiData) +static s32 AI_CalcAdditionalEffectScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, struct AiLogicData *aiData) { // move data s32 score = 0; @@ -5936,7 +5986,7 @@ static s32 AI_CalcAdditionalEffectScore(u32 battlerAtk, u32 battlerDef, enum Mov case MOVE_EFFECT_SP_DEF_MINUS_1: case MOVE_EFFECT_EVS_MINUS_1: { - u32 statId = STAT_ATK + additionalEffect->moveEffect - MOVE_EFFECT_ATK_MINUS_1; + enum Stat statId = STAT_ATK + additionalEffect->moveEffect - MOVE_EFFECT_ATK_MINUS_1; if (CanLowerStat(battlerAtk, battlerDef, aiData, statId)) ADJUST_SCORE(IncreaseStatDownScore(battlerAtk, battlerDef, statId)); break; @@ -5947,7 +5997,7 @@ static s32 AI_CalcAdditionalEffectScore(u32 battlerAtk, u32 battlerDef, enum Mov case MOVE_EFFECT_SP_DEF_MINUS_2: case MOVE_EFFECT_EVS_MINUS_2: { - u32 statId = STAT_ATK + additionalEffect->moveEffect - MOVE_EFFECT_ATK_MINUS_2; + enum Stat statId = STAT_ATK + additionalEffect->moveEffect - MOVE_EFFECT_ATK_MINUS_2; if (CanLowerStat(battlerAtk, battlerDef, aiData, statId)) ADJUST_SCORE(IncreaseStatDownScore(battlerAtk, battlerDef, statId)); break; @@ -5970,7 +6020,7 @@ static s32 AI_CalcAdditionalEffectScore(u32 battlerAtk, u32 battlerDef, enum Mov case MOVE_EFFECT_LOWER_SP_ATK_SIDE: case MOVE_EFFECT_LOWER_SP_DEF_SIDE: { - u32 statId = STAT_ATK + additionalEffect->moveEffect - MOVE_EFFECT_LOWER_ATTACK_SIDE; + enum Stat statId = STAT_ATK + additionalEffect->moveEffect - MOVE_EFFECT_LOWER_ATTACK_SIDE; if (CanLowerStat(battlerAtk, battlerDef, aiData, statId)) ADJUST_SCORE(IncreaseStatDownScore(battlerAtk, battlerDef, statId)); break; @@ -6150,7 +6200,7 @@ static s32 AI_CalcAdditionalEffectScore(u32 battlerAtk, u32 battlerDef, enum Mov } // AI_FLAG_CHECK_VIABILITY - Chooses best possible move to hit player -static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) +static s32 AI_CheckViability(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score) { struct AiLogicData *aiData = gAiLogicData; @@ -6178,7 +6228,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, enum Move move, s32 } // Effects that are encouraged on the first turn of battle -static s32 AI_ForceSetupFirstTurn(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) +static s32 AI_ForceSetupFirstTurn(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score) { if (IsTargetingPartner(battlerAtk, battlerDef) || gBattleResults.battleTurnCounter != 0) @@ -6286,7 +6336,7 @@ static s32 AI_ForceSetupFirstTurn(u32 battlerAtk, u32 battlerDef, enum Move move } // Adds score bonus to 'riskier' move effects and high crit moves -static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) +static s32 AI_Risky(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score) { struct AiLogicData *aiData = gAiLogicData; @@ -6333,7 +6383,6 @@ static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) case EFFECT_FLATTER: case EFFECT_ATTRACT: case EFFECT_OHKO: - case EFFECT_SHEER_COLD: ADJUST_SCORE(AVERAGE_RISKY_EFFECT); break; case EFFECT_HIT: @@ -6363,7 +6412,7 @@ static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) } // Adds score bonus to OHKOs and 2HKOs -static s32 AI_TryTo2HKO(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) +static s32 AI_TryTo2HKO(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score) { if (IsTargetingPartner(battlerAtk, battlerDef)) return score; @@ -6377,7 +6426,7 @@ static s32 AI_TryTo2HKO(u32 battlerAtk, u32 battlerDef, enum Move move, s32 scor } // Adds score bonus to targeting "partner" -static s32 AI_AttacksPartner(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) +static s32 AI_AttacksPartner(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score) { if (battlerDef == BATTLE_PARTNER(battlerAtk) // natural enemies in wild battles try to kill each other @@ -6402,7 +6451,7 @@ static s32 AI_AttacksPartner(u32 battlerAtk, u32 battlerDef, enum Move move, s32 } // Prefers moves that are good for baton pass -static s32 AI_PreferBatonPass(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) +static s32 AI_PreferBatonPass(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score) { if (IsTargetingPartner(battlerAtk, battlerDef) || CountUsablePartyMons(battlerAtk) == 0 @@ -6454,7 +6503,7 @@ static s32 AI_PreferBatonPass(u32 battlerAtk, u32 battlerDef, enum Move move, s3 return score; } -static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) +static s32 AI_HPAware(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score) { enum BattleMoveEffects effect = GetMoveEffect(move); enum Type moveType = 0; @@ -6637,6 +6686,8 @@ static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) case MOVE_EFFECT_POISON: ADJUST_SCORE(-2); break; + default: + break; } } else @@ -6650,7 +6701,7 @@ static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) return score; } -static s32 AI_PowerfulStatus(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) +static s32 AI_PowerfulStatus(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score) { enum BattleMoveEffects moveEffect = GetMoveEffect(move); @@ -6762,7 +6813,7 @@ bool32 DoesSideHaveDamagingHazards(enum BattleSide side) return FALSE; } -static s32 AI_PredictSwitch(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) +static s32 AI_PredictSwitch(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score) { u32 unmodifiedScore = score; enum Ability ability = gBattleMons[battlerAtk].ability; @@ -6771,7 +6822,7 @@ static s32 AI_PredictSwitch(u32 battlerAtk, u32 battlerDef, enum Move move, s32 enum BattleMoveEffects moveEffect = GetMoveEffect(move); struct AiLogicData *aiData = gAiLogicData; uq4_12_t effectiveness = aiData->effectiveness[battlerAtk][battlerDef][gAiThinkingStruct->movesetIndex]; - u32 predictedMove = GetIncomingMove(battlerAtk, battlerDef, gAiLogicData); + enum Move predictedMove = GetIncomingMove(battlerAtk, battlerDef, gAiLogicData); enum Move predictedMoveSpeedCheck = GetIncomingMoveSpeedCheck(battlerAtk, battlerDef, gAiLogicData); // Switch benefit @@ -6916,7 +6967,7 @@ static s32 AI_PredictSwitch(u32 battlerAtk, u32 battlerDef, enum Move move, s32 return score; } -static s32 AI_CheckPpStall(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) +static s32 AI_CheckPpStall(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score) { if (!IsOnPlayerSide(battlerAtk)) score -= PpStallReduction(move, battlerAtk); @@ -6934,7 +6985,7 @@ static void AI_Watch(void) } // Roaming pokemon logic -static s32 AI_Roaming(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) +static s32 AI_Roaming(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score) { bool32 roamerCanFlee = FALSE; @@ -6953,7 +7004,7 @@ static s32 AI_Roaming(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) } // Safari pokemon logic -static s32 AI_Safari(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) +static s32 AI_Safari(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score) { u32 safariFleeRate = gBattleStruct->safariEscapeFactor * 5; // Safari flee rate, from 0-20. @@ -6966,7 +7017,7 @@ static s32 AI_Safari(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) } // First battle logic -static s32 AI_FirstBattle(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) +static s32 AI_FirstBattle(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score) { if (!IS_FRLG && gAiLogicData->hpPercents[battlerDef] <= 20) AI_Flee(); @@ -6974,12 +7025,11 @@ static s32 AI_FirstBattle(u32 battlerAtk, u32 battlerDef, enum Move move, s32 sc return score; } - // Dynamic AI Functions // For specific battle scenarios // Example - prefer attacking opposite foe in a tag battle -s32 AI_TagBattlePreferFoe(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) +s32 AI_TagBattlePreferFoe(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score) { if (!(gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)) { @@ -7000,7 +7050,7 @@ s32 AI_TagBattlePreferFoe(u32 battlerAtk, u32 battlerDef, enum Move move, s32 sc return score; } -static s32 AI_DynamicFunc(u32 battlerAtk, u32 battlerDef, enum Move move, s32 score) +static s32 AI_DynamicFunc(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 score) { if (sDynamicAiFunc != NULL) score = sDynamicAiFunc(battlerAtk, battlerDef, move, score); diff --git a/src/battle_ai_switch.c b/src/battle_ai_switch.c index 1202ee093..405fc298c 100644 --- a/src/battle_ai_switch.c +++ b/src/battle_ai_switch.c @@ -32,23 +32,23 @@ struct IncomingHealInfo u16 healEndOfTurn:1; u16 curesStatus:1; }; -static bool32 CanUseSuperEffectiveMoveAgainstOpponents(u32 battler); -static bool32 FindMonWithFlagsAndSuperEffective(u32 battler, u16 flags, u32 moduloPercent); -static u32 GetSwitchinHazardsDamage(u32 battler); -static bool32 AI_CanSwitchinAbilityTrapOpponent(enum Ability ability, u32 opponent); -static u32 GetBattlerTypeMatchup(u32 opposingBattler, u32 battler); -static u32 GetSwitchinHitsToKO(s32 damageTaken, u32 battler, const struct IncomingHealInfo *healInfo, u32 originalHp); -static void GetIncomingHealInfo(u32 battler, struct IncomingHealInfo *healInfo); -static u32 GetWishHealAmountForBattler(u32 battler); +static bool32 CanUseSuperEffectiveMoveAgainstOpponents(enum BattlerId battler); +static bool32 FindMonWithFlagsAndSuperEffective(enum BattlerId battler, u16 flags, u32 moduloPercent); +static u32 GetSwitchinHazardsDamage(enum BattlerId battler); +static bool32 AI_CanSwitchinAbilityTrapOpponent(enum Ability ability, enum BattlerId opposingBattler); +static u32 GetBattlerTypeMatchup(enum BattlerId opposingBattler, enum BattlerId battler); +static u32 GetSwitchinHitsToKO(s32 damageTaken, enum BattlerId battler, const struct IncomingHealInfo *healInfo, u32 originalHp); +static void GetIncomingHealInfo(enum BattlerId battler, struct IncomingHealInfo *healInfo); +static u32 GetWishHealAmountForBattler(enum BattlerId battler); -static void InitializeSwitchinCandidate(u32 switchinBattler, struct Pokemon *mon) +static void InitializeSwitchinCandidate(enum BattlerId switchinBattler, struct Pokemon *mon) { PokemonToBattleMon(mon, &gBattleMons[switchinBattler]); // Setup switchin battler data gAiThinkingStruct->saved[switchinBattler].saved = TRUE; SetBattlerAiData(switchinBattler, gAiLogicData); SetBattlerFieldStatusForSwitchin(switchinBattler); - for (u32 battlerIndex = 0; battlerIndex < gBattlersCount; battlerIndex++) + for (enum BattlerId battlerIndex = 0; battlerIndex < gBattlersCount; battlerIndex++) { if (switchinBattler == battlerIndex || !IsBattlerAlive(battlerIndex)) continue; @@ -60,7 +60,7 @@ static void InitializeSwitchinCandidate(u32 switchinBattler, struct Pokemon *mon gAiThinkingStruct->saved[switchinBattler].saved = FALSE; } -static u32 GetWishHealAmountForBattler(u32 battler) +static u32 GetWishHealAmountForBattler(enum BattlerId battler) { u32 wishHeal = 0; @@ -82,7 +82,7 @@ static u32 GetWishHealAmountForBattler(u32 battler) return wishHeal; } -static void GetIncomingHealInfo(u32 battler, struct IncomingHealInfo *healInfo) +static void GetIncomingHealInfo(enum BattlerId battler, struct IncomingHealInfo *healInfo) { memset(healInfo, 0, sizeof(*healInfo)); @@ -189,7 +189,7 @@ u32 GetSwitchChance(enum ShouldSwitchScenario shouldSwitchScenario) } } -bool32 IsAceMon(u32 battler, u32 monPartyId) +bool32 IsAceMon(enum BattlerId battler, u32 monPartyId) { if (gAiThinkingStruct->aiFlags[battler] & AI_FLAG_ACE_POKEMON && !gProtectStructs[battler].forcedSwitch @@ -202,7 +202,7 @@ bool32 IsAceMon(u32 battler, u32 monPartyId) return FALSE; } -static bool32 AreStatsRaised(u32 battler) +static bool32 AreStatsRaised(enum BattlerId battler) { u8 buffedStatsValue = 0; @@ -215,13 +215,13 @@ static bool32 AreStatsRaised(u32 battler) return (buffedStatsValue > STAY_IN_STATS_RAISED); } -static inline bool32 SetSwitchinAndSwitch(u32 battler, u32 switchinId) +static inline bool32 SetSwitchinAndSwitch(enum BattlerId battler, u32 switchinId) { gBattleStruct->AI_monToSwitchIntoId[battler] = switchinId; return TRUE; } -static bool32 AI_DoesChoiceEffectBlockMove(u32 battler, enum Move move) +static bool32 AI_DoesChoiceEffectBlockMove(enum BattlerId battler, enum Move move) { // Choice locked into something else if (gAiLogicData->lastUsedMove[battler] != MOVE_NONE && gAiLogicData->lastUsedMove[battler] != move @@ -262,11 +262,11 @@ static inline bool32 CanBattlerWin1v1(u32 hitsToKOAI, u32 hitsToKOPlayer, bool32 // Note that as many return statements as possible are INTENTIONALLY put after all of the loops; // the function can take a max of about 0.06s to run, and this prevents the player from identifying // whether the mon will switch or not by seeing how long the delay is before they select a move -static bool32 ShouldSwitchIfHasBadOdds(u32 battler) +static bool32 ShouldSwitchIfHasBadOdds(enum BattlerId battler) { //Variable initialization enum BattlerPosition opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(battler)); - u32 opposingBattler = GetBattlerAtPosition(opposingPosition); + enum BattlerId opposingBattler = GetBattlerAtPosition(opposingPosition); enum Move *playerMoves = GetMovesArray(opposingBattler); enum Move aiMove, playerMove, bestPlayerPriorityMove = MOVE_NONE, bestPlayerMove = MOVE_NONE, expectedMove = MOVE_NONE; enum Ability aiAbility = gAiLogicData->abilities[battler]; @@ -312,7 +312,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler) aiMoveEffect = GetMoveEffect(aiMove); if (aiMove != MOVE_NONE && gBattleMons[battler].pp[moveIndex] > 0) { - u32 nonVolatileStatus = GetMoveNonVolatileStatus(aiMove); + enum MoveEffect nonVolatileStatus = GetMoveNonVolatileStatus(aiMove); // Check if mon has an "important" status move if (aiMoveEffect == EFFECT_REFLECT || aiMoveEffect == EFFECT_LIGHT_SCREEN || aiMoveEffect == EFFECT_SPIKES || aiMoveEffect == EFFECT_TOXIC_SPIKES || aiMoveEffect == EFFECT_STEALTH_ROCK || aiMoveEffect == EFFECT_STICKY_WEB || aiMoveEffect == EFFECT_LEECH_SEED @@ -394,7 +394,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler) return FALSE; } -static bool32 ShouldSwitchIfTruant(u32 battler) +static bool32 ShouldSwitchIfTruant(enum BattlerId battler) { // Switch if mon with truant is bodied by Protect or invulnerability spam if (gAiLogicData->abilities[battler] == ABILITY_TRUANT @@ -409,7 +409,7 @@ static bool32 ShouldSwitchIfTruant(u32 battler) return FALSE; } -static u32 FindMonWithMoveOfEffectiveness(u32 battler, u32 opposingBattler, uq4_12_t effectiveness) +static u32 FindMonWithMoveOfEffectiveness(enum BattlerId battler, enum BattlerId opposingBattler, uq4_12_t effectiveness) { enum Move move; s32 firstId; @@ -441,43 +441,66 @@ static u32 FindMonWithMoveOfEffectiveness(u32 battler, u32 opposingBattler, uq4_ return FALSE; // There is not a single Pokémon in the party that has a move with this effectiveness threshold } -static bool32 ShouldSwitchIfAllMovesBad(u32 battler) +static bool32 CanMoveAffectTarget(struct BattleContext *ctx, u32 moveIndex) { - u32 moveIndex; - u32 opposingBattler = GetOppositeBattler(battler); - enum Move aiMove; + if (ctx->move != MOVE_NONE + && gAiLogicData->effectiveness[ctx->battlerAtk][ctx->battlerDef][moveIndex] > UQ_4_12(0.0) + && !AI_CanMoveBeBlockedByTarget(ctx)) + return TRUE; + return FALSE; +} + +static bool32 IsMoveBad(struct BattleContext *ctx, u32 moveIndex) +{ + if (CanMoveAffectTarget(ctx, moveIndex)) + return FALSE; + if (!ALL_MOVES_BAD_STATUS_MOVES_BAD || GetMovePower(ctx->move) != 0) // If using ALL_MOVES_BAD_STATUS_MOVES_BAD, then need power to be non-zero + return TRUE; + return FALSE; +} + +static bool32 ShouldSwitchIfAllMovesBad(enum BattlerId battler) +{ + enum BattlerId opposingBattler = GetOppositeBattler(battler); + struct BattleContext ctx = {0}; + ctx.battlerAtk = battler; + ctx.battlerDef = opposingBattler; + ctx.abilityAtk = gAiLogicData->abilities[ctx.battlerAtk]; + ctx.abilityDef = gAiLogicData->abilities[ctx.battlerDef]; + ctx.holdEffectAtk = gAiLogicData->holdEffects[ctx.battlerAtk]; + ctx.holdEffectDef = gAiLogicData->holdEffects[ctx.battlerDef]; // Switch if no moves affect opponents - if (IsDoubleBattle()) + if (HasTwoOpponents(battler)) { - u32 opposingPartner = BATTLE_PARTNER(opposingBattler); - for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) + enum BattlerId opposingPartner = BATTLE_PARTNER(opposingBattler); + for (u32 moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) { - aiMove = gBattleMons[battler].moves[moveIndex]; - if (aiMove == MOVE_NONE) - continue; - if (gAiLogicData->effectiveness[battler][opposingBattler][moveIndex] > UQ_4_12(0.0) - || gAiLogicData->effectiveness[battler][opposingPartner][moveIndex] > UQ_4_12(0.0)) + ctx.move = ctx.chosenMove = gBattleMons[battler].moves[moveIndex]; + ctx.moveType = GetBattleMoveType(ctx.move); + // Check if move is bad in the context of both opposing battlers + if (!IsMoveBad(&ctx, moveIndex)) + { return FALSE; + } + else + { + // Set partner data in ctx + ctx.battlerDef = opposingPartner; + ctx.abilityDef = gAiLogicData->abilities[ctx.battlerDef]; + ctx.holdEffectDef = gAiLogicData->holdEffects[ctx.battlerDef]; + if (!IsMoveBad(&ctx, moveIndex)) + return FALSE; + } } } else { - struct BattleContext ctx = {0}; - ctx.battlerAtk = battler; - ctx.battlerDef = opposingBattler; - ctx.abilityAtk = gAiLogicData->abilities[ctx.battlerAtk]; - ctx.abilityDef = gAiLogicData->abilities[ctx.battlerDef]; - ctx.holdEffectAtk = gAiLogicData->holdEffects[ctx.battlerAtk]; - ctx.holdEffectDef = gAiLogicData->holdEffects[ctx.battlerDef]; - - for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) + for (u32 moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) { - aiMove = gBattleMons[battler].moves[moveIndex]; - if (aiMove != MOVE_NONE - && gAiLogicData->effectiveness[battler][opposingBattler][moveIndex] > UQ_4_12(0.0) - && !AI_CanMoveBeBlockedByTarget(&ctx) - && (!ALL_MOVES_BAD_STATUS_MOVES_BAD || GetMovePower(aiMove) != 0)) // If using ALL_MOVES_BAD_STATUS_MOVES_BAD, then need power to be non-zero + ctx.move = ctx.chosenMove = gBattleMons[battler].moves[moveIndex]; + ctx.moveType = GetBattleMoveType(ctx.move); + if (!IsMoveBad(&ctx, moveIndex)) return FALSE; } } @@ -494,9 +517,9 @@ static bool32 ShouldSwitchIfAllMovesBad(u32 battler) return FALSE; } -static bool32 ShouldSwitchIfWonderGuard(u32 battler) +static bool32 ShouldSwitchIfWonderGuard(enum BattlerId battler) { - u32 opposingBattler = GetOppositeBattler(battler); + enum BattlerId opposingBattler = GetOppositeBattler(battler); if (IsDoubleBattle()) return FALSE; @@ -522,17 +545,17 @@ static bool32 ShouldSwitchIfWonderGuard(u32 battler) return FALSE; } -static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler) +static bool32 FindMonThatAbsorbsOpponentsMove(enum BattlerId battler) { - u32 battlerIn1, battlerIn2; + enum BattlerId battlerIn1, battlerIn2; u8 numAbsorbingAbilities = 0; - enum Ability absorbingTypeAbilities[3]; // Array size is maximum number of absorbing abilities for a single type + enum Ability absorbingTypeAbilities[8]; // Max needed for type + move property absorbers s32 firstId; s32 lastId; struct Pokemon *party; enum Ability monAbility; enum Move aiMove; - u32 opposingBattler = GetOppositeBattler(battler); + enum BattlerId opposingBattler = GetOppositeBattler(battler); enum Move incomingMove = GetIncomingMove(battler, opposingBattler, gAiLogicData); enum Type incomingType = CheckDynamicMoveType(GetBattlerMon(opposingBattler), incomingMove, opposingBattler, MON_IN_BATTLE); bool32 isOpposingBattlerChargingOrInvulnerable = !BreaksThroughSemiInvulnerablity(battler, opposingBattler, gAiLogicData->abilities[battler], gAiLogicData->abilities[opposingBattler], incomingMove) || IsTwoTurnNotSemiInvulnerableMove(opposingBattler, incomingMove); @@ -570,42 +593,47 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler) { absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_FLASH_FIRE; } - else if (incomingType == TYPE_WATER || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_WATER)) + if (incomingType == TYPE_WATER || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_WATER)) { absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_WATER_ABSORB; absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_DRY_SKIN; if (GetConfig(CONFIG_REDIRECT_ABILITY_IMMUNITY) >= GEN_5) absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_STORM_DRAIN; } - else if (incomingType == TYPE_ELECTRIC || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_ELECTRIC)) + if (incomingType == TYPE_ELECTRIC || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_ELECTRIC)) { absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_VOLT_ABSORB; absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_MOTOR_DRIVE; if (GetConfig(CONFIG_REDIRECT_ABILITY_IMMUNITY) >= GEN_5) absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_LIGHTNING_ROD; } - else if (incomingType == TYPE_GRASS || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_GRASS)) + if (incomingType == TYPE_GRASS || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_GRASS)) { absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_SAP_SIPPER; } - else if (incomingType == TYPE_GROUND || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_GROUND)) + if (incomingType == TYPE_GROUND || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_GROUND)) { absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_EARTH_EATER; absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_LEVITATE; } - else if (IsSoundMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsSoundMove(incomingMove))) + if (IsSoundMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsSoundMove(incomingMove))) { absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_SOUNDPROOF; } - else if (IsBallisticMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsBallisticMove(incomingMove))) + if (IsBallisticMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsBallisticMove(incomingMove))) { absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_BULLETPROOF; } - else if (IsWindMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsWindMove(incomingMove))) + if (IsWindMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsWindMove(incomingMove))) { absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_WIND_RIDER; } - else + if (IsPowderMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsPowderMove(incomingMove))) + { + if (GetConfig(CONFIG_POWDER_OVERCOAT) >= GEN_6) + absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_OVERCOAT; + } + if (numAbsorbingAbilities == 0) { return FALSE; } @@ -649,10 +677,10 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler) return FALSE; } -static bool32 ShouldSwitchIfOpponentChargingOrInvulnerable(u32 battler) +static bool32 ShouldSwitchIfOpponentChargingOrInvulnerable(enum BattlerId battler) { - u32 opposingBattler = GetOppositeBattler(battler); - u32 incomingMove = GetIncomingMove(battler, opposingBattler, gAiLogicData); + enum BattlerId opposingBattler = GetOppositeBattler(battler); + enum Move incomingMove = GetIncomingMove(battler, opposingBattler, gAiLogicData); bool32 isOpposingBattlerChargingOrInvulnerable = !BreaksThroughSemiInvulnerablity(battler, opposingBattler, gAiLogicData->abilities[battler], gAiLogicData->abilities[opposingBattler], incomingMove) || IsTwoTurnNotSemiInvulnerableMove(opposingBattler, incomingMove); @@ -666,7 +694,7 @@ static bool32 ShouldSwitchIfOpponentChargingOrInvulnerable(u32 battler) return FALSE; } -static bool32 ShouldSwitchIfTrapperInParty(u32 battler) +static bool32 ShouldSwitchIfTrapperInParty(enum BattlerId battler) { s32 firstId; s32 lastId; @@ -703,13 +731,13 @@ static bool32 ShouldSwitchIfTrapperInParty(u32 battler) return FALSE; } -static bool32 ShouldSwitchIfBadlyStatused(u32 battler) +static bool32 ShouldSwitchIfBadlyStatused(enum BattlerId battler) { bool32 switchMon = FALSE; enum Ability monAbility = gAiLogicData->abilities[battler]; enum HoldEffect holdEffect = gAiLogicData->holdEffects[battler]; enum BattlerPosition opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(battler)); - u8 opposingBattler = GetBattlerAtPosition(opposingPosition); + enum BattlerId opposingBattler = GetBattlerAtPosition(opposingPosition); bool32 hasStatRaised = AnyUsefulStatIsRaised(battler); //Perish Song @@ -803,7 +831,7 @@ static bool32 ShouldSwitchIfBadlyStatused(u32 battler) return FALSE; } -static bool32 GetHitEscapeTransformState(u32 battlerAtk, enum Move move) +static bool32 GetHitEscapeTransformState(enum BattlerId battlerAtk, enum Move move) { u32 moveIndex; bool32 hasValidTarget = FALSE; @@ -836,7 +864,7 @@ static bool32 GetHitEscapeTransformState(u32 battlerAtk, enum Move move) ctx.abilityAtk = gAiLogicData->abilities[battlerAtk]; - for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) + for (enum BattlerId battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) { if (!IsBattlerAlive(battlerDef) || IsBattlerAlly(battlerDef, battlerAtk)) continue; @@ -878,7 +906,7 @@ static bool32 GetHitEscapeTransformState(u32 battlerAtk, enum Move move) return isFasterThanAll; } -static bool32 ShouldSwitchIfAbilityBenefit(u32 battler) +static bool32 ShouldSwitchIfAbilityBenefit(enum BattlerId battler) { bool32 hasStatRaised = AnyUsefulStatIsRaised(battler); @@ -943,7 +971,7 @@ static bool32 ShouldSwitchIfAbilityBenefit(u32 battler) return SetSwitchinAndSwitch(battler, PARTY_SIZE); } -static bool32 CanUseSuperEffectiveMoveAgainstOpponent(u32 battler, u32 opposingBattler) +static bool32 CanUseSuperEffectiveMoveAgainstOpponent(enum BattlerId battler, enum BattlerId opposingBattler) { enum Move move; @@ -962,23 +990,23 @@ static bool32 CanUseSuperEffectiveMoveAgainstOpponent(u32 battler, u32 opposingB return FALSE; } -static bool32 CanUseSuperEffectiveMoveAgainstOpponents(u32 battler) +static bool32 CanUseSuperEffectiveMoveAgainstOpponents(enum BattlerId battler) { - u32 opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(battler)); - u32 opposingBattler = GetBattlerAtPosition(opposingPosition); + enum BattlerPosition opposingPosition = GetBattlerPosition(BATTLE_OPPOSITE(battler)); + enum BattlerId opposingBattler = GetBattlerAtPosition(opposingPosition); if (CanUseSuperEffectiveMoveAgainstOpponent(battler, opposingBattler)) return TRUE; - if (IsDoubleBattle() && CanUseSuperEffectiveMoveAgainstOpponent(battler, BATTLE_PARTNER(opposingPosition))) + if (HasTwoOpponents(battler) && CanUseSuperEffectiveMoveAgainstOpponent(battler, BATTLE_PARTNER(BATTLE_OPPOSITE(battler)))) return TRUE; return FALSE; } -static bool32 FindMonWithFlagsAndSuperEffective(u32 battler, u16 flags, u32 percentChance) +static bool32 FindMonWithFlagsAndSuperEffective(enum BattlerId battler, u16 flags, u32 percentChance) { - u32 battlerIn1, battlerIn2; + enum BattlerId battlerIn1, battlerIn2; s32 firstId; s32 lastId; // + 1 struct Pokemon *party; @@ -1039,9 +1067,9 @@ static bool32 FindMonWithFlagsAndSuperEffective(u32 battler, u16 flags, u32 perc return FALSE; } -static bool32 CanMonSurviveHazardSwitchin(u32 battler) +static bool32 CanMonSurviveHazardSwitchin(enum BattlerId battler) { - u32 battlerIn1, battlerIn2; + enum BattlerId battlerIn1, battlerIn2; u32 hazardDamage = 0, battlerHp = gBattleMons[battler].hp; enum Ability ability = gAiLogicData->abilities[battler]; enum Move aiMove; @@ -1082,10 +1110,10 @@ static bool32 CanMonSurviveHazardSwitchin(u32 battler) return TRUE; } -static bool32 ShouldSwitchIfEncored(u32 battler) +static bool32 ShouldSwitchIfEncored(enum BattlerId battler) { enum Move encoredMove = gBattleMons[battler].volatiles.encoredMove; - u32 opposingBattler = GetOppositeBattler(battler); + enum BattlerId opposingBattler = GetOppositeBattler(battler); // Only use this if AI_FLAG_SMART_SWITCHING is set for the trainer if (!(gAiThinkingStruct->aiFlags[battler] & AI_FLAG_SMART_SWITCHING)) @@ -1110,29 +1138,46 @@ static bool32 ShouldSwitchIfEncored(u32 battler) return FALSE; } -static bool32 ShouldSwitchIfBadChoiceLock(u32 battler) +static bool32 ShouldSwitchIfBadChoiceLock(enum BattlerId battler) { - enum Move lastUsedMove = gAiLogicData->lastUsedMove[battler]; - u32 opposingBattler = GetOppositeBattler(battler); - bool32 moveAffectsTarget = TRUE; + enum Move choicedMove = gBattleStruct->choicedMove[battler]; + enum BattlerId opposingBattler = GetOppositeBattler(battler); struct BattleContext ctx = {0}; ctx.battlerAtk = battler; ctx.battlerDef = opposingBattler; - ctx.move = ctx.chosenMove = lastUsedMove; - ctx.moveType = GetBattleMoveType(lastUsedMove); + ctx.move = ctx.chosenMove = choicedMove; + ctx.moveType = GetBattleMoveType(choicedMove); ctx.abilityAtk = gAiLogicData->abilities[ctx.battlerAtk]; ctx.abilityDef = gAiLogicData->abilities[ctx.battlerDef]; ctx.holdEffectAtk = gAiLogicData->holdEffects[ctx.battlerAtk]; ctx.holdEffectDef = gAiLogicData->holdEffects[ctx.battlerDef]; - if (lastUsedMove != MOVE_NONE - && (AI_GetMoveEffectiveness(lastUsedMove, battler, opposingBattler) == UQ_4_12(0.0) || AI_CanMoveBeBlockedByTarget(&ctx))) - moveAffectsTarget = FALSE; + // Not locked in to anything yet, or not choiced + if (choicedMove == MOVE_NONE) + return FALSE; - if (IsHoldEffectChoice(ctx.holdEffectAtk) && IsBattlerItemEnabled(battler)) + u32 moveIndex = GetMoveIndex(battler, choicedMove); + + if (HasTwoOpponents(battler)) { - if ((GetMoveCategory(lastUsedMove) == DAMAGE_CATEGORY_STATUS || !moveAffectsTarget) && RandomPercentage(RNG_AI_SWITCH_CHOICE_LOCKED, GetSwitchChance(SHOULD_SWITCH_CHOICE_LOCKED))) + enum BattlerId opposingPartner = BATTLE_PARTNER(opposingBattler); + if (IsHoldEffectChoice(ctx.holdEffectAtk) && IsBattlerItemEnabled(battler)) + { + if (GetMoveCategory(choicedMove) == DAMAGE_CATEGORY_STATUS || !CanMoveAffectTarget(&ctx, moveIndex)) + { + // Set partner data in ctx + ctx.battlerDef = opposingPartner; + ctx.abilityDef = gAiLogicData->abilities[ctx.battlerDef]; + ctx.holdEffectDef = gAiLogicData->holdEffects[ctx.battlerDef]; + if (!CanMoveAffectTarget(&ctx, moveIndex) && RandomPercentage(RNG_AI_SWITCH_CHOICE_LOCKED, GetSwitchChance(SHOULD_SWITCH_CHOICE_LOCKED))) + return SetSwitchinAndSwitch(battler, PARTY_SIZE); + } + } + } + else if (IsHoldEffectChoice(ctx.holdEffectAtk) && IsBattlerItemEnabled(battler)) + { + if ((GetMoveCategory(choicedMove) == DAMAGE_CATEGORY_STATUS || !CanMoveAffectTarget(&ctx, moveIndex)) && RandomPercentage(RNG_AI_SWITCH_CHOICE_LOCKED, GetSwitchChance(SHOULD_SWITCH_CHOICE_LOCKED))) return SetSwitchinAndSwitch(battler, PARTY_SIZE); } @@ -1140,7 +1185,7 @@ static bool32 ShouldSwitchIfBadChoiceLock(u32 battler) } // AI should switch if it's become setup fodder and has something better to switch to -static bool32 ShouldSwitchIfAttackingStatsLowered(u32 battler) +static bool32 ShouldSwitchIfAttackingStatsLowered(enum BattlerId battler) { s8 attackingStage = gBattleMons[battler].statStages[STAT_ATK]; s8 spAttackingStage = gBattleMons[battler].statStages[STAT_SPATK]; @@ -1185,7 +1230,7 @@ static bool32 ShouldSwitchIfAttackingStatsLowered(u32 battler) return FALSE; } -bool32 ShouldSwitchDynFuncExample(u32 battler) +bool32 ShouldSwitchDynFuncExample(enum BattlerId battler) { // Chance to switch if trainer class is Guitarist, perhaps thematic for Jugglers if (GetTrainerClassFromId(TRAINER_BATTLE_PARAM.opponentA) == TRAINER_CLASS_GUITARIST @@ -1196,7 +1241,7 @@ bool32 ShouldSwitchDynFuncExample(u32 battler) return FALSE; } -static bool32 CanBattlerConsiderSwitch(u32 battler) +static bool32 CanBattlerConsiderSwitch(enum BattlerId battler) { if (gBattleMons[battler].volatiles.wrapped) return FALSE; @@ -1213,9 +1258,9 @@ static bool32 CanBattlerConsiderSwitch(u32 battler) return TRUE; } -bool32 ShouldSwitch(u32 battler) +bool32 ShouldSwitch(enum BattlerId battler) { - u32 battlerIn1, battlerIn2; + enum BattlerId battlerIn1, battlerIn2; s32 firstId; s32 lastId; // + 1 struct Pokemon *party; @@ -1306,7 +1351,7 @@ bool32 ShouldSwitch(u32 battler) return FALSE; } -bool32 ShouldSwitchIfAllScoresBad(u32 battler) +bool32 ShouldSwitchIfAllScoresBad(enum BattlerId battler) { u32 score, opposingBattler = GetOppositeBattler(battler); if (!(gAiThinkingStruct->aiFlags[battler] & AI_FLAG_SMART_SWITCHING)) @@ -1314,9 +1359,19 @@ bool32 ShouldSwitchIfAllScoresBad(u32 battler) for (u32 moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) { - score = gAiBattleData->finalScore[battler][opposingBattler][moveIndex]; - if (score > AI_BAD_SCORE_THRESHOLD) - return FALSE; + if (HasTwoOpponents(battler)) + { + u32 score1 = gAiBattleData->finalScore[battler][opposingBattler][moveIndex]; + u32 score2 = gAiBattleData->finalScore[battler][BATTLE_PARTNER(opposingBattler)][moveIndex]; + if (score1 > AI_BAD_SCORE_THRESHOLD || score2 > AI_BAD_SCORE_THRESHOLD) + return FALSE; + } + else + { + score = gAiBattleData->finalScore[battler][opposingBattler][moveIndex]; + if (score > AI_BAD_SCORE_THRESHOLD) + return FALSE; + } } if (RandomPercentage(RNG_AI_SWITCH_ALL_SCORES_BAD, GetSwitchChance(SHOULD_SWITCH_ALL_SCORES_BAD)) && (gAiLogicData->mostSuitableMonId[battler] != PARTY_SIZE || !ALL_SCORES_BAD_NEEDS_GOOD_SWITCHIN)) @@ -1324,9 +1379,10 @@ bool32 ShouldSwitchIfAllScoresBad(u32 battler) return FALSE; } -bool32 ShouldStayInToUseMove(u32 battler) +bool32 ShouldStayInToUseMove(enum BattlerId battler) { - u32 aiMove, opposingBattler = GetOppositeBattler(battler); + enum Move aiMove; + u32 opposingBattler = GetOppositeBattler(battler); enum BattleMoveEffects aiMoveEffect; for (u32 moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) { @@ -1341,16 +1397,17 @@ bool32 ShouldStayInToUseMove(u32 battler) && !GetHitEscapeTransformState(battler, aiMove)) continue; - if (gAiBattleData->finalScore[battler][opposingBattler][moveIndex] > AI_GOOD_SCORE_THRESHOLD) + if (gAiBattleData->finalScore[battler][opposingBattler][moveIndex] > AI_GOOD_SCORE_THRESHOLD + || (HasTwoOpponents(battler) && gAiBattleData->finalScore[battler][BATTLE_PARTNER(opposingBattler)][moveIndex] > AI_GOOD_SCORE_THRESHOLD)) return TRUE; } } return FALSE; } -void ModifySwitchAfterMoveScoring(u32 battler) +void ModifySwitchAfterMoveScoring(enum BattlerId battler) { - u32 battlerIn1, battlerIn2; + enum BattlerId battlerIn1, battlerIn2; s32 firstId; s32 lastId; // + 1 struct Pokemon *party; @@ -1396,12 +1453,12 @@ void ModifySwitchAfterMoveScoring(u32 battler) gAiLogicData->shouldSwitch &= ~(1u << battler); } -bool32 IsSwitchinValid(u32 battler) +bool32 IsSwitchinValid(enum BattlerId battler) { // Edge case: See if partner already chose to switch into the same mon if (IsDoubleBattle()) { - u32 partner = BATTLE_PARTNER(battler); + enum BattlerId partner = BATTLE_PARTNER(battler); if (gBattleStruct->AI_monToSwitchIntoId[battler] == PARTY_SIZE) // Generic switch { if ((gAiLogicData->shouldSwitch & (1u << partner)) && gAiLogicData->monToSwitchInId[partner] == gAiLogicData->mostSuitableMonId[battler]) @@ -1421,12 +1478,13 @@ bool32 IsSwitchinValid(u32 battler) } // Gets hazard damage -static u32 GetSwitchinHazardsDamage(u32 battler) +static u32 GetSwitchinHazardsDamage(enum BattlerId battler) { u8 tSpikesLayers; - u16 heldItemEffect = gAiLogicData->holdEffects[battler]; + enum HoldEffect heldItemEffect = gAiLogicData->holdEffects[battler]; u32 maxHP = gBattleMons[battler].maxHP; - enum Ability ability = gAiLogicData->abilities[battler], status = gBattleMons[battler].status1; + enum Ability ability = gAiLogicData->abilities[battler]; + u32 status = gBattleMons[battler].status1; u32 spikesDamage = 0, tSpikesDamage = 0, hazardDamage = 0; enum BattleSide side = GetBattlerSide(battler); @@ -1479,7 +1537,7 @@ static u32 GetSwitchinHazardsDamage(u32 battler) } // Gets damage / healing from weather -static s32 GetSwitchinWeatherImpact(u32 battler) +static s32 GetSwitchinWeatherImpact(enum BattlerId battler) { s32 weatherImpact = 0, maxHP = gBattleMons[battler].maxHP; enum Ability ability = gAiLogicData->abilities[battler]; @@ -1542,7 +1600,7 @@ static s32 GetSwitchinWeatherImpact(u32 battler) } // Gets one turn of recurring healing -static u32 GetSwitchinRecurringHealing(u32 battler) +static u32 GetSwitchinRecurringHealing(enum BattlerId battler) { u32 recurringHealing = 0, maxHP = gBattleMons[battler].maxHP; enum Ability ability = gAiLogicData->abilities[battler]; @@ -1577,7 +1635,7 @@ static u32 GetSwitchinRecurringHealing(u32 battler) } // Gets one turn of recurring damage -static u32 GetSwitchinRecurringDamage(u32 battler) +static u32 GetSwitchinRecurringDamage(enum BattlerId battler) { u32 passiveDamage = 0, maxHP = gBattleMons[battler].maxHP; enum Ability ability = gAiLogicData->abilities[battler]; @@ -1609,7 +1667,7 @@ static u32 GetSwitchinRecurringDamage(u32 battler) } // Gets one turn of status damage -static u32 GetSwitchinStatusDamage(u32 battler) +static u32 GetSwitchinStatusDamage(enum BattlerId battler) { u8 tSpikesLayers = gSideTimers[GetBattlerSide(battler)].toxicSpikesAmount; enum HoldEffect heldItemEffect = gAiLogicData->holdEffects[battler]; @@ -1682,7 +1740,7 @@ static u32 GetSwitchinStatusDamage(u32 battler) } // Gets number of hits to KO factoring in hazards, healing held items, status, weather, and incoming heals -static u32 GetSwitchinHitsToKO(s32 damageTaken, u32 battler, const struct IncomingHealInfo *healInfo, u32 originalHp) +static u32 GetSwitchinHitsToKO(s32 damageTaken, enum BattlerId battler, const struct IncomingHealInfo *healInfo, u32 originalHp) { u32 hazardDamage = GetSwitchinHazardsDamage(battler); u32 hazardCheckHp = healInfo->healBeforeHazards ? gBattleMons[battler].maxHP : gBattleMons[battler].hp; @@ -1709,7 +1767,7 @@ static u32 GetSwitchinHitsToKO(s32 damageTaken, u32 battler, const struct Incomi u32 hitsToKO = 0; u16 maxHP = gBattleMons[battler].maxHP, item = gAiLogicData->items[battler], heldItemEffect = GetItemHoldEffect(item); u8 weatherDuration = gBattleStruct->weatherDuration, holdEffectParam = GetItemHoldEffectParam(item); - u32 opposingBattler = GetOppositeBattler(battler); + enum BattlerId opposingBattler = GetOppositeBattler(battler); enum Ability opposingAbility = gAiLogicData->abilities[opposingBattler], ability = gAiLogicData->abilities[battler]; bool32 usedSingleUseHealingItem = FALSE, opponentCanBreakMold = IsMoldBreakerTypeAbility(opposingBattler, opposingAbility); s32 currentHP = startingHP, singleUseItemHeal = 0; @@ -1809,16 +1867,16 @@ static u32 GetSwitchinHitsToKO(s32 damageTaken, u32 battler, const struct Incomi } // Disguise will always add an extra hit to KO - if (!opponentCanBreakMold && gBattleMons[battler].species == SPECIES_MIMIKYU_DISGUISED) + if (!opponentCanBreakMold && IsMimikyuDisguised(battler)) hitsToKO++; return hitsToKO; } -static u32 GetBattlerTypeMatchup(u32 opposingBattler, u32 battler) +static u32 GetBattlerTypeMatchup(enum BattlerId opposingBattler, enum BattlerId battler) { // Check type matchup - u32 typeEffectiveness1 = UQ_4_12(1.0), typeEffectiveness2 = UQ_4_12(1.0); + uq4_12_t typeEffectiveness1 = UQ_4_12(1.0), typeEffectiveness2 = UQ_4_12(1.0); enum Type atkType1 = gBattleMons[opposingBattler].types[0], atkType2 = gBattleMons[opposingBattler].types[1]; enum Type defType1 = gBattleMons[battler].types[0], defType2 = gBattleMons[battler].types[1]; @@ -1845,7 +1903,7 @@ static u32 GetBattlerTypeMatchup(u32 opposingBattler, u32 battler) return typeEffectiveness1 + typeEffectiveness2; } -static u32 GetSwitchinCandidate(u32 switchinCategory, u32 battler, int firstId, int lastId, enum SwitchType switchType) +static u32 GetSwitchinCandidate(u32 switchinCategory, enum BattlerId battler, int firstId, int lastId, enum SwitchType switchType) { if (switchinCategory == 0) return PARTY_SIZE; @@ -1870,7 +1928,7 @@ static u32 GetSwitchinCandidate(u32 switchinCategory, u32 battler, int firstId, return PARTY_SIZE; } -static u32 GetValidSwitchinCandidate(u32 validMonIds, u32 battler, u32 firstId, u32 lastId, enum SwitchType switchType) +static u32 GetValidSwitchinCandidate(u32 validMonIds, enum BattlerId battler, u32 firstId, u32 lastId, enum SwitchType switchType) { if (validMonIds == 0) return PARTY_SIZE; @@ -1895,7 +1953,7 @@ static u32 GetValidSwitchinCandidate(u32 validMonIds, u32 battler, u32 firstId, return PARTY_SIZE; } -static s32 GetMaxDamagePlayerCouldDealToSwitchin(u32 battler, u32 opposingBattler, enum Move *bestPlayerMove) +static s32 GetMaxDamagePlayerCouldDealToSwitchin(enum BattlerId battler, enum BattlerId opposingBattler, enum Move *bestPlayerMove) { enum Move playerMove; enum Move *playerMoves = GetMovesArray(opposingBattler); @@ -1922,7 +1980,7 @@ static s32 GetMaxDamagePlayerCouldDealToSwitchin(u32 battler, u32 opposingBattle return maxDamageTaken; } -static s32 GetMaxPriorityDamagePlayerCouldDealToSwitchin(u32 battler, u32 opposingBattler, enum Move *bestPlayerPriorityMove) +static s32 GetMaxPriorityDamagePlayerCouldDealToSwitchin(enum BattlerId battler, enum BattlerId opposingBattler, enum Move *bestPlayerPriorityMove) { enum Move playerMove; enum Move *playerMoves = GetMovesArray(opposingBattler); @@ -1953,7 +2011,7 @@ static s32 GetMaxPriorityDamagePlayerCouldDealToSwitchin(u32 battler, u32 opposi return maxDamageTaken; } -static bool32 AI_CanSwitchinAbilityTrapOpponent(enum Ability ability, u32 opposingBattler) +static bool32 AI_CanSwitchinAbilityTrapOpponent(enum Ability ability, enum BattlerId opposingBattler) { if (AI_CanBattlerEscape(opposingBattler)) return FALSE; @@ -1974,7 +2032,7 @@ static bool32 AI_CanSwitchinAbilityTrapOpponent(enum Ability ability, u32 opposi return FALSE; } -static inline bool32 IsFreeSwitch(enum SwitchType switchType, u32 battlerSwitchingOut, u32 opposingBattler) +static inline bool32 IsFreeSwitch(enum SwitchType switchType, enum BattlerId battlerSwitchingOut, enum BattlerId opposingBattler) { bool32 movedSecond = GetBattlerTurnOrderNum(battlerSwitchingOut) > GetBattlerTurnOrderNum(opposingBattler) ? TRUE : FALSE; @@ -2031,7 +2089,7 @@ static inline bool32 CanSwitchinWin1v1(u32 hitsToKOAI, u32 hitsToKOPlayer, bool3 // This function splits switching behaviour depending on whether the switch is free. // Everything runs in the same loop to minimize computation time. This makes it harder to read, but hopefully the comments can guide you! -static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId, u32 battler, u32 opposingBattler, u32 battlerIn1, u32 battlerIn2, enum SwitchType switchType) +static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId, enum BattlerId battler, enum BattlerId opposingBattler, enum BattlerId battlerIn1, enum BattlerId battlerIn2, enum SwitchType switchType) { struct IncomingHealInfo healInfoData; const struct IncomingHealInfo *healInfo = &healInfoData; @@ -2272,7 +2330,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId, return PARTY_SIZE; } -static u32 GetBestMonVanilla(struct Pokemon *party, int firstId, int lastId, u32 battler, u32 opposingBattler, u32 battlerIn1, u32 battlerIn2, enum SwitchType switchType) +static u32 GetBestMonVanilla(struct Pokemon *party, int firstId, int lastId, enum BattlerId battler, enum BattlerId opposingBattler, enum BattlerId battlerIn1, enum BattlerId battlerIn2, enum SwitchType switchType) { s32 aceMonCount = 0; u32 validMonIds = 0, batonPassIds = 0, typeMatchupIds = 0, bestDamageId = PARTY_SIZE, aceMonId = PARTY_SIZE; @@ -2315,7 +2373,7 @@ static u32 GetBestMonVanilla(struct Pokemon *party, int firstId, int lastId, u32 if (gBattleMons[battler].pp[moveIndex] < 1) continue; - u32 aiMove = gBattleMons[battler].moves[moveIndex]; + enum Move aiMove = gBattleMons[battler].moves[moveIndex]; // Baton Pass if (GetMoveEffect(aiMove) == EFFECT_BATON_PASS) @@ -2367,7 +2425,7 @@ static u32 GetBestMonVanilla(struct Pokemon *party, int firstId, int lastId, u32 return PARTY_SIZE; } -static u32 GetNextMonInParty(struct Pokemon *party, int firstId, int lastId, u32 battlerIn1, u32 battlerIn2) +static u32 GetNextMonInParty(struct Pokemon *party, int firstId, int lastId, enum BattlerId battlerIn1, enum BattlerId battlerIn2) { // Iterate through mons for (u32 monIndex = firstId; monIndex < lastId; monIndex++) @@ -2382,11 +2440,11 @@ static u32 GetNextMonInParty(struct Pokemon *party, int firstId, int lastId, u32 return PARTY_SIZE; } -u32 GetMostSuitableMonToSwitchInto(u32 battler, enum SwitchType switchType) +u32 GetMostSuitableMonToSwitchInto(enum BattlerId battler, enum SwitchType switchType) { - u32 opposingBattler = 0; + enum BattlerId opposingBattler = 0; u32 bestMonId = PARTY_SIZE; - u32 battlerIn1 = 0, battlerIn2 = 0; + enum BattlerId battlerIn1 = 0, battlerIn2 = 0; s32 firstId = 0; s32 lastId = 0; // + 1 struct Pokemon *party; @@ -2421,10 +2479,10 @@ u32 GetMostSuitableMonToSwitchInto(u32 battler, enum SwitchType switchType) } } -u32 AI_SelectRevivalBlessingMon(u32 battler) +u32 AI_SelectRevivalBlessingMon(enum BattlerId battler) { s32 firstId = 0, lastId = 0; - u32 opposingBattler = 0; + enum BattlerId opposingBattler = 0; struct Pokemon *party = GetBattlerParty(battler); u32 bestMonId = PARTY_SIZE; s32 bestScore = -1; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 4530ebe79..a5054f41c 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -7,7 +7,7 @@ #include "battle_ai_util.h" #include "battle_ai_main.h" #include "battle_controllers.h" -// #include "battle_factory.h" +#include "battle_factory.h" #include "battle_setup.h" #include "event_data.h" #include "data.h" @@ -24,10 +24,10 @@ #include "constants/items.h" static u32 GetAIEffectGroup(enum BattleMoveEffects effect); -static u32 GetAIEffectGroupFromMove(u32 battler, enum Move move); +static u32 GetAIEffectGroupFromMove(enum BattlerId battler, enum Move move); // Functions -enum Ability AI_GetMoldBreakerSanitizedAbility(u32 battlerAtk, enum Ability abilityAtk, enum Ability abilityDef, enum HoldEffect holdEffectDef, enum Move move) +enum Ability AI_GetMoldBreakerSanitizedAbility(enum BattlerId battlerAtk, enum Ability abilityAtk, enum Ability abilityDef, enum HoldEffect holdEffectDef, enum Move move) { if (MoveIgnoresTargetAbility(move)) return ABILITY_NONE; @@ -38,7 +38,7 @@ enum Ability AI_GetMoldBreakerSanitizedAbility(u32 battlerAtk, enum Ability abil return abilityDef; } -static bool32 AI_IsDoubleSpreadMove(u32 battlerAtk, enum Move move) +static bool32 AI_IsDoubleSpreadMove(enum BattlerId battlerAtk, enum Move move) { u32 numOfTargets = 0; enum MoveTarget moveTargetType = AI_GetBattlerMoveTargetType(battlerAtk, move); @@ -46,7 +46,7 @@ static bool32 AI_IsDoubleSpreadMove(u32 battlerAtk, enum Move move) if (!IsSpreadMove(moveTargetType)) return FALSE; - for (u32 battlerDef = 0; battlerDef < MAX_BATTLERS_COUNT; battlerDef++) + for (enum BattlerId battlerDef = 0; battlerDef < MAX_BATTLERS_COUNT; battlerDef++) { if (battlerAtk == battlerDef) continue; @@ -64,19 +64,19 @@ static bool32 AI_IsDoubleSpreadMove(u32 battlerAtk, enum Move move) return FALSE; } -bool32 AI_IsBattlerGrounded(u32 battler) +bool32 AI_IsBattlerGrounded(enum BattlerId battler) { return IsBattlerGrounded(battler, gAiLogicData->abilities[battler], gAiLogicData->holdEffects[battler]); } -static u32 AI_CanBattlerHitBothFoesInTerrain(u32 battler, enum Move move, enum BattleMoveEffects effect) +static bool32 AI_CanBattlerHitBothFoesInTerrain(enum BattlerId battler, enum Move move, enum BattleMoveEffects effect) { return effect == EFFECT_TERRAIN_BOOST && GetMoveTerrainBoost_HitsBothFoes(move) && IsBattlerTerrainAffected(battler, gAiLogicData->abilities[battler], gAiLogicData->holdEffects[battler], gFieldStatuses, GetMoveTerrainBoost_Terrain(move)); } -enum MoveTarget AI_GetBattlerMoveTargetType(u32 battler, enum Move move) +enum MoveTarget AI_GetBattlerMoveTargetType(enum BattlerId battler, enum Move move) { enum BattleMoveEffects effect = GetMoveEffect(move); if (effect == EFFECT_CURSE && !IS_BATTLER_OF_TYPE(battler, TYPE_GHOST)) @@ -89,11 +89,10 @@ enum MoveTarget AI_GetBattlerMoveTargetType(u32 battler, enum Move move) return GetMoveTarget(move); } -u32 AI_GetDamage(u32 battlerAtk, u32 battlerDef, u32 moveIndex, enum DamageCalcContext calcContext, struct AiLogicData *aiData) +u32 AI_GetDamage(enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 moveIndex, enum DamageCalcContext calcContext, struct AiLogicData *aiData) { if (calcContext == AI_ATTACKING && BattlerHasAi(battlerAtk)) { - if ((gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_RISKY) && !(gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_CONSERVATIVE)) // Risky assumes it deals max damage return aiData->simulatedDmg[battlerAtk][battlerDef][moveIndex].maximum; if ((gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_CONSERVATIVE) && !(gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_RISKY)) // Conservative assumes it deals min damage @@ -114,17 +113,17 @@ u32 AI_GetDamage(u32 battlerAtk, u32 battlerDef, u32 moveIndex, enum DamageCalcC } } -bool32 AI_IsFaster(u32 battlerAi, u32 battlerDef, enum Move aiMove, enum Move playerMove, enum ConsiderPriority considerPriority) +bool32 AI_IsFaster(enum BattlerId battlerAi, enum BattlerId battlerDef, enum Move aiMove, enum Move playerMove, enum ConsiderPriority considerPriority) { return (AI_WhoStrikesFirst(battlerAi, battlerDef, aiMove, playerMove, considerPriority) == AI_IS_FASTER); } -bool32 AI_IsSlower(u32 battlerAi, u32 battlerDef, enum Move aiMove, enum Move playerMove, enum ConsiderPriority considerPriority) +bool32 AI_IsSlower(enum BattlerId battlerAi, enum BattlerId battlerDef, enum Move aiMove, enum Move playerMove, enum ConsiderPriority considerPriority) { return (AI_WhoStrikesFirst(battlerAi, battlerDef, aiMove, playerMove, considerPriority) == AI_IS_SLOWER); } -enum Move GetAIChosenMove(u32 battlerId) +enum Move GetAIChosenMove(enum BattlerId battlerId) { return (gBattleMons[battlerId].moves[gAiBattleData->chosenMoveIndex[battlerId]]); } @@ -138,7 +137,7 @@ bool32 AI_RandLessThan(u32 val) bool32 IsAiFlagPresent(u64 flag) { - for (u32 battlerIndex = 0; battlerIndex < MAX_BATTLERS_COUNT; battlerIndex++) + for (enum BattlerId battlerIndex = 0; battlerIndex < MAX_BATTLERS_COUNT; battlerIndex++) { if (gAiThinkingStruct->aiFlags[battlerIndex] & flag) return TRUE; @@ -147,7 +146,7 @@ bool32 IsAiFlagPresent(u64 flag) return FALSE; } -bool32 IsAiBattlerAware(u32 battlerId) +bool32 IsAiBattlerAware(enum BattlerId battlerId) { if (IsAiFlagPresent(AI_FLAG_OMNISCIENT)) return TRUE; @@ -155,7 +154,7 @@ bool32 IsAiBattlerAware(u32 battlerId) return BattlerHasAi(battlerId); } -bool32 IsAiBattlerAssumingStab(u32 battlerId) +bool32 IsAiBattlerAssumingStab(enum BattlerId battlerId) { if (IsAiFlagPresent(AI_FLAG_ASSUME_STAB)) return TRUE; @@ -163,7 +162,7 @@ bool32 IsAiBattlerAssumingStab(u32 battlerId) return FALSE; } -bool32 IsAiBattlerAssumingStatusMoves(u32 battlerId) +bool32 IsAiBattlerAssumingStatusMoves(enum BattlerId battlerId) { if (IsAiFlagPresent(AI_FLAG_ASSUME_STATUS_MOVES)) return TRUE; @@ -171,7 +170,7 @@ bool32 IsAiBattlerAssumingStatusMoves(u32 battlerId) return FALSE; } -bool32 IsAiBattlerPredictingAbility(u32 battlerId) +bool32 IsAiBattlerPredictingAbility(enum BattlerId battlerId) { if (IsAiFlagPresent(AI_FLAG_WEIGH_ABILITY_PREDICTION)) return TRUE; @@ -179,7 +178,7 @@ bool32 IsAiBattlerPredictingAbility(u32 battlerId) return FALSE; } -bool32 CanAiPredictMove(u32 battlerId) +bool32 CanAiPredictMove(enum BattlerId battlerId) { if (IsAiFlagPresent(AI_FLAG_PREDICT_MOVE)) return TRUE; @@ -187,10 +186,10 @@ bool32 CanAiPredictMove(u32 battlerId) return FALSE; } -bool32 IsBattlerPredictedToSwitch(u32 battler) +bool32 IsBattlerPredictedToSwitch(enum BattlerId battler) { // Check for prediction flag on AI, whether they're using those predictions this turn, and whether the AI thinks the player should switch - for (u32 battlerIndex = 0; battlerIndex < MAX_BATTLERS_COUNT; battlerIndex++) + for (enum BattlerId battlerIndex = 0; battlerIndex < MAX_BATTLERS_COUNT; battlerIndex++) { if (gAiThinkingStruct->aiFlags[battlerIndex] & AI_FLAG_PREDICT_SWITCH) { @@ -202,7 +201,7 @@ bool32 IsBattlerPredictedToSwitch(u32 battler) } // Either a predicted move or the last used move from an opposing battler -enum Move GetIncomingMove(u32 battler, u32 opposingBattler, struct AiLogicData *aiData) +enum Move GetIncomingMove(enum BattlerId battler, enum BattlerId opposingBattler, struct AiLogicData *aiData) { if (aiData->predictingMove && CanAiPredictMove(battler)) return aiData->predictedMove[opposingBattler]; @@ -210,7 +209,7 @@ enum Move GetIncomingMove(u32 battler, u32 opposingBattler, struct AiLogicData * } // When not predicting, don't want to reference player's previous move; leads to weird behaviour for cases like Fake Out or Protect, especially in doubles -enum Move GetIncomingMoveSpeedCheck(u32 battler, u32 opposingBattler, struct AiLogicData *aiData) +enum Move GetIncomingMoveSpeedCheck(enum BattlerId battler, enum BattlerId opposingBattler, struct AiLogicData *aiData) { if (aiData->predictingMove && CanAiPredictMove(battler)) { @@ -222,14 +221,14 @@ enum Move GetIncomingMoveSpeedCheck(u32 battler, u32 opposingBattler, struct AiL return MOVE_NONE; } -void ClearBattlerMoveHistory(u32 battlerId) +void ClearBattlerMoveHistory(enum BattlerId battlerId) { memset(gBattleHistory->usedMoves[battlerId], 0, sizeof(gBattleHistory->usedMoves[battlerId])); memset(gBattleHistory->moveHistory[battlerId], 0, sizeof(gBattleHistory->moveHistory[battlerId])); gBattleHistory->moveHistoryIndex[battlerId] = 0; } -void RecordLastUsedMoveBy(u32 battlerId, enum Move move) +void RecordLastUsedMoveBy(enum BattlerId battlerId, enum Move move) { u8 *index = &gBattleHistory->moveHistoryIndex[battlerId]; @@ -238,7 +237,7 @@ void RecordLastUsedMoveBy(u32 battlerId, enum Move move) gBattleHistory->moveHistory[battlerId][*index] = move; } -void RecordKnownMove(u32 battler, enum Move move) +void RecordKnownMove(enum BattlerId battler, enum Move move) { s32 moveIndex; @@ -255,34 +254,34 @@ void RecordKnownMove(u32 battler, enum Move move) } } -void RecordAllMoves(u32 battler) +void RecordAllMoves(enum BattlerId battler) { memcpy(gAiPartyData->mons[GetBattlerSide(battler)][gBattlerPartyIndexes[battler]].moves, gBattleMons[battler].moves, MAX_MON_MOVES * sizeof(u16)); } -void RecordAbilityBattle(u32 battlerId, enum Ability abilityId) +void RecordAbilityBattle(enum BattlerId battlerId, enum Ability abilityId) { gBattleHistory->abilities[battlerId] = abilityId; gAiPartyData->mons[GetBattlerSide(battlerId)][gBattlerPartyIndexes[battlerId]].ability = abilityId; } -void ClearBattlerAbilityHistory(u32 battlerId) +void ClearBattlerAbilityHistory(enum BattlerId battlerId) { gBattleHistory->abilities[battlerId] = ABILITY_NONE; } -void RecordItemEffectBattle(u32 battlerId, enum HoldEffect itemEffect) +void RecordItemEffectBattle(enum BattlerId battlerId, enum HoldEffect itemEffect) { gBattleHistory->itemEffects[battlerId] = itemEffect; gAiPartyData->mons[GetBattlerSide(battlerId)][gBattlerPartyIndexes[battlerId]].heldEffect = itemEffect; } -void ClearBattlerItemEffectHistory(u32 battlerId) +void ClearBattlerItemEffectHistory(enum BattlerId battlerId) { - gBattleHistory->itemEffects[battlerId] = 0; + gBattleHistory->itemEffects[battlerId] = HOLD_EFFECT_NONE; } -void SaveBattlerData(u32 battlerId) +void SaveBattlerData(enum BattlerId battlerId) { if (!BattlerHasAi(battlerId) && !gAiThinkingStruct->saved[battlerId].saved) { @@ -358,7 +357,7 @@ bool32 ShouldRecordStatusMove(enum Move move) return RandomPercentage(RNG_AI_ASSUME_ALL_STATUS, ASSUME_ALL_STATUS_ODDS) && IsBattleMoveStatus(move); } -static bool32 ShouldFailForIllusion(u32 illusionSpecies, u32 battlerId) +static bool32 ShouldFailForIllusion(u32 illusionSpecies, enum BattlerId battlerId) { u32 learnsetMoveIndex; const struct LevelUpMove *learnset; @@ -394,12 +393,12 @@ static bool32 ShouldFailForIllusion(u32 illusionSpecies, u32 battlerId) return TRUE; } -void SetBattlerData(u32 battlerId) +void SetBattlerData(enum BattlerId battlerId) { if (!BattlerHasAi(battlerId) && gAiThinkingStruct->saved[battlerId].saved) { - u32 species, illusionSpecies, side; - side = GetBattlerSide(battlerId); + u32 species, illusionSpecies; + enum BattleSide side = GetBattlerSide(battlerId); // Simulate Illusion species = gBattleMons[battlerId].species; @@ -438,7 +437,7 @@ void SetBattlerData(u32 battlerId) } } -void RestoreBattlerData(u32 battlerId) +void RestoreBattlerData(enum BattlerId battlerId) { if (!BattlerHasAi(battlerId) && gAiThinkingStruct->saved[battlerId].saved) { @@ -453,12 +452,12 @@ void RestoreBattlerData(u32 battlerId) gBattleMons[battlerId].types[1] = gAiThinkingStruct->saved[battlerId].types[1]; } -u32 GetHealthPercentage(u32 battlerId) +u32 GetHealthPercentage(enum BattlerId battlerId) { return (u32)((100 * gBattleMons[battlerId].hp) / gBattleMons[battlerId].maxHP); } -bool32 AI_BattlerAtMaxHp(u32 battlerId) +bool32 AI_BattlerAtMaxHp(enum BattlerId battlerId) { if (gAiLogicData->hpPercents[battlerId] == 100) return TRUE; @@ -466,7 +465,7 @@ bool32 AI_BattlerAtMaxHp(u32 battlerId) } -bool32 AI_CanBattlerEscape(u32 battler) +bool32 AI_CanBattlerEscape(enum BattlerId battler) { enum HoldEffect holdEffect = gAiLogicData->holdEffects[battler]; @@ -478,7 +477,7 @@ bool32 AI_CanBattlerEscape(u32 battler) return FALSE; } -bool32 IsBattlerTrapped(u32 battlerAtk, u32 battlerDef) +bool32 IsBattlerTrapped(enum BattlerId battlerAtk, enum BattlerId battlerDef) { if (AI_CanBattlerEscape(battlerDef)) return FALSE; @@ -519,7 +518,7 @@ u32 GetTotalBaseStat(u32 species) + GetSpeciesBaseSpDefense(species); } -bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler) +bool32 IsTruantMonVulnerable(enum BattlerId battlerAI, enum BattlerId opposingBattler) { enum Move predictedMoveSpeedCheck = GetIncomingMoveSpeedCheck(battlerAI, opposingBattler, gAiLogicData); @@ -535,7 +534,7 @@ bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler) return FALSE; } -bool32 Ai_IsPriorityBlocked(u32 battlerAtk, u32 battlerDef, enum Move move, struct AiLogicData *aiData) +bool32 Ai_IsPriorityBlocked(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, struct AiLogicData *aiData) { s32 atkPriority = GetBattleMovePriority(battlerAtk, aiData->abilities[battlerAtk], move); @@ -596,7 +595,7 @@ bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, enum DamageCategory c } // To save computation time this function has 2 variants. One saves, sets and restores battlers, while the other doesn't. -struct SimulatedDamage AI_CalcDamageSaveBattlers(enum Move move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, enum AIConsiderGimmick considerGimmickAtk, enum AIConsiderGimmick considerGimmickDef) +struct SimulatedDamage AI_CalcDamageSaveBattlers(enum Move move, enum BattlerId battlerAtk, enum BattlerId battlerDef, uq4_12_t *typeEffectiveness, enum AIConsiderGimmick considerGimmickAtk, enum AIConsiderGimmick considerGimmickDef) { struct SimulatedDamage dmg; @@ -724,7 +723,7 @@ bool32 IsDamageMoveUnusable(struct BattleContext *ctx) return FALSE; } -bool32 IsAdditionalEffectBlocked(u32 battlerAtk, u32 abilityAtk, u32 battlerDef, enum Ability abilityDef) +bool32 IsAdditionalEffectBlocked(enum BattlerId battlerAtk, u32 abilityAtk, enum BattlerId battlerDef, enum Ability abilityDef) { if (gAiLogicData->holdEffects[battlerDef] == HOLD_EFFECT_COVERT_CLOAK) return TRUE; @@ -745,14 +744,14 @@ static inline s32 GetDamageByRollType(s32 dmg, enum DamageRollType rollType) return DmgRoll(dmg); } -static inline void AI_StoreBattlerTypes(u32 battlerAtk, enum Type *types) +static inline void AI_StoreBattlerTypes(enum BattlerId battlerAtk, enum Type *types) { types[0] = gBattleMons[battlerAtk].types[0]; types[1] = gBattleMons[battlerAtk].types[1]; types[2] = gBattleMons[battlerAtk].types[2]; } -static inline void AI_RestoreBattlerTypes(u32 battlerAtk, enum Type *types) +static inline void AI_RestoreBattlerTypes(enum BattlerId battlerAtk, enum Type *types) { gBattleMons[battlerAtk].types[0] = types[0]; gBattleMons[battlerAtk].types[1] = types[1]; @@ -893,7 +892,7 @@ static s32 AI_ApplyModifiersAfterDmgRoll(struct BattleContext *ctx, s32 dmg) return dmg; } -struct SimulatedDamage AI_CalcDamage(enum Move move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, enum AIConsiderGimmick considerGimmickAtk, enum AIConsiderGimmick considerGimmickDef, u32 weather, u32 fieldStatuses) +struct SimulatedDamage AI_CalcDamage(enum Move move, enum BattlerId battlerAtk, enum BattlerId battlerDef, uq4_12_t *typeEffectiveness, enum AIConsiderGimmick considerGimmickAtk, enum AIConsiderGimmick considerGimmickDef, u32 weather, u32 fieldStatuses) { struct SimulatedDamage simDamage = {0}; enum BattleMoveEffects moveEffect = GetMoveEffect(move); @@ -907,7 +906,7 @@ struct SimulatedDamage AI_CalcDamage(enum Move move, u32 battlerAtk, u32 battler return simDamage; if (moveEffect == EFFECT_NATURE_POWER) - move = GetNaturePowerMove(battlerAtk); + move = GetNaturePowerMove(); // Temporarily enable gimmicks for damage calcs if planned if (gBattleStruct->gimmick.usableGimmick[battlerAtk] && GetActiveGimmick(battlerAtk) == GIMMICK_NONE @@ -1021,7 +1020,7 @@ struct SimulatedDamage AI_CalcDamage(enum Move move, u32 battlerAtk, u32 battler return simDamage; } -bool32 AI_IsDamagedByRecoil(u32 battler) +bool32 AI_IsDamagedByRecoil(enum BattlerId battler) { enum Ability ability = gAiLogicData->abilities[battler]; if (ability == ABILITY_MAGIC_GUARD || ability == ABILITY_ROCK_HEAD) @@ -1030,7 +1029,7 @@ bool32 AI_IsDamagedByRecoil(u32 battler) } // Decide whether move having an additional effect for . -static bool32 AI_IsMoveEffectInPlus(u32 battlerAtk, u32 battlerDef, enum Move move, s32 noOfHitsToKo) +static bool32 AI_IsMoveEffectInPlus(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 noOfHitsToKo) { enum Ability abilityDef = gAiLogicData->abilities[battlerDef]; enum Ability abilityAtk = gAiLogicData->abilities[battlerAtk]; @@ -1214,7 +1213,7 @@ static bool32 AI_IsMoveEffectInPlus(u32 battlerAtk, u32 battlerDef, enum Move mo return FALSE; } -static bool32 AI_IsMoveEffectInMinus(u32 battlerAtk, u32 battlerDef, enum Move move, s32 noOfHitsToKo) +static bool32 AI_IsMoveEffectInMinus(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 noOfHitsToKo) { enum Ability abilityAtk = gAiLogicData->abilities[battlerAtk]; enum Ability abilityDef = gAiLogicData->abilities[battlerDef]; @@ -1243,6 +1242,13 @@ static bool32 AI_IsMoveEffectInMinus(u32 battlerAtk, u32 battlerDef, enum Move m if (AI_IsDamagedByRecoil(battlerAtk)) return TRUE; break; + case EFFECT_SEMI_INVULNERABLE: + if (abilityAtk == ABILITY_NO_GUARD || abilityDef == ABILITY_NO_GUARD) + { + if (gAiLogicData->holdEffects[battlerAtk] != HOLD_EFFECT_POWER_HERB) + return TRUE; + } + break; case EFFECT_ABSORB: if (abilityDef == ABILITY_LIQUID_OOZE) return TRUE; @@ -1312,7 +1318,7 @@ static bool32 AI_IsMoveEffectInMinus(u32 battlerAtk, u32 battlerDef, enum Move m } // Checks if one of the moves has side effects or perks, assuming equal dmg or equal no of hits to KO -enum MoveComparisonResult CompareMoveEffects(enum Move move1, enum Move move2, u32 battlerAtk, u32 battlerDef, s32 noOfHitsToKo) +enum MoveComparisonResult CompareMoveEffects(enum Move move1, enum Move move2, enum BattlerId battlerAtk, enum BattlerId battlerDef, s32 noOfHitsToKo) { bool32 effect1, effect2; enum Ability defAbility = gAiLogicData->abilities[battlerDef]; @@ -1356,12 +1362,12 @@ u32 GetNoOfHitsToKO(u32 dmg, s32 hp) return (hp + dmg - 1) / dmg; } -u32 GetNoOfHitsToKOBattlerDmg(u32 dmg, u32 battlerDef) +u32 GetNoOfHitsToKOBattlerDmg(u32 dmg, enum BattlerId battlerDef) { return GetNoOfHitsToKO(dmg, gBattleMons[battlerDef].hp); } -u32 GetNoOfHitsToKOBattler(u32 battlerAtk, u32 battlerDef, u32 moveIndex, enum DamageCalcContext calcContext, enum AiConsiderEndure considerEndure) +u32 GetNoOfHitsToKOBattler(enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 moveIndex, enum DamageCalcContext calcContext, enum AiConsiderEndure considerEndure) { u32 hitsToKO = GetNoOfHitsToKOBattlerDmg(AI_GetDamage(battlerAtk, battlerDef, moveIndex, calcContext, gAiLogicData), battlerDef); enum Move *moves = GetMovesArray(battlerAtk); @@ -1372,7 +1378,7 @@ u32 GetNoOfHitsToKOBattler(u32 battlerAtk, u32 battlerDef, u32 moveIndex, enum D return hitsToKO; } -u32 GetBestNoOfHitsToKO(u32 battlerAtk, u32 battlerDef, enum DamageCalcContext calcContext) +u32 GetBestNoOfHitsToKO(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum DamageCalcContext calcContext) { u32 result = 100; u32 tempResult = 0; @@ -1394,14 +1400,14 @@ u32 GetBestNoOfHitsToKO(u32 battlerAtk, u32 battlerDef, enum DamageCalcContext c return result; } -u32 GetCurrDamageHpPercent(u32 battlerAtk, u32 battlerDef, enum DamageCalcContext calcContext) +u32 GetCurrDamageHpPercent(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum DamageCalcContext calcContext) { int bestDmg = AI_GetDamage(battlerAtk, battlerDef, gAiThinkingStruct->movesetIndex, calcContext, gAiLogicData); return (bestDmg * 100) / gBattleMons[battlerDef].maxHP; } -uq4_12_t AI_GetMoveEffectiveness(enum Move move, u32 battlerAtk, u32 battlerDef) +uq4_12_t AI_GetMoveEffectiveness(enum Move move, enum BattlerId battlerAtk, enum BattlerId battlerDef) { uq4_12_t typeEffectiveness; @@ -1437,7 +1443,7 @@ uq4_12_t AI_GetMoveEffectiveness(enum Move move, u32 battlerAtk, u32 battlerDef) * AI_IS_FASTER: is user(ai) faster * AI_IS_SLOWER: is target faster */ -s32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler, enum Move aiMoveConsidered, enum Move playerMoveConsidered, enum ConsiderPriority considerPriority) +s32 AI_WhoStrikesFirst(enum BattlerId battlerAI, enum BattlerId battler, enum Move aiMoveConsidered, enum Move playerMoveConsidered, enum ConsiderPriority considerPriority) { u32 speedBattlerAI, speedBattler; enum HoldEffect holdEffectAI = gAiLogicData->holdEffects[battlerAI]; @@ -1491,7 +1497,7 @@ s32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler, enum Move aiMoveConsidered, e return AI_IS_SLOWER; } -bool32 CanEndureHit(u32 battler, u32 battlerTarget, enum Move move) +bool32 CanEndureHit(enum BattlerId battler, enum BattlerId battlerTarget, enum Move move) { if (!AI_BattlerAtMaxHp(battlerTarget) || IsMultiHitMove(move) || gAiLogicData->abilities[battler] == ABILITY_PARENTAL_BOND) return FALSE; @@ -1515,7 +1521,7 @@ bool32 CanEndureHit(u32 battler, u32 battlerTarget, enum Move move) } // Check if target has means to faint ai mon. -bool32 CanTargetFaintAi(u32 battlerDef, u32 battlerAtk) +bool32 CanTargetFaintAi(enum BattlerId battlerDef, enum BattlerId battlerAtk) { struct AiLogicData *aiData = gAiLogicData; enum Move *moves = GetMovesArray(battlerDef); @@ -1534,7 +1540,7 @@ bool32 CanTargetFaintAi(u32 battlerDef, u32 battlerAtk) return FALSE; } -u32 NoOfHitsForTargetToFaintBattler(u32 battlerDef, u32 battlerAtk, enum AiConsiderEndure considerEndure) +u32 NoOfHitsForTargetToFaintBattler(enum BattlerId battlerDef, enum BattlerId battlerAtk, enum AiConsiderEndure considerEndure) { u32 currNumberOfHits; u32 leastNumberOfHits = UNKNOWN_NO_OF_HITS; @@ -1551,7 +1557,7 @@ u32 NoOfHitsForTargetToFaintBattler(u32 battlerDef, u32 battlerAtk, enum AiConsi return leastNumberOfHits; } -u32 NoOfHitsForTargetToFaintBattlerWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod) +u32 NoOfHitsForTargetToFaintBattlerWithMod(enum BattlerId battlerDef, enum BattlerId battlerAtk, s32 hpMod) { u32 currNumberOfHits; u32 leastNumberOfHits = UNKNOWN_NO_OF_HITS; @@ -1576,7 +1582,7 @@ u32 NoOfHitsForTargetToFaintBattlerWithMod(u32 battlerDef, u32 battlerAtk, s32 h return leastNumberOfHits; } -void GetBestDmgMovesFromBattler(u32 battlerAtk, u32 battlerDef, enum DamageCalcContext calcContext, enum Move *bestMoves) +void GetBestDmgMovesFromBattler(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum DamageCalcContext calcContext, enum Move *bestMoves) { struct AiLogicData *aiData = gAiLogicData; u32 bestDmg = 0; @@ -1616,7 +1622,7 @@ void GetBestDmgMovesFromBattler(u32 battlerAtk, u32 battlerDef, enum DamageCalcC } } -u32 GetMoveIndex(u32 battler, enum Move move) +u32 GetMoveIndex(enum BattlerId battler, enum Move move) { enum Move *moves = GetMovesArray(battler); @@ -1629,7 +1635,7 @@ u32 GetMoveIndex(u32 battler, enum Move move) return MAX_MON_MOVES; } -bool32 IsBestDmgMove(u32 battlerAtk, u32 battlerDef, enum DamageCalcContext calcContext, enum Move move) +bool32 IsBestDmgMove(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum DamageCalcContext calcContext, enum Move move) { enum Move bestMoves[MAX_MON_MOVES] = {MOVE_NONE}; u32 index = GetMoveIndex(battlerAtk, move); @@ -1648,7 +1654,7 @@ bool32 IsBestDmgMove(u32 battlerAtk, u32 battlerDef, enum DamageCalcContext calc return FALSE; } -bool32 BestDmgMoveHasEffect(u32 battlerAtk, u32 battlerDef, enum DamageCalcContext calcContext, enum BattleMoveEffects moveEffect) +bool32 BestDmgMoveHasEffect(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum DamageCalcContext calcContext, enum BattleMoveEffects moveEffect) { enum Move bestMoves[MAX_MON_MOVES] = {MOVE_NONE}; @@ -1666,7 +1672,7 @@ bool32 BestDmgMoveHasEffect(u32 battlerAtk, u32 battlerDef, enum DamageCalcConte return FALSE; } -u32 GetBestDmgFromBattler(u32 battler, u32 battlerTarget, enum DamageCalcContext calcContext) +u32 GetBestDmgFromBattler(enum BattlerId battler, enum BattlerId battlerTarget, enum DamageCalcContext calcContext) { struct AiLogicData *aiData = gAiLogicData; u32 bestDmg = 0; @@ -1688,7 +1694,7 @@ u32 GetBestDmgFromBattler(u32 battler, u32 battlerTarget, enum DamageCalcContext // Check if AI mon has the means to faint the target with any of its moves. // If numHits > 1, check if the target will be KO'ed by that number of hits (ignoring healing effects) -bool32 CanAIFaintTarget(u32 battlerAtk, u32 battlerDef, u32 numHits) +bool32 CanAIFaintTarget(enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 numHits) { struct AiLogicData *aiData = gAiLogicData; s32 dmg; @@ -1719,7 +1725,7 @@ bool32 CanAIFaintTarget(u32 battlerAtk, u32 battlerDef, u32 numHits) } // Can battler KO the target ignoring any Endure effects (Sturdy, Focus Sash, etc.) -bool32 CanBattlerKOTargetIgnoringSturdy(u32 battlerAtk, u32 battlerDef) +bool32 CanBattlerKOTargetIgnoringSturdy(enum BattlerId battlerAtk, enum BattlerId battlerDef) { struct AiLogicData *aiData = gAiLogicData; s32 dmg; @@ -1738,7 +1744,7 @@ bool32 CanBattlerKOTargetIgnoringSturdy(u32 battlerAtk, u32 battlerDef) return FALSE; } -bool32 CanTargetMoveFaintAi(enum Move move, u32 battlerDef, u32 battlerAtk, u32 nHits) +bool32 CanTargetMoveFaintAi(enum Move move, enum BattlerId battlerDef, enum BattlerId battlerAtk, u32 nHits) { u32 indexSlot = GetMoveSlot(GetMovesArray(battlerDef), move); if (indexSlot < MAX_MON_MOVES) @@ -1751,7 +1757,7 @@ bool32 CanTargetMoveFaintAi(enum Move move, u32 battlerDef, u32 battlerAtk, u32 } // Check if target has means to faint ai mon after modding hp/dmg -bool32 CanTargetFaintAiWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod, s32 dmgMod) +bool32 CanTargetFaintAiWithMod(enum BattlerId battlerDef, enum BattlerId battlerAtk, s32 hpMod, s32 dmgMod) { struct AiLogicData *aiData = gAiLogicData; s32 dmg; @@ -1786,7 +1792,7 @@ bool32 CanTargetFaintAiWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod, s32 dm return FALSE; } -bool32 AI_IsAbilityOnSide(u32 battlerId, enum Ability ability) +bool32 AI_IsAbilityOnSide(enum BattlerId battlerId, enum Ability ability) { if (IsBattlerAlive(battlerId) && gAiLogicData->abilities[battlerId] == ability) return TRUE; @@ -1797,9 +1803,9 @@ bool32 AI_IsAbilityOnSide(u32 battlerId, enum Ability ability) } // does NOT include ability suppression checks -enum Ability AI_DecideKnownAbilityForTurn(u32 battlerId) +enum Ability AI_DecideKnownAbilityForTurn(enum BattlerId battlerId) { - u32 validAbilities[NUM_ABILITY_SLOTS]; + enum Ability validAbilities[NUM_ABILITY_SLOTS]; u8 numValidAbilities = 0; enum Ability knownAbility = GetBattlerAbilityIgnoreMoldBreaker(battlerId); enum Ability indexAbility; @@ -1843,7 +1849,7 @@ enum Ability AI_DecideKnownAbilityForTurn(u32 battlerId) return ABILITY_NONE; // Unknown. } -enum HoldEffect AI_DecideHoldEffectForTurn(u32 battlerId) +enum HoldEffect AI_DecideHoldEffectForTurn(enum BattlerId battlerId) { enum HoldEffect holdEffect = HOLD_EFFECT_NONE; @@ -1868,7 +1874,7 @@ enum HoldEffect AI_DecideHoldEffectForTurn(u32 battlerId) return holdEffect; } -bool32 DoesBattlerIgnoreAbilityChecks(u32 battlerAtk, enum Ability atkAbility, enum Move move) +bool32 DoesBattlerIgnoreAbilityChecks(enum BattlerId battlerAtk, enum Ability atkAbility, enum Move move) { if (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_NEGATE_UNAWARE) return FALSE; // AI handicap flag: doesn't understand ability suppression concept @@ -1900,7 +1906,7 @@ u32 AI_GetWeather(void) return gBattleWeather; } -u32 AI_GetSwitchinWeather(u32 battler) +u32 AI_GetSwitchinWeather(enum BattlerId battler) { enum Ability ability = gBattleMons[battler].ability; // Forced weather behaviour @@ -1942,7 +1948,7 @@ u32 SwitchinChangeBattleTerrain(u32 newTerrain, u32 fieldStatus) return fieldStatus; } -u32 AI_GetSwitchinFieldStatus(u32 battler) +u32 AI_GetSwitchinFieldStatus(enum BattlerId battler) { enum Ability ability = gBattleMons[battler].ability; u32 startingFieldStatus = gFieldStatuses; @@ -2053,7 +2059,7 @@ bool32 IsHazardMove(enum Move move) bool32 IsHazardClearingMove(enum Move move) { // Hazard clearing effects like Rapid Spin, Tidy Up, etc. - u32 moveEffect = GetMoveEffect(move); + enum BattleMoveEffects moveEffect = GetMoveEffect(move); switch (moveEffect) { case EFFECT_RAPID_SPIN: @@ -2063,6 +2069,8 @@ bool32 IsHazardClearingMove(enum Move move) if (GetConfig(CONFIG_DEFOG_EFFECT_CLEARING) >= GEN_6) return TRUE; break; + default: + break; } u32 additionalEffectCount = GetMoveAdditionalEffectCount(move); @@ -2081,7 +2089,7 @@ bool32 IsHazardClearingMove(enum Move move) return FALSE; } -bool32 IsAllyProtectingFromMove(u32 battlerAtk, enum Move attackerMove, enum Move allyMove) +bool32 IsAllyProtectingFromMove(enum BattlerId battlerAtk, enum Move attackerMove, enum Move allyMove) { enum BattleMoveEffects effect = GetMoveEffect(allyMove); @@ -2089,44 +2097,49 @@ bool32 IsAllyProtectingFromMove(u32 battlerAtk, enum Move attackerMove, enum Mov { return FALSE; } - else + + enum ProtectMethod protectMethod = GetMoveProtectMethod(allyMove); + + switch (protectMethod) { - enum ProtectMethod protectMethod = GetMoveProtectMethod(allyMove); - - if (protectMethod == PROTECT_QUICK_GUARD) + case PROTECT_CRAFTY_SHIELD: + if (!IsBattleMoveStatus(attackerMove)) { - u32 priority = GetBattleMovePriority(battlerAtk, gAiLogicData->abilities[battlerAtk], attackerMove); - return (priority > 0); + return FALSE; } - - if (IsBattleMoveStatus(attackerMove)) + else if (GetMoveEffect(attackerMove) == EFFECT_HOLD_HANDS) { - switch (protectMethod) - { - case PROTECT_NORMAL: - case PROTECT_CRAFTY_SHIELD: - case PROTECT_MAX_GUARD: - case PROTECT_WIDE_GUARD: - return TRUE; - - default: - return FALSE; - } + return TRUE; } else { - switch (protectMethod) - { - case PROTECT_CRAFTY_SHIELD: - return FALSE; - default: - return TRUE; - } + u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, attackerMove); + return (GetBattlerSide(battlerAtk) != GetBattlerSide(BATTLE_PARTNER(battlerAtk)) + && moveTarget != TARGET_OPPONENTS_FIELD + && moveTarget != TARGET_ALL_BATTLERS); } + case PROTECT_WIDE_GUARD: + return IsSpreadMove(GetBattlerMoveTargetType(battlerAtk, attackerMove)); + case PROTECT_NORMAL: + case PROTECT_SPIKY_SHIELD: + case PROTECT_MAX_GUARD: + case PROTECT_BANEFUL_BUNKER: + case PROTECT_BURNING_BULWARK: + return TRUE; + case PROTECT_OBSTRUCT: + case PROTECT_SILK_TRAP: + case PROTECT_KINGS_SHIELD: + return !IsBattleMoveStatus(attackerMove); + case PROTECT_QUICK_GUARD: + return (GetChosenMovePriority(battlerAtk, gAiLogicData->abilities[battlerAtk]) > 0); + case PROTECT_MAT_BLOCK: + return !IsBattleMoveStatus(attackerMove); + default: + return FALSE; } } -bool32 IsMoveRedirectionPrevented(u32 battlerAtk, enum Move move, enum Ability atkAbility) +bool32 IsMoveRedirectionPrevented(enum BattlerId battlerAtk, enum Move move, enum Ability atkAbility) { if (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_NEGATE_UNAWARE) return FALSE; @@ -2140,7 +2153,7 @@ bool32 IsMoveRedirectionPrevented(u32 battlerAtk, enum Move move, enum Ability a return FALSE; } -bool32 ShouldTryOHKO(u32 battlerAtk, u32 battlerDef, enum Ability atkAbility, enum Ability defAbility, enum Move move) +bool32 ShouldTryOHKO(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability atkAbility, enum Ability defAbility, enum Move move) { enum HoldEffect holdEffect = gAiLogicData->holdEffects[battlerDef]; u32 accuracy = gAiLogicData->moveAccuracy[battlerAtk][battlerDef][gAiThinkingStruct->movesetIndex]; @@ -2164,7 +2177,7 @@ bool32 ShouldTryOHKO(u32 battlerAtk, u32 battlerDef, enum Ability atkAbility, en else // test the odds { u32 odds = accuracy + (gBattleMons[battlerAtk].level - gBattleMons[battlerDef].level); - if (B_SHEER_COLD_ACC >= GEN_7 && GetMoveEffect(move) == EFFECT_SHEER_COLD && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_ICE)) + if (MoveHasIncreasedAccByTenOnSameType(move) && !IS_BATTLER_OF_TYPE(battlerAtk, GetMoveType(move))) odds -= 10; if (Random() % 100 + 1 < odds && gBattleMons[battlerAtk].level >= gBattleMons[battlerDef].level) return TRUE; @@ -2172,7 +2185,7 @@ bool32 ShouldTryOHKO(u32 battlerAtk, u32 battlerDef, enum Ability atkAbility, en return FALSE; } -bool32 ShouldRaiseAnyStat(u32 battlerAtk, u32 battlerDef) +bool32 ShouldRaiseAnyStat(enum BattlerId battlerAtk, enum BattlerId battlerDef) { if (AreBattlersStatsMaxed(battlerAtk)) return FALSE; @@ -2209,7 +2222,7 @@ bool32 ShouldRaiseAnyStat(u32 battlerAtk, u32 battlerDef) return TRUE; } -bool32 ShouldSetWeather(u32 battler, u32 weather) +bool32 ShouldSetWeather(enum BattlerId battler, u32 weather) { if (AI_GetWeather() & weather) return FALSE; @@ -2217,12 +2230,12 @@ bool32 ShouldSetWeather(u32 battler, u32 weather) return WeatherChecker(battler, weather, FIELD_EFFECT_POSITIVE); } -bool32 ShouldClearWeather(u32 battler, u32 weather) +bool32 ShouldClearWeather(enum BattlerId battler, u32 weather) { return WeatherChecker(battler, weather, FIELD_EFFECT_NEGATIVE); } -bool32 ShouldSetFieldStatus(u32 battler, u32 fieldStatus) +bool32 ShouldSetFieldStatus(enum BattlerId battler, u32 fieldStatus) { if (gFieldStatuses & fieldStatus) { @@ -2236,12 +2249,12 @@ bool32 ShouldSetFieldStatus(u32 battler, u32 fieldStatus) return FieldStatusChecker(battler, fieldStatus, FIELD_EFFECT_POSITIVE); } -bool32 ShouldClearFieldStatus(u32 battler, u32 fieldStatus) +bool32 ShouldClearFieldStatus(enum BattlerId battler, u32 fieldStatus) { return FieldStatusChecker(battler, fieldStatus, FIELD_EFFECT_NEGATIVE); } -bool32 IsBattlerDamagedByStatus(u32 battler) +bool32 IsBattlerDamagedByStatus(enum BattlerId battler) { return gBattleMons[battler].status1 & STATUS1_DAMAGING || gBattleMons[battler].volatiles.wrapped @@ -2253,7 +2266,7 @@ bool32 IsBattlerDamagedByStatus(u32 battler) || gSideStatuses[GetBattlerSide(battler)] & (SIDE_STATUS_SEA_OF_FIRE | SIDE_STATUS_DAMAGE_NON_TYPES); } -s32 ProtectChecks(u32 battlerAtk, u32 battlerDef, enum Move move, enum Move predictedMove) +s32 ProtectChecks(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, enum Move predictedMove) { s32 score = 0; @@ -2307,7 +2320,7 @@ s32 ProtectChecks(u32 battlerAtk, u32 battlerDef, enum Move move, enum Move pred } // stat stages -bool32 CanLowerStat(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData, enum Stat stat) +bool32 CanLowerStat(enum BattlerId battlerAtk, enum BattlerId battlerDef, struct AiLogicData *aiData, enum Stat stat) { if (gBattleMons[battlerDef].statStages[stat] == MIN_STAT_STAGE) return FALSE; @@ -2370,7 +2383,7 @@ bool32 CanLowerStat(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData, return TRUE; } -enum AIScore IncreaseStatDownScore(u32 battlerAtk, u32 battlerDef, enum Stat stat) +enum AIScore IncreaseStatDownScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Stat stat) { enum AIScore tempScore = NO_INCREASE; @@ -2443,7 +2456,7 @@ enum AIScore IncreaseStatDownScore(u32 battlerAtk, u32 battlerDef, enum Stat sta return (tempScore > BEST_EFFECT) ? BEST_EFFECT : tempScore; // don't inflate score so only max +4 } -bool32 BattlerStatCanRise(u32 battler, enum Ability battlerAbility, enum Stat stat) +bool32 BattlerStatCanRise(enum BattlerId battler, enum Ability battlerAbility, enum Stat stat) { if ((gBattleMons[battler].statStages[stat] < MAX_STAT_STAGE && battlerAbility != ABILITY_CONTRARY) || (battlerAbility == ABILITY_CONTRARY && gBattleMons[battler].statStages[stat] > MIN_STAT_STAGE)) @@ -2451,7 +2464,7 @@ bool32 BattlerStatCanRise(u32 battler, enum Ability battlerAbility, enum Stat st return FALSE; } -bool32 AreBattlersStatsMaxed(u32 battlerId) +bool32 AreBattlersStatsMaxed(enum BattlerId battlerId) { for (enum Stat statId = STAT_ATK; statId < NUM_BATTLE_STATS; statId++) { @@ -2461,7 +2474,7 @@ bool32 AreBattlersStatsMaxed(u32 battlerId) return TRUE; } -bool32 AnyStatIsRaised(u32 battlerId) +bool32 AnyStatIsRaised(enum BattlerId battlerId) { for (enum Stat statId = STAT_ATK; statId < NUM_BATTLE_STATS; statId++) { @@ -2471,7 +2484,7 @@ bool32 AnyStatIsRaised(u32 battlerId) return FALSE; } -u32 CountPositiveStatStages(u32 battlerId) +u32 CountPositiveStatStages(enum BattlerId battlerId) { u32 count = 0; for (enum Stat statId = STAT_ATK; statId < NUM_BATTLE_STATS; statId++) @@ -2482,7 +2495,7 @@ u32 CountPositiveStatStages(u32 battlerId) return count; } -u32 CountNegativeStatStages(u32 battlerId) +u32 CountNegativeStatStages(enum BattlerId battlerId) { u32 count = 0; for (enum Stat statId = STAT_ATK; statId < NUM_BATTLE_STATS; statId++) @@ -2493,10 +2506,10 @@ u32 CountNegativeStatStages(u32 battlerId) return count; } -bool32 CanIndexMoveFaintTarget(u32 battlerAtk, u32 battlerDef, u32 moveIndex, enum DamageCalcContext calcContext) +bool32 CanIndexMoveFaintTarget(enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 moveIndex, enum DamageCalcContext calcContext) { s32 dmg; - u16 *moves = gBattleMons[battlerAtk].moves; + enum Move *moves = gBattleMons[battlerAtk].moves; if (IsDoubleBattle() && battlerDef == BATTLE_PARTNER(battlerAtk)) dmg = gAiLogicData->simulatedDmg[battlerAtk][battlerDef][moveIndex].maximum; // Attacking partner, be careful @@ -2508,7 +2521,7 @@ bool32 CanIndexMoveFaintTarget(u32 battlerAtk, u32 battlerDef, u32 moveIndex, en return FALSE; } -enum Move *GetMovesArray(u32 battler) +enum Move *GetMovesArray(enum BattlerId battler) { if (IsAiBattlerAware(battler) || IsAiBattlerAware(BATTLE_PARTNER(battler))) return gBattleMons[battler].moves; @@ -2516,7 +2529,7 @@ enum Move *GetMovesArray(u32 battler) return gBattleHistory->usedMoves[battler]; } -u32 GetBattlerMoveIndexWithEffect(u32 battler, enum BattleMoveEffects effect) +u32 GetBattlerMoveIndexWithEffect(enum BattlerId battler, enum BattleMoveEffects effect) { enum Move *moves = GetMovesArray(battler); for (u32 moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) @@ -2527,7 +2540,7 @@ u32 GetBattlerMoveIndexWithEffect(u32 battler, enum BattleMoveEffects effect) return MAX_MON_MOVES; } -bool32 HasPhysicalBestMove(u32 battlerAtk, u32 battlerDef, enum DamageCalcContext calcContext) +bool32 HasPhysicalBestMove(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum DamageCalcContext calcContext) { enum Move atkBestMoves[MAX_MON_MOVES] = {MOVE_NONE}; GetBestDmgMovesFromBattler(battlerAtk, battlerDef, calcContext, atkBestMoves); @@ -2540,7 +2553,7 @@ bool32 HasPhysicalBestMove(u32 battlerAtk, u32 battlerDef, enum DamageCalcContex } else { - if (GetBattleMoveCategory(atkBestMoves[moveIndex]) == DAMAGE_CATEGORY_SPECIAL) + if (GetBattleMoveCategory(atkBestMoves[moveIndex]) == DAMAGE_CATEGORY_SPECIAL) { bestMoveIsPhysical = FALSE; break; @@ -2550,7 +2563,7 @@ bool32 HasPhysicalBestMove(u32 battlerAtk, u32 battlerDef, enum DamageCalcContex return bestMoveIsPhysical; } -bool32 HasOnlyMovesWithCategory(u32 battlerId, enum DamageCategory category, bool32 onlyOffensive) +bool32 HasOnlyMovesWithCategory(enum BattlerId battlerId, enum DamageCategory category, bool32 onlyOffensive) { enum Move *moves = GetMovesArray(battlerId); @@ -2565,7 +2578,7 @@ bool32 HasOnlyMovesWithCategory(u32 battlerId, enum DamageCategory category, boo return TRUE; } -bool32 HasMoveWithCategory(u32 battler, enum DamageCategory category) +bool32 HasMoveWithCategory(enum BattlerId battler, enum DamageCategory category) { enum Move *moves = GetMovesArray(battler); @@ -2577,7 +2590,7 @@ bool32 HasMoveWithCategory(u32 battler, enum DamageCategory category) return FALSE; } -bool32 HasMoveWithType(u32 battler, enum Type type) +bool32 HasMoveWithType(enum BattlerId battler, enum Type type) { enum Move *moves = GetMovesArray(battler); @@ -2590,7 +2603,7 @@ bool32 HasMoveWithType(u32 battler, enum Type type) return FALSE; } -bool32 HasMoveWithEffect(u32 battler, enum BattleMoveEffects effect) +bool32 HasMoveWithEffect(enum BattlerId battler, enum BattleMoveEffects effect) { enum Move *moves = GetMovesArray(battler); @@ -2603,7 +2616,7 @@ bool32 HasMoveWithEffect(u32 battler, enum BattleMoveEffects effect) return FALSE; } -bool32 HasMoveWithAIEffect(u32 battler, u32 aiEffect) +bool32 HasMoveWithAIEffect(enum BattlerId battler, u32 aiEffect) { enum Move *moves = GetMovesArray(battler); @@ -2619,7 +2632,7 @@ bool32 HasMoveWithAIEffect(u32 battler, u32 aiEffect) return FALSE; } -bool32 HasBattlerSideMoveWithEffect(u32 battler, enum BattleMoveEffects effect) +bool32 HasBattlerSideMoveWithEffect(enum BattlerId battler, enum BattleMoveEffects effect) { if (HasMoveWithEffect(battler, effect)) return TRUE; @@ -2628,7 +2641,7 @@ bool32 HasBattlerSideMoveWithEffect(u32 battler, enum BattleMoveEffects effect) return FALSE; } -bool32 HasBattlerSideMoveWithAIEffect(u32 battler, u32 aiEffect) +bool32 HasBattlerSideMoveWithAIEffect(enum BattlerId battler, u32 aiEffect) { if (HasMoveWithAIEffect(battler, aiEffect)) return TRUE; @@ -2640,7 +2653,7 @@ bool32 HasBattlerSideMoveWithAIEffect(u32 battler, u32 aiEffect) // HasBattlerSideMoveWithEffect checks if the AI knows a side has a move effect, // while HasBattlerSideUsedMoveWithEffect checks if the side has actively USED the move effect. // It matches both on move effect and on AI move effect; eg, EFFECT_HAZE will also bring up Freezy Frost or Clear Smog, anything with AI_EFFECT_RESET_STATS. -bool32 HasBattlerSideUsedMoveWithEffect(u32 battler, enum BattleMoveEffects effect) +bool32 HasBattlerSideUsedMoveWithEffect(enum BattlerId battler, enum BattleMoveEffects effect) { u32 aiEffect = GetAIEffectGroup(effect); for (u32 moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) @@ -2669,7 +2682,7 @@ bool32 HasBattlerSideUsedMoveWithEffect(u32 battler, enum BattleMoveEffects effe return FALSE; } -bool32 HasNonVolatileMoveEffect(u32 battlerId, enum MoveEffect effect) +bool32 HasNonVolatileMoveEffect(enum BattlerId battlerId, enum MoveEffect effect) { enum Move *moves = GetMovesArray(battlerId); @@ -2682,7 +2695,7 @@ bool32 HasNonVolatileMoveEffect(u32 battlerId, enum MoveEffect effect) return FALSE; } -bool32 IsPowerBasedOnStatus(u32 battlerId, enum BattleMoveEffects effect, u32 argument) +bool32 IsPowerBasedOnStatus(enum BattlerId battlerId, enum BattleMoveEffects effect, u32 argument) { enum Move *moves = GetMovesArray(battlerId); @@ -2697,7 +2710,7 @@ bool32 IsPowerBasedOnStatus(u32 battlerId, enum BattleMoveEffects effect, u32 ar return FALSE; } -bool32 HasMoveWithAdditionalEffect(u32 battlerId, enum MoveEffect moveEffect) +bool32 HasMoveWithAdditionalEffect(enum BattlerId battlerId, enum MoveEffect moveEffect) { enum Move *moves = GetMovesArray(battlerId); @@ -2711,7 +2724,7 @@ bool32 HasMoveWithAdditionalEffect(u32 battlerId, enum MoveEffect moveEffect) return FALSE; } -bool32 HasBattlerSideMoveWithAdditionalEffect(u32 battler, enum MoveEffect moveEffect) +bool32 HasBattlerSideMoveWithAdditionalEffect(enum BattlerId battler, enum MoveEffect moveEffect) { if (HasMoveWithAdditionalEffect(battler, moveEffect)) return TRUE; @@ -2720,7 +2733,7 @@ bool32 HasBattlerSideMoveWithAdditionalEffect(u32 battler, enum MoveEffect moveE return FALSE; } -bool32 HasMoveWithCriticalHitChance(u32 battlerId) +bool32 HasMoveWithCriticalHitChance(enum BattlerId battlerId) { enum Move *moves = GetMovesArray(battlerId); @@ -2734,7 +2747,7 @@ bool32 HasMoveWithCriticalHitChance(u32 battlerId) return FALSE; } -bool32 HasMoveWithMoveEffectExcept(u32 battlerId, enum MoveEffect moveEffect, enum BattleMoveEffects exception) +bool32 HasMoveWithMoveEffectExcept(enum BattlerId battlerId, enum MoveEffect moveEffect, enum BattleMoveEffects exception) { enum Move *moves = GetMovesArray(battlerId); @@ -2749,7 +2762,7 @@ bool32 HasMoveWithMoveEffectExcept(u32 battlerId, enum MoveEffect moveEffect, en return FALSE; } -bool32 HasMove(u32 battlerId, enum Move move) +bool32 HasMove(enum BattlerId battlerId, enum Move move) { enum Move *moves = GetMovesArray(battlerId); @@ -2762,7 +2775,7 @@ bool32 HasMove(u32 battlerId, enum Move move) return FALSE; } -bool32 HasAnyKnownMove(u32 battlerId) +bool32 HasAnyKnownMove(enum BattlerId battlerId) { enum Move *moves = GetMovesArray(battlerId); @@ -2775,10 +2788,11 @@ bool32 HasAnyKnownMove(u32 battlerId) return FALSE; } -bool32 HasMoveThatLowersOwnStats(u32 battlerId) +bool32 HasMoveThatLowersOwnStats(enum BattlerId battlerId) { enum Move aiMove; enum Move *moves = GetMovesArray(battlerId); + for (u32 moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) { aiMove = moves[moveIndex]; @@ -2796,10 +2810,11 @@ bool32 HasMoveThatLowersOwnStats(u32 battlerId) return FALSE; } -bool32 HasMoveThatRaisesOwnStats(u32 battlerId) +bool32 HasMoveThatRaisesOwnStats(enum BattlerId battlerId) { enum Move aiMove; enum Move *moves = GetMovesArray(battlerId); + for (u32 moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) { aiMove = moves[moveIndex]; @@ -2817,7 +2832,7 @@ bool32 HasMoveThatRaisesOwnStats(u32 battlerId) return FALSE; } -bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool32 ignoreStatus) +bool32 HasMoveWithLowAccuracy(enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 accCheck, bool32 ignoreStatus) { enum Move *moves = GetMovesArray(battlerAtk); u32 moveLimitations = gAiLogicData->moveLimitations[battlerAtk]; @@ -2844,7 +2859,7 @@ bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool return FALSE; } -bool32 HasSleepMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef) +bool32 HasSleepMoveWithLowAccuracy(enum BattlerId battlerAtk, enum BattlerId battlerDef) { enum Move *moves = GetMovesArray(battlerAtk); u32 moveLimitations = gAiLogicData->moveLimitations[battlerAtk]; @@ -2861,7 +2876,7 @@ bool32 HasSleepMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef) return FALSE; } -bool32 HasHealingEffect(u32 battlerId) +bool32 HasHealingEffect(enum BattlerId battlerId) { enum Move *moves = GetMovesArray(battlerId); @@ -2888,7 +2903,7 @@ bool32 IsTrappingMove(enum Move move) } } -bool32 HasTrappingMoveEffect(u32 battler) +bool32 HasTrappingMoveEffect(enum BattlerId battler) { enum Move *moves = GetMovesArray(battler); @@ -2901,7 +2916,7 @@ bool32 HasTrappingMoveEffect(u32 battler) return FALSE; } -bool32 HasThawingMove(u32 battler) +bool32 HasThawingMove(enum BattlerId battler) { enum Move *moves = GetMovesArray(battler); for (u32 moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) @@ -2912,7 +2927,7 @@ bool32 HasThawingMove(u32 battler) return FALSE; } -bool32 HasUsableWhileAsleepMove(u32 battler) +bool32 HasUsableWhileAsleepMove(enum BattlerId battler) { enum Move *moves = GetMovesArray(battler); for (u32 moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) @@ -3162,6 +3177,8 @@ static inline bool32 IsMoveSleepClauseTrigger(enum Move move) { case MOVE_EFFECT_SLEEP: return TRUE; + default: + break; } // Sleeping effects like G-Max Befuddle, G-Max Snooze, etc. @@ -3182,7 +3199,7 @@ static inline bool32 IsMoveSleepClauseTrigger(enum Move move) return FALSE; } -bool32 HasDamagingMove(u32 battler) +bool32 HasDamagingMove(enum BattlerId battler) { enum Move *moves = GetMovesArray(battler); @@ -3195,7 +3212,7 @@ bool32 HasDamagingMove(u32 battler) return FALSE; } -bool32 HasDamagingMoveOfType(u32 battler, enum Type type) +bool32 HasDamagingMoveOfType(enum BattlerId battler, enum Type type) { enum Move *moves = GetMovesArray(battler); @@ -3209,7 +3226,7 @@ bool32 HasDamagingMoveOfType(u32 battler, enum Type type) return TRUE; if (GetMoveType(moves[moveIndex]) == type) return TRUE; - if (GetMoveEffect(moves[moveIndex]) == EFFECT_NATURE_POWER && GetMoveType(GetNaturePowerMove(moves[moveIndex])) == type) + if (GetMoveEffect(moves[moveIndex]) == EFFECT_NATURE_POWER && GetMoveType(GetNaturePowerMove()) == type) return TRUE; } } @@ -3217,7 +3234,7 @@ bool32 HasDamagingMoveOfType(u32 battler, enum Type type) return FALSE; } -bool32 HasMoveWithFlag(u32 battler, MoveFlag getFlag) +bool32 HasMoveWithFlag(enum BattlerId battler, MoveFlag getFlag) { enum Move *moves = GetMovesArray(battler); u32 moveLimitations = gAiLogicData->moveLimitations[battler]; @@ -3233,7 +3250,7 @@ bool32 HasMoveWithFlag(u32 battler, MoveFlag getFlag) return FALSE; } -bool32 IsTwoTurnNotSemiInvulnerableMove(u32 battlerAtk, enum Move move) +bool32 IsTwoTurnNotSemiInvulnerableMove(enum BattlerId battlerAtk, enum Move move) { switch (GetMoveEffect(move)) { @@ -3246,7 +3263,7 @@ bool32 IsTwoTurnNotSemiInvulnerableMove(u32 battlerAtk, enum Move move) } } -static u32 GetLeechSeedDamage(u32 battler) +static u32 GetLeechSeedDamage(enum BattlerId battler) { u32 damage = 0; u32 leechSeeder = gBattleMons[battler].volatiles.leechSeed; @@ -3259,7 +3276,7 @@ static u32 GetLeechSeedDamage(u32 battler) return damage; } -static u32 GetNightmareDamage(u32 battlerId) +static u32 GetNightmareDamage(enum BattlerId battlerId) { u32 damage = 0; if (gBattleMons[battlerId].volatiles.nightmare @@ -3273,7 +3290,7 @@ static u32 GetNightmareDamage(u32 battlerId) return damage; } -static u32 GetCurseDamage(u32 battlerId) +static u32 GetCurseDamage(enum BattlerId battlerId) { u32 damage = 0; if (gBattleMons[battlerId].volatiles.cursed) @@ -3285,7 +3302,7 @@ static u32 GetCurseDamage(u32 battlerId) return damage; } -static u32 GetTrapDamage(u32 battler) +static u32 GetTrapDamage(enum BattlerId battler) { // ai has no knowledge about turns remaining u32 damage = 0; @@ -3302,7 +3319,7 @@ static u32 GetTrapDamage(u32 battler) return damage; } -static u32 GetPoisonDamage(u32 battlerId) +static u32 GetPoisonDamage(enum BattlerId battlerId) { u32 damage = 0; @@ -3328,7 +3345,7 @@ static u32 GetPoisonDamage(u32 battlerId) return damage; } -static bool32 BattlerAffectedBySandstorm(u32 battlerId, enum Ability ability) +static bool32 BattlerAffectedBySandstorm(enum BattlerId battlerId, enum Ability ability) { if (!IS_BATTLER_ANY_TYPE(battlerId, TYPE_ROCK, TYPE_GROUND, TYPE_STEEL) && ability != ABILITY_SAND_VEIL @@ -3339,7 +3356,7 @@ static bool32 BattlerAffectedBySandstorm(u32 battlerId, enum Ability ability) return FALSE; } -static bool32 BattlerAffectedByHail(u32 battlerId, enum Ability ability) +static bool32 BattlerAffectedByHail(enum BattlerId battlerId, enum Ability ability) { if (!IS_BATTLER_OF_TYPE(battlerId, TYPE_ICE) && ability != ABILITY_SNOW_CLOAK @@ -3349,7 +3366,7 @@ static bool32 BattlerAffectedByHail(u32 battlerId, enum Ability ability) return FALSE; } -static u32 GetWeatherDamage(u32 battlerId) +static u32 GetWeatherDamage(enum BattlerId battlerId) { enum Ability ability = gAiLogicData->abilities[battlerId]; enum HoldEffect holdEffect = gAiLogicData->holdEffects[battlerId]; @@ -3385,7 +3402,7 @@ static u32 GetWeatherDamage(u32 battlerId) return damage; } -u32 GetBattlerSecondaryDamage(u32 battlerId) +u32 GetBattlerSecondaryDamage(enum BattlerId battlerId) { u32 secondaryDamage; @@ -3402,7 +3419,7 @@ u32 GetBattlerSecondaryDamage(u32 battlerId) return secondaryDamage; } -bool32 BattlerWillFaintFromWeather(u32 battler, enum Ability ability) +bool32 BattlerWillFaintFromWeather(enum BattlerId battler, enum Ability ability) { if ((BattlerAffectedBySandstorm(battler, ability) || BattlerAffectedByHail(battler, ability)) && gBattleMons[battler].hp <= max(1, gBattleMons[battler].maxHP / 16)) @@ -3411,7 +3428,7 @@ bool32 BattlerWillFaintFromWeather(u32 battler, enum Ability ability) return FALSE; } -bool32 BattlerWillFaintFromSecondaryDamage(u32 battler, enum Ability ability) +bool32 BattlerWillFaintFromSecondaryDamage(enum BattlerId battler, enum Ability ability) { if (GetBattlerSecondaryDamage(battler) != 0 && gBattleMons[battler].hp <= max(1, gBattleMons[battler].maxHP / 16)) @@ -3419,7 +3436,7 @@ bool32 BattlerWillFaintFromSecondaryDamage(u32 battler, enum Ability ability) return FALSE; } -bool32 AnyUsefulStatIsRaised(u32 battler) +bool32 AnyUsefulStatIsRaised(enum BattlerId battler) { for (enum Stat statId = STAT_ATK; statId < NUM_BATTLE_STATS; statId++) { @@ -3446,9 +3463,9 @@ bool32 AnyUsefulStatIsRaised(u32 battler) return FALSE; } -bool32 BattlerHasMaxHPProtection(u32 battler) +bool32 BattlerHasMaxHPProtection(enum BattlerId battler) { - u32 ability = gAiLogicData->abilities[battler]; + enum Ability ability = gAiLogicData->abilities[battler]; if (!AI_BattlerAtMaxHp(battler)) return FALSE; if (gAiLogicData->holdEffects[battler] == HOLD_EFFECT_FOCUS_SASH) @@ -3460,7 +3477,7 @@ bool32 BattlerHasMaxHPProtection(u32 battler) return FALSE; } -enum AIPivot ShouldPivot(u32 battlerAtk, u32 battlerDef, enum Move move) +enum AIPivot ShouldPivot(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move) { enum Move predictedMoveSpeedCheck = GetIncomingMoveSpeedCheck(battlerAtk, battlerDef, gAiLogicData); bool32 aiIsFaster = AI_IsFaster(battlerAtk, battlerDef, move, predictedMoveSpeedCheck, CONSIDER_PRIORITY); @@ -3495,7 +3512,7 @@ enum AIPivot ShouldPivot(u32 battlerAtk, u32 battlerDef, enum Move move) #define BATTLE_TYPE_CANT_KNOCK_OFF (BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_LINK \ | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_SECRET_BASE \ | (B_TRAINERS_KNOCK_OFF_ITEMS == TRUE ? BATTLE_TYPE_TRAINER : 0)) -bool32 CanKnockOffItem(u32 fromBattler, u32 battler, enum Item item) +bool32 CanKnockOffItem(enum BattlerId fromBattler, enum BattlerId battler, enum Item item) { if (item == ITEM_NONE) return FALSE; @@ -3514,7 +3531,7 @@ bool32 CanKnockOffItem(u32 fromBattler, u32 battler, enum Item item) #undef BATTLE_TYPE_CANT_KNOCK_OFF // status checks -bool32 IsBattlerIncapacitated(u32 battler, enum Ability ability) +bool32 IsBattlerIncapacitated(enum BattlerId battler, enum Ability ability) { if ((gBattleMons[battler].status1 & STATUS1_FREEZE) && !HasThawingMove(battler)) return TRUE; // if battler has thawing move we assume they will definitely use it, and thus being frozen should be neglected @@ -3528,7 +3545,7 @@ bool32 IsBattlerIncapacitated(u32 battler, enum Ability ability) return FALSE; } -bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, enum Ability defAbility, enum Move move, enum Move partnerMove) +bool32 AI_CanPutToSleep(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability defAbility, enum Move move, enum Move partnerMove) { if (!CanBeSlept(battlerAtk, battlerDef, defAbility, BLOCKED_BY_SLEEP_CLAUSE) || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) @@ -3537,7 +3554,7 @@ bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, enum Ability defAbility, return TRUE; } -static inline bool32 DoesBattlerBenefitFromAllVolatileStatus(u32 battler, enum Ability ability) +static inline bool32 DoesBattlerBenefitFromAllVolatileStatus(enum BattlerId battler, enum Ability ability) { if (ability == ABILITY_MARVEL_SCALE || ability == ABILITY_QUICK_FEET @@ -3549,7 +3566,7 @@ static inline bool32 DoesBattlerBenefitFromAllVolatileStatus(u32 battler, enum A return FALSE; } -bool32 ShouldPoison(u32 battlerAtk, u32 battlerDef) +bool32 ShouldPoison(enum BattlerId battlerAtk, enum BattlerId battlerDef) { enum Ability abilityDef = gAiLogicData->abilities[battlerDef]; // Battler can be poisoned and has move/ability that synergizes with being poisoned @@ -3569,7 +3586,7 @@ bool32 ShouldPoison(u32 battlerAtk, u32 battlerDef) return TRUE; } -bool32 ShouldBurn(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef) +bool32 ShouldBurn(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityDef) { // Battler can be burned and has move/ability that synergizes with being burned if (CanBeBurned(battlerAtk, battlerDef, abilityDef) && ( @@ -3589,7 +3606,7 @@ bool32 ShouldBurn(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef) return TRUE; } -bool32 ShouldFreezeOrFrostbite(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef) +bool32 ShouldFreezeOrFrostbite(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityDef) { if (!B_USE_FROSTBITE) { @@ -3621,7 +3638,7 @@ bool32 ShouldFreezeOrFrostbite(u32 battlerAtk, u32 battlerDef, enum Ability abil } } -bool32 ShouldParalyze(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef) +bool32 ShouldParalyze(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityDef) { // Battler can be paralyzed and has move/ability that synergizes with being paralyzed if (CanBeParalyzed(battlerAtk, battlerDef, abilityDef) && ( @@ -3638,7 +3655,7 @@ bool32 ShouldParalyze(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef) return TRUE; } -bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, enum Ability defAbility, enum Move move, enum Move partnerMove) +bool32 AI_CanPoison(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability defAbility, enum Move move, enum Move partnerMove) { if (!CanBePoisoned(battlerAtk, battlerDef, gAiLogicData->abilities[battlerAtk], defAbility) || gAiLogicData->effectiveness[battlerAtk][battlerDef][gAiThinkingStruct->movesetIndex] == UQ_4_12(0.0) @@ -3649,7 +3666,7 @@ bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, enum Ability defAbility, enu return TRUE; } -bool32 AI_CanParalyze(u32 battlerAtk, u32 battlerDef, enum Ability defAbility, enum Move move, enum Move partnerMove) +bool32 AI_CanParalyze(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability defAbility, enum Move move, enum Move partnerMove) { if (!CanBeParalyzed(battlerAtk, battlerDef, defAbility) || gAiLogicData->effectiveness[battlerAtk][battlerDef][gAiThinkingStruct->movesetIndex] == UQ_4_12(0.0) @@ -3659,7 +3676,7 @@ bool32 AI_CanParalyze(u32 battlerAtk, u32 battlerDef, enum Ability defAbility, e return TRUE; } -bool32 AI_CanBeConfused(u32 battlerAtk, u32 battlerDef, enum Move move, enum Ability abilityDef) +bool32 AI_CanBeConfused(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, enum Ability abilityDef) { if (gBattleMons[battlerDef].volatiles.confusionTurns > 0 || (abilityDef == ABILITY_OWN_TEMPO && !DoesBattlerIgnoreAbilityChecks(battlerAtk, gAiLogicData->abilities[battlerAtk], move)) @@ -3670,7 +3687,7 @@ bool32 AI_CanBeConfused(u32 battlerAtk, u32 battlerDef, enum Move move, enum Abi return TRUE; } -bool32 AI_CanConfuse(u32 battlerAtk, u32 battlerDef, enum Ability defAbility, u32 battlerAtkPartner, enum Move move, enum Move partnerMove) +bool32 AI_CanConfuse(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability defAbility, enum BattlerId battlerAtkPartner, enum Move move, enum Move partnerMove) { if (AI_GetBattlerMoveTargetType(battlerAtk, move) == TARGET_FOES_AND_ALLY && AI_CanBeConfused(battlerAtk, battlerDef, move, defAbility) @@ -3684,7 +3701,7 @@ bool32 AI_CanConfuse(u32 battlerAtk, u32 battlerDef, enum Ability defAbility, u3 return TRUE; } -bool32 AI_CanBurn(u32 battlerAtk, u32 battlerDef, enum Ability defAbility, u32 battlerAtkPartner, enum Move move, enum Move partnerMove) +bool32 AI_CanBurn(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability defAbility, enum BattlerId battlerAtkPartner, enum Move move, enum Move partnerMove) { if (!CanBeBurned(battlerAtk, battlerDef, defAbility) || gAiLogicData->effectiveness[battlerAtk][battlerDef][gAiThinkingStruct->movesetIndex] == UQ_4_12(0.0) @@ -3696,7 +3713,7 @@ bool32 AI_CanBurn(u32 battlerAtk, u32 battlerDef, enum Ability defAbility, u32 b return TRUE; } -bool32 AI_CanGiveFrostbite(u32 battlerAtk, u32 battlerDef, enum Ability defAbility, u32 battlerAtkPartner, enum Move move, enum Move partnerMove) +bool32 AI_CanGiveFrostbite(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability defAbility, enum BattlerId battlerAtkPartner, enum Move move, enum Move partnerMove) { if (!CanBeFrozen(battlerAtk, battlerDef, defAbility) || gAiLogicData->effectiveness[battlerAtk][battlerDef][gAiThinkingStruct->movesetIndex] == UQ_4_12(0.0) @@ -3708,7 +3725,7 @@ bool32 AI_CanGiveFrostbite(u32 battlerAtk, u32 battlerDef, enum Ability defAbili return TRUE; } -bool32 AI_CanBeInfatuated(u32 battlerAtk, u32 battlerDef, enum Ability defAbility) +bool32 AI_CanBeInfatuated(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability defAbility) { if (gBattleMons[battlerDef].volatiles.infatuation || gAiLogicData->effectiveness[battlerAtk][battlerDef][gAiThinkingStruct->movesetIndex] == UQ_4_12(0.0) @@ -3719,7 +3736,7 @@ bool32 AI_CanBeInfatuated(u32 battlerAtk, u32 battlerDef, enum Ability defAbilit return TRUE; } -bool32 ShouldTryToFlinch(u32 battlerAtk, u32 battlerDef, enum Ability atkAbility, enum Ability defAbility, enum Move move) +bool32 ShouldTryToFlinch(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability atkAbility, enum Ability defAbility, enum Move move) { enum Move predictedMoveSpeedCheck = GetIncomingMoveSpeedCheck(battlerAtk, battlerDef, gAiLogicData); if (((!IsMoldBreakerTypeAbility(battlerAtk, gAiLogicData->abilities[battlerAtk]) && (defAbility == ABILITY_SHIELD_DUST || defAbility == ABILITY_INNER_FOCUS)) @@ -3741,7 +3758,7 @@ bool32 ShouldTryToFlinch(u32 battlerAtk, u32 battlerDef, enum Ability atkAbility return FALSE; // don't try to flinch } -bool32 ShouldTrap(u32 battlerAtk, u32 battlerDef, enum Move move) +bool32 ShouldTrap(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move) { if (AI_CanBattlerEscape(battlerDef)) return FALSE; @@ -3761,7 +3778,7 @@ bool32 ShouldTrap(u32 battlerAtk, u32 battlerDef, enum Move move) return FALSE; } -bool32 IsFlinchGuaranteed(u32 battlerAtk, u32 battlerDef, enum Move move) +bool32 IsFlinchGuaranteed(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move) { if (!MoveHasAdditionalEffect(move, MOVE_EFFECT_FLINCH)) return FALSE; @@ -3793,7 +3810,7 @@ bool32 IsFlinchGuaranteed(u32 battlerAtk, u32 battlerDef, enum Move move) return FALSE; } -bool32 HasChoiceEffect(u32 battler) +bool32 HasChoiceEffect(enum BattlerId battler) { enum Ability ability = gAiLogicData->abilities[battler]; if (ability == ABILITY_GORILLA_TACTICS) @@ -3814,27 +3831,24 @@ bool32 HasChoiceEffect(u32 battler) } } -static u32 FindMoveUsedXTurnsAgo(u32 battlerId, u32 x) +bool32 IsWakeupTurn(enum BattlerId battler) { - s32 index = gBattleHistory->moveHistoryIndex[battlerId]; - for (u32 turnsAgo = 0; turnsAgo < x; turnsAgo++) - { - if (--index < 0) - index = AI_MOVE_HISTORY_COUNT - 1; - } - return gBattleHistory->moveHistory[battlerId][index]; -} + u32 sleepTurns = gBattleMons[battler].status1 & STATUS1_SLEEP; + u32 toSub; -bool32 IsWakeupTurn(u32 battler) -{ - // Check if rest was used 2 turns ago - if ((gBattleMons[battler].status1 & STATUS1_SLEEP) == 1 && GetMoveEffect(FindMoveUsedXTurnsAgo(battler, 2)) == EFFECT_REST) - return TRUE; - else // no way to know + if (sleepTurns == 0) return FALSE; + + // Early Bird reduces the sleep timer twice as fast. + if (gAiLogicData->abilities[battler] == ABILITY_EARLY_BIRD) + toSub = 2; + else + toSub = 1; + + return sleepTurns <= toSub; } -bool32 AnyPartyMemberStatused(u32 battlerId, bool32 checkSoundproof) +bool32 AnyPartyMemberStatused(enum BattlerId battlerId, bool32 checkSoundproof) { struct Pokemon *party; u32 battlerOnField1, battlerOnField2; @@ -3884,7 +3898,7 @@ bool32 AnyPartyMemberStatused(u32 battlerId, bool32 checkSoundproof) return hasStatusToCure; } -bool32 ShouldUseRecoilMove(u32 battlerAtk, u32 battlerDef, u32 recoilDmg, u32 moveIndex) +bool32 ShouldUseRecoilMove(enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 recoilDmg, u32 moveIndex) { if (recoilDmg >= gBattleMons[battlerAtk].hp //Recoil kills attacker && CountUsablePartyMons(battlerDef) != 0) //Foe has more than 1 target left @@ -3898,7 +3912,7 @@ bool32 ShouldUseRecoilMove(u32 battlerAtk, u32 battlerDef, u32 recoilDmg, u32 mo return TRUE; } -static inline bool32 RecoveryEnablesWinning1v1(u32 battlerAtk, u32 battlerDef, enum Move move, u32 aiIsFaster, u32 healAmount) +static inline bool32 RecoveryEnablesWinning1v1(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, bool32 aiIsFaster, u32 healAmount) { if (aiIsFaster) { @@ -3920,7 +3934,7 @@ static inline bool32 RecoveryEnablesWinning1v1(u32 battlerAtk, u32 battlerDef, e return FALSE; } -static inline bool32 ShouldDrainHPToWithstandHit(u32 battlerAtk, u32 battlerDef, u32 currHP, u32 healAmount) +static inline bool32 ShouldDrainHPToWithstandHit(enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 currHP, u32 healAmount) { s32 bestDamageFromPlayer = GetBestDmgFromBattler(battlerDef, battlerAtk, AI_DEFENDING); @@ -3933,7 +3947,7 @@ static inline bool32 ShouldDrainHPToWithstandHit(u32 battlerAtk, u32 battlerDef, return FALSE; } -bool32 ShouldAbsorb(u32 battlerAtk, u32 battlerDef, enum Move move) +bool32 ShouldAbsorb(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move) { u32 maxHP = gBattleMons[battlerAtk].maxHP; u32 currHP = gBattleMons[battlerAtk].hp; @@ -3960,7 +3974,7 @@ bool32 ShouldAbsorb(u32 battlerAtk, u32 battlerDef, enum Move move) return FALSE; } -bool32 ShouldRecover(u32 battlerAtk, u32 battlerDef, enum Move move, u32 healPercent) +bool32 ShouldRecover(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, u32 healPercent) { u32 maxHP = gBattleMons[battlerAtk].maxHP; u32 currHP = gBattleMons[battlerAtk].hp; @@ -3979,9 +3993,9 @@ bool32 ShouldRecover(u32 battlerAtk, u32 battlerDef, enum Move move, u32 healPer return FALSE; } -bool32 ShouldSetScreen(u32 battlerAtk, u32 battlerDef, enum BattleMoveEffects moveEffect) +bool32 ShouldSetScreen(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum BattleMoveEffects moveEffect) { - u32 atkSide = GetBattlerSide(battlerAtk); + enum BattleSide atkSide = GetBattlerSide(battlerAtk); // Don't waste a turn if screens will be broken if (HasMoveWithAIEffect(battlerDef, AI_EFFECT_BREAK_SCREENS)) @@ -4014,7 +4028,7 @@ bool32 ShouldSetScreen(u32 battlerAtk, u32 battlerDef, enum BattleMoveEffects mo return FALSE; } -static bool32 ShouldCureStatusInternal(u32 battlerAtk, u32 battlerDef, bool32 usingItem, struct AiLogicData *aiData) +static bool32 ShouldCureStatusInternal(enum BattlerId battlerAtk, enum BattlerId battlerDef, bool32 usingItem, struct AiLogicData *aiData) { bool32 targetingSelf = (battlerAtk == battlerDef); bool32 targetingAlly = IsTargetingPartner(battlerAtk, battlerDef); @@ -4100,12 +4114,12 @@ static bool32 ShouldCureStatusInternal(u32 battlerAtk, u32 battlerDef, bool32 us } } -bool32 ShouldCureStatus(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData) +bool32 ShouldCureStatus(enum BattlerId battlerAtk, enum BattlerId battlerDef, struct AiLogicData *aiData) { return ShouldCureStatusInternal(battlerAtk, battlerDef, FALSE, aiData); } -bool32 ShouldCureStatusWithItem(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData) +bool32 ShouldCureStatusWithItem(enum BattlerId battlerAtk, enum BattlerId battlerDef, struct AiLogicData *aiData) { return ShouldCureStatusInternal(battlerAtk, battlerDef, TRUE, aiData); } @@ -4114,13 +4128,13 @@ bool32 ShouldCureStatusWithItem(u32 battlerAtk, u32 battlerDef, struct AiLogicDa bool32 IsBattle1v1(void) { if (IsDoubleBattle() - && ((IsBattlerAlive(B_POSITION_PLAYER_LEFT) && IsBattlerAlive(B_POSITION_PLAYER_RIGHT)) - || (IsBattlerAlive(B_POSITION_OPPONENT_LEFT) && IsBattlerAlive(B_POSITION_OPPONENT_RIGHT)))) + && ((IsBattlerAlive(GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)) && IsBattlerAlive(GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT))) + || (IsBattlerAlive(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT)) && IsBattlerAlive(GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT))))) return FALSE; return TRUE; } -bool32 HasTwoOpponents(u32 battler) +bool32 HasTwoOpponents(enum BattlerId battler) { if (IsDoubleBattle() && IsBattlerAlive(LEFT_FOE(battler)) && IsBattlerAlive(RIGHT_FOE(battler))) @@ -4128,7 +4142,7 @@ bool32 HasTwoOpponents(u32 battler) return FALSE; } -bool32 HasPartner(u32 battler) +bool32 HasPartner(enum BattlerId battler) { if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battler))) { @@ -4140,7 +4154,7 @@ bool32 HasPartner(u32 battler) return FALSE; } -bool32 HasPartnerIgnoreFlags(u32 battler) +bool32 HasPartnerIgnoreFlags(enum BattlerId battler) { if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battler))) { @@ -4149,31 +4163,31 @@ bool32 HasPartnerIgnoreFlags(u32 battler) return FALSE; } -bool32 IsTargetingPartner(u32 battlerAtk, u32 battlerDef) +bool32 IsTargetingPartner(enum BattlerId battlerAtk, enum BattlerId battlerDef) { if (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_ATTACKS_PARTNER) return FALSE; return ((battlerAtk) == (battlerDef ^ BIT_FLANK)); } -enum Move GetAllyChosenMove(u32 battlerId) +enum Move GetAllyChosenMove(enum BattlerId battlerId) { - u32 partnerBattler = BATTLE_PARTNER(battlerId); + enum BattlerId partnerBattler = BATTLE_PARTNER(battlerId); if (!IsBattlerAlive(partnerBattler) || !IsAiBattlerAware(partnerBattler)) return MOVE_NONE; else if (partnerBattler > battlerId) // Battler with the lower id chooses the move first. return gAiLogicData->lastUsedMove[partnerBattler]; else - return GetChosenMoveFromPosition(partnerBattler); + return GetBattlerChosenMove(partnerBattler); } -bool32 AreMovesEquivalent(u32 battlerAtk, u32 battlerAtkPartner, enum Move move, enum Move partnerMove) +bool32 AreMovesEquivalent(enum BattlerId battlerAtk, enum BattlerId battlerAtkPartner, enum Move move, enum Move partnerMove) { if (!IsBattlerAlive(battlerAtkPartner) || partnerMove == MOVE_NONE) return FALSE; - u32 battlerDef = gBattleStruct->moveTarget[battlerAtk]; + enum BattlerId battlerDef = gBattleStruct->moveTarget[battlerAtk]; // We don't care the effect is basically the same; we would use this move anyway. if (IsBestDmgMove(battlerAtk, battlerDef, AI_ATTACKING, move)) @@ -4262,7 +4276,7 @@ static u32 GetAIEffectGroup(enum BattleMoveEffects effect) return aiEffect; } -static u32 GetAIEffectGroupFromMove(u32 battler, enum Move move) +static u32 GetAIEffectGroupFromMove(enum BattlerId battler, enum Move move) { u32 aiEffect = GetAIEffectGroup(GetMoveEffect(move)); @@ -4317,7 +4331,7 @@ static u32 GetAIEffectGroupFromMove(u32 battler, enum Move move) } // It matches both on move effect and on AI move effect; eg, EFFECT_HAZE will also bring up Freezy Frost or Clear Smog, anything with AI_EFFECT_RESET_STATS. -bool32 DoesPartnerHaveSameMoveEffect(u32 battlerAtkPartner, u32 battlerDef, enum Move move, enum Move partnerMove) +bool32 DoesPartnerHaveSameMoveEffect(enum BattlerId battlerAtkPartner, enum BattlerId battlerDef, enum Move move, enum Move partnerMove) { if (!HasPartner(battlerAtkPartner)) return FALSE; @@ -4335,13 +4349,13 @@ bool32 DoesPartnerHaveSameMoveEffect(u32 battlerAtkPartner, u32 battlerDef, enum } //PARTNER_MOVE_EFFECT_IS_STATUS_SAME_TARGET -bool32 PartnerMoveEffectIsStatusSameTarget(u32 battlerAtkPartner, u32 battlerDef, enum Move partnerMove) +bool32 PartnerMoveEffectIsStatusSameTarget(enum BattlerId battlerAtkPartner, enum BattlerId battlerDef, enum Move partnerMove) { if (!HasPartner(battlerAtkPartner)) return FALSE; enum BattleMoveEffects partnerEffect = GetMoveEffect(partnerMove); - u32 nonVolatileStatus = GetMoveNonVolatileStatus(partnerMove); + enum MoveEffect nonVolatileStatus = GetMoveNonVolatileStatus(partnerMove); if (partnerMove != MOVE_NONE && gBattleStruct->moveTarget[battlerAtkPartner] == battlerDef && (nonVolatileStatus == MOVE_EFFECT_POISON @@ -4355,7 +4369,7 @@ bool32 PartnerMoveEffectIsStatusSameTarget(u32 battlerAtkPartner, u32 battlerDef } //PARTNER_MOVE_EFFECT_IS -bool32 PartnerMoveEffectIs(u32 battlerAtkPartner, enum Move partnerMove, enum BattleMoveEffects effectCheck) +bool32 PartnerMoveEffectIs(enum BattlerId battlerAtkPartner, enum Move partnerMove, enum BattleMoveEffects effectCheck) { if (!HasPartner(battlerAtkPartner)) return FALSE; @@ -4367,7 +4381,7 @@ bool32 PartnerMoveEffectIs(u32 battlerAtkPartner, enum Move partnerMove, enum Ba } //PARTNER_MOVE_IS_TAILWIND_TRICKROOM -bool32 PartnerMoveIs(u32 battlerAtkPartner, enum Move partnerMove, enum Move moveCheck) +bool32 PartnerMoveIs(enum BattlerId battlerAtkPartner, enum Move partnerMove, enum Move moveCheck) { if (!HasPartner(battlerAtkPartner)) return FALSE; @@ -4378,7 +4392,7 @@ bool32 PartnerMoveIs(u32 battlerAtkPartner, enum Move partnerMove, enum Move mov } //PARTNER_MOVE_IS_SAME -bool32 PartnerMoveIsSameAsAttacker(u32 battlerAtkPartner, u32 battlerDef, enum Move move, enum Move partnerMove) +bool32 PartnerMoveIsSameAsAttacker(enum BattlerId battlerAtkPartner, enum BattlerId battlerDef, enum Move move, enum Move partnerMove) { if (!HasPartner(battlerAtkPartner)) return FALSE; @@ -4389,7 +4403,7 @@ bool32 PartnerMoveIsSameAsAttacker(u32 battlerAtkPartner, u32 battlerDef, enum M } //PARTNER_MOVE_IS_SAME_NO_TARGET -bool32 PartnerMoveIsSameNoTarget(u32 battlerAtkPartner, enum Move move, enum Move partnerMove) +bool32 PartnerMoveIsSameNoTarget(enum BattlerId battlerAtkPartner, enum Move move, enum Move partnerMove) { if (!HasPartner(battlerAtkPartner)) return FALSE; @@ -4405,10 +4419,10 @@ bool32 PartnerMoveActivatesSleepClause(enum Move partnerMove) return IsMoveSleepClauseTrigger(partnerMove); } -bool32 ShouldUseWishAromatherapy(u32 battlerAtk, u32 battlerDef, enum Move move) +bool32 ShouldUseWishAromatherapy(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move) { s32 firstId, lastId; - struct Pokemon* party; + struct Pokemon *party; bool32 hasStatus = AnyPartyMemberStatused(battlerAtk, IsSoundMove(move)); bool32 needHealing = FALSE; @@ -4499,7 +4513,7 @@ void FreeRestoreAiLogicData(struct AiLogicData *savedAiLogicData) } // Set potential field effect from ability for switch in -void SetBattlerFieldStatusForSwitchin(u32 battler) +void SetBattlerFieldStatusForSwitchin(enum BattlerId battler) { switch (gAiLogicData->abilities[battler]) { @@ -4521,7 +4535,7 @@ void SetBattlerFieldStatusForSwitchin(u32 battler) } // party logic -s32 CountUsablePartyMons(u32 battlerId) +s32 CountUsablePartyMons(enum BattlerId battlerId) { s32 battlerOnField1, battlerOnField2, ret; struct Pokemon *party; @@ -4555,10 +4569,9 @@ s32 CountUsablePartyMons(u32 battlerId) return ret; } -bool32 IsPartyFullyHealedExceptBattler(u32 battlerId) +bool32 IsPartyFullyHealedExceptBattler(enum BattlerId battlerId) { - struct Pokemon *party; - party = GetBattlerParty(battlerId); + struct Pokemon *party = GetBattlerParty(battlerId); for (u32 monIndex = 0; monIndex < PARTY_SIZE; monIndex++) { @@ -4572,7 +4585,7 @@ bool32 IsPartyFullyHealedExceptBattler(u32 battlerId) return TRUE; } -bool32 PartyHasMoveCategory(u32 battlerId, enum DamageCategory category) +bool32 PartyHasMoveCategory(enum BattlerId battlerId, enum DamageCategory category) { struct Pokemon *party = GetBattlerParty(battlerId); @@ -4598,7 +4611,7 @@ bool32 PartyHasMoveCategory(u32 battlerId, enum DamageCategory category) return FALSE; } -bool32 SideHasMoveCategory(u32 battlerId, enum DamageCategory category) +bool32 SideHasMoveCategory(enum BattlerId battlerId, enum DamageCategory category) { if (HasPartnerIgnoreFlags(battlerId)) { @@ -4666,7 +4679,7 @@ bool32 IsStatBoostingBerry(enum Item item) } } -bool32 ShouldRestoreHpBerry(u32 battlerAtk, enum Item item) +bool32 ShouldRestoreHpBerry(enum BattlerId battlerAtk, enum Item item) { switch (item) { @@ -4696,7 +4709,7 @@ bool32 IsRecycleEncouragedItem(enum Item item) return FALSE; } -static bool32 HasMoveThatChangesKOThreshold(u32 battlerId, u32 noOfHitsToFaint, u32 aiIsFaster) +static bool32 HasMoveThatChangesKOThreshold(enum BattlerId battlerId, u32 noOfHitsToFaint, bool32 aiIsFaster) { enum Move *moves = GetMovesArray(battlerId); @@ -4794,13 +4807,13 @@ static u32 GetStagesOfStatChange(enum StatChange statChange) return 0; // STAT_HP, should never be getting changed } -static enum AIScore IncreaseStatUpScoreInternal(u32 battlerAtk, u32 battlerDef, enum StatChange statChange, bool32 considerContrary) +static enum AIScore IncreaseStatUpScoreInternal(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum StatChange statChange, bool32 considerContrary) { enum AIScore tempScore = NO_INCREASE; u32 noOfHitsToFaint = NoOfHitsForTargetToFaintBattler(battlerDef, battlerAtk, DONT_CONSIDER_ENDURE); enum Move predictedMoveSpeedCheck = GetIncomingMoveSpeedCheck(battlerAtk, battlerDef, gAiLogicData); - u32 aiIsFaster = AI_IsFaster(battlerAtk, battlerDef, MOVE_NONE, predictedMoveSpeedCheck, DONT_CONSIDER_PRIORITY); // Don't care about the priority of our setup move, care about outspeeding otherwise - u32 shouldSetUp = ((noOfHitsToFaint >= 2 && aiIsFaster) || (noOfHitsToFaint >= 3 && !aiIsFaster) || noOfHitsToFaint == UNKNOWN_NO_OF_HITS); + bool32 aiIsFaster = AI_IsFaster(battlerAtk, battlerDef, MOVE_NONE, predictedMoveSpeedCheck, DONT_CONSIDER_PRIORITY); // Don't care about the priority of our setup move, care about outspeeding otherwise + bool32 shouldSetUp = ((noOfHitsToFaint >= 2 && aiIsFaster) || (noOfHitsToFaint >= 3 && !aiIsFaster) || noOfHitsToFaint == UNKNOWN_NO_OF_HITS); enum Stat statId = GetStatBeingChanged(statChange); u32 stages = GetStagesOfStatChange(statChange); @@ -4918,7 +4931,7 @@ static enum AIScore IncreaseStatUpScoreInternal(u32 battlerAtk, u32 battlerDef, return tempScore; } -bool32 HasHPForDamagingSetup(u32 battlerAtk, u32 battlerDef, u32 hpThreshold) +bool32 HasHPForDamagingSetup(enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 hpThreshold) { bool32 bestMoveIsPhysical = HasPhysicalBestMove(battlerDef, battlerAtk, AI_DEFENDING); @@ -4939,17 +4952,17 @@ bool32 HasHPForDamagingSetup(u32 battlerAtk, u32 battlerDef, u32 hpThreshold) return FALSE; } -enum AIScore IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, enum StatChange statChange) +enum AIScore IncreaseStatUpScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum StatChange statChange) { return IncreaseStatUpScoreInternal(battlerAtk, battlerDef, statChange, TRUE); } -enum AIScore IncreaseStatUpScoreContrary(u32 battlerAtk, u32 battlerDef, enum StatChange statChange) +enum AIScore IncreaseStatUpScoreContrary(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum StatChange statChange) { return IncreaseStatUpScoreInternal(battlerAtk, battlerDef, statChange, FALSE); } -void IncreasePoisonScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *score) +void IncreasePoisonScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 *score) { if (((gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) || gAiLogicData->holdEffects[battlerDef] == HOLD_EFFECT_CURE_PSN || gAiLogicData->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS) @@ -4972,7 +4985,7 @@ void IncreasePoisonScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *sc } } -void IncreaseBurnScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *score) +void IncreaseBurnScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 *score) { if (((gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) || gAiLogicData->holdEffects[battlerDef] == HOLD_EFFECT_CURE_BRN || gAiLogicData->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS) @@ -4985,7 +4998,7 @@ void IncreaseBurnScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *scor && GetSpeciesBaseAttack(gBattleMons[battlerDef].species) >= GetSpeciesBaseSpAttack(gBattleMons[battlerDef].species) + 10)) { enum Move defBestMoves[MAX_MON_MOVES] = {MOVE_NONE}; - bool8 hasPhysical = FALSE; + bool32 hasPhysical = FALSE; GetBestDmgMovesFromBattler(battlerAtk, battlerDef, AI_DEFENDING, defBestMoves); @@ -5013,7 +5026,7 @@ void IncreaseBurnScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *scor } } -void IncreaseParalyzeScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *score) +void IncreaseParalyzeScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 *score) { if (((gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) || gAiLogicData->holdEffects[battlerDef] == HOLD_EFFECT_CURE_PAR || gAiLogicData->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS) @@ -5035,7 +5048,7 @@ void IncreaseParalyzeScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 * } } -void IncreaseSleepScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *score) +void IncreaseSleepScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 *score) { if (gAiLogicData->holdEffects[battlerDef] == HOLD_EFFECT_CURE_SLP || gAiLogicData->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS) return; @@ -5067,7 +5080,7 @@ void IncreaseSleepScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *sco ADJUST_SCORE_PTR(WEAK_EFFECT); } -void IncreaseConfusionScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *score) +void IncreaseConfusionScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 *score) { if (((gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) || gAiLogicData->holdEffects[battlerDef] == HOLD_EFFECT_CURE_CONFUSION || gAiLogicData->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS) @@ -5086,7 +5099,7 @@ void IncreaseConfusionScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 } } -void IncreaseFrostbiteScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *score) +void IncreaseFrostbiteScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 *score) { if ((gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0)) return; @@ -5098,7 +5111,7 @@ void IncreaseFrostbiteScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 && GetSpeciesBaseSpAttack(gBattleMons[battlerDef].species) >= GetSpeciesBaseAttack(gBattleMons[battlerDef].species) + 10)) { enum Move defBestMoves[MAX_MON_MOVES] = {MOVE_NONE}; - bool8 hasSpecial = FALSE; + bool32 hasSpecial = FALSE; GetBestDmgMovesFromBattler(battlerAtk, battlerDef, AI_DEFENDING, defBestMoves); @@ -5126,7 +5139,7 @@ void IncreaseFrostbiteScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 } } -bool32 AI_MoveMakesContact(u32 battlerAtk, u32 battlerDef, enum Ability ability, enum HoldEffect holdEffect, enum Move move) +bool32 AI_MoveMakesContact(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability ability, enum HoldEffect holdEffect, enum Move move) { if (GetMoveEffect(move) == EFFECT_SHELL_SIDE_ARM) { @@ -5137,6 +5150,7 @@ bool32 AI_MoveMakesContact(u32 battlerAtk, u32 battlerDef, enum Ability ability, { return FALSE; } + if (ability == ABILITY_LONG_REACH) return FALSE; if (holdEffect == HOLD_EFFECT_PROTECTIVE_PADS) @@ -5146,7 +5160,7 @@ bool32 AI_MoveMakesContact(u32 battlerAtk, u32 battlerDef, enum Ability ability, return TRUE; } -bool32 IsUnseenFistContactMove(u32 battlerAtk, u32 battlerDef, enum Move move) +bool32 IsUnseenFistContactMove(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move) { if (move == MOVE_NONE || move == MOVE_UNAVAILABLE) return FALSE; @@ -5161,13 +5175,15 @@ bool32 IsUnseenFistContactMove(u32 battlerAtk, u32 battlerDef, enum Move move) { return FALSE; } + if (gAiLogicData->holdEffects[battlerAtk] == HOLD_EFFECT_PUNCHING_GLOVE && IsPunchingMove(move)) return FALSE; + return TRUE; } -bool32 IsConsideringZMove(u32 battlerAtk, u32 battlerDef, enum Move move) +bool32 IsConsideringZMove(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move) { if (GetMovePower(move) == 0 && GetMoveZEffect(move) == Z_EFFECT_NONE) return FALSE; @@ -5176,7 +5192,7 @@ bool32 IsConsideringZMove(u32 battlerAtk, u32 battlerDef, enum Move move) } //TODO - this could use some more sophisticated logic -bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, enum Move chosenMove) +bool32 ShouldUseZMove(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move chosenMove) { // simple logic. just upgrades chosen move to z move if possible, unless regular move would kill opponent enum MoveTarget target = AI_GetBattlerMoveTargetType(battlerAtk, chosenMove); @@ -5220,11 +5236,11 @@ bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, enum Move chosenMove) break; } - u32 zMove = GetUsableZMove(battlerAtk, chosenMove); + enum Move zMove = GetUsableZMove(battlerAtk, chosenMove); if (IsBattleMoveStatus(chosenMove)) { - u8 zEffect = GetMoveZEffect(chosenMove); + enum ZEffect zEffect = GetMoveZEffect(chosenMove); enum StatChange statChange = 0; if (zEffect == Z_EFFECT_CURSE) @@ -5346,7 +5362,7 @@ bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, enum Move chosenMove) return FALSE; } -void SetAIUsingGimmick(u32 battler, enum AIConsiderGimmick use) +void SetAIUsingGimmick(enum BattlerId battler, enum AIConsiderGimmick use) { if (use == USE_GIMMICK) gAiBattleData->aiUsingGimmick |= (1<aiUsingGimmick &= ~(1<aiUsingGimmick & (1<gimmick.usableGimmick[battler] != GIMMICK_TERA) return; @@ -5381,7 +5398,7 @@ void DecideTerastal(u32 battler) // TODO: A lot of these checks are most effective for an omnicient ai. // If we don't have enough information about the opponent's moves, consider simpler checks based on type effectivness. - u32 opposingBattler = GetOppositeBattler(battler); + enum BattlerId opposingBattler = GetOppositeBattler(battler); // Default calculations automatically assume gimmicks for the attacker, but not the defender. // Consider calcs for the other possibilities. @@ -5446,7 +5463,7 @@ void DecideTerastal(u32 battler) #define takenWithTera altCalcs->takenWithTera #define takenWithoutTera gAiLogicData->simulatedDmg[opposingBattler][battler] -enum AIConsiderGimmick ShouldTeraFromCalcs(u32 battler, u32 opposingBattler, struct AltTeraCalcs *altCalcs) +enum AIConsiderGimmick ShouldTeraFromCalcs(enum BattlerId battler, enum BattlerId opposingBattler, struct AltTeraCalcs *altCalcs) { struct Pokemon *party = GetBattlerParty(battler); @@ -5461,8 +5478,8 @@ enum AIConsiderGimmick ShouldTeraFromCalcs(u32 battler, u32 opposingBattler, str numPossibleTera++; } - u16 aiHp = gBattleMons[battler].hp; - u16 oppHp = gBattleMons[opposingBattler].hp; + u32 aiHp = gBattleMons[battler].hp; + u32 oppHp = gBattleMons[opposingBattler].hp; enum Move *aiMoves = GetMovesArray(battler); enum Move *oppMoves = GetMovesArray(opposingBattler); @@ -5566,7 +5583,7 @@ enum AIConsiderGimmick ShouldTeraFromCalcs(u32 battler, u32 opposingBattler, str } // Decide to conserve tera based on number of possible later oppotunities - u16 conserveTeraChance = AI_CONSERVE_TERA_CHANCE_PER_MON * (numPossibleTera-1); + u32 conserveTeraChance = AI_CONSERVE_TERA_CHANCE_PER_MON * (numPossibleTera-1); if (RandomPercentage(RNG_AI_CONSERVE_TERA, conserveTeraChance)) return NO_GIMMICK; @@ -5608,13 +5625,12 @@ enum AIConsiderGimmick ShouldTeraFromCalcs(u32 battler, u32 opposingBattler, str #undef takenWithTera #undef takenWithoutTera - -bool32 AI_IsBattlerAsleepOrComatose(u32 battlerId) +bool32 AI_IsBattlerAsleepOrComatose(enum BattlerId battlerId) { return (gBattleMons[battlerId].status1 & STATUS1_SLEEP) || gAiLogicData->abilities[battlerId] == ABILITY_COMATOSE; } -s32 AI_TryToClearStats(u32 battlerAtk, u32 battlerDef, bool32 isDoubleBattle) +s32 AI_TryToClearStats(enum BattlerId battlerAtk, enum BattlerId battlerDef, bool32 isDoubleBattle) { if (isDoubleBattle) return min(CountPositiveStatStages(battlerDef) + CountPositiveStatStages(BATTLE_PARTNER(battlerDef)), 7); @@ -5622,7 +5638,7 @@ s32 AI_TryToClearStats(u32 battlerAtk, u32 battlerDef, bool32 isDoubleBattle) return min(CountPositiveStatStages(battlerDef), 4); } -bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef) +bool32 AI_ShouldCopyStatChanges(enum BattlerId battlerAtk, enum BattlerId battlerDef) { // Want to copy positive stat changes for (enum Stat statId = STAT_ATK; statId < NUM_BATTLE_STATS; statId++) @@ -5653,7 +5669,7 @@ bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef) } //TODO - track entire opponent party data to determine hazard effectiveness -bool32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, enum Move move, struct AiLogicData *aiData) +bool32 AI_ShouldSetUpHazards(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, struct AiLogicData *aiData) { if (CountUsablePartyMons(battlerDef) == 0 || HasBattlerSideMoveWithAIEffect(battlerDef, AI_EFFECT_CLEAR_HAZARDS)) @@ -5678,7 +5694,7 @@ bool32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, enum Move move, str return TRUE; } -void IncreaseTidyUpScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *score) +void IncreaseTidyUpScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 *score) { if (AreAnyHazardsOnSide(GetBattlerSide(battlerAtk)) && CountUsablePartyMons(battlerAtk) != 0) ADJUST_SCORE_PTR(GOOD_EFFECT); @@ -5696,12 +5712,12 @@ void IncreaseTidyUpScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *sc ADJUST_SCORE_PTR(-2); } -bool32 AI_ShouldSpicyExtract(u32 battlerAtk, u32 battlerAtkPartner, enum Move move, struct AiLogicData *aiData) +bool32 AI_ShouldSpicyExtract(enum BattlerId battlerAtk, enum BattlerId battlerAtkPartner, enum Move move, struct AiLogicData *aiData) { - u32 preventsStatLoss; + bool32 preventsStatLoss; enum Ability partnerAbility = aiData->abilities[battlerAtkPartner]; enum BattlerPosition opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(battlerAtk)); - u32 opposingBattler = GetBattlerAtPosition(opposingPosition); + enum BattlerId opposingBattler = GetBattlerAtPosition(opposingPosition); if (gBattleMons[battlerAtkPartner].statStages[STAT_ATK] == MAX_STAT_STAGE || partnerAbility == ABILITY_CONTRARY @@ -5729,7 +5745,7 @@ bool32 AI_ShouldSpicyExtract(u32 battlerAtk, u32 battlerAtkPartner, enum Move mo && HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_PHYSICAL)); } -u32 IncreaseSubstituteMoveScore(u32 battlerAtk, u32 battlerDef, enum Move move) +u32 IncreaseSubstituteMoveScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move) { enum BattleMoveEffects effect = GetMoveEffect(move); u32 scoreIncrease = 0; @@ -5769,7 +5785,7 @@ u32 IncreaseSubstituteMoveScore(u32 battlerAtk, u32 battlerDef, enum Move move) return scoreIncrease; } -bool32 IsBattlerItemEnabled(u32 battler) +bool32 IsBattlerItemEnabled(enum BattlerId battler) { if (gAiThinkingStruct->aiFlags[battler] & AI_FLAG_NEGATE_UNAWARE) return TRUE; @@ -5782,7 +5798,7 @@ bool32 IsBattlerItemEnabled(u32 battler) return TRUE; } -u32 GetFriendlyFireKOThreshold(u32 battler) +u32 GetFriendlyFireKOThreshold(enum BattlerId battler) { if (gAiThinkingStruct->aiFlags[battler] & AI_FLAG_RISKY) return FRIENDLY_FIRE_RISKY_THRESHOLD; @@ -5839,7 +5855,7 @@ bool32 DoesIntimidateRaiseStats(enum Ability ability) } // TODO: work out when to attack into the player's contextually 'beneficial' ability -bool32 ShouldTriggerAbility(u32 battlerAtk, u32 battlerDef, enum Ability ability) +bool32 ShouldTriggerAbility(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability ability) { if (IsTargetingPartner(battlerAtk, battlerDef)) { @@ -5894,9 +5910,9 @@ bool32 ShouldTriggerAbility(u32 battlerAtk, u32 battlerDef, enum Ability ability // Used by CheckBadMove; this is determining purely if the effect CAN change an ability, not if it SHOULD. // At the moment, the parts about Mummy and Wandering Spirit are not actually used. -bool32 CanEffectChangeAbility(u32 battlerAtk, u32 battlerDef, enum Move move, struct AiLogicData *aiData) +bool32 CanEffectChangeAbility(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, struct AiLogicData *aiData) { - u32 effect = GetMoveEffect(move); + enum BattleMoveEffects effect = GetMoveEffect(move); // Dynamaxed Pokemon are immune to some ability-changing effects. if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX) @@ -5945,7 +5961,7 @@ bool32 CanEffectChangeAbility(u32 battlerAtk, u32 battlerDef, enum Move move, st if (HasPartnerIgnoreFlags(battlerAtk)) { - u32 partnerAbility = aiData->abilities[BATTLE_PARTNER(battlerAtk)]; + enum Ability partnerAbility = aiData->abilities[BATTLE_PARTNER(battlerAtk)]; if (gAbilitiesInfo[partnerAbility].cantBeSuppressed) return FALSE; if (partnerAbility == defAbility) @@ -6027,16 +6043,16 @@ bool32 DoesEffectReplaceTargetAbility(u32 effect) } } -void AbilityChangeScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *score, struct AiLogicData *aiData) +void AbilityChangeScore(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, s32 *score, struct AiLogicData *aiData) { - u32 effect = GetMoveEffect(move); + enum BattleMoveEffects effect = GetMoveEffect(move); bool32 isTargetingPartner = IsTargetingPartner(battlerAtk, battlerDef); enum Ability abilityAtk = aiData->abilities[battlerAtk]; enum Ability abilityDef = aiData->abilities[battlerDef]; bool32 partnerHasBadAbility = FALSE; - u32 partnerAbility = ABILITY_NONE; + enum Ability partnerAbility = ABILITY_NONE; bool32 attackerHasBadAbility = (gAbilitiesInfo[abilityAtk].aiRating < 0); - s32 currentAbilityScore, transferredAbilityScore = 0; + enum AIScore currentAbilityScore, transferredAbilityScore = NO_INCREASE; if (HasPartner(battlerAtk)) { @@ -6100,7 +6116,7 @@ void AbilityChangeScore(u32 battlerAtk, u32 battlerDef, enum Move move, s32 *sco } } -s32 BattlerBenefitsFromAbilityScore(u32 battler, enum Ability ability, struct AiLogicData *aiData) +enum AIScore BattlerBenefitsFromAbilityScore(enum BattlerId battler, enum Ability ability, struct AiLogicData *aiData) { if (gAbilitiesInfo[ability].aiRating < 0) return WORST_EFFECT; @@ -6253,7 +6269,7 @@ u32 GetAIExplosionChanceFromHP(u32 hpPercent) return (EXPLOSION_HIGHER_HP_THRESHOLD - hpPercent); } -bool32 ShouldFinalGambit(u32 battlerAtk, u32 battlerDef, bool32 aiIsFaster) +bool32 ShouldFinalGambit(enum BattlerId battlerAtk, enum BattlerId battlerDef, bool32 aiIsFaster) { if (!gAiLogicData->shouldConsiderFinalGambit) return FALSE; @@ -6261,9 +6277,7 @@ bool32 ShouldFinalGambit(u32 battlerAtk, u32 battlerDef, bool32 aiIsFaster) if (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_OMNISCIENT) { if (gBattleMons[battlerAtk].hp >= gBattleMons[battlerDef].hp && aiIsFaster) - { return TRUE; - } } else if (gAiLogicData->hpPercents[battlerAtk] >= gAiLogicData->hpPercents[battlerDef] // Consider using GetScaledHPFraction and moving B_HEALTHBAR_PIXELS define && GetSpeciesBaseHP(gBattleMons[battlerAtk].species) >= GetSpeciesBaseHP(gBattleMons[battlerDef].species) @@ -6274,7 +6288,7 @@ bool32 ShouldFinalGambit(u32 battlerAtk, u32 battlerDef, bool32 aiIsFaster) return FALSE; } -bool32 ShouldConsiderSelfSacrificeDamageEffect(u32 battlerAtk, u32 battlerDef, enum Move move, bool32 aiIsFaster) +bool32 ShouldConsiderSelfSacrificeDamageEffect(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, bool32 aiIsFaster) { if (gAiThinkingStruct->aiFlags[battlerAtk] & AI_FLAG_WILL_SUICIDE) return TRUE; @@ -6285,7 +6299,7 @@ bool32 ShouldConsiderSelfSacrificeDamageEffect(u32 battlerAtk, u32 battlerDef, e return FALSE; } -bool32 AiExpectsToFaintPlayer(u32 battler) +bool32 AiExpectsToFaintPlayer(enum BattlerId battler) { u8 target = gAiBattleData->chosenTarget[battler]; @@ -6303,10 +6317,10 @@ bool32 AiExpectsToFaintPlayer(u32 battler) return FALSE; } -bool32 AI_OpponentCanFaintAiWithMod(u32 battler, u32 healAmount) +bool32 AI_OpponentCanFaintAiWithMod(enum BattlerId battler, u32 healAmount) { // Check special cases to NOT heal - for (u32 battlerIndex = 0; battlerIndex < gBattlersCount; battlerIndex++) + for (enum BattlerId battlerIndex = 0; battlerIndex < gBattlersCount; battlerIndex++) { if (IsOnPlayerSide(battlerIndex) && CanTargetFaintAiWithMod(battlerIndex, battler, healAmount, 0)) { @@ -6317,7 +6331,7 @@ bool32 AI_OpponentCanFaintAiWithMod(u32 battler, u32 healAmount) return FALSE; } -void GetAIPartyIndexes(u32 battler, s32 *firstId, s32 *lastId) +void GetAIPartyIndexes(enum BattlerId battler, s32 *firstId, s32 *lastId) { if (BATTLE_TWO_VS_ONE_OPPONENT && (battler & BIT_SIDE) == B_SIDE_OPPONENT) { @@ -6336,7 +6350,7 @@ void GetAIPartyIndexes(u32 battler, s32 *firstId, s32 *lastId) } } -bool32 ShouldInstructPartner(u32 partner, enum Move move) +bool32 ShouldInstructPartner(enum BattlerId partner, enum Move move) { if (GetMoveEffect(move) == EFFECT_MAX_HP_50_RECOIL && gAiLogicData->abilities[partner] != ABILITY_MAGIC_GUARD) return FALSE; @@ -6360,7 +6374,7 @@ bool32 ShouldInstructPartner(u32 partner, enum Move move) return FALSE; } -bool32 CanMoveBeBouncedBack(u32 battler, enum Move move) +bool32 CanMoveBeBouncedBack(enum BattlerId battler, enum Move move) { if (!MoveCanBeBouncedBack(move) || !IsBattleMoveStatus(move)) return FALSE; @@ -6380,9 +6394,9 @@ bool32 CanMoveBeBouncedBack(u32 battler, enum Move move) return FALSE; } -u32 GetActiveBattlerIds(u32 battler, u32 *battlerIn1, u32 *battlerIn2) +u32 GetActiveBattlerIds(enum BattlerId battler, enum BattlerId *battlerIn1, enum BattlerId *battlerIn2) { - u32 opposingBattler = 0; + enum BattlerId opposingBattler = 0; enum BattlerPosition battlerPosition = GetBattlerPosition(battler); if (IsDoubleBattle()) { @@ -6406,7 +6420,7 @@ u32 GetActiveBattlerIds(u32 battler, u32 *battlerIn1, u32 *battlerIn2) return opposingBattler; } -bool32 IsPartyMonOnFieldOrChosenToSwitch(u32 partyIndex, u32 battlerIn1, u32 battlerIn2) +bool32 IsPartyMonOnFieldOrChosenToSwitch(u32 partyIndex, enum BattlerId battlerIn1, enum BattlerId battlerIn2) { if (partyIndex == gBattlerPartyIndexes[battlerIn1] || partyIndex == gBattlerPartyIndexes[battlerIn2]) diff --git a/src/battle_anim.c b/src/battle_anim.c index 7e02a6b0a..b8b5ae158 100644 --- a/src/battle_anim.c +++ b/src/battle_anim.c @@ -261,6 +261,8 @@ static const u8* const sBattleAnims_General[NUM_B_ANIMS_GENERAL] = [B_ANIM_SILPH_SCOPED] = gBattleAnimGeneral_SilphScoped, [B_ANIM_ROCK_THROW] = gBattleAnimGeneral_SafariRockThrow, [B_ANIM_SAFARI_REACTION] = gBattleAnimGeneral_SafariReaction, + [B_ANIM_FORM_CHANGE_INSTANT] = gBattleAnimGeneral_FormChangeInstant, + [B_ANIM_FORM_CHANGE_DISGUISE] = gBattleAnimGeneral_FormChangeDisguise, }; static const u8* const sBattleAnims_Special[NUM_B_ANIMS_SPECIAL] = diff --git a/src/battle_anim_effects_3.c b/src/battle_anim_effects_3.c index 89d114ea8..e95c2eef1 100644 --- a/src/battle_anim_effects_3.c +++ b/src/battle_anim_effects_3.c @@ -2488,7 +2488,7 @@ void AnimTask_HideSwapSprite(u8 taskId) gTasks[taskId].data[0]++; break; case 1: - HandleSpeciesGfxDataChange(gBattleAnimAttacker, gBattleAnimTarget, gTasks[taskId].data[10], gBattleAnimArgs[1], FALSE); + HandleSpeciesGfxDataChange(gBattleAnimAttacker, gBattleAnimTarget, SPECIES_GFX_CHANGE_FORM_CHANGE); GetBgDataForTransform(&animBg, gBattleAnimAttacker); if (IsContest()) @@ -2576,14 +2576,10 @@ void AnimTask_SetOpponentShadowCallbacks(u8 taskId) DestroyAnimVisualTask(taskId); } -#define megaEvo data[10] -#define trackEnemyPersonality data[11] -#define ghostUnveil data[12] - void AnimTask_TransformMon(u8 taskId) { // int i, j; - u8 position; + enum BattlerPosition position; struct BattleAnimBgData animBg; u8 *dest; u8 *src; @@ -2593,15 +2589,19 @@ void AnimTask_TransformMon(u8 taskId) switch (gTasks[taskId].data[0]) { case 0: + gTasks[taskId].data[10] = gBattleAnimArgs[0]; + if (gTasks[taskId].data[10] == SPECIES_GFX_CHANGE_FORM_CHANGE_INSTANT) + { + // Skip mosaic animation + gTasks[taskId].data[0] = 2; + break; + } SetGpuReg(REG_OFFSET_MOSAIC, 0); if (GetBattlerSpriteBGPriorityRank(gBattleAnimAttacker) == 1) SetAnimBgAttribute(1, BG_ANIM_MOSAIC, 1); else SetAnimBgAttribute(2, BG_ANIM_MOSAIC, 1); - gTasks[taskId].megaEvo = gBattleAnimArgs[0]; - gTasks[taskId].trackEnemyPersonality = gBattleAnimArgs[1]; - gTasks[taskId].ghostUnveil = gBattleAnimArgs[2]; gTasks[taskId].data[0]++; break; case 1: @@ -2616,7 +2616,7 @@ void AnimTask_TransformMon(u8 taskId) } break; case 2: - HandleSpeciesGfxDataChange(gBattleAnimAttacker, gBattleAnimTarget, gTasks[taskId].megaEvo, gTasks[taskId].trackEnemyPersonality, gTasks[taskId].ghostUnveil); + HandleSpeciesGfxDataChange(gBattleAnimAttacker, gBattleAnimTarget, gTasks[taskId].data[10]); GetBgDataForTransform(&animBg, gBattleAnimAttacker); if (IsContest()) @@ -2660,6 +2660,12 @@ void AnimTask_TransformMon(u8 taskId) // StartSpriteAffineAnim(&gSprites[gBattlerSpriteIds[gBattleAnimAttacker]], BATTLER_AFFINE_NORMAL); } + if (gTasks[taskId].data[10] == SPECIES_GFX_CHANGE_FORM_CHANGE_INSTANT) + { + // Skip mosaic animation + DestroyAnimVisualTask(taskId); + break; + } gTasks[taskId].data[0]++; break; case 3: @@ -2685,7 +2691,7 @@ void AnimTask_TransformMon(u8 taskId) { if (!IsOnPlayerSide(gBattleAnimAttacker)) { - if (gTasks[taskId].data[10] == 0) + if (gTasks[taskId].data[10] == SPECIES_GFX_CHANGE_TRANSFORM) SetBattlerShadowSpriteCallback(gBattleAnimAttacker, gBattleSpritesDataPtr->battlerData[gBattleAnimAttacker].transformSpecies); } } @@ -2695,10 +2701,6 @@ void AnimTask_TransformMon(u8 taskId) } } -#undef megaEvo -#undef trackEnemyPersonality -#undef ghostUnveil - void AnimTask_IsMonInvisible(u8 taskId) { gBattleAnimArgs[ARG_RET_ID] = gSprites[gBattlerSpriteIds[gBattleAnimAttacker]].invisible; diff --git a/src/battle_arena.c b/src/battle_arena.c index 123ea879e..f2ecd1109 100644 --- a/src/battle_arena.c +++ b/src/battle_arena.c @@ -562,48 +562,56 @@ static void BufferArenaOpponentName(void) void DrawArenaRefereeTextBox(void) { - u8 width = 27; - u8 palNum = 7; + u32 width = 0x1A; + u32 pal = 7; - FillBgTilemapBufferRect(0, 0, 254, 14, 1, 6, palNum); - FillBgTilemapBufferRect(0, 0, 32, 14, 1, 6, palNum); - FillBgTilemapBufferRect(0, 0x31, 0, 14, 1, 1, palNum); - FillBgTilemapBufferRect(0, 0x33, 1, 14, 1, 1, palNum); - FillBgTilemapBufferRect(0, 0x34, 2, 14, width, 1, palNum); - width++; - FillBgTilemapBufferRect(0, 0x35, 28, 14, 1, 1, palNum); - FillBgTilemapBufferRect(0, 0x36, 29, 14, 1, 1, palNum); - FillBgTilemapBufferRect(0, 0x37, 0, 15, 1, 5, palNum); - FillBgTilemapBufferRect(0, 0x39, 1, 15, width, 5, palNum); - FillBgTilemapBufferRect(0, 0x3A, 29, 15, 1, 5, palNum); - FillBgTilemapBufferRect(0, 0x831, 0, 19, 1, 1, palNum); - FillBgTilemapBufferRect(0, 0x833, 1, 19, 1, 1, palNum); - FillBgTilemapBufferRect(0, 0x834, 2, 19, width - 2, 1, palNum); - FillBgTilemapBufferRect(0, 0x835, 28, 19, 1, 1, palNum); - FillBgTilemapBufferRect(0, 0x836, 29, 19, 1, 1, palNum); + FillBgTilemapBufferRect(0, 0x30, 0, 0xE, 1, 1, pal); + FillBgTilemapBufferRect(0, 0x31, 1, 0xE, 1, 1, pal); + FillBgTilemapBufferRect(0, 0x32, 2, 0xE, width, 1, pal); + FillBgTilemapBufferRect(0, 0x33, 0x1C, 0xE, 1, 1, pal); + FillBgTilemapBufferRect(0, 0x34, 0x1D, 0xE, 1, 1, pal); + FillBgTilemapBufferRect(0, 0x35, 0, 0xF, 1, 1, pal); + FillBgTilemapBufferRect(0, 0x36, 1, 0xF, 1, 1, pal); + FillBgTilemapBufferRect(0, 0x38, 0x1C, 0xF, 1, 1, pal); + FillBgTilemapBufferRect(0, 0x39, 0x1D, 0xF, 1, 1, pal); + FillBgTilemapBufferRect(0, 0x3A, 0, 0x10, 1, 1, pal); + FillBgTilemapBufferRect(0, 0x3B, 1, 0x10, 1, 1, pal); + FillBgTilemapBufferRect(0, 0x3C, 0x1C, 0x10, 1, 1, pal); + FillBgTilemapBufferRect(0, 0x3D, 0x1D, 0x10, 1, 1, pal); + FillBgTilemapBufferRect(0, BG_TILE_V_FLIP(0x3A), 0, 0x11, 1, 1, pal); + FillBgTilemapBufferRect(0, BG_TILE_V_FLIP(0x3B), 1, 0x11, 1, 1, pal); + FillBgTilemapBufferRect(0, BG_TILE_V_FLIP(0x3C), 0x1C, 0x11, 1, 1, pal); + FillBgTilemapBufferRect(0, BG_TILE_V_FLIP(0x3D), 0x1D, 0x11, 1, 1, pal); + FillBgTilemapBufferRect(0, BG_TILE_V_FLIP(0x35), 0, 0x12, 1, 1, pal); + FillBgTilemapBufferRect(0, BG_TILE_V_FLIP(0x36), 1, 0x12, 1, 1, pal); + FillBgTilemapBufferRect(0, BG_TILE_V_FLIP(0x38), 0x1C, 0x12, 1, 1, pal); + FillBgTilemapBufferRect(0, BG_TILE_V_FLIP(0x39), 0x1D, 0x12, 1, 1, pal); + FillBgTilemapBufferRect(0, BG_TILE_V_FLIP(0x30), 0, 0x13, 1, 1, pal); + FillBgTilemapBufferRect(0, BG_TILE_V_FLIP(0x31), 1, 0x13, 1, 1, pal); + FillBgTilemapBufferRect(0, BG_TILE_V_FLIP(0x32), 2, 0x13, width, 1, pal); + FillBgTilemapBufferRect(0, BG_TILE_V_FLIP(0x33), 0x1C, 0x13, 1, 1, pal); + FillBgTilemapBufferRect(0, BG_TILE_V_FLIP(0x34), 0x1D, 0x13, 1, 1, pal); } void EraseArenaRefereeTextBox(void) { - u8 width; - u8 height; - u8 palNum = 0; + u32 pal = 0; + u32 width = 0x1A; + u32 height = 4; - FillBgTilemapBufferRect(0, 3, 0, 14, 1, 1, palNum); - height = 4; - FillBgTilemapBufferRect(0, 4, 1, 14, 1, 1, palNum); - width = 27; - FillBgTilemapBufferRect(0, 5, 2, 14, width, 1, palNum); - FillBgTilemapBufferRect(0, 6, 28, 14, 1, 1, palNum); - FillBgTilemapBufferRect(0, 7, 29, 14, 1, 1, palNum); - FillBgTilemapBufferRect(0, 8, 0, 15, 1, height, palNum); - FillBgTilemapBufferRect(0, 9, 1, 15, 1, height, palNum); - FillBgTilemapBufferRect(0, 0xA, 2, 15, width, height, palNum); - FillBgTilemapBufferRect(0, 0xB, 28, 15, 1, height, palNum); - FillBgTilemapBufferRect(0, 0xC, 29, 15, 1, height, palNum); - FillBgTilemapBufferRect(0, 0xD, 0, 19, 1, 1, palNum); - FillBgTilemapBufferRect(0, 0xE, 1, 19, 1, 1, palNum); - FillBgTilemapBufferRect(0, 0xF, 2, 19, width, 1, palNum); - FillBgTilemapBufferRect(0, 0x10, 28, 19, 1, 1, palNum); - FillBgTilemapBufferRect(0, 0x11, 29, 19, 1, 1, palNum); + FillBgTilemapBufferRect(0, 3, 0, 0xE, 1, 1, pal); + FillBgTilemapBufferRect(0, 4, 1, 0xE, 1, 1, pal); + FillBgTilemapBufferRect(0, 5, 2, 0xE, width, 1, pal); + FillBgTilemapBufferRect(0, 6, 0x1C, 0xE, 1, 1, pal); + FillBgTilemapBufferRect(0, 7, 0x1D, 0xE, 1, 1, pal); + FillBgTilemapBufferRect(0, 8, 0, 0xF, 1, height, pal); + FillBgTilemapBufferRect(0, 9, 1, 0xF, 1, height, pal); + FillBgTilemapBufferRect(0, 0xA, 2, 0xF, width, height, pal); + FillBgTilemapBufferRect(0, 0xB, 0x1C, 0xF, 1, height, pal); + FillBgTilemapBufferRect(0, 0xC, 0x1D, 0xF, 1, height, pal); + FillBgTilemapBufferRect(0, 0xD, 0, 0x13, 1, 1, pal); + FillBgTilemapBufferRect(0, 0xE, 1, 0x13, 1, 1, pal); + FillBgTilemapBufferRect(0, 0xF, 2, 0x13, width, 1, pal); + FillBgTilemapBufferRect(0, 0x10, 0x1C, 0x13, 1, 1, pal); + FillBgTilemapBufferRect(0, 0x11, 0x1D, 0x13, 1, 1, pal); } diff --git a/src/battle_bg.c b/src/battle_bg.c index 0ef23c656..cf727b4ad 100644 --- a/src/battle_bg.c +++ b/src/battle_bg.c @@ -732,7 +732,7 @@ void LoadBattleMenuWindowGfx(void) gPlttBufferUnfaded[BG_PLTT_ID(5) + 15] = RGB( 26, 26, 25); CpuCopy16(&gPlttBufferUnfaded[BG_PLTT_ID(5) + 12], &gPlttBufferFaded[BG_PLTT_ID(5) + 12], PLTT_SIZEOF(4)); - if (gBattleTypeFlags & (BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_POKEDUDE)) + if (gBattleTypeFlags & (BATTLE_TYPE_ARENA | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_POKEDUDE)) { // Load graphics for the Battle Arena referee's mid-battle messages. Menu_LoadStdPalAt(BG_PLTT_ID(7)); diff --git a/src/battle_controller_link_opponent.c b/src/battle_controller_link_opponent.c index b18ac912f..e72c37f75 100644 --- a/src/battle_controller_link_opponent.c +++ b/src/battle_controller_link_opponent.c @@ -6,9 +6,11 @@ #include "battle_interface.h" #include "battle_message.h" #include "battle_setup.h" +#include "battle_tower.h" // #include "battle_tv.h" #include "bg.h" #include "data.h" +#include "frontier_util.h" #include "link.h" #include "main.h" #include "m4a.h" @@ -29,17 +31,17 @@ #include "recorded_battle.h" #include "random.h" -static void LinkOpponentHandleDrawTrainerPic(u32 battler); -static void LinkOpponentHandleTrainerSlide(u32 battler); -static void LinkOpponentHandleTrainerSlideBack(u32 battler); -static void LinkOpponentHandleIntroTrainerBallThrow(u32 battler); -static void LinkOpponentHandleDrawPartyStatusSummary(u32 battler); -static void LinkOpponentHandleLinkStandbyMsg(u32 battler); -static void LinkOpponentHandleEndLinkBattle(u32 battler); +static void LinkOpponentHandleDrawTrainerPic(enum BattlerId battler); +static void LinkOpponentHandleTrainerSlide(enum BattlerId battler); +static void LinkOpponentHandleTrainerSlideBack(enum BattlerId battler); +static void LinkOpponentHandleIntroTrainerBallThrow(enum BattlerId battler); +static void LinkOpponentHandleDrawPartyStatusSummary(enum BattlerId battler); +static void LinkOpponentHandleLinkStandbyMsg(enum BattlerId battler); +static void LinkOpponentHandleEndLinkBattle(enum BattlerId battler); -static void LinkOpponentBufferRunCommand(u32 battler); +static void LinkOpponentBufferRunCommand(enum BattlerId battler); -static void (*const sLinkOpponentBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) = +static void (*const sLinkOpponentBufferCommands[CONTROLLER_CMDS_COUNT])(enum BattlerId battler) = { [CONTROLLER_GETMONDATA] = BtlController_HandleGetMonData, [CONTROLLER_GETRAWMONDATA] = BtlController_Empty, @@ -96,13 +98,14 @@ static void (*const sLinkOpponentBufferCommands[CONTROLLER_CMDS_COUNT])(u32 batt [CONTROLLER_TERMINATOR_NOP] = BtlController_TerminatorNop }; -void SetControllerToLinkOpponent(u32 battler) +void SetControllerToLinkOpponent(enum BattlerId battler) { + gBattlerBattleController[battler] = BATTLE_CONTROLLER_LINK_OPPONENT; gBattlerControllerEndFuncs[battler] = LinkOpponentBufferExecCompleted; gBattlerControllerFuncs[battler] = LinkOpponentBufferRunCommand; } -static void LinkOpponentBufferRunCommand(u32 battler) +static void LinkOpponentBufferRunCommand(enum BattlerId battler) { if (IsBattleControllerActiveOnLocal(battler)) { @@ -113,7 +116,7 @@ static void LinkOpponentBufferRunCommand(u32 battler) } } -static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) +static void Intro_WaitForShinyAnimAndHealthbox(enum BattlerId battler) { bool32 healthboxAnimDone = FALSE; bool32 twoMons = FALSE; @@ -148,8 +151,7 @@ static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].triedShinyMonAnim = FALSE; gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].finishedShinyMonAnim = FALSE; - FreeSpriteTilesByTag(ANIM_TAG_GOLD_STARS); - FreeSpritePaletteByTag(ANIM_TAG_GOLD_STARS); + FreeShinyStars(); } else { @@ -160,10 +162,7 @@ static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = FALSE; if (GetBattlerPosition(battler) == B_POSITION_OPPONENT_RIGHT) - { - FreeSpriteTilesByTag(ANIM_TAG_GOLD_STARS); - FreeSpritePaletteByTag(ANIM_TAG_GOLD_STARS); - } + FreeShinyStars(); } gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay = 3; @@ -171,7 +170,7 @@ static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) } } -static void Intro_TryShinyAnimShowHealthbox(u32 battler) +static void Intro_TryShinyAnimShowHealthbox(enum BattlerId battler) { bool32 bgmRestored = FALSE; @@ -248,7 +247,7 @@ static void Intro_TryShinyAnimShowHealthbox(u32 battler) DestroySprite(&gSprites[gBattleControllerData[battler]]); - SetBattlerShadowSpriteCallback(battler, GetMonData(GetBattlerMon(battler), MON_DATA_SPECIES)); + SetBattlerShadowSpriteCallback(battler, GetBattlerVisualSpecies(battler)); gBattleSpritesDataPtr->animationData->introAnimActive = FALSE; gBattleSpritesDataPtr->healthBoxesData[battler].bgmRestored = FALSE; @@ -259,7 +258,7 @@ static void Intro_TryShinyAnimShowHealthbox(u32 battler) } } -void LinkOpponentBufferExecCompleted(u32 battler) +void LinkOpponentBufferExecCompleted(enum BattlerId battler) { gBattlerControllerFuncs[battler] = LinkOpponentBufferRunCommand; if (gBattleTypeFlags & BATTLE_TYPE_LINK) @@ -275,34 +274,41 @@ void LinkOpponentBufferExecCompleted(u32 battler) } } -static void LinkOpponentHandleDrawTrainerPic(u32 battler) +static void LinkOpponentHandleDrawTrainerPic(enum BattlerId battler) { s16 xPos; - u32 trainerPicId; + enum TrainerPicID trainerPicId; + enum BattlerPosition position = GetBattlerPosition(battler); if (gBattleTypeFlags & BATTLE_TYPE_MULTI) { - if ((GetBattlerPosition(battler) & BIT_FLANK) != 0) // second mon + if ((position & BIT_FLANK) != 0) // second mon xPos = 152; else // first mon xPos = 200; - if ((gLinkPlayers[GetBattlerMultiplayerId(battler)].version & 0xFF) == VERSION_RUBY - || (gLinkPlayers[GetBattlerMultiplayerId(battler)].version & 0xFF) == VERSION_SAPPHIRE - || (gLinkPlayers[GetBattlerMultiplayerId(battler)].version & 0xFF) == VERSION_EMERALD) + if (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) { - if (gLinkPlayers[GetBattlerMultiplayerId(battler)].gender != MALE) - trainerPicId = gFacilityClassToPicIndex[FACILITY_CLASS_PKMN_TRAINER_MAY]; + if (position == B_POSITION_OPPONENT_LEFT) + trainerPicId = GetFrontierTrainerFrontSpriteId(TRAINER_BATTLE_PARAM.opponentA); else - trainerPicId = gFacilityClassToPicIndex[FACILITY_CLASS_PKMN_TRAINER_BRENDAN]; - } - else if (gLinkPlayers[GetBattlerMultiplayerId(battler)].gender != MALE) - { - trainerPicId = gFacilityClassToPicIndex[FACILITY_CLASS_LEAF]; + trainerPicId = GetFrontierTrainerFrontSpriteId(TRAINER_BATTLE_PARAM.opponentB); } else { - trainerPicId = gFacilityClassToPicIndex[FACILITY_CLASS_RED]; + if ((gLinkPlayers[GetBattlerMultiplayerId(battler)].version & 0xFF) == VERSION_RUBY + || (gLinkPlayers[GetBattlerMultiplayerId(battler)].version & 0xFF) == VERSION_SAPPHIRE + || (gLinkPlayers[GetBattlerMultiplayerId(battler)].version & 0xFF) == VERSION_EMERALD) + { + if (gLinkPlayers[GetBattlerMultiplayerId(battler)].gender != MALE) + trainerPicId = gFacilityClassToPicIndex[FACILITY_CLASS_PKMN_TRAINER_MAY]; + else + trainerPicId = gFacilityClassToPicIndex[FACILITY_CLASS_PKMN_TRAINER_BRENDAN]; + } + else + { + trainerPicId = PlayerGenderToFrontTrainerPicId(gLinkPlayers[GetBattlerMultiplayerId(battler)].gender); + } } } else @@ -321,57 +327,59 @@ static void LinkOpponentHandleDrawTrainerPic(u32 battler) else trainerPicId = gFacilityClassToPicIndex[FACILITY_CLASS_PKMN_TRAINER_BRENDAN]; } - else if (gLinkPlayers[GetMultiplayerId() ^ BIT_SIDE].gender != MALE) - { - trainerPicId = gFacilityClassToPicIndex[FACILITY_CLASS_LEAF]; - } else { - trainerPicId = gFacilityClassToPicIndex[FACILITY_CLASS_RED]; + trainerPicId = PlayerGenderToFrontTrainerPicId(gLinkPlayers[GetMultiplayerId() ^ BIT_SIDE].gender); } } + BtlController_HandleDrawTrainerPic(battler, trainerPicId, TRUE, xPos, 40, -1); } -static void LinkOpponentHandleTrainerSlide(u32 battler) +static void LinkOpponentHandleTrainerSlide(enum BattlerId battler) { - u32 trainerPicId = TRAINER_PIC_RED; + enum TrainerPicID trainerPicId; + + if (GetBattlerPosition(battler) == B_POSITION_OPPONENT_LEFT) + trainerPicId = GetFrontierTrainerFrontSpriteId(TRAINER_BATTLE_PARAM.opponentA); + else + trainerPicId = GetFrontierTrainerFrontSpriteId(TRAINER_BATTLE_PARAM.opponentB); BtlController_HandleTrainerSlide(battler, trainerPicId); BtlController_Complete(battler); // Possibly a bug, because execution should be completed after the slide in finishes. See Controller_WaitForTrainerPic. } -static void LinkOpponentHandleTrainerSlideBack(u32 battler) +static void LinkOpponentHandleTrainerSlideBack(enum BattlerId battler) { BtlController_HandleTrainerSlideBack(battler, 35, FALSE); } -static void LinkOpponentHandleIntroTrainerBallThrow(u32 battler) +static void LinkOpponentHandleIntroTrainerBallThrow(enum BattlerId battler) { BtlController_HandleIntroTrainerBallThrow(battler, 0, NULL, 0, Intro_TryShinyAnimShowHealthbox); } -static void LinkOpponentHandleDrawPartyStatusSummary(u32 battler) +static void LinkOpponentHandleDrawPartyStatusSummary(enum BattlerId battler) { BtlController_HandleDrawPartyStatusSummary(battler, B_SIDE_OPPONENT, TRUE); } -static void LinkOpponentHandleLinkStandbyMsg(u32 battler) +static void LinkOpponentHandleLinkStandbyMsg(enum BattlerId battler) { - // RecordedBattle_RecordAllBattlerData(&gBattleResources->bufferA[battler][2]); + RecordedBattle_RecordAllBattlerData(&gBattleResources->bufferA[battler][2]); BtlController_Complete(battler); } -static void LinkOpponentHandleEndLinkBattle(u32 battler) +static void LinkOpponentHandleEndLinkBattle(enum BattlerId battler) { - // RecordedBattle_RecordAllBattlerData(&gBattleResources->bufferA[battler][4]); + RecordedBattle_RecordAllBattlerData(&gBattleResources->bufferA[battler][4]); if (gBattleResources->bufferA[battler][1] == B_OUTCOME_DREW) gBattleOutcome = gBattleResources->bufferA[battler][1]; else gBattleOutcome = gBattleResources->bufferA[battler][1] ^ B_OUTCOME_DREW; - // gSaveBlock2Ptr->frontier.disableRecordBattle = gBattleResources->bufferA[battler][2]; + gSaveBlock2Ptr->frontier.disableRecordBattle = gBattleResources->bufferA[battler][2]; FadeOutMapMusic(5); BeginFastPaletteFade(3); BtlController_Complete(battler); diff --git a/src/battle_controller_link_partner.c b/src/battle_controller_link_partner.c index 837f695ad..bff3f8f5b 100644 --- a/src/battle_controller_link_partner.c +++ b/src/battle_controller_link_partner.c @@ -6,9 +6,11 @@ #include "battle_interface.h" #include "battle_message.h" #include "battle_setup.h" +#include "battle_tower.h" // #include "battle_tv.h" #include "bg.h" #include "data.h" +#include "frontier_util.h" #include "link.h" #include "main.h" #include "m4a.h" @@ -29,16 +31,16 @@ #include "recorded_battle.h" #include "random.h" -static void LinkPartnerHandleDrawTrainerPic(u32 battler); -static void LinkPartnerHandleTrainerSlideBack(u32 battler); -static void LinkPartnerHandleIntroTrainerBallThrow(u32 battler); -static void LinkPartnerHandleDrawPartyStatusSummary(u32 battler); -static void LinkPartnerHandleLinkStandbyMsg(u32 battler); -static void LinkPartnerHandleEndLinkBattle(u32 battler); +static void LinkPartnerHandleDrawTrainerPic(enum BattlerId battler); +static void LinkPartnerHandleTrainerSlideBack(enum BattlerId battler); +static void LinkPartnerHandleIntroTrainerBallThrow(enum BattlerId battler); +static void LinkPartnerHandleDrawPartyStatusSummary(enum BattlerId battler); +static void LinkPartnerHandleLinkStandbyMsg(enum BattlerId battler); +static void LinkPartnerHandleEndLinkBattle(enum BattlerId battler); -static void LinkPartnerBufferRunCommand(u32 battler); +static void LinkPartnerBufferRunCommand(enum BattlerId battler); -static void (*const sLinkPartnerBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) = +static void (*const sLinkPartnerBufferCommands[CONTROLLER_CMDS_COUNT])(enum BattlerId battler) = { [CONTROLLER_GETMONDATA] = BtlController_HandleGetMonData, [CONTROLLER_GETRAWMONDATA] = BtlController_Empty, @@ -95,13 +97,14 @@ static void (*const sLinkPartnerBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battl [CONTROLLER_TERMINATOR_NOP] = BtlController_TerminatorNop }; -void SetControllerToLinkPartner(u32 battler) +void SetControllerToLinkPartner(enum BattlerId battler) { + gBattlerBattleController[battler] = BATTLE_CONTROLLER_LINK_PARTNER; gBattlerControllerEndFuncs[battler] = LinkPartnerBufferExecCompleted; gBattlerControllerFuncs[battler] = LinkPartnerBufferRunCommand; } -static void LinkPartnerBufferRunCommand(u32 battler) +static void LinkPartnerBufferRunCommand(enum BattlerId battler) { if (IsBattleControllerActiveOnLocal(battler)) { @@ -112,7 +115,7 @@ static void LinkPartnerBufferRunCommand(u32 battler) } } -void LinkPartnerBufferExecCompleted(u32 battler) +void LinkPartnerBufferExecCompleted(enum BattlerId battler) { gBattlerControllerFuncs[battler] = LinkPartnerBufferRunCommand; if (gBattleTypeFlags & BATTLE_TYPE_LINK) @@ -128,10 +131,10 @@ void LinkPartnerBufferExecCompleted(u32 battler) } } -static void LinkPartnerHandleDrawTrainerPic(u32 battler) +static void LinkPartnerHandleDrawTrainerPic(enum BattlerId battler) { s16 xPos; - u32 trainerPicId; + enum TrainerPicID trainerPicId; if (gBattleTypeFlags & BATTLE_TYPE_MULTI) { @@ -151,35 +154,35 @@ static void LinkPartnerHandleDrawTrainerPic(u32 battler) -1); } -static void LinkPartnerHandleTrainerSlideBack(u32 battler) +static void LinkPartnerHandleTrainerSlideBack(enum BattlerId battler) { BtlController_HandleTrainerSlideBack(battler, 35, FALSE); } -static void LinkPartnerHandleIntroTrainerBallThrow(u32 battler) +static void LinkPartnerHandleIntroTrainerBallThrow(enum BattlerId battler) { - u32 trainerPicId = LinkPlayerGetTrainerPicId(GetBattlerMultiplayerId(battler)); + enum TrainerPicID trainerPicId = LinkPlayerGetTrainerPicId(GetBattlerMultiplayerId(battler)); const u16 *trainerPal = gTrainerBacksprites[trainerPicId].palette.data; // Link partner uses the same intro sequence as the player partner. BtlController_HandleIntroTrainerBallThrow(battler, 0xD6F9, trainerPal, 24, Controller_PlayerPartnerShowIntroHealthbox); } -static void LinkPartnerHandleDrawPartyStatusSummary(u32 battler) +static void LinkPartnerHandleDrawPartyStatusSummary(enum BattlerId battler) { BtlController_HandleDrawPartyStatusSummary(battler, B_SIDE_PLAYER, TRUE); } -static void LinkPartnerHandleLinkStandbyMsg(u32 battler) +static void LinkPartnerHandleLinkStandbyMsg(enum BattlerId battler) { - // RecordedBattle_RecordAllBattlerData(&gBattleResources->bufferA[battler][2]); + RecordedBattle_RecordAllBattlerData(&gBattleResources->bufferA[battler][2]); BtlController_Complete(battler); } -static void LinkPartnerHandleEndLinkBattle(u32 battler) +static void LinkPartnerHandleEndLinkBattle(enum BattlerId battler) { - // RecordedBattle_RecordAllBattlerData(&gBattleResources->bufferA[battler][4]); + RecordedBattle_RecordAllBattlerData(&gBattleResources->bufferA[battler][4]); gBattleOutcome = gBattleResources->bufferA[battler][1]; - // gSaveBlock2Ptr->frontier.disableRecordBattle = gBattleResources->bufferA[battler][2]; + gSaveBlock2Ptr->frontier.disableRecordBattle = gBattleResources->bufferA[battler][2]; FadeOutMapMusic(5); BeginFastPaletteFade(3); BtlController_Complete(battler); diff --git a/src/battle_controller_oak_old_man.c b/src/battle_controller_oak_old_man.c index 3d1a65d70..6246ddbf1 100644 --- a/src/battle_controller_oak_old_man.c +++ b/src/battle_controller_oak_old_man.c @@ -1,48 +1,52 @@ #include "global.h" -#include "gflib.h" -#include "task.h" -#include "pokeball.h" -#include "party_menu.h" -#include "util.h" -#include "m4a.h" -#include "link.h" -#include "item.h" -#include "item_menu.h" -#include "strings.h" #include "battle.h" #include "battle_anim.h" #include "battle_controllers.h" #include "battle_interface.h" #include "battle_message.h" +#include "bg.h" +#include "item.h" +#include "item_menu.h" +#include "link.h" +#include "m4a.h" +#include "palette.h" +#include "party_menu.h" +#include "pokeball.h" #include "reshow_battle_screen.h" +#include "sound.h" +#include "strings.h" +#include "task.h" +#include "text.h" +#include "util.h" #include "constants/battle_string_ids.h" -#include "constants/songs.h" #include "constants/items.h" +#include "constants/rgb.h" +#include "constants/songs.h" -static void OakOldManHandleDrawTrainerPic(u32 battler); -static void OakOldManHandleTrainerSlide(u32 battler); -static void OakOldManHandlePrintString(u32 battler); -static void OakOldManHandlePrintSelectionString(u32 battler); -static void OakOldManHandleChooseAction(u32 battler); -static void OakOldManHandleChooseMove(u32 battler); -static void OakOldManHandleChooseItem(u32 battler); -static void OakOldManHandleChoosePokemon(u32 battler); -static void OakOldManHandlePlaySE(u32 battler); -static void OakOldManHandleFaintingCry(u32 battler); -static void OakOldManHandleIntroTrainerBallThrow(u32 battler); -static void OakOldManHandleDrawPartyStatusSummary(u32 battler); -static void OakOldManHandleEndBounceEffect(u32 battler); -static void OakOldManHandleLinkStandbyMsg(u32 battler); -static void OakOldManHandleEndLinkBattle(u32 battler); +static void OakOldManHandleDrawTrainerPic(enum BattlerId battler); +static void OakOldManHandleTrainerSlide(enum BattlerId battler); +static void OakOldManHandlePrintString(enum BattlerId battler); +static void OakOldManHandlePrintSelectionString(enum BattlerId battler); +static void OakOldManHandleChooseAction(enum BattlerId battler); +static void OakOldManHandleChooseMove(enum BattlerId battler); +static void OakOldManHandleChooseItem(enum BattlerId battler); +static void OakOldManHandleChoosePokemon(enum BattlerId battler); +static void OakOldManHandlePlaySE(enum BattlerId battler); +static void OakOldManHandleFaintingCry(enum BattlerId battler); +static void OakOldManHandleIntroTrainerBallThrow(enum BattlerId battler); +static void OakOldManHandleDrawPartyStatusSummary(enum BattlerId battler); +static void OakOldManHandleEndBounceEffect(enum BattlerId battler); +static void OakOldManHandleLinkStandbyMsg(enum BattlerId battler); +static void OakOldManHandleEndLinkBattle(enum BattlerId battler); -static void OakOldManBufferRunCommand(u32 battler); -static void WaitForMonSelection(u32 battler); -static void CompleteWhenChoseItem(u32 battler); -static void PrintOakText_KeepAnEyeOnHP(u32 battler); -static void Intro_WaitForShinyAnimAndHealthbox(u32 battler); -static void PrintOakText_ForPetesSake(u32 battler); -static void PrintOakTextWithMainBgDarkened(u32 battler, const u8 *text, u8 delay); -static void HandleInputChooseAction(u32 battler); +static void OakOldManBufferRunCommand(enum BattlerId battler); +static void WaitForMonSelection(enum BattlerId battler); +static void CompleteWhenChoseItem(enum BattlerId battler); +static void PrintOakText_KeepAnEyeOnHP(enum BattlerId battler); +static void Intro_WaitForShinyAnimAndHealthbox(enum BattlerId battler); +static void PrintOakText_ForPetesSake(enum BattlerId battler); +static void PrintOakTextWithMainBgDarkened(enum BattlerId battler, const u8 *text, u8 delay); +static void HandleInputChooseAction(enum BattlerId battler); static const u8 sText_ForPetesSake[] = _("OAK: Oh, for Pete's sake…\nSo pushy, as always.\p{B_PLAYER_NAME}.\pYou've never had a POKéMON battle\nbefore, have you?\pA POKéMON battle is when TRAINERS\npit their POKéMON against each\lother.\p"); static const u8 sText_HowDissapointing[] = _("OAK: Hm…\nHow disappointing…\pIf you win, you earn prize money,\nand your POKéMON grow.\pBut if you lose, {B_PLAYER_NAME}, you end\nup paying prize money…\pHowever, since you had no warning\nthis time, I'll pay for you.\pBut things won't be this way once\nyou step outside these doors.\pThat's why you must strengthen your\nPOKéMON by battling wild POKéMON.\p"); @@ -55,7 +59,7 @@ static const u8 sText_TryBattling[] = _("But rather than talking about it,\nyou' static const u8 sText_WinEarnsPrizeMoney[] = _("OAK: Hm! Excellent!\pIf you win, you earn prize money,\nand your POKéMON will grow!\pBattle other TRAINERS and make\nyour POKéMON strong!\p"); static const u8 gText_WhatWillOldManDo[] = _("What will the\nold man do?"); -static void (*const sOakOldManBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) = +static void (*const sOakOldManBufferCommands[CONTROLLER_CMDS_COUNT])(enum BattlerId battler) = { [CONTROLLER_GETMONDATA] = BtlController_HandleGetMonData, [CONTROLLER_GETRAWMONDATA] = BtlController_Empty, @@ -112,8 +116,9 @@ static void (*const sOakOldManBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler [CONTROLLER_TERMINATOR_NOP] = BtlController_TerminatorNop, }; -void SetControllerToOakOrOldMan(u32 battler) +void SetControllerToOakOrOldMan(enum BattlerId battler) { + gBattlerBattleController[battler] = BATTLE_CONTROLLER_OAK_OLD_MAN; gBattlerControllerEndFuncs[battler] = OakOldManBufferExecCompleted; gBattlerControllerFuncs[battler] = OakOldManBufferRunCommand; gBattleStruct->simulatedInputState[0] = 0; @@ -122,7 +127,7 @@ void SetControllerToOakOrOldMan(u32 battler) gBattleStruct->simulatedInputState[3] = 0; } -static void OakOldManBufferRunCommand(u32 battler) +static void OakOldManBufferRunCommand(enum BattlerId battler) { if (gBattleControllerExecFlags & (1u << battler)) { @@ -133,7 +138,7 @@ static void OakOldManBufferRunCommand(u32 battler) } } -static void HandleInputChooseAction(u32 battler) +static void HandleInputChooseAction(enum BattlerId battler) { // Like player, but specifically for Rival in Oak's Lab u16 itemId = gBattleResources->bufferA[battler][2] | (gBattleResources->bufferA[battler][3] << 8); @@ -205,13 +210,13 @@ static void HandleInputChooseAction(u32 battler) { if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT - && !(gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)]) + && !(gAbsentBattlerFlags & (1 << GetBattlerAtPosition(B_POSITION_PLAYER_LEFT))) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) { if (gBattleResources->bufferA[battler][1] == B_ACTION_USE_ITEM) { // Add item to bag if it is a ball - if (IsItemBall(itemId)) + if (GetItemPocket(itemId) == POCKET_POKE_BALLS) AddBagItem(itemId, 1); else return; @@ -227,7 +232,7 @@ static void HandleInputChooseAction(u32 battler) } } -static void SimulateInputChooseAction(u32 battler) +static void SimulateInputChooseAction(enum BattlerId battler) { // Old Man switch (gBattleStruct->simulatedInputState[0]) @@ -259,13 +264,13 @@ static void SimulateInputChooseAction(u32 battler) } } -static void CompleteOnInactiveTextPrinter(u32 battler) +static void CompleteOnInactiveTextPrinter(enum BattlerId battler) { if (!IsTextPrinterActiveOnWindow(0)) OakOldManBufferExecCompleted(battler); } -static void OakOldManSetBattleEndCallbacks(u32 battler) +static void OakOldManSetBattleEndCallbacks(enum BattlerId battler) { if (!gPaletteFade.active) { @@ -275,14 +280,14 @@ static void OakOldManSetBattleEndCallbacks(u32 battler) } } -void OakOldManHandleInputChooseMove(u32 battler) +void OakOldManHandleInputChooseMove(enum BattlerId battler) { HandleInputChooseMove(battler); if (!(gBattleControllerExecFlags & (1u << battler))) OakOldManBufferExecCompleted(battler); } -static void OpenPartyMenuToChooseMon(u32 battler) +static void OpenPartyMenuToChooseMon(enum BattlerId battler) { if (!gPaletteFade.active) { @@ -296,7 +301,7 @@ static void OpenPartyMenuToChooseMon(u32 battler) } } -static void WaitForMonSelection(u32 battler) +static void WaitForMonSelection(enum BattlerId battler) { if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active) { @@ -308,7 +313,7 @@ static void WaitForMonSelection(u32 battler) } } -static void OpenBagAndChooseItem(u32 battler) +static void OpenBagAndChooseItem(enum BattlerId battler) { if (!gPaletteFade.active) { @@ -322,7 +327,7 @@ static void OpenBagAndChooseItem(u32 battler) } } -static void CompleteWhenChoseItem(u32 battler) +static void CompleteWhenChoseItem(enum BattlerId battler) { if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active) { @@ -341,7 +346,7 @@ static void CompleteWhenChoseItem(u32 battler) } } -static void Intro_TryShinyAnimShowHealthbox(u32 battler) +static void Intro_TryShinyAnimShowHealthbox(enum BattlerId battler) { if (!gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim && !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive) @@ -371,7 +376,7 @@ static void Intro_TryShinyAnimShowHealthbox(u32 battler) } } -static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) +static void Intro_WaitForShinyAnimAndHealthbox(enum BattlerId battler) { bool32 r4 = FALSE; @@ -393,7 +398,7 @@ static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) } } -static void PrintOakText_ForPetesSake(u32 battler) +static void PrintOakText_ForPetesSake(enum BattlerId battler) { u32 mask; @@ -446,7 +451,7 @@ static void PrintOakText_ForPetesSake(u32 battler) case 5: if (!IsTextPrinterActiveOnWindow(B_WIN_OAK_OLD_MAN)) { - mask = (gBitTable[gBattleStruct->simulatedInputState[1]] | gBitTable[gBattleStruct->simulatedInputState[3]]) << 16; + mask = ((1 << gBattleStruct->simulatedInputState[1]) | (1 << gBattleStruct->simulatedInputState[3])) << 16; BeginNormalPaletteFade(mask, 4, 0, @@ -486,32 +491,32 @@ static void PrintOakText_ForPetesSake(u32 battler) } } -void PrintOakText_InflictingDamageIsKey(u32 battler) +void PrintOakText_InflictingDamageIsKey(enum BattlerId battler) { PrintOakTextWithMainBgDarkened(battler, sText_InflictingDamageIsKey, 1); } -static void PrintOakText_LoweringStats(u32 battler) +static void PrintOakText_LoweringStats(enum BattlerId battler) { PrintOakTextWithMainBgDarkened(battler, sText_LoweringStats, 64); } -void PrintOakText_OakNoRunningFromATrainer(u32 battler) +void PrintOakText_OakNoRunningFromATrainer(enum BattlerId battler) { PrintOakTextWithMainBgDarkened(battler, sText_OakNoRunningFromATrainer, 1); } -static void PrintOakText_WinEarnsPrizeMoney(u32 battler) +static void PrintOakText_WinEarnsPrizeMoney(enum BattlerId battler) { PrintOakTextWithMainBgDarkened(battler, sText_WinEarnsPrizeMoney, 64); } -void PrintOakText_HowDisappointing(u32 battler) +void PrintOakText_HowDisappointing(enum BattlerId battler) { PrintOakTextWithMainBgDarkened(battler, sText_HowDissapointing, 64); } -static void PrintOakTextWithMainBgDarkened(u32 battler, const u8 *text, u8 delay) +static void PrintOakTextWithMainBgDarkened(enum BattlerId battler, const u8 *text, u8 delay) { // If delay is 0, it's treated as 256. switch (gBattleStruct->simulatedInputState[0]) @@ -572,7 +577,7 @@ static void PrintOakTextWithMainBgDarkened(u32 battler, const u8 *text, u8 delay } } -static void PrintOakText_KeepAnEyeOnHP(u32 battler) +static void PrintOakText_KeepAnEyeOnHP(enum BattlerId battler) { u32 mask; @@ -593,7 +598,7 @@ static void PrintOakText_KeepAnEyeOnHP(u32 battler) case 1: if (!gPaletteFade.active) { - mask = (gBitTable[gBattleStruct->simulatedInputState[1]] | gBitTable[gBattleStruct->simulatedInputState[3]]) << 16; + mask = ((1 << gBattleStruct->simulatedInputState[1]) | (1 << gBattleStruct->simulatedInputState[3])) << 16; BeginNormalPaletteFade(mask, 4, 8, @@ -617,7 +622,7 @@ static void PrintOakText_KeepAnEyeOnHP(u32 battler) case 4: if (!IsTextPrinterActiveOnWindow(B_WIN_OAK_OLD_MAN)) { - mask = (gBitTable[gBattleStruct->simulatedInputState[1]] | gBitTable[gBattleStruct->simulatedInputState[3]]) << 16; + mask = ((1 << gBattleStruct->simulatedInputState[1]) | (1 << gBattleStruct->simulatedInputState[3])) << 16; BeginNormalPaletteFade(mask, 4, 0, @@ -649,7 +654,7 @@ static void PrintOakText_KeepAnEyeOnHP(u32 battler) } } -void OakOldManBufferExecCompleted(u32 battler) +void OakOldManBufferExecCompleted(enum BattlerId battler) { gBattlerControllerFuncs[battler] = OakOldManBufferRunCommand; if (gBattleTypeFlags & BATTLE_TYPE_LINK) @@ -665,26 +670,26 @@ void OakOldManBufferExecCompleted(u32 battler) } } -static void OakOldManHandleDrawTrainerPic(u32 battler) +static void OakOldManHandleDrawTrainerPic(enum BattlerId battler) { u32 trainerPicId = (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) ? gSaveBlock2Ptr->playerGender + TRAINER_BACK_PIC_RED : TRAINER_BACK_PIC_OLD_MAN; BtlController_HandleDrawTrainerPic(battler, trainerPicId, FALSE, 80, (8 - gTrainerBacksprites[trainerPicId].coordinates.size) * 4 + 80, 30); } -static void OakOldManHandleTrainerSlide(u32 battler) +static void OakOldManHandleTrainerSlide(enum BattlerId battler) { u32 trainerPicId = (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) ? gSaveBlock2Ptr->playerGender + TRAINER_BACK_PIC_RED : TRAINER_BACK_PIC_OLD_MAN; BtlController_HandleTrainerSlide(battler, trainerPicId); } -static void OakOldManHandlePrintString(u32 battler) +static void OakOldManHandlePrintString(enum BattlerId battler) { u16 *stringId; gBattle_BG0_X = 0; gBattle_BG0_Y = 0; stringId = (u16 *)(&gBattleResources->bufferA[battler][2]); - if (gBattleTypeFlags & BATTLE_TYPE_CATCH_TUTORIAL&& *stringId == STRINGID_INTROSENDOUT) + if (gBattleTypeFlags & BATTLE_TYPE_CATCH_TUTORIAL && *stringId == STRINGID_INTROSENDOUT) { OakOldManBufferExecCompleted(battler); } @@ -722,7 +727,7 @@ static void OakOldManHandlePrintString(u32 battler) } } -static void OakOldManHandlePrintSelectionString(u32 battler) +static void OakOldManHandlePrintSelectionString(enum BattlerId battler) { if (GetBattlerSide(battler) == B_SIDE_PLAYER) OakOldManHandlePrintString(battler); @@ -730,7 +735,7 @@ static void OakOldManHandlePrintSelectionString(u32 battler) OakOldManBufferExecCompleted(battler); } -static void HandleChooseActionAfterDma3(u32 battler) +static void HandleChooseActionAfterDma3(enum BattlerId battler) { if (!IsDma3ManagerBusyWithBgCopy()) { @@ -743,7 +748,7 @@ static void HandleChooseActionAfterDma3(u32 battler) } } -static void OakOldManHandleChooseAction(u32 battler) +static void OakOldManHandleChooseAction(enum BattlerId battler) { s32 i; @@ -763,7 +768,7 @@ static void OakOldManHandleChooseAction(u32 battler) BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_ACTION_PROMPT); } -static void OakHandleChooseMove_WaitDma3(u32 battler) +static void OakHandleChooseMove_WaitDma3(enum BattlerId battler) { if (!IsDma3ManagerBusyWithBgCopy()) { @@ -773,7 +778,7 @@ static void OakHandleChooseMove_WaitDma3(u32 battler) } } -static void OakOldManHandleChooseMove(u32 battler) +static void OakOldManHandleChooseMove(enum BattlerId battler) { if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) { @@ -801,7 +806,7 @@ static void OakOldManHandleChooseMove(u32 battler) } } -static void OakOldManHandleChooseItem(u32 battler) +static void OakOldManHandleChooseItem(enum BattlerId battler) { s32 i; @@ -812,7 +817,7 @@ static void OakOldManHandleChooseItem(u32 battler) gBattlePartyCurrentOrder[i] = gBattleResources->bufferA[battler][i + 1]; } -static void OakOldManHandleChoosePokemon(u32 battler) +static void OakOldManHandleChoosePokemon(enum BattlerId battler) { s32 i; @@ -828,13 +833,13 @@ static void OakOldManHandleChoosePokemon(u32 battler) gBattlerInMenuId = battler; } -static void OakOldManHandlePlaySE(u32 battler) +static void OakOldManHandlePlaySE(enum BattlerId battler) { PlaySE(gBattleResources->bufferA[battler][1] | (gBattleResources->bufferA[battler][2] << 8)); OakOldManBufferExecCompleted(battler); } -static void OakOldManHandleFaintingCry(u32 battler) +static void OakOldManHandleFaintingCry(enum BattlerId battler) { u16 species = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES); @@ -842,7 +847,7 @@ static void OakOldManHandleFaintingCry(u32 battler) OakOldManBufferExecCompleted(battler); } -static void OakOldManHandleIntroTrainerBallThrow(u32 battler) +static void OakOldManHandleIntroTrainerBallThrow(enum BattlerId battler) { if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) { @@ -857,7 +862,7 @@ static void OakOldManHandleIntroTrainerBallThrow(u32 battler) } } -static void OakOldManHandleDrawPartyStatusSummary(u32 battler) +static void OakOldManHandleDrawPartyStatusSummary(enum BattlerId battler) { if (gBattleResources->bufferA[battler][1] != 0 && GetBattlerSide(battler) == B_SIDE_PLAYER) @@ -875,14 +880,14 @@ static void OakOldManHandleDrawPartyStatusSummary(u32 battler) } } -static void OakOldManHandleEndBounceEffect(u32 battler) +static void OakOldManHandleEndBounceEffect(enum BattlerId battler) { EndBounceEffect(battler, BOUNCE_HEALTHBOX); EndBounceEffect(battler, BOUNCE_MON); OakOldManBufferExecCompleted(battler); } -static void OakOldManHandleLinkStandbyMsg(u32 battler) +static void OakOldManHandleLinkStandbyMsg(enum BattlerId battler) { switch (gBattleResources->bufferA[battler][1]) { @@ -897,7 +902,7 @@ static void OakOldManHandleLinkStandbyMsg(u32 battler) OakOldManBufferExecCompleted(battler); } -static void OakOldManHandleEndLinkBattle(u32 battler) +static void OakOldManHandleEndLinkBattle(enum BattlerId battler) { gBattleOutcome = gBattleResources->bufferA[battler][1]; FadeOutMapMusic(5); diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 8bd089029..6e94a0052 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -3,20 +3,20 @@ #include "battle_ai_main.h" #include "battle_ai_switch.h" #include "battle_ai_util.h" +#include "constants/battle.h" #include "constants/battle_ai.h" #include "battle_anim.h" -// #include "battle_arena.h" +#include "battle_arena.h" #include "battle_controllers.h" #include "battle_message.h" #include "battle_interface.h" #include "battle_setup.h" #include "battle_special.h" -// #include "battle_special.h" // #include "battle_tv.h" #include "battle_z_move.h" #include "bg.h" #include "data.h" -// #include "frontier_util.h" +#include "frontier_util.h" #include "item.h" #include "link.h" #include "main.h" @@ -46,18 +46,18 @@ #include "test/battle.h" #include "test/test_runner_battle.h" -static void OpponentHandleDrawTrainerPic(u32 battler); -static void OpponentHandleTrainerSlideBack(u32 battler); -static void OpponentHandleChooseAction(u32 battler); -static void OpponentHandleChooseMove(u32 battler); -static void OpponentHandleChooseItem(u32 battler); -static void OpponentHandleChoosePokemon(u32 battler); -static void OpponentHandleIntroTrainerBallThrow(u32 battler); -static void OpponentHandleDrawPartyStatusSummary(u32 battler); -static void OpponentHandleEndLinkBattle(u32 battler); -static void OpponentBufferRunCommand(u32 battler); +static void OpponentHandleDrawTrainerPic(enum BattlerId battler); +static void OpponentHandleTrainerSlideBack(enum BattlerId battler); +static void OpponentHandleChooseAction(enum BattlerId battler); +static void OpponentHandleChooseMove(enum BattlerId battler); +static void OpponentHandleChooseItem(enum BattlerId battler); +static void OpponentHandleChoosePokemon(enum BattlerId battler); +static void OpponentHandleIntroTrainerBallThrow(enum BattlerId battler); +static void OpponentHandleDrawPartyStatusSummary(enum BattlerId battler); +static void OpponentHandleEndLinkBattle(enum BattlerId battler); +static void OpponentBufferRunCommand(enum BattlerId battler); -static void (*const sOpponentBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) = +static void (*const sOpponentBufferCommands[CONTROLLER_CMDS_COUNT])(enum BattlerId battler) = { [CONTROLLER_GETMONDATA] = BtlController_HandleGetMonData, [CONTROLLER_GETRAWMONDATA] = BtlController_HandleGetRawMonData, @@ -114,14 +114,14 @@ static void (*const sOpponentBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) [CONTROLLER_TERMINATOR_NOP] = BtlController_TerminatorNop }; -void SetControllerToOpponent(u32 battler) +void SetControllerToOpponent(enum BattlerId battler) { gBattlerBattleController[battler] = BATTLE_CONTROLLER_OPPONENT; gBattlerControllerEndFuncs[battler] = OpponentBufferExecCompleted; gBattlerControllerFuncs[battler] = OpponentBufferRunCommand; } -static void OpponentBufferRunCommand(u32 battler) +static void OpponentBufferRunCommand(enum BattlerId battler) { if (IsBattleControllerActiveOnLocal(battler)) { @@ -132,7 +132,7 @@ static void OpponentBufferRunCommand(u32 battler) } } -static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) +static void Intro_WaitForShinyAnimAndHealthbox(enum BattlerId battler) { bool8 healthboxAnimDone = FALSE; bool8 twoMons; @@ -197,7 +197,7 @@ static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) } } -static void Intro_TryShinyAnimShowHealthbox(u32 battler) +static void Intro_TryShinyAnimShowHealthbox(enum BattlerId battler) { bool32 bgmRestored = FALSE; bool32 battlerAnimsDone = FALSE; @@ -295,7 +295,7 @@ static void Intro_TryShinyAnimShowHealthbox(u32 battler) } } -void OpponentBufferExecCompleted(u32 battler) +void OpponentBufferExecCompleted(enum BattlerId battler) { gBattlerControllerFuncs[battler] = OpponentBufferRunCommand; if (gBattleTypeFlags & BATTLE_TYPE_LINK) @@ -311,18 +311,36 @@ void OpponentBufferExecCompleted(u32 battler) } } -static u32 OpponentGetTrainerPicId(u32 battlerId) +static u32 OpponentGetTrainerPicId(enum BattlerId battlerId) { enum TrainerPicID trainerPicId; - if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER) + if (gBattleTypeFlags & BATTLE_TYPE_SECRET_BASE) { - trainerPicId = TRAINER_PIC_RED; // placeholder + trainerPicId = GetSecretBaseTrainerPicIndex(); + } + else if (TRAINER_BATTLE_PARAM.opponentA == TRAINER_FRONTIER_BRAIN) + { + trainerPicId = GetFrontierBrainTrainerPicIndex(); } else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER) { trainerPicId = GetTrainerTowerTrainerFrontSpriteId(); } + else if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER) + { + if (gBattleTypeFlags & (BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_TOWER_LINK_MULTI)) + { + if (battlerId == 1) + trainerPicId = GetFrontierTrainerFrontSpriteId(TRAINER_BATTLE_PARAM.opponentA); + else + trainerPicId = GetFrontierTrainerFrontSpriteId(TRAINER_BATTLE_PARAM.opponentB); + } + else + { + trainerPicId = GetFrontierTrainerFrontSpriteId(TRAINER_BATTLE_PARAM.opponentA); + } + } else if (gBattleTypeFlags & BATTLE_TYPE_EREADER_TRAINER) { trainerPicId = GetEreaderTrainerFrontSpriteId(); @@ -342,7 +360,7 @@ static u32 OpponentGetTrainerPicId(u32 battlerId) return trainerPicId; } -static void OpponentHandleDrawTrainerPic(u32 battler) +static void OpponentHandleDrawTrainerPic(enum BattlerId battler) { s16 xPos; enum TrainerPicID trainerPicId; @@ -384,24 +402,24 @@ static void OpponentHandleDrawTrainerPic(u32 battler) BtlController_HandleDrawTrainerPic(battler, trainerPicId, TRUE, xPos, 40, -1); } -void OpponentHandleTrainerSlide(u32 battler) +void OpponentHandleTrainerSlide(enum BattlerId battler) { enum TrainerPicID trainerPicId = OpponentGetTrainerPicId(battler); BtlController_HandleTrainerSlide(battler, trainerPicId); } -static void OpponentHandleTrainerSlideBack(u32 battler) +static void OpponentHandleTrainerSlideBack(enum BattlerId battler) { BtlController_HandleTrainerSlideBack(battler, 35, FALSE); } -static void OpponentHandleChooseAction(u32 battler) +static void OpponentHandleChooseAction(enum BattlerId battler) { AI_TrySwitchOrUseItem(battler); BtlController_Complete(battler); } -static void OpponentHandleChooseMove(u32 battler) +static void OpponentHandleChooseMove(enum BattlerId battler) { u32 chosenMoveIndex; struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); @@ -411,7 +429,7 @@ static void OpponentHandleChooseMove(u32 battler) { if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { - // BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_EXEC_SCRIPT, ChooseMoveAndTargetInBattlePalace(battler)); + BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_EXEC_SCRIPT, ChooseMoveAndTargetInBattlePalace(battler)); } else if (gAiBattleData->actionFlee) { @@ -464,16 +482,18 @@ static void OpponentHandleChooseMove(u32 battler) move = moveInfo->moves[chosenMoveIndex]; } while (move == MOVE_NONE); - enum MoveTarget target = GetBattlerMoveTargetType(battler, move); - if (target == TARGET_USER || target == TARGET_USER_OR_ALLY) + enum MoveTarget moveTarget = GetBattlerMoveTargetType(battler, move); + if (moveTarget == TARGET_USER || moveTarget == TARGET_USER_OR_ALLY) { BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_EXEC_SCRIPT, (chosenMoveIndex) | (battler << 8)); } else if (IsDoubleBattle()) { + enum BattlerId targetBattler; do { - target = GetBattlerAtPosition(Random() & 2); - } while (!CanTargetBattler(battler, target, move)); + enum BattlerPosition pos = RandomPercentage(RNG_WILD_MON_TARGET, 50) ? B_POSITION_PLAYER_LEFT : B_POSITION_PLAYER_RIGHT; + targetBattler = GetBattlerAtPosition(pos); + } while (!CanTargetBattler(battler, targetBattler, move)); // Don't bother to check if they're enemies if the move can't attack ally if (B_WILD_NATURAL_ENEMIES == TRUE && GetBattlerMoveTargetType(battler, move) != TARGET_BOTH) @@ -484,14 +504,14 @@ static void OpponentHandleChooseMove(u32 battler) bool32 isPartnerEnemy = IsNaturalEnemy(speciesAttacker, speciesTarget); - if (isPartnerEnemy && CanTargetBattler(battler, target, move)) + if (isPartnerEnemy && CanTargetBattler(battler, GetBattlerAtPosition(BATTLE_PARTNER(battler)), move)) BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_EXEC_SCRIPT, (chosenMoveIndex) | (GetBattlerAtPosition(BATTLE_PARTNER(battler)) << 8)); else - BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_EXEC_SCRIPT, (chosenMoveIndex) | (target << 8)); + BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_EXEC_SCRIPT, (chosenMoveIndex) | (targetBattler << 8)); } else { - BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_EXEC_SCRIPT, (chosenMoveIndex) | (target << 8)); + BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_EXEC_SCRIPT, (chosenMoveIndex) | (targetBattler << 8)); } } else @@ -501,13 +521,13 @@ static void OpponentHandleChooseMove(u32 battler) } } -static void OpponentHandleChooseItem(u32 battler) +static void OpponentHandleChooseItem(enum BattlerId battler) { BtlController_EmitOneReturnValue(battler, B_COMM_TO_ENGINE, gBattleStruct->chosenItem[battler]); BtlController_Complete(battler); } -static void OpponentHandleChoosePokemon(u32 battler) +static void OpponentHandleChoosePokemon(enum BattlerId battler) { s32 chosenMonId; enum SwitchType switchType = SWITCH_AFTER_KO; @@ -534,7 +554,8 @@ static void OpponentHandleChoosePokemon(u32 battler) chosenMonId = GetMostSuitableMonToSwitchInto(battler, switchType); if (chosenMonId == PARTY_SIZE) // Advanced logic failed so we pick the next available battler { - s32 battler1, battler2, firstId, lastId; + enum BattlerId battler1, battler2; + s32 firstId, lastId; if (!IsDoubleBattle()) { @@ -570,17 +591,17 @@ static void OpponentHandleChoosePokemon(u32 battler) BtlController_Complete(battler); } -static void OpponentHandleIntroTrainerBallThrow(u32 battler) +static void OpponentHandleIntroTrainerBallThrow(enum BattlerId battler) { BtlController_HandleIntroTrainerBallThrow(battler, 0, NULL, 0, Intro_TryShinyAnimShowHealthbox); } -static void OpponentHandleDrawPartyStatusSummary(u32 battler) +static void OpponentHandleDrawPartyStatusSummary(enum BattlerId battler) { BtlController_HandleDrawPartyStatusSummary(battler, B_SIDE_OPPONENT, TRUE); } -static void OpponentHandleEndLinkBattle(u32 battler) +static void OpponentHandleEndLinkBattle(enum BattlerId battler) { if (gBattleTypeFlags & BATTLE_TYPE_LINK && !(gBattleTypeFlags & BATTLE_TYPE_IS_MASTER)) { diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index 1c3322709..cda3c6d14 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -1,9 +1,9 @@ #include "global.h" #include "battle.h" #include "battle_anim.h" -// #include "battle_arena.h" +#include "battle_arena.h" #include "battle_controllers.h" -// #include "battle_dome.h" +#include "battle_dome.h" #include "battle_interface.h" #include "battle_message.h" #include "battle_setup.h" @@ -51,41 +51,41 @@ #include "test/battle.h" #include "test/test_runner_battle.h" -static void PlayerHandleLoadMonSprite(u32 battler); -static void PlayerHandleDrawTrainerPic(u32 battler); -static void PlayerHandleTrainerSlide(u32 battler); -static void PlayerHandleTrainerSlideBack(u32 battler); -static void PlayerHandlePaletteFade(u32 battler); -static void PlayerHandlePause(u32 battler); -static void PlayerHandleChooseAction(u32 battler); -static void PlayerHandleYesNoBox(u32 battler); -static void PlayerHandleChooseItem(u32 battler); -static void PlayerHandleChoosePokemon(u32 battler); -static void PlayerHandleCmd23(u32 battler); -static void PlayerHandleStatusXor(u32 battler); -static void PlayerHandleDMA3Transfer(u32 battler); -static void PlayerHandlePlayBGM(u32 battler); -static void PlayerHandleTwoReturnValues(u32 battler); -static void PlayerHandleChosenMonReturnValue(u32 battler); -static void PlayerHandleOneReturnValue(u32 battler); -static void PlayerHandleOneReturnValue_Duplicate(u32 battler); -static void PlayerHandleIntroTrainerBallThrow(u32 battler); -static void PlayerHandleDrawPartyStatusSummary(u32 battler); -static void PlayerHandleEndBounceEffect(u32 battler); -static void PlayerHandleLinkStandbyMsg(u32 battler); -static void PlayerHandleResetActionMoveSelection(u32 battler); -static void PlayerHandleEndLinkBattle(u32 battler); -static void PlayerHandleBattleDebug(u32 battler); +static void PlayerHandleLoadMonSprite(enum BattlerId battler); +static void PlayerHandleDrawTrainerPic(enum BattlerId battler); +static void PlayerHandleTrainerSlide(enum BattlerId battler); +static void PlayerHandleTrainerSlideBack(enum BattlerId battler); +static void PlayerHandlePaletteFade(enum BattlerId battler); +static void PlayerHandlePause(enum BattlerId battler); +static void PlayerHandleChooseAction(enum BattlerId battler); +static void PlayerHandleYesNoBox(enum BattlerId battler); +static void PlayerHandleChooseItem(enum BattlerId battler); +static void PlayerHandleChoosePokemon(enum BattlerId battler); +static void PlayerHandleCmd23(enum BattlerId battler); +static void PlayerHandleStatusXor(enum BattlerId battler); +static void PlayerHandleDMA3Transfer(enum BattlerId battler); +static void PlayerHandlePlayBGM(enum BattlerId battler); +static void PlayerHandleTwoReturnValues(enum BattlerId battler); +static void PlayerHandleChosenMonReturnValue(enum BattlerId battler); +static void PlayerHandleOneReturnValue(enum BattlerId battler); +static void PlayerHandleOneReturnValue_Duplicate(enum BattlerId battler); +static void PlayerHandleIntroTrainerBallThrow(enum BattlerId battler); +static void PlayerHandleDrawPartyStatusSummary(enum BattlerId battler); +static void PlayerHandleEndBounceEffect(enum BattlerId battler); +static void PlayerHandleLinkStandbyMsg(enum BattlerId battler); +static void PlayerHandleResetActionMoveSelection(enum BattlerId battler); +static void PlayerHandleEndLinkBattle(enum BattlerId battler); +static void PlayerHandleBattleDebug(enum BattlerId battler); -static void PlayerBufferRunCommand(u32 battler); -static void MoveSelectionDisplayPpNumber(u32 battler); -static void MoveSelectionDisplayPpString(u32 battler); -static void MoveSelectionDisplayMoveType(u32 battler); -static void MoveSelectionDisplayMoveNames(u32 battler); -static void TryMoveSelectionDisplayMoveDescription(u32 battler); -static void MoveSelectionDisplayMoveDescription(u32 battler); -static void WaitForMonSelection(u32 battler); -static void CompleteWhenChoseItem(u32 battler); +static void PlayerBufferRunCommand(enum BattlerId battler); +static void MoveSelectionDisplayPpNumber(enum BattlerId battler); +static void MoveSelectionDisplayPpString(enum BattlerId battler); +static void MoveSelectionDisplayMoveType(enum BattlerId battler); +static void MoveSelectionDisplayMoveNames(enum BattlerId battler); +static void TryMoveSelectionDisplayMoveDescription(enum BattlerId battler); +static void MoveSelectionDisplayMoveDescription(enum BattlerId battler); +static void WaitForMonSelection(enum BattlerId battler); +static void CompleteWhenChoseItem(enum BattlerId battler); static void Task_LaunchLvlUpAnim(u8); static void Task_PrepareToGiveExpWithExpBar(u8); static void Task_SetControllerToWaitForString(u8); @@ -94,12 +94,12 @@ static void Task_UpdateLvlInHealthbox(u8); static void PrintLinkStandbyMsg(void); static void Task_CreateLevelUpVerticalStripes(u8 taskId); -static void ReloadMoveNames(u32 battler); -static u32 CheckTypeEffectiveness(u32 battlerAtk, u32 battlerDef); -static u32 CheckTargetTypeEffectiveness(u32 battler); -static void MoveSelectionDisplayMoveEffectiveness(u32 foeEffectiveness, u32 battler); +static void ReloadMoveNames(enum BattlerId battler); +static u32 CheckTypeEffectiveness(enum BattlerId battlerAtk, enum BattlerId battlerDef); +static u32 CheckTargetTypeEffectiveness(enum BattlerId battler); +static void MoveSelectionDisplayMoveEffectiveness(u32 foeEffectiveness, enum BattlerId battler); -static void (*const sPlayerBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) = +static void (*const sPlayerBufferCommands[CONTROLLER_CMDS_COUNT])(enum BattlerId battler) = { [CONTROLLER_GETMONDATA] = BtlController_HandleGetMonData, [CONTROLLER_GETRAWMONDATA] = BtlController_HandleGetRawMonData, @@ -156,7 +156,7 @@ static void (*const sPlayerBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) = [CONTROLLER_TERMINATOR_NOP] = BtlController_TerminatorNop }; -void SetControllerToPlayer(u32 battler) +void SetControllerToPlayer(enum BattlerId battler) { gBattlerBattleController[battler] = BATTLE_CONTROLLER_PLAYER; gBattlerControllerEndFuncs[battler] = PlayerBufferExecCompleted; @@ -165,7 +165,7 @@ void SetControllerToPlayer(u32 battler) gPlayerDpadHoldFrames = 0; } -void PlayerBufferExecCompleted(u32 battler) +void PlayerBufferExecCompleted(enum BattlerId battler) { gBattlerControllerFuncs[battler] = PlayerBufferRunCommand; if (gBattleTypeFlags & BATTLE_TYPE_LINK) @@ -181,7 +181,7 @@ void PlayerBufferExecCompleted(u32 battler) } } -static void PlayerBufferRunCommand(u32 battler) +static void PlayerBufferRunCommand(enum BattlerId battler) { if (IsBattleControllerActiveOnLocal(battler)) { @@ -192,7 +192,7 @@ static void PlayerBufferRunCommand(u32 battler) } } -static void CompleteOnBattlerSpritePosX_0(u32 battler) +static void CompleteOnBattlerSpritePosX_0(enum BattlerId battler) { if (gSprites[gBattlerSpriteIds[battler]].x2 == 0) BtlController_Complete(battler); @@ -232,7 +232,7 @@ static u32 GetNextBall(u32 ballId) return ballId; } -static void HandleInputChooseAction(u32 battler) +static void HandleInputChooseAction(enum BattlerId battler) { enum Item itemId = gBattleResources->bufferA[battler][2] | (gBattleResources->bufferA[battler][3] << 8); @@ -411,12 +411,18 @@ static void HandleInputChooseAction(u32 battler) } } -void HandleInputChooseTarget(u32 battler) +void HandleInputChooseTarget(enum BattlerId battler) { - s32 i; - static const u8 identities[MAX_BATTLERS_COUNT] = {B_POSITION_PLAYER_LEFT, B_POSITION_PLAYER_RIGHT, B_POSITION_OPPONENT_RIGHT, B_POSITION_OPPONENT_LEFT}; + enum BattlerId i; + static const enum BattlerPosition identities[MAX_BATTLERS_COUNT] = + { + B_POSITION_PLAYER_LEFT, + B_POSITION_PLAYER_RIGHT, + B_POSITION_OPPONENT_RIGHT, + B_POSITION_OPPONENT_LEFT, + }; enum Move move = GetMonData(GetBattlerMon(battler), MON_DATA_MOVE1 + gMoveSelectionCursor[battler]); - u16 moveTarget = GetBattlerMoveTargetType(battler, move); + enum MoveTarget moveTarget = GetBattlerMoveTargetType(battler, move); DoBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX, 15, 1); for (i = 0; i < gBattlersCount; i++) @@ -502,7 +508,7 @@ void HandleInputChooseTarget(u32 battler) break; } if (B_SHOW_EFFECTIVENESS) - MoveSelectionDisplayMoveEffectiveness(CheckTypeEffectiveness(battler, GetBattlerPosition(gMultiUsePlayerCursor)), battler); + MoveSelectionDisplayMoveEffectiveness(CheckTypeEffectiveness(battler, gMultiUsePlayerCursor), battler); if (gAbsentBattlerFlags & (1u << gMultiUsePlayerCursor) || !CanTargetBattler(battler, gMultiUsePlayerCursor, move) @@ -555,7 +561,7 @@ void HandleInputChooseTarget(u32 battler) break; } if (B_SHOW_EFFECTIVENESS) - MoveSelectionDisplayMoveEffectiveness(CheckTypeEffectiveness(battler, GetBattlerPosition(gMultiUsePlayerCursor)), battler); + MoveSelectionDisplayMoveEffectiveness(CheckTypeEffectiveness(battler, gMultiUsePlayerCursor), battler); if (gAbsentBattlerFlags & (1u << gMultiUsePlayerCursor) || !CanTargetBattler(battler, gMultiUsePlayerCursor, move) @@ -570,8 +576,7 @@ void HandleInputChooseTarget(u32 battler) static void HideAllTargets(void) { - s32 i; - for (i = 0; i < MAX_BATTLERS_COUNT; i++) + for (enum BattlerId i = 0; i < MAX_BATTLERS_COUNT; i++) { if (IsBattlerAlive(i) && gBattleSpritesDataPtr->healthBoxesData[i].healthboxIsBouncing) { @@ -581,7 +586,7 @@ static void HideAllTargets(void) } } -static void HideShownTargets(u32 battler) +static void HideShownTargets(enum BattlerId battler) { s32 i; for (i = 0; i < MAX_BATTLERS_COUNT; i++) @@ -594,7 +599,7 @@ static void HideShownTargets(u32 battler) } } -void HandleInputShowEntireFieldTargets(u32 battler) +void HandleInputShowEntireFieldTargets(enum BattlerId battler) { if (JOY_HELD(DPAD_ANY) && gSaveBlock2Ptr->optionsButtonMode == OPTIONS_BUTTON_MODE_L_EQUALS_A) gPlayerDpadHoldFrames++; @@ -622,7 +627,7 @@ void HandleInputShowEntireFieldTargets(u32 battler) } } -void HandleInputShowTargets(u32 battler) +void HandleInputShowTargets(enum BattlerId battler) { if (JOY_HELD(DPAD_ANY) && gSaveBlock2Ptr->optionsButtonMode == OPTIONS_BUTTON_MODE_L_EQUALS_A) gPlayerDpadHoldFrames++; @@ -651,7 +656,7 @@ void HandleInputShowTargets(u32 battler) } } -static void TryShowAsTarget(u32 battler) +static void TryShowAsTarget(enum BattlerId battler) { if (IsBattlerAlive(battler)) { @@ -681,7 +686,7 @@ static bool32 CanSelectBattler(enum MoveTarget target) return FALSE; } -void HandleInputChooseMove(u32 battler) +void HandleInputChooseMove(enum BattlerId battler) { u32 canSelectTarget = 0; struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); @@ -737,8 +742,7 @@ void HandleInputChooseMove(u32 battler) // Show all available targets for multi-target moves if (moveTarget == TARGET_ALL_BATTLERS || moveTarget == TARGET_FIELD) { - u32 i = 0; - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) TryShowAsTarget(i); canSelectTarget = 3; @@ -776,7 +780,7 @@ void HandleInputChooseMove(u32 battler) else gMultiUsePlayerCursor = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); if (B_SHOW_EFFECTIVENESS) - MoveSelectionDisplayMoveEffectiveness(CheckTypeEffectiveness(battler, GetBattlerPosition(gMultiUsePlayerCursor)), battler); + MoveSelectionDisplayMoveEffectiveness(CheckTypeEffectiveness(battler, gMultiUsePlayerCursor), battler); gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCB_ShowAsMoveTarget; break; @@ -929,7 +933,7 @@ void HandleInputChooseMove(u32 battler) } } -static void ReloadMoveNames(u32 battler) +static void ReloadMoveNames(enum BattlerId battler) { if (gBattleStruct->zmove.viable && !gBattleStruct->zmove.viewing) { @@ -949,7 +953,7 @@ static void ReloadMoveNames(u32 battler) } } -static u32 UNUSED HandleMoveInputUnused(u32 battler) +static u32 UNUSED HandleMoveInputUnused(enum BattlerId battler) { u32 var = 0; @@ -999,7 +1003,7 @@ static u32 UNUSED HandleMoveInputUnused(u32 battler) return var; } -void HandleMoveSwitching(u32 battler) +void HandleMoveSwitching(enum BattlerId battler) { u8 perMovePPBonuses[MAX_MON_MOVES]; struct ChooseMoveStruct moveStruct; @@ -1199,7 +1203,7 @@ void HandleMoveSwitching(u32 battler) } } -static void SetLinkBattleEndCallbacks(u32 battler) +static void SetLinkBattleEndCallbacks(enum BattlerId battler) { if (gWirelessCommType == 0) { @@ -1230,7 +1234,7 @@ static void SetLinkBattleEndCallbacks(u32 battler) } // Despite handling link battles separately, this is only ever used by link battles -void SetBattleEndCallbacks(u32 battler) +void SetBattleEndCallbacks(enum BattlerId battler) { if (!gPaletteFade.active) { @@ -1256,7 +1260,7 @@ void SetBattleEndCallbacks(u32 battler) } } -static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) +static void Intro_WaitForShinyAnimAndHealthbox(enum BattlerId battler) { bool8 healthboxAnimDone = FALSE; @@ -1294,7 +1298,7 @@ static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) } } -static void Intro_TryShinyAnimShowHealthbox(u32 battler) +static void Intro_TryShinyAnimShowHealthbox(enum BattlerId battler) { bool32 bgmRestored = FALSE; bool32 battlerAnimsDone = FALSE; @@ -1395,7 +1399,7 @@ void Task_PlayerController_RestoreBgmAfterCry(u8 taskId) #define tExpTask_gainedExp_2 data[4] // Stored as two half-words containing a word. #define tExpTask_frames data[10] -static void DynamaxModifyHPLevelUp(struct Pokemon *mon, u32 battler, u32 oldMaxHP) +static void DynamaxModifyHPLevelUp(struct Pokemon *mon, enum BattlerId battler, u32 oldMaxHP) { ApplyDynamaxHPMultiplier(mon); gBattleScripting.levelUpHP = GetMonData(mon, MON_DATA_MAX_HP) - oldMaxHP; // overwrite levelUpHP since it overflows @@ -1411,7 +1415,7 @@ static s32 GetTaskExpValue(u8 taskId) static void Task_GiveExpToMon(u8 taskId) { u32 monId = (u8)(gTasks[taskId].tExpTask_monId); - u8 battler = gTasks[taskId].tExpTask_battler; + enum BattlerId battler = gTasks[taskId].tExpTask_battler; s32 gainedExp = GetTaskExpValue(taskId); if (GetBattlerCoordsIndex(battler) == BATTLE_COORDS_DOUBLES || monId != gBattlerPartyIndexes[battler]) // Give exp without moving the expbar. @@ -1461,7 +1465,7 @@ static void Task_PrepareToGiveExpWithExpBar(u8 taskId) { u8 monIndex = gTasks[taskId].tExpTask_monId; s32 gainedExp = GetTaskExpValue(taskId); - u8 battler = gTasks[taskId].tExpTask_battler; + enum BattlerId battler = gTasks[taskId].tExpTask_battler; struct Pokemon *mon = &gPlayerParty[monIndex]; u8 level = GetMonData(mon, MON_DATA_LEVEL); u16 species = GetMonData(mon, MON_DATA_SPECIES); @@ -1492,7 +1496,7 @@ static void Task_GiveExpWithExpBar(u8 taskId) { u8 monId = gTasks[taskId].tExpTask_monId; s32 gainedExp = GetTaskExpValue(taskId); - u8 battler = gTasks[taskId].tExpTask_battler; + enum BattlerId battler = gTasks[taskId].tExpTask_battler; struct Pokemon *mon = &gPlayerParty[monId]; newExpPoints = MoveBattleBar(battler, gHealthboxSpriteIds[battler], EXP_BAR, 0); @@ -1537,7 +1541,7 @@ static void Task_GiveExpWithExpBar(u8 taskId) static void Task_LaunchLvlUpAnim(u8 taskId) { - u8 battler = gTasks[taskId].tExpTask_battler; + enum BattlerId battler = gTasks[taskId].tExpTask_battler; u8 monIndex = gTasks[taskId].tExpTask_monId; if (IsDoubleBattle() == TRUE && monIndex == gBattlerPartyIndexes[BATTLE_PARTNER(battler)]) @@ -1549,7 +1553,7 @@ static void Task_LaunchLvlUpAnim(u8 taskId) static void Task_UpdateLvlInHealthbox(u8 taskId) { - u8 battler = gTasks[taskId].tExpTask_battler; + enum BattlerId battler = gTasks[taskId].tExpTask_battler; if (!gBattleSpritesDataPtr->healthBoxesData[battler].specialAnimActive) { @@ -1566,16 +1570,16 @@ static void Task_UpdateLvlInHealthbox(u8 taskId) static void Task_SetControllerToWaitForString(u8 taskId) { - u8 battlerId = gTasks[taskId].tExpTask_battler; + enum BattlerId battler = gTasks[taskId].tExpTask_battler; - if (IsBattlerSpriteVisible(battlerId) == TRUE && !gTestRunnerEnabled) + if (IsBattlerSpriteVisible(battler) == TRUE && !gTestRunnerEnabled) { gTasks[taskId].func = Task_CreateLevelUpVerticalStripes; gTasks[taskId].data[15] = 0; } else { - gBattlerControllerFuncs[battlerId] = Controller_WaitForString; + gBattlerControllerFuncs[battler] = Controller_WaitForString; DestroyTask(taskId); } } @@ -1583,10 +1587,10 @@ static void Task_SetControllerToWaitForString(u8 taskId) static void Task_CreateLevelUpVerticalStripes(u8 taskId) { s16 *data = gTasks[taskId].data; - u8 battlerId = tExpTask_battler; - u16 bgPriorityRank = GetBattlerSpriteBGPriorityRank(battlerId); + enum BattlerId battler = tExpTask_battler; + u16 bgPriorityRank = GetBattlerSpriteBGPriorityRank(battler); bool32 isOnBg2 = ((bgPriorityRank ^ 1)) != 0; - struct Sprite *sprite = &gSprites[gBattlerSpriteIds[battlerId]]; + struct Sprite *sprite = &gSprites[gBattlerSpriteIds[battler]]; switch (data[15]) { @@ -1611,12 +1615,7 @@ static void Task_CreateLevelUpVerticalStripes(u8 taskId) } break; case 1: - { - u32 battlerIdAlt = battlerId; - bool32 v6Alt = isOnBg2; - - MoveBattlerSpriteToBG(battlerIdAlt, v6Alt, FALSE); - } + MoveBattlerSpriteToBG(battler, isOnBg2, FALSE); ++data[15]; break; case 2: @@ -1655,13 +1654,13 @@ static void Task_CreateLevelUpVerticalStripes(u8 taskId) gBattle_BG2_X = data[14]; gBattle_BG2_Y = data[13]; } - gBattlerControllerFuncs[battlerId] = Controller_WaitForString; + gBattlerControllerFuncs[battler] = Controller_WaitForString; DestroyTask(taskId); break; } } -static void OpenPartyMenuToChooseMon(u32 battler) +static void OpenPartyMenuToChooseMon(enum BattlerId battler) { if (!gPaletteFade.active) { @@ -1675,7 +1674,7 @@ static void OpenPartyMenuToChooseMon(u32 battler) } } -static void WaitForMonSelection(u32 battler) +static void WaitForMonSelection(enum BattlerId battler) { if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active) { @@ -1691,7 +1690,7 @@ static void WaitForMonSelection(u32 battler) } } -static void OpenBagAndChooseItem(u32 battler) +static void OpenBagAndChooseItem(enum BattlerId battler) { if (!gPaletteFade.active) { @@ -1702,7 +1701,7 @@ static void OpenBagAndChooseItem(u32 battler) } } -static void CompleteWhenChoseItem(u32 battler) +static void CompleteWhenChoseItem(enum BattlerId battler) { if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active) { @@ -1711,7 +1710,7 @@ static void CompleteWhenChoseItem(u32 battler) } } -static void PlayerHandleYesNoInput(u32 battler) +static void PlayerHandleYesNoInput(enum BattlerId battler) { if (JOY_NEW(DPAD_UP) && gMultiUsePlayerCursor != 0) { @@ -1747,7 +1746,7 @@ static void PlayerHandleYesNoInput(u32 battler) } } -static void MoveSelectionDisplayMoveNames(u32 battler) +static void MoveSelectionDisplayMoveNames(enum BattlerId battler) { s32 i; struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); @@ -1767,13 +1766,13 @@ static void MoveSelectionDisplayMoveNames(u32 battler) } } -static void MoveSelectionDisplayPpString(u32 battler) +static void MoveSelectionDisplayPpString(enum BattlerId battler) { StringCopy(gDisplayedStringBattle, gText_MoveInterfacePP); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_PP); } -static void MoveSelectionDisplayPpNumber(u32 battler) +static void MoveSelectionDisplayPpNumber(enum BattlerId battler) { u8 *txtPtr; struct ChooseMoveStruct *moveInfo; @@ -1790,7 +1789,7 @@ static void MoveSelectionDisplayPpNumber(u32 battler) BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_PP_REMAINING); } -static void MoveSelectionDisplayMoveType(u32 battler) +static void MoveSelectionDisplayMoveType(enum BattlerId battler) { u8 *txtPtr, *end; u32 speciesId = gBattleMons[battler].species; @@ -1834,7 +1833,7 @@ static void MoveSelectionDisplayMoveType(u32 battler) BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MOVE_TYPE); } -static void TryMoveSelectionDisplayMoveDescription(u32 battler) +static void TryMoveSelectionDisplayMoveDescription(enum BattlerId battler) { if (!B_SHOW_MOVE_DESCRIPTION) return; @@ -1843,7 +1842,7 @@ static void TryMoveSelectionDisplayMoveDescription(u32 battler) MoveSelectionDisplayMoveDescription(battler); } -static void MoveSelectionDisplayMoveDescription(u32 battler) +static void MoveSelectionDisplayMoveDescription(enum BattlerId battler) { struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct*)(&gBattleResources->bufferA[battler][4]); enum Move move = moveInfo->moves[gMoveSelectionCursor[battler]]; @@ -1955,7 +1954,7 @@ static void PrintLinkStandbyMsg(void) } } -static void PlayerHandleLoadMonSprite(u32 battler) +static void PlayerHandleLoadMonSprite(enum BattlerId battler) { BattleLoadMonSpriteGfx(GetBattlerMon(battler), battler); gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = battler; @@ -1992,7 +1991,7 @@ static enum TrainerPicID PlayerGetTrainerBackPicId(void) // In emerald it's possible to have a tag battle in the battle frontier facilities with AI // which use the front sprite for both the player and the partner as opposed to any other battles (including the one with Steven) // that use an animated back pic. -static void PlayerHandleDrawTrainerPic(u32 battler) +static void PlayerHandleDrawTrainerPic(enum BattlerId battler) { bool32 isFrontPic; s16 xPos, yPos; @@ -2049,24 +2048,24 @@ static void PlayerHandleDrawTrainerPic(u32 battler) BtlController_HandleDrawTrainerPic(battler, trainerPicId, isFrontPic, xPos, yPos, -1); } -static void PlayerHandleTrainerSlide(u32 battler) +static void PlayerHandleTrainerSlide(enum BattlerId battler) { enum TrainerPicID trainerPicId = PlayerGetTrainerBackPicId(); BtlController_HandleTrainerSlide(battler, trainerPicId); } -static void PlayerHandleTrainerSlideBack(u32 battler) +static void PlayerHandleTrainerSlideBack(enum BattlerId battler) { BtlController_HandleTrainerSlideBack(battler, 50, TRUE); } -static void PlayerHandlePaletteFade(u32 battler) +static void PlayerHandlePaletteFade(enum BattlerId battler) { BeginNormalPaletteFade(PALETTES_ALL, 2, 0, 16, RGB_BLACK); BtlController_Complete(battler); } -static void PlayerHandlePause(u32 battler) +static void PlayerHandlePause(enum BattlerId battler) { u8 timer = gBattleResources->bufferA[battler][1]; @@ -2076,7 +2075,7 @@ static void PlayerHandlePause(u32 battler) BtlController_Complete(battler); } -static void HandleChooseActionAfterDma3(u32 battler) +static void HandleChooseActionAfterDma3(enum BattlerId battler) { if (!IsDma3ManagerBusyWithBgCopy()) { @@ -2103,7 +2102,7 @@ static void HandleChooseActionAfterDma3(u32 battler) } } -static void PlayerHandleChooseAction(u32 battler) +static void PlayerHandleChooseAction(enum BattlerId battler) { s32 i; @@ -2119,21 +2118,22 @@ static void PlayerHandleChooseAction(u32 battler) PREPARE_MON_NICK_BUFFER(gBattleTextBuff1, battler, gBattlerPartyIndexes[battler]); BattleStringExpandPlaceholdersToDisplayedString(gText_WhatWillPkmnDo); - if (B_SHOW_PARTNER_TARGET && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && IsBattlerAlive(B_POSITION_PLAYER_RIGHT)) + enum BattlerId partner = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); + if (B_SHOW_PARTNER_TARGET && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && IsBattlerAlive(partner)) { StringCopy(gStringVar1, COMPOUND_STRING("Partner will use:\n")); - enum Move move = GetChosenMoveFromPosition(B_POSITION_PLAYER_RIGHT); + enum Move move = GetBattlerChosenMove(partner); StringAppend(gStringVar1, GetMoveName(move)); - enum MoveTarget moveTarget = GetBattlerMoveTargetType(B_POSITION_PLAYER_RIGHT, move); + enum MoveTarget moveTarget = GetBattlerMoveTargetType(partner, move); if (moveTarget == TARGET_SELECTED || moveTarget == TARGET_SMART) { - if (gAiBattleData->chosenTarget[B_POSITION_PLAYER_RIGHT] == B_POSITION_OPPONENT_LEFT) + if (gAiBattleData->chosenTarget[partner] == B_POSITION_OPPONENT_LEFT) StringAppend(gStringVar1, COMPOUND_STRING(" -{UP_ARROW}")); - else if (gAiBattleData->chosenTarget[B_POSITION_PLAYER_RIGHT] == B_POSITION_OPPONENT_RIGHT) + else if (gAiBattleData->chosenTarget[partner] == B_POSITION_OPPONENT_RIGHT) StringAppend(gStringVar1, COMPOUND_STRING(" {UP_ARROW}-")); - else if (gAiBattleData->chosenTarget[B_POSITION_PLAYER_RIGHT] == B_POSITION_PLAYER_LEFT) + else if (gAiBattleData->chosenTarget[partner] == B_POSITION_PLAYER_LEFT) StringAppend(gStringVar1, COMPOUND_STRING(" {DOWN_ARROW}-")); - else if (gAiBattleData->chosenTarget[B_POSITION_PLAYER_RIGHT] == B_POSITION_PLAYER_RIGHT) + else if (gAiBattleData->chosenTarget[partner] == B_POSITION_PLAYER_RIGHT) StringAppend(gStringVar1, COMPOUND_STRING(" -{DOWN_ARROW}")); } else if (moveTarget == TARGET_USER_AND_ALLY) @@ -2160,7 +2160,7 @@ static void PlayerHandleChooseAction(u32 battler) } } -static void PlayerHandleYesNoBox(u32 battler) +static void PlayerHandleYesNoBox(enum BattlerId battler) { if (IsOnPlayerSide(battler)) { @@ -2176,7 +2176,7 @@ static void PlayerHandleYesNoBox(u32 battler) } } -void HandleChooseMoveAfterDma3(u32 battler) +void HandleChooseMoveAfterDma3(enum BattlerId battler) { if (!IsDma3ManagerBusyWithBgCopy()) { @@ -2188,22 +2188,22 @@ void HandleChooseMoveAfterDma3(u32 battler) // arenaMindPoints is used here as a placeholder for a timer. -// static void PlayerChooseMoveInBattlePalace(u32 battler) -// { -// if (--gBattleStruct->arenaMindPoints[battler] == 0) -// { -// gBattlePalaceMoveSelectionRngValue = gRngValue; -// BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_EXEC_SCRIPT, ChooseMoveAndTargetInBattlePalace(battler)); -// BtlController_Complete(battler); -// } -// } +static void PlayerChooseMoveInBattlePalace(enum BattlerId battler) +{ + if (--gBattleStruct->arenaMindPoints[battler] == 0) + { + gBattlePalaceMoveSelectionRngValue = gRngValue; + BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_EXEC_SCRIPT, ChooseMoveAndTargetInBattlePalace(battler)); + BtlController_Complete(battler); + } +} -void PlayerHandleChooseMove(u32 battler) +void PlayerHandleChooseMove(enum BattlerId battler) { if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { - // gBattleStruct->arenaMindPoints[battler] = 8; - // gBattlerControllerFuncs[battler] = PlayerChooseMoveInBattlePalace; + gBattleStruct->arenaMindPoints[battler] = 8; + gBattlerControllerFuncs[battler] = PlayerChooseMoveInBattlePalace; } else { @@ -2227,7 +2227,7 @@ void PlayerHandleChooseMove(u32 battler) } } -void InitMoveSelectionsVarsAndStrings(u32 battler) +void InitMoveSelectionsVarsAndStrings(enum BattlerId battler) { LoadTypeIcons(battler); MoveSelectionDisplayMoveNames(battler); @@ -2241,7 +2241,7 @@ void InitMoveSelectionsVarsAndStrings(u32 battler) MoveSelectionDisplayMoveType(battler); } -static void PlayerHandleChooseItem(u32 battler) +static void PlayerHandleChooseItem(enum BattlerId battler) { s32 i; @@ -2253,7 +2253,7 @@ static void PlayerHandleChooseItem(u32 battler) gBattlePartyCurrentOrder[i] = gBattleResources->bufferA[battler][1 + i]; } -static void PlayerHandleChoosePokemon(u32 battler) +static void PlayerHandleChoosePokemon(enum BattlerId battler) { s32 i; @@ -2280,14 +2280,14 @@ static void PlayerHandleChoosePokemon(u32 battler) } } -static void PlayerHandleCmd23(u32 battler) +static void PlayerHandleCmd23(enum BattlerId battler) { BattleStopLowHpSound(); BeginNormalPaletteFade(PALETTES_ALL, 2, 0, 16, RGB_BLACK); BtlController_Complete(battler); } -void PlayerHandleExpUpdate(u32 battler) +void PlayerHandleExpUpdate(enum BattlerId battler) { u8 monId = gBattleResources->bufferA[battler][1]; s32 taskId, expPointsToGive; @@ -2315,7 +2315,7 @@ void PlayerHandleExpUpdate(u32 battler) #undef tExpTask_gainedExp_2 #undef tExpTask_frames -static void PlayerHandleStatusXor(u32 battler) +static void PlayerHandleStatusXor(enum BattlerId battler) { u32 val = GetMonData(GetBattlerMon(battler), MON_DATA_STATUS) ^ gBattleResources->bufferA[battler][1]; @@ -2323,7 +2323,7 @@ static void PlayerHandleStatusXor(u32 battler) BtlController_Complete(battler); } -static void PlayerHandleDMA3Transfer(u32 battler) +static void PlayerHandleDMA3Transfer(enum BattlerId battler) { u32 dstArg = gBattleResources->bufferA[battler][1] | (gBattleResources->bufferA[battler][2] << 8) @@ -2350,56 +2350,56 @@ static void PlayerHandleDMA3Transfer(u32 battler) BtlController_Complete(battler); } -static void PlayerHandlePlayBGM(u32 battler) +static void PlayerHandlePlayBGM(enum BattlerId battler) { PlayBGM(gBattleResources->bufferA[battler][1] | (gBattleResources->bufferA[battler][2] << 8)); BtlController_Complete(battler); } -static void PlayerHandleTwoReturnValues(u32 battler) +static void PlayerHandleTwoReturnValues(enum BattlerId battler) { BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_USE_MOVE, 0); BtlController_Complete(battler); } -static void PlayerHandleChosenMonReturnValue(u32 battler) +static void PlayerHandleChosenMonReturnValue(enum BattlerId battler) { BtlController_EmitChosenMonReturnValue(battler, B_COMM_TO_ENGINE, 0, NULL); BtlController_Complete(battler); } -static void PlayerHandleOneReturnValue(u32 battler) +static void PlayerHandleOneReturnValue(enum BattlerId battler) { BtlController_EmitOneReturnValue(battler, B_COMM_TO_ENGINE, 0); BtlController_Complete(battler); } -static void PlayerHandleOneReturnValue_Duplicate(u32 battler) +static void PlayerHandleOneReturnValue_Duplicate(enum BattlerId battler) { BtlController_EmitOneReturnValue_Duplicate(battler, B_COMM_TO_ENGINE, 0); BtlController_Complete(battler); } -static void PlayerHandleIntroTrainerBallThrow(u32 battler) +static void PlayerHandleIntroTrainerBallThrow(enum BattlerId battler) { const u32 paletteIndex = PlayerGetTrainerBackPicId(); // - TRAINER_PIC_FRONT_COUNT; const u16 *trainerPal = gTrainerBacksprites[paletteIndex].palette.data; BtlController_HandleIntroTrainerBallThrow(battler, 0xD6F8, trainerPal, 31, Intro_TryShinyAnimShowHealthbox); } -static void PlayerHandleDrawPartyStatusSummary(u32 battler) +static void PlayerHandleDrawPartyStatusSummary(enum BattlerId battler) { BtlController_HandleDrawPartyStatusSummary(battler, B_SIDE_PLAYER, TRUE); } -static void PlayerHandleEndBounceEffect(u32 battler) +static void PlayerHandleEndBounceEffect(enum BattlerId battler) { EndBounceEffect(battler, BOUNCE_HEALTHBOX); EndBounceEffect(battler, BOUNCE_MON); BtlController_Complete(battler); } -static void PlayerHandleLinkStandbyMsg(u32 battler) +static void PlayerHandleLinkStandbyMsg(enum BattlerId battler) { RecordedBattle_RecordAllBattlerData(&gBattleResources->bufferA[battler][2]); switch (gBattleResources->bufferA[battler][1]) @@ -2418,7 +2418,7 @@ static void PlayerHandleLinkStandbyMsg(u32 battler) BtlController_Complete(battler); } -static void PlayerHandleResetActionMoveSelection(u32 battler) +static void PlayerHandleResetActionMoveSelection(enum BattlerId battler) { switch (gBattleResources->bufferA[battler][1]) { @@ -2436,18 +2436,18 @@ static void PlayerHandleResetActionMoveSelection(u32 battler) BtlController_Complete(battler); } -static void PlayerHandleEndLinkBattle(u32 battler) +static void PlayerHandleEndLinkBattle(enum BattlerId battler) { - // RecordedBattle_RecordAllBattlerData(&gBattleResources->bufferA[battler][4]); + RecordedBattle_RecordAllBattlerData(&gBattleResources->bufferA[battler][4]); gBattleOutcome = gBattleResources->bufferA[battler][1]; - // gSaveBlock2Ptr->frontier.disableRecordBattle = gBattleResources->bufferA[battler][2]; + gSaveBlock2Ptr->frontier.disableRecordBattle = gBattleResources->bufferA[battler][2]; FadeOutMapMusic(5); BeginFastPaletteFade(3); BtlController_Complete(battler); gBattlerControllerFuncs[battler] = SetBattleEndCallbacks; } -static void Controller_WaitForDebug(u32 battler) +static void Controller_WaitForDebug(enum BattlerId battler) { if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active) { @@ -2455,7 +2455,7 @@ static void Controller_WaitForDebug(u32 battler) } } -static void PlayerHandleBattleDebug(u32 battler) +static void PlayerHandleBattleDebug(enum BattlerId battler) { BeginNormalPaletteFade(-1, 0, 0, 0x10, 0); SetMainCallback2(CB2_BattleDebugMenu); @@ -2486,7 +2486,7 @@ static bool32 ShouldShowTypeEffectiveness(u32 targetId) return TRUE; } -static u32 CheckTypeEffectiveness(u32 battlerAtk, u32 battlerDef) +static u32 CheckTypeEffectiveness(enum BattlerId battlerAtk, enum BattlerId battlerDef) { struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battlerAtk][4]); struct BattleContext ctx = {0}; @@ -2514,14 +2514,14 @@ static u32 CheckTypeEffectiveness(u32 battlerAtk, u32 battlerDef) return EFFECTIVENESS_NORMAL; // Normal effectiveness } -static u32 CheckTargetTypeEffectiveness(u32 battler) +static u32 CheckTargetTypeEffectiveness(enum BattlerId battler) { - u32 battlerFoe = BATTLE_OPPOSITE(battler); + enum BattlerId battlerFoe = BATTLE_OPPOSITE(battler); u32 foeEffectiveness = CheckTypeEffectiveness(battler, battlerFoe); if (IsDoubleBattle()) { - u32 partnerFoe = BATTLE_PARTNER(battlerFoe); + enum BattlerId partnerFoe = BATTLE_PARTNER(battlerFoe); u32 partnerFoeEffectiveness = CheckTypeEffectiveness(battler, partnerFoe); if (!IsBattlerAlive(battlerFoe)) return partnerFoeEffectiveness; @@ -2532,7 +2532,7 @@ static u32 CheckTargetTypeEffectiveness(u32 battler) return foeEffectiveness; // fallthrough for any other circumstance } -static void MoveSelectionDisplayMoveEffectiveness(u32 foeEffectiveness, u32 battler) +static void MoveSelectionDisplayMoveEffectiveness(u32 foeEffectiveness, enum BattlerId battler) { static const u8 noIcon[] = _(""); static const u8 effectiveIcon[] = _("{CIRCLE_HOLLOW}"); diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index 523b92cfc..be597ef61 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -8,10 +8,11 @@ #include "battle_message.h" #include "battle_interface.h" #include "battle_setup.h" +#include "battle_tower.h" #include "battle_z_move.h" #include "bg.h" #include "data.h" -// #include "frontier_util.h" +#include "frontier_util.h" #include "item_use.h" #include "link.h" #include "main.h" @@ -35,19 +36,19 @@ #include "test/battle.h" #include "test/test_runner_battle.h" -static void PlayerPartnerHandleDrawTrainerPic(u32 battler); -static void PlayerPartnerHandleTrainerSlide(u32 battler); -static void PlayerPartnerHandleTrainerSlideBack(u32 battler); -static void PlayerPartnerHandleChooseAction(u32 battler); -static void PlayerPartnerHandleChooseMove(u32 battler); -static void PlayerPartnerHandleChoosePokemon(u32 battler); -static void PlayerPartnerHandleIntroTrainerBallThrow(u32 battler); -static void PlayerPartnerHandleDrawPartyStatusSummary(u32 battler); -static void PlayerPartnerHandleEndLinkBattle(u32 battler); +static void PlayerPartnerHandleDrawTrainerPic(enum BattlerId battler); +static void PlayerPartnerHandleTrainerSlide(enum BattlerId battler); +static void PlayerPartnerHandleTrainerSlideBack(enum BattlerId battler); +static void PlayerPartnerHandleChooseAction(enum BattlerId battler); +static void PlayerPartnerHandleChooseMove(enum BattlerId battler); +static void PlayerPartnerHandleChoosePokemon(enum BattlerId battler); +static void PlayerPartnerHandleIntroTrainerBallThrow(enum BattlerId battler); +static void PlayerPartnerHandleDrawPartyStatusSummary(enum BattlerId battler); +static void PlayerPartnerHandleEndLinkBattle(enum BattlerId battler); -static void PlayerPartnerBufferRunCommand(u32 battler); +static void PlayerPartnerBufferRunCommand(enum BattlerId battler); -static void (*const sPlayerPartnerBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) = +static void (*const sPlayerPartnerBufferCommands[CONTROLLER_CMDS_COUNT])(enum BattlerId battler) = { [CONTROLLER_GETMONDATA] = BtlController_HandleGetMonData, [CONTROLLER_GETRAWMONDATA] = BtlController_Empty, @@ -104,14 +105,14 @@ static void (*const sPlayerPartnerBufferCommands[CONTROLLER_CMDS_COUNT])(u32 bat [CONTROLLER_TERMINATOR_NOP] = BtlController_TerminatorNop }; -void SetControllerToPlayerPartner(u32 battler) +void SetControllerToPlayerPartner(enum BattlerId battler) { gBattlerBattleController[battler] = BATTLE_CONTROLLER_PLAYER_PARTNER; gBattlerControllerEndFuncs[battler] = PlayerPartnerBufferExecCompleted; gBattlerControllerFuncs[battler] = PlayerPartnerBufferRunCommand; } -static void PlayerPartnerBufferRunCommand(u32 battler) +static void PlayerPartnerBufferRunCommand(enum BattlerId battler) { if (IsBattleControllerActiveOnLocal(battler)) { @@ -122,7 +123,7 @@ static void PlayerPartnerBufferRunCommand(u32 battler) } } -static void Intro_WaitForHealthbox(u32 battler) +static void Intro_WaitForHealthbox(enum BattlerId battler) { bool32 finished = FALSE; @@ -151,7 +152,7 @@ static void Intro_WaitForHealthbox(u32 battler) } // Also used by the link partner. -void Controller_PlayerPartnerShowIntroHealthbox(u32 battler) +void Controller_PlayerPartnerShowIntroHealthbox(enum BattlerId battler) { if (!gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].ballAnimActive @@ -181,7 +182,7 @@ void Controller_PlayerPartnerShowIntroHealthbox(u32 battler) } } -void PlayerPartnerBufferExecCompleted(u32 battler) +void PlayerPartnerBufferExecCompleted(enum BattlerId battler) { gBattlerControllerFuncs[battler] = PlayerPartnerBufferRunCommand; if (gBattleTypeFlags & BATTLE_TYPE_LINK) @@ -212,7 +213,7 @@ static enum TrainerPicID PlayerPartnerGetTrainerBackPicId(enum DifficultyLevel d // some explanation here // in emerald it's possible to have a tag battle in the battle frontier facilities with AI // which use the front sprite for both the player and the partner as opposed to any other battles (including the one with Steven) that use the back pic as well as animate it -static void PlayerPartnerHandleDrawTrainerPic(u32 battler) +static void PlayerPartnerHandleDrawTrainerPic(enum BattlerId battler) { bool32 isFrontPic; s16 xPos, yPos; @@ -228,7 +229,7 @@ static void PlayerPartnerHandleDrawTrainerPic(u32 battler) } else if (gPartnerTrainerId > TRAINER_PARTNER(PARTNER_NONE)) { - trainerPicId = gBattlePartners[difficulty][gPartnerTrainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerBackPic; + trainerPicId = PlayerPartnerGetTrainerBackPicId(difficulty); xPos = 90; yPos = (8 - gTrainerBacksprites[trainerPicId].coordinates.size) * 4 + 80; } @@ -240,7 +241,7 @@ static void PlayerPartnerHandleDrawTrainerPic(u32 battler) } else { - trainerPicId = TRAINER_PIC_RED; + trainerPicId = GetFrontierTrainerFrontSpriteId(gPartnerTrainerId); xPos = 32; yPos = 80; } @@ -254,32 +255,32 @@ static void PlayerPartnerHandleDrawTrainerPic(u32 battler) BtlController_HandleDrawTrainerPic(battler, trainerPicId, isFrontPic, xPos, yPos, -1); } -static void PlayerPartnerHandleTrainerSlide(u32 battler) +static void PlayerPartnerHandleTrainerSlide(enum BattlerId battler) { enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(gPartnerTrainerId); enum TrainerPicID trainerPicId = PlayerPartnerGetTrainerBackPicId(difficulty); BtlController_HandleTrainerSlide(battler, trainerPicId); } -static void PlayerPartnerHandleTrainerSlideBack(u32 battler) +static void PlayerPartnerHandleTrainerSlideBack(enum BattlerId battler) { BtlController_HandleTrainerSlideBack(battler, 35, FALSE); } -static void PlayerPartnerHandleChooseAction(u32 battler) +static void PlayerPartnerHandleChooseAction(enum BattlerId battler) { AI_TrySwitchOrUseItem(battler); BtlController_Complete(battler); } -static void PlayerPartnerHandleChooseMove(u32 battler) +static void PlayerPartnerHandleChooseMove(enum BattlerId battler) { u32 chosenMoveIndex; struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); chosenMoveIndex = gAiBattleData->chosenMoveIndex[battler]; gBattlerTarget = gAiBattleData->chosenTarget[battler]; - u32 moveTarget = GetBattlerMoveTargetType(battler, moveInfo->moves[chosenMoveIndex]); + enum MoveTarget moveTarget = GetBattlerMoveTargetType(battler, moveInfo->moves[chosenMoveIndex]); if (moveTarget == TARGET_USER || moveTarget == TARGET_USER_OR_ALLY) { @@ -307,7 +308,7 @@ static void PlayerPartnerHandleChooseMove(u32 battler) BtlController_Complete(battler); } -static void PlayerPartnerHandleChoosePokemon(u32 battler) +static void PlayerPartnerHandleChoosePokemon(enum BattlerId battler) { s32 chosenMonId; // Choosing Revival Blessing target @@ -322,8 +323,8 @@ static void PlayerPartnerHandleChoosePokemon(u32 battler) if (chosenMonId == PARTY_SIZE || !IsValidForBattle(&gPlayerParty[chosenMonId])) // just switch to the next mon { s32 firstId = (IsAiVsAiBattle()) ? 0 : (PARTY_SIZE / 2); - u32 battler1 = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); - u32 battler2 = IsDoubleBattle() ? GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT) : battler1; + enum BattlerId battler1 = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + enum BattlerId battler2 = IsDoubleBattle() ? GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT) : battler1; for (chosenMonId = firstId; chosenMonId < PARTY_SIZE; chosenMonId++) { @@ -343,13 +344,14 @@ static void PlayerPartnerHandleChoosePokemon(u32 battler) gBattleStruct->AI_monToSwitchIntoId[battler] = PARTY_SIZE; gBattleStruct->monToSwitchIntoId[battler] = chosenMonId; } - if (TESTING) - TestRunner_Battle_CheckSwitch(battler, chosenMonId); + #if TESTING + TestRunner_Battle_CheckSwitch(battler, chosenMonId); + #endif BtlController_EmitChosenMonReturnValue(battler, B_COMM_TO_ENGINE, chosenMonId, NULL); BtlController_Complete(battler); } -static void PlayerPartnerHandleIntroTrainerBallThrow(u32 battler) +static void PlayerPartnerHandleIntroTrainerBallThrow(enum BattlerId battler) { const u16 *trainerPal; enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(gPartnerTrainerId); @@ -359,17 +361,17 @@ static void PlayerPartnerHandleIntroTrainerBallThrow(u32 battler) else if (IsAiVsAiBattle()) trainerPal = gTrainerSprites[GetTrainerBackPicFromId(gPartnerTrainerId)].palette.data; else - trainerPal = gTrainerSprites[TRAINER_PIC_RED].palette.data; // 2 vs 2 multi battle in Battle Frontier, load front sprite and pal. + trainerPal = gTrainerSprites[GetFrontierTrainerFrontSpriteId(gPartnerTrainerId)].palette.data; // 2 vs 2 multi battle in Battle Frontier, load front sprite and pal. BtlController_HandleIntroTrainerBallThrow(battler, 0xD6F9, trainerPal, 24, Controller_PlayerPartnerShowIntroHealthbox); } -static void PlayerPartnerHandleDrawPartyStatusSummary(u32 battler) +static void PlayerPartnerHandleDrawPartyStatusSummary(enum BattlerId battler) { BtlController_HandleDrawPartyStatusSummary(battler, B_SIDE_PLAYER, TRUE); } -static void PlayerPartnerHandleEndLinkBattle(u32 battler) +static void PlayerPartnerHandleEndLinkBattle(enum BattlerId battler) { gBattleOutcome = gBattleResources->bufferA[battler][1]; FadeOutMapMusic(5); diff --git a/src/battle_controller_pokedude.c b/src/battle_controller_pokedude.c index e5770800f..8cd74fc58 100644 --- a/src/battle_controller_pokedude.c +++ b/src/battle_controller_pokedude.c @@ -27,7 +27,7 @@ struct PokedudeTextScriptHeader u8 btlcmd; u8 side; u16 stringid; - void (*callback)(u32 battler); + void (*callback)(enum BattlerId battler); }; struct PokedudeBattlePartyInfo @@ -40,33 +40,33 @@ struct PokedudeBattlePartyInfo u8 gender; }; -static void PokedudeHandleDrawTrainerPic(u32 battler); -static void PokedudeHandleTrainerSlide(u32 battler); -static void PokedudeHandlePrintSelectionString(u32 battler); -static void PokedudeHandleChooseAction(u32 battler); -static void PokedudeHandleChooseMove(u32 battler); -static void PokedudeHandleChooseItem(u32 battler); -static void PokedudeHandleChoosePokemon(u32 battler); -static void PokedudeHandleStatusXor(u32 battler); -static void PokedudeHandlePlaySE(u32 battler); -static void PokedudeHandleIntroTrainerBallThrow(u32 battler); -static void PokedudeHandleDrawPartyStatusSummary(u32 battler); -static void PokedudeHandleEndBounceEffect(u32 battler); -static void PokedudeHandleLinkStandbyMsg(u32 battler); -static void PokedudeHandleEndLinkBattle(u32 battler); +static void PokedudeHandleDrawTrainerPic(enum BattlerId battler); +static void PokedudeHandleTrainerSlide(enum BattlerId battler); +static void PokedudeHandlePrintSelectionString(enum BattlerId battler); +static void PokedudeHandleChooseAction(enum BattlerId battler); +static void PokedudeHandleChooseMove(enum BattlerId battler); +static void PokedudeHandleChooseItem(enum BattlerId battler); +static void PokedudeHandleChoosePokemon(enum BattlerId battler); +static void PokedudeHandleStatusXor(enum BattlerId battler); +static void PokedudeHandlePlaySE(enum BattlerId battler); +static void PokedudeHandleIntroTrainerBallThrow(enum BattlerId battler); +static void PokedudeHandleDrawPartyStatusSummary(enum BattlerId battler); +static void PokedudeHandleEndBounceEffect(enum BattlerId battler); +static void PokedudeHandleLinkStandbyMsg(enum BattlerId battler); +static void PokedudeHandleEndLinkBattle(enum BattlerId battler); -static void PokedudeAction_PrintVoiceoverMessage(u32 battler); -static void PokedudeAction_PrintMessageWithHealthboxPals(u32 battler); -static void PokedudeSimulateInputChooseAction(u32 battler); -static void PokedudeBufferRunCommand(u32 battler); -static bool8 HandlePokedudeVoiceoverEtc(u32 battler); -static void PokedudeSimulateInputChooseMove(u32 battler); -static void WaitForMonSelection(u32 battler); -static void CompleteWhenChoseItem(u32 battler); -static void Intro_WaitForShinyAnimAndHealthbox(u32 battler); +static void PokedudeAction_PrintVoiceoverMessage(enum BattlerId battler); +static void PokedudeAction_PrintMessageWithHealthboxPals(enum BattlerId battler); +static void PokedudeSimulateInputChooseAction(enum BattlerId battler); +static void PokedudeBufferRunCommand(enum BattlerId battler); +static bool8 HandlePokedudeVoiceoverEtc(enum BattlerId battler); +static void PokedudeSimulateInputChooseMove(enum BattlerId battler); +static void WaitForMonSelection(enum BattlerId battler); +static void CompleteWhenChoseItem(enum BattlerId battler); +static void Intro_WaitForShinyAnimAndHealthbox(enum BattlerId battler); static const u8 *GetPokedudeText(void); -static void (*const sPokedudeBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) = +static void (*const sPokedudeBufferCommands[CONTROLLER_CMDS_COUNT])(enum BattlerId battler) = { [CONTROLLER_GETMONDATA] = BtlController_HandleGetMonData, [CONTROLLER_GETRAWMONDATA] = BtlController_Empty, @@ -128,7 +128,7 @@ static void (*const sPokedudeBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) #define pdScriptNum simulatedInputState[2] #define pdMessageNo simulatedInputState[3] -void SetControllerToPokedude(u32 battler) +void SetControllerToPokedude(enum BattlerId battler) { gBattlerControllerEndFuncs[battler] = PokedudeBufferExecCompleted; gBattlerControllerFuncs[battler] = PokedudeBufferRunCommand; @@ -136,7 +136,7 @@ void SetControllerToPokedude(u32 battler) gBattleStruct->pdMessageNo = 0; } -static void PokedudeBufferRunCommand(u32 battler) +static void PokedudeBufferRunCommand(enum BattlerId battler) { if (gBattleControllerExecFlags & (1u << battler)) { @@ -152,12 +152,12 @@ static void PokedudeBufferRunCommand(u32 battler) } } -static void HandleInputChooseAction(u32 battler) +static void HandleInputChooseAction(enum BattlerId battler) { PokedudeSimulateInputChooseAction(battler); } -static void Pokedude_SetBattleEndCallbacks(u32 battler) +static void Pokedude_SetBattleEndCallbacks(enum BattlerId battler) { if (!gPaletteFade.active) { @@ -167,7 +167,7 @@ static void Pokedude_SetBattleEndCallbacks(u32 battler) } } -static void Intro_DelayAndEnd(u32 battler) +static void Intro_DelayAndEnd(enum BattlerId battler) { if (--gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay == 255) { @@ -176,12 +176,12 @@ static void Intro_DelayAndEnd(u32 battler) } } -static void PokedudeHandleInputChooseMove(u32 battler) +static void PokedudeHandleInputChooseMove(enum BattlerId battler) { PokedudeSimulateInputChooseMove(battler); } -static void OpenPartyMenuToChooseMon(u32 battler) +static void OpenPartyMenuToChooseMon(enum BattlerId battler) { if (!gPaletteFade.active) { @@ -192,7 +192,7 @@ static void OpenPartyMenuToChooseMon(u32 battler) } } -static void WaitForMonSelection(u32 battler) +static void WaitForMonSelection(enum BattlerId battler) { if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active) { @@ -204,7 +204,7 @@ static void WaitForMonSelection(u32 battler) } } -static void OpenBagAndChooseItem(u32 battler) +static void OpenBagAndChooseItem(enum BattlerId battler) { u8 callbackId; @@ -227,7 +227,7 @@ static void OpenBagAndChooseItem(u32 battler) } } -static void CompleteWhenChoseItem(u32 battler) +static void CompleteWhenChoseItem(enum BattlerId battler) { if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active) { @@ -236,7 +236,7 @@ static void CompleteWhenChoseItem(u32 battler) } } -static void Intro_TryShinyAnimShowHealthbox(u32 battler) +static void Intro_TryShinyAnimShowHealthbox(enum BattlerId battler) { if (!gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim && !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive) @@ -266,7 +266,7 @@ static void Intro_TryShinyAnimShowHealthbox(u32 battler) } } -static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) +static void Intro_WaitForShinyAnimAndHealthbox(enum BattlerId battler) { bool32 r4 = FALSE; @@ -288,7 +288,7 @@ static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) } } -void PokedudeBufferExecCompleted(u32 battler) +void PokedudeBufferExecCompleted(enum BattlerId battler) { gBattlerControllerFuncs[battler] = PokedudeBufferRunCommand; if (gBattleTypeFlags & BATTLE_TYPE_LINK) @@ -304,7 +304,7 @@ void PokedudeBufferExecCompleted(u32 battler) } } -static void PokedudeHandleDrawTrainerPic(u32 battler) +static void PokedudeHandleDrawTrainerPic(enum BattlerId battler) { u32 trainerPicId; bool32 isFrontPic; @@ -329,12 +329,12 @@ static void PokedudeHandleDrawTrainerPic(u32 battler) BtlController_HandleDrawTrainerPic(battler, trainerPicId, isFrontPic, xPos, yPos, subpriority); } -static void PokedudeHandleTrainerSlide(u32 battler) +static void PokedudeHandleTrainerSlide(enum BattlerId battler) { BtlController_HandleTrainerSlide(battler, TRAINER_BACK_PIC_POKEDUDE); } -static void PokedudeHandlePrintSelectionString(u32 battler) +static void PokedudeHandlePrintSelectionString(enum BattlerId battler) { if (GetBattlerSide(battler) == B_SIDE_PLAYER) BtlController_HandlePrintString(battler); @@ -342,7 +342,7 @@ static void PokedudeHandlePrintSelectionString(u32 battler) PokedudeBufferExecCompleted(battler); } -static void HandleChooseActionAfterDma3(u32 battler) +static void HandleChooseActionAfterDma3(enum BattlerId battler) { if (!IsDma3ManagerBusyWithBgCopy()) { @@ -352,7 +352,7 @@ static void HandleChooseActionAfterDma3(u32 battler) } } -static void PokedudeHandleChooseAction(u32 battler) +static void PokedudeHandleChooseAction(enum BattlerId battler) { s32 i; @@ -374,7 +374,7 @@ static void PokedudeHandleChooseAction(u32 battler) } } -static void PokedudeHandleChooseMoveAfterDma3(u32 battler) +static void PokedudeHandleChooseMoveAfterDma3(enum BattlerId battler) { if (!IsDma3ManagerBusyWithBgCopy()) { @@ -384,7 +384,7 @@ static void PokedudeHandleChooseMoveAfterDma3(u32 battler) } } -static void PokedudeHandleChooseMove(u32 battler) +static void PokedudeHandleChooseMove(enum BattlerId battler) { if (GetBattlerSide(battler) == B_SIDE_PLAYER) { @@ -397,7 +397,7 @@ static void PokedudeHandleChooseMove(u32 battler) } } -static void PokedudeHandleChooseItem(u32 battler) +static void PokedudeHandleChooseItem(enum BattlerId battler) { s32 i; @@ -408,7 +408,7 @@ static void PokedudeHandleChooseItem(u32 battler) gBattlePartyCurrentOrder[i] = gBattleResources->bufferA[battler][i + 1]; } -static void PokedudeHandleChoosePokemon(u32 battler) +static void PokedudeHandleChoosePokemon(enum BattlerId battler) { s32 i; @@ -425,7 +425,7 @@ static void PokedudeHandleChoosePokemon(u32 battler) } // shared with battle_controller_player -static void PokedudeHandleStatusXor(u32 battler) +static void PokedudeHandleStatusXor(enum BattlerId battler) { struct Pokemon *mon; u8 val; @@ -439,18 +439,18 @@ static void PokedudeHandleStatusXor(u32 battler) PokedudeBufferExecCompleted(battler); } -static void PokedudeHandlePlaySE(u32 battler) +static void PokedudeHandlePlaySE(enum BattlerId battler) { PlaySE(gBattleResources->bufferA[battler][1] | (gBattleResources->bufferA[battler][2] << 8)); PokedudeBufferExecCompleted(battler); } -static void PokedudeHandleIntroTrainerBallThrow(u32 battler) +static void PokedudeHandleIntroTrainerBallThrow(enum BattlerId battler) { BtlController_HandleIntroTrainerBallThrow(battler, 0xD6F8, gTrainerBacksprites[TRAINER_BACK_PIC_POKEDUDE].palette.data, 31, Intro_TryShinyAnimShowHealthbox); } -static void PokedudeHandleDrawPartyStatusSummary(u32 battler) +static void PokedudeHandleDrawPartyStatusSummary(enum BattlerId battler) { if (gBattleResources->bufferA[battler][1] != 0 && GetBattlerSide(battler) == B_SIDE_PLAYER) @@ -468,14 +468,14 @@ static void PokedudeHandleDrawPartyStatusSummary(u32 battler) } } -static void PokedudeHandleEndBounceEffect(u32 battler) +static void PokedudeHandleEndBounceEffect(enum BattlerId battler) { EndBounceEffect(battler, BOUNCE_HEALTHBOX); EndBounceEffect(battler, BOUNCE_MON); PokedudeBufferExecCompleted(battler); } -static void PokedudeHandleLinkStandbyMsg(u32 battler) +static void PokedudeHandleLinkStandbyMsg(enum BattlerId battler) { switch (gBattleResources->bufferA[battler][1]) { @@ -490,7 +490,7 @@ static void PokedudeHandleLinkStandbyMsg(u32 battler) PokedudeBufferExecCompleted(battler); } -static void PokedudeHandleEndLinkBattle(u32 battler) +static void PokedudeHandleEndLinkBattle(enum BattlerId battler) { gBattleOutcome = gBattleResources->bufferA[battler][1]; FadeOutMapMusic(5); @@ -936,7 +936,7 @@ static const struct PokedudeBattlePartyInfo *const sPokedudeBattlePartyPointers[ COMMON_DATA struct PokedudeBattlerState *gPokedudeBattlerStates[MAX_BATTLERS_COUNT] = {0}; -static void PokedudeSimulateInputChooseAction(u32 battler) +static void PokedudeSimulateInputChooseAction(enum BattlerId battler) { const struct PokedudeInputScript *script_p = sInputScripts_ChooseAction[gBattleStruct->pdScriptNum]; @@ -984,7 +984,7 @@ static void PokedudeSimulateInputChooseAction(u32 battler) } } -static void PokedudeSimulateInputChooseMove(u32 battler) +static void PokedudeSimulateInputChooseMove(enum BattlerId battler) { const struct PokedudeInputScript *script_p = sInputScripts_ChooseMove[gBattleStruct->pdScriptNum]; @@ -1015,7 +1015,7 @@ static void PokedudeSimulateInputChooseMove(u32 battler) } } -static bool8 HandlePokedudeVoiceoverEtc(u32 battler) +static bool8 HandlePokedudeVoiceoverEtc(enum BattlerId battler) { const struct PokedudeTextScriptHeader *header_p = sPokedudeTextScripts[gBattleStruct->pdScriptNum]; const u16 * bstringid_p = (const u16 *)&gBattleResources->bufferA[battler][2]; @@ -1038,13 +1038,13 @@ static bool8 HandlePokedudeVoiceoverEtc(u32 battler) return TRUE; } -static void ReturnFromPokedudeAction(u32 battler) +static void ReturnFromPokedudeAction(enum BattlerId battler) { gPokedudeBattlerStates[battler]->timer = 0; gBattlerControllerFuncs[battler] = PokedudeBufferRunCommand; } -static void PokedudeAction_PrintVoiceoverMessage(u32 battler) +static void PokedudeAction_PrintVoiceoverMessage(enum BattlerId battler) { switch (gPokedudeBattlerStates[battler]->timer) { @@ -1093,7 +1093,7 @@ static void PokedudeAction_PrintVoiceoverMessage(u32 battler) } } -static void PokedudeAction_PrintMessageWithHealthboxPals(u32 battler) +static void PokedudeAction_PrintMessageWithHealthboxPals(enum BattlerId battler) { switch (gPokedudeBattlerStates[battler]->timer) { diff --git a/src/battle_controller_recorded_opponent.c b/src/battle_controller_recorded_opponent.c index eb812ae69..a99826837 100644 --- a/src/battle_controller_recorded_opponent.c +++ b/src/battle_controller_recorded_opponent.c @@ -6,10 +6,11 @@ #include "battle_interface.h" #include "battle_message.h" #include "battle_setup.h" +#include "battle_tower.h" // #include "battle_tv.h" #include "bg.h" #include "data.h" -// #include "frontier_util.h" +#include "frontier_util.h" #include "item_menu.h" #include "item_use.h" #include "link.h" @@ -34,20 +35,20 @@ #include "test/battle.h" #include "test/test_runner_battle.h" -static void RecordedOpponentHandleDrawTrainerPic(u32 battler); -static void RecordedOpponentHandleTrainerSlideBack(u32 battler); -static void RecordedOpponentHandleChooseAction(u32 battler); -static void RecordedOpponentHandleChooseMove(u32 battler); -static void RecordedOpponentHandleChooseItem(u32 battler); -static void RecordedOpponentHandleChoosePokemon(u32 battler); -static void RecordedOpponentHandleStatusAnimation(u32 battler); -static void RecordedOpponentHandleIntroTrainerBallThrow(u32 battler); -static void RecordedOpponentHandleDrawPartyStatusSummary(u32 battler); -static void RecordedOpponentHandleEndLinkBattle(u32 battler); +static void RecordedOpponentHandleDrawTrainerPic(enum BattlerId battler); +static void RecordedOpponentHandleTrainerSlideBack(enum BattlerId battler); +static void RecordedOpponentHandleChooseAction(enum BattlerId battler); +static void RecordedOpponentHandleChooseMove(enum BattlerId battler); +static void RecordedOpponentHandleChooseItem(enum BattlerId battler); +static void RecordedOpponentHandleChoosePokemon(enum BattlerId battler); +static void RecordedOpponentHandleStatusAnimation(enum BattlerId battler); +static void RecordedOpponentHandleIntroTrainerBallThrow(enum BattlerId battler); +static void RecordedOpponentHandleDrawPartyStatusSummary(enum BattlerId battler); +static void RecordedOpponentHandleEndLinkBattle(enum BattlerId battler); -static void RecordedOpponentBufferRunCommand(u32 battler); +static void RecordedOpponentBufferRunCommand(enum BattlerId battler); -static void (*const sRecordedOpponentBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) = +static void (*const sRecordedOpponentBufferCommands[CONTROLLER_CMDS_COUNT])(enum BattlerId battler) = { [CONTROLLER_GETMONDATA] = BtlController_HandleGetMonData, [CONTROLLER_GETRAWMONDATA] = BtlController_Empty, @@ -104,14 +105,14 @@ static void (*const sRecordedOpponentBufferCommands[CONTROLLER_CMDS_COUNT])(u32 [CONTROLLER_TERMINATOR_NOP] = BtlController_TerminatorNop }; -void SetControllerToRecordedOpponent(u32 battler) +void SetControllerToRecordedOpponent(enum BattlerId battler) { gBattlerBattleController[battler] = BATTLE_CONTROLLER_RECORDED_OPPONENT; gBattlerControllerEndFuncs[battler] = RecordedOpponentBufferExecCompleted; gBattlerControllerFuncs[battler] = RecordedOpponentBufferRunCommand; } -static void RecordedOpponentBufferRunCommand(u32 battler) +static void RecordedOpponentBufferRunCommand(enum BattlerId battler) { if (IsBattleControllerActiveOnLocal(battler)) { @@ -122,7 +123,7 @@ static void RecordedOpponentBufferRunCommand(u32 battler) } } -void RecordedOpponentBufferExecCompleted(u32 battler) +void RecordedOpponentBufferExecCompleted(enum BattlerId battler) { gBattlerControllerFuncs[battler] = RecordedOpponentBufferRunCommand; if (gBattleTypeFlags & BATTLE_TYPE_LINK) @@ -138,7 +139,7 @@ void RecordedOpponentBufferExecCompleted(u32 battler) } } -static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) +static void Intro_WaitForShinyAnimAndHealthbox(enum BattlerId battler) { bool8 healthboxAnimDone = FALSE; @@ -179,7 +180,7 @@ static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) } } -static void Intro_TryShinyAnimShowHealthbox(u32 battler) +static void Intro_TryShinyAnimShowHealthbox(enum BattlerId battler) { bool32 bgmRestored = FALSE; bool32 battlerAnimsDone = FALSE; @@ -269,15 +270,16 @@ static void Intro_TryShinyAnimShowHealthbox(u32 battler) } } -static void RecordedOpponentHandleDrawTrainerPic(u32 battler) +static void RecordedOpponentHandleDrawTrainerPic(enum BattlerId battler) { s16 xPos; enum TrainerPicID trainerPicId; + enum BattlerPosition position = GetBattlerPosition(battler); // Sets Multibattle test opponent sprites to not be Hiker if (IsMultibattleTest()) { - if (GetBattlerPosition(battler) == B_POSITION_OPPONENT_LEFT) + if (position == B_POSITION_OPPONENT_LEFT) { trainerPicId = TRAINER_PIC_LEAF; if (!(gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)) @@ -293,44 +295,55 @@ static void RecordedOpponentHandleDrawTrainerPic(u32 battler) } else if (gBattleTypeFlags & BATTLE_TYPE_MULTI) { - if ((GetBattlerPosition(battler) & BIT_FLANK) != 0) // second mon + if ((position & BIT_FLANK) != 0) // second mon xPos = 152; else // first mon xPos = 200; if (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) - trainerPicId = TRAINER_PIC_RED; + { + if (position == B_POSITION_OPPONENT_LEFT) + trainerPicId = GetFrontierTrainerFrontSpriteId(TRAINER_BATTLE_PARAM.opponentA); + else + trainerPicId = GetFrontierTrainerFrontSpriteId(TRAINER_BATTLE_PARAM.opponentB); + } else + { trainerPicId = PlayerGenderToFrontTrainerPicId(GetBattlerLinkPlayerGender(battler)); + } } else { xPos = 176; if (TRAINER_BATTLE_PARAM.opponentA == TRAINER_UNION_ROOM) + { trainerPicId = GetUnionRoomTrainerPic(); + } else + { trainerPicId = PlayerGenderToFrontTrainerPicId(gLinkPlayers[gRecordedBattleMultiplayerId ^ BIT_SIDE].gender); + } } BtlController_HandleDrawTrainerPic(battler, trainerPicId, TRUE, xPos, 40, -1); } -static void RecordedOpponentHandleTrainerSlideBack(u32 battler) +static void RecordedOpponentHandleTrainerSlideBack(enum BattlerId battler) { BtlController_HandleTrainerSlideBack(battler, 35, FALSE); } -static void RecordedOpponentHandleChooseAction(u32 battler) +static void RecordedOpponentHandleChooseAction(enum BattlerId battler) { BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, RecordedBattle_GetBattlerAction(RECORDED_ACTION_TYPE, battler), 0); BtlController_Complete(battler); } -static void RecordedOpponentHandleChooseMove(u32 battler) +static void RecordedOpponentHandleChooseMove(enum BattlerId battler) { if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { - // BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_EXEC_SCRIPT, ChooseMoveAndTargetInBattlePalace(battler)); + BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_EXEC_SCRIPT, ChooseMoveAndTargetInBattlePalace(battler)); } else { @@ -342,7 +355,7 @@ static void RecordedOpponentHandleChooseMove(u32 battler) BtlController_Complete(battler); } -static void RecordedOpponentHandleChooseItem(u32 battler) +static void RecordedOpponentHandleChooseItem(enum BattlerId battler) { u8 byte1 = RecordedBattle_GetBattlerAction(RECORDED_ITEM_ID, battler); u8 byte2 = RecordedBattle_GetBattlerAction(RECORDED_ITEM_ID, battler); @@ -353,7 +366,7 @@ static void RecordedOpponentHandleChooseItem(u32 battler) BtlController_Complete(battler); } -static void RecordedOpponentHandleChoosePokemon(u32 battler) +static void RecordedOpponentHandleChoosePokemon(enum BattlerId battler) { gBattleStruct->monToSwitchIntoId[battler] = RecordedBattle_GetBattlerAction(RECORDED_PARTY_INDEX, battler); gSelectedMonPartyId = gBattleStruct->monToSwitchIntoId[battler]; // Revival Blessing @@ -361,22 +374,22 @@ static void RecordedOpponentHandleChoosePokemon(u32 battler) BtlController_Complete(battler); } -static void RecordedOpponentHandleStatusAnimation(u32 battler) +static void RecordedOpponentHandleStatusAnimation(enum BattlerId battler) { BtlController_HandleStatusAnimation(battler); } -static void RecordedOpponentHandleIntroTrainerBallThrow(u32 battler) +static void RecordedOpponentHandleIntroTrainerBallThrow(enum BattlerId battler) { BtlController_HandleIntroTrainerBallThrow(battler, 0, NULL, 0, Intro_TryShinyAnimShowHealthbox); } -static void RecordedOpponentHandleDrawPartyStatusSummary(u32 battler) +static void RecordedOpponentHandleDrawPartyStatusSummary(enum BattlerId battler) { BtlController_HandleDrawPartyStatusSummary(battler, B_SIDE_OPPONENT, TRUE); } -static void RecordedOpponentHandleEndLinkBattle(u32 battler) +static void RecordedOpponentHandleEndLinkBattle(enum BattlerId battler) { if (gBattleResources->bufferA[battler][1] == B_OUTCOME_DREW) gBattleOutcome = gBattleResources->bufferA[battler][1]; diff --git a/src/battle_controller_recorded_partner.c b/src/battle_controller_recorded_partner.c index 2b90c76ee..6c5e89a83 100644 --- a/src/battle_controller_recorded_partner.c +++ b/src/battle_controller_recorded_partner.c @@ -7,10 +7,11 @@ #include "battle_message.h" #include "battle_interface.h" #include "battle_setup.h" +#include "battle_tower.h" #include "battle_z_move.h" #include "bg.h" #include "data.h" -// #include "frontier_util.h" +#include "frontier_util.h" #include "item_use.h" #include "link.h" #include "main.h" @@ -34,18 +35,18 @@ #include "constants/party_menu.h" #include "constants/trainers.h" -static void RecordedPartnerHandleDrawTrainerPic(u32 battler); -static void RecordedPartnerHandleTrainerSlide(u32 battler); -static void RecordedPartnerHandleTrainerSlideBack(u32 battler); -static void RecordedPartnerHandleChooseAction(u32 battler); -static void RecordedPartnerHandleChooseMove(u32 battler); -static void RecordedPartnerHandleChoosePokemon(u32 battler); -static void RecordedPartnerHandleIntroTrainerBallThrow(u32 battler); -static void RecordedPartnerHandleDrawPartyStatusSummary(u32 battler); -static void RecordedPartnerHandleEndLinkBattle(u32 battler); -static void RecordedPartnerBufferRunCommand(u32 battler); +static void RecordedPartnerHandleDrawTrainerPic(enum BattlerId battler); +static void RecordedPartnerHandleTrainerSlide(enum BattlerId battler); +static void RecordedPartnerHandleTrainerSlideBack(enum BattlerId battler); +static void RecordedPartnerHandleChooseAction(enum BattlerId battler); +static void RecordedPartnerHandleChooseMove(enum BattlerId battler); +static void RecordedPartnerHandleChoosePokemon(enum BattlerId battler); +static void RecordedPartnerHandleIntroTrainerBallThrow(enum BattlerId battler); +static void RecordedPartnerHandleDrawPartyStatusSummary(enum BattlerId battler); +static void RecordedPartnerHandleEndLinkBattle(enum BattlerId battler); +static void RecordedPartnerBufferRunCommand(enum BattlerId battler); -static void (*const sRecordedPartnerBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) = +static void (*const sRecordedPartnerBufferCommands[CONTROLLER_CMDS_COUNT])(enum BattlerId battler) = { [CONTROLLER_GETMONDATA] = BtlController_HandleGetMonData, [CONTROLLER_GETRAWMONDATA] = BtlController_Empty, @@ -102,14 +103,14 @@ static void (*const sRecordedPartnerBufferCommands[CONTROLLER_CMDS_COUNT])(u32 b [CONTROLLER_TERMINATOR_NOP] = BtlController_TerminatorNop }; -void SetControllerToRecordedPartner(u32 battler) +void SetControllerToRecordedPartner(enum BattlerId battler) { gBattlerBattleController[battler] = BATTLE_CONTROLLER_RECORDED_PARTNER; gBattlerControllerEndFuncs[battler] = RecordedPartnerBufferExecCompleted; gBattlerControllerFuncs[battler] = RecordedPartnerBufferRunCommand; } -static void RecordedPartnerBufferRunCommand(u32 battler) +static void RecordedPartnerBufferRunCommand(enum BattlerId battler) { if (IsBattleControllerActiveOnLocal(battler)) { @@ -120,7 +121,7 @@ static void RecordedPartnerBufferRunCommand(u32 battler) } } -static void Intro_WaitForHealthbox(u32 battler) +static void Intro_WaitForHealthbox(enum BattlerId battler) { bool32 finished = FALSE; @@ -148,7 +149,7 @@ static void Intro_WaitForHealthbox(u32 battler) } } -void Controller_RecordedPartnerShowIntroHealthbox(u32 battler) +void Controller_RecordedPartnerShowIntroHealthbox(enum BattlerId battler) { if (!gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive && !gBattleSpritesDataPtr->healthBoxesData[BATTLE_PARTNER(battler)].ballAnimActive @@ -178,7 +179,7 @@ void Controller_RecordedPartnerShowIntroHealthbox(u32 battler) } } -void RecordedPartnerBufferExecCompleted(u32 battler) +void RecordedPartnerBufferExecCompleted(enum BattlerId battler) { gBattlerControllerFuncs[battler] = RecordedPartnerBufferRunCommand; if (gBattleTypeFlags & BATTLE_TYPE_LINK) @@ -209,7 +210,7 @@ static enum TrainerPicID RecordedPartnerGetTrainerBackPicId(enum DifficultyLevel // some explanation here // in emerald it's possible to have a tag battle in the battle frontier facilities with AI // which use the front sprite for both the player and the partner as opposed to any other battles (including the one with Steven) that use the back pic as well as animate it -static void RecordedPartnerHandleDrawTrainerPic(u32 battler) +static void RecordedPartnerHandleDrawTrainerPic(enum BattlerId battler) { bool32 isFrontPic; s16 xPos, yPos; @@ -224,25 +225,25 @@ static void RecordedPartnerHandleDrawTrainerPic(u32 battler) BtlController_HandleDrawTrainerPic(battler, trainerPicId, isFrontPic, xPos, yPos, -1); } -static void RecordedPartnerHandleTrainerSlide(u32 battler) +static void RecordedPartnerHandleTrainerSlide(enum BattlerId battler) { enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(gPartnerTrainerId); enum TrainerPicID trainerPicId = RecordedPartnerGetTrainerBackPicId(difficulty); BtlController_HandleTrainerSlide(battler, trainerPicId); } -static void RecordedPartnerHandleTrainerSlideBack(u32 battler) +static void RecordedPartnerHandleTrainerSlideBack(enum BattlerId battler) { BtlController_HandleTrainerSlideBack(battler, 35, FALSE); } -static void RecordedPartnerHandleChooseAction(u32 battler) +static void RecordedPartnerHandleChooseAction(enum BattlerId battler) { BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, RecordedBattle_GetBattlerAction(RECORDED_ACTION_TYPE, battler), 0); BtlController_Complete(battler); } -static void RecordedPartnerHandleChooseMove(u32 battler) +static void RecordedPartnerHandleChooseMove(enum BattlerId battler) { u8 moveIndex = RecordedBattle_GetBattlerAction(RECORDED_MOVE_SLOT, battler); u8 target = RecordedBattle_GetBattlerAction(RECORDED_MOVE_TARGET, battler); @@ -251,7 +252,7 @@ static void RecordedPartnerHandleChooseMove(u32 battler) BtlController_Complete(battler); } -static void RecordedPartnerHandleChoosePokemon(u32 battler) +static void RecordedPartnerHandleChoosePokemon(enum BattlerId battler) { gBattleStruct->monToSwitchIntoId[battler] = RecordedBattle_GetBattlerAction(RECORDED_PARTY_INDEX, battler); gSelectedMonPartyId = gBattleStruct->monToSwitchIntoId[battler]; // Revival Blessing @@ -259,7 +260,7 @@ static void RecordedPartnerHandleChoosePokemon(u32 battler) BtlController_Complete(battler); } -static void RecordedPartnerHandleIntroTrainerBallThrow(u32 battler) +static void RecordedPartnerHandleIntroTrainerBallThrow(enum BattlerId battler) { const u16 *trainerPal; enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(gPartnerTrainerId); @@ -269,17 +270,17 @@ static void RecordedPartnerHandleIntroTrainerBallThrow(u32 battler) else if (IsAiVsAiBattle()) trainerPal = gTrainerSprites[GetTrainerPicFromId(gPartnerTrainerId)].palette.data; else - trainerPal = gTrainerSprites[TRAINER_PIC_RED].palette.data; // 2 vs 2 multi battle in Battle Frontier, load front sprite and pal. + trainerPal = gTrainerSprites[GetFrontierTrainerFrontSpriteId(gPartnerTrainerId)].palette.data; // 2 vs 2 multi battle in Battle Frontier, load front sprite and pal. BtlController_HandleIntroTrainerBallThrow(battler, 0xD6F9, trainerPal, 24, Controller_RecordedPartnerShowIntroHealthbox); } -static void RecordedPartnerHandleDrawPartyStatusSummary(u32 battler) +static void RecordedPartnerHandleDrawPartyStatusSummary(enum BattlerId battler) { BtlController_HandleDrawPartyStatusSummary(battler, B_SIDE_PLAYER, TRUE); } -static void RecordedPartnerHandleEndLinkBattle(u32 battler) +static void RecordedPartnerHandleEndLinkBattle(enum BattlerId battler) { gBattleOutcome = gBattleResources->bufferA[battler][1]; FadeOutMapMusic(5); diff --git a/src/battle_controller_recorded_player.c b/src/battle_controller_recorded_player.c index c9663ac6b..230fee285 100644 --- a/src/battle_controller_recorded_player.c +++ b/src/battle_controller_recorded_player.c @@ -31,19 +31,19 @@ #include "test/battle.h" #include "test/test_runner_battle.h" -static void RecordedPlayerHandleDrawTrainerPic(u32 battler); -static void RecordedPlayerHandleTrainerSlideBack(u32 battler); -static void RecordedPlayerHandleChooseAction(u32 battler); -static void RecordedPlayerHandleChooseMove(u32 battler); -static void RecordedPlayerHandleChooseItem(u32 battler); -static void RecordedPlayerHandleChoosePokemon(u32 battler); -static void RecordedPlayerHandleStatusAnimation(u32 battler); -static void RecordedPlayerHandleIntroTrainerBallThrow(u32 battler); -static void RecordedPlayerHandleDrawPartyStatusSummary(u32 battler); -static void RecordedPlayerHandleEndLinkBattle(u32 battler); -static void RecordedPlayerBufferRunCommand(u32 battler); +static void RecordedPlayerHandleDrawTrainerPic(enum BattlerId battler); +static void RecordedPlayerHandleTrainerSlideBack(enum BattlerId battler); +static void RecordedPlayerHandleChooseAction(enum BattlerId battler); +static void RecordedPlayerHandleChooseMove(enum BattlerId battler); +static void RecordedPlayerHandleChooseItem(enum BattlerId battler); +static void RecordedPlayerHandleChoosePokemon(enum BattlerId battler); +static void RecordedPlayerHandleStatusAnimation(enum BattlerId battler); +static void RecordedPlayerHandleIntroTrainerBallThrow(enum BattlerId battler); +static void RecordedPlayerHandleDrawPartyStatusSummary(enum BattlerId battler); +static void RecordedPlayerHandleEndLinkBattle(enum BattlerId battler); +static void RecordedPlayerBufferRunCommand(enum BattlerId battler); -static void (*const sRecordedPlayerBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) = +static void (*const sRecordedPlayerBufferCommands[CONTROLLER_CMDS_COUNT])(enum BattlerId battler) = { [CONTROLLER_GETMONDATA] = BtlController_HandleGetMonData, [CONTROLLER_GETRAWMONDATA] = BtlController_Empty, @@ -100,14 +100,14 @@ static void (*const sRecordedPlayerBufferCommands[CONTROLLER_CMDS_COUNT])(u32 ba [CONTROLLER_TERMINATOR_NOP] = BtlController_TerminatorNop }; -void SetControllerToRecordedPlayer(u32 battler) +void SetControllerToRecordedPlayer(enum BattlerId battler) { gBattlerBattleController[battler] = BATTLE_CONTROLLER_RECORDED_PLAYER; gBattlerControllerEndFuncs[battler] = RecordedPlayerBufferExecCompleted; gBattlerControllerFuncs[battler] = RecordedPlayerBufferRunCommand; } -static void RecordedPlayerBufferRunCommand(u32 battler) +static void RecordedPlayerBufferRunCommand(enum BattlerId battler) { if (IsBattleControllerActiveOnLocal(battler)) { @@ -118,7 +118,7 @@ static void RecordedPlayerBufferRunCommand(u32 battler) } } -static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) +static void Intro_WaitForShinyAnimAndHealthbox(enum BattlerId battler) { bool32 healthboxAnimDone = FALSE; @@ -184,7 +184,7 @@ static void Intro_WaitForShinyAnimAndHealthbox(u32 battler) } } -static void Intro_TryShinyAnimShowHealthbox(u32 battler) +static void Intro_TryShinyAnimShowHealthbox(enum BattlerId battler) { bool32 bgmRestored = FALSE; @@ -253,7 +253,7 @@ static void Intro_TryShinyAnimShowHealthbox(u32 battler) } } -void RecordedPlayerBufferExecCompleted(u32 battler) +void RecordedPlayerBufferExecCompleted(enum BattlerId battler) { gBattlerControllerFuncs[battler] = RecordedPlayerBufferRunCommand; if (gBattleTypeFlags & BATTLE_TYPE_LINK) @@ -269,7 +269,7 @@ void RecordedPlayerBufferExecCompleted(u32 battler) } } -static void RecordedPlayerHandleDrawTrainerPic(u32 battler) +static void RecordedPlayerHandleDrawTrainerPic(enum BattlerId battler) { bool32 isFrontPic; s16 xPos, yPos; @@ -290,12 +290,12 @@ static void RecordedPlayerHandleDrawTrainerPic(u32 battler) if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK) { if (gBattleTypeFlags & BATTLE_TYPE_MULTI) - trainerPicId = GetBattlerLinkPlayerGender(battler); + trainerPicId = GetBattlerLinkPlayerGender(battler) + TRAINER_BACK_PIC_RED; else - trainerPicId = gLinkPlayers[gRecordedBattleMultiplayerId].gender; + trainerPicId = gLinkPlayers[gRecordedBattleMultiplayerId].gender + TRAINER_BACK_PIC_RED; } else - trainerPicId = gLinkPlayers[0].gender; + trainerPicId = gLinkPlayers[0].gender + TRAINER_BACK_PIC_RED; if (gBattleTypeFlags & BATTLE_TYPE_MULTI) { @@ -330,25 +330,25 @@ static void RecordedPlayerHandleDrawTrainerPic(u32 battler) BtlController_HandleDrawTrainerPic(battler, trainerPicId, isFrontPic, xPos, yPos, -1); } -static void RecordedPlayerHandleTrainerSlideBack(u32 battler) +static void RecordedPlayerHandleTrainerSlideBack(enum BattlerId battler) { BtlController_HandleTrainerSlideBack(battler, 35, FALSE); } -// static void ChooseActionInBattlePalace(u32 battler) -// { -// if (gBattleCommunication[4] >= gBattlersCount / 2) -// { -// BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, RecordedBattle_GetBattlerAction(RECORDED_BATTLE_PALACE_ACTION, battler), 0); -// BtlController_Complete(battler); -// } -// } +static void ChooseActionInBattlePalace(enum BattlerId battler) +{ + if (gBattleCommunication[4] >= gBattlersCount / 2) + { + BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, RecordedBattle_GetBattlerAction(RECORDED_BATTLE_PALACE_ACTION, battler), 0); + BtlController_Complete(battler); + } +} -static void RecordedPlayerHandleChooseAction(u32 battler) +static void RecordedPlayerHandleChooseAction(enum BattlerId battler) { if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { - // gBattlerControllerFuncs[battler] = ChooseActionInBattlePalace; + gBattlerControllerFuncs[battler] = ChooseActionInBattlePalace; } else { @@ -357,11 +357,11 @@ static void RecordedPlayerHandleChooseAction(u32 battler) } } -static void RecordedPlayerHandleChooseMove(u32 battler) +static void RecordedPlayerHandleChooseMove(enum BattlerId battler) { if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { - // BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_EXEC_SCRIPT, ChooseMoveAndTargetInBattlePalace(battler)); + BtlController_EmitTwoReturnValues(battler, B_COMM_TO_ENGINE, B_ACTION_EXEC_SCRIPT, ChooseMoveAndTargetInBattlePalace(battler)); } else { @@ -373,7 +373,7 @@ static void RecordedPlayerHandleChooseMove(u32 battler) BtlController_Complete(battler); } -static void RecordedPlayerHandleChooseItem(u32 battler) +static void RecordedPlayerHandleChooseItem(enum BattlerId battler) { u8 byte1 = RecordedBattle_GetBattlerAction(RECORDED_ITEM_ID, battler); u8 byte2 = RecordedBattle_GetBattlerAction(RECORDED_ITEM_ID, battler); @@ -384,7 +384,7 @@ static void RecordedPlayerHandleChooseItem(u32 battler) BtlController_Complete(battler); } -static void RecordedPlayerHandleChoosePokemon(u32 battler) +static void RecordedPlayerHandleChoosePokemon(enum BattlerId battler) { gBattleStruct->monToSwitchIntoId[battler] = RecordedBattle_GetBattlerAction(RECORDED_PARTY_INDEX, battler); gSelectedMonPartyId = gBattleStruct->monToSwitchIntoId[battler]; // Revival Blessing @@ -392,12 +392,12 @@ static void RecordedPlayerHandleChoosePokemon(u32 battler) BtlController_Complete(battler); } -static void RecordedPlayerHandleStatusAnimation(u32 battler) +static void RecordedPlayerHandleStatusAnimation(enum BattlerId battler) { BtlController_HandleStatusAnimation(battler); } -static void RecordedPlayerHandleIntroTrainerBallThrow(u32 battler) +static void RecordedPlayerHandleIntroTrainerBallThrow(enum BattlerId battler) { enum TrainerPicID trainerPicId; const u16 *trainerPal; @@ -411,12 +411,12 @@ static void RecordedPlayerHandleIntroTrainerBallThrow(u32 battler) BtlController_HandleIntroTrainerBallThrow(battler, 0xD6F9, trainerPal, 24, Intro_TryShinyAnimShowHealthbox); } -static void RecordedPlayerHandleDrawPartyStatusSummary(u32 battler) +static void RecordedPlayerHandleDrawPartyStatusSummary(enum BattlerId battler) { BtlController_HandleDrawPartyStatusSummary(battler, B_SIDE_PLAYER, TRUE); } -static void RecordedPlayerHandleEndLinkBattle(u32 battler) +static void RecordedPlayerHandleEndLinkBattle(enum BattlerId battler) { gBattleOutcome = gBattleResources->bufferA[battler][1]; FadeOutMapMusic(5); diff --git a/src/battle_controller_safari.c b/src/battle_controller_safari.c index c698e4ec4..f497d5398 100644 --- a/src/battle_controller_safari.c +++ b/src/battle_controller_safari.c @@ -29,18 +29,18 @@ #include "constants/trainers.h" #include "constants/rgb.h" -static void SafariHandleDrawTrainerPic(u32 battler); -static void SafariHandleChooseAction(u32 battler); -static void SafariHandleChooseItem(u32 battler); -static void SafariHandleStatusIconUpdate(u32 battler); -static void SafariHandleFaintingCry(u32 battler); -static void SafariHandleIntroTrainerBallThrow(u32 battler); -static void SafariHandleEndLinkBattle(u32 battler); +static void SafariHandleDrawTrainerPic(enum BattlerId battler); +static void SafariHandleChooseAction(enum BattlerId battler); +static void SafariHandleChooseItem(enum BattlerId battler); +static void SafariHandleStatusIconUpdate(enum BattlerId battler); +static void SafariHandleFaintingCry(enum BattlerId battler); +static void SafariHandleIntroTrainerBallThrow(enum BattlerId battler); +static void SafariHandleEndLinkBattle(enum BattlerId battler); -static void SafariBufferRunCommand(u32 battler); -static void CompleteWhenChosePokeblock(u32 battler); +static void SafariBufferRunCommand(enum BattlerId battler); +static void CompleteWhenChosePokeblock(enum BattlerId battler); -static void (*const sSafariBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) = +static void (*const sSafariBufferCommands[CONTROLLER_CMDS_COUNT])(enum BattlerId battler) = { [CONTROLLER_GETMONDATA] = BtlController_Empty, [CONTROLLER_GETRAWMONDATA] = BtlController_Empty, @@ -100,14 +100,14 @@ static void (*const sSafariBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) = static const u8 sText_WhatWillPlayerThrow[] = _("What will {B_PLAYER_NAME}\nthrow?"); static const u8 sText_SafariZoneMenu[] = _("{PALETTE 5}{COLOR_HIGHLIGHT_SHADOW 13 14 15}BALL{CLEAR_TO 56}BAIT\nROCK{CLEAR_TO 56}RUN"); -void SetControllerToSafari(u32 battler) +void SetControllerToSafari(enum BattlerId battler) { gBattlerBattleController[battler] = BATTLE_CONTROLLER_SAFARI; gBattlerControllerEndFuncs[battler] = SafariBufferExecCompleted; gBattlerControllerFuncs[battler] = SafariBufferRunCommand; } -static void SafariBufferRunCommand(u32 battler) +static void SafariBufferRunCommand(enum BattlerId battler) { if (IsBattleControllerActiveOnLocal(battler)) { @@ -118,7 +118,7 @@ static void SafariBufferRunCommand(u32 battler) } } -static void HandleInputChooseAction(u32 battler) +static void HandleInputChooseAction(enum BattlerId battler) { if (JOY_NEW(A_BUTTON)) { @@ -190,13 +190,13 @@ static void HandleInputChooseAction(u32 battler) } } -static void Controller_WaitForHealthbox(u32 battler) +static void Controller_WaitForHealthbox(enum BattlerId battler) { if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy) BtlController_Complete(battler); } -static void SafariSetBattleEndCallbacks(u32 battler) +static void SafariSetBattleEndCallbacks(enum BattlerId battler) { if (!gPaletteFade.active) { @@ -206,7 +206,7 @@ static void SafariSetBattleEndCallbacks(u32 battler) } } -static void SafariOpenPokeblockCase(u32 battler) +static void SafariOpenPokeblockCase(enum BattlerId battler) { if (!gPaletteFade.active) { @@ -214,7 +214,7 @@ static void SafariOpenPokeblockCase(u32 battler) } } -static void CompleteWhenChosePokeblock(u32 battler) +static void CompleteWhenChosePokeblock(enum BattlerId battler) { if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active) { @@ -223,7 +223,7 @@ static void CompleteWhenChosePokeblock(u32 battler) } } -void SafariBufferExecCompleted(u32 battler) +void SafariBufferExecCompleted(enum BattlerId battler) { gBattlerControllerFuncs[battler] = SafariBufferRunCommand; if (gBattleTypeFlags & BATTLE_TYPE_LINK) @@ -239,7 +239,7 @@ void SafariBufferExecCompleted(u32 battler) } } -static void SafariHandleDrawTrainerPic(u32 battler) +static void SafariHandleDrawTrainerPic(enum BattlerId battler) { enum TrainerPicID trainerPicId = gSaveBlock2Ptr->playerGender + TRAINER_BACK_PIC_RED; @@ -248,7 +248,7 @@ static void SafariHandleDrawTrainerPic(u32 battler) 30); } -static void HandleChooseActionAfterDma3(u32 battler) +static void HandleChooseActionAfterDma3(enum BattlerId battler) { if (!IsDma3ManagerBusyWithBgCopy()) { @@ -258,7 +258,7 @@ static void HandleChooseActionAfterDma3(u32 battler) } } -static void SafariHandleChooseAction(u32 battler) +static void SafariHandleChooseAction(enum BattlerId battler) { s32 i; @@ -274,14 +274,14 @@ static void SafariHandleChooseAction(u32 battler) BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_ACTION_PROMPT); } -static void SafariHandleChooseItem(u32 battler) +static void SafariHandleChooseItem(enum BattlerId battler) { BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 0x10, RGB_BLACK); gBattlerControllerFuncs[battler] = SafariOpenPokeblockCase; gBattlerInMenuId = battler; } -static void SafariHandleStatusIconUpdate(u32 battler) +static void SafariHandleStatusIconUpdate(enum BattlerId battler) { UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], GetBattlerMon(battler), HEALTHBOX_SAFARI_BALLS_TEXT); BtlController_Complete(battler); @@ -289,7 +289,7 @@ static void SafariHandleStatusIconUpdate(u32 battler) // All of the other controllers(except Wally's) use CRY_MODE_FAINT. // Player is not a pokemon, so it can't really faint in the Safari anyway. -static void SafariHandleFaintingCry(u32 battler) +static void SafariHandleFaintingCry(enum BattlerId battler) { u16 species = GetMonData(GetBattlerMon(battler), MON_DATA_SPECIES); @@ -297,7 +297,7 @@ static void SafariHandleFaintingCry(u32 battler) BtlController_Complete(battler); } -static void SafariHandleIntroTrainerBallThrow(u32 battler) +static void SafariHandleIntroTrainerBallThrow(enum BattlerId battler) { UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], GetBattlerMon(battler), HEALTHBOX_SAFARI_ALL_TEXT); StartHealthboxSlideIn(battler); @@ -305,7 +305,7 @@ static void SafariHandleIntroTrainerBallThrow(u32 battler) gBattlerControllerFuncs[battler] = Controller_WaitForHealthbox; } -static void SafariHandleEndLinkBattle(u32 battler) +static void SafariHandleEndLinkBattle(enum BattlerId battler) { gBattleOutcome = gBattleResources->bufferA[battler][1]; FadeOutMapMusic(5); diff --git a/src/battle_controllers.c b/src/battle_controllers.c index 01f849744..eab9a479b 100644 --- a/src/battle_controllers.c +++ b/src/battle_controllers.c @@ -3,7 +3,7 @@ #include "battle_ai_main.h" #include "battle_ai_util.h" #include "battle_anim.h" -// #include "battle_arena.h" +#include "battle_arena.h" #include "battle_controllers.h" #include "battle_gfx_sfx_util.h" #include "battle_interface.h" @@ -39,9 +39,9 @@ static EWRAM_DATA u8 sLinkSendTaskId = 0; static EWRAM_DATA u8 sLinkReceiveTaskId = 0; -COMMON_DATA void (*gBattlerControllerFuncs[MAX_BATTLERS_COUNT])(u32 battler) = {0}; +COMMON_DATA void (*gBattlerControllerFuncs[MAX_BATTLERS_COUNT])(enum BattlerId battler) = {0}; COMMON_DATA u8 gBattleControllerData[MAX_BATTLERS_COUNT] = {0}; // Used by the battle controllers to store misc sprite/task IDs for each battler -COMMON_DATA void (*gBattlerControllerEndFuncs[MAX_BATTLERS_COUNT])(u32 battler) = {0}; // Controller's buffer complete function for each battler +COMMON_DATA void (*gBattlerControllerEndFuncs[MAX_BATTLERS_COUNT])(enum BattlerId battler) = {0}; // Controller's buffer complete function for each battler u8 gBattlerBattleController[MAX_BATTLERS_COUNT] = {0}; // Battle controller for each battler static void CreateTasksForSendRecvLinkBuffers(void); @@ -53,8 +53,8 @@ static void Task_StartSendOutAnim(u8 taskId); static void SpriteCB_FreePlayerSpriteLoadMonSprite(struct Sprite *sprite); static void SpriteCB_FreeOpponentSprite(struct Sprite *sprite); static u32 ReturnAnimIdForBattler(bool32 isPlayerSide, u32 specificBattler); -static void LaunchKOAnimation(u32 battlerId, u16 animId, bool32 isFront); -static void AnimateMonAfterKnockout(u32 battler); +static void LaunchKOAnimation(enum BattlerId battlerId, u16 animId, bool32 isFront); +static void AnimateMonAfterKnockout(enum BattlerId battler); bool32 IsAiVsAiBattle(void) @@ -62,52 +62,52 @@ bool32 IsAiVsAiBattle(void) return (B_FLAG_AI_VS_AI_BATTLE && FlagGet(B_FLAG_AI_VS_AI_BATTLE)); } -bool32 BattlerIsPlayer(u32 battlerId) +bool32 BattlerIsPlayer(enum BattlerId battlerId) { return (gBattlerBattleController[battlerId] == BATTLE_CONTROLLER_PLAYER || gBattlerBattleController[battlerId] == BATTLE_CONTROLLER_RECORDED_PLAYER); } -bool32 BattlerIsPartner(u32 battlerId) +bool32 BattlerIsPartner(enum BattlerId battlerId) { return (gBattlerBattleController[battlerId] == BATTLE_CONTROLLER_PLAYER_PARTNER || gBattlerBattleController[battlerId] == BATTLE_CONTROLLER_RECORDED_PARTNER || gBattlerBattleController[battlerId] == BATTLE_CONTROLLER_LINK_PARTNER); } -bool32 BattlerIsOpponent(u32 battlerId) +bool32 BattlerIsOpponent(enum BattlerId battlerId) { return (gBattlerBattleController[battlerId] == BATTLE_CONTROLLER_OPPONENT || gBattlerBattleController[battlerId] == BATTLE_CONTROLLER_RECORDED_OPPONENT || gBattlerBattleController[battlerId] == BATTLE_CONTROLLER_LINK_OPPONENT); } -bool32 BattlerIsRecorded(u32 battlerId) +bool32 BattlerIsRecorded(enum BattlerId battlerId) { return (gBattlerBattleController[battlerId] == BATTLE_CONTROLLER_RECORDED_PLAYER || gBattlerBattleController[battlerId] == BATTLE_CONTROLLER_RECORDED_PARTNER || gBattlerBattleController[battlerId] == BATTLE_CONTROLLER_RECORDED_OPPONENT); } -bool32 BattlerIsLink(u32 battlerId) +bool32 BattlerIsLink(enum BattlerId battlerId) { return (gBattlerBattleController[battlerId] == BATTLE_CONTROLLER_LINK_PARTNER || gBattlerBattleController[battlerId] == BATTLE_CONTROLLER_LINK_OPPONENT); } -bool32 BattlerIsWally(u32 battlerId) +bool32 BattlerIsOldMan(enum BattlerId battlerId) { - return (gBattlerBattleController[battlerId] == BATTLE_CONTROLLER_WALLY); + return (gBattlerBattleController[battlerId] == BATTLE_CONTROLLER_OAK_OLD_MAN); } -bool32 BattlerHasAi(u32 battlerId) +bool32 BattlerHasAi(enum BattlerId battlerId) { switch (gBattlerBattleController[battlerId]) { case BATTLE_CONTROLLER_OPPONENT: case BATTLE_CONTROLLER_PLAYER_PARTNER: case BATTLE_CONTROLLER_SAFARI: - case BATTLE_CONTROLLER_WALLY: + case BATTLE_CONTROLLER_OAK_OLD_MAN: return TRUE; default: break; @@ -151,21 +151,10 @@ void SetUpBattleVars(void) ClearBattleAnimationVars(); BattleAI_SetupItems(); BattleAI_SetupFlags(); - - if (!IS_FRLG && gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) - { - ZeroEnemyPartyMons(); - CreateMonWithIVs(&gEnemyParty[0], SPECIES_ZIGZAGOON, 2, 0, OTID_STRUCT_PLAYER_ID, USE_RANDOM_IVS); - GiveMonInitialMoveset(&gEnemyParty[0]); - i = ITEM_NONE; - SetMonData(&gEnemyParty[0], MON_DATA_HELD_ITEM, &i); - } } void InitBattleControllers(void) { - s32 i; - if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED)) RecordedBattle_Init(B_RECORD_MODE_RECORDING); else @@ -180,8 +169,8 @@ void InitBattleControllers(void) if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI)) { - for (i = 0; i < gBattlersCount; i++) - BufferBattlePartyCurrentOrderBySide(i, 0); + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) + BufferBattlePartyCurrentOrderBySide(battler, 0); } // for (i = 0; i < sizeof(gBattleStruct->tvMovePoints); i++) @@ -426,27 +415,27 @@ bool32 IsValidForBattleButDead(struct Pokemon *mon) && GetMonData(mon, MON_DATA_IS_EGG) == FALSE); } -static inline bool32 IsControllerPlayer(u32 battler) +static inline bool32 IsControllerPlayer(enum BattlerId battler) { return (gBattlerControllerEndFuncs[battler] == PlayerBufferExecCompleted); } -static inline bool32 IsControllerRecordedPlayer(u32 battler) +static inline bool32 IsControllerRecordedPlayer(enum BattlerId battler) { return (gBattlerControllerEndFuncs[battler] == RecordedPlayerBufferExecCompleted); } -static inline bool32 IsControllerRecordedPartner(u32 battler) +static inline bool32 IsControllerRecordedPartner(enum BattlerId battler) { return (gBattlerControllerEndFuncs[battler] == RecordedPartnerBufferExecCompleted); } -static inline bool32 IsControllerOpponent(u32 battler) +static inline bool32 IsControllerOpponent(enum BattlerId battler) { return (gBattlerControllerEndFuncs[battler] == OpponentBufferExecCompleted); } -static inline bool32 IsControllerPlayerPartner(u32 battler) +static inline bool32 IsControllerPlayerPartner(enum BattlerId battler) { return (gBattlerControllerEndFuncs[battler] == PlayerPartnerBufferExecCompleted); } @@ -461,35 +450,33 @@ static inline bool32 IsControllerOakOldMan(u32 battler) return (gBattlerControllerEndFuncs[battler] == OakOldManBufferExecCompleted); } -static inline bool32 IsControllerRecordedOpponent(u32 battler) +static inline bool32 IsControllerRecordedOpponent(enum BattlerId battler) { return (gBattlerControllerEndFuncs[battler] == RecordedOpponentBufferExecCompleted); } -static inline bool32 IsControllerLinkOpponent(u32 battler) +static inline bool32 IsControllerLinkOpponent(enum BattlerId battler) { return (gBattlerControllerEndFuncs[battler] == LinkOpponentBufferExecCompleted); } -static inline bool32 IsControllerLinkPartner(u32 battler) +static inline bool32 IsControllerLinkPartner(enum BattlerId battler) { return (gBattlerControllerEndFuncs[battler] == LinkPartnerBufferExecCompleted); } -static inline bool32 IsControllerSafari(u32 battler) +static inline bool32 IsControllerSafari(enum BattlerId battler) { return (gBattlerControllerEndFuncs[battler] == SafariBufferExecCompleted); } static void SetBattlePartyIds(void) { - s32 i, j; - if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI)) { - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { - for (j = 0; j < PARTY_SIZE; j++) + for (u32 j = 0; j < PARTY_SIZE; j++) { if (i < 2) { @@ -534,7 +521,7 @@ static void SetBattlePartyIds(void) } } -static void PrepareBufferDataTransfer(u32 battler, u32 bufferId, u8 *data, u16 size) +static void PrepareBufferDataTransfer(enum BattlerId battler, u32 bufferId, u8 *data, u16 size) { s32 i; @@ -610,7 +597,7 @@ enum // We want to send a message. Place it into the "send" buffer. // First argument is a BATTLELINKCOMMTYPE_ -void PrepareBufferDataTransferLink(u32 battler, u32 bufferId, u16 size, u8 *data) +void PrepareBufferDataTransferLink(enum BattlerId battler, u32 bufferId, u16 size, u8 *data) { s32 alignedSize; s32 i; @@ -669,25 +656,30 @@ static void Task_HandleSendLinkBuffersData(u8 taskId) gTasks[taskId].tInitialDelayTimer--; if (gTasks[taskId].tInitialDelayTimer == 0) gTasks[taskId].tState++; - if (gReceivedRemoteLinkPlayers) - gTasks[taskId].tState = SENDTASK_STATE_BEGIN_SEND_BLOCK; break; case SENDTASK_STATE_COUNT_PLAYERS: - if (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) - numPlayers = 2; - else - numPlayers = (gBattleTypeFlags & BATTLE_TYPE_MULTI) ? 4 : 2; - - if (GetLinkPlayerCount_2() >= numPlayers) + if (gWirelessCommType) { - if (IsLinkMaster()) - { - CheckShouldAdvanceLinkState(); - gTasks[taskId].tState++; - } + gTasks[taskId].tState++; + } + else + { + if (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) + numPlayers = 2; else + numPlayers = (gBattleTypeFlags & BATTLE_TYPE_MULTI) ? 4 : 2; + + if (GetLinkPlayerCount_2() >= numPlayers) { - gTasks[taskId].tState++; + if (IsLinkMaster()) + { + CheckShouldAdvanceLinkState(); + gTasks[taskId].tState++; + } + else + { + gTasks[taskId].tState++; + } } } break; @@ -748,7 +740,7 @@ void TryReceiveLinkBattleData(void) s32 j; u8 *recvBuffer; - if (gReceivedRemoteLinkPlayers && (gBattleTypeFlags & BATTLE_TYPE_LINK_IN_BATTLE) && (gLinkPlayers[0].linkType == 0x2211)) + if (gReceivedRemoteLinkPlayers && (gBattleTypeFlags & BATTLE_TYPE_LINK_IN_BATTLE)) { DestroyTask_RfuIdle(); for (i = 0; i < GetLinkPlayerCount(); i++) @@ -783,7 +775,7 @@ void TryReceiveLinkBattleData(void) static void Task_HandleCopyReceivedLinkBuffersData(u8 taskId) { u16 blockSize; - u8 battler; + enum BattlerId battler; u8 playerId; #define BYTE_TO_RECEIVE(offset) \ @@ -839,7 +831,7 @@ static void Task_HandleCopyReceivedLinkBuffersData(u8 taskId) #undef tCurrentBlock_End #undef tCurrentBlock_Start -void BtlController_EmitGetMonData(u32 battler, u32 bufferId, u8 requestId, u8 monToCheck) +void BtlController_EmitGetMonData(enum BattlerId battler, u32 bufferId, u8 requestId, u8 monToCheck) { gBattleResources->transferBuffer[0] = CONTROLLER_GETMONDATA; gBattleResources->transferBuffer[1] = requestId; @@ -848,7 +840,7 @@ void BtlController_EmitGetMonData(u32 battler, u32 bufferId, u8 requestId, u8 mo PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -static void UNUSED BtlController_EmitGetRawMonData(u32 battler, u32 bufferId, u8 monId, u8 bytes) +static void UNUSED BtlController_EmitGetRawMonData(enum BattlerId battler, u32 bufferId, u8 monId, u8 bytes) { gBattleResources->transferBuffer[0] = CONTROLLER_GETRAWMONDATA; gBattleResources->transferBuffer[1] = monId; @@ -857,7 +849,7 @@ static void UNUSED BtlController_EmitGetRawMonData(u32 battler, u32 bufferId, u8 PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitSetMonData(u32 battler, u32 bufferId, u8 requestId, u8 monToCheck, u8 bytes, void *data) +void BtlController_EmitSetMonData(enum BattlerId battler, u32 bufferId, u8 requestId, u8 monToCheck, u8 bytes, void *data) { s32 i; @@ -869,7 +861,7 @@ void BtlController_EmitSetMonData(u32 battler, u32 bufferId, u8 requestId, u8 mo PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 3 + bytes); } -static void UNUSED BtlController_EmitSetRawMonData(u32 battler, u32 bufferId, u8 monId, u8 bytes, void *data) +static void UNUSED BtlController_EmitSetRawMonData(enum BattlerId battler, u32 bufferId, u8 monId, u8 bytes, void *data) { s32 i; @@ -881,7 +873,7 @@ static void UNUSED BtlController_EmitSetRawMonData(u32 battler, u32 bufferId, u8 PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, bytes + 3); } -void BtlController_EmitLoadMonSprite(u32 battler, u32 bufferId) +void BtlController_EmitLoadMonSprite(enum BattlerId battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_LOADMONSPRITE; gBattleResources->transferBuffer[1] = CONTROLLER_LOADMONSPRITE; @@ -890,7 +882,7 @@ void BtlController_EmitLoadMonSprite(u32 battler, u32 bufferId) PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitSwitchInAnim(u32 battler, u32 bufferId, u8 partyId, bool8 dontClearTransform, bool8 dontClearSubstituteBit) +void BtlController_EmitSwitchInAnim(enum BattlerId battler, u32 bufferId, u8 partyId, bool8 dontClearTransform, bool8 dontClearSubstituteBit) { gBattleResources->transferBuffer[0] = CONTROLLER_SWITCHINANIM; gBattleResources->transferBuffer[1] = partyId; @@ -899,14 +891,14 @@ void BtlController_EmitSwitchInAnim(u32 battler, u32 bufferId, u8 partyId, bool8 PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitReturnMonToBall(u32 battler, u32 bufferId, bool8 skipAnim) +void BtlController_EmitReturnMonToBall(enum BattlerId battler, u32 bufferId, bool8 skipAnim) { gBattleResources->transferBuffer[0] = CONTROLLER_RETURNMONTOBALL; gBattleResources->transferBuffer[1] = skipAnim; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 2); } -void BtlController_EmitDrawTrainerPic(u32 battler, u32 bufferId) +void BtlController_EmitDrawTrainerPic(enum BattlerId battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_DRAWTRAINERPIC; gBattleResources->transferBuffer[1] = CONTROLLER_DRAWTRAINERPIC; @@ -915,7 +907,7 @@ void BtlController_EmitDrawTrainerPic(u32 battler, u32 bufferId) PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitTrainerSlide(u32 battler, u32 bufferId) +void BtlController_EmitTrainerSlide(enum BattlerId battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_TRAINERSLIDE; gBattleResources->transferBuffer[1] = CONTROLLER_TRAINERSLIDE; @@ -924,7 +916,7 @@ void BtlController_EmitTrainerSlide(u32 battler, u32 bufferId) PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitTrainerSlideBack(u32 battler, u32 bufferId) +void BtlController_EmitTrainerSlideBack(enum BattlerId battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_TRAINERSLIDEBACK; gBattleResources->transferBuffer[1] = CONTROLLER_TRAINERSLIDEBACK; @@ -933,7 +925,7 @@ void BtlController_EmitTrainerSlideBack(u32 battler, u32 bufferId) PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitFaintAnimation(u32 battler, u32 bufferId) +void BtlController_EmitFaintAnimation(enum BattlerId battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_FAINTANIMATION; gBattleResources->transferBuffer[1] = CONTROLLER_FAINTANIMATION; @@ -942,7 +934,7 @@ void BtlController_EmitFaintAnimation(u32 battler, u32 bufferId) PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -static void UNUSED BtlController_EmitPaletteFade(u32 battler, u32 bufferId) +static void UNUSED BtlController_EmitPaletteFade(enum BattlerId battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_PALETTEFADE; gBattleResources->transferBuffer[1] = CONTROLLER_PALETTEFADE; @@ -951,14 +943,14 @@ static void UNUSED BtlController_EmitPaletteFade(u32 battler, u32 bufferId) PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitBallThrowAnim(u32 battler, u32 bufferId, u8 caseId) +void BtlController_EmitBallThrowAnim(enum BattlerId battler, u32 bufferId, u8 caseId) { gBattleResources->transferBuffer[0] = CONTROLLER_BALLTHROWANIM; gBattleResources->transferBuffer[1] = caseId; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 2); } -static void UNUSED BtlController_EmitPause(u32 battler, u32 bufferId, u8 toWait, void *data) +static void UNUSED BtlController_EmitPause(enum BattlerId battler, u32 bufferId, u8 toWait, void *data) { s32 i; @@ -969,7 +961,7 @@ static void UNUSED BtlController_EmitPause(u32 battler, u32 bufferId, u8 toWait, PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, toWait * 3 + 2); } -void BtlController_EmitMoveAnimation(u32 battler, u32 bufferId, enum Move move, u8 turnOfMove, u16 movePower, s32 dmg, u8 friendship, u8 multihit) +void BtlController_EmitMoveAnimation(enum BattlerId battler, u32 bufferId, enum Move move, u8 turnOfMove, u16 movePower, s32 dmg, u8 friendship, u8 multihit) { gBattleResources->transferBuffer[0] = CONTROLLER_MOVEANIMATION; gBattleResources->transferBuffer[1] = move; @@ -1003,12 +995,13 @@ void BtlController_EmitMoveAnimation(u32 battler, u32 bufferId, enum Move move, anim.furyCutterCounter = gBattleMons[battler].volatiles.furyCutterCounter; anim.syrupBombIsShiny = gBattleMons[battler].volatiles.syrupBombIsShiny; anim.isTransformedMonShiny = gBattleMons[battler].volatiles.isTransformedMonShiny; + anim.stockpileCounter = gBattleMons[battler].volatiles.stockpileCounter; memcpy(&gBattleResources->transferBuffer[16], &anim, sizeof(struct LinkBattleAnim)); PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 16 + sizeof(struct LinkBattleAnim)); } -void BtlController_EmitPrintString(u32 battler, u32 bufferId, enum StringID stringID) +void BtlController_EmitPrintString(enum BattlerId battler, u32 bufferId, enum StringID stringID) { s32 i; struct BattleMsgData *stringInfo; @@ -1040,7 +1033,7 @@ void BtlController_EmitPrintString(u32 battler, u32 bufferId, enum StringID stri PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, sizeof(struct BattleMsgData) + 4); } -void BtlController_EmitPrintSelectionString(u32 battler, u32 bufferId, enum StringID stringID) +void BtlController_EmitPrintSelectionString(enum BattlerId battler, u32 bufferId, enum StringID stringID) { s32 i; struct BattleMsgData *stringInfo; @@ -1070,7 +1063,7 @@ void BtlController_EmitPrintSelectionString(u32 battler, u32 bufferId, enum Stri } // itemId only relevant for B_ACTION_USE_ITEM -void BtlController_EmitChooseAction(u32 battler, u32 bufferId, u8 action, enum Item itemId) +void BtlController_EmitChooseAction(enum BattlerId battler, u32 bufferId, u8 action, enum Item itemId) { gBattleResources->transferBuffer[0] = CONTROLLER_CHOOSEACTION; gBattleResources->transferBuffer[1] = action; @@ -1081,7 +1074,7 @@ void BtlController_EmitChooseAction(u32 battler, u32 bufferId, u8 action, enum I // Only used by the forfeit prompt in the Battle Frontier // For other Yes/No boxes in battle, see Cmd_yesnobox -void BtlController_EmitYesNoBox(u32 battler, u32 bufferId) +void BtlController_EmitYesNoBox(enum BattlerId battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_YESNOBOX; gBattleResources->transferBuffer[1] = CONTROLLER_YESNOBOX; @@ -1090,7 +1083,7 @@ void BtlController_EmitYesNoBox(u32 battler, u32 bufferId) PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitChooseMove(u32 battler, u32 bufferId, bool8 isDoubleBattle, bool8 NoPpNumber, struct ChooseMoveStruct *movePpData) +void BtlController_EmitChooseMove(enum BattlerId battler, u32 bufferId, bool8 isDoubleBattle, bool8 NoPpNumber, struct ChooseMoveStruct *movePpData) { s32 i; @@ -1103,7 +1096,7 @@ void BtlController_EmitChooseMove(u32 battler, u32 bufferId, bool8 isDoubleBattl PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, sizeof(*movePpData) + 4); } -void BtlController_EmitChooseItem(u32 battler, u32 bufferId, u8 *battlePartyOrder) +void BtlController_EmitChooseItem(enum BattlerId battler, u32 bufferId, u8 *battlePartyOrder) { s32 i; @@ -1113,7 +1106,7 @@ void BtlController_EmitChooseItem(u32 battler, u32 bufferId, u8 *battlePartyOrde PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitChoosePokemon(u32 battler, u32 bufferId, u8 caseId, u8 slotId, u16 abilityId, u8 battlerPreventingSwitchout, u8 *data) +void BtlController_EmitChoosePokemon(enum BattlerId battler, u32 bufferId, u8 caseId, u8 slotId, u16 abilityId, enum BattlerId battlerPreventingSwitchout, u8 *data) { s32 i; @@ -1128,7 +1121,7 @@ void BtlController_EmitChoosePokemon(u32 battler, u32 bufferId, u8 caseId, u8 sl PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 9); // Only 7 bytes were written. } -static void UNUSED BtlController_EmitCmd23(u32 battler, u32 bufferId) +static void UNUSED BtlController_EmitCmd23(enum BattlerId battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_23; gBattleResources->transferBuffer[1] = CONTROLLER_23; @@ -1138,7 +1131,7 @@ static void UNUSED BtlController_EmitCmd23(u32 battler, u32 bufferId) } // why is the argument u16 if it's being cast to s16 anyway? -void BtlController_EmitHealthBarUpdate(u32 battler, u32 bufferId, u16 hpValue) +void BtlController_EmitHealthBarUpdate(enum BattlerId battler, u32 bufferId, u16 hpValue) { gBattleResources->transferBuffer[0] = CONTROLLER_HEALTHBARUPDATE; gBattleResources->transferBuffer[1] = 0; @@ -1147,7 +1140,7 @@ void BtlController_EmitHealthBarUpdate(u32 battler, u32 bufferId, u16 hpValue) PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitExpUpdate(u32 battler, u32 bufferId, u8 partyId, s32 expPoints) +void BtlController_EmitExpUpdate(enum BattlerId battler, u32 bufferId, u8 partyId, s32 expPoints) { gBattleResources->transferBuffer[0] = CONTROLLER_EXPUPDATE; gBattleResources->transferBuffer[1] = partyId; @@ -1158,7 +1151,7 @@ void BtlController_EmitExpUpdate(u32 battler, u32 bufferId, u8 partyId, s32 expP PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 6); } -void BtlController_EmitStatusIconUpdate(u32 battler, u32 bufferId, u32 status) +void BtlController_EmitStatusIconUpdate(enum BattlerId battler, u32 bufferId, u32 status) { gBattleResources->transferBuffer[0] = CONTROLLER_STATUSICONUPDATE; gBattleResources->transferBuffer[1] = status; @@ -1168,7 +1161,7 @@ void BtlController_EmitStatusIconUpdate(u32 battler, u32 bufferId, u32 status) PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 5); } -void BtlController_EmitStatusAnimation(u32 battler, u32 bufferId, bool8 isVolatile, u32 status) +void BtlController_EmitStatusAnimation(enum BattlerId battler, u32 bufferId, bool8 isVolatile, u32 status) { gBattleResources->transferBuffer[0] = CONTROLLER_STATUSANIMATION; gBattleResources->transferBuffer[1] = isVolatile; @@ -1179,14 +1172,14 @@ void BtlController_EmitStatusAnimation(u32 battler, u32 bufferId, bool8 isVolati PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 6); } -static void UNUSED BtlController_EmitStatusXor(u32 battler, u32 bufferId, u8 b) +static void UNUSED BtlController_EmitStatusXor(enum BattlerId battler, u32 bufferId, u8 b) { gBattleResources->transferBuffer[0] = CONTROLLER_STATUSXOR; gBattleResources->transferBuffer[1] = b; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 2); } -void BtlController_EmitDataTransfer(u32 battler, u32 bufferId, u16 size, void *data) +void BtlController_EmitDataTransfer(enum BattlerId battler, u32 bufferId, u16 size, void *data) { s32 i; @@ -1199,7 +1192,7 @@ void BtlController_EmitDataTransfer(u32 battler, u32 bufferId, u16 size, void *d PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, size + 4); } -static void UNUSED BtlController_EmitDMA3Transfer(u32 battler, u32 bufferId, void *dst, u16 size, void *data) +static void UNUSED BtlController_EmitDMA3Transfer(enum BattlerId battler, u32 bufferId, void *dst, u16 size, void *data) { s32 i; @@ -1215,7 +1208,7 @@ static void UNUSED BtlController_EmitDMA3Transfer(u32 battler, u32 bufferId, voi PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, size + 7); } -static void UNUSED BtlController_EmitPlayBGM(u32 battler, u32 bufferId, u16 songId, void *data) +static void UNUSED BtlController_EmitPlayBGM(enum BattlerId battler, u32 bufferId, u16 songId, void *data) { s32 i; @@ -1230,7 +1223,7 @@ static void UNUSED BtlController_EmitPlayBGM(u32 battler, u32 bufferId, u16 song PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, songId + 3); } -static void UNUSED BtlController_EmitCmd32(u32 battler, u32 bufferId, u16 size, void *data) +static void UNUSED BtlController_EmitCmd32(enum BattlerId battler, u32 bufferId, u16 size, void *data) { s32 i; @@ -1242,7 +1235,7 @@ static void UNUSED BtlController_EmitCmd32(u32 battler, u32 bufferId, u16 size, PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, size + 3); } -void BtlController_EmitTwoReturnValues(u32 battler, u32 bufferId, u8 ret8, u32 ret32) +void BtlController_EmitTwoReturnValues(enum BattlerId battler, u32 bufferId, u8 ret8, u32 ret32) { gBattleResources->transferBuffer[0] = CONTROLLER_TWORETURNVALUES; gBattleResources->transferBuffer[1] = ret8; @@ -1253,7 +1246,7 @@ void BtlController_EmitTwoReturnValues(u32 battler, u32 bufferId, u8 ret8, u32 r PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 6); } -void BtlController_EmitChosenMonReturnValue(u32 battler, u32 bufferId, u8 partyId, u8 *battlePartyOrder) +void BtlController_EmitChosenMonReturnValue(enum BattlerId battler, u32 bufferId, u8 partyId, u8 *battlePartyOrder) { s32 i; @@ -1267,7 +1260,7 @@ void BtlController_EmitChosenMonReturnValue(u32 battler, u32 bufferId, u8 partyI PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 5); } -void BtlController_EmitOneReturnValue(u32 battler, u32 bufferId, u16 ret) +void BtlController_EmitOneReturnValue(enum BattlerId battler, u32 bufferId, u16 ret) { gBattleResources->transferBuffer[0] = CONTROLLER_ONERETURNVALUE; gBattleResources->transferBuffer[1] = ret; @@ -1276,7 +1269,7 @@ void BtlController_EmitOneReturnValue(u32 battler, u32 bufferId, u16 ret) PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitOneReturnValue_Duplicate(u32 battler, u32 bufferId, u16 ret) +void BtlController_EmitOneReturnValue_Duplicate(enum BattlerId battler, u32 bufferId, u16 ret) { gBattleResources->transferBuffer[0] = CONTROLLER_ONERETURNVALUE_DUPLICATE; gBattleResources->transferBuffer[1] = ret; @@ -1285,7 +1278,7 @@ void BtlController_EmitOneReturnValue_Duplicate(u32 battler, u32 bufferId, u16 r PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitHitAnimation(u32 battler, u32 bufferId) +void BtlController_EmitHitAnimation(enum BattlerId battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_HITANIMATION; gBattleResources->transferBuffer[1] = CONTROLLER_HITANIMATION; @@ -1294,7 +1287,7 @@ void BtlController_EmitHitAnimation(u32 battler, u32 bufferId) PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitCantSwitch(u32 battler, u32 bufferId) +void BtlController_EmitCantSwitch(enum BattlerId battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_CANTSWITCH; gBattleResources->transferBuffer[1] = CONTROLLER_CANTSWITCH; @@ -1303,7 +1296,7 @@ void BtlController_EmitCantSwitch(u32 battler, u32 bufferId) PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitPlaySE(u32 battler, u32 bufferId, u16 songId) +void BtlController_EmitPlaySE(enum BattlerId battler, u32 bufferId, u16 songId) { gBattleResources->transferBuffer[0] = CONTROLLER_PLAYSE; gBattleResources->transferBuffer[1] = songId; @@ -1312,7 +1305,7 @@ void BtlController_EmitPlaySE(u32 battler, u32 bufferId, u16 songId) PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitPlayFanfareOrBGM(u32 battler, u32 bufferId, u16 songId, bool8 playBGM) +void BtlController_EmitPlayFanfareOrBGM(enum BattlerId battler, u32 bufferId, u16 songId, bool8 playBGM) { gBattleResources->transferBuffer[0] = CONTROLLER_PLAYFANFAREORBGM; gBattleResources->transferBuffer[1] = songId; @@ -1321,7 +1314,7 @@ void BtlController_EmitPlayFanfareOrBGM(u32 battler, u32 bufferId, u16 songId, b PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitFaintingCry(u32 battler, u32 bufferId) +void BtlController_EmitFaintingCry(enum BattlerId battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_FAINTINGCRY; gBattleResources->transferBuffer[1] = CONTROLLER_FAINTINGCRY; @@ -1330,14 +1323,14 @@ void BtlController_EmitFaintingCry(u32 battler, u32 bufferId) PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitIntroSlide(u32 battler, u32 bufferId, u8 environmentId) +void BtlController_EmitIntroSlide(enum BattlerId battler, u32 bufferId, u8 environmentId) { gBattleResources->transferBuffer[0] = CONTROLLER_INTROSLIDE; gBattleResources->transferBuffer[1] = environmentId; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 2); } -void BtlController_EmitIntroTrainerBallThrow(u32 battler, u32 bufferId) +void BtlController_EmitIntroTrainerBallThrow(enum BattlerId battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_INTROTRAINERBALLTHROW; gBattleResources->transferBuffer[1] = CONTROLLER_INTROTRAINERBALLTHROW; @@ -1346,7 +1339,7 @@ void BtlController_EmitIntroTrainerBallThrow(u32 battler, u32 bufferId) PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitDrawPartyStatusSummary(u32 battler, u32 bufferId, struct HpAndStatus *hpAndStatus, u8 flags) +void BtlController_EmitDrawPartyStatusSummary(enum BattlerId battler, u32 bufferId, struct HpAndStatus *hpAndStatus, u8 flags) { s32 i; @@ -1359,7 +1352,7 @@ void BtlController_EmitDrawPartyStatusSummary(u32 battler, u32 bufferId, struct PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, sizeof(struct HpAndStatus) * PARTY_SIZE + 4); } -void BtlController_EmitHidePartyStatusSummary(u32 battler, u32 bufferId) +void BtlController_EmitHidePartyStatusSummary(enum BattlerId battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_HIDEPARTYSTATUSSUMMARY; gBattleResources->transferBuffer[1] = CONTROLLER_HIDEPARTYSTATUSSUMMARY; @@ -1368,7 +1361,7 @@ void BtlController_EmitHidePartyStatusSummary(u32 battler, u32 bufferId) PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitEndBounceEffect(u32 battler, u32 bufferId) +void BtlController_EmitEndBounceEffect(enum BattlerId battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_ENDBOUNCE; gBattleResources->transferBuffer[1] = CONTROLLER_ENDBOUNCE; @@ -1377,7 +1370,7 @@ void BtlController_EmitEndBounceEffect(u32 battler, u32 bufferId) PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitSpriteInvisibility(u32 battler, u32 bufferId, bool8 isInvisible) +void BtlController_EmitSpriteInvisibility(enum BattlerId battler, u32 bufferId, bool8 isInvisible) { gBattleResources->transferBuffer[0] = CONTROLLER_SPRITEINVISIBILITY; gBattleResources->transferBuffer[1] = isInvisible; @@ -1386,7 +1379,7 @@ void BtlController_EmitSpriteInvisibility(u32 battler, u32 bufferId, bool8 isInv PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4); } -void BtlController_EmitBattleAnimation(u32 battler, u32 bufferId, u8 animationId, u16 argument) +void BtlController_EmitBattleAnimation(enum BattlerId battler, u32 bufferId, u8 animationId, u16 argument) { gBattleResources->transferBuffer[0] = CONTROLLER_BATTLEANIMATION; gBattleResources->transferBuffer[1] = animationId; @@ -1400,13 +1393,14 @@ void BtlController_EmitBattleAnimation(u32 battler, u32 bufferId, u8 animationId anim.furyCutterCounter = gBattleMons[battler].volatiles.furyCutterCounter; anim.syrupBombIsShiny = gBattleMons[battler].volatiles.syrupBombIsShiny; anim.isTransformedMonShiny = gBattleMons[battler].volatiles.isTransformedMonShiny; + anim.stockpileCounter = gBattleMons[battler].volatiles.stockpileCounter; memcpy(&gBattleResources->transferBuffer[4], &anim, sizeof(struct LinkBattleAnim)); PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 4 + sizeof(struct LinkBattleAnim)); } // mode is a LINK_STANDBY_* constant -void BtlController_EmitLinkStandbyMsg(u32 battler, u32 bufferId, u8 mode, bool32 record) +void BtlController_EmitLinkStandbyMsg(enum BattlerId battler, u32 bufferId, u8 mode, bool32 record) { gBattleResources->transferBuffer[0] = CONTROLLER_LINKSTANDBYMSG; gBattleResources->transferBuffer[1] = mode; @@ -1419,24 +1413,24 @@ void BtlController_EmitLinkStandbyMsg(u32 battler, u32 bufferId, u8 mode, bool32 PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, gBattleResources->transferBuffer[2] + 4); } -void BtlController_EmitResetActionMoveSelection(u32 battler, u32 bufferId, u8 caseId) +void BtlController_EmitResetActionMoveSelection(enum BattlerId battler, u32 bufferId, u8 caseId) { gBattleResources->transferBuffer[0] = CONTROLLER_RESETACTIONMOVESELECTION; gBattleResources->transferBuffer[1] = caseId; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 2); } -void BtlController_EmitEndLinkBattle(u32 battler, u32 bufferId, u8 battleOutcome) +void BtlController_EmitEndLinkBattle(enum BattlerId battler, u32 bufferId, u8 battleOutcome) { gBattleResources->transferBuffer[0] = CONTROLLER_ENDLINKBATTLE; gBattleResources->transferBuffer[1] = battleOutcome; - // gBattleResources->transferBuffer[2] = gSaveBlock2Ptr->frontier.disableRecordBattle; - // gBattleResources->transferBuffer[3] = gSaveBlock2Ptr->frontier.disableRecordBattle; - // gBattleResources->transferBuffer[5] = gBattleResources->transferBuffer[4] = RecordedBattle_BufferNewBattlerData(&gBattleResources->transferBuffer[6]); + gBattleResources->transferBuffer[2] = gSaveBlock2Ptr->frontier.disableRecordBattle; + gBattleResources->transferBuffer[3] = gSaveBlock2Ptr->frontier.disableRecordBattle; + gBattleResources->transferBuffer[5] = gBattleResources->transferBuffer[4] = RecordedBattle_BufferNewBattlerData(&gBattleResources->transferBuffer[6]); PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, gBattleResources->transferBuffer[4] + 6); } -void BtlController_EmitDebugMenu(u32 battler, u32 bufferId) +void BtlController_EmitDebugMenu(enum BattlerId battler, u32 bufferId) { gBattleResources->transferBuffer[0] = CONTROLLER_DEBUGMENU; PrepareBufferDataTransfer(battler, bufferId, gBattleResources->transferBuffer, 1); @@ -1445,12 +1439,12 @@ void BtlController_EmitDebugMenu(u32 battler, u32 bufferId) // Standardized Controller functions // Can be used for all the controllers. -void BtlController_Complete(u32 battler) +void BtlController_Complete(enum BattlerId battler) { gBattlerControllerEndFuncs[battler](battler); } -static u32 GetBattlerMonData(u32 battler, struct Pokemon *party, u32 monId, u8 *dst) +static u32 GetBattlerMonData(enum BattlerId battler, struct Pokemon *party, u32 monId, u8 *dst) { struct BattlePokemon battleMon; struct MovePpInfo moveData; @@ -1766,7 +1760,7 @@ static u32 GetBattlerMonData(u32 battler, struct Pokemon *party, u32 monId, u8 * return size; } -static void SetBattlerMonData(u32 battler, struct Pokemon *party, u32 monId) +static void SetBattlerMonData(enum BattlerId battler, struct Pokemon *party, u32 monId) { struct BattlePokemon *battlePokemon = (struct BattlePokemon *)&gBattleResources->bufferA[battler][3]; struct MovePpInfo *moveData = (struct MovePpInfo *)&gBattleResources->bufferA[battler][3]; @@ -1986,7 +1980,7 @@ static void SetBattlerMonData(u32 battler, struct Pokemon *party, u32 monId) } // In normal singles, if follower Pokémon exists, and the Pokémon following is being sent out, have it slide in instead of being thrown -static bool8 ShouldDoSlideInAnim(u32 battler) +static bool8 ShouldDoSlideInAnim(enum BattlerId battler) { struct ObjectEvent *followerObj = GetFollowerObject(); if (!followerObj || followerObj->invisible) @@ -2005,7 +1999,7 @@ static bool8 ShouldDoSlideInAnim(u32 battler) return TRUE; } -void StartSendOutAnim(u32 battler, bool32 dontClearTransform, bool32 dontClearSubstituteBit, bool32 doSlideIn) +void StartSendOutAnim(enum BattlerId battler, bool32 dontClearTransform, bool32 dontClearSubstituteBit, bool32 doSlideIn) { u16 species; struct Pokemon *mon = GetBattlerMon(battler); @@ -2049,7 +2043,7 @@ void StartSendOutAnim(u32 battler, bool32 dontClearTransform, bool32 dontClearSu gSprites[gBattleControllerData[battler]].data[0] = DoPokeballSendOutAnimation(battler, 0, sendoutType); } -static void FreeMonSprite(u32 battler) +static void FreeMonSprite(enum BattlerId battler) { FreeSpriteOamMatrix(&gSprites[gBattlerSpriteIds[battler]]); DestroySprite(&gSprites[gBattlerSpriteIds[battler]]); @@ -2058,7 +2052,7 @@ static void FreeMonSprite(u32 battler) SetHealthboxSpriteInvisible(gHealthboxSpriteIds[battler]); } -static void Controller_ReturnMonToBall2(u32 battler) +static void Controller_ReturnMonToBall2(enum BattlerId battler) { if (!gBattleSpritesDataPtr->healthBoxesData[battler].specialAnimActive) { @@ -2067,7 +2061,7 @@ static void Controller_ReturnMonToBall2(u32 battler) } } -static void Controller_ReturnMonToBall(u32 battler) +static void Controller_ReturnMonToBall(enum BattlerId battler) { switch (gBattleSpritesDataPtr->healthBoxesData[battler].animationState) { @@ -2088,7 +2082,7 @@ static void Controller_ReturnMonToBall(u32 battler) } } -static void Controller_FaintPlayerMon(u32 battler) +static void Controller_FaintPlayerMon(enum BattlerId battler) { u32 spriteId = gBattlerSpriteIds[battler]; if (gSprites[spriteId].y + gSprites[spriteId].y2 > DISPLAY_HEIGHT) @@ -2101,7 +2095,7 @@ static void Controller_FaintPlayerMon(u32 battler) } } -static void Controller_FaintOpponentMon(u32 battler) +static void Controller_FaintOpponentMon(enum BattlerId battler) { if (!gSprites[gBattlerSpriteIds[battler]].inUse) { @@ -2110,7 +2104,7 @@ static void Controller_FaintOpponentMon(u32 battler) } } -static void Controller_DoMoveAnimation(u32 battler) +static void Controller_DoMoveAnimation(enum BattlerId battler) { enum Move move = gBattleResources->bufferA[battler][1] | (gBattleResources->bufferA[battler][2] << 8); @@ -2160,7 +2154,7 @@ static void Controller_DoMoveAnimation(u32 battler) } } -static void Controller_HandleTrainerSlideBack(u32 battler) +static void Controller_HandleTrainerSlideBack(enum BattlerId battler) { if (gSprites[gBattleStruct->trainerSlideSpriteIds[battler]].callback == SpriteCallbackDummy) { @@ -2172,7 +2166,7 @@ static void Controller_HandleTrainerSlideBack(u32 battler) } } -void Controller_WaitForHealthBar(u32 battler) +void Controller_WaitForHealthBar(enum BattlerId battler) { s16 hpValue = MoveBattleBar(battler, gHealthboxSpriteIds[battler], HEALTH_BAR, 0); @@ -2198,37 +2192,37 @@ void Controller_WaitForHealthBar(u32 battler) } } -static void Controller_WaitForBallThrow(u32 battler) +static void Controller_WaitForBallThrow(enum BattlerId battler) { if (!gDoingBattleAnim || !gBattleSpritesDataPtr->healthBoxesData[battler].specialAnimActive) BtlController_Complete(battler); } -static void Controller_WaitForBattleAnimation(u32 battler) +static void Controller_WaitForBattleAnimation(enum BattlerId battler) { if (!gBattleSpritesDataPtr->healthBoxesData[battler].animFromTableActive) BtlController_Complete(battler); } -static void Controller_WaitForStatusAnimation(u32 battler) +static void Controller_WaitForStatusAnimation(enum BattlerId battler) { if (!gBattleSpritesDataPtr->healthBoxesData[battler].statusAnimActive) BtlController_Complete(battler); } -static void Controller_WaitForTrainerPic(u32 battler) +static void Controller_WaitForTrainerPic(enum BattlerId battler) { if (gSprites[gBattleStruct->trainerSlideSpriteIds[battler]].callback == SpriteCallbackDummy) BtlController_Complete(battler); } -void Controller_WaitForString(u32 battler) +void Controller_WaitForString(enum BattlerId battler) { if (!IsTextPrinterActiveOnWindow(B_WIN_MSG)) BtlController_Complete(battler); } -static void Controller_WaitForPartyStatusSummary(u32 battler) +static void Controller_WaitForPartyStatusSummary(enum BattlerId battler) { if (gBattleSpritesDataPtr->healthBoxesData[battler].partyStatusDelayTimer++ > 92) { @@ -2237,7 +2231,7 @@ static void Controller_WaitForPartyStatusSummary(u32 battler) } } -static void Controller_HitAnimation(u32 battler) +static void Controller_HitAnimation(enum BattlerId battler) { u32 spriteId = gBattlerSpriteIds[battler]; @@ -2257,22 +2251,22 @@ static void Controller_HitAnimation(u32 battler) } // Used for all the commands which do nothing. -void BtlController_Empty(u32 battler) +void BtlController_Empty(enum BattlerId battler) { BtlController_Complete(battler); } // Dummy function at the end of the table. -void BtlController_TerminatorNop(u32 battler) +void BtlController_TerminatorNop(enum BattlerId battler) { } -void BattleControllerDummy(u32 battler) +void BattleControllerDummy(enum BattlerId battler) { } // Handlers of the controller commands -void BtlController_HandleGetMonData(u32 battler) +void BtlController_HandleGetMonData(enum BattlerId battler) { u8 monData[sizeof(struct Pokemon) * 2 + 56]; // this allows to get full data of two pokemon, trying to get more will result in overwriting data struct Pokemon *party = GetBattlerParty(battler); @@ -2298,7 +2292,7 @@ void BtlController_HandleGetMonData(u32 battler) BtlController_Complete(battler); } -void BtlController_HandleGetRawMonData(u32 battler) +void BtlController_HandleGetRawMonData(enum BattlerId battler) { struct BattlePokemon battleMon; struct Pokemon *mon = GetBattlerMon(battler); @@ -2314,7 +2308,7 @@ void BtlController_HandleGetRawMonData(u32 battler) BtlController_Complete(battler); } -void BtlController_HandleSetMonData(u32 battler) +void BtlController_HandleSetMonData(enum BattlerId battler) { struct Pokemon *party = GetBattlerParty(battler); u32 i, monToCheck; @@ -2336,7 +2330,7 @@ void BtlController_HandleSetMonData(u32 battler) BtlController_Complete(battler); } -void BtlController_HandleSetRawMonData(u32 battler) +void BtlController_HandleSetRawMonData(enum BattlerId battler) { u32 i; u8 *dst = (u8 *)GetBattlerMon(battler) + gBattleResources->bufferA[battler][1]; @@ -2347,7 +2341,7 @@ void BtlController_HandleSetRawMonData(u32 battler) BtlController_Complete(battler); } -static void CompleteOnBattlerSpritePosX_0(u32 battler) +static void CompleteOnBattlerSpritePosX_0(enum BattlerId battler) { if (gSprites[gBattlerSpriteIds[battler]].animEnded == TRUE && gSprites[gBattlerSpriteIds[battler]].x2 == 0) @@ -2367,7 +2361,7 @@ static void CompleteOnBattlerSpritePosX_0(u32 battler) } } -void BtlController_HandleLoadMonSprite(u32 battler) +void BtlController_HandleLoadMonSprite(enum BattlerId battler) { u32 y; struct Pokemon *mon = GetBattlerMon(battler); @@ -2409,7 +2403,7 @@ void BtlController_HandleLoadMonSprite(u32 battler) gBattlerControllerFuncs[battler] = WaitForMonAnimAfterLoad; } -void BtlController_HandleSwitchInAnim(u32 battler) +void BtlController_HandleSwitchInAnim(enum BattlerId battler) { bool32 isPlayerSide = (IsControllerPlayer(battler) || IsControllerPlayerPartner(battler) @@ -2437,7 +2431,7 @@ void BtlController_HandleSwitchInAnim(u32 battler) gBattlerControllerFuncs[battler] = BtlController_HandleSwitchInTryShinyAnim; } -void BtlController_HandleReturnMonToBall(u32 battler) +void BtlController_HandleReturnMonToBall(enum BattlerId battler) { if (gBattleResources->bufferA[battler][1] == 0) { @@ -2457,7 +2451,7 @@ void BtlController_HandleReturnMonToBall(u32 battler) #define sSpeedX data[0] -void BtlController_HandleDrawTrainerPic(u32 battler, enum TrainerPicID trainerPicId, bool32 isFrontPic, s16 xPos, s16 yPos, s32 subpriority) +void BtlController_HandleDrawTrainerPic(enum BattlerId battler, enum TrainerPicID trainerPicId, bool32 isFrontPic, s16 xPos, s16 yPos, s32 subpriority) { if (!IsOnPlayerSide(battler)) // Always the front sprite for the opponent. { @@ -2506,7 +2500,6 @@ void BtlController_HandleDrawTrainerPic(u32 battler, enum TrainerPicID trainerPi if ((gBattleTypeFlags & BATTLE_TYPE_SAFARI) && GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT) gBattlerSpriteIds[battler] = gBattleStruct->trainerSlideSpriteIds[battler]; - // Sets sprite priority to 1 so mons don't remain in foreground gSprites[gBattleStruct->trainerSlideSpriteIds[battler]].oam.priority = 1; // Aiming for palette slots 8 and 9 for Player and PlayerPartner to prevent Trainer Slides causing mons to change colour @@ -2523,7 +2516,7 @@ void BtlController_HandleDrawTrainerPic(u32 battler, enum TrainerPicID trainerPi gBattlerControllerFuncs[battler] = Controller_WaitForTrainerPic; } -void BtlController_HandleTrainerSlide(u32 battler, enum TrainerPicID trainerPicId) +void BtlController_HandleTrainerSlide(enum BattlerId battler, enum TrainerPicID trainerPicId) { if (IsOnPlayerSide(battler)) { @@ -2560,7 +2553,7 @@ void BtlController_HandleTrainerSlide(u32 battler, enum TrainerPicID trainerPicI #undef sSpeedX -void BtlController_HandleTrainerSlideBack(u32 battler, s16 data0, bool32 startAnim) +void BtlController_HandleTrainerSlideBack(enum BattlerId battler, s16 data0, bool32 startAnim) { SetSpritePrimaryCoordsFromSecondaryCoords(&gSprites[gBattleStruct->trainerSlideSpriteIds[battler]]); gSprites[gBattleStruct->trainerSlideSpriteIds[battler]].data[0] = data0; @@ -2576,7 +2569,7 @@ void BtlController_HandleTrainerSlideBack(u32 battler, s16 data0, bool32 startAn #define sSpeedX data[1] #define sSpeedY data[2] -void BtlController_HandleFaintAnimation(u32 battler) +void BtlController_HandleFaintAnimation(enum BattlerId battler) { SetHealthboxSpriteInvisible(gHealthboxSpriteIds[battler]); if (gBattleSpritesDataPtr->healthBoxesData[battler].animationState == 0) @@ -2615,7 +2608,7 @@ void BtlController_HandleFaintAnimation(u32 battler) #undef sSpeedX #undef sSpeedY -static void HandleBallThrow(u32 battler, u32 target, u32 animId, bool32 allowCriticalCapture) +static void HandleBallThrow(enum BattlerId battler, enum BattlerId target, u32 animId, bool32 allowCriticalCapture) { gDoingBattleAnim = TRUE; if (allowCriticalCapture && IsCriticalCapture()) @@ -2625,11 +2618,11 @@ static void HandleBallThrow(u32 battler, u32 target, u32 animId, bool32 allowCri gBattlerControllerFuncs[battler] = Controller_WaitForBallThrow; } -void BtlController_HandleBallThrowAnim(u32 battler) +void BtlController_HandleBallThrowAnim(enum BattlerId battler) { bool32 allowCriticalCapture = FALSE; u32 animId = B_ANIM_BALL_THROW; - u32 target = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + enum BattlerId target = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); if (BattlerIsPlayer(battler)) { @@ -2645,7 +2638,7 @@ void BtlController_HandleBallThrowAnim(u32 battler) HandleBallThrow(battler, target, animId, allowCriticalCapture); } -void BtlController_HandleMoveAnimation(u32 battler) +void BtlController_HandleMoveAnimation(enum BattlerId battler) { if (!IsBattleSEPlaying(battler)) { @@ -2662,7 +2655,7 @@ void BtlController_HandleMoveAnimation(u32 battler) } } -void BtlController_HandlePrintString(u32 battler) +void BtlController_HandlePrintString(enum BattlerId battler) { u16 *stringId; @@ -2681,6 +2674,7 @@ void BtlController_HandlePrintString(u32 battler) } } + if (BattleStringShouldBeColored(*stringId)) BattlePutTextOnWindow(gDisplayedStringBattle, (B_WIN_MSG | B_TEXT_FLAG_NPC_CONTEXT_FONT)); else @@ -2700,9 +2694,12 @@ void BtlController_HandlePrintString(u32 battler) } gBattlerControllerFuncs[battler] = Controller_WaitForString; + if (IsControllerPlayer(battler) + || IsControllerOpponent(battler)) + BattleArena_DeductSkillPoints(battler, *stringId); } -void BtlController_HandlePrintStringPlayerOnly(u32 battler) +void BtlController_HandlePrintStringPlayerOnly(enum BattlerId battler) { if (IsOnPlayerSide(battler)) BtlController_HandlePrintString(battler); @@ -2710,7 +2707,7 @@ void BtlController_HandlePrintStringPlayerOnly(u32 battler) BtlController_Complete(battler); } -void BtlController_HandleHealthBarUpdate(u32 battler) +void BtlController_HandleHealthBarUpdate(enum BattlerId battler) { s32 maxHP, curHP; s16 hpVal; @@ -2741,7 +2738,7 @@ void BtlController_HandleHealthBarUpdate(u32 battler) gBattlerControllerFuncs[battler] = Controller_WaitForHealthBar; } -void BtlController_HandleStatusIconUpdate(u32 battler) +void BtlController_HandleStatusIconUpdate(enum BattlerId battler) { if (!IsBattleSEPlaying(battler)) { @@ -2763,7 +2760,7 @@ void BtlController_HandleStatusIconUpdate(u32 battler) } } -void BtlController_HandleStatusAnimation(u32 battler) +void BtlController_HandleStatusAnimation(enum BattlerId battler) { if (!IsBattleSEPlaying(battler)) { @@ -2773,7 +2770,7 @@ void BtlController_HandleStatusAnimation(u32 battler) } } -void BtlController_HandleHitAnimation(u32 battler) +void BtlController_HandleHitAnimation(enum BattlerId battler) { if (gSprites[gBattlerSpriteIds[battler]].invisible == TRUE || (gTestRunnerHeadless && !gBattleTestRunnerState->forceMoveAnim)) { @@ -2788,7 +2785,7 @@ void BtlController_HandleHitAnimation(u32 battler) } } -void BtlController_HandlePlaySE(u32 battler) +void BtlController_HandlePlaySE(enum BattlerId battler) { if (gTestRunnerHeadless && !gBattleTestRunnerState->forceMoveAnim) { @@ -2801,7 +2798,7 @@ void BtlController_HandlePlaySE(u32 battler) BtlController_Complete(battler); } -void BtlController_HandlePlayFanfareOrBGM(u32 battler) +void BtlController_HandlePlayFanfareOrBGM(enum BattlerId battler) { if (gTestRunnerHeadless && !gBattleTestRunnerState->forceMoveAnim) { @@ -2821,7 +2818,7 @@ void BtlController_HandlePlayFanfareOrBGM(u32 battler) BtlController_Complete(battler); } -void BtlController_HandleFaintingCry(u32 battler) +void BtlController_HandleFaintingCry(enum BattlerId battler) { struct Pokemon *party = GetBattlerParty(battler); s8 pan; @@ -2835,14 +2832,14 @@ void BtlController_HandleFaintingCry(u32 battler) BtlController_Complete(battler); } -void BtlController_HandleIntroSlide(u32 battler) +void BtlController_HandleIntroSlide(enum BattlerId battler) { HandleIntroSlide(gBattleResources->bufferA[battler][1]); gIntroSlideFlags |= 1; BtlController_Complete(battler); } -void BtlController_HandleSpriteInvisibility(u32 battler) +void BtlController_HandleSpriteInvisibility(enum BattlerId battler) { if (IsBattlerSpritePresent(battler)) { @@ -2852,12 +2849,12 @@ void BtlController_HandleSpriteInvisibility(u32 battler) BtlController_Complete(battler); } -bool32 TwoPlayerIntroMons(u32 battler) // Double battle with both player pokemon active. +bool32 TwoPlayerIntroMons(enum BattlerId battler) // Double battle with both player pokemon active. { return (IsDoubleBattle() && IsValidForBattle(GetBattlerMon(battler ^ BIT_FLANK))); } -bool32 TwoOpponentIntroMons(u32 battler) // Double battle with both opponent pokemon active. +bool32 TwoOpponentIntroMons(enum BattlerId battler) // Double battle with both opponent pokemon active. { return (IsDoubleBattle() && IsValidForBattle(GetBattlerMon(battler)) @@ -2874,7 +2871,7 @@ bool32 TwoOpponentIntroMons(u32 battler) // Double battle with both opponent pok // Sprite data for SpriteCB_FreePlayerSpriteLoadMonSprite #define sBattlerId data[5] -void BtlController_HandleIntroTrainerBallThrow(u32 battler, u16 tagTrainerPal, const u16 *trainerPal, s16 framesToWait, void (*controllerCallback)(u32 battler)) +void BtlController_HandleIntroTrainerBallThrow(enum BattlerId battler, u16 tagTrainerPal, const u16 *trainerPal, s16 framesToWait, void (*controllerCallback)(enum BattlerId battler)) { u8 paletteNum, taskId; enum BattleSide side = GetBattlerSide(battler); @@ -2924,7 +2921,7 @@ void BtlController_HandleIntroTrainerBallThrow(u32 battler, u16 tagTrainerPal, c gBattlerControllerFuncs[battler] = BattleControllerDummy; } -static bool32 TwoMonsAtSendOut(u32 battler) +static bool32 TwoMonsAtSendOut(enum BattlerId battler) { if (IsOnPlayerSide(battler)) { @@ -2954,8 +2951,8 @@ static void Task_StartSendOutAnim(u8 taskId) } else { - u32 battlerPartner; - u32 battler = gTasks[taskId].tBattlerId; + enum BattlerId battlerPartner; + enum BattlerId battler = gTasks[taskId].tBattlerId; if (TwoMonsAtSendOut(battler)) { @@ -2985,7 +2982,7 @@ static void Task_StartSendOutAnim(u8 taskId) static void SpriteCB_FreePlayerSpriteLoadMonSprite(struct Sprite *sprite) { - u8 battler = sprite->sBattlerId; + enum BattlerId battler = sprite->sBattlerId; // Free player trainer sprite FreeSpriteOamMatrix(sprite); @@ -3006,7 +3003,7 @@ static void SpriteCB_FreeOpponentSprite(struct Sprite *sprite) #undef sBattlerId -void BtlController_HandleDrawPartyStatusSummary(u32 battler, enum BattleSide side, bool32 considerDelay) +void BtlController_HandleDrawPartyStatusSummary(enum BattlerId battler, enum BattleSide side, bool32 considerDelay) { if (gBattleResources->bufferA[battler][1] != 0 && IsOnPlayerSide(battler)) { @@ -3040,14 +3037,14 @@ void BtlController_HandleDrawPartyStatusSummary(u32 battler, enum BattleSide sid } } -void BtlController_HandleHidePartyStatusSummary(u32 battler) +void BtlController_HandleHidePartyStatusSummary(enum BattlerId battler) { if (gBattleSpritesDataPtr->healthBoxesData[battler].partyStatusSummaryShown) gTasks[gBattlerStatusSummaryTaskId[battler]].func = Task_HidePartyStatusSummary; BtlController_Complete(battler); } -void BtlController_HandleBattleAnimation(u32 battler) +void BtlController_HandleBattleAnimation(enum BattlerId battler) { if ((gBattleTypeFlags & (BATTLE_TYPE_SAFARI | BATTLE_TYPE_CATCH_TUTORIAL)) || IsControllerOakOldMan(battler) @@ -3066,7 +3063,7 @@ void BtlController_HandleBattleAnimation(u32 battler) } } -void AnimateMonAfterPokeBallFail(u32 battler) +void AnimateMonAfterPokeBallFail(enum BattlerId battler) { if (B_ANIMATE_MON_AFTER_FAILED_POKEBALL == FALSE) return; @@ -3075,13 +3072,13 @@ void AnimateMonAfterPokeBallFail(u32 battler) TryShinyAnimation(gBattlerTarget, GetBattlerMon(gBattlerTarget)); } -static void AnimateMonAfterKnockout(u32 battler) +static void AnimateMonAfterKnockout(enum BattlerId battler) { if (B_ANIMATE_MON_AFTER_KO == FALSE) return; - u32 oppositeBattler = BATTLE_OPPOSITE(battler); - u32 partnerBattler = BATTLE_PARTNER(oppositeBattler); + enum BattlerId oppositeBattler = BATTLE_OPPOSITE(battler); + enum BattlerId partnerBattler = BATTLE_PARTNER(oppositeBattler); bool32 wasPlayerSideKnockedOut = (IsOnPlayerSide(battler)); if (IsBattlerAlive(oppositeBattler)) @@ -3091,7 +3088,7 @@ static void AnimateMonAfterKnockout(u32 battler) LaunchKOAnimation(partnerBattler, ReturnAnimIdForBattler(wasPlayerSideKnockedOut, partnerBattler), wasPlayerSideKnockedOut); } -static void LaunchKOAnimation(u32 battlerId, u16 animId, bool32 isFront) +static void LaunchKOAnimation(enum BattlerId battlerId, u16 animId, bool32 isFront) { u32 species = GetBattlerVisualSpecies(battlerId); u32 spriteId = gBattlerSpriteIds[battlerId]; @@ -3122,7 +3119,7 @@ static u32 ReturnAnimIdForBattler(bool32 wasPlayerSideKnockedOut, u32 specificBa return GetSpeciesBackAnimSet(species); } -void TrySetBattlerShadowSpriteCallback(u32 battler) +void TrySetBattlerShadowSpriteCallback(enum BattlerId battler) { if (gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteIdPrimary].callback == SpriteCallbackDummy && (B_ENEMY_MON_SHADOW_STYLE <= GEN_3 || P_GBA_STYLE_SPECIES_GFX == TRUE @@ -3130,7 +3127,7 @@ void TrySetBattlerShadowSpriteCallback(u32 battler) SetBattlerShadowSpriteCallback(battler, GetBattlerVisualSpecies(battler)); } -void TryShinyAnimAfterMonAnim(u32 battler) +void TryShinyAnimAfterMonAnim(enum BattlerId battler) { if (gSprites[gBattlerSpriteIds[battler]].x2 == 0) { @@ -3149,13 +3146,13 @@ void TryShinyAnimAfterMonAnim(u32 battler) } } -void WaitForMonAnimAfterLoad(u32 battler) +void WaitForMonAnimAfterLoad(enum BattlerId battler) { if (gSprites[gBattlerSpriteIds[battler]].animEnded && gSprites[gBattlerSpriteIds[battler]].x2 == 0) BtlController_Complete(battler); } -void BtlController_HandleSwitchInShowSubstitute(u32 battler) +void BtlController_HandleSwitchInShowSubstitute(enum BattlerId battler) { if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy) { @@ -3174,14 +3171,14 @@ void BtlController_HandleSwitchInShowSubstitute(u32 battler) } } -void BtlController_HandleSwitchInWaitAndEnd(u32 battler) +void BtlController_HandleSwitchInWaitAndEnd(enum BattlerId battler) { if (!gBattleSpritesDataPtr->healthBoxesData[battler].specialAnimActive && gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy) BtlController_Complete(battler); } -void BtlController_Intro_DelayAndEnd(u32 battler) +void BtlController_Intro_DelayAndEnd(enum BattlerId battler) { if (--gBattleSpritesDataPtr->healthBoxesData[battler].introEndDelay == (u8)-1) { @@ -3190,7 +3187,7 @@ void BtlController_Intro_DelayAndEnd(u32 battler) } } -void BtlController_HandleSwitchInSoundAndEnd(u32 battler) +void BtlController_HandleSwitchInSoundAndEnd(enum BattlerId battler) { if (!gBattleSpritesDataPtr->healthBoxesData[battler].specialAnimActive && !IsCryPlayingOrClearCrySongs()) { @@ -3206,7 +3203,7 @@ void BtlController_HandleSwitchInSoundAndEnd(u32 battler) } } -void BtlController_HandleSwitchInShowHealthbox(u32 battler) +void BtlController_HandleSwitchInShowHealthbox(enum BattlerId battler) { enum BattleSide side = GetBattlerSide(battler); if (gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim @@ -3233,7 +3230,7 @@ void BtlController_HandleSwitchInShowHealthbox(u32 battler) } } -static void SwitchIn_CleanShinyAnimShowSubstitute(u32 battler) +static void SwitchIn_CleanShinyAnimShowSubstitute(enum BattlerId battler) { if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy && gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim @@ -3254,7 +3251,7 @@ static void SwitchIn_CleanShinyAnimShowSubstitute(u32 battler) } } -void BtlController_HandleSwitchInTryShinyAnim(u32 battler) +void BtlController_HandleSwitchInTryShinyAnim(enum BattlerId battler) { if (!gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive && !gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim) @@ -3282,7 +3279,7 @@ void BtlController_HandleSwitchInTryShinyAnim(u32 battler) } } -void UpdateFriendshipFromXItem(u32 battler) +void UpdateFriendshipFromXItem(enum BattlerId battler) { struct Pokemon *party = GetBattlerParty(battler); @@ -3308,14 +3305,14 @@ void UpdateFriendshipFromXItem(u32 battler) } } -bool32 ShouldBattleRestrictionsApply(u32 battler) +bool32 ShouldBattleRestrictionsApply(enum BattlerId battler) { return IsControllerPlayer(battler); } void FreeShinyStars(void) { - for (u32 battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { if (gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim) return; diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index 2481cce63..db94ee146 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -70,7 +70,7 @@ static const struct GMaxMove sGMaxMoveTable[] = }; // Returns whether a battler can Dynamax. -bool32 CanDynamax(u32 battler) +bool32 CanDynamax(enum BattlerId battler) { u16 species = GetBattlerVisualSpecies(battler); enum HoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); @@ -120,7 +120,7 @@ bool32 CanDynamax(u32 battler) } // Returns whether a battler is transformed into a Gigantamax form. -bool32 IsGigantamaxed(u32 battler) +bool32 IsGigantamaxed(enum BattlerId battler) { struct Pokemon *mon = GetBattlerMon(battler); if ((gSpeciesInfo[gBattleMons[battler].species].isGigantamax) && GetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR)) @@ -144,7 +144,7 @@ void ApplyDynamaxHPMultiplier(struct Pokemon* mon) } // Returns the non-Dynamax HP of a Pokemon. -u32 GetNonDynamaxHP(u32 battler) +u32 GetNonDynamaxHP(enum BattlerId battler) { if (GetActiveGimmick(battler) != GIMMICK_DYNAMAX || gBattleMons[battler].species == SPECIES_SHEDINJA) return gBattleMons[battler].hp; @@ -158,7 +158,7 @@ u32 GetNonDynamaxHP(u32 battler) } // Returns the non-Dynamax Max HP of a Pokemon. -u32 GetNonDynamaxMaxHP(u32 battler) +u32 GetNonDynamaxMaxHP(enum BattlerId battler) { if (GetActiveGimmick(battler) != GIMMICK_DYNAMAX || gBattleMons[battler].species == SPECIES_SHEDINJA) return gBattleMons[battler].maxHP; @@ -172,7 +172,7 @@ u32 GetNonDynamaxMaxHP(u32 battler) } // Sets flags used for Dynamaxing and checks Gigantamax forms. -void ActivateDynamax(u32 battler) +void ActivateDynamax(enum BattlerId battler) { // Set appropriate use flags. SetActiveGimmick(battler, GIMMICK_DYNAMAX); @@ -188,13 +188,13 @@ void ActivateDynamax(u32 battler) // Try Gigantamax form change. if (!gBattleMons[battler].volatiles.transformed) // Ditto cannot Gigantamax. - TryBattleFormChange(battler, FORM_CHANGE_BATTLE_GIGANTAMAX); + TryBattleFormChange(battler, FORM_CHANGE_BATTLE_GIGANTAMAX, GetBattlerAbility(battler)); BattleScriptPushCursorAndCallback(BattleScript_DynamaxBegins); } // Unsets the flags used for Dynamaxing and reverts max HP if needed. -void UndoDynamax(u32 battler) +void UndoDynamax(enum BattlerId battler) { // Revert HP if battler is still Dynamaxed. if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX) @@ -211,7 +211,7 @@ void UndoDynamax(u32 battler) // Undo form change if needed. if (IsGigantamaxed(battler)) - TryBattleFormChange(battler, FORM_CHANGE_END_BATTLE); + TryBattleFormChange(battler, FORM_CHANGE_END_BATTLE, GetBattlerAbility(battler)); } // Certain moves are blocked by Max Guard that normally ignore protection. @@ -234,15 +234,16 @@ bool32 IsMoveBlockedByMaxGuard(enum Move move) } } -static enum Move GetTypeBasedMaxMove(u32 battler, enum Type type) +static enum Move GetTypeBasedMaxMove(enum BattlerId battler, enum Type type) { // Gigantamax check u32 i; u32 species = gBattleMons[battler].species; u32 targetSpecies = species; + enum Ability ability = GetBattlerAbility(battler); if (!gSpeciesInfo[species].isGigantamax) - targetSpecies = GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_GIGANTAMAX); + targetSpecies = GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_GIGANTAMAX, ability); if (targetSpecies != species) species = targetSpecies; @@ -263,7 +264,7 @@ static enum Move GetTypeBasedMaxMove(u32 battler, enum Type type) } // Returns the appropriate Max Move or G-Max Move for a battler to use. -enum Move GetMaxMove(u32 battler, enum Move baseMove) +enum Move GetMaxMove(enum BattlerId battler, enum Move baseMove) { enum Type moveType; SetTypeBeforeUsingMove(baseMove, battler); @@ -402,7 +403,6 @@ static enum MaxPowerTier GetMaxPowerTier(enum Move move) case EFFECT_FINAL_GAMBIT: return MAX_POWER_TIER_2; case EFFECT_OHKO: - case EFFECT_SHEER_COLD: case EFFECT_RETURN: case EFFECT_FRUSTRATION: case EFFECT_HEAT_CRASH: @@ -459,43 +459,3 @@ void ChooseDamageNonTypesString(enum Type type) break; } } - -// Updates Dynamax HP multipliers and healthboxes. -void BS_UpdateDynamax(void) -{ - NATIVE_ARGS(); - u32 battler = gBattleScripting.battler; - struct Pokemon *mon = GetBattlerMon(battler); - - if (!IsGigantamaxed(battler)) // RecalcBattlerStats will get called on form change. - RecalcBattlerStats(battler, mon, GetActiveGimmick(battler) == GIMMICK_DYNAMAX); - - UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL); - gBattlescriptCurrInstr = cmd->nextInstr; -} - -// Goes to the jump instruction if the target is Dynamaxed. -void BS_JumpIfDynamaxed(void) -{ - NATIVE_ARGS(const u8 *jumpInstr); - if ((GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX)) - gBattlescriptCurrInstr = cmd->jumpInstr; - else - gBattlescriptCurrInstr = cmd->nextInstr; -} - -void BS_UndoDynamax(void) -{ - NATIVE_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); - - if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX) - { - UndoDynamax(battler); - gBattleScripting.battler = battler; - BattleScriptCall(BattleScript_DynamaxEnds_Ret); - return; - } - - gBattlescriptCurrInstr = cmd->nextInstr; -} diff --git a/src/battle_end_turn.c b/src/battle_end_turn.c index 341ab90dc..0e0691b20 100644 --- a/src/battle_end_turn.c +++ b/src/battle_end_turn.c @@ -15,7 +15,7 @@ static u32 GetBattlerSideForMessage(enum BattleSide side) { - u32 battler = 0; + enum BattlerId battler = 0; for (battler = 0; battler < gBattlersCount; battler++) { @@ -26,23 +26,22 @@ static u32 GetBattlerSideForMessage(enum BattleSide side) return battler; } -static bool32 HandleEndTurnOrder(u32 battler) +static bool32 HandleEndTurnOrder(enum BattlerId battler) { bool32 effect = FALSE; gBattleTurnCounter++; gBattleStruct->eventState.endTurn++; - for (u32 i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) gBattlerByTurnOrder[i] = i; SortBattlersBySpeed(gBattlerByTurnOrder, FALSE); return effect; } -static bool32 HandleEndTurnVarious(u32 battler) +static bool32 HandleEndTurnVarious(enum BattlerId battler) { - u32 i; bool32 effect = FALSE; gBattleStruct->eventState.endTurn++; @@ -50,13 +49,13 @@ static bool32 HandleEndTurnVarious(u32 battler) if (gFieldTimers.fairyLockTimer > 0 && --gFieldTimers.fairyLockTimer == 0) gFieldStatuses &= ~STATUS_FIELD_FAIRY_LOCK; - for (i = 0; i < NUM_BATTLE_SIDES; i++) + for (enum BattleSide i = 0; i < NUM_BATTLE_SIDES; i++) { if (gSideTimers[i].damageNonTypesTimer > 0 && --gSideTimers[i].damageNonTypesTimer == 0) gSideStatuses[i] &= ~SIDE_STATUS_DAMAGE_NON_TYPES; } - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].volatiles.throatChopTimer > 0) gBattleMons[i].volatiles.throatChopTimer--; @@ -87,13 +86,13 @@ static bool32 HandleEndTurnVarious(u32 battler) return effect; } -static bool32 HandleEndTurnWeather(u32 battler) +static bool32 HandleEndTurnWeather(enum BattlerId battler) { gBattleStruct->eventState.endTurn++; return EndOrContinueWeather(); } -static bool32 HandleEndTurnWeatherDamage(u32 battler) +static bool32 HandleEndTurnWeatherDamage(enum BattlerId battler) { bool32 effect = FALSE; @@ -182,7 +181,7 @@ static bool32 HandleEndTurnWeatherDamage(u32 battler) return effect; } -static bool32 HandleEndTurnEmergencyExit(u32 battler) +static bool32 HandleEndTurnEmergencyExit(enum BattlerId battler) { bool32 effect = FALSE; enum Ability ability = GetBattlerAbility(battler); @@ -201,7 +200,7 @@ static bool32 HandleEndTurnEmergencyExit(u32 battler) return effect; } -static bool32 HandleEndTurnAffection(u32 battler) +static bool32 HandleEndTurnAffection(enum BattlerId battler) { bool32 effect = FALSE; @@ -224,7 +223,7 @@ static bool32 HandleEndTurnAffection(u32 battler) // Note: Technically Future Sight, Doom Desire and Wish need a queue but // I think we should accept this slight inconsistency so custom moves don't have to touch this code -static bool32 HandleEndTurnFutureSight(u32 battler) +static bool32 HandleEndTurnFutureSight(enum BattlerId battler) { bool32 effect = FALSE; @@ -258,7 +257,7 @@ static bool32 HandleEndTurnFutureSight(u32 battler) return effect; } -static bool32 HandleEndTurnWish(u32 battler) +static bool32 HandleEndTurnWish(enum BattlerId battler) { bool32 effect = FALSE; @@ -288,7 +287,7 @@ static bool32 HandleEndTurnWish(u32 battler) return effect; } -static bool32 HandleEndTurnFirstEventBlock(u32 battler) +static bool32 HandleEndTurnFirstEventBlock(enum BattlerId battler) { bool32 effect = FALSE; enum BattleSide side; @@ -392,7 +391,7 @@ static bool32 HandleEndTurnFirstEventBlock(u32 battler) return effect; } -static bool32 HandleEndTurnAquaRing(u32 battler) +static bool32 HandleEndTurnAquaRing(enum BattlerId battler) { bool32 effect = FALSE; @@ -411,7 +410,7 @@ static bool32 HandleEndTurnAquaRing(u32 battler) return effect; } -static bool32 HandleEndTurnIngrain(u32 battler) +static bool32 HandleEndTurnIngrain(enum BattlerId battler) { bool32 effect = FALSE; @@ -430,7 +429,7 @@ static bool32 HandleEndTurnIngrain(u32 battler) return effect; } -static bool32 HandleEndTurnLeechSeed(u32 battler) +static bool32 HandleEndTurnLeechSeed(enum BattlerId battler) { bool32 effect = FALSE; @@ -470,7 +469,7 @@ static bool32 HandleEndTurnLeechSeed(u32 battler) return effect; } -static bool32 HandleEndTurnPoison(u32 battler) +static bool32 HandleEndTurnPoison(enum BattlerId battler) { bool32 effect = FALSE; @@ -511,7 +510,7 @@ static bool32 HandleEndTurnPoison(u32 battler) return effect; } -static bool32 HandleEndTurnBurn(u32 battler) +static bool32 HandleEndTurnBurn(enum BattlerId battler) { bool32 effect = FALSE; @@ -538,7 +537,7 @@ static bool32 HandleEndTurnBurn(u32 battler) return effect; } -static bool32 HandleEndTurnFrostbite(u32 battler) +static bool32 HandleEndTurnFrostbite(enum BattlerId battler) { bool32 effect = FALSE; @@ -556,7 +555,7 @@ static bool32 HandleEndTurnFrostbite(u32 battler) return effect; } -static bool32 HandleEndTurnNightmare(u32 battler) +static bool32 HandleEndTurnNightmare(enum BattlerId battler) { bool32 effect = FALSE; @@ -581,7 +580,7 @@ static bool32 HandleEndTurnNightmare(u32 battler) return effect; } -static bool32 HandleEndTurnCurse(u32 battler) +static bool32 HandleEndTurnCurse(enum BattlerId battler) { bool32 effect = FALSE; @@ -599,7 +598,7 @@ static bool32 HandleEndTurnCurse(u32 battler) return effect; } -static bool32 HandleEndTurnWrap(u32 battler) +static bool32 HandleEndTurnWrap(enum BattlerId battler) { bool32 effect = FALSE; @@ -636,7 +635,7 @@ static bool32 HandleEndTurnWrap(u32 battler) return effect; } -static bool32 HandleEndTurnSaltCure(u32 battler) +static bool32 HandleEndTurnSaltCure(enum BattlerId battler) { bool32 effect = FALSE; @@ -660,7 +659,7 @@ static bool32 HandleEndTurnSaltCure(u32 battler) return effect; } -static bool32 HandleEndTurnOctolock(u32 battler) +static bool32 HandleEndTurnOctolock(enum BattlerId battler) { bool32 effect = FALSE; @@ -677,7 +676,7 @@ static bool32 HandleEndTurnOctolock(u32 battler) return effect; } -static bool32 HandleEndTurnSyrupBomb(u32 battler) +static bool32 HandleEndTurnSyrupBomb(enum BattlerId battler) { bool32 effect = FALSE; @@ -696,7 +695,7 @@ static bool32 HandleEndTurnSyrupBomb(u32 battler) return effect; } -static bool32 HandleEndTurnTaunt(u32 battler) +static bool32 HandleEndTurnTaunt(enum BattlerId battler) { bool32 effect = FALSE; @@ -713,7 +712,7 @@ static bool32 HandleEndTurnTaunt(u32 battler) return effect; } -static bool32 HandleEndTurnTorment(u32 battler) +static bool32 HandleEndTurnTorment(enum BattlerId battler) { bool32 effect = FALSE; @@ -730,7 +729,7 @@ static bool32 HandleEndTurnTorment(u32 battler) return effect; } -static bool32 HandleEndTurnEncore(u32 battler) +static bool32 HandleEndTurnEncore(enum BattlerId battler) { bool32 effect = FALSE; @@ -757,7 +756,7 @@ static bool32 HandleEndTurnEncore(u32 battler) return effect; } -static bool32 HandleEndTurnDisable(u32 battler) +static bool32 HandleEndTurnDisable(enum BattlerId battler) { bool32 effect = FALSE; @@ -788,7 +787,7 @@ static bool32 HandleEndTurnDisable(u32 battler) return effect; } -static bool32 HandleEndTurnMagnetRise(u32 battler) +static bool32 HandleEndTurnMagnetRise(enum BattlerId battler) { bool32 effect = FALSE; @@ -805,7 +804,7 @@ static bool32 HandleEndTurnMagnetRise(u32 battler) return effect; } -static bool32 HandleEndTurnTelekinesis(u32 battler) +static bool32 HandleEndTurnTelekinesis(enum BattlerId battler) { bool32 effect = FALSE; @@ -821,7 +820,7 @@ static bool32 HandleEndTurnTelekinesis(u32 battler) return effect; } -static bool32 HandleEndTurnHealBlock(u32 battler) +static bool32 HandleEndTurnHealBlock(enum BattlerId battler) { bool32 effect = FALSE; @@ -839,7 +838,7 @@ static bool32 HandleEndTurnHealBlock(u32 battler) return effect; } -static bool32 HandleEndTurnEmbargo(u32 battler) +static bool32 HandleEndTurnEmbargo(enum BattlerId battler) { bool32 effect = FALSE; @@ -855,7 +854,7 @@ static bool32 HandleEndTurnEmbargo(u32 battler) return effect; } -static bool32 HandleEndTurnYawn(u32 battler) +static bool32 HandleEndTurnYawn(enum BattlerId battler) { bool32 effect = FALSE; @@ -919,7 +918,7 @@ static bool32 HandleEndTurnYawn(u32 battler) return effect; } -static bool32 HandleEndTurnPerishSong(u32 battler) +static bool32 HandleEndTurnPerishSong(enum BattlerId battler) { bool32 effect = FALSE; @@ -945,7 +944,7 @@ static bool32 HandleEndTurnPerishSong(u32 battler) return effect; } -static bool32 HandleEndTurnRoost(u32 battler) +static bool32 HandleEndTurnRoost(enum BattlerId battler) { bool32 effect = FALSE; @@ -957,7 +956,7 @@ static bool32 HandleEndTurnRoost(u32 battler) return effect; } -static bool32 HandleEndTurnSecondEventBlock(u32 battler) +static bool32 HandleEndTurnSecondEventBlock(enum BattlerId battler) { bool32 effect = FALSE; @@ -1078,7 +1077,7 @@ static bool32 HandleEndTurnSecondEventBlock(u32 battler) return effect; } -static bool32 HandleEndTurnTrickRoom(u32 battler) +static bool32 HandleEndTurnTrickRoom(enum BattlerId battler) { bool32 effect = FALSE; @@ -1094,7 +1093,7 @@ static bool32 HandleEndTurnTrickRoom(u32 battler) return effect; } -static bool32 HandleEndTurnGravity(u32 battler) +static bool32 HandleEndTurnGravity(enum BattlerId battler) { bool32 effect = FALSE; @@ -1110,7 +1109,7 @@ static bool32 HandleEndTurnGravity(u32 battler) return effect; } -static bool32 HandleEndTurnWaterSport(u32 battler) +static bool32 HandleEndTurnWaterSport(enum BattlerId battler) { bool32 effect = FALSE; @@ -1126,7 +1125,7 @@ static bool32 HandleEndTurnWaterSport(u32 battler) return effect; } -static bool32 HandleEndTurnMudSport(u32 battler) +static bool32 HandleEndTurnMudSport(enum BattlerId battler) { bool32 effect = FALSE; @@ -1142,7 +1141,7 @@ static bool32 HandleEndTurnMudSport(u32 battler) return effect; } -static bool32 HandleEndTurnWonderRoom(u32 battler) +static bool32 HandleEndTurnWonderRoom(enum BattlerId battler) { bool32 effect = FALSE; @@ -1158,7 +1157,7 @@ static bool32 HandleEndTurnWonderRoom(u32 battler) return effect; } -static bool32 HandleEndTurnMagicRoom(u32 battler) +static bool32 HandleEndTurnMagicRoom(enum BattlerId battler) { bool32 effect = FALSE; @@ -1188,7 +1187,7 @@ static bool32 EndTurnTerrain(u32 terrainFlag, u32 stringTableId) return FALSE; } -static bool32 HandleEndTurnTerrain(u32 battler) +static bool32 HandleEndTurnTerrain(enum BattlerId battler) { bool32 effect = FALSE; @@ -1206,7 +1205,7 @@ static bool32 HandleEndTurnTerrain(u32 battler) return effect; } -static bool32 HandleEndTurnThirdEventBlock(u32 battler) +static bool32 HandleEndTurnThirdEventBlock(enum BattlerId battler) { bool32 effect = FALSE; @@ -1312,7 +1311,7 @@ static bool32 HandleEndTurnThirdEventBlock(u32 battler) return effect; } -static bool32 HandleEndTurnFormChangeAbilities(u32 battler) +static bool32 HandleEndTurnFormChange(enum BattlerId battler) { bool32 effect = FALSE; @@ -1320,29 +1319,30 @@ static bool32 HandleEndTurnFormChangeAbilities(u32 battler) gBattleStruct->eventState.endTurnBattler++; - switch (ability) + if (TryBattleFormChange(battler, FORM_CHANGE_BATTLE_TURN_END, ability) + || TryBattleFormChange(battler, FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, ability)) { - case ABILITY_POWER_CONSTRUCT: - case ABILITY_SCHOOLING: - case ABILITY_SHIELDS_DOWN: - case ABILITY_ZEN_MODE: - case ABILITY_HUNGER_SWITCH: - if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, MOVE_NONE, TRUE)) - effect = TRUE; - default: - break; + gBattleScripting.battler = battler; + gBattleScripting.abilityPopupOverwrite = ability; // To prevent the new form's ability from pop up + if (ability == ABILITY_POWER_CONSTRUCT) // Special animation + BattleScriptExecute(BattleScript_PowerConstruct); + else if (ability == ABILITY_HUNGER_SWITCH) + BattleScriptExecute(BattleScript_BattlerFormChangeEnd3NoPopup); + else + BattleScriptExecute(BattleScript_BattlerFormChangeEnd2); // Generic animation + effect = TRUE; } return effect; } -static bool32 HandleEndTurnEjectPack(u32 battler) +static bool32 HandleEndTurnEjectPack(enum BattlerId battler) { gBattleStruct->eventState.endTurn++; return TrySwitchInEjectPack(END_TURN); } -static bool32 HandleEndTurnDynamax(u32 battler) +static bool32 HandleEndTurnDynamax(enum BattlerId battler) { bool32 effect = FALSE; @@ -1359,7 +1359,7 @@ static bool32 HandleEndTurnDynamax(u32 battler) return effect; } -static bool32 TryEndTurnTrainerSlide(u32 battler) +static bool32 TryEndTurnTrainerSlide(enum BattlerId battler) { return ((ShouldDoTrainerSlide(battler, TRAINER_SLIDE_LAST_LOW_HP) != TRAINER_SLIDE_TARGET_NONE) || (ShouldDoTrainerSlide(battler, TRAINER_SLIDE_LAST_HALF_HP) != TRAINER_SLIDE_TARGET_NONE) @@ -1370,7 +1370,7 @@ static bool32 TryEndTurnTrainerSlide(u32 battler) || (ShouldDoTrainerSlide(battler, TRAINER_SLIDE_ENEMY_MON_UNAFFECTED) != TRAINER_SLIDE_TARGET_NONE)); } -static bool32 HandleEndTurnTrainerASlides(u32 battler) +static bool32 HandleEndTurnTrainerASlides(enum BattlerId battler) { gBattleStruct->eventState.endTurnBattler++; bool32 slide = TryEndTurnTrainerSlide(B_BATTLER_1); @@ -1379,7 +1379,7 @@ static bool32 HandleEndTurnTrainerASlides(u32 battler) return slide; } -static bool32 HandleEndTurnTrainerBSlides(u32 battler) +static bool32 HandleEndTurnTrainerBSlides(enum BattlerId battler) { gBattleStruct->eventState.endTurnBattler++; @@ -1401,7 +1401,7 @@ static bool32 HandleEndTurnTrainerBSlides(u32 battler) return slide; } -static bool32 HandleEndTurnTrainerPartnerSlides(u32 battler) +static bool32 HandleEndTurnTrainerPartnerSlides(enum BattlerId battler) { gBattleStruct->eventState.endTurnBattler++; @@ -1429,7 +1429,7 @@ static bool32 HandleEndTurnTrainerPartnerSlides(u32 battler) some commands end2 */ -static bool32 (*const sEndTurnEffectHandlers[])(u32 battler) = +static bool32 (*const sEndTurnEffectHandlers[])(enum BattlerId battler) = { [ENDTURN_ORDER] = HandleEndTurnOrder, [ENDTURN_VARIOUS] = HandleEndTurnVarious, @@ -1475,7 +1475,7 @@ static bool32 (*const sEndTurnEffectHandlers[])(u32 battler) = [ENDTURN_TERRAIN] = HandleEndTurnTerrain, [ENDTURN_THIRD_EVENT_BLOCK] = HandleEndTurnThirdEventBlock, [ENDTURN_EMERGENCY_EXIT_4] = HandleEndTurnEmergencyExit, - [ENDTURN_FORM_CHANGE_ABILITIES] = HandleEndTurnFormChangeAbilities, + [ENDTURN_FORM_CHANGE] = HandleEndTurnFormChange, [ENDTURN_EJECT_PACK] = HandleEndTurnEjectPack, [ENDTURN_DYNAMAX] = HandleEndTurnDynamax, [ENDTURN_TRAINER_A_SLIDES] = HandleEndTurnTrainerASlides, diff --git a/src/battle_frontier.c b/src/battle_frontier.c index 5ed6f0adf..9a1fb4c6d 100644 --- a/src/battle_frontier.c +++ b/src/battle_frontier.c @@ -71,15 +71,6 @@ static void Task_StartBattleAfterTransition(u8 taskId) } } -// placeholders -#define B_TRANSITION_GROUP_B_PALACE B_TRANSITION_BLUR -#define B_TRANSITION_GROUP_B_TOWER B_TRANSITION_BLUR -#define B_TRANSITION_GROUP_B_DOME B_TRANSITION_BLUR -#define B_TRANSITION_GROUP_B_ARENA B_TRANSITION_BLUR -#define B_TRANSITION_GROUP_B_FACTORY B_TRANSITION_BLUR -#define B_TRANSITION_GROUP_B_PIKE B_TRANSITION_BLUR -#define B_TRANSITION_GROUP_B_PYRAMID B_TRANSITION_BLUR - static void DoFacilityTrainerBattleInternal(u8 facility) { gBattleScripting.specialTrainerBattleType = facility; diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index 851a6ba6e..f8ad351e2 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -26,13 +26,13 @@ #include "trainer_pokemon_sprites.h" #include "constants/songs.h" #include "constants/rgb.h" -// #include "constants/battle_palace.h" +#include "constants/battle_palace.h" #include "constants/battle_move_effects.h" #include "constants/event_objects.h" // only for SHADOW_SIZE constants // this file's functions -// static u8 GetBattlePalaceMoveGroup(u8 battler, u16 move); -// static u16 GetBattlePalaceTarget(u32 battler); +static u8 GetBattlePalaceMoveGroup(enum BattlerId battler, enum Move move); +static u16 GetBattlePalaceTarget(enum BattlerId battler); static void SpriteCB_TrainerSlideVertical(struct Sprite *sprite); static bool8 ShouldAnimBeDoneRegardlessOfSubstitute(u8 animId); static void Task_ClearBitWhenBattleTableAnimDone(u8 taskId); @@ -187,6 +187,254 @@ void FreeBattleSpritesData(void) FREE_AND_SET_NULL(gBattleSpritesDataPtr); } +// Pokémon chooses move to use in Battle Palace rather than player +u16 ChooseMoveAndTargetInBattlePalace(enum BattlerId battler) +{ + s32 i, var1, var2; + s32 chosenMoveIndex = -1; + struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); + u8 unusableMovesBits = CheckMoveLimitations(battler, 0, MOVE_LIMITATIONS_ALL); + s32 percent = Random() % 100; + + // Heavy variable re-use here makes this hard to read without defines + // Possibly just optimization? might still match with additional vars + #define maxGroupNum var1 + #define minGroupNum var2 + #define selectedGroup percent + #define selectedMoves var2 + #define numMovesPerGroup var1 + #define numMultipleMoveGroups var2 + #define randSelectGroup var2 + + // If battler is < 50% HP and not asleep, use second set of move group likelihoods + // otherwise use first set + i = (gBattleStruct->palaceFlags & (1u << battler)) ? 2 : 0; + minGroupNum = i; + + maxGroupNum = i + 2; // + 2 because there are two percentages per set of likelihoods + + // Each nature has a different percent chance to select a move from one of 3 move groups + // If percent is less than 1st check, use move from "Attack" group + // If percent is less than 2nd check, use move from "Defense" group + // Otherwise use move from "Support" group + for (; i < maxGroupNum; i++) + { + if (gNaturesInfo[GetNatureFromPersonality(gBattleMons[battler].personality)].battlePalacePercents[i] > percent) + break; + } + selectedGroup = i - minGroupNum; + if (i == maxGroupNum) + selectedGroup = PALACE_MOVE_GROUP_SUPPORT; + + // Flag moves that match selected group, to be passed to AI + for (selectedMoves = 0, i = 0; i < MAX_MON_MOVES; i++) + { + if (moveInfo->moves[i] == MOVE_NONE) + break; + if (selectedGroup == GetBattlePalaceMoveGroup(battler, moveInfo->moves[i]) && moveInfo->currentPp[i] != 0) + selectedMoves |= 1u << i; + } + + // Pass selected moves to AI, pick one + if (selectedMoves != 0) + { + // Lower 4 bits of palaceFlags are flags for each battler. + // Clear the rest of palaceFlags, then set the selected moves in the upper 4 bits. + gBattleStruct->palaceFlags &= (1 << MAX_BATTLERS_COUNT) - 1; + gBattleStruct->palaceFlags |= (selectedMoves << MAX_BATTLERS_COUNT); + BattleAI_SetupAIData(selectedMoves, battler); + chosenMoveIndex = BattleAI_ChooseMoveIndex(battler); + } + + // If no moves matched the selected group, pick a new move from groups the Pokémon has + // In this case the AI is not checked again, so the choice may be worse + // If a move is chosen this way, there's a 50% chance that it will be unable to use it anyway + if (chosenMoveIndex == -1 || chosenMoveIndex >= MAX_MON_MOVES) + { + chosenMoveIndex = -1; + if (unusableMovesBits != ALL_MOVES_MASK) + { + numMovesPerGroup = 0, numMultipleMoveGroups = 0; + + for (i = 0; i < MAX_MON_MOVES; i++) + { + // Count the number of usable moves the battler has in each move group. + // The totals will be stored separately in 3 groups of 4 bits each in numMovesPerGroup. + if (GetBattlePalaceMoveGroup(battler, moveInfo->moves[i]) == PALACE_MOVE_GROUP_ATTACK && !((1u << i) & unusableMovesBits)) + numMovesPerGroup += (1 << 0); + if (GetBattlePalaceMoveGroup(battler, moveInfo->moves[i]) == PALACE_MOVE_GROUP_DEFENSE && !((1u << i) & unusableMovesBits)) + numMovesPerGroup += (1 << 4); + if (GetBattlePalaceMoveGroup(battler, moveInfo->moves[i]) == PALACE_MOVE_GROUP_SUPPORT && !((1u << i) & unusableMovesBits)) + numMovesPerGroup += (1 << 8); + } + + // Count the number of move groups for which the battler has at least 2 usable moves. + // This is a roundabout way to determine if there is a move group that should be + // preferred, because it has multiple move options and the others do not. + // The condition intended to check the total for the Support group is accidentally + // checking the Defense total, and is never true. As a result the preferences for + // random move selection here will skew away from the Support move group. + if ((numMovesPerGroup & 0xF) >= 2) + numMultipleMoveGroups++; + if ((numMovesPerGroup & (0xF << 4)) >= (2 << 4)) + numMultipleMoveGroups++; +#ifdef BUGFIX + if ((numMovesPerGroup & (0xF << 8)) >= (2 << 8)) +#else + if ((numMovesPerGroup & (0xF << 4)) >= (2 << 8)) +#endif + numMultipleMoveGroups++; + + + // By this point we already know the battler only has usable moves from at most 2 of the 3 move groups, + // because they had no usable moves from the move group that was selected based on Nature. + // + // The below condition is effectively 'numMultipleMoveGroups != 1'. + // There is no stand-out group with multiple moves to choose from, so we pick randomly. + // Note that because of the bug above the battler may actually have any number of Support moves. + if (numMultipleMoveGroups > 1 || numMultipleMoveGroups == 0) + { + do + { + i = Random() % MAX_MON_MOVES; + if (!((1u << i) & unusableMovesBits)) + chosenMoveIndex = i; + } while (chosenMoveIndex == -1); + } + else + { + // The battler has just 1 move group with multiple move options to choose from. + // Choose a move randomly from this group. + + // Same bug as the previous set of conditions (the condition for Support is never true). + // This bug won't cause a softlock below, because if Support is the only group with multiple + // moves then it won't have been counted, and the 'numMultipleMoveGroups == 0' above will be true. + if ((numMovesPerGroup & 0xF) >= 2) + randSelectGroup = PALACE_MOVE_GROUP_ATTACK; + if ((numMovesPerGroup & (0xF << 4)) >= (2 << 4)) + randSelectGroup = PALACE_MOVE_GROUP_DEFENSE; +#ifdef BUGFIX + if ((numMovesPerGroup & (0xF << 8)) >= (2 << 8)) +#else + if ((numMovesPerGroup & (0xF << 4)) >= (2 << 8)) +#endif + randSelectGroup = PALACE_MOVE_GROUP_SUPPORT; + + do + { + i = Random() % MAX_MON_MOVES; + if (!((1u << i) & unusableMovesBits) && randSelectGroup == GetBattlePalaceMoveGroup(battler, moveInfo->moves[i])) + chosenMoveIndex = i; + } while (chosenMoveIndex == -1); + } + + // Because the selected move was not from the Nature-chosen move group there's a 50% chance + // that it will be unable to use it. This could have been checked earlier to avoid the above work. + if (Random() % 100 >= 50) + { + gProtectStructs[battler].palaceUnableToUseMove = TRUE; + return 0; + } + } + else + { + // All the battler's moves were flagged as unusable. + gProtectStructs[battler].palaceUnableToUseMove = TRUE; + return 0; + } + } + + enum MoveTarget moveTarget = GetBattlerMoveTargetType(battler, moveInfo->moves[chosenMoveIndex]); + + if (moveTarget == TARGET_USER || moveTarget == TARGET_USER_OR_ALLY || moveTarget == TARGET_USER_AND_ALLY) + chosenMoveIndex |= (battler << 8); + else if (moveTarget == TARGET_SELECTED || moveTarget == TARGET_SMART) + chosenMoveIndex |= GetBattlePalaceTarget(battler); + else + chosenMoveIndex |= (GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerSide(battler))) << 8); + + return chosenMoveIndex; +} + +#undef maxGroupNum +#undef minGroupNum +#undef selectedGroup +#undef selectedMoves +#undef moveTarget +#undef numMovesPerGroup +#undef numMultipleMoveGroups +#undef randSelectGroup + +static u8 GetBattlePalaceMoveGroup(enum BattlerId battler, enum Move move) +{ + switch (GetBattlerMoveTargetType(battler, move)) + { + case TARGET_SELECTED: + case TARGET_USER_AND_ALLY: + case TARGET_SMART: + case TARGET_OPPONENT: + case TARGET_RANDOM: + case TARGET_BOTH: + case TARGET_FOES_AND_ALLY: + case TARGET_ALL_BATTLERS: + case TARGET_FIELD: + if (IsBattleMoveStatus(move)) + return PALACE_MOVE_GROUP_SUPPORT; + else + return PALACE_MOVE_GROUP_ATTACK; + break; + case TARGET_DEPENDS: + case TARGET_OPPONENTS_FIELD: + case TARGET_ALLY: + case TARGET_USER_OR_ALLY: + return PALACE_MOVE_GROUP_SUPPORT; + case TARGET_USER: + return PALACE_MOVE_GROUP_DEFENSE; + default: + return PALACE_MOVE_GROUP_ATTACK; + } +} + +static u16 GetBattlePalaceTarget(enum BattlerId battler) +{ + if (IsDoubleBattle()) + { + enum BattlerId opposing1, opposing2; + + if (IsOnPlayerSide(battler)) + { + opposing1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + opposing2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); + } + else + { + opposing1 = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + opposing2 = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); + } + + if (gBattleMons[opposing1].hp == gBattleMons[opposing2].hp) + return (BATTLE_OPPOSITE(battler & BIT_SIDE) + (Random() & 2)) << 8; + + switch (gNaturesInfo[GetNatureFromPersonality(gBattleMons[battler].personality)].battlePalaceSmokescreen) + { + case PALACE_TARGET_STRONGER: + if (gBattleMons[opposing1].hp > gBattleMons[opposing2].hp) + return opposing1 << 8; + else + return opposing2 << 8; + case PALACE_TARGET_WEAKER: + if (gBattleMons[opposing1].hp < gBattleMons[opposing2].hp) + return opposing1 << 8; + else + return opposing2 << 8; + case PALACE_TARGET_RANDOM: + return (BATTLE_OPPOSITE(battler & BIT_SIDE) + (Random() & 2)) << 8; + } + } + + return BATTLE_OPPOSITE(battler) << 8; +} + // Wait for the Pokémon to finish appearing out from the Poké Ball on send out void SpriteCB_WaitForBattlerBallReleaseAnim(struct Sprite *sprite) { @@ -719,7 +967,7 @@ void CopyBattleSpriteInvisibility(u8 battler) gBattleSpritesDataPtr->battlerData[battler].invisible = gSprites[gBattlerSpriteIds[battler]].invisible; } -void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bool32 trackEnemyPersonality, bool32 ghostUnveil) +void HandleSpeciesGfxDataChange(enum BattlerId battlerAtk, enum BattlerId battlerDef, u8 changeType) { u32 personalityValue, position, paletteOffset, targetSpecies; bool32 isShiny; @@ -744,7 +992,7 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo else { position = GetBattlerPosition(battlerAtk); - if (gBattleSpritesDataPtr->battlerData[battlerAtk].transformSpecies == SPECIES_NONE) + if (changeType == SPECIES_GFX_CHANGE_TRANSFORM) { // Get base form if its currently Gigantamax if (IsGigantamaxed(battlerDef)) @@ -753,24 +1001,23 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo targetSpecies = GetIllusionMonSpecies(battlerDef); else targetSpecies = GetMonData(monDef, MON_DATA_SPECIES); - personalityValue = GetMonData(monAtk, MON_DATA_PERSONALITY); - isShiny = GetMonData(monAtk, MON_DATA_IS_SHINY); } else { - targetSpecies = gBattleSpritesDataPtr->battlerData[battlerAtk].transformSpecies; - if (B_TRANSFORM_SHINY >= GEN_4 && trackEnemyPersonality && !megaEvo) - { - personalityValue = GetMonData(monDef, MON_DATA_PERSONALITY); - isShiny = GetMonData(monDef, MON_DATA_IS_SHINY); - } - else - { - personalityValue = GetMonData(monAtk, MON_DATA_PERSONALITY); - isShiny = GetMonData(monAtk, MON_DATA_IS_SHINY); - } + targetSpecies = GetMonData(monAtk, MON_DATA_SPECIES); } + gBattleSpritesDataPtr->battlerData[battlerAtk].transformSpecies = targetSpecies; + if (changeType == SPECIES_GFX_CHANGE_TRANSFORM) + { + personalityValue = gTransformedPersonalities[battlerAtk]; + isShiny = gTransformedShininess[battlerAtk]; + } + else + { + personalityValue = GetMonData(monAtk, MON_DATA_PERSONALITY); + isShiny = GetMonData(monAtk, MON_DATA_IS_SHINY); + } HandleLoadSpecialPokePic(!IsOnPlayerSide(battlerAtk), gMonSpritesGfxPtr->spritesGfx[position], targetSpecies, @@ -783,20 +1030,16 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo paletteData = GetMonSpritePalFromSpeciesAndPersonality(targetSpecies, isShiny, personalityValue); LoadPalette(paletteData, paletteOffset, PLTT_SIZE_4BPP); - if (ghostUnveil) + if (changeType == SPECIES_GFX_CHANGE_GHOST_UNVEIL) { SetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_NICKNAME, gSpeciesInfo[targetSpecies].speciesName); UpdateNickInHealthbox(gHealthboxSpriteIds[battlerAtk], &gEnemyParty[gBattlerPartyIndexes[battlerAtk]]); - TryAddPokeballIconToHealthbox(gHealthboxSpriteIds[battlerAtk], 1); + TryAddPokeballIconToHealthbox(gHealthboxSpriteIds[battlerAtk], TRUE); } - else if (!megaEvo) + else if (changeType == SPECIES_GFX_CHANGE_TRANSFORM) { BlendPalette(paletteOffset, 16, 6, RGB_WHITE); CpuCopy32(&gPlttBufferFaded[paletteOffset], &gPlttBufferUnfaded[paletteOffset], PLTT_SIZEOF(16)); - if (!IsContest()) - { - gBattleSpritesDataPtr->battlerData[battlerAtk].transformSpecies = targetSpecies; - } } // dynamax tint @@ -810,6 +1053,13 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo CpuCopy32(gPlttBufferFaded + paletteOffset, gPlttBufferUnfaded + paletteOffset, PLTT_SIZEOF(16)); } + // Terastallization's tint + if (changeType != SPECIES_GFX_CHANGE_ILLUSION_OFF && GetActiveGimmick(battlerAtk) == GIMMICK_TERA) + { + BlendPalette(paletteOffset, 16, 8, GetTeraTypeRGB(GetBattlerTeraType(battlerAtk))); + CpuCopy32(gPlttBufferFaded + paletteOffset, gPlttBufferUnfaded + paletteOffset, PLTT_SIZEOF(16)); + } + gSprites[gBattlerSpriteIds[battlerAtk]].y = GetBattlerSpriteDefault_Y(battlerAtk); StartSpriteAnim(&gSprites[gBattlerSpriteIds[battlerAtk]], 0); } diff --git a/src/battle_gimmick.c b/src/battle_gimmick.c index 2381a6de0..26d189efe 100644 --- a/src/battle_gimmick.c +++ b/src/battle_gimmick.c @@ -19,11 +19,10 @@ // Populates gBattleStruct->gimmick.usableGimmick for each battler. void AssignUsableGimmicks(void) { - u32 battler, gimmick; - for (battler = 0; battler < gBattlersCount; ++battler) + for (enum BattlerId battler = 0; battler < gBattlersCount; ++battler) { gBattleStruct->gimmick.usableGimmick[battler] = GIMMICK_NONE; - for (gimmick = 0; gimmick < GIMMICKS_COUNT; ++gimmick) + for (enum Gimmick gimmick = 0; gimmick < GIMMICKS_COUNT; ++gimmick) { if (CanActivateGimmick(battler, gimmick)) { @@ -35,44 +34,43 @@ void AssignUsableGimmicks(void) } // Returns whether a battler is able to use a gimmick. Checks consumption and gimmick specific functions. -bool32 CanActivateGimmick(u32 battler, enum Gimmick gimmick) +bool32 CanActivateGimmick(enum BattlerId battler, enum Gimmick gimmick) { return gGimmicksInfo[gimmick].CanActivate != NULL && gGimmicksInfo[gimmick].CanActivate(battler); } // Returns whether the player has a gimmick selected while in the move selection menu. -bool32 IsGimmickSelected(u32 battler, enum Gimmick gimmick) +bool32 IsGimmickSelected(enum BattlerId battler, enum Gimmick gimmick) { // There's no player select in tests, but some gimmicks need to test choice before they are fully activated. - if (TESTING) - return (gBattleStruct->gimmick.toActivate & (1u << battler)) && gBattleStruct->gimmick.usableGimmick[battler] == gimmick; - else - return gBattleStruct->gimmick.usableGimmick[battler] == gimmick && gBattleStruct->gimmick.playerSelect; + #if TESTING + return (gBattleStruct->gimmick.toActivate & (1u << battler)) && gBattleStruct->gimmick.usableGimmick[battler] == gimmick; + #else + return gBattleStruct->gimmick.usableGimmick[battler] == gimmick && gBattleStruct->gimmick.playerSelect; + #endif } // Sets a battler as having a gimmick active using their party index. -void SetActiveGimmick(u32 battler, enum Gimmick gimmick) +void SetActiveGimmick(enum BattlerId battler, enum Gimmick gimmick) { gBattleStruct->gimmick.activeGimmick[GetBattlerSide(battler)][gBattlerPartyIndexes[battler]] = gimmick; } // Returns a battler's active gimmick, if any. -enum Gimmick GetActiveGimmick(u32 battler) +enum Gimmick GetActiveGimmick(enum BattlerId battler) { return gBattleStruct->gimmick.activeGimmick[GetBattlerSide(battler)][gBattlerPartyIndexes[battler]]; } // Returns whether a trainer mon is intended to use an unrestrictive gimmick via .useGimmick (i.e Tera). -bool32 ShouldTrainerBattlerUseGimmick(u32 battler, enum Gimmick gimmick) +bool32 ShouldTrainerBattlerUseGimmick(enum BattlerId battler, enum Gimmick gimmick) { // There are no trainer party settings in battles, but the AI needs to know which gimmick to use. - if (TESTING) - { - return gimmick == TestRunner_Battle_GetChosenGimmick(GetBattlerTrainer(battler), gBattlerPartyIndexes[battler]); - } + #if TESTING + return gimmick == TestRunner_Battle_GetChosenGimmick(GetBattlerTrainer(battler), gBattlerPartyIndexes[battler]); + #else // The player can bypass these checks because they can choose through the controller. - else if (IsOnPlayerSide(battler) - && !((gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT)) + if (IsOnPlayerSide(battler) && !((gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT)) { return TRUE; } @@ -84,16 +82,17 @@ bool32 ShouldTrainerBattlerUseGimmick(u32 battler, enum Gimmick gimmick) if (gimmick == GIMMICK_DYNAMAX && gBattleStruct->opponentMonCanDynamax & 1 << gBattlerPartyIndexes[battler]) return TRUE; } + #endif return FALSE; } // Returns whether a trainer has used a gimmick during a battle. -bool32 HasTrainerUsedGimmick(u32 battler, enum Gimmick gimmick) +bool32 HasTrainerUsedGimmick(enum BattlerId battler, enum Gimmick gimmick) { if (IsDoubleBattle() && (IsPartnerMonFromSameTrainer(battler) || (gimmick == GIMMICK_DYNAMAX))) { - u32 partner = BATTLE_PARTNER(battler); + enum BattlerId partner = BATTLE_PARTNER(battler); if (gBattleStruct->gimmick.activated[partner][gimmick] || ((gBattleStruct->gimmick.toActivate & (1u << partner)) && gBattleStruct->gimmick.usableGimmick[partner] == gimmick)) return TRUE; @@ -103,7 +102,7 @@ bool32 HasTrainerUsedGimmick(u32 battler, enum Gimmick gimmick) } // Sets a gimmick as used by a trainer with checks for Multi Battles. -void SetGimmickAsActivated(u32 battler, enum Gimmick gimmick) +void SetGimmickAsActivated(enum BattlerId battler, enum Gimmick gimmick) { gBattleStruct->gimmick.activated[battler][gimmick] = TRUE; if (IsDoubleBattle() && (IsPartnerMonFromSameTrainer(battler) || (gimmick == GIMMICK_DYNAMAX))) @@ -128,7 +127,7 @@ void ChangeGimmickTriggerSprite(u32 spriteId, u32 animId) StartSpriteAnim(&gSprites[spriteId], animId); } -void CreateGimmickTriggerSprite(u32 battler) +void CreateGimmickTriggerSprite(enum BattlerId battler) { const struct GimmickInfo * gimmick = &gGimmicksInfo[gBattleStruct->gimmick.usableGimmick[battler]]; @@ -173,7 +172,7 @@ bool32 IsGimmickTriggerSpriteActive(void) return FALSE; } -bool32 IsGimmickTriggerSpriteMatchingBattler(u32 battler) +bool32 IsGimmickTriggerSpriteMatchingBattler(enum BattlerId battler) { if (battler == gSprites[gBattleStruct->gimmick.triggerSpriteId].tBattler) return TRUE; @@ -274,7 +273,7 @@ void LoadIndicatorSpritesGfx(void) static void SpriteCb_GimmickIndicator(struct Sprite *sprite) { - u32 battler = sprite->tBattler; + enum BattlerId battler = sprite->tBattler; sprite->x = gSprites[gHealthboxSpriteIds[battler]].x + sprite->tPosX + sprite->tLevelXDelta; sprite->x2 = gSprites[gHealthboxSpriteIds[battler]].x2; @@ -286,7 +285,7 @@ static inline u32 GetIndicatorSpriteId(u32 healthboxId) return gBattleStruct->gimmick.indicatorSpriteId[gSprites[healthboxId].hMain_Battler]; } -const u32 *GetIndicatorSpriteSrc(u32 battler) +const u32 *GetIndicatorSpriteSrc(enum BattlerId battler) { u32 gimmick = GetActiveGimmick(battler); @@ -311,7 +310,7 @@ const u32 *GetIndicatorSpriteSrc(u32 battler) } } -u32 GetIndicatorPalTag(u32 battler) +u32 GetIndicatorPalTag(enum BattlerId battler) { u32 gimmick = GetActiveGimmick(battler); if (IsBattlerPrimalReverted(battler)) @@ -326,7 +325,7 @@ u32 GetIndicatorPalTag(u32 battler) void UpdateIndicatorVisibilityAndType(u32 healthboxId, bool32 invisible) { - u32 battler = gSprites[healthboxId].hMain_Battler; + enum BattlerId battler = gSprites[healthboxId].hMain_Battler; u32 palTag = GetIndicatorPalTag(battler); struct Sprite *sprite = &gSprites[GetIndicatorSpriteId(healthboxId)]; @@ -378,7 +377,7 @@ static const s8 sIndicatorPositions[][2] = [B_POSITION_OPPONENT_RIGHT] = {40, -9}, }; -void CreateIndicatorSprite(u32 battler) +void CreateIndicatorSprite(enum BattlerId battler) { enum BattlerPosition position; u32 spriteId; diff --git a/src/battle_interface.c b/src/battle_interface.c index ac2456b87..8342fc1a7 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -1179,50 +1179,43 @@ void SwapHpBarsWithHpText(void) #define sSpeed data[3] #define sIsEmptyBall data[7] -u8 CreatePartyStatusSummarySprites(u8 battlerId, struct HpAndStatus *partyInfo, bool8 isSwitchingMons, bool8 isBattleStart) +u8 CreatePartyStatusSummarySprites(enum BattlerId battler, struct HpAndStatus *partyInfo, bool8 skipPlayer, bool8 isBattleStart) { bool8 isOpponent; - s8 nValidMons; - s16 x, y, x2, speed; - s32 i; + s16 bar_X, bar_Y, bar_pos2_X, bar_data0; + s32 i, j, var; u8 summaryBarSpriteId; u8 ballIconSpritesIds[PARTY_SIZE]; u8 taskId; - if (!isSwitchingMons || GetBattlerPosition(battlerId) != B_POSITION_OPPONENT_RIGHT) + if (!skipPlayer || GetBattlerPosition(battler) != B_POSITION_OPPONENT_RIGHT) { - if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) + if (IsOnPlayerSide(battler)) { isOpponent = FALSE; - x = 136, y = 96; - x2 = 100; - speed = -5; + bar_X = 136, bar_Y = 96; + bar_pos2_X = 100; + bar_data0 = -5; } else { isOpponent = TRUE; - if (!isSwitchingMons || !GetBattlerCoordsIndex(battlerId)) - x = 104, y = 40; + if (!skipPlayer || GetBattlerCoordsIndex(battler) == BATTLE_COORDS_SINGLES) + bar_X = 104, bar_Y = 40; else - x = 104, y = 16; + bar_X = 104, bar_Y = 16; - x2 = -100; - speed = 5; + bar_pos2_X = -100; + bar_data0 = 5; } } else { isOpponent = TRUE; - x = 104, y = 40; - x2 = -100; - speed = 5; - } - - for (i = 0, nValidMons = 0; i < PARTY_SIZE; i++) - { - if (partyInfo[i].hp != HP_EMPTY_SLOT) - nValidMons++; + bar_X = 104, bar_Y = 40; + bar_pos2_X = -100; + bar_data0 = 5; } LoadCompressedSpriteSheetUsingHeap(&sStatusSummaryBarSpriteSheet); @@ -1230,10 +1223,10 @@ u8 CreatePartyStatusSummarySprites(u8 battlerId, struct HpAndStatus *partyInfo, LoadSpritePalette(&sPartySummaryBarSpritePals); LoadSpritePalette(&sPartySummaryBallSpritePals); - summaryBarSpriteId = CreateSprite(&sPartySummaryBarSpriteTemplates[isOpponent], x, y, 10); + summaryBarSpriteId = CreateSprite(&sPartySummaryBarSpriteTemplates[isOpponent], bar_X, bar_Y, 10); SetSubspriteTables(&gSprites[summaryBarSpriteId], sStatusSummaryBar_SubspriteTable_Enter); - gSprites[summaryBarSpriteId].x2 = x2; - gSprites[summaryBarSpriteId].sEnterSpeed = speed; + gSprites[summaryBarSpriteId].x2 = bar_pos2_X; + gSprites[summaryBarSpriteId].sEnterSpeed = bar_data0; if (isOpponent) { @@ -1241,11 +1234,13 @@ u8 CreatePartyStatusSummarySprites(u8 battlerId, struct HpAndStatus *partyInfo, gSprites[summaryBarSpriteId].oam.matrixNum = ST_OAM_HFLIP; } else + { gSprites[summaryBarSpriteId].x += 96; + } for (i = 0; i < PARTY_SIZE; i++) { - ballIconSpritesIds[i] = CreateSpriteAtEnd(&sPartySummaryBallSpriteTemplates[isOpponent], x, y - 4, 9); + ballIconSpritesIds[i] = CreateSpriteAtEnd(&sPartySummaryBallSpriteTemplates[isOpponent], bar_X, bar_Y - 4, 9); if (!isBattleStart) gSprites[ballIconSpritesIds[i]].callback = SpriteCB_PartySummaryBall_OnSwitchout; @@ -1274,81 +1269,132 @@ u8 CreatePartyStatusSummarySprites(u8 battlerId, struct HpAndStatus *partyInfo, gSprites[ballIconSpritesIds[i]].sIsOpponent = isOpponent; } - if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) + if (IsOnPlayerSide(battler)) { - for (i = 0; i < PARTY_SIZE; i++) + if (gBattleTypeFlags & BATTLE_TYPE_MULTI) { - if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + for (i = 0; i < PARTY_SIZE; i++) { if (partyInfo[i].hp == HP_EMPTY_SLOT) { + // empty slot or an egg gSprites[ballIconSpritesIds[i]].oam.tileNum += 1; gSprites[ballIconSpritesIds[i]].sIsEmptyBall = TRUE; } else if (partyInfo[i].hp == 0) - gSprites[ballIconSpritesIds[i]].oam.tileNum += 3; - else if (partyInfo[i].status != STATUS1_NONE) - gSprites[ballIconSpritesIds[i]].oam.tileNum += 2; - } - else - { - if (i >= nValidMons) { - gSprites[ballIconSpritesIds[i]].oam.tileNum += 1; - gSprites[ballIconSpritesIds[i]].sIsEmptyBall = TRUE; - } - else if (partyInfo[i].hp == 0) + // fainted mon gSprites[ballIconSpritesIds[i]].oam.tileNum += 3; + } else if (partyInfo[i].status != STATUS1_NONE) + { + // mon with major status gSprites[ballIconSpritesIds[i]].oam.tileNum += 2; + } + } + } + else + { + for (i = 0, var = PARTY_SIZE - 1, j = 0; j < PARTY_SIZE; j++) + { + if (partyInfo[j].hp == HP_EMPTY_SLOT) + { + // empty slot or an egg + gSprites[ballIconSpritesIds[var]].oam.tileNum += 1; + gSprites[ballIconSpritesIds[var]].sIsEmptyBall = TRUE; + var--; + continue; + } + else if (partyInfo[j].hp == 0) + { + // fainted mon + gSprites[ballIconSpritesIds[i]].oam.tileNum += 3; + } + else if (gBattleTypeFlags & BATTLE_TYPE_ARENA && gBattleStruct->arenaLostPlayerMons & (1u << j)) + { + // fainted arena mon + gSprites[ballIconSpritesIds[i]].oam.tileNum += 3; + } + else if (partyInfo[j].status != STATUS1_NONE) + { + // mon with primary status + gSprites[ballIconSpritesIds[i]].oam.tileNum += 2; + } + i++; } } } else { - for (i = 0; i < PARTY_SIZE; i++) + if (gBattleTypeFlags & (BATTLE_TYPE_MULTI | BATTLE_TYPE_TWO_OPPONENTS)) { - if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + for (var = PARTY_SIZE - 1, i = 0; i < PARTY_SIZE; i++) { if (partyInfo[i].hp == HP_EMPTY_SLOT) { - gSprites[ballIconSpritesIds[5 - i]].oam.tileNum += 1; - gSprites[ballIconSpritesIds[5 - i]].sIsEmptyBall = TRUE; + // empty slot or an egg + gSprites[ballIconSpritesIds[var]].oam.tileNum += 1; + gSprites[ballIconSpritesIds[var]].sIsEmptyBall = TRUE; } else if (partyInfo[i].hp == 0) - gSprites[ballIconSpritesIds[5 - i]].oam.tileNum += 3; + { + // fainted mon + gSprites[ballIconSpritesIds[var]].oam.tileNum += 3; + } else if (partyInfo[i].status != STATUS1_NONE) - gSprites[ballIconSpritesIds[5 - i]].oam.tileNum += 2; + { + // mon with primary status + gSprites[ballIconSpritesIds[var]].oam.tileNum += 2; + } + var--; } - else + } + else + { + for (var = 0, i = 0, j = 0; j < PARTY_SIZE; j++) { - ballIconSpritesIds[5 - i] += 0; - if (i >= nValidMons) + if (partyInfo[j].hp == HP_EMPTY_SLOT) { - gSprites[ballIconSpritesIds[5 - i]].oam.tileNum += 1; - gSprites[ballIconSpritesIds[5 - i]].sIsEmptyBall = TRUE; + // empty slot or an egg + gSprites[ballIconSpritesIds[i]].oam.tileNum += 1; + gSprites[ballIconSpritesIds[i]].sIsEmptyBall = TRUE; + i++; + continue; } - else if (partyInfo[i].hp == 0) - gSprites[ballIconSpritesIds[5 - i]].oam.tileNum += 3; - else if (partyInfo[i].status != STATUS1_NONE) + else if (partyInfo[j].hp == 0) { - do - { - gSprites[ballIconSpritesIds[5 - i]].oam.tileNum += 2; - } while (0); + // fainted mon + gSprites[ballIconSpritesIds[PARTY_SIZE - 1 - var]].oam.tileNum += 3; } + else if (gBattleTypeFlags & BATTLE_TYPE_ARENA && gBattleStruct->arenaLostOpponentMons & (1u << j)) + { + // fainted arena mon + gSprites[ballIconSpritesIds[PARTY_SIZE - 1 - var]].oam.tileNum += 3; + } + else if (partyInfo[j].status != STATUS1_NONE) + { + // mon with primary status + gSprites[ballIconSpritesIds[PARTY_SIZE - 1 - var]].oam.tileNum += 2; + } + var++; } } } taskId = CreateTask(TaskDummy, 5); - gTasks[taskId].tBattler = battlerId; + gTasks[taskId].tBattler = battler; gTasks[taskId].tSummaryBarSpriteId = summaryBarSpriteId; for (i = 0; i < PARTY_SIZE; i++) gTasks[taskId].tBallIconSpriteId(i) = ballIconSpritesIds[i]; gTasks[taskId].tIsBattleStart = isBattleStart; + + if (isBattleStart) + { + gBattleSpritesDataPtr->animationData->field_9_x1C++; + } + PlaySE12WithPanning(SE_BALL_TRAY_ENTER, 0); return taskId; } diff --git a/src/battle_intro.c b/src/battle_intro.c index 226c4e46f..dff6b5a53 100644 --- a/src/battle_intro.c +++ b/src/battle_intro.c @@ -12,6 +12,7 @@ static void BattleIntroSlide1(u8 taskId); static void BattleIntroSlide2(u8 taskId); static void BattleIntroSlide3(u8 taskId); static void BattleIntroSlideLink(u8 taskId); +static void BattleIntroSlidePartner(u8); static const u8 sBattleAnimBgCnts[] = {REG_OFFSET_BG0CNT, REG_OFFSET_BG1CNT, REG_OFFSET_BG2CNT, REG_OFFSET_BG3CNT}; @@ -97,10 +98,17 @@ void HandleIntroSlide(u8 environment) { u8 taskId; - if (gBattleTypeFlags & BATTLE_TYPE_LINK) + if ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) && gPartnerTrainerId < TRAINER_PARTNER(PARTNER_NONE)) + { + taskId = CreateTask(BattleIntroSlidePartner, 0); + } else if (gBattleTypeFlags & BATTLE_TYPE_LINK) { taskId = CreateTask(BattleIntroSlideLink, 0); } + else if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER) + { + taskId = CreateTask(BattleIntroSlide3, 0); + } else if ((gBattleTypeFlags & BATTLE_TYPE_LEGENDARY) && GetMonData(&gEnemyParty[0], MON_DATA_SPECIES, NULL) == SPECIES_KYOGRE) { environment = BATTLE_ENVIRONMENT_UNDERWATER; @@ -524,6 +532,76 @@ static void BattleIntroSlideLink(u8 taskId) } } +static void BattleIntroSlidePartner(u8 taskId) +{ + switch (gTasks[taskId].tState) + { + case 0: + gTasks[taskId].data[2] = 1; + gTasks[taskId].tState++; + break; + case 1: + if (--gTasks[taskId].data[2] == 0) + { + gTasks[taskId].tState++; + SetGpuReg(REG_OFFSET_BG1CNT, BGCNT_PRIORITY(2) | BGCNT_CHARBASE(2) | BGCNT_16COLOR | BGCNT_SCREENBASE(28) | BGCNT_TXT512x256); + SetGpuReg(REG_OFFSET_BG2CNT, BGCNT_PRIORITY(2) | BGCNT_CHARBASE(2) | BGCNT_16COLOR | BGCNT_SCREENBASE(30) | BGCNT_TXT512x256); + SetGpuReg(REG_OFFSET_DISPCNT, GetGpuReg(REG_OFFSET_DISPCNT) | DISPCNT_OBJ_1D_MAP | DISPCNT_OBJ_ON | DISPCNT_WIN0_ON | DISPCNT_WIN1_ON | DISPCNT_OBJWIN_ON); + SetGpuReg(REG_OFFSET_WININ, WININ_WIN1_BG1 | WININ_WIN1_BG2 | WININ_WIN1_BG3 | WININ_WIN1_OBJ | WININ_WIN1_CLR); + SetGpuReg(REG_OFFSET_WINOUT, WINOUT_WIN01_BG_ALL | WINOUT_WIN01_OBJ | WINOUT_WIN01_CLR | WINOUT_WINOBJ_BG_ALL | WINOUT_WINOBJ_OBJ | WINOUT_WINOBJ_CLR); + gBattle_BG0_Y = -48; + gBattle_BG1_X = DISPLAY_WIDTH; + gBattle_BG2_X = -DISPLAY_WIDTH; + } + break; + case 2: + gBattle_WIN0V += 0x100; + if ((gBattle_WIN0V & 0xFF00) != 0x100) + gBattle_WIN0V--; + + if ((gBattle_WIN0V & 0xFF00) == 0x2000) + { + gTasks[taskId].tState++; + gTasks[taskId].data[2] = DISPLAY_WIDTH; + gIntroSlideFlags &= ~1; + } + break; + case 3: + if ((gBattle_WIN0V & 0xFF00) != 0x4C00) + gBattle_WIN0V += 0x3FC; + + if (gTasks[taskId].data[2]) + gTasks[taskId].data[2] -= 2; + + gBattle_BG1_X = gTasks[taskId].data[2]; + gBattle_BG2_X = -gTasks[taskId].data[2]; + if (gTasks[taskId].data[2] == 0) + gTasks[taskId].tState++; + break; + case 4: + gBattle_BG0_Y += 2; + gBattle_BG2_Y += 2; + if ((gBattle_WIN0V & 0xFF00) != 0x5000) + gBattle_WIN0V += 0xFF; + + if (!gBattle_BG0_Y) + { + CpuFill32(0, (void *)BG_SCREEN_ADDR(28), BG_SCREEN_SIZE * 4); + SetGpuReg(REG_OFFSET_DISPCNT, GetGpuReg(REG_OFFSET_DISPCNT) & ~DISPCNT_WIN1_ON); + SetBgAttribute(1, BG_ATTR_CHARBASEINDEX, 0); + SetBgAttribute(2, BG_ATTR_CHARBASEINDEX, 0); + SetGpuReg(REG_OFFSET_BG1CNT, BGCNT_PRIORITY(0) | BGCNT_CHARBASE(0) | BGCNT_16COLOR | BGCNT_SCREENBASE(28) | BGCNT_TXT256x512); + SetGpuReg(REG_OFFSET_BG2CNT, BGCNT_PRIORITY(0) | BGCNT_CHARBASE(0) | BGCNT_16COLOR | BGCNT_SCREENBASE(30) | BGCNT_TXT512x256); + gScanlineEffect.state = 3; + gTasks[taskId].tState++; + } + break; + case 5: + BattleIntroSlideEnd(taskId); + break; + } +} + void DrawBattlerOnBg(int bgId, u8 x, u8 y, u8 battlerPosition, u8 paletteId, u8 *tiles, u16 *tilemap, u16 tilesOffset) { int i, j; diff --git a/src/battle_main.c b/src/battle_main.c index d3ec67d1b..08c6b4927 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3,16 +3,17 @@ #include "battle_anim.h" #include "battle_ai_main.h" #include "battle_ai_util.h" -// #include "battle_arena.h" +#include "battle_arena.h" #include "battle_controllers.h" #include "battle_end_turn.h" #include "battle_hold_effects.h" #include "battle_interface.h" #include "battle_main.h" #include "battle_message.h" -// #include "battle_pyramid.h" +#include "battle_pyramid.h" #include "battle_scripts.h" #include "battle_setup.h" +#include "battle_tower.h" #include "battle_z_move.h" #include "battle_gimmick.h" #include "berry.h" @@ -24,7 +25,7 @@ #include "dma3.h" #include "event_data.h" #include "evolution_scene.h" -// #include "frontier_util.h" +#include "frontier_util.h" #include "field_weather.h" #include "follower_npc.h" #include "graphics.h" @@ -115,7 +116,7 @@ static void TryDoEventsBeforeFirstTurn(void); static void HandleTurnActionSelectionState(void); static void RunTurnActionsFunctions(void); static void SetActionsAndBattlersTurnOrder(void); -static void UpdateBattlerPartyOrdersOnSwitch(u32 battler); +static void UpdateBattlerPartyOrdersOnSwitch(enum BattlerId battler); static bool8 AllAtActionConfirmed(void); static void TryChangeTurnOrder(void); static void TryChangingTurnOrderEffects(struct BattleCalcValues *calcValues, u32 *quickClawRandom, u32 *quickDrawRandom); @@ -160,8 +161,8 @@ EWRAM_DATA u8 gBattlersCount = 0; EWRAM_DATA u16 gBattlerPartyIndexes[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u8 gBattlerPositions[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u8 gActionsByTurnOrder[MAX_BATTLERS_COUNT] = {0}; -EWRAM_DATA u8 gBattlerByTurnOrder[MAX_BATTLERS_COUNT] = {0}; -EWRAM_DATA u8 gBattlersBySpeed[MAX_BATTLERS_COUNT] = {0}; +EWRAM_DATA enum BattlerId gBattlerByTurnOrder[MAX_BATTLERS_COUNT] = {0}; +EWRAM_DATA enum BattlerId gBattlersBySpeed[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u8 gCurrentTurnActionNumber = 0; EWRAM_DATA u8 gCurrentActionFuncId = 0; EWRAM_DATA struct BattlePokemon gBattleMons[MAX_BATTLERS_COUNT] = {0}; @@ -174,17 +175,17 @@ EWRAM_DATA u16 gCalledMove = 0; EWRAM_DATA s32 gBideDmg[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u16 gLastUsedItem = 0; EWRAM_DATA enum Ability gLastUsedAbility = 0; -EWRAM_DATA u8 gBattlerAttacker = 0; -EWRAM_DATA u8 gBattlerTarget = 0; -EWRAM_DATA u8 gBattlerFainted = 0; -EWRAM_DATA u8 gEffectBattler = 0; -EWRAM_DATA u8 gPotentialItemEffectBattler = 0; +EWRAM_DATA enum BattlerId gBattlerAttacker = 0; +EWRAM_DATA enum BattlerId gBattlerTarget = 0; +EWRAM_DATA enum BattlerId gBattlerFainted = 0; +EWRAM_DATA enum BattlerId gEffectBattler = 0; +EWRAM_DATA enum BattlerId gPotentialItemEffectBattler = 0; EWRAM_DATA u8 gAbsentBattlerFlags = 0; EWRAM_DATA u8 gMultiHitCounter = 0; EWRAM_DATA const u8 *gBattlescriptCurrInstr = NULL; EWRAM_DATA u8 gChosenActionByBattler[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA const u8 *gSelectionBattleScripts[MAX_BATTLERS_COUNT] = {NULL}; -// EWRAM_DATA const u8 *gPalaceSelectionBattleScripts[MAX_BATTLERS_COUNT] = {NULL}; +EWRAM_DATA const u8 *gPalaceSelectionBattleScripts[MAX_BATTLERS_COUNT] = {NULL}; EWRAM_DATA u16 gLastPrintedMoves[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u16 gLastMoves[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u16 gLastLandedMoves[MAX_BATTLERS_COUNT] = {0}; @@ -236,7 +237,7 @@ EWRAM_DATA u16 gMoveToLearn = 0; EWRAM_DATA u32 gFieldStatuses = 0; EWRAM_DATA struct FieldTimer gFieldTimers = {0}; EWRAM_DATA u16 gBattleTurnCounter = 0; -EWRAM_DATA u8 gBattlerAbility = 0; +EWRAM_DATA enum BattlerId gBattlerAbility = 0; EWRAM_DATA struct QueuedStatBoost gQueuedStatBoosts[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA bool8 gHasFetchedBall = FALSE; EWRAM_DATA u16 gLastUsedBall = 0; @@ -468,7 +469,7 @@ void CB2_InitBattle(void) AllocateBattleResources(); AllocateBattleSpritesData(); AllocateMonSpritesGfx(); - // RecordedBattle_ClearFrontierPassFlag(); + RecordedBattle_ClearFrontierPassFlag(); #if T_SHOULD_RUN_MOVE_ANIM gLoadFail = FALSE; @@ -571,29 +572,12 @@ static void CB2_InitBattleInternal(void) gBattle_BG3_Y = 0; if (!DEBUG_OVERWORLD_MENU || (DEBUG_OVERWORLD_MENU && !gIsDebugBattle)) - { gBattleEnvironment = BattleSetup_GetEnvironmentId(); - } if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) gBattleEnvironment = BATTLE_ENVIRONMENT_BUILDING; if (TestRunner_Battle_GetForcedEnvironment()) gBattleEnvironment = TestRunner_Battle_GetForcedEnvironment() - 1; - if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && !(gBattleTypeFlags & (BATTLE_TYPE_FRONTIER - | BATTLE_TYPE_EREADER_TRAINER - | BATTLE_TYPE_TRAINER_HILL - | BATTLE_TYPE_RECORDED))) - { - switch (GetTrainerBattleType(TRAINER_BATTLE_PARAM.opponentA)) - { - case TRAINER_BATTLE_TYPE_SINGLES: - break; - case TRAINER_BATTLE_TYPE_DOUBLES: - gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; - break; - } - } - InitBattleBgsVideo(); LoadBattleTextboxAndBackground(); ResetSpriteData(); @@ -627,22 +611,21 @@ static void CB2_InitBattleInternal(void) } gMain.inBattle = TRUE; - // gSaveBlock2Ptr->frontier.disableRecordBattle = FALSE; + gSaveBlock2Ptr->frontier.disableRecordBattle = FALSE; for (i = 0; i < PARTY_SIZE; i++) { AdjustFriendship(&gPlayerParty[i], FRIENDSHIP_EVENT_LEAGUE_BATTLE); // Apply party-wide start-of-battle form changes for both sides. - TryFormChange(i, B_SIDE_PLAYER, FORM_CHANGE_BEGIN_BATTLE); - TryFormChange(i, B_SIDE_OPPONENT, FORM_CHANGE_BEGIN_BATTLE); + TryFormChange(&gPlayerParty[i], FORM_CHANGE_BEGIN_BATTLE); + TryFormChange(&gEnemyParty[i], FORM_CHANGE_BEGIN_BATTLE); } - if (TESTING) - { - gPlayerPartyCount = CalculatePartyCount(gPlayerParty); - gEnemyPartyCount = CalculatePartyCount(gEnemyParty); - } + #if TESTING + gPlayerPartyCount = CalculatePartyCount(gPlayerParty); + gEnemyPartyCount = CalculatePartyCount(gEnemyParty); + #endif gBattleCommunication[MULTIUSE_STATE] = 0; } @@ -685,7 +668,7 @@ static void BufferPartyVsScreenHealth_AtStart(void) BUFFER_PARTY_VS_SCREEN_STATUS(gPlayerParty, flags, i); gBattleStruct->multiBuffer.linkBattlerHeader.vsScreenHealthFlagsLo = flags; *(&gBattleStruct->multiBuffer.linkBattlerHeader.vsScreenHealthFlagsHi) = flags >> 8; - // gBattleStruct->multiBuffer.linkBattlerHeader.vsScreenHealthFlagsHi |= FlagGet(FLAG_SYS_FRONTIER_PASS) << 7; + gBattleStruct->multiBuffer.linkBattlerHeader.vsScreenHealthFlagsHi |= FlagGet(FLAG_SYS_FRONTIER_PASS) << 7; } static void SetPlayerBerryDataInBattleStruct(void) @@ -781,7 +764,7 @@ static void SetAllPlayersBerryData(void) { s32 numPlayers; struct BattleEnigmaBerry *src; - u32 battler; + enum BattlerId battler; if (gBattleTypeFlags & BATTLE_TYPE_MULTI) { @@ -968,8 +951,8 @@ static void CB2_HandleStartBattle(void) gTasks[taskId].data[5] = 0; gTasks[taskId].data[3] = gBattleStruct->multiBuffer.linkBattlerHeader.vsScreenHealthFlagsLo | (gBattleStruct->multiBuffer.linkBattlerHeader.vsScreenHealthFlagsHi << 8); gTasks[taskId].data[4] = gBlockRecvBuffer[enemyMultiplayerId][1]; - // RecordedBattle_SetFrontierPassFlagFromHword(gBlockRecvBuffer[playerMultiplayerId][1]); - // RecordedBattle_SetFrontierPassFlagFromHword(gBlockRecvBuffer[enemyMultiplayerId][1]); + RecordedBattle_SetFrontierPassFlagFromHword(gBlockRecvBuffer[playerMultiplayerId][1]); + RecordedBattle_SetFrontierPassFlagFromHword(gBlockRecvBuffer[enemyMultiplayerId][1]); gBattleCommunication[MULTIUSE_STATE]++; } break; @@ -1038,43 +1021,44 @@ static void CB2_HandleStartBattle(void) gBattleCommunication[MULTIUSE_STATE]++; gBattleCommunication[SPRITES_INIT_STATE1] = 0; gBattleCommunication[SPRITES_INIT_STATE2] = 0; - // if (gBattleTypeFlags & BATTLE_TYPE_LINK) - // { - // // Check if both players are using Emerald - // // to determine if the recorded battle rng - // // seed needs to be sent - // s32 i; - // for (i = 0; i < 2 && (gLinkPlayers[i].version & 0xFF) == VERSION_EMERALD; i++); + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + { + // Check if both players are using Emerald + // to determine if the recorded battle rng + // seed needs to be sent + // s32 i; + // for (i = 0; i < 2 && (gLinkPlayers[i].version & 0xFF) == VERSION_EMERALD; i++); - // if (i == 2) - // gBattleCommunication[MULTIUSE_STATE] = 16; - // else - // gBattleCommunication[MULTIUSE_STATE] = 18; - // } - // else - // { - // gBattleCommunication[MULTIUSE_STATE] = 18; - // } + // if (i == 2) + // gBattleCommunication[MULTIUSE_STATE] = 16; + // else + // gBattleCommunication[MULTIUSE_STATE] = 18; + gBattleCommunication[MULTIUSE_STATE] = 16; + } + else + { + gBattleCommunication[MULTIUSE_STATE] = 18; + } break; case 16: // Both players are using Emerald, send rng seed for recorded battle - // if (IsLinkTaskFinished()) - // { - // SendBlock(BitmaskAllOtherLinkPlayers(), &gRecordedBattleRngSeed, sizeof(gRecordedBattleRngSeed)); - // gBattleCommunication[MULTIUSE_STATE]++; - // } - // break; - // case 17: - // // Receive rng seed for recorded battle (only read it if partner is the link master) - // if ((GetBlockReceivedStatus() & 3) == 3) - // { - // ResetBlockReceivedFlags(); - // if (!(gBattleTypeFlags & BATTLE_TYPE_IS_MASTER)) - // memcpy(&gRecordedBattleRngSeed, gBlockRecvBuffer[enemyMultiplayerId], sizeof(gRecordedBattleRngSeed)); - // gBattleCommunication[MULTIUSE_STATE]++; - // } - // break; - // case 18: + if (IsLinkTaskFinished()) + { + SendBlock(BitmaskAllOtherLinkPlayers(), &gRecordedBattleRngSeed, sizeof(gRecordedBattleRngSeed)); + gBattleCommunication[MULTIUSE_STATE]++; + } + break; + case 17: + // Receive rng seed for recorded battle (only read it if partner is the link master) + if ((GetBlockReceivedStatus() & 3) == 3) + { + ResetBlockReceivedFlags(); + if (!(gBattleTypeFlags & BATTLE_TYPE_IS_MASTER)) + memcpy(&gRecordedBattleRngSeed, gBlockRecvBuffer[enemyMultiplayerId], sizeof(gRecordedBattleRngSeed)); + gBattleCommunication[MULTIUSE_STATE]++; + } + break; + case 18: // Finish, start battle if (BattleInitAllSprites(&gBattleCommunication[SPRITES_INIT_STATE1], &gBattleCommunication[SPRITES_INIT_STATE2])) { @@ -1129,45 +1113,45 @@ static void CB2_HandleStartMultiPartnerBattle(void) LoadWirelessStatusIndicatorSpriteGfx(); // fall through case 1: - // if (gBattleTypeFlags & BATTLE_TYPE_LINK) - // { - // if (gReceivedRemoteLinkPlayers) - // { - // u8 language; + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + { + if (gReceivedRemoteLinkPlayers) + { + u8 language; - // gLinkPlayers[0].id = 0; - // gLinkPlayers[1].id = 2; - // gLinkPlayers[2].id = 1; - // gLinkPlayers[3].id = 3; - // GetFrontierTrainerName(gLinkPlayers[2].name, TRAINER_BATTLE_PARAM.opponentA); - // GetFrontierTrainerName(gLinkPlayers[3].name, TRAINER_BATTLE_PARAM.opponentB); - // GetBattleTowerTrainerLanguage(&language, TRAINER_BATTLE_PARAM.opponentA); - // gLinkPlayers[2].language = language; - // GetBattleTowerTrainerLanguage(&language, TRAINER_BATTLE_PARAM.opponentB); - // gLinkPlayers[3].language = language; + gLinkPlayers[0].id = 0; + gLinkPlayers[1].id = 2; + gLinkPlayers[2].id = 1; + gLinkPlayers[3].id = 3; + GetFrontierTrainerName(gLinkPlayers[2].name, TRAINER_BATTLE_PARAM.opponentA); + GetFrontierTrainerName(gLinkPlayers[3].name, TRAINER_BATTLE_PARAM.opponentB); + GetBattleTowerTrainerLanguage(&language, TRAINER_BATTLE_PARAM.opponentA); + gLinkPlayers[2].language = language; + GetBattleTowerTrainerLanguage(&language, TRAINER_BATTLE_PARAM.opponentB); + gLinkPlayers[3].language = language; - // if (IsLinkTaskFinished()) - // { - // // 0x300 - // *(&gBattleStruct->multiBuffer.linkBattlerHeader.versionSignatureLo) = 0; - // *(&gBattleStruct->multiBuffer.linkBattlerHeader.versionSignatureHi) = 3; - // BufferPartyVsScreenHealth_AtStart(); - // SetPlayerBerryDataInBattleStruct(); - // SendBlock(BitmaskAllOtherLinkPlayers(), &gBattleStruct->multiBuffer.linkBattlerHeader, sizeof(gBattleStruct->multiBuffer.linkBattlerHeader)); - // gBattleCommunication[MULTIUSE_STATE] = 2; - // } + if (IsLinkTaskFinished()) + { + // 0x201 + *(&gBattleStruct->multiBuffer.linkBattlerHeader.versionSignatureLo) = 1; + *(&gBattleStruct->multiBuffer.linkBattlerHeader.versionSignatureHi) = 2; + BufferPartyVsScreenHealth_AtStart(); + SetPlayerBerryDataInBattleStruct(); + SendBlock(BitmaskAllOtherLinkPlayers(), &gBattleStruct->multiBuffer.linkBattlerHeader, sizeof(gBattleStruct->multiBuffer.linkBattlerHeader)); + gBattleCommunication[MULTIUSE_STATE] = 2; + } - // if (gWirelessCommType) - // CreateWirelessStatusIndicatorSprite(0, 0); - // } - // } - // else - // { + if (gWirelessCommType) + CreateWirelessStatusIndicatorSprite(0, 0); + } + } + else + { if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED)) gBattleTypeFlags |= BATTLE_TYPE_IS_MASTER; gBattleCommunication[MULTIUSE_STATE] = 13; SetAllPlayersBerryData(); - // } + } break; case 2: if ((GetBlockReceivedStatus() & 3) == 3) @@ -1338,7 +1322,7 @@ static void CB2_HandleStartMultiPartnerBattle(void) // Finish, start battle if (BattleInitAllSprites(&gBattleCommunication[SPRITES_INIT_STATE1], &gBattleCommunication[SPRITES_INIT_STATE2])) { - // TrySetLinkBattleTowerEnemyPartyLevel(); + TrySetLinkBattleTowerEnemyPartyLevel(); gPreBattleCallback1 = gMain.callback1; gMain.callback1 = BattleMainCB1; SetMainCallback2(BattleMainCB2); @@ -1415,8 +1399,15 @@ static void CB2_PreInitMultiBattle(void) if (i == playerMultiplierId) continue; - if ((!(gLinkPlayers[i].id & 1) && !(gLinkPlayers[playerMultiplierId].id & 1)) - || (gLinkPlayers[i].id & 1 && gLinkPlayers[playerMultiplierId].id & 1)) + if (numPlayers == MAX_LINK_PLAYERS) + { + if ((!(gLinkPlayers[i].id & 1) && !(gLinkPlayers[playerMultiplierId].id & 1)) + || (gLinkPlayers[i].id & 1 && gLinkPlayers[playerMultiplierId].id & 1)) + { + memcpy(gMultiPartnerParty, gBlockRecvBuffer[i], sizeof(gMultiPartnerParty)); + } + } + else { memcpy(gMultiPartnerParty, gBlockRecvBuffer[i], sizeof(gMultiPartnerParty)); } @@ -1429,7 +1420,7 @@ static void CB2_PreInitMultiBattle(void) } break; case 2: - if (!gPaletteFade.active) + if (IsLinkTaskFinished() && !gPaletteFade.active) { gBattleCommunication[MULTIUSE_STATE]++; if (gWirelessCommType) @@ -1569,7 +1560,7 @@ static void CB2_HandleStartMultiBattle(void) for (id = 0; id < MAX_LINK_PLAYERS; id++) { - // RecordedBattle_SetFrontierPassFlagFromHword(gBlockRecvBuffer[id][1]); + RecordedBattle_SetFrontierPassFlagFromHword(gBlockRecvBuffer[id][1]); switch (gLinkPlayers[id].id) { case 0: @@ -1657,14 +1648,14 @@ static void CB2_HandleStartMultiBattle(void) gBattleCommunication[MULTIUSE_STATE]++; } break; - case 7: + case 5: if (IsLinkTaskFinished()) { SendBlock(BitmaskAllOtherLinkPlayers(), gPlayerParty + 2, sizeof(struct Pokemon)); gBattleCommunication[MULTIUSE_STATE]++; } break; - case 8: + case 6: if ((GetBlockReceivedStatus() & 0xF) == 0xF) { ResetBlockReceivedFlags(); @@ -1734,55 +1725,55 @@ static void CB2_HandleStartMultiBattle(void) gBattleCommunication[MULTIUSE_STATE]++; } break; - case 11: + case 7: InitBattleControllers(); RecordedBattle_SetTrainerInfo(); - gBattleCommunication[MULTIUSE_STATE]++; gBattleCommunication[SPRITES_INIT_STATE1] = 0; gBattleCommunication[SPRITES_INIT_STATE2] = 0; - // if (gBattleTypeFlags & BATTLE_TYPE_LINK) - // { - // for (id = 0; id < MAX_LINK_PLAYERS && (gLinkPlayers[id].version & 0xFF) == VERSION_EMERALD; id++); + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + { + // for (id = 0; id < MAX_LINK_PLAYERS && (gLinkPlayers[id].version & 0xFF) == VERSION_EMERALD; id++); - // if (id == MAX_LINK_PLAYERS) - // gBattleCommunication[MULTIUSE_STATE] = 8; - // else - // gBattleCommunication[MULTIUSE_STATE] = 10; - // } - // else - // { - // gBattleCommunication[MULTIUSE_STATE] = 10; - // } + // if (id == MAX_LINK_PLAYERS) + // gBattleCommunication[MULTIUSE_STATE] = 8; + // else + // gBattleCommunication[MULTIUSE_STATE] = 10; + gBattleCommunication[MULTIUSE_STATE] = 8; + } + else + { + gBattleCommunication[MULTIUSE_STATE] = 10; + } break; - case 12: - // if (IsLinkTaskFinished()) - // { - // struct BattleVideo *ptr = &gBattleStruct->multiBuffer.battleVideo; - // ptr->battleTypeFlags = gBattleTypeFlags; - // ptr->rngSeed = gRecordedBattleRngSeed; + case 8: + if (IsLinkTaskFinished()) + { + struct BattleVideo *ptr = &gBattleStruct->multiBuffer.battleVideo; + ptr->battleTypeFlags = gBattleTypeFlags; + ptr->rngSeed = gRecordedBattleRngSeed; - // SendBlock(BitmaskAllOtherLinkPlayers(), ptr, sizeof(gBattleStruct->multiBuffer.battleVideo)); - // gBattleCommunication[MULTIUSE_STATE]++; - // } - // break; - // case 9: - // if ((GetBlockReceivedStatus() & 0xF) == 0xF) - // { - // ResetBlockReceivedFlags(); - // for (var = 0; var < 4; var++) - // { - // u32 blockValue = gBlockRecvBuffer[var][0]; - // if (blockValue & 4) - // { - // memcpy(&gRecordedBattleRngSeed, &gBlockRecvBuffer[var][2], sizeof(gRecordedBattleRngSeed)); - // break; - // } - // } + SendBlock(BitmaskAllOtherLinkPlayers(), ptr, sizeof(gBattleStruct->multiBuffer.battleVideo)); + gBattleCommunication[MULTIUSE_STATE]++; + } + break; + case 9: + if ((GetBlockReceivedStatus() & 0xF) == 0xF) + { + ResetBlockReceivedFlags(); + for (var = 0; var < 4; var++) + { + u32 blockValue = gBlockRecvBuffer[var][0]; + if (blockValue & 4) + { + memcpy(&gRecordedBattleRngSeed, &gBlockRecvBuffer[var][2], sizeof(gRecordedBattleRngSeed)); + break; + } + } - // gBattleCommunication[MULTIUSE_STATE]++; - // } - // break; - // case 10: + gBattleCommunication[MULTIUSE_STATE]++; + } + break; + case 10: if (BattleInitAllSprites(&gBattleCommunication[SPRITES_INIT_STATE1], &gBattleCommunication[SPRITES_INIT_STATE2])) { gPreBattleCallback1 = gMain.callback1; @@ -1795,18 +1786,6 @@ static void CB2_HandleStartMultiBattle(void) } } break; - case 5: - case 9: - case 13: - ++gBattleCommunication[0]; - gBattleCommunication[1] = 1; - // fall through - case 6: - case 10: - case 14: - if (--gBattleCommunication[1] == 0) - ++gBattleCommunication[0]; - break; } } @@ -2312,13 +2291,15 @@ static void CB2_EndLinkBattle(void) EndLinkBattleInSteps(); AnimateSprites(); BuildOamBuffer(); - // RunTextPrinters(); + RunTextPrinters(); UpdatePaletteFade(); RunTasks(); } static void EndLinkBattleInSteps(void) { + s32 i; + switch (gBattleCommunication[MULTIUSE_STATE]) { case 0: @@ -2338,11 +2319,100 @@ static void EndLinkBattleInSteps(void) case 2: if (!gPaletteFade.active) { + u32 battlerCount; + + gMain.anyLinkBattlerHasFrontierPass = RecordedBattle_GetFrontierPassFlag(); + + if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + battlerCount = 4; + else + battlerCount = 2; + + for (i = 0; i < battlerCount && (gLinkPlayers[i].version & 0xFF) == VERSION_EMERALD; i++); + + if (!gSaveBlock2Ptr->frontier.disableRecordBattle && i == battlerCount) + { + /* if (FlagGet(FLAG_SYS_FRONTIER_PASS)) + { + // Ask player if they want to record the battle + FreeAllWindowBuffers(); + SetMainCallback2(CB2_InitAskRecordBattle); + } + else */if (!gMain.anyLinkBattlerHasFrontierPass) + { + // No players can record this battle, end + SetMainCallback2(gMain.savedCallback); + FreeBattleResources(); + FreeBattleSpritesData(); + FreeMonSpritesGfx(); + } + else if (gReceivedRemoteLinkPlayers == 0) + { + // Player can't record battle but + // another player can, reconnect with them + CreateTask(Task_ReconnectWithLinkPlayers, 5); + gBattleCommunication[MULTIUSE_STATE]++; + } + else + { + gBattleCommunication[MULTIUSE_STATE]++; + } + } + else + { + SetMainCallback2(gMain.savedCallback); + FreeBattleResources(); + FreeBattleSpritesData(); + FreeMonSpritesGfx(); + } + } + break; + case 3: + CpuFill32(0, (void *)VRAM, VRAM_SIZE); + + for (i = 0; i < 2; i++) + LoadChosenBattleElement(i); + + BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0, RGB_BLACK); + gBattleCommunication[MULTIUSE_STATE]++; + break; + case 4: + if (!gPaletteFade.active) + gBattleCommunication[MULTIUSE_STATE]++; + break; + case 5: + if (!FuncIsActiveTask(Task_ReconnectWithLinkPlayers)) + gBattleCommunication[MULTIUSE_STATE]++; + break; + case 6: + if (IsLinkTaskFinished() == TRUE) + { + SetLinkStandbyCallback(); + BattlePutTextOnWindow(gText_LinkStandby3, B_WIN_MSG); + gBattleCommunication[MULTIUSE_STATE]++; + } + break; + case 7: + if (!IsTextPrinterActiveOnWindow(B_WIN_MSG)) + { + if (IsLinkTaskFinished() == TRUE) + gBattleCommunication[MULTIUSE_STATE]++; + } + break; + case 8: + if (!gWirelessCommType) + SetCloseLinkCallback(); + gBattleCommunication[MULTIUSE_STATE]++; + break; + case 9: + if (!gMain.anyLinkBattlerHasFrontierPass || gWirelessCommType || gReceivedRemoteLinkPlayers != 1) + { + gMain.anyLinkBattlerHasFrontierPass = FALSE; SetMainCallback2(gMain.savedCallback); TrySetQuestLogLinkBattleEvent(); - FreeMonSpritesGfx(); - FreeBattleSpritesData(); FreeBattleResources(); + FreeBattleSpritesData(); + FreeMonSpritesGfx(); } break; } @@ -2380,10 +2450,238 @@ u32 GetBattleBgTemplateData(u8 arrayId, u8 caseId) return ret; } +// static void CB2_InitAskRecordBattle(void) +// { +// s32 i; + +// SetHBlankCallback(NULL); +// SetVBlankCallback(NULL); +// CpuFill32(0, (void *)(VRAM), VRAM_SIZE); +// ResetPaletteFade(); +// gBattle_BG0_X = 0; +// gBattle_BG0_Y = 0; +// gBattle_BG1_X = 0; +// gBattle_BG1_Y = 0; +// gBattle_BG2_X = 0; +// gBattle_BG2_Y = 0; +// gBattle_BG3_X = 0; +// gBattle_BG3_Y = 0; +// InitBattleBgsVideo(); +// SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP); +// LoadBattleMenuWindowGfx(); + +// for (i = 0; i < 2; i++) +// LoadChosenBattleElement(i); + +// ResetSpriteData(); +// ResetTasks(); +// FreeAllSpritePalettes(); +// gReservedSpritePaletteCount = MAX_BATTLERS_COUNT; +// SetVBlankCallback(VBlankCB_Battle); +// SetMainCallback2(CB2_AskRecordBattle); +// BeginNormalPaletteFade(PALETTES_ALL, 0, 0x10, 0, RGB_BLACK); +// gBattleCommunication[MULTIUSE_STATE] = 0; +// } + +// static void CB2_AskRecordBattle(void) +// { +// AskRecordBattle(); +// AnimateSprites(); +// BuildOamBuffer(); +// RunTextPrinters(); +// UpdatePaletteFade(); +// RunTasks(); +// } + + +// // States for AskRecordBattle +// #define STATE_INIT 0 +// #define STATE_LINK 1 +// #define STATE_WAIT_LINK 2 +// #define STATE_ASK_RECORD 3 +// #define STATE_PRINT_YES_NO 4 +// #define STATE_HANDLE_YES_NO 5 +// #define STATE_RECORD_NO 6 +// #define STATE_END_RECORD_NO 7 +// #define STATE_WAIT_END 8 +// #define STATE_END 9 +// #define STATE_RECORD_YES 10 +// #define STATE_RECORD_WAIT 11 +// #define STATE_END_RECORD_YES 12 + +// static void AskRecordBattle(void) +// { +// switch (gBattleCommunication[MULTIUSE_STATE]) +// { +// case STATE_INIT: +// ShowBg(0); +// ShowBg(1); +// ShowBg(2); +// gBattleCommunication[MULTIUSE_STATE]++; +// break; +// case STATE_LINK: +// if (gMain.anyLinkBattlerHasFrontierPass && gReceivedRemoteLinkPlayers == 0) +// CreateTask(Task_ReconnectWithLinkPlayers, 5); +// gBattleCommunication[MULTIUSE_STATE]++; +// break; +// case STATE_WAIT_LINK: +// if (!FuncIsActiveTask(Task_ReconnectWithLinkPlayers)) +// gBattleCommunication[MULTIUSE_STATE]++; +// break; +// case STATE_ASK_RECORD: +// if (!gPaletteFade.active) +// { +// // "Would you like to record your battle on your FRONTIER PASS?" +// BattlePutTextOnWindow(gText_RecordBattleToPass, B_WIN_MSG); +// gBattleCommunication[MULTIUSE_STATE]++; +// } +// break; +// case STATE_PRINT_YES_NO: +// if (!IsTextPrinterActiveOnWindow(B_WIN_MSG)) +// { +// HandleBattleWindow(YESNOBOX_X_Y, 0); +// BattlePutTextOnWindow(gText_BattleYesNoChoice, B_WIN_YESNO); +// gBattleCommunication[CURSOR_POSITION] = 1; +// BattleCreateYesNoCursorAt(1); +// gBattleCommunication[MULTIUSE_STATE]++; +// } +// break; +// case STATE_HANDLE_YES_NO: +// if (JOY_NEW(DPAD_UP)) +// { +// if (gBattleCommunication[CURSOR_POSITION] != 0) +// { +// // Moved cursor onto Yes +// PlaySE(SE_SELECT); +// BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]); +// gBattleCommunication[CURSOR_POSITION] = 0; +// BattleCreateYesNoCursorAt(0); +// } +// } +// else if (JOY_NEW(DPAD_DOWN)) +// { +// if (gBattleCommunication[CURSOR_POSITION] == 0) +// { +// // Moved cursor onto No +// PlaySE(SE_SELECT); +// BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]); +// gBattleCommunication[CURSOR_POSITION] = 1; +// BattleCreateYesNoCursorAt(1); +// } +// } +// else if (JOY_NEW(A_BUTTON)) +// { +// PlaySE(SE_SELECT); +// if (gBattleCommunication[CURSOR_POSITION] == 0) +// { +// // Selected Yes +// HandleBattleWindow(YESNOBOX_X_Y, WINDOW_CLEAR); +// gBattleCommunication[1] = MoveRecordedBattleToSaveData(); +// gBattleCommunication[MULTIUSE_STATE] = STATE_RECORD_YES; +// } +// else +// { +// // Selected No +// gBattleCommunication[MULTIUSE_STATE]++; +// } +// } +// else if (JOY_NEW(B_BUTTON)) +// { +// PlaySE(SE_SELECT); +// gBattleCommunication[MULTIUSE_STATE]++; +// } +// break; +// case STATE_RECORD_NO: +// if (IsLinkTaskFinished() == TRUE) +// { +// HandleBattleWindow(YESNOBOX_X_Y, WINDOW_CLEAR); +// if (gMain.anyLinkBattlerHasFrontierPass) +// { +// // Other battlers may be recording, wait for them +// SetLinkStandbyCallback(); +// BattlePutTextOnWindow(gText_LinkStandby3, B_WIN_MSG); +// } +// gBattleCommunication[MULTIUSE_STATE]++; // STATE_END_RECORD_NO +// } +// break; +// case STATE_WAIT_END: +// if (--gBattleCommunication[1] == 0) +// { +// if (gMain.anyLinkBattlerHasFrontierPass && !gWirelessCommType) +// SetCloseLinkCallback(); +// gBattleCommunication[MULTIUSE_STATE]++; +// } +// break; +// case STATE_END: +// if (!gMain.anyLinkBattlerHasFrontierPass || gWirelessCommType || gReceivedRemoteLinkPlayers != 1) +// { +// gMain.anyLinkBattlerHasFrontierPass = FALSE; +// if (!gPaletteFade.active) +// { +// SetMainCallback2(gMain.savedCallback); +// FreeBattleResources(); +// FreeBattleSpritesData(); +// FreeMonSpritesGfx(); +// } +// } +// break; +// case STATE_RECORD_YES: +// if (gBattleCommunication[1] == 1) +// { +// PlaySE(SE_SAVE); +// BattleStringExpandPlaceholdersToDisplayedString(gText_BattleRecordedOnPass); +// BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG); +// gBattleCommunication[1] = 128; // Delay +// gBattleCommunication[MULTIUSE_STATE]++; +// } +// else +// { +// BattleStringExpandPlaceholdersToDisplayedString(BattleFrontier_BattleTowerBattleRoom_Text_RecordCouldntBeSaved); +// BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG); +// gBattleCommunication[1] = 128; // Delay +// gBattleCommunication[MULTIUSE_STATE]++; +// } +// break; +// case STATE_RECORD_WAIT: +// if (IsLinkTaskFinished() == TRUE && !IsTextPrinterActiveOnWindow(B_WIN_MSG) && --gBattleCommunication[1] == 0) +// { +// if (gMain.anyLinkBattlerHasFrontierPass) +// { +// SetLinkStandbyCallback(); +// BattlePutTextOnWindow(gText_LinkStandby3, B_WIN_MSG); +// } +// gBattleCommunication[MULTIUSE_STATE]++; +// } +// break; +// case STATE_END_RECORD_YES: +// case STATE_END_RECORD_NO: +// if (!IsTextPrinterActiveOnWindow(B_WIN_MSG)) +// { +// if (gMain.anyLinkBattlerHasFrontierPass) +// { +// if (IsLinkTaskFinished() == TRUE) +// { +// BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK); +// gBattleCommunication[1] = 32; // Delay +// gBattleCommunication[MULTIUSE_STATE] = STATE_WAIT_END; +// } + +// } +// else +// { +// BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK); +// gBattleCommunication[1] = 32; // Delay +// gBattleCommunication[MULTIUSE_STATE] = STATE_WAIT_END; +// } +// } +// break; +// } +// } + static void TryCorrectShedinjaLanguage(struct Pokemon *mon) { u8 nickname[POKEMON_NAME_LENGTH + 1]; - u8 language = LANGUAGE_JAPANESE; + enum Language language = LANGUAGE_JAPANESE; if (GetMonData(mon, MON_DATA_SPECIES) == SPECIES_SHEDINJA && GetMonData(mon, MON_DATA_LANGUAGE) != language) @@ -2459,7 +2757,7 @@ void SpriteCallbackDummy_2(struct Sprite *sprite) void SpriteCB_FaintOpponentMon(struct Sprite *sprite) { - u8 battler = sprite->sBattler; + enum BattlerId battler = sprite->sBattler; u32 personality = GetMonData(GetBattlerMon(battler), MON_DATA_PERSONALITY); u16 species; u8 yOffset; @@ -2590,7 +2888,7 @@ void SpriteCB_FaintSlideAnim(struct Sprite *sprite) #define sBouncerSpriteId data[6] #define sWhich data[7] -void DoBounceEffect(u8 battler, u8 which, s8 delta, s8 amplitude) +void DoBounceEffect(enum BattlerId battler, u8 which, s8 delta, s8 amplitude) { u8 invisibleSpriteId; u8 bouncerSpriteId; @@ -2632,7 +2930,7 @@ void DoBounceEffect(u8 battler, u8 which, s8 delta, s8 amplitude) gSprites[bouncerSpriteId].y2 = 0; } -void EndBounceEffect(u8 battler, u8 which) +void EndBounceEffect(enum BattlerId battler, u8 which) { u8 bouncerSpriteId; @@ -2748,10 +3046,8 @@ void BeginBattleIntro(void) static void BattleMainCB1(void) { - u32 battler; - gBattleMainFunc(); - for (battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) gBattlerControllerFuncs[battler](battler); } @@ -2763,10 +3059,10 @@ static void ClearSetBScriptingStruct(void) memset(&gBattleScripting, 0, sizeof(gBattleScripting)); gBattleScripting.windowsType = temp; - if (TESTING) + gBattleScripting.battleStyle = gSaveBlock2Ptr->optionsBattleStyle; + #if TESTING gBattleScripting.battleStyle = OPTIONS_BATTLE_STYLE_SET; - else - gBattleScripting.battleStyle = gSaveBlock2Ptr->optionsBattleStyle; + #endif gBattleScripting.expOnCatch = (GetConfig(CONFIG_EXP_CATCH) >= GEN_6); gBattleScripting.specialTrainerBattleType = specialBattleType; } @@ -2795,10 +3091,10 @@ static void BattleStartClearSetData(void) gLastHitBy[i] = 0xFF; gLockedMoves[i] = MOVE_NONE; gLastPrintedMoves[i] = MOVE_NONE; - // gPalaceSelectionBattleScripts[i] = 0; + gSelectionBattleScripts[i] = NULL; + gPalaceSelectionBattleScripts[i] = NULL; gBattleStruct->lastTakenMove[i] = MOVE_NONE; gBattleStruct->choicedMove[i] = MOVE_NONE; - gBattleStruct->changedItems[i] = 0; gBattleStruct->lastTakenMoveFrom[i][0] = MOVE_NONE; gBattleStruct->lastTakenMoveFrom[i][1] = MOVE_NONE; gBattleStruct->lastTakenMoveFrom[i][2] = MOVE_NONE; @@ -2892,17 +3188,16 @@ static void BattleStartClearSetData(void) #define UNPACK_VOLATILE_BATON_PASSABLES(_enum, _fieldName, _typeMaxValue, ...) __VA_OPT__(if ((FIRST(__VA_ARGS__)) & V_BATON_PASSABLE) gBattleMons[battler].volatiles._fieldName = volatilesCopy->_fieldName;) -void SwitchInClearSetData(u32 battler, struct Volatiles *volatilesCopy) +void SwitchInClearSetData(enum BattlerId battler, struct Volatiles *volatilesCopy) { - s32 i; enum BattleMoveEffects effect = GetMoveEffect(gCurrentMove); ClearIllusionMon(battler); if (effect != EFFECT_BATON_PASS) { - for (i = 0; i < NUM_BATTLE_STATS; i++) + for (enum Stat i = 0; i < NUM_BATTLE_STATS; i++) gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE; - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].volatiles.escapePrevention && gBattleMons[i].volatiles.battlerPreventingEscape == battler) gBattleMons[i].volatiles.escapePrevention = FALSE; @@ -2927,6 +3222,7 @@ void SwitchInClearSetData(u32 battler, struct Volatiles *volatilesCopy) * ...etc */ + enum BattlerId i; for (i = 0; i < gBattlersCount; i++) { if (!IsBattlerAlly(battler, i) @@ -2940,7 +3236,7 @@ void SwitchInClearSetData(u32 battler, struct Volatiles *volatilesCopy) SWAP(gBattleMons[battler].attack, gBattleMons[battler].defense, i); } - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].volatiles.infatuation == INFATUATED_WITH(battler)) gBattleMons[i].volatiles.infatuation = 0; @@ -2998,14 +3294,14 @@ void SwitchInClearSetData(u32 battler, struct Volatiles *volatilesCopy) ClearPursuitValuesIfSet(battler); - for (i = 0; i < ARRAY_COUNT(gSideTimers); i++) + for (u32 i = 0; i < ARRAY_COUNT(gSideTimers); i++) { // Switched into sticky web user slot, so reset stored battler ID if (gSideTimers[i].stickyWebBattlerId == battler) gSideTimers[i].stickyWebBattlerId = 0xFF; } - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (i != battler && !IsBattlerAlly(i, battler)) gBattleStruct->lastTakenMove[i] = MOVE_NONE; @@ -3033,26 +3329,25 @@ void SwitchInClearSetData(u32 battler, struct Volatiles *volatilesCopy) enum BattleTrainer trainer = GetBattlerTrainer(battler); u32 partyIndex = gBattlerPartyIndexes[battler]; if (TestRunner_Battle_GetForcedAbility(trainer, partyIndex)) - gBattleMons[i].ability = TestRunner_Battle_GetForcedAbility(trainer, partyIndex); + gBattleMons[battler].ability = TestRunner_Battle_GetForcedAbility(trainer, partyIndex); } #endif // TESTING Ai_UpdateSwitchInData(battler); } -const u8* FaintClearSetData(u32 battler) +const u8* FaintClearSetData(enum BattlerId battler) { - s32 i; const u8 *result = NULL; - for (i = 0; i < NUM_BATTLE_STATS; i++) + for (enum Stat i = 0; i < NUM_BATTLE_STATS; i++) gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE; bool32 keepGastroAcid = gBattleMons[battler].volatiles.gastroAcid; memset(&gBattleMons[battler].volatiles, 0, sizeof(struct Volatiles)); gBattleMons[battler].volatiles.gastroAcid = keepGastroAcid; // Edge case: Keep Gastro Acid if pokemon's ability can have effect after fainting, for example Innards Out. - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].volatiles.escapePrevention && gBattleMons[i].volatiles.battlerPreventingEscape == battler) gBattleMons[i].volatiles.escapePrevention = FALSE; @@ -3099,12 +3394,14 @@ const u8* FaintClearSetData(u32 battler) gBattleStruct->lastTakenMoveFrom[battler][2] = 0; gBattleStruct->lastTakenMoveFrom[battler][3] = 0; gBattleStruct->palaceFlags &= ~(1u << battler); + if (battler == gBattlerAttacker) + gBattleStruct->moldBreakerActive = FALSE; ClearPursuitValuesIfSet(battler); if (gBattleStruct->battlerState[battler].commanderSpecies != SPECIES_NONE) { - u32 partner = BATTLE_PARTNER(battler); + enum BattlerId partner = BATTLE_PARTNER(battler); if (IsBattlerAlive(partner)) { BtlController_EmitSpriteInvisibility(partner, B_COMM_TO_CONTROLLER, FALSE); @@ -3112,14 +3409,14 @@ const u8* FaintClearSetData(u32 battler) } } - for (i = 0; i < ARRAY_COUNT(gSideTimers); i++) + for (u32 i = 0; i < ARRAY_COUNT(gSideTimers); i++) { // User of sticky web fainted, so reset the stored battler ID if (gSideTimers[i].stickyWebBattlerId == battler) gSideTimers[i].stickyWebBattlerId = 0xFF; } - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (i != battler && !IsBattlerAlly(i, battler)) gBattleStruct->lastTakenMove[i] = MOVE_NONE; @@ -3132,7 +3429,7 @@ const u8* FaintClearSetData(u32 battler) gBattleMons[battler].types[2] = TYPE_MYSTERY; Ai_UpdateFaintData(battler); - TryBattleFormChange(battler, FORM_CHANGE_FAINT); + TryBattleFormChange(battler, FORM_CHANGE_FAINT, GetBattlerAbility(battler)); // If the fainted mon was involved in a Sky Drop if (gBattleStruct->skyDropTargets[battler] != SKY_DROP_NO_TARGET) @@ -3180,7 +3477,7 @@ const u8* FaintClearSetData(u32 battler) static void DoBattleIntro(void) { s32 i; - u32 battler; + enum BattlerId battler; switch ((enum BattleIntroStates)gBattleStruct->eventState.battleIntro) { @@ -3290,8 +3587,8 @@ static void DoBattleIntro(void) break; } - // if (gBattleTypeFlags & BATTLE_TYPE_ARENA) - // BattleArena_InitPoints(); + if (gBattleTypeFlags & BATTLE_TYPE_ARENA) + BattleArena_InitPoints(); } if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) @@ -3524,12 +3821,12 @@ static void TryDoEventsBeforeFirstTurn(void) // 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++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { - gBattleStruct->monToSwitchIntoId[i] = PARTY_SIZE; // Included here because switches can happen before during set ups (eg. eject pack) - struct Pokemon *mon = GetBattlerMon(i); - if (!IsBattlerAlive(i) || gBattleMons[i].species == SPECIES_NONE || GetMonData(mon, MON_DATA_IS_EGG)) - gAbsentBattlerFlags |= 1u << i; + gBattleStruct->monToSwitchIntoId[battler] = PARTY_SIZE; // Included here because switches can happen before during set ups (eg. eject pack) + struct Pokemon *mon = GetBattlerMon(battler); + if (!IsBattlerAlive(battler) || gBattleMons[battler].species == SPECIES_NONE || GetMonData(mon, MON_DATA_IS_EGG)) + gAbsentBattlerFlags |= 1u << battler; } } @@ -3537,12 +3834,12 @@ static void TryDoEventsBeforeFirstTurn(void) #if TESTING if (gTestRunnerEnabled) { - for (i = 0; i < gBattlersCount; ++i) + for (enum BattlerId battler = 0; battler < gBattlersCount; ++battler) { - enum BattleTrainer trainer = GetBattlerTrainer(i); - u32 partyIndex = gBattlerPartyIndexes[i]; + enum BattleTrainer trainer = GetBattlerTrainer(battler); + u32 partyIndex = gBattlerPartyIndexes[battler]; if (TestRunner_Battle_GetForcedAbility(trainer, partyIndex)) - gBattleMons[i].ability = TestRunner_Battle_GetForcedAbility(trainer, partyIndex); + gBattleMons[battler].ability = TestRunner_Battle_GetForcedAbility(trainer, partyIndex); } } #endif // TESTING @@ -3571,11 +3868,11 @@ static void TryDoEventsBeforeFirstTurn(void) gBattleStruct->eventState.beforeFirstTurn++; break; case FIRST_TURN_EVENTS_TOTEM_BOOST: - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { - if (gQueuedStatBoosts[i].stats != 0 && !gProtectStructs[i].eatMirrorHerb && gProtectStructs[i].activateOpportunist == 0) + if (gQueuedStatBoosts[battler].stats != 0 && !gProtectStructs[battler].eatMirrorHerb && gProtectStructs[battler].activateOpportunist == 0) { - gBattlerAttacker = i; + gBattlerAttacker = battler; BattleScriptExecute(BattleScript_TotemVar); return; } @@ -3585,7 +3882,7 @@ static void TryDoEventsBeforeFirstTurn(void) break; case FIRST_TURN_SWITCH_IN_EVENTS: gBattleStruct->eventState.switchIn = 0; - for (u32 battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) gBattleStruct->battlerState[battler].switchIn = TRUE; BattleScriptPushCursorAndCallback(BattleScript_FirstTurnSwitchInEvents); gBattleStruct->eventState.beforeFirstTurn++; @@ -3621,11 +3918,11 @@ static void TryDoEventsBeforeFirstTurn(void) gBattleStruct->eventState.beforeFirstTurn++; break; case FIRST_TURN_EVENTS_END: - for (i = 0; i < MAX_BATTLERS_COUNT; i++) + for (enum BattlerId battler = 0; battler < MAX_BATTLERS_COUNT; battler++) { - gBattleStruct->monToSwitchIntoId[i] = PARTY_SIZE; - gChosenActionByBattler[i] = B_ACTION_NONE; - gChosenMoveByBattler[i] = MOVE_NONE; + gBattleStruct->monToSwitchIntoId[battler] = PARTY_SIZE; + gChosenActionByBattler[battler] = B_ACTION_NONE; + gChosenMoveByBattler[battler] = MOVE_NONE; } TurnValuesCleanUp(FALSE); memset(&gSpecialStatuses, 0, sizeof(gSpecialStatuses)); @@ -3647,11 +3944,11 @@ static void TryDoEventsBeforeFirstTurn(void) SetShellSideArmCategory(); SetAiLogicDataForTurn(gAiLogicData); // get assumed abilities, hold effects, etc of all battlers - // if (gBattleTypeFlags & BATTLE_TYPE_ARENA) - // { - // StopCryAndClearCrySongs(); - // BattleScriptExecute(BattleScript_ArenaTurnBeginning); - // } + if (gBattleTypeFlags & BATTLE_TYPE_ARENA) + { + StopCryAndClearCrySongs(); + BattleScriptExecute(BattleScript_ArenaTurnBeginning); + } gBattleStruct->eventState.beforeFirstTurn = 0; break; @@ -3660,18 +3957,16 @@ static void TryDoEventsBeforeFirstTurn(void) static void HandleEndTurn_ContinueBattle(void) { - s32 i; - if (gBattleControllerExecFlags == 0) { gBattleMainFunc = BattleTurnPassed; - for (i = 0; i < BATTLE_COMMUNICATION_ENTRIES_COUNT; i++) + for (u32 i = 0; i < BATTLE_COMMUNICATION_ENTRIES_COUNT; i++) gBattleCommunication[i] = 0; - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { - gBattleMons[i].volatiles.flinched = FALSE; - if ((gBattleMons[i].status1 & STATUS1_SLEEP) && (gBattleMons[i].volatiles.multipleTurns)) - CancelMultiTurnMoves(i, SKY_DROP_IGNORE); + gBattleMons[battler].volatiles.flinched = FALSE; + if ((gBattleMons[battler].status1 & STATUS1_SLEEP) && (gBattleMons[battler].volatiles.multipleTurns)) + CancelMultiTurnMoves(battler, SKY_DROP_IGNORE); } gBattleStruct->eventState.endTurnBlock = 0; gBattleStruct->eventState.endTurnBattler = 0; @@ -3689,8 +3984,8 @@ void BattleTurnPassed(void) if (gBattleOutcome == 0 && DoEndTurnEffects()) return; - // if (BattleArenaTurnEnd()) - // return; + if (BattleArenaTurnEnd()) + return; if (HandleFaintedMonActions()) return; @@ -3718,17 +4013,17 @@ void BattleTurnPassed(void) gBattleStruct->eventState.arenaTurn++; } - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { - gChosenActionByBattler[i] = B_ACTION_NONE; - gChosenMoveByBattler[i] = MOVE_NONE; - gBattleStruct->monToSwitchIntoId[i] = PARTY_SIZE; - gBattleMons[i].volatiles.electrified = FALSE; - gBattleMons[i].volatiles.flinched = FALSE; - gBattleMons[i].volatiles.powder = FALSE; + gChosenActionByBattler[battler] = B_ACTION_NONE; + gChosenMoveByBattler[battler] = MOVE_NONE; + gBattleStruct->monToSwitchIntoId[battler] = PARTY_SIZE; + gBattleMons[battler].volatiles.electrified = FALSE; + gBattleMons[battler].volatiles.flinched = FALSE; + gBattleMons[battler].volatiles.powder = FALSE; - if (gBattleStruct->battlerState[i].stompingTantrumTimer > 0) - gBattleStruct->battlerState[i].stompingTantrumTimer--; + if (gBattleStruct->battlerState[battler].stompingTantrumTimer > 0) + gBattleStruct->battlerState[battler].stompingTantrumTimer--; } for (i = 0; i < NUM_BATTLE_SIDES; i++) @@ -3745,13 +4040,13 @@ void BattleTurnPassed(void) SetAiLogicDataForTurn(gAiLogicData); // get assumed abilities, hold effects, etc of all battlers gBattleMainFunc = HandleTurnActionSelectionState; - // if (gBattleTypeFlags & BATTLE_TYPE_PALACE) - // BattleScriptExecute(BattleScript_PalacePrintFlavorText); - // else if (gBattleTypeFlags & BATTLE_TYPE_ARENA && gBattleStruct->eventState.arenaTurn == 0) - // BattleScriptExecute(BattleScript_ArenaTurnBeginning); + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + BattleScriptExecute(BattleScript_PalacePrintFlavorText); + else if (gBattleTypeFlags & BATTLE_TYPE_ARENA && gBattleStruct->eventState.arenaTurn == 0) + BattleScriptExecute(BattleScript_ArenaTurnBeginning); } -u8 IsRunningFromBattleImpossible(u32 battler) +u8 IsRunningFromBattleImpossible(enum BattlerId battler) { enum HoldEffect holdEffect; u32 i; @@ -3806,7 +4101,7 @@ u8 IsRunningFromBattleImpossible(u32 battler) return BATTLE_RUN_SUCCESS; } -void SwitchTwoBattlersInParty(u32 battler, u32 battler2) +void SwitchTwoBattlersInParty(enum BattlerId battler, enum BattlerId battler2) { s32 i; u32 partyId1, partyId2; @@ -3825,7 +4120,7 @@ void SwitchTwoBattlersInParty(u32 battler, u32 battler2) } } -void SwitchPartyOrder(u32 battler) +void SwitchPartyOrder(enum BattlerId battler) { s32 i; u32 partyId1, partyId2; @@ -3869,10 +4164,10 @@ enum static void HandleTurnActionSelectionState(void) { - s32 i, battler; + s32 i; gBattleCommunication[ACTIONS_CONFIRMED_COUNT] = 0; - for (battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { enum BattlerPosition position = GetBattlerPosition(battler); switch (gBattleCommunication[battler]) @@ -4053,12 +4348,6 @@ static void HandleTurnActionSelectionState(void) return; } break; -#if !IS_FRLG - case B_ACTION_SAFARI_POKEBLOCK: - BtlController_EmitChooseItem(battler, B_COMM_TO_CONTROLLER, gBattleStruct->battlerPartyOrders[battler]); - MarkBattlerForControllerExec(battler); - break; -#endif case B_ACTION_CANCEL_PARTNER: gBattleCommunication[battler] = STATE_WAIT_SET_BEFORE_ACTION; gBattleCommunication[GetPartnerBattler(battler)] = STATE_BEFORE_ACTION_CHOSEN; @@ -4115,14 +4404,14 @@ static void HandleTurnActionSelectionState(void) gBattleStruct->stateIdAfterSelScript[battler] = STATE_BEFORE_ACTION_CHOSEN; return; } - // else if (CanPlayerForfeitNormalTrainerBattle() && gBattleResources->bufferB[battler][1] == B_ACTION_RUN) - // { - // gSelectionBattleScripts[battler] = BattleScript_QuestionForfeitBattle; - // gBattleCommunication[battler] = STATE_SELECTION_SCRIPT_MAY_RUN; - // gBattleStruct->battlerState[battler].selectionScriptFinished = FALSE; - // gBattleStruct->stateIdAfterSelScript[battler] = STATE_BEFORE_ACTION_CHOSEN; - // return; - // } + else if (CanPlayerForfeitNormalTrainerBattle() && gBattleResources->bufferB[battler][1] == B_ACTION_RUN) + { + gSelectionBattleScripts[battler] = BattleScript_QuestionForfeitBattle; + gBattleCommunication[battler] = STATE_SELECTION_SCRIPT_MAY_RUN; + gBattleStruct->battlerState[battler].selectionScriptFinished = FALSE; + gBattleStruct->stateIdAfterSelScript[battler] = STATE_BEFORE_ACTION_CHOSEN; + return; + } else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) && gBattleResources->bufferB[battler][1] == B_ACTION_RUN) @@ -4193,7 +4482,7 @@ static void HandleTurnActionSelectionState(void) // Get the chosen move position (and thus the chosen move) and target from the returned buffer. gBattleStruct->chosenMovePositions[battler] = gBattleResources->bufferB[battler][2] & ~RET_GIMMICK; - gChosenMoveByBattler[battler] = GetChosenMoveFromPosition(battler); + gChosenMoveByBattler[battler] = GetBattlerChosenMove(battler); gBattleStruct->moveTarget[battler] = gBattleResources->bufferB[battler][3]; if (IsBattleMoveStatus(gChosenMoveByBattler[battler]) && GetBattlerAbility(battler) == ABILITY_MYCELIUM_MIGHT) gProtectStructs[battler].myceliumMight = TRUE; @@ -4207,7 +4496,7 @@ static void HandleTurnActionSelectionState(void) // Max Move check if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX || IsGimmickSelected(battler, GIMMICK_DYNAMAX)) { - gBattleStruct->dynamax.baseMoves[battler] = GetChosenMoveFromPosition(battler); + gBattleStruct->dynamax.baseMoves[battler] = GetBattlerChosenMove(battler); } gBattleCommunication[battler]++; @@ -4309,10 +4598,12 @@ static void HandleTurnActionSelectionState(void) case STATE_SELECTION_SCRIPT: if (gBattleStruct->battlerState[battler].selectionScriptFinished) { + gSelectionBattleScripts[battler] = NULL; gBattleCommunication[battler] = gBattleStruct->stateIdAfterSelScript[battler]; } else { + assertf(gSelectionBattleScripts[battler] != NULL, "selection script set to run, but pointer is null"); gBattlerAttacker = battler; gBattlescriptCurrInstr = gSelectionBattleScripts[battler]; if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler)) @@ -4331,6 +4622,7 @@ static void HandleTurnActionSelectionState(void) case STATE_SELECTION_SCRIPT_MAY_RUN: if (gBattleStruct->battlerState[battler].selectionScriptFinished) { + gSelectionBattleScripts[battler] = NULL; if (gBattleResources->bufferB[battler][1] == B_ACTION_NOTHING_FAINTED) { gHitMarker |= HITMARKER_RUN; @@ -4345,6 +4637,7 @@ static void HandleTurnActionSelectionState(void) } else { + assertf(gSelectionBattleScripts[battler] != NULL, "selection script set to run, but pointer is null"); gBattlerAttacker = battler; gBattlescriptCurrInstr = gSelectionBattleScripts[battler]; if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler)) @@ -4379,10 +4672,10 @@ static void HandleTurnActionSelectionState(void) if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) { - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { - if (gChosenActionByBattler[i] == B_ACTION_SWITCH) - SwitchPartyOrderInGameMulti(i, gBattleStruct->monToSwitchIntoId[i]); + if (gChosenActionByBattler[battler] == B_ACTION_SWITCH) + SwitchPartyOrderInGameMulti(battler, gBattleStruct->monToSwitchIntoId[battler]); } } } @@ -4390,7 +4683,8 @@ static void HandleTurnActionSelectionState(void) static bool8 AllAtActionConfirmed(void) { - s32 i, count; + enum BattlerId i; + s32 count; for (count = 0, i = 0; i < gBattlersCount; i++) { @@ -4404,7 +4698,7 @@ static bool8 AllAtActionConfirmed(void) return FALSE; } -static void UpdateBattlerPartyOrdersOnSwitch(u32 battler) +static void UpdateBattlerPartyOrdersOnSwitch(enum BattlerId battler) { gBattleStruct->monToSwitchIntoId[battler] = gBattleResources->bufferB[battler][1]; RecordedBattle_SetBattlerAction(battler, gBattleResources->bufferB[battler][1]); @@ -4430,7 +4724,7 @@ void SwapTurnOrder(u8 id1, u8 id2) } // For AI, so it doesn't 'cheat' by knowing player's ability -u32 GetBattlerTotalSpeedStat(u32 battler, enum Ability ability, enum HoldEffect holdEffect) +u32 GetBattlerTotalSpeedStat(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect) { u32 speed = gBattleMons[battler].speed; @@ -4497,7 +4791,7 @@ u32 GetBattlerTotalSpeedStat(u32 battler, enum Ability ability, enum HoldEffect return speed; } -s32 GetChosenMovePriority(u32 battler, enum Ability ability) +s32 GetChosenMovePriority(enum BattlerId battler, enum Ability ability) { enum Move move; @@ -4505,12 +4799,12 @@ s32 GetChosenMovePriority(u32 battler, enum Ability ability) if (gProtectStructs[battler].noValidMoves) move = MOVE_STRUGGLE; else - move = GetChosenMoveFromPosition(battler); + move = GetBattlerChosenMove(battler); return GetBattleMovePriority(battler, ability, move); } -s32 GetBattleMovePriority(u32 battler, enum Ability ability, enum Move move) +s32 GetBattleMovePriority(enum BattlerId battler, enum Ability ability, enum Move move) { s32 priority = 0; @@ -4686,7 +4980,7 @@ s32 GetWhichBattlerFaster(struct BattleCalcValues *calcValues, bool32 ignoreChos static void SetActionsAndBattlersTurnOrder(void) { s32 turnOrderId = 0; - s32 i, j, battler; + enum BattlerId battler, battler2; if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) { @@ -4714,12 +5008,12 @@ static void SetActionsAndBattlersTurnOrder(void) { if (gChosenActionByBattler[0] == B_ACTION_RUN) { - battler = 0; + battler = B_BATTLER_0; turnOrderId = 5; } if (gChosenActionByBattler[2] == B_ACTION_RUN) { - battler = 2; + battler = B_BATTLER_2; turnOrderId = 5; } } @@ -4729,12 +5023,12 @@ static void SetActionsAndBattlersTurnOrder(void) gActionsByTurnOrder[0] = gChosenActionByBattler[battler]; gBattlerByTurnOrder[0] = battler; turnOrderId = 1; - for (i = 0; i < gBattlersCount; i++) + for (battler2 = 0; battler2 < gBattlersCount; battler2++) { - if (i != battler) + if (battler2 != battler) { - gActionsByTurnOrder[turnOrderId] = gChosenActionByBattler[i]; - gBattlerByTurnOrder[turnOrderId] = i; + gActionsByTurnOrder[turnOrderId] = gChosenActionByBattler[battler2]; + gBattlerByTurnOrder[turnOrderId] = battler2; turnOrderId++; } } @@ -4769,27 +5063,27 @@ static void SetActionsAndBattlersTurnOrder(void) } } struct BattleCalcValues calcValues = {0}; - for (i = 0; i < gBattlersCount; i++) + for (battler = 0; battler < gBattlersCount; battler++) { - calcValues.abilities[i] = GetBattlerAbility(i); - calcValues.holdEffects[i] = GetBattlerHoldEffect(i); + calcValues.abilities[battler] = GetBattlerAbility(battler); + calcValues.holdEffects[battler] = GetBattlerHoldEffect(battler); } - for (i = 0; i < gBattlersCount - 1; i++) + for (battler = 0; battler < gBattlersCount - 1; battler++) { - for (j = i + 1; j < gBattlersCount; j++) + for (battler2 = battler + 1; battler2 < gBattlersCount; battler2++) { - calcValues.battlerAtk = gBattlerByTurnOrder[i]; - calcValues.battlerDef = gBattlerByTurnOrder[j]; + calcValues.battlerAtk = gBattlerByTurnOrder[battler]; + calcValues.battlerDef = gBattlerByTurnOrder[battler2]; TryChangingTurnOrderEffects(&calcValues, quickClawRandom, quickDrawRandom); - if (gActionsByTurnOrder[i] != B_ACTION_USE_ITEM - && gActionsByTurnOrder[j] != B_ACTION_USE_ITEM - && gActionsByTurnOrder[i] != B_ACTION_SWITCH - && gActionsByTurnOrder[j] != B_ACTION_SWITCH - && gActionsByTurnOrder[i] != B_ACTION_THROW_BALL - && gActionsByTurnOrder[j] != B_ACTION_THROW_BALL) + if (gActionsByTurnOrder[battler] != B_ACTION_USE_ITEM + && gActionsByTurnOrder[battler2] != B_ACTION_USE_ITEM + && gActionsByTurnOrder[battler] != B_ACTION_SWITCH + && gActionsByTurnOrder[battler2] != B_ACTION_SWITCH + && gActionsByTurnOrder[battler] != B_ACTION_THROW_BALL + && gActionsByTurnOrder[battler2] != B_ACTION_THROW_BALL) { if (GetWhichBattlerFaster(&calcValues, FALSE) == -1) - SwapTurnOrder(i, j); + SwapTurnOrder(battler, battler2); } } } @@ -4801,9 +5095,7 @@ static void SetActionsAndBattlersTurnOrder(void) static void TurnValuesCleanUp(bool8 var0) { - s32 i; - - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (var0) { @@ -4851,12 +5143,11 @@ static void TurnValuesCleanUp(bool8 var0) static void PopulateArrayWithBattlers(u8 *battlers) { - u32 i; - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) battlers[i] = i; } -static bool32 TryActivateGimmick(u32 battler) +static bool32 TryActivateGimmick(enum BattlerId battler) { if ((gBattleStruct->gimmick.toActivate & (1u << battler)) && !(gProtectStructs[battler].noValidMoves)) { @@ -4875,15 +5166,14 @@ static bool32 TryDoGimmicksBeforeMoves(void) { if (!(gHitMarker & HITMARKER_RUN) && gBattleStruct->gimmick.toActivate) { - u32 i; - u8 order[MAX_BATTLERS_COUNT]; + enum BattlerId battlers[MAX_BATTLERS_COUNT]; - PopulateArrayWithBattlers(order); - SortBattlersBySpeed(order, FALSE); - for (i = 0; i < gBattlersCount; i++) + PopulateArrayWithBattlers(battlers); + SortBattlersBySpeed(battlers, FALSE); + for (enum BattlerId i = 0; i < gBattlersCount; i++) { // Search through each battler and activate their gimmick if they have one prepared. - if (TryActivateGimmick(order[i])) + if (TryActivateGimmick(battlers[i])) return TRUE; } } @@ -4897,20 +5187,20 @@ static bool32 TryDoMoveEffectsBeforeMoves(void) { if (!(gHitMarker & HITMARKER_RUN)) { - u32 i; - u8 battlers[MAX_BATTLERS_COUNT]; + enum BattlerId battlers[MAX_BATTLERS_COUNT]; PopulateArrayWithBattlers(battlers); SortBattlersBySpeed(battlers, FALSE); - for (i = 0; i < gBattlersCount; i++) + for (u32 i = 0; i < gBattlersCount; i++) { - if (!gBattleStruct->battlerState[battlers[i]].focusPunchBattlers - && !(gBattleMons[battlers[i]].status1 & STATUS1_SLEEP) - && !(gBattleMons[battlers[i]].volatiles.truantCounter) - && !(gProtectStructs[battlers[i]].noValidMoves)) + enum BattlerId battler = battlers[i]; + if (!gBattleStruct->battlerState[battler].focusPunchBattlers + && !(gBattleMons[battler].status1 & STATUS1_SLEEP) + && !(gBattleMons[battler].volatiles.truantCounter) + && !(gProtectStructs[battler].noValidMoves)) { - gBattleStruct->battlerState[battlers[i]].focusPunchBattlers = TRUE; - gBattlerAttacker = battlers[i]; + gBattleStruct->battlerState[battler].focusPunchBattlers = TRUE; + gBattlerAttacker = battler; switch (GetMoveEffect(gChosenMoveByBattler[gBattlerAttacker])) { case EFFECT_FOCUS_PUNCH: @@ -4935,7 +5225,7 @@ static bool32 TryDoMoveEffectsBeforeMoves(void) // In gen7, priority and speed are recalculated during the turn in which a pokemon mega evolves static void TryChangeTurnOrder(void) { - u32 i, j; + enum BattlerId i, j; struct BattleCalcValues calcValues = {0}; for (i = 0; i < gBattlersCount; i++) @@ -4962,8 +5252,8 @@ static void TryChangeTurnOrder(void) static void TryChangingTurnOrderEffects(struct BattleCalcValues *calcValues, u32 *quickClawRandom, u32 *quickDrawRandom) { - u32 battler1 = calcValues->battlerAtk; - u32 battler2 = calcValues->battlerDef; + enum BattlerId battler1 = calcValues->battlerAtk; + enum BattlerId battler2 = calcValues->battlerDef; enum Ability ability1 = calcValues->abilities[calcValues->battlerAtk]; enum Ability ability2 = calcValues->abilities[calcValues->battlerDef]; enum HoldEffect holdEffectBattler1 = calcValues->holdEffects[calcValues->battlerAtk]; @@ -4992,7 +5282,7 @@ static void TryChangingTurnOrderEffects(struct BattleCalcValues *calcValues, u32 static void CheckChangingTurnOrderEffects(void) { - u32 i, battler; + enum BattlerId i, battler; if (!(gHitMarker & HITMARKER_RUN)) { @@ -5106,11 +5396,20 @@ static void HandleEndTurn_BattleWon(void) gBattlescriptCurrInstr = BattleScript_LinkBattleWonOrLost; gBattleOutcome &= ~B_OUTCOME_LINK_BATTLE_RAN; } - else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER - && gBattleTypeFlags & (BATTLE_TYPE_FRONTIER | BATTLE_TYPE_TRAINER_HILL | BATTLE_TYPE_EREADER_TRAINER)) + else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && gBattleTypeFlags & (BATTLE_TYPE_FRONTIER | BATTLE_TYPE_EREADER_TRAINER)) { BattleStopLowHpSound(); - gBattlescriptCurrInstr = BattleScript_BattleTowerTrainerBattleWon; + gBattlescriptCurrInstr = BattleScript_FrontierTrainerBattleWon; + + if (TRAINER_BATTLE_PARAM.opponentA == TRAINER_FRONTIER_BRAIN) + PlayBGM(MUS_VICTORY_GYM_LEADER); + else + PlayBGM(MUS_VICTORY_TRAINER); + } + else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER) + { + BattleStopLowHpSound(); + gBattlescriptCurrInstr = BattleScript_TrainerTowerTrainerBattleWon; PlayBGM(MUS_VICTORY_TRAINER); } else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && !(gBattleTypeFlags & BATTLE_TYPE_LINK)) @@ -5148,21 +5447,21 @@ static void HandleEndTurn_BattleLost(void) if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) { - // if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER) - // { - // if (gBattleOutcome & B_OUTCOME_LINK_BATTLE_RAN) - // { - // gBattlescriptCurrInstr = BattleScript_PrintPlayerForfeitedLinkBattle; - // gBattleOutcome &= ~B_OUTCOME_LINK_BATTLE_RAN; - // gSaveBlock2Ptr->frontier.disableRecordBattle = TRUE; - // } - // else - // { - // gBattlescriptCurrInstr = BattleScript_FrontierLinkBattleLost; - // gBattleOutcome &= ~B_OUTCOME_LINK_BATTLE_RAN; - // } - // } - // else + if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER) + { + if (gBattleOutcome & B_OUTCOME_LINK_BATTLE_RAN) + { + gBattlescriptCurrInstr = BattleScript_PrintPlayerForfeitedLinkBattle; + gBattleOutcome &= ~B_OUTCOME_LINK_BATTLE_RAN; + gSaveBlock2Ptr->frontier.disableRecordBattle = TRUE; + } + else + { + gBattlescriptCurrInstr = BattleScript_FrontierLinkBattleLost; + gBattleOutcome &= ~B_OUTCOME_LINK_BATTLE_RAN; + } + } + else { gBattleTextBuff1[0] = gBattleOutcome; gBattlerAttacker = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); @@ -5194,24 +5493,24 @@ static void HandleEndTurn_RanFromBattle(void) { gCurrentActionFuncId = 0; - // if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER && gBattleTypeFlags & BATTLE_TYPE_TRAINER) - // { - // gBattlescriptCurrInstr = BattleScript_PrintPlayerForfeited; - // gBattleOutcome = B_OUTCOME_FORFEITED; - // gSaveBlock2Ptr->frontier.disableRecordBattle = TRUE; - // } - // else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_HILL) - // { - // gBattlescriptCurrInstr = BattleScript_PrintPlayerForfeited; - // gBattleOutcome = B_OUTCOME_FORFEITED; - // } - // else if (CanPlayerForfeitNormalTrainerBattle()) - // { - // gBattlescriptCurrInstr = BattleScript_ForfeitBattleGaveMoney; - // gBattleOutcome = B_OUTCOME_FORFEITED; - // } - // else - // { + if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER && gBattleTypeFlags & BATTLE_TYPE_TRAINER) + { + gBattlescriptCurrInstr = BattleScript_PrintPlayerForfeited; + gBattleOutcome = B_OUTCOME_FORFEITED; + gSaveBlock2Ptr->frontier.disableRecordBattle = TRUE; + } + else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_HILL) + { + gBattlescriptCurrInstr = BattleScript_PrintPlayerForfeited; + gBattleOutcome = B_OUTCOME_FORFEITED; + } + else if (CanPlayerForfeitNormalTrainerBattle()) + { + gBattlescriptCurrInstr = BattleScript_ForfeitBattleGaveMoney; + gBattleOutcome = B_OUTCOME_FORFEITED; + } + else + { switch (gProtectStructs[gBattlerAttacker].fleeType) { default: @@ -5224,7 +5523,7 @@ static void HandleEndTurn_RanFromBattle(void) gBattlescriptCurrInstr = BattleScript_RanAwayUsingMonAbility; break; } - // } + } gBattleMainFunc = HandleEndTurn_FinishBattle; } @@ -5241,21 +5540,19 @@ static void HandleEndTurn_MonFled(void) static void HandleEndTurn_FinishBattle(void) { - u32 i, battler; - if (gCurrentActionFuncId == B_ACTION_TRY_FINISH || gCurrentActionFuncId == B_ACTION_FINISHED) { if (!(gBattleTypeFlags & (BATTLE_TYPE_TRAINER_TOWER - | BATTLE_TYPE_RECORDED_LINK - | BATTLE_TYPE_EREADER_TRAINER - | BATTLE_TYPE_CATCH_TUTORIAL - | BATTLE_TYPE_BATTLE_TOWER - | BATTLE_TYPE_SAFARI - | BATTLE_TYPE_FIRST_BATTLE - | BATTLE_TYPE_LINK)) + | BATTLE_TYPE_RECORDED_LINK + | BATTLE_TYPE_EREADER_TRAINER + | BATTLE_TYPE_CATCH_TUTORIAL + | BATTLE_TYPE_FRONTIER + | BATTLE_TYPE_SAFARI + | BATTLE_TYPE_FIRST_BATTLE + | BATTLE_TYPE_LINK)) && !(gBattleTypeFlags & BATTLE_TYPE_GHOST && IsGhostBattleWithoutScope())) { - for (battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { if (IsOnPlayerSide(battler)) { @@ -5302,7 +5599,7 @@ static void HandleEndTurn_FinishBattle(void) if (B_TRAINERS_KNOCK_OFF_ITEMS == TRUE || B_RESTORE_HELD_BATTLE_ITEMS >= GEN_9) TryRestoreHeldItems(); - for (i = 0; i < PARTY_SIZE; i++) + for (u32 i = 0; i < PARTY_SIZE; i++) { bool32 changedForm = TryRevertPartyMonFormChange(i); @@ -5316,13 +5613,13 @@ static void HandleEndTurn_FinishBattle(void) // Clear battle mon species to avoid a bug on the next battle that causes // healthboxes loading incorrectly due to it trying to create a Mega Indicator // if the previous battler would've had it. - for (i = 0; i < MAX_BATTLERS_COUNT; i++) + for (enum BattlerId i = 0; i < MAX_BATTLERS_COUNT; i++) { gBattleMons[i].species = SPECIES_NONE; } // Set Battle Controllers to BATTLE_CONTROLLER_NONE - for (i = 0; i < MAX_BATTLERS_COUNT; i++) + for (enum BattlerId i = 0; i < MAX_BATTLERS_COUNT; i++) { gBattlerBattleController[i] = BATTLE_CONTROLLER_NONE; } @@ -5359,7 +5656,7 @@ static void FreeResetData_ReturnToOvOrDoEvolutions(void) | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI - | BATTLE_TYPE_BATTLE_TOWER + | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_CATCH_TUTORIAL | BATTLE_TYPE_POKEDUDE)) @@ -5490,7 +5787,7 @@ void RunBattleScriptCommands(void) gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]](); } -enum Type TrySetAteType(enum Move move, u32 battlerAtk, enum Ability attackerAbility) +enum Type TrySetAteType(enum Move move, enum BattlerId battlerAtk, enum Ability attackerAbility) { enum Type ateType = TYPE_NONE; @@ -5538,7 +5835,7 @@ enum Type TrySetAteType(enum Move move, u32 battlerAtk, enum Ability attackerAbi } // Returns TYPE_NONE if type doesn't change. -enum Type GetDynamicMoveType(struct Pokemon *mon, enum Move move, u32 battler, enum MonState state) +enum Type GetDynamicMoveType(struct Pokemon *mon, enum Move move, enum BattlerId battler, enum MonState state) { enum Type moveType = GetMoveType(move); enum BattleMoveEffects moveEffect = GetMoveEffect(move); @@ -5736,6 +6033,10 @@ enum Type GetDynamicMoveType(struct Pokemon *mon, enum Move move, u32 battler, e if (species == SPECIES_TERAPAGOS_STELLAR) return TYPE_STELLAR; break; + case EFFECT_NATURE_POWER: + if (state == MON_IN_BATTLE) + return GetMoveType(GetNaturePowerMove()); + break; default: break; } @@ -5776,7 +6077,7 @@ enum Type GetDynamicMoveType(struct Pokemon *mon, enum Move move, u32 battler, e return TYPE_NONE; } -void SetTypeBeforeUsingMove(enum Move move, u32 battler) +void SetTypeBeforeUsingMove(enum Move move, enum BattlerId battler) { enum Type moveType; u32 heldItem = gBattleMons[battler].item; @@ -5804,8 +6105,7 @@ void SetTypeBeforeUsingMove(enum Move move, u32 battler) if (holdEffect == HOLD_EFFECT_GEMS && GetBattleMoveType(move) == GetItemSecondaryId(heldItem) && effect != EFFECT_PLEDGE - && effect != EFFECT_OHKO - && effect != EFFECT_SHEER_COLD) + && effect != EFFECT_OHKO) { gSpecialStatuses[battler].gemParam = GetBattlerHoldEffectParam(battler); gSpecialStatuses[battler].gemBoost = TRUE; @@ -5815,7 +6115,7 @@ void SetTypeBeforeUsingMove(enum Move move, u32 battler) // Queues stat boosts for a given battler for totem battles void ScriptSetTotemBoost(struct ScriptContext *ctx) { - u32 battler = VarGet(ScriptReadHalfword(ctx)); + enum BattlerId battler = VarGet(ScriptReadHalfword(ctx)); u32 stat; u32 i; diff --git a/src/battle_message.c b/src/battle_message.c index 62fd0941a..fb5aa1027 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -10,6 +10,7 @@ #include "event_data.h" #include "event_scripts.h" #include "field_specials.h" +#include "frontier_util.h" #include "graphics.h" #include "item.h" #include "line_break.h" @@ -22,6 +23,7 @@ #include "constants/abilities.h" #include "constants/battle_dome.h" #include "constants/battle_string_ids.h" +#include "constants/frontier_util.h" #include "constants/moves.h" #include "constants/items.h" #include "constants/trainers.h" @@ -87,7 +89,12 @@ static const u8 sText_LinkTrainerSentOutPkmn[] = _("{B_LINK_OPPONENT1_NAME} sent static const u8 sText_LinkTrainerSentOutTwoPkmn[] = _("{B_LINK_OPPONENT1_NAME} sent out {B_OPPONENT_MON1_NAME} and {B_OPPONENT_MON2_NAME}!"); static const u8 sText_TwoLinkTrainersSentOutPkmn[] = _("{B_LINK_OPPONENT1_NAME} sent out {B_LINK_OPPONENT_MON1_NAME}! {B_LINK_OPPONENT2_NAME} sent out {B_LINK_OPPONENT_MON2_NAME}!"); static const u8 sText_LinkTrainerSentOutPkmn2[] = _("{B_LINK_OPPONENT1_NAME} sent out {B_BUFF1}!"); +static const u8 sText_LinkTrainer2SentOutPkmn2[] = _("{B_LINK_OPPONENT2_NAME} sent out {B_OPPONENT_MON2_NAME}!"); static const u8 sText_LinkTrainerMultiSentOutPkmn[] = _("{B_LINK_SCR_TRAINER_NAME} sent out {B_BUFF1}!"); +static const u8 sText_LinkPartnerSentOutPkmn1GoPkmn[] = _("{B_LINK_PARTNER_NAME} sent out {B_LINK_PLAYER_MON1_NAME}! Go! {B_LINK_PLAYER_MON2_NAME}!"); +static const u8 sText_LinkPartnerSentOutPkmn2GoPkmn[] = _("{B_LINK_PARTNER_NAME} sent out {B_LINK_PLAYER_MON2_NAME}! Go! {B_LINK_PLAYER_MON1_NAME}!"); +static const u8 sText_LinkPartnerSentOutPkmn1[] = _("{B_LINK_PARTNER_NAME} sent out {B_LINK_PLAYER_MON1_NAME}!"); +static const u8 sText_LinkPartnerSentOutPkmn2[] = _("{B_LINK_PARTNER_NAME} sent out {B_LINK_PLAYER_MON2_NAME}!"); static const u8 sText_GoPkmn[] = _("Go! {B_PLAYER_MON1_NAME}!"); static const u8 sText_GoTwoPkmn[] = _("Go! {B_PLAYER_MON1_NAME} and {B_PLAYER_MON2_NAME}!"); static const u8 sText_GoPkmn2[] = _("Go! {B_BUFF1}!"); @@ -104,7 +111,9 @@ static const u8 sText_PkmnGoodComeBack[] = _("Good job, {B_BUFF1}! Come back!"); static const u8 sText_Trainer1WithdrewPkmn[] = _("{B_TRAINER1_NAME_WITH_CLASS} withdrew {B_BUFF1}!"); static const u8 sText_Trainer2WithdrewPkmn[] = _("{B_TRAINER2_NAME_WITH_CLASS} withdrew {B_BUFF1}!"); static const u8 sText_LinkTrainer1WithdrewPkmn[] = _("{B_LINK_OPPONENT1_NAME} withdrew {B_BUFF1}!"); -static const u8 sText_LinkTrainer2WithdrewPkmn[] = _("{B_LINK_SCR_TRAINER_NAME} withdrew {B_BUFF1}!"); +static const u8 sText_LinkTrainer2WithdrewPkmn[] = _("{B_LINK_OPPONENT2_NAME} withdrew {B_BUFF1}!"); +static const u8 sText_LinkPartnerWithdrewPkmn1[] = _("{B_LINK_PARTNER_NAME} withdrew {B_LINK_PLAYER_MON1_NAME}!"); +static const u8 sText_LinkPartnerWithdrewPkmn2[] = _("{B_LINK_PARTNER_NAME} withdrew {B_LINK_PLAYER_MON2_NAME}!"); static const u8 sText_WildPkmnPrefix[] = _("The wild "); static const u8 sText_FoePkmnPrefix[] = _("The opposing "); static const u8 sText_WildPkmnPrefixLower[] = _("the wild "); @@ -230,7 +239,9 @@ static const u8 sText_TwoTrainersSentPkmn[] = _("{B_TRAINER1_NAME_WITH_CLASS} se static const u8 sText_Trainer2SentOutPkmn[] = _("{B_TRAINER2_NAME_WITH_CLASS} sent out {B_BUFF1}!"); static const u8 sText_TwoTrainersWantToBattle[] = _("You are challenged by {B_TRAINER1_NAME_WITH_CLASS} and {B_TRAINER2_NAME_WITH_CLASS}!\p"); static const u8 sText_InGamePartnerSentOutZGoN[] = _("{B_PARTNER_NAME_WITH_CLASS} sent out {B_PLAYER_MON2_NAME}! Go, {B_PLAYER_MON1_NAME}!"); +static const u8 sText_InGamePartnerSentOutPkmn1[] = _("{B_PARTNER_NAME_WITH_CLASS} sent out {B_PLAYER_MON1_NAME}!"); static const u8 sText_InGamePartnerSentOutPkmn2[] = _("{B_PARTNER_NAME_WITH_CLASS} sent out {B_PLAYER_MON2_NAME}!"); +static const u8 sText_InGamePartnerWithdrewPkmn1[] = _("{B_PARTNER_NAME_WITH_CLASS} withdrew {B_PLAYER_MON1_NAME}!"); static const u8 sText_InGamePartnerWithdrewPkmn2[] = _("{B_PARTNER_NAME_WITH_CLASS} withdrew {B_PLAYER_MON2_NAME}!"); const u16 gBattlePalaceFlavorTextTable[] = @@ -322,7 +333,6 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_ATTACKMISSED] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}'s attack missed!"), [STRINGID_PKMNPROTECTEDITSELF] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} protected itself!"), [STRINGID_STATSWONTINCREASE2] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}'s stats won't go any higher!"), - [STRINGID_AVOIDEDDAMAGE] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} avoided damage with {B_DEF_ABILITY}!"), //not in gen 5+, ability popup [STRINGID_ITDOESNTAFFECT] = COMPOUND_STRING("It doesn't affect {B_DEF_NAME_WITH_PREFIX2}…"), [STRINGID_SCR_ITDOESNTAFFECT] = COMPOUND_STRING("It doesn't affect {B_SCR_NAME_WITH_PREFIX2}…"), [STRINGID_BATTLERFAINTED] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} fainted!\p"), @@ -414,7 +424,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_PKMNSTAYEDAWAKEUSING] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} stayed awake using its {B_DEF_ABILITY}!"), //not in gen 5+, ability popup [STRINGID_PKMNSTORINGENERGY] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} is storing energy!"), [STRINGID_PKMNUNLEASHEDENERGY] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} unleashed its energy!"), - [STRINGID_PKMNFATIGUECONFUSION] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} became confused due to fatigue!"), + [STRINGID_PKMNFATIGUECONFUSION] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} became confused due to fatigue!"), [STRINGID_PLAYERPICKEDUPMONEY] = COMPOUND_STRING("You picked up ¥{B_BUFF1}!\p"), [STRINGID_PKMNUNAFFECTED] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} is unaffected!"), [STRINGID_PKMNTRANSFORMEDINTO] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} transformed into {B_BUFF1}!"), @@ -488,11 +498,11 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_PKMNMADEITRAIN] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ABILITY} made it rain!"), //not in gen 5+, ability popup [STRINGID_PKMNPROTECTEDBY] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} was protected by {B_DEF_ABILITY}!"), //not in gen 5+, ability popup [STRINGID_PKMNPREVENTSUSAGE] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} prevents {B_ATK_NAME_WITH_PREFIX2} from using {B_CURRENT_MOVE}!"), //I don't see this in SV text - [STRINGID_PKMNRESTOREDHPUSING] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} restored HP using its {B_DEF_ABILITY}!"), //not in gen 5+, ability popup + [STRINGID_PKMNRESTOREDHPUSING] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} restored HP using its {B_SCR_ABILITY}!"), //not in gen 5+, ability popup [STRINGID_PKMNCHANGEDTYPEWITH] = COMPOUND_STRING("{B_EFF_NAME_WITH_PREFIX}'s {B_EFF_ABILITY} made it the {B_BUFF1} type!"), //not in gen 5+, ability popup [STRINGID_PKMNPREVENTSROMANCEWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} prevents romance!"), //not in gen 5+, ability popup [STRINGID_PKMNPREVENTSCONFUSIONWITH] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ABILITY} prevents confusion!"), //not in gen 5+, ability popup - [STRINGID_PKMNRAISEDFIREPOWERWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} raised the power of Fire-type moves!"), //not in gen 5+, ability popup + [STRINGID_PKMNRAISEDFIREPOWERWITH] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ABILITY} raised the power of Fire-type moves!"), //not in gen 5+, ability popup [STRINGID_PKMNANCHORSITSELFWITH] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} anchors itself with {B_DEF_ABILITY}!"), //not in gen 5+, ability popup [STRINGID_PKMNCUTSATTACKWITH] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ABILITY} cuts {B_DEF_NAME_WITH_PREFIX2}'s Attack!"), //not in gen 5+, ability popup [STRINGID_PKMNPREVENTSSTATLOSSWITH] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ABILITY} prevents stat loss!"), //not in gen 5+, ability popup @@ -595,7 +605,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_PKMNSXWHIPPEDUPSANDSTORM] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ABILITY} whipped up a sandstorm!"), //not in gen 5+, ability popup [STRINGID_PKMNSXPREVENTSYLOSS] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ABILITY} prevents {B_BUFF1} loss!"), //not in gen 5+, ability popup [STRINGID_PKMNSXINFATUATEDY] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} infatuated {B_ATK_NAME_WITH_PREFIX2}!"), //not in gen 5+, ability popup - [STRINGID_PKMNSXMADEYINEFFECTIVE] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} made {B_CURRENT_MOVE} ineffective!"), //not in gen 5+, ability popup + [STRINGID_PKMNSXMADEYINEFFECTIVE] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ABILITY} made {B_CURRENT_MOVE} ineffective!"), //not in gen 5+, ability popup [STRINGID_PKMNSXCUREDYPROBLEM] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ABILITY} cured its {B_BUFF1} problem!"), //not in gen 5+, ability popup [STRINGID_ITSUCKEDLIQUIDOOZE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} sucked up the liquid ooze!"), [STRINGID_PKMNTRANSFORMED] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} transformed!"), @@ -611,12 +621,11 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_USINGITEMSTATOFPKMNROSE] = COMPOUND_STRING("Using {B_LAST_ITEM}, the {B_BUFF1} of {B_SCR_NAME_WITH_PREFIX2} {B_BUFF2}rose!"), //todo: update this, will require code changes [STRINGID_USINGITEMSTATOFPKMNFELL] = COMPOUND_STRING("Using {B_LAST_ITEM}, the {B_BUFF1} of {B_SCR_NAME_WITH_PREFIX2} {B_BUFF2}fell!"), [STRINGID_PKMNUSEDXTOGETPUMPED] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} used the {B_LAST_ITEM} to get pumped!"), - [STRINGID_PKMNSXMADEYUSELESS] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} made {B_CURRENT_MOVE} useless!"), //not in gen 5+, ability popup + [STRINGID_PKMNSXMADEYUSELESS] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ABILITY} made {B_CURRENT_MOVE} useless!"), //not in gen 5+, ability popup [STRINGID_PKMNTRAPPEDBYSANDTOMB] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} became trapped by the quicksand!"), [STRINGID_EMPTYSTRING4] = COMPOUND_STRING(""), [STRINGID_ABOOSTED] = COMPOUND_STRING(" a boosted"), [STRINGID_PKMNSXINTENSIFIEDSUN] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ABILITY} intensified the sun's rays!"), //not in gen 5+, ability popup - [STRINGID_PKMNMAKESGROUNDMISS] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} makes Ground-type moves miss with {B_DEF_ABILITY}!"), //not in gen 5+, ability popup [STRINGID_YOUTHROWABALLNOWRIGHT] = COMPOUND_STRING("You throw a Ball now, right? I… I'll do my best!"), [STRINGID_PKMNSXTOOKATTACK] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} took the attack!"), //In gen 5+ but without naming the ability [STRINGID_PKMNCHOSEXASDESTINY] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} chose Doom Desire as its destiny!"), @@ -756,7 +765,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_HEALINGWISHCAMETRUE] = COMPOUND_STRING("The healing wish came true for {B_SCR_NAME_WITH_PREFIX2}!"), [STRINGID_HEALINGWISHHEALED] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} regained health!"), [STRINGID_LUNARDANCECAMETRUE] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} became cloaked in mystical moonlight!"), - [STRINGID_CURSEDBODYDISABLED] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}'s {B_BUFF1} was disabled by {B_DEF_NAME_WITH_PREFIX2}'s {B_DEF_ABILITY}!"), + [STRINGID_CURSEDBODYDISABLED] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}'s {B_BUFF1} was disabled by {B_DEF_NAME_WITH_PREFIX2}'s {B_DEF_ABILITY}!"), [STRINGID_ATTACKERACQUIREDABILITY] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} acquired {B_ATK_ABILITY}!"), [STRINGID_TARGETABILITYSTATLOWER] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY} {B_BUFF2}lowered its {B_BUFF1}!"), [STRINGID_TARGETSTATWONTGOHIGHER] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s {B_BUFF1} won't go any higher!"), @@ -797,8 +806,8 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_MISTYTERRAINPREVENTS] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} surrounds itself with a protective mist!"), [STRINGID_GRASSYTERRAINHEALS] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} is healed by the grassy terrain!"), [STRINGID_ELECTRICTERRAINPREVENTS] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} surrounds itself with electrified terrain!"), - [STRINGID_PSYCHICTERRAINPREVENTS] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} is protected by the Psychic Terrain!"), - [STRINGID_SAFETYGOGGLESPROTECTED] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} is not affected thanks to its {B_LAST_ITEM}!"), + [STRINGID_PSYCHICTERRAINPREVENTS] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} is protected by the Psychic Terrain!"), + [STRINGID_SAFETYGOGGLESPROTECTED] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} is not affected thanks to its {B_LAST_ITEM}!"), [STRINGID_FLOWERVEILPROTECTED] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} surrounded itself with a veil of petals!"), [STRINGID_SWEETVEILPROTECTED] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} can't fall asleep due to a veil of sweetness!"), [STRINGID_AROMAVEILPROTECTED] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} is protected by an aromatic veil!"), @@ -1015,16 +1024,17 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_SILPHSCOPEUNVEILED] = COMPOUND_STRING("SILPH SCOPE unveiled the GHOST's\nidentity!"), [STRINGID_GHOSTWASMAROWAK] = COMPOUND_STRING("The GHOST was MAROWAK!\p\n"), [STRINGID_TRAINER1MON1COMEBACK] = COMPOUND_STRING("{B_TRAINER1_NAME}: {B_OPPONENT_MON1_NAME}, come back!"), + [STRINGID_THREWROCK] = COMPOUND_STRING("{B_PLAYER_NAME} threw a ROCK\nat the {B_OPPONENT_MON1_NAME}!"), + [STRINGID_THREWBAIT] = COMPOUND_STRING("{B_PLAYER_NAME} threw some BAIT\nat the {B_OPPONENT_MON1_NAME}!"), + [STRINGID_PKMNANGRY] = COMPOUND_STRING("{B_OPPONENT_MON1_NAME} is angry!"), + [STRINGID_PKMNEATING] = COMPOUND_STRING("{B_OPPONENT_MON1_NAME} is eating!"), + [STRINGID_PKMNDISGUISEWASBUSTED] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s disguise was busted!"), // pokefirered strings [STRINGID_PKMNTRANSFERREDBILLSPC] = gText_PkmnTransferredBillsPC, [STRINGID_PKMNBOXBILLSPCFULL] = gText_PkmnTransferredBillsPCBoxFull, [STRINGID_OLDMANUSEDITEM] = COMPOUND_STRING("The old man used\n{B_LAST_ITEM}!"), [STRINGID_GOTCHAPKMNCAUGHTOLDMAN] = COMPOUND_STRING("Gotcha!\n{B_OPPONENT_MON1_NAME} was caught!{WAIT_SE}{PLAY_BGM MUS_CAUGHT}{PAUSE 127}"), - [STRINGID_THREWROCK] = COMPOUND_STRING("{B_PLAYER_NAME} threw a ROCK\nat the {B_OPPONENT_MON1_NAME}!"), - [STRINGID_THREWBAIT] = COMPOUND_STRING("{B_PLAYER_NAME} threw some BAIT\nat the {B_OPPONENT_MON1_NAME}!"), - [STRINGID_PKMNANGRY] = COMPOUND_STRING("{B_OPPONENT_MON1_NAME} is angry!"), - [STRINGID_PKMNEATING] = COMPOUND_STRING("{B_OPPONENT_MON1_NAME} is eating!"), [STRINGID_POKEDUDEUSED] = COMPOUND_STRING("The POKé DUDE used\n{B_LAST_ITEM}!"), [STRINGID_TRAINER1MON2COMEBACK] = COMPOUND_STRING("{B_TRAINER1_NAME}: {B_OPPONENT_MON2_NAME}, come back!"), [STRINGID_TRAINER1MON1AND2COMEBACK] = COMPOUND_STRING("{B_TRAINER1_NAME}: {B_OPPONENT_MON1_NAME} and\n{B_OPPONENT_MON2_NAME}, come back!"), @@ -1141,8 +1151,6 @@ const u16 gMissStringIds[] = [B_MSG_MISSED] = STRINGID_ATTACKMISSED, [B_MSG_PROTECTED] = STRINGID_PKMNPROTECTEDITSELF, [B_MSG_AVOIDED_ATK] = STRINGID_PKMNAVOIDEDATTACK, - [B_MSG_AVOIDED_DMG] = STRINGID_AVOIDEDDAMAGE, - [B_MSG_GROUND_MISS] = STRINGID_PKMNMAKESGROUNDMISS }; const u16 gNoEscapeStringIds[] = @@ -1807,7 +1815,7 @@ void BufferStringBattle(enum StringID stringID, u32 battler) } else { - if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK))) + if (!(BattlerIsLink(battler) || (BattlerIsRecorded(battler) && BattlerIsOpponent(battler)))) stringPtr = sText_Trainer1SentOutPkmn; else if (TRAINER_BATTLE_PARAM.opponentA == TRAINER_UNION_ROOM) stringPtr = sText_Trainer1SentOutPkmn; @@ -1817,120 +1825,156 @@ void BufferStringBattle(enum StringID stringID, u32 battler) } break; case STRINGID_RETURNMON: // sending poke to ball msg - if (IsOnPlayerSide(battler)) + if ((GetBattlerPosition(battler) & BIT_FLANK) == B_FLANK_LEFT) // battler 0 and 1 { - if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT) - stringPtr = sText_InGamePartnerWithdrewPkmn2; - else if (*(&gBattleStruct->hpScale) == 0) - stringPtr = sText_PkmnThatsEnough; - else if (*(&gBattleStruct->hpScale) == 1 || IsDoubleBattle()) - stringPtr = sText_PkmnComeBack; - else if (*(&gBattleStruct->hpScale) == 2) - stringPtr = sText_PkmnOkComeBack; - else - stringPtr = sText_PkmnGoodComeBack; - } - else - { - if (TRAINER_BATTLE_PARAM.opponentA == TRAINER_LINK_OPPONENT || gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK) + if (BattlerIsPlayer(battler) || BattlerIsOldMan(battler)) // Player { - if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + if (*(&gBattleStruct->hpScale) == 0) + stringPtr = sText_PkmnThatsEnough; + else if (*(&gBattleStruct->hpScale) == 1 || IsDoubleBattle()) + stringPtr = sText_PkmnComeBack; + else if (*(&gBattleStruct->hpScale) == 2) + stringPtr = sText_PkmnOkComeBack; + else + stringPtr = sText_PkmnGoodComeBack; + } + else if (BattlerIsPartner(battler)) + { + if (BattlerIsLink(battler)) // Link Partner + { + stringPtr = sText_LinkPartnerWithdrewPkmn1; + } + else // In-game Partner + { + stringPtr = sText_InGamePartnerWithdrewPkmn1; + } + } + else if (BattlerIsLink(battler) || TRAINER_BATTLE_PARAM.opponentA == TRAINER_LINK_OPPONENT + || gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK) // Link Opponent 1 and test opponent + { + stringPtr = sText_LinkTrainer1WithdrewPkmn; + } + else // Opponent A + { + stringPtr = sText_Trainer1WithdrewPkmn; + } + } + else // battler 2 and 3 + { + if (BattlerIsPlayer(battler)) // Player + { + if (*(&gBattleStruct->hpScale) == 0) + stringPtr = sText_PkmnThatsEnough; + else if (*(&gBattleStruct->hpScale) == 1 || IsDoubleBattle()) + stringPtr = sText_PkmnComeBack; + else if (*(&gBattleStruct->hpScale) == 2) + stringPtr = sText_PkmnOkComeBack; + else + stringPtr = sText_PkmnGoodComeBack; + } + else if (BattlerIsPartner(battler)) + { + if (BattlerIsLink(battler)) // Link Partner + { + stringPtr = sText_LinkPartnerWithdrewPkmn2; + } + else // In-game Partner + { + stringPtr = sText_InGamePartnerWithdrewPkmn2; + } + } + else if (BattlerIsLink(battler) || TRAINER_BATTLE_PARAM.opponentA == TRAINER_LINK_OPPONENT + || TRAINER_BATTLE_PARAM.opponentB == TRAINER_LINK_OPPONENT || gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK) // Link Opponent B and test opponent + { + if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) stringPtr = sText_LinkTrainer2WithdrewPkmn; else stringPtr = sText_LinkTrainer1WithdrewPkmn; } - else + else if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) // Opponent B { - if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) - { - if (GetBattlerPosition(battler) == B_POSITION_OPPONENT_LEFT) - stringPtr = sText_Trainer1WithdrewPkmn; - else - stringPtr = sText_Trainer2WithdrewPkmn; - - } - else - { - stringPtr = sText_Trainer1WithdrewPkmn; - } + stringPtr = sText_Trainer2WithdrewPkmn; + } + else // Opponent A + { + stringPtr = sText_Trainer1WithdrewPkmn; } } break; case STRINGID_SWITCHINMON: // switch-in msg - if (IsOnPlayerSide(gBattleScripting.battler)) + if ((GetBattlerPosition(gBattleScripting.battler) & BIT_FLANK) == B_FLANK_LEFT) // battler 0 and 1 { - if ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) && (GetBattlerAtPosition(gBattleScripting.battler) == 2)) - stringPtr = sText_InGamePartnerSentOutPkmn2; - else if (*(&gBattleStruct->hpScale) == 0 || IsDoubleBattle()) - stringPtr = sText_GoPkmn2; - else if (*(&gBattleStruct->hpScale) == 1) - stringPtr = sText_DoItPkmn; - else if (*(&gBattleStruct->hpScale) == 2) - stringPtr = sText_GoForItPkmn; - else - stringPtr = sText_YourFoesWeakGetEmPkmn; - } - else - { - if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) + if (BattlerIsPlayer(gBattleScripting.battler)) // Player { - if (gBattleTypeFlags & BATTLE_TYPE_TOWER_LINK_MULTI) - { - if (gBattleScripting.battler == 1) - stringPtr = sText_Trainer1SentOutPkmn2; - else - stringPtr = sText_Trainer2SentOutPkmn; - } + if (*(&gBattleStruct->hpScale) == 0) + stringPtr = sText_GoPkmn2; + else if (*(&gBattleStruct->hpScale) == 1 || IsDoubleBattle()) + stringPtr = sText_DoItPkmn; + else if (*(&gBattleStruct->hpScale) == 2) + stringPtr = sText_GoForItPkmn; else + stringPtr = sText_YourFoesWeakGetEmPkmn; + } + else if (BattlerIsPartner(gBattleScripting.battler)) + { + if (BattlerIsLink(gBattleScripting.battler)) // Link Partner { - if (TESTING && gBattleTypeFlags & BATTLE_TYPE_MULTI) - { - if (gBattleScripting.battler == 1) - { - stringPtr = sText_Trainer1SentOutPkmn; - } - else - { - if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) - stringPtr = sText_Trainer2SentOutPkmn; - else - stringPtr = sText_Trainer1SentOutPkmn2; - } - } - else if (TESTING && gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) - { - if (gBattleScripting.battler == 1) - stringPtr = sText_Trainer1SentOutPkmn; - else - stringPtr = sText_Trainer2SentOutPkmn; - } - else if (gBattleTypeFlags & BATTLE_TYPE_MULTI) - { - stringPtr = sText_LinkTrainerMultiSentOutPkmn; - } - else if (TRAINER_BATTLE_PARAM.opponentA == TRAINER_UNION_ROOM) - { - stringPtr = sText_Trainer1SentOutPkmn2; - } - else - { - stringPtr = sText_LinkTrainerSentOutPkmn2; - } + stringPtr = sText_LinkPartnerSentOutPkmn1; + } + else // In-game Partner + { + stringPtr = sText_InGamePartnerSentOutPkmn1; } } - else + else if (BattlerIsLink(gBattleScripting.battler) || TRAINER_BATTLE_PARAM.opponentA == TRAINER_LINK_OPPONENT + || gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK) // Link Opponent 1 and test opponent + { + stringPtr = sText_LinkTrainerSentOutPkmn; + } + else // Opponent A + { + stringPtr = sText_Trainer1SentOutPkmn; + } + } + else // battler 2 and 3 + { + if (BattlerIsPlayer(gBattleScripting.battler)) // Player + { + if (*(&gBattleStruct->hpScale) == 0) + stringPtr = sText_GoPkmn2; + else if (*(&gBattleStruct->hpScale) == 1 || IsDoubleBattle()) + stringPtr = sText_DoItPkmn; + else if (*(&gBattleStruct->hpScale) == 2) + stringPtr = sText_GoForItPkmn; + else + stringPtr = sText_YourFoesWeakGetEmPkmn; + } + else if (BattlerIsPartner(gBattleScripting.battler)) + { + if (BattlerIsLink(gBattleScripting.battler)) // Link Partner + { + stringPtr = sText_LinkPartnerSentOutPkmn2; + } + else // In-game Partner + { + stringPtr = sText_InGamePartnerSentOutPkmn2; + } + } + else if (BattlerIsLink(gBattleScripting.battler) || TRAINER_BATTLE_PARAM.opponentA == TRAINER_LINK_OPPONENT + || TRAINER_BATTLE_PARAM.opponentB == TRAINER_LINK_OPPONENT || gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK) // Link Opponent B and test opponent { if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) - { - if (gBattleScripting.battler == 1) - stringPtr = sText_Trainer1SentOutPkmn2; - else - stringPtr = sText_Trainer2SentOutPkmn; - } + stringPtr = sText_LinkTrainer2SentOutPkmn2; else - { - stringPtr = sText_Trainer1SentOutPkmn2; - } + stringPtr = sText_LinkTrainerSentOutPkmn2; + } + else if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) // Opponent B + { + stringPtr = sText_Trainer2SentOutPkmn; + } + else // Opponent A + { + stringPtr = sText_Trainer1SentOutPkmn2; } } break; @@ -2151,9 +2195,15 @@ static const u8 *BattleStringGetOpponentNameByTrainerId(u16 trainerId, u8 *text, else toCpy = gLinkPlayers[GetBattlerMultiplayerId(battler) & BIT_SIDE].name; } - else if (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) + else if (trainerId == TRAINER_FRONTIER_BRAIN) { - StringCopy(text, gText_Placeholder); + CopyFrontierBrainTrainerName(text); + toCpy = text; + } + else if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER) + { + GetFrontierTrainerName(text, trainerId); + toCpy = text; } else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER) { @@ -2256,8 +2306,10 @@ static const u8 *BattleStringGetOpponentClassByTrainerId(u16 trainerId) toCpy = gTrainerClasses[GetSecretBaseTrainerClass()].name; else if (trainerId == TRAINER_UNION_ROOM) toCpy = gTrainerClasses[GetUnionRoomTrainerClass()].name; - else if (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) - toCpy = gTrainerClasses[TRAINER_CLASS_PLAYER].name; + else if (trainerId == TRAINER_FRONTIER_BRAIN) + toCpy = gTrainerClasses[GetFrontierBrainTrainerClass()].name; + else if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER) + toCpy = gTrainerClasses[GetFrontierOpponentClass(trainerId)].name; else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER) toCpy = gTrainerClasses[GetTrainerTowerOpponentClass()].name; else if (gBattleTypeFlags & BATTLE_TYPE_EREADER_TRAINER) @@ -2598,7 +2650,12 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst, u32 dstSize) toCpy = BattleStringGetPlayerName(text, GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)); break; case B_TXT_TRAINER1_LOSE_TEXT: // trainerA lose text - if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER) + if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER) + { + CopyFrontierTrainerText(FRONTIER_PLAYER_WON_TEXT, TRAINER_BATTLE_PARAM.opponentA); + toCpy = gStringVar4; + } + else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER) { GetTrainerTowerOpponentLoseText(gStringVar4, 0); toCpy = gStringVar4; @@ -2609,7 +2666,12 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst, u32 dstSize) } break; case B_TXT_TRAINER1_WIN_TEXT: // trainerA win text - if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER) + if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER) + { + CopyFrontierTrainerText(FRONTIER_PLAYER_LOST_TEXT, TRAINER_BATTLE_PARAM.opponentA); + toCpy = gStringVar4; + } + else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER) { GetTrainerTowerOpponentWinText(gStringVar4, 0); toCpy = gStringVar4; @@ -2620,7 +2682,12 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst, u32 dstSize) } break; case B_TXT_TRAINER2_LOSE_TEXT: - if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER) + if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER) + { + CopyFrontierTrainerText(FRONTIER_PLAYER_WON_TEXT, TRAINER_BATTLE_PARAM.opponentB); + toCpy = gStringVar4; + } + else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER) { GetTrainerTowerOpponentLoseText(gStringVar4, 1); toCpy = gStringVar4; @@ -2631,7 +2698,11 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst, u32 dstSize) } break; case B_TXT_TRAINER2_WIN_TEXT: - if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER) + if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER) + { + CopyFrontierTrainerText(FRONTIER_PLAYER_LOST_TEXT, TRAINER_BATTLE_PARAM.opponentB); + toCpy = gStringVar4; + } else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER) { GetTrainerTowerOpponentWinText(gStringVar4, 1); toCpy = gStringVar4; diff --git a/src/battle_move_resolution.c b/src/battle_move_resolution.c index 9c7a46989..49595f5e3 100644 --- a/src/battle_move_resolution.c +++ b/src/battle_move_resolution.c @@ -1,5 +1,6 @@ #include "global.h" #include "battle.h" +#include "battle_environment.h" #include "battle_hold_effects.h" #include "battle_ai_util.h" #include "battle_util.h" @@ -13,33 +14,1677 @@ static void ValidateBattlers(void); static enum Move GetOriginallyUsedMove(enum Move chosenMove); -static void SetSameMoveTurnValues(u32 moveEffect); +static void SetSameMoveTurnValues(enum BattleMoveEffects moveEffect); static void TryClearChargeVolatile(enum Type moveType); -static inline bool32 IsBattlerUsingBeakBlast(u32 battler); +static inline bool32 IsBattlerUsingBeakBlast(enum BattlerId battler); +static void RequestNonVolatileChangee(enum BattlerId battlerAtk); +static bool32 CanBattlerBounceBackMove(struct BattleContext *ctx); +static bool32 TryMagicBounce(struct BattleContext *ctx); +static bool32 TryMagicCoat(struct BattleContext *ctx); +static bool32 TryActivatePowderStatus(enum Move move); +static void CalculateMagnitudeDamage(void); +// Submoves +static enum Move GetMirrorMoveMove(void); +static enum Move GetMetronomeMove(void); +static enum Move GetAssistMove(void); +static enum Move GetSleepTalkMove(void); +static enum Move GetCopycatMove(void); +static enum Move GetMeFirstMove(void); + +// ============== // Attackcanceler +// ============== -// ---- -// TODO -// ---- +static enum CancelerResult CancelerClearFlags(struct BattleContext *ctx) +{ + gBattleMons[ctx->battlerAtk].volatiles.grudge = FALSE; + gBattleMons[ctx->battlerAtk].volatiles.glaiveRush = FALSE; + gBattleStruct->eventState.atkCancelerBattler = 0; + return CANCELER_RESULT_SUCCESS; +} +static bool32 TryFormChangeBeforeMove(void) +{ + enum Ability ability = GetBattlerAbility(gBattlerAttacker); + + if (TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_BEFORE_MOVE, ability) + || TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_BEFORE_MOVE_CATEGORY, ability)) + { + gBattleScripting.battler = gBattlerAttacker; + BattleScriptCall(BattleScript_BattlerFormChange); + return TRUE; + } + return FALSE; +} + +static enum CancelerResult CancelerStanceChangeOne(struct BattleContext *ctx) +{ + if (B_STANCE_CHANGE_FAIL < GEN_7 && ctx->chosenMove == ctx->move && TryFormChangeBeforeMove()) + return CANCELER_RESULT_BREAK; + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerSkyDrop(struct BattleContext *ctx) +{ + // If Pokemon is being held in Sky Drop + if (gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable == STATE_SKY_DROP) + { + gBattlescriptCurrInstr = BattleScript_MoveEnd; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerRecharge(struct BattleContext *ctx) +{ + if (gBattleMons[ctx->battlerAtk].volatiles.rechargeTimer > 0) + { + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_MoveUsedMustRecharge; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerChillyReception(struct BattleContext *ctx) +{ + if (GetMoveEffect(ctx->move) == EFFECT_WEATHER_AND_SWITCH) + { + BattleScriptCall(BattleScript_ChillyReceptionMessage); + return CANCELER_RESULT_BREAK; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerAsleepOrFrozen(struct BattleContext *ctx) +{ + enum CancelerResult result = CANCELER_RESULT_BREAK; + + if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP) + { + if (UproarWakeUpCheck(ctx->battlerAtk)) + { + TryDeactivateSleepClause(GetBattlerSide(ctx->battlerAtk), gBattlerPartyIndexes[ctx->battlerAtk]); + gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_SLEEP; + gBattleMons[ctx->battlerAtk].volatiles.nightmare = FALSE; + gEffectBattler = ctx->battlerAtk; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WOKE_UP_UPROAR; + result = CANCELER_RESULT_BREAK; + BattleScriptCall(BattleScript_MoveUsedWokeUp); + } + else + { + u32 toSub; + if (IsAbilityAndRecord(ctx->battlerAtk, ctx->abilityAtk, ABILITY_EARLY_BIRD)) + toSub = 2; + else + toSub = 1; + + if ((gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP) < toSub) + gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_SLEEP; + else + gBattleMons[ctx->battlerAtk].status1 -= toSub; + + if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP) + { + enum BattleMoveEffects moveEffect = GetMoveEffect(ctx->move); + if (moveEffect == EFFECT_SNORE) + { + BattleScriptCall(BattleScript_BeforeSnoreMessage); + result = CANCELER_RESULT_BREAK; + } + else if (moveEffect == EFFECT_SLEEP_TALK) + { + result = CANCELER_RESULT_BREAK; + } + else + { + gBattlescriptCurrInstr = BattleScript_MoveUsedIsAsleep; + result = CANCELER_RESULT_FAILURE; + } + } + else + { + TryDeactivateSleepClause(GetBattlerSide(ctx->battlerAtk), gBattlerPartyIndexes[ctx->battlerAtk]); + gBattleMons[ctx->battlerAtk].volatiles.nightmare = FALSE; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WOKE_UP; + result = CANCELER_RESULT_BREAK; + BattleScriptCall(BattleScript_MoveUsedWokeUp); + } + } + RequestNonVolatileChangee(ctx->battlerAtk); + } + else if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_FREEZE && !MoveThawsUser(ctx->move)) + { + if (!RandomPercentage(RNG_FROZEN, 20)) + { + result = CANCELER_RESULT_FAILURE; + gBattlescriptCurrInstr = BattleScript_MoveUsedIsFrozen; + } + else // unfreeze + { + gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_FREEZE; + result = CANCELER_RESULT_BREAK; + BattleScriptCall(BattleScript_MoveUsedUnfroze); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DEFROSTED; + } + RequestNonVolatileChangee(ctx->battlerAtk); + } + + return result; +} + +static enum CancelerResult CancelerObedience(struct BattleContext *ctx) +{ + if (!gBattleMons[ctx->battlerAtk].volatiles.multipleTurns) + { + enum Obedience obedienceResult = GetAttackerObedienceForAction(); + switch (obedienceResult) + { + case OBEYS: + return CANCELER_RESULT_SUCCESS; + case DISOBEYS_LOAFS: + // Randomly select, then print a disobedient string + // B_MSG_LOAFING, B_MSG_WONT_OBEY, B_MSG_TURNED_AWAY, or B_MSG_PRETEND_NOT_NOTICE + gBattleCommunication[MULTISTRING_CHOOSER] = MOD(Random(), NUM_LOAF_STRINGS); + gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround; + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; + return CANCELER_RESULT_FAILURE; + case DISOBEYS_HITS_SELF: + gBattlerTarget = ctx->battlerAtk; + struct BattleContext dmgCtx = {0}; + dmgCtx.battlerAtk = dmgCtx.battlerDef = ctx->battlerAtk; + dmgCtx.move = dmgCtx.chosenMove = MOVE_NONE; + dmgCtx.moveType = TYPE_MYSTERY; + dmgCtx.isCrit = FALSE; + dmgCtx.randomFactor = FALSE; + dmgCtx.updateFlags = TRUE; + dmgCtx.isSelfInflicted = TRUE; + dmgCtx.fixedBasePower = 40; + gBattleStruct->moveDamage[ctx->battlerAtk] = CalculateMoveDamage(&dmgCtx); + gBattlescriptCurrInstr = BattleScript_IgnoresAndHitsItself; + return CANCELER_RESULT_FAILURE; // Move doesn't fail but mon hits itself + case DISOBEYS_FALL_ASLEEP: + if (IsSleepClauseEnabled()) + gBattleStruct->battlerState[ctx->battlerAtk].sleepClauseEffectExempt = TRUE; + gBattlescriptCurrInstr = BattleScript_IgnoresAndFallsAsleep; + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; + return CANCELER_RESULT_FAILURE; + break; + case DISOBEYS_WHILE_ASLEEP: + gBattlescriptCurrInstr = BattleScript_IgnoresWhileAsleep; + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; + return CANCELER_RESULT_FAILURE; + case DISOBEYS_RANDOM_MOVE: + gCurrentMove = gCalledMove = gBattleMons[ctx->battlerAtk].moves[gCurrMovePos]; + BattleScriptCall(BattleScript_IgnoresAndUsesRandomMove); + gBattlerTarget = GetBattleMoveTarget(gCalledMove, TARGET_NONE); + return CANCELER_RESULT_BREAK; + } + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerPowerPoints(struct BattleContext *ctx) +{ + if (gBattleMons[ctx->battlerAtk].pp[gCurrMovePos] == 0 + && ctx->move != MOVE_STRUGGLE + && !gSpecialStatuses[ctx->battlerAtk].dancerUsedMove + && !gBattleMons[ctx->battlerAtk].volatiles.multipleTurns) + { + gBattlescriptCurrInstr = BattleScript_NoPPForMove; + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; + return CANCELER_RESULT_FAILURE; + } + + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerTruant(struct BattleContext *ctx) +{ + if (GetBattlerAbility(ctx->battlerAtk) == ABILITY_TRUANT && gBattleMons[ctx->battlerAtk].volatiles.truantCounter) + { + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LOAFING; + gBattlerAbility = ctx->battlerAtk; + gBattlescriptCurrInstr = BattleScript_TruantLoafingAround; + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerFocus(struct BattleContext *ctx) +{ + u32 focusPunchFailureConfig = GetConfig(CONFIG_FOCUS_PUNCH_FAILURE); + + // In Gens 3-4, only check if is using Focus Punch. + // In Gens 5-6, only check if the chosen move is Focus Punch. + // In Gens 7+, check if chose and is using Focus Punch. + if ((gProtectStructs[ctx->battlerAtk].physicalDmg || gProtectStructs[ctx->battlerAtk].specialDmg) + && (focusPunchFailureConfig < GEN_5 || GetMoveEffect(gChosenMoveByBattler[ctx->battlerAtk]) == EFFECT_FOCUS_PUNCH) + && (focusPunchFailureConfig == GEN_5 || focusPunchFailureConfig == GEN_6 || GetMoveEffect(ctx->move) == EFFECT_FOCUS_PUNCH) + && !gProtectStructs[ctx->battlerAtk].survivedOHKO) + { + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_FocusPunchLostFocus; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerFocusPreGen5(struct BattleContext *ctx) +{ + if (GetConfig(CONFIG_FOCUS_PUNCH_FAILURE) < GEN_5) + return CancelerFocus(ctx); + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerFocusGen5(struct BattleContext *ctx) +{ + if (GetConfig(CONFIG_FOCUS_PUNCH_FAILURE) >= GEN_5) + return CancelerFocus(ctx); + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerFlinch(struct BattleContext *ctx) +{ + if (gBattleMons[ctx->battlerAtk].volatiles.flinched) + { + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_MoveUsedFlinched; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerDisabled(struct BattleContext *ctx) +{ + if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE + && gBattleMons[ctx->battlerAtk].volatiles.disabledMove == ctx->move + && gBattleMons[ctx->battlerAtk].volatiles.disabledMove != MOVE_NONE) + { + gBattleScripting.battler = ctx->battlerAtk; + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_MoveUsedIsDisabled; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerVolatileBlocked(struct BattleContext *ctx) +{ + if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE + && gBattleMons[ctx->battlerAtk].volatiles.healBlock + && IsHealBlockPreventingMove(ctx->battlerAtk, ctx->move)) + { + gBattleScripting.battler = ctx->battlerAtk; + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_MoveUsedHealBlockPrevents; + return CANCELER_RESULT_FAILURE; + } + else if (gFieldStatuses & STATUS_FIELD_GRAVITY && IsGravityPreventingMove(ctx->move)) + { + gBattleScripting.battler = ctx->battlerAtk; + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_MoveUsedGravityPrevents; + return CANCELER_RESULT_FAILURE; + } + else if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE && gBattleMons[ctx->battlerAtk].volatiles.throatChopTimer > 0 && IsSoundMove(ctx->move)) + { + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_MoveUsedIsThroatChopPrevented; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerTaunted(struct BattleContext *ctx) +{ + if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE && gBattleMons[ctx->battlerAtk].volatiles.tauntTimer && IsBattleMoveStatus(ctx->move)) + { + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_MoveUsedIsTaunted; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerImprisoned(struct BattleContext *ctx) +{ + if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE && GetImprisonedMovesCount(ctx->battlerAtk, ctx->move)) + { + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_MoveUsedIsImprisoned; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerConfused(struct BattleContext *ctx) +{ + if (gBattleMons[ctx->battlerAtk].volatiles.confusionTurns) + { + if (!gBattleMons[ctx->battlerAtk].volatiles.infiniteConfusion) + gBattleMons[ctx->battlerAtk].volatiles.confusionTurns--; + if (gBattleMons[ctx->battlerAtk].volatiles.confusionTurns) + { + // confusion dmg + if (RandomPercentage(RNG_CONFUSION, (GetConfig(CONFIG_CONFUSION_SELF_DMG_CHANCE) >= GEN_7 ? 33 : 50))) + { + gBattleCommunication[MULTISTRING_CHOOSER] = TRUE; + gBattlerTarget = gBattlerAttacker; + struct BattleContext dmgCtx = {0}; + dmgCtx.battlerAtk = dmgCtx.battlerDef = ctx->battlerAtk; + dmgCtx.move = dmgCtx.chosenMove = MOVE_NONE; + dmgCtx.moveType = TYPE_MYSTERY; + dmgCtx.isCrit = FALSE; + dmgCtx.randomFactor = FALSE; + dmgCtx.updateFlags = TRUE; + dmgCtx.isSelfInflicted = TRUE; + dmgCtx.fixedBasePower = 40; + gBattleStruct->passiveHpUpdate[ctx->battlerAtk] = CalculateMoveDamage(&dmgCtx); + gBattlescriptCurrInstr = BattleScript_MoveUsedIsConfused; + return CANCELER_RESULT_FAILURE; + } + else + { + gBattleCommunication[MULTISTRING_CHOOSER] = FALSE; + BattleScriptCall(BattleScript_MoveUsedIsConfused); + return CANCELER_RESULT_BREAK; + } + } + else // snapped out of confusion + { + BattleScriptCall(BattleScript_MoveUsedIsConfusedNoMore); + return CANCELER_RESULT_BREAK; + } + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerGhost(struct BattleContext *ctx) // GHOST in pokemon tower +{ + if (IsGhostBattleWithoutScope()) + { + if (GetBattlerSide(ctx->battlerAtk) == B_SIDE_PLAYER) + gBattlescriptCurrInstr = BattleScript_TooScaredToMove; + else + gBattlescriptCurrInstr = BattleScript_GhostGetOutGetOut; + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerParalyzed(struct BattleContext *ctx) +{ + if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_PARALYSIS + && !(B_MAGIC_GUARD == GEN_4 && IsAbilityAndRecord(ctx->battlerAtk, ctx->abilityAtk, ABILITY_MAGIC_GUARD)) + && !RandomPercentage(RNG_PARALYSIS, 75)) + { + CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_MoveUsedIsParalyzed; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerInfatuation(struct BattleContext *ctx) +{ + if (gBattleMons[ctx->battlerAtk].volatiles.infatuation) + { + gBattleScripting.battler = gBattleMons[ctx->battlerAtk].volatiles.infatuation - 1; + if (!RandomPercentage(RNG_INFATUATION, 50)) + { + BattleScriptCall(BattleScript_MoveUsedIsInLove); + return CANCELER_RESULT_BREAK; + } + else + { + BattleScriptPush(BattleScript_MoveUsedIsInLoveCantAttack); + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_MoveUsedIsInLove; + return CANCELER_RESULT_FAILURE; + } + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerBide(struct BattleContext *ctx) +{ + if (gBattleMons[ctx->battlerAtk].volatiles.bideTurns) + { + if (--gBattleMons[ctx->battlerAtk].volatiles.bideTurns) + { + gBattlescriptCurrInstr = BattleScript_BideStoringEnergy; + } + else + { + // This is removed in FRLG and Emerald for some reason + //gBattleMons[gBattlerAttacker].volatiles.multipleTurns = FALSE; + if (gBideDmg[ctx->battlerAtk]) + { + gCurrentMove = MOVE_BIDE; + gBattlerTarget = gBideTarget[ctx->battlerAtk]; + if (!IsBattlerAlive(ctx->battlerDef)) + gBattlerTarget = GetBattleMoveTarget(MOVE_BIDE, TARGET_SELECTED); + gBattlescriptCurrInstr = BattleScript_BideAttack; + return CANCELER_RESULT_BREAK; // Jumps to a different script but no failure + } + else + { + gBattlescriptCurrInstr = BattleScript_BideNoEnergyToAttack; + return CANCELER_RESULT_FAILURE; + } + } + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerZMoves(struct BattleContext *ctx) +{ + if (GetActiveGimmick(ctx->battlerAtk) == GIMMICK_Z_MOVE) + { + // attacker has a queued z move + RecordItemEffectBattle(ctx->battlerAtk, HOLD_EFFECT_Z_CRYSTAL); + SetGimmickAsActivated(ctx->battlerAtk, GIMMICK_Z_MOVE); + + gBattleScripting.battler = ctx->battlerAtk; + if (GetMoveCategory(ctx->move) == DAMAGE_CATEGORY_STATUS) + BattleScriptCall(BattleScript_ZMoveActivateStatus); + else + BattleScriptCall(BattleScript_ZMoveActivateDamaging); + + return CANCELER_RESULT_BREAK; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerChoiceLock(struct BattleContext *ctx) +{ + enum Move *choicedMoveAtk = &gBattleStruct->choicedMove[ctx->battlerAtk]; + enum HoldEffect holdEffect = GetBattlerHoldEffect(ctx->battlerAtk); + + if (gChosenMove != MOVE_STRUGGLE + && (*choicedMoveAtk == MOVE_NONE || *choicedMoveAtk == MOVE_UNAVAILABLE) + && (IsHoldEffectChoice(holdEffect) || ctx->abilityAtk == ABILITY_GORILLA_TACTICS)) + *choicedMoveAtk = gChosenMove; + + u32 moveIndex; + for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) + { + if (gBattleMons[ctx->battlerAtk].moves[moveIndex] == *choicedMoveAtk) + break; + } + + if (moveIndex == MAX_MON_MOVES) + *choicedMoveAtk = MOVE_NONE; + + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerCallSubmove(struct BattleContext *ctx) +{ + bool32 noEffect = FALSE; + enum Move calledMove = MOVE_NONE; + const u8 *battleScript = NULL; + battleScript = BattleScript_SubmoveAttackstring; + + switch(GetMoveEffect(ctx->move)) + { + case EFFECT_MIRROR_MOVE: + calledMove = GetMirrorMoveMove(); + break; + case EFFECT_METRONOME: + calledMove = GetMetronomeMove(); + battleScript = BattleScript_MetronomeAttackstring; + break; + case EFFECT_ASSIST: + calledMove = GetAssistMove(); + break; + case EFFECT_NATURE_POWER: + calledMove = GetNaturePowerMove(); + battleScript = BattleScript_NaturePowerAttackstring; + break; + case EFFECT_SLEEP_TALK: + calledMove = GetSleepTalkMove(); + battleScript = BattleScript_SleepTalkAttackstring; + break; + case EFFECT_COPYCAT: + calledMove = GetCopycatMove(); + break; + case EFFECT_ME_FIRST: + calledMove = GetMeFirstMove(); + break; + default: + noEffect = TRUE; + break; + } + + if (noEffect) + { + gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT; + return CANCELER_RESULT_SUCCESS; + } + + if (calledMove != MOVE_NONE) + { + if (GetActiveGimmick(ctx->battlerAtk) == GIMMICK_Z_MOVE && !IsBattleMoveStatus(calledMove)) + calledMove = GetTypeBasedZMove(calledMove); + if (GetMoveEffect(ctx->move) == EFFECT_COPYCAT && IsMaxMove(calledMove)) + calledMove = gBattleStruct->dynamax.lastUsedBaseMove; + + gBattleStruct->submoveAnnouncement = SUBMOVE_SUCCESS; + gCalledMove = calledMove; + BattleScriptCall(battleScript); + return CANCELER_RESULT_BREAK; + } + + gBattleStruct->submoveAnnouncement = SUBMOVE_FAILURE; + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerThaw(struct BattleContext *ctx) +{ + enum CancelerResult result = CANCELER_RESULT_BREAK; + + if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_FREEZE) + { + if (!(IsMoveEffectRemoveSpeciesType(ctx->move, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_FIRE) && !IS_BATTLER_OF_TYPE(ctx->battlerAtk, TYPE_FIRE))) + { + gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_FREEZE; + result = CANCELER_RESULT_BREAK; + BattleScriptCall(BattleScript_MoveUsedUnfroze); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DEFROSTED_BY_MOVE; + } + else + { + result = CANCELER_RESULT_FAILURE; + } + RequestNonVolatileChangee(ctx->battlerAtk); + } + else if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_FROSTBITE && MoveThawsUser(ctx->move)) + { + if (!(IsMoveEffectRemoveSpeciesType(ctx->move, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_FIRE) && !IS_BATTLER_OF_TYPE(ctx->battlerAtk, TYPE_FIRE))) + { + gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_FROSTBITE; + result = CANCELER_RESULT_BREAK; + BattleScriptCall(BattleScript_MoveUsedUnfrostbite); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FROSTBITE_HEALED_BY_MOVE; + } + else + { + result = CANCELER_RESULT_FAILURE; + } + RequestNonVolatileChangee(ctx->battlerAtk); + } + return result; +} + +static enum CancelerResult CancelerStanceChangeTwo(struct BattleContext *ctx) +{ + if (B_STANCE_CHANGE_FAIL >= GEN_7 && gChosenMove == ctx->move && TryFormChangeBeforeMove()) + return CANCELER_RESULT_BREAK; + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerAttackstring(struct BattleContext *ctx) +{ + BattleScriptCall(BattleScript_Attackstring); + if (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove) + { + gBattleMons[gBattlerAttacker].volatiles.usedMoves |= 1u << gCurrMovePos; + gBattleStruct->battlerState[gBattlerAttacker].lastMoveTarget = gBattlerTarget; + gLastPrintedMoves[gBattlerAttacker] = gChosenMove; + RecordKnownMove(gBattlerAttacker, gChosenMove); + } + return CANCELER_RESULT_BREAK; +} + +static enum CancelerResult CancelerPPDeduction(struct BattleContext *ctx) +{ + if (gBattleMons[ctx->battlerAtk].volatiles.multipleTurns + || gSpecialStatuses[ctx->battlerAtk].dancerUsedMove + || ctx->move == MOVE_STRUGGLE) + return CANCELER_RESULT_SUCCESS; + + s32 ppToDeduct = 1; + enum MoveTarget moveTarget = GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move); + u32 movePosition = gCurrMovePos; + + if (gBattleStruct->submoveAnnouncement == SUBMOVE_SUCCESS) + movePosition = gChosenMovePos; + + if (IsSpreadMove(moveTarget) + || moveTarget == TARGET_ALL_BATTLERS + || moveTarget == TARGET_FIELD + || MoveForcesPressure(ctx->move)) + { + for (u32 i = 0; i < gBattlersCount; i++) + { + if (!IsBattlerAlly(i, ctx->battlerAtk) && IsBattlerAlive(i)) + ppToDeduct += (GetBattlerAbility(i) == ABILITY_PRESSURE); + } + } + else if (moveTarget != TARGET_OPPONENTS_FIELD) + { + if (ctx->battlerAtk != ctx->battlerDef && GetBattlerAbility(ctx->battlerDef) == ABILITY_PRESSURE) + ppToDeduct++; + } + + // For item Metronome, echoed voice + if (ctx->move != gLastResultingMoves[ctx->battlerAtk] || gBattleStruct->unableToUseMove) + gBattleMons[ctx->battlerAtk].volatiles.metronomeItemCounter = 0; + + if (gBattleMons[ctx->battlerAtk].pp[movePosition] > ppToDeduct) + gBattleMons[ctx->battlerAtk].pp[movePosition] -= ppToDeduct; + else + gBattleMons[ctx->battlerAtk].pp[movePosition] = 0; + + if (MOVE_IS_PERMANENT(ctx->battlerAtk, movePosition)) + { + BtlController_EmitSetMonData( + ctx->battlerAtk, + B_COMM_TO_CONTROLLER, + REQUEST_PPMOVE1_BATTLE + movePosition, + 0, + sizeof(gBattleMons[ctx->battlerAtk].pp[movePosition]), + &gBattleMons[ctx->battlerAtk].pp[movePosition]); + MarkBattlerForControllerExec(ctx->battlerAtk); + } + + if (gBattleStruct->submoveAnnouncement != SUBMOVE_NO_EFFECT) + { + if (gBattleStruct->submoveAnnouncement == SUBMOVE_FAILURE) + { + gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT; + gBattlescriptCurrInstr = BattleScript_ButItFailed; + return CANCELER_RESULT_FAILURE; + } + else if (CancelerVolatileBlocked(ctx) == CANCELER_RESULT_FAILURE) // Check Gravity/Heal Block/Throat Chop for Submove + { + gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT; + return CANCELER_RESULT_FAILURE; + } + else + { + gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT; + gBattlerTarget = GetBattleMoveTarget(ctx->move, TARGET_NONE); + gBattleScripting.animTurn = 0; + gBattleScripting.animTargetsHit = 0; + + // Possibly better to just move type setting and redirection to attackcanceler as a new case at this point + SetTypeBeforeUsingMove(ctx->move, ctx->battlerAtk); + DetermineTarget(moveTarget, FALSE); + ClearDamageCalcResults(); + gBattlescriptCurrInstr = GetMoveBattleScript(ctx->move); + return CANCELER_RESULT_BREAK; + } + } + + return CANCELER_RESULT_SUCCESS; +} + +// We don't have clear data on where this belongs to but I assume it should at least be checked before Protean +static enum CancelerResult CancelerSkyBattle(struct BattleContext *ctx) +{ + if (gBattleStruct->isSkyBattle && IsMoveSkyBattleBanned(gCurrentMove)) + { + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_ButItFailed; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerWeatherPrimal(struct BattleContext *ctx) +{ + enum CancelerResult result = CANCELER_RESULT_SUCCESS; + + if (GetMovePower(ctx->move) > 0 && HasWeatherEffect()) + { + enum Type moveType = GetBattleMoveType(ctx->move); + if (moveType == TYPE_FIRE && gBattleWeather & B_WEATHER_RAIN_PRIMAL && (GetConfig(CONFIG_POWDER_STATUS_HEAVY_RAIN) >= GEN_7 || !TryActivatePowderStatus(ctx->move))) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_FIZZLED_BY_RAIN; + result = CANCELER_RESULT_FAILURE; + } + else if (moveType == TYPE_WATER && gBattleWeather & B_WEATHER_SUN_PRIMAL) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_EVAPORATED_IN_SUN; + result = CANCELER_RESULT_FAILURE; + } + if (result == CANCELER_RESULT_FAILURE) + { + gProtectStructs[ctx->battlerAtk].chargingTurn = FALSE; + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove; + } + } + + return result; +} + +static enum CancelerResult CancelerMoveFailure(struct BattleContext *ctx) +{ + const u8 *battleScript = NULL; + + switch (GetMoveEffect(ctx->move)) + { + case EFFECT_FAIL_IF_NOT_ARG_TYPE: + if (!IS_BATTLER_OF_TYPE(ctx->battlerAtk, GetMoveArgType(ctx->move))) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_AURA_WHEEL: + if (gBattleMons[ctx->battlerAtk].species != SPECIES_MORPEKO_FULL_BELLY + && gBattleMons[ctx->battlerAtk].species != SPECIES_MORPEKO_HANGRY) + battleScript = BattleScript_PokemonCantUseTheMove; + break; + case EFFECT_AURORA_VEIL: + if (!(gBattleWeather & B_WEATHER_ICY_ANY && HasWeatherEffect())) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_CLANGOROUS_SOUL: + if (gBattleMons[ctx->battlerAtk].hp <= max(1, GetNonDynamaxMaxHP(ctx->battlerAtk) / 3)) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_REFLECT_DAMAGE: + { + enum DamageCategory reflectCategory = GetReflectDamageMoveDamageCategory(ctx->battlerAtk, ctx->move); + if (IsBattlerAlly(ctx->battlerAtk, ctx->battlerDef)) + battleScript = BattleScript_ButItFailed; + // Counter / Metal Burst and took physical damage + else if (reflectCategory == DAMAGE_CATEGORY_PHYSICAL + && gProtectStructs[ctx->battlerAtk].physicalDmg > 0 + && (GetConfig(CONFIG_COUNTER_TRY_HIT_PARTNER) >= GEN_5 || gBattleMons[gProtectStructs[ctx->battlerAtk].physicalBattlerId].hp)) + break; + // Mirror Coat / Metal Burst and took special damage + else if (reflectCategory == DAMAGE_CATEGORY_SPECIAL + && gProtectStructs[ctx->battlerAtk].specialDmg > 0 + && (GetConfig(CONFIG_COUNTER_TRY_HIT_PARTNER) >= GEN_5 || gBattleMons[gProtectStructs[ctx->battlerAtk].specialBattlerId].hp)) + break; + else + battleScript = BattleScript_ButItFailed; + break; + } + case EFFECT_DESTINY_BOND: + if (DoesDestinyBondFail(ctx->battlerAtk)) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_FIRST_TURN_ONLY: + if (!gBattleStruct->battlerState[ctx->battlerAtk].isFirstTurn || gSpecialStatuses[ctx->battlerAtk].instructedChosenTarget) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_MAT_BLOCK: + if (!gBattleStruct->battlerState[ctx->battlerAtk].isFirstTurn || gSpecialStatuses[ctx->battlerAtk].instructedChosenTarget) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_FLING: + if (!CanFling(ctx->battlerAtk)) + battleScript = BattleScript_ButItFailed; + else if (!IsBattlerAlive(ctx->battlerDef)) // Edge case for removing a mon's item when there is no target available after using Fling. + battleScript = BattleScript_FlingFailConsumeItem; + break; + case EFFECT_FOLLOW_ME: + if (B_UPDATED_MOVE_DATA >= GEN_8 && !(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_FUTURE_SIGHT: + if (gBattleStruct->futureSight[ctx->battlerDef].counter > 0) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_LAST_RESORT: + if (!CanUseLastResort(ctx->battlerAtk)) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_NO_RETREAT: + if (gBattleMons[ctx->battlerDef].volatiles.noRetreat) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_POLTERGEIST: + if (gBattleMons[ctx->battlerDef].item == ITEM_NONE + || gFieldStatuses & STATUS_FIELD_MAGIC_ROOM + || ctx->abilityDef == ABILITY_KLUTZ) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_PROTECT: + case EFFECT_ENDURE: + TryResetConsecutiveUseCounter(gBattlerAttacker); + if (IsLastMonToMove(ctx->battlerAtk)) + { + battleScript = BattleScript_ButItFailed; + } + else + { + enum ProtectMethod protectMethod = GetMoveProtectMethod(ctx->move); + bool32 canUseProtectSecondTime = CanUseMoveConsecutively(ctx->battlerAtk); + bool32 canUseWideGuard = (GetConfig(CONFIG_WIDE_GUARD) >= GEN_6 && protectMethod == PROTECT_WIDE_GUARD); + bool32 canUseQuickGuard = (GetConfig(CONFIG_QUICK_GUARD) >= GEN_6 && protectMethod == PROTECT_QUICK_GUARD); + + if (!canUseProtectSecondTime + && !canUseWideGuard + && !canUseQuickGuard + && protectMethod != PROTECT_CRAFTY_SHIELD) + battleScript = BattleScript_ButItFailed; + } + if (battleScript != NULL) + { + gBattleMons[gBattlerAttacker].volatiles.consecutiveMoveUses = 0; + gBattleStruct->battlerState[gBattlerAttacker].stompingTantrumTimer = 2; + } + break; + case EFFECT_REST: + if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP + || ctx->abilityAtk == ABILITY_COMATOSE) + battleScript = BattleScript_RestIsAlreadyAsleep; + else if (gBattleMons[ctx->battlerAtk].hp == gBattleMons[ctx->battlerAtk].maxHP) + battleScript = BattleScript_AlreadyAtFullHp; + else if (ctx->abilityAtk == ABILITY_INSOMNIA + || ctx->abilityAtk == ABILITY_VITAL_SPIRIT + || ctx->abilityAtk == ABILITY_PURIFYING_SALT) + battleScript = BattleScript_InsomniaProtects; + break; + case EFFECT_SUCKER_PUNCH: + if (HasBattlerActedThisTurn(ctx->battlerDef) + || (IsBattleMoveStatus(GetBattlerChosenMove(ctx->battlerDef)) && !gProtectStructs[ctx->battlerDef].noValidMoves)) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_UPPER_HAND: + { + s32 prio = GetChosenMovePriority(ctx->battlerDef, GetBattlerAbility(ctx->battlerDef)); + if (prio < 1 || prio > 3 // Fails if priority is less than 1 or greater than 3, if target already moved, or if using a status + || HasBattlerActedThisTurn(ctx->battlerDef) + || gChosenMoveByBattler[ctx->battlerDef] == MOVE_NONE + || IsBattleMoveStatus(gChosenMoveByBattler[ctx->battlerDef])) + battleScript = BattleScript_ButItFailed; + break; + } + case EFFECT_SNORE: + if (!(gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP) + && ctx->abilityAtk != ABILITY_COMATOSE) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_STEEL_ROLLER: + if (!(gFieldStatuses & STATUS_FIELD_TERRAIN_ANY)) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_STOCKPILE: + if (gBattleMons[ctx->battlerAtk].volatiles.stockpileCounter >= 3) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_STUFF_CHEEKS: + if (GetItemPocket(gBattleMons[ctx->battlerAtk].item) != POCKET_BERRIES) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_SWALLOW: + case EFFECT_SPIT_UP: + if (gBattleMons[ctx->battlerAtk].volatiles.stockpileCounter == 0 && !gBattleStruct->snatchedMoveIsUsed) + battleScript = BattleScript_ButItFailed; + break; + case EFFECT_TELEPORT: + // TODO: follow up: Can't make sense of teleport logic + break; + case EFFECT_LOW_KICK: + case EFFECT_HEAT_CRASH: + if (GetActiveGimmick(ctx->battlerDef) == GIMMICK_DYNAMAX) + battleScript = BattleScript_MoveBlockedByDynamax; + break; + case EFFECT_NATURAL_GIFT: + if (GetItemPocket(gBattleMons[ctx->battlerAtk].item) != POCKET_BERRIES + || gFieldStatuses & STATUS_FIELD_MAGIC_ROOM + || ctx->abilityAtk == ABILITY_KLUTZ + || gBattleMons[ctx->battlerAtk].volatiles.embargo) + battleScript = BattleScript_ButItFailed; + break; + default: + break; + } + + if (battleScript != NULL) + { + gBattlescriptCurrInstr = battleScript; + return CANCELER_RESULT_FAILURE; + } + + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerPowderStatus(struct BattleContext *ctx) +{ + if (TryActivatePowderStatus(ctx->move)) + { + if (!IsAbilityAndRecord(ctx->battlerAtk, ctx->abilityAtk, ABILITY_MAGIC_GUARD)) + SetPassiveDamageAmount(ctx->battlerAtk, GetNonDynamaxMaxHP(ctx->battlerAtk) / 4); + + // This might be incorrect + if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE + || HasTrainerUsedGimmick(ctx->battlerAtk, GIMMICK_Z_MOVE)) + gBattlescriptCurrInstr = BattleScript_MoveUsedPowder; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +bool32 IsDazzlingAbility(enum Ability ability) +{ + switch (ability) + { + case ABILITY_DAZZLING: return TRUE; + case ABILITY_QUEENLY_MAJESTY: return TRUE; + case ABILITY_ARMOR_TAIL: return TRUE; + default: break; + } + return FALSE; +} + +static enum CancelerResult CancelerPriorityBlock(struct BattleContext *ctx) +{ + bool32 effect = FALSE; + s32 priority = GetChosenMovePriority(ctx->battlerAtk, ctx->abilityAtk); + + if (priority <= 0) + return CANCELER_RESULT_SUCCESS; + + enum BattlerId battler; + enum Ability ability = ABILITY_NONE; // ability of battler who is blocking + bool32 isSpreadMove = IsSpreadMove(GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move)); + for (battler = 0; battler < gBattlersCount; battler++) + { + if (!IsBattlerAlive(battler) || IsBattlerAlly(ctx->battlerAtk, battler)) + continue; + if (!isSpreadMove && !IsBattlerAlly(battler, ctx->battlerDef)) + continue; + + ability = GetBattlerAbility(battler); + if (IsDazzlingAbility(ability)) + { + effect = TRUE; + break; + } + } + + if (effect) + { + gLastUsedAbility = ability; + RecordAbilityBattle(battler, ability); + gBattleScripting.battler = gBattlerAbility = battler; + gBattlescriptCurrInstr = BattleScript_DazzlingProtected; + return CANCELER_RESULT_FAILURE; + } + + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerProtean(struct BattleContext *ctx) +{ + enum Type moveType = GetBattleMoveType(ctx->move); + if (ProteanTryChangeType(ctx->battlerAtk, ctx->abilityAtk, ctx->move, moveType)) + { + if (GetConfig(CONFIG_PROTEAN_LIBERO) >= GEN_9) + gBattleMons[ctx->battlerAtk].volatiles.usedProteanLibero = TRUE; + PREPARE_TYPE_BUFFER(gBattleTextBuff1, moveType); + gBattlerAbility = ctx->battlerAtk; + PrepareStringBattle(STRINGID_EMPTYSTRING3, ctx->battlerAtk); + gBattleCommunication[MSG_DISPLAY] = 1; + BattleScriptCall(BattleScript_ProteanActivates); + return CANCELER_RESULT_BREAK; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerExplodingDamp(struct BattleContext *ctx) +{ + u32 dampBattler = IsAbilityOnField(ABILITY_DAMP); + if (dampBattler && IsMoveDampBanned(ctx->move)) + { + gBattleScripting.battler = dampBattler - 1; + gBattlescriptCurrInstr = BattleScript_DampStopsExplosion; + return CANCELER_RESULT_FAILURE; + } + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerExplosion(struct BattleContext *ctx) +{ + // KO user of Explosion; for Final Gambit doesn't happen if target is immune or if it missed + if (IsExplosionMove(ctx->move) + && (GetMoveEffect(ctx->move) != EFFECT_FINAL_GAMBIT || !IsBattlerUnaffectedByMove(ctx->battlerDef))) + { + BattleScriptCall(BattleScript_Explosion); + return CANCELER_RESULT_BREAK; + } + + return CANCELER_RESULT_SUCCESS; +} + +static bool32 CanTwoTurnMoveFireThisTurn(struct BattleContext *ctx) +{ + if (gBattleMoveEffects[GetMoveEffect(ctx->move)].semiInvulnerableEffect + || GetMoveEffect(ctx->move) == EFFECT_GEOMANCY + || !IsBattlerWeatherAffected(ctx->battlerAtk, GetMoveTwoTurnAttackWeather(ctx->move))) + return FALSE; + return TRUE; +} + +static enum CancelerResult CancelerCharging(struct BattleContext *ctx) +{ + if (!gBattleMoveEffects[GetMoveEffect(ctx->move)].twoTurnEffect + || GetMoveEffect(ctx->move) == EFFECT_SKY_DROP) + return CANCELER_RESULT_SUCCESS; + + enum CancelerResult result = CANCELER_RESULT_SUCCESS; + + if (gBattleMons[ctx->battlerAtk].volatiles.multipleTurns) // Second turn + { + gBattleScripting.animTurn = 1; + gBattleScripting.animTargetsHit = 0; + gBattleMons[ctx->battlerAtk].volatiles.multipleTurns = FALSE; + if (gBattleMoveEffects[GetMoveEffect(ctx->move)].semiInvulnerableEffect) + gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable = STATE_NONE; + result = CANCELER_RESULT_SUCCESS; + } + else if (!gProtectStructs[ctx->battlerAtk].chargingTurn) // First turn charge + { + gLockedMoves[ctx->battlerAtk] = ctx->move; + gProtectStructs[ctx->battlerAtk].chargingTurn = TRUE; + if (gBattleMoveEffects[GetMoveEffect(ctx->move)].semiInvulnerableEffect) + gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable = GetMoveTwoTurnAttackStatus(ctx->move); + BattleScriptCall(BattleScript_TwoTurnMoveCharging); + result = CANCELER_RESULT_PAUSE; + } + else // Try move this turn. Otherwise use next turn + { + if (CanTwoTurnMoveFireThisTurn(ctx)) + { + gBattleScripting.animTurn = 1; + gBattleScripting.animTargetsHit = 0; + gProtectStructs[ctx->battlerAtk].chargingTurn = FALSE; + result = CANCELER_RESULT_SUCCESS; + } + else if (ctx->holdEffectAtk == HOLD_EFFECT_POWER_HERB) + { + gBattleScripting.animTurn = 1; + gBattleScripting.animTargetsHit = 0; + gProtectStructs[ctx->battlerAtk].chargingTurn = FALSE; + gLastUsedItem = gBattleMons[ctx->battlerAtk].item; + BattleScriptCall(BattleScript_PowerHerbActivation); + result = CANCELER_RESULT_BREAK; + } + else // Use move next turn + { + gBattleMons[ctx->battlerAtk].volatiles.multipleTurns = TRUE; + gBattlescriptCurrInstr = BattleScript_MoveEnd; + result = CANCELER_RESULT_BREAK; + } + + } + + return result; +} + +static enum CancelerResult CancelerMoveSpecificMessage(struct BattleContext *ctx) +{ + switch (GetMoveEffect(ctx->move)) + { + case EFFECT_MAGNITUDE: + CalculateMagnitudeDamage(); + BattleScriptCall(BattleScript_MagnitudeMessage); + return CANCELER_RESULT_BREAK; + case EFFECT_FICKLE_BEAM: + gBattleStruct->fickleBeamBoosted = RandomPercentage(RNG_FICKLE_BEAM, 30); + if (gBattleStruct->fickleBeamBoosted) + { + BattleScriptCall(BattleScript_FickleBeamMessage); + return CANCELER_RESULT_BREAK; + } + break; + default: + break; + } + + return CANCELER_RESULT_SUCCESS; +} + +static bool32 NoTargetPresent(enum BattlerId battler, enum Move move, enum MoveTarget moveTarget) +{ + switch (moveTarget) + { + case TARGET_USER_AND_ALLY: + return FALSE; // At least user is present + case TARGET_ALLY: + if (!IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker))) // Seems like TARGET_ALLY is retargeting if no ally + return TRUE; + break; + case TARGET_SELECTED: + case TARGET_DEPENDS: + case TARGET_RANDOM: + if (!IsBattlerAlive(gBattlerTarget) && !IsBattlerAlive(GetBattleMoveTarget(move, TARGET_NONE))) + return TRUE; + break; + case TARGET_BOTH: + case TARGET_SMART: + if (!IsBattlerAlive(gBattlerTarget) && !IsBattlerAlive(BATTLE_PARTNER(gBattlerTarget))) + return TRUE; + break; + case TARGET_FOES_AND_ALLY: + if (!IsBattlerAlive(gBattlerTarget) && !IsBattlerAlive(BATTLE_PARTNER(gBattlerTarget)) && !IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker))) + return TRUE; + break; + default: + break; + } + + return FALSE; +} + +static enum CancelerResult CancelerNoTarget(struct BattleContext *ctx) +{ + enum MoveTarget moveTarget = GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move); + + if (NoTargetPresent(gBattlerAttacker, gCurrentMove, moveTarget)) + { + gBattlescriptCurrInstr = BattleScript_ButItFailed; + return CANCELER_RESULT_FAILURE; + } + + if (ctx->battlerAtk == ctx->battlerDef + && moveTarget == TARGET_ALLY + && gProtectStructs[BATTLE_PARTNER(ctx->battlerAtk)].usedAllySwitch) + { + gBattlescriptCurrInstr = BattleScript_ButItFailed; + return CANCELER_RESULT_FAILURE; + } + + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult CancelerTookAttack(struct BattleContext *ctx) +{ + if (gSpecialStatuses[gBattlerTarget].abilityRedirected) + { + gSpecialStatuses[gBattlerTarget].abilityRedirected = FALSE; + BattleScriptCall(BattleScript_TookAttack); + return CANCELER_RESULT_BREAK; + } + return CANCELER_RESULT_SUCCESS; +} + +#define checkFailure FALSE +#define skipFailure TRUE +static bool32 IsSingleTarget(enum BattlerId battlerAtk, enum BattlerId battlerDef) +{ + if (battlerDef != gBattlerTarget) + return skipFailure; + return checkFailure; +} + +static bool32 IsSmartTarget(enum BattlerId battlerAtk, enum BattlerId battlerDef) +{ + if (battlerAtk == BATTLE_PARTNER(battlerDef)) + return skipFailure; + return checkFailure; +} + +static bool32 IsTargetingBothFoes(enum BattlerId battlerAtk, enum BattlerId battlerDef) +{ + if (battlerDef == BATTLE_PARTNER(battlerAtk) || battlerAtk == battlerDef) + { + // Because of Magic Bounce and Magic Coat we don't want to set MOVE_RESULT_NO_EFFECT + if (GetMoveCategory(gCurrentMove) != DAMAGE_CATEGORY_STATUS) + gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT; + return skipFailure; + } + return checkFailure; +} + +static bool32 IsTargetingSelf(enum BattlerId battlerAtk, enum BattlerId battlerDef) +{ + return skipFailure; +} + +static bool32 IsTargetingAlly(enum BattlerId battlerAtk, enum BattlerId battlerDef) +{ + if (battlerDef != BATTLE_PARTNER(battlerAtk)) + { + gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT; + return skipFailure; + } + return checkFailure; +} + +static bool32 IsTargetingSelfAndAlly(enum BattlerId battlerAtk, enum BattlerId battlerDef) +{ + if (battlerDef != BATTLE_PARTNER(battlerAtk)) + { + if (battlerDef != battlerAtk) // Don't set result flags for user + gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT; + return skipFailure; + } + return checkFailure; +} + +static bool32 IsTargetingSelfOrAlly(enum BattlerId battlerAtk, enum BattlerId battlerDef) +{ + if (battlerDef == battlerAtk) + return skipFailure; + + if (battlerDef != BATTLE_PARTNER(battlerAtk)) + { + gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT; + return skipFailure; + } + + return checkFailure; +} + +static bool32 IsTargetingFoesAndAlly(enum BattlerId battlerAtk, enum BattlerId battlerDef) +{ + if (battlerAtk == battlerDef) + return skipFailure; // Don't set result flags for user + return checkFailure; +} + +static bool32 IsTargetingField(enum BattlerId battlerAtk, enum BattlerId battlerDef) +{ + return skipFailure; +} + +static bool32 IsTargetingOpponentsField(enum BattlerId battlerAtk, enum BattlerId battlerDef) +{ + return checkFailure; // Bounce failure only +} + +static bool32 IsTargetingAllBattlers(enum BattlerId battlerAtk, enum BattlerId battlerDef) +{ + if (GetConfig(CONFIG_CHECK_USER_FAILURE) >= GEN_5 && battlerAtk == battlerDef) + return skipFailure; + return checkFailure; +} + +static bool32 (*const sShouldCheckTargetMoveFailure[])(enum BattlerId battlerAtk, enum BattlerId battlerDef) = +{ + [TARGET_NONE] = IsTargetingField, + [TARGET_SELECTED] = IsSingleTarget, + [TARGET_DEPENDS] = IsSingleTarget, + [TARGET_OPPONENT] = IsSingleTarget, + [TARGET_RANDOM] = IsSingleTarget, + [TARGET_BOTH] = IsTargetingBothFoes, + [TARGET_USER] = IsTargetingSelf, + [TARGET_SMART] = IsSmartTarget, + [TARGET_ALLY] = IsTargetingAlly, + [TARGET_USER_AND_ALLY] = IsTargetingSelfAndAlly, + [TARGET_USER_OR_ALLY] = IsTargetingSelfOrAlly, + [TARGET_FOES_AND_ALLY] = IsTargetingFoesAndAlly, + [TARGET_FIELD] = IsTargetingField, + [TARGET_OPPONENTS_FIELD] = IsTargetingOpponentsField, + [TARGET_ALL_BATTLERS] = IsTargetingAllBattlers, +}; + +static bool32 ShouldCheckTargetMoveFailure(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, enum MoveTarget moveTarget) +{ + // For Bounced moves + if (IsBattlerUnaffectedByMove(battlerDef)) + return skipFailure; + + return sShouldCheckTargetMoveFailure[moveTarget](battlerAtk, battlerDef); +} +#undef checkFailure +#undef skipFailure + + +static enum CancelerResult CancelerTargetFailure(struct BattleContext *ctx) +{ + bool32 targetAvoidedAttack = FALSE; + enum MoveTarget moveTarget = GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move); + s32 movePriority = GetChosenMovePriority(ctx->battlerAtk, ctx->abilityAtk); + ctx->moveType = GetBattleMoveType(ctx->move); + ctx->updateFlags = TRUE; + ctx->runScript = TRUE; + + while (gBattleStruct->eventState.atkCancelerBattler < gBattlersCount) + { + ctx->battlerDef = gBattleStruct->eventState.atkCancelerBattler++; + + if (ShouldCheckTargetMoveFailure(ctx->battlerAtk, ctx->battlerDef, ctx->move, moveTarget)) + continue; + + ctx->abilityDef = GetBattlerAbility(ctx->battlerDef); + ctx->holdEffectDef = GetBattlerHoldEffect(ctx->battlerDef); + + if (moveTarget == TARGET_OPPONENTS_FIELD) + { + if (CanBattlerBounceBackMove(ctx)) + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FAILED; + continue; + } + + if (!IsBattlerAlive(ctx->battlerDef)) + { + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FAILED; + continue; + } + else if (!BreaksThroughSemiInvulnerablity(ctx->battlerAtk, ctx->battlerDef, ctx->abilityAtk, ctx->abilityDef, ctx->move)) + { + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FAILED; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_AVOIDED_ATK; + if (GetMoveEffect(ctx->move) == EFFECT_FLING) + BattleScriptCall(BattleScript_TargetAvoidsAttackConsumeFlingItem); + else + BattleScriptCall(BattleScript_TargetAvoidsAttack); + targetAvoidedAttack = TRUE; + } + else if (IsBattlerProtected(ctx)) + { + SetOrClearRageVolatile(); + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED; + if (GetMoveEffect(ctx->move) == EFFECT_FLING) + BattleScriptCall(BattleScript_TargetAvoidsAttackConsumeFlingItem); + else + BattleScriptCall(BattleScript_TargetAvoidsAttack); + targetAvoidedAttack = TRUE; + } + else if (CanBattlerBounceBackMove(ctx)) + { + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FAILED; + } + else if (CanMoveBeBlockedByTarget(ctx, movePriority)) + { + + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; + targetAvoidedAttack = TRUE; + } + else if (GetMoveEffect(ctx->move) == EFFECT_SYNCHRONOISE && !DoBattlersShareType(ctx->battlerAtk, ctx->battlerDef)) + { + gBattleStruct->moveResultFlags[ctx->battlerDef] = MOVE_RESULT_NO_EFFECT; + BattleScriptCall(BattleScript_ItDoesntAffectFoe); + targetAvoidedAttack = TRUE; + } + else + { + CalcTypeEffectivenessMultiplier(ctx); + + if (ctx->abilityBlocked) + { + ctx->abilityBlocked = FALSE; + gBattleStruct->moveResultFlags[ctx->battlerDef] = MOVE_RESULT_FAILED; + gBattlerAbility = ctx->battlerDef; + RecordAbilityBattle(ctx->battlerDef, ctx->abilityDef); + BattleScriptCall(BattleScript_AbilityPopUp); + targetAvoidedAttack = TRUE; + } + else if (ctx->airBalloonBlocked) + { + ctx->airBalloonBlocked = FALSE; + gBattleStruct->moveResultFlags[ctx->battlerDef] = MOVE_RESULT_FAILED; + BattleScriptCall(BattleScript_DoesntAffectScripting); + targetAvoidedAttack = TRUE; + } + } + + if (targetAvoidedAttack) + { + gLastLandedMoves[gBattlerTarget] = 0; // Might need investigation on what exactly clears is + gLastHitByType[gBattlerTarget] = 0; + gBattleScripting.battler = ctx->battlerDef; + gBattleStruct->pledgeMove = FALSE; + CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); + return CANCELER_RESULT_PAUSE; + } + } + + if (IsDoubleBattle()) + { + if (moveTarget == TARGET_BOTH) + gBattleStruct->numSpreadTargets = CountAliveMonsInBattle(BATTLE_ALIVE_EXCEPT_BATTLER_SIDE, gBattlerAttacker); + else if (moveTarget == TARGET_FOES_AND_ALLY) + gBattleStruct->numSpreadTargets = CountAliveMonsInBattle(BATTLE_ALIVE_EXCEPT_BATTLER, gBattlerAttacker); + } + + ctx->battlerDef = gBattlerTarget; + gBattleStruct->eventState.atkCancelerBattler = 0; + return CANCELER_RESULT_SUCCESS; +} + +static bool32 CantFullyProtectFromMove(enum BattlerId battlerDef) +{ + if (MoveIgnoresProtect(gCurrentMove)) + return FALSE; + if (!IsZMove(gCurrentMove) && !IsMaxMove(gCurrentMove)) + return FALSE; + return GetProtectType(gProtectStructs[battlerDef].protected) == PROTECT_TYPE_SINGLE + && gProtectStructs[battlerDef].protected != PROTECT_MAX_GUARD; +} + +static enum CancelerResult CancelerNotFullyProtected(struct BattleContext *ctx) +{ + while (gBattleStruct->eventState.atkCancelerBattler < gBattlersCount) + { + enum BattlerId battlerDef = gBattleStruct->eventState.atkCancelerBattler++; + + if (CantFullyProtectFromMove(battlerDef)) + { + BattleScriptCall(BattleScript_CouldntFullyProtect); + gBattleScripting.battler = battlerDef; + return CANCELER_RESULT_PAUSE; + } + } + + gBattleStruct->eventState.atkCancelerBattler = 0; + return CANCELER_RESULT_SUCCESS; +} + +static bool32 IsMoveParentalBondAffected(struct BattleContext *ctx) +{ + if (ctx->abilityAtk != ABILITY_PARENTAL_BOND + || gBattleStruct->numSpreadTargets > 1 + || IsMoveParentalBondBanned(ctx->move) + || GetMoveCategory(ctx->move) == DAMAGE_CATEGORY_STATUS + || GetMoveEffect(ctx->move) == EFFECT_SEMI_INVULNERABLE + || GetMoveEffect(ctx->move) == EFFECT_TWO_TURNS_ATTACK + || GetActiveGimmick(ctx->battlerAtk) == GIMMICK_Z_MOVE + || ctx->move == MOVE_STRUGGLE) + return FALSE; + return TRUE; +} + +static void SetPossibleNewSmartTarget(u32 move) +{ + if (!IsBattlerUnaffectedByMove(gBattlerTarget) + || !CanTargetPartner(gBattlerAttacker, gBattlerTarget) + || IsAffectedByFollowMe(gBattlerAttacker, GetBattlerSide(gBattlerTarget), move) + || GetBattlerMoveTargetType(gBattlerAttacker, move) != TARGET_SMART) + return; + + enum BattlerId partner = BATTLE_PARTNER(gBattlerTarget); + if (!IsBattlerUnaffectedByMove(partner)) + gBattlerTarget = partner; +} + +static void SetRandomMultiHitCounter() +{ + if (GetBattlerHoldEffect(gBattlerAttacker) == HOLD_EFFECT_LOADED_DICE) + gMultiHitCounter = RandomUniform(RNG_LOADED_DICE, 4, 5); + else if (GetConfig(CONFIG_MULTI_HIT_CHANCE) >= GEN_5) + gMultiHitCounter = RandomWeighted(RNG_HITS, 0, 0, 7, 7, 3, 3); // 35%: 2 hits, 35%: 3 hits, 15% 4 hits, 15% 5 hits. + else + gMultiHitCounter = RandomWeighted(RNG_HITS, 0, 0, 3, 3, 1, 1); // 37.5%: 2 hits, 37.5%: 3 hits, 12.5% 4 hits, 12.5% 5 hits. +} + +static enum CancelerResult CancelerMultihitMoves(struct BattleContext *ctx) +{ + SetPossibleNewSmartTarget(ctx->move); + + if (IsBattlerUnaffectedByMove(gBattlerTarget)) // Dragon Darts can still hit partner + { + gMultiHitCounter = 0; + } + else if (IsMultiHitMove(ctx->move)) + { + enum Ability ability = ctx->abilityAtk; + + if (ability == ABILITY_SKILL_LINK) + { + gMultiHitCounter = 5; + } + else if (GetMoveEffect(ctx->move) == EFFECT_SPECIES_POWER_OVERRIDE + && gBattleMons[ctx->battlerAtk].species == GetMoveSpeciesPowerOverride_Species(ctx->move)) + { + gMultiHitCounter = GetMoveSpeciesPowerOverride_NumOfHits(ctx->move); + } + else + { + SetRandomMultiHitCounter(); + } + + PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0) + } + else if (GetMoveStrikeCount(ctx->move) > 1) + { + if (GetMoveEffect(ctx->move) == EFFECT_POPULATION_BOMB && GetBattlerHoldEffect(ctx->battlerAtk) == HOLD_EFFECT_LOADED_DICE) + { + gMultiHitCounter = RandomUniform(RNG_LOADED_DICE, 4, 10); + } + else + { + gMultiHitCounter = GetMoveStrikeCount(ctx->move); + } + + PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 3, 0) + } + else if (GetMoveEffect(ctx->move) == EFFECT_BEAT_UP) + { + struct Pokemon* party = GetBattlerParty(ctx->battlerAtk); + int i; + gBattleStruct->beatUpSlot = 0; + gMultiHitCounter = 0; + memset(gBattleStruct->beatUpSpecies, 0xFF, sizeof(gBattleStruct->beatUpSpecies)); + + for (i = 0; i < PARTY_SIZE; i++) + { + u32 species = GetMonData(&party[i], MON_DATA_SPECIES); + if (species != SPECIES_NONE + && GetMonData(&party[i], MON_DATA_HP) + && !GetMonData(&party[i], MON_DATA_IS_EGG) + && !GetMonData(&party[i], MON_DATA_STATUS)) + { + if (GetConfig(CONFIG_BEAT_UP) >= GEN_5) + gBattleStruct->beatUpSpecies[gMultiHitCounter] = species; + else + gBattleStruct->beatUpSpecies[gMultiHitCounter] = i; + gMultiHitCounter++; + } + } + + PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0) + } + else if (IsMoveParentalBondAffected(ctx)) + { + gSpecialStatuses[gBattlerAttacker].parentalBondState = PARENTAL_BOND_1ST_HIT; + gMultiHitCounter = 2; + PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0) + } + else + { + gMultiHitCounter = 0; + } + + return CANCELER_RESULT_SUCCESS; +} + +static enum CancelerResult (*const sMoveSuccessOrderCancelers[])(struct BattleContext *ctx) = +{ + [CANCELER_CLEAR_FLAGS] = CancelerClearFlags, + [CANCELER_STANCE_CHANGE_1] = CancelerStanceChangeOne, + [CANCELER_SKY_DROP] = CancelerSkyDrop, + [CANCELER_RECHARGE] = CancelerRecharge, + [CANCELER_CHILLY_RECEPTION] = CancelerChillyReception, + [CANCELER_ASLEEP_OR_FROZEN] = CancelerAsleepOrFrozen, + [CANCELER_OBEDIENCE] = CancelerObedience, + [CANCELER_POWER_POINTS] = CancelerPowerPoints, + [CANCELER_TRUANT] = CancelerTruant, + [CANCELER_FOCUS_GEN5] = CancelerFocusGen5, + [CANCELER_FLINCH] = CancelerFlinch, + [CANCELER_DISABLED] = CancelerDisabled, + [CANCELER_VOLATILE_BLOCKED] = CancelerVolatileBlocked, + [CANCELER_TAUNTED] = CancelerTaunted, + [CANCELER_IMPRISONED] = CancelerImprisoned, + [CANCELER_CONFUSED] = CancelerConfused, + [CANCELER_GHOST] = CancelerGhost, + [CANCELER_PARALYZED] = CancelerParalyzed, + [CANCELER_INFATUATION] = CancelerInfatuation, + [CANCELER_BIDE] = CancelerBide, + [CANCELER_Z_MOVES] = CancelerZMoves, + [CANCELER_CHOICE_LOCK] = CancelerChoiceLock, + [CANCELER_CALLSUBMOVE] = CancelerCallSubmove, + [CANCELER_THAW] = CancelerThaw, + [CANCELER_STANCE_CHANGE_2] = CancelerStanceChangeTwo, + [CANCELER_ATTACKSTRING] = CancelerAttackstring, + [CANCELER_PPDEDUCTION] = CancelerPPDeduction, + [CANCELER_SKY_BATTLE] = CancelerSkyBattle, + [CANCELER_WEATHER_PRIMAL] = CancelerWeatherPrimal, + [CANCELER_FOCUS_PRE_GEN5] = CancelerFocusPreGen5, + [CANCELER_MOVE_FAILURE] = CancelerMoveFailure, + [CANCELER_POWDER_STATUS] = CancelerPowderStatus, + [CANCELER_PRIORITY_BLOCK] = CancelerPriorityBlock, + [CANCELER_PROTEAN] = CancelerProtean, + [CANCELER_EXPLODING_DAMP] = CancelerExplodingDamp, + [CANCELER_EXPLOSION] = CancelerExplosion, + [CANCELER_CHARGING] = CancelerCharging, + [CANCELER_MOVE_SPECIFIC_MESSAGE] = CancelerMoveSpecificMessage, + [CANCELER_NO_TARGET] = CancelerNoTarget, + [CANCELER_TOOK_ATTACK] = CancelerTookAttack, + [CANCELER_TARGET_FAILURE] = CancelerTargetFailure, + [CANCELER_NOT_FULLY_PROTECTED] = CancelerNotFullyProtected, + [CANCELER_MULTIHIT_MOVES] = CancelerMultihitMoves, +}; + +enum CancelerResult DoAttackCanceler(void) +{ + enum CancelerResult result = CANCELER_RESULT_SUCCESS; + + struct BattleContext ctx = {0}; + ctx.battlerAtk = gBattlerAttacker; + ctx.battlerDef = gBattlerTarget; + ctx.move = gCurrentMove; + ctx.chosenMove = gChosenMove; + ctx.abilityAtk = GetBattlerAbility(ctx.battlerAtk); + ctx.holdEffectAtk = GetBattlerHoldEffect(ctx.battlerAtk); + + while (gBattleStruct->eventState.atkCanceler < CANCELER_END && result == CANCELER_RESULT_SUCCESS) + { + result = sMoveSuccessOrderCancelers[gBattleStruct->eventState.atkCanceler](&ctx); + if (result != CANCELER_RESULT_PAUSE) + gBattleStruct->eventState.atkCanceler++; + } + + if (result == CANCELER_RESULT_FAILURE) + gBattleStruct->unableToUseMove = TRUE; + return result; +} + +// ======== // Move End +// ======== -static enum MoveEndResult MoveEnd_SetValues(void) +static enum MoveEndResult MoveEndSetValues(void) { gBattleScripting.savedDmg += gBattleStruct->moveDamage[gBattlerTarget]; gBattleStruct->eventState.moveEndBattler = 0; gBattleStruct->eventState.moveEndBlock = 0; gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_ProtectLikeEffect(void) +static enum MoveEndResult MoveEndProtectLikeEffect(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; u32 temp = 0; - if (CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), gCurrentMove)) + if (gProtectStructs[gBattlerAttacker].chargingTurn + || CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), gCurrentMove)) { gBattleScripting.moveendState++; return result; @@ -54,7 +1699,7 @@ static enum MoveEndResult MoveEnd_ProtectLikeEffect(void) SetPassiveDamageAmount(gBattlerAttacker, GetNonDynamaxMaxHP(gBattlerAttacker) / 8); PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_SPIKY_SHIELD); BattleScriptCall(BattleScript_SpikyShieldEffect); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case PROTECT_KINGS_SHIELD: @@ -64,14 +1709,14 @@ static enum MoveEndResult MoveEnd_ProtectLikeEffect(void) else gBattleScripting.moveEffect = MOVE_EFFECT_ATK_MINUS_2; BattleScriptCall(BattleScript_KingsShieldEffect); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; break; case PROTECT_BANEFUL_BUNKER: if (CanBePoisoned(gBattlerTarget, gBattlerAttacker, GetBattlerAbility(gBattlerTarget), GetBattlerAbility(gBattlerAttacker))) { gBattleScripting.moveEffect = MOVE_EFFECT_POISON; BattleScriptCall(BattleScript_BanefulBunkerEffect); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case PROTECT_BURNING_BULWARK: @@ -79,20 +1724,20 @@ static enum MoveEndResult MoveEnd_ProtectLikeEffect(void) { gBattleScripting.moveEffect = MOVE_EFFECT_BURN; BattleScriptCall(BattleScript_BanefulBunkerEffect); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case PROTECT_OBSTRUCT: SWAP(gBattlerAttacker, gBattlerTarget, temp); // gBattlerTarget and gBattlerAttacker are swapped in order to activate Defiant, if applicable gBattleScripting.moveEffect = MOVE_EFFECT_DEF_MINUS_2; BattleScriptCall(BattleScript_KingsShieldEffect); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; break; case PROTECT_SILK_TRAP: SWAP(gBattlerAttacker, gBattlerTarget, temp); // gBattlerTarget and gBattlerAttacker are swapped in order to activate Defiant, if applicable gBattleScripting.moveEffect = MOVE_EFFECT_SPD_MINUS_1; BattleScriptCall(BattleScript_KingsShieldEffect); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; break; default: break; @@ -100,26 +1745,26 @@ static enum MoveEndResult MoveEnd_ProtectLikeEffect(void) // Not strictly a protect effect, but works the same way if (IsBattlerUsingBeakBlast(gBattlerTarget) - && CanBeBurned(gBattlerAttacker, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) - && !IsBattlerUnaffectedByMove(gBattlerTarget)) + && IsBattlerTurnDamaged(gBattlerTarget) + && CanBeBurned(gBattlerAttacker, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))) { gBattleMons[gBattlerAttacker].status1 = STATUS1_BURN; BtlController_EmitSetMonData(gBattlerAttacker, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerAttacker].status1), &gBattleMons[gBattlerAttacker].status1); MarkBattlerForControllerExec(gBattlerAttacker); BattleScriptCall(BattleScript_BeakBlastBurn); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_Absorb(void) +static enum MoveEndResult MoveEndAbsorb(void) { if (gBattleStruct->unableToUseMove) { gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } if (IsExplosionMove(gCurrentMove) @@ -130,10 +1775,10 @@ static enum MoveEndResult MoveEnd_Absorb(void) gBattleStruct->passiveHpUpdate[gBattlerAttacker] = 0; BattleScriptCall(BattleScript_FaintAttackerForExplosion); gBattleScripting.moveendState++; - return MOVEEND_STEP_RUN_SCRIPT; + return MOVEEND_RESULT_RUN_SCRIPT; } - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; enum BattleMoveEffects moveEffect = GetMoveEffect(gCurrentMove); switch (moveEffect) @@ -160,7 +1805,7 @@ static enum MoveEndResult MoveEnd_Absorb(void) gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABSORB_OOZE; BattleScriptCall(BattleScript_EffectAbsorbLiquidOoze); } - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_MAX_HP_50_RECOIL: @@ -176,7 +1821,7 @@ static enum MoveEndResult MoveEnd_Absorb(void) gSpecialStatuses[gBattlerAttacker].mindBlownRecoil = TRUE; TryUpdateEvolutionTracker(IF_RECOIL_DAMAGE_GE, gBattleStruct->passiveHpUpdate[gBattlerAttacker], MOVE_NONE); BattleScriptCall(BattleScript_MaxHp50Recoil); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; default: @@ -187,9 +1832,9 @@ static enum MoveEndResult MoveEnd_Absorb(void) return result; } -static enum MoveEndResult MoveEnd_Rage(void) +static enum MoveEndResult MoveEndRage(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (gBattleMons[gBattlerTarget].volatiles.rage && IsBattlerAlive(gBattlerTarget) @@ -201,92 +1846,103 @@ static enum MoveEndResult MoveEnd_Rage(void) { SET_STATCHANGER(STAT_ATK, 1, FALSE); BattleScriptCall(BattleScript_RageIsBuilding); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_SynchronizeTarget(void) +static enum MoveEndResult MoveEndSynchronizeTarget(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (AbilityBattleEffects(ABILITYEFFECT_SYNCHRONIZE, gBattlerTarget, 0, 0, TRUE)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_Abilities(void) +static enum MoveEndResult MoveEndAbilities(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; enum Ability targetAbility = GetBattlerAbility(gBattlerTarget); if (AbilityBattleEffects(ABILITYEFFECT_MOVE_END, gBattlerTarget, targetAbility, 0, TRUE)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; else if (TryClearIllusion(gBattlerTarget, targetAbility)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_AbilitiesAttacker(void) +static enum MoveEndResult MoveEndFormChangeOnHit(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; + + if (AbilityBattleEffects(ABILITYEFFECT_FORM_CHANGE_ON_HIT, gBattlerTarget, GetBattlerAbility(gBattlerTarget), 0, TRUE)) + result = MOVEEND_RESULT_RUN_SCRIPT; + + gBattleScripting.moveendState++; + return result; +} + +static enum MoveEndResult MoveEndAbilitiesAttacker(void) +{ + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (AbilityBattleEffects(ABILITYEFFECT_MOVE_END_ATTACKER, gBattlerAttacker, 0, 0, TRUE)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_StatusImmunityAbilities(void) +static enum MoveEndResult MoveEndStatusImmunityAbilities(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; - for (u32 battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { if (AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, battler, 0, 0, TRUE)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } - if (result == MOVEEND_STEP_CONTINUE) + if (result == MOVEEND_RESULT_CONTINUE) gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_SynchronizeAttacker(void) +static enum MoveEndResult MoveEndSynchronizeAttacker(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (AbilityBattleEffects(ABILITYEFFECT_ATK_SYNCHRONIZE, gBattlerAttacker, 0, 0, TRUE)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_AttackerInvisible(void) +static enum MoveEndResult MoveEndAttackerInvisible(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (IsSemiInvulnerable(gBattlerAttacker, CHECK_ALL) && gHitMarker & (HITMARKER_NO_ANIMATIONS | HITMARKER_DISABLE_ANIMATION)) { BtlController_EmitSpriteInvisibility(gBattlerAttacker, B_COMM_TO_CONTROLLER, TRUE); MarkBattlerForControllerExec(gBattlerAttacker); - result = MOVEEND_STEP_BREAK; + result = MOVEEND_RESULT_BREAK; } gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_AttackerVisible(void) +static enum MoveEndResult MoveEndAttackerVisible(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (IsBattlerUnaffectedByMove(gBattlerTarget) || !IsSemiInvulnerable(gBattlerAttacker, CHECK_ALL) @@ -297,15 +1953,15 @@ static enum MoveEndResult MoveEnd_AttackerVisible(void) gBattleMons[gBattlerAttacker].volatiles.semiInvulnerable = STATE_NONE; gSpecialStatuses[gBattlerAttacker].restoredBattlerSprite = TRUE; gBattleScripting.moveendState++; - return MOVEEND_STEP_BREAK; + return MOVEEND_RESULT_BREAK; } gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_TargetVisible(void) +static enum MoveEndResult MoveEndTargetVisible(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (!gSpecialStatuses[gBattlerTarget].restoredBattlerSprite && gBattlerTarget < gBattlersCount && !IsSemiInvulnerable(gBattlerTarget, CHECK_ALL)) @@ -313,45 +1969,45 @@ static enum MoveEndResult MoveEnd_TargetVisible(void) BtlController_EmitSpriteInvisibility(gBattlerTarget, B_COMM_TO_CONTROLLER, FALSE); MarkBattlerForControllerExec(gBattlerTarget); gBattleMons[gBattlerTarget].volatiles.semiInvulnerable = STATE_NONE; - result = MOVEEND_STEP_BREAK; + result = MOVEEND_RESULT_BREAK; } gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_ItemEffectsTarget(void) +static enum MoveEndResult MoveEndItemEffectsTarget(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; enum HoldEffect holdEffect = GetBattlerHoldEffect(gBattlerTarget); if (ItemBattleEffects(gBattlerTarget, gBattlerAttacker, holdEffect, IsOnTargetHitActivation) || ItemBattleEffects(gBattlerTarget, gBattlerAttacker, holdEffect, IsOnStatusChangeActivation)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_ItemEffectsAttacker1(void) +static enum MoveEndResult MoveEndItemEffectsAttacker1(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; enum HoldEffect holdEffect = GetBattlerHoldEffect(gBattlerAttacker); if (ItemBattleEffects(gBattlerAttacker, gBattlerTarget, holdEffect, IsOnAttackerAfterHitActivation) || ItemBattleEffects(gBattlerAttacker, gBattlerTarget, holdEffect, IsOnStatusChangeActivation) || ItemBattleEffects(gBattlerAttacker, gBattlerTarget, holdEffect, IsOnHpThresholdActivation)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_Symbiosis(void) +static enum MoveEndResult MoveEndSymbiosis(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; - for (u32 battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { if ((gSpecialStatuses[battler].berryReduced || (GetConfig(CONFIG_SYMBIOSIS_GEMS) >= GEN_7 && gSpecialStatuses[battler].gemBoost)) @@ -363,7 +2019,7 @@ static enum MoveEndResult MoveEnd_Symbiosis(void) gBattleScripting.battler = gBattlerAbility = BATTLE_PARTNER(battler); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_SymbiosisActivates; - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; break; } } @@ -372,11 +2028,11 @@ static enum MoveEndResult MoveEnd_Symbiosis(void) return result; } -static enum MoveEndResult MoveEnd_Substitute(void) +static enum MoveEndResult MoveEndSubstitute(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; - for (u32 i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].volatiles.substituteHP == 0) gBattleMons[i].volatiles.substitute = FALSE; @@ -386,9 +2042,9 @@ static enum MoveEndResult MoveEnd_Substitute(void) return result; } -static enum MoveEndResult MoveEnd_FaintBlock(void) +static enum MoveEndResult MoveEndFaintBlock(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; do { @@ -402,7 +2058,7 @@ static enum MoveEndResult MoveEnd_FaintBlock(void) && !IsBattlerUnaffectedByMove(gBattlerTarget)) { BattleScriptCall(BattleScript_FinalGambit); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } gBattleStruct->eventState.moveEndBlock++; break; @@ -411,7 +2067,7 @@ static enum MoveEndResult MoveEnd_FaintBlock(void) || gBattleStruct->battlerState[gBattlerTarget].fainted) { gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } gBattleStruct->eventState.moveEndBlock++; @@ -423,7 +2079,7 @@ static enum MoveEndResult MoveEnd_FaintBlock(void) if (!IsNeutralizingGasOnField()) { BattleScriptCall(BattleScript_NeutralizingGasExits); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } } gBattleStruct->eventState.moveEndBlock++; @@ -474,7 +2130,7 @@ static enum MoveEndResult MoveEnd_FaintBlock(void) gSideTimers[B_SIDE_OPPONENT].retaliateTimer = 2; } BattleScriptCall(BattleScript_FaintBattler); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; gBattleStruct->eventState.moveEndBlock++; break; case FAINT_BLOCK_DO_DESTINY_BOND: @@ -482,7 +2138,7 @@ static enum MoveEndResult MoveEnd_FaintBlock(void) { gBattleStruct->passiveHpUpdate[gBattlerAttacker] = gBattleMons[gBattlerAttacker].hp; BattleScriptCall(BattleScript_DestinyBondTakesLife); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } gBattleStruct->eventState.moveEndBlock++; break; @@ -496,7 +2152,7 @@ static enum MoveEndResult MoveEnd_FaintBlock(void) MarkBattlerForControllerExec(gBattlerAttacker); PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerAttacker].moves[moveIndex]) BattleScriptCall(BattleScript_GrudgeTakesPp); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } gBattleStruct->eventState.moveEndBlock++; break; @@ -507,12 +2163,12 @@ static enum MoveEndResult MoveEnd_FaintBlock(void) break; } - if (result == MOVEEND_STEP_RUN_SCRIPT) + if (result == MOVEEND_RESULT_RUN_SCRIPT) break; } while (gBattleStruct->eventState.moveEndBlock != 0); - if (result == MOVEEND_STEP_CONTINUE) + if (result == MOVEEND_RESULT_CONTINUE) { gBattleStruct->eventState.moveEndBlock = 0; gBattleScripting.moveendState++; @@ -521,16 +2177,16 @@ static enum MoveEndResult MoveEnd_FaintBlock(void) return result; } -static enum MoveEndResult MoveEnd_SkyDropConfuse(void) +static enum MoveEndResult MoveEndSkyDropConfuse(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; - for (u32 battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { if (gBattleStruct->skyDropTargets[battler] == SKY_DROP_RELEASED_TARGET) { // Find the battler id of the Pokemon that was held by Sky Drop - u32 targetId; + enum BattlerId targetId; for (targetId = 0; targetId < gBattlersCount; targetId++) { if (gBattleStruct->skyDropTargets[targetId] == battler) @@ -543,7 +2199,7 @@ static enum MoveEndResult MoveEnd_SkyDropConfuse(void) // Clear skyDropTargets data gBattleStruct->skyDropTargets[battler] = SKY_DROP_NO_TARGET; gBattleStruct->skyDropTargets[targetId] = SKY_DROP_NO_TARGET; - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; break; } } @@ -552,17 +2208,11 @@ static enum MoveEndResult MoveEnd_SkyDropConfuse(void) return result; } -static enum MoveEndResult MoveEnd_UpdateLastMoves(void) +static enum MoveEndResult MoveEndUpdateLastMoves(void) { if (!IsOnPlayerSide(gBattlerAttacker)) UpdateStallMons(); - if ((gBattleStruct->moveResultFlags[gBattlerTarget] & (MOVE_RESULT_FAILED | MOVE_RESULT_DOESNT_AFFECT_FOE)) - || gBattleMons[gBattlerAttacker].volatiles.flinched - || gBattleStruct->pledgeMove == TRUE // Is the battler that uses the first Pledge move in the combo - || gBattleStruct->unableToUseMove) - gBattleStruct->battlerState[gBattlerAttacker].stompingTantrumTimer = 2; - // After swapattackerwithtarget is used for snatch the correct battlers have to be restored so data is stored correctly if (gBattleStruct->snatchedMoveIsUsed) { @@ -570,14 +2220,8 @@ static enum MoveEndResult MoveEnd_UpdateLastMoves(void) SWAP(gBattlerAttacker, gBattlerTarget, temp); } - if (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove) - { - gBattleMons[gBattlerAttacker].volatiles.usedMoves |= 1u << gCurrMovePos; - gBattleStruct->battlerState[gBattlerAttacker].lastMoveTarget = gBattlerTarget; - } - enum BattleMoveEffects originalEffect = GetMoveEffect(GetOriginallyUsedMove(gChosenMove)); - if (IsBattlerAlive(gBattlerAttacker) + if (IsBattlerAlive(gBattlerAttacker) // Why do we need to check if user fainted? We just want to set with what move the target got hit && originalEffect != EFFECT_BATON_PASS && originalEffect != EFFECT_HEALING_WISH && originalEffect != EFFECT_LUNAR_DANCE) @@ -587,7 +2231,6 @@ static enum MoveEndResult MoveEnd_UpdateLastMoves(void) if (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove) { gLastMoves[gBattlerAttacker] = gChosenMove; - RecordKnownMove(gBattlerAttacker, gChosenMove); gLastResultingMoves[gBattlerAttacker] = gCurrentMove; gLastUsedMoveType[gBattlerAttacker] = GetBattleMoveType(gCurrentMove); } @@ -627,10 +2270,10 @@ static enum MoveEndResult MoveEnd_UpdateLastMoves(void) } gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_MirrorMove(void) +static enum MoveEndResult MoveEndMirrorMove(void) { if (!gBattleStruct->unableToUseMove && gBattlerAttacker != gBattlerTarget @@ -644,12 +2287,12 @@ static enum MoveEndResult MoveEnd_MirrorMove(void) } gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_Defrost(void) +static enum MoveEndResult MoveEndDefrost(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (gBattleMons[gBattlerTarget].status1 & STATUS1_FREEZE && IsBattlerTurnDamaged(gBattlerTarget) @@ -662,7 +2305,7 @@ static enum MoveEndResult MoveEnd_Defrost(void) BtlController_EmitSetMonData(gBattlerTarget, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].status1), &gBattleMons[gBattlerTarget].status1); MarkBattlerForControllerExec(gBattlerTarget); BattleScriptCall(BattleScript_DefrostedViaFireMove); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } else if (gBattleMons[gBattlerTarget].status1 & STATUS1_FROSTBITE && IsBattlerTurnDamaged(gBattlerTarget) @@ -675,14 +2318,14 @@ static enum MoveEndResult MoveEnd_Defrost(void) BtlController_EmitSetMonData(gBattlerTarget, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].status1), &gBattleMons[gBattlerTarget].status1); MarkBattlerForControllerExec(gBattlerTarget); BattleScriptCall(BattleScript_FrostbiteHealedViaFireMove); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_NextTarget(void) +static enum MoveEndResult MoveEndNextTarget(void) { enum MoveTarget moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); @@ -694,7 +2337,7 @@ static enum MoveEndResult MoveEnd_NextTarget(void) } else if (moveTarget == TARGET_USER_AND_ALLY) { - u32 partner = BATTLE_PARTNER(gBattlerAttacker); + enum BattlerId partner = BATTLE_PARTNER(gBattlerAttacker); if (partner != gBattlerTarget && IsBattlerAlive(partner)) { gBattleStruct->moveTarget[gBattlerAttacker] = gBattlerTarget = partner; @@ -702,7 +2345,7 @@ static enum MoveEndResult MoveEnd_NextTarget(void) gBattlescriptCurrInstr = BattleScript_FlushMessageBox; gBattleScripting.moveendState = 0; MoveValuesCleanUp(); - return MOVEEND_STEP_BREAK; + return MOVEEND_RESULT_BREAK; } } else if (IsSpreadMove(moveTarget)) @@ -714,16 +2357,9 @@ static enum MoveEndResult MoveEnd_NextTarget(void) gBattleStruct->moveTarget[gBattlerAttacker] = gBattlerTarget = nextTarget; // Fix for moxie spread moves gBattleScripting.moveendState = 0; MoveValuesCleanUp(); - - enum BattleMoveEffects moveEffect = GetMoveEffect(gCurrentMove); - - // Edge cases for moves that shouldn't repeat their own script - if (moveEffect == EFFECT_MAGNITUDE) - BattleScriptPush(gBattleMoveEffects[EFFECT_HIT].battleScript); - else - BattleScriptPush(GetMoveBattleScript(gCurrentMove)); + BattleScriptPush(GetMoveBattleScript(gCurrentMove)); gBattlescriptCurrInstr = BattleScript_FlushMessageBox; - return MOVEEND_STEP_BREAK; + return MOVEEND_RESULT_BREAK; } // Check if the move used was actually a bounced move. If so, we need to go back to the original attacker and make sure, its move hits all 2 or 3 pokemon. else if (gBattleStruct->bouncedMoveIsUsed) @@ -732,7 +2368,7 @@ static enum MoveEndResult MoveEnd_NextTarget(void) gBattleStruct->bouncedMoveIsUsed = FALSE; gBattlerAttacker = gBattleStruct->attackerBeforeBounce; gBattleStruct->battlerState[gBattlerAttacker].targetsDone[originalBounceTarget] = TRUE; - for (u32 i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) gBattleStruct->battlerState[originalBounceTarget].targetsDone[i] = FALSE; nextTarget = GetNextTarget(moveTarget, FALSE); if (nextTarget != MAX_BATTLERS_COUNT) @@ -745,33 +2381,33 @@ static enum MoveEndResult MoveEnd_NextTarget(void) MoveValuesCleanUp(); BattleScriptPush(GetMoveBattleScript(gCurrentMove)); gBattlescriptCurrInstr = BattleScript_FlushMessageBox; - return MOVEEND_STEP_BREAK; + return MOVEEND_RESULT_BREAK; } } } RecordLastUsedMoveBy(gBattlerAttacker, gCurrentMove); gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_HpThresholdItemsTarget(void) +static enum MoveEndResult MoveEndHpThresholdItemsTarget(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (gMultiHitCounter && ItemBattleEffects(gBattlerTarget, gBattlerAttacker, GetBattlerHoldEffect(gBattlerTarget), IsOnHpThresholdActivation)) { - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_MultihitMove(void) +static enum MoveEndResult MoveEndMultihitMove(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (!IsBattlerUnaffectedByMove(gBattlerTarget) && !gBattleStruct->unableToUseMove @@ -789,7 +2425,7 @@ static enum MoveEndResult MoveEnd_MultihitMove(void) BattleScriptCall(BattleScript_ScaleShot); else BattleScriptCall(BattleScript_MultiHitPrintStrings); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } else { @@ -816,12 +2452,12 @@ static enum MoveEndResult MoveEnd_MultihitMove(void) MoveValuesCleanUp(); BattleScriptPush(GetMoveBattleScript(gCurrentMove)); gBattlescriptCurrInstr = BattleScript_FlushMessageBox; - return MOVEEND_STEP_BREAK; + return MOVEEND_RESULT_BREAK; } else { BattleScriptCall(BattleScript_MultiHitPrintStrings); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } } } @@ -833,14 +2469,26 @@ static enum MoveEndResult MoveEnd_MultihitMove(void) return result; } -static enum MoveEndResult MoveEnd_MoveBlock(void) +static enum MoveEndResult MoveEndMoveBlock(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; enum BattleSide side = GetBattlerSide(gBattlerTarget); enum BattleMoveEffects moveEffect = GetMoveEffect(gCurrentMove); switch (moveEffect) { + case EFFECT_SPIT_UP: + case EFFECT_SWALLOW: + if (!gBattleStruct->unableToUseMove) + { + gBattleMons[gBattlerAttacker].volatiles.stockpileCounter = 0; + if (B_STOCKPILE_RAISES_DEFS >= GEN_4) + { + BattleScriptCall(BattleScript_MoveEffectStockpileWoreOff); + result = MOVEEND_RESULT_RUN_SCRIPT; + } + } + break; case EFFECT_KNOCK_OFF: if (gBattleMons[gBattlerTarget].item != ITEM_NONE && IsBattlerAlive(gBattlerAttacker) @@ -856,7 +2504,7 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) { gBattlerAbility = gBattlerTarget; BattleScriptCall(BattleScript_StickyHoldActivatesRet); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; break; } @@ -880,7 +2528,7 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) } BattleScriptCall(BattleScript_KnockedOff); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_STEAL_ITEM: @@ -890,14 +2538,14 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) || !IsBattlerAlive(gBattlerAttacker) || !CanStealItem(gBattlerAttacker, gBattlerTarget, gBattleMons[gBattlerTarget].item)) { - result = MOVEEND_STEP_CONTINUE; + result = MOVEEND_RESULT_CONTINUE; } else if (GetBattlerAbility(gBattlerTarget) == ABILITY_STICKY_HOLD) { BattleScriptCall(BattleScript_NoItemSteal); gLastUsedAbility = gBattleMons[gBattlerTarget].ability; RecordAbilityBattle(gBattlerTarget, gLastUsedAbility); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } else { @@ -906,12 +2554,11 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) if (!(GetConfig(CONFIG_STEAL_WILD_ITEMS) >= GEN_9 && !(gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_PALACE)))) { - gBattleMons[gBattlerAttacker].item = ITEM_NONE; // Item assigned later on with thief (see MOVEEND_CHANGED_ITEMS) - gBattleStruct->changedItems[gBattlerAttacker] = gLastUsedItem; // Stolen item to be assigned later + gBattleMons[gBattlerAttacker].item = gLastUsedItem; } gEffectBattler = gBattlerTarget; BattleScriptCall(BattleScript_ItemSteal); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_HIT_SWITCH_TARGET: @@ -920,7 +2567,7 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) && IsBattlerAlive(gBattlerAttacker) && gBattleMons[BATTLE_PARTNER(gBattlerTarget)].volatiles.semiInvulnerable != STATE_COMMANDER) { - u32 targetAbility = GetBattlerAbility(gBattlerTarget); + enum Ability targetAbility = GetBattlerAbility(gBattlerTarget); if (targetAbility == ABILITY_GUARD_DOG) break; @@ -941,7 +2588,7 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) gBattleScripting.switchCase = B_SWITCH_HIT; BattleScriptCall(BattleScript_TryHitSwitchTarget); } - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_SMACK_DOWN: @@ -955,7 +2602,7 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) gBattleMons[gBattlerTarget].volatiles.magnetRise = FALSE; gBattleMons[gBattlerTarget].volatiles.semiInvulnerable = STATE_NONE; BattleScriptCall(BattleScript_MoveEffectSmackDown); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_RECOIL_IF_MISS: @@ -989,7 +2636,7 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) } SetPassiveDamageAmount(gBattlerAttacker, recoil); BattleScriptCall(BattleScript_RecoilIfMiss); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_RECOIL: @@ -1003,7 +2650,7 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) SetPassiveDamageAmount(gBattlerAttacker, gBattleScripting.savedDmg * max(1, GetMoveRecoil(gCurrentMove)) / 100); TryUpdateEvolutionTracker(IF_RECOIL_DAMAGE_GE, gBattleStruct->passiveHpUpdate[gBattlerAttacker], MOVE_NONE); BattleScriptCall(BattleScript_MoveEffectRecoil); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_CHLOROBLAST: @@ -1018,14 +2665,14 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) SetPassiveDamageAmount(gBattlerAttacker, recoil); TryUpdateEvolutionTracker(IF_RECOIL_DAMAGE_GE, gBattleStruct->passiveHpUpdate[gBattlerAttacker], MOVE_NONE); BattleScriptCall(BattleScript_MoveEffectRecoil); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_RAPID_SPIN: if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker)) { BattleScriptCall(BattleScript_RapidSpinAway); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_FELL_STINGER: @@ -1039,7 +2686,7 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_FellStingerRaisesStat; - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_STONE_AXE: @@ -1050,7 +2697,7 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_POINTEDSTONESFLOAT; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_StealthRockActivates; - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_CEASELESS_EDGE: @@ -1062,18 +2709,18 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) BattleScriptPush(gBattlescriptCurrInstr + 1); if (gBattleStruct->isSkyBattle) { - result = MOVEEND_STEP_CONTINUE; + result = MOVEEND_RESULT_CONTINUE; } else { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_SpikesActivates; - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } } break; default: - result = MOVEEND_STEP_CONTINUE; + result = MOVEEND_RESULT_CONTINUE; break; } @@ -1081,43 +2728,43 @@ static enum MoveEndResult MoveEnd_MoveBlock(void) return result; } -static enum MoveEndResult MoveEnd_ItemEffectsAttacker2(void) +static enum MoveEndResult MoveEndItemEffectsAttacker2(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; enum HoldEffect holdEffect = GetBattlerHoldEffect(gBattlerAttacker); if (ItemBattleEffects(gBattlerAttacker, gBattlerTarget, holdEffect, IsOnStatusChangeActivation) || ItemBattleEffects(gBattlerAttacker, gBattlerTarget, holdEffect, IsOnHpThresholdActivation)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_AbilityEffectFoesFainted(void) +static enum MoveEndResult MoveEndAbilityEffectFoesFainted(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (AbilityBattleEffects(ABILITYEFFECT_MOVE_END_FOES_FAINTED, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), gCurrentMove, TRUE)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_SheerForce(void) +static enum MoveEndResult MoveEndSheerForce(void) { if (IsSheerForceAffected(gCurrentMove, GetBattlerAbility(gBattlerAttacker))) gBattleScripting.moveendState = MOVEEND_EJECT_PACK; else gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_ShellTrap(void) +static enum MoveEndResult MoveEndShellTrap(void) { - for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) + for (enum BattlerId battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) { if (battlerDef == gBattlerAttacker || IsBattlerAlly(battlerDef, gBattlerAttacker)) continue; @@ -1135,43 +2782,43 @@ static enum MoveEndResult MoveEnd_ShellTrap(void) } gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_ColorChange(void) +static enum MoveEndResult MoveEndColorChange(void) { while (gBattleStruct->eventState.moveEndBattler < gBattlersCount) { - u32 battler = gBattleStruct->eventState.moveEndBattler++; + enum BattlerId battler = gBattleStruct->eventState.moveEndBattler++; if (battler == gBattlerAttacker) continue; if (AbilityBattleEffects(ABILITYEFFECT_COLOR_CHANGE, battler, GetBattlerAbility(battler), 0, TRUE)) - return MOVEEND_STEP_RUN_SCRIPT; + return MOVEEND_RESULT_RUN_SCRIPT; } gBattleStruct->eventState.moveEndBattler = 0; gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_KeeMarangaHpThresholdItemTarget(void) +static enum MoveEndResult MoveEndKeeMarangaHpThresholdItemTarget(void) { while (gBattleStruct->eventState.moveEndBattler < gBattlersCount) { - u32 battlerDef = gBattleStruct->eventState.moveEndBattler++; + enum BattlerId battlerDef = gBattleStruct->eventState.moveEndBattler++; if (battlerDef == gBattlerAttacker) continue; enum HoldEffect holdEffect = GetBattlerHoldEffect(battlerDef); if (ItemBattleEffects(battlerDef, gBattlerAttacker, holdEffect, IsKeeMarangaBerryActivation) || ItemBattleEffects(battlerDef, gBattlerAttacker, holdEffect, IsOnHpThresholdActivation)) - return MOVEEND_STEP_RUN_SCRIPT; + return MOVEEND_RESULT_RUN_SCRIPT; } gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static bool32 TryRedCard(u32 battlerAtk, u32 redCardBattler, u32 move) +static bool32 TryRedCard(enum BattlerId battlerAtk, enum BattlerId redCardBattler, enum Move move) { if (!IsBattlerAlive(redCardBattler) || !IsBattlerTurnDamaged(redCardBattler) @@ -1194,7 +2841,7 @@ static bool32 TryRedCard(u32 battlerAtk, u32 redCardBattler, u32 move) return TRUE; } -static bool32 TryEjectButton(u32 battlerAtk, u32 ejectButtonBattler) +static bool32 TryEjectButton(enum BattlerId battlerAtk, u32 ejectButtonBattler) { if (!IsBattlerTurnDamaged(ejectButtonBattler) || !IsBattlerAlive(ejectButtonBattler) @@ -1209,14 +2856,14 @@ static bool32 TryEjectButton(u32 battlerAtk, u32 ejectButtonBattler) return TRUE; } -static enum MoveEndResult MoveEnd_CardButton(void) +static enum MoveEndResult MoveEndCardButton(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; u32 redCardBattlers = 0; u32 ejectButtonBattlers = 0; // Because sorting the battlers by speed takes lots of cycles, it's better to just check if any of the battlers has the Eject items. - for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) + for (enum BattlerId battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) { if (gBattlerAttacker == battlerDef) continue; @@ -1231,25 +2878,25 @@ static enum MoveEndResult MoveEnd_CardButton(void) if (!redCardBattlers && !ejectButtonBattlers) { gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } - u8 battlers[4] = {0, 1, 2, 3}; + enum BattlerId battlers[MAX_BATTLERS_COUNT] = {0, 1, 2, 3}; SortBattlersBySpeed(battlers, FALSE); - for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) + for (enum BattlerId battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) { - u32 battler = battlers[battlerDef]; + enum BattlerId battler = battlers[battlerDef]; // Only fastest red card or eject button activates if (redCardBattlers & 1u << battler && TryRedCard(gBattlerAttacker, battler, gCurrentMove)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; else if (ejectButtonBattlers & 1u << battler && TryEjectButton(gBattlerAttacker, battler)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; - if (result == MOVEEND_STEP_RUN_SCRIPT) + if (result == MOVEEND_RESULT_RUN_SCRIPT) { - for (u32 i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) gBattleMons[i].volatiles.tryEjectPack = FALSE; gBattleScripting.moveendState = MOVEEND_JUMP_TO_HIT_ESCAPE_PLUS_ONE; return result; @@ -1260,24 +2907,24 @@ static enum MoveEndResult MoveEnd_CardButton(void) return result; } -static enum MoveEndResult MoveEnd_LifeOrbShellBell(void) +static enum MoveEndResult MoveEndLifeOrbShellBell(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (ItemBattleEffects(gBattlerAttacker, 0, GetBattlerHoldEffect(gBattlerAttacker), IsLifeOrbShellBellActivation)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; gBattleScripting.moveendState++; return result; } -static enum MoveEndResult MoveEnd_FormChange(void) +static enum MoveEndResult MoveEndFormChange(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; - if (TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_AFTER_MOVE)) + if (TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_AFTER_MOVE, GetBattlerAbility(gBattlerAttacker))) { - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; BattleScriptCall(BattleScript_AttackerFormChangeMoveEffect); } @@ -1285,17 +2932,19 @@ static enum MoveEndResult MoveEnd_FormChange(void) return result; } -static enum MoveEndResult MoveEnd_EmergencyExit(void) +static enum MoveEndResult MoveEndEmergencyExit(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; u32 numEmergencyExitBattlers = 0; u32 emergencyExitBattlers = 0; // Because sorting the battlers by speed takes lots of cycles, // we check if EE can be activated and count how many. - for (u32 i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { - if (IsBattlerTurnDamaged(i) && EmergencyExitCanBeTriggered(i)) + if (!IsBattleMoveStatus(gCurrentMove) + && !gBattleStruct->unableToUseMove + && EmergencyExitCanBeTriggered(i)) { emergencyExitBattlers |= 1u << i; numEmergencyExitBattlers++; @@ -1308,34 +2957,34 @@ static enum MoveEndResult MoveEnd_EmergencyExit(void) return result; } - for (u32 i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) gBattleMons[i].volatiles.tryEjectPack = FALSE; - u8 battlers[4] = {0, 1, 2, 3}; + enum BattlerId battlers[MAX_BATTLERS_COUNT] = {0, 1, 2, 3}; if (numEmergencyExitBattlers > 1) SortBattlersBySpeed(battlers, FALSE); for (u32 i = 0; i < gBattlersCount; i++) { - u32 battler = battlers[i]; + enum BattlerId battler = battlers[i]; if (!(emergencyExitBattlers & 1u << battler)) continue; gBattleScripting.battler = battler; BattleScriptCall(BattleScript_EmergencyExit); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; break; // Only the fastest Emergency Exit / Wimp Out activates } - if (result == MOVEEND_STEP_RUN_SCRIPT) + if (result == MOVEEND_RESULT_RUN_SCRIPT) gBattleScripting.moveendState = MOVEEND_JUMP_TO_HIT_ESCAPE_PLUS_ONE; else gBattleScripting.moveendState++; return result; } -static inline bool32 CanEjectPackTrigger(u32 battlerAtk, u32 battlerDef, enum BattleMoveEffects moveEffect) +static inline bool32 CanEjectPackTrigger(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum BattleMoveEffects moveEffect) { if (gBattleMons[battlerDef].volatiles.tryEjectPack && GetBattlerHoldEffect(battlerDef) == HOLD_EFFECT_EJECT_PACK @@ -1348,14 +2997,14 @@ static inline bool32 CanEjectPackTrigger(u32 battlerAtk, u32 battlerDef, enum Ba return FALSE; } -static enum MoveEndResult MoveEnd_EjectPack(void) +static enum MoveEndResult MoveEndEjectPack(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; u32 ejectPackBattlers = 0; u32 numEjectPackBattlers = 0; // Because sorting the battlers by speed takes lots of cycles, it's better to just check if any of the battlers has the Eject items. - for (u32 i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (CanEjectPackTrigger(gBattlerAttacker, i, GetMoveEffect(gCurrentMove))) { @@ -1370,16 +3019,16 @@ static enum MoveEndResult MoveEnd_EjectPack(void) return result; } - u8 battlers[4] = {0, 1, 2, 3}; + enum BattlerId battlers[MAX_BATTLERS_COUNT] = {0, 1, 2, 3}; if (numEjectPackBattlers > 1) SortBattlersBySpeed(battlers, FALSE); - for (u32 i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) gBattleMons[i].volatiles.tryEjectPack = FALSE; for (u32 i = 0; i < gBattlersCount; i++) { - u32 battler = battlers[i]; + enum BattlerId battler = battlers[i]; if (!(ejectPackBattlers & 1u << battler)) continue; @@ -1389,7 +3038,7 @@ static enum MoveEndResult MoveEnd_EjectPack(void) gBattleStruct->battlerState[battler].usedEjectItem = TRUE; BattleScriptCall(BattleScript_EjectPackActivates); gAiLogicData->ejectPackSwitch = TRUE; - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; break; // Only the fastest Eject item activates } @@ -1397,9 +3046,9 @@ static enum MoveEndResult MoveEnd_EjectPack(void) return result; } -static enum MoveEndResult MoveEnd_HitEscape(void) +static enum MoveEndResult MoveEndHitEscape(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (GetMoveEffect(gCurrentMove) == EFFECT_HIT_ESCAPE && !gBattleStruct->unableToUseMove @@ -1407,7 +3056,7 @@ static enum MoveEndResult MoveEnd_HitEscape(void) && IsBattlerAlive(gBattlerAttacker) && !NoAliveMonsForBattlerSide(gBattlerTarget)) { - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; BattleScriptCall(BattleScript_EffectHitEscape); } @@ -1415,104 +3064,103 @@ static enum MoveEndResult MoveEnd_HitEscape(void) return result; } -static enum MoveEndResult MoveEnd_ItemsEffectsAll(void) +static enum MoveEndResult MoveEndItemsEffectsAll(void) { while (gBattleStruct->eventState.moveEndBattler < gBattlersCount) { - u32 battler = gBattleStruct->eventState.moveEndBattler++; + enum BattlerId battler = gBattleStruct->eventState.moveEndBattler++; enum HoldEffect holdEffect = GetBattlerHoldEffect(battler); if (ItemBattleEffects(battler, 0, holdEffect, IsOnStatusChangeActivation) || ItemBattleEffects(battler, 0, holdEffect, IsOnHpThresholdActivation)) - return MOVEEND_STEP_RUN_SCRIPT; + return MOVEEND_RESULT_RUN_SCRIPT; } gBattleStruct->eventState.moveEndBattler = 0; gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_WhiteHerb(void) +static enum MoveEndResult MoveEndWhiteHerb(void) { while (gBattleStruct->eventState.moveEndBattler < gBattlersCount) { - u32 battler = gBattleStruct->eventState.moveEndBattler++; + enum BattlerId battler = gBattleStruct->eventState.moveEndBattler++; if (!IsBattlerAlive(battler)) continue; if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsWhiteHerbActivation)) - return MOVEEND_STEP_RUN_SCRIPT; + return MOVEEND_RESULT_RUN_SCRIPT; } gBattleStruct->eventState.moveEndBattler = 0; gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_Opportunist(void) +static enum MoveEndResult MoveEndOpportunist(void) { while (gBattleStruct->eventState.moveEndBattler < gBattlersCount) { - u32 battler = gBattleStruct->eventState.moveEndBattler++; + enum BattlerId battler = gBattleStruct->eventState.moveEndBattler++; if (!IsBattlerAlive(battler)) continue; if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, GetBattlerAbility(battler), 0, TRUE)) - return MOVEEND_STEP_RUN_SCRIPT; + return MOVEEND_RESULT_RUN_SCRIPT; } gBattleStruct->eventState.moveEndBattler = 0; gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_MirrorHerb(void) +static enum MoveEndResult MoveEndMirrorHerb(void) { while (gBattleStruct->eventState.moveEndBattler < gBattlersCount) { - u32 battler = gBattleStruct->eventState.moveEndBattler++; + enum BattlerId battler = gBattleStruct->eventState.moveEndBattler++; if (!IsBattlerAlive(battler)) continue; if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsMirrorHerbActivation)) - return MOVEEND_STEP_RUN_SCRIPT; + return MOVEEND_RESULT_RUN_SCRIPT; } gBattleStruct->eventState.moveEndBattler = 0; gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_Pickpocket(void) +static enum MoveEndResult MoveEndPickpocket(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (IsBattlerAlive(gBattlerAttacker) - && gBattleMons[gBattlerAttacker].item != ITEM_NONE // Attacker must be holding an item - && !GetBattlerPartyState(gBattlerAttacker)->isKnockedOff // But not knocked off - && IsMoveMakingContact(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), gCurrentMove) // Pickpocket requires contact - && !IsBattlerUnaffectedByMove(gBattlerTarget)) // Obviously attack needs to have worked + && gBattleMons[gBattlerAttacker].item != ITEM_NONE + && !GetBattlerPartyState(gBattlerAttacker)->isKnockedOff) // Gen3 edge case where the knocked of item was not removed { - u8 battlers[4] = {0, 1, 2, 3}; + enum BattlerId battlers[MAX_BATTLERS_COUNT] = {0, 1, 2, 3}; SortBattlersBySpeed(battlers, FALSE); // Pickpocket activates for fastest mon without item for (u32 i = 0; i < gBattlersCount; i++) { - u8 battler = battlers[i]; - // Attacker is mon who made contact, battler is mon with pickpocket - if (battler != gBattlerAttacker // Cannot pickpocket yourself - && GetBattlerAbility(battler) == ABILITY_PICKPOCKET // Target must have pickpocket ability - && IsBattlerTurnDamaged(battler) // Target needs to have been damaged - && !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) // Subsitute unaffected - && IsBattlerAlive(battler) // Battler must be alive to pickpocket - && gBattleMons[battler].item == ITEM_NONE // Pickpocketer can't have an item already - && CanStealItem(battler, gBattlerAttacker, gBattleMons[gBattlerAttacker].item)) // Cannot steal plates, mega stones, etc + enum BattlerId battlerDef = battlers[i]; + if (battlerDef != gBattlerAttacker + && !IsBattlerUnaffectedByMove(battlerDef) + && GetBattlerAbility(battlerDef) == ABILITY_PICKPOCKET + && IsMoveMakingContact(gBattlerAttacker, battlerDef, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), gCurrentMove) + && IsBattlerTurnDamaged(battlerDef) + && !DoesSubstituteBlockMove(gBattlerAttacker, battlerDef, gCurrentMove) + && IsBattlerAlive(battlerDef) + && gBattleMons[battlerDef].item == ITEM_NONE + && CanStealItem(battlerDef, gBattlerAttacker, gBattleMons[gBattlerAttacker].item)) { - gBattlerTarget = gBattlerAbility = battler; + gBattlerTarget = gBattlerAbility = battlerDef; // Battle scripting is super brittle so we shall do the item exchange now (if possible) if (GetBattlerAbility(gBattlerAttacker) != ABILITY_STICKY_HOLD) - StealTargetItem(gBattlerTarget, gBattlerAttacker); // Target takes attacker's item + StealTargetItem(battlerDef, gBattlerAttacker); // Target takes attacker's item gEffectBattler = gBattlerAttacker; BattleScriptCall(BattleScript_Pickpocket); // Includes sticky hold check to print separate string - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; break; // Pickpocket activates on fastest mon, so exit loop. } } @@ -1522,9 +3170,9 @@ static enum MoveEndResult MoveEnd_Pickpocket(void) return result; } -static enum MoveEndResult MoveEnd_ThirdMoveBlock(void) +static enum MoveEndResult MoveEndThirdMoveBlock(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; enum BattleMoveEffects moveEffect = GetMoveEffect(gCurrentMove); switch (moveEffect) @@ -1533,7 +3181,7 @@ static enum MoveEndResult MoveEnd_ThirdMoveBlock(void) if (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY && IsBattlerTurnDamaged(gBattlerTarget)) { BattleScriptCall(BattleScript_RemoveTerrain); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_ICE_SPINNER: @@ -1543,7 +3191,7 @@ static enum MoveEndResult MoveEnd_ThirdMoveBlock(void) && IsBattlerTurnDamaged(gBattlerTarget)) { BattleScriptCall(BattleScript_RemoveTerrain); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; case EFFECT_NATURAL_GIFT: @@ -1565,7 +3213,7 @@ static enum MoveEndResult MoveEnd_ThirdMoveBlock(void) ClearBattlerItemEffectHistory(gBattlerAttacker); if (!TrySymbiosis(gBattlerAttacker, item, TRUE)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } break; default: @@ -1576,22 +3224,29 @@ static enum MoveEndResult MoveEnd_ThirdMoveBlock(void) return result; } -static enum MoveEndResult MoveEnd_ChangedItems(void) +static bool32 ShouldSetStompingTantrumTimer(void) { - for (u32 battler = 0; battler < gBattlersCount; battler++) + u32 numNotAffectedTargets = 0; + + if (gBattleStruct->pledgeMove == TRUE // Is the battler that uses the first Pledge move in the combo + || gBattleStruct->unableToUseMove) + return TRUE; + + if (!IsDoubleSpreadMove()) + return gBattleStruct->moveResultFlags[gBattlerTarget] & (MOVE_RESULT_FAILED | MOVE_RESULT_DOESNT_AFFECT_FOE); + + for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) { - if (gBattleStruct->changedItems[battler] != ITEM_NONE) - { - gBattleMons[battler].item = gBattleStruct->changedItems[battler]; - gBattleStruct->changedItems[battler] = ITEM_NONE; - } + if (gBattlerAttacker == battlerDef) + continue; + if (gBattleStruct->moveResultFlags[battlerDef] & (MOVE_RESULT_FAILED | MOVE_RESULT_DOESNT_AFFECT_FOE)) + numNotAffectedTargets++; } - gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return numNotAffectedTargets == gBattleStruct->numSpreadTargets; } -static enum MoveEndResult MoveEnd_ClearBits(void) +static enum MoveEndResult MoveEndClearBits(void) { ValidateBattlers(); @@ -1599,6 +3254,9 @@ static enum MoveEndResult MoveEnd_ClearBits(void) enum Type moveType = GetBattleMoveType(gCurrentMove); enum BattleMoveEffects moveEffect = GetMoveEffect(gCurrentMove); + if (ShouldSetStompingTantrumTimer()) + gBattleStruct->battlerState[gBattlerAttacker].stompingTantrumTimer = 2; + if (gSpecialStatuses[gBattlerAttacker].instructedChosenTarget) gBattleStruct->moveTarget[gBattlerAttacker] = gSpecialStatuses[gBattlerAttacker].instructedChosenTarget & 0x3; if (gSpecialStatuses[gBattlerAttacker].dancerOriginalTarget) @@ -1640,14 +3298,14 @@ static enum MoveEndResult MoveEnd_ClearBits(void) ExpendTypeStellarBoost(gBattlerAttacker, moveType); memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts)); - for (u32 i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { gBattleStruct->battlerState[gBattlerAttacker].targetsDone[i] = FALSE; gBattleMons[i].volatiles.tryEjectPack = FALSE; if (gBattleStruct->battlerState[i].commanderSpecies != SPECIES_NONE && !IsBattlerAlive(i)) { - u32 partner = BATTLE_PARTNER(i); + enum BattlerId partner = BATTLE_PARTNER(i); gBattleStruct->battlerState[i].commanderSpecies = SPECIES_NONE; if (IsBattlerAlive(partner)) gBattleMons[partner].volatiles.semiInvulnerable = STATE_NONE; @@ -1658,16 +3316,17 @@ static enum MoveEndResult MoveEnd_ClearBits(void) gBattleMons[gBattlerAttacker].volatiles.unableToUseMove = gBattleStruct->unableToUseMove; gBattleScripting.moveendState++; - return MOVEEND_STEP_CONTINUE; + return MOVEEND_RESULT_CONTINUE; } -static enum MoveEndResult MoveEnd_Dancer(void) +static enum MoveEndResult MoveEndDancer(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (IsDanceMove(gCurrentMove) && !gBattleStruct->snatchedMoveIsUsed) { - u32 battler, nextDancer = 0; + enum BattlerId battler; + u32 nextDancer = 0; bool32 hasDancerTriggered = FALSE; for (battler = 0; battler < gBattlersCount; battler++) @@ -1700,7 +3359,7 @@ static enum MoveEndResult MoveEnd_Dancer(void) } } if (nextDancer && AbilityBattleEffects(ABILITYEFFECT_MOVE_END_OTHER, nextDancer & 0x3, 0, gCurrentMove, TRUE)) - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } } @@ -1708,9 +3367,9 @@ static enum MoveEndResult MoveEnd_Dancer(void) return result; } -static enum MoveEndResult MoveEnd_PursuitNextAction(void) +static enum MoveEndResult MoveEndPursuitNextAction(void) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; if (gBattleStruct->battlerState[gBattlerTarget].pursuitTarget) { @@ -1730,7 +3389,7 @@ static enum MoveEndResult MoveEnd_PursuitNextAction(void) gBattlescriptCurrInstr = BattleScript_DoSwitchOut; gBattleStruct->monToSwitchIntoId[gBattlerTarget] = gBattleStruct->pursuitStoredSwitch; ClearPursuitValues(); - result = MOVEEND_STEP_RUN_SCRIPT; + result = MOVEEND_RESULT_RUN_SCRIPT; } } @@ -1740,69 +3399,69 @@ static enum MoveEndResult MoveEnd_PursuitNextAction(void) static enum MoveEndResult (*const sMoveEndHandlers[])(void) = { - [MOVEEND_SET_VALUES] = MoveEnd_SetValues, - [MOVEEND_PROTECT_LIKE_EFFECT] = MoveEnd_ProtectLikeEffect, - [MOVEEND_ABSORB] = MoveEnd_Absorb, - [MOVEEND_RAGE] = MoveEnd_Rage, - [MOVEEND_SYNCHRONIZE_TARGET] = MoveEnd_SynchronizeTarget, - [MOVEEND_ABILITIES] = MoveEnd_Abilities, - [MOVEEND_ABILITIES_ATTACKER] = MoveEnd_AbilitiesAttacker, - [MOVEEND_STATUS_IMMUNITY_ABILITIES] = MoveEnd_StatusImmunityAbilities, - [MOVEEND_SYNCHRONIZE_ATTACKER] = MoveEnd_SynchronizeAttacker, - [MOVEEND_ATTACKER_INVISIBLE] = MoveEnd_AttackerInvisible, - [MOVEEND_ATTACKER_VISIBLE] = MoveEnd_AttackerVisible, - [MOVEEND_TARGET_VISIBLE] = MoveEnd_TargetVisible, - [MOVEEND_ITEM_EFFECTS_TARGET] = MoveEnd_ItemEffectsTarget, - [MOVEEND_ITEM_EFFECTS_ATTACKER_1] = MoveEnd_ItemEffectsAttacker1, - [MOVEEND_SYMBIOSIS] = MoveEnd_Symbiosis, - [MOVEEND_SUBSTITUTE] = MoveEnd_Substitute, - [MOVEEND_FAINT_BLOCK] = MoveEnd_FaintBlock, - [MOVEEND_SKY_DROP_CONFUSE] = MoveEnd_SkyDropConfuse, - [MOVEEND_UPDATE_LAST_MOVES] = MoveEnd_UpdateLastMoves, - [MOVEEND_MIRROR_MOVE] = MoveEnd_MirrorMove, - [MOVEEND_DEFROST] = MoveEnd_Defrost, - [MOVEEND_NEXT_TARGET] = MoveEnd_NextTarget, - [MOVEEND_HP_THRESHOLD_ITEMS_TARGET] = MoveEnd_HpThresholdItemsTarget, - [MOVEEND_MULTIHIT_MOVE] = MoveEnd_MultihitMove, - [MOVEEND_MOVE_BLOCK] = MoveEnd_MoveBlock, - [MOVEEND_ITEM_EFFECTS_ATTACKER_2] = MoveEnd_ItemEffectsAttacker2, - [MOVEEND_ABILITY_EFFECT_FOES_FAINTED] = MoveEnd_AbilityEffectFoesFainted, - [MOVEEND_SHEER_FORCE] = MoveEnd_SheerForce, - [MOVEEND_SHELL_TRAP] = MoveEnd_ShellTrap, - [MOVEEND_COLOR_CHANGE] = MoveEnd_ColorChange, - [MOVEEND_KEE_MARANGA_HP_THRESHOLD_ITEM_TARGET] = MoveEnd_KeeMarangaHpThresholdItemTarget, - [MOVEEND_CARD_BUTTON] = MoveEnd_CardButton, - [MOVEEND_LIFE_ORB_SHELL_BELL] = MoveEnd_LifeOrbShellBell, - [MOVEEND_FORM_CHANGE] = MoveEnd_FormChange, - [MOVEEND_EMERGENCY_EXIT] = MoveEnd_EmergencyExit, - [MOVEEND_EJECT_PACK] = MoveEnd_EjectPack, - [MOVEEND_HIT_ESCAPE] = MoveEnd_HitEscape, - [MOVEEND_ITEMS_EFFECTS_ALL] = MoveEnd_ItemsEffectsAll, - [MOVEEND_WHITE_HERB] = MoveEnd_WhiteHerb, - [MOVEEND_OPPORTUNIST] = MoveEnd_Opportunist, - [MOVEEND_MIRROR_HERB] = MoveEnd_MirrorHerb, - [MOVEEND_PICKPOCKET] = MoveEnd_Pickpocket, - [MOVEEND_THIRD_MOVE_BLOCK] = MoveEnd_ThirdMoveBlock, - [MOVEEND_CHANGED_ITEMS] = MoveEnd_ChangedItems, - [MOVEEND_CLEAR_BITS] = MoveEnd_ClearBits, - [MOVEEND_DANCER] = MoveEnd_Dancer, - [MOVEEND_PURSUIT_NEXT_ACTION] = MoveEnd_PursuitNextAction, + [MOVEEND_SET_VALUES] = MoveEndSetValues, + [MOVEEND_PROTECT_LIKE_EFFECT] = MoveEndProtectLikeEffect, + [MOVEEND_ABSORB] = MoveEndAbsorb, + [MOVEEND_RAGE] = MoveEndRage, + [MOVEEND_SYNCHRONIZE_TARGET] = MoveEndSynchronizeTarget, + [MOVEEND_ABILITIES] = MoveEndAbilities, + [MOVEEND_FORM_CHANGE_ON_HIT] = MoveEndFormChangeOnHit, + [MOVEEND_ABILITIES_ATTACKER] = MoveEndAbilitiesAttacker, + [MOVEEND_STATUS_IMMUNITY_ABILITIES] = MoveEndStatusImmunityAbilities, + [MOVEEND_SYNCHRONIZE_ATTACKER] = MoveEndSynchronizeAttacker, + [MOVEEND_ATTACKER_INVISIBLE] = MoveEndAttackerInvisible, + [MOVEEND_ATTACKER_VISIBLE] = MoveEndAttackerVisible, + [MOVEEND_TARGET_VISIBLE] = MoveEndTargetVisible, + [MOVEEND_ITEM_EFFECTS_TARGET] = MoveEndItemEffectsTarget, + [MOVEEND_ITEM_EFFECTS_ATTACKER_1] = MoveEndItemEffectsAttacker1, + [MOVEEND_SYMBIOSIS] = MoveEndSymbiosis, + [MOVEEND_SUBSTITUTE] = MoveEndSubstitute, + [MOVEEND_FAINT_BLOCK] = MoveEndFaintBlock, + [MOVEEND_SKY_DROP_CONFUSE] = MoveEndSkyDropConfuse, + [MOVEEND_UPDATE_LAST_MOVES] = MoveEndUpdateLastMoves, + [MOVEEND_MIRROR_MOVE] = MoveEndMirrorMove, + [MOVEEND_DEFROST] = MoveEndDefrost, + [MOVEEND_NEXT_TARGET] = MoveEndNextTarget, + [MOVEEND_HP_THRESHOLD_ITEMS_TARGET] = MoveEndHpThresholdItemsTarget, + [MOVEEND_MULTIHIT_MOVE] = MoveEndMultihitMove, + [MOVEEND_MOVE_BLOCK] = MoveEndMoveBlock, + [MOVEEND_ITEM_EFFECTS_ATTACKER_2] = MoveEndItemEffectsAttacker2, + [MOVEEND_ABILITY_EFFECT_FOES_FAINTED] = MoveEndAbilityEffectFoesFainted, + [MOVEEND_SHEER_FORCE] = MoveEndSheerForce, + [MOVEEND_SHELL_TRAP] = MoveEndShellTrap, + [MOVEEND_COLOR_CHANGE] = MoveEndColorChange, + [MOVEEND_KEE_MARANGA_HP_THRESHOLD_ITEM_TARGET] = MoveEndKeeMarangaHpThresholdItemTarget, + [MOVEEND_CARD_BUTTON] = MoveEndCardButton, + [MOVEEND_LIFE_ORB_SHELL_BELL] = MoveEndLifeOrbShellBell, + [MOVEEND_FORM_CHANGE] = MoveEndFormChange, + [MOVEEND_EMERGENCY_EXIT] = MoveEndEmergencyExit, + [MOVEEND_EJECT_PACK] = MoveEndEjectPack, + [MOVEEND_HIT_ESCAPE] = MoveEndHitEscape, + [MOVEEND_ITEMS_EFFECTS_ALL] = MoveEndItemsEffectsAll, + [MOVEEND_WHITE_HERB] = MoveEndWhiteHerb, + [MOVEEND_OPPORTUNIST] = MoveEndOpportunist, + [MOVEEND_MIRROR_HERB] = MoveEndMirrorHerb, + [MOVEEND_PICKPOCKET] = MoveEndPickpocket, + [MOVEEND_THIRD_MOVE_BLOCK] = MoveEndThirdMoveBlock, + [MOVEEND_CLEAR_BITS] = MoveEndClearBits, + [MOVEEND_DANCER] = MoveEndDancer, + [MOVEEND_PURSUIT_NEXT_ACTION] = MoveEndPursuitNextAction, }; -bool32 DoMoveEnd(enum MoveEndState endMode, enum MoveEndState endState) +enum MoveEndResult DoMoveEnd(enum MoveEndState endMode, enum MoveEndState endState) { - enum MoveEndResult result = MOVEEND_STEP_CONTINUE; + enum MoveEndResult result = MOVEEND_RESULT_CONTINUE; do { result = sMoveEndHandlers[gBattleScripting.moveendState](); - if (endMode == 1 && result == MOVEEND_STEP_CONTINUE) + if (endMode == 1 && result == MOVEEND_RESULT_CONTINUE) gBattleScripting.moveendState = MOVEEND_COUNT; if (endMode == 2 && endState == gBattleScripting.moveendState) gBattleScripting.moveendState = MOVEEND_COUNT; - } while (gBattleScripting.moveendState != MOVEEND_COUNT && result == MOVEEND_STEP_CONTINUE); + } while (gBattleScripting.moveendState != MOVEEND_COUNT && result == MOVEEND_RESULT_CONTINUE); return result; } @@ -1826,7 +3485,7 @@ static enum Move GetOriginallyUsedMove(enum Move chosenMove) return (gChosenMove == MOVE_UNAVAILABLE) ? MOVE_NONE : gChosenMove; } -static void SetSameMoveTurnValues(u32 moveEffect) +static void SetSameMoveTurnValues(enum BattleMoveEffects moveEffect) { bool32 increment = IsAnyTargetAffected() && !gBattleStruct->unableToUseMove @@ -1876,14 +3535,14 @@ static void TryClearChargeVolatile(enum Type moveType) if (moveType == TYPE_ELECTRIC && gBattleMons[gBattlerAttacker].volatiles.chargeTimer == 1) gBattleMons[gBattlerAttacker].volatiles.chargeTimer = 0; - for (u32 battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { if (gBattleMons[battler].volatiles.chargeTimer == 2) // Has been set this turn by move or ability gBattleMons[battler].volatiles.chargeTimer--; } } -static inline bool32 IsBattlerUsingBeakBlast(u32 battler) +static inline bool32 IsBattlerUsingBeakBlast(enum BattlerId battler) { if (gChosenActionByBattler[battler] != B_ACTION_USE_MOVE) return FALSE; @@ -1899,3 +3558,283 @@ void MoveValuesCleanUp(void) gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_NONE; gBattleCommunication[MISS_TYPE] = 0; } + +static void RequestNonVolatileChangee(enum BattlerId battlerAtk) +{ + BtlController_EmitSetMonData( + battlerAtk, + B_COMM_TO_CONTROLLER, + REQUEST_STATUS_BATTLE, + 0, + 4, + &gBattleMons[battlerAtk].status1); + MarkBattlerForControllerExec(battlerAtk); +} + +static enum Move GetMirrorMoveMove(void) +{ + s32 i, validMovesCount; + enum Move move = MOVE_NONE; + enum Move validMoves[MAX_BATTLERS_COUNT] = {MOVE_NONE}; + + for (validMovesCount = 0, i = 0; i < gBattlersCount; i++) + { + if (i != gBattlerAttacker) + { + move = gBattleStruct->lastTakenMoveFrom[gBattlerAttacker][i]; + if (move != MOVE_NONE && move != MOVE_UNAVAILABLE) + { + validMoves[validMovesCount] = move; + validMovesCount++; + } + } + } + + move = gBattleStruct->lastTakenMove[gBattlerAttacker]; + if ((move == MOVE_NONE || move == MOVE_UNAVAILABLE) && validMovesCount != 0) + move = validMoves[Random() % validMovesCount]; + + if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IsBattleMoveStatus(move)) + move = GetTypeBasedZMove(move); + + return move; +} + +static bool32 InvalidMetronomeMove(u32 move) +{ + return GetMoveEffect(move) == EFFECT_PLACEHOLDER + || IsMoveMetronomeBanned(move); +} + +static enum Move GetMetronomeMove(void) +{ + enum Move move = MOVE_NONE; + +#if B_METRONOME_MOVES >= GEN_9 + u32 moveCount = MOVES_COUNT_GEN9; +#elif B_METRONOME_MOVES >= GEN_8 + u32 moveCount = MOVES_COUNT_GEN8; +#elif B_METRONOME_MOVES >= GEN_7 + u32 moveCount = MOVES_COUNT_GEN7; +#elif B_METRONOME_MOVES >= GEN_6 + u32 moveCount = MOVES_COUNT_GEN6; +#elif B_METRONOME_MOVES >= GEN_5 + u32 moveCount = MOVES_COUNT_GEN5; +#elif B_METRONOME_MOVES >= GEN_4 + u32 moveCount = MOVES_COUNT_GEN4; +#elif B_METRONOME_MOVES >= GEN_3 + u32 moveCount = MOVES_COUNT_GEN3; +#elif B_METRONOME_MOVES >= GEN_2 + u32 moveCount = MOVES_COUNT_GEN2; +#else + u32 moveCount = MOVES_COUNT_GEN1; +#endif + + move = RandomUniformExcept(RNG_METRONOME, 1, moveCount - 1, InvalidMetronomeMove); + return move; +} + +static enum Move GetAssistMove(void) +{ + enum Move move = MOVE_NONE; + u32 chooseableMovesNo = 0; + struct Pokemon *party; + enum Move validMoves[PARTY_SIZE * MAX_MON_MOVES] = {MOVE_NONE}; + + party = GetBattlerParty(gBattlerAttacker); + + for (u32 monId = 0; monId < PARTY_SIZE; monId++) + { + if (monId == gBattlerPartyIndexes[gBattlerAttacker]) + continue; + if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_NONE) + continue; + if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG) + continue; + + for (u32 moveId = 0; moveId < MAX_MON_MOVES; moveId++) + { + enum Move move = GetMonData(&party[monId], MON_DATA_MOVE1 + moveId); + + if (IsMoveAssistBanned(move)) + continue; + + validMoves[chooseableMovesNo++] = move; + } + } + + if (chooseableMovesNo) + move = validMoves[Random() % chooseableMovesNo]; + + return move; +} + +enum Move GetNaturePowerMove(void) +{ + enum Move move = gBattleEnvironmentInfo[gBattleEnvironment].naturePower; + if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN) + move = MOVE_MOONBLAST; + else if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) + move = MOVE_THUNDERBOLT; + else if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN) + move = MOVE_ENERGY_BALL; + else if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN) + move = MOVE_PSYCHIC; + else if (gBattleEnvironmentInfo[gBattleEnvironment].naturePower == MOVE_NONE) + move = B_NATURE_POWER_MOVES >= GEN_4 ? MOVE_TRI_ATTACK : MOVE_SWIFT; + + return move; +} + +static enum Move GetSleepTalkMove(void) +{ + enum Move move = MOVE_NONE; + + u32 i, unusableMovesBits = 0, movePosition; + + if (GetBattlerAbility(gBattlerAttacker) != ABILITY_COMATOSE + && !(gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP)) + return move; + + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (IsMoveSleepTalkBanned(gBattleMons[gBattlerAttacker].moves[i]) + || gBattleMoveEffects[GetMoveEffect(gBattleMons[gBattlerAttacker].moves[i])].twoTurnEffect) + unusableMovesBits |= (1 << (i)); + } + + unusableMovesBits = CheckMoveLimitations(gBattlerAttacker, unusableMovesBits, ~(MOVE_LIMITATION_PP | MOVE_LIMITATION_CHOICE_ITEM)); + if (unusableMovesBits == ALL_MOVES_MASK) // all 4 moves cannot be chosen + return move; + + // Set Sleep Talk as used move, so it works with Last Resort. + gBattleMons[gBattlerAttacker].volatiles.usedMoves |= 1u << gCurrMovePos; + do + { + movePosition = MOD(Random(), MAX_MON_MOVES); + } while ((1u << movePosition) & unusableMovesBits); + + move = gBattleMons[gBattlerAttacker].moves[movePosition]; + gCurrMovePos = movePosition; + + return move; +} + +static enum Move GetCopycatMove(void) +{ + if (gLastUsedMove == MOVE_NONE + || gLastUsedMove == MOVE_UNAVAILABLE + || IsMoveCopycatBanned(gLastUsedMove) + || IsZMove(gLastUsedMove)) + return MOVE_NONE; + + return gLastUsedMove; +} + +static enum Move GetMeFirstMove(void) +{ + enum Move move = GetBattlerChosenMove(gBattlerTarget); + + if (IsBattleMoveStatus(move) + || IsMoveMeFirstBanned(move) + || HasBattlerActedThisTurn(gBattlerTarget)) + return MOVE_NONE; + + return move; +} + +static bool32 CanBattlerBounceBackMove(struct BattleContext *ctx) +{ + return TryMagicBounce(ctx) || TryMagicCoat(ctx); +} + +static bool32 TryMagicBounce(struct BattleContext *ctx) +{ + if (!MoveCanBeBouncedBack(ctx->move)) + return FALSE; + + if (gBattleStruct->magicBounceActive || gBattleStruct->bouncedMoveIsUsed) + return FALSE; + + if (ctx->abilityDef != ABILITY_MAGIC_BOUNCE) + return FALSE; + + gBattleStruct->magicBounceActive = TRUE; + gBattleStruct->moveBouncer = ctx->battlerDef; + + return TRUE; +} + +static bool32 TryMagicCoat(struct BattleContext *ctx) +{ + if (!MoveCanBeBouncedBack(ctx->move) || gBattleStruct->magicBounceActive) // Magic Bounce has precedence over magic coat + return FALSE; + + if (gBattleStruct->magicCoatActive || gBattleStruct->bouncedMoveIsUsed) + return FALSE; + + if (!gProtectStructs[ctx->battlerDef].bounceMove) + return FALSE; + + gBattleStruct->magicCoatActive = TRUE; + gBattleStruct->moveBouncer = ctx->battlerDef; + + return TRUE; +} + +static bool32 TryActivatePowderStatus(enum Move move) +{ + enum Move partnerMove = GetBattlerChosenMove(BATTLE_PARTNER(gBattlerAttacker)); + if (!gBattleMons[gBattlerAttacker].volatiles.powder) + return FALSE; + if (GetBattleMoveType(move) == TYPE_FIRE && !gBattleStruct->pledgeMove) + return TRUE; + if (move == MOVE_FIRE_PLEDGE && partnerMove == MOVE_GRASS_PLEDGE) + return TRUE; + if (move == MOVE_GRASS_PLEDGE && partnerMove == MOVE_FIRE_PLEDGE && gBattleStruct->pledgeMove) + return TRUE; + return FALSE; +} + +static void CalculateMagnitudeDamage(void) +{ + u32 magnitude = RandomUniform(RNG_MAGNITUDE, 0, 99); + + if (magnitude < 5) + { + gBattleStruct->magnitudeBasePower = 10; + magnitude = 4; + } + else if (magnitude < 15) + { + gBattleStruct->magnitudeBasePower = 30; + magnitude = 5; + } + else if (magnitude < 35) + { + gBattleStruct->magnitudeBasePower = 50; + magnitude = 6; + } + else if (magnitude < 65) + { + gBattleStruct->magnitudeBasePower = 70; + magnitude = 7; + } + else if (magnitude < 85) + { + gBattleStruct->magnitudeBasePower = 90; + magnitude = 8; + } + else if (magnitude < 95) + { + gBattleStruct->magnitudeBasePower = 110; + magnitude = 9; + } + else + { + gBattleStruct->magnitudeBasePower = 150; + magnitude = 10; + } + + PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff1, 2, magnitude) +} diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 6f34ae992..7c0e420dc 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -43,10 +43,9 @@ #include "wild_encounter.h" #include "rtc.h" #include "party_menu.h" -#include "trainer_pokemon_sprites.h" -// #include "battle_arena.h" -// #include "battle_pike.h" -// #include "battle_pyramid.h" +#include "battle_arena.h" +#include "battle_pike.h" +#include "battle_pyramid.h" #include "field_specials.h" #include "pokemon_summary_screen.h" // #include "pokenav.h" @@ -77,6 +76,25 @@ #include "load_save.h" #include "test/test_runner_battle.h" +// Helper for accessing command arguments and advancing gBattlescriptCurrInstr. +// +// For example accuracycheck is defined as: +// +// .macro accuracycheck failInstr:req, move:req +// .byte 0x1 +// .4byte \failInstr +// .2byte \move +// .endm +// +// Which corresponds to: +// +// CMD_ARGS(const u8 *failInstr, u16 move); +// +// The arguments can be accessed as cmd->failInstr and cmd->move. +// gBattlescriptCurrInstr = cmd->nextInstr; advances to the next instruction. +#define CMD_ARGS(...) const struct __attribute__((packed)) { u8 opcode; RECURSIVELY(R_FOR_EACH(APPEND_SEMICOLON, __VA_ARGS__)) const u8 nextInstr[0]; } *const cmd UNUSED = (const void *)gBattlescriptCurrInstr +#define NATIVE_ARGS(...) CMD_ARGS(void (*func)(void), ##__VA_ARGS__) + // table to avoid ugly powing on gba (courtesy of doesnt) // this returns (i^2.5)/4 // the quarters cancel so no need to re-quadruple them in actual calculation @@ -315,7 +333,7 @@ enum GiveCaughtMonStates #define TAG_LVLUP_BANNER_MON_ICON 55130 -static u32 ChangeStatBuffs(u32 battler, s8 statValue, enum Stat statId, union StatChangeFlags flags, u32 stats, const u8 *BS_ptr); +static u32 ChangeStatBuffs(enum BattlerId battler, s8 statValue, enum Stat statId, union StatChangeFlags flags, u32 stats, const u8 *BS_ptr); static void InitLevelUpBanner(void); static bool8 SlideInLevelUpBanner(void); static bool8 SlideOutLevelUpBanner(void); @@ -332,7 +350,8 @@ static void RemoveAllTerrains(void); static bool32 CanAbilityPreventStatLoss(enum Ability abilityDef); static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u8 *failInstr); static void ResetValuesForCalledMove(void); -static bool32 CanAbilityShieldActivateForBattler(u32 battler); +static bool32 CanAbilityShieldActivateForBattler(enum BattlerId battler); +static void PlayAnimation(enum BattlerId battler, u8 animId, const u16 *argPtr, const u8 *nextInstr); static void Cmd_attackcanceler(void); static void Cmd_accuracycheck(void); @@ -341,7 +360,6 @@ static void Cmd_printselectionstringfromtable(void); static void Cmd_critcalc(void); static void Cmd_damagecalc(void); static void Cmd_typecalc(void); -static void Cmd_adjustdamage(void); static void Cmd_multihitresultmessage(void); static void Cmd_attackanimation(void); static void Cmd_waitanimation(void); @@ -393,7 +411,7 @@ static void Cmd_bichalfword(void); static void Cmd_bicword(void); static void Cmd_pause(void); static void Cmd_waitstate(void); -static void Cmd_isdmgblockedbydisguise(void); +static void Cmd_tryselfconfusiondmgformchange(void); static void Cmd_return(void); static void Cmd_end(void); static void Cmd_end2(void); @@ -452,25 +470,20 @@ static void Cmd_jumpifplayerran(void); static void Cmd_hpthresholds(void); static void Cmd_hpthresholds2(void); static void Cmd_useitemonopponent(void); -static void Cmd_unused_0x78(void); static void Cmd_setprotectlike(void); static void Cmd_tryexplosion(void); static void Cmd_setatkhptozero(void); static void Cmd_jumpifnexttargetvalid(void); static void Cmd_tryhealhalfhealth(void); -static void Cmd_unused_0x7e(void); static void Cmd_setfieldweather(void); static void Cmd_setreflect(void); static void Cmd_setseeded(void); static void Cmd_manipulatedamage(void); static void Cmd_trysetrest(void); -static void Cmd_unused_0x82(void); -static void Cmd_unused_0x83(void); static void Cmd_jumpifuproarwakes(void); static void Cmd_stockpile(void); static void Cmd_stockpiletobasedamage(void); static void Cmd_stockpiletohpheal(void); -static void Cmd_unused_0x88(void); static void Cmd_statbuffchange(void); static void Cmd_normalisebuffs(void); static void Cmd_setbide(void); @@ -493,19 +506,12 @@ static void Cmd_transformdataexecution(void); static void Cmd_setsubstitute(void); static void Cmd_mimicattackcopy(void); static void Cmd_setcalledmove(void); -static void Cmd_unused_0x9f(void); -static void Cmd_unused_0xA0(void); -static void Cmd_unused_0xA1(void); -static void Cmd_unused_0xA2(void); static void Cmd_disablelastusedattack(void); static void Cmd_trysetencore(void); static void Cmd_painsplitdmgcalc(void); static void Cmd_settypetorandomresistance(void); static void Cmd_setalwayshitflag(void); static void Cmd_copymovepermanently(void); -static void Cmd_unused_0xA9(void); -static void Cmd_unused_AA(void); -static void Cmd_unused_0xab(void); static void Cmd_settailwind(void); static void Cmd_tryspiteppreduce(void); static void Cmd_healpartystatus(void); @@ -513,35 +519,25 @@ static void Cmd_cursetarget(void); static void Cmd_trysetspikes(void); static void Cmd_setvolatile(void); static void Cmd_trysetperishsong(void); -static void Cmd_unused_0xb3(void); static void Cmd_jumpifconfusedandstatmaxed(void); -static void Cmd_unused_0xb5(void); static void Cmd_setembargo(void); static void Cmd_presentdamagecalculation(void); static void Cmd_setsafeguard(void); -static void Cmd_magnitudedamagecalculation(void); static void Cmd_jumpifnopursuitswitchdmg(void); static void Cmd_tryactivateitem(void); static void Cmd_halvehp(void); static void Cmd_copyfoestats(void); static void Cmd_rapidspinfree(void); -static void Cmd_unused_0xBF(void); static void Cmd_recoverbasedonsunlight(void); static void Cmd_setstickyweb(void); static void Cmd_selectfirstvalidtarget(void); static void Cmd_setfutureattack(void); static void Cmd_trydobeatup(void); static void Cmd_setsemiinvulnerablebit(void); -static void Cmd_unused_0xC6(void); -static void Cmd_unused_0xC7(void); -static void Cmd_unused_c8(void); static void Cmd_trymemento(void); static void Cmd_setforcedtarget(void); -static void Cmd_unused_0xcb(void); -static void Cmd_unused_0xCC(void); static void Cmd_curestatuswithmove(void); static void Cmd_settorment(void); -static void Cmd_unused_0xCF(void); static void Cmd_settaunt(void); static void Cmd_trysethelpinghand(void); static void Cmd_tryswapitems(void); @@ -550,29 +546,22 @@ static void Cmd_trywish(void); static void Cmd_settoxicspikes(void); static void Cmd_setgastroacid(void); static void Cmd_setyawn(void); -static void Cmd_unused0xd8(void); static void Cmd_setroom(void); static void Cmd_tryswapabilities(void); static void Cmd_tryimprison(void); static void Cmd_setstealthrock(void); static void Cmd_trysetvolatile(void); -static void Cmd_unused_0xde(void); static void Cmd_trysetmagiccoat(void); static void Cmd_trysetsnatch(void); -static void Cmd_unused2(void); static void Cmd_switchoutabilities(void); static void Cmd_jumpifhasnohp(void); -static void Cmd_unused_0xE4(void); static void Cmd_pickup(void); -static void Cmd_unused_0xE6(void); -static void Cmd_unused_0xE7(void); static void Cmd_settypebasedhalvers(void); static void Cmd_jumpifsubstituteblocks(void); static void Cmd_tryrecycleitem(void); static void Cmd_settypetoenvironment(void); static void Cmd_pursuitdoubles(void); static void Cmd_snatchsetbattlers(void); -static void Cmd_unused_0xee(void); static void Cmd_handleballthrow(void); static void Cmd_givecaughtmon(void); static void Cmd_trysetcaughtmondexflags(void); @@ -589,6 +578,7 @@ static void Cmd_averagestats(void); static void Cmd_jumpifcaptivateaffected(void); static void Cmd_setnonvolatilestatus(void); static void Cmd_tryoverwriteability(void); +static void Cmd_dummy(void); static void Cmd_callnative(void); void (*const gBattleScriptingCommandsTable[])(void) = @@ -600,7 +590,6 @@ void (*const gBattleScriptingCommandsTable[])(void) = [B_SCR_OP_CRITCALC] = Cmd_critcalc, [B_SCR_OP_DAMAGECALC] = Cmd_damagecalc, [B_SCR_OP_TYPECALC] = Cmd_typecalc, - [B_SCR_OP_ADJUSTDAMAGE] = Cmd_adjustdamage, [B_SCR_OP_MULTIHITRESULTMESSAGE] = Cmd_multihitresultmessage, [B_SCR_OP_ATTACKANIMATION] = Cmd_attackanimation, [B_SCR_OP_WAITANIMATION] = Cmd_waitanimation, @@ -652,7 +641,7 @@ void (*const gBattleScriptingCommandsTable[])(void) = [B_SCR_OP_BICWORD] = Cmd_bicword, [B_SCR_OP_PAUSE] = Cmd_pause, [B_SCR_OP_WAITSTATE] = Cmd_waitstate, - [B_SCR_OP_ISDMGBLOCKEDBYDISGUISE] = Cmd_isdmgblockedbydisguise, + [B_SCR_OP_TRYSELFCONFUSIONDMGFORMCHANGE] = Cmd_tryselfconfusiondmgformchange, [B_SCR_OP_RETURN] = Cmd_return, [B_SCR_OP_END] = Cmd_end, [B_SCR_OP_END2] = Cmd_end2, @@ -711,25 +700,20 @@ void (*const gBattleScriptingCommandsTable[])(void) = [B_SCR_OP_HPTHRESHOLDS] = Cmd_hpthresholds, [B_SCR_OP_HPTHRESHOLDS2] = Cmd_hpthresholds2, [B_SCR_OP_USEITEMONOPPONENT] = Cmd_useitemonopponent, - [B_SCR_OP_UNUSED_0X78] = Cmd_unused_0x78, [B_SCR_OP_SETPROTECTLIKE] = Cmd_setprotectlike, [B_SCR_OP_TRYEXPLOSION] = Cmd_tryexplosion, [B_SCR_OP_SETATKHPTOZERO] = Cmd_setatkhptozero, [B_SCR_OP_JUMPIFNEXTTARGETVALID] = Cmd_jumpifnexttargetvalid, [B_SCR_OP_TRYHEALHALFHEALTH] = Cmd_tryhealhalfhealth, - [B_SCR_OP_UNUSED_0X7E] = Cmd_unused_0x7e, [B_SCR_OP_SETFIELDWEATHER] = Cmd_setfieldweather, [B_SCR_OP_SETREFLECT] = Cmd_setreflect, [B_SCR_OP_SETSEEDED] = Cmd_setseeded, [B_SCR_OP_MANIPULATEDAMAGE] = Cmd_manipulatedamage, [B_SCR_OP_TRYSETREST] = Cmd_trysetrest, - [B_SCR_OP_UNUSED_0X82] = Cmd_unused_0x82, - [B_SCR_OP_UNUSED_0X83] = Cmd_unused_0x83, [B_SCR_OP_JUMPIFUPROARWAKES] = Cmd_jumpifuproarwakes, [B_SCR_OP_STOCKPILE] = Cmd_stockpile, [B_SCR_OP_STOCKPILETOBASEDAMAGE] = Cmd_stockpiletobasedamage, [B_SCR_OP_STOCKPILETOHPHEAL] = Cmd_stockpiletohpheal, - [B_SCR_OP_UNUSED_0X88] = Cmd_unused_0x88, [B_SCR_OP_STATBUFFCHANGE] = Cmd_statbuffchange, [B_SCR_OP_NORMALISEBUFFS] = Cmd_normalisebuffs, [B_SCR_OP_SETBIDE] = Cmd_setbide, @@ -752,19 +736,12 @@ void (*const gBattleScriptingCommandsTable[])(void) = [B_SCR_OP_SETSUBSTITUTE] = Cmd_setsubstitute, [B_SCR_OP_MIMICATTACKCOPY] = Cmd_mimicattackcopy, [B_SCR_OP_SETCALLEDMOVE] = Cmd_setcalledmove, - [B_SCR_OP_UNUSED_0X9F] = Cmd_unused_0x9f, - [B_SCR_OP_UNUSED_0XA0] = Cmd_unused_0xA0, - [B_SCR_OP_UNUSED_0XA1] = Cmd_unused_0xA1, - [B_SCR_OP_UNUSED_0XA2] = Cmd_unused_0xA2, [B_SCR_OP_DISABLELASTUSEDATTACK] = Cmd_disablelastusedattack, [B_SCR_OP_TRYSETENCORE] = Cmd_trysetencore, [B_SCR_OP_PAINSPLITDMGCALC] = Cmd_painsplitdmgcalc, [B_SCR_OP_SETTYPETORANDOMRESISTANCE] = Cmd_settypetorandomresistance, [B_SCR_OP_SETALWAYSHITFLAG] = Cmd_setalwayshitflag, [B_SCR_OP_COPYMOVEPERMANENTLY] = Cmd_copymovepermanently, - [B_SCR_OP_UNUSED_0XA9] = Cmd_unused_0xA9, - [B_SCR_OP_UNUSED_AA] = Cmd_unused_AA, - [B_SCR_OP_UNUSED_0XAB] = Cmd_unused_0xab, [B_SCR_OP_SETTAILWIND] = Cmd_settailwind, [B_SCR_OP_TRYSPITEPPREDUCE] = Cmd_tryspiteppreduce, [B_SCR_OP_HEALPARTYSTATUS] = Cmd_healpartystatus, @@ -772,35 +749,25 @@ void (*const gBattleScriptingCommandsTable[])(void) = [B_SCR_OP_TRYSETSPIKES] = Cmd_trysetspikes, [B_SCR_OP_SETVOLATILE] = Cmd_setvolatile, [B_SCR_OP_TRYSETPERISHSONG] = Cmd_trysetperishsong, - [B_SCR_OP_UNUSED_0XB3] = Cmd_unused_0xb3, [B_SCR_OP_JUMPIFCONFUSEDANDSTATMAXED] = Cmd_jumpifconfusedandstatmaxed, - [B_SCR_OP_UNUSED_0XB5] = Cmd_unused_0xb5, [B_SCR_OP_SETEMBARGO] = Cmd_setembargo, [B_SCR_OP_PRESENTDAMAGECALCULATION] = Cmd_presentdamagecalculation, [B_SCR_OP_SETSAFEGUARD] = Cmd_setsafeguard, - [B_SCR_OP_MAGNITUDEDAMAGECALCULATION] = Cmd_magnitudedamagecalculation, [B_SCR_OP_JUMPIFNOPURSUITSWITCHDMG] = Cmd_jumpifnopursuitswitchdmg, [B_SCR_OP_TRYACTIVATEITEM] = Cmd_tryactivateitem, [B_SCR_OP_HALVEHP] = Cmd_halvehp, [B_SCR_OP_COPYFOESTATS] = Cmd_copyfoestats, [B_SCR_OP_RAPIDSPINFREE] = Cmd_rapidspinfree, - [B_SCR_OP_UNUSED_0XBF] = Cmd_unused_0xBF, [B_SCR_OP_RECOVERBASEDONSUNLIGHT] = Cmd_recoverbasedonsunlight, [B_SCR_OP_SETSTICKYWEB] = Cmd_setstickyweb, [B_SCR_OP_SELECTFIRSTVALIDTARGET] = Cmd_selectfirstvalidtarget, [B_SCR_OP_SETFUTUREATTACK] = Cmd_setfutureattack, [B_SCR_OP_TRYDOBEATUP] = Cmd_trydobeatup, [B_SCR_OP_SETSEMIINVULNERABLEBIT] = Cmd_setsemiinvulnerablebit, - [B_SCR_OP_UNUSED_0XC6] = Cmd_unused_0xC6, - [B_SCR_OP_UNUSED_0XC7] = Cmd_unused_0xC7, - [B_SCR_OP_UNUSED_C8] = Cmd_unused_c8, [B_SCR_OP_TRYMEMENTO] = Cmd_trymemento, [B_SCR_OP_SETFORCEDTARGET] = Cmd_setforcedtarget, - [B_SCR_OP_UNUSED_0XCB] = Cmd_unused_0xcb, - [B_SCR_OP_UNUSED_0XCC] = Cmd_unused_0xCC, [B_SCR_OP_CURESTATUSWITHMOVE] = Cmd_curestatuswithmove, [B_SCR_OP_SETTORMENT] = Cmd_settorment, - [B_SCR_OP_UNUSED_0XCF] = Cmd_unused_0xCF, [B_SCR_OP_SETTAUNT] = Cmd_settaunt, [B_SCR_OP_TRYSETHELPINGHAND] = Cmd_trysethelpinghand, [B_SCR_OP_TRYSWAPITEMS] = Cmd_tryswapitems, @@ -809,29 +776,22 @@ void (*const gBattleScriptingCommandsTable[])(void) = [B_SCR_OP_SETTOXICSPIKES] = Cmd_settoxicspikes, [B_SCR_OP_SETGASTROACID] = Cmd_setgastroacid, [B_SCR_OP_SETYAWN] = Cmd_setyawn, - [B_SCR_OP_UNUSED0XD8] = Cmd_unused0xd8, [B_SCR_OP_SETROOM] = Cmd_setroom, [B_SCR_OP_TRYSWAPABILITIES] = Cmd_tryswapabilities, [B_SCR_OP_TRYIMPRISON] = Cmd_tryimprison, [B_SCR_OP_SETSTEALTHROCK] = Cmd_setstealthrock, [B_SCR_OP_TRYSETVOLATILE] = Cmd_trysetvolatile, - [B_SCR_OP_UNUSED_0XDE] = Cmd_unused_0xde, [B_SCR_OP_TRYSETMAGICCOAT] = Cmd_trysetmagiccoat, [B_SCR_OP_TRYSETSNATCH] = Cmd_trysetsnatch, - [B_SCR_OP_UNUSED2] = Cmd_unused2, [B_SCR_OP_SWITCHOUTABILITIES] = Cmd_switchoutabilities, [B_SCR_OP_JUMPIFHASNOHP] = Cmd_jumpifhasnohp, - [B_SCR_OP_UNUSED_0XE4] = Cmd_unused_0xE4, [B_SCR_OP_PICKUP] = Cmd_pickup, - [B_SCR_OP_UNUSED_0XE6] = Cmd_unused_0xE6, - [B_SCR_OP_UNUSED_0XE7] = Cmd_unused_0xE7, [B_SCR_OP_SETTYPEBASEDHALVERS] = Cmd_settypebasedhalvers, [B_SCR_OP_JUMPIFSUBSTITUTEBLOCKS] = Cmd_jumpifsubstituteblocks, [B_SCR_OP_TRYRECYCLEITEM] = Cmd_tryrecycleitem, [B_SCR_OP_SETTYPETOENVIRONMENT] = Cmd_settypetoenvironment, [B_SCR_OP_PURSUITDOUBLES] = Cmd_pursuitdoubles, [B_SCR_OP_SNATCHSETBATTLERS] = Cmd_snatchsetbattlers, - [B_SCR_OP_UNUSED_0XEE] = Cmd_unused_0xee, [B_SCR_OP_HANDLEBALLTHROW] = Cmd_handleballthrow, [B_SCR_OP_GIVECAUGHTMON] = Cmd_givecaughtmon, [B_SCR_OP_TRYSETCAUGHTMONDEXFLAGS] = Cmd_trysetcaughtmondexflags, @@ -848,6 +808,36 @@ void (*const gBattleScriptingCommandsTable[])(void) = [B_SCR_OP_JUMPIFCAPTIVATEAFFECTED] = Cmd_jumpifcaptivateaffected, [B_SCR_OP_SETNONVOLATILESTATUS] = Cmd_setnonvolatilestatus, [B_SCR_OP_TRYOVERWRITEABILITY] = Cmd_tryoverwriteability, + [B_SCR_OP_UNUSED_1] = Cmd_dummy, + [B_SCR_OP_UNUSED_2] = Cmd_dummy, + [B_SCR_OP_UNUSED_3] = Cmd_dummy, + [B_SCR_OP_UNUSED_4] = Cmd_dummy, + [B_SCR_OP_UNUSED_5] = Cmd_dummy, + [B_SCR_OP_UNUSED_6] = Cmd_dummy, + [B_SCR_OP_UNUSED_7] = Cmd_dummy, + [B_SCR_OP_UNUSED_8] = Cmd_dummy, + [B_SCR_OP_UNUSED_9] = Cmd_dummy, + [B_SCR_OP_UNUSED_10] = Cmd_dummy, + [B_SCR_OP_UNUSED_11] = Cmd_dummy, + [B_SCR_OP_UNUSED_12] = Cmd_dummy, + [B_SCR_OP_UNUSED_13] = Cmd_dummy, + [B_SCR_OP_UNUSED_14] = Cmd_dummy, + [B_SCR_OP_UNUSED_15] = Cmd_dummy, + [B_SCR_OP_UNUSED_16] = Cmd_dummy, + [B_SCR_OP_UNUSED_17] = Cmd_dummy, + [B_SCR_OP_UNUSED_18] = Cmd_dummy, + [B_SCR_OP_UNUSED_19] = Cmd_dummy, + [B_SCR_OP_UNUSED_20] = Cmd_dummy, + [B_SCR_OP_UNUSED_21] = Cmd_dummy, + [B_SCR_OP_UNUSED_22] = Cmd_dummy, + [B_SCR_OP_UNUSED_23] = Cmd_dummy, + [B_SCR_OP_UNUSED_24] = Cmd_dummy, + [B_SCR_OP_UNUSED_25] = Cmd_dummy, + [B_SCR_OP_UNUSED_26] = Cmd_dummy, + [B_SCR_OP_UNUSED_27] = Cmd_dummy, + [B_SCR_OP_UNUSED_28] = Cmd_dummy, + [B_SCR_OP_UNUSED_29] = Cmd_dummy, + [B_SCR_OP_UNUSED_30] = Cmd_dummy, [B_SCR_OP_CALLNATIVE] = Cmd_callnative, }; @@ -904,9 +894,6 @@ static const struct SpriteTemplate sSpriteTemplate_MonIconOnLvlUpBanner = .tileTag = TAG_LVLUP_BANNER_MON_ICON, .paletteTag = TAG_LVLUP_BANNER_MON_ICON, .oam = &sOamData_MonIconOnLvlUpBanner, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCB_MonIconOnLvlUpBanner }; @@ -948,7 +935,7 @@ static const struct PickupItem sPickupTable[] = #undef _ -bool32 ProteanTryChangeType(u32 battler, enum Ability ability, enum Move move, enum Type moveType) +bool32 ProteanTryChangeType(enum BattlerId battler, enum Ability ability, enum Move move, enum Type moveType) { if ((ability == ABILITY_PROTEAN || ability == ABILITY_LIBERO) && !gBattleMons[gBattlerAttacker].volatiles.usedProteanLibero @@ -970,7 +957,7 @@ u32 NumAffectedSpreadMoveTargets(void) if (!IsDoubleSpreadMove()) return targetCount; - for (u32 battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { if (battler == gBattlerAttacker) continue; @@ -982,7 +969,7 @@ u32 NumAffectedSpreadMoveTargets(void) return targetCount; } -static inline bool32 IsBattlerUsingBeakBlast(u32 battler) +static inline bool32 IsBattlerUsingBeakBlast(enum BattlerId battler) { if (gChosenActionByBattler[battler] != B_ACTION_USE_MOVE) return FALSE; @@ -1038,7 +1025,7 @@ static void Cmd_attackcanceler(void) return; } - if (AtkCanceler_MoveSuccessOrder() != MOVE_STEP_SUCCESS) + if (DoAttackCanceler() != CANCELER_RESULT_SUCCESS) return; if (gBattleStruct->magicBounceActive && !gBattleStruct->bouncedMoveIsUsed) @@ -1100,7 +1087,7 @@ static void Cmd_setchargingturn(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static bool32 ShouldSkipAccuracyCalcPastFirstHit(u32 battlerAtk, enum Ability abilityAtk, enum HoldEffect holdEffectAtk, u32 moveEffect) +static bool32 ShouldSkipAccuracyCalcPastFirstHit(enum BattlerId battlerAtk, enum Ability abilityAtk, enum HoldEffect holdEffectAtk, u32 moveEffect) { if (gSpecialStatuses[battlerAtk].parentalBondState == PARENTAL_BOND_2ND_HIT) return TRUE; @@ -1117,17 +1104,33 @@ static bool32 ShouldSkipAccuracyCalcPastFirstHit(u32 battlerAtk, enum Ability ab return FALSE; } +static bool32 ShouldBypassAccuracyCheckFrlg(void) +{ + if (!IS_FRLG) + return FALSE; + + if ((gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE + && (!BtlCtrl_OakOldMan_TestState2Flag(1) || !BtlCtrl_OakOldMan_TestState2Flag(2)) + && gMovesInfo[gCurrentMove].power != 0 + && GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)) + { + return TRUE; + } + + if (gBattleTypeFlags & BATTLE_TYPE_POKEDUDE) + return TRUE; + + return FALSE; +} + static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u8 *failInstr) { enum Ability abilityAtk = GetBattlerAbility(gBattlerAttacker); enum HoldEffect holdEffectAtk = GetBattlerHoldEffect(gBattlerAttacker); - enum BattleMoveEffects effect = GetMoveEffect(gCurrentMove); - if (IS_FRLG && - ((gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE && (!BtlCtrl_OakOldMan_TestState2Flag(1) || !BtlCtrl_OakOldMan_TestState2Flag(2)) - && gMovesInfo[gCurrentMove].power != 0 && GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) - || (gBattleTypeFlags & BATTLE_TYPE_POKEDUDE))) - { if (gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_MISSED) + if (ShouldBypassAccuracyCheckFrlg()) + { + if (gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_MISSED) { gBattleStruct->moveResultFlags[gBattlerTarget] = MOVE_RESULT_MISSED; gLastLandedMoves[gBattlerTarget] = 0; @@ -1138,9 +1141,10 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u { gBattlescriptCurrInstr = nextInstr; } + return; } - if (ShouldSkipAccuracyCalcPastFirstHit(gBattlerAttacker, abilityAtk, holdEffectAtk, effect) + if (ShouldSkipAccuracyCalcPastFirstHit(gBattlerAttacker, abilityAtk, holdEffectAtk, GetMoveEffect(gCurrentMove)) || IsMaxMove(gCurrentMove) || IsZMove(gCurrentMove)) { @@ -1152,7 +1156,7 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u enum MoveTarget moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); bool32 calcSpreadMove = IsSpreadMove(moveTarget); - for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) + for (enum BattlerId battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) { if (gBattleStruct->calculatedSpreadMoveAccuracy) break; @@ -1164,23 +1168,19 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u continue; numTargets++; - enum Ability abilityDef = GetBattlerAbility(battlerDef); - if (CanMoveSkipAccuracyCalc(gBattlerAttacker, battlerDef, abilityAtk, abilityDef, gCurrentMove, RUN_SCRIPT)) - continue; + struct BattleCalcValues cv = {0}; + cv.move = gCurrentMove; + cv.battlerAtk = gBattlerAttacker; + cv.battlerDef = battlerDef; + cv.abilities[gBattlerAttacker] = abilityAtk; + cv.abilities[battlerDef] = GetBattlerAbility(battlerDef); + cv.holdEffects[gBattlerAttacker] = holdEffectAtk; + cv.holdEffects[battlerDef] = GetBattlerHoldEffect(battlerDef); - u32 holdEffectDef = GetBattlerHoldEffect(battlerDef); - u32 accuracy = GetTotalAccuracy(gBattlerAttacker, - battlerDef, - gCurrentMove, - abilityAtk, - abilityDef, - holdEffectAtk, - holdEffectDef); - - if (!RandomPercentage(RNG_ACCURACY, accuracy)) + if (DoesMoveMissTarget(&cv)) { - gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_MISSED; + gBattleStruct->moveResultFlags[battlerDef] |= MOVE_RESULT_MISSED; gBattleCommunication[MISS_TYPE] = B_MSG_MISSED; numMisses++; @@ -1202,7 +1202,7 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u } } - if (numTargets == numMisses) + if (numTargets != 0 && numTargets == numMisses) { SetOrClearRageVolatile(); gBattleStruct->battlerState[gBattlerAttacker].stompingTantrumTimer = 2; @@ -1213,10 +1213,24 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u if (gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_MISSED) { - gBattleStruct->moveResultFlags[gBattlerTarget] = MOVE_RESULT_MISSED; gLastLandedMoves[gBattlerTarget] = 0; gLastHitByType[gBattlerTarget] = 0; - gBattlescriptCurrInstr = failInstr; + + if (gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_ONE_HIT_KO_STURDY) + { + gLastUsedAbility = ABILITY_STURDY; + gBattlescriptCurrInstr = BattleScript_SturdyPreventsOHKO; + gBattlerAbility = gBattlerTarget; + } + else + { + gBattlescriptCurrInstr = failInstr; + } + + if (gBattleStruct->moveResultFlags[gBattlerTarget] & (MOVE_RESULT_ONE_HIT_KO_NO_AFFECT | MOVE_RESULT_ONE_HIT_KO_STURDY)) + gBattleStruct->moveResultFlags[gBattlerTarget] = MOVE_RESULT_DOESNT_AFFECT_FOE; + else + gBattleStruct->moveResultFlags[gBattlerTarget] = MOVE_RESULT_MISSED; } else { @@ -1250,6 +1264,8 @@ static void Cmd_printselectionstringfromtable(void) { CMD_ARGS(const u16 *ptr); + assertf(gSelectionBattleScripts[gBattlerAttacker] != NULL, "wrong use of printselectionstringfromtable"); + if (gBattleControllerExecFlags == 0) { const u16 *ptr = cmd->ptr; @@ -1298,7 +1314,8 @@ static void Cmd_damagecalc(void) { CMD_ARGS(); - if (gBattleStruct->calculatedDamageDone) + // Test if unableToUseMove check is needed + if (gBattleStruct->calculatedDamageDone || gBattleStruct->unableToUseMove) { gBattlescriptCurrInstr = cmd->nextInstr; return; @@ -1316,8 +1333,7 @@ static void Cmd_damagecalc(void) if (IsSpreadMove(GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove))) { - u32 battlerDef; - for (battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) + for (enum BattlerId battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) { if (IsBattlerInvalidForSpreadMove(gBattlerAttacker, battlerDef)) continue; @@ -1325,6 +1341,7 @@ static void Cmd_damagecalc(void) ctx.battlerDef = battlerDef; CalculateAndSetMoveDamage(&ctx); } + gBattleStruct->calculatedDamageDone = TRUE; } else { @@ -1333,6 +1350,14 @@ static void Cmd_damagecalc(void) } gBattlescriptCurrInstr = cmd->nextInstr; + + if (gSpecialStatuses[gBattlerAttacker].gemBoost + && gBattleMons[gBattlerAttacker].item + && IsAnyTargetAffected()) + { + BattleScriptCall(BattleScript_GemActivates); + gLastUsedItem = gBattleMons[gBattlerAttacker].item; + } } static void Cmd_typecalc(void) @@ -1355,120 +1380,6 @@ static void Cmd_typecalc(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_adjustdamage(void) -{ - CMD_ARGS(); - - enum HoldEffect holdEffect; - u8 param; - u32 battlerDef; - u32 rand = Random() % 100; - u32 affectionScore; - enum BattleMoveEffects moveEffect = GetMoveEffect(gCurrentMove); - bool32 calcSpreadMoveDamage = IsSpreadMove(GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove)); - u32 enduredHit = 0; - - for (battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) - { - if (gBattleStruct->calculatedDamageDone) - break; - - if (!calcSpreadMoveDamage && battlerDef != gBattlerTarget) - continue; - - if (IsBattlerInvalidForSpreadMove(gBattlerAttacker, battlerDef)) - continue; - - if (DoesSubstituteBlockMove(gBattlerAttacker, battlerDef, gCurrentMove)) - continue; - - if (DoesDisguiseBlockMove(battlerDef, gCurrentMove)) - continue; - - if (GetBattlerAbility(battlerDef) == ABILITY_ICE_FACE && IsBattleMovePhysical(gCurrentMove) && gBattleMons[battlerDef].species == SPECIES_EISCUE) - { - // Damage deals typeless 0 HP. - gBattleStruct->moveResultFlags[battlerDef] &= ~(MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE); - gBattleStruct->moveDamage[battlerDef] = 0; - RecordAbilityBattle(battlerDef, ABILITY_ICE_FACE); - gBattleMons[battlerDef].volatiles.triggerIceFace = TRUE; - // Form change will be done after attack animation in Cmd_resultmessage. - continue; - } - - if (gBattleMons[battlerDef].hp > gBattleStruct->moveDamage[battlerDef]) - continue; - - holdEffect = GetBattlerHoldEffect(battlerDef); - param = GetBattlerHoldEffectParam(battlerDef); - affectionScore = GetBattlerAffectionHearts(battlerDef); - - gPotentialItemEffectBattler = battlerDef; - - if (moveEffect == EFFECT_FALSE_SWIPE) - { - enduredHit |= 1u << battlerDef; - } - else if (gBattleMons[battlerDef].volatiles.endured) - { - enduredHit |= 1u << battlerDef; - gBattleStruct->moveResultFlags[battlerDef] |= MOVE_RESULT_FOE_ENDURED; - } - else if (holdEffect == HOLD_EFFECT_FOCUS_BAND && rand < param) - { - enduredHit |= 1u << battlerDef; - RecordItemEffectBattle(battlerDef, holdEffect); - gLastUsedItem = gBattleMons[battlerDef].item; - gBattleStruct->moveResultFlags[battlerDef] |= MOVE_RESULT_FOE_HUNG_ON; - } - else if (GetConfig(CONFIG_STURDY) >= GEN_5 && GetBattlerAbility(battlerDef) == ABILITY_STURDY && IsBattlerAtMaxHp(battlerDef)) - { - enduredHit |= 1u << battlerDef; - RecordAbilityBattle(battlerDef, ABILITY_STURDY); - gLastUsedAbility = ABILITY_STURDY; - gBattleStruct->moveResultFlags[battlerDef] |= MOVE_RESULT_STURDIED; - } - else if (holdEffect == HOLD_EFFECT_FOCUS_SASH && IsBattlerAtMaxHp(battlerDef)) - { - enduredHit |= 1u << battlerDef; - RecordItemEffectBattle(battlerDef, holdEffect); - gLastUsedItem = gBattleMons[battlerDef].item; - gBattleStruct->moveResultFlags[battlerDef] |= MOVE_RESULT_FOE_HUNG_ON; - } - else if (B_AFFECTION_MECHANICS == TRUE && IsOnPlayerSide(battlerDef) && affectionScore >= AFFECTION_THREE_HEARTS) - { - if ((affectionScore == AFFECTION_FIVE_HEARTS && rand < 20) - || (affectionScore == AFFECTION_FOUR_HEARTS && rand < 15) - || (affectionScore == AFFECTION_THREE_HEARTS && rand < 10)) - { - enduredHit |= 1u << battlerDef; - gBattleStruct->moveResultFlags[battlerDef] |= MOVE_RESULT_FOE_ENDURED_AFFECTION; - } - } - - // Handle reducing the dmg to 1 hp. - if (enduredHit & 1u << battlerDef) - { - gBattleStruct->moveDamage[battlerDef] = gBattleMons[battlerDef].hp - 1; - gProtectStructs[battlerDef].assuranceDoubled = TRUE; - } - } - - if (calcSpreadMoveDamage) - gBattleStruct->calculatedDamageDone = TRUE; - - gBattlescriptCurrInstr = cmd->nextInstr; - - if (gSpecialStatuses[gBattlerAttacker].gemBoost - && !IsBattlerUnaffectedByMove(gBattlerTarget) - && !gBattleStruct->unableToUseMove - && gBattleMons[gBattlerAttacker].item) - { - BattleScriptCall(BattleScript_GemActivates); - gLastUsedItem = gBattleMons[gBattlerAttacker].item; - } -} - static void Cmd_multihitresultmessage(void) { CMD_ARGS(); @@ -1497,16 +1408,16 @@ static void Cmd_multihitresultmessage(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static inline bool32 DoesBattlerNegateDamage(u32 battler) +static inline bool32 DoesBattlerNegateDamage(enum BattlerId battler) { u32 species = gBattleMons[battler].species; enum Ability ability = GetBattlerAbility(battler); if (gBattleMons[battler].volatiles.transformed) return FALSE; - if (ability == ABILITY_DISGUISE && species == SPECIES_MIMIKYU) + if (ability == ABILITY_DISGUISE && IsMimikyuDisguised(battler)) return TRUE; - if (ability == ABILITY_ICE_FACE && species == SPECIES_EISCUE && GetBattleMoveCategory(gCurrentMove) == DAMAGE_CATEGORY_PHYSICAL) + if (ability == ABILITY_ICE_FACE && species == SPECIES_EISCUE_ICE && GetBattleMoveCategory(gCurrentMove) == DAMAGE_CATEGORY_PHYSICAL) return TRUE; return FALSE; @@ -1517,7 +1428,7 @@ static u32 UpdateEffectivenessResultFlagsForDoubleSpreadMoves(u32 resultFlags) // Only play the "best" sound for (u32 sound = 0; sound < 3; sound++) { - for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) + for (enum BattlerId battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) { if (IsBattlerInvalidForSpreadMove(gBattlerAttacker, battlerDef)) continue; @@ -1545,7 +1456,7 @@ static u32 UpdateEffectivenessResultFlagsForDoubleSpreadMoves(u32 resultFlags) return resultFlags; } -static inline bool32 TryStrongWindsWeakenAttack(u32 battlerDef, enum Type moveType) +static inline bool32 TryStrongWindsWeakenAttack(enum BattlerId battlerDef, enum Type moveType) { if (gBattleWeather & B_WEATHER_STRONG_WINDS && HasWeatherEffect()) { @@ -1563,7 +1474,7 @@ static inline bool32 TryStrongWindsWeakenAttack(u32 battlerDef, enum Type moveTy return FALSE; } -static inline bool32 TryTeraShellDistortTypeMatchups(u32 battlerDef) +static inline bool32 TryTeraShellDistortTypeMatchups(enum BattlerId battlerDef) { if (gSpecialStatuses[battlerDef].teraShellAbilityDone) { @@ -1577,7 +1488,7 @@ static inline bool32 TryTeraShellDistortTypeMatchups(u32 battlerDef) // According to Gen5 Weakness berry activation happens after the attackanimation. // It doesn't have any impact on gameplay and is only a visual thing which can be adjusted later. -static inline bool32 TryActivateWeaknessBerry(u32 battlerDef) +static inline bool32 TryActivateWeaknessBerry(enum BattlerId battlerDef) { if (DoesDisguiseBlockMove(battlerDef, gCurrentMove)) { @@ -1603,7 +1514,7 @@ static bool32 ProcessPreAttackAnimationFuncs(void) { if (!gBattleStruct->printedStrongWindsWeakenedAttack) { - for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) + for (enum BattlerId battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) { if (IsBattlerInvalidForSpreadMove(gBattlerAttacker, battlerDef)) continue; @@ -1612,7 +1523,7 @@ static bool32 ProcessPreAttackAnimationFuncs(void) } } - for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) + for (enum BattlerId battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) { if (IsBattlerInvalidForSpreadMove(gBattlerAttacker, battlerDef)) continue; @@ -1642,7 +1553,7 @@ static void Cmd_attackanimation(void) if (gBattleControllerExecFlags || ProcessPreAttackAnimationFuncs()) return; - u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); + enum MoveTarget moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); u32 moveResultFlags = gBattleStruct->moveResultFlags[gBattlerTarget]; enum BattleMoveEffects effect = GetMoveEffect(gCurrentMove); @@ -1735,11 +1646,20 @@ static void Cmd_waitanimation(void) { CMD_ARGS(); - if (gBattleControllerExecFlags == 0 && gBattleStruct->battlerKOAnimsRunning == 0) + if (gBattleControllerExecFlags != 0 || gBattleStruct->battlerKOAnimsRunning != 0) + return; + + #if T_SHOULD_RUN_MOVE_ANIM + gCountAllocs = FALSE; + #endif + + if (TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_HP_PERCENT_DURING_MOVE, GetBattlerAbility(gBattlerAttacker))) + { + // Only execute B_ANIM_FORM_CHANGE_INSTANT for those who have changed forms + PlayAnimation(gBattlerAttacker, B_ANIM_FORM_CHANGE_INSTANT, NULL, cmd->nextInstr); + } + else { -#if T_SHOULD_RUN_MOVE_ANIM - gCountAllocs = FALSE; -#endif gBattlescriptCurrInstr = cmd->nextInstr; } } @@ -1749,12 +1669,10 @@ static void DoublesHPBarReduction(void) if (gBattleStruct->doneDoublesSpreadHit) return; - for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) + for (enum BattlerId battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) { if (IsBattlerUnaffectedByMove(battlerDef) - || gBattleStruct->moveDamage[battlerDef] == 0 - || DoesSubstituteBlockMove(gBattlerAttacker, battlerDef, gCurrentMove) - || DoesDisguiseBlockMove(battlerDef, gCurrentMove)) + || gBattleStruct->moveDamage[battlerDef] == 0) continue; s32 dmgUpdate = min(gBattleStruct->moveDamage[battlerDef], 10000); @@ -1770,7 +1688,7 @@ static void DoublesHPBarReduction(void) static void Cmd_healthbarupdate(void) { CMD_ARGS(u8 battler, u8 updateState); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (gBattleControllerExecFlags) return; @@ -1807,7 +1725,7 @@ static void Cmd_healthbarupdate(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void PassiveDataHpUpdate(u32 battler, const u8 *nextInstr) +static void PassiveDataHpUpdate(enum BattlerId battler, const u8 *nextInstr) { if (gBattleStruct->passiveHpUpdate[battler] < 0) { @@ -1839,7 +1757,7 @@ static void PassiveDataHpUpdate(u32 battler, const u8 *nextInstr) gBattlescriptCurrInstr = nextInstr; } -static void MoveDamageDataHpUpdate(u32 battler, u32 scriptBattler, const u8 *nextInstr) +static void MoveDamageDataHpUpdate(enum BattlerId battler, u32 scriptBattler, const u8 *nextInstr) { if (IsBattlerUnaffectedByMove(gBattlerTarget)) { @@ -1871,24 +1789,15 @@ static void MoveDamageDataHpUpdate(u32 battler, u32 scriptBattler, const u8 *nex } return; } - else if (DoesDisguiseBlockMove(battler, gCurrentMove)) + else if (DoesDisguiseBlockMove(battler, gCurrentMove) + || DoesIceFaceBlockMove(battler, gCurrentMove)) { - // TODO: Convert this to a proper FORM_CHANGE type. - gBattleScripting.battler = battler; + // Damage deals typeless 0 HP. + gBattleStruct->moveResultFlags[battler] &= ~(MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE); gBattleStruct->moveDamage[battler] = 0; - gBattleStruct->moveResultFlags[battler] &= ~(MOVE_RESULT_NOT_VERY_EFFECTIVE | MOVE_RESULT_SUPER_EFFECTIVE); if (GetMoveEffect(gCurrentMove) == EFFECT_OHKO) gProtectStructs[battler].survivedOHKO = TRUE; - if (GetBattlerPartyState(battler)->changedSpecies == SPECIES_NONE) - GetBattlerPartyState(battler)->changedSpecies = gBattleMons[battler].species; - if (gBattleMons[battler].species == SPECIES_MIMIKYU_TOTEM_DISGUISED) - gBattleMons[battler].species = SPECIES_MIMIKYU_BUSTED_TOTEM; - else - gBattleMons[battler].species = SPECIES_MIMIKYU_BUSTED; - if (GetConfig(CONFIG_DISGUISE_HP_LOSS) >= GEN_8) - SetPassiveDamageAmount(battler, GetNonDynamaxMaxHP(battler) / 8); - BattleScriptPush(nextInstr); - gBattlescriptCurrInstr = BattleScript_TargetFormChange; + gBattlescriptCurrInstr = nextInstr; } else { @@ -1953,7 +1862,7 @@ static void MoveDamageDataHpUpdate(u32 battler, u32 scriptBattler, const u8 *nex static void Cmd_datahpupdate(void) { CMD_ARGS(u8 battler, u8 updateState); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (gBattleControllerExecFlags) return; @@ -2066,7 +1975,7 @@ static inline bool32 ShouldPrintTwoFoesMessage(u32 moveResult) static inline bool32 ShouldRelyOnTwoFoesMessage(u32 moveResult) { - u32 oppositeTarget = BATTLE_OPPOSITE(gBattlerAttacker); + enum BattlerId oppositeTarget = BATTLE_OPPOSITE(gBattlerAttacker); return gBattlerTarget == BATTLE_PARTNER(oppositeTarget) && gBattleStruct->moveResultFlags[oppositeTarget] & moveResult && !(gBattleStruct->moveResultFlags[oppositeTarget] & MOVE_RESULT_AVOIDED_ATTACK); @@ -2082,19 +1991,6 @@ static void Cmd_resultmessage(void) if (gBattleControllerExecFlags) return; - // TODO: Convert this to a proper FORM_CHANGE type. - // Do Ice Face form change which was set up in Cmd_adjustdamage. - if (gBattleMons[gBattlerTarget].volatiles.triggerIceFace) - { - gBattleMons[gBattlerTarget].volatiles.triggerIceFace = FALSE; - if (GetBattlerPartyState(gBattlerTarget)->changedSpecies == SPECIES_NONE) - GetBattlerPartyState(gBattlerTarget)->changedSpecies = gBattleMons[gBattlerTarget].species; - gBattleMons[gBattlerTarget].species = SPECIES_EISCUE_NOICE; - gBattleScripting.battler = gBattlerTarget; // For STRINGID_PKMNTRANSFORMED - BattleScriptCall(BattleScript_IceFaceNullsDamage); - return; - } - if (*moveResultFlags & MOVE_RESULT_MISSED && !(*moveResultFlags & MOVE_RESULT_DOESNT_AFFECT_FOE)) { if (gMultiHitCounter && gMultiHitCounter < GetMoveStrikeCount(gCurrentMove)) @@ -2256,6 +2152,8 @@ static void Cmd_printselectionstring(void) { CMD_ARGS(u16 id); + assertf(gSelectionBattleScripts[gBattlerAttacker] != NULL, "wrong use of printselectionstring"); + BtlController_EmitPrintSelectionString(gBattlerAttacker, B_COMM_TO_CONTROLLER, cmd->id); MarkBattlerForControllerExec(gBattlerAttacker); @@ -2303,10 +2201,10 @@ static void Cmd_printfromtable(void) } } -bool32 HasBattlerActedThisTurn(u32 battler) +bool32 HasBattlerActedThisTurn(enum BattlerId battler) { u32 i; - for (i = 0; i < gCurrentTurnActionNumber; i++) + for (i = 0; i <= gCurrentTurnActionNumber; i++) { if (gBattlerByTurnOrder[i] == battler) return TRUE; @@ -2314,7 +2212,7 @@ bool32 HasBattlerActedThisTurn(u32 battler) return FALSE; } -u32 GetBattlerTurnOrderNum(u32 battler) +u32 GetBattlerTurnOrderNum(enum BattlerId battler) { u32 i; for (i = 0; i < gBattlersCount; i++) @@ -2326,7 +2224,7 @@ u32 GetBattlerTurnOrderNum(u32 battler) } // battlerStealer steals the item of itemBattler -void StealTargetItem(u8 battlerStealer, u8 itemBattler) +void StealTargetItem(enum BattlerId battlerStealer, enum BattlerId itemBattler) { gLastUsedItem = gBattleMons[itemBattler].item; gBattleMons[itemBattler].item = ITEM_NONE; @@ -2360,7 +2258,7 @@ void StealTargetItem(u8 battlerStealer, u8 itemBattler) TrySaveExchangedItem(itemBattler, gLastUsedItem); } -static inline bool32 TrySetReflect(u32 battler) +static inline bool32 TrySetReflect(enum BattlerId battler) { enum BattleSide side = GetBattlerSide(battler); if (!(gSideStatuses[side] & SIDE_STATUS_REFLECT)) @@ -2381,7 +2279,7 @@ static inline bool32 TrySetReflect(u32 battler) return FALSE; } -static inline bool32 TrySetLightScreen(u32 battler) +static inline bool32 TrySetLightScreen(enum BattlerId battler) { enum BattleSide side = GetBattlerSide(battler); if (!(gSideStatuses[side] & SIDE_STATUS_LIGHTSCREEN)) @@ -2402,7 +2300,7 @@ static inline bool32 TrySetLightScreen(u32 battler) return FALSE; } -static void SetNonVolatileStatus(u32 effectBattler, enum MoveEffect effect, const u8 *battleScript, enum StatusTrigger trigger) +static void SetNonVolatileStatus(enum BattlerId effectBattler, enum MoveEffect effect, const u8 *battleScript, enum StatusTrigger trigger) { gEffectBattler = effectBattler; @@ -2479,7 +2377,7 @@ static void SetNonVolatileStatus(u32 effectBattler, enum MoveEffect effect, cons } // To avoid confusion the arguments are naned battler/effectBattler since they can be different from gBattlerAttacker/gBattlerTarget -void SetMoveEffect(u32 battlerAtk, u32 effectBattler, enum MoveEffect moveEffect, const u8 *battleScript, enum SetMoveEffectFlags effectFlags) +void SetMoveEffect(enum BattlerId battlerAtk, enum BattlerId effectBattler, enum MoveEffect moveEffect, const u8 *battleScript, enum SetMoveEffectFlags effectFlags) { enum Ability abilities[MAX_BATTLERS_COUNT] = {ABILITY_NONE}; abilities[battlerAtk] = GetBattlerAbility(battlerAtk); @@ -2633,7 +2531,7 @@ void SetMoveEffect(u32 battlerAtk, u32 effectBattler, enum MoveEffect moveEffect if (IsOnPlayerSide(gBattlerAttacker) && gSpecialStatuses[gBattlerAttacker].parentalBondState!= PARENTAL_BOND_2ND_HIT) { u16 payday = gPaydayMoney; - u16 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); + enum MoveTarget moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); gPaydayMoney += (gBattleMons[gBattlerAttacker].level * 5); if (payday > gPaydayMoney) gPaydayMoney = 0xFFFF; @@ -2898,7 +2796,7 @@ void SetMoveEffect(u32 battlerAtk, u32 effectBattler, enum MoveEffect moveEffect && !IsSemiInvulnerable(BATTLE_PARTNER(gBattlerTarget), CHECK_ALL) && GetBattlerAbility(BATTLE_PARTNER(gBattlerTarget)) != ABILITY_MAGIC_GUARD) { - u32 partnerTarget = BATTLE_PARTNER(gBattlerTarget); + enum BattlerId partnerTarget = BATTLE_PARTNER(gBattlerTarget); gBattleScripting.battler = partnerTarget; SetPassiveDamageAmount(partnerTarget, gBattleMons[partnerTarget].maxHP / 16); BattleScriptPush(battleScript); @@ -2921,7 +2819,7 @@ void SetMoveEffect(u32 battlerAtk, u32 effectBattler, enum MoveEffect moveEffect if (i) { BattleScriptPush(battleScript); - if (gCurrentMove == MOVE_HYPERSPACE_FURY) + if (GetMoveEffect(gCurrentMove) == EFFECT_HYPERSPACE_FURY) gBattlescriptCurrInstr = BattleScript_HyperspaceFuryRemoveProtect; else gBattlescriptCurrInstr = BattleScript_MoveEffectFeint; @@ -3076,7 +2974,7 @@ void SetMoveEffect(u32 battlerAtk, u32 effectBattler, enum MoveEffect moveEffect break; case MOVE_EFFECT_PSYCHIC_NOISE: { - u32 battler = IsAbilityOnSide(gEffectBattler, ABILITY_AROMA_VEIL); + enum BattlerId battler = IsAbilityOnSide(gEffectBattler, ABILITY_AROMA_VEIL); if (battler) { @@ -3151,8 +3049,8 @@ void SetMoveEffect(u32 battlerAtk, u32 effectBattler, enum MoveEffect moveEffect } break; case MOVE_EFFECT_HAZE: - for (i = 0; i < gBattlersCount; i++) - TryResetBattlerStatChanges(i); + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) + TryResetBattlerStatChanges(battler); BattleScriptPush(battleScript); gBattlescriptCurrInstr = BattleScript_MoveEffectHaze; break; @@ -3436,7 +3334,7 @@ void SetMoveEffect(u32 battlerAtk, u32 effectBattler, enum MoveEffect moveEffect case MOVE_EFFECT_SANDBLAST_SIDE: case MOVE_EFFECT_FIRE_SPIN_SIDE: // Affects both opponents, but doesn't print strings so we can handle it here. - for (u32 battler = 0; battler < MAX_BATTLERS_COUNT; ++battler) + for (enum BattlerId battler = 0; battler < MAX_BATTLERS_COUNT; ++battler) { if (!IsBattlerAlly(battler, gBattlerTarget)) continue; @@ -3607,47 +3505,43 @@ void SetMoveEffect(u32 battlerAtk, u32 effectBattler, enum MoveEffect moveEffect break; case MOVE_EFFECT_STEAL_STATS: if (gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT) - { break; - } - else + + bool32 contrary = abilities[gBattlerAttacker] == ABILITY_CONTRARY; + gBattleStruct->stolenStats[0] = 0; // Stats to steal. + gBattleScripting.animArg1 = 0; + for (enum Stat stat = STAT_ATK; stat < NUM_BATTLE_STATS; stat++) { - bool32 contrary = abilities[gBattlerAttacker] == ABILITY_CONTRARY; - gBattleStruct->stolenStats[0] = 0; // Stats to steal. - gBattleScripting.animArg1 = 0; - for (enum Stat stat = STAT_ATK; stat < NUM_BATTLE_STATS; stat++) + if (gBattleMons[gBattlerTarget].statStages[stat] > DEFAULT_STAT_STAGE && gBattleMons[gBattlerAttacker].statStages[stat] != MAX_STAT_STAGE) { - if (gBattleMons[gBattlerTarget].statStages[stat] > DEFAULT_STAT_STAGE && gBattleMons[gBattlerAttacker].statStages[stat] != MAX_STAT_STAGE) + bool32 byTwo = FALSE; + + gBattleStruct->stolenStats[0] |= (1 << (stat)); + // Store by how many stages to raise the stat. + gBattleStruct->stolenStats[stat] = gBattleMons[gBattlerTarget].statStages[stat] - DEFAULT_STAT_STAGE; + + while (gBattleMons[gBattlerAttacker].statStages[stat] + gBattleStruct->stolenStats[stat] > MAX_STAT_STAGE) + gBattleStruct->stolenStats[stat]--; + + gBattleMons[gBattlerTarget].statStages[stat] = DEFAULT_STAT_STAGE; + + if (gBattleStruct->stolenStats[stat] >= 2) + byTwo++; + + if (gBattleScripting.animArg1 == 0) { - bool32 byTwo = FALSE; - - gBattleStruct->stolenStats[0] |= (1 << (stat)); - // Store by how many stages to raise the stat. - gBattleStruct->stolenStats[stat] = gBattleMons[gBattlerTarget].statStages[stat] - DEFAULT_STAT_STAGE; - - while (gBattleMons[gBattlerAttacker].statStages[stat] + gBattleStruct->stolenStats[stat] > MAX_STAT_STAGE) - gBattleStruct->stolenStats[stat]--; - - gBattleMons[gBattlerTarget].statStages[stat] = DEFAULT_STAT_STAGE; - - if (gBattleStruct->stolenStats[stat] >= 2) - byTwo++; - - if (gBattleScripting.animArg1 == 0) - { - if (byTwo) - gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MINUS2 : STAT_ANIM_PLUS2) + stat; - else - gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MINUS1 : STAT_ANIM_PLUS1) + stat; - } + if (byTwo) + gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MINUS2 : STAT_ANIM_PLUS2) + stat; else - { - if (byTwo) - gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MULTIPLE_MINUS2 : STAT_ANIM_MULTIPLE_PLUS2); - else - gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MULTIPLE_MINUS1 : STAT_ANIM_MULTIPLE_PLUS1); - } - } + gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MINUS1 : STAT_ANIM_PLUS1) + stat; + } + else + { + if (byTwo) + gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MULTIPLE_MINUS2 : STAT_ANIM_MULTIPLE_PLUS2); + else + gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MULTIPLE_MINUS1 : STAT_ANIM_MULTIPLE_PLUS1); + } } if (gBattleStruct->stolenStats[0] != 0) @@ -3658,6 +3552,26 @@ void SetMoveEffect(u32 battlerAtk, u32 effectBattler, enum MoveEffect moveEffect } } break; + case MOVE_EFFECT_BEAT_UP_MESSAGE: + if (GetConfig(CONFIG_BEAT_UP) >= GEN_5) // Gen5+ don't print any custom message on attack + break; + + if (!IsBattlerAlive(gBattlerTarget)) + { + gMultiHitCounter = 0; + gBattlescriptCurrInstr = BattleScript_MoveEnd; + } + else if (gBattleStruct->beatUpSlot == 0 && gMultiHitCounter == 0) + { + gBattlescriptCurrInstr = BattleScript_ButItFailed; + } + else + { + PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gBattlerAttacker, gBattleStruct->beatUpSpecies[gBattleStruct->beatUpSlot]) + BattleScriptPush(battleScript); + gBattlescriptCurrInstr = BattleScript_BeatUpAttackMessage; + } + break; default: break; } @@ -3785,8 +3699,8 @@ static void Cmd_seteffectprimary(void) { CMD_ARGS(u8 battler, u8 effectBattler); - u32 battler = GetBattlerForBattleScript(cmd->battler); - u32 effectBattler = GetBattlerForBattleScript(cmd->effectBattler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId effectBattler = GetBattlerForBattleScript(cmd->effectBattler); SetMoveEffect(battler, effectBattler, gBattleScripting.moveEffect, cmd->nextInstr, EFFECT_PRIMARY); } @@ -3794,8 +3708,8 @@ static void Cmd_seteffectsecondary(void) { CMD_ARGS(u8 battler, u8 effectBattler); - u32 battler = GetBattlerForBattleScript(cmd->battler); - u32 effectBattler = GetBattlerForBattleScript(cmd->effectBattler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId effectBattler = GetBattlerForBattleScript(cmd->effectBattler); SetMoveEffect(battler, effectBattler, gBattleScripting.moveEffect, cmd->nextInstr, EFFECT_PRIMARY); } @@ -3803,7 +3717,7 @@ static void Cmd_clearvolatile(void) { CMD_ARGS(u8 battler, u8 _volatile); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); SetMonVolatile(battler, cmd->_volatile, 0); if (cmd->_volatile == VOLATILE_MULTIPLETURNS) @@ -3815,7 +3729,7 @@ static void Cmd_clearvolatile(void) static void Cmd_tryfaintmon(void) { CMD_ARGS(u8 battler, bool8 isSpikes, const u8 *instr); - u32 battler; + enum BattlerId battler; battler = GetBattlerForBattleScript(cmd->battler); if (cmd->isSpikes != 0) @@ -3883,7 +3797,7 @@ static void Cmd_dofaintanimation(void) if (gBattleControllerExecFlags != 0) return; - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX) { @@ -3906,7 +3820,7 @@ static void Cmd_cleareffectsonfaint(void) if (gBattleControllerExecFlags == 0) { - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); const u8 *clearDataResult = NULL; if (!(gBattleTypeFlags & BATTLE_TYPE_ARENA) || !IsBattlerAlive(battler)) { @@ -3927,7 +3841,7 @@ static void Cmd_jumpifstatus(void) { CMD_ARGS(u8 battler, u32 flags, const u8 *jumpInstr); - u8 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); u32 flags = cmd->flags; const u8 *jumpInstr = cmd->jumpInstr; @@ -3941,7 +3855,7 @@ static void Cmd_jumpifvolatile(void) { CMD_ARGS(u8 battler, u8 _volatile, const u8 *jumpInstr); - u8 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); const u8 *jumpInstr = cmd->jumpInstr; if (GetBattlerVolatile(battler, cmd->_volatile) != 0 && IsBattlerAlive(battler)) @@ -3954,7 +3868,7 @@ static void Cmd_jumpifability(void) { CMD_ARGS(u8 battler, enum Ability ability, const u8 *jumpInstr); - u32 battler; + enum BattlerId battler; bool32 hasAbility = FALSE; enum Ability ability = cmd->ability; @@ -4013,7 +3927,7 @@ static void Cmd_jumpifstat(void) CMD_ARGS(u8 battler, u8 comparison, u8 stat, u8 value, const u8 *jumpInstr); bool32 ret = 0; - u8 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); u8 stat = cmd->stat; u8 value = cmd->value; u8 comparison = cmd->comparison; @@ -4031,7 +3945,7 @@ static void Cmd_jumpifstatignorecontrary(void) CMD_ARGS(u8 battler, u8 comparison, u8 stat, u8 value, const u8 *jumpInstr); bool32 ret = 0; - u8 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); u8 stat = cmd->stat; u8 value = cmd->value; u8 comparison = cmd->comparison; @@ -4048,7 +3962,7 @@ static void Cmd_jumpbasedontype(void) { CMD_ARGS(u8 battler, u8 type, u8 jumpIfType, const u8 *jumpInstr); - u8 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); u8 type = cmd->type; const u8 *jumpInstr = cmd->jumpInstr; @@ -4425,7 +4339,7 @@ static void Cmd_getexp(void) } } -static u32 CountAliveMonsForBattlerSide(u32 battler) +static u32 CountAliveMonsForBattlerSide(enum BattlerId battler) { u32 aliveMons = 0; struct Pokemon *party = GetBattlerParty(battler); @@ -4441,7 +4355,7 @@ static u32 CountAliveMonsForBattlerSide(u32 battler) return aliveMons; } -bool32 NoAliveMonsForBattlerSide(u32 battler) +bool32 NoAliveMonsForBattlerSide(enum BattlerId battler) { return CountAliveMonsForBattlerSide(battler) == 0; } @@ -4534,7 +4448,8 @@ static void Cmd_checkteamslost(void) // In non-multi battles, jump to pointer if 1 spot is missing on both sides if (gBattleOutcome == 0 && (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_TRAINER))) { - s32 i, emptyPlayerSpots, emptyOpponentSpots; + enum BattlerId i; + s32 emptyPlayerSpots, emptyOpponentSpots; for (emptyPlayerSpots = 0, i = 0; i < gBattlersCount; i += 2) { @@ -4560,8 +4475,8 @@ static void Cmd_checkteamslost(void) { u32 occupiedPlayerSpots = (gBattlersCount / 2) - emptyPlayerSpots; u32 occupiedOpponentSpots = (gBattlersCount / 2) - emptyOpponentSpots; - u32 alivePlayerPartyMons = CountAliveMonsForBattlerSide(B_POSITION_PLAYER_LEFT) - occupiedPlayerSpots; - u32 aliveOpponentPartyMons = CountAliveMonsForBattlerSide(B_POSITION_OPPONENT_LEFT) - occupiedOpponentSpots; + u32 alivePlayerPartyMons = CountAliveMonsForBattlerSide(GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)) - occupiedPlayerSpots; + u32 aliveOpponentPartyMons = CountAliveMonsForBattlerSide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT)) - occupiedOpponentSpots; u32 emptySlotsTotal = emptyPlayerSpots + emptyOpponentSpots; u32 alivePartyMonsTotal = alivePlayerPartyMons + aliveOpponentPartyMons; @@ -4924,33 +4839,39 @@ static void Cmd_waitstate(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_isdmgblockedbydisguise(void) +static void Cmd_tryselfconfusiondmgformchange(void) { CMD_ARGS(); - if (!IsMimikyuDisguised(gBattlerAttacker) - || gBattleMons[gBattlerAttacker].volatiles.transformed - || !IsAbilityAndRecord(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), ABILITY_DISGUISE)) - { - gBattlescriptCurrInstr = cmd->nextInstr; - return; - } + enum Ability ability = GetBattlerAbility(gBattlerAttacker); + bool32 wasDisguised = IsMimikyuDisguised(gBattlerAttacker); - gBattleScripting.battler = gBattlerAttacker; - if (GetBattlerPartyState(gBattlerAttacker)->changedSpecies == SPECIES_NONE) - GetBattlerPartyState(gBattlerAttacker)->changedSpecies = gBattleMons[gBattlerAttacker].species; - if (gBattleMons[gBattlerAttacker].species == SPECIES_MIMIKYU_TOTEM_DISGUISED) - gBattleMons[gBattlerAttacker].species = SPECIES_MIMIKYU_BUSTED_TOTEM; - else - gBattleMons[gBattlerAttacker].species = SPECIES_MIMIKYU_BUSTED; - if (GetConfig(CONFIG_DISGUISE_HP_LOSS) >= GEN_8) - SetPassiveDamageAmount(gBattlerAttacker, GetNonDynamaxMaxHP(gBattlerAttacker) / 8); - BattleScriptPush(BattleScript_MoveEnd); - gBattlescriptCurrInstr = BattleScript_TargetFormChange; + if (TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_HIT_BY_CONFUSION_SELF_DMG, ability)) + { + gBattleScripting.battler = gBattlerAttacker; + switch (ability) + { + case ABILITY_DISGUISE: + if (GetConfig(CONFIG_DISGUISE_HP_LOSS) >= GEN_8 && wasDisguised) + SetPassiveDamageAmount(gBattlerAttacker, GetNonDynamaxMaxHP(gBattlerAttacker) / 8); + BattleScriptPush(BattleScript_MoveEnd); + gBattlescriptCurrInstr = BattleScript_BattlerFormChangeDisguise; + break; + default: + BattleScriptPush(BattleScript_MoveEnd); + gBattlescriptCurrInstr = BattleScript_TargetFormChange; + break; + } + return; + }; + + gBattlescriptCurrInstr = cmd->nextInstr; } static void Cmd_return(void) { + assertf(gBattleResources->battleScriptsStack->size != 0, "return used with nothing to return to, did you mean end/end2/end3?"); + BattleScriptPop(); } @@ -4958,8 +4879,11 @@ static void Cmd_end(void) { CMD_ARGS(); - // if (gBattleTypeFlags & BATTLE_TYPE_ARENA) - // BattleArena_AddSkillPoints(gBattlerAttacker); + assertf(gSelectionBattleScripts[gBattlerAttacker] == NULL, "incorrect use of end in selection script, did you mean endselectionscript?"); + assertf(gBattleMainFunc != RunBattleScriptCommands, "incorrect use of end in battle script, did you mean end3?"); + + if (gBattleTypeFlags & BATTLE_TYPE_ARENA) + BattleArena_AddSkillPoints(gBattlerAttacker); gCurrentActionFuncId = B_ACTION_TRY_FINISH; } @@ -4968,6 +4892,9 @@ static void Cmd_end2(void) { CMD_ARGS(); + assertf(gSelectionBattleScripts[gBattlerAttacker] == NULL, "incorrect use of end2 in selection script, did you mean endselectionscript?"); + assertf(gBattleMainFunc != RunBattleScriptCommands, "incorrect use of end2 in battle script, did you mean end3?"); + gCurrentActionFuncId = B_ACTION_TRY_FINISH; } @@ -4976,9 +4903,14 @@ static void Cmd_end3(void) { CMD_ARGS(); + assertf(gSelectionBattleScripts[gBattlerAttacker] == NULL, "incorrect use of end3 in selection script, did you mean endselectionscript?"); + BattleScriptPop(); if (gBattleResources->battleCallbackStack->size != 0) gBattleResources->battleCallbackStack->size--; + else // nothing to callback to + assertf(gBattleMainFunc != RunBattleScriptCommands_PopCallbacksStack, "incorrect use of end3 in battle script, did you mean end/end2?"); + gBattleMainFunc = gBattleResources->battleCallbackStack->function[gBattleResources->battleCallbackStack->size]; } @@ -4986,6 +4918,8 @@ static void Cmd_call(void) { CMD_ARGS(const u8 *instr); + assertf(gBattleResources->battleScriptsStack->size < UINT8_MAX, "call used, but battleScriptsStack is full!"); + BattleScriptPush(cmd->nextInstr); gBattlescriptCurrInstr = cmd->instr; } @@ -5018,10 +4952,12 @@ static void Cmd_jumpifabilitypresent(void) static void Cmd_endselectionscript(void) { CMD_ARGS(); + + assertf(gSelectionBattleScripts[gBattlerAttacker] != NULL, "wrong use of endselectionscript"); gBattleStruct->battlerState[gBattlerAttacker].selectionScriptFinished = TRUE; } -static void PlayAnimation(u32 battler, u8 animId, const u16 *argPtr, const u8 *nextInstr) +static void PlayAnimation(enum BattlerId battler, u8 animId, const u16 *argPtr, const u8 *nextInstr) { if (B_TERRAIN_BG_CHANGE == FALSE && animId == B_ANIM_RESTORE_BG) { @@ -5039,7 +4975,8 @@ static void PlayAnimation(u32 battler, u8 animId, const u16 *argPtr, const u8 *n || animId == B_ANIM_PRIMAL_REVERSION || animId == B_ANIM_ULTRA_BURST || animId == B_ANIM_TERA_CHARGE - || animId == B_ANIM_TERA_ACTIVATE) + || animId == B_ANIM_TERA_ACTIVATE + || animId == B_ANIM_FORM_CHANGE_INSTANT) { BtlController_EmitBattleAnimation(battler, B_COMM_TO_CONTROLLER, animId, *argPtr); MarkBattlerForControllerExec(battler); @@ -5077,7 +5014,7 @@ static void Cmd_playanimation(void) { CMD_ARGS(u8 battler, u8 animId, const u16 *argPtr); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); PlayAnimation(battler, cmd->animId, cmd->argPtr, cmd->nextInstr); } @@ -5086,14 +5023,14 @@ static void Cmd_playanimation_var(void) { CMD_ARGS(u8 battler, const u8 *animIdPtr, const u16 *argPtr); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); PlayAnimation(battler, *(cmd->animIdPtr), cmd->argPtr, cmd->nextInstr); } static void Cmd_jumpfifsemiinvulnerable(void) { CMD_ARGS(u8 battler, u8 state, const u8 *jumpInstr); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (gBattleMons[battler].volatiles.semiInvulnerable == cmd->state) gBattlescriptCurrInstr = cmd->jumpInstr; @@ -5105,14 +5042,14 @@ static void Cmd_trainerslidein(void) { CMD_ARGS(u8 position); - u32 battler = GetBattlerForBattleScript(cmd->position); + enum BattlerId battler = GetBattlerForBattleScript(cmd->position); BtlController_EmitTrainerSlide(battler, B_COMM_TO_CONTROLLER); MarkBattlerForControllerExec(battler); gBattlescriptCurrInstr = cmd->nextInstr; } -static inline bool32 IsProtectivePadsProtected(u32 battler, enum HoldEffect holdEffect) +static inline bool32 IsProtectivePadsProtected(enum BattlerId battler, enum HoldEffect holdEffect) { if (holdEffect != HOLD_EFFECT_PROTECTIVE_PADS) return FALSE; @@ -5129,10 +5066,10 @@ static void Cmd_moveend(void) enum MoveEndResult result = DoMoveEnd(cmd->endMode, cmd->endState); - if (result == MOVEEND_STEP_BREAK) + if (result == MOVEEND_RESULT_BREAK) return; - if (gBattleScripting.moveendState == MOVEEND_COUNT && result == MOVEEND_STEP_CONTINUE) + if (gBattleScripting.moveendState == MOVEEND_COUNT && result == MOVEEND_RESULT_CONTINUE) gBattlescriptCurrInstr = cmd->nextInstr; } @@ -5173,7 +5110,7 @@ static bool32 IsValidSwitchIn(enum BattleSide side, u32 index) if (!IsValidForBattle(&party[index])) return FALSE; - for (u32 i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (GetBattlerSide(i) == side && gBattlerPartyIndexes[i] == index && IsBattlerAlive(i)) return FALSE; @@ -5198,7 +5135,7 @@ static void Cmd_getswitchedmondata(void) { CMD_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (gBattleControllerExecFlags) return; @@ -5221,7 +5158,7 @@ static void Cmd_switchindataupdate(void) CMD_ARGS(u8 battler); struct BattlePokemon oldData; - u32 battler, i; + enum BattlerId battler, i; u8 *monData; if (gBattleControllerExecFlags) @@ -5289,7 +5226,7 @@ static void Cmd_switchindataupdate(void) static void Cmd_switchinanim(void) { - u32 battler; + enum BattlerId battler; CMD_ARGS(u8 battler, bool8 dontClearTransform, bool8 dontClearSubstitute); @@ -5307,13 +5244,14 @@ static void Cmd_switchinanim(void) gBattlescriptCurrInstr = cmd->nextInstr; - // if (gBattleTypeFlags & BATTLE_TYPE_ARENA) - // BattleArena_InitPoints(); + if (gBattleTypeFlags & BATTLE_TYPE_ARENA) + BattleArena_InitPoints(); } -bool32 CanBattlerSwitch(u32 battler) +bool32 CanBattlerSwitch(enum BattlerId battler) { - s32 i, lastMonId, battlerIn1, battlerIn2; + s32 i, lastMonId; + enum BattlerId battlerIn1, battlerIn2; bool32 ret = FALSE; struct Pokemon *party; @@ -5400,7 +5338,7 @@ bool32 CanBattlerSwitch(u32 battler) party = gEnemyParty; lastMonId = 0; - if (battler == B_POSITION_OPPONENT_RIGHT) + if (GetBattlerPosition(battler) == B_POSITION_OPPONENT_RIGHT) lastMonId = PARTY_SIZE / 2; for (i = lastMonId; i < lastMonId + (PARTY_SIZE / 2); i++) @@ -5458,7 +5396,7 @@ static void Cmd_jumpifcantswitch(void) { CMD_ARGS(u8 battler:7, u8 ignoreEscapePrevention:1, const u8 *jumpInstr); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (!cmd->ignoreEscapePrevention && !CanBattlerEscape(battler) && GetBattlerHoldEffect(battler) != HOLD_EFFECT_SHED_SHELL) { gBattlescriptCurrInstr = cmd->jumpInstr; @@ -5475,7 +5413,7 @@ static void Cmd_jumpifcantswitch(void) // Opens the party screen to choose a new Pokémon to send out. // slotId is the Pokémon to replace. // Note that this is not used by the Switch action, only replacing fainted Pokémon or Baton Pass -static void ChooseMonToSendOut(u32 battler, u8 slotId) +static void ChooseMonToSendOut(enum BattlerId battler, u8 slotId) { gBattleStruct->battlerPartyIndexes[battler] = gBattlerPartyIndexes[battler]; gBattleStruct->monToSwitchIntoId[battler] = PARTY_SIZE; @@ -5491,7 +5429,7 @@ static void Cmd_openpartyscreen(void) u32 flags = 0; u8 hitmarkerFaintBits = 0; - u32 i, battler = 0; + enum BattlerId battler = 0; const u8 *failInstr = cmd->failInstr; if (cmd->battler == BS_FAINTED_MULTIPLE_1) @@ -5527,7 +5465,7 @@ static void Cmd_openpartyscreen(void) bool32 hasReplacement; hitmarkerFaintBits = gHitMarker >> 28; - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (((1u << i) & hitmarkerFaintBits)) { @@ -5565,7 +5503,7 @@ static void Cmd_openpartyscreen(void) } } - for (i = 0; i < NUM_BATTLE_SIDES; i++) + for (enum BattlerId i = 0; i < MAX_BATTLERS_COUNT / 2; i++) { if (!(gSpecialStatuses[i].faintedHasReplacement)) { @@ -5592,7 +5530,7 @@ static void Cmd_openpartyscreen(void) if (IsDoubleBattle()) { hitmarkerFaintBits = gHitMarker >> 28; - for (i = 0; i < NUM_BATTLE_SIDES; i++) + for (enum BattlerId i = 0; i < MAX_BATTLERS_COUNT / 2; i++) { if ((1 << BATTLE_PARTNER(i)) & hitmarkerFaintBits && (1 << i) & hitmarkerFaintBits) { @@ -5669,7 +5607,7 @@ static void Cmd_openpartyscreen(void) if (gBattleTypeFlags & BATTLE_TYPE_MULTI) { - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (i != battler) { @@ -5680,7 +5618,7 @@ static void Cmd_openpartyscreen(void) } else { - u32 battlerOpposite = GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerPosition(battler))); + enum BattlerId battlerOpposite = GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerPosition(battler))); if (gAbsentBattlerFlags & (1u << battlerOpposite)) battlerOpposite ^= BIT_FLANK; @@ -5699,7 +5637,7 @@ static void Cmd_switchhandleorder(void) { CMD_ARGS(u8 battler, u8 state); - u32 battler, i; + enum BattlerId battler, i; if (gBattleControllerExecFlags) return; @@ -5763,7 +5701,7 @@ static void Cmd_switchhandleorder(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void UpdateSentMonFlags(u32 battler) +static void UpdateSentMonFlags(enum BattlerId battler) { UpdateSentPokesToOpponentValue(battler); gHitMarker &= ~HITMARKER_FAINTED(battler); @@ -5789,7 +5727,7 @@ static void UpdateSentMonFlags(u32 battler) static void Cmd_switchineffects(void) { CMD_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); UpdateSentMonFlags(battler); @@ -5844,7 +5782,7 @@ static void Cmd_playfaintcry(void) { CMD_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); BtlController_EmitFaintingCry(battler, B_COMM_TO_CONTROLLER); MarkBattlerForControllerExec(battler); @@ -5855,7 +5793,7 @@ static void Cmd_endlinkbattle(void) { CMD_ARGS(); - u32 battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + enum BattlerId battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); BtlController_EmitEndLinkBattle(battler, B_COMM_TO_CONTROLLER, gBattleOutcome); MarkBattlerForControllerExec(battler); @@ -5866,13 +5804,13 @@ static void Cmd_returntoball(void) { CMD_ARGS(u8 battler, bool8 changingForm); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); BtlController_EmitReturnMonToBall(battler, B_COMM_TO_CONTROLLER, TRUE); MarkBattlerForControllerExec(battler); // Don't always execute a form change here otherwise we can stomp gigantamax if (!cmd->changingForm) - TryBattleFormChange(battler, FORM_CHANGE_BATTLE_SWITCH); + TryBattleFormChange(battler, FORM_CHANGE_BATTLE_SWITCH_OUT, GetBattlerAbility(battler)); gBattlescriptCurrInstr = cmd->nextInstr; } @@ -5920,7 +5858,7 @@ static void Cmd_handlelearnnewmove(void) } else { - u32 battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + enum BattlerId battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); if (gBattlerPartyIndexes[battler] == monId && !(gBattleMons[battler].volatiles.transformed)) @@ -5993,7 +5931,7 @@ static void Cmd_yesnoboxlearnmove(void) if (!gPaletteFade.active) { FreeAllWindowBuffers(); - ShowSelectMovePokemonSummaryScreen(gPlayerParty, gBattleStruct->expGetterMonId, gPlayerPartyCount - 1, ReshowBattleScreenAfterMenu, gMoveToLearn); + ShowSelectMovePokemonSummaryScreen(gPlayerParty, gBattleStruct->expGetterMonId, ReshowBattleScreenAfterMenu, gMoveToLearn); gBattleScripting.learnMoveState++; } break; @@ -6016,7 +5954,7 @@ static void Cmd_yesnoboxlearnmove(void) enum Move move = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_MOVE1 + movePosition); if (CannotForgetMove(move)) { - PrepareStringBattle(STRINGID_HMMOVESCANTBEFORGOTTEN, B_POSITION_PLAYER_LEFT); + PrepareStringBattle(STRINGID_HMMOVESCANTBEFORGOTTEN, GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)); gBattleScripting.learnMoveState = 6; } else @@ -6113,7 +6051,7 @@ static void Cmd_hitanimation(void) if (!IsDoubleSpreadMove()) { - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (!IsBattlerUnaffectedByMove(battler)) { @@ -6128,8 +6066,7 @@ static void Cmd_hitanimation(void) } else if (!gBattleStruct->doneDoublesSpreadHit) { - u32 battlerDef; - for (battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) + for (enum BattlerId battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) { if (IsBattlerInvalidForSpreadMove(gBattlerAttacker, battlerDef)) continue; @@ -6226,7 +6163,7 @@ static void Cmd_updatebattlermoves(void) { CMD_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); switch (gBattleCommunication[0]) { @@ -6274,7 +6211,7 @@ static void Cmd_drawpartystatussummary(void) { CMD_ARGS(u8 battler); - u32 battler, i; + enum BattlerId battler, i; struct Pokemon *party; struct HpAndStatus hpStatuses[PARTY_SIZE]; @@ -6309,7 +6246,7 @@ static void Cmd_hidepartystatussummary(void) { CMD_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); BtlController_EmitHidePartyStatusSummary(battler, B_COMM_TO_CONTROLLER); MarkBattlerForControllerExec(battler); @@ -6349,8 +6286,8 @@ static void Cmd_statusanimation(void) if (gBattleControllerExecFlags == 0) { - u32 battler = GetBattlerForBattleScript(cmd->battler), - statusFlag = (cmd->isVolatile || cmd->status) ? cmd->status : gBattleMons[battler].status1; + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); + u32 statusFlag = (cmd->isVolatile || cmd->status) ? cmd->status : gBattleMons[battler].status1; if (!IsSemiInvulnerable(battler, CHECK_ALL) && gBattleMons[battler].volatiles.substituteHP == 0 && !(gHitMarker & (HITMARKER_NO_ANIMATIONS | HITMARKER_DISABLE_ANIMATION))) @@ -6368,7 +6305,7 @@ static void Cmd_futuresighttargetfailure(void) CMD_ARGS(const u8 *failInstr); // Just do CancelerTargetFailure - if (!DONE_TARGET_FAILURE && AtkCanceler_MoveSuccessOrder() != MOVE_STEP_SUCCESS) + if (!DONE_TARGET_FAILURE && DoAttackCanceler() != CANCELER_RESULT_SUCCESS) return; if (IsBattlerUnaffectedByMove(gBattlerTarget)) @@ -6376,6 +6313,7 @@ static void Cmd_futuresighttargetfailure(void) else gBattlescriptCurrInstr = cmd->nextInstr; } +#undef DONE_TARGET_FAILURE static u32 GetPossibleNextTarget(void) { @@ -6388,7 +6326,7 @@ static u32 GetPossibleNextTarget(void) for (u32 i = 0; i < MAX_BATTLERS_COUNT; i++) { - u32 battler = targetOrder[i]; + enum BattlerId battler = targetOrder[i]; if (!IsBattlerAlive(battler)) continue; @@ -6491,7 +6429,7 @@ static void Cmd_setgravity(void) } } -static bool32 TryCheekPouch(u32 battler, enum Item itemId, const u8 *nextInstr) +static bool32 TryCheekPouch(enum BattlerId battler, enum Item itemId, const u8 *nextInstr) { if (GetItemPocket(itemId) == POCKET_BERRIES && GetBattlerAbility(battler) == ABILITY_CHEEK_POUCH @@ -6512,7 +6450,7 @@ static void Cmd_removeitem(void) { CMD_ARGS(u8 battler); - u32 battler; + enum BattlerId battler; enum Item itemId = 0; if (gBattleScripting.overrideBerryRequirements) @@ -6852,7 +6790,7 @@ static void Cmd_setatktoplayer0(void) static void Cmd_makevisible(void) { CMD_ARGS(u8 battler); - u32 battler; + enum BattlerId battler; if (gBattleControllerExecFlags) return; @@ -6868,7 +6806,7 @@ static void Cmd_recordability(void) { CMD_ARGS(u8 battler); - u8 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); RecordAbilityBattle(battler, gBattleMons[battler].ability); gBattlescriptCurrInstr = cmd->nextInstr; } @@ -6902,8 +6840,8 @@ static void Cmd_hpthresholds(void) if (!(IsDoubleBattle())) { - u32 battler = GetBattlerForBattleScript(cmd->battler); - u32 opposingBattler = BATTLE_OPPOSITE(battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId opposingBattler = BATTLE_OPPOSITE(battler); s32 result = gBattleMons[opposingBattler].hp * 100 / gBattleMons[opposingBattler].maxHP; if (result == 0) @@ -6928,8 +6866,8 @@ static void Cmd_hpthresholds2(void) if (!(IsDoubleBattle())) { - u32 battler = GetBattlerForBattleScript(cmd->battler); - u32 opposingBattler = BATTLE_OPPOSITE(battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId opposingBattler = BATTLE_OPPOSITE(battler); u32 hpSwitchout = gBattleStruct->battlerState[opposingBattler].hpOnSwitchout; s32 result = (hpSwitchout - gBattleMons[opposingBattler].hp) * 100 / hpSwitchout; @@ -6955,7 +6893,7 @@ static void Cmd_useitemonopponent(void) gBattlescriptCurrInstr = cmd->nextInstr; } -bool32 CanUseLastResort(u8 battler) +bool32 CanUseLastResort(enum BattlerId battler) { u32 moveIndex; for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) @@ -7065,7 +7003,7 @@ static bool32 DefogClearHazards(u32 saveBattler, enum BattleSide side, bool32 cl return FALSE; } -static bool32 TryDefogClear(u32 battlerAtk, bool32 clear) +static bool32 TryDefogClear(enum BattlerId battlerAtk, bool32 clear) { s32 i; u8 saveBattler = gBattlerAttacker; @@ -7115,7 +7053,7 @@ static bool32 TryDefogClear(u32 battlerAtk, bool32 clear) return FALSE; } -static bool32 TryTidyUpClear(u32 battlerAtk, bool32 clear) +static bool32 TryTidyUpClear(enum BattlerId battlerAtk, bool32 clear) { u32 i; u32 saveBattler = gBattlerAttacker; @@ -7147,7 +7085,7 @@ static bool32 TryTidyUpClear(u32 battlerAtk, bool32 clear) return FALSE; } -u32 IsFlowerVeilProtected(u32 battler) +u32 IsFlowerVeilProtected(enum BattlerId battler) { if (IS_BATTLER_OF_TYPE(battler, TYPE_GRASS)) return IsAbilityOnSide(battler, ABILITY_FLOWER_VEIL); @@ -7155,7 +7093,7 @@ u32 IsFlowerVeilProtected(u32 battler) return 0; } -u32 IsLeafGuardProtected(u32 battler, enum Ability ability) +u32 IsLeafGuardProtected(enum BattlerId battler, enum Ability ability) { if (IsBattlerWeatherAffected(battler, B_WEATHER_SUN)) return ability == ABILITY_LEAF_GUARD; @@ -7163,13 +7101,13 @@ u32 IsLeafGuardProtected(u32 battler, enum Ability ability) return 0; } -bool32 IsShieldsDownProtected(u32 battler, enum Ability ability) +bool32 IsShieldsDownProtected(enum BattlerId battler, enum Ability ability) { return (ability == ABILITY_SHIELDS_DOWN && GetFormIdFromFormSpeciesId(gBattleMons[battler].species) < GetFormIdFromFormSpeciesId(SPECIES_MINIOR_CORE_RED)); // Minior is not in core form } -u32 IsAbilityStatusProtected(u32 battler, enum Ability ability) +u32 IsAbilityStatusProtected(enum BattlerId battler, enum Ability ability) { return IsLeafGuardProtected(battler, ability) || IsShieldsDownProtected(battler, ability) @@ -7243,45 +7181,6 @@ void BS_CourtChangeSwapSideStatuses(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void HandleScriptMegaPrimalBurst(u32 caseId, u32 battler, u32 type) -{ - struct Pokemon *mon = GetBattlerMon(battler); - - // Change species. - if (caseId == 0) - { - if (type == HANDLE_TYPE_MEGA_EVOLUTION) - { - if (!TryBattleFormChange(battler, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM)) - TryBattleFormChange(battler, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE); - } - else if (type == HANDLE_TYPE_PRIMAL_REVERSION) - TryBattleFormChange(battler, FORM_CHANGE_BATTLE_PRIMAL_REVERSION); - else - TryBattleFormChange(battler, FORM_CHANGE_BATTLE_ULTRA_BURST); - - PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[battler].species); - - BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_SPECIES_BATTLE, 1u << gBattlerPartyIndexes[battler], sizeof(gBattleMons[battler].species), &gBattleMons[battler].species); - MarkBattlerForControllerExec(battler); - } - // Update healthbox and elevation and play cry. - else - { - UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL); - if (!IsOnPlayerSide(battler)) - SetBattlerShadowSpriteCallback(battler, gBattleMons[battler].species); - if (type == HANDLE_TYPE_MEGA_EVOLUTION) - SetGimmickAsActivated(battler, GIMMICK_MEGA); - if (type == HANDLE_TYPE_ULTRA_BURST) - SetGimmickAsActivated(battler, GIMMICK_ULTRA_BURST); - } -} - -static void Cmd_unused_0x78(void) -{ -} - static void Cmd_setprotectlike(void) { CMD_ARGS(); @@ -7380,10 +7279,6 @@ static void Cmd_tryhealhalfhealth(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_unused_0x7e(void) -{ -} - static void Cmd_setfieldweather(void) { CMD_ARGS(); @@ -7488,17 +7383,9 @@ static void Cmd_trysetrest(void) } } -static void Cmd_unused_0x82(void) +bool8 UproarWakeUpCheck(enum BattlerId battler) { -} - -static void Cmd_unused_0x83(void) -{ -} - -bool8 UproarWakeUpCheck(u8 battler) -{ - s32 i; + enum BattlerId i; bool32 hasSoundproof = (B_UPROAR_IGNORE_SOUNDPROOF < GEN_5 && GetBattlerAbility(battler) == ABILITY_SOUNDPROOF); for (i = 0; i < gBattlersCount; i++) @@ -7566,12 +7453,6 @@ static void Cmd_stockpile(void) static void Cmd_stockpiletobasedamage(void) { - CMD_ARGS(); - - if (gBattleCommunication[MISS_TYPE] != B_MSG_PROTECTED) - gBattleScripting.animTurn = gBattleMons[gBattlerAttacker].volatiles.stockpileCounter; - - gBattlescriptCurrInstr = cmd->nextInstr; } static void Cmd_stockpiletohpheal(void) @@ -7604,28 +7485,6 @@ static void Cmd_stockpiletohpheal(void) } } -void BS_RemoveStockpileCounters(void) -{ - NATIVE_ARGS(); - - if (GetMoveEffect(gCurrentMove) == EFFECT_SPIT_UP - && gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_1ST_HIT - && IsBattlerAlive(gBattlerTarget)) - { - gBattlescriptCurrInstr = cmd->nextInstr; - } - else - { - gBattleMons[gBattlerAttacker].volatiles.stockpileCounter = 0; - BattleScriptPush(cmd->nextInstr); - gBattlescriptCurrInstr = BattleScript_MoveEffectStockpileWoreOff; - } -} - -static void Cmd_unused_0x88(void) -{ -} - static u16 ReverseStatChangeMoveEffect(u16 moveEffect) { switch (moveEffect) @@ -7694,7 +7553,7 @@ static u16 ReverseStatChangeMoveEffect(u16 moveEffect) } } -static void TryPlayStatChangeAnimation(u32 battler, enum Ability ability, u32 stats, s32 statValue, u32 statId, bool32 certain) +static void TryPlayStatChangeAnimation(enum BattlerId battler, enum Ability ability, u32 stats, s32 statValue, u32 statId, bool32 certain) { enum Stat currStat = 0; u32 changeableStatsCount = 1; // current stat is counted automatically @@ -7782,7 +7641,7 @@ static void TryPlayStatChangeAnimation(u32 battler, enum Ability ability, u32 st } } -static u32 ChangeStatBuffs(u32 battler, s8 statValue, enum Stat statId, union StatChangeFlags flags, u32 stats, const u8 *BS_ptr) +static u32 ChangeStatBuffs(enum BattlerId battler, s8 statValue, enum Stat statId, union StatChangeFlags flags, u32 stats, const u8 *BS_ptr) { u32 index, battlerAbility; enum HoldEffect battlerHoldEffect; @@ -7903,6 +7762,8 @@ static u32 ChangeStatBuffs(u32 battler, s8 statValue, enum Stat statId, union St } else if (battlerAbility == ABILITY_MIRROR_ARMOR && !flags.mirrorArmored && gBattlerAttacker != gBattlerTarget && battler == gBattlerTarget) { + if (GetMoveEffect(gCurrentMove) == EFFECT_PARTING_SHOT) + gBattleScripting.animTargetsHit = 1; if (flags.allowPtr) { SET_STATCHANGER(statId, GET_STAT_BUFF_VALUE(statValue) | STAT_BUFF_NEGATIVE, TRUE); @@ -7992,7 +7853,7 @@ static u32 ChangeStatBuffs(u32 battler, s8 statValue, enum Stat statId, union St if (statIncrease) { // Check Mirror Herb / Opportunist - for (index = 0; index < gBattlersCount; index++) + for (enum BattlerId index = 0; index < gBattlersCount; index++) { if (IsBattlerAlly(index, battler)) continue; // Only triggers on opposing side @@ -8064,7 +7925,7 @@ static void Cmd_statbuffchange(void) gBattlescriptCurrInstr = failInstr; } -bool32 TryResetBattlerStatChanges(u8 battler) +bool32 TryResetBattlerStatChanges(enum BattlerId battler) { u32 j; bool32 ret = FALSE; @@ -8087,9 +7948,7 @@ static void Cmd_normalisebuffs(void) { CMD_ARGS(); - s32 i; - - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) TryResetBattlerStatChanges(i); gBattlescriptCurrInstr = cmd->nextInstr; @@ -8466,126 +8325,9 @@ static void Cmd_setlightscreen(void) gBattlescriptCurrInstr = cmd->nextInstr; } -// for var lands -#define NO_HIT 0 -#define CALC_ACC 1 -#define SURE_HIT 2 -// for var endured -#define NOT_ENDURED 0 -#define FOCUS_SASHED 1 -#define FOCUS_BANDED 2 -#define AFFECTION_ENDURED 3 static void Cmd_tryKO(void) { - CMD_ARGS(const u8 *failInstr); - - enum BattleMoveEffects effect = GetMoveEffect(gCurrentMove); - enum HoldEffect holdEffect = GetBattlerHoldEffect(gBattlerTarget); - enum Ability targetAbility = GetBattlerAbility(gBattlerTarget); - u32 rand = Random() % 100; - u32 affectionScore = GetBattlerAffectionHearts(gBattlerTarget); - u32 endured = NOT_ENDURED; - - // Dynamaxed Pokemon cannot be hit by OHKO moves. - if ((GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX)) - { - gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_KO_UNAFFECTED; - gBattlescriptCurrInstr = cmd->failInstr; - return; - } - - gPotentialItemEffectBattler = gBattlerTarget; - if (holdEffect == HOLD_EFFECT_FOCUS_BAND - && (Random() % 100) < GetBattlerHoldEffectParam(gBattlerTarget)) - { - endured = FOCUS_BANDED; - RecordItemEffectBattle(gBattlerTarget, holdEffect); - } - else if (holdEffect == HOLD_EFFECT_FOCUS_SASH && IsBattlerAtMaxHp(gBattlerTarget)) - { - endured = FOCUS_SASHED; - RecordItemEffectBattle(gBattlerTarget, holdEffect); - } - else if (B_AFFECTION_MECHANICS == TRUE && IsOnPlayerSide(gBattlerTarget) && affectionScore >= AFFECTION_THREE_HEARTS) - { - if ((affectionScore == AFFECTION_FIVE_HEARTS && rand < 20) - || (affectionScore == AFFECTION_FOUR_HEARTS && rand < 15) - || (affectionScore == AFFECTION_THREE_HEARTS && rand < 10)) - endured = AFFECTION_ENDURED; - } - - if (targetAbility == ABILITY_STURDY) - { - gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED; - gLastUsedAbility = ABILITY_STURDY; - gBattlescriptCurrInstr = BattleScript_SturdyPreventsOHKO; - gBattlerAbility = gBattlerTarget; - } - else - { - u32 lands = NO_HIT; - if (gBattleMons[gBattlerTarget].level > gBattleMons[gBattlerAttacker].level) - lands = NO_HIT; - else if ((gBattleMons[gBattlerTarget].volatiles.lockOn && gBattleMons[gBattlerTarget].volatiles.battlerWithSureHit == gBattlerAttacker) - || IsAbilityAndRecord(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), ABILITY_NO_GUARD) - || IsAbilityAndRecord(gBattlerTarget, targetAbility, ABILITY_NO_GUARD)) - lands = SURE_HIT; - else - lands = CALC_ACC; - - if (lands == CALC_ACC) - { - u16 odds = GetMoveAccuracy(gCurrentMove) + (gBattleMons[gBattlerAttacker].level - gBattleMons[gBattlerTarget].level); - if (B_SHEER_COLD_ACC >= GEN_7 && effect == EFFECT_SHEER_COLD && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_ICE)) - odds -= 10; - if (RandomPercentage(RNG_ACCURACY, odds) && gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level) - lands = SURE_HIT; - } - - if (lands == SURE_HIT) - { - if (gBattleMons[gBattlerTarget].volatiles.endured) - { - gBattleStruct->moveDamage[gBattlerTarget] = gBattleMons[gBattlerTarget].hp - 1; - gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_FOE_ENDURED; - } - else if (endured == FOCUS_BANDED || endured == FOCUS_SASHED) - { - gBattleStruct->moveDamage[gBattlerTarget] = gBattleMons[gBattlerTarget].hp - 1; - gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_FOE_HUNG_ON; - gLastUsedItem = gBattleMons[gBattlerTarget].item; - } - else if (endured == AFFECTION_ENDURED) - { - gBattleStruct->moveDamage[gBattlerTarget] = gBattleMons[gBattlerTarget].hp - 1; - gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_FOE_ENDURED_AFFECTION; - } - else - { - gBattleStruct->moveDamage[gBattlerTarget] = gBattleMons[gBattlerTarget].hp; - gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_ONE_HIT_KO; - } - gBattlescriptCurrInstr = cmd->nextInstr; - } - else - { - gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED; - if (gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level) - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_KO_MISS; - else - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_KO_UNAFFECTED; - gBattlescriptCurrInstr = cmd->failInstr; - } - } } -#undef NO_HIT -#undef CALC_ACC -#undef SURE_HIT -#undef NOT_ENDURED -#undef FOCUS_SASHED -#undef FOCUS_BANDED -#undef AFFECTION_ENDURED static void Cmd_checknonvolatiletrigger(void) { @@ -8615,7 +8357,7 @@ static void Cmd_copybidedmg(void) static void Cmd_animatewildpokemonafterfailedpokeball(void) { CMD_ARGS(u8 battler); - u8 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); AnimateMonAfterPokeBallFail(battler); gBattlescriptCurrInstr = cmd->nextInstr; } @@ -8648,7 +8390,7 @@ static void Cmd_tryinfatuating(void) static void Cmd_updatestatusicon(void) { CMD_ARGS(u8 battler); - u32 battler; + enum BattlerId battler; if (gBattleControllerExecFlags) return; @@ -8714,7 +8456,7 @@ static void Cmd_setmist(void) static void Cmd_setfocusenergy(void) { CMD_ARGS(u8 battler); - u8 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); enum BattleMoveEffects effect = GetMoveEffect(gCurrentMove); if ((effect == EFFECT_DRAGON_CHEER && (!(IsDoubleBattle()) || (gAbsentBattlerFlags & (1u << battler)))) @@ -8887,22 +8629,6 @@ static void Cmd_setcalledmove(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_unused_0x9f(void) -{ -} - -static void Cmd_unused_0xA0(void) -{ -} - -static void Cmd_unused_0xA1(void) -{ -} - -static void Cmd_unused_0xA2(void) -{ -} - static void Cmd_disablelastusedattack(void) { CMD_ARGS(const u8 *failInstr); @@ -8975,7 +8701,7 @@ static void Cmd_trysetencore(void) // If the target's selected move is not the same as the move being Encored into, // the target will select a random opposing target // Redirection such as Follow Me is already covered in HandleAction_UseMove of battle_util.c - if (gBattleMons[gBattlerTarget].volatiles.encoredMove != GetChosenMoveFromPosition(gBattlerTarget)) + if (gBattleMons[gBattlerTarget].volatiles.encoredMove != GetBattlerChosenMove(gBattlerTarget)) gBattleStruct->moveTarget[gBattlerTarget] = SetRandomTarget(gBattlerTarget); // Encore always lasts 3 turns, but we need to account for a scenario where Encore changes the move during the same turn. @@ -9143,13 +8869,9 @@ static void Cmd_copymovepermanently(void) } } -static void Cmd_unused_0xA9(void) -{ -} - static inline bool32 IsDanamaxMonPresent(void) { - for (u32 battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { if (battler == gBattlerAttacker) continue; @@ -9161,14 +8883,6 @@ static inline bool32 IsDanamaxMonPresent(void) return FALSE; } -static void Cmd_unused_AA(void) -{ -} - -static void Cmd_unused_0xab(void) -{ -} - static void Cmd_settailwind(void) { CMD_ARGS(const u8 *failInstr); @@ -9264,7 +8978,7 @@ static void Cmd_healpartystatus(void) u32 i = 0; u32 zero = 0; u32 toHeal = 0; - u32 partner = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerAttacker))); + enum BattlerId partner = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerAttacker))); struct Pokemon *party = GetBattlerParty(gBattlerAttacker); bool32 isSoundMove = IsSoundMove(gCurrentMove); @@ -9374,7 +9088,7 @@ static void Cmd_trysetspikes(void) { CMD_ARGS(const u8 *failInstr); - u8 targetSide = BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker)); + enum BattleSide targetSide = GetBattlerSide(BATTLE_OPPOSITE(gBattlerAttacker)); if (gSideTimers[targetSide].spikesAmount == 3) { @@ -9401,10 +9115,9 @@ static void Cmd_trysetperishsong(void) { CMD_ARGS(const u8 *failInstr); - s32 i = 0; s32 notAffectedCount = 0; - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].volatiles.perishSong || IsBattlerUnaffectedByMove(i) @@ -9426,10 +9139,6 @@ static void Cmd_trysetperishsong(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_unused_0xb3(void) -{ -} - static void Cmd_jumpifconfusedandstatmaxed(void) { CMD_ARGS(u8 stat, const u8 *jumpInstr); @@ -9441,10 +9150,6 @@ static void Cmd_jumpifconfusedandstatmaxed(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_unused_0xb5(void) -{ -} - static void Cmd_setembargo(void) { CMD_ARGS(const u8 *failInstr); @@ -9531,52 +9236,6 @@ static void Cmd_setsafeguard(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_magnitudedamagecalculation(void) -{ - CMD_ARGS(); - - u32 magnitude = RandomUniform(RNG_MAGNITUDE, 0, 99); - - if (magnitude < 5) - { - gBattleStruct->magnitudeBasePower = 10; - magnitude = 4; - } - else if (magnitude < 15) - { - gBattleStruct->magnitudeBasePower = 30; - magnitude = 5; - } - else if (magnitude < 35) - { - gBattleStruct->magnitudeBasePower = 50; - magnitude = 6; - } - else if (magnitude < 65) - { - gBattleStruct->magnitudeBasePower = 70; - magnitude = 7; - } - else if (magnitude < 85) - { - gBattleStruct->magnitudeBasePower = 90; - magnitude = 8; - } - else if (magnitude < 95) - { - gBattleStruct->magnitudeBasePower = 110; - magnitude = 9; - } - else - { - gBattleStruct->magnitudeBasePower = 150; - magnitude = 10; - } - - PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff1, 2, magnitude) - gBattlescriptCurrInstr = cmd->nextInstr; -} - static void Cmd_jumpifnopursuitswitchdmg(void) { CMD_ARGS(const u8 *jumpInstr); @@ -9601,7 +9260,7 @@ static void Cmd_jumpifnopursuitswitchdmg(void) static void Cmd_tryactivateitem(void) { CMD_ARGS(u8 battler, u8 flag); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); gBattlescriptCurrInstr = cmd->nextInstr; @@ -9660,7 +9319,15 @@ static void Cmd_copyfoestats(void) { gBattleMons[gBattlerAttacker].statStages[i] = gBattleMons[gBattlerTarget].statStages[i]; } - gBattleScripting.battler = gBattlerTarget; + if (GetConfig(CONFIG_PSYCH_UP_CRIT_RATIO) >= GEN_6) + { + // Copy crit boosts (Focus Energy, Dragon Cheer, G-Max Chi Strike) + gBattleMons[gBattlerAttacker].volatiles.focusEnergy = gBattleMons[gBattlerTarget].volatiles.focusEnergy; + gBattleMons[gBattlerAttacker].volatiles.dragonCheer = gBattleMons[gBattlerTarget].volatiles.dragonCheer; + gBattleMons[gBattlerAttacker].volatiles.bonusCritStages = gBattleMons[gBattlerTarget].volatiles.bonusCritStages; + } + gEffectBattler = gBattlerTarget; + gBattleScripting.battler = gBattlerAttacker; gBattlescriptCurrInstr = cmd->nextInstr; } @@ -9703,10 +9370,6 @@ static void Cmd_rapidspinfree(void) } } -static void Cmd_unused_0xBF(void) -{ -} - static void Cmd_recoverbasedonsunlight(void) { CMD_ARGS(const u8 *failInstr); @@ -9828,22 +9491,6 @@ static void Cmd_setfutureattack(void) static void Cmd_trydobeatup(void) { - CMD_ARGS(const u8 *endInstr, const u8 *failInstr); - - if (!IsBattlerAlive(gBattlerTarget)) - { - gMultiHitCounter = 0; - gBattlescriptCurrInstr = cmd->endInstr; - } - else if (gBattleStruct->beatUpSlot == 0 && gMultiHitCounter == 0) - { - gBattlescriptCurrInstr = cmd->failInstr; - } - else - { - PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gBattlerAttacker, gBattleStruct->beatUpSpecies[gBattleStruct->beatUpSlot]) - gBattlescriptCurrInstr = cmd->nextInstr; - } } static void Cmd_setsemiinvulnerablebit(void) @@ -9862,23 +9509,11 @@ static void Cmd_setsemiinvulnerablebit(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_unused_0xC6(void) -{ -} - -static void Cmd_unused_0xC7(void) -{ -} - -static void Cmd_unused_c8(void) -{ -} - static void Cmd_trymemento(void) { CMD_ARGS(const u8 *failInstr); - if (B_MEMENTO_FAIL >= GEN_5 && DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove)) + if (B_MEMENTO_FAIL >= GEN_4 && DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove)) { // Failed, target was protected. gBattlescriptCurrInstr = cmd->failInstr; @@ -9913,14 +9548,6 @@ static void Cmd_setforcedtarget(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_unused_0xcb(void) -{ -} - -static void Cmd_unused_0xCC(void) -{ -} - static void Cmd_curestatuswithmove(void) { CMD_ARGS(const u8 *failInstr); @@ -9963,10 +9590,6 @@ static void Cmd_settorment(void) } } -static void Cmd_unused_0xCF(void) -{ -} - static void Cmd_settaunt(void) { CMD_ARGS(const u8 *failInstr); @@ -10140,7 +9763,7 @@ static void Cmd_tryswapitems(void) } } -static bool32 CanAbilityShieldActivateForBattler(u32 battler) +static bool32 CanAbilityShieldActivateForBattler(enum BattlerId battler) { if (GetBattlerHoldEffectIgnoreAbility(battler) != HOLD_EFFECT_ABILITY_SHIELD) return FALSE; @@ -10156,8 +9779,8 @@ static void Cmd_trycopyability(void) { CMD_ARGS(u8 battler, const u8 *failInstr); - u32 battler = GetBattlerForBattleScript(cmd->battler); - u32 partner = BATTLE_PARTNER(battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId partner = BATTLE_PARTNER(battler); enum Ability defAbility = gBattleMons[gBattlerTarget].ability; bool32 shouldConsiderPartner = IsBattlerAlive(partner) && GetMoveEffect(gCurrentMove) == EFFECT_DOODLE; @@ -10275,10 +9898,6 @@ static void Cmd_setyawn(void) } } -static void Cmd_unused0xd8(void) -{ -} - static void HandleRoomMove(u32 statusFlag, u16 *timer, u8 stringId) { if (gFieldStatuses & statusFlag) @@ -10368,7 +9987,7 @@ static void Cmd_tryimprison(void) } else { - u8 battler; + enum BattlerId battler; for (battler = 0; battler < gBattlersCount; battler++) { @@ -10420,7 +10039,7 @@ static void Cmd_trysetvolatile(void) { CMD_ARGS(u8 battler, u8 _volatile, const u8 *failInstr); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (GetBattlerVolatile(battler, cmd->_volatile) != 0) { @@ -10444,10 +10063,6 @@ static void Cmd_trysetvolatile(void) } } -static void Cmd_unused_0xde(void) -{ -} - static void Cmd_trysetmagiccoat(void) { CMD_ARGS(const u8 *failInstr); @@ -10479,15 +10094,11 @@ static void Cmd_trysetsnatch(void) } } -static void Cmd_unused2(void) -{ -} - static void Cmd_switchoutabilities(void) { CMD_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (gBattleMons[battler].volatiles.neutralizingGas) { gBattleMons[battler].volatiles.neutralizingGas = FALSE; @@ -10536,7 +10147,7 @@ static void Cmd_jumpifhasnohp(void) { CMD_ARGS(u8 battler, const u8 *jumpInstr); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (!IsBattlerAlive(battler)) gBattlescriptCurrInstr = cmd->jumpInstr; @@ -10544,10 +10155,6 @@ static void Cmd_jumpifhasnohp(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_unused_0xE4(void) -{ -} - static void Cmd_pickup(void) { CMD_ARGS(); @@ -10557,67 +10164,71 @@ static void Cmd_pickup(void) u8 lvlDivBy10; enum Ability ability; - for (i = 0; i < PARTY_SIZE; i++) + if (!InBattlePike()) // No items in Battle Pike. { - species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG); - heldItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM); - lvlDivBy10 = (GetMonData(&gPlayerParty[i], MON_DATA_LEVEL)-1) / 10; //Moving this here makes it easier to add in abilities like Honey Gather. - if (lvlDivBy10 > 9) - lvlDivBy10 = 9; - - ability = GetSpeciesAbility(species, GetMonData(&gPlayerParty[i], MON_DATA_ABILITY_NUM)); - - if (ability == ABILITY_PICKUP - && species != SPECIES_NONE - && species != SPECIES_EGG - && heldItem == ITEM_NONE - && (Random() % 10) == 0) + bool32 isInPyramid = CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE; + for (i = 0; i < PARTY_SIZE; i++) { - u32 rand = Random() % 100; - u32 percentTotal = 0; + species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG); + heldItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM); + lvlDivBy10 = (GetMonData(&gPlayerParty[i], MON_DATA_LEVEL)-1) / 10; //Moving this here makes it easier to add in abilities like Honey Gather. + if (lvlDivBy10 > 9) + lvlDivBy10 = 9; - for (j = 0; j < ARRAY_COUNT(sPickupTable); j++) + ability = GetSpeciesAbility(species, GetMonData(&gPlayerParty[i], MON_DATA_ABILITY_NUM)); + + if (ability == ABILITY_PICKUP + && species != SPECIES_NONE + && species != SPECIES_EGG + && heldItem == ITEM_NONE + && (Random() % 10) == 0) { - percentTotal += sPickupTable[j].percentage[lvlDivBy10]; - if (rand < percentTotal) + if (isInPyramid) { - SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &sPickupTable[j].itemId); - break; + heldItem = GetBattlePyramidPickupItemId(); + SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &heldItem); + } + else + { + u32 rand = Random() % 100; + u32 percentTotal = 0; + + for (j = 0; j < ARRAY_COUNT(sPickupTable); j++) + { + percentTotal += sPickupTable[j].percentage[lvlDivBy10]; + if (rand < percentTotal) + { + SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &sPickupTable[j].itemId); + break; + } + } } } - } - else if (ability == ABILITY_HONEY_GATHER - && species != 0 - && species != SPECIES_EGG - && heldItem == ITEM_NONE) - { - if ((lvlDivBy10 + 1 ) * 5 > Random() % 100) + else if (ability == ABILITY_HONEY_GATHER + && species != 0 + && species != SPECIES_EGG + && heldItem == ITEM_NONE) { - heldItem = ITEM_HONEY; + if ((lvlDivBy10 + 1 ) * 5 > Random() % 100) + { + heldItem = ITEM_HONEY; + SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &heldItem); + } + } + else if (P_SHUCKLE_BERRY_JUICE == GEN_2 + && species == SPECIES_SHUCKLE + && heldItem == ITEM_ORAN_BERRY + && (Random() % 16) == 0) + { + heldItem = ITEM_BERRY_JUICE; SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &heldItem); } } - else if (P_SHUCKLE_BERRY_JUICE == GEN_2 - && species == SPECIES_SHUCKLE - && heldItem == ITEM_ORAN_BERRY - && (Random() % 16) == 0) - { - heldItem = ITEM_BERRY_JUICE; - SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &heldItem); - } } gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_unused_0xE6(void) -{ -} - -static void Cmd_unused_0xE7(void) -{ -} - // Water and Mud Sport static void Cmd_settypebasedhalvers(void) { @@ -10679,7 +10290,7 @@ static void Cmd_settypebasedhalvers(void) gBattlescriptCurrInstr = cmd->failInstr; } -bool32 DoesSubstituteBlockMove(u32 battlerAtk, u32 battlerDef, enum Move move) +bool32 DoesSubstituteBlockMove(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move) { if (!gBattleMons[battlerDef].volatiles.substitute) return FALSE; @@ -10691,7 +10302,7 @@ bool32 DoesSubstituteBlockMove(u32 battlerAtk, u32 battlerDef, enum Move move) return TRUE; } -bool32 DoesDisguiseBlockMove(u32 battler, enum Move move) +bool32 DoesDisguiseBlockMove(enum BattlerId battler, enum Move move) { if (!IsMimikyuDisguised(battler) || gBattleMons[battler].volatiles.transformed @@ -10702,6 +10313,17 @@ bool32 DoesDisguiseBlockMove(u32 battler, enum Move move) return TRUE; } +bool32 DoesIceFaceBlockMove(enum BattlerId battler, enum Move move) +{ + if (gBattleMons[battler].species != SPECIES_EISCUE_ICE + || gBattleMons[battler].volatiles.transformed + || !IsBattleMovePhysical(move) + || !IsAbilityAndRecord(battler, GetBattlerAbility(battler), ABILITY_ICE_FACE)) + return FALSE; + else + return TRUE; +} + static void Cmd_jumpifsubstituteblocks(void) { CMD_ARGS(const u8 *jumpInstr); @@ -10739,7 +10361,7 @@ static void Cmd_tryrecycleitem(void) } } -bool32 CanCamouflage(u8 battler) +bool32 CanCamouflage(enum BattlerId battler) { if (IS_BATTLER_OF_TYPE(battler, gBattleEnvironmentInfo[gBattleEnvironment].camouflageType)) return FALSE; @@ -10788,7 +10410,7 @@ static void Cmd_pursuitdoubles(void) { CMD_ARGS(const u8 *failInstr); - u32 battler = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerAttacker))); + enum BattlerId battler = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerAttacker))); if (IsDoubleBattle() && !(gAbsentBattlerFlags & (1u << battler)) @@ -10823,10 +10445,6 @@ static void Cmd_snatchsetbattlers(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_unused_0xee(void) -{ -} - u8 GetCatchingBattler(void) { if (IsBattlerAlive(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT))) @@ -10846,7 +10464,7 @@ static void FinalizeCapture(void) } BtlController_EmitBallThrowAnim(gBattlerAttacker, B_COMM_TO_CONTROLLER, BALL_3_SHAKES_SUCCESS); MarkBattlerForControllerExec(gBattlerAttacker); - TryBattleFormChange(gBattlerTarget, FORM_CHANGE_END_BATTLE); + TryBattleFormChange(gBattlerTarget, FORM_CHANGE_END_BATTLE, GetBattlerAbility(gBattlerTarget)); gBattlescriptCurrInstr = BattleScript_SuccessBallThrow; struct Pokemon *caughtMon = GetBattlerMon(gBattlerTarget); SetMonData(caughtMon, MON_DATA_POKEBALL, &ballId); @@ -11392,6 +11010,13 @@ static void Cmd_givecaughtmon(void) SetMonData(caughtMon, MON_DATA_HELD_ITEM, &lostItem); // Restore non-berry items } + u32 emptySlot; + for (emptySlot = 0; emptySlot < PARTY_SIZE; emptySlot++) + { + if (GetMonData(&gPlayerParty[emptySlot], MON_DATA_SPECIES) == SPECIES_NONE) + break; + } + if (GiveCapturedMonToPlayer(caughtMon) != MON_GIVEN_TO_PARTY && gBattleCommunication[MULTISTRING_CHOOSER] != B_MSG_SWAPPED_INTO_PARTY) { @@ -11414,6 +11039,10 @@ static void Cmd_givecaughtmon(void) gBattleCommunication[MULTISTRING_CHOOSER]++; } + // Copy changedSpecies to allow caught mon to revert to its original species. + if (emptySlot != PARTY_SIZE) + gBattleStruct->partyState[B_SIDE_PLAYER][emptySlot].changedSpecies = GetBattlerPartyState(GetCatchingBattler())->changedSpecies; + gBattleResults.caughtMonSpecies = GetMonData(caughtMon, MON_DATA_SPECIES); GetMonData(caughtMon, MON_DATA_NICKNAME, gBattleResults.caughtMonNick); gBattleResults.caughtMonBall = GetMonData(caughtMon, MON_DATA_POKEBALL); @@ -11513,7 +11142,7 @@ static void Cmd_displaydexinfo(void) void HandleBattleWindow(u8 xStart, u8 yStart, u8 xEnd, u8 yEnd, u8 flags) { - s32 destY, destX; + s32 destY, destX, bgId; u16 var = 0; for (destY = yStart; destY <= yEnd; destY++) @@ -11551,10 +11180,8 @@ void HandleBattleWindow(u8 xStart, u8 yStart, u8 xEnd, u8 yEnd, u8 flags) if (flags & WINDOW_CLEAR) var = 0; - if (flags & WINDOW_BG1) - CopyToBgTilemapBufferRect_ChangePalette(1, &var, destX, destY, 1, 1, 0x11); - else - CopyToBgTilemapBufferRect_ChangePalette(0, &var, destX, destY, 1, 1, 0x11); + bgId = (flags & WINDOW_BG1) ? 1 : 0; + CopyToBgTilemapBufferRect_ChangePalette(bgId, &var, destX, destY, 1, 1, 0x11); } } CopyBgTilemapBufferToVram(1); @@ -11663,7 +11290,7 @@ static void Cmd_sortbattlers(void) CMD_ARGS(); if (!gBattleStruct->battlersSorted) { - for (u32 i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) gBattlersBySpeed[i] = i; SortBattlersBySpeed(gBattlersBySpeed, FALSE); @@ -11695,7 +11322,7 @@ static void Cmd_trainerslideout(void) { CMD_ARGS(u8 position); - u32 battler = GetBattlerForBattleScript(cmd->position); + enum BattlerId battler = GetBattlerForBattleScript(cmd->position); BtlController_EmitTrainerSlideBack(battler, B_COMM_TO_CONTROLLER); MarkBattlerForControllerExec(battler); @@ -11851,6 +11478,10 @@ static void Cmd_tryoverwriteability(void) } } +static void Cmd_dummy(void) +{ +} + static void Cmd_callnative(void) { CMD_ARGS(void (*func)(void)); @@ -11860,7 +11491,7 @@ static void Cmd_callnative(void) // Callnative Funcs -void SaveBattlerTarget(u32 battler) +void SaveBattlerTarget(enum BattlerId battler) { assertf(gBattleStruct->savedTargetCount < ARRAY_COUNT(gBattleStruct->savedBattlerTarget), "Too many savedBattlerTargets") { @@ -11870,7 +11501,7 @@ void SaveBattlerTarget(u32 battler) gBattleStruct->savedBattlerTarget[gBattleStruct->savedTargetCount++] = battler; } -void SaveBattlerAttacker(u32 battler) +void SaveBattlerAttacker(enum BattlerId battler) { assertf(gBattleStruct->savedAttackerCount < ARRAY_COUNT(gBattleStruct->savedBattlerAttacker), "Too many savedBattlerAttackers") { @@ -11927,7 +11558,7 @@ void BS_JumpIfMoreThanHalfHP(void) { NATIVE_ARGS(u8 battler, const u8 *jumpInstr); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (gBattleMons[battler].hp > (gBattleMons[battler].maxHP + 1) / 2) gBattlescriptCurrInstr = cmd->jumpInstr; else @@ -11938,7 +11569,7 @@ void BS_DoStockpileStatChangesWearOff(void) { NATIVE_ARGS(u8 battler, const u8 *statChangeInstr); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (gBattleMons[battler].volatiles.stockpileDef != 0) { SET_STATCHANGER(STAT_DEF, abs(gBattleMons[battler].volatiles.stockpileDef), TRUE); @@ -12035,9 +11666,9 @@ void BS_GetBattlerSide(void) void BS_TrySymbiosis(void) { NATIVE_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); //called by Bestow, Fling, and Bug Bite, which don't work with Cmd_removeitem. - u32 partner = BATTLE_PARTNER(battler); + enum BattlerId partner = BATTLE_PARTNER(battler); if (TryTriggerSymbiosis(battler, partner)) { BestowItem(partner, battler); @@ -12101,7 +11732,7 @@ static void TryUpdateRoundTurnOrder(void) } } -u8 GetFirstFaintedPartyIndex(u8 battler) +u8 GetFirstFaintedPartyIndex(enum BattlerId battler) { u32 i; u32 start = 0; @@ -12173,7 +11804,7 @@ void BS_ItemRestoreHP(void) { NATIVE_ARGS(const u8 *alreadyMaxHpInstr, const u8 *restoreBattlerInstr); u16 healAmount; - u32 battler = MAX_BATTLERS_COUNT; + enum BattlerId battler = MAX_BATTLERS_COUNT; u32 healParam = GetItemEffect(gLastUsedItem)[6]; struct Pokemon *party = GetBattlerParty(gBattlerAttacker); u16 hp = GetMonData(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], MON_DATA_HP); @@ -12229,7 +11860,7 @@ void BS_ItemRestoreHP(void) hp += healAmount; SetMonData(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], MON_DATA_HP, &hp); - u32 partner = BATTLE_PARTNER(gBattlerAttacker); + enum BattlerId partner = BATTLE_PARTNER(gBattlerAttacker); // Absent battlers on the field need to be replaced if (IsDoubleBattle() && (gAbsentBattlerFlags & (1u << partner))) { @@ -12311,7 +11942,7 @@ void BS_ItemRestorePP(void) NATIVE_ARGS(); const u8 *effect = GetItemEffect(gLastUsedItem); u32 i, pp, maxPP, moveId, loopEnd; - u32 battler = MAX_BATTLERS_COUNT; + enum BattlerId battler = MAX_BATTLERS_COUNT; struct Pokemon *mon = (IsOnPlayerSide(gBattlerAttacker)) ? &gPlayerParty[gBattleStruct->itemPartyIndex[gBattlerAttacker]] : &gEnemyParty[gBattleStruct->itemPartyIndex[gBattlerAttacker]]; // Check whether to apply to all moves. @@ -12363,7 +11994,8 @@ void BS_ItemRestorePP(void) void BS_TryRevertWeatherForm(void) { NATIVE_ARGS(); - if (IsBattlerAlive(gBattlerTarget) && TryBattleFormChange(gBattlerTarget, FORM_CHANGE_BATTLE_WEATHER)) + if (IsBattlerAlive(gBattlerTarget) + && TryBattleFormChange(gBattlerTarget, FORM_CHANGE_BATTLE_WEATHER, GetBattlerAbility(gBattlerTarget))) { gBattleScripting.battler = gBattlerTarget; BattleScriptPush(cmd->nextInstr); @@ -12373,38 +12005,11 @@ void BS_TryRevertWeatherForm(void) gBattlescriptCurrInstr = cmd->nextInstr; } -void BS_HandleMegaEvolution(void) -{ - NATIVE_ARGS(u8 battler, u8 caseId); - - u8 battler = GetBattlerForBattleScript(cmd->battler); - HandleScriptMegaPrimalBurst(cmd->caseId, battler, HANDLE_TYPE_MEGA_EVOLUTION); - gBattlescriptCurrInstr = cmd->nextInstr; -} - -void BS_HandlePrimalReversion(void) -{ - NATIVE_ARGS(u8 battler, u8 caseId); - - u8 battler = GetBattlerForBattleScript(cmd->battler); - HandleScriptMegaPrimalBurst(cmd->caseId, battler, HANDLE_TYPE_PRIMAL_REVERSION); - gBattlescriptCurrInstr = cmd->nextInstr; -} - -void BS_HandleUltraBurst(void) -{ - NATIVE_ARGS(u8 battler, u8 caseId); - - u8 battler = GetBattlerForBattleScript(cmd->battler); - HandleScriptMegaPrimalBurst(cmd->caseId, battler, HANDLE_TYPE_ULTRA_BURST); - gBattlescriptCurrInstr = cmd->nextInstr; -} - void BS_JumpIfShellTrap(void) { NATIVE_ARGS(u8 battler, const u8 *jumpInstr); - u8 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (gProtectStructs[battler].shellTrap) gBattlescriptCurrInstr = cmd->jumpInstr; else @@ -12467,7 +12072,7 @@ void BS_SetTerrain(void) void BS_JumpIfTerrainAffected(void) { NATIVE_ARGS(u8 battler, u32 flags, const u8 *jumpInstr); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (IsBattlerTerrainAffected(battler, GetBattlerAbility(battler), GetBattlerHoldEffect(battler), gFieldStatuses, cmd->flags)) gBattlescriptCurrInstr = cmd->jumpInstr; @@ -12546,8 +12151,8 @@ void BS_SetPledge(void) { NATIVE_ARGS(const u8 *jumpInstr); - u32 partner = BATTLE_PARTNER(gBattlerAttacker); - u32 partnerMove = GetChosenMoveFromPosition(partner); + enum BattlerId partner = BATTLE_PARTNER(gBattlerAttacker); + enum Move partnerMove = GetBattlerChosenMove(partner); u32 i = 0; u32 k = 0; @@ -12626,7 +12231,7 @@ void BS_SetPledgeStatus(void) { NATIVE_ARGS(u8 battler, u32 sideStatus); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); enum BattleSide side = GetBattlerSide(battler); gBattleStruct->pledgeMove = FALSE; @@ -12784,7 +12389,7 @@ void BS_TryDefog(void) void BS_TryTriggerStatusForm(void) { NATIVE_ARGS(); - if (TryBattleFormChange(gBattlerTarget, FORM_CHANGE_STATUS)) + if (TryBattleFormChange(gBattlerTarget, FORM_CHANGE_STATUS, GetBattlerAbility(gBattlerTarget))) { gBattleScripting.battler = gBattlerTarget; BattleScriptPush(cmd->nextInstr); @@ -12853,17 +12458,15 @@ void BS_TryTidyUp(void) } } -void BS_TryGulpMissile(void) +void BS_TryTwoTurnMovesPowerHerbFormChange(void) { NATIVE_ARGS(); - if ((gBattleMons[gBattlerAttacker].species == SPECIES_CRAMORANT) - && (gCurrentMove == MOVE_DIVE) - && GetBattlerAbility(gBattlerAttacker) == ABILITY_GULP_MISSILE - && TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_HP_PERCENT)) + if (TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_HP_PERCENT_DURING_MOVE, GetBattlerAbility(gBattlerAttacker))) { + // Doesn't need to set B_ANIM_FORM_CHANGE_INSTANT, as it was already handled on the first turn gBattleScripting.battler = gBattlerAttacker; - gBattlescriptCurrInstr = BattleScript_GulpMissileFormChange; + gBattlescriptCurrInstr = BattleScript_TwoTurnMovesSecondTurnFormChange; } else { @@ -12871,38 +12474,10 @@ void BS_TryGulpMissile(void) } } -void BS_TryActivateGulpMissile(void) -{ - NATIVE_ARGS(); - - if (!gBattleStruct->unableToUseMove - && IsBattlerAlive(gBattlerAttacker) - && IsBattlerTurnDamaged(gBattlerTarget) - && gBattleMons[gBattlerTarget].species != SPECIES_CRAMORANT - && GetBattlerAbility(gBattlerTarget) == ABILITY_GULP_MISSILE) - { - if (!IsAbilityAndRecord(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), ABILITY_MAGIC_GUARD)) - SetPassiveDamageAmount(gBattlerTarget, GetNonDynamaxMaxHP(gBattlerAttacker) / 4); - - switch(gBattleMons[gBattlerTarget].species) - { - case SPECIES_CRAMORANT_GORGING: - TryBattleFormChange(gBattlerTarget, FORM_CHANGE_HIT_BY_MOVE); - BattleScriptCall(BattleScript_GulpMissileGorging); - return; - case SPECIES_CRAMORANT_GULPING: - TryBattleFormChange(gBattlerTarget, FORM_CHANGE_HIT_BY_MOVE); - BattleScriptCall(BattleScript_GulpMissileGulping); - return; - } - } - gBattlescriptCurrInstr = cmd->nextInstr; -} - void BS_TryQuash(void) { NATIVE_ARGS(const u8 *failInstr); - u32 i, j; + enum BattlerId i, j; // It's true if foe is faster, has a bigger priority, or switches if (HasBattlerActedThisTurn(gBattlerTarget)) @@ -12942,7 +12517,7 @@ void BS_CopyFoesStatIncrease(void) { NATIVE_ARGS(u8 battler, const u8 *jumpInstr); u32 stat = 0; - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (gQueuedStatBoosts[battler].stats == 0) { @@ -13005,21 +12580,6 @@ void BS_JumpIfSleepClause(void) gBattlescriptCurrInstr = cmd->nextInstr; } -void BS_FickleBeamDamageCalculation(void) -{ - NATIVE_ARGS(); - - if (RandomPercentage(RNG_FICKLE_BEAM, 30)) - { - gBattleStruct->fickleBeamBoosted = TRUE; - gBattlescriptCurrInstr = BattleScript_FickleBeamDoubled; - } - else - { - gBattlescriptCurrInstr = cmd->nextInstr; - } -} - void BS_TryTarShot(void) { NATIVE_ARGS(const u8 *failInstr); @@ -13048,7 +12608,7 @@ void BS_CanTarShotWork(void) void BS_JumpIfBlockedBySoundproof(void) { NATIVE_ARGS(u8 battler, const u8 *jumpInstr); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (IsSoundMove(gCurrentMove) && GetBattlerAbility(battler) == ABILITY_SOUNDPROOF) { gLastUsedAbility = ABILITY_SOUNDPROOF; @@ -13080,14 +12640,14 @@ void BS_JumpIfNoBerry(void) { NATIVE_ARGS(u8 battler, const u8 *jumpInstr); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (GetItemPocket(gBattleMons[battler].item) == POCKET_BERRIES) gBattlescriptCurrInstr = cmd->nextInstr; else gBattlescriptCurrInstr = cmd->jumpInstr; } -static bool32 IsTeatimeAffected(u32 battler) +static bool32 IsTeatimeAffected(enum BattlerId battler) { if (GetItemPocket(gBattleMons[battler].item) != POCKET_BERRIES) return FALSE; // Only berries @@ -13099,9 +12659,9 @@ static bool32 IsTeatimeAffected(u32 battler) void BS_CheckTeaTimeTargets(void) { NATIVE_ARGS(const u8 *failInstr); - u32 count = 0, i; + u32 count = 0; - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (IsTeatimeAffected(i)) count++; @@ -13116,7 +12676,7 @@ void BS_TryWindRiderPower(void) { NATIVE_ARGS(u8 battler, const u8 *failInstr); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); enum Ability ability = GetBattlerAbility(battler); if (IsBattlerAlly(battler, gBattlerAttacker) && (ability == ABILITY_WIND_RIDER || ability == ABILITY_WIND_POWER)) @@ -13136,7 +12696,7 @@ void BS_ActivateWeatherChangeAbilities(void) { NATIVE_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); gBattlescriptCurrInstr = cmd->nextInstr; AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battler, GetBattlerAbility(battler), MOVE_NONE, TRUE); } @@ -13145,7 +12705,7 @@ void BS_ActivateTerrainChangeAbilities(void) { NATIVE_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); gBattlescriptCurrInstr = cmd->nextInstr; AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, battler, GetBattlerAbility(battler), MOVE_NONE, TRUE); } @@ -13154,7 +12714,7 @@ void BS_ResetTerrainAbilityFlags(void) { NATIVE_ARGS(); // reset terrain ability checks - for (u32 i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) gBattleMons[i].volatiles.terrainAbilityDone = 0; gBattlescriptCurrInstr = cmd->nextInstr; @@ -13164,7 +12724,7 @@ void BS_StoreHealingWish(void) { NATIVE_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (GetMoveEffect(gCurrentMove) == EFFECT_LUNAR_DANCE) gBattleStruct->battlerState[battler].storedLunarDance = TRUE; else @@ -13198,7 +12758,7 @@ void BS_TryRevivalBlessing(void) if (IsDoubleBattle() && gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerAttacker)] == gSelectedMonPartyId) { - u32 i = BATTLE_PARTNER(gBattlerAttacker); + enum BattlerId i = BATTLE_PARTNER(gBattlerAttacker); gAbsentBattlerFlags &= ~(1u << i); gBattleStruct->monToSwitchIntoId[i] = gSelectedMonPartyId; gBattleScripting.battler = i; @@ -13231,7 +12791,7 @@ void BS_JumpIfCommanderActive(void) static void UpdatePokeFlutePartyStatus(struct Pokemon* party, u8 position) { s32 i; - u8 battler; + enum BattlerId battler; u32 monToCheck, status; u16 species, abilityNum; monToCheck = 0; @@ -13261,8 +12821,7 @@ void BS_CheckPokeFlute(void) NATIVE_ARGS(); gBattleCommunication[MULTISTRING_CHOOSER] = 0; - s32 i; - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (GetBattlerAbility(i) != ABILITY_SOUNDPROOF) { @@ -13547,7 +13106,6 @@ void BS_TryRecycleBerry(void) NATIVE_ARGS(const u8 *failInstr); u16 *usedHeldItem = &GetBattlerPartyState(gBattlerTarget)->usedHeldItem; if (gBattleMons[gBattlerTarget].item == ITEM_NONE - && gBattleStruct->changedItems[gBattlerTarget] == ITEM_NONE // Will not inherit an item && GetItemPocket(*usedHeldItem) == POCKET_BERRIES) { gLastUsedItem = *usedHeldItem; @@ -13624,7 +13182,7 @@ void BS_JumpIfIntimidateAbilityPrevented(void) void BS_JumpIfCanGigantamax(void) { NATIVE_ARGS(u8 battler, const u8 *jumpInstr); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (GetMonData(GetBattlerMon(battler), MON_DATA_GIGANTAMAX_FACTOR) && GetGMaxTargetSpecies(gBattleMons[battler].species) != SPECIES_NONE) @@ -13704,7 +13262,7 @@ void BS_TryBoosterEnergy(void) for (u32 orderNum = 0; orderNum < gBattlersCount; orderNum++) { - u32 battler = gBattlerByTurnOrder[orderNum]; + enum BattlerId battler = gBattlerByTurnOrder[orderNum]; enum HoldEffect holdEffect = GetBattlerHoldEffect(battler); if (holdEffect != HOLD_EFFECT_BOOSTER_ENERGY) continue; @@ -13724,7 +13282,7 @@ void BS_TryBoosterEnergy(void) void BS_JumpIfAbilityCantBeReactivated(void) { NATIVE_ARGS(u8 battler, const u8 *jumpInstr); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); u32 ability = gBattleMons[battler].ability; switch (ability) @@ -13747,7 +13305,7 @@ void BS_JumpIfAbilityCantBeReactivated(void) void BS_TryActivateAbilityShield(void) { NATIVE_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); enum Ability ability = GetBattlerAbility(battler); gBattlescriptCurrInstr = cmd->nextInstr; @@ -13795,7 +13353,7 @@ void BS_JumpIfAbsent(void) void BS_JumpIfHoldEffect(void) { NATIVE_ARGS(u8 battler, u8 holdEffect, const u8 *jumpInstr, u8 equal); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if ((GetBattlerHoldEffect(battler) == cmd->holdEffect) == cmd->equal) { if (cmd->equal) @@ -13813,7 +13371,7 @@ void BS_JumpIfHoldEffect(void) void BS_JumpIfNoAlly(void) { NATIVE_ARGS(u8 battler, const u8 *jumpInstr); - u32 partner = BATTLE_PARTNER(GetBattlerForBattleScript(cmd->battler)); + enum BattlerId partner = BATTLE_PARTNER(GetBattlerForBattleScript(cmd->battler)); if (!IsBattlerAlive(partner)) gBattlescriptCurrInstr = cmd->jumpInstr; else @@ -13915,7 +13473,7 @@ void BS_TryFriskMessage(void) void BS_SetTracedAbility(void) { NATIVE_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); gBattleMons[battler].ability = gBattleMons[battler].volatiles.overwrittenAbility = gBattleStruct->tracedAbility[battler]; gBattlescriptCurrInstr = cmd->nextInstr; } @@ -13923,7 +13481,7 @@ void BS_SetTracedAbility(void) void BS_TryIllusionOff(void) { NATIVE_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (TryClearIllusion(battler, GetBattlerAbility(battler))) return; gBattlescriptCurrInstr = cmd->nextInstr; @@ -13932,7 +13490,7 @@ void BS_TryIllusionOff(void) void BS_UpdateNick(void) { NATIVE_ARGS(); - u32 battler = gBattleScripting.battler; + enum BattlerId battler = gBattleScripting.battler; UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], GetBattlerMon(battler), HEALTHBOX_NICK); gBattlescriptCurrInstr = cmd->nextInstr; } @@ -14031,7 +13589,7 @@ void BS_UpdateChoiceMoveOnLvlUp(void) NATIVE_ARGS(); if (gBattlerPartyIndexes[0] == gBattleStruct->expGetterMonId || gBattlerPartyIndexes[2] == gBattleStruct->expGetterMonId) { - u32 battler; + enum BattlerId battler; if (gBattlerPartyIndexes[0] == gBattleStruct->expGetterMonId) battler = 0; else @@ -14054,8 +13612,8 @@ void BS_ResetPlayerFainted(void) NATIVE_ARGS(); if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_DOUBLE)) && gBattleTypeFlags & BATTLE_TYPE_TRAINER - && IsBattlerAlive(B_POSITION_PLAYER_LEFT) - && IsBattlerAlive(B_POSITION_OPPONENT_LEFT)) + && IsBattlerAlive(GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)) + && IsBattlerAlive(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT))) { gHitMarker &= ~HITMARKER_PLAYER_FAINTED; } @@ -14066,44 +13624,44 @@ void BS_PalaceFlavorText(void) { NATIVE_ARGS(); // Try and print end-of-turn Battle Palace flavor text (e.g. "A glint appears in mon's eyes") - // u32 battler; - // gBattleCommunication[0] = FALSE; // whether or not msg should be printed - // gBattleScripting.battler = battler = gBattleCommunication[1]; - // if (!(gBattleStruct->palaceFlags & (1u << battler)) - // && gBattleMons[battler].maxHP / 2 >= gBattleMons[battler].hp - // && IsBattlerAlive(battler) - // && !(gBattleMons[battler].status1 & STATUS1_SLEEP)) - // { - // gBattleStruct->palaceFlags |= 1u << battler; - // gBattleCommunication[0] = TRUE; - // gBattleCommunication[MULTISTRING_CHOOSER] = gNaturesInfo[GetNatureFromPersonality(gBattleMons[battler].personality)].battlePalaceFlavorText; - // } + enum BattlerId battler; + gBattleCommunication[0] = FALSE; // whether or not msg should be printed + gBattleScripting.battler = battler = gBattleCommunication[1]; + if (!(gBattleStruct->palaceFlags & (1u << battler)) + && gBattleMons[battler].maxHP / 2 >= gBattleMons[battler].hp + && IsBattlerAlive(battler) + && !(gBattleMons[battler].status1 & STATUS1_SLEEP)) + { + gBattleStruct->palaceFlags |= 1u << battler; + gBattleCommunication[0] = TRUE; + gBattleCommunication[MULTISTRING_CHOOSER] = gNaturesInfo[GetNatureFromPersonality(gBattleMons[battler].personality)].battlePalaceFlavorText; + } gBattlescriptCurrInstr = cmd->nextInstr; } void BS_ArenaJudgmentWindow(void) { NATIVE_ARGS(); - // u32 judgmentWindow = BattleArena_ShowJudgmentWindow(&gBattleCommunication[0]); + u32 judgmentWindow = BattleArena_ShowJudgmentWindow(&gBattleCommunication[0]); - // // BattleArena_ShowJudgmentWindow's last state was an intermediate step. - // // Return without advancing the current instruction so that it will be called again. - // if (judgmentWindow == ARENA_RESULT_RUNNING) - // return; + // BattleArena_ShowJudgmentWindow's last state was an intermediate step. + // Return without advancing the current instruction so that it will be called again. + if (judgmentWindow == ARENA_RESULT_RUNNING) + return; - // gBattleCommunication[1] = judgmentWindow; + gBattleCommunication[1] = judgmentWindow; gBattlescriptCurrInstr = cmd->nextInstr; } -static void SetArenMonLostValues(u32 battler, enum BattleSide side) +static void SetArenMonLostValues(enum BattlerId battler, enum BattleSide side) { - // gBattleMons[battler].hp = 0; - // gHitMarker |= HITMARKER_FAINTED(battler); - // if (side == B_SIDE_PLAYER) - // gBattleStruct->arenaLostPlayerMons |= 1u << gBattlerPartyIndexes[battler]; - // else - // gBattleStruct->arenaLostOpponentMons |= 1u << gBattlerPartyIndexes[battler]; - // gBattleMons[battler].volatiles.truantSwitchInHack = TRUE; + gBattleMons[battler].hp = 0; + gHitMarker |= HITMARKER_FAINTED(battler); + if (side == B_SIDE_PLAYER) + gBattleStruct->arenaLostPlayerMons |= 1u << gBattlerPartyIndexes[battler]; + else + gBattleStruct->arenaLostOpponentMons |= 1u << gBattlerPartyIndexes[battler]; + gBattleMons[battler].volatiles.truantSwitchInHack = TRUE; } #define playerMon 0 @@ -14143,30 +13701,30 @@ void BS_ForfeitYesNoBox(void) void BS_DrawArenaRefTextBox(void) { NATIVE_ARGS(); - // DrawArenaRefereeTextBox(); + DrawArenaRefereeTextBox(); gBattlescriptCurrInstr = cmd->nextInstr; } void BS_EraseArenaRefTextBox(void) { NATIVE_ARGS(); - // EraseArenaRefereeTextBox(); + EraseArenaRefereeTextBox(); gBattlescriptCurrInstr = cmd->nextInstr; } void BS_ArenaJudgmentString(void) { NATIVE_ARGS(u8 id); - // BattleStringExpandPlaceholdersToDisplayedString(gRefereeStringsTable[cmd->id]); - // BattlePutTextOnWindow(gDisplayedStringBattle, ARENA_WIN_JUDGMENT_TEXT); + BattleStringExpandPlaceholdersToDisplayedString(gRefereeStringsTable[cmd->id]); + BattlePutTextOnWindow(gDisplayedStringBattle, ARENA_WIN_JUDGMENT_TEXT); gBattlescriptCurrInstr = cmd->nextInstr; } void BS_ArenaWaitMessage(void) { NATIVE_ARGS(); - // if (IsTextPrinterActiveOnWindow(ARENA_WIN_JUDGMENT_TEXT)) - // return; + if (IsTextPrinterActiveOnWindow(ARENA_WIN_JUDGMENT_TEXT)) + return; gBattlescriptCurrInstr = cmd->nextInstr; } @@ -14231,15 +13789,15 @@ void BS_SetAlreadyStatusedMoveAttempt(void) void BS_PalaceTryEscapeStatus(void) { NATIVE_ARGS(); - // if (BattlePalace_TryEscapeStatus(gBattlerAttacker)) - // return; + if (BattlePalace_TryEscapeStatus(gBattlerAttacker)) + return; gBattlescriptCurrInstr = cmd->nextInstr; } void BS_SetTeleportOutcome(void) { NATIVE_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); // Don't end the battle if one of the wild mons teleported from the wild double battle // and its partner is still alive. @@ -14281,7 +13839,7 @@ void BS_StatTextBuffer(void) void BS_SwitchinAbilities(void) { NATIVE_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); u32 ability = GetBattlerAbility(battler); gBattlescriptCurrInstr = cmd->nextInstr; AbilityBattleEffects(ABILITYEFFECT_TERA_SHIFT, battler, ability, MOVE_NONE, TRUE); @@ -14306,7 +13864,7 @@ void BS_InstantHpDrop(void) void BS_ClearStatus(void) { NATIVE_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); gBattleMons[battler].status1 = 0; BtlController_EmitSetMonData( battler, @@ -14322,7 +13880,7 @@ void BS_ClearStatus(void) void BS_RestoreMovePp(void) { NATIVE_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); u32 moveIndex; u32 data[MAX_MON_MOVES + 1]; for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) @@ -14339,7 +13897,7 @@ void BS_RestoreMovePp(void) void BS_TryActivateReceiver(void) { NATIVE_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); gBattlerAbility = BATTLE_PARTNER(battler); u32 partnerAbility = GetBattlerAbility(gBattlerAbility); if (IsBattlerAlive(gBattlerAbility) @@ -14502,22 +14060,21 @@ void BS_TrySoak(void) void BS_HandleFormChange(void) { - NATIVE_ARGS(u8 battler, u8 case_); - u32 battler = GetBattlerForBattleScript(cmd->battler); - struct Pokemon *mon = GetBattlerMon(battler); + NATIVE_ARGS(u8 battler, u8 caseId, bool8 bufferSpeciesName); - if (cmd->case_ == 0) // Change species. + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); + if (cmd->caseId == 0) // Buffer name and emit species. { + if (cmd->bufferSpeciesName) + PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[battler].species); BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_SPECIES_BATTLE, 1u << gBattlerPartyIndexes[battler], sizeof(gBattleMons[battler].species), &gBattleMons[battler].species); MarkBattlerForControllerExec(battler); } - else if (cmd->case_ == 1) // Change stats. - { - RecalcBattlerStats(battler, mon, FALSE); - } else // Update healthbox. { - UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL); + UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], GetBattlerMon(battler), HEALTHBOX_ALL); + if (!IsOnPlayerSide(battler)) + SetBattlerShadowSpriteCallback(battler, gBattleMons[battler].species); } gBattlescriptCurrInstr = cmd->nextInstr; } @@ -14654,7 +14211,7 @@ void BS_TryPsychoShift(void) void BS_CureStatus(void) { NATIVE_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (gBattleMons[battler].status1 & STATUS1_SLEEP) TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]); @@ -14709,7 +14266,7 @@ void BS_TryBestow(void) void BS_HandleTrainerSlideMsg(void) { NATIVE_ARGS(u8 battler, u8 case_); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (cmd->case_ == PRINT_SLIDE_MESSAGE) { BtlController_EmitPrintString(battler, B_COMM_TO_CONTROLLER, STRINGID_TRAINERSLIDE); @@ -14722,7 +14279,7 @@ void BS_HandleTrainerSlideMsg(void) SetBattlerShadowSpriteCallback(battler, gBattleMons[battler].species); BattleLoadMonSpriteGfx(GetBattlerMon(battler), battler); } - u32 partner = BATTLE_PARTNER(battler); + enum BattlerId partner = BATTLE_PARTNER(battler); if (IsBattlerAlive(partner)) { SetBattlerShadowSpriteCallback(partner, gBattleMons[partner].species); @@ -14735,7 +14292,7 @@ void BS_HandleTrainerSlideMsg(void) void BS_TryTrainerSlideMsgFirstOff(void) { NATIVE_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); u32 shouldDoTrainerSlide = 0; if ((shouldDoTrainerSlide = ShouldDoTrainerSlide(battler, TRAINER_SLIDE_PLAYER_LANDS_FIRST_DOWN))) { @@ -14766,7 +14323,7 @@ void BS_TryTrainerSlideMsgLastOn(void) { NATIVE_ARGS(u8 battler); u32 shouldDoTrainerSlide = 0; - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if ((shouldDoTrainerSlide = ShouldDoTrainerSlide(battler, TRAINER_SLIDE_LAST_SWITCHIN))) { gBattleScripting.battler = battler; @@ -14837,7 +14394,7 @@ void BS_TryThirdType(void) void BS_DestroyAbilityPopup(void) { NATIVE_ARGS(); - for (u32 battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) DestroyAbilityPopUp(battler); gBattlescriptCurrInstr = cmd->nextInstr; } @@ -14845,7 +14402,7 @@ void BS_DestroyAbilityPopup(void) void BS_GetTotemBoost(void) { NATIVE_ARGS(const u8 *jumpInstr); - u32 battler = gBattlerAttacker; + enum BattlerId battler = gBattlerAttacker; if (gQueuedStatBoosts[battler].stats == 0) { gBattlescriptCurrInstr = cmd->nextInstr; // stats done, exit @@ -14883,7 +14440,7 @@ void BS_GetTotemBoost(void) void BS_ActivateItemEffects(void) { NATIVE_ARGS(); - for (u32 battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { if (!IsBattlerAlive(battler)) continue; @@ -14897,7 +14454,7 @@ void BS_ActivateItemEffects(void) void BS_TryRoomService(void) { NATIVE_ARGS(u8 battler, const u8 *failInstr); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); enum HoldEffect holdEffect = GetBattlerHoldEffect(battler); if (holdEffect == HOLD_EFFECT_ROOM_SERVICE && ItemBattleEffects(battler, 0, holdEffect, IsOnEffectActivation)) return; @@ -14907,7 +14464,7 @@ void BS_TryRoomService(void) void BS_TryTerrainSeed(void) { NATIVE_ARGS(u8 battler, const u8 *failInstr); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); enum HoldEffect holdEffect = GetBattlerHoldEffect(battler); if (holdEffect == HOLD_EFFECT_TERRAIN_SEED && ItemBattleEffects(battler, 0, holdEffect, IsOnEffectActivation)) return; @@ -14917,7 +14474,7 @@ void BS_TryTerrainSeed(void) void BS_MakeInvisible(void) { NATIVE_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (gBattleControllerExecFlags) return; @@ -14930,10 +14487,10 @@ void BS_MakeInvisible(void) void BS_JumpIfTeamHealthy(void) { NATIVE_ARGS(const u8 *jumpInstr); - u32 battler = gBattlerAttacker; + enum BattlerId battler = gBattlerAttacker; if ((IsDoubleBattle()) && IsBattlerAlive(BATTLE_PARTNER(battler))) { - u8 partner = BATTLE_PARTNER(battler); + enum BattlerId partner = BATTLE_PARTNER(battler); if ((gBattleMons[battler].hp == gBattleMons[battler].maxHP && !(gBattleMons[battler].status1 & STATUS1_ANY)) && (gBattleMons[partner].hp == gBattleMons[partner].maxHP && !(gBattleMons[partner].status1 & STATUS1_ANY))) gBattlescriptCurrInstr = cmd->jumpInstr; @@ -14952,7 +14509,7 @@ void BS_JumpIfTeamHealthy(void) void BS_TryHealQuarterHealth(void) { NATIVE_ARGS(u8 battler, const u8 *failInstr); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); SetHealAmount(battler, GetNonDynamaxMaxHP(battler) / 4); if (gBattleMons[battler].hp == gBattleMons[battler].maxHP) gBattlescriptCurrInstr = cmd->failInstr; // fail @@ -15061,7 +14618,7 @@ void BS_TryToClearPrimalWeather(void) NATIVE_ARGS(); bool32 shouldNotClear = FALSE; - for (u32 i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { enum Ability ability = GetBattlerAbility(i); if (((ability == ABILITY_DESOLATE_LAND && gBattleWeather & B_WEATHER_SUN_PRIMAL) @@ -15109,7 +14666,7 @@ void BS_TryEndNeutralizingGas(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static bool32 IsRototillerAffected(u32 battler, u32 move) +static bool32 IsRototillerAffected(enum BattlerId battler, u32 move) { if (IsBattlerUnaffectedByMove(battler)) return FALSE; @@ -15125,7 +14682,7 @@ void BS_GetRototillerTargets(void) NATIVE_ARGS(const u8 *failInstr); u32 count = 0; - for (u32 battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { if (IsRototillerAffected(battler, gCurrentMove)) count++; @@ -15143,7 +14700,7 @@ void BS_GetRototillerTargets(void) void BS_ConsumeBerry(void) { NATIVE_ARGS(u8 battler, bool8 fromBattler); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); if (cmd->fromBattler) gLastUsedItem = gBattleMons[battler].item; @@ -15195,7 +14752,7 @@ void BS_JumpIfSpecies(void) void BS_JumpIfAbilityPreventsRest(void) { NATIVE_ARGS(u8 battler, const u8 *jumpInstr); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); u32 ability = GetBattlerAbility(battler); if (GetConfig(CONFIG_LEAF_GUARD_PREVENTS_REST) >= GEN_5 && IsLeafGuardProtected(battler, ability)) gBattlescriptCurrInstr = cmd->jumpInstr; @@ -15244,7 +14801,7 @@ void BS_CutOneThirdHpAndRaiseStats(void) void BS_SetPoltergeistMessage(void) { - NATIVE_ARGS(const u8 *failInstr); + NATIVE_ARGS(); PREPARE_ITEM_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerTarget].item); gLastUsedItem = gBattleMons[gBattlerTarget].item; gBattlescriptCurrInstr = cmd->nextInstr; @@ -15293,7 +14850,7 @@ void BS_TryActivateAbilityWithAbilityShield(void) { NATIVE_ARGS(u8 battler, bool8 switchedItems); - u32 battler = GetBattlerForBattleScript(cmd->battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); gBattlescriptCurrInstr = cmd->nextInstr; @@ -15325,6 +14882,46 @@ void BS_TryActivateAbilityWithAbilityShield(void) } } +// Updates Dynamax HP multipliers and healthboxes. +void BS_UpdateDynamax(void) +{ + NATIVE_ARGS(); + enum BattlerId battler = gBattleScripting.battler; + struct Pokemon *mon = GetBattlerMon(battler); + + if (!IsGigantamaxed(battler)) // RecalcBattlerStats will get called on form change. + RecalcBattlerStats(battler, mon, GetActiveGimmick(battler) == GIMMICK_DYNAMAX); + + UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL); + gBattlescriptCurrInstr = cmd->nextInstr; +} + +// Goes to the jump instruction if the target is Dynamaxed. +void BS_JumpIfDynamaxed(void) +{ + NATIVE_ARGS(const u8 *jumpInstr); + if ((GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX)) + gBattlescriptCurrInstr = cmd->jumpInstr; + else + gBattlescriptCurrInstr = cmd->nextInstr; +} + +void BS_UndoDynamax(void) +{ + NATIVE_ARGS(u8 battler); + enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); + + if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX) + { + UndoDynamax(battler); + gBattleScripting.battler = battler; + BattleScriptCall(BattleScript_DynamaxEnds_Ret); + return; + } + + gBattlescriptCurrInstr = cmd->nextInstr; +} + // firered void BS_GetBattlersForRecall(void) { diff --git a/src/battle_setup.c b/src/battle_setup.c index 63abd1c15..7a77ee7dd 100644 --- a/src/battle_setup.c +++ b/src/battle_setup.c @@ -2,6 +2,7 @@ #include "battle.h" #include "load_save.h" #include "battle_setup.h" +#include "battle_tower.h" #include "battle_transition.h" #include "main.h" #include "task.h" @@ -29,9 +30,11 @@ #include "string_util.h" #include "overworld.h" #include "field_weather.h" +#include "battle_tower.h" // #include "gym_leader_rematch.h" -// #include "battle_pike.h" -// #include "battle_pyramid.h" +#include "battle_frontier.h" +#include "battle_pike.h" +#include "battle_pyramid.h" #include "fldeff.h" // #include "fldeff_misc.h" #include "field_control_avatar.h" @@ -40,6 +43,8 @@ #include "data.h" #include "vs_seeker.h" #include "item.h" +#include "script.h" +// #include "field_name_box.h" #include "constants/battle_frontier.h" #include "constants/battle_setup.h" #include "constants/event_objects.h" @@ -50,11 +55,13 @@ #include "constants/trainers.h" // #include "constants/trainer_hill.h" #include "constants/weather.h" +#include "fishing.h" #include "wild_encounter.h" #include "help_system.h" #include "quest_log.h" -enum { +enum TransitionType +{ TRANSITION_TYPE_NORMAL, TRANSITION_TYPE_CAVE, TRANSITION_TYPE_FLASH, @@ -68,6 +75,7 @@ static void DoGhostBattle(void); static void DoStandardWildBattle(bool32 isDouble); static void CB2_EndWildBattle(void); static void CB2_EndScriptedWildBattle(void); +static void CB2_EndMarowakBattle(void); // static void TryUpdateGymLeaderRematchFromWild(void); // static void TryUpdateGymLeaderRematchFromTrainer(void); // static void CB2_GiveStarter(void); @@ -75,7 +83,6 @@ static void CB2_EndScriptedWildBattle(void); // static void CB2_EndFirstBattle(void); // static void SaveChangesToPlayerParty(void); // static void HandleBattleVariantEndParty(void); -static void CB2_EndMarowakBattle(void); static void CB2_EndTrainerBattle(void); static bool32 IsPlayerDefeated(u32 battleOutcome); // #if FREE_MATCH_CALL == FALSE @@ -110,6 +117,38 @@ static const u8 sBattleTransitionTable_Trainer[][2] = [TRANSITION_TYPE_WATER] = {B_TRANSITION_SWIRL, B_TRANSITION_RIPPLE}, }; +// Battle Frontier (excluding Pyramid and Dome, which have their own tables below) +static const u8 sBattleTransitionTable_BattleFrontier[] = +{ + B_TRANSITION_FRONTIER_LOGO_WIGGLE, + B_TRANSITION_FRONTIER_LOGO_WAVE, + B_TRANSITION_FRONTIER_SQUARES, + B_TRANSITION_FRONTIER_SQUARES_SCROLL, + B_TRANSITION_FRONTIER_CIRCLES_MEET, + B_TRANSITION_FRONTIER_CIRCLES_CROSS, + B_TRANSITION_FRONTIER_CIRCLES_ASYMMETRIC_SPIRAL, + B_TRANSITION_FRONTIER_CIRCLES_SYMMETRIC_SPIRAL, + B_TRANSITION_FRONTIER_CIRCLES_MEET_IN_SEQ, + B_TRANSITION_FRONTIER_CIRCLES_CROSS_IN_SEQ, + B_TRANSITION_FRONTIER_CIRCLES_ASYMMETRIC_SPIRAL_IN_SEQ, + B_TRANSITION_FRONTIER_CIRCLES_SYMMETRIC_SPIRAL_IN_SEQ +}; + +static const u8 sBattleTransitionTable_BattlePyramid[] = +{ + B_TRANSITION_FRONTIER_SQUARES, + B_TRANSITION_FRONTIER_SQUARES_SCROLL, + B_TRANSITION_FRONTIER_SQUARES_SPIRAL +}; + +static const u8 sBattleTransitionTable_BattleDome[] = +{ + B_TRANSITION_FRONTIER_LOGO_WIGGLE, + B_TRANSITION_FRONTIER_SQUARES, + B_TRANSITION_FRONTIER_SQUARES_SCROLL, + B_TRANSITION_FRONTIER_SQUARES_SPIRAL +}; + #define tState data[0] #define tTransition data[1] @@ -143,7 +182,7 @@ static void Task_BattleStart(u8 taskId) } } -static void CreateBattleStartTask(u8 transition, u16 song) +static void CreateBattleStartTask(enum BattleTransition transition, u16 song) { u8 taskId = CreateTask(Task_BattleStart, 1); @@ -240,11 +279,11 @@ static void DoStandardWildBattle(bool32 isDouble) } else if (isDouble) gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; - // if (InBattlePyramid()) - // { - // VarSet(VAR_TEMP_E, 0); - // gBattleTypeFlags |= BATTLE_TYPE_PYRAMID; - // } + if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) + { + VarSet(VAR_TEMP_E, 0); + gBattleTypeFlags |= BATTLE_TYPE_PYRAMID; + } CreateBattleStartTask(GetWildBattleTransition(), 0); IncrementGameStat(GAME_STAT_TOTAL_BATTLES); IncrementGameStat(GAME_STAT_WILD_BATTLES); @@ -259,11 +298,11 @@ void DoStandardWildBattle_Debug(void) StopPlayerAvatar(); gMain.savedCallback = CB2_EndWildBattle; gBattleTypeFlags = 0; - // if (InBattlePyramid()) - // { - // VarSet(VAR_TEMP_PLAYING_PYRAMID_MUSIC, 0); - // gBattleTypeFlags |= BATTLE_TYPE_PYRAMID; - // } + if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) + { + VarSet(VAR_TEMP_PLAYING_PYRAMID_MUSIC, 0); + gBattleTypeFlags |= BATTLE_TYPE_PYRAMID; + } CreateBattleStartTask_Debug(GetWildBattleTransition(), 0); //IncrementGameStat(GAME_STAT_TOTAL_BATTLES); //IncrementGameStat(GAME_STAT_WILD_BATTLES); @@ -330,16 +369,26 @@ static void DoTrainerBattle(void) // TryUpdateGymLeaderRematchFromTrainer(); } -// static void DoBattlePyramidTrainerHillBattle(void) -// { -// if (InBattlePyramid()) -// CreateBattleStartTask(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_PYRAMID), 0); -// else -// CreateBattleStartTask(GetSpecialBattleTransition(B_TRANSITION_GROUP_TRAINER_HILL), 0); +static void DoBattlePyramidTrainerHillBattle(void) +{ + if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) + CreateBattleStartTask(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_PYRAMID), 0); + else + CreateBattleStartTask(GetSpecialBattleTransition(B_TRANSITION_GROUP_TRAINER_TOWER), 0); -// IncrementGameStat(GAME_STAT_TOTAL_BATTLES); -// IncrementGameStat(GAME_STAT_TRAINER_BATTLES); -// TryUpdateGymLeaderRematchFromTrainer(); + IncrementGameStat(GAME_STAT_TOTAL_BATTLES); + IncrementGameStat(GAME_STAT_TRAINER_BATTLES); + // TryUpdateGymLeaderRematchFromTrainer(); +} + +// Initiates battle where Wally catches Ralts +// void StartWallyTutorialBattle(void) +// { +// CreateMaleMon(&gEnemyParty[0], SPECIES_RALTS, 5); +// LockPlayerFieldControls(); +// gMain.savedCallback = CB2_ReturnToFieldContinueScriptPlayMapMusic; +// gBattleTypeFlags = BATTLE_TYPE_CATCH_TUTORIAL; +// CreateBattleStartTask(B_TRANSITION_SLICE, 0); // } void StartOldManTutorialBattle(void) @@ -387,6 +436,7 @@ void StartMarowakBattle(void) CreateMonWithIVsPersonality(&gEnemyParty[0], SPECIES_MAROWAK, 30, 31, personality); } + CreateBattleStartTask(GetWildBattleTransition(), 0); SetMonData(&gEnemyParty[0], MON_DATA_NICKNAME, gText_Ghost); IncrementGameStat(GAME_STAT_TOTAL_BATTLES); @@ -407,13 +457,24 @@ void BattleSetup_StartLatiBattle(void) void BattleSetup_StartLegendaryBattle(void) { - u16 species; LockPlayerFieldControls(); gMain.savedCallback = CB2_EndScriptedWildBattle; gBattleTypeFlags = BATTLE_TYPE_LEGENDARY; - species = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); - switch (species) + + switch (GetMonData(&gEnemyParty[0], MON_DATA_SPECIES)) { + case SPECIES_GROUDON: + case SPECIES_GROUDON_PRIMAL: + CreateBattleStartTask(B_TRANSITION_GROUDON, MUS_VS_KYOGRE_GROUDON); + break; + case SPECIES_KYOGRE: + case SPECIES_KYOGRE_PRIMAL: + CreateBattleStartTask(B_TRANSITION_KYOGRE, MUS_VS_KYOGRE_GROUDON); + break; + case SPECIES_RAYQUAZA: + case SPECIES_RAYQUAZA_MEGA: + CreateBattleStartTask(B_TRANSITION_RAYQUAZA, MUS_VS_RAYQUAZA); + break; case SPECIES_MEWTWO: case SPECIES_MEWTWO_MEGA_X: case SPECIES_MEWTWO_MEGA_Y: @@ -425,9 +486,14 @@ void BattleSetup_StartLegendaryBattle(void) case SPECIES_DEOXYS_SPEED: CreateBattleStartTask(B_TRANSITION_BLUR, MUS_VS_DEOXYS); break; + case SPECIES_LUGIA: + case SPECIES_HO_OH: default: CreateBattleStartTask(B_TRANSITION_BLUR, MUS_VS_LEGEND); break; + case SPECIES_MEW: + CreateBattleStartTask(B_TRANSITION_GRID_SQUARES, MUS_VS_MEW); + break; } IncrementGameStat(GAME_STAT_TOTAL_BATTLES); @@ -442,7 +508,10 @@ void StartGroudonKyogreBattle(void) gMain.savedCallback = CB2_EndScriptedWildBattle; gBattleTypeFlags = BATTLE_TYPE_LEGENDARY; - CreateBattleStartTask(B_TRANSITION_ANGLED_WIPES, MUS_RS_VS_TRAINER); + if (gGameVersion == VERSION_RUBY) + CreateBattleStartTask(B_TRANSITION_ANGLED_WIPES, MUS_VS_KYOGRE_GROUDON); // GROUDON + else + CreateBattleStartTask(B_TRANSITION_RIPPLE, MUS_VS_KYOGRE_GROUDON); // KYOGRE IncrementGameStat(GAME_STAT_TOTAL_BATTLES); IncrementGameStat(GAME_STAT_WILD_BATTLES); @@ -452,14 +521,30 @@ void StartGroudonKyogreBattle(void) void StartRegiBattle(void) { - u8 transitionId = B_TRANSITION_BLUR; - // u16 species; + enum BattleTransition transitionId; + u16 species; LockPlayerFieldControls(); gMain.savedCallback = CB2_EndScriptedWildBattle; gBattleTypeFlags = BATTLE_TYPE_LEGENDARY; - CreateBattleStartTask(transitionId, MUS_RS_VS_TRAINER); + species = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES); + switch (species) + { + case SPECIES_REGIROCK: + transitionId = B_TRANSITION_REGIROCK; + break; + case SPECIES_REGICE: + transitionId = B_TRANSITION_REGICE; + break; + case SPECIES_REGISTEEL: + transitionId = B_TRANSITION_REGISTEEL; + break; + default: + transitionId = B_TRANSITION_GRID_SQUARES; + break; + } + CreateBattleStartTask(transitionId, MUS_VS_REGI); IncrementGameStat(GAME_STAT_TOTAL_BATTLES); IncrementGameStat(GAME_STAT_WILD_BATTLES); @@ -494,7 +579,7 @@ static void CB2_EndWildBattle(void) HealPlayerParty(); } - if (IsPlayerDefeated(gBattleOutcome) == TRUE) + if (IsPlayerDefeated(gBattleOutcome) == TRUE && CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE && !InBattlePike()) { SetMainCallback2(CB2_WhiteOut); } @@ -513,7 +598,10 @@ static void CB2_EndScriptedWildBattle(void) if (IsPlayerDefeated(gBattleOutcome) == TRUE) { - SetMainCallback2(CB2_WhiteOut); + if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) + SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); + else + SetMainCallback2(CB2_WhiteOut); } else { @@ -526,6 +614,7 @@ static void CB2_EndMarowakBattle(void) { CpuFill16(0, (void *)BG_PLTT, BG_PLTT_SIZE); ResetOamRange(0, 128); + if (IsPlayerDefeated(gBattleOutcome)) { SetMainCallback2(CB2_WhiteOut); @@ -542,12 +631,12 @@ static void CB2_EndMarowakBattle(void) } } -u8 BattleSetup_GetEnvironmentId(void) +enum BattleEnvironments BattleSetup_GetEnvironmentId(void) { u16 tileBehavior; s16 x, y; - if (I_FISHING_ENVIRONMENT >= GEN_4 && gIsFishingEncounter) + if (ShouldUseFishingEnvironmentInBattle()) GetXYCoordsOneStepInFrontOfPlayer(&x, &y); else PlayerGetDestCoords(&x, &y); @@ -593,13 +682,17 @@ u8 BattleSetup_GetEnvironmentId(void) { if (MetatileBehavior_GetBridgeType(tileBehavior)) return BATTLE_ENVIRONMENT_POND; + if (MetatileBehavior_IsBridge(tileBehavior) == TRUE) return BATTLE_ENVIRONMENT_WATER; } + if (GetSavedWeather() == WEATHER_SANDSTORM) + return BATTLE_ENVIRONMENT_SAND; + return BATTLE_ENVIRONMENT_PLAIN; } -static u8 GetBattleTransitionTypeByMap(void) +static enum TransitionType GetBattleTransitionTypeByMap(void) { u16 tileBehavior; s16 x, y; @@ -662,7 +755,7 @@ static u8 GetSumOfEnemyPartyLevel(u16 opponentId, u8 numMons) return sum; } -u8 GetWildBattleTransition(void) +enum BattleTransition GetWildBattleTransition(void) { u8 transitionType = GetBattleTransitionTypeByMap(); u8 enemyLevel = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL); @@ -670,25 +763,42 @@ u8 GetWildBattleTransition(void) if (enemyLevel < playerLevel) { - return sBattleTransitionTable_Wild[transitionType][0]; + if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) + return B_TRANSITION_BLUR; + else + return sBattleTransitionTable_Wild[transitionType][0]; } else { - return sBattleTransitionTable_Wild[transitionType][1]; + if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) + return B_TRANSITION_GRID_SQUARES; + else + return sBattleTransitionTable_Wild[transitionType][1]; } } -u8 GetTrainerBattleTransition(void) +enum BattleTransition GetTrainerBattleTransition(void) { u8 minPartyCount = 1; u8 transitionType; u8 enemyLevel; u8 playerLevel; u32 trainerId = SanitizeTrainerId(TRAINER_BATTLE_PARAM.opponentA); + enum TrainerClassID trainerClass = GetTrainerClassFromId(TRAINER_BATTLE_PARAM.opponentA); if (DoesTrainerHaveMugshot(trainerId)) return B_TRANSITION_MUGSHOT; + if (trainerClass == TRAINER_CLASS_TEAM_MAGMA + || trainerClass == TRAINER_CLASS_MAGMA_LEADER + || trainerClass == TRAINER_CLASS_MAGMA_ADMIN) + return B_TRANSITION_MAGMA; + + if (trainerClass == TRAINER_CLASS_TEAM_AQUA + || trainerClass == TRAINER_CLASS_AQUA_LEADER + || trainerClass == TRAINER_CLASS_AQUA_ADMIN) + return B_TRANSITION_AQUA; + switch (GetTrainerBattleType(trainerId)) { case TRAINER_BATTLE_TYPE_SINGLES: @@ -709,21 +819,56 @@ u8 GetTrainerBattleTransition(void) return sBattleTransitionTable_Trainer[transitionType][1]; } -// TODO: transitions -u8 GetSpecialBattleTransition(s32 id) -{ - return B_TRANSITION_POKEBALLS_TRAIL; -} - -u8 BattleSetup_GetBattleTowerBattleTransition(void) +#define RANDOM_TRANSITION(table) (table[Random() % ARRAY_COUNT(table)]) +enum BattleTransition GetSpecialBattleTransition(enum BattleTransitionGroup id) { + u16 var; u8 enemyLevel = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL); u8 playerLevel = GetSumOfPlayerPartyLevel(1); if (enemyLevel < playerLevel) - return B_TRANSITION_POKEBALLS_TRAIL; + { + switch (id) + { + case B_TRANSITION_GROUP_TRAINER_TOWER: + case B_TRANSITION_GROUP_SECRET_BASE: + case B_TRANSITION_GROUP_E_READER: + return B_TRANSITION_POKEBALLS_TRAIL; + case B_TRANSITION_GROUP_B_PYRAMID: + return RANDOM_TRANSITION(sBattleTransitionTable_BattlePyramid); + case B_TRANSITION_GROUP_B_DOME: + return RANDOM_TRANSITION(sBattleTransitionTable_BattleDome); + default: + break; + } + + if (VarGet(VAR_FRONTIER_BATTLE_MODE) != FRONTIER_MODE_LINK_MULTIS) + return RANDOM_TRANSITION(sBattleTransitionTable_BattleFrontier); + } else - return B_TRANSITION_BIG_POKEBALL; + { + switch (id) + { + case B_TRANSITION_GROUP_TRAINER_TOWER: + case B_TRANSITION_GROUP_SECRET_BASE: + case B_TRANSITION_GROUP_E_READER: + return B_TRANSITION_BIG_POKEBALL; + case B_TRANSITION_GROUP_B_PYRAMID: + return RANDOM_TRANSITION(sBattleTransitionTable_BattlePyramid); + case B_TRANSITION_GROUP_B_DOME: + return RANDOM_TRANSITION(sBattleTransitionTable_BattleDome); + default: + break; + } + + if (VarGet(VAR_FRONTIER_BATTLE_MODE) != FRONTIER_MODE_LINK_MULTIS) + return RANDOM_TRANSITION(sBattleTransitionTable_BattleFrontier); + } + + var = gSaveBlock2Ptr->frontier.trainerIds[gSaveBlock2Ptr->frontier.curChallengeBattleNum * 2 + 0] + + gSaveBlock2Ptr->frontier.trainerIds[gSaveBlock2Ptr->frontier.curChallengeBattleNum * 2 + 1]; + + return sBattleTransitionTable_BattleFrontier[var % ARRAY_COUNT(sBattleTransitionTable_BattleFrontier)]; } static u16 GetTrainerAFlag(void) @@ -812,7 +957,7 @@ void TrainerBattleLoadArgsSecondTrainer(const u8 *data) void SetMapVarsToTrainerA(void) { - if (TRAINER_BATTLE_PARAM.objEventLocalIdA != 0) + if (TRAINER_BATTLE_PARAM.objEventLocalIdA != LOCALID_NONE) { gSpecialVar_LastTalked = TRAINER_BATTLE_PARAM.objEventLocalIdA; gSelectedObjectEvent = GetObjectEventIdByLocalIdAndMap(TRAINER_BATTLE_PARAM.objEventLocalIdA, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); @@ -866,8 +1011,10 @@ const u8 *BattleSetup_ConfigureTrainerBattle(const u8 *data) case TRAINER_BATTLE_EARLY_RIVAL: SetMapVarsToTrainerA(); return EventScript_DoNoIntroTrainerBattle; - case TRAINER_BATTLE_SET_TRAINERS_FOR_MULTI_BATTLE: - return sTrainerBattleEndScript; + case TRAINER_BATTLE_TWO_TRAINERS_NO_INTRO: + gNoOfApproachingTrainers = 2; // set TWO_OPPONENTS gBattleTypeFlags + gApproachingTrainerId = 1; // prevent trainer approach + return EventScript_DoNoIntroTrainerBattle; default: if (gApproachingTrainerId == 0) { @@ -883,28 +1030,17 @@ const u8* BattleSetup_ConfigureFacilityTrainerBattle(u8 facility, const u8* scri switch (facility) { - // case FACILITY_BATTLE_PYRAMID: - // if (gApproachingTrainerId == 0) - // { - // SetMapVarsToTrainerA(); - // TRAINER_BATTLE_PARAM.opponentA = LocalIdToPyramidTrainerId(gSpecialVar_LastTalked); - // } - // else - // { - // TRAINER_BATTLE_PARAM.opponentB = LocalIdToPyramidTrainerId(gSpecialVar_LastTalked); - // } - // return EventScript_TryDoNormalTrainerBattle; - // case FACILITY_BATTLE_TRAINER_HILL: - // if (gApproachingTrainerId == 0) - // { - // SetMapVarsToTrainerA(); - // TRAINER_BATTLE_PARAM.opponentA = LocalIdToHillTrainerId(gSpecialVar_LastTalked); - // } - // else - // { - // TRAINER_BATTLE_PARAM.opponentB = LocalIdToHillTrainerId(gSpecialVar_LastTalked); - // } - // return EventScript_TryDoNormalTrainerBattle; + case FACILITY_BATTLE_PYRAMID: + if (gApproachingTrainerId == 0) + { + SetMapVarsToTrainerA(); + TRAINER_BATTLE_PARAM.opponentA = LocalIdToPyramidTrainerId(gSpecialVar_LastTalked); + } + else + { + TRAINER_BATTLE_PARAM.opponentB = LocalIdToPyramidTrainerId(gSpecialVar_LastTalked); + } + return EventScript_TryDoNormalTrainerBattle; default: return sTrainerBattleEndScript; } @@ -950,7 +1086,7 @@ bool32 GetTrainerFlagFromScriptPointer(const u8 *data) // Set trainer's movement type so they stop and remain facing that direction // Note: Only for trainers who are spoken to directly // For trainers who spot the player this is handled by PlayerFaceApproachingTrainer -void SetUpTrainerMovement(void) +void SetTrainerFacingDirection(void) { struct ObjectEvent *objectEvent = &gObjectEvents[gSelectedObjectEvent]; SetTrainerMovementType(objectEvent, GetTrainerFacingDirectionMovementType(objectEvent->facingDirection)); @@ -966,9 +1102,12 @@ u16 GetRivalBattleFlags(void) return TRAINER_BATTLE_PARAM.rivalBattleFlags; } -u16 Script_HasTrainerBeenFought(void) +bool8 GetTrainerFlag(void) { - return FlagGet(GetTrainerAFlag()); + if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) + return GetBattlePyramidTrainerFlag(gSelectedObjectEvent); + else + return FlagGet(GetTrainerAFlag()); } static void SetBattledTrainersFlags(void) @@ -1023,24 +1162,69 @@ void BattleSetup_StartTrainerBattle(void) if (GetTrainerBattleMode() == TRAINER_BATTLE_EARLY_RIVAL && GetRivalBattleFlags() & RIVAL_BATTLE_TUTORIAL) gBattleTypeFlags |= BATTLE_TYPE_FIRST_BATTLE; + if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) + { + VarSet(VAR_TEMP_PLAYING_PYRAMID_MUSIC, 0); + gBattleTypeFlags |= BATTLE_TYPE_PYRAMID; + + if (gNoOfApproachingTrainers == 2) + { + FillFrontierTrainersParties(1); + ZeroMonData(&gEnemyParty[1]); + ZeroMonData(&gEnemyParty[2]); + ZeroMonData(&gEnemyParty[4]); + ZeroMonData(&gEnemyParty[5]); + } + else + { + FillFrontierTrainerParty(1); + ZeroMonData(&gEnemyParty[1]); + ZeroMonData(&gEnemyParty[2]); + } + + MarkApproachingPyramidTrainersAsBattled(); + } + else if (GetTrainerBattleType(TRAINER_BATTLE_PARAM.opponentA) == TRAINER_BATTLE_TYPE_DOUBLES) + { + gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; + } + sNoOfPossibleTrainerRetScripts = gNoOfApproachingTrainers; gNoOfApproachingTrainers = 0; sShouldCheckTrainerBScript = FALSE; gWhichTrainerToFaceAfterBattle = 0; gMain.savedCallback = CB2_EndTrainerBattle; - DoTrainerBattle(); + if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) + DoBattlePyramidTrainerHillBattle(); + else + DoTrainerBattle(); ScriptContext_Stop(); } +static void CB2_EndDebugBattle(void) +{ + if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) + { + for (u32 i = 0; i < 3; i++) + { + u16 monId = gSaveBlock2Ptr->frontier.selectedPartyMons[i] - 1; + if (monId < PARTY_SIZE) + SavePlayerPartyMon(gSaveBlock2Ptr->frontier.selectedPartyMons[i] - 1, &gPlayerParty[i]); + } + LoadPlayerParty(); + } + SetMainCallback2(CB2_EndTrainerBattle); +} + void BattleSetup_StartTrainerBattle_Debug(void) { sNoOfPossibleTrainerRetScripts = gNoOfApproachingTrainers; gNoOfApproachingTrainers = 0; sShouldCheckTrainerBScript = FALSE; gWhichTrainerToFaceAfterBattle = 0; - gMain.savedCallback = CB2_EndTrainerBattle; + gMain.savedCallback = CB2_EndDebugBattle; CreateBattleStartTask_Debug(GetWildBattleTransition(), 0); @@ -1055,7 +1239,7 @@ static void SaveChangesToPlayerParty(void) { if ((participatedPokemon >> i & 1) == 1) { - gSaveBlock1Ptr->playerParty[i] = gPlayerParty[j]; + SavePlayerPartyMon(i, &gPlayerParty[j]); j++; } } @@ -1098,42 +1282,38 @@ static void CB2_EndTrainerBattle(void) SetMainCallback2(CB2_WhiteOut); return; } - SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); - SetBattledTrainerFlag(); - QuestLogEvents_HandleEndTrainerBattle(); } else { gSpecialVar_Result = FALSE; - DowngradeBadPoison(); - SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); - SetBattledTrainerFlag(); - QuestLogEvents_HandleEndTrainerBattle(); } - + DowngradeBadPoison(); + SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); + SetBattledTrainerFlag(); + QuestLogEvents_HandleEndTrainerBattle(); + } + else if (TRAINER_BATTLE_PARAM.opponentA == TRAINER_SECRET_BASE) + { + DowngradeBadPoison(); + SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); + } + else if (IsPlayerDefeated(gBattleOutcome) == TRUE) + { + if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE || (!NoAliveMonsForPlayer()) || FlagGet(B_FLAG_NO_WHITEOUT)) + SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); + else + SetMainCallback2(CB2_WhiteOut); + } + else if (DidPlayerForfeitNormalTrainerBattle()) + { + SetMainCallback2(CB2_WhiteOut); } else { - if (TRAINER_BATTLE_PARAM.opponentA == TRAINER_SECRET_BASE) + SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); + DowngradeBadPoison(); + if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE) { - DowngradeBadPoison(); - SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); - } - else if (IsPlayerDefeated(gBattleOutcome) == TRUE) - { - // if (InBattlePyramid() || InTrainerHillChallenge() || (!NoAliveMonsForPlayer()) || FlagGet(B_FLAG_NO_WHITEOUT)) - // SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); - // else - SetMainCallback2(CB2_WhiteOut); - } - // else if (DidPlayerForfeitNormalTrainerBattle()) - // { - // SetMainCallback2(CB2_WhiteOut); - // } - else - { - SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic); - DowngradeBadPoison(); SetBattledTrainersFlags(); QuestLogEvents_HandleEndTrainerBattle(); } @@ -1173,7 +1353,19 @@ void BattleSetup_StartRematchBattle(void) void ShowTrainerIntroSpeech(void) { - ShowFieldMessage(GetIntroSpeechOfApproachingTrainer()); + if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) + { + if (gNoOfApproachingTrainers == 0 || gNoOfApproachingTrainers == 1) + CopyPyramidTrainerSpeechBefore(LocalIdToPyramidTrainerId(gSpecialVar_LastTalked)); + else + CopyPyramidTrainerSpeechBefore(LocalIdToPyramidTrainerId(gObjectEvents[gApproachingTrainers[gApproachingTrainerId].objectEventId].localId)); + + ShowFieldMessageFromBuffer(); + } + else + { + ShowFieldMessage(GetIntroSpeechOfApproachingTrainer()); + } } const u8 *BattleSetup_GetScriptAddrAfterBattle(void) @@ -1204,7 +1396,7 @@ const u8 *BattleSetup_GetTrainerPostBattleScript(void) } } - return EventScript_TestSignpostMsg; + return EventScript_TryGetTrainerScript; } void ShowTrainerCantBattleSpeech(void) @@ -1236,8 +1428,8 @@ void PlayTrainerEncounterMusic(void) trainerId = TRAINER_BATTLE_PARAM.opponentB; if (!QL_IS_PLAYBACK_STATE - && GetTrainerBattleMode() != TRAINER_BATTLE_CONTINUE_SCRIPT_NO_MUSIC - && GetTrainerBattleMode() != TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE_NO_MUSIC) + && TRAINER_BATTLE_PARAM.mode != TRAINER_BATTLE_CONTINUE_SCRIPT_NO_MUSIC + && TRAINER_BATTLE_PARAM.mode != TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE_NO_MUSIC) { switch (GetTrainerEncounterMusicId(trainerId)) { @@ -1305,6 +1497,21 @@ static const u8 *GetTrainerCantBattleSpeech(void) return ReturnEmptyStringIfNull(TRAINER_BATTLE_PARAM.cannotBattleText); } +void ShouldTryGetTrainerScript(void) +{ + if (sNoOfPossibleTrainerRetScripts > 1) + { + sNoOfPossibleTrainerRetScripts = 0; + sShouldCheckTrainerBScript = TRUE; + gSpecialVar_Result = TRUE; + } + else + { + sShouldCheckTrainerBScript = FALSE; + gSpecialVar_Result = FALSE; + } +} + u16 CountMaxPossibleRematch(u16 trainerId) { for (u32 i = 1; i < MAX_REMATCH_PARTIES; i++) @@ -1341,3 +1548,4 @@ void SetMultiTrainerBattle(struct ScriptContext *ctx) TRAINER_BATTLE_PARAM.defeatTextB = (u8*)ScriptReadWord(ctx); gPartnerTrainerId = TRAINER_PARTNER(ScriptReadHalfword(ctx)); }; + diff --git a/src/battle_special.c b/src/battle_special.c index 68fc3f6b3..25abab114 100644 --- a/src/battle_special.c +++ b/src/battle_special.c @@ -60,7 +60,7 @@ void DoSpecialTrainerBattle(void) CreateTask(Task_StartBattleAfterTransition, 1); PlayMapChosenOrBattleBGM(0); - BattleTransition_StartOnField(BattleSetup_GetBattleTowerBattleTransition()); + BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_E_READER)); #endif //FREE_BATTLE_TOWER_E_READER break; } diff --git a/src/battle_switch_in.c b/src/battle_switch_in.c index 681007b55..e38d60401 100644 --- a/src/battle_switch_in.c +++ b/src/battle_switch_in.c @@ -10,12 +10,12 @@ #include "constants/moves.h" static bool32 FirstEventBlockEvents(struct BattleCalcValues *calcValues); -static bool32 TryHazardsOnSwitchIn(u32 battler, enum Ability ability, enum HoldEffect holdEffect, enum Hazards hazardType); +static bool32 TryHazardsOnSwitchIn(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect, enum Hazards hazardType); static bool32 SecondEventBlockEvents(struct BattleCalcValues *calcValues); bool32 DoSwitchInEvents(void) { - u32 battler; + enum BattlerId battler; struct BattleCalcValues calcValues = {0}; for (battler = 0; battler < gBattlersCount; battler++) @@ -29,7 +29,7 @@ bool32 DoSwitchInEvents(void) switch (gBattleStruct->eventState.switchIn) { case SWITCH_IN_EVENTS_ORDER_BY_SPEED: - for (u32 i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) gBattlersBySpeed[i] = i; SortBattlersBySpeed(gBattlersBySpeed, FALSE); gBattleStruct->battlersSorted = TRUE; @@ -92,6 +92,16 @@ bool32 DoSwitchInEvents(void) gBattleStruct->switchInBattlerCounter = 0; gBattleStruct->eventState.switchIn++; break; + case SWITCH_IN_EVENTS_FORM_CHANGE: + while (gBattleStruct->switchInBattlerCounter < gBattlersCount) + { + battler = gBattlersBySpeed[gBattleStruct->switchInBattlerCounter++]; + if (AbilityBattleEffects(ABILITYEFFECT_SWITCH_IN_FORM_CHANGE, battler, calcValues.abilities[battler], 0, gBattleStruct->battlerState[battler].switchIn)) + return TRUE; + } + gBattleStruct->switchInBattlerCounter = 0; + gBattleStruct->eventState.switchIn++; + break; case SWITCH_IN_EVENTS_SECOND_BLOCK: while (gBattleStruct->switchInBattlerCounter < gBattlersCount) { @@ -120,7 +130,7 @@ bool32 DoSwitchInEvents(void) case SWITCH_IN_EVENTS_WHITE_HERB: while (gBattleStruct->switchInBattlerCounter < gBattlersCount) { - u32 battler = gBattlersBySpeed[gBattleStruct->switchInBattlerCounter++]; + enum BattlerId battler = gBattlersBySpeed[gBattleStruct->switchInBattlerCounter++]; if (ItemBattleEffects(battler, 0, calcValues.holdEffects[battler], IsWhiteHerbActivation)) return TRUE; } @@ -130,7 +140,7 @@ bool32 DoSwitchInEvents(void) case SWITCH_IN_EVENTS_OPPORTUNIST: while (gBattleStruct->switchInBattlerCounter < gBattlersCount) { - u32 battler = gBattlersBySpeed[gBattleStruct->switchInBattlerCounter++]; + enum BattlerId battler = gBattlersBySpeed[gBattleStruct->switchInBattlerCounter++]; if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, calcValues.abilities[battler], 0, TRUE)) return TRUE; } @@ -140,7 +150,7 @@ bool32 DoSwitchInEvents(void) case SWITCH_IN_EVENTS_MIRROR_HERB: while (gBattleStruct->switchInBattlerCounter < gBattlersCount) { - u32 battler = gBattlersBySpeed[gBattleStruct->switchInBattlerCounter++]; + enum BattlerId battler = gBattlersBySpeed[gBattleStruct->switchInBattlerCounter++]; if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsMirrorHerbActivation)) return TRUE; } @@ -171,7 +181,7 @@ bool32 DoSwitchInEvents(void) return FALSE; } -static bool32 CanBattlerBeHealed(u32 battler) +static bool32 CanBattlerBeHealed(enum BattlerId battler) { if (GetConfig(CONFIG_HEALING_WISH_SWITCH) < GEN_8) return TRUE; @@ -194,7 +204,7 @@ static bool32 CanBattlerBeHealed(u32 battler) static bool32 FirstEventBlockEvents(struct BattleCalcValues *calcValues) { bool32 effect = FALSE; - u32 battler = calcValues->battlerAtk; + enum BattlerId battler = calcValues->battlerAtk; switch (gBattleStruct->eventState.battlerSwitchIn) { @@ -282,14 +292,14 @@ static bool32 FirstEventBlockEvents(struct BattleCalcValues *calcValues) return effect; } -static void SetDmgHazardsBattlescript(u8 battler, u8 multistringId) +static void SetDmgHazardsBattlescript(enum BattlerId battler, u8 multistringId) { gBattleScripting.battler = battler; gBattleCommunication[MULTISTRING_CHOOSER] = multistringId; BattleScriptCall(BattleScript_DmgHazardsOnBattler); } -static bool32 TryHazardsOnSwitchIn(u32 battler, enum Ability ability, enum HoldEffect holdEffect, enum Hazards hazardType) +static bool32 TryHazardsOnSwitchIn(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect, enum Hazards hazardType) { bool32 effect = FALSE; enum BattleSide side = GetBattlerSide(battler); @@ -389,7 +399,7 @@ static bool32 TryHazardsOnSwitchIn(u32 battler, enum Ability ability, enum HoldE static bool32 SecondEventBlockEvents(struct BattleCalcValues *calcValues) { bool32 effect = FALSE; - u32 battler = calcValues->battlerAtk; + enum BattlerId battler = calcValues->battlerAtk; switch (gBattleStruct->eventState.battlerSwitchIn) { diff --git a/src/battle_terastal.c b/src/battle_terastal.c index fa0612c0d..0a63cd687 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -17,7 +17,7 @@ #include "constants/rgb.h" // Sets flags and variables upon a battler's Terastallization. -void ActivateTera(u32 battler) +void ActivateTera(enum BattlerId battler) { // Set appropriate flags. SetActiveGimmick(battler, GIMMICK_TERA); @@ -34,7 +34,7 @@ void ActivateTera(u32 battler) // Execute battle script. PREPARE_TYPE_BUFFER(gBattleTextBuff1, GetBattlerTeraType(battler)); - if (TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_TERASTALLIZATION)) + if (TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_TERASTALLIZATION, GetBattlerAbility(gBattlerAttacker))) BattleScriptPushCursorAndCallback(BattleScript_TeraFormChange); else if (gBattleStruct->illusion[gBattlerAttacker].state == ILLUSION_ON && DoesSpeciesHaveFormChangeMethod(GetIllusionMonSpecies(gBattlerAttacker), FORM_CHANGE_BATTLE_TERASTALLIZATION)) @@ -44,7 +44,7 @@ void ActivateTera(u32 battler) } // Applies palette blend and enables UI indicator after animation has played -void ApplyBattlerVisualsForTeraAnim(u32 battler) +void ApplyBattlerVisualsForTeraAnim(enum BattlerId battler) { struct Pokemon *party = GetBattlerParty(battler); u32 index = gBattlerPartyIndexes[battler]; @@ -59,7 +59,7 @@ void ApplyBattlerVisualsForTeraAnim(u32 battler) } // Returns whether a battler can Terastallize. -bool32 CanTerastallize(u32 battler) +bool32 CanTerastallize(enum BattlerId battler) { enum HoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); @@ -108,20 +108,20 @@ bool32 CanTerastallize(u32 battler) } // Returns a battler's Tera type. -enum Type GetBattlerTeraType(u32 battler) +enum Type GetBattlerTeraType(enum BattlerId battler) { return GetMonData(GetBattlerMon(battler), MON_DATA_TERA_TYPE); } // Uses up a type's Stellar boost. -void ExpendTypeStellarBoost(u32 battler, enum Type type) +void ExpendTypeStellarBoost(enum BattlerId battler, enum Type type) { if (type < 32 && gBattleMons[battler].species != SPECIES_TERAPAGOS_STELLAR) // avoid OOB access gBattleStruct->stellarBoostFlags[GetBattlerSide(battler)] |= 1u << type; } // Checks whether a type's Stellar boost has been expended. -bool32 IsTypeStellarBoosted(u32 battler, enum Type type) +bool32 IsTypeStellarBoosted(enum BattlerId battler, enum Type type) { if (type < 32) // avoid OOB access return !(gBattleStruct->stellarBoostFlags[GetBattlerSide(battler)] & (1u << type)); diff --git a/src/battle_transition.c b/src/battle_transition.c index 00decd2ae..cee29c595 100644 --- a/src/battle_transition.c +++ b/src/battle_transition.c @@ -578,7 +578,7 @@ static const struct SpritePalette sSpritePalette_UnusedTrainer = static const u16 sBigPokeball_Tilemap[] = INCBIN_U16("graphics/battle_transitions/big_pokeball_tilemap.bin"); static const u16 sMugshotsTilemap[] = INCBIN_U16("graphics/battle_transitions/vsbar_tilemap.bin"); -void BattleTransition_StartOnField(u8 transitionId) +void BattleTransition_StartOnField(enum BattleTransition transitionId) { sTransitionData = AllocZeroed(sizeof(*sTransitionData)); gMain.callback2 = CB2_OverworldBasic; diff --git a/src/battle_util.c b/src/battle_util.c index d3a7c2994..02313e0b0 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -1,9 +1,9 @@ #include "global.h" #include "battle.h" #include "battle_anim.h" -// #include "battle_arena.h" +#include "battle_arena.h" #include "battle_environment.h" -// #include "battle_pyramid.h" +#include "battle_pyramid.h" #include "battle_util.h" #include "battle_controllers.h" #include "battle_interface.h" @@ -53,31 +53,19 @@ #include "constants/weather.h" #include "constants/pokemon.h" -static bool32 TryRemoveScreens(u32 battler); -static bool32 IsUnnerveAbilityOnOpposingSide(u32 battler); +static bool32 TryRemoveScreens(enum BattlerId battler); +static bool32 IsUnnerveAbilityOnOpposingSide(enum BattlerId battler); static u32 GetFlingPowerFromItemId(enum Item itemId); -static void SetRandomMultiHitCounter(); -static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, enum Ability abilityDef, bool32 abilityAffected, const u8 *battleScript, enum ResultOption option); -static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum ResultOption option); -static bool32 IsOpposingSideEmpty(u32 battler); -static void ResetParadoxWeatherStat(u32 battler); -static void ResetParadoxTerrainStat(u32 battler); -static bool32 CanBattlerFormChange(u32 battler, enum FormChanges method); -static bool32 CanBattlerBounceBackMove(struct BattleContext *ctx); +static bool32 IsNonVolatileStatusBlocked(enum BattlerId battlerDef, enum Ability abilityDef, bool32 abilityAffected, const u8 *battleScript, enum ResultOption option); +static bool32 CanSleepDueToSleepClause(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum ResultOption option); +static bool32 IsOpposingSideEmpty(enum BattlerId battler); +static void ResetParadoxWeatherStat(enum BattlerId battler); +static void ResetParadoxTerrainStat(enum BattlerId battler); +static bool32 CanBattlerFormChange(enum BattlerId battler, enum FormChanges method); static bool32 IsPowderMoveBlocked(struct BattleContext *ctx); -const u8 *AbsorbedByDrainHpAbility(u32 battlerDef); -const u8 *AbsorbedByStatIncreaseAbility(u32 battlerDef, enum Ability abilityDef, enum Stat statId, u32 statAmount); -const u8 *AbsorbedByFlashFire(u32 battlerDef); -static bool32 TryMagicBounce(struct BattleContext *ctx); -static bool32 TryMagicCoat(struct BattleContext *ctx); - -// Submoves -static enum Move GetMirrorMoveMove(void); -static enum Move GetMetronomeMove(void); -static enum Move GetAssistMove(void); -static enum Move GetSleepTalkMove(void); -static enum Move GetCopycatMove(void); -static enum Move GetMeFirstMove(void); +const u8 *AbsorbedByDrainHpAbility(enum BattlerId battlerDef); +const u8 *AbsorbedByStatIncreaseAbility(enum BattlerId battlerDef, enum Ability abilityDef, enum Stat statId, u32 statAmount); +const u8 *AbsorbedByFlashFire(enum BattlerId battlerDef); ARM_FUNC NOINLINE static uq4_12_t PercentToUQ4_12(u32 percent); ARM_FUNC NOINLINE static uq4_12_t PercentToUQ4_12_Floored(u32 percent); @@ -238,7 +226,7 @@ bool32 EndOrContinueWeather(void) if (gBattleStruct->weatherDuration > 0 && --gBattleStruct->weatherDuration == 0) { gBattleWeather = B_WEATHER_NONE; - for (u32 battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { gBattleMons[battler].volatiles.weatherAbilityDone = FALSE; ResetParadoxWeatherStat(battler); @@ -290,7 +278,7 @@ static s32 CalcBeatUpDamage(struct BattleContext *ctx) return dmg; } -static enum DamageCategory GetReflectDamageMoveDamageCategory(u32 battler, enum Move move) +enum DamageCategory GetReflectDamageMoveDamageCategory(enum BattlerId battler, enum Move move) { u32 damageCategories = GetMoveReflectDamage_DamageCategories(move); @@ -306,7 +294,7 @@ static enum DamageCategory GetReflectDamageMoveDamageCategory(u32 battler, enum return DAMAGE_CATEGORY_PHYSICAL; } -static bool32 ShouldTeraShellDistortTypeMatchups(enum Move move, u32 battlerDef, enum Ability abilityDef) +static bool32 ShouldTeraShellDistortTypeMatchups(enum Move move, enum BattlerId battlerDef, enum Ability abilityDef) { if (!gSpecialStatuses[battlerDef].distortedTypeMatchups && gBattleMons[battlerDef].species == SPECIES_TERAPAGOS_TERASTAL @@ -318,7 +306,7 @@ static bool32 ShouldTeraShellDistortTypeMatchups(enum Move move, u32 battlerDef, return FALSE; } -bool32 IsUnnerveBlocked(u32 battler, enum Item itemId) +bool32 IsUnnerveBlocked(enum BattlerId battler, enum Item itemId) { if (GetItemPocket(itemId) != POCKET_BERRIES) return FALSE; @@ -332,9 +320,9 @@ bool32 IsUnnerveBlocked(u32 battler, enum Item itemId) return FALSE; } -static bool32 IsUnnerveAbilityOnOpposingSide(u32 battler) +static bool32 IsUnnerveAbilityOnOpposingSide(enum BattlerId battler) { - for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) + for (enum BattlerId battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) { if (battler == battlerDef || IsBattlerAlly(battler, battlerDef)) continue; @@ -357,7 +345,7 @@ static bool32 IsUnnerveAbilityOnOpposingSide(u32 battler) return FALSE; } -static inline bool32 IsDragonDartsSecondHit(u32 battlerAtk, enum Move move) +static inline bool32 IsDragonDartsSecondHit(enum BattlerId battlerAtk, enum Move move) { if (GetBattlerMoveTargetType(battlerAtk, move) != TARGET_SMART) return FALSE; @@ -368,7 +356,7 @@ static inline bool32 IsDragonDartsSecondHit(u32 battlerAtk, enum Move move) return FALSE; } -bool32 IsAffectedByFollowMe(u32 battlerAtk, enum BattleSide defSide, enum Move move) +bool32 IsAffectedByFollowMe(enum BattlerId battlerAtk, enum BattleSide defSide, enum Move move) { enum Ability ability = GetBattlerAbility(battlerAtk); enum BattleMoveEffects effect = GetMoveEffect(move); @@ -429,7 +417,7 @@ static bool32 HandleMoveTargetRedirection(void) { // Find first battler that redirects the move (in turn order) enum Ability abilityAtk = GetBattlerAbility(gBattlerAttacker); - u32 battler; + enum BattlerId battler; for (battler = 0; battler < gBattlersCount; battler++) { ability = GetBattlerAbility(battler); @@ -523,7 +511,8 @@ static bool32 WasOriginalTargetAlly(enum MoveTarget target) // Functions void HandleAction_UseMove(void) { - u32 i, moveTarget; + u32 i; + enum MoveTarget moveTarget; gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]; if (gAbsentBattlerFlags & 1u << gBattlerAttacker @@ -610,27 +599,27 @@ void HandleAction_UseMove(void) DetermineTarget(moveTarget, TRUE); - // if (gBattleTypeFlags & BATTLE_TYPE_PALACE && gProtectStructs[gBattlerAttacker].palaceUnableToUseMove) - // { - // // Battle Palace, select battle script for failure to use move - // if (!IsBattlerAlive(gBattlerAttacker)) - // { - // gCurrentActionFuncId = B_ACTION_FINISHED; - // return; - // } - // else if (gPalaceSelectionBattleScripts[gBattlerAttacker] != NULL) - // { - // gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_INCAPABLE_OF_POWER; - // gBattlescriptCurrInstr = gPalaceSelectionBattleScripts[gBattlerAttacker]; - // gPalaceSelectionBattleScripts[gBattlerAttacker] = NULL; - // } - // else - // { - // gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_INCAPABLE_OF_POWER; - // gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround; - // } - // } - if (IsBattlerAlly(gBattlerAttacker, gBattlerTarget) && !IsBattlerAlive(gBattlerTarget)) + if (gBattleTypeFlags & BATTLE_TYPE_PALACE && gProtectStructs[gBattlerAttacker].palaceUnableToUseMove) + { + // Battle Palace, select battle script for failure to use move + if (!IsBattlerAlive(gBattlerAttacker)) + { + gCurrentActionFuncId = B_ACTION_FINISHED; + return; + } + else if (gPalaceSelectionBattleScripts[gBattlerAttacker] != NULL) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_INCAPABLE_OF_POWER; + gBattlescriptCurrInstr = gPalaceSelectionBattleScripts[gBattlerAttacker]; + gPalaceSelectionBattleScripts[gBattlerAttacker] = NULL; + } + else + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_INCAPABLE_OF_POWER; + gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround; + } + } + else if (IsBattlerAlly(gBattlerAttacker, gBattlerTarget) && !IsBattlerAlive(gBattlerTarget)) { gBattlescriptCurrInstr = BattleScript_FailedFromAtkCanceler; } @@ -643,8 +632,8 @@ void HandleAction_UseMove(void) gBattlescriptCurrInstr = GetMoveBattleScript(gCurrentMove); } - // if (gBattleTypeFlags & BATTLE_TYPE_ARENA) - // BattleArena_AddMindPoints(gBattlerAttacker); + if (gBattleTypeFlags & BATTLE_TYPE_ARENA) + BattleArena_AddMindPoints(gBattlerAttacker); for (i = 0; i < MAX_BATTLERS_COUNT; i++) gBattleStruct->battlerState[i].wasAboveHalfHp = gBattleMons[i].hp > gBattleMons[i].maxHP / 2; @@ -679,7 +668,7 @@ void HandleAction_Switch(void) if (gBattleResults.playerSwitchesCounter < 255) gBattleResults.playerSwitchesCounter++; - TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_SWITCH); + TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_SWITCH_OUT, GetBattlerAbility(gBattlerAttacker)); } void HandleAction_UseItem(void) @@ -699,11 +688,11 @@ void HandleAction_UseItem(void) gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT; } -bool32 TryRunFromBattle(u32 battler) +bool32 TryRunFromBattle(enum BattlerId battler) { bool32 effect = FALSE; u8 holdEffect; - // u8 pyramidMultiplier; + u8 pyramidMultiplier; u8 speedVar; // If this flag is set, running will never be successful under any circumstances. @@ -729,24 +718,24 @@ bool32 TryRunFromBattle(u32 battler) } else if (GetBattlerAbility(battler) == ABILITY_RUN_AWAY) { - // if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) - // { - // gBattleStruct->runTries++; - // pyramidMultiplier = GetPyramidRunMultiplier(); - // speedVar = (gBattleMons[battler].speed * pyramidMultiplier) / (gBattleMons[BATTLE_OPPOSITE(battler)].speed) + (gBattleStruct->runTries * 30); - // if (speedVar > (Random() & 0xFF)) - // { - // gLastUsedAbility = ABILITY_RUN_AWAY; - // gProtectStructs[battler].fleeType = FLEE_ABILITY; - // effect++; - // } - // } - // else - // { + if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) + { + gBattleStruct->runTries++; + pyramidMultiplier = GetPyramidRunMultiplier(); + speedVar = (gBattleMons[battler].speed * pyramidMultiplier) / (gBattleMons[BATTLE_OPPOSITE(battler)].speed) + (gBattleStruct->runTries * 30); + if (speedVar > (Random() & 0xFF)) + { + gLastUsedAbility = ABILITY_RUN_AWAY; + gProtectStructs[battler].fleeType = FLEE_ABILITY; + effect++; + } + } + else + { gLastUsedAbility = ABILITY_RUN_AWAY; gProtectStructs[battler].fleeType = FLEE_ABILITY; effect++; - // } + } } else if (IsGhostBattleWithoutScope()) { @@ -757,24 +746,24 @@ bool32 TryRunFromBattle(u32 battler) { effect++; } - // else if (CanPlayerForfeitNormalTrainerBattle()) - // { - // effect++; - // } + else if (CanPlayerForfeitNormalTrainerBattle()) + { + effect++; + } else { - u8 runningFromBattler = BATTLE_OPPOSITE(battler); + enum BattlerId runningFromBattler = BATTLE_OPPOSITE(battler); if (!IsBattlerAlive(runningFromBattler)) runningFromBattler |= BIT_FLANK; - // if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) - // { - // pyramidMultiplier = GetPyramidRunMultiplier(); - // speedVar = (gBattleMons[battler].speed * pyramidMultiplier) / (gBattleMons[runningFromBattler].speed) + (gBattleStruct->runTries * 30); - // if (speedVar > (Random() & 0xFF)) - // effect++; - // } - if (gBattleMons[battler].speed < gBattleMons[runningFromBattler].speed) + if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) + { + pyramidMultiplier = GetPyramidRunMultiplier(); + speedVar = (gBattleMons[battler].speed * pyramidMultiplier) / (gBattleMons[runningFromBattler].speed) + (gBattleStruct->runTries * 30); + if (speedVar > (Random() & 0xFF)) + effect++; + } + else if (gBattleMons[battler].speed < gBattleMons[runningFromBattler].speed) { speedVar = (gBattleMons[battler].speed * 128) / (gBattleMons[runningFromBattler].speed) + (gBattleStruct->runTries * 30); if (speedVar > (Random() & 0xFF)) @@ -799,14 +788,12 @@ bool32 TryRunFromBattle(u32 battler) void HandleAction_Run(void) { - s32 i; - gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]; if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) { gCurrentTurnActionNumber = gBattlersCount; - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (IsOnPlayerSide(i)) { @@ -821,7 +808,7 @@ void HandleAction_Run(void) } gBattleOutcome |= B_OUTCOME_LINK_BATTLE_RAN; - // gSaveBlock2Ptr->frontier.disableRecordBattle = TRUE; + gSaveBlock2Ptr->frontier.disableRecordBattle = TRUE; } else { @@ -1019,10 +1006,10 @@ void HandleAction_ActionFinished(void) // taken action. It's been previously increased, which we want in order to not recalculate the turn of the mon that just finished its action struct BattleCalcValues calcValues = {0}; - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { - calcValues.abilities[i] = GetBattlerAbility(i); - calcValues.holdEffects[i] = GetBattlerHoldEffect(i); + calcValues.abilities[battler] = GetBattlerAbility(battler); + calcValues.holdEffects[battler] = GetBattlerHoldEffect(battler); } for (i = gCurrentTurnActionNumber; i < gBattlersCount - 1; i++) { @@ -1069,9 +1056,9 @@ static inline uq4_12_t PercentToUQ4_12AddOne(u32 percent) return uq4_12_add(UQ_4_12(1.0), PercentToUQ4_12(percent)); } -u8 GetBattlerForBattleScript(u8 caseId) +enum BattlerId GetBattlerForBattleScript(u8 caseId) { - u8 ret = 0; + enum BattlerId ret = 0; switch (caseId) { case BS_TARGET: @@ -1125,7 +1112,7 @@ u8 GetBattlerForBattleScript(u8 caseId) static void UNUSED MarkAllBattlersForControllerExec(void) { - int i; + enum BattlerId i; if (gBattleTypeFlags & BATTLE_TYPE_LINK) { @@ -1139,7 +1126,7 @@ static void UNUSED MarkAllBattlersForControllerExec(void) } } -bool32 IsBattlerMarkedForControllerExec(u32 battler) +bool32 IsBattlerMarkedForControllerExec(enum BattlerId battler) { if (gBattleTypeFlags & BATTLE_TYPE_LINK) return IsBattleControllerMessageSynchronizedOverLink(battler); @@ -1147,7 +1134,7 @@ bool32 IsBattlerMarkedForControllerExec(u32 battler) return IsBattleControllerActiveOnLocal(battler); } -void MarkBattlerForControllerExec(u32 battler) +void MarkBattlerForControllerExec(enum BattlerId battler) { if (gBattleTypeFlags & BATTLE_TYPE_LINK) MarkBattleControllerMessageOutboundOverLink(battler); @@ -1155,7 +1142,7 @@ void MarkBattlerForControllerExec(u32 battler) MarkBattleControllerActiveOnLocal(battler); } -void MarkBattlerReceivedLinkData(u32 battler) +void MarkBattlerReceivedLinkData(enum BattlerId battler) { s32 i; @@ -1165,7 +1152,7 @@ void MarkBattlerReceivedLinkData(u32 battler) MarkBattleControllerMessageSynchronizedOverLink(battler); } -const u8 *CheckSkyDropState(u32 battler, enum SkyDropState skyDropState) +const u8 *CheckSkyDropState(enum BattlerId battler, enum SkyDropState skyDropState) { const u8 *result = NULL; @@ -1225,7 +1212,7 @@ const u8 *CheckSkyDropState(u32 battler, enum SkyDropState skyDropState) return result; } -const u8 *CancelMultiTurnMoves(u32 battler, enum SkyDropState skyDropState) +const u8 *CancelMultiTurnMoves(enum BattlerId battler, enum SkyDropState skyDropState) { const u8 *result = NULL; gBattleMons[battler].volatiles.uproarTurns = 0; @@ -1257,7 +1244,7 @@ const u8 *CancelMultiTurnMoves(u32 battler, enum SkyDropState skyDropState) // Returns TRUE if no other battler after this one in turn order will use a move -bool32 IsLastMonToMove(u32 battler) +bool32 IsLastMonToMove(enum BattlerId battler) { u32 i; u32 battlerTurnOrderNum = GetBattlerTurnOrderNum(battler); @@ -1267,7 +1254,7 @@ bool32 IsLastMonToMove(u32 battler) for (i = battlerTurnOrderNum + 1; i < gBattlersCount; i++) { - u32 otherBattler = gBattlerByTurnOrder[i]; + enum BattlerId otherBattler = gBattlerByTurnOrder[i]; if (!IsBattlerAlive(otherBattler)) continue; if (gActionsByTurnOrder[i] == B_ACTION_USE_MOVE) @@ -1276,7 +1263,7 @@ bool32 IsLastMonToMove(u32 battler) return TRUE; } -bool32 ShouldDefiantCompetitiveActivate(u32 battler, enum Ability ability) +bool32 ShouldDefiantCompetitiveActivate(enum BattlerId battler, enum Ability ability) { enum BattleSide side = GetBattlerSide(battler); if (ability != ABILITY_DEFIANT && ability != ABILITY_COMPETITIVE) @@ -1291,7 +1278,7 @@ bool32 ShouldDefiantCompetitiveActivate(u32 battler, enum Ability ability) return gSideTimers[side].stickyWebBattlerSide != side; } -void PrepareStringBattle(enum StringID stringId, u32 battler) +void PrepareStringBattle(enum StringID stringId, enum BattlerId battler) { u16 battlerAbility = GetBattlerAbility(battler); u16 targetAbility = GetBattlerAbility(gBattlerTarget); @@ -1374,7 +1361,7 @@ void ResetSentPokesToOpponentValue(void) gSentPokesToOpponent[(i & BIT_FLANK) >> 1] = bits; } -void OpponentSwitchInResetSentPokesToOpponentValue(u32 battler) +void OpponentSwitchInResetSentPokesToOpponentValue(enum BattlerId battler) { s32 i = 0; u32 bits = 0; @@ -1393,7 +1380,7 @@ void OpponentSwitchInResetSentPokesToOpponentValue(u32 battler) } } -void UpdateSentPokesToOpponentValue(u32 battler) +void UpdateSentPokesToOpponentValue(enum BattlerId battler) { if (!IsOnPlayerSide(battler)) { @@ -1409,11 +1396,13 @@ void UpdateSentPokesToOpponentValue(u32 battler) void BattleScriptPush(const u8 *bsPtr) { + assertf(gBattleResources->battleScriptsStack->size < UINT8_MAX, "attempted to push a battle script, but battleScriptsStack is full!"); gBattleResources->battleScriptsStack->ptr[gBattleResources->battleScriptsStack->size++] = bsPtr; } void BattleScriptPushCursor(void) { + assertf(gBattleResources->battleScriptsStack->size < UINT8_MAX, "attempted to push cursor, but battleScriptsStack is full!"); gBattleResources->battleScriptsStack->ptr[gBattleResources->battleScriptsStack->size++] = gBattlescriptCurrInstr; } @@ -1429,7 +1418,22 @@ void BattleScriptPop(void) gBattlescriptCurrInstr = gBattleResources->battleScriptsStack->ptr[--gBattleResources->battleScriptsStack->size]; } -static bool32 IsGravityPreventingMove(enum Move move) +void BattleScriptExecute(const u8 *BS_ptr) +{ + gBattlescriptCurrInstr = BS_ptr; + gBattleResources->battleCallbackStack->function[gBattleResources->battleCallbackStack->size++] = gBattleMainFunc; + gBattleMainFunc = RunBattleScriptCommands_PopCallbacksStack; + gCurrentActionFuncId = 0; +} + +void BattleScriptPushCursorAndCallback(const u8 *BS_ptr) +{ + BattleScriptCall(BS_ptr); + gBattleResources->battleCallbackStack->function[gBattleResources->battleCallbackStack->size++] = gBattleMainFunc; + gBattleMainFunc = RunBattleScriptCommands; +} + +bool32 IsGravityPreventingMove(enum Move move) { if (!(gFieldStatuses & STATUS_FIELD_GRAVITY)) return FALSE; @@ -1437,7 +1441,7 @@ static bool32 IsGravityPreventingMove(enum Move move) return IsMoveGravityBanned(move); } -bool32 IsHealBlockPreventingMove(u32 battler, enum Move move) +bool32 IsHealBlockPreventingMove(enum BattlerId battler, enum Move move) { if (!gBattleMons[battler].volatiles.healBlock) return FALSE; @@ -1445,7 +1449,7 @@ bool32 IsHealBlockPreventingMove(u32 battler, enum Move move) return IsHealingMove(move); } -bool32 IsBelchPreventingMove(u32 battler, enum Move move) +bool32 IsBelchPreventingMove(enum BattlerId battler, enum Move move) { if (GetMoveEffect(move) != EFFECT_BELCH) return FALSE; @@ -1456,7 +1460,7 @@ bool32 IsBelchPreventingMove(u32 battler, enum Move move) // Dynamax bypasses all selection prevention except Taunt and Assault Vest. #define DYNAMAX_BYPASS_CHECK (!IsGimmickSelected(battler, GIMMICK_DYNAMAX) && GetActiveGimmick(battler) != GIMMICK_DYNAMAX) -u32 TrySetCantSelectMoveBattleScript(u32 battler) +u32 TrySetCantSelectMoveBattleScript(enum BattlerId battler) { u32 limitations = 0; u8 moveId = gBattleResources->bufferB[battler][2] & ~RET_GIMMICK; @@ -1470,16 +1474,16 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler) { gBattleScripting.battler = battler; gCurrentMove = gBattleMons[battler].volatiles.encoredMove; - // if (gBattleTypeFlags & BATTLE_TYPE_PALACE) - // { - // gPalaceSelectionBattleScripts[battler] = BattleScript_EncoredMoveInPalace; - // gProtectStructs[battler].palaceUnableToUseMove = TRUE; - // } - // else - // { + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + { + gPalaceSelectionBattleScripts[battler] = BattleScript_EncoredMoveInPalace; + gProtectStructs[battler].palaceUnableToUseMove = TRUE; + } + else + { gSelectionBattleScripts[battler] = BattleScript_EncoredMove; limitations++; - // } + } return limitations; } @@ -1487,31 +1491,31 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler) { gBattleScripting.battler = battler; gCurrentMove = move; - // if (gBattleTypeFlags & BATTLE_TYPE_PALACE) - // { - // gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingDisabledMoveInPalace; - // gProtectStructs[battler].palaceUnableToUseMove = TRUE; - // } - // else - // { + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + { + gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingDisabledMoveInPalace; + gProtectStructs[battler].palaceUnableToUseMove = TRUE; + } + else + { gSelectionBattleScripts[battler] = BattleScript_SelectingDisabledMove; limitations++; - // } + } } if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(battler) != GIMMICK_Z_MOVE && move == gLastMoves[battler] && move != MOVE_STRUGGLE && (gBattleMons[battler].volatiles.torment == TRUE)) { CancelMultiTurnMoves(battler, SKY_DROP_IGNORE); - // if (gBattleTypeFlags & BATTLE_TYPE_PALACE) - // { - // gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingTormentedMoveInPalace; - // gProtectStructs[battler].palaceUnableToUseMove = TRUE; - // } - // else - // { + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + { + gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingTormentedMoveInPalace; + gProtectStructs[battler].palaceUnableToUseMove = TRUE; + } + else + { gSelectionBattleScripts[battler] = BattleScript_SelectingTormentedMove; limitations++; - // } + } } if (GetActiveGimmick(battler) != GIMMICK_Z_MOVE && gBattleMons[battler].volatiles.tauntTimer != 0 && IsBattleMoveStatus(move)) @@ -1520,122 +1524,122 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler) gCurrentMove = MOVE_MAX_GUARD; else gCurrentMove = move; - // if (gBattleTypeFlags & BATTLE_TYPE_PALACE) - // { - // gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveTauntInPalace; - // gProtectStructs[battler].palaceUnableToUseMove = TRUE; - // } - // else - // { + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + { + gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveTauntInPalace; + gProtectStructs[battler].palaceUnableToUseMove = TRUE; + } + else + { gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveTaunt; limitations++; - // } + } } if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(battler) != GIMMICK_Z_MOVE && gBattleMons[battler].volatiles.throatChopTimer > 0 && IsSoundMove(move)) { gCurrentMove = move; - // if (gBattleTypeFlags & BATTLE_TYPE_PALACE) - // { - // gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveThroatChopInPalace; - // gProtectStructs[battler].palaceUnableToUseMove = TRUE; - // } - // else - // { + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + { + gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveThroatChopInPalace; + gProtectStructs[battler].palaceUnableToUseMove = TRUE; + } + else + { gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveThroatChop; limitations++; - // } + } } if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(battler) != GIMMICK_Z_MOVE && GetImprisonedMovesCount(battler, move)) { gCurrentMove = move; - // if (gBattleTypeFlags & BATTLE_TYPE_PALACE) - // { - // gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingImprisonedMoveInPalace; - // gProtectStructs[battler].palaceUnableToUseMove = TRUE; - // } - // else - // { + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + { + gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingImprisonedMoveInPalace; + gProtectStructs[battler].palaceUnableToUseMove = TRUE; + } + else + { gSelectionBattleScripts[battler] = BattleScript_SelectingImprisonedMove; limitations++; - // } + } } if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(battler) != GIMMICK_Z_MOVE && IsGravityPreventingMove(move)) { gCurrentMove = move; - // if (gBattleTypeFlags & BATTLE_TYPE_PALACE) - // { - // gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveGravityInPalace; - // gProtectStructs[battler].palaceUnableToUseMove = TRUE; - // } - // else - // { + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + { + gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveGravityInPalace; + gProtectStructs[battler].palaceUnableToUseMove = TRUE; + } + else + { gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveGravity; limitations++; - // } + } } if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(battler) != GIMMICK_Z_MOVE && IsHealBlockPreventingMove(battler, move)) { gCurrentMove = move; - // if (gBattleTypeFlags & BATTLE_TYPE_PALACE) - // { - // gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveHealBlockInPalace; - // gProtectStructs[battler].palaceUnableToUseMove = TRUE; - // } - // else - // { + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + { + gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveHealBlockInPalace; + gProtectStructs[battler].palaceUnableToUseMove = TRUE; + } + else + { gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveHealBlock; limitations++; - // } + } } if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(battler) != GIMMICK_Z_MOVE && IsBelchPreventingMove(battler, move)) { gCurrentMove = move; - // if (gBattleTypeFlags & BATTLE_TYPE_PALACE) - // { - // gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedBelchInPalace; - // gProtectStructs[battler].palaceUnableToUseMove = TRUE; - // } - // else - // { + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + { + gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedBelchInPalace; + gProtectStructs[battler].palaceUnableToUseMove = TRUE; + } + else + { gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedBelch; limitations++; - // } + } } if (DYNAMAX_BYPASS_CHECK && moveEffect == EFFECT_STUFF_CHEEKS && GetItemPocket(gBattleMons[battler].item) != POCKET_BERRIES) { gCurrentMove = move; - // if (gBattleTypeFlags & BATTLE_TYPE_PALACE) - // { - // gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedStuffCheeksInPalace; - // gProtectStructs[battler].palaceUnableToUseMove = TRUE; - // } - // else - // { + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + { + gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedStuffCheeksInPalace; + gProtectStructs[battler].palaceUnableToUseMove = TRUE; + } + else + { gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedStuffCheeks; limitations++; - // } + } } if (MoveCantBeUsedTwice(move) && move == gLastResultingMoves[battler]) { gCurrentMove = move; PREPARE_MOVE_BUFFER(gBattleTextBuff1, gCurrentMove); - // if (gBattleTypeFlags & BATTLE_TYPE_PALACE) - // { - // gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedCurrentMoveInPalace; - // gProtectStructs[battler].palaceUnableToUseMove = TRUE; - // } - // else - // { + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + { + gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedCurrentMoveInPalace; + gProtectStructs[battler].palaceUnableToUseMove = TRUE; + } + else + { gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedCurrentMove; limitations++; - // } + } } gPotentialItemEffectBattler = battler; @@ -1643,16 +1647,16 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler) { gCurrentMove = *choicedMove; gLastUsedItem = gBattleMons[battler].item; - // if (gBattleTypeFlags & BATTLE_TYPE_PALACE) - // { - // gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveChoiceItemInPalace; - // gProtectStructs[battler].palaceUnableToUseMove = TRUE; - // } - // else - // { + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + { + gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveChoiceItemInPalace; + gProtectStructs[battler].palaceUnableToUseMove = TRUE; + } + else + { gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveChoiceItem; limitations++; - // } + } } else if (holdEffect == HOLD_EFFECT_ASSAULT_VEST && IsBattleMoveStatus(move) && moveEffect != EFFECT_ME_FIRST) { @@ -1661,65 +1665,65 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler) else gCurrentMove = move; gLastUsedItem = gBattleMons[battler].item; - // if (gBattleTypeFlags & BATTLE_TYPE_PALACE) - // { - // gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveAssaultVestInPalace; - // gProtectStructs[battler].palaceUnableToUseMove = TRUE; - // } - // else - // { + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + { + gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveAssaultVestInPalace; + gProtectStructs[battler].palaceUnableToUseMove = TRUE; + } + else + { gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveAssaultVest; limitations++; - // } + } } if (DYNAMAX_BYPASS_CHECK && (GetBattlerAbility(battler) == ABILITY_GORILLA_TACTICS) && *choicedMove != MOVE_NONE && *choicedMove != MOVE_UNAVAILABLE && *choicedMove != move) { gCurrentMove = *choicedMove; gLastUsedItem = gBattleMons[battler].item; - // if (gBattleTypeFlags & BATTLE_TYPE_PALACE) - // { - // gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveGorillaTacticsInPalace; - // gProtectStructs[battler].palaceUnableToUseMove = TRUE; - // } - // else - // { + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + { + gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveGorillaTacticsInPalace; + gProtectStructs[battler].palaceUnableToUseMove = TRUE; + } + else + { gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveGorillaTactics; limitations++; - // } + } } if (gBattleMons[battler].pp[moveId] == 0) { - // if (gBattleTypeFlags & BATTLE_TYPE_PALACE) - // { - // gProtectStructs[battler].palaceUnableToUseMove = TRUE; - // } - // else - // { + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + { + gProtectStructs[battler].palaceUnableToUseMove = TRUE; + } + else + { gSelectionBattleScripts[battler] = BattleScript_SelectingMoveWithNoPP; limitations++; - // } + } } if (moveEffect == EFFECT_PLACEHOLDER) { - // if (gBattleTypeFlags & BATTLE_TYPE_PALACE) - // { - // gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedPlaceholderInPalace; - // gProtectStructs[battler].palaceUnableToUseMove = TRUE; - // } - // else - // { + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + { + gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedPlaceholderInPalace; + gProtectStructs[battler].palaceUnableToUseMove = TRUE; + } + else + { gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedPlaceholder; limitations++; - // } + } } return limitations; } -u32 CheckMoveLimitations(u32 battler, u8 unusableMoves, u16 check) +u32 CheckMoveLimitations(enum BattlerId battler, u8 unusableMoves, u16 check) { enum Move move; enum BattleMoveEffects moveEffect; @@ -1789,7 +1793,7 @@ u32 CheckMoveLimitations(u32 battler, u8 unusableMoves, u16 check) } #define ALL_MOVES_MASK ((1 << MAX_MON_MOVES) - 1) -bool32 AreAllMovesUnusable(u32 battler) +bool32 AreAllMovesUnusable(enum BattlerId battler) { u32 unusable = CheckMoveLimitations(battler, 0, MOVE_LIMITATIONS_ALL); @@ -1806,7 +1810,7 @@ bool32 AreAllMovesUnusable(u32 battler) return (unusable == ALL_MOVES_MASK); } -u8 GetImprisonedMovesCount(u32 battler, enum Move move) +u8 GetImprisonedMovesCount(enum BattlerId battler, enum Move move) { s32 i; u8 imprisonedMoves = 0; @@ -1830,7 +1834,7 @@ u8 GetImprisonedMovesCount(u32 battler, enum Move move) return imprisonedMoves; } -u32 GetBattlerAffectionHearts(u32 battler) +u32 GetBattlerAffectionHearts(enum BattlerId battler) { struct Pokemon *mon = GetBattlerMon(battler); u16 species = GetMonData(mon, MON_DATA_SPECIES); @@ -1862,7 +1866,7 @@ bool32 MoodyCantLowerStat(u32 stat) void TryToRevertMimicryAndFlags(void) { - for (u32 battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { gBattleMons[battler].volatiles.terrainAbilityDone = FALSE; ResetParadoxTerrainStat(battler); @@ -1871,24 +1875,24 @@ void TryToRevertMimicryAndFlags(void) } } -// bool32 BattleArenaTurnEnd(void) -// { -// if ((gBattleTypeFlags & BATTLE_TYPE_ARENA) -// && gBattleStruct->eventState.arenaTurn == 2 -// && IsBattlerAlive(B_POSITION_PLAYER_LEFT) && IsBattlerAlive(B_POSITION_OPPONENT_LEFT)) -// { -// for (u32 battler = 0; battler < 2; battler++) -// CancelMultiTurnMoves(battler, SKY_DROP_IGNORE); +bool32 BattleArenaTurnEnd(void) +{ + if ((gBattleTypeFlags & BATTLE_TYPE_ARENA) + && gBattleStruct->eventState.arenaTurn == 2 + && IsBattlerAlive(GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)) && IsBattlerAlive(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT))) + { + for (enum BattlerId battler = 0; battler < 2; battler++) + CancelMultiTurnMoves(battler, SKY_DROP_IGNORE); -// gBattlescriptCurrInstr = BattleScript_ArenaDoJudgment; -// BattleScriptExecute(BattleScript_ArenaDoJudgment); -// return TRUE; -// } -// return FALSE; -// } + gBattlescriptCurrInstr = BattleScript_ArenaDoJudgment; + BattleScriptExecute(BattleScript_ArenaDoJudgment); + return TRUE; + } + return FALSE; +} // Ingrain, Leech Seed, Strength Sap and Aqua Ring -s32 GetDrainedBigRootHp(u32 battler, s32 hp) +s32 GetDrainedBigRootHp(enum BattlerId battler, s32 hp) { if (GetBattlerHoldEffect(battler) == HOLD_EFFECT_BIG_ROOT) hp = (hp * 1300) / 1000; @@ -1899,7 +1903,7 @@ s32 GetDrainedBigRootHp(u32 battler, s32 hp) } // Should always be the last check. Otherwise the ability might be wrongly recorded. -bool32 IsAbilityAndRecord(u32 battler, enum Ability battlerAbility, enum Ability abilityToCheck) +bool32 IsAbilityAndRecord(enum BattlerId battler, enum Ability battlerAbility, enum Ability abilityToCheck) { if (battlerAbility != abilityToCheck) return FALSE; @@ -1915,13 +1919,12 @@ bool32 HandleFaintedMonActions(void) do { - s32 i; switch (gBattleStruct->eventState.faintedAction) { case FAINTED_ACTIONS_NO_MONS_TO_SWITCH: gBattleStruct->eventState.faintedActionBattler = 0; gBattleStruct->eventState.faintedAction++; - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (gAbsentBattlerFlags & (1u << i) && !HasNoMonsToSwitch(i, PARTY_SIZE, PARTY_SIZE)) gAbsentBattlerFlags &= ~(1u << i); @@ -2000,1612 +2003,14 @@ bool32 HandleFaintedMonActions(void) void TryClearRageAndFuryCutter(void) { - s32 i; - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (!MoveHasAdditionalEffect(gChosenMoveByBattler[i], MOVE_EFFECT_RAGE)) gBattleMons[i].volatiles.rage = FALSE; } } -static inline bool32 TryFormChangeBeforeMove(void) -{ - bool32 result = TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_BEFORE_MOVE); - if (!result) - result = TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_BEFORE_MOVE_CATEGORY); - if (!result) - return FALSE; - - gBattleScripting.battler = gBattlerAttacker; - BattleScriptCall(BattleScript_BattlerFormChange); - return TRUE; -} - -static inline bool32 TryActivatePowderStatus(enum Move move) -{ - u32 partnerMove = GetChosenMoveFromPosition(BATTLE_PARTNER(gBattlerAttacker)); - if (!gBattleMons[gBattlerAttacker].volatiles.powder) - return FALSE; - if (GetBattleMoveType(move) == TYPE_FIRE && !gBattleStruct->pledgeMove) - return TRUE; - if (move == MOVE_FIRE_PLEDGE && partnerMove == MOVE_GRASS_PLEDGE) - return TRUE; - if (move == MOVE_GRASS_PLEDGE && partnerMove == MOVE_FIRE_PLEDGE && gBattleStruct->pledgeMove) - return TRUE; - return FALSE; -} - -static void RequestNonVolatileChangee(u32 battlerAtk) -{ - BtlController_EmitSetMonData( - battlerAtk, - B_COMM_TO_CONTROLLER, - REQUEST_STATUS_BATTLE, - 0, - 4, - &gBattleMons[battlerAtk].status1); - MarkBattlerForControllerExec(battlerAtk); -} - -static enum MoveCanceler CancelerClearFlags(struct BattleContext *ctx) -{ - gBattleMons[ctx->battlerAtk].volatiles.grudge = FALSE; - gBattleMons[ctx->battlerAtk].volatiles.glaiveRush = FALSE; - gBattleStruct->eventState.atkCancelerBattler = 0; - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerStanceChangeOne(struct BattleContext *ctx) -{ - if (B_STANCE_CHANGE_FAIL < GEN_7 && ctx->chosenMove == ctx->move && TryFormChangeBeforeMove()) - return MOVE_STEP_BREAK; - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerSkyDrop(struct BattleContext *ctx) -{ - // If Pokemon is being held in Sky Drop - if (gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable == STATE_SKY_DROP) - { - gBattlescriptCurrInstr = BattleScript_MoveEnd; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerRecharge(struct BattleContext *ctx) -{ - if (gBattleMons[ctx->battlerAtk].volatiles.rechargeTimer > 0) - { - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedMustRecharge; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerChillyReception(struct BattleContext *ctx) -{ - if (GetMoveEffect(ctx->move) == EFFECT_WEATHER_AND_SWITCH) - { - BattleScriptCall(BattleScript_ChillyReceptionMessage); - return MOVE_STEP_BREAK; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerAsleepOrFrozen(struct BattleContext *ctx) -{ - enum MoveCanceler effect = MOVE_STEP_BREAK; - - if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP) - { - if (UproarWakeUpCheck(ctx->battlerAtk)) - { - TryDeactivateSleepClause(GetBattlerSide(ctx->battlerAtk), gBattlerPartyIndexes[ctx->battlerAtk]); - gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_SLEEP; - gBattleMons[ctx->battlerAtk].volatiles.nightmare = FALSE; - gEffectBattler = ctx->battlerAtk; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WOKE_UP_UPROAR; - effect = MOVE_STEP_BREAK; - BattleScriptCall(BattleScript_MoveUsedWokeUp); - } - else - { - u32 toSub; - if (IsAbilityAndRecord(ctx->battlerAtk, ctx->abilityAtk, ABILITY_EARLY_BIRD)) - toSub = 2; - else - toSub = 1; - - if ((gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP) < toSub) - gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_SLEEP; - else - gBattleMons[ctx->battlerAtk].status1 -= toSub; - - enum BattleMoveEffects moveEffect = GetMoveEffect(ctx->move); - if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP) - { - if (!IsUsableWhileAsleepEffect(moveEffect)) - { - effect = MOVE_STEP_FAILURE; - gBattlescriptCurrInstr = BattleScript_MoveUsedIsAsleep; - } - else - { - effect = MOVE_STEP_BREAK; - } - } - else - { - TryDeactivateSleepClause(GetBattlerSide(ctx->battlerAtk), gBattlerPartyIndexes[ctx->battlerAtk]); - gBattleMons[ctx->battlerAtk].volatiles.nightmare = FALSE; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WOKE_UP; - effect = MOVE_STEP_BREAK; - BattleScriptCall(BattleScript_MoveUsedWokeUp); - } - } - RequestNonVolatileChangee(ctx->battlerAtk); - } - else if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_FREEZE && !MoveThawsUser(ctx->move)) - { - if (!RandomPercentage(RNG_FROZEN, 20)) - { - effect = MOVE_STEP_FAILURE; - gBattlescriptCurrInstr = BattleScript_MoveUsedIsFrozen; - } - else // unfreeze - { - gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_FREEZE; - effect = MOVE_STEP_BREAK; - BattleScriptCall(BattleScript_MoveUsedUnfroze); - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DEFROSTED; - } - RequestNonVolatileChangee(ctx->battlerAtk); - } - - return effect; -} - -static enum MoveCanceler CancelerObedience(struct BattleContext *ctx) -{ - if (!gBattleMons[ctx->battlerAtk].volatiles.multipleTurns) - { - enum Obedience obedienceResult = GetAttackerObedienceForAction(); - switch (obedienceResult) - { - case OBEYS: - return MOVE_STEP_SUCCESS; - case DISOBEYS_LOAFS: - // Randomly select, then print a disobedient string - // B_MSG_LOAFING, B_MSG_WONT_OBEY, B_MSG_TURNED_AWAY, or B_MSG_PRETEND_NOT_NOTICE - gBattleCommunication[MULTISTRING_CHOOSER] = MOD(Random(), NUM_LOAF_STRINGS); - gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround; - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; - return MOVE_STEP_FAILURE; - case DISOBEYS_HITS_SELF: - gBattlerTarget = ctx->battlerAtk; - struct BattleContext dmgCtx = {0}; - dmgCtx.battlerAtk = dmgCtx.battlerDef = ctx->battlerAtk; - dmgCtx.move = dmgCtx.chosenMove = MOVE_NONE; - dmgCtx.moveType = TYPE_MYSTERY; - dmgCtx.isCrit = FALSE; - dmgCtx.randomFactor = FALSE; - dmgCtx.updateFlags = TRUE; - dmgCtx.isSelfInflicted = TRUE; - dmgCtx.fixedBasePower = 40; - gBattleStruct->moveDamage[ctx->battlerAtk] = CalculateMoveDamage(&dmgCtx); - gBattlescriptCurrInstr = BattleScript_IgnoresAndHitsItself; - return MOVE_STEP_FAILURE; // Move doesn't fail but mon hits itself - case DISOBEYS_FALL_ASLEEP: - if (IsSleepClauseEnabled()) - gBattleStruct->battlerState[ctx->battlerAtk].sleepClauseEffectExempt = TRUE; - gBattlescriptCurrInstr = BattleScript_IgnoresAndFallsAsleep; - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; - return MOVE_STEP_FAILURE; - break; - case DISOBEYS_WHILE_ASLEEP: - gBattlescriptCurrInstr = BattleScript_IgnoresWhileAsleep; - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; - return MOVE_STEP_FAILURE; - case DISOBEYS_RANDOM_MOVE: - gCurrentMove = gCalledMove = gBattleMons[ctx->battlerAtk].moves[gCurrMovePos]; - BattleScriptCall(BattleScript_IgnoresAndUsesRandomMove); - gBattlerTarget = GetBattleMoveTarget(gCalledMove, TARGET_NONE); - return MOVE_STEP_BREAK; - } - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerPowerPoints(struct BattleContext *ctx) -{ - if (gBattleMons[ctx->battlerAtk].pp[gCurrMovePos] == 0 - && ctx->move != MOVE_STRUGGLE - && !gSpecialStatuses[ctx->battlerAtk].dancerUsedMove - && !gBattleMons[ctx->battlerAtk].volatiles.multipleTurns) - { - gBattlescriptCurrInstr = BattleScript_NoPPForMove; - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; - return MOVE_STEP_FAILURE; - } - - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerTruant(struct BattleContext *ctx) -{ - if (GetBattlerAbility(ctx->battlerAtk) == ABILITY_TRUANT && gBattleMons[ctx->battlerAtk].volatiles.truantCounter) - { - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LOAFING; - gBattlerAbility = ctx->battlerAtk; - gBattlescriptCurrInstr = BattleScript_TruantLoafingAround; - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerFocus(struct BattleContext *ctx) -{ - u32 focusPunchFailureConfig = GetConfig(CONFIG_FOCUS_PUNCH_FAILURE); - - // In Gens 3-4, only check if is using Focus Punch. - // In Gens 5-6, only check if the chosen move is Focus Punch. - // In Gens 7+, check if chose and is using Focus Punch. - if ((gProtectStructs[ctx->battlerAtk].physicalDmg || gProtectStructs[ctx->battlerAtk].specialDmg) - && (focusPunchFailureConfig < GEN_5 || GetMoveEffect(gChosenMoveByBattler[ctx->battlerAtk]) == EFFECT_FOCUS_PUNCH) - && (focusPunchFailureConfig == GEN_5 || focusPunchFailureConfig == GEN_6 || GetMoveEffect(ctx->move) == EFFECT_FOCUS_PUNCH) - && !gProtectStructs[ctx->battlerAtk].survivedOHKO) - { - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_FocusPunchLostFocus; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerFocusPreGen5(struct BattleContext *ctx) -{ - if (GetConfig(CONFIG_FOCUS_PUNCH_FAILURE) < GEN_5) - return CancelerFocus(ctx); - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerFocusGen5(struct BattleContext *ctx) -{ - if (GetConfig(CONFIG_FOCUS_PUNCH_FAILURE) >= GEN_5) - return CancelerFocus(ctx); - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerFlinch(struct BattleContext *ctx) -{ - if (gBattleMons[ctx->battlerAtk].volatiles.flinched) - { - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedFlinched; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerDisabled(struct BattleContext *ctx) -{ - if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE - && gBattleMons[ctx->battlerAtk].volatiles.disabledMove == ctx->move - && gBattleMons[ctx->battlerAtk].volatiles.disabledMove != MOVE_NONE) - { - gBattleScripting.battler = ctx->battlerAtk; - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedIsDisabled; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerVolatileBlocked(struct BattleContext *ctx) -{ - if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE - && gBattleMons[ctx->battlerAtk].volatiles.healBlock - && IsHealBlockPreventingMove(ctx->battlerAtk, ctx->move)) - { - gBattleScripting.battler = ctx->battlerAtk; - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedHealBlockPrevents; - return MOVE_STEP_FAILURE; - } - else if (gFieldStatuses & STATUS_FIELD_GRAVITY && IsGravityPreventingMove(ctx->move)) - { - gBattleScripting.battler = ctx->battlerAtk; - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedGravityPrevents; - return MOVE_STEP_FAILURE; - } - else if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE && gBattleMons[ctx->battlerAtk].volatiles.throatChopTimer > 0 && IsSoundMove(ctx->move)) - { - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedIsThroatChopPrevented; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerTaunted(struct BattleContext *ctx) -{ - if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE && gBattleMons[ctx->battlerAtk].volatiles.tauntTimer && IsBattleMoveStatus(ctx->move)) - { - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedIsTaunted; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerImprisoned(struct BattleContext *ctx) -{ - if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE && GetImprisonedMovesCount(ctx->battlerAtk, ctx->move)) - { - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedIsImprisoned; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerConfused(struct BattleContext *ctx) -{ - if (gBattleMons[ctx->battlerAtk].volatiles.confusionTurns) - { - if (!gBattleMons[ctx->battlerAtk].volatiles.infiniteConfusion) - gBattleMons[ctx->battlerAtk].volatiles.confusionTurns--; - if (gBattleMons[ctx->battlerAtk].volatiles.confusionTurns) - { - // confusion dmg - if (RandomPercentage(RNG_CONFUSION, (GetConfig(CONFIG_CONFUSION_SELF_DMG_CHANCE) >= GEN_7 ? 33 : 50))) - { - gBattleCommunication[MULTISTRING_CHOOSER] = TRUE; - struct BattleContext dmgCtx = {0}; - dmgCtx.battlerAtk = dmgCtx.battlerDef = ctx->battlerAtk; - dmgCtx.move = dmgCtx.chosenMove = MOVE_NONE; - dmgCtx.moveType = TYPE_MYSTERY; - dmgCtx.isCrit = FALSE; - dmgCtx.randomFactor = FALSE; - dmgCtx.updateFlags = TRUE; - dmgCtx.isSelfInflicted = TRUE; - dmgCtx.fixedBasePower = 40; - gBattleStruct->passiveHpUpdate[ctx->battlerAtk] = CalculateMoveDamage(&dmgCtx); - gBattlescriptCurrInstr = BattleScript_MoveUsedIsConfused; - return MOVE_STEP_FAILURE; - } - else - { - gBattleCommunication[MULTISTRING_CHOOSER] = FALSE; - BattleScriptCall(BattleScript_MoveUsedIsConfused); - return MOVE_STEP_BREAK; - } - } - else // snapped out of confusion - { - BattleScriptCall(BattleScript_MoveUsedIsConfusedNoMore); - return MOVE_STEP_BREAK; - } - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerGhost(struct BattleContext *ctx) // GHOST in pokemon tower -{ - if (IsGhostBattleWithoutScope()) - { - if (GetBattlerSide(ctx->battlerAtk) == B_SIDE_PLAYER) - gBattlescriptCurrInstr = BattleScript_TooScaredToMove; - else - gBattlescriptCurrInstr = BattleScript_GhostGetOutGetOut; - gBattleCommunication[MULTISTRING_CHOOSER] = 0; - return MOVE_STEP_BREAK; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerParalyzed(struct BattleContext *ctx) -{ - if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_PARALYSIS - && !(B_MAGIC_GUARD == GEN_4 && IsAbilityAndRecord(ctx->battlerAtk, ctx->abilityAtk, ABILITY_MAGIC_GUARD)) - && !RandomPercentage(RNG_PARALYSIS, 75)) - { - CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedIsParalyzed; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerInfatuation(struct BattleContext *ctx) -{ - if (gBattleMons[ctx->battlerAtk].volatiles.infatuation) - { - gBattleScripting.battler = gBattleMons[ctx->battlerAtk].volatiles.infatuation - 1; - if (!RandomPercentage(RNG_INFATUATION, 50)) - { - BattleScriptCall(BattleScript_MoveUsedIsInLove); - return MOVE_STEP_BREAK; - } - else - { - BattleScriptPush(BattleScript_MoveUsedIsInLoveCantAttack); - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_MoveUsedIsInLove; - return MOVE_STEP_FAILURE; - } - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerBide(struct BattleContext *ctx) -{ - if (gBattleMons[ctx->battlerAtk].volatiles.bideTurns) - { - if (--gBattleMons[ctx->battlerAtk].volatiles.bideTurns) - { - gBattlescriptCurrInstr = BattleScript_BideStoringEnergy; - } - else - { - // This is removed in FRLG and Emerald for some reason - //gBattleMons[gBattlerAttacker].volatiles.multipleTurns = FALSE; - if (gBideDmg[ctx->battlerAtk]) - { - gCurrentMove = MOVE_BIDE; - gBattlerTarget = gBideTarget[ctx->battlerAtk]; - if (!IsBattlerAlive(ctx->battlerDef)) - gBattlerTarget = GetBattleMoveTarget(MOVE_BIDE, TARGET_SELECTED); - gBattlescriptCurrInstr = BattleScript_BideAttack; - return MOVE_STEP_BREAK; // Jumps to a different script but no failure - } - else - { - gBattlescriptCurrInstr = BattleScript_BideNoEnergyToAttack; - return MOVE_STEP_FAILURE; - } - } - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerZMoves(struct BattleContext *ctx) -{ - if (GetActiveGimmick(ctx->battlerAtk) == GIMMICK_Z_MOVE) - { - // attacker has a queued z move - RecordItemEffectBattle(ctx->battlerAtk, HOLD_EFFECT_Z_CRYSTAL); - SetGimmickAsActivated(ctx->battlerAtk, GIMMICK_Z_MOVE); - - gBattleScripting.battler = ctx->battlerAtk; - if (GetMoveCategory(ctx->move) == DAMAGE_CATEGORY_STATUS) - BattleScriptCall(BattleScript_ZMoveActivateStatus); - else - BattleScriptCall(BattleScript_ZMoveActivateDamaging); - - return MOVE_STEP_BREAK; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerChoiceLock(struct BattleContext *ctx) -{ - u16 *choicedMoveAtk = &gBattleStruct->choicedMove[ctx->battlerAtk]; - enum HoldEffect holdEffect = GetBattlerHoldEffect(ctx->battlerAtk); - - if (gChosenMove != MOVE_STRUGGLE - && (*choicedMoveAtk == MOVE_NONE || *choicedMoveAtk == MOVE_UNAVAILABLE) - && (IsHoldEffectChoice(holdEffect) || ctx->abilityAtk == ABILITY_GORILLA_TACTICS)) - *choicedMoveAtk = gChosenMove; - - u32 moveIndex; - for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) - { - if (gBattleMons[ctx->battlerAtk].moves[moveIndex] == *choicedMoveAtk) - break; - } - - if (moveIndex == MAX_MON_MOVES) - *choicedMoveAtk = MOVE_NONE; - - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerCallSubmove(struct BattleContext *ctx) -{ - bool32 noEffect = FALSE; - enum Move calledMove = MOVE_NONE; - const u8 *battleScript = NULL; - battleScript = BattleScript_SubmoveAttackstring; - - switch(GetMoveEffect(ctx->move)) - { - case EFFECT_MIRROR_MOVE: - calledMove = GetMirrorMoveMove(); - break; - case EFFECT_METRONOME: - calledMove = GetMetronomeMove(); - battleScript = BattleScript_MetronomeAttackstring; - break; - case EFFECT_ASSIST: - calledMove = GetAssistMove(); - break; - case EFFECT_NATURE_POWER: - calledMove = GetNaturePowerMove(ctx->battlerAtk); - battleScript = BattleScript_NaturePowerAttackstring; - break; - case EFFECT_SLEEP_TALK: - calledMove = GetSleepTalkMove(); - battleScript = BattleScript_SleepTalkAttackstring; - break; - case EFFECT_COPYCAT: - calledMove = GetCopycatMove(); - break; - case EFFECT_ME_FIRST: - calledMove = GetMeFirstMove(); - break; - default: - noEffect = TRUE; - break; - } - - if (noEffect) - { - gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT; - return MOVE_STEP_SUCCESS; - } - - if (calledMove != MOVE_NONE) - { - if (GetActiveGimmick(ctx->battlerAtk) == GIMMICK_Z_MOVE && !IsBattleMoveStatus(calledMove)) - calledMove = GetTypeBasedZMove(calledMove); - if (GetMoveEffect(ctx->move) == EFFECT_COPYCAT && IsMaxMove(calledMove)) - calledMove = gBattleStruct->dynamax.lastUsedBaseMove; - - gBattleStruct->submoveAnnouncement = SUBMOVE_SUCCESS; - gCalledMove = calledMove; - BattleScriptCall(battleScript); - return MOVE_STEP_BREAK; - } - - gBattleStruct->submoveAnnouncement = SUBMOVE_FAILURE; - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerThaw(struct BattleContext *ctx) -{ - enum MoveCanceler effect = MOVE_STEP_BREAK; - - if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_FREEZE) - { - if (!(IsMoveEffectRemoveSpeciesType(ctx->move, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_FIRE) && !IS_BATTLER_OF_TYPE(ctx->battlerAtk, TYPE_FIRE))) - { - gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_FREEZE; - effect = MOVE_STEP_BREAK; - BattleScriptCall(BattleScript_MoveUsedUnfroze); - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DEFROSTED_BY_MOVE; - } - else - { - effect = MOVE_STEP_FAILURE; - } - RequestNonVolatileChangee(ctx->battlerAtk); - } - else if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_FROSTBITE && MoveThawsUser(ctx->move)) - { - if (!(IsMoveEffectRemoveSpeciesType(ctx->move, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_FIRE) && !IS_BATTLER_OF_TYPE(ctx->battlerAtk, TYPE_FIRE))) - { - gBattleMons[ctx->battlerAtk].status1 &= ~STATUS1_FROSTBITE; - effect = MOVE_STEP_BREAK; - BattleScriptCall(BattleScript_MoveUsedUnfrostbite); - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FROSTBITE_HEALED_BY_MOVE; - } - else - { - effect = MOVE_STEP_FAILURE; - } - RequestNonVolatileChangee(ctx->battlerAtk); - } - return effect; -} - -static enum MoveCanceler CancelerStanceChangeTwo(struct BattleContext *ctx) -{ - if (B_STANCE_CHANGE_FAIL >= GEN_7 && gChosenMove == ctx->move && TryFormChangeBeforeMove()) - return MOVE_STEP_BREAK; - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerAttackstring(struct BattleContext *ctx) -{ - BattleScriptCall(BattleScript_Attackstring); - if (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove) - gLastPrintedMoves[gBattlerAttacker] = gChosenMove; - return MOVE_STEP_BREAK; -} - -static enum MoveCanceler CancelerPPDeduction(struct BattleContext *ctx) -{ - if (gBattleMons[ctx->battlerAtk].volatiles.multipleTurns - || gSpecialStatuses[ctx->battlerAtk].dancerUsedMove - || ctx->move == MOVE_STRUGGLE) - return MOVE_STEP_SUCCESS; - - s32 ppToDeduct = 1; - u32 moveTarget = GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move); - u32 movePosition = gCurrMovePos; - - if (gBattleStruct->submoveAnnouncement == SUBMOVE_SUCCESS) - movePosition = gChosenMovePos; - - if (IsSpreadMove(moveTarget) - || moveTarget == TARGET_ALL_BATTLERS - || moveTarget == TARGET_FIELD - || MoveForcesPressure(ctx->move)) - { - for (u32 i = 0; i < gBattlersCount; i++) - { - if (!IsBattlerAlly(i, ctx->battlerAtk) && IsBattlerAlive(i)) - ppToDeduct += (GetBattlerAbility(i) == ABILITY_PRESSURE); - } - } - else if (moveTarget != TARGET_OPPONENTS_FIELD) - { - if (ctx->battlerAtk != ctx->battlerDef && GetBattlerAbility(ctx->battlerDef) == ABILITY_PRESSURE) - ppToDeduct++; - } - - // For item Metronome, echoed voice - if (ctx->move != gLastResultingMoves[ctx->battlerAtk] || gBattleStruct->unableToUseMove) - gBattleMons[ctx->battlerAtk].volatiles.metronomeItemCounter = 0; - - if (gBattleMons[ctx->battlerAtk].pp[movePosition] > ppToDeduct) - gBattleMons[ctx->battlerAtk].pp[movePosition] -= ppToDeduct; - else - gBattleMons[ctx->battlerAtk].pp[movePosition] = 0; - - if (MOVE_IS_PERMANENT(ctx->battlerAtk, movePosition)) - { - BtlController_EmitSetMonData( - ctx->battlerAtk, - B_COMM_TO_CONTROLLER, - REQUEST_PPMOVE1_BATTLE + movePosition, - 0, - sizeof(gBattleMons[ctx->battlerAtk].pp[movePosition]), - &gBattleMons[ctx->battlerAtk].pp[movePosition]); - MarkBattlerForControllerExec(ctx->battlerAtk); - } - - if (gBattleStruct->submoveAnnouncement != SUBMOVE_NO_EFFECT) - { - if (gBattleStruct->submoveAnnouncement == SUBMOVE_FAILURE) - { - gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT; - gBattlescriptCurrInstr = BattleScript_ButItFailed; - return MOVE_STEP_FAILURE; - } - else if (CancelerVolatileBlocked(ctx) == MOVE_STEP_FAILURE) // Check Gravity/Heal Block/Throat Chop for Submove - { - gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT; - return MOVE_STEP_FAILURE; - } - else - { - gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT; - gBattlerTarget = GetBattleMoveTarget(ctx->move, TARGET_NONE); - gBattleScripting.animTurn = 0; - gBattleScripting.animTargetsHit = 0; - - // Possibly better to just move type setting and redirection to attackcanceler as a new case at this point - SetTypeBeforeUsingMove(ctx->move, ctx->battlerAtk); - DetermineTarget(moveTarget, FALSE); - ClearDamageCalcResults(); - gBattlescriptCurrInstr = GetMoveBattleScript(ctx->move); - return MOVE_STEP_BREAK; - } - } - - return MOVE_STEP_SUCCESS; -} - -// We don't have clear data on where this belongs to but I assume it should at least be checked before Protean -static enum MoveCanceler CancelerSkyBattle(struct BattleContext *ctx) -{ - if (gBattleStruct->isSkyBattle && IsMoveSkyBattleBanned(gCurrentMove)) - { - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_ButItFailed; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerWeatherPrimal(struct BattleContext *ctx) -{ - enum MoveCanceler effect = MOVE_STEP_SUCCESS; - if (HasWeatherEffect() && GetMovePower(ctx->move) > 0) - { - enum Type moveType = GetBattleMoveType(ctx->move); - if (moveType == TYPE_FIRE && gBattleWeather & B_WEATHER_RAIN_PRIMAL && (GetConfig(CONFIG_POWDER_STATUS_HEAVY_RAIN) >= GEN_7 || !TryActivatePowderStatus(ctx->move))) - { - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_FIZZLED_BY_RAIN; - effect = MOVE_STEP_FAILURE; - } - else if (moveType == TYPE_WATER && gBattleWeather & B_WEATHER_SUN_PRIMAL) - { - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_EVAPORATED_IN_SUN; - effect = MOVE_STEP_FAILURE; - } - if (effect == MOVE_STEP_FAILURE) - { - gProtectStructs[ctx->battlerAtk].chargingTurn = FALSE; - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove; - } - } - return effect; -} - -static enum MoveCanceler CancelerMoveFailure(struct BattleContext *ctx) -{ - const u8 *battleScript = NULL; - - switch (GetMoveEffect(ctx->move)) - { - case EFFECT_FAIL_IF_NOT_ARG_TYPE: - if (!IS_BATTLER_OF_TYPE(ctx->battlerAtk, GetMoveArgType(ctx->move))) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_AURA_WHEEL: - if (gBattleMons[ctx->battlerAtk].species != SPECIES_MORPEKO_FULL_BELLY - && gBattleMons[ctx->battlerAtk].species != SPECIES_MORPEKO_HANGRY) - battleScript = BattleScript_PokemonCantUseTheMove; - break; - case EFFECT_AURORA_VEIL: - if (!(gBattleWeather & B_WEATHER_ICY_ANY && HasWeatherEffect())) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_CLANGOROUS_SOUL: - if (gBattleMons[ctx->battlerAtk].hp <= max(1, GetNonDynamaxMaxHP(ctx->battlerAtk) / 3)) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_REFLECT_DAMAGE: - { - enum DamageCategory reflectCategory = GetReflectDamageMoveDamageCategory(ctx->battlerAtk, ctx->move); - if (IsBattlerAlly(ctx->battlerAtk, ctx->battlerDef)) - battleScript = BattleScript_ButItFailed; - // Counter / Metal Burst and took physical damage - else if (reflectCategory == DAMAGE_CATEGORY_PHYSICAL - && gProtectStructs[ctx->battlerAtk].physicalDmg > 0 - && (GetConfig(CONFIG_COUNTER_TRY_HIT_PARTNER) >= GEN_5 || gBattleMons[gProtectStructs[ctx->battlerAtk].physicalBattlerId].hp)) - break; - // Mirror Coat / Metal Burst and took special damage - else if (reflectCategory == DAMAGE_CATEGORY_SPECIAL - && gProtectStructs[ctx->battlerAtk].specialDmg > 0 - && (GetConfig(CONFIG_COUNTER_TRY_HIT_PARTNER) >= GEN_5 || gBattleMons[gProtectStructs[ctx->battlerAtk].specialBattlerId].hp)) - break; - else - battleScript = BattleScript_ButItFailed; - break; - } - case EFFECT_DESTINY_BOND: - if (DoesDestinyBondFail(ctx->battlerAtk)) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_FIRST_TURN_ONLY: - if (!gBattleStruct->battlerState[ctx->battlerAtk].isFirstTurn || gSpecialStatuses[ctx->battlerAtk].instructedChosenTarget) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_MAT_BLOCK: - if (!gBattleStruct->battlerState[ctx->battlerAtk].isFirstTurn || gSpecialStatuses[ctx->battlerAtk].instructedChosenTarget) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_FLING: - if (!CanFling(ctx->battlerAtk, ctx->battlerDef)) - battleScript = BattleScript_ButItFailed; - else if (!IsBattlerAlive(ctx->battlerDef)) // Edge case for removing a mon's item when there is no target available after using Fling. - battleScript = BattleScript_FlingFailConsumeItem; - break; - case EFFECT_FOLLOW_ME: - if (B_UPDATED_MOVE_DATA >= GEN_8 && !(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_FUTURE_SIGHT: - if (gBattleStruct->futureSight[ctx->battlerDef].counter > 0) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_LAST_RESORT: - if (!CanUseLastResort(ctx->battlerAtk)) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_NO_RETREAT: - if (gBattleMons[ctx->battlerDef].volatiles.noRetreat) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_POLTERGEIST: - if (gBattleMons[ctx->battlerDef].item == ITEM_NONE - || gFieldStatuses & STATUS_FIELD_MAGIC_ROOM - || ctx->abilityDef == ABILITY_KLUTZ) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_PROTECT: - case EFFECT_ENDURE: - TryResetConsecutiveUseCounter(gBattlerAttacker); - if (IsLastMonToMove(ctx->battlerAtk)) - { - battleScript = BattleScript_ButItFailed; - } - else - { - u32 protectMethod = GetMoveProtectMethod(ctx->move); - bool32 canUseProtectSecondTime = CanUseMoveConsecutively(ctx->battlerAtk); - bool32 canUseWideGuard = (GetConfig(CONFIG_WIDE_GUARD) >= GEN_6 && protectMethod == PROTECT_WIDE_GUARD); - bool32 canUseQuickGuard = (GetConfig(CONFIG_QUICK_GUARD) >= GEN_6 && protectMethod == PROTECT_QUICK_GUARD); - - if (!canUseProtectSecondTime && !canUseWideGuard && !canUseQuickGuard) - battleScript = BattleScript_ButItFailed; - } - if (battleScript != NULL) - { - gBattleMons[gBattlerAttacker].volatiles.consecutiveMoveUses = 0; - gBattleStruct->battlerState[gBattlerAttacker].stompingTantrumTimer = 2; - } - break; - case EFFECT_REST: - if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP - || ctx->abilityAtk == ABILITY_COMATOSE) - battleScript = BattleScript_RestIsAlreadyAsleep; - else if (gBattleMons[ctx->battlerAtk].hp == gBattleMons[ctx->battlerAtk].maxHP) - battleScript = BattleScript_AlreadyAtFullHp; - else if (ctx->abilityAtk == ABILITY_INSOMNIA - || ctx->abilityAtk == ABILITY_VITAL_SPIRIT - || ctx->abilityAtk == ABILITY_PURIFYING_SALT) - battleScript = BattleScript_InsomniaProtects; - break; - case EFFECT_SUCKER_PUNCH: - if (HasBattlerActedThisTurn(ctx->battlerDef) - || (IsBattleMoveStatus(GetChosenMoveFromPosition(ctx->battlerDef)) && !gProtectStructs[ctx->battlerDef].noValidMoves)) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_UPPER_HAND: - { - u32 prio = GetChosenMovePriority(ctx->battlerDef, GetBattlerAbility(ctx->battlerDef)); - if (prio < 1 || prio > 3 // Fails if priority is less than 1 or greater than 3, if target already moved, or if using a status - || HasBattlerActedThisTurn(ctx->battlerDef) - || gChosenMoveByBattler[ctx->battlerDef] == MOVE_NONE - || IsBattleMoveStatus(gChosenMoveByBattler[ctx->battlerDef])) - battleScript = BattleScript_ButItFailed; - break; - } - case EFFECT_SNORE: - if (!(gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP) - && ctx->abilityAtk != ABILITY_COMATOSE) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_STEEL_ROLLER: - if (!(gFieldStatuses & STATUS_FIELD_TERRAIN_ANY)) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_STOCKPILE: - if (gBattleMons[ctx->battlerAtk].volatiles.stockpileCounter >= 3) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_STUFF_CHEEKS: - if (GetItemPocket(gBattleMons[ctx->battlerAtk].item) != POCKET_BERRIES) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_SWALLOW: - case EFFECT_SPIT_UP: - if (gBattleMons[ctx->battlerAtk].volatiles.stockpileCounter == 0 && !gBattleStruct->snatchedMoveIsUsed) - battleScript = BattleScript_ButItFailed; - break; - case EFFECT_TELEPORT: - // TODO: follow up: Can't make sense of teleport logic - break; - case EFFECT_LOW_KICK: - case EFFECT_HEAT_CRASH: - if (GetActiveGimmick(ctx->battlerDef) == GIMMICK_DYNAMAX) - battleScript = BattleScript_MoveBlockedByDynamax; - break; - default: - break; - } - - if (battleScript != NULL) - { - gBattlescriptCurrInstr = battleScript; - return MOVE_STEP_FAILURE; - } - - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerPowderStatus(struct BattleContext *ctx) -{ - if (TryActivatePowderStatus(ctx->move)) - { - if (!IsAbilityAndRecord(ctx->battlerAtk, ctx->abilityAtk, ABILITY_MAGIC_GUARD)) - SetPassiveDamageAmount(ctx->battlerAtk, GetNonDynamaxMaxHP(ctx->battlerAtk) / 4); - - // This might be incorrect - if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_Z_MOVE - || HasTrainerUsedGimmick(ctx->battlerAtk, GIMMICK_Z_MOVE)) - gBattlescriptCurrInstr = BattleScript_MoveUsedPowder; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -bool32 IsDazzlingAbility(enum Ability ability) -{ - switch (ability) - { - case ABILITY_DAZZLING: return TRUE; - case ABILITY_QUEENLY_MAJESTY: return TRUE; - case ABILITY_ARMOR_TAIL: return TRUE; - default: break; - } - return FALSE; -} - -static enum MoveCanceler CancelerPriorityBlock(struct BattleContext *ctx) -{ - bool32 effect = FALSE; - s32 priority = GetChosenMovePriority(ctx->battlerAtk, ctx->abilityAtk); - - if (priority <= 0) - return MOVE_STEP_SUCCESS; - - u32 battler; - u32 ability = ABILITY_NONE; // ability of battler who is blocking - bool32 isSpreadMove = IsSpreadMove(GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move)); - for (battler = 0; battler < gBattlersCount; battler++) - { - if (!IsBattlerAlive(battler) || IsBattlerAlly(ctx->battlerAtk, battler)) - continue; - if (!isSpreadMove && !IsBattlerAlly(battler, ctx->battlerDef)) - continue; - - ability = GetBattlerAbility(battler); - if (IsDazzlingAbility(ability)) - { - effect = TRUE; - break; - } - } - - if (effect) - { - gLastUsedAbility = ability; - RecordAbilityBattle(battler, ability); - gBattleScripting.battler = gBattlerAbility = battler; - gBattlescriptCurrInstr = BattleScript_DazzlingProtected; - return MOVE_STEP_FAILURE; - } - - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerProtean(struct BattleContext *ctx) -{ - enum Type moveType = GetBattleMoveType(ctx->move); - if (ProteanTryChangeType(ctx->battlerAtk, ctx->abilityAtk, ctx->move, moveType)) - { - if (GetConfig(CONFIG_PROTEAN_LIBERO) >= GEN_9) - gBattleMons[ctx->battlerAtk].volatiles.usedProteanLibero = TRUE; - PREPARE_TYPE_BUFFER(gBattleTextBuff1, moveType); - gBattlerAbility = ctx->battlerAtk; - PrepareStringBattle(STRINGID_EMPTYSTRING3, ctx->battlerAtk); - gBattleCommunication[MSG_DISPLAY] = 1; - BattleScriptCall(BattleScript_ProteanActivates); - return MOVE_STEP_BREAK; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerExplodingDamp(struct BattleContext *ctx) -{ - u32 dampBattler = IsAbilityOnField(ABILITY_DAMP); - if (dampBattler && IsMoveDampBanned(ctx->move)) - { - gBattleScripting.battler = dampBattler - 1; - gBattlescriptCurrInstr = BattleScript_DampStopsExplosion; - return MOVE_STEP_FAILURE; - } - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerExplosion(struct BattleContext *ctx) -{ - // KO user of Explosion; for Final Gambit doesn't happen if target is immune or if it missed - if (IsExplosionMove(ctx->move) - && (GetMoveEffect(ctx->move) != EFFECT_FINAL_GAMBIT || !IsBattlerUnaffectedByMove(ctx->battlerDef))) - { - BattleScriptCall(BattleScript_Explosion); - return MOVE_STEP_BREAK; - } - - return MOVE_STEP_SUCCESS; -} - -static bool32 CanTwoTurnMoveFireThisTurn(struct BattleContext *ctx) -{ - if (gBattleMoveEffects[GetMoveEffect(ctx->move)].semiInvulnerableEffect - || GetMoveEffect(ctx->move) == EFFECT_GEOMANCY - || !IsBattlerWeatherAffected(ctx->battlerAtk, GetMoveTwoTurnAttackWeather(ctx->move))) - return FALSE; - return TRUE; -} - -static enum MoveCanceler CancelerCharging(struct BattleContext *ctx) -{ - if (!gBattleMoveEffects[GetMoveEffect(ctx->move)].twoTurnEffect - || GetMoveEffect(ctx->move) == EFFECT_SKY_DROP) - return MOVE_STEP_SUCCESS; - - enum MoveCanceler step = MOVE_STEP_SUCCESS; - - if (gBattleMons[ctx->battlerAtk].volatiles.multipleTurns) // Second turn - { - gBattleScripting.animTurn = 1; - gBattleScripting.animTargetsHit = 0; - gBattleMons[ctx->battlerAtk].volatiles.multipleTurns = FALSE; - if (gBattleMoveEffects[GetMoveEffect(ctx->move)].semiInvulnerableEffect) - gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable = STATE_NONE; - step = MOVE_STEP_SUCCESS; - } - else if (!gProtectStructs[ctx->battlerAtk].chargingTurn) // First turn charge - { - gLockedMoves[ctx->battlerAtk] = ctx->move; - gProtectStructs[ctx->battlerAtk].chargingTurn = TRUE; - if (gBattleMoveEffects[GetMoveEffect(ctx->move)].semiInvulnerableEffect) - gBattleMons[ctx->battlerAtk].volatiles.semiInvulnerable = GetMoveTwoTurnAttackStatus(ctx->move); - BattleScriptCall(BattleScript_TwoTurnMoveCharging); - step = MOVE_STEP_PAUSE; - } - else // Try move this turn. Otherwise use next turn - { - if (CanTwoTurnMoveFireThisTurn(ctx)) - { - gBattleScripting.animTurn = 1; - gBattleScripting.animTargetsHit = 0; - gProtectStructs[ctx->battlerAtk].chargingTurn = FALSE; - step = MOVE_STEP_SUCCESS; - } - else if (ctx->holdEffectAtk == HOLD_EFFECT_POWER_HERB) - { - gBattleScripting.animTurn = 1; - gBattleScripting.animTargetsHit = 0; - gProtectStructs[ctx->battlerAtk].chargingTurn = FALSE; - gLastUsedItem = gBattleMons[ctx->battlerAtk].item; - BattleScriptCall(BattleScript_PowerHerbActivation); - step = MOVE_STEP_BREAK; - } - else // Use move next turn - { - gBattleMons[ctx->battlerAtk].volatiles.multipleTurns = TRUE; - gBattlescriptCurrInstr = BattleScript_MoveEnd; - step = MOVE_STEP_BREAK; - } - - } - - return step; -} - -static bool32 NoTargetPresent(u32 battler, u32 move, u32 moveTarget) -{ - switch (moveTarget) - { - case TARGET_USER_AND_ALLY: - return FALSE; // At least user is present - case TARGET_ALLY: - if (!IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker))) // Seems like TARGET_ALLY is retargeting if no ally - return TRUE; - break; - case TARGET_SELECTED: - case TARGET_DEPENDS: - case TARGET_RANDOM: - if (!IsBattlerAlive(gBattlerTarget) && !IsBattlerAlive(GetBattleMoveTarget(move, TARGET_NONE))) - return TRUE; - break; - case TARGET_BOTH: - case TARGET_SMART: - if (!IsBattlerAlive(gBattlerTarget) && !IsBattlerAlive(BATTLE_PARTNER(gBattlerTarget))) - return TRUE; - break; - case TARGET_FOES_AND_ALLY: - if (!IsBattlerAlive(gBattlerTarget) && !IsBattlerAlive(BATTLE_PARTNER(gBattlerTarget)) && !IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker))) - return TRUE; - break; - } - - return FALSE; -} - -static enum MoveCanceler CancelerNoTarget(struct BattleContext *ctx) -{ - enum MoveTarget moveTarget = GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move); - - if (NoTargetPresent(gBattlerAttacker, gCurrentMove, moveTarget)) - { - gBattlescriptCurrInstr = BattleScript_ButItFailed; - return MOVE_STEP_FAILURE; - } - - if (ctx->battlerAtk == ctx->battlerDef - && moveTarget == TARGET_ALLY - && gProtectStructs[BATTLE_PARTNER(ctx->battlerAtk)].usedAllySwitch) - { - gBattlescriptCurrInstr = BattleScript_ButItFailed; - return MOVE_STEP_FAILURE; - } - - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler CancelerTookAttack(struct BattleContext *ctx) -{ - if (gSpecialStatuses[gBattlerTarget].abilityRedirected) - { - gSpecialStatuses[gBattlerTarget].abilityRedirected = FALSE; - BattleScriptCall(BattleScript_TookAttack); - return MOVE_STEP_BREAK; - } - return MOVE_STEP_SUCCESS; -} - -#define checkFailure FALSE -#define skipFailure TRUE -static bool32 IsSingleTarget(u32 battlerAtk, u32 battlerDef) -{ - if (battlerDef != gBattlerTarget) - return skipFailure; - return checkFailure; -} - -static bool32 IsSmartTarget(u32 battlerAtk, u32 battlerDef) -{ - if (battlerAtk == BATTLE_PARTNER(battlerDef)) - return skipFailure; - return checkFailure; -} - -static bool32 IsTargetingBothFoes(u32 battlerAtk, u32 battlerDef) -{ - if (battlerDef == BATTLE_PARTNER(battlerAtk) || battlerAtk == battlerDef) - { - // Because of Magic Bounce and Magic Coat we don't want to set MOVE_RESULT_NO_EFFECT - if (GetMoveCategory(gCurrentMove) != DAMAGE_CATEGORY_STATUS) - gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT; - return skipFailure; - } - return checkFailure; -} - -static bool32 IsTargetingSelf(u32 battlerAtk, u32 battlerDef) -{ - if (battlerAtk != battlerDef) - return skipFailure; - return skipFailure; // In Gen3 the user checks it's own failure. Unclear because no such moves exists -} - -static bool32 IsTargetingAlly(u32 battlerAtk, u32 battlerDef) -{ - if (battlerDef != BATTLE_PARTNER(battlerAtk)) - { - gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT; - return skipFailure; - } - return checkFailure; -} - -static bool32 IsTargetingSelfAndAlly(u32 battlerAtk, u32 battlerDef) -{ - if (battlerDef != BATTLE_PARTNER(battlerAtk)) - { - if (battlerDef != battlerAtk) // Don't set result flags for user - gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT; - return skipFailure; - } - return checkFailure; // In Gen3 the user checks it's own failure. Unclear because no such moves exists -} - -static bool32 IsTargetingSelfOrAlly(u32 battlerAtk, u32 battlerDef) -{ - if (battlerDef == battlerAtk) - return skipFailure; - - if (battlerDef != BATTLE_PARTNER(battlerAtk)) - { - gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT; - return skipFailure; - } - - return checkFailure; // In Gen3 the user checks it's own failure. Unclear because no such move exists -} - -static bool32 IsTargetingFoesAndAlly(u32 battlerAtk, u32 battlerDef) -{ - if (battlerAtk == battlerDef) - return skipFailure; // Don't set result flags for user - return checkFailure; -} - -static bool32 IsTargetingField(u32 battlerAtk, u32 battlerDef) -{ - return skipFailure; -} - -static bool32 IsTargetingOpponentsField(u32 battlerAtk, u32 battlerDef) -{ - return checkFailure; // Bounce failure only -} - -static bool32 IsTargetingAllBattlers(u32 battlerAtk, u32 battlerDef) -{ - if (GetConfig(CONFIG_CHECK_USER_FAILURE) >= GEN_5 && battlerAtk == battlerDef) - return skipFailure; - return checkFailure; -} - -// ShouldCheckFailureOnTarget -static bool32 (*const sShouldCheckTargetMoveFailure[])(u32 battlerAtk, u32 battlerDef) = -{ - [TARGET_NONE] = IsTargetingField, - [TARGET_SELECTED] = IsSingleTarget, - [TARGET_DEPENDS] = IsSingleTarget, - [TARGET_OPPONENT] = IsSingleTarget, - [TARGET_RANDOM] = IsSingleTarget, - [TARGET_BOTH] = IsTargetingBothFoes, - [TARGET_USER] = IsTargetingSelf, - [TARGET_SMART] = IsSmartTarget, - [TARGET_ALLY] = IsTargetingAlly, - [TARGET_USER_AND_ALLY] = IsTargetingSelfAndAlly, - [TARGET_USER_OR_ALLY] = IsTargetingSelfOrAlly, - [TARGET_FOES_AND_ALLY] = IsTargetingFoesAndAlly, - [TARGET_FIELD] = IsTargetingField, - [TARGET_OPPONENTS_FIELD] = IsTargetingOpponentsField, - [TARGET_ALL_BATTLERS] = IsTargetingAllBattlers, -}; - -static bool32 ShouldCheckTargetMoveFailure(u32 battlerAtk, u32 battlerDef, u32 move, u32 moveTarget) -{ - // For Bounced moves - if (IsBattlerUnaffectedByMove(battlerDef)) - return skipFailure; - - return sShouldCheckTargetMoveFailure[moveTarget](battlerAtk, battlerDef); -} -#undef checkFailure -#undef skipFailure - - -static enum MoveCanceler CancelerTargetFailure(struct BattleContext *ctx) -{ - bool32 targetAvoidedAttack = FALSE; - enum MoveTarget moveTarget = GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move); - s32 movePriority = GetChosenMovePriority(ctx->battlerAtk, ctx->abilityAtk); - ctx->moveType = GetBattleMoveType(ctx->move); - ctx->updateFlags = TRUE; - ctx->runScript = TRUE; - - while (gBattleStruct->eventState.atkCancelerBattler < gBattlersCount) - { - ctx->battlerDef = gBattleStruct->eventState.atkCancelerBattler++; - - if (ShouldCheckTargetMoveFailure(ctx->battlerAtk, ctx->battlerDef, ctx->move, moveTarget)) - continue; - - ctx->abilityDef = GetBattlerAbility(ctx->battlerDef); - ctx->holdEffectDef = GetBattlerHoldEffect(ctx->battlerDef); - - if (moveTarget == TARGET_OPPONENTS_FIELD) - { - if (CanBattlerBounceBackMove(ctx)) - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FAILED; - continue; - } - - if (!IsBattlerAlive(ctx->battlerDef)) - { - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FAILED; - continue; - } - else if (!BreaksThroughSemiInvulnerablity(ctx->battlerAtk, ctx->battlerDef, ctx->abilityAtk, ctx->abilityDef, ctx->move)) - { - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FAILED; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_AVOIDED_ATK; - if (GetMoveEffect(ctx->move) == EFFECT_FLING) - BattleScriptCall(BattleScript_TargetAvoidsAttackConsumeFlingItem); - else - BattleScriptCall(BattleScript_TargetAvoidsAttack); - targetAvoidedAttack = TRUE; - } - else if (IsBattlerProtected(ctx)) - { - SetOrClearRageVolatile(); - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED; - if (GetMoveEffect(ctx->move) == EFFECT_FLING) - BattleScriptCall(BattleScript_TargetAvoidsAttackConsumeFlingItem); - else - BattleScriptCall(BattleScript_TargetAvoidsAttack); - targetAvoidedAttack = TRUE; - } - else if (CanBattlerBounceBackMove(ctx)) - { - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FAILED; - } - else if (CanMoveBeBlockedByTarget(ctx, movePriority)) - { - - gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED; - targetAvoidedAttack = TRUE; - } - else if (GetMoveEffect(ctx->move) == EFFECT_SYNCHRONOISE && !DoBattlersShareType(ctx->battlerAtk, ctx->battlerDef)) - { - gBattleStruct->moveResultFlags[ctx->battlerDef] = MOVE_RESULT_NO_EFFECT; - BattleScriptCall(BattleScript_ItDoesntAffectFoe); - targetAvoidedAttack = TRUE; - } - else - { - CalcTypeEffectivenessMultiplier(ctx); - - if (ctx->abilityBlocked) - { - ctx->abilityBlocked = FALSE; - gBattleStruct->moveResultFlags[ctx->battlerDef] = MOVE_RESULT_FAILED; - gBattlerAbility = ctx->battlerDef; - RecordAbilityBattle(ctx->battlerDef, ctx->abilityDef); - BattleScriptCall(BattleScript_AbilityPopUp); - targetAvoidedAttack = TRUE; - } - else if (ctx->airBalloonBlocked) - { - ctx->airBalloonBlocked = FALSE; - gBattleStruct->moveResultFlags[ctx->battlerDef] = MOVE_RESULT_FAILED; - BattleScriptCall(BattleScript_DoesntAffectScripting); - targetAvoidedAttack = TRUE; - } - } - - if (targetAvoidedAttack) - { - gLastLandedMoves[gBattlerTarget] = 0; // Might need investigation on what exactly clears is - gLastHitByType[gBattlerTarget] = 0; - gBattleScripting.battler = ctx->battlerDef; - gBattleStruct->pledgeMove = FALSE; - CancelMultiTurnMoves(ctx->battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); - return MOVE_STEP_PAUSE; - } - } - - if (IsDoubleBattle()) - { - if (moveTarget == TARGET_BOTH) - gBattleStruct->numSpreadTargets = CountAliveMonsInBattle(BATTLE_ALIVE_EXCEPT_BATTLER_SIDE, gBattlerAttacker); - else if (moveTarget == TARGET_FOES_AND_ALLY) - gBattleStruct->numSpreadTargets = CountAliveMonsInBattle(BATTLE_ALIVE_EXCEPT_BATTLER, gBattlerAttacker); - } - - ctx->battlerDef = gBattlerTarget; - gBattleStruct->eventState.atkCancelerBattler = 0; - return MOVE_STEP_SUCCESS; -} - -static bool32 CantFullyProtectFromMove(u32 battlerDef) -{ - if (MoveIgnoresProtect(gCurrentMove)) - return FALSE; - if (!IsZMove(gCurrentMove) && !IsMaxMove(gCurrentMove)) - return FALSE; - return GetProtectType(gProtectStructs[battlerDef].protected) == PROTECT_TYPE_SINGLE - && gProtectStructs[battlerDef].protected != PROTECT_MAX_GUARD; -} - -static enum MoveCanceler CancelerNotFullyProtected(struct BattleContext *ctx) -{ - while (gBattleStruct->eventState.atkCancelerBattler < gBattlersCount) - { - u32 battlerDef = gBattleStruct->eventState.atkCancelerBattler++; - - if (CantFullyProtectFromMove(battlerDef)) - { - BattleScriptCall(BattleScript_CouldntFullyProtect); - gBattleScripting.battler = battlerDef; - return MOVE_STEP_PAUSE; - } - } - - gBattleStruct->eventState.atkCancelerBattler = 0; - return MOVE_STEP_SUCCESS; -} - -static bool32 IsMoveParentalBondAffected(struct BattleContext *ctx) -{ - if (ctx->abilityAtk != ABILITY_PARENTAL_BOND - || gBattleStruct->numSpreadTargets > 1 - || IsMoveParentalBondBanned(ctx->move) - || GetMoveCategory(ctx->move) == DAMAGE_CATEGORY_STATUS - || GetMoveEffect(ctx->move) == EFFECT_SEMI_INVULNERABLE - || GetMoveEffect(ctx->move) == EFFECT_TWO_TURNS_ATTACK - || GetActiveGimmick(ctx->battlerAtk) == GIMMICK_Z_MOVE - || ctx->move == MOVE_STRUGGLE) - return FALSE; - return TRUE; -} - -static void SetPossibleNewSmartTarget(u32 move) -{ - if (!IsBattlerUnaffectedByMove(gBattlerTarget) - || !CanTargetPartner(gBattlerAttacker, gBattlerTarget) - || IsAffectedByFollowMe(gBattlerAttacker, GetBattlerSide(gBattlerTarget), move) - || GetBattlerMoveTargetType(gBattlerAttacker, move) != TARGET_SMART) - return; - - u32 partner = BATTLE_PARTNER(gBattlerTarget); - if (!IsBattlerUnaffectedByMove(partner)) - gBattlerTarget = partner; -} - -static enum MoveCanceler CancelerMultihitMoves(struct BattleContext *ctx) -{ - SetPossibleNewSmartTarget(ctx->move); - - if (IsBattlerUnaffectedByMove(gBattlerTarget)) // Dragon Darts can still hit partner - { - gMultiHitCounter = 0; - } - else if (IsMultiHitMove(ctx->move)) - { - enum Ability ability = ctx->abilityAtk; - - if (ability == ABILITY_SKILL_LINK) - { - gMultiHitCounter = 5; - } - else if (GetMoveEffect(ctx->move) == EFFECT_SPECIES_POWER_OVERRIDE - && gBattleMons[ctx->battlerAtk].species == GetMoveSpeciesPowerOverride_Species(ctx->move)) - { - gMultiHitCounter = GetMoveSpeciesPowerOverride_NumOfHits(ctx->move); - } - else - { - SetRandomMultiHitCounter(); - } - - PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0) - } - else if (GetMoveStrikeCount(ctx->move) > 1) - { - if (GetMoveEffect(ctx->move) == EFFECT_POPULATION_BOMB && GetBattlerHoldEffect(ctx->battlerAtk) == HOLD_EFFECT_LOADED_DICE) - { - gMultiHitCounter = RandomUniform(RNG_LOADED_DICE, 4, 10); - } - else - { - gMultiHitCounter = GetMoveStrikeCount(ctx->move); - } - - PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 3, 0) - } - else if (GetMoveEffect(ctx->move) == EFFECT_BEAT_UP) - { - struct Pokemon* party = GetBattlerParty(ctx->battlerAtk); - int i; - gBattleStruct->beatUpSlot = 0; - gMultiHitCounter = 0; - memset(gBattleStruct->beatUpSpecies, 0xFF, sizeof(gBattleStruct->beatUpSpecies)); - - for (i = 0; i < PARTY_SIZE; i++) - { - u32 species = GetMonData(&party[i], MON_DATA_SPECIES); - if (species != SPECIES_NONE - && GetMonData(&party[i], MON_DATA_HP) - && !GetMonData(&party[i], MON_DATA_IS_EGG) - && !GetMonData(&party[i], MON_DATA_STATUS)) - { - if (GetConfig(CONFIG_BEAT_UP) >= GEN_5) - gBattleStruct->beatUpSpecies[gMultiHitCounter] = species; - else - gBattleStruct->beatUpSpecies[gMultiHitCounter] = i; - gMultiHitCounter++; - } - } - - PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0) - } - else if (IsMoveParentalBondAffected(ctx)) - { - gSpecialStatuses[gBattlerAttacker].parentalBondState = PARENTAL_BOND_1ST_HIT; - gMultiHitCounter = 2; - PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0) - } - else - { - gMultiHitCounter = 0; - } - - return MOVE_STEP_SUCCESS; -} - -static enum MoveCanceler (*const sMoveSuccessOrderCancelers[])(struct BattleContext *ctx) = -{ - [CANCELER_CLEAR_FLAGS] = CancelerClearFlags, - [CANCELER_STANCE_CHANGE_1] = CancelerStanceChangeOne, - [CANCELER_SKY_DROP] = CancelerSkyDrop, - [CANCELER_RECHARGE] = CancelerRecharge, - [CANCELER_CHILLY_RECEPTION] = CancelerChillyReception, - [CANCELER_ASLEEP_OR_FROZEN] = CancelerAsleepOrFrozen, - [CANCELER_OBEDIENCE] = CancelerObedience, - [CANCELER_POWER_POINTS] = CancelerPowerPoints, - [CANCELER_TRUANT] = CancelerTruant, - [CANCELER_FOCUS_GEN5] = CancelerFocusGen5, - [CANCELER_FLINCH] = CancelerFlinch, - [CANCELER_DISABLED] = CancelerDisabled, - [CANCELER_VOLATILE_BLOCKED] = CancelerVolatileBlocked, - [CANCELER_TAUNTED] = CancelerTaunted, - [CANCELER_IMPRISONED] = CancelerImprisoned, - [CANCELER_CONFUSED] = CancelerConfused, - [CANCELER_GHOST] = CancelerGhost, - [CANCELER_PARALYZED] = CancelerParalyzed, - [CANCELER_INFATUATION] = CancelerInfatuation, - [CANCELER_BIDE] = CancelerBide, - [CANCELER_Z_MOVES] = CancelerZMoves, - [CANCELER_CHOICE_LOCK] = CancelerChoiceLock, - [CANCELER_CALLSUBMOVE] = CancelerCallSubmove, - [CANCELER_THAW] = CancelerThaw, - [CANCELER_STANCE_CHANGE_2] = CancelerStanceChangeTwo, - [CANCELER_ATTACKSTRING] = CancelerAttackstring, - [CANCELER_PPDEDUCTION] = CancelerPPDeduction, - [CANCELER_SKY_BATTLE] = CancelerSkyBattle, - [CANCELER_WEATHER_PRIMAL] = CancelerWeatherPrimal, - [CANCELER_FOCUS_PRE_GEN5] = CancelerFocusPreGen5, - [CANCELER_MOVE_FAILURE] = CancelerMoveFailure, - [CANCELER_POWDER_STATUS] = CancelerPowderStatus, - [CANCELER_PRIORITY_BLOCK] = CancelerPriorityBlock, - [CANCELER_PROTEAN] = CancelerProtean, - [CANCELER_EXPLODING_DAMP] = CancelerExplodingDamp, - [CANCELER_EXPLOSION] = CancelerExplosion, - [CANCELER_CHARGING] = CancelerCharging, - [CANCELER_NO_TARGET] = CancelerNoTarget, - [CANCELER_TOOK_ATTACK] = CancelerTookAttack, - [CANCELER_TARGET_FAILURE] = CancelerTargetFailure, - [CANCELER_NOT_FULLY_PROTECTED] = CancelerNotFullyProtected, - [CANCELER_MULTIHIT_MOVES] = CancelerMultihitMoves, -}; - -enum MoveCanceler AtkCanceler_MoveSuccessOrder(void) -{ - enum MoveCanceler effect = MOVE_STEP_SUCCESS; - - struct BattleContext ctx = {0}; - ctx.battlerAtk = gBattlerAttacker; - ctx.battlerDef = gBattlerTarget; - ctx.move = gCurrentMove; - ctx.chosenMove = gChosenMove; - ctx.abilityAtk = GetBattlerAbility(ctx.battlerAtk); - ctx.holdEffectAtk = GetBattlerHoldEffect(ctx.battlerAtk); - - while (gBattleStruct->eventState.atkCanceler < CANCELER_END && effect == MOVE_STEP_SUCCESS) - { - effect = sMoveSuccessOrderCancelers[gBattleStruct->eventState.atkCanceler](&ctx); - gBattleStruct->unableToUseMove = (effect == MOVE_STEP_FAILURE); - if (effect != MOVE_STEP_PAUSE) - gBattleStruct->eventState.atkCanceler++; - } - - return effect; -} - -bool32 HasNoMonsToSwitch(u32 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2) +bool32 HasNoMonsToSwitch(enum BattlerId battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2) { u32 i, playerId, flankId; struct Pokemon *party; @@ -3763,7 +2168,7 @@ bool32 HasNoMonsToSwitch(u32 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2 } } -bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, enum Ability ability) +bool32 TryChangeBattleWeather(enum BattlerId battler, u32 battleWeatherId, enum Ability ability) { if (gBattleWeather & sBattleWeatherInfo[battleWeatherId].flag) return FALSE; @@ -3803,7 +2208,7 @@ bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, enum Ability abi gBattleCommunication[MULTISTRING_CHOOSER] = sBattleWeatherInfo[battleWeatherId].moveStartMessage; } - for (u32 i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { gBattleMons[i].volatiles.weatherAbilityDone = FALSE; ResetParadoxWeatherStat(i); @@ -3812,7 +2217,7 @@ bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, enum Ability abi return TRUE; } -bool32 TryChangeBattleTerrain(u32 battler, u32 statusFlag) +bool32 TryChangeBattleTerrain(enum BattlerId battler, u32 statusFlag) { if (gBattleStruct->isSkyBattle) return FALSE; @@ -3821,7 +2226,7 @@ bool32 TryChangeBattleTerrain(u32 battler, u32 statusFlag) { gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY; gFieldStatuses |= statusFlag; - for (u32 i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { gBattleMons[i].volatiles.terrainAbilityDone = FALSE; ResetParadoxTerrainStat(i); @@ -3837,10 +2242,10 @@ bool32 TryChangeBattleTerrain(u32 battler, u32 statusFlag) return FALSE; } -static void ForewarnChooseMove(u32 battler) +static void ForewarnChooseMove(enum BattlerId battler) { struct Forewarn { - u8 battler; + enum BattlerId battler; u8 power; u16 moveId; }; @@ -3861,7 +2266,6 @@ static void ForewarnChooseMove(u32 battler) switch (GetMoveEffect(data[count].moveId)) { case EFFECT_OHKO: - case EFFECT_SHEER_COLD: data[count].power = 150; break; case EFFECT_REFLECT_DAMAGE: @@ -3882,12 +2286,43 @@ static void ForewarnChooseMove(u32 battler) } } - for (bestId = 0, i = 1; i < count; i++) + if (count == 0) { - if (data[i].power > data[bestId].power) - bestId = i; - else if (data[i].power == data[bestId].power && Random() & 1) + Free(data); + return; + } + + u32 tieCount = 1; + u8 bestPower = data[0].power; + + bestId = 0; + for (i = 1; i < count; i++) + { + if (data[i].power > bestPower) + { + bestPower = data[i].power; bestId = i; + tieCount = 1; + } + else if (data[i].power == bestPower) + { + tieCount++; + } + } + + if (tieCount > 1) + { + u32 tieIndex = RandomUniform(RNG_FOREWARN, 0, tieCount - 1); + for (i = 0, bestId = 0; i < count; i++) + { + if (data[i].power != bestPower) + continue; + if (tieIndex-- == 0) + { + bestId = i; + break; + } + } } gEffectBattler = data[bestId].battler; @@ -3897,7 +2332,7 @@ static void ForewarnChooseMove(u32 battler) Free(data); } -bool32 ChangeTypeBasedOnTerrain(u32 battler) +bool32 ChangeTypeBasedOnTerrain(enum BattlerId battler) { enum Type battlerType; @@ -3922,29 +2357,29 @@ static inline u8 GetSideFaintCounter(enum BattleSide side) return (side == B_SIDE_PLAYER) ? gBattleResults.playerFaintCounter : gBattleResults.opponentFaintCounter; } -static inline u8 GetBattlerSideFaintCounter(u32 battler) +static inline u8 GetBattlerSideFaintCounter(enum BattlerId battler) { return GetSideFaintCounter(GetBattlerSide(battler)); } // Supreme Overlord adds a x0.1 damage boost for each fainted ally. -static inline uq4_12_t GetSupremeOverlordModifier(u32 battler) +static inline uq4_12_t GetSupremeOverlordModifier(enum BattlerId battler) { return UQ_4_12(1.0) + (PercentToUQ4_12(gBattleStruct->supremeOverlordCounter[battler] * 10)); } -bool32 HadMoreThanHalfHpNowDoesnt(u32 battler) +bool32 HadMoreThanHalfHpNowDoesnt(enum BattlerId battler) { // Had more than half of hp before, now has less return gBattleStruct->battlerState[battler].wasAboveHalfHp && gBattleMons[battler].hp <= gBattleMons[battler].maxHP / 2; } -u32 NumFaintedBattlersByAttacker(u32 battlerAtk) +u32 NumFaintedBattlersByAttacker(enum BattlerId battlerAtk) { - u32 battler, numMonsFainted = 0; + u32 numMonsFainted = 0; - for (battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { if (battler == battlerAtk) continue; @@ -3964,7 +2399,7 @@ u32 NumFaintedBattlersByAttacker(u32 battlerAtk) #define ANIM_STAT_SPEED 5 #define ANIM_STAT_ACC 6 #define ANIM_STAT_EVASION 7 -void ChooseStatBoostAnimation(u32 battler) +void ChooseStatBoostAnimation(enum BattlerId battler) { u32 stat; bool32 statBuffMoreThan1 = FALSE; @@ -4138,7 +2573,7 @@ bool32 CanAbilityAbsorbMove(struct BattleContext *ctx) return TRUE; } -const u8 *AbsorbedByDrainHpAbility(u32 battlerDef) +const u8 *AbsorbedByDrainHpAbility(enum BattlerId battlerDef) { if (IsBattlerAtMaxHp(battlerDef) || (B_HEAL_BLOCKING >= GEN_5 && gBattleMons[battlerDef].volatiles.healBlock)) { @@ -4151,7 +2586,7 @@ const u8 *AbsorbedByDrainHpAbility(u32 battlerDef) } } -const u8 *AbsorbedByStatIncreaseAbility(u32 battlerDef, enum Ability abilityDef, enum Stat statId, u32 statAmount) +const u8 *AbsorbedByStatIncreaseAbility(enum BattlerId battlerDef, enum Ability abilityDef, enum Stat statId, u32 statAmount) { if (!CompareStat(battlerDef, statId, MAX_STAT_STAGE, CMP_LESS_THAN, abilityDef)) { @@ -4164,7 +2599,7 @@ const u8 *AbsorbedByStatIncreaseAbility(u32 battlerDef, enum Ability abilityDef, } } -const u8 *AbsorbedByFlashFire(u32 battlerDef) +const u8 *AbsorbedByFlashFire(enum BattlerId battlerDef) { if (!gBattleMons[battlerDef].volatiles.flashFireBoosted) { @@ -4179,48 +2614,9 @@ const u8 *AbsorbedByFlashFire(u32 battlerDef) } } -static bool32 CanBattlerBounceBackMove(struct BattleContext *ctx) -{ - return TryMagicBounce(ctx) || TryMagicCoat(ctx); -} - -static bool32 TryMagicBounce(struct BattleContext *ctx) -{ - if (!MoveCanBeBouncedBack(ctx->move)) - return FALSE; - - if (gBattleStruct->magicBounceActive || gBattleStruct->bouncedMoveIsUsed) - return FALSE; - - if (ctx->abilityDef != ABILITY_MAGIC_BOUNCE) - return FALSE; - - gBattleStruct->magicBounceActive = TRUE; - gBattleStruct->moveBouncer = ctx->battlerDef; - - return TRUE; -} - -static bool32 TryMagicCoat(struct BattleContext *ctx) -{ - if (!MoveCanBeBouncedBack(ctx->move) || gBattleStruct->magicBounceActive) // Magic Bounce has precedence over magic coat - return FALSE; - - if (gBattleStruct->magicCoatActive || gBattleStruct->bouncedMoveIsUsed) - return FALSE; - - if (!gProtectStructs[ctx->battlerDef].bounceMove) - return FALSE; - - gBattleStruct->magicCoatActive = TRUE; - gBattleStruct->moveBouncer = ctx->battlerDef; - - return TRUE; -} - static u32 GetFirstBattlerOnSide(enum BattleSide side) { - for (u32 battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { if (GetBattlerSide(battler) == side && !IsBattlerAlive(battler)) return battler; @@ -4250,7 +2646,7 @@ static inline bool32 SetStartingSideStatus(u32 flag, enum BattleSide side, u32 m { if (!(gSideStatuses[side] & flag)) { - gBattlerAttacker = gBattlerTarget = side; + gBattlerAttacker = gBattlerTarget = (enum BattlerId)side; gBattleCommunication[MULTISTRING_CHOOSER] = message; gSideStatuses[side] |= flag; gBattleScripting.animArg1 = anim; @@ -4718,13 +3114,14 @@ bool32 TryFieldEffects(enum FieldEffectCases caseId) return effect; } -u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ability, enum Move move, bool32 shouldAbilityTrigger) +u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum Ability ability, enum Move move, bool32 shouldAbilityTrigger) { u32 effect = 0; enum Type moveType = 0; u32 side = 0; u32 i = 0, j = 0; u32 partner = 0; + u32 speciesForm = SPECIES_NONE; if (gBattleTypeFlags & (BATTLE_TYPE_SAFARI | BATTLE_TYPE_CATCH_TUTORIAL)) return 0; @@ -4750,9 +3147,9 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab { case ABILITY_TRACE: { - u32 chosenTarget; - u32 target1; - u32 target2; + enum BattlerId chosenTarget; + enum BattlerId target1; + enum BattlerId target2; if (GetBattlerHoldEffectIgnoreAbility(battler) == HOLD_EFFECT_ABILITY_SHIELD) break; @@ -4789,7 +3186,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab case ABILITY_IMPOSTER: if (gBattleStruct->battlerState[battler].switchIn) { - u32 diagonalBattler = BATTLE_OPPOSITE(battler); + enum BattlerId diagonalBattler = BATTLE_OPPOSITE(battler); if (IsDoubleBattle()) diagonalBattler = BATTLE_PARTNER(diagonalBattler); @@ -4888,9 +3285,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab ctx.isAnticipation = TRUE; modifier = CalcTypeEffectivenessMultiplier(&ctx); - if (modifier >= UQ_4_12(2.0) - || moveEffect == EFFECT_OHKO - || moveEffect == EFFECT_SHEER_COLD) + if (modifier >= UQ_4_12(2.0) || moveEffect == EFFECT_OHKO) { effect++; break; @@ -4914,7 +3309,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab } return effect; // Note: It returns effect as to not record the ability if Frisk does not activate. case ABILITY_FOREWARN: - if (shouldAbilityTrigger) + if (shouldAbilityTrigger && !IsOpposingSideEmpty(battler)) { ForewarnChooseMove(battler); gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_FOREWARN; @@ -4926,7 +3321,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab if (shouldAbilityTrigger) { enum Stat statId; - u32 opposingBattler; + enum BattlerId opposingBattler; u32 opposingDef = 0, opposingSpDef = 0; opposingBattler = BATTLE_OPPOSITE(battler); @@ -5141,18 +3536,6 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab effect++; } break; - case ABILITY_SCHOOLING: - if (gBattleMons[battler].level < 20) - break; - // Fallthrough - case ABILITY_ZEN_MODE: - case ABILITY_SHIELDS_DOWN: - if (TryBattleFormChange(battler, FORM_CHANGE_BATTLE_HP_PERCENT)) - { - BattleScriptCall(BattleScript_BattlerFormChange); - effect++; - } - break; case ABILITY_INTREPID_SWORD: if (shouldAbilityTrigger && !GetBattlerPartyState(battler)->intrepidSwordBoost) { @@ -5303,6 +3686,16 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab break; } break; + case ABILITYEFFECT_SWITCH_IN_FORM_CHANGE: + if (TryBattleFormChange(battler, FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, ability)) + { + gBattleScripting.battler = battler; + // To prevent the new form's ability from pop up + gBattleScripting.abilityPopupOverwrite = ability; + BattleScriptCall(BattleScript_BattlerFormChange); + effect++; + } + break; case ABILITYEFFECT_ENDTURN: if (IsBattlerAlive(battler)) { @@ -5311,7 +3704,6 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab { case ABILITY_PICKUP: if (gBattleMons[battler].item == ITEM_NONE - && gBattleStruct->changedItems[battler] == ITEM_NONE // Will not inherit an item && PickupHasValidTarget(battler)) { gBattlerTarget = RandomUniformExcept(RNG_PICKUP, 0, gBattlersCount - 1, CantPickupItem); @@ -5323,7 +3715,6 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab case ABILITY_HARVEST: if ((IsBattlerWeatherAffected(battler, B_WEATHER_SUN) || RandomPercentage(RNG_HARVEST, 50)) && gBattleMons[battler].item == ITEM_NONE - && gBattleStruct->changedItems[battler] == ITEM_NONE // Will not inherit an item && GetItemPocket(GetBattlerPartyState(battler)->usedHeldItem) == POCKET_BERRIES) { gLastUsedItem = GetBattlerPartyState(battler)->usedHeldItem; @@ -5468,27 +3859,6 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab effect++; } break; - case ABILITY_SCHOOLING: - if (gBattleMons[battler].level < 20) - break; - // Fallthrough - case ABILITY_ZEN_MODE: - case ABILITY_SHIELDS_DOWN: - if (TryBattleFormChange(battler, FORM_CHANGE_BATTLE_HP_PERCENT)) - { - gBattleScripting.battler = battler; - BattleScriptExecute(BattleScript_BattlerFormChangeEnd2); - effect++; - } - break; - case ABILITY_POWER_CONSTRUCT: - if (TryBattleFormChange(battler, FORM_CHANGE_BATTLE_HP_PERCENT)) - { - gBattleScripting.battler = battler; - BattleScriptExecute(BattleScript_PowerConstruct); - effect++; - } - break; case ABILITY_BALL_FETCH: if (!(gBattleTypeFlags & BATTLE_TYPE_RAID) && gBattleMons[battler].item == ITEM_NONE @@ -5505,16 +3875,6 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab effect++; } break; - case ABILITY_HUNGER_SWITCH: - if (!gBattleMons[battler].volatiles.transformed - && GetActiveGimmick(battler) != GIMMICK_TERA - && TryBattleFormChange(battler, FORM_CHANGE_BATTLE_TURN_END)) - { - gBattleScripting.battler = battler; - BattleScriptExecute(BattleScript_BattlerFormChangeEnd3NoPopup); - effect++; - } - break; case ABILITY_CUD_CHEW: if (gBattleMons[battler].volatiles.cudChew == TRUE) { @@ -5914,11 +4274,10 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab } break; case ABILITY_COTTON_DOWN: - if (IsBattlerAlive(gBattlerAttacker) - && !gBattleStruct->unableToUseMove - && IsBattlerTurnDamaged(gBattlerTarget)) + if (IsBattlerTurnDamaged(gBattlerTarget)) { gEffectBattler = gBattlerTarget; + // Will ability popup activate if there is only one target? BattleScriptCall(BattleScript_CottonDownActivates); effect++; } @@ -5971,30 +4330,6 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab effect++; } break; - case ABILITY_GULP_MISSILE: - if (!gBattleStruct->unableToUseMove - && IsBattlerTurnDamaged(gBattlerTarget) - && IsBattlerAlive(gBattlerAttacker) - && gBattleMons[gBattlerTarget].species != SPECIES_CRAMORANT) - { - if (!IsAbilityAndRecord(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), ABILITY_MAGIC_GUARD)) - SetPassiveDamageAmount(gBattlerAttacker, GetNonDynamaxMaxHP(gBattlerAttacker) / 4); - - switch (gBattleMons[gBattlerTarget].species) - { - case SPECIES_CRAMORANT_GORGING: - TryBattleFormChange(battler, FORM_CHANGE_HIT_BY_MOVE); - BattleScriptCall(BattleScript_GulpMissileGorging); - effect++; - break; - case SPECIES_CRAMORANT_GULPING: - TryBattleFormChange(battler, FORM_CHANGE_HIT_BY_MOVE); - BattleScriptCall(BattleScript_GulpMissileGulping); - effect++; - break; - } - } - break; case ABILITY_SEED_SOWER: if (!gBattleStruct->unableToUseMove && IsBattlerTurnDamaged(gBattlerTarget) @@ -6091,16 +4426,6 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab effect++; } break; - case ABILITY_GULP_MISSILE: - if ((gBattleMons[gBattlerAttacker].species == SPECIES_CRAMORANT) - && ((gCurrentMove == MOVE_SURF && IsBattlerTurnDamaged(gBattlerTarget)) || gBattleMons[gBattlerAttacker].volatiles.semiInvulnerable == STATE_UNDERWATER) - && TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_HP_PERCENT)) - { - gBattleScripting.battler = gBattlerAttacker; - BattleScriptCall(BattleScript_BattlerFormChange); - effect++; - } - break; case ABILITY_POISON_PUPPETEER: if (gBattleMons[gBattlerAttacker].species == SPECIES_PECHARUNT && gBattleStruct->poisonPuppeteerConfusion == TRUE @@ -6117,6 +4442,53 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab break; } break; + case ABILITYEFFECT_FORM_CHANGE_ON_HIT: + speciesForm = gBattleMons[gBattlerTarget].species; + + if (gBattleStruct->unableToUseMove + || !IsBattlerTurnDamaged(gBattlerTarget) + || !TryBattleFormChange(gBattlerTarget, FORM_CHANGE_BATTLE_HIT_BY_MOVE_CATEGORY, ability)) + break; + + gBattleScripting.abilityPopupOverwrite = ability; + gBattleScripting.battler = battler; + effect++; + + switch (ability) + { + case ABILITY_GULP_MISSILE: + if (!IsBattlerAlive(gBattlerAttacker)) + break; + + if (!IsAbilityAndRecord(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), ABILITY_MAGIC_GUARD)) + SetPassiveDamageAmount(gBattlerAttacker, GetNonDynamaxMaxHP(gBattlerAttacker) / 4); + + switch (speciesForm) + { + case SPECIES_CRAMORANT_GORGING: + BattleScriptCall(BattleScript_GulpMissileGorging); + break; + case SPECIES_CRAMORANT_GULPING: + BattleScriptCall(BattleScript_GulpMissileGulping); + break; + default: + BattleScriptCall(BattleScript_BattlerFormChange); // Fallback + break; + } + break; + case ABILITY_DISGUISE: + if (GetConfig(CONFIG_DISGUISE_HP_LOSS) >= GEN_8 && ability == ABILITY_DISGUISE) + SetPassiveDamageAmount(gBattlerTarget, GetNonDynamaxMaxHP(gBattlerTarget) / 8); + BattleScriptCall(BattleScript_BattlerFormChangeDisguise); + break; + case ABILITY_ICE_FACE: + BattleScriptCall(BattleScript_IceFaceNullsDamage); + break; + default: + BattleScriptCall(BattleScript_BattlerFormChange); + break; + } + break; case ABILITYEFFECT_MOVE_END_OTHER: // Abilities that activate on *another* battler's moveend: Dancer, Soul-Heart, Receiver, Symbiosis switch (GetBattlerAbility(battler)) { @@ -6159,7 +4531,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab u32 numMagicianTargets = 0; u32 magicianTargets = 0; - for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) + for (enum BattlerId battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) { if (gBattleMons[battlerDef].item != ITEM_NONE && battlerDef != battler @@ -6180,13 +4552,13 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab break; } - u8 battlers[4] = {0, 1, 2, 3}; + enum BattlerId battlers[MAX_BATTLERS_COUNT] = {0, 1, 2, 3}; if (numMagicianTargets > 1) SortBattlersBySpeed(battlers, FALSE); for (u32 i = 0; i < gBattlersCount; i++) { - u32 targetBattler = battlers[i]; + enum BattlerId targetBattler = battlers[i]; if (!(magicianTargets & 1u << targetBattler)) continue; @@ -6246,7 +4618,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab if (GetConfig(CONFIG_BATTLE_BOND) < GEN_9) { - // TODO: Convert this to a proper FORM_CHANGE type. + // Can't use TryBattleFormChange as we can't test form change const data changes. gLastUsedAbility = ability; GetBattlerPartyState(battler)->battleBondBoost = TRUE; PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[battler].species); @@ -6378,12 +4750,10 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab } break; case ABILITYEFFECT_TERA_SHIFT: - if (ability == ABILITY_TERA_SHIFT - && gBattleMons[battler].species == SPECIES_TERAPAGOS_NORMAL - && TryBattleFormChange(battler, FORM_CHANGE_BATTLE_SWITCH)) + if (TryBattleFormChange(battler, FORM_CHANGE_BATTLE_SWITCH_IN, ability)) { gBattleScripting.battler = battler; - gBattleScripting.abilityPopupOverwrite = gLastUsedAbility = ABILITY_TERA_SHIFT; + gBattleScripting.abilityPopupOverwrite = gLastUsedAbility = ability; BattleScriptCall(BattleScript_BattlerFormChangeWithString); effect++; } @@ -6438,6 +4808,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab && IsBattlerAlive(battler) && gBattleStruct->battlerState[partner].commanderSpecies == SPECIES_NONE && gBattleMons[partner].species == SPECIES_DONDOZO + && (gChosenActionByBattler[partner] != B_ACTION_SWITCH || HasBattlerActedThisTurn(partner)) && GET_BASE_SPECIES_ID(GetMonData(GetBattlerMon(battler), MON_DATA_SPECIES)) == SPECIES_TATSUGIRI) { SaveBattlerAttacker(gBattlerAttacker); @@ -6497,7 +4868,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab case ABILITY_FLOWER_GIFT: case ABILITY_ICE_FACE: { - u32 battlerWeatherAffected = IsBattlerWeatherAffected(battler, gBattleWeather); + bool32 battlerWeatherAffected = IsBattlerWeatherAffected(battler, gBattleWeather); if (battlerWeatherAffected && !CanBattlerFormChange(battler, FORM_CHANGE_BATTLE_WEATHER)) { // If Hail/Snow activates when in Eiscue is in base, prevent reversion when Eiscue Noice gets broken @@ -6507,7 +4878,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab if (((!gBattleMons[battler].volatiles.weatherAbilityDone && battlerWeatherAffected) || gBattleWeather == B_WEATHER_NONE || !HasWeatherEffect()) // Air Lock active - && TryBattleFormChange(battler, FORM_CHANGE_BATTLE_WEATHER)) + && TryBattleFormChange(battler, FORM_CHANGE_BATTLE_WEATHER, gLastUsedAbility)) { gBattleScripting.battler = battler; gBattleMons[battler].volatiles.weatherAbilityDone = TRUE; @@ -6579,10 +4950,9 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab return effect; } -bool32 TryPrimalReversion(u32 battler) +bool32 TryPrimalReversion(enum BattlerId battler) { - if (GetBattlerHoldEffectIgnoreNegation(battler) == HOLD_EFFECT_PRIMAL_ORB - && GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_PRIMAL_REVERSION) != gBattleMons[battler].species) + if (TryBattleFormChange(battler, FORM_CHANGE_BATTLE_PRIMAL_REVERSION, GetBattlerAbility(battler))) { gBattleScripting.battler = battler; BattleScriptCall(BattleScript_PrimalReversion); @@ -6593,9 +4963,7 @@ bool32 TryPrimalReversion(u32 battler) bool32 IsNeutralizingGasOnField(void) { - u32 i; - - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].volatiles.neutralizingGas && !gBattleMons[i].volatiles.gastroAcid) return TRUE; @@ -6604,7 +4972,7 @@ bool32 IsNeutralizingGasOnField(void) return FALSE; } -bool32 IsMoldBreakerTypeAbility(u32 battler, enum Ability ability) +bool32 IsMoldBreakerTypeAbility(enum BattlerId battler, enum Ability ability) { if (gBattleMons[battler].volatiles.gastroAcid) return FALSE; @@ -6621,29 +4989,29 @@ bool32 IsMoldBreakerTypeAbility(u32 battler, enum Ability ability) return FALSE; } -static inline bool32 CanBreakThroughAbility(u32 battlerAtk, u32 battlerDef, bool32 hasAbilityShield, bool32 ignoreMoldBreaker) +static inline bool32 CanBreakThroughAbility(enum BattlerId battlerAtk, enum BattlerId battlerDef, bool32 hasAbilityShield, bool32 ignoreMoldBreaker) { if (hasAbilityShield || ignoreMoldBreaker || battlerDef == battlerAtk) return FALSE; return gBattleStruct->moldBreakerActive && gAbilitiesInfo[gBattleMons[battlerDef].ability].breakable; } -enum Ability GetBattlerAbilityNoAbilityShield(u32 battler) +enum Ability GetBattlerAbilityNoAbilityShield(enum BattlerId battler) { return GetBattlerAbilityInternal(battler, FALSE, TRUE); } -enum Ability GetBattlerAbilityIgnoreMoldBreaker(u32 battler) +enum Ability GetBattlerAbilityIgnoreMoldBreaker(enum BattlerId battler) { return GetBattlerAbilityInternal(battler, TRUE, FALSE); } -enum Ability GetBattlerAbility(u32 battler) +enum Ability GetBattlerAbility(enum BattlerId battler) { return GetBattlerAbilityInternal(battler, FALSE, FALSE); } -enum Ability GetBattlerAbilityInternal(u32 battler, bool32 ignoreMoldBreaker, bool32 noAbilityShield) +enum Ability GetBattlerAbilityInternal(enum BattlerId battler, bool32 ignoreMoldBreaker, bool32 noAbilityShield) { bool32 hasAbilityShield = !noAbilityShield && GetBattlerHoldEffectIgnoreAbility(battler) == HOLD_EFFECT_ABILITY_SHIELD; bool32 abilityCantBeSuppressed = gAbilitiesInfo[gBattleMons[battler].ability].cantBeSuppressed; @@ -6676,7 +5044,7 @@ enum Ability GetBattlerAbilityInternal(u32 battler, bool32 ignoreMoldBreaker, bo return gBattleMons[battler].ability; } -u32 IsAbilityOnSide(u32 battler, enum Ability ability) +u32 IsAbilityOnSide(enum BattlerId battler, enum Ability ability) { if (IsBattlerAlive(battler) && GetBattlerAbility(battler) == ability) return battler + 1; @@ -6686,16 +5054,14 @@ u32 IsAbilityOnSide(u32 battler, enum Ability ability) return 0; } -u32 IsAbilityOnOpposingSide(u32 battler, enum Ability ability) +u32 IsAbilityOnOpposingSide(enum BattlerId battler, enum Ability ability) { return IsAbilityOnSide(BATTLE_OPPOSITE(battler), ability); } u32 IsAbilityOnField(enum Ability ability) { - u32 i; - - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (IsBattlerAlive(i) && GetBattlerAbility(i) == ability) return i + 1; @@ -6704,11 +5070,9 @@ u32 IsAbilityOnField(enum Ability ability) return 0; } -u32 IsAbilityOnFieldExcept(u32 battler, enum Ability ability) +u32 IsAbilityOnFieldExcept(enum BattlerId battler, enum Ability ability) { - u32 i; - - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (i != battler && IsBattlerAlive(i) && GetBattlerAbility(i) == ability) return i + 1; @@ -6717,13 +5081,13 @@ u32 IsAbilityOnFieldExcept(u32 battler, enum Ability ability) return 0; } -u32 IsAbilityPreventingEscape(u32 battler) +u32 IsAbilityPreventingEscape(enum BattlerId battler) { if (GetConfig(CONFIG_GHOSTS_ESCAPE) >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_GHOST)) return 0; bool32 isBattlerGrounded = IsBattlerGrounded(battler, GetBattlerAbility(battler), GetBattlerHoldEffect(battler)); - for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) + for (enum BattlerId battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) { if (battler == battlerDef || IsBattlerAlly(battler, battlerDef)) continue; @@ -6743,7 +5107,7 @@ u32 IsAbilityPreventingEscape(u32 battler) return 0; } -bool32 CanBattlerEscape(u32 battler) // no ability check +bool32 CanBattlerEscape(enum BattlerId battler) // no ability check { if (gBattleStruct->battlerState[battler].commanderSpecies != SPECIES_NONE) return FALSE; @@ -6763,47 +5127,32 @@ bool32 CanBattlerEscape(u32 battler) // no ability check return TRUE; } -void BattleScriptExecute(const u8 *BS_ptr) -{ - gBattlescriptCurrInstr = BS_ptr; - gBattleResources->battleCallbackStack->function[gBattleResources->battleCallbackStack->size++] = gBattleMainFunc; - gBattleMainFunc = RunBattleScriptCommands_PopCallbacksStack; - gCurrentActionFuncId = 0; -} - -void BattleScriptPushCursorAndCallback(const u8 *BS_ptr) -{ - BattleScriptCall(BS_ptr); - gBattleResources->battleCallbackStack->function[gBattleResources->battleCallbackStack->size++] = gBattleMainFunc; - gBattleMainFunc = RunBattleScriptCommands; -} - -bool32 IsPsychicTerrainAffected(u32 battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses) +bool32 IsPsychicTerrainAffected(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses) { return IsBattlerTerrainAffected(battler, ability, holdEffect, fieldStatuses, STATUS_FIELD_PSYCHIC_TERRAIN); } -bool32 IsMistyTerrainAffected(u32 battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses) +bool32 IsMistyTerrainAffected(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses) { return IsBattlerTerrainAffected(battler, ability, holdEffect, fieldStatuses, STATUS_FIELD_MISTY_TERRAIN); } -bool32 IsGrassyTerrainAffected(u32 battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses) +bool32 IsGrassyTerrainAffected(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses) { return IsBattlerTerrainAffected(battler, ability, holdEffect, fieldStatuses, STATUS_FIELD_GRASSY_TERRAIN); } -bool32 IsElectricTerrainAffected(u32 battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses) +bool32 IsElectricTerrainAffected(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses) { return IsBattlerTerrainAffected(battler, ability, holdEffect, fieldStatuses, STATUS_FIELD_ELECTRIC_TERRAIN); } -bool32 IsAnyTerrainAffected(u32 battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses) +bool32 IsAnyTerrainAffected(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatuses) { return IsBattlerTerrainAffected(battler, ability, holdEffect, fieldStatuses, STATUS_FIELD_TERRAIN_ANY); } -bool32 IsBattlerTerrainAffected(u32 battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatus, u32 terrainFlag) +bool32 IsBattlerTerrainAffected(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect, u32 fieldStatus, u32 terrainFlag) { if (!(fieldStatus & terrainFlag)) return FALSE; @@ -6813,7 +5162,7 @@ bool32 IsBattlerTerrainAffected(u32 battler, enum Ability ability, enum HoldEffe return IsBattlerGrounded(battler, ability, holdEffect); } -enum Stat GetHighestStatId(u32 battler) +enum Stat GetHighestStatId(enum BattlerId battler) { enum Stat highestId = STAT_ATK; bool32 wonderRoom = (gFieldStatuses & STATUS_FIELD_WONDER_ROOM) != 0; @@ -6856,7 +5205,7 @@ enum Stat GetHighestStatId(u32 battler) return highestId; } -static u32 GetStatValueWithStages(u32 battler, enum Stat stat) +static u32 GetStatValueWithStages(enum BattlerId battler, enum Stat stat) { u32 statValue; @@ -6887,7 +5236,7 @@ static u32 GetStatValueWithStages(u32 battler, enum Stat stat) return statValue; } -enum Stat GetParadoxHighestStatId(u32 battler) +enum Stat GetParadoxHighestStatId(enum BattlerId battler) { enum Stat highestId = STAT_ATK; u32 highestStat = GetStatValueWithStages(battler, STAT_ATK); @@ -6912,21 +5261,21 @@ enum Stat GetParadoxHighestStatId(u32 battler) return highestId; } -static void ResetParadoxWeatherStat(u32 battler) +static void ResetParadoxWeatherStat(enum BattlerId battler) { if (gBattleMons[battler].ability == ABILITY_PROTOSYNTHESIS && !gBattleMons[battler].volatiles.boosterEnergyActivated) gBattleMons[battler].volatiles.paradoxBoostedStat = 0; } -static void ResetParadoxTerrainStat(u32 battler) +static void ResetParadoxTerrainStat(enum BattlerId battler) { if (gBattleMons[battler].ability == ABILITY_QUARK_DRIVE && !gBattleMons[battler].volatiles.boosterEnergyActivated) gBattleMons[battler].volatiles.paradoxBoostedStat = 0; } -enum Stat GetParadoxBoostedStatId(u32 battler) +enum Stat GetParadoxBoostedStatId(enum BattlerId battler) { if (gBattleMons[battler].volatiles.paradoxBoostedStat == 0) gBattleMons[battler].volatiles.paradoxBoostedStat = GetParadoxHighestStatId(battler); @@ -6934,7 +5283,7 @@ enum Stat GetParadoxBoostedStatId(u32 battler) return gBattleMons[battler].volatiles.paradoxBoostedStat; } -bool32 CanBeSlept(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef, enum SleepClauseBlock isBlockedBySleepClause) +bool32 CanBeSlept(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityDef, enum SleepClauseBlock isBlockedBySleepClause) { if (IsSleepClauseActiveForSide(GetBattlerSide(battlerDef)) && isBlockedBySleepClause != NOT_BLOCKED_BY_SLEEP_CLAUSE) return FALSE; @@ -6956,7 +5305,7 @@ bool32 CanBeSlept(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef, enum return effect; } -bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum Ability abilityDef) +bool32 CanBePoisoned(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum Ability abilityDef) { if (CanSetNonVolatileStatus( battlerAtk, @@ -6970,7 +5319,7 @@ bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, en } // TODO: check order of battlerAtk and battlerDef -bool32 CanBeBurned(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef) +bool32 CanBeBurned(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityDef) { if (CanSetNonVolatileStatus( battlerAtk, @@ -6983,7 +5332,7 @@ bool32 CanBeBurned(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef) return FALSE; } -bool32 CanBeParalyzed(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef) +bool32 CanBeParalyzed(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityDef) { if (CanSetNonVolatileStatus( battlerAtk, @@ -6996,7 +5345,7 @@ bool32 CanBeParalyzed(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef) return FALSE; } -bool32 CanBeFrozen(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef) +bool32 CanBeFrozen(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityDef) { if (CanSetNonVolatileStatus( battlerAtk, @@ -7009,7 +5358,7 @@ bool32 CanBeFrozen(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef) return FALSE; } // Unused, technically also redundant because it is just a copy of CanBeFrozen -bool32 CanGetFrostbite(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef) +bool32 CanGetFrostbite(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityDef) { if (CanSetNonVolatileStatus( battlerAtk, @@ -7022,7 +5371,7 @@ bool32 CanGetFrostbite(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef) return FALSE; } -bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum MoveEffect effect, enum ResultOption option) +bool32 CanSetNonVolatileStatus(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum MoveEffect effect, enum ResultOption option) { const u8 *battleScript = NULL; u32 sideBattler = 0; @@ -7189,7 +5538,7 @@ bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, enum Ability abil return TRUE; } -static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, enum Ability abilityDef, bool32 abilityAffected, const u8 *battleScript, enum ResultOption option) +static bool32 IsNonVolatileStatusBlocked(enum BattlerId battlerDef, enum Ability abilityDef, bool32 abilityAffected, const u8 *battleScript, enum ResultOption option) { if (battleScript != NULL) { @@ -7214,7 +5563,7 @@ static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, enum Ability abilityDef return FALSE; } -static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum ResultOption option) +static bool32 CanSleepDueToSleepClause(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum ResultOption option) { // Can freely sleep own partner if (IsDoubleBattle() && IsSleepClauseEnabled() && IsBattlerAlly(battlerAtk, battlerDef)) @@ -7233,7 +5582,7 @@ static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum Resu return FALSE; } -bool32 CanBeConfused(u32 battler) +bool32 CanBeConfused(enum BattlerId battler) { enum Ability ability = GetBattlerAbility(battler); if (gBattleMons[battler].volatiles.confusionTurns > 0 @@ -7244,7 +5593,7 @@ bool32 CanBeConfused(u32 battler) } // second argument is 1/X of current hp compared to max hp -bool32 HasEnoughHpToEatBerry(u32 battler, enum Ability ability, u32 hpFraction, enum Item itemId) +bool32 HasEnoughHpToEatBerry(enum BattlerId battler, enum Ability ability, u32 hpFraction, enum Item itemId) { if (!IsBattlerAlive(battler)) return FALSE; @@ -7261,7 +5610,7 @@ bool32 HasEnoughHpToEatBerry(u32 battler, enum Ability ability, u32 hpFraction, return FALSE; } -void ClearVariousBattlerFlags(u32 battler) +void ClearVariousBattlerFlags(enum BattlerId battler) { gBattleMons[battler].volatiles.furyCutterCounter = 0; gBattleMons[battler].volatiles.destinyBond = 0; @@ -7275,10 +5624,10 @@ void HandleAction_RunBattleScript(void) // identical to RunBattleScriptCommands gBattleScriptingCommandsTable[*gBattlescriptCurrInstr](); } -u32 SetRandomTarget(u32 battlerAtk) +u32 SetRandomTarget(enum BattlerId battlerAtk) { - u32 target; - static const u8 targets[2][2] = + enum BattlerId target; + static const u8 targets[NUM_BATTLE_SIDES][MAX_BATTLERS_COUNT / 2] = { [B_SIDE_PLAYER] = {B_POSITION_OPPONENT_LEFT, B_POSITION_OPPONENT_RIGHT}, [B_SIDE_OPPONENT] = {B_POSITION_PLAYER_LEFT, B_POSITION_PLAYER_RIGHT}, @@ -7470,7 +5819,7 @@ enum Obedience GetAttackerObedienceForAction(void) if (calc < obedienceLevel && CanBeSlept(gBattlerAttacker, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), NOT_BLOCKED_BY_SLEEP_CLAUSE)) { // try putting asleep - int i; + enum BattlerId i; for (i = 0; i < gBattlersCount; i++) if (gBattleMons[i].volatiles.uproarTurns) break; @@ -7485,17 +5834,17 @@ enum Obedience GetAttackerObedienceForAction(void) } } -enum HoldEffect GetBattlerHoldEffect(u32 battler) +enum HoldEffect GetBattlerHoldEffect(enum BattlerId battler) { return GetBattlerHoldEffectInternal(battler, GetBattlerAbility(battler)); } -enum HoldEffect GetBattlerHoldEffectIgnoreAbility(u32 battler) +enum HoldEffect GetBattlerHoldEffectIgnoreAbility(enum BattlerId battler) { return GetBattlerHoldEffectInternal(battler, ABILITY_NONE); } -enum HoldEffect GetBattlerHoldEffectInternal(u32 battler, enum Ability ability) +enum HoldEffect GetBattlerHoldEffectInternal(enum BattlerId battler, enum Ability ability) { if (gBattleMons[battler].volatiles.embargo) return HOLD_EFFECT_NONE; @@ -7512,7 +5861,7 @@ enum HoldEffect GetBattlerHoldEffectInternal(u32 battler, enum Ability ability) return GetItemHoldEffect(gBattleMons[battler].item); } -enum HoldEffect GetBattlerHoldEffectIgnoreNegation(u32 battler) +enum HoldEffect GetBattlerHoldEffectIgnoreNegation(enum BattlerId battler) { gPotentialItemEffectBattler = battler; if (gBattleMons[battler].item == ITEM_ENIGMA_BERRY_E_READER) @@ -7521,7 +5870,7 @@ enum HoldEffect GetBattlerHoldEffectIgnoreNegation(u32 battler) return GetItemHoldEffect(gBattleMons[battler].item); } -u32 GetBattlerHoldEffectParam(u32 battler) +u32 GetBattlerHoldEffectParam(enum BattlerId battler) { if (gBattleMons[battler].item == ITEM_ENIGMA_BERRY_E_READER) return gEnigmaBerries[battler].holdEffectParam; @@ -7529,7 +5878,7 @@ u32 GetBattlerHoldEffectParam(u32 battler) return GetItemHoldEffectParam(gBattleMons[battler].item); } -bool32 CanBattlerAvoidContactEffects(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum HoldEffect holdEffectAtk, enum Move move) +bool32 CanBattlerAvoidContactEffects(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum HoldEffect holdEffectAtk, enum Move move) { if (holdEffectAtk == HOLD_EFFECT_PROTECTIVE_PADS) { @@ -7540,7 +5889,7 @@ bool32 CanBattlerAvoidContactEffects(u32 battlerAtk, u32 battlerDef, enum Abilit return !IsMoveMakingContact(battlerAtk, battlerDef, abilityAtk, holdEffectAtk, move); } -bool32 IsMoveMakingContact(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum HoldEffect holdEffectAtk, enum Move move) +bool32 IsMoveMakingContact(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum HoldEffect holdEffectAtk, enum Move move) { if (!(MoveMakesContact(move) || (GetMoveEffect(move) == EFFECT_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] == DAMAGE_CATEGORY_PHYSICAL))) @@ -7560,12 +5909,32 @@ bool32 IsMoveMakingContact(u32 battlerAtk, u32 battlerDef, enum Ability abilityA return TRUE; } -static inline bool32 IsSideProtected(u32 battler, enum ProtectMethod method) +static inline bool32 IsSideProtected(enum BattlerId battler, enum ProtectMethod method) { return gProtectStructs[battler].protected == method || gProtectStructs[BATTLE_PARTNER(battler)].protected == method; } +static bool32 IsCraftyShieldProtected(u32 battlerAtk, u32 battlerDef, u32 move) +{ + if (!IsBattleMoveStatus(move)) + return FALSE; + + if (!IsSideProtected(battlerDef, PROTECT_CRAFTY_SHIELD)) + return FALSE; + + if (GetMoveEffect(move) == EFFECT_HOLD_HANDS) + return TRUE; + + u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, move); + if (!IsBattlerAlly(battlerAtk, battlerDef) + && moveTarget != TARGET_OPPONENTS_FIELD + && moveTarget != TARGET_ALL_BATTLERS) + return TRUE; + + return FALSE; +} + bool32 IsBattlerProtected(struct BattleContext *ctx) { if (gProtectStructs[ctx->battlerDef].protected == PROTECT_NONE @@ -7589,9 +5958,7 @@ bool32 IsBattlerProtected(struct BattleContext *ctx) bool32 isProtected = FALSE; - if (IsSideProtected(ctx->battlerDef, PROTECT_CRAFTY_SHIELD) - && IsBattleMoveStatus(ctx->move) - && GetMoveEffect(ctx->move) != EFFECT_COACHING) + if (IsCraftyShieldProtected(ctx->battlerAtk, ctx->battlerDef, ctx->move)) isProtected = TRUE; else if (MoveIgnoresProtect(ctx->move)) isProtected = FALSE; @@ -7661,7 +6028,7 @@ enum IronBallCheck }; // Only called directly when calculating damage type effectiveness, and Iron Ball's type effectiveness mechanics -static bool32 IsBattlerGroundedInverseCheck(u32 battler, enum Ability ability, enum HoldEffect holdEffect, enum InverseBattleCheck checkInverse, bool32 isAnticipation) +static bool32 IsBattlerGroundedInverseCheck(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect, enum InverseBattleCheck checkInverse, bool32 isAnticipation) { if (holdEffect == HOLD_EFFECT_IRON_BALL) return TRUE; @@ -7684,7 +6051,7 @@ static bool32 IsBattlerGroundedInverseCheck(u32 battler, enum Ability ability, e return TRUE; } -bool32 IsBattlerGrounded(u32 battler, enum Ability ability, enum HoldEffect holdEffect) +bool32 IsBattlerGrounded(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect) { return IsBattlerGroundedInverseCheck(battler, ability, holdEffect, NOT_INVERSE_BATTLE, FALSE); } @@ -7701,7 +6068,7 @@ u32 GetMoveSlot(u16 *moves, enum Move move) return i; } -u32 GetBattlerWeight(u32 battler) +u32 GetBattlerWeight(enum BattlerId battler) { u32 i; u32 weight = GetSpeciesWeight(gBattleMons[battler].species); @@ -7735,7 +6102,7 @@ u32 GetBattlerWeight(u32 battler) return weight; } -u32 CountBattlerStatIncreases(u32 battler, bool32 countEvasionAcc) +u32 CountBattlerStatIncreases(enum BattlerId battler, bool32 countEvasionAcc) { enum Stat i; u32 count = 0; @@ -7751,7 +6118,7 @@ u32 CountBattlerStatIncreases(u32 battler, bool32 countEvasionAcc) return count; } -bool32 BattlerHasCopyableChanges(u32 battler) +bool32 BattlerHasCopyableChanges(enum BattlerId battler) { u32 i; @@ -7771,8 +6138,8 @@ bool32 BattlerHasCopyableChanges(u32 battler) u32 GetMoveTargetCount(struct BattleContext *ctx) { - u32 battlerAtk = ctx->battlerAtk; - u32 battlerDef = ctx->battlerDef; + enum BattlerId battlerAtk = ctx->battlerAtk; + enum BattlerId battlerDef = ctx->battlerDef; enum Move move = ctx->move; switch (GetBattlerMoveTargetType(battlerAtk, move)) @@ -7894,7 +6261,7 @@ const struct TypePower gNaturalGiftTable[] = [ITEM_TO_BERRY(ITEM_MARANGA_BERRY)] = {TYPE_DARK, 100}, }; -static inline u32 CalcRolloutBasePower(u32 battlerAtk, u32 basePower) +static inline u32 CalcRolloutBasePower(enum BattlerId battlerAtk, u32 basePower) { u32 i; for (i = 0; i < gBattleMons[battlerAtk].volatiles.rolloutTimer; i++) @@ -7904,7 +6271,7 @@ static inline u32 CalcRolloutBasePower(u32 battlerAtk, u32 basePower) return basePower; } -static inline u32 CalcFuryCutterBasePower(u32 battlerAtk, u32 basePower) +static inline u32 CalcFuryCutterBasePower(enum BattlerId battlerAtk, u32 basePower) { for (u32 i = 0; i < gBattleMons[battlerAtk].volatiles.furyCutterCounter; i++) basePower *= 2; @@ -7938,7 +6305,7 @@ static inline u32 IsFieldMudSportAffected(enum Type moveType) if (B_SPORT_TURNS < GEN_6) { - for (u32 battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { if (gBattleMons[battler].volatiles.mudSport) return TRUE; @@ -7958,7 +6325,7 @@ static inline u32 IsFieldWaterSportAffected(enum Type moveType) if (B_SPORT_TURNS < GEN_6) { - for (u32 battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { if (gBattleMons[battler].volatiles.waterSport) return TRUE; @@ -7970,8 +6337,8 @@ static inline u32 IsFieldWaterSportAffected(enum Type moveType) static inline u32 CalcMoveBasePower(struct BattleContext *ctx) { - u32 battlerAtk = ctx->battlerAtk; - u32 battlerDef = ctx->battlerDef; + enum BattlerId battlerAtk = ctx->battlerAtk; + enum BattlerId battlerDef = ctx->battlerDef; enum Move move = ctx->move; u32 i; @@ -8191,7 +6558,7 @@ static inline u32 CalcMoveBasePower(struct BattleContext *ctx) basePower = CalcBeatUpPower(); break; case EFFECT_MAX_MOVE: - basePower = GetMaxMovePower(GetChosenMoveFromPosition(battlerAtk)); + basePower = GetMaxMovePower(GetBattlerChosenMove(battlerAtk)); break; case EFFECT_RAGE_FIST: basePower += 50 * GetBattlerPartyState(battlerAtk)->timesGotHit; @@ -8224,8 +6591,8 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct BattleContext *ctx) { u32 holdEffectParamAtk; u32 basePower = CalcMoveBasePower(ctx); - u32 battlerAtk = ctx->battlerAtk; - u32 battlerDef = ctx->battlerDef; + enum BattlerId battlerAtk = ctx->battlerAtk; + enum BattlerId battlerDef = ctx->battlerDef; enum Move move = ctx->move; enum Type moveType = ctx->moveType; enum BattleMoveEffects moveEffect = GetMoveEffect(move); @@ -8521,7 +6888,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct BattleContext *ctx) static bool32 IsRuinStatusActive(u32 fieldEffect) { bool32 isNeutralizingGasOnField = IsNeutralizingGasOnField(); - for (u32 battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { // Mold Breaker doesn't ignore Ruin field status but Gastro Acid and Neutralizing Gas do if (gBattleMons[battler].volatiles.gastroAcid) @@ -8538,7 +6905,7 @@ static bool32 IsRuinStatusActive(u32 fieldEffect) return FALSE; } -static inline uq4_12_t ApplyOffensiveBadgeBoost(uq4_12_t modifier, u32 battler, enum Move move) +static inline uq4_12_t ApplyOffensiveBadgeBoost(uq4_12_t modifier, enum BattlerId battler, enum Move move) { if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_ATTACK, battler) && IsBattleMovePhysical(move)) modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier()); @@ -8547,7 +6914,7 @@ static inline uq4_12_t ApplyOffensiveBadgeBoost(uq4_12_t modifier, u32 battler, return modifier; } -static inline uq4_12_t ApplyDefensiveBadgeBoost(uq4_12_t modifier, u32 battler, enum Move move) +static inline uq4_12_t ApplyDefensiveBadgeBoost(uq4_12_t modifier, enum BattlerId battler, enum Move move) { if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_DEFENSE, battler) && IsBattleMovePhysical(move)) modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier()); @@ -8562,8 +6929,8 @@ static inline u32 CalcAttackStat(struct BattleContext *ctx) u32 atkStat; uq4_12_t modifier; u16 atkBaseSpeciesId; - u32 battlerAtk = ctx->battlerAtk; - u32 battlerDef = ctx->battlerDef; + enum BattlerId battlerAtk = ctx->battlerAtk; + enum BattlerId battlerDef = ctx->battlerDef; enum Move move = ctx->move; enum Type moveType = ctx->moveType; enum BattleMoveEffects moveEffect = GetMoveEffect(move); @@ -8859,7 +7226,7 @@ static inline u32 CalcDefenseStat(struct BattleContext *ctx) u8 defStage; u32 defStat, def, spDef; uq4_12_t modifier; - u32 battlerDef = ctx->battlerDef; + enum BattlerId battlerDef = ctx->battlerDef; enum Move move = ctx->move; enum BattleMoveEffects moveEffect = GetMoveEffect(move); @@ -9052,7 +7419,7 @@ static inline uq4_12_t GetTargetDamageModifier(struct BattleContext *ctx) return UQ_4_12(1.0); } -static inline uq4_12_t GetParentalBondModifier(u32 battlerAtk) +static inline uq4_12_t GetParentalBondModifier(enum BattlerId battlerAtk) { if (gSpecialStatuses[battlerAtk].parentalBondState != PARENTAL_BOND_2ND_HIT) return UQ_4_12(1.0); @@ -9118,7 +7485,7 @@ static inline uq4_12_t GetCriticalModifier(bool32 isCrit) return UQ_4_12(1.0); } -static inline uq4_12_t GetGlaiveRushModifier(u32 battlerDef) +static inline uq4_12_t GetGlaiveRushModifier(enum BattlerId battlerDef) { if (gBattleMons[battlerDef].volatiles.glaiveRush) return UQ_4_12(2.0); @@ -9136,28 +7503,28 @@ static inline uq4_12_t GetZMaxMoveAgainstProtectionModifier(struct BattleContext return UQ_4_12(1.0); } -static inline uq4_12_t GetMinimizeModifier(enum Move move, u32 battlerDef) +static inline uq4_12_t GetMinimizeModifier(enum Move move, enum BattlerId battlerDef) { if (MoveIncreasesPowerToMinimizedTargets(move) && gBattleMons[battlerDef].volatiles.minimize) return UQ_4_12(2.0); return UQ_4_12(1.0); } -static inline uq4_12_t GetUndergroundModifier(enum Move move, u32 battlerDef) +static inline uq4_12_t GetUndergroundModifier(enum Move move, enum BattlerId battlerDef) { if (MoveDamagesUnderground(move) && gBattleMons[battlerDef].volatiles.semiInvulnerable == STATE_UNDERGROUND) return UQ_4_12(2.0); return UQ_4_12(1.0); } -static inline uq4_12_t GetDiveModifier(enum Move move, u32 battlerDef) +static inline uq4_12_t GetDiveModifier(enum Move move, enum BattlerId battlerDef) { if (MoveDamagesUnderWater(move) && gBattleMons[battlerDef].volatiles.semiInvulnerable == STATE_UNDERWATER) return UQ_4_12(2.0); return UQ_4_12(1.0); } -static inline uq4_12_t GetAirborneModifier(enum Move move, u32 battlerDef) +static inline uq4_12_t GetAirborneModifier(enum Move move, enum BattlerId battlerDef) { if (MoveDamagesAirborneDoubleDamage(move) && gBattleMons[battlerDef].volatiles.semiInvulnerable == STATE_ON_AIR) return UQ_4_12(2.0); @@ -9195,7 +7562,7 @@ static inline uq4_12_t GetCollisionCourseElectroDriftModifier(enum Move move, uq return UQ_4_12(1.0); } -static inline uq4_12_t GetAttackerAbilitiesModifier(u32 battlerAtk, uq4_12_t typeEffectivenessModifier, bool32 isCrit, enum Ability abilityAtk) +static inline uq4_12_t GetAttackerAbilitiesModifier(enum BattlerId battlerAtk, uq4_12_t typeEffectivenessModifier, bool32 isCrit, enum Ability abilityAtk) { switch (abilityAtk) { @@ -9277,7 +7644,7 @@ static inline uq4_12_t GetDefenderAbilitiesModifier(struct BattleContext *ctx) return modifier; } -static inline uq4_12_t GetDefenderPartnerAbilitiesModifier(u32 battlerPartnerDef) +static inline uq4_12_t GetDefenderPartnerAbilitiesModifier(enum BattlerId battlerPartnerDef) { if (!IsBattlerAlive(battlerPartnerDef)) return UQ_4_12(1.0); @@ -9293,7 +7660,7 @@ static inline uq4_12_t GetDefenderPartnerAbilitiesModifier(u32 battlerPartnerDef return UQ_4_12(1.0); } -static inline uq4_12_t GetAttackerItemsModifier(u32 battlerAtk, uq4_12_t typeEffectivenessModifier, enum HoldEffect holdEffectAtk) +static inline uq4_12_t GetAttackerItemsModifier(enum BattlerId battlerAtk, uq4_12_t typeEffectivenessModifier, enum HoldEffect holdEffectAtk) { u32 metronomeTurns; uq4_12_t metronomeBoostBase; @@ -9354,7 +7721,7 @@ static inline uq4_12_t GetDefenderItemsModifier(struct BattleContext *ctx) static inline uq4_12_t GetOtherModifiers(struct BattleContext *ctx) { uq4_12_t finalModifier = UQ_4_12(1.0); - u32 battlerDefPartner = BATTLE_PARTNER(ctx->battlerDef); + enum BattlerId battlerDefPartner = BATTLE_PARTNER(ctx->battlerDef); u32 unmodifiedAttackerSpeed = gBattleMons[ctx->battlerAtk].speed; u32 unmodifiedDefenderSpeed = gBattleMons[ctx->battlerDef].speed; @@ -9510,6 +7877,9 @@ s32 DoFixedDamageMoveCalc(struct BattleContext *ctx) dmg = GetNonDynamaxHP(ctx->battlerDef) - gBattleMons[ctx->battlerAtk].hp; } break; + case EFFECT_OHKO: + dmg = gBattleMons[ctx->battlerDef].hp; + break; default: break; } @@ -9542,8 +7912,8 @@ static inline s32 DoFutureSightAttackDamageCalcVars(struct BattleContext *ctx) s32 dmg; u32 userFinalAttack; u32 targetFinalDefense; - u32 battlerAtk = ctx->battlerAtk; - u32 battlerDef = ctx->battlerDef; + enum BattlerId battlerAtk = ctx->battlerAtk; + enum BattlerId battlerDef = ctx->battlerDef; enum Move move = ctx->move; enum Type moveType = ctx->moveType; @@ -9590,7 +7960,7 @@ static inline s32 DoFutureSightAttackDamageCalc(struct BattleContext *ctx) return DoFutureSightAttackDamageCalcVars(ctx); } -bool32 IsFutureSightAttackerInParty(u32 battlerAtk, u32 battlerDef, enum Move move) +bool32 IsFutureSightAttackerInParty(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move) { if (GetMoveEffect(move) != EFFECT_FUTURE_SIGHT) return FALSE; @@ -9625,7 +7995,7 @@ static inline u32 GetCriticalHitOdds(u32 critChance) return sCriticalHitOdds[critChance]; } -static inline u32 IsBattlerLeekAffected(u32 battler, enum HoldEffect holdEffect) +static inline u32 IsBattlerLeekAffected(enum BattlerId battler, enum HoldEffect holdEffect) { if (holdEffect == HOLD_EFFECT_LEEK) { @@ -9635,7 +8005,7 @@ static inline u32 IsBattlerLeekAffected(u32 battler, enum HoldEffect holdEffect) return FALSE; } -static inline u32 GetHoldEffectCritChanceIncrease(u32 battler, enum HoldEffect holdEffect) +static inline u32 GetHoldEffectCritChanceIncrease(enum BattlerId battler, enum HoldEffect holdEffect) { u32 critStageIncrease = 0; @@ -9740,20 +8110,15 @@ s32 CalcCritChanceStageGen1(struct BattleContext *ctx) if (critChance > 255) critChance = 255; - // Prevented crits - if (gSideStatuses[ctx->battlerDef] & SIDE_STATUS_LUCKY_CHANT) - critChance = CRITICAL_HIT_BLOCKED; - else if (ctx->abilityDef == ABILITY_BATTLE_ARMOR || ctx->abilityDef == ABILITY_SHELL_ARMOR) + if (ctx->abilityDef == ABILITY_BATTLE_ARMOR || ctx->abilityDef == ABILITY_SHELL_ARMOR) { if (ctx->updateFlags) RecordAbilityBattle(ctx->battlerDef, ctx->abilityDef); critChance = CRITICAL_HIT_BLOCKED; } - - // Guaranteed crits else if (gBattleMons[ctx->battlerAtk].volatiles.laserFocus - || MoveAlwaysCrits(ctx->move) - || (ctx->abilityAtk == ABILITY_MERCILESS && gBattleMons[ctx->battlerDef].status1 & STATUS1_PSN_ANY)) + || MoveAlwaysCrits(ctx->move) + || (ctx->abilityAtk == ABILITY_MERCILESS && gBattleMons[ctx->battlerDef].status1 & STATUS1_PSN_ANY)) { critChance = CRITICAL_HIT_ALWAYS; } @@ -9763,6 +8128,15 @@ s32 CalcCritChanceStageGen1(struct BattleContext *ctx) static bool32 IsCriticalHit(struct BattleContext *ctx) { + + if ((gBattleTypeFlags & (BATTLE_TYPE_CATCH_TUTORIAL | BATTLE_TYPE_POKEDUDE)) + || ((gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) && (!IS_FRLG || !BtlCtrl_OakOldMan_TestState2Flag(1)))) + return FALSE; + if (ctx->isSelfInflicted) + return FALSE; + if (gSideStatuses[ctx->battlerDef] & SIDE_STATUS_LUCKY_CHANT) + return FALSE; + bool32 isCrit = FALSE; s32 critChance = 0; @@ -9771,22 +8145,16 @@ static bool32 IsCriticalHit(struct BattleContext *ctx) else critChance = CalcCritChanceStage(ctx); - if ((gBattleTypeFlags & (BATTLE_TYPE_CATCH_TUTORIAL | BATTLE_TYPE_POKEDUDE)) - || ((gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) && (!IS_FRLG || !BtlCtrl_OakOldMan_TestState2Flag(1)))) + if (critChance == CRITICAL_HIT_BLOCKED) isCrit = FALSE; - else if (critChance == -1) - isCrit = FALSE; - else if (critChance == -2) + else if (critChance == CRITICAL_HIT_ALWAYS) isCrit = TRUE; + else if (GetConfig(CONFIG_CRIT_CHANCE) == GEN_1) + isCrit = RandomChance(RNG_CRITICAL_HIT, critChance, 256); + else if (GetConfig(CONFIG_CRIT_CHANCE) == GEN_2) + isCrit = RandomChance(RNG_CRITICAL_HIT, GetCriticalHitOdds(critChance), 256); else - { - if (GetConfig(CONFIG_CRIT_CHANCE) == GEN_1) - isCrit = RandomChance(RNG_CRITICAL_HIT, critChance, 256); - else if (GetConfig(CONFIG_CRIT_CHANCE) == GEN_2) - isCrit = RandomChance(RNG_CRITICAL_HIT, GetCriticalHitOdds(critChance), 256); - else - isCrit = RandomChance(RNG_CRITICAL_HIT, 1, GetCriticalHitOdds(critChance)); - } + isCrit = RandomChance(RNG_CRITICAL_HIT, 1, GetCriticalHitOdds(critChance)); // Counter for IF_CRITICAL_HITS_GE evolution condition. if (isCrit && IsOnPlayerSide(ctx->battlerAtk) @@ -9797,21 +8165,88 @@ static bool32 IsCriticalHit(struct BattleContext *ctx) return isCrit; } +s32 GetAdjustedDamage(struct BattleContext *ctx, s32 damage) +{ + if (DoesSubstituteBlockMove(ctx->battlerAtk, ctx->battlerDef, ctx->move) + || DoesDisguiseBlockMove(ctx->battlerDef, ctx->move) + || DoesIceFaceBlockMove(ctx->battlerDef, ctx->move)) + return damage; // No damage will be dealt + + if (gBattleMons[ctx->battlerDef].hp > damage) + return damage; + + bool32 enduredHit = FALSE; + u32 rand = Random() % 100; + u32 affectionScore = GetBattlerAffectionHearts(ctx->battlerDef); + + if (GetMoveEffect(ctx->move) == EFFECT_FALSE_SWIPE) + { + enduredHit = TRUE; + } + else if (gBattleMons[ctx->battlerDef].volatiles.endured) + { + enduredHit = TRUE; + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FOE_ENDURED; + } + else if (ctx->holdEffectDef == HOLD_EFFECT_FOCUS_BAND && rand < GetBattlerHoldEffectParam(ctx->battlerDef)) + { + enduredHit = TRUE; + RecordItemEffectBattle(ctx->battlerDef, ctx->holdEffectDef); + gLastUsedItem = gBattleMons[ctx->battlerDef].item; + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FOE_HUNG_ON; + } + else if (GetConfig(CONFIG_STURDY) >= GEN_5 && ctx->abilityDef == ABILITY_STURDY && IsBattlerAtMaxHp(ctx->battlerDef)) + { + enduredHit = TRUE; + RecordAbilityBattle(ctx->battlerDef, ABILITY_STURDY); + gLastUsedAbility = ABILITY_STURDY; + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_STURDIED; + } + else if (ctx->holdEffectDef == HOLD_EFFECT_FOCUS_SASH && IsBattlerAtMaxHp(ctx->battlerDef)) + { + enduredHit = TRUE; + RecordItemEffectBattle(ctx->battlerDef, ctx->holdEffectDef); + gLastUsedItem = gBattleMons[ctx->battlerDef].item; + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FOE_HUNG_ON; + } + else if (B_AFFECTION_MECHANICS == TRUE && IsOnPlayerSide(ctx->battlerDef) && affectionScore >= AFFECTION_THREE_HEARTS) + { + if ((affectionScore == AFFECTION_FIVE_HEARTS && rand < 20) + || (affectionScore == AFFECTION_FOUR_HEARTS && rand < 15) + || (affectionScore == AFFECTION_THREE_HEARTS && rand < 10)) + { + enduredHit = TRUE; + gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FOE_ENDURED_AFFECTION; + } + } + + if (enduredHit) + { + damage = gBattleMons[ctx->battlerDef].hp - 1; // Reduce damage to 1 hp. + gProtectStructs[ctx->battlerDef].assuranceDoubled = TRUE; + } + + return damage; +} + s32 CalculateMoveDamage(struct BattleContext *ctx) { + s32 damage = 0; + ctx->abilityAtk = GetBattlerAbility(ctx->battlerAtk); ctx->abilityDef = GetBattlerAbility(ctx->battlerDef); ctx->holdEffectAtk = GetBattlerHoldEffect(ctx->battlerAtk); ctx->holdEffectDef = GetBattlerHoldEffect(ctx->battlerDef); ctx->typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(ctx); - if (!ctx->isSelfInflicted) - ctx->isCrit = IsCriticalHit(ctx); + ctx->isCrit = IsCriticalHit(ctx); if (IsFutureSightAttackerInParty(ctx->battlerAtk, ctx->battlerDef, ctx->move)) - return DoFutureSightAttackDamageCalc(ctx); + damage = DoFutureSightAttackDamageCalc(ctx); + else + damage = DoMoveDamageCalc(ctx); - return DoMoveDamageCalc(ctx); + return GetAdjustedDamage(ctx, damage); } // for AI so that typeEffectivenessModifier, weather, abilities and holdEffects are calculated only once @@ -9873,7 +8308,7 @@ static inline void MulByTypeEffectiveness(struct BattleContext *ctx, uq4_12_t *m *modifier = uq4_12_multiply(*modifier, mod); } -static inline void TryNoticeIllusionInTypeEffectiveness(enum Move move, enum Type moveType, u32 battlerAtk, u32 battlerDef, uq4_12_t resultingModifier, u32 illusionSpecies) +static inline void TryNoticeIllusionInTypeEffectiveness(enum Move move, enum Type moveType, enum BattlerId battlerAtk, enum BattlerId battlerDef, uq4_12_t resultingModifier, u32 illusionSpecies) { // Check if the type effectiveness would've been different if the pokemon really had the types as the disguise. uq4_12_t presumedModifier = UQ_4_12(1.0); @@ -9959,7 +8394,7 @@ static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(struct BattleCont ctx->airBalloonBlocked = TRUE; } } - else if (GetConfig(CONFIG_SHEER_COLD_IMMUNITY) >= GEN_7 && GetMoveEffect(ctx->move) == EFFECT_SHEER_COLD && IS_BATTLER_OF_TYPE(ctx->battlerDef, TYPE_ICE)) + else if (MoveHasNoEffectOnSameType(ctx->move) && IS_BATTLER_OF_TYPE(ctx->battlerDef, GetMoveType(ctx->move))) { modifier = UQ_4_12(0.0); } @@ -10143,7 +8578,7 @@ s32 GetStealthHazardDamageByTypesAndHP(enum TypeSideHazard hazardType, enum Type return dmg; } -s32 GetStealthHazardDamage(enum TypeSideHazard hazardType, u32 battler) +s32 GetStealthHazardDamage(enum TypeSideHazard hazardType, enum BattlerId battler) { enum Type types[3]; GetBattlerTypes(battler, FALSE, types); @@ -10152,7 +8587,7 @@ s32 GetStealthHazardDamage(enum TypeSideHazard hazardType, u32 battler) return GetStealthHazardDamageByTypesAndHP(hazardType, types[0], types[1], maxHp); } -bool32 IsPartnerMonFromSameTrainer(u32 battler) +bool32 IsPartnerMonFromSameTrainer(enum BattlerId battler) { if (!IsOnPlayerSide(battler)) return !(gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS); @@ -10165,6 +8600,9 @@ bool32 DoesSpeciesUseHoldItemToChangeForm(u16 species, u16 heldItemId) u32 i; const struct FormChange *formChanges = GetSpeciesFormChanges(species); + if (heldItemId == ITEM_NONE) + return FALSE; + for (i = 0; formChanges != NULL && formChanges[i].method != FORM_CHANGE_TERMINATOR; i++) { enum FormChanges method = formChanges[i].method; @@ -10185,13 +8623,14 @@ bool32 DoesSpeciesUseHoldItemToChangeForm(u16 species, u16 heldItemId) return FALSE; } -bool32 CanMegaEvolve(u32 battler) +bool32 CanMegaEvolve(enum BattlerId battler) { enum HoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); + enum BattlerPosition position = GetBattlerPosition(battler); // Check if Player has a Mega Ring. if (!TESTING - && (GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT)) + && (position == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && position == B_POSITION_PLAYER_RIGHT)) && !CheckBagHasItem(ITEM_MEGA_RING, 1)) return FALSE; @@ -10211,25 +8650,28 @@ bool32 CanMegaEvolve(u32 battler) if (holdEffect == HOLD_EFFECT_Z_CRYSTAL) return FALSE; + enum Ability ability = GetBattlerAbility(battler); + // Check if there is an entry in the form change table for regular Mega Evolution and battler is holding Mega Stone. - if (GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM) != gBattleMons[battler].species && holdEffect == HOLD_EFFECT_MEGA_STONE) + if (GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, ability) != gBattleMons[battler].species) return TRUE; // Check if there is an entry in the form change table for Wish Mega Evolution. - if (GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE) != gBattleMons[battler].species) + if (GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE, ability) != gBattleMons[battler].species) return TRUE; // No checks passed, the mon CAN'T mega evolve. return FALSE; } -bool32 CanUltraBurst(u32 battler) +bool32 CanUltraBurst(enum BattlerId battler) { enum HoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); + enum BattlerPosition position = GetBattlerPosition(battler); // Check if Player has a Z-Ring - if (!TESTING && (GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT - || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT)) + if (!TESTING && (position == B_POSITION_PLAYER_LEFT + || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && position == B_POSITION_PLAYER_RIGHT)) && !CheckBagHasItem(ITEM_Z_POWER_RING, 1)) return FALSE; @@ -10245,32 +8687,45 @@ bool32 CanUltraBurst(u32 battler) if (gBattleMons[battler].volatiles.semiInvulnerable == STATE_SKY_DROP) return FALSE; + enum Ability ability = GetBattlerAbility(battler); + // Check if there is an entry in the form change table for Ultra Burst and battler is holding a Z-Crystal. - if (GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_ULTRA_BURST) != gBattleMons[battler].species && holdEffect == HOLD_EFFECT_Z_CRYSTAL) + if (GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_ULTRA_BURST, ability) != gBattleMons[battler].species && holdEffect == HOLD_EFFECT_Z_CRYSTAL) return TRUE; // No checks passed, the mon CAN'T ultra burst. return FALSE; } -void ActivateMegaEvolution(u32 battler) +void ActivateMegaEvolution(enum BattlerId battler) { + enum Ability ability = GetBattlerAbility(battler); gLastUsedItem = gBattleMons[battler].item; SetActiveGimmick(battler, GIMMICK_MEGA); - if (GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE) != gBattleMons[battler].species) + SetGimmickAsActivated(battler, GIMMICK_MEGA); + + if (TryBattleFormChange(battler, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE, ability)) + { BattleScriptPushCursorAndCallback(BattleScript_WishMegaEvolution); + } else + { + TryBattleFormChange(battler, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, ability); BattleScriptPushCursorAndCallback(BattleScript_MegaEvolution); + } } -void ActivateUltraBurst(u32 battler) +void ActivateUltraBurst(enum BattlerId battler) { + enum Ability ability = GetBattlerAbility(battler); gLastUsedItem = gBattleMons[battler].item; SetActiveGimmick(battler, GIMMICK_ULTRA_BURST); + SetGimmickAsActivated(battler, GIMMICK_ULTRA_BURST); + TryBattleFormChange(battler, FORM_CHANGE_BATTLE_ULTRA_BURST, ability); BattleScriptPushCursorAndCallback(BattleScript_UltraBurst); } -bool32 IsBattlerMegaEvolved(u32 battler) +bool32 IsBattlerMegaEvolved(enum BattlerId battler) { // While Transform does copy stats and visuals, it shouldn't be counted as true Mega Evolution. if (gBattleMons[battler].volatiles.transformed) @@ -10278,7 +8733,7 @@ bool32 IsBattlerMegaEvolved(u32 battler) return (gSpeciesInfo[gBattleMons[battler].species].isMegaEvolution); } -bool32 IsBattlerPrimalReverted(u32 battler) +bool32 IsBattlerPrimalReverted(enum BattlerId battler) { // While Transform does copy stats and visuals, it shouldn't be counted as true Primal Revesion. if (gBattleMons[battler].volatiles.transformed) @@ -10286,7 +8741,7 @@ bool32 IsBattlerPrimalReverted(u32 battler) return (gSpeciesInfo[gBattleMons[battler].species].isPrimalReversion); } -bool32 IsBattlerUltraBursted(u32 battler) +bool32 IsBattlerUltraBursted(enum BattlerId battler) { // While Transform does copy stats and visuals, it shouldn't be counted as true Ultra Burst. if (gBattleMons[battler].volatiles.transformed) @@ -10294,7 +8749,7 @@ bool32 IsBattlerUltraBursted(u32 battler) return (gSpeciesInfo[gBattleMons[battler].species].isUltraBurst); } -bool32 IsBattlerInTeraForm(u32 battler) +bool32 IsBattlerInTeraForm(enum BattlerId battler) { // While Transform does copy stats and visuals, it shouldn't be counted as a true Tera Form. if (gBattleMons[battler].volatiles.transformed) @@ -10302,117 +8757,35 @@ bool32 IsBattlerInTeraForm(u32 battler) return (gSpeciesInfo[gBattleMons[battler].species].isTeraForm); } -// Returns SPECIES_NONE if no form change is possible -u32 GetBattleFormChangeTargetSpecies(u32 battler, enum FormChanges method) +u32 GetBattleFormChangeTargetSpecies(enum BattlerId battler, enum FormChanges method, enum Ability ability) { - u32 i; u32 species = gBattleMons[battler].species; - u32 targetSpecies = species; const struct FormChange *formChanges = GetSpeciesFormChanges(species); - struct Pokemon *mon = GetBattlerMon(battler); - u16 heldItem = gBattleMons[battler].item; - for (i = 0; formChanges != NULL && formChanges[i].method != FORM_CHANGE_TERMINATOR; i++) + if (formChanges == NULL) + return species; + + struct FormChangeContext ctx = { - if (method == formChanges[i].method && species != formChanges[i].targetSpecies) - { - switch (method) - { - case FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM: - case FORM_CHANGE_BATTLE_PRIMAL_REVERSION: - case FORM_CHANGE_BATTLE_ULTRA_BURST: - if (heldItem == formChanges[i].param1) - targetSpecies = formChanges[i].targetSpecies; - break; - case FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE: - if (gBattleMons[battler].moves[0] == formChanges[i].param1 - || gBattleMons[battler].moves[1] == formChanges[i].param1 - || gBattleMons[battler].moves[2] == formChanges[i].param1 - || gBattleMons[battler].moves[3] == formChanges[i].param1) - targetSpecies = formChanges[i].targetSpecies; - break; - case FORM_CHANGE_BATTLE_SWITCH: - if (formChanges[i].param1 == GetBattlerAbility(battler) || formChanges[i].param1 == ABILITY_NONE) - targetSpecies = formChanges[i].targetSpecies; - break; - case FORM_CHANGE_BATTLE_HP_PERCENT: - if (formChanges[i].param1 == GetBattlerAbility(battler)) - { - // We multiply by 100 to make sure that integer division doesn't mess with the health check. - u32 hpCheck = gBattleMons[battler].hp * 100 * 100 / gBattleMons[battler].maxHP; - switch(formChanges[i].param2) - { - case HP_HIGHER_THAN: - if (hpCheck > formChanges[i].param3 * 100) - targetSpecies = formChanges[i].targetSpecies; - break; - case HP_LOWER_EQ_THAN: - if (hpCheck <= formChanges[i].param3 * 100) - targetSpecies = formChanges[i].targetSpecies; - break; - } - } - break; - case FORM_CHANGE_BATTLE_GIGANTAMAX: - if (GetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR)) - targetSpecies = formChanges[i].targetSpecies; - break; - case FORM_CHANGE_BATTLE_WEATHER: - // Check if there is a required ability and if the battler's ability does not match it - // or is suppressed. If so, revert to the no weather form. - if (formChanges[i].param2 - && GetBattlerAbility(battler) != formChanges[i].param2 - && formChanges[i].param1 == B_WEATHER_NONE) - { - targetSpecies = formChanges[i].targetSpecies; - } - // We need to revert the weather form if the field is under Air Lock, too. - else if (!HasWeatherEffect() && formChanges[i].param1 == B_WEATHER_NONE) - { - targetSpecies = formChanges[i].targetSpecies; - } - // Otherwise, just check for a match between the weather and the form change table. - // Added a check for whether the weather is in effect to prevent end-of-turn soft locks with Cloud Nine / Air Lock - else if (((gBattleWeather & formChanges[i].param1) && HasWeatherEffect()) - || (gBattleWeather == B_WEATHER_NONE && formChanges[i].param1 == B_WEATHER_NONE)) - { - targetSpecies = formChanges[i].targetSpecies; - } - break; - case FORM_CHANGE_BATTLE_TURN_END: - case FORM_CHANGE_HIT_BY_MOVE: - if (formChanges[i].param1 == GetBattlerAbility(battler)) - targetSpecies = formChanges[i].targetSpecies; - break; - case FORM_CHANGE_STATUS: - if (gBattleMons[battler].status1 & formChanges[i].param1) - targetSpecies = formChanges[i].targetSpecies; - break; - case FORM_CHANGE_BATTLE_TERASTALLIZATION: - if (GetBattlerTeraType(battler) == formChanges[i].param1) - targetSpecies = formChanges[i].targetSpecies; - break; - case FORM_CHANGE_BATTLE_BEFORE_MOVE: - case FORM_CHANGE_BATTLE_AFTER_MOVE: - if (formChanges[i].param1 == gCurrentMove - && (formChanges[i].param2 == ABILITY_NONE || formChanges[i].param2 == GetBattlerAbility(battler))) - targetSpecies = formChanges[i].targetSpecies; - break; - case FORM_CHANGE_BATTLE_BEFORE_MOVE_CATEGORY: - if (formChanges[i].param1 == GetBattleMoveCategory(gCurrentMove) - && (formChanges[i].param2 == ABILITY_NONE || formChanges[i].param2 == GetBattlerAbility(battler))) - targetSpecies = formChanges[i].targetSpecies; - break; - default: - break; - } - } - } + .method = method, + .currentSpecies = gBattleMons[battler].species, + .heldItem = gBattleMons[battler].item, + .ability = ability, + .status = gBattleMons[battler].status1, + .gmaxFactor = GetMonData(GetBattlerMon(battler), MON_DATA_GIGANTAMAX_FACTOR), + .hp = gBattleMons[battler].hp, + .maxHP = gBattleMons[battler].maxHP, + .teraType = GetBattlerTeraType(battler), + .level = gBattleMons[battler].level, + }; - return targetSpecies; + for (u32 i = 0; i < MAX_MON_MOVES; i++) + ctx.moves[i] = gBattleMons[battler].moves[i]; + + return GetFormChangeTargetSpecies_Internal(ctx); } -static bool32 CanBattlerFormChange(u32 battler, enum FormChanges method) +static bool32 CanBattlerFormChange(enum BattlerId battler, enum FormChanges method) { // Can't change form if transformed. if (gBattleMons[battler].volatiles.transformed @@ -10429,10 +8802,14 @@ static bool32 CanBattlerFormChange(u32 battler, enum FormChanges method) if (IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler) || IsBattlerInTeraForm(battler) || IsGigantamaxed(battler)) return TRUE; break; - case FORM_CHANGE_BATTLE_SWITCH: + case FORM_CHANGE_BATTLE_SWITCH_OUT: if (IsGigantamaxed(battler)) return TRUE; - else if (GetActiveGimmick(battler) == GIMMICK_TERA && GetBattlerAbility(battler) == ABILITY_HUNGER_SWITCH) + else if (GetActiveGimmick(battler) == GIMMICK_TERA && DoesSpeciesHaveFormChangeMethod(gBattleMons[battler].species, FORM_CHANGE_BATTLE_TURN_END)) + return FALSE; + break; + case FORM_CHANGE_BATTLE_TURN_END: + if (GetActiveGimmick(battler) == GIMMICK_TERA) return FALSE; break; default: @@ -10447,10 +8824,10 @@ bool32 TryRevertPartyMonFormChange(u32 partyIndex) // Appeared in battle and didn't faint if (gBattleStruct->partyState[B_SIDE_PLAYER][partyIndex].sentOut && GetMonData(&gPlayerParty[partyIndex], MON_DATA_HP) != 0) - changedForm = TryFormChange(partyIndex, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE_ENVIRONMENT); + changedForm = TryFormChange(&gPlayerParty[partyIndex], FORM_CHANGE_END_BATTLE_ENVIRONMENT); if (!changedForm) - changedForm = TryFormChange(partyIndex, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE); + changedForm = TryFormChange(&gPlayerParty[partyIndex], FORM_CHANGE_END_BATTLE); // Clear original species field gBattleStruct->partyState[B_SIDE_PLAYER][partyIndex].changedSpecies = SPECIES_NONE; @@ -10458,93 +8835,75 @@ bool32 TryRevertPartyMonFormChange(u32 partyIndex) return changedForm; } -bool32 TryBattleFormChange(u32 battler, enum FormChanges method) +bool32 TryBattleFormChange(enum BattlerId battler, enum FormChanges method, enum Ability ability) { - u32 monId = gBattlerPartyIndexes[battler]; - struct Pokemon *party = GetBattlerParty(battler); - u32 currentSpecies = GetMonData(&party[monId], MON_DATA_SPECIES); - u32 targetSpecies; + struct Pokemon *mon = GetBattlerMon(battler); if (!CanBattlerFormChange(battler, method)) return FALSE; - targetSpecies = GetBattleFormChangeTargetSpecies(battler, method); - if (targetSpecies == currentSpecies) - targetSpecies = GetFormChangeTargetSpecies(&party[monId], method, 0); - if (targetSpecies != currentSpecies && targetSpecies != SPECIES_NONE) + u32 currentSpecies = GetMonData(mon, MON_DATA_SPECIES); + u32 targetSpecies = GetBattleFormChangeTargetSpecies(battler, method, ability); + + struct PartyState *battlePartyState = GetBattlerPartyState(battler); + // If the battle ends, and there's not a specified species to change back to, + // use the species at the start of the battle. + if (targetSpecies == SPECIES_NONE + && battlePartyState != NULL && battlePartyState->changedSpecies != SPECIES_NONE + // This is added to prevent FORM_CHANGE_END_BATTLE_ENVIRONMENT from omitting move changes + // at the end of the battle, as it was being counting as a successful form change. + && (method == FORM_CHANGE_END_BATTLE || method == FORM_CHANGE_FAINT)) + { + targetSpecies = battlePartyState->changedSpecies; + } + + assertf(targetSpecies != SPECIES_NONE, "form change target returned NONE. cur:%d, method:%d", currentSpecies, method) + { + return FALSE; + } + + if (targetSpecies != currentSpecies) { // Saves the original species on the first form change. - if (GetBattlerPartyState(battler)->changedSpecies == SPECIES_NONE) GetBattlerPartyState(battler)->changedSpecies = gBattleMons[battler].species; - TryToSetBattleFormChangeMoves(&party[monId], method); - SetMonData(&party[monId], MON_DATA_SPECIES, &targetSpecies); + TryToSetBattleFormChangeMoves(mon, method); + SetMonData(mon, MON_DATA_SPECIES, &targetSpecies); gBattleMons[battler].species = targetSpecies; - RecalcBattlerStats(battler, &party[monId], method == FORM_CHANGE_BATTLE_GIGANTAMAX); + RecalcBattlerStats(battler, mon, method == FORM_CHANGE_BATTLE_GIGANTAMAX); return TRUE; } - else if (GetBattlerPartyState(battler)->changedSpecies != SPECIES_NONE) - { - bool32 restoreSpecies = FALSE; - - switch (method) - { - case FORM_CHANGE_END_BATTLE: - restoreSpecies = TRUE; - break; - case FORM_CHANGE_FAINT: - if (IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler) || IsBattlerInTeraForm(battler) || IsGigantamaxed(battler)) - restoreSpecies = TRUE; - break; - case FORM_CHANGE_BATTLE_SWITCH: - if (IsGigantamaxed(battler)) - restoreSpecies = TRUE; - break; - default: - break; - } - - if (restoreSpecies) - { - enum Ability abilityForm = gBattleMons[battler].ability; - // Reverts the original species - TryToSetBattleFormChangeMoves(&party[monId], method); - u32 changedSpecies = GetBattlerPartyState(battler)->changedSpecies; - SetMonData(&party[monId], MON_DATA_SPECIES, &changedSpecies); - RecalcBattlerStats(battler, &party[monId], method == FORM_CHANGE_BATTLE_GIGANTAMAX); - // Battler data is not updated with regular form's ability, not doing so could cause wrong ability activation. - if (method == FORM_CHANGE_FAINT) - gBattleMons[battler].ability = abilityForm; - return TRUE; - } - } return FALSE; } -bool32 DoBattlersShareType(u32 battler1, u32 battler2) +bool32 DoBattlersShareType(enum BattlerId battler1, enum BattlerId battler2) { s32 i; + s32 j; enum Type types1[3], types2[3]; GetBattlerTypes(battler1, FALSE, types1); GetBattlerTypes(battler2, FALSE, types2); - if (types1[2] == TYPE_MYSTERY) - types1[2] = types1[0]; - if (types2[2] == TYPE_MYSTERY) - types2[2] = types2[0]; - for (i = 0; i < 3; i++) { - if (types1[i] == types2[0] || types1[i] == types2[1] || types1[i] == types2[2]) - return TRUE; + if (types1[i] == TYPE_MYSTERY) + continue; + + for (j = 0; j < 3; j++) + { + if (types2[j] == TYPE_MYSTERY) + continue; + if (types1[i] == types2[j]) + return TRUE; + } } return FALSE; } -bool32 CanBattlerGetOrLoseItem(u32 fromBattler, u32 battler, enum Item itemId) +bool32 CanBattlerGetOrLoseItem(enum BattlerId fromBattler, enum BattlerId battler, enum Item itemId) { u16 species = gBattleMons[fromBattler].species; enum HoldEffect holdEffect = GetItemHoldEffect(itemId); // Raw hold effect @@ -10564,7 +8923,7 @@ bool32 CanBattlerGetOrLoseItem(u32 fromBattler, u32 battler, enum Item itemId) return TRUE; } -u32 GetBattlerVisualSpecies(u32 battler) +u32 GetBattlerVisualSpecies(enum BattlerId battler) { u32 illusionSpecies = GetIllusionMonSpecies(battler); if (illusionSpecies != SPECIES_NONE) @@ -10572,7 +8931,7 @@ u32 GetBattlerVisualSpecies(u32 battler) return gBattleMons[battler].species; } -bool32 TryClearIllusion(u32 battler, enum Ability ability) +bool32 TryClearIllusion(enum BattlerId battler, enum Ability ability) { if (gBattleStruct->illusion[battler].state != ILLUSION_ON) return FALSE; @@ -10584,7 +8943,7 @@ bool32 TryClearIllusion(u32 battler, enum Ability ability) return TRUE; } -struct Pokemon *GetIllusionMonPtr(u32 battler) +struct Pokemon *GetIllusionMonPtr(enum BattlerId battler) { if (gBattleStruct->illusion[battler].state == ILLUSION_NOT_SET) SetIllusionMon(GetBattlerMon(battler), battler); @@ -10594,12 +8953,12 @@ struct Pokemon *GetIllusionMonPtr(u32 battler) return gBattleStruct->illusion[battler].mon; } -void ClearIllusionMon(u32 battler) +void ClearIllusionMon(enum BattlerId battler) { memset(&gBattleStruct->illusion[battler], 0, sizeof(gBattleStruct->illusion[battler])); } -u32 GetIllusionMonSpecies(u32 battler) +u32 GetIllusionMonSpecies(enum BattlerId battler) { struct Pokemon *illusionMon = GetIllusionMonPtr(battler); if (illusionMon != NULL) @@ -10607,7 +8966,7 @@ u32 GetIllusionMonSpecies(u32 battler) return SPECIES_NONE; } -u32 GetIllusionMonPartyId(struct Pokemon *party, struct Pokemon *mon, struct Pokemon *partnerMon, u32 battler) +u32 GetIllusionMonPartyId(struct Pokemon *party, struct Pokemon *mon, struct Pokemon *partnerMon, enum BattlerId battler) { s32 partyEnd=6; s32 partyStart=0; @@ -10647,7 +9006,7 @@ u32 GetIllusionMonPartyId(struct Pokemon *party, struct Pokemon *mon, struct Pok return PARTY_SIZE; } -void SetIllusionMon(struct Pokemon *mon, u32 battler) +void SetIllusionMon(struct Pokemon *mon, enum BattlerId battler) { struct Pokemon *party, *partnerMon; u32 id; @@ -10671,7 +9030,7 @@ void SetIllusionMon(struct Pokemon *mon, u32 battler) } } -enum ImmunityHealStatusOutcome TryImmunityAbilityHealStatus(u32 battler) +enum ImmunityHealStatusOutcome TryImmunityAbilityHealStatus(enum BattlerId battler) { enum ImmunityHealStatusOutcome outcome = IMMUNITY_NO_EFFECT; switch (GetBattlerAbilityIgnoreMoldBreaker(battler)) @@ -10770,7 +9129,7 @@ uq4_12_t GetBadgeBoostModifier(void) return UQ_4_12(1.1); } -bool32 ShouldGetStatBadgeBoost(u16 badgeFlag, u32 battler) +bool32 ShouldGetStatBadgeBoost(u16 badgeFlag, enum BattlerId battler) { if (GetConfig(CONFIG_BADGE_BOOST) <= GEN_3 && badgeFlag != 0) { @@ -10816,7 +9175,7 @@ enum DamageCategory GetBattleMoveCategory(enum Move move) return GetMoveCategory(move); } -void SetDynamicMoveCategory(u32 battlerAtk, u32 battlerDef, enum Move move) +void SetDynamicMoveCategory(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move) { switch (GetMoveEffect(move)) { @@ -10841,7 +9200,7 @@ void SetDynamicMoveCategory(u32 battlerAtk, u32 battlerDef, enum Move move) } } -static bool32 TryRemoveScreens(u32 battler) +static bool32 TryRemoveScreens(enum BattlerId battler) { bool32 removed = FALSE; u32 battlerSide = GetBattlerSide(battler); @@ -10865,7 +9224,7 @@ static bool32 TryRemoveScreens(u32 battler) } // Photon Geyser, Light That Burns the Sky, Tera Blast -enum DamageCategory GetCategoryBasedOnStats(u32 battler) +enum DamageCategory GetCategoryBasedOnStats(enum BattlerId battler) { u32 attack = gBattleMons[battler].attack; u32 spAttack = gBattleMons[battler].spAttack; @@ -10895,7 +9254,7 @@ static u32 GetFlingPowerFromItemId(enum Item itemId) return GetItemFlingPower(itemId); } -bool32 CanFling(u32 battlerAtk, u32 battlerDef) +bool32 CanFling(enum BattlerId battlerAtk) { enum Item item = gBattleMons[battlerAtk].item; @@ -10903,8 +9262,9 @@ bool32 CanFling(u32 battlerAtk, u32 battlerDef) || (GetConfig(CONFIG_KLUTZ_FLING_INTERACTION) >= GEN_5 && GetBattlerAbility(battlerAtk) == ABILITY_KLUTZ) || gFieldStatuses & STATUS_FIELD_MAGIC_ROOM || gBattleMons[battlerAtk].volatiles.embargo + || (GetItemTMHMIndex(item) != 0 && GetItemImportance(item) == 1) // don't fling reusable TMs || GetFlingPowerFromItemId(item) == 0 - || !CanBattlerGetOrLoseItem(battlerAtk, battlerDef, item)) + || !CanBattlerGetOrLoseItem(battlerAtk, battlerAtk, item)) // defender being a paradox mon doesn't matter return FALSE; return TRUE; @@ -10912,14 +9272,15 @@ bool32 CanFling(u32 battlerAtk, u32 battlerDef) // Sort an array of battlers by speed // Useful for effects like pickpocket, eject button, red card, dancer -void SortBattlersBySpeed(u8 *battlers, bool32 slowToFast) +void SortBattlersBySpeed(enum BattlerId *battlers, bool32 slowToFast) { - int i, j, currSpeed, currBattler; + int i, j, currSpeed; + enum BattlerId currBattler; u16 speeds[MAX_BATTLERS_COUNT] = {0}; for (i = 0; i < gBattlersCount; i++) { - u32 battler = battlers[i]; + enum BattlerId battler = battlers[i]; speeds[i] = GetBattlerTotalSpeedStat(battler, GetBattlerAbility(battler), GetBattlerHoldEffect(battler)); } @@ -10976,7 +9337,7 @@ void TryRestoreHeldItems(void) } } -bool32 CanStealItem(u32 battlerStealing, u32 battlerItem, enum Item item) +bool32 CanStealItem(enum BattlerId battlerStealing, enum BattlerId battlerItem, enum Item item) { enum BattleSide stealerSide = GetBattlerSide(battlerStealing); @@ -11008,6 +9369,7 @@ bool32 CanStealItem(u32 battlerStealing, u32 battlerItem, enum Item item) } // It's supposed to pop before trying to steal but this also works + // Now that the order is correct this is redundant. The question is whether Trick can steal it. if (GetItemHoldEffect(item) == HOLD_EFFECT_AIR_BALLOON) return FALSE; @@ -11018,7 +9380,7 @@ bool32 CanStealItem(u32 battlerStealing, u32 battlerItem, enum Item item) return TRUE; } -void TrySaveExchangedItem(u32 battler, enum Item stolenItem) +void TrySaveExchangedItem(enum BattlerId battler, enum Item stolenItem) { // Because BtlController_EmitSetMonData does SetMonData, we need to save the stolen item only if it matches the battler's original // So, if the player steals an item during battle and has it stolen from it, it will not end the battle with it (naturally) @@ -11032,7 +9394,7 @@ void TrySaveExchangedItem(u32 battler, enum Item stolenItem) gBattleStruct->itemLost[B_SIDE_PLAYER][gBattlerPartyIndexes[battler]].stolen = TRUE; } -bool32 IsBattlerAffectedByHazards(u32 battler, enum HoldEffect holdEffect, bool32 toxicSpikes) +bool32 IsBattlerAffectedByHazards(enum BattlerId battler, enum HoldEffect holdEffect, bool32 toxicSpikes) { bool32 ret = TRUE; if (!IsBattlerAlive(battler)) @@ -11058,7 +9420,7 @@ bool32 IsSheerForceAffected(enum Move move, enum Ability ability) } // This function is the body of "jumpifstat", but can be used dynamically in a function -bool32 CompareStat(u32 battler, enum Stat statId, u32 cmpTo, u32 cmpKind, enum Ability ability) +bool32 CompareStat(enum BattlerId battler, enum Stat statId, u32 cmpTo, u32 cmpKind, enum Ability ability) { bool32 ret = FALSE; u32 statValue = gBattleMons[battler].statStages[statId]; @@ -11109,7 +9471,7 @@ bool32 CompareStat(u32 battler, enum Stat statId, u32 cmpTo, u32 cmpKind, enum A return ret; } -bool32 BlocksPrankster(enum Move move, u32 battlerPrankster, u32 battlerDef, bool32 checkTarget) +bool32 BlocksPrankster(enum Move move, enum BattlerId battlerPrankster, enum BattlerId battlerDef, bool32 checkTarget) { if (GetConfig(CONFIG_PRANKSTER_DARK_TYPES) < GEN_7) return FALSE; @@ -11129,18 +9491,19 @@ bool32 BlocksPrankster(enum Move move, u32 battlerPrankster, u32 battlerDef, boo return TRUE; } -bool32 CantPickupItem(u32 battler) +// Not enum BattlerId to allow using it with RandomUniformExcept +bool32 CantPickupItem(u32 _battler) { + enum BattlerId battler = _battler; // Used by RandomUniformExcept() for RNG_PICKUP if (battler == gBattlerAttacker && (GetConfig(CONFIG_PICKUP_WILD) < GEN_9 || gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_LINK))) return TRUE; return !(IsBattlerAlive(battler) && GetBattlerPartyState(battler)->usedHeldItem && gBattleStruct->battlerState[battler].canPickupItem); } -bool32 PickupHasValidTarget(u32 battler) +bool32 PickupHasValidTarget(enum BattlerId battler) { - u32 i; - for (i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (!CantPickupItem(i)) return TRUE; @@ -11148,7 +9511,7 @@ bool32 PickupHasValidTarget(u32 battler) return FALSE; } -bool32 IsBattlerWeatherAffected(u32 battler, u32 weatherFlags) +bool32 IsBattlerWeatherAffected(enum BattlerId battler, u32 weatherFlags) { if (gBattleWeather & weatherFlags && HasWeatherEffect()) { @@ -11161,14 +9524,14 @@ bool32 IsBattlerWeatherAffected(u32 battler, u32 weatherFlags) return FALSE; } -static u32 CanBattlerHitBothFoesInTerrain(u32 battler, enum Move move, enum BattleMoveEffects effect) +static u32 CanBattlerHitBothFoesInTerrain(enum BattlerId battler, enum Move move, enum BattleMoveEffects effect) { return effect == EFFECT_TERRAIN_BOOST && GetMoveTerrainBoost_HitsBothFoes(move) && IsBattlerTerrainAffected(battler, GetBattlerAbility(battler), GetBattlerHoldEffect(battler), gFieldStatuses, GetMoveTerrainBoost_Terrain(move)); } -enum MoveTarget GetBattlerMoveTargetType(u32 battler, enum Move move) +enum MoveTarget GetBattlerMoveTargetType(enum BattlerId battler, enum Move move) { enum BattleMoveEffects effect = GetMoveEffect(move); if (effect == EFFECT_CURSE && !IS_BATTLER_OF_TYPE(battler, TYPE_GHOST)) @@ -11181,7 +9544,7 @@ enum MoveTarget GetBattlerMoveTargetType(u32 battler, enum Move move) return GetMoveTarget(move); } -bool32 CanTargetBattler(u32 battlerAtk, u32 battlerDef, enum Move move) +bool32 CanTargetBattler(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move) { if (GetMoveEffect(move) == EFFECT_HIT_ENEMY_HEAL_ALLY && IsBattlerAlly(battlerAtk, battlerDef) @@ -11196,7 +9559,7 @@ bool32 CanTargetBattler(u32 battlerAtk, u32 battlerDef, enum Move move) u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent) { - u32 battler; + enum BattlerId battler; for (battler = 0; battler < MAX_BATTLERS_COUNT; battler++) { if (battler == gBattlerAttacker || !IsBattlerAlive(battler)) @@ -11210,17 +9573,7 @@ u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent) return battler; } -static void SetRandomMultiHitCounter() -{ - if (GetBattlerHoldEffect(gBattlerAttacker) == HOLD_EFFECT_LOADED_DICE) - gMultiHitCounter = RandomUniform(RNG_LOADED_DICE, 4, 5); - else if (GetConfig(CONFIG_MULTI_HIT_CHANCE) >= GEN_5) - gMultiHitCounter = RandomWeighted(RNG_HITS, 0, 0, 7, 7, 3, 3); // 35%: 2 hits, 35%: 3 hits, 15% 4 hits, 15% 5 hits. - else - gMultiHitCounter = RandomWeighted(RNG_HITS, 0, 0, 3, 3, 1, 1); // 37.5%: 2 hits, 37.5%: 3 hits, 12.5% 4 hits, 12.5% 5 hits. -} - -void CopyMonLevelAndBaseStatsToBattleMon(u32 battler, struct Pokemon *mon) +void CopyMonLevelAndBaseStatsToBattleMon(enum BattlerId battler, struct Pokemon *mon) { gBattleMons[battler].level = GetMonData(mon, MON_DATA_LEVEL); gBattleMons[battler].hp = GetMonData(mon, MON_DATA_HP); @@ -11232,15 +9585,24 @@ void CopyMonLevelAndBaseStatsToBattleMon(u32 battler, struct Pokemon *mon) gBattleMons[battler].spDefense = GetMonData(mon, MON_DATA_SPDEF); } -void CopyMonAbilityAndTypesToBattleMon(u32 battler, struct Pokemon *mon) +void CopyMonAbilityAndTypesToBattleMon(enum BattlerId battler, struct Pokemon *mon) { gBattleMons[battler].ability = GetMonAbility(mon); + #if TESTING + if (gTestRunnerEnabled) + { + u32 array = (!IsPartnerMonFromSameTrainer(battler)) ? battler : GetBattlerSide(battler); + u32 partyIndex = gBattlerPartyIndexes[battler]; + if (TestRunner_Battle_GetForcedAbility(array, partyIndex)) + gBattleMons[battler].ability = TestRunner_Battle_GetForcedAbility(array, partyIndex); + } + #endif gBattleMons[battler].types[0] = GetSpeciesType(gBattleMons[battler].species, 0); gBattleMons[battler].types[1] = GetSpeciesType(gBattleMons[battler].species, 1); gBattleMons[battler].types[2] = TYPE_MYSTERY; } -void RecalcBattlerStats(u32 battler, struct Pokemon *mon, bool32 isDynamaxing) +void RecalcBattlerStats(enum BattlerId battler, struct Pokemon *mon, bool32 isDynamaxing) { u32 hp = GetMonData(mon, MON_DATA_HP); u32 oldMaxHp = GetMonData(mon, MON_DATA_MAX_HP); @@ -11266,19 +9628,19 @@ void RecalcBattlerStats(u32 battler, struct Pokemon *mon, bool32 isDynamaxing) CopyMonAbilityAndTypesToBattleMon(battler, mon); } -void RemoveConfusionStatus(u32 battler) +void RemoveConfusionStatus(enum BattlerId battler) { gBattleMons[battler].volatiles.confusionTurns = 0; gBattleMons[battler].volatiles.infiniteConfusion = FALSE; } -u32 GetBattlerGender(u32 battler) +u32 GetBattlerGender(enum BattlerId battler) { return GetGenderFromSpeciesAndPersonality(gBattleMons[battler].species, gBattleMons[battler].personality); } -bool32 AreBattlersOfOppositeGender(u32 battler1, u32 battler2) +bool32 AreBattlersOfOppositeGender(enum BattlerId battler1, enum BattlerId battler2) { u32 gender1 = GetBattlerGender(battler1); u32 gender2 = GetBattlerGender(battler2); @@ -11286,7 +9648,7 @@ bool32 AreBattlersOfOppositeGender(u32 battler1, u32 battler2) return (gender1 != MON_GENDERLESS && gender2 != MON_GENDERLESS && gender1 != gender2); } -bool32 AreBattlersOfSameGender(u32 battler1, u32 battler2) +bool32 AreBattlersOfSameGender(enum BattlerId battler1, enum BattlerId battler2) { u32 gender1 = GetBattlerGender(battler1); u32 gender2 = GetBattlerGender(battler2); @@ -11294,7 +9656,7 @@ bool32 AreBattlersOfSameGender(u32 battler1, u32 battler2) return (gender1 != MON_GENDERLESS && gender2 != MON_GENDERLESS && gender1 == gender2); } -u32 CalcSecondaryEffectChance(u32 battler, enum Ability battlerAbility, const struct AdditionalEffect *additionalEffect) +u32 CalcSecondaryEffectChance(enum BattlerId battler, enum Ability battlerAbility, const struct AdditionalEffect *additionalEffect) { bool8 hasSereneGrace = (battlerAbility == ABILITY_SERENE_GRACE); bool8 hasRainbow = (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_RAINBOW) != 0; @@ -11311,7 +9673,7 @@ u32 CalcSecondaryEffectChance(u32 battler, enum Ability battlerAbility, const st return secondaryEffectChance; } -bool32 MoveEffectIsGuaranteed(u32 battler, enum Ability battlerAbility, const struct AdditionalEffect *additionalEffect) +bool32 MoveEffectIsGuaranteed(enum BattlerId battler, enum Ability battlerAbility, const struct AdditionalEffect *additionalEffect) { return additionalEffect->chance == 0 || CalcSecondaryEffectChance(battler, battlerAbility, additionalEffect) >= 100; } @@ -11411,7 +9773,7 @@ bool32 CanMonParticipateInSkyBattle(struct Pokemon *mon) return FALSE; } -void GetBattlerTypes(u32 battler, bool32 ignoreTera, enum Type types[static 3]) +void GetBattlerTypes(enum BattlerId battler, bool32 ignoreTera, enum Type types[static 3]) { // Terastallization. bool32 isTera = GetActiveGimmick(battler) == GIMMICK_TERA; @@ -11441,14 +9803,14 @@ void GetBattlerTypes(u32 battler, bool32 ignoreTera, enum Type types[static 3]) } } -enum Type GetBattlerType(u32 battler, u32 typeIndex, bool32 ignoreTera) +enum Type GetBattlerType(enum BattlerId battler, u32 typeIndex, bool32 ignoreTera) { enum Type types[3]; GetBattlerTypes(battler, ignoreTera, types); return types[typeIndex]; } -void RemoveBattlerType(u32 battler, enum Type type) +void RemoveBattlerType(enum BattlerId battler, enum Type type) { u32 i; if (GetActiveGimmick(battler) == GIMMICK_TERA) // don't remove type if Terastallized @@ -11462,7 +9824,7 @@ void RemoveBattlerType(u32 battler, enum Type type) void SetShellSideArmCategory(void) { - u32 battlerAtk, battlerDef; + enum BattlerId battlerAtk, battlerDef; u32 attackerAtkStat; u32 targetDefStat; u32 attackerSpAtkStat; @@ -11519,14 +9881,14 @@ void SetShellSideArmCategory(void) } } -bool32 CanTargetPartner(u32 battlerAtk, u32 battlerDef) +bool32 CanTargetPartner(enum BattlerId battlerAtk, enum BattlerId battlerDef) { return (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battlerDef)) && battlerDef != BATTLE_PARTNER(battlerAtk)); } -bool32 IsBattlerUnaffectedByMove(u32 battler) +bool32 IsBattlerUnaffectedByMove(enum BattlerId battler) { return gBattleStruct->moveResultFlags[battler] & MOVE_RESULT_NO_EFFECT; } @@ -11546,7 +9908,7 @@ enum Type GetBattleMoveType(enum Move move) return GetMoveType(move); } -void TryActivateSleepClause(u32 battler, u32 indexInParty) +void TryActivateSleepClause(enum BattlerId battler, u32 indexInParty) { if (gBattleStruct->battlerState[battler].sleepClauseEffectExempt) { @@ -11585,7 +9947,7 @@ bool32 IsSleepClauseEnabled(void) void ClearDamageCalcResults(void) { - for (u32 battler = 0; battler < MAX_BATTLERS_COUNT; battler++) + for (enum BattlerId battler = 0; battler < MAX_BATTLERS_COUNT; battler++) { gBattleStruct->moveDamage[battler] = 0; gBattleStruct->moveResultFlags[battler] = 0; @@ -11607,7 +9969,7 @@ void ClearDamageCalcResults(void) gBattleStruct->moldBreakerActive = FALSE; } -bool32 DoesDestinyBondFail(u32 battler) +bool32 DoesDestinyBondFail(enum BattlerId battler) { return GetConfig(CONFIG_DESTINY_BOND_FAIL) >= GEN_7 && gBattleMons[battler].volatiles.destinyBond; } @@ -11629,12 +9991,12 @@ bool32 IsMoveEffectBlockedByTarget(enum Ability ability) return FALSE; } -bool32 SetTargetToNextPursuiter(u32 battlerDef) +bool32 SetTargetToNextPursuiter(enum BattlerId battlerDef) { u32 i; for (i = gCurrentTurnActionNumber + 1; i < gBattlersCount; i++) { - u32 battler = gBattlerByTurnOrder[i]; + enum BattlerId battler = gBattlerByTurnOrder[i]; if (gChosenActionByBattler[battler] == B_ACTION_USE_MOVE && GetMoveEffect(gChosenMoveByBattler[battler]) == EFFECT_PURSUIT && IsBattlerAlive(battlerDef) @@ -11654,7 +10016,7 @@ bool32 SetTargetToNextPursuiter(u32 battlerDef) bool32 IsPursuitTargetSet(void) { - for (u32 battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { if (gBattleStruct->battlerState[battler].pursuitTarget) return TRUE; @@ -11664,12 +10026,12 @@ bool32 IsPursuitTargetSet(void) void ClearPursuitValues(void) { - for (u32 i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) gBattleStruct->battlerState[i].pursuitTarget = FALSE; gBattleStruct->pursuitStoredSwitch = PARTY_SIZE; } -void ClearPursuitValuesIfSet(u32 battler) +void ClearPursuitValuesIfSet(enum BattlerId battler) { if (gBattleStruct->battlerState[battler].pursuitTarget) ClearPursuitValues(); @@ -11677,7 +10039,7 @@ void ClearPursuitValuesIfSet(u32 battler) bool32 HasWeatherEffect(void) { - for (u32 battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { if (!IsBattlerAlive(battler)) continue; @@ -11737,7 +10099,7 @@ bool32 TrySwitchInEjectPack(enum EjectPackTiming timing) u32 ejectPackBattlers = 0; u32 numEjectPackBattlers = 0; - for (u32 i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].volatiles.tryEjectPack && GetBattlerHoldEffect(i) == HOLD_EFFECT_EJECT_PACK @@ -11752,16 +10114,16 @@ bool32 TrySwitchInEjectPack(enum EjectPackTiming timing) if (numEjectPackBattlers == 0) return FALSE; - u8 battlers[4] = {0, 1, 2, 3}; + enum BattlerId battlers[MAX_BATTLERS_COUNT] = {0, 1, 2, 3}; if (numEjectPackBattlers > 1) SortBattlersBySpeed(battlers, FALSE); - for (u32 i = 0; i < gBattlersCount; i++) + for (enum BattlerId i = 0; i < gBattlersCount; i++) gBattleMons[i].volatiles.tryEjectPack = FALSE; for (u32 i = 0; i < gBattlersCount; i++) { - u32 battler = battlers[i]; + enum BattlerId battler = battlers[i]; if (!(ejectPackBattlers & 1u << battler)) continue; @@ -11779,7 +10141,7 @@ bool32 TrySwitchInEjectPack(enum EjectPackTiming timing) return FALSE; } -bool32 EmergencyExitCanBeTriggered(u32 battler) +bool32 EmergencyExitCanBeTriggered(enum BattlerId battler) { enum Ability ability = GetBattlerAbility(battler); @@ -11796,7 +10158,7 @@ bool32 EmergencyExitCanBeTriggered(u32 battler) return FALSE; } -bool32 TryTriggerSymbiosis(u32 battler, u32 ally) +bool32 TryTriggerSymbiosis(enum BattlerId battler, u32 ally) { return GetBattlerAbility(ally) == ABILITY_SYMBIOSIS && gBattleMons[battler].item == ITEM_NONE @@ -11808,10 +10170,9 @@ bool32 TryTriggerSymbiosis(u32 battler, u32 ally) } // Called by Cmd_removeitem. itemId represents the item that was removed, not being given. -bool32 TrySymbiosis(u32 battler, enum Item itemId, bool32 moveEnd) +bool32 TrySymbiosis(enum BattlerId battler, enum Item itemId, bool32 moveEnd) { if (!gBattleStruct->itemLost[B_SIDE_PLAYER][gBattlerPartyIndexes[battler]].stolen - && gBattleStruct->changedItems[battler] == ITEM_NONE && GetBattlerHoldEffect(battler) != HOLD_EFFECT_EJECT_BUTTON && GetBattlerHoldEffect(battler) != HOLD_EFFECT_EJECT_PACK && (GetConfig(CONFIG_SYMBIOSIS_GEMS) < GEN_7 || !(gSpecialStatuses[battler].gemBoost)) @@ -11834,7 +10195,7 @@ bool32 TrySymbiosis(u32 battler, enum Item itemId, bool32 moveEnd) } // Used by Bestow and Symbiosis to take an item from one battler and give to another. -void BestowItem(u32 battlerAtk, u32 battlerDef) +void BestowItem(enum BattlerId battlerAtk, enum BattlerId battlerDef) { gLastUsedItem = gBattleMons[battlerAtk].item; @@ -11854,7 +10215,7 @@ void BestowItem(u32 battlerAtk, u32 battlerDef) // Gets the value of a volatile status flag for a certain battler // Primarily used for the debug menu and scripts. Outside of it explicit references are preferred // Uses Arm because there is a compiler bug when it tries to compile in thumb -ARM_FUNC u32 GetBattlerVolatile(u32 battler, enum Volatile _volatile) +ARM_FUNC u32 GetBattlerVolatile(enum BattlerId battler, enum Volatile _volatile) { switch (_volatile) { @@ -11872,7 +10233,7 @@ ARM_FUNC u32 GetBattlerVolatile(u32 battler, enum Volatile _volatile) // Sets the value of a volatile status flag for a certain battler // Primarily used for the debug menu and scripts. Outside of it explicit references are preferred -void SetMonVolatile(u32 battler, enum Volatile _volatile, u32 newValue) +void SetMonVolatile(enum BattlerId battler, enum Volatile _volatile, u32 newValue) { switch (_volatile) { @@ -11887,7 +10248,7 @@ void SetMonVolatile(u32 battler, enum Volatile _volatile, u32 newValue) } } -bool32 ItemHealMonVolatile(u32 battler, enum Item itemId) +bool32 ItemHealMonVolatile(enum BattlerId battler, enum Item itemId) { bool32 statusChanged = FALSE; const u8 *effect = GetItemEffect(itemId); @@ -11989,21 +10350,19 @@ void RemoveHazardFromField(enum BattleSide side, enum Hazards hazardType) } } -static bool32 CanToxicSkipAccuracyCheck(u32 battlerAtk, u32 move) +static bool32 CanMoveSkipAccuracyCheck(enum BattlerId battlerAtk, u32 move) { - if (GetConfig(CONFIG_TOXIC_NEVER_MISS) < GEN_6) - return FALSE; - return move == MOVE_TOXIC && IS_BATTLER_OF_TYPE(battlerAtk, TYPE_POISON); + return MoveAlwaysHitsOnSameType(move) && IS_BATTLER_OF_TYPE(battlerAtk, GetMoveType(move)); } -bool32 CanMoveSkipAccuracyCalc(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum Move move, enum ResultOption option) +bool32 CanMoveSkipAccuracyCalc(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum Move move, enum ResultOption option) { bool32 effect = FALSE; enum Ability ability = ABILITY_NONE; enum BattleMoveEffects moveEffect = GetMoveEffect(move); if ((gBattleMons[battlerDef].volatiles.lockOn && gBattleMons[battlerDef].volatiles.battlerWithSureHit == battlerAtk) - || CanToxicSkipAccuracyCheck(battlerAtk, move) + || CanMoveSkipAccuracyCheck(battlerAtk, move) || gBattleMons[battlerDef].volatiles.glaiveRush) { effect = TRUE; @@ -12026,8 +10385,7 @@ bool32 CanMoveSkipAccuracyCalc(u32 battlerAtk, u32 battlerDef, enum Ability abil // If the target is under the effects of Telekinesis, and the move isn't a OH-KO move, move hits. else if (gBattleMons[battlerDef].volatiles.telekinesis && !IsSemiInvulnerable(battlerDef, CHECK_ALL) - && moveEffect != EFFECT_OHKO - && moveEffect != EFFECT_SHEER_COLD) + && moveEffect != EFFECT_OHKO) { effect = TRUE; } @@ -12067,7 +10425,7 @@ bool32 CanMoveSkipAccuracyCalc(u32 battlerAtk, u32 battlerDef, enum Ability abil return effect; } -u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, enum Move move, enum Ability atkAbility, enum Ability defAbility, enum HoldEffect atkHoldEffect, enum HoldEffect defHoldEffect) +u32 GetTotalAccuracy(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, enum Ability atkAbility, enum Ability defAbility, enum HoldEffect atkHoldEffect, enum HoldEffect defHoldEffect) { u32 calc, moveAcc; s8 buff, accStage, evasionStage; @@ -12143,7 +10501,7 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, enum Move move, enum Abilit } // Attacker's ally's ability - u32 atkAlly = BATTLE_PARTNER(battlerAtk); + enum BattlerId atkAlly = BATTLE_PARTNER(battlerAtk); switch (GetBattlerAbility(atkAlly)) { case ABILITY_VICTORY_STAR: @@ -12154,6 +10512,9 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, enum Move move, enum Abilit break; } + if (MoveHasIncreasedAccByTenOnSameType(move) && !IS_BATTLER_OF_TYPE(battlerAtk, GetMoveType(move))) + calc = (calc * 110) / 100; + // Attacker's hold effect switch (atkHoldEffect) { @@ -12199,20 +10560,99 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, enum Move move, enum Abilit return calc; } -bool32 IsSemiInvulnerable(u32 battler, enum SemiInvulnerableExclusion excludeCommander) +bool32 DoesOHKOMoveMissTarget(struct BattleCalcValues *cv) +{ + enum OHKOResult { + NO_HIT, + CALC_ACC, + SURE_HIT, + }; + + // Dynamaxed Pokemon cannot be hit by OHKO moves. + if (GetActiveGimmick(cv->battlerDef) == GIMMICK_DYNAMAX) + { + gBattleStruct->moveResultFlags[cv->battlerDef] |= MOVE_RESULT_ONE_HIT_KO_NO_AFFECT; + return TRUE; + } + + if (cv->abilities[cv->battlerDef] == ABILITY_STURDY) + { + gBattleStruct->moveResultFlags[cv->battlerDef] |= MOVE_RESULT_ONE_HIT_KO_STURDY; + return TRUE; + } + + enum OHKOResult lands = NO_HIT; + + if (gBattleMons[cv->battlerDef].level > gBattleMons[cv->battlerAtk].level) + { + lands = NO_HIT; + } + else if ((gBattleMons[cv->battlerDef].volatiles.lockOn && gBattleMons[cv->battlerDef].volatiles.battlerWithSureHit == cv->battlerAtk) + || IsAbilityAndRecord(cv->battlerAtk, cv->abilities[cv->battlerAtk], ABILITY_NO_GUARD) + || IsAbilityAndRecord(cv->battlerDef, cv->abilities[cv->battlerDef], ABILITY_NO_GUARD)) + { + lands = SURE_HIT; + } + else + { + lands = CALC_ACC; + } + + if (lands == CALC_ACC) + { + u32 odds = GetMoveAccuracy(cv->move) + (gBattleMons[cv->battlerAtk].level - gBattleMons[cv->battlerDef].level); + if (MoveHasIncreasedAccByTenOnSameType(cv->move) && !IS_BATTLER_OF_TYPE(cv->battlerAtk, GetMoveType(cv->move))) + odds -= 10; + if (RandomPercentage(RNG_ACCURACY, odds) && gBattleMons[cv->battlerAtk].level >= gBattleMons[cv->battlerDef].level) + lands = SURE_HIT; + } + + if (lands == SURE_HIT) + { + gBattleStruct->moveResultFlags[cv->battlerDef] |= MOVE_RESULT_ONE_HIT_KO_NO_AFFECT; + return FALSE; + } + + if (gBattleMons[cv->battlerAtk].level < gBattleMons[cv->battlerDef].level) + gBattleStruct->moveResultFlags[cv->battlerDef] |= MOVE_RESULT_ONE_HIT_KO_NO_AFFECT; + + return TRUE; +} + +bool32 DoesMoveMissTarget(struct BattleCalcValues *cv) +{ + if (GetMoveEffect(cv->move) == EFFECT_OHKO) + return DoesOHKOMoveMissTarget(cv); + + if (CanMoveSkipAccuracyCalc(cv->battlerAtk, cv->battlerDef, cv->abilities[cv->battlerAtk], cv->abilities[cv->battlerDef], cv->move, RUN_SCRIPT)) + return FALSE; + + u32 accuracy = GetTotalAccuracy( + cv->battlerAtk, + cv->battlerDef, + cv->move, + cv->abilities[cv->battlerAtk], + cv->abilities[cv->battlerDef], + cv->holdEffects[cv->battlerAtk], + cv->holdEffects[cv->battlerDef] + ); + return !RandomPercentage(RNG_ACCURACY, accuracy); +} + +bool32 IsSemiInvulnerable(enum BattlerId battler, enum SemiInvulnerableExclusion excludeCommander) { if (gBattleMons[battler].volatiles.semiInvulnerable == STATE_COMMANDER) return excludeCommander != EXCLUDE_COMMANDER; return gBattleMons[battler].volatiles.semiInvulnerable != STATE_NONE; } -bool32 BreaksThroughSemiInvulnerablity(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum Move move) +bool32 BreaksThroughSemiInvulnerablity(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum Move move) { enum SemiInvulnerableState state = gBattleMons[battlerDef].volatiles.semiInvulnerable; if (state != STATE_COMMANDER) { - if (CanToxicSkipAccuracyCheck(battlerAtk, move)) + if (CanMoveSkipAccuracyCheck(battlerAtk, move)) return TRUE; if (abilityAtk == ABILITY_NO_GUARD || abilityDef == ABILITY_NO_GUARD) return TRUE; @@ -12241,7 +10681,7 @@ bool32 BreaksThroughSemiInvulnerablity(u32 battlerAtk, u32 battlerDef, enum Abil return FALSE; } -bool32 HasPartnerTrainer(u32 battler) +bool32 HasPartnerTrainer(enum BattlerId battler) { if ((GetBattlerSide(battler) == B_SIDE_PLAYER && gBattleTypeFlags & BATTLE_TYPE_PLAYER_HAS_PARTNER) || (GetBattlerSide(battler) == B_SIDE_OPPONENT && gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)) @@ -12250,9 +10690,9 @@ bool32 HasPartnerTrainer(u32 battler) return FALSE; } -static bool32 IsOpposingSideEmpty(u32 battler) +static bool32 IsOpposingSideEmpty(enum BattlerId battler) { - u32 oppositeBattler = BATTLE_OPPOSITE(battler); + enum BattlerId oppositeBattler = BATTLE_OPPOSITE(battler); if (IsBattlerAlive(oppositeBattler)) return FALSE; @@ -12265,7 +10705,7 @@ static bool32 IsOpposingSideEmpty(u32 battler) return TRUE; } -bool32 IsAffectedByPowderMove(u32 battler, enum Ability ability, enum HoldEffect holdEffect) +bool32 IsAffectedByPowderMove(enum BattlerId battler, enum Ability ability, enum HoldEffect holdEffect) { if (GetConfig(CONFIG_POWDER_OVERCOAT) >= GEN_6 && ability == ABILITY_OVERCOAT) return FALSE; @@ -12276,184 +10716,7 @@ bool32 IsAffectedByPowderMove(u32 battler, enum Ability ability, enum HoldEffect return TRUE; } -static enum Move GetMirrorMoveMove(void) -{ - s32 i, validMovesCount; - enum Move move = MOVE_NONE; - u16 validMoves[MAX_BATTLERS_COUNT] = {0}; - - for (validMovesCount = 0, i = 0; i < gBattlersCount; i++) - { - if (i != gBattlerAttacker) - { - move = gBattleStruct->lastTakenMoveFrom[gBattlerAttacker][i]; - if (move != MOVE_NONE && move != MOVE_UNAVAILABLE) - { - validMoves[validMovesCount] = move; - validMovesCount++; - } - } - } - - move = gBattleStruct->lastTakenMove[gBattlerAttacker]; - if ((move == MOVE_NONE || move == MOVE_UNAVAILABLE) && validMovesCount != 0) - move = validMoves[Random() % validMovesCount]; - - if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IsBattleMoveStatus(move)) - move = GetTypeBasedZMove(move); - - return move; -} - -static bool32 InvalidMetronomeMove(u32 move) -{ - return GetMoveEffect(move) == EFFECT_PLACEHOLDER - || IsMoveMetronomeBanned(move); -} - -static enum Move GetMetronomeMove(void) -{ - enum Move move = MOVE_NONE; - -#if B_METRONOME_MOVES >= GEN_9 - u32 moveCount = MOVES_COUNT_GEN9; -#elif B_METRONOME_MOVES >= GEN_8 - u32 moveCount = MOVES_COUNT_GEN8; -#elif B_METRONOME_MOVES >= GEN_7 - u32 moveCount = MOVES_COUNT_GEN7; -#elif B_METRONOME_MOVES >= GEN_6 - u32 moveCount = MOVES_COUNT_GEN6; -#elif B_METRONOME_MOVES >= GEN_5 - u32 moveCount = MOVES_COUNT_GEN5; -#elif B_METRONOME_MOVES >= GEN_4 - u32 moveCount = MOVES_COUNT_GEN4; -#elif B_METRONOME_MOVES >= GEN_3 - u32 moveCount = MOVES_COUNT_GEN3; -#elif B_METRONOME_MOVES >= GEN_2 - u32 moveCount = MOVES_COUNT_GEN2; -#else - u32 moveCount = MOVES_COUNT_GEN1; -#endif - - move = RandomUniformExcept(RNG_METRONOME, 1, moveCount - 1, InvalidMetronomeMove); - return move; -} - -static enum Move GetAssistMove(void) -{ - enum Move move = MOVE_NONE; - s32 chooseableMovesNo = 0; - struct Pokemon *party; - enum Move *validMoves = Alloc(sizeof(u16) * PARTY_SIZE * MAX_MON_MOVES); - - if (validMoves != NULL) - { - party = GetBattlerParty(gBattlerAttacker); - - for (u32 monId = 0; monId < PARTY_SIZE; monId++) - { - if (monId == gBattlerPartyIndexes[gBattlerAttacker]) - continue; - if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_NONE) - continue; - if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG) - continue; - - for (u32 moveId = 0; moveId < MAX_MON_MOVES; moveId++) - { - enum Move move = GetMonData(&party[monId], MON_DATA_MOVE1 + moveId); - - if (IsMoveAssistBanned(move)) - continue; - - validMoves[chooseableMovesNo++] = move; - } - } - } - - if (chooseableMovesNo) - move = validMoves[Random() % chooseableMovesNo]; - - TRY_FREE_AND_SET_NULL(validMoves); - - return move; -} - -enum Move GetNaturePowerMove(u32 battler) -{ - enum Move move = gBattleEnvironmentInfo[gBattleEnvironment].naturePower; - if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN) - move = MOVE_MOONBLAST; - else if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) - move = MOVE_THUNDERBOLT; - else if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN) - move = MOVE_ENERGY_BALL; - else if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN) - move = MOVE_PSYCHIC; - else if (gBattleEnvironmentInfo[gBattleEnvironment].naturePower == MOVE_NONE) - move = B_NATURE_POWER_MOVES >= GEN_4 ? MOVE_TRI_ATTACK : MOVE_SWIFT; - - return move; -} - -static enum Move GetSleepTalkMove(void) -{ - enum Move move = MOVE_NONE; - - u32 i, unusableMovesBits = 0, movePosition; - - if (GetBattlerAbility(gBattlerAttacker) != ABILITY_COMATOSE - && !(gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP)) - return move; - - for (i = 0; i < MAX_MON_MOVES; i++) - { - if (IsMoveSleepTalkBanned(gBattleMons[gBattlerAttacker].moves[i]) - || gBattleMoveEffects[GetMoveEffect(gBattleMons[gBattlerAttacker].moves[i])].twoTurnEffect) - unusableMovesBits |= (1 << (i)); - } - - unusableMovesBits = CheckMoveLimitations(gBattlerAttacker, unusableMovesBits, ~(MOVE_LIMITATION_PP | MOVE_LIMITATION_CHOICE_ITEM)); - if (unusableMovesBits == ALL_MOVES_MASK) // all 4 moves cannot be chosen - return move; - - // Set Sleep Talk as used move, so it works with Last Resort. - gBattleMons[gBattlerAttacker].volatiles.usedMoves |= 1u << gCurrMovePos; - do - { - movePosition = MOD(Random(), MAX_MON_MOVES); - } while ((1u << movePosition) & unusableMovesBits); - - move = gBattleMons[gBattlerAttacker].moves[movePosition]; - gCurrMovePos = movePosition; - - return move; -} - -static enum Move GetCopycatMove(void) -{ - if (gLastUsedMove == MOVE_NONE - || gLastUsedMove == MOVE_UNAVAILABLE - || IsMoveCopycatBanned(gLastUsedMove) - || IsZMove(gLastUsedMove)) - return MOVE_NONE; - - return gLastUsedMove; -} - -static enum Move GetMeFirstMove(void) -{ - enum Move move = GetChosenMoveFromPosition(gBattlerTarget); - - if (IsBattleMoveStatus(move) - || IsMoveMeFirstBanned(move) - || HasBattlerActedThisTurn(gBattlerTarget)) - return MOVE_NONE; - - return move; -} - -void RemoveAbilityFlags(u32 battler) +void RemoveAbilityFlags(enum BattlerId battler) { gBattleMons[battler].volatiles.unburdenActive = FALSE; @@ -12479,15 +10742,15 @@ void RemoveAbilityFlags(u32 battler) } } -void CheckSetUnburden(u32 battler) +void CheckSetUnburden(enum BattlerId battler) { if (IsAbilityAndRecord(battler, GetBattlerAbility(battler), ABILITY_UNBURDEN)) gBattleMons[battler].volatiles.unburdenActive = TRUE; } -bool32 IsAnyTargetTurnDamaged(u32 battlerAtk) +bool32 IsAnyTargetTurnDamaged(enum BattlerId battlerAtk) { - for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) + for (enum BattlerId battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) { if (battlerDef == battlerAtk) continue; @@ -12499,7 +10762,7 @@ bool32 IsAnyTargetTurnDamaged(u32 battlerAtk) bool32 IsAnyTargetAffected(void) { - for (u32 battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { if (battler == gBattlerAttacker) continue; @@ -12517,7 +10780,7 @@ bool32 IsDoubleSpreadMove(void) && IsSpreadMove(GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove)); } -bool32 IsBattlerInvalidForSpreadMove(u32 battlerAtk, u32 battlerDef) +bool32 IsBattlerInvalidForSpreadMove(enum BattlerId battlerAtk, enum BattlerId battlerDef) { return battlerDef == battlerAtk || !IsBattlerAlive(battlerDef) @@ -12539,7 +10802,7 @@ bool32 IsAllowedToUseBag(void) } } -bool32 IsMimikyuDisguised(u32 battler) +bool32 IsMimikyuDisguised(enum BattlerId battler) { return gBattleMons[battler].species == SPECIES_MIMIKYU_DISGUISED || gBattleMons[battler].species == SPECIES_MIMIKYU_TOTEM_DISGUISED; @@ -12575,7 +10838,7 @@ bool32 IsUsableWhileAsleepEffect(enum BattleMoveEffects effect) } } -void SetWrapTurns(u32 battler, enum HoldEffect holdEffect) +void SetWrapTurns(enum BattlerId battler, enum HoldEffect holdEffect) { u32 normalWrapTurns = B_WRAP_TURNS - 2; // 5 turns if (holdEffect == HOLD_EFFECT_GRIP_CLAW) @@ -12704,7 +10967,7 @@ static const u16 sGen5ProtectSuccessRates[] = USHRT_MAX / 27 }; -bool32 CanUseMoveConsecutively(u32 battler) +bool32 CanUseMoveConsecutively(enum BattlerId battler) { u32 moveUses = gBattleMons[battler].volatiles.consecutiveMoveUses; if (moveUses >= ARRAY_COUNT(sProtectSuccessRates)) @@ -12718,7 +10981,7 @@ bool32 CanUseMoveConsecutively(u32 battler) } // Used for Protect, Endure and Ally switch -void TryResetConsecutiveUseCounter(u32 battler) +void TryResetConsecutiveUseCounter(enum BattlerId battler) { u32 lastMove = gLastResultingMoves[battler]; if (lastMove == MOVE_UNAVAILABLE) diff --git a/src/battle_util2.c b/src/battle_util2.c index aeb0de7d5..727a72b68 100644 --- a/src/battle_util2.c +++ b/src/battle_util2.c @@ -5,7 +5,6 @@ #include "malloc.h" #include "pokemon.h" #include "trainer_tower.h" -// #include "trainer_hill.h" #include "party_menu.h" #include "event_data.h" #include "constants/abilities.h" @@ -65,6 +64,7 @@ void FreeBattleResources(void) TRY_FREE_AND_SET_NULL(gPokedudeBattlerStates[i]); } } + gFieldStatuses = 0; if (gBattleResources != NULL) { @@ -89,13 +89,13 @@ void FreeBattleResources(void) } } -void AdjustFriendshipOnBattleFaint(u8 battler) +void AdjustFriendshipOnBattleFaint(enum BattlerId battler) { - u8 opposingBattlerId; + enum BattlerId opposingBattlerId; if (IsDoubleBattle()) { - u8 opposingBattlerId2; + enum BattlerId opposingBattlerId2; opposingBattlerId = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); opposingBattlerId2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); @@ -121,7 +121,7 @@ void AdjustFriendshipOnBattleFaint(u8 battler) } } -void SwitchPartyOrderInGameMulti(u8 battler, u8 arg1) +void SwitchPartyOrderInGameMulti(enum BattlerId battler, u8 arg1) { if (IsOnPlayerSide(battler)) { @@ -138,92 +138,90 @@ void SwitchPartyOrderInGameMulti(u8 battler, u8 arg1) // Called when a Pokémon is unable to attack during a Battle Palace battle. // Check if it was because they are frozen/asleep, and if so try to cure the status. -// u32 BattlePalace_TryEscapeStatus(u8 battler) -// { -// u32 effect = 0; +u32 BattlePalace_TryEscapeStatus(enum BattlerId battler) +{ + u32 effect = 0; -// do -// { -// switch (gBattleCommunication[MULTIUSE_STATE]) -// { -// case 0: -// if (gBattleMons[battler].status1 & STATUS1_SLEEP) -// { -// if (UproarWakeUpCheck(battler)) -// { -// // Wake up from Uproar -// gBattleMons[battler].status1 &= ~(STATUS1_SLEEP); -// gBattleMons[battler].status2 &= ~(STATUS2_NIGHTMARE); -// BattleScriptPushCursor(); -// gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WOKE_UP_UPROAR; -// gBattlescriptCurrInstr = BattleScript_MoveUsedWokeUp; -// effect = 2; -// } -// else -// { -// u32 toSub; + do + { + switch (gBattleCommunication[MULTIUSE_STATE]) + { + case 0: + if (gBattleMons[battler].status1 & STATUS1_SLEEP) + { + if (UproarWakeUpCheck(battler)) + { + // Wake up from Uproar + gEffectBattler = battler; + gBattleMons[battler].status1 &= ~(STATUS1_SLEEP); + gBattleMons[battler].volatiles.nightmare = FALSE; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WOKE_UP_UPROAR; + BattleScriptCall(BattleScript_MoveUsedWokeUp); + effect = 2; + } + else + { + u32 toSub; -// if (GetBattlerAbility(battler) == ABILITY_EARLY_BIRD) -// toSub = 2; -// else -// toSub = 1; + if (GetBattlerAbility(battler) == ABILITY_EARLY_BIRD) + toSub = 2; + else + toSub = 1; -// // Reduce number of sleep turns -// if ((gBattleMons[battler].status1 & STATUS1_SLEEP) < toSub) -// gBattleMons[battler].status1 &= ~(STATUS1_SLEEP); -// else -// gBattleMons[battler].status1 -= toSub; + // Reduce number of sleep turns + if ((gBattleMons[battler].status1 & STATUS1_SLEEP) < toSub) + gBattleMons[battler].status1 &= ~(STATUS1_SLEEP); + else + gBattleMons[battler].status1 -= toSub; -// if (gBattleMons[battler].status1 & STATUS1_SLEEP) -// { -// // Still asleep -// gBattlescriptCurrInstr = BattleScript_MoveUsedIsAsleep; -// effect = 2; -// } -// else -// { -// // Wake up -// gBattleMons[battler].status2 &= ~(STATUS2_NIGHTMARE); -// BattleScriptPushCursor(); -// gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WOKE_UP; -// gBattlescriptCurrInstr = BattleScript_MoveUsedWokeUp; -// effect = 2; -// } -// } -// } -// gBattleCommunication[MULTIUSE_STATE]++; -// break; -// case 1: -// if (gBattleMons[battler].status1 & STATUS1_FREEZE) -// { -// if (Random() % 5 != 0) -// { -// // Still frozen -// gBattlescriptCurrInstr = BattleScript_MoveUsedIsFrozen; -// } -// else -// { -// // Unfreeze -// gBattleMons[battler].status1 &= ~(STATUS1_FREEZE); -// BattleScriptPushCursor(); -// gBattlescriptCurrInstr = BattleScript_MoveUsedUnfroze; -// gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DEFROSTED; -// } -// effect = 2; -// } -// gBattleCommunication[MULTIUSE_STATE]++; -// break; -// case 2: -// break; -// } -// // Loop until reaching the final state, or stop early if Pokémon was Asleep/Frozen -// } while (gBattleCommunication[MULTIUSE_STATE] != 2 && effect == 0); + if (gBattleMons[battler].status1 & STATUS1_SLEEP) + { + // Still asleep + gBattlescriptCurrInstr = BattleScript_MoveUsedIsAsleep; + effect = 2; + } + else + { + // Wake up + gBattleMons[battler].volatiles.nightmare = FALSE; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WOKE_UP; + BattleScriptCall(BattleScript_MoveUsedWokeUp); + effect = 2; + } + } + } + gBattleCommunication[MULTIUSE_STATE]++; + break; + case 1: + if (gBattleMons[battler].status1 & STATUS1_FREEZE) + { + if (Random() % 5 != 0) + { + // Still frozen + gBattlescriptCurrInstr = BattleScript_MoveUsedIsFrozen; + } + else + { + // Unfreeze + gBattleMons[battler].status1 &= ~(STATUS1_FREEZE); + BattleScriptCall(BattleScript_MoveUsedUnfroze); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DEFROSTED; + } + effect = 2; + } + gBattleCommunication[MULTIUSE_STATE]++; + break; + case 2: + break; + } + // Loop until reaching the final state, or stop early if Pokémon was Asleep/Frozen + } while (gBattleCommunication[MULTIUSE_STATE] != 2 && effect == 0); -// if (effect == 2) -// { -// BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1); -// MarkBattlerForControllerExec(battler); -// } + if (effect == 2) + { + BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1); + MarkBattlerForControllerExec(battler); + } -// return effect; -// } + return effect; +} diff --git a/src/battle_z_move.c b/src/battle_z_move.c index 4df1f920e..d11794f35 100644 --- a/src/battle_z_move.c +++ b/src/battle_z_move.c @@ -44,9 +44,9 @@ #define STAT_STAGE(battler, stat) (gBattleMons[battler].statStages[stat - 1]) // Function Declarations -static void ZMoveSelectionDisplayPpNumber(u32 battler); -static void ZMoveSelectionDisplayPower(u16 move, u16 zMove); -static void ZMoveSelectionDisplayMoveType(u16 zMove, u32 battler); +static void ZMoveSelectionDisplayPpNumber(enum BattlerId battler); +static void ZMoveSelectionDisplayPower(enum Move move, enum Move zMove); +static void ZMoveSelectionDisplayMoveType(enum Move zMove, enum BattlerId battler); // Const Data static const struct SignatureZMove sSignatureZMoves[] = @@ -105,18 +105,19 @@ static const u8 sText_PowerColon[] = _("Power: "); static const u8 sText_NoAdditionalEffect[] = _("No Additional Effect"); // Functions -bool32 IsZMove(u32 move) +bool32 IsZMove(enum Move move) { return move >= FIRST_Z_MOVE && move <= LAST_Z_MOVE; } -bool32 CanUseZMove(u32 battler) +bool32 CanUseZMove(enum BattlerId battler) { enum HoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); + enum BattlerPosition position = GetBattlerPosition(battler); // Check if Player has Z-Power Ring. - if (!TESTING && (battler == B_POSITION_PLAYER_LEFT - || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT)) + if (!TESTING && (position == B_POSITION_PLAYER_LEFT + || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && position == B_POSITION_PLAYER_RIGHT)) && !CheckBagHasItem(ITEM_Z_POWER_RING, 1)) return FALSE; @@ -140,14 +141,14 @@ bool32 CanUseZMove(u32 battler) return TRUE; } -u32 GetUsableZMove(u32 battler, u32 move) +enum Move GetUsableZMove(enum BattlerId battler, enum Move move) { - u32 item = gBattleMons[battler].item; + enum Item item = gBattleMons[battler].item; enum HoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); if (holdEffect == HOLD_EFFECT_Z_CRYSTAL) { - u16 zMove = GetSignatureZMove(move, gBattleMons[battler].species, item); + enum Move zMove = GetSignatureZMove(move, gBattleMons[battler].species, item); if (zMove != MOVE_NONE) return zMove; // Signature z move exists @@ -158,14 +159,14 @@ u32 GetUsableZMove(u32 battler, u32 move) return MOVE_NONE; } -void ActivateZMove(u32 battler) +void ActivateZMove(enum BattlerId battler) { SetActiveGimmick(battler, GIMMICK_Z_MOVE); } -bool32 IsViableZMove(u32 battler, u32 move) +bool32 IsViableZMove(enum BattlerId battler, enum Move move) { - u32 item; + enum Item item; enum HoldEffect holdEffect = GetBattlerHoldEffectIgnoreNegation(battler); int moveSlotIndex; @@ -180,8 +181,9 @@ bool32 IsViableZMove(u32 battler, u32 move) return FALSE; } + enum BattlerPosition position = GetBattlerPosition(battler); // Check if Player has Z-Power Ring. - if ((battler == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT)) + if ((position == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && position == B_POSITION_PLAYER_RIGHT)) && !CheckBagHasItem(ITEM_Z_POWER_RING, 1)) { return FALSE; @@ -190,7 +192,7 @@ bool32 IsViableZMove(u32 battler, u32 move) // Check for signature Z-Move or type-based Z-Move. if (holdEffect == HOLD_EFFECT_Z_CRYSTAL) { - u16 zMove = GetSignatureZMove(move, gBattleMons[battler].species, item); + enum Move zMove = GetSignatureZMove(move, gBattleMons[battler].species, item); if (zMove != MOVE_NONE) return TRUE; @@ -201,7 +203,7 @@ bool32 IsViableZMove(u32 battler, u32 move) return FALSE; } -void AssignUsableZMoves(u32 battler, u16 *moves) +void AssignUsableZMoves(enum BattlerId battler, enum Move *moves) { u32 i; gBattleStruct->zmove.possibleZMoves[battler] = 0; @@ -212,7 +214,7 @@ void AssignUsableZMoves(u32 battler, u16 *moves) } } -bool32 TryChangeZTrigger(u32 battler, u32 moveIndex) +bool32 TryChangeZTrigger(enum BattlerId battler, u32 moveIndex) { bool32 viableZMove = (gBattleStruct->zmove.possibleZMoves[battler] & (1u << moveIndex)) != 0; @@ -226,7 +228,7 @@ bool32 TryChangeZTrigger(u32 battler, u32 moveIndex) return viableZMove; } -u32 GetSignatureZMove(u32 move, u32 species, u32 item) +enum Move GetSignatureZMove(enum Move move, u32 species, enum Item item) { u32 i; @@ -240,9 +242,9 @@ u32 GetSignatureZMove(u32 move, u32 species, u32 item) return MOVE_NONE; } -u32 GetTypeBasedZMove(u32 move) +enum Move GetTypeBasedZMove(enum Move move) { - u32 moveType = GetMoveType(move); + enum Type moveType = GetMoveType(move); if (moveType >= NUMBER_OF_MON_TYPES) moveType = TYPE_MYSTERY; @@ -257,11 +259,11 @@ u32 GetTypeBasedZMove(u32 move) return gTypesInfo[moveType].zMove; } -bool32 MoveSelectionDisplayZMove(u16 zmove, u32 battler) +bool32 MoveSelectionDisplayZMove(enum Move zmove, enum BattlerId battler) { u32 i; struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); - u16 move = moveInfo->moves[gMoveSelectionCursor[battler]]; + enum Move move = moveInfo->moves[gMoveSelectionCursor[battler]]; PlaySE(SE_SELECT); gBattleStruct->zmove.viewing = TRUE; @@ -277,7 +279,7 @@ bool32 MoveSelectionDisplayZMove(u16 zmove, u32 battler) if (IsBattleMoveStatus(move)) { - u8 zEffect = GetMoveZEffect(move); + enum ZEffect zEffect = GetMoveZEffect(move); gDisplayedStringBattle[0] = EOS; @@ -385,7 +387,7 @@ bool32 MoveSelectionDisplayZMove(u16 zmove, u32 battler) return FALSE; } -static void ZMoveSelectionDisplayPower(u16 move, u16 zMove) +static void ZMoveSelectionDisplayPower(enum Move move, enum Move zMove) { u8 *txtPtr; u16 power = GetZMovePower(move); @@ -401,7 +403,7 @@ static void ZMoveSelectionDisplayPower(u16 move, u16 zMove) } } -static void ZMoveSelectionDisplayPpNumber(u32 battler) +static void ZMoveSelectionDisplayPpNumber(enum BattlerId battler) { u8 *txtPtr; @@ -415,7 +417,7 @@ static void ZMoveSelectionDisplayPpNumber(u32 battler) BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_PP_REMAINING); } -static void ZMoveSelectionDisplayMoveType(u16 zMove, u32 battler) +static void ZMoveSelectionDisplayMoveType(enum Move zMove, enum BattlerId battler) { u8 *txtPtr, *end; enum Type zMoveType = GetBattleMoveType(zMove); @@ -436,7 +438,7 @@ static void ZMoveSelectionDisplayMoveType(u16 zMove, u32 battler) void SetZEffect(void) { u32 i; - u32 effect = GetMoveZEffect(gChosenMove); + enum ZEffect effect = GetMoveZEffect(gChosenMove); if (effect == Z_EFFECT_CURSE) { @@ -517,7 +519,7 @@ void SetZEffect(void) } break; case Z_EFFECT_RESTORE_REPLACEMENT_HP: - gBattleStruct->zmove.healReplacement = TRUE; + gBattleStruct->zmove.healReplacement |= 1u << gBattlerAttacker; BattleScriptPush(gBattlescriptCurrInstr + Z_EFFECT_BS_LENGTH); gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_Z_HP_TRAP; gBattlescriptCurrInstr = BattleScript_ZEffectPrintString; @@ -543,12 +545,12 @@ void SetZEffect(void) } } -u32 GetZMovePower(u32 move) +u32 GetZMovePower(enum Move move) { if (GetMoveCategory(move) == DAMAGE_CATEGORY_STATUS) return 0; enum BattleMoveEffects moveEffect = GetMoveEffect(move); - if (moveEffect == EFFECT_OHKO || moveEffect == EFFECT_SHEER_COLD) + if (moveEffect == EFFECT_OHKO) return 180; u32 power = GetMoveZPowerOverride(move); diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index dd02d498e..31e2e2e6e 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -1,6 +1,6 @@ #include "battle.h" #include "battle_scripts.h" -// #include "constants/battle_factory.h" +#include "constants/battle_factory.h" #include "constants/battle_move_effects.h" const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = @@ -216,17 +216,11 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_OHKO] = { - .battleScript = BattleScript_EffectOHKO, + .battleScript = BattleScript_EffectHit, .battleTvScore = 7, .battleFactoryStyle = FACTORY_STYLE_HIGH_RISK, }, - [EFFECT_SHEER_COLD] = - { - .battleScript = BattleScript_EffectOHKO, - .battleTvScore = 7, - }, - [EFFECT_FUSION_COMBO] = { .battleScript = BattleScript_EffectHit, @@ -508,7 +502,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_SNORE] = { - .battleScript = BattleScript_EffectSnore, + .battleScript = BattleScript_EffectHit, .battleTvScore = 3, }, @@ -721,7 +715,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_MAGNITUDE] = { - .battleScript = BattleScript_EffectMagnitude, + .battleScript = BattleScript_EffectHit, .battleTvScore = 1, }, @@ -832,7 +826,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_BEAT_UP] = { - .battleScript = BattleScript_EffectBeatUp, + .battleScript = BattleScript_EffectHit, .battleTvScore = 2, }, @@ -882,7 +876,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_SPIT_UP] = { - .battleScript = BattleScript_EffectSpitUp, + .battleScript = BattleScript_EffectHit, .battleTvScore = 3, .encourageEncore = TRUE, }, @@ -1204,7 +1198,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_NATURAL_GIFT] = { - .battleScript = BattleScript_EffectNaturalGift, + .battleScript = BattleScript_EffectHit, .battleTvScore = 0, // TODO: Assign points .encourageEncore = TRUE, }, @@ -2131,7 +2125,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_FICKLE_BEAM] = { - .battleScript = BattleScript_EffectFickleBeam, + .battleScript = BattleScript_EffectHit, .battleTvScore = 0, // TODO: Assign points }, diff --git a/src/data/moves_info.h b/src/data/moves_info.h index b1fde5f4f..27f4717b7 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -4,7 +4,6 @@ #include "constants/battle_move_effects.h" #include "constants/battle_script_commands.h" #include "constants/battle_string_ids.h" -#include "constants/battle_z_move_effects.h" #include "constants/hold_effects.h" #include "constants/moves.h" #include "constants/contest.h" @@ -1920,11 +1919,11 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = }, .ignoresProtect = B_UPDATED_MOVE_FLAGS < GEN_5, .ignoresKingsRock = (B_UPDATED_MOVE_FLAGS == GEN_3 || B_UPDATED_MOVE_FLAGS == GEN_4), + .mirrorMoveBanned = B_UPDATED_MOVE_FLAGS >= GEN_4, .meFirstBanned = TRUE, .metronomeBanned = B_UPDATED_MOVE_FLAGS >= GEN_2, .copycatBanned = TRUE, .assistBanned = TRUE, - .mirrorMoveBanned = B_UPDATED_MOVE_FLAGS >= GEN_4, .contestEffect = C_UPDATED_MOVE_EFFECTS >= GEN_6 ? CONTEST_EFFECT_BETTER_IF_LAST : CONTEST_EFFECT_AVOID_STARTLE_ONCE, .contestCategory = CONTEST_CATEGORY_TOUGH, .contestComboStarterId = 0, @@ -2544,6 +2543,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .argument = { .nonVolatileStatus = MOVE_EFFECT_TOXIC }, .zMove = { .effect = Z_EFFECT_DEF_UP_1 }, .magicCoatAffected = TRUE, + .alwaysHitsOnSameType = B_TOXIC_NEVER_MISS >= GEN_6, .contestEffect = CONTEST_EFFECT_WORSEN_CONDITION_OF_PREV_MONS, .contestCategory = CONTEST_CATEGORY_SMART, .contestComboStarterId = COMBO_STARTER_TOXIC, @@ -3221,7 +3221,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .name = COMPOUND_STRING("Metronome"), .description = COMPOUND_STRING( "Waggles a finger\nto use any " - "Pokémon\nmove at random."), + "\nPokémon move at\nrandom."), .effect = EFFECT_METRONOME, .power = 0, .type = TYPE_NORMAL, @@ -6200,15 +6200,13 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .priority = 0, .category = DAMAGE_CATEGORY_PHYSICAL, .makesContact = TRUE, - .additionalEffects = ADDITIONAL_EFFECTS( #if B_SPEED_BUFFING_RAPID_SPIN >= GEN_8 - { - .moveEffect = MOVE_EFFECT_SPD_PLUS_1, - .self = TRUE, - .chance = 100, - } + .additionalEffects = ADDITIONAL_EFFECTS({ + .moveEffect = MOVE_EFFECT_SPD_PLUS_1, + .self = TRUE, + .chance = 100, + }), #endif - ), .contestEffect = C_UPDATED_MOVE_EFFECTS >= GEN_6 ? CONTEST_EFFECT_BETTER_WHEN_AUDIENCE_EXCITED : CONTEST_EFFECT_AVOID_STARTLE_ONCE, .contestCategory = CONTEST_CATEGORY_COOL, .contestComboStarterId = 0, @@ -6594,6 +6592,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .mirrorMoveBanned = B_UPDATED_MOVE_FLAGS >= GEN_4, .meFirstBanned = TRUE, .metronomeBanned = TRUE, + .copycatBanned = B_UPDATED_MOVE_FLAGS <= GEN_8, .assistBanned = TRUE, .contestEffect = C_UPDATED_MOVE_EFFECTS >= GEN_6 ? CONTEST_EFFECT_BETTER_IF_LAST : CONTEST_EFFECT_AVOID_STARTLE_ONCE, .contestCategory = CONTEST_CATEGORY_BEAUTY, @@ -6798,7 +6797,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .name = COMPOUND_STRING("Beat Up"), .description = COMPOUND_STRING( "Summons party\nPokémon to " - "join in\nthe attack."), + "join\nin the attack."), .effect = EFFECT_BEAT_UP, .power = B_UPDATED_MOVE_DATA >= GEN_5 ? 1 : 10, .type = TYPE_DARK, @@ -6807,6 +6806,10 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .target = TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_PHYSICAL, + .additionalEffects = ADDITIONAL_EFFECTS({ + .moveEffect = MOVE_EFFECT_BEAT_UP_MESSAGE, + .preAttackEffect = TRUE, + }), .contestEffect = C_UPDATED_MOVE_EFFECTS >= GEN_6 ? CONTEST_EFFECT_BETTER_WITH_GOOD_CONDITION : CONTEST_EFFECT_BADLY_STARTLE_MONS_WITH_GOOD_APPEALS, .contestCategory = CONTEST_CATEGORY_SMART, .contestComboStarterId = 0, @@ -8839,13 +8842,15 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .description = COMPOUND_STRING( "A chilling attack\nthat " "causes\nfainting if it\nhits."), - .effect = EFFECT_SHEER_COLD, + .effect = EFFECT_OHKO, .power = 1, .type = TYPE_ICE, .accuracy = 30, .pp = 5, .target = TARGET_SELECTED, .priority = 0, + .noAffectOnSameTypeTarget = B_SHEER_COLD_IMMUNITY >= GEN_7, + .accIncreaseByTenOnSameType = B_SHEER_COLD_ACC >= GEN_7, .category = DAMAGE_CATEGORY_SPECIAL, .contestEffect = CONTEST_EFFECT_BADLY_STARTLE_MONS_WITH_GOOD_APPEALS, .contestCategory = CONTEST_CATEGORY_BEAUTY, @@ -8992,7 +8997,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .priority = 0, .category = DAMAGE_CATEGORY_STATUS, .zMove = { .effect = Z_EFFECT_DEF_UP_1 }, - .ignoresProtect = (B_UPDATED_MOVE_FLAGS >= GEN_6 || B_UPDATED_MOVE_FLAGS < GEN_3), + .ignoresProtect = B_UPDATED_MOVE_FLAGS >= GEN_6, .magicCoatAffected = TRUE, .contestEffect = CONTEST_EFFECT_MAKE_FOLLOWING_MONS_NERVOUS, .contestCategory = CONTEST_CATEGORY_CUTE, @@ -9247,8 +9252,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = #endif .contestEffect = CONTEST_EFFECT_USER_MORE_EASILY_STARTLED, .contestCategory = CONTEST_CATEGORY_COOL, - .contestComboStarterId = COMBO_STARTER_CHARGE, - .contestComboMoves = {0}, + .contestComboStarterId = 0, + .contestComboMoves = {COMBO_STARTER_CHARGE}, .battleAnimScript = gBattleAnimMove_VoltTackle, .validApprenticeMove = TRUE, }, @@ -11413,8 +11418,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = { .name = COMPOUND_STRING("Trick Room"), .description = COMPOUND_STRING( - "Slower Pokémon get\nto move " - "first for\n5 turns."), + "Slower Pokémon\nget to move " + "first\nfor 5 turns."), .effect = EFFECT_TRICK_ROOM, .power = 0, .type = TYPE_PSYCHIC, @@ -13549,8 +13554,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .category = DAMAGE_CATEGORY_SPECIAL, .contestEffect = CONTEST_EFFECT_AVOID_STARTLE, //CONTEST_EFFECT_QUICKLY_GROW_BORED .contestCategory = CONTEST_CATEGORY_COOL, - .contestComboStarterId = COMBO_STARTER_CHARGE, - .contestComboMoves = {0}, + .contestComboStarterId = 0, + .contestComboMoves = {COMBO_STARTER_CHARGE}, .battleAnimScript = gBattleAnimMove_VoltSwitch, }, @@ -16027,7 +16032,9 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .metronomeBanned = TRUE, .sketchBanned = (B_SKETCH_BANS >= GEN_9), .additionalEffects = ADDITIONAL_EFFECTS({ - // Feint move effect handled in script as it goes before animation + .moveEffect = MOVE_EFFECT_FEINT, // TODO: Is this supposed to happen before the attack animation? + }, + { .moveEffect = MOVE_EFFECT_DEF_MINUS_1, .self = TRUE, }), @@ -19400,8 +19407,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = { .name = COMPOUND_STRING("Stone Axe"), .description = COMPOUND_STRING( - "High critical hit\nratio. Sets " - "\nSplinters that\nhurt the foe."), + "Sets sharp rocks\nthat hurt " + "the foe."), .effect = EFFECT_STONE_AXE, .power = 65, .type = TYPE_ROCK, @@ -19737,8 +19744,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = { .name = COMPOUND_STRING("Ceaseless Edge"), .description = COMPOUND_STRING( - "High critical hit\nratio. Sets " - "\nSplinters that\nhurt the foe."), + "Sets Spikes that\nhurt the " + "foe."), .effect = EFFECT_CEASELESS_EDGE, .power = 65, .type = TYPE_DARK, @@ -19944,7 +19951,6 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .target = TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_PHYSICAL, - .metronomeBanned = TRUE, // Only since it isn't implemented yet .battleAnimScript = gBattleAnimMove_LastRespects, }, @@ -20198,8 +20204,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .priority = 0, .category = DAMAGE_CATEGORY_PHYSICAL, .makesContact = TRUE, - .additionalEffects = ADDITIONAL_EFFECTS( - { + .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_POISON, .chance = 100, }), @@ -21031,7 +21036,6 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .category = DAMAGE_CATEGORY_SPECIAL, .argument = { .absorbPercentage = 50 }, .thawsUser = TRUE, - .metronomeBanned = TRUE, .healingMove = TRUE, .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_BURN, @@ -21055,7 +21059,6 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .priority = 0, .category = DAMAGE_CATEGORY_SPECIAL, .ballisticMove = TRUE, - .metronomeBanned = TRUE, .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_SYRUP_BOMB, .chance = 100, @@ -21078,7 +21081,6 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .target = TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_PHYSICAL, - .metronomeBanned = TRUE, .battleAnimScript = gBattleAnimMove_IvyCudgel, }, @@ -21162,7 +21164,6 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .zMove = { .effect = Z_EFFECT_RESET_STATS }, .ignoresProtect = TRUE, .mirrorMoveBanned = TRUE, - .metronomeBanned = TRUE, .copycatBanned = TRUE, .assistBanned = TRUE, .battleAnimScript = gBattleAnimMove_BurningBulwark, diff --git a/src/data/party_menu.h b/src/data/party_menu.h index d44d13cab..a1c6d4a7c 100644 --- a/src/data/party_menu.h +++ b/src/data/party_menu.h @@ -1155,11 +1155,18 @@ static const u8 *const sUnionRoomTradeMessages[] = [UR_TRADE_MSG_CANT_TRADE_WITH_PARTNER_2 - 1] = gText_CantTradeWithTrainer, }; +#define ROTOM_BASE_MOVE MOVE_THUNDER_SHOCK +#define ROTOM_HEAT_MOVE MOVE_OVERHEAT +#define ROTOM_WASH_MOVE MOVE_HYDRO_PUMP +#define ROTOM_FROST_MOVE MOVE_BLIZZARD +#define ROTOM_FAN_MOVE MOVE_AIR_SLASH +#define ROTOM_MOW_MOVE MOVE_LEAF_STORM + static const u16 sRotomFormChangeMoves[5] = { - MOVE_HYDRO_PUMP, - MOVE_BLIZZARD, - MOVE_OVERHEAT, - MOVE_AIR_SLASH, - MOVE_LEAF_STORM, + ROTOM_HEAT_MOVE, + ROTOM_WASH_MOVE, + ROTOM_FROST_MOVE, + ROTOM_FAN_MOVE, + ROTOM_MOW_MOVE, }; diff --git a/src/data/pokemon/form_change_tables.h b/src/data/pokemon/form_change_tables.h index 4707741d0..fe6a299de 100644 --- a/src/data/pokemon/form_change_tables.h +++ b/src/data/pokemon/form_change_tables.h @@ -7,6 +7,7 @@ static const struct FormChange sVenusaurFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_VENUSAUR_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_VENUSAUR}, {FORM_CHANGE_END_BATTLE, SPECIES_VENUSAUR}, {FORM_CHANGE_TERMINATOR}, }; @@ -22,6 +23,7 @@ static const struct FormChange sCharizardFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_CHARIZARD_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_CHARIZARD}, {FORM_CHANGE_END_BATTLE, SPECIES_CHARIZARD}, {FORM_CHANGE_TERMINATOR}, }; @@ -36,6 +38,7 @@ static const struct FormChange sBlastoiseFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_BLASTOISE_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_BLASTOISE}, {FORM_CHANGE_END_BATTLE, SPECIES_BLASTOISE}, {FORM_CHANGE_TERMINATOR}, }; @@ -47,6 +50,7 @@ static const struct FormChange sButterfreeFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_BUTTERFREE_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_BUTTERFREE}, {FORM_CHANGE_END_BATTLE, SPECIES_BUTTERFREE}, {FORM_CHANGE_TERMINATOR}, }; @@ -58,6 +62,7 @@ static const struct FormChange sBeedrillFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_BEEDRILL_MEGA, ITEM_BEEDRILLITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_BEEDRILL}, {FORM_CHANGE_END_BATTLE, SPECIES_BEEDRILL}, {FORM_CHANGE_TERMINATOR}, }; @@ -69,6 +74,7 @@ static const struct FormChange sPidgeotFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_PIDGEOT_MEGA, ITEM_PIDGEOTITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_PIDGEOT}, {FORM_CHANGE_END_BATTLE, SPECIES_PIDGEOT}, {FORM_CHANGE_TERMINATOR}, }; @@ -80,6 +86,7 @@ static const struct FormChange sPikachuFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_PIKACHU_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_PIKACHU}, {FORM_CHANGE_END_BATTLE, SPECIES_PIKACHU}, {FORM_CHANGE_TERMINATOR}, }; @@ -89,6 +96,7 @@ static const struct FormChange sRaichuFormChangeTable[] = { {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_RAICHU_MEGA_X, ITEM_RAICHUNITE_X}, {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_RAICHU_MEGA_Y, ITEM_RAICHUNITE_Y}, #endif + {FORM_CHANGE_FAINT, SPECIES_RAICHU}, {FORM_CHANGE_END_BATTLE, SPECIES_RAICHU}, {FORM_CHANGE_TERMINATOR}, }; @@ -100,6 +108,7 @@ static const struct FormChange sClefableFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_CLEFABLE_MEGA, ITEM_CLEFABLITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_CLEFABLE}, {FORM_CHANGE_END_BATTLE, SPECIES_CLEFABLE}, {FORM_CHANGE_TERMINATOR}, }; @@ -111,6 +120,7 @@ static const struct FormChange sMeowthFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_MEOWTH_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_MEOWTH}, {FORM_CHANGE_END_BATTLE, SPECIES_MEOWTH}, {FORM_CHANGE_TERMINATOR}, }; @@ -122,6 +132,7 @@ static const struct FormChange sAlakazamFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_ALAKAZAM_MEGA, ITEM_ALAKAZITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_ALAKAZAM}, {FORM_CHANGE_END_BATTLE, SPECIES_ALAKAZAM}, {FORM_CHANGE_TERMINATOR}, }; @@ -133,6 +144,7 @@ static const struct FormChange sMachampFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_MACHAMP_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_MACHAMP}, {FORM_CHANGE_END_BATTLE, SPECIES_MACHAMP}, {FORM_CHANGE_TERMINATOR}, }; @@ -144,6 +156,7 @@ static const struct FormChange sVictreebelFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_VICTREEBEL_MEGA, ITEM_VICTREEBELITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_VICTREEBEL}, {FORM_CHANGE_END_BATTLE, SPECIES_VICTREEBEL}, {FORM_CHANGE_TERMINATOR}, }; @@ -155,6 +168,7 @@ static const struct FormChange sSlowbroFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_SLOWBRO_MEGA, ITEM_SLOWBRONITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_SLOWBRO}, {FORM_CHANGE_END_BATTLE, SPECIES_SLOWBRO}, {FORM_CHANGE_TERMINATOR}, }; @@ -169,6 +183,7 @@ static const struct FormChange sGengarFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_GENGAR_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_GENGAR}, {FORM_CHANGE_END_BATTLE, SPECIES_GENGAR}, {FORM_CHANGE_TERMINATOR}, }; @@ -181,6 +196,7 @@ static const struct FormChange sSteelixFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_STEELIX_MEGA, ITEM_STEELIXITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_STEELIX}, {FORM_CHANGE_END_BATTLE, SPECIES_STEELIX}, {FORM_CHANGE_TERMINATOR}, }; @@ -193,6 +209,7 @@ static const struct FormChange sKinglerFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_KINGLER_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_KINGLER}, {FORM_CHANGE_END_BATTLE, SPECIES_KINGLER}, {FORM_CHANGE_TERMINATOR}, }; @@ -204,6 +221,7 @@ static const struct FormChange sKangaskhanFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_KANGASKHAN_MEGA, ITEM_KANGASKHANITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_KANGASKHAN}, {FORM_CHANGE_END_BATTLE, SPECIES_KANGASKHAN}, {FORM_CHANGE_TERMINATOR}, }; @@ -215,6 +233,7 @@ static const struct FormChange sStarmieFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_STARMIE_MEGA, ITEM_STARMINITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_STARMIE}, {FORM_CHANGE_END_BATTLE, SPECIES_STARMIE}, {FORM_CHANGE_TERMINATOR}, }; @@ -227,6 +246,7 @@ static const struct FormChange sScizorFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_SCIZOR_MEGA, ITEM_SCIZORITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_SCIZOR}, {FORM_CHANGE_END_BATTLE, SPECIES_SCIZOR}, {FORM_CHANGE_TERMINATOR}, }; @@ -239,6 +259,7 @@ static const struct FormChange sPinsirFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_PINSIR_MEGA, ITEM_PINSIRITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_PINSIR}, {FORM_CHANGE_END_BATTLE, SPECIES_PINSIR}, {FORM_CHANGE_TERMINATOR}, }; @@ -250,6 +271,7 @@ static const struct FormChange sGyaradosFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_GYARADOS_MEGA, ITEM_GYARADOSITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_GYARADOS}, {FORM_CHANGE_END_BATTLE, SPECIES_GYARADOS}, {FORM_CHANGE_TERMINATOR}, }; @@ -261,6 +283,7 @@ static const struct FormChange sLaprasFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_LAPRAS_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_LAPRAS}, {FORM_CHANGE_END_BATTLE, SPECIES_LAPRAS}, {FORM_CHANGE_TERMINATOR}, }; @@ -272,6 +295,7 @@ static const struct FormChange sEeveeFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_EEVEE_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_EEVEE}, {FORM_CHANGE_END_BATTLE, SPECIES_EEVEE}, {FORM_CHANGE_TERMINATOR}, }; @@ -283,6 +307,7 @@ static const struct FormChange sAerodactylFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_AERODACTYL_MEGA, ITEM_AERODACTYLITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_AERODACTYL}, {FORM_CHANGE_END_BATTLE, SPECIES_AERODACTYL}, {FORM_CHANGE_TERMINATOR}, }; @@ -294,6 +319,7 @@ static const struct FormChange sSnorlaxFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_SNORLAX_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_SNORLAX}, {FORM_CHANGE_END_BATTLE, SPECIES_SNORLAX}, {FORM_CHANGE_TERMINATOR}, }; @@ -305,6 +331,7 @@ static const struct FormChange sDragoniteFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_DRAGONITE_MEGA, ITEM_DRAGONINITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_DRAGONITE}, {FORM_CHANGE_END_BATTLE, SPECIES_DRAGONITE}, {FORM_CHANGE_TERMINATOR}, }; @@ -317,6 +344,7 @@ static const struct FormChange sMewtwoFormChangeTable[] = {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_MEWTWO_MEGA_X, ITEM_MEWTWONITE_X}, {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_MEWTWO_MEGA_Y, ITEM_MEWTWONITE_Y}, #endif + {FORM_CHANGE_FAINT, SPECIES_MEWTWO}, {FORM_CHANGE_END_BATTLE, SPECIES_MEWTWO}, {FORM_CHANGE_TERMINATOR}, }; @@ -328,6 +356,7 @@ static const struct FormChange sMeganiumFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_MEGANIUM_MEGA, ITEM_MEGANIUMITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_MEGANIUM}, {FORM_CHANGE_END_BATTLE, SPECIES_MEGANIUM}, {FORM_CHANGE_TERMINATOR}, }; @@ -339,6 +368,7 @@ static const struct FormChange sFeraligatrFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_FERALIGATR_MEGA, ITEM_FERALIGITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_FERALIGATR}, {FORM_CHANGE_END_BATTLE, SPECIES_FERALIGATR}, {FORM_CHANGE_TERMINATOR}, }; @@ -350,6 +380,7 @@ static const struct FormChange sAmpharosFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_AMPHAROS_MEGA, ITEM_AMPHAROSITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_AMPHAROS}, {FORM_CHANGE_END_BATTLE, SPECIES_AMPHAROS}, {FORM_CHANGE_TERMINATOR}, }; @@ -361,6 +392,7 @@ static const struct FormChange sHeracrossFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_HERACROSS_MEGA, ITEM_HERACRONITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_HERACROSS}, {FORM_CHANGE_END_BATTLE, SPECIES_HERACROSS}, {FORM_CHANGE_TERMINATOR}, }; @@ -372,6 +404,7 @@ static const struct FormChange sSkarmoryFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_SKARMORY_MEGA, ITEM_SKARMORITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_SKARMORY}, {FORM_CHANGE_END_BATTLE, SPECIES_SKARMORY}, {FORM_CHANGE_TERMINATOR}, }; @@ -383,6 +416,7 @@ static const struct FormChange sHoundoomFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_HOUNDOOM_MEGA, ITEM_HOUNDOOMINITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_HOUNDOOM}, {FORM_CHANGE_END_BATTLE, SPECIES_HOUNDOOM}, {FORM_CHANGE_TERMINATOR}, }; @@ -394,6 +428,7 @@ static const struct FormChange sTyranitarFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_TYRANITAR_MEGA, ITEM_TYRANITARITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_TYRANITAR}, {FORM_CHANGE_END_BATTLE, SPECIES_TYRANITAR}, {FORM_CHANGE_TERMINATOR}, }; @@ -405,6 +440,7 @@ static const struct FormChange sSceptileFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_SCEPTILE_MEGA, ITEM_SCEPTILITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_SCEPTILE}, {FORM_CHANGE_END_BATTLE, SPECIES_SCEPTILE}, {FORM_CHANGE_TERMINATOR}, }; @@ -416,6 +452,7 @@ static const struct FormChange sBlazikenFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_BLAZIKEN_MEGA, ITEM_BLAZIKENITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_BLAZIKEN}, {FORM_CHANGE_END_BATTLE, SPECIES_BLAZIKEN}, {FORM_CHANGE_TERMINATOR}, }; @@ -427,6 +464,7 @@ static const struct FormChange sSwampertFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_SWAMPERT_MEGA, ITEM_SWAMPERTITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_SWAMPERT}, {FORM_CHANGE_END_BATTLE, SPECIES_SWAMPERT}, {FORM_CHANGE_TERMINATOR}, }; @@ -438,6 +476,7 @@ static const struct FormChange sGardevoirFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_GARDEVOIR_MEGA, ITEM_GARDEVOIRITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_GARDEVOIR}, {FORM_CHANGE_END_BATTLE, SPECIES_GARDEVOIR}, {FORM_CHANGE_TERMINATOR}, }; @@ -448,6 +487,7 @@ static const struct FormChange sGalladeFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_GALLADE_MEGA, ITEM_GALLADITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_GALLADE}, {FORM_CHANGE_END_BATTLE, SPECIES_GALLADE}, {FORM_CHANGE_TERMINATOR}, }; @@ -460,6 +500,7 @@ static const struct FormChange sSableyeFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_SABLEYE_MEGA, ITEM_SABLENITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_SABLEYE}, {FORM_CHANGE_END_BATTLE, SPECIES_SABLEYE}, {FORM_CHANGE_TERMINATOR}, }; @@ -471,6 +512,7 @@ static const struct FormChange sMawileFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_MAWILE_MEGA, ITEM_MAWILITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_MAWILE}, {FORM_CHANGE_END_BATTLE, SPECIES_MAWILE}, {FORM_CHANGE_TERMINATOR}, }; @@ -482,6 +524,7 @@ static const struct FormChange sAggronFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_AGGRON_MEGA, ITEM_AGGRONITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_AGGRON}, {FORM_CHANGE_END_BATTLE, SPECIES_AGGRON}, {FORM_CHANGE_TERMINATOR}, }; @@ -493,6 +536,7 @@ static const struct FormChange sMedichamFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_MEDICHAM_MEGA, ITEM_MEDICHAMITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_MEDICHAM}, {FORM_CHANGE_END_BATTLE, SPECIES_MEDICHAM}, {FORM_CHANGE_TERMINATOR}, }; @@ -504,6 +548,7 @@ static const struct FormChange sManectricFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_MANECTRIC_MEGA, ITEM_MANECTITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_MANECTRIC}, {FORM_CHANGE_END_BATTLE, SPECIES_MANECTRIC}, {FORM_CHANGE_TERMINATOR}, }; @@ -515,6 +560,7 @@ static const struct FormChange sSharpedoFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_SHARPEDO_MEGA, ITEM_SHARPEDONITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_SHARPEDO}, {FORM_CHANGE_END_BATTLE, SPECIES_SHARPEDO}, {FORM_CHANGE_TERMINATOR}, }; @@ -526,6 +572,7 @@ static const struct FormChange sCameruptFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_CAMERUPT_MEGA, ITEM_CAMERUPTITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_CAMERUPT}, {FORM_CHANGE_END_BATTLE, SPECIES_CAMERUPT}, {FORM_CHANGE_TERMINATOR}, }; @@ -537,6 +584,7 @@ static const struct FormChange sAltariaFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_ALTARIA_MEGA, ITEM_ALTARIANITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_ALTARIA}, {FORM_CHANGE_END_BATTLE, SPECIES_ALTARIA}, {FORM_CHANGE_TERMINATOR}, }; @@ -548,6 +596,7 @@ static const struct FormChange sBanetteFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_BANETTE_MEGA, ITEM_BANETTITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_BANETTE}, {FORM_CHANGE_END_BATTLE, SPECIES_BANETTE}, {FORM_CHANGE_TERMINATOR}, }; @@ -558,6 +607,7 @@ static const struct FormChange sChimechoFormChangeTable[] = { #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_CHIMECHO_MEGA, ITEM_CHIMECHITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_CHIMECHO}, {FORM_CHANGE_END_BATTLE, SPECIES_CHIMECHO}, {FORM_CHANGE_TERMINATOR}, }; @@ -572,6 +622,7 @@ static const struct FormChange sAbsolFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_ABSOL_MEGA_Z, ITEM_ABSOLITE_Z}, #endif + {FORM_CHANGE_FAINT, SPECIES_ABSOL}, {FORM_CHANGE_END_BATTLE, SPECIES_ABSOL}, {FORM_CHANGE_TERMINATOR}, }; @@ -583,6 +634,7 @@ static const struct FormChange sGlalieFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_GLALIE_MEGA, ITEM_GLALITITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_GLALIE}, {FORM_CHANGE_END_BATTLE, SPECIES_GLALIE}, {FORM_CHANGE_TERMINATOR}, }; @@ -592,6 +644,7 @@ static const struct FormChange sFroslassFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_FROSLASS_MEGA, ITEM_FROSLASSITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_FROSLASS}, {FORM_CHANGE_END_BATTLE, SPECIES_FROSLASS}, {FORM_CHANGE_TERMINATOR}, }; @@ -624,7 +677,7 @@ static const struct FormChange sCastformFormChangeTable[] = {FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_NORMAL, ~(B_WEATHER_SUN | B_WEATHER_RAIN | B_WEATHER_ICY_ANY)}, {FORM_CHANGE_BATTLE_WEATHER, SPECIES_CASTFORM_NORMAL, B_WEATHER_NONE}, #endif - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_CASTFORM_NORMAL}, + {FORM_CHANGE_BATTLE_SWITCH_OUT, SPECIES_CASTFORM_NORMAL}, {FORM_CHANGE_FAINT, SPECIES_CASTFORM_NORMAL}, {FORM_CHANGE_END_BATTLE, SPECIES_CASTFORM_NORMAL}, {FORM_CHANGE_TERMINATOR}, @@ -637,6 +690,7 @@ static const struct FormChange sSalamenceFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_SALAMENCE_MEGA, ITEM_SALAMENCITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_SALAMENCE}, {FORM_CHANGE_END_BATTLE, SPECIES_SALAMENCE}, {FORM_CHANGE_TERMINATOR}, }; @@ -648,6 +702,7 @@ static const struct FormChange sMetagrossFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_METAGROSS_MEGA, ITEM_METAGROSSITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_METAGROSS}, {FORM_CHANGE_END_BATTLE, SPECIES_METAGROSS}, {FORM_CHANGE_TERMINATOR}, }; @@ -659,6 +714,7 @@ static const struct FormChange sLatiasFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_LATIAS_MEGA, ITEM_LATIASITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_LATIAS}, {FORM_CHANGE_END_BATTLE, SPECIES_LATIAS}, {FORM_CHANGE_TERMINATOR}, }; @@ -670,6 +726,7 @@ static const struct FormChange sLatiosFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_LATIOS_MEGA, ITEM_LATIOSITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_LATIOS}, {FORM_CHANGE_END_BATTLE, SPECIES_LATIOS}, {FORM_CHANGE_TERMINATOR}, }; @@ -703,6 +760,7 @@ static const struct FormChange sRayquazaFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE, SPECIES_RAYQUAZA_MEGA, MOVE_DRAGON_ASCENT}, #endif + {FORM_CHANGE_FAINT, SPECIES_RAYQUAZA}, {FORM_CHANGE_END_BATTLE, SPECIES_RAYQUAZA}, {FORM_CHANGE_TERMINATOR}, }; @@ -739,6 +797,7 @@ static const struct FormChange sStaraptorFormChangeTable[] = { #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_STARAPTOR_MEGA, ITEM_STARAPTITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_STARAPTOR}, {FORM_CHANGE_END_BATTLE, SPECIES_STARAPTOR}, {FORM_CHANGE_TERMINATOR}, }; @@ -777,7 +836,7 @@ static const struct FormChange sCherrimFormChangeTable[] = {FORM_CHANGE_BATTLE_WEATHER, SPECIES_CHERRIM_OVERCAST, ~B_WEATHER_SUN}, {FORM_CHANGE_BATTLE_WEATHER, SPECIES_CHERRIM_OVERCAST, B_WEATHER_NONE}, #endif - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_CHERRIM_OVERCAST}, + {FORM_CHANGE_BATTLE_SWITCH_OUT, SPECIES_CHERRIM_OVERCAST}, {FORM_CHANGE_FAINT, SPECIES_CHERRIM_OVERCAST}, {FORM_CHANGE_END_BATTLE, SPECIES_CHERRIM_OVERCAST}, {FORM_CHANGE_TERMINATOR}, @@ -790,6 +849,7 @@ static const struct FormChange sLopunnyFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_LOPUNNY_MEGA, ITEM_LOPUNNITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_LOPUNNY}, {FORM_CHANGE_END_BATTLE, SPECIES_LOPUNNY}, {FORM_CHANGE_TERMINATOR}, }; @@ -804,6 +864,7 @@ static const struct FormChange sGarchompFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_GARCHOMP_MEGA_Z, ITEM_GARCHOMPITE_Z}, #endif + {FORM_CHANGE_FAINT, SPECIES_GARCHOMP}, {FORM_CHANGE_END_BATTLE, SPECIES_GARCHOMP}, {FORM_CHANGE_TERMINATOR}, }; @@ -818,6 +879,7 @@ static const struct FormChange sLucarioFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_LUCARIO_MEGA_Z, ITEM_LUCARIONITE_Z}, #endif + {FORM_CHANGE_FAINT, SPECIES_LUCARIO}, {FORM_CHANGE_END_BATTLE, SPECIES_LUCARIO}, {FORM_CHANGE_TERMINATOR}, }; @@ -829,6 +891,7 @@ static const struct FormChange sAbomasnowFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_ABOMASNOW_MEGA, ITEM_ABOMASITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_ABOMASNOW}, {FORM_CHANGE_END_BATTLE, SPECIES_ABOMASNOW}, {FORM_CHANGE_TERMINATOR}, }; @@ -870,6 +933,7 @@ static const struct FormChange sHeatranFormChangeTable[] = { #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_HEATRAN_MEGA, ITEM_HEATRANITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_HEATRAN}, {FORM_CHANGE_END_BATTLE, SPECIES_HEATRAN}, {FORM_CHANGE_TERMINATOR}, }; @@ -892,6 +956,7 @@ static const struct FormChange sDarkraiFormChangeTable[] = { #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_DARKRAI_MEGA, ITEM_DARKRANITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_DARKRAI}, {FORM_CHANGE_END_BATTLE, SPECIES_DARKRAI}, {FORM_CHANGE_TERMINATOR}, }; @@ -956,6 +1021,7 @@ static const struct FormChange sEmboarFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_EMBOAR_MEGA, ITEM_EMBOARITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_EMBOAR}, {FORM_CHANGE_END_BATTLE, SPECIES_EMBOAR}, {FORM_CHANGE_TERMINATOR}, }; @@ -967,6 +1033,7 @@ static const struct FormChange sExcadrillFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_EXCADRILL_MEGA, ITEM_EXCADRITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_EXCADRILL}, {FORM_CHANGE_END_BATTLE, SPECIES_EXCADRILL}, {FORM_CHANGE_TERMINATOR}, }; @@ -978,6 +1045,7 @@ static const struct FormChange sAudinoFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_AUDINO_MEGA, ITEM_AUDINITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_AUDINO}, {FORM_CHANGE_END_BATTLE, SPECIES_AUDINO}, {FORM_CHANGE_TERMINATOR}, }; @@ -989,6 +1057,7 @@ static const struct FormChange sScolipedeFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_SCOLIPEDE_MEGA, ITEM_SCOLIPITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_SCOLIPEDE}, {FORM_CHANGE_END_BATTLE, SPECIES_SCOLIPEDE}, {FORM_CHANGE_TERMINATOR}, }; @@ -997,20 +1066,24 @@ static const struct FormChange sScolipedeFormChangeTable[] = #if P_FAMILY_DARUMAKA static const struct FormChange sDarmanitanFormChangeTable[] = { - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_DARMANITAN_STANDARD, ABILITY_ZEN_MODE, HP_HIGHER_THAN, 50}, - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_DARMANITAN_ZEN, ABILITY_ZEN_MODE, HP_LOWER_EQ_THAN, 50}, - {FORM_CHANGE_FAINT, SPECIES_DARMANITAN_STANDARD}, - {FORM_CHANGE_END_BATTLE, SPECIES_DARMANITAN_STANDARD}, + {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_DARMANITAN_STANDARD, ABILITY_ZEN_MODE, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_DARMANITAN_ZEN, ABILITY_ZEN_MODE, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_DARMANITAN_STANDARD, ABILITY_ZEN_MODE, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_DARMANITAN_ZEN, ABILITY_ZEN_MODE, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_FAINT, SPECIES_DARMANITAN_STANDARD}, + {FORM_CHANGE_END_BATTLE, SPECIES_DARMANITAN_STANDARD}, {FORM_CHANGE_TERMINATOR}, }; #if P_GALARIAN_FORMS static const struct FormChange sDarmanitanGalarFormChangeTable[] = { - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_DARMANITAN_GALAR_STANDARD, ABILITY_ZEN_MODE, HP_HIGHER_THAN, 50}, - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_DARMANITAN_GALAR_ZEN, ABILITY_ZEN_MODE, HP_LOWER_EQ_THAN, 50}, - {FORM_CHANGE_FAINT, SPECIES_DARMANITAN_GALAR_STANDARD}, - {FORM_CHANGE_END_BATTLE, SPECIES_DARMANITAN_GALAR_STANDARD}, + {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_DARMANITAN_GALAR_STANDARD, ABILITY_ZEN_MODE, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_DARMANITAN_GALAR_ZEN, ABILITY_ZEN_MODE, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_DARMANITAN_GALAR_STANDARD, ABILITY_ZEN_MODE, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_DARMANITAN_GALAR_ZEN, ABILITY_ZEN_MODE, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_FAINT, SPECIES_DARMANITAN_GALAR_STANDARD}, + {FORM_CHANGE_END_BATTLE, SPECIES_DARMANITAN_GALAR_STANDARD}, {FORM_CHANGE_TERMINATOR}, }; #endif //P_GALARIAN_FORMS @@ -1022,6 +1095,7 @@ static const struct FormChange sScraftyFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_SCRAFTY_MEGA, ITEM_SCRAFTINITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_SCRAFTY}, {FORM_CHANGE_END_BATTLE, SPECIES_SCRAFTY}, {FORM_CHANGE_TERMINATOR}, }; @@ -1033,6 +1107,7 @@ static const struct FormChange sGarbodorFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_GARBODOR_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_GARBODOR}, {FORM_CHANGE_END_BATTLE, SPECIES_GARBODOR}, {FORM_CHANGE_TERMINATOR}, }; @@ -1044,6 +1119,7 @@ static const struct FormChange sEelektrossFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_EELEKTROSS_MEGA, ITEM_EELEKTROSSITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_EELEKTROSS}, {FORM_CHANGE_END_BATTLE, SPECIES_EELEKTROSS}, {FORM_CHANGE_TERMINATOR}, }; @@ -1055,6 +1131,7 @@ static const struct FormChange sChandelureFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_CHANDELURE_MEGA, ITEM_CHANDELURITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_CHANDELURE}, {FORM_CHANGE_END_BATTLE, SPECIES_CHANDELURE}, {FORM_CHANGE_TERMINATOR}, }; @@ -1065,6 +1142,7 @@ static const struct FormChange sGolurkFormChangeTable[] = { #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_GOLURK_MEGA, ITEM_GOLURKITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_GOLURK}, {FORM_CHANGE_END_BATTLE, SPECIES_GOLURK}, {FORM_CHANGE_TERMINATOR}, }; @@ -1143,6 +1221,7 @@ static const struct FormChange sChesnaughtFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_CHESNAUGHT_MEGA, ITEM_CHESNAUGHTITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_CHESNAUGHT}, {FORM_CHANGE_END_BATTLE, SPECIES_CHESNAUGHT}, {FORM_CHANGE_TERMINATOR}, }; @@ -1154,6 +1233,7 @@ static const struct FormChange sDelphoxFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_DELPHOX_MEGA, ITEM_DELPHOXITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_DELPHOX}, {FORM_CHANGE_END_BATTLE, SPECIES_DELPHOX}, {FORM_CHANGE_TERMINATOR}, }; @@ -1165,6 +1245,7 @@ static const struct FormChange sGreninjaFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_GRENINJA_MEGA, ITEM_GRENINJITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_GRENINJA}, {FORM_CHANGE_END_BATTLE, SPECIES_GRENINJA}, {FORM_CHANGE_TERMINATOR}, }; @@ -1183,6 +1264,7 @@ static const struct FormChange sPyroarFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_PYROAR_MEGA, ITEM_PYROARITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_PYROAR}, {FORM_CHANGE_END_BATTLE, SPECIES_PYROAR}, {FORM_CHANGE_TERMINATOR}, }; @@ -1194,6 +1276,7 @@ static const struct FormChange sFloetteEternalFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_FLOETTE_MEGA, ITEM_FLOETTITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_FLOETTE}, {FORM_CHANGE_END_BATTLE, SPECIES_FLOETTE}, {FORM_CHANGE_TERMINATOR}, }; @@ -1213,6 +1296,7 @@ static const struct FormChange sMeowsticMFormChangeTable[] = { #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_MEOWSTIC_M_MEGA, ITEM_MEOWSTICITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_MEOWSTIC_M}, {FORM_CHANGE_END_BATTLE, SPECIES_MEOWSTIC_M}, {FORM_CHANGE_TERMINATOR}, }; @@ -1221,6 +1305,7 @@ static const struct FormChange sMeowsticFFormChangeTable[] = { #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_MEOWSTIC_F_MEGA, ITEM_MEOWSTICITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_MEOWSTIC_F}, {FORM_CHANGE_END_BATTLE, SPECIES_MEOWSTIC_F}, {FORM_CHANGE_TERMINATOR}, }; @@ -1232,7 +1317,7 @@ static const struct FormChange sAegislashFormChangeTable[] = {FORM_CHANGE_BATTLE_BEFORE_MOVE_CATEGORY, SPECIES_AEGISLASH_BLADE, DAMAGE_CATEGORY_PHYSICAL, ABILITY_STANCE_CHANGE}, {FORM_CHANGE_BATTLE_BEFORE_MOVE_CATEGORY, SPECIES_AEGISLASH_BLADE, DAMAGE_CATEGORY_SPECIAL, ABILITY_STANCE_CHANGE}, {FORM_CHANGE_BATTLE_BEFORE_MOVE, SPECIES_AEGISLASH_SHIELD, MOVE_KINGS_SHIELD, ABILITY_STANCE_CHANGE}, - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_AEGISLASH_SHIELD}, + {FORM_CHANGE_BATTLE_SWITCH_OUT, SPECIES_AEGISLASH_SHIELD}, {FORM_CHANGE_FAINT, SPECIES_AEGISLASH_SHIELD}, {FORM_CHANGE_END_BATTLE, SPECIES_AEGISLASH_SHIELD}, {FORM_CHANGE_TERMINATOR}, @@ -1245,6 +1330,7 @@ static const struct FormChange sMalamarFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_MALAMAR_MEGA, ITEM_MALAMARITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_MALAMAR}, {FORM_CHANGE_END_BATTLE, SPECIES_MALAMAR}, {FORM_CHANGE_TERMINATOR}, }; @@ -1256,6 +1342,7 @@ static const struct FormChange sBarbaracleFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_BARBARACLE_MEGA, ITEM_BARBARACITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_BARBARACLE}, {FORM_CHANGE_END_BATTLE, SPECIES_BARBARACLE}, {FORM_CHANGE_TERMINATOR}, }; @@ -1267,6 +1354,7 @@ static const struct FormChange sDragalgeFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_DRAGALGE_MEGA, ITEM_DRAGALGITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_DRAGALGE}, {FORM_CHANGE_END_BATTLE, SPECIES_DRAGALGE}, {FORM_CHANGE_TERMINATOR}, }; @@ -1278,6 +1366,7 @@ static const struct FormChange sHawluchaFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_HAWLUCHA_MEGA, ITEM_HAWLUCHANITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_HAWLUCHA}, {FORM_CHANGE_END_BATTLE, SPECIES_HAWLUCHA}, {FORM_CHANGE_TERMINATOR}, }; @@ -1309,17 +1398,17 @@ static const struct FormChange sZygarde10AuraBreakFormChangeTable[] = static const struct FormChange sZygarde50PowerConstructFormChangeTable[] = { - {FORM_CHANGE_ITEM_USE_MULTICHOICE, SPECIES_ZYGARDE_10_POWER_CONSTRUCT, ITEM_ZYGARDE_CUBE, 0}, - {FORM_CHANGE_ITEM_USE_MULTICHOICE, SPECIES_ZYGARDE_50, ITEM_ZYGARDE_CUBE, 1}, - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_ZYGARDE_COMPLETE, ABILITY_POWER_CONSTRUCT, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_ITEM_USE_MULTICHOICE, SPECIES_ZYGARDE_10_POWER_CONSTRUCT, ITEM_ZYGARDE_CUBE, 0}, + {FORM_CHANGE_ITEM_USE_MULTICHOICE, SPECIES_ZYGARDE_50, ITEM_ZYGARDE_CUBE, 1}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_ZYGARDE_COMPLETE, ABILITY_POWER_CONSTRUCT, HP_LOWER_EQ_THAN, 50}, {FORM_CHANGE_TERMINATOR}, }; static const struct FormChange sZygarde10PowerConstructFormChangeTable[] = { - {FORM_CHANGE_ITEM_USE_MULTICHOICE, SPECIES_ZYGARDE_50_POWER_CONSTRUCT, ITEM_ZYGARDE_CUBE, 0}, - {FORM_CHANGE_ITEM_USE_MULTICHOICE, SPECIES_ZYGARDE_10_AURA_BREAK, ITEM_ZYGARDE_CUBE, 1}, - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_ZYGARDE_COMPLETE, ABILITY_POWER_CONSTRUCT, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_ITEM_USE_MULTICHOICE, SPECIES_ZYGARDE_50_POWER_CONSTRUCT, ITEM_ZYGARDE_CUBE, 0}, + {FORM_CHANGE_ITEM_USE_MULTICHOICE, SPECIES_ZYGARDE_10_AURA_BREAK, ITEM_ZYGARDE_CUBE, 1}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_ZYGARDE_COMPLETE, ABILITY_POWER_CONSTRUCT, HP_LOWER_EQ_THAN, 50}, {FORM_CHANGE_TERMINATOR}, }; @@ -1340,6 +1429,7 @@ static const struct FormChange sDiancieFormChangeTable[] = #if P_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_DIANCIE_MEGA, ITEM_DIANCITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_DIANCIE}, {FORM_CHANGE_END_BATTLE, SPECIES_DIANCIE}, {FORM_CHANGE_TERMINATOR}, }; @@ -1360,6 +1450,7 @@ static const struct FormChange sCrabominableFormChangeTable[] = { #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_CRABOMINABLE_MEGA, ITEM_CRABOMINITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_CRABOMINABLE}, {FORM_CHANGE_END_BATTLE, SPECIES_CRABOMINABLE}, {FORM_CHANGE_TERMINATOR}, }; @@ -1379,11 +1470,13 @@ static const struct FormChange sOricorioFormChangeTable[] = #if P_FAMILY_WISHIWASHI static const struct FormChange sWishiwashiFormChangeTable[] = { - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_WISHIWASHI_SCHOOL, ABILITY_SCHOOLING, HP_HIGHER_THAN, 25}, - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_WISHIWASHI_SOLO, ABILITY_SCHOOLING, HP_LOWER_EQ_THAN, 25}, - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_WISHIWASHI_SOLO}, - {FORM_CHANGE_FAINT, SPECIES_WISHIWASHI_SOLO}, - {FORM_CHANGE_END_BATTLE, SPECIES_WISHIWASHI_SOLO}, + {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_WISHIWASHI_SCHOOL, ABILITY_SCHOOLING, HP_HIGHER_THAN, 25, 20}, + {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_WISHIWASHI_SOLO, ABILITY_SCHOOLING, HP_LOWER_EQ_THAN, 25}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_WISHIWASHI_SCHOOL, ABILITY_SCHOOLING, HP_HIGHER_THAN, 25, 20}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_WISHIWASHI_SOLO, ABILITY_SCHOOLING, HP_LOWER_EQ_THAN, 25}, + {FORM_CHANGE_BATTLE_SWITCH_OUT, SPECIES_WISHIWASHI_SOLO}, + {FORM_CHANGE_FAINT, SPECIES_WISHIWASHI_SOLO}, + {FORM_CHANGE_END_BATTLE, SPECIES_WISHIWASHI_SOLO}, {FORM_CHANGE_TERMINATOR}, }; #endif //P_FAMILY_WISHIWASHI @@ -1393,6 +1486,7 @@ static const struct FormChange sGolisopodFormChangeTable[] = { #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_GOLISOPOD_MEGA, ITEM_GOLISOPITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_GOLISOPOD}, {FORM_CHANGE_END_BATTLE, SPECIES_GOLISOPOD}, {FORM_CHANGE_TERMINATOR}, }; @@ -1426,71 +1520,86 @@ static const struct FormChange sSilvallyFormChangeTable[] = #if P_FAMILY_MINIOR static const struct FormChange sMiniorRedFormChangeTable[] = { - {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_RED}, - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_RED, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_RED, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_RED}, - {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_RED}, - {FORM_CHANGE_END_BATTLE, SPECIES_MINIOR_CORE_RED}, + {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_RED}, + {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_METEOR_RED, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_CORE_RED, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_MINIOR_METEOR_RED, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_MINIOR_CORE_RED, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_SWITCH_OUT, SPECIES_MINIOR_CORE_RED}, + {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_RED}, + {FORM_CHANGE_END_BATTLE, SPECIES_MINIOR_CORE_RED}, {FORM_CHANGE_TERMINATOR}, }; static const struct FormChange sMiniorBlueFormChangeTable[] = { - {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_BLUE}, - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_BLUE, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_BLUE, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_BLUE}, - {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_BLUE}, - {FORM_CHANGE_END_BATTLE, SPECIES_MINIOR_CORE_BLUE}, + {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_BLUE}, + {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_METEOR_BLUE, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_CORE_BLUE, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_MINIOR_METEOR_BLUE, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_MINIOR_CORE_BLUE, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_SWITCH_OUT, SPECIES_MINIOR_CORE_BLUE}, + {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_BLUE}, + {FORM_CHANGE_END_BATTLE, SPECIES_MINIOR_CORE_BLUE}, {FORM_CHANGE_TERMINATOR}, }; static const struct FormChange sMiniorGreenFormChangeTable[] = { - {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_GREEN}, - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_GREEN, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_GREEN, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_GREEN}, - {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_GREEN}, - {FORM_CHANGE_END_BATTLE, SPECIES_MINIOR_CORE_GREEN}, + {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_GREEN}, + {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_METEOR_GREEN, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_CORE_GREEN, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_MINIOR_METEOR_GREEN, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_MINIOR_CORE_GREEN, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_SWITCH_OUT, SPECIES_MINIOR_CORE_GREEN}, + {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_GREEN}, + {FORM_CHANGE_END_BATTLE, SPECIES_MINIOR_CORE_GREEN}, {FORM_CHANGE_TERMINATOR}, }; static const struct FormChange sMiniorIndigoFormChangeTable[] = { - {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_INDIGO}, - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_INDIGO, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_INDIGO, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_INDIGO}, - {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_INDIGO}, - {FORM_CHANGE_END_BATTLE, SPECIES_MINIOR_CORE_INDIGO}, + {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_INDIGO}, + {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_METEOR_INDIGO, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_CORE_INDIGO, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_MINIOR_METEOR_INDIGO, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_MINIOR_CORE_INDIGO, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_SWITCH_OUT, SPECIES_MINIOR_CORE_INDIGO}, + {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_INDIGO}, + {FORM_CHANGE_END_BATTLE, SPECIES_MINIOR_CORE_INDIGO}, {FORM_CHANGE_TERMINATOR}, }; static const struct FormChange sMiniorOrangeFormChangeTable[] = { - {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_ORANGE}, - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_ORANGE, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_ORANGE, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_ORANGE}, - {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_ORANGE}, - {FORM_CHANGE_END_BATTLE, SPECIES_MINIOR_CORE_ORANGE}, + {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_ORANGE}, + {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_METEOR_ORANGE, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_CORE_ORANGE, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_MINIOR_METEOR_ORANGE, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_MINIOR_CORE_ORANGE, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_SWITCH_OUT, SPECIES_MINIOR_CORE_ORANGE}, + {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_ORANGE}, + {FORM_CHANGE_END_BATTLE, SPECIES_MINIOR_CORE_ORANGE}, {FORM_CHANGE_TERMINATOR}, }; static const struct FormChange sMiniorVioletFormChangeTable[] = { - {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_VIOLET}, - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_VIOLET, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_VIOLET, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_VIOLET}, - {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_VIOLET}, - {FORM_CHANGE_END_BATTLE, SPECIES_MINIOR_CORE_VIOLET}, + {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_VIOLET}, + {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_METEOR_VIOLET, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_CORE_VIOLET, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_MINIOR_METEOR_VIOLET, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_MINIOR_CORE_VIOLET, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_SWITCH_OUT, SPECIES_MINIOR_CORE_VIOLET}, + {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_VIOLET}, + {FORM_CHANGE_END_BATTLE, SPECIES_MINIOR_CORE_VIOLET}, {FORM_CHANGE_TERMINATOR}, }; -static const struct FormChange sMiniorYellowFormChangeTable[] = { - {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_YELLOW}, - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_YELLOW, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_YELLOW, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_YELLOW}, - {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_YELLOW}, - {FORM_CHANGE_END_BATTLE, SPECIES_MINIOR_CORE_YELLOW}, +static const struct FormChange sMiniorYellowFormChangeTable[] = +{ + {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_YELLOW}, + {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_METEOR_YELLOW, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_CORE_YELLOW, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_MINIOR_METEOR_YELLOW, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, + {FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END, SPECIES_MINIOR_CORE_YELLOW, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, + {FORM_CHANGE_BATTLE_SWITCH_OUT, SPECIES_MINIOR_CORE_YELLOW}, + {FORM_CHANGE_FAINT, SPECIES_MINIOR_CORE_YELLOW}, + {FORM_CHANGE_END_BATTLE, SPECIES_MINIOR_CORE_YELLOW}, {FORM_CHANGE_TERMINATOR}, }; #endif //P_FAMILY_MINIOR @@ -1498,15 +1607,21 @@ static const struct FormChange sMiniorYellowFormChangeTable[] = { #if P_FAMILY_MIMIKYU static const struct FormChange sMimikyuFormChangeTable[] = { - {FORM_CHANGE_FAINT, SPECIES_MIMIKYU_DISGUISED}, - {FORM_CHANGE_END_BATTLE, SPECIES_MIMIKYU_DISGUISED}, + {FORM_CHANGE_BATTLE_HIT_BY_CONFUSION_SELF_DMG, SPECIES_MIMIKYU_BUSTED, ABILITY_DISGUISE}, + {FORM_CHANGE_BATTLE_HIT_BY_MOVE_CATEGORY, SPECIES_MIMIKYU_BUSTED, ABILITY_DISGUISE, DAMAGE_CATEGORY_PHYSICAL}, + {FORM_CHANGE_BATTLE_HIT_BY_MOVE_CATEGORY, SPECIES_MIMIKYU_BUSTED, ABILITY_DISGUISE, DAMAGE_CATEGORY_SPECIAL}, + {FORM_CHANGE_FAINT, SPECIES_MIMIKYU_DISGUISED}, + {FORM_CHANGE_END_BATTLE, SPECIES_MIMIKYU_DISGUISED}, {FORM_CHANGE_TERMINATOR}, }; static const struct FormChange sMimikyuTotemFormChangeTable[] = { - {FORM_CHANGE_FAINT, SPECIES_MIMIKYU_TOTEM_DISGUISED}, - {FORM_CHANGE_END_BATTLE, SPECIES_MIMIKYU_TOTEM_DISGUISED}, + {FORM_CHANGE_BATTLE_HIT_BY_CONFUSION_SELF_DMG, SPECIES_MIMIKYU_BUSTED_TOTEM, ABILITY_DISGUISE}, + {FORM_CHANGE_BATTLE_HIT_BY_MOVE_CATEGORY, SPECIES_MIMIKYU_BUSTED_TOTEM, ABILITY_DISGUISE, DAMAGE_CATEGORY_PHYSICAL}, + {FORM_CHANGE_BATTLE_HIT_BY_MOVE_CATEGORY, SPECIES_MIMIKYU_BUSTED_TOTEM, ABILITY_DISGUISE, DAMAGE_CATEGORY_SPECIAL}, + {FORM_CHANGE_FAINT, SPECIES_MIMIKYU_TOTEM_DISGUISED}, + {FORM_CHANGE_END_BATTLE, SPECIES_MIMIKYU_TOTEM_DISGUISED}, {FORM_CHANGE_TERMINATOR}, }; #endif //P_FAMILY_MIMIKYU @@ -1517,6 +1632,7 @@ static const struct FormChange sDrampaFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_DRAMPA_MEGA, ITEM_DRAMPANITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_DRAMPA}, {FORM_CHANGE_END_BATTLE, SPECIES_DRAMPA}, {FORM_CHANGE_TERMINATOR}, }; @@ -1534,6 +1650,7 @@ static const struct FormChange sNecrozmaDuskManeFormChangeTable[] = #if P_ULTRA_BURST_FORMS {FORM_CHANGE_BATTLE_ULTRA_BURST, SPECIES_NECROZMA_ULTRA, ITEM_ULTRANECROZIUM_Z}, #endif + {FORM_CHANGE_FAINT, SPECIES_NECROZMA_DUSK_MANE}, {FORM_CHANGE_END_BATTLE, SPECIES_NECROZMA_DUSK_MANE}, {FORM_CHANGE_TERMINATOR}, }; @@ -1542,11 +1659,13 @@ static const struct FormChange sNecrozmaDawnWingsFormChangeTable[] = #if P_ULTRA_BURST_FORMS {FORM_CHANGE_BATTLE_ULTRA_BURST, SPECIES_NECROZMA_ULTRA, ITEM_ULTRANECROZIUM_Z}, #endif + {FORM_CHANGE_FAINT, SPECIES_NECROZMA_DAWN_WINGS}, {FORM_CHANGE_END_BATTLE, SPECIES_NECROZMA_DAWN_WINGS}, {FORM_CHANGE_TERMINATOR}, }; static const struct FormChange sNecrozmaUltraFormChangeTable[] = { + {FORM_CHANGE_FAINT}, {FORM_CHANGE_END_BATTLE}, {FORM_CHANGE_TERMINATOR}, }; @@ -1557,6 +1676,7 @@ static const struct FormChange sMagearnaFormChangeTable[] = { #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_MAGEARNA_MEGA, ITEM_MAGEARNITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_MAGEARNA}, {FORM_CHANGE_END_BATTLE, SPECIES_MAGEARNA}, {FORM_CHANGE_TERMINATOR}, }; @@ -1565,6 +1685,7 @@ static const struct FormChange sMagearnaOriginalFormChangeTable[] = { #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_MAGEARNA_ORIGINAL_MEGA, ITEM_MAGEARNITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_MAGEARNA_ORIGINAL}, {FORM_CHANGE_END_BATTLE, SPECIES_MAGEARNA_ORIGINAL}, {FORM_CHANGE_TERMINATOR}, }; @@ -1575,6 +1696,7 @@ static const struct FormChange sZeraoraFormChangeTable[] = { #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_ZERAORA_MEGA, ITEM_ZERAORITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_ZERAORA}, {FORM_CHANGE_END_BATTLE, SPECIES_ZERAORA}, {FORM_CHANGE_TERMINATOR}, }; @@ -1586,6 +1708,7 @@ static const struct FormChange sMelmetalFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_MELMETAL_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_MELMETAL}, {FORM_CHANGE_END_BATTLE, SPECIES_MELMETAL}, {FORM_CHANGE_TERMINATOR}, }; @@ -1597,6 +1720,7 @@ static const struct FormChange sRillaboomFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_RILLABOOM_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_RILLABOOM}, {FORM_CHANGE_END_BATTLE, SPECIES_RILLABOOM}, {FORM_CHANGE_TERMINATOR}, }; @@ -1608,6 +1732,7 @@ static const struct FormChange sCinderaceFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_CINDERACE_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_CINDERACE}, {FORM_CHANGE_END_BATTLE, SPECIES_CINDERACE}, {FORM_CHANGE_TERMINATOR}, }; @@ -1619,6 +1744,7 @@ static const struct FormChange sInteleonFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_INTELEON_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_INTELEON}, {FORM_CHANGE_END_BATTLE, SPECIES_INTELEON}, {FORM_CHANGE_TERMINATOR}, }; @@ -1630,6 +1756,7 @@ static const struct FormChange sCorviknightFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_CORVIKNIGHT_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_CORVIKNIGHT}, {FORM_CHANGE_END_BATTLE, SPECIES_CORVIKNIGHT}, {FORM_CHANGE_TERMINATOR}, }; @@ -1641,6 +1768,7 @@ static const struct FormChange sOrbeetleFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_ORBEETLE_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_ORBEETLE}, {FORM_CHANGE_END_BATTLE, SPECIES_ORBEETLE}, {FORM_CHANGE_TERMINATOR}, }; @@ -1652,6 +1780,7 @@ static const struct FormChange sDrednawFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_DREDNAW_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_DREDNAW}, {FORM_CHANGE_END_BATTLE, SPECIES_DREDNAW}, {FORM_CHANGE_TERMINATOR}, }; @@ -1663,6 +1792,7 @@ static const struct FormChange sCoalossalFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_COALOSSAL_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_COALOSSAL}, {FORM_CHANGE_END_BATTLE, SPECIES_COALOSSAL}, {FORM_CHANGE_TERMINATOR}, }; @@ -1674,6 +1804,7 @@ static const struct FormChange sFlappleFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_FLAPPLE_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_FLAPPLE}, {FORM_CHANGE_END_BATTLE, SPECIES_FLAPPLE}, {FORM_CHANGE_TERMINATOR}, }; @@ -1683,6 +1814,7 @@ static const struct FormChange sAppletunFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_APPLETUN_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_APPLETUN}, {FORM_CHANGE_END_BATTLE, SPECIES_APPLETUN}, {FORM_CHANGE_TERMINATOR}, }; @@ -1694,6 +1826,7 @@ static const struct FormChange sSandacondaFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_SANDACONDA_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_SANDACONDA}, {FORM_CHANGE_END_BATTLE, SPECIES_SANDACONDA}, {FORM_CHANGE_TERMINATOR}, }; @@ -1702,12 +1835,15 @@ static const struct FormChange sSandacondaFormChangeTable[] = #if P_FAMILY_CRAMORANT static const struct FormChange sCramorantFormChangeTable[] = { - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_CRAMORANT_GULPING, ABILITY_GULP_MISSILE, HP_HIGHER_THAN, 50}, - {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_CRAMORANT_GORGING, ABILITY_GULP_MISSILE, HP_LOWER_EQ_THAN, 50}, - {FORM_CHANGE_HIT_BY_MOVE, SPECIES_CRAMORANT, ABILITY_GULP_MISSILE}, - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_CRAMORANT}, - {FORM_CHANGE_FAINT, SPECIES_CRAMORANT}, - {FORM_CHANGE_END_BATTLE, SPECIES_CRAMORANT}, + {FORM_CHANGE_BATTLE_HP_PERCENT_DURING_MOVE, SPECIES_CRAMORANT_GULPING, ABILITY_GULP_MISSILE, HP_HIGHER_THAN, 50, MOVE_SURF}, + {FORM_CHANGE_BATTLE_HP_PERCENT_DURING_MOVE, SPECIES_CRAMORANT_GORGING, ABILITY_GULP_MISSILE, HP_LOWER_EQ_THAN, 50, MOVE_SURF}, + {FORM_CHANGE_BATTLE_HP_PERCENT_DURING_MOVE, SPECIES_CRAMORANT_GULPING, ABILITY_GULP_MISSILE, HP_HIGHER_THAN, 50, MOVE_DIVE}, + {FORM_CHANGE_BATTLE_HP_PERCENT_DURING_MOVE, SPECIES_CRAMORANT_GORGING, ABILITY_GULP_MISSILE, HP_LOWER_EQ_THAN, 50, MOVE_DIVE}, + {FORM_CHANGE_BATTLE_HIT_BY_MOVE_CATEGORY, SPECIES_CRAMORANT, ABILITY_GULP_MISSILE, DAMAGE_CATEGORY_PHYSICAL}, + {FORM_CHANGE_BATTLE_HIT_BY_MOVE_CATEGORY, SPECIES_CRAMORANT, ABILITY_GULP_MISSILE, DAMAGE_CATEGORY_SPECIAL}, + {FORM_CHANGE_BATTLE_SWITCH_OUT, SPECIES_CRAMORANT}, + {FORM_CHANGE_FAINT, SPECIES_CRAMORANT}, + {FORM_CHANGE_END_BATTLE, SPECIES_CRAMORANT}, {FORM_CHANGE_TERMINATOR}, }; #endif //P_FAMILY_CRAMORANT @@ -1718,6 +1854,7 @@ static const struct FormChange sToxtricityAmpedFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_TOXTRICITY_AMPED_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_TOXTRICITY_AMPED}, {FORM_CHANGE_END_BATTLE, SPECIES_TOXTRICITY_AMPED}, {FORM_CHANGE_TERMINATOR}, }; @@ -1727,6 +1864,7 @@ static const struct FormChange sToxtricityLowKeyFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_TOXTRICITY_LOW_KEY_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_TOXTRICITY_LOW_KEY}, {FORM_CHANGE_END_BATTLE, SPECIES_TOXTRICITY_LOW_KEY}, {FORM_CHANGE_TERMINATOR}, }; @@ -1738,6 +1876,7 @@ static const struct FormChange sCentiskorchFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_CENTISKORCH_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_CENTISKORCH}, {FORM_CHANGE_END_BATTLE, SPECIES_CENTISKORCH}, {FORM_CHANGE_TERMINATOR}, }; @@ -1749,6 +1888,7 @@ static const struct FormChange sHattereneFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_HATTERENE_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_HATTERENE}, {FORM_CHANGE_END_BATTLE, SPECIES_HATTERENE}, {FORM_CHANGE_TERMINATOR}, }; @@ -1760,6 +1900,7 @@ static const struct FormChange sGrimmsnarlFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_GRIMMSNARL_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_GRIMMSNARL}, {FORM_CHANGE_END_BATTLE, SPECIES_GRIMMSNARL}, {FORM_CHANGE_TERMINATOR}, }; @@ -1771,6 +1912,7 @@ static const struct FormChange sAlcremieFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_ALCREMIE_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_ALCREMIE}, {FORM_CHANGE_END_BATTLE, SPECIES_ALCREMIE}, {FORM_CHANGE_TERMINATOR}, }; @@ -1782,6 +1924,7 @@ static const struct FormChange sFalinksFormChangeTable[] = #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_FALINKS_MEGA, ITEM_FALINKSITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_FALINKS}, {FORM_CHANGE_END_BATTLE, SPECIES_FALINKS}, {FORM_CHANGE_TERMINATOR}, }; @@ -1790,9 +1933,11 @@ static const struct FormChange sFalinksFormChangeTable[] = #if P_FAMILY_EISCUE static const struct FormChange sEiscueFormChangeTable[] = { - {FORM_CHANGE_BATTLE_WEATHER, SPECIES_EISCUE_ICE, B_WEATHER_ICY_ANY, ABILITY_ICE_FACE}, - {FORM_CHANGE_FAINT, SPECIES_EISCUE_ICE}, - {FORM_CHANGE_END_BATTLE, SPECIES_EISCUE_ICE}, + {FORM_CHANGE_BATTLE_WEATHER, SPECIES_EISCUE_ICE, B_WEATHER_ICY_ANY, ABILITY_ICE_FACE}, + {FORM_CHANGE_BATTLE_HIT_BY_CONFUSION_SELF_DMG, SPECIES_EISCUE_NOICE, ABILITY_ICE_FACE}, + {FORM_CHANGE_BATTLE_HIT_BY_MOVE_CATEGORY, SPECIES_EISCUE_NOICE, ABILITY_ICE_FACE, DAMAGE_CATEGORY_PHYSICAL}, + {FORM_CHANGE_FAINT, SPECIES_EISCUE_ICE}, + {FORM_CHANGE_END_BATTLE, SPECIES_EISCUE_ICE}, {FORM_CHANGE_TERMINATOR}, }; #endif //P_FAMILY_EISCUE @@ -1802,7 +1947,7 @@ static const struct FormChange sMorpekoFormChangeTable[] = { {FORM_CHANGE_BATTLE_TURN_END, SPECIES_MORPEKO_HANGRY, ABILITY_HUNGER_SWITCH}, {FORM_CHANGE_BATTLE_TURN_END, SPECIES_MORPEKO_FULL_BELLY, ABILITY_HUNGER_SWITCH}, - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MORPEKO_FULL_BELLY}, + {FORM_CHANGE_BATTLE_SWITCH_OUT, SPECIES_MORPEKO_FULL_BELLY}, {FORM_CHANGE_FAINT, SPECIES_MORPEKO_FULL_BELLY}, {FORM_CHANGE_END_BATTLE, SPECIES_MORPEKO_FULL_BELLY}, {FORM_CHANGE_TERMINATOR}, @@ -1815,6 +1960,7 @@ static const struct FormChange sCopperajahFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_COPPERAJAH_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_COPPERAJAH}, {FORM_CHANGE_END_BATTLE, SPECIES_COPPERAJAH}, {FORM_CHANGE_TERMINATOR}, }; @@ -1826,6 +1972,7 @@ static const struct FormChange sDuraludonFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_DURALUDON_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_DURALUDON}, {FORM_CHANGE_END_BATTLE, SPECIES_DURALUDON}, {FORM_CHANGE_TERMINATOR}, }; @@ -1855,6 +2002,7 @@ static const struct FormChange sUrshifuSingleStrikeFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_URSHIFU_SINGLE_STRIKE_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_URSHIFU_SINGLE_STRIKE}, {FORM_CHANGE_END_BATTLE, SPECIES_URSHIFU_SINGLE_STRIKE}, {FORM_CHANGE_TERMINATOR}, }; @@ -1864,6 +2012,7 @@ static const struct FormChange sUrshifuRapidStrikeFormChangeTable[] = #if P_GIGANTAMAX_FORMS {FORM_CHANGE_BATTLE_GIGANTAMAX, SPECIES_URSHIFU_RAPID_STRIKE_GMAX}, #endif + {FORM_CHANGE_FAINT, SPECIES_URSHIFU_RAPID_STRIKE}, {FORM_CHANGE_END_BATTLE, SPECIES_URSHIFU_RAPID_STRIKE}, {FORM_CHANGE_TERMINATOR}, }; @@ -1891,6 +2040,7 @@ static const struct FormChange sScovillainFormChangeTable[] = { #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_SCOVILLAIN_MEGA, ITEM_SCOVILLAINITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_SCOVILLAIN}, {FORM_CHANGE_END_BATTLE, SPECIES_SCOVILLAIN}, {FORM_CHANGE_TERMINATOR}, }; @@ -1899,8 +2049,8 @@ static const struct FormChange sScovillainFormChangeTable[] = { #if P_FAMILY_FINIZEN static const struct FormChange sPalafinZeroFormChangeTable[] = { - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_PALAFIN_HERO}, - {FORM_CHANGE_END_BATTLE, SPECIES_PALAFIN_ZERO}, + {FORM_CHANGE_BATTLE_SWITCH_OUT, SPECIES_PALAFIN_HERO}, + {FORM_CHANGE_END_BATTLE, SPECIES_PALAFIN_ZERO}, {FORM_CHANGE_TERMINATOR}, }; #endif //P_FAMILY_FINIZEN @@ -1910,6 +2060,7 @@ static const struct FormChange sGlimmoraFormChangeTable[] = { #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_GLIMMORA_MEGA, ITEM_GLIMMORANITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_GLIMMORA}, {FORM_CHANGE_END_BATTLE, SPECIES_GLIMMORA}, {FORM_CHANGE_TERMINATOR}, }; @@ -1920,6 +2071,7 @@ static const struct FormChange sTatsugiriCurlyFormChangeTable[] = { #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_TATSUGIRI_CURLY_MEGA, ITEM_TATSUGIRINITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_TATSUGIRI_CURLY}, {FORM_CHANGE_END_BATTLE, SPECIES_TATSUGIRI_CURLY}, {FORM_CHANGE_TERMINATOR}, }; @@ -1927,6 +2079,7 @@ static const struct FormChange sTatsugiriDroopyFormChangeTable[] = { #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_TATSUGIRI_DROOPY_MEGA, ITEM_TATSUGIRINITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_TATSUGIRI_DROOPY}, {FORM_CHANGE_END_BATTLE, SPECIES_TATSUGIRI_DROOPY}, {FORM_CHANGE_TERMINATOR}, }; @@ -1934,6 +2087,7 @@ static const struct FormChange sTatsugiriStretchyFormChangeTable[] = { #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_TATSUGIRI_STRETCHY_MEGA, ITEM_TATSUGIRINITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_TATSUGIRI_STRETCHY}, {FORM_CHANGE_END_BATTLE, SPECIES_TATSUGIRI_STRETCHY}, {FORM_CHANGE_TERMINATOR}, }; @@ -1944,25 +2098,62 @@ static const struct FormChange sBaxcaliburFormChangeTable[] = { #if P_GEN_9_MEGA_EVOLUTIONS {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_BAXCALIBUR_MEGA, ITEM_BAXCALIBRITE}, #endif + {FORM_CHANGE_FAINT, SPECIES_BAXCALIBUR}, {FORM_CHANGE_END_BATTLE, SPECIES_BAXCALIBUR}, {FORM_CHANGE_TERMINATOR}, }; #endif //P_FAMILY_FRIGIBAX #if P_FAMILY_OGERPON -static const struct FormChange sOgerponFormChangeTable[] = +static const struct FormChange sOgerponTealFormChangeTable[] = { - {FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_TEAL, ITEM_NONE}, {FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_WELLSPRING, ITEM_WELLSPRING_MASK}, {FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_HEARTHFLAME, ITEM_HEARTHFLAME_MASK}, {FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_CORNERSTONE, ITEM_CORNERSTONE_MASK}, #if P_TERA_FORMS {FORM_CHANGE_BATTLE_TERASTALLIZATION, SPECIES_OGERPON_TEAL_TERA, TYPE_GRASS}, +#endif + {FORM_CHANGE_FAINT, SPECIES_OGERPON_TEAL}, + {FORM_CHANGE_END_BATTLE, SPECIES_OGERPON_TEAL}, + {FORM_CHANGE_TERMINATOR}, +}; + +static const struct FormChange sOgerponWellspringFormChangeTable[] = +{ + {FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_TEAL, ITEM_NONE}, + {FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_HEARTHFLAME, ITEM_HEARTHFLAME_MASK}, + {FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_CORNERSTONE, ITEM_CORNERSTONE_MASK}, +#if P_TERA_FORMS {FORM_CHANGE_BATTLE_TERASTALLIZATION, SPECIES_OGERPON_WELLSPRING_TERA, TYPE_WATER}, +#endif + {FORM_CHANGE_FAINT, SPECIES_OGERPON_WELLSPRING}, + {FORM_CHANGE_END_BATTLE, SPECIES_OGERPON_WELLSPRING}, + {FORM_CHANGE_TERMINATOR}, +}; + +static const struct FormChange sOgerponHearthflameFormChangeTable[] = +{ + {FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_TEAL, ITEM_NONE}, + {FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_WELLSPRING, ITEM_WELLSPRING_MASK}, + {FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_CORNERSTONE, ITEM_CORNERSTONE_MASK}, +#if P_TERA_FORMS {FORM_CHANGE_BATTLE_TERASTALLIZATION, SPECIES_OGERPON_HEARTHFLAME_TERA, TYPE_FIRE}, +#endif + {FORM_CHANGE_FAINT, SPECIES_OGERPON_HEARTHFLAME}, + {FORM_CHANGE_END_BATTLE, SPECIES_OGERPON_HEARTHFLAME}, + {FORM_CHANGE_TERMINATOR}, +}; + +static const struct FormChange sOgerponCornerstoneFormChangeTable[] = +{ + {FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_TEAL, ITEM_NONE}, + {FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_WELLSPRING, ITEM_WELLSPRING_MASK}, + {FORM_CHANGE_ITEM_HOLD, SPECIES_OGERPON_HEARTHFLAME, ITEM_HEARTHFLAME_MASK}, +#if P_TERA_FORMS {FORM_CHANGE_BATTLE_TERASTALLIZATION, SPECIES_OGERPON_CORNERSTONE_TERA, TYPE_ROCK}, #endif - {FORM_CHANGE_END_BATTLE}, + {FORM_CHANGE_FAINT, SPECIES_OGERPON_CORNERSTONE}, + {FORM_CHANGE_END_BATTLE, SPECIES_OGERPON_CORNERSTONE}, {FORM_CHANGE_TERMINATOR}, }; #endif //P_FAMILY_OGERPON @@ -1970,10 +2161,11 @@ static const struct FormChange sOgerponFormChangeTable[] = #if P_FAMILY_TERAPAGOS static const struct FormChange sTerapagosFormChangeTable[] = { - {FORM_CHANGE_BATTLE_SWITCH, SPECIES_TERAPAGOS_TERASTAL, ABILITY_TERA_SHIFT}, + {FORM_CHANGE_BATTLE_SWITCH_IN, SPECIES_TERAPAGOS_TERASTAL, ABILITY_TERA_SHIFT}, #if P_TERA_FORMS {FORM_CHANGE_BATTLE_TERASTALLIZATION, SPECIES_TERAPAGOS_STELLAR, TYPE_STELLAR}, #endif + {FORM_CHANGE_FAINT, SPECIES_TERAPAGOS_NORMAL}, {FORM_CHANGE_END_BATTLE, SPECIES_TERAPAGOS_NORMAL}, {FORM_CHANGE_TERMINATOR}, }; diff --git a/src/data/pokemon/species_info/gen_9_families.h b/src/data/pokemon/species_info/gen_9_families.h index 1bbb6a525..afd67bd9d 100644 --- a/src/data/pokemon/species_info/gen_9_families.h +++ b/src/data/pokemon/species_info/gen_9_families.h @@ -8024,7 +8024,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .levelUpLearnset = sOgerponLevelUpLearnset, \ .teachableLearnset = sOgerponTeachableLearnset, \ .formSpeciesIdTable = sOgerponFormSpeciesIdTable, \ - .formChangeTable = sOgerponFormChangeTable, \ + .formChangeTable = sOgerpon##Form1##FormChangeTable, \ .isSubLegendary = TRUE, \ .isTeraForm = isTeraform, \ .perfectIVCount = LEGENDARY_PERFECT_IV_COUNT, \ diff --git a/src/evolution_scene.c b/src/evolution_scene.c index 06f91885a..b46d91d6c 100644 --- a/src/evolution_scene.c +++ b/src/evolution_scene.c @@ -984,9 +984,7 @@ static void Task_EvolutionScene(u8 taskId) if (!gPaletteFade.active) { FreeAllWindowBuffers(); - ShowSelectMovePokemonSummaryScreen(gPlayerParty, gTasks[taskId].tPartyId, - gPlayerPartyCount - 1, CB2_EvolutionSceneLoadGraphics, - gMoveToLearn); + ShowSelectMovePokemonSummaryScreen(gPlayerParty, gTasks[taskId].tPartyId, CB2_EvolutionSceneLoadGraphics, gMoveToLearn); gTasks[taskId].tLearnMoveState++; } break; @@ -1385,9 +1383,7 @@ static void Task_TradeEvolutionScene(u8 taskId) Free(GetBgTilemapBuffer(0)); FreeAllWindowBuffers(); - ShowSelectMovePokemonSummaryScreen(gPlayerParty, gTasks[taskId].tPartyId, - gPlayerPartyCount - 1, CB2_TradeEvolutionSceneLoadGraphics, - gMoveToLearn); + ShowSelectMovePokemonSummaryScreen(gPlayerParty, gTasks[taskId].tPartyId, CB2_TradeEvolutionSceneLoadGraphics, gMoveToLearn); gTasks[taskId].tLearnMoveState++; } break; diff --git a/src/field_message_box.c b/src/field_message_box.c index 520808574..2a6747ee4 100644 --- a/src/field_message_box.c +++ b/src/field_message_box.c @@ -9,6 +9,7 @@ static EWRAM_DATA u8 sMessageBoxType = 0; static void ExpandStringAndStartDrawFieldMessageBox(const u8 *str); +static void StartDrawFieldMessage(void); void InitFieldMessageBox(void) { @@ -79,6 +80,17 @@ bool8 ShowFieldAutoScrollMessage(const u8 *str) return TRUE; } +// Same as ShowFieldMessage, but instead of accepting a +// string arg it just prints whats already in gStringVar4 +bool8 ShowFieldMessageFromBuffer(void) +{ + if (sMessageBoxType != FIELD_MESSAGE_BOX_HIDDEN) + return FALSE; + sMessageBoxType = FIELD_MESSAGE_BOX_NORMAL; + StartDrawFieldMessage(); + return TRUE; +} + static void ExpandStringAndStartDrawFieldMessageBox(const u8 *str) { StringExpandPlaceholders(gStringVar4, str); @@ -86,6 +98,12 @@ static void ExpandStringAndStartDrawFieldMessageBox(const u8 *str) CreateTask_DrawFieldMessageBox(); } +static void StartDrawFieldMessage(void) +{ + AddTextPrinterForMessage(TRUE); + CreateTask_DrawFieldMessageBox(); +} + void HideFieldMessageBox(void) { DestroyTask_DrawFieldMessageBox(); diff --git a/src/field_poison.c b/src/field_poison.c index 7eb21d956..6a917e1ba 100644 --- a/src/field_poison.c +++ b/src/field_poison.c @@ -1,15 +1,19 @@ #include "global.h" -#include "gflib.h" -#include "strings.h" -#include "task.h" -#include "field_message_box.h" -#include "script.h" +#include "battle_pike.h" +#include "battle_pyramid.h" #include "event_data.h" +#include "event_object_movement.h" +#include "field_message_box.h" +#include "field_poison.h" #include "fldeff.h" #include "party_menu.h" #include "pokemon.h" -#include "field_poison.h" +#include "script.h" +#include "string_util.h" +#include "strings.h" +#include "task.h" #include "constants/battle.h" +#include "constants/field_poison.h" #include "constants/form_change_types.h" static bool32 IsMonValidSpecies(struct Pokemon *pokemon) @@ -77,9 +81,17 @@ static void Task_TryFieldPoisonWhiteOut(u8 taskId) break; case 2: if (AllMonsFainted()) - gSpecialVar_Result = TRUE; + { + if (CurrentBattlePyramidLocation() || InBattlePike()) + gSpecialVar_Result = FLDPSN_FRONTIER_WHITEOUT; + else + gSpecialVar_Result = FLDPSN_WHITEOUT; + } else - gSpecialVar_Result = FALSE; + { + gSpecialVar_Result = FLDPSN_NO_WHITEOUT; + UpdateFollowingPokemon(); + } ScriptContext_Enable(); DestroyTask(taskId); break; @@ -96,7 +108,7 @@ s32 DoPoisonFieldEffect(void) { int i; u32 hp; - + struct Pokemon *pokemon = gPlayerParty; u32 numPoisoned = 0; u32 numFainted = 0; @@ -108,7 +120,7 @@ s32 DoPoisonFieldEffect(void) hp = GetMonData(pokemon, MON_DATA_HP); if (OW_POISON_DAMAGE < GEN_4 && (hp == 0 || --hp == 0)) { - TryFormChange(i, B_SIDE_PLAYER, FORM_CHANGE_FAINT); + TryFormChange(&gPlayerParty[i], FORM_CHANGE_FAINT); numFainted++; } else if (OW_POISON_DAMAGE >= GEN_4 && (hp == 1 || --hp == 1)) diff --git a/src/field_specials.c b/src/field_specials.c index 527b42169..2f7b579a2 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -3815,6 +3815,15 @@ void CloseBattleFrontierTutorWindow(void) RemoveWindow(sTutorMoveAndElevatorWindowId); } +bool8 InMultiPartnerRoom(void) +{ + if (gSaveBlock1Ptr->location.mapGroup == MAP_GROUP(MAP_BATTLE_FRONTIER_BATTLE_TOWER_MULTI_PARTNER_ROOM) + && gSaveBlock1Ptr->location.mapNum == MAP_NUM(MAP_BATTLE_FRONTIER_BATTLE_TOWER_MULTI_PARTNER_ROOM) && + VarGet(VAR_FRONTIER_BATTLE_MODE) == FRONTIER_MODE_MULTIS) + return TRUE; + return FALSE; +} + void OffsetCameraForBattle(void) { SetCameraPanningCallback(NULL); diff --git a/src/item_menu.c b/src/item_menu.c index 6b08c6747..93bffbada 100644 --- a/src/item_menu.c +++ b/src/item_menu.c @@ -3,6 +3,9 @@ #include "apprentice.h" #include "battle.h" #include "battle_controllers.h" +#include "battle_pike.h" +#include "battle_pyramid_bag.h" +#include "battle_pyramid.h" #include "battle_main.h" #include "berry_pouch.h" #include "decompress.h" @@ -10,6 +13,7 @@ #include "event_scripts.h" #include "event_object_movement.h" #include "field_player_avatar.h" +#include "field_specials.h" #include "graphics.h" #include "help_system.h" #include "international_string_util.h" @@ -648,7 +652,10 @@ void CB2_BagMenuFromStartMenu(void) void CB2_BagMenuFromBattle(void) { - GoToBagMenu(ITEMMENULOCATION_BATTLE, POCKETS_COUNT_NO_CASES, CB2_SetUpReshowBattleScreenAfterMenu2); + if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE) + GoToBagMenu(ITEMMENULOCATION_BATTLE, POCKETS_COUNT_NO_CASES, CB2_SetUpReshowBattleScreenAfterMenu2); + else + GoToBattlePyramidBagMenu(PYRAMIDBAG_LOC_BATTLE, CB2_SetUpReshowBattleScreenAfterMenu2); } static void ReturnToBagMenuFromSubmenu_PCBox(void) @@ -1845,7 +1852,10 @@ static void ConfirmToss(u8 taskId) ConvertIntToDecimalStringN(gStringVar2, tItemCount, STR_CONV_MODE_LEFT_ALIGN, MAX_ITEM_DIGITS); StringExpandPlaceholders(gStringVar4, gText_ThrewAwayVar2Var1s); BagMenu_Print(BagMenu_AddWindow(ITEMWIN_TOSSED), FONT_NORMAL, gStringVar4, 0, 2, 1, 0, 0, COLORID_BLACK_CURSOR); - gTasks[taskId].func = Task_TossItemFromBag; + if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE || FlagGet(FLAG_STORING_ITEMS_IN_PYRAMID_BAG) == TRUE) + gTasks[taskId].func = Task_RemoveItemFromBag; + else + gTasks[taskId].func = Task_TossItemFromBag; } static void Task_RemoveItemFromBag(u8 taskId) @@ -2033,7 +2043,7 @@ static void Task_ItemContext_GiveToPC(u8 taskId) bool8 UseRegisteredKeyItemOnField(void) { u8 taskId; - if (InUnionRoom() == TRUE) + if (InUnionRoom() == TRUE || CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE || InBattlePike() || InMultiPartnerRoom() == TRUE) return FALSE; HideMapNamePopUpWindow(); ChangeBgY(0, 0, 0); diff --git a/src/item_use.c b/src/item_use.c index e6cd0f931..17dfc6716 100644 --- a/src/item_use.c +++ b/src/item_use.c @@ -2,6 +2,8 @@ #include "gflib.h" #include "battle.h" #include "battle_anim.h" +#include "battle_pyramid_bag.h" +#include "battle_pyramid.h" #include "berry.h" #include "berry_pouch.h" #include "berry_powder.h" @@ -65,6 +67,7 @@ static void Task_InitBerryPouchFromField(u8 taskId); static void InitBerryPouchFromBattle(void); static void InitTeachyTvFromBag(void); static void Task_InitTeachyTvFromField(u8 taskId); +static void Task_StartUseRepel(u8 taskId); static void Task_UseRepel(u8 taskId); static void Task_StartUseLure(u8 taskId); static void Task_UseLure(u8 taskId); @@ -99,7 +102,13 @@ static void SetUpItemUseCallback(u8 taskId) itemType = gTasks[taskId].tEnigmaBerryType - 1; else itemType = GetItemType(gSpecialVar_ItemId) - 1; - if (GetPocketByItemId(gSpecialVar_ItemId) == POCKET_BERRIES) + + if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) + { + gPyramidBagMenu->newScreenCallback = sExitCallbackByItemType[itemType]; + CloseBattlePyramidBag(taskId); + } + else if (GetPocketByItemId(gSpecialVar_ItemId) == POCKET_BERRIES) { BerryPouch_SetExitCallback(sExitCallbackByItemType[itemType]); BerryPouch_StartFadeToExitCallback(taskId); @@ -138,9 +147,16 @@ static void DisplayItemMessageInCurrentContext(u8 taskId, bool8 inField, u8 font { StringExpandPlaceholders(gStringVar4, str); if (inField == FALSE) - DisplayItemMessage(taskId, fontId, gStringVar4, CloseItemMessage); + { + if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE) + DisplayItemMessage(taskId, fontId, gStringVar4, CloseItemMessage); + else + DisplayItemMessageInBattlePyramid(taskId, gText_OakForbidsUseOfItemHere, Task_CloseBattlePyramidBagMessage); + } else + { DisplayItemMessageOnField(taskId, fontId, gStringVar4, Task_ItemUse_CloseMessageBoxAndReturnToField); + } } static void PrintNotTheTimeToUseThat(u8 taskId, bool8 inField) @@ -719,13 +735,23 @@ static void Task_InitTeachyTvFromField(u8 taskId) void ItemUseOutOfBattle_Repel(u8 taskId) { if (REPEL_STEP_COUNT == 0) + gTasks[taskId].func = Task_StartUseRepel; + else if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE) + DisplayItemMessage(taskId, FONT_NORMAL, gText_RepelEffectsLingered, CloseItemMessage); + else + DisplayItemMessageInBattlePyramid(taskId, gText_RepelEffectsLingered, Task_CloseBattlePyramidBagMessage); +} + +static void Task_StartUseRepel(u8 taskId) +{ + s16 *data = gTasks[taskId].data; + + if (++data[8] > 7) { + data[8] = 0; PlaySE(SE_REPEL); gTasks[taskId].func = Task_UseRepel; } - else - // An earlier repel is still in effect - DisplayItemMessage(taskId, FONT_NORMAL, gText_RepelEffectsLingered, CloseItemMessage); } static void Task_UseRepel(u8 taskId) @@ -738,7 +764,10 @@ static void Task_UseRepel(u8 taskId) VarSet(VAR_LAST_REPEL_LURE_USED, gSpecialVar_ItemId); #endif RemoveUsedItem(); - DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, CloseItemMessage); + if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE) + DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, CloseItemMessage); + else + DisplayItemMessageInBattlePyramid(taskId, gStringVar4, Task_CloseBattlePyramidBagMessage); } } @@ -746,8 +775,10 @@ void ItemUseOutOfBattle_Lure(u8 taskId) { if (LURE_STEP_COUNT == 0) gTasks[taskId].func = Task_StartUseLure; - else + else if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE) DisplayItemMessage(taskId, FONT_NORMAL, gText_LureEffectsLingered, CloseItemMessage); + else + DisplayItemMessageInBattlePyramid(taskId, gText_LureEffectsLingered, Task_CloseBattlePyramidBagMessage); } static void Task_StartUseLure(u8 taskId) @@ -771,7 +802,10 @@ static void Task_UseLure(u8 taskId) VarSet(VAR_LAST_REPEL_LURE_USED, gSpecialVar_ItemId); #endif RemoveUsedItem(); - DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, CloseItemMessage); + if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE) + DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, CloseItemMessage); + else + DisplayItemMessageInBattlePyramid(taskId, gStringVar4, Task_CloseBattlePyramidBagMessage); } } @@ -791,10 +825,19 @@ static void RemoveUsedItem(void) { u8 pocketId = GetItemPocket(gSpecialVar_ItemId); RemoveBagItem(gSpecialVar_ItemId, 1); - UpdatePocketItemList(pocketId); - UpdatePocketListPosition(pocketId); CopyItemName(gSpecialVar_ItemId, gStringVar2); StringExpandPlaceholders(gStringVar4, gText_PlayerUsedVar2); + + if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE) + { + UpdatePocketItemList(pocketId); + UpdatePocketListPosition(pocketId); + } + else + { + UpdatePyramidBagList(); + UpdatePyramidBagCursorPos(); + } } void ItemUseOutOfBattle_BlackWhiteFlute(u8 taskId) @@ -825,7 +868,10 @@ static void Task_UsedBlackWhiteFlute(u8 taskId) if (++gTasks[taskId].data[8] > 7) { PlaySE(SE_GLASS_FLUTE); - DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, CloseItemMessage); + if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE) + DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, CloseItemMessage); + else + DisplayItemMessageInBattlePyramid(taskId, gStringVar4, Task_CloseBattlePyramidBagMessage); } } @@ -928,16 +974,21 @@ void Task_ItemUse_CloseMessageBoxAndReturnToField_VsSeeker(u8 taskId) Task_ItemUse_CloseMessageBoxAndReturnToField(taskId); } -static void ItemUse_SwitchToPartyMenuInBattle(u8 taskId) +static void ItemUseInBattle_ShowPartyMenu(u8 taskId) { + if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) + { + gPyramidBagMenu->newScreenCallback = ChooseMonForInBattleItem; + CloseBattlePyramidBag(taskId); + } if (GetPocketByItemId(gSpecialVar_ItemId) == POCKET_BERRIES) { - BerryPouch_SetExitCallback(EnterPartyFromItemMenuInBattle); + BerryPouch_SetExitCallback(ChooseMonForInBattleItem); BerryPouch_StartFadeToExitCallback(taskId); } else { - ItemMenu_SetExitCallback(EnterPartyFromItemMenuInBattle); + ItemMenu_SetExitCallback(ChooseMonForInBattleItem); Task_FadeAndCloseBagMenu(taskId); } } @@ -950,30 +1001,35 @@ void ItemUseInBattle_BagMenu(u8 taskId) } else if (CannotUseItemsInBattle(gSpecialVar_ItemId, NULL)) { - DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, CloseItemMessage); + if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE) + DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, CloseItemMessage); + else + DisplayItemMessageInBattlePyramid(taskId, gStringVar4, Task_CloseBattlePyramidBagMessage); } else { PlaySE(SE_SELECT); if (!(B_TRY_CATCH_TRAINER_BALL >= GEN_4 && (GetItemBattleUsage(gSpecialVar_ItemId) == EFFECT_ITEM_THROW_BALL) && (gBattleTypeFlags & BATTLE_TYPE_TRAINER))) - { - RemoveBagItem(gSpecialVar_ItemId, 1); - } + RemoveUsedItem(); + ScheduleBgCopyTilemapToVram(2); - gTasks[taskId].func = Task_FadeAndCloseBagMenu; + if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE) + gTasks[taskId].func = Task_FadeAndCloseBagMenu; + else + gTasks[taskId].func = CloseBattlePyramidBag; } } void ItemUseInBattle_PartyMenu(u8 taskId) { gItemUseCB = ItemUseCB_BattleScript; - ItemUse_SwitchToPartyMenuInBattle(taskId); + ItemUseInBattle_ShowPartyMenu(taskId); } void ItemUseInBattle_PartyMenuChooseMove(u8 taskId) { gItemUseCB = ItemUseCB_BattleChooseMove; - ItemUse_SwitchToPartyMenuInBattle(taskId); + ItemUseInBattle_ShowPartyMenu(taskId); } static u32 GetBallThrowableState(void) diff --git a/src/learn_move.c b/src/learn_move.c index 2754356e6..db8cd1c5b 100644 --- a/src/learn_move.c +++ b/src/learn_move.c @@ -592,7 +592,7 @@ static void MoveRelearnerStateMachine(void) { ListMenuGetScrollAndRow(sMoveRelearner->listMenuTaskId, &sMoveRelearner->listMenuScrollPos, &sMoveRelearner->listMenuScrollRow); FreeAllWindowBuffers(); - ShowSelectMovePokemonSummaryScreen(gPlayerParty, sMoveRelearner->selectedPartyMember, gPlayerPartyCount - 1, CB2_MoveRelearner_Resume, sMoveRelearner->learnableMoves[sMoveRelearner->selectedIndex]); + ShowSelectMovePokemonSummaryScreen(gPlayerParty, sMoveRelearner->selectedPartyMember, CB2_MoveRelearner_Resume, sMoveRelearner->learnableMoves[sMoveRelearner->selectedIndex]); sMoveRelearner->state = 28; } break; diff --git a/src/party_menu.c b/src/party_menu.c index 54127da1f..74d89a17e 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -5,6 +5,8 @@ #include "battle_controllers.h" #include "battle_gfx_sfx_util.h" #include "battle_interface.h" +#include "battle_pyramid_bag.h" +#include "battle_pyramid.h" #include "berry_pouch.h" #include "data.h" #include "decompress.h" @@ -224,7 +226,7 @@ static void DisplayPartyPokemonGender(u8 gender, u16 species, u8 *nickname, stru static void DisplayPartyPokemonHP(u16 hp, struct PartyMenuBox *menuBox); static void DisplayPartyPokemonMaxHP(u16 maxhp, struct PartyMenuBox *menuBox); static void DisplayPartyPokemonHPBar(u16 hp, u16 maxhp, struct PartyMenuBox *menuBox); -static void CreatePartyMonIconSpriteParameterized(u16 species, u32 pid, struct PartyMenuBox *menuBox, u8 priority); +static void CreatePartyMonIconSpriteParameterized(u16 species, u32 pid, bool32 isEgg, struct PartyMenuBox *menuBox, u8 priority); static void CreatePartyMonHeldItemSpriteParameterized(u16 species, u16 item, struct PartyMenuBox *menuBox); static void CreatePartyMonPokeballSpriteParameterized(u16 species, struct PartyMenuBox *menuBox); static void CreatePartyMonStatusSpriteParameterized(u16 species, u8 status, struct PartyMenuBox *menuBox); @@ -237,6 +239,7 @@ static void DrawCancelConfirmButtons(void); static u8 CreatePokeballButtonSprite(u8 x, u8 y); static u8 CreateSmallPokeballButtonSprite(u8 x, u8 y); static u8 GetPartyBoxPaletteFlags(u8 slot, u8 animNum); +static bool8 PartyBoxPal_ParnterOrDisqualifiedInArena(u8); static void AnimateSelectedPartyIcon(u8 spriteId, u8 animNum); static void PartyMenuStartSpriteAnim(u8 spriteId, u8 animNum); static void Task_ClosePartyMenuAndSetCB2(u8 taskId); @@ -411,7 +414,7 @@ static void Task_ReplaceMoveWithTMHM(u8 taskId); static void CB2_UseEvolutionStone(void); static bool8 MonCanEvolve(void); static u8 CanTeachMove(struct Pokemon *mon, u16 move); -static void TryItemHoldFormChange(struct Pokemon *mon); +static void TryItemHoldFormChange(struct Pokemon *mon, s8 slotId); static void Task_TossHeldItemYesNo(u8 taskId); static void Task_HandleTossHeldItemYesNoInput(u8); static void Task_TossHeldItem(u8); @@ -1113,7 +1116,7 @@ static void CreatePartyMonSprites(u8 slot) actualSlot = slot - MULTI_PARTY_SIZE; if (gMultiPartnerParty[actualSlot].species != SPECIES_NONE) { - CreatePartyMonIconSpriteParameterized(gMultiPartnerParty[actualSlot].species, gMultiPartnerParty[actualSlot].personality, &sPartyMenuBoxes[slot], 0); + CreatePartyMonIconSpriteParameterized(gMultiPartnerParty[actualSlot].species, gMultiPartnerParty[actualSlot].personality, FALSE, &sPartyMenuBoxes[slot], 0); CreatePartyMonHeldItemSpriteParameterized(gMultiPartnerParty[actualSlot].species, gMultiPartnerParty[actualSlot].heldItem, &sPartyMenuBoxes[slot]); CreatePartyMonPokeballSpriteParameterized(gMultiPartnerParty[actualSlot].species, &sPartyMenuBoxes[slot]); if (gMultiPartnerParty[actualSlot].hp == 0) @@ -1215,8 +1218,7 @@ static u8 GetPartyBoxPaletteFlags(u8 slot, u8 animNum) palFlags |= PARTY_PAL_SELECTED; if (GetMonData(&gPlayerParty[slot], MON_DATA_HP) == 0) palFlags |= PARTY_PAL_FAINTED; - if (gPartyMenu.layout == PARTY_LAYOUT_MULTI - && (slot == 1 || slot == 4 || slot == 5)) + if (PartyBoxPal_ParnterOrDisqualifiedInArena(slot) == TRUE) palFlags |= PARTY_PAL_MULTI_ALT; if (gPartyMenu.action == PARTY_ACTION_SWITCHING) palFlags |= PARTY_PAL_SWITCHING; @@ -1230,6 +1232,17 @@ static u8 GetPartyBoxPaletteFlags(u8 slot, u8 animNum) return palFlags; } +static bool8 PartyBoxPal_ParnterOrDisqualifiedInArena(u8 slot) +{ + if (gPartyMenu.layout == PARTY_LAYOUT_MULTI && (slot == 1 || slot == 4 || slot == 5)) + return TRUE; + + if (slot < MULTI_PARTY_SIZE && (gBattleTypeFlags & BATTLE_TYPE_ARENA) && gMain.inBattle && (gBattleStruct->arenaLostPlayerMons >> GetPartyIdFromBattleSlot(slot) & 1)) + return TRUE; + + return FALSE; +} + static void DrawCancelConfirmButtons(void) { CopyToBgTilemapBufferRect_ChangePalette(1, sConfirmButton_Tilemap, 23, 16, 7, 2, 17); @@ -1860,7 +1873,7 @@ static void DisplaySwitchedHeldItemMessage(u16 item, u16 item2, bool8 keepOpen) ScheduleBgCopyTilemapToVram(2); } -static void GiveItemToMon(struct Pokemon *mon, u16 item) +static void GiveItemToMon(struct Pokemon *mon, enum Item item) { u8 itemBytes[2]; @@ -1872,20 +1885,21 @@ static void GiveItemToMon(struct Pokemon *mon, u16 item) itemBytes[0] = item; itemBytes[1] = item >> 8; SetMonData(mon, MON_DATA_HELD_ITEM, itemBytes); - TryItemHoldFormChange(&gPlayerParty[gPartyMenu.slotId]); + TryItemHoldFormChange(&gPlayerParty[gPartyMenu.slotId], gPartyMenu.slotId); } static u8 TryTakeMonItem(struct Pokemon *mon) { - u16 item = GetMonData(mon, MON_DATA_HELD_ITEM); + enum Item item = GetMonData(mon, MON_DATA_HELD_ITEM); if (item == ITEM_NONE) return 0; if (AddBagItem(item, 1) == FALSE) return 1; + item = ITEM_NONE; SetMonData(mon, MON_DATA_HELD_ITEM, &item); - TryItemHoldFormChange(&gPlayerParty[gPartyMenu.slotId]); + TryItemHoldFormChange(&gPlayerParty[gPartyMenu.slotId], gPartyMenu.slotId); return 2; } @@ -2863,16 +2877,17 @@ static void ToggleFieldMoveDescriptionWindow(u8 action) static void CreatePartyMonIconSprite(struct Pokemon *mon, struct PartyMenuBox *menuBox, u32 slot) { - u16 species = GetMonData(mon, MON_DATA_SPECIES_OR_EGG); - CreatePartyMonIconSpriteParameterized(species, GetMonData(mon, MON_DATA_PERSONALITY), menuBox, 1); + u32 species = GetMonData(mon, MON_DATA_SPECIES); + bool32 isEgg = GetMonData(mon, MON_DATA_IS_EGG); + CreatePartyMonIconSpriteParameterized(species, GetMonData(mon, MON_DATA_PERSONALITY), isEgg, menuBox, 1); UpdatePartyMonHPBar(menuBox->monSpriteId, mon); } -static void CreatePartyMonIconSpriteParameterized(u16 species, u32 pid, struct PartyMenuBox *menuBox, u8 priority) +static void CreatePartyMonIconSpriteParameterized(u16 species, u32 pid, bool32 isEgg, struct PartyMenuBox *menuBox, u8 priority) { if (species != SPECIES_NONE) { - menuBox->monSpriteId = CreateMonIcon(species, SpriteCB_MonIcon, menuBox->spriteCoords[0], menuBox->spriteCoords[1], 4, pid); + menuBox->monSpriteId = CreateMonIconIsEgg(species, SpriteCB_MonIcon, menuBox->spriteCoords[0], menuBox->spriteCoords[1], 4, pid, isEgg); gSprites[menuBox->monSpriteId].oam.priority = priority; } } @@ -3673,7 +3688,10 @@ static void CursorCB_Give(u8 taskId) void CB2_SelectBagItemToGive(void) { - GoToBagMenu(ITEMMENULOCATION_PARTY, POCKETS_COUNT_NO_CASES, CB2_GiveHoldItem); + if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE) + GoToBagMenu(ITEMMENULOCATION_PARTY, POCKETS_COUNT_NO_CASES, CB2_GiveHoldItem); + else + GoToBattlePyramidBagMenu(PYRAMIDBAG_LOC_PARTY, CB2_GiveHoldItem); } void CB2_GiveHoldItem(void) @@ -4602,19 +4620,26 @@ void CB2_ShowPartyMenuForItemUse(void) } else { - switch (GetPocketByItemId(gSpecialVar_ItemId)) + if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE) + { + switch (GetPocketByItemId(gSpecialVar_ItemId)) + { + default: + msgId = PARTY_MSG_USE_ON_WHICH_MON; + break; + case POCKET_TM_HM: + msgId = PARTY_MSG_TEACH_WHICH_MON; + callback = CB2_ReturnToTMCaseMenu; + break; + case POCKET_BERRIES: + msgId = PARTY_MSG_USE_ON_WHICH_MON; + callback = CB2_ReturnToBerryPouchMenu; + break; + } + } + else { - default: msgId = PARTY_MSG_USE_ON_WHICH_MON; - break; - case POCKET_TM_HM: - msgId = PARTY_MSG_TEACH_WHICH_MON; - callback = CB2_ReturnToTMCaseMenu; - break; - case POCKET_BERRIES: - msgId = PARTY_MSG_USE_ON_WHICH_MON; - callback = CB2_ReturnToBerryPouchMenu; - break; } task = Task_HandleChooseMonInput; } @@ -4623,7 +4648,10 @@ void CB2_ShowPartyMenuForItemUse(void) static void CB2_ReturnToBagMenu(void) { - GoToBagMenu(ITEMMENULOCATION_LAST, POCKETS_COUNT_NO_CASES, NULL); + if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE) + GoToBagMenu(ITEMMENULOCATION_LAST, POCKETS_COUNT_NO_CASES, NULL); + else + GoToBattlePyramidBagMenu(PYRAMIDBAG_LOC_PREV, gPyramidBagMenuState.exitCallback); } static void CB2_ReturnToTMCaseMenu(void) @@ -5707,7 +5735,7 @@ static void Task_ShowSummaryScreenToForgetMove(u8 taskId) static void CB2_ShowSummaryScreenToForgetMove(void) { - ShowSelectMovePokemonSummaryScreen(gPlayerParty, gPartyMenu.slotId, gPlayerPartyCount - 1, CB2_ReturnToPartyMenuWhileLearningMove, gPartyMenu.learnMoveId); + ShowSelectMovePokemonSummaryScreen(gPlayerParty, gPartyMenu.slotId, CB2_ReturnToPartyMenuWhileLearningMove, gPartyMenu.learnMoveId); } static void CB2_ReturnToPartyMenuWhileLearningMove(void) @@ -6403,17 +6431,24 @@ void CB2_ChooseMonToGiveItem(void) { MainCallback callback; - switch (GetPocketByItemId(gSpecialVar_ItemId)) + if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE) { - default: - callback = CB2_ReturnToBagMenu; - break; - case POCKET_TM_HM: - callback = CB2_ReturnToTMCaseMenu; - break; - case POCKET_BERRIES: - callback = CB2_ReturnToBerryPouchMenu; - break; + switch (GetPocketByItemId(gSpecialVar_ItemId)) + { + default: + callback = CB2_ReturnToBagMenu; + break; + case POCKET_TM_HM: + callback = CB2_ReturnToTMCaseMenu; + break; + case POCKET_BERRIES: + callback = CB2_ReturnToBerryPouchMenu; + break; + } + } + else + { + callback = CB2_ReturnToPyramidBagMenu; } InitPartyMenu(PARTY_MENU_TYPE_FIELD, PARTY_LAYOUT_SINGLE, PARTY_ACTION_GIVE_ITEM, FALSE, PARTY_MSG_GIVE_TO_WHICH_MON, Task_HandleChooseMonInput, callback); gPartyMenu.bagItem = gSpecialVar_ItemId; @@ -6867,7 +6902,7 @@ void Pokedude_ChooseMonForInBattleItem(void) UpdatePartyToBattleOrder(); } -void EnterPartyFromItemMenuInBattle(void) +void ChooseMonForInBattleItem(void) { if (!BtlCtrl_OakOldMan_TestState2Flag(FIRST_BATTLE_MSG_FLAG_PARTY_MENU) && (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE)) { @@ -6884,7 +6919,9 @@ void EnterPartyFromItemMenuInBattle(void) { MainCallback callback; - if (GetPocketByItemId(gSpecialVar_ItemId) == POCKET_BERRIES) + if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) + callback = CB2_BagMenuFromBattle; + else if (GetPocketByItemId(gSpecialVar_ItemId) == POCKET_BERRIES) callback = CB2_ReturnToBerryPouchMenu; else callback = CB2_BagMenuFromBattle; @@ -6902,12 +6939,14 @@ void EnterPartyFromItemMenuInBattle(void) static u8 GetPartyMenuActionsTypeInBattle(struct Pokemon *mon) { - if (GetMonData(&gPlayerParty[1], MON_DATA_SPECIES) == SPECIES_NONE || GetMonData(mon, MON_DATA_IS_EGG)) - return ACTIONS_SUMMARY_ONLY; - else if (gPartyMenu.action == PARTY_ACTION_SEND_OUT) - return ACTIONS_SEND_OUT; - else - return ACTIONS_SHIFT; + if (GetMonData(&gPlayerParty[1], MON_DATA_SPECIES) != SPECIES_NONE && GetMonData(mon, MON_DATA_IS_EGG) == FALSE) + { + if (gPartyMenu.action == PARTY_ACTION_SEND_OUT) + return ACTIONS_SEND_OUT; + if (!(gBattleTypeFlags & BATTLE_TYPE_ARENA)) + return ACTIONS_SHIFT; + } + return ACTIONS_SUMMARY_ONLY; } static bool8 TrySwitchInPokemon(void) @@ -7821,32 +7860,19 @@ static void SpriteCB_FormChangeIconMosaic(struct Sprite *sprite) static void Task_TryItemUseFormChange(u8 taskId) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; - u16 targetSpecies; struct Sprite *icon = &gSprites[sPartyMenuBoxes[gPartyMenu.slotId].monSpriteId]; switch (gTasks[taskId].tState) { case 0: - targetSpecies = gTasks[taskId].tTargetSpecies; - SetMonData(mon, MON_DATA_SPECIES, &targetSpecies); - TrySetDayLimitToFormChange(mon); - CalculateMonStats(mon); - gTasks[taskId].tState++; - break; - case 1: - gTasks[taskId].tState++; - break; - case 2: PlaySE(SE_M_TELEPORT); gTasks[taskId].tState++; break; - case 3: - targetSpecies = gTasks[taskId].tTargetSpecies; - + case 1: if (gTasks[taskId].tAnimWait == 0) { FreeAndDestroyMonIconSprite(icon); - CreatePartyMonIconSpriteParameterized(targetSpecies, GetMonData(mon, MON_DATA_PERSONALITY, NULL), &sPartyMenuBoxes[gPartyMenu.slotId], 1); + CreatePartyMonIconSpriteParameterized(gTasks[taskId].tTargetSpecies, GetMonData(mon, MON_DATA_PERSONALITY), FALSE, &sPartyMenuBoxes[gPartyMenu.slotId], 1); icon->oam.mosaic = TRUE; icon->data[0] = 10; icon->data[1] = 1; @@ -7854,17 +7880,14 @@ static void Task_TryItemUseFormChange(u8 taskId) icon->callback = SpriteCB_FormChangeIconMosaic; SetGpuReg(REG_OFFSET_MOSAIC, (icon->data[0] << 12) | (icon->data[1] << 8)); } - if (++gTasks[taskId].tAnimWait == 60) gTasks[taskId].tState++; - break; - case 4: - targetSpecies = gTasks[taskId].tTargetSpecies; - PlayCry_Normal(targetSpecies, 0); + case 2: + PlayCry_Normal(gTasks[taskId].tTargetSpecies, 0); gTasks[taskId].tState++; break; - case 5: + case 3: if (IsCryFinished()) { GetMonNickname(mon, gStringVar1); @@ -7875,16 +7898,16 @@ static void Task_TryItemUseFormChange(u8 taskId) } break; - case 6: + case 4: if (!IsPartyMenuTextPrinterActive()) { - if (gSpecialVar_ItemId == ITEM_ROTOM_CATALOG) //only for rotom currently + if (gSpecialVar_ItemId == ITEM_ROTOM_CATALOG) //only for Rotom currently { u32 i; for (i = 0; i < ARRAY_COUNT(sRotomFormChangeMoves); i++) DeleteMove(mon, sRotomFormChangeMoves[i]); - if (gSpecialVar_0x8000 == MOVE_THUNDER_SHOCK) + if (I_ROTOM_CATALOG_THUNDER_SHOCK < GEN_9 && gSpecialVar_0x8000 == ROTOM_BASE_MOVE) { if (!DoesMonHaveAnyMoves(mon)) FormChangeTeachMove(taskId, gSpecialVar_0x8000, gPartyMenu.slotId); @@ -7896,7 +7919,7 @@ static void Task_TryItemUseFormChange(u8 taskId) gTasks[taskId].tState++; } break; - case 7: + case 5: gTasks[taskId].func = (void *)GetWordTaskArg(taskId, tNextFunc); break; } @@ -7905,15 +7928,14 @@ static void Task_TryItemUseFormChange(u8 taskId) bool32 TryItemUseFormChange(u8 taskId, TaskFunc task) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; - u16 targetSpecies = GetFormChangeTargetSpecies(mon, FORM_CHANGE_ITEM_USE, gSpecialVar_ItemId); - if (targetSpecies != SPECIES_NONE) + if (TryFormChange(mon, FORM_CHANGE_ITEM_USE)) { gPartyMenuUseExitCallback = TRUE; SetWordTaskArg(taskId, tNextFunc, (u32)task); gTasks[taskId].func = Task_TryItemUseFormChange; gTasks[taskId].tState = 0; - gTasks[taskId].tTargetSpecies = targetSpecies; + gTasks[taskId].tTargetSpecies = GetMonData(mon, MON_DATA_SPECIES); gTasks[taskId].tAnimWait = 0; return TRUE; } @@ -7931,18 +7953,17 @@ bool32 TryItemUseFormChange(u8 taskId, TaskFunc task) bool32 TryMultichoiceFormChange(u8 taskId) { struct Pokemon *mon = &gPlayerParty[gPartyMenu.slotId]; - u32 targetSpecies = GetFormChangeTargetSpecies(mon, FORM_CHANGE_ITEM_USE_MULTICHOICE, gSpecialVar_ItemId); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[1]); - if (targetSpecies != SPECIES_NONE) + if (TryFormChange(mon, FORM_CHANGE_ITEM_USE_MULTICHOICE)) { gPartyMenuUseExitCallback = TRUE; SetWordTaskArg(taskId, tNextFunc, (u32)Task_ClosePartyMenuAfterText); gTasks[taskId].func = Task_TryItemUseFormChange; gTasks[taskId].tState = 0; - gTasks[taskId].tTargetSpecies = targetSpecies; + gTasks[taskId].tTargetSpecies = GetMonData(mon, MON_DATA_SPECIES); gTasks[taskId].tAnimWait = 0; return TRUE; } @@ -8043,18 +8064,15 @@ static void CursorCB_ChangeAbility(u8 taskId) TryMultichoiceFormChange(taskId); } -static void TryItemHoldFormChange(struct Pokemon *mon) +static void TryItemHoldFormChange(struct Pokemon *mon, s8 slotId) { - u32 currentSpecies = GetMonData(mon, MON_DATA_SPECIES); - u32 targetSpecies = GetFormChangeTargetSpecies(mon, FORM_CHANGE_ITEM_HOLD, 0); - if (targetSpecies != currentSpecies) + if (TryFormChange(mon, FORM_CHANGE_ITEM_HOLD)) { - PlayCry_NormalNoDucking(targetSpecies, 0, CRY_VOLUME_RS, CRY_VOLUME_RS); - SetMonData(mon, MON_DATA_SPECIES, &targetSpecies); - FreeAndDestroyMonIconSprite(&gSprites[sPartyMenuBoxes[gPartyMenu.slotId].monSpriteId]); - CreatePartyMonIconSpriteParameterized(targetSpecies, GetMonData(mon, MON_DATA_PERSONALITY, NULL), &sPartyMenuBoxes[gPartyMenu.slotId], 1); - CalculateMonStats(mon); - UpdatePartyMonHeldItemSprite(mon, &sPartyMenuBoxes[gPartyMenu.slotId]); + u32 species = GetMonData(mon, MON_DATA_SPECIES); + PlayCry_NormalNoDucking(species, 0, CRY_VOLUME_RS, CRY_VOLUME_RS); + FreeAndDestroyMonIconSprite(&gSprites[sPartyMenuBoxes[slotId].monSpriteId]); + CreatePartyMonIconSpriteParameterized(species, GetMonData(mon, MON_DATA_PERSONALITY), FALSE, &sPartyMenuBoxes[slotId], 1); + UpdatePartyMonHeldItemSprite(mon, &sPartyMenuBoxes[slotId]); } } diff --git a/src/party_menu_specials.c b/src/party_menu_specials.c index 9e91d65d7..a04f2e83a 100644 --- a/src/party_menu_specials.c +++ b/src/party_menu_specials.c @@ -44,7 +44,7 @@ static void Task_ChoosePartyMon(u8 taskId) void SelectMoveDeleterMove(void) { - ShowSelectMovePokemonSummaryScreen(gPlayerParty, gSpecialVar_0x8004, gPlayerPartyCount - 1, CB2_ReturnToField, 0); + ShowSelectMovePokemonSummaryScreen(gPlayerParty, gSpecialVar_0x8004, CB2_ReturnToField, 0); SetPokemonSummaryScreenMode(PSS_MODE_FORGET_MOVE); gFieldCallback = FieldCB_ContinueScriptHandleMusic; } diff --git a/src/pokemon.c b/src/pokemon.c index 57c4d09b9..d197fe801 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -9,6 +9,7 @@ #include "battle_pike.h" #include "battle_pyramid.h" #include "battle_setup.h" +#include "battle_tower.h" #include "battle_z_move.h" #include "caps.h" #include "data.h" @@ -29,6 +30,7 @@ #include "m4a.h" #include "main.h" // #include "move_relearner.h" +#include "naming_screen.h" #include "overworld.h" #include "party_menu.h" #include "pokedex.h" @@ -64,6 +66,7 @@ #include "constants/items.h" #include "constants/layouts.h" #include "constants/moves.h" +#include "constants/party_menu.h" #include "constants/regions.h" #include "constants/songs.h" #include "constants/trainers.h" @@ -71,6 +74,8 @@ #include "constants/weather.h" #include "constants/move_relearner.h" +extern u16 gSpecialVar_ItemId; + #define FRIENDSHIP_EVO_THRESHOLD ((P_FRIENDSHIP_EVO_THRESHOLD >= GEN_8) ? 160 : 220) struct SpeciesItem @@ -644,7 +649,6 @@ static const struct SpindaSpot gSpindaSpotGraphics[] = // Support percentages are listed in comments off to the side instead #define PALACE_STYLE(atk, def, atkLow, defLow) {atk, atk + def, atkLow, atkLow + defLow} - const struct NatureInfo gNaturesInfo[NUM_NATURES] = { [NATURE_HARDY] = @@ -1114,7 +1118,6 @@ static const struct SpriteTemplate sTrainerBackSpriteTemplate = .paletteTag = 0, .oam = &gOamData_BattleSpritePlayerSide, .anims = NULL, - .images = NULL, .affineAnims = gAffineAnims_BattleSpritePlayerSide, .callback = SpriteCB_BattleSpriteStartSlideLeft, }; @@ -1205,10 +1208,6 @@ static const struct SpriteTemplate sSpriteTemplate_64x64 = .tileTag = TAG_NONE, .paletteTag = TAG_NONE, .oam = &sOamData_64x64, - .anims = gDummySpriteAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCallbackDummy, }; // NOTE: Reordering this array will break compatibility with existing @@ -1557,7 +1556,7 @@ void CreateBattleTowerMon(struct Pokemon *mon, struct BattleTowerPokemon *src) { s32 i; u8 nickname[max(32, POKEMON_NAME_BUFFER_SIZE)]; - u8 language; + enum Language language; u8 value; CreateMon(mon, src->species, src->level, src->personality, OTID_STRUCT_PRESET(src->otId)); @@ -1612,7 +1611,7 @@ void CreateBattleTowerMon_HandleLevel(struct Pokemon *mon, struct BattleTowerPok s32 i; u8 nickname[max(32, POKEMON_NAME_BUFFER_SIZE)]; u8 level; - u8 language; + enum Language language; u8 value; if (gSaveBlock2Ptr->frontier.lvlMode != FRONTIER_LVL_50) @@ -1990,9 +1989,14 @@ u16 GiveMoveToBattleMon(struct BattlePokemon *mon, enum Move move) void SetMonMoveSlot(struct Pokemon *mon, enum Move move, u8 slot) { - SetMonData(mon, MON_DATA_MOVE1 + slot, &move); + SetBoxMonMoveSlot(&mon->box, move, slot); +} + +void SetBoxMonMoveSlot(struct BoxPokemon *mon, enum Move move, u8 slot) +{ + SetBoxMonData(mon, MON_DATA_MOVE1 + slot, &move); u32 pp = GetMovePP(move); - SetMonData(mon, MON_DATA_PP1 + slot, &pp); + SetBoxMonData(mon, MON_DATA_PP1 + slot, &pp); } static void SetMonMoveSlot_KeepPP(struct Pokemon *mon, enum Move move, u8 slot) @@ -2216,9 +2220,9 @@ void DeleteFirstMoveAndGiveMoveToBoxMon(struct BoxPokemon *boxMon, enum Move mov SetBoxMonData(boxMon, MON_DATA_PP_BONUSES, &ppBonuses); } -u8 CountAliveMonsInBattle(u8 caseId, u32 battler) +u8 CountAliveMonsInBattle(u8 caseId, enum BattlerId battler) { - u32 i; + enum BattlerId i; u32 retVal = 0; switch (caseId) @@ -2249,7 +2253,7 @@ u8 CountAliveMonsInBattle(u8 caseId, u32 battler) return retVal; } -u8 GetDefaultMoveTarget(u8 battlerId) +u8 GetDefaultMoveTarget(enum BattlerId battlerId) { u8 opposing = BATTLE_OPPOSITE(GetBattlerSide(battlerId)); @@ -3505,7 +3509,7 @@ u8 CalculatePartyCount(struct Pokemon *party) return partyCount; } -u8 CalculatePartyCountOfSide(u32 battler, struct Pokemon *party) +u8 CalculatePartyCountOfSide(enum BattlerId battler, struct Pokemon *party) { s32 partyCount, partySize; GetAIPartyIndexes(battler, &partyCount, &partySize); @@ -3531,7 +3535,7 @@ u8 CalculateEnemyPartyCount(void) return gEnemyPartyCount; } -u8 CalculateEnemyPartyCountInSide(u32 battler) +u8 CalculateEnemyPartyCountInSide(enum BattlerId battler) { return CalculatePartyCountOfSide(battler, gEnemyParty); } @@ -3895,7 +3899,7 @@ void PokemonToBattleMon(struct Pokemon *src, struct BattlePokemon *dst) memset(&dst->volatiles, 0, sizeof(struct Volatiles)); } -void CopyPartyMonToBattleData(u32 battler, u32 partyIndex) +void CopyPartyMonToBattleData(enum BattlerId battler, u32 partyIndex) { enum BattleSide side = GetBattlerSide(battler); struct Pokemon *party = GetSideParty(side); @@ -3948,7 +3952,7 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, enum Item item, u8 partyIndex, u32 temp1, temp2; s8 friendshipChange = 0; enum HoldEffect holdEffect; - u8 battler = MAX_BATTLERS_COUNT; + enum BattlerId battler = MAX_BATTLERS_COUNT; bool32 friendshipOnly = FALSE; enum Item heldItem; u8 effectFlags; @@ -4386,7 +4390,7 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, enum Item item, u8 partyIndex, return retVal; } -bool8 HealStatusConditions(struct Pokemon *mon, u32 healMask, u8 battler) +bool8 HealStatusConditions(struct Pokemon *mon, u32 healMask, enum BattlerId battler) { u32 status = GetMonData(mon, MON_DATA_STATUS, 0); @@ -4421,7 +4425,7 @@ bool8 HealStatusConditions(struct Pokemon *mon, u32 healMask, u8 battler) } } -u8 GetItemEffectParamOffset(u32 battler, enum Item itemId, u8 effectByte, u8 effectBit) +u8 GetItemEffectParamOffset(enum BattlerId battler, enum Item itemId, u8 effectByte, u8 effectBit) { const u8 *temp; const u8 *itemEffect; @@ -5458,7 +5462,10 @@ u8 GetTrainerEncounterMusicId(u16 trainerOpponentId) u32 sanitizedTrainerId = SanitizeTrainerId(trainerOpponentId); enum DifficultyLevel difficulty = GetTrainerDifficultyLevel(sanitizedTrainerId); - return gTrainers[difficulty][sanitizedTrainerId].encounterMusic; + if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) + return GetTrainerEncounterMusicIdInBattlePyramid(trainerOpponentId); + else + return gTrainers[difficulty][sanitizedTrainerId].encounterMusic; } u16 ModifyStatByNature(u8 nature, u16 stat, enum Stat statIndex) @@ -6186,7 +6193,12 @@ u16 GetBattleBGM(void) } else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) { - enum TrainerClassID trainerClass = GetTrainerClassFromId(TRAINER_BATTLE_PARAM.opponentA); + enum TrainerClassID trainerClass; + + if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER) + trainerClass = GetFrontierOpponentClass(TRAINER_BATTLE_PARAM.opponentA); + else + trainerClass = GetTrainerClassFromId(TRAINER_BATTLE_PARAM.opponentA); switch (trainerClass) { @@ -6195,6 +6207,14 @@ u16 GetBattleBGM(void) case TRAINER_CLASS_LEADER: case TRAINER_CLASS_ELITE_FOUR: return MUS_VS_GYM_LEADER; + case TRAINER_CLASS_SALON_MAIDEN: + case TRAINER_CLASS_DOME_ACE: + case TRAINER_CLASS_PALACE_MAVEN: + case TRAINER_CLASS_ARENA_TYCOON: + case TRAINER_CLASS_FACTORY_HEAD: + case TRAINER_CLASS_PIKE_QUEEN: + case TRAINER_CLASS_PYRAMID_KING: + return MUS_VS_FRONTIER_BRAIN; case TRAINER_CLASS_BOSS: case TRAINER_CLASS_TEAM_ROCKET: case TRAINER_CLASS_COOLTRAINER: @@ -6335,13 +6355,13 @@ bool8 IsMonSpriteNotFlipped(u16 species) return gSpeciesInfo[species].noFlip; } -s8 GetMonFlavorRelation(struct Pokemon *mon, u8 flavor) +s8 GetMonFlavorRelation(struct Pokemon *mon, enum Flavor flavor) { u8 nature = GetNature(mon); return gPokeblockFlavorCompatibilityTable[nature * FLAVOR_COUNT + flavor]; } -s8 GetFlavorRelationByPersonality(u32 personality, u8 flavor) +s8 GetFlavorRelationByPersonality(u32 personality, enum Flavor flavor) { u8 nature = GetNatureFromPersonality(personality); return gPokeblockFlavorCompatibilityTable[nature * FLAVOR_COUNT + flavor]; @@ -6503,7 +6523,8 @@ const u8 *GetTrainerPartnerName(void) { if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) { - return GetTrainerNameFromId(gPartnerTrainerId); + GetFrontierTrainerName(gStringVar1, gPartnerTrainerId); + return gStringVar1; } else { @@ -6695,7 +6716,7 @@ enum TrainerPicID FacilityClassToPicIndex(u16 facilityClass) return gFacilityClassToPicIndex[facilityClass]; } -enum TrainerPicID PlayerGenderToFrontTrainerPicId(u8 playerGender) +enum TrainerPicID PlayerGenderToFrontTrainerPicId(enum Gender playerGender) { if (playerGender != MALE) return FacilityClassToPicIndex(FACILITY_CLASS_LEAF); @@ -6947,129 +6968,255 @@ u8 GetFormIdFromFormSpeciesId(u16 formSpeciesId) } // Returns the current species if no form change is possible -u32 GetFormChangeTargetSpecies(struct Pokemon *mon, enum FormChanges method, u32 arg) +u32 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *boxMon, enum FormChanges method) { - return GetFormChangeTargetSpeciesBoxMon(&mon->box, method, arg); + u32 species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL); + const struct FormChange *formChanges = GetSpeciesFormChanges(species); + + if (formChanges == NULL) + return species; + + struct FormChangeContext ctx = + { + .method = method, + .currentSpecies = species, + .heldItem = GetBoxMonData(boxMon, MON_DATA_HELD_ITEM), + .ability = GetAbilityBySpecies(species, GetBoxMonData(boxMon, MON_DATA_ABILITY_NUM)), + .partyItemUsed = gSpecialVar_ItemId, + .multichoiceSelection = gSpecialVar_Result, + .status = GetBoxMonData(boxMon, MON_DATA_STATUS), + }; + + return GetFormChangeTargetSpecies_Internal(ctx); } // Returns the current species if no form change is possible -u32 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *boxMon, enum FormChanges method, u32 arg) +u32 GetFormChangeTargetSpecies(struct Pokemon *mon, enum FormChanges method) +{ + return GetFormChangeTargetSpeciesBoxMon(&mon->box, method); +} + +u32 GetFormChangeTargetSpecies_Internal(struct FormChangeContext ctx) { u32 i; - u32 species = GetBoxMonData(boxMon, MON_DATA_SPECIES); - u32 targetSpecies = species; - const struct FormChange *formChanges = GetSpeciesFormChanges(species); - u16 heldItem; - enum Ability ability; + u32 targetSpecies = ctx.currentSpecies; + const struct FormChange *formChanges = GetSpeciesFormChanges(ctx.currentSpecies); - if (formChanges != NULL) + if (formChanges == NULL) + return ctx.currentSpecies; + + for (i = 0; formChanges[i].method != FORM_CHANGE_TERMINATOR; i++) { - heldItem = GetBoxMonData(boxMon, MON_DATA_HELD_ITEM); - ability = GetAbilityBySpecies(species, GetBoxMonData(boxMon, MON_DATA_ABILITY_NUM)); + if (!(ctx.method == formChanges[i].method && ctx.currentSpecies != formChanges[i].targetSpecies)) + continue; - for (i = 0; formChanges[i].method != FORM_CHANGE_TERMINATOR; i++) + switch (ctx.method) { - if (method == formChanges[i].method && species != formChanges[i].targetSpecies) + case FORM_CHANGE_ITEM_HOLD: + if ((ctx.heldItem == formChanges[i].param1 || formChanges[i].param1 == ITEM_NONE) + && (ctx.ability == formChanges[i].param2 || formChanges[i].param2 == ABILITY_NONE)) { - switch (method) + // This is to prevent reverting to base form when giving the item to the corresponding form. + // Eg. Giving a Zap Plate to an Electric Arceus without an item (most likely to happen when using givemon) + bool32 currentItemForm = FALSE; + for (u32 j = 0; formChanges[j].method != FORM_CHANGE_TERMINATOR; j++) { - case FORM_CHANGE_ITEM_HOLD: - if ((heldItem == formChanges[i].param1 || formChanges[i].param1 == ITEM_NONE) - && (ability == formChanges[i].param2 || formChanges[i].param2 == ABILITY_NONE)) + if (ctx.currentSpecies == formChanges[j].targetSpecies + && formChanges[j].param1 == ctx.heldItem + && formChanges[j].param1 != ITEM_NONE) { - // This is to prevent reverting to base form when giving the item to the corresponding form. - // Eg. Giving a Zap Plate to an Electric Arceus without an item (most likely to happen when using givemon) - bool32 currentItemForm = FALSE; - for (u32 j = 0; formChanges[j].method != FORM_CHANGE_TERMINATOR; j++) - { - if (species == formChanges[j].targetSpecies - && formChanges[j].param1 == heldItem - && formChanges[j].param1 != ITEM_NONE) - { - currentItemForm = TRUE; - break; - } - } - if (!currentItemForm) - targetSpecies = formChanges[i].targetSpecies; + currentItemForm = TRUE; + break; } - break; - case FORM_CHANGE_ITEM_USE: - if (arg == formChanges[i].param1) - { - bool32 pass = TRUE; - switch (formChanges[i].param2) - { - case DAY: - if (GetTimeOfDay() == TIME_NIGHT) - pass = FALSE; - break; - case NIGHT: - if (GetTimeOfDay() != TIME_NIGHT) - pass = FALSE; - break; - } - - if (formChanges[i].param3 != STATUS1_NONE && GetBoxMonData(boxMon, MON_DATA_STATUS) & formChanges[i].param3) - pass = FALSE; - - if (pass) - targetSpecies = formChanges[i].targetSpecies; - } - break; - case FORM_CHANGE_ITEM_USE_MULTICHOICE: - if (arg == formChanges[i].param1) - { - if (formChanges[i].param2 == gSpecialVar_Result) - targetSpecies = formChanges[i].targetSpecies; - } - break; - case FORM_CHANGE_MOVE: - if (BoxMonKnowsMove(boxMon, formChanges[i].param1) != formChanges[i].param2) - targetSpecies = formChanges[i].targetSpecies; - break; - case FORM_CHANGE_BEGIN_BATTLE: - case FORM_CHANGE_END_BATTLE: - if (heldItem == formChanges[i].param1 || formChanges[i].param1 == ITEM_NONE) - targetSpecies = formChanges[i].targetSpecies; - break; - case FORM_CHANGE_END_BATTLE_ENVIRONMENT: - if (gBattleEnvironment == formChanges[i].param1) - targetSpecies = formChanges[i].targetSpecies; - break; - case FORM_CHANGE_WITHDRAW: - case FORM_CHANGE_DEPOSIT: - case FORM_CHANGE_FAINT: - case FORM_CHANGE_DAYS_PASSED: + } + if (!currentItemForm) targetSpecies = formChanges[i].targetSpecies; + } + break; + case FORM_CHANGE_ITEM_USE: + if (ctx.partyItemUsed == formChanges[i].param1) + { + bool32 pass = TRUE; + switch (formChanges[i].param2) + { + case DAY: + if (GetTimeOfDay() == TIME_NIGHT) + pass = FALSE; break; - case FORM_CHANGE_STATUS: - if (GetBoxMonData(boxMon, MON_DATA_STATUS) & formChanges[i].param1) + case NIGHT: + if (GetTimeOfDay() != TIME_NIGHT) + pass = FALSE; + break; + } + + if (formChanges[i].param3 != STATUS1_NONE && ctx.status & formChanges[i].param3) + pass = FALSE; + + if (pass) + targetSpecies = formChanges[i].targetSpecies; + } + break; + case FORM_CHANGE_ITEM_USE_MULTICHOICE: + if (ctx.partyItemUsed == formChanges[i].param1 + && ctx.multichoiceSelection == formChanges[i].param2) + { + targetSpecies = formChanges[i].targetSpecies; + } + break; + case FORM_CHANGE_MOVE: + if (ctx.learnedMove != formChanges[i].param2) + targetSpecies = formChanges[i].targetSpecies; + break; + case FORM_CHANGE_BEGIN_BATTLE: + case FORM_CHANGE_END_BATTLE: + if (ctx.heldItem == formChanges[i].param1 || formChanges[i].param1 == ITEM_NONE) + targetSpecies = formChanges[i].targetSpecies; + break; + case FORM_CHANGE_END_BATTLE_ENVIRONMENT: + if (gBattleEnvironment == formChanges[i].param1) + targetSpecies = formChanges[i].targetSpecies; + break; + case FORM_CHANGE_WITHDRAW: + case FORM_CHANGE_DEPOSIT: + case FORM_CHANGE_FAINT: + case FORM_CHANGE_DAYS_PASSED: + targetSpecies = formChanges[i].targetSpecies; + break; + case FORM_CHANGE_STATUS: + if (ctx.status & formChanges[i].param1) + targetSpecies = formChanges[i].targetSpecies; + break; + case FORM_CHANGE_TIME_OF_DAY: + switch (formChanges[i].param1) + { + case DAY: + if (GetTimeOfDay() != TIME_NIGHT) + targetSpecies = formChanges[i].targetSpecies; + break; + case NIGHT: + if (GetTimeOfDay() == TIME_NIGHT) + targetSpecies = formChanges[i].targetSpecies; + break; + } + break; + case FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM: + case FORM_CHANGE_BATTLE_PRIMAL_REVERSION: + case FORM_CHANGE_BATTLE_ULTRA_BURST: + if (ctx.heldItem == formChanges[i].param1) + targetSpecies = formChanges[i].targetSpecies; + break; + case FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE: + if (ctx.moves[0] == formChanges[i].param1 + || ctx.moves[1] == formChanges[i].param1 + || ctx.moves[2] == formChanges[i].param1 + || ctx.moves[3] == formChanges[i].param1) + targetSpecies = formChanges[i].targetSpecies; + break; + case FORM_CHANGE_BATTLE_SWITCH_OUT: + if (formChanges[i].param1 == ctx.ability || formChanges[i].param1 == ABILITY_NONE) + targetSpecies = formChanges[i].targetSpecies; + break; + case FORM_CHANGE_BATTLE_HP_PERCENT_DURING_MOVE: + if (ctx.ability == formChanges[i].param1 + && gCurrentMove == formChanges[i].param4) + { + // We multiply by 100 to make sure that integer division doesn't mess with the health check. + u32 hpCheck = ctx.hp * 100 * 100 / ctx.maxHP; + switch(formChanges[i].param2) + { + case HP_HIGHER_THAN: + if (hpCheck > formChanges[i].param3 * 100) targetSpecies = formChanges[i].targetSpecies; break; - case FORM_CHANGE_TIME_OF_DAY: - switch (formChanges[i].param1) - { - case DAY: - if (GetTimeOfDay() != TIME_NIGHT) - targetSpecies = formChanges[i].targetSpecies; - break; - case NIGHT: - if (GetTimeOfDay() == TIME_NIGHT) - targetSpecies = formChanges[i].targetSpecies; - break; - } - break; - default: + case HP_LOWER_EQ_THAN: + if (hpCheck <= formChanges[i].param3 * 100) + targetSpecies = formChanges[i].targetSpecies; break; } } + break; + case FORM_CHANGE_BATTLE_HP_PERCENT_TURN_END: + case FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT: + if (ctx.ability == formChanges[i].param1 + && ctx.level >= formChanges[i].param4) + { + // We multiply by 100 to make sure that integer division doesn't mess with the health check. + u32 hpCheck = ctx.hp * 100 * 100 / ctx.maxHP; + switch(formChanges[i].param2) + { + case HP_HIGHER_THAN: + if (hpCheck > formChanges[i].param3 * 100) + targetSpecies = formChanges[i].targetSpecies; + break; + case HP_LOWER_EQ_THAN: + if (hpCheck <= formChanges[i].param3 * 100) + targetSpecies = formChanges[i].targetSpecies; + break; + } + } + break; + case FORM_CHANGE_BATTLE_GIGANTAMAX: + if (ctx.gmaxFactor) + targetSpecies = formChanges[i].targetSpecies; + break; + case FORM_CHANGE_BATTLE_WEATHER: + // Check if there is a required ability and if the battler's ability does not match it + // or is suppressed. If so, revert to the no weather form. + if (formChanges[i].param2 + && ctx.ability != formChanges[i].param2 + && formChanges[i].param1 == B_WEATHER_NONE) + { + targetSpecies = formChanges[i].targetSpecies; + } + // We need to revert the weather form if the field is under Air Lock, too. + else if (!HasWeatherEffect() && formChanges[i].param1 == B_WEATHER_NONE) + { + targetSpecies = formChanges[i].targetSpecies; + } + // Otherwise, just check for a match between the weather and the form change table. + // Added a check for whether the weather is in effect to prevent end-of-turn soft locks with Cloud Nine / Air Lock + else if (((gBattleWeather & formChanges[i].param1) && HasWeatherEffect()) + || (gBattleWeather == B_WEATHER_NONE && formChanges[i].param1 == B_WEATHER_NONE)) + { + targetSpecies = formChanges[i].targetSpecies; + } + break; + case FORM_CHANGE_BATTLE_HIT_BY_MOVE_CATEGORY: + if (ctx.ability == formChanges[i].param1 + && formChanges[i].param2 == GetBattleMoveCategory(gCurrentMove)) + targetSpecies = formChanges[i].targetSpecies; + break; + case FORM_CHANGE_BATTLE_SWITCH_IN: + case FORM_CHANGE_BATTLE_TURN_END: + case FORM_CHANGE_BATTLE_HIT_BY_CONFUSION_SELF_DMG: + if (formChanges[i].param1 == ctx.ability) + targetSpecies = formChanges[i].targetSpecies; + break; + case FORM_CHANGE_BATTLE_TERASTALLIZATION: + if (ctx.teraType == formChanges[i].param1) + targetSpecies = formChanges[i].targetSpecies; + break; + case FORM_CHANGE_BATTLE_BEFORE_MOVE: + case FORM_CHANGE_BATTLE_AFTER_MOVE: + if (formChanges[i].param1 == gCurrentMove + && (formChanges[i].param2 == ABILITY_NONE || formChanges[i].param2 == ctx.ability)) + targetSpecies = formChanges[i].targetSpecies; + break; + case FORM_CHANGE_BATTLE_BEFORE_MOVE_CATEGORY: + if (formChanges[i].param1 == GetBattleMoveCategory(gCurrentMove) + && (formChanges[i].param2 == ABILITY_NONE || formChanges[i].param2 == ctx.ability)) + targetSpecies = formChanges[i].targetSpecies; + break; + case FORM_CHANGE_OVERWORLD_WEATHER: + case FORM_CHANGE_TERMINATOR: + break; } } return targetSpecies; } + void TrySetDayLimitToFormChange(struct Pokemon *mon) { u32 i; @@ -7226,40 +7373,83 @@ bool32 SpeciesHasGenderDifferences(u16 species) return FALSE; } -bool32 TryFormChange(u32 monId, enum BattleSide side, enum FormChanges method) +static struct PartyState *GetBattlerPartyStateByPokemon(struct Pokemon *partyMon) { - struct Pokemon *party = (side == B_SIDE_PLAYER) ? gPlayerParty : gEnemyParty; + if (gBattleStruct == NULL) + return NULL; - if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG, 0) == SPECIES_NONE - || GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG, 0) == SPECIES_EGG) + for (int i = 0; i < NUM_BATTLE_SIDES; i++) + { + for (int j = 0; j < PARTY_SIZE; j++) + { + struct Pokemon *mon = &GetSideParty(i)[j]; + if (partyMon == mon) + return &gBattleStruct->partyState[i][j]; + } + } + return NULL; +} + +bool32 TryFormChange(struct Pokemon *mon, enum FormChanges method) +{ + if (GetMonData(mon, MON_DATA_SPECIES_OR_EGG, 0) == SPECIES_NONE + || GetMonData(mon, MON_DATA_SPECIES_OR_EGG, 0) == SPECIES_EGG) return FALSE; - u32 currentSpecies = GetMonData(&party[monId], MON_DATA_SPECIES); - u32 targetSpecies = GetFormChangeTargetSpecies(&party[monId], method, 0); + u32 currentSpecies = GetMonData(mon, MON_DATA_SPECIES); + u32 targetSpecies = GetFormChangeTargetSpecies(mon, method); - // If the battle ends, and there's not a specified species to change back to,, + struct PartyState *battlePartyState = GetBattlerPartyStateByPokemon(mon); + // If the battle ends, and there's not a specified species to change back to, // use the species at the start of the battle. if (targetSpecies == SPECIES_NONE - && gBattleStruct != NULL - && gBattleStruct->partyState[side][monId].changedSpecies != SPECIES_NONE + && battlePartyState != NULL && battlePartyState->changedSpecies != SPECIES_NONE // This is added to prevent FORM_CHANGE_END_BATTLE_ENVIRONMENT from omitting move changes // at the end of the battle, as it was being counting as a successful form change. - && method == FORM_CHANGE_END_BATTLE) + && (method == FORM_CHANGE_END_BATTLE || method == FORM_CHANGE_FAINT)) { - targetSpecies = gBattleStruct->partyState[side][monId].changedSpecies; + targetSpecies = battlePartyState->changedSpecies; } - if (targetSpecies != currentSpecies && targetSpecies != SPECIES_NONE) + assertf(targetSpecies != SPECIES_NONE, "form change target returned NONE. cur:%d, method:%d", currentSpecies, method) { - TryToSetBattleFormChangeMoves(&party[monId], method); - SetMonData(&party[monId], MON_DATA_SPECIES, &targetSpecies); - CalculateMonStats(&party[monId]); + return FALSE; + } + + if (targetSpecies != currentSpecies) + { + TryToSetBattleFormChangeMoves(mon, method); + SetMonData(mon, MON_DATA_SPECIES, &targetSpecies); + TrySetDayLimitToFormChange(mon); + CalculateMonStats(mon); return TRUE; } return FALSE; } +bool32 TryBoxMonFormChange(struct BoxPokemon *boxMon, enum FormChanges method) +{ + if (GetBoxMonData(boxMon, MON_DATA_SPECIES_OR_EGG, 0) == SPECIES_NONE + || GetBoxMonData(boxMon, MON_DATA_SPECIES_OR_EGG, 0) == SPECIES_EGG) + return FALSE; + + u32 currentSpecies = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL); + u32 targetSpecies = GetFormChangeTargetSpeciesBoxMon(boxMon, method); + + assertf(targetSpecies != SPECIES_NONE, "form change target returned NONE. cur:%d, method:%d", currentSpecies, method) + { + return FALSE; + } + + if (targetSpecies != currentSpecies) + { + SetBoxMonData(boxMon, MON_DATA_SPECIES, &targetSpecies); + return TRUE; + } + return FALSE; +} + u16 SanitizeSpeciesId(u16 species) { assertf(species <= NUM_SPECIES && (species == SPECIES_NONE || IsSpeciesEnabled(species)), "invalid species: %d", species) @@ -7462,19 +7652,11 @@ void UpdateDaysPassedSinceFormChange(u16 days) SetMonData(mon, MON_DATA_DAYS_SINCE_FORM_CHANGE, &daysSinceFormChange); if (daysSinceFormChange == 0) - { - u32 targetSpecies = GetFormChangeTargetSpecies(mon, FORM_CHANGE_DAYS_PASSED, 0); - - if (targetSpecies != currentSpecies && targetSpecies != SPECIES_NONE) - { - SetMonData(mon, MON_DATA_SPECIES, &targetSpecies); - CalculateMonStats(mon); - } - } + TryFormChange(mon, FORM_CHANGE_DAYS_PASSED); } } -enum Type CheckDynamicMoveType(struct Pokemon *mon, enum Move move, u32 battler, enum MonState state) +enum Type CheckDynamicMoveType(struct Pokemon *mon, enum Move move, enum BattlerId battler, enum MonState state) { enum Type moveType = GetDynamicMoveType(mon, move, battler, state); if (moveType != TYPE_NONE) @@ -7630,6 +7812,14 @@ u32 GiveScriptedMonToPlayer(struct Pokemon *mon, u8 slot) return sentToPc; } +void ChangePokemonNicknameWithCallback(void (*callback)(void)) +{ + struct BoxPokemon *boxMon = GetSelectedBoxMonFromPcOrParty(); + GetBoxMonData(boxMon, MON_DATA_NICKNAME, gStringVar3); + GetBoxMonData(boxMon, MON_DATA_NICKNAME, gStringVar2); + DoNamingScreen(NAMING_SCREEN_NICKNAME, gStringVar2, GetBoxMonData(boxMon, MON_DATA_SPECIES), GetBoxMonGender(boxMon), GetBoxMonData(boxMon, MON_DATA_PERSONALITY), callback); +} + //pokefirered specific u8 GetPlayerPartyHighestLevel(void) { @@ -7706,3 +7896,4 @@ u16 GetFirstPartnerMove(u16 species) return MOVE_NONE; } } + diff --git a/src/pokemon_icon.c b/src/pokemon_icon.c index 00aa93ffc..29d19a4b4 100644 --- a/src/pokemon_icon.c +++ b/src/pokemon_icon.c @@ -135,12 +135,17 @@ static const u16 sSpriteImageSizes[3][4] = }; u8 CreateMonIcon(u16 species, void (*callback)(struct Sprite *), s16 x, s16 y, u8 subpriority, u32 personality) +{ + return CreateMonIconIsEgg(species, callback, x, y, subpriority, personality, FALSE); +} + +u8 CreateMonIconIsEgg(u16 species, void (*callback)(struct Sprite *), s16 x, s16 y, u8 subpriority, u32 personality, bool32 isEgg) { u8 spriteId; struct MonIconSpriteTemplate iconTemplate = { .oam = &sMonIconOamData, - .image = GetMonIconPtr(species, personality), + .image = GetMonIconPtrIsEgg(species, personality, isEgg), .anims = sMonIconAnims, .affineAnims = sMonIconAffineAnims, .callback = callback, @@ -148,11 +153,22 @@ u8 CreateMonIcon(u16 species, void (*callback)(struct Sprite *), s16 x, s16 y, u }; species = SanitizeSpeciesId(species); - if (species > NUM_SPECIES) + if (isEgg) + { + if (gSpeciesInfo[species].eggId != EGG_ID_NONE) + iconTemplate.paletteTag = POKE_ICON_BASE_PAL_TAG + gEggDatas[gSpeciesInfo[species].eggId].eggIconPalIndex; + else + iconTemplate.paletteTag = POKE_ICON_BASE_PAL_TAG + gSpeciesInfo[SPECIES_EGG].iconPalIndex; + } + else if (species > NUM_SPECIES) + { iconTemplate.paletteTag = POKE_ICON_BASE_PAL_TAG; + } #if P_GENDER_DIFFERENCES else if (gSpeciesInfo[species].iconSpriteFemale != NULL && IsPersonalityFemale(species, personality)) + { iconTemplate.paletteTag = POKE_ICON_BASE_PAL_TAG + gSpeciesInfo[species].iconPalIndexFemale; + } #endif spriteId = CreateMonIconSprite(&iconTemplate, x, y, subpriority); @@ -211,7 +227,12 @@ u16 GetIconSpeciesNoPersonality(u16 species) const u8 *GetMonIconPtr(u16 species, u32 personality) { - return GetMonIconTiles(GetIconSpecies(species, personality), personality); + return GetMonIconPtrIsEgg(species, personality, FALSE); +} + +const u8 *GetMonIconPtrIsEgg(u16 species, u32 personality, bool32 isEgg) +{ + return GetMonIconTilesIsEgg(GetIconSpecies(species, personality), personality, isEgg); } void FreeAndDestroyMonIconSprite(struct Sprite *sprite) @@ -284,21 +305,36 @@ void SpriteCB_MonIcon(struct Sprite *sprite) } const u8 *GetMonIconTiles(u16 species, u32 personality) +{ + return GetMonIconTilesIsEgg(species, personality, FALSE); +} + +const u8 *GetMonIconTilesIsEgg(u16 species, u32 personality, bool32 isEgg) { const u8 *iconSprite; if (species > NUM_SPECIES) species = SPECIES_NONE; + if (isEgg) + { + if (gSpeciesInfo[species].eggId != EGG_ID_NONE) + iconSprite = gEggDatas[gSpeciesInfo[species].eggId].eggIcon; + else + iconSprite = gSpeciesInfo[SPECIES_EGG].iconSprite; + } + else + { #if P_GENDER_DIFFERENCES - if (gSpeciesInfo[species].iconSpriteFemale != NULL && IsPersonalityFemale(species, personality)) - iconSprite = gSpeciesInfo[species].iconSpriteFemale; - else + if (gSpeciesInfo[species].iconSpriteFemale != NULL && IsPersonalityFemale(species, personality)) + iconSprite = gSpeciesInfo[species].iconSpriteFemale; + else #endif - if (gSpeciesInfo[species].iconSprite != NULL) - iconSprite = gSpeciesInfo[species].iconSprite; - else - iconSprite = gSpeciesInfo[SPECIES_NONE].iconSprite; + if (gSpeciesInfo[species].iconSprite != NULL) + iconSprite = gSpeciesInfo[species].iconSprite; + else + iconSprite = gSpeciesInfo[SPECIES_NONE].iconSprite; + } return iconSprite; } diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index 0abb47ad4..427dac3c9 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -1277,9 +1277,9 @@ void ShowPokemonSummaryScreen(struct Pokemon * party, u8 cursorPos, u8 lastIdx, SetMainCallback2(CB2_SetUpPSS); } -void ShowSelectMovePokemonSummaryScreen(struct Pokemon * party, u8 cursorPos, u8 lastIdx, MainCallback savedCallback, u16 a4) +void ShowSelectMovePokemonSummaryScreen(struct Pokemon * party, u8 cursorPos, MainCallback savedCallback, u16 a4) { - ShowPokemonSummaryScreen(party, cursorPos, lastIdx, savedCallback, PSS_MODE_SELECT_MOVE); + ShowPokemonSummaryScreen(party, cursorPos, gPlayerPartyCount - 1, savedCallback, PSS_MODE_SELECT_MOVE); sMonSummaryScreen->moveIds[4] = a4; } diff --git a/src/recorded_battle.c b/src/recorded_battle.c index f0f1986b3..b619c6fe4 100644 --- a/src/recorded_battle.c +++ b/src/recorded_battle.c @@ -43,6 +43,7 @@ EWRAM_DATA static u8 sRecordMode = 0; EWRAM_DATA static u8 sFrontierFacility = 0; EWRAM_DATA static u8 sFrontierBrainSymbol = 0; EWRAM_DATA u8 gRecordedBattleMultiplayerId = 0; +EWRAM_DATA static u8 sFrontierPassFlag = 0; EWRAM_DATA static u8 sBattleScene = 0; EWRAM_DATA static u8 sTextSpeed = 0; EWRAM_DATA static u32 sBattleFlags = 0; @@ -328,6 +329,22 @@ void RecordedBattle_SaveParties(void) } } +void RecordedBattle_ClearFrontierPassFlag(void) +{ + sFrontierPassFlag = 0; +} + +// Set sFrontierPassFlag to received state of FLAG_SYS_FRONTIER_PASS +void RecordedBattle_SetFrontierPassFlagFromHword(u16 flags) +{ + sFrontierPassFlag |= (flags & (1 << 15)) >> 15; +} + +u8 RecordedBattle_GetFrontierPassFlag(void) +{ + return sFrontierPassFlag; +} + u8 GetBattleSceneInRecordedBattle(void) { return sBattleScene; @@ -359,12 +376,12 @@ void RecordedBattle_CopyBattlerMoves(u32 battler) void RecordedBattle_CheckMovesetChanges(u8 mode) { - s32 battler, j, k; + s32 j, k; if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) return; - for (battler = 0; battler < gBattlersCount; battler++) + for (enum BattlerId battler = 0; battler < gBattlersCount; battler++) { // Player's side only if (IsOnPlayerSide(battler)) @@ -436,12 +453,12 @@ void RecordedBattle_CheckMovesetChanges(u8 mode) { struct Pokemon *mon = GetBattlerMon(battler); for (j = 0; j < MAX_MON_MOVES; j++) - ppBonuses[j] = (GetMonData(mon, MON_DATA_PP_BONUSES, NULL) & ((3 << (j << 1)))) >> (j << 1); + ppBonuses[j] = (GetMonData(mon, MON_DATA_PP_BONUSES) & ((3 << (j << 1)))) >> (j << 1); for (j = 0; j < MAX_MON_MOVES; j++) { - movePp.moves[j] = GetMonData(mon, MON_DATA_MOVE1 + moveSlots[j], NULL); - movePp.currentPp[j] = GetMonData(mon, MON_DATA_PP1 + moveSlots[j], NULL); + movePp.moves[j] = GetMonData(mon, MON_DATA_MOVE1 + moveSlots[j]); + movePp.currentPp[j] = GetMonData(mon, MON_DATA_PP1 + moveSlots[j]); movePp.maxPp[j] = ppBonuses[moveSlots[j]]; } for (j = 0; j < MAX_MON_MOVES; j++) @@ -455,7 +472,7 @@ void RecordedBattle_CheckMovesetChanges(u8 mode) SetMonData(mon, MON_DATA_PP_BONUSES, &ppBonusSet); } - gChosenMoveByBattler[battler] = GetChosenMoveFromPosition(battler); + gChosenMoveByBattler[battler] = GetBattlerChosenMove(battler); } } } diff --git a/src/region_map.c b/src/region_map.c index c996e6a8c..e7bda6856 100644 --- a/src/region_map.c +++ b/src/region_map.c @@ -3564,7 +3564,6 @@ static void CreateFlyIcons(void) { if (GetMapsecType(GetSelectedMapSection(i, LAYER_MAP, y, x)) == MAPSECTYPE_VISITED) { - DebugPrintfLevel(MGBA_LOG_ERROR, "Creating fly icon for map %d, x %d, y %d", i, x, y); CreateFlyIconSprite(i, numIcons, x, y, numIcons + 10, 10); numIcons++; } diff --git a/src/reshow_battle_screen.c b/src/reshow_battle_screen.c index 2a0038a52..4852dd86e 100644 --- a/src/reshow_battle_screen.c +++ b/src/reshow_battle_screen.c @@ -316,6 +316,7 @@ void CreateBattlerSprite(u32 battler) if (battler < gBattlersCount) { u8 posY; + enum BattlerPosition position = GetBattlerPosition(battler); if (IsGhostBattleWithoutScope()) posY = GetGhostSpriteDefault_Y(battler); @@ -323,21 +324,27 @@ void CreateBattlerSprite(u32 battler) posY = GetSubstituteSpriteDefault_Y(battler); else posY = GetBattlerSpriteDefault_Y(battler); - if (GetBattlerSide(battler) != B_SIDE_PLAYER) + + if (!IsOnPlayerSide(battler)) { - if (GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_HP) == 0) + struct Pokemon *mon = GetBattlerMon(battler); + if (GetMonData(mon, MON_DATA_HP) == 0) return; - SetMultiuseSpriteTemplateToPokemon(GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES), GetBattlerPosition(battler)); + if (gBattleScripting.monCaught) // Don't create opponent sprite if it has been caught. + return; + u32 species = GetMonData(mon, MON_DATA_SPECIES); + + SetMultiuseSpriteTemplateToPokemon(species, position); gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, GetBattlerSpriteCoord(battler, BATTLER_COORD_X_2), posY, GetBattlerSpriteSubpriority(battler)); gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = battler; gSprites[gBattlerSpriteIds[battler]].callback = SpriteCallbackDummy; gSprites[gBattlerSpriteIds[battler]].data[0] = battler; - gSprites[gBattlerSpriteIds[battler]].data[2] = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES); + gSprites[gBattlerSpriteIds[battler]].data[2] = species; StartSpriteAnim(&gSprites[gBattlerSpriteIds[battler]], 0); } else if (gBattleTypeFlags & BATTLE_TYPE_SAFARI && battler == B_POSITION_PLAYER_LEFT) { - SetMultiuseSpriteTemplateToTrainerBack(gSaveBlock2Ptr->playerGender, GetBattlerPosition(B_POSITION_PLAYER_LEFT)); + SetMultiuseSpriteTemplateToTrainerBack(gSaveBlock2Ptr->playerGender, position); gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, 80, (8 - gTrainerBacksprites[gSaveBlock2Ptr->playerGender].coordinates.size) * 4 + 80, GetBattlerSpriteSubpriority(0)); diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c index 0b2beabd0..a5dac9dfd 100644 --- a/src/script_pokemon_util.c +++ b/src/script_pokemon_util.c @@ -155,7 +155,6 @@ static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, e { struct Pokemon mon; u32 i; - u16 targetSpecies; bool32 isShiny; u32 personality = GetMonPersonality(species, gender, nature, RANDOM_UNOWN_LETTER); @@ -238,9 +237,7 @@ static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, e SetMonData(&mon, MON_DATA_HELD_ITEM, &item); // In case a mon with a form changing item is given. Eg: SPECIES_ARCEUS_NORMAL with ITEM_SPLASH_PLATE will transform into SPECIES_ARCEUS_WATER upon gifted. - targetSpecies = GetFormChangeTargetSpecies(&mon, FORM_CHANGE_ITEM_HOLD, 0); - if (targetSpecies != SPECIES_NONE) - SetMonData(&mon, MON_DATA_SPECIES, &targetSpecies); + TryFormChange(&mon, FORM_CHANGE_ITEM_HOLD); if (side == B_SIDE_PLAYER) return GiveScriptedMonToPlayer(&mon, slot); diff --git a/src/trainer_slide.c b/src/trainer_slide.c index 2dd2188d1..2b11945c6 100644 --- a/src/trainer_slide.c +++ b/src/trainer_slide.c @@ -4,10 +4,11 @@ #include "battle_controllers.h" #include "battle_message.h" #include "battle_setup.h" +#include "battle_tower.h" #include "battle_z_move.h" #include "data.h" #include "event_data.h" -// #include "frontier_util.h" +#include "frontier_util.h" #include "graphics.h" #include "international_string_util.h" #include "item.h" @@ -24,10 +25,10 @@ #include "window.h" #include "line_break.h" #include "constants/abilities.h" -// #include "constants/battle_dome.h" +#include "constants/battle_dome.h" #include "constants/battle_string_ids.h" #include "constants/flags.h" -// #include "constants/frontier_util.h" +#include "constants/frontier_util.h" #include "constants/items.h" #include "constants/moves.h" #include "constants/opponents.h" @@ -39,20 +40,20 @@ #include "trainer_slide.h" #include "battle_message.h" -static u32 BattlerHPPercentage(u32 battler, u32 operation, u32 threshold); +static u32 BattlerHPPercentage(enum BattlerId battler, u32 operation, u32 threshold); static u32 GetPartyMonCount(u32 firstId, u32 lastId, enum BattleSide side, bool32 onlyAlive); static bool32 DoesTrainerHaveSlideMessage(enum DifficultyLevel difficulty, u32 trainerId, u32 slideId); -static bool32 ShouldRunTrainerSlidePlayerLandsFirstCriticalHit(u32 battler, enum TrainerSlideType slideId); -static bool32 ShouldRunTrainerSlideEnemyLandsFirstCriticalHit(u32 battler, enum TrainerSlideType slideId); -static bool32 ShouldRunTrainerSlidePlayerLandsFirstSuperEffectiveHit(u32 battler, enum TrainerSlideType slideId); -static bool32 ShouldRunTrainerSlidePlayerLandsFirstSTABMove(u32 firstId, u32 lastId, enum BattleSide side, u32 battler, enum TrainerSlideType slideId); +static bool32 ShouldRunTrainerSlidePlayerLandsFirstCriticalHit(enum BattlerId battler, enum TrainerSlideType slideId); +static bool32 ShouldRunTrainerSlideEnemyLandsFirstCriticalHit(enum BattlerId battler, enum TrainerSlideType slideId); +static bool32 ShouldRunTrainerSlidePlayerLandsFirstSuperEffectiveHit(enum BattlerId battler, enum TrainerSlideType slideId); +static bool32 ShouldRunTrainerSlidePlayerLandsFirstSTABMove(u32 firstId, u32 lastId, enum BattleSide side, enum BattlerId battler, enum TrainerSlideType slideId); static bool32 ShouldRunTrainerSlidePlayerLandsFirstDown(u32 firstId, u32 lastId, enum BattleSide side); -static bool32 ShouldRunTrainerSlideEnemyMonUnaffected(u32 firstId, u32 lastId, enum BattleSide side, u32 battler, enum TrainerSlideType slideId); -static bool32 ShouldRunTrainerSlideLastSwitchIn(u32 battler); -static bool32 ShouldRunTrainerSlideLastHalfHP(u32 firstId, u32 lastId, enum BattleSide side, u32 battler); -static bool32 ShouldRunTrainerSlideLastLowHp(u32 firstId, u32 lastId, enum BattleSide side, u32 battler); -static void SetTrainerSlideParameters(u32 battler, u32* firstId, u32* lastId, u32* trainerId, u32* retValue); -static bool32 IsSlideInitalizedOrPlayed(u32 battler, enum TrainerSlideType slideId); +static bool32 ShouldRunTrainerSlideEnemyMonUnaffected(u32 firstId, u32 lastId, enum BattleSide side, enum BattlerId battler, enum TrainerSlideType slideId); +static bool32 ShouldRunTrainerSlideLastSwitchIn(enum BattlerId battler); +static bool32 ShouldRunTrainerSlideLastHalfHP(u32 firstId, u32 lastId, enum BattleSide side, enum BattlerId battler); +static bool32 ShouldRunTrainerSlideLastLowHp(u32 firstId, u32 lastId, enum BattleSide side, enum BattlerId battler); +static void SetTrainerSlideParameters(enum BattlerId battler, u32* firstId, u32* lastId, u32* trainerId, u32* retValue); +static bool32 IsSlideInitalizedOrPlayed(enum BattlerId battler, enum TrainerSlideType slideId); // Partner trainers must be added as TRAINER_PARTNER(PARTNER_XXXX) static const u8* const sTrainerSlides[DIFFICULTY_COUNT][TRAINER_PARTNER(PARTNER_COUNT)][TRAINER_SLIDE_COUNT] = @@ -62,7 +63,6 @@ static const u8* const sTrainerSlides[DIFFICULTY_COUNT][TRAINER_PARTNER(PARTNER_ }, }; -#define FRONTIER_TRAINERS_COUNT 300 static const u8* const sFrontierTrainerSlides[DIFFICULTY_COUNT][FRONTIER_TRAINERS_COUNT][TRAINER_SLIDE_COUNT] = { [DIFFICULTY_NORMAL] = @@ -75,7 +75,7 @@ static const u8* const sTestTrainerSlides[DIFFICULTY_COUNT][MAX_TRAINERS_COUNT + #include "../test/battle/trainer_slides.h" }; -static u32 BattlerHPPercentage(u32 battler, u32 operation, u32 threshold) +static u32 BattlerHPPercentage(enum BattlerId battler, u32 operation, u32 threshold) { switch (operation) { @@ -180,17 +180,17 @@ void SetTrainerSlideMessage(enum DifficultyLevel difficulty, u32 trainerId, u32 gBattleStruct->trainerSlideMsg = trainerSlidesNormal[slideId]; } -static bool32 ShouldRunTrainerSlidePlayerLandsFirstCriticalHit(u32 battler, enum TrainerSlideType slideId) +static bool32 ShouldRunTrainerSlidePlayerLandsFirstCriticalHit(enum BattlerId battler, enum TrainerSlideType slideId) { return IsTrainerSlideInitialized(battler, slideId); } -static bool32 ShouldRunTrainerSlideEnemyLandsFirstCriticalHit(u32 battler, enum TrainerSlideType slideId) +static bool32 ShouldRunTrainerSlideEnemyLandsFirstCriticalHit(enum BattlerId battler, enum TrainerSlideType slideId) { return IsTrainerSlideInitialized(battler, slideId); } -static bool32 ShouldRunTrainerSlidePlayerLandsFirstSuperEffectiveHit(u32 battler, enum TrainerSlideType slideId) +static bool32 ShouldRunTrainerSlidePlayerLandsFirstSuperEffectiveHit(enum BattlerId battler, enum TrainerSlideType slideId) { if (!IsTrainerSlideInitialized(battler, slideId)) return FALSE; @@ -201,7 +201,7 @@ static bool32 ShouldRunTrainerSlidePlayerLandsFirstSuperEffectiveHit(u32 battler return TRUE; } -static bool32 ShouldRunTrainerSlidePlayerLandsFirstSTABMove(u32 firstId, u32 lastId, enum BattleSide side, u32 battler, enum TrainerSlideType slideId) +static bool32 ShouldRunTrainerSlidePlayerLandsFirstSTABMove(u32 firstId, u32 lastId, enum BattleSide side, enum BattlerId battler, enum TrainerSlideType slideId) { if (!IsTrainerSlideInitialized(battler, slideId)) return FALSE; @@ -217,7 +217,7 @@ static bool32 ShouldRunTrainerSlidePlayerLandsFirstDown(u32 firstId, u32 lastId, return ((GetPartyMonCount(firstId, lastId, side, TRUE) == (GetPartyMonCount(firstId, lastId, side, FALSE) - 1))); } -static bool32 ShouldRunTrainerSlideEnemyMonUnaffected(u32 firstId, u32 lastId, enum BattleSide side, u32 battler, enum TrainerSlideType slideId) +static bool32 ShouldRunTrainerSlideEnemyMonUnaffected(u32 firstId, u32 lastId, enum BattleSide side, enum BattlerId battler, enum TrainerSlideType slideId) { if (!IsTrainerSlideInitialized(battler, slideId)) return FALSE; @@ -225,12 +225,12 @@ static bool32 ShouldRunTrainerSlideEnemyMonUnaffected(u32 firstId, u32 lastId, e return (GetPartyMonCount(firstId, lastId, side, TRUE) == GetPartyMonCount(firstId, lastId, side, FALSE)); } -static bool32 ShouldRunTrainerSlideLastSwitchIn(u32 battler) +static bool32 ShouldRunTrainerSlideLastSwitchIn(enum BattlerId battler) { return !CanBattlerSwitch(battler); } -static bool32 ShouldRunTrainerSlideLastHalfHP(u32 firstId, u32 lastId, enum BattleSide side, u32 battler) +static bool32 ShouldRunTrainerSlideLastHalfHP(u32 firstId, u32 lastId, enum BattleSide side, enum BattlerId battler) { if (GetPartyMonCount(firstId, lastId, side, TRUE) != 1) return FALSE; @@ -241,7 +241,7 @@ static bool32 ShouldRunTrainerSlideLastHalfHP(u32 firstId, u32 lastId, enum Batt return (BattlerHPPercentage(battler, GREATER_THAN, 4)); } -static bool32 ShouldRunTrainerSlideLastLowHp(u32 firstId, u32 lastId, enum BattleSide side, u32 battler) +static bool32 ShouldRunTrainerSlideLastLowHp(u32 firstId, u32 lastId, enum BattleSide side, enum BattlerId battler) { if (GetPartyMonCount(firstId, lastId, side, TRUE) != 1) return FALSE; @@ -252,7 +252,7 @@ static bool32 ShouldRunTrainerSlideLastLowHp(u32 firstId, u32 lastId, enum Battl return (BattlerHPPercentage(battler, LESS_THAN_OR_EQUAL, 4)); } -static void SetTrainerSlideParameters(u32 battler, u32* firstId, u32* lastId, u32* trainerId, u32* retValue) +static void SetTrainerSlideParameters(enum BattlerId battler, u32* firstId, u32* lastId, u32* trainerId, u32* retValue) { if ((battler & BIT_SIDE) == B_SIDE_OPPONENT) { @@ -288,7 +288,7 @@ static void SetTrainerSlideParameters(u32 battler, u32* firstId, u32* lastId, u3 } } -enum TrainerSlideTargets ShouldDoTrainerSlide(u32 battler, enum TrainerSlideType slideId) +enum TrainerSlideTargets ShouldDoTrainerSlide(enum BattlerId battler, enum TrainerSlideType slideId) { u32 firstId = 0, lastId = PARTY_SIZE, trainerId = 0; enum BattleSide side = GetBattlerSide(battler); @@ -367,7 +367,7 @@ enum TrainerSlideTargets ShouldDoTrainerSlide(u32 battler, enum TrainerSlideType return retValue; } -static bool32 IsSlideInitalizedOrPlayed(u32 battler, enum TrainerSlideType slideId) +static bool32 IsSlideInitalizedOrPlayed(enum BattlerId battler, enum TrainerSlideType slideId) { if (IsTrainerSlideInitialized(battler, slideId)) return TRUE; @@ -378,7 +378,7 @@ static bool32 IsSlideInitalizedOrPlayed(u32 battler, enum TrainerSlideType slide return FALSE; } -void TryInitializeFirstSTABMoveTrainerSlide(u32 battlerDef, u32 battlerAtk, enum Type moveType) +void TryInitializeFirstSTABMoveTrainerSlide(enum BattlerId battlerDef, enum BattlerId battlerAtk, enum Type moveType) { enum TrainerSlideType slideId = TRAINER_SLIDE_PLAYER_LANDS_FIRST_STAB_MOVE; @@ -446,7 +446,7 @@ void TryInitializeTrainerSlideEnemyMonUnaffected(u32 target) InitalizeTrainerSlide(target, slideId); } -bool32 IsTrainerSlideInitialized(u32 battler, enum TrainerSlideType slideId) +bool32 IsTrainerSlideInitialized(enum BattlerId battler, enum TrainerSlideType slideId) { u32 arrayIndex = slideId / TRAINER_SLIDES_PER_ARRAY; u32 bitPosition = slideId % TRAINER_SLIDES_PER_ARRAY; @@ -454,7 +454,7 @@ bool32 IsTrainerSlideInitialized(u32 battler, enum TrainerSlideType slideId) return (gBattleStruct->slideMessageStatus.messageInitalized[battler][arrayIndex] & (1 << bitPosition)) != 0; } -bool32 IsTrainerSlidePlayed(u32 battler, enum TrainerSlideType slideId) +bool32 IsTrainerSlidePlayed(enum BattlerId battler, enum TrainerSlideType slideId) { u32 arrayIndex = slideId / TRAINER_SLIDES_PER_ARRAY; u32 bitPosition = slideId % TRAINER_SLIDES_PER_ARRAY; @@ -462,7 +462,7 @@ bool32 IsTrainerSlidePlayed(u32 battler, enum TrainerSlideType slideId) return (gBattleStruct->slideMessageStatus.messagePlayed[battler][arrayIndex] & (1 << bitPosition)) != 0; } -void InitalizeTrainerSlide(u32 battler, enum TrainerSlideType slideId) +void InitalizeTrainerSlide(enum BattlerId battler, enum TrainerSlideType slideId) { u32 arrayIndex = slideId / TRAINER_SLIDES_PER_ARRAY; u32 bitPosition = slideId % TRAINER_SLIDES_PER_ARRAY; @@ -470,7 +470,7 @@ void InitalizeTrainerSlide(u32 battler, enum TrainerSlideType slideId) gBattleStruct->slideMessageStatus.messageInitalized[battler][arrayIndex] |= (1 << bitPosition); } -void MarkInitializedTrainerSlidesAsPlayed(u32 battler, enum TrainerSlideType slideId) +void MarkInitializedTrainerSlidesAsPlayed(enum BattlerId battler, enum TrainerSlideType slideId) { u32 arrayIndex = slideId / TRAINER_SLIDES_PER_ARRAY; u32 bitPosition = slideId % TRAINER_SLIDES_PER_ARRAY; @@ -479,7 +479,7 @@ void MarkInitializedTrainerSlidesAsPlayed(u32 battler, enum TrainerSlideType sli gBattleStruct->slideMessageStatus.messagePlayed[battler][arrayIndex] |= (1 << bitPosition); } -void MarkTrainerSlideAsPlayed(u32 battler, enum TrainerSlideType slideId) +void MarkTrainerSlideAsPlayed(enum BattlerId battler, enum TrainerSlideType slideId) { u32 arrayIndex = slideId / TRAINER_SLIDES_PER_ARRAY; u32 bitPosition = slideId % TRAINER_SLIDES_PER_ARRAY; diff --git a/src/trainer_tower.c b/src/trainer_tower.c index 4d48515fd..afa802de5 100644 --- a/src/trainer_tower.c +++ b/src/trainer_tower.c @@ -748,7 +748,7 @@ static void DoTrainerTowerBattle(void) BuildEnemyParty(); CreateTask(Task_DoTrainerTowerBattle, 1); PlayMapChosenOrBattleBGM(0); - BattleTransition_StartOnField(BattleSetup_GetBattleTowerBattleTransition()); + BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_TRAINER_TOWER)); } static void TrainerTowerGetChallengeType(void) diff --git a/src/type_icons.c b/src/type_icons.c index c47690fca..3df968fd7 100644 --- a/src/type_icons.c +++ b/src/type_icons.c @@ -10,27 +10,27 @@ #include "type_icons.h" static void LoadTypeSpritesAndPalettes(void); -static void LoadTypeIconsPerBattler(u32, u32); +static void LoadTypeIconsPerBattler(enum BattlerId, u32); static bool32 UseDoubleBattleCoords(u32); -static u32 GetMonPublicType(u32, u32); +static enum Type GetMonPublicType(enum BattlerId, u32); static bool32 ShouldHideUncaughtType(u32 species); static bool32 ShouldHideUnseenType(u32 species); -static u32 GetMonDefensiveTeraType(struct Pokemon *, struct Pokemon*, u32, u32, u32, u32); -static u32 IsIllusionActiveAndTypeUnchanged(struct Pokemon*, u32, u32); +static enum Type GetMonDefensiveTeraType(struct Pokemon *, struct Pokemon *, enum BattlerId, u32, u32, u32); +static bool32 IsIllusionActiveAndTypeUnchanged(struct Pokemon *, u32, enum BattlerId); -static void CreateSpriteFromType(u32, bool32, u32[], u32, u32); -static bool32 ShouldSkipSecondType(u32[], u32); +static void CreateSpriteFromType(u32, bool32, enum Type[], u32, enum BattlerId); +static bool32 ShouldSkipSecondType(enum Type[], u32); static void SetTypeIconXY(s32*, s32*, u32, bool32, u32); -static void CreateSpriteAndSetTypeSpriteAttributes(u32, u32 x, u32 y, u32, u32, bool32); -static bool32 ShouldFlipTypeIcon(bool32, u32, u32); +static void CreateSpriteAndSetTypeSpriteAttributes(enum Type, u32 x, u32 y, u32, enum BattlerId, bool32); +static bool32 ShouldFlipTypeIcon(bool32, u32, enum Type); static void SpriteCB_TypeIcon(struct Sprite*); static void DestroyTypeIcon(struct Sprite*); static void FreeAllTypeIconResources(void); -static bool32 ShouldHideTypeIcon(u32); +static bool32 ShouldHideTypeIcon(enum BattlerId); static s32 GetTypeIconHideMovement(bool32, u32); static s32 GetTypeIconSlideMovement(bool32, u32, s32); static s32 GetTypeIconBounceMovement(s32, u32); @@ -220,8 +220,6 @@ const struct SpriteTemplate sSpriteTemplate_TypeIcons1 = .paletteTag = TYPE_ICON_TAG, .oam = &sOamData_TypeIcons, .anims = sSpriteAnimTable_TypeIcons, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCB_TypeIcon }; @@ -231,19 +229,17 @@ const struct SpriteTemplate sSpriteTemplate_TypeIcons2 = .paletteTag = TYPE_ICON_TAG_2, .oam = &sOamData_TypeIcons, .anims = sSpriteAnimTable_TypeIcons, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCB_TypeIcon }; -void LoadTypeIcons(u32 battler) +void LoadTypeIcons(enum BattlerId battler) { u32 position; struct Pokemon* mon = GetBattlerMon(battler); - u32 species = GetMonData(mon, MON_DATA_SPECIES, NULL); + u32 species = GetMonData(mon, MON_DATA_SPECIES); - if (B_SHOW_TYPES == SHOW_TYPES_NEVER + if (B_SHOW_TYPES == SHOW_TYPES_NEVER || (B_SHOW_TYPES == SHOW_TYPES_SEEN && !GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_SEEN))) return; @@ -264,10 +260,11 @@ static void LoadTypeSpritesAndPalettes(void) LoadSpritePalette(&sTypeIconPal2); } -static void LoadTypeIconsPerBattler(u32 battler, u32 position) +static void LoadTypeIconsPerBattler(enum BattlerId battler, u32 position) { - u32 typeNum, types[2]; - u32 battlerId = GetBattlerAtPosition(position); + u32 typeNum; + enum Type types[2]; + enum BattlerId battlerId = GetBattlerAtPosition(position); bool32 useDoubleBattleCoords = UseDoubleBattleCoords(battlerId); if (!IsBattlerAlive(battlerId)) @@ -294,11 +291,11 @@ static bool32 UseDoubleBattleCoords(u32 position) return TRUE; } -static u32 GetMonPublicType(u32 battlerId, u32 typeNum) +static enum Type GetMonPublicType(enum BattlerId battlerId, u32 typeNum) { - struct Pokemon* mon = GetBattlerMon(battlerId); + struct Pokemon *mon = GetBattlerMon(battlerId); u32 monSpecies = GetMonData(mon,MON_DATA_SPECIES,NULL); - struct Pokemon* monIllusion; + struct Pokemon *monIllusion; u32 illusionSpecies; if (ShouldHideUncaughtType(monSpecies) || ShouldHideUnseenType(monSpecies)) @@ -311,7 +308,7 @@ static u32 GetMonPublicType(u32 battlerId, u32 typeNum) return GetMonDefensiveTeraType(mon,monIllusion,battlerId,typeNum,illusionSpecies,monSpecies); if (IsIllusionActiveAndTypeUnchanged(monIllusion,monSpecies, battlerId)) - return gSpeciesInfo[illusionSpecies].types[typeNum]; + return GetSpeciesType(illusionSpecies, typeNum); return gBattleMons[battlerId].types[typeNum]; } @@ -338,9 +335,9 @@ static bool32 ShouldHideUnseenType(u32 species) return TRUE; } -static u32 GetMonDefensiveTeraType(struct Pokemon * mon, struct Pokemon* monIllusion, u32 battlerId, u32 typeNum, u32 illusionSpecies, u32 monSpecies) +static enum Type GetMonDefensiveTeraType(struct Pokemon *mon, struct Pokemon *monIllusion, enum BattlerId battlerId, u32 typeNum, u32 illusionSpecies, u32 monSpecies) { - u32 teraType = GetBattlerTeraType(battlerId); + enum Type teraType = GetBattlerTeraType(battlerId); u32 targetSpecies; if (teraType != TYPE_STELLAR) @@ -348,10 +345,10 @@ static u32 GetMonDefensiveTeraType(struct Pokemon * mon, struct Pokemon* monIllu targetSpecies = (monIllusion != NULL) ? illusionSpecies : monSpecies; - return gSpeciesInfo[targetSpecies].types[typeNum]; + return GetSpeciesType(targetSpecies, typeNum); } -static u32 IsIllusionActiveAndTypeUnchanged(struct Pokemon* monIllusion, u32 monSpecies, u32 battlerId) +static bool32 IsIllusionActiveAndTypeUnchanged(struct Pokemon *monIllusion, u32 monSpecies, enum BattlerId battlerId) { u32 typeNum; @@ -359,13 +356,13 @@ static u32 IsIllusionActiveAndTypeUnchanged(struct Pokemon* monIllusion, u32 mon return FALSE; for (typeNum = 0; typeNum < 2; typeNum++) - if (gSpeciesInfo[monSpecies].types[typeNum] != gBattleMons[battlerId].types[typeNum]) + if (GetSpeciesType(monSpecies, typeNum) != gBattleMons[battlerId].types[typeNum]) return FALSE; return TRUE; } -static void CreateSpriteFromType(u32 position, bool32 useDoubleBattleCoords, u32 types[], u32 typeNum, u32 battler) +static void CreateSpriteFromType(u32 position, bool32 useDoubleBattleCoords, enum Type types[], u32 typeNum, enum BattlerId battler) { s32 x = 0, y = 0; @@ -377,7 +374,7 @@ static void CreateSpriteFromType(u32 position, bool32 useDoubleBattleCoords, u32 CreateSpriteAndSetTypeSpriteAttributes(types[typeNum], x, y, position, battler, useDoubleBattleCoords); } -static bool32 ShouldSkipSecondType(u32 types[], u32 typeNum) +static bool32 ShouldSkipSecondType(enum Type types[], u32 typeNum) { if (!typeNum) return FALSE; @@ -394,7 +391,7 @@ static void SetTypeIconXY(s32* x, s32* y, u32 position, bool32 useDoubleBattleCo *y = sTypeIconPositions[position][useDoubleBattleCoords].y + (11 * typeNum); } -static void CreateSpriteAndSetTypeSpriteAttributes(u32 type, u32 x, u32 y, u32 position, u32 battler, bool32 useDoubleBattleCoords) +static void CreateSpriteAndSetTypeSpriteAttributes(enum Type type, u32 x, u32 y, u32 position, enum BattlerId battler, bool32 useDoubleBattleCoords) { struct Sprite* sprite; const struct SpriteTemplate* spriteTemplate = gTypesInfo[type].useSecondTypeIconPalette ? &sSpriteTemplate_TypeIcons2 : &sSpriteTemplate_TypeIcons1; @@ -413,9 +410,9 @@ static void CreateSpriteAndSetTypeSpriteAttributes(u32 type, u32 x, u32 y, u32 p StartSpriteAnim(sprite, type); } -static bool32 ShouldFlipTypeIcon(bool32 useDoubleBattleCoords, u32 position, u32 typeId) +static bool32 ShouldFlipTypeIcon(bool32 useDoubleBattleCoords, u32 position, enum Type typeId) { - bool32 side = (useDoubleBattleCoords) ? B_SIDE_OPPONENT : B_SIDE_PLAYER; + enum BattleSide side = (useDoubleBattleCoords) ? B_SIDE_OPPONENT : B_SIDE_PLAYER; if (GetBattlerSide(GetBattlerAtPosition(position)) != side) return FALSE; @@ -423,10 +420,10 @@ static bool32 ShouldFlipTypeIcon(bool32 useDoubleBattleCoords, u32 position, u32 return !gTypesInfo[typeId].isSpecialCaseType; } -static void SpriteCB_TypeIcon(struct Sprite* sprite) +static void SpriteCB_TypeIcon(struct Sprite *sprite) { u32 position = sprite->tMonPosition; - u32 battlerId = sprite->tBattlerId; + enum BattlerId battlerId = sprite->tBattlerId; bool32 useDoubleBattleCoords = UseDoubleBattleCoords(GetBattlerAtPosition(position)); if (sprite->tHideIconTimer == NUM_FRAMES_HIDE_TYPE_ICON) @@ -487,7 +484,7 @@ static void FreeAllTypeIconResources(void) } } -static void (* const sShowTypesControllerFuncs[])(u32 battler) = +static void (*const sShowTypesControllerFuncs[])(enum BattlerId battler) = { PlayerHandleChooseMove, HandleChooseMoveAfterDma3, @@ -499,7 +496,7 @@ static void (* const sShowTypesControllerFuncs[])(u32 battler) = }; -static bool32 ShouldHideTypeIcon(u32 battlerId) +static bool32 ShouldHideTypeIcon(enum BattlerId battlerId) { u32 funcIndex; @@ -562,7 +559,6 @@ static s32 GetTypeIconSlideMovement(bool32 useDoubleBattleCoords, u32 position, static s32 GetTypeIconBounceMovement(s32 originalY, u32 position) { - struct Sprite* healthbox = &gSprites[gHealthboxSpriteIds[GetBattlerAtPosition(position)]]; + struct Sprite *healthbox = &gSprites[gHealthboxSpriteIds[GetBattlerAtPosition(position)]]; return originalY + healthbox->y2; } - diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 8506056f4..77a6be8c5 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -880,6 +880,8 @@ bool8 UpdateRepelCounter(void) u16 steps = REPEL_LURE_STEPS(repelLureVar); bool32 isLure = IS_LAST_USED_LURE(repelLureVar); + if (InBattlePike() || CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) + return FALSE; if (InUnionRoom() == TRUE) return FALSE; diff --git a/test/battle/ability/aerilate.c b/test/battle/ability/aerilate.c index d9eede03c..c34c3e3a7 100644 --- a/test/battle/ability/aerilate.c +++ b/test/battle/ability/aerilate.c @@ -162,22 +162,124 @@ SINGLE_BATTLE_TEST("Aerilate doesn't affect Hidden Power's type") ASSUME(GetMoveEffect(MOVE_HIDDEN_POWER) == EFFECT_HIDDEN_POWER); ASSUME(gTypesInfo[TYPE_ELECTRIC].isHiddenPowerType == TRUE); ASSUME(GetSpeciesType(SPECIES_DIGLETT, 0) == TYPE_GROUND); - PLAYER(SPECIES_PINSIR) { Ability(ABILITY_AERILATE); HPIV(31); AttackIV(31); DefenseIV(31); SpAttackIV(30); SpDefenseIV(31); SpeedIV(31); } // HP Electric + PLAYER(SPECIES_PINSIR) { Item(ITEM_PINSIRITE); HPIV(31); AttackIV(31); DefenseIV(31); SpAttackIV(30); SpDefenseIV(31); SpeedIV(31); } // HP Electric OPPONENT(SPECIES_DIGLETT); } WHEN { - TURN { MOVE(player, MOVE_HIDDEN_POWER); } + TURN { MOVE(player, MOVE_HIDDEN_POWER, gimmick: GIMMICK_MEGA); } } SCENE { NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_HIDDEN_POWER, player); } MESSAGE("It doesn't affect the opposing Diglett…"); } } -TO_DO_BATTLE_TEST("Aerilate doesn't override Electrify (Gen7+)"); // No mon with Aerilate exists in Gen8+, but probably behaves similar to Pixilate, which does. -TO_DO_BATTLE_TEST("Aerilate doesn't override Ion Deluge (Gen7+)"); // Ion Deluge doesn't exist in Gen 8+, but we probably could assume it behaves similar to under Electrify. TODO: Test by hacking SV. -TO_DO_BATTLE_TEST("Aerilate overrides Electrify (Gen6)") -TO_DO_BATTLE_TEST("Aerilate overrides Ion Deluge (Gen6)") -TO_DO_BATTLE_TEST("Aerilate doesn't affect Tera Starstorm's type"); +SINGLE_BATTLE_TEST("Aerilate doesn't override Electrify") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_ELECTRIFY) == EFFECT_ELECTRIFY); + ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND); + PLAYER(SPECIES_PINSIR) { Item(ITEM_PINSIRITE); Speed(1); } + OPPONENT(SPECIES_SANDSHREW) { Moves(MOVE_ELECTRIFY); Speed(10); } + } WHEN { + TURN { MOVE(opponent, MOVE_ELECTRIFY); MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_MEGA); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIFY, opponent); + NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); } + MESSAGE("It doesn't affect the opposing Sandshrew…"); + } +} + +SINGLE_BATTLE_TEST("Aerilate overrides Ion Deluge") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_ION_DELUGE) == EFFECT_ION_DELUGE); + ASSUME(GetSpeciesType(SPECIES_MACHOP, 0) == TYPE_FIGHTING || GetSpeciesType(SPECIES_MACHOP, 1) == TYPE_FIGHTING); + PLAYER(SPECIES_PINSIR) { Item(ITEM_PINSIRITE); Speed(1); } + OPPONENT(SPECIES_MACHOP) { Moves(MOVE_ION_DELUGE); Speed(10); } + } WHEN { + TURN { MOVE(opponent, MOVE_ION_DELUGE); MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_MEGA); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ION_DELUGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + MESSAGE("It's super effective!"); + } +} + +SINGLE_BATTLE_TEST("Aerilate changes Tera Blast's type when not Terastallized") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_TERA_BLAST) == EFFECT_TERA_BLAST); + ASSUME(GetMoveType(MOVE_TERA_BLAST) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_MACHOP, 0) == TYPE_FIGHTING || GetSpeciesType(SPECIES_MACHOP, 1) == TYPE_FIGHTING); + ASSUME(GetMoveEffect(MOVE_SKILL_SWAP) == EFFECT_SKILL_SWAP); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_SALAMENCE) { Item(ITEM_SALAMENCITE); Moves(MOVE_SKILL_SWAP); } + OPPONENT(SPECIES_MACHOP); + } WHEN { + TURN { MOVE(opponent, MOVE_SKILL_SWAP, gimmick: GIMMICK_MEGA, target: player); } + TURN { SWITCH(opponent, 1); MOVE(player, MOVE_TERA_BLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + MESSAGE("It's super effective!"); + } +} + +SINGLE_BATTLE_TEST("Aerilate doesn't change Tera Blast's type when Terastallized") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_TERA_BLAST) == EFFECT_TERA_BLAST); + ASSUME(GetMoveType(MOVE_TERA_BLAST) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_MISDREAVUS, 0) == TYPE_GHOST); + ASSUME(GetMoveEffect(MOVE_SKILL_SWAP) == EFFECT_SKILL_SWAP); + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_SALAMENCE) { Item(ITEM_SALAMENCITE); Moves(MOVE_SKILL_SWAP); } + OPPONENT(SPECIES_MISDREAVUS); + } WHEN { + TURN { MOVE(opponent, MOVE_SKILL_SWAP, gimmick: GIMMICK_MEGA, target: player); } + TURN { SWITCH(opponent, 1); MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); } + } SCENE { + NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); } + MESSAGE("It doesn't affect the opposing Misdreavus…"); + } +} + +SINGLE_BATTLE_TEST("Aerilate doesn't affect Terrain Pulse's type") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_TERRAIN_PULSE) == EFFECT_TERRAIN_PULSE); + ASSUME(GetMoveType(MOVE_TERRAIN_PULSE) == TYPE_NORMAL); + ASSUME(GetMoveEffect(MOVE_SKILL_SWAP) == EFFECT_SKILL_SWAP); + ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_SALAMENCE) { Item(ITEM_SALAMENCITE); Moves(MOVE_SKILL_SWAP); } + OPPONENT(SPECIES_SANDSHREW); + } WHEN { + TURN { MOVE(opponent, MOVE_SKILL_SWAP, gimmick: GIMMICK_MEGA, target: player); MOVE(player, MOVE_ELECTRIC_TERRAIN); } + TURN { SWITCH(opponent, 1); MOVE(player, MOVE_TERRAIN_PULSE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIC_TERRAIN, player); + NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_TERRAIN_PULSE, player); } + MESSAGE("It doesn't affect the opposing Sandshrew…"); + } +} + +SINGLE_BATTLE_TEST("Aerilate doesn't affect damaging Z-Move types") +{ + GIVEN { + ASSUME(GetMoveType(MOVE_SCRATCH) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_MACHOP, 0) == TYPE_FIGHTING || GetSpeciesType(SPECIES_MACHOP, 1) == TYPE_FIGHTING); + ASSUME(GetMoveEffect(MOVE_SKILL_SWAP) == EFFECT_SKILL_SWAP); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); } + OPPONENT(SPECIES_SALAMENCE) { Item(ITEM_SALAMENCITE); Moves(MOVE_SKILL_SWAP); } + OPPONENT(SPECIES_MACHOP); + } WHEN { + TURN { MOVE(opponent, MOVE_SKILL_SWAP, gimmick: GIMMICK_MEGA, target: player); } + TURN { SWITCH(opponent, 1); MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_Z_MOVE); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, player); + NOT { MESSAGE("It's super effective!"); } + } +} + TO_DO_BATTLE_TEST("Aerilate doesn't affect Max Strike's type"); -TO_DO_BATTLE_TEST("Aerilate doesn't affect Terrain Pulse's type"); -TO_DO_BATTLE_TEST("Aerilate doesn't affect damaging Z-Move types"); TO_DO_BATTLE_TEST("(DYNAMAX) Aerilate turns Max Strike into Max Airstream"); // All other -ate abilities do this, so interpolating this as no Aerilate mon is available in a Dynamax game diff --git a/test/battle/ability/anger_shell.c b/test/battle/ability/anger_shell.c index d55d003f0..350860b7a 100644 --- a/test/battle/ability/anger_shell.c +++ b/test/battle/ability/anger_shell.c @@ -14,7 +14,7 @@ SINGLE_BATTLE_TEST("Anger Shell activates only if the target had more than 50% o PARAMETRIZE { hp = 254; activates = TRUE; } GIVEN { - ASSUME(!IsBattleMoveStatus(MOVE_SCRATCH)); + ASSUME(GetMoveCategory(MOVE_SCRATCH) != DAMAGE_CATEGORY_STATUS); PLAYER(SPECIES_KLAWF) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(hp); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -41,7 +41,7 @@ SINGLE_BATTLE_TEST("Anger Shell lowers Def/Sp.Def by 1 and raises Atk/Sp.Atk/Spd { u16 maxHp = 500; GIVEN { - ASSUME(!IsBattleMoveStatus(MOVE_SCRATCH)); + ASSUME(GetMoveCategory(MOVE_SCRATCH) != DAMAGE_CATEGORY_STATUS); PLAYER(SPECIES_KLAWF) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(maxHp / 2 + 1); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { diff --git a/test/battle/ability/arena_trap.c b/test/battle/ability/arena_trap.c index 67384fa68..79ed93c8e 100644 --- a/test/battle/ability/arena_trap.c +++ b/test/battle/ability/arena_trap.c @@ -9,8 +9,8 @@ SINGLE_BATTLE_TEST("Arena Trap prevents grounded adjacent opponents from switchi } WHEN { TURN { MOVE(player, MOVE_CELEBRATE); } } THEN { - u32 battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); - u32 trapper = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + enum BattlerId battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + enum BattlerId trapper = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); EXPECT_EQ(IsAbilityPreventingEscape(battler), trapper + 1); } } @@ -86,8 +86,8 @@ WILD_BATTLE_TEST("Arena Trap prevents switching but Run Away allows fleeing") } WHEN { TURN { MOVE(player, MOVE_CELEBRATE); } } THEN { - u32 battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); - u32 trapper = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + enum BattlerId battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + enum BattlerId trapper = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); EXPECT_EQ(IsAbilityPreventingEscape(battler), trapper + 1); EXPECT_EQ(IsRunningFromBattleImpossible(battler), BATTLE_RUN_SUCCESS); } @@ -102,8 +102,8 @@ WILD_BATTLE_TEST("Arena Trap prevents switching but Smoke Ball allows fleeing") } WHEN { TURN { MOVE(player, MOVE_CELEBRATE); } } THEN { - u32 battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); - u32 trapper = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + enum BattlerId battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + enum BattlerId trapper = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); EXPECT_EQ(IsAbilityPreventingEscape(battler), trapper + 1); EXPECT_EQ(IsRunningFromBattleImpossible(battler), BATTLE_RUN_SUCCESS); } @@ -119,8 +119,8 @@ SINGLE_BATTLE_TEST("Arena Trap prevents switch outs from Ghost-type Pokémon (Ge } WHEN { TURN { MOVE(player, MOVE_CELEBRATE); } } THEN { - u32 battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); - u32 trapper = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + enum BattlerId battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + enum BattlerId trapper = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); EXPECT_EQ(IsAbilityPreventingEscape(battler), trapper + 1); } } @@ -135,7 +135,7 @@ SINGLE_BATTLE_TEST("Arena Trap doesn't prevent switch outs from Ghost-type Poké } WHEN { TURN { MOVE(player, MOVE_CELEBRATE); } } THEN { - u32 battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + enum BattlerId battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); EXPECT_EQ(IsAbilityPreventingEscape(battler), 0); } } diff --git a/test/battle/ability/battle_bond.c b/test/battle/ability/battle_bond.c index 9d8bb1b3c..162958599 100644 --- a/test/battle/ability/battle_bond.c +++ b/test/battle/ability/battle_bond.c @@ -3,7 +3,7 @@ ASSUMPTIONS { - ASSUME(!IsBattleMoveStatus(MOVE_WATER_GUN)); + ASSUME(GetMoveCategory(MOVE_WATER_GUN) != DAMAGE_CATEGORY_STATUS); } // Battle Bond transforms the Pokémon when fainting any battler(opposing or partner), unless it's the last Pokémon and the battle ends. diff --git a/test/battle/ability/berserk.c b/test/battle/ability/berserk.c index 8b9b452a9..277448b09 100644 --- a/test/battle/ability/berserk.c +++ b/test/battle/ability/berserk.c @@ -14,7 +14,7 @@ SINGLE_BATTLE_TEST("Berserk activates only if the target had more than 50% of it PARAMETRIZE { hp = 254; activates = TRUE; } GIVEN { - ASSUME(!IsBattleMoveStatus(MOVE_SCRATCH)); + ASSUME(GetMoveCategory(MOVE_SCRATCH) != DAMAGE_CATEGORY_STATUS); PLAYER(SPECIES_DRAMPA) { Ability(ABILITY_BERSERK); MaxHP(maxHp); HP(hp); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -37,7 +37,7 @@ SINGLE_BATTLE_TEST("Berserk raises Sp.Atk by 1") { u16 maxHp = 500; GIVEN { - ASSUME(!IsBattleMoveStatus(MOVE_SCRATCH)); + ASSUME(GetMoveCategory(MOVE_SCRATCH) != DAMAGE_CATEGORY_STATUS); PLAYER(SPECIES_DRAMPA) { Ability(ABILITY_BERSERK); MaxHP(maxHp); HP(maxHp / 2 + 1); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -121,7 +121,7 @@ SINGLE_BATTLE_TEST("Berserk activates before the hp can be restored on non multi { u16 maxHp = 500; GIVEN { - ASSUME(!IsBattleMoveStatus(MOVE_SCRATCH)); + ASSUME(GetMoveCategory(MOVE_SCRATCH) != DAMAGE_CATEGORY_STATUS); PLAYER(SPECIES_DRAMPA) { Ability(ABILITY_BERSERK); Item(ITEM_SITRUS_BERRY); MaxHP(maxHp); HP(maxHp / 2 + 1); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { diff --git a/test/battle/ability/cheek_pouch.c b/test/battle/ability/cheek_pouch.c index 0e52ca3fe..8109dbe17 100644 --- a/test/battle/ability/cheek_pouch.c +++ b/test/battle/ability/cheek_pouch.c @@ -119,7 +119,7 @@ SINGLE_BATTLE_TEST("Cheek Pouch doesn't activate when using Natural Gift") } } -SINGLE_BATTLE_TEST("Cheek Pouch doesn't activate when using Fling") +SINGLE_BATTLE_TEST("Cheek Pouch doesn't activate when user uses Fling") { GIVEN { ASSUME(GetMoveEffect(MOVE_FLING) == EFFECT_FLING); diff --git a/test/battle/ability/commander.c b/test/battle/ability/commander.c index 96e4facce..dca954260 100644 --- a/test/battle/ability/commander.c +++ b/test/battle/ability/commander.c @@ -455,3 +455,22 @@ DOUBLE_BATTLE_TEST("Commander prevent Dondozo from switch out by Dragon Tail") NOT MESSAGE("Wobbuffet was dragged out!"); } } + +DOUBLE_BATTLE_TEST("Commander will not activate if partner Dondozo is about to switch out") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_DONDOZO); + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { + SWITCH(playerLeft, 2); + SWITCH(playerRight, 3); + } + } SCENE { + NOT ABILITY_POPUP(playerRight, ABILITY_COMMANDER); + } +} diff --git a/test/battle/ability/corrosion.c b/test/battle/ability/corrosion.c index 47c2dec80..0b49f09d4 100644 --- a/test/battle/ability/corrosion.c +++ b/test/battle/ability/corrosion.c @@ -66,34 +66,7 @@ SINGLE_BATTLE_TEST("Corrosion does not effect poison type damaging moves if the } } -SINGLE_BATTLE_TEST("Corrosion can poison Poison- and Steel-type targets if it uses Fling while holding a Toxic Orb or a Poison Barb") -{ - u16 heldItem; - - PARAMETRIZE { heldItem = ITEM_POISON_BARB; } - PARAMETRIZE { heldItem = ITEM_TOXIC_ORB; } - - GIVEN { - ASSUME(GetMoveEffect(MOVE_FLING) == EFFECT_FLING); - ASSUME(gItemsInfo[ITEM_POISON_BARB].holdEffect == HOLD_EFFECT_TYPE_POWER); - ASSUME(gItemsInfo[ITEM_POISON_BARB].secondaryId == TYPE_POISON); - ASSUME(gItemsInfo[ITEM_TOXIC_ORB].holdEffect == HOLD_EFFECT_TOXIC_ORB); - PLAYER(SPECIES_SALANDIT) { Ability(ABILITY_CORROSION); Item(heldItem); } - OPPONENT(SPECIES_ODDISH); - } WHEN { - TURN { MOVE(player, MOVE_FLING); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_FLING, player); - HP_BAR(opponent); - ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); - if (heldItem == ITEM_POISON_BARB) - STATUS_ICON(opponent, poison: TRUE); - else - STATUS_ICON(opponent, badPoison: TRUE); - } -} - -SINGLE_BATTLE_TEST("If a Poison- or Steel-type Pokémon with Corrosion holds a Toxic Orb, it will badly poison itself") +SINGLE_BATTLE_TEST("Corrosion badly poisons its Poison/Steel-type user who holds a Toxic Orb") { GIVEN { ASSUME(gItemsInfo[ITEM_TOXIC_ORB].holdEffect == HOLD_EFFECT_TOXIC_ORB); @@ -107,7 +80,7 @@ SINGLE_BATTLE_TEST("If a Poison- or Steel-type Pokémon with Corrosion holds a T } } -SINGLE_BATTLE_TEST("If a Poison- or Steel-type Pokémon with Corrosion poisons a target with Synchronize, Synchronize will not poison Poison- or Steel-type Pokémon") +SINGLE_BATTLE_TEST("Corrosion can poison a target with Synchronize and Synchronize will not poison Poison- or Steel-type Pokémon") { enum Move move; PARAMETRIZE { move = MOVE_TOXIC; } diff --git a/test/battle/ability/dancer.c b/test/battle/ability/dancer.c index 075a3e46a..c132c4705 100644 --- a/test/battle/ability/dancer.c +++ b/test/battle/ability/dancer.c @@ -1,6 +1,5 @@ #include "global.h" #include "test/battle.h" -#include "constants/battle_z_move_effects.h" SINGLE_BATTLE_TEST("Dancer can copy a dance move immediately after it was used and allow the user of Dancer to still use its move") { diff --git a/test/battle/ability/dazzling.c b/test/battle/ability/dazzling.c index 2199f4928..2a55e6161 100644 --- a/test/battle/ability/dazzling.c +++ b/test/battle/ability/dazzling.c @@ -1,6 +1,5 @@ #include "global.h" #include "test/battle.h" -#include "constants/battle_z_move_effects.h" ASSUMPTIONS { diff --git a/test/battle/ability/desolate_land.c b/test/battle/ability/desolate_land.c index 8a081a621..f3f182307 100644 --- a/test/battle/ability/desolate_land.c +++ b/test/battle/ability/desolate_land.c @@ -3,7 +3,7 @@ ASSUMPTIONS { - ASSUME(!IsBattleMoveStatus(MOVE_WATER_GUN)); + ASSUME(GetMoveCategory(MOVE_WATER_GUN) != DAMAGE_CATEGORY_STATUS); ASSUME(GetMoveType(MOVE_WATER_GUN) == TYPE_WATER); } @@ -32,7 +32,7 @@ SINGLE_BATTLE_TEST("Desolate Land blocks damaging Water-type moves") DOUBLE_BATTLE_TEST("Desolate Land blocks damaging Water-type moves and prints the message only once with moves hitting multiple targets") { GIVEN { - ASSUME(!IsBattleMoveStatus(MOVE_SURF)); + ASSUME(GetMoveCategory(MOVE_SURF) != DAMAGE_CATEGORY_STATUS); ASSUME(GetMoveType(MOVE_SURF) == TYPE_WATER); ASSUME(GetMoveTarget(MOVE_SURF) == TARGET_FOES_AND_ALLY); PLAYER(SPECIES_GROUDON) { Item(ITEM_RED_ORB); Speed(5); } diff --git a/test/battle/ability/disguise.c b/test/battle/ability/disguise.c index c0ef6fbc9..a4fb40818 100644 --- a/test/battle/ability/disguise.c +++ b/test/battle/ability/disguise.c @@ -8,9 +8,12 @@ ASSUMPTIONS SINGLE_BATTLE_TEST("Disguised Mimikyu doesn't lose 1/8 of its max HP upon changing to its busted form (Gen7)") { + u32 species, newSpecies; + PARAMETRIZE { species = SPECIES_MIMIKYU_DISGUISED; newSpecies = SPECIES_MIMIKYU_BUSTED; } + PARAMETRIZE { species = SPECIES_MIMIKYU_TOTEM_DISGUISED; newSpecies = SPECIES_MIMIKYU_BUSTED_TOTEM; } GIVEN { WITH_CONFIG(CONFIG_DISGUISE_HP_LOSS, GEN_7); - PLAYER(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); } + PLAYER(species) { Ability(ABILITY_DISGUISE); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(opponent, MOVE_AERIAL_ACE); } @@ -19,7 +22,7 @@ SINGLE_BATTLE_TEST("Disguised Mimikyu doesn't lose 1/8 of its max HP upon changi NOT HP_BAR(player); ABILITY_POPUP(player, ABILITY_DISGUISE); } THEN { - EXPECT_EQ(player->species, SPECIES_MIMIKYU_BUSTED); + EXPECT_EQ(player->species, newSpecies); EXPECT_EQ(player->hp, player->maxHP); } } @@ -27,10 +30,12 @@ SINGLE_BATTLE_TEST("Disguised Mimikyu doesn't lose 1/8 of its max HP upon changi SINGLE_BATTLE_TEST("Disguised Mimikyu will lose 1/8 of its max HP upon changing to its busted form (Gen8+)") { s16 disguiseDamage; - + u32 species, newSpecies; + PARAMETRIZE { species = SPECIES_MIMIKYU_DISGUISED; newSpecies = SPECIES_MIMIKYU_BUSTED; } + PARAMETRIZE { species = SPECIES_MIMIKYU_TOTEM_DISGUISED; newSpecies = SPECIES_MIMIKYU_BUSTED_TOTEM; } GIVEN { WITH_CONFIG(CONFIG_DISGUISE_HP_LOSS, GEN_8); - PLAYER(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); } + PLAYER(species) { Ability(ABILITY_DISGUISE); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(opponent, MOVE_AERIAL_ACE); } @@ -39,16 +44,19 @@ SINGLE_BATTLE_TEST("Disguised Mimikyu will lose 1/8 of its max HP upon changing ABILITY_POPUP(player, ABILITY_DISGUISE); HP_BAR(player, captureDamage: &disguiseDamage); } THEN { - EXPECT_EQ(player->species, SPECIES_MIMIKYU_BUSTED); + EXPECT_EQ(player->species, newSpecies); EXPECT_EQ(disguiseDamage, player->maxHP / 8); } } SINGLE_BATTLE_TEST("Disguised Mimikyu takes no damage from a confusion hit and changes to its busted form") { + u32 species, newSpecies; + PARAMETRIZE { species = SPECIES_MIMIKYU_DISGUISED; newSpecies = SPECIES_MIMIKYU_BUSTED; } + PARAMETRIZE { species = SPECIES_MIMIKYU_TOTEM_DISGUISED; newSpecies = SPECIES_MIMIKYU_BUSTED_TOTEM; } GIVEN { ASSUME(GetMoveEffect(MOVE_CONFUSE_RAY) == EFFECT_CONFUSE); - PLAYER(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); } + PLAYER(species) { Ability(ABILITY_DISGUISE); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(opponent, MOVE_CONFUSE_RAY); } @@ -62,15 +70,18 @@ SINGLE_BATTLE_TEST("Disguised Mimikyu takes no damage from a confusion hit and c NOT HP_BAR(player); ABILITY_POPUP(player, ABILITY_DISGUISE); } THEN { - EXPECT_EQ(player->species, SPECIES_MIMIKYU_BUSTED); + EXPECT_EQ(player->species, newSpecies); } } SINGLE_BATTLE_TEST("Disguised Mimikyu's Air Balloon will pop upon changing to its busted form") { + u32 species, newSpecies; + PARAMETRIZE { species = SPECIES_MIMIKYU_DISGUISED; newSpecies = SPECIES_MIMIKYU_BUSTED; } + PARAMETRIZE { species = SPECIES_MIMIKYU_TOTEM_DISGUISED; newSpecies = SPECIES_MIMIKYU_BUSTED_TOTEM; } GIVEN { ASSUME(gItemsInfo[ITEM_AIR_BALLOON].holdEffect == HOLD_EFFECT_AIR_BALLOON); - PLAYER(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); Item(ITEM_AIR_BALLOON); } + PLAYER(species) { Ability(ABILITY_DISGUISE); Item(ITEM_AIR_BALLOON); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(opponent, MOVE_AERIAL_ACE); } @@ -81,16 +92,19 @@ SINGLE_BATTLE_TEST("Disguised Mimikyu's Air Balloon will pop upon changing to it ABILITY_POPUP(player, ABILITY_DISGUISE); MESSAGE("Mimikyu's Air Balloon popped!"); } THEN { - EXPECT_EQ(player->species, SPECIES_MIMIKYU_BUSTED); + EXPECT_EQ(player->species, newSpecies); } } SINGLE_BATTLE_TEST("Disguised Mimikyu takes damage from secondary damage without breaking the disguise - Stealth Rock") { + u32 species; + PARAMETRIZE { species = SPECIES_MIMIKYU_DISGUISED; } + PARAMETRIZE { species = SPECIES_MIMIKYU_TOTEM_DISGUISED; } GIVEN { ASSUME(GetMoveEffect(MOVE_STEALTH_ROCK) == EFFECT_STEALTH_ROCK); PLAYER(SPECIES_WOBBUFFET); - PLAYER(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); } + PLAYER(species) { Ability(ABILITY_DISGUISE); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(opponent, MOVE_STEALTH_ROCK); } @@ -100,7 +114,7 @@ SINGLE_BATTLE_TEST("Disguised Mimikyu takes damage from secondary damage without HP_BAR(player); MESSAGE("Pointed stones dug into Mimikyu!"); } THEN { - EXPECT_EQ(player->species, SPECIES_MIMIKYU_DISGUISED); + EXPECT_EQ(player->species, species); } } @@ -126,9 +140,12 @@ SINGLE_BATTLE_TEST("Disguised Mimikyu takes damage from secondary damage without SINGLE_BATTLE_TEST("Disguised Mimikyu takes damage from Rocky Helmet without breaking the disguise") { + u32 species; + PARAMETRIZE { species = SPECIES_MIMIKYU_DISGUISED; } + PARAMETRIZE { species = SPECIES_MIMIKYU_TOTEM_DISGUISED; } GIVEN { ASSUME(gItemsInfo[ITEM_ROCKY_HELMET].holdEffect == HOLD_EFFECT_ROCKY_HELMET); - PLAYER(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); } + PLAYER(species) { Ability(ABILITY_DISGUISE); } OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_ROCKY_HELMET); } } WHEN { TURN { MOVE(player, MOVE_AERIAL_ACE); } @@ -139,14 +156,17 @@ SINGLE_BATTLE_TEST("Disguised Mimikyu takes damage from Rocky Helmet without bre HP_BAR(player); MESSAGE("Mimikyu was hurt by the opposing Wobbuffet's Rocky Helmet!"); } THEN { - EXPECT_EQ(player->species, SPECIES_MIMIKYU_DISGUISED); + EXPECT_EQ(player->species, species); } } SINGLE_BATTLE_TEST("Disguised Mimikyu takes damage from Rough Skin without breaking the disguise") { + u32 species; + PARAMETRIZE { species = SPECIES_MIMIKYU_DISGUISED; } + PARAMETRIZE { species = SPECIES_MIMIKYU_TOTEM_DISGUISED; } GIVEN { - PLAYER(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); } + PLAYER(species) { Ability(ABILITY_DISGUISE); } OPPONENT(SPECIES_CARVANHA) { Ability(ABILITY_ROUGH_SKIN); } } WHEN { TURN { MOVE(player, MOVE_AERIAL_ACE); } @@ -157,28 +177,36 @@ SINGLE_BATTLE_TEST("Disguised Mimikyu takes damage from Rough Skin without break HP_BAR(player); MESSAGE("Mimikyu was hurt by the opposing Carvanha's Rough Skin!"); } THEN { - EXPECT_EQ(player->species, SPECIES_MIMIKYU_DISGUISED); + EXPECT_EQ(player->species, species); } } SINGLE_BATTLE_TEST("Disguised Mimikyu is ignored by Mold Breaker") { + u32 species; + PARAMETRIZE { species = SPECIES_MIMIKYU_DISGUISED; } + PARAMETRIZE { species = SPECIES_MIMIKYU_TOTEM_DISGUISED; } GIVEN { - PLAYER(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); } + PLAYER(species) { Ability(ABILITY_DISGUISE); } OPPONENT(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); } } WHEN { TURN { MOVE(opponent, MOVE_AERIAL_ACE); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_AERIAL_ACE, opponent); NOT ABILITY_POPUP(player, ABILITY_DISGUISE); + } THEN { + EXPECT_EQ(player->species, species); } } SINGLE_BATTLE_TEST("Disguised Mimikyu's types revert back to Ghost/Fairy when Disguise is broken") { + u32 species; + PARAMETRIZE { species = SPECIES_MIMIKYU_DISGUISED; } + PARAMETRIZE { species = SPECIES_MIMIKYU_TOTEM_DISGUISED; } GIVEN { ASSUME(GetMoveType(MOVE_SHADOW_CLAW) == TYPE_GHOST); - PLAYER(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); } + PLAYER(species) { Ability(ABILITY_DISGUISE); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(opponent, MOVE_SOAK); } @@ -196,10 +224,13 @@ SINGLE_BATTLE_TEST("Disguised Mimikyu's types revert back to Ghost/Fairy when Di SINGLE_BATTLE_TEST("Disguised Mimikyu blocks a move after getting Gastro Acid Batton Passed") { + u32 species, newSpecies; + PARAMETRIZE { species = SPECIES_MIMIKYU_DISGUISED; newSpecies = SPECIES_MIMIKYU_BUSTED; } + PARAMETRIZE { species = SPECIES_MIMIKYU_TOTEM_DISGUISED; newSpecies = SPECIES_MIMIKYU_BUSTED_TOTEM; } GIVEN { ASSUME(GetMoveEffect(MOVE_BATON_PASS) == EFFECT_BATON_PASS); PLAYER(SPECIES_WOBBUFFET); - PLAYER(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); } + PLAYER(species) { Ability(ABILITY_DISGUISE); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(opponent, MOVE_GASTRO_ACID); MOVE(player, MOVE_BATON_PASS); SEND_OUT(player, 1); } @@ -210,15 +241,20 @@ SINGLE_BATTLE_TEST("Disguised Mimikyu blocks a move after getting Gastro Acid Ba ANIMATION(ANIM_TYPE_MOVE, MOVE_BATON_PASS, player); ANIMATION(ANIM_TYPE_MOVE, MOVE_SHADOW_CLAW, opponent); ABILITY_POPUP(player, ABILITY_DISGUISE); + } THEN { + EXPECT_EQ(player->species, newSpecies); } } SINGLE_BATTLE_TEST("Disguise does not break from a teammate's Wish") { + u32 species; + PARAMETRIZE { species = SPECIES_MIMIKYU_DISGUISED; } + PARAMETRIZE { species = SPECIES_MIMIKYU_TOTEM_DISGUISED; } GIVEN { ASSUME(GetMoveEffect(MOVE_WISH) == EFFECT_WISH); PLAYER(SPECIES_JIRACHI); - PLAYER(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); HP(219); MaxHP(220); } + PLAYER(species) { Ability(ABILITY_DISGUISE); HP(219); MaxHP(220); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(player, MOVE_WISH); } @@ -226,5 +262,7 @@ SINGLE_BATTLE_TEST("Disguise does not break from a teammate's Wish") } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_WISH, player); NOT ABILITY_POPUP(player, ABILITY_DISGUISE); + } THEN { + EXPECT_EQ(player->species, species); } } diff --git a/test/battle/ability/early_bird.c b/test/battle/ability/early_bird.c index 9b368970f..0f41fffc0 100644 --- a/test/battle/ability/early_bird.c +++ b/test/battle/ability/early_bird.c @@ -1,4 +1,49 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Early Bird (Ability) test titles") +SINGLE_BATTLE_TEST("Early Bird wakes up if 1 sleep turn is preset") +{ + GIVEN { + PLAYER(SPECIES_DODUO) { Ability(ABILITY_EARLY_BIRD); Status1(STATUS1_SLEEP_TURN(1)); Moves(MOVE_CELEBRATE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); } + } SCENE { + MESSAGE("Doduo woke up!"); + STATUS_ICON(player, none: TRUE); + MESSAGE("Doduo used Celebrate!"); + } +} + +SINGLE_BATTLE_TEST("Early Bird turns a 3-turn sleep into one missed turn") +{ + GIVEN { + PLAYER(SPECIES_DODUO) { Ability(ABILITY_EARLY_BIRD); Status1(STATUS1_SLEEP_TURN(3)); Moves(MOVE_CELEBRATE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_CELEBRATE); } + } SCENE { + MESSAGE("Doduo is fast asleep."); + MESSAGE("Doduo woke up!"); + STATUS_ICON(player, none: TRUE); + MESSAGE("Doduo used Celebrate!"); + } +} + +SINGLE_BATTLE_TEST("Early Bird reduces Rest sleep to one turn") +{ + GIVEN { + PLAYER(SPECIES_DODUO) { Ability(ABILITY_EARLY_BIRD); MaxHP(99); HP(66); Moves(MOVE_REST, MOVE_CELEBRATE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_REST); } + TURN { MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_CELEBRATE); } + } SCENE { + MESSAGE("Doduo is fast asleep."); + MESSAGE("Doduo woke up!"); + STATUS_ICON(player, none: TRUE); + MESSAGE("Doduo used Celebrate!"); + } +} diff --git a/test/battle/ability/electromorphosis.c b/test/battle/ability/electromorphosis.c index cc457db77..fda6a882b 100644 --- a/test/battle/ability/electromorphosis.c +++ b/test/battle/ability/electromorphosis.c @@ -10,11 +10,9 @@ SINGLE_BATTLE_TEST("Electromorphosis sets up Charge when hit by any move") PARAMETRIZE { move = MOVE_GUST; } GIVEN { - ASSUME(!IsBattleMoveStatus(MOVE_SCRATCH)); - ASSUME(!IsBattleMoveStatus(MOVE_GUST)); ASSUME(GetMoveCategory(MOVE_GUST) == DAMAGE_CATEGORY_SPECIAL); ASSUME(GetMoveCategory(MOVE_SCRATCH) == DAMAGE_CATEGORY_PHYSICAL); - ASSUME(!IsBattleMoveStatus(MOVE_THUNDER_SHOCK)); + ASSUME(GetMoveCategory(MOVE_THUNDER_SHOCK) != DAMAGE_CATEGORY_STATUS); ASSUME(GetMoveType(MOVE_THUNDER_SHOCK) == TYPE_ELECTRIC); PLAYER(SPECIES_BELLIBOLT) { Ability(ABILITY_ELECTROMORPHOSIS); Speed(10); } diff --git a/test/battle/ability/emergency_exit.c b/test/battle/ability/emergency_exit.c index a03a4853c..6112267aa 100644 --- a/test/battle/ability/emergency_exit.c +++ b/test/battle/ability/emergency_exit.c @@ -209,3 +209,73 @@ WILD_BATTLE_TEST("Emergency Exit activates when taking residual damage and falli EXPECT_EQ(gBattleOutcome, B_OUTCOME_PLAYER_TELEPORTED); } } + +SINGLE_BATTLE_TEST("Emergency Exit will trigger due to recoil damage") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_MIND_BLOWN) == EFFECT_MAX_HP_50_RECOIL); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(262); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_MIND_BLOWN); SEND_OUT(opponent, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIND_BLOWN, opponent); + HP_BAR(player); + HP_BAR(opponent); + ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT); + } +} + +SINGLE_BATTLE_TEST("Emergency Exit will trigger due to confusion damage") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_CONFUSE_RAY) == EFFECT_CONFUSE); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(133); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { + MOVE(player, MOVE_CONFUSE_RAY); + MOVE(opponent, MOVE_POUND); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFUSE_RAY, player); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, opponent); + HP_BAR(opponent); + NOT ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT); + } +} + +SINGLE_BATTLE_TEST("Emergency Exit is not triggered by Pain Split") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_PAIN_SPLIT) == EFFECT_PAIN_SPLIT); + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(133); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_PAIN_SPLIT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PAIN_SPLIT, player); + HP_BAR(opponent); + NOT ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT); + } +} + +SINGLE_BATTLE_TEST("Emergency Exit will trigger due to Jump Kick recoil") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_JUMP_KICK) == EFFECT_RECOIL_IF_MISS); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(262); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_JUMP_KICK, hit: FALSE); SEND_OUT(opponent, 1); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_JUMP_KICK, opponent); + HP_BAR(opponent); + ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT); + } +} + diff --git a/test/battle/ability/forewarn.c b/test/battle/ability/forewarn.c index 810fcd975..dd1c99da9 100644 --- a/test/battle/ability/forewarn.c +++ b/test/battle/ability/forewarn.c @@ -1,4 +1,84 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Forewarn (Ability) test titles") +DOUBLE_BATTLE_TEST("Forewarn warns about the highest power move among all opposing battlers") +{ + GIVEN { + PLAYER(SPECIES_MUSHARNA) { Ability(ABILITY_FOREWARN); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_ZUBAT) { Moves(MOVE_CRUNCH, MOVE_CELEBRATE); } + OPPONENT(SPECIES_EXCADRILL) { Moves(MOVE_FISSURE, MOVE_CELEBRATE); } + } WHEN { + TURN {} + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_FOREWARN); + MESSAGE("Forewarn alerted Musharna to the opposing Excadrill's Fissure!"); + } +} + +SINGLE_BATTLE_TEST("Forewarn randomly chooses between same-power moves on one opponent") +{ + PASSES_RANDOMLY(1, 3, RNG_FOREWARN); + GIVEN { + ASSUME(GetMovePower(MOVE_TACKLE) == GetMovePower(MOVE_POUND)); + ASSUME(GetMovePower(MOVE_TACKLE) == GetMovePower(MOVE_SCRATCH)); + PLAYER(SPECIES_MUSHARNA) { Ability(ABILITY_FOREWARN); } + OPPONENT(SPECIES_ZUBAT) { Moves(MOVE_TACKLE, MOVE_POUND, MOVE_SCRATCH, MOVE_CELEBRATE); } + } WHEN { + TURN {} + } SCENE { + ABILITY_POPUP(player, ABILITY_FOREWARN); + MESSAGE("Forewarn alerted Musharna to the opposing Zubat's Tackle!"); + } +} + +DOUBLE_BATTLE_TEST("Forewarn randomly chooses between opponents with same-power moves") +{ + PASSES_RANDOMLY(1, 4, RNG_FOREWARN); + GIVEN { + ASSUME(GetMovePower(MOVE_TACKLE) == GetMovePower(MOVE_POUND)); + ASSUME(GetMovePower(MOVE_TACKLE) == GetMovePower(MOVE_SCRATCH)); + ASSUME(GetMovePower(MOVE_TACKLE) == GetMovePower(MOVE_QUICK_ATTACK)); + PLAYER(SPECIES_MUSHARNA) { Ability(ABILITY_FOREWARN); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_ZUBAT) { Moves(MOVE_TACKLE, MOVE_POUND, MOVE_PECK, MOVE_CELEBRATE); } + OPPONENT(SPECIES_EXCADRILL) { Moves(MOVE_SCRATCH, MOVE_QUICK_ATTACK, MOVE_ABSORB, MOVE_CELEBRATE); } + } WHEN { + TURN {} + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_FOREWARN); + MESSAGE("Forewarn alerted Musharna to the opposing Zubat's Tackle!"); + } +} + +DOUBLE_BATTLE_TEST("Forewarn does not trigger if a mon switches in while the opposing field is empty") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_U_TURN) == EFFECT_HIT_ESCAPE); + ASSUME(GetMoveEffect(MOVE_HEALING_WISH) == EFFECT_HEALING_WISH); + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_MUSHARNA) { Ability(ABILITY_FOREWARN); } + OPPONENT(SPECIES_WYNAUT) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_TREECKO); + OPPONENT(SPECIES_TORCHIC); + } WHEN { + TURN { + MOVE(opponentRight, MOVE_HEALING_WISH); + MOVE(playerLeft, MOVE_U_TURN, target: opponentLeft); + SEND_OUT(playerLeft, 2); + SEND_OUT(opponentLeft, 2); + SEND_OUT(opponentRight, 3); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEALING_WISH, opponentRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, playerLeft); + HP_BAR(opponentLeft); + NOT ABILITY_POPUP(playerLeft, ABILITY_FOREWARN); + MESSAGE("2 sent out Treecko!"); + MESSAGE("2 sent out Torchic!"); + NOT ABILITY_POPUP(playerLeft, ABILITY_FOREWARN); + } +} diff --git a/test/battle/ability/frisk.c b/test/battle/ability/frisk.c index b39f53c48..c50600977 100644 --- a/test/battle/ability/frisk.c +++ b/test/battle/ability/frisk.c @@ -42,7 +42,7 @@ DOUBLE_BATTLE_TEST("Frisk triggers for player in a Double Battle after switching PARAMETRIZE { target = playerRight; } GIVEN { - ASSUME(!IsBattleMoveStatus(MOVE_POUND)); + ASSUME(GetMoveCategory(MOVE_POUND) != DAMAGE_CATEGORY_STATUS); PLAYER(SPECIES_WOBBUFFET) { HP(1); } PLAYER(SPECIES_WOBBUFFET) { HP(1); } PLAYER(SPECIES_FURRET) { Ability(ABILITY_FRISK); } @@ -65,7 +65,7 @@ DOUBLE_BATTLE_TEST("Frisk triggers for opponent in a Double Battle after switchi PARAMETRIZE { target = opponentRight; } GIVEN { - ASSUME(!IsBattleMoveStatus(MOVE_POUND)); + ASSUME(GetMoveCategory(MOVE_POUND) != DAMAGE_CATEGORY_STATUS); PLAYER(SPECIES_WYNAUT) { Item(ITEM_POTION); } PLAYER(SPECIES_WYNAUT); OPPONENT(SPECIES_WOBBUFFET) { HP(1); } diff --git a/test/battle/ability/galvanize.c b/test/battle/ability/galvanize.c index e672457a3..248246d3d 100644 --- a/test/battle/ability/galvanize.c +++ b/test/battle/ability/galvanize.c @@ -164,9 +164,72 @@ SINGLE_BATTLE_TEST("Galvanize doesn't affect Hidden Power's type") } } -TO_DO_BATTLE_TEST("Galvanize doesn't affect Tera Starstorm's type"); +SINGLE_BATTLE_TEST("Galvanize changes Tera Blast's type when not Terastallized") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_TERA_BLAST) == EFFECT_TERA_BLAST); + ASSUME(GetMoveType(MOVE_TERA_BLAST) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_ROOKIDEE, 0) == TYPE_FLYING || GetSpeciesType(SPECIES_ROOKIDEE, 1) == TYPE_FLYING); + PLAYER(SPECIES_GEODUDE_ALOLA) { Ability(ABILITY_GALVANIZE); } + OPPONENT(SPECIES_ROOKIDEE); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + MESSAGE("It's super effective!"); + } +} + +SINGLE_BATTLE_TEST("Galvanize doesn't change Tera Blast's type when Terastallized") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_TERA_BLAST) == EFFECT_TERA_BLAST); + ASSUME(GetMoveType(MOVE_TERA_BLAST) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND); + PLAYER(SPECIES_GEODUDE_ALOLA) { Ability(ABILITY_GALVANIZE); TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_SANDSHREW); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + NOT { MESSAGE("It doesn't affect the opposing Sandshrew…"); } + } +} + +SINGLE_BATTLE_TEST("Galvanize doesn't affect Terrain Pulse's type") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_TERRAIN_PULSE) == EFFECT_TERRAIN_PULSE); + ASSUME(GetMoveType(MOVE_TERRAIN_PULSE) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND); + PLAYER(SPECIES_GEODUDE_ALOLA) { Ability(ABILITY_GALVANIZE); } + OPPONENT(SPECIES_SANDSHREW); + } WHEN { + TURN { MOVE(opponent, MOVE_GRASSY_TERRAIN); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_TERRAIN_PULSE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_GRASSY_TERRAIN, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERRAIN_PULSE, player); + MESSAGE("It's super effective!"); + } +} + +SINGLE_BATTLE_TEST("Galvanize doesn't affect damaging Z-Move types") +{ + GIVEN { + ASSUME(GetMoveType(MOVE_SCRATCH) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_GYARADOS, 0) == TYPE_WATER || GetSpeciesType(SPECIES_GYARADOS, 1) == TYPE_WATER); + PLAYER(SPECIES_GEODUDE_ALOLA) { Ability(ABILITY_GALVANIZE); Item(ITEM_NORMALIUM_Z); } + OPPONENT(SPECIES_GYARADOS); + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_Z_MOVE); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, player); + NOT { MESSAGE("It's super effective!"); } + } +} + TO_DO_BATTLE_TEST("Galvanize doesn't affect Max Strike's type"); -TO_DO_BATTLE_TEST("Galvanize doesn't affect Terrain Pulse's type"); -TO_DO_BATTLE_TEST("Galvanize doesn't affect damaging Z-Move types"); TO_DO_BATTLE_TEST("(DYNAMAX) Galvanize turns Max Strike into Max Lightning when not used by Gigantamax Pikachu/Toxtricity"); //TO_DO_BATTLE_TEST("(DYNAMAX) Galvanize doesn't turn Max Strike into Max Lightning when used by Gigantamax Pikachu/Toxtricity, instead becoming G-Max Volt Crash/Stun Shock"); // Marked in Bulbapedia as "needs research", so this assumes that it behaves like Pixilate. diff --git a/test/battle/ability/gulp_missile.c b/test/battle/ability/gulp_missile.c index 5a3266ff1..ea18e1a67 100644 --- a/test/battle/ability/gulp_missile.c +++ b/test/battle/ability/gulp_missile.c @@ -1,12 +1,7 @@ #include "global.h" #include "test/battle.h" -ASSUMPTIONS -{ - // ASSUME(GetMoveCategory(MOVE_AERIAL_ACE) == DAMAGE_CATEGORY_PHYSICAL); -} - -SINGLE_BATTLE_TEST("(Gulp Missile) If base Cramorant hits target with Surf it transforms into Gulping form if max HP is over 1/2") +SINGLE_BATTLE_TEST("Gulp Missile: If base Cramorant hits target with Surf it transforms into Gulping form if max HP is over 1/2") { GIVEN { PLAYER(SPECIES_CRAMORANT) { Ability(ABILITY_GULP_MISSILE); } @@ -15,14 +10,14 @@ SINGLE_BATTLE_TEST("(Gulp Missile) If base Cramorant hits target with Surf it tr TURN { MOVE(player, MOVE_SURF); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE_INSTANT, player); HP_BAR(opponent); - ABILITY_POPUP(player, ABILITY_GULP_MISSILE); } THEN { EXPECT_EQ(player->species, SPECIES_CRAMORANT_GULPING); } } -SINGLE_BATTLE_TEST("(Gulp Missile) If base Cramorant hits target with Surf it transforms into Gorging form if max HP is under 1/2") +SINGLE_BATTLE_TEST("Gulp Missile: If base Cramorant hits target with Surf it transforms into Gorging form if max HP is under 1/2") { GIVEN { PLAYER(SPECIES_CRAMORANT) { HP(120); MaxHP(250); Ability(ABILITY_GULP_MISSILE); } @@ -31,30 +26,31 @@ SINGLE_BATTLE_TEST("(Gulp Missile) If base Cramorant hits target with Surf it tr TURN { MOVE(player, MOVE_SURF); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE_INSTANT, player); HP_BAR(opponent); - ABILITY_POPUP(player, ABILITY_GULP_MISSILE); } THEN { EXPECT_EQ(player->species, SPECIES_CRAMORANT_GORGING); } } -SINGLE_BATTLE_TEST("(Gulp Missile) If base Cramorant is under water it transforms into one of its forms") +SINGLE_BATTLE_TEST("Gulp Missile: If base Cramorant is under water it transforms into one of its forms") { GIVEN { PLAYER(SPECIES_CRAMORANT) { Ability(ABILITY_GULP_MISSILE); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(player, MOVE_DIVE); } + TURN { SKIP_TURN(player); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_DIVE, player); - NOT HP_BAR(opponent); - ABILITY_POPUP(player, ABILITY_GULP_MISSILE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE_INSTANT, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DIVE, player); } THEN { EXPECT_EQ(player->species, SPECIES_CRAMORANT_GULPING); } } -SINGLE_BATTLE_TEST("(Gulp Missile) Power Herb does not prevent Cramaront from transforming") +SINGLE_BATTLE_TEST("Gulp Missile: Power Herb does not prevent Cramaront from transforming") { GIVEN { PLAYER(SPECIES_CRAMORANT) { Ability(ABILITY_GULP_MISSILE); Item(ITEM_POWER_HERB); } @@ -63,15 +59,15 @@ SINGLE_BATTLE_TEST("(Gulp Missile) Power Herb does not prevent Cramaront from tr TURN { MOVE(player, MOVE_DIVE); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_DIVE, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE_INSTANT, player); MESSAGE("Cramorant became fully charged due to its Power Herb!"); - ABILITY_POPUP(player, ABILITY_GULP_MISSILE); HP_BAR(opponent); } THEN { EXPECT_EQ(player->species, SPECIES_CRAMORANT_GULPING); } } -SINGLE_BATTLE_TEST("(Gulp Missile) Transformed Cramorant deal 1/4 of damage opposing mon if hit by a damaging move, Gulping also lowers defense") +SINGLE_BATTLE_TEST("Gulp Missile: Transformed Cramorant deal 1/4 of damage opposing mon if hit by a damaging move, Gulping also lowers defense") { s16 gulpMissileDamage; @@ -82,11 +78,12 @@ SINGLE_BATTLE_TEST("(Gulp Missile) Transformed Cramorant deal 1/4 of damage oppo TURN { MOVE(player, MOVE_SURF); MOVE(opponent, MOVE_SCRATCH); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE_INSTANT, player); HP_BAR(opponent); - ABILITY_POPUP(player, ABILITY_GULP_MISSILE); ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent); HP_BAR(player); ABILITY_POPUP(player, ABILITY_GULP_MISSILE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE_INSTANT, player); HP_BAR(opponent, captureDamage: &gulpMissileDamage); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); MESSAGE("The opposing Wobbuffet's Defense fell!"); @@ -96,7 +93,7 @@ SINGLE_BATTLE_TEST("(Gulp Missile) Transformed Cramorant deal 1/4 of damage oppo } } -SINGLE_BATTLE_TEST("(Gulp Missile) Cramorant in Gorging paralyzes the target if hit by a damaging move") +SINGLE_BATTLE_TEST("Gulp Missile: Cramorant in Gorging paralyzes the target if hit by a damaging move") { GIVEN { PLAYER(SPECIES_CRAMORANT) { HP(120); MaxHP(250); Ability(ABILITY_GULP_MISSILE); } @@ -105,18 +102,19 @@ SINGLE_BATTLE_TEST("(Gulp Missile) Cramorant in Gorging paralyzes the target if TURN { MOVE(player, MOVE_SURF); MOVE(opponent, MOVE_SCRATCH); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE_INSTANT, player); HP_BAR(opponent); - ABILITY_POPUP(player, ABILITY_GULP_MISSILE); ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent); HP_BAR(player); ABILITY_POPUP(player, ABILITY_GULP_MISSILE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE_INSTANT, player); HP_BAR(opponent); ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponent); STATUS_ICON(opponent, paralysis: TRUE); } } -SINGLE_BATTLE_TEST("(Gulp Missile) triggers even if the user is fainted by opposing mon") +SINGLE_BATTLE_TEST("Gulp Missile: triggers even if the user is fainted by opposing mon") { GIVEN { PLAYER(SPECIES_CRAMORANT) { HP(1); MaxHP(250); Ability(ABILITY_GULP_MISSILE); } @@ -126,16 +124,18 @@ SINGLE_BATTLE_TEST("(Gulp Missile) triggers even if the user is fainted by oppos TURN { MOVE(player, MOVE_SURF); MOVE(opponent, MOVE_SCRATCH); SEND_OUT(player, 1); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE_INSTANT, player); HP_BAR(opponent); - ABILITY_POPUP(player, ABILITY_GULP_MISSILE); ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent); HP_BAR(player); + ABILITY_POPUP(player, ABILITY_GULP_MISSILE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE_INSTANT, player); ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponent); STATUS_ICON(opponent, paralysis: TRUE); } } -SINGLE_BATTLE_TEST("(Gulp Missile) Transformed Cramorant Gulping lowers defense but is prevented by stat reduction preventing abilities") +SINGLE_BATTLE_TEST("Gulp Missile: Transformed Cramorant Gulping lowers defense but is prevented by stat reduction preventing abilities") { u32 species; enum Ability ability; @@ -149,11 +149,12 @@ SINGLE_BATTLE_TEST("(Gulp Missile) Transformed Cramorant Gulping lowers defense TURN { MOVE(player, MOVE_SURF); MOVE(opponent, MOVE_SCRATCH); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE_INSTANT, player); HP_BAR(opponent); - ABILITY_POPUP(player, ABILITY_GULP_MISSILE); ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent); HP_BAR(player); ABILITY_POPUP(player, ABILITY_GULP_MISSILE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE_INSTANT, player); ABILITY_POPUP(opponent, ability); NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); } THEN { @@ -161,7 +162,7 @@ SINGLE_BATTLE_TEST("(Gulp Missile) Transformed Cramorant Gulping lowers defense } } -SINGLE_BATTLE_TEST("(Gulp Missile) Transformed Cramorant Gulping lowers defense and still triggers other effects after") +SINGLE_BATTLE_TEST("Gulp Missile: Transformed Cramorant Gulping lowers defense and still triggers other effects after") { // Make sure attacker and target are correct after triggering the ability enum Ability ability; @@ -175,11 +176,12 @@ SINGLE_BATTLE_TEST("(Gulp Missile) Transformed Cramorant Gulping lowers defense TURN { MOVE(player, MOVE_SURF); MOVE(opponent, MOVE_SCRATCH); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE_INSTANT, player); HP_BAR(opponent); - ABILITY_POPUP(player, ABILITY_GULP_MISSILE); ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent); HP_BAR(player); ABILITY_POPUP(player, ABILITY_GULP_MISSILE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE_INSTANT, player); HP_BAR(opponent); if (ability == ABILITY_INFILTRATOR) { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); @@ -201,3 +203,51 @@ SINGLE_BATTLE_TEST("Gulp Missile triggered by explosion doesn't freeze the game" TURN { MOVE(opponent, MOVE_SURF); MOVE(player, MOVE_EXPLOSION); } } } + +SINGLE_BATTLE_TEST("Gulp Missile only changes forms for Cramorant") +{ + GIVEN { + ASSUME(!gAbilitiesInfo[ABILITY_GULP_MISSILE].cantBeSwapped); + ASSUME(!gAbilitiesInfo[ABILITY_LIGHTNING_ROD].cantBeSwapped); + PLAYER(SPECIES_CRAMORANT) { Ability(ABILITY_GULP_MISSILE); } + OPPONENT(SPECIES_PIKACHU) { Ability(ABILITY_LIGHTNING_ROD); } + } WHEN { + TURN { MOVE(opponent, MOVE_SKILL_SWAP); } + TURN { MOVE(opponent, MOVE_SURF); MOVE(player, MOVE_SURF); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE_INSTANT, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE_INSTANT, opponent); + } + } THEN { + EXPECT_EQ(player->species, SPECIES_CRAMORANT); + EXPECT_EQ(opponent->species, SPECIES_PIKACHU); + } +} + +SINGLE_BATTLE_TEST("Gulp Missile: If Cramorant loses Gulp Missile, it cannot spit out its prey") +{ + GIVEN { + ASSUME(!gAbilitiesInfo[ABILITY_GULP_MISSILE].cantBeSwapped); + ASSUME(!gAbilitiesInfo[ABILITY_LIGHTNING_ROD].cantBeSwapped); + PLAYER(SPECIES_CRAMORANT) { Ability(ABILITY_GULP_MISSILE); } + OPPONENT(SPECIES_PIKACHU) { Ability(ABILITY_LIGHTNING_ROD); } + } WHEN { + TURN { MOVE(player, MOVE_SURF); MOVE(opponent, MOVE_SKILL_SWAP); } + TURN { MOVE(opponent, MOVE_SCRATCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE_INSTANT, player); + HP_BAR(opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent); + HP_BAR(player); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); + HP_BAR(opponent); + } + } THEN { + EXPECT_EQ(player->species, SPECIES_CRAMORANT_GULPING); + EXPECT_EQ(opponent->species, SPECIES_PIKACHU); + } +} diff --git a/test/battle/ability/innards_out.c b/test/battle/ability/innards_out.c index 87f730c1b..02378d249 100644 --- a/test/battle/ability/innards_out.c +++ b/test/battle/ability/innards_out.c @@ -14,7 +14,6 @@ SINGLE_BATTLE_TEST("Innards Out deal dmg on fainting equal to the amount of dmg PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET) { HP(70); SpAttack(1000); } OPPONENT(SPECIES_WOBBUFFET); - ASSUME(!IsBattleMoveStatus(MOVE_PSYCHIC)); ASSUME(GetMoveCategory(MOVE_PSYCHIC) == DAMAGE_CATEGORY_SPECIAL); } WHEN { TURN { MOVE(opponent, MOVE_PSYCHIC); SEND_OUT(player, 1); if (hp == 100) { SEND_OUT(opponent, 1); } } @@ -32,7 +31,7 @@ SINGLE_BATTLE_TEST("Innards Out does not trigger after Gastro Acid has been used PLAYER(SPECIES_PYUKUMUKU) { HP(1); Ability(ABILITY_INNARDS_OUT); } PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); - ASSUME(!IsBattleMoveStatus(MOVE_PSYCHIC)); + ASSUME(GetMoveCategory(MOVE_PSYCHIC) != DAMAGE_CATEGORY_STATUS); ASSUME(GetMoveEffect(MOVE_GASTRO_ACID) == EFFECT_GASTRO_ACID); } WHEN { TURN { MOVE(opponent, MOVE_GASTRO_ACID); } @@ -55,7 +54,7 @@ SINGLE_BATTLE_TEST("Innards Out does not damage Magic Guard Pokemon") PLAYER(SPECIES_PYUKUMUKU) { HP(1); Ability(ABILITY_INNARDS_OUT); } PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_CLEFABLE) { Ability(ABILITY_MAGIC_GUARD); } - ASSUME(!IsBattleMoveStatus(MOVE_PSYCHIC)); + ASSUME(GetMoveCategory(MOVE_PSYCHIC) != DAMAGE_CATEGORY_STATUS); } WHEN { TURN { MOVE(opponent, MOVE_PSYCHIC); SEND_OUT(player, 1); } } SCENE { diff --git a/test/battle/ability/magician.c b/test/battle/ability/magician.c index a54ba2df7..94487fa15 100644 --- a/test/battle/ability/magician.c +++ b/test/battle/ability/magician.c @@ -5,7 +5,7 @@ SINGLE_BATTLE_TEST("Magician gets self-damage recoil after stealing Life Orb") { GIVEN { ASSUME(gItemsInfo[ITEM_LIFE_ORB].holdEffect == HOLD_EFFECT_LIFE_ORB); - ASSUME(!IsBattleMoveStatus(MOVE_SCRATCH)); + ASSUME(GetMoveCategory(MOVE_SCRATCH) != DAMAGE_CATEGORY_STATUS); PLAYER(SPECIES_DELPHOX) { Ability(ABILITY_MAGICIAN); Item(ITEM_NONE); } OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_LIFE_ORB); } } WHEN { diff --git a/test/battle/ability/mold_breaker.c b/test/battle/ability/mold_breaker.c index fb6f9d11a..0a9cf597f 100644 --- a/test/battle/ability/mold_breaker.c +++ b/test/battle/ability/mold_breaker.c @@ -19,4 +19,29 @@ SINGLE_BATTLE_TEST("Mold Breaker cancels damage reduction from Ice Scales", s16 } } +DOUBLE_BATTLE_TEST("Mold Breaker will deactivate if user faints") +{ + u32 move; + + PARAMETRIZE { move = MOVE_STEEL_BEAM; } + PARAMETRIZE { move = MOVE_POUND; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_STEEL_BEAM) == EFFECT_MAX_HP_50_RECOIL); + PLAYER(SPECIES_PINSIR) { HP(1); Ability(ABILITY_MOLD_BREAKER); } + PLAYER(SPECIES_ALTARIA) { Ability(ABILITY_COTTON_DOWN); } + OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, move, target: playerRight); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, playerLeft); + ABILITY_POPUP(playerRight, ABILITY_COTTON_DOWN); + if (move == MOVE_STEEL_BEAM) + ABILITY_POPUP(opponentLeft, ABILITY_CLEAR_BODY); + else + NOT ABILITY_POPUP(opponentLeft, ABILITY_CLEAR_BODY); + } +} + TO_DO_BATTLE_TEST("TODO: Write more Mold Breaker (Ability) test titles") diff --git a/test/battle/ability/neuroforce.c b/test/battle/ability/neuroforce.c index e469032ac..27f3df6d3 100644 --- a/test/battle/ability/neuroforce.c +++ b/test/battle/ability/neuroforce.c @@ -12,9 +12,10 @@ SINGLE_BATTLE_TEST("Neuroforce increases the strength of super-effective moves b GIVEN { ASSUME(GetMoveType(MOVE_SHADOW_BALL) == TYPE_GHOST); ASSUME(GetMoveType(MOVE_SCRATCH) == TYPE_NORMAL); - PLAYER(SPECIES_NECROZMA_ULTRA) { Ability(ability); } + PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Ability(ability); Item(ITEM_ULTRANECROZIUM_Z); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_ULTRA_BURST); } TURN { MOVE(player, move); } } SCENE { HP_BAR(opponent, captureDamage: &results[i].damage); diff --git a/test/battle/ability/normalize.c b/test/battle/ability/normalize.c index b85db768d..5c96cb58d 100644 --- a/test/battle/ability/normalize.c +++ b/test/battle/ability/normalize.c @@ -274,7 +274,72 @@ SINGLE_BATTLE_TEST("Normalize doesn't affect Hidden Power's type") } } -TO_DO_BATTLE_TEST("Aerilate doesn't affect Tera Starstorm's type"); -TO_DO_BATTLE_TEST("Normalize makes Flying Press do Normal/Flying damage"); -TO_DO_BATTLE_TEST("Normalize doesn't affect Terrain Pulse's type"); -TO_DO_BATTLE_TEST("Normalize doesn't affect damaging Z-Move types"); +SINGLE_BATTLE_TEST("Normalize doesn't change Tera Blast's type when Terastallized") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_TERA_BLAST) == EFFECT_TERA_BLAST); + ASSUME(GetMoveType(MOVE_TERA_BLAST) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_MISDREAVUS, 0) == TYPE_GHOST); + PLAYER(SPECIES_SKITTY) { Ability(ABILITY_NORMALIZE); TeraType(TYPE_DARK); } + OPPONENT(SPECIES_MISDREAVUS); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + MESSAGE("It's super effective!"); + } +} + +SINGLE_BATTLE_TEST("Normalize makes Flying Press do Normal/Flying damage") +{ + enum Ability ability; + PARAMETRIZE { ability = ABILITY_CUTE_CHARM; } + PARAMETRIZE { ability = ABILITY_NORMALIZE; } + GIVEN { + ASSUME(GetSpeciesType(SPECIES_GOLEM, 0) == TYPE_ROCK || GetSpeciesType(SPECIES_GOLEM, 1) == TYPE_ROCK); + PLAYER(SPECIES_SKITTY) { Ability(ability); Moves(MOVE_FLYING_PRESS); } + OPPONENT(SPECIES_GOLEM); + } WHEN { + TURN { MOVE(player, MOVE_FLYING_PRESS); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FLYING_PRESS, player); + if (ability == ABILITY_NORMALIZE) + MESSAGE("It's not very effective…"); + else + NOT { MESSAGE("It's not very effective…"); } + } +} + +SINGLE_BATTLE_TEST("Normalize doesn't affect Terrain Pulse's type") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_TERRAIN_PULSE) == EFFECT_TERRAIN_PULSE); + ASSUME(GetMoveType(MOVE_TERRAIN_PULSE) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND); + PLAYER(SPECIES_SKITTY) { Ability(ABILITY_NORMALIZE); } + OPPONENT(SPECIES_SANDSHREW); + } WHEN { + TURN { MOVE(opponent, MOVE_ELECTRIC_TERRAIN); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_TERRAIN_PULSE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIC_TERRAIN, opponent); + NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_TERRAIN_PULSE, player); } + MESSAGE("It doesn't affect the opposing Sandshrew…"); + } +} + +SINGLE_BATTLE_TEST("Normalize doesn't affect damaging Z-Move types") +{ + GIVEN { + ASSUME(GetMoveType(MOVE_WATER_GUN) == TYPE_WATER); + ASSUME(GetSpeciesType(SPECIES_GOLEM, 0) == TYPE_ROCK || GetSpeciesType(SPECIES_GOLEM, 1) == TYPE_ROCK); + PLAYER(SPECIES_SKITTY) { Ability(ABILITY_NORMALIZE); Item(ITEM_WATERIUM_Z); Moves(MOVE_WATER_GUN); } + OPPONENT(SPECIES_GOLEM); + } WHEN { + TURN { MOVE(player, MOVE_WATER_GUN, gimmick: GIMMICK_Z_MOVE); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HYDRO_VORTEX, player); + MESSAGE("It's super effective!"); + } +} diff --git a/test/battle/ability/pickpocket.c b/test/battle/ability/pickpocket.c index 1e8ec6a52..32f4d0d26 100644 --- a/test/battle/ability/pickpocket.c +++ b/test/battle/ability/pickpocket.c @@ -1,4 +1,312 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Pickpocket (Ability) test titles") +ASSUMPTIONS +{ + ASSUME(MoveMakesContact(MOVE_BREAKING_SWIPE)); + ASSUME(MoveMakesContact(MOVE_SCRATCH)); +} + +DOUBLE_BATTLE_TEST("Pickpocket checks contact/effect per target for spread moves") +{ + GIVEN { + ASSUME(GetSpeciesType(SPECIES_CLEFAIRY, 0) == TYPE_FAIRY); + ASSUME(GetMoveType(MOVE_BREAKING_SWIPE) == TYPE_DRAGON); + ASSUME(GetMoveTarget(MOVE_BREAKING_SWIPE) == TARGET_BOTH); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_MAGOST_BERRY); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_SNEASEL) { Ability(ABILITY_PICKPOCKET); } + OPPONENT(SPECIES_CLEFAIRY); + } WHEN { + TURN { MOVE(playerLeft, MOVE_BREAKING_SWIPE); } + } SCENE { + ABILITY_POPUP(opponentLeft, ABILITY_PICKPOCKET); + MESSAGE("The opposing Sneasel stole Wobbuffet's Magost Berry!"); + } THEN { + EXPECT(opponentLeft->item == ITEM_MAGOST_BERRY); + EXPECT(playerLeft->item == ITEM_NONE); + } +} + +DOUBLE_BATTLE_TEST("Pickpocket activates for the fastest itemless target when both are hit by a contact spread move") +{ + GIVEN { + ASSUME(GetMoveTarget(MOVE_BREAKING_SWIPE) == TARGET_BOTH); + PLAYER(SPECIES_WOBBUFFET) { Speed(20); Item(ITEM_MAGOST_BERRY); } + PLAYER(SPECIES_WYNAUT) { Speed(10); } + OPPONENT(SPECIES_SNEASEL) { Speed(40); Ability(ABILITY_PICKPOCKET); } + OPPONENT(SPECIES_SNEASEL) { Speed(30); Ability(ABILITY_PICKPOCKET); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_BREAKING_SWIPE); } + } SCENE { + ABILITY_POPUP(opponentLeft, ABILITY_PICKPOCKET); + MESSAGE("The opposing Sneasel stole Wobbuffet's Magost Berry!"); + } THEN { + EXPECT(opponentLeft->item == ITEM_MAGOST_BERRY); + EXPECT(opponentRight->item == ITEM_NONE); + EXPECT(playerLeft->item == ITEM_NONE); + } +} + +SINGLE_BATTLE_TEST("Pickpocket steals the attacker's item unless it already has one") +{ + bool32 targetHasItem; + PARAMETRIZE { targetHasItem = FALSE; } + PARAMETRIZE { targetHasItem = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_MAGOST_BERRY); } + OPPONENT(SPECIES_SNEASEL) { Ability(ABILITY_PICKPOCKET); Item(targetHasItem ? ITEM_EVIOLITE : ITEM_NONE); } + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH); } + } SCENE { + if (targetHasItem) { + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_PICKPOCKET); + MESSAGE("The opposing Sneasel stole Wobbuffet's Magost Berry!"); + } + } else { + ABILITY_POPUP(opponent, ABILITY_PICKPOCKET); + MESSAGE("The opposing Sneasel stole Wobbuffet's Magost Berry!"); + } + } THEN { + if (targetHasItem) { + EXPECT(opponent->item == ITEM_EVIOLITE); + EXPECT(player->item == ITEM_MAGOST_BERRY); + } else { + EXPECT(opponent->item == ITEM_MAGOST_BERRY); + EXPECT(player->item == ITEM_NONE); + } + } +} + +SINGLE_BATTLE_TEST("Pickpocket does not activate if the user faints") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_MAGOST_BERRY); } + OPPONENT(SPECIES_SNEASEL) { Ability(ABILITY_PICKPOCKET); HP(1); } + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_PICKPOCKET); + MESSAGE("The opposing Sneasel stole Wobbuffet's Magost Berry!"); + } + MESSAGE("The opposing Sneasel fainted!"); + } THEN { + EXPECT(opponent->item == ITEM_NONE); + EXPECT(player->item == ITEM_MAGOST_BERRY); + } +} + +SINGLE_BATTLE_TEST("Pickpocket cannot steal from Sticky Hold") +{ + GIVEN { + PLAYER(SPECIES_GRIMER) { Ability(ABILITY_STICKY_HOLD); Item(ITEM_MAGOST_BERRY); } + OPPONENT(SPECIES_SNEASEL) { Ability(ABILITY_PICKPOCKET); } + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_PICKPOCKET); + ABILITY_POPUP(player, ABILITY_STICKY_HOLD); + MESSAGE("Grimer's item cannot be removed!"); + } THEN { + EXPECT(opponent->item == ITEM_NONE); + EXPECT(player->item == ITEM_MAGOST_BERRY); + } +} + +SINGLE_BATTLE_TEST("Pickpocket cannot steal restricted held items") +{ + GIVEN { + ASSUME(gItemsInfo[ITEM_NORMALIUM_Z].holdEffect == HOLD_EFFECT_Z_CRYSTAL); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); } + OPPONENT(SPECIES_SNEASEL) { Ability(ABILITY_PICKPOCKET); } + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH); } + } SCENE { + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_PICKPOCKET); + } + } THEN { + EXPECT(opponent->item == ITEM_NONE); + EXPECT(player->item == ITEM_NORMALIUM_Z); + } +} + +SINGLE_BATTLE_TEST("Pickpocket activates after the final hit of a multi-strike move") +{ + GIVEN { + ASSUME(IsMultiHitMove(MOVE_FURY_SWIPES)); + ASSUME(MoveMakesContact(MOVE_FURY_SWIPES)); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_MAGOST_BERRY); } + OPPONENT(SPECIES_SNEASEL) { Ability(ABILITY_PICKPOCKET); } + } WHEN { + TURN { MOVE(player, MOVE_FURY_SWIPES, WITH_RNG(RNG_HITS, 3)); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FURY_SWIPES, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FURY_SWIPES, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FURY_SWIPES, player); + MESSAGE("The Pokémon was hit 3 time(s)!"); + ABILITY_POPUP(opponent, ABILITY_PICKPOCKET); + MESSAGE("The opposing Sneasel stole Wobbuffet's Magost Berry!"); + } THEN { + EXPECT(opponent->item == ITEM_MAGOST_BERRY); + EXPECT(player->item == ITEM_NONE); + } +} + +SINGLE_BATTLE_TEST("Pickpocket activates after Magician steals an item") +{ + GIVEN { + PLAYER(SPECIES_DELPHOX) { Ability(ABILITY_MAGICIAN); } + OPPONENT(SPECIES_SNEASEL) { Ability(ABILITY_PICKPOCKET); Item(ITEM_MAGOST_BERRY); } + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH); } + } SCENE { + ABILITY_POPUP(player, ABILITY_MAGICIAN); + MESSAGE("Delphox stole the opposing Sneasel's Magost Berry!"); + ABILITY_POPUP(opponent, ABILITY_PICKPOCKET); + MESSAGE("The opposing Sneasel stole Delphox's Magost Berry!"); + } THEN { + EXPECT(opponent->item == ITEM_MAGOST_BERRY); + EXPECT(player->item == ITEM_NONE); + } +} + +SINGLE_BATTLE_TEST("Pickpocket activates after Sticky Barb transfers") +{ + GIVEN { + ASSUME(gItemsInfo[ITEM_STICKY_BARB].holdEffect == HOLD_EFFECT_STICKY_BARB); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_SNEASEL) { Ability(ABILITY_PICKPOCKET); Item(ITEM_STICKY_BARB); } + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH); } + } SCENE { + MESSAGE("The Sticky Barb attached itself to Wobbuffet!"); + ABILITY_POPUP(opponent, ABILITY_PICKPOCKET); + MESSAGE("The opposing Sneasel stole Wobbuffet's Sticky Barb!"); + } THEN { + EXPECT(opponent->item == ITEM_STICKY_BARB); + EXPECT(player->item == ITEM_NONE); + } +} + +SINGLE_BATTLE_TEST("Pickpocket activates after Thief or Covet steals an item") +{ + u16 move; + PARAMETRIZE { move = MOVE_THIEF; } + PARAMETRIZE { move = MOVE_COVET; } + GIVEN { + ASSUME(GetMoveEffect(move) == EFFECT_STEAL_ITEM); + ASSUME(MoveMakesContact(move)); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_SNEASEL) { Ability(ABILITY_PICKPOCKET); Item(ITEM_MAGOST_BERRY); } + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + MESSAGE("Wobbuffet stole the opposing Sneasel's Magost Berry!"); + ABILITY_POPUP(opponent, ABILITY_PICKPOCKET); + MESSAGE("The opposing Sneasel stole Wobbuffet's Magost Berry!"); + } THEN { + EXPECT(opponent->item == ITEM_MAGOST_BERRY); + EXPECT(player->item == ITEM_NONE); + } +} + +SINGLE_BATTLE_TEST("Pickpocket activates after Focus Sash is consumed") +{ + GIVEN { + ASSUME(MoveMakesContact(MOVE_SEISMIC_TOSS)); + ASSUME(gItemsInfo[ITEM_FOCUS_SASH].holdEffect == HOLD_EFFECT_FOCUS_SASH); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_MAGOST_BERRY); Level(100); } + OPPONENT(SPECIES_SNEASEL) { Ability(ABILITY_PICKPOCKET); Item(ITEM_FOCUS_SASH); MaxHP(6); HP(6); } + } WHEN { + TURN { MOVE(player, MOVE_SEISMIC_TOSS); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SEISMIC_TOSS, player); + MESSAGE("The opposing Sneasel hung on using its Focus Sash!"); + ABILITY_POPUP(opponent, ABILITY_PICKPOCKET); + MESSAGE("The opposing Sneasel stole Wobbuffet's Magost Berry!"); + } THEN { + EXPECT(opponent->item == ITEM_MAGOST_BERRY); + EXPECT(player->item == ITEM_NONE); + } +} + +SINGLE_BATTLE_TEST("Pickpocket activates after Knock Off, Bug Bite, or Pluck") +{ + u16 move; + PARAMETRIZE { move = MOVE_KNOCK_OFF; } + PARAMETRIZE { move = MOVE_BUG_BITE; } + PARAMETRIZE { move = MOVE_PLUCK; } + GIVEN { + ASSUME(MoveMakesContact(move)); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_MAGOST_BERRY); } + OPPONENT(SPECIES_SNEASEL) { Ability(ABILITY_PICKPOCKET); Item(ITEM_ORAN_BERRY); } + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_PICKPOCKET); + MESSAGE("The opposing Sneasel stole Wobbuffet's Magost Berry!"); + } THEN { + EXPECT(opponent->item == ITEM_MAGOST_BERRY); + EXPECT(player->item == ITEM_NONE); + } +} + +SINGLE_BATTLE_TEST("Pickpocket steals Life Orb after it activates") +{ + GIVEN { + ASSUME(gItemsInfo[ITEM_LIFE_ORB].holdEffect == HOLD_EFFECT_LIFE_ORB); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LIFE_ORB); } + OPPONENT(SPECIES_SNEASEL) { Ability(ABILITY_PICKPOCKET); } + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH); } + } SCENE { + MESSAGE("Wobbuffet was hurt by the Life Orb!"); + ABILITY_POPUP(opponent, ABILITY_PICKPOCKET); + MESSAGE("The opposing Sneasel stole Wobbuffet's Life Orb!"); + } THEN { + EXPECT(opponent->item == ITEM_LIFE_ORB); + EXPECT(player->item == ITEM_NONE); + } +} + +SINGLE_BATTLE_TEST("Pickpocket steals Shell Bell after it heals the user") +{ + GIVEN { + ASSUME(gItemsInfo[ITEM_SHELL_BELL].holdEffect == HOLD_EFFECT_SHELL_BELL); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_SHELL_BELL); MaxHP(100); HP(66); } + OPPONENT(SPECIES_SNEASEL) { Ability(ABILITY_PICKPOCKET); } + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + HP_BAR(opponent); + HP_BAR(player); + ABILITY_POPUP(opponent, ABILITY_PICKPOCKET); + MESSAGE("The opposing Sneasel stole Wobbuffet's Shell Bell!"); + } THEN { + EXPECT(opponent->item == ITEM_SHELL_BELL); + EXPECT(player->item == ITEM_NONE); + } +} + +SINGLE_BATTLE_TEST("Pickpocket does not prevent King's Rock or Razor Fang flinches") +{ + GIVEN { + ASSUME(gItemsInfo[ITEM_KINGS_ROCK].holdEffect == HOLD_EFFECT_FLINCH); + PLAYER(SPECIES_WOBBUFFET) { Speed(20); Item(ITEM_KINGS_ROCK); } + OPPONENT(SPECIES_SNEASEL) { Speed(10); Ability(ABILITY_PICKPOCKET); } + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH, WITH_RNG(RNG_HOLD_EFFECT_FLINCH, 1)); MOVE(opponent, MOVE_SCRATCH); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_PICKPOCKET); + MESSAGE("The opposing Sneasel stole Wobbuffet's King's Rock!"); + MESSAGE("The opposing Sneasel flinched and couldn't move!"); + } THEN { + EXPECT(opponent->item == ITEM_KINGS_ROCK); + EXPECT(player->item == ITEM_NONE); + } +} diff --git a/test/battle/ability/pixilate.c b/test/battle/ability/pixilate.c index ed9fa6591..58ae55530 100644 --- a/test/battle/ability/pixilate.c +++ b/test/battle/ability/pixilate.c @@ -138,13 +138,104 @@ SINGLE_BATTLE_TEST("Pixilate doesn't affect Hidden Power's type") } } -TO_DO_BATTLE_TEST("Pixilate doesn't override Electrify (Gen7+)"); -TO_DO_BATTLE_TEST("Pixilate doesn't override Ion Deluge (Gen7+)"); // Ion Deluge doesn't exist in Gen 8+, but we probably could assume it behaves similar to under Electrify. TODO: Test by hacking SV. -TO_DO_BATTLE_TEST("Pixilate overrides Electrify (Gen6)") -TO_DO_BATTLE_TEST("Pixilate overrides Ion Deluge (Gen6)") -TO_DO_BATTLE_TEST("Pixilate doesn't affect Tera Starstorm's type"); +SINGLE_BATTLE_TEST("Pixilate doesn't override Electrify") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_ELECTRIFY) == EFFECT_ELECTRIFY); + ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND); + PLAYER(SPECIES_SYLVEON) { Ability(ABILITY_PIXILATE); } + OPPONENT(SPECIES_SANDSHREW); + } WHEN { + TURN { MOVE(opponent, MOVE_ELECTRIFY); MOVE(player, MOVE_SCRATCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIFY, opponent); + NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); } + MESSAGE("It doesn't affect the opposing Sandshrew…"); + } +} + +SINGLE_BATTLE_TEST("Pixilate overrides Ion Deluge") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_ION_DELUGE) == EFFECT_ION_DELUGE); + ASSUME(GetSpeciesType(SPECIES_BAGON, 0) == TYPE_DRAGON || GetSpeciesType(SPECIES_BAGON, 1) == TYPE_DRAGON); + PLAYER(SPECIES_SYLVEON) { Ability(ABILITY_PIXILATE); } + OPPONENT(SPECIES_BAGON); + } WHEN { + TURN { MOVE(opponent, MOVE_ION_DELUGE); MOVE(player, MOVE_SCRATCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ION_DELUGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + MESSAGE("It's super effective!"); + } +} + +SINGLE_BATTLE_TEST("Pixilate changes Tera Blast's type when not Terastallized") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_TERA_BLAST) == EFFECT_TERA_BLAST); + ASSUME(GetMoveType(MOVE_TERA_BLAST) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_MACHOP, 0) == TYPE_FIGHTING || GetSpeciesType(SPECIES_MACHOP, 1) == TYPE_FIGHTING); + PLAYER(SPECIES_SYLVEON) { Ability(ABILITY_PIXILATE); } + OPPONENT(SPECIES_MACHOP); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + MESSAGE("It's super effective!"); + } +} + +SINGLE_BATTLE_TEST("Pixilate doesn't change Tera Blast's type when Terastallized") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_TERA_BLAST) == EFFECT_TERA_BLAST); + ASSUME(GetMoveType(MOVE_TERA_BLAST) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_MISDREAVUS, 0) == TYPE_GHOST); + PLAYER(SPECIES_SYLVEON) { Ability(ABILITY_PIXILATE); TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_MISDREAVUS); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); } + } SCENE { + NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); } + MESSAGE("It doesn't affect the opposing Misdreavus…"); + } +} + +SINGLE_BATTLE_TEST("Pixilate doesn't affect Terrain Pulse's type") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_TERRAIN_PULSE) == EFFECT_TERRAIN_PULSE); + ASSUME(GetMoveType(MOVE_TERRAIN_PULSE) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND); + PLAYER(SPECIES_SYLVEON) { Ability(ABILITY_PIXILATE); } + OPPONENT(SPECIES_SANDSHREW); + } WHEN { + TURN { MOVE(opponent, MOVE_ELECTRIC_TERRAIN); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_TERRAIN_PULSE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIC_TERRAIN, opponent); + NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_TERRAIN_PULSE, player); } + MESSAGE("It doesn't affect the opposing Sandshrew…"); + } +} + +SINGLE_BATTLE_TEST("Pixilate doesn't affect damaging Z-Move types") +{ + GIVEN { + ASSUME(GetMoveType(MOVE_SCRATCH) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_BAGON, 0) == TYPE_DRAGON || GetSpeciesType(SPECIES_BAGON, 1) == TYPE_DRAGON); + PLAYER(SPECIES_SYLVEON) { Ability(ABILITY_PIXILATE); Item(ITEM_NORMALIUM_Z); } + OPPONENT(SPECIES_BAGON); + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_Z_MOVE); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, player); + NOT { MESSAGE("It's super effective!"); } + } +} + TO_DO_BATTLE_TEST("Pixilate doesn't affect Max Strike's type"); -TO_DO_BATTLE_TEST("Pixilate doesn't affect Terrain Pulse's type"); -TO_DO_BATTLE_TEST("Pixilate doesn't affect damaging Z-Move types"); TO_DO_BATTLE_TEST("(DYNAMAX) Pixilate turns Max Strike into Max Starfall when not used by Gigantamax Alcremie"); TO_DO_BATTLE_TEST("(DYNAMAX) Pixilate doesn't turn Max Strike into Max Starfall when used by Gigantamax Alcremie, instead becoming G-Max Finale"); diff --git a/test/battle/ability/primordial_sea.c b/test/battle/ability/primordial_sea.c index 480838886..7dd3858ee 100644 --- a/test/battle/ability/primordial_sea.c +++ b/test/battle/ability/primordial_sea.c @@ -3,7 +3,7 @@ ASSUMPTIONS { - ASSUME(!IsBattleMoveStatus(MOVE_EMBER)); + ASSUME(GetMoveCategory(MOVE_EMBER) != DAMAGE_CATEGORY_STATUS); ASSUME(GetMoveType(MOVE_EMBER) == TYPE_FIRE); } @@ -32,7 +32,7 @@ SINGLE_BATTLE_TEST("Primordial Sea blocks damaging Fire-type moves") DOUBLE_BATTLE_TEST("Primordial Sea blocks damaging Fire-type moves and prints the message only once with moves hitting multiple targets") { GIVEN { - ASSUME(!IsBattleMoveStatus(MOVE_ERUPTION)); + ASSUME(GetMoveCategory(MOVE_ERUPTION) != DAMAGE_CATEGORY_STATUS); ASSUME(GetMoveType(MOVE_ERUPTION) == TYPE_FIRE); ASSUME(GetMoveTarget(MOVE_ERUPTION) == TARGET_BOTH); PLAYER(SPECIES_KYOGRE) { Item(ITEM_BLUE_ORB); Speed(5); } diff --git a/test/battle/ability/rattled.c b/test/battle/ability/rattled.c index 7e892bcda..10268120f 100644 --- a/test/battle/ability/rattled.c +++ b/test/battle/ability/rattled.c @@ -4,13 +4,13 @@ ASSUMPTIONS { ASSUME(GetMoveType(MOVE_FURY_CUTTER) == TYPE_BUG); - ASSUME(!IsBattleMoveStatus(MOVE_FURY_CUTTER)); + ASSUME(GetMoveCategory(MOVE_FURY_CUTTER) != DAMAGE_CATEGORY_STATUS); ASSUME(GetMoveType(MOVE_FEINT_ATTACK) == TYPE_DARK); - ASSUME(!IsBattleMoveStatus(MOVE_FEINT_ATTACK)); + ASSUME(GetMoveCategory(MOVE_FEINT_ATTACK) != DAMAGE_CATEGORY_STATUS); ASSUME(GetMoveType(MOVE_SHADOW_PUNCH) == TYPE_GHOST); - ASSUME(!IsBattleMoveStatus(MOVE_SHADOW_PUNCH)); + ASSUME(GetMoveCategory(MOVE_SHADOW_PUNCH) != DAMAGE_CATEGORY_STATUS); ASSUME(GetMoveType(MOVE_SCRATCH) == TYPE_NORMAL); - ASSUME(!IsBattleMoveStatus(MOVE_SCRATCH)); + ASSUME(GetMoveCategory(MOVE_SCRATCH) != DAMAGE_CATEGORY_STATUS); } SINGLE_BATTLE_TEST("Rattled boosts speed by 1 when hit by Bug, Dark or Ghost type move") diff --git a/test/battle/ability/refrigerate.c b/test/battle/ability/refrigerate.c index 834254f26..15c009ed7 100644 --- a/test/battle/ability/refrigerate.c +++ b/test/battle/ability/refrigerate.c @@ -137,13 +137,104 @@ SINGLE_BATTLE_TEST("Refrigerate doesn't affect Hidden Power's type") } } -TO_DO_BATTLE_TEST("Refrigerate doesn't override Electrify (Gen7+)"); // No mon with Refrigerate exists in Gen8+, but probably behaves similar to Pixilate, which does. -TO_DO_BATTLE_TEST("Refrigerate doesn't override Ion Deluge (Gen7+)"); // Ion Deluge doesn't exist in Gen 8+, but we probably could assume it behaves similar to under Electrify. TODO: Test by hacking SV. -TO_DO_BATTLE_TEST("Refrigerate overrides Electrify (Gen6)") -TO_DO_BATTLE_TEST("Refrigerate overrides Ion Deluge (Gen6)") -TO_DO_BATTLE_TEST("Refrigerate doesn't affect Tera Starstorm's type"); +SINGLE_BATTLE_TEST("Refrigerate doesn't override Electrify") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_ELECTRIFY) == EFFECT_ELECTRIFY); + ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND); + PLAYER(SPECIES_AMAURA) { Ability(ABILITY_REFRIGERATE); } + OPPONENT(SPECIES_SANDSHREW); + } WHEN { + TURN { MOVE(opponent, MOVE_ELECTRIFY); MOVE(player, MOVE_SCRATCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIFY, opponent); + NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); } + MESSAGE("It doesn't affect the opposing Sandshrew…"); + } +} + +SINGLE_BATTLE_TEST("Refrigerate overrides Ion Deluge") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_ION_DELUGE) == EFFECT_ION_DELUGE); + ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND); + PLAYER(SPECIES_AMAURA) { Ability(ABILITY_REFRIGERATE); } + OPPONENT(SPECIES_SANDSHREW); + } WHEN { + TURN { MOVE(opponent, MOVE_ION_DELUGE); MOVE(player, MOVE_SCRATCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ION_DELUGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + MESSAGE("It's super effective!"); + } +} + +SINGLE_BATTLE_TEST("Refrigerate changes Tera Blast's type when not Terastallized") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_TERA_BLAST) == EFFECT_TERA_BLAST); + ASSUME(GetMoveType(MOVE_TERA_BLAST) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_CHARMANDER, 0) == TYPE_FIRE || GetSpeciesType(SPECIES_CHARMANDER, 1) == TYPE_FIRE); + PLAYER(SPECIES_AMAURA) { Ability(ABILITY_REFRIGERATE); } + OPPONENT(SPECIES_CHARMANDER); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + MESSAGE("It's not very effective…"); + } +} + +SINGLE_BATTLE_TEST("Refrigerate doesn't change Tera Blast's type when Terastallized") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_TERA_BLAST) == EFFECT_TERA_BLAST); + ASSUME(GetMoveType(MOVE_TERA_BLAST) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_MISDREAVUS, 0) == TYPE_GHOST); + PLAYER(SPECIES_AMAURA) { Ability(ABILITY_REFRIGERATE); TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_MISDREAVUS); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); } + } SCENE { + NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); } + MESSAGE("It doesn't affect the opposing Misdreavus…"); + } +} + +SINGLE_BATTLE_TEST("Refrigerate doesn't affect Terrain Pulse's type") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_TERRAIN_PULSE) == EFFECT_TERRAIN_PULSE); + ASSUME(GetMoveType(MOVE_TERRAIN_PULSE) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND); + PLAYER(SPECIES_AMAURA) { Ability(ABILITY_REFRIGERATE); } + OPPONENT(SPECIES_SANDSHREW); + } WHEN { + TURN { MOVE(opponent, MOVE_ELECTRIC_TERRAIN); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_TERRAIN_PULSE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIC_TERRAIN, opponent); + NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_TERRAIN_PULSE, player); } + MESSAGE("It doesn't affect the opposing Sandshrew…"); + } +} + +SINGLE_BATTLE_TEST("Refrigerate doesn't affect damaging Z-Move types") +{ + GIVEN { + ASSUME(GetMoveType(MOVE_SCRATCH) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_BAGON, 0) == TYPE_DRAGON || GetSpeciesType(SPECIES_BAGON, 1) == TYPE_DRAGON); + PLAYER(SPECIES_AMAURA) { Ability(ABILITY_REFRIGERATE); Item(ITEM_NORMALIUM_Z); } + OPPONENT(SPECIES_BAGON); + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_Z_MOVE); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, player); + NOT { MESSAGE("It's super effective!"); } + } +} + TO_DO_BATTLE_TEST("Refrigerate doesn't affect Max Strike's type"); -TO_DO_BATTLE_TEST("Refrigerate doesn't affect Terrain Pulse's type"); -TO_DO_BATTLE_TEST("Refrigerate doesn't affect damaging Z-Move types"); TO_DO_BATTLE_TEST("(DYNAMAX) Refrigerate turns Max Strike into Max Hailstorm when not used by Gigantamax Lapras"); //TO_DO_BATTLE_TEST("(DYNAMAX) Refrigerate doesn't turn Max Strike into Max Hailstorm when used by Gigantamax Lapras, instead becoming G-Max Resonance"); // Marked in Bulbapedia as "needs research", so this assumes that it behaves like Pixilate. diff --git a/test/battle/ability/stamina.c b/test/battle/ability/stamina.c index db6558336..5a6a299cc 100644 --- a/test/battle/ability/stamina.c +++ b/test/battle/ability/stamina.c @@ -24,8 +24,6 @@ SINGLE_BATTLE_TEST("Stamina raises Defense by 1 when hit by a move") PARAMETRIZE { move = MOVE_GUST; } GIVEN { - ASSUME(!IsBattleMoveStatus(MOVE_SCRATCH)); - ASSUME(!IsBattleMoveStatus(MOVE_GUST)); ASSUME(GetMoveCategory(MOVE_GUST) == DAMAGE_CATEGORY_SPECIAL); ASSUME(GetMoveCategory(MOVE_SCRATCH) == DAMAGE_CATEGORY_PHYSICAL); PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_STAMINA); } diff --git a/test/battle/ability/weak_armor.c b/test/battle/ability/weak_armor.c index 1e55c8454..a6d6b070d 100644 --- a/test/battle/ability/weak_armor.c +++ b/test/battle/ability/weak_armor.c @@ -3,8 +3,6 @@ ASSUMPTIONS { - ASSUME(!IsBattleMoveStatus(MOVE_SCRATCH)); - ASSUME(!IsBattleMoveStatus(MOVE_GUST)); ASSUME(GetMoveCategory(MOVE_GUST) == DAMAGE_CATEGORY_SPECIAL); ASSUME(GetMoveCategory(MOVE_SCRATCH) == DAMAGE_CATEGORY_PHYSICAL); } diff --git a/test/battle/ability/wind_power.c b/test/battle/ability/wind_power.c index c938a8f12..7fa91bf17 100644 --- a/test/battle/ability/wind_power.c +++ b/test/battle/ability/wind_power.c @@ -3,14 +3,14 @@ ASSUMPTIONS { - ASSUME(!IsBattleMoveStatus(MOVE_NUZZLE)); + ASSUME(GetMoveCategory(MOVE_NUZZLE) != DAMAGE_CATEGORY_STATUS); ASSUME(GetMoveType(MOVE_NUZZLE) == TYPE_ELECTRIC); - ASSUME(!IsBattleMoveStatus(MOVE_SCRATCH)); + ASSUME(GetMoveCategory(MOVE_SCRATCH) != DAMAGE_CATEGORY_STATUS); ASSUME(!IsWindMove(MOVE_SCRATCH)); - ASSUME(!IsBattleMoveStatus(MOVE_AIR_CUTTER)); + ASSUME(GetMoveCategory(MOVE_AIR_CUTTER) != DAMAGE_CATEGORY_STATUS); ASSUME(GetMoveTarget(MOVE_AIR_CUTTER) == TARGET_BOTH); ASSUME(IsWindMove(MOVE_AIR_CUTTER)); - ASSUME(!IsBattleMoveStatus(MOVE_PETAL_BLIZZARD)); + ASSUME(GetMoveCategory(MOVE_PETAL_BLIZZARD) != DAMAGE_CATEGORY_STATUS); ASSUME(GetMoveTarget(MOVE_PETAL_BLIZZARD) == TARGET_FOES_AND_ALLY); ASSUME(IsWindMove(MOVE_PETAL_BLIZZARD)); ASSUME(!IsWindMove(MOVE_SCRATCH)); diff --git a/test/battle/ai/ai_check_viability.c b/test/battle/ai/ai_check_viability.c index b12e3d1dc..27cc881f7 100644 --- a/test/battle/ai/ai_check_viability.c +++ b/test/battle/ai/ai_check_viability.c @@ -135,6 +135,25 @@ AI_SINGLE_BATTLE_TEST("AI will only use Dream Eater if target is asleep") } } +AI_SINGLE_BATTLE_TEST("AI chooses Sleep Talk only when it will not wake up with Early Bird") +{ + enum Ability ability; + + PARAMETRIZE { ability = ABILITY_RUN_AWAY; } + PARAMETRIZE { ability = ABILITY_EARLY_BIRD; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_DODRIO) { Ability(ability); Status1(STATUS1_SLEEP_TURN(2)); Moves(MOVE_SLEEP_TALK, MOVE_TACKLE); } + } WHEN { + if (ability == ABILITY_EARLY_BIRD) + TURN { EXPECT_MOVE(opponent, MOVE_TACKLE); } + else + TURN { EXPECT_MOVE(opponent, MOVE_SLEEP_TALK); } + } +} + AI_SINGLE_BATTLE_TEST("AI sees increased base power of Spit Up") { GIVEN { @@ -397,6 +416,51 @@ AI_SINGLE_BATTLE_TEST("AI uses Trick Room (singles)") } } +AI_SINGLE_BATTLE_TEST("AI uses Tailwind to trigger Wind Rider (Single)") +{ + bool32 expectTailwind; + u16 tailwindSpecies; + enum Ability tailwindAbility; + + PARAMETRIZE { tailwindSpecies = SPECIES_BRAMBLEGHAST; tailwindAbility = ABILITY_WIND_RIDER; expectTailwind = TRUE; } + PARAMETRIZE { tailwindSpecies = SPECIES_BRAMBLEGHAST; tailwindAbility = ABILITY_INFILTRATOR; expectTailwind = FALSE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_TAILWIND) == EFFECT_TAILWIND); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY); + PLAYER(SPECIES_WOBBUFFET) { Speed(20); } + OPPONENT(tailwindSpecies) { Ability(tailwindAbility); Speed(9); Moves(MOVE_TAILWIND, MOVE_HEADBUTT); } + } WHEN { + if (expectTailwind) + TURN { EXPECT_MOVE(opponent, MOVE_TAILWIND); } + else + TURN { NOT_EXPECT_MOVE(opponent, MOVE_TAILWIND); } + } +} + +AI_SINGLE_BATTLE_TEST("AI uses Tailwind to trigger Wind Power (Single)") +{ + bool32 expectTailwind; + u16 tailwindSpecies; + enum Ability tailwindAbility; + + PARAMETRIZE { tailwindSpecies = SPECIES_KILOWATTREL; tailwindAbility = ABILITY_WIND_POWER; expectTailwind = TRUE; } + PARAMETRIZE { tailwindSpecies = SPECIES_KILOWATTREL; tailwindAbility = ABILITY_COMPETITIVE; expectTailwind = FALSE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_TAILWIND) == EFFECT_TAILWIND); + ASSUME(GetMoveType(MOVE_THUNDERSHOCK) == TYPE_ELECTRIC); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY); + PLAYER(SPECIES_WOBBUFFET) { Speed(20); } + OPPONENT(tailwindSpecies) { Ability(tailwindAbility); Speed(9); Moves(MOVE_TAILWIND, MOVE_THUNDERSHOCK); } + } WHEN { + if (expectTailwind) + TURN { EXPECT_MOVE(opponent, MOVE_TAILWIND); } + else + TURN { NOT_EXPECT_MOVE(opponent, MOVE_TAILWIND); } + } +} + AI_SINGLE_BATTLE_TEST("AI uses Quick Guard against Quick Attack when opponent would take poison damage") { PASSES_RANDOMLY(PREDICT_MOVE_CHANCE, 100, RNG_AI_PREDICT_MOVE); diff --git a/test/battle/ai/ai_choice.c b/test/battle/ai/ai_choice.c index 410b3fc53..060f896ca 100644 --- a/test/battle/ai/ai_choice.c +++ b/test/battle/ai/ai_choice.c @@ -256,3 +256,30 @@ AI_SINGLE_BATTLE_TEST("Choiced Pokémon will only see choiced moves when conside TURN { MOVE(player, MOVE_WATER_GUN); EXPECT_SWITCH(opponent, 1); } } } + +AI_DOUBLE_BATTLE_TEST("Choiced Pokémon won't switch out if they can still affect one opposing Pokémon in doubles") +{ + u32 defendingSpecies = SPECIES_NONE; + enum Ability defendingAbility = ABILITY_NONE; + PARAMETRIZE { defendingSpecies = SPECIES_VAPOREON; defendingAbility = ABILITY_WATER_ABSORB; } + PARAMETRIZE { defendingSpecies = SPECIES_ZIGZAGOON; defendingAbility = SPECIES_ZIGZAGOON; } + + PASSES_RANDOMLY(SHOULD_SWITCH_CHOICE_LOCKED_PERCENTAGE, 100, RNG_AI_SWITCH_CHOICE_LOCKED); + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_RISKY | AI_FLAG_SMART_SWITCHING | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_MON_CHOICES); + PLAYER(SPECIES_CHARMANDER) { Level(5); Moves(MOVE_CELEBRATE); } + PLAYER(SPECIES_VAPOREON) { Ability(ABILITY_WATER_ABSORB); Moves(MOVE_CELEBRATE); } + PLAYER(defendingSpecies) { Ability(defendingAbility); SpDefense(500); Moves(MOVE_CELEBRATE); } + OPPONENT(SPECIES_VAPOREON) { Moves(MOVE_SCALD); Item(ITEM_CHOICE_SPECS); } + OPPONENT(SPECIES_VAPOREON) { Moves(MOVE_SCALD); Item(ITEM_CHOICE_SPECS); } + OPPONENT(SPECIES_ZIGZAGOON); + OPPONENT(SPECIES_ZIGZAGOON); + } WHEN { + TURN { SWITCH(playerLeft, 2); MOVE(playerRight, MOVE_CELEBRATE); EXPECT_MOVE(opponentLeft, MOVE_SCALD, target:playerLeft); EXPECT_MOVE(opponentRight, MOVE_SCALD, target:playerLeft); } + if (defendingSpecies == SPECIES_VAPOREON) + TURN { MOVE(playerLeft, MOVE_CELEBRATE); MOVE(playerRight, MOVE_CELEBRATE); EXPECT_SWITCH(opponentLeft, 3); EXPECT_MOVE(opponentRight, MOVE_SCALD); } + else + TURN { MOVE(playerLeft, MOVE_CELEBRATE); MOVE(playerRight, MOVE_CELEBRATE); EXPECT_MOVE(opponentLeft, MOVE_SCALD, target:playerLeft); EXPECT_MOVE(opponentRight, MOVE_SCALD, target:playerLeft); SEND_OUT(playerLeft, 0); } + } +} diff --git a/test/battle/ai/ai_doubles.c b/test/battle/ai/ai_doubles.c index 3844533e3..d07136576 100644 --- a/test/battle/ai/ai_doubles.c +++ b/test/battle/ai/ai_doubles.c @@ -602,7 +602,7 @@ AI_DOUBLE_BATTLE_TEST("Battler 3 has Battler 1 AI flags set correctly (doubles)" ASSUME(IsExplosionMove(MOVE_EXPLOSION)); u32 aiFlags; - u32 battler; + enum BattlerId battler; PARAMETRIZE { aiFlags = 0; battler = 1; } PARAMETRIZE { aiFlags = 0; battler = 3; } @@ -946,27 +946,86 @@ AI_DOUBLE_BATTLE_TEST("AI does not use Helping Hand on Good as Gold ally") } } -AI_DOUBLE_BATTLE_TEST("AI uses Tailwind") +AI_DOUBLE_BATTLE_TEST("AI uses Tailwind based on speed matchups") { u32 speed1, speed2, speed3, speed4; + bool32 expectTailwind; - PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 20; speed4 = 20; } - PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 5; speed4 = 5; } - PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 15; speed4 = 15; } - PARAMETRIZE { speed1 = 1; speed2 = 1; speed3 = 5; speed4 = 5; } - PARAMETRIZE { speed1 = 1; speed2 = 20; speed3 = 15; speed4 = 15; } - PARAMETRIZE { speed1 = 1; speed2 = 20; speed3 = 20; speed4 = 15; } + // All four comparisons qualify -> tailwindScore = 5 + PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 20; speed4 = 20; expectTailwind = TRUE; } + // Only the attacker flips one foe matchup -> tailwindScore = 2 + PARAMETRIZE { speed1 = 20; speed2 = 40; speed3 = 20; speed4 = 50; expectTailwind = TRUE; } + // Only the partner flips one foe matchup -> tailwindScore = 2 + PARAMETRIZE { speed1 = 10; speed2 = 29; speed3 = 50; speed4 = 15; expectTailwind = TRUE; } + // Too slow: even after doubling, still slower than both foes -> tailwindScore = 0. + PARAMETRIZE { speed1 = 40; speed2 = 40; speed3 = 10; speed4 = 10; expectTailwind = FALSE; } + // Already faster: Tailwind doesn't improve matchups -> tailwindScore = 0. + PARAMETRIZE { speed1 = 5; speed2 = 5; speed3 = 10; speed4 = 10; expectTailwind = FALSE; } + // Boundary: speed*2 == foe speed does not count -> tailwindScore = 0. + PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 10; speed4 = 30; expectTailwind = FALSE; } GIVEN { - ASSUME(GetMoveEffect(MOVE_AFTER_YOU) == EFFECT_AFTER_YOU); - ASSUME(GetMoveEffect(MOVE_TRICK_ROOM) == EFFECT_TRICK_ROOM); + ASSUME(GetMoveEffect(MOVE_TAILWIND) == EFFECT_TAILWIND); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_DOUBLE_BATTLE); PLAYER(SPECIES_WOBBUFFET) { Speed(speed1); } PLAYER(SPECIES_WOBBUFFET) { Speed(speed2); } OPPONENT(SPECIES_WOBBUFFET) { Speed(speed3); Moves(MOVE_TAILWIND, MOVE_HEADBUTT); } OPPONENT(SPECIES_WOBBUFFET) { Speed(speed4); Moves(MOVE_TAILWIND, MOVE_HEADBUTT); } } WHEN { - if (speed3 > 10) + if (expectTailwind) + TURN { EXPECT_MOVE(opponentLeft, MOVE_TAILWIND); } + else + TURN { NOT_EXPECT_MOVE(opponentLeft, MOVE_TAILWIND); } + } +} + +AI_DOUBLE_BATTLE_TEST("AI uses Tailwind to trigger Wind Rider (Doubles)") +{ + bool32 expectTailwind; + u16 tailwindSpecies, partnerSpecies; + enum Ability tailwindAbility, partnerAbility; + + PARAMETRIZE { tailwindSpecies = SPECIES_BRAMBLEGHAST; tailwindAbility = ABILITY_WIND_RIDER; partnerSpecies = SPECIES_BRAMBLEGHAST; partnerAbility = ABILITY_WIND_RIDER; expectTailwind = TRUE; } + PARAMETRIZE { tailwindSpecies = SPECIES_BRAMBLEGHAST; tailwindAbility = ABILITY_WIND_RIDER; partnerSpecies = SPECIES_BRAMBLEGHAST; partnerAbility = ABILITY_INFILTRATOR; expectTailwind = TRUE; } + PARAMETRIZE { tailwindSpecies = SPECIES_BRAMBLEGHAST; tailwindAbility = ABILITY_INFILTRATOR; partnerSpecies = SPECIES_BRAMBLEGHAST; partnerAbility = ABILITY_WIND_RIDER; expectTailwind = TRUE; } + PARAMETRIZE { tailwindSpecies = SPECIES_BRAMBLEGHAST; tailwindAbility = ABILITY_INFILTRATOR; partnerSpecies = SPECIES_BRAMBLEGHAST; partnerAbility = ABILITY_INFILTRATOR; expectTailwind = FALSE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_TAILWIND) == EFFECT_TAILWIND); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_DOUBLE_BATTLE); + PLAYER(SPECIES_WOBBUFFET) { Speed(20); } + PLAYER(SPECIES_WOBBUFFET) { Speed(20); } + OPPONENT(tailwindSpecies) { Ability(tailwindAbility); Speed(9); Moves(MOVE_TAILWIND, MOVE_HEADBUTT); } + OPPONENT(partnerSpecies) { Ability(partnerAbility); Speed(9); Moves(MOVE_HEADBUTT); } + } WHEN { + if (expectTailwind) + TURN { EXPECT_MOVE(opponentLeft, MOVE_TAILWIND); } + else + TURN { NOT_EXPECT_MOVE(opponentLeft, MOVE_TAILWIND); } + } +} + +AI_DOUBLE_BATTLE_TEST("AI uses Tailwind to trigger Wind Power (Doubles)") +{ + bool32 expectTailwind; + u16 tailwindSpecies, partnerSpecies; + enum Ability tailwindAbility, partnerAbility; + + PARAMETRIZE { tailwindSpecies = SPECIES_KILOWATTREL; tailwindAbility = ABILITY_WIND_POWER; partnerSpecies = SPECIES_KILOWATTREL; partnerAbility = ABILITY_WIND_POWER; expectTailwind = TRUE; } + PARAMETRIZE { tailwindSpecies = SPECIES_KILOWATTREL; tailwindAbility = ABILITY_WIND_POWER; partnerSpecies = SPECIES_KILOWATTREL; partnerAbility = ABILITY_COMPETITIVE; expectTailwind = TRUE; } + PARAMETRIZE { tailwindSpecies = SPECIES_KILOWATTREL; tailwindAbility = ABILITY_COMPETITIVE; partnerSpecies = SPECIES_KILOWATTREL; partnerAbility = ABILITY_WIND_POWER; expectTailwind = TRUE; } + PARAMETRIZE { tailwindSpecies = SPECIES_KILOWATTREL; tailwindAbility = ABILITY_COMPETITIVE; partnerSpecies = SPECIES_KILOWATTREL; partnerAbility = ABILITY_COMPETITIVE; expectTailwind = FALSE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_TAILWIND) == EFFECT_TAILWIND); + ASSUME(GetMoveType(MOVE_THUNDERSHOCK) == TYPE_ELECTRIC); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_DOUBLE_BATTLE); + PLAYER(SPECIES_WOBBUFFET) { Speed(20); } + PLAYER(SPECIES_WOBBUFFET) { Speed(20); } + OPPONENT(tailwindSpecies) { Ability(tailwindAbility); Speed(21); Moves(MOVE_TAILWIND, MOVE_THUNDERSHOCK); } + OPPONENT(partnerSpecies) { Ability(partnerAbility); Speed(21); Moves(MOVE_THUNDERSHOCK); } + } WHEN { + if (expectTailwind) TURN { EXPECT_MOVE(opponentLeft, MOVE_TAILWIND); } else TURN { NOT_EXPECT_MOVE(opponentLeft, MOVE_TAILWIND); } @@ -1040,3 +1099,29 @@ AI_DOUBLE_BATTLE_TEST("AI prefers to Fake Out the opponent vulnerable to flinchi TURN { EXPECT_MOVE(opponentLeft, MOVE_FAKE_OUT, target:playerRight); } } } + +AI_DOUBLE_BATTLE_TEST("AI uses Gear Up") +{ + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } + OPPONENT(SPECIES_KLINKLANG) { Ability(ABILITY_PLUS); Moves(MOVE_GEAR_UP, MOVE_WATER_GUN, MOVE_POUND); } + OPPONENT(SPECIES_KLINKLANG) { Ability(ABILITY_PLUS); Moves(MOVE_GEAR_UP, MOVE_WATER_GUN, MOVE_POUND); } + } WHEN { + TURN { EXPECT_MOVE(opponentLeft, MOVE_GEAR_UP); } + } +} + +AI_DOUBLE_BATTLE_TEST("AI uses Magnetic Flux") +{ + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } + OPPONENT(SPECIES_KLINK) { Ability(ABILITY_PLUS); Moves(MOVE_MAGNETIC_FLUX, MOVE_POUND); } + OPPONENT(SPECIES_KLINK) { Ability(ABILITY_PLUS); Moves(MOVE_MAGNETIC_FLUX, MOVE_POUND); } + } WHEN { + TURN { EXPECT_MOVE(opponentLeft, MOVE_MAGNETIC_FLUX); } + } +} diff --git a/test/battle/ai/ai_multi.c b/test/battle/ai/ai_multi.c index b61aefa36..4a6e44caa 100644 --- a/test/battle/ai/ai_multi.c +++ b/test/battle/ai/ai_multi.c @@ -8,7 +8,7 @@ AI_MULTI_BATTLE_TEST("AI will only explode and kill everything on the field with ASSUME(IsExplosionMove(MOVE_EXPLOSION)); u32 aiFlags; - u32 battler; + enum BattlerId battler; PARAMETRIZE { aiFlags = 0; battler = 1; } PARAMETRIZE { aiFlags = 0; battler = 3; } @@ -38,7 +38,7 @@ AI_ONE_VS_TWO_BATTLE_TEST("AI will only explode and kill everything on the field ASSUME(IsExplosionMove(MOVE_EXPLOSION)); u32 aiFlags; - u32 battler; + enum BattlerId battler; PARAMETRIZE { aiFlags = 0; battler = 1; } PARAMETRIZE { aiFlags = 0; battler = 3; } @@ -102,7 +102,7 @@ AI_TWO_VS_ONE_BATTLE_TEST("Battler 3 has Battler 1 AI flags set correctly (2v1)" ASSUME(IsExplosionMove(MOVE_EXPLOSION)); u32 aiFlags; - u32 battler; + enum BattlerId battler; PARAMETRIZE { aiFlags = 0; battler = 1; } PARAMETRIZE { aiFlags = 0; battler = 3; } @@ -235,7 +235,7 @@ AI_MULTI_BATTLE_TEST("Pollen Puff: AI correctly scores moves with EFFECT_HIT_ENE MULTI_OPPONENT_A(SPECIES_WOBBUFFET) { Speed(1); HP(50); Moves(MOVE_POLLEN_PUFF); } MULTI_OPPONENT_B(SPECIES_WOBBUFFET) { Speed(1); HP(50); Moves(MOVE_POLLEN_PUFF); } } WHEN { - TURN { + TURN { // Targeting ally SCORE_EQ_VAL(opponentLeft, MOVE_POLLEN_PUFF, AI_SCORE_DEFAULT + WEAK_EFFECT, target:opponentRight); SCORE_EQ_VAL(playerRight, MOVE_POLLEN_PUFF, AI_SCORE_DEFAULT + WEAK_EFFECT, target:playerLeft); diff --git a/test/battle/ai/ai_switching.c b/test/battle/ai/ai_switching.c index f96f64c1f..3a8b9ee19 100644 --- a/test/battle/ai/ai_switching.c +++ b/test/battle/ai/ai_switching.c @@ -403,6 +403,36 @@ AI_SINGLE_BATTLE_TEST("AI will switch out if it has no move that affects the pla } } +AI_DOUBLE_BATTLE_TEST("AI will switch out if it has no moves that affect either of the player's battlers") +{ + PASSES_RANDOMLY(SHOULD_SWITCH_ALL_MOVES_BAD_PERCENTAGE, 100, RNG_AI_SWITCH_ALL_MOVES_BAD); + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_RATTATA); + PLAYER(SPECIES_RATTATA); + OPPONENT(SPECIES_GENGAR) { Moves(MOVE_SHADOW_BALL); } + OPPONENT(SPECIES_RATTATA) { Moves(MOVE_SCRATCH); } + OPPONENT(SPECIES_RATTATA) { Moves(MOVE_SCRATCH);} + } WHEN { + TURN { EXPECT_SWITCH(opponentLeft, 2); EXPECT_MOVE(opponentRight, MOVE_SCRATCH); } + } +} + +AI_DOUBLE_BATTLE_TEST("AI will not switch out if it's moves can still affect one of the player's battlers") +{ + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_GASTLY); + PLAYER(SPECIES_RATTATA); + OPPONENT(SPECIES_GENGAR) { Moves(MOVE_SHADOW_BALL); } + OPPONENT(SPECIES_RATTATA) { Moves(MOVE_SCRATCH); } + OPPONENT(SPECIES_RATTATA) { Moves(MOVE_SCRATCH);} + } WHEN { + TURN { EXPECT_MOVE(opponentLeft, MOVE_SHADOW_BALL); EXPECT_MOVE(opponentRight, MOVE_SCRATCH); } + } +} + + AI_SINGLE_BATTLE_TEST("When AI switches out due to having no move that affects the player, AI will send in a mon that can hit the player, even if not ideal") { GIVEN { @@ -1157,15 +1187,18 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has an PARAMETRIZE { aiMon = SPECIES_JOLTEON; absorbingAbility = ABILITY_VOLT_ABSORB; move = MOVE_THUNDERBOLT; } PARAMETRIZE { aiMon = SPECIES_ELECTIVIRE; absorbingAbility = ABILITY_MOTOR_DRIVE; move = MOVE_THUNDERBOLT; } PARAMETRIZE { aiMon = SPECIES_MANECTRIC; absorbingAbility = ABILITY_LIGHTNING_ROD; move = MOVE_THUNDERBOLT; } - PARAMETRIZE { aiMon = SPECIES_ELECTIVIRE; absorbingAbility = ABILITY_MOTOR_DRIVE; move = MOVE_THUNDERBOLT; } PARAMETRIZE { aiMon = SPECIES_AZUMARILL; absorbingAbility = ABILITY_SAP_SIPPER; move = MOVE_GIGA_DRAIN; } PARAMETRIZE { aiMon = SPECIES_ORTHWORM; absorbingAbility = ABILITY_EARTH_EATER; move = MOVE_EARTHQUAKE; } PARAMETRIZE { aiMon = SPECIES_BRONZONG; absorbingAbility = ABILITY_LEVITATE; move = MOVE_EARTHQUAKE; } PARAMETRIZE { aiMon = SPECIES_ELECTRODE; absorbingAbility = ABILITY_SOUNDPROOF; move = MOVE_HYPER_VOICE; } PARAMETRIZE { aiMon = SPECIES_CHESNAUGHT; absorbingAbility = ABILITY_BULLETPROOF; move = MOVE_SLUDGE_BOMB; } PARAMETRIZE { aiMon = SPECIES_BRAMBLEGHAST; absorbingAbility = ABILITY_WIND_RIDER; move = MOVE_HURRICANE; } + PARAMETRIZE { aiMon = SPECIES_BRAMBLEGHAST; absorbingAbility = ABILITY_WIND_RIDER; move = MOVE_HEAT_WAVE; } + PARAMETRIZE { aiMon = SPECIES_SHELLDER; absorbingAbility = ABILITY_OVERCOAT; move = MOVE_MAGIC_POWDER; } + PARAMETRIZE { aiMon = SPECIES_SHELLDER; absorbingAbility = ABILITY_OVERCOAT; move = MOVE_STUN_SPORE; } GIVEN { WITH_CONFIG(CONFIG_REDIRECT_ABILITY_IMMUNITY, GEN_5); + WITH_CONFIG(CONFIG_POWDER_OVERCOAT, GEN_6); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING); PLAYER(SPECIES_ZIGZAGOON) { Moves(move); } OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_SCRATCH); } diff --git a/test/battle/ai/can_use_all_moves.c b/test/battle/ai/can_use_all_moves.c index 78e115405..208809d9b 100644 --- a/test/battle/ai/can_use_all_moves.c +++ b/test/battle/ai/can_use_all_moves.c @@ -299,7 +299,7 @@ AI_DOUBLE_BATTLE_TEST("AI can use all moves, 301-400") switch (effect) { //TODO: AI HANDLING - case EFFECT_SHEER_COLD: // Guillotine is crashing the test entirely. + case EFFECT_OHKO: // Guillotine is crashing the test entirely. case EFFECT_WATER_SPORT: case EFFECT_LUCKY_CHANT: case EFFECT_ME_FIRST: diff --git a/test/battle/ai/check_bad_move.c b/test/battle/ai/check_bad_move.c index 62fc86001..198a8d1a2 100644 --- a/test/battle/ai/check_bad_move.c +++ b/test/battle/ai/check_bad_move.c @@ -50,6 +50,19 @@ AI_DOUBLE_BATTLE_TEST("AI will not try to lower opposing stats if target is prot } } +AI_SINGLE_BATTLE_TEST("AI sees No Guard affects semi-invulnerable moves") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_PHANTOM_FORCE) == EFFECT_SEMI_INVULNERABLE); + ASSUME(GetMovePower(MOVE_PHANTOM_FORCE) == GetMovePower(MOVE_SPECTRAL_THIEF)); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_GOLURK) { Ability(ABILITY_NO_GUARD); Moves(MOVE_DYNAMIC_PUNCH, MOVE_CELEBRATE); } + OPPONENT(SPECIES_SMEARGLE) { Moves(MOVE_PHANTOM_FORCE, MOVE_SPECTRAL_THIEF); } + } WHEN { + TURN { EXPECT_MOVE(opponent, MOVE_SPECTRAL_THIEF); } + } +} + AI_SINGLE_BATTLE_TEST("Protect: AI avoids Protect vs Unseen Fist contact (Single)") { static const enum Move protectMoves[] = diff --git a/test/battle/ai/gimmick_z_move.c b/test/battle/ai/gimmick_z_move.c index 1d344c5a4..cf1e6e3ff 100644 --- a/test/battle/ai/gimmick_z_move.c +++ b/test/battle/ai/gimmick_z_move.c @@ -1,7 +1,6 @@ #include "global.h" #include "test/battle.h" #include "battle_ai_util.h" -#include "constants/battle_z_move_effects.h" AI_SINGLE_BATTLE_TEST("AI uses Z-Moves.") { diff --git a/test/battle/form_change/end_battle.c b/test/battle/form_change/end_battle.c index d4f29e431..4ccc1c80c 100644 --- a/test/battle/form_change/end_battle.c +++ b/test/battle/form_change/end_battle.c @@ -202,7 +202,7 @@ SINGLE_BATTLE_TEST("Mimikyu Busted reverts to Disguised form upon battle end aft } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_AERIAL_ACE, opponent); ABILITY_POPUP(player, ABILITY_DISGUISE); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE_DISGUISE, player); } THEN { EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), species); } @@ -217,8 +217,8 @@ SINGLE_BATTLE_TEST("Cramorant reverts to base Form upon battle end after using S TURN { MOVE(player, MOVE_SURF); } } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE_INSTANT, player); HP_BAR(opponent); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); } THEN { EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_CRAMORANT); } @@ -259,13 +259,13 @@ SINGLE_BATTLE_TEST("Morpeko Hangry reverts to Full Belly Form upon battle end af SINGLE_BATTLE_TEST("Ogerpon reverts to the correct form upon battle end after terastallizing") { - u32 species; - PARAMETRIZE { species = SPECIES_OGERPON_TEAL; } - PARAMETRIZE { species = SPECIES_OGERPON_WELLSPRING; } - PARAMETRIZE { species = SPECIES_OGERPON_HEARTHFLAME; } - PARAMETRIZE { species = SPECIES_OGERPON_CORNERSTONE; } + u32 species, item; + PARAMETRIZE { species = SPECIES_OGERPON_TEAL; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_OGERPON_WELLSPRING; item = ITEM_WELLSPRING_MASK; } + PARAMETRIZE { species = SPECIES_OGERPON_HEARTHFLAME; item = ITEM_HEARTHFLAME_MASK; } + PARAMETRIZE { species = SPECIES_OGERPON_CORNERSTONE; item = ITEM_CORNERSTONE_MASK; } GIVEN { - PLAYER(species); + PLAYER(species) { Item(item); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); } @@ -285,3 +285,28 @@ SINGLE_BATTLE_TEST("Terapagos reverts to the correct form upon battle end after EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_TERAPAGOS_NORMAL); } } + +SINGLE_BATTLE_TEST("Power Construct Zygarde reverts to its original form upon battle end") +{ + u16 baseSpecies; + PARAMETRIZE { baseSpecies = SPECIES_ZYGARDE_10_POWER_CONSTRUCT; } + PARAMETRIZE { baseSpecies = SPECIES_ZYGARDE_50_POWER_CONSTRUCT; } + + GIVEN { + PLAYER(baseSpecies) + { + Ability(ABILITY_POWER_CONSTRUCT); + HP((GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP) / 2) + 1); + } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SCRATCH); } + } SCENE { + MESSAGE("You sense the presence of many!"); + ABILITY_POPUP(player, ABILITY_POWER_CONSTRUCT); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_POWER_CONSTRUCT, player); + + } THEN { + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), baseSpecies); + } +} diff --git a/test/battle/form_change/faint.c b/test/battle/form_change/faint.c index 4f66dc84c..b0566d1e5 100644 --- a/test/battle/form_change/faint.c +++ b/test/battle/form_change/faint.c @@ -69,3 +69,46 @@ DOUBLE_BATTLE_TEST("Causing a Forecast or Flower Gift Pokémon to faint should n } } } + +SINGLE_BATTLE_TEST("Ogerpon reverts to the correct form upon fainting after terastallizing") +{ + u32 species, item; + PARAMETRIZE { species = SPECIES_OGERPON_TEAL; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_OGERPON_WELLSPRING; item = ITEM_WELLSPRING_MASK; } + PARAMETRIZE { species = SPECIES_OGERPON_HEARTHFLAME; item = ITEM_HEARTHFLAME_MASK; } + PARAMETRIZE { species = SPECIES_OGERPON_CORNERSTONE; item = ITEM_CORNERSTONE_MASK; } + GIVEN { + PLAYER(species) { HP(1); Item(item); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { + MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); + MOVE(opponent, MOVE_SCRATCH); + SEND_OUT(player, 1); + } + TURN { USE_ITEM(player, ITEM_REVIVE, 0); } + TURN { SWITCH(player, 0); } + } THEN { + EXPECT_EQ(player->species, species); + } +} + +SINGLE_BATTLE_TEST("Terapagos reverts to the correct form upon fainting after terastallizing") +{ + GIVEN { + PLAYER(SPECIES_TERAPAGOS_NORMAL) { HP(1); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { + MOVE(player, MOVE_MEMENTO, gimmick: GIMMICK_TERA); + MOVE(opponent, MOVE_SCRATCH); + SEND_OUT(player, 1); + } + TURN { USE_ITEM(player, ITEM_REVIVE, 0); } + TURN { SWITCH(player, 0); } + } THEN { + EXPECT_EQ(player->species, SPECIES_TERAPAGOS_TERASTAL); // Not Normal form due to Tera Shift + } +} diff --git a/test/battle/form_change/gigantamax.c b/test/battle/form_change/gigantamax.c index 92e2b4922..ae34a5acf 100644 --- a/test/battle/form_change/gigantamax.c +++ b/test/battle/form_change/gigantamax.c @@ -43,3 +43,22 @@ SINGLE_BATTLE_TEST("Dynamax: Venusaur returns its base Form upon battle end afte EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_VENUSAUR); } } + +SINGLE_BATTLE_TEST("Dynamax: Venusaur returns its base Form upon fainting end after Gigantamaxing") +{ + GIVEN { + PLAYER(SPECIES_VENUSAUR) { HP(1); GigantamaxFactor(TRUE); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { + MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_DYNAMAX); + MOVE(opponent, MOVE_SCRATCH); + SEND_OUT(player, 1); + } + TURN { USE_ITEM(player, ITEM_REVIVE, 0); } + TURN { SWITCH(player, 0); } + } THEN { + EXPECT_EQ(player->species, SPECIES_VENUSAUR); + } +} diff --git a/test/battle/form_change/mega_evolution.c b/test/battle/form_change/mega_evolution.c index f246cf1cc..8e1945c55 100644 --- a/test/battle/form_change/mega_evolution.c +++ b/test/battle/form_change/mega_evolution.c @@ -216,3 +216,41 @@ SINGLE_BATTLE_TEST("Rayquaza returns its base Form upon battle end after Mega Ev EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_RAYQUAZA); } } + +SINGLE_BATTLE_TEST("Venusaur returns its base Form upon fainting end after Mega Evolving") +{ + GIVEN { + PLAYER(SPECIES_VENUSAUR) { HP(1); Item(ITEM_VENUSAURITE); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { + MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); + MOVE(opponent, MOVE_SCRATCH); + SEND_OUT(player, 1); + } + TURN { USE_ITEM(player, ITEM_REVIVE, 0); } + TURN { SWITCH(player, 0); } + } THEN { + EXPECT_EQ(player->species, SPECIES_VENUSAUR); + } +} + +SINGLE_BATTLE_TEST("Rayquaza returns its base Form upon fainting end after Mega Evolving") +{ + GIVEN { + PLAYER(SPECIES_RAYQUAZA) { HP(1); Moves(MOVE_DRAGON_ASCENT, MOVE_CELEBRATE); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { + MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); + MOVE(opponent, MOVE_SCRATCH); + SEND_OUT(player, 1); + } + TURN { USE_ITEM(player, ITEM_REVIVE, 0); } + TURN { SWITCH(player, 0); } + } THEN { + EXPECT_EQ(player->species, SPECIES_RAYQUAZA); + } +} diff --git a/test/battle/form_change/primal_reversion.c b/test/battle/form_change/primal_reversion.c index cbd7435a7..ee026560e 100644 --- a/test/battle/form_change/primal_reversion.c +++ b/test/battle/form_change/primal_reversion.c @@ -1,7 +1,7 @@ #include "global.h" #include "test/battle.h" -SINGLE_BATTLE_TEST("Primal reversion happens for Groudon only when holding Red Orb") +SINGLE_BATTLE_TEST("Primal Reversion happens for Groudon only when holding Red Orb") { u16 heldItem; PARAMETRIZE { heldItem = ITEM_NONE; } @@ -33,7 +33,7 @@ SINGLE_BATTLE_TEST("Primal reversion happens for Groudon only when holding Red O } } -SINGLE_BATTLE_TEST("Primal reversion happens for Kyogre only when holding Blue Orb") +SINGLE_BATTLE_TEST("Primal Reversion happens for Kyogre only when holding Blue Orb") { u16 heldItem; PARAMETRIZE { heldItem = ITEM_NONE; } @@ -65,7 +65,7 @@ SINGLE_BATTLE_TEST("Primal reversion happens for Kyogre only when holding Blue O } } -DOUBLE_BATTLE_TEST("Primal reversion's order is determined by Speed - opponent faster") +DOUBLE_BATTLE_TEST("Primal Reversion's order is determined by Speed - opponent faster") { GIVEN { PLAYER(SPECIES_KYOGRE) { Item(ITEM_BLUE_ORB); Speed(5); } @@ -91,7 +91,7 @@ DOUBLE_BATTLE_TEST("Primal reversion's order is determined by Speed - opponent f } } -DOUBLE_BATTLE_TEST("Primal reversion's order is determined by Speed - player faster") +DOUBLE_BATTLE_TEST("Primal Reversion's order is determined by Speed - player faster") { GIVEN { PLAYER(SPECIES_KYOGRE) { Item(ITEM_BLUE_ORB); Speed(20); } @@ -117,10 +117,10 @@ DOUBLE_BATTLE_TEST("Primal reversion's order is determined by Speed - player fas } } -SINGLE_BATTLE_TEST("Primal reversion happens after a mon is sent out after a mon is fainted") +SINGLE_BATTLE_TEST("Primal Reversion happens after a mon is sent out after a mon is fainted") { GIVEN { - ASSUME(!IsBattleMoveStatus(MOVE_SCRATCH)); + ASSUME(GetMoveCategory(MOVE_SCRATCH) != DAMAGE_CATEGORY_STATUS); PLAYER(SPECIES_WOBBUFFET) { HP(1); } PLAYER(SPECIES_GROUDON) { Item(ITEM_RED_ORB); } OPPONENT(SPECIES_WOBBUFFET); @@ -136,7 +136,7 @@ SINGLE_BATTLE_TEST("Primal reversion happens after a mon is sent out after a mon } } -SINGLE_BATTLE_TEST("Primal reversion happens after a mon is switched in") +SINGLE_BATTLE_TEST("Primal Reversion happens after a mon is switched in") { GIVEN { PLAYER(SPECIES_WOBBUFFET); @@ -153,10 +153,10 @@ SINGLE_BATTLE_TEST("Primal reversion happens after a mon is switched in") } } -SINGLE_BATTLE_TEST("Primal reversion happens after a switch-in caused by Eject Button") +SINGLE_BATTLE_TEST("Primal Reversion happens after a switch-in caused by Eject Button") { GIVEN { - ASSUME(!IsBattleMoveStatus(MOVE_SCRATCH)); + ASSUME(GetMoveCategory(MOVE_SCRATCH) != DAMAGE_CATEGORY_STATUS); ASSUME(gItemsInfo[ITEM_EJECT_BUTTON].holdEffect == HOLD_EFFECT_EJECT_BUTTON); PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_BUTTON); } PLAYER(SPECIES_GROUDON) { Item(ITEM_RED_ORB); } @@ -174,10 +174,10 @@ SINGLE_BATTLE_TEST("Primal reversion happens after a switch-in caused by Eject B } } -SINGLE_BATTLE_TEST("Primal reversion happens after a switch-in caused by Red Card") +SINGLE_BATTLE_TEST("Primal Reversion happens after a switch-in caused by Red Card") { GIVEN { - ASSUME(!IsBattleMoveStatus(MOVE_SCRATCH)); + ASSUME(GetMoveCategory(MOVE_SCRATCH) != DAMAGE_CATEGORY_STATUS); ASSUME(gItemsInfo[ITEM_RED_CARD].holdEffect == HOLD_EFFECT_RED_CARD); PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_GROUDON) { Item(ITEM_RED_ORB); } @@ -194,7 +194,7 @@ SINGLE_BATTLE_TEST("Primal reversion happens after a switch-in caused by Red Car } } -SINGLE_BATTLE_TEST("Primal reversion happens after the entry hazards damage") +SINGLE_BATTLE_TEST("Primal Reversion happens after the entry hazards damage") { GIVEN { ASSUME(GetMoveEffect(MOVE_SPIKES) == EFFECT_SPIKES); @@ -215,7 +215,7 @@ SINGLE_BATTLE_TEST("Primal reversion happens after the entry hazards damage") } } -SINGLE_BATTLE_TEST("Primal reversion happens immediately if it was brought in by U-turn") +SINGLE_BATTLE_TEST("Primal Reversion happens immediately if it was brought in by U-turn") { GIVEN { PLAYER(SPECIES_WOBBUFFET); @@ -236,7 +236,7 @@ SINGLE_BATTLE_TEST("Primal reversion happens immediately if it was brought in by } -DOUBLE_BATTLE_TEST("Primal reversion triggers for multiple battlers if multiple fainted the previous turn") +DOUBLE_BATTLE_TEST("Primal Reversion triggers for multiple battlers if multiple fainted the previous turn") { GIVEN { ASSUME(GetMoveTarget(MOVE_EARTHQUAKE) == TARGET_FOES_AND_ALLY); @@ -259,7 +259,7 @@ DOUBLE_BATTLE_TEST("Primal reversion triggers for multiple battlers if multiple } } -DOUBLE_BATTLE_TEST("Primal reversion triggers for all battlers if multiple fainted the previous turn") +DOUBLE_BATTLE_TEST("Primal Reversion triggers for all battlers if multiple fainted the previous turn") { GIVEN { ASSUME(IsExplosionMove(MOVE_EXPLOSION)); @@ -287,7 +287,7 @@ DOUBLE_BATTLE_TEST("Primal reversion triggers for all battlers if multiple faint } } -DOUBLE_BATTLE_TEST("Primal reversion and other switch-in effects trigger for all battlers if multiple fainted the previous turn") +DOUBLE_BATTLE_TEST("Primal Reversion and other switch-in effects trigger for all battlers if multiple fainted the previous turn") { GIVEN { ASSUME(IsExplosionMove(MOVE_EXPLOSION)); @@ -333,7 +333,7 @@ DOUBLE_BATTLE_TEST("Primal reversion and other switch-in effects trigger for all } } -SINGLE_BATTLE_TEST("Primal reversion is reverted upon battle end") +SINGLE_BATTLE_TEST("Primal Reversion is reverted upon battle end") { u32 species, item; PARAMETRIZE { species = SPECIES_GROUDON; item = ITEM_RED_ORB; } @@ -347,3 +347,25 @@ SINGLE_BATTLE_TEST("Primal reversion is reverted upon battle end") EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), species); } } + +SINGLE_BATTLE_TEST("Primal Reversion is NOT reverted upon fainting") +{ + u32 species, item, targetSpecies; + PARAMETRIZE { species = SPECIES_GROUDON; item = ITEM_RED_ORB; targetSpecies = SPECIES_GROUDON_PRIMAL; } + PARAMETRIZE { species = SPECIES_KYOGRE; item = ITEM_BLUE_ORB; targetSpecies = SPECIES_KYOGRE_PRIMAL; } + GIVEN { + PLAYER(species) { HP(1); Item(item); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { + MOVE(player, MOVE_CELEBRATE); + MOVE(opponent, MOVE_SCRATCH); + SEND_OUT(player, 1); + } + TURN { USE_ITEM(player, ITEM_REVIVE, 0); } + TURN { SWITCH(player, 0); } + } THEN { + EXPECT_EQ(player->species, targetSpecies); + } +} diff --git a/test/battle/form_change/ultra_burst.c b/test/battle/form_change/ultra_burst.c index fcb0efded..1db0bf7fd 100644 --- a/test/battle/form_change/ultra_burst.c +++ b/test/battle/form_change/ultra_burst.c @@ -135,3 +135,25 @@ SINGLE_BATTLE_TEST("Necrozma returns its proper Form upon battle end after Ultra EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), species); } } + +SINGLE_BATTLE_TEST("Necrozma returns its proper Form upon fainting after Ultra Bursting") +{ + u32 species; + PARAMETRIZE { species = SPECIES_NECROZMA_DUSK_MANE; } + PARAMETRIZE { species = SPECIES_NECROZMA_DAWN_WINGS; } + GIVEN { + PLAYER(species) { HP(1); Item(ITEM_ULTRANECROZIUM_Z); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { + MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_ULTRA_BURST); + MOVE(opponent, MOVE_SCRATCH); + SEND_OUT(player, 1); + } + TURN { USE_ITEM(player, ITEM_REVIVE, 0); } + TURN { SWITCH(player, 0); } + } THEN { + EXPECT_EQ(player->species, species); + } +} diff --git a/test/battle/gimmick/dynamax.c b/test/battle/gimmick/dynamax.c index 387de6ab1..60a4abeef 100644 --- a/test/battle/gimmick/dynamax.c +++ b/test/battle/gimmick/dynamax.c @@ -224,7 +224,7 @@ SINGLE_BATTLE_TEST("Dynamax: Dynamaxed Pokemon cannot be hit by OHKO moves") } SCENE { MESSAGE("Wobbuffet used Max Strike!"); MESSAGE("The opposing Machamp used Fissure!"); - MESSAGE("Wobbuffet is unaffected!"); + MESSAGE("It doesn't affect Wobbuffet…"); NONE_OF { HP_BAR(player); } } } diff --git a/test/battle/gimmick/terastal.c b/test/battle/gimmick/terastal.c index 0de100776..9df8e06f2 100644 --- a/test/battle/gimmick/terastal.c +++ b/test/battle/gimmick/terastal.c @@ -761,19 +761,19 @@ SINGLE_BATTLE_TEST("(TERA) Transformed Pokémon can't Terastalize") SINGLE_BATTLE_TEST("(TERA) Pokemon with Tera forms change upon Terastallizing") { - u32 species, targetSpecies; - PARAMETRIZE { species = SPECIES_OGERPON_TEAL; targetSpecies = SPECIES_OGERPON_TEAL_TERA; } - PARAMETRIZE { species = SPECIES_OGERPON_WELLSPRING; targetSpecies = SPECIES_OGERPON_WELLSPRING_TERA; } - PARAMETRIZE { species = SPECIES_OGERPON_HEARTHFLAME; targetSpecies = SPECIES_OGERPON_HEARTHFLAME_TERA; } - PARAMETRIZE { species = SPECIES_OGERPON_CORNERSTONE; targetSpecies = SPECIES_OGERPON_CORNERSTONE_TERA; } - PARAMETRIZE { species = SPECIES_TERAPAGOS_TERASTAL; targetSpecies = SPECIES_TERAPAGOS_STELLAR; } + u32 species, target, item; + PARAMETRIZE { species = SPECIES_OGERPON_TEAL; target = SPECIES_OGERPON_TEAL_TERA; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_OGERPON_WELLSPRING; target = SPECIES_OGERPON_WELLSPRING_TERA; item = ITEM_WELLSPRING_MASK; } + PARAMETRIZE { species = SPECIES_OGERPON_HEARTHFLAME; target = SPECIES_OGERPON_HEARTHFLAME_TERA; item = ITEM_HEARTHFLAME_MASK; } + PARAMETRIZE { species = SPECIES_OGERPON_CORNERSTONE; target = SPECIES_OGERPON_CORNERSTONE_TERA; item = ITEM_CORNERSTONE_MASK; } + PARAMETRIZE { species = SPECIES_TERAPAGOS_TERASTAL; target = SPECIES_TERAPAGOS_STELLAR; item = ITEM_NONE; } GIVEN { - PLAYER(species); + PLAYER(species) { Item(item); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); } } THEN { - EXPECT_EQ(player->species, targetSpecies); + EXPECT_EQ(player->species, target); } } diff --git a/test/battle/gimmick/zmove.c b/test/battle/gimmick/zmove.c index d95322c6d..9954887f0 100644 --- a/test/battle/gimmick/zmove.c +++ b/test/battle/gimmick/zmove.c @@ -1,6 +1,5 @@ #include "global.h" #include "test/battle.h" -#include "constants/battle_z_move_effects.h" // Basic Functionality SINGLE_BATTLE_TEST("(Z-MOVE) Z-Moves do not retain priority") diff --git a/test/battle/hold_effect/booster_energy.c b/test/battle/hold_effect/booster_energy.c index 6803ffc4c..6f128c9c7 100644 --- a/test/battle/hold_effect/booster_energy.c +++ b/test/battle/hold_effect/booster_energy.c @@ -230,20 +230,6 @@ SINGLE_BATTLE_TEST("Booster Energy increases special defense by 30% if it is the } } -SINGLE_BATTLE_TEST("Booster Energy can't be flung if a Paradox species is involved") -{ - GIVEN { - ASSUME(gSpeciesInfo[SPECIES_IRON_MOTH].isParadox == TRUE); - PLAYER(SPECIES_IRON_MOTH); - OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_BOOSTER_ENERGY); } - } WHEN { - TURN { MOVE(opponent, MOVE_FLING); } - } SCENE { - NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_FLING, opponent); - MESSAGE("But it failed!"); - } -} - SINGLE_BATTLE_TEST("Booster Energy can't be tricked if a Paradox species is involved") { GIVEN { diff --git a/test/battle/hold_effect/restore_hp.c b/test/battle/hold_effect/restore_hp.c index 52dc04599..d472f1ad0 100644 --- a/test/battle/hold_effect/restore_hp.c +++ b/test/battle/hold_effect/restore_hp.c @@ -82,3 +82,27 @@ SINGLE_BATTLE_TEST("Sitrus Berry restores HP immediately after Leech Seed damage HP_BAR(player); } } + +SINGLE_BATTLE_TEST("Sitrus Berry restores HP before Shields Down form change") +{ + GIVEN { + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_MINIOR_CORE) { + Ability(ABILITY_SHIELDS_DOWN); HP(53); MaxHP(101); Item(ITEM_SITRUS_BERRY); + } + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_SHIELDS_DOWN); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + HP_BAR(opponent); // Scratch + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_SHIELDS_DOWN); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent); + } + HP_BAR(opponent); // Heal + } THEN { + EXPECT_EQ(opponent->species, SPECIES_MINIOR_METEOR); + } +} diff --git a/test/battle/move_animations/all_anims.c b/test/battle/move_animations/all_anims.c index 78f870d02..435be8f9e 100644 --- a/test/battle/move_animations/all_anims.c +++ b/test/battle/move_animations/all_anims.c @@ -310,7 +310,7 @@ static void WhenSingles(enum Move move, struct BattlePokemon *attacker, struct B MOVE(attacker, move); MOVE(defender, MOVE_SWORDS_DANCE); } - else if (effect == EFFECT_OHKO || effect == EFFECT_SHEER_COLD) + else if (effect == EFFECT_OHKO) { // defender needs to send out a different team member MOVE(attacker, move); SEND_OUT(defender, 1); @@ -526,7 +526,7 @@ static void DoublesWhen(enum Move move, struct BattlePokemon *attacker, struct B MOVE(attacker, move, target: target); MOVE(target, MOVE_SWORDS_DANCE); } - else if (effect == EFFECT_OHKO || effect == EFFECT_SHEER_COLD) + else if (effect == EFFECT_OHKO) { // Opponent needs to send out a different team member MOVE(attacker, move, target: target); SEND_OUT(target, 2); diff --git a/test/battle/move_effect/beak_blast.c b/test/battle/move_effect/beak_blast.c index 507d6cfdd..e6edc59af 100644 --- a/test/battle/move_effect/beak_blast.c +++ b/test/battle/move_effect/beak_blast.c @@ -87,7 +87,6 @@ SINGLE_BATTLE_TEST("Beak Blast burns only when contact moves are used") OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(opponent, move); MOVE(player, MOVE_BEAK_BLAST); } - TURN {} } SCENE { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_BEAK_BLAST_SETUP, player); MESSAGE("Wobbuffet started heating up its beak!"); @@ -112,6 +111,35 @@ SINGLE_BATTLE_TEST("Beak Blast burns only when contact moves are used") } } +SINGLE_BATTLE_TEST("Beak Blast doesn't burn when charging a two turn move") +{ + u32 move; + PARAMETRIZE { move = MOVE_BOUNCE; } + PARAMETRIZE { move = MOVE_DIG; } + + GIVEN { + ASSUME(MoveMakesContact(MOVE_BOUNCE)); + ASSUME(MoveMakesContact(MOVE_DIG)); + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_BOUNCE)].twoTurnEffect); + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_BEAK_BLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_BEAK_BLAST_SETUP, player); + MESSAGE("Wobbuffet started heating up its beak!"); + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + + NONE_OF { + HP_BAR(player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponent); + MESSAGE("The opposing Wobbuffet was burned!"); + STATUS_ICON(opponent, burn: TRUE); + } + } +} + SINGLE_BATTLE_TEST("Beak Blast doesn't burn fire types") { GIVEN { diff --git a/test/battle/move_effect/bide.c b/test/battle/move_effect/bide.c index d5c543cc0..34c2ec812 100644 --- a/test/battle/move_effect/bide.c +++ b/test/battle/move_effect/bide.c @@ -35,3 +35,4 @@ SINGLE_BATTLE_TEST("Bide deals twice the taken damage over two turns") TO_DO_BATTLE_TEST("Bide hits the last Pokémon that attacked the user, even allies"); TO_DO_BATTLE_TEST("Bide has +1 priority if called via a different move"); // Gen 5 onwards + diff --git a/test/battle/move_effect/charge.c b/test/battle/move_effect/charge.c index 610230de1..7207c4bd6 100644 --- a/test/battle/move_effect/charge.c +++ b/test/battle/move_effect/charge.c @@ -3,7 +3,7 @@ ASSUMPTIONS { - ASSUME(!IsBattleMoveStatus(MOVE_THUNDERBOLT)); + ASSUME(GetMoveCategory(MOVE_THUNDERBOLT) != DAMAGE_CATEGORY_STATUS); ASSUME(GetMoveType(MOVE_THUNDERBOLT) == TYPE_ELECTRIC); } @@ -108,7 +108,7 @@ SINGLE_BATTLE_TEST("Charge's effect is removed regardless if the next move is El s16 damage[2]; GIVEN { ASSUME(GetMoveType(MOVE_SCRATCH) != TYPE_ELECTRIC); - ASSUME(!IsBattleMoveStatus(MOVE_SCRATCH)); + ASSUME(GetMoveCategory(MOVE_SCRATCH) != DAMAGE_CATEGORY_STATUS); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { diff --git a/test/battle/move_effect/fling.c b/test/battle/move_effect/fling.c index b9c82ab0b..eccf289e4 100644 --- a/test/battle/move_effect/fling.c +++ b/test/battle/move_effect/fling.c @@ -199,7 +199,7 @@ SINGLE_BATTLE_TEST("Fling - Item does not get blocked by Unnerve if it isn't a b } } -SINGLE_BATTLE_TEST("Fling doesn't consume the item if Pokémon is asleep/frozen/paralyzed") +SINGLE_BATTLE_TEST("Fling doesn't consume the item if the user is asleep/frozen/paralyzed") { u32 status; enum Item item; @@ -514,7 +514,7 @@ SINGLE_BATTLE_TEST("Fling deals damage based on items fling power") } } -SINGLE_BATTLE_TEST("Fling deals damage based on a TM's move power") +SINGLE_BATTLE_TEST("Fling deals damage based on a TM's move power if reusable or fails if breakable") { s16 damage[2]; @@ -527,33 +527,49 @@ SINGLE_BATTLE_TEST("Fling deals damage based on a TM's move power") TURN { MOVE(player, MOVE_FLING); } TURN { MOVE(player, MOVE_EGG_BOMB); } } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_FLING, player); - HP_BAR(opponent, captureDamage: &damage[0]); + if (GetItemImportance(ITEM_TM_EARTHQUAKE) == 0) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FLING, player); + HP_BAR(opponent, captureDamage: &damage[0]); + } else { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_FLING, player); + MESSAGE("But it failed!"); + } ANIMATION(ANIM_TYPE_MOVE, MOVE_EGG_BOMB, player); HP_BAR(opponent, captureDamage: &damage[1]); } THEN { - EXPECT_EQ(damage[0], damage[1]); + if (GetItemImportance(ITEM_TM_EARTHQUAKE) == 0) + EXPECT_EQ(damage[0], damage[1]); } } -SINGLE_BATTLE_TEST("Fling deals damage based on a TM's move power") +SINGLE_BATTLE_TEST("Fling fails when a Paradox mon holds a Booster Energy") { - s16 damage[2]; - GIVEN { - ASSUME(GetMovePower(MOVE_EARTHQUAKE) == GetMovePower(MOVE_EGG_BOMB)); - ASSUME(!IsSpeciesOfType(SPECIES_WOBBUFFET, TYPE_DARK)); - PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_TM_EARTHQUAKE); } - OPPONENT(SPECIES_HIPPOWDON); + ASSUME(GetItemHoldEffect(ITEM_BOOSTER_ENERGY) == HOLD_EFFECT_BOOSTER_ENERGY); + ASSUME(gSpeciesInfo[SPECIES_RAGING_BOLT].isParadox == TRUE); + PLAYER(SPECIES_RAGING_BOLT) { Item(ITEM_BOOSTER_ENERGY); Ability(ABILITY_PROTOSYNTHESIS); } + OPPONENT(SPECIES_TORKOAL) { Ability(ABILITY_DROUGHT); } } WHEN { TURN { MOVE(player, MOVE_FLING); } - TURN { MOVE(player, MOVE_EGG_BOMB); } } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_FLING, player); - HP_BAR(opponent, captureDamage: &damage[0]); - ANIMATION(ANIM_TYPE_MOVE, MOVE_EGG_BOMB, player); - HP_BAR(opponent, captureDamage: &damage[1]); + MESSAGE("But it failed!"); } THEN { - EXPECT_EQ(damage[0], damage[1]); + EXPECT(player->item == ITEM_BOOSTER_ENERGY); + } +} + +SINGLE_BATTLE_TEST("Fling doesn't fail when holding a Booster Energy and the target is a Paradox mon") +{ + GIVEN { + ASSUME(GetItemHoldEffect(ITEM_BOOSTER_ENERGY) == HOLD_EFFECT_BOOSTER_ENERGY); + ASSUME(gSpeciesInfo[SPECIES_RAGING_BOLT].isParadox == TRUE); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_BOOSTER_ENERGY); } + OPPONENT(SPECIES_RAGING_BOLT) { Ability(ABILITY_PROTOSYNTHESIS); } + } WHEN { + TURN { MOVE(player, MOVE_FLING); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FLING, player); + } THEN { + EXPECT(player->item == ITEM_NONE); } } diff --git a/test/battle/move_effect/foresight.c b/test/battle/move_effect/foresight.c index 59209030d..9879774ba 100644 --- a/test/battle/move_effect/foresight.c +++ b/test/battle/move_effect/foresight.c @@ -1,14 +1,123 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Foresight removes Ghost's type immunity to Normal and Fighting types") +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_FORESIGHT) == EFFECT_FORESIGHT); +} + +SINGLE_BATTLE_TEST("Foresight removes Ghost's type immunity to Normal and Fighting types") +{ + GIVEN { + ASSUME(GetMoveType(MOVE_SCRATCH) == TYPE_NORMAL); + ASSUME(GetMoveType(MOVE_LOW_KICK) == TYPE_FIGHTING); + ASSUME(GetSpeciesType(SPECIES_GENGAR, 0) == TYPE_GHOST); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_FORESIGHT, MOVE_SCRATCH, MOVE_LOW_KICK); } + OPPONENT(SPECIES_GENGAR) { Moves(MOVE_SPLASH); } + } WHEN { + TURN { MOVE(player, MOVE_FORESIGHT); MOVE(opponent, MOVE_SPLASH); } + TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SPLASH); } + TURN { MOVE(player, MOVE_LOW_KICK); MOVE(opponent, MOVE_SPLASH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + HP_BAR(opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_LOW_KICK, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Foresight always hits unless the target is semi-invulnerable") +{ + bool32 semiInvulnerable = FALSE; + PARAMETRIZE { semiInvulnerable = FALSE; } + PARAMETRIZE { semiInvulnerable = TRUE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP); + ASSUME(GetMoveEffect(MOVE_FLY) == EFFECT_SEMI_INVULNERABLE); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_FORESIGHT, MOVE_SPLASH); Speed(10); } + OPPONENT(SPECIES_SQUAWKABILLY) { Moves(MOVE_DOUBLE_TEAM, MOVE_FLY); Speed(20); } + } WHEN { + if (semiInvulnerable) + TURN { MOVE(player, MOVE_FORESIGHT); MOVE(opponent, MOVE_FLY); } + else + TURN { MOVE(player, MOVE_FORESIGHT); MOVE(opponent, MOVE_DOUBLE_TEAM); } + + if (semiInvulnerable) + TURN { MOVE(player, MOVE_SPLASH); SKIP_TURN(opponent); } + } SCENE { + if (semiInvulnerable) { + MESSAGE("The opposing Squawkabilly avoided the attack!"); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + } + } +} + +SINGLE_BATTLE_TEST("Foresight causes moves against the target to ignore positive evasion stat stages") +{ + PASSES_RANDOMLY(100, 100, RNG_ACCURACY); + GIVEN { + ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_FORESIGHT, MOVE_SCRATCH); Speed(10); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_DOUBLE_TEAM, MOVE_SPLASH); Speed(20); } + } WHEN { + TURN { MOVE(player, MOVE_FORESIGHT); MOVE(opponent, MOVE_DOUBLE_TEAM); } + TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SPLASH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Foresight fails if the target is already under its effect (Gen 2 and Gen5+)") +{ + u32 genConfig = GEN_2; + PARAMETRIZE { genConfig = GEN_2; } + PARAMETRIZE { genConfig = GEN_5; } + GIVEN { + WITH_CONFIG(CONFIG_FORESIGHT_FAIL, genConfig); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_FORESIGHT); } + TURN { MOVE(player, MOVE_FORESIGHT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Foresight doesn't fail if the target is already under its effect (Gen 3-4)") +{ + u32 genConfig = GEN_3; + PARAMETRIZE { genConfig = GEN_3; } + PARAMETRIZE { genConfig = GEN_4; } + GIVEN { + WITH_CONFIG(CONFIG_FORESIGHT_FAIL, genConfig); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_FORESIGHT); } + TURN { MOVE(player, MOVE_FORESIGHT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + NOT MESSAGE("But it failed!"); + } +} + TO_DO_BATTLE_TEST("Foresight causes accuracy/evasion stat changes only between the user/target when the user's accuracy stage is less than the target's evasion stage (Gen 2)") TO_DO_BATTLE_TEST("Foresight causes all moves against the target to ignore evasion stat changes (Gen 3)") TO_DO_BATTLE_TEST("Foresight causes all moves against the target to ignore only positive evasion stat changes (Gen 4+)") // Eg. Doesn't ignore Sweet Scent TO_DO_BATTLE_TEST("Foresight doesn't cause moves used against the target to always hit (Gen 2-3)") TO_DO_BATTLE_TEST("Foresight causes moves used against the target to always hit (Gen 4+)") -TO_DO_BATTLE_TEST("Foresight does not make moves hit semi-invulnerable targets") -TO_DO_BATTLE_TEST("Foresight fails if the target is already under its effect (Gen 2 and Gen5+)") -TO_DO_BATTLE_TEST("Foresight doesn't fail if the target is already under its effect (Gen 3-4)") TO_DO_BATTLE_TEST("Baton Pass passes Foresight's effect (Gen 2)"); TO_DO_BATTLE_TEST("Baton Pass doesn't pass Foresight's effect (Gen 3+)"); diff --git a/test/battle/move_effect/gear_up.c b/test/battle/move_effect/gear_up.c index 9a9bdab79..9cac73d69 100644 --- a/test/battle/move_effect/gear_up.c +++ b/test/battle/move_effect/gear_up.c @@ -1,17 +1,47 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Gear Up increases the Attack and Sp. Attack of the user and allies if they have Plus or Minus") +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_GEAR_UP) == EFFECT_GEAR_UP); +} -AI_DOUBLE_BATTLE_TEST("AI uses Gear Up") +SINGLE_BATTLE_TEST("Gear Up raises Attack and Sp. Attack of the user with Plus/Minus in singles") { GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); - PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } - PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } - OPPONENT(SPECIES_KLINKLANG) { Ability(ABILITY_PLUS); Moves(MOVE_GEAR_UP, MOVE_WATER_GUN, MOVE_POUND); } - OPPONENT(SPECIES_KLINKLANG) { Ability(ABILITY_PLUS); Moves(MOVE_GEAR_UP, MOVE_WATER_GUN, MOVE_POUND); } + PLAYER(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); } + OPPONENT(SPECIES_MINUN) { Ability(ABILITY_MINUS); } } WHEN { - TURN { EXPECT_MOVE(opponentLeft, MOVE_GEAR_UP); } + TURN { MOVE(player, MOVE_GEAR_UP); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_GEAR_UP, player); + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + } +} + +DOUBLE_BATTLE_TEST("Gear Up raises Attack and Sp. Attack of all Plus/Minus allies in doubles") +{ + GIVEN { + PLAYER(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); } + PLAYER(SPECIES_MINUN) { Ability(ABILITY_MINUS); } + OPPONENT(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); } + OPPONENT(SPECIES_MINUN) { Ability(ABILITY_MINUS); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_GEAR_UP); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_GEAR_UP, playerLeft); + } THEN { + EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(playerLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(playerRight->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(opponentLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponentLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponentRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponentRight->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); } } diff --git a/test/battle/move_effect/hyperspace_fury.c b/test/battle/move_effect/hyperspace_fury.c deleted file mode 100644 index 080758c94..000000000 --- a/test/battle/move_effect/hyperspace_fury.c +++ /dev/null @@ -1,4 +0,0 @@ -#include "global.h" -#include "test/battle.h" - -TO_DO_BATTLE_TEST("TODO: Write Hyperspace Fury (Move Effect) test titles") diff --git a/test/battle/move_effect/ivy_cudgel.c b/test/battle/move_effect/ivy_cudgel.c index 7dd7f3344..92f85d66e 100644 --- a/test/battle/move_effect/ivy_cudgel.c +++ b/test/battle/move_effect/ivy_cudgel.c @@ -12,10 +12,10 @@ SINGLE_BATTLE_TEST("Ivy Cudgel changes the move type depending on the form of Og u16 ogerpon; enum Item item; - PARAMETRIZE { species = SPECIES_BLASTOISE; ogerpon = SPECIES_OGERPON_TEAL; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_BLASTOISE; ogerpon = SPECIES_OGERPON_TEAL; item = ITEM_NONE; } PARAMETRIZE { species = SPECIES_CHARIZARD; ogerpon = SPECIES_OGERPON_CORNERSTONE; item = ITEM_CORNERSTONE_MASK; } - PARAMETRIZE { species = SPECIES_CHARIZARD; ogerpon = SPECIES_OGERPON_WELLSPRING; item = ITEM_WELLSPRING_MASK; } - PARAMETRIZE { species = SPECIES_VENUSAUR; ogerpon = SPECIES_OGERPON_HEARTHFLAME; item = ITEM_HEARTHFLAME_MASK; } + PARAMETRIZE { species = SPECIES_CHARIZARD; ogerpon = SPECIES_OGERPON_WELLSPRING; item = ITEM_WELLSPRING_MASK; } + PARAMETRIZE { species = SPECIES_VENUSAUR; ogerpon = SPECIES_OGERPON_HEARTHFLAME; item = ITEM_HEARTHFLAME_MASK; } GIVEN { PLAYER(ogerpon) { Item(item); } diff --git a/test/battle/move_effect/last_resort.c b/test/battle/move_effect/last_resort.c index 089a72330..cb5a0f93b 100644 --- a/test/battle/move_effect/last_resort.c +++ b/test/battle/move_effect/last_resort.c @@ -92,6 +92,39 @@ SINGLE_BATTLE_TEST("Last Resort works only when all of the known moves have been } } +// PP needs to be deducted for Last Resort to work +SINGLE_BATTLE_TEST("Last Resort fails if mon was paralyzed last turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_PARALYSIS); Moves(MOVE_LAST_RESORT, MOVE_CELEBRATE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, WITH_RNG(RNG_PARALYSIS, FALSE)); } + TURN { MOVE(player, MOVE_LAST_RESORT); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_LAST_RESORT, player); + } + } +} + +SINGLE_BATTLE_TEST("Last Resort does not fail if previous move was blocked by Dazzling") +{ + GIVEN { + ASSUME(GetMovePriority(MOVE_QUICK_ATTACK) > 0); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_LAST_RESORT, MOVE_QUICK_ATTACK); } + OPPONENT(SPECIES_BRUXISH) { Ability(ABILITY_DAZZLING); } + } WHEN { + TURN { MOVE(player, MOVE_QUICK_ATTACK); } + TURN { MOVE(player, MOVE_LAST_RESORT); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_DAZZLING); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_LAST_RESORT, player); + } +} + SINGLE_BATTLE_TEST("Last Resort works with Sleep Talk") { GIVEN { diff --git a/test/battle/move_effect/magnetic_flux.c b/test/battle/move_effect/magnetic_flux.c index 7bbcd5275..ac969a9dd 100644 --- a/test/battle/move_effect/magnetic_flux.c +++ b/test/battle/move_effect/magnetic_flux.c @@ -1,17 +1,47 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Magnetic Flux (Move Effect) test titles") +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_MAGNETIC_FLUX) == EFFECT_MAGNETIC_FLUX); +} -AI_DOUBLE_BATTLE_TEST("AI uses Magnetic Flux") +SINGLE_BATTLE_TEST("Magnetic Flux raises Defense and Sp. Defense of the user with Plus/Minus in singles") { GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); - PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } - PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } - OPPONENT(SPECIES_KLINK) { Ability(ABILITY_PLUS); Moves(MOVE_MAGNETIC_FLUX, MOVE_POUND); } - OPPONENT(SPECIES_KLINK) { Ability(ABILITY_PLUS); Moves(MOVE_MAGNETIC_FLUX, MOVE_POUND); } + PLAYER(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); } + OPPONENT(SPECIES_MINUN) { Ability(ABILITY_MINUS); } } WHEN { - TURN { EXPECT_MOVE(opponentLeft, MOVE_MAGNETIC_FLUX); } + TURN { MOVE(player, MOVE_MAGNETIC_FLUX); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGNETIC_FLUX, player); + } THEN { + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE); + } +} + +DOUBLE_BATTLE_TEST("Magnetic Flux raises Defense and Sp. Defense of all Plus/Minus allies in doubles") +{ + GIVEN { + PLAYER(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); } + PLAYER(SPECIES_MINUN) { Ability(ABILITY_MINUS); } + OPPONENT(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); } + OPPONENT(SPECIES_MINUN) { Ability(ABILITY_MINUS); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_MAGNETIC_FLUX); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGNETIC_FLUX, playerLeft); + } THEN { + EXPECT_EQ(playerLeft->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(playerLeft->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(playerRight->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(playerRight->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(opponentLeft->statStages[STAT_DEF], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponentLeft->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponentRight->statStages[STAT_DEF], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponentRight->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE); } } diff --git a/test/battle/move_effect/magnitude.c b/test/battle/move_effect/magnitude.c index 75799a4dc..1781c3cb4 100644 --- a/test/battle/move_effect/magnitude.c +++ b/test/battle/move_effect/magnitude.c @@ -1,4 +1,23 @@ #include "global.h" #include "test/battle.h" +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_MAGNITUDE) == EFFECT_MAGNITUDE); +} + TO_DO_BATTLE_TEST("TODO: Write Magnitude (Move Effect) test titles") + +SINGLE_BATTLE_TEST("Magnitude message is printed before failing because of Levitate") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_FLYGON) { Ability(ABILITY_LEVITATE); } + } WHEN { + TURN { MOVE(player, MOVE_MAGNITUDE); } + } SCENE { + MESSAGE("Magnitude 10!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGNITUDE, player); + ABILITY_POPUP(opponent, ABILITY_LEVITATE); + } +} diff --git a/test/battle/move_effect/miracle_eye.c b/test/battle/move_effect/miracle_eye.c index 784323f1c..5ba1e48f1 100644 --- a/test/battle/move_effect/miracle_eye.c +++ b/test/battle/move_effect/miracle_eye.c @@ -1,4 +1,106 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Miracle Eye (Move Effect) test titles") +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_MIRACLE_EYE) == EFFECT_MIRACLE_EYE); +} + +SINGLE_BATTLE_TEST("Miracle Eye removes Dark-type immunity to Psychic-type moves") +{ + GIVEN { + ASSUME(GetMoveType(MOVE_PSYCHIC) == TYPE_PSYCHIC); + ASSUME(GetSpeciesType(SPECIES_UMBREON, 0) == TYPE_DARK); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_MIRACLE_EYE, MOVE_PSYCHIC); } + OPPONENT(SPECIES_UMBREON) { Moves(MOVE_SPLASH); } + } WHEN { + TURN { MOVE(player, MOVE_PSYCHIC); MOVE(opponent, MOVE_SPLASH); } + TURN { MOVE(player, MOVE_MIRACLE_EYE); MOVE(opponent, MOVE_SPLASH); } + TURN { MOVE(player, MOVE_PSYCHIC); MOVE(opponent, MOVE_SPLASH); } + } SCENE { + NOT HP_BAR(opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Miracle Eye always hits unless the target is semi-invulnerable") +{ + bool32 semiInvulnerable = FALSE; + PARAMETRIZE { semiInvulnerable = FALSE; } + PARAMETRIZE { semiInvulnerable = TRUE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP); + ASSUME(GetMoveEffect(MOVE_FLY) == EFFECT_SEMI_INVULNERABLE); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_MIRACLE_EYE, MOVE_SPLASH); Speed(10); } + OPPONENT(SPECIES_SQUAWKABILLY) { Moves(MOVE_DOUBLE_TEAM, MOVE_FLY); Speed(20); } + } WHEN { + if (semiInvulnerable) + TURN { MOVE(player, MOVE_MIRACLE_EYE); MOVE(opponent, MOVE_FLY); } + else + TURN { MOVE(player, MOVE_MIRACLE_EYE); MOVE(opponent, MOVE_DOUBLE_TEAM); } + + if (semiInvulnerable) + TURN { MOVE(player, MOVE_SPLASH); SKIP_TURN(opponent); } + } SCENE { + if (semiInvulnerable) { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + } + } +} + +SINGLE_BATTLE_TEST("Miracle Eye causes moves against the target to ignore positive evasion stat stages") +{ + PASSES_RANDOMLY(100, 100, RNG_ACCURACY); + GIVEN { + ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_MIRACLE_EYE, MOVE_SCRATCH); Speed(10); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_DOUBLE_TEAM, MOVE_SPLASH); Speed(20); } + } WHEN { + TURN { MOVE(player, MOVE_MIRACLE_EYE); MOVE(opponent, MOVE_DOUBLE_TEAM); } + TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SPLASH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Miracle Eye fails if the target is already affected by Miracle Eye (Gen5+)") +{ + GIVEN { + WITH_CONFIG(CONFIG_MIRACLE_EYE_FAIL, GEN_5); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_MIRACLE_EYE); } + TURN { MOVE(player, MOVE_MIRACLE_EYE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Miracle Eye does not fail if the target is already affected by Miracle Eye (Gen4)") +{ + GIVEN { + WITH_CONFIG(CONFIG_MIRACLE_EYE_FAIL, GEN_4); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_MIRACLE_EYE); } + TURN { MOVE(player, MOVE_MIRACLE_EYE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + NOT MESSAGE("But it failed!"); + } +} diff --git a/test/battle/move_effect/ohko.c b/test/battle/move_effect/ohko.c index 28c23d716..7a5f9c8d9 100644 --- a/test/battle/move_effect/ohko.c +++ b/test/battle/move_effect/ohko.c @@ -83,6 +83,51 @@ SINGLE_BATTLE_TEST("OHKO moves fail if target protects") NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_FISSURE, player); } } + +SINGLE_BATTLE_TEST("Sheer Cold can hit semi-invulnerable mons when the user has No-Guard") +{ + GIVEN { + ASSUME(GetItemHoldEffect(ITEM_FOCUS_SASH) == HOLD_EFFECT_FOCUS_SASH); + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_NO_GUARD); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_FLY); } + TURN { MOVE(player, MOVE_SHEER_COLD); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SHEER_COLD, player); + HP_BAR(opponent, hp: 0); + } +} + +SINGLE_BATTLE_TEST("Sheer Cold can be endured by Focus Sash") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_FOCUS_SASH); } + } WHEN { + TURN { MOVE(player, MOVE_SHEER_COLD); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SHEER_COLD, player); + HP_BAR(opponent, hp: 1); + MESSAGE("The opposing Wobbuffet hung on using its Focus Sash!"); + } +} + +SINGLE_BATTLE_TEST("Sheer Cold can be endured by Sturdy") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GEODUDE) { Ability(ABILITY_STURDY); } + } WHEN { + TURN { MOVE(player, MOVE_SHEER_COLD); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SHEER_COLD, player); + ABILITY_POPUP(opponent, ABILITY_STURDY); + } +} + TO_DO_BATTLE_TEST("OHKO moves faints the target, skipping regular damage calculations") TO_DO_BATTLE_TEST("OHKO moves's accuracy increases by 1% for every level the user has over the target") TO_DO_BATTLE_TEST("OHKO moves's ignores non-stage accuracy modifiers") // Gravity, Wide Lens, Compound Eyes +TO_DO_BATTLE_TEST("OHKO moves ignore non-stage accuracy modifiers") // Gravity, Wide Lens, Compound Eyes +TO_DO_BATTLE_TEST("OHKO: Sheer Cold's accuracy decreasaes by 10% if the user is not Ice type") diff --git a/test/battle/move_effect/parting_shot.c b/test/battle/move_effect/parting_shot.c index 4d55d0931..58c5ba521 100644 --- a/test/battle/move_effect/parting_shot.c +++ b/test/battle/move_effect/parting_shot.c @@ -1,4 +1,385 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Parting Shot (Move Effect) test titles") +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_PARTING_SHOT) == EFFECT_PARTING_SHOT); +} + +SINGLE_BATTLE_TEST("Parting Shot: Passes Substitute and switches the user out") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_CELEBRATE); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SUBSTITUTE, MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SUBSTITUTE); } + TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); SEND_OUT(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player); + SEND_IN_MESSAGE("Wynaut"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(player->species, SPECIES_WYNAUT); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Soundproof and Good as Gold block Parting Shot") +{ + u16 species, ability; + + PARAMETRIZE { species = SPECIES_EXPLOUD; ability = ABILITY_SOUNDPROOF; } + PARAMETRIZE { species = SPECIES_GHOLDENGO; ability = ABILITY_GOOD_AS_GOLD; } + + GIVEN { + ASSUME(IsSoundMove(MOVE_PARTING_SHOT)); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(species) { Ability(ability); Moves(MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + ABILITY_POPUP(opponent, ability); + if (ability == ABILITY_SOUNDPROOF) + MESSAGE("The opposing Exploud's Soundproof blocks Parting Shot!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WOBBUFFET); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Hyper Cutter blocks Attack drop but still switches") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_KRABBY) { Ability(ABILITY_HYPER_CUTTER); } + } WHEN { + TURN { MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player); + SEND_IN_MESSAGE("Wynaut"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(player->species, SPECIES_WYNAUT); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Magic Coat bounces it and switches the target out") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_MAGIC_COAT); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponent, MOVE_MAGIC_COAT); MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(opponent, 1); } + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(opponent->species, SPECIES_WYNAUT); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Magic Bounce bounces it and switches the target out") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); } + OPPONENT(SPECIES_ESPEON) { Ability(ABILITY_MAGIC_BOUNCE); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(opponent, 1); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_MAGIC_BOUNCE); + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(opponent->species, SPECIES_WYNAUT); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Mirror Armor switches the user even if reflected drops fail") +{ + u16 species, ability, item; + + PARAMETRIZE { species = SPECIES_METAGROSS; ability = ABILITY_CLEAR_BODY; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_LUCARIO; ability = ABILITY_INNER_FOCUS; item = ITEM_CLEAR_AMULET; } + + GIVEN { + ASSUME(gItemsInfo[ITEM_CLEAR_AMULET].holdEffect == HOLD_EFFECT_CLEAR_AMULET); + PLAYER(species) { Ability(ability); Item(item); Moves(MOVE_PARTING_SHOT); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); } + } WHEN { + TURN { MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(player, 1); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_MIRROR_ARMOR); + if (ability == ABILITY_CLEAR_BODY) { + ABILITY_POPUP(player, ABILITY_CLEAR_BODY); + MESSAGE("Metagross's Clear Body prevents stat loss!"); + } else if (ability == ABILITY_WHITE_SMOKE) { + ABILITY_POPUP(player, ABILITY_WHITE_SMOKE); + MESSAGE("Torkoal's White Smoke prevents stat loss!"); + } else if (ability == ABILITY_FULL_METAL_BODY) { + ABILITY_POPUP(player, ABILITY_FULL_METAL_BODY); + MESSAGE("Solgaleo's Full Metal Body prevents stat loss!"); + } else if (item == ITEM_CLEAR_AMULET) { + MESSAGE("The effects of the Clear Amulet held by Lucario prevents its stats from being lowered!"); + } + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WYNAUT); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Mirror Armor switches even if reflected stats are at minimum") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_SHELL_SMASH, MOVE_CELEBRATE); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); Moves(MOVE_TOPSY_TURVY, MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(player, MOVE_SHELL_SMASH); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_SHELL_SMASH); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_SHELL_SMASH); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TOPSY_TURVY); } + TURN { MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(player, 1); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_MIRROR_ARMOR); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WYNAUT); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Does not switch if both stats are at minimum (Gen7+)") +{ + GIVEN { + WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_7); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_TOPSY_TURVY, MOVE_CELEBRATE); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_OMASTAR) { Moves(MOVE_SHELL_SMASH, MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_TOPSY_TURVY); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + MESSAGE("The opposing Omastar's stats won't go any lower!"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], MIN_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], MIN_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WOBBUFFET); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Does not switch if Contrary is at maximum stats (Gen7+)") +{ + GIVEN { + WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_7); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_TOPSY_TURVY, MOVE_CELEBRATE); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_INKAY) { Ability(ABILITY_CONTRARY); Moves(MOVE_SHELL_SMASH, MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_TOPSY_TURVY); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + MESSAGE("The opposing Inkay's stats won't go any higher!"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], MAX_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], MAX_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WOBBUFFET); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Stat drop prevention by abilities/items does not switch (Gen7+)") +{ + u16 species, ability, item; + + PARAMETRIZE { species = SPECIES_METAGROSS; ability = ABILITY_CLEAR_BODY; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_LUCARIO; ability = ABILITY_INNER_FOCUS; item = ITEM_CLEAR_AMULET; } + + GIVEN { + WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_7); + ASSUME(gItemsInfo[ITEM_CLEAR_AMULET].holdEffect == HOLD_EFFECT_CLEAR_AMULET); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(species) { Ability(ability); Item(item); } + } WHEN { + TURN { MOVE(player, MOVE_PARTING_SHOT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WOBBUFFET); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Mist prevents stat drops and does not switch (Gen7+)") +{ + GIVEN { + WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_7); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_CELEBRATE); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_MIST, MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(opponent, MOVE_MIST); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_PARTING_SHOT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WOBBUFFET); + } +} + +DOUBLE_BATTLE_TEST("Parting Shot: Flower Veil prevents stat drops and does not switch (Gen7+)") +{ + GIVEN { + ASSUME(GetSpeciesType(SPECIES_BULBASAUR, 0) == TYPE_GRASS); + WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_7); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); } + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_BULBASAUR); + OPPONENT(SPECIES_COMFEY) { Ability(ABILITY_FLOWER_VEIL); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_PARTING_SHOT, target: opponentLeft); MOVE(playerRight, MOVE_CELEBRATE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, playerLeft); + } THEN { + EXPECT_EQ(opponentLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponentLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(playerLeft->species, SPECIES_WOBBUFFET); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Switches if both stats are at minimum (Gen6)") +{ + GIVEN { + WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_6); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_TOPSY_TURVY, MOVE_CELEBRATE); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_OMASTAR) { Moves(MOVE_SHELL_SMASH, MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_TOPSY_TURVY); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); SEND_OUT(player, 1); } + } SCENE { + MESSAGE("The opposing Omastar's stats won't go any lower!"); + SEND_IN_MESSAGE("Wynaut"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], MIN_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], MIN_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WYNAUT); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Switches if Contrary is at maximum stats (Gen6)") +{ + GIVEN { + WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_6); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_TOPSY_TURVY, MOVE_CELEBRATE); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_INKAY) { Ability(ABILITY_CONTRARY); Moves(MOVE_SHELL_SMASH, MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_TOPSY_TURVY); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); SEND_OUT(player, 1); } + } SCENE { + MESSAGE("The opposing Inkay's stats won't go any higher!"); + SEND_IN_MESSAGE("Wynaut"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], MAX_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], MAX_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WYNAUT); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Stat drop prevention by abilities/items switches (Gen6)") +{ + u16 species, ability, item; + + PARAMETRIZE { species = SPECIES_METAGROSS; ability = ABILITY_CLEAR_BODY; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_LUCARIO; ability = ABILITY_INNER_FOCUS; item = ITEM_CLEAR_AMULET; } + + GIVEN { + WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_6); + ASSUME(gItemsInfo[ITEM_CLEAR_AMULET].holdEffect == HOLD_EFFECT_CLEAR_AMULET); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(species) { Ability(ability); Item(item); } + } WHEN { + TURN { MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player); + SEND_IN_MESSAGE("Wynaut"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WYNAUT); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Mist prevents stat drops and switches (Gen6)") +{ + GIVEN { + WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_6); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_CELEBRATE); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_MIST, MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(opponent, MOVE_MIST); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player); + SEND_IN_MESSAGE("Wynaut"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WYNAUT); + } +} + +DOUBLE_BATTLE_TEST("Parting Shot: Flower Veil prevents stat drops and switches (Gen6)") +{ + GIVEN { + WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_6); + ASSUME(GetSpeciesType(SPECIES_BULBASAUR, 0) == TYPE_GRASS); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); } + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_PIKACHU); + OPPONENT(SPECIES_BULBASAUR); + OPPONENT(SPECIES_COMFEY) { Ability(ABILITY_FLOWER_VEIL); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_PARTING_SHOT, target: opponentLeft); MOVE(playerRight, MOVE_CELEBRATE); SEND_OUT(playerLeft, 2); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, playerLeft); + SEND_IN_MESSAGE("Pikachu"); + } THEN { + EXPECT_EQ(opponentLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponentLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(playerLeft->species, SPECIES_PIKACHU); + } +} diff --git a/test/battle/move_effect/protect.c b/test/battle/move_effect/protect.c index dea0a9eab..66b7b419c 100644 --- a/test/battle/move_effect/protect.c +++ b/test/battle/move_effect/protect.c @@ -125,6 +125,36 @@ SINGLE_BATTLE_TEST("Protect: King's Shield, Silk Trap and Obstruct protect from } } +SINGLE_BATTLE_TEST("Protect: King's Shield, Silk Trap and Obstruct don't lower stats when charging a two turn move") +{ + u32 move, protectMove; + PARAMETRIZE { move = MOVE_BOUNCE; protectMove = MOVE_KINGS_SHIELD; } + PARAMETRIZE { move = MOVE_DIG; protectMove = MOVE_KINGS_SHIELD; } + PARAMETRIZE { move = MOVE_BOUNCE; protectMove = MOVE_SILK_TRAP; } + PARAMETRIZE { move = MOVE_DIG; protectMove = MOVE_SILK_TRAP; } + PARAMETRIZE { move = MOVE_BOUNCE; protectMove = MOVE_OBSTRUCT; } + PARAMETRIZE { move = MOVE_DIG; protectMove = MOVE_OBSTRUCT; } + + GIVEN { + ASSUME(MoveMakesContact(MOVE_BOUNCE)); + ASSUME(MoveMakesContact(MOVE_DIG)); + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_BOUNCE)].twoTurnEffect); + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, protectMove); MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, protectMove, player); + + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + NONE_OF { + HP_BAR(player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + } + } +} + SINGLE_BATTLE_TEST("Protect: Spiky Shield does 1/8 dmg of max hp of attackers making contact and may faint them") { enum Move usedMove = MOVE_NONE; @@ -162,6 +192,32 @@ SINGLE_BATTLE_TEST("Protect: Spiky Shield does 1/8 dmg of max hp of attackers ma } } +SINGLE_BATTLE_TEST("Protect: Spiky Shield doesn't hurt attacker when charging a two turn move") +{ + u32 move; + PARAMETRIZE { move = MOVE_BOUNCE; } + PARAMETRIZE { move = MOVE_DIG; } + + GIVEN { + ASSUME(MoveMakesContact(MOVE_BOUNCE)); + ASSUME(MoveMakesContact(MOVE_DIG)); + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_BOUNCE)].twoTurnEffect); + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SPIKY_SHIELD); MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKY_SHIELD, player); + + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + NONE_OF { + HP_BAR(player); + HP_BAR(opponent); + } + } +} + SINGLE_BATTLE_TEST("Protect: Baneful Bunker poisons Pokémon for moves making contact") { enum Move usedMove = MOVE_NONE; @@ -214,6 +270,32 @@ SINGLE_BATTLE_TEST("Protect: Baneful Bunker can't poison Pokémon if they are al } } +SINGLE_BATTLE_TEST("Protect: Baneful Bunker doesn't poison attacker when charging a two turn move") +{ + u32 move; + PARAMETRIZE { move = MOVE_BOUNCE; } + PARAMETRIZE { move = MOVE_DIG; } + + GIVEN { + ASSUME(MoveMakesContact(MOVE_BOUNCE)); + ASSUME(MoveMakesContact(MOVE_DIG)); + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_BOUNCE)].twoTurnEffect); + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BANEFUL_BUNKER); MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BANEFUL_BUNKER, player); + + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + NONE_OF { + HP_BAR(player); + STATUS_ICON(opponent, STATUS1_POISON); + } + } +} + SINGLE_BATTLE_TEST("Protect: Burning Bulwark burns Pokémon for moves making contact") { enum Move usedMove = MOVE_NONE; @@ -266,6 +348,32 @@ SINGLE_BATTLE_TEST("Protect: Burning Bulwark can't burn Pokémon if they are alr } } +SINGLE_BATTLE_TEST("Protect: Burning Bulwark doesn't burn attacker when charging a two turn move") +{ + u32 move; + PARAMETRIZE { move = MOVE_BOUNCE; } + PARAMETRIZE { move = MOVE_DIG; } + + GIVEN { + ASSUME(MoveMakesContact(MOVE_BOUNCE)); + ASSUME(MoveMakesContact(MOVE_DIG)); + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_BOUNCE)].twoTurnEffect); + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BURNING_BULWARK); MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BURNING_BULWARK, player); + + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + NONE_OF { + HP_BAR(player); + STATUS_ICON(opponent, STATUS1_BURN); + } + } +} + SINGLE_BATTLE_TEST("Protect: Recoil damage is not applied if target was protected") { u32 j, k; @@ -530,7 +638,7 @@ DOUBLE_BATTLE_TEST("Protect: Quick Guard can not fail on consecutive turns (Gen6 } } -DOUBLE_BATTLE_TEST("Protect: Crafty Shield protects self and ally from status moves") +DOUBLE_BATTLE_TEST("Crafty Shield protects self and ally from opposing status moves") { enum Move move = MOVE_NONE; struct BattlePokemon *targetOpponent = NULL; @@ -571,6 +679,72 @@ DOUBLE_BATTLE_TEST("Protect: Crafty Shield protects self and ally from status mo } } +DOUBLE_BATTLE_TEST("Crafty Shield does not protect against status moves used on the user's side") +{ + u32 move; + + PARAMETRIZE { move = MOVE_AROMATHERAPY; } + PARAMETRIZE { move = MOVE_ACUPRESSURE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_AROMATHERAPY) == EFFECT_HEAL_BELL); + ASSUME(GetMoveEffect(MOVE_ACUPRESSURE) == EFFECT_ACUPRESSURE); + PLAYER(SPECIES_WOBBUFFET) { Speed(5); } + PLAYER(SPECIES_WOBBUFFET) { Speed(5); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(10); } + OPPONENT(SPECIES_WYNAUT) { Speed(5); Status1(STATUS1_BURN); } + } WHEN { + TURN { + MOVE(opponentLeft, MOVE_CRAFTY_SHIELD); + if (move == MOVE_ACUPRESSURE) + MOVE(opponentRight, move, target: opponentLeft); + else + MOVE(opponentRight, move); + } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CRAFTY_SHIELD, opponentLeft); + if (move == MOVE_ACUPRESSURE) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ACUPRESSURE, opponentRight); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_AROMATHERAPY, opponentRight); + STATUS_ICON(opponentRight, none: TRUE); + } + } +} + +DOUBLE_BATTLE_TEST("Crafty Shield does not protect against entry hazard moves") +{ + u32 move; + + PARAMETRIZE { move = MOVE_SPIKES; } + PARAMETRIZE { move = MOVE_STEALTH_ROCK; } + PARAMETRIZE { move = MOVE_TOXIC_SPIKES; } + PARAMETRIZE { move = MOVE_STICKY_WEB; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_CRAFTY_SHIELD); MOVE(playerLeft, move, target: opponentLeft); } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CRAFTY_SHIELD, opponentLeft); + if (move == MOVE_SPIKES) { + MESSAGE("Spikes were scattered on the ground all around the opposing team!"); + } else if (move == MOVE_TOXIC_SPIKES) { + MESSAGE("Poison spikes were scattered on the ground all around the opposing team!"); + } else if (move == MOVE_STEALTH_ROCK) { + MESSAGE("Pointed stones float in the air around the opposing team!"); + } else { + MESSAGE("A sticky web has been laid out on the ground around the opposing team!"); + } + } +} + SINGLE_BATTLE_TEST("Protect: Protect does not block Confide or Decorate") { enum Move move; @@ -620,6 +794,11 @@ DOUBLE_BATTLE_TEST("Crafty Shield protects self and ally from Confide and Decora DOUBLE_BATTLE_TEST("Crafty Shield does not protect against moves that target all battlers") { + u32 move; + + PARAMETRIZE { move = MOVE_FLOWER_SHIELD; } + PARAMETRIZE { move = MOVE_PERISH_SONG; } + GIVEN { ASSUME(GetSpeciesType(SPECIES_TANGELA, 0) == TYPE_GRASS); ASSUME(GetSpeciesType(SPECIES_TANGROWTH, 0) == TYPE_GRASS); @@ -630,18 +809,26 @@ DOUBLE_BATTLE_TEST("Crafty Shield does not protect against moves that target all OPPONENT(SPECIES_SUNKERN); OPPONENT(SPECIES_SUNFLORA); } WHEN { - TURN { MOVE(opponentLeft, MOVE_CRAFTY_SHIELD); MOVE(opponentRight, MOVE_CELEBRATE); MOVE(playerLeft, MOVE_FLOWER_SHIELD); MOVE(playerRight, MOVE_CELEBRATE); } + TURN { MOVE(opponentLeft, MOVE_CRAFTY_SHIELD); MOVE(opponentRight, MOVE_CELEBRATE); MOVE(playerLeft, move); MOVE(playerRight, MOVE_CELEBRATE); } } SCENE { - MESSAGE("Tangela used Flower Shield!"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); - MESSAGE("Tangela's Defense rose!"); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); - MESSAGE("The opposing Sunkern's Defense rose!"); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); - MESSAGE("Tangrowth's Defense rose!"); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); - MESSAGE("The opposing Sunflora's Defense rose!"); + if (move == MOVE_FLOWER_SHIELD) { + MESSAGE("Tangela used Flower Shield!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + MESSAGE("Tangela's Defense rose!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + MESSAGE("The opposing Sunkern's Defense rose!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + MESSAGE("Tangrowth's Defense rose!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); + MESSAGE("The opposing Sunflora's Defense rose!"); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PERISH_SONG, playerLeft); + NONE_OF { + MESSAGE("The opposing Sunkern protected itself!"); + MESSAGE("The opposing Sunflora protected itself!"); + } + } } } diff --git a/test/battle/move_effect/psych_up.c b/test/battle/move_effect/psych_up.c index 36241a4ed..ca2ecd3f5 100644 --- a/test/battle/move_effect/psych_up.c +++ b/test/battle/move_effect/psych_up.c @@ -1,4 +1,122 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Psych Up (Move Effect) test titles") +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_PSYCH_UP) == EFFECT_PSYCH_UP); +} + +SINGLE_BATTLE_TEST("Psych Up displays the correct battlers when used by the player") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_SWORDS_DANCE) == EFFECT_ATTACK_UP_2); + PLAYER(SPECIES_TORNADUS) { Speed(66); } + OPPONENT(SPECIES_LANDORUS) { Speed(99); } + } WHEN { + TURN { MOVE(opponent, MOVE_SWORDS_DANCE); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_PSYCH_UP); MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, opponent); + MESSAGE("Tornadus copied the opposing Landorus's stat changes!"); + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], opponent->statStages[STAT_ATK]); + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2); + } +} + +SINGLE_BATTLE_TEST("Psych Up displays the correct battlers when used by the opponent") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_SWORDS_DANCE) == EFFECT_ATTACK_UP_2); + PLAYER(SPECIES_TORNADUS) { Speed(66); } + OPPONENT(SPECIES_LANDORUS) { Speed(99); } + } WHEN { + TURN { MOVE(player, MOVE_SWORDS_DANCE); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(opponent, MOVE_PSYCH_UP); MOVE(player, MOVE_CELEBRATE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, player); + MESSAGE("The opposing Landorus copied Tornadus's stat changes!"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], player->statStages[STAT_ATK]); + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2); + } +} + +SINGLE_BATTLE_TEST("Psych Up ignores Spiky Shield and Baneful Bunker but fails against Crafty Shield") +{ + u32 protectMove = MOVE_NONE; + bool32 shouldFail = FALSE; + + PARAMETRIZE { protectMove = MOVE_SPIKY_SHIELD; shouldFail = FALSE; } + PARAMETRIZE { protectMove = MOVE_BANEFUL_BUNKER; shouldFail = FALSE; } + PARAMETRIZE { protectMove = MOVE_CRAFTY_SHIELD; shouldFail = TRUE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_SWORDS_DANCE) == EFFECT_ATTACK_UP_2); + ASSUME(GetMoveEffect(MOVE_SPIKY_SHIELD) == EFFECT_PROTECT); + ASSUME(GetMoveEffect(MOVE_BANEFUL_BUNKER) == EFFECT_PROTECT); + ASSUME(GetMoveEffect(MOVE_CRAFTY_SHIELD) == EFFECT_PROTECT); + PLAYER(SPECIES_TORNADUS) { Speed(66); } + OPPONENT(SPECIES_LANDORUS) { Speed(99); } + } WHEN { + TURN { MOVE(opponent, MOVE_SWORDS_DANCE); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(opponent, protectMove); MOVE(player, MOVE_PSYCH_UP); } + } SCENE { + if (shouldFail) { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCH_UP, player); + MESSAGE("Tornadus copied the opposing Landorus's stat changes!"); + } + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCH_UP, player); + MESSAGE("Tornadus copied the opposing Landorus's stat changes!"); + } + } THEN { + if (shouldFail) { + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + } else { + EXPECT_EQ(player->statStages[STAT_ATK], opponent->statStages[STAT_ATK]); + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2); + } + } +} + +SINGLE_BATTLE_TEST("Psych Up does not copy the target's critical hit ratio (Gen5)") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_FOCUS_ENERGY) == EFFECT_FOCUS_ENERGY); + WITH_CONFIG(CONFIG_PSYCH_UP_CRIT_RATIO, GEN_5); + WITH_CONFIG(CONFIG_FOCUS_ENERGY_CRIT_RATIO, GEN_9); + PLAYER(SPECIES_TORNADUS) { Speed(66); } + OPPONENT(SPECIES_LANDORUS) { Speed(99); } + } WHEN { + TURN { MOVE(opponent, MOVE_FOCUS_ENERGY); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_PSYCH_UP); MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCH_UP, player); + MESSAGE("Tornadus copied the opposing Landorus's stat changes!"); + } THEN { + EXPECT(opponent->volatiles.focusEnergy); + EXPECT(!player->volatiles.focusEnergy); + } +} + +SINGLE_BATTLE_TEST("Psych Up copies the target's critical hit ratio (Gen6+)") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_FOCUS_ENERGY) == EFFECT_FOCUS_ENERGY); + WITH_CONFIG(CONFIG_PSYCH_UP_CRIT_RATIO, GEN_6); + WITH_CONFIG(CONFIG_FOCUS_ENERGY_CRIT_RATIO, GEN_9); + PLAYER(SPECIES_TORNADUS) { Speed(66); } + OPPONENT(SPECIES_LANDORUS) { Speed(99); } + } WHEN { + TURN { MOVE(opponent, MOVE_FOCUS_ENERGY); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_PSYCH_UP); MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCH_UP, player); + MESSAGE("Tornadus copied the opposing Landorus's stat changes!"); + } THEN { + EXPECT(opponent->volatiles.focusEnergy); + EXPECT(player->volatiles.focusEnergy); + } +} diff --git a/test/battle/move_effect/role_play.c b/test/battle/move_effect/role_play.c index ebd56bce2..19cbfbbea 100644 --- a/test/battle/move_effect/role_play.c +++ b/test/battle/move_effect/role_play.c @@ -70,7 +70,6 @@ SINGLE_BATTLE_TEST("Role Play and Doodle fail if target's ability can't be copie PARAMETRIZE { species = SPECIES_ZYGARDE; ability = ABILITY_POWER_CONSTRUCT; } PARAMETRIZE { species = SPECIES_GRENINJA_BATTLE_BOND; ability = ABILITY_BATTLE_BOND; } PARAMETRIZE { species = SPECIES_EISCUE; ability = ABILITY_ICE_FACE; } - PARAMETRIZE { species = SPECIES_CRAMORANT; ability = ABILITY_GULP_MISSILE; } PARAMETRIZE { species = SPECIES_KOFFING; ability = ABILITY_NEUTRALIZING_GAS; } PARAMETRIZE { species = SPECIES_PALAFIN_ZERO; ability = ABILITY_ZERO_TO_HERO; } PARAMETRIZE { species = SPECIES_TATSUGIRI; ability = ABILITY_COMMANDER; } diff --git a/test/battle/move_effect/round.c b/test/battle/move_effect/round.c index 985822d46..51c46404c 100644 --- a/test/battle/move_effect/round.c +++ b/test/battle/move_effect/round.c @@ -112,3 +112,24 @@ DOUBLE_BATTLE_TEST("Round causes opposing Pokémon to use Round immediately") ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerLeft); } } + +DOUBLE_BATTLE_TEST("Round usages beyond the first one has double base power even if the first attacker fainted") +{ + s16 damage[2]; + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); Item(ITEM_LIFE_ORB); } + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_LIFE_ORB); } + } WHEN { + TURN { + MOVE(opponentLeft, MOVE_ROUND, target: playerLeft); + MOVE(opponentRight, MOVE_ROUND, target: playerLeft); + } + } SCENE { + HP_BAR(playerLeft, captureDamage: &damage[0]); + HP_BAR(playerLeft, captureDamage: &damage[1]); + } THEN { + EXPECT_MUL_EQ(damage[0], Q_4_12(2.0), damage[1]); + } +} diff --git a/test/battle/move_effect/sheer_cold.c b/test/battle/move_effect/sheer_cold.c deleted file mode 100644 index c0b076f44..000000000 --- a/test/battle/move_effect/sheer_cold.c +++ /dev/null @@ -1,86 +0,0 @@ -#include "global.h" -#include "test/battle.h" - -ASSUMPTIONS -{ - ASSUME(GetMoveEffect(MOVE_SHEER_COLD) == EFFECT_SHEER_COLD); -} - -SINGLE_BATTLE_TEST("Sheer Cold doesn't affect Ice-type Pokémon (Gen3-6)") -{ - GIVEN { - WITH_CONFIG(CONFIG_SHEER_COLD_IMMUNITY, GEN_6); - ASSUME(GetSpeciesType(SPECIES_GLALIE, 0) == TYPE_ICE); - PLAYER(SPECIES_WYNAUT); - OPPONENT(SPECIES_GLALIE); - } WHEN { - TURN { MOVE(player, MOVE_SHEER_COLD); } - } SCENE { - NOT MESSAGE("It doesn't affect the opposing Glalie…"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_SHEER_COLD, player); - HP_BAR(opponent, hp: 0); - } -} - -SINGLE_BATTLE_TEST("Sheer Cold doesn't affect Ice-type Pokémon (Gen7+)") -{ - GIVEN { - WITH_CONFIG(CONFIG_SHEER_COLD_IMMUNITY, GEN_7); - ASSUME(GetSpeciesType(SPECIES_GLALIE, 0) == TYPE_ICE); - PLAYER(SPECIES_WYNAUT); - OPPONENT(SPECIES_GLALIE); - } WHEN { - TURN { MOVE(player, MOVE_SHEER_COLD); } - } SCENE { - NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SHEER_COLD, player); - MESSAGE("It doesn't affect the opposing Glalie…"); - } -} - -SINGLE_BATTLE_TEST("Sheer Cold can hit semi-invulnerable mons when the user has No-Guard") -{ - GIVEN { - ASSUME(GetItemHoldEffect(ITEM_FOCUS_SASH) == HOLD_EFFECT_FOCUS_SASH); - PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_NO_GUARD); } - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(opponent, MOVE_FLY); } - TURN { MOVE(player, MOVE_SHEER_COLD); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_SHEER_COLD, player); - HP_BAR(opponent, hp: 0); - } -} - -SINGLE_BATTLE_TEST("Sheer Cold can be endured by Focus Sash") -{ - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_FOCUS_SASH); } - } WHEN { - TURN { MOVE(player, MOVE_SHEER_COLD); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_SHEER_COLD, player); - HP_BAR(opponent, hp: 1); - MESSAGE("The opposing Wobbuffet hung on using its Focus Sash!"); - } -} - -SINGLE_BATTLE_TEST("Sheer Cold can be endured by Sturdy") -{ - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_GEODUDE) { Ability(ABILITY_STURDY); } - } WHEN { - TURN { MOVE(player, MOVE_SHEER_COLD); } - } SCENE { - NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SHEER_COLD, player); - ABILITY_POPUP(opponent, ABILITY_STURDY); - } -} - -TO_DO_BATTLE_TEST("Sheer Cold faints the target, skipping regular damage calculations") -TO_DO_BATTLE_TEST("Sheer Cold always fails if the target has a higher level than the user") -TO_DO_BATTLE_TEST("Sheer Cold's accuracy increases by 1% for every level the user has over the target") -TO_DO_BATTLE_TEST("Sheer Cold's accuracy decreasaes by 10% if the user is not Ice type") -TO_DO_BATTLE_TEST("Sheer Cold's ignores non-stage accuracy modifiers") // Gravity, Wide Lens, Compound Eyes diff --git a/test/battle/move_effect/snore.c b/test/battle/move_effect/snore.c index 410406a65..22f4ea29d 100644 --- a/test/battle/move_effect/snore.c +++ b/test/battle/move_effect/snore.c @@ -19,6 +19,7 @@ SINGLE_BATTLE_TEST("Snore fails if not asleep") TURN { MOVE(player, MOVE_SNORE); } } SCENE { if (status == STATUS1_SLEEP) { + MESSAGE("Wobbuffet is fast asleep."); ANIMATION(ANIM_TYPE_MOVE, MOVE_SNORE, player); NOT MESSAGE("But it failed!"); } diff --git a/test/battle/move_effect/stockpile.c b/test/battle/move_effect/stockpile.c index 108ac915c..ded249802 100644 --- a/test/battle/move_effect/stockpile.c +++ b/test/battle/move_effect/stockpile.c @@ -272,3 +272,42 @@ DOUBLE_BATTLE_TEST("Stockpile's Def and Sp. Def boost is lost after using Spit U EXPECT_MUL_EQ(results[2].dmgSpecialBefore, UQ_4_12(1.0), results[2].dmgSpecialAfter); } } + +SINGLE_BATTLE_TEST("Spit Up's Stockpile's are romoved if move is absorbed") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_ELECTRIFY) == EFFECT_ELECTRIFY); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_PIKACHU) { Ability(ABILITY_LIGHTNING_ROD); } + } WHEN { + TURN { MOVE(player, MOVE_STOCKPILE); } + TURN { MOVE(opponent, MOVE_ELECTRIFY); MOVE(player, MOVE_SPIT_UP); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STOCKPILE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIFY, opponent); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIT_UP, player); + ABILITY_POPUP(opponent, ABILITY_LIGHTNING_ROD); + } THEN { + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE); + EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE); + } +} + +SINGLE_BATTLE_TEST("Spit Up's Stockpile's are romoved if hit into Protect") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_PROTECT) == EFFECT_PROTECT); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_STOCKPILE); } + TURN { MOVE(opponent, MOVE_PROTECT); MOVE(player, MOVE_SPIT_UP); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STOCKPILE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIT_UP, player); + } THEN { + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE); + EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE); + } +} diff --git a/test/battle/move_effect/stomping_tantrum.c b/test/battle/move_effect/stomping_tantrum.c index 43580f685..5d2932a07 100644 --- a/test/battle/move_effect/stomping_tantrum.c +++ b/test/battle/move_effect/stomping_tantrum.c @@ -157,3 +157,29 @@ SINGLE_BATTLE_TEST("Stomping Tantrum will deal double damage if user was immune EXPECT_MUL_EQ(damage[0], Q_4_12(2.0), damage[1]); } } + +DOUBLE_BATTLE_TEST("Stomping Tantrum will not deal double damage if spread moved failed one target") +{ + s16 damage[2]; + GIVEN { + ASSUME(GetMoveTarget(MOVE_EARTHQUAKE) == TARGET_FOES_AND_ALLY); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_PIDGEY); + } WHEN { + TURN { MOVE(playerLeft, MOVE_STOMPING_TANTRUM, target: opponentLeft); } + TURN { MOVE(playerLeft, MOVE_EARTHQUAKE); } + TURN { MOVE(playerLeft, MOVE_STOMPING_TANTRUM, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STOMPING_TANTRUM, playerLeft); + HP_BAR(opponentLeft, captureDamage: &damage[0]); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_EARTHQUAKE, playerLeft); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_STOMPING_TANTRUM, playerLeft); + HP_BAR(opponentLeft, captureDamage: &damage[1]); + } THEN { + EXPECT_EQ(damage[0], damage[1]); + } +} diff --git a/test/battle/move_effect/synchronoise.c b/test/battle/move_effect/synchronoise.c index a9db897ce..2c7600854 100644 --- a/test/battle/move_effect/synchronoise.c +++ b/test/battle/move_effect/synchronoise.c @@ -1,6 +1,17 @@ #include "global.h" #include "test/battle.h" +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_SYNCHRONOISE) == EFFECT_SYNCHRONOISE); + ASSUME(GetSpeciesType(SPECIES_WOBBUFFET, 0) == TYPE_PSYCHIC); + ASSUME(GetSpeciesType(SPECIES_WOBBUFFET, 1) == TYPE_PSYCHIC); + ASSUME(GetSpeciesType(SPECIES_BULBASAUR, 0) == TYPE_GRASS); + ASSUME(GetSpeciesType(SPECIES_BULBASAUR, 1) == TYPE_POISON); + ASSUME(GetSpeciesType(SPECIES_ARCANINE, 0) == TYPE_FIRE); + ASSUME(GetSpeciesType(SPECIES_ARCANINE, 1) == TYPE_FIRE); +} + DOUBLE_BATTLE_TEST("Synchronoise hits all Pokemon that share a type with the attacker") { GIVEN { @@ -73,4 +84,35 @@ DOUBLE_BATTLE_TEST("Synchronoise will fail if the corresponding typing mon prote } } +DOUBLE_BATTLE_TEST("Synchronoise will fail for a typeless user even if a target is typeless") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_BURN_UP) == EFFECT_FAIL_IF_NOT_ARG_TYPE); + PLAYER(SPECIES_ARCANINE) { Moves(MOVE_BURN_UP, MOVE_SYNCHRONOISE); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_ARCANINE) { Moves(MOVE_BURN_UP, MOVE_CELEBRATE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { + MOVE(playerLeft, MOVE_BURN_UP, target: opponentRight); + MOVE(opponentLeft, MOVE_BURN_UP, target: playerRight); + MOVE(playerRight, MOVE_CELEBRATE); + MOVE(opponentRight, MOVE_CELEBRATE); + } + TURN { + MOVE(playerLeft, MOVE_SYNCHRONOISE); + MOVE(opponentLeft, MOVE_CELEBRATE); + MOVE(playerRight, MOVE_CELEBRATE); + MOVE(opponentRight, MOVE_CELEBRATE); + } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SYNCHRONOISE, playerLeft); + MESSAGE("Arcanine used Synchronoise!"); + MESSAGE("It doesn't affect the opposing Arcanine…"); + MESSAGE("It doesn't affect Wobbuffet…"); + MESSAGE("It doesn't affect the opposing Wobbuffet…"); + NOT MESSAGE("But it failed!"); + } +} + TO_DO_BATTLE_TEST("TODO: Write Synchronoise (Move Effect) test titles") diff --git a/test/battle/move_effect/trick.c b/test/battle/move_effect/trick.c index 2110c8504..d9a924769 100644 --- a/test/battle/move_effect/trick.c +++ b/test/battle/move_effect/trick.c @@ -184,6 +184,18 @@ SINGLE_BATTLE_TEST("Trick fails if the target is behind a Substitute") } } +SINGLE_BATTLE_TEST("Trick can be used against targets with an active form change that doesn't require items") +{ + GIVEN { + PLAYER(SPECIES_XERNEAS); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_ORAN_BERRY); } + } WHEN { + TURN { MOVE(opponent, MOVE_TRICK); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRICK, opponent); + } +} + SINGLE_BATTLE_TEST("Trick does not remove the user's choice lock if both the target and use are holding choice items before Gen 5") { GIVEN { diff --git a/test/battle/move_effects_combined/hyperspace_fury.c b/test/battle/move_effects_combined/hyperspace_fury.c new file mode 100644 index 000000000..5c0a865c2 --- /dev/null +++ b/test/battle/move_effects_combined/hyperspace_fury.c @@ -0,0 +1,97 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_HYPERSPACE_FURY) == EFFECT_HYPERSPACE_FURY); + ASSUME(MoveHasAdditionalEffect(MOVE_HYPERSPACE_FURY, MOVE_EFFECT_FEINT)); + ASSUME(MoveHasAdditionalEffectSelf(MOVE_HYPERSPACE_FURY, MOVE_EFFECT_DEF_MINUS_1)); + ASSUME(GetMoveEffect(MOVE_PROTECT) == EFFECT_PROTECT); +} + +SINGLE_BATTLE_TEST("Hyperspace Fury fails if used by a Pokémon other than Hoopa Unbound") +{ + u32 species; + PARAMETRIZE { species = SPECIES_WOBBUFFET; } + PARAMETRIZE { species = SPECIES_HOOPA_CONFINED; } + PARAMETRIZE { species = SPECIES_HOOPA_UNBOUND; } + + GIVEN { + PLAYER(species); + OPPONENT(SPECIES_REGIROCK); + } WHEN { + TURN { MOVE(player, MOVE_HYPERSPACE_FURY); } + } SCENE { + switch (species) + { + case SPECIES_HOOPA_UNBOUND: + ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, player); + break; + case SPECIES_HOOPA_CONFINED: + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, player); + MESSAGE("But Hoopa can't use it the way it is now!"); + break; + case SPECIES_WOBBUFFET: + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, player); + MESSAGE("But Wobbuffet can't use the move!"); + break; + default: + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, player); + break; + } + } +} + +DOUBLE_BATTLE_TEST("Hyperspace Fury hits the target through Protect and breaks it") +{ + GIVEN { + PLAYER(SPECIES_HOOPA_UNBOUND); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_REGIROCK); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_PROTECT); MOVE(playerLeft, MOVE_HYPERSPACE_FURY, target: opponentLeft); MOVE(playerRight, MOVE_SCRATCH, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, playerLeft); + HP_BAR(opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerRight); + HP_BAR(opponentLeft); + } +} + +SINGLE_BATTLE_TEST("Hyperspace Fury lowers the user's Defense by 1 stage after hitting the target") +{ + GIVEN { + PLAYER(SPECIES_HOOPA_UNBOUND); + OPPONENT(SPECIES_REGIROCK); + } WHEN { + TURN { MOVE(player, MOVE_HYPERSPACE_FURY); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + } THEN { + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1); + } +} + +DOUBLE_BATTLE_TEST("Hyperspace Fury breaks protection and lowers the user's Defense by 1 stage") +{ + GIVEN { + PLAYER(SPECIES_HOOPA_UNBOUND); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_REGIROCK); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_PROTECT); MOVE(playerLeft, MOVE_HYPERSPACE_FURY, target: opponentLeft); MOVE(playerRight, MOVE_SCRATCH, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, playerLeft); + HP_BAR(opponentLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerRight); + HP_BAR(opponentLeft); + } THEN { + EXPECT_EQ(playerLeft->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1); + } +} diff --git a/test/battle/move_flags/move_has_no_effect_on_same_type.c b/test/battle/move_flags/move_has_no_effect_on_same_type.c new file mode 100644 index 000000000..41e5774e1 --- /dev/null +++ b/test/battle/move_flags/move_has_no_effect_on_same_type.c @@ -0,0 +1,39 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(MoveHasNoEffectOnSameType(MOVE_SHEER_COLD)); +} + +SINGLE_BATTLE_TEST("Sheer Cold does affect Ice-type Pokémon (Gen3-6)") +{ + GIVEN { + WITH_CONFIG(CONFIG_SHEER_COLD_IMMUNITY, GEN_6); + ASSUME(GetSpeciesType(SPECIES_GLALIE, 0) == TYPE_ICE); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_GLALIE); + } WHEN { + TURN { MOVE(player, MOVE_SHEER_COLD); } + } SCENE { + NOT MESSAGE("It doesn't affect the opposing Glalie…"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SHEER_COLD, player); + HP_BAR(opponent, hp: 0); + } +} + +SINGLE_BATTLE_TEST("Sheer Cold doesn't affect Ice-type Pokémon (Gen7+)") +{ + GIVEN { + WITH_CONFIG(CONFIG_SHEER_COLD_IMMUNITY, GEN_7); + ASSUME(GetSpeciesType(SPECIES_GLALIE, 0) == TYPE_ICE); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_GLALIE); + } WHEN { + TURN { MOVE(player, MOVE_SHEER_COLD); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SHEER_COLD, player); + MESSAGE("It doesn't affect the opposing Glalie…"); + } +} + diff --git a/test/battle/volatiles/confusion.c b/test/battle/volatiles/confusion.c index b9a123795..246111702 100644 --- a/test/battle/volatiles/confusion.c +++ b/test/battle/volatiles/confusion.c @@ -52,3 +52,34 @@ SINGLE_BATTLE_TEST("Confusion self hit does not consume Gems") MESSAGE("It hurt itself in its confusion!"); } } + +SINGLE_BATTLE_TEST("Confusion damage activates Focus Sash") +{ + GIVEN { + ASSUME(GetItemHoldEffect(ITEM_FOCUS_SASH) == HOLD_EFFECT_FOCUS_SASH); + PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(1); Item(ITEM_FOCUS_SASH); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponent, MOVE_CONFUSE_RAY); MOVE(player, MOVE_POUND); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFUSE_RAY, opponent); + HP_BAR(player); // Confusion damage + MESSAGE("Wobbuffet hung on using its Focus Sash!"); + } +} + +SINGLE_BATTLE_TEST("Confusion damage Breaks Ice Face") +{ + GIVEN { + PLAYER(SPECIES_EISCUE) { Ability(ABILITY_ICE_FACE); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponent, MOVE_CONFUSE_RAY); MOVE(player, MOVE_FAIRY_WIND); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFUSE_RAY, opponent); + NOT HP_BAR(player); // Confusion damage is blocked by Ice Face + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); + } THEN { + EXPECT_EQ(player->species, SPECIES_EISCUE_NOICE); + } +} diff --git a/test/species.c b/test/species.c index 8a089af9f..f9ca25fa7 100644 --- a/test/species.c +++ b/test/species.c @@ -68,14 +68,39 @@ TEST("Forms have the appropriate species form changes") { if (gSpeciesInfo[i].isMegaEvolution || gSpeciesInfo[i].isGigantamax - || gSpeciesInfo[i].isUltraBurst) + || gSpeciesInfo[i].isUltraBurst + || gSpeciesInfo[i].isPrimalReversion) { PARAMETRIZE_LABEL("%S", gSpeciesInfo[i].speciesName) { species = i; } } } - EXPECT(DoesSpeciesHaveFormChangeMethod(species, FORM_CHANGE_END_BATTLE)); + bool32 hasBattleEnd = FALSE, hasFaint = FALSE; + + const struct FormChange *formChanges = GetSpeciesFormChanges(species); + EXPECT(formChanges != NULL); + + for (u32 j = 0; formChanges[j].method != FORM_CHANGE_TERMINATOR; j++) + { + if (species != formChanges[j].targetSpecies) + { + if (formChanges[j].method == FORM_CHANGE_END_BATTLE) + hasBattleEnd = TRUE; + else if (formChanges[j].method == FORM_CHANGE_FAINT) + hasFaint = TRUE; + } } + EXPECT(hasBattleEnd); + + // Primal Reversion don't change forms upon fainting + if (gSpeciesInfo[species].isMegaEvolution + || gSpeciesInfo[species].isGigantamax + || gSpeciesInfo[species].isUltraBurst) + { + EXPECT(hasFaint); + } +} + TEST("Form change targets have the appropriate species flags") { u32 i; diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 493069e21..5b7fcbd38 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -55,8 +55,8 @@ STATIC_ASSERT(sizeof(struct BattleTestRunnerState) <= sizeof(sBackupMapData), sB static void CB2_BattleTest_NextParameter(void); static void CB2_BattleTest_NextTrial(void); static void PushBattlerAction(u32 sourceLine, s32 battlerId, u32 actionType, u32 byte); -static void PrintAiMoveLog(u32 battlerId, u32 moveSlot, enum Move moveId, s32 totalScore); -static void ClearAiLog(u32 battlerId); +static void PrintAiMoveLog(enum BattlerId battlerId, u32 moveSlot, enum Move moveId, s32 totalScore); +static void ClearAiLog(enum BattlerId battlerId); static const char *BattlerIdentifier(s32 battlerId); NAKED static void InvokeSingleTestFunctionWithStack(void *results, u32 i, struct BattlePokemon *player, struct BattlePokemon *opponent, SingleBattleTestFunction function, void *stack) @@ -671,7 +671,7 @@ static u32 BattleTest_RandomUniform(enum RandomTag tag, u32 lo, u32 hi, bool32 ( const struct BattlerTurn *turn = NULL; if (gCurrentTurnActionNumber < gBattlersCount) { - u32 battlerId = gBattlerByTurnOrder[gCurrentTurnActionNumber]; + enum BattlerId battlerId = gBattlerByTurnOrder[gCurrentTurnActionNumber]; turn = &DATA.battleRecordTurns[gBattleResults.battleTurnCounter][battlerId]; if (turn && turn->rng.tag == tag) { @@ -733,7 +733,7 @@ static u32 BattleTest_RandomWeightedArray(enum RandomTag tag, u32 sum, u32 n, co const struct BattlerTurn *turn = NULL; if (gCurrentTurnActionNumber < gBattlersCount || tag == RNG_SHELL_SIDE_ARM) { - u32 battlerId = gBattlerByTurnOrder[gCurrentTurnActionNumber]; + enum BattlerId battlerId = gBattlerByTurnOrder[gCurrentTurnActionNumber]; turn = &DATA.battleRecordTurns[gBattleResults.battleTurnCounter][battlerId]; if (turn && turn->rng.tag == tag) return turn->rng.value; @@ -778,7 +778,7 @@ static const void *BattleTest_RandomElementArray(enum RandomTag tag, const void const struct BattlerTurn *turn = NULL; if (gCurrentTurnActionNumber < gBattlersCount) { - u32 battlerId = gBattlerByTurnOrder[gCurrentTurnActionNumber]; + enum BattlerId battlerId = gBattlerByTurnOrder[gCurrentTurnActionNumber]; turn = &DATA.battleRecordTurns[gBattleResults.battleTurnCounter][battlerId]; if (turn && turn->rng.tag == tag) { @@ -802,7 +802,7 @@ static const void *BattleTest_RandomElementArray(enum RandomTag tag, const void return RandomElementArrayDefaultValue(tag, array, size, count, caller); } -static s32 TryAbilityPopUp(s32 i, s32 n, u32 battlerId, enum Ability ability) +static s32 TryAbilityPopUp(s32 i, s32 n, enum BattlerId battlerId, enum Ability ability) { struct QueuedAbilityEvent *event; s32 iMax = i + n; @@ -820,7 +820,7 @@ static s32 TryAbilityPopUp(s32 i, s32 n, u32 battlerId, enum Ability ability) return -1; } -void TestRunner_Battle_RecordAbilityPopUp(u32 battlerId, enum Ability ability) +void TestRunner_Battle_RecordAbilityPopUp(enum BattlerId battlerId, enum Ability ability) { s32 queuedEvent; s32 match; @@ -930,7 +930,7 @@ void TestRunner_Battle_RecordAnimation(u32 animType, u32 animId) } } -static s32 TryHP(s32 i, s32 n, u32 battlerId, u32 oldHP, u32 newHP) +static s32 TryHP(s32 i, s32 n, enum BattlerId battlerId, u32 oldHP, u32 newHP) { struct QueuedHPEvent *event; s32 iMax = i + n; @@ -977,7 +977,7 @@ static s32 TryHP(s32 i, s32 n, u32 battlerId, u32 oldHP, u32 newHP) return -1; } -void TestRunner_Battle_RecordHP(u32 battlerId, u32 oldHP, u32 newHP) +void TestRunner_Battle_RecordHP(enum BattlerId battlerId, u32 oldHP, u32 newHP) { s32 queuedEvent; s32 match; @@ -1022,7 +1022,7 @@ void TestRunner_Battle_RecordHP(u32 battlerId, u32 oldHP, u32 newHP) } } -static s32 TrySubHit(s32 i, s32 n, u32 battlerId, u32 damage, bool32 broke) +static s32 TrySubHit(s32 i, s32 n, enum BattlerId battlerId, u32 damage, bool32 broke) { struct QueuedSubHitEvent *event; s32 iMax = i + n; @@ -1058,7 +1058,7 @@ static s32 TrySubHit(s32 i, s32 n, u32 battlerId, u32 damage, bool32 broke) return -1; } -void TestRunner_Battle_RecordSubHit(u32 battlerId, u32 damage, bool32 broke) +void TestRunner_Battle_RecordSubHit(enum BattlerId battlerId, u32 damage, bool32 broke) { s32 queuedEvent; s32 match; @@ -1120,7 +1120,7 @@ static const char *const sGimmickIdentifiers[GIMMICKS_COUNT] = [GIMMICK_TERA] = "Terastallize", }; -static u32 CountAiExpectMoves(struct ExpectedAIAction *expectedAction, u32 battlerId, bool32 printLog) +static u32 CountAiExpectMoves(struct ExpectedAIAction *expectedAction, enum BattlerId battlerId, bool32 printLog) { u32 i, countExpected = 0; for (i = 0; i < MAX_MON_MOVES; i++) @@ -1135,7 +1135,7 @@ static u32 CountAiExpectMoves(struct ExpectedAIAction *expectedAction, u32 battl return countExpected; } -void TestRunner_Battle_CheckChosenMove(u32 battlerId, enum Move moveId, u32 target, enum Gimmick gimmick) +void TestRunner_Battle_CheckChosenMove(enum BattlerId battlerId, enum Move moveId, u32 target, enum Gimmick gimmick) { const char *filename = gTestRunnerState.test->filename; u32 id = DATA.trial.aiActionsPlayed[battlerId]; @@ -1212,7 +1212,7 @@ void TestRunner_Battle_CheckChosenMove(u32 battlerId, enum Move moveId, u32 targ DATA.trial.aiActionsPlayed[battlerId]++; } -void TestRunner_Battle_CheckSwitch(u32 battlerId, u32 partyIndex) +void TestRunner_Battle_CheckSwitch(enum BattlerId battlerId, u32 partyIndex) { const char *filename = gTestRunnerState.test->filename; u32 id = DATA.trial.aiActionsPlayed[battlerId]; @@ -1258,7 +1258,7 @@ static const char *const sCmpToStringTable[] = [CMP_GREATER_THAN] = "GT", }; -static void CheckIfMaxScoreEqualExpectMove(u32 battlerId, s32 target, struct ExpectedAIAction *aiAction, const char *filename) +static void CheckIfMaxScoreEqualExpectMove(enum BattlerId battlerId, s32 target, struct ExpectedAIAction *aiAction, const char *filename) { u32 i; s32 *scores = gAiBattleData->finalScore[battlerId][target]; @@ -1297,7 +1297,7 @@ static void CheckIfMaxScoreEqualExpectMove(u32 battlerId, s32 target, struct Exp } } -static void PrintAiMoveLog(u32 battlerId, u32 moveSlot, enum Move moveId, s32 totalScore) +static void PrintAiMoveLog(enum BattlerId battlerId, u32 moveSlot, enum Move moveId, s32 totalScore) { s32 i, scoreFromLogs = 0; @@ -1339,7 +1339,7 @@ static void PrintAiMoveLog(u32 battlerId, u32 moveSlot, enum Move moveId, s32 to Test_MgbaPrintf("Total: %d\n", totalScore); } -static void ClearAiLog(u32 battlerId) +static void ClearAiLog(enum BattlerId battlerId) { u32 i, j; for (i = 0; i < MAX_MON_MOVES; i++) @@ -1351,7 +1351,7 @@ static void ClearAiLog(u32 battlerId) DATA.aiLogPrintedForMove[battlerId] = 0; } -void TestRunner_Battle_CheckAiMoveScores(u32 battlerId) +void TestRunner_Battle_CheckAiMoveScores(enum BattlerId battlerId) { s32 i; struct ExpectedAIAction *aiAction; @@ -1411,7 +1411,7 @@ void TestRunner_Battle_CheckAiMoveScores(u32 battlerId) } } -static s32 TryExp(s32 i, s32 n, u32 battlerId, u32 oldExp, u32 newExp) +static s32 TryExp(s32 i, s32 n, enum BattlerId battlerId, u32 oldExp, u32 newExp) { struct QueuedExpEvent *event; s32 iMax = i + n; @@ -1458,7 +1458,7 @@ static s32 TryExp(s32 i, s32 n, u32 battlerId, u32 oldExp, u32 newExp) return -1; } -void TestRunner_Battle_RecordExp(u32 battlerId, u32 oldExp, u32 newExp) +void TestRunner_Battle_RecordExp(enum BattlerId battlerId, u32 oldExp, u32 newExp) { s32 queuedEvent; s32 match; @@ -1595,7 +1595,7 @@ void TestRunner_Battle_RecordMessage(const u8 *string) } } -static s32 TryStatus(s32 i, s32 n, u32 battlerId, u32 status1) +static s32 TryStatus(s32 i, s32 n, enum BattlerId battlerId, u32 status1) { struct QueuedStatusEvent *event; s32 iMax = i + n; @@ -1617,7 +1617,7 @@ static s32 TryStatus(s32 i, s32 n, u32 battlerId, u32 status1) return -1; } -void TestRunner_Battle_RecordStatus1(u32 battlerId, u32 status1) +void TestRunner_Battle_RecordStatus1(enum BattlerId battlerId, u32 status1) { s32 queuedEvent; s32 match; @@ -1939,7 +1939,7 @@ void AIFlags_(u32 sourceLine, u64 flags) DATA.hasAI = TRUE; } -void BattlerAIFlags_(u32 sourceLine, u32 battler, u64 flags) +void BattlerAIFlags_(u32 sourceLine, enum BattlerId battler, u64 flags) { INVALID_IF(!IsAITest(), "AI_FLAGS is usable only in AI_SINGLE_BATTLE_TEST, AI_DOUBLE_BATTLE_TEST, AI_MULTI_BATTLE_TEST, and AI_TWO_VS_ONE_TEST"); DATA.recordedBattle.AI_scripts[battler] |= flags; @@ -2155,7 +2155,7 @@ void ClosePokemon(u32 sourceLine) DATA.currentMon = NULL; } -static void SetGimmick(u32 sourceLine, u32 battler, u32 partyIndex, enum Gimmick gimmick) +static void SetGimmick(u32 sourceLine, enum BattlerId battler, u32 partyIndex, enum Gimmick gimmick) { enum Gimmick currentGimmick = DATA.chosenGimmick[GetBattlerTrainer(battler)][partyIndex]; if (!((currentGimmick == GIMMICK_ULTRA_BURST && gimmick == GIMMICK_Z_MOVE) @@ -2493,7 +2493,7 @@ static void PushBattlerAction(u32 sourceLine, s32 battlerId, u32 actionType, u32 DATA.recordedBattle.battleRecord[battlerId][recordIndex] = byte; } -void TestRunner_Battle_CheckBattleRecordActionType(u32 battlerId, u32 recordIndex, u32 actionType) +void TestRunner_Battle_CheckBattleRecordActionType(enum BattlerId battlerId, u32 recordIndex, u32 actionType) { // An illegal move choice will cause the battle to request a new // move slot and target. This detects the move slot. @@ -2937,7 +2937,7 @@ void ExpectSendOut(u32 sourceLine, struct BattlePokemon *battler, u32 partyIndex gTestRunnerState.expectedFailState = EXPECT_FAIL_TURN_OPEN; } -s32 GetAiMoveTargetForScoreCompare(u32 battlerId, enum Move moveId, struct MoveContext *ctx, u32 sourceLine) +s32 GetAiMoveTargetForScoreCompare(enum BattlerId battlerId, enum Move moveId, struct MoveContext *ctx, u32 sourceLine) { s32 target; @@ -3100,9 +3100,10 @@ void UseItem(u32 sourceLine, struct BattlePokemon *battler, struct ItemContext c { s32 i; s32 battlerId = battler - gBattleMons; - bool32 requirePartyIndex = GetItemType(ctx.itemId) == ITEM_USE_PARTY_MENU - || GetItemType(ctx.itemId) == ITEM_USE_PARTY_MENU_MOVES - || (GetItemType(ctx.itemId) == ITEM_USE_BATTLER && GetBattleTest()->type != BATTLE_TEST_AI_DOUBLES && STATE->battlersCount > 2); + enum ItemType ctxItemType = GetItemType(ctx.itemId); + bool32 requirePartyIndex = ctxItemType == ITEM_USE_PARTY_MENU + || ctxItemType == ITEM_USE_PARTY_MENU_MOVES + || (ctxItemType == ITEM_USE_BATTLER && GetBattleTest()->type != BATTLE_TEST_AI_DOUBLES && STATE->battlersCount > 2); // Check general bad use. INVALID_IF(DATA.turnState == TURN_CLOSED, "USE_ITEM outside TURN"); INVALID_IF(DATA.actionBattlers & (1 << battlerId), "Multiple battler actions"); @@ -3112,7 +3113,7 @@ void UseItem(u32 sourceLine, struct BattlePokemon *battler, struct ItemContext c INVALID_IF(requirePartyIndex && ctx.partyIndex >= ((battlerId & BIT_SIDE) == B_SIDE_PLAYER ? DATA.playerPartySize : DATA.opponentPartySize), \ "USE_ITEM to invalid party index"); // Check move slot items. - if (GetItemType(ctx.itemId) == ITEM_USE_PARTY_MENU_MOVES) + if (ctxItemType == ITEM_USE_PARTY_MENU_MOVES) { INVALID_IF(!ctx.explicitMove, "%S requires an explicit move", GetItemName(ctx.itemId)); for (i = 0; i < MAX_MON_MOVES; i++) @@ -3174,7 +3175,7 @@ void CloseQueueGroup(u32 sourceLine) void QueueAbility(u32 sourceLine, struct BattlePokemon *battler, struct AbilityEventContext ctx) { - s32 battlerId = battler - gBattleMons; + enum BattlerId battlerId = battler - gBattleMons; if (gTestRunnerState.expectedFailState == EXPECT_FAIL_OPEN) gTestRunnerState.expectedFailState = EXPECT_FAIL_SCENE_OPEN; @@ -3475,7 +3476,7 @@ u32 TestRunner_Battle_GetChosenGimmick(enum BattleTrainer trainer, u32 partyInde // TODO: Consider storing the last successful i and searching from i+1 // to improve performance. -struct AILogLine *GetLogLine(u32 battlerId, u32 moveIndex) +struct AILogLine *GetLogLine(enum BattlerId battlerId, u32 moveIndex) { s32 i; @@ -3492,7 +3493,7 @@ struct AILogLine *GetLogLine(u32 battlerId, u32 moveIndex) return NULL; } -void TestRunner_Battle_AILogScore(const char *file, u32 line, u32 battlerId, u32 moveIndex, s32 score, bool32 setScore) +void TestRunner_Battle_AILogScore(const char *file, u32 line, enum BattlerId battlerId, u32 moveIndex, s32 score, bool32 setScore) { struct AILogLine *log; @@ -3505,12 +3506,12 @@ void TestRunner_Battle_AILogScore(const char *file, u32 line, u32 battlerId, u32 log->set = setScore; } -void TestRunner_Battle_AISetScore(const char *file, u32 line, u32 battlerId, u32 moveIndex, s32 score) +void TestRunner_Battle_AISetScore(const char *file, u32 line, enum BattlerId battlerId, u32 moveIndex, s32 score) { TestRunner_Battle_AILogScore(file, line, battlerId, moveIndex, score, TRUE); } -void TestRunner_Battle_AIAdjustScore(const char *file, u32 line, u32 battlerId, u32 moveIndex, s32 score) +void TestRunner_Battle_AIAdjustScore(const char *file, u32 line, enum BattlerId battlerId, u32 moveIndex, s32 score) { TestRunner_Battle_AILogScore(file, line, battlerId, moveIndex, score, FALSE); }