ref: 3be81c1bf9cedbff4e0f081619883511433ef88b
dir: /engine/battle/core.asm/
; 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, BATTLEACTION_FORFEIT 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 ; BUG: PRZ and BRN stat reductions don't apply to switched Pokémon (see docs/bugs_and_glitches.md) 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 NUM_UNLOCKED_UNOWN_SETS * 2 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