summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorentrpntr <entrpntr@gmail.com>2020-04-19 15:25:51 -0400
committerentrpntr <entrpntr@gmail.com>2020-04-19 15:25:51 -0400
commit7b63a9c032a130a08fc554b9b6790fe47d170530 (patch)
treec5a5169c8c3946154cf846f0c61d87516bb0c87d
parente953302d3f81f080fd5d8423000496ce2fad36d3 (diff)
Finish disassembling effect commands.
-rw-r--r--constants/battle_constants.asm5
-rw-r--r--data/battle/stat_multipliers.asm20
-rw-r--r--data/battle/stat_names.asm10
-rw-r--r--data/moves/magnitude_power.asm9
-rw-r--r--data/moves/metronome_exception_moves.asm17
-rw-r--r--data/moves/present_power.asm6
-rwxr-xr-xdata/text/battle.asm6
-rw-r--r--data/text/common_2.asm26
-rw-r--r--engine/battle/effect_commands.asm3292
-rw-r--r--engine/battle/move_effects/attract.asm76
-rw-r--r--engine/battle/move_effects/baton_pass.asm194
-rw-r--r--engine/battle/move_effects/belly_drum.asm30
-rw-r--r--engine/battle/move_effects/bide.asm100
-rw-r--r--engine/battle/move_effects/conversion.asm96
-rw-r--r--engine/battle/move_effects/curse.asm93
-rw-r--r--engine/battle/move_effects/disable.asm72
-rw-r--r--engine/battle/move_effects/endure.asm16
-rw-r--r--engine/battle/move_effects/focus_energy.asm15
-rw-r--r--engine/battle/move_effects/foresight.asm22
-rw-r--r--engine/battle/move_effects/frustration.asm27
-rw-r--r--engine/battle/move_effects/fury_cutter.asm55
-rw-r--r--engine/battle/move_effects/future_sight.asm81
-rw-r--r--engine/battle/move_effects/hidden_power.asm8
-rw-r--r--engine/battle/move_effects/leech_seed.asm40
-rw-r--r--engine/battle/move_effects/magnitude.asm29
-rw-r--r--engine/battle/move_effects/metronome.asm43
-rw-r--r--engine/battle/move_effects/mimic.asm50
-rw-r--r--engine/battle/move_effects/mirror_coat.asm60
-rw-r--r--engine/battle/move_effects/mirror_move.asm51
-rw-r--r--engine/battle/move_effects/mist.asm15
-rw-r--r--engine/battle/move_effects/nightmare.asm37
-rw-r--r--engine/battle/move_effects/pay_day.asm26
-rw-r--r--engine/battle/move_effects/perish_song.asm38
-rw-r--r--engine/battle/move_effects/present.asm75
-rw-r--r--engine/battle/move_effects/protect.asm75
-rw-r--r--engine/battle/move_effects/psych_up.asm47
-rw-r--r--engine/battle/move_effects/pursuit.asm24
-rw-r--r--engine/battle/move_effects/rage.asm6
-rw-r--r--engine/battle/move_effects/rain_dance.asm9
-rw-r--r--engine/battle/move_effects/rapid_spin.asm36
-rw-r--r--engine/battle/move_effects/return.asm25
-rw-r--r--engine/battle/move_effects/rollout.asm95
-rw-r--r--engine/battle/move_effects/safeguard.asm23
-rw-r--r--engine/battle/move_effects/sandstorm.asm18
-rw-r--r--engine/battle/move_effects/selfdestruct.asm30
-rw-r--r--engine/battle/move_effects/spikes.asm26
-rw-r--r--engine/battle/move_effects/splash.asm3
-rw-r--r--engine/battle/move_effects/substitute.asm90
-rw-r--r--engine/battle/move_effects/sunny_day.asm9
-rw-r--r--engine/battle/move_effects/teleport.asm87
-rw-r--r--engine/battle/move_effects/thief.asm112
-rw-r--r--engine/battle/move_effects/thunder.asm18
-rw-r--r--engine/battle/move_effects/transform.asm155
-rwxr-xr-xengine/items/item_effects.asm4
-rw-r--r--main.asm161
-rw-r--r--wram.asm66
56 files changed, 5718 insertions, 141 deletions
diff --git a/constants/battle_constants.asm b/constants/battle_constants.asm
index 4fc3020b..796697db 100644
--- a/constants/battle_constants.asm
+++ b/constants/battle_constants.asm
@@ -90,8 +90,6 @@ const_value SET 1
const BATTLETYPE_TREE
const BATTLETYPE_TRAP
const BATTLETYPE_FORCEITEM
- const BATTLETYPE_CELEBI
- const BATTLETYPE_SUICUNE
; battle variables
const_def
@@ -248,3 +246,6 @@ SUBSTATUS_CURLED EQU 0
const WIN
const LOSE
const DRAW
+
+BATTLERESULT_BOX_FULL EQU 7
+BATTLERESULT_BITMASK EQU (1 << BATTLERESULT_BOX_FULL)
diff --git a/data/battle/stat_multipliers.asm b/data/battle/stat_multipliers.asm
new file mode 100644
index 00000000..bbb1cadd
--- /dev/null
+++ b/data/battle/stat_multipliers.asm
@@ -0,0 +1,20 @@
+; Multiplier ratios for all stats from modifier -6 to +6
+; (except accuracy, see data/battle/accuracy_multipliers.asm).
+
+; This table is identical to data/battle/stat_multipliers_2.asm.
+; This one is used by CalcBattleStats.
+
+StatLevelMultipliers:
+ db 25, 100 ; -6 = 25%
+ db 28, 100 ; -5 = 28%
+ db 33, 100 ; -4 = 33%
+ db 40, 100 ; -3 = 40%
+ db 50, 100 ; -2 = 50%
+ db 66, 100 ; -1 = 66%
+ db 1, 1 ; 0 = 100%
+ db 15, 10 ; +1 = 150%
+ db 2, 1 ; +2 = 200%
+ db 25, 10 ; +3 = 250%
+ db 3, 1 ; +4 = 300%
+ db 35, 10 ; +5 = 350%
+ db 4, 1 ; +6 = 400%
diff --git a/data/battle/stat_names.asm b/data/battle/stat_names.asm
new file mode 100644
index 00000000..a144a225
--- /dev/null
+++ b/data/battle/stat_names.asm
@@ -0,0 +1,10 @@
+StatNames:
+; entries correspond to stat ids
+ db "ATTACK@"
+ db "DEFENSE@"
+ db "SPEED@"
+ db "SPCL.ATK@"
+ db "SPCL.DEF@"
+ db "ACCURACY@"
+ db "EVASION@"
+ db "ABILITY@" ; used for BattleCommand_Curse
diff --git a/data/moves/magnitude_power.asm b/data/moves/magnitude_power.asm
new file mode 100644
index 00000000..7359bdb1
--- /dev/null
+++ b/data/moves/magnitude_power.asm
@@ -0,0 +1,9 @@
+MagnitudePower:
+ ; chance, power, magnitude #
+ db 13, 10, 4
+ db 38, 30, 5
+ db 89, 50, 6
+ db 166, 70, 7
+ db 217, 90, 8
+ db 242, 110, 9
+ db 255, 150, 10
diff --git a/data/moves/metronome_exception_moves.asm b/data/moves/metronome_exception_moves.asm
new file mode 100644
index 00000000..a5aa4413
--- /dev/null
+++ b/data/moves/metronome_exception_moves.asm
@@ -0,0 +1,17 @@
+; Metronome cannot turn into these moves.
+
+MetronomeExcepts:
+ db NO_MOVE
+ db METRONOME
+ db STRUGGLE
+ db SKETCH
+ db MIMIC
+ db COUNTER
+ db MIRROR_COAT
+ db PROTECT
+ db DETECT
+ db ENDURE
+ db DESTINY_BOND
+ db SLEEP_TALK
+ db THIEF
+ db -1
diff --git a/data/moves/present_power.asm b/data/moves/present_power.asm
new file mode 100644
index 00000000..885e9c69
--- /dev/null
+++ b/data/moves/present_power.asm
@@ -0,0 +1,6 @@
+PresentPower:
+ ; chance, power
+ db 40 percent, 40 ; 40%
+ db 70 percent + 1, 80 ; 30%
+ db 80 percent, 120 ; 10%
+ db -1 ; 20% chance to heal instead
diff --git a/data/text/battle.asm b/data/text/battle.asm
index a222cac5..cccc9421 100755
--- a/data/text/battle.asm
+++ b/data/text/battle.asm
@@ -442,7 +442,7 @@ UsedBindText:
cont "<TARGET>!"
prompt
-WhirlpoolTrapText:
+WasTrappedText:
text "<TARGET>"
line "was trapped!"
prompt
@@ -806,7 +806,7 @@ SubFadedText:
line "SUBSTITUTE faded!"
prompt
-LearnedMoveText:
+MimicLearnedMoveText:
text "<USER>"
line "learned"
cont "@"
@@ -1072,7 +1072,7 @@ BeatUpAttackText:
line "attack!"
done
-CanReceiveGiftText:
+CantReceiveGiftText:
text "<TARGET> can't"
line "receive the gift!"
prompt
diff --git a/data/text/common_2.asm b/data/text/common_2.asm
index 355c88bb..d82fdccd 100644
--- a/data/text/common_2.asm
+++ b/data/text/common_2.asm
@@ -372,65 +372,65 @@ _EndUsedMove5Text::
text "!"
done
-UnknownText_0x1c0cc6::
+Text_BattleEffectActivate::
text "<USER>'s"
line "@"
text_ram wStringBuffer2
db "@@"
-UnknownText_0x1c0cd0::
+_BattleStatWentWayUpText::
text_pause
text "<SCROLL>went way up!"
prompt
-UnknownText_0x1c0ce0::
+_BattleStatWentUpText::
text " went up!"
prompt
-UnknownText_0x1c0ceb::
+Text_BattleFoeEffectActivate::
text "<TARGET>'s"
line "@"
text_ram wStringBuffer2
db "@@"
-UnknownText_0x1c0cf5::
+_BattleStatSharplyFellText::
text_pause
text "<SCROLL>sharply fell!"
prompt
-UnknownText_0x1c0d06::
+_BattleStatFellText::
text " fell!"
prompt
-UnknownText_0x1c0d0e::
+Text_BattleUser::
text "<USER>@@"
-UnknownText_0x1c0d12::
+_BattleMadeWhirlwindText::
text_start
line "made a whirlwind!"
prompt
-UnknownText_0x1c0d26::
+_BattleTookSunlightText::
text_start
line "took in sunlight!"
prompt
-UnknownText_0x1c0d3a::
+_BattleLoweredHeadText::
text_start
line "lowered its head!"
prompt
-UnknownText_0x1c0d4e::
+_BattleGlowingText::
text_start
line "is glowing!"
prompt
-UnknownText_0x1c0d5c::
+_BattleFlewText::
text_start
line "flew up high!"
prompt
-UnknownText_0x1c0d6c::
+_BattleDugText::
text_start
line "dug a hole!"
prompt
diff --git a/engine/battle/effect_commands.asm b/engine/battle/effect_commands.asm
index 7734c66f..815cc17d 100644
--- a/engine/battle/effect_commands.asm
+++ b/engine/battle/effect_commands.asm
@@ -3571,3 +3571,3295 @@ DoSubstituteDamage:
call RefreshBattleHuds
.done
jp ResetDamage
+
+UpdateMoveData:
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVarAddr
+ ld d, h
+ ld e, l
+
+ ld a, BATTLE_VARS_MOVE
+ call GetBattleVar
+ ld [wCurSpecies], a
+ ld [wNamedObjectIndexBuffer], a
+
+ dec a
+ call GetMoveData
+ call GetMoveName
+ jp CopyName1
+
+BattleCommand_SleepTarget:
+; sleeptarget
+
+ call GetOpponentItem
+ ld a, b
+ cp HELD_PREVENT_SLEEP
+ jr nz, .not_protected_by_item
+
+ ld a, [hl]
+ ld [wNamedObjectIndexBuffer], a
+ call GetItemName
+ ld hl, ProtectedByText
+ jr .fail
+
+.not_protected_by_item
+ ld a, BATTLE_VARS_STATUS_OPP
+ call GetBattleVarAddr
+ ld d, h
+ ld e, l
+ ld a, [de]
+ and SLP
+ ld hl, AlreadyAsleepText
+ jr nz, .fail
+
+ ld a, [wAttackMissed]
+ and a
+ jp nz, PrintDidntAffect2
+
+ ld hl, DidntAffect1Text
+ call .CheckAIRandomFail
+ jr c, .fail
+
+ ld a, [de]
+ and a
+ jr nz, .fail
+
+ call CheckSubstituteOpp
+ jr nz, .fail
+
+ call AnimateCurrentMove
+
+.random_loop
+ call BattleRandom
+ and 7
+ jr z, .random_loop
+ cp 7
+ jr z, .random_loop
+ inc a
+ ld [de], a
+ call UpdateOpponentInParty
+ call RefreshBattleHuds
+
+ ld hl, FellAsleepText
+ call StdBattleTextbox
+
+ farcall UseHeldStatusHealingItem
+ ret nz
+
+ call OpponentCantMove
+ ret
+
+.fail
+ call AnimateFailedMove
+ jp StdBattleTextbox
+
+.CheckAIRandomFail:
+ ; Enemy turn
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .dont_fail
+
+ ; Not in link battle
+ ld a, [wLinkMode]
+ and a
+ jr nz, .dont_fail
+
+ ; Not locked-on by the enemy
+ ld a, [wPlayerSubStatus5]
+ bit SUBSTATUS_LOCK_ON, a
+ jr nz, .dont_fail
+
+ call BattleRandom
+ cp 25 percent + 1 ; 25% chance AI fails
+ ret c
+
+.dont_fail
+ xor a
+ ret
+
+BattleCommand_PoisonTarget:
+; poisontarget
+
+ call CheckSubstituteOpp
+ ret nz
+ ld a, BATTLE_VARS_STATUS_OPP
+ call GetBattleVarAddr
+ and a
+ ret nz
+ ld a, [wTypeModifier]
+ and $7f
+ ret z
+ call CheckIfTargetIsPoisonType
+ ret z
+ call GetOpponentItem
+ ld a, b
+ cp HELD_PREVENT_POISON
+ ret z
+ ld a, [wEffectFailed]
+ and a
+ ret nz
+ call SafeCheckSafeguard
+ ret nz
+
+ call PoisonOpponent
+ ld de, ANIM_PSN
+ call PlayOpponentBattleAnim
+ call RefreshBattleHuds
+
+ ld hl, WasPoisonedText
+ call StdBattleTextbox
+
+ farcall UseHeldStatusHealingItem
+ ret
+
+BattleCommand_Poison:
+; poison
+
+ ld hl, DoesntAffectText
+ ld a, [wTypeModifier]
+ and $7f
+ jp z, .failed
+
+ call CheckIfTargetIsPoisonType
+ jp z, .failed
+
+ ld a, BATTLE_VARS_STATUS_OPP
+ call GetBattleVar
+ ld b, a
+ ld hl, AlreadyPoisonedText
+ and 1 << PSN
+ jp nz, .failed
+
+ call GetOpponentItem
+ ld a, b
+ cp HELD_PREVENT_POISON
+ jr nz, .do_poison
+ ld a, [hl]
+ ld [wNamedObjectIndexBuffer], a
+ call GetItemName
+ ld hl, ProtectedByText
+ jr .failed
+
+.do_poison
+ ld hl, DidntAffect1Text
+ ld a, BATTLE_VARS_STATUS_OPP
+ call GetBattleVar
+ and a
+ jr nz, .failed
+
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .dont_sample_failure
+
+ ld a, [wLinkMode]
+ and a
+ jr nz, .dont_sample_failure
+
+ ld a, [wPlayerSubStatus5]
+ bit SUBSTATUS_LOCK_ON, a
+ jr nz, .dont_sample_failure
+
+ call BattleRandom
+ cp 25 percent + 1 ; 25% chance AI fails
+ jr c, .failed
+
+.dont_sample_failure
+ call CheckSubstituteOpp
+ jr nz, .failed
+ ld a, [wAttackMissed]
+ and a
+ jr nz, .failed
+ call .check_toxic
+ jr z, .toxic
+
+ call .apply_poison
+ ld hl, WasPoisonedText
+ call StdBattleTextbox
+ jr .finished
+
+.toxic
+ set SUBSTATUS_TOXIC, [hl]
+ xor a
+ ld [de], a
+ call .apply_poison
+
+ ld hl, BadlyPoisonedText
+ call StdBattleTextbox
+
+.finished
+ farcall UseHeldStatusHealingItem
+ ret
+
+.failed
+ call AnimateFailedMove
+ jp StdBattleTextbox
+
+.apply_poison
+ call AnimateCurrentMove
+ call PoisonOpponent
+ jp RefreshBattleHuds
+
+.check_toxic
+ ld a, BATTLE_VARS_SUBSTATUS5_OPP
+ call GetBattleVarAddr
+ ldh a, [hBattleTurn]
+ and a
+ ld de, wEnemyToxicCount
+ jr z, .ok
+ ld de, wPlayerToxicCount
+.ok
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ cp EFFECT_TOXIC
+ ret
+
+CheckIfTargetIsPoisonType:
+ ld de, wEnemyMonType1
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .ok
+ ld de, wBattleMonType1
+.ok
+ ld a, [de]
+ inc de
+ cp POISON
+ ret z
+ ld a, [de]
+ cp POISON
+ ret
+
+PoisonOpponent:
+ ld a, BATTLE_VARS_STATUS_OPP
+ call GetBattleVarAddr
+ set PSN, [hl]
+ jp UpdateOpponentInParty
+
+BattleCommand_DrainTarget:
+; draintarget
+ call SapHealth
+ ld hl, SuckedHealthText
+ jp StdBattleTextbox
+
+BattleCommand_EatDream:
+; eatdream
+ call SapHealth
+ ld hl, DreamEatenText
+ jp StdBattleTextbox
+
+SapHealth:
+ ; Divide damage by 2, store it in hDividend
+ ld hl, wCurDamage
+ ld a, [hli]
+ srl a
+ ldh [hDividend], a
+ ld b, a
+ ld a, [hl]
+ rr a
+ ldh [hDividend + 1], a
+ or b
+ jr nz, .at_least_one
+ ld a, 1
+ ldh [hDividend + 1], a
+.at_least_one
+
+ ld hl, wBattleMonHP
+ ld de, wBattleMonMaxHP
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .battlemonhp
+ ld hl, wEnemyMonHP
+ ld de, wEnemyMonMaxHP
+.battlemonhp
+
+ ; Store current HP in little endian wBuffer3/4
+ ld bc, wBuffer4
+ ld a, [hli]
+ ld [bc], a
+ ld a, [hl]
+ dec bc
+ ld [bc], a
+
+ ; Store max HP in little endian wBuffer1/2
+ ld a, [de]
+ dec bc
+ ld [bc], a
+ inc de
+ ld a, [de]
+ dec bc
+ ld [bc], a
+
+ ; Add hDividend to current HP and copy it to little endian wBuffer5/6
+ ldh a, [hDividend + 1]
+ ld b, [hl]
+ add b
+ ld [hld], a
+ ld [wBuffer5], a
+ ldh a, [hDividend]
+ ld b, [hl]
+ adc b
+ ld [hli], a
+ ld [wBuffer6], a
+ jr c, .max_hp
+
+ ; Substract current HP from max HP (to see if we have more than max HP)
+ ld a, [hld]
+ ld b, a
+ ld a, [de]
+ dec de
+ sub b
+ ld a, [hli]
+ ld b, a
+ ld a, [de]
+ inc de
+ sbc b
+ jr nc, .finish
+
+.max_hp
+ ; Load max HP into current HP and copy it to little endian wBuffer5/6
+ ld a, [de]
+ ld [hld], a
+ ld [wBuffer5], a
+ dec de
+ ld a, [de]
+ ld [hli], a
+ ld [wBuffer6], a
+ inc de
+
+.finish
+ ldh a, [hBattleTurn]
+ and a
+ hlcoord 10, 9
+ ld a, $1
+ jr z, .hp_bar
+ hlcoord 2, 2
+ xor a
+.hp_bar
+ ld [wWhichHPBar], a
+ predef AnimateHPBar
+ call RefreshBattleHuds
+ jp UpdateBattleMonInParty
+
+BattleCommand_BurnTarget:
+; burntarget
+
+ xor a
+ ld [wNumHits], a
+ call CheckSubstituteOpp
+ ret nz
+ ld a, BATTLE_VARS_STATUS_OPP
+ call GetBattleVarAddr
+ and a
+ jp nz, Defrost
+ ld a, [wTypeModifier]
+ and $7f
+ ret z
+ call CheckMoveTypeMatchesTarget ; Don't burn a Fire-type
+ ret z
+ call GetOpponentItem
+ ld a, b
+ cp HELD_PREVENT_BURN
+ ret z
+ ld a, [wEffectFailed]
+ and a
+ ret nz
+ call SafeCheckSafeguard
+ ret nz
+ ld a, BATTLE_VARS_STATUS_OPP
+ call GetBattleVarAddr
+ set BRN, [hl]
+ call UpdateOpponentInParty
+ ld hl, ApplyBrnEffectOnAttack
+ call CallBattleCore
+ ld de, ANIM_BRN
+ call PlayOpponentBattleAnim
+ call RefreshBattleHuds
+
+ ld hl, WasBurnedText
+ call StdBattleTextbox
+
+ farcall UseHeldStatusHealingItem
+ ret
+
+Defrost:
+ ld a, [hl]
+ and 1 << FRZ
+ ret z
+
+ xor a
+ ld [hl], a
+
+ ldh a, [hBattleTurn]
+ and a
+ ld a, [wCurOTMon]
+ ld hl, wOTPartyMon1Status
+ jr z, .ok
+ ld hl, wPartyMon1Status
+ ld a, [wCurBattleMon]
+.ok
+
+ call GetPartyLocation
+ xor a
+ ld [hl], a
+ call UpdateOpponentInParty
+
+ ld hl, DefrostedOpponentText
+ jp StdBattleTextbox
+
+BattleCommand_FreezeTarget:
+; freezetarget
+
+ xor a
+ ld [wNumHits], a
+ call CheckSubstituteOpp
+ ret nz
+ ld a, BATTLE_VARS_STATUS_OPP
+ call GetBattleVarAddr
+ and a
+ ret nz
+ ld a, [wTypeModifier]
+ and $7f
+ ret z
+ ld a, [wBattleWeather]
+ cp WEATHER_SUN
+ ret z
+ call CheckMoveTypeMatchesTarget ; Don't freeze an Ice-type
+ ret z
+ call GetOpponentItem
+ ld a, b
+ cp HELD_PREVENT_FREEZE
+ ret z
+ ld a, [wEffectFailed]
+ and a
+ ret nz
+ call SafeCheckSafeguard
+ ret nz
+ ld a, BATTLE_VARS_STATUS_OPP
+ call GetBattleVarAddr
+ set FRZ, [hl]
+ call UpdateOpponentInParty
+ ld de, ANIM_FRZ
+ call PlayOpponentBattleAnim
+ call RefreshBattleHuds
+
+ ld hl, WasFrozenText
+ call StdBattleTextbox
+
+ farcall UseHeldStatusHealingItem
+ ret nz
+
+ call OpponentCantMove
+ call EndRechargeOpp
+ ld hl, wEnemyJustGotFrozen
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .finish
+ ld hl, wPlayerJustGotFrozen
+.finish
+ ld [hl], $1
+ ret
+
+BattleCommand_ParalyzeTarget:
+; paralyzetarget
+
+ xor a
+ ld [wNumHits], a
+ call CheckSubstituteOpp
+ ret nz
+ ld a, BATTLE_VARS_STATUS_OPP
+ call GetBattleVarAddr
+ and a
+ ret nz
+ ld a, [wTypeModifier]
+ and $7f
+ ret z
+ call GetOpponentItem
+ ld a, b
+ cp HELD_PREVENT_PARALYZE
+ ret z
+ ld a, [wEffectFailed]
+ and a
+ ret nz
+ call SafeCheckSafeguard
+ ret nz
+ ld a, BATTLE_VARS_STATUS_OPP
+ call GetBattleVarAddr
+ set PAR, [hl]
+ call UpdateOpponentInParty
+ ld hl, ApplyPrzEffectOnSpeed
+ call CallBattleCore
+ ld de, ANIM_PAR
+ call PlayOpponentBattleAnim
+ call RefreshBattleHuds
+ call PrintParalyze
+ ld hl, UseHeldStatusHealingItem
+ jp CallBattleCore
+
+BattleCommand_AttackUp:
+; attackup
+ ld b, ATTACK
+ jr BattleCommand_StatUp
+
+BattleCommand_DefenseUp:
+; defenseup
+ ld b, DEFENSE
+ jr BattleCommand_StatUp
+
+BattleCommand_SpeedUp:
+; speedup
+ ld b, SPEED
+ jr BattleCommand_StatUp
+
+BattleCommand_SpecialAttackUp:
+; specialattackup
+ ld b, SP_ATTACK
+ jr BattleCommand_StatUp
+
+BattleCommand_SpecialDefenseUp:
+; specialdefenseup
+ ld b, SP_DEFENSE
+ jr BattleCommand_StatUp
+
+BattleCommand_AccuracyUp:
+; accuracyup
+ ld b, ACCURACY
+ jr BattleCommand_StatUp
+
+BattleCommand_EvasionUp:
+; evasionup
+ ld b, EVASION
+ jr BattleCommand_StatUp
+
+BattleCommand_AttackUp2:
+; attackup2
+ ld b, $10 | ATTACK
+ jr BattleCommand_StatUp
+
+BattleCommand_DefenseUp2:
+; defenseup2
+ ld b, $10 | DEFENSE
+ jr BattleCommand_StatUp
+
+BattleCommand_SpeedUp2:
+; speedup2
+ ld b, $10 | SPEED
+ jr BattleCommand_StatUp
+
+BattleCommand_SpecialAttackUp2:
+; specialattackup2
+ ld b, $10 | SP_ATTACK
+ jr BattleCommand_StatUp
+
+BattleCommand_SpecialDefenseUp2:
+; specialdefenseup2
+ ld b, $10 | SP_DEFENSE
+ jr BattleCommand_StatUp
+
+BattleCommand_AccuracyUp2:
+; accuracyup2
+ ld b, $10 | ACCURACY
+ jr BattleCommand_StatUp
+
+BattleCommand_EvasionUp2:
+; evasionup2
+ ld b, $10 | EVASION
+ jr BattleCommand_StatUp
+
+BattleCommand_StatUp:
+; statup
+ call RaiseStat
+ ld a, [wFailedMessage]
+ and a
+ ret nz
+ jp MinimizeDropSub
+
+RaiseStat:
+ ld a, b
+ ld [wLoweredStat], a
+ ld hl, wPlayerStatLevels
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_stat_levels
+ ld hl, wEnemyStatLevels
+.got_stat_levels
+ ld a, [wAttackMissed]
+ and a
+ jp nz, .stat_raise_failed
+ ld a, [wEffectFailed]
+ and a
+ jp nz, .stat_raise_failed
+ ld a, [wLoweredStat]
+ and $f
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld b, [hl]
+ inc b
+ ld a, $d
+ cp b
+ jp c, .cant_raise_stat
+ ld a, [wLoweredStat]
+ and $f0
+ jr z, .got_num_stages
+ inc b
+ ld a, $d
+ cp b
+ jr nc, .got_num_stages
+ ld b, a
+.got_num_stages
+ ld [hl], b
+ push hl
+ ld a, c
+ cp $5
+ jr nc, .done_calcing_stats
+ ld hl, wBattleMonStats + 1
+ ld de, wPlayerStats
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_stats_pointer
+ ld hl, wEnemyMonStats + 1
+ ld de, wEnemyStats
+.got_stats_pointer
+ push bc
+ sla c
+ ld b, 0
+ add hl, bc
+ ld a, c
+ add e
+ ld e, a
+ jr nc, .no_carry
+ inc d
+.no_carry
+ pop bc
+ ld a, [hld]
+ sub LOW(MAX_STAT_VALUE)
+ jr nz, .not_already_max
+ ld a, [hl]
+ sbc HIGH(MAX_STAT_VALUE)
+ jp z, .stats_already_max
+.not_already_max
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .calc_player_stats
+ call CalcEnemyStats
+ jr .done_calcing_stats
+
+.calc_player_stats
+ call CalcPlayerStats
+.done_calcing_stats
+ pop hl
+ xor a
+ ld [wFailedMessage], a
+ ret
+
+.stats_already_max
+ pop hl
+ dec [hl]
+ ; fallthrough
+
+.cant_raise_stat
+ ld a, $2
+ ld [wFailedMessage], a
+ ld a, $1
+ ld [wAttackMissed], a
+ ret
+
+.stat_raise_failed
+ ld a, $1
+ ld [wFailedMessage], a
+ ret
+
+MinimizeDropSub:
+; Lower the substitute if we're minimizing
+
+ ld bc, wPlayerMinimized
+ ld hl, DropPlayerSub
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .do_player
+ ld bc, wEnemyMinimized
+ ld hl, DropEnemySub
+.do_player
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+ cp MINIMIZE
+ ret nz
+
+ ld a, $1
+ ld [bc], a
+ ; check battle scene
+ ld a, [wOptions]
+ add a
+ ret nc
+
+ xor a
+ ldh [hBGMapMode], a
+ call CallBattleCore
+ call WaitBGMap
+ jp BattleCommand_MoveDelay
+
+BattleCommand_AttackDown:
+; attackdown
+ ld a, ATTACK
+ jr BattleCommand_StatDown
+
+BattleCommand_DefenseDown:
+; defensedown
+ ld a, DEFENSE
+ jr BattleCommand_StatDown
+
+BattleCommand_SpeedDown:
+; speeddown
+ ld a, SPEED
+ jr BattleCommand_StatDown
+
+BattleCommand_SpecialAttackDown:
+; specialattackdown
+ ld a, SP_ATTACK
+ jr BattleCommand_StatDown
+
+BattleCommand_SpecialDefenseDown:
+; specialdefensedown
+ ld a, SP_DEFENSE
+ jr BattleCommand_StatDown
+
+BattleCommand_AccuracyDown:
+; accuracydown
+ ld a, ACCURACY
+ jr BattleCommand_StatDown
+
+BattleCommand_EvasionDown:
+; evasiondown
+ ld a, EVASION
+ jr BattleCommand_StatDown
+
+BattleCommand_AttackDown2:
+; attackdown2
+ ld a, $10 | ATTACK
+ jr BattleCommand_StatDown
+
+BattleCommand_DefenseDown2:
+; defensedown2
+ ld a, $10 | DEFENSE
+ jr BattleCommand_StatDown
+
+BattleCommand_SpeedDown2:
+; speeddown2
+ ld a, $10 | SPEED
+ jr BattleCommand_StatDown
+
+BattleCommand_SpecialAttackDown2:
+; specialattackdown2
+ ld a, $10 | SP_ATTACK
+ jr BattleCommand_StatDown
+
+BattleCommand_SpecialDefenseDown2:
+; specialdefensedown2
+ ld a, $10 | SP_DEFENSE
+ jr BattleCommand_StatDown
+
+BattleCommand_AccuracyDown2:
+; accuracydown2
+ ld a, $10 | ACCURACY
+ jr BattleCommand_StatDown
+
+BattleCommand_EvasionDown2:
+; evasiondown2
+ ld a, $10 | EVASION
+
+BattleCommand_StatDown:
+; statdown
+
+ ld [wLoweredStat], a
+
+ call CheckMist
+ jp nz, .Mist
+
+ ld hl, wEnemyStatLevels
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .GetStatLevel
+ ld hl, wPlayerStatLevels
+
+.GetStatLevel:
+; Attempt to lower the stat.
+ ld a, [wLoweredStat]
+ and $f
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld b, [hl]
+ dec b
+ jp z, .CantLower
+
+; Sharply lower the stat if applicable.
+ ld a, [wLoweredStat]
+ and $f0
+ jr z, .ComputerMiss
+ dec b
+ jr nz, .ComputerMiss
+ inc b
+
+.ComputerMiss:
+; Computer opponents have a 25% chance of failing.
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .DidntMiss
+
+ ld a, [wLinkMode]
+ and a
+ jr nz, .DidntMiss
+
+; Lock-On still always works.
+ ld a, [wPlayerSubStatus5]
+ bit SUBSTATUS_LOCK_ON, a
+ jr nz, .DidntMiss
+
+; Attacking moves that also lower accuracy are unaffected.
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ cp EFFECT_ACCURACY_DOWN_HIT
+ jr z, .DidntMiss
+
+ call BattleRandom
+ cp 25 percent + 1 ; 25% chance AI fails
+ jr c, .Failed
+
+.DidntMiss:
+ call CheckSubstituteOpp
+ jr nz, .Failed
+
+ ld a, [wAttackMissed]
+ and a
+ jr nz, .Failed
+
+ ld a, [wEffectFailed]
+ and a
+ jr nz, .Failed
+
+ call CheckHiddenOpponent
+ jr nz, .Failed
+
+; Accuracy/Evasion reduction don't involve stats.
+ ld [hl], b
+ ld a, c
+ cp ACCURACY
+ jr nc, .Hit
+
+ push hl
+ ld hl, wEnemyMonAttack + 1
+ ld de, wEnemyStats
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .do_enemy
+ ld hl, wBattleMonAttack + 1
+ ld de, wPlayerStats
+.do_enemy
+ call TryLowerStat
+ pop hl
+ jr z, .CouldntLower
+
+.Hit:
+ xor a
+ ld [wFailedMessage], a
+ ret
+
+.CouldntLower:
+ inc [hl]
+.CantLower:
+ ld a, 3
+ ld [wFailedMessage], a
+ ld a, 1
+ ld [wAttackMissed], a
+ ret
+
+.Failed:
+ ld a, 1
+ ld [wFailedMessage], a
+ ld [wAttackMissed], a
+ ret
+
+.Mist:
+ ld a, 2
+ ld [wFailedMessage], a
+ ld a, 1
+ ld [wAttackMissed], a
+ ret
+
+CheckMist:
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ cp EFFECT_ATTACK_DOWN
+ jr c, .dont_check_mist
+ cp EFFECT_EVASION_DOWN + 1
+ jr c, .check_mist
+ cp EFFECT_ATTACK_DOWN_2
+ jr c, .dont_check_mist
+ cp EFFECT_EVASION_DOWN_2 + 1
+ jr c, .check_mist
+ cp EFFECT_ATTACK_DOWN_HIT
+ jr c, .dont_check_mist
+ cp EFFECT_EVASION_DOWN_HIT + 1
+ jr c, .check_mist
+.dont_check_mist
+ xor a
+ ret
+
+.check_mist
+ ld a, BATTLE_VARS_SUBSTATUS4_OPP
+ call GetBattleVar
+ bit SUBSTATUS_MIST, a
+ ret
+
+BattleCommand_StatUpMessage:
+ ld a, [wFailedMessage]
+ and a
+ ret nz
+ ld a, [wLoweredStat]
+ and $f
+ ld b, a
+ inc b
+ call GetStatName
+ ld hl, .stat
+ jp PrintText
+
+.stat
+ text_far Text_BattleEffectActivate
+ text_asm
+ ld hl, .BattleStatWentUpText
+ ld a, [wLoweredStat]
+ and $f0
+ ret z
+ ld hl, .BattleStatWentWayUpText
+ ret
+
+.BattleStatWentWayUpText:
+ text_far _BattleStatWentWayUpText
+ text_end
+
+.BattleStatWentUpText:
+ text_far _BattleStatWentUpText
+ text_end
+
+BattleCommand_StatDownMessage:
+ ld a, [wFailedMessage]
+ and a
+ ret nz
+ ld a, [wLoweredStat]
+ and $f
+ ld b, a
+ inc b
+ call GetStatName
+ ld hl, .stat
+ jp PrintText
+
+.stat
+ text_far Text_BattleFoeEffectActivate
+ text_asm
+ ld hl, .BattleStatFellText
+ ld a, [wLoweredStat]
+ and $f0
+ ret z
+ ld hl, .BattleStatSharplyFellText
+ ret
+
+.BattleStatSharplyFellText:
+ text_far _BattleStatSharplyFellText
+ text_end
+
+.BattleStatFellText:
+ text_far _BattleStatFellText
+ text_end
+
+TryLowerStat:
+; Lower stat c from stat struct hl (buffer de).
+
+ push bc
+ sla c
+ ld b, 0
+ add hl, bc
+ ; add de, c
+ ld a, c
+ add e
+ ld e, a
+ jr nc, .no_carry
+ inc d
+.no_carry
+ pop bc
+
+; The lowest possible stat is 1.
+ ld a, [hld]
+ sub 1
+ jr nz, .not_min
+ ld a, [hl]
+ and a
+ ret z
+
+.not_min
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .Player
+
+ call BattleCommand_SwitchTurn
+ call CalcPlayerStats
+ call BattleCommand_SwitchTurn
+ jr .end
+
+.Player:
+ call BattleCommand_SwitchTurn
+ call CalcEnemyStats
+ call BattleCommand_SwitchTurn
+.end
+ ld a, 1
+ and a
+ ret
+
+BattleCommand_StatUpFailText:
+; statupfailtext
+ ld a, [wFailedMessage]
+ and a
+ ret z
+ push af
+ call BattleCommand_MoveDelay
+ pop af
+ dec a
+ jp z, TryPrintButItFailed
+ ld a, [wLoweredStat]
+ and $f
+ ld b, a
+ inc b
+ call GetStatName
+ ld hl, WontRiseAnymoreText
+ jp StdBattleTextbox
+
+BattleCommand_StatDownFailText:
+; statdownfailtext
+ ld a, [wFailedMessage]
+ and a
+ ret z
+ push af
+ call BattleCommand_MoveDelay
+ pop af
+ dec a
+ jp z, TryPrintButItFailed
+ dec a
+ ld hl, ProtectedByMistText
+ jp z, StdBattleTextbox
+ ld a, [wLoweredStat]
+ and $f
+ ld b, a
+ inc b
+ call GetStatName
+ ld hl, WontDropAnymoreText
+ jp StdBattleTextbox
+
+GetStatName:
+ ld hl, StatNames
+ ld c, "@"
+.CheckName:
+ dec b
+ jr z, .Copy
+.GetName:
+ ld a, [hli]
+ cp c
+ jr z, .CheckName
+ jr .GetName
+
+.Copy:
+ ld de, wStringBuffer2
+ ld bc, 10
+ jp CopyBytes
+
+INCLUDE "data/battle/stat_names.asm"
+
+INCLUDE "data/battle/stat_multipliers.asm"
+
+BattleCommand_AllStatsUp:
+; allstatsup
+
+; Attack
+ call ResetMiss
+ call BattleCommand_AttackUp
+ call BattleCommand_StatUpMessage
+
+; Defense
+ call ResetMiss
+ call BattleCommand_DefenseUp
+ call BattleCommand_StatUpMessage
+
+; Speed
+ call ResetMiss
+ call BattleCommand_SpeedUp
+ call BattleCommand_StatUpMessage
+
+; Special Attack
+ call ResetMiss
+ call BattleCommand_SpecialAttackUp
+ call BattleCommand_StatUpMessage
+
+; Special Defense
+ call ResetMiss
+ call BattleCommand_SpecialDefenseUp
+ jp BattleCommand_StatUpMessage
+
+ResetMiss:
+ xor a
+ ld [wAttackMissed], a
+ ret
+
+LowerStat:
+ ld [wLoweredStat], a
+
+ ld hl, wPlayerStatLevels
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_target
+ ld hl, wEnemyStatLevels
+
+.got_target
+ ld a, [wLoweredStat]
+ and $f
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld b, [hl]
+ dec b
+ jr z, .cant_lower_anymore
+
+ ld a, [wLoweredStat]
+ and $f0
+ jr z, .got_num_stages
+ dec b
+ jr nz, .got_num_stages
+ inc b
+
+.got_num_stages
+ ld [hl], b
+ ld a, c
+ cp 5
+ jr nc, .accuracy_evasion
+
+ push hl
+ ld hl, wBattleMonStats + 1
+ ld de, wPlayerStats
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_target_2
+ ld hl, wEnemyMonStats + 1
+ ld de, wEnemyStats
+
+.got_target_2
+ call TryLowerStat
+ pop hl
+ jr z, .failed
+
+.accuracy_evasion
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .player
+
+ call CalcEnemyStats
+
+ jr .finish
+
+.player
+ call CalcPlayerStats
+
+.finish
+ xor a
+ ld [wFailedMessage], a
+ ret
+
+.failed
+ inc [hl]
+
+.cant_lower_anymore
+ ld a, 2
+ ld [wFailedMessage], a
+ ret
+
+BattleCommand_TriStatusChance:
+; tristatuschance
+
+ call BattleCommand_EffectChance
+
+.loop
+ ; 1/3 chance of each status
+ call BattleRandom
+ swap a
+ and %11
+ jr z, .loop
+ dec a
+ ld hl, .ptrs
+ rst JumpTable
+ ret
+
+.ptrs
+ dw BattleCommand_ParalyzeTarget ; paralyze
+ dw BattleCommand_FreezeTarget ; freeze
+ dw BattleCommand_BurnTarget ; burn
+
+BattleCommand_Curl:
+; curl
+ ld a, BATTLE_VARS_SUBSTATUS2
+ call GetBattleVarAddr
+ set SUBSTATUS_CURLED, [hl]
+ ret
+
+BattleCommand_RaiseSubNoAnim:
+ ld hl, GetBattleMonBackpic
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .PlayerTurn
+ ld hl, GetEnemyMonFrontpic
+.PlayerTurn:
+ xor a
+ ldh [hBGMapMode], a
+ call CallBattleCore
+ jp WaitBGMap
+
+BattleCommand_LowerSubNoAnim:
+ ld hl, DropPlayerSub
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .PlayerTurn
+ ld hl, DropEnemySub
+.PlayerTurn:
+ xor a
+ ldh [hBGMapMode], a
+ call CallBattleCore
+ jp WaitBGMap
+
+CalcPlayerStats:
+ ld hl, wPlayerAtkLevel
+ ld de, wPlayerStats
+ ld bc, wBattleMonAttack
+
+ ld a, 5
+ call CalcBattleStats
+
+ ld hl, BadgeStatBoosts
+ call CallBattleCore
+
+ call BattleCommand_SwitchTurn
+
+ ld hl, ApplyPrzEffectOnSpeed
+ call CallBattleCore
+
+ ld hl, ApplyBrnEffectOnAttack
+ call CallBattleCore
+
+ jp BattleCommand_SwitchTurn
+
+CalcEnemyStats:
+ ld hl, wEnemyAtkLevel
+ ld de, wEnemyStats
+ ld bc, wEnemyMonAttack
+
+ ld a, 5
+ call CalcBattleStats
+
+ call BattleCommand_SwitchTurn
+
+ ld hl, ApplyPrzEffectOnSpeed
+ call CallBattleCore
+
+ ld hl, ApplyBrnEffectOnAttack
+ call CallBattleCore
+
+ jp BattleCommand_SwitchTurn
+
+CalcBattleStats:
+.loop
+ push af
+ ld a, [hli]
+ push hl
+ push bc
+
+ ld c, a
+ dec c
+ ld b, 0
+ ld hl, StatLevelMultipliers
+ add hl, bc
+ add hl, bc
+
+ xor a
+ ldh [hMultiplicand + 0], a
+ ld a, [de]
+ ldh [hMultiplicand + 1], a
+ inc de
+ ld a, [de]
+ ldh [hMultiplicand + 2], a
+ inc de
+
+ ld a, [hli]
+ ldh [hMultiplier], a
+ call Multiply
+
+ ld a, [hl]
+ ldh [hDivisor], a
+ ld b, 4
+ call Divide
+
+ ldh a, [hQuotient + 2]
+ ld b, a
+ ldh a, [hQuotient + 3]
+ or b
+ jr nz, .check_maxed_out
+
+ ld a, 1
+ ldh [hQuotient + 3], a
+ jr .not_maxed_out
+
+.check_maxed_out
+ ldh a, [hQuotient + 3]
+ cp LOW(MAX_STAT_VALUE)
+ ld a, b
+ sbc HIGH(MAX_STAT_VALUE)
+ jr c, .not_maxed_out
+
+ ld a, LOW(MAX_STAT_VALUE)
+ ldh [hQuotient + 3], a
+ ld a, HIGH(MAX_STAT_VALUE)
+ ldh [hQuotient + 2], a
+
+.not_maxed_out
+ pop bc
+ ldh a, [hQuotient + 2]
+ ld [bc], a
+ inc bc
+ ldh a, [hQuotient + 3]
+ ld [bc], a
+ inc bc
+ pop hl
+ pop af
+ dec a
+ jr nz, .loop
+
+ ret
+
+INCLUDE "engine/battle/move_effects/bide.asm"
+
+BattleCommand_CheckRampage:
+; checkrampage
+
+ ld de, wPlayerRolloutCount
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .player
+ ld de, wEnemyRolloutCount
+.player
+ ld a, BATTLE_VARS_SUBSTATUS3
+ call GetBattleVarAddr
+ bit SUBSTATUS_RAMPAGE, [hl]
+ ret z
+ ld a, [de]
+ dec a
+ ld [de], a
+ jr nz, .continue_rampage
+
+ res SUBSTATUS_RAMPAGE, [hl]
+ call BattleCommand_SwitchTurn
+ call SafeCheckSafeguard
+ push af
+ call BattleCommand_SwitchTurn
+ pop af
+ jr nz, .continue_rampage
+
+ set SUBSTATUS_CONFUSED, [hl]
+ call BattleRandom
+ and %00000001
+ inc a
+ inc a
+ inc de ; ConfuseCount
+ ld [de], a
+.continue_rampage
+ ld b, rampage_command
+ jp SkipToBattleCommand
+
+BattleCommand_Rampage:
+; rampage
+
+; No rampage during Sleep Talk.
+ ld a, BATTLE_VARS_STATUS
+ call GetBattleVar
+ and SLP
+ ret nz
+
+ ld de, wPlayerRolloutCount
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .ok
+ ld de, wEnemyRolloutCount
+.ok
+ ld a, BATTLE_VARS_SUBSTATUS3
+ call GetBattleVarAddr
+ set SUBSTATUS_RAMPAGE, [hl]
+; Rampage for 1 or 2 more turns
+ call BattleRandom
+ and %00000001
+ inc a
+ ld [de], a
+ ld a, 1
+ ld [wSomeoneIsRampaging], a
+ ret
+
+INCLUDE "engine/battle/move_effects/teleport.asm"
+
+BattleCommand_ForceSwitch:
+; forceswitch
+
+ ld a, [wBattleType]
+ cp BATTLETYPE_SHINY
+ jp z, .fail
+ cp BATTLETYPE_TRAP
+ jp z, .fail
+ ldh a, [hBattleTurn]
+ and a
+ jp nz, .force_player_switch
+ ld a, [wAttackMissed]
+ and a
+ jr nz, .missed
+ ld a, [wBattleMode]
+ dec a
+ jr nz, .trainer
+ ld a, [wCurPartyLevel]
+ ld b, a
+ ld a, [wBattleMonLevel]
+ cp b
+ jr nc, .wild_force_flee
+ add b
+ ld c, a
+ inc c
+.random_loop_wild
+ call BattleRandom
+ cp c
+ jr nc, .random_loop_wild
+ srl b
+ srl b
+ cp b
+ jr nc, .wild_force_flee
+.missed
+ jp .fail
+
+.wild_force_flee
+ call UpdateBattleMonInParty
+ xor a
+ ld [wNumHits], a
+ inc a
+ ld [wForcedSwitch], a
+ ; set battle draw
+ inc a
+ ld [wBattleResult], a
+ ld a, [wPlayerMoveStructAnimation]
+ jp .succeed
+
+.trainer
+ call FindAliveEnemyMons
+ jr c, .switch_fail
+ ld a, [wEnemyGoesFirst]
+ and a
+ jr z, .switch_fail
+ call UpdateEnemyMonInParty
+ ld a, $1
+ ld [wKickCounter], a
+ call AnimateCurrentMove
+ ld c, $14
+ call DelayFrames
+ hlcoord 1, 0
+ lb bc, 4, 10
+ call ClearBox
+ ld c, 20
+ call DelayFrames
+ ld a, [wOTPartyCount]
+ ld b, a
+ ld a, [wCurOTMon]
+ ld c, a
+; select a random enemy mon to switch to
+.random_loop_trainer
+ call BattleRandom
+ and $7
+ cp b
+ jr nc, .random_loop_trainer
+ cp c
+ jr z, .random_loop_trainer
+ push af
+ push bc
+ ld hl, wOTPartyMon1HP
+ call GetPartyLocation
+ ld a, [hli]
+ or [hl]
+ pop bc
+ pop de
+ jr z, .random_loop_trainer
+ ld a, d
+ inc a
+ ld [wEnemySwitchMonIndex], a
+ callfar ForceEnemySwitch
+
+ ld hl, DraggedOutText
+ call StdBattleTextbox
+
+ ld hl, SpikesDamage
+ jp CallBattleCore
+
+.switch_fail
+ jp .fail
+
+.force_player_switch
+ ld a, [wAttackMissed]
+ and a
+ jr nz, .player_miss
+
+ ld a, [wBattleMode]
+ dec a
+ jr nz, .vs_trainer
+
+ ld a, [wBattleMonLevel]
+ ld b, a
+ ld a, [wCurPartyLevel]
+ cp b
+ jr nc, .wild_succeed_playeristarget
+
+ add b
+ ld c, a
+ inc c
+.wild_random_loop_playeristarget
+ call BattleRandom
+ cp c
+ jr nc, .wild_random_loop_playeristarget
+
+ srl b
+ srl b
+ cp b
+ jr nc, .wild_succeed_playeristarget
+
+.player_miss
+ jp .fail
+
+.wild_succeed_playeristarget
+ call UpdateBattleMonInParty
+ xor a
+ ld [wNumHits], a
+ inc a
+ ld [wForcedSwitch], a
+ ; set battle draw
+ inc a
+ ld [wBattleResult], a
+ ld a, [wEnemyMoveStructAnimation]
+ jr .succeed
+
+.vs_trainer
+ call CheckPlayerHasMonToSwitchTo
+ jr c, .switch_fail2
+
+ ld a, [wEnemyGoesFirst]
+ cp $1
+ jr z, .switch_fail
+
+ call UpdateBattleMonInParty
+ ld a, $1
+ ld [wKickCounter], a
+ call AnimateCurrentMove
+ ld c, 20
+ call DelayFrames
+ hlcoord 9, 7
+ lb bc, 5, 11
+ call ClearBox
+ ld c, 20
+ call DelayFrames
+ ld a, [wPartyCount]
+ ld b, a
+ ld a, [wCurBattleMon]
+ ld c, a
+.random_loop_trainer_playeristarget
+ call BattleRandom
+ and $7
+ cp b
+ jr nc, .random_loop_trainer_playeristarget
+
+ cp c
+ jr z, .random_loop_trainer_playeristarget
+
+ push af
+ push bc
+ ld hl, wPartyMon1HP
+ call GetPartyLocation
+ ld a, [hli]
+ or [hl]
+ pop bc
+ pop de
+ jr z, .random_loop_trainer_playeristarget
+
+ ld a, d
+ ld [wCurPartyMon], a
+ ld hl, SwitchPlayerMon
+ call CallBattleCore
+
+ ld hl, DraggedOutText
+ call StdBattleTextbox
+
+ ld hl, SpikesDamage
+ jp CallBattleCore
+
+.switch_fail2
+ jr .fail
+
+.succeed
+ push af
+ ld a, DRAW
+ ld [wBattleResult], a
+ ld a, $1
+ ld [wKickCounter], a
+ call AnimateCurrentMove
+ ld c, 20
+ call DelayFrames
+ pop af
+
+ ld hl, FledInFearText
+ cp ROAR
+ jr z, .do_text
+ ld hl, BlownAwayText
+.do_text
+ jp StdBattleTextbox
+
+.fail
+ call BattleCommand_LowerSub
+ call BattleCommand_MoveDelay
+ call BattleCommand_RaiseSub
+ jp PrintButItFailed
+
+CheckPlayerHasMonToSwitchTo:
+ ld a, [wPartyCount]
+ ld d, a
+ ld e, 0
+ ld bc, PARTYMON_STRUCT_LENGTH
+.loop
+ ld a, [wCurBattleMon]
+ cp e
+ jr z, .next
+
+ ld a, e
+ ld hl, wPartyMon1HP
+ call AddNTimes
+ ld a, [hli]
+ or [hl]
+ jr nz, .not_fainted
+
+.next
+ inc e
+ dec d
+ jr nz, .loop
+
+ scf
+ ret
+
+.not_fainted
+ and a
+ ret
+
+BattleCommand_EndLoop:
+; endloop
+
+; Loop back to 'critical'.
+
+ ld de, wPlayerRolloutCount
+ ld bc, wPlayerDamageTaken
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_addrs
+ ld de, wEnemyRolloutCount
+ ld bc, wEnemyDamageTaken
+.got_addrs
+
+ ld a, BATTLE_VARS_SUBSTATUS3
+ call GetBattleVarAddr
+ bit SUBSTATUS_IN_LOOP, [hl]
+ jp nz, .in_loop
+ set SUBSTATUS_IN_LOOP, [hl]
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVarAddr
+ ld a, [hl]
+ cp EFFECT_POISON_MULTI_HIT
+ jr z, .twineedle
+ cp EFFECT_DOUBLE_HIT
+ ld a, 1
+ jr z, .double_hit
+ ld a, [hl]
+ cp EFFECT_BEAT_UP
+ jr z, .beat_up
+ cp EFFECT_TRIPLE_KICK
+ jr nz, .not_triple_kick
+.reject_triple_kick_sample
+ call BattleRandom
+ and $3
+ jr z, .reject_triple_kick_sample
+ dec a
+ jr nz, .double_hit
+ ld a, 1
+ ld [bc], a
+ jr .done_loop
+
+.beat_up
+ ldh a, [hBattleTurn]
+ and a
+ jr nz, .check_ot_beat_up
+ ld a, [wPartyCount]
+ cp 1
+ jp z, .only_one_beatup
+ dec a
+ jr .double_hit
+
+.check_ot_beat_up
+ ld a, [wBattleMode]
+ cp WILD_BATTLE
+ jp z, .only_one_beatup
+ ld a, [wOTPartyCount]
+ cp 1
+ jp z, .only_one_beatup
+ dec a
+ jr .double_hit
+
+.only_one_beatup
+ ld a, BATTLE_VARS_SUBSTATUS3
+ call GetBattleVarAddr
+ res SUBSTATUS_IN_LOOP, [hl]
+ call BattleCommand_BeatUpFailText
+ jp EndMoveEffect
+
+.not_triple_kick
+ call BattleRandom
+ and $3
+ cp 2
+ jr c, .got_number_hits
+ call BattleRandom
+ and $3
+.got_number_hits
+ inc a
+.double_hit
+ ld [de], a
+ inc a
+ ld [bc], a
+ jr .loop_back_to_critical
+
+.twineedle
+ ld a, 1
+ jr .double_hit
+
+.in_loop
+ ld a, [de]
+ dec a
+ ld [de], a
+ jr nz, .loop_back_to_critical
+.done_loop
+ ld a, BATTLE_VARS_SUBSTATUS3
+ call GetBattleVarAddr
+ res SUBSTATUS_IN_LOOP, [hl]
+
+ ld hl, PlayerHitTimesText
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_hit_n_times_text
+ ld hl, EnemyHitTimesText
+.got_hit_n_times_text
+
+ push bc
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ cp EFFECT_BEAT_UP
+ jr z, .beat_up_2
+ call StdBattleTextbox
+.beat_up_2
+
+ pop bc
+ xor a
+ ld [bc], a
+ ret
+
+.loop_back_to_critical
+ ld a, [wBattleScriptBufferAddress + 1]
+ ld h, a
+ ld a, [wBattleScriptBufferAddress]
+ ld l, a
+.not_critical
+ ld a, [hld]
+ cp critical_command
+ jr nz, .not_critical
+ inc hl
+ ld a, h
+ ld [wBattleScriptBufferAddress + 1], a
+ ld a, l
+ ld [wBattleScriptBufferAddress], a
+ ret
+
+BattleCommand_FakeOut:
+ ld a, [wAttackMissed]
+ and a
+ ret nz
+
+ call CheckSubstituteOpp
+ jr nz, .fail
+
+ ld a, BATTLE_VARS_STATUS_OPP
+ call GetBattleVar
+ and 1 << FRZ | SLP
+ jr nz, .fail
+
+ call CheckOpponentWentFirst
+ jr z, FlinchTarget
+
+.fail
+ ld a, 1
+ ld [wAttackMissed], a
+ ret
+
+BattleCommand_FlinchTarget:
+ call CheckSubstituteOpp
+ ret nz
+
+ ld a, BATTLE_VARS_STATUS_OPP
+ call GetBattleVar
+ and 1 << FRZ | SLP
+ ret nz
+
+ call CheckOpponentWentFirst
+ ret nz
+
+ ld a, [wEffectFailed]
+ and a
+ ret nz
+
+ ; fallthrough
+
+FlinchTarget:
+ ld a, BATTLE_VARS_SUBSTATUS3_OPP
+ call GetBattleVarAddr
+ set SUBSTATUS_FLINCHED, [hl]
+ jp EndRechargeOpp
+
+CheckOpponentWentFirst:
+; Returns a=0, z if user went first
+; Returns a=1, nz if opponent went first
+ push bc
+ ld a, [wEnemyGoesFirst] ; 0 if player went first
+ ld b, a
+ ldh a, [hBattleTurn] ; 0 if it's the player's turn
+ xor b ; 1 if opponent went first
+ pop bc
+ ret
+
+BattleCommand_HeldFlinch:
+; kingsrock
+
+ ld a, [wAttackMissed]
+ and a
+ ret nz
+
+ call GetUserItem
+ ld a, b
+ cp HELD_FLINCH
+ ret nz
+
+ call CheckSubstituteOpp
+ ret nz
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVarAddr
+ ld d, h
+ ld e, l
+ call GetUserItem
+ call BattleRandom
+ cp c
+ ret nc
+ call EndRechargeOpp
+ ld a, BATTLE_VARS_SUBSTATUS3_OPP
+ call GetBattleVarAddr
+ set SUBSTATUS_FLINCHED, [hl]
+ ret
+
+BattleCommand_OHKO:
+; ohko
+
+ call ResetDamage
+ ld a, [wTypeModifier]
+ and $7f
+ jr z, .no_effect
+ ld hl, wEnemyMonLevel
+ ld de, wBattleMonLevel
+ ld bc, wPlayerMoveStruct + MOVE_ACC
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_move_accuracy
+ push hl
+ ld h, d
+ ld l, e
+ pop de
+ ld bc, wEnemyMoveStruct + MOVE_ACC
+.got_move_accuracy
+ ld a, [de]
+ sub [hl]
+ jr c, .no_effect
+ add a
+ ld e, a
+ ld a, [bc]
+ add e
+ jr nc, .finish_ohko
+ ld a, $ff
+.finish_ohko
+ ld [bc], a
+ call BattleCommand_CheckHit
+ ld hl, wCurDamage
+ ld a, $ff
+ ld [hli], a
+ ld [hl], a
+ ld a, $2
+ ld [wCriticalHit], a
+ ret
+
+.no_effect
+ ld a, $ff
+ ld [wCriticalHit], a
+ ld a, $1
+ ld [wAttackMissed], a
+ ret
+
+BattleCommand_CheckCharge:
+; checkcharge
+
+ ld a, BATTLE_VARS_SUBSTATUS3
+ call GetBattleVarAddr
+ bit SUBSTATUS_CHARGED, [hl]
+ ret z
+ res SUBSTATUS_CHARGED, [hl]
+ res SUBSTATUS_UNDERGROUND, [hl]
+ res SUBSTATUS_FLYING, [hl]
+ ld b, charge_command
+ jp SkipToBattleCommand
+
+BattleCommand_Charge:
+; charge
+
+ call BattleCommand_ClearText
+ ld a, BATTLE_VARS_STATUS
+ call GetBattleVar
+ and SLP
+ jr z, .awake
+
+ call BattleCommand_MoveDelay
+ call BattleCommand_RaiseSub
+ call PrintButItFailed
+ jp EndMoveEffect
+
+.awake
+ ld a, BATTLE_VARS_SUBSTATUS3
+ call GetBattleVarAddr
+ set SUBSTATUS_CHARGED, [hl]
+
+ ld hl, IgnoredOrders2Text
+ ld a, [wAlreadyDisobeyed]
+ and a
+ call nz, StdBattleTextbox
+
+ call BattleCommand_LowerSub
+ xor a
+ ld [wNumHits], a
+ inc a
+ ld [wKickCounter], a
+ call LoadMoveAnim
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+ cp FLY
+ jr z, .flying
+ cp DIG
+ jr z, .flying
+ call BattleCommand_RaiseSub
+ jr .not_flying
+
+.flying
+ call DisappearUser
+.not_flying
+ ld a, BATTLE_VARS_SUBSTATUS3
+ call GetBattleVarAddr
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+ ld b, a
+ cp FLY
+ jr z, .set_flying
+ cp DIG
+ jr nz, .dont_set_digging
+ set SUBSTATUS_UNDERGROUND, [hl]
+ jr .dont_set_digging
+
+.set_flying
+ set SUBSTATUS_FLYING, [hl]
+
+.dont_set_digging
+ call CheckUserIsCharging
+ jr nz, .mimic
+ ld a, BATTLE_VARS_LAST_COUNTER_MOVE
+ call GetBattleVarAddr
+ ld [hl], b
+ ld a, BATTLE_VARS_LAST_MOVE
+ call GetBattleVarAddr
+ ld [hl], b
+
+.mimic
+ call ResetDamage
+
+ ld hl, .UsedText
+ call PrintText
+
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ cp EFFECT_SKULL_BASH
+ ld b, endturn_command
+ jp z, SkipToBattleCommand
+ jp EndMoveEffect
+
+.UsedText:
+ text_far Text_BattleUser ; "<USER>"
+ text_asm
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+ cp RAZOR_WIND
+ ld hl, .BattleMadeWhirlwindText
+ jr z, .done
+
+ cp SOLARBEAM
+ ld hl, .BattleTookSunlightText
+ jr z, .done
+
+ cp SKULL_BASH
+ ld hl, .BattleLoweredHeadText
+ jr z, .done
+
+ cp SKY_ATTACK
+ ld hl, .BattleGlowingText
+ jr z, .done
+
+ cp FLY
+ ld hl, .BattleFlewText
+ jr z, .done
+
+ cp DIG
+ ld hl, .BattleDugText
+
+.done
+ ret
+
+.BattleMadeWhirlwindText:
+ text_far _BattleMadeWhirlwindText
+ text_end
+
+.BattleTookSunlightText:
+ text_far _BattleTookSunlightText
+ text_end
+
+.BattleLoweredHeadText:
+ text_far _BattleLoweredHeadText
+ text_end
+
+.BattleGlowingText:
+ text_far _BattleGlowingText
+ text_end
+
+.BattleFlewText:
+ text_far _BattleFlewText
+ text_end
+
+.BattleDugText:
+ text_far _BattleDugText
+ text_end
+
+BattleCommand3c:
+; unused
+ ret
+
+BattleCommand_TrapTarget:
+; traptarget
+
+ ld a, [wAttackMissed]
+ and a
+ ret nz
+ ld hl, wEnemyWrapCount
+ ld de, wEnemyTrappingMove
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_trap
+ ld hl, wPlayerWrapCount
+ ld de, wPlayerTrappingMove
+
+.got_trap
+ ld a, [hl]
+ and a
+ ret nz
+ ld a, BATTLE_VARS_SUBSTATUS4_OPP
+ call GetBattleVar
+ bit SUBSTATUS_SUBSTITUTE, a
+ ret nz
+ call BattleRandom
+ ; trapped for 2-5 turns
+ and %11
+ inc a
+ inc a
+ inc a
+ ld [hl], a
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+ ld [de], a
+ ld b, a
+ ld hl, .Traps
+
+.find_trap_text
+ ld a, [hli]
+ cp b
+ jr z, .found_trap_text
+ inc hl
+ inc hl
+ jr .find_trap_text
+
+.found_trap_text
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ jp StdBattleTextbox
+
+.Traps:
+ dbw BIND, UsedBindText ; 'used BIND on'
+ dbw WRAP, WrappedByText ; 'was WRAPPED by'
+ dbw FIRE_SPIN, WasTrappedText ; 'was trapped!'
+ dbw CLAMP, ClampedByText ; 'was CLAMPED by'
+ dbw WHIRLPOOL, WasTrappedText ; 'was trapped!'
+
+INCLUDE "engine/battle/move_effects/mist.asm"
+
+INCLUDE "engine/battle/move_effects/focus_energy.asm"
+
+BattleCommand_Recoil:
+; recoil
+
+ ld hl, wBattleMonMaxHP
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_hp
+ ld hl, wEnemyMonMaxHP
+.got_hp
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+ ld d, a
+; get 1/4 damage or 1 HP, whichever is higher
+ ld a, [wCurDamage]
+ ld b, a
+ ld a, [wCurDamage + 1]
+ ld c, a
+ srl b
+ rr c
+ srl b
+ rr c
+ ld a, b
+ or c
+ jr nz, .min_damage
+ inc c
+.min_damage
+ ld a, [hli]
+ ld [wBuffer2], a
+ ld a, [hl]
+ ld [wBuffer1], a
+ dec hl
+ dec hl
+ ld a, [hl]
+ ld [wBuffer3], a
+ sub c
+ ld [hld], a
+ ld [wBuffer5], a
+ ld a, [hl]
+ ld [wBuffer4], a
+ sbc b
+ ld [hl], a
+ ld [wBuffer6], a
+ jr nc, .dont_ko
+ xor a
+ ld [hli], a
+ ld [hl], a
+ ld hl, wBuffer5
+ ld [hli], a
+ ld [hl], a
+.dont_ko
+ hlcoord 10, 9
+ ldh a, [hBattleTurn]
+ and a
+ ld a, 1
+ jr z, .animate_hp_bar
+ hlcoord 2, 2
+ xor a
+.animate_hp_bar
+ ld [wWhichHPBar], a
+ predef AnimateHPBar
+ call RefreshBattleHuds
+ ld hl, RecoilText
+ jp StdBattleTextbox
+
+BattleCommand_ConfuseTarget:
+; confusetarget
+
+ call GetOpponentItem
+ ld a, b
+ cp HELD_PREVENT_CONFUSE
+ ret z
+ ld a, [wEffectFailed]
+ and a
+ ret nz
+ call SafeCheckSafeguard
+ ret nz
+ call CheckSubstituteOpp
+ ret nz
+ ld a, BATTLE_VARS_SUBSTATUS3_OPP
+ call GetBattleVarAddr
+ bit SUBSTATUS_CONFUSED, [hl]
+ ret nz
+ jr BattleCommand_FinishConfusingTarget
+
+BattleCommand_Confuse:
+; confuse
+
+ call GetOpponentItem
+ ld a, b
+ cp HELD_PREVENT_CONFUSE
+ jr nz, .no_item_protection
+ ld a, [hl]
+ ld [wNamedObjectIndexBuffer], a
+ call GetItemName
+ call AnimateFailedMove
+ ld hl, ProtectedByText
+ jp StdBattleTextbox
+
+.no_item_protection
+ ld a, BATTLE_VARS_SUBSTATUS3_OPP
+ call GetBattleVarAddr
+ bit SUBSTATUS_CONFUSED, [hl]
+ jr z, .not_already_confused
+ call AnimateFailedMove
+ ld hl, AlreadyConfusedText
+ jp StdBattleTextbox
+
+.not_already_confused
+ call CheckSubstituteOpp
+ jr nz, BattleCommand_Confuse_CheckSnore_Swagger_ConfuseHit
+ ld a, [wAttackMissed]
+ and a
+ jr nz, BattleCommand_Confuse_CheckSnore_Swagger_ConfuseHit
+BattleCommand_FinishConfusingTarget:
+ ld bc, wEnemyConfuseCount
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_confuse_count
+ ld bc, wPlayerConfuseCount
+
+.got_confuse_count
+ set SUBSTATUS_CONFUSED, [hl]
+ ; confused for 2-5 turns
+ call BattleRandom
+ and %11
+ inc a
+ inc a
+ ld [bc], a
+
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ cp EFFECT_CONFUSE_HIT
+ jr z, .got_effect
+ cp EFFECT_SNORE
+ jr z, .got_effect
+ cp EFFECT_SWAGGER
+ jr z, .got_effect
+ call AnimateCurrentMove
+
+.got_effect
+ ld de, ANIM_CONFUSED
+ call PlayOpponentBattleAnim
+
+ ld hl, BecameConfusedText
+ call StdBattleTextbox
+
+ call GetOpponentItem
+ ld a, b
+ cp HELD_HEAL_STATUS
+ jr z, .heal_confusion
+ cp HELD_HEAL_CONFUSION
+ ret nz
+.heal_confusion
+ ld hl, UseConfusionHealingItem
+ jp CallBattleCore
+
+BattleCommand_Confuse_CheckSnore_Swagger_ConfuseHit:
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ cp EFFECT_CONFUSE_HIT
+ ret z
+ cp EFFECT_SNORE
+ ret z
+ cp EFFECT_SWAGGER
+ ret z
+ jp PrintDidntAffect2
+
+BattleCommand_Paralyze:
+; paralyze
+
+ ld a, BATTLE_VARS_STATUS_OPP
+ call GetBattleVar
+ bit PAR, a
+ jr nz, .paralyzed
+ ld a, [wTypeModifier]
+ and $7f
+ jr z, .didnt_affect
+ call GetOpponentItem
+ ld a, b
+ cp HELD_PREVENT_PARALYZE
+ jr nz, .no_item_protection
+ ld a, [hl]
+ ld [wNamedObjectIndexBuffer], a
+ call GetItemName
+ call AnimateFailedMove
+ ld hl, ProtectedByText
+ jp StdBattleTextbox
+
+.no_item_protection
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .dont_sample_failure
+
+ ld a, [wLinkMode]
+ and a
+ jr nz, .dont_sample_failure
+
+ ld a, [wPlayerSubStatus5]
+ bit SUBSTATUS_LOCK_ON, a
+ jr nz, .dont_sample_failure
+
+ call BattleRandom
+ cp 25 percent + 1 ; 25% chance AI fails
+ jr c, .failed
+
+.dont_sample_failure
+ ld a, BATTLE_VARS_STATUS_OPP
+ call GetBattleVarAddr
+ and a
+ jr nz, .failed
+ ld a, [wAttackMissed]
+ and a
+ jr nz, .failed
+ call CheckSubstituteOpp
+ jr nz, .failed
+ ld c, 30
+ call DelayFrames
+ call AnimateCurrentMove
+ ld a, $1
+ ldh [hBGMapMode], a
+ ld a, BATTLE_VARS_STATUS_OPP
+ call GetBattleVarAddr
+ set PAR, [hl]
+ call UpdateOpponentInParty
+ ld hl, ApplyPrzEffectOnSpeed
+ call CallBattleCore
+ call UpdateBattleHuds
+ call PrintParalyze
+ ld hl, UseHeldStatusHealingItem
+ jp CallBattleCore
+
+.paralyzed
+ call AnimateFailedMove
+ ld hl, AlreadyParalyzedText
+ jp StdBattleTextbox
+
+.failed
+ jp PrintDidntAffect2
+
+.didnt_affect
+ call AnimateFailedMove
+ jp PrintDoesntAffect
+
+CheckMoveTypeMatchesTarget:
+; Compare move type to opponent type.
+; Return z if matching the opponent type,
+; unless the move is Normal (Tri Attack).
+
+ push hl
+
+ ld hl, wEnemyMonType1
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .ok
+ ld hl, wBattleMonType1
+.ok
+
+ ld a, BATTLE_VARS_MOVE_TYPE
+ call GetBattleVar
+ cp NORMAL
+ jr z, .normal
+
+ cp [hl]
+ jr z, .return
+
+ inc hl
+ cp [hl]
+
+.return
+ pop hl
+ ret
+
+.normal
+ ld a, 1
+ and a
+ pop hl
+ ret
+
+INCLUDE "engine/battle/move_effects/substitute.asm"
+
+BattleCommand_RechargeNextTurn:
+; rechargenextturn
+ ld a, BATTLE_VARS_SUBSTATUS4
+ call GetBattleVarAddr
+ set SUBSTATUS_RECHARGE, [hl]
+ ret
+
+EndRechargeOpp:
+ push hl
+ ld a, BATTLE_VARS_SUBSTATUS4_OPP
+ call GetBattleVarAddr
+ res SUBSTATUS_RECHARGE, [hl]
+ pop hl
+ ret
+
+INCLUDE "engine/battle/move_effects/rage.asm"
+
+BattleCommand_DoubleFlyingDamage:
+; doubleflyingdamage
+ ld a, BATTLE_VARS_SUBSTATUS3_OPP
+ call GetBattleVar
+ bit SUBSTATUS_FLYING, a
+ ret z
+ jr DoubleDamage
+
+BattleCommand_DoubleUndergroundDamage:
+; doubleundergrounddamage
+ ld a, BATTLE_VARS_SUBSTATUS3_OPP
+ call GetBattleVar
+ bit SUBSTATUS_UNDERGROUND, a
+ ret z
+
+ ; fallthrough
+
+DoubleDamage:
+ ld hl, wCurDamage + 1
+ sla [hl]
+ dec hl
+ rl [hl]
+ jr nc, .quit
+
+ ld a, $ff
+ ld [hli], a
+ ld [hl], a
+.quit
+ ret
+
+INCLUDE "engine/battle/move_effects/mimic.asm"
+
+INCLUDE "engine/battle/move_effects/leech_seed.asm"
+
+INCLUDE "engine/battle/move_effects/splash.asm"
+
+INCLUDE "engine/battle/move_effects/disable.asm"
+
+INCLUDE "engine/battle/move_effects/pay_day.asm"
+
+INCLUDE "engine/battle/move_effects/conversion.asm"
+
+BattleCommand_ResetStats:
+; resetstats
+
+ ld a, 7 ; neutral
+ ld hl, wPlayerStatLevels
+ call .Fill
+ ld hl, wEnemyStatLevels
+ call .Fill
+
+ ldh a, [hBattleTurn]
+ push af
+
+ call SetPlayerTurn
+ call CalcPlayerStats
+ call SetEnemyTurn
+ call CalcEnemyStats
+
+ pop af
+ ldh [hBattleTurn], a
+
+ call AnimateCurrentMove
+
+ ld hl, EliminatedStatsText
+ jp StdBattleTextbox
+
+.Fill:
+ ld b, wPlayerStatLevelsEnd - wPlayerStatLevels
+.next
+ ld [hli], a
+ dec b
+ jr nz, .next
+ ret
+
+BattleCommand_Heal:
+; heal
+
+ ld de, wBattleMonHP
+ ld hl, wBattleMonMaxHP
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_hp
+ ld de, wEnemyMonHP
+ ld hl, wEnemyMonMaxHP
+.got_hp
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+ ld b, a
+ push hl
+ push de
+ push bc
+ ld c, 2
+ call CompareBytes
+ pop bc
+ pop de
+ pop hl
+ jp z, .hp_full
+ ld a, b
+ cp REST
+ jr nz, .not_rest
+
+ push hl
+ push de
+ push af
+ call BattleCommand_MoveDelay
+ ld a, BATTLE_VARS_SUBSTATUS5
+ call GetBattleVarAddr
+ res SUBSTATUS_TOXIC, [hl]
+ ld a, BATTLE_VARS_STATUS
+ call GetBattleVarAddr
+ ld a, [hl]
+ and a
+ ld [hl], REST_SLEEP_TURNS + 1
+ ld hl, WentToSleepText
+ jr z, .no_status_to_heal
+ ld hl, RestedText
+.no_status_to_heal
+ call StdBattleTextbox
+ ldh a, [hBattleTurn]
+ and a
+ jr nz, .calc_enemy_stats
+ call CalcPlayerStats
+ jr .got_stats
+
+.calc_enemy_stats
+ call CalcEnemyStats
+.got_stats
+ pop af
+ pop de
+ pop hl
+
+.not_rest
+ jr z, .restore_full_hp
+ ld hl, GetHalfMaxHP
+ call CallBattleCore
+ jr .finish
+
+.restore_full_hp
+ ld hl, GetMaxHP
+ call CallBattleCore
+.finish
+ call AnimateCurrentMove
+ call BattleCommand_SwitchTurn
+ ld hl, RestoreHP
+ call CallBattleCore
+ call BattleCommand_SwitchTurn
+ call UpdateUserInParty
+ call RefreshBattleHuds
+ ld hl, RegainedHealthText
+ jp StdBattleTextbox
+
+.hp_full
+ call AnimateFailedMove
+ ld hl, HPIsFullText
+ jp StdBattleTextbox
+
+INCLUDE "engine/battle/move_effects/transform.asm"
+
+BattleEffect_ButItFailed:
+ call AnimateFailedMove
+ jp PrintButItFailed
+
+ClearLastMove:
+ ld a, BATTLE_VARS_LAST_COUNTER_MOVE
+ call GetBattleVarAddr
+ xor a
+ ld [hl], a
+
+ ld a, BATTLE_VARS_LAST_MOVE
+ call GetBattleVarAddr
+ xor a
+ ld [hl], a
+ ret
+
+ResetActorDisable:
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .player
+
+ xor a
+ ld [wEnemyDisableCount], a
+ ld [wEnemyDisabledMove], a
+ ret
+
+.player
+ xor a
+ ld [wPlayerDisableCount], a
+ ld [wDisabledMove], a
+ ret
+
+BattleCommand_Screen:
+; screen
+
+ ld hl, wPlayerScreens
+ ld bc, wPlayerLightScreenCount
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_screens_pointer
+ ld hl, wEnemyScreens
+ ld bc, wEnemyLightScreenCount
+
+.got_screens_pointer
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ cp EFFECT_LIGHT_SCREEN
+ jr nz, .Reflect
+
+ bit SCREENS_LIGHT_SCREEN, [hl]
+ jr nz, .failed
+ set SCREENS_LIGHT_SCREEN, [hl]
+ ld a, 5
+ ld [bc], a
+ ld hl, LightScreenEffectText
+ jr .good
+
+.Reflect:
+ bit SCREENS_REFLECT, [hl]
+ jr nz, .failed
+ set SCREENS_REFLECT, [hl]
+
+ ; LightScreenCount -> ReflectCount
+ inc bc
+
+ ld a, 5
+ ld [bc], a
+ ld hl, ReflectEffectText
+
+.good
+ call AnimateCurrentMove
+ jp StdBattleTextbox
+
+.failed
+ call AnimateFailedMove
+ jp PrintButItFailed
+
+PrintDoesntAffect:
+; 'it doesn't affect'
+ ld hl, DoesntAffectText
+ jp StdBattleTextbox
+
+PrintNothingHappened:
+; 'but nothing happened!'
+ ld hl, NothingHappenedText
+ jp StdBattleTextbox
+
+TryPrintButItFailed:
+ ld a, [wAlreadyFailed]
+ and a
+ ret nz
+
+ ; fallthrough
+
+PrintButItFailed:
+; 'but it failed!'
+ ld hl, ButItFailedText
+ jp StdBattleTextbox
+
+FailMove:
+ call AnimateFailedMove
+ ; fallthrough
+
+FailMimic:
+ ld hl, ButItFailedText ; 'but it failed!'
+ ld de, ItFailedText ; 'it failed!'
+ jp FailText_CheckOpponentProtect
+
+PrintDidntAffect:
+; 'it didn't affect'
+ ld hl, DidntAffect1Text
+ jp StdBattleTextbox
+
+PrintDidntAffect2:
+ call AnimateFailedMove
+ ld hl, DidntAffect1Text ; 'it didn't affect'
+ ld de, DidntAffect2Text ; 'it didn't affect'
+ jp FailText_CheckOpponentProtect
+
+PrintParalyze:
+; 'paralyzed! maybe it can't attack!'
+ ld hl, ParalyzedText
+ jp StdBattleTextbox
+
+CheckSubstituteOpp:
+ ld a, BATTLE_VARS_SUBSTATUS4_OPP
+ call GetBattleVar
+ bit SUBSTATUS_SUBSTITUTE, a
+ ret
+
+INCLUDE "engine/battle/move_effects/selfdestruct.asm"
+
+INCLUDE "engine/battle/move_effects/mirror_move.asm"
+
+INCLUDE "engine/battle/move_effects/metronome.asm"
+
+CheckUserMove:
+; Return z if the user has move a.
+ ld b, a
+ ld de, wBattleMonMoves
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .ok
+ ld de, wEnemyMonMoves
+.ok
+
+ ld c, NUM_MOVES
+.loop
+ ld a, [de]
+ inc de
+ cp b
+ ret z
+
+ dec c
+ jr nz, .loop
+
+ ld a, 1
+ and a
+ ret
+
+ResetTurn:
+ ld hl, wPlayerCharging
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .player
+ ld hl, wEnemyCharging
+
+.player
+ ld [hl], 1
+ xor a
+ ld [wAlreadyDisobeyed], a
+ call DoMove
+ jp EndMoveEffect
+
+INCLUDE "engine/battle/move_effects/thief.asm"
+
+BattleCommand_ArenaTrap:
+; arenatrap
+
+; Doesn't work on an absent opponent.
+
+ call CheckHiddenOpponent
+ jr nz, .failed
+
+; Don't trap if the opponent is already trapped.
+
+ ld a, BATTLE_VARS_SUBSTATUS5
+ call GetBattleVarAddr
+ bit SUBSTATUS_CANT_RUN, [hl]
+ jr nz, .failed
+
+; Otherwise trap the opponent.
+
+ set SUBSTATUS_CANT_RUN, [hl]
+ call AnimateCurrentMove
+ ld hl, CantEscapeNowText
+ jp StdBattleTextbox
+
+.failed
+ call AnimateFailedMove
+ jp PrintButItFailed
+
+INCLUDE "engine/battle/move_effects/nightmare.asm"
+
+BattleCommand_Defrost:
+; defrost
+
+; Thaw the user.
+
+ ld a, BATTLE_VARS_STATUS
+ call GetBattleVarAddr
+ bit FRZ, [hl]
+ ret z
+ res FRZ, [hl]
+
+; Don't update the enemy's party struct in a wild battle.
+
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .party
+
+ ld a, [wBattleMode]
+ dec a
+ jr z, .done
+
+.party
+ ld a, MON_STATUS
+ call UserPartyAttr
+ res FRZ, [hl]
+
+.done
+ call RefreshBattleHuds
+ ld hl, WasDefrostedText
+ jp StdBattleTextbox
+
+INCLUDE "engine/battle/move_effects/curse.asm"
+
+INCLUDE "engine/battle/move_effects/protect.asm"
+
+INCLUDE "engine/battle/move_effects/endure.asm"
+
+INCLUDE "engine/battle/move_effects/spikes.asm"
+
+INCLUDE "engine/battle/move_effects/foresight.asm"
+
+INCLUDE "engine/battle/move_effects/perish_song.asm"
+
+INCLUDE "engine/battle/move_effects/sandstorm.asm"
+
+INCLUDE "engine/battle/move_effects/rollout.asm"
+
+BattleCommand5d:
+; unused
+ ret
+
+INCLUDE "engine/battle/move_effects/fury_cutter.asm"
+
+INCLUDE "engine/battle/move_effects/attract.asm"
+
+INCLUDE "engine/battle/move_effects/return.asm"
+
+INCLUDE "engine/battle/move_effects/present.asm"
+
+INCLUDE "engine/battle/move_effects/frustration.asm"
+
+INCLUDE "engine/battle/move_effects/safeguard.asm"
+
+SafeCheckSafeguard:
+ push hl
+ ld hl, wEnemyScreens
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_turn
+ ld hl, wPlayerScreens
+
+.got_turn
+ bit SCREENS_SAFEGUARD, [hl]
+ pop hl
+ ret
+
+BattleCommand_CheckSafeguard:
+; checksafeguard
+ ld hl, wEnemyScreens
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_turn
+ ld hl, wPlayerScreens
+.got_turn
+ bit SCREENS_SAFEGUARD, [hl]
+ ret z
+ ld a, 1
+ ld [wAttackMissed], a
+ call BattleCommand_MoveDelay
+ ld hl, SafeguardProtectText
+ call StdBattleTextbox
+ jp EndMoveEffect
+
+INCLUDE "engine/battle/move_effects/magnitude.asm"
+
+INCLUDE "engine/battle/move_effects/baton_pass.asm"
+
+INCLUDE "engine/battle/move_effects/pursuit.asm"
+
+INCLUDE "engine/battle/move_effects/rapid_spin.asm"
+
+BattleCommand_HealMorn:
+; healmorn
+ ld b, MORN_F
+ jr BattleCommand_TimeBasedHealContinue
+
+BattleCommand_HealDay:
+; healday
+ ld b, DAY_F
+ jr BattleCommand_TimeBasedHealContinue
+
+BattleCommand_HealNite:
+; healnite
+ ld b, NITE_F
+ ; fallthrough
+
+BattleCommand_TimeBasedHealContinue:
+; Time- and weather-sensitive heal.
+
+ ld hl, wBattleMonMaxHP
+ ld de, wBattleMonHP
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .start
+ ld hl, wEnemyMonMaxHP
+ ld de, wEnemyMonHP
+
+.start
+; Index for .Multipliers
+; Default restores half max HP.
+ ld c, 2
+
+; Don't bother healing if HP is already full.
+ push bc
+ call CompareBytes
+ pop bc
+ jr z, .Full
+
+; Don't factor in time of day in link battles.
+ ld a, [wLinkMode]
+ and a
+ jr nz, .Weather
+
+ ld a, [wTimeOfDay]
+ cp b
+ jr z, .Weather
+ dec c ; double
+
+.Weather:
+ ld a, [wBattleWeather]
+ and a
+ jr z, .Heal
+
+; x2 in sun
+; /2 in rain/sandstorm
+ inc c
+ cp WEATHER_SUN
+ jr z, .Heal
+ dec c
+ dec c
+
+.Heal:
+ ld b, 0
+ ld hl, .Multipliers
+ add hl, bc
+ add hl, bc
+
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld a, BANK(GetMaxHP)
+ rst FarCall
+
+ call AnimateCurrentMove
+ call BattleCommand_SwitchTurn
+
+ callfar RestoreHP
+
+ call BattleCommand_SwitchTurn
+ call UpdateUserInParty
+
+; 'regained health!'
+ ld hl, RegainedHealthText
+ jp StdBattleTextbox
+
+.Full:
+ call AnimateFailedMove
+
+; 'hp is full!'
+ ld hl, HPIsFullText
+ jp StdBattleTextbox
+
+.Multipliers:
+ dw GetEighthMaxHP
+ dw GetQuarterMaxHP
+ dw GetHalfMaxHP
+ dw GetMaxHP
+
+INCLUDE "engine/battle/move_effects/hidden_power.asm"
+
+INCLUDE "engine/battle/move_effects/rain_dance.asm"
+
+INCLUDE "engine/battle/move_effects/sunny_day.asm"
+
+INCLUDE "engine/battle/move_effects/belly_drum.asm"
+
+INCLUDE "engine/battle/move_effects/psych_up.asm"
+
+INCLUDE "engine/battle/move_effects/mirror_coat.asm"
+
+BattleCommand_DoubleMinimizeDamage:
+; doubleminimizedamage
+
+ ld hl, wEnemyMinimized
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .ok
+ ld hl, wPlayerMinimized
+.ok
+ ld a, [hl]
+ and a
+ ret z
+ ld hl, wCurDamage + 1
+ sla [hl]
+ dec hl
+ rl [hl]
+ ret nc
+ ld a, $ff
+ ld [hli], a
+ ld [hl], a
+ ret
+
+BattleCommand_SkipSunCharge:
+; mimicsuncharge
+ ld a, [wBattleWeather]
+ cp WEATHER_SUN
+ ret nz
+ ld b, charge_command
+ jp SkipToBattleCommand
+
+INCLUDE "engine/battle/move_effects/future_sight.asm"
+
+INCLUDE "engine/battle/move_effects/thunder.asm"
+
+CheckHiddenOpponent:
+; BUG: This routine is completely redundant and introduces a bug, since BattleCommand_CheckHit does these checks properly.
+ ld a, BATTLE_VARS_SUBSTATUS3_OPP
+ call GetBattleVar
+ and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND
+ ret
+
+GetUserItem:
+; Return the effect of the user's item in bc, and its id at hl.
+ ld hl, wBattleMonItem
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .go
+ ld hl, wEnemyMonItem
+.go
+ ld b, [hl]
+ jp GetItemHeldEffect
+
+GetOpponentItem:
+; Return the effect of the opponent's item in bc, and its id at hl.
+ ld hl, wEnemyMonItem
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .go
+ ld hl, wBattleMonItem
+.go
+ ld b, [hl]
+ jp GetItemHeldEffect
+
+GetItemHeldEffect:
+; Return the effect of item b in bc.
+ ld a, b
+ and a
+ ret z
+
+ push hl
+ ld hl, ItemAttributes + ITEMATTR_EFFECT
+ dec a
+ ld c, a
+ ld b, 0
+ ld a, ITEMATTR_STRUCT_LENGTH
+ call AddNTimes
+ ld a, BANK(ItemAttributes)
+ call GetFarHalfword
+ ld b, l
+ ld c, h
+ pop hl
+ ret
+
+AnimateCurrentMoveEitherSide:
+ push hl
+ push de
+ push bc
+ ld a, [wKickCounter]
+ push af
+ call BattleCommand_LowerSub
+ pop af
+ ld [wKickCounter], a
+ call PlayDamageAnim
+ call BattleCommand_RaiseSub
+ pop bc
+ pop de
+ pop hl
+ ret
+
+AnimateCurrentMove:
+ push hl
+ push de
+ push bc
+ ld a, [wKickCounter]
+ push af
+ call BattleCommand_LowerSub
+ pop af
+ ld [wKickCounter], a
+ call LoadMoveAnim
+ call BattleCommand_RaiseSub
+ pop bc
+ pop de
+ pop hl
+ ret
+
+PlayDamageAnim:
+ xor a
+ ld [wFXAnimID + 1], a
+
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+ and a
+ ret z
+
+ ld [wFXAnimID], a
+
+ ldh a, [hBattleTurn]
+ and a
+ ld a, BATTLEANIM_ENEMY_DAMAGE
+ jr z, .player
+ ld a, BATTLEANIM_PLAYER_DAMAGE
+
+.player
+ ld [wNumHits], a
+
+ jp PlayUserBattleAnim
+
+LoadMoveAnim:
+ xor a
+ ld [wNumHits], a
+ ld [wFXAnimID + 1], a
+
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+ and a
+ ret z
+
+ ; fallthrough
+
+LoadAnim:
+ ld [wFXAnimID], a
+
+ ; fallthrough
+
+PlayUserBattleAnim:
+ push hl
+ push de
+ push bc
+ callfar PlayBattleAnim
+ pop bc
+ pop de
+ pop hl
+ ret
+
+PlayOpponentBattleAnim:
+ ld a, e
+ ld [wFXAnimID], a
+ ld a, d
+ ld [wFXAnimID + 1], a
+ xor a
+ ld [wNumHits], a
+
+ push hl
+ push de
+ push bc
+ call BattleCommand_SwitchTurn
+
+ callfar PlayBattleAnim
+
+ call BattleCommand_SwitchTurn
+ pop bc
+ pop de
+ pop hl
+ ret
+
+CallBattleCore:
+ ld a, BANK("Battle Core")
+ rst FarCall
+ ret
+
+AnimateFailedMove:
+ call BattleCommand_LowerSub
+ call BattleCommand_MoveDelay
+ jp BattleCommand_RaiseSub
+
+BattleCommand_MoveDelay:
+; movedelay
+; Wait 40 frames.
+ ld c, 40
+ jp DelayFrames
+
+BattleCommand_ClearText:
+; cleartext
+
+; Used in multi-hit moves.
+ ld hl, .text
+ jp PrintText
+
+.text:
+ text_end
+
+SkipToBattleCommand:
+; Skip over commands until reaching command b.
+ ld a, [wBattleScriptBufferAddress + 1]
+ ld h, a
+ ld a, [wBattleScriptBufferAddress]
+ ld l, a
+.loop
+ ld a, [hli]
+ cp b
+ jr nz, .loop
+
+ ld a, h
+ ld [wBattleScriptBufferAddress + 1], a
+ ld a, l
+ ld [wBattleScriptBufferAddress], a
+ ret
+
+GetMoveAttr:
+; Assuming hl = Moves + x, return attribute x of move a.
+ push bc
+ ld bc, MOVE_LENGTH
+ call AddNTimes
+ call GetMoveByte
+ pop bc
+ ret
+
+GetMoveData:
+; Copy move struct a to de.
+ ld hl, Moves
+ ld bc, MOVE_LENGTH
+ call AddNTimes
+ ld a, BANK(Moves)
+ jp FarCopyBytes
+
+GetMoveByte:
+ ld a, BANK(Moves)
+ jp GetFarByte
+
+DisappearUser:
+ farcall _DisappearUser
+ ret
+
+AppearUserLowerSub:
+ farcall _AppearUserLowerSub
+ ret
+
+AppearUserRaiseSub:
+ farcall _AppearUserRaiseSub
+ ret
diff --git a/engine/battle/move_effects/attract.asm b/engine/battle/move_effects/attract.asm
new file mode 100644
index 00000000..5eb82d65
--- /dev/null
+++ b/engine/battle/move_effects/attract.asm
@@ -0,0 +1,76 @@
+BattleCommand_Attract:
+; attract
+ ld a, [wAttackMissed]
+ and a
+ jr nz, .failed
+ call CheckOppositeGender
+ jr c, .failed
+ call CheckHiddenOpponent
+ jr nz, .failed
+ ld a, BATTLE_VARS_SUBSTATUS1_OPP
+ call GetBattleVarAddr
+ bit SUBSTATUS_IN_LOVE, [hl]
+ jr nz, .failed
+
+ set SUBSTATUS_IN_LOVE, [hl]
+ call AnimateCurrentMove
+
+; 'fell in love!'
+ ld hl, FellInLoveText
+ jp StdBattleTextbox
+
+.failed
+ jp FailMove
+
+CheckOppositeGender:
+ ld a, MON_SPECIES
+ call BattlePartyAttr
+ ld a, [hl]
+ ld [wCurPartySpecies], a
+
+ ld a, [wCurBattleMon]
+ ld [wCurPartyMon], a
+ xor a
+ ld [wMonType], a
+
+ farcall GetGender
+ jr c, .genderless_samegender
+
+ ld b, 1
+ jr nz, .got_gender
+ dec b
+
+.got_gender
+ push bc
+ ld a, [wTempEnemyMonSpecies]
+ ld [wCurPartySpecies], a
+ ld hl, wEnemyMonDVs
+ ld a, [wEnemySubStatus5]
+ bit SUBSTATUS_TRANSFORMED, a
+ jr z, .not_transformed
+ ld hl, wEnemyBackupDVs
+.not_transformed
+ ld a, [hli]
+ ld [wTempMonDVs], a
+ ld a, [hl]
+ ld [wTempMonDVs + 1], a
+ ld a, 3
+ ld [wMonType], a
+ farcall GetGender
+ pop bc
+ jr c, .genderless_samegender
+
+ ld a, 1
+ jr nz, .got_enemy_gender
+ dec a
+
+.got_enemy_gender
+ xor b
+ jr z, .genderless_samegender
+
+ and a
+ ret
+
+.genderless_samegender
+ scf
+ ret
diff --git a/engine/battle/move_effects/baton_pass.asm b/engine/battle/move_effects/baton_pass.asm
new file mode 100644
index 00000000..bf9e3235
--- /dev/null
+++ b/engine/battle/move_effects/baton_pass.asm
@@ -0,0 +1,194 @@
+BattleCommand_BatonPass:
+; batonpass
+
+ ldh a, [hBattleTurn]
+ and a
+ jp nz, .Enemy
+
+; Need something to switch to
+ call CheckAnyOtherAlivePartyMons
+ jp z, FailedBatonPass
+
+ call UpdateBattleMonInParty
+ call AnimateCurrentMove
+
+ ld c, 50
+ call DelayFrames
+
+; Transition into switchmon menu
+ call LoadStandardMenuHeader
+ farcall SetUpBattlePartyMenu_NoLoop
+
+ farcall ForcePickSwitchMonInBattle
+
+; Return to battle scene
+ call ClearPalettes
+ farcall _LoadBattleFontsHPBar
+ call CloseWindow
+ call ClearSprites
+ hlcoord 1, 0
+ lb bc, 4, 10
+ call ClearBox
+ ld b, SCGB_BATTLE_COLORS
+ call GetSGBLayout
+ call SetPalettes
+ call BatonPass_LinkPlayerSwitch
+
+ ld hl, PassedBattleMonEntrance
+ call CallBattleCore
+
+ call ResetBatonPassStatus
+ ret
+
+.Enemy:
+; Wildmons don't have anything to switch to
+ ld a, [wBattleMode]
+ dec a ; WILDMON
+ jp z, FailedBatonPass
+
+ call CheckAnyOtherAliveEnemyMons
+ jp z, FailedBatonPass
+
+ call UpdateEnemyMonInParty
+ call AnimateCurrentMove
+ call BatonPass_LinkEnemySwitch
+
+; Passed enemy PartyMon entrance
+ xor a
+ ld [wEnemySwitchMonIndex], a
+ ld hl, EnemySwitch_SetMode
+ call CallBattleCore
+ ld hl, ResetBattleParticipants
+ call CallBattleCore
+ ld a, TRUE
+ ld [wApplyStatLevelMultipliersToEnemy], a
+ ld hl, ApplyStatLevelMultiplierOnAllStats
+ call CallBattleCore
+
+ ld hl, SpikesDamage
+ call CallBattleCore
+
+ jr ResetBatonPassStatus
+
+BatonPass_LinkPlayerSwitch:
+ ld a, [wLinkMode]
+ and a
+ ret z
+
+ ld a, BATTLEPLAYERACTION_USEITEM
+ ld [wBattlePlayerAction], a
+
+ call LoadStandardMenuHeader
+ ld hl, LinkBattleSendReceiveAction
+ call CallBattleCore
+ call CloseWindow
+
+ xor a ; BATTLEPLAYERACTION_USEMOVE
+ ld [wBattlePlayerAction], a
+ ret
+
+BatonPass_LinkEnemySwitch:
+ ld a, [wLinkMode]
+ and a
+ ret z
+
+ call LoadStandardMenuHeader
+ ld hl, LinkBattleSendReceiveAction
+ call CallBattleCore
+ jp CloseWindow
+
+FailedBatonPass:
+ call AnimateFailedMove
+ jp PrintButItFailed
+
+ResetBatonPassStatus:
+; Reset status changes that aren't passed by Baton Pass.
+
+ ; Nightmare isn't passed.
+ ld a, BATTLE_VARS_STATUS
+ call GetBattleVar
+ and SLP
+ jr nz, .ok
+
+ ld a, BATTLE_VARS_SUBSTATUS1
+ call GetBattleVarAddr
+ res SUBSTATUS_NIGHTMARE, [hl]
+.ok
+
+ ; Disable isn't passed.
+ call ResetActorDisable
+
+ ; Attraction isn't passed.
+ ld hl, wPlayerSubStatus1
+ res SUBSTATUS_IN_LOVE, [hl]
+ ld hl, wEnemySubStatus1
+ res SUBSTATUS_IN_LOVE, [hl]
+ ld hl, wPlayerSubStatus5
+
+ ld a, BATTLE_VARS_SUBSTATUS5
+ call GetBattleVarAddr
+ res SUBSTATUS_TRANSFORMED, [hl]
+ res SUBSTATUS_ENCORED, [hl]
+
+ ; New mon hasn't used a move yet.
+ ld a, BATTLE_VARS_LAST_MOVE
+ call GetBattleVarAddr
+ ld [hl], 0
+
+ xor a
+ ld [wPlayerWrapCount], a
+ ld [wEnemyWrapCount], a
+ ret
+
+CheckAnyOtherAlivePartyMons:
+ ld hl, wPartyMon1HP
+ ld a, [wPartyCount]
+ ld d, a
+ ld a, [wCurBattleMon]
+ ld e, a
+ jr CheckAnyOtherAliveMons
+
+CheckAnyOtherAliveEnemyMons:
+ ld hl, wOTPartyMon1HP
+ ld a, [wOTPartyCount]
+ ld d, a
+ ld a, [wCurOTMon]
+ ld e, a
+
+ ; fallthrough
+
+CheckAnyOtherAliveMons:
+; Check for nonzero HP starting from partymon
+; HP at hl for d partymons, besides current mon e.
+
+; Return nz if any are alive.
+
+ xor a
+ ld b, a
+ ld c, a
+.loop
+ ld a, c
+ cp d
+ jr z, .done
+ cp e
+ jr z, .next
+
+ ld a, [hli]
+ or b
+ ld b, a
+ ld a, [hld]
+ or b
+ ld b, a
+
+.next
+ push bc
+ ld bc, PARTYMON_STRUCT_LENGTH
+ add hl, bc
+ pop bc
+ inc c
+ jr .loop
+
+.done
+ ld a, b
+ and a
+ ret
diff --git a/engine/battle/move_effects/belly_drum.asm b/engine/battle/move_effects/belly_drum.asm
new file mode 100644
index 00000000..5b1361f2
--- /dev/null
+++ b/engine/battle/move_effects/belly_drum.asm
@@ -0,0 +1,30 @@
+BattleCommand_BellyDrum:
+; bellydrum
+; This command is buggy because it raises the user's attack
+; before checking that it has enough HP to use the move.
+; Swap the order of these two blocks to fix.
+ call BattleCommand_AttackUp2
+ ld a, [wAttackMissed]
+ and a
+ jr nz, .failed
+
+ callfar GetHalfMaxHP
+ callfar CheckUserHasEnoughHP
+ jr nc, .failed
+
+ push bc
+ call AnimateCurrentMove
+ pop bc
+ callfar SubtractHPFromUser
+ call UpdateUserInParty
+
+rept 5
+ call BattleCommand_AttackUp2
+endr
+
+ ld hl, BellyDrumText
+ jp StdBattleTextbox
+
+.failed
+ call AnimateFailedMove
+ jp PrintButItFailed
diff --git a/engine/battle/move_effects/bide.asm b/engine/battle/move_effects/bide.asm
new file mode 100644
index 00000000..74f7c9cf
--- /dev/null
+++ b/engine/battle/move_effects/bide.asm
@@ -0,0 +1,100 @@
+BattleCommand_StoreEnergy:
+; storeenergy
+
+ ld a, BATTLE_VARS_SUBSTATUS3
+ call GetBattleVar
+ bit SUBSTATUS_BIDE, a
+ ret z
+
+ ld hl, wPlayerRolloutCount
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .check_still_storing_energy
+ ld hl, wEnemyRolloutCount
+.check_still_storing_energy
+ dec [hl]
+ jr nz, .still_storing
+
+ ld a, BATTLE_VARS_SUBSTATUS3
+ call GetBattleVarAddr
+ res SUBSTATUS_BIDE, [hl]
+
+ ld hl, UnleashedEnergyText
+ call StdBattleTextbox
+
+ ld a, BATTLE_VARS_MOVE_POWER
+ call GetBattleVarAddr
+ ld a, 1
+ ld [hl], a
+ ld hl, wPlayerDamageTaken + 1
+ ld de, wPlayerCharging ; player
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .player
+ ld hl, wEnemyDamageTaken + 1
+ ld de, wEnemyCharging ; enemy
+.player
+ ld a, [hld]
+ add a
+ ld b, a
+ ld [wCurDamage + 1], a
+ ld a, [hl]
+ rl a
+ ld [wCurDamage], a
+ jr nc, .not_maxed
+ ld a, $ff
+ ld [wCurDamage], a
+ ld [wCurDamage + 1], a
+.not_maxed
+ or b
+ jr nz, .built_up_something
+ ld a, 1
+ ld [wAttackMissed], a
+.built_up_something
+ xor a
+ ld [hli], a
+ ld [hl], a
+ ld [de], a
+
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVarAddr
+ ld a, BIDE
+ ld [hl], a
+
+ ld b, unleashenergy_command
+ jp SkipToBattleCommand
+
+.still_storing
+ ld hl, StoringEnergyText
+ call StdBattleTextbox
+ jp EndMoveEffect
+
+BattleCommand_UnleashEnergy:
+; unleashenergy
+
+ ld de, wPlayerDamageTaken
+ ld bc, wPlayerRolloutCount
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_damage
+ ld de, wEnemyDamageTaken
+ ld bc, wEnemyRolloutCount
+.got_damage
+ ld a, BATTLE_VARS_SUBSTATUS3
+ call GetBattleVarAddr
+ set SUBSTATUS_BIDE, [hl]
+ xor a
+ ld [de], a
+ inc de
+ ld [de], a
+ ld [wPlayerMoveStructEffect], a
+ ld [wEnemyMoveStructEffect], a
+ call BattleRandom
+ and 1
+ inc a
+ inc a
+ ld [bc], a
+ ld a, 1
+ ld [wKickCounter], a
+ call AnimateCurrentMove
+ jp EndMoveEffect
diff --git a/engine/battle/move_effects/conversion.asm b/engine/battle/move_effects/conversion.asm
new file mode 100644
index 00000000..b6081a6b
--- /dev/null
+++ b/engine/battle/move_effects/conversion.asm
@@ -0,0 +1,96 @@
+BattleCommand_Conversion:
+; conversion
+
+ ld hl, wBattleMonMoves
+ ld de, wBattleMonType1
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_moves
+ ld hl, wEnemyMonMoves
+ ld de, wEnemyMonType1
+.got_moves
+ push de
+ ld c, 0
+ ld de, wStringBuffer1
+.loop
+ push hl
+ ld b, 0
+ add hl, bc
+ ld a, [hl]
+ pop hl
+ and a
+ jr z, .okay
+ push hl
+ push bc
+ dec a
+ ld hl, Moves + MOVE_TYPE
+ call GetMoveAttr
+ ld [de], a
+ inc de
+ pop bc
+ pop hl
+ inc c
+ ld a, c
+ cp NUM_MOVES
+ jr c, .loop
+.okay
+ ld a, $ff
+ ld [de], a
+ inc de
+ ld [de], a
+ inc de
+ ld [de], a
+ pop de
+ ld hl, wStringBuffer1
+.loop2
+ ld a, [hl]
+ cp -1
+ jr z, .fail
+ cp CURSE_TYPE
+ jr z, .next
+ ld a, [de]
+ cp [hl]
+ jr z, .next
+ inc de
+ ld a, [de]
+ dec de
+ cp [hl]
+ jr nz, .done
+.next
+ inc hl
+ jr .loop2
+
+.fail
+ call AnimateFailedMove
+ jp PrintButItFailed
+
+.done
+.loop3
+ call BattleRandom
+ maskbits NUM_MOVES
+ ld c, a
+ ld b, 0
+ ld hl, wStringBuffer1
+ add hl, bc
+ ld a, [hl]
+ cp -1
+ jr z, .loop3
+ cp CURSE_TYPE
+ jr z, .loop3
+ ld a, [de]
+ cp [hl]
+ jr z, .loop3
+ inc de
+ ld a, [de]
+ dec de
+ cp [hl]
+ jr z, .loop3
+ ld a, [hl]
+ ld [de], a
+ inc de
+ ld [de], a
+ ld [wNamedObjectIndexBuffer], a
+ farcall GetTypeName
+ call AnimateCurrentMove
+ ld hl, TransformedTypeText
+ jp StdBattleTextbox
diff --git a/engine/battle/move_effects/curse.asm b/engine/battle/move_effects/curse.asm
new file mode 100644
index 00000000..3507c668
--- /dev/null
+++ b/engine/battle/move_effects/curse.asm
@@ -0,0 +1,93 @@
+BattleCommand_Curse:
+; curse
+
+ ld de, wBattleMonType1
+ ld bc, wPlayerStatLevels
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .go
+ ld de, wEnemyMonType1
+ ld bc, wEnemyStatLevels
+
+.go
+
+; Curse is different for Ghost-types.
+
+ ld a, [de]
+ cp GHOST
+ jr z, .ghost
+ inc de
+ ld a, [de]
+ cp GHOST
+ jr z, .ghost
+
+; If no stats can be increased, don't.
+
+; Attack
+ ld a, [bc]
+ cp MAX_STAT_LEVEL
+ jr c, .raise
+
+; Defense
+ inc bc
+ ld a, [bc]
+ cp MAX_STAT_LEVEL
+ jr nc, .cantraise
+
+.raise
+
+; Raise Attack and Defense, and lower Speed.
+
+ ld a, $1
+ ld [wKickCounter], a
+ call AnimateCurrentMove
+ ld a, SPEED
+ call LowerStat
+ call BattleCommand_SwitchTurn
+ call BattleCommand_StatDownMessage
+ call ResetMiss
+ call BattleCommand_SwitchTurn
+ call BattleCommand_AttackUp
+ call BattleCommand_StatUpMessage
+ call ResetMiss
+ call BattleCommand_DefenseUp
+ jp BattleCommand_StatUpMessage
+
+.ghost
+
+; Cut HP in half and put a curse on the opponent.
+
+ call CheckHiddenOpponent
+ jr nz, .failed
+
+ call CheckSubstituteOpp
+ jr nz, .failed
+
+ ld a, BATTLE_VARS_SUBSTATUS1_OPP
+ call GetBattleVarAddr
+ bit SUBSTATUS_CURSE, [hl]
+ jr nz, .failed
+
+ set SUBSTATUS_CURSE, [hl]
+ call AnimateCurrentMove
+ ld hl, GetHalfMaxHP
+ call CallBattleCore
+ ld hl, SubtractHPFromUser
+ call CallBattleCore
+ call UpdateUserInParty
+ ld hl, PutACurseText
+ jp StdBattleTextbox
+
+.failed
+ call AnimateFailedMove
+ jp PrintButItFailed
+
+.cantraise
+
+; Can't raise either stat.
+
+ ld b, ABILITY + 1
+ call GetStatName
+ call AnimateFailedMove
+ ld hl, WontRiseAnymoreText
+ jp StdBattleTextbox
diff --git a/engine/battle/move_effects/disable.asm b/engine/battle/move_effects/disable.asm
new file mode 100644
index 00000000..de6dbc60
--- /dev/null
+++ b/engine/battle/move_effects/disable.asm
@@ -0,0 +1,72 @@
+BattleCommand_Disable:
+; disable
+
+ ld a, [wAttackMissed]
+ and a
+ jr nz, .failed
+
+ ld de, wEnemyDisableCount
+ ld hl, wEnemyMonMoves
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_moves
+ ld de, wPlayerDisableCount
+ ld hl, wBattleMonMoves
+.got_moves
+
+ ld a, [de]
+ and a
+ jr nz, .failed
+
+ ld a, BATTLE_VARS_LAST_COUNTER_MOVE_OPP
+ call GetBattleVar
+ and a
+ jr z, .failed
+ cp STRUGGLE
+ jr z, .failed
+
+ ld b, a
+ ld c, $ff
+.loop
+ inc c
+ ld a, [hli]
+ cp b
+ jr nz, .loop
+
+ ldh a, [hBattleTurn]
+ and a
+ ld hl, wEnemyMonPP
+ jr z, .got_pp
+ ld hl, wBattleMonPP
+.got_pp
+ ld b, 0
+ add hl, bc
+ ld a, [hl]
+ and a
+ jr z, .failed
+.loop2
+ call BattleRandom
+ and 7
+ jr z, .loop2
+ inc a
+ inc c
+ swap c
+ add c
+ ld [de], a
+ call AnimateCurrentMove
+ ld hl, wDisabledMove
+ ldh a, [hBattleTurn]
+ and a
+ jr nz, .got_disabled_move_pointer
+ inc hl
+.got_disabled_move_pointer
+ ld a, BATTLE_VARS_LAST_COUNTER_MOVE_OPP
+ call GetBattleVar
+ ld [hl], a
+ ld [wNamedObjectIndexBuffer], a
+ call GetMoveName
+ ld hl, WasDisabledText
+ jp StdBattleTextbox
+
+.failed
+ jp FailMove
diff --git a/engine/battle/move_effects/endure.asm b/engine/battle/move_effects/endure.asm
new file mode 100644
index 00000000..00ccb130
--- /dev/null
+++ b/engine/battle/move_effects/endure.asm
@@ -0,0 +1,16 @@
+BattleCommand_Endure:
+; endure
+
+; Endure shares code with Protect. See protect.asm.
+
+ call ProtectChance
+ ret c
+
+ ld a, BATTLE_VARS_SUBSTATUS1
+ call GetBattleVarAddr
+ set SUBSTATUS_ENDURE, [hl]
+
+ call AnimateCurrentMove
+
+ ld hl, BracedItselfText
+ jp StdBattleTextbox
diff --git a/engine/battle/move_effects/focus_energy.asm b/engine/battle/move_effects/focus_energy.asm
new file mode 100644
index 00000000..c4eb1f33
--- /dev/null
+++ b/engine/battle/move_effects/focus_energy.asm
@@ -0,0 +1,15 @@
+BattleCommand_FocusEnergy:
+; focusenergy
+
+ ld a, BATTLE_VARS_SUBSTATUS4
+ call GetBattleVarAddr
+ bit SUBSTATUS_FOCUS_ENERGY, [hl]
+ jr nz, .already_pumped
+ set SUBSTATUS_FOCUS_ENERGY, [hl]
+ call AnimateCurrentMove
+ ld hl, GettingPumpedText
+ jp StdBattleTextbox
+
+.already_pumped
+ call AnimateFailedMove
+ jp PrintButItFailed
diff --git a/engine/battle/move_effects/foresight.asm b/engine/battle/move_effects/foresight.asm
new file mode 100644
index 00000000..ff25b04e
--- /dev/null
+++ b/engine/battle/move_effects/foresight.asm
@@ -0,0 +1,22 @@
+BattleCommand_Foresight:
+; foresight
+
+ ld a, [wAttackMissed]
+ and a
+ jr nz, .failed
+
+ call CheckHiddenOpponent
+ jr nz, .failed
+
+ ld a, BATTLE_VARS_SUBSTATUS1_OPP
+ call GetBattleVarAddr
+ bit SUBSTATUS_IDENTIFIED, [hl]
+ jr nz, .failed
+
+ set SUBSTATUS_IDENTIFIED, [hl]
+ call AnimateCurrentMove
+ ld hl, IdentifiedText
+ jp StdBattleTextbox
+
+.failed
+ jp FailMove
diff --git a/engine/battle/move_effects/frustration.asm b/engine/battle/move_effects/frustration.asm
new file mode 100644
index 00000000..b07942c7
--- /dev/null
+++ b/engine/battle/move_effects/frustration.asm
@@ -0,0 +1,27 @@
+BattleCommand_FrustrationPower:
+; frustrationpower
+
+ push bc
+ ld hl, wBattleMonHappiness
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_happiness
+ ld hl, wEnemyMonHappiness
+.got_happiness
+ ld a, $ff
+ sub [hl]
+ ldh [hMultiplicand + 2], a
+ xor a
+ ldh [hMultiplicand + 0], a
+ ldh [hMultiplicand + 1], a
+ ld a, 10
+ ldh [hMultiplier], a
+ call Multiply
+ ld a, 25
+ ldh [hDivisor], a
+ ld b, 4
+ call Divide
+ ldh a, [hQuotient + 3]
+ ld d, a
+ pop bc
+ ret
diff --git a/engine/battle/move_effects/fury_cutter.asm b/engine/battle/move_effects/fury_cutter.asm
new file mode 100644
index 00000000..a1284849
--- /dev/null
+++ b/engine/battle/move_effects/fury_cutter.asm
@@ -0,0 +1,55 @@
+BattleCommand_FuryCutter:
+; furycutter
+
+ ld hl, wPlayerFuryCutterCount
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .go
+ ld hl, wEnemyFuryCutterCount
+
+.go
+ ld a, [wAttackMissed]
+ and a
+ jp nz, ResetFuryCutterCount
+
+ inc [hl]
+
+; Damage capped at 5 turns' worth (16x).
+ ld a, [hl]
+ ld b, a
+ cp 6
+ jr c, .checkdouble
+ ld b, 5
+
+.checkdouble
+ dec b
+ ret z
+
+; Double the damage
+ ld hl, wCurDamage + 1
+ sla [hl]
+ dec hl
+ rl [hl]
+ jr nc, .checkdouble
+
+; No overflow
+ ld a, $ff
+ ld [hli], a
+ ld [hl], a
+ ret
+
+ResetFuryCutterCount:
+ push hl
+
+ ld hl, wPlayerFuryCutterCount
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .reset
+ ld hl, wEnemyFuryCutterCount
+
+.reset
+ xor a
+ ld [hl], a
+
+ pop hl
+ ret
diff --git a/engine/battle/move_effects/future_sight.asm b/engine/battle/move_effects/future_sight.asm
new file mode 100644
index 00000000..129a9e08
--- /dev/null
+++ b/engine/battle/move_effects/future_sight.asm
@@ -0,0 +1,81 @@
+BattleCommand_CheckFutureSight:
+; checkfuturesight
+
+ ld hl, wPlayerFutureSightCount
+ ld de, wPlayerFutureSightDamage
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .ok
+ ld hl, wEnemyFutureSightCount
+ ld de, wEnemyFutureSightDamage
+.ok
+
+ ld a, [hl]
+ and a
+ ret z
+ cp 1
+ ret nz
+
+ ld [hl], 0
+ ld a, [de]
+ inc de
+ ld [wCurDamage], a
+ ld a, [de]
+ ld [wCurDamage + 1], a
+ ld b, futuresight_command
+ jp SkipToBattleCommand
+
+BattleCommand_FutureSight:
+; futuresight
+
+ call CheckUserIsCharging
+ jr nz, .AlreadyChargingFutureSight
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+ ld b, a
+ ld a, BATTLE_VARS_LAST_COUNTER_MOVE
+ call GetBattleVarAddr
+ ld [hl], b
+ ld a, BATTLE_VARS_LAST_MOVE
+ call GetBattleVarAddr
+ ld [hl], b
+.AlreadyChargingFutureSight:
+ ld hl, wPlayerFutureSightCount
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .GotFutureSightCount
+ ld hl, wEnemyFutureSightCount
+.GotFutureSightCount:
+ ld a, [hl]
+ and a
+ jr nz, .failed
+ ld a, 4
+ ld [hl], a
+ call BattleCommand_LowerSub
+ call BattleCommand_MoveDelay
+ ld hl, ForesawAttackText
+ call StdBattleTextbox
+ call BattleCommand_RaiseSub
+ ld de, wPlayerFutureSightDamage
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .StoreDamage
+ ld de, wEnemyFutureSightDamage
+.StoreDamage:
+ ld hl, wCurDamage
+ ld a, [hl]
+ ld [de], a
+ ld [hl], 0
+ inc hl
+ inc de
+ ld a, [hl]
+ ld [de], a
+ ld [hl], 0
+ jp EndMoveEffect
+
+.failed
+ pop bc
+ call ResetDamage
+ call AnimateFailedMove
+ call PrintButItFailed
+ jp EndMoveEffect
diff --git a/engine/battle/move_effects/hidden_power.asm b/engine/battle/move_effects/hidden_power.asm
new file mode 100644
index 00000000..3b40a6c3
--- /dev/null
+++ b/engine/battle/move_effects/hidden_power.asm
@@ -0,0 +1,8 @@
+BattleCommand_HiddenPower:
+; hiddenpower
+
+ ld a, [wAttackMissed]
+ and a
+ ret nz
+ farcall HiddenPowerDamage
+ ret
diff --git a/engine/battle/move_effects/leech_seed.asm b/engine/battle/move_effects/leech_seed.asm
new file mode 100644
index 00000000..bb17ee00
--- /dev/null
+++ b/engine/battle/move_effects/leech_seed.asm
@@ -0,0 +1,40 @@
+BattleCommand_LeechSeed:
+; leechseed
+ ld a, [wAttackMissed]
+ and a
+ jr nz, .evaded
+ call CheckSubstituteOpp
+ jr nz, .evaded
+
+ ld de, wEnemyMonType1
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .ok
+ ld de, wBattleMonType1
+.ok
+
+ ld a, [de]
+ cp GRASS
+ jr z, .grass
+ inc de
+ ld a, [de]
+ cp GRASS
+ jr z, .grass
+
+ ld a, BATTLE_VARS_SUBSTATUS4_OPP
+ call GetBattleVarAddr
+ bit SUBSTATUS_LEECH_SEED, [hl]
+ jr nz, .evaded
+ set SUBSTATUS_LEECH_SEED, [hl]
+ call AnimateCurrentMove
+ ld hl, WasSeededText
+ jp StdBattleTextbox
+
+.grass
+ call AnimateFailedMove
+ jp PrintDoesntAffect
+
+.evaded
+ call AnimateFailedMove
+ ld hl, EvadedText
+ jp StdBattleTextbox
diff --git a/engine/battle/move_effects/magnitude.asm b/engine/battle/move_effects/magnitude.asm
new file mode 100644
index 00000000..f8961b66
--- /dev/null
+++ b/engine/battle/move_effects/magnitude.asm
@@ -0,0 +1,29 @@
+BattleCommand_GetMagnitude:
+; getmagnitude
+
+ push bc
+ call BattleRandom
+ ld b, a
+ ld hl, MagnitudePower
+.loop
+ ld a, [hli]
+ cp b
+ jr nc, .ok
+ inc hl
+ inc hl
+ jr .loop
+
+.ok
+ ld d, [hl]
+ push de
+ inc hl
+ ld a, [hl]
+ ld [wDeciramBuffer], a
+ call BattleCommand_MoveDelay
+ ld hl, MagnitudeText
+ call StdBattleTextbox
+ pop de
+ pop bc
+ ret
+
+INCLUDE "data/moves/magnitude_power.asm"
diff --git a/engine/battle/move_effects/metronome.asm b/engine/battle/move_effects/metronome.asm
new file mode 100644
index 00000000..25197d7c
--- /dev/null
+++ b/engine/battle/move_effects/metronome.asm
@@ -0,0 +1,43 @@
+BattleCommand_Metronome:
+; metronome
+
+ call ClearLastMove
+ call CheckUserIsCharging
+ jr nz, .asm_3742b
+
+ ld a, [wKickCounter]
+ push af
+ call BattleCommand_LowerSub
+ pop af
+ ld [wKickCounter], a
+
+.asm_3742b
+ call LoadMoveAnim
+
+.GetMove:
+ call BattleRandom
+
+; No invalid moves.
+ cp NUM_ATTACKS + 1
+ jr nc, .GetMove
+
+; None of the moves in MetronomeExcepts.
+ push af
+ ld de, 1
+ ld hl, MetronomeExcepts
+ call IsInArray
+ pop bc
+ jr c, .GetMove
+
+; No moves the user already has.
+ ld a, b
+ call CheckUserMove
+ jr z, .GetMove
+
+ ld a, BATTLE_VARS_MOVE
+ call GetBattleVarAddr
+ ld [hl], b
+ call UpdateMoveData
+ jp ResetTurn
+
+INCLUDE "data/moves/metronome_exception_moves.asm"
diff --git a/engine/battle/move_effects/mimic.asm b/engine/battle/move_effects/mimic.asm
new file mode 100644
index 00000000..71eb72c6
--- /dev/null
+++ b/engine/battle/move_effects/mimic.asm
@@ -0,0 +1,50 @@
+BattleCommand_Mimic:
+; mimic
+
+ call ClearLastMove
+ call BattleCommand_MoveDelay
+ ld a, [wAttackMissed]
+ and a
+ jr nz, .fail
+ ld hl, wBattleMonMoves
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .player_turn
+ ld hl, wEnemyMonMoves
+.player_turn
+ call CheckHiddenOpponent
+ jr nz, .fail
+ ld a, BATTLE_VARS_LAST_COUNTER_MOVE_OPP
+ call GetBattleVar
+ and a
+ jr z, .fail
+ cp STRUGGLE
+ jr z, .fail
+ ld b, a
+ ld c, NUM_MOVES
+.check_already_knows_move
+ ld a, [hli]
+ cp b
+ jr z, .fail
+ dec c
+ jr nz, .check_already_knows_move
+ dec hl
+.find_mimic
+ ld a, [hld]
+ cp MIMIC
+ jr nz, .find_mimic
+ inc hl
+ ld a, BATTLE_VARS_LAST_COUNTER_MOVE_OPP
+ call GetBattleVar
+ ld [hl], a
+ ld [wNamedObjectIndexBuffer], a
+ ld bc, wBattleMonPP - wBattleMonMoves
+ add hl, bc
+ ld [hl], 5
+ call GetMoveName
+ call AnimateCurrentMove
+ ld hl, MimicLearnedMoveText
+ jp StdBattleTextbox
+
+.fail
+ jp FailMimic
diff --git a/engine/battle/move_effects/mirror_coat.asm b/engine/battle/move_effects/mirror_coat.asm
new file mode 100644
index 00000000..96afa317
--- /dev/null
+++ b/engine/battle/move_effects/mirror_coat.asm
@@ -0,0 +1,60 @@
+BattleCommand_MirrorCoat:
+; mirrorcoat
+
+ ld a, 1
+ ld [wAttackMissed], a
+
+ ld a, BATTLE_VARS_LAST_COUNTER_MOVE_OPP
+ call GetBattleVar
+ and a
+ ret z
+
+ ld b, a
+ callfar GetMoveEffect
+ ld a, b
+ cp EFFECT_MIRROR_COAT
+ ret z
+
+ call BattleCommand_ResetTypeMatchup
+ ld a, [wTypeMatchup]
+ and a
+ ret z
+
+ call CheckOpponentWentFirst
+ ret z
+
+ ld a, BATTLE_VARS_LAST_COUNTER_MOVE_OPP
+ call GetBattleVar
+ dec a
+ ld de, wStringBuffer1
+ call GetMoveData
+
+ ld a, [wStringBuffer1 + MOVE_POWER]
+ and a
+ ret z
+
+ ld a, [wStringBuffer1 + MOVE_TYPE]
+ cp SPECIAL
+ ret c
+
+ ; BUG: Move should fail with all non-damaging battle actions
+ ld hl, wCurDamage
+ ld a, [hli]
+ or [hl]
+ ret z
+
+ ld a, [hl]
+ add a
+ ld [hld], a
+ ld a, [hl]
+ adc a
+ ld [hl], a
+ jr nc, .capped
+ ld a, $ff
+ ld [hli], a
+ ld [hl], a
+.capped
+
+ xor a
+ ld [wAttackMissed], a
+ ret
diff --git a/engine/battle/move_effects/mirror_move.asm b/engine/battle/move_effects/mirror_move.asm
new file mode 100644
index 00000000..98e8aacc
--- /dev/null
+++ b/engine/battle/move_effects/mirror_move.asm
@@ -0,0 +1,51 @@
+BattleCommand_MirrorMove:
+; mirrormove
+
+ call ClearLastMove
+
+ ld a, BATTLE_VARS_MOVE
+ call GetBattleVarAddr
+
+ ld a, BATTLE_VARS_LAST_COUNTER_MOVE_OPP
+ call GetBattleVar
+ and a
+ jr z, .failed
+
+ call CheckUserMove
+ jr nz, .use
+
+.failed
+ call AnimateFailedMove
+
+ ld hl, MirrorMoveFailedText
+ call StdBattleTextbox
+ jp EndMoveEffect
+
+.use
+ ld a, b
+ ld [hl], a
+ ld [wNamedObjectIndexBuffer], a
+
+ push af
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVarAddr
+ ld d, h
+ ld e, l
+ pop af
+
+ dec a
+ call GetMoveData
+ call GetMoveName
+ call CopyName1
+ call CheckUserIsCharging
+ jr nz, .done
+
+ ld a, [wKickCounter]
+ push af
+ call BattleCommand_LowerSub
+ pop af
+ ld [wKickCounter], a
+
+.done
+ call BattleCommand_MoveDelay
+ jp ResetTurn
diff --git a/engine/battle/move_effects/mist.asm b/engine/battle/move_effects/mist.asm
new file mode 100644
index 00000000..26fafdd2
--- /dev/null
+++ b/engine/battle/move_effects/mist.asm
@@ -0,0 +1,15 @@
+BattleCommand_Mist:
+; mist
+
+ ld a, BATTLE_VARS_SUBSTATUS4
+ call GetBattleVarAddr
+ bit SUBSTATUS_MIST, [hl]
+ jr nz, .already_mist
+ set SUBSTATUS_MIST, [hl]
+ call AnimateCurrentMove
+ ld hl, MistText
+ jp StdBattleTextbox
+
+.already_mist
+ call AnimateFailedMove
+ jp PrintButItFailed
diff --git a/engine/battle/move_effects/nightmare.asm b/engine/battle/move_effects/nightmare.asm
new file mode 100644
index 00000000..9354b15b
--- /dev/null
+++ b/engine/battle/move_effects/nightmare.asm
@@ -0,0 +1,37 @@
+BattleCommand_Nightmare:
+; nightmare
+
+; Can't hit an absent opponent.
+
+ call CheckHiddenOpponent
+ jr nz, .failed
+
+; Can't hit a substitute.
+
+ call CheckSubstituteOpp
+ jr nz, .failed
+
+; Only works on a sleeping opponent.
+
+ ld a, BATTLE_VARS_STATUS_OPP
+ call GetBattleVarAddr
+ and SLP
+ jr z, .failed
+
+; Bail if the opponent is already having a nightmare.
+
+ ld a, BATTLE_VARS_SUBSTATUS1_OPP
+ call GetBattleVarAddr
+ bit SUBSTATUS_NIGHTMARE, [hl]
+ jr nz, .failed
+
+; Otherwise give the opponent a nightmare.
+
+ set SUBSTATUS_NIGHTMARE, [hl]
+ call AnimateCurrentMove
+ ld hl, StartedNightmareText
+ jp StdBattleTextbox
+
+.failed
+ call AnimateFailedMove
+ jp PrintButItFailed
diff --git a/engine/battle/move_effects/pay_day.asm b/engine/battle/move_effects/pay_day.asm
new file mode 100644
index 00000000..5f857aea
--- /dev/null
+++ b/engine/battle/move_effects/pay_day.asm
@@ -0,0 +1,26 @@
+BattleCommand_PayDay:
+; payday
+
+ xor a
+ ld hl, wStringBuffer1
+ ld [hli], a
+
+ ldh a, [hBattleTurn]
+ and a
+ ld a, [wBattleMonLevel]
+ jr z, .ok
+ ld a, [wEnemyMonLevel]
+.ok
+
+ add a
+ ld hl, wPayDayMoney + 2
+ add [hl]
+ ld [hld], a
+ jr nc, .done
+ inc [hl]
+ dec hl
+ jr nz, .done
+ inc [hl]
+.done
+ ld hl, CoinsScatteredText
+ jp StdBattleTextbox
diff --git a/engine/battle/move_effects/perish_song.asm b/engine/battle/move_effects/perish_song.asm
new file mode 100644
index 00000000..1758b65a
--- /dev/null
+++ b/engine/battle/move_effects/perish_song.asm
@@ -0,0 +1,38 @@
+BattleCommand_PerishSong:
+; perishsong
+
+ ld hl, wPlayerSubStatus1
+ ld de, wEnemySubStatus1
+ bit SUBSTATUS_PERISH, [hl]
+ jr z, .ok
+
+ ld a, [de]
+ bit SUBSTATUS_PERISH, a
+ jr nz, .failed
+
+.ok
+ bit SUBSTATUS_PERISH, [hl]
+ jr nz, .enemy
+
+ set SUBSTATUS_PERISH, [hl]
+ ld a, 4
+ ld [wPlayerPerishCount], a
+
+.enemy
+ ld a, [de]
+ bit SUBSTATUS_PERISH, a
+ jr nz, .done
+
+ set SUBSTATUS_PERISH, a
+ ld [de], a
+ ld a, 4
+ ld [wEnemyPerishCount], a
+
+.done
+ call AnimateCurrentMove
+ ld hl, StartPerishText
+ jp StdBattleTextbox
+
+.failed
+ call AnimateFailedMove
+ jp PrintButItFailed
diff --git a/engine/battle/move_effects/present.asm b/engine/battle/move_effects/present.asm
new file mode 100644
index 00000000..360a4172
--- /dev/null
+++ b/engine/battle/move_effects/present.asm
@@ -0,0 +1,75 @@
+BattleCommand_Present:
+; present
+
+ call BattleCommand_Stab
+ ld a, [wTypeMatchup]
+ and a
+ jp z, AnimateFailedMove
+ ld a, [wAttackMissed]
+ and a
+ jp nz, AnimateFailedMove
+
+ push bc
+ call BattleRandom
+ ld b, a
+ ld hl, PresentPower
+ ld c, 0
+.next
+ ld a, [hli]
+ cp -1
+ jr z, .heal_effect
+ cp b
+ jr nc, .got_power
+ inc c
+ inc hl
+ jr .next
+
+.got_power
+ ld a, c
+ ld [wPresentPower], a
+ call AnimateCurrentMoveEitherSide
+ ld d, [hl]
+ pop bc
+ ret
+
+.heal_effect
+ pop bc
+ ld a, 3
+ ld [wPresentPower], a
+ call AnimateCurrentMove
+ call BattleCommand_SwitchTurn
+ ld hl, AICheckPlayerMaxHP
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_hp_fn_pointer
+ ld hl, AICheckEnemyMaxHP
+.got_hp_fn_pointer
+ ld a, BANK(AICheckPlayerMaxHP) ; aka BANK(AICheckEnemyMaxHP)
+ rst FarCall
+ jr c, .already_fully_healed
+
+ ld hl, GetQuarterMaxHP
+ call CallBattleCore
+ call BattleCommand_SwitchTurn
+ ld hl, RestoreHP
+ call CallBattleCore
+ call BattleCommand_SwitchTurn
+ ld hl, RegainedHealthText
+ call StdBattleTextbox
+ call BattleCommand_SwitchTurn
+ call UpdateOpponentInParty
+ jr .do_animation
+
+.already_fully_healed
+ call BattleCommand_SwitchTurn
+ ; check battle scene
+ ld a, [wOptions]
+ add a
+ jr nc, .do_animation
+ call AnimateFailedMove
+ ld hl, CantReceiveGiftText
+ call StdBattleTextbox
+.do_animation
+ jp EndMoveEffect
+
+INCLUDE "data/moves/present_power.asm"
diff --git a/engine/battle/move_effects/protect.asm b/engine/battle/move_effects/protect.asm
new file mode 100644
index 00000000..e66bce44
--- /dev/null
+++ b/engine/battle/move_effects/protect.asm
@@ -0,0 +1,75 @@
+BattleCommand_Protect:
+; protect
+ call ProtectChance
+ ret c
+
+ ld a, BATTLE_VARS_SUBSTATUS1
+ call GetBattleVarAddr
+ set SUBSTATUS_PROTECT, [hl]
+
+ call AnimateCurrentMove
+
+ ld hl, ProtectedItselfText
+ jp StdBattleTextbox
+
+ProtectChance:
+ ld de, wPlayerProtectCount
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .asm_37637
+ ld de, wEnemyProtectCount
+.asm_37637
+
+ call CheckOpponentWentFirst
+ jr nz, .failed
+
+; Can't have a substitute.
+
+ ld a, BATTLE_VARS_SUBSTATUS4
+ call GetBattleVar
+ bit SUBSTATUS_SUBSTITUTE, a
+ jr nz, .failed
+
+; Halve the chance of a successful Protect for each consecutive use.
+
+ ld b, $ff
+ ld a, [de]
+ ld c, a
+.loop
+ ld a, c
+ and a
+ jr z, .done
+ dec c
+
+ srl b
+ ld a, b
+ and a
+ jr nz, .loop
+ jr .failed
+.done
+
+.rand
+ call BattleRandom
+ and a
+ jr z, .rand
+
+ dec a
+ cp b
+ jr nc, .failed
+
+; Another consecutive Protect use.
+
+ ld a, [de]
+ inc a
+ ld [de], a
+
+ and a
+ ret
+
+.failed
+ xor a
+ ld [de], a
+ call AnimateFailedMove
+ call PrintButItFailed
+ scf
+ ret
diff --git a/engine/battle/move_effects/psych_up.asm b/engine/battle/move_effects/psych_up.asm
new file mode 100644
index 00000000..3473b9db
--- /dev/null
+++ b/engine/battle/move_effects/psych_up.asm
@@ -0,0 +1,47 @@
+BattleCommand_PsychUp:
+; psychup
+
+ ld hl, wEnemyStatLevels
+ ld de, wPlayerStatLevels
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .pointers_correct
+; It's the enemy's turn, so swap the pointers.
+ ld hl, wPlayerStatLevels
+ ld de, wEnemyStatLevels
+.pointers_correct
+ push hl
+ ld b, NUM_LEVEL_STATS
+; If any of the enemy's stats is modified from its base level,
+; the move succeeds. Otherwise, it fails.
+.loop
+ ld a, [hli]
+ cp BASE_STAT_LEVEL
+ jr nz, .break
+ dec b
+ jr nz, .loop
+ pop hl
+ call AnimateFailedMove
+ jp PrintButItFailed
+
+.break
+ pop hl
+ ld b, NUM_LEVEL_STATS
+.loop2
+ ld a, [hli]
+ ld [de], a
+ inc de
+ dec b
+ jr nz, .loop2
+ ldh a, [hBattleTurn]
+ and a
+ jr nz, .calc_enemy_stats
+ call CalcPlayerStats
+ jr .merge
+
+.calc_enemy_stats
+ call CalcEnemyStats
+.merge
+ call AnimateCurrentMove
+ ld hl, CopiedStatsText
+ jp StdBattleTextbox
diff --git a/engine/battle/move_effects/pursuit.asm b/engine/battle/move_effects/pursuit.asm
new file mode 100644
index 00000000..f8979fb9
--- /dev/null
+++ b/engine/battle/move_effects/pursuit.asm
@@ -0,0 +1,24 @@
+BattleCommand_Pursuit:
+; pursuit
+; Double damage if the opponent is switching.
+
+ ld hl, wEnemyIsSwitching
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .ok
+ ld hl, wPlayerIsSwitching
+.ok
+ ld a, [hl]
+ and a
+ ret z
+
+ ld hl, wCurDamage + 1
+ sla [hl]
+ dec hl
+ rl [hl]
+ ret nc
+
+ ld a, $ff
+ ld [hli], a
+ ld [hl], a
+ ret
diff --git a/engine/battle/move_effects/rage.asm b/engine/battle/move_effects/rage.asm
new file mode 100644
index 00000000..df206a6b
--- /dev/null
+++ b/engine/battle/move_effects/rage.asm
@@ -0,0 +1,6 @@
+BattleCommand_Rage:
+; rage
+ ld a, BATTLE_VARS_SUBSTATUS4
+ call GetBattleVarAddr
+ set SUBSTATUS_RAGE, [hl]
+ ret
diff --git a/engine/battle/move_effects/rain_dance.asm b/engine/battle/move_effects/rain_dance.asm
new file mode 100644
index 00000000..c22fb9fd
--- /dev/null
+++ b/engine/battle/move_effects/rain_dance.asm
@@ -0,0 +1,9 @@
+BattleCommand_StartRain:
+; startrain
+ ld a, WEATHER_RAIN
+ ld [wBattleWeather], a
+ ld a, 5
+ ld [wWeatherCount], a
+ call AnimateCurrentMove
+ ld hl, DownpourText
+ jp StdBattleTextbox
diff --git a/engine/battle/move_effects/rapid_spin.asm b/engine/battle/move_effects/rapid_spin.asm
new file mode 100644
index 00000000..eb396a35
--- /dev/null
+++ b/engine/battle/move_effects/rapid_spin.asm
@@ -0,0 +1,36 @@
+BattleCommand_ClearHazards:
+; clearhazards
+
+ ld a, BATTLE_VARS_SUBSTATUS4
+ call GetBattleVarAddr
+ bit SUBSTATUS_LEECH_SEED, [hl]
+ jr z, .not_leeched
+ res SUBSTATUS_LEECH_SEED, [hl]
+ ld hl, ShedLeechSeedText
+ call StdBattleTextbox
+.not_leeched
+
+ ld hl, wPlayerScreens
+ ld de, wPlayerWrapCount
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_screens_wrap
+ ld hl, wEnemyScreens
+ ld de, wEnemyWrapCount
+.got_screens_wrap
+ bit SCREENS_SPIKES, [hl]
+ jr z, .no_spikes
+ res SCREENS_SPIKES, [hl]
+ ld hl, BlewSpikesText
+ push de
+ call StdBattleTextbox
+ pop de
+.no_spikes
+
+ ld a, [de]
+ and a
+ ret z
+ xor a
+ ld [de], a
+ ld hl, ReleasedByText
+ jp StdBattleTextbox
diff --git a/engine/battle/move_effects/return.asm b/engine/battle/move_effects/return.asm
new file mode 100644
index 00000000..7c7c5fcb
--- /dev/null
+++ b/engine/battle/move_effects/return.asm
@@ -0,0 +1,25 @@
+BattleCommand_HappinessPower:
+; happinesspower
+ push bc
+ ld hl, wBattleMonHappiness
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .ok
+ ld hl, wEnemyMonHappiness
+.ok
+ xor a
+ ldh [hMultiplicand + 0], a
+ ldh [hMultiplicand + 1], a
+ ld a, [hl]
+ ldh [hMultiplicand + 2], a
+ ld a, 10
+ ldh [hMultiplier], a
+ call Multiply
+ ld a, 25
+ ldh [hDivisor], a
+ ld b, 4
+ call Divide
+ ldh a, [hQuotient + 3]
+ ld d, a
+ pop bc
+ ret
diff --git a/engine/battle/move_effects/rollout.asm b/engine/battle/move_effects/rollout.asm
new file mode 100644
index 00000000..e2f810e6
--- /dev/null
+++ b/engine/battle/move_effects/rollout.asm
@@ -0,0 +1,95 @@
+MAX_ROLLOUT_COUNT EQU 5
+
+BattleCommand_CheckCurl:
+; checkcurl
+
+ ld de, wPlayerRolloutCount
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .ok
+ ld de, wEnemyRolloutCount
+.ok
+ ld a, BATTLE_VARS_SUBSTATUS1
+ call GetBattleVar
+ bit SUBSTATUS_ROLLOUT, a
+ jr z, .reset
+
+ ld b, $4 ; doturn
+ jp SkipToBattleCommand
+
+.reset
+ xor a
+ ld [de], a
+ ret
+
+BattleCommand_RolloutPower:
+; rolloutpower
+
+ ld a, BATTLE_VARS_STATUS
+ call GetBattleVar
+ and SLP
+ ret nz
+
+ ld hl, wPlayerRolloutCount
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_rollout_count
+ ld hl, wEnemyRolloutCount
+
+.got_rollout_count
+ ld a, [hl]
+ and a
+ jr nz, .skip_set_rampage
+ ld a, 1
+ ld [wSomeoneIsRampaging], a
+
+.skip_set_rampage
+ ld a, [wAttackMissed]
+ and a
+ jr z, .hit
+
+ ld a, BATTLE_VARS_SUBSTATUS1
+ call GetBattleVarAddr
+ res 6, [hl]
+ ret
+
+.hit
+ inc [hl]
+ ld a, [hl]
+ ld b, a
+ cp MAX_ROLLOUT_COUNT
+ jr c, .not_done_with_rollout
+
+ ld a, BATTLE_VARS_SUBSTATUS1
+ call GetBattleVarAddr
+ res SUBSTATUS_ROLLOUT, [hl]
+ jr .done_with_substatus_flag
+
+.not_done_with_rollout
+ ld a, BATTLE_VARS_SUBSTATUS1
+ call GetBattleVarAddr
+ set SUBSTATUS_ROLLOUT, [hl]
+
+.done_with_substatus_flag
+ ld a, BATTLE_VARS_SUBSTATUS2
+ call GetBattleVar
+ bit SUBSTATUS_CURLED, a
+ jr z, .not_curled
+ inc b
+.not_curled
+.loop
+ dec b
+ jr z, .done_damage
+
+ ld hl, wCurDamage + 1
+ sla [hl]
+ dec hl
+ rl [hl]
+ jr nc, .loop
+
+ ld a, $ff
+ ld [hli], a
+ ld [hl], a
+
+.done_damage
+ ret
diff --git a/engine/battle/move_effects/safeguard.asm b/engine/battle/move_effects/safeguard.asm
new file mode 100644
index 00000000..e64e8092
--- /dev/null
+++ b/engine/battle/move_effects/safeguard.asm
@@ -0,0 +1,23 @@
+BattleCommand_Safeguard:
+; safeguard
+
+ ld hl, wPlayerScreens
+ ld de, wPlayerSafeguardCount
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .ok
+ ld hl, wEnemyScreens
+ ld de, wEnemySafeguardCount
+.ok
+ bit SCREENS_SAFEGUARD, [hl]
+ jr nz, .failed
+ set SCREENS_SAFEGUARD, [hl]
+ ld a, 5
+ ld [de], a
+ call AnimateCurrentMove
+ ld hl, CoveredByVeilText
+ jp StdBattleTextbox
+
+.failed
+ call AnimateFailedMove
+ jp PrintButItFailed
diff --git a/engine/battle/move_effects/sandstorm.asm b/engine/battle/move_effects/sandstorm.asm
new file mode 100644
index 00000000..c88529fb
--- /dev/null
+++ b/engine/battle/move_effects/sandstorm.asm
@@ -0,0 +1,18 @@
+BattleCommand_StartSandstorm:
+; startsandstorm
+
+ ld a, [wBattleWeather]
+ cp WEATHER_SANDSTORM
+ jr z, .failed
+
+ ld a, WEATHER_SANDSTORM
+ ld [wBattleWeather], a
+ ld a, 5
+ ld [wWeatherCount], a
+ call AnimateCurrentMove
+ ld hl, SandstormBrewedText
+ jp StdBattleTextbox
+
+.failed
+ call AnimateFailedMove
+ jp PrintButItFailed
diff --git a/engine/battle/move_effects/selfdestruct.asm b/engine/battle/move_effects/selfdestruct.asm
new file mode 100644
index 00000000..b4967b03
--- /dev/null
+++ b/engine/battle/move_effects/selfdestruct.asm
@@ -0,0 +1,30 @@
+BattleCommand_Selfdestruct:
+ ld a, BATTLEANIM_PLAYER_DAMAGE
+ ld [wNumHits], a
+ ld c, 3
+ call DelayFrames
+ ld a, BATTLE_VARS_STATUS
+ call GetBattleVarAddr
+ xor a
+ ld [hli], a
+ inc hl
+ ld [hli], a
+ ld [hl], a
+ ld a, $1
+ ld [wKickCounter], a
+ call BattleCommand_LowerSub
+ call LoadMoveAnim
+ ld a, BATTLE_VARS_SUBSTATUS4
+ call GetBattleVarAddr
+ res SUBSTATUS_LEECH_SEED, [hl]
+ ld a, BATTLE_VARS_SUBSTATUS5_OPP
+ call GetBattleVarAddr
+ res SUBSTATUS_DESTINY_BOND, [hl]
+ ; check battle scene
+ ld a, [wOptions]
+ add a
+ ret nc
+ farcall DrawPlayerHUD
+ farcall DrawEnemyHUD
+ call WaitBGMap
+ jp RefreshBattleHuds
diff --git a/engine/battle/move_effects/spikes.asm b/engine/battle/move_effects/spikes.asm
new file mode 100644
index 00000000..96163a68
--- /dev/null
+++ b/engine/battle/move_effects/spikes.asm
@@ -0,0 +1,26 @@
+BattleCommand_Spikes:
+; spikes
+
+ ld hl, wEnemyScreens
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .asm_3768e
+ ld hl, wPlayerScreens
+.asm_3768e
+
+; Fails if spikes are already down!
+
+ bit SCREENS_SPIKES, [hl]
+ jr nz, .failed
+
+; Nothing else stops it from working.
+
+ set SCREENS_SPIKES, [hl]
+
+ call AnimateCurrentMove
+
+ ld hl, SpikesText
+ jp StdBattleTextbox
+
+.failed
+ jp FailMove
diff --git a/engine/battle/move_effects/splash.asm b/engine/battle/move_effects/splash.asm
new file mode 100644
index 00000000..1be307f8
--- /dev/null
+++ b/engine/battle/move_effects/splash.asm
@@ -0,0 +1,3 @@
+BattleCommand_Splash:
+ call AnimateCurrentMove
+ jp PrintNothingHappened
diff --git a/engine/battle/move_effects/substitute.asm b/engine/battle/move_effects/substitute.asm
new file mode 100644
index 00000000..c5ab8741
--- /dev/null
+++ b/engine/battle/move_effects/substitute.asm
@@ -0,0 +1,90 @@
+BattleCommand_Substitute:
+; substitute
+
+ call BattleCommand_MoveDelay
+ ld hl, wBattleMonMaxHP
+ ld de, wPlayerSubstituteHP
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_hp
+ ld hl, wEnemyMonMaxHP
+ ld de, wEnemySubstituteHP
+.got_hp
+
+ ld a, BATTLE_VARS_SUBSTATUS4
+ call GetBattleVar
+ bit SUBSTATUS_SUBSTITUTE, a
+ jr nz, .already_has_sub
+
+ ld a, [hli]
+ ld b, [hl]
+ srl a
+ rr b
+ srl a
+ rr b
+ dec hl
+ dec hl
+ ld a, b
+ ld [de], a
+ ld a, [hld]
+ sub b
+ ld e, a
+ ld a, [hl]
+ sbc 0
+ ld d, a
+ jr c, .too_weak_to_sub
+ ld a, d
+ or e
+ jr z, .too_weak_to_sub
+ ld [hl], d
+ inc hl
+ ld [hl], e
+
+ ld a, BATTLE_VARS_SUBSTATUS4
+ call GetBattleVarAddr
+ set SUBSTATUS_SUBSTITUTE, [hl]
+
+ ld hl, wPlayerWrapCount
+ ld de, wPlayerTrappingMove
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .player
+ ld hl, wEnemyWrapCount
+ ld de, wEnemyTrappingMove
+.player
+
+ xor a
+ ld [hl], a
+ ld [de], a
+ ; check battle scene
+ ld a, [wOptions]
+ add a
+ jr c, .no_anim
+
+ xor a
+ ld [wNumHits], a
+ ld [wFXAnimID + 1], a
+ ld [wKickCounter], a
+ ld a, SUBSTITUTE
+ call LoadAnim
+ jr .finish
+
+.no_anim
+ call BattleCommand_RaiseSubNoAnim
+.finish
+ ld hl, MadeSubstituteText
+ call StdBattleTextbox
+ jp RefreshBattleHuds
+
+.already_has_sub
+ call CheckUserIsCharging
+ call nz, BattleCommand_RaiseSub
+ ld hl, HasSubstituteText
+ jr .jp_stdbattletextbox
+
+.too_weak_to_sub
+ call CheckUserIsCharging
+ call nz, BattleCommand_RaiseSub
+ ld hl, TooWeakSubText
+.jp_stdbattletextbox
+ jp StdBattleTextbox
diff --git a/engine/battle/move_effects/sunny_day.asm b/engine/battle/move_effects/sunny_day.asm
new file mode 100644
index 00000000..0edc38e4
--- /dev/null
+++ b/engine/battle/move_effects/sunny_day.asm
@@ -0,0 +1,9 @@
+BattleCommand_StartSun:
+; startsun
+ ld a, WEATHER_SUN
+ ld [wBattleWeather], a
+ ld a, 5
+ ld [wWeatherCount], a
+ call AnimateCurrentMove
+ ld hl, SunGotBrightText
+ jp StdBattleTextbox
diff --git a/engine/battle/move_effects/teleport.asm b/engine/battle/move_effects/teleport.asm
new file mode 100644
index 00000000..7d866ccf
--- /dev/null
+++ b/engine/battle/move_effects/teleport.asm
@@ -0,0 +1,87 @@
+BattleCommand_Teleport:
+; teleport
+
+ ld a, [wBattleType]
+ cp BATTLETYPE_SHINY
+ jr z, .failed
+ cp BATTLETYPE_TRAP
+ jr z, .failed
+
+ ld a, BATTLE_VARS_SUBSTATUS5_OPP
+ call GetBattleVar
+ bit SUBSTATUS_CANT_RUN, a
+ jr nz, .failed
+; Only need to check these next things if it's your turn
+ ldh a, [hBattleTurn]
+ and a
+ jr nz, .enemy_turn
+; Can't teleport from a trainer battle
+ ld a, [wBattleMode]
+ dec a
+ jr nz, .failed
+; If your level is greater than the opponent's, you run without fail.
+ ld a, [wCurPartyLevel]
+ ld b, a
+ ld a, [wBattleMonLevel]
+ cp b
+ jr nc, .run_away
+; Generate a number between 0 and (YourLevel + TheirLevel).
+ add b
+ ld c, a
+ inc c
+.loop_player
+ call BattleRandom
+ cp c
+ jr nc, .loop_player
+; If that number is greater than 4 times your level, run away.
+ srl b
+ srl b
+ cp b
+ jr nc, .run_away
+
+.failed
+ call AnimateFailedMove
+ jp PrintButItFailed
+
+.enemy_turn
+ ld a, [wBattleMode]
+ dec a
+ jr nz, .failed
+ ld a, [wBattleMonLevel]
+ ld b, a
+ ld a, [wCurPartyLevel]
+ cp b
+ jr nc, .run_away
+ add b
+ ld c, a
+ inc c
+.loop_enemy
+ call BattleRandom
+ cp c
+ jr nc, .loop_enemy
+ srl b
+ srl b
+ cp b
+ ; This should be jr c, .failed
+ ; As written, it makes enemy use of Teleport always succeed if able
+ jr nc, .run_away
+.run_away
+ call UpdateBattleMonInParty
+ xor a
+ ld [wNumHits], a
+ inc a
+ ld [wForcedSwitch], a
+ ; set battle draw
+ inc a
+ ld [wBattleResult], a
+ ld a, 1
+ ld [wKickCounter], a
+ call BattleCommand_LowerSub
+ call LoadMoveAnim
+ ld c, 20
+ call DelayFrames
+ ld a, DRAW
+ ld [wBattleResult], a
+
+ ld hl, FledFromBattleText
+ jp StdBattleTextbox
diff --git a/engine/battle/move_effects/thief.asm b/engine/battle/move_effects/thief.asm
new file mode 100644
index 00000000..e588c5ff
--- /dev/null
+++ b/engine/battle/move_effects/thief.asm
@@ -0,0 +1,112 @@
+BattleCommand_Thief:
+; thief
+
+ ldh a, [hBattleTurn]
+ and a
+ jr nz, .enemy
+
+; The player needs to be able to steal an item.
+
+ call .playeritem
+ ld a, [hl]
+ and a
+ ret nz
+
+; The enemy needs to have an item to steal.
+
+ call .enemyitem
+ ld a, [hl]
+ and a
+ ret z
+
+; Can't steal mail.
+
+ ld [wNamedObjectIndexBuffer], a
+ ld d, a
+ farcall ItemIsMail
+ ret c
+
+ ld a, [wEffectFailed]
+ and a
+ ret nz
+
+ ld a, [wLinkMode]
+ and a
+ jr z, .stealenemyitem
+
+ ld a, [wBattleMode]
+ dec a
+ ret z
+
+.stealenemyitem
+ call .enemyitem
+ xor a
+ ld [hl], a
+ ld [de], a
+
+ call .playeritem
+ ld a, [wNamedObjectIndexBuffer]
+ ld [hl], a
+ ld [de], a
+ jr .stole
+
+.enemy
+
+; The enemy can't already have an item.
+
+ call .enemyitem
+ ld a, [hl]
+ and a
+ ret nz
+
+; The player must have an item to steal.
+
+ call .playeritem
+ ld a, [hl]
+ and a
+ ret z
+
+; Can't steal mail!
+
+ ld [wNamedObjectIndexBuffer], a
+ ld d, a
+ farcall ItemIsMail
+ ret c
+
+ ld a, [wEffectFailed]
+ and a
+ ret nz
+
+; If the enemy steals your item,
+; it's gone for good if you don't get it back.
+
+ call .playeritem
+ xor a
+ ld [hl], a
+ ld [de], a
+
+ call .enemyitem
+ ld a, [wNamedObjectIndexBuffer]
+ ld [hl], a
+ ld [de], a
+
+.stole
+ call GetItemName
+ ld hl, StoleText
+ jp StdBattleTextbox
+
+.playeritem
+ ld a, 1
+ call BattlePartyAttr
+ ld d, h
+ ld e, l
+ ld hl, wBattleMonItem
+ ret
+
+.enemyitem
+ ld a, 1
+ call OTPartyAttr
+ ld d, h
+ ld e, l
+ ld hl, wEnemyMonItem
+ ret
diff --git a/engine/battle/move_effects/thunder.asm b/engine/battle/move_effects/thunder.asm
new file mode 100644
index 00000000..b2a64378
--- /dev/null
+++ b/engine/battle/move_effects/thunder.asm
@@ -0,0 +1,18 @@
+BattleCommand_ThunderAccuracy:
+; thunderaccuracy
+
+ ld a, BATTLE_VARS_MOVE_TYPE
+ call GetBattleVarAddr
+ inc hl
+ ld a, [wBattleWeather]
+ cp WEATHER_RAIN
+ jr z, .rain
+ cp WEATHER_SUN
+ ret nz
+ ld [hl], 50 percent + 1
+ ret
+
+.rain
+ ; Redundant with CheckHit guranteeing hit
+ ld [hl], 100 percent
+ ret
diff --git a/engine/battle/move_effects/transform.asm b/engine/battle/move_effects/transform.asm
new file mode 100644
index 00000000..0247d8bb
--- /dev/null
+++ b/engine/battle/move_effects/transform.asm
@@ -0,0 +1,155 @@
+BattleCommand_Transform:
+; transform
+
+ call ClearLastMove
+ ld a, BATTLE_VARS_SUBSTATUS5_OPP
+ call GetBattleVarAddr
+ bit SUBSTATUS_TRANSFORMED, [hl]
+ jp nz, BattleEffect_ButItFailed
+ call CheckHiddenOpponent
+ jp nz, BattleEffect_ButItFailed
+ xor a
+ ld [wNumHits], a
+ ld [wFXAnimID + 1], a
+ ld a, $1
+ ld [wKickCounter], a
+ ld a, BATTLE_VARS_SUBSTATUS4
+ call GetBattleVarAddr
+ bit SUBSTATUS_SUBSTITUTE, [hl]
+ push af
+ jr z, .mimic_substitute
+ call CheckUserIsCharging
+ jr nz, .mimic_substitute
+ ld a, SUBSTITUTE
+ call LoadAnim
+.mimic_substitute
+ ld a, BATTLE_VARS_SUBSTATUS5
+ call GetBattleVarAddr
+ set SUBSTATUS_TRANSFORMED, [hl]
+ call ResetActorDisable
+ ld hl, wBattleMonSpecies
+ ld de, wEnemyMonSpecies
+ ldh a, [hBattleTurn]
+ and a
+ jr nz, .got_mon_species
+ ld hl, wEnemyMonSpecies
+ ld de, wBattleMonSpecies
+ xor a
+ ld [wCurMoveNum], a
+.got_mon_species
+ push hl
+ ld a, [hli]
+ ld [de], a
+ inc hl
+ inc de
+ inc de
+ ld bc, NUM_MOVES
+ call CopyBytes
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .mimic_enemy_backup
+ ld a, [de]
+ ld [wEnemyBackupDVs], a
+ inc de
+ ld a, [de]
+ ld [wEnemyBackupDVs + 1], a
+ dec de
+.mimic_enemy_backup
+; copy DVs
+ ld a, [hli]
+ ld [de], a
+ inc de
+ ld a, [hli]
+ ld [de], a
+ inc de
+; move pointer to stats
+ ld bc, wBattleMonStats - wBattleMonPP
+ add hl, bc
+ push hl
+ ld h, d
+ ld l, e
+ add hl, bc
+ ld d, h
+ ld e, l
+ pop hl
+ ld bc, wBattleMonStructEnd - wBattleMonStats
+ call CopyBytes
+; init the power points
+ ld bc, wBattleMonMoves - wBattleMonStructEnd
+ add hl, bc
+ push de
+ ld d, h
+ ld e, l
+ pop hl
+ ld bc, wBattleMonPP - wBattleMonStructEnd
+ add hl, bc
+ ld b, NUM_MOVES
+.pp_loop
+ ld a, [de]
+ inc de
+ and a
+ jr z, .done_move
+ cp SKETCH
+ ld a, 1
+ jr z, .done_move
+ ld a, 5
+.done_move
+ ld [hli], a
+ dec b
+ jr nz, .pp_loop
+ pop hl
+ ld a, [hl]
+ ld [wNamedObjectIndexBuffer], a
+ call GetPokemonName
+ ld hl, wEnemyStats
+ ld de, wPlayerStats
+ ld bc, 2 * 5
+ call BattleSideCopy
+ ld hl, wEnemyStatLevels
+ ld de, wPlayerStatLevels
+ ld bc, 8
+ call BattleSideCopy
+ ; check battle scene
+ ld a, [wOptions]
+ add a
+ jr c, .mimic_anims
+ ldh a, [hBattleTurn]
+ and a
+ ld a, [wPlayerMinimized]
+ jr z, .got_byte
+ ld a, [wEnemyMinimized]
+.got_byte
+ and a
+ jr nz, .mimic_anims
+ call LoadMoveAnim
+ jr .after_anim
+
+.mimic_anims
+ call BattleCommand_MoveDelay
+ call BattleCommand_RaiseSubNoAnim
+.after_anim
+ xor a
+ ld [wNumHits], a
+ ld [wFXAnimID + 1], a
+ ld a, $2
+ ld [wKickCounter], a
+ pop af
+ ld a, SUBSTITUTE
+ call nz, LoadAnim
+ ld hl, TransformedText
+ jp StdBattleTextbox
+
+BattleSideCopy:
+; Copy bc bytes from hl to de if it's the player's turn.
+; Copy bc bytes from de to hl if it's the enemy's turn.
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .copy
+
+; Swap hl and de
+ push hl
+ ld h, d
+ ld l, e
+ pop de
+.copy
+ jp CopyBytes
diff --git a/engine/items/item_effects.asm b/engine/items/item_effects.asm
index 46713dfe..bf35d84e 100755
--- a/engine/items/item_effects.asm
+++ b/engine/items/item_effects.asm
@@ -443,7 +443,7 @@ UltraBall: ; e926
.not_ditto
set SUBSTATUS_TRANSFORMED, [hl]
- ld hl, wcbd0
+ ld hl, wEnemyBackupDVs
ld a, [wEnemyMonDVs]
ld [hli], a
ld a, [wEnemyMonMovesEnd + 1]
@@ -1975,7 +1975,7 @@ PokeDoll: ; f4e5 (3:74e5)
dec a
jr nz, .asm_f4f6
inc a
- ld [wd11c], a
+ ld [wForcedSwitch], a
inc a
ld [wBattleResult], a
jp Functionf7d0
diff --git a/main.asm b/main.asm
index 6d5e031e..6a3f7ef5 100644
--- a/main.asm
+++ b/main.asm
@@ -280,85 +280,6 @@ SECTION "Effect Commands", ROMX
INCLUDE "engine/battle/effect_commands.asm"
-UpdateMoveData:
- dr $35f7c, $36201
-
-Defrost:
- dr $36201, $36308
-BattleCommand_StatUp:
- dr $36308, $36313
-RaiseStat:
- dr $36313, $364d7
-BattleCommand_StatUpMessage:
- dr $364d7, $3656b
-BattleCommand_StatUpFailText:
- dr $3656b, $366ce
-BattleCommand_RaiseSubNoAnim:
- dr $366ce, $366e2
-BattleCommand_LowerSubNoAnim:
- dr $366e2, $366f6
-CalcPlayerStats:
- dr $366f6, $3671c
-CalcEnemyStats:
- dr $3671c, $36bcd
-CheckOpponentWentFirst:
- dr $36bcd, $373dc
-
-ClearLastMove:
- dr $373dc, $37441
-PrintDoesntAffect:
- dr $37441, $37447
-PrintNothingHappened:
- dr $37447, $3744d
-TryPrintButItFailed:
- dr $3744d, $37452
-PrintButItFailed:
- dr $37452, $37458
-FailMove:
- dr $37458, $37464
-PrintDidntAffect:
- dr $37464, $3746a
-PrintDidntAffect2:
- dr $3746a, $3747c
-CheckSubstituteOpp:
- dr $3747c, $3757a
-ResetTurn:
- dr $3757a, $378bd
-ResetFuryCutterCount:
- dr $378bd, $378f4
-CheckOppositeGender:
- dr $378f4, $37e7d
-GetUserItem:
- dr $37e7d, $37e8c
-GetOpponentItem:
- dr $37e8c, $37e9b
-GetItemHeldEffect:
- dr $37e9b, $37ecc
-AnimateCurrentMove:
- dr $37ecc, $37f01
-LoadMoveAnim:
- dr $37f01, $37f0f
-LoadAnim:
- dr $37f0f, $37f3e
-CallBattleCore:
- dr $37f3e, $37f42
-AnimateFailedMove:
- dr $37f42, $37f4b
-BattleCommand_MoveDelay:
- dr $37f4b, $37f57
-SkipToBattleCommand:
- dr $37f57, $37f6c
-GetMoveAttr:
- dr $37f6c, $37f78
-GetMoveData:
- dr $37f78, $37f86
-GetMoveByte:
- dr $37f86, $37f92
-AppearUserLowerSub:
- dr $37f92, $37f99
-AppearUserRaiseSub:
- dr $37f99, $37fa0
-
SECTION "Enemy Trainers", ROMX
@@ -373,10 +294,32 @@ SECTION "Battle Core", ROMX
FleeMons::
dr $3c551, $3c5a4
GetMoveEffect:
- dr $3c5a4, $3d39f
-
+ dr $3c5a4, $3cbe7
+SubtractHPFromUser:
+ dr $3cbe7, $3cc2b
+GetEighthMaxHP:
+ dr $3cc2b, $3cc36
+GetQuarterMaxHP:
+ dr $3cc36, $3cc47
+GetHalfMaxHP:
+ dr $3cc47, $3cc54
+GetMaxHP:
+ dr $3cc54, $3cc86
+CheckUserHasEnoughHP:
+ dr $3cc86, $3cc97
+RestoreHP:
+ dr $3cc97, $3d224
+
+SetUpBattlePartyMenu_NoLoop:
+ dr $3d224, $3d28f
+ForcePickSwitchMonInBattle:
+ dr $3d28f, $3d381
+ForceEnemySwitch:
+ dr $3d381, $3d39f
EnemySwitch:
- dr $3d39f, $3d438
+ dr $3d39f, $3d3d5
+EnemySwitch_SetMode:
+ dr $3d3d5, $3d438
ResetBattleParticipants:
dr $3d438, $3d6cb
NewEnemyMonStatus:
@@ -388,27 +331,57 @@ CheckPlayerPartyForFitMon::
Function3d8f5:
dr $3d8f5, $3d907
Function3d907:
- dr $3d907, $3dabc
+ dr $3d907, $3d9a2
+SwitchPlayerMon:
+ dr $3d9a2, $3da84
+SpikesDamage:
+ dr $3da84, $3dabc
PursuitSwitch:
- dr $3dabc, $3dda9
+ dr $3dabc, $3dc4a
+UseHeldStatusHealingItem:
+ dr $3dc4a, $3dcb2
+UseConfusionHealingItem:
+ dr $3dcb2, $3dda9
UpdatePlayerHUD::
- dr $3dda9, $3de97
+ dr $3dda9, $3ddb9
+DrawPlayerHUD:
+ dr $3ddb9, $3de97
UpdateEnemyHUD::
- dr $3de97, $3e6e8
+ dr $3de97, $3dea4
+DrawEnemyHUD:
+ dr $3dea4, $3e290
+PassedBattleMonEntrance:
+ dr $3e290, $3e6e8
CheckEnemyLockedIn::
- dr $3e6e8, $3e74b
+ dr $3e6e8, $3e6fb
+LinkBattleSendReceiveAction:
+ dr $3e6fb, $3e74b
LoadEnemyMon:
- dr $3e74b, $3ec11
+ dr $3e74b, $3ea77
+ApplyPrzEffectOnSpeed:
+ dr $3ea77, $3eab4
+ApplyBrnEffectOnAttack:
+ dr $3eab4, $3eae9
+ApplyStatLevelMultiplierOnAllStats:
+ dr $3eae9, $3eb83
+BadgeStatBoosts:
+ dr $3eb83, $3ebd8
+_LoadBattleFontsHPBar:
+ dr $3ebd8, $3ec11
_BattleRandom::
dr $3ec11, $3f196
FillInExpBar::
dr $3f196, $3f243
GetBattleMonBackpic::
- dr $3f243, $3f282
+ dr $3f243, $3f24d
+DropPlayerSub:
+ dr $3f24d, $3f282
GetEnemyMonFrontpic::
- dr $3f282, $3f2c7
+ dr $3f282, $3f28c
+DropEnemySub:
+ dr $3f28c, $3f2c7
StartBattle::
dr $3f2c7, $3f55d
Function3f55d:
@@ -843,7 +816,13 @@ CheckMagikarpLength:
MagikarpHouseSign:
dr $fbdd6, $fbdf1
HiddenPowerDamage:
- dr $fbdf1, $fbeaa
+ dr $fbdf1, $fbe5a
+_DisappearUser:
+ dr $fbe5a, $fbe6f
+_AppearUserRaiseSub:
+ dr $fbe6f, $fbe77
+_AppearUserLowerSub:
+ dr $fbe77, $fbeaa
DoWeatherModifiers:
dr $fbeaa, $fbf2b
DoBadgeTypeBoosts:
diff --git a/wram.asm b/wram.asm
index 97a827e2..27306268 100644
--- a/wram.asm
+++ b/wram.asm
@@ -1692,12 +1692,12 @@ wEnemySubStatus4:: ds 1 ; cb4e
wEnemySubStatus5:: ds 1 ; cb4f
wPlayerRolloutCount:: db ; cb50
wPlayerConfuseCount:: db ; cb51
-wcb52:: ds 1 ; cb52
+wPlayerToxicCount:: db ; cb52
wPlayerDisableCount:: db ; cb53
wPlayerEncoreCount:: db ; cb54
-wcb55:: ds 1 ; cb55
+wPlayerPerishCount:: db ; cb55
wPlayerFuryCutterCount:: db ; cb56
-wcb57:: ds 1 ; cb57
+wPlayerProtectCount:: db ; cb57
wEnemyRolloutCount:: db ; cb58
wEnemyConfuseCount:: db ; cb59
wEnemyToxicCount:: db ; cb5a
@@ -1716,9 +1716,10 @@ wBattleReward::
wcb64:: ds 1 ; cb64
wcb65:: ds 1 ; cb65
wcb66:: ds 1 ; cb66
-wKickCounter:: ; cb67
wBattleAnimParam::
- db
+wKickCounter::
+wPresentPower::
+ db ; cb67
wBattleScriptBuffer:: db ; cb68
wcb69:: ds 1 ; cb69
wcb6a:: ds 1 ; cb6a
@@ -1780,6 +1781,8 @@ wEnemySpAtk:: dw
wEnemySpDef:: dw
ds 1
+wPlayerStatLevels:: ; cbaa
+; 07 neutral
wPlayerAtkLevel:: db ; cbaa
wPlayerDefLevel:: db ; cbab
wPlayerSpdLevel:: db ; cbac
@@ -1788,6 +1791,10 @@ wPlayerSDefLevel:: db ; cbae
wPlayerAccLevel:: db ; cbaf
wPlayerEvaLevel:: db ; cbb0
ds 1
+wPlayerStatLevelsEnd::
+
+wEnemyStatLevels:: ; cbb2
+; 07 neutral
wEnemyAtkLevel:: db ; cbb2
wEnemyDefLevel:: db ; cbb3
wEnemySpdLevel:: db ; cbb4
@@ -1796,6 +1803,8 @@ wEnemySDefLevel:: db ; cbb6
wEnemyAccLevel:: db ; cbb7
wEnemyEvaLevel:: db ; cbb8
ds 1
+wEnemyStatLevelsEnd::
+
wEnemyTurnsTaken:: db ; cbba
wPlayerTurnsTaken:: db ; cbbb
wcbbc:: ds 1 ; cbbc
@@ -1817,8 +1826,7 @@ wPayDayMoney:: ds 3 ; cbca
wcbcd:: ds 1 ; cbcd
wcbce:: ds 1 ; cbce
wcbcf:: ds 1 ; cbcf
-wcbd0:: ds 1 ; cbd0
-wcbd1:: ds 1 ; cbd1
+wEnemyBackupDVs:: dw
wAlreadyDisobeyed:: db ; cbd2
wDisabledMove:: db ; cbd3
wEnemyDisabledMove:: db ; cbd4
@@ -1828,29 +1836,29 @@ wcbd5:: ds 1 ; cbd5
wLastPlayerCounterMove:: db ; cbd6
wLastEnemyCounterMove:: db ; cbd7
-wcbd8:: ds 1 ; cbd8
+wEnemyMinimized:: db ; cbd8
wAlreadyFailed:: db ; cbd9
wcbda:: ds 1 ; cbda
wcbdb:: ds 1 ; cbdb
wPlayerMinimized:: db ; cbdc
wPlayerScreens:: db ; cbdd
wEnemyScreens:: db ; cbde
-wcbdf:: ds 1 ; cbdf
-wcbe0:: ds 1 ; cbe0
+wPlayerSafeguardCount:: db ; cbdf
+wPlayerLightScreenCount:: db ; cbe0
wcbe1:: ds 1 ; cbe1
wcbe2:: ds 1 ; cbe2
-wcbe3:: ds 1 ; cbe3
-wcbe4:: ds 1 ; cbe4
+wEnemySafeguardCount:: db ; cbe3
+wEnemyLightScreenCount:: db ; cbe4
wcbe5:: ds 1 ; cbe5
wcbe6:: ds 1 ; cbe6
wcbe7:: ds 1 ; cbe7
wBattleWeather:: db ; cbe8
-wcbe9:: ds 1 ; cbe9
-wcbea:: ds 1 ; cbea
+wWeatherCount:: db ; cbe9
+wLoweredStat:: db ; cbea
wEffectFailed:: db ; cbeb
-wcbec:: ds 1 ; cbec
+wFailedMessage:: db ; cbec
wEnemyGoesFirst:: db ; cbed
-wcbee:: ds 1 ; cbee
+wPlayerIsSwitching:: db ; cbee
wEnemyIsSwitching:: db ; cbef
wPlayerUsedMoves:: ; cbf0
; add a move that has been used once by the player
@@ -1864,8 +1872,8 @@ wcbf7:: ds 1 ; cbf7
wcbf8:: ds 1 ; cbf8
wLastPlayerMove:: ds 1 ; cbf9
wLastEnemyMove:: ds 1 ; cbfa
-wcbfb:: ds 1 ; cbfb
-wcbfc:: ds 1 ; cbfc
+wPlayerFutureSightCount:: db ; cbfb
+wEnemyFutureSightCount:: db ; cbfc
wcbfd:: ds 1 ; cbfd
wcbfe:: ds 1 ; cbfe
wcbff:: ds 1 ; cbff
@@ -1874,15 +1882,13 @@ wcc01:: ds 1 ; cc01
wcc02:: ds 1 ; cc02
wcc03:: ds 1 ; cc03
wcc04:: ds 1 ; cc04
-wcc05:: ds 1 ; cc05
-wcc06:: ds 1 ; cc06
-wcc07:: ds 1 ; cc07
-wcc08:: ds 1 ; cc08
+wPlayerFutureSightDamage:: dw ; cc05
+wEnemyFutureSightDamage:: dw ; cc07
wPlayerRageCounter:: db ; cc09
wEnemyRageCounter:: db ; cc0a
wBeatUpHitAtLeastOnce:: db ; cc0b
-wcc0c:: ds 1 ; cc0c
-wcc0d:: ds 1 ; cc0d
+wPlayerTrappingMove:: db ; cc0c
+wEnemyTrappingMove:: db ; cc0d
wPlayerWrapCount:: db ; cc0e
wEnemyWrapCount:: db ; cc0f
wPlayerCharging:: db ; cc10
@@ -1895,8 +1901,8 @@ wWildMonPP:: ds NUM_MOVES ; cc17
wcc1b:: ds 1 ; cc1b
wSomeoneIsRampaging:: db ; cc1c
-wcc1d:: ds 1 ; cc1d
-wcc1e:: ds 1 ; cc1e
+wPlayerJustGotFrozen:: db ; cc1d
+wEnemyJustGotFrozen:: db ; cc1e
wcc1f:: ds 1 ; cc1f
ENDU ; cc20
@@ -2847,12 +2853,12 @@ wOtherTrainerClass:: ; d118
; 0 if opponent is a wild Pokémon, not a trainer
db
-wBattleType:: ds 1 ; d119
+wBattleType:: db ; d119
wd11a:: ds 1 ; d11a
wOtherTrainerID:: db ; d11b
-wd11c:: ds 1 ; d11c
-wTrainerClass:: ds 1 ; d11d
-wUnownLetter:: ds 1 ; d11e
+wForcedSwitch:: db ; d11c
+wTrainerClass:: db ; d11d
+wUnownLetter:: db ; d11e
wd11f:: ds 1 ; d11f
wBaseDexNo:: ; d120