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