summaryrefslogtreecommitdiff
path: root/engine
diff options
context:
space:
mode:
Diffstat (limited to 'engine')
-rw-r--r--engine/battle/effect_commands.asm1221
-rw-r--r--engine/battle/move_effects/triple_kick.asm34
-rw-r--r--engine/battle/used_move_text.asm235
-rwxr-xr-xengine/items/item_effects.asm18
4 files changed, 1499 insertions, 9 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"
diff --git a/engine/battle/move_effects/triple_kick.asm b/engine/battle/move_effects/triple_kick.asm
new file mode 100644
index 00000000..e41044c9
--- /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
diff --git a/engine/battle/used_move_text.asm b/engine/battle/used_move_text.asm
new file mode 100644
index 00000000..5b148ab5
--- /dev/null
+++ b/engine/battle/used_move_text.asm
@@ -0,0 +1,235 @@
+BattleCommand_UsedMoveText:
+; battle command 03
+ ld hl, UsedMoveText
+ call PrintText
+ jp WaitBGMap
+
+UsedMoveText:
+; this is a stream of text and asm from 105db9 to 105ef6
+ text_far _ActorNameText
+ text_asm
+ ldh a, [hBattleTurn]
+ and a
+ jr nz, .start
+
+ ld a, [wPlayerMoveStruct + MOVE_ANIM]
+ call UpdateUsedMoves
+
+.start
+ ld a, BATTLE_VARS_LAST_MOVE
+ call GetBattleVarAddr
+ ld d, h
+ ld e, l
+
+ ld a, BATTLE_VARS_LAST_COUNTER_MOVE
+ call GetBattleVarAddr
+
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+ ld [wMoveGrammar], a
+
+ call CheckUserIsCharging
+ jr nz, .grammar
+
+ ; update last move
+ ld a, [wMoveGrammar]
+ ld [hl], a
+ ld [de], a
+
+.grammar
+ call GetMoveGrammar ; convert move id to grammar index
+
+; everything except 'CheckObedience' made redundant in localization
+
+ ; check obedience
+ ld a, [wAlreadyDisobeyed]
+ and a
+ ld hl, UsedMove2Text
+ ret nz
+
+ ; check move grammar
+ ld a, [wMoveGrammar]
+ cp $3
+ ld hl, UsedMove2Text
+ ret c
+ ld hl, UsedMove1Text
+ ret
+
+UsedMove1Text:
+ text_far _UsedMove1Text
+ text_asm
+ jr UsedMoveText_CheckObedience
+
+UsedMove2Text:
+ text_far _UsedMove2Text
+ text_asm
+UsedMoveText_CheckObedience:
+; check obedience
+ ld a, [wAlreadyDisobeyed]
+ and a
+ jr z, .GetMoveNameText
+; print "instead,"
+ ld hl, .UsedInsteadText
+ ret
+
+.UsedInsteadText:
+ text_far _UsedInsteadText
+ text_asm
+.GetMoveNameText:
+ ld hl, MoveNameText
+ ret
+
+MoveNameText:
+ text_far _MoveNameText
+ text_asm
+; get start address
+ ld hl, .endusedmovetexts
+
+; get move id
+ ld a, [wMoveGrammar]
+
+; 2-byte pointer
+ add a
+
+; seek
+ push bc
+ ld b, 0
+ ld c, a
+ add hl, bc
+ pop bc
+
+; get pointer to usedmovetext ender
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ret
+
+.endusedmovetexts
+; entries correspond to MoveGrammar sets
+ dw EndUsedMove1Text
+ dw EndUsedMove2Text
+ dw EndUsedMove3Text
+ dw EndUsedMove4Text
+ dw EndUsedMove5Text
+
+EndUsedMove1Text:
+ text_far _EndUsedMove1Text
+ text_end
+
+EndUsedMove2Text:
+ text_far _EndUsedMove2Text
+ text_end
+
+EndUsedMove3Text:
+ text_far _EndUsedMove3Text
+ text_end
+
+EndUsedMove4Text:
+ text_far _EndUsedMove4Text
+ text_end
+
+EndUsedMove5Text:
+ text_far _EndUsedMove5Text
+ text_end
+
+GetMoveGrammar:
+; store move grammar type in wMoveGrammar
+
+ push bc
+; wMoveGrammar contains move id
+ ld a, [wMoveGrammar]
+ ld c, a ; move id
+ ld b, 0 ; grammar index
+
+; read grammar table
+ ld hl, MoveGrammar
+.loop
+ ld a, [hli]
+; end of table?
+ cp -1
+ jr z, .end
+; match?
+ cp c
+ jr z, .end
+; advance grammar type at 0
+ and a
+ jr nz, .loop
+; next grammar type
+ inc b
+ jr .loop
+
+.end
+; wMoveGrammar now contains move grammar
+ ld a, b
+ ld [wMoveGrammar], a
+
+; we're done
+ pop bc
+ ret
+
+INCLUDE "data/moves/grammar.asm"
+
+CheckUserIsCharging:
+ ldh a, [hBattleTurn]
+ and a
+ ld a, [wPlayerCharging] ; player
+ jr z, .end
+ ld a, [wEnemyCharging] ; enemy
+.end
+ and a
+ ret
+
+UpdateUsedMoves:
+; append move a to wPlayerUsedMoves unless it has already been used
+
+ push bc
+; start of list
+ ld hl, wPlayerUsedMoves
+; get move id
+ ld b, a
+; next count
+ ld c, NUM_MOVES
+
+.loop
+; get move from the list
+ ld a, [hli]
+; not used yet?
+ and a
+ jr z, .add
+; already used?
+ cp b
+ jr z, .quit
+; next byte
+ dec c
+ jr nz, .loop
+
+; if the list is full and the move hasn't already been used
+; shift the list back one byte, deleting the first move used
+; this can occur with struggle or a new learned move
+ ld hl, wPlayerUsedMoves + 1
+; 1 = 2
+ ld a, [hld]
+ ld [hli], a
+; 2 = 3
+ inc hl
+ ld a, [hld]
+ ld [hli], a
+; 3 = 4
+ inc hl
+ ld a, [hld]
+ ld [hl], a
+; 4 = new move
+ ld a, b
+ ld [wPlayerUsedMoves + 3], a
+ jr .quit
+
+.add
+; go back to the byte we just inced from
+ dec hl
+; add the new move
+ ld [hl], b
+
+.quit
+; list updated
+ pop bc
+ ret
diff --git a/engine/items/item_effects.asm b/engine/items/item_effects.asm
index f50f7fda..8d694eac 100755
--- a/engine/items/item_effects.asm
+++ b/engine/items/item_effects.asm
@@ -387,7 +387,7 @@ UltraBall: ; e926
jr c, .asm_ea34
ld a, $5
.asm_ea34
- ld [wcb67], a
+ ld [wBattleAnimParam], a
ld de, ANIM_THROW_POKE_BALL
ld a, e
ld [wcf3e], a
@@ -396,7 +396,7 @@ UltraBall: ; e926
xor a
ldh [hBattleTurn], a
ld [wBuffer2], a
- ld [wcf46], a
+ ld [wNumHits], a
predef PlayBattleAnim
ld a, [wWildMon]
and a
@@ -2018,8 +2018,8 @@ XSpeed: ; f515
ld b, [hl]
xor a
ldh [hBattleTurn], a
- ld [wcb45], a
- ld [wcbeb], a
+ ld [wAttackMissed], a
+ ld [wEffectFailed], a
farcall RaiseStat
call WaitSFX
farcall BattleCommand_StatUpMessage
@@ -2178,10 +2178,10 @@ PPUp: ; f606 (3:7606)
ld hl, Text_RestoreThePPOfWhichMove
.asm_f62e
call PrintText
- ld a, [wcfc7]
+ ld a, [wCurMoveNum]
push af
xor a
- ld [wcfc7], a
+ ld [wCurMoveNum], a
ld a, $2
ld [wd11f], a
ld a, $f
@@ -2189,7 +2189,7 @@ PPUp: ; f606 (3:7606)
rst FarCall
pop bc
ld a, b
- ld [wcfc7], a
+ ld [wCurMoveNum], a
jr nz, .asm_f60c
ld hl, wPartyMon1Moves
ld bc, $30
@@ -2541,9 +2541,9 @@ Functionf7e7: ; f7e7 (3:77e7)
ld a, d
ld [wcf3f], a
xor a
- ld [wcb67], a
+ ld [wBattleAnimParam], a
ldh [hBattleTurn], a
- ld [wcf46], a
+ ld [wNumHits], a
predef PlayBattleAnim
ld hl, Text_BlockedTheBall
call PrintText