diff options
Diffstat (limited to 'engine/battle/effect_commands.asm')
-rw-r--r-- | engine/battle/effect_commands.asm | 6865 |
1 files changed, 6865 insertions, 0 deletions
diff --git a/engine/battle/effect_commands.asm b/engine/battle/effect_commands.asm new file mode 100644 index 00000000..815cc17d --- /dev/null +++ b/engine/battle/effect_commands.asm @@ -0,0 +1,6865 @@ +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" + +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 + +UpdateMoveData: + ld a, BATTLE_VARS_MOVE_ANIM + call GetBattleVarAddr + ld d, h + ld e, l + + ld a, BATTLE_VARS_MOVE + call GetBattleVar + ld [wCurSpecies], a + ld [wNamedObjectIndexBuffer], a + + dec a + call GetMoveData + call GetMoveName + jp CopyName1 + +BattleCommand_SleepTarget: +; sleeptarget + + call GetOpponentItem + ld a, b + cp HELD_PREVENT_SLEEP + jr nz, .not_protected_by_item + + ld a, [hl] + ld [wNamedObjectIndexBuffer], a + call GetItemName + ld hl, ProtectedByText + jr .fail + +.not_protected_by_item + ld a, BATTLE_VARS_STATUS_OPP + call GetBattleVarAddr + ld d, h + ld e, l + ld a, [de] + and SLP + ld hl, AlreadyAsleepText + jr nz, .fail + + ld a, [wAttackMissed] + and a + jp nz, PrintDidntAffect2 + + ld hl, DidntAffect1Text + call .CheckAIRandomFail + jr c, .fail + + ld a, [de] + and a + jr nz, .fail + + call CheckSubstituteOpp + jr nz, .fail + + call AnimateCurrentMove + +.random_loop + call BattleRandom + and 7 + jr z, .random_loop + cp 7 + jr z, .random_loop + inc a + ld [de], a + call UpdateOpponentInParty + call RefreshBattleHuds + + ld hl, FellAsleepText + call StdBattleTextbox + + farcall UseHeldStatusHealingItem + ret nz + + call OpponentCantMove + ret + +.fail + call AnimateFailedMove + jp StdBattleTextbox + +.CheckAIRandomFail: + ; Enemy turn + ldh a, [hBattleTurn] + and a + jr z, .dont_fail + + ; Not in link battle + ld a, [wLinkMode] + and a + jr nz, .dont_fail + + ; Not locked-on by the enemy + ld a, [wPlayerSubStatus5] + bit SUBSTATUS_LOCK_ON, a + jr nz, .dont_fail + + call BattleRandom + cp 25 percent + 1 ; 25% chance AI fails + ret c + +.dont_fail + xor a + ret + +BattleCommand_PoisonTarget: +; poisontarget + + call CheckSubstituteOpp + ret nz + ld a, BATTLE_VARS_STATUS_OPP + call GetBattleVarAddr + and a + ret nz + ld a, [wTypeModifier] + and $7f + ret z + call CheckIfTargetIsPoisonType + ret z + call GetOpponentItem + ld a, b + cp HELD_PREVENT_POISON + ret z + ld a, [wEffectFailed] + and a + ret nz + call SafeCheckSafeguard + ret nz + + call PoisonOpponent + ld de, ANIM_PSN + call PlayOpponentBattleAnim + call RefreshBattleHuds + + ld hl, WasPoisonedText + call StdBattleTextbox + + farcall UseHeldStatusHealingItem + ret + +BattleCommand_Poison: +; poison + + ld hl, DoesntAffectText + ld a, [wTypeModifier] + and $7f + jp z, .failed + + call CheckIfTargetIsPoisonType + jp z, .failed + + ld a, BATTLE_VARS_STATUS_OPP + call GetBattleVar + ld b, a + ld hl, AlreadyPoisonedText + and 1 << PSN + jp nz, .failed + + call GetOpponentItem + ld a, b + cp HELD_PREVENT_POISON + jr nz, .do_poison + ld a, [hl] + ld [wNamedObjectIndexBuffer], a + call GetItemName + ld hl, ProtectedByText + jr .failed + +.do_poison + ld hl, DidntAffect1Text + ld a, BATTLE_VARS_STATUS_OPP + call GetBattleVar + and a + jr nz, .failed + + ldh a, [hBattleTurn] + and a + jr z, .dont_sample_failure + + ld a, [wLinkMode] + and a + jr nz, .dont_sample_failure + + ld a, [wPlayerSubStatus5] + bit SUBSTATUS_LOCK_ON, a + jr nz, .dont_sample_failure + + call BattleRandom + cp 25 percent + 1 ; 25% chance AI fails + jr c, .failed + +.dont_sample_failure + call CheckSubstituteOpp + jr nz, .failed + ld a, [wAttackMissed] + and a + jr nz, .failed + call .check_toxic + jr z, .toxic + + call .apply_poison + ld hl, WasPoisonedText + call StdBattleTextbox + jr .finished + +.toxic + set SUBSTATUS_TOXIC, [hl] + xor a + ld [de], a + call .apply_poison + + ld hl, BadlyPoisonedText + call StdBattleTextbox + +.finished + farcall UseHeldStatusHealingItem + ret + +.failed + call AnimateFailedMove + jp StdBattleTextbox + +.apply_poison + call AnimateCurrentMove + call PoisonOpponent + jp RefreshBattleHuds + +.check_toxic + ld a, BATTLE_VARS_SUBSTATUS5_OPP + call GetBattleVarAddr + ldh a, [hBattleTurn] + and a + ld de, wEnemyToxicCount + jr z, .ok + ld de, wPlayerToxicCount +.ok + ld a, BATTLE_VARS_MOVE_EFFECT + call GetBattleVar + cp EFFECT_TOXIC + ret + +CheckIfTargetIsPoisonType: + ld de, wEnemyMonType1 + ldh a, [hBattleTurn] + and a + jr z, .ok + ld de, wBattleMonType1 +.ok + ld a, [de] + inc de + cp POISON + ret z + ld a, [de] + cp POISON + ret + +PoisonOpponent: + ld a, BATTLE_VARS_STATUS_OPP + call GetBattleVarAddr + set PSN, [hl] + jp UpdateOpponentInParty + +BattleCommand_DrainTarget: +; draintarget + call SapHealth + ld hl, SuckedHealthText + jp StdBattleTextbox + +BattleCommand_EatDream: +; eatdream + call SapHealth + ld hl, DreamEatenText + jp StdBattleTextbox + +SapHealth: + ; Divide damage by 2, store it in hDividend + ld hl, wCurDamage + ld a, [hli] + srl a + ldh [hDividend], a + ld b, a + ld a, [hl] + rr a + ldh [hDividend + 1], a + or b + jr nz, .at_least_one + ld a, 1 + ldh [hDividend + 1], a +.at_least_one + + ld hl, wBattleMonHP + ld de, wBattleMonMaxHP + ldh a, [hBattleTurn] + and a + jr z, .battlemonhp + ld hl, wEnemyMonHP + ld de, wEnemyMonMaxHP +.battlemonhp + + ; Store current HP in little endian wBuffer3/4 + ld bc, wBuffer4 + ld a, [hli] + ld [bc], a + ld a, [hl] + dec bc + ld [bc], a + + ; Store max HP in little endian wBuffer1/2 + ld a, [de] + dec bc + ld [bc], a + inc de + ld a, [de] + dec bc + ld [bc], a + + ; Add hDividend to current HP and copy it to little endian wBuffer5/6 + ldh a, [hDividend + 1] + ld b, [hl] + add b + ld [hld], a + ld [wBuffer5], a + ldh a, [hDividend] + ld b, [hl] + adc b + ld [hli], a + ld [wBuffer6], a + jr c, .max_hp + + ; Substract current HP from max HP (to see if we have more than max HP) + ld a, [hld] + ld b, a + ld a, [de] + dec de + sub b + ld a, [hli] + ld b, a + ld a, [de] + inc de + sbc b + jr nc, .finish + +.max_hp + ; Load max HP into current HP and copy it to little endian wBuffer5/6 + ld a, [de] + ld [hld], a + ld [wBuffer5], a + dec de + ld a, [de] + ld [hli], a + ld [wBuffer6], a + inc de + +.finish + ldh a, [hBattleTurn] + and a + hlcoord 10, 9 + ld a, $1 + jr z, .hp_bar + hlcoord 2, 2 + xor a +.hp_bar + ld [wWhichHPBar], a + predef AnimateHPBar + call RefreshBattleHuds + jp UpdateBattleMonInParty + +BattleCommand_BurnTarget: +; burntarget + + xor a + ld [wNumHits], a + call CheckSubstituteOpp + ret nz + ld a, BATTLE_VARS_STATUS_OPP + call GetBattleVarAddr + and a + jp nz, Defrost + ld a, [wTypeModifier] + and $7f + ret z + call CheckMoveTypeMatchesTarget ; Don't burn a Fire-type + ret z + call GetOpponentItem + ld a, b + cp HELD_PREVENT_BURN + ret z + ld a, [wEffectFailed] + and a + ret nz + call SafeCheckSafeguard + ret nz + ld a, BATTLE_VARS_STATUS_OPP + call GetBattleVarAddr + set BRN, [hl] + call UpdateOpponentInParty + ld hl, ApplyBrnEffectOnAttack + call CallBattleCore + ld de, ANIM_BRN + call PlayOpponentBattleAnim + call RefreshBattleHuds + + ld hl, WasBurnedText + call StdBattleTextbox + + farcall UseHeldStatusHealingItem + ret + +Defrost: + ld a, [hl] + and 1 << FRZ + ret z + + xor a + ld [hl], a + + ldh a, [hBattleTurn] + and a + ld a, [wCurOTMon] + ld hl, wOTPartyMon1Status + jr z, .ok + ld hl, wPartyMon1Status + ld a, [wCurBattleMon] +.ok + + call GetPartyLocation + xor a + ld [hl], a + call UpdateOpponentInParty + + ld hl, DefrostedOpponentText + jp StdBattleTextbox + +BattleCommand_FreezeTarget: +; freezetarget + + xor a + ld [wNumHits], a + call CheckSubstituteOpp + ret nz + ld a, BATTLE_VARS_STATUS_OPP + call GetBattleVarAddr + and a + ret nz + ld a, [wTypeModifier] + and $7f + ret z + ld a, [wBattleWeather] + cp WEATHER_SUN + ret z + call CheckMoveTypeMatchesTarget ; Don't freeze an Ice-type + ret z + call GetOpponentItem + ld a, b + cp HELD_PREVENT_FREEZE + ret z + ld a, [wEffectFailed] + and a + ret nz + call SafeCheckSafeguard + ret nz + ld a, BATTLE_VARS_STATUS_OPP + call GetBattleVarAddr + set FRZ, [hl] + call UpdateOpponentInParty + ld de, ANIM_FRZ + call PlayOpponentBattleAnim + call RefreshBattleHuds + + ld hl, WasFrozenText + call StdBattleTextbox + + farcall UseHeldStatusHealingItem + ret nz + + call OpponentCantMove + call EndRechargeOpp + ld hl, wEnemyJustGotFrozen + ldh a, [hBattleTurn] + and a + jr z, .finish + ld hl, wPlayerJustGotFrozen +.finish + ld [hl], $1 + ret + +BattleCommand_ParalyzeTarget: +; paralyzetarget + + xor a + ld [wNumHits], a + call CheckSubstituteOpp + ret nz + ld a, BATTLE_VARS_STATUS_OPP + call GetBattleVarAddr + and a + ret nz + ld a, [wTypeModifier] + and $7f + ret z + call GetOpponentItem + ld a, b + cp HELD_PREVENT_PARALYZE + ret z + ld a, [wEffectFailed] + and a + ret nz + call SafeCheckSafeguard + ret nz + ld a, BATTLE_VARS_STATUS_OPP + call GetBattleVarAddr + set PAR, [hl] + call UpdateOpponentInParty + ld hl, ApplyPrzEffectOnSpeed + call CallBattleCore + ld de, ANIM_PAR + call PlayOpponentBattleAnim + call RefreshBattleHuds + call PrintParalyze + ld hl, UseHeldStatusHealingItem + jp CallBattleCore + +BattleCommand_AttackUp: +; attackup + ld b, ATTACK + jr BattleCommand_StatUp + +BattleCommand_DefenseUp: +; defenseup + ld b, DEFENSE + jr BattleCommand_StatUp + +BattleCommand_SpeedUp: +; speedup + ld b, SPEED + jr BattleCommand_StatUp + +BattleCommand_SpecialAttackUp: +; specialattackup + ld b, SP_ATTACK + jr BattleCommand_StatUp + +BattleCommand_SpecialDefenseUp: +; specialdefenseup + ld b, SP_DEFENSE + jr BattleCommand_StatUp + +BattleCommand_AccuracyUp: +; accuracyup + ld b, ACCURACY + jr BattleCommand_StatUp + +BattleCommand_EvasionUp: +; evasionup + ld b, EVASION + jr BattleCommand_StatUp + +BattleCommand_AttackUp2: +; attackup2 + ld b, $10 | ATTACK + jr BattleCommand_StatUp + +BattleCommand_DefenseUp2: +; defenseup2 + ld b, $10 | DEFENSE + jr BattleCommand_StatUp + +BattleCommand_SpeedUp2: +; speedup2 + ld b, $10 | SPEED + jr BattleCommand_StatUp + +BattleCommand_SpecialAttackUp2: +; specialattackup2 + ld b, $10 | SP_ATTACK + jr BattleCommand_StatUp + +BattleCommand_SpecialDefenseUp2: +; specialdefenseup2 + ld b, $10 | SP_DEFENSE + jr BattleCommand_StatUp + +BattleCommand_AccuracyUp2: +; accuracyup2 + ld b, $10 | ACCURACY + jr BattleCommand_StatUp + +BattleCommand_EvasionUp2: +; evasionup2 + ld b, $10 | EVASION + jr BattleCommand_StatUp + +BattleCommand_StatUp: +; statup + call RaiseStat + ld a, [wFailedMessage] + and a + ret nz + jp MinimizeDropSub + +RaiseStat: + ld a, b + ld [wLoweredStat], a + ld hl, wPlayerStatLevels + ldh a, [hBattleTurn] + and a + jr z, .got_stat_levels + ld hl, wEnemyStatLevels +.got_stat_levels + ld a, [wAttackMissed] + and a + jp nz, .stat_raise_failed + ld a, [wEffectFailed] + and a + jp nz, .stat_raise_failed + ld a, [wLoweredStat] + and $f + ld c, a + ld b, 0 + add hl, bc + ld b, [hl] + inc b + ld a, $d + cp b + jp c, .cant_raise_stat + ld a, [wLoweredStat] + and $f0 + jr z, .got_num_stages + inc b + ld a, $d + cp b + jr nc, .got_num_stages + ld b, a +.got_num_stages + ld [hl], b + push hl + ld a, c + cp $5 + jr nc, .done_calcing_stats + ld hl, wBattleMonStats + 1 + ld de, wPlayerStats + ldh a, [hBattleTurn] + and a + jr z, .got_stats_pointer + ld hl, wEnemyMonStats + 1 + ld de, wEnemyStats +.got_stats_pointer + push bc + sla c + ld b, 0 + add hl, bc + ld a, c + add e + ld e, a + jr nc, .no_carry + inc d +.no_carry + pop bc + ld a, [hld] + sub LOW(MAX_STAT_VALUE) + jr nz, .not_already_max + ld a, [hl] + sbc HIGH(MAX_STAT_VALUE) + jp z, .stats_already_max +.not_already_max + ldh a, [hBattleTurn] + and a + jr z, .calc_player_stats + call CalcEnemyStats + jr .done_calcing_stats + +.calc_player_stats + call CalcPlayerStats +.done_calcing_stats + pop hl + xor a + ld [wFailedMessage], a + ret + +.stats_already_max + pop hl + dec [hl] + ; fallthrough + +.cant_raise_stat + ld a, $2 + ld [wFailedMessage], a + ld a, $1 + ld [wAttackMissed], a + ret + +.stat_raise_failed + ld a, $1 + ld [wFailedMessage], a + ret + +MinimizeDropSub: +; Lower the substitute if we're minimizing + + ld bc, wPlayerMinimized + ld hl, DropPlayerSub + ldh a, [hBattleTurn] + and a + jr z, .do_player + ld bc, wEnemyMinimized + ld hl, DropEnemySub +.do_player + ld a, BATTLE_VARS_MOVE_ANIM + call GetBattleVar + cp MINIMIZE + ret nz + + ld a, $1 + ld [bc], a + ; check battle scene + ld a, [wOptions] + add a + ret nc + + xor a + ldh [hBGMapMode], a + call CallBattleCore + call WaitBGMap + jp BattleCommand_MoveDelay + +BattleCommand_AttackDown: +; attackdown + ld a, ATTACK + jr BattleCommand_StatDown + +BattleCommand_DefenseDown: +; defensedown + ld a, DEFENSE + jr BattleCommand_StatDown + +BattleCommand_SpeedDown: +; speeddown + ld a, SPEED + jr BattleCommand_StatDown + +BattleCommand_SpecialAttackDown: +; specialattackdown + ld a, SP_ATTACK + jr BattleCommand_StatDown + +BattleCommand_SpecialDefenseDown: +; specialdefensedown + ld a, SP_DEFENSE + jr BattleCommand_StatDown + +BattleCommand_AccuracyDown: +; accuracydown + ld a, ACCURACY + jr BattleCommand_StatDown + +BattleCommand_EvasionDown: +; evasiondown + ld a, EVASION + jr BattleCommand_StatDown + +BattleCommand_AttackDown2: +; attackdown2 + ld a, $10 | ATTACK + jr BattleCommand_StatDown + +BattleCommand_DefenseDown2: +; defensedown2 + ld a, $10 | DEFENSE + jr BattleCommand_StatDown + +BattleCommand_SpeedDown2: +; speeddown2 + ld a, $10 | SPEED + jr BattleCommand_StatDown + +BattleCommand_SpecialAttackDown2: +; specialattackdown2 + ld a, $10 | SP_ATTACK + jr BattleCommand_StatDown + +BattleCommand_SpecialDefenseDown2: +; specialdefensedown2 + ld a, $10 | SP_DEFENSE + jr BattleCommand_StatDown + +BattleCommand_AccuracyDown2: +; accuracydown2 + ld a, $10 | ACCURACY + jr BattleCommand_StatDown + +BattleCommand_EvasionDown2: +; evasiondown2 + ld a, $10 | EVASION + +BattleCommand_StatDown: +; statdown + + ld [wLoweredStat], a + + call CheckMist + jp nz, .Mist + + ld hl, wEnemyStatLevels + ldh a, [hBattleTurn] + and a + jr z, .GetStatLevel + ld hl, wPlayerStatLevels + +.GetStatLevel: +; Attempt to lower the stat. + ld a, [wLoweredStat] + and $f + ld c, a + ld b, 0 + add hl, bc + ld b, [hl] + dec b + jp z, .CantLower + +; Sharply lower the stat if applicable. + ld a, [wLoweredStat] + and $f0 + jr z, .ComputerMiss + dec b + jr nz, .ComputerMiss + inc b + +.ComputerMiss: +; Computer opponents have a 25% chance of failing. + ldh a, [hBattleTurn] + and a + jr z, .DidntMiss + + ld a, [wLinkMode] + and a + jr nz, .DidntMiss + +; Lock-On still always works. + ld a, [wPlayerSubStatus5] + bit SUBSTATUS_LOCK_ON, a + jr nz, .DidntMiss + +; Attacking moves that also lower accuracy are unaffected. + ld a, BATTLE_VARS_MOVE_EFFECT + call GetBattleVar + cp EFFECT_ACCURACY_DOWN_HIT + jr z, .DidntMiss + + call BattleRandom + cp 25 percent + 1 ; 25% chance AI fails + jr c, .Failed + +.DidntMiss: + call CheckSubstituteOpp + jr nz, .Failed + + ld a, [wAttackMissed] + and a + jr nz, .Failed + + ld a, [wEffectFailed] + and a + jr nz, .Failed + + call CheckHiddenOpponent + jr nz, .Failed + +; Accuracy/Evasion reduction don't involve stats. + ld [hl], b + ld a, c + cp ACCURACY + jr nc, .Hit + + push hl + ld hl, wEnemyMonAttack + 1 + ld de, wEnemyStats + ldh a, [hBattleTurn] + and a + jr z, .do_enemy + ld hl, wBattleMonAttack + 1 + ld de, wPlayerStats +.do_enemy + call TryLowerStat + pop hl + jr z, .CouldntLower + +.Hit: + xor a + ld [wFailedMessage], a + ret + +.CouldntLower: + inc [hl] +.CantLower: + ld a, 3 + ld [wFailedMessage], a + ld a, 1 + ld [wAttackMissed], a + ret + +.Failed: + ld a, 1 + ld [wFailedMessage], a + ld [wAttackMissed], a + ret + +.Mist: + ld a, 2 + ld [wFailedMessage], a + ld a, 1 + ld [wAttackMissed], a + ret + +CheckMist: + ld a, BATTLE_VARS_MOVE_EFFECT + call GetBattleVar + cp EFFECT_ATTACK_DOWN + jr c, .dont_check_mist + cp EFFECT_EVASION_DOWN + 1 + jr c, .check_mist + cp EFFECT_ATTACK_DOWN_2 + jr c, .dont_check_mist + cp EFFECT_EVASION_DOWN_2 + 1 + jr c, .check_mist + cp EFFECT_ATTACK_DOWN_HIT + jr c, .dont_check_mist + cp EFFECT_EVASION_DOWN_HIT + 1 + jr c, .check_mist +.dont_check_mist + xor a + ret + +.check_mist + ld a, BATTLE_VARS_SUBSTATUS4_OPP + call GetBattleVar + bit SUBSTATUS_MIST, a + ret + +BattleCommand_StatUpMessage: + ld a, [wFailedMessage] + and a + ret nz + ld a, [wLoweredStat] + and $f + ld b, a + inc b + call GetStatName + ld hl, .stat + jp PrintText + +.stat + text_far Text_BattleEffectActivate + text_asm + ld hl, .BattleStatWentUpText + ld a, [wLoweredStat] + and $f0 + ret z + ld hl, .BattleStatWentWayUpText + ret + +.BattleStatWentWayUpText: + text_far _BattleStatWentWayUpText + text_end + +.BattleStatWentUpText: + text_far _BattleStatWentUpText + text_end + +BattleCommand_StatDownMessage: + ld a, [wFailedMessage] + and a + ret nz + ld a, [wLoweredStat] + and $f + ld b, a + inc b + call GetStatName + ld hl, .stat + jp PrintText + +.stat + text_far Text_BattleFoeEffectActivate + text_asm + ld hl, .BattleStatFellText + ld a, [wLoweredStat] + and $f0 + ret z + ld hl, .BattleStatSharplyFellText + ret + +.BattleStatSharplyFellText: + text_far _BattleStatSharplyFellText + text_end + +.BattleStatFellText: + text_far _BattleStatFellText + text_end + +TryLowerStat: +; Lower stat c from stat struct hl (buffer de). + + push bc + sla c + ld b, 0 + add hl, bc + ; add de, c + ld a, c + add e + ld e, a + jr nc, .no_carry + inc d +.no_carry + pop bc + +; The lowest possible stat is 1. + ld a, [hld] + sub 1 + jr nz, .not_min + ld a, [hl] + and a + ret z + +.not_min + ldh a, [hBattleTurn] + and a + jr z, .Player + + call BattleCommand_SwitchTurn + call CalcPlayerStats + call BattleCommand_SwitchTurn + jr .end + +.Player: + call BattleCommand_SwitchTurn + call CalcEnemyStats + call BattleCommand_SwitchTurn +.end + ld a, 1 + and a + ret + +BattleCommand_StatUpFailText: +; statupfailtext + ld a, [wFailedMessage] + and a + ret z + push af + call BattleCommand_MoveDelay + pop af + dec a + jp z, TryPrintButItFailed + ld a, [wLoweredStat] + and $f + ld b, a + inc b + call GetStatName + ld hl, WontRiseAnymoreText + jp StdBattleTextbox + +BattleCommand_StatDownFailText: +; statdownfailtext + ld a, [wFailedMessage] + and a + ret z + push af + call BattleCommand_MoveDelay + pop af + dec a + jp z, TryPrintButItFailed + dec a + ld hl, ProtectedByMistText + jp z, StdBattleTextbox + ld a, [wLoweredStat] + and $f + ld b, a + inc b + call GetStatName + ld hl, WontDropAnymoreText + jp StdBattleTextbox + +GetStatName: + ld hl, StatNames + ld c, "@" +.CheckName: + dec b + jr z, .Copy +.GetName: + ld a, [hli] + cp c + jr z, .CheckName + jr .GetName + +.Copy: + ld de, wStringBuffer2 + ld bc, 10 + jp CopyBytes + +INCLUDE "data/battle/stat_names.asm" + +INCLUDE "data/battle/stat_multipliers.asm" + +BattleCommand_AllStatsUp: +; allstatsup + +; Attack + call ResetMiss + call BattleCommand_AttackUp + call BattleCommand_StatUpMessage + +; Defense + call ResetMiss + call BattleCommand_DefenseUp + call BattleCommand_StatUpMessage + +; Speed + call ResetMiss + call BattleCommand_SpeedUp + call BattleCommand_StatUpMessage + +; Special Attack + call ResetMiss + call BattleCommand_SpecialAttackUp + call BattleCommand_StatUpMessage + +; Special Defense + call ResetMiss + call BattleCommand_SpecialDefenseUp + jp BattleCommand_StatUpMessage + +ResetMiss: + xor a + ld [wAttackMissed], a + ret + +LowerStat: + ld [wLoweredStat], a + + ld hl, wPlayerStatLevels + ldh a, [hBattleTurn] + and a + jr z, .got_target + ld hl, wEnemyStatLevels + +.got_target + ld a, [wLoweredStat] + and $f + ld c, a + ld b, 0 + add hl, bc + ld b, [hl] + dec b + jr z, .cant_lower_anymore + + ld a, [wLoweredStat] + and $f0 + jr z, .got_num_stages + dec b + jr nz, .got_num_stages + inc b + +.got_num_stages + ld [hl], b + ld a, c + cp 5 + jr nc, .accuracy_evasion + + push hl + ld hl, wBattleMonStats + 1 + ld de, wPlayerStats + ldh a, [hBattleTurn] + and a + jr z, .got_target_2 + ld hl, wEnemyMonStats + 1 + ld de, wEnemyStats + +.got_target_2 + call TryLowerStat + pop hl + jr z, .failed + +.accuracy_evasion + ldh a, [hBattleTurn] + and a + jr z, .player + + call CalcEnemyStats + + jr .finish + +.player + call CalcPlayerStats + +.finish + xor a + ld [wFailedMessage], a + ret + +.failed + inc [hl] + +.cant_lower_anymore + ld a, 2 + ld [wFailedMessage], a + ret + +BattleCommand_TriStatusChance: +; tristatuschance + + call BattleCommand_EffectChance + +.loop + ; 1/3 chance of each status + call BattleRandom + swap a + and %11 + jr z, .loop + dec a + ld hl, .ptrs + rst JumpTable + ret + +.ptrs + dw BattleCommand_ParalyzeTarget ; paralyze + dw BattleCommand_FreezeTarget ; freeze + dw BattleCommand_BurnTarget ; burn + +BattleCommand_Curl: +; curl + ld a, BATTLE_VARS_SUBSTATUS2 + call GetBattleVarAddr + set SUBSTATUS_CURLED, [hl] + ret + +BattleCommand_RaiseSubNoAnim: + ld hl, GetBattleMonBackpic + ldh a, [hBattleTurn] + and a + jr z, .PlayerTurn + ld hl, GetEnemyMonFrontpic +.PlayerTurn: + xor a + ldh [hBGMapMode], a + call CallBattleCore + jp WaitBGMap + +BattleCommand_LowerSubNoAnim: + ld hl, DropPlayerSub + ldh a, [hBattleTurn] + and a + jr z, .PlayerTurn + ld hl, DropEnemySub +.PlayerTurn: + xor a + ldh [hBGMapMode], a + call CallBattleCore + jp WaitBGMap + +CalcPlayerStats: + ld hl, wPlayerAtkLevel + ld de, wPlayerStats + ld bc, wBattleMonAttack + + ld a, 5 + call CalcBattleStats + + ld hl, BadgeStatBoosts + call CallBattleCore + + call BattleCommand_SwitchTurn + + ld hl, ApplyPrzEffectOnSpeed + call CallBattleCore + + ld hl, ApplyBrnEffectOnAttack + call CallBattleCore + + jp BattleCommand_SwitchTurn + +CalcEnemyStats: + ld hl, wEnemyAtkLevel + ld de, wEnemyStats + ld bc, wEnemyMonAttack + + ld a, 5 + call CalcBattleStats + + call BattleCommand_SwitchTurn + + ld hl, ApplyPrzEffectOnSpeed + call CallBattleCore + + ld hl, ApplyBrnEffectOnAttack + call CallBattleCore + + jp BattleCommand_SwitchTurn + +CalcBattleStats: +.loop + push af + ld a, [hli] + push hl + push bc + + ld c, a + dec c + ld b, 0 + ld hl, StatLevelMultipliers + add hl, bc + add hl, bc + + xor a + ldh [hMultiplicand + 0], a + ld a, [de] + ldh [hMultiplicand + 1], a + inc de + ld a, [de] + ldh [hMultiplicand + 2], a + inc de + + ld a, [hli] + ldh [hMultiplier], a + call Multiply + + ld a, [hl] + ldh [hDivisor], a + ld b, 4 + call Divide + + ldh a, [hQuotient + 2] + ld b, a + ldh a, [hQuotient + 3] + or b + jr nz, .check_maxed_out + + ld a, 1 + ldh [hQuotient + 3], a + jr .not_maxed_out + +.check_maxed_out + ldh a, [hQuotient + 3] + cp LOW(MAX_STAT_VALUE) + ld a, b + sbc HIGH(MAX_STAT_VALUE) + jr c, .not_maxed_out + + ld a, LOW(MAX_STAT_VALUE) + ldh [hQuotient + 3], a + ld a, HIGH(MAX_STAT_VALUE) + ldh [hQuotient + 2], a + +.not_maxed_out + pop bc + ldh a, [hQuotient + 2] + ld [bc], a + inc bc + ldh a, [hQuotient + 3] + ld [bc], a + inc bc + pop hl + pop af + dec a + jr nz, .loop + + ret + +INCLUDE "engine/battle/move_effects/bide.asm" + +BattleCommand_CheckRampage: +; checkrampage + + ld de, wPlayerRolloutCount + ldh a, [hBattleTurn] + and a + jr z, .player + ld de, wEnemyRolloutCount +.player + ld a, BATTLE_VARS_SUBSTATUS3 + call GetBattleVarAddr + bit SUBSTATUS_RAMPAGE, [hl] + ret z + ld a, [de] + dec a + ld [de], a + jr nz, .continue_rampage + + res SUBSTATUS_RAMPAGE, [hl] + call BattleCommand_SwitchTurn + call SafeCheckSafeguard + push af + call BattleCommand_SwitchTurn + pop af + jr nz, .continue_rampage + + set SUBSTATUS_CONFUSED, [hl] + call BattleRandom + and %00000001 + inc a + inc a + inc de ; ConfuseCount + ld [de], a +.continue_rampage + ld b, rampage_command + jp SkipToBattleCommand + +BattleCommand_Rampage: +; rampage + +; No rampage during Sleep Talk. + ld a, BATTLE_VARS_STATUS + call GetBattleVar + and SLP + ret nz + + ld de, wPlayerRolloutCount + ldh a, [hBattleTurn] + and a + jr z, .ok + ld de, wEnemyRolloutCount +.ok + ld a, BATTLE_VARS_SUBSTATUS3 + call GetBattleVarAddr + set SUBSTATUS_RAMPAGE, [hl] +; Rampage for 1 or 2 more turns + call BattleRandom + and %00000001 + inc a + ld [de], a + ld a, 1 + ld [wSomeoneIsRampaging], a + ret + +INCLUDE "engine/battle/move_effects/teleport.asm" + +BattleCommand_ForceSwitch: +; forceswitch + + ld a, [wBattleType] + cp BATTLETYPE_SHINY + jp z, .fail + cp BATTLETYPE_TRAP + jp z, .fail + ldh a, [hBattleTurn] + and a + jp nz, .force_player_switch + ld a, [wAttackMissed] + and a + jr nz, .missed + ld a, [wBattleMode] + dec a + jr nz, .trainer + ld a, [wCurPartyLevel] + ld b, a + ld a, [wBattleMonLevel] + cp b + jr nc, .wild_force_flee + add b + ld c, a + inc c +.random_loop_wild + call BattleRandom + cp c + jr nc, .random_loop_wild + srl b + srl b + cp b + jr nc, .wild_force_flee +.missed + jp .fail + +.wild_force_flee + call UpdateBattleMonInParty + xor a + ld [wNumHits], a + inc a + ld [wForcedSwitch], a + ; set battle draw + inc a + ld [wBattleResult], a + ld a, [wPlayerMoveStructAnimation] + jp .succeed + +.trainer + call FindAliveEnemyMons + jr c, .switch_fail + ld a, [wEnemyGoesFirst] + and a + jr z, .switch_fail + call UpdateEnemyMonInParty + ld a, $1 + ld [wKickCounter], a + call AnimateCurrentMove + ld c, $14 + call DelayFrames + hlcoord 1, 0 + lb bc, 4, 10 + call ClearBox + ld c, 20 + call DelayFrames + ld a, [wOTPartyCount] + ld b, a + ld a, [wCurOTMon] + ld c, a +; select a random enemy mon to switch to +.random_loop_trainer + call BattleRandom + and $7 + cp b + jr nc, .random_loop_trainer + cp c + jr z, .random_loop_trainer + push af + push bc + ld hl, wOTPartyMon1HP + call GetPartyLocation + ld a, [hli] + or [hl] + pop bc + pop de + jr z, .random_loop_trainer + ld a, d + inc a + ld [wEnemySwitchMonIndex], a + callfar ForceEnemySwitch + + ld hl, DraggedOutText + call StdBattleTextbox + + ld hl, SpikesDamage + jp CallBattleCore + +.switch_fail + jp .fail + +.force_player_switch + ld a, [wAttackMissed] + and a + jr nz, .player_miss + + ld a, [wBattleMode] + dec a + jr nz, .vs_trainer + + ld a, [wBattleMonLevel] + ld b, a + ld a, [wCurPartyLevel] + cp b + jr nc, .wild_succeed_playeristarget + + add b + ld c, a + inc c +.wild_random_loop_playeristarget + call BattleRandom + cp c + jr nc, .wild_random_loop_playeristarget + + srl b + srl b + cp b + jr nc, .wild_succeed_playeristarget + +.player_miss + jp .fail + +.wild_succeed_playeristarget + call UpdateBattleMonInParty + xor a + ld [wNumHits], a + inc a + ld [wForcedSwitch], a + ; set battle draw + inc a + ld [wBattleResult], a + ld a, [wEnemyMoveStructAnimation] + jr .succeed + +.vs_trainer + call CheckPlayerHasMonToSwitchTo + jr c, .switch_fail2 + + ld a, [wEnemyGoesFirst] + cp $1 + jr z, .switch_fail + + call UpdateBattleMonInParty + ld a, $1 + ld [wKickCounter], a + call AnimateCurrentMove + ld c, 20 + call DelayFrames + hlcoord 9, 7 + lb bc, 5, 11 + call ClearBox + ld c, 20 + call DelayFrames + ld a, [wPartyCount] + ld b, a + ld a, [wCurBattleMon] + ld c, a +.random_loop_trainer_playeristarget + call BattleRandom + and $7 + cp b + jr nc, .random_loop_trainer_playeristarget + + cp c + jr z, .random_loop_trainer_playeristarget + + push af + push bc + ld hl, wPartyMon1HP + call GetPartyLocation + ld a, [hli] + or [hl] + pop bc + pop de + jr z, .random_loop_trainer_playeristarget + + ld a, d + ld [wCurPartyMon], a + ld hl, SwitchPlayerMon + call CallBattleCore + + ld hl, DraggedOutText + call StdBattleTextbox + + ld hl, SpikesDamage + jp CallBattleCore + +.switch_fail2 + jr .fail + +.succeed + push af + ld a, DRAW + ld [wBattleResult], a + ld a, $1 + ld [wKickCounter], a + call AnimateCurrentMove + ld c, 20 + call DelayFrames + pop af + + ld hl, FledInFearText + cp ROAR + jr z, .do_text + ld hl, BlownAwayText +.do_text + jp StdBattleTextbox + +.fail + call BattleCommand_LowerSub + call BattleCommand_MoveDelay + call BattleCommand_RaiseSub + jp PrintButItFailed + +CheckPlayerHasMonToSwitchTo: + ld a, [wPartyCount] + ld d, a + ld e, 0 + ld bc, PARTYMON_STRUCT_LENGTH +.loop + ld a, [wCurBattleMon] + cp e + jr z, .next + + ld a, e + ld hl, wPartyMon1HP + call AddNTimes + ld a, [hli] + or [hl] + jr nz, .not_fainted + +.next + inc e + dec d + jr nz, .loop + + scf + ret + +.not_fainted + and a + ret + +BattleCommand_EndLoop: +; endloop + +; Loop back to 'critical'. + + ld de, wPlayerRolloutCount + ld bc, wPlayerDamageTaken + ldh a, [hBattleTurn] + and a + jr z, .got_addrs + ld de, wEnemyRolloutCount + ld bc, wEnemyDamageTaken +.got_addrs + + ld a, BATTLE_VARS_SUBSTATUS3 + call GetBattleVarAddr + bit SUBSTATUS_IN_LOOP, [hl] + jp nz, .in_loop + set SUBSTATUS_IN_LOOP, [hl] + ld a, BATTLE_VARS_MOVE_EFFECT + call GetBattleVarAddr + ld a, [hl] + cp EFFECT_POISON_MULTI_HIT + jr z, .twineedle + cp EFFECT_DOUBLE_HIT + ld a, 1 + jr z, .double_hit + ld a, [hl] + cp EFFECT_BEAT_UP + jr z, .beat_up + cp EFFECT_TRIPLE_KICK + jr nz, .not_triple_kick +.reject_triple_kick_sample + call BattleRandom + and $3 + jr z, .reject_triple_kick_sample + dec a + jr nz, .double_hit + ld a, 1 + ld [bc], a + jr .done_loop + +.beat_up + ldh a, [hBattleTurn] + and a + jr nz, .check_ot_beat_up + ld a, [wPartyCount] + cp 1 + jp z, .only_one_beatup + dec a + jr .double_hit + +.check_ot_beat_up + ld a, [wBattleMode] + cp WILD_BATTLE + jp z, .only_one_beatup + ld a, [wOTPartyCount] + cp 1 + jp z, .only_one_beatup + dec a + jr .double_hit + +.only_one_beatup + ld a, BATTLE_VARS_SUBSTATUS3 + call GetBattleVarAddr + res SUBSTATUS_IN_LOOP, [hl] + call BattleCommand_BeatUpFailText + jp EndMoveEffect + +.not_triple_kick + call BattleRandom + and $3 + cp 2 + jr c, .got_number_hits + call BattleRandom + and $3 +.got_number_hits + inc a +.double_hit + ld [de], a + inc a + ld [bc], a + jr .loop_back_to_critical + +.twineedle + ld a, 1 + jr .double_hit + +.in_loop + ld a, [de] + dec a + ld [de], a + jr nz, .loop_back_to_critical +.done_loop + ld a, BATTLE_VARS_SUBSTATUS3 + call GetBattleVarAddr + res SUBSTATUS_IN_LOOP, [hl] + + ld hl, PlayerHitTimesText + ldh a, [hBattleTurn] + and a + jr z, .got_hit_n_times_text + ld hl, EnemyHitTimesText +.got_hit_n_times_text + + push bc + ld a, BATTLE_VARS_MOVE_EFFECT + call GetBattleVar + cp EFFECT_BEAT_UP + jr z, .beat_up_2 + call StdBattleTextbox +.beat_up_2 + + pop bc + xor a + ld [bc], a + ret + +.loop_back_to_critical + ld a, [wBattleScriptBufferAddress + 1] + ld h, a + ld a, [wBattleScriptBufferAddress] + ld l, a +.not_critical + ld a, [hld] + cp critical_command + jr nz, .not_critical + inc hl + ld a, h + ld [wBattleScriptBufferAddress + 1], a + ld a, l + ld [wBattleScriptBufferAddress], a + ret + +BattleCommand_FakeOut: + ld a, [wAttackMissed] + and a + ret nz + + call CheckSubstituteOpp + jr nz, .fail + + ld a, BATTLE_VARS_STATUS_OPP + call GetBattleVar + and 1 << FRZ | SLP + jr nz, .fail + + call CheckOpponentWentFirst + jr z, FlinchTarget + +.fail + ld a, 1 + ld [wAttackMissed], a + ret + +BattleCommand_FlinchTarget: + call CheckSubstituteOpp + ret nz + + ld a, BATTLE_VARS_STATUS_OPP + call GetBattleVar + and 1 << FRZ | SLP + ret nz + + call CheckOpponentWentFirst + ret nz + + ld a, [wEffectFailed] + and a + ret nz + + ; fallthrough + +FlinchTarget: + ld a, BATTLE_VARS_SUBSTATUS3_OPP + call GetBattleVarAddr + set SUBSTATUS_FLINCHED, [hl] + jp EndRechargeOpp + +CheckOpponentWentFirst: +; Returns a=0, z if user went first +; Returns a=1, nz if opponent went first + push bc + ld a, [wEnemyGoesFirst] ; 0 if player went first + ld b, a + ldh a, [hBattleTurn] ; 0 if it's the player's turn + xor b ; 1 if opponent went first + pop bc + ret + +BattleCommand_HeldFlinch: +; kingsrock + + ld a, [wAttackMissed] + and a + ret nz + + call GetUserItem + ld a, b + cp HELD_FLINCH + ret nz + + call CheckSubstituteOpp + ret nz + ld a, BATTLE_VARS_MOVE_EFFECT + call GetBattleVarAddr + ld d, h + ld e, l + call GetUserItem + call BattleRandom + cp c + ret nc + call EndRechargeOpp + ld a, BATTLE_VARS_SUBSTATUS3_OPP + call GetBattleVarAddr + set SUBSTATUS_FLINCHED, [hl] + ret + +BattleCommand_OHKO: +; ohko + + call ResetDamage + ld a, [wTypeModifier] + and $7f + jr z, .no_effect + ld hl, wEnemyMonLevel + ld de, wBattleMonLevel + ld bc, wPlayerMoveStruct + MOVE_ACC + ldh a, [hBattleTurn] + and a + jr z, .got_move_accuracy + push hl + ld h, d + ld l, e + pop de + ld bc, wEnemyMoveStruct + MOVE_ACC +.got_move_accuracy + ld a, [de] + sub [hl] + jr c, .no_effect + add a + ld e, a + ld a, [bc] + add e + jr nc, .finish_ohko + ld a, $ff +.finish_ohko + ld [bc], a + call BattleCommand_CheckHit + ld hl, wCurDamage + ld a, $ff + ld [hli], a + ld [hl], a + ld a, $2 + ld [wCriticalHit], a + ret + +.no_effect + ld a, $ff + ld [wCriticalHit], a + ld a, $1 + ld [wAttackMissed], a + ret + +BattleCommand_CheckCharge: +; checkcharge + + ld a, BATTLE_VARS_SUBSTATUS3 + call GetBattleVarAddr + bit SUBSTATUS_CHARGED, [hl] + ret z + res SUBSTATUS_CHARGED, [hl] + res SUBSTATUS_UNDERGROUND, [hl] + res SUBSTATUS_FLYING, [hl] + ld b, charge_command + jp SkipToBattleCommand + +BattleCommand_Charge: +; charge + + call BattleCommand_ClearText + ld a, BATTLE_VARS_STATUS + call GetBattleVar + and SLP + jr z, .awake + + call BattleCommand_MoveDelay + call BattleCommand_RaiseSub + call PrintButItFailed + jp EndMoveEffect + +.awake + ld a, BATTLE_VARS_SUBSTATUS3 + call GetBattleVarAddr + set SUBSTATUS_CHARGED, [hl] + + ld hl, IgnoredOrders2Text + ld a, [wAlreadyDisobeyed] + and a + call nz, StdBattleTextbox + + call BattleCommand_LowerSub + xor a + ld [wNumHits], a + inc a + ld [wKickCounter], a + call LoadMoveAnim + ld a, BATTLE_VARS_MOVE_ANIM + call GetBattleVar + cp FLY + jr z, .flying + cp DIG + jr z, .flying + call BattleCommand_RaiseSub + jr .not_flying + +.flying + call DisappearUser +.not_flying + ld a, BATTLE_VARS_SUBSTATUS3 + call GetBattleVarAddr + ld a, BATTLE_VARS_MOVE_ANIM + call GetBattleVar + ld b, a + cp FLY + jr z, .set_flying + cp DIG + jr nz, .dont_set_digging + set SUBSTATUS_UNDERGROUND, [hl] + jr .dont_set_digging + +.set_flying + set SUBSTATUS_FLYING, [hl] + +.dont_set_digging + call CheckUserIsCharging + jr nz, .mimic + ld a, BATTLE_VARS_LAST_COUNTER_MOVE + call GetBattleVarAddr + ld [hl], b + ld a, BATTLE_VARS_LAST_MOVE + call GetBattleVarAddr + ld [hl], b + +.mimic + call ResetDamage + + ld hl, .UsedText + call PrintText + + ld a, BATTLE_VARS_MOVE_EFFECT + call GetBattleVar + cp EFFECT_SKULL_BASH + ld b, endturn_command + jp z, SkipToBattleCommand + jp EndMoveEffect + +.UsedText: + text_far Text_BattleUser ; "<USER>" + text_asm + ld a, BATTLE_VARS_MOVE_ANIM + call GetBattleVar + cp RAZOR_WIND + ld hl, .BattleMadeWhirlwindText + jr z, .done + + cp SOLARBEAM + ld hl, .BattleTookSunlightText + jr z, .done + + cp SKULL_BASH + ld hl, .BattleLoweredHeadText + jr z, .done + + cp SKY_ATTACK + ld hl, .BattleGlowingText + jr z, .done + + cp FLY + ld hl, .BattleFlewText + jr z, .done + + cp DIG + ld hl, .BattleDugText + +.done + ret + +.BattleMadeWhirlwindText: + text_far _BattleMadeWhirlwindText + text_end + +.BattleTookSunlightText: + text_far _BattleTookSunlightText + text_end + +.BattleLoweredHeadText: + text_far _BattleLoweredHeadText + text_end + +.BattleGlowingText: + text_far _BattleGlowingText + text_end + +.BattleFlewText: + text_far _BattleFlewText + text_end + +.BattleDugText: + text_far _BattleDugText + text_end + +BattleCommand3c: +; unused + ret + +BattleCommand_TrapTarget: +; traptarget + + ld a, [wAttackMissed] + and a + ret nz + ld hl, wEnemyWrapCount + ld de, wEnemyTrappingMove + ldh a, [hBattleTurn] + and a + jr z, .got_trap + ld hl, wPlayerWrapCount + ld de, wPlayerTrappingMove + +.got_trap + ld a, [hl] + and a + ret nz + ld a, BATTLE_VARS_SUBSTATUS4_OPP + call GetBattleVar + bit SUBSTATUS_SUBSTITUTE, a + ret nz + call BattleRandom + ; trapped for 2-5 turns + and %11 + inc a + inc a + inc a + ld [hl], a + ld a, BATTLE_VARS_MOVE_ANIM + call GetBattleVar + ld [de], a + ld b, a + ld hl, .Traps + +.find_trap_text + ld a, [hli] + cp b + jr z, .found_trap_text + inc hl + inc hl + jr .find_trap_text + +.found_trap_text + ld a, [hli] + ld h, [hl] + ld l, a + jp StdBattleTextbox + +.Traps: + dbw BIND, UsedBindText ; 'used BIND on' + dbw WRAP, WrappedByText ; 'was WRAPPED by' + dbw FIRE_SPIN, WasTrappedText ; 'was trapped!' + dbw CLAMP, ClampedByText ; 'was CLAMPED by' + dbw WHIRLPOOL, WasTrappedText ; 'was trapped!' + +INCLUDE "engine/battle/move_effects/mist.asm" + +INCLUDE "engine/battle/move_effects/focus_energy.asm" + +BattleCommand_Recoil: +; recoil + + ld hl, wBattleMonMaxHP + ldh a, [hBattleTurn] + and a + jr z, .got_hp + ld hl, wEnemyMonMaxHP +.got_hp + ld a, BATTLE_VARS_MOVE_ANIM + call GetBattleVar + ld d, a +; get 1/4 damage or 1 HP, whichever is higher + ld a, [wCurDamage] + ld b, a + ld a, [wCurDamage + 1] + ld c, a + srl b + rr c + srl b + rr c + ld a, b + or c + jr nz, .min_damage + inc c +.min_damage + ld a, [hli] + ld [wBuffer2], a + ld a, [hl] + ld [wBuffer1], a + dec hl + dec hl + ld a, [hl] + ld [wBuffer3], a + sub c + ld [hld], a + ld [wBuffer5], a + ld a, [hl] + ld [wBuffer4], a + sbc b + ld [hl], a + ld [wBuffer6], a + jr nc, .dont_ko + xor a + ld [hli], a + ld [hl], a + ld hl, wBuffer5 + ld [hli], a + ld [hl], a +.dont_ko + hlcoord 10, 9 + ldh a, [hBattleTurn] + and a + ld a, 1 + jr z, .animate_hp_bar + hlcoord 2, 2 + xor a +.animate_hp_bar + ld [wWhichHPBar], a + predef AnimateHPBar + call RefreshBattleHuds + ld hl, RecoilText + jp StdBattleTextbox + +BattleCommand_ConfuseTarget: +; confusetarget + + call GetOpponentItem + ld a, b + cp HELD_PREVENT_CONFUSE + ret z + ld a, [wEffectFailed] + and a + ret nz + call SafeCheckSafeguard + ret nz + call CheckSubstituteOpp + ret nz + ld a, BATTLE_VARS_SUBSTATUS3_OPP + call GetBattleVarAddr + bit SUBSTATUS_CONFUSED, [hl] + ret nz + jr BattleCommand_FinishConfusingTarget + +BattleCommand_Confuse: +; confuse + + call GetOpponentItem + ld a, b + cp HELD_PREVENT_CONFUSE + jr nz, .no_item_protection + ld a, [hl] + ld [wNamedObjectIndexBuffer], a + call GetItemName + call AnimateFailedMove + ld hl, ProtectedByText + jp StdBattleTextbox + +.no_item_protection + ld a, BATTLE_VARS_SUBSTATUS3_OPP + call GetBattleVarAddr + bit SUBSTATUS_CONFUSED, [hl] + jr z, .not_already_confused + call AnimateFailedMove + ld hl, AlreadyConfusedText + jp StdBattleTextbox + +.not_already_confused + call CheckSubstituteOpp + jr nz, BattleCommand_Confuse_CheckSnore_Swagger_ConfuseHit + ld a, [wAttackMissed] + and a + jr nz, BattleCommand_Confuse_CheckSnore_Swagger_ConfuseHit +BattleCommand_FinishConfusingTarget: + ld bc, wEnemyConfuseCount + ldh a, [hBattleTurn] + and a + jr z, .got_confuse_count + ld bc, wPlayerConfuseCount + +.got_confuse_count + set SUBSTATUS_CONFUSED, [hl] + ; confused for 2-5 turns + call BattleRandom + and %11 + inc a + inc a + ld [bc], a + + ld a, BATTLE_VARS_MOVE_EFFECT + call GetBattleVar + cp EFFECT_CONFUSE_HIT + jr z, .got_effect + cp EFFECT_SNORE + jr z, .got_effect + cp EFFECT_SWAGGER + jr z, .got_effect + call AnimateCurrentMove + +.got_effect + ld de, ANIM_CONFUSED + call PlayOpponentBattleAnim + + ld hl, BecameConfusedText + call StdBattleTextbox + + call GetOpponentItem + ld a, b + cp HELD_HEAL_STATUS + jr z, .heal_confusion + cp HELD_HEAL_CONFUSION + ret nz +.heal_confusion + ld hl, UseConfusionHealingItem + jp CallBattleCore + +BattleCommand_Confuse_CheckSnore_Swagger_ConfuseHit: + ld a, BATTLE_VARS_MOVE_EFFECT + call GetBattleVar + cp EFFECT_CONFUSE_HIT + ret z + cp EFFECT_SNORE + ret z + cp EFFECT_SWAGGER + ret z + jp PrintDidntAffect2 + +BattleCommand_Paralyze: +; paralyze + + ld a, BATTLE_VARS_STATUS_OPP + call GetBattleVar + bit PAR, a + jr nz, .paralyzed + ld a, [wTypeModifier] + and $7f + jr z, .didnt_affect + call GetOpponentItem + ld a, b + cp HELD_PREVENT_PARALYZE + jr nz, .no_item_protection + ld a, [hl] + ld [wNamedObjectIndexBuffer], a + call GetItemName + call AnimateFailedMove + ld hl, ProtectedByText + jp StdBattleTextbox + +.no_item_protection + ldh a, [hBattleTurn] + and a + jr z, .dont_sample_failure + + ld a, [wLinkMode] + and a + jr nz, .dont_sample_failure + + ld a, [wPlayerSubStatus5] + bit SUBSTATUS_LOCK_ON, a + jr nz, .dont_sample_failure + + call BattleRandom + cp 25 percent + 1 ; 25% chance AI fails + jr c, .failed + +.dont_sample_failure + ld a, BATTLE_VARS_STATUS_OPP + call GetBattleVarAddr + and a + jr nz, .failed + ld a, [wAttackMissed] + and a + jr nz, .failed + call CheckSubstituteOpp + jr nz, .failed + ld c, 30 + call DelayFrames + call AnimateCurrentMove + ld a, $1 + ldh [hBGMapMode], a + ld a, BATTLE_VARS_STATUS_OPP + call GetBattleVarAddr + set PAR, [hl] + call UpdateOpponentInParty + ld hl, ApplyPrzEffectOnSpeed + call CallBattleCore + call UpdateBattleHuds + call PrintParalyze + ld hl, UseHeldStatusHealingItem + jp CallBattleCore + +.paralyzed + call AnimateFailedMove + ld hl, AlreadyParalyzedText + jp StdBattleTextbox + +.failed + jp PrintDidntAffect2 + +.didnt_affect + call AnimateFailedMove + jp PrintDoesntAffect + +CheckMoveTypeMatchesTarget: +; Compare move type to opponent type. +; Return z if matching the opponent type, +; unless the move is Normal (Tri Attack). + + push hl + + ld hl, wEnemyMonType1 + ldh a, [hBattleTurn] + and a + jr z, .ok + ld hl, wBattleMonType1 +.ok + + ld a, BATTLE_VARS_MOVE_TYPE + call GetBattleVar + cp NORMAL + jr z, .normal + + cp [hl] + jr z, .return + + inc hl + cp [hl] + +.return + pop hl + ret + +.normal + ld a, 1 + and a + pop hl + ret + +INCLUDE "engine/battle/move_effects/substitute.asm" + +BattleCommand_RechargeNextTurn: +; rechargenextturn + ld a, BATTLE_VARS_SUBSTATUS4 + call GetBattleVarAddr + set SUBSTATUS_RECHARGE, [hl] + ret + +EndRechargeOpp: + push hl + ld a, BATTLE_VARS_SUBSTATUS4_OPP + call GetBattleVarAddr + res SUBSTATUS_RECHARGE, [hl] + pop hl + ret + +INCLUDE "engine/battle/move_effects/rage.asm" + +BattleCommand_DoubleFlyingDamage: +; doubleflyingdamage + ld a, BATTLE_VARS_SUBSTATUS3_OPP + call GetBattleVar + bit SUBSTATUS_FLYING, a + ret z + jr DoubleDamage + +BattleCommand_DoubleUndergroundDamage: +; doubleundergrounddamage + ld a, BATTLE_VARS_SUBSTATUS3_OPP + call GetBattleVar + bit SUBSTATUS_UNDERGROUND, a + ret z + + ; fallthrough + +DoubleDamage: + ld hl, wCurDamage + 1 + sla [hl] + dec hl + rl [hl] + jr nc, .quit + + ld a, $ff + ld [hli], a + ld [hl], a +.quit + ret + +INCLUDE "engine/battle/move_effects/mimic.asm" + +INCLUDE "engine/battle/move_effects/leech_seed.asm" + +INCLUDE "engine/battle/move_effects/splash.asm" + +INCLUDE "engine/battle/move_effects/disable.asm" + +INCLUDE "engine/battle/move_effects/pay_day.asm" + +INCLUDE "engine/battle/move_effects/conversion.asm" + +BattleCommand_ResetStats: +; resetstats + + ld a, 7 ; neutral + ld hl, wPlayerStatLevels + call .Fill + ld hl, wEnemyStatLevels + call .Fill + + ldh a, [hBattleTurn] + push af + + call SetPlayerTurn + call CalcPlayerStats + call SetEnemyTurn + call CalcEnemyStats + + pop af + ldh [hBattleTurn], a + + call AnimateCurrentMove + + ld hl, EliminatedStatsText + jp StdBattleTextbox + +.Fill: + ld b, wPlayerStatLevelsEnd - wPlayerStatLevels +.next + ld [hli], a + dec b + jr nz, .next + ret + +BattleCommand_Heal: +; heal + + ld de, wBattleMonHP + ld hl, wBattleMonMaxHP + ldh a, [hBattleTurn] + and a + jr z, .got_hp + ld de, wEnemyMonHP + ld hl, wEnemyMonMaxHP +.got_hp + ld a, BATTLE_VARS_MOVE_ANIM + call GetBattleVar + ld b, a + push hl + push de + push bc + ld c, 2 + call CompareBytes + pop bc + pop de + pop hl + jp z, .hp_full + ld a, b + cp REST + jr nz, .not_rest + + push hl + push de + push af + call BattleCommand_MoveDelay + ld a, BATTLE_VARS_SUBSTATUS5 + call GetBattleVarAddr + res SUBSTATUS_TOXIC, [hl] + ld a, BATTLE_VARS_STATUS + call GetBattleVarAddr + ld a, [hl] + and a + ld [hl], REST_SLEEP_TURNS + 1 + ld hl, WentToSleepText + jr z, .no_status_to_heal + ld hl, RestedText +.no_status_to_heal + call StdBattleTextbox + ldh a, [hBattleTurn] + and a + jr nz, .calc_enemy_stats + call CalcPlayerStats + jr .got_stats + +.calc_enemy_stats + call CalcEnemyStats +.got_stats + pop af + pop de + pop hl + +.not_rest + jr z, .restore_full_hp + ld hl, GetHalfMaxHP + call CallBattleCore + jr .finish + +.restore_full_hp + ld hl, GetMaxHP + call CallBattleCore +.finish + call AnimateCurrentMove + call BattleCommand_SwitchTurn + ld hl, RestoreHP + call CallBattleCore + call BattleCommand_SwitchTurn + call UpdateUserInParty + call RefreshBattleHuds + ld hl, RegainedHealthText + jp StdBattleTextbox + +.hp_full + call AnimateFailedMove + ld hl, HPIsFullText + jp StdBattleTextbox + +INCLUDE "engine/battle/move_effects/transform.asm" + +BattleEffect_ButItFailed: + call AnimateFailedMove + jp PrintButItFailed + +ClearLastMove: + ld a, BATTLE_VARS_LAST_COUNTER_MOVE + call GetBattleVarAddr + xor a + ld [hl], a + + ld a, BATTLE_VARS_LAST_MOVE + call GetBattleVarAddr + xor a + ld [hl], a + ret + +ResetActorDisable: + ldh a, [hBattleTurn] + and a + jr z, .player + + xor a + ld [wEnemyDisableCount], a + ld [wEnemyDisabledMove], a + ret + +.player + xor a + ld [wPlayerDisableCount], a + ld [wDisabledMove], a + ret + +BattleCommand_Screen: +; screen + + ld hl, wPlayerScreens + ld bc, wPlayerLightScreenCount + ldh a, [hBattleTurn] + and a + jr z, .got_screens_pointer + ld hl, wEnemyScreens + ld bc, wEnemyLightScreenCount + +.got_screens_pointer + ld a, BATTLE_VARS_MOVE_EFFECT + call GetBattleVar + cp EFFECT_LIGHT_SCREEN + jr nz, .Reflect + + bit SCREENS_LIGHT_SCREEN, [hl] + jr nz, .failed + set SCREENS_LIGHT_SCREEN, [hl] + ld a, 5 + ld [bc], a + ld hl, LightScreenEffectText + jr .good + +.Reflect: + bit SCREENS_REFLECT, [hl] + jr nz, .failed + set SCREENS_REFLECT, [hl] + + ; LightScreenCount -> ReflectCount + inc bc + + ld a, 5 + ld [bc], a + ld hl, ReflectEffectText + +.good + call AnimateCurrentMove + jp StdBattleTextbox + +.failed + call AnimateFailedMove + jp PrintButItFailed + +PrintDoesntAffect: +; 'it doesn't affect' + ld hl, DoesntAffectText + jp StdBattleTextbox + +PrintNothingHappened: +; 'but nothing happened!' + ld hl, NothingHappenedText + jp StdBattleTextbox + +TryPrintButItFailed: + ld a, [wAlreadyFailed] + and a + ret nz + + ; fallthrough + +PrintButItFailed: +; 'but it failed!' + ld hl, ButItFailedText + jp StdBattleTextbox + +FailMove: + call AnimateFailedMove + ; fallthrough + +FailMimic: + ld hl, ButItFailedText ; 'but it failed!' + ld de, ItFailedText ; 'it failed!' + jp FailText_CheckOpponentProtect + +PrintDidntAffect: +; 'it didn't affect' + ld hl, DidntAffect1Text + jp StdBattleTextbox + +PrintDidntAffect2: + call AnimateFailedMove + ld hl, DidntAffect1Text ; 'it didn't affect' + ld de, DidntAffect2Text ; 'it didn't affect' + jp FailText_CheckOpponentProtect + +PrintParalyze: +; 'paralyzed! maybe it can't attack!' + ld hl, ParalyzedText + jp StdBattleTextbox + +CheckSubstituteOpp: + ld a, BATTLE_VARS_SUBSTATUS4_OPP + call GetBattleVar + bit SUBSTATUS_SUBSTITUTE, a + ret + +INCLUDE "engine/battle/move_effects/selfdestruct.asm" + +INCLUDE "engine/battle/move_effects/mirror_move.asm" + +INCLUDE "engine/battle/move_effects/metronome.asm" + +CheckUserMove: +; Return z if the user has move a. + ld b, a + ld de, wBattleMonMoves + ldh a, [hBattleTurn] + and a + jr z, .ok + ld de, wEnemyMonMoves +.ok + + ld c, NUM_MOVES +.loop + ld a, [de] + inc de + cp b + ret z + + dec c + jr nz, .loop + + ld a, 1 + and a + ret + +ResetTurn: + ld hl, wPlayerCharging + ldh a, [hBattleTurn] + and a + jr z, .player + ld hl, wEnemyCharging + +.player + ld [hl], 1 + xor a + ld [wAlreadyDisobeyed], a + call DoMove + jp EndMoveEffect + +INCLUDE "engine/battle/move_effects/thief.asm" + +BattleCommand_ArenaTrap: +; arenatrap + +; Doesn't work on an absent opponent. + + call CheckHiddenOpponent + jr nz, .failed + +; Don't trap if the opponent is already trapped. + + ld a, BATTLE_VARS_SUBSTATUS5 + call GetBattleVarAddr + bit SUBSTATUS_CANT_RUN, [hl] + jr nz, .failed + +; Otherwise trap the opponent. + + set SUBSTATUS_CANT_RUN, [hl] + call AnimateCurrentMove + ld hl, CantEscapeNowText + jp StdBattleTextbox + +.failed + call AnimateFailedMove + jp PrintButItFailed + +INCLUDE "engine/battle/move_effects/nightmare.asm" + +BattleCommand_Defrost: +; defrost + +; Thaw the user. + + ld a, BATTLE_VARS_STATUS + call GetBattleVarAddr + bit FRZ, [hl] + ret z + res FRZ, [hl] + +; Don't update the enemy's party struct in a wild battle. + + ldh a, [hBattleTurn] + and a + jr z, .party + + ld a, [wBattleMode] + dec a + jr z, .done + +.party + ld a, MON_STATUS + call UserPartyAttr + res FRZ, [hl] + +.done + call RefreshBattleHuds + ld hl, WasDefrostedText + jp StdBattleTextbox + +INCLUDE "engine/battle/move_effects/curse.asm" + +INCLUDE "engine/battle/move_effects/protect.asm" + +INCLUDE "engine/battle/move_effects/endure.asm" + +INCLUDE "engine/battle/move_effects/spikes.asm" + +INCLUDE "engine/battle/move_effects/foresight.asm" + +INCLUDE "engine/battle/move_effects/perish_song.asm" + +INCLUDE "engine/battle/move_effects/sandstorm.asm" + +INCLUDE "engine/battle/move_effects/rollout.asm" + +BattleCommand5d: +; unused + ret + +INCLUDE "engine/battle/move_effects/fury_cutter.asm" + +INCLUDE "engine/battle/move_effects/attract.asm" + +INCLUDE "engine/battle/move_effects/return.asm" + +INCLUDE "engine/battle/move_effects/present.asm" + +INCLUDE "engine/battle/move_effects/frustration.asm" + +INCLUDE "engine/battle/move_effects/safeguard.asm" + +SafeCheckSafeguard: + push hl + ld hl, wEnemyScreens + ldh a, [hBattleTurn] + and a + jr z, .got_turn + ld hl, wPlayerScreens + +.got_turn + bit SCREENS_SAFEGUARD, [hl] + pop hl + ret + +BattleCommand_CheckSafeguard: +; checksafeguard + ld hl, wEnemyScreens + ldh a, [hBattleTurn] + and a + jr z, .got_turn + ld hl, wPlayerScreens +.got_turn + bit SCREENS_SAFEGUARD, [hl] + ret z + ld a, 1 + ld [wAttackMissed], a + call BattleCommand_MoveDelay + ld hl, SafeguardProtectText + call StdBattleTextbox + jp EndMoveEffect + +INCLUDE "engine/battle/move_effects/magnitude.asm" + +INCLUDE "engine/battle/move_effects/baton_pass.asm" + +INCLUDE "engine/battle/move_effects/pursuit.asm" + +INCLUDE "engine/battle/move_effects/rapid_spin.asm" + +BattleCommand_HealMorn: +; healmorn + ld b, MORN_F + jr BattleCommand_TimeBasedHealContinue + +BattleCommand_HealDay: +; healday + ld b, DAY_F + jr BattleCommand_TimeBasedHealContinue + +BattleCommand_HealNite: +; healnite + ld b, NITE_F + ; fallthrough + +BattleCommand_TimeBasedHealContinue: +; Time- and weather-sensitive heal. + + ld hl, wBattleMonMaxHP + ld de, wBattleMonHP + ldh a, [hBattleTurn] + and a + jr z, .start + ld hl, wEnemyMonMaxHP + ld de, wEnemyMonHP + +.start +; Index for .Multipliers +; Default restores half max HP. + ld c, 2 + +; Don't bother healing if HP is already full. + push bc + call CompareBytes + pop bc + jr z, .Full + +; Don't factor in time of day in link battles. + ld a, [wLinkMode] + and a + jr nz, .Weather + + ld a, [wTimeOfDay] + cp b + jr z, .Weather + dec c ; double + +.Weather: + ld a, [wBattleWeather] + and a + jr z, .Heal + +; x2 in sun +; /2 in rain/sandstorm + inc c + cp WEATHER_SUN + jr z, .Heal + dec c + dec c + +.Heal: + ld b, 0 + ld hl, .Multipliers + add hl, bc + add hl, bc + + ld a, [hli] + ld h, [hl] + ld l, a + ld a, BANK(GetMaxHP) + rst FarCall + + call AnimateCurrentMove + call BattleCommand_SwitchTurn + + callfar RestoreHP + + call BattleCommand_SwitchTurn + call UpdateUserInParty + +; 'regained health!' + ld hl, RegainedHealthText + jp StdBattleTextbox + +.Full: + call AnimateFailedMove + +; 'hp is full!' + ld hl, HPIsFullText + jp StdBattleTextbox + +.Multipliers: + dw GetEighthMaxHP + dw GetQuarterMaxHP + dw GetHalfMaxHP + dw GetMaxHP + +INCLUDE "engine/battle/move_effects/hidden_power.asm" + +INCLUDE "engine/battle/move_effects/rain_dance.asm" + +INCLUDE "engine/battle/move_effects/sunny_day.asm" + +INCLUDE "engine/battle/move_effects/belly_drum.asm" + +INCLUDE "engine/battle/move_effects/psych_up.asm" + +INCLUDE "engine/battle/move_effects/mirror_coat.asm" + +BattleCommand_DoubleMinimizeDamage: +; doubleminimizedamage + + ld hl, wEnemyMinimized + ldh a, [hBattleTurn] + and a + jr z, .ok + ld hl, wPlayerMinimized +.ok + ld a, [hl] + and a + ret z + ld hl, wCurDamage + 1 + sla [hl] + dec hl + rl [hl] + ret nc + ld a, $ff + ld [hli], a + ld [hl], a + ret + +BattleCommand_SkipSunCharge: +; mimicsuncharge + ld a, [wBattleWeather] + cp WEATHER_SUN + ret nz + ld b, charge_command + jp SkipToBattleCommand + +INCLUDE "engine/battle/move_effects/future_sight.asm" + +INCLUDE "engine/battle/move_effects/thunder.asm" + +CheckHiddenOpponent: +; BUG: This routine is completely redundant and introduces a bug, since BattleCommand_CheckHit does these checks properly. + ld a, BATTLE_VARS_SUBSTATUS3_OPP + call GetBattleVar + and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND + ret + +GetUserItem: +; Return the effect of the user's item in bc, and its id at hl. + ld hl, wBattleMonItem + ldh a, [hBattleTurn] + and a + jr z, .go + ld hl, wEnemyMonItem +.go + ld b, [hl] + jp GetItemHeldEffect + +GetOpponentItem: +; Return the effect of the opponent's item in bc, and its id at hl. + ld hl, wEnemyMonItem + ldh a, [hBattleTurn] + and a + jr z, .go + ld hl, wBattleMonItem +.go + ld b, [hl] + jp GetItemHeldEffect + +GetItemHeldEffect: +; Return the effect of item b in bc. + ld a, b + and a + ret z + + push hl + ld hl, ItemAttributes + ITEMATTR_EFFECT + dec a + ld c, a + ld b, 0 + ld a, ITEMATTR_STRUCT_LENGTH + call AddNTimes + ld a, BANK(ItemAttributes) + call GetFarHalfword + ld b, l + ld c, h + pop hl + ret + +AnimateCurrentMoveEitherSide: + push hl + push de + push bc + ld a, [wKickCounter] + push af + call BattleCommand_LowerSub + pop af + ld [wKickCounter], a + call PlayDamageAnim + call BattleCommand_RaiseSub + pop bc + pop de + pop hl + ret + +AnimateCurrentMove: + push hl + push de + push bc + ld a, [wKickCounter] + push af + call BattleCommand_LowerSub + pop af + ld [wKickCounter], a + call LoadMoveAnim + call BattleCommand_RaiseSub + pop bc + pop de + pop hl + ret + +PlayDamageAnim: + xor a + ld [wFXAnimID + 1], a + + ld a, BATTLE_VARS_MOVE_ANIM + call GetBattleVar + and a + ret z + + ld [wFXAnimID], a + + ldh a, [hBattleTurn] + and a + ld a, BATTLEANIM_ENEMY_DAMAGE + jr z, .player + ld a, BATTLEANIM_PLAYER_DAMAGE + +.player + ld [wNumHits], a + + jp PlayUserBattleAnim + +LoadMoveAnim: + xor a + ld [wNumHits], a + ld [wFXAnimID + 1], a + + ld a, BATTLE_VARS_MOVE_ANIM + call GetBattleVar + and a + ret z + + ; fallthrough + +LoadAnim: + ld [wFXAnimID], a + + ; fallthrough + +PlayUserBattleAnim: + push hl + push de + push bc + callfar PlayBattleAnim + pop bc + pop de + pop hl + ret + +PlayOpponentBattleAnim: + ld a, e + ld [wFXAnimID], a + ld a, d + ld [wFXAnimID + 1], a + xor a + ld [wNumHits], a + + push hl + push de + push bc + call BattleCommand_SwitchTurn + + callfar PlayBattleAnim + + call BattleCommand_SwitchTurn + pop bc + pop de + pop hl + ret + +CallBattleCore: + ld a, BANK("Battle Core") + rst FarCall + ret + +AnimateFailedMove: + call BattleCommand_LowerSub + call BattleCommand_MoveDelay + jp BattleCommand_RaiseSub + +BattleCommand_MoveDelay: +; movedelay +; Wait 40 frames. + ld c, 40 + jp DelayFrames + +BattleCommand_ClearText: +; cleartext + +; Used in multi-hit moves. + ld hl, .text + jp PrintText + +.text: + text_end + +SkipToBattleCommand: +; Skip over commands until reaching command b. + ld a, [wBattleScriptBufferAddress + 1] + ld h, a + ld a, [wBattleScriptBufferAddress] + ld l, a +.loop + ld a, [hli] + cp b + jr nz, .loop + + ld a, h + ld [wBattleScriptBufferAddress + 1], a + ld a, l + ld [wBattleScriptBufferAddress], a + ret + +GetMoveAttr: +; Assuming hl = Moves + x, return attribute x of move a. + push bc + ld bc, MOVE_LENGTH + call AddNTimes + call GetMoveByte + pop bc + ret + +GetMoveData: +; Copy move struct a to de. + ld hl, Moves + ld bc, MOVE_LENGTH + call AddNTimes + ld a, BANK(Moves) + jp FarCopyBytes + +GetMoveByte: + ld a, BANK(Moves) + jp GetFarByte + +DisappearUser: + farcall _DisappearUser + ret + +AppearUserLowerSub: + farcall _AppearUserLowerSub + ret + +AppearUserRaiseSub: + farcall _AppearUserRaiseSub + ret |