summaryrefslogtreecommitdiff
path: root/engine/battle/move_effects
diff options
context:
space:
mode:
Diffstat (limited to 'engine/battle/move_effects')
-rw-r--r--engine/battle/move_effects/attract.asm76
-rw-r--r--engine/battle/move_effects/baton_pass.asm217
-rw-r--r--engine/battle/move_effects/beat_up.asm224
-rw-r--r--engine/battle/move_effects/belly_drum.asm34
-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/conversion2.asm64
-rw-r--r--engine/battle/move_effects/counter.asm59
-rw-r--r--engine/battle/move_effects/curse.asm93
-rw-r--r--engine/battle/move_effects/destiny_bond.asm9
-rw-r--r--engine/battle/move_effects/disable.asm72
-rw-r--r--engine/battle/move_effects/encore.asm120
-rw-r--r--engine/battle/move_effects/endure.asm16
-rw-r--r--engine/battle/move_effects/false_swipe.asm48
-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/heal_bell.asm34
-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/lock_on.asm21
-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/pain_split.asm93
-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.asm88
-rw-r--r--engine/battle/move_effects/protect.asm75
-rw-r--r--engine/battle/move_effects/psych_up.asm49
-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.asm29
-rw-r--r--engine/battle/move_effects/sketch.asm117
-rw-r--r--engine/battle/move_effects/sleep_talk.asm143
-rw-r--r--engine/battle/move_effects/snore.asm11
-rw-r--r--engine/battle/move_effects/spikes.asm26
-rw-r--r--engine/battle/move_effects/spite.asm86
-rw-r--r--engine/battle/move_effects/splash.asm4
-rw-r--r--engine/battle/move_effects/substitute.asm88
-rw-r--r--engine/battle/move_effects/sunny_day.asm9
-rw-r--r--engine/battle/move_effects/teleport.asm89
-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.asm139
-rw-r--r--engine/battle/move_effects/triple_kick.asm34
58 files changed, 3326 insertions, 0 deletions
diff --git a/engine/battle/move_effects/attract.asm b/engine/battle/move_effects/attract.asm
new file mode 100644
index 000000000..bb7b4fce4
--- /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 000000000..5d9f19838
--- /dev/null
+++ b/engine/battle/move_effects/baton_pass.asm
@@ -0,0 +1,217 @@
+BattleCommand_BatonPass:
+; batonpass
+
+ ld 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
+
+; Mobile link battles handle entrances differently
+ farcall CheckMobileBattleError
+ jp c, EndMoveEffect
+
+ 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
+
+; Mobile link battles handle entrances differently
+ farcall CheckMobileBattleError
+ jp c, EndMoveEffect
+
+; Passed enemy PartyMon entrance
+ xor a
+ ld [wEnemySwitchMonIndex], a
+ ld hl, EnemySwitch_SetMode
+ call CallBattleCore
+ ld hl, ResetBattleParticipants
+ call CallBattleCore
+ ld a, 1
+ ld [wTypeMatchup], a
+ ld hl, ApplyStatLevelMultiplierOnAllStats
+ call CallBattleCore
+
+ ld hl, SpikesDamage
+ call CallBattleCore
+
+ jr ResetBatonPassStatus
+
+BatonPass_LinkPlayerSwitch:
+ ld a, [wLinkMode]
+ and a
+ ret z
+
+ ld a, 1
+ ld [wBattlePlayerAction], a
+
+ call LoadStandardMenuHeader
+ ld hl, LinkBattleSendReceiveAction
+ call CallBattleCore
+ call CloseWindow
+
+ xor a
+ ld [wBattlePlayerAction], a
+ ret
+
+BatonPass_LinkEnemySwitch:
+ ld a, [wLinkMode]
+ and a
+ ret z
+
+ call LoadStandardMenuHeader
+ ld hl, LinkBattleSendReceiveAction
+ call CallBattleCore
+
+ ld a, [wOTPartyCount]
+ add BATTLEACTION_SWITCH1
+ ld b, a
+ ld a, [wBattleAction]
+ cp BATTLEACTION_SWITCH1
+ jr c, .baton_pass
+ cp b
+ jr c, .switch
+
+.baton_pass
+ ld a, [wCurOTMon]
+ add BATTLEACTION_SWITCH1
+ ld [wBattleAction], a
+.switch
+ 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/beat_up.asm b/engine/battle/move_effects/beat_up.asm
new file mode 100644
index 000000000..673fc46c6
--- /dev/null
+++ b/engine/battle/move_effects/beat_up.asm
@@ -0,0 +1,224 @@
+BattleCommand_BeatUp:
+; beatup
+
+ call ResetDamage
+ ld a, [hBattleTurn]
+ and a
+ jp nz, .enemy_beats_up
+
+ ld a, [wPlayerSubStatus3]
+ bit SUBSTATUS_IN_LOOP, a
+ jr nz, .next_mon
+
+ ld c, 20
+ call DelayFrames
+ xor a
+ ld [wPlayerRolloutCount], a
+ ld [wd002], a
+ ld [wBeatUpHitAtLeastOnce], a
+ jr .got_mon
+
+.next_mon
+ ld a, [wPlayerRolloutCount]
+ ld b, a
+ ld a, [wPartyCount]
+ sub b
+ ld [wd002], a
+
+.got_mon
+ ld a, [wd002]
+ ld hl, wPartyMonNicknames
+ call GetNick
+ ld a, MON_HP
+ call GetBeatupMonLocation
+ ld a, [hli]
+ or [hl]
+ jp z, .beatup_fail ; fainted
+ ld a, [wd002]
+ ld c, a
+ ld a, [wCurBattleMon]
+ ; BUG: this can desynchronize link battles
+ ; Change "cp [hl]" to "cp c" to fix
+ cp [hl]
+ ld hl, wBattleMonStatus
+ jr z, .active_mon
+ ld a, MON_STATUS
+ call GetBeatupMonLocation
+.active_mon
+ ld a, [hl]
+ and a
+ jp nz, .beatup_fail
+
+ ld a, $1
+ ld [wBeatUpHitAtLeastOnce], a
+ ld hl, BeatUpAttackText
+ call StdBattleTextBox
+
+ ld a, [wEnemyMonSpecies]
+ ld [wCurSpecies], a
+ call GetBaseData
+ ld a, [wBaseDefense]
+ ld c, a
+
+ push bc
+ ld a, MON_SPECIES
+ call GetBeatupMonLocation
+ ld a, [hl]
+ ld [wCurSpecies], a
+ call GetBaseData
+ ld a, [wBaseAttack]
+ pop bc
+ ld b, a
+
+ push bc
+ ld a, MON_LEVEL
+ call GetBeatupMonLocation
+ ld a, [hl]
+ ld e, a
+ pop bc
+
+ ld a, [wPlayerMoveStructPower]
+ ld d, a
+ ret
+
+.enemy_beats_up
+ ld a, [wEnemySubStatus3]
+ bit SUBSTATUS_IN_LOOP, a
+ jr nz, .enemy_next_mon
+
+ xor a
+ ld [wEnemyRolloutCount], a
+ ld [wd002], a
+ ld [wBeatUpHitAtLeastOnce], a
+ jr .enemy_got_mon
+
+.enemy_next_mon
+ ld a, [wEnemyRolloutCount]
+ ld b, a
+ ld a, [wOTPartyCount]
+ sub b
+ ld [wd002], a
+
+.enemy_got_mon
+ ld a, [wBattleMode]
+ dec a
+ jr z, .wild
+
+ ld a, [wLinkMode]
+ and a
+ jr nz, .link_or_tower
+
+ ld a, [wInBattleTowerBattle]
+ and a
+ jr nz, .link_or_tower
+
+ ld a, [wd002]
+ ld c, a
+ ld b, 0
+ ld hl, wOTPartySpecies
+ add hl, bc
+ ld a, [hl]
+ ld [wNamedObjectIndexBuffer], a
+ call GetPokemonName
+ jr .got_enemy_nick
+
+.link_or_tower
+ ld a, [wd002]
+ ld hl, wOTPartyMonNicknames
+ ld bc, NAME_LENGTH
+ call AddNTimes
+ ld de, wStringBuffer1
+ call CopyBytes
+
+.got_enemy_nick
+ ld a, MON_HP
+ call GetBeatupMonLocation
+ ld a, [hli]
+ or [hl]
+ jp z, .beatup_fail
+
+ ld a, [wd002]
+ ld b, a
+ ld a, [wCurOTMon]
+ cp b
+ ld hl, wEnemyMonStatus
+ jr z, .active_enemy
+ ld a, MON_STATUS
+ call GetBeatupMonLocation
+.active_enemy
+ ld a, [hl]
+ and a
+ jr nz, .beatup_fail
+
+ ld a, $1
+ ld [wBeatUpHitAtLeastOnce], a
+ jr .finish_beatup
+
+.wild
+ ld a, [wEnemyMonSpecies]
+ ld [wNamedObjectIndexBuffer], a
+ call GetPokemonName
+ ld hl, BeatUpAttackText
+ call StdBattleTextBox
+ jp EnemyAttackDamage
+
+.finish_beatup
+ ld hl, BeatUpAttackText
+ call StdBattleTextBox
+
+ ld a, [wBattleMonSpecies]
+ ld [wCurSpecies], a
+ call GetBaseData
+ ld a, [wBaseDefense]
+ ld c, a
+
+ push bc
+ ld a, MON_SPECIES
+ call GetBeatupMonLocation
+ ld a, [hl]
+ ld [wCurSpecies], a
+ call GetBaseData
+ ld a, [wBaseAttack]
+ pop bc
+ ld b, a
+
+ push bc
+ ld a, MON_LEVEL
+ call GetBeatupMonLocation
+ ld a, [hl]
+ ld e, a
+ pop bc
+
+ ld a, [wEnemyMoveStructPower]
+ ld d, a
+ ret
+
+.beatup_fail
+ ld b, buildopponentrage_command
+ jp SkipToBattleCommand
+
+BattleCommand_BeatUpFailText:
+; beatupfailtext
+
+ ld a, [wBeatUpHitAtLeastOnce]
+ and a
+ ret nz
+
+ jp PrintButItFailed
+
+GetBeatupMonLocation:
+ push bc
+ ld c, a
+ ld b, 0
+ ld a, [hBattleTurn]
+ and a
+ ld hl, wPartyMon1Species
+ jr z, .got_species
+ ld hl, wOTPartyMon1Species
+
+.got_species
+ ld a, [wd002]
+ add hl, bc
+ call GetPartyLocation
+ pop bc
+ ret
diff --git a/engine/battle/move_effects/belly_drum.asm b/engine/battle/move_effects/belly_drum.asm
new file mode 100644
index 000000000..27ef1a542
--- /dev/null
+++ b/engine/battle/move_effects/belly_drum.asm
@@ -0,0 +1,34 @@
+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
+ ld a, 5
+
+.max_attack_loop
+ push af
+ call BattleCommand_AttackUp2
+ pop af
+ dec a
+ jr nz, .max_attack_loop
+
+ 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 000000000..871e0e7a8
--- /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
+ ld 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
+ ld 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
+ ld 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 000000000..f9307056c
--- /dev/null
+++ b/engine/battle/move_effects/conversion.asm
@@ -0,0 +1,96 @@
+BattleCommand_Conversion:
+; conversion
+
+ ld hl, wBattleMonMoves
+ ld de, wBattleMonType1
+ ld 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_T
+ 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_T
+ 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/conversion2.asm b/engine/battle/move_effects/conversion2.asm
new file mode 100644
index 000000000..14a7e257a
--- /dev/null
+++ b/engine/battle/move_effects/conversion2.asm
@@ -0,0 +1,64 @@
+BattleCommand_Conversion2:
+; conversion2
+
+ ld a, [wAttackMissed]
+ and a
+ jr nz, .failed
+ ld hl, wBattleMonType1
+ ld a, [hBattleTurn]
+ and a
+ jr z, .got_type
+ ld hl, wEnemyMonType1
+.got_type
+ ld a, BATTLE_VARS_LAST_COUNTER_MOVE_OPP
+ call GetBattleVar
+ and a
+ jr z, .failed
+ push hl
+ dec a
+ ld hl, Moves + MOVE_TYPE
+ call GetMoveAttr
+ ld d, a
+ pop hl
+ cp CURSE_T
+ jr z, .failed
+ call AnimateCurrentMove
+ call BattleCommand_SwitchTurn
+
+.loop
+ call BattleRandom
+ maskbits NUM_TYPES
+ cp UNUSED_TYPES
+ jr c, .okay
+ cp UNUSED_TYPES_END
+ jr c, .loop
+ cp TYPES_END
+ jr nc, .loop
+.okay
+ ld [hli], a
+ ld [hld], a
+ push hl
+ ld a, BATTLE_VARS_MOVE_TYPE
+ call GetBattleVarAddr
+ push af
+ push hl
+ ld a, d
+ ld [hl], a
+ call BattleCheckTypeMatchup
+ pop hl
+ pop af
+ ld [hl], a
+ pop hl
+ ld a, [wTypeMatchup]
+ cp EFFECTIVE
+ jr nc, .loop
+ call BattleCommand_SwitchTurn
+
+ ld a, [hl]
+ ld [wNamedObjectIndexBuffer], a
+ predef GetTypeName
+ ld hl, TransformedTypeText
+ jp StdBattleTextBox
+
+.failed
+ jp FailMove
diff --git a/engine/battle/move_effects/counter.asm b/engine/battle/move_effects/counter.asm
new file mode 100644
index 000000000..031c399a6
--- /dev/null
+++ b/engine/battle/move_effects/counter.asm
@@ -0,0 +1,59 @@
+BattleCommand_Counter:
+; counter
+
+ 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_COUNTER
+ 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 nc
+
+ ; 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/curse.asm b/engine/battle/move_effects/curse.asm
new file mode 100644
index 000000000..4861e9a37
--- /dev/null
+++ b/engine/battle/move_effects/curse.asm
@@ -0,0 +1,93 @@
+BattleCommand_Curse:
+; curse
+
+ ld de, wBattleMonType1
+ ld bc, wPlayerStatLevels
+ ld 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/destiny_bond.asm b/engine/battle/move_effects/destiny_bond.asm
new file mode 100644
index 000000000..55809d98a
--- /dev/null
+++ b/engine/battle/move_effects/destiny_bond.asm
@@ -0,0 +1,9 @@
+BattleCommand_DestinyBond:
+; destinybond
+
+ ld a, BATTLE_VARS_SUBSTATUS5
+ call GetBattleVarAddr
+ set SUBSTATUS_DESTINY_BOND, [hl]
+ call AnimateCurrentMove
+ ld hl, DestinyBondEffectText
+ jp StdBattleTextBox
diff --git a/engine/battle/move_effects/disable.asm b/engine/battle/move_effects/disable.asm
new file mode 100644
index 000000000..44017e30d
--- /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
+ ld 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
+
+ ld 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
+ ld 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/encore.asm b/engine/battle/move_effects/encore.asm
new file mode 100644
index 000000000..f436fd970
--- /dev/null
+++ b/engine/battle/move_effects/encore.asm
@@ -0,0 +1,120 @@
+BattleCommand_Encore:
+; encore
+
+ ld hl, wEnemyMonMoves
+ ld de, wEnemyEncoreCount
+ ld a, [hBattleTurn]
+ and a
+ jr z, .ok
+ ld hl, wBattleMonMoves
+ ld de, wPlayerEncoreCount
+.ok
+ ld a, BATTLE_VARS_LAST_MOVE_OPP
+ call GetBattleVar
+ and a
+ jp z, .failed
+ cp STRUGGLE
+ jp z, .failed
+ cp ENCORE
+ jp z, .failed
+ cp MIRROR_MOVE
+ jp z, .failed
+ ld b, a
+
+.got_move
+ ld a, [hli]
+ cp b
+ jr nz, .got_move
+
+ ld bc, wBattleMonPP - wBattleMonMoves - 1
+ add hl, bc
+ ld a, [hl]
+ and PP_MASK
+ jp z, .failed
+ ld a, [wAttackMissed]
+ and a
+ jp nz, .failed
+ ld a, BATTLE_VARS_SUBSTATUS5_OPP
+ call GetBattleVarAddr
+ bit SUBSTATUS_ENCORED, [hl]
+ jp nz, .failed
+ set SUBSTATUS_ENCORED, [hl]
+ call BattleRandom
+ and $3
+ inc a
+ inc a
+ inc a
+ ld [de], a
+ call CheckOpponentWentFirst
+ jr nz, .finish_move
+ ld a, [hBattleTurn]
+ and a
+ jr z, .force_last_enemy_move
+
+ push hl
+ ld a, [wLastPlayerMove]
+ ld b, a
+ ld c, 0
+ ld hl, wBattleMonMoves
+.find_player_move
+ ld a, [hli]
+ cp b
+ jr z, .got_player_move
+ inc c
+ ld a, c
+ cp NUM_MOVES
+ jr c, .find_player_move
+ pop hl
+ res SUBSTATUS_ENCORED, [hl]
+ xor a
+ ld [de], a
+ jr .failed
+
+.got_player_move
+ pop hl
+ ld a, c
+ ld [wCurMoveNum], a
+ ld a, b
+ ld [wCurPlayerMove], a
+ dec a
+ ld de, wPlayerMoveStruct
+ call GetMoveData
+ jr .finish_move
+
+.force_last_enemy_move
+ push hl
+ ld a, [wLastEnemyMove]
+ ld b, a
+ ld c, 0
+ ld hl, wEnemyMonMoves
+.find_enemy_move
+ ld a, [hli]
+ cp b
+ jr z, .got_enemy_move
+ inc c
+ ld a, c
+ cp NUM_MOVES
+ jr c, .find_enemy_move
+ pop hl
+ res SUBSTATUS_ENCORED, [hl]
+ xor a
+ ld [de], a
+ jr .failed
+
+.got_enemy_move
+ pop hl
+ ld a, c
+ ld [wCurEnemyMoveNum], a
+ ld a, b
+ ld [wCurEnemyMove], a
+ dec a
+ ld de, wEnemyMoveStruct
+ call GetMoveData
+
+.finish_move
+ call AnimateCurrentMove
+ ld hl, GotAnEncoreText
+ jp StdBattleTextBox
+
+.failed
+ jp PrintDidntAffect2
diff --git a/engine/battle/move_effects/endure.asm b/engine/battle/move_effects/endure.asm
new file mode 100644
index 000000000..f0555a737
--- /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/false_swipe.asm b/engine/battle/move_effects/false_swipe.asm
new file mode 100644
index 000000000..4f4eb329c
--- /dev/null
+++ b/engine/battle/move_effects/false_swipe.asm
@@ -0,0 +1,48 @@
+BattleCommand_FalseSwipe:
+; falseswipe
+
+; Makes sure wCurDamage < MonHP
+
+ ld hl, wEnemyMonHP
+ ld a, [hBattleTurn]
+ and a
+ jr z, .got_hp
+ ld hl, wBattleMonHP
+.got_hp
+ ld de, wCurDamage
+ ld c, 2
+ push hl
+ push de
+ call StringCmp
+ pop de
+ pop hl
+ jr c, .done
+
+ ld a, [hli]
+ ld [de], a
+ inc de
+ ld a, [hl]
+ dec a
+ ld [de], a
+
+ inc a
+ jr nz, .okay
+ dec de
+ ld a, [de]
+ dec a
+ ld [de], a
+.okay
+
+ ld a, [wCriticalHit]
+ cp 2
+ jr nz, .carry
+ xor a
+ ld [wCriticalHit], a
+
+.carry
+ scf
+ ret
+
+.done
+ and a
+ ret
diff --git a/engine/battle/move_effects/focus_energy.asm b/engine/battle/move_effects/focus_energy.asm
new file mode 100644
index 000000000..ffdc7b136
--- /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 000000000..4235e0878
--- /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 000000000..3d395b510
--- /dev/null
+++ b/engine/battle/move_effects/frustration.asm
@@ -0,0 +1,27 @@
+BattleCommand_FrustrationPower:
+; frustrationpower
+
+ push bc
+ ld hl, wBattleMonHappiness
+ ld a, [hBattleTurn]
+ and a
+ jr z, .got_happiness
+ ld hl, wEnemyMonHappiness
+.got_happiness
+ ld a, $ff
+ sub [hl]
+ ld [hMultiplicand + 2], a
+ xor a
+ ld [hMultiplicand + 0], a
+ ld [hMultiplicand + 1], a
+ ld a, 10
+ ld [hMultiplier], a
+ call Multiply
+ ld a, 25
+ ld [hDivisor], a
+ ld b, 4
+ call Divide
+ ld a, [hQuotient + 2]
+ 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 000000000..6aa6b7838
--- /dev/null
+++ b/engine/battle/move_effects/fury_cutter.asm
@@ -0,0 +1,55 @@
+BattleCommand_FuryCutter:
+; furycutter
+
+ ld hl, wPlayerFuryCutterCount
+ ld 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
+ ld 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 000000000..c28841b35
--- /dev/null
+++ b/engine/battle/move_effects/future_sight.asm
@@ -0,0 +1,81 @@
+BattleCommand_CheckFutureSight:
+; checkfuturesight
+
+ ld hl, wPlayerFutureSightCount
+ ld de, wPlayerFutureSightDamage
+ ld 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
+ ld 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
+ ld 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/heal_bell.asm b/engine/battle/move_effects/heal_bell.asm
new file mode 100644
index 000000000..2570a53ba
--- /dev/null
+++ b/engine/battle/move_effects/heal_bell.asm
@@ -0,0 +1,34 @@
+BattleCommand_HealBell:
+; healbell
+
+ ld a, BATTLE_VARS_SUBSTATUS1
+ call GetBattleVarAddr
+ res SUBSTATUS_NIGHTMARE, [hl]
+ ld de, wPartyMon1Status
+ ld a, [hBattleTurn]
+ and a
+ jr z, .got_status
+ ld de, wOTPartyMon1Status
+.got_status
+ ld a, BATTLE_VARS_STATUS
+ call GetBattleVarAddr
+ xor a
+ ld [hl], a
+ ld h, d
+ ld l, e
+ ld bc, PARTYMON_STRUCT_LENGTH
+ ld d, PARTY_LENGTH
+.loop
+ ld [hl], a
+ add hl, bc
+ dec d
+ jr nz, .loop
+ call AnimateCurrentMove
+
+ ld hl, BellChimedText
+ call StdBattleTextBox
+
+ ld a, [hBattleTurn]
+ and a
+ jp z, CalcPlayerStats
+ jp CalcEnemyStats
diff --git a/engine/battle/move_effects/hidden_power.asm b/engine/battle/move_effects/hidden_power.asm
new file mode 100644
index 000000000..3b40a6c31
--- /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 000000000..5602e7f62
--- /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
+ ld 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/lock_on.asm b/engine/battle/move_effects/lock_on.asm
new file mode 100644
index 000000000..7dc4a013d
--- /dev/null
+++ b/engine/battle/move_effects/lock_on.asm
@@ -0,0 +1,21 @@
+BattleCommand_LockOn:
+; lockon
+
+ call CheckSubstituteOpp
+ jr nz, .fail
+
+ ld a, [wAttackMissed]
+ and a
+ jr nz, .fail
+
+ ld a, BATTLE_VARS_SUBSTATUS5_OPP
+ call GetBattleVarAddr
+ set SUBSTATUS_LOCK_ON, [hl]
+ call AnimateCurrentMove
+
+ ld hl, TookAimText
+ jp StdBattleTextBox
+
+.fail
+ call AnimateFailedMove
+ jp PrintDidntAffect
diff --git a/engine/battle/move_effects/magnitude.asm b/engine/battle/move_effects/magnitude.asm
new file mode 100644
index 000000000..4dcf919d7
--- /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 [wTypeMatchup], 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 000000000..25197d7cd
--- /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 000000000..b1c6608fa
--- /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
+ ld 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, LearnedMoveText
+ 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 000000000..fb3a30d58
--- /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 + 2]
+ and a
+ ret z
+
+ ld a, [wStringBuffer1 + 3]
+ 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 000000000..089403839
--- /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 [wd265], 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 000000000..199a30045
--- /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 000000000..dd85c722d
--- /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/pain_split.asm b/engine/battle/move_effects/pain_split.asm
new file mode 100644
index 000000000..4e4380b23
--- /dev/null
+++ b/engine/battle/move_effects/pain_split.asm
@@ -0,0 +1,93 @@
+BattleCommand_PainSplit:
+; painsplit
+
+ ld a, [wAttackMissed]
+ and a
+ jp nz, .ButItFailed
+ call CheckSubstituteOpp
+ jp nz, .ButItFailed
+ call AnimateCurrentMove
+ ld hl, wBattleMonMaxHP + 1
+ ld de, wEnemyMonMaxHP + 1
+ call .PlayerShareHP
+ ld a, $1
+ ld [wWhichHPBar], a
+ hlcoord 10, 9
+ predef AnimateHPBar
+ ld hl, wEnemyMonHP
+ ld a, [hli]
+ ld [wBuffer4], a
+ ld a, [hli]
+ ld [wBuffer3], a
+ ld a, [hli]
+ ld [wBuffer2], a
+ ld a, [hl]
+ ld [wBuffer1], a
+ call .EnemyShareHP
+ xor a
+ ld [wWhichHPBar], a
+ call ResetDamage
+ hlcoord 2, 2
+ predef AnimateHPBar
+ farcall _UpdateBattleHUDs
+
+ ld hl, SharedPainText
+ jp StdBattleTextBox
+
+.PlayerShareHP:
+ ld a, [hld]
+ ld [wBuffer1], a
+ ld a, [hld]
+ ld [wBuffer2], a
+ ld a, [hld]
+ ld b, a
+ ld [wBuffer3], a
+ ld a, [hl]
+ ld [wBuffer4], a
+ dec de
+ dec de
+ ld a, [de]
+ dec de
+ add b
+ ld [wCurDamage + 1], a
+ ld b, [hl]
+ ld a, [de]
+ adc b
+ srl a
+ ld [wCurDamage], a
+ ld a, [wCurDamage + 1]
+ rr a
+ ld [wCurDamage + 1], a
+ inc hl
+ inc hl
+ inc hl
+ inc de
+ inc de
+ inc de
+
+.EnemyShareHP:
+ ld c, [hl]
+ dec hl
+ ld a, [wCurDamage + 1]
+ sub c
+ ld b, [hl]
+ dec hl
+ ld a, [wCurDamage]
+ sbc b
+ jr nc, .skip
+
+ ld a, [wCurDamage]
+ ld b, a
+ ld a, [wCurDamage + 1]
+ ld c, a
+.skip
+ ld a, c
+ ld [hld], a
+ ld [wBuffer5], a
+ ld a, b
+ ld [hli], a
+ ld [wBuffer6], a
+ ret
+
+.ButItFailed:
+ jp PrintDidntAffect2
diff --git a/engine/battle/move_effects/pay_day.asm b/engine/battle/move_effects/pay_day.asm
new file mode 100644
index 000000000..ba1dce884
--- /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
+
+ ld 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 000000000..4df52cc1c
--- /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 000000000..f0949558f
--- /dev/null
+++ b/engine/battle/move_effects/present.asm
@@ -0,0 +1,88 @@
+BattleCommand_Present:
+; present
+
+ ld a, [wLinkMode]
+ cp LINK_COLOSSEUM
+ jr z, .colosseum_skippush
+ push bc
+ push de
+.colosseum_skippush
+
+ call BattleCommand_Stab
+
+ ld a, [wLinkMode]
+ cp LINK_COLOSSEUM
+ jr z, .colosseum_skippop
+ pop de
+ pop bc
+.colosseum_skippop
+
+ 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
+ ld a, [hBattleTurn]
+ and a
+ jr z, .got_hp_fn_pointer
+ ld hl, AICheckEnemyMaxHP
+.got_hp_fn_pointer
+ ld a, BANK(AICheckPlayerMaxHP)
+ 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
+ call _CheckBattleScene
+ jr nc, .do_animation
+ call AnimateFailedMove
+ ld hl, RefusedGiftText
+ 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 000000000..9025d2f4b
--- /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
+ ld 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 000000000..02fdef5d2
--- /dev/null
+++ b/engine/battle/move_effects/psych_up.asm
@@ -0,0 +1,49 @@
+BattleCommand_PsychUp:
+; psychup
+
+ ld hl, wEnemyStatLevels
+ ld de, wPlayerStatLevels
+ ld a, [hBattleTurn]
+ and a
+ jr z, .pointers_correct
+; It's the enemy's turn, so swap the pointers.
+ push hl
+ ld h, d
+ ld l, e
+ pop de
+.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
+ ld 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 000000000..18c358144
--- /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
+ ld 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 000000000..df206a6bf
--- /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 000000000..e9ef5d123
--- /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 000000000..0dc8b7de2
--- /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
+ ld 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 000000000..d3071d23a
--- /dev/null
+++ b/engine/battle/move_effects/return.asm
@@ -0,0 +1,25 @@
+BattleCommand_HappinessPower:
+; happinesspower
+ push bc
+ ld hl, wBattleMonHappiness
+ ld a, [hBattleTurn]
+ and a
+ jr z, .ok
+ ld hl, wEnemyMonHappiness
+.ok
+ xor a
+ ld [hMultiplicand + 0], a
+ ld [hMultiplicand + 1], a
+ ld a, [hl]
+ ld [hMultiplicand + 2], a
+ ld a, 10
+ ld [hMultiplier], a
+ call Multiply
+ ld a, 25
+ ld [hDivisor], a
+ ld b, 4
+ call Divide
+ ld a, [hQuotient + 2]
+ 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 000000000..de4bfcaa6
--- /dev/null
+++ b/engine/battle/move_effects/rollout.asm
@@ -0,0 +1,95 @@
+MAX_ROLLOUT_COUNT EQU 5
+
+BattleCommand_CheckCurl:
+; checkcurl
+
+ ld de, wPlayerRolloutCount
+ ld 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
+ ld 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 000000000..a926bdccc
--- /dev/null
+++ b/engine/battle/move_effects/safeguard.asm
@@ -0,0 +1,23 @@
+BattleCommand_Safeguard:
+; safeguard
+
+ ld hl, wPlayerScreens
+ ld de, wPlayerSafeguardCount
+ ld 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 000000000..77d6e24b5
--- /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 000000000..d6eeac484
--- /dev/null
+++ b/engine/battle/move_effects/selfdestruct.asm
@@ -0,0 +1,29 @@
+BattleCommand_Selfdestruct:
+ farcall StubbedTrainerRankings_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]
+ call _CheckBattleScene
+ ret nc
+ farcall DrawPlayerHUD
+ farcall DrawEnemyHUD
+ call WaitBGMap
+ jp RefreshBattleHuds
diff --git a/engine/battle/move_effects/sketch.asm b/engine/battle/move_effects/sketch.asm
new file mode 100644
index 000000000..4c15dbe02
--- /dev/null
+++ b/engine/battle/move_effects/sketch.asm
@@ -0,0 +1,117 @@
+BattleCommand_Sketch:
+; sketch
+
+ call ClearLastMove
+; Don't sketch during a link battle
+ ld a, [wLinkMode]
+ and a
+ jr z, .not_linked
+ call AnimateFailedMove
+ jp PrintNothingHappened
+
+.not_linked
+; If the opponent has a substitute up, fail.
+ call CheckSubstituteOpp
+ jp nz, .fail
+; If the opponent is transformed, fail.
+ ld a, BATTLE_VARS_SUBSTATUS5_OPP
+ call GetBattleVarAddr
+ bit SUBSTATUS_TRANSFORMED, [hl]
+ jp nz, .fail
+; Get the user's moveset in its party struct.
+; This move replacement shall be permanent.
+; Pointer will be in de.
+ ld a, MON_MOVES
+ call UserPartyAttr
+ ld d, h
+ ld e, l
+; Get the battle move structs.
+ ld hl, wBattleMonMoves
+ ld a, [hBattleTurn]
+ and a
+ jr z, .get_last_move
+ ld hl, wEnemyMonMoves
+.get_last_move
+ ld a, BATTLE_VARS_LAST_COUNTER_MOVE_OPP
+ call GetBattleVar
+ ld [wTypeMatchup], a
+ ld b, a
+; Fail if move is invalid or is Struggle.
+ and a
+ jr z, .fail
+ cp STRUGGLE
+ jr z, .fail
+; Fail if user already knows that move
+ ld c, NUM_MOVES
+.does_user_already_know_move
+ ld a, [hli]
+ cp b
+ jr z, .fail
+ dec c
+ jr nz, .does_user_already_know_move
+; Find Sketch in the user's moveset.
+; Pointer in hl, and index in c.
+ dec hl
+ ld c, NUM_MOVES
+.find_sketch
+ dec c
+ ld a, [hld]
+ cp SKETCH
+ jr nz, .find_sketch
+ inc hl
+; The Sketched move is loaded to that slot.
+ ld a, b
+ ld [hl], a
+; Copy the base PP from that move.
+ push bc
+ push hl
+ dec a
+ ld hl, Moves + MOVE_PP
+ call GetMoveAttr
+ pop hl
+ ld bc, wBattleMonPP - wBattleMonMoves
+ add hl, bc
+ ld [hl], a
+ pop bc
+
+ ld a, [hBattleTurn]
+ and a
+ jr z, .user_trainer
+ ld a, [wBattleMode]
+ dec a
+ jr nz, .user_trainer
+; wildmon
+ ld a, [hl]
+ push bc
+ ld hl, wWildMonPP
+ ld b, 0
+ add hl, bc
+ ld [hl], a
+ ld hl, wWildMonMoves
+ add hl, bc
+ pop bc
+ ld [hl], b
+ jr .done_copy
+
+.user_trainer
+ ld a, [hl]
+ push af
+ ld l, c
+ ld h, 0
+ add hl, de
+ ld a, b
+ ld [hl], a
+ pop af
+ ld de, MON_PP - MON_MOVES
+ add hl, de
+ ld [hl], a
+.done_copy
+ call GetMoveName
+ call AnimateCurrentMove
+
+ ld hl, SketchedText
+ jp StdBattleTextBox
+
+.fail
+ call AnimateFailedMove
+ jp PrintDidntAffect
diff --git a/engine/battle/move_effects/sleep_talk.asm b/engine/battle/move_effects/sleep_talk.asm
new file mode 100644
index 000000000..ae2d252b7
--- /dev/null
+++ b/engine/battle/move_effects/sleep_talk.asm
@@ -0,0 +1,143 @@
+BattleCommand_SleepTalk:
+; sleeptalk
+
+ call ClearLastMove
+ ld a, [wAttackMissed]
+ and a
+ jr nz, .fail
+ ld a, [hBattleTurn]
+ and a
+ ld hl, wBattleMonMoves + 1
+ ld a, [wDisabledMove]
+ ld d, a
+ jr z, .got_moves
+ ld hl, wEnemyMonMoves + 1
+ ld a, [wEnemyDisabledMove]
+ ld d, a
+.got_moves
+ ld a, BATTLE_VARS_STATUS
+ call GetBattleVar
+ and SLP
+ jr z, .fail
+ ld a, [hl]
+ and a
+ jr z, .fail
+ call .safely_check_has_usable_move
+ jr c, .fail
+ dec hl
+.sample_move
+ push hl
+ call BattleRandom
+ maskbits NUM_MOVES
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld a, [hl]
+ pop hl
+ and a
+ jr z, .sample_move
+ ld e, a
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+ cp e
+ jr z, .sample_move
+ ld a, e
+ cp d
+ jr z, .sample_move
+ call .check_two_turn_move
+ jr z, .sample_move
+ ld a, BATTLE_VARS_MOVE
+ call GetBattleVarAddr
+ ld a, e
+ ld [hl], a
+ call CheckUserIsCharging
+ jr nz, .charging
+ ld a, [wKickCounter]
+ push af
+ call BattleCommand_LowerSub
+ pop af
+ ld [wKickCounter], a
+.charging
+ call LoadMoveAnim
+ call UpdateMoveData
+ jp ResetTurn
+
+.fail
+ call AnimateFailedMove
+ jp TryPrintButItFailed
+
+.safely_check_has_usable_move
+ push hl
+ push de
+ push bc
+ call .check_has_usable_move
+ pop bc
+ pop de
+ pop hl
+ ret
+
+.check_has_usable_move
+ ld a, [hBattleTurn]
+ and a
+ ld a, [wDisabledMove]
+ jr z, .got_move_2
+
+ ld a, [wEnemyDisabledMove]
+.got_move_2
+ ld b, a
+ ld a, BATTLE_VARS_MOVE
+ call GetBattleVar
+ ld c, a
+ dec hl
+ ld d, NUM_MOVES
+.loop2
+ ld a, [hl]
+ and a
+ jr z, .carry
+
+ cp c
+ jr z, .nope
+ cp b
+ jr z, .nope
+
+ call .check_two_turn_move
+ jr nz, .no_carry
+
+.nope
+ inc hl
+ dec d
+ jr nz, .loop2
+
+.carry
+ scf
+ ret
+
+.no_carry
+ and a
+ ret
+
+.check_two_turn_move
+ push hl
+ push de
+ push bc
+
+ ld b, a
+ callfar GetMoveEffect
+ ld a, b
+
+ pop bc
+ pop de
+ pop hl
+
+ cp EFFECT_SKULL_BASH
+ ret z
+ cp EFFECT_RAZOR_WIND
+ ret z
+ cp EFFECT_SKY_ATTACK
+ ret z
+ cp EFFECT_SOLARBEAM
+ ret z
+ cp EFFECT_FLY
+ ret z
+ cp EFFECT_BIDE
+ ret
diff --git a/engine/battle/move_effects/snore.asm b/engine/battle/move_effects/snore.asm
new file mode 100644
index 000000000..e2432c59f
--- /dev/null
+++ b/engine/battle/move_effects/snore.asm
@@ -0,0 +1,11 @@
+BattleCommand_Snore:
+; snore
+ ld a, BATTLE_VARS_STATUS
+ call GetBattleVar
+ and SLP
+ ret nz
+ call ResetDamage
+ ld a, $1
+ ld [wAttackMissed], a
+ call FailMove
+ jp EndMoveEffect
diff --git a/engine/battle/move_effects/spikes.asm b/engine/battle/move_effects/spikes.asm
new file mode 100644
index 000000000..1eca7ae53
--- /dev/null
+++ b/engine/battle/move_effects/spikes.asm
@@ -0,0 +1,26 @@
+BattleCommand_Spikes:
+; spikes
+
+ ld hl, wEnemyScreens
+ ld 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/spite.asm b/engine/battle/move_effects/spite.asm
new file mode 100644
index 000000000..8aba187a4
--- /dev/null
+++ b/engine/battle/move_effects/spite.asm
@@ -0,0 +1,86 @@
+BattleCommand_Spite:
+; spite
+
+ ld a, [wAttackMissed]
+ and a
+ jp nz, .failed
+ ld bc, PARTYMON_STRUCT_LENGTH ; ????
+ ld hl, wEnemyMonMoves
+ ld a, [hBattleTurn]
+ and a
+ jr z, .got_moves
+ ld hl, wBattleMonMoves
+.got_moves
+ 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, -1
+.loop
+ inc c
+ ld a, [hli]
+ cp b
+ jr nz, .loop
+ ld [wTypeMatchup], a
+ dec hl
+ ld b, 0
+ push bc
+ ld c, wBattleMonPP - wBattleMonMoves
+ add hl, bc
+ pop bc
+ ld a, [hl]
+ and PP_MASK
+ jr z, .failed
+ push bc
+ call GetMoveName
+ ; lose 2-5 PP
+ call BattleRandom
+ and %11
+ inc a
+ inc a
+ ld b, a
+ ld a, [hl]
+ and PP_MASK
+ cp b
+ jr nc, .deplete_pp
+ ld b, a
+.deplete_pp
+ ld a, [hl]
+ sub b
+ ld [hl], a
+ push af
+ ld a, MON_PP
+ call OpponentPartyAttr
+ ld d, b
+ pop af
+ pop bc
+ add hl, bc
+ ld e, a
+ ld a, BATTLE_VARS_SUBSTATUS5_OPP
+ call GetBattleVar
+ bit SUBSTATUS_TRANSFORMED, a
+ jr nz, .transformed
+ ld a, [hBattleTurn]
+ and a
+ jr nz, .not_wildmon
+ ld a, [wBattleMode]
+ dec a
+ jr nz, .not_wildmon
+ ld hl, wWildMonPP
+ add hl, bc
+.not_wildmon
+ ld [hl], e
+.transformed
+ push de
+ call AnimateCurrentMove
+ pop de
+ ld a, d
+ ld [wTypeMatchup], a
+ ld hl, SpiteEffectText
+ jp StdBattleTextBox
+
+.failed
+ jp PrintDidntAffect2
diff --git a/engine/battle/move_effects/splash.asm b/engine/battle/move_effects/splash.asm
new file mode 100644
index 000000000..493ea8ca6
--- /dev/null
+++ b/engine/battle/move_effects/splash.asm
@@ -0,0 +1,4 @@
+BattleCommand_Splash:
+ call AnimateCurrentMove
+ farcall StubbedTrainerRankings_Splash
+ jp PrintNothingHappened
diff --git a/engine/battle/move_effects/substitute.asm b/engine/battle/move_effects/substitute.asm
new file mode 100644
index 000000000..8f14bc315
--- /dev/null
+++ b/engine/battle/move_effects/substitute.asm
@@ -0,0 +1,88 @@
+BattleCommand_Substitute:
+; substitute
+
+ call BattleCommand_MoveDelay
+ ld hl, wBattleMonMaxHP
+ ld de, wPlayerSubstituteHP
+ ld 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
+ ld a, [hBattleTurn]
+ and a
+ jr z, .player
+ ld hl, wEnemyWrapCount
+ ld de, wEnemyTrappingMove
+.player
+
+ xor a
+ ld [hl], a
+ ld [de], a
+ call _CheckBattleScene
+ 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 000000000..f5825930f
--- /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 000000000..1c67ef112
--- /dev/null
+++ b/engine/battle/move_effects/teleport.asm
@@ -0,0 +1,89 @@
+BattleCommand_Teleport:
+; teleport
+
+ ld a, [wBattleType]
+ cp BATTLETYPE_SHINY
+ jr z, .failed
+ cp BATTLETYPE_TRAP
+ jr z, .failed
+ cp BATTLETYPE_CELEBI
+ jr z, .failed
+ cp BATTLETYPE_SUICUNE
+ 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
+ ld 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 does the wrong thing. What was
+ ; probably intended was jr c, .failed
+ ; The way this is made 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
+ ld [wKickCounter], a
+ call SetBattleDraw
+ call BattleCommand_LowerSub
+ call LoadMoveAnim
+ ld c, 20
+ call DelayFrames
+ call SetBattleDraw
+
+ 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 000000000..64de1a4fe
--- /dev/null
+++ b/engine/battle/move_effects/thief.asm
@@ -0,0 +1,112 @@
+BattleCommand_Thief:
+; thief
+
+ ld 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 [wd265], 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, [wd265]
+ 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 [wd265], 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, [wd265]
+ 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 000000000..b2a643787
--- /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 000000000..ef8071290
--- /dev/null
+++ b/engine/battle/move_effects/transform.asm
@@ -0,0 +1,139 @@
+
+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
+ ld 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
+ ld 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
+ call _CheckBattleScene
+ jr c, .mimic_anims
+ ld 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
diff --git a/engine/battle/move_effects/triple_kick.asm b/engine/battle/move_effects/triple_kick.asm
new file mode 100644
index 000000000..e41044c9d
--- /dev/null
+++ b/engine/battle/move_effects/triple_kick.asm
@@ -0,0 +1,34 @@
+BattleCommand_TripleKick:
+; triplekick
+
+ ld a, [wKickCounter]
+ ld b, a
+ inc b
+ ld hl, wCurDamage + 1
+ ld a, [hld]
+ ld e, a
+ ld a, [hli]
+ ld d, a
+.next_kick
+ dec b
+ ret z
+ ld a, [hl]
+ add e
+ ld [hld], a
+ ld a, [hl]
+ adc d
+ ld [hli], a
+
+; No overflow.
+ jr nc, .next_kick
+ ld a, $ff
+ ld [hld], a
+ ld [hl], a
+ ret
+
+BattleCommand_KickCounter:
+; kickcounter
+
+ ld hl, wKickCounter
+ inc [hl]
+ ret