; 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 .print_text call StdBattleTextbox .skip_text call StopDangerSound ; BUG: SFX_RUN does not play correctly when a wild Pokémon flees from battle (see docs/bugs_and_glitches.md) ld de, SFX_RUN call PlaySFX .skip_sfx call SetPlayerTurn ld a, 1 ld [wBattleEnded], a ret BattleTurn: .loop 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 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 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 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 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 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 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 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 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 ld a, [wLinkMode] and a ret nz 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_coins .give_coins ld a, [wAmuletCoin] and a call nz, .DoubleReward call .CheckMaxedOutMomCoins push af ld a, FALSE jr nc, .okay ld a, [wMomSavingCoins] and MOM_SAVING_COINS_MASK cp (1 << MOM_SAVING_SOME_COINS_F) | (1 << MOM_SAVING_HALF_COINS_F) jr nz, .okay inc a ; TRUE .okay ld b, a ld c, 4 .loop ld a, b and a jr z, .loop2 call .AddCoinsToMom dec c dec b jr .loop .loop2 ld a, c and a jr z, .done call .AddCoinsToWallet dec c jr .loop2 .done call .DoubleReward call .DoubleReward pop af jr nc, .KeepItAll ld a, [wMomSavingCoins] and MOM_SAVING_COINS_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, GotCoinsForWinningText jp StdBattleTextbox .AddCoinsToMom: push bc ld hl, wBattleReward + 2 ld de, wMomsCoins + 2 call AddBattleCoinsToAccount pop bc ret .AddCoinsToWallet: push bc ld hl, wBattleReward + 2 ld de, wCoins + 2 call AddBattleCoinsToAccount 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 .CheckMaxedOutMomCoins: ld hl, wMomsCoins + 2 ld a, [hld] cp LOW(MAX_COINS) ld a, [hld] sbc HIGH(MAX_COINS) ; mid ld a, [hl] sbc HIGH(MAX_COINS >> 8) ret AddBattleCoinsToAccount: ld c, 3 and a push de push hl push bc ld b, h ld c, l 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_COINS) ld a, [hld] sbc HIGH(MAX_COINS) ; mid ld a, [hl] sbc HIGH(MAX_COINS >> 8) ret c ld [hl], HIGH(MAX_COINS >> 8) inc hl ld [hl], HIGH(MAX_COINS) ; mid inc hl ld [hl], LOW(MAX_COINS) 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, wPayDayCoins 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 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 ld hl, wEnemyMonHP ld a, [hli] or [hl] jr nz, .send_out_pokemon call ClearSprites call ClearBGPalettes call _LoadHPBar call ExitMenu call LoadTilemapToTempTilemap call WaitBGMap call GetMemCGBLayout call SetDefaultBGPAndOBP 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 GetMemCGBLayout call SetDefaultBGPAndOBP 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 SetUpBattlePartyMenu: call ClearBGPalettes SetUpBattlePartyMenu_Loop: ; switch to fullscreen menu? farcall LoadPartyMenuGFX farcall InitPartyMenuWithCancel farcall InitPartyMenuBGPal7 farcall InitPartyMenuGFX ret JumpToPartyMenuAndPrintText: farcall WritePartyMenuTilemap farcall PlacePartyMenuText call WaitBGMap call SetDefaultBGPAndOBP call DelayFrame ret SelectBattleMon: farcall 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 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 SwitchMonAlreadyOut jr c, .pick xor a ret LostBattle: ld a, 1 ld [wBattleEnded], a 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 .not_canlose ld a, [wLinkMode] and a jr nz, .LostLinkBattle ; Grayscale ld b, CGB_BATTLE_GRAYSCALE call GetCGBLayout call SetDefaultBGPAndOBP 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 .text call StdBattleTextbox .end 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_FORCESHINY 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 ; 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 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: farcall LoadBattleMenu and a ret BattleMenu_Pack: ld a, [wLinkMode] 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 ClearMenuAndWindowData call FinishBattleAnim and a ret .run xor a ld [wWildMon], a ld a, [wBattleResult] and BATTLERESULT_BITMASK ld [wBattleResult], a ; WIN call ClearMenuAndWindowData call SetDefaultBGPAndOBP 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: jr BattleMenuPKMN_Loop .Stats: call Battle_StatsScreen jp BattleMenuPKMN_ReturnFromStats .Cancel: call ClearSprites call ClearPalettes call DelayFrame call _LoadHPBar call CloseWindow call LoadTilemapToTempTilemap call GetMemCGBLayout call SetDefaultBGPAndOBP jp BattleMenu .GetMenu: farcall BattleMonMenu 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 GetMemCGBLayout call SetDefaultBGPAndOBP 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: 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 Textbox1bpp 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 Textbox1bpp 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 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: call .StageForSend ld [wLinkBattleSentAction], a farcall PlaceWaitingText call .LinkBattle_SendReceiveAction ret .StageForSend: ld a, [wBattlePlayerAction] and a ; BATTLEPLAYERACTION_USEMOVE? jr nz, .switch ld a, [wCurPlayerMove] ld b, BATTLEACTION_STRUGGLE cp STRUGGLE jr z, .struggle ld b, BATTLEACTION_SKIPTURN cp $ff jr z, .struggle ld a, [wCurMoveNum] jr .use_move .switch ld a, [wCurPartyMon] add BATTLEACTION_SWITCH1 jr .use_move .struggle ld a, b .use_move and $0f ret .LinkBattle_SendReceiveAction: ld a, [wLinkBattleSentAction] ld [wPlayerLinkAction], a ld a, $ff ld [wOtherPlayerLinkAction], a .waiting call LinkTransfer call DelayFrame ld a, [wOtherPlayerLinkAction] inc a jr z, .waiting vc_hook Wireless_end_exchange vc_patch Wireless_net_delay_3 if DEF(_CRYSTAL11_VC) ld b, 26 else ld b, 10 endc vc_patch_end .receive call DelayFrame call LinkTransfer dec b jr nz, .receive if DEF(_CRYSTAL11_VC) ld b, 26 else ld b, 10 endc vc_patch_end .acknowledge call DelayFrame call LinkDataReceived dec b jr nz, .acknowledge vc_hook Wireless_end_send_zero_bytes ld a, [wOtherPlayerLinkAction] ld [wBattleAction], a 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 ; 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_FORCESHINY 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: ; 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_BATTLE_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, [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, CGB_BATTLE_COLORS call GetCGBLayout call SetDefaultBGPAndOBP call DelayFrame pop hl pop de pop bc pop af ret GiveExperiencePoints: ; Give experience. ; Don't give experience if linked. ld a, [wLinkMode] and 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 ; 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 Textbox1bpp 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 " ; 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: 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, CGB_BATTLE_GRAYSCALE call GetCGBLayout 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 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 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, wPayDayCoins ld a, [hli] or [hl] inc hl or [hl] ret z ld a, [wAmuletCoin] and a jr z, .okay ld hl, wPayDayCoins + 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, wPayDayCoins + 2 ld de, wCoins + 2 call AddBattleCoinsToAccount ld hl, BattleText_PlayerPickedUpPayDayCoins call StdBattleTextbox ret ShowLinkBattleParticipantsAfterEnd: ld a, [wCurOTMon] ld hl, wOTPartyMon1Status call GetPartyLocation ld a, [wEnemyMonStatus] ld [hl], a call ClearTilemap farcall _ShowLinkBattleParticipants ret DisplayLinkBattleResult: ld a, [wBattleResult] and $f cp LOSE jr c, .win ; WIN jr z, .lose ; LOSE ; DRAW ld de, .Draw jr .store_result .win ld de, .YouWin jr .store_result .lose ld de, .YouLose jr .store_result .store_result hlcoord 6, 8 call PlaceString ld c, 200 call DelayFrames ld a, BANK(sLinkBattleStats) call OpenSRAM call AddLastLinkBattleToLinkRecord call ReadAndPrintLinkBattleRecord call CloseSRAM call WaitPressAorB_BlinkCursor call ClearTilemap ret .YouWin: db "YOU WIN@" .YouLose: db "YOU LOSE@" .Draw: db " DRAW@" _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, CGB_DIPLOMA call GetCGBLayout call SetDefaultBGPAndOBP 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 " --- " db " - - -@" .Record: db "'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 Textbox1bpp 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, CGB_BATTLE_COLORS call GetCGBLayout call SetDefaultBGPAndOBP 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 .PrintBattleStartText .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 ld hl, HookedPokemonAttackedText jr .PrintBattleStartText .NotFishing: ld hl, PokemonFellFromTreeText cp BATTLETYPE_TREE jr z, .PrintBattleStartText ld hl, WildCelebiAppearedText cp BATTLETYPE_CELEBI jr z, .PrintBattleStartText ld hl, WildPokemonAppearedText .PrintBattleStartText: push hl farcall BattleStart_TrainerHuds pop hl call StdBattleTextbox ret