summaryrefslogtreecommitdiff
path: root/de/engine/battle/core.asm
diff options
context:
space:
mode:
authorIIMarckus <iimarckus@gmail.com>2018-06-02 04:27:31 -0600
committerIIMarckus <iimarckus@gmail.com>2018-06-02 04:27:31 -0600
commit5d2473f767b431910da023a6e37ad3239dcdd575 (patch)
tree6200e65d9bd7e8fa8429c62d3907f21587ab74df /de/engine/battle/core.asm
parentf31a2ed69f7368d88a0a1aed5c35aa8cb34af6b3 (diff)
Start of German translation. Needs work.multilang
Diffstat (limited to 'de/engine/battle/core.asm')
-rwxr-xr-xde/engine/battle/core.asm8720
1 files changed, 8720 insertions, 0 deletions
diff --git a/de/engine/battle/core.asm b/de/engine/battle/core.asm
new file mode 100755
index 00000000..6f0ad8b6
--- /dev/null
+++ b/de/engine/battle/core.asm
@@ -0,0 +1,8720 @@
+BattleCore:
+
+; These are move effects (second value from the Moves table in bank $E).
+ResidualEffects1:
+; most non-side effects
+ db CONVERSION_EFFECT
+ db HAZE_EFFECT
+ db SWITCH_AND_TELEPORT_EFFECT
+ db MIST_EFFECT
+ db FOCUS_ENERGY_EFFECT
+ db CONFUSION_EFFECT
+ db HEAL_EFFECT
+ db TRANSFORM_EFFECT
+ db LIGHT_SCREEN_EFFECT
+ db REFLECT_EFFECT
+ db POISON_EFFECT
+ db PARALYZE_EFFECT
+ db SUBSTITUTE_EFFECT
+ db MIMIC_EFFECT
+ db LEECH_SEED_EFFECT
+ db SPLASH_EFFECT
+ db -1
+SetDamageEffects:
+; moves that do damage but not through normal calculations
+; e.g., Super Fang, Psywave
+ db SUPER_FANG_EFFECT
+ db SPECIAL_DAMAGE_EFFECT
+ db -1
+ResidualEffects2:
+; non-side effects not included in ResidualEffects1
+; stat-affecting moves, sleep-inflicting moves, and Bide
+; e.g., Meditate, Bide, Hypnosis
+ db $01
+ db ATTACK_UP1_EFFECT
+ db DEFENSE_UP1_EFFECT
+ db SPEED_UP1_EFFECT
+ db SPECIAL_UP1_EFFECT
+ db ACCURACY_UP1_EFFECT
+ db EVASION_UP1_EFFECT
+ db ATTACK_DOWN1_EFFECT
+ db DEFENSE_DOWN1_EFFECT
+ db SPEED_DOWN1_EFFECT
+ db SPECIAL_DOWN1_EFFECT
+ db ACCURACY_DOWN1_EFFECT
+ db EVASION_DOWN1_EFFECT
+ db BIDE_EFFECT
+ db SLEEP_EFFECT
+ db ATTACK_UP2_EFFECT
+ db DEFENSE_UP2_EFFECT
+ db SPEED_UP2_EFFECT
+ db SPECIAL_UP2_EFFECT
+ db ACCURACY_UP2_EFFECT
+ db EVASION_UP2_EFFECT
+ db ATTACK_DOWN2_EFFECT
+ db DEFENSE_DOWN2_EFFECT
+ db SPEED_DOWN2_EFFECT
+ db SPECIAL_DOWN2_EFFECT
+ db ACCURACY_DOWN2_EFFECT
+ db EVASION_DOWN2_EFFECT
+ db -1
+AlwaysHappenSideEffects:
+; Attacks that aren't finished after they faint the opponent.
+ db DRAIN_HP_EFFECT
+ db EXPLODE_EFFECT
+ db DREAM_EATER_EFFECT
+ db PAY_DAY_EFFECT
+ db TWO_TO_FIVE_ATTACKS_EFFECT
+ db $1E
+ db ATTACK_TWICE_EFFECT
+ db RECOIL_EFFECT
+ db TWINEEDLE_EFFECT
+ db RAGE_EFFECT
+ db -1
+SpecialEffects:
+; Effects from arrays 2, 4, and 5B, minus Twineedle and Rage.
+; Includes all effects that do not need to be called at the end of
+; ExecutePlayerMove (or ExecuteEnemyMove), because they have already been handled
+ db DRAIN_HP_EFFECT
+ db EXPLODE_EFFECT
+ db DREAM_EATER_EFFECT
+ db PAY_DAY_EFFECT
+ db SWIFT_EFFECT
+ db TWO_TO_FIVE_ATTACKS_EFFECT
+ db $1E
+ db CHARGE_EFFECT
+ db SUPER_FANG_EFFECT
+ db SPECIAL_DAMAGE_EFFECT
+ db FLY_EFFECT
+ db ATTACK_TWICE_EFFECT
+ db JUMP_KICK_EFFECT
+ db RECOIL_EFFECT
+ ; fallthrough to Next EffectsArray
+SpecialEffectsCont:
+; damaging moves whose effect is executed prior to damage calculation
+ db THRASH_PETAL_DANCE_EFFECT
+ db TRAPPING_EFFECT
+ db -1
+
+SlidePlayerAndEnemySilhouettesOnScreen:
+ call LoadPlayerBackPic
+ ld a, MESSAGE_BOX ; the usual text box at the bottom of the screen
+ ld [wTextBoxID], a
+ call DisplayTextBoxID
+ coord hl, 1, 5
+ lb bc, 3, 7
+ call ClearScreenArea
+ call DisableLCD
+ call LoadFontTilePatterns
+ call LoadHudAndHpBarAndStatusTilePatterns
+ ld hl, vBGMap0
+ ld bc, $400
+.clearBackgroundLoop
+ ld a, " "
+ ld [hli], a
+ dec bc
+ ld a, b
+ or c
+ jr nz, .clearBackgroundLoop
+; copy the work RAM tile map to VRAM
+ coord hl, 0, 0
+ ld de, vBGMap0
+ ld b, 18 ; number of rows
+.copyRowLoop
+ ld c, 20 ; number of columns
+.copyColumnLoop
+ ld a, [hli]
+ ld [de], a
+ inc e
+ dec c
+ jr nz, .copyColumnLoop
+ ld a, 12 ; number of off screen tiles to the right of screen in VRAM
+ add e ; skip the off screen tiles
+ ld e, a
+ jr nc, .noCarry
+ inc d
+.noCarry
+ dec b
+ jr nz, .copyRowLoop
+ call EnableLCD
+ ld a, $90
+ ld [hWY], a
+ ld [rWY], a
+ xor a
+ ld [hTilesetType], a
+ ld [hSCY], a
+ dec a
+ ld [wUpdateSpritesEnabled], a
+ call Delay3
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ld b, $70
+ ld c, $90
+ ld a, c
+ ld [hSCX], a
+ call DelayFrame
+ ld a, %11100100 ; inverted palette for silhouette effect
+ ld [rBGP], a
+ ld [rOBP0], a
+ ld [rOBP1], a
+.slideSilhouettesLoop ; slide silhouettes of the player's pic and the enemy's pic onto the screen
+ ld h, b
+ ld l, $40
+ call SetScrollXForSlidingPlayerBodyLeft ; begin background scrolling on line $40
+ inc b
+ inc b
+ ld h, $0
+ ld l, $60
+ call SetScrollXForSlidingPlayerBodyLeft ; end background scrolling on line $60
+ call SlidePlayerHeadLeft
+ ld a, c
+ ld [hSCX], a
+ dec c
+ dec c
+ jr nz, .slideSilhouettesLoop
+ ld a, $1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ld a, $31
+ ld [hStartTileID], a
+ coord hl, 1, 5
+ predef CopyUncompressedPicToTilemap
+ xor a
+ ld [hWY], a
+ ld [rWY], a
+ inc a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call Delay3
+ ld b, SET_PAL_BATTLE
+ call RunPaletteCommand
+ call HideSprites
+ jpab PrintBeginningBattleText
+
+; when a battle is starting, silhouettes of the player's pic and the enemy's pic are slid onto the screen
+; the lower of the player's pic (his body) is part of the background, but his head is a sprite
+; the reason for this is that it shares Y coordinates with the lower part of the enemy pic, so background scrolling wouldn't work for both pics
+; instead, the enemy pic is part of the background and uses the scroll register, while the player's head is a sprite and is slid by changing its X coordinates in a loop
+SlidePlayerHeadLeft:
+ push bc
+ ld hl, wOAMBuffer + $01
+ ld c, $15 ; number of OAM entries
+ ld de, $4 ; size of OAM entry
+.loop
+ dec [hl] ; decrement X
+ dec [hl] ; decrement X
+ add hl, de ; next OAM entry
+ dec c
+ jr nz, .loop
+ pop bc
+ ret
+
+SetScrollXForSlidingPlayerBodyLeft:
+ ld a, [rLY]
+ cp l
+ jr nz, SetScrollXForSlidingPlayerBodyLeft
+ ld a, h
+ ld [rSCX], a
+.loop
+ ld a, [rLY]
+ cp h
+ jr z, .loop
+ ret
+
+StartBattle:
+ xor a
+ ld [wPartyGainExpFlags], a
+ ld [wPartyFoughtCurrentEnemyFlags], a
+ ld [wActionResultOrTookBattleTurn], a
+ inc a
+ ld [wFirstMonsNotOutYet], a
+ ld hl, wEnemyMon1HP
+ ld bc, wEnemyMon2 - wEnemyMon1 - 1
+ ld d, $3
+.findFirstAliveEnemyMonLoop
+ inc d
+ ld a, [hli]
+ or [hl]
+ jr nz, .foundFirstAliveEnemyMon
+ add hl, bc
+ jr .findFirstAliveEnemyMonLoop
+.foundFirstAliveEnemyMon
+ ld a, d
+ ld [wSerialExchangeNybbleReceiveData], a
+ ld a, [wIsInBattle]
+ dec a ; is it a trainer battle?
+ call nz, EnemySendOutFirstMon ; if it is a trainer battle, send out enemy mon
+ ld c, 40
+ call DelayFrames
+ call SaveScreenTilesToBuffer1
+.checkAnyPartyAlive
+ call AnyPartyAlive
+ ld a, d
+ and a
+ jp z, HandlePlayerBlackOut ; jump if no mon is alive
+ call LoadScreenTilesFromBuffer1
+ ld a, [wBattleType]
+ and a ; is it a normal battle?
+ jp z, .playerSendOutFirstMon ; if so, send out player mon
+; safari zone battle
+.displaySafariZoneBattleMenu
+ call DisplayBattleMenu
+ ret c ; return if the player ran from battle
+ ld a, [wActionResultOrTookBattleTurn]
+ and a ; was the item used successfully?
+ jr z, .displaySafariZoneBattleMenu ; if not, display the menu again; XXX does this ever jump?
+ ld a, [wNumSafariBalls]
+ and a
+ jr nz, .notOutOfSafariBalls
+ call LoadScreenTilesFromBuffer1
+ ld hl, .outOfSafariBallsText
+ jp PrintText
+.notOutOfSafariBalls
+ callab PrintSafariZoneBattleText
+ ld a, [wEnemyMonSpeed + 1]
+ add a
+ ld b, a ; init b (which is later compared with random value) to (enemy speed % 256) * 2
+ jp c, EnemyRan ; if (enemy speed % 256) > 127, the enemy runs
+ ld a, [wSafariBaitFactor]
+ and a ; is bait factor 0?
+ jr z, .checkEscapeFactor
+; bait factor is not 0
+; divide b by 4 (making the mon less likely to run)
+ srl b
+ srl b
+.checkEscapeFactor
+ ld a, [wSafariEscapeFactor]
+ and a ; is escape factor 0?
+ jr z, .compareWithRandomValue
+; escape factor is not 0
+; multiply b by 2 (making the mon more likely to run)
+ sla b
+ jr nc, .compareWithRandomValue
+; cap b at 255
+ ld b, $ff
+.compareWithRandomValue
+ call Random
+ cp b
+ jr nc, .checkAnyPartyAlive
+ jr EnemyRan ; if b was greater than the random value, the enemy runs
+
+.outOfSafariBallsText
+ TX_FAR _OutOfSafariBallsText
+ db "@"
+
+.playerSendOutFirstMon
+ xor a
+ ld [wWhichPokemon], a
+.findFirstAliveMonLoop
+ call HasMonFainted
+ jr nz, .foundFirstAliveMon
+; fainted, go to the next one
+ ld hl, wWhichPokemon
+ inc [hl]
+ jr .findFirstAliveMonLoop
+.foundFirstAliveMon
+ ld a, [wWhichPokemon]
+ ld [wPlayerMonNumber], a
+ inc a
+ ld hl, wPartySpecies - 1
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld a, [hl] ; species
+ ld [wcf91], a
+ ld [wBattleMonSpecies2], a
+ call LoadScreenTilesFromBuffer1
+ coord hl, 1, 5
+ ld a, $9
+ call SlideTrainerPicOffScreen
+ call SaveScreenTilesToBuffer1
+ ld a, [wWhichPokemon]
+ ld c, a
+ ld b, FLAG_SET
+ push bc
+ ld hl, wPartyGainExpFlags
+ predef FlagActionPredef
+ ld hl, wPartyFoughtCurrentEnemyFlags
+ pop bc
+ predef FlagActionPredef
+ call LoadBattleMonFromParty
+ call LoadScreenTilesFromBuffer1
+ call SendOutMon
+ jr MainInBattleLoop
+
+; wild mon or link battle enemy ran from battle
+EnemyRan:
+ call LoadScreenTilesFromBuffer1
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ ld hl, WildRanText
+ jr nz, .printText
+; link battle
+ xor a
+ ld [wBattleResult], a
+ ld hl, EnemyRanText
+.printText
+ call PrintText
+ ld a, SFX_RUN
+ call PlaySoundWaitForCurrent
+ xor a
+ ld [H_WHOSETURN], a
+ jpab AnimationSlideEnemyMonOff
+
+WildRanText:
+ TX_FAR _WildRanText
+ db "@"
+
+EnemyRanText:
+ TX_FAR _EnemyRanText
+ db "@"
+
+MainInBattleLoop:
+ call ReadPlayerMonCurHPAndStatus
+ ld hl, wBattleMonHP
+ ld a, [hli]
+ or [hl] ; is battle mon HP 0?
+ jp z, HandlePlayerMonFainted ; if battle mon HP is 0, jump
+ ld hl, wEnemyMonHP
+ ld a, [hli]
+ or [hl] ; is enemy mon HP 0?
+ jp z, HandleEnemyMonFainted ; if enemy mon HP is 0, jump
+ call SaveScreenTilesToBuffer1
+ xor a
+ ld [wFirstMonsNotOutYet], a
+ ld a, [wPlayerBattleStatus2]
+ and (1 << NeedsToRecharge) | (1 << UsingRage) ; check if the player is using Rage or needs to recharge
+ jr nz, .selectEnemyMove
+; the player is not using Rage and doesn't need to recharge
+ ld hl, wEnemyBattleStatus1
+ res Flinched, [hl] ; reset flinch bit
+ ld hl, wPlayerBattleStatus1
+ res Flinched, [hl] ; reset flinch bit
+ ld a, [hl]
+ and (1 << ThrashingAbout) | (1 << ChargingUp) ; check if the player is thrashing about or charging for an attack
+ jr nz, .selectEnemyMove ; if so, jump
+; the player is neither thrashing about nor charging for an attack
+ call DisplayBattleMenu ; show battle menu
+ ret c ; return if player ran from battle
+ ld a, [wEscapedFromBattle]
+ and a
+ ret nz ; return if pokedoll was used to escape from battle
+ ld a, [wBattleMonStatus]
+ and (1 << FRZ) | SLP ; is mon frozen or asleep?
+ jr nz, .selectEnemyMove ; if so, jump
+ ld a, [wPlayerBattleStatus1]
+ and (1 << StoringEnergy) | (1 << UsingTrappingMove) ; check player is using Bide or using a multi-turn attack like wrap
+ jr nz, .selectEnemyMove ; if so, jump
+ ld a, [wEnemyBattleStatus1]
+ bit UsingTrappingMove, a ; check if enemy is using a multi-turn attack like wrap
+ jr z, .selectPlayerMove ; if not, jump
+; enemy is using a multi-turn attack like wrap, so player is trapped and cannot execute a move
+ ld a, $ff
+ ld [wPlayerSelectedMove], a
+ jr .selectEnemyMove
+.selectPlayerMove
+ ld a, [wActionResultOrTookBattleTurn]
+ and a ; has the player already used the turn (e.g. by using an item, trying to run or switching pokemon)
+ jr nz, .selectEnemyMove
+ ld [wMoveMenuType], a
+ inc a
+ ld [wAnimationID], a
+ xor a
+ ld [wMenuItemToSwap], a
+ call MoveSelectionMenu
+ push af
+ call LoadScreenTilesFromBuffer1
+ call DrawHUDsAndHPBars
+ pop af
+ jr nz, MainInBattleLoop ; if the player didn't select a move, jump
+.selectEnemyMove
+ call SelectEnemyMove
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ jr nz, .noLinkBattle
+; link battle
+ ld a, [wSerialExchangeNybbleReceiveData]
+ cp LINKBATTLE_RUN
+ jp z, EnemyRan
+ cp LINKBATTLE_STRUGGLE
+ jr z, .noLinkBattle
+ cp LINKBATTLE_NO_ACTION
+ jr z, .noLinkBattle
+ sub 4
+ jr c, .noLinkBattle
+; the link battle enemy has switched mons
+ ld a, [wPlayerBattleStatus1]
+ bit UsingTrappingMove, a ; check if using multi-turn move like Wrap
+ jr z, .specialMoveNotUsed
+ ld a, [wPlayerMoveListIndex]
+ ld hl, wBattleMonMoves
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld a, [hl]
+ cp METRONOME ; a MIRROR MOVE check is missing, might lead to a desync in link battles
+ ; when combined with multi-turn moves
+ jr nz, .specialMoveNotUsed
+ ld [wPlayerSelectedMove], a
+.specialMoveNotUsed
+ callab SwitchEnemyMon
+.noLinkBattle
+ ld a, [wPlayerSelectedMove]
+ cp QUICK_ATTACK
+ jr nz, .playerDidNotUseQuickAttack
+ ld a, [wEnemySelectedMove]
+ cp QUICK_ATTACK
+ jr z, .compareSpeed ; if both used Quick Attack
+ jp .playerMovesFirst ; if player used Quick Attack and enemy didn't
+.playerDidNotUseQuickAttack
+ ld a, [wEnemySelectedMove]
+ cp QUICK_ATTACK
+ jr z, .enemyMovesFirst ; if enemy used Quick Attack and player didn't
+ ld a, [wPlayerSelectedMove]
+ cp COUNTER
+ jr nz, .playerDidNotUseCounter
+ ld a, [wEnemySelectedMove]
+ cp COUNTER
+ jr z, .compareSpeed ; if both used Counter
+ jr .enemyMovesFirst ; if player used Counter and enemy didn't
+.playerDidNotUseCounter
+ ld a, [wEnemySelectedMove]
+ cp COUNTER
+ jr z, .playerMovesFirst ; if enemy used Counter and player didn't
+.compareSpeed
+ ld de, wBattleMonSpeed ; player speed value
+ ld hl, wEnemyMonSpeed ; enemy speed value
+ ld c, $2
+ call StringCmp ; compare speed values
+ jr z, .speedEqual
+ jr nc, .playerMovesFirst ; if player is faster
+ jr .enemyMovesFirst ; if enemy is faster
+.speedEqual ; 50/50 chance for both players
+ ld a, [hSerialConnectionStatus]
+ cp USING_INTERNAL_CLOCK
+ jr z, .invertOutcome
+ call BattleRandom
+ cp $80
+ jr c, .playerMovesFirst
+ jr .enemyMovesFirst
+.invertOutcome
+ call BattleRandom
+ cp $80
+ jr c, .enemyMovesFirst
+ jr .playerMovesFirst
+.enemyMovesFirst
+ ld a, $1
+ ld [H_WHOSETURN], a
+ callab TrainerAI
+ jr c, .AIActionUsedEnemyFirst
+ call ExecuteEnemyMove
+ ld a, [wEscapedFromBattle]
+ and a ; was Teleport, Road, or Whirlwind used to escape from battle?
+ ret nz ; if so, return
+ ld a, b
+ and a
+ jp z, HandlePlayerMonFainted
+.AIActionUsedEnemyFirst
+ call HandlePoisonBurnLeechSeed
+ jp z, HandleEnemyMonFainted
+ call DrawHUDsAndHPBars
+ call ExecutePlayerMove
+ ld a, [wEscapedFromBattle]
+ and a ; was Teleport, Road, or Whirlwind used to escape from battle?
+ ret nz ; if so, return
+ ld a, b
+ and a
+ jp z, HandleEnemyMonFainted
+ call HandlePoisonBurnLeechSeed
+ jp z, HandlePlayerMonFainted
+ call DrawHUDsAndHPBars
+ call CheckNumAttacksLeft
+ jp MainInBattleLoop
+.playerMovesFirst
+ call ExecutePlayerMove
+ ld a, [wEscapedFromBattle]
+ and a ; was Teleport, Road, or Whirlwind used to escape from battle?
+ ret nz ; if so, return
+ ld a, b
+ and a
+ jp z, HandleEnemyMonFainted
+ call HandlePoisonBurnLeechSeed
+ jp z, HandlePlayerMonFainted
+ call DrawHUDsAndHPBars
+ ld a, $1
+ ld [H_WHOSETURN], a
+ callab TrainerAI
+ jr c, .AIActionUsedPlayerFirst
+ call ExecuteEnemyMove
+ ld a, [wEscapedFromBattle]
+ and a ; was Teleport, Road, or Whirlwind used to escape from battle?
+ ret nz ; if so, return
+ ld a, b
+ and a
+ jp z, HandlePlayerMonFainted
+.AIActionUsedPlayerFirst
+ call HandlePoisonBurnLeechSeed
+ jp z, HandleEnemyMonFainted
+ call DrawHUDsAndHPBars
+ call CheckNumAttacksLeft
+ jp MainInBattleLoop
+
+HandlePoisonBurnLeechSeed:
+ ld hl, wBattleMonHP
+ ld de, wBattleMonStatus
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .playersTurn
+ ld hl, wEnemyMonHP
+ ld de, wEnemyMonStatus
+.playersTurn
+ ld a, [de]
+ and (1 << BRN) | (1 << PSN)
+ jr z, .notBurnedOrPoisoned
+ push hl
+ ld hl, HurtByPoisonText
+ ld a, [de]
+ and 1 << BRN
+ jr z, .poisoned
+ ld hl, HurtByBurnText
+.poisoned
+ call PrintText
+ xor a
+ ld [wAnimationType], a
+ ld a,BURN_PSN_ANIM
+ call PlayMoveAnimation ; play burn/poison animation
+ pop hl
+ call HandlePoisonBurnLeechSeed_DecreaseOwnHP
+.notBurnedOrPoisoned
+ ld de, wPlayerBattleStatus2
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .playersTurn2
+ ld de, wEnemyBattleStatus2
+.playersTurn2
+ ld a, [de]
+ add a
+ jr nc, .notLeechSeeded
+ push hl
+ ld a, [H_WHOSETURN]
+ push af
+ xor $1
+ ld [H_WHOSETURN], a
+ xor a
+ ld [wAnimationType], a
+ ld a,ABSORB
+ call PlayMoveAnimation ; play leech seed animation (from opposing mon)
+ pop af
+ ld [H_WHOSETURN], a
+ pop hl
+ call HandlePoisonBurnLeechSeed_DecreaseOwnHP
+ call HandlePoisonBurnLeechSeed_IncreaseEnemyHP
+ push hl
+ ld hl, HurtByLeechSeedText
+ call PrintText
+ pop hl
+.notLeechSeeded
+ ld a, [hli]
+ or [hl]
+ ret nz ; test if fainted
+ call DrawHUDsAndHPBars
+ ld c, 20
+ call DelayFrames
+ xor a
+ ret
+
+HurtByPoisonText:
+ TX_FAR _HurtByPoisonText
+ db "@"
+
+HurtByBurnText:
+ TX_FAR _HurtByBurnText
+ db "@"
+
+HurtByLeechSeedText:
+ TX_FAR _HurtByLeechSeedText
+ db "@"
+
+; decreases the mon's current HP by 1/16 of the Max HP (multiplied by number of toxic ticks if active)
+; note that the toxic ticks are considered even if the damage is not poison (hence the Leech Seed glitch)
+; hl: HP pointer
+; bc (out): total damage
+HandlePoisonBurnLeechSeed_DecreaseOwnHP:
+ push hl
+ push hl
+ ld bc, $e ; skip to max HP
+ add hl, bc
+ ld a, [hli] ; load max HP
+ ld [wHPBarMaxHP+1], a
+ ld b, a
+ ld a, [hl]
+ ld [wHPBarMaxHP], a
+ ld c, a
+ srl b
+ rr c
+ srl b
+ rr c
+ srl c
+ srl c ; c = max HP/16 (assumption: HP < 1024)
+ ld a, c
+ and a
+ jr nz, .nonZeroDamage
+ inc c ; damage is at least 1
+.nonZeroDamage
+ ld hl, wPlayerBattleStatus3
+ ld de, wPlayerToxicCounter
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .playersTurn
+ ld hl, wEnemyBattleStatus3
+ ld de, wEnemyToxicCounter
+.playersTurn
+ bit BadlyPoisoned, [hl]
+ jr z, .noToxic
+ ld a, [de] ; increment toxic counter
+ inc a
+ ld [de], a
+ ld hl, $0000
+.toxicTicksLoop
+ add hl, bc
+ dec a
+ jr nz, .toxicTicksLoop
+ ld b, h ; bc = damage * toxic counter
+ ld c, l
+.noToxic
+ pop hl
+ inc hl
+ ld a, [hl] ; subtract total damage from current HP
+ ld [wHPBarOldHP], a
+ sub c
+ ld [hld], a
+ ld [wHPBarNewHP], a
+ ld a, [hl]
+ ld [wHPBarOldHP+1], a
+ sbc b
+ ld [hl], a
+ ld [wHPBarNewHP+1], a
+ jr nc, .noOverkill
+ xor a ; overkill: zero HP
+ ld [hli], a
+ ld [hl], a
+ ld [wHPBarNewHP], a
+ ld [wHPBarNewHP+1], a
+.noOverkill
+ call UpdateCurMonHPBar
+ pop hl
+ ret
+
+; adds bc to enemy HP
+; bc isn't updated if HP subtracted was capped to prevent overkill
+HandlePoisonBurnLeechSeed_IncreaseEnemyHP:
+ push hl
+ ld hl, wEnemyMonMaxHP
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .playersTurn
+ ld hl, wBattleMonMaxHP
+.playersTurn
+ ld a, [hli]
+ ld [wHPBarMaxHP+1], a
+ ld a, [hl]
+ ld [wHPBarMaxHP], a
+ ld de, wBattleMonHP - wBattleMonMaxHP
+ add hl, de ; skip back from max hp to current hp
+ ld a, [hl]
+ ld [wHPBarOldHP], a ; add bc to current HP
+ add c
+ ld [hld], a
+ ld [wHPBarNewHP], a
+ ld a, [hl]
+ ld [wHPBarOldHP+1], a
+ adc b
+ ld [hli], a
+ ld [wHPBarNewHP+1], a
+ ld a, [wHPBarMaxHP]
+ ld c, a
+ ld a, [hld]
+ sub c
+ ld a, [wHPBarMaxHP+1]
+ ld b, a
+ ld a, [hl]
+ sbc b
+ jr c, .noOverfullHeal
+ ld a, b ; overfull heal, set HP to max HP
+ ld [hli], a
+ ld [wHPBarNewHP+1], a
+ ld a, c
+ ld [hl], a
+ ld [wHPBarNewHP], a
+.noOverfullHeal
+ ld a, [H_WHOSETURN]
+ xor $1
+ ld [H_WHOSETURN], a
+ call UpdateCurMonHPBar
+ ld a, [H_WHOSETURN]
+ xor $1
+ ld [H_WHOSETURN], a
+ pop hl
+ ret
+
+UpdateCurMonHPBar:
+ coord hl, 10, 9 ; tile pointer to player HP bar
+ ld a, [H_WHOSETURN]
+ and a
+ ld a, $1
+ jr z, .playersTurn
+ coord hl, 2, 2 ; tile pointer to enemy HP bar
+ xor a
+.playersTurn
+ push bc
+ ld [wHPBarType], a
+ predef UpdateHPBar2
+ pop bc
+ ret
+
+CheckNumAttacksLeft:
+ ld a, [wPlayerNumAttacksLeft]
+ and a
+ jr nz, .checkEnemy
+; player has 0 attacks left
+ ld hl, wPlayerBattleStatus1
+ res UsingTrappingMove, [hl] ; player not using multi-turn attack like wrap any more
+.checkEnemy
+ ld a, [wEnemyNumAttacksLeft]
+ and a
+ ret nz
+; enemy has 0 attacks left
+ ld hl, wEnemyBattleStatus1
+ res UsingTrappingMove, [hl] ; enemy not using multi-turn attack like wrap any more
+ ret
+
+HandleEnemyMonFainted:
+ xor a
+ ld [wInHandlePlayerMonFainted], a
+ call FaintEnemyPokemon
+ call AnyPartyAlive
+ ld a, d
+ and a
+ jp z, HandlePlayerBlackOut ; if no party mons are alive, the player blacks out
+ ld hl, wBattleMonHP
+ ld a, [hli]
+ or [hl] ; is battle mon HP zero?
+ call nz, DrawPlayerHUDAndHPBar ; if battle mon HP is not zero, draw player HD and HP bar
+ ld a, [wIsInBattle]
+ dec a
+ ret z ; return if it's a wild battle
+ call AnyEnemyPokemonAliveCheck
+ jp z, TrainerBattleVictory
+ ld hl, wBattleMonHP
+ ld a, [hli]
+ or [hl] ; does battle mon have 0 HP?
+ jr nz, .skipReplacingBattleMon ; if not, skip replacing battle mon
+ call DoUseNextMonDialogue ; this call is useless in a trainer battle. it shouldn't be here
+ ret c
+ call ChooseNextMon
+.skipReplacingBattleMon
+ ld a, $1
+ ld [wActionResultOrTookBattleTurn], a
+ call ReplaceFaintedEnemyMon
+ jp z, EnemyRan
+ xor a
+ ld [wActionResultOrTookBattleTurn], a
+ jp MainInBattleLoop
+
+FaintEnemyPokemon:
+ call ReadPlayerMonCurHPAndStatus
+ ld a, [wIsInBattle]
+ dec a
+ jr z, .wild
+ ld a, [wEnemyMonPartyPos]
+ ld hl, wEnemyMon1HP
+ ld bc, wEnemyMon2 - wEnemyMon1
+ call AddNTimes
+ xor a
+ ld [hli], a
+ ld [hl], a
+.wild
+ ld hl, wPlayerBattleStatus1
+ res AttackingMultipleTimes, [hl]
+; Bug. This only zeroes the high byte of the player's accumulated damage,
+; setting the accumulated damage to itself mod 256 instead of 0 as was probably
+; intended. That alone is problematic, but this mistake has another more severe
+; effect. This function's counterpart for when the player mon faints,
+; RemoveFaintedPlayerMon, zeroes both the high byte and the low byte. In a link
+; battle, the other player's Game Boy will call that function in response to
+; the enemy mon (the player mon from the other side's perspective) fainting,
+; and the states of the two Game Boys will go out of sync unless the damage
+; was congruent to 0 modulo 256.
+ xor a
+ ld [wPlayerBideAccumulatedDamage], a
+ ld hl, wEnemyStatsToDouble ; clear enemy statuses
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ ld [hl], a
+ ld [wEnemyDisabledMove], a
+ ld [wEnemyDisabledMoveNumber], a
+ ld [wEnemyMonMinimized], a
+ ld hl, wPlayerUsedMove
+ ld [hli], a
+ ld [hl], a
+ coord hl, 12, 5
+ coord de, 12, 6
+ call SlideDownFaintedMonPic
+ coord hl, 0, 0
+ lb bc, 4, 11
+ call ClearScreenArea
+ ld a, [wIsInBattle]
+ dec a
+ jr z, .wild_win
+ xor a
+ ld [wFrequencyModifier], a
+ ld [wTempoModifier], a
+ ld a, SFX_FAINT_FALL
+ call PlaySoundWaitForCurrent
+.sfxwait
+ ld a, [wChannelSoundIDs + Ch4]
+ cp SFX_FAINT_FALL
+ jr z, .sfxwait
+ ld a, SFX_FAINT_THUD
+ call PlaySound
+ call WaitForSoundToFinish
+ jr .sfxplayed
+.wild_win
+ call EndLowHealthAlarm
+ ld a, MUSIC_DEFEATED_WILD_MON
+ call PlayBattleVictoryMusic
+.sfxplayed
+; bug: win sfx is played for wild battles before checking for player mon HP
+; this can lead to odd scenarios where both player and enemy faint, as the win sfx plays yet the player never won the battle
+ ld hl, wBattleMonHP
+ ld a, [hli]
+ or [hl]
+ jr nz, .playermonnotfaint
+ ld a, [wInHandlePlayerMonFainted]
+ and a ; was this called by HandlePlayerMonFainted?
+ jr nz, .playermonnotfaint ; if so, don't call RemoveFaintedPlayerMon twice
+ call RemoveFaintedPlayerMon
+.playermonnotfaint
+ call AnyPartyAlive
+ ld a, d
+ and a
+ ret z
+ ld hl, EnemyMonFaintedText
+ call PrintText
+ call PrintEmptyString
+ call SaveScreenTilesToBuffer1
+ xor a
+ ld [wBattleResult], a
+ ld b, EXP_ALL
+ call IsItemInBag
+ push af
+ jr z, .giveExpToMonsThatFought ; if no exp all, then jump
+
+; the player has exp all
+; first, we halve the values that determine exp gain
+; the enemy mon base stats are added to stat exp, so they are halved
+; the base exp (which determines normal exp) is also halved
+ ld hl, wEnemyMonBaseStats
+ ld b, $7
+.halveExpDataLoop
+ srl [hl]
+ inc hl
+ dec b
+ jr nz, .halveExpDataLoop
+
+; give exp (divided evenly) to the mons that actually fought in battle against the enemy mon that has fainted
+; if exp all is in the bag, this will be only be half of the stat exp and normal exp, due to the above loop
+.giveExpToMonsThatFought
+ xor a
+ ld [wBoostExpByExpAll], a
+ callab GainExperience
+ pop af
+ ret z ; return if no exp all
+
+; the player has exp all
+; now, set the gain exp flag for every party member
+; half of the total stat exp and normal exp will divided evenly amongst every party member
+ ld a, $1
+ ld [wBoostExpByExpAll], a
+ ld a, [wPartyCount]
+ ld b, 0
+.gainExpFlagsLoop
+ scf
+ rl b
+ dec a
+ jr nz, .gainExpFlagsLoop
+ ld a, b
+ ld [wPartyGainExpFlags], a
+ jpab GainExperience
+
+EnemyMonFaintedText:
+ TX_FAR _EnemyMonFaintedText
+ db "@"
+
+EndLowHealthAlarm:
+; This function is called when the player has the won the battle. It turns off
+; the low health alarm and prevents it from reactivating until the next battle.
+ xor a
+ ld [wLowHealthAlarm], a ; turn off low health alarm
+ ld [wChannelSoundIDs + Ch4], a
+ inc a
+ ld [wLowHealthAlarmDisabled], a ; prevent it from reactivating
+ ret
+
+AnyEnemyPokemonAliveCheck:
+ ld a, [wEnemyPartyCount]
+ ld b, a
+ xor a
+ ld hl, wEnemyMon1HP
+ ld de, wEnemyMon2 - wEnemyMon1
+.nextPokemon
+ or [hl]
+ inc hl
+ or [hl]
+ dec hl
+ add hl, de
+ dec b
+ jr nz, .nextPokemon
+ and a
+ ret
+
+; stores whether enemy ran in Z flag
+ReplaceFaintedEnemyMon:
+ ld hl, wEnemyHPBarColor
+ ld e, $30
+ call GetBattleHealthBarColor
+ callab DrawEnemyPokeballs
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ jr nz, .notLinkBattle
+; link battle
+ call LinkBattleExchangeData
+ ld a, [wSerialExchangeNybbleReceiveData]
+ cp LINKBATTLE_RUN
+ ret z
+ call LoadScreenTilesFromBuffer1
+.notLinkBattle
+ call EnemySendOut
+ xor a
+ ld [wEnemyMoveNum], a
+ ld [wActionResultOrTookBattleTurn], a
+ ld [wAILayer2Encouragement], a
+ inc a ; reset Z flag
+ ret
+
+TrainerBattleVictory:
+ call EndLowHealthAlarm
+ ld b, MUSIC_DEFEATED_GYM_LEADER
+ ld a, [wGymLeaderNo]
+ and a
+ jr nz, .gymleader
+ ld b, MUSIC_DEFEATED_TRAINER
+.gymleader
+ ld a, [wTrainerClass]
+ cp SONY3 ; final battle against rival
+ jr nz, .notrival
+ ld b, MUSIC_DEFEATED_GYM_LEADER
+ ld hl, wFlags_D733
+ set 1, [hl]
+.notrival
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ ld a, b
+ call nz, PlayBattleVictoryMusic
+ ld hl, TrainerDefeatedText
+ call PrintText
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ ret z
+ call ScrollTrainerPicAfterBattle
+ ld c, 40
+ call DelayFrames
+ call PrintEndBattleText
+; win money
+ ld hl, MoneyForWinningText
+ call PrintText
+ ld de, wPlayerMoney + 2
+ ld hl, wAmountMoneyWon + 2
+ ld c, $3
+ predef_jump AddBCDPredef
+
+MoneyForWinningText:
+ TX_FAR _MoneyForWinningText
+ db "@"
+
+TrainerDefeatedText:
+ TX_FAR _TrainerDefeatedText
+ db "@"
+
+PlayBattleVictoryMusic:
+ push af
+ ld a, $ff
+ ld [wNewSoundID], a
+ call PlaySoundWaitForCurrent
+ ld c, BANK(Music_DefeatedTrainer)
+ pop af
+ call PlayMusic
+ jp Delay3
+
+HandlePlayerMonFainted:
+ ld a, 1
+ ld [wInHandlePlayerMonFainted], a
+ call RemoveFaintedPlayerMon
+ call AnyPartyAlive ; test if any more mons are alive
+ ld a, d
+ and a
+ jp z, HandlePlayerBlackOut
+ ld hl, wEnemyMonHP
+ ld a, [hli]
+ or [hl] ; is enemy mon's HP 0?
+ jr nz, .doUseNextMonDialogue ; if not, jump
+; the enemy mon has 0 HP
+ call FaintEnemyPokemon
+ ld a, [wIsInBattle]
+ dec a
+ ret z ; if wild encounter, battle is over
+ call AnyEnemyPokemonAliveCheck
+ jp z, TrainerBattleVictory
+.doUseNextMonDialogue
+ call DoUseNextMonDialogue
+ ret c ; return if the player ran from battle
+ call ChooseNextMon
+ jp nz, MainInBattleLoop ; if the enemy mon has more than 0 HP, go back to battle loop
+; the enemy mon has 0 HP
+ ld a, $1
+ ld [wActionResultOrTookBattleTurn], a
+ call ReplaceFaintedEnemyMon
+ jp z, EnemyRan ; if enemy ran from battle rather than sending out another mon, jump
+ xor a
+ ld [wActionResultOrTookBattleTurn], a
+ jp MainInBattleLoop
+
+; resets flags, slides mon's pic down, plays cry, and prints fainted message
+RemoveFaintedPlayerMon:
+ ld a, [wPlayerMonNumber]
+ ld c, a
+ ld hl, wPartyGainExpFlags
+ ld b, FLAG_RESET
+ predef FlagActionPredef ; clear gain exp flag for fainted mon
+ ld hl, wEnemyBattleStatus1
+ res 2, [hl] ; reset "attacking multiple times" flag
+ ld a, [wLowHealthAlarm]
+ bit 7, a ; skip sound flag (red bar (?))
+ jr z, .skipWaitForSound
+ ld a, $ff
+ ld [wLowHealthAlarm], a ;disable low health alarm
+ call WaitForSoundToFinish
+.skipWaitForSound
+; a is 0, so this zeroes the enemy's accumulated damage.
+ ld hl, wEnemyBideAccumulatedDamage
+ ld [hli], a
+ ld [hl], a
+ ld [wBattleMonStatus], a
+ call ReadPlayerMonCurHPAndStatus
+ coord hl, 9, 7
+ lb bc, 5, 11
+ call ClearScreenArea
+ coord hl, 1, 10
+ coord de, 1, 11
+ call SlideDownFaintedMonPic
+ ld a, $1
+ ld [wBattleResult], a
+
+; When the player mon and enemy mon faint at the same time and the fact that the
+; enemy mon has fainted is detected first (e.g. when the player mon knocks out
+; the enemy mon using a move with recoil and faints due to the recoil), don't
+; play the player mon's cry or show the "[player mon] fainted!" message.
+ ld a, [wInHandlePlayerMonFainted]
+ and a ; was this called by HandleEnemyMonFainted?
+ ret z ; if so, return
+
+ ld a, [wBattleMonSpecies]
+ call PlayCry
+ ld hl, PlayerMonFaintedText
+ jp PrintText
+
+PlayerMonFaintedText:
+ TX_FAR _PlayerMonFaintedText
+ db "@"
+
+; asks if you want to use next mon
+; stores whether you ran in C flag
+DoUseNextMonDialogue:
+ call PrintEmptyString
+ call SaveScreenTilesToBuffer1
+ ld a, [wIsInBattle]
+ and a
+ dec a
+ ret nz ; return if it's a trainer battle
+ ld hl, UseNextMonText
+ call PrintText
+.displayYesNoBox
+ coord hl, 13, 9
+ lb bc, 10, 14
+ ld a, TWO_OPTION_MENU
+ ld [wTextBoxID], a
+ call DisplayTextBoxID
+ ld a, [wMenuExitMethod]
+ cp CHOSE_SECOND_ITEM ; did the player choose NO?
+ jr z, .tryRunning ; if the player chose NO, try running
+ and a ; reset carry
+ ret
+.tryRunning
+ ld a, [wCurrentMenuItem]
+ and a
+ jr z, .displayYesNoBox ; xxx when does this happen?
+ ld hl, wPartyMon1Speed
+ ld de, wEnemyMonSpeed
+ jp TryRunningFromBattle
+
+UseNextMonText:
+ TX_FAR _UseNextMonText
+ db "@"
+
+; choose next player mon to send out
+; stores whether enemy mon has no HP left in Z flag
+ChooseNextMon:
+ ld a, BATTLE_PARTY_MENU
+ ld [wPartyMenuTypeOrMessageID], a
+ call DisplayPartyMenu
+.checkIfMonChosen
+ jr nc, .monChosen
+.goBackToPartyMenu
+ call GoBackToPartyMenu
+ jr .checkIfMonChosen
+.monChosen
+ call HasMonFainted
+ jr z, .goBackToPartyMenu ; if mon fainted, you have to choose another
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ jr nz, .notLinkBattle
+ inc a
+ ld [wActionResultOrTookBattleTurn], a
+ call LinkBattleExchangeData
+.notLinkBattle
+ xor a
+ ld [wActionResultOrTookBattleTurn], a
+ call ClearSprites
+ ld a, [wWhichPokemon]
+ ld [wPlayerMonNumber], a
+ ld c, a
+ ld hl, wPartyGainExpFlags
+ ld b, FLAG_SET
+ push bc
+ predef FlagActionPredef
+ pop bc
+ ld hl, wPartyFoughtCurrentEnemyFlags
+ predef FlagActionPredef
+ call LoadBattleMonFromParty
+ call GBPalWhiteOut
+ call LoadHudTilePatterns
+ call LoadScreenTilesFromBuffer1
+ call RunDefaultPaletteCommand
+ call GBPalNormal
+ call SendOutMon
+ ld hl, wEnemyMonHP
+ ld a, [hli]
+ or [hl]
+ ret
+
+; called when player is out of usable mons.
+; prints appropriate lose message, sets carry flag if player blacked out (special case for initial rival fight)
+HandlePlayerBlackOut:
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ jr z, .notSony1Battle
+ ld a, [wCurOpponent]
+ cp OPP_SONY1
+ jr nz, .notSony1Battle
+ coord hl, 0, 0 ; sony 1 battle
+ lb bc, 8, 21
+ call ClearScreenArea
+ call ScrollTrainerPicAfterBattle
+ ld c, 40
+ call DelayFrames
+ ld hl, Sony1WinText
+ call PrintText
+ ld a, [wCurMap]
+ cp OAKS_LAB
+ ret z ; starter battle in oak's lab: don't black out
+.notSony1Battle
+ ld b, SET_PAL_BATTLE_BLACK
+ call RunPaletteCommand
+ ld hl, PlayerBlackedOutText2
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ jr nz, .noLinkBattle
+ ld hl, LinkBattleLostText
+.noLinkBattle
+ call PrintText
+ ld a, [wd732]
+ res 5, a
+ ld [wd732], a
+ call ClearScreen
+ scf
+ ret
+
+Sony1WinText:
+ TX_FAR _Sony1WinText
+ db "@"
+
+PlayerBlackedOutText2:
+ TX_FAR _PlayerBlackedOutText2
+ db "@"
+
+LinkBattleLostText:
+ TX_FAR _LinkBattleLostText
+ db "@"
+
+; slides pic of fainted mon downwards until it disappears
+; bug: when this is called, [H_AUTOBGTRANSFERENABLED] is non-zero, so there is screen tearing
+SlideDownFaintedMonPic:
+ ld a, [wd730]
+ push af
+ set 6, a
+ ld [wd730], a
+ ld b, 7 ; number of times to slide
+.slideStepLoop ; each iteration, the mon is slid down one row
+ push bc
+ push de
+ push hl
+ ld b, 6 ; number of rows
+.rowLoop
+ push bc
+ push hl
+ push de
+ ld bc, $7
+ call CopyData
+ pop de
+ pop hl
+ ld bc, -SCREEN_WIDTH
+ add hl, bc
+ push hl
+ ld h, d
+ ld l, e
+ add hl, bc
+ ld d, h
+ ld e, l
+ pop hl
+ pop bc
+ dec b
+ jr nz, .rowLoop
+ ld bc, SCREEN_WIDTH
+ add hl, bc
+ ld de, SevenSpacesText
+ call PlaceString
+ ld c, 2
+ call DelayFrames
+ pop hl
+ pop de
+ pop bc
+ dec b
+ jr nz, .slideStepLoop
+ pop af
+ ld [wd730], a
+ ret
+
+SevenSpacesText:
+ db " @"
+
+; slides the player or enemy trainer off screen
+; a is the number of tiles to slide it horizontally (always 9 for the player trainer or 8 for the enemy trainer)
+; if a is 8, the slide is to the right, else it is to the left
+; bug: when this is called, [H_AUTOBGTRANSFERENABLED] is non-zero, so there is screen tearing
+SlideTrainerPicOffScreen:
+ ld [hSlideAmount], a
+ ld c, a
+.slideStepLoop ; each iteration, the trainer pic is slid one tile left/right
+ push bc
+ push hl
+ ld b, 7 ; number of rows
+.rowLoop
+ push hl
+ ld a, [hSlideAmount]
+ ld c, a
+.columnLoop
+ ld a, [hSlideAmount]
+ cp 8
+ jr z, .slideRight
+.slideLeft ; slide player sprite off screen
+ ld a, [hld]
+ ld [hli], a
+ inc hl
+ jr .nextColumn
+.slideRight ; slide enemy trainer sprite off screen
+ ld a, [hli]
+ ld [hld], a
+ dec hl
+.nextColumn
+ dec c
+ jr nz, .columnLoop
+ pop hl
+ ld de, 20
+ add hl, de
+ dec b
+ jr nz, .rowLoop
+ ld c, 2
+ call DelayFrames
+ pop hl
+ pop bc
+ dec c
+ jr nz, .slideStepLoop
+ ret
+
+; send out a trainer's mon
+EnemySendOut:
+ ld hl,wPartyGainExpFlags
+ xor a
+ ld [hl],a
+ ld a,[wPlayerMonNumber]
+ ld c,a
+ ld b,FLAG_SET
+ push bc
+ predef FlagActionPredef
+ ld hl,wPartyFoughtCurrentEnemyFlags
+ xor a
+ ld [hl],a
+ pop bc
+ predef FlagActionPredef
+
+; don't change wPartyGainExpFlags or wPartyFoughtCurrentEnemyFlags
+EnemySendOutFirstMon:
+ xor a
+ ld hl,wEnemyStatsToDouble ; clear enemy statuses
+ ld [hli],a
+ ld [hli],a
+ ld [hli],a
+ ld [hli],a
+ ld [hl],a
+ ld [wEnemyDisabledMove],a
+ ld [wEnemyDisabledMoveNumber],a
+ ld [wEnemyMonMinimized],a
+ ld hl,wPlayerUsedMove
+ ld [hli],a
+ ld [hl],a
+ dec a
+ ld [wAICount],a
+ ld hl,wPlayerBattleStatus1
+ res 5,[hl]
+ coord hl, 18, 0
+ ld a,8
+ call SlideTrainerPicOffScreen
+ call PrintEmptyString
+ call SaveScreenTilesToBuffer1
+ ld a,[wLinkState]
+ cp LINK_STATE_BATTLING
+ jr nz,.next
+ ld a,[wSerialExchangeNybbleReceiveData]
+ sub 4
+ ld [wWhichPokemon],a
+ jr .next3
+.next
+ ld b,$FF
+.next2
+ inc b
+ ld a,[wEnemyMonPartyPos]
+ cp b
+ jr z,.next2
+ ld hl,wEnemyMon1
+ ld a,b
+ ld [wWhichPokemon],a
+ push bc
+ ld bc,wEnemyMon2 - wEnemyMon1
+ call AddNTimes
+ pop bc
+ inc hl
+ ld a,[hli]
+ ld c,a
+ ld a,[hl]
+ or c
+ jr z,.next2
+.next3
+ ld a,[wWhichPokemon]
+ ld hl,wEnemyMon1Level
+ ld bc,wEnemyMon2 - wEnemyMon1
+ call AddNTimes
+ ld a,[hl]
+ ld [wCurEnemyLVL],a
+ ld a,[wWhichPokemon]
+ inc a
+ ld hl,wEnemyPartyCount
+ ld c,a
+ ld b,0
+ add hl,bc
+ ld a,[hl]
+ ld [wEnemyMonSpecies2],a
+ ld [wcf91],a
+ call LoadEnemyMonData
+ ld hl,wEnemyMonHP
+ ld a,[hli]
+ ld [wLastSwitchInEnemyMonHP],a
+ ld a,[hl]
+ ld [wLastSwitchInEnemyMonHP + 1],a
+ ld a,1
+ ld [wCurrentMenuItem],a
+ ld a,[wFirstMonsNotOutYet]
+ dec a
+ jr z,.next4
+ ld a,[wPartyCount]
+ dec a
+ jr z,.next4
+ ld a,[wLinkState]
+ cp LINK_STATE_BATTLING
+ jr z,.next4
+ ld a,[wOptions]
+ bit 6,a
+ jr nz,.next4
+ ld hl, TrainerAboutToUseText
+ call PrintText
+ coord hl, 0, 7
+ lb bc, 8, 1
+ ld a,TWO_OPTION_MENU
+ ld [wTextBoxID],a
+ call DisplayTextBoxID
+ ld a,[wCurrentMenuItem]
+ and a
+ jr nz,.next4
+ ld a,BATTLE_PARTY_MENU
+ ld [wPartyMenuTypeOrMessageID],a
+ call DisplayPartyMenu
+.next9
+ ld a,1
+ ld [wCurrentMenuItem],a
+ jr c,.next7
+ ld hl,wPlayerMonNumber
+ ld a,[wWhichPokemon]
+ cp [hl]
+ jr nz,.next6
+ ld hl,AlreadyOutText
+ call PrintText
+.next8
+ call GoBackToPartyMenu
+ jr .next9
+.next6
+ call HasMonFainted
+ jr z,.next8
+ xor a
+ ld [wCurrentMenuItem],a
+.next7
+ call GBPalWhiteOut
+ call LoadHudTilePatterns
+ call LoadScreenTilesFromBuffer1
+.next4
+ call ClearSprites
+ coord hl, 0, 0
+ lb bc, 4, 11
+ call ClearScreenArea
+ ld b, SET_PAL_BATTLE
+ call RunPaletteCommand
+ call GBPalNormal
+ ld hl,TrainerSentOutText
+ call PrintText
+ ld a,[wEnemyMonSpecies2]
+ ld [wcf91],a
+ ld [wd0b5],a
+ call GetMonHeader
+ ld de,vFrontPic
+ call LoadMonFrontSprite
+ ld a,-$31
+ ld [hStartTileID],a
+ coord hl, 15, 6
+ predef AnimateSendingOutMon
+ ld a,[wEnemyMonSpecies2]
+ call PlayCry
+ call DrawEnemyHUDAndHPBar
+ ld a,[wCurrentMenuItem]
+ and a
+ ret nz
+ xor a
+ ld [wPartyGainExpFlags],a
+ ld [wPartyFoughtCurrentEnemyFlags],a
+ call SaveScreenTilesToBuffer1
+ jp SwitchPlayerMon
+
+TrainerAboutToUseText:
+ TX_FAR _TrainerAboutToUseText
+ db "@"
+
+TrainerSentOutText:
+ TX_FAR _TrainerSentOutText
+ db "@"
+
+; tests if the player has any pokemon that are not fainted
+; sets d = 0 if all fainted, d != 0 if some mons are still alive
+AnyPartyAlive:
+ ld a, [wPartyCount]
+ ld e, a
+ xor a
+ ld hl, wPartyMon1HP
+ ld bc, wPartyMon2 - wPartyMon1 - 1
+.partyMonsLoop
+ or [hl]
+ inc hl
+ or [hl]
+ add hl, bc
+ dec e
+ jr nz, .partyMonsLoop
+ ld d, a
+ ret
+
+; tests if player mon has fainted
+; stores whether mon has fainted in Z flag
+HasMonFainted:
+ ld a, [wWhichPokemon]
+ ld hl, wPartyMon1HP
+ ld bc, wPartyMon2 - wPartyMon1
+ call AddNTimes
+ ld a, [hli]
+ or [hl]
+ ret nz
+ ld a, [wFirstMonsNotOutYet]
+ and a
+ jr nz, .done
+ ld hl, NoWillText
+ call PrintText
+.done
+ xor a
+ ret
+
+NoWillText:
+ TX_FAR _NoWillText
+ db "@"
+
+; try to run from battle (hl = player speed, de = enemy speed)
+; stores whether the attempt was successful in carry flag
+TryRunningFromBattle:
+ call IsGhostBattle
+ jp z, .canEscape ; jump if it's a ghost battle
+ ld a, [wBattleType]
+ cp BATTLE_TYPE_SAFARI
+ jp z, .canEscape ; jump if it's a safari battle
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ jp z, .canEscape
+ ld a, [wIsInBattle]
+ dec a
+ jr nz, .trainerBattle ; jump if it's a trainer battle
+ ld a, [wNumRunAttempts]
+ inc a
+ ld [wNumRunAttempts], a
+ ld a, [hli]
+ ld [H_MULTIPLICAND + 1], a
+ ld a, [hl]
+ ld [H_MULTIPLICAND + 2], a
+ ld a, [de]
+ ld [hEnemySpeed], a
+ inc de
+ ld a, [de]
+ ld [hEnemySpeed + 1], a
+ call LoadScreenTilesFromBuffer1
+ ld de, H_MULTIPLICAND + 1
+ ld hl, hEnemySpeed
+ ld c, 2
+ call StringCmp
+ jr nc, .canEscape ; jump if player speed greater than enemy speed
+ xor a
+ ld [H_MULTIPLICAND], a
+ ld a, 32
+ ld [H_MULTIPLIER], a
+ call Multiply ; multiply player speed by 32
+ ld a, [H_PRODUCT + 2]
+ ld [H_DIVIDEND], a
+ ld a, [H_PRODUCT + 3]
+ ld [H_DIVIDEND + 1], a
+ ld a, [hEnemySpeed]
+ ld b, a
+ ld a, [hEnemySpeed + 1]
+; divide enemy speed by 4
+ srl b
+ rr a
+ srl b
+ rr a
+ and a
+ jr z, .canEscape ; jump if enemy speed divided by 4, mod 256 is 0
+ ld [H_DIVISOR], a ; ((enemy speed / 4) % 256)
+ ld b, $2
+ call Divide ; divide (player speed * 32) by ((enemy speed / 4) % 256)
+ ld a, [H_QUOTIENT + 2]
+ and a ; is the quotient greater than 256?
+ jr nz, .canEscape ; if so, the player can escape
+ ld a, [wNumRunAttempts]
+ ld c, a
+; add 30 to the quotient for each run attempt
+.loop
+ dec c
+ jr z, .compareWithRandomValue
+ ld b, 30
+ ld a, [H_QUOTIENT + 3]
+ add b
+ ld [H_QUOTIENT + 3], a
+ jr c, .canEscape
+ jr .loop
+.compareWithRandomValue
+ call BattleRandom
+ ld b, a
+ ld a, [H_QUOTIENT + 3]
+ cp b
+ jr nc, .canEscape ; if the random value was less than or equal to the quotient
+ ; plus 30 times the number of attempts, the player can escape
+; can't escape
+ ld a, $1
+ ld [wActionResultOrTookBattleTurn], a ; you lose your turn when you can't escape
+ ld hl, CantEscapeText
+ jr .printCantEscapeOrNoRunningText
+.trainerBattle
+ ld hl, NoRunningText
+.printCantEscapeOrNoRunningText
+ call PrintText
+ ld a, 1
+ ld [wForcePlayerToChooseMon], a
+ call SaveScreenTilesToBuffer1
+ and a ; reset carry
+ ret
+.canEscape
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ ld a, $2
+ jr nz, .playSound
+; link battle
+ call SaveScreenTilesToBuffer1
+ xor a
+ ld [wActionResultOrTookBattleTurn], a
+ ld a, LINKBATTLE_RUN
+ ld [wPlayerMoveListIndex], a
+ call LinkBattleExchangeData
+ call LoadScreenTilesFromBuffer1
+ ld a, [wSerialExchangeNybbleReceiveData]
+ cp LINKBATTLE_RUN
+ ld a, $2
+ jr z, .playSound
+ dec a
+.playSound
+ ld [wBattleResult], a
+ ld a, SFX_RUN
+ call PlaySoundWaitForCurrent
+ ld hl, GotAwayText
+ call PrintText
+ call WaitForSoundToFinish
+ call SaveScreenTilesToBuffer1
+ scf ; set carry
+ ret
+
+CantEscapeText:
+ TX_FAR _CantEscapeText
+ db "@"
+
+NoRunningText:
+ TX_FAR _NoRunningText
+ db "@"
+
+GotAwayText:
+ TX_FAR _GotAwayText
+ db "@"
+
+; copies from party data to battle mon data when sending out a new player mon
+LoadBattleMonFromParty:
+ ld a, [wWhichPokemon]
+ ld bc, wPartyMon2 - wPartyMon1
+ ld hl, wPartyMon1Species
+ call AddNTimes
+ ld de, wBattleMonSpecies
+ ld bc, wBattleMonDVs - wBattleMonSpecies
+ call CopyData
+ ld bc, wPartyMon1DVs - wPartyMon1OTID
+ add hl, bc
+ ld de, wBattleMonDVs
+ ld bc, NUM_DVS
+ call CopyData
+ ld de, wBattleMonPP
+ ld bc, NUM_MOVES
+ call CopyData
+ ld de, wBattleMonLevel
+ ld bc, wBattleMonPP - wBattleMonLevel
+ call CopyData
+ ld a, [wBattleMonSpecies2]
+ ld [wd0b5], a
+ call GetMonHeader
+ ld hl, wPartyMonNicks
+ ld a, [wPlayerMonNumber]
+ call SkipFixedLengthTextEntries
+ ld de, wBattleMonNick
+ ld bc, NAME_LENGTH
+ call CopyData
+ ld hl, wBattleMonLevel
+ ld de, wPlayerMonUnmodifiedLevel ; block of memory used for unmodified stats
+ ld bc, 1 + NUM_STATS * 2
+ call CopyData
+ call ApplyBurnAndParalysisPenaltiesToPlayer
+ call ApplyBadgeStatBoosts
+ ld a, $7 ; default stat modifier
+ ld b, NUM_STAT_MODS
+ ld hl, wPlayerMonAttackMod
+.statModLoop
+ ld [hli], a
+ dec b
+ jr nz, .statModLoop
+ ret
+
+; copies from enemy party data to current enemy mon data when sending out a new enemy mon
+LoadEnemyMonFromParty:
+ ld a, [wWhichPokemon]
+ ld bc, wEnemyMon2 - wEnemyMon1
+ ld hl, wEnemyMons
+ call AddNTimes
+ ld de, wEnemyMonSpecies
+ ld bc, wEnemyMonDVs - wEnemyMonSpecies
+ call CopyData
+ ld bc, wEnemyMon1DVs - wEnemyMon1OTID
+ add hl, bc
+ ld de, wEnemyMonDVs
+ ld bc, NUM_DVS
+ call CopyData
+ ld de, wEnemyMonPP
+ ld bc, NUM_MOVES
+ call CopyData
+ ld de, wEnemyMonLevel
+ ld bc, wEnemyMonPP - wEnemyMonLevel
+ call CopyData
+ ld a, [wEnemyMonSpecies]
+ ld [wd0b5], a
+ call GetMonHeader
+ ld hl, wEnemyMonNicks
+ ld a, [wWhichPokemon]
+ call SkipFixedLengthTextEntries
+ ld de, wEnemyMonNick
+ ld bc, NAME_LENGTH
+ call CopyData
+ ld hl, wEnemyMonLevel
+ ld de, wEnemyMonUnmodifiedLevel ; block of memory used for unmodified stats
+ ld bc, 1 + NUM_STATS * 2
+ call CopyData
+ call ApplyBurnAndParalysisPenaltiesToEnemy
+ ld hl, wMonHBaseStats
+ ld de, wEnemyMonBaseStats
+ ld b, NUM_STATS
+.copyBaseStatsLoop
+ ld a, [hli]
+ ld [de], a
+ inc de
+ dec b
+ jr nz, .copyBaseStatsLoop
+ ld a, $7 ; default stat modifier
+ ld b, NUM_STAT_MODS
+ ld hl, wEnemyMonStatMods
+.statModLoop
+ ld [hli], a
+ dec b
+ jr nz, .statModLoop
+ ld a, [wWhichPokemon]
+ ld [wEnemyMonPartyPos], a
+ ret
+
+SendOutMon:
+ callab PrintSendOutMonMessage
+ ld hl, wEnemyMonHP
+ ld a, [hli]
+ or [hl] ; is enemy mon HP zero?
+ jp z, .skipDrawingEnemyHUDAndHPBar; if HP is zero, skip drawing the HUD and HP bar
+ call DrawEnemyHUDAndHPBar
+.skipDrawingEnemyHUDAndHPBar
+ call DrawPlayerHUDAndHPBar
+ predef LoadMonBackPic
+ xor a
+ ld [hStartTileID], a
+ ld hl, wBattleAndStartSavedMenuItem
+ ld [hli], a
+ ld [hl], a
+ ld [wBoostExpByExpAll], a
+ ld [wDamageMultipliers], a
+ ld [wPlayerMoveNum], a
+ ld hl, wPlayerUsedMove
+ ld [hli], a
+ ld [hl], a
+ ld hl, wPlayerStatsToDouble
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ ld [hl], a
+ ld [wPlayerDisabledMove], a
+ ld [wPlayerDisabledMoveNumber], a
+ ld [wPlayerMonMinimized], a
+ ld b, SET_PAL_BATTLE
+ call RunPaletteCommand
+ ld hl, wEnemyBattleStatus1
+ res UsingTrappingMove, [hl]
+ ld a, $1
+ ld [H_WHOSETURN], a
+ ld a, POOF_ANIM
+ call PlayMoveAnimation
+ coord hl, 4, 11
+ predef AnimateSendingOutMon
+ ld a, [wcf91]
+ call PlayCry
+ call PrintEmptyString
+ jp SaveScreenTilesToBuffer1
+
+; show 2 stages of the player mon getting smaller before disappearing
+AnimateRetreatingPlayerMon:
+ coord hl, 1, 5
+ lb bc, 7, 7
+ call ClearScreenArea
+ coord hl, 3, 7
+ lb bc, 5, 5
+ xor a
+ ld [wDownscaledMonSize], a
+ ld [hBaseTileID], a
+ predef CopyDownscaledMonTiles
+ ld c, 4
+ call DelayFrames
+ call .clearScreenArea
+ coord hl, 4, 9
+ lb bc, 3, 3
+ ld a, 1
+ ld [wDownscaledMonSize], a
+ xor a
+ ld [hBaseTileID], a
+ predef CopyDownscaledMonTiles
+ call Delay3
+ call .clearScreenArea
+ ld a, $4c
+ Coorda 5, 11
+.clearScreenArea
+ coord hl, 1, 5
+ lb bc, 7, 7
+ jp ClearScreenArea
+
+; reads player's current mon's HP into wBattleMonHP
+ReadPlayerMonCurHPAndStatus:
+ ld a, [wPlayerMonNumber]
+ ld hl, wPartyMon1HP
+ ld bc, wPartyMon2 - wPartyMon1
+ call AddNTimes
+ ld d, h
+ ld e, l
+ ld hl, wBattleMonHP
+ ld bc, $4 ; 2 bytes HP, 1 byte unknown (unused?), 1 byte status
+ jp CopyData
+
+DrawHUDsAndHPBars:
+ call DrawPlayerHUDAndHPBar
+ jp DrawEnemyHUDAndHPBar
+
+DrawPlayerHUDAndHPBar:
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ coord hl, 9, 7
+ lb bc, 5, 11
+ call ClearScreenArea
+ callab PlacePlayerHUDTiles
+ coord hl, 18, 9
+ ld [hl], $73
+ ld de, wBattleMonNick
+ coord hl, 10, 7
+ call CenterMonName
+ call PlaceString
+ ld hl, wBattleMonSpecies
+ ld de, wLoadedMon
+ ld bc, wBattleMonDVs - wBattleMonSpecies
+ call CopyData
+ ld hl, wBattleMonLevel
+ ld de, wLoadedMonLevel
+ ld bc, wBattleMonPP - wBattleMonLevel
+ call CopyData
+ coord hl, 14, 8
+ push hl
+ inc hl
+ ld de, wLoadedMonStatus
+ call PrintStatusConditionNotFainted
+ pop hl
+ jr nz, .doNotPrintLevel
+ call PrintLevel
+.doNotPrintLevel
+ ld a, [wLoadedMonSpecies]
+ ld [wcf91], a
+ coord hl, 10, 9
+ predef DrawHP
+ ld a, $1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ld hl, wPlayerHPBarColor
+ call GetBattleHealthBarColor
+ ld hl, wBattleMonHP
+ ld a, [hli]
+ or [hl]
+ jr z, .fainted
+ ld a, [wLowHealthAlarmDisabled]
+ and a ; has the alarm been disabled because the player has already won?
+ ret nz ; if so, return
+ ld a, [wPlayerHPBarColor]
+ cp HP_BAR_RED
+ jr z, .setLowHealthAlarm
+.fainted
+ ld hl, wLowHealthAlarm
+ bit 7, [hl] ;low health alarm enabled?
+ ld [hl], $0
+ ret z
+ xor a
+ ld [wChannelSoundIDs + Ch4], a
+ ret
+.setLowHealthAlarm
+ ld hl, wLowHealthAlarm
+ set 7, [hl] ;enable low health alarm
+ ret
+
+DrawEnemyHUDAndHPBar:
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ coord hl, 0, 0
+ lb bc, 4, 12
+ call ClearScreenArea
+ callab PlaceEnemyHUDTiles
+ ld de, wEnemyMonNick
+ coord hl, 1, 0
+ call CenterMonName
+ call PlaceString
+ coord hl, 4, 1
+ push hl
+ inc hl
+ ld de, wEnemyMonStatus
+ call PrintStatusConditionNotFainted
+ pop hl
+ jr nz, .skipPrintLevel ; if the mon has a status condition, skip printing the level
+ ld a, [wEnemyMonLevel]
+ ld [wLoadedMonLevel], a
+ call PrintLevel
+.skipPrintLevel
+ ld hl, wEnemyMonHP
+ ld a, [hli]
+ ld [H_MULTIPLICAND + 1], a
+ ld a, [hld]
+ ld [H_MULTIPLICAND + 2], a
+ or [hl] ; is current HP zero?
+ jr nz, .hpNonzero
+; current HP is 0
+; set variables for DrawHPBar
+ ld c, a
+ ld e, a
+ ld d, $6
+ jp .drawHPBar
+.hpNonzero
+ xor a
+ ld [H_MULTIPLICAND], a
+ ld a, 48
+ ld [H_MULTIPLIER], a
+ call Multiply ; multiply current HP by 48
+ ld hl, wEnemyMonMaxHP
+ ld a, [hli]
+ ld b, a
+ ld a, [hl]
+ ld [H_DIVISOR], a
+ ld a, b
+ and a ; is max HP > 255?
+ jr z, .doDivide
+; if max HP > 255, scale both (current HP * 48) and max HP by dividing by 4 so that max HP fits in one byte
+; (it needs to be one byte so it can be used as the divisor for the Divide function)
+ ld a, [H_DIVISOR]
+ srl b
+ rr a
+ srl b
+ rr a
+ ld [H_DIVISOR], a
+ ld a, [H_PRODUCT + 2]
+ ld b, a
+ srl b
+ ld a, [H_PRODUCT + 3]
+ rr a
+ srl b
+ rr a
+ ld [H_PRODUCT + 3], a
+ ld a, b
+ ld [H_PRODUCT + 2], a
+.doDivide
+ ld a, [H_PRODUCT + 2]
+ ld [H_DIVIDEND], a
+ ld a, [H_PRODUCT + 3]
+ ld [H_DIVIDEND + 1], a
+ ld a, $2
+ ld b, a
+ call Divide ; divide (current HP * 48) by max HP
+ ld a, [H_QUOTIENT + 3]
+; set variables for DrawHPBar
+ ld e, a
+ ld a, $6
+ ld d, a
+ ld c, a
+.drawHPBar
+ xor a
+ ld [wHPBarType], a
+ coord hl, 2, 2
+ call DrawHPBar
+ ld a, $1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ld hl, wEnemyHPBarColor
+
+GetBattleHealthBarColor:
+ ld b, [hl]
+ call GetHealthBarColor
+ ld a, [hl]
+ cp b
+ ret z
+ ld b, SET_PAL_BATTLE
+ jp RunPaletteCommand
+
+; center's mon's name on the battle screen
+; if the name is 1 or 2 letters long, it is printed 2 spaces more to the right than usual
+; (i.e. for names longer than 4 letters)
+; if the name is 3 or 4 letters long, it is printed 1 space more to the right than usual
+; (i.e. for names longer than 4 letters)
+CenterMonName:
+ push de
+ inc hl
+ inc hl
+ ld b, $2
+.loop
+ inc de
+ ld a, [de]
+ cp "@"
+ jr z, .done
+ inc de
+ ld a, [de]
+ cp "@"
+ jr z, .done
+ dec hl
+ dec b
+ jr nz, .loop
+.done
+ pop de
+ ret
+
+DisplayBattleMenu:
+ call LoadScreenTilesFromBuffer1 ; restore saved screen
+ ld a, [wBattleType]
+ and a
+ jr nz, .nonstandardbattle
+ call DrawHUDsAndHPBars
+ call PrintEmptyString
+ call SaveScreenTilesToBuffer1
+.nonstandardbattle
+ ld a, [wBattleType]
+ cp BATTLE_TYPE_SAFARI
+ ld a, BATTLE_MENU_TEMPLATE
+ jr nz, .menuselected
+ ld a, SAFARI_BATTLE_MENU_TEMPLATE
+.menuselected
+ ld [wTextBoxID], a
+ call DisplayTextBoxID
+ ld a, [wBattleType]
+ dec a
+ jp nz, .handleBattleMenuInput ; handle menu input if it's not the old man tutorial
+; the following happens for the old man tutorial
+ ld hl, wPlayerName
+ ld de, wGrassRate
+ ld bc, NAME_LENGTH
+ call CopyData ; temporarily save the player name in unused space,
+ ; which is supposed to get overwritten when entering a
+ ; map with wild Pokémon. Due to an oversight, the data
+ ; may not get overwritten (cinnabar) and the infamous
+ ; Missingno. glitch can show up.
+ ld hl, .oldManName
+ ld de, wPlayerName
+ ld bc, NAME_LENGTH
+ call CopyData
+; the following simulates the keystrokes by drawing menus on screen
+ coord hl, 7, 14
+ ld [hl], "▶"
+ ld c, 80
+ call DelayFrames
+ ld [hl], " "
+ coord hl, 7, 16
+ ld [hl], "▶"
+ ld c, 50
+ call DelayFrames
+ ld [hl], "▷"
+ ld a, $2 ; select the "ITEM" menu
+ jp .upperLeftMenuItemWasNotSelected
+.oldManName
+ db "GREIS@"
+.handleBattleMenuInput
+ ld a, [wBattleAndStartSavedMenuItem]
+ ld [wCurrentMenuItem], a
+ ld [wLastMenuItem], a
+ sub 2 ; check if the cursor is in the left column
+ jr c, .leftColumn
+; cursor is in the right column
+ ld [wCurrentMenuItem], a
+ ld [wLastMenuItem], a
+ jr .rightColumn
+.leftColumn ; put cursor in left column of menu
+ ld a, [wBattleType]
+ cp BATTLE_TYPE_SAFARI
+ ld a, " "
+ jr z, .safariLeftColumn
+; put cursor in left column for normal battle menu (i.e. when it's not a Safari battle)
+ Coorda 12, 14 ; clear upper cursor position in right column
+ Coorda 12, 16 ; clear lower cursor position in right column
+ ld b, $7 ; top menu item X
+ jr .leftColumn_WaitForInput
+.safariLeftColumn
+ Coorda 12, 14
+ Coorda 12, 16
+ coord hl, 7, 14
+ ld de, wNumSafariBalls
+ lb bc, 1, 2
+ call PrintNumber
+ ld b, $1 ; top menu item X
+.leftColumn_WaitForInput
+ ld hl, wTopMenuItemY
+ ld a, $e
+ ld [hli], a ; wTopMenuItemY
+ ld a, b
+ ld [hli], a ; wTopMenuItemX
+ inc hl
+ inc hl
+ ld a, $1
+ ld [hli], a ; wMaxMenuItem
+ ld [hl], D_RIGHT | A_BUTTON ; wMenuWatchedKeys
+ call HandleMenuInput
+ bit 4, a ; check if right was pressed
+ jr nz, .rightColumn
+ jr .AButtonPressed ; the A button was pressed
+.rightColumn ; put cursor in right column of menu
+ ld a, [wBattleType]
+ cp BATTLE_TYPE_SAFARI
+ ld a, " "
+ jr z, .safariRightColumn
+; put cursor in right column for normal battle menu (i.e. when it's not a Safari battle)
+ Coorda 7, 14 ; clear upper cursor position in left column
+ Coorda 7, 16 ; clear lower cursor position in left column
+ ld b, $c ; top menu item X
+ jr .rightColumn_WaitForInput
+.safariRightColumn
+ Coorda 1, 14 ; clear upper cursor position in left column
+ Coorda 1, 16 ; clear lower cursor position in left column
+ coord hl, 7, 14
+ ld de, wNumSafariBalls
+ lb bc, 1, 2
+ call PrintNumber
+ ld b, $c ; top menu item X
+.rightColumn_WaitForInput
+ ld hl, wTopMenuItemY
+ ld a, $e
+ ld [hli], a ; wTopMenuItemY
+ ld a, b
+ ld [hli], a ; wTopMenuItemX
+ inc hl
+ inc hl
+ ld a, $1
+ ld [hli], a ; wMaxMenuItem
+ ld a, D_LEFT | A_BUTTON
+ ld [hli], a ; wMenuWatchedKeys
+ call HandleMenuInput
+ bit 5, a ; check if left was pressed
+ jr nz, .leftColumn ; if left was pressed, jump
+ ld a, [wCurrentMenuItem]
+ add $2 ; if we're in the right column, the actual id is +2
+ ld [wCurrentMenuItem], a
+.AButtonPressed
+ call PlaceUnfilledArrowMenuCursor
+ ld a, [wBattleType]
+ cp BATTLE_TYPE_SAFARI
+ ld a, [wCurrentMenuItem]
+ ld [wBattleAndStartSavedMenuItem], a
+ jr z, .handleMenuSelection
+; not Safari battle
+; swap the IDs of the item menu and party menu (this is probably because they swapped the positions
+; of these menu items in first generation English versions)
+ cp $1 ; was the item menu selected?
+ jr nz, .notItemMenu
+; item menu was selected
+ inc a ; increment a to 2
+ jr .handleMenuSelection
+.notItemMenu
+ cp $2 ; was the party menu selected?
+ jr nz, .handleMenuSelection
+; party menu selected
+ dec a ; decrement a to 1
+.handleMenuSelection
+ and a
+ jr nz, .upperLeftMenuItemWasNotSelected
+; the upper left menu item was selected
+ ld a, [wBattleType]
+ cp BATTLE_TYPE_SAFARI
+ jr z, .throwSafariBallWasSelected
+; the "FIGHT" menu was selected
+ xor a
+ ld [wNumRunAttempts], a
+ jp LoadScreenTilesFromBuffer1 ; restore saved screen and return
+.throwSafariBallWasSelected
+ ld a, SAFARI_BALL
+ ld [wcf91], a
+ jr UseBagItem
+
+.upperLeftMenuItemWasNotSelected ; a menu item other than the upper left item was selected
+ cp $2
+ jp nz, PartyMenuOrRockOrRun
+
+; either the bag (normal battle) or bait (safari battle) was selected
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ jr nz, .notLinkBattle
+
+; can't use items in link battles
+ ld hl, ItemsCantBeUsedHereText
+ call PrintText
+ jp DisplayBattleMenu
+
+.notLinkBattle
+ call SaveScreenTilesToBuffer2
+ ld a, [wBattleType]
+ cp BATTLE_TYPE_SAFARI
+ jr nz, BagWasSelected
+
+; bait was selected
+ ld a, SAFARI_BAIT
+ ld [wcf91], a
+ jr UseBagItem
+
+BagWasSelected:
+ call LoadScreenTilesFromBuffer1
+ ld a, [wBattleType]
+ and a ; is it a normal battle?
+ jr nz, .next
+
+; normal battle
+ call DrawHUDsAndHPBars
+.next
+ ld a, [wBattleType]
+ dec a ; is it the old man tutorial?
+ jr nz, DisplayPlayerBag ; no, it is a normal battle
+ ld hl, OldManItemList
+ ld a, l
+ ld [wListPointer], a
+ ld a, h
+ ld [wListPointer + 1], a
+ jr DisplayBagMenu
+
+OldManItemList:
+ db 1 ; # items
+ db POKE_BALL, 50
+ db -1
+
+DisplayPlayerBag:
+ ; get the pointer to player's bag when in a normal battle
+ ld hl, wNumBagItems
+ ld a, l
+ ld [wListPointer], a
+ ld a, h
+ ld [wListPointer + 1], a
+
+DisplayBagMenu:
+ xor a
+ ld [wPrintItemPrices], a
+ ld a, ITEMLISTMENU
+ ld [wListMenuID], a
+ ld a, [wBagSavedMenuItem]
+ ld [wCurrentMenuItem], a
+ call DisplayListMenuID
+ ld a, [wCurrentMenuItem]
+ ld [wBagSavedMenuItem], a
+ ld a, $0
+ ld [wMenuWatchMovingOutOfBounds], a
+ ld [wMenuItemToSwap], a
+ jp c, DisplayBattleMenu ; go back to battle menu if an item was not selected
+
+UseBagItem:
+ ; either use an item from the bag or use a safari zone item
+ ld a, [wcf91]
+ ld [wd11e], a
+ call GetItemName
+ call CopyStringToCF50 ; copy name
+ xor a
+ ld [wPseudoItemID], a
+ call UseItem
+ call LoadHudTilePatterns
+ call ClearSprites
+ xor a
+ ld [wCurrentMenuItem], a
+ ld a, [wBattleType]
+ cp BATTLE_TYPE_SAFARI
+ jr z, .checkIfMonCaptured
+
+ ld a, [wActionResultOrTookBattleTurn]
+ and a ; was the item used successfully?
+ jp z, BagWasSelected ; if not, go back to the bag menu
+
+ ld a, [wPlayerBattleStatus1]
+ bit UsingTrappingMove, a ; is the player using a multi-turn move like wrap?
+ jr z, .checkIfMonCaptured
+ ld hl, wPlayerNumAttacksLeft
+ dec [hl]
+ jr nz, .checkIfMonCaptured
+ ld hl, wPlayerBattleStatus1
+ res UsingTrappingMove, [hl] ; not using multi-turn move any more
+
+.checkIfMonCaptured
+ ld a, [wCapturedMonSpecies]
+ and a ; was the enemy mon captured with a ball?
+ jr nz, .returnAfterCapturingMon
+
+ ld a, [wBattleType]
+ cp BATTLE_TYPE_SAFARI
+ jr z, .returnAfterUsingItem_NoCapture
+; not a safari battle
+ call LoadScreenTilesFromBuffer1
+ call DrawHUDsAndHPBars
+ call Delay3
+.returnAfterUsingItem_NoCapture
+
+ call GBPalNormal
+ and a ; reset carry
+ ret
+
+.returnAfterCapturingMon
+ call GBPalNormal
+ xor a
+ ld [wCapturedMonSpecies], a
+ ld a, $2
+ ld [wBattleResult], a
+ scf ; set carry
+ ret
+
+ItemsCantBeUsedHereText:
+ TX_FAR _ItemsCantBeUsedHereText
+ db "@"
+
+PartyMenuOrRockOrRun:
+ dec a ; was Run selected?
+ jp nz, BattleMenu_RunWasSelected
+; party menu or rock was selected
+ call SaveScreenTilesToBuffer2
+ ld a, [wBattleType]
+ cp BATTLE_TYPE_SAFARI
+ jr nz, .partyMenuWasSelected
+; safari battle
+ ld a, SAFARI_ROCK
+ ld [wcf91], a
+ jp UseBagItem
+.partyMenuWasSelected
+ call LoadScreenTilesFromBuffer1
+ xor a ; NORMAL_PARTY_MENU
+ ld [wPartyMenuTypeOrMessageID], a
+ ld [wMenuItemToSwap], a
+ call DisplayPartyMenu
+.checkIfPartyMonWasSelected
+ jp nc, .partyMonWasSelected ; if a party mon was selected, jump, else we quit the party menu
+.quitPartyMenu
+ call ClearSprites
+ call GBPalWhiteOut
+ call LoadHudTilePatterns
+ call LoadScreenTilesFromBuffer2
+ call RunDefaultPaletteCommand
+ call GBPalNormal
+ jp DisplayBattleMenu
+.partyMonDeselected
+ coord hl, 11, 11
+ ld bc, 6 * SCREEN_WIDTH + 9
+ ld a, " "
+ call FillMemory
+ xor a ; NORMAL_PARTY_MENU
+ ld [wPartyMenuTypeOrMessageID], a
+ call GoBackToPartyMenu
+ jr .checkIfPartyMonWasSelected
+.partyMonWasSelected
+ ld a, SWITCH_STATS_CANCEL_MENU_TEMPLATE
+ ld [wTextBoxID], a
+ call DisplayTextBoxID
+ ld hl, wTopMenuItemY
+ ld a, $c
+ ld [hli], a ; wTopMenuItemY
+ ld [hli], a ; wTopMenuItemX
+ xor a
+ ld [hli], a ; wCurrentMenuItem
+ inc hl
+ ld a, $2
+ ld [hli], a ; wMaxMenuItem
+ ld a, B_BUTTON | A_BUTTON
+ ld [hli], a ; wMenuWatchedKeys
+ xor a
+ ld [hl], a ; wLastMenuItem
+ call HandleMenuInput
+ bit 1, a ; was A pressed?
+ jr nz, .partyMonDeselected ; if B was pressed, jump
+; A was pressed
+ call PlaceUnfilledArrowMenuCursor
+ ld a, [wCurrentMenuItem]
+ cp $2 ; was Cancel selected?
+ jr z, .quitPartyMenu ; if so, quit the party menu entirely
+ and a ; was Switch selected?
+ jr z, .switchMon ; if so, jump
+; Stats was selected
+ xor a ; PLAYER_PARTY_DATA
+ ld [wMonDataLocation], a
+ ld hl, wPartyMon1
+ call ClearSprites
+; display the two status screens
+ predef StatusScreen
+ predef StatusScreen2
+; now we need to reload the enemy mon pic
+ ld a, [wEnemyBattleStatus2]
+ bit HasSubstituteUp, a ; does the enemy mon have a substitute?
+ ld hl, AnimationSubstitute
+ jr nz, .doEnemyMonAnimation
+; enemy mon doesn't have substitute
+ ld a, [wEnemyMonMinimized]
+ and a ; has the enemy mon used Minimise?
+ ld hl, AnimationMinimizeMon
+ jr nz, .doEnemyMonAnimation
+; enemy mon is not minimised
+ ld a, [wEnemyMonSpecies]
+ ld [wcf91], a
+ ld [wd0b5], a
+ call GetMonHeader
+ ld de, vFrontPic
+ call LoadMonFrontSprite
+ jr .enemyMonPicReloaded
+.doEnemyMonAnimation
+ ld b, BANK(AnimationSubstitute) ; BANK(AnimationMinimizeMon)
+ call Bankswitch
+.enemyMonPicReloaded ; enemy mon pic has been reloaded, so return to the party menu
+ jp .partyMenuWasSelected
+.switchMon
+ ld a, [wPlayerMonNumber]
+ ld d, a
+ ld a, [wWhichPokemon]
+ cp d ; check if the mon to switch to is already out
+ jr nz, .notAlreadyOut
+; mon is already out
+ ld hl, AlreadyOutText
+ call PrintText
+ jp .partyMonDeselected
+.notAlreadyOut
+ call HasMonFainted
+ jp z, .partyMonDeselected ; can't switch to fainted mon
+ ld a, $1
+ ld [wActionResultOrTookBattleTurn], a
+ call GBPalWhiteOut
+ call ClearSprites
+ call LoadHudTilePatterns
+ call LoadScreenTilesFromBuffer1
+ call RunDefaultPaletteCommand
+ call GBPalNormal
+; fall through to SwitchPlayerMon
+
+SwitchPlayerMon:
+ callab RetreatMon
+ ld c, 50
+ call DelayFrames
+ call AnimateRetreatingPlayerMon
+ ld a, [wWhichPokemon]
+ ld [wPlayerMonNumber], a
+ ld c, a
+ ld b, FLAG_SET
+ push bc
+ ld hl, wPartyGainExpFlags
+ predef FlagActionPredef
+ pop bc
+ ld hl, wPartyFoughtCurrentEnemyFlags
+ predef FlagActionPredef
+ call LoadBattleMonFromParty
+ call SendOutMon
+ call SaveScreenTilesToBuffer1
+ ld a, $2
+ ld [wCurrentMenuItem], a
+ and a
+ ret
+
+AlreadyOutText:
+ TX_FAR _AlreadyOutText
+ db "@"
+
+BattleMenu_RunWasSelected:
+ call LoadScreenTilesFromBuffer1
+ ld a, $3
+ ld [wCurrentMenuItem], a
+ ld hl, wBattleMonSpeed
+ ld de, wEnemyMonSpeed
+ call TryRunningFromBattle
+ ld a, 0
+ ld [wForcePlayerToChooseMon], a
+ ret c
+ ld a, [wActionResultOrTookBattleTurn]
+ and a
+ ret nz ; return if the player couldn't escape
+ jp DisplayBattleMenu
+
+MoveSelectionMenu:
+ ld a, [wMoveMenuType]
+ dec a
+ jr z, .mimicmenu
+ dec a
+ jr z, .relearnmenu
+ jr .regularmenu
+
+.loadmoves
+ ld de, wMoves
+ ld bc, NUM_MOVES
+ call CopyData
+ callab FormatMovesString
+ ret
+
+.writemoves
+ ld de, wMovesString
+ ld a, [hFlags_0xFFF6]
+ set 2, a
+ ld [hFlags_0xFFF6], a
+ call PlaceString
+ ld a, [hFlags_0xFFF6]
+ res 2, a
+ ld [hFlags_0xFFF6], a
+ ret
+
+.regularmenu
+ call AnyMoveToSelect
+ ret z
+ ld hl, wBattleMonMoves
+ call .loadmoves
+ coord hl, 4, 12
+ ld b, 4
+ ld c, 14
+ di ; out of pure coincidence, it is possible for vblank to occur between the di and ei
+ ; so it is necessary to put the di ei block to not cause tearing
+ call TextBoxBorder
+ coord hl, 4, 12
+ ld [hl], $7a
+ coord hl, 10, 12
+ ld [hl], $7e
+ ei
+ coord hl, 6, 13
+ call .writemoves
+ ld b, $5
+ ld a, $c
+ jr .menuset
+.mimicmenu
+ ld hl, wEnemyMonMoves
+ call .loadmoves
+ coord hl, 0, 7
+ ld b, 4
+ ld c, 14
+ call TextBoxBorder
+ coord hl, 2, 8
+ call .writemoves
+ ld b, $1
+ ld a, $7
+ jr .menuset
+.relearnmenu
+ ld a, [wWhichPokemon]
+ ld hl, wPartyMon1Moves
+ ld bc, wPartyMon2 - wPartyMon1
+ call AddNTimes
+ call .loadmoves
+ coord hl, 4, 7
+ ld b, 4
+ ld c, 14
+ call TextBoxBorder
+ coord hl, 6, 8
+ call .writemoves
+ ld b, $5
+ ld a, $7
+.menuset
+ ld hl, wTopMenuItemY
+ ld [hli], a ; wTopMenuItemY
+ ld a, b
+ ld [hli], a ; wTopMenuItemX
+ ld a, [wMoveMenuType]
+ cp $1
+ jr z, .selectedmoveknown
+ ld a, $1
+ jr nc, .selectedmoveknown
+ ld a, [wPlayerMoveListIndex]
+ inc a
+.selectedmoveknown
+ ld [hli], a ; wCurrentMenuItem
+ inc hl ; wTileBehindCursor untouched
+ ld a, [wNumMovesMinusOne]
+ inc a
+ inc a
+ ld [hli], a ; wMaxMenuItem
+ ld a, [wMoveMenuType]
+ dec a
+ ld b, D_UP | D_DOWN | A_BUTTON
+ jr z, .matchedkeyspicked
+ dec a
+ ld b, D_UP | D_DOWN | A_BUTTON | B_BUTTON
+ jr z, .matchedkeyspicked
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ jr z, .matchedkeyspicked
+ ld a, [wFlags_D733]
+ bit BIT_TEST_BATTLE, a
+ ld b, D_UP | D_DOWN | A_BUTTON | B_BUTTON | SELECT
+ jr z, .matchedkeyspicked
+ ld b, $ff
+.matchedkeyspicked
+ ld a, b
+ ld [hli], a ; wMenuWatchedKeys
+ ld a, [wMoveMenuType]
+ cp $1
+ jr z, .movelistindex1
+ ld a, [wPlayerMoveListIndex]
+ inc a
+.movelistindex1
+ ld [hl], a
+; fallthrough
+
+SelectMenuItem:
+ ld a, [wMoveMenuType]
+ and a
+ jr z, .battleselect
+ dec a
+ jr nz, .select
+ coord hl, 1, 14
+ ld de, WhichTechniqueString
+ call PlaceString
+ jr .select
+.battleselect
+ ld a, [wFlags_D733]
+ bit BIT_TEST_BATTLE, a
+ jr nz, .select
+ call PrintMenuItem
+ ld a, [wMenuItemToSwap]
+ and a
+ jr z, .select
+ coord hl, 5, 13
+ dec a
+ ld bc, SCREEN_WIDTH
+ call AddNTimes
+ ld [hl], "▷"
+.select
+ ld hl, hFlags_0xFFF6
+ set 1, [hl]
+ call HandleMenuInput
+ ld hl, hFlags_0xFFF6
+ res 1, [hl]
+ bit 6, a
+ jp nz, SelectMenuItem_CursorUp ; up
+ bit 7, a
+ jp nz, SelectMenuItem_CursorDown ; down
+ bit 2, a
+ jp nz, SwapMovesInMenu ; select
+ bit 1, a ; B, but was it reset above?
+ push af
+ xor a
+ ld [wMenuItemToSwap], a
+ ld a, [wCurrentMenuItem]
+ dec a
+ ld [wCurrentMenuItem], a
+ ld b, a
+ ld a, [wMoveMenuType]
+ dec a ; if not mimic
+ jr nz, .notB
+ pop af
+ ret
+.notB
+ dec a
+ ld a, b
+ ld [wPlayerMoveListIndex], a
+ jr nz, .moveselected
+ pop af
+ ret
+.moveselected
+ pop af
+ ret nz
+ ld hl, wBattleMonPP
+ ld a, [wCurrentMenuItem]
+ ld c, a
+ ld b, $0
+ add hl, bc
+ ld a, [hl]
+ and $3f
+ jr z, .noPP
+ ld a, [wPlayerDisabledMove]
+ swap a
+ and $f
+ dec a
+ cp c
+ jr z, .disabled
+ ld a, [wPlayerBattleStatus3]
+ bit 3, a ; transformed
+ jr nz, .dummy ; game freak derp
+.dummy
+ ld a, [wCurrentMenuItem]
+ ld hl, wBattleMonMoves
+ ld c, a
+ ld b, $0
+ add hl, bc
+ ld a, [hl]
+ ld [wPlayerSelectedMove], a
+ xor a
+ ret
+.disabled
+ ld hl, MoveDisabledText
+ jr .print
+.noPP
+ ld hl, MoveNoPPText
+.print
+ call PrintText
+ call LoadScreenTilesFromBuffer1
+ jp MoveSelectionMenu
+
+MoveNoPPText:
+ TX_FAR _MoveNoPPText
+ db "@"
+
+MoveDisabledText:
+ TX_FAR _MoveDisabledText
+ db "@"
+
+WhichTechniqueString:
+ db "Welche attacke?"
+ next " @"
+
+SelectMenuItem_CursorUp:
+ ld a, [wCurrentMenuItem]
+ and a
+ jp nz, SelectMenuItem
+ call EraseMenuCursor
+ ld a, [wNumMovesMinusOne]
+ inc a
+ ld [wCurrentMenuItem], a
+ jp SelectMenuItem
+
+SelectMenuItem_CursorDown:
+ ld a, [wCurrentMenuItem]
+ ld b, a
+ ld a, [wNumMovesMinusOne]
+ inc a
+ inc a
+ cp b
+ jp nz, SelectMenuItem
+ call EraseMenuCursor
+ ld a, $1
+ ld [wCurrentMenuItem], a
+ jp SelectMenuItem
+
+AnyMoveToSelect:
+; return z and Struggle as the selected move if all moves have 0 PP and/or are disabled
+ ld a, STRUGGLE
+ ld [wPlayerSelectedMove], a
+ ld a, [wPlayerDisabledMove]
+ and a
+ ld hl, wBattleMonPP
+ jr nz, .handleDisabledMove
+ ld a, [hli]
+ or [hl]
+ inc hl
+ or [hl]
+ inc hl
+ or [hl]
+ and $3f
+ ret nz
+ jr .noMovesLeft
+.handleDisabledMove
+ swap a
+ and $f ; get disabled move
+ ld b, a
+ ld d, NUM_MOVES + 1
+ xor a
+.handleDisabledMovePPLoop
+ dec d
+ jr z, .allMovesChecked
+ ld c, [hl] ; get move PP
+ inc hl
+ dec b ; is this the disabled move?
+ jr z, .handleDisabledMovePPLoop ; if so, ignore its PP value
+ or c
+ jr .handleDisabledMovePPLoop
+.allMovesChecked
+ and a ; any PP left?
+ ret nz ; return if a move has PP left
+.noMovesLeft
+ ld hl, NoMovesLeftText
+ call PrintText
+ ld c, 60
+ call DelayFrames
+ xor a
+ ret
+
+NoMovesLeftText:
+ TX_FAR _NoMovesLeftText
+ db "@"
+
+SwapMovesInMenu:
+ ld a, [wMenuItemToSwap]
+ and a
+ jr z, .noMenuItemSelected
+ ld hl, wBattleMonMoves
+ call .swapBytes ; swap moves
+ ld hl, wBattleMonPP
+ call .swapBytes ; swap move PP
+; update the index of the disabled move if necessary
+ ld hl, wPlayerDisabledMove
+ ld a, [hl]
+ swap a
+ and $f
+ ld b, a
+ ld a, [wCurrentMenuItem]
+ cp b
+ jr nz, .next
+ ld a, [hl]
+ and $f
+ ld b, a
+ ld a, [wMenuItemToSwap]
+ swap a
+ add b
+ ld [hl], a
+ jr .swapMovesInPartyMon
+.next
+ ld a, [wMenuItemToSwap]
+ cp b
+ jr nz, .swapMovesInPartyMon
+ ld a, [hl]
+ and $f
+ ld b, a
+ ld a, [wCurrentMenuItem]
+ swap a
+ add b
+ ld [hl], a
+.swapMovesInPartyMon
+ ld hl, wPartyMon1Moves
+ ld a, [wPlayerMonNumber]
+ ld bc, wPartyMon2 - wPartyMon1
+ call AddNTimes
+ push hl
+ call .swapBytes ; swap moves
+ pop hl
+ ld bc, wPartyMon1PP - wPartyMon1Moves
+ add hl, bc
+ call .swapBytes ; swap move PP
+ xor a
+ ld [wMenuItemToSwap], a ; deselect the item
+ jp MoveSelectionMenu
+.swapBytes
+ push hl
+ ld a, [wMenuItemToSwap]
+ dec a
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld d, h
+ ld e, l
+ pop hl
+ ld a, [wCurrentMenuItem]
+ dec a
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld a, [de]
+ ld b, [hl]
+ ld [hl], a
+ ld a, b
+ ld [de], a
+ ret
+.noMenuItemSelected
+ ld a, [wCurrentMenuItem]
+ ld [wMenuItemToSwap], a ; select the current menu item for swapping
+ jp MoveSelectionMenu
+
+PrintMenuItem:
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ coord hl, 0, 8
+ ld b, 3
+ ld c, 9
+ call TextBoxBorder
+ ld a, [wPlayerDisabledMove]
+ and a
+ jr z, .notDisabled
+ swap a
+ and $f
+ ld b, a
+ ld a, [wCurrentMenuItem]
+ cp b
+ jr nz, .notDisabled
+ coord hl, 1, 10
+ ld de, DisabledText
+ call PlaceString
+ jr .moveDisabled
+.notDisabled
+ ld hl, wCurrentMenuItem
+ dec [hl]
+ xor a
+ ld [H_WHOSETURN], a
+ ld hl, wBattleMonMoves
+ ld a, [wCurrentMenuItem]
+ ld c, a
+ ld b, $0 ; which item in the menu is the cursor pointing to? (0-3)
+ add hl, bc ; point to the item (move) in memory
+ ld a, [hl]
+ ld [wPlayerSelectedMove], a ; update wPlayerSelectedMove even if the move
+ ; isn't actually selected (just pointed to by the cursor)
+ ld a, [wPlayerMonNumber]
+ ld [wWhichPokemon], a
+ ld a, BATTLE_MON_DATA
+ ld [wMonDataLocation], a
+ callab GetMaxPP
+ ld hl, wCurrentMenuItem
+ ld c, [hl]
+ inc [hl]
+ ld b, $0
+ ld hl, wBattleMonPP
+ add hl, bc
+ ld a, [hl]
+ and $3f
+ ld [wcd6d], a
+; print TYPE/<type> and <curPP>/<maxPP>
+ coord hl, 1, 9
+ ld de, TypeText
+ call PlaceString
+ coord hl, 7, 11
+ ld [hl], "/"
+ coord hl, 4, 9
+ ld [hl], "/"
+ coord hl, 5, 11
+ ld de, wcd6d
+ lb bc, 1, 2
+ call PrintNumber
+ coord hl, 8, 11
+ ld de, wMaxPP
+ lb bc, 1, 2
+ call PrintNumber
+ call GetCurrentMove
+ coord hl, 2, 10
+ predef PrintMoveType
+.moveDisabled
+ ld a, $1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ jp Delay3
+
+DisabledText:
+ db "BLOCKIERT@"
+
+TypeText:
+ db "TYP@"
+
+SelectEnemyMove:
+ ld a, [wLinkState]
+ sub LINK_STATE_BATTLING
+ jr nz, .noLinkBattle
+; link battle
+ call SaveScreenTilesToBuffer1
+ call LinkBattleExchangeData
+ call LoadScreenTilesFromBuffer1
+ ld a, [wSerialExchangeNybbleReceiveData]
+ cp LINKBATTLE_STRUGGLE
+ jp z, .linkedOpponentUsedStruggle
+ cp LINKBATTLE_NO_ACTION
+ jr z, .unableToSelectMove
+ cp 4
+ ret nc
+ ld [wEnemyMoveListIndex], a
+ ld c, a
+ ld hl, wEnemyMonMoves
+ ld b, 0
+ add hl, bc
+ ld a, [hl]
+ jr .done
+.noLinkBattle
+ ld a, [wEnemyBattleStatus2]
+ and (1 << NeedsToRecharge) | (1 << UsingRage) ; need to recharge or using rage
+ ret nz
+ ld hl, wEnemyBattleStatus1
+ ld a, [hl]
+ and (1 << ChargingUp) | (1 << ThrashingAbout) ; using a charging move or thrash/petal dance
+ ret nz
+ ld a, [wEnemyMonStatus]
+ and SLP | 1 << FRZ ; sleeping or frozen
+ ret nz
+ ld a, [wEnemyBattleStatus1]
+ and (1 << UsingTrappingMove) | (1 << StoringEnergy) ; using a trapping move like wrap or bide
+ ret nz
+ ld a, [wPlayerBattleStatus1]
+ bit UsingTrappingMove, a ; caught in player's trapping move (e.g. wrap)
+ jr z, .canSelectMove
+.unableToSelectMove
+ ld a, $ff
+ jr .done
+.canSelectMove
+ ld hl, wEnemyMonMoves+1 ; 2nd enemy move
+ ld a, [hld]
+ and a
+ jr nz, .atLeastTwoMovesAvailable
+ ld a, [wEnemyDisabledMove]
+ and a
+ ld a, STRUGGLE ; struggle if the only move is disabled
+ jr nz, .done
+.atLeastTwoMovesAvailable
+ ld a, [wIsInBattle]
+ dec a
+ jr z, .chooseRandomMove ; wild encounter
+ callab AIEnemyTrainerChooseMoves
+.chooseRandomMove
+ push hl
+ call BattleRandom
+ ld b, $1
+ cp $3f ; select move 1, [0,3e] (63/256 chance)
+ jr c, .moveChosen
+ inc hl
+ inc b
+ cp $7f ; select move 2, [3f,7e] (64/256 chance)
+ jr c, .moveChosen
+ inc hl
+ inc b
+ cp $be ; select move 3, [7f,bd] (63/256 chance)
+ jr c, .moveChosen
+ inc hl
+ inc b ; select move 4, [be,ff] (66/256 chance)
+.moveChosen
+ ld a, b
+ dec a
+ ld [wEnemyMoveListIndex], a
+ ld a, [wEnemyDisabledMove]
+ swap a
+ and $f
+ cp b
+ ld a, [hl]
+ pop hl
+ jr z, .chooseRandomMove ; move disabled, try again
+ and a
+ jr z, .chooseRandomMove ; move non-existant, try again
+.done
+ ld [wEnemySelectedMove], a
+ ret
+.linkedOpponentUsedStruggle
+ ld a, STRUGGLE
+ jr .done
+
+; this appears to exchange data with the other gameboy during link battles
+LinkBattleExchangeData:
+ ld a, $ff
+ ld [wSerialExchangeNybbleReceiveData], a
+ ld a, [wPlayerMoveListIndex]
+ cp LINKBATTLE_RUN ; is the player running from battle?
+ jr z, .doExchange
+ ld a, [wActionResultOrTookBattleTurn]
+ and a ; is the player switching in another mon?
+ jr nz, .switching
+; the player used a move
+ ld a, [wPlayerSelectedMove]
+ cp STRUGGLE
+ ld b, LINKBATTLE_STRUGGLE
+ jr z, .next
+ dec b ; LINKBATTLE_NO_ACTION
+ inc a ; does move equal -1 (i.e. no action)?
+ jr z, .next
+ ld a, [wPlayerMoveListIndex]
+ jr .doExchange
+.switching
+ ld a, [wWhichPokemon]
+ add 4
+ ld b, a
+.next
+ ld a, b
+.doExchange
+ ld [wSerialExchangeNybbleSendData], a
+ callab PrintWaitingText
+.syncLoop1
+ call Serial_ExchangeNybble
+ call DelayFrame
+ ld a, [wSerialExchangeNybbleReceiveData]
+ inc a
+ jr z, .syncLoop1
+ ld b, 10
+.syncLoop2
+ call DelayFrame
+ call Serial_ExchangeNybble
+ dec b
+ jr nz, .syncLoop2
+ ld b, 10
+.syncLoop3
+ call DelayFrame
+ call Serial_SendZeroByte
+ dec b
+ jr nz, .syncLoop3
+ ret
+
+ExecutePlayerMove:
+ xor a
+ ld [H_WHOSETURN], a ; set player's turn
+ ld a, [wPlayerSelectedMove]
+ inc a
+ jp z, ExecutePlayerMoveDone ; for selected move = FF, skip most of player's turn
+ xor a
+ ld [wMoveMissed], a
+ ld [wMonIsDisobedient], a
+ ld [wMoveDidntMiss], a
+ ld a, $a
+ ld [wDamageMultipliers], a
+ ld a, [wActionResultOrTookBattleTurn]
+ and a ; has the player already used the turn (e.g. by using an item, trying to run or switching pokemon)
+ jp nz, ExecutePlayerMoveDone
+ call PrintGhostText
+ jp z, ExecutePlayerMoveDone
+ call CheckPlayerStatusConditions
+ jr nz, .playerHasNoSpecialCondition
+ jp hl
+.playerHasNoSpecialCondition
+ call GetCurrentMove
+ ld hl, wPlayerBattleStatus1
+ bit ChargingUp, [hl] ; charging up for attack
+ jr nz, PlayerCanExecuteChargingMove
+ call CheckForDisobedience
+ jp z, ExecutePlayerMoveDone
+
+CheckIfPlayerNeedsToChargeUp:
+ ld a, [wPlayerMoveEffect]
+ cp CHARGE_EFFECT
+ jp z, JumpMoveEffect
+ cp FLY_EFFECT
+ jp z, JumpMoveEffect
+ jr PlayerCanExecuteMove
+
+; in-battle stuff
+PlayerCanExecuteChargingMove:
+ ld hl,wPlayerBattleStatus1
+ res ChargingUp,[hl] ; reset charging up and invulnerability statuses if mon was charging up for an attack
+ ; being fully paralyzed or hurting oneself in confusion removes charging up status
+ ; resulting in the Pokemon being invulnerable for the whole battle
+ res Invulnerable,[hl]
+PlayerCanExecuteMove:
+ call PrintMonName1Text
+ ld hl,DecrementPP
+ ld de,wPlayerSelectedMove ; pointer to the move just used
+ ld b,BANK(DecrementPP)
+ call Bankswitch
+ ld a,[wPlayerMoveEffect] ; effect of the move just used
+ ld hl,ResidualEffects1
+ ld de,1
+ call IsInArray
+ jp c,JumpMoveEffect ; ResidualEffects1 moves skip damage calculation and accuracy tests
+ ; unless executed as part of their exclusive effect functions
+ ld a,[wPlayerMoveEffect]
+ ld hl,SpecialEffectsCont
+ ld de,1
+ call IsInArray
+ call c,JumpMoveEffect ; execute the effects of SpecialEffectsCont moves (e.g. Wrap, Thrash) but don't skip anything
+PlayerCalcMoveDamage:
+ ld a,[wPlayerMoveEffect]
+ ld hl,SetDamageEffects
+ ld de,1
+ call IsInArray
+ jp c,.moveHitTest ; SetDamageEffects moves (e.g. Seismic Toss and Super Fang) skip damage calculation
+ call CriticalHitTest
+ call HandleCounterMove
+ jr z,handleIfPlayerMoveMissed
+ call GetDamageVarsForPlayerAttack
+ call CalculateDamage
+ jp z,playerCheckIfFlyOrChargeEffect ; for moves with 0 BP, skip any further damage calculation and, for now, skip MoveHitTest
+ ; for these moves, accuracy tests will only occur if they are called as part of the effect itself
+ call AdjustDamageForMoveType
+ call RandomizeDamage
+.moveHitTest
+ call MoveHitTest
+handleIfPlayerMoveMissed:
+ ld a,[wMoveMissed]
+ and a
+ jr z,getPlayerAnimationType
+ ld a,[wPlayerMoveEffect]
+ sub a,EXPLODE_EFFECT
+ jr z,playPlayerMoveAnimation ; don't play any animation if the move missed, unless it was EXPLODE_EFFECT
+ jr playerCheckIfFlyOrChargeEffect
+getPlayerAnimationType:
+ ld a,[wPlayerMoveEffect]
+ and a
+ ld a,4 ; move has no effect other than dealing damage
+ jr z,playPlayerMoveAnimation
+ ld a,5 ; move has effect
+playPlayerMoveAnimation:
+ push af
+ ld a,[wPlayerBattleStatus2]
+ bit HasSubstituteUp,a
+ ld hl,HideSubstituteShowMonAnim
+ ld b,BANK(HideSubstituteShowMonAnim)
+ call nz,Bankswitch
+ pop af
+ ld [wAnimationType],a
+ ld a,[wPlayerMoveNum]
+ call PlayMoveAnimation
+ call HandleExplodingAnimation
+ call DrawPlayerHUDAndHPBar
+ ld a,[wPlayerBattleStatus2]
+ bit HasSubstituteUp,a
+ ld hl,ReshowSubstituteAnim
+ ld b,BANK(ReshowSubstituteAnim)
+ call nz,Bankswitch
+ jr MirrorMoveCheck
+playerCheckIfFlyOrChargeEffect:
+ ld c,30
+ call DelayFrames
+ ld a,[wPlayerMoveEffect]
+ cp a,FLY_EFFECT
+ jr z,.playAnim
+ cp a,CHARGE_EFFECT
+ jr z,.playAnim
+ jr MirrorMoveCheck
+.playAnim
+ xor a
+ ld [wAnimationType],a
+ ld a,STATUS_AFFECTED_ANIM
+ call PlayMoveAnimation
+MirrorMoveCheck:
+ ld a,[wPlayerMoveEffect]
+ cp a,MIRROR_MOVE_EFFECT
+ jr nz,.metronomeCheck
+ call MirrorMoveCopyMove
+ jp z,ExecutePlayerMoveDone
+ xor a
+ ld [wMonIsDisobedient],a
+ jp CheckIfPlayerNeedsToChargeUp ; if Mirror Move was successful go back to damage calculation for copied move
+.metronomeCheck
+ cp a,METRONOME_EFFECT
+ jr nz,.next
+ call MetronomePickMove
+ jp CheckIfPlayerNeedsToChargeUp ; Go back to damage calculation for the move picked by Metronome
+.next
+ ld a,[wPlayerMoveEffect]
+ ld hl,ResidualEffects2
+ ld de,1
+ call IsInArray
+ jp c,JumpMoveEffect ; done here after executing effects of ResidualEffects2
+ ld a,[wMoveMissed]
+ and a
+ jr z,.moveDidNotMiss
+ call PrintMoveFailureText
+ ld a,[wPlayerMoveEffect]
+ cp a,EXPLODE_EFFECT ; even if Explosion or Selfdestruct missed, its effect still needs to be activated
+ jr z,.notDone
+ jp ExecutePlayerMoveDone ; otherwise, we're done if the move missed
+.moveDidNotMiss
+ call ApplyAttackToEnemyPokemon
+ call PrintCriticalOHKOText
+ callab DisplayEffectiveness
+ ld a,1
+ ld [wMoveDidntMiss],a
+.notDone
+ ld a,[wPlayerMoveEffect]
+ ld hl,AlwaysHappenSideEffects
+ ld de,1
+ call IsInArray
+ call c,JumpMoveEffect ; not done after executing effects of AlwaysHappenSideEffects
+ ld hl,wEnemyMonHP
+ ld a,[hli]
+ ld b,[hl]
+ or b
+ ret z ; don't do anything else if the enemy fainted
+ call HandleBuildingRage
+
+ ld hl,wPlayerBattleStatus1
+ bit AttackingMultipleTimes,[hl]
+ jr z,.executeOtherEffects
+ ld a,[wPlayerNumAttacksLeft]
+ dec a
+ ld [wPlayerNumAttacksLeft],a
+ jp nz,getPlayerAnimationType ; for multi-hit moves, apply attack until PlayerNumAttacksLeft hits 0 or the enemy faints.
+ ; damage calculation and accuracy tests only happen for the first hit
+ res AttackingMultipleTimes,[hl] ; clear attacking multiple times status when all attacks are over
+ ld hl,MultiHitText
+ call PrintText
+ xor a
+ ld [wPlayerNumHits],a
+.executeOtherEffects
+ ld a,[wPlayerMoveEffect]
+ and a
+ jp z,ExecutePlayerMoveDone
+ ld hl,SpecialEffects
+ ld de,1
+ call IsInArray
+ call nc,JumpMoveEffect ; move effects not included in SpecialEffects or in either of the ResidualEffect arrays,
+ ; which are the effects not covered yet. Rage effect will be executed for a second time (though it's irrelevant).
+ ; Includes side effects that only need to be called if the target didn't faint.
+ ; Responsible for executing Twineedle's second side effect (poison).
+ jp ExecutePlayerMoveDone
+
+MultiHitText:
+ TX_FAR _MultiHitText
+ db "@"
+
+ExecutePlayerMoveDone:
+ xor a
+ ld [wActionResultOrTookBattleTurn],a
+ ld b,1
+ ret
+
+PrintGhostText:
+; print the ghost battle messages
+ call IsGhostBattle
+ ret nz
+ ld a,[H_WHOSETURN]
+ and a
+ jr nz,.Ghost
+ ld a,[wBattleMonStatus] ; player’s turn
+ and a,SLP | (1 << FRZ)
+ ret nz
+ ld hl,ScaredText
+ call PrintText
+ xor a
+ ret
+.Ghost ; ghost’s turn
+ ld hl,GetOutText
+ call PrintText
+ xor a
+ ret
+
+ScaredText:
+ TX_FAR _ScaredText
+ db "@"
+
+GetOutText:
+ TX_FAR _GetOutText
+ db "@"
+
+IsGhostBattle:
+ ld a,[wIsInBattle]
+ dec a
+ ret nz
+ ld a,[wCurMap]
+ cp a,POKEMONTOWER_1
+ jr c,.next
+ cp a,LAVENDER_HOUSE_1
+ jr nc,.next
+ ld b,SILPH_SCOPE
+ call IsItemInBag
+ ret z
+.next
+ ld a,1
+ and a
+ ret
+
+; checks for various status conditions affecting the player mon
+; stores whether the mon cannot use a move this turn in Z flag
+CheckPlayerStatusConditions:
+ ld hl,wBattleMonStatus
+ ld a,[hl]
+ and a,SLP ; sleep mask
+ jr z,.FrozenCheck
+; sleeping
+ dec a
+ ld [wBattleMonStatus],a ; decrement number of turns left
+ and a
+ jr z,.WakeUp ; if the number of turns hit 0, wake up
+; fast asleep
+ xor a
+ ld [wAnimationType],a
+ ld a,SLP_ANIM - 1
+ call PlayMoveAnimation
+ ld hl,FastAsleepText
+ call PrintText
+ jr .sleepDone
+.WakeUp
+ ld hl,WokeUpText
+ call PrintText
+.sleepDone
+ xor a
+ ld [wPlayerUsedMove],a
+ ld hl,ExecutePlayerMoveDone ; player can't move this turn
+ jp .returnToHL
+
+.FrozenCheck
+ bit FRZ,[hl] ; frozen?
+ jr z,.HeldInPlaceCheck
+ ld hl,IsFrozenText
+ call PrintText
+ xor a
+ ld [wPlayerUsedMove],a
+ ld hl,ExecutePlayerMoveDone ; player can't move this turn
+ jp .returnToHL
+
+.HeldInPlaceCheck
+ ld a,[wEnemyBattleStatus1]
+ bit UsingTrappingMove,a ; is enemy using a mult-turn move like wrap?
+ jp z,.FlinchedCheck
+ ld hl,CantMoveText
+ call PrintText
+ ld hl,ExecutePlayerMoveDone ; player can't move this turn
+ jp .returnToHL
+
+.FlinchedCheck
+ ld hl,wPlayerBattleStatus1
+ bit Flinched,[hl]
+ jp z,.HyperBeamCheck
+ res Flinched,[hl] ; reset player's flinch status
+ ld hl,FlinchedText
+ call PrintText
+ ld hl,ExecutePlayerMoveDone ; player can't move this turn
+ jp .returnToHL
+
+.HyperBeamCheck
+ ld hl,wPlayerBattleStatus2
+ bit NeedsToRecharge,[hl]
+ jr z,.AnyMoveDisabledCheck
+ res NeedsToRecharge,[hl] ; reset player's recharge status
+ ld hl,MustRechargeText
+ call PrintText
+ ld hl,ExecutePlayerMoveDone ; player can't move this turn
+ jp .returnToHL
+
+.AnyMoveDisabledCheck
+ ld hl,wPlayerDisabledMove
+ ld a,[hl]
+ and a
+ jr z,.ConfusedCheck
+ dec a
+ ld [hl],a
+ and $f ; did Disable counter hit 0?
+ jr nz,.ConfusedCheck
+ ld [hl],a
+ ld [wPlayerDisabledMoveNumber],a
+ ld hl,DisabledNoMoreText
+ call PrintText
+
+.ConfusedCheck
+ ld a,[wPlayerBattleStatus1]
+ add a ; is player confused?
+ jr nc,.TriedToUseDisabledMoveCheck
+ ld hl,wPlayerConfusedCounter
+ dec [hl]
+ jr nz,.IsConfused
+ ld hl,wPlayerBattleStatus1
+ res Confused,[hl] ; if confused counter hit 0, reset confusion status
+ ld hl,ConfusedNoMoreText
+ call PrintText
+ jr .TriedToUseDisabledMoveCheck
+.IsConfused
+ ld hl,IsConfusedText
+ call PrintText
+ xor a
+ ld [wAnimationType],a
+ ld a,CONF_ANIM - 1
+ call PlayMoveAnimation
+ call BattleRandom
+ cp a,$80 ; 50% chance to hurt itself
+ jr c,.TriedToUseDisabledMoveCheck
+ ld hl,wPlayerBattleStatus1
+ ld a,[hl]
+ and a, 1 << Confused ; if mon hurts itself, clear every other status from wPlayerBattleStatus1
+ ld [hl],a
+ call HandleSelfConfusionDamage
+ jr .MonHurtItselfOrFullyParalysed
+
+.TriedToUseDisabledMoveCheck
+; prevents a disabled move that was selected before being disabled from being used
+ ld a,[wPlayerDisabledMoveNumber]
+ and a
+ jr z,.ParalysisCheck
+ ld hl,wPlayerSelectedMove
+ cp [hl]
+ jr nz,.ParalysisCheck
+ call PrintMoveIsDisabledText
+ ld hl,ExecutePlayerMoveDone ; if a disabled move was somehow selected, player can't move this turn
+ jp .returnToHL
+
+.ParalysisCheck
+ ld hl,wBattleMonStatus
+ bit PAR,[hl]
+ jr z,.BideCheck
+ call BattleRandom
+ cp a,$3F ; 25% to be fully paralyzed
+ jr nc,.BideCheck
+ ld hl,FullyParalyzedText
+ call PrintText
+
+.MonHurtItselfOrFullyParalysed
+ ld hl,wPlayerBattleStatus1
+ ld a,[hl]
+ ; clear bide, thrashing, charging up, and trapping moves such as warp (already cleared for confusion damage)
+ and $ff ^ ((1 << StoringEnergy) | (1 << ThrashingAbout) | (1 << ChargingUp) | (1 << UsingTrappingMove))
+ ld [hl],a
+ ld a,[wPlayerMoveEffect]
+ cp a,FLY_EFFECT
+ jr z,.FlyOrChargeEffect
+ cp a,CHARGE_EFFECT
+ jr z,.FlyOrChargeEffect
+ jr .NotFlyOrChargeEffect
+
+.FlyOrChargeEffect
+ xor a
+ ld [wAnimationType],a
+ ld a,STATUS_AFFECTED_ANIM
+ call PlayMoveAnimation
+.NotFlyOrChargeEffect
+ ld hl,ExecutePlayerMoveDone
+ jp .returnToHL ; if using a two-turn move, we need to recharge the first turn
+
+.BideCheck
+ ld hl,wPlayerBattleStatus1
+ bit StoringEnergy,[hl] ; is mon using bide?
+ jr z,.ThrashingAboutCheck
+ xor a
+ ld [wPlayerMoveNum],a
+ ld hl,wDamage
+ ld a,[hli]
+ ld b,a
+ ld c,[hl]
+ ld hl,wPlayerBideAccumulatedDamage + 1
+ ld a,[hl]
+ add c ; accumulate damage taken
+ ld [hld],a
+ ld a,[hl]
+ adc b
+ ld [hl],a
+ ld hl,wPlayerNumAttacksLeft
+ dec [hl] ; did Bide counter hit 0?
+ jr z,.UnleashEnergy
+ ld hl,ExecutePlayerMoveDone
+ jp .returnToHL ; unless mon unleashes energy, can't move this turn
+.UnleashEnergy
+ ld hl,wPlayerBattleStatus1
+ res StoringEnergy,[hl] ; not using bide any more
+ ld hl,UnleashedEnergyText
+ call PrintText
+ ld a,1
+ ld [wPlayerMovePower],a
+ ld hl,wPlayerBideAccumulatedDamage + 1
+ ld a,[hld]
+ add a
+ ld b,a
+ ld [wDamage + 1],a
+ ld a,[hl]
+ rl a ; double the damage
+ ld [wDamage],a
+ or b
+ jr nz,.next
+ ld a,1
+ ld [wMoveMissed],a
+.next
+ xor a
+ ld [hli],a
+ ld [hl],a
+ ld a,BIDE
+ ld [wPlayerMoveNum],a
+ ld hl,handleIfPlayerMoveMissed ; skip damage calculation, DecrementPP and MoveHitTest
+ jp .returnToHL
+
+.ThrashingAboutCheck
+ bit ThrashingAbout,[hl] ; is mon using thrash or petal dance?
+ jr z,.MultiturnMoveCheck
+ ld a,THRASH
+ ld [wPlayerMoveNum],a
+ ld hl,ThrashingAboutText
+ call PrintText
+ ld hl,wPlayerNumAttacksLeft
+ dec [hl] ; did Thrashing About counter hit 0?
+ ld hl,PlayerCalcMoveDamage ; skip DecrementPP
+ jp nz,.returnToHL
+ push hl
+ ld hl,wPlayerBattleStatus1
+ res ThrashingAbout,[hl] ; no longer thrashing about
+ set Confused,[hl] ; confused
+ call BattleRandom
+ and a,3
+ inc a
+ inc a ; confused for 2-5 turns
+ ld [wPlayerConfusedCounter],a
+ pop hl ; skip DecrementPP
+ jp .returnToHL
+
+.MultiturnMoveCheck
+ bit UsingTrappingMove,[hl] ; is mon using multi-turn move?
+ jp z,.RageCheck
+ ld hl,AttackContinuesText
+ call PrintText
+ ld a,[wPlayerNumAttacksLeft]
+ dec a ; did multi-turn move end?
+ ld [wPlayerNumAttacksLeft],a
+ ld hl,getPlayerAnimationType ; if it didn't, skip damage calculation (deal damage equal to last hit),
+ ; DecrementPP and MoveHitTest
+ jp nz,.returnToHL
+ jp .returnToHL
+
+.RageCheck
+ ld a, [wPlayerBattleStatus2]
+ bit UsingRage, a ; is mon using rage?
+ jp z, .checkPlayerStatusConditionsDone ; if we made it this far, mon can move normally this turn
+ ld a, RAGE
+ ld [wd11e], a
+ call GetMoveName
+ call CopyStringToCF50
+ xor a
+ ld [wPlayerMoveEffect], a
+ ld hl, PlayerCanExecuteMove
+ jp .returnToHL
+
+.returnToHL
+ xor a
+ ret
+
+.checkPlayerStatusConditionsDone
+ ld a, $1
+ and a
+ ret
+
+FastAsleepText:
+ TX_FAR _FastAsleepText
+ db "@"
+
+WokeUpText:
+ TX_FAR _WokeUpText
+ db "@"
+
+IsFrozenText:
+ TX_FAR _IsFrozenText
+ db "@"
+
+FullyParalyzedText:
+ TX_FAR _FullyParalyzedText
+ db "@"
+
+FlinchedText:
+ TX_FAR _FlinchedText
+ db "@"
+
+MustRechargeText:
+ TX_FAR _MustRechargeText
+ db "@"
+
+DisabledNoMoreText:
+ TX_FAR _DisabledNoMoreText
+ db "@"
+
+IsConfusedText:
+ TX_FAR _IsConfusedText
+ db "@"
+
+HurtItselfText:
+ TX_FAR _HurtItselfText
+ db "@"
+
+ConfusedNoMoreText:
+ TX_FAR _ConfusedNoMoreText
+ db "@"
+
+SavingEnergyText:
+ TX_FAR _SavingEnergyText
+ db "@"
+
+UnleashedEnergyText:
+ TX_FAR _UnleashedEnergyText
+ db "@"
+
+ThrashingAboutText:
+ TX_FAR _ThrashingAboutText
+ db "@"
+
+AttackContinuesText:
+ TX_FAR _AttackContinuesText
+ db "@"
+
+CantMoveText:
+ TX_FAR _CantMoveText
+ db "@"
+
+PrintMoveIsDisabledText:
+ ld hl, wPlayerSelectedMove
+ ld de, wPlayerBattleStatus1
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .removeChargingUp
+ inc hl
+ ld de, wEnemyBattleStatus1
+.removeChargingUp
+ ld a, [de]
+ res ChargingUp, a ; end the pokemon's
+ ld [de], a
+ ld a, [hl]
+ ld [wd11e], a
+ call GetMoveName
+ ld hl, MoveIsDisabledText
+ jp PrintText
+
+MoveIsDisabledText:
+ TX_FAR _MoveIsDisabledText
+ db "@"
+
+HandleSelfConfusionDamage:
+ ld hl, HurtItselfText
+ call PrintText
+ ld hl, wEnemyMonDefense
+ ld a, [hli]
+ push af
+ ld a, [hld]
+ push af
+ ld a, [wBattleMonDefense]
+ ld [hli], a
+ ld a, [wBattleMonDefense + 1]
+ ld [hl], a
+ ld hl, wPlayerMoveEffect
+ push hl
+ ld a, [hl]
+ push af
+ xor a
+ ld [hli], a
+ ld [wCriticalHitOrOHKO], a ; self-inflicted confusion damage can't be a Critical Hit
+ ld a, 40 ; 40 base power
+ ld [hli], a
+ xor a
+ ld [hl], a
+ call GetDamageVarsForPlayerAttack
+ call CalculateDamage ; ignores AdjustDamageForMoveType (type-less damage), RandomizeDamage,
+ ; and MoveHitTest (always hits)
+ pop af
+ pop hl
+ ld [hl], a
+ ld hl, wEnemyMonDefense + 1
+ pop af
+ ld [hld], a
+ pop af
+ ld [hl], a
+ xor a
+ ld [wAnimationType], a
+ inc a
+ ld [H_WHOSETURN], a
+ call PlayMoveAnimation
+ call DrawPlayerHUDAndHPBar
+ xor a
+ ld [H_WHOSETURN], a
+ jp ApplyDamageToPlayerPokemon
+
+PrintMonName1Text:
+ ld hl, MonName1Text
+ jp PrintText
+
+; this function wastes time calling DetermineExclamationPointTextNum
+; and choosing between Used1Text and Used2Text, even though
+; those text strings are identical and both continue at PrintInsteadText
+; this likely had to do with Japanese grammar that got translated,
+; but the functionality didn't get removed
+MonName1Text:
+ TX_FAR _MonName1Text
+ TX_ASM
+ ld a, [H_WHOSETURN]
+ and a
+ ld a, [wPlayerMoveNum]
+ ld hl, wPlayerUsedMove
+ jr z, .playerTurn
+ ld a, [wEnemyMoveNum]
+ ld hl, wEnemyUsedMove
+.playerTurn
+ ld [hl], a
+ ld [wd11e], a
+ call DetermineExclamationPointTextNum
+ ld a, [wMonIsDisobedient]
+ and a
+ ld hl, Used2Text
+ ret nz
+ ld a, [wd11e]
+ cp 3
+ ld hl, Used2Text
+ ret c
+ ld hl, Used1Text
+ ret
+
+Used1Text:
+ TX_FAR _Used1Text
+ TX_ASM
+ jr PrintInsteadText
+
+Used2Text:
+ TX_FAR _Used2Text
+ TX_ASM
+ ; fall through
+
+PrintInsteadText:
+ ld a, [wMonIsDisobedient]
+ and a
+ jr z, PrintMoveName
+ ld hl, InsteadText
+ ret
+
+InsteadText:
+ TX_FAR _InsteadText
+ TX_ASM
+ ; fall through
+
+PrintMoveName:
+ ld hl, _PrintMoveName
+ ret
+
+_PrintMoveName:
+ TX_FAR _CF50Text
+ TX_ASM
+ ld hl, ExclamationPointPointerTable
+ ld a, [wd11e] ; exclamation point num
+ add a
+ push bc
+ ld b, $0
+ ld c, a
+ add hl, bc
+ pop bc
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ret
+
+ExclamationPointPointerTable:
+ dw ExclamationPoint1Text
+ dw ExclamationPoint2Text
+ dw ExclamationPoint3Text
+ dw ExclamationPoint4Text
+ dw ExclamationPoint5Text
+
+ExclamationPoint1Text:
+ TX_FAR _ExclamationPoint1Text
+ db "@"
+
+ExclamationPoint2Text:
+ TX_FAR _ExclamationPoint2Text
+ db "@"
+
+ExclamationPoint3Text:
+ TX_FAR _ExclamationPoint3Text
+ db "@"
+
+ExclamationPoint4Text:
+ TX_FAR _ExclamationPoint4Text
+ db "@"
+
+ExclamationPoint5Text:
+ TX_FAR _ExclamationPoint5Text
+ db "@"
+
+; this function does nothing useful
+; if the move being used is in set [1-4] from ExclamationPointMoveSets,
+; use ExclamationPoint[1-4]Text
+; otherwise, use ExclamationPoint5Text
+; but all five text strings are identical
+; this likely had to do with Japanese grammar that got translated,
+; but the functionality didn't get removed
+DetermineExclamationPointTextNum:
+ push bc
+ ld a, [wd11e] ; move ID
+ ld c, a
+ ld b, $0
+ ld hl, ExclamationPointMoveSets
+.loop
+ ld a, [hli]
+ cp $ff
+ jr z, .done
+ cp c
+ jr z, .done
+ and a
+ jr nz, .loop
+ inc b
+ jr .loop
+.done
+ ld a, b
+ ld [wd11e], a ; exclamation point num
+ pop bc
+ ret
+
+ExclamationPointMoveSets:
+ db SWORDS_DANCE, GROWTH
+ db $00
+ db RECOVER, BIDE, SELFDESTRUCT, AMNESIA
+ db $00
+ db MEDITATE, AGILITY, TELEPORT, MIMIC, DOUBLE_TEAM, BARRAGE
+ db $00
+ db POUND, SCRATCH, VICEGRIP, WING_ATTACK, FLY, BIND, SLAM, HORN_ATTACK, BODY_SLAM
+ db WRAP, THRASH, TAIL_WHIP, LEER, BITE, GROWL, ROAR, SING, PECK, COUNTER
+ db STRENGTH, ABSORB, STRING_SHOT, EARTHQUAKE, FISSURE, DIG, TOXIC, SCREECH, HARDEN
+ db MINIMIZE, WITHDRAW, DEFENSE_CURL, METRONOME, LICK, CLAMP, CONSTRICT, POISON_GAS
+ db LEECH_LIFE, BUBBLE, FLASH, SPLASH, ACID_ARMOR, FURY_SWIPES, REST, SHARPEN, SLASH, SUBSTITUTE
+ db $00
+ db $FF ; terminator
+
+PrintMoveFailureText:
+ ld de, wPlayerMoveEffect
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .playersTurn
+ ld de, wEnemyMoveEffect
+.playersTurn
+ ld hl, DoesntAffectMonText
+ ld a, [wDamageMultipliers]
+ and $7f
+ jr z, .gotTextToPrint
+ ld hl, AttackMissedText
+ ld a, [wCriticalHitOrOHKO]
+ cp $ff
+ jr nz, .gotTextToPrint
+ ld hl, UnaffectedText
+.gotTextToPrint
+ push de
+ call PrintText
+ xor a
+ ld [wCriticalHitOrOHKO], a
+ pop de
+ ld a, [de]
+ cp JUMP_KICK_EFFECT
+ ret nz
+
+ ; if you get here, the mon used jump kick or hi jump kick and missed
+ ld hl, wDamage ; since the move missed, wDamage will always contain 0 at this point.
+ ; Thus, recoil damage will always be equal to 1
+ ; even if it was intended to be potential damage/8.
+ ld a, [hli]
+ ld b, [hl]
+ srl a
+ rr b
+ srl a
+ rr b
+ srl a
+ rr b
+ ld [hl], b
+ dec hl
+ ld [hli], a
+ or b
+ jr nz, .applyRecoil
+ inc a
+ ld [hl], a
+.applyRecoil
+ ld hl, KeptGoingAndCrashedText
+ call PrintText
+ ld b, $4
+ predef PredefShakeScreenHorizontally
+ ld a, [H_WHOSETURN]
+ and a
+ jr nz, .enemyTurn
+ jp ApplyDamageToPlayerPokemon
+.enemyTurn
+ jp ApplyDamageToEnemyPokemon
+
+AttackMissedText:
+ TX_FAR _AttackMissedText
+ db "@"
+
+KeptGoingAndCrashedText:
+ TX_FAR _KeptGoingAndCrashedText
+ db "@"
+
+UnaffectedText:
+ TX_FAR _UnaffectedText
+ db "@"
+
+PrintDoesntAffectText:
+ ld hl, DoesntAffectMonText
+ jp PrintText
+
+DoesntAffectMonText:
+ TX_FAR _DoesntAffectMonText
+ db "@"
+
+; if there was a critical hit or an OHKO was successful, print the corresponding text
+PrintCriticalOHKOText:
+ ld a, [wCriticalHitOrOHKO]
+ and a
+ jr z, .done ; do nothing if there was no critical hit or successful OHKO
+ dec a
+ add a
+ ld hl, CriticalOHKOTextPointers
+ ld b, $0
+ ld c, a
+ add hl, bc
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ call PrintText
+ xor a
+ ld [wCriticalHitOrOHKO], a
+.done
+ ld c, 20
+ jp DelayFrames
+
+CriticalOHKOTextPointers:
+ dw CriticalHitText
+ dw OHKOText
+
+CriticalHitText:
+ TX_FAR _CriticalHitText
+ db "@"
+
+OHKOText:
+ TX_FAR _OHKOText
+ db "@"
+
+; checks if a traded mon will disobey due to lack of badges
+; stores whether the mon will use a move in Z flag
+CheckForDisobedience:
+ xor a
+ ld [wMonIsDisobedient], a
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ jr nz, .checkIfMonIsTraded
+ ld a, $1
+ and a
+ ret
+; compare the mon's original trainer ID with the player's ID to see if it was traded
+.checkIfMonIsTraded
+ ld hl, wPartyMon1OTID
+ ld bc, wPartyMon2 - wPartyMon1
+ ld a, [wPlayerMonNumber]
+ call AddNTimes
+ ld a, [wPlayerID]
+ cp [hl]
+ jr nz, .monIsTraded
+ inc hl
+ ld a, [wPlayerID + 1]
+ cp [hl]
+ jp z, .canUseMove
+; it was traded
+.monIsTraded
+; what level might disobey?
+ ld hl, wObtainedBadges
+ bit 7, [hl]
+ ld a, 101
+ jr nz, .next
+ bit 5, [hl]
+ ld a, 70
+ jr nz, .next
+ bit 3, [hl]
+ ld a, 50
+ jr nz, .next
+ bit 1, [hl]
+ ld a, 30
+ jr nz, .next
+ ld a, 10
+.next
+ ld b, a
+ ld c, a
+ ld a, [wBattleMonLevel]
+ ld d, a
+ add b
+ ld b, a
+ jr nc, .noCarry
+ ld b, $ff ; cap b at $ff
+.noCarry
+ ld a, c
+ cp d
+ jp nc, .canUseMove
+.loop1
+ call BattleRandom
+ swap a
+ cp b
+ jr nc, .loop1
+ cp c
+ jp c, .canUseMove
+.loop2
+ call BattleRandom
+ cp b
+ jr nc, .loop2
+ cp c
+ jr c, .useRandomMove
+ ld a, d
+ sub c
+ ld b, a
+ call BattleRandom
+ swap a
+ sub b
+ jr c, .monNaps
+ cp b
+ jr nc, .monDoesNothing
+ ld hl, WontObeyText
+ call PrintText
+ call HandleSelfConfusionDamage
+ jp .cannotUseMove
+.monNaps
+ call BattleRandom
+ add a
+ swap a
+ and SLP ; sleep mask
+ jr z, .monNaps ; keep trying until we get at least 1 turn of sleep
+ ld [wBattleMonStatus], a
+ ld hl, BeganToNapText
+ jr .printText
+.monDoesNothing
+ call BattleRandom
+ and $3
+ ld hl, LoafingAroundText
+ and a
+ jr z, .printText
+ ld hl, WontObeyText
+ dec a
+ jr z, .printText
+ ld hl, TurnedAwayText
+ dec a
+ jr z, .printText
+ ld hl, IgnoredOrdersText
+.printText
+ call PrintText
+ jr .cannotUseMove
+.useRandomMove
+ ld a, [wBattleMonMoves + 1]
+ and a ; is the second move slot empty?
+ jr z, .monDoesNothing ; mon will not use move if it only knows one move
+ ld a, [wPlayerDisabledMoveNumber]
+ and a
+ jr nz, .monDoesNothing
+ ld a, [wPlayerSelectedMove]
+ cp STRUGGLE
+ jr z, .monDoesNothing ; mon will not use move if struggling
+; check if only one move has remaining PP
+ ld hl, wBattleMonPP
+ push hl
+ ld a, [hli]
+ and $3f
+ ld b, a
+ ld a, [hli]
+ and $3f
+ add b
+ ld b, a
+ ld a, [hli]
+ and $3f
+ add b
+ ld b, a
+ ld a, [hl]
+ and $3f
+ add b
+ pop hl
+ push af
+ ld a, [wCurrentMenuItem]
+ ld c, a
+ ld b, $0
+ add hl, bc
+ ld a, [hl]
+ and $3f
+ ld b, a
+ pop af
+ cp b
+ jr z, .monDoesNothing ; mon will not use move if only one move has remaining PP
+ ld a, $1
+ ld [wMonIsDisobedient], a
+ ld a, [wMaxMenuItem]
+ ld b, a
+ ld a, [wCurrentMenuItem]
+ ld c, a
+.chooseMove
+ call BattleRandom
+ and $3
+ cp b
+ jr nc, .chooseMove ; if the random number is greater than the move count, choose another
+ cp c
+ jr z, .chooseMove ; if the random number matches the move the player selected, choose another
+ ld [wCurrentMenuItem], a
+ ld hl, wBattleMonPP
+ ld e, a
+ ld d, $0
+ add hl, de
+ ld a, [hl]
+ and a ; does the move have any PP left?
+ jr z, .chooseMove ; if the move has no PP left, choose another
+ ld a, [wCurrentMenuItem]
+ ld c, a
+ ld b, $0
+ ld hl, wBattleMonMoves
+ add hl, bc
+ ld a, [hl]
+ ld [wPlayerSelectedMove], a
+ call GetCurrentMove
+.canUseMove
+ ld a, $1
+ and a; clear Z flag
+ ret
+.cannotUseMove
+ xor a ; set Z flag
+ ret
+
+LoafingAroundText:
+ TX_FAR _LoafingAroundText
+ db "@"
+
+BeganToNapText:
+ TX_FAR _BeganToNapText
+ db "@"
+
+WontObeyText:
+ TX_FAR _WontObeyText
+ db "@"
+
+TurnedAwayText:
+ TX_FAR _TurnedAwayText
+ db "@"
+
+IgnoredOrdersText:
+ TX_FAR _IgnoredOrdersText
+ db "@"
+
+; sets b, c, d, and e for the CalculateDamage routine in the case of an attack by the player mon
+GetDamageVarsForPlayerAttack:
+ xor a
+ ld hl, wDamage ; damage to eventually inflict, initialise to zero
+ ldi [hl], a
+ ld [hl], a
+ ld hl, wPlayerMovePower
+ ld a, [hli]
+ and a
+ ld d, a ; d = move power
+ ret z ; return if move power is zero
+ ld a, [hl] ; a = [wPlayerMoveType]
+ cp FIRE ; types >= FIRE are all special
+ jr nc, .specialAttack
+.physicalAttack
+ ld hl, wEnemyMonDefense
+ ld a, [hli]
+ ld b, a
+ ld c, [hl] ; bc = enemy defense
+ ld a, [wEnemyBattleStatus3]
+ bit HasReflectUp, a ; check for Reflect
+ jr z, .physicalAttackCritCheck
+; if the enemy has used Reflect, double the enemy's defense
+ sla c
+ rl b
+.physicalAttackCritCheck
+ ld hl, wBattleMonAttack
+ ld a, [wCriticalHitOrOHKO]
+ and a ; check for critical hit
+ jr z, .scaleStats
+; in the case of a critical hit, reset the player's attack and the enemy's defense to their base values
+ ld c, 3 ; defense stat
+ call GetEnemyMonStat
+ ld a, [H_PRODUCT + 2]
+ ld b, a
+ ld a, [H_PRODUCT + 3]
+ ld c, a
+ push bc
+ ld hl, wPartyMon1Attack
+ ld a, [wPlayerMonNumber]
+ ld bc, wPartyMon2 - wPartyMon1
+ call AddNTimes
+ pop bc
+ jr .scaleStats
+.specialAttack
+ ld hl, wEnemyMonSpecial
+ ld a, [hli]
+ ld b, a
+ ld c, [hl] ; bc = enemy special
+ ld a, [wEnemyBattleStatus3]
+ bit HasLightScreenUp, a ; check for Light Screen
+ jr z, .specialAttackCritCheck
+; if the enemy has used Light Screen, double the enemy's special
+ sla c
+ rl b
+; reflect and light screen boosts do not cap the stat at 999, so weird things will happen during stats scaling if
+; a Pokemon with 512 or more Defense has used Reflect, or if a Pokemon with 512 or more Special has used Light Screen
+.specialAttackCritCheck
+ ld hl, wBattleMonSpecial
+ ld a, [wCriticalHitOrOHKO]
+ and a ; check for critical hit
+ jr z, .scaleStats
+; in the case of a critical hit, reset the player's and enemy's specials to their base values
+ ld c, 5 ; special stat
+ call GetEnemyMonStat
+ ld a, [H_PRODUCT + 2]
+ ld b, a
+ ld a, [H_PRODUCT + 3]
+ ld c, a
+ push bc
+ ld hl, wPartyMon1Special
+ ld a, [wPlayerMonNumber]
+ ld bc, wPartyMon2 - wPartyMon1
+ call AddNTimes
+ pop bc
+; if either the offensive or defensive stat is too large to store in a byte, scale both stats by dividing them by 4
+; this allows values with up to 10 bits (values up to 1023) to be handled
+; anything larger will wrap around
+.scaleStats
+ ld a, [hli]
+ ld l, [hl]
+ ld h, a ; hl = player's offensive stat
+ or b ; is either high byte nonzero?
+ jr z, .next ; if not, we don't need to scale
+; bc /= 4 (scale enemy's defensive stat)
+ srl b
+ rr c
+ srl b
+ rr c
+; defensive stat can actually end up as 0, leading to a division by 0 freeze during damage calculation
+; hl /= 4 (scale player's offensive stat)
+ srl h
+ rr l
+ srl h
+ rr l
+ ld a, l
+ or h ; is the player's offensive stat 0?
+ jr nz, .next
+ inc l ; if the player's offensive stat is 0, bump it up to 1
+.next
+ ld b, l ; b = player's offensive stat (possibly scaled)
+ ; (c already contains enemy's defensive stat (possibly scaled))
+ ld a, [wBattleMonLevel]
+ ld e, a ; e = level
+ ld a, [wCriticalHitOrOHKO]
+ and a ; check for critical hit
+ jr z, .done
+ sla e ; double level if it was a critical hit
+.done
+ ld a, 1
+ and a
+ ret
+
+; sets b, c, d, and e for the CalculateDamage routine in the case of an attack by the enemy mon
+GetDamageVarsForEnemyAttack:
+ ld hl, wDamage ; damage to eventually inflict, initialise to zero
+ xor a
+ ld [hli], a
+ ld [hl], a
+ ld hl, wEnemyMovePower
+ ld a, [hli]
+ ld d, a ; d = move power
+ and a
+ ret z ; return if move power is zero
+ ld a, [hl] ; a = [wEnemyMoveType]
+ cp FIRE ; types >= FIRE are all special
+ jr nc, .specialAttack
+.physicalAttack
+ ld hl, wBattleMonDefense
+ ld a, [hli]
+ ld b, a
+ ld c, [hl] ; bc = player defense
+ ld a, [wPlayerBattleStatus3]
+ bit HasReflectUp, a ; check for Reflect
+ jr z, .physicalAttackCritCheck
+; if the player has used Reflect, double the player's defense
+ sla c
+ rl b
+.physicalAttackCritCheck
+ ld hl, wEnemyMonAttack
+ ld a, [wCriticalHitOrOHKO]
+ and a ; check for critical hit
+ jr z, .scaleStats
+; in the case of a critical hit, reset the player's defense and the enemy's attack to their base values
+ ld hl, wPartyMon1Defense
+ ld a, [wPlayerMonNumber]
+ ld bc, wPartyMon2 - wPartyMon1
+ call AddNTimes
+ ld a, [hli]
+ ld b, a
+ ld c, [hl]
+ push bc
+ ld c, 2 ; attack stat
+ call GetEnemyMonStat
+ ld hl, H_PRODUCT + 2
+ pop bc
+ jr .scaleStats
+.specialAttack
+ ld hl, wBattleMonSpecial
+ ld a, [hli]
+ ld b, a
+ ld c, [hl]
+ ld a, [wPlayerBattleStatus3]
+ bit HasLightScreenUp, a ; check for Light Screen
+ jr z, .specialAttackCritCheck
+; if the player has used Light Screen, double the player's special
+ sla c
+ rl b
+; reflect and light screen boosts do not cap the stat at 999, so weird things will happen during stats scaling if
+; a Pokemon with 512 or more Defense has used Reflect, or if a Pokemon with 512 or more Special has used Light Screen
+.specialAttackCritCheck
+ ld hl, wEnemyMonSpecial
+ ld a, [wCriticalHitOrOHKO]
+ and a ; check for critical hit
+ jr z, .scaleStats
+; in the case of a critical hit, reset the player's and enemy's specials to their base values
+ ld hl, wPartyMon1Special
+ ld a, [wPlayerMonNumber]
+ ld bc, wPartyMon2 - wPartyMon1
+ call AddNTimes
+ ld a, [hli]
+ ld b, a
+ ld c, [hl]
+ push bc
+ ld c, 5 ; special stat
+ call GetEnemyMonStat
+ ld hl, H_PRODUCT + 2
+ pop bc
+; if either the offensive or defensive stat is too large to store in a byte, scale both stats by dividing them by 4
+; this allows values with up to 10 bits (values up to 1023) to be handled
+; anything larger will wrap around
+.scaleStats
+ ld a, [hli]
+ ld l, [hl]
+ ld h, a ; hl = enemy's offensive stat
+ or b ; is either high byte nonzero?
+ jr z, .next ; if not, we don't need to scale
+; bc /= 4 (scale player's defensive stat)
+ srl b
+ rr c
+ srl b
+ rr c
+; defensive stat can actually end up as 0, leading to a division by 0 freeze during damage calculation
+; hl /= 4 (scale enemy's offensive stat)
+ srl h
+ rr l
+ srl h
+ rr l
+ ld a, l
+ or h ; is the enemy's offensive stat 0?
+ jr nz, .next
+ inc l ; if the enemy's offensive stat is 0, bump it up to 1
+.next
+ ld b, l ; b = enemy's offensive stat (possibly scaled)
+ ; (c already contains player's defensive stat (possibly scaled))
+ ld a, [wEnemyMonLevel]
+ ld e, a
+ ld a, [wCriticalHitOrOHKO]
+ and a ; check for critical hit
+ jr z, .done
+ sla e ; double level if it was a critical hit
+.done
+ ld a, $1
+ and a
+ and a
+ ret
+
+; get stat c of enemy mon
+; c: stat to get (HP=1,Attack=2,Defense=3,Speed=4,Special=5)
+GetEnemyMonStat:
+ push de
+ push bc
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ jr nz, .notLinkBattle
+ ld hl, wEnemyMon1Stats
+ dec c
+ sla c
+ ld b, $0
+ add hl, bc
+ ld a, [wEnemyMonPartyPos]
+ ld bc, wEnemyMon2 - wEnemyMon1
+ call AddNTimes
+ ld a, [hli]
+ ld [H_MULTIPLICAND + 1], a
+ ld a, [hl]
+ ld [H_MULTIPLICAND + 2], a
+ pop bc
+ pop de
+ ret
+.notLinkBattle
+ ld a, [wEnemyMonLevel]
+ ld [wCurEnemyLVL], a
+ ld a, [wEnemyMonSpecies]
+ ld [wd0b5], a
+ call GetMonHeader
+ ld hl, wEnemyMonDVs
+ ld de, wLoadedMonSpeedExp
+ ld a, [hli]
+ ld [de], a
+ inc de
+ ld a, [hl]
+ ld [de], a
+ pop bc
+ ld b, $0
+ ld hl, wLoadedMonSpeedExp - $b ; this base address makes CalcStat look in [wLoadedMonSpeedExp] for DVs
+ call CalcStat
+ pop de
+ ret
+
+CalculateDamage:
+; input:
+; b: attack
+; c: opponent defense
+; d: base power
+; e: level
+
+ ld a, [H_WHOSETURN] ; whose turn?
+ and a
+ ld a, [wPlayerMoveEffect]
+ jr z, .effect
+ ld a, [wEnemyMoveEffect]
+.effect
+
+; EXPLODE_EFFECT halves defense.
+ cp a, EXPLODE_EFFECT
+ jr nz, .ok
+ srl c
+ jr nz, .ok
+ inc c ; ...with a minimum value of 1 (used as a divisor later on)
+.ok
+
+; Multi-hit attacks may or may not have 0 bp.
+ cp a, TWO_TO_FIVE_ATTACKS_EFFECT
+ jr z, .skipbp
+ cp a, $1e
+ jr z, .skipbp
+
+; Calculate OHKO damage based on remaining HP.
+ cp a, OHKO_EFFECT
+ jp z, JumpToOHKOMoveEffect
+
+; Don't calculate damage for moves that don't do any.
+ ld a, d ; base power
+ and a
+ ret z
+.skipbp
+
+ xor a
+ ld hl, H_DIVIDEND
+ ldi [hl], a
+ ldi [hl], a
+ ld [hl], a
+
+; Multiply level by 2
+ ld a, e ; level
+ add a
+ jr nc, .nc
+ push af
+ ld a, 1
+ ld [hl], a
+ pop af
+.nc
+ inc hl
+ ldi [hl], a
+
+; Divide by 5
+ ld a, 5
+ ldd [hl], a
+ push bc
+ ld b, 4
+ call Divide
+ pop bc
+
+; Add 2
+ inc [hl]
+ inc [hl]
+
+ inc hl ; multiplier
+
+; Multiply by attack base power
+ ld [hl], d
+ call Multiply
+
+; Multiply by attack stat
+ ld [hl], b
+ call Multiply
+
+; Divide by defender's defense stat
+ ld [hl], c
+ ld b, 4
+ call Divide
+
+; Divide by 50
+ ld [hl], 50
+ ld b, 4
+ call Divide
+
+ ld hl, wDamage
+ ld b, [hl]
+ ld a, [H_QUOTIENT + 3]
+ add b
+ ld [H_QUOTIENT + 3], a
+ jr nc, .asm_3dfd0
+
+ ld a, [H_QUOTIENT + 2]
+ inc a
+ ld [H_QUOTIENT + 2], a
+ and a
+ jr z, .asm_3e004
+
+.asm_3dfd0
+ ld a, [H_QUOTIENT]
+ ld b, a
+ ld a, [H_QUOTIENT + 1]
+ or a
+ jr nz, .asm_3e004
+
+ ld a, [H_QUOTIENT + 2]
+ cp 998 / $100
+ jr c, .asm_3dfe8
+ cp 998 / $100 + 1
+ jr nc, .asm_3e004
+ ld a, [H_QUOTIENT + 3]
+ cp 998 % $100
+ jr nc, .asm_3e004
+
+.asm_3dfe8
+ inc hl
+ ld a, [H_QUOTIENT + 3]
+ ld b, [hl]
+ add b
+ ld [hld], a
+
+ ld a, [H_QUOTIENT + 2]
+ ld b, [hl]
+ adc b
+ ld [hl], a
+ jr c, .asm_3e004
+
+ ld a, [hl]
+ cp 998 / $100
+ jr c, .asm_3e00a
+ cp 998 / $100 + 1
+ jr nc, .asm_3e004
+ inc hl
+ ld a, [hld]
+ cp 998 % $100
+ jr c, .asm_3e00a
+
+.asm_3e004
+; cap at 997
+ ld a, 997 / $100
+ ld [hli], a
+ ld a, 997 % $100
+ ld [hld], a
+
+.asm_3e00a
+; add 2
+ inc hl
+ ld a, [hl]
+ add 2
+ ld [hld], a
+ jr nc, .done
+ inc [hl]
+
+.done
+; minimum damage is 1
+ ld a, 1
+ and a
+ ret
+
+JumpToOHKOMoveEffect:
+ call JumpMoveEffect
+ ld a, [wMoveMissed]
+ dec a
+ ret
+
+
+UnusedHighCriticalMoves:
+ db KARATE_CHOP
+ db RAZOR_LEAF
+ db CRABHAMMER
+ db SLASH
+ db $FF
+
+; determines if attack is a critical hit
+; azure heights claims "the fastest pokémon (who are,not coincidentally,
+; among the most popular) tend to CH about 20 to 25% of the time."
+CriticalHitTest:
+ xor a
+ ld [wCriticalHitOrOHKO], a
+ ld a, [H_WHOSETURN]
+ and a
+ ld a, [wEnemyMonSpecies]
+ jr nz, .handleEnemy
+ ld a, [wBattleMonSpecies]
+.handleEnemy
+ ld [wd0b5], a
+ call GetMonHeader
+ ld a, [wMonHBaseSpeed]
+ ld b, a
+ srl b ; (effective (base speed/2))
+ ld a, [H_WHOSETURN]
+ and a
+ ld hl, wPlayerMovePower
+ ld de, wPlayerBattleStatus2
+ jr z, .calcCriticalHitProbability
+ ld hl, wEnemyMovePower
+ ld de, wEnemyBattleStatus2
+.calcCriticalHitProbability
+ ld a, [hld] ; read base power from RAM
+ and a
+ ret z ; do nothing if zero
+ dec hl
+ ld c, [hl] ; read move id
+ ld a, [de]
+ bit GettingPumped, a ; test for focus energy
+ jr nz, .focusEnergyUsed ; bug: using focus energy causes a shift to the right instead of left,
+ ; resulting in 1/4 the usual crit chance
+ sla b ; (effective (base speed/2)*2)
+ jr nc, .noFocusEnergyUsed
+ ld b, $ff ; cap at 255/256
+ jr .noFocusEnergyUsed
+.focusEnergyUsed
+ srl b
+.noFocusEnergyUsed
+ ld hl, HighCriticalMoves ; table of high critical hit moves
+.Loop
+ ld a, [hli] ; read move from move table
+ cp c ; does it match the move about to be used?
+ jr z, .HighCritical ; if so, the move about to be used is a high critical hit ratio move
+ inc a ; move on to the next move, FF terminates loop
+ jr nz, .Loop ; check the next move in HighCriticalMoves
+ srl b ; /2 for regular move (effective (base speed / 2))
+ jr .SkipHighCritical ; continue as a normal move
+.HighCritical
+ sla b ; *2 for high critical hit moves
+ jr nc, .noCarry
+ ld b, $ff ; cap at 255/256
+.noCarry
+ sla b ; *4 for high critical move (effective (base speed/2)*8))
+ jr nc, .SkipHighCritical
+ ld b, $ff
+.SkipHighCritical
+ call BattleRandom ; generates a random value, in "a"
+ rlc a
+ rlc a
+ rlc a
+ cp b ; check a against calculated crit rate
+ ret nc ; no critical hit if no borrow
+ ld a, $1
+ ld [wCriticalHitOrOHKO], a ; set critical hit flag
+ ret
+
+; high critical hit moves
+HighCriticalMoves:
+ db KARATE_CHOP
+ db RAZOR_LEAF
+ db CRABHAMMER
+ db SLASH
+ db $FF
+
+
+; function to determine if Counter hits and if so, how much damage it does
+HandleCounterMove:
+; The variables checked by Counter are updated whenever the cursor points to a new move in the battle selection menu.
+; This is irrelevant for the opponent's side outside of link battles, since the move selection is controlled by the AI.
+; However, in the scenario where the player switches out and the opponent uses Counter,
+; the outcome may be affected by the player's actions in the move selection menu prior to switching the Pokemon.
+; This might also lead to desync glitches in link battles.
+
+ ld a,[H_WHOSETURN] ; whose turn
+ and a
+; player's turn
+ ld hl,wEnemySelectedMove
+ ld de,wEnemyMovePower
+ ld a,[wPlayerSelectedMove]
+ jr z,.next
+; enemy's turn
+ ld hl,wPlayerSelectedMove
+ ld de,wPlayerMovePower
+ ld a,[wEnemySelectedMove]
+.next
+ cp a,COUNTER
+ ret nz ; return if not using Counter
+ ld a,$01
+ ld [wMoveMissed],a ; initialize the move missed variable to true (it is set to false below if the move hits)
+ ld a,[hl]
+ cp a,COUNTER
+ ret z ; miss if the opponent's last selected move is Counter.
+ ld a,[de]
+ and a
+ ret z ; miss if the opponent's last selected move's Base Power is 0.
+; check if the move the target last selected was Normal or Fighting type
+ inc de
+ ld a,[de]
+ and a ; normal type
+ jr z,.counterableType
+ cp a,FIGHTING
+ jr z,.counterableType
+; if the move wasn't Normal or Fighting type, miss
+ xor a
+ ret
+.counterableType
+ ld hl,wDamage
+ ld a,[hli]
+ or [hl]
+ ret z ; If we made it here, Counter still misses if the last move used in battle did no damage to its target.
+ ; wDamage is shared by both players, so Counter may strike back damage dealt by the Counter user itself
+ ; if the conditions meet, even though 99% of the times damage will come from the target.
+; if it did damage, double it
+ ld a,[hl]
+ add a
+ ldd [hl],a
+ ld a,[hl]
+ adc a
+ ld [hl],a
+ jr nc,.noCarry
+; damage is capped at 0xFFFF
+ ld a,$ff
+ ld [hli],a
+ ld [hl],a
+.noCarry
+ xor a
+ ld [wMoveMissed],a
+ call MoveHitTest ; do the normal move hit test in addition to Counter's special rules
+ xor a
+ ret
+
+ApplyAttackToEnemyPokemon:
+ ld a,[wPlayerMoveEffect]
+ cp a,OHKO_EFFECT
+ jr z,ApplyDamageToEnemyPokemon
+ cp a,SUPER_FANG_EFFECT
+ jr z,.superFangEffect
+ cp a,SPECIAL_DAMAGE_EFFECT
+ jr z,.specialDamage
+ ld a,[wPlayerMovePower]
+ and a
+ jp z,ApplyAttackToEnemyPokemonDone ; no attack to apply if base power is 0
+ jr ApplyDamageToEnemyPokemon
+.superFangEffect
+; set the damage to half the target's HP
+ ld hl,wEnemyMonHP
+ ld de,wDamage
+ ld a,[hli]
+ srl a
+ ld [de],a
+ inc de
+ ld b,a
+ ld a,[hl]
+ rr a
+ ld [de],a
+ or b
+ jr nz,ApplyDamageToEnemyPokemon
+; make sure Super Fang's damage is always at least 1
+ ld a,$01
+ ld [de],a
+ jr ApplyDamageToEnemyPokemon
+.specialDamage
+ ld hl,wBattleMonLevel
+ ld a,[hl]
+ ld b,a ; Seismic Toss deals damage equal to the user's level
+ ld a,[wPlayerMoveNum]
+ cp a,SEISMIC_TOSS
+ jr z,.storeDamage
+ cp a,NIGHT_SHADE
+ jr z,.storeDamage
+ ld b,SONICBOOM_DAMAGE ; 20
+ cp a,SONICBOOM
+ jr z,.storeDamage
+ ld b,DRAGON_RAGE_DAMAGE ; 40
+ cp a,DRAGON_RAGE
+ jr z,.storeDamage
+; Psywave
+ ld a,[hl]
+ ld b,a
+ srl a
+ add b
+ ld b,a ; b = level * 1.5
+; loop until a random number in the range [1, b) is found
+.loop
+ call BattleRandom
+ and a
+ jr z,.loop
+ cp b
+ jr nc,.loop
+ ld b,a
+.storeDamage ; store damage value at b
+ ld hl,wDamage
+ xor a
+ ld [hli],a
+ ld a,b
+ ld [hl],a
+
+ApplyDamageToEnemyPokemon:
+ ld hl,wDamage
+ ld a,[hli]
+ ld b,a
+ ld a,[hl]
+ or b
+ jr z,ApplyAttackToEnemyPokemonDone ; we're done if damage is 0
+ ld a,[wEnemyBattleStatus2]
+ bit HasSubstituteUp,a ; does the enemy have a substitute?
+ jp nz,AttackSubstitute
+; subtract the damage from the pokemon's current HP
+; also, save the current HP at wHPBarOldHP
+ ld a,[hld]
+ ld b,a
+ ld a,[wEnemyMonHP + 1]
+ ld [wHPBarOldHP],a
+ sub b
+ ld [wEnemyMonHP + 1],a
+ ld a,[hl]
+ ld b,a
+ ld a,[wEnemyMonHP]
+ ld [wHPBarOldHP+1],a
+ sbc b
+ ld [wEnemyMonHP],a
+ jr nc,.animateHpBar
+; if more damage was done than the current HP, zero the HP and set the damage (wDamage)
+; equal to how much HP the pokemon had before the attack
+ ld a,[wHPBarOldHP+1]
+ ld [hli],a
+ ld a,[wHPBarOldHP]
+ ld [hl],a
+ xor a
+ ld hl,wEnemyMonHP
+ ld [hli],a
+ ld [hl],a
+.animateHpBar
+ ld hl,wEnemyMonMaxHP
+ ld a,[hli]
+ ld [wHPBarMaxHP+1],a
+ ld a,[hl]
+ ld [wHPBarMaxHP],a
+ ld hl,wEnemyMonHP
+ ld a,[hli]
+ ld [wHPBarNewHP+1],a
+ ld a,[hl]
+ ld [wHPBarNewHP],a
+ coord hl, 2, 2
+ xor a
+ ld [wHPBarType],a
+ predef UpdateHPBar2 ; animate the HP bar shortening
+ApplyAttackToEnemyPokemonDone:
+ jp DrawHUDsAndHPBars
+
+ApplyAttackToPlayerPokemon:
+ ld a,[wEnemyMoveEffect]
+ cp a,OHKO_EFFECT
+ jr z,ApplyDamageToPlayerPokemon
+ cp a,SUPER_FANG_EFFECT
+ jr z,.superFangEffect
+ cp a,SPECIAL_DAMAGE_EFFECT
+ jr z,.specialDamage
+ ld a,[wEnemyMovePower]
+ and a
+ jp z,ApplyAttackToPlayerPokemonDone
+ jr ApplyDamageToPlayerPokemon
+.superFangEffect
+; set the damage to half the target's HP
+ ld hl,wBattleMonHP
+ ld de,wDamage
+ ld a,[hli]
+ srl a
+ ld [de],a
+ inc de
+ ld b,a
+ ld a,[hl]
+ rr a
+ ld [de],a
+ or b
+ jr nz,ApplyDamageToPlayerPokemon
+; make sure Super Fang's damage is always at least 1
+ ld a,$01
+ ld [de],a
+ jr ApplyDamageToPlayerPokemon
+.specialDamage
+ ld hl,wEnemyMonLevel
+ ld a,[hl]
+ ld b,a
+ ld a,[wEnemyMoveNum]
+ cp a,SEISMIC_TOSS
+ jr z,.storeDamage
+ cp a,NIGHT_SHADE
+ jr z,.storeDamage
+ ld b,SONICBOOM_DAMAGE
+ cp a,SONICBOOM
+ jr z,.storeDamage
+ ld b,DRAGON_RAGE_DAMAGE
+ cp a,DRAGON_RAGE
+ jr z,.storeDamage
+; Psywave
+ ld a,[hl]
+ ld b,a
+ srl a
+ add b
+ ld b,a ; b = attacker's level * 1.5
+; loop until a random number in the range [0, b) is found
+; this differs from the range when the player attacks, which is [1, b)
+; it's possible for the enemy to do 0 damage with Psywave, but the player always does at least 1 damage
+.loop
+ call BattleRandom
+ cp b
+ jr nc,.loop
+ ld b,a
+.storeDamage
+ ld hl,wDamage
+ xor a
+ ld [hli],a
+ ld a,b
+ ld [hl],a
+
+ApplyDamageToPlayerPokemon:
+ ld hl,wDamage
+ ld a,[hli]
+ ld b,a
+ ld a,[hl]
+ or b
+ jr z,ApplyAttackToPlayerPokemonDone ; we're done if damage is 0
+ ld a,[wPlayerBattleStatus2]
+ bit HasSubstituteUp,a ; does the player have a substitute?
+ jp nz,AttackSubstitute
+; subtract the damage from the pokemon's current HP
+; also, save the current HP at wHPBarOldHP and the new HP at wHPBarNewHP
+ ld a,[hld]
+ ld b,a
+ ld a,[wBattleMonHP + 1]
+ ld [wHPBarOldHP],a
+ sub b
+ ld [wBattleMonHP + 1],a
+ ld [wHPBarNewHP],a
+ ld b,[hl]
+ ld a,[wBattleMonHP]
+ ld [wHPBarOldHP+1],a
+ sbc b
+ ld [wBattleMonHP],a
+ ld [wHPBarNewHP+1],a
+ jr nc,.animateHpBar
+; if more damage was done than the current HP, zero the HP and set the damage (wDamage)
+; equal to how much HP the pokemon had before the attack
+ ld a,[wHPBarOldHP+1]
+ ld [hli],a
+ ld a,[wHPBarOldHP]
+ ld [hl],a
+ xor a
+ ld hl,wBattleMonHP
+ ld [hli],a
+ ld [hl],a
+ ld hl,wHPBarNewHP
+ ld [hli],a
+ ld [hl],a
+.animateHpBar
+ ld hl,wBattleMonMaxHP
+ ld a,[hli]
+ ld [wHPBarMaxHP+1],a
+ ld a,[hl]
+ ld [wHPBarMaxHP],a
+ coord hl, 10, 9
+ ld a,$01
+ ld [wHPBarType],a
+ predef UpdateHPBar2 ; animate the HP bar shortening
+ApplyAttackToPlayerPokemonDone:
+ jp DrawHUDsAndHPBars
+
+AttackSubstitute:
+; Unlike the two ApplyAttackToPokemon functions, Attack Substitute is shared by player and enemy.
+; Self-confusion damage as well as Hi-Jump Kick and Jump Kick recoil cause a momentary turn swap before being applied.
+; If the user has a Substitute up and would take damage because of that,
+; damage will be applied to the other player's Substitute.
+; Normal recoil such as from Double-Edge isn't affected by this glitch,
+; because this function is never called in that case.
+
+ ld hl,SubstituteTookDamageText
+ call PrintText
+; values for player turn
+ ld de,wEnemySubstituteHP
+ ld bc,wEnemyBattleStatus2
+ ld a,[H_WHOSETURN]
+ and a
+ jr z,.applyDamageToSubstitute
+; values for enemy turn
+ ld de,wPlayerSubstituteHP
+ ld bc,wPlayerBattleStatus2
+.applyDamageToSubstitute
+ ld hl,wDamage
+ ld a,[hli]
+ and a
+ jr nz,.substituteBroke ; damage > 0xFF always breaks substitutes
+; subtract damage from HP of substitute
+ ld a,[de]
+ sub [hl]
+ ld [de],a
+ ret nc
+.substituteBroke
+; If the target's Substitute breaks, wDamage isn't updated with the amount of HP
+; the Substitute had before being attacked.
+ ld h,b
+ ld l,c
+ res HasSubstituteUp,[hl] ; unset the substitute bit
+ ld hl,SubstituteBrokeText
+ call PrintText
+; flip whose turn it is for the next function call
+ ld a,[H_WHOSETURN]
+ xor a,$01
+ ld [H_WHOSETURN],a
+ callab HideSubstituteShowMonAnim ; animate the substitute breaking
+; flip the turn back to the way it was
+ ld a,[H_WHOSETURN]
+ xor a,$01
+ ld [H_WHOSETURN],a
+ ld hl,wPlayerMoveEffect ; value for player's turn
+ and a
+ jr z,.nullifyEffect
+ ld hl,wEnemyMoveEffect ; value for enemy's turn
+.nullifyEffect
+ xor a
+ ld [hl],a ; zero the effect of the attacker's move
+ jp DrawHUDsAndHPBars
+
+SubstituteTookDamageText:
+ TX_FAR _SubstituteTookDamageText
+ db "@"
+
+SubstituteBrokeText:
+ TX_FAR _SubstituteBrokeText
+ db "@"
+
+; this function raises the attack modifier of a pokemon using Rage when that pokemon is attacked
+HandleBuildingRage:
+; values for the player turn
+ ld hl,wEnemyBattleStatus2
+ ld de,wEnemyMonStatMods
+ ld bc,wEnemyMoveNum
+ ld a,[H_WHOSETURN]
+ and a
+ jr z,.next
+; values for the enemy turn
+ ld hl,wPlayerBattleStatus2
+ ld de,wPlayerMonStatMods
+ ld bc,wPlayerMoveNum
+.next
+ bit UsingRage,[hl] ; is the pokemon being attacked under the effect of Rage?
+ ret z ; return if not
+ ld a,[de]
+ cp a,$0d ; maximum stat modifier value
+ ret z ; return if attack modifier is already maxed
+ ld a,[H_WHOSETURN]
+ xor a,$01 ; flip turn for the stat modifier raising function
+ ld [H_WHOSETURN],a
+; temporarily change the target pokemon's move to $00 and the effect to the one
+; that causes the attack modifier to go up one stage
+ ld h,b
+ ld l,c
+ ld [hl],$00 ; null move number
+ inc hl
+ ld [hl],ATTACK_UP1_EFFECT
+ push hl
+ ld hl,BuildingRageText
+ call PrintText
+ call StatModifierUpEffect ; stat modifier raising function
+ pop hl
+ xor a
+ ldd [hl],a ; null move effect
+ ld a,RAGE
+ ld [hl],a ; restore the target pokemon's move number to Rage
+ ld a,[H_WHOSETURN]
+ xor a,$01 ; flip turn back to the way it was
+ ld [H_WHOSETURN],a
+ ret
+
+BuildingRageText:
+ TX_FAR _BuildingRageText
+ db "@"
+
+; copy last move for Mirror Move
+; sets zero flag on failure and unsets zero flag on success
+MirrorMoveCopyMove:
+; Mirror Move makes use of ccf1 (wPlayerUsedMove) and ccf2 (wEnemyUsedMove) addresses,
+; which are mainly used to print the "[Pokemon] used [Move]" text.
+; Both are set to 0 whenever a new Pokemon is sent out
+; ccf1 is also set to 0 whenever the player is fast asleep or frozen solid.
+; ccf2 is also set to 0 whenever the enemy is fast asleep or frozen solid.
+
+ ld a,[H_WHOSETURN]
+ and a
+; values for player turn
+ ld a,[wEnemyUsedMove]
+ ld hl,wPlayerSelectedMove
+ ld de,wPlayerMoveNum
+ jr z,.next
+; values for enemy turn
+ ld a,[wPlayerUsedMove]
+ ld de,wEnemyMoveNum
+ ld hl,wEnemySelectedMove
+.next
+ ld [hl],a
+ cp a,MIRROR_MOVE ; did the target Pokemon last use Mirror Move, and miss?
+ jr z,.mirrorMoveFailed
+ and a ; has the target selected any move yet?
+ jr nz,ReloadMoveData
+.mirrorMoveFailed
+ ld hl,MirrorMoveFailedText
+ call PrintText
+ xor a
+ ret
+
+MirrorMoveFailedText:
+ TX_FAR _MirrorMoveFailedText
+ db "@"
+
+; function used to reload move data for moves like Mirror Move and Metronome
+ReloadMoveData:
+ ld [wd11e],a
+ dec a
+ ld hl,Moves
+ ld bc,MoveEnd - Moves
+ call AddNTimes
+ ld a,BANK(Moves)
+ call FarCopyData ; copy the move's stats
+ call IncrementMovePP
+; the follow two function calls are used to reload the move name
+ call GetMoveName
+ call CopyStringToCF50
+ ld a,$01
+ and a
+ ret
+
+; function that picks a random move for metronome
+MetronomePickMove:
+ xor a
+ ld [wAnimationType],a
+ ld a,METRONOME
+ call PlayMoveAnimation ; play Metronome's animation
+; values for player turn
+ ld de,wPlayerMoveNum
+ ld hl,wPlayerSelectedMove
+ ld a,[H_WHOSETURN]
+ and a
+ jr z,.pickMoveLoop
+; values for enemy turn
+ ld de,wEnemyMoveNum
+ ld hl,wEnemySelectedMove
+; loop to pick a random number in the range [1, $a5) to be the move used by Metronome
+.pickMoveLoop
+ call BattleRandom
+ and a
+ jr z,.pickMoveLoop
+ cp a,NUM_ATTACKS + 1 ; max normal move number + 1 (this is Struggle's move number)
+ jr nc,.pickMoveLoop
+ cp a,METRONOME
+ jr z,.pickMoveLoop
+ ld [hl],a
+ jr ReloadMoveData
+
+; this function increments the current move's PP
+; it's used to prevent moves that run another move within the same turn
+; (like Mirror Move and Metronome) from losing 2 PP
+IncrementMovePP:
+ ld a,[H_WHOSETURN]
+ and a
+; values for player turn
+ ld hl,wBattleMonPP
+ ld de,wPartyMon1PP
+ ld a,[wPlayerMoveListIndex]
+ jr z,.next
+; values for enemy turn
+ ld hl,wEnemyMonPP
+ ld de,wEnemyMon1PP
+ ld a,[wEnemyMoveListIndex]
+.next
+ ld b,$00
+ ld c,a
+ add hl,bc
+ inc [hl] ; increment PP in the currently battling pokemon memory location
+ ld h,d
+ ld l,e
+ add hl,bc
+ ld a,[H_WHOSETURN]
+ and a
+ ld a,[wPlayerMonNumber] ; value for player turn
+ jr z,.updatePP
+ ld a,[wEnemyMonPartyPos] ; value for enemy turn
+.updatePP
+ ld bc,wEnemyMon2 - wEnemyMon1
+ call AddNTimes
+ inc [hl] ; increment PP in the party memory location
+ ret
+
+; function to adjust the base damage of an attack to account for type effectiveness
+AdjustDamageForMoveType:
+; values for player turn
+ ld hl,wBattleMonType
+ ld a,[hli]
+ ld b,a ; b = type 1 of attacker
+ ld c,[hl] ; c = type 2 of attacker
+ ld hl,wEnemyMonType
+ ld a,[hli]
+ ld d,a ; d = type 1 of defender
+ ld e,[hl] ; e = type 2 of defender
+ ld a,[wPlayerMoveType]
+ ld [wMoveType],a
+ ld a,[H_WHOSETURN]
+ and a
+ jr z,.next
+; values for enemy turn
+ ld hl,wEnemyMonType
+ ld a,[hli]
+ ld b,a ; b = type 1 of attacker
+ ld c,[hl] ; c = type 2 of attacker
+ ld hl,wBattleMonType
+ ld a,[hli]
+ ld d,a ; d = type 1 of defender
+ ld e,[hl] ; e = type 2 of defender
+ ld a,[wEnemyMoveType]
+ ld [wMoveType],a
+.next
+ ld a,[wMoveType]
+ cp b ; does the move type match type 1 of the attacker?
+ jr z,.sameTypeAttackBonus
+ cp c ; does the move type match type 2 of the attacker?
+ jr z,.sameTypeAttackBonus
+ jr .skipSameTypeAttackBonus
+.sameTypeAttackBonus
+; if the move type matches one of the attacker's types
+ ld hl,wDamage + 1
+ ld a,[hld]
+ ld h,[hl]
+ ld l,a ; hl = damage
+ ld b,h
+ ld c,l ; bc = damage
+ srl b
+ rr c ; bc = floor(0.5 * damage)
+ add hl,bc ; hl = floor(1.5 * damage)
+; store damage
+ ld a,h
+ ld [wDamage],a
+ ld a,l
+ ld [wDamage + 1],a
+ ld hl,wDamageMultipliers
+ set 7,[hl]
+.skipSameTypeAttackBonus
+ ld a,[wMoveType]
+ ld b,a
+ ld hl,TypeEffects
+.loop
+ ld a,[hli] ; a = "attacking type" of the current type pair
+ cp a,$ff
+ jr z,.done
+ cp b ; does move type match "attacking type"?
+ jr nz,.nextTypePair
+ ld a,[hl] ; a = "defending type" of the current type pair
+ cp d ; does type 1 of defender match "defending type"?
+ jr z,.matchingPairFound
+ cp e ; does type 2 of defender match "defending type"?
+ jr z,.matchingPairFound
+ jr .nextTypePair
+.matchingPairFound
+; if the move type matches the "attacking type" and one of the defender's types matches the "defending type"
+ push hl
+ push bc
+ inc hl
+ ld a,[wDamageMultipliers]
+ and a,$80
+ ld b,a
+ ld a,[hl] ; a = damage multiplier
+ ld [H_MULTIPLIER],a
+ add b
+ ld [wDamageMultipliers],a
+ xor a
+ ld [H_MULTIPLICAND],a
+ ld hl,wDamage
+ ld a,[hli]
+ ld [H_MULTIPLICAND + 1],a
+ ld a,[hld]
+ ld [H_MULTIPLICAND + 2],a
+ call Multiply
+ ld a,10
+ ld [H_DIVISOR],a
+ ld b,$04
+ call Divide
+ ld a,[H_QUOTIENT + 2]
+ ld [hli],a
+ ld b,a
+ ld a,[H_QUOTIENT + 3]
+ ld [hl],a
+ or b ; is damage 0?
+ jr nz,.skipTypeImmunity
+.typeImmunity
+; if damage is 0, make the move miss
+; this only occurs if a move that would do 2 or 3 damage is 0.25x effective against the target
+ inc a
+ ld [wMoveMissed],a
+.skipTypeImmunity
+ pop bc
+ pop hl
+.nextTypePair
+ inc hl
+ inc hl
+ jp .loop
+.done
+ ret
+
+; function to tell how effective the type of an enemy attack is on the player's current pokemon
+; this doesn't take into account the effects that dual types can have
+; (e.g. 4x weakness / resistance, weaknesses and resistances canceling)
+; the result is stored in [wTypeEffectiveness]
+; ($05 is not very effective, $10 is neutral, $14 is super effective)
+; as far is can tell, this is only used once in some AI code to help decide which move to use
+AIGetTypeEffectiveness:
+ ld a,[wEnemyMoveType]
+ ld d,a ; d = type of enemy move
+ ld hl,wBattleMonType
+ ld b,[hl] ; b = type 1 of player's pokemon
+ inc hl
+ ld c,[hl] ; c = type 2 of player's pokemon
+ ld a,$10
+ ld [wTypeEffectiveness],a ; initialize to neutral effectiveness
+ ld hl,TypeEffects
+.loop
+ ld a,[hli]
+ cp a,$ff
+ ret z
+ cp d ; match the type of the move
+ jr nz,.nextTypePair1
+ ld a,[hli]
+ cp b ; match with type 1 of pokemon
+ jr z,.done
+ cp c ; or match with type 2 of pokemon
+ jr z,.done
+ jr .nextTypePair2
+.nextTypePair1
+ inc hl
+.nextTypePair2
+ inc hl
+ jr .loop
+.done
+ ld a,[hl]
+ ld [wTypeEffectiveness],a ; store damage multiplier
+ ret
+
+INCLUDE "data/type_effects.asm"
+
+; some tests that need to pass for a move to hit
+MoveHitTest:
+; player's turn
+ ld hl,wEnemyBattleStatus1
+ ld de,wPlayerMoveEffect
+ ld bc,wEnemyMonStatus
+ ld a,[H_WHOSETURN]
+ and a
+ jr z,.dreamEaterCheck
+; enemy's turn
+ ld hl,wPlayerBattleStatus1
+ ld de,wEnemyMoveEffect
+ ld bc,wBattleMonStatus
+.dreamEaterCheck
+ ld a,[de]
+ cp a,DREAM_EATER_EFFECT
+ jr nz,.swiftCheck
+ ld a,[bc]
+ and a,SLP ; is the target pokemon sleeping?
+ jp z,.moveMissed
+.swiftCheck
+ ld a,[de]
+ cp a,SWIFT_EFFECT
+ ret z ; Swift never misses (interestingly, Azure Heights lists this is a myth, but it appears to be true)
+ call CheckTargetSubstitute ; substitute check (note that this overwrites a)
+ jr z,.checkForDigOrFlyStatus
+; this code is buggy. it's supposed to prevent HP draining moves from working on substitutes.
+; since $7b79 overwrites a with either $00 or $01, it never works.
+ cp a,DRAIN_HP_EFFECT
+ jp z,.moveMissed
+ cp a,DREAM_EATER_EFFECT
+ jp z,.moveMissed
+.checkForDigOrFlyStatus
+ bit Invulnerable,[hl]
+ jp nz,.moveMissed
+ ld a,[H_WHOSETURN]
+ and a
+ jr nz,.enemyTurn
+.playerTurn
+; this checks if the move effect is disallowed by mist
+ ld a,[wPlayerMoveEffect]
+ cp a,ATTACK_DOWN1_EFFECT
+ jr c,.skipEnemyMistCheck
+ cp a,HAZE_EFFECT + 1
+ jr c,.enemyMistCheck
+ cp a,ATTACK_DOWN2_EFFECT
+ jr c,.skipEnemyMistCheck
+ cp a,REFLECT_EFFECT + 1
+ jr c,.enemyMistCheck
+ jr .skipEnemyMistCheck
+.enemyMistCheck
+; if move effect is from $12 to $19 inclusive or $3a to $41 inclusive
+; i.e. the following moves
+; GROWL, TAIL WHIP, LEER, STRING SHOT, SAND-ATTACK, SMOKESCREEN, KINESIS,
+; FLASH, CONVERSION*, HAZE*, SCREECH, LIGHT SCREEN*, REFLECT*
+; the moves that are marked with an asterisk are not affected since this
+; function is not called when those moves are used
+ ld a,[wEnemyBattleStatus2]
+ bit ProtectedByMist,a ; is mon protected by mist?
+ jp nz,.moveMissed
+.skipEnemyMistCheck
+ ld a,[wPlayerBattleStatus2]
+ bit UsingXAccuracy,a ; is the player using X Accuracy?
+ ret nz ; if so, always hit regardless of accuracy/evasion
+ jr .calcHitChance
+.enemyTurn
+ ld a,[wEnemyMoveEffect]
+ cp a,ATTACK_DOWN1_EFFECT
+ jr c,.skipPlayerMistCheck
+ cp a,HAZE_EFFECT + 1
+ jr c,.playerMistCheck
+ cp a,ATTACK_DOWN2_EFFECT
+ jr c,.skipPlayerMistCheck
+ cp a,REFLECT_EFFECT + 1
+ jr c,.playerMistCheck
+ jr .skipPlayerMistCheck
+.playerMistCheck
+; similar to enemy mist check
+ ld a,[wPlayerBattleStatus2]
+ bit ProtectedByMist,a ; is mon protected by mist?
+ jp nz,.moveMissed
+.skipPlayerMistCheck
+ ld a,[wEnemyBattleStatus2]
+ bit UsingXAccuracy,a ; is the enemy using X Accuracy?
+ ret nz ; if so, always hit regardless of accuracy/evasion
+.calcHitChance
+ call CalcHitChance ; scale the move accuracy according to attacker's accuracy and target's evasion
+ ld a,[wPlayerMoveAccuracy]
+ ld b,a
+ ld a,[H_WHOSETURN]
+ and a
+ jr z,.doAccuracyCheck
+ ld a,[wEnemyMoveAccuracy]
+ ld b,a
+.doAccuracyCheck
+; if the random number generated is greater than or equal to the scaled accuracy, the move misses
+; note that this means that even the highest accuracy is still just a 255/256 chance, not 100%
+ call BattleRandom
+ cp b
+ jr nc,.moveMissed
+ ret
+.moveMissed
+ xor a
+ ld hl,wDamage ; zero the damage
+ ld [hli],a
+ ld [hl],a
+ inc a
+ ld [wMoveMissed],a
+ ld a,[H_WHOSETURN]
+ and a
+ jr z,.playerTurn2
+.enemyTurn2
+ ld hl,wEnemyBattleStatus1
+ res UsingTrappingMove,[hl] ; end multi-turn attack e.g. wrap
+ ret
+.playerTurn2
+ ld hl,wPlayerBattleStatus1
+ res UsingTrappingMove,[hl] ; end multi-turn attack e.g. wrap
+ ret
+
+; values for player turn
+CalcHitChance:
+ ld hl,wPlayerMoveAccuracy
+ ld a,[H_WHOSETURN]
+ and a
+ ld a,[wPlayerMonAccuracyMod]
+ ld b,a
+ ld a,[wEnemyMonEvasionMod]
+ ld c,a
+ jr z,.next
+; values for enemy turn
+ ld hl,wEnemyMoveAccuracy
+ ld a,[wEnemyMonAccuracyMod]
+ ld b,a
+ ld a,[wPlayerMonEvasionMod]
+ ld c,a
+.next
+ ld a,$0e
+ sub c
+ ld c,a ; c = 14 - EVASIONMOD (this "reflects" the value over 7, so that an increase in the target's evasion
+ ; decreases the hit chance instead of increasing the hit chance)
+; zero the high bytes of the multiplicand
+ xor a
+ ld [H_MULTIPLICAND],a
+ ld [H_MULTIPLICAND + 1],a
+ ld a,[hl]
+ ld [H_MULTIPLICAND + 2],a ; set multiplicand to move accuracy
+ push hl
+ ld d,$02 ; loop has two iterations
+; loop to do the calculations, the first iteration multiplies by the accuracy ratio and
+; the second iteration multiplies by the evasion ratio
+.loop
+ push bc
+ ld hl, StatModifierRatios ; stat modifier ratios
+ dec b
+ sla b
+ ld c,b
+ ld b,$00
+ add hl,bc ; hl = address of stat modifier ratio
+ pop bc
+ ld a,[hli]
+ ld [H_MULTIPLIER],a ; set multiplier to the numerator of the ratio
+ call Multiply
+ ld a,[hl]
+ ld [H_DIVISOR],a ; set divisor to the the denominator of the ratio
+ ; (the dividend is the product of the previous multiplication)
+ ld b,$04 ; number of bytes in the dividend
+ call Divide
+ ld a,[H_QUOTIENT + 3]
+ ld b,a
+ ld a,[H_QUOTIENT + 2]
+ or b
+ jp nz,.nextCalculation
+; make sure the result is always at least one
+ ld [H_QUOTIENT + 2],a
+ ld a,$01
+ ld [H_QUOTIENT + 3],a
+.nextCalculation
+ ld b,c
+ dec d
+ jr nz,.loop
+ ld a,[H_QUOTIENT + 2]
+ and a ; is the calculated hit chance over 0xFF?
+ ld a,[H_QUOTIENT + 3]
+ jr z,.storeAccuracy
+; if calculated hit chance over 0xFF
+ ld a,$ff ; set the hit chance to 0xFF
+.storeAccuracy
+ pop hl
+ ld [hl],a ; store the hit chance in the move accuracy variable
+ ret
+
+; multiplies damage by a random percentage from ~85% to 100%
+RandomizeDamage:
+ ld hl, wDamage
+ ld a, [hli]
+ and a
+ jr nz, .DamageGreaterThanOne
+ ld a, [hl]
+ cp 2
+ ret c ; return if damage is equal to 0 or 1
+.DamageGreaterThanOne
+ xor a
+ ld [H_MULTIPLICAND], a
+ dec hl
+ ld a, [hli]
+ ld [H_MULTIPLICAND + 1], a
+ ld a, [hl]
+ ld [H_MULTIPLICAND + 2], a
+; loop until a random number greater than or equal to 217 is generated
+.loop
+ call BattleRandom
+ rrca
+ cp 217
+ jr c, .loop
+ ld [H_MULTIPLIER], a
+ call Multiply ; multiply damage by the random number, which is in the range [217, 255]
+ ld a, 255
+ ld [H_DIVISOR], a
+ ld b, $4
+ call Divide ; divide the result by 255
+; store the modified damage
+ ld a, [H_QUOTIENT + 2]
+ ld hl, wDamage
+ ld [hli], a
+ ld a, [H_QUOTIENT + 3]
+ ld [hl], a
+ ret
+
+; for more detailed commentary, see equivalent function for player side (ExecutePlayerMove)
+ExecuteEnemyMove:
+ ld a, [wEnemySelectedMove]
+ inc a
+ jp z, ExecuteEnemyMoveDone
+ call PrintGhostText
+ jp z, ExecuteEnemyMoveDone
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ jr nz, .executeEnemyMove
+ ld b, $1
+ ld a, [wSerialExchangeNybbleReceiveData]
+ cp LINKBATTLE_STRUGGLE
+ jr z, .executeEnemyMove
+ cp 4
+ ret nc
+.executeEnemyMove
+ ld hl, wAILayer2Encouragement
+ inc [hl]
+ xor a
+ ld [wMoveMissed], a
+ ld [wMoveDidntMiss], a
+ ld a, $a
+ ld [wDamageMultipliers], a
+ call CheckEnemyStatusConditions
+ jr nz, .enemyHasNoSpecialConditions
+ jp hl
+.enemyHasNoSpecialConditions
+ ld hl, wEnemyBattleStatus1
+ bit ChargingUp, [hl] ; is the enemy charging up for attack?
+ jr nz, EnemyCanExecuteChargingMove ; if so, jump
+ call GetCurrentMove
+
+CheckIfEnemyNeedsToChargeUp:
+ ld a, [wEnemyMoveEffect]
+ cp CHARGE_EFFECT
+ jp z, JumpMoveEffect
+ cp FLY_EFFECT
+ jp z, JumpMoveEffect
+ jr EnemyCanExecuteMove
+EnemyCanExecuteChargingMove:
+ ld hl, wEnemyBattleStatus1
+ res ChargingUp, [hl] ; no longer charging up for attack
+ res Invulnerable, [hl] ; no longer invulnerable to typical attacks
+ ld a, [wEnemyMoveNum]
+ ld [wd0b5], a
+ ld a, BANK(MoveNames)
+ ld [wPredefBank], a
+ ld a, MOVE_NAME
+ ld [wNameListType], a
+ call GetName
+ ld de, wcd6d
+ call CopyStringToCF50
+EnemyCanExecuteMove:
+ xor a
+ ld [wMonIsDisobedient], a
+ call PrintMonName1Text
+ ld a, [wEnemyMoveEffect]
+ ld hl, ResidualEffects1
+ ld de, $1
+ call IsInArray
+ jp c, JumpMoveEffect
+ ld a, [wEnemyMoveEffect]
+ ld hl, SpecialEffectsCont
+ ld de, $1
+ call IsInArray
+ call c, JumpMoveEffect
+EnemyCalcMoveDamage:
+ call SwapPlayerAndEnemyLevels
+ ld a, [wEnemyMoveEffect]
+ ld hl, SetDamageEffects
+ ld de, $1
+ call IsInArray
+ jp c, EnemyMoveHitTest
+ call CriticalHitTest
+ call HandleCounterMove
+ jr z, handleIfEnemyMoveMissed
+ call SwapPlayerAndEnemyLevels
+ call GetDamageVarsForEnemyAttack
+ call SwapPlayerAndEnemyLevels
+ call CalculateDamage
+ jp z, EnemyCheckIfFlyOrChargeEffect
+ call AdjustDamageForMoveType
+ call RandomizeDamage
+
+EnemyMoveHitTest:
+ call MoveHitTest
+handleIfEnemyMoveMissed:
+ ld a, [wMoveMissed]
+ and a
+ jr z, .moveDidNotMiss
+ ld a, [wEnemyMoveEffect]
+ cp EXPLODE_EFFECT
+ jr z, handleExplosionMiss
+ jr EnemyCheckIfFlyOrChargeEffect
+.moveDidNotMiss
+ call SwapPlayerAndEnemyLevels
+
+GetEnemyAnimationType:
+ ld a, [wEnemyMoveEffect]
+ and a
+ ld a, $1
+ jr z, playEnemyMoveAnimation
+ ld a, $2
+ jr playEnemyMoveAnimation
+handleExplosionMiss:
+ call SwapPlayerAndEnemyLevels
+ xor a
+playEnemyMoveAnimation:
+ push af
+ ld a, [wEnemyBattleStatus2]
+ bit HasSubstituteUp, a ; does mon have a substitute?
+ ld hl, HideSubstituteShowMonAnim
+ ld b, BANK(HideSubstituteShowMonAnim)
+ call nz, Bankswitch
+ pop af
+ ld [wAnimationType], a
+ ld a, [wEnemyMoveNum]
+ call PlayMoveAnimation
+ call HandleExplodingAnimation
+ call DrawEnemyHUDAndHPBar
+ ld a, [wEnemyBattleStatus2]
+ bit HasSubstituteUp, a ; does mon have a substitute?
+ ld hl, ReshowSubstituteAnim
+ ld b, BANK(ReshowSubstituteAnim)
+ call nz, Bankswitch ; slide the substitute's sprite out
+ jr EnemyCheckIfMirrorMoveEffect
+
+EnemyCheckIfFlyOrChargeEffect:
+ call SwapPlayerAndEnemyLevels
+ ld c, 30
+ call DelayFrames
+ ld a, [wEnemyMoveEffect]
+ cp FLY_EFFECT
+ jr z, .playAnim
+ cp CHARGE_EFFECT
+ jr z, .playAnim
+ jr EnemyCheckIfMirrorMoveEffect
+.playAnim
+ xor a
+ ld [wAnimationType], a
+ ld a,STATUS_AFFECTED_ANIM
+ call PlayMoveAnimation
+EnemyCheckIfMirrorMoveEffect:
+ ld a, [wEnemyMoveEffect]
+ cp MIRROR_MOVE_EFFECT
+ jr nz, .notMirrorMoveEffect
+ call MirrorMoveCopyMove
+ jp z, ExecuteEnemyMoveDone
+ jp CheckIfEnemyNeedsToChargeUp
+.notMirrorMoveEffect
+ cp METRONOME_EFFECT
+ jr nz, .notMetronomeEffect
+ call MetronomePickMove
+ jp CheckIfEnemyNeedsToChargeUp
+.notMetronomeEffect
+ ld a, [wEnemyMoveEffect]
+ ld hl, ResidualEffects2
+ ld de, $1
+ call IsInArray
+ jp c, JumpMoveEffect
+ ld a, [wMoveMissed]
+ and a
+ jr z, .moveDidNotMiss
+ call PrintMoveFailureText
+ ld a, [wEnemyMoveEffect]
+ cp EXPLODE_EFFECT
+ jr z, .handleExplosionMiss
+ jp ExecuteEnemyMoveDone
+.moveDidNotMiss
+ call ApplyAttackToPlayerPokemon
+ call PrintCriticalOHKOText
+ callab DisplayEffectiveness
+ ld a, 1
+ ld [wMoveDidntMiss], a
+.handleExplosionMiss
+ ld a, [wEnemyMoveEffect]
+ ld hl, AlwaysHappenSideEffects
+ ld de, $1
+ call IsInArray
+ call c, JumpMoveEffect
+ ld hl, wBattleMonHP
+ ld a, [hli]
+ ld b, [hl]
+ or b
+ ret z
+ call HandleBuildingRage
+ ld hl, wEnemyBattleStatus1
+ bit AttackingMultipleTimes, [hl] ; is mon hitting multiple times? (example: double kick)
+ jr z, .notMultiHitMove
+ push hl
+ ld hl, wEnemyNumAttacksLeft
+ dec [hl]
+ pop hl
+ jp nz, GetEnemyAnimationType
+ res AttackingMultipleTimes, [hl] ; mon is no longer hitting multiple times
+ ld hl, HitXTimesText
+ call PrintText
+ xor a
+ ld [wEnemyNumHits], a
+.notMultiHitMove
+ ld a, [wEnemyMoveEffect]
+ and a
+ jr z, ExecuteEnemyMoveDone
+ ld hl, SpecialEffects
+ ld de, $1
+ call IsInArray
+ call nc, JumpMoveEffect
+ jr ExecuteEnemyMoveDone
+
+HitXTimesText:
+ TX_FAR _HitXTimesText
+ db "@"
+
+ExecuteEnemyMoveDone:
+ ld b, $1
+ ret
+
+; checks for various status conditions affecting the enemy mon
+; stores whether the mon cannot use a move this turn in Z flag
+CheckEnemyStatusConditions:
+ ld hl, wEnemyMonStatus
+ ld a, [hl]
+ and SLP ; sleep mask
+ jr z, .checkIfFrozen
+ dec a ; decrement number of turns left
+ ld [wEnemyMonStatus], a
+ and a
+ jr z, .wokeUp ; if the number of turns hit 0, wake up
+ ld hl, FastAsleepText
+ call PrintText
+ xor a
+ ld [wAnimationType], a
+ ld a,SLP_ANIM
+ call PlayMoveAnimation
+ jr .sleepDone
+.wokeUp
+ ld hl, WokeUpText
+ call PrintText
+.sleepDone
+ xor a
+ ld [wEnemyUsedMove], a
+ ld hl, ExecuteEnemyMoveDone ; enemy can't move this turn
+ jp .enemyReturnToHL
+.checkIfFrozen
+ bit FRZ, [hl]
+ jr z, .checkIfTrapped
+ ld hl, IsFrozenText
+ call PrintText
+ xor a
+ ld [wEnemyUsedMove], a
+ ld hl, ExecuteEnemyMoveDone ; enemy can't move this turn
+ jp .enemyReturnToHL
+.checkIfTrapped
+ ld a, [wPlayerBattleStatus1]
+ bit UsingTrappingMove, a ; is the player using a multi-turn attack like warp
+ jp z, .checkIfFlinched
+ ld hl, CantMoveText
+ call PrintText
+ ld hl, ExecuteEnemyMoveDone ; enemy can't move this turn
+ jp .enemyReturnToHL
+.checkIfFlinched
+ ld hl, wEnemyBattleStatus1
+ bit Flinched, [hl] ; check if enemy mon flinched
+ jp z, .checkIfMustRecharge
+ res Flinched, [hl]
+ ld hl, FlinchedText
+ call PrintText
+ ld hl, ExecuteEnemyMoveDone ; enemy can't move this turn
+ jp .enemyReturnToHL
+.checkIfMustRecharge
+ ld hl, wEnemyBattleStatus2
+ bit NeedsToRecharge, [hl] ; check if enemy mon has to recharge after using a move
+ jr z, .checkIfAnyMoveDisabled
+ res NeedsToRecharge, [hl]
+ ld hl, MustRechargeText
+ call PrintText
+ ld hl, ExecuteEnemyMoveDone ; enemy can't move this turn
+ jp .enemyReturnToHL
+.checkIfAnyMoveDisabled
+ ld hl, wEnemyDisabledMove
+ ld a, [hl]
+ and a
+ jr z, .checkIfConfused
+ dec a ; decrement disable counter
+ ld [hl], a
+ and $f ; did disable counter hit 0?
+ jr nz, .checkIfConfused
+ ld [hl], a
+ ld [wEnemyDisabledMoveNumber], a
+ ld hl, DisabledNoMoreText
+ call PrintText
+.checkIfConfused
+ ld a, [wEnemyBattleStatus1]
+ add a ; check if enemy mon is confused
+ jp nc, .checkIfTriedToUseDisabledMove
+ ld hl, wEnemyConfusedCounter
+ dec [hl]
+ jr nz, .isConfused
+ ld hl, wEnemyBattleStatus1
+ res Confused, [hl] ; if confused counter hit 0, reset confusion status
+ ld hl, ConfusedNoMoreText
+ call PrintText
+ jp .checkIfTriedToUseDisabledMove
+.isConfused
+ ld hl, IsConfusedText
+ call PrintText
+ xor a
+ ld [wAnimationType], a
+ ld a,CONF_ANIM
+ call PlayMoveAnimation
+ call BattleRandom
+ cp $80
+ jr c, .checkIfTriedToUseDisabledMove
+ ld hl, wEnemyBattleStatus1
+ ld a, [hl]
+ and 1 << Confused ; if mon hurts itself, clear every other status from wEnemyBattleStatus1
+ ld [hl], a
+ ld hl, HurtItselfText
+ call PrintText
+ ld hl, wBattleMonDefense
+ ld a, [hli]
+ push af
+ ld a, [hld]
+ push af
+ ld a, [wEnemyMonDefense]
+ ld [hli], a
+ ld a, [wEnemyMonDefense + 1]
+ ld [hl], a
+ ld hl, wEnemyMoveEffect
+ push hl
+ ld a, [hl]
+ push af
+ xor a
+ ld [hli], a
+ ld [wCriticalHitOrOHKO], a
+ ld a, 40
+ ld [hli], a
+ xor a
+ ld [hl], a
+ call GetDamageVarsForEnemyAttack
+ call CalculateDamage
+ pop af
+ pop hl
+ ld [hl], a
+ ld hl, wBattleMonDefense + 1
+ pop af
+ ld [hld], a
+ pop af
+ ld [hl], a
+ xor a
+ ld [wAnimationType], a
+ ld [H_WHOSETURN], a
+ ld a, POUND
+ call PlayMoveAnimation
+ ld a, $1
+ ld [H_WHOSETURN], a
+ call ApplyDamageToEnemyPokemon
+ jr .monHurtItselfOrFullyParalysed
+.checkIfTriedToUseDisabledMove
+; prevents a disabled move that was selected before being disabled from being used
+ ld a, [wEnemyDisabledMoveNumber]
+ and a
+ jr z, .checkIfParalysed
+ ld hl, wEnemySelectedMove
+ cp [hl]
+ jr nz, .checkIfParalysed
+ call PrintMoveIsDisabledText
+ ld hl, ExecuteEnemyMoveDone ; if a disabled move was somehow selected, player can't move this turn
+ jp .enemyReturnToHL
+.checkIfParalysed
+ ld hl, wEnemyMonStatus
+ bit PAR, [hl]
+ jr z, .checkIfUsingBide
+ call BattleRandom
+ cp $3f ; 25% to be fully paralysed
+ jr nc, .checkIfUsingBide
+ ld hl, FullyParalyzedText
+ call PrintText
+.monHurtItselfOrFullyParalysed
+ ld hl, wEnemyBattleStatus1
+ ld a, [hl]
+ ; clear bide, thrashing about, charging up, and multi-turn moves such as warp
+ and $ff ^ ((1 << StoringEnergy) | (1 << ThrashingAbout) | (1 << ChargingUp) | (1 << UsingTrappingMove))
+ ld [hl], a
+ ld a, [wEnemyMoveEffect]
+ cp FLY_EFFECT
+ jr z, .flyOrChargeEffect
+ cp CHARGE_EFFECT
+ jr z, .flyOrChargeEffect
+ jr .notFlyOrChargeEffect
+.flyOrChargeEffect
+ xor a
+ ld [wAnimationType], a
+ ld a, STATUS_AFFECTED_ANIM
+ call PlayMoveAnimation
+.notFlyOrChargeEffect
+ ld hl, ExecuteEnemyMoveDone
+ jp .enemyReturnToHL ; if using a two-turn move, enemy needs to recharge the first turn
+.checkIfUsingBide
+ ld hl, wEnemyBattleStatus1
+ bit StoringEnergy, [hl] ; is mon using bide?
+ jr z, .checkIfThrashingAbout
+ xor a
+ ld [wEnemyMoveNum], a
+ ld hl, wDamage
+ ld a, [hli]
+ ld b, a
+ ld c, [hl]
+ ld hl, wEnemyBideAccumulatedDamage + 1
+ ld a, [hl]
+ add c ; accumulate damage taken
+ ld [hld], a
+ ld a, [hl]
+ adc b
+ ld [hl], a
+ ld hl, wEnemyNumAttacksLeft
+ dec [hl] ; did Bide counter hit 0?
+ jr z, .unleashEnergy
+ ld hl, ExecuteEnemyMoveDone
+ jp .enemyReturnToHL ; unless mon unleashes energy, can't move this turn
+.unleashEnergy
+ ld hl, wEnemyBattleStatus1
+ res StoringEnergy, [hl] ; not using bide any more
+ ld hl, UnleashedEnergyText
+ call PrintText
+ ld a, $1
+ ld [wEnemyMovePower], a
+ ld hl, wEnemyBideAccumulatedDamage + 1
+ ld a, [hld]
+ add a
+ ld b, a
+ ld [wDamage + 1], a
+ ld a, [hl]
+ rl a ; double the damage
+ ld [wDamage], a
+ or b
+ jr nz, .next
+ ld a, $1
+ ld [wMoveMissed], a
+.next
+ xor a
+ ld [hli], a
+ ld [hl], a
+ ld a, BIDE
+ ld [wEnemyMoveNum], a
+ call SwapPlayerAndEnemyLevels
+ ld hl, handleIfEnemyMoveMissed ; skip damage calculation, DecrementPP and MoveHitTest
+ jp .enemyReturnToHL
+.checkIfThrashingAbout
+ bit ThrashingAbout, [hl] ; is mon using thrash or petal dance?
+ jr z, .checkIfUsingMultiturnMove
+ ld a, THRASH
+ ld [wEnemyMoveNum], a
+ ld hl, ThrashingAboutText
+ call PrintText
+ ld hl, wEnemyNumAttacksLeft
+ dec [hl] ; did Thrashing About counter hit 0?
+ ld hl, EnemyCalcMoveDamage ; skip DecrementPP
+ jp nz, .enemyReturnToHL
+ push hl
+ ld hl, wEnemyBattleStatus1
+ res ThrashingAbout, [hl] ; mon is no longer using thrash or petal dance
+ set Confused, [hl] ; mon is now confused
+ call BattleRandom
+ and $3
+ inc a
+ inc a ; confused for 2-5 turns
+ ld [wEnemyConfusedCounter], a
+ pop hl ; skip DecrementPP
+ jp .enemyReturnToHL
+.checkIfUsingMultiturnMove
+ bit UsingTrappingMove, [hl] ; is mon using multi-turn move?
+ jp z, .checkIfUsingRage
+ ld hl, AttackContinuesText
+ call PrintText
+ ld hl, wEnemyNumAttacksLeft
+ dec [hl] ; did multi-turn move end?
+ ld hl, GetEnemyAnimationType ; if it didn't, skip damage calculation (deal damage equal to last hit),
+ ; DecrementPP and MoveHitTest
+ jp nz, .enemyReturnToHL
+ jp .enemyReturnToHL
+.checkIfUsingRage
+ ld a, [wEnemyBattleStatus2]
+ bit UsingRage, a ; is mon using rage?
+ jp z, .checkEnemyStatusConditionsDone ; if we made it this far, mon can move normally this turn
+ ld a, RAGE
+ ld [wd11e], a
+ call GetMoveName
+ call CopyStringToCF50
+ xor a
+ ld [wEnemyMoveEffect], a
+ ld hl, EnemyCanExecuteMove
+ jp .enemyReturnToHL
+.enemyReturnToHL
+ xor a ; set Z flag
+ ret
+.checkEnemyStatusConditionsDone
+ ld a, $1
+ and a ; clear Z flag
+ ret
+
+GetCurrentMove:
+ ld a, [H_WHOSETURN]
+ and a
+ jp z, .player
+ ld de, wEnemyMoveNum
+ ld a, [wEnemySelectedMove]
+ jr .selected
+.player
+ ld de, wPlayerMoveNum
+ ld a, [wFlags_D733]
+ bit BIT_TEST_BATTLE, a
+ ld a, [wTestBattlePlayerSelectedMove]
+ jr nz, .selected
+ ld a, [wPlayerSelectedMove]
+.selected
+ ld [wd0b5], a
+ dec a
+ ld hl, Moves
+ ld bc, MoveEnd - Moves
+ call AddNTimes
+ ld a, BANK(Moves)
+ call FarCopyData
+
+ ld a, BANK(MoveNames)
+ ld [wPredefBank], a
+ ld a, MOVE_NAME
+ ld [wNameListType], a
+ call GetName
+ ld de, wcd6d
+ jp CopyStringToCF50
+
+LoadEnemyMonData:
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ jp z, LoadEnemyMonFromParty
+ ld a, [wEnemyMonSpecies2]
+ ld [wEnemyMonSpecies], a
+ ld [wd0b5], a
+ call GetMonHeader
+ ld a, [wEnemyBattleStatus3]
+ bit Transformed, a ; is enemy mon transformed?
+ ld hl, wTransformedEnemyMonOriginalDVs ; original DVs before transforming
+ ld a, [hli]
+ ld b, [hl]
+ jr nz, .storeDVs
+ ld a, [wIsInBattle]
+ cp $2 ; is it a trainer battle?
+; fixed DVs for trainer mon
+ ld a, $98
+ ld b, $88
+ jr z, .storeDVs
+; random DVs for wild mon
+ call BattleRandom
+ ld b, a
+ call BattleRandom
+.storeDVs
+ ld hl, wEnemyMonDVs
+ ld [hli], a
+ ld [hl], b
+ ld de, wEnemyMonLevel
+ ld a, [wCurEnemyLVL]
+ ld [de], a
+ inc de
+ ld b, $0
+ ld hl, wEnemyMonHP
+ push hl
+ call CalcStats
+ pop hl
+ ld a, [wIsInBattle]
+ cp $2 ; is it a trainer battle?
+ jr z, .copyHPAndStatusFromPartyData
+ ld a, [wEnemyBattleStatus3]
+ bit Transformed, a ; is enemy mon transformed?
+ jr nz, .copyTypes ; if transformed, jump
+; if it's a wild mon and not transformed, init the current HP to max HP and the status to 0
+ ld a, [wEnemyMonMaxHP]
+ ld [hli], a
+ ld a, [wEnemyMonMaxHP+1]
+ ld [hli], a
+ xor a
+ inc hl
+ ld [hl], a ; init status to 0
+ jr .copyTypes
+; if it's a trainer mon, copy the HP and status from the enemy party data
+.copyHPAndStatusFromPartyData
+ ld hl, wEnemyMon1HP
+ ld a, [wWhichPokemon]
+ ld bc, wEnemyMon2 - wEnemyMon1
+ call AddNTimes
+ ld a, [hli]
+ ld [wEnemyMonHP], a
+ ld a, [hli]
+ ld [wEnemyMonHP + 1], a
+ ld a, [wWhichPokemon]
+ ld [wEnemyMonPartyPos], a
+ inc hl
+ ld a, [hl]
+ ld [wEnemyMonStatus], a
+ jr .copyTypes
+.copyTypes
+ ld hl, wMonHTypes
+ ld de, wEnemyMonType
+ ld a, [hli] ; copy type 1
+ ld [de], a
+ inc de
+ ld a, [hli] ; copy type 2
+ ld [de], a
+ inc de
+ ld a, [hli] ; copy catch rate
+ ld [de], a
+ inc de
+ ld a, [wIsInBattle]
+ cp $2 ; is it a trainer battle?
+ jr nz, .copyStandardMoves
+; if it's a trainer battle, copy moves from enemy party data
+ ld hl, wEnemyMon1Moves
+ ld a, [wWhichPokemon]
+ ld bc, wEnemyMon2 - wEnemyMon1
+ call AddNTimes
+ ld bc, NUM_MOVES
+ call CopyData
+ jr .loadMovePPs
+.copyStandardMoves
+; for a wild mon, first copy default moves from the mon header
+ ld hl, wMonHMoves
+ ld a, [hli]
+ ld [de], a
+ inc de
+ ld a, [hli]
+ ld [de], a
+ inc de
+ ld a, [hli]
+ ld [de], a
+ inc de
+ ld a, [hl]
+ ld [de], a
+ dec de
+ dec de
+ dec de
+ xor a
+ ld [wLearningMovesFromDayCare], a
+ predef WriteMonMoves ; get moves based on current level
+.loadMovePPs
+ ld hl, wEnemyMonMoves
+ ld de, wEnemyMonPP - 1
+ predef LoadMovePPs
+ ld hl, wMonHBaseStats
+ ld de, wEnemyMonBaseStats
+ ld b, NUM_STATS
+.copyBaseStatsLoop
+ ld a, [hli]
+ ld [de], a
+ inc de
+ dec b
+ jr nz, .copyBaseStatsLoop
+ ld hl, wMonHCatchRate
+ ld a, [hli]
+ ld [de], a
+ inc de
+ ld a, [hl] ; base exp
+ ld [de], a
+ ld a, [wEnemyMonSpecies2]
+ ld [wd11e], a
+ call GetMonName
+ ld hl, wcd6d
+ ld de, wEnemyMonNick
+ ld bc, NAME_LENGTH
+ call CopyData
+ ld a, [wEnemyMonSpecies2]
+ ld [wd11e], a
+ predef IndexToPokedex
+ ld a, [wd11e]
+ dec a
+ ld c, a
+ ld b, FLAG_SET
+ ld hl, wPokedexSeen
+ predef FlagActionPredef ; mark this mon as seen in the pokedex
+ ld hl, wEnemyMonLevel
+ ld de, wEnemyMonUnmodifiedLevel
+ ld bc, 1 + NUM_STATS * 2
+ call CopyData
+ ld a, $7 ; default stat mod
+ ld b, NUM_STAT_MODS ; number of stat mods
+ ld hl, wEnemyMonStatMods
+.statModLoop
+ ld [hli], a
+ dec b
+ jr nz, .statModLoop
+ ret
+
+; calls BattleTransition to show the battle transition animation and initializes some battle variables
+DoBattleTransitionAndInitBattleVariables:
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ jr nz, .next
+; link battle
+ xor a
+ ld [wMenuJoypadPollCount], a
+ callab DisplayLinkBattleVersusTextBox
+ ld a, $1
+ ld [wUpdateSpritesEnabled], a
+ call ClearScreen
+.next
+ call DelayFrame
+ predef BattleTransition
+ callab LoadHudAndHpBarAndStatusTilePatterns
+ ld a, $1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ld a, $ff
+ ld [wUpdateSpritesEnabled], a
+ call ClearSprites
+ call ClearScreen
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ld [hWY], a
+ ld [rWY], a
+ ld [hTilesetType], a
+ ld hl, wPlayerStatsToDouble
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ ld [hl], a
+ ld [wPlayerDisabledMove], a
+ ret
+
+; swaps the level values of the BattleMon and EnemyMon structs
+SwapPlayerAndEnemyLevels:
+ push bc
+ ld a, [wBattleMonLevel]
+ ld b, a
+ ld a, [wEnemyMonLevel]
+ ld [wBattleMonLevel], a
+ ld a, b
+ ld [wEnemyMonLevel], a
+ pop bc
+ ret
+
+; loads either red back pic or old man back pic
+; also writes OAM data and loads tile patterns for the Red or Old Man back sprite's head
+; (for use when scrolling the player sprite and enemy's silhouettes on screen)
+LoadPlayerBackPic:
+ ld a, [wBattleType]
+ dec a ; is it the old man tutorial?
+ ld de, RedPicBack
+ jr nz, .next
+ ld de, OldManPic
+.next
+ ld a, BANK(RedPicBack)
+ call UncompressSpriteFromDE
+ predef ScaleSpriteByTwo
+ ld hl, wOAMBuffer
+ xor a
+ ld [hOAMTile], a ; initial tile number
+ ld b, $7 ; 7 columns
+ ld e, $a0 ; X for the left-most column
+.loop ; each loop iteration writes 3 OAM entries in a vertical column
+ ld c, $3 ; 3 tiles per column
+ ld d, $38 ; Y for the top of each column
+.innerLoop ; each loop iteration writes 1 OAM entry in the column
+ ld [hl], d ; OAM Y
+ inc hl
+ ld [hl], e ; OAM X
+ ld a, $8 ; height of tile
+ add d ; increase Y by height of tile
+ ld d, a
+ inc hl
+ ld a, [hOAMTile]
+ ld [hli], a ; OAM tile number
+ inc a ; increment tile number
+ ld [hOAMTile], a
+ inc hl
+ dec c
+ jr nz, .innerLoop
+ ld a, [hOAMTile]
+ add $4 ; increase tile number by 4
+ ld [hOAMTile], a
+ ld a, $8 ; width of tile
+ add e ; increase X by width of tile
+ ld e, a
+ dec b
+ jr nz, .loop
+ ld de, vBackPic
+ call InterlaceMergeSpriteBuffers
+ ld a, $a
+ ld [$0], a
+ xor a
+ ld [$4000], a
+ ld hl, vSprites
+ ld de, sSpriteBuffer1
+ ld a, [H_LOADEDROMBANK]
+ ld b, a
+ ld c, 7 * 7
+ call CopyVideoData
+ xor a
+ ld [$0], a
+ ld a, $31
+ ld [hStartTileID], a
+ coord hl, 1, 5
+ predef_jump CopyUncompressedPicToTilemap
+
+; does nothing since no stats are ever selected (barring glitches)
+DoubleOrHalveSelectedStats:
+ callab DoubleSelectedStats
+ jpab HalveSelectedStats
+
+ScrollTrainerPicAfterBattle:
+ jpab _ScrollTrainerPicAfterBattle
+
+ApplyBurnAndParalysisPenaltiesToPlayer:
+ ld a, $1
+ jr ApplyBurnAndParalysisPenalties
+
+ApplyBurnAndParalysisPenaltiesToEnemy:
+ xor a
+
+ApplyBurnAndParalysisPenalties:
+ ld [H_WHOSETURN], a
+ call QuarterSpeedDueToParalysis
+ jp HalveAttackDueToBurn
+
+QuarterSpeedDueToParalysis:
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .playerTurn
+.enemyTurn ; quarter the player's speed
+ ld a, [wBattleMonStatus]
+ and 1 << PAR
+ ret z ; return if player not paralysed
+ ld hl, wBattleMonSpeed + 1
+ ld a, [hld]
+ ld b, a
+ ld a, [hl]
+ srl a
+ rr b
+ srl a
+ rr b
+ ld [hli], a
+ or b
+ jr nz, .storePlayerSpeed
+ ld b, 1 ; give the player a minimum of at least one speed point
+.storePlayerSpeed
+ ld [hl], b
+ ret
+.playerTurn ; quarter the enemy's speed
+ ld a, [wEnemyMonStatus]
+ and 1 << PAR
+ ret z ; return if enemy not paralysed
+ ld hl, wEnemyMonSpeed + 1
+ ld a, [hld]
+ ld b, a
+ ld a, [hl]
+ srl a
+ rr b
+ srl a
+ rr b
+ ld [hli], a
+ or b
+ jr nz, .storeEnemySpeed
+ ld b, 1 ; give the enemy a minimum of at least one speed point
+.storeEnemySpeed
+ ld [hl], b
+ ret
+
+HalveAttackDueToBurn:
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .playerTurn
+.enemyTurn ; halve the player's attack
+ ld a, [wBattleMonStatus]
+ and 1 << BRN
+ ret z ; return if player not burnt
+ ld hl, wBattleMonAttack + 1
+ ld a, [hld]
+ ld b, a
+ ld a, [hl]
+ srl a
+ rr b
+ ld [hli], a
+ or b
+ jr nz, .storePlayerAttack
+ ld b, 1 ; give the player a minimum of at least one attack point
+.storePlayerAttack
+ ld [hl], b
+ ret
+.playerTurn ; halve the enemy's attack
+ ld a, [wEnemyMonStatus]
+ and 1 << BRN
+ ret z ; return if enemy not burnt
+ ld hl, wEnemyMonAttack + 1
+ ld a, [hld]
+ ld b, a
+ ld a, [hl]
+ srl a
+ rr b
+ ld [hli], a
+ or b
+ jr nz, .storeEnemyAttack
+ ld b, 1 ; give the enemy a minimum of at least one attack point
+.storeEnemyAttack
+ ld [hl], b
+ ret
+
+CalculateModifiedStats:
+ ld c, 0
+.loop
+ call CalculateModifiedStat
+ inc c
+ ld a, c
+ cp NUM_STATS - 1
+ jr nz, .loop
+ ret
+
+; calculate modified stat for stat c (0 = attack, 1 = defense, 2 = speed, 3 = special)
+CalculateModifiedStat:
+ push bc
+ push bc
+ ld a, [wCalculateWhoseStats]
+ and a
+ ld a, c
+ ld hl, wBattleMonAttack
+ ld de, wPlayerMonUnmodifiedAttack
+ ld bc, wPlayerMonStatMods
+ jr z, .next
+ ld hl, wEnemyMonAttack
+ ld de, wEnemyMonUnmodifiedAttack
+ ld bc, wEnemyMonStatMods
+.next
+ add c
+ ld c, a
+ jr nc, .noCarry1
+ inc b
+.noCarry1
+ ld a, [bc]
+ pop bc
+ ld b, a
+ push bc
+ sla c
+ ld b, 0
+ add hl, bc
+ ld a, c
+ add e
+ ld e, a
+ jr nc, .noCarry2
+ inc d
+.noCarry2
+ pop bc
+ push hl
+ ld hl, StatModifierRatios
+ dec b
+ sla b
+ ld c, b
+ ld b, 0
+ add hl, bc
+ xor a
+ ld [H_MULTIPLICAND], a
+ ld a, [de]
+ ld [H_MULTIPLICAND + 1], a
+ inc de
+ ld a, [de]
+ ld [H_MULTIPLICAND + 2], a
+ ld a, [hli]
+ ld [H_MULTIPLIER], a
+ call Multiply
+ ld a, [hl]
+ ld [H_DIVISOR], a
+ ld b, $4
+ call Divide
+ pop hl
+ ld a, [H_DIVIDEND + 3]
+ sub 999 % $100
+ ld a, [H_DIVIDEND + 2]
+ sbc 999 / $100
+ jp c, .storeNewStatValue
+; cap the stat at 999
+ ld a, 999 / $100
+ ld [H_DIVIDEND + 2], a
+ ld a, 999 % $100
+ ld [H_DIVIDEND + 3], a
+.storeNewStatValue
+ ld a, [H_DIVIDEND + 2]
+ ld [hli], a
+ ld b, a
+ ld a, [H_DIVIDEND + 3]
+ ld [hl], a
+ or b
+ jr nz, .done
+ inc [hl] ; if the stat is 0, bump it up to 1
+.done
+ pop bc
+ ret
+
+ApplyBadgeStatBoosts:
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ ret z ; return if link battle
+ ld a, [wObtainedBadges]
+ ld b, a
+ ld hl, wBattleMonAttack
+ ld c, $4
+; the boost is applied for badges whose bit position is even
+; the order of boosts matches the order they are laid out in RAM
+; Boulder (bit 0) - attack
+; Thunder (bit 2) - defense
+; Soul (bit 4) - speed
+; Volcano (bit 6) - special
+.loop
+ srl b
+ call c, .applyBoostToStat
+ inc hl
+ inc hl
+ srl b
+ dec c
+ jr nz, .loop
+ ret
+
+; multiply stat at hl by 1.125
+; cap stat at 999
+.applyBoostToStat
+ ld a, [hli]
+ ld d, a
+ ld e, [hl]
+ srl d
+ rr e
+ srl d
+ rr e
+ srl d
+ rr e
+ ld a, [hl]
+ add e
+ ld [hld], a
+ ld a, [hl]
+ adc d
+ ld [hli], a
+ ld a, [hld]
+ sub 999 % $100
+ ld a, [hl]
+ sbc 999 / $100
+ ret c
+ ld a, 999 / $100
+ ld [hli], a
+ ld a, 999 % $100
+ ld [hld], a
+ ret
+
+LoadHudAndHpBarAndStatusTilePatterns:
+ call LoadHpBarAndStatusTilePatterns
+
+LoadHudTilePatterns:
+ ld a, [rLCDC]
+ add a ; is LCD disabled?
+ jr c, .lcdEnabled
+.lcdDisabled
+ ld hl, BattleHudTiles1
+ ld de, vChars2 + $6d0
+ ld bc, BattleHudTiles1End - BattleHudTiles1
+ ld a, BANK(BattleHudTiles1)
+ call FarCopyDataDouble
+ ld hl, BattleHudTiles2
+ ld de, vChars2 + $730
+ ld bc, BattleHudTiles3End - BattleHudTiles2
+ ld a, BANK(BattleHudTiles2)
+ jp FarCopyDataDouble
+.lcdEnabled
+ ld de, BattleHudTiles1
+ ld hl, vChars2 + $6d0
+ lb bc, BANK(BattleHudTiles1), (BattleHudTiles1End - BattleHudTiles1) / $8
+ call CopyVideoDataDouble
+ ld de, BattleHudTiles2
+ ld hl, vChars2 + $730
+ lb bc, BANK(BattleHudTiles2), (BattleHudTiles3End - BattleHudTiles2) / $8
+ jp CopyVideoDataDouble
+
+PrintEmptyString:
+ ld hl, .emptyString
+ jp PrintText
+.emptyString
+ db "@"
+
+
+BattleRandom:
+; Link battles use a shared PRNG.
+
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ jp nz, Random
+
+ push hl
+ push bc
+ ld a, [wLinkBattleRandomNumberListIndex]
+ ld c, a
+ ld b, 0
+ ld hl, wLinkBattleRandomNumberList
+ add hl, bc
+ inc a
+ ld [wLinkBattleRandomNumberListIndex], a
+ cp 9
+ ld a, [hl]
+ pop bc
+ pop hl
+ ret c
+
+; if we picked the last seed, we need to recalculate the nine seeds
+ push hl
+ push bc
+ push af
+
+; point to seed 0 so we pick the first number the next time
+ xor a
+ ld [wLinkBattleRandomNumberListIndex], a
+
+ ld hl, wLinkBattleRandomNumberList
+ ld b, 9
+.loop
+ ld a, [hl]
+ ld c, a
+; multiply by 5
+ add a
+ add a
+ add c
+; add 1
+ inc a
+ ld [hli], a
+ dec b
+ jr nz, .loop
+
+ pop af
+ pop bc
+ pop hl
+ ret
+
+
+HandleExplodingAnimation:
+ ld a, [H_WHOSETURN]
+ and a
+ ld hl, wEnemyMonType1
+ ld de, wEnemyBattleStatus1
+ ld a, [wPlayerMoveNum]
+ jr z, .player
+ ld hl, wBattleMonType1
+ ld de, wEnemyBattleStatus1
+ ld a, [wEnemyMoveNum]
+.player
+ cp SELFDESTRUCT
+ jr z, .isExplodingMove
+ cp EXPLOSION
+ ret nz
+.isExplodingMove
+ ld a, [de]
+ bit Invulnerable, a ; fly/dig
+ ret nz
+ ld a, [hli]
+ cp GHOST
+ ret z
+ ld a, [hl]
+ cp GHOST
+ ret z
+ ld a, [wMoveMissed]
+ and a
+ ret nz
+ ld a, 5
+ ld [wAnimationType], a
+
+PlayMoveAnimation:
+ ld [wAnimationID],a
+ call Delay3
+ predef_jump MoveAnimation
+
+InitBattle:
+ ld a, [wCurOpponent]
+ and a
+ jr z, DetermineWildOpponent
+
+InitOpponent:
+ ld a, [wCurOpponent]
+ ld [wcf91], a
+ ld [wEnemyMonSpecies2], a
+ jr InitBattleCommon
+
+DetermineWildOpponent:
+ ld a, [wd732]
+ bit 1, a
+ jr z, .asm_3ef2f
+ ld a, [hJoyHeld]
+ bit 1, a ; B button pressed?
+ ret nz
+.asm_3ef2f
+ ld a, [wNumberOfNoRandomBattleStepsLeft]
+ and a
+ ret nz
+ callab TryDoWildEncounter
+ ret nz
+InitBattleCommon:
+ ld a, [wMapPalOffset]
+ push af
+ ld hl, wLetterPrintingDelayFlags
+ ld a, [hl]
+ push af
+ res 1, [hl]
+ callab InitBattleVariables
+ ld a, [wEnemyMonSpecies2]
+ sub 200
+ jp c, InitWildBattle
+ ld [wTrainerClass], a
+ call GetTrainerInformation
+ callab ReadTrainer
+ call DoBattleTransitionAndInitBattleVariables
+ call _LoadTrainerPic
+ xor a
+ ld [wEnemyMonSpecies2], a
+ ld [hStartTileID], a
+ dec a
+ ld [wAICount], a
+ coord hl, 12, 0
+ predef CopyUncompressedPicToTilemap
+ ld a, $ff
+ ld [wEnemyMonPartyPos], a
+ ld a, $2
+ ld [wIsInBattle], a
+ jp _InitBattleCommon
+
+InitWildBattle:
+ ld a, $1
+ ld [wIsInBattle], a
+ call LoadEnemyMonData
+ call DoBattleTransitionAndInitBattleVariables
+ ld a, [wCurOpponent]
+ cp MAROWAK
+ jr z, .isGhost
+ call IsGhostBattle
+ jr nz, .isNoGhost
+.isGhost
+ ld hl, wMonHSpriteDim
+ ld a, $66
+ ld [hli], a ; write sprite dimensions
+ ld bc, GhostPic
+ ld a, c
+ ld [hli], a ; write front sprite pointer
+ ld [hl], b
+ ld hl, wEnemyMonNick ; set name to "GHOST"
+ ld a, "G"
+ ld [hli], a
+ ld a, "E"
+ ld [hli], a
+ ld a, "I"
+ ld [hli], a
+ ld a, "S"
+ ld [hli], a
+ ld a, "T"
+ ld [hli], a
+ ld [hl], "@"
+ ld a, [wcf91]
+ push af
+ ld a, MON_GHOST
+ ld [wcf91], a
+ ld de, vFrontPic
+ call LoadMonFrontSprite ; load ghost sprite
+ pop af
+ ld [wcf91], a
+ jr .spriteLoaded
+.isNoGhost
+ ld de, vFrontPic
+ call LoadMonFrontSprite ; load mon sprite
+.spriteLoaded
+ xor a
+ ld [wTrainerClass], a
+ ld [hStartTileID], a
+ coord hl, 12, 0
+ predef CopyUncompressedPicToTilemap
+
+; common code that executes after init battle code specific to trainer or wild battles
+_InitBattleCommon:
+ ld b, SET_PAL_BATTLE_BLACK
+ call RunPaletteCommand
+ call SlidePlayerAndEnemySilhouettesOnScreen
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ld hl, .emptyString
+ call PrintText
+ call SaveScreenTilesToBuffer1
+ call ClearScreen
+ ld a, $98
+ ld [H_AUTOBGTRANSFERDEST + 1], a
+ ld a, $1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call Delay3
+ ld a, $9c
+ ld [H_AUTOBGTRANSFERDEST + 1], a
+ call LoadScreenTilesFromBuffer1
+ coord hl, 9, 7
+ lb bc, 5, 10
+ call ClearScreenArea
+ coord hl, 1, 0
+ lb bc, 4, 10
+ call ClearScreenArea
+ call ClearSprites
+ ld a, [wIsInBattle]
+ dec a ; is it a wild battle?
+ call z, DrawEnemyHUDAndHPBar ; draw enemy HUD and HP bar if it's a wild battle
+ call StartBattle
+ callab EndOfBattle
+ pop af
+ ld [wLetterPrintingDelayFlags], a
+ pop af
+ ld [wMapPalOffset], a
+ ld a, [wSavedTilesetType]
+ ld [hTilesetType], a
+ scf
+ ret
+.emptyString
+ db "@"
+
+_LoadTrainerPic:
+; wd033-wd034 contain pointer to pic
+ ld a, [wTrainerPicPointer]
+ ld e, a
+ ld a, [wTrainerPicPointer + 1]
+ ld d, a ; de contains pointer to trainer pic
+ ld a, [wLinkState]
+ and a
+ ld a, Bank(TrainerPics) ; this is where all the trainer pics are (not counting Red's)
+ jr z, .loadSprite
+ ld a, Bank(RedPicFront)
+.loadSprite
+ call UncompressSpriteFromDE
+ ld de, vFrontPic
+ ld a, $77
+ ld c, a
+ jp LoadUncompressedSpriteData
+
+; unreferenced
+ResetCryModifiers:
+ xor a
+ ld [wFrequencyModifier], a
+ ld [wTempoModifier], a
+ jp PlaySound
+
+; animates the mon "growing" out of the pokeball
+AnimateSendingOutMon:
+ ld a, [wPredefRegisters]
+ ld h, a
+ ld a, [wPredefRegisters + 1]
+ ld l, a
+ ld a, [hStartTileID]
+ ld [hBaseTileID], a
+ ld b, $4c
+ ld a, [wIsInBattle]
+ and a
+ jr z, .notInBattle
+ add b
+ ld [hl], a
+ call Delay3
+ ld bc, -(SCREEN_WIDTH * 2 + 1)
+ add hl, bc
+ ld a, 1
+ ld [wDownscaledMonSize], a
+ lb bc, 3, 3
+ predef CopyDownscaledMonTiles
+ ld c, 4
+ call DelayFrames
+ ld bc, -(SCREEN_WIDTH * 2 + 1)
+ add hl, bc
+ xor a
+ ld [wDownscaledMonSize], a
+ lb bc, 5, 5
+ predef CopyDownscaledMonTiles
+ ld c, 5
+ call DelayFrames
+ ld bc, -(SCREEN_WIDTH * 2 + 1)
+ jr .next
+.notInBattle
+ ld bc, -(SCREEN_WIDTH * 6 + 3)
+.next
+ add hl, bc
+ ld a, [hBaseTileID]
+ add $31
+ jr CopyUncompressedPicToHL
+
+CopyUncompressedPicToTilemap:
+ ld a, [wPredefRegisters]
+ ld h, a
+ ld a, [wPredefRegisters + 1]
+ ld l, a
+ ld a, [hStartTileID]
+CopyUncompressedPicToHL:
+ lb bc, 7, 7
+ ld de, SCREEN_WIDTH
+ push af
+ ld a, [wSpriteFlipped]
+ and a
+ jr nz, .flipped
+ pop af
+.loop
+ push bc
+ push hl
+.innerLoop
+ ld [hl], a
+ add hl, de
+ inc a
+ dec c
+ jr nz, .innerLoop
+ pop hl
+ inc hl
+ pop bc
+ dec b
+ jr nz, .loop
+ ret
+
+.flipped
+ push bc
+ ld b, 0
+ dec c
+ add hl, bc
+ pop bc
+ pop af
+.flippedLoop
+ push bc
+ push hl
+.flippedInnerLoop
+ ld [hl], a
+ add hl, de
+ inc a
+ dec c
+ jr nz, .flippedInnerLoop
+ pop hl
+ dec hl
+ pop bc
+ dec b
+ jr nz, .flippedLoop
+ ret
+
+LoadMonBackPic:
+; Assumes the monster's attributes have
+; been loaded with GetMonHeader.
+ ld a, [wBattleMonSpecies2]
+ ld [wcf91], a
+ coord hl, 1, 5
+ ld b, 7
+ ld c, 8
+ call ClearScreenArea
+ ld hl, wMonHBackSprite - wMonHeader
+ call UncompressMonSprite
+ predef ScaleSpriteByTwo
+ ld de, vBackPic
+ call InterlaceMergeSpriteBuffers ; combine the two buffers to a single 2bpp sprite
+ ld hl, vSprites
+ ld de, vBackPic
+ ld c, (2*SPRITEBUFFERSIZE)/16 ; count of 16-byte chunks to be copied
+ ld a, [H_LOADEDROMBANK]
+ ld b, a
+ jp CopyVideoData
+
+JumpMoveEffect:
+ call _JumpMoveEffect
+ ld b, $1
+ ret
+
+_JumpMoveEffect:
+ ld a, [H_WHOSETURN]
+ and a
+ ld a, [wPlayerMoveEffect]
+ jr z, .next1
+ ld a, [wEnemyMoveEffect]
+.next1
+ dec a ; subtract 1, there is no special effect for 00
+ add a ; x2, 16bit pointers
+ ld hl, MoveEffectPointerTable
+ ld b, 0
+ ld c, a
+ add hl, bc
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ jp hl ; jump to special effect handler
+
+MoveEffectPointerTable:
+ dw SleepEffect ; unused effect
+ dw PoisonEffect ; POISON_SIDE_EFFECT1
+ dw DrainHPEffect ; DRAIN_HP_EFFECT
+ dw FreezeBurnParalyzeEffect ; BURN_SIDE_EFFECT1
+ dw FreezeBurnParalyzeEffect ; FREEZE_SIDE_EFFECT
+ dw FreezeBurnParalyzeEffect ; PARALYZE_SIDE_EFFECT1
+ dw ExplodeEffect ; EXPLODE_EFFECT
+ dw DrainHPEffect ; DREAM_EATER_EFFECT
+ dw $0000 ; MIRROR_MOVE_EFFECT
+ dw StatModifierUpEffect ; ATTACK_UP1_EFFECT
+ dw StatModifierUpEffect ; DEFENSE_UP1_EFFECT
+ dw StatModifierUpEffect ; SPEED_UP1_EFFECT
+ dw StatModifierUpEffect ; SPECIAL_UP1_EFFECT
+ dw StatModifierUpEffect ; ACCURACY_UP1_EFFECT
+ dw StatModifierUpEffect ; EVASION_UP1_EFFECT
+ dw PayDayEffect ; PAY_DAY_EFFECT
+ dw $0000 ; SWIFT_EFFECT
+ dw StatModifierDownEffect ; ATTACK_DOWN1_EFFECT
+ dw StatModifierDownEffect ; DEFENSE_DOWN1_EFFECT
+ dw StatModifierDownEffect ; SPEED_DOWN1_EFFECT
+ dw StatModifierDownEffect ; SPECIAL_DOWN1_EFFECT
+ dw StatModifierDownEffect ; ACCURACY_DOWN1_EFFECT
+ dw StatModifierDownEffect ; EVASION_DOWN1_EFFECT
+ dw ConversionEffect ; CONVERSION_EFFECT
+ dw HazeEffect ; HAZE_EFFECT
+ dw BideEffect ; BIDE_EFFECT
+ dw ThrashPetalDanceEffect ; THRASH_PETAL_DANCE_EFFECT
+ dw SwitchAndTeleportEffect ; SWITCH_AND_TELEPORT_EFFECT
+ dw TwoToFiveAttacksEffect ; TWO_TO_FIVE_ATTACKS_EFFECT
+ dw TwoToFiveAttacksEffect ; unused effect
+ dw FlinchSideEffect ; FLINCH_SIDE_EFFECT1
+ dw SleepEffect ; SLEEP_EFFECT
+ dw PoisonEffect ; POISON_SIDE_EFFECT2
+ dw FreezeBurnParalyzeEffect ; BURN_SIDE_EFFECT2
+ dw FreezeBurnParalyzeEffect ; unused effect
+ dw FreezeBurnParalyzeEffect ; PARALYZE_SIDE_EFFECT2
+ dw FlinchSideEffect ; FLINCH_SIDE_EFFECT2
+ dw OneHitKOEffect ; OHKO_EFFECT
+ dw ChargeEffect ; CHARGE_EFFECT
+ dw $0000 ; SUPER_FANG_EFFECT
+ dw $0000 ; SPECIAL_DAMAGE_EFFECT
+ dw TrappingEffect ; TRAPPING_EFFECT
+ dw ChargeEffect ; FLY_EFFECT
+ dw TwoToFiveAttacksEffect ; ATTACK_TWICE_EFFECT
+ dw $0000 ; JUMP_KICK_EFFECT
+ dw MistEffect ; MIST_EFFECT
+ dw FocusEnergyEffect ; FOCUS_ENERGY_EFFECT
+ dw RecoilEffect ; RECOIL_EFFECT
+ dw ConfusionEffect ; CONFUSION_EFFECT
+ dw StatModifierUpEffect ; ATTACK_UP2_EFFECT
+ dw StatModifierUpEffect ; DEFENSE_UP2_EFFECT
+ dw StatModifierUpEffect ; SPEED_UP2_EFFECT
+ dw StatModifierUpEffect ; SPECIAL_UP2_EFFECT
+ dw StatModifierUpEffect ; ACCURACY_UP2_EFFECT
+ dw StatModifierUpEffect ; EVASION_UP2_EFFECT
+ dw HealEffect ; HEAL_EFFECT
+ dw TransformEffect ; TRANSFORM_EFFECT
+ dw StatModifierDownEffect ; ATTACK_DOWN2_EFFECT
+ dw StatModifierDownEffect ; DEFENSE_DOWN2_EFFECT
+ dw StatModifierDownEffect ; SPEED_DOWN2_EFFECT
+ dw StatModifierDownEffect ; SPECIAL_DOWN2_EFFECT
+ dw StatModifierDownEffect ; ACCURACY_DOWN2_EFFECT
+ dw StatModifierDownEffect ; EVASION_DOWN2_EFFECT
+ dw ReflectLightScreenEffect ; LIGHT_SCREEN_EFFECT
+ dw ReflectLightScreenEffect ; REFLECT_EFFECT
+ dw PoisonEffect ; POISON_EFFECT
+ dw ParalyzeEffect ; PARALYZE_EFFECT
+ dw StatModifierDownEffect ; ATTACK_DOWN_SIDE_EFFECT
+ dw StatModifierDownEffect ; DEFENSE_DOWN_SIDE_EFFECT
+ dw StatModifierDownEffect ; SPEED_DOWN_SIDE_EFFECT
+ dw StatModifierDownEffect ; SPECIAL_DOWN_SIDE_EFFECT
+ dw StatModifierDownEffect ; unused effect
+ dw StatModifierDownEffect ; unused effect
+ dw StatModifierDownEffect ; unused effect
+ dw StatModifierDownEffect ; unused effect
+ dw ConfusionSideEffect ; CONFUSION_SIDE_EFFECT
+ dw TwoToFiveAttacksEffect ; TWINEEDLE_EFFECT
+ dw $0000 ; unused effect
+ dw SubstituteEffect ; SUBSTITUTE_EFFECT
+ dw HyperBeamEffect ; HYPER_BEAM_EFFECT
+ dw RageEffect ; RAGE_EFFECT
+ dw MimicEffect ; MIMIC_EFFECT
+ dw $0000 ; METRONOME_EFFECT
+ dw LeechSeedEffect ; LEECH_SEED_EFFECT
+ dw SplashEffect ; SPLASH_EFFECT
+ dw DisableEffect ; DISABLE_EFFECT
+
+SleepEffect:
+ ld de, wEnemyMonStatus
+ ld bc, wEnemyBattleStatus2
+ ld a, [H_WHOSETURN]
+ and a
+ jp z, .sleepEffect
+ ld de, wBattleMonStatus
+ ld bc, wPlayerBattleStatus2
+
+.sleepEffect
+ ld a, [bc]
+ bit NeedsToRecharge, a ; does the target need to recharge? (hyper beam)
+ res NeedsToRecharge, a ; target no longer needs to recharge
+ ld [bc], a
+ jr nz, .setSleepCounter ; if the target had to recharge, all hit tests will be skipped
+ ; including the event where the target already has another status
+ ld a, [de]
+ ld b, a
+ and $7
+ jr z, .notAlreadySleeping ; can't affect a mon that is already asleep
+ ld hl, AlreadyAsleepText
+ jp PrintText
+.notAlreadySleeping
+ ld a, b
+ and a
+ jr nz, .didntAffect ; can't affect a mon that is already statused
+ push de
+ call MoveHitTest ; apply accuracy tests
+ pop de
+ ld a, [wMoveMissed]
+ and a
+ jr nz, .didntAffect
+.setSleepCounter
+; set target's sleep counter to a random number between 1 and 7
+ call BattleRandom
+ and $7
+ jr z, .setSleepCounter
+ ld [de], a
+ call PlayCurrentMoveAnimation2
+ ld hl, FellAsleepText
+ jp PrintText
+.didntAffect
+ jp PrintDidntAffectText
+
+FellAsleepText:
+ TX_FAR _FellAsleepText
+ db "@"
+
+AlreadyAsleepText:
+ TX_FAR _AlreadyAsleepText
+ db "@"
+
+PoisonEffect:
+ ld hl, wEnemyMonStatus
+ ld de, wPlayerMoveEffect
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .poisonEffect
+ ld hl, wBattleMonStatus
+ ld de, wEnemyMoveEffect
+.poisonEffect
+ call CheckTargetSubstitute
+ jr nz, .noEffect ; can't poison a substitute target
+ ld a, [hli]
+ ld b, a
+ and a
+ jr nz, .noEffect ; miss if target is already statused
+ ld a, [hli]
+ cp POISON ; can't poison a poison-type target
+ jr z, .noEffect
+ ld a, [hld]
+ cp POISON ; can't poison a poison-type target
+ jr z, .noEffect
+ ld a, [de]
+ cp POISON_SIDE_EFFECT1
+ ld b, $34 ; ~20% chance of poisoning
+ jr z, .sideEffectTest
+ cp POISON_SIDE_EFFECT2
+ ld b, $67 ; ~40% chance of poisoning
+ jr z, .sideEffectTest
+ push hl
+ push de
+ call MoveHitTest ; apply accuracy tests
+ pop de
+ pop hl
+ ld a, [wMoveMissed]
+ and a
+ jr nz, .didntAffect
+ jr .inflictPoison
+.sideEffectTest
+ call BattleRandom
+ cp b ; was side effect successful?
+ ret nc
+.inflictPoison
+ dec hl
+ set 3, [hl] ; mon is now poisoned
+ push de
+ dec de
+ ld a, [H_WHOSETURN]
+ and a
+ ld b, ANIM_C7
+ ld hl, wPlayerBattleStatus3
+ ld a, [de]
+ ld de, wPlayerToxicCounter
+ jr nz, .ok
+ ld b, ANIM_A9
+ ld hl, wEnemyBattleStatus3
+ ld de, wEnemyToxicCounter
+.ok
+ cp TOXIC
+ jr nz, .normalPoison ; done if move is not Toxic
+ set BadlyPoisoned, [hl] ; else set Toxic battstatus
+ xor a
+ ld [de], a
+ ld hl, BadlyPoisonedText
+ jr .continue
+.normalPoison
+ ld hl, PoisonedText
+.continue
+ pop de
+ ld a, [de]
+ cp POISON_EFFECT
+ jr z, .regularPoisonEffect
+ ld a, b
+ call PlayBattleAnimation2
+ jp PrintText
+.regularPoisonEffect
+ call PlayCurrentMoveAnimation2
+ jp PrintText
+.noEffect
+ ld a, [de]
+ cp POISON_EFFECT
+ ret nz
+.didntAffect
+ ld c, 50
+ call DelayFrames
+ jp PrintDidntAffectText
+
+PoisonedText:
+ TX_FAR _PoisonedText
+ db "@"
+
+BadlyPoisonedText:
+ TX_FAR _BadlyPoisonedText
+ db "@"
+
+DrainHPEffect:
+ jpab DrainHPEffect_
+
+ExplodeEffect:
+ ld hl, wBattleMonHP
+ ld de, wPlayerBattleStatus2
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .faintUser
+ ld hl, wEnemyMonHP
+ ld de, wEnemyBattleStatus2
+.faintUser
+ xor a
+ ld [hli], a ; set the mon's HP to 0
+ ld [hli], a
+ inc hl
+ ld [hl], a ; set mon's status to 0
+ ld a, [de]
+ res Seeded, a ; clear mon's leech seed status
+ ld [de], a
+ ret
+
+FreezeBurnParalyzeEffect:
+ xor a
+ ld [wAnimationType], a
+ call CheckTargetSubstitute ; test bit 4 of d063/d068 flags [target has substitute flag]
+ ret nz ; return if they have a substitute, can't effect them
+ ld a, [H_WHOSETURN]
+ and a
+ jp nz, opponentAttacker
+ ld a, [wEnemyMonStatus]
+ and a
+ jp nz, CheckDefrost ; can't inflict status if opponent is already statused
+ ld a, [wPlayerMoveType]
+ ld b, a
+ ld a, [wEnemyMonType1]
+ cp b ; do target type 1 and move type match?
+ ret z ; return if they match (an ice move can't freeze an ice-type, body slam can't paralyze a normal-type, etc.)
+ ld a, [wEnemyMonType2]
+ cp b ; do target type 2 and move type match?
+ ret z ; return if they match
+ ld a, [wPlayerMoveEffect]
+ cp a, PARALYZE_SIDE_EFFECT1 + 1 ; 10% status effects are 04, 05, 06 so 07 will set carry for those
+ ld b, $1a ; 0x1A/0x100 or 26/256 = 10.2%~ chance
+ jr c, .next1 ; branch ahead if this is a 10% chance effect..
+ ld b, $4d ; else use 0x4D/0x100 or 77/256 = 30.1%~ chance
+ sub a, $1e ; subtract $1E to map to equivalent 10% chance effects
+.next1
+ push af
+ call BattleRandom ; get random 8bit value for probability test
+ cp b
+ pop bc
+ ret nc ; do nothing if random value is >= 1A or 4D [no status applied]
+ ld a, b ; what type of effect is this?
+ cp a, BURN_SIDE_EFFECT1
+ jr z, .burn
+ cp a, FREEZE_SIDE_EFFECT
+ jr z, .freeze
+; .paralyze
+ ld a, 1 << PAR
+ ld [wEnemyMonStatus], a
+ call QuarterSpeedDueToParalysis ; quarter speed of affected mon
+ ld a, ANIM_A9
+ call PlayBattleAnimation
+ jp PrintMayNotAttackText ; print paralysis text
+.burn
+ ld a, 1 << BRN
+ ld [wEnemyMonStatus], a
+ call HalveAttackDueToBurn ; halve attack of affected mon
+ ld a, ANIM_A9
+ call PlayBattleAnimation
+ ld hl, BurnedText
+ jp PrintText
+.freeze
+ call ClearHyperBeam ; resets hyper beam (recharge) condition from target
+ ld a, 1 << FRZ
+ ld [wEnemyMonStatus], a
+ ld a, ANIM_A9
+ call PlayBattleAnimation
+ ld hl, FrozenText
+ jp PrintText
+opponentAttacker:
+ ld a, [wBattleMonStatus] ; mostly same as above with addresses swapped for opponent
+ and a
+ jp nz, CheckDefrost
+ ld a, [wEnemyMoveType]
+ ld b, a
+ ld a, [wBattleMonType1]
+ cp b
+ ret z
+ ld a, [wBattleMonType2]
+ cp b
+ ret z
+ ld a, [wEnemyMoveEffect]
+ cp a, PARALYZE_SIDE_EFFECT1 + 1
+ ld b, $1a
+ jr c, .next1
+ ld b, $4d
+ sub a, $1e
+.next1
+ push af
+ call BattleRandom
+ cp b
+ pop bc
+ ret nc
+ ld a, b
+ cp a, BURN_SIDE_EFFECT1
+ jr z, .burn
+ cp a, FREEZE_SIDE_EFFECT
+ jr z, .freeze
+ ld a, 1 << PAR
+ ld [wBattleMonStatus], a
+ call QuarterSpeedDueToParalysis
+ jp PrintMayNotAttackText
+.burn
+ ld a, 1 << BRN
+ ld [wBattleMonStatus], a
+ call HalveAttackDueToBurn
+ ld hl, BurnedText
+ jp PrintText
+.freeze
+; hyper beam bits aren't reseted for opponent's side
+ ld a, 1 << FRZ
+ ld [wBattleMonStatus], a
+ ld hl, FrozenText
+ jp PrintText
+
+BurnedText:
+ TX_FAR _BurnedText
+ db "@"
+
+FrozenText:
+ TX_FAR _FrozenText
+ db "@"
+
+CheckDefrost:
+; any fire-type move that has a chance inflict burn (all but Fire Spin) will defrost a frozen target
+ and a, 1 << FRZ ; are they frozen?
+ ret z ; return if so
+ ld a, [H_WHOSETURN]
+ and a
+ jr nz, .opponent
+ ;player [attacker]
+ ld a, [wPlayerMoveType]
+ sub a, FIRE
+ ret nz ; return if type of move used isn't fire
+ ld [wEnemyMonStatus], a ; set opponent status to 00 ["defrost" a frozen monster]
+ ld hl, wEnemyMon1Status
+ ld a, [wEnemyMonPartyPos]
+ ld bc, wEnemyMon2 - wEnemyMon1
+ call AddNTimes
+ xor a
+ ld [hl], a ; clear status in roster
+ ld hl, FireDefrostedText
+ jr .common
+.opponent
+ ld a, [wEnemyMoveType] ; same as above with addresses swapped
+ sub a, FIRE
+ ret nz
+ ld [wBattleMonStatus], a
+ ld hl, wPartyMon1Status
+ ld a, [wPlayerMonNumber]
+ ld bc, wPartyMon2 - wPartyMon1
+ call AddNTimes
+ xor a
+ ld [hl], a
+ ld hl, FireDefrostedText
+.common
+ jp PrintText
+
+FireDefrostedText:
+ TX_FAR _FireDefrostedText
+ db "@"
+
+StatModifierUpEffect:
+ ld hl, wPlayerMonStatMods
+ ld de, wPlayerMoveEffect
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .statModifierUpEffect
+ ld hl, wEnemyMonStatMods
+ ld de, wEnemyMoveEffect
+.statModifierUpEffect
+ ld a, [de]
+ sub ATTACK_UP1_EFFECT
+ cp EVASION_UP1_EFFECT + $3 - ATTACK_UP1_EFFECT ; covers all +1 effects
+ jr c, .incrementStatMod
+ sub ATTACK_UP2_EFFECT - ATTACK_UP1_EFFECT ; map +2 effects to equivalent +1 effect
+.incrementStatMod
+ ld c, a
+ ld b, $0
+ add hl, bc
+ ld b, [hl]
+ inc b ; increment corresponding stat mod
+ ld a, $d
+ cp b ; can't raise stat past +6 ($d or 13)
+ jp c, PrintNothingHappenedText
+ ld a, [de]
+ cp ATTACK_UP1_EFFECT + $8 ; is it a +2 effect?
+ jr c, .ok
+ inc b ; if so, increment stat mod again
+ ld a, $d
+ cp b ; unless it's already +6
+ jr nc, .ok
+ ld b, a
+.ok
+ ld [hl], b
+ ld a, c
+ cp $4
+ jr nc, UpdateStatDone ; jump if mod affected is evasion/accuracy
+ push hl
+ ld hl, wBattleMonAttack + 1
+ ld de, wPlayerMonUnmodifiedAttack
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .pointToStats
+ ld hl, wEnemyMonAttack + 1
+ ld de, wEnemyMonUnmodifiedAttack
+.pointToStats
+ push bc
+ sla c
+ ld b, $0
+ add hl, bc ; hl = modified stat
+ ld a, c
+ add e
+ ld e, a
+ jr nc, .checkIf999
+ inc d ; de = unmodified (original) stat
+.checkIf999
+ pop bc
+ ld a, [hld]
+ sub 999 % $100 ; check if stat is already 999
+ jr nz, .recalculateStat
+ ld a, [hl]
+ sbc 999 / $100
+ jp z, RestoreOriginalStatModifier
+.recalculateStat ; recalculate affected stat
+ ; paralysis and burn penalties, as well as badge boosts are ignored
+ push hl
+ push bc
+ ld hl, StatModifierRatios
+ dec b
+ sla b
+ ld c, b
+ ld b, $0
+ add hl, bc
+ pop bc
+ xor a
+ ld [H_MULTIPLICAND], a
+ ld a, [de]
+ ld [H_MULTIPLICAND + 1], a
+ inc de
+ ld a, [de]
+ ld [H_MULTIPLICAND + 2], a
+ ld a, [hli]
+ ld [H_MULTIPLIER], a
+ call Multiply
+ ld a, [hl]
+ ld [H_DIVISOR], a
+ ld b, $4
+ call Divide
+ pop hl
+; cap at 999
+ ld a, [H_PRODUCT + 3]
+ sub 999 % $100
+ ld a, [H_PRODUCT + 2]
+ sbc 999 / $100
+ jp c, UpdateStat
+ ld a, 999 / $100
+ ld [H_MULTIPLICAND + 1], a
+ ld a, 999 % $100
+ ld [H_MULTIPLICAND + 2], a
+
+UpdateStat:
+ ld a, [H_PRODUCT + 2]
+ ld [hli], a
+ ld a, [H_PRODUCT + 3]
+ ld [hl], a
+ pop hl
+UpdateStatDone:
+ ld b, c
+ inc b
+ call PrintStatText
+ ld hl, wPlayerBattleStatus2
+ ld de, wPlayerMoveNum
+ ld bc, wPlayerMonMinimized
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .asm_3f4e6
+ ld hl, wEnemyBattleStatus2
+ ld de, wEnemyMoveNum
+ ld bc, wEnemyMonMinimized
+.asm_3f4e6
+ ld a, [de]
+ cp MINIMIZE
+ jr nz, .asm_3f4f9
+ ; if a substitute is up, slide off the substitute and show the mon pic before
+ ; playing the minimize animation
+ bit HasSubstituteUp, [hl]
+ push af
+ push bc
+ ld hl, HideSubstituteShowMonAnim
+ ld b, BANK(HideSubstituteShowMonAnim)
+ push de
+ call nz, Bankswitch
+ pop de
+.asm_3f4f9
+ call PlayCurrentMoveAnimation
+ ld a, [de]
+ cp MINIMIZE
+ jr nz, .applyBadgeBoostsAndStatusPenalties
+ pop bc
+ ld a, $1
+ ld [bc], a
+ ld hl, ReshowSubstituteAnim
+ ld b, BANK(ReshowSubstituteAnim)
+ pop af
+ call nz, Bankswitch
+.applyBadgeBoostsAndStatusPenalties
+ ld a, [H_WHOSETURN]
+ and a
+ call z, ApplyBadgeStatBoosts ; whenever the player uses a stat-up move, badge boosts get reapplied again to every stat,
+ ; even to those not affected by the stat-up move (will be boosted further)
+ ld hl, MonsStatsRoseText
+ call PrintText
+
+; these shouldn't be here
+ call QuarterSpeedDueToParalysis ; apply speed penalty to the player whose turn is not, if it's paralyzed
+ jp HalveAttackDueToBurn ; apply attack penalty to the player whose turn is not, if it's burned
+
+RestoreOriginalStatModifier:
+ pop hl
+ dec [hl]
+
+PrintNothingHappenedText:
+ ld hl, NothingHappenedText
+ jp PrintText
+
+MonsStatsRoseText:
+ TX_FAR _MonsStatsRoseText
+ TX_ASM
+ ld hl, GreatlyRoseText
+ ld a, [H_WHOSETURN]
+ and a
+ ld a, [wPlayerMoveEffect]
+ jr z, .playerTurn
+ ld a, [wEnemyMoveEffect]
+.playerTurn
+ cp ATTACK_DOWN1_EFFECT
+ ret nc
+ ld hl, RoseText
+ ret
+
+GreatlyRoseText:
+ TX_DELAY
+ TX_FAR _GreatlyRoseText
+; fallthrough
+RoseText:
+ TX_FAR _RoseText
+ db "@"
+
+StatModifierDownEffect:
+ ld hl, wEnemyMonStatMods
+ ld de, wPlayerMoveEffect
+ ld bc, wEnemyBattleStatus1
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .statModifierDownEffect
+ ld hl, wPlayerMonStatMods
+ ld de, wEnemyMoveEffect
+ ld bc, wPlayerBattleStatus1
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ jr z, .statModifierDownEffect
+ call BattleRandom
+ cp $40 ; 1/4 chance to miss by in regular battle
+ jp c, MoveMissed
+.statModifierDownEffect
+ call CheckTargetSubstitute ; can't hit through substitute
+ jp nz, MoveMissed
+ ld a, [de]
+ cp ATTACK_DOWN_SIDE_EFFECT
+ jr c, .nonSideEffect
+ call BattleRandom
+ cp $55 ; 85/256 chance for side effects
+ jp nc, CantLowerAnymore
+ ld a, [de]
+ sub ATTACK_DOWN_SIDE_EFFECT ; map each stat to 0-3
+ jr .decrementStatMod
+.nonSideEffect ; non-side effects only
+ push hl
+ push de
+ push bc
+ call MoveHitTest ; apply accuracy tests
+ pop bc
+ pop de
+ pop hl
+ ld a, [wMoveMissed]
+ and a
+ jp nz, MoveMissed
+ ld a, [bc]
+ bit Invulnerable, a ; fly/dig
+ jp nz, MoveMissed
+ ld a, [de]
+ sub ATTACK_DOWN1_EFFECT
+ cp EVASION_DOWN1_EFFECT + $3 - ATTACK_DOWN1_EFFECT ; covers all -1 effects
+ jr c, .decrementStatMod
+ sub ATTACK_DOWN2_EFFECT - ATTACK_DOWN1_EFFECT ; map -2 effects to corresponding -1 effect
+.decrementStatMod
+ ld c, a
+ ld b, $0
+ add hl, bc
+ ld b, [hl]
+ dec b ; dec corresponding stat mod
+ jp z, CantLowerAnymore ; if stat mod is 1 (-6), can't lower anymore
+ ld a, [de]
+ cp ATTACK_DOWN2_EFFECT - $16 ; $24
+ jr c, .ok
+ cp EVASION_DOWN2_EFFECT + $5 ; $44
+ jr nc, .ok
+ dec b ; stat down 2 effects only (dec mod again)
+ jr nz, .ok
+ inc b ; increment mod to 1 (-6) if it would become 0 (-7)
+.ok
+ ld [hl], b ; save modified mod
+ ld a, c
+ cp $4
+ jr nc, UpdateLoweredStatDone ; jump for evasion/accuracy
+ push hl
+ push de
+ ld hl, wEnemyMonAttack + 1
+ ld de, wEnemyMonUnmodifiedAttack
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .pointToStat
+ ld hl, wBattleMonAttack + 1
+ ld de, wPlayerMonUnmodifiedAttack
+.pointToStat
+ push bc
+ sla c
+ ld b, $0
+ add hl, bc ; hl = modified stat
+ ld a, c
+ add e
+ ld e, a
+ jr nc, .noCarry
+ inc d ; de = unmodified stat
+.noCarry
+ pop bc
+ ld a, [hld]
+ sub $1 ; can't lower stat below 1 (-6)
+ jr nz, .recalculateStat
+ ld a, [hl]
+ and a
+ jp z, CantLowerAnymore_Pop
+.recalculateStat
+; recalculate affected stat
+; paralysis and burn penalties, as well as badge boosts are ignored
+ push hl
+ push bc
+ ld hl, StatModifierRatios
+ dec b
+ sla b
+ ld c, b
+ ld b, $0
+ add hl, bc
+ pop bc
+ xor a
+ ld [H_MULTIPLICAND], a
+ ld a, [de]
+ ld [H_MULTIPLICAND + 1], a
+ inc de
+ ld a, [de]
+ ld [H_MULTIPLICAND + 2], a
+ ld a, [hli]
+ ld [H_MULTIPLIER], a
+ call Multiply
+ ld a, [hl]
+ ld [H_DIVISOR], a
+ ld b, $4
+ call Divide
+ pop hl
+ ld a, [H_PRODUCT + 3]
+ ld b, a
+ ld a, [H_PRODUCT + 2]
+ or b
+ jp nz, UpdateLoweredStat
+ ld [H_MULTIPLICAND + 1], a
+ ld a, $1
+ ld [H_MULTIPLICAND + 2], a
+
+UpdateLoweredStat:
+ ld a, [H_PRODUCT + 2]
+ ld [hli], a
+ ld a, [H_PRODUCT + 3]
+ ld [hl], a
+ pop de
+ pop hl
+UpdateLoweredStatDone:
+ ld b, c
+ inc b
+ push de
+ call PrintStatText
+ pop de
+ ld a, [de]
+ cp $44
+ jr nc, .ApplyBadgeBoostsAndStatusPenalties
+ call PlayCurrentMoveAnimation2
+.ApplyBadgeBoostsAndStatusPenalties
+ ld a, [H_WHOSETURN]
+ and a
+ call nz, ApplyBadgeStatBoosts ; whenever the player uses a stat-down move, badge boosts get reapplied again to every stat,
+ ; even to those not affected by the stat-up move (will be boosted further)
+ ld hl, MonsStatsFellText
+ call PrintText
+
+; These where probably added given that a stat-down move affecting speed or attack will override
+; the stat penalties from paralysis and burn respectively.
+; But they are always called regardless of the stat affected by the stat-down move.
+ call QuarterSpeedDueToParalysis
+ jp HalveAttackDueToBurn
+
+CantLowerAnymore_Pop:
+ pop de
+ pop hl
+ inc [hl]
+
+CantLowerAnymore:
+ ld a, [de]
+ cp ATTACK_DOWN_SIDE_EFFECT
+ ret nc
+ ld hl, NothingHappenedText
+ jp PrintText
+
+MoveMissed:
+ ld a, [de]
+ cp $44
+ ret nc
+ jp ConditionalPrintButItFailed
+
+MonsStatsFellText:
+ TX_FAR _MonsStatsFellText
+ TX_ASM
+ ld hl, FellText
+ ld a, [H_WHOSETURN]
+ and a
+ ld a, [wPlayerMoveEffect]
+ jr z, .playerTurn
+ ld a, [wEnemyMoveEffect]
+.playerTurn
+; check if the move's effect decreases a stat by 2
+ cp BIDE_EFFECT
+ ret c
+ cp ATTACK_DOWN_SIDE_EFFECT
+ ret nc
+ ld hl, GreatlyFellText
+ ret
+
+GreatlyFellText:
+ TX_DELAY
+ TX_FAR _GreatlyFellText
+; fallthrough
+FellText:
+ TX_FAR _FellText
+ db "@"
+
+PrintStatText:
+ ld hl, StatsTextStrings
+ ld c, "@"
+.findStatName_outer
+ dec b
+ jr z, .foundStatName
+.findStatName_inner
+ ld a, [hli]
+ cp c
+ jr z, .findStatName_outer
+ jr .findStatName_inner
+.foundStatName
+ ld de, wcf50
+ ld bc, $a
+ jp CopyData
+
+StatsTextStrings:
+ db "ANGR@"
+ db "VERT@"
+ db "INIT@"
+ db "SPEZ@"
+ db "GENA@"
+ db "FLU@"
+
+StatModifierRatios:
+; first byte is numerator, second byte is denominator
+ db 25, 100 ; 0.25
+ db 28, 100 ; 0.28
+ db 33, 100 ; 0.33
+ db 40, 100 ; 0.40
+ db 50, 100 ; 0.50
+ db 66, 100 ; 0.66
+ db 1, 1 ; 1.00
+ db 15, 10 ; 1.50
+ db 2, 1 ; 2.00
+ db 25, 10 ; 2.50
+ db 3, 1 ; 3.00
+ db 35, 10 ; 3.50
+ db 4, 1 ; 4.00
+
+BideEffect:
+ ld hl, wPlayerBattleStatus1
+ ld de, wPlayerBideAccumulatedDamage
+ ld bc, wPlayerNumAttacksLeft
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .bideEffect
+ ld hl, wEnemyBattleStatus1
+ ld de, wEnemyBideAccumulatedDamage
+ ld bc, wEnemyNumAttacksLeft
+.bideEffect
+ set StoringEnergy, [hl] ; mon is now using bide
+ xor a
+ ld [de], a
+ inc de
+ ld [de], a
+ ld [wPlayerMoveEffect], a
+ ld [wEnemyMoveEffect], a
+ call BattleRandom
+ and $1
+ inc a
+ inc a
+ ld [bc], a ; set Bide counter to 2 or 3 at random
+ ld a, [H_WHOSETURN]
+ add XSTATITEM_ANIM
+ jp PlayBattleAnimation2
+
+ThrashPetalDanceEffect:
+ ld hl, wPlayerBattleStatus1
+ ld de, wPlayerNumAttacksLeft
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .thrashPetalDanceEffect
+ ld hl, wEnemyBattleStatus1
+ ld de, wEnemyNumAttacksLeft
+.thrashPetalDanceEffect
+ set ThrashingAbout, [hl] ; mon is now using thrash/petal dance
+ call BattleRandom
+ and $1
+ inc a
+ inc a
+ ld [de], a ; set thrash/petal dance counter to 2 or 3 at random
+ ld a, [H_WHOSETURN]
+ add ANIM_B0
+ jp PlayBattleAnimation2
+
+SwitchAndTeleportEffect:
+ ld a, [H_WHOSETURN]
+ and a
+ jr nz, .handleEnemy
+ ld a, [wIsInBattle]
+ dec a
+ jr nz, .notWildBattle1
+ ld a, [wCurEnemyLVL]
+ ld b, a
+ ld a, [wBattleMonLevel]
+ cp b ; is the player's level greater than the enemy's level?
+ jr nc, .playerMoveWasSuccessful ; if so, teleport will always succeed
+ add b
+ ld c, a
+ inc c ; c = sum of player level and enemy level
+.rejectionSampleLoop1
+ call BattleRandom
+ cp c ; get a random number between 0 and c
+ jr nc, .rejectionSampleLoop1
+ srl b
+ srl b ; b = enemyLevel / 4
+ cp b ; is rand[0, playerLevel + enemyLevel) >= (enemyLevel / 4)?
+ jr nc, .playerMoveWasSuccessful ; if so, allow teleporting
+ ld c, 50
+ call DelayFrames
+ ld a, [wPlayerMoveNum]
+ cp TELEPORT
+ jp nz, PrintDidntAffectText
+ jp PrintButItFailedText_
+.playerMoveWasSuccessful
+ call ReadPlayerMonCurHPAndStatus
+ xor a
+ ld [wAnimationType], a
+ inc a
+ ld [wEscapedFromBattle], a
+ ld a, [wPlayerMoveNum]
+ jr .playAnimAndPrintText
+.notWildBattle1
+ ld c, 50
+ call DelayFrames
+ ld hl, IsUnaffectedText
+ ld a, [wPlayerMoveNum]
+ cp TELEPORT
+ jp nz, PrintText
+ jp PrintButItFailedText_
+.handleEnemy
+ ld a, [wIsInBattle]
+ dec a
+ jr nz, .notWildBattle2
+ ld a, [wBattleMonLevel]
+ ld b, a
+ ld a, [wCurEnemyLVL]
+ cp b
+ jr nc, .enemyMoveWasSuccessful
+ add b
+ ld c, a
+ inc c
+.rejectionSampleLoop2
+ call BattleRandom
+ cp c
+ jr nc, .rejectionSampleLoop2
+ srl b
+ srl b
+ cp b
+ jr nc, .enemyMoveWasSuccessful
+ ld c, 50
+ call DelayFrames
+ ld a, [wEnemyMoveNum]
+ cp TELEPORT
+ jp nz, PrintDidntAffectText
+ jp PrintButItFailedText_
+.enemyMoveWasSuccessful
+ call ReadPlayerMonCurHPAndStatus
+ xor a
+ ld [wAnimationType], a
+ inc a
+ ld [wEscapedFromBattle], a
+ ld a, [wEnemyMoveNum]
+ jr .playAnimAndPrintText
+.notWildBattle2
+ ld c, 50
+ call DelayFrames
+ ld hl, IsUnaffectedText
+ ld a, [wEnemyMoveNum]
+ cp TELEPORT
+ jp nz, PrintText
+ jp ConditionalPrintButItFailed
+.playAnimAndPrintText
+ push af
+ call PlayBattleAnimation
+ ld c, 20
+ call DelayFrames
+ pop af
+ ld hl, RanFromBattleText
+ cp TELEPORT
+ jr z, .printText
+ ld hl, RanAwayScaredText
+ cp ROAR
+ jr z, .printText
+ ld hl, WasBlownAwayText
+.printText
+ jp PrintText
+
+RanFromBattleText:
+ TX_FAR _RanFromBattleText
+ db "@"
+
+RanAwayScaredText:
+ TX_FAR _RanAwayScaredText
+ db "@"
+
+WasBlownAwayText:
+ TX_FAR _WasBlownAwayText
+ db "@"
+
+TwoToFiveAttacksEffect:
+ ld hl, wPlayerBattleStatus1
+ ld de, wPlayerNumAttacksLeft
+ ld bc, wPlayerNumHits
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .twoToFiveAttacksEffect
+ ld hl, wEnemyBattleStatus1
+ ld de, wEnemyNumAttacksLeft
+ ld bc, wEnemyNumHits
+.twoToFiveAttacksEffect
+ bit AttackingMultipleTimes, [hl] ; is mon attacking multiple times?
+ ret nz
+ set AttackingMultipleTimes, [hl] ; mon is now attacking multiple times
+ ld hl, wPlayerMoveEffect
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .setNumberOfHits
+ ld hl, wEnemyMoveEffect
+.setNumberOfHits
+ ld a, [hl]
+ cp TWINEEDLE_EFFECT
+ jr z, .twineedle
+ cp ATTACK_TWICE_EFFECT
+ ld a, $2 ; number of hits it's always 2 for ATTACK_TWICE_EFFECT
+ jr z, .saveNumberOfHits
+; for TWO_TO_FIVE_ATTACKS_EFFECT 3/8 chance for 2 and 3 hits, and 1/8 chance for 4 and 5 hits
+ call BattleRandom
+ and $3
+ cp $2
+ jr c, .gotNumHits
+; if the number of hits was greater than 2, re-roll again for a lower chance
+ call BattleRandom
+ and $3
+.gotNumHits
+ inc a
+ inc a
+.saveNumberOfHits
+ ld [de], a
+ ld [bc], a
+ ret
+.twineedle
+ ld a, POISON_SIDE_EFFECT1
+ ld [hl], a ; set Twineedle's effect to poison effect
+ jr .saveNumberOfHits
+
+FlinchSideEffect:
+ call CheckTargetSubstitute
+ ret nz
+ ld hl, wEnemyBattleStatus1
+ ld de, wPlayerMoveEffect
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .flinchSideEffect
+ ld hl, wPlayerBattleStatus1
+ ld de, wEnemyMoveEffect
+.flinchSideEffect
+ ld a, [de]
+ cp FLINCH_SIDE_EFFECT1
+ ld b, $1a ; ~10% chance of flinch
+ jr z, .gotEffectChance
+ ld b, $4d ; ~30% chance of flinch
+.gotEffectChance
+ call BattleRandom
+ cp b
+ ret nc
+ set Flinched, [hl] ; set mon's status to flinching
+ call ClearHyperBeam
+ ret
+
+OneHitKOEffect:
+ jpab OneHitKOEffect_
+
+ChargeEffect:
+ ld hl, wPlayerBattleStatus1
+ ld de, wPlayerMoveEffect
+ ld a, [H_WHOSETURN]
+ and a
+ ld b, XSTATITEM_ANIM
+ jr z, .chargeEffect
+ ld hl, wEnemyBattleStatus1
+ ld de, wEnemyMoveEffect
+ ld b, ANIM_AF
+.chargeEffect
+ set ChargingUp, [hl]
+ ld a, [de]
+ dec de ; de contains enemy or player MOVENUM
+ cp FLY_EFFECT
+ jr nz, .notFly
+ set Invulnerable, [hl] ; mon is now invulnerable to typical attacks (fly/dig)
+ ld b, TELEPORT ; load Teleport's animation
+.notFly
+ ld a, [de]
+ cp DIG
+ jr nz, .notDigOrFly
+ set Invulnerable, [hl] ; mon is now invulnerable to typical attacks (fly/dig)
+ ld b, ANIM_C0
+.notDigOrFly
+ xor a
+ ld [wAnimationType], a
+ ld a, b
+ call PlayBattleAnimation
+ ld a, [de]
+ ld [wChargeMoveNum], a
+ ld hl, ChargeMoveEffectText
+ jp PrintText
+
+ChargeMoveEffectText:
+ TX_FAR _ChargeMoveEffectText
+ TX_ASM
+ ld a, [wChargeMoveNum]
+ cp RAZOR_WIND
+ ld hl, MadeWhirlwindText
+ jr z, .gotText
+ cp SOLARBEAM
+ ld hl, TookInSunlightText
+ jr z, .gotText
+ cp SKULL_BASH
+ ld hl, LoweredItsHeadText
+ jr z, .gotText
+ cp SKY_ATTACK
+ ld hl, SkyAttackGlowingText
+ jr z, .gotText
+ cp FLY
+ ld hl, FlewUpHighText
+ jr z, .gotText
+ cp DIG
+ ld hl, DugAHoleText
+.gotText
+ ret
+
+MadeWhirlwindText:
+ TX_FAR _MadeWhirlwindText
+ db "@"
+
+TookInSunlightText:
+ TX_FAR _TookInSunlightText
+ db "@"
+
+LoweredItsHeadText:
+ TX_FAR _LoweredItsHeadText
+ db "@"
+
+SkyAttackGlowingText:
+ TX_FAR _SkyAttackGlowingText
+ db "@"
+
+FlewUpHighText:
+ TX_FAR _FlewUpHighText
+ db "@"
+
+DugAHoleText:
+ TX_FAR _DugAHoleText
+ db "@"
+
+TrappingEffect:
+ ld hl, wPlayerBattleStatus1
+ ld de, wPlayerNumAttacksLeft
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .trappingEffect
+ ld hl, wEnemyBattleStatus1
+ ld de, wEnemyNumAttacksLeft
+.trappingEffect
+ bit UsingTrappingMove, [hl]
+ ret nz
+ call ClearHyperBeam ; since this effect is called before testing whether the move will hit,
+ ; the target won't need to recharge even if the trapping move missed
+ set UsingTrappingMove, [hl] ; mon is now using a trapping move
+ call BattleRandom ; 3/8 chance for 2 and 3 attacks, and 1/8 chance for 4 and 5 attacks
+ and $3
+ cp $2
+ jr c, .setTrappingCounter
+ call BattleRandom
+ and $3
+.setTrappingCounter
+ inc a
+ ld [de], a
+ ret
+
+MistEffect:
+ jpab MistEffect_
+
+FocusEnergyEffect:
+ jpab FocusEnergyEffect_
+
+RecoilEffect:
+ jpab RecoilEffect_
+
+ConfusionSideEffect:
+ call BattleRandom
+ cp $19 ; ~10% chance
+ ret nc
+ jr ConfusionSideEffectSuccess
+
+ConfusionEffect:
+ call CheckTargetSubstitute
+ jr nz, ConfusionEffectFailed
+ call MoveHitTest
+ ld a, [wMoveMissed]
+ and a
+ jr nz, ConfusionEffectFailed
+
+ConfusionSideEffectSuccess:
+ ld a, [H_WHOSETURN]
+ and a
+ ld hl, wEnemyBattleStatus1
+ ld bc, wEnemyConfusedCounter
+ ld a, [wPlayerMoveEffect]
+ jr z, .confuseTarget
+ ld hl, wPlayerBattleStatus1
+ ld bc, wPlayerConfusedCounter
+ ld a, [wEnemyMoveEffect]
+.confuseTarget
+ bit Confused, [hl] ; is mon confused?
+ jr nz, ConfusionEffectFailed
+ set Confused, [hl] ; mon is now confused
+ push af
+ call BattleRandom
+ and $3
+ inc a
+ inc a
+ ld [bc], a ; confusion status will last 2-5 turns
+ pop af
+ cp CONFUSION_SIDE_EFFECT
+ call nz, PlayCurrentMoveAnimation2
+ ld hl, BecameConfusedText
+ jp PrintText
+
+BecameConfusedText:
+ TX_FAR _BecameConfusedText
+ db "@"
+
+ConfusionEffectFailed:
+ cp CONFUSION_SIDE_EFFECT
+ ret z
+ ld c, 50
+ call DelayFrames
+ jp ConditionalPrintButItFailed
+
+ParalyzeEffect:
+ jpab ParalyzeEffect_
+
+SubstituteEffect:
+ jpab SubstituteEffect_
+
+HyperBeamEffect:
+ ld hl, wPlayerBattleStatus2
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .hyperBeamEffect
+ ld hl, wEnemyBattleStatus2
+.hyperBeamEffect
+ set NeedsToRecharge, [hl] ; mon now needs to recharge
+ ret
+
+ClearHyperBeam:
+ push hl
+ ld hl, wEnemyBattleStatus2
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .playerTurn
+ ld hl, wPlayerBattleStatus2
+.playerTurn
+ res NeedsToRecharge, [hl] ; mon no longer needs to recharge
+ pop hl
+ ret
+
+RageEffect:
+ ld hl, wPlayerBattleStatus2
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .player
+ ld hl, wEnemyBattleStatus2
+.player
+ set UsingRage, [hl] ; mon is now in "rage" mode
+ ret
+
+MimicEffect:
+ ld c, 50
+ call DelayFrames
+ call MoveHitTest
+ ld a, [wMoveMissed]
+ and a
+ jr nz, .mimicMissed
+ ld a, [H_WHOSETURN]
+ and a
+ ld hl, wBattleMonMoves
+ ld a, [wPlayerBattleStatus1]
+ jr nz, .enemyTurn
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ jr nz, .letPlayerChooseMove
+ ld hl, wEnemyMonMoves
+ ld a, [wEnemyBattleStatus1]
+.enemyTurn
+ bit Invulnerable, a
+ jr nz, .mimicMissed
+.getRandomMove
+ push hl
+ call BattleRandom
+ and $3
+ ld c, a
+ ld b, $0
+ add hl, bc
+ ld a, [hl]
+ pop hl
+ and a
+ jr z, .getRandomMove
+ ld d, a
+ ld a, [H_WHOSETURN]
+ and a
+ ld hl, wBattleMonMoves
+ ld a, [wPlayerMoveListIndex]
+ jr z, .playerTurn
+ ld hl, wEnemyMonMoves
+ ld a, [wEnemyMoveListIndex]
+ jr .playerTurn
+.letPlayerChooseMove
+ ld a, [wEnemyBattleStatus1]
+ bit Invulnerable, a
+ jr nz, .mimicMissed
+ ld a, [wCurrentMenuItem]
+ push af
+ ld a, $1
+ ld [wMoveMenuType], a
+ call MoveSelectionMenu
+ call LoadScreenTilesFromBuffer1
+ ld hl, wEnemyMonMoves
+ ld a, [wCurrentMenuItem]
+ ld c, a
+ ld b, $0
+ add hl, bc
+ ld d, [hl]
+ pop af
+ ld hl, wBattleMonMoves
+.playerTurn
+ ld c, a
+ ld b, $0
+ add hl, bc
+ ld a, d
+ ld [hl], a
+ ld [wd11e], a
+ call GetMoveName
+ call PlayCurrentMoveAnimation
+ ld hl, MimicLearnedMoveText
+ jp PrintText
+.mimicMissed
+ jp PrintButItFailedText_
+
+MimicLearnedMoveText:
+ TX_FAR _MimicLearnedMoveText
+ db "@"
+
+LeechSeedEffect:
+ jpab LeechSeedEffect_
+
+SplashEffect:
+ call PlayCurrentMoveAnimation
+ jp PrintNoEffectText
+
+DisableEffect:
+ call MoveHitTest
+ ld a, [wMoveMissed]
+ and a
+ jr nz, .moveMissed
+ ld de, wEnemyDisabledMove
+ ld hl, wEnemyMonMoves
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .disableEffect
+ ld de, wPlayerDisabledMove
+ ld hl, wBattleMonMoves
+.disableEffect
+; no effect if target already has a move disabled
+ ld a, [de]
+ and a
+ jr nz, .moveMissed
+.pickMoveToDisable
+ push hl
+ call BattleRandom
+ and $3
+ ld c, a
+ ld b, $0
+ add hl, bc
+ ld a, [hl]
+ pop hl
+ and a
+ jr z, .pickMoveToDisable ; loop until a non-00 move slot is found
+ ld [wd11e], a ; store move number
+ push hl
+ ld a, [H_WHOSETURN]
+ and a
+ ld hl, wBattleMonPP
+ jr nz, .enemyTurn
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ pop hl ; wEnemyMonMoves
+ jr nz, .playerTurnNotLinkBattle
+; .playerTurnLinkBattle
+ push hl
+ ld hl, wEnemyMonPP
+.enemyTurn
+ push hl
+ ld a, [hli]
+ or [hl]
+ inc hl
+ or [hl]
+ inc hl
+ or [hl]
+ and $3f
+ pop hl ; wBattleMonPP or wEnemyMonPP
+ jr z, .moveMissedPopHL ; nothing to do if all moves have no PP left
+ add hl, bc
+ ld a, [hl]
+ pop hl
+ and a
+ jr z, .pickMoveToDisable ; pick another move if this one had 0 PP
+.playerTurnNotLinkBattle
+; non-link battle enemies have unlimited PP so the previous checks aren't needed
+ call BattleRandom
+ and $7
+ inc a ; 1-8 turns disabled
+ inc c ; move 1-4 will be disabled
+ swap c
+ add c ; map disabled move to high nibble of wEnemyDisabledMove / wPlayerDisabledMove
+ ld [de], a
+ call PlayCurrentMoveAnimation2
+ ld hl, wPlayerDisabledMoveNumber
+ ld a, [H_WHOSETURN]
+ and a
+ jr nz, .printDisableText
+ inc hl ; wEnemyDisabledMoveNumber
+.printDisableText
+ ld a, [wd11e] ; move number
+ ld [hl], a
+ call GetMoveName
+ ld hl, MoveWasDisabledText
+ jp PrintText
+.moveMissedPopHL
+ pop hl
+.moveMissed
+ jp PrintButItFailedText_
+
+MoveWasDisabledText:
+ TX_FAR _MoveWasDisabledText
+ db "@"
+
+PayDayEffect:
+ jpab PayDayEffect_
+
+ConversionEffect:
+ jpab ConversionEffect_
+
+HazeEffect:
+ jpab HazeEffect_
+
+HealEffect:
+ jpab HealEffect_
+
+TransformEffect:
+ jpab TransformEffect_
+
+ReflectLightScreenEffect:
+ jpab ReflectLightScreenEffect_
+
+NothingHappenedText:
+ TX_FAR _NothingHappenedText
+ db "@"
+
+PrintNoEffectText:
+ ld hl, NoEffectText
+ jp PrintText
+
+NoEffectText:
+ TX_FAR _NoEffectText
+ db "@"
+
+ConditionalPrintButItFailed:
+ ld a, [wMoveDidntMiss]
+ and a
+ ret nz ; return if the side effect failed, yet the attack was successful
+
+PrintButItFailedText_:
+ ld hl, ButItFailedText
+ jp PrintText
+
+ButItFailedText:
+ TX_FAR _ButItFailedText
+ db "@"
+
+PrintDidntAffectText:
+ ld hl, DidntAffectText
+ jp PrintText
+
+DidntAffectText:
+ TX_FAR _DidntAffectText
+ db "@"
+
+IsUnaffectedText:
+ TX_FAR _IsUnaffectedText
+ db "@"
+
+PrintMayNotAttackText:
+ ld hl, ParalyzedMayNotAttackText
+ jp PrintText
+
+ParalyzedMayNotAttackText:
+ TX_FAR _ParalyzedMayNotAttackText
+ db "@"
+
+CheckTargetSubstitute:
+ push hl
+ ld hl, wEnemyBattleStatus2
+ ld a, [H_WHOSETURN]
+ and a
+ jr z, .next1
+ ld hl, wPlayerBattleStatus2
+.next1
+ bit HasSubstituteUp, [hl]
+ pop hl
+ ret
+
+PlayCurrentMoveAnimation2:
+; animation at MOVENUM will be played unless MOVENUM is 0
+; plays wAnimationType 3 or 6
+ ld a, [H_WHOSETURN]
+ and a
+ ld a, [wPlayerMoveNum]
+ jr z, .notEnemyTurn
+ ld a, [wEnemyMoveNum]
+.notEnemyTurn
+ and a
+ ret z
+
+PlayBattleAnimation2:
+; play animation ID at a and animation type 6 or 3
+ ld [wAnimationID], a
+ ld a, [H_WHOSETURN]
+ and a
+ ld a, $6
+ jr z, .storeAnimationType
+ ld a, $3
+.storeAnimationType
+ ld [wAnimationType], a
+ jp PlayBattleAnimationGotID
+
+PlayCurrentMoveAnimation:
+; animation at MOVENUM will be played unless MOVENUM is 0
+; resets wAnimationType
+ xor a
+ ld [wAnimationType], a
+ ld a, [H_WHOSETURN]
+ and a
+ ld a, [wPlayerMoveNum]
+ jr z, .notEnemyTurn
+ ld a, [wEnemyMoveNum]
+.notEnemyTurn
+ and a
+ ret z
+
+PlayBattleAnimation:
+; play animation ID at a and predefined animation type
+ ld [wAnimationID], a
+
+PlayBattleAnimationGotID:
+; play animation at wAnimationID
+ push hl
+ push de
+ push bc
+ predef MoveAnimation
+ pop bc
+ pop de
+ pop hl
+ ret