pokecrystal-board/engine/battle/core.asm

9143 lines
144 KiB
NASM
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

; Core components of the battle engine.
DoBattle:
xor a
ld [wBattleParticipantsNotFainted], a
ld [wBattleParticipantsIncludingFainted], a
ld [wBattlePlayerAction], a
ld [wBattleEnded], a
inc a
ld [wBattleHasJustStarted], a
ld hl, wOTPartyMon1HP
ld bc, PARTYMON_STRUCT_LENGTH - 1
ld d, BATTLEACTION_SWITCH1 - 1
.loop
inc d
ld a, [hli]
or [hl]
jr nz, .alive
add hl, bc
jr .loop
.alive
ld a, d
ld [wBattleAction], a
ld a, [wLinkMode]
and a
jr z, .not_linked
ldh a, [hSerialConnectionStatus]
cp USING_INTERNAL_CLOCK
jr z, .player_2
.not_linked
ld a, [wBattleMode]
dec a
jr z, .wild
xor a
ld [wEnemySwitchMonIndex], a
call NewEnemyMonStatus
call ResetEnemyStatLevels
call BreakAttraction
call EnemySwitch
.wild
ld c, 40
call DelayFrames
.player_2
call LoadTilemapToTempTilemap
call CheckPlayerPartyForFitMon
ld a, d
and a
jp z, LostBattle
call SafeLoadTempTilemapToTilemap
ld a, [wBattleType]
cp BATTLETYPE_DEBUG
jp z, .tutorial_debug
cp BATTLETYPE_TUTORIAL
jp z, .tutorial_debug
xor a
ld [wCurPartyMon], a
.loop2
call CheckIfCurPartyMonIsFitToFight
jr nz, .alive2
ld hl, wCurPartyMon
inc [hl]
jr .loop2
.alive2
ld a, [wCurBattleMon]
ld [wLastPlayerMon], a
ld a, [wCurPartyMon]
ld [wCurBattleMon], a
inc a
ld hl, wPartySpecies - 1
ld c, a
ld b, 0
add hl, bc
ld a, [hl]
ld [wCurPartySpecies], a
ld [wTempBattleMonSpecies], a
hlcoord 1, 5
ld a, 9
call SlideBattlePicOut
call LoadTilemapToTempTilemap
call ResetBattleParticipants
call InitBattleMon
call ResetPlayerStatLevels
call SendOutMonText
call NewBattleMonStatus
call BreakAttraction
call SendOutPlayerMon
call EmptyBattleTextbox
call LoadTilemapToTempTilemap
call SetPlayerTurn
call SpikesDamage
ld a, [wLinkMode]
and a
jr z, .not_linked_2
ldh a, [hSerialConnectionStatus]
cp USING_INTERNAL_CLOCK
jr nz, .not_linked_2
xor a
ld [wEnemySwitchMonIndex], a
call NewEnemyMonStatus
call ResetEnemyStatLevels
call BreakAttraction
call EnemySwitch
call SetEnemyTurn
call SpikesDamage
.not_linked_2
jp BattleTurn
.tutorial_debug
jp BattleMenu
WildFled_EnemyFled_LinkBattleCanceled:
call SafeLoadTempTilemapToTilemap
ld a, [wBattleResult]
and BATTLERESULT_BITMASK
add DRAW
ld [wBattleResult], a
ld a, [wLinkMode]
and a
ld hl, BattleText_WildFled
jr z, .print_text
ld a, [wBattleResult]
and BATTLERESULT_BITMASK
ld [wBattleResult], a ; WIN
ld hl, BattleText_EnemyFled
call CheckMobileBattleError
jr nc, .print_text
ld hl, wcd2a
bit 4, [hl]
jr nz, .skip_text
ld hl, BattleText_LinkErrorBattleCanceled
.print_text
call StdBattleTextbox
.skip_text
call StopDangerSound
call CheckMobileBattleError
jr c, .skip_sfx
ld de, SFX_RUN
call PlaySFX
.skip_sfx
call SetPlayerTurn
ld a, 1
ld [wBattleEnded], a
ret
BattleTurn:
.loop
call Stubbed_Increments5_a89a
call CheckContestBattleOver
jp c, .quit
xor a
ld [wPlayerIsSwitching], a
ld [wEnemyIsSwitching], a
ld [wBattleHasJustStarted], a
ld [wPlayerJustGotFrozen], a
ld [wEnemyJustGotFrozen], a
ld [wCurDamage], a
ld [wCurDamage + 1], a
call HandleBerserkGene
call UpdateBattleMonInParty
farcall AIChooseMove
call IsMobileBattle
jr nz, .not_disconnected
farcall Function100da5
farcall StartMobileInactivityTimer
farcall Function100dd8
jp c, .quit
.not_disconnected
call CheckPlayerLockedIn
jr c, .skip_iteration
.loop1
call BattleMenu
jr c, .quit
ld a, [wBattleEnded]
and a
jr nz, .quit
ld a, [wForcedSwitch] ; roared/whirlwinded/teleported
and a
jr nz, .quit
.skip_iteration
call ParsePlayerAction
jr nz, .loop1
call EnemyTriesToFlee
jr c, .quit
call DetermineMoveOrder
jr c, .false
call Battle_EnemyFirst
jr .proceed
.false
call Battle_PlayerFirst
.proceed
call CheckMobileBattleError
jr c, .quit
ld a, [wForcedSwitch]
and a
jr nz, .quit
ld a, [wBattleEnded]
and a
jr nz, .quit
call HandleBetweenTurnEffects
ld a, [wBattleEnded]
and a
jr nz, .quit
jp .loop
.quit
ret
Stubbed_Increments5_a89a:
ret
ld a, BANK(s5_a89a) ; MBC30 bank used by JP Crystal; inaccessible by MBC3
call OpenSRAM
ld hl, s5_a89a + 1 ; address of MBC30 bank
inc [hl]
jr nz, .finish
dec hl
inc [hl]
jr nz, .finish
dec [hl]
inc hl
dec [hl]
.finish
call CloseSRAM
ret
HandleBetweenTurnEffects:
ldh a, [hSerialConnectionStatus]
cp USING_EXTERNAL_CLOCK
jr z, .CheckEnemyFirst
call CheckFaint_PlayerThenEnemy
ret c
call HandleFutureSight
call CheckFaint_PlayerThenEnemy
ret c
call HandleWeather
call CheckFaint_PlayerThenEnemy
ret c
call HandleWrap
call CheckFaint_PlayerThenEnemy
ret c
call HandlePerishSong
call CheckFaint_PlayerThenEnemy
ret c
jr .NoMoreFaintingConditions
.CheckEnemyFirst:
call CheckFaint_EnemyThenPlayer
ret c
call HandleFutureSight
call CheckFaint_EnemyThenPlayer
ret c
call HandleWeather
call CheckFaint_EnemyThenPlayer
ret c
call HandleWrap
call CheckFaint_EnemyThenPlayer
ret c
call HandlePerishSong
call CheckFaint_EnemyThenPlayer
ret c
.NoMoreFaintingConditions:
call HandleLeftovers
call HandleMysteryberry
call HandleDefrost
call HandleSafeguard
call HandleScreens
call HandleStatBoostingHeldItems
call HandleHealingItems
call UpdateBattleMonInParty
call LoadTilemapToTempTilemap
jp HandleEncore
CheckFaint_PlayerThenEnemy:
; BUG: Perish Song and Spikes can leave a Pokemon with 0 HP and not faint (see docs/bugs_and_glitches.md)
call HasPlayerFainted
jr nz, .PlayerNotFainted
call HandlePlayerMonFaint
ld a, [wBattleEnded]
and a
jr nz, .BattleIsOver
.PlayerNotFainted:
call HasEnemyFainted
jr nz, .BattleContinues
call HandleEnemyMonFaint
ld a, [wBattleEnded]
and a
jr nz, .BattleIsOver
.BattleContinues:
and a
ret
.BattleIsOver:
scf
ret
CheckFaint_EnemyThenPlayer:
; BUG: Perish Song and Spikes can leave a Pokemon with 0 HP and not faint (see docs/bugs_and_glitches.md)
call HasEnemyFainted
jr nz, .EnemyNotFainted
call HandleEnemyMonFaint
ld a, [wBattleEnded]
and a
jr nz, .BattleIsOver
.EnemyNotFainted:
call HasPlayerFainted
jr nz, .BattleContinues
call HandlePlayerMonFaint
ld a, [wBattleEnded]
and a
jr nz, .BattleIsOver
.BattleContinues:
and a
ret
.BattleIsOver:
scf
ret
HandleBerserkGene:
ldh a, [hSerialConnectionStatus]
cp USING_EXTERNAL_CLOCK
jr z, .reverse
call .player
jr .enemy
.reverse
call .enemy
; fallthrough
.player
call SetPlayerTurn
ld de, wPartyMon1Item
ld a, [wCurBattleMon]
ld b, a
jr .go
.enemy
call SetEnemyTurn
ld de, wOTPartyMon1Item
ld a, [wCurOTMon]
ld b, a
; fallthrough
.go
push de
push bc
callfar GetUserItem
ld a, [hl]
ld [wNamedObjectIndex], a
sub BERSERK_GENE
pop bc
pop de
ret nz
ld [hl], a
ld h, d
ld l, e
ld a, b
call GetPartyLocation
xor a
ld [hl], a
; BUG: Berserk Gene's confusion lasts for 256 turns or the previous Pokémon's confusion count (see docs/bugs_and_glitches.md)
ld a, BATTLE_VARS_SUBSTATUS3
call GetBattleVarAddr
push af
set SUBSTATUS_CONFUSED, [hl]
ld a, BATTLE_VARS_MOVE_ANIM
call GetBattleVarAddr
push hl
push af
xor a
ld [hl], a
ld [wAttackMissed], a
ld [wEffectFailed], a
farcall BattleCommand_AttackUp2
pop af
pop hl
ld [hl], a
call GetItemName
ld hl, BattleText_UsersStringBuffer1Activated
call StdBattleTextbox
callfar BattleCommand_StatUpMessage
pop af
bit SUBSTATUS_CONFUSED, a
ret nz
xor a
ld [wNumHits], a
ld de, ANIM_CONFUSED
call Call_PlayBattleAnim_OnlyIfVisible
call SwitchTurnCore
ld hl, BecameConfusedText
jp StdBattleTextbox
EnemyTriesToFlee:
ld a, [wLinkMode]
and a
jr z, .not_linked
ld a, [wBattleAction]
cp BATTLEACTION_FORFEIT
jr z, .forfeit
.not_linked
and a
ret
.forfeit
call WildFled_EnemyFled_LinkBattleCanceled
scf
ret
DetermineMoveOrder:
ld a, [wLinkMode]
and a
jr z, .use_move
ld a, [wBattleAction]
cp BATTLEACTION_STRUGGLE
jr z, .use_move
cp BATTLEACTION_SKIPTURN
jr z, .use_move
sub BATTLEACTION_SWITCH1
jr c, .use_move
ld a, [wBattlePlayerAction]
cp BATTLEPLAYERACTION_SWITCH
jr nz, .switch
ldh a, [hSerialConnectionStatus]
cp USING_INTERNAL_CLOCK
jr z, .player_2
call BattleRandom
cp 50 percent + 1
jp c, .player_first
jp .enemy_first
.player_2
call BattleRandom
cp 50 percent + 1
jp c, .enemy_first
jp .player_first
.switch
callfar AI_Switch
call SetEnemyTurn
call SpikesDamage
jp .enemy_first
.use_move
ld a, [wBattlePlayerAction]
and a ; BATTLEPLAYERACTION_USEMOVE?
jp nz, .player_first
call CompareMovePriority
jr z, .equal_priority
jp c, .player_first ; player goes first
jp .enemy_first
.equal_priority
call SetPlayerTurn
callfar GetUserItem
push bc
callfar GetOpponentItem
pop de
ld a, d
cp HELD_QUICK_CLAW
jr nz, .player_no_quick_claw
ld a, b
cp HELD_QUICK_CLAW
jr z, .both_have_quick_claw
call BattleRandom
cp e
jr nc, .speed_check
jp .player_first
.player_no_quick_claw
ld a, b
cp HELD_QUICK_CLAW
jr nz, .speed_check
call BattleRandom
cp c
jr nc, .speed_check
jp .enemy_first
.both_have_quick_claw
ldh a, [hSerialConnectionStatus]
cp USING_INTERNAL_CLOCK
jr z, .player_2b
call BattleRandom
cp c
jp c, .enemy_first
call BattleRandom
cp e
jp c, .player_first
jr .speed_check
.player_2b
call BattleRandom
cp e
jp c, .player_first
call BattleRandom
cp c
jp c, .enemy_first
jr .speed_check
.speed_check
ld de, wBattleMonSpeed
ld hl, wEnemyMonSpeed
ld c, 2
call CompareBytes
jr z, .speed_tie
jp nc, .player_first
jp .enemy_first
.speed_tie
ldh a, [hSerialConnectionStatus]
cp USING_INTERNAL_CLOCK
jr z, .player_2c
call BattleRandom
cp 50 percent + 1
jp c, .player_first
jp .enemy_first
.player_2c
call BattleRandom
cp 50 percent + 1
jp c, .enemy_first
.player_first
scf
ret
.enemy_first
and a
ret
CheckContestBattleOver:
ld a, [wBattleType]
cp BATTLETYPE_CONTEST
jr nz, .contest_not_over
ld a, [wParkBallsRemaining]
and a
jr nz, .contest_not_over
ld a, [wBattleResult]
and BATTLERESULT_BITMASK
add DRAW
ld [wBattleResult], a
scf
ret
.contest_not_over
and a
ret
CheckPlayerLockedIn:
ld a, [wPlayerSubStatus4]
and 1 << SUBSTATUS_RECHARGE
jp nz, .quit
ld hl, wEnemySubStatus3
res SUBSTATUS_FLINCHED, [hl]
ld hl, wPlayerSubStatus3
res SUBSTATUS_FLINCHED, [hl]
ld a, [hl]
and 1 << SUBSTATUS_CHARGED | 1 << SUBSTATUS_RAMPAGE
jp nz, .quit
ld hl, wPlayerSubStatus1
bit SUBSTATUS_ROLLOUT, [hl]
jp nz, .quit
and a
ret
.quit
scf
ret
ParsePlayerAction:
call CheckPlayerLockedIn
jp c, .locked_in
ld hl, wPlayerSubStatus5
bit SUBSTATUS_ENCORED, [hl]
jr z, .not_encored
ld a, [wLastPlayerMove]
ld [wCurPlayerMove], a
jr .encored
.not_encored
ld a, [wBattlePlayerAction]
cp BATTLEPLAYERACTION_SWITCH
jr z, .reset_rage
and a
jr nz, .reset_bide
ld a, [wPlayerSubStatus3]
and 1 << SUBSTATUS_BIDE
jr nz, .locked_in
xor a
ld [wMoveSelectionMenuType], a
inc a ; POUND
ld [wFXAnimID], a
call MoveSelectionScreen
push af
call SafeLoadTempTilemapToTilemap
call UpdateBattleHuds
ld a, [wCurPlayerMove]
cp STRUGGLE
jr z, .struggle
call PlayClickSFX
.struggle
ld a, $1
ldh [hBGMapMode], a
pop af
ret nz
.encored
call SetPlayerTurn
callfar UpdateMoveData
xor a
ld [wPlayerCharging], a
ld a, [wPlayerMoveStruct + MOVE_EFFECT]
cp EFFECT_FURY_CUTTER
jr z, .continue_fury_cutter
xor a
ld [wPlayerFuryCutterCount], a
.continue_fury_cutter
ld a, [wPlayerMoveStruct + MOVE_EFFECT]
cp EFFECT_RAGE
jr z, .continue_rage
ld hl, wPlayerSubStatus4
res SUBSTATUS_RAGE, [hl]
xor a
ld [wPlayerRageCounter], a
.continue_rage
ld a, [wPlayerMoveStruct + MOVE_EFFECT]
cp EFFECT_PROTECT
jr z, .continue_protect
cp EFFECT_ENDURE
jr z, .continue_protect
xor a
ld [wPlayerProtectCount], a
jr .continue_protect
.reset_bide
ld hl, wPlayerSubStatus3
res SUBSTATUS_BIDE, [hl]
.locked_in
xor a
ld [wPlayerFuryCutterCount], a
ld [wPlayerProtectCount], a
ld [wPlayerRageCounter], a
ld hl, wPlayerSubStatus4
res SUBSTATUS_RAGE, [hl]
.continue_protect
call ParseEnemyAction
xor a
ret
.reset_rage
xor a
ld [wPlayerFuryCutterCount], a
ld [wPlayerProtectCount], a
ld [wPlayerRageCounter], a
ld hl, wPlayerSubStatus4
res SUBSTATUS_RAGE, [hl]
xor a
ret
HandleEncore:
ldh a, [hSerialConnectionStatus]
cp USING_EXTERNAL_CLOCK
jr z, .player_1
call .do_player
jr .do_enemy
.player_1
call .do_enemy
.do_player
ld hl, wPlayerSubStatus5
bit SUBSTATUS_ENCORED, [hl]
ret z
ld a, [wPlayerEncoreCount]
dec a
ld [wPlayerEncoreCount], a
jr z, .end_player_encore
ld hl, wBattleMonPP
ld a, [wCurMoveNum]
ld c, a
ld b, 0
add hl, bc
ld a, [hl]
and PP_MASK
ret nz
.end_player_encore
ld hl, wPlayerSubStatus5
res SUBSTATUS_ENCORED, [hl]
call SetEnemyTurn
ld hl, BattleText_TargetsEncoreEnded
jp StdBattleTextbox
.do_enemy
ld hl, wEnemySubStatus5
bit SUBSTATUS_ENCORED, [hl]
ret z
ld a, [wEnemyEncoreCount]
dec a
ld [wEnemyEncoreCount], a
jr z, .end_enemy_encore
ld hl, wEnemyMonPP
ld a, [wCurEnemyMoveNum]
ld c, a
ld b, 0
add hl, bc
ld a, [hl]
and PP_MASK
ret nz
.end_enemy_encore
ld hl, wEnemySubStatus5
res SUBSTATUS_ENCORED, [hl]
call SetPlayerTurn
ld hl, BattleText_TargetsEncoreEnded
jp StdBattleTextbox
TryEnemyFlee:
ld a, [wBattleMode]
dec a
jr nz, .Stay
ld a, [wPlayerSubStatus5]
bit SUBSTATUS_CANT_RUN, a
jr nz, .Stay
ld a, [wEnemyWrapCount]
and a
jr nz, .Stay
ld a, [wEnemyMonStatus]
and 1 << FRZ | SLP_MASK
jr nz, .Stay
ld a, [wTempEnemyMonSpecies]
ld de, 1
ld hl, AlwaysFleeMons
call IsInArray
jr c, .Flee
call BattleRandom
ld b, a
cp 50 percent + 1
jr nc, .Stay
push bc
ld a, [wTempEnemyMonSpecies]
ld de, 1
ld hl, OftenFleeMons
call IsInArray
pop bc
jr c, .Flee
ld a, b
cp 10 percent + 1
jr nc, .Stay
ld a, [wTempEnemyMonSpecies]
ld de, 1
ld hl, SometimesFleeMons
call IsInArray
jr c, .Flee
.Stay:
and a
ret
.Flee:
scf
ret
INCLUDE "data/wild/flee_mons.asm"
CompareMovePriority:
; Compare the priority of the player and enemy's moves.
; Return carry if the player goes first, or z if they match.
ld a, [wCurPlayerMove]
call GetMovePriority
ld b, a
push bc
ld a, [wCurEnemyMove]
call GetMovePriority
pop bc
cp b
ret
GetMovePriority:
; Return the priority (0-3) of move a.
ld b, a
; Vital Throw goes last.
cp VITAL_THROW
ld a, 0
ret z
call GetMoveEffect
ld hl, MoveEffectPriorities
.loop
ld a, [hli]
cp b
jr z, .done
inc hl
cp -1
jr nz, .loop
ld a, BASE_PRIORITY
ret
.done
ld a, [hl]
ret
INCLUDE "data/moves/effects_priorities.asm"
GetMoveEffect:
ld a, b
dec a
ld hl, Moves + MOVE_EFFECT
ld bc, MOVE_LENGTH
call AddNTimes
ld a, BANK(Moves)
call GetFarByte
ld b, a
ret
Battle_EnemyFirst:
call LoadTilemapToTempTilemap
call TryEnemyFlee
jp c, WildFled_EnemyFled_LinkBattleCanceled
call SetEnemyTurn
ld a, $1
ld [wEnemyGoesFirst], a
callfar AI_SwitchOrTryItem
jr c, .switch_item
call EnemyTurn_EndOpponentProtectEndureDestinyBond
call CheckMobileBattleError
ret c
ld a, [wForcedSwitch]
and a
ret nz
call HasPlayerFainted
jp z, HandlePlayerMonFaint
call HasEnemyFainted
jp z, HandleEnemyMonFaint
.switch_item
call SetEnemyTurn
call ResidualDamage
jp z, HandleEnemyMonFaint
call RefreshBattleHuds
call PlayerTurn_EndOpponentProtectEndureDestinyBond
call CheckMobileBattleError
ret c
ld a, [wForcedSwitch]
and a
ret nz
call HasEnemyFainted
jp z, HandleEnemyMonFaint
call HasPlayerFainted
jp z, HandlePlayerMonFaint
call SetPlayerTurn
call ResidualDamage
jp z, HandlePlayerMonFaint
call RefreshBattleHuds
xor a ; BATTLEPLAYERACTION_USEMOVE
ld [wBattlePlayerAction], a
ret
Battle_PlayerFirst:
xor a
ld [wEnemyGoesFirst], a
call SetEnemyTurn
callfar AI_SwitchOrTryItem
push af
call PlayerTurn_EndOpponentProtectEndureDestinyBond
pop bc
ld a, [wForcedSwitch]
and a
ret nz
call CheckMobileBattleError
ret c
call HasEnemyFainted
jp z, HandleEnemyMonFaint
call HasPlayerFainted
jp z, HandlePlayerMonFaint
push bc
call SetPlayerTurn
call ResidualDamage
pop bc
jp z, HandlePlayerMonFaint
push bc
call RefreshBattleHuds
pop af
jr c, .switched_or_used_item
call LoadTilemapToTempTilemap
call TryEnemyFlee
jp c, WildFled_EnemyFled_LinkBattleCanceled
call EnemyTurn_EndOpponentProtectEndureDestinyBond
call CheckMobileBattleError
ret c
ld a, [wForcedSwitch]
and a
ret nz
call HasPlayerFainted
jp z, HandlePlayerMonFaint
call HasEnemyFainted
jp z, HandleEnemyMonFaint
.switched_or_used_item
call SetEnemyTurn
call ResidualDamage
jp z, HandleEnemyMonFaint
call RefreshBattleHuds
xor a ; BATTLEPLAYERACTION_USEMOVE
ld [wBattlePlayerAction], a
ret
PlayerTurn_EndOpponentProtectEndureDestinyBond:
call SetPlayerTurn
call EndUserDestinyBond
callfar DoPlayerTurn
jp EndOpponentProtectEndureDestinyBond
EnemyTurn_EndOpponentProtectEndureDestinyBond:
call SetEnemyTurn
call EndUserDestinyBond
callfar DoEnemyTurn
jp EndOpponentProtectEndureDestinyBond
EndOpponentProtectEndureDestinyBond:
ld a, BATTLE_VARS_SUBSTATUS1_OPP
call GetBattleVarAddr
res SUBSTATUS_PROTECT, [hl]
res SUBSTATUS_ENDURE, [hl]
ld a, BATTLE_VARS_SUBSTATUS5_OPP
call GetBattleVarAddr
res SUBSTATUS_DESTINY_BOND, [hl]
ret
EndUserDestinyBond:
ld a, BATTLE_VARS_SUBSTATUS5
call GetBattleVarAddr
res SUBSTATUS_DESTINY_BOND, [hl]
ret
HasUserFainted:
ldh a, [hBattleTurn]
and a
jr z, HasPlayerFainted
HasEnemyFainted:
ld hl, wEnemyMonHP
jr CheckIfHPIsZero
HasPlayerFainted:
ld hl, wBattleMonHP
CheckIfHPIsZero:
ld a, [hli]
or [hl]
ret
ResidualDamage:
; Return z if the user fainted before
; or as a result of residual damage.
; For Sandstorm damage, see HandleWeather.
call HasUserFainted
ret z
ld a, BATTLE_VARS_STATUS
call GetBattleVar
and 1 << PSN | 1 << BRN
jr z, .did_psn_brn
ld hl, HurtByPoisonText
ld de, ANIM_PSN
and 1 << BRN
jr z, .got_anim
ld hl, HurtByBurnText
ld de, ANIM_BRN
.got_anim
push de
call StdBattleTextbox
pop de
xor a
ld [wNumHits], a
call Call_PlayBattleAnim_OnlyIfVisible
call GetEighthMaxHP
ld de, wPlayerToxicCount
ldh a, [hBattleTurn]
and a
jr z, .check_toxic
ld de, wEnemyToxicCount
.check_toxic
ld a, BATTLE_VARS_SUBSTATUS5
call GetBattleVar
bit SUBSTATUS_TOXIC, a
jr z, .did_toxic
call GetSixteenthMaxHP
ld a, [de]
inc a
ld [de], a
ld hl, 0
.add
add hl, bc
dec a
jr nz, .add
ld b, h
ld c, l
.did_toxic
call SubtractHPFromUser
.did_psn_brn
call HasUserFainted
jp z, .fainted
ld a, BATTLE_VARS_SUBSTATUS4
call GetBattleVarAddr
bit SUBSTATUS_LEECH_SEED, [hl]
jr z, .not_seeded
call SwitchTurnCore
xor a
ld [wNumHits], a
ld de, ANIM_SAP
ld a, BATTLE_VARS_SUBSTATUS3_OPP
call GetBattleVar
and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND
call z, Call_PlayBattleAnim_OnlyIfVisible
call SwitchTurnCore
call GetEighthMaxHP
call SubtractHPFromUser
ld a, $1
ldh [hBGMapMode], a
call RestoreHP
ld hl, LeechSeedSapsText
call StdBattleTextbox
.not_seeded
call HasUserFainted
jr z, .fainted
ld a, BATTLE_VARS_SUBSTATUS1
call GetBattleVarAddr
bit SUBSTATUS_NIGHTMARE, [hl]
jr z, .not_nightmare
xor a
ld [wNumHits], a
ld de, ANIM_IN_NIGHTMARE
call Call_PlayBattleAnim_OnlyIfVisible
call GetQuarterMaxHP
call SubtractHPFromUser
ld hl, HasANightmareText
call StdBattleTextbox
.not_nightmare
call HasUserFainted
jr z, .fainted
ld a, BATTLE_VARS_SUBSTATUS1
call GetBattleVarAddr
bit SUBSTATUS_CURSE, [hl]
jr z, .not_cursed
xor a
ld [wNumHits], a
ld de, ANIM_IN_NIGHTMARE
call Call_PlayBattleAnim_OnlyIfVisible
call GetQuarterMaxHP
call SubtractHPFromUser
ld hl, HurtByCurseText
call StdBattleTextbox
.not_cursed
ld hl, wBattleMonHP
ldh a, [hBattleTurn]
and a
jr z, .check_fainted
ld hl, wEnemyMonHP
.check_fainted
ld a, [hli]
or [hl]
ret nz
.fainted
call RefreshBattleHuds
ld c, 20
call DelayFrames
xor a
ret
HandlePerishSong:
ldh a, [hSerialConnectionStatus]
cp USING_EXTERNAL_CLOCK
jr z, .EnemyFirst
call SetPlayerTurn
call .do_it
call SetEnemyTurn
jp .do_it
.EnemyFirst:
call SetEnemyTurn
call .do_it
call SetPlayerTurn
.do_it
ld hl, wPlayerPerishCount
ldh a, [hBattleTurn]
and a
jr z, .got_count
ld hl, wEnemyPerishCount
.got_count
ld a, BATTLE_VARS_SUBSTATUS1
call GetBattleVar
bit SUBSTATUS_PERISH, a
ret z
dec [hl]
ld a, [hl]
ld [wTextDecimalByte], a
push af
ld hl, PerishCountText
call StdBattleTextbox
pop af
ret nz
ld a, BATTLE_VARS_SUBSTATUS1
call GetBattleVarAddr
res SUBSTATUS_PERISH, [hl]
ldh a, [hBattleTurn]
and a
jr nz, .kill_enemy
ld hl, wBattleMonHP
xor a
ld [hli], a
ld [hl], a
ld hl, wPartyMon1HP
ld a, [wCurBattleMon]
call GetPartyLocation
xor a
ld [hli], a
ld [hl], a
ret
.kill_enemy
ld hl, wEnemyMonHP
xor a
ld [hli], a
ld [hl], a
ld a, [wBattleMode]
dec a
ret z
ld hl, wOTPartyMon1HP
ld a, [wCurOTMon]
call GetPartyLocation
xor a
ld [hli], a
ld [hl], a
ret
HandleWrap:
ldh a, [hSerialConnectionStatus]
cp USING_EXTERNAL_CLOCK
jr z, .EnemyFirst
call SetPlayerTurn
call .do_it
call SetEnemyTurn
jp .do_it
.EnemyFirst:
call SetEnemyTurn
call .do_it
call SetPlayerTurn
.do_it
ld hl, wPlayerWrapCount
ld de, wPlayerTrappingMove
ldh a, [hBattleTurn]
and a
jr z, .got_addrs
ld hl, wEnemyWrapCount
ld de, wEnemyTrappingMove
.got_addrs
ld a, [hl]
and a
ret z
ld a, BATTLE_VARS_SUBSTATUS4
call GetBattleVar
bit SUBSTATUS_SUBSTITUTE, a
ret nz
ld a, [de]
ld [wNamedObjectIndex], a
ld [wFXAnimID], a
call GetMoveName
dec [hl]
jr z, .release_from_bounds
ld a, BATTLE_VARS_SUBSTATUS3
call GetBattleVar
and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND
jr nz, .skip_anim
call SwitchTurnCore
xor a
ld [wNumHits], a
ld [wFXAnimID + 1], a
predef PlayBattleAnim
call SwitchTurnCore
.skip_anim
call GetSixteenthMaxHP
call SubtractHPFromUser
ld hl, BattleText_UsersHurtByStringBuffer1
jr .print_text
.release_from_bounds
ld hl, BattleText_UserWasReleasedFromStringBuffer1
.print_text
jp StdBattleTextbox
SwitchTurnCore:
ldh a, [hBattleTurn]
xor 1
ldh [hBattleTurn], a
ret
HandleLeftovers:
ldh a, [hSerialConnectionStatus]
cp USING_EXTERNAL_CLOCK
jr z, .DoEnemyFirst
call SetPlayerTurn
call .do_it
call SetEnemyTurn
jp .do_it
.DoEnemyFirst:
call SetEnemyTurn
call .do_it
call SetPlayerTurn
.do_it
callfar GetUserItem
ld a, [hl]
ld [wNamedObjectIndex], a
call GetItemName
ld a, b
cp HELD_LEFTOVERS
ret nz
ld hl, wBattleMonHP
ldh a, [hBattleTurn]
and a
jr z, .got_hp
ld hl, wEnemyMonHP
.got_hp
; Don't restore if we're already at max HP
ld a, [hli]
ld b, a
ld a, [hli]
ld c, a
ld a, [hli]
cp b
jr nz, .restore
ld a, [hl]
cp c
ret z
.restore
call GetSixteenthMaxHP
call SwitchTurnCore
call RestoreHP
ld hl, BattleText_TargetRecoveredWithItem
jp StdBattleTextbox
HandleMysteryberry:
ldh a, [hSerialConnectionStatus]
cp USING_EXTERNAL_CLOCK
jr z, .DoEnemyFirst
call SetPlayerTurn
call .do_it
call SetEnemyTurn
jp .do_it
.DoEnemyFirst:
call SetEnemyTurn
call .do_it
call SetPlayerTurn
.do_it
callfar GetUserItem
ld a, b
cp HELD_RESTORE_PP
jr nz, .quit
ld hl, wPartyMon1PP
ld a, [wCurBattleMon]
call GetPartyLocation
ld d, h
ld e, l
ld hl, wPartyMon1Moves
ld a, [wCurBattleMon]
call GetPartyLocation
ldh a, [hBattleTurn]
and a
jr z, .wild
ld de, wWildMonPP
ld hl, wWildMonMoves
ld a, [wBattleMode]
dec a
jr z, .wild
ld hl, wOTPartyMon1PP
ld a, [wCurOTMon]
call GetPartyLocation
ld d, h
ld e, l
ld hl, wOTPartyMon1Moves
ld a, [wCurOTMon]
call GetPartyLocation
.wild
ld c, $0
.loop
ld a, [hl]
and a
jr z, .quit
ld a, [de]
and PP_MASK
jr z, .restore
inc hl
inc de
inc c
ld a, c
cp NUM_MOVES
jr nz, .loop
.quit
ret
.restore
; lousy hack
ld a, [hl]
cp SKETCH
ld b, 1
jr z, .sketch
ld b, 5
.sketch
ld a, [de]
add b
ld [de], a
push bc
push bc
ld a, [hl]
ld [wTempByteValue], a
ld de, wBattleMonMoves - 1
ld hl, wBattleMonPP
ldh a, [hBattleTurn]
and a
jr z, .player_pp
ld de, wEnemyMonMoves - 1
ld hl, wEnemyMonPP
.player_pp
inc de
pop bc
ld b, 0
add hl, bc
push hl
ld h, d
ld l, e
add hl, bc
pop de
pop bc
ld a, [wTempByteValue]
cp [hl]
jr nz, .skip_checks
ldh a, [hBattleTurn]
and a
ld a, [wPlayerSubStatus5]
jr z, .check_transform
ld a, [wEnemySubStatus5]
.check_transform
bit SUBSTATUS_TRANSFORMED, a
jr nz, .skip_checks
ld a, [de]
add b
ld [de], a
.skip_checks
callfar GetUserItem
ld a, [hl]
ld [wNamedObjectIndex], a
xor a
ld [hl], a
call GetPartymonItem
ldh a, [hBattleTurn]
and a
jr z, .consume_item
ld a, [wBattleMode]
dec a
jr z, .skip_consumption
call GetOTPartymonItem
.consume_item
xor a
ld [hl], a
.skip_consumption
call GetItemName
call SwitchTurnCore
call ItemRecoveryAnim
call SwitchTurnCore
ld hl, BattleText_UserRecoveredPPUsing
jp StdBattleTextbox
HandleFutureSight:
ldh a, [hSerialConnectionStatus]
cp USING_EXTERNAL_CLOCK
jr z, .enemy_first
call SetPlayerTurn
call .do_it
call SetEnemyTurn
jp .do_it
.enemy_first
call SetEnemyTurn
call .do_it
call SetPlayerTurn
.do_it
ld hl, wPlayerFutureSightCount
ldh a, [hBattleTurn]
and a
jr z, .okay
ld hl, wEnemyFutureSightCount
.okay
ld a, [hl]
and a
ret z
dec a
ld [hl], a
cp $1
ret nz
ld hl, BattleText_TargetWasHitByFutureSight
call StdBattleTextbox
ld a, BATTLE_VARS_MOVE
call GetBattleVarAddr
push af
ld a, FUTURE_SIGHT
ld [hl], a
callfar UpdateMoveData
xor a
ld [wAttackMissed], a
ld [wAlreadyDisobeyed], a
ld a, EFFECTIVE
ld [wTypeModifier], a
callfar DoMove
xor a
ld [wCurDamage], a
ld [wCurDamage + 1], a
ld a, BATTLE_VARS_MOVE
call GetBattleVarAddr
pop af
ld [hl], a
call UpdateBattleMonInParty
jp UpdateEnemyMonInParty
HandleDefrost:
ldh a, [hSerialConnectionStatus]
cp USING_EXTERNAL_CLOCK
jr z, .enemy_first
call .do_player_turn
jr .do_enemy_turn
.enemy_first
call .do_enemy_turn
.do_player_turn
ld a, [wBattleMonStatus]
bit FRZ, a
ret z
ld a, [wPlayerJustGotFrozen]
and a
ret nz
call BattleRandom
cp 10 percent
ret nc
xor a
ld [wBattleMonStatus], a
ld a, [wCurBattleMon]
ld hl, wPartyMon1Status
call GetPartyLocation
ld [hl], 0
call UpdateBattleHuds
call SetEnemyTurn
ld hl, DefrostedOpponentText
jp StdBattleTextbox
.do_enemy_turn
ld a, [wEnemyMonStatus]
bit FRZ, a
ret z
ld a, [wEnemyJustGotFrozen]
and a
ret nz
call BattleRandom
cp 10 percent
ret nc
xor a
ld [wEnemyMonStatus], a
ld a, [wBattleMode]
dec a
jr z, .wild
ld a, [wCurOTMon]
ld hl, wOTPartyMon1Status
call GetPartyLocation
ld [hl], 0
.wild
call UpdateBattleHuds
call SetPlayerTurn
ld hl, DefrostedOpponentText
jp StdBattleTextbox
HandleSafeguard:
ldh a, [hSerialConnectionStatus]
cp USING_EXTERNAL_CLOCK
jr z, .player1
call .CheckPlayer
jr .CheckEnemy
.player1
call .CheckEnemy
.CheckPlayer:
ld a, [wPlayerScreens]
bit SCREENS_SAFEGUARD, a
ret z
ld hl, wPlayerSafeguardCount
dec [hl]
ret nz
res SCREENS_SAFEGUARD, a
ld [wPlayerScreens], a
xor a
jr .print
.CheckEnemy:
ld a, [wEnemyScreens]
bit SCREENS_SAFEGUARD, a
ret z
ld hl, wEnemySafeguardCount
dec [hl]
ret nz
res SCREENS_SAFEGUARD, a
ld [wEnemyScreens], a
ld a, $1
.print
ldh [hBattleTurn], a
ld hl, BattleText_SafeguardFaded
jp StdBattleTextbox
HandleScreens:
ldh a, [hSerialConnectionStatus]
cp USING_EXTERNAL_CLOCK
jr z, .Both
call .CheckPlayer
jr .CheckEnemy
.Both:
call .CheckEnemy
.CheckPlayer:
call SetPlayerTurn
ld de, .Your
call .Copy
ld hl, wPlayerScreens
ld de, wPlayerLightScreenCount
jr .TickScreens
.CheckEnemy:
call SetEnemyTurn
ld de, .Enemy
call .Copy
ld hl, wEnemyScreens
ld de, wEnemyLightScreenCount
.TickScreens:
bit SCREENS_LIGHT_SCREEN, [hl]
call nz, .LightScreenTick
bit SCREENS_REFLECT, [hl]
call nz, .ReflectTick
ret
.Copy:
ld hl, wStringBuffer1
jp CopyName2
.Your:
db "Your@"
.Enemy:
db "Enemy@"
.LightScreenTick:
ld a, [de]
dec a
ld [de], a
ret nz
res SCREENS_LIGHT_SCREEN, [hl]
push hl
push de
ld hl, BattleText_MonsLightScreenFell
call StdBattleTextbox
pop de
pop hl
ret
.ReflectTick:
inc de
ld a, [de]
dec a
ld [de], a
ret nz
res SCREENS_REFLECT, [hl]
ld hl, BattleText_MonsReflectFaded
jp StdBattleTextbox
HandleWeather:
ld a, [wBattleWeather]
cp WEATHER_NONE
ret z
ld hl, wWeatherCount
dec [hl]
jr z, .ended
ld hl, .WeatherMessages
call .PrintWeatherMessage
ld a, [wBattleWeather]
cp WEATHER_SANDSTORM
ret nz
ldh a, [hSerialConnectionStatus]
cp USING_EXTERNAL_CLOCK
jr z, .enemy_first
; player first
call SetPlayerTurn
call .SandstormDamage
call SetEnemyTurn
jr .SandstormDamage
.enemy_first
call SetEnemyTurn
call .SandstormDamage
call SetPlayerTurn
.SandstormDamage:
ld a, BATTLE_VARS_SUBSTATUS3
call GetBattleVar
bit SUBSTATUS_UNDERGROUND, a
ret nz
ld hl, wBattleMonType1
ldh a, [hBattleTurn]
and a
jr z, .ok
ld hl, wEnemyMonType1
.ok
ld a, [hli]
cp ROCK
ret z
cp GROUND
ret z
cp STEEL
ret z
ld a, [hl]
cp ROCK
ret z
cp GROUND
ret z
cp STEEL
ret z
call SwitchTurnCore
xor a
ld [wNumHits], a
ld de, ANIM_IN_SANDSTORM
call Call_PlayBattleAnim
call SwitchTurnCore
call GetEighthMaxHP
call SubtractHPFromUser
ld hl, SandstormHitsText
jp StdBattleTextbox
.ended
ld hl, .WeatherEndedMessages
call .PrintWeatherMessage
xor a
ld [wBattleWeather], a
ret
.PrintWeatherMessage:
ld a, [wBattleWeather]
dec a
ld c, a
ld b, 0
add hl, bc
add hl, bc
ld a, [hli]
ld h, [hl]
ld l, a
jp StdBattleTextbox
.WeatherMessages:
; entries correspond to WEATHER_* constants
dw BattleText_RainContinuesToFall
dw BattleText_TheSunlightIsStrong
dw BattleText_TheSandstormRages
.WeatherEndedMessages:
; entries correspond to WEATHER_* constants
dw BattleText_TheRainStopped
dw BattleText_TheSunlightFaded
dw BattleText_TheSandstormSubsided
SubtractHPFromTarget:
call SubtractHP
jp UpdateHPBar
SubtractHPFromUser:
; Subtract HP from mon
call SubtractHP
jp UpdateHPBarBattleHuds
SubtractHP:
ld hl, wBattleMonHP
ldh a, [hBattleTurn]
and a
jr z, .ok
ld hl, wEnemyMonHP
.ok
inc hl
ld a, [hl]
ld [wHPBuffer2], a
sub c
ld [hld], a
ld [wHPBuffer3], a
ld a, [hl]
ld [wHPBuffer2 + 1], a
sbc b
ld [hl], a
ld [wHPBuffer3 + 1], a
ret nc
ld a, [wHPBuffer2]
ld c, a
ld a, [wHPBuffer2 + 1]
ld b, a
xor a
ld [hli], a
ld [hl], a
ld [wHPBuffer3], a
ld [wHPBuffer3 + 1], a
ret
GetSixteenthMaxHP:
call GetQuarterMaxHP
; quarter result
srl c
srl c
; at least 1
ld a, c
and a
jr nz, .ok
inc c
.ok
ret
GetEighthMaxHP:
; output: bc
call GetQuarterMaxHP
; assumes nothing can have 1024 or more hp
; halve result
srl c
; at least 1
ld a, c
and a
jr nz, .end
inc c
.end
ret
GetQuarterMaxHP:
; output: bc
call GetMaxHP
; quarter result
srl b
rr c
srl b
rr c
; assumes nothing can have 1024 or more hp
; at least 1
ld a, c
and a
jr nz, .end
inc c
.end
ret
GetHalfMaxHP:
; output: bc
call GetMaxHP
; halve result
srl b
rr c
; at least 1
ld a, c
or b
jr nz, .end
inc c
.end
ret
GetMaxHP:
; output: bc, wHPBuffer1
ld hl, wBattleMonMaxHP
ldh a, [hBattleTurn]
and a
jr z, .ok
ld hl, wEnemyMonMaxHP
.ok
ld a, [hli]
ld [wHPBuffer1 + 1], a
ld b, a
ld a, [hl]
ld [wHPBuffer1], a
ld c, a
ret
GetHalfHP: ; unreferenced
ld hl, wBattleMonHP
ldh a, [hBattleTurn]
and a
jr z, .ok
ld hl, wEnemyMonHP
.ok
ld a, [hli]
ld b, a
ld a, [hli]
ld c, a
srl b
rr c
ld a, [hli]
ld [wHPBuffer1 + 1], a
ld a, [hl]
ld [wHPBuffer1], a
ret
CheckUserHasEnoughHP:
ld hl, wBattleMonHP + 1
ldh a, [hBattleTurn]
and a
jr z, .ok
ld hl, wEnemyMonHP + 1
.ok
ld a, c
sub [hl]
dec hl
ld a, b
sbc [hl]
ret
RestoreHP:
ld hl, wEnemyMonMaxHP
ldh a, [hBattleTurn]
and a
jr z, .ok
ld hl, wBattleMonMaxHP
.ok
ld a, [hli]
ld [wHPBuffer1 + 1], a
ld a, [hld]
ld [wHPBuffer1], a
dec hl
ld a, [hl]
ld [wHPBuffer2], a
add c
ld [hld], a
ld [wHPBuffer3], a
ld a, [hl]
ld [wHPBuffer2 + 1], a
adc b
ld [hli], a
ld [wHPBuffer3 + 1], a
ld a, [wHPBuffer1]
ld c, a
ld a, [hld]
sub c
ld a, [wHPBuffer1 + 1]
ld b, a
ld a, [hl]
sbc b
jr c, .overflow
ld a, b
ld [hli], a
ld [wHPBuffer3 + 1], a
ld a, c
ld [hl], a
ld [wHPBuffer3], a
.overflow
call SwitchTurnCore
call UpdateHPBarBattleHuds
jp SwitchTurnCore
UpdateHPBarBattleHuds:
call UpdateHPBar
jp UpdateBattleHuds
UpdateHPBar:
hlcoord 10, 9
ldh a, [hBattleTurn]
and a
ld a, 1
jr z, .ok
hlcoord 2, 2
xor a
.ok
push bc
ld [wWhichHPBar], a
predef AnimateHPBar
pop bc
ret
HandleEnemyMonFaint:
call FaintEnemyPokemon
ld hl, wBattleMonHP
ld a, [hli]
or [hl]
call z, FaintYourPokemon
xor a
ld [wWhichMonFaintedFirst], a
call UpdateBattleStateAndExperienceAfterEnemyFaint
call CheckPlayerPartyForFitMon
ld a, d
and a
jp z, LostBattle
ld hl, wBattleMonHP
ld a, [hli]
or [hl]
call nz, UpdatePlayerHUD
ld a, $1
ldh [hBGMapMode], a
ld c, 60
call DelayFrames
ld a, [wBattleMode]
dec a
jr nz, .trainer
ld a, 1
ld [wBattleEnded], a
ret
.trainer
call CheckEnemyTrainerDefeated
jp z, WinTrainerBattle
ld hl, wBattleMonHP
ld a, [hli]
or [hl]
jr nz, .player_mon_not_fainted
call AskUseNextPokemon
jr nc, .dont_flee
ld a, 1
ld [wBattleEnded], a
ret
.dont_flee
call ForcePlayerMonChoice
call CheckMobileBattleError
jp c, WildFled_EnemyFled_LinkBattleCanceled
ld a, BATTLEPLAYERACTION_USEITEM
ld [wBattlePlayerAction], a
call HandleEnemySwitch
jp z, WildFled_EnemyFled_LinkBattleCanceled
jr DoubleSwitch
.player_mon_not_fainted
ld a, BATTLEPLAYERACTION_USEITEM
ld [wBattlePlayerAction], a
call HandleEnemySwitch
jp z, WildFled_EnemyFled_LinkBattleCanceled
xor a ; BATTLEPLAYERACTION_USEMOVE
ld [wBattlePlayerAction], a
ret
DoubleSwitch:
ldh a, [hSerialConnectionStatus]
cp USING_EXTERNAL_CLOCK
jr z, .player_1
call ClearSprites
hlcoord 1, 0
lb bc, 4, 10
call ClearBox
call PlayerPartyMonEntrance
ld a, $1
call EnemyPartyMonEntrance
jr .done
.player_1
ld a, [wCurPartyMon]
push af
ld a, $1
call EnemyPartyMonEntrance
call ClearSprites
call LoadTilemapToTempTilemap
pop af
ld [wCurPartyMon], a
call PlayerPartyMonEntrance
.done
xor a ; BATTLEPLAYERACTION_USEMOVE
ld [wBattlePlayerAction], a
ret
UpdateBattleStateAndExperienceAfterEnemyFaint:
call UpdateBattleMonInParty
ld a, [wBattleMode]
dec a
jr z, .wild
ld a, [wCurOTMon]
ld hl, wOTPartyMon1HP
call GetPartyLocation
xor a
ld [hli], a
ld [hl], a
.wild
ld hl, wPlayerSubStatus3
res SUBSTATUS_IN_LOOP, [hl]
xor a
ld hl, wEnemyDamageTaken
ld [hli], a
ld [hl], a
call NewEnemyMonStatus
call BreakAttraction
ld a, [wBattleMode]
dec a
jr z, .wild2
jr .trainer
.wild2
call StopDangerSound
ld a, $1
ld [wBattleLowHealthAlarm], a
.trainer
ld hl, wBattleMonHP
ld a, [hli]
or [hl]
jr nz, .player_mon_did_not_faint
ld a, [wWhichMonFaintedFirst]
and a
jr nz, .player_mon_did_not_faint
call UpdateFaintedPlayerMon
.player_mon_did_not_faint
call CheckPlayerPartyForFitMon
ld a, d
and a
ret z
ld a, [wBattleMode]
dec a
call z, PlayVictoryMusic
call EmptyBattleTextbox
call LoadTilemapToTempTilemap
ld a, [wBattleResult]
and BATTLERESULT_BITMASK
ld [wBattleResult], a ; WIN
call IsAnyMonHoldingExpShare
jr z, .skip_exp
ld hl, wEnemyMonBaseStats
ld b, wEnemyMonEnd - wEnemyMonBaseStats
.loop
srl [hl]
inc hl
dec b
jr nz, .loop
.skip_exp
ld hl, wEnemyMonBaseStats
ld de, wBackupEnemyMonBaseStats
ld bc, wEnemyMonEnd - wEnemyMonBaseStats
call CopyBytes
xor a
ld [wGivingExperienceToExpShareHolders], a
call GiveExperiencePoints
call IsAnyMonHoldingExpShare
ret z
ld a, [wBattleParticipantsNotFainted]
push af
ld a, d
ld [wBattleParticipantsNotFainted], a
ld hl, wBackupEnemyMonBaseStats
ld de, wEnemyMonBaseStats
ld bc, wEnemyMonEnd - wEnemyMonBaseStats
call CopyBytes
ld a, $1
ld [wGivingExperienceToExpShareHolders], a
call GiveExperiencePoints
pop af
ld [wBattleParticipantsNotFainted], a
ret
IsAnyMonHoldingExpShare:
ld a, [wPartyCount]
ld b, a
ld hl, wPartyMon1
ld c, 1
ld d, 0
.loop
push hl
push bc
ld bc, MON_HP
add hl, bc
ld a, [hli]
or [hl]
pop bc
pop hl
jr z, .next
push hl
push bc
ld bc, MON_ITEM
add hl, bc
pop bc
ld a, [hl]
pop hl
cp EXP_SHARE
jr nz, .next
ld a, d
or c
ld d, a
.next
sla c
push de
ld de, PARTYMON_STRUCT_LENGTH
add hl, de
pop de
dec b
jr nz, .loop
ld a, d
ld e, 0
ld b, PARTY_LENGTH
.loop2
srl a
jr nc, .okay
inc e
.okay
dec b
jr nz, .loop2
ld a, e
and a
ret
StopDangerSound:
xor a
ld [wLowHealthAlarm], a
ret
FaintYourPokemon:
call StopDangerSound
call WaitSFX
ld a, $f0
ld [wCryTracks], a
ld a, [wBattleMonSpecies]
call PlayStereoCry
call PlayerMonFaintedAnimation
hlcoord 9, 7
lb bc, 5, 11
call ClearBox
ld hl, BattleText_MonFainted
jp StdBattleTextbox
FaintEnemyPokemon:
call WaitSFX
ld de, SFX_KINESIS
call PlaySFX
call EnemyMonFaintedAnimation
ld de, SFX_FAINT
call PlaySFX
hlcoord 1, 0
lb bc, 4, 10
call ClearBox
ld hl, BattleText_EnemyMonFainted
jp StdBattleTextbox
CheckEnemyTrainerDefeated:
ld a, [wOTPartyCount]
ld b, a
xor a
ld hl, wOTPartyMon1HP
ld de, PARTYMON_STRUCT_LENGTH
.loop
or [hl]
inc hl
or [hl]
dec hl
add hl, de
dec b
jr nz, .loop
and a
ret
HandleEnemySwitch:
ld hl, wEnemyHPPal
ld e, HP_BAR_LENGTH_PX
call UpdateHPPal
call WaitBGMap
farcall EnemySwitch_TrainerHud
ld a, [wLinkMode]
and a
jr z, .not_linked
call LinkBattleSendReceiveAction
ld a, [wBattleAction]
cp BATTLEACTION_FORFEIT
ret z
call SafeLoadTempTilemapToTilemap
.not_linked
ld hl, wBattleMonHP
ld a, [hli]
or [hl]
ld a, $0
jr nz, EnemyPartyMonEntrance
inc a
ret
EnemyPartyMonEntrance:
push af
xor a
ld [wEnemySwitchMonIndex], a
call NewEnemyMonStatus
call ResetEnemyStatLevels
call BreakAttraction
pop af
and a
jr nz, .set
call EnemySwitch
jr .done_switch
.set
call EnemySwitch_SetMode
.done_switch
call ResetBattleParticipants
call SetEnemyTurn
call SpikesDamage
xor a
ld [wEnemyMoveStruct + MOVE_ANIM], a
ld [wBattlePlayerAction], a
inc a
ret
WinTrainerBattle:
; Player won the battle
call StopDangerSound
ld a, $1
ld [wBattleLowHealthAlarm], a
ld [wBattleEnded], a
ld a, [wLinkMode]
and a
ld a, b
call z, PlayVictoryMusic
callfar Battle_GetTrainerName
ld hl, BattleText_EnemyWasDefeated
call StdBattleTextbox
call IsMobileBattle
jr z, .mobile
ld a, [wLinkMode]
and a
ret nz
ld a, [wInBattleTowerBattle]
bit 0, a
jr nz, .battle_tower
call BattleWinSlideInEnemyTrainerFrontpic
ld c, 40
call DelayFrames
ld a, [wBattleType]
cp BATTLETYPE_CANLOSE
jr nz, .skip_heal
predef HealParty
.skip_heal
ld a, [wDebugFlags]
bit DEBUG_BATTLE_F, a
jr nz, .skip_win_loss_text
call PrintWinLossText
.skip_win_loss_text
jp .give_money
.mobile
call BattleWinSlideInEnemyTrainerFrontpic
ld c, 40
call DelayFrames
ld c, $4 ; win
farcall Mobile_PrintOpponentBattleMessage
ret
.battle_tower
call BattleWinSlideInEnemyTrainerFrontpic
ld c, 40
call DelayFrames
call EmptyBattleTextbox
ld c, BATTLETOWERTEXT_LOSS_TEXT
farcall BattleTowerText
call WaitPressAorB_BlinkCursor
ld hl, wPayDayMoney
ld a, [hli]
or [hl]
inc hl
or [hl]
ret nz
call ClearTilemap
call ClearBGPalettes
ret
.give_money
ld a, [wAmuletCoin]
and a
call nz, .DoubleReward
call .CheckMaxedOutMomMoney
push af
ld a, FALSE
jr nc, .okay
ld a, [wMomSavingMoney]
and MOM_SAVING_MONEY_MASK
cp (1 << MOM_SAVING_SOME_MONEY_F) | (1 << MOM_SAVING_HALF_MONEY_F)
jr nz, .okay
inc a ; TRUE
.okay
ld b, a
ld c, 4
.loop
ld a, b
and a
jr z, .loop2
call .AddMoneyToMom
dec c
dec b
jr .loop
.loop2
ld a, c
and a
jr z, .done
call .AddMoneyToWallet
dec c
jr .loop2
.done
call .DoubleReward
call .DoubleReward
pop af
jr nc, .KeepItAll
ld a, [wMomSavingMoney]
and MOM_SAVING_MONEY_MASK
jr z, .KeepItAll
ld hl, .SentToMomTexts
dec a
ld c, a
ld b, 0
add hl, bc
add hl, bc
ld a, [hli]
ld h, [hl]
ld l, a
jp StdBattleTextbox
.KeepItAll:
ld hl, GotMoneyForWinningText
jp StdBattleTextbox
.AddMoneyToMom:
push bc
ld hl, wBattleReward + 2
ld de, wMomsMoney + 2
call AddBattleMoneyToAccount
pop bc
ret
.AddMoneyToWallet:
push bc
ld hl, wBattleReward + 2
ld de, wMoney + 2
call AddBattleMoneyToAccount
pop bc
ret
.DoubleReward:
ld hl, wBattleReward + 2
sla [hl]
dec hl
rl [hl]
dec hl
rl [hl]
ret nc
ld a, $ff
ld [hli], a
ld [hli], a
ld [hl], a
ret
.SentToMomTexts:
; entries correspond to MOM_SAVING_* constants
dw SentSomeToMomText
dw SentHalfToMomText
dw SentAllToMomText
.CheckMaxedOutMomMoney:
ld hl, wMomsMoney + 2
ld a, [hld]
cp LOW(MAX_MONEY)
ld a, [hld]
sbc HIGH(MAX_MONEY) ; mid
ld a, [hl]
sbc HIGH(MAX_MONEY >> 8)
ret
AddBattleMoneyToAccount:
ld c, 3
and a
push de
push hl
push bc
ld b, h
ld c, l
farcall StubbedTrainerRankings_AddToBattlePayouts
pop bc
pop hl
.loop
ld a, [de]
adc [hl]
ld [de], a
dec de
dec hl
dec c
jr nz, .loop
pop hl
ld a, [hld]
cp LOW(MAX_MONEY)
ld a, [hld]
sbc HIGH(MAX_MONEY) ; mid
ld a, [hl]
sbc HIGH(MAX_MONEY >> 8)
ret c
ld [hl], HIGH(MAX_MONEY >> 8)
inc hl
ld [hl], HIGH(MAX_MONEY) ; mid
inc hl
ld [hl], LOW(MAX_MONEY)
ret
PlayVictoryMusic:
push de
ld de, MUSIC_NONE
call PlayMusic
call DelayFrame
ld de, MUSIC_WILD_VICTORY
ld a, [wBattleMode]
dec a
jr nz, .trainer_victory
push de
call IsAnyMonHoldingExpShare
pop de
jr nz, .play_music
ld hl, wPayDayMoney
ld a, [hli]
or [hl]
jr nz, .play_music
ld a, [wBattleParticipantsNotFainted]
and a
jr z, .lost
jr .play_music
.trainer_victory
ld de, MUSIC_GYM_VICTORY
call IsGymLeader
jr c, .play_music
ld de, MUSIC_TRAINER_VICTORY
.play_music
call PlayMusic
.lost
pop de
ret
IsKantoGymLeader:
ld hl, KantoGymLeaders
jr IsGymLeaderCommon
IsGymLeader:
ld hl, GymLeaders
IsGymLeaderCommon:
push de
ld a, [wOtherTrainerClass]
ld de, 1
call IsInArray
pop de
ret
INCLUDE "data/trainers/leaders.asm"
HandlePlayerMonFaint:
call FaintYourPokemon
ld hl, wEnemyMonHP
ld a, [hli]
or [hl]
call z, FaintEnemyPokemon
ld a, $1
ld [wWhichMonFaintedFirst], a
call UpdateFaintedPlayerMon
call CheckPlayerPartyForFitMon
ld a, d
and a
jp z, LostBattle
ld hl, wEnemyMonHP
ld a, [hli]
or [hl]
jr nz, .notfainted
call UpdateBattleStateAndExperienceAfterEnemyFaint
ld a, [wBattleMode]
dec a
jr nz, .trainer
ld a, $1
ld [wBattleEnded], a
ret
.trainer
call CheckEnemyTrainerDefeated
jp z, WinTrainerBattle
.notfainted
call AskUseNextPokemon
jr nc, .switch
ld a, $1
ld [wBattleEnded], a
ret
.switch
call ForcePlayerMonChoice
call CheckMobileBattleError
jp c, WildFled_EnemyFled_LinkBattleCanceled
ld a, c
and a
ret nz
ld a, BATTLEPLAYERACTION_USEITEM
ld [wBattlePlayerAction], a
call HandleEnemySwitch
jp z, WildFled_EnemyFled_LinkBattleCanceled
jp DoubleSwitch
UpdateFaintedPlayerMon:
ld a, [wCurBattleMon]
ld c, a
ld hl, wBattleParticipantsNotFainted
ld b, RESET_FLAG
predef SmallFarFlagAction
ld hl, wEnemySubStatus3
res SUBSTATUS_IN_LOOP, [hl]
xor a
ld [wLowHealthAlarm], a
ld hl, wPlayerDamageTaken
ld [hli], a
ld [hl], a
ld [wBattleMonStatus], a
call UpdateBattleMonInParty
ld c, HAPPINESS_FAINTED
; If TheirLevel > (YourLevel + 30), use a different parameter
ld a, [wBattleMonLevel]
add 30
ld b, a
ld a, [wEnemyMonLevel]
cp b
jr c, .got_param
ld c, HAPPINESS_BEATENBYSTRONGFOE
.got_param
ld a, [wCurBattleMon]
ld [wCurPartyMon], a
callfar ChangeHappiness
ld a, [wBattleResult]
and BATTLERESULT_BITMASK
add LOSE
ld [wBattleResult], a
ld a, [wWhichMonFaintedFirst]
and a
ret z
; code was probably dummied out here
ret
AskUseNextPokemon:
call EmptyBattleTextbox
call LoadTilemapToTempTilemap
; We don't need to be here if we're in a Trainer battle,
; as that decision is made for us.
ld a, [wBattleMode]
and a
dec a
ret nz
ld hl, BattleText_UseNextMon
call StdBattleTextbox
.loop
lb bc, 1, 7
call PlaceYesNoBox
ld a, [wMenuCursorY]
jr c, .pressed_b
and a
ret
.pressed_b
ld a, [wMenuCursorY]
cp $1 ; YES
jr z, .loop
ld hl, wPartyMon1Speed
ld de, wEnemyMonSpeed
jp TryToRunAwayFromBattle
ForcePlayerMonChoice:
call EmptyBattleTextbox
call LoadStandardMenuHeader
call SetUpBattlePartyMenu
call ForcePickPartyMonInBattle
ld a, [wLinkMode]
and a
jr z, .skip_link
ld a, BATTLEPLAYERACTION_USEITEM
ld [wBattlePlayerAction], a
call LinkBattleSendReceiveAction
.skip_link
xor a ; BATTLEPLAYERACTION_USEMOVE
ld [wBattlePlayerAction], a
call CheckMobileBattleError
jr c, .enemy_fainted_mobile_error
ld hl, wEnemyMonHP
ld a, [hli]
or [hl]
jr nz, .send_out_pokemon
.enemy_fainted_mobile_error
call ClearSprites
call ClearBGPalettes
call _LoadHPBar
call ExitMenu
call LoadTilemapToTempTilemap
call WaitBGMap
call GetMemSGBLayout
call SetPalettes
xor a
ld c, a
ret
.send_out_pokemon
call ClearSprites
ld a, [wCurBattleMon]
ld [wLastPlayerMon], a
ld a, [wCurPartyMon]
ld [wCurBattleMon], a
call AddBattleParticipant
call InitBattleMon
call ResetPlayerStatLevels
call ClearPalettes
call DelayFrame
call _LoadHPBar
call CloseWindow
call GetMemSGBLayout
call SetPalettes
call SendOutMonText
call NewBattleMonStatus
call BreakAttraction
call SendOutPlayerMon
call EmptyBattleTextbox
call LoadTilemapToTempTilemap
call SetPlayerTurn
call SpikesDamage
ld a, $1
and a
ld c, a
ret
PlayerPartyMonEntrance:
ld a, [wCurBattleMon]
ld [wLastPlayerMon], a
ld a, [wCurPartyMon]
ld [wCurBattleMon], a
call AddBattleParticipant
call InitBattleMon
call ResetPlayerStatLevels
call SendOutMonText
call NewBattleMonStatus
call BreakAttraction
call SendOutPlayerMon
call EmptyBattleTextbox
call LoadTilemapToTempTilemap
call SetPlayerTurn
jp SpikesDamage
CheckMobileBattleError:
ld a, [wLinkMode]
cp LINK_MOBILE
jr nz, .not_mobile ; It's not a mobile battle
ld a, [wcd2b]
and a
jr z, .not_mobile
; We have a mobile battle and something else happened
scf
ret
.not_mobile
xor a
ret
IsMobileBattle:
ld a, [wLinkMode]
cp LINK_MOBILE
ret
SetUpBattlePartyMenu:
call ClearBGPalettes
SetUpBattlePartyMenu_Loop: ; switch to fullscreen menu?
farcall LoadPartyMenuGFX
farcall InitPartyMenuWithCancel
farcall InitPartyMenuBGPal7
farcall InitPartyMenuGFX
ret
JumpToPartyMenuAndPrintText:
farcall WritePartyMenuTilemap
farcall PrintPartyMenuText
call WaitBGMap
call SetPalettes
call DelayFrame
ret
SelectBattleMon:
call IsMobileBattle
jr z, .mobile
farcall PartyMenuSelect
ret
.mobile
farcall Mobile_PartyMenuSelect
ret
PickPartyMonInBattle:
.loop
ld a, PARTYMENUACTION_SWITCH ; Which PKMN?
ld [wPartyMenuActionText], a
call JumpToPartyMenuAndPrintText
call SelectBattleMon
ret c
call CheckIfCurPartyMonIsFitToFight
jr z, .loop
xor a
ret
SwitchMonAlreadyOut:
ld hl, wCurBattleMon
ld a, [wCurPartyMon]
cp [hl]
jr nz, .notout
ld hl, BattleText_MonIsAlreadyOut
call StdBattleTextbox
scf
ret
.notout
xor a
ret
ForcePickPartyMonInBattle:
; Can't back out.
.pick
call PickPartyMonInBattle
ret nc
call CheckMobileBattleError
ret c
ld de, SFX_WRONG
call PlaySFX
call WaitSFX
jr .pick
PickSwitchMonInBattle:
.pick
call PickPartyMonInBattle
ret c
call SwitchMonAlreadyOut
jr c, .pick
xor a
ret
ForcePickSwitchMonInBattle:
; Can't back out.
.pick
call ForcePickPartyMonInBattle
call CheckMobileBattleError
ret c
call SwitchMonAlreadyOut
jr c, .pick
xor a
ret
LostBattle:
ld a, 1
ld [wBattleEnded], a
ld a, [wInBattleTowerBattle]
bit 0, a
jr nz, .battle_tower
ld a, [wBattleType]
cp BATTLETYPE_CANLOSE
jr nz, .not_canlose
; Remove the enemy from the screen.
hlcoord 0, 0
lb bc, 8, 21
call ClearBox
call BattleWinSlideInEnemyTrainerFrontpic
ld c, 40
call DelayFrames
ld a, [wDebugFlags]
bit DEBUG_BATTLE_F, a
jr nz, .skip_win_loss_text
call PrintWinLossText
.skip_win_loss_text
ret
.battle_tower
; Remove the enemy from the screen.
hlcoord 0, 0
lb bc, 8, 21
call ClearBox
call BattleWinSlideInEnemyTrainerFrontpic
ld c, 40
call DelayFrames
call EmptyBattleTextbox
ld c, BATTLETOWERTEXT_WIN_TEXT
farcall BattleTowerText
call WaitPressAorB_BlinkCursor
call ClearTilemap
call ClearBGPalettes
ret
.not_canlose
ld a, [wLinkMode]
and a
jr nz, .LostLinkBattle
; Grayscale
ld b, SCGB_BATTLE_GRAYSCALE
call GetSGBLayout
call SetPalettes
jr .end
.LostLinkBattle:
call UpdateEnemyMonInParty
call CheckEnemyTrainerDefeated
jr nz, .not_tied
ld hl, TiedAgainstText
ld a, [wBattleResult]
and BATTLERESULT_BITMASK
add DRAW
ld [wBattleResult], a
jr .text
.not_tied
ld hl, LostAgainstText
call IsMobileBattle
jr z, .mobile
.text
call StdBattleTextbox
.end
scf
ret
.mobile
; Remove the enemy from the screen.
hlcoord 0, 0
lb bc, 8, 21
call ClearBox
call BattleWinSlideInEnemyTrainerFrontpic
ld c, 40
call DelayFrames
ld c, $3 ; lost
farcall Mobile_PrintOpponentBattleMessage
scf
ret
EnemyMonFaintedAnimation:
hlcoord 12, 5
decoord 12, 6
jp MonFaintedAnimation
PlayerMonFaintedAnimation:
hlcoord 1, 10
decoord 1, 11
jp MonFaintedAnimation
MonFaintedAnimation:
ld a, [wJoypadDisable]
push af
set JOYPAD_DISABLE_MON_FAINT_F, a
ld [wJoypadDisable], a
ld b, 7
.OuterLoop:
push bc
push de
push hl
ld b, 6
.InnerLoop:
push bc
push hl
push de
ld bc, 7
call CopyBytes
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, .InnerLoop
ld bc, 20
add hl, bc
ld de, .Spaces
call PlaceString
ld c, 2
call DelayFrames
pop hl
pop de
pop bc
dec b
jr nz, .OuterLoop
pop af
ld [wJoypadDisable], a
ret
.Spaces:
db " @"
SlideBattlePicOut:
ldh [hMapObjectIndex], a
ld c, a
.loop
push bc
push hl
ld b, $7
.loop2
push hl
call .DoFrame
pop hl
ld de, SCREEN_WIDTH
add hl, de
dec b
jr nz, .loop2
ld c, 2
call DelayFrames
pop hl
pop bc
dec c
jr nz, .loop
ret
.DoFrame:
ldh a, [hMapObjectIndex]
ld c, a
cp $8
jr nz, .back
.forward
ld a, [hli]
ld [hld], a
dec hl
dec c
jr nz, .forward
ret
.back
ld a, [hld]
ld [hli], a
inc hl
dec c
jr nz, .back
ret
ForceEnemySwitch:
call ResetEnemyBattleVars
ld a, [wEnemySwitchMonIndex]
dec a
ld b, a
call LoadEnemyMonToSwitchTo
call ClearEnemyMonBox
call NewEnemyMonStatus
call ResetEnemyStatLevels
call ShowSetEnemyMonAndSendOutAnimation
call BreakAttraction
call ResetBattleParticipants
ret
EnemySwitch:
call CheckWhetherToAskSwitch
jr nc, EnemySwitch_SetMode
; Shift Mode
call ResetEnemyBattleVars
call CheckWhetherSwitchmonIsPredetermined
jr c, .skip
call FindMonInOTPartyToSwitchIntoBattle
.skip
; 'b' contains the PartyNr of the mon the AI will switch to
call LoadEnemyMonToSwitchTo
call OfferSwitch
push af
call ClearEnemyMonBox
call ShowBattleTextEnemySentOut
call ShowSetEnemyMonAndSendOutAnimation
pop af
ret c
; If we're here, then we're switching too
xor a
ld [wBattleParticipantsNotFainted], a
ld [wBattleParticipantsIncludingFainted], a
ld [wBattlePlayerAction], a
inc a
ld [wEnemyIsSwitching], a
call LoadTilemapToTempTilemap
jp PlayerSwitch
EnemySwitch_SetMode:
call ResetEnemyBattleVars
call CheckWhetherSwitchmonIsPredetermined
jr c, .skip
call FindMonInOTPartyToSwitchIntoBattle
.skip
; 'b' contains the PartyNr of the mon the AI will switch to
call LoadEnemyMonToSwitchTo
ld a, 1
ld [wEnemyIsSwitching], a
call ClearEnemyMonBox
call ShowBattleTextEnemySentOut
jp ShowSetEnemyMonAndSendOutAnimation
CheckWhetherSwitchmonIsPredetermined:
; returns the enemy switchmon index in b, or
; returns carry if the index is not yet determined.
ld a, [wLinkMode]
and a
jr z, .not_linked
ld a, [wBattleAction]
sub BATTLEACTION_SWITCH1
ld b, a
jr .return_carry
.not_linked
ld a, [wEnemySwitchMonIndex]
and a
jr z, .check_wBattleHasJustStarted
dec a
ld b, a
jr .return_carry
.check_wBattleHasJustStarted
ld a, [wBattleHasJustStarted]
and a
ld b, 0
jr nz, .return_carry
and a
ret
.return_carry
scf
ret
ResetEnemyBattleVars:
; and draw empty Textbox
xor a
ld [wLastPlayerCounterMove], a
ld [wLastEnemyCounterMove], a
ld [wLastEnemyMove], a
ld [wCurEnemyMove], a
dec a
ld [wEnemyItemState], a
xor a
ld [wPlayerWrapCount], a
hlcoord 18, 0
ld a, 8
call SlideBattlePicOut
call EmptyBattleTextbox
jp LoadStandardMenuHeader
ResetBattleParticipants:
xor a
ld [wBattleParticipantsNotFainted], a
ld [wBattleParticipantsIncludingFainted], a
AddBattleParticipant:
ld a, [wCurBattleMon]
ld c, a
ld hl, wBattleParticipantsNotFainted
ld b, SET_FLAG
push bc
predef SmallFarFlagAction
pop bc
ld hl, wBattleParticipantsIncludingFainted
predef_jump SmallFarFlagAction
FindMonInOTPartyToSwitchIntoBattle:
ld b, -1
ld a, %000001
ld [wEnemyEffectivenessVsPlayerMons], a
ld [wPlayerEffectivenessVsEnemyMons], a
.loop
ld hl, wEnemyEffectivenessVsPlayerMons
sla [hl]
inc hl ; wPlayerEffectivenessVsEnemyMons
sla [hl]
inc b
ld a, [wOTPartyCount]
cp b
jp z, ScoreMonTypeMatchups
ld a, [wCurOTMon]
cp b
jr z, .discourage
ld hl, wOTPartyMon1HP
push bc
ld a, b
call GetPartyLocation
ld a, [hli]
ld c, a
ld a, [hl]
or c
pop bc
jr z, .discourage
call LookUpTheEffectivenessOfEveryMove
call IsThePlayerMonTypesEffectiveAgainstOTMon
jr .loop
.discourage
ld hl, wPlayerEffectivenessVsEnemyMons
set 0, [hl]
jr .loop
LookUpTheEffectivenessOfEveryMove:
push bc
ld hl, wOTPartyMon1Moves
ld a, b
call GetPartyLocation
pop bc
ld e, NUM_MOVES + 1
.loop
dec e
jr z, .done
ld a, [hli]
and a
jr z, .done
push hl
push de
push bc
dec a
ld hl, Moves
ld bc, MOVE_LENGTH
call AddNTimes
ld de, wEnemyMoveStruct
ld a, BANK(Moves)
call FarCopyBytes
call SetEnemyTurn
callfar BattleCheckTypeMatchup
pop bc
pop de
pop hl
ld a, [wTypeMatchup]
cp EFFECTIVE + 1
jr c, .loop
ld hl, wEnemyEffectivenessVsPlayerMons
set 0, [hl]
ret
.done
ret
IsThePlayerMonTypesEffectiveAgainstOTMon:
; Calculates the effectiveness of the types of the PlayerMon
; against the OTMon
push bc
ld hl, wOTPartyCount
ld a, b
inc a
ld c, a
ld b, 0
add hl, bc
ld a, [hl]
dec a
ld hl, BaseData + BASE_TYPES
ld bc, BASE_DATA_SIZE
call AddNTimes
ld de, wEnemyMonType
ld bc, BASE_CATCH_RATE - BASE_TYPES
ld a, BANK(BaseData)
call FarCopyBytes
ld a, [wBattleMonType1]
ld [wPlayerMoveStruct + MOVE_TYPE], a
call SetPlayerTurn
callfar BattleCheckTypeMatchup
ld a, [wTypeMatchup]
cp EFFECTIVE + 1
jr nc, .super_effective
ld a, [wBattleMonType2]
ld [wPlayerMoveStruct + MOVE_TYPE], a
callfar BattleCheckTypeMatchup
ld a, [wTypeMatchup]
cp EFFECTIVE + 1
jr nc, .super_effective
pop bc
ret
.super_effective
pop bc
ld hl, wEnemyEffectivenessVsPlayerMons
bit 0, [hl]
jr nz, .reset
inc hl ; wPlayerEffectivenessVsEnemyMons
set 0, [hl]
ret
.reset
res 0, [hl]
ret
ScoreMonTypeMatchups:
.loop1
ld hl, wEnemyEffectivenessVsPlayerMons
sla [hl]
inc hl ; wPlayerEffectivenessVsEnemyMons
sla [hl]
jr nc, .loop1
ld a, [wOTPartyCount]
ld b, a
ld c, [hl]
.loop2
sla c
jr nc, .okay
dec b
jr z, .loop5
jr .loop2
.okay
ld a, [wEnemyEffectivenessVsPlayerMons]
and a
jr z, .okay2
ld b, -1
ld c, a
.loop3
inc b
sla c
jr nc, .loop3
jr .quit
.okay2
ld b, -1
ld a, [wPlayerEffectivenessVsEnemyMons]
ld c, a
.loop4
inc b
sla c
jr c, .loop4
jr .quit
.loop5
ld a, [wOTPartyCount]
ld b, a
call BattleRandom
and $7
cp b
jr nc, .loop5
ld b, a
ld a, [wCurOTMon]
cp b
jr z, .loop5
ld hl, wOTPartyMon1HP
push bc
ld a, b
call GetPartyLocation
pop bc
ld a, [hli]
ld c, a
ld a, [hl]
or c
jr z, .loop5
.quit
ret
LoadEnemyMonToSwitchTo:
; 'b' contains the PartyNr of the mon the AI will switch to
ld a, b
ld [wCurPartyMon], a
ld hl, wOTPartyMon1Level
call GetPartyLocation
ld a, [hl]
ld [wCurPartyLevel], a
ld a, [wCurPartyMon]
inc a
ld hl, wOTPartyCount
ld c, a
ld b, 0
add hl, bc
ld a, [hl]
ld [wTempEnemyMonSpecies], a
ld [wCurPartySpecies], a
call LoadEnemyMon
ld a, [wCurPartySpecies]
cp UNOWN
jr nz, .skip_unown
ld a, [wFirstUnownSeen]
and a
jr nz, .skip_unown
ld hl, wEnemyMonDVs
predef GetUnownLetter
ld a, [wUnownLetter]
ld [wFirstUnownSeen], a
.skip_unown
ld hl, wEnemyMonHP
ld a, [hli]
ld [wEnemyHPAtTimeOfPlayerSwitch], a
ld a, [hl]
ld [wEnemyHPAtTimeOfPlayerSwitch + 1], a
ret
CheckWhetherToAskSwitch:
ld a, [wBattleHasJustStarted]
dec a
jp z, .return_nc
ld a, [wPartyCount]
dec a
jp z, .return_nc
ld a, [wLinkMode]
and a
jp nz, .return_nc
ld a, [wOptions]
bit BATTLE_SHIFT, a
jr nz, .return_nc
ld a, [wCurPartyMon]
push af
ld a, [wCurBattleMon]
ld [wCurPartyMon], a
farcall CheckCurPartyMonFainted
pop bc
ld a, b
ld [wCurPartyMon], a
jr c, .return_nc
scf
ret
.return_nc
and a
ret
OfferSwitch:
ld a, [wCurPartyMon]
push af
callfar Battle_GetTrainerName
ld hl, BattleText_EnemyIsAboutToUseWillPlayerChangeMon
call StdBattleTextbox
lb bc, 1, 7
call PlaceYesNoBox
ld a, [wMenuCursorY]
dec a
jr nz, .said_no
call SetUpBattlePartyMenu
call PickSwitchMonInBattle
jr c, .canceled_switch
ld a, [wCurBattleMon]
ld [wLastPlayerMon], a
ld a, [wCurPartyMon]
ld [wCurBattleMon], a
call ClearPalettes
call DelayFrame
call _LoadHPBar
pop af
ld [wCurPartyMon], a
xor a
ld [wCurEnemyMove], a
ld [wCurPlayerMove], a
and a
ret
.canceled_switch
call ClearPalettes
call DelayFrame
call _LoadHPBar
.said_no
pop af
ld [wCurPartyMon], a
scf
ret
ClearEnemyMonBox:
xor a
ldh [hBGMapMode], a
call ExitMenu
call ClearSprites
hlcoord 1, 0
lb bc, 4, 10
call ClearBox
call WaitBGMap
jp FinishBattleAnim
ShowBattleTextEnemySentOut:
callfar Battle_GetTrainerName
ld hl, BattleText_EnemySentOut
call StdBattleTextbox
jp WaitBGMap
ShowSetEnemyMonAndSendOutAnimation:
ld a, [wTempEnemyMonSpecies]
ld [wCurPartySpecies], a
ld [wCurSpecies], a
call GetBaseData
ld a, OTPARTYMON
ld [wMonType], a
predef CopyMonToTempMon
call GetEnemyMonFrontpic
xor a
ld [wNumHits], a
ld [wBattleAnimParam], a
call SetEnemyTurn
ld de, ANIM_SEND_OUT_MON
call Call_PlayBattleAnim
call BattleCheckEnemyShininess
jr nc, .not_shiny
ld a, 1 ; shiny anim
ld [wBattleAnimParam], a
ld de, ANIM_SEND_OUT_MON
call Call_PlayBattleAnim
.not_shiny
ld bc, wTempMonSpecies
farcall CheckFaintedFrzSlp
jr c, .skip_cry
farcall CheckBattleScene
jr c, .cry_no_anim
hlcoord 12, 0
ld d, $0
ld e, ANIM_MON_SLOW
predef AnimateFrontpic
jr .skip_cry
.cry_no_anim
ld a, $f
ld [wCryTracks], a
ld a, [wTempEnemyMonSpecies]
call PlayStereoCry
.skip_cry
call UpdateEnemyHUD
ld a, $1
ldh [hBGMapMode], a
ret
NewEnemyMonStatus:
xor a
ld [wLastPlayerCounterMove], a
ld [wLastEnemyCounterMove], a
ld [wLastEnemyMove], a
ld hl, wEnemySubStatus1
rept 4
ld [hli], a
endr
ld [hl], a
ld [wEnemyDisableCount], a
ld [wEnemyFuryCutterCount], a
ld [wEnemyProtectCount], a
ld [wEnemyRageCounter], a
ld [wEnemyDisabledMove], a
ld [wEnemyMinimized], a
ld [wPlayerWrapCount], a
ld [wEnemyWrapCount], a
ld [wEnemyTurnsTaken], a
ld hl, wPlayerSubStatus5
res SUBSTATUS_CANT_RUN, [hl]
ret
ResetEnemyStatLevels:
ld a, BASE_STAT_LEVEL
ld b, NUM_LEVEL_STATS
ld hl, wEnemyStatLevels
.loop
ld [hli], a
dec b
jr nz, .loop
ret
CheckPlayerPartyForFitMon:
; Has the player any mon in his Party that can fight?
ld a, [wPartyCount]
ld e, a
xor a
ld hl, wPartyMon1HP
ld bc, PARTYMON_STRUCT_LENGTH - 1
.loop
or [hl]
inc hl ; + 1
or [hl]
add hl, bc
dec e
jr nz, .loop
ld d, a
ret
CheckIfCurPartyMonIsFitToFight:
ld a, [wCurPartyMon]
ld hl, wPartyMon1HP
call GetPartyLocation
ld a, [hli]
or [hl]
ret nz
ld a, [wBattleHasJustStarted]
and a
jr nz, .finish_fail
ld hl, wPartySpecies
ld a, [wCurPartyMon]
ld c, a
ld b, 0
add hl, bc
ld a, [hl]
cp EGG
ld hl, BattleText_AnEGGCantBattle
jr z, .print_textbox
ld hl, BattleText_TheresNoWillToBattle
.print_textbox
call StdBattleTextbox
.finish_fail
xor a
ret
TryToRunAwayFromBattle:
; Run away from battle, with or without item
ld a, [wBattleType]
cp BATTLETYPE_DEBUG
jp z, .can_escape
cp BATTLETYPE_CONTEST
jp z, .can_escape
cp BATTLETYPE_TRAP
jp z, .cant_escape
cp BATTLETYPE_CELEBI
jp z, .cant_escape
cp BATTLETYPE_SHINY
jp z, .cant_escape
cp BATTLETYPE_SUICUNE
jp z, .cant_escape
ld a, [wLinkMode]
and a
jp nz, .can_escape
ld a, [wBattleMode]
dec a
jp nz, .cant_run_from_trainer
ld a, [wEnemySubStatus5]
bit SUBSTATUS_CANT_RUN, a
jp nz, .cant_escape
ld a, [wPlayerWrapCount]
and a
jp nz, .cant_escape
push hl
push de
ld a, [wBattleMonItem]
ld [wNamedObjectIndex], a
ld b, a
callfar GetItemHeldEffect
ld a, b
cp HELD_ESCAPE
pop de
pop hl
jr nz, .no_flee_item
call SetPlayerTurn
call GetItemName
ld hl, BattleText_UserFledUsingAStringBuffer1
call StdBattleTextbox
jp .can_escape
.no_flee_item
ld a, [wNumFleeAttempts]
inc a
ld [wNumFleeAttempts], a
ld a, [hli]
ldh [hMultiplicand + 1], a
ld a, [hl]
ldh [hMultiplicand + 2], a
ld a, [de]
inc de
ldh [hEnemyMonSpeed + 0], a
ld a, [de]
ldh [hEnemyMonSpeed + 1], a
call SafeLoadTempTilemapToTilemap
ld de, hMultiplicand + 1
ld hl, hEnemyMonSpeed
ld c, 2
call CompareBytes
jr nc, .can_escape
xor a
ldh [hMultiplicand + 0], a
ld a, 32
ldh [hMultiplier], a
call Multiply
ldh a, [hProduct + 2]
ldh [hDividend + 0], a
ldh a, [hProduct + 3]
ldh [hDividend + 1], a
ldh a, [hEnemyMonSpeed + 0]
ld b, a
ldh a, [hEnemyMonSpeed + 1]
srl b
rr a
srl b
rr a
and a
jr z, .can_escape
ldh [hDivisor], a
ld b, 2
call Divide
ldh a, [hQuotient + 2]
and a
jr nz, .can_escape
ld a, [wNumFleeAttempts]
ld c, a
.loop
dec c
jr z, .cant_escape_2
ld b, 30
ldh a, [hQuotient + 3]
add b
ldh [hQuotient + 3], a
jr c, .can_escape
jr .loop
.cant_escape_2
call BattleRandom
ld b, a
ldh a, [hQuotient + 3]
cp b
jr nc, .can_escape
ld a, BATTLEPLAYERACTION_USEITEM
ld [wBattlePlayerAction], a
ld hl, BattleText_CantEscape2
jr .print_inescapable_text
.cant_escape
ld hl, BattleText_CantEscape
jr .print_inescapable_text
.cant_run_from_trainer
ld hl, BattleText_TheresNoEscapeFromTrainerBattle
.print_inescapable_text
call StdBattleTextbox
ld a, TRUE
ld [wFailedToFlee], a
call LoadTilemapToTempTilemap
and a
ret
.can_escape
ld a, [wLinkMode]
and a
ld a, DRAW
jr z, .fled
call LoadTilemapToTempTilemap
xor a ; BATTLEPLAYERACTION_USEMOVE
ld [wBattlePlayerAction], a
ld a, $f
ld [wCurMoveNum], a
xor a
ld [wCurPlayerMove], a
call LinkBattleSendReceiveAction
call SafeLoadTempTilemapToTilemap
call CheckMobileBattleError
jr c, .mobile
; Got away safely
ld a, [wBattleAction]
cp BATTLEACTION_FORFEIT
ld a, DRAW
jr z, .fled
dec a ; LOSE
.fled
ld b, a
ld a, [wBattleResult]
and BATTLERESULT_BITMASK
add b
ld [wBattleResult], a
call StopDangerSound
push de
ld de, SFX_RUN
call WaitPlaySFX
pop de
call WaitSFX
ld hl, BattleText_GotAwaySafely
call StdBattleTextbox
call WaitSFX
call LoadTilemapToTempTilemap
scf
ret
.mobile
call StopDangerSound
ld hl, wcd2a
bit 4, [hl]
jr nz, .skip_link_error
ld hl, BattleText_LinkErrorBattleCanceled
call StdBattleTextbox
.skip_link_error
call WaitSFX
call LoadTilemapToTempTilemap
scf
ret
InitBattleMon:
ld a, MON_SPECIES
call GetPartyParamLocation
ld de, wBattleMonSpecies
ld bc, MON_ID
call CopyBytes
ld bc, MON_DVS - MON_ID
add hl, bc
ld de, wBattleMonDVs
ld bc, MON_POKERUS - MON_DVS
call CopyBytes
inc hl
inc hl
inc hl
ld de, wBattleMonLevel
ld bc, PARTYMON_STRUCT_LENGTH - MON_LEVEL
call CopyBytes
ld a, [wBattleMonSpecies]
ld [wTempBattleMonSpecies], a
ld [wCurPartySpecies], a
ld [wCurSpecies], a
call GetBaseData
ld a, [wBaseType1]
ld [wBattleMonType1], a
ld a, [wBaseType2]
ld [wBattleMonType2], a
ld hl, wPartyMonNicknames
ld a, [wCurBattleMon]
call SkipNames
ld de, wBattleMonNickname
ld bc, MON_NAME_LENGTH
call CopyBytes
ld hl, wBattleMonAttack
ld de, wPlayerStats
ld bc, PARTYMON_STRUCT_LENGTH - MON_ATK
call CopyBytes
call ApplyStatusEffectOnPlayerStats
call BadgeStatBoosts
ret
BattleCheckPlayerShininess:
call GetPartyMonDVs
jr BattleCheckShininess
BattleCheckEnemyShininess:
call GetEnemyMonDVs
BattleCheckShininess:
ld b, h
ld c, l
callfar CheckShininess
ret
GetPartyMonDVs:
ld hl, wBattleMonDVs
ld a, [wPlayerSubStatus5]
bit SUBSTATUS_TRANSFORMED, a
ret z
ld hl, wPartyMon1DVs
ld a, [wCurBattleMon]
jp GetPartyLocation
GetEnemyMonDVs:
ld hl, wEnemyMonDVs
ld a, [wEnemySubStatus5]
bit SUBSTATUS_TRANSFORMED, a
ret z
ld hl, wEnemyBackupDVs
ld a, [wBattleMode]
dec a
ret z
ld hl, wOTPartyMon1DVs
ld a, [wCurOTMon]
jp GetPartyLocation
ResetPlayerStatLevels:
ld a, BASE_STAT_LEVEL
ld b, NUM_LEVEL_STATS
ld hl, wPlayerStatLevels
.loop
ld [hli], a
dec b
jr nz, .loop
ret
InitEnemyMon:
ld a, [wCurPartyMon]
ld hl, wOTPartyMon1Species
call GetPartyLocation
ld de, wEnemyMonSpecies
ld bc, MON_ID
call CopyBytes
ld bc, MON_DVS - MON_ID
add hl, bc
ld de, wEnemyMonDVs
ld bc, MON_POKERUS - MON_DVS
call CopyBytes
inc hl
inc hl
inc hl
ld de, wEnemyMonLevel
ld bc, PARTYMON_STRUCT_LENGTH - MON_LEVEL
call CopyBytes
ld a, [wEnemyMonSpecies]
ld [wCurSpecies], a
call GetBaseData
ld hl, wOTPartyMonNicknames
ld a, [wCurPartyMon]
call SkipNames
ld de, wEnemyMonNickname
ld bc, MON_NAME_LENGTH
call CopyBytes
ld hl, wEnemyMonAttack
ld de, wEnemyStats
ld bc, PARTYMON_STRUCT_LENGTH - MON_ATK
call CopyBytes
call ApplyStatusEffectOnEnemyStats
ld hl, wBaseType1
ld de, wEnemyMonType1
ld a, [hli]
ld [de], a
inc de
ld a, [hl]
ld [de], a
; The enemy mon's base Sp. Def isn't needed since its base
; Sp. Atk is also used to calculate Sp. Def stat experience.
ld hl, wBaseStats
ld de, wEnemyMonBaseStats
ld b, NUM_STATS - 1
.loop
ld a, [hli]
ld [de], a
inc de
dec b
jr nz, .loop
ld a, [wCurPartyMon]
ld [wCurOTMon], a
ret
SwitchPlayerMon:
call ClearSprites
ld a, [wCurBattleMon]
ld [wLastPlayerMon], a
ld a, [wCurPartyMon]
ld [wCurBattleMon], a
call AddBattleParticipant
call InitBattleMon
call ResetPlayerStatLevels
call NewBattleMonStatus
call BreakAttraction
call SendOutPlayerMon
call EmptyBattleTextbox
call LoadTilemapToTempTilemap
ld hl, wEnemyMonHP
ld a, [hli]
or [hl]
ret
SendOutPlayerMon:
ld hl, wBattleMonDVs
predef GetUnownLetter
hlcoord 1, 5
ld b, 7
ld c, 8
call ClearBox
call WaitBGMap
xor a
ldh [hBGMapMode], a
call GetBattleMonBackpic
xor a
ldh [hGraphicStartTile], a
ld [wBattleMenuCursorPosition], a
ld [wCurMoveNum], a
ld [wTypeModifier], a
ld [wPlayerMoveStruct + MOVE_ANIM], a
ld [wLastPlayerCounterMove], a
ld [wLastEnemyCounterMove], a
ld [wLastPlayerMove], a
call CheckAmuletCoin
call FinishBattleAnim
xor a
ld [wEnemyWrapCount], a
call SetPlayerTurn
xor a
ld [wNumHits], a
ld [wBattleAnimParam], a
ld de, ANIM_SEND_OUT_MON
call Call_PlayBattleAnim
call BattleCheckPlayerShininess
jr nc, .not_shiny
ld a, 1
ld [wBattleAnimParam], a
ld de, ANIM_SEND_OUT_MON
call Call_PlayBattleAnim
.not_shiny
ld a, MON_SPECIES
call GetPartyParamLocation
ld b, h
ld c, l
farcall CheckFaintedFrzSlp
jr c, .statused
ld a, $f0
ld [wCryTracks], a
ld a, [wCurPartySpecies]
call PlayStereoCry
.statused
call UpdatePlayerHUD
ld a, $1
ldh [hBGMapMode], a
ret
NewBattleMonStatus:
xor a
ld [wLastPlayerCounterMove], a
ld [wLastEnemyCounterMove], a
ld [wLastPlayerMove], a
ld hl, wPlayerSubStatus1
rept 4
ld [hli], a
endr
ld [hl], a
ld hl, wPlayerUsedMoves
ld [hli], a
ld [hli], a
ld [hli], a
ld [hl], a
ld [wPlayerDisableCount], a
ld [wPlayerFuryCutterCount], a
ld [wPlayerProtectCount], a
ld [wPlayerRageCounter], a
ld [wDisabledMove], a
ld [wPlayerMinimized], a
ld [wEnemyWrapCount], a
ld [wPlayerWrapCount], a
ld [wPlayerTurnsTaken], a
ld hl, wEnemySubStatus5
res SUBSTATUS_CANT_RUN, [hl]
ret
BreakAttraction:
ld hl, wPlayerSubStatus1
res SUBSTATUS_IN_LOVE, [hl]
ld hl, wEnemySubStatus1
res SUBSTATUS_IN_LOVE, [hl]
ret
SpikesDamage:
ld hl, wPlayerScreens
ld de, wBattleMonType
ld bc, UpdatePlayerHUD
ldh a, [hBattleTurn]
and a
jr z, .ok
ld hl, wEnemyScreens
ld de, wEnemyMonType
ld bc, UpdateEnemyHUD
.ok
bit SCREENS_SPIKES, [hl]
ret z
; Flying-types aren't affected by Spikes.
ld a, [de]
cp FLYING
ret z
inc de
ld a, [de]
cp FLYING
ret z
push bc
ld hl, BattleText_UserHurtBySpikes ; "hurt by SPIKES!"
call StdBattleTextbox
call GetEighthMaxHP
call SubtractHPFromTarget
pop hl
call .hl
jp WaitBGMap
.hl
jp hl
PursuitSwitch:
ld a, BATTLE_VARS_MOVE
call GetBattleVar
ld b, a
call GetMoveEffect
ld a, b
cp EFFECT_PURSUIT
jr nz, .done
ld a, [wCurBattleMon]
push af
ld hl, DoPlayerTurn
ldh a, [hBattleTurn]
and a
jr z, .do_turn
ld hl, DoEnemyTurn
ld a, [wLastPlayerMon]
ld [wCurBattleMon], a
.do_turn
ld a, BANK(DoPlayerTurn) ; aka BANK(DoEnemyTurn)
rst FarCall
ld a, BATTLE_VARS_MOVE
call GetBattleVarAddr
ld a, $ff
ld [hl], a
pop af
ld [wCurBattleMon], a
ldh a, [hBattleTurn]
and a
jr z, .check_enemy_fainted
ld a, [wLastPlayerMon]
call UpdateBattleMon
ld hl, wBattleMonHP
ld a, [hli]
or [hl]
jr nz, .done
; BUG: A Pokémon that fainted from Pursuit will have its old status condition when revived (see docs/bugs_and_glitches.md)
ld a, $f0
ld [wCryTracks], a
ld a, [wBattleMonSpecies]
call PlayStereoCry
ld a, [wLastPlayerMon]
ld c, a
ld hl, wBattleParticipantsNotFainted
ld b, RESET_FLAG
predef SmallFarFlagAction
call PlayerMonFaintedAnimation
ld hl, BattleText_MonFainted
jr .done_fainted
.check_enemy_fainted
ld hl, wEnemyMonHP
ld a, [hli]
or [hl]
jr nz, .done
ld de, SFX_KINESIS
call PlaySFX
call WaitSFX
ld de, SFX_FAINT
call PlaySFX
call WaitSFX
call EnemyMonFaintedAnimation
ld hl, BattleText_EnemyMonFainted
.done_fainted
call StdBattleTextbox
scf
ret
.done
and a
ret
RecallPlayerMon:
ldh a, [hBattleTurn]
push af
xor a
ldh [hBattleTurn], a
ld [wNumHits], a
ld de, ANIM_RETURN_MON
call Call_PlayBattleAnim
pop af
ldh [hBattleTurn], a
ret
HandleHealingItems:
ldh a, [hSerialConnectionStatus]
cp USING_EXTERNAL_CLOCK
jr z, .player_1
call SetPlayerTurn
call HandleHPHealingItem
call UseHeldStatusHealingItem
call UseConfusionHealingItem
call SetEnemyTurn
call HandleHPHealingItem
call UseHeldStatusHealingItem
jp UseConfusionHealingItem
.player_1
call SetEnemyTurn
call HandleHPHealingItem
call UseHeldStatusHealingItem
call UseConfusionHealingItem
call SetPlayerTurn
call HandleHPHealingItem
call UseHeldStatusHealingItem
jp UseConfusionHealingItem
HandleHPHealingItem:
callfar GetOpponentItem
ld a, b
cp HELD_BERRY
ret nz
ld de, wEnemyMonHP + 1
ld hl, wEnemyMonMaxHP
ldh a, [hBattleTurn]
and a
jr z, .go
ld de, wBattleMonHP + 1
ld hl, wBattleMonMaxHP
.go
; If, and only if, Pokemon's HP is less than half max, use the item.
; Store current HP in Buffer 3/4
push bc
ld a, [de]
ld [wHPBuffer2], a
add a
ld c, a
dec de
ld a, [de]
inc de
ld [wHPBuffer2 + 1], a
adc a
ld b, a
ld a, b
cp [hl]
ld a, c
pop bc
jr z, .equal
jr c, .less
ret
.equal
inc hl
cp [hl]
dec hl
ret nc
.less
call ItemRecoveryAnim
; store max HP in wHPBuffer1
ld a, [hli]
ld [wHPBuffer1 + 1], a
ld a, [hl]
ld [wHPBuffer1], a
ld a, [de]
add c
ld [wHPBuffer3], a
ld c, a
dec de
ld a, [de]
adc 0
ld [wHPBuffer3 + 1], a
ld b, a
ld a, [hld]
cp c
ld a, [hl]
sbc b
jr nc, .okay
ld a, [hli]
ld [wHPBuffer3 + 1], a
ld a, [hl]
ld [wHPBuffer3], a
.okay
ld a, [wHPBuffer3 + 1]
ld [de], a
inc de
ld a, [wHPBuffer3]
ld [de], a
ldh a, [hBattleTurn]
ld [wWhichHPBar], a
and a
hlcoord 2, 2
jr z, .got_hp_bar_coords
hlcoord 10, 9
.got_hp_bar_coords
ld [wWhichHPBar], a
predef AnimateHPBar
UseOpponentItem:
call RefreshBattleHuds
callfar GetOpponentItem
ld a, [hl]
ld [wNamedObjectIndex], a
call GetItemName
callfar ConsumeHeldItem
ld hl, RecoveredUsingText
jp StdBattleTextbox
ItemRecoveryAnim:
push hl
push de
push bc
call EmptyBattleTextbox
ld a, RECOVER
ld [wFXAnimID], a
call SwitchTurnCore
xor a
ld [wNumHits], a
ld [wFXAnimID + 1], a
predef PlayBattleAnim
call SwitchTurnCore
pop bc
pop de
pop hl
ret
UseHeldStatusHealingItem:
callfar GetOpponentItem
ld hl, HeldStatusHealingEffects
.loop
ld a, [hli]
cp $ff
ret z
inc hl
cp b
jr nz, .loop
dec hl
ld b, [hl]
ld a, BATTLE_VARS_STATUS_OPP
call GetBattleVarAddr
and b
ret z
xor a
ld [hl], a
push bc
call UpdateOpponentInParty
pop bc
ld a, BATTLE_VARS_SUBSTATUS5_OPP
call GetBattleVarAddr
and [hl]
res SUBSTATUS_TOXIC, [hl]
ld a, BATTLE_VARS_SUBSTATUS1_OPP
call GetBattleVarAddr
and [hl]
res SUBSTATUS_NIGHTMARE, [hl]
ld a, b
cp ALL_STATUS
jr nz, .skip_confuse
ld a, BATTLE_VARS_SUBSTATUS3_OPP
call GetBattleVarAddr
res SUBSTATUS_CONFUSED, [hl]
.skip_confuse
ld hl, CalcEnemyStats
ldh a, [hBattleTurn]
and a
jr z, .got_pointer
ld hl, CalcPlayerStats
.got_pointer
call SwitchTurnCore
ld a, BANK(CalcPlayerStats) ; aka BANK(CalcEnemyStats)
rst FarCall
call SwitchTurnCore
call ItemRecoveryAnim
call UseOpponentItem
ld a, $1
and a
ret
INCLUDE "data/battle/held_heal_status.asm"
UseConfusionHealingItem:
ld a, BATTLE_VARS_SUBSTATUS3_OPP
call GetBattleVar
bit SUBSTATUS_CONFUSED, a
ret z
callfar GetOpponentItem
ld a, b
cp HELD_HEAL_CONFUSION
jr z, .heal_status
cp HELD_HEAL_STATUS
ret nz
.heal_status
ld a, [hl]
ld [wNamedObjectIndex], a
ld a, BATTLE_VARS_SUBSTATUS3_OPP
call GetBattleVarAddr
res SUBSTATUS_CONFUSED, [hl]
call GetItemName
call ItemRecoveryAnim
ld hl, BattleText_ItemHealedConfusion
call StdBattleTextbox
ldh a, [hBattleTurn]
and a
jr nz, .do_partymon
call GetOTPartymonItem
xor a
ld [bc], a
ld a, [wBattleMode]
dec a
ret z
ld [hl], $0
ret
.do_partymon
call GetPartymonItem
xor a
ld [bc], a
ld [hl], a
ret
HandleStatBoostingHeldItems:
; The effects handled here are not used in-game.
ldh a, [hSerialConnectionStatus]
cp USING_EXTERNAL_CLOCK
jr z, .player_1
call .DoPlayer
jp .DoEnemy
.player_1
call .DoEnemy
jp .DoPlayer
.DoPlayer:
call GetPartymonItem
ld a, $0
jp .HandleItem
.DoEnemy:
call GetOTPartymonItem
ld a, $1
.HandleItem:
ldh [hBattleTurn], a
ld d, h
ld e, l
push de
push bc
ld a, [bc]
ld b, a
callfar GetItemHeldEffect
ld hl, HeldStatUpItems
.loop
ld a, [hli]
cp -1
jr z, .finish
inc hl
inc hl
cp b
jr nz, .loop
pop bc
ld a, [bc]
ld [wNamedObjectIndex], a
push bc
dec hl
dec hl
ld a, [hli]
ld h, [hl]
ld l, a
ld a, BANK(BattleCommand_AttackUp)
rst FarCall
pop bc
pop de
ld a, [wFailedMessage]
and a
ret nz
xor a
ld [bc], a
ld [de], a
call GetItemName
ld hl, BattleText_UsersStringBuffer1Activated
call StdBattleTextbox
callfar BattleCommand_StatUpMessage
ret
.finish
pop bc
pop de
ret
INCLUDE "data/battle/held_stat_up.asm"
GetPartymonItem:
ld hl, wPartyMon1Item
ld a, [wCurBattleMon]
call GetPartyLocation
ld bc, wBattleMonItem
ret
GetOTPartymonItem:
ld hl, wOTPartyMon1Item
ld a, [wCurOTMon]
call GetPartyLocation
ld bc, wEnemyMonItem
ret
UpdateBattleHUDs:
push hl
push de
push bc
call DrawPlayerHUD
ld hl, wPlayerHPPal
call SetHPPal
call CheckDanger
call DrawEnemyHUD
ld hl, wEnemyHPPal
call SetHPPal
pop bc
pop de
pop hl
ret
UpdatePlayerHUD::
push hl
push de
push bc
call DrawPlayerHUD
call UpdatePlayerHPPal
call CheckDanger
pop bc
pop de
pop hl
ret
DrawPlayerHUD:
xor a
ldh [hBGMapMode], a
; Clear the area
hlcoord 9, 7
lb bc, 5, 11
call ClearBox
farcall DrawPlayerHUDBorder
hlcoord 18, 9
ld [hl], $73 ; vertical bar
call PrintPlayerHUD
; HP bar
hlcoord 10, 9
ld b, 1
xor a ; PARTYMON
ld [wMonType], a
predef DrawPlayerHP
; Exp bar
push de
ld a, [wCurBattleMon]
ld hl, wPartyMon1Exp + 2
call GetPartyLocation
ld d, h
ld e, l
hlcoord 10, 11
ld a, [wTempMonLevel]
ld b, a
call FillInExpBar
pop de
ret
UpdatePlayerHPPal:
ld hl, wPlayerHPPal
jp UpdateHPPal
CheckDanger:
ld hl, wBattleMonHP
ld a, [hli]
or [hl]
jr z, .no_danger
ld a, [wBattleLowHealthAlarm]
and a
jr nz, .done
ld a, [wPlayerHPPal]
cp HP_RED
jr z, .danger
.no_danger
ld hl, wLowHealthAlarm
res DANGER_ON_F, [hl]
jr .done
.danger
ld hl, wLowHealthAlarm
set DANGER_ON_F, [hl]
.done
ret
PrintPlayerHUD:
ld de, wBattleMonNickname
hlcoord 10, 7
call Battle_DummyFunction
call PlaceString
push bc
ld a, [wCurBattleMon]
ld hl, wPartyMon1DVs
call GetPartyLocation
ld de, wTempMonDVs
ld a, [hli]
ld [de], a
inc de
ld a, [hl]
ld [de], a
ld hl, wBattleMonLevel
ld de, wTempMonLevel
ld bc, wTempMonStructEnd - wTempMonLevel
call CopyBytes ; battle_struct and party_struct end with the same data
ld a, [wCurBattleMon]
ld hl, wPartyMon1Species
call GetPartyLocation
ld a, [hl]
ld [wCurPartySpecies], a
ld [wCurSpecies], a
call GetBaseData
pop hl
dec hl
ld a, TEMPMON
ld [wMonType], a
callfar GetGender
ld a, " "
jr c, .got_gender_char
ld a, "♂"
jr nz, .got_gender_char
ld a, "♀"
.got_gender_char
hlcoord 17, 8
ld [hl], a
hlcoord 14, 8
push af ; back up gender
push hl
ld de, wBattleMonStatus
predef PlaceNonFaintStatus
pop hl
pop bc
ret nz
ld a, b
cp " "
jr nz, .copy_level ; male or female
dec hl ; genderless
.copy_level
ld a, [wBattleMonLevel]
ld [wTempMonLevel], a
jp PrintLevel
UpdateEnemyHUD::
push hl
push de
push bc
call DrawEnemyHUD
call UpdateEnemyHPPal
pop bc
pop de
pop hl
ret
DrawEnemyHUD:
xor a
ldh [hBGMapMode], a
hlcoord 1, 0
lb bc, 4, 11
call ClearBox
farcall DrawEnemyHUDBorder
ld a, [wTempEnemyMonSpecies]
ld [wCurSpecies], a
ld [wCurPartySpecies], a
call GetBaseData
ld de, wEnemyMonNickname
hlcoord 1, 0
call Battle_DummyFunction
call PlaceString
ld h, b
ld l, c
dec hl
ld hl, wEnemyMonDVs
ld de, wTempMonDVs
ld a, [wEnemySubStatus5]
bit SUBSTATUS_TRANSFORMED, a
jr z, .ok
ld hl, wEnemyBackupDVs
.ok
ld a, [hli]
ld [de], a
inc de
ld a, [hl]
ld [de], a
ld a, TEMPMON
ld [wMonType], a
callfar GetGender
ld a, " "
jr c, .got_gender
ld a, "♂"
jr nz, .got_gender
ld a, "♀"
.got_gender
hlcoord 9, 1
ld [hl], a
hlcoord 6, 1
push af
push hl
ld de, wEnemyMonStatus
predef PlaceNonFaintStatus
pop hl
pop bc
jr nz, .skip_level
ld a, b
cp " "
jr nz, .print_level
dec hl
.print_level
ld a, [wEnemyMonLevel]
ld [wTempMonLevel], a
call PrintLevel
.skip_level
ld hl, wEnemyMonHP
ld a, [hli]
ldh [hMultiplicand + 1], a
ld a, [hld]
ldh [hMultiplicand + 2], a
or [hl]
jr nz, .not_fainted
ld c, a
ld e, a
ld d, HP_BAR_LENGTH
jp .draw_bar
.not_fainted
xor a
ldh [hMultiplicand + 0], a
ld a, HP_BAR_LENGTH_PX
ldh [hMultiplier], a
call Multiply
ld hl, wEnemyMonMaxHP
ld a, [hli]
ld b, a
ld a, [hl]
ldh [hMultiplier], a
ld a, b
and a
jr z, .less_than_256_max
ldh a, [hMultiplier]
srl b
rr a
srl b
rr a
ldh [hDivisor], a
ldh a, [hProduct + 2]
ld b, a
srl b
ldh a, [hProduct + 3]
rr a
srl b
rr a
ldh [hProduct + 3], a
ld a, b
ldh [hProduct + 2], a
.less_than_256_max
ldh a, [hProduct + 2]
ldh [hDividend + 0], a
ldh a, [hProduct + 3]
ldh [hDividend + 1], a
ld a, 2
ld b, a
call Divide
ldh a, [hQuotient + 3]
ld e, a
ld a, HP_BAR_LENGTH
ld d, a
ld c, a
.draw_bar
xor a
ld [wWhichHPBar], a
hlcoord 2, 2
ld b, 0
call DrawBattleHPBar
ret
UpdateEnemyHPPal:
ld hl, wEnemyHPPal
call UpdateHPPal
ret
UpdateHPPal:
ld b, [hl]
call SetHPPal
ld a, [hl]
cp b
ret z
jp FinishBattleAnim
Battle_DummyFunction:
; called before placing either battler's nickname in the HUD
ret
BattleMenu:
xor a
ldh [hBGMapMode], a
call LoadTempTilemapToTilemap
ld a, [wBattleType]
cp BATTLETYPE_DEBUG
jr z, .ok
cp BATTLETYPE_TUTORIAL
jr z, .ok
call EmptyBattleTextbox
call UpdateBattleHuds
call EmptyBattleTextbox
call LoadTilemapToTempTilemap
.ok
.loop
ld a, [wBattleType]
cp BATTLETYPE_CONTEST
jr nz, .not_contest
farcall ContestBattleMenu
jr .next
.not_contest
; Auto input: choose "ITEM"
ld a, [wInputType]
or a
jr z, .skip_dude_pack_select
farcall _DudeAutoInput_DownA
.skip_dude_pack_select
call LoadBattleMenu2
ret c
.next
ld a, $1
ldh [hBGMapMode], a
ld a, [wBattleMenuCursorPosition]
cp $1
jp z, BattleMenu_Fight
cp $3
jp z, BattleMenu_Pack
cp $2
jp z, BattleMenu_PKMN
cp $4
jp z, BattleMenu_Run
jr .loop
BattleMenu_Fight:
xor a
ld [wNumFleeAttempts], a
call SafeLoadTempTilemapToTilemap
and a
ret
LoadBattleMenu2:
call IsMobileBattle
jr z, .mobile
farcall LoadBattleMenu
and a
ret
.mobile
farcall Mobile_LoadBattleMenu
ld a, [wcd2b]
and a
ret z
ld hl, wcd2a
bit 4, [hl]
jr nz, .error
ld hl, BattleText_LinkErrorBattleCanceled
call StdBattleTextbox
ld c, 60
call DelayFrames
.error
scf
ret
BattleMenu_Pack:
ld a, [wLinkMode]
and a
jp nz, .ItemsCantBeUsed
ld a, [wInBattleTowerBattle]
and a
jp nz, .ItemsCantBeUsed
call LoadStandardMenuHeader
ld a, [wBattleType]
cp BATTLETYPE_TUTORIAL
jr z, .tutorial
cp BATTLETYPE_CONTEST
jr z, .contest
farcall BattlePack
ld a, [wBattlePlayerAction]
and a ; BATTLEPLAYERACTION_USEMOVE?
jr z, .didnt_use_item
jr .got_item
.tutorial
farcall TutorialPack
ld a, POKE_BALL
ld [wCurItem], a
call DoItemEffect
jr .got_item
.contest
ld a, PARK_BALL
ld [wCurItem], a
call DoItemEffect
.got_item
call .UseItem
ret
.didnt_use_item
call ClearPalettes
call DelayFrame
call _LoadBattleFontsHPBar
call GetBattleMonBackpic
call GetEnemyMonFrontpic
call ExitMenu
call WaitBGMap
call FinishBattleAnim
call LoadTilemapToTempTilemap
jp BattleMenu
.ItemsCantBeUsed:
ld hl, BattleText_ItemsCantBeUsedHere
call StdBattleTextbox
jp BattleMenu
.UseItem:
ld a, [wWildMon]
and a
jr nz, .run
callfar CheckItemPocket
ld a, [wItemAttributeValue]
cp BALL
jr z, .ball
call ClearBGPalettes
.ball
xor a
ldh [hBGMapMode], a
call _LoadBattleFontsHPBar
call ClearSprites
ld a, [wBattleType]
cp BATTLETYPE_TUTORIAL
jr z, .tutorial2
call GetBattleMonBackpic
.tutorial2
call GetEnemyMonFrontpic
ld a, $1
ld [wMenuCursorY], a
call ExitMenu
call UpdateBattleHUDs
call WaitBGMap
call LoadTilemapToTempTilemap
call ClearWindowData
call FinishBattleAnim
and a
ret
.run
xor a
ld [wWildMon], a
ld a, [wBattleResult]
and BATTLERESULT_BITMASK
ld [wBattleResult], a ; WIN
call ClearWindowData
call SetPalettes
scf
ret
BattleMenu_PKMN:
call LoadStandardMenuHeader
BattleMenuPKMN_ReturnFromStats:
call ExitMenu
call LoadStandardMenuHeader
call ClearBGPalettes
BattleMenuPKMN_Loop:
call SetUpBattlePartyMenu_Loop
xor a
ld [wPartyMenuActionText], a
call JumpToPartyMenuAndPrintText
call SelectBattleMon
jr c, .Cancel
.loop
farcall FreezeMonIcons
call .GetMenu
jr c, .PressedB
call PlaceHollowCursor
ld a, [wMenuCursorY]
cp $1 ; SWITCH
jp z, TryPlayerSwitch
cp $2 ; STATS
jr z, .Stats
cp $3 ; CANCEL
jr z, .Cancel
jr .loop
.PressedB:
call CheckMobileBattleError
jr c, .Cancel
jr BattleMenuPKMN_Loop
.Stats:
call Battle_StatsScreen
call CheckMobileBattleError
jr c, .Cancel
jp BattleMenuPKMN_ReturnFromStats
.Cancel:
call ClearSprites
call ClearPalettes
call DelayFrame
call _LoadHPBar
call CloseWindow
call LoadTilemapToTempTilemap
call GetMemSGBLayout
call SetPalettes
jp BattleMenu
.GetMenu:
call IsMobileBattle
jr z, .mobile
farcall BattleMonMenu
ret
.mobile
farcall MobileBattleMonMenu
ret
Battle_StatsScreen:
call DisableLCD
ld hl, vTiles2 tile $31
ld de, vTiles0
ld bc, $11 tiles
call CopyBytes
ld hl, vTiles2
ld de, vTiles0 tile $11
ld bc, $31 tiles
call CopyBytes
call EnableLCD
call ClearSprites
call LowVolume
xor a ; PARTYMON
ld [wMonType], a
farcall BattleStatsScreenInit
call MaxVolume
call DisableLCD
ld hl, vTiles0
ld de, vTiles2 tile $31
ld bc, $11 tiles
call CopyBytes
ld hl, vTiles0 tile $11
ld de, vTiles2
ld bc, $31 tiles
call CopyBytes
call EnableLCD
ret
TryPlayerSwitch:
ld a, [wCurBattleMon]
ld d, a
ld a, [wCurPartyMon]
cp d
jr nz, .check_trapped
ld hl, BattleText_MonIsAlreadyOut
call StdBattleTextbox
jp BattleMenuPKMN_Loop
.check_trapped
ld a, [wPlayerWrapCount]
and a
jr nz, .trapped
ld a, [wEnemySubStatus5]
bit SUBSTATUS_CANT_RUN, a
jr z, .try_switch
.trapped
ld hl, BattleText_MonCantBeRecalled
call StdBattleTextbox
jp BattleMenuPKMN_Loop
.try_switch
call CheckIfCurPartyMonIsFitToFight
jp z, BattleMenuPKMN_Loop
ld a, [wCurBattleMon]
ld [wLastPlayerMon], a
ld a, BATTLEPLAYERACTION_SWITCH
ld [wBattlePlayerAction], a
call ClearPalettes
call DelayFrame
call ClearSprites
call _LoadHPBar
call CloseWindow
call GetMemSGBLayout
call SetPalettes
ld a, [wCurPartyMon]
ld [wCurBattleMon], a
PlayerSwitch:
ld a, 1
ld [wPlayerIsSwitching], a
ld a, [wLinkMode]
and a
jr z, .not_linked
call LoadStandardMenuHeader
call LinkBattleSendReceiveAction
call CloseWindow
.not_linked
call ParseEnemyAction
ld a, [wLinkMode]
and a
jr nz, .linked
.switch
call BattleMonEntrance
and a
ret
.linked
ld a, [wBattleAction]
cp BATTLEACTION_STRUGGLE
jp z, .switch
cp BATTLEACTION_SKIPTURN
jp z, .switch
cp BATTLEACTION_SWITCH1
jp c, .switch
cp BATTLEACTION_FORFEIT
jr nz, .dont_run
call WildFled_EnemyFled_LinkBattleCanceled
ret
.dont_run
ldh a, [hSerialConnectionStatus]
cp USING_EXTERNAL_CLOCK
jr z, .player_1
call BattleMonEntrance
call EnemyMonEntrance
and a
ret
.player_1
call EnemyMonEntrance
call BattleMonEntrance
and a
ret
EnemyMonEntrance:
callfar AI_Switch
call SetEnemyTurn
jp SpikesDamage
BattleMonEntrance:
call WithdrawMonText
ld c, 50
call DelayFrames
ld hl, wPlayerSubStatus4
res SUBSTATUS_RAGE, [hl]
call SetEnemyTurn
call PursuitSwitch
jr c, .ok
call RecallPlayerMon
.ok
hlcoord 9, 7
lb bc, 5, 11
call ClearBox
ld a, [wCurBattleMon]
ld [wCurPartyMon], a
call AddBattleParticipant
call InitBattleMon
call ResetPlayerStatLevels
call SendOutMonText
call NewBattleMonStatus
call BreakAttraction
call SendOutPlayerMon
call EmptyBattleTextbox
call LoadTilemapToTempTilemap
call SetPlayerTurn
call SpikesDamage
ld a, $2
ld [wMenuCursorY], a
ret
PassedBattleMonEntrance:
ld c, 50
call DelayFrames
hlcoord 9, 7
lb bc, 5, 11
call ClearBox
ld a, [wCurPartyMon]
ld [wCurBattleMon], a
call AddBattleParticipant
call InitBattleMon
xor a ; FALSE
ld [wApplyStatLevelMultipliersToEnemy], a
call ApplyStatLevelMultiplierOnAllStats
call SendOutPlayerMon
call EmptyBattleTextbox
call LoadTilemapToTempTilemap
call SetPlayerTurn
jp SpikesDamage
BattleMenu_Run:
call SafeLoadTempTilemapToTilemap
ld a, $3
ld [wMenuCursorY], a
ld hl, wBattleMonSpeed
ld de, wEnemyMonSpeed
call TryToRunAwayFromBattle
ld a, FALSE
ld [wFailedToFlee], a
ret c
ld a, [wBattlePlayerAction]
and a ; BATTLEPLAYERACTION_USEMOVE?
ret nz
jp BattleMenu
CheckAmuletCoin:
ld a, [wBattleMonItem]
ld b, a
callfar GetItemHeldEffect
ld a, b
cp HELD_AMULET_COIN
ret nz
ld a, 1
ld [wAmuletCoin], a
ret
MoveSelectionScreen:
call IsMobileBattle
jr nz, .not_mobile
farcall Mobile_MoveSelectionScreen
ret
.not_mobile
ld hl, wEnemyMonMoves
ld a, [wMoveSelectionMenuType]
dec a
jr z, .got_menu_type
dec a
jr z, .ether_elixer_menu
call CheckPlayerHasUsableMoves
ret z ; use Struggle
ld hl, wBattleMonMoves
jr .got_menu_type
.ether_elixer_menu
ld a, MON_MOVES
call GetPartyParamLocation
.got_menu_type
ld de, wListMoves_MoveIndicesBuffer
ld bc, NUM_MOVES
call CopyBytes
xor a
ldh [hBGMapMode], a
hlcoord 4, 17 - NUM_MOVES - 1
ld b, 4
ld c, 14
ld a, [wMoveSelectionMenuType]
cp $2
jr nz, .got_dims
hlcoord 4, 17 - NUM_MOVES - 1 - 4
ld b, 4
ld c, 14
.got_dims
call Textbox
hlcoord 6, 17 - NUM_MOVES
ld a, [wMoveSelectionMenuType]
cp $2
jr nz, .got_start_coord
hlcoord 6, 17 - NUM_MOVES - 4
.got_start_coord
ld a, SCREEN_WIDTH
ld [wListMovesLineSpacing], a
predef ListMoves
ld b, 5
ld a, [wMoveSelectionMenuType]
cp $2
ld a, 17 - NUM_MOVES
jr nz, .got_default_coord
ld b, 5
ld a, 17 - NUM_MOVES - 4
.got_default_coord
ld [w2DMenuCursorInitY], a
ld a, b
ld [w2DMenuCursorInitX], a
ld a, [wMoveSelectionMenuType]
cp $1
jr z, .skip_inc
ld a, [wCurMoveNum]
inc a
.skip_inc
ld [wMenuCursorY], a
ld a, 1
ld [wMenuCursorX], a
ld a, [wNumMoves]
inc a
ld [w2DMenuNumRows], a
ld a, 1
ld [w2DMenuNumCols], a
ld c, STATICMENU_ENABLE_LEFT_RIGHT | STATICMENU_ENABLE_START | STATICMENU_WRAP
ld a, [wMoveSelectionMenuType]
dec a
ld b, D_DOWN | D_UP | A_BUTTON
jr z, .okay
dec a
ld b, D_DOWN | D_UP | A_BUTTON | B_BUTTON
jr z, .okay
ld a, [wLinkMode]
and a
jr nz, .okay
ld b, D_DOWN | D_UP | A_BUTTON | B_BUTTON | SELECT
.okay
ld a, b
ld [wMenuJoypadFilter], a
ld a, c
ld [w2DMenuFlags1], a
xor a
ld [w2DMenuFlags2], a
ld a, $10
ld [w2DMenuCursorOffsets], a
.menu_loop
ld a, [wMoveSelectionMenuType]
and a
jr z, .battle_player_moves
dec a
jr nz, .interpret_joypad
hlcoord 11, 14
ld de, .empty_string
call PlaceString
jr .interpret_joypad
.battle_player_moves
call MoveInfoBox
ld a, [wSwappingMove]
and a
jr z, .interpret_joypad
hlcoord 5, 13
ld bc, SCREEN_WIDTH
dec a
call AddNTimes
ld [hl], "â–·"
.interpret_joypad
ld a, $1
ldh [hBGMapMode], a
call ScrollingMenuJoypad
bit D_UP_F, a
jp nz, .pressed_up
bit D_DOWN_F, a
jp nz, .pressed_down
bit SELECT_F, a
jp nz, .pressed_select
bit B_BUTTON_F, a
; A button
push af
xor a
ld [wSwappingMove], a
ld a, [wMenuCursorY]
dec a
ld [wMenuCursorY], a
ld b, a
ld a, [wMoveSelectionMenuType]
dec a
jr nz, .not_enemy_moves_process_b
pop af
ret
.not_enemy_moves_process_b
dec a
ld a, b
ld [wCurMoveNum], a
jr nz, .use_move
pop af
ret
.use_move
pop af
ret nz
ld hl, wBattleMonPP
ld a, [wMenuCursorY]
ld c, a
ld b, 0
add hl, bc
ld a, [hl]
and PP_MASK
jr z, .no_pp_left
ld a, [wPlayerDisableCount]
swap a
and $f
dec a
cp c
jr z, .move_disabled
ld a, [wUnusedPlayerLockedMove]
and a
jr nz, .skip2
ld a, [wMenuCursorY]
ld hl, wBattleMonMoves
ld c, a
ld b, 0
add hl, bc
ld a, [hl]
.skip2
ld [wCurPlayerMove], a
xor a
ret
.move_disabled
ld hl, BattleText_TheMoveIsDisabled
jr .place_textbox_start_over
.no_pp_left
ld hl, BattleText_TheresNoPPLeftForThisMove
.place_textbox_start_over
call StdBattleTextbox
call SafeLoadTempTilemapToTilemap
jp MoveSelectionScreen
.empty_string
db "@"
.pressed_up
ld a, [wMenuCursorY]
and a
jp nz, .menu_loop
ld a, [wNumMoves]
inc a
ld [wMenuCursorY], a
jp .menu_loop
.pressed_down
ld a, [wMenuCursorY]
ld b, a
ld a, [wNumMoves]
inc a
inc a
cp b
jp nz, .menu_loop
ld a, $1
ld [wMenuCursorY], a
jp .menu_loop
.pressed_select
ld a, [wSwappingMove]
and a
jr z, .start_swap
ld hl, wBattleMonMoves
call .swap_bytes
ld hl, wBattleMonPP
call .swap_bytes
ld hl, wPlayerDisableCount
ld a, [hl]
swap a
and $f
ld b, a
ld a, [wMenuCursorY]
cp b
jr nz, .not_swapping_disabled_move
ld a, [hl]
and $f
ld b, a
ld a, [wSwappingMove]
swap a
add b
ld [hl], a
jr .swap_moves_in_party_struct
.not_swapping_disabled_move
ld a, [wSwappingMove]
cp b
jr nz, .swap_moves_in_party_struct
ld a, [hl]
and $f
ld b, a
ld a, [wMenuCursorY]
swap a
add b
ld [hl], a
.swap_moves_in_party_struct
; Fixes the COOLTRAINER glitch
ld a, [wPlayerSubStatus5]
bit SUBSTATUS_TRANSFORMED, a
jr nz, .transformed
ld hl, wPartyMon1Moves
ld a, [wCurBattleMon]
call GetPartyLocation
push hl
call .swap_bytes
pop hl
ld bc, MON_PP - MON_MOVES
add hl, bc
call .swap_bytes
.transformed
xor a
ld [wSwappingMove], a
jp MoveSelectionScreen
.swap_bytes
push hl
ld a, [wSwappingMove]
dec a
ld c, a
ld b, 0
add hl, bc
ld d, h
ld e, l
pop hl
ld a, [wMenuCursorY]
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
.start_swap
ld a, [wMenuCursorY]
ld [wSwappingMove], a
jp MoveSelectionScreen
MoveInfoBox:
xor a
ldh [hBGMapMode], a
hlcoord 0, 8
ld b, 3
ld c, 9
call Textbox
call MobileTextBorder
ld a, [wPlayerDisableCount]
and a
jr z, .not_disabled
swap a
and $f
ld b, a
ld a, [wMenuCursorY]
cp b
jr nz, .not_disabled
hlcoord 1, 10
ld de, .Disabled
call PlaceString
jr .done
.not_disabled
ld hl, wMenuCursorY
dec [hl]
call SetPlayerTurn
ld hl, wBattleMonMoves
ld a, [wMenuCursorY]
ld c, a
ld b, 0
add hl, bc
ld a, [hl]
ld [wCurPlayerMove], a
ld a, [wCurBattleMon]
ld [wCurPartyMon], a
ld a, WILDMON
ld [wMonType], a
callfar GetMaxPPOfMove
ld hl, wMenuCursorY
ld c, [hl]
inc [hl]
ld b, 0
ld hl, wBattleMonPP
add hl, bc
ld a, [hl]
and PP_MASK
ld [wStringBuffer1], a
call .PrintPP
hlcoord 1, 9
ld de, .Type
call PlaceString
hlcoord 7, 11
ld [hl], "/"
callfar UpdateMoveData
ld a, [wPlayerMoveStruct + MOVE_ANIM]
ld b, a
hlcoord 2, 10
predef PrintMoveType
.done
ret
.Disabled:
db "Disabled!@"
.Type:
db "TYPE/@"
.PrintPP:
hlcoord 5, 11
ld a, [wLinkMode] ; What's the point of this check?
cp LINK_MOBILE
jr c, .ok
hlcoord 5, 11
.ok
push hl
ld de, wStringBuffer1
lb bc, 1, 2
call PrintNum
pop hl
inc hl
inc hl
ld [hl], "/"
inc hl
ld de, wNamedObjectIndex
lb bc, 1, 2
call PrintNum
ret
CheckPlayerHasUsableMoves:
ld a, STRUGGLE
ld [wCurPlayerMove], a
ld a, [wPlayerDisableCount]
and a
ld hl, wBattleMonPP
jr nz, .disabled
ld a, [hli]
or [hl]
inc hl
or [hl]
inc hl
or [hl]
and PP_MASK
ret nz
jr .force_struggle
.disabled
swap a
and $f
ld b, a
ld d, NUM_MOVES + 1
xor a
.loop
dec d
jr z, .done
ld c, [hl]
inc hl
dec b
jr z, .loop
or c
jr .loop
.done
; BUG: A Disabled but PP Up–enhanced move may not trigger Struggle (see docs/bugs_and_glitches.md)
and a
ret nz
.force_struggle
ld hl, BattleText_MonHasNoMovesLeft
call StdBattleTextbox
ld c, 60
call DelayFrames
xor a
ret
ParseEnemyAction:
ld a, [wEnemyIsSwitching]
and a
ret nz
ld a, [wLinkMode]
and a
jr z, .not_linked
call EmptyBattleTextbox
call LoadTilemapToTempTilemap
ld a, [wBattlePlayerAction]
and a ; BATTLEPLAYERACTION_USEMOVE?
call z, LinkBattleSendReceiveAction
call SafeLoadTempTilemapToTilemap
ld a, [wBattleAction]
cp BATTLEACTION_STRUGGLE
jp z, .struggle
cp BATTLEACTION_SKIPTURN
jp z, .skip_turn
cp BATTLEACTION_SWITCH1
jp nc, ResetVarsForSubstatusRage
ld [wCurEnemyMoveNum], a
ld c, a
ld a, [wEnemySubStatus1]
bit SUBSTATUS_ROLLOUT, a
jp nz, .skip_load
ld a, [wEnemySubStatus3]
and 1 << SUBSTATUS_CHARGED | 1 << SUBSTATUS_RAMPAGE | 1 << SUBSTATUS_BIDE
jp nz, .skip_load
ld hl, wEnemySubStatus5
bit SUBSTATUS_ENCORED, [hl]
ld a, [wLastEnemyMove]
jp nz, .finish
ld hl, wEnemyMonMoves
ld b, 0
add hl, bc
ld a, [hl]
jp .finish
.not_linked
ld hl, wEnemySubStatus5
bit SUBSTATUS_ENCORED, [hl]
jr z, .skip_encore
ld a, [wLastEnemyMove]
jp .finish
.skip_encore
call CheckEnemyLockedIn
jp nz, ResetVarsForSubstatusRage
jr .continue
.skip_turn
ld a, $ff
jr .finish
.continue
ld hl, wEnemyMonMoves
ld de, wEnemyMonPP
ld b, NUM_MOVES
.loop
ld a, [hl]
and a
jp z, .struggle
ld a, [wEnemyDisabledMove]
cp [hl]
jr z, .disabled
ld a, [de]
and PP_MASK
jr nz, .enough_pp
.disabled
inc hl
inc de
dec b
jr nz, .loop
jr .struggle
.enough_pp
ld a, [wBattleMode]
dec a
jr nz, .skip_load
; wild
.loop2
ld hl, wEnemyMonMoves
call BattleRandom
maskbits NUM_MOVES
ld c, a
ld b, 0
add hl, bc
ld a, [wEnemyDisableCount]
swap a
and $f
dec a
cp c
jr z, .loop2
ld a, [hl]
and a
jr z, .loop2
ld hl, wEnemyMonPP
add hl, bc
ld b, a
ld a, [hl]
and PP_MASK
jr z, .loop2
ld a, c
ld [wCurEnemyMoveNum], a
ld a, b
.finish
ld [wCurEnemyMove], a
.skip_load
call SetEnemyTurn
callfar UpdateMoveData
call CheckEnemyLockedIn
jr nz, .raging
xor a
ld [wEnemyCharging], a
.raging
ld a, [wEnemyMoveStruct + MOVE_EFFECT]
cp EFFECT_FURY_CUTTER
jr z, .fury_cutter
xor a
ld [wEnemyFuryCutterCount], a
.fury_cutter
ld a, [wEnemyMoveStruct + MOVE_EFFECT]
cp EFFECT_RAGE
jr z, .no_rage
ld hl, wEnemySubStatus4
res SUBSTATUS_RAGE, [hl]
xor a
ld [wEnemyRageCounter], a
.no_rage
ld a, [wEnemyMoveStruct + MOVE_EFFECT]
cp EFFECT_PROTECT
ret z
cp EFFECT_ENDURE
ret z
xor a
ld [wEnemyProtectCount], a
ret
.struggle
ld a, STRUGGLE
jr .finish
ResetVarsForSubstatusRage:
xor a
ld [wEnemyFuryCutterCount], a
ld [wEnemyProtectCount], a
ld [wEnemyRageCounter], a
ld hl, wEnemySubStatus4
res SUBSTATUS_RAGE, [hl]
ret
CheckEnemyLockedIn:
ld a, [wEnemySubStatus4]
and 1 << SUBSTATUS_RECHARGE
ret nz
ld hl, wEnemySubStatus3
ld a, [hl]
and 1 << SUBSTATUS_CHARGED | 1 << SUBSTATUS_RAMPAGE | 1 << SUBSTATUS_BIDE
ret nz
ld hl, wEnemySubStatus1
bit SUBSTATUS_ROLLOUT, [hl]
ret
LinkBattleSendReceiveAction:
farcall _LinkBattleSendReceiveAction
ret
LoadEnemyMon:
; Initialize enemy monster parameters
; To do this we pull the species from wTempEnemyMonSpecies
; Notes:
; BattleRandom is used to ensure sync between Game Boys
; Clear the whole enemy mon struct (wEnemyMon)
xor a
ld hl, wEnemyMonSpecies
ld bc, wEnemyMonEnd - wEnemyMon
call ByteFill
; We don't need to be here if we're in a link battle
ld a, [wLinkMode]
and a
jp nz, InitEnemyMon
; and also not in a BattleTower-Battle
ld a, [wInBattleTowerBattle]
bit 0, a
jp nz, InitEnemyMon
; Make sure everything knows what species we're working with
ld a, [wTempEnemyMonSpecies]
ld [wEnemyMonSpecies], a
ld [wCurSpecies], a
ld [wCurPartySpecies], a
; Grab the BaseData for this species
call GetBaseData
; Let's get the item:
; Is the item predetermined?
ld a, [wBattleMode]
dec a
jr z, .WildItem
; If we're in a trainer battle, the item is in the party struct
ld a, [wCurPartyMon]
ld hl, wOTPartyMon1Item
call GetPartyLocation ; bc = PartyMon[wCurPartyMon] - wPartyMons
ld a, [hl]
jr .UpdateItem
.WildItem:
; In a wild battle, we pull from the item slots in BaseData
; Force Item1
; Used for Ho-Oh, Lugia and Snorlax encounters
ld a, [wBattleType]
cp BATTLETYPE_FORCEITEM
ld a, [wBaseItem1]
jr z, .UpdateItem
; Failing that, it's all up to chance
; Effective chances:
; 75% None
; 23% Item1
; 2% Item2
; 25% chance of getting an item
call BattleRandom
cp 75 percent + 1
ld a, NO_ITEM
jr c, .UpdateItem
; From there, an 8% chance for Item2
call BattleRandom
cp 8 percent ; 8% of 25% = 2% Item2
ld a, [wBaseItem1]
jr nc, .UpdateItem
ld a, [wBaseItem2]
.UpdateItem:
ld [wEnemyMonItem], a
; Initialize DVs
; If we're in a trainer battle, DVs are predetermined
ld a, [wBattleMode]
and a
jr z, .InitDVs
ld a, [wEnemySubStatus5]
bit SUBSTATUS_TRANSFORMED, a
jr z, .InitDVs
; Unknown
ld hl, wEnemyBackupDVs
ld de, wEnemyMonDVs
ld a, [hli]
ld [de], a
inc de
ld a, [hl]
ld [de], a
jp .Happiness
.InitDVs:
; Trainer DVs
; All trainers have preset DVs, determined by class
; See GetTrainerDVs for more on that
farcall GetTrainerDVs
; These are the DVs we'll use if we're actually in a trainer battle
ld a, [wBattleMode]
dec a
jr nz, .UpdateDVs
; Wild DVs
; Here's where the fun starts
; Roaming monsters (Entei, Raikou) work differently
; They have their own structs, which are shorter than normal
ld a, [wBattleType]
cp BATTLETYPE_ROAMING
jr nz, .NotRoaming
; Grab HP
call GetRoamMonHP
ld a, [hl]
; Check if the HP has been initialized
and a
; We'll do something with the result in a minute
push af
; Grab DVs
call GetRoamMonDVs
inc hl
ld a, [hld]
ld c, a
ld b, [hl]
; Get back the result of our check
pop af
; If the RoamMon struct has already been initialized, we're done
jr nz, .UpdateDVs
; If it hasn't, we need to initialize the DVs
; (HP is initialized at the end of the battle)
call GetRoamMonDVs
inc hl
call BattleRandom
ld [hld], a
ld c, a
call BattleRandom
ld [hl], a
ld b, a
; We're done with DVs
jr .UpdateDVs
.NotRoaming:
; Register a contains wBattleType
; Forced shiny battle type
; Used by Red Gyarados at Lake of Rage
cp BATTLETYPE_SHINY
jr nz, .GenerateDVs
ld b, ATKDEFDV_SHINY ; $ea
ld c, SPDSPCDV_SHINY ; $aa
jr .UpdateDVs
.GenerateDVs:
; Generate new random DVs
call BattleRandom
ld b, a
call BattleRandom
ld c, a
.UpdateDVs:
; Input DVs in register bc
ld hl, wEnemyMonDVs
ld a, b
ld [hli], a
ld [hl], c
; We've still got more to do if we're dealing with a wild monster
ld a, [wBattleMode]
dec a
jr nz, .Happiness
; Species-specfic:
; Unown
ld a, [wTempEnemyMonSpecies]
cp UNOWN
jr nz, .Magikarp
; Get letter based on DVs
ld hl, wEnemyMonDVs
predef GetUnownLetter
; Can't use any letters that haven't been unlocked
; If combined with forced shiny battletype, causes an infinite loop
call CheckUnownLetter
jr c, .GenerateDVs ; try again
.Magikarp:
; These filters are untranslated.
; They expect at wMagikarpLength a 2-byte value in mm,
; but the value is in feet and inches (one byte each).
; The first filter is supposed to make very large Magikarp even rarer,
; by targeting those 1600 mm (= 5'3") or larger.
; After the conversion to feet, it is unable to target any,
; since the largest possible Magikarp is 5'3", and $0503 = 1283 mm.
ld a, [wTempEnemyMonSpecies]
cp MAGIKARP
jr nz, .Happiness
; Get Magikarp's length
; BUG: Magikarp length limits have a unit conversion error (see docs/bugs_and_glitches.md)
ld de, wEnemyMonDVs
ld bc, wPlayerID
callfar CalcMagikarpLength
; No reason to keep going if length > 1536 mm (i.e. if HIGH(length) > 6 feet)
ld a, [wMagikarpLength]
cp HIGH(1536)
jr nz, .CheckMagikarpArea
; 5% chance of skipping both size checks
call Random
cp 5 percent
jr c, .CheckMagikarpArea
; Try again if length >= 1616 mm (i.e. if LOW(length) >= 4 inches)
ld a, [wMagikarpLength + 1]
cp LOW(1616)
jr nc, .GenerateDVs
; 20% chance of skipping this check
call Random
cp 20 percent - 1
jr c, .CheckMagikarpArea
; Try again if length >= 1600 mm (i.e. if LOW(length) >= 3 inches)
ld a, [wMagikarpLength + 1]
cp LOW(1600)
jr nc, .GenerateDVs
.CheckMagikarpArea:
; BUG: Magikarp in Lake of Rage are shorter, not longer (see docs/bugs_and_glitches.md)
ld a, [wMapGroup]
cp GROUP_LAKE_OF_RAGE
jr z, .Happiness
ld a, [wMapNumber]
cp MAP_LAKE_OF_RAGE
jr z, .Happiness
; 40% chance of not flooring
call Random
cp 39 percent + 1
jr c, .Happiness
; Try again if length < 1024 mm (i.e. if HIGH(length) < 3 feet)
ld a, [wMagikarpLength]
cp HIGH(1024)
jr c, .GenerateDVs ; try again
; Finally done with DVs
.Happiness:
; Set happiness
ld a, BASE_HAPPINESS
ld [wEnemyMonHappiness], a
; Set level
ld a, [wCurPartyLevel]
ld [wEnemyMonLevel], a
; Fill stats
ld de, wEnemyMonMaxHP
ld b, FALSE
ld hl, wEnemyMonDVs - (MON_DVS - MON_STAT_EXP + 1)
predef CalcMonStats
; If we're in a trainer battle,
; get the rest of the parameters from the party struct
ld a, [wBattleMode]
cp TRAINER_BATTLE
jr z, .OpponentParty
; If we're in a wild battle, check wild-specific stuff
and a
jr z, .TreeMon
ld a, [wEnemySubStatus5]
bit SUBSTATUS_TRANSFORMED, a
jp nz, .Moves
.TreeMon:
; If we're headbutting trees, some monsters enter battle asleep
call CheckSleepingTreeMon
ld a, TREEMON_SLEEP_TURNS
jr c, .UpdateStatus
; Otherwise, no status
xor a
.UpdateStatus:
ld hl, wEnemyMonStatus
ld [hli], a
; Unused byte
xor a
ld [hli], a
; Full HP..
ld a, [wEnemyMonMaxHP]
ld [hli], a
ld a, [wEnemyMonMaxHP + 1]
ld [hl], a
; ..unless it's a RoamMon
ld a, [wBattleType]
cp BATTLETYPE_ROAMING
jr nz, .Moves
; Grab HP
call GetRoamMonHP
ld a, [hl]
; Check if it's been initialized again
and a
jr z, .InitRoamHP
; Update from the struct if it has
ld a, [hl]
ld [wEnemyMonHP + 1], a
jr .Moves
.InitRoamHP:
; HP only uses the lo byte in the RoamMon struct since
; Raikou and Entei will have < 256 hp at level 40
ld a, [wEnemyMonHP + 1]
ld [hl], a
jr .Moves
.OpponentParty:
; Get HP from the party struct
ld hl, (wOTPartyMon1HP + 1)
ld a, [wCurPartyMon]
call GetPartyLocation
ld a, [hld]
ld [wEnemyMonHP + 1], a
ld a, [hld]
ld [wEnemyMonHP], a
; Make sure everything knows which monster the opponent is using
ld a, [wCurPartyMon]
ld [wCurOTMon], a
; Get status from the party struct
dec hl
ld a, [hl] ; OTPartyMonStatus
ld [wEnemyMonStatus], a
.Moves:
ld hl, wBaseType1
ld de, wEnemyMonType1
ld a, [hli]
ld [de], a
inc de
ld a, [hl]
ld [de], a
; Get moves
ld de, wEnemyMonMoves
; Are we in a trainer battle?
ld a, [wBattleMode]
cp TRAINER_BATTLE
jr nz, .WildMoves
; Then copy moves from the party struct
ld hl, wOTPartyMon1Moves
ld a, [wCurPartyMon]
call GetPartyLocation
ld bc, NUM_MOVES
call CopyBytes
jr .PP
.WildMoves:
; Clear wEnemyMonMoves
xor a
ld h, d
ld l, e
ld [hli], a
ld [hli], a
ld [hli], a
ld [hl], a
ld [wSkipMovesBeforeLevelUp], a
; Fill moves based on level
predef FillMoves
.PP:
; Trainer battle?
ld a, [wBattleMode]
cp TRAINER_BATTLE
jr z, .TrainerPP
; Fill wild PP
ld hl, wEnemyMonMoves
ld de, wEnemyMonPP
predef FillPP
jr .Finish
.TrainerPP:
; Copy PP from the party struct
ld hl, wOTPartyMon1PP
ld a, [wCurPartyMon]
call GetPartyLocation
ld de, wEnemyMonPP
ld bc, NUM_MOVES
call CopyBytes
.Finish:
; Copy the first five base stats (the enemy mon's base Sp. Atk
; is also used to calculate Sp. Def stat experience)
ld hl, wBaseStats
ld de, wEnemyMonBaseStats
ld b, NUM_STATS - 1
.loop
ld a, [hli]
ld [de], a
inc de
dec b
jr nz, .loop
ld a, [wBaseCatchRate]
ld [de], a
inc de
ld a, [wBaseExp]
ld [de], a
ld a, [wTempEnemyMonSpecies]
ld [wNamedObjectIndex], a
call GetPokemonName
; Did we catch it?
ld a, [wBattleMode]
and a
ret z
; Update enemy nickname
ld hl, wStringBuffer1
ld de, wEnemyMonNickname
ld bc, MON_NAME_LENGTH
call CopyBytes
; Saw this mon
ld a, [wTempEnemyMonSpecies]
dec a
ld c, a
ld b, SET_FLAG
ld hl, wPokedexSeen
predef SmallFarFlagAction
ld hl, wEnemyMonStats
ld de, wEnemyStats
ld bc, NUM_EXP_STATS * 2
call CopyBytes
ret
CheckSleepingTreeMon:
; Return carry if species is in the list
; for the current time of day
; Don't do anything if this isn't a tree encounter
ld a, [wBattleType]
cp BATTLETYPE_TREE
jr nz, .NotSleeping
; Get list for the time of day
ld hl, AsleepTreeMonsMorn
ld a, [wTimeOfDay]
cp DAY_F
jr c, .Check
ld hl, AsleepTreeMonsDay
jr z, .Check
ld hl, AsleepTreeMonsNite
.Check:
ld a, [wTempEnemyMonSpecies]
ld de, 1 ; length of species id
call IsInArray
; If it's a match, the opponent is asleep
ret c
.NotSleeping:
and a
ret
INCLUDE "data/wild/treemons_asleep.asm"
CheckUnownLetter:
; Return carry if the Unown letter hasn't been unlocked yet
ld a, [wUnlockedUnowns]
ld c, a
ld de, 0
.loop
; Don't check this set unless it's been unlocked
srl c
jr nc, .next
; Is our letter in the set?
ld hl, UnlockedUnownLetterSets
add hl, de
ld a, [hli]
ld h, [hl]
ld l, a
push de
ld a, [wUnownLetter]
ld de, 1
push bc
call IsInArray
pop bc
pop de
jr c, .match
.next
; Make sure we haven't gone past the end of the table
inc e
inc e
ld a, e
cp UnlockedUnownLetterSets.End - UnlockedUnownLetterSets
jr c, .loop
; Hasn't been unlocked, or the letter is invalid
scf
ret
.match
; Valid letter
and a
ret
INCLUDE "data/wild/unlocked_unowns.asm"
SwapBattlerLevels: ; unreferenced
push bc
ld a, [wBattleMonLevel]
ld b, a
ld a, [wEnemyMonLevel]
ld [wBattleMonLevel], a
ld a, b
ld [wEnemyMonLevel], a
pop bc
ret
BattleWinSlideInEnemyTrainerFrontpic:
xor a
ld [wTempEnemyMonSpecies], a
call FinishBattleAnim
ld a, [wOtherTrainerClass]
ld [wTrainerClass], a
ld de, vTiles2
callfar GetTrainerPic
hlcoord 19, 0
ld c, 0
.outer_loop
inc c
ld a, c
cp 7
ret z
xor a
ldh [hBGMapMode], a
ldh [hBGMapThird], a
ld d, $0
push bc
push hl
.inner_loop
call .CopyColumn
inc hl
ld a, 7
add d
ld d, a
dec c
jr nz, .inner_loop
ld a, $1
ldh [hBGMapMode], a
ld c, 4
call DelayFrames
pop hl
pop bc
dec hl
jr .outer_loop
.CopyColumn:
push hl
push de
push bc
ld e, 7
.loop
ld [hl], d
ld bc, SCREEN_WIDTH
add hl, bc
inc d
dec e
jr nz, .loop
pop bc
pop de
pop hl
ret
ApplyStatusEffectOnPlayerStats:
ld a, 1
jr ApplyStatusEffectOnStats
ApplyStatusEffectOnEnemyStats:
xor a
ApplyStatusEffectOnStats:
ldh [hBattleTurn], a
call ApplyPrzEffectOnSpeed
jp ApplyBrnEffectOnAttack
ApplyPrzEffectOnSpeed:
ldh a, [hBattleTurn]
and a
jr z, .enemy
ld a, [wBattleMonStatus]
and 1 << PAR
ret z
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, .player_ok
ld b, $1 ; min speed
.player_ok
ld [hl], b
ret
.enemy
ld a, [wEnemyMonStatus]
and 1 << PAR
ret z
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, .enemy_ok
ld b, $1 ; min speed
.enemy_ok
ld [hl], b
ret
ApplyBrnEffectOnAttack:
ldh a, [hBattleTurn]
and a
jr z, .enemy
ld a, [wBattleMonStatus]
and 1 << BRN
ret z
ld hl, wBattleMonAttack + 1
ld a, [hld]
ld b, a
ld a, [hl]
srl a
rr b
ld [hli], a
or b
jr nz, .player_ok
ld b, $1 ; min attack
.player_ok
ld [hl], b
ret
.enemy
ld a, [wEnemyMonStatus]
and 1 << BRN
ret z
ld hl, wEnemyMonAttack + 1
ld a, [hld]
ld b, a
ld a, [hl]
srl a
rr b
ld [hli], a
or b
jr nz, .enemy_ok
ld b, $1 ; min attack
.enemy_ok
ld [hl], b
ret
ApplyStatLevelMultiplierOnAllStats:
; Apply StatLevelMultipliers on all 5 Stats
ld c, 0
.stat_loop
call ApplyStatLevelMultiplier
inc c
ld a, c
cp NUM_BATTLE_STATS
jr nz, .stat_loop
ret
ApplyStatLevelMultiplier:
push bc
push bc
ld a, [wApplyStatLevelMultipliersToEnemy]
and a
ld a, c
ld hl, wBattleMonAttack
ld de, wPlayerStats
ld bc, wPlayerAtkLevel
jr z, .got_pointers
ld hl, wEnemyMonAttack
ld de, wEnemyStats
ld bc, wEnemyAtkLevel
.got_pointers
add c
ld c, a
jr nc, .okay
inc b
.okay
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, .okay2
inc d
.okay2
pop bc
push hl
ld hl, StatLevelMultipliers_Applied
dec b
sla b
ld c, b
ld b, 0
add hl, bc
xor a
ldh [hMultiplicand + 0], a
ld a, [de]
ldh [hMultiplicand + 1], a
inc de
ld a, [de]
ldh [hMultiplicand + 2], a
ld a, [hli]
ldh [hMultiplier], a
call Multiply
ld a, [hl]
ldh [hDivisor], a
ld b, 4
call Divide
pop hl
; Cap at 999.
ldh a, [hQuotient + 3]
sub LOW(MAX_STAT_VALUE)
ldh a, [hQuotient + 2]
sbc HIGH(MAX_STAT_VALUE)
jp c, .okay3
ld a, HIGH(MAX_STAT_VALUE)
ldh [hQuotient + 2], a
ld a, LOW(MAX_STAT_VALUE)
ldh [hQuotient + 3], a
.okay3
ldh a, [hQuotient + 2]
ld [hli], a
ld b, a
ldh a, [hQuotient + 3]
ld [hl], a
or b
jr nz, .okay4
inc [hl]
.okay4
pop bc
ret
INCLUDE "data/battle/stat_multipliers_2.asm"
BadgeStatBoosts:
; Raise the stats of the battle mon in wBattleMon
; depending on which badges have been obtained.
; Every other badge boosts a stat, starting from the first.
; GlacierBadge also boosts Special Defense, although the relevant code is buggy (see below).
; ZephyrBadge: Attack
; PlainBadge: Speed
; MineralBadge: Defense
; GlacierBadge: Special Attack and Special Defense
; The boosted stats are in order, except PlainBadge and MineralBadge's boosts are swapped.
ld a, [wLinkMode]
and a
ret nz
ld a, [wInBattleTowerBattle]
and a
ret nz
ld a, [wJohtoBadges]
; Swap badges 3 (PlainBadge) and 5 (MineralBadge).
ld d, a
and (1 << PLAINBADGE)
add a
add a
ld b, a
ld a, d
and (1 << MINERALBADGE)
rrca
rrca
ld c, a
ld a, d
and ((1 << ZEPHYRBADGE) | (1 << HIVEBADGE) | (1 << FOGBADGE) | (1 << STORMBADGE) | (1 << GLACIERBADGE) | (1 << RISINGBADGE))
or b
or c
ld b, a
ld hl, wBattleMonAttack
ld c, 4
.CheckBadge:
; BUG: Glacier Badge may not boost Special Defense depending on the value of Special Attack (see docs/bugs_and_glitches.md)
ld a, b
srl b
call c, BoostStat
inc hl
inc hl
; Check every other badge.
srl b
dec c
jr nz, .CheckBadge
srl a
call c, BoostStat
ret
BoostStat:
; Raise stat at hl by 1/8.
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
; Cap at 999.
ld a, [hld]
sub LOW(MAX_STAT_VALUE)
ld a, [hl]
sbc HIGH(MAX_STAT_VALUE)
ret c
ld a, HIGH(MAX_STAT_VALUE)
ld [hli], a
ld a, LOW(MAX_STAT_VALUE)
ld [hld], a
ret
_LoadBattleFontsHPBar:
callfar LoadBattleFontsHPBar
ret
_LoadHPBar:
callfar LoadHPBar
ret
LoadHPExpBarGFX: ; unreferenced
ld de, EnemyHPBarBorderGFX
ld hl, vTiles2 tile $6c
lb bc, BANK(EnemyHPBarBorderGFX), 4
call Get1bpp
ld de, HPExpBarBorderGFX
ld hl, vTiles2 tile $73
lb bc, BANK(HPExpBarBorderGFX), 6
call Get1bpp
ld de, ExpBarGFX
ld hl, vTiles2 tile $55
lb bc, BANK(ExpBarGFX), 8
jp Get2bpp
EmptyBattleTextbox:
ld hl, .empty
jp BattleTextbox
.empty:
text_end
_BattleRandom::
; If the normal RNG is used in a link battle it'll desync.
; To circumvent this a shared PRNG is used instead.
; But if we're in a non-link battle we're safe to use it
ld a, [wLinkMode]
and a
jp z, Random
; The PRNG operates in streams of 10 values.
; Which value are we trying to pull?
push hl
push bc
ld a, [wLinkBattleRNCount]
ld c, a
ld b, 0
ld hl, wLinkBattleRNs
add hl, bc
inc a
ld [wLinkBattleRNCount], a
; If we haven't hit the end yet, we're good
cp 10 - 1 ; Exclude last value. See the closing comment
ld a, [hl]
pop bc
pop hl
ret c
; If we have, we have to generate new pseudorandom data
; Instead of having multiple PRNGs, ten seeds are used
push hl
push bc
push af
; Reset count to 0
xor a
ld [wLinkBattleRNCount], a
ld hl, wLinkBattleRNs
ld b, 10 ; number of seeds
; Generate next number in the sequence for each seed
; a[n+1] = (a[n] * 5 + 1) % 256
.loop
; get last #
ld a, [hl]
; a * 5 + 1
ld c, a
add a
add a
add c
inc a
; update #
ld [hli], a
dec b
jr nz, .loop
; This has the side effect of pulling the last value first,
; then wrapping around. As a result, when we check to see if
; we've reached the end, we check the one before it.
pop af
pop bc
pop hl
ret
Call_PlayBattleAnim_OnlyIfVisible:
ld a, BATTLE_VARS_SUBSTATUS3
call GetBattleVar
and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND
ret nz
Call_PlayBattleAnim:
ld a, e
ld [wFXAnimID], a
ld a, d
ld [wFXAnimID + 1], a
call WaitBGMap
predef_jump PlayBattleAnim
FinishBattleAnim:
push af
push bc
push de
push hl
ld b, SCGB_BATTLE_COLORS
call GetSGBLayout
call SetPalettes
call DelayFrame
pop hl
pop de
pop bc
pop af
ret
GiveExperiencePoints:
; Give experience.
; Don't give experience if linked or in the Battle Tower.
ld a, [wLinkMode]
and a
ret nz
ld a, [wInBattleTowerBattle]
bit 0, a
ret nz
call .EvenlyDivideExpAmongParticipants
xor a
ld [wCurPartyMon], a
ld bc, wPartyMon1Species
.loop
ld hl, MON_HP
add hl, bc
ld a, [hli]
or [hl]
jp z, .next_mon ; fainted
push bc
ld hl, wBattleParticipantsNotFainted
ld a, [wCurPartyMon]
ld c, a
ld b, CHECK_FLAG
ld d, 0
predef SmallFarFlagAction
ld a, c
and a
pop bc
jp z, .next_mon
; give stat exp
ld hl, MON_STAT_EXP + 1
add hl, bc
ld d, h
ld e, l
ld hl, wEnemyMonBaseStats - 1
push bc
ld c, NUM_EXP_STATS
.stat_exp_loop
inc hl
ld a, [de]
add [hl]
ld [de], a
jr nc, .no_carry_stat_exp
dec de
ld a, [de]
inc a
jr z, .stat_exp_maxed_out
ld [de], a
inc de
.no_carry_stat_exp
push hl
push bc
ld a, MON_POKERUS
call GetPartyParamLocation
ld a, [hl]
and a
pop bc
pop hl
jr z, .stat_exp_awarded
ld a, [de]
add [hl]
ld [de], a
jr nc, .stat_exp_awarded
dec de
ld a, [de]
inc a
jr z, .stat_exp_maxed_out
ld [de], a
inc de
jr .stat_exp_awarded
.stat_exp_maxed_out
ld a, $ff
ld [de], a
inc de
ld [de], a
.stat_exp_awarded
inc de
inc de
dec c
jr nz, .stat_exp_loop
xor a
ldh [hMultiplicand + 0], a
ldh [hMultiplicand + 1], a
ld a, [wEnemyMonBaseExp]
ldh [hMultiplicand + 2], a
ld a, [wEnemyMonLevel]
ldh [hMultiplier], a
call Multiply
ld a, 7
ldh [hDivisor], a
ld b, 4
call Divide
; Boost Experience for traded Pokemon
pop bc
ld hl, MON_ID
add hl, bc
ld a, [wPlayerID]
cp [hl]
jr nz, .boosted
inc hl
ld a, [wPlayerID + 1]
cp [hl]
ld a, 0
jr z, .no_boost
.boosted
call BoostExp
ld a, 1
.no_boost
; Boost experience for a Trainer Battle
ld [wStringBuffer2 + 2], a
ld a, [wBattleMode]
dec a
call nz, BoostExp
; Boost experience for Lucky Egg
push bc
ld a, MON_ITEM
call GetPartyParamLocation
ld a, [hl]
cp LUCKY_EGG
call z, BoostExp
ldh a, [hQuotient + 3]
ld [wStringBuffer2 + 1], a
ldh a, [hQuotient + 2]
ld [wStringBuffer2], a
ld a, [wCurPartyMon]
ld hl, wPartyMonNicknames
call GetNickname
ld hl, Text_MonGainedExpPoint
call BattleTextbox
ld a, [wStringBuffer2 + 1]
ldh [hQuotient + 3], a
ld a, [wStringBuffer2]
ldh [hQuotient + 2], a
pop bc
call AnimateExpBar
push bc
call LoadTilemapToTempTilemap
pop bc
ld hl, MON_EXP + 2
add hl, bc
ld d, [hl]
ldh a, [hQuotient + 3]
add d
ld [hld], a
ld d, [hl]
ldh a, [hQuotient + 2]
adc d
ld [hl], a
jr nc, .no_exp_overflow
dec hl
inc [hl]
jr nz, .no_exp_overflow
ld a, $ff
ld [hli], a
ld [hli], a
ld [hl], a
.no_exp_overflow
ld a, [wCurPartyMon]
ld e, a
ld d, 0
ld hl, wPartySpecies
add hl, de
ld a, [hl]
ld [wCurSpecies], a
call GetBaseData
push bc
ld d, MAX_LEVEL
callfar CalcExpAtLevel
pop bc
ld hl, MON_EXP + 2
add hl, bc
push bc
ldh a, [hQuotient + 1]
ld b, a
ldh a, [hQuotient + 2]
ld c, a
ldh a, [hQuotient + 3]
ld d, a
ld a, [hld]
sub d
ld a, [hld]
sbc c
ld a, [hl]
sbc b
jr c, .not_max_exp
ld a, b
ld [hli], a
ld a, c
ld [hli], a
ld a, d
ld [hld], a
.not_max_exp
; Check if the mon leveled up
xor a ; PARTYMON
ld [wMonType], a
predef CopyMonToTempMon
callfar CalcLevel
pop bc
ld hl, MON_LEVEL
add hl, bc
ld a, [hl]
cp MAX_LEVEL
jp nc, .next_mon
cp d
jp z, .next_mon
; <NICKNAME> grew to level ##!
ld [wTempLevel], a
ld a, [wCurPartyLevel]
push af
ld a, d
ld [wCurPartyLevel], a
ld [hl], a
ld hl, MON_SPECIES
add hl, bc
ld a, [hl]
ld [wCurSpecies], a
ld [wTempSpecies], a ; unused?
call GetBaseData
ld hl, MON_MAXHP + 1
add hl, bc
ld a, [hld]
ld e, a
ld d, [hl]
push de
ld hl, MON_MAXHP
add hl, bc
ld d, h
ld e, l
ld hl, MON_STAT_EXP - 1
add hl, bc
push bc
ld b, TRUE
predef CalcMonStats
pop bc
pop de
ld hl, MON_MAXHP + 1
add hl, bc
ld a, [hld]
sub e
ld e, a
ld a, [hl]
sbc d
ld d, a
dec hl
ld a, [hl]
add e
ld [hld], a
ld a, [hl]
adc d
ld [hl], a
ld a, [wCurBattleMon]
ld d, a
ld a, [wCurPartyMon]
cp d
jr nz, .skip_active_mon_update
ld de, wBattleMonHP
ld a, [hli]
ld [de], a
inc de
ld a, [hli]
ld [de], a
ld de, wBattleMonMaxHP
push bc
ld bc, PARTYMON_STRUCT_LENGTH - MON_MAXHP
call CopyBytes
pop bc
ld hl, MON_LEVEL
add hl, bc
ld a, [hl]
ld [wBattleMonLevel], a
ld a, [wPlayerSubStatus5]
bit SUBSTATUS_TRANSFORMED, a
jr nz, .transformed
ld hl, MON_ATK
add hl, bc
ld de, wPlayerStats
ld bc, PARTYMON_STRUCT_LENGTH - MON_ATK
call CopyBytes
.transformed
xor a ; FALSE
ld [wApplyStatLevelMultipliersToEnemy], a
call ApplyStatLevelMultiplierOnAllStats
callfar ApplyStatusEffectOnPlayerStats
callfar BadgeStatBoosts
callfar UpdatePlayerHUD
call EmptyBattleTextbox
call LoadTilemapToTempTilemap
ld a, $1
ldh [hBGMapMode], a
.skip_active_mon_update
farcall LevelUpHappinessMod
ld a, [wCurBattleMon]
ld b, a
ld a, [wCurPartyMon]
cp b
jr z, .skip_exp_bar_animation
ld de, SFX_HIT_END_OF_EXP_BAR
call PlaySFX
call WaitSFX
ld hl, BattleText_StringBuffer1GrewToLevel
call StdBattleTextbox
call LoadTilemapToTempTilemap
.skip_exp_bar_animation
xor a ; PARTYMON
ld [wMonType], a
predef CopyMonToTempMon
hlcoord 9, 0
ld b, 10
ld c, 9
call Textbox
hlcoord 11, 1
ld bc, 4
predef PrintTempMonStats
ld c, 30
call DelayFrames
call WaitPressAorB_BlinkCursor
call SafeLoadTempTilemapToTilemap
xor a ; PARTYMON
ld [wMonType], a
ld a, [wCurSpecies]
ld [wTempSpecies], a ; unused?
ld a, [wCurPartyLevel]
push af
ld c, a
ld a, [wTempLevel]
ld b, a
.level_loop
inc b
ld a, b
ld [wCurPartyLevel], a
push bc
predef LearnLevelMoves
pop bc
ld a, b
cp c
jr nz, .level_loop
pop af
ld [wCurPartyLevel], a
ld hl, wEvolvableFlags
ld a, [wCurPartyMon]
ld c, a
ld b, SET_FLAG
predef SmallFarFlagAction
pop af
ld [wCurPartyLevel], a
.next_mon
ld a, [wPartyCount]
ld b, a
ld a, [wCurPartyMon]
inc a
cp b
jr z, .done
ld [wCurPartyMon], a
ld a, MON_SPECIES
call GetPartyParamLocation
ld b, h
ld c, l
jp .loop
.done
jp ResetBattleParticipants
.EvenlyDivideExpAmongParticipants:
; count number of battle participants
ld a, [wBattleParticipantsNotFainted]
ld b, a
ld c, PARTY_LENGTH
ld d, 0
.count_loop
xor a
srl b
adc d
ld d, a
dec c
jr nz, .count_loop
cp 2
ret c
ld [wTempByteValue], a
ld hl, wEnemyMonBaseStats
ld c, wEnemyMonEnd - wEnemyMonBaseStats
.base_stat_division_loop
xor a
ldh [hDividend + 0], a
ld a, [hl]
ldh [hDividend + 1], a
ld a, [wTempByteValue]
ldh [hDivisor], a
ld b, 2
call Divide
ldh a, [hQuotient + 3]
ld [hli], a
dec c
jr nz, .base_stat_division_loop
ret
BoostExp:
; Multiply experience by 1.5x
push bc
; load experience value
ldh a, [hProduct + 2]
ld b, a
ldh a, [hProduct + 3]
ld c, a
; halve it
srl b
rr c
; add it back to the whole exp value
add c
ldh [hProduct + 3], a
ldh a, [hProduct + 2]
adc b
ldh [hProduct + 2], a
pop bc
ret
Text_MonGainedExpPoint:
text_far Text_Gained
text_asm
ld hl, ExpPointsText
ld a, [wStringBuffer2 + 2] ; IsTradedMon
and a
ret z
ld hl, BoostedExpPointsText
ret
BoostedExpPointsText:
text_far _BoostedExpPointsText
text_end
ExpPointsText:
text_far _ExpPointsText
text_end
AnimateExpBar:
push bc
ld hl, wCurPartyMon
ld a, [wCurBattleMon]
cp [hl]
jp nz, .finish
ld a, [wBattleMonLevel]
cp MAX_LEVEL
jp nc, .finish
ldh a, [hProduct + 3]
ld [wExperienceGained + 2], a
push af
ldh a, [hProduct + 2]
ld [wExperienceGained + 1], a
push af
xor a
ld [wExperienceGained], a
xor a ; PARTYMON
ld [wMonType], a
predef CopyMonToTempMon
ld a, [wTempMonLevel]
ld b, a
ld e, a
push de
ld de, wTempMonExp + 2
call CalcExpBar
push bc
ld hl, wTempMonExp + 2
ld a, [wExperienceGained + 2]
add [hl]
ld [hld], a
ld a, [wExperienceGained + 1]
adc [hl]
ld [hld], a
jr nc, .NoOverflow
inc [hl]
jr nz, .NoOverflow
ld a, $ff
ld [hli], a
ld [hli], a
ld [hl], a
.NoOverflow:
ld d, MAX_LEVEL
callfar CalcExpAtLevel
ldh a, [hProduct + 1]
ld b, a
ldh a, [hProduct + 2]
ld c, a
ldh a, [hProduct + 3]
ld d, a
ld hl, wTempMonExp + 2
ld a, [hld]
sub d
ld a, [hld]
sbc c
ld a, [hl]
sbc b
jr c, .AlreadyAtMaxExp
ld a, b
ld [hli], a
ld a, c
ld [hli], a
ld a, d
ld [hld], a
.AlreadyAtMaxExp:
callfar CalcLevel
ld a, d
pop bc
pop de
ld d, a
cp e
jr nc, .LoopLevels
ld a, e
ld d, a
.LoopLevels:
ld a, e
cp MAX_LEVEL
jr nc, .FinishExpBar
cp d
jr z, .FinishExpBar
inc a
ld [wTempMonLevel], a
ld [wCurPartyLevel], a
ld [wBattleMonLevel], a
push de
call .PlayExpBarSound
ld c, $40
call .LoopBarAnimation
call PrintPlayerHUD
ld hl, wBattleMonNickname
ld de, wStringBuffer1
ld bc, MON_NAME_LENGTH
call CopyBytes
call TerminateExpBarSound
ld de, SFX_HIT_END_OF_EXP_BAR
call PlaySFX
farcall AnimateEndOfExpBar
call WaitSFX
ld hl, BattleText_StringBuffer1GrewToLevel
call StdBattleTextbox
pop de
inc e
ld b, $0
jr .LoopLevels
.FinishExpBar:
push bc
ld b, d
ld de, wTempMonExp + 2
call CalcExpBar
ld a, b
pop bc
ld c, a
call .PlayExpBarSound
call .LoopBarAnimation
call TerminateExpBarSound
pop af
ldh [hProduct + 2], a
pop af
ldh [hProduct + 3], a
.finish
pop bc
ret
.PlayExpBarSound:
push bc
call WaitSFX
ld de, SFX_EXP_BAR
call PlaySFX
ld c, 10
call DelayFrames
pop bc
ret
.LoopBarAnimation:
ld d, 3
dec b
.anim_loop
inc b
push bc
push de
hlcoord 17, 11
call PlaceExpBar
pop de
ld a, $1
ldh [hBGMapMode], a
ld c, d
call DelayFrames
xor a
ldh [hBGMapMode], a
pop bc
ld a, c
cp b
jr z, .end_animation
inc b
push bc
push de
hlcoord 17, 11
call PlaceExpBar
pop de
ld a, $1
ldh [hBGMapMode], a
ld c, d
call DelayFrames
xor a
ldh [hBGMapMode], a
dec d
jr nz, .min_number_of_frames
ld d, 1
.min_number_of_frames
pop bc
ld a, c
cp b
jr nz, .anim_loop
.end_animation
ld a, $1
ldh [hBGMapMode], a
ret
SendOutMonText:
ld a, [wLinkMode]
and a
jr z, .not_linked
; If we're in a LinkBattle print just "Go <PlayerMon>"
; unless DoBattle already set [wBattleHasJustStarted]
ld hl, GoMonText
ld a, [wBattleHasJustStarted]
and a
jr nz, .skip_to_textbox
.not_linked
; Depending on the HP of the enemy mon, the game prints a different text
ld hl, wEnemyMonHP
ld a, [hli]
or [hl]
ld hl, GoMonText
jr z, .skip_to_textbox
; BUG: Switching out or switching against a Pokémon with max HP below 4 freezes the game (see docs/bugs_and_glitches.md)
; compute enemy health remaining as a percentage
xor a
ldh [hMultiplicand + 0], a
ld hl, wEnemyMonHP
ld a, [hli]
ld [wEnemyHPAtTimeOfPlayerSwitch], a
ldh [hMultiplicand + 1], a
ld a, [hl]
ld [wEnemyHPAtTimeOfPlayerSwitch + 1], a
ldh [hMultiplicand + 2], a
ld a, 25
ldh [hMultiplier], a
call Multiply
ld hl, wEnemyMonMaxHP
ld a, [hli]
ld b, [hl]
srl a
rr b
srl a
rr b
ld a, b
ld b, 4
ldh [hDivisor], a
call Divide
ldh a, [hQuotient + 3]
ld hl, GoMonText
cp 70
jr nc, .skip_to_textbox
ld hl, DoItMonText
cp 40
jr nc, .skip_to_textbox
ld hl, GoForItMonText
cp 10
jr nc, .skip_to_textbox
ld hl, YourFoesWeakGetmMonText
.skip_to_textbox
jp BattleTextbox
GoMonText:
text_far _GoMonText
text_asm
jr PrepareBattleMonNicknameText
DoItMonText:
text_far _DoItMonText
text_asm
jr PrepareBattleMonNicknameText
GoForItMonText:
text_far _GoForItMonText
text_asm
jr PrepareBattleMonNicknameText
YourFoesWeakGetmMonText:
text_far _YourFoesWeakGetmMonText
text_asm
PrepareBattleMonNicknameText:
ld hl, BattleMonNicknameText
ret
BattleMonNicknameText:
text_far _BattleMonNicknameText
text_end
WithdrawMonText:
ld hl, .WithdrawMonText
jp BattleTextbox
.WithdrawMonText:
text_far _BattleMonNickCommaText
text_asm
; Depending on the HP lost since the enemy mon was sent out, the game prints a different text
push de
push bc
; compute enemy health lost as a percentage
ld hl, wEnemyMonHP + 1
ld de, wEnemyHPAtTimeOfPlayerSwitch + 1
ld b, [hl]
dec hl
ld a, [de]
sub b
ldh [hMultiplicand + 2], a
dec de
ld b, [hl]
ld a, [de]
sbc b
ldh [hMultiplicand + 1], a
ld a, 25
ldh [hMultiplier], a
call Multiply
ld hl, wEnemyMonMaxHP
ld a, [hli]
ld b, [hl]
srl a
rr b
srl a
rr b
ld a, b
ld b, 4
ldh [hDivisor], a
call Divide
pop bc
pop de
ldh a, [hQuotient + 3]
ld hl, ThatsEnoughComeBackText
and a
ret z
ld hl, ComeBackText
cp 30
ret c
ld hl, OKComeBackText
cp 70
ret c
ld hl, GoodComeBackText
ret
ThatsEnoughComeBackText:
text_far _ThatsEnoughComeBackText
text_end
OKComeBackText:
text_far _OKComeBackText
text_end
GoodComeBackText:
text_far _GoodComeBackText
text_end
TextJump_ComeBack: ; unreferenced
ld hl, ComeBackText
ret
ComeBackText:
text_far _ComeBackText
text_end
HandleSafariAngerEatingStatus: ; unreferenced
ld hl, wSafariMonEating
ld a, [hl]
and a
jr z, .angry
dec [hl]
ld hl, BattleText_WildMonIsEating
jr .finish
.angry
dec hl
assert wSafariMonEating - 1 == wSafariMonAngerCount
ld a, [hl]
and a
ret z
dec [hl]
ld hl, BattleText_WildMonIsAngry
jr nz, .finish
push hl
ld a, [wEnemyMonSpecies]
ld [wCurSpecies], a
call GetBaseData
ld a, [wBaseCatchRate]
ld [wEnemyMonCatchRate], a
pop hl
.finish
push hl
call SafeLoadTempTilemapToTilemap
pop hl
jp StdBattleTextbox
FillInExpBar:
push hl
call CalcExpBar
pop hl
ld de, 7
add hl, de
jp PlaceExpBar
CalcExpBar:
; Calculate the percent exp between this level and the next
; Level in b
push de
ld d, b
push de
callfar CalcExpAtLevel
pop de
; exp at current level gets pushed to the stack
ld hl, hMultiplicand
ld a, [hli]
push af
ld a, [hli]
push af
ld a, [hl]
push af
; next level
inc d
callfar CalcExpAtLevel
; back up the next level exp, and subtract the two levels
ld hl, hMultiplicand + 2
ld a, [hl]
ldh [hMathBuffer + 2], a
pop bc
sub b
ld [hld], a
ld a, [hl]
ldh [hMathBuffer + 1], a
pop bc
sbc b
ld [hld], a
ld a, [hl]
ldh [hMathBuffer], a
pop bc
sbc b
ld [hl], a
pop de
ld hl, hMultiplicand + 1
ld a, [hli]
push af
ld a, [hl]
push af
; get the amount of exp remaining to the next level
ld a, [de]
dec de
ld c, a
ldh a, [hMathBuffer + 2]
sub c
ld [hld], a
ld a, [de]
dec de
ld b, a
ldh a, [hMathBuffer + 1]
sbc b
ld [hld], a
ld a, [de]
ld c, a
ldh a, [hMathBuffer]
sbc c
ld [hld], a
xor a
ld [hl], a
ld a, 64
ldh [hMultiplier], a
call Multiply
pop af
ld c, a
pop af
ld b, a
.loop
ld a, b
and a
jr z, .done
srl b
rr c
ld hl, hProduct
srl [hl]
inc hl
rr [hl]
inc hl
rr [hl]
inc hl
rr [hl]
jr .loop
.done
ld a, c
ldh [hDivisor], a
ld b, 4
call Divide
ldh a, [hQuotient + 3]
ld b, a
ld a, $40
sub b
ld b, a
ret
PlaceExpBar:
ld c, $8 ; number of tiles
.loop1
ld a, b
sub $8
jr c, .next
ld b, a
ld a, $6a ; full bar
ld [hld], a
dec c
jr z, .finish
jr .loop1
.next
add $8
jr z, .loop2
add $54 ; tile to the left of small exp bar tile
jr .skip
.loop2
ld a, $62 ; empty bar
.skip
ld [hld], a
ld a, $62 ; empty bar
dec c
jr nz, .loop2
.finish
ret
GetBattleMonBackpic:
ld a, [wPlayerSubStatus4]
bit SUBSTATUS_SUBSTITUTE, a
ld hl, BattleAnimCmd_RaiseSub
jr nz, GetBattleMonBackpic_DoAnim ; substitute
DropPlayerSub:
ld a, [wPlayerMinimized]
and a
ld hl, BattleAnimCmd_MinimizeOpp
jr nz, GetBattleMonBackpic_DoAnim
ld a, [wCurPartySpecies]
push af
ld a, [wBattleMonSpecies]
ld [wCurPartySpecies], a
ld hl, wBattleMonDVs
predef GetUnownLetter
ld de, vTiles2 tile $31
predef GetMonBackpic
pop af
ld [wCurPartySpecies], a
ret
GetBattleMonBackpic_DoAnim:
ldh a, [hBattleTurn]
push af
xor a
ldh [hBattleTurn], a
ld a, BANK(BattleAnimCommands)
rst FarCall
pop af
ldh [hBattleTurn], a
ret
GetEnemyMonFrontpic:
ld a, [wEnemySubStatus4]
bit SUBSTATUS_SUBSTITUTE, a
ld hl, BattleAnimCmd_RaiseSub
jr nz, GetEnemyMonFrontpic_DoAnim
DropEnemySub:
ld a, [wEnemyMinimized]
and a
ld hl, BattleAnimCmd_MinimizeOpp
jr nz, GetEnemyMonFrontpic_DoAnim
ld a, [wCurPartySpecies]
push af
ld a, [wEnemyMonSpecies]
ld [wCurSpecies], a
ld [wCurPartySpecies], a
call GetBaseData
ld hl, wEnemyMonDVs
predef GetUnownLetter
ld de, vTiles2
predef GetAnimatedFrontpic
pop af
ld [wCurPartySpecies], a
ret
GetEnemyMonFrontpic_DoAnim:
ldh a, [hBattleTurn]
push af
call SetEnemyTurn
ld a, BANK(BattleAnimCommands)
rst FarCall
pop af
ldh [hBattleTurn], a
ret
StartBattle:
; This check prevents you from entering a battle without any Pokemon.
; Those using walk-through-walls to bypass getting a Pokemon experience
; the effects of this check.
ld a, [wPartyCount]
and a
ret z
ld a, [wTimeOfDayPal]
push af
call BattleIntro
call DoBattle
call ExitBattle
pop af
ld [wTimeOfDayPal], a
scf
ret
CallDoBattle: ; unreferenced
call DoBattle
ret
BattleIntro:
farcall StubbedTrainerRankings_Battles ; mobile
call LoadTrainerOrWildMonPic
xor a
ld [wTempBattleMonSpecies], a
ld [wBattleMenuCursorPosition], a
xor a
ldh [hMapAnims], a
farcall PlayBattleMusic
farcall ShowLinkBattleParticipants
farcall FindFirstAliveMonAndStartBattle
call DisableSpriteUpdates
farcall ClearBattleRAM
call InitEnemy
call BackUpBGMap2
ld b, SCGB_BATTLE_GRAYSCALE
call GetSGBLayout
ld hl, rLCDC
res rLCDC_WINDOW_TILEMAP, [hl] ; select vBGMap0/vBGMap2
call InitBattleDisplay
call BattleStartMessage
ld hl, rLCDC
set rLCDC_WINDOW_TILEMAP, [hl] ; select vBGMap1/vBGMap3
xor a
ldh [hBGMapMode], a
call EmptyBattleTextbox
hlcoord 9, 7
lb bc, 5, 11
call ClearBox
hlcoord 1, 0
lb bc, 4, 10
call ClearBox
call ClearSprites
ld a, [wBattleMode]
cp WILD_BATTLE
call z, UpdateEnemyHUD
ld a, $1
ldh [hBGMapMode], a
ret
LoadTrainerOrWildMonPic:
ld a, [wOtherTrainerClass]
and a
jr nz, .Trainer
ld a, [wTempWildMonSpecies]
ld [wCurPartySpecies], a
.Trainer:
ld [wTempEnemyMonSpecies], a
ret
InitEnemy:
ld a, [wOtherTrainerClass]
and a
jp nz, InitEnemyTrainer ; trainer
jp InitEnemyWildmon ; wild
BackUpBGMap2:
ldh a, [rSVBK]
push af
ld a, BANK(wDecompressScratch)
ldh [rSVBK], a
ld hl, wDecompressScratch
ld bc, $40 tiles ; vBGMap3 - vBGMap2
ld a, $2
call ByteFill
ldh a, [rVBK]
push af
ld a, $1
ldh [rVBK], a
ld de, wDecompressScratch
hlbgcoord 0, 0 ; vBGMap2
lb bc, BANK(BackUpBGMap2), $40
call Request2bpp
pop af
ldh [rVBK], a
pop af
ldh [rSVBK], a
ret
InitEnemyTrainer:
ld [wTrainerClass], a
farcall StubbedTrainerRankings_TrainerBattles
xor a
ld [wTempEnemyMonSpecies], a
callfar GetTrainerAttributes
callfar ReadTrainerParty
; RIVAL1's first mon has no held item
ld a, [wTrainerClass]
cp RIVAL1
jr nz, .ok
xor a
ld [wOTPartyMon1Item], a
.ok
ld de, vTiles2
callfar GetTrainerPic
xor a
ldh [hGraphicStartTile], a
dec a
ld [wEnemyItemState], a
hlcoord 12, 0
lb bc, 7, 7
predef PlaceGraphic
ld a, -1
ld [wCurOTMon], a
ld a, TRAINER_BATTLE
ld [wBattleMode], a
call IsGymLeader
jr nc, .done
xor a
ld [wCurPartyMon], a
ld a, [wPartyCount]
ld b, a
.partyloop
push bc
ld a, MON_HP
call GetPartyParamLocation
ld a, [hli]
or [hl]
jr z, .skipfaintedmon
ld c, HAPPINESS_GYMBATTLE
callfar ChangeHappiness
.skipfaintedmon
pop bc
dec b
jr z, .done
ld hl, wCurPartyMon
inc [hl]
jr .partyloop
.done
ret
InitEnemyWildmon:
ld a, WILD_BATTLE
ld [wBattleMode], a
farcall StubbedTrainerRankings_WildBattles
call LoadEnemyMon
ld hl, wEnemyMonMoves
ld de, wWildMonMoves
ld bc, NUM_MOVES
call CopyBytes
ld hl, wEnemyMonPP
ld de, wWildMonPP
ld bc, NUM_MOVES
call CopyBytes
ld hl, wEnemyMonDVs
predef GetUnownLetter
ld a, [wCurPartySpecies]
cp UNOWN
jr nz, .skip_unown
ld a, [wFirstUnownSeen]
and a
jr nz, .skip_unown
ld a, [wUnownLetter]
ld [wFirstUnownSeen], a
.skip_unown
ld de, vTiles2
predef GetAnimatedFrontpic
xor a
ld [wTrainerClass], a
ldh [hGraphicStartTile], a
hlcoord 12, 0
lb bc, 7, 7
predef PlaceGraphic
ret
FillEnemyMovesFromMoveIndicesBuffer: ; unreferenced
ld hl, wEnemyMonMoves
ld de, wListMoves_MoveIndicesBuffer
ld b, NUM_MOVES
.loop
ld a, [de]
inc de
ld [hli], a
and a
jr z, .clearpp
push bc
push hl
push hl
dec a
ld hl, Moves + MOVE_PP
ld bc, MOVE_LENGTH
call AddNTimes
ld a, BANK(Moves)
call GetFarByte
pop hl
ld bc, wEnemyMonPP - (wEnemyMonMoves + 1)
add hl, bc
ld [hl], a
pop hl
pop bc
dec b
jr nz, .loop
ret
.clear
xor a
ld [hli], a
.clearpp
push bc
push hl
ld bc, wEnemyMonPP - (wEnemyMonMoves + 1)
add hl, bc
xor a
ld [hl], a
pop hl
pop bc
dec b
jr nz, .clear
ret
ExitBattle:
call .HandleEndOfBattle
call CleanUpBattleRAM
ret
.HandleEndOfBattle:
ld a, [wLinkMode]
and a
jr z, .not_linked
call ShowLinkBattleParticipantsAfterEnd
ld c, 150
call DelayFrames
call DisplayLinkBattleResult
ret
.not_linked
ld a, [wBattleResult]
and $f
ret nz
call CheckPayDay
xor a
ld [wForceEvolution], a
predef EvolveAfterBattle
farcall GivePokerusAndConvertBerries
ret
CleanUpBattleRAM:
call BattleEnd_HandleRoamMons
xor a
ld [wLowHealthAlarm], a
ld [wBattleMode], a
ld [wBattleType], a
ld [wAttackMissed], a
ld [wTempWildMonSpecies], a
ld [wOtherTrainerClass], a
ld [wFailedToFlee], a
ld [wNumFleeAttempts], a
ld [wForcedSwitch], a
ld [wPartyMenuCursor], a
ld [wKeyItemsPocketCursor], a
ld [wItemsPocketCursor], a
ld [wBattleMenuCursorPosition], a
ld [wCurMoveNum], a
ld [wBallsPocketCursor], a
ld [wLastPocket], a
ld [wMenuScrollPosition], a
ld [wKeyItemsPocketScrollPosition], a
ld [wItemsPocketScrollPosition], a
ld [wBallsPocketScrollPosition], a
ld hl, wPlayerSubStatus1
ld b, wEnemyFuryCutterCount - wPlayerSubStatus1
.loop
ld [hli], a
dec b
jr nz, .loop
call WaitSFX
ret
CheckPayDay:
ld hl, wPayDayMoney
ld a, [hli]
or [hl]
inc hl
or [hl]
ret z
ld a, [wAmuletCoin]
and a
jr z, .okay
ld hl, wPayDayMoney + 2
sla [hl]
dec hl
rl [hl]
dec hl
rl [hl]
jr nc, .okay
ld a, $ff
ld [hli], a
ld [hli], a
ld [hl], a
.okay
ld hl, wPayDayMoney + 2
ld de, wMoney + 2
call AddBattleMoneyToAccount
ld hl, BattleText_PlayerPickedUpPayDayMoney
call StdBattleTextbox
ld a, [wInBattleTowerBattle]
bit 0, a
ret z
call ClearTilemap
call ClearBGPalettes
ret
ShowLinkBattleParticipantsAfterEnd:
farcall StubbedTrainerRankings_LinkBattles
farcall BackupMobileEventIndex
ld a, [wCurOTMon]
ld hl, wOTPartyMon1Status
call GetPartyLocation
ld a, [wEnemyMonStatus]
ld [hl], a
call ClearTilemap
farcall _ShowLinkBattleParticipants
ret
DisplayLinkBattleResult:
farcall CheckMobileBattleError
jp c, .Mobile_InvalidBattle
call IsMobileBattle2
jr nz, .proceed
ld hl, wcd2a
bit 4, [hl]
jr z, .proceed
farcall DetermineLinkBattleResult
.proceed
ld a, [wBattleResult]
and $f
cp LOSE
jr c, .win ; WIN
jr z, .lose ; LOSE
; DRAW
farcall StubbedTrainerRankings_ColosseumDraws
ld de, .Draw
jr .store_result
.win
farcall StubbedTrainerRankings_ColosseumWins
ld de, .YouWin
jr .store_result
.lose
farcall StubbedTrainerRankings_ColosseumLosses
ld de, .YouLose
jr .store_result
.store_result
hlcoord 6, 8
call PlaceString
farcall BackupMobileEventIndex
ld c, 200
call DelayFrames
ld a, BANK(sLinkBattleStats)
call OpenSRAM
call AddLastLinkBattleToLinkRecord
call ReadAndPrintLinkBattleRecord
call CloseSRAM
call IsMobileBattle2
jr z, .mobile
call WaitPressAorB_BlinkCursor
call ClearTilemap
ret
.mobile
ld c, 200
call DelayFrames
call ClearTilemap
ret
.YouWin:
db "YOU WIN@"
.YouLose:
db "YOU LOSE@"
.Draw:
db " DRAW@"
.Mobile_InvalidBattle:
hlcoord 6, 8
ld de, .InvalidBattle
call PlaceString
ld c, 200
call DelayFrames
call ClearTilemap
ret
.InvalidBattle:
db "INVALID BATTLE@"
IsMobileBattle2:
ld a, [wLinkMode]
cp LINK_MOBILE
ret
_DisplayLinkRecord:
ld a, BANK(sLinkBattleStats)
call OpenSRAM
call ReadAndPrintLinkBattleRecord
call CloseSRAM
hlcoord 0, 0, wAttrmap
xor a
ld bc, SCREEN_WIDTH * SCREEN_HEIGHT
call ByteFill
call WaitBGMap2
ld b, SCGB_DIPLOMA
call GetSGBLayout
call SetPalettes
ld c, 8
call DelayFrames
call WaitPressAorB_BlinkCursor
ret
ReadAndPrintLinkBattleRecord:
call ClearTilemap
call ClearSprites
call .PrintBattleRecord
hlcoord 0, 8
ld b, NUM_LINK_BATTLE_RECORDS
ld de, sLinkBattleRecord1Name
.loop
push bc
push hl
push de
ld a, [de]
and a
jr z, .PrintFormatString
ld a, [wSavedAtLeastOnce]
and a
jr z, .PrintFormatString
push hl
push hl
ld h, d
ld l, e
ld de, wLinkBattleRecordName
ld bc, NAME_LENGTH - 1
call CopyBytes
ld a, "@"
ld [de], a
inc de ; wLinkBattleRecordWins
ld bc, 6
call CopyBytes
ld de, wLinkBattleRecordName
pop hl
call PlaceString
pop hl
ld de, 26
add hl, de
push hl
ld de, wLinkBattleRecordWins
lb bc, 2, 4
call PrintNum
pop hl
ld de, 5
add hl, de
push hl
ld de, wLinkBattleRecordLosses
lb bc, 2, 4
call PrintNum
pop hl
ld de, 5
add hl, de
ld de, wLinkBattleRecordDraws
lb bc, 2, 4
call PrintNum
jr .next
.PrintFormatString:
ld de, .Format
call PlaceString
.next
pop hl
ld bc, LINK_BATTLE_RECORD_LENGTH
add hl, bc
ld d, h
ld e, l
pop hl
ld bc, 2 * SCREEN_WIDTH
add hl, bc
pop bc
dec b
jr nz, .loop
ret
.PrintBattleRecord:
hlcoord 1, 0
ld de, .Record
call PlaceString
hlcoord 0, 6
ld de, .Result
call PlaceString
hlcoord 0, 2
ld de, .Total
call PlaceString
hlcoord 6, 4
ld de, sLinkBattleWins
call .PrintZerosIfNoSaveFileExists
jr c, .quit
lb bc, 2, 4
call PrintNum
hlcoord 11, 4
ld de, sLinkBattleLosses
call .PrintZerosIfNoSaveFileExists
lb bc, 2, 4
call PrintNum
hlcoord 16, 4
ld de, sLinkBattleDraws
call .PrintZerosIfNoSaveFileExists
lb bc, 2, 4
call PrintNum
.quit
ret
.PrintZerosIfNoSaveFileExists:
ld a, [wSavedAtLeastOnce]
and a
ret nz
ld de, .Scores
call PlaceString
scf
ret
.Scores:
db " 0 0 0@"
.Format:
db " --- <LF>"
db " - - -@"
.Record:
db "<PLAYER>'s RECORD@"
.Result:
db "RESULT WIN LOSE DRAW@"
.Total:
db "TOTAL WIN LOSE DRAW@"
BattleEnd_HandleRoamMons:
ld a, [wBattleType]
cp BATTLETYPE_ROAMING
jr nz, .not_roaming
ld a, [wBattleResult]
and $f
jr z, .caught_or_defeated_roam_mon ; WIN
call GetRoamMonHP
ld a, [wEnemyMonHP + 1]
ld [hl], a
jr .update_roam_mons
.caught_or_defeated_roam_mon
call GetRoamMonHP
ld [hl], 0
call GetRoamMonMapGroup
ld [hl], GROUP_N_A
call GetRoamMonMapNumber
ld [hl], MAP_N_A
call GetRoamMonSpecies
ld [hl], 0
ret
.not_roaming
call BattleRandom
and $f
ret nz
.update_roam_mons
callfar UpdateRoamMons
ret
GetRoamMonMapGroup:
ld a, [wTempEnemyMonSpecies]
ld b, a
ld a, [wRoamMon1Species]
cp b
ld hl, wRoamMon1MapGroup
ret z
ld a, [wRoamMon2Species]
cp b
ld hl, wRoamMon2MapGroup
ret z
ld hl, wRoamMon3MapGroup
ret
GetRoamMonMapNumber:
ld a, [wTempEnemyMonSpecies]
ld b, a
ld a, [wRoamMon1Species]
cp b
ld hl, wRoamMon1MapNumber
ret z
ld a, [wRoamMon2Species]
cp b
ld hl, wRoamMon2MapNumber
ret z
ld hl, wRoamMon3MapNumber
ret
GetRoamMonHP:
; output: hl = wRoamMonHP
ld a, [wTempEnemyMonSpecies]
ld b, a
ld a, [wRoamMon1Species]
cp b
ld hl, wRoamMon1HP
ret z
ld a, [wRoamMon2Species]
cp b
ld hl, wRoamMon2HP
ret z
ld hl, wRoamMon3HP
ret
GetRoamMonDVs:
; output: hl = wRoamMonDVs
ld a, [wTempEnemyMonSpecies]
ld b, a
ld a, [wRoamMon1Species]
cp b
ld hl, wRoamMon1DVs
ret z
ld a, [wRoamMon2Species]
cp b
ld hl, wRoamMon2DVs
ret z
ld hl, wRoamMon3DVs
ret
GetRoamMonSpecies:
ld a, [wTempEnemyMonSpecies]
ld hl, wRoamMon1Species
cp [hl]
ret z
ld hl, wRoamMon2Species
cp [hl]
ret z
ld hl, wRoamMon3Species
ret
AddLastLinkBattleToLinkRecord:
ld hl, wOTPlayerID
ld de, wStringBuffer1
ld bc, 2
call CopyBytes
ld hl, wOTPlayerName
ld bc, NAME_LENGTH - 1
call CopyBytes
ld hl, sLinkBattleStats - (LINK_BATTLE_RECORD_LENGTH - 6)
call .StoreResult
ld hl, sLinkBattleRecord
ld d, NUM_LINK_BATTLE_RECORDS
.loop
push hl
inc hl
inc hl
ld a, [hl]
dec hl
dec hl
and a
jr z, .copy
push de
ld bc, LINK_BATTLE_RECORD_LENGTH - 6
ld de, wStringBuffer1
call CompareBytesLong
pop de
pop hl
jr c, .done
ld bc, LINK_BATTLE_RECORD_LENGTH
add hl, bc
dec d
jr nz, .loop
ld bc, -LINK_BATTLE_RECORD_LENGTH
add hl, bc
push hl
.copy
ld d, h
ld e, l
ld hl, wStringBuffer1
ld bc, LINK_BATTLE_RECORD_LENGTH - 6
call CopyBytes
ld b, 6
xor a
.loop2
ld [de], a
inc de
dec b
jr nz, .loop2
pop hl
.done
call .StoreResult
call .FindOpponentAndAppendRecord
ret
.StoreResult:
ld a, [wBattleResult]
and $f
cp LOSE
ld bc, (sLinkBattleRecord1Wins - sLinkBattleRecord1) + 1
jr c, .okay ; WIN
ld bc, (sLinkBattleRecord1Losses - sLinkBattleRecord1) + 1
jr z, .okay ; LOSE
; DRAW
ld bc, (sLinkBattleRecord1Draws - sLinkBattleRecord1) + 1
.okay
add hl, bc
call .CheckOverflow
ret nc
inc [hl]
ret nz
dec hl
inc [hl]
ret
.CheckOverflow:
dec hl
ld a, [hl]
inc hl
cp HIGH(MAX_LINK_RECORD)
ret c
ld a, [hl]
cp LOW(MAX_LINK_RECORD)
ret
.FindOpponentAndAppendRecord:
ld b, NUM_LINK_BATTLE_RECORDS
ld hl, sLinkBattleRecord1End - 1
ld de, wLinkBattleRecordBuffer
.loop3
push bc
push de
push hl
call .LoadPointer
pop hl
ld a, e
pop de
ld [de], a
inc de
ld a, b
ld [de], a
inc de
ld a, c
ld [de], a
inc de
ld bc, LINK_BATTLE_RECORD_LENGTH
add hl, bc
pop bc
dec b
jr nz, .loop3
ld b, $0
ld c, $1
.loop4
ld a, b
add b
add b
ld e, a
ld d, 0
ld hl, wLinkBattleRecordBuffer
add hl, de
push hl
ld a, c
add c
add c
ld e, a
ld d, 0
ld hl, wLinkBattleRecordBuffer
add hl, de
ld d, h
ld e, l
pop hl
push bc
ld c, 3
call CompareBytes
pop bc
jr z, .equal
jr nc, .done2
.equal
inc c
ld a, c
cp $5
jr nz, .loop4
inc b
ld c, b
inc c
ld a, b
cp $4
jr nz, .loop4
ret
.done2
push bc
ld a, b
ld bc, LINK_BATTLE_RECORD_LENGTH
ld hl, sLinkBattleRecord
call AddNTimes
push hl
ld de, wLinkBattleRecordBuffer
ld bc, LINK_BATTLE_RECORD_LENGTH
call CopyBytes
pop hl
pop bc
push hl
ld a, c
ld bc, LINK_BATTLE_RECORD_LENGTH
ld hl, sLinkBattleRecord
call AddNTimes
pop de
push hl
ld bc, LINK_BATTLE_RECORD_LENGTH
call CopyBytes
ld hl, wLinkBattleRecordBuffer
ld bc, LINK_BATTLE_RECORD_LENGTH
pop de
call CopyBytes
ret
.LoadPointer:
ld e, $0
ld a, [hld]
ld c, a
ld a, [hld]
ld b, a
ld a, [hld]
add c
ld c, a
ld a, [hld]
adc b
ld b, a
jr nc, .okay2
inc e
.okay2
ld a, [hld]
add c
ld c, a
ld a, [hl]
adc b
ld b, a
ret nc
inc e
ret
InitBattleDisplay:
call .InitBackPic
hlcoord 0, 12
ld b, 4
ld c, 18
call Textbox
farcall MobileTextBorder
hlcoord 1, 5
lb bc, 3, 7
call ClearBox
call LoadStandardFont
call _LoadBattleFontsHPBar
call .BlankBGMap
xor a
ldh [hMapAnims], a
ldh [hSCY], a
ld a, $90
ldh [hWY], a
ldh [rWY], a
call WaitBGMap
xor a
ldh [hBGMapMode], a
farcall BattleIntroSlidingPics
ld a, $1
ldh [hBGMapMode], a
ld a, $31
ldh [hGraphicStartTile], a
hlcoord 2, 6
lb bc, 6, 6
predef PlaceGraphic
xor a
ldh [hWY], a
vc_hook Unknown_InitBattleDisplay
ldh [rWY], a
call WaitBGMap
call HideSprites
ld b, SCGB_BATTLE_COLORS
call GetSGBLayout
call SetPalettes
ld a, $90
ldh [hWY], a
xor a
ldh [hSCX], a
ret
.BlankBGMap:
ldh a, [rSVBK]
push af
ld a, BANK(wDecompressScratch)
ldh [rSVBK], a
ld hl, wDecompressScratch
ld bc, BG_MAP_WIDTH * BG_MAP_HEIGHT
ld a, " "
call ByteFill
ld de, wDecompressScratch
hlbgcoord 0, 0
lb bc, BANK(@), (BG_MAP_WIDTH * BG_MAP_HEIGHT) / LEN_2BPP_TILE
call Request2bpp
pop af
ldh [rSVBK], a
ret
.InitBackPic:
call GetTrainerBackpic
call CopyBackpic
ret
GetTrainerBackpic:
; Load the player character's backpic (6x6) into VRAM starting from vTiles2 tile $31.
; Special exception for Dude.
ld b, BANK(DudeBackpic)
ld hl, DudeBackpic
ld a, [wBattleType]
cp BATTLETYPE_TUTORIAL
jr z, .Decompress
; What gender are we?
ld a, [wPlayerSpriteSetupFlags]
bit PLAYERSPRITESETUP_FEMALE_TO_MALE_F, a
jr nz, .Chris
ld a, [wPlayerGender]
bit PLAYERGENDER_FEMALE_F, a
jr z, .Chris
; It's a girl.
farcall GetKrisBackpic
ret
.Chris:
; It's a boy.
ld b, BANK(ChrisBackpic)
ld hl, ChrisBackpic
.Decompress:
ld de, vTiles2 tile $31
ld c, 7 * 7
predef DecompressGet2bpp
ret
CopyBackpic:
ldh a, [rSVBK]
push af
ld a, BANK(wDecompressScratch)
ldh [rSVBK], a
ld hl, vTiles0
ld de, vTiles2 tile $31
ldh a, [hROMBank]
ld b, a
ld c, 7 * 7
call Get2bpp
pop af
ldh [rSVBK], a
call .LoadTrainerBackpicAsOAM
ld a, $31
ldh [hGraphicStartTile], a
hlcoord 2, 6
lb bc, 6, 6
predef PlaceGraphic
ret
.LoadTrainerBackpicAsOAM:
ld hl, wShadowOAMSprite00
xor a
ldh [hMapObjectIndex], a
ld b, 6
ld e, (SCREEN_WIDTH + 1) * TILE_WIDTH
.outer_loop
ld c, 3
ld d, 8 * TILE_WIDTH
.inner_loop
ld [hl], d ; y
inc hl
ld [hl], e ; x
inc hl
ldh a, [hMapObjectIndex]
ld [hli], a ; tile id
inc a
ldh [hMapObjectIndex], a
ld a, PAL_BATTLE_OB_PLAYER
ld [hli], a ; attributes
ld a, d
add 1 * TILE_WIDTH
ld d, a
dec c
jr nz, .inner_loop
ldh a, [hMapObjectIndex]
add $3
ldh [hMapObjectIndex], a
ld a, e
add 1 * TILE_WIDTH
ld e, a
dec b
jr nz, .outer_loop
ret
BattleStartMessage:
ld a, [wBattleMode]
dec a
jr z, .wild
ld de, SFX_SHINE
call PlaySFX
call WaitSFX
ld c, 20
call DelayFrames
farcall Battle_GetTrainerName
ld hl, WantsToBattleText
jr .PlaceBattleStartText
.wild
call BattleCheckEnemyShininess
jr nc, .not_shiny
xor a
ld [wNumHits], a
ld a, 1
ldh [hBattleTurn], a
ld a, 1
ld [wBattleAnimParam], a
ld de, ANIM_SEND_OUT_MON
call Call_PlayBattleAnim
.not_shiny
farcall CheckSleepingTreeMon
jr c, .skip_cry
farcall CheckBattleScene
jr c, .cry_no_anim
hlcoord 12, 0
ld d, $0
ld e, ANIM_MON_NORMAL
predef AnimateFrontpic
jr .skip_cry ; cry is played during the animation
.cry_no_anim
ld a, $f
ld [wCryTracks], a
ld a, [wTempEnemyMonSpecies]
call PlayStereoCry
.skip_cry
ld a, [wBattleType]
cp BATTLETYPE_FISH
jr nz, .NotFishing
farcall StubbedTrainerRankings_HookedEncounters
ld hl, HookedPokemonAttackedText
jr .PlaceBattleStartText
.NotFishing:
ld hl, PokemonFellFromTreeText
cp BATTLETYPE_TREE
jr z, .PlaceBattleStartText
ld hl, WildCelebiAppearedText
cp BATTLETYPE_CELEBI
jr z, .PlaceBattleStartText
ld hl, WildPokemonAppearedText
.PlaceBattleStartText:
push hl
farcall BattleStart_TrainerHuds
pop hl
call StdBattleTextbox
call IsMobileBattle2
ret nz
ld c, $2 ; start
farcall Mobile_PrintOpponentBattleMessage
ret