summaryrefslogtreecommitdiff
path: root/engine
diff options
context:
space:
mode:
authorentrpntr <entrpntr@gmail.com>2020-04-19 12:31:56 -0400
committerentrpntr <entrpntr@gmail.com>2020-04-19 12:31:56 -0400
commite953302d3f81f080fd5d8423000496ce2fad36d3 (patch)
treebb66787122ec1029524153c96c3f34b66780e5c7 /engine
parente10598eb204358343491c92348b9220185e3b33d (diff)
Continue with effect commands (about 50% complete).
Diffstat (limited to 'engine')
-rw-r--r--engine/battle/effect_commands.asm2352
-rw-r--r--engine/battle/move_effects/beat_up.asm220
-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/destiny_bond.asm9
-rw-r--r--engine/battle/move_effects/encore.asm120
-rw-r--r--engine/battle/move_effects/false_swipe.asm48
-rw-r--r--engine/battle/move_effects/heal_bell.asm34
-rw-r--r--engine/battle/move_effects/lock_on.asm21
-rw-r--r--engine/battle/move_effects/pain_split.asm92
-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/spite.asm86
-rwxr-xr-xengine/items/item_effects.asm8
15 files changed, 3380 insertions, 4 deletions
diff --git a/engine/battle/effect_commands.asm b/engine/battle/effect_commands.asm
index 023730ac..7734c66f 100644
--- a/engine/battle/effect_commands.asm
+++ b/engine/battle/effect_commands.asm
@@ -1219,3 +1219,2355 @@ INCLUDE "data/moves/critical_hit_moves.asm"
INCLUDE "data/battle/critical_hit_chances.asm"
INCLUDE "engine/battle/move_effects/triple_kick.asm"
+
+BattleCommand_Stab:
+; STAB = Same Type Attack Bonus
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+ cp STRUGGLE
+ ret z
+
+ ld hl, wBattleMonType1
+ ld a, [hli]
+ ld b, a
+ ld c, [hl]
+ ld hl, wEnemyMonType1
+ ld a, [hli]
+ ld d, a
+ ld e, [hl]
+
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .go ; Who Attacks and who Defends
+
+ ld hl, wEnemyMonType1
+ ld a, [hli]
+ ld b, a
+ ld c, [hl]
+ ld hl, wBattleMonType1
+ ld a, [hli]
+ ld d, a
+ ld e, [hl]
+
+.go
+ ld a, BATTLE_VARS_MOVE_TYPE
+ call GetBattleVarAddr
+ ld [wCurType], a
+
+ push hl
+ push de
+ push bc
+ farcall DoWeatherModifiers
+ pop bc
+ pop de
+ pop hl
+
+ push de
+ push bc
+ farcall DoBadgeTypeBoosts
+ pop bc
+ pop de
+
+ ld a, [wCurType]
+ cp b
+ jr z, .stab
+ cp c
+ jr z, .stab
+
+ jr .SkipStab
+
+.stab
+ ld hl, wCurDamage + 1
+ ld a, [hld]
+ ld h, [hl]
+ ld l, a
+
+ ld b, h
+ ld c, l
+ srl b
+ rr c
+ add hl, bc
+
+ ld a, h
+ ld [wCurDamage], a
+ ld a, l
+ ld [wCurDamage + 1], a
+
+ ld hl, wTypeModifier
+ set 7, [hl]
+
+.SkipStab:
+ ld a, BATTLE_VARS_MOVE_TYPE
+ call GetBattleVar
+ ld b, a
+ ld hl, TypeMatchups
+
+.TypesLoop:
+ ld a, [hli]
+
+ cp -1
+ jr z, .end
+
+ ; foresight
+ cp -2
+ jr nz, .SkipForesightCheck
+ ld a, BATTLE_VARS_SUBSTATUS1_OPP
+ call GetBattleVar
+ bit SUBSTATUS_IDENTIFIED, a
+ jr nz, .end
+
+ jr .TypesLoop
+
+.SkipForesightCheck:
+ cp b
+ jr nz, .SkipType
+ ld a, [hl]
+ cp d
+ jr z, .GotMatchup
+ cp e
+ jr z, .GotMatchup
+ jr .SkipType
+
+.GotMatchup:
+ push hl
+ push bc
+ inc hl
+ ld a, [wTypeModifier]
+ and %10000000
+ ld b, a
+; If the target is immune to the move, treat it as a miss and calculate the damage as 0
+ ld a, [hl]
+ and a
+ jr nz, .NotImmune
+ inc a
+ ld [wAttackMissed], a
+ xor a
+.NotImmune:
+ ldh [hMultiplier], a
+ add b
+ ld [wTypeModifier], a
+
+ xor a
+ ldh [hMultiplicand + 0], a
+
+ ld hl, wCurDamage
+ ld a, [hli]
+ ldh [hMultiplicand + 1], a
+ ld a, [hld]
+ ldh [hMultiplicand + 2], a
+
+ call Multiply
+
+ ldh a, [hProduct + 1]
+ ld b, a
+ ldh a, [hProduct + 2]
+ or b
+ ld b, a
+ ldh a, [hProduct + 3]
+ or b
+ jr z, .ok ; This is a very convoluted way to get back that we've essentially dealt no damage.
+
+; Take the product and divide it by 10.
+ ld a, 10
+ ldh [hDivisor], a
+ ld b, 4
+ call Divide
+ ldh a, [hQuotient + 2]
+ ld b, a
+ ldh a, [hQuotient + 3]
+ or b
+ jr nz, .ok
+
+ ld a, 1
+ ldh [hMultiplicand + 2], a
+
+.ok
+ ldh a, [hMultiplicand + 1]
+ ld [hli], a
+ ldh a, [hMultiplicand + 2]
+ ld [hl], a
+ pop bc
+ pop hl
+
+.SkipType:
+ inc hl
+ inc hl
+ jr .TypesLoop
+
+.end
+ call BattleCheckTypeMatchup
+ ld a, [wTypeMatchup]
+ ld b, a
+ ld a, [wTypeModifier]
+ and %10000000
+ or b
+ ld [wTypeModifier], a
+ ret
+
+BattleCheckTypeMatchup:
+ ld hl, wEnemyMonType1
+ ldh a, [hBattleTurn]
+ and a
+ jr z, CheckTypeMatchup
+ ld hl, wBattleMonType1
+CheckTypeMatchup:
+; There is an incorrect assumption about this function made in the AI related code: when
+; the AI calls CheckTypeMatchup (not BattleCheckTypeMatchup), it assumes that placing the
+; offensive type in a will make this function do the right thing. Since a is overwritten,
+; this assumption is incorrect. A simple fix would be to load the move type for the
+; current move into a in BattleCheckTypeMatchup, before falling through, which is
+; consistent with how the rest of the code assumes this code works like.
+ push hl
+ push de
+ push bc
+ ld a, BATTLE_VARS_MOVE_TYPE
+ call GetBattleVar
+ ld d, a
+ ld b, [hl]
+ inc hl
+ ld c, [hl]
+ ld a, 10 ; 1.0
+ ld [wTypeMatchup], a
+ ld hl, TypeMatchups
+.TypesLoop:
+ ld a, [hli]
+ cp -1
+ jr z, .End
+ cp -2
+ jr nz, .Next
+ ld a, BATTLE_VARS_SUBSTATUS1_OPP
+ call GetBattleVar
+ bit SUBSTATUS_IDENTIFIED, a
+ jr nz, .End
+ jr .TypesLoop
+
+.Next:
+ cp d
+ jr nz, .Nope
+ ld a, [hli]
+ cp b
+ jr z, .Yup
+ cp c
+ jr z, .Yup
+ jr .Nope2
+
+.Nope:
+ inc hl
+.Nope2:
+ inc hl
+ jr .TypesLoop
+
+.Yup:
+ xor a
+ ldh [hDividend + 0], a
+ ldh [hMultiplicand + 0], a
+ ldh [hMultiplicand + 1], a
+ ld a, [hli]
+ ldh [hMultiplicand + 2], a
+ ld a, [wTypeMatchup]
+ ldh [hMultiplier], a
+ call Multiply
+ ld a, 10
+ ldh [hDivisor], a
+ push bc
+ ld b, 4
+ call Divide
+ pop bc
+ ldh a, [hQuotient + 3]
+ ld [wTypeMatchup], a
+ jr .TypesLoop
+
+.End:
+ pop bc
+ pop de
+ pop hl
+ ret
+
+BattleCommand_ResetTypeMatchup:
+; Reset the type matchup multiplier to 1.0, if the type matchup is not 0.
+; If there is immunity in play, the move automatically misses.
+ call BattleCheckTypeMatchup
+ ld a, [wTypeMatchup]
+ and a
+ ld a, 10 ; 1.0
+ jr nz, .reset
+ call ResetDamage
+ xor a
+ ld [wTypeModifier], a
+ inc a
+ ld [wAttackMissed], a
+ ret
+
+.reset
+ ld [wTypeMatchup], a
+ ret
+
+INCLUDE "engine/battle/ai/switch.asm"
+
+INCLUDE "data/types/type_matchups.asm"
+
+BattleCommand_DamageVariation:
+; damagevariation
+
+; Modify the damage spread between 85% and 100%.
+
+; Because of the method of division the probability distribution
+; is not consistent. This makes the highest damage multipliers
+; rarer than normal.
+
+; No point in reducing 1 or 0 damage.
+ ld hl, wCurDamage
+ ld a, [hli]
+ and a
+ jr nz, .go
+ ld a, [hl]
+ cp 2
+ ret c
+
+.go
+; Start with the maximum damage.
+ xor a
+ ldh [hMultiplicand + 0], a
+ dec hl
+ ld a, [hli]
+ ldh [hMultiplicand + 1], a
+ ld a, [hl]
+ ldh [hMultiplicand + 2], a
+
+; Multiply by 85-100%...
+.loop
+ call BattleRandom
+ rrca
+ cp 85 percent + 1
+ jr c, .loop
+
+ ldh [hMultiplier], a
+ call Multiply
+
+; ...divide by 100%...
+ ld a, $ff ; 100%
+ ldh [hDivisor], a
+ ld b, $4
+ call Divide
+
+; ...to get .85-1.00x damage.
+ ldh a, [hQuotient + 2]
+ ld hl, wCurDamage
+ ld [hli], a
+ ldh a, [hQuotient + 3]
+ ld [hl], a
+ ret
+
+BattleCommand_CheckHit:
+; checkhit
+
+ call .DreamEater
+ jp z, .Miss
+
+ call .Protect
+ jp nz, .Miss
+
+ call .DrainSub
+ jp z, .Miss
+
+ call .LockOn
+ ret nz
+
+ call .FlyDigMoves
+ jp nz, .Miss
+
+ call .ThunderRain
+ ret z
+
+ call .XAccuracy
+ ret nz
+
+ ; Perfect-accuracy moves
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ cp EFFECT_ALWAYS_HIT
+ ret z
+
+ call .StatModifiers
+
+ ld a, [wPlayerMoveStruct + MOVE_ACC]
+ ld b, a
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .BrightPowder
+ ld a, [wEnemyMoveStruct + MOVE_ACC]
+ ld b, a
+
+.BrightPowder:
+ push bc
+ call GetOpponentItem
+ ld a, b
+ cp HELD_BRIGHTPOWDER
+ ld a, c ; % miss
+ pop bc
+ jr nz, .skip_brightpowder
+
+ ld c, a
+ ld a, b
+ sub c
+ ld b, a
+ jr nc, .skip_brightpowder
+ ld b, 0
+
+.skip_brightpowder
+ ld a, b
+ cp -1
+ jr z, .Hit
+
+ call BattleRandom
+ cp b
+ jr nc, .Miss
+
+.Hit:
+ ret
+
+.Miss:
+; Keep the damage value intact if we're using (Hi) Jump Kick.
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ cp EFFECT_JUMP_KICK
+ jr z, .Missed
+ call ResetDamage
+
+.Missed:
+ ld a, 1
+ ld [wAttackMissed], a
+ ret
+
+.DreamEater:
+; Return z if we're trying to eat the dream of
+; a monster that isn't sleeping.
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ cp EFFECT_DREAM_EATER
+ ret nz
+
+ ld a, BATTLE_VARS_STATUS_OPP
+ call GetBattleVar
+ and SLP
+ ret
+
+.Protect:
+; Return nz if the opponent is protected.
+ ld a, BATTLE_VARS_SUBSTATUS1_OPP
+ call GetBattleVar
+ bit SUBSTATUS_PROTECT, a
+ ret z
+
+ ld c, 40
+ call DelayFrames
+
+; 'protecting itself!'
+ ld hl, ProtectingItselfText
+ call StdBattleTextbox
+
+ ld c, 40
+ call DelayFrames
+
+ ld a, 1
+ and a
+ ret
+
+.LockOn:
+; Return nz if we are locked-on and aren't trying to use Earthquake,
+; Fissure or Magnitude on a monster that is flying.
+ ld a, BATTLE_VARS_SUBSTATUS5_OPP
+ call GetBattleVarAddr
+ bit SUBSTATUS_LOCK_ON, [hl]
+ res SUBSTATUS_LOCK_ON, [hl]
+ ret z
+
+ ld a, BATTLE_VARS_SUBSTATUS3_OPP
+ call GetBattleVar
+ bit SUBSTATUS_FLYING, a
+ jr z, .LockedOn
+
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+
+ cp EARTHQUAKE
+ ret z
+ cp FISSURE
+ ret z
+ cp MAGNITUDE
+ ret z
+
+.LockedOn:
+ ld a, 1
+ and a
+ ret
+
+.DrainSub:
+; Return z if using an HP drain move on a substitute.
+ call CheckSubstituteOpp
+ jr z, .not_draining_sub
+
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+
+ cp EFFECT_LEECH_HIT
+ ret z
+ cp EFFECT_DREAM_EATER
+ ret z
+
+.not_draining_sub
+ ld a, 1
+ and a
+ ret
+
+.FlyDigMoves:
+; Check for moves that can hit underground/flying opponents.
+; Return z if the current move can hit the opponent.
+
+ ld a, BATTLE_VARS_SUBSTATUS3_OPP
+ call GetBattleVar
+ and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND
+ ret z
+
+ bit SUBSTATUS_FLYING, a
+ jr z, .DigMoves
+
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+
+ cp GUST
+ ret z
+ cp WHIRLWIND
+ ret z
+ cp THUNDER
+ ret z
+ cp TWISTER
+ ret
+
+.DigMoves:
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+
+ cp EARTHQUAKE
+ ret z
+ cp FISSURE
+ ret z
+ cp MAGNITUDE
+ ret
+
+.ThunderRain:
+; Return z if the current move always hits in rain, and it is raining.
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ cp EFFECT_THUNDER
+ ret nz
+
+ ld a, [wBattleWeather]
+ cp WEATHER_RAIN
+ ret
+
+.XAccuracy:
+ ld a, BATTLE_VARS_SUBSTATUS4
+ call GetBattleVar
+ bit SUBSTATUS_X_ACCURACY, a
+ ret
+
+.StatModifiers:
+ ldh a, [hBattleTurn]
+ and a
+
+ ; load the user's accuracy into b and the opponent's evasion into c.
+ ld hl, wPlayerMoveStruct + MOVE_ACC
+ ld a, [wPlayerAccLevel]
+ ld b, a
+ ld a, [wEnemyEvaLevel]
+ ld c, a
+
+ jr z, .got_acc_eva
+
+ ld hl, wEnemyMoveStruct + MOVE_ACC
+ ld a, [wEnemyAccLevel]
+ ld b, a
+ ld a, [wPlayerEvaLevel]
+ ld c, a
+
+.got_acc_eva
+ cp b
+ jr c, .skip_foresight_check
+
+ ; if the target's evasion is greater than the user's accuracy,
+ ; check the target's foresight status
+ ld a, BATTLE_VARS_SUBSTATUS1_OPP
+ call GetBattleVar
+ bit SUBSTATUS_IDENTIFIED, a
+ ret nz
+
+.skip_foresight_check
+ ; subtract evasion from 14
+ ld a, MAX_STAT_LEVEL + 1
+ sub c
+ ld c, a
+ ; store the base move accuracy for math ops
+ xor a
+ ldh [hMultiplicand + 0], a
+ ldh [hMultiplicand + 1], a
+ ld a, [hl]
+ ldh [hMultiplicand + 2], a
+ push hl
+ ld d, 2 ; do this twice, once for the user's accuracy and once for the target's evasion
+
+.accuracy_loop
+ ; look up the multiplier from the table
+ push bc
+ ld hl, AccuracyLevelMultipliers
+ dec b
+ sla b
+ ld c, b
+ ld b, 0
+ add hl, bc
+ pop bc
+ ; multiply by the first byte in that row...
+ ld a, [hli]
+ ldh [hMultiplier], a
+ call Multiply
+ ; ... and divide by the second byte
+ ld a, [hl]
+ ldh [hDivisor], a
+ ld b, 4
+ call Divide
+ ; minimum accuracy is $0001
+ ldh a, [hQuotient + 3]
+ ld b, a
+ ldh a, [hQuotient + 2]
+ or b
+ jr nz, .min_accuracy
+ ldh [hQuotient + 2], a
+ ld a, 1
+ ldh [hQuotient + 3], a
+
+.min_accuracy
+ ; do the same thing to the target's evasion
+ ld b, c
+ dec d
+ jr nz, .accuracy_loop
+
+ ; if the result is more than 2 bytes, max out at 100%
+ ldh a, [hQuotient + 2]
+ and a
+ ldh a, [hQuotient + 3]
+ jr z, .finish_accuracy
+ ld a, $ff
+
+.finish_accuracy
+ pop hl
+ ld [hl], a
+ ret
+
+INCLUDE "data/battle/accuracy_multipliers.asm"
+
+BattleCommand_EffectChance:
+; effectchance
+
+ xor a
+ ld [wEffectFailed], a
+ call CheckSubstituteOpp
+ jr nz, .failed
+
+ push hl
+ ld hl, wPlayerMoveStruct + MOVE_CHANCE
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_move_chance
+ ld hl, wEnemyMoveStruct + MOVE_CHANCE
+.got_move_chance
+
+ ; BUG: 1/256 chance to fail even for a 100% effect chance,
+ ; since carry is not set if BattleRandom == [hl] == 255
+ call BattleRandom
+ cp [hl]
+ pop hl
+ ret c
+
+.failed
+ ld a, 1
+ ld [wEffectFailed], a
+ and a
+ ret
+
+BattleCommand_LowerSub:
+; lowersub
+
+ ld a, BATTLE_VARS_SUBSTATUS4
+ call GetBattleVar
+ bit SUBSTATUS_SUBSTITUTE, a
+ ret z
+
+ ld a, BATTLE_VARS_SUBSTATUS3
+ call GetBattleVar
+ bit SUBSTATUS_CHARGED, a
+ jr nz, .already_charged
+
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ cp EFFECT_RAZOR_WIND
+ jr z, .charge_turn
+ cp EFFECT_SKY_ATTACK
+ jr z, .charge_turn
+ cp EFFECT_SKULL_BASH
+ jr z, .charge_turn
+ cp EFFECT_SOLARBEAM
+ jr z, .charge_turn
+ cp EFFECT_FLY
+ jr z, .charge_turn
+
+.already_charged
+ call .Rampage
+ jr z, .charge_turn
+
+ call CheckUserIsCharging
+ ret nz
+
+.charge_turn
+ ; check battle scene
+ ld a, [wOptions]
+ add a
+ jr c, .mimic_anims
+
+ xor a
+ ld [wNumHits], a
+ ld [wFXAnimID + 1], a
+ inc a
+ ld [wKickCounter], a
+ ld a, SUBSTITUTE
+ jp LoadAnim
+
+.mimic_anims
+ call BattleCommand_LowerSubNoAnim
+ jp BattleCommand_MoveDelay
+
+.Rampage:
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ cp EFFECT_ROLLOUT
+ jr z, .rollout_rampage
+ cp EFFECT_RAMPAGE
+ jr z, .rollout_rampage
+
+ ld a, 1
+ and a
+ ret
+
+.rollout_rampage
+ ld a, [wSomeoneIsRampaging]
+ and a
+ ld a, 0
+ ld [wSomeoneIsRampaging], a
+ ret
+
+BattleCommand_MoveAnim:
+; moveanim
+ call BattleCommand_LowerSub
+ call BattleCommand_MoveAnimNoSub
+ jp BattleCommand_RaiseSub
+
+BattleCommand_MoveAnimNoSub:
+ ld a, [wAttackMissed]
+ and a
+ jp nz, BattleCommand_MoveDelay
+
+ ldh a, [hBattleTurn]
+ and a
+ ld de, wPlayerRolloutCount
+ ld a, BATTLEANIM_ENEMY_DAMAGE
+ jr z, .got_rollout_count
+ ld de, wEnemyRolloutCount
+ ld a, BATTLEANIM_PLAYER_DAMAGE
+
+.got_rollout_count
+ ld [wNumHits], a
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ cp EFFECT_MULTI_HIT
+ jr z, .alternate_anim
+ cp EFFECT_CONVERSION
+ jr z, .alternate_anim
+ cp EFFECT_DOUBLE_HIT
+ jr z, .alternate_anim
+ cp EFFECT_POISON_MULTI_HIT
+ jr z, .alternate_anim
+ cp EFFECT_TRIPLE_KICK
+ jr z, .triplekick
+ xor a
+ ld [wKickCounter], a
+
+.triplekick
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+ ld e, a
+ ld d, 0
+ call PlayFXAnimID
+
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+ cp FLY
+ jr z, .clear_sprite
+ cp DIG
+ ret nz
+.clear_sprite
+ jp AppearUserLowerSub
+
+.alternate_anim
+ ld a, [wKickCounter]
+ and 1
+ xor 1
+ ld [wKickCounter], a
+ ld a, [de]
+ cp 1
+ push af
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+ ld e, a
+ ld d, 0
+ pop af
+ jp z, PlayFXAnimID
+ xor a
+ ld [wNumHits], a
+ jp PlayFXAnimID
+
+BattleCommand_StatUpAnim:
+ ld a, [wAttackMissed]
+ and a
+ jp nz, BattleCommand_MoveDelay
+
+ xor a
+ jr BattleCommand_StatUpDownAnim
+
+BattleCommand_StatDownAnim:
+ ld a, [wAttackMissed]
+ and a
+ jp nz, BattleCommand_MoveDelay
+
+ ldh a, [hBattleTurn]
+ and a
+ ld a, BATTLEANIM_ENEMY_STAT_DOWN
+ jr z, BattleCommand_StatUpDownAnim
+ ld a, BATTLEANIM_WOBBLE
+
+ ; fallthrough
+
+BattleCommand_StatUpDownAnim:
+ ld [wNumHits], a
+ xor a
+ ld [wKickCounter], a
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVar
+ ld e, a
+ ld d, 0
+ jp PlayFXAnimID
+
+BattleCommand_SwitchTurn:
+; switchturn
+
+ ldh a, [hBattleTurn]
+ xor 1
+ ldh [hBattleTurn], a
+ ret
+
+BattleCommand_RaiseSub:
+; raisesub
+
+ ld a, BATTLE_VARS_SUBSTATUS4
+ call GetBattleVar
+ bit SUBSTATUS_SUBSTITUTE, a
+ ret z
+
+ ; check battle scene
+ ld a, [wOptions]
+ add a
+ jp c, BattleCommand_RaiseSubNoAnim
+
+ xor a
+ ld [wNumHits], a
+ ld [wFXAnimID + 1], a
+ ld a, $2
+ ld [wKickCounter], a
+ ld a, SUBSTITUTE
+ jp LoadAnim
+
+BattleCommand_FailureText:
+; failuretext
+; If the move missed or failed, load the appropriate
+; text, and end the effects of multi-turn or multi-
+; hit moves.
+ ld a, [wAttackMissed]
+ and a
+ ret z
+
+ call GetFailureResultText
+ ld a, BATTLE_VARS_MOVE_ANIM
+ call GetBattleVarAddr
+
+ cp FLY
+ jr z, .fly_dig
+ cp DIG
+ jr z, .fly_dig
+
+; Move effect:
+ inc hl
+ ld a, [hl]
+
+ cp EFFECT_MULTI_HIT
+ jr z, .multihit
+ cp EFFECT_DOUBLE_HIT
+ jr z, .multihit
+ cp EFFECT_POISON_MULTI_HIT
+ jr z, .multihit
+ jp EndMoveEffect
+
+.multihit
+ call BattleCommand_RaiseSub
+ jp EndMoveEffect
+
+.fly_dig
+ ld a, BATTLE_VARS_SUBSTATUS3
+ call GetBattleVarAddr
+ res SUBSTATUS_UNDERGROUND, [hl]
+ res SUBSTATUS_FLYING, [hl]
+ call AppearUserRaiseSub
+ jp EndMoveEffect
+
+BattleCommand_ApplyDamage:
+; applydamage
+
+ ld a, BATTLE_VARS_SUBSTATUS1_OPP
+ call GetBattleVar
+ bit SUBSTATUS_ENDURE, a
+ jr z, .focus_band
+
+ call BattleCommand_FalseSwipe
+ ld b, 0
+ jr nc, .damage
+ ld b, 1
+ jr .damage
+
+.focus_band
+ call GetOpponentItem
+ ld a, b
+ cp HELD_FOCUS_BAND
+ ld b, 0
+ jr nz, .damage
+
+ call BattleRandom
+ cp c
+ jr nc, .damage
+ call BattleCommand_FalseSwipe
+ ld b, 0
+ jr nc, .damage
+ ld b, 2
+
+.damage
+ push bc
+ call .update_damage_taken
+ ld c, FALSE
+ ldh a, [hBattleTurn]
+ and a
+ jr nz, .damage_player
+ call DoEnemyDamage
+ jr .done_damage
+
+.damage_player
+ call DoPlayerDamage
+
+.done_damage
+ pop bc
+ ld a, b
+ and a
+ ret z
+
+ dec a
+ jr nz, .focus_band_text
+ ld hl, EnduredText
+ jp StdBattleTextbox
+
+.focus_band_text
+ call GetOpponentItem
+ ld a, [hl]
+ ld [wNamedObjectIndexBuffer], a
+ call GetItemName
+ ld hl, HungOnText
+ jp StdBattleTextbox
+
+.update_damage_taken
+ ld a, BATTLE_VARS_SUBSTATUS4_OPP
+ call GetBattleVar
+ bit SUBSTATUS_SUBSTITUTE, a
+ ret nz
+
+ ld de, wPlayerDamageTaken + 1
+ ldh a, [hBattleTurn]
+ and a
+ jr nz, .got_damage_taken
+ ld de, wEnemyDamageTaken + 1
+
+.got_damage_taken
+ ld a, [wCurDamage + 1]
+ ld b, a
+ ld a, [de]
+ add b
+ ld [de], a
+ dec de
+ ld a, [wCurDamage]
+ ld b, a
+ ld a, [de]
+ adc b
+ ld [de], a
+ ret nc
+ ld a, $ff
+ ld [de], a
+ inc de
+ ld [de], a
+ ret
+
+GetFailureResultText:
+ ld hl, DoesntAffectText
+ ld de, DoesntAffectText
+ ld a, [wTypeModifier]
+ and $7f
+ jr z, .got_text
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ cp EFFECT_FUTURE_SIGHT
+ ld hl, ButItFailedText
+ ld de, ItFailedText
+ jr z, .got_text
+ ld hl, AttackMissedText
+ ld de, AttackMissed2Text
+ ld a, [wCriticalHit]
+ cp -1
+ jr nz, .got_text
+ ld hl, UnaffectedText
+.got_text
+ call FailText_CheckOpponentProtect
+ xor a
+ ld [wCriticalHit], a
+
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ cp EFFECT_JUMP_KICK
+ ret nz
+
+ ld a, [wTypeModifier]
+ and $7f
+ ret z
+
+ ld hl, wCurDamage
+ ld a, [hli]
+ ld b, [hl]
+rept 3
+ srl a
+ rr b
+endr
+ ld [hl], b
+ dec hl
+ ld [hli], a
+ or b
+ jr nz, .do_at_least_1_damage
+ inc a
+ ld [hl], a
+.do_at_least_1_damage
+ ld hl, CrashedText
+ call StdBattleTextbox
+ ld a, $1
+ ld [wKickCounter], a
+ call LoadMoveAnim
+ ld c, TRUE
+ ldh a, [hBattleTurn]
+ and a
+ jp nz, DoEnemyDamage
+ jp DoPlayerDamage
+
+FailText_CheckOpponentProtect:
+ ld a, BATTLE_VARS_SUBSTATUS1_OPP
+ call GetBattleVar
+ bit SUBSTATUS_PROTECT, a
+ jr z, .not_protected
+ ld h, d
+ ld l, e
+.not_protected
+ jp StdBattleTextbox
+
+BattleCommand_BideFailText:
+ ld a, [wAttackMissed]
+ and a
+ ret z
+
+ ld a, [wTypeModifier]
+ and $7f
+ jp z, PrintDoesntAffect
+ jp PrintButItFailed
+
+BattleCommand_CriticalText:
+; criticaltext
+; Prints the message for critical hits or one-hit KOs.
+
+; If there is no message to be printed, wait 20 frames.
+ ld a, [wCriticalHit]
+ and a
+ jr z, .wait
+
+ dec a
+ add a
+ ld hl, .texts
+ ld b, 0
+ ld c, a
+ add hl, bc
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ call StdBattleTextbox
+
+ xor a
+ ld [wCriticalHit], a
+
+.wait
+ ld c, 20
+ jp DelayFrames
+
+.texts
+ dw CriticalHitText
+ dw OneHitKOText
+
+BattleCommand_StartLoop:
+; startloop
+
+ ld hl, wPlayerRolloutCount
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .ok
+ ld hl, wEnemyRolloutCount
+.ok
+ xor a
+ ld [hl], a
+ ret
+
+BattleCommand_SuperEffectiveLoopText:
+; supereffectivelooptext
+
+ ld a, BATTLE_VARS_SUBSTATUS3
+ call GetBattleVarAddr
+ bit SUBSTATUS_IN_LOOP, a
+ ret nz
+
+ ; fallthrough
+
+BattleCommand_SuperEffectiveText:
+; supereffectivetext
+
+ ld a, [wTypeModifier]
+ and $7f
+ cp 10 ; 1.0
+ ret z
+ ld hl, SuperEffectiveText
+ jr nc, .print
+ ld hl, NotVeryEffectiveText
+.print
+ jp StdBattleTextbox
+
+BattleCommand_CheckFaint:
+; checkfaint
+
+; Faint the opponent if its HP reached zero
+; and faint the user along with it if it used Destiny Bond.
+; Ends the move effect if the opponent faints.
+
+ ld hl, wEnemyMonHP
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_hp
+ ld hl, wBattleMonHP
+
+.got_hp
+ ld a, [hli]
+ or [hl]
+ ret nz
+
+ ld a, BATTLE_VARS_SUBSTATUS5_OPP
+ call GetBattleVar
+ bit SUBSTATUS_DESTINY_BOND, a
+ jr z, .no_dbond
+
+ ld hl, TookDownWithItText
+ call StdBattleTextbox
+
+ ldh a, [hBattleTurn]
+ and a
+ ld hl, wEnemyMonMaxHP + 1
+ bccoord 2, 2 ; hp bar
+ ld a, 0
+ jr nz, .got_max_hp
+ ld hl, wBattleMonMaxHP + 1
+ bccoord 10, 9 ; hp bar
+ ld a, 1
+
+.got_max_hp
+ ld [wWhichHPBar], a
+ ld a, [hld]
+ ld [wBuffer1], a
+ ld a, [hld]
+ ld [wBuffer2], a
+ ld a, [hl]
+ ld [wBuffer3], a
+ xor a
+ ld [hld], a
+ ld a, [hl]
+ ld [wBuffer4], a
+ xor a
+ ld [hl], a
+ ld [wBuffer5], a
+ ld [wBuffer6], a
+ ld h, b
+ ld l, c
+ predef AnimateHPBar
+ call RefreshBattleHuds
+
+ call BattleCommand_SwitchTurn
+ xor a
+ ld [wNumHits], a
+ ld [wFXAnimID + 1], a
+ inc a
+ ld [wKickCounter], a
+ ld a, DESTINY_BOND
+ call LoadAnim
+ call BattleCommand_SwitchTurn
+
+ jr .finish
+
+.no_dbond
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ cp EFFECT_MULTI_HIT
+ jr z, .multiple_hit_raise_sub
+ cp EFFECT_DOUBLE_HIT
+ jr z, .multiple_hit_raise_sub
+ cp EFFECT_POISON_MULTI_HIT
+ jr z, .multiple_hit_raise_sub
+ cp EFFECT_TRIPLE_KICK
+ jr z, .multiple_hit_raise_sub
+ cp EFFECT_BEAT_UP
+ jr nz, .finish
+
+.multiple_hit_raise_sub
+ call BattleCommand_RaiseSub
+
+.finish
+ jp EndMoveEffect
+
+BattleCommand_BuildOpponentRage:
+; buildopponentrage
+
+ jp .start
+
+.start
+ ld a, [wAttackMissed]
+ and a
+ ret nz
+
+ ld a, BATTLE_VARS_SUBSTATUS4_OPP
+ call GetBattleVar
+ bit SUBSTATUS_RAGE, a
+ ret z
+
+ ld de, wEnemyRageCounter
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .player
+ ld de, wPlayerRageCounter
+.player
+ ld a, [de]
+ inc a
+ ret z
+ ld [de], a
+
+ call BattleCommand_SwitchTurn
+ ld hl, RageBuildingText
+ call StdBattleTextbox
+ jp BattleCommand_SwitchTurn
+
+BattleCommand_RageDamage:
+; ragedamage
+
+ ld a, [wCurDamage]
+ ld h, a
+ ld b, a
+ ld a, [wCurDamage + 1]
+ ld l, a
+ ld c, a
+ ldh a, [hBattleTurn]
+ and a
+ ld a, [wPlayerRageCounter]
+ jr z, .rage_loop
+ ld a, [wEnemyRageCounter]
+.rage_loop
+ and a
+ jr z, .done
+ dec a
+ add hl, bc
+ jr nc, .rage_loop
+ ld hl, $ffff
+.done
+ ld a, h
+ ld [wCurDamage], a
+ ld a, l
+ ld [wCurDamage + 1], a
+ ret
+
+EndMoveEffect:
+ ld a, [wBattleScriptBufferAddress]
+ ld l, a
+ ld a, [wBattleScriptBufferAddress + 1]
+ ld h, a
+ ld a, $ff
+ ld [hli], a
+ ld [hli], a
+ ld [hl], a
+ ret
+
+DittoMetalPowder:
+ ld a, MON_SPECIES
+ call BattlePartyAttr
+ ldh a, [hBattleTurn]
+ and a
+ ld a, [hl]
+ jr nz, .Ditto
+ ld a, [wTempEnemyMonSpecies]
+
+.Ditto:
+ cp DITTO
+ ret nz
+
+ push bc
+ call GetOpponentItem
+ ld a, [hl]
+ cp METAL_POWDER
+ pop bc
+ ret nz
+
+ ld a, c
+ srl a
+ add c
+ ld c, a
+ ret nc
+
+ srl b
+ ld a, b
+ and a
+ jr nz, .done
+ inc b
+.done
+ scf
+ rr c
+ ret
+
+BattleCommand_DamageStats:
+; damagestats
+
+ ldh a, [hBattleTurn]
+ and a
+ jp nz, EnemyAttackDamage
+
+ ; fallthrough
+
+PlayerAttackDamage:
+; Return move power d, player level e, enemy defense c and player attack b.
+
+ call ResetDamage
+
+ ld hl, wPlayerMoveStructPower
+ ld a, [hli]
+ and a
+ ld d, a
+ ret z
+
+ ld a, [hl]
+ cp SPECIAL
+ jr nc, .special
+
+.physical
+ ld hl, wEnemyMonDefense
+ ld a, [hli]
+ ld b, a
+ ld c, [hl]
+
+ ld a, [wEnemyScreens]
+ bit SCREENS_REFLECT, a
+ jr z, .physicalcrit
+ sla c
+ rl b
+
+.physicalcrit
+ ld hl, wBattleMonAttack
+ call CheckDamageStatsCritical
+ jr c, .thickclub
+
+ ld hl, wEnemyDefense
+ ld a, [hli]
+ ld b, a
+ ld c, [hl]
+ ld hl, wPlayerAttack
+ jr .thickclub
+
+.special
+ ld hl, wEnemyMonSpclDef
+ ld a, [hli]
+ ld b, a
+ ld c, [hl]
+
+ ld a, [wEnemyScreens]
+ bit SCREENS_LIGHT_SCREEN, a
+ jr z, .specialcrit
+ sla c
+ rl b
+
+.specialcrit
+ ld hl, wBattleMonSpclAtk
+ call CheckDamageStatsCritical
+ jr c, .lightball
+
+ ld hl, wEnemySpDef
+ ld a, [hli]
+ ld b, a
+ ld c, [hl]
+ ld hl, wPlayerSpAtk
+
+.lightball
+; Note: Returns player special attack at hl in hl.
+ call LightBallBoost
+ jr .done
+
+.thickclub
+; Note: Returns player attack at hl in hl.
+ call ThickClubBoost
+
+.done
+ call TruncateHL_BC
+
+ ld a, [wBattleMonLevel]
+ ld e, a
+ call DittoMetalPowder
+
+ ld a, 1
+ and a
+ ret
+
+TruncateHL_BC:
+.loop
+; Truncate 16-bit values hl and bc to 8-bit values b and c respectively.
+; b = hl, c = bc
+
+ ld a, h
+ or b
+ jr z, .done
+
+ srl b
+ rr c
+ srl b
+ rr c
+
+ ld a, c
+ or b
+ jr nz, .done_bc
+ inc c
+
+.done_bc
+ srl h
+ rr l
+ srl h
+ rr l
+
+ ld a, l
+ or h
+ jr nz, .done
+ inc l
+
+.done
+ ld b, l
+ ret
+
+CheckDamageStatsCritical:
+; Return carry if boosted stats should be used in damage calculations.
+; Unboosted stats should be used if the attack is a critical hit,
+; and the stage of the opponent's defense is higher than the user's attack.
+
+ ld a, [wCriticalHit]
+ and a
+ scf
+ ret z
+
+ push hl
+ push bc
+ ldh a, [hBattleTurn]
+ and a
+ jr nz, .enemy
+ ld a, [wPlayerMoveStructType]
+ cp SPECIAL
+; special
+ ld a, [wPlayerSAtkLevel]
+ ld b, a
+ ld a, [wEnemySDefLevel]
+ jr nc, .end
+; physical
+ ld a, [wPlayerAtkLevel]
+ ld b, a
+ ld a, [wEnemyDefLevel]
+ jr .end
+
+.enemy
+ ld a, [wEnemyMoveStructType]
+ cp SPECIAL
+; special
+ ld a, [wEnemySAtkLevel]
+ ld b, a
+ ld a, [wPlayerSDefLevel]
+ jr nc, .end
+; physical
+ ld a, [wEnemyAtkLevel]
+ ld b, a
+ ld a, [wPlayerDefLevel]
+.end
+ cp b
+ pop bc
+ pop hl
+ ret
+
+ThickClubBoost:
+; Return in hl the stat value at hl.
+
+; If the attacking monster is Cubone or Marowak and
+; it's holding a Thick Club, double it.
+ push bc
+ push de
+ ld b, CUBONE
+ ld c, MAROWAK
+ ld d, THICK_CLUB
+ call SpeciesItemBoost
+ pop de
+ pop bc
+ ret
+
+LightBallBoost:
+; Return in hl the stat value at hl.
+
+; If the attacking monster is Pikachu and it's
+; holding a Light Ball, double it.
+ push bc
+ push de
+ ld b, PIKACHU
+ ld c, PIKACHU
+ ld d, LIGHT_BALL
+ call SpeciesItemBoost
+ pop de
+ pop bc
+ ret
+
+SpeciesItemBoost:
+; Return in hl the stat value at hl.
+
+; If the attacking monster is species b or c and
+; it's holding item d, double it.
+
+ ld a, [hli]
+ ld l, [hl]
+ ld h, a
+
+ push hl
+ ld a, MON_SPECIES
+ call BattlePartyAttr
+
+ ldh a, [hBattleTurn]
+ and a
+ ld a, [hl]
+ jr z, .CompareSpecies
+ ld a, [wTempEnemyMonSpecies]
+.CompareSpecies:
+ pop hl
+
+ cp b
+ jr z, .GetItemHeldEffect
+ cp c
+ ret nz
+
+.GetItemHeldEffect:
+ push hl
+ call GetUserItem
+ ld a, [hl]
+ pop hl
+ cp d
+ ret nz
+
+; Double the stat
+ sla l
+ rl h
+ ret
+
+EnemyAttackDamage:
+ call ResetDamage
+
+; No damage dealt with 0 power.
+ ld hl, wEnemyMoveStructPower
+ ld a, [hli] ; hl = wEnemyMoveStructType
+ ld d, a
+ and a
+ ret z
+
+ ld a, [hl]
+ cp SPECIAL
+ jr nc, .Special
+
+.physical
+ ld hl, wBattleMonDefense
+ ld a, [hli]
+ ld b, a
+ ld c, [hl]
+
+ ld a, [wPlayerScreens]
+ bit SCREENS_REFLECT, a
+ jr z, .physicalcrit
+ sla c
+ rl b
+
+.physicalcrit
+ ld hl, wEnemyMonAttack
+ call CheckDamageStatsCritical
+ jr c, .thickclub
+
+ ld hl, wPlayerDefense
+ ld a, [hli]
+ ld b, a
+ ld c, [hl]
+ ld hl, wEnemyAttack
+ jr .thickclub
+
+.Special:
+ ld hl, wBattleMonSpclDef
+ ld a, [hli]
+ ld b, a
+ ld c, [hl]
+
+ ld a, [wPlayerScreens]
+ bit SCREENS_LIGHT_SCREEN, a
+ jr z, .specialcrit
+ sla c
+ rl b
+
+.specialcrit
+ ld hl, wEnemyMonSpclAtk
+ call CheckDamageStatsCritical
+ jr c, .lightball
+ ld hl, wPlayerSpDef
+ ld a, [hli]
+ ld b, a
+ ld c, [hl]
+ ld hl, wEnemySpAtk
+
+.lightball
+ call LightBallBoost
+ jr .done
+
+.thickclub
+ call ThickClubBoost
+
+.done
+ call TruncateHL_BC
+
+ ld a, [wEnemyMonLevel]
+ ld e, a
+ call DittoMetalPowder
+
+ ld a, 1
+ and a
+ ret
+
+INCLUDE "engine/battle/move_effects/beat_up.asm"
+
+BattleCommand_ClearMissDamage:
+; clearmissdamage
+ ld a, [wAttackMissed]
+ and a
+ ret z
+
+ jp ResetDamage
+
+HitSelfInConfusion:
+ call ResetDamage
+ ldh a, [hBattleTurn]
+ and a
+ ld hl, wBattleMonDefense
+ ld de, wPlayerScreens
+ ld a, [wBattleMonLevel]
+ jr z, .got_it
+
+ ld hl, wEnemyMonDefense
+ ld de, wEnemyScreens
+ ld a, [wEnemyMonLevel]
+.got_it
+ push af
+ ld a, [hli]
+ ld b, a
+ ld c, [hl]
+ ld a, [de]
+ bit SCREENS_REFLECT, a
+ jr z, .mimic_screen
+
+ sla c
+ rl b
+.mimic_screen
+ dec hl
+ dec hl
+ dec hl
+ ld a, [hli]
+ ld l, [hl]
+ ld h, a
+ call TruncateHL_BC
+ ld d, 40
+ pop af
+ ld e, a
+ ret
+
+BattleCommand_DamageCalc:
+; damagecalc
+
+; Return a damage value for move power d, player level e, enemy defense c and player attack b.
+
+; Return 1 if successful, else 0.
+
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+
+; Selfdestruct and Explosion halve defense.
+ cp EFFECT_SELFDESTRUCT
+ jr nz, .dont_selfdestruct
+
+ srl c
+ jr nz, .dont_selfdestruct
+ inc c
+
+.dont_selfdestruct
+
+; Variable-hit moves and Conversion can have a power of 0.
+ cp EFFECT_MULTI_HIT
+ jr z, .skip_zero_damage_check
+
+ cp EFFECT_CONVERSION
+ jr z, .skip_zero_damage_check
+
+; No damage if move power is 0.
+ ld a, d
+ and a
+ ret z
+
+.skip_zero_damage_check
+; Minimum defense value is 1.
+ ld a, c
+ and a
+ jr nz, .not_dividing_by_zero
+ ld c, 1
+.not_dividing_by_zero
+
+ xor a
+ ld hl, hDividend
+ ld [hli], a
+ ld [hli], a
+ ld [hl], a
+
+; Level * 2
+ ld a, e
+ add a
+ jr nc, .level_not_overflowing
+ ld [hl], 1
+.level_not_overflowing
+ inc hl
+ ld [hli], a
+
+; / 5
+ ld a, 5
+ ld [hld], a
+ push bc
+ ld b, 4
+ call Divide
+ pop bc
+
+; + 2
+ inc [hl]
+ inc [hl]
+
+; * bp
+ inc hl
+ ld [hl], d
+ call Multiply
+
+; * Attack
+ ld [hl], b
+ call Multiply
+
+; / Defense
+ ld [hl], c
+ ld b, 4
+ call Divide
+
+; / 50
+ ld [hl], 50
+ ld b, $4
+ call Divide
+
+; Item boosts
+ call GetUserItem
+
+ ld a, b
+ and a
+ jr z, .DoneItem
+
+ ld hl, TypeBoostItems
+
+.NextItem:
+ ld a, [hli]
+ cp -1
+ jr z, .DoneItem
+
+; Item effect
+ cp b
+ ld a, [hli]
+ jr nz, .NextItem
+
+; Type
+ ld b, a
+ ld a, BATTLE_VARS_MOVE_TYPE
+ call GetBattleVar
+ cp b
+ jr nz, .DoneItem
+
+; * 100 + item effect amount
+ ld a, c
+ add 100
+ ldh [hMultiplier], a
+ call Multiply
+
+; / 100
+ ld a, 100
+ ldh [hDivisor], a
+ ld b, 4
+ call Divide
+
+.DoneItem:
+; Critical hits
+ call .CriticalMultiplier
+
+; Update wCurDamage (capped at 997).
+ ld hl, wCurDamage
+ ld b, [hl]
+ ldh a, [hProduct + 3]
+ add b
+ ldh [hProduct + 3], a
+ jr nc, .dont_cap_1
+
+ ldh a, [hProduct + 2]
+ inc a
+ ldh [hProduct + 2], a
+ and a
+ jr z, .Cap
+
+.dont_cap_1
+ ldh a, [hProduct]
+ ld b, a
+ ldh a, [hProduct + 1]
+ or a
+ jr nz, .Cap
+
+ ldh a, [hProduct + 2]
+ cp HIGH(MAX_STAT_VALUE - MIN_NEUTRAL_DAMAGE + 1)
+ jr c, .dont_cap_2
+
+ cp HIGH(MAX_STAT_VALUE - MIN_NEUTRAL_DAMAGE + 1) + 1
+ jr nc, .Cap
+
+ ldh a, [hProduct + 3]
+ cp LOW(MAX_STAT_VALUE - MIN_NEUTRAL_DAMAGE + 1)
+ jr nc, .Cap
+
+.dont_cap_2
+ inc hl
+
+ ldh a, [hProduct + 3]
+ ld b, [hl]
+ add b
+ ld [hld], a
+
+ ldh a, [hProduct + 2]
+ ld b, [hl]
+ adc b
+ ld [hl], a
+ jr c, .Cap
+
+ ld a, [hl]
+ cp HIGH(MAX_STAT_VALUE - MIN_NEUTRAL_DAMAGE + 1)
+ jr c, .dont_cap_3
+
+ cp HIGH(MAX_STAT_VALUE - MIN_NEUTRAL_DAMAGE + 1) + 1
+ jr nc, .Cap
+
+ inc hl
+ ld a, [hld]
+ cp LOW(MAX_STAT_VALUE - MIN_NEUTRAL_DAMAGE + 1)
+ jr c, .dont_cap_3
+
+.Cap:
+ ld a, HIGH(MAX_STAT_VALUE - MIN_NEUTRAL_DAMAGE)
+ ld [hli], a
+ ld a, LOW(MAX_STAT_VALUE - MIN_NEUTRAL_DAMAGE)
+ ld [hld], a
+
+.dont_cap_3
+; Minimum neutral damage is 2 (bringing the cap to 999).
+ inc hl
+ ld a, [hl]
+ add MIN_NEUTRAL_DAMAGE
+ ld [hld], a
+ jr nc, .dont_floor
+ inc [hl]
+.dont_floor
+
+ ld a, 1
+ and a
+ ret
+
+.CriticalMultiplier:
+ ld a, [wCriticalHit]
+ and a
+ ret z
+
+; x2
+ ldh a, [hQuotient + 3]
+ sla a
+ ldh [hProduct + 3], a
+
+ ldh a, [hQuotient + 2]
+ rl a
+ ldh [hProduct + 2], a
+
+; Cap at $ffff.
+ ret nc
+
+ ld a, $ff
+ ldh [hProduct + 2], a
+ ldh [hProduct + 3], a
+
+ ret
+
+INCLUDE "data/types/type_boost_items.asm"
+
+BattleCommand_ConstantDamage:
+; constantdamage
+
+ ld hl, wBattleMonLevel
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_turn
+ ld hl, wEnemyMonLevel
+
+.got_turn
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ cp EFFECT_LEVEL_DAMAGE
+ ld b, [hl]
+ ld a, 0
+ jr z, .got_power
+
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ cp EFFECT_PSYWAVE
+ jr z, .psywave
+
+ cp EFFECT_SUPER_FANG
+ jr z, .super_fang
+
+ cp EFFECT_REVERSAL
+ jr z, .reversal
+
+ ld a, BATTLE_VARS_MOVE_POWER
+ call GetBattleVar
+ ld b, a
+ ld a, $0
+ jr .got_power
+
+.psywave
+ ld a, b
+ srl a
+ add b
+ ld b, a
+.psywave_loop
+ call BattleRandom
+ and a
+ jr z, .psywave_loop
+ cp b
+ jr nc, .psywave_loop
+ ld b, a
+ ld a, 0
+ jr .got_power
+
+.super_fang
+ ld hl, wEnemyMonHP
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_hp
+ ld hl, wBattleMonHP
+.got_hp
+ ld a, [hli]
+ srl a
+ ld b, a
+ ld a, [hl]
+ rr a
+ push af
+ ld a, b
+ pop bc
+ and a
+ jr nz, .got_power
+ or b
+ ld a, 0
+ jr nz, .got_power
+ ld b, 1
+ jr .got_power
+
+.got_power
+ ld hl, wCurDamage
+ ld [hli], a
+ ld [hl], b
+ ret
+
+.reversal
+ ld hl, wBattleMonHP
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .reversal_got_hp
+ ld hl, wEnemyMonHP
+.reversal_got_hp
+ xor a
+ ldh [hDividend], a
+ ldh [hMultiplicand + 0], a
+ ld a, [hli]
+ ldh [hMultiplicand + 1], a
+ ld a, [hli]
+ ldh [hMultiplicand + 2], a
+ ld a, 48
+ ldh [hMultiplier], a
+ call Multiply
+ ld a, [hli]
+ ld b, a
+ ld a, [hl]
+ ldh [hDivisor], a
+ ld a, b
+ and a
+ jr z, .skip_to_divide
+
+ ldh a, [hProduct + 4]
+ srl b
+ rr a
+ srl b
+ rr a
+ ldh [hDivisor], a
+ ldh a, [hProduct + 2]
+ ld b, a
+ srl b
+ ldh a, [hProduct + 3]
+ rr a
+ srl b
+ rr a
+ ldh [hDividend + 3], a
+ ld a, b
+ ldh [hDividend + 2], a
+
+.skip_to_divide
+ ld b, 4
+ call Divide
+ ldh a, [hQuotient + 3]
+ ld b, a
+ ld hl, FlailReversalPower
+
+.reversal_loop
+ ld a, [hli]
+ cp b
+ jr nc, .break_loop
+ inc hl
+ jr .reversal_loop
+
+.break_loop
+ ldh a, [hBattleTurn]
+ and a
+ ld a, [hl]
+ jr nz, .notPlayersTurn
+
+ ld hl, wPlayerMoveStructPower
+ ld [hl], a
+ push hl
+ call PlayerAttackDamage
+ jr .notEnemysTurn
+
+.notPlayersTurn
+ ld hl, wEnemyMoveStructPower
+ ld [hl], a
+ push hl
+ call EnemyAttackDamage
+
+.notEnemysTurn
+ call BattleCommand_DamageCalc
+ pop hl
+ ld [hl], 1
+ ret
+
+INCLUDE "data/moves/flail_reversal_power.asm"
+
+INCLUDE "engine/battle/move_effects/counter.asm"
+
+INCLUDE "engine/battle/move_effects/encore.asm"
+
+INCLUDE "engine/battle/move_effects/pain_split.asm"
+
+INCLUDE "engine/battle/move_effects/snore.asm"
+
+INCLUDE "engine/battle/move_effects/conversion2.asm"
+
+INCLUDE "engine/battle/move_effects/lock_on.asm"
+
+INCLUDE "engine/battle/move_effects/sketch.asm"
+
+BattleCommand_DefrostOpponent:
+; defrostopponent
+; Thaw the opponent if frozen, and
+; raise the user's Attack one stage.
+
+ call AnimateCurrentMove
+
+ ld a, BATTLE_VARS_STATUS_OPP
+ call GetBattleVarAddr
+ call Defrost
+
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVarAddr
+ ld a, [hl]
+ push hl
+ push af
+
+ ld a, EFFECT_ATTACK_UP
+ ld [hl], a
+ call BattleCommand_StatUp
+
+ pop af
+ pop hl
+ ld [hl], a
+ ret
+
+INCLUDE "engine/battle/move_effects/sleep_talk.asm"
+
+INCLUDE "engine/battle/move_effects/destiny_bond.asm"
+
+INCLUDE "engine/battle/move_effects/spite.asm"
+
+INCLUDE "engine/battle/move_effects/false_swipe.asm"
+
+INCLUDE "engine/battle/move_effects/heal_bell.asm"
+
+FarPlayBattleAnimation:
+; play animation de
+
+ ld a, BATTLE_VARS_SUBSTATUS3
+ call GetBattleVar
+ and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND
+ ret nz
+
+ ; fallthrough
+
+PlayFXAnimID:
+ ld a, e
+ ld [wFXAnimID], a
+ ld a, d
+ ld [wFXAnimID + 1], a
+
+ ld c, 3
+ call DelayFrames
+ callfar PlayBattleAnim
+ ret
+
+DoEnemyDamage:
+ ld hl, wCurDamage
+ ld a, [hli]
+ ld b, a
+ ld a, [hl]
+ or b
+ jr z, .did_no_damage
+
+ ld a, c
+ and a
+ jr nz, .ignore_substitute
+ ld a, [wEnemySubStatus4]
+ bit SUBSTATUS_SUBSTITUTE, a
+ jp nz, DoSubstituteDamage
+
+.ignore_substitute
+ ; Substract wCurDamage from wEnemyMonHP.
+ ; store original HP in little endian wBuffer3/4
+ ld a, [hld]
+ ld b, a
+ ld a, [wEnemyMonHP + 1]
+ ld [wBuffer3], a
+ sub b
+ ld [wEnemyMonHP + 1], a
+ ld a, [hl]
+ ld b, a
+ ld a, [wEnemyMonHP]
+ ld [wBuffer4], a
+ sbc b
+ ld [wEnemyMonHP], a
+ jr nc, .no_underflow
+
+ ld a, [wBuffer4]
+ ld [hli], a
+ ld a, [wBuffer3]
+ ld [hl], a
+ xor a
+ ld hl, wEnemyMonHP
+ ld [hli], a
+ ld [hl], a
+
+.no_underflow
+ ld hl, wEnemyMonMaxHP
+ ld a, [hli]
+ ld [wBuffer2], a
+ ld a, [hl]
+ ld [wBuffer1], a
+ ld hl, wEnemyMonHP
+ ld a, [hli]
+ ld [wBuffer6], a
+ ld a, [hl]
+ ld [wBuffer5], a
+
+ hlcoord 2, 2
+ xor a
+ ld [wWhichHPBar], a
+ predef AnimateHPBar
+.did_no_damage
+ jp RefreshBattleHuds
+
+DoPlayerDamage:
+ ld hl, wCurDamage
+ ld a, [hli]
+ ld b, a
+ ld a, [hl]
+ or b
+ jr z, .did_no_damage
+
+ ld a, c
+ and a
+ jr nz, .ignore_substitute
+ ld a, [wPlayerSubStatus4]
+ bit SUBSTATUS_SUBSTITUTE, a
+ jp nz, DoSubstituteDamage
+
+.ignore_substitute
+ ; Substract wCurDamage from wBattleMonHP.
+ ; store original HP in little endian wBuffer3/4
+ ; store new HP in little endian wBuffer5/6
+ ld a, [hld]
+ ld b, a
+ ld a, [wBattleMonHP + 1]
+ ld [wBuffer3], a
+ sub b
+ ld [wBattleMonHP + 1], a
+ ld [wBuffer5], a
+ ld b, [hl]
+ ld a, [wBattleMonHP]
+ ld [wBuffer4], a
+ sbc b
+ ld [wBattleMonHP], a
+ ld [wBuffer6], a
+ jr nc, .no_underflow
+
+ ld a, [wBuffer4]
+ ld [hli], a
+ ld a, [wBuffer3]
+ ld [hl], a
+ xor a
+ ld hl, wBattleMonHP
+ ld [hli], a
+ ld [hl], a
+ ld hl, wBuffer5
+ ld [hli], a
+ ld [hl], a
+
+.no_underflow
+ ld hl, wBattleMonMaxHP
+ ld a, [hli]
+ ld [wBuffer2], a
+ ld a, [hl]
+ ld [wBuffer1], a
+
+ hlcoord 10, 9
+ ld a, 1
+ ld [wWhichHPBar], a
+ predef AnimateHPBar
+.did_no_damage
+ jp RefreshBattleHuds
+
+DoSubstituteDamage:
+ ld hl, SubTookDamageText
+ call StdBattleTextbox
+
+ ld de, wEnemySubstituteHP
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_hp
+ ld de, wPlayerSubstituteHP
+.got_hp
+
+ ld hl, wCurDamage
+ ld a, [hli]
+ and a
+ jr nz, .broke
+
+ ld a, [de]
+ sub [hl]
+ ld [de], a
+ jr z, .broke
+ jr nc, .done
+
+.broke
+ ld a, BATTLE_VARS_SUBSTATUS4_OPP
+ call GetBattleVarAddr
+ res SUBSTATUS_SUBSTITUTE, [hl]
+
+ ld hl, SubFadedText
+ call StdBattleTextbox
+
+ call BattleCommand_SwitchTurn
+ call BattleCommand_LowerSubNoAnim
+ ld a, BATTLE_VARS_SUBSTATUS3
+ call GetBattleVar
+ and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND
+ call z, AppearUserLowerSub
+ call BattleCommand_SwitchTurn
+
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVarAddr
+ cp EFFECT_MULTI_HIT
+ jr z, .ok
+ cp EFFECT_DOUBLE_HIT
+ jr z, .ok
+ cp EFFECT_POISON_MULTI_HIT
+ jr z, .ok
+ cp EFFECT_TRIPLE_KICK
+ jr z, .ok
+ cp EFFECT_BEAT_UP
+ jr z, .ok
+ xor a
+ ld [hl], a
+.ok
+ call RefreshBattleHuds
+.done
+ jp ResetDamage
diff --git a/engine/battle/move_effects/beat_up.asm b/engine/battle/move_effects/beat_up.asm
new file mode 100644
index 00000000..8546c37d
--- /dev/null
+++ b/engine/battle/move_effects/beat_up.asm
@@ -0,0 +1,220 @@
+BattleCommand_BeatUp:
+; beatup
+
+ call ResetDamage
+ ldh 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 [wceed], a
+ ld [wBeatUpHitAtLeastOnce], a
+ jr .got_mon
+
+.next_mon
+ ld a, [wPlayerRolloutCount]
+ ld b, a
+ ld a, [wPartyCount]
+ sub b
+ ld [wceed], a
+
+.got_mon
+ ld a, [wceed]
+ ld hl, wPartyMonNicknames
+ call GetNick
+ ld a, MON_HP
+ call GetBeatupMonLocation
+ ld a, [hli]
+ or [hl]
+ jp z, .beatup_fail ; fainted
+ ld a, [wceed]
+ 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 [wceed], a
+ ld [wBeatUpHitAtLeastOnce], a
+ jr .enemy_got_mon
+
+.enemy_next_mon
+ ld a, [wEnemyRolloutCount]
+ ld b, a
+ ld a, [wOTPartyCount]
+ sub b
+ ld [wceed], a
+
+.enemy_got_mon
+ ld a, [wBattleMode]
+ dec a
+ jr z, .wild
+
+ ld a, [wLinkMode]
+ and a
+ jr nz, .linked
+
+ ld a, [wceed]
+ ld c, a
+ ld b, 0
+ ld hl, wOTPartySpecies
+ add hl, bc
+ ld a, [hl]
+ ld [wNamedObjectIndexBuffer], a
+ call GetPokemonName
+ jr .got_enemy_nick
+
+.linked
+ ld a, [wceed]
+ 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, [wceed]
+ 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
+ ldh a, [hBattleTurn]
+ and a
+ ld hl, wPartyMon1Species
+ jr z, .got_species
+ ld hl, wOTPartyMon1Species
+
+.got_species
+ ld a, [wceed]
+ add hl, bc
+ call GetPartyLocation
+ pop bc
+ ret
diff --git a/engine/battle/move_effects/conversion2.asm b/engine/battle/move_effects/conversion2.asm
new file mode 100644
index 00000000..bc866727
--- /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
+ ldh 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_TYPE
+ 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 00000000..031c399a
--- /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/destiny_bond.asm b/engine/battle/move_effects/destiny_bond.asm
new file mode 100644
index 00000000..6a03b9a7
--- /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/encore.asm b/engine/battle/move_effects/encore.asm
new file mode 100644
index 00000000..8ca3595f
--- /dev/null
+++ b/engine/battle/move_effects/encore.asm
@@ -0,0 +1,120 @@
+BattleCommand_Encore:
+; encore
+
+ ld hl, wEnemyMonMoves
+ ld de, wEnemyEncoreCount
+ ldh 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
+ ldh 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/false_swipe.asm b/engine/battle/move_effects/false_swipe.asm
new file mode 100644
index 00000000..e2e0c6f1
--- /dev/null
+++ b/engine/battle/move_effects/false_swipe.asm
@@ -0,0 +1,48 @@
+BattleCommand_FalseSwipe:
+; falseswipe
+
+; Makes sure wCurDamage < MonHP
+
+ ld hl, wEnemyMonHP
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_hp
+ ld hl, wBattleMonHP
+.got_hp
+ ld de, wCurDamage
+ ld c, 2
+ push hl
+ push de
+ call CompareBytes
+ 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/heal_bell.asm b/engine/battle/move_effects/heal_bell.asm
new file mode 100644
index 00000000..62309f1d
--- /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
+ ldh 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
+
+ ldh a, [hBattleTurn]
+ and a
+ jp z, CalcPlayerStats
+ jp CalcEnemyStats
diff --git a/engine/battle/move_effects/lock_on.asm b/engine/battle/move_effects/lock_on.asm
new file mode 100644
index 00000000..1de3e14b
--- /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/pain_split.asm b/engine/battle/move_effects/pain_split.asm
new file mode 100644
index 00000000..68d7cfb4
--- /dev/null
+++ b/engine/battle/move_effects/pain_split.asm
@@ -0,0 +1,92 @@
+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
+
+ 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/sketch.asm b/engine/battle/move_effects/sketch.asm
new file mode 100644
index 00000000..654fb3f5
--- /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
+ ldh 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 [wNamedObjectIndexBuffer], 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
+
+ ldh 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 00000000..92bff260
--- /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
+ ldh 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
+ ldh 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 00000000..e2432c59
--- /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/spite.asm b/engine/battle/move_effects/spite.asm
new file mode 100644
index 00000000..06627268
--- /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
+ ldh 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 [wNamedObjectIndexBuffer], 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
+ ldh 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 [wDeciramBuffer], a
+ ld hl, SpiteEffectText
+ jp StdBattleTextbox
+
+.failed
+ jp PrintDidntAffect2
diff --git a/engine/items/item_effects.asm b/engine/items/item_effects.asm
index 8d694eac..46713dfe 100755
--- a/engine/items/item_effects.asm
+++ b/engine/items/item_effects.asm
@@ -390,9 +390,9 @@ UltraBall: ; e926
ld [wBattleAnimParam], a
ld de, ANIM_THROW_POKE_BALL
ld a, e
- ld [wcf3e], a
+ ld [wFXAnimID], a
ld a, d
- ld [wcf3f], a
+ ld [wFXAnimID + 1], a
xor a
ldh [hBattleTurn], a
ld [wBuffer2], a
@@ -2537,9 +2537,9 @@ Functionf7e7: ; f7e7 (3:77e7)
call ReturnToBattle_UseBall
ld de, Start
ld a, e
- ld [wcf3e], a
+ ld [wFXAnimID], a
ld a, d
- ld [wcf3f], a
+ ld [wFXAnimID + 1], a
xor a
ld [wBattleAnimParam], a
ldh [hBattleTurn], a