summaryrefslogtreecommitdiff
path: root/engine/battle/effect_commands.asm
diff options
context:
space:
mode:
authorentrpntr <entrpntr@gmail.com>2020-04-19 10:45:35 -0400
committerentrpntr <entrpntr@gmail.com>2020-04-19 10:48:54 -0400
commite10598eb204358343491c92348b9220185e3b33d (patch)
treed9df3c7c21997600449695ad4d55b24ed13eb3bb /engine/battle/effect_commands.asm
parent6ebd8441e249bba020036b5be5ccbe020b12e32b (diff)
Start adding engine/battle/effect_commands.asm.
Diffstat (limited to 'engine/battle/effect_commands.asm')
-rw-r--r--engine/battle/effect_commands.asm1221
1 files changed, 1221 insertions, 0 deletions
diff --git a/engine/battle/effect_commands.asm b/engine/battle/effect_commands.asm
new file mode 100644
index 00000000..023730ac
--- /dev/null
+++ b/engine/battle/effect_commands.asm
@@ -0,0 +1,1221 @@
+DoPlayerTurn:
+; Read in and execute the player's move effects for this turn.
+ call SetPlayerTurn
+
+ ld a, [wBattlePlayerAction]
+ and a ; BATTLEPLAYERACTION_USEMOVE?
+ ret nz
+
+ xor a
+ ld [wTurnEnded], a
+
+ ; Effect command checkturn is called for every move.
+ call CheckTurn
+
+ ld a, [wTurnEnded]
+ and a
+ ret nz
+
+ call UpdateMoveData
+ jr DoMove
+
+DoEnemyTurn:
+; Read in and execute the enemy's move effects for this turn.
+ call SetEnemyTurn
+
+ ld a, [wLinkMode]
+ and a
+ jr z, .do_it
+
+ ld a, [wBattleAction]
+ cp BATTLEACTION_STRUGGLE
+ jr z, .do_it
+ cp BATTLEACTION_SWITCH1
+ ret nc
+
+.do_it
+ xor a
+ ld [wTurnEnded], a
+
+ ; Effect command checkturn is called for every move.
+ call CheckTurn
+
+ ld a, [wTurnEnded]
+ and a
+ ret nz
+
+ call UpdateMoveData
+
+ ; fallthrough
+
+DoMove:
+; Get the user's move effect.
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ ld c, a
+ ld b, 0
+ ld hl, MoveEffectsPointers
+ add hl, bc
+ add hl, bc
+ ld a, BANK(MoveEffectsPointers)
+ call GetFarHalfword
+
+ ld de, wBattleScriptBuffer
+
+.GetMoveEffect:
+ ld a, BANK(MoveEffects)
+ call GetFarByte
+ inc hl
+ ld [de], a
+ inc de
+ cp endmove_command
+ jr nz, .GetMoveEffect
+
+; Start at the first command.
+ ld hl, wBattleScriptBuffer
+ ld a, l
+ ld [wBattleScriptBufferAddress], a
+ ld a, h
+ ld [wBattleScriptBufferAddress + 1], a
+
+.ReadMoveEffectCommand:
+; ld a, [wBattleScriptBufferAddress++]
+ ld a, [wBattleScriptBufferAddress]
+ ld l, a
+ ld a, [wBattleScriptBufferAddress + 1]
+ ld h, a
+
+ ld a, [hli]
+
+ push af
+ ld a, l
+ ld [wBattleScriptBufferAddress], a
+ ld a, h
+ ld [wBattleScriptBufferAddress + 1], a
+ pop af
+
+; endturn_command (-2) is used to terminate branches without ending the read cycle.
+ cp endturn_command
+ ret nc
+
+; The rest of the commands (01-af) are read from BattleCommandPointers.
+ push bc
+ dec a
+ ld c, a
+ ld b, 0
+ ld hl, BattleCommandPointers
+ add hl, bc
+ add hl, bc
+ pop bc
+
+ ld a, BANK(BattleCommandPointers)
+ call GetFarHalfword
+
+ call .DoMoveEffectCommand
+
+ jr .ReadMoveEffectCommand
+
+.DoMoveEffectCommand:
+ jp hl
+
+CheckTurn:
+BattleCommand_CheckTurn:
+; checkturn
+
+; Repurposed as hardcoded turn handling. Useless as a command.
+
+; Move $ff immediately ends the turn.
+ ld a, BATTLE_VARS_MOVE
+ call GetBattleVar
+ inc a
+ jp z, EndTurn
+
+ xor a
+ ld [wAttackMissed], a
+ ld [wEffectFailed], a
+ ld [wKickCounter], a
+ ld [wAlreadyDisobeyed], a
+ ld [wAlreadyFailed], a
+ ld [wSomeoneIsRampaging], a
+
+ ld a, EFFECTIVE
+ ld [wTypeModifier], a
+
+ ldh a, [hBattleTurn]
+ and a
+ jp nz, CheckEnemyTurn
+
+CheckPlayerTurn:
+ ld hl, wPlayerSubStatus4
+ bit SUBSTATUS_RECHARGE, [hl]
+ jr z, .no_recharge
+
+ res SUBSTATUS_RECHARGE, [hl]
+ ld hl, MustRechargeText
+ call StdBattleTextbox
+ call CantMove
+ jp EndTurn
+
+.no_recharge
+
+ ld hl, wBattleMonStatus
+ ld a, [hl]
+ and SLP
+ jr z, .not_asleep
+
+ dec a
+ ld [wBattleMonStatus], a
+ and SLP
+ jr z, .woke_up
+
+ xor a
+ ld [wNumHits], a
+ ld de, ANIM_SLP
+ call FarPlayBattleAnimation
+ jr .fast_asleep
+
+.woke_up
+ ld hl, WokeUpText
+ call StdBattleTextbox
+ call CantMove
+ call UpdateBattleMonInParty
+ ld hl, UpdatePlayerHUD
+ call CallBattleCore
+ ld a, $1
+ ldh [hBGMapMode], a
+ ld hl, wPlayerSubStatus1
+ res SUBSTATUS_NIGHTMARE, [hl]
+ jr .not_asleep
+
+.fast_asleep
+ ld hl, FastAsleepText
+ call StdBattleTextbox
+
+ ; Snore and Sleep Talk bypass sleep.
+ ld a, [wCurPlayerMove]
+ cp SNORE
+ jr z, .not_asleep
+ cp SLEEP_TALK
+ jr z, .not_asleep
+
+ call CantMove
+ jp EndTurn
+
+.not_asleep
+
+ ld hl, wBattleMonStatus
+ bit FRZ, [hl]
+ jr z, .not_frozen
+
+ ; Flame Wheel and Sacred Fire thaw the user.
+ ld a, [wCurPlayerMove]
+ cp FLAME_WHEEL
+ jr z, .not_frozen
+ cp SACRED_FIRE
+ jr z, .not_frozen
+
+ ld hl, FrozenSolidText
+ call StdBattleTextbox
+
+ call CantMove
+ jp EndTurn
+
+.not_frozen
+
+ ld hl, wPlayerSubStatus3
+ bit SUBSTATUS_FLINCHED, [hl]
+ jr z, .not_flinched
+
+ res SUBSTATUS_FLINCHED, [hl]
+ ld hl, FlinchedText
+ call StdBattleTextbox
+
+ call CantMove
+ jp EndTurn
+
+.not_flinched
+
+ ld hl, wPlayerDisableCount
+ ld a, [hl]
+ and a
+ jr z, .not_disabled
+
+ dec a
+ ld [hl], a
+ and $f
+ jr nz, .not_disabled
+
+ ld [hl], a
+ ld [wDisabledMove], a
+ ld hl, DisabledNoMoreText
+ call StdBattleTextbox
+
+.not_disabled
+
+ ld a, [wPlayerSubStatus3]
+ add a
+ jr nc, .not_confused
+ ld hl, wPlayerConfuseCount
+ dec [hl]
+ jr nz, .confused
+
+ ld hl, wPlayerSubStatus3
+ res SUBSTATUS_CONFUSED, [hl]
+ ld hl, ConfusedNoMoreText
+ call StdBattleTextbox
+ jr .not_confused
+
+.confused
+ ld hl, IsConfusedText
+ call StdBattleTextbox
+ xor a
+ ld [wNumHits], a
+ ld de, ANIM_CONFUSED
+ call FarPlayBattleAnimation
+
+ ; 50% chance of hitting itself
+ call BattleRandom
+ cp 50 percent + 1
+ jr nc, .not_confused
+
+ ; clear confusion-dependent substatus
+ ld hl, wPlayerSubStatus3
+ ld a, [hl]
+ and 1 << SUBSTATUS_CONFUSED
+ ld [hl], a
+
+ call HitConfusion
+ call CantMove
+ jp EndTurn
+
+.not_confused
+
+ ld a, [wPlayerSubStatus1]
+ add a ; bit SUBSTATUS_ATTRACT
+ jr nc, .not_infatuated
+
+ ld hl, InLoveWithText
+ call StdBattleTextbox
+ xor a
+ ld [wNumHits], a
+ ld de, ANIM_IN_LOVE
+ call FarPlayBattleAnimation
+
+ ; 50% chance of infatuation
+ call BattleRandom
+ cp 50 percent + 1
+ jr c, .not_infatuated
+
+ ld hl, InfatuationText
+ call StdBattleTextbox
+ call CantMove
+ jp EndTurn
+
+.not_infatuated
+
+ ; We can't disable a move that doesn't exist.
+ ld a, [wDisabledMove]
+ and a
+ jr z, .no_disabled_move
+
+ ; Are we using the disabled move?
+ ld hl, wCurPlayerMove
+ cp [hl]
+ jr nz, .no_disabled_move
+
+ call MoveDisabled
+ call CantMove
+ jp EndTurn
+
+.no_disabled_move
+
+ ld hl, wBattleMonStatus
+ bit PAR, [hl]
+ ret z
+
+ ; 25% chance to be fully paralyzed
+ call BattleRandom
+ cp 25 percent
+ ret nc
+
+ ld hl, FullyParalyzedText
+ call StdBattleTextbox
+ call CantMove
+ jp EndTurn
+
+CantMove:
+ ld a, BATTLE_VARS_SUBSTATUS1
+ call GetBattleVarAddr
+ res SUBSTATUS_ROLLOUT, [hl]
+
+ ld a, BATTLE_VARS_SUBSTATUS3
+ call GetBattleVarAddr
+ ld a, [hl]
+ and $ff ^ (1 << SUBSTATUS_BIDE | 1 << SUBSTATUS_RAMPAGE | 1 << SUBSTATUS_CHARGED)
+ ld [hl], a
+
+ call ResetFuryCutterCount
+
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+ cp FLY
+ jr z, .fly_dig
+
+ cp DIG
+ ret nz
+
+.fly_dig
+ res SUBSTATUS_UNDERGROUND, [hl]
+ res SUBSTATUS_FLYING, [hl]
+ jp AppearUserRaiseSub
+
+OpponentCantMove:
+ call BattleCommand_SwitchTurn
+ call CantMove
+ jp BattleCommand_SwitchTurn
+
+CheckEnemyTurn:
+ ld hl, wEnemySubStatus4
+ bit SUBSTATUS_RECHARGE, [hl]
+ jr z, .no_recharge
+
+ res SUBSTATUS_RECHARGE, [hl]
+ ld hl, MustRechargeText
+ call StdBattleTextbox
+ call CantMove
+ jp EndTurn
+
+.no_recharge
+
+ ld hl, wEnemyMonStatus
+ ld a, [hl]
+ and SLP
+ jr z, .not_asleep
+
+ dec a
+ ld [wEnemyMonStatus], a
+ and a
+ jr z, .woke_up
+
+ ld hl, FastAsleepText
+ call StdBattleTextbox
+ xor a
+ ld [wNumHits], a
+ ld de, ANIM_SLP
+ call FarPlayBattleAnimation
+ jr .fast_asleep
+
+.woke_up
+ ld hl, WokeUpText
+ call StdBattleTextbox
+ call CantMove
+ call UpdateEnemyMonInParty
+ ld hl, UpdateEnemyHUD
+ call CallBattleCore
+ ld a, $1
+ ldh [hBGMapMode], a
+ ld hl, wEnemySubStatus1
+ res SUBSTATUS_NIGHTMARE, [hl]
+ jr .not_asleep
+
+.fast_asleep
+ ; Snore and Sleep Talk bypass sleep.
+ ld a, [wCurEnemyMove]
+ cp SNORE
+ jr z, .not_asleep
+ cp SLEEP_TALK
+ jr z, .not_asleep
+ call CantMove
+ jp EndTurn
+
+.not_asleep
+
+ ld hl, wEnemyMonStatus
+ bit FRZ, [hl]
+ jr z, .not_frozen
+
+ ; Flame Wheel and Sacred Fire thaw the user.
+ ld a, [wCurEnemyMove]
+ cp FLAME_WHEEL
+ jr z, .not_frozen
+ cp SACRED_FIRE
+ jr z, .not_frozen
+
+ ld hl, FrozenSolidText
+ call StdBattleTextbox
+ call CantMove
+ jp EndTurn
+
+.not_frozen
+
+ ld hl, wEnemySubStatus3
+ bit SUBSTATUS_FLINCHED, [hl]
+ jr z, .not_flinched
+
+ res SUBSTATUS_FLINCHED, [hl]
+ ld hl, FlinchedText
+ call StdBattleTextbox
+
+ call CantMove
+ jp EndTurn
+
+.not_flinched
+
+ ld hl, wEnemyDisableCount
+ ld a, [hl]
+ and a
+ jr z, .not_disabled
+
+ dec a
+ ld [hl], a
+ and $f
+ jr nz, .not_disabled
+
+ ld [hl], a
+ ld [wEnemyDisabledMove], a
+
+ ld hl, DisabledNoMoreText
+ call StdBattleTextbox
+
+.not_disabled
+
+ ld a, [wEnemySubStatus3]
+ add a ; bit SUBSTATUS_CONFUSED
+ jr nc, .not_confused
+
+ ld hl, wEnemyConfuseCount
+ dec [hl]
+ jr nz, .confused
+
+ ld hl, wEnemySubStatus3
+ res SUBSTATUS_CONFUSED, [hl]
+ ld hl, ConfusedNoMoreText
+ call StdBattleTextbox
+ jr .not_confused
+
+.confused
+ ld hl, IsConfusedText
+ call StdBattleTextbox
+
+ xor a
+ ld [wNumHits], a
+ ld de, ANIM_CONFUSED
+ call FarPlayBattleAnimation
+
+ ; 50% chance of hitting itself
+ call BattleRandom
+ cp 50 percent + 1
+ jr nc, .not_confused
+
+ ; clear confusion-dependent substatus
+ ld hl, wEnemySubStatus3
+ ld a, [hl]
+ and 1 << SUBSTATUS_CONFUSED
+ ld [hl], a
+
+ ld hl, HurtItselfText
+ call StdBattleTextbox
+
+ call HitSelfInConfusion
+ call BattleCommand_DamageCalc
+ call BattleCommand_LowerSub
+
+ xor a
+ ld [wNumHits], a
+
+ ; Flicker the monster pic unless flying or underground.
+ ld de, ANIM_HIT_CONFUSION
+ ld a, BATTLE_VARS_SUBSTATUS3_OPP
+ call GetBattleVar
+ and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND
+ call z, PlayFXAnimID
+
+ ld c, TRUE
+ call DoEnemyDamage
+ call BattleCommand_RaiseSub
+ call CantMove
+ jp EndTurn
+
+.not_confused
+
+ ld a, [wEnemySubStatus1]
+ add a ; bit SUBSTATUS_ATTRACT
+ jr nc, .not_infatuated
+
+ ld hl, InLoveWithText
+ call StdBattleTextbox
+ xor a
+ ld [wNumHits], a
+ ld de, ANIM_IN_LOVE
+ call FarPlayBattleAnimation
+
+ ; 50% chance of infatuation
+ call BattleRandom
+ cp 50 percent + 1
+ jr c, .not_infatuated
+
+ ld hl, InfatuationText
+ call StdBattleTextbox
+ call CantMove
+ jp EndTurn
+
+.not_infatuated
+
+ ; We can't disable a move that doesn't exist.
+ ld a, [wEnemyDisabledMove]
+ and a
+ jr z, .no_disabled_move
+
+ ; Are we using the disabled move?
+ ld hl, wCurEnemyMove
+ cp [hl]
+ jr nz, .no_disabled_move
+
+ call MoveDisabled
+
+ call CantMove
+ jp EndTurn
+
+.no_disabled_move
+
+ ld hl, wEnemyMonStatus
+ bit PAR, [hl]
+ ret z
+
+ ; 25% chance to be fully paralyzed
+ call BattleRandom
+ cp 25 percent
+ ret nc
+
+ ld hl, FullyParalyzedText
+ call StdBattleTextbox
+ call CantMove
+
+ ; fallthrough
+
+EndTurn:
+ ld a, $1
+ ld [wTurnEnded], a
+ jp ResetDamage
+
+MoveDisabled:
+ ; Make sure any charged moves fail
+ ld a, BATTLE_VARS_SUBSTATUS3
+ call GetBattleVarAddr
+ res SUBSTATUS_CHARGED, [hl]
+
+ ld a, BATTLE_VARS_MOVE
+ call GetBattleVar
+ ld [wNamedObjectIndexBuffer], a
+ call GetMoveName
+
+ ld hl, DisabledMoveText
+ jp StdBattleTextbox
+
+HitConfusion:
+ ld hl, HurtItselfText
+ call StdBattleTextbox
+
+ xor a
+ ld [wCriticalHit], a
+
+ call HitSelfInConfusion
+ call BattleCommand_DamageCalc
+ call BattleCommand_LowerSub
+
+ xor a
+ ld [wNumHits], a
+
+ ; Flicker the monster pic unless flying or underground.
+ ld de, ANIM_HIT_CONFUSION
+ ld a, BATTLE_VARS_SUBSTATUS3_OPP
+ call GetBattleVar
+ and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND
+ call z, PlayFXAnimID
+
+ ld hl, UpdatePlayerHUD
+ call CallBattleCore
+ ld a, $1
+ ldh [hBGMapMode], a
+ ld c, TRUE
+ call DoPlayerDamage
+ jp BattleCommand_RaiseSub
+
+BattleCommand_CheckObedience:
+; checkobedience
+
+ ; Enemy can't disobey
+ ldh a, [hBattleTurn]
+ and a
+ ret nz
+
+ call CheckUserIsCharging
+ ret nz
+
+ ; If we've already checked this turn
+ ld a, [wAlreadyDisobeyed]
+ and a
+ ret nz
+
+ xor a
+ ld [wAlreadyDisobeyed], a
+
+ ; No obedience in link battles
+ ; (since no handling exists for enemy)
+ ld a, [wLinkMode]
+ and a
+ ret nz
+
+ ; If the monster's id doesn't match the player's,
+ ; some conditions need to be met.
+ ld a, MON_ID
+ call BattlePartyAttr
+
+ ld a, [wPlayerID]
+ cp [hl]
+ jr nz, .obeylevel
+ inc hl
+ ld a, [wPlayerID + 1]
+ cp [hl]
+ ret z
+
+.obeylevel
+ ; The maximum obedience level is constrained by owned badges:
+ ld hl, wJohtoBadges
+
+ ; risingbadge
+ bit RISINGBADGE, [hl]
+ ld a, MAX_LEVEL + 1
+ jr nz, .getlevel
+
+ ; stormbadge
+ bit STORMBADGE, [hl]
+ ld a, 70
+ jr nz, .getlevel
+
+ ; fogbadge
+ bit FOGBADGE, [hl]
+ ld a, 50
+ jr nz, .getlevel
+
+ ; hivebadge
+ bit HIVEBADGE, [hl]
+ ld a, 30
+ jr nz, .getlevel
+
+ ; no badges
+ ld a, 10
+
+.getlevel
+; c = obedience level
+; d = monster level
+; b = c + d
+
+ ld b, a
+ ld c, a
+
+ ld a, [wBattleMonLevel]
+ ld d, a
+
+ add b
+ ld b, a
+
+; No overflow (this should never happen)
+ jr nc, .checklevel
+ ld b, $ff
+
+.checklevel
+; If the monster's level is lower than the obedience level, it will obey.
+ ld a, c
+ cp d
+ ret nc
+
+; Random number from 0 to obedience level + monster level
+.rand1
+ call BattleRandom
+ swap a
+ cp b
+ jr nc, .rand1
+
+; The higher above the obedience level the monster is,
+; the more likely it is to disobey.
+ cp c
+ ret c
+
+; Sleep-only moves have separate handling, and a higher chance of
+; being ignored. Lazy monsters like their sleep.
+ call IgnoreSleepOnly
+ ret c
+
+; Another random number from 0 to obedience level + monster level
+.rand2
+ call BattleRandom
+ cp b
+ jr nc, .rand2
+
+; A second chance.
+ cp c
+ jr c, .UseInstead
+
+; No hope of using a move now.
+
+; b = number of levels the monster is above the obedience level
+ ld a, d
+ sub c
+ ld b, a
+
+; The chance of napping is the difference out of 256.
+ call BattleRandom
+ swap a
+ sub b
+ jr c, .Nap
+
+; The chance of not hitting itself is the same.
+ cp b
+ jr nc, .DoNothing
+
+ ld hl, WontObeyText
+ call StdBattleTextbox
+ call HitConfusion
+ jp .EndDisobedience
+
+.Nap:
+ call BattleRandom
+ add a
+ swap a
+ and SLP
+ jr z, .Nap
+
+ ld [wBattleMonStatus], a
+
+ ld hl, BeganToNapText
+ jr .Print
+
+.DoNothing:
+ ; 4 random choices
+ call BattleRandom
+ and %11
+
+ ld hl, LoafingAroundText
+ and a ; 0
+ jr z, .Print
+
+ ld hl, WontObeyText
+ dec a ; 1
+ jr z, .Print
+
+ ld hl, TurnedAwayText
+ dec a ; 2
+ jr z, .Print
+
+ ld hl, IgnoredOrdersText
+
+.Print:
+ call StdBattleTextbox
+ jp .EndDisobedience
+
+.UseInstead:
+; Can't use another move if the monster only has one!
+ ld a, [wBattleMonMoves + 1]
+ and a
+ jr z, .DoNothing
+
+; Don't bother trying to handle Disable.
+ ld a, [wDisabledMove]
+ and a
+ jr nz, .DoNothing
+
+ ld hl, wBattleMonPP
+ ld de, wBattleMonMoves
+ ld b, 0
+ ld c, NUM_MOVES
+
+.GetTotalPP:
+ ld a, [hli]
+ and PP_MASK
+ add b
+ ld b, a
+
+ dec c
+ jr z, .CheckMovePP
+
+; Stop at undefined moves.
+ inc de
+ ld a, [de]
+ and a
+ jr nz, .GetTotalPP
+
+.CheckMovePP:
+ ld hl, wBattleMonPP
+ ld a, [wCurMoveNum]
+ ld e, a
+ ld d, 0
+ add hl, de
+
+; Can't use another move if only one move has PP.
+ ld a, [hl]
+ and PP_MASK
+ cp b
+ jr z, .DoNothing
+
+; Make sure we can actually use the move once we get there.
+ ld a, 1
+ ld [wAlreadyDisobeyed], a
+
+ ld a, [w2DMenuNumRows]
+ ld b, a
+
+; Save the move we originally picked for afterward.
+ ld a, [wCurMoveNum]
+ ld c, a
+ push af
+
+.RandomMove:
+ call BattleRandom
+ maskbits NUM_MOVES
+
+ cp b
+ jr nc, .RandomMove
+
+; Not the move we were trying to use.
+ cp c
+ jr z, .RandomMove
+
+; Make sure it has PP.
+ ld [wCurMoveNum], a
+ ld hl, wBattleMonPP
+ ld e, a
+ ld d, 0
+ add hl, de
+ ld a, [hl]
+ and PP_MASK
+ jr z, .RandomMove
+
+; Use it.
+ ld a, [wCurMoveNum]
+ ld c, a
+ ld b, 0
+ ld hl, wBattleMonMoves
+ add hl, bc
+ ld a, [hl]
+ ld [wCurPlayerMove], a
+
+ call SetPlayerTurn
+ call UpdateMoveData
+ call DoMove
+
+; Restore original move choice.
+ pop af
+ ld [wCurMoveNum], a
+
+.EndDisobedience:
+ xor a
+ ld [wLastPlayerMove], a
+ ld [wLastPlayerCounterMove], a
+
+ ; Break Encore too.
+ ld hl, wPlayerSubStatus5
+ res SUBSTATUS_ENCORED, [hl]
+ xor a
+ ld [wPlayerEncoreCount], a
+
+ jp EndMoveEffect
+
+IgnoreSleepOnly:
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+
+ ; Snore and Sleep Talk bypass sleep.
+ cp SNORE
+ jr z, .CheckSleep
+ cp SLEEP_TALK
+ jr z, .CheckSleep
+ and a
+ ret
+
+.CheckSleep:
+ ld a, BATTLE_VARS_STATUS
+ call GetBattleVar
+ and SLP
+ ret z
+
+; 'ignored orders…sleeping!'
+ ld hl, IgnoredSleepingText
+ call StdBattleTextbox
+
+ call EndMoveEffect
+
+ scf
+ ret
+
+INCLUDE "engine/battle/used_move_text.asm"
+
+BattleCommand_DoTurn:
+ call CheckUserIsCharging
+ ret nz
+
+ ld hl, wBattleMonPP
+ ld de, wPlayerSubStatus3
+ ld bc, wPlayerTurnsTaken
+
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .proceed
+
+ ld hl, wEnemyMonPP
+ ld de, wEnemySubStatus3
+ ld bc, wEnemyTurnsTaken
+
+.proceed
+
+; If we've gotten this far, this counts as a turn.
+ ld a, [bc]
+ inc a
+ ld [bc], a
+
+ ld a, BATTLE_VARS_MOVE
+ call GetBattleVar
+ cp STRUGGLE
+ ret z
+
+ ld a, [de]
+ and 1 << SUBSTATUS_IN_LOOP | 1 << SUBSTATUS_RAMPAGE | 1 << SUBSTATUS_BIDE
+ ret nz
+
+ call .consume_pp
+ ld a, b
+ and a
+ jp nz, EndMoveEffect
+
+ ; SubStatus5
+ inc de
+ inc de
+
+ ld a, [de]
+ bit SUBSTATUS_TRANSFORMED, a
+ ret nz
+
+ ldh a, [hBattleTurn]
+ and a
+
+ ld hl, wPartyMon1PP
+ ld a, [wCurBattleMon]
+ jr z, .player
+
+; mimic this part entirely if wildbattle
+ ld a, [wBattleMode]
+ dec a
+ jr z, .wild
+
+ ld hl, wOTPartyMon1PP
+ ld a, [wCurOTMon]
+
+.player
+ call GetPartyLocation
+ push hl
+ call CheckMimicUsed
+ pop hl
+ ret c
+
+.consume_pp
+ ldh a, [hBattleTurn]
+ and a
+ ld a, [wCurMoveNum]
+ jr z, .okay
+ ld a, [wCurEnemyMoveNum]
+
+.okay
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld a, [hl]
+ and PP_MASK
+ jr z, .out_of_pp
+ dec [hl]
+ ld b, 0
+ ret
+
+.wild
+ ld hl, wEnemyMonMoves
+ ld a, [wCurEnemyMoveNum]
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld a, [hl]
+ cp MIMIC
+ jr z, .mimic
+ ld hl, wWildMonMoves
+ add hl, bc
+ ld a, [hl]
+ cp MIMIC
+ ret z
+
+.mimic
+ ld hl, wWildMonPP
+ call .consume_pp
+ ret
+
+.out_of_pp
+ call BattleCommand_MoveDelay
+; 'has no pp left for [move]'
+ ld hl, HasNoPPLeftText
+; get move effect
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+; continuous?
+ cp EFFECT_RAZOR_WIND
+ jr z, .print
+
+ cp EFFECT_SKY_ATTACK
+ jr z, .print
+
+ cp EFFECT_SKULL_BASH
+ jr z, .print
+
+ cp EFFECT_SOLARBEAM
+ jr z, .print
+
+ cp EFFECT_FLY
+ jr z, .print
+
+ cp EFFECT_ROLLOUT
+ jr z, .print
+
+ cp EFFECT_BIDE
+ jr z, .print
+
+ cp EFFECT_RAMPAGE
+ jr z, .print
+
+; 'but no pp is left for the move'
+ ld hl, NoPPLeftText
+.print
+ call StdBattleTextbox
+ ld b, 1
+ ret
+
+CheckMimicUsed:
+ ldh a, [hBattleTurn]
+ and a
+ ld a, [wCurMoveNum]
+ jr z, .player
+ ld a, [wCurEnemyMoveNum]
+
+.player
+ ld c, a
+ ld a, MON_MOVES
+ call UserPartyAttr
+
+ ld a, BATTLE_VARS_MOVE
+ call GetBattleVar
+ cp MIMIC
+ jr z, .mimic
+;
+ ld b, 0
+ add hl, bc
+ ld a, [hl]
+ cp MIMIC
+ jr nz, .mimic
+
+ scf
+ ret
+
+.mimic
+ and a
+ ret
+
+BattleCommand_Critical:
+; critical
+
+; Determine whether this attack's hit will be critical.
+
+ xor a
+ ld [wCriticalHit], a
+
+ ld a, BATTLE_VARS_MOVE_POWER
+ call GetBattleVar
+ and a
+ ret z
+
+ ldh a, [hBattleTurn]
+ and a
+ ld hl, wEnemyMonItem
+ ld a, [wEnemyMonSpecies]
+ jr nz, .Item
+ ld hl, wBattleMonItem
+ ld a, [wBattleMonSpecies]
+
+.Item:
+ ld c, 0
+
+ cp CHANSEY
+ jr nz, .Farfetchd
+ ld a, [hl]
+ cp LUCKY_PUNCH
+ jr nz, .FocusEnergy
+
+; +2 critical level
+ ld c, 2
+ jr .Tally
+
+.Farfetchd:
+ cp FARFETCH_D
+ jr nz, .FocusEnergy
+ ld a, [hl]
+ cp STICK
+ jr nz, .FocusEnergy
+
+; +2 critical level
+ ld c, 2
+ jr .Tally
+
+.FocusEnergy:
+ ld a, BATTLE_VARS_SUBSTATUS4
+ call GetBattleVar
+ bit SUBSTATUS_FOCUS_ENERGY, a
+ jr z, .CheckCritical
+
+; +1 critical level
+ inc c
+
+.CheckCritical:
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+ ld de, 1
+ ld hl, CriticalHitMoves
+ push bc
+ call IsInArray
+ pop bc
+ jr nc, .ScopeLens
+
+; +2 critical level
+ inc c
+ inc c
+
+.ScopeLens:
+ push bc
+ call GetUserItem
+ ld a, b
+ cp HELD_CRITICAL_UP ; Increased critical chance. Only Scope Lens has this.
+ pop bc
+ jr nz, .Tally
+
+; +1 critical level
+ inc c
+
+.Tally:
+ ld hl, CriticalHitChances
+ ld b, 0
+ add hl, bc
+ call BattleRandom
+ cp [hl]
+ ret nc
+ ld a, 1
+ ld [wCriticalHit], a
+ ret
+
+INCLUDE "data/moves/critical_hit_moves.asm"
+
+INCLUDE "data/battle/critical_hit_chances.asm"
+
+INCLUDE "engine/battle/move_effects/triple_kick.asm"