summaryrefslogtreecommitdiff
path: root/en/engine
diff options
context:
space:
mode:
Diffstat (limited to 'en/engine')
-rwxr-xr-xen/engine/HoF_room_pc.asm270
-rwxr-xr-xen/engine/battle/core.asm8719
-rwxr-xr-xen/engine/battle/end_of_battle.asm88
-rw-r--r--en/engine/battle/link_battle_versus_text.asm23
-rw-r--r--en/engine/battle/save_trainer_name.asm112
-rwxr-xr-xen/engine/cable_club.asm977
-rwxr-xr-xen/engine/clear_save.asm23
-rwxr-xr-xen/engine/evolve_trade.asm44
-rwxr-xr-xen/engine/hall_of_fame.asm288
-rwxr-xr-xen/engine/hidden_object_functions17.asm475
-rwxr-xr-xen/engine/hidden_object_functions7.asm467
-rwxr-xr-xen/engine/items/items.asm2989
-rwxr-xr-xen/engine/learn_move.asm226
-rw-r--r--en/engine/menu/bills_pc.asm554
-rwxr-xr-xen/engine/menu/diploma.asm113
-rw-r--r--en/engine/menu/draw_start_menu.asm89
-rwxr-xr-xen/engine/menu/league_pc.asm120
-rwxr-xr-xen/engine/menu/main_menu.asm712
-rwxr-xr-xen/engine/menu/naming_screen.asm512
-rwxr-xr-xen/engine/menu/party_menu.asm325
-rwxr-xr-xen/engine/menu/players_pc.asm303
-rwxr-xr-xen/engine/menu/pokedex.asm665
-rwxr-xr-xen/engine/menu/prize_menu.asm306
-rwxr-xr-xen/engine/menu/start_sub_menus.asm854
-rwxr-xr-xen/engine/menu/status_screen.asm481
-rw-r--r--en/engine/menu/text_box.asm767
-rwxr-xr-xen/engine/menu/vending_machine.asm139
-rwxr-xr-xen/engine/oak_speech2.asm272
-rw-r--r--en/engine/overworld/movement.asm880
-rwxr-xr-xen/engine/overworld/pokemart.asm272
-rw-r--r--en/engine/print_waiting_text.asm20
-rwxr-xr-xen/engine/save.asm708
-rwxr-xr-xen/engine/slot_machine.asm892
-rwxr-xr-xen/engine/status_ailments.asm46
-rwxr-xr-xen/engine/titlescreen.asm403
-rwxr-xr-xen/engine/town_map.asm619
-rwxr-xr-xen/engine/trade.asm853
37 files changed, 25606 insertions, 0 deletions
diff --git a/en/engine/HoF_room_pc.asm b/en/engine/HoF_room_pc.asm
new file mode 100755
index 00000000..390de962
--- /dev/null
+++ b/en/engine/HoF_room_pc.asm
@@ -0,0 +1,270 @@
+HallOfFamePC:
+ callba AnimateHallOfFame
+ call ClearScreen
+ ld c, 100
+ call DelayFrames
+ call DisableLCD
+ ld hl, vFont
+ ld bc, $800 / 2
+ call ZeroMemory
+ ld hl, vChars2 + $600
+ ld bc, $200 / 2
+ call ZeroMemory
+ ld hl, vChars2 + $7e0
+ ld bc, $10
+ ld a, $ff
+ call FillMemory
+ coord hl, 0, 0
+ call FillFourRowsWithBlack
+ coord hl, 0, 14
+ call FillFourRowsWithBlack
+ ld a, %11000000
+ ld [rBGP], a
+ call EnableLCD
+ ld a, $ff
+ call PlaySoundWaitForCurrent
+ ld c, BANK(Music_Credits)
+ ld a, MUSIC_CREDITS
+ call PlayMusic
+ ld c, 128
+ call DelayFrames
+ xor a
+ ld [wUnusedCD3D], a ; not read
+ ld [wNumCreditsMonsDisplayed], a
+ jp Credits
+
+FadeInCreditsText:
+ ld hl, HoFGBPalettes
+ ld b, 4
+.loop
+ ld a, [hli]
+ ld [rBGP], a
+ ld c, 5
+ call DelayFrames
+ dec b
+ jr nz, .loop
+ ret
+
+DisplayCreditsMon:
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call SaveScreenTilesToBuffer1
+ call FillMiddleOfScreenWithWhite
+
+ ; display the next monster from CreditsMons
+ ld hl, wNumCreditsMonsDisplayed
+ ld c, [hl] ; how many monsters have we displayed so far?
+ inc [hl]
+ ld b, 0
+ ld hl, CreditsMons
+ add hl, bc ; go that far in the list of monsters and get the next one
+ ld a, [hl]
+ ld [wcf91], a
+ ld [wd0b5], a
+ coord hl, 8, 6
+ call GetMonHeader
+ call LoadFrontSpriteByMonIndex
+ ld hl, vBGMap0 + $c
+ call CreditsCopyTileMapToVRAM
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call LoadScreenTilesFromBuffer1
+ ld hl, vBGMap0
+ call CreditsCopyTileMapToVRAM
+ ld a, $A7
+ ld [rWX], a
+ ld hl, vBGMap1
+ call CreditsCopyTileMapToVRAM
+ call FillMiddleOfScreenWithWhite
+ ld a, %11111100 ; make the mon a black silhouette
+ ld [rBGP], a
+
+; scroll the mon left by one tile 7 times
+ ld bc, 7
+.scrollLoop1
+ call ScrollCreditsMonLeft
+ dec c
+ jr nz, .scrollLoop1
+
+; scroll the mon left by one tile 20 times
+; This time, we have to move the window left too in order to hide the text that
+; is wrapping around to the right side of the screen.
+ ld c, 20
+.scrollLoop2
+ call ScrollCreditsMonLeft
+ ld a, [rWX]
+ sub 8
+ ld [rWX], a
+ dec c
+ jr nz, .scrollLoop2
+
+ xor a
+ ld [hWY], a
+ ld a, %11000000
+ ld [rBGP], a
+ ret
+
+INCLUDE "data/credit_mons.asm"
+
+ScrollCreditsMonLeft:
+ ld h, b
+ ld l, $20
+ call ScrollCreditsMonLeft_SetSCX
+ ld h, $0
+ ld l, $70
+ call ScrollCreditsMonLeft_SetSCX
+ ld a, b
+ add $8
+ ld b, a
+ ret
+
+ScrollCreditsMonLeft_SetSCX:
+ ld a, [rLY]
+ cp l
+ jr nz, ScrollCreditsMonLeft_SetSCX
+ ld a, h
+ ld [rSCX], a
+.loop
+ ld a, [rLY]
+ cp h
+ jr z, .loop
+ ret
+
+HoFGBPalettes:
+ db %11000000
+ db %11010000
+ db %11100000
+ db %11110000
+
+CreditsCopyTileMapToVRAM:
+ ld a, l
+ ld [H_AUTOBGTRANSFERDEST], a
+ ld a, h
+ ld [H_AUTOBGTRANSFERDEST + 1], a
+ ld a, 1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ jp Delay3
+
+ZeroMemory:
+; zero bc bytes at hl
+ ld [hl], 0
+ inc hl
+ inc hl
+ dec bc
+ ld a, b
+ or c
+ jr nz, ZeroMemory
+ ret
+
+FillFourRowsWithBlack:
+ ld bc, SCREEN_WIDTH * 4
+ ld a, $7e
+ jp FillMemory
+
+FillMiddleOfScreenWithWhite:
+ coord hl, 0, 4
+ ld bc, SCREEN_WIDTH * 10
+ ld a, " "
+ jp FillMemory
+
+Credits:
+ ld de, CreditsOrder
+ push de
+.nextCreditsScreen
+ pop de
+ coord hl, 9, 6
+ push hl
+ call FillMiddleOfScreenWithWhite
+ pop hl
+.nextCreditsCommand
+ ld a, [de]
+ inc de
+ push de
+ cp $ff
+ jr z, .fadeInTextAndShowMon
+ cp $fe
+ jr z, .showTextAndShowMon
+ cp $fd
+ jr z, .fadeInText
+ cp $fc
+ jr z, .showText
+ cp $fb
+ jr z, .showCopyrightText
+ cp $fa
+ jr z, .showTheEnd
+ push hl
+ push hl
+ ld hl, CreditsTextPointers
+ add a
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ ld a, [de]
+ inc de
+ ld c, a
+ ld b, $ff
+ pop hl
+ add hl, bc
+ call PlaceString
+ pop hl
+ ld bc, SCREEN_WIDTH * 2
+ add hl, bc
+ pop de
+ jr .nextCreditsCommand
+.fadeInTextAndShowMon
+ call FadeInCreditsText
+ ld c, 90
+ jr .next1
+.showTextAndShowMon
+ ld c, 110
+.next1
+ call DelayFrames
+ call DisplayCreditsMon
+ jr .nextCreditsScreen
+.fadeInText
+ call FadeInCreditsText
+ ld c, 120
+ jr .next2
+.showText
+ ld c, 140
+.next2
+ call DelayFrames
+ jr .nextCreditsScreen
+.showCopyrightText
+ push de
+ callba LoadCopyrightTiles
+ pop de
+ pop de
+ jr .nextCreditsCommand
+.showTheEnd
+ ld c, 16
+ call DelayFrames
+ call FillMiddleOfScreenWithWhite
+ pop de
+ ld de, TheEndGfx
+ ld hl, vChars2 + $600
+ lb bc, BANK(TheEndGfx), (TheEndGfxEnd - TheEndGfx) / $10
+ call CopyVideoData
+ coord hl, 4, 8
+ ld de, TheEndTextString
+ call PlaceString
+ coord hl, 4, 9
+ inc de
+ call PlaceString
+ jp FadeInCreditsText
+
+TheEndTextString:
+; "T H E E N D"
+ db $60," ",$62," ",$64," ",$64," ",$66," ",$68,"@"
+ db $61," ",$63," ",$65," ",$65," ",$67," ",$69,"@"
+
+ inclang data/credits_order.asm
+
+ inclang text/credits_text.asm
+
+TheEndGfx:
+ INCBIN "gfx/theend.2bpp"
+TheEndGfxEnd:
diff --git a/en/engine/battle/core.asm b/en/engine/battle/core.asm
new file mode 100755
index 00000000..9265503d
--- /dev/null
+++ b/en/engine/battle/core.asm
@@ -0,0 +1,8719 @@
+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
diff --git a/en/engine/battle/end_of_battle.asm b/en/engine/battle/end_of_battle.asm
new file mode 100755
index 00000000..c77e3b39
--- /dev/null
+++ b/en/engine/battle/end_of_battle.asm
@@ -0,0 +1,88 @@
+EndOfBattle:
+ ld a, [wLinkState]
+ cp LINK_STATE_BATTLING
+ jr nz, .notLinkBattle
+; link battle
+ ld a, [wEnemyMonPartyPos]
+ ld hl, wEnemyMon1Status
+ ld bc, wEnemyMon2 - wEnemyMon1
+ call AddNTimes
+ ld a, [wEnemyMonStatus]
+ ld [hl], a
+ call ClearScreen
+ callab DisplayLinkBattleVersusTextBox
+ ld a, [wBattleResult]
+ cp $1
+ ld de, YouWinText
+ jr c, .placeWinOrLoseString
+ ld de, YouLoseText
+ jr z, .placeWinOrLoseString
+ ld de, DrawText
+.placeWinOrLoseString
+ coord hl, 6, 8
+ call PlaceString
+ ld c, 200
+ call DelayFrames
+ jr .evolution
+.notLinkBattle
+ ld a, [wBattleResult]
+ and a
+ jr nz, .resetVariables
+ ld hl, wTotalPayDayMoney
+ ld a, [hli]
+ or [hl]
+ inc hl
+ or [hl]
+ jr z, .evolution ; if pay day money is 0, jump
+ ld de, wPlayerMoney + 2
+ ld c, $3
+ predef AddBCDPredef
+ ld hl, PickUpPayDayMoneyText
+ call PrintText
+.evolution
+ xor a
+ ld [wForceEvolution], a
+ predef EvolutionAfterBattle
+.resetVariables
+ xor a
+ ld [wLowHealthAlarm], a ;disable low health alarm
+ ld [wChannelSoundIDs + Ch4], a
+ ld [wIsInBattle], a
+ ld [wBattleType], a
+ ld [wMoveMissed], a
+ ld [wCurOpponent], a
+ ld [wForcePlayerToChooseMon], a
+ ld [wNumRunAttempts], a
+ ld [wEscapedFromBattle], a
+ ld hl, wPartyAndBillsPCSavedMenuItem
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ ld [hl], a
+ ld [wListScrollOffset], a
+ ld hl, wPlayerStatsToDouble
+ ld b, $18
+.loop
+ ld [hli], a
+ dec b
+ jr nz, .loop
+ ld hl, wd72c
+ set 0, [hl]
+ call WaitForSoundToFinish
+ call GBPalWhiteOut
+ ld a, $ff
+ ld [wDestinationWarpID], a
+ ret
+
+YouWinText:
+ db "YOU WIN@"
+
+YouLoseText:
+ db "YOU LOSE@"
+
+DrawText:
+ db " DRAW@"
+
+PickUpPayDayMoneyText:
+ TX_FAR _PickUpPayDayMoneyText
+ db "@"
diff --git a/en/engine/battle/link_battle_versus_text.asm b/en/engine/battle/link_battle_versus_text.asm
new file mode 100644
index 00000000..76559117
--- /dev/null
+++ b/en/engine/battle/link_battle_versus_text.asm
@@ -0,0 +1,23 @@
+; display "[player] VS [enemy]" text box with pokeballs representing their parties next to the names
+DisplayLinkBattleVersusTextBox:
+ call LoadTextBoxTilePatterns
+ coord hl, 3, 4
+ ld b, 7
+ ld c, 12
+ call TextBoxBorder
+ coord hl, 4, 5
+ ld de, wPlayerName
+ call PlaceString
+ coord hl, 4, 10
+ ld de, wLinkEnemyTrainerName
+ call PlaceString
+; place bold "VS" tiles between the names
+ coord hl, 9, 8
+ ld a, $69
+ ld [hli], a
+ ld [hl], $6a
+ xor a
+ ld [wUpdateSpritesEnabled], a
+ callab SetupPlayerAndEnemyPokeballs
+ ld c, 150
+ jp DelayFrames
diff --git a/en/engine/battle/save_trainer_name.asm b/en/engine/battle/save_trainer_name.asm
new file mode 100644
index 00000000..49d0429f
--- /dev/null
+++ b/en/engine/battle/save_trainer_name.asm
@@ -0,0 +1,112 @@
+SaveTrainerName:
+ ld hl, TrainerNamePointers
+ ld a, [wTrainerClass]
+ dec a
+ ld c, a
+ ld b, 0
+ add hl, bc
+ add hl, bc
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld de, wcd6d
+.CopyCharacter
+ ld a, [hli]
+ ld [de], a
+ inc de
+ cp "@"
+ jr nz, .CopyCharacter
+ ret
+
+TrainerNamePointers:
+; what is the point of these?
+ dw YoungsterName
+ dw BugCatcherName
+ dw LassName
+ dw wTrainerName
+ dw JrTrainerMName
+ dw JrTrainerFName
+ dw PokemaniacName
+ dw SuperNerdName
+ dw wTrainerName
+ dw wTrainerName
+ dw BurglarName
+ dw EngineerName
+ dw JugglerXName
+ dw wTrainerName
+ dw SwimmerName
+ dw wTrainerName
+ dw wTrainerName
+ dw BeautyName
+ dw wTrainerName
+ dw RockerName
+ dw JugglerName
+ dw wTrainerName
+ dw wTrainerName
+ dw BlackbeltName
+ dw wTrainerName
+ dw ProfOakName
+ dw ChiefName
+ dw ScientistName
+ dw wTrainerName
+ dw RocketName
+ dw CooltrainerMName
+ dw CooltrainerFName
+ dw wTrainerName
+ dw wTrainerName
+ dw wTrainerName
+ dw wTrainerName
+ dw wTrainerName
+ dw wTrainerName
+ dw wTrainerName
+ dw wTrainerName
+ dw wTrainerName
+ dw wTrainerName
+ dw wTrainerName
+ dw wTrainerName
+ dw wTrainerName
+ dw wTrainerName
+ dw wTrainerName
+
+YoungsterName:
+ db "YOUNGSTER@"
+BugCatcherName:
+ db "BUG CATCHER@"
+LassName:
+ db "LASS@"
+JrTrainerMName:
+ db "JR.TRAINER♂@"
+JrTrainerFName:
+ db "JR.TRAINER♀@"
+PokemaniacName:
+ db "POKéMANIAC@"
+SuperNerdName:
+ db "SUPER NERD@"
+BurglarName:
+ db "BURGLAR@"
+EngineerName:
+ db "ENGINEER@"
+JugglerXName:
+ db "JUGGLER@"
+SwimmerName:
+ db "SWIMMER@"
+BeautyName:
+ db "BEAUTY@"
+RockerName:
+ db "ROCKER@"
+JugglerName:
+ db "JUGGLER@"
+BlackbeltName:
+ db "BLACKBELT@"
+ProfOakName:
+ db "PROF.OAK@"
+ChiefName:
+ db "CHIEF@"
+ScientistName:
+ db "SCIENTIST@"
+RocketName:
+ db "ROCKET@"
+CooltrainerMName:
+ db "COOLTRAINER♂@"
+CooltrainerFName:
+ db "COOLTRAINER♀@"
diff --git a/en/engine/cable_club.asm b/en/engine/cable_club.asm
new file mode 100755
index 00000000..3e72daa9
--- /dev/null
+++ b/en/engine/cable_club.asm
@@ -0,0 +1,977 @@
+; performs the appropriate action when the player uses the gameboy on the table in the Colosseum or Trade Center
+; In the Colosseum, it starts a battle. In the Trade Center, it displays the trade selection screen.
+; Before doing either action, it swaps random numbers, trainer names and party data with the other gameboy.
+CableClub_DoBattleOrTrade:
+ ld c, 80
+ call DelayFrames
+ call ClearScreen
+ call UpdateSprites
+ call LoadFontTilePatterns
+ call LoadHpBarAndStatusTilePatterns
+ call LoadTrainerInfoTextBoxTiles
+ coord hl, 3, 8
+ ld b, 2
+ ld c, 12
+ call CableClub_TextBoxBorder
+ coord hl, 4, 10
+ ld de, PleaseWaitString
+ call PlaceString
+ ld hl, wPlayerNumHits
+ xor a
+ ld [hli], a
+ ld [hl], $50
+ ; fall through
+
+; This is called after completing a trade.
+CableClub_DoBattleOrTradeAgain:
+ ld hl, wSerialPlayerDataBlock
+ ld a, SERIAL_PREAMBLE_BYTE
+ ld b, 6
+.writePlayerDataBlockPreambleLoop
+ ld [hli], a
+ dec b
+ jr nz, .writePlayerDataBlockPreambleLoop
+ ld hl, wSerialRandomNumberListBlock
+ ld a, SERIAL_PREAMBLE_BYTE
+ ld b, 7
+.writeRandomNumberListPreambleLoop
+ ld [hli], a
+ dec b
+ jr nz, .writeRandomNumberListPreambleLoop
+ ld b, 10
+.generateRandomNumberListLoop
+ call Random
+ cp SERIAL_PREAMBLE_BYTE ; all the random numbers have to be less than the preamble byte
+ jr nc, .generateRandomNumberListLoop
+ ld [hli], a
+ dec b
+ jr nz, .generateRandomNumberListLoop
+ ld hl, wSerialPartyMonsPatchList
+ ld a, SERIAL_PREAMBLE_BYTE
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ ld b, $c8
+ xor a
+.zeroPlayerDataPatchListLoop
+ ld [hli], a
+ dec b
+ jr nz, .zeroPlayerDataPatchListLoop
+ ld hl, wGrassRate
+ ld bc, wTrainerHeaderPtr - wGrassRate
+.zeroEnemyPartyLoop
+ xor a
+ ld [hli], a
+ dec bc
+ ld a, b
+ or c
+ jr nz, .zeroEnemyPartyLoop
+ ld hl, wPartyMons - 1
+ ld de, wSerialPartyMonsPatchList + 10
+ ld bc, 0
+.patchPartyMonsLoop
+ inc c
+ ld a, c
+ cp SERIAL_PREAMBLE_BYTE
+ jr z, .startPatchListPart2
+ ld a, b
+ dec a ; are we in part 2 of the patch list?
+ jr nz, .checkPlayerDataByte ; jump if in part 1
+; if we're in part 2
+ ld a, c
+ cp (wPartyMonOT - (wPartyMons - 1)) - (SERIAL_PREAMBLE_BYTE - 1)
+ jr z, .finishedPatchingPlayerData
+.checkPlayerDataByte
+ inc hl
+ ld a, [hl]
+ cp SERIAL_NO_DATA_BYTE
+ jr nz, .patchPartyMonsLoop
+; if the player data byte matches SERIAL_NO_DATA_BYTE, patch it with $FF and record the offset in the patch list
+ ld a, c
+ ld [de], a
+ inc de
+ ld [hl], $ff
+ jr .patchPartyMonsLoop
+.startPatchListPart2
+ ld a, SERIAL_PATCH_LIST_PART_TERMINATOR
+ ld [de], a ; end of part 1
+ inc de
+ lb bc, 1, 0
+ jr .patchPartyMonsLoop
+.finishedPatchingPlayerData
+ ld a, SERIAL_PATCH_LIST_PART_TERMINATOR
+ ld [de], a ; end of part 2
+ call Serial_SyncAndExchangeNybble
+ ld a, [hSerialConnectionStatus]
+ cp USING_INTERNAL_CLOCK
+ jr nz, .skipSendingTwoZeroBytes
+; if using internal clock
+; send two zero bytes for syncing purposes?
+ call Delay3
+ xor a
+ ld [hSerialSendData], a
+ ld a, START_TRANSFER_INTERNAL_CLOCK
+ ld [rSC], a
+ call DelayFrame
+ xor a
+ ld [hSerialSendData], a
+ ld a, START_TRANSFER_INTERNAL_CLOCK
+ ld [rSC], a
+.skipSendingTwoZeroBytes
+ call Delay3
+ ld a, (1 << SERIAL)
+ ld [rIE], a
+ ld hl, wSerialRandomNumberListBlock
+ ld de, wSerialOtherGameboyRandomNumberListBlock
+ ld bc, $11
+ call Serial_ExchangeBytes
+ ld a, SERIAL_NO_DATA_BYTE
+ ld [de], a
+ ld hl, wSerialPlayerDataBlock
+ ld de, wSerialEnemyDataBlock
+ ld bc, $1a8
+ call Serial_ExchangeBytes
+ ld a, SERIAL_NO_DATA_BYTE
+ ld [de], a
+ ld hl, wSerialPartyMonsPatchList
+ ld de, wSerialEnemyMonsPatchList
+ ld bc, $c8
+ call Serial_ExchangeBytes
+ ld a, (1 << SERIAL) | (1 << TIMER) | (1 << VBLANK)
+ ld [rIE], a
+ ld a, $ff
+ call PlaySound
+ ld a, [hSerialConnectionStatus]
+ cp USING_INTERNAL_CLOCK
+ jr z, .skipCopyingRandomNumberList ; the list generated by the gameboy clocking the connection is used by both gameboys
+ ld hl, wSerialOtherGameboyRandomNumberListBlock
+.findStartOfRandomNumberListLoop
+ ld a, [hli]
+ and a
+ jr z, .findStartOfRandomNumberListLoop
+ cp SERIAL_PREAMBLE_BYTE
+ jr z, .findStartOfRandomNumberListLoop
+ cp SERIAL_NO_DATA_BYTE
+ jr z, .findStartOfRandomNumberListLoop
+ dec hl
+ ld de, wLinkBattleRandomNumberList
+ ld c, 10
+.copyRandomNumberListLoop
+ ld a, [hli]
+ cp SERIAL_NO_DATA_BYTE
+ jr z, .copyRandomNumberListLoop
+ ld [de], a
+ inc de
+ dec c
+ jr nz, .copyRandomNumberListLoop
+.skipCopyingRandomNumberList
+ ld hl, wSerialEnemyDataBlock + 3
+.findStartOfEnemyNameLoop
+ ld a, [hli]
+ and a
+ jr z, .findStartOfEnemyNameLoop
+ cp SERIAL_PREAMBLE_BYTE
+ jr z, .findStartOfEnemyNameLoop
+ cp SERIAL_NO_DATA_BYTE
+ jr z, .findStartOfEnemyNameLoop
+ dec hl
+ ld de, wLinkEnemyTrainerName
+ ld c, NAME_LENGTH
+.copyEnemyNameLoop
+ ld a, [hli]
+ cp SERIAL_NO_DATA_BYTE
+ jr z, .copyEnemyNameLoop
+ ld [de], a
+ inc de
+ dec c
+ jr nz, .copyEnemyNameLoop
+ ld de, wEnemyPartyCount
+ ld bc, wTrainerHeaderPtr - wEnemyPartyCount
+.copyEnemyPartyLoop
+ ld a, [hli]
+ cp SERIAL_NO_DATA_BYTE
+ jr z, .copyEnemyPartyLoop
+ ld [de], a
+ inc de
+ dec bc
+ ld a, b
+ or c
+ jr nz, .copyEnemyPartyLoop
+ ld de, wSerialPartyMonsPatchList
+ ld hl, wPartyMons
+ ld c, 2 ; patch list has 2 parts
+.unpatchPartyMonsLoop
+ ld a, [de]
+ inc de
+ and a
+ jr z, .unpatchPartyMonsLoop
+ cp SERIAL_PREAMBLE_BYTE
+ jr z, .unpatchPartyMonsLoop
+ cp SERIAL_NO_DATA_BYTE
+ jr z, .unpatchPartyMonsLoop
+ cp SERIAL_PATCH_LIST_PART_TERMINATOR
+ jr z, .finishedPartyMonsPatchListPart
+ push hl
+ push bc
+ ld b, 0
+ dec a
+ ld c, a
+ add hl, bc
+ ld a, SERIAL_NO_DATA_BYTE
+ ld [hl], a
+ pop bc
+ pop hl
+ jr .unpatchPartyMonsLoop
+.finishedPartyMonsPatchListPart
+ ld hl, wPartyMons + (SERIAL_PREAMBLE_BYTE - 1)
+ dec c ; is there another part?
+ jr nz, .unpatchPartyMonsLoop
+ ld de, wSerialEnemyMonsPatchList
+ ld hl, wEnemyMons
+ ld c, 2 ; patch list has 2 parts
+.unpatchEnemyMonsLoop
+ ld a, [de]
+ inc de
+ and a
+ jr z, .unpatchEnemyMonsLoop
+ cp SERIAL_PREAMBLE_BYTE
+ jr z, .unpatchEnemyMonsLoop
+ cp SERIAL_NO_DATA_BYTE
+ jr z, .unpatchEnemyMonsLoop
+ cp SERIAL_PATCH_LIST_PART_TERMINATOR
+ jr z, .finishedEnemyMonsPatchListPart
+ push hl
+ push bc
+ ld b, 0
+ dec a
+ ld c, a
+ add hl, bc
+ ld a, SERIAL_NO_DATA_BYTE
+ ld [hl], a
+ pop bc
+ pop hl
+ jr .unpatchEnemyMonsLoop
+.finishedEnemyMonsPatchListPart
+ ld hl, wEnemyMons + (SERIAL_PREAMBLE_BYTE - 1)
+ dec c
+ jr nz, .unpatchEnemyMonsLoop
+ ld a, wEnemyMonOT % $100
+ ld [wUnusedCF8D], a
+ ld a, wEnemyMonOT / $100
+ ld [wUnusedCF8D + 1], a
+ xor a
+ ld [wTradeCenterPointerTableIndex], a
+ ld a, $ff
+ call PlaySound
+ ld a, [hSerialConnectionStatus]
+ cp USING_INTERNAL_CLOCK
+ ld c, 66
+ call z, DelayFrames ; delay if using internal clock
+ ld a, [wLinkState]
+ cp LINK_STATE_START_BATTLE
+ ld a, LINK_STATE_TRADING
+ ld [wLinkState], a
+ jr nz, .trading
+ ld a, LINK_STATE_BATTLING
+ ld [wLinkState], a
+ ld a, OPP_SONY1
+ ld [wCurOpponent], a
+ call ClearScreen
+ call Delay3
+ ld hl, wOptions
+ res 7, [hl]
+ predef InitOpponent
+ predef HealParty
+ jp ReturnToCableClubRoom
+.trading
+ ld c, BANK(Music_GameCorner)
+ ld a, MUSIC_GAME_CORNER
+ call PlayMusic
+ jr CallCurrentTradeCenterFunction
+
+PleaseWaitString:
+ db "PLEASE WAIT!@"
+
+CallCurrentTradeCenterFunction:
+ ld hl, TradeCenterPointerTable
+ ld b, 0
+ ld a, [wTradeCenterPointerTableIndex]
+ cp $ff
+ jp z, DisplayTitleScreen
+ add a
+ ld c, a
+ add hl, bc
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ jp hl
+
+TradeCenter_SelectMon:
+ call ClearScreen
+ call LoadTrainerInfoTextBoxTiles
+ call TradeCenter_DrawPartyLists
+ call TradeCenter_DrawCancelBox
+ xor a
+ ld hl, wSerialSyncAndExchangeNybbleReceiveData
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ ld [hl], a
+ ld [wMenuWatchMovingOutOfBounds], a
+ ld [wCurrentMenuItem], a
+ ld [wLastMenuItem], a
+ ld [wMenuJoypadPollCount], a
+ inc a
+ ld [wSerialExchangeNybbleSendData], a
+ jp .playerMonMenu
+.enemyMonMenu
+ xor a
+ ld [wMenuWatchMovingOutOfBounds], a
+ inc a
+ ld [wWhichTradeMonSelectionMenu], a
+ ld a, D_DOWN | D_LEFT | A_BUTTON
+ ld [wMenuWatchedKeys], a
+ ld a, [wEnemyPartyCount]
+ ld [wMaxMenuItem], a
+ ld a, 9
+ ld [wTopMenuItemY], a
+ ld a, 1
+ ld [wTopMenuItemX], a
+.enemyMonMenu_HandleInput
+ ld hl, hFlags_0xFFF6
+ set 1, [hl]
+ call HandleMenuInput
+ ld hl, hFlags_0xFFF6
+ res 1, [hl]
+ and a
+ jp z, .getNewInput
+ bit 0, a ; A button pressed?
+ jr z, .enemyMonMenu_ANotPressed
+; if A button pressed
+ ld a, [wMaxMenuItem]
+ ld c, a
+ ld a, [wCurrentMenuItem]
+ cp c
+ jr c, .displayEnemyMonStats
+ ld a, [wMaxMenuItem]
+ dec a
+ ld [wCurrentMenuItem], a
+.displayEnemyMonStats
+ ld a, INIT_ENEMYOT_LIST
+ ld [wInitListType], a
+ callab InitList ; the list isn't used
+ ld hl, wEnemyMons
+ call TradeCenter_DisplayStats
+ jp .getNewInput
+.enemyMonMenu_ANotPressed
+ bit 5, a ; Left pressed?
+ jr z, .enemyMonMenu_LeftNotPressed
+; if Left pressed, switch back to the player mon menu
+ xor a ; player mon menu
+ ld [wWhichTradeMonSelectionMenu], a
+ ld a, [wMenuCursorLocation]
+ ld l, a
+ ld a, [wMenuCursorLocation + 1]
+ ld h, a
+ ld a, [wTileBehindCursor]
+ ld [hl], a
+ ld a, [wCurrentMenuItem]
+ ld b, a
+ ld a, [wPartyCount]
+ dec a
+ cp b
+ jr nc, .playerMonMenu
+ ld [wCurrentMenuItem], a
+ jr .playerMonMenu
+.enemyMonMenu_LeftNotPressed
+ bit 7, a ; Down pressed?
+ jp z, .getNewInput
+ jp .selectedCancelMenuItem ; jump if Down pressed
+.playerMonMenu
+ xor a ; player mon menu
+ ld [wWhichTradeMonSelectionMenu], a
+ ld [wMenuWatchMovingOutOfBounds], a
+ ld a, D_DOWN | D_RIGHT | A_BUTTON
+ ld [wMenuWatchedKeys], a
+ ld a, [wPartyCount]
+ ld [wMaxMenuItem], a
+ ld a, 1
+ ld [wTopMenuItemY], a
+ ld a, 1
+ ld [wTopMenuItemX], a
+ coord hl, 1, 1
+ lb bc, 6, 1
+ call ClearScreenArea
+.playerMonMenu_HandleInput
+ ld hl, hFlags_0xFFF6
+ set 1, [hl]
+ call HandleMenuInput
+ ld hl, hFlags_0xFFF6
+ res 1, [hl]
+ and a ; was anything pressed?
+ jr nz, .playerMonMenu_SomethingPressed
+ jp .getNewInput
+.playerMonMenu_SomethingPressed
+ bit 0, a ; A button pressed?
+ jr z, .playerMonMenu_ANotPressed
+ jp .chosePlayerMon ; jump if A button pressed
+; unreachable code
+ ld a, INIT_PLAYEROT_LIST
+ ld [wInitListType], a
+ callab InitList ; the list isn't used
+ call TradeCenter_DisplayStats
+ jp .getNewInput
+.playerMonMenu_ANotPressed
+ bit 4, a ; Right pressed?
+ jr z, .playerMonMenu_RightNotPressed
+; if Right pressed, switch to the enemy mon menu
+ ld a, $1 ; enemy mon menu
+ ld [wWhichTradeMonSelectionMenu], a
+ ld a, [wMenuCursorLocation]
+ ld l, a
+ ld a, [wMenuCursorLocation + 1]
+ ld h, a
+ ld a, [wTileBehindCursor]
+ ld [hl], a
+ ld a, [wCurrentMenuItem]
+ ld b, a
+ ld a, [wEnemyPartyCount]
+ dec a
+ cp b
+ jr nc, .notPastLastEnemyMon
+; when switching to the enemy mon menu, if the menu selection would be past the last enemy mon, select the last enemy mon
+ ld [wCurrentMenuItem], a
+.notPastLastEnemyMon
+ jp .enemyMonMenu
+.playerMonMenu_RightNotPressed
+ bit 7, a ; Down pressed?
+ jr z, .getNewInput
+ jp .selectedCancelMenuItem ; jump if Down pressed
+.getNewInput
+ ld a, [wWhichTradeMonSelectionMenu]
+ and a
+ jp z, .playerMonMenu_HandleInput
+ jp .enemyMonMenu_HandleInput
+.chosePlayerMon
+ call SaveScreenTilesToBuffer1
+ call PlaceUnfilledArrowMenuCursor
+ ld a, [wMaxMenuItem]
+ ld c, a
+ ld a, [wCurrentMenuItem]
+ cp c
+ jr c, .displayStatsTradeMenu
+ ld a, [wMaxMenuItem]
+ dec a
+.displayStatsTradeMenu
+ push af
+ coord hl, 0, 14
+ ld b, 2
+ ld c, 18
+ call CableClub_TextBoxBorder
+ coord hl, 2, 16
+ ld de, .statsTrade
+ call PlaceString
+ xor a
+ ld [wCurrentMenuItem], a
+ ld [wLastMenuItem], a
+ ld [wMenuJoypadPollCount], a
+ ld [wMaxMenuItem], a
+ ld a, 16
+ ld [wTopMenuItemY], a
+.selectStatsMenuItem
+ ld a, " "
+ Coorda 11, 16
+ ld a, D_RIGHT | B_BUTTON | A_BUTTON
+ ld [wMenuWatchedKeys], a
+ ld a, 1
+ ld [wTopMenuItemX], a
+ call HandleMenuInput
+ bit 4, a ; Right pressed?
+ jr nz, .selectTradeMenuItem
+ bit 1, a ; B button pressed?
+ jr z, .displayPlayerMonStats
+.cancelPlayerMonChoice
+ pop af
+ ld [wCurrentMenuItem], a
+ call LoadScreenTilesFromBuffer1
+ jp .playerMonMenu
+.selectTradeMenuItem
+ ld a, " "
+ Coorda 1, 16
+ ld a, D_LEFT | B_BUTTON | A_BUTTON
+ ld [wMenuWatchedKeys], a
+ ld a, 11
+ ld [wTopMenuItemX], a
+ call HandleMenuInput
+ bit 5, a ; Left pressed?
+ jr nz, .selectStatsMenuItem
+ bit 1, a ; B button pressed?
+ jr nz, .cancelPlayerMonChoice
+ jr .choseTrade
+.displayPlayerMonStats
+ pop af
+ ld [wCurrentMenuItem], a
+ ld a, INIT_PLAYEROT_LIST
+ ld [wInitListType], a
+ callab InitList ; the list isn't used
+ call TradeCenter_DisplayStats
+ call LoadScreenTilesFromBuffer1
+ jp .playerMonMenu
+.choseTrade
+ call PlaceUnfilledArrowMenuCursor
+ pop af
+ ld [wCurrentMenuItem], a
+ ld [wTradingWhichPlayerMon], a
+ ld [wSerialExchangeNybbleSendData], a
+ call Serial_PrintWaitingTextAndSyncAndExchangeNybble
+ ld a, [wSerialSyncAndExchangeNybbleReceiveData]
+ cp $f
+ jp z, CallCurrentTradeCenterFunction ; go back to the beginning of the trade selection menu if the other person cancelled
+ ld [wTradingWhichEnemyMon], a
+ call TradeCenter_PlaceSelectedEnemyMonMenuCursor
+ ld a, $1 ; TradeCenter_Trade
+ ld [wTradeCenterPointerTableIndex], a
+ jp CallCurrentTradeCenterFunction
+.statsTrade
+ db "STATS TRADE@"
+.selectedCancelMenuItem
+ ld a, [wCurrentMenuItem]
+ ld b, a
+ ld a, [wMaxMenuItem]
+ cp b
+ jp nz, .getNewInput
+ ld a, [wMenuCursorLocation]
+ ld l, a
+ ld a, [wMenuCursorLocation + 1]
+ ld h, a
+ ld a, " "
+ ld [hl], a
+.cancelMenuItem_Loop
+ ld a, "▶" ; filled arrow cursor
+ Coorda 1, 16
+.cancelMenuItem_JoypadLoop
+ call JoypadLowSensitivity
+ ld a, [hJoy5]
+ and a ; pressed anything?
+ jr z, .cancelMenuItem_JoypadLoop
+ bit 0, a ; A button pressed?
+ jr nz, .cancelMenuItem_APressed
+ bit 6, a ; Up pressed?
+ jr z, .cancelMenuItem_JoypadLoop
+; if Up pressed
+ ld a, " "
+ Coorda 1, 16
+ ld a, [wPartyCount]
+ dec a
+ ld [wCurrentMenuItem], a
+ jp .playerMonMenu
+.cancelMenuItem_APressed
+ ld a, "▷" ; unfilled arrow cursor
+ Coorda 1, 16
+ ld a, $f
+ ld [wSerialExchangeNybbleSendData], a
+ call Serial_PrintWaitingTextAndSyncAndExchangeNybble
+ ld a, [wSerialSyncAndExchangeNybbleReceiveData]
+ cp $f ; did the other person choose Cancel too?
+ jr nz, .cancelMenuItem_Loop
+ ; fall through
+
+ReturnToCableClubRoom:
+ call GBPalWhiteOutWithDelay3
+ ld hl, wFontLoaded
+ ld a, [hl]
+ push af
+ push hl
+ res 0, [hl]
+ xor a
+ ld [wd72d], a
+ dec a
+ ld [wDestinationWarpID], a
+ call LoadMapData
+ callba ClearVariablesOnEnterMap
+ pop hl
+ pop af
+ ld [hl], a
+ call GBFadeInFromWhite
+ ret
+
+TradeCenter_DrawCancelBox:
+ coord hl, 11, 15
+ ld a, $7e
+ ld bc, 2 * SCREEN_WIDTH + 9
+ call FillMemory
+ coord hl, 0, 15
+ ld b, 1
+ ld c, 9
+ call CableClub_TextBoxBorder
+ coord hl, 2, 16
+ ld de, CancelTextString
+ jp PlaceString
+
+CancelTextString:
+ db "CANCEL@"
+
+TradeCenter_PlaceSelectedEnemyMonMenuCursor:
+ ld a, [wSerialSyncAndExchangeNybbleReceiveData]
+ coord hl, 1, 9
+ ld bc, SCREEN_WIDTH
+ call AddNTimes
+ ld [hl], "▷" ; cursor
+ ret
+
+TradeCenter_DisplayStats:
+ ld a, [wCurrentMenuItem]
+ ld [wWhichPokemon], a
+ predef StatusScreen
+ predef StatusScreen2
+ call GBPalNormal
+ call LoadTrainerInfoTextBoxTiles
+ call TradeCenter_DrawPartyLists
+ jp TradeCenter_DrawCancelBox
+
+TradeCenter_DrawPartyLists:
+ coord hl, 0, 0
+ ld b, 6
+ ld c, 18
+ call CableClub_TextBoxBorder
+ coord hl, 0, 8
+ ld b, 6
+ ld c, 18
+ call CableClub_TextBoxBorder
+ coord hl, 5, 0
+ ld de, wPlayerName
+ call PlaceString
+ coord hl, 5, 8
+ ld de, wLinkEnemyTrainerName
+ call PlaceString
+ coord hl, 2, 1
+ ld de, wPartySpecies
+ call TradeCenter_PrintPartyListNames
+ coord hl, 2, 9
+ ld de, wEnemyPartyMons
+ ; fall through
+
+TradeCenter_PrintPartyListNames:
+ ld c, $0
+.loop
+ ld a, [de]
+ cp $ff
+ ret z
+ ld [wd11e], a
+ push bc
+ push hl
+ push de
+ push hl
+ ld a, c
+ ld [$ff95], a
+ call GetMonName
+ pop hl
+ call PlaceString
+ pop de
+ inc de
+ pop hl
+ ld bc, 20
+ add hl, bc
+ pop bc
+ inc c
+ jr .loop
+
+TradeCenter_Trade:
+ ld c, 100
+ call DelayFrames
+ xor a
+ ld [wSerialExchangeNybbleSendData + 1], a ; unnecessary
+ ld [wSerialExchangeNybbleReceiveData], a
+ ld [wMenuWatchMovingOutOfBounds], a
+ ld [wMenuJoypadPollCount], a
+ coord hl, 0, 12
+ ld b, 4
+ ld c, 18
+ call CableClub_TextBoxBorder
+ ld a, [wTradingWhichPlayerMon]
+ ld hl, wPartySpecies
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld a, [hl]
+ ld [wd11e], a
+ call GetMonName
+ ld hl, wcd6d
+ ld de, wNameOfPlayerMonToBeTraded
+ ld bc, NAME_LENGTH
+ call CopyData
+ ld a, [wTradingWhichEnemyMon]
+ ld hl, wEnemyPartyMons
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld a, [hl]
+ ld [wd11e], a
+ call GetMonName
+ ld hl, WillBeTradedText
+ coord bc, 1, 14
+ call TextCommandProcessor
+ call SaveScreenTilesToBuffer1
+ coord hl, 10, 7
+ lb bc, 8, 11
+ ld a, TRADE_CANCEL_MENU
+ ld [wTwoOptionMenuID], a
+ ld a, TWO_OPTION_MENU
+ ld [wTextBoxID], a
+ call DisplayTextBoxID
+ call LoadScreenTilesFromBuffer1
+ ld a, [wCurrentMenuItem]
+ and a
+ jr z, .tradeConfirmed
+; if trade cancelled
+ ld a, $1
+ ld [wSerialExchangeNybbleSendData], a
+ coord hl, 0, 12
+ ld b, 4
+ ld c, 18
+ call CableClub_TextBoxBorder
+ coord hl, 1, 14
+ ld de, TradeCanceled
+ call PlaceString
+ call Serial_PrintWaitingTextAndSyncAndExchangeNybble
+ jp .tradeCancelled
+.tradeConfirmed
+ ld a, $2
+ ld [wSerialExchangeNybbleSendData], a
+ call Serial_PrintWaitingTextAndSyncAndExchangeNybble
+ ld a, [wSerialSyncAndExchangeNybbleReceiveData]
+ dec a ; did the other person cancel?
+ jr nz, .doTrade
+; if the other person cancelled
+ coord hl, 0, 12
+ ld b, 4
+ ld c, 18
+ call CableClub_TextBoxBorder
+ coord hl, 1, 14
+ ld de, TradeCanceled
+ call PlaceString
+ jp .tradeCancelled
+.doTrade
+ ld a, [wTradingWhichPlayerMon]
+ ld hl, wPartyMonOT
+ call SkipFixedLengthTextEntries
+ ld de, wTradedPlayerMonOT
+ ld bc, NAME_LENGTH
+ call CopyData
+ ld hl, wPartyMon1Species
+ ld a, [wTradingWhichPlayerMon]
+ ld bc, wPartyMon2 - wPartyMon1
+ call AddNTimes
+ ld bc, wPartyMon1OTID - wPartyMon1
+ add hl, bc
+ ld a, [hli]
+ ld [wTradedPlayerMonOTID], a
+ ld a, [hl]
+ ld [wTradedPlayerMonOTID + 1], a
+ ld a, [wTradingWhichEnemyMon]
+ ld hl, wEnemyMonOT
+ call SkipFixedLengthTextEntries
+ ld de, wTradedEnemyMonOT
+ ld bc, NAME_LENGTH
+ call CopyData
+ ld hl, wEnemyMons
+ ld a, [wTradingWhichEnemyMon]
+ ld bc, wEnemyMon2 - wEnemyMon1
+ call AddNTimes
+ ld bc, wEnemyMon1OTID - wEnemyMon1
+ add hl, bc
+ ld a, [hli]
+ ld [wTradedEnemyMonOTID], a
+ ld a, [hl]
+ ld [wTradedEnemyMonOTID + 1], a
+ ld a, [wTradingWhichPlayerMon]
+ ld [wWhichPokemon], a
+ ld hl, wPartySpecies
+ ld b, 0
+ ld c, a
+ add hl, bc
+ ld a, [hl]
+ ld [wTradedPlayerMonSpecies], a
+ xor a
+ ld [wRemoveMonFromBox], a
+ call RemovePokemon
+ ld a, [wTradingWhichEnemyMon]
+ ld c, a
+ ld [wWhichPokemon], a
+ ld hl, wEnemyPartyMons
+ ld d, 0
+ ld e, a
+ add hl, de
+ ld a, [hl]
+ ld [wcf91], a
+ ld hl, wEnemyMons
+ ld a, c
+ ld bc, wEnemyMon2 - wEnemyMon1
+ call AddNTimes
+ ld de, wLoadedMon
+ ld bc, wEnemyMon2 - wEnemyMon1
+ call CopyData
+ call AddEnemyMonToPlayerParty
+ ld a, [wPartyCount]
+ dec a
+ ld [wWhichPokemon], a
+ ld a, $1
+ ld [wForceEvolution], a
+ ld a, [wTradingWhichEnemyMon]
+ ld hl, wEnemyPartyMons
+ ld b, 0
+ ld c, a
+ add hl, bc
+ ld a, [hl]
+ ld [wTradedEnemyMonSpecies], a
+ ld a, 10
+ ld [wAudioFadeOutControl], a
+ ld a, $2
+ ld [wAudioSavedROMBank], a
+ ld a, MUSIC_SAFARI_ZONE
+ ld [wNewSoundID], a
+ call PlaySound
+ ld c, 100
+ call DelayFrames
+ call ClearScreen
+ call LoadHpBarAndStatusTilePatterns
+ xor a
+ ld [wUnusedCC5B], a
+ ld a, [hSerialConnectionStatus]
+ cp USING_EXTERNAL_CLOCK
+ jr z, .usingExternalClock
+ predef InternalClockTradeAnim
+ jr .tradeCompleted
+.usingExternalClock
+ predef ExternalClockTradeAnim
+.tradeCompleted
+ callab TryEvolvingMon
+ call ClearScreen
+ call LoadTrainerInfoTextBoxTiles
+ call Serial_PrintWaitingTextAndSyncAndExchangeNybble
+ ld c, 40
+ call DelayFrames
+ coord hl, 0, 12
+ ld b, 4
+ ld c, 18
+ call CableClub_TextBoxBorder
+ coord hl, 1, 14
+ ld de, TradeCompleted
+ call PlaceString
+ predef SaveSAVtoSRAM2
+ ld c, 50
+ call DelayFrames
+ xor a
+ ld [wTradeCenterPointerTableIndex], a
+ jp CableClub_DoBattleOrTradeAgain
+.tradeCancelled
+ ld c, 100
+ call DelayFrames
+ xor a ; TradeCenter_SelectMon
+ ld [wTradeCenterPointerTableIndex], a
+ jp CallCurrentTradeCenterFunction
+
+WillBeTradedText:
+ TX_FAR _WillBeTradedText
+ db "@"
+
+TradeCompleted:
+ db "Trade completed!@"
+
+TradeCanceled:
+ db "Too bad! The trade"
+ next "was canceled!@"
+
+TradeCenterPointerTable:
+ dw TradeCenter_SelectMon
+ dw TradeCenter_Trade
+
+CableClub_Run:
+ ld a, [wLinkState]
+ cp LINK_STATE_START_TRADE
+ jr z, .doBattleOrTrade
+ cp LINK_STATE_START_BATTLE
+ jr z, .doBattleOrTrade
+ cp LINK_STATE_RESET ; this is never used
+ ret nz
+ predef EmptyFunc3
+ jp Init
+.doBattleOrTrade
+ call CableClub_DoBattleOrTrade
+ ld hl, Club_GFX
+ ld a, h
+ ld [wTilesetGfxPtr + 1], a
+ ld a, l
+ ld [wTilesetGfxPtr], a
+ ld a, Bank(Club_GFX)
+ ld [wTilesetBank], a
+ ld hl, Club_Coll
+ ld a, h
+ ld [wTilesetCollisionPtr + 1], a
+ ld a, l
+ ld [wTilesetCollisionPtr], a
+ xor a
+ ld [wGrassRate], a
+ inc a ; LINK_STATE_IN_CABLE_CLUB
+ ld [wLinkState], a
+ ld [hJoy5], a
+ ld a, 10
+ ld [wAudioFadeOutControl], a
+ ld a, BANK(Music_Celadon)
+ ld [wAudioSavedROMBank], a
+ ld a, MUSIC_CELADON
+ ld [wNewSoundID], a
+ jp PlaySound
+
+EmptyFunc3:
+ ret
+
+Diploma_TextBoxBorder:
+ call GetPredefRegisters
+
+; b = height
+; c = width
+CableClub_TextBoxBorder:
+ push hl
+ ld a, $78 ; border upper left corner tile
+ ld [hli], a
+ inc a ; border top horizontal line tile
+ call CableClub_DrawHorizontalLine
+ inc a ; border upper right corner tile
+ ld [hl], a
+ pop hl
+ ld de, 20
+ add hl, de
+.loop
+ push hl
+ ld a, $7b ; border left vertical line tile
+ ld [hli], a
+ ld a, " "
+ call CableClub_DrawHorizontalLine
+ ld [hl], $77 ; border right vertical line tile
+ pop hl
+ ld de, 20
+ add hl, de
+ dec b
+ jr nz, .loop
+ ld a, $7c ; border lower left corner tile
+ ld [hli], a
+ ld a, $76 ; border bottom horizontal line tile
+ call CableClub_DrawHorizontalLine
+ ld [hl], $7d ; border lower right corner tile
+ ret
+
+; c = width
+CableClub_DrawHorizontalLine:
+ ld d, c
+.loop
+ ld [hli], a
+ dec d
+ jr nz, .loop
+ ret
+
+LoadTrainerInfoTextBoxTiles:
+ ld de, TrainerInfoTextBoxTileGraphics
+ ld hl, vChars2 + $760
+ lb bc, BANK(TrainerInfoTextBoxTileGraphics), (TrainerInfoTextBoxTileGraphicsEnd - TrainerInfoTextBoxTileGraphics) / $10
+ jp CopyVideoData
diff --git a/en/engine/clear_save.asm b/en/engine/clear_save.asm
new file mode 100755
index 00000000..b47cd6c4
--- /dev/null
+++ b/en/engine/clear_save.asm
@@ -0,0 +1,23 @@
+DoClearSaveDialogue:
+ call ClearScreen
+ call RunDefaultPaletteCommand
+ call LoadFontTilePatterns
+ call LoadTextBoxTilePatterns
+ ld hl, ClearSaveDataText
+ call PrintText
+ coord hl, 14, 7
+ lb bc, 8, 15
+ ld a, NO_YES_MENU
+ ld [wTwoOptionMenuID], a
+ ld a, TWO_OPTION_MENU
+ ld [wTextBoxID], a
+ call DisplayTextBoxID
+ ld a, [wCurrentMenuItem]
+ and a
+ jp z, Init
+ callba ClearSAV
+ jp Init
+
+ClearSaveDataText:
+ TX_FAR _ClearSaveDataText
+ db "@"
diff --git a/en/engine/evolve_trade.asm b/en/engine/evolve_trade.asm
new file mode 100755
index 00000000..e17fc05c
--- /dev/null
+++ b/en/engine/evolve_trade.asm
@@ -0,0 +1,44 @@
+EvolveTradeMon:
+; Verify the TradeMon's species name before
+; attempting to initiate a trade evolution.
+
+; The names of the trade evolutions in Blue (JP)
+; are checked. In that version, TradeMons that
+; can evolve are Graveler and Haunter.
+
+; In localization, this check was translated
+; before monster names were finalized.
+; Then, Haunter's name was "Spectre".
+; Since its name no longer starts with
+; "SP", it is prevented from evolving.
+
+; This may have been why Red/Green's trades
+; were used instead, where none can evolve.
+
+; This was fixed in Yellow.
+
+ ld a, [wInGameTradeReceiveMonName]
+
+ ; GRAVELER
+ cp "G"
+ jr z, .ok
+
+ ; "SPECTRE" (HAUNTER)
+ cp "S"
+ ret nz
+ ld a, [wInGameTradeReceiveMonName + 1]
+ cp "P"
+ ret nz
+
+.ok
+ ld a, [wPartyCount]
+ dec a
+ ld [wWhichPokemon], a
+ ld a, $1
+ ld [wForceEvolution], a
+ ld a, LINK_STATE_TRADING
+ ld [wLinkState], a
+ callab TryEvolvingMon
+ xor a ; LINK_STATE_NONE
+ ld [wLinkState], a
+ jp PlayDefaultMusic
diff --git a/en/engine/hall_of_fame.asm b/en/engine/hall_of_fame.asm
new file mode 100755
index 00000000..3c9b1723
--- /dev/null
+++ b/en/engine/hall_of_fame.asm
@@ -0,0 +1,288 @@
+AnimateHallOfFame:
+ call HoFFadeOutScreenAndMusic
+ call ClearScreen
+ ld c, 100
+ call DelayFrames
+ call LoadFontTilePatterns
+ call LoadTextBoxTilePatterns
+ call DisableLCD
+ ld hl, vBGMap0
+ ld bc, $800
+ ld a, " "
+ call FillMemory
+ call EnableLCD
+ ld hl, rLCDC
+ set 3, [hl]
+ xor a
+ ld hl, wHallOfFame
+ ld bc, HOF_TEAM
+ call FillMemory
+ xor a
+ ld [wUpdateSpritesEnabled], a
+ ld [hTilesetType], a
+ ld [wSpriteFlipped], a
+ ld [wLetterPrintingDelayFlags], a ; no delay
+ ld [wHoFMonOrPlayer], a ; mon
+ inc a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ld hl, wNumHoFTeams
+ ld a, [hl]
+ inc a
+ jr z, .skipInc ; don't wrap around to 0
+ inc [hl]
+.skipInc
+ ld a, $90
+ ld [hWY], a
+ ld c, BANK(Music_HallOfFame)
+ ld a, MUSIC_HALL_OF_FAME
+ call PlayMusic
+ ld hl, wPartySpecies
+ ld c, $ff
+.partyMonLoop
+ ld a, [hli]
+ cp $ff
+ jr z, .doneShowingParty
+ inc c
+ push hl
+ push bc
+ ld [wHoFMonSpecies], a
+ ld a, c
+ ld [wHoFPartyMonIndex], a
+ ld hl, wPartyMon1Level
+ ld bc, wPartyMon2 - wPartyMon1
+ call AddNTimes
+ ld a, [hl]
+ ld [wHoFMonLevel], a
+ call HoFShowMonOrPlayer
+ call HoFDisplayAndRecordMonInfo
+ ld c, 80
+ call DelayFrames
+ coord hl, 2, 13
+ ld b, 3
+ ld c, 14
+ call TextBoxBorder
+ coord hl, 4, 15
+ ld de, HallOfFameText
+ call PlaceString
+ ld c, 180
+ call DelayFrames
+ call GBFadeOutToWhite
+ pop bc
+ pop hl
+ jr .partyMonLoop
+.doneShowingParty
+ ld a, c
+ inc a
+ ld hl, wHallOfFame
+ ld bc, HOF_MON
+ call AddNTimes
+ ld [hl], $ff
+ call SaveHallOfFameTeams
+ xor a
+ ld [wHoFMonSpecies], a
+ inc a
+ ld [wHoFMonOrPlayer], a ; player
+ call HoFShowMonOrPlayer
+ call HoFDisplayPlayerStats
+ call HoFFadeOutScreenAndMusic
+ xor a
+ ld [hWY], a
+ ld hl, rLCDC
+ res 3, [hl]
+ ret
+
+HallOfFameText:
+ db "HALL OF FAME@"
+
+HoFShowMonOrPlayer:
+ call ClearScreen
+ ld a, $d0
+ ld [hSCY], a
+ ld a, $c0
+ ld [hSCX], a
+ ld a, [wHoFMonSpecies]
+ ld [wcf91], a
+ ld [wd0b5], a
+ ld [wBattleMonSpecies2], a
+ ld [wWholeScreenPaletteMonSpecies], a
+ ld a, [wHoFMonOrPlayer]
+ and a
+ jr z, .showMon
+; show player
+ call HoFLoadPlayerPics
+ jr .next1
+.showMon
+ coord hl, 12, 5
+ call GetMonHeader
+ call LoadFrontSpriteByMonIndex
+ predef LoadMonBackPic
+.next1
+ ld b, SET_PAL_POKEMON_WHOLE_SCREEN
+ ld c, 0
+ call RunPaletteCommand
+ ld a, %11100100
+ ld [rBGP], a
+ ld c, $31 ; back pic
+ call HoFLoadMonPlayerPicTileIDs
+ ld d, $a0
+ ld e, 4
+ ld a, [wOnSGB]
+ and a
+ jr z, .next2
+ sla e ; scroll more slowly on SGB
+.next2
+ call .ScrollPic ; scroll back pic left
+ xor a
+ ld [hSCY], a
+ ld c, a ; front pic
+ call HoFLoadMonPlayerPicTileIDs
+ ld d, 0
+ ld e, -4
+; scroll front pic right
+
+.ScrollPic
+ call DelayFrame
+ ld a, [hSCX]
+ add e
+ ld [hSCX], a
+ cp d
+ jr nz, .ScrollPic
+ ret
+
+HoFDisplayAndRecordMonInfo:
+ ld a, [wHoFPartyMonIndex]
+ ld hl, wPartyMonNicks
+ call GetPartyMonName
+ call HoFDisplayMonInfo
+ jp HoFRecordMonInfo
+
+HoFDisplayMonInfo:
+ coord hl, 0, 2
+ ld b, 9
+ ld c, 10
+ call TextBoxBorder
+ coord hl, 2, 6
+ ld de, HoFMonInfoText
+ call PlaceString
+ coord hl, 1, 4
+ ld de, wcd6d
+ call PlaceString
+ ld a, [wHoFMonLevel]
+ coord hl, 8, 7
+ call PrintLevelCommon
+ ld a, [wHoFMonSpecies]
+ ld [wd0b5], a
+ coord hl, 3, 9
+ predef PrintMonType
+ ld a, [wHoFMonSpecies]
+ jp PlayCry
+
+HoFMonInfoText:
+ db "LEVEL/"
+ next "TYPE1/"
+ next "TYPE2/@"
+
+HoFLoadPlayerPics:
+ ld de, RedPicFront
+ ld a, BANK(RedPicFront)
+ call UncompressSpriteFromDE
+ ld hl, sSpriteBuffer1
+ ld de, sSpriteBuffer0
+ ld bc, $310
+ call CopyData
+ ld de, vFrontPic
+ call InterlaceMergeSpriteBuffers
+ ld de, RedPicBack
+ ld a, BANK(RedPicBack)
+ call UncompressSpriteFromDE
+ predef ScaleSpriteByTwo
+ ld de, vBackPic
+ call InterlaceMergeSpriteBuffers
+ ld c, $1
+
+HoFLoadMonPlayerPicTileIDs:
+; c = base tile ID
+ ld b, 0
+ coord hl, 12, 5
+ predef_jump CopyTileIDsFromList
+
+HoFDisplayPlayerStats:
+ SetEvent EVENT_HALL_OF_FAME_DEX_RATING
+ predef DisplayDexRating
+ coord hl, 0, 4
+ ld b, 6
+ ld c, 10
+ call TextBoxBorder
+ coord hl, 5, 0
+ ld b, 2
+ ld c, 9
+ call TextBoxBorder
+ coord hl, 7, 2
+ ld de, wPlayerName
+ call PlaceString
+ coord hl, 1, 6
+ ld de, HoFPlayTimeText
+ call PlaceString
+ coord hl, 5, 7
+ ld de, wPlayTimeHours
+ lb bc, 1, 3
+ call PrintNumber
+ ld [hl], $6d
+ inc hl
+ ld de, wPlayTimeMinutes
+ lb bc, LEADING_ZEROES | 1, 2
+ call PrintNumber
+ coord hl, 1, 9
+ ld de, HoFMoneyText
+ call PlaceString
+ coord hl, 4, 10
+ ld de, wPlayerMoney
+ ld c, $a3
+ call PrintBCDNumber
+ ld hl, DexSeenOwnedText
+ call HoFPrintTextAndDelay
+ ld hl, DexRatingText
+ call HoFPrintTextAndDelay
+ ld hl, wDexRatingText
+
+HoFPrintTextAndDelay:
+ call PrintText
+ ld c, 120
+ jp DelayFrames
+
+HoFPlayTimeText:
+ db "PLAY TIME@"
+
+HoFMoneyText:
+ db "MONEY@"
+
+DexSeenOwnedText:
+ TX_FAR _DexSeenOwnedText
+ db "@"
+
+DexRatingText:
+ TX_FAR _DexRatingText
+ db "@"
+
+HoFRecordMonInfo:
+ ld hl, wHallOfFame
+ ld bc, HOF_MON
+ ld a, [wHoFPartyMonIndex]
+ call AddNTimes
+ ld a, [wHoFMonSpecies]
+ ld [hli], a
+ ld a, [wHoFMonLevel]
+ ld [hli], a
+ ld e, l
+ ld d, h
+ ld hl, wcd6d
+ ld bc, NAME_LENGTH
+ jp CopyData
+
+HoFFadeOutScreenAndMusic:
+ ld a, 10
+ ld [wAudioFadeOutCounterReloadValue], a
+ ld [wAudioFadeOutCounter], a
+ ld a, $ff
+ ld [wAudioFadeOutControl], a
+ jp GBFadeOutToWhite
diff --git a/en/engine/hidden_object_functions17.asm b/en/engine/hidden_object_functions17.asm
new file mode 100755
index 00000000..c1fa5aa4
--- /dev/null
+++ b/en/engine/hidden_object_functions17.asm
@@ -0,0 +1,475 @@
+PrintRedSNESText:
+ call EnableAutoTextBoxDrawing
+ tx_pre_jump RedBedroomSNESText
+
+RedBedroomSNESText:
+ TX_FAR _RedBedroomSNESText
+ db "@"
+
+OpenRedsPC:
+ call EnableAutoTextBoxDrawing
+ tx_pre_jump RedBedroomPCText
+
+RedBedroomPCText:
+ TX_PLAYERS_PC
+
+Route15GateLeftBinoculars:
+ ld a, [wSpriteStateData1 + 9]
+ cp SPRITE_FACING_UP
+ ret nz
+ call EnableAutoTextBoxDrawing
+ tx_pre Route15UpstairsBinocularsText
+ ld a, ARTICUNO
+ ld [wcf91], a
+ call PlayCry
+ jp DisplayMonFrontSpriteInBox
+
+Route15UpstairsBinocularsText:
+ TX_FAR _Route15UpstairsBinocularsText
+ db "@"
+
+AerodactylFossil:
+ ld a, FOSSIL_AERODACTYL
+ ld [wcf91], a
+ call DisplayMonFrontSpriteInBox
+ call EnableAutoTextBoxDrawing
+ tx_pre AerodactylFossilText
+ ret
+
+AerodactylFossilText:
+ TX_FAR _AerodactylFossilText
+ db "@"
+
+KabutopsFossil:
+ ld a, FOSSIL_KABUTOPS
+ ld [wcf91], a
+ call DisplayMonFrontSpriteInBox
+ call EnableAutoTextBoxDrawing
+ tx_pre KabutopsFossilText
+ ret
+
+KabutopsFossilText:
+ TX_FAR _KabutopsFossilText
+ db "@"
+
+DisplayMonFrontSpriteInBox:
+; Displays a pokemon's front sprite in a pop-up window.
+; [wcf91] = pokemon internal id number
+ ld a, 1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call Delay3
+ xor a
+ ld [hWY], a
+ call SaveScreenTilesToBuffer1
+ ld a, MON_SPRITE_POPUP
+ ld [wTextBoxID], a
+ call DisplayTextBoxID
+ call UpdateSprites
+ ld a, [wcf91]
+ ld [wd0b5], a
+ call GetMonHeader
+ ld de, vChars1 + $310
+ call LoadMonFrontSprite
+ ld a, $80
+ ld [hStartTileID], a
+ coord hl, 10, 11
+ predef AnimateSendingOutMon
+ call WaitForTextScrollButtonPress
+ call LoadScreenTilesFromBuffer1
+ call Delay3
+ ld a, $90
+ ld [hWY], a
+ ret
+
+PrintBlackboardLinkCableText:
+ call EnableAutoTextBoxDrawing
+ ld a, $1
+ ld [wDoNotWaitForButtonPressAfterDisplayingText], a
+ ld a, [wHiddenObjectFunctionArgument]
+ call PrintPredefTextID
+ ret
+
+LinkCableHelp:
+ TX_ASM
+ call SaveScreenTilesToBuffer1
+ ld hl, LinkCableHelpText1
+ call PrintText
+ xor a
+ ld [wMenuItemOffset], a ; not used
+ ld [wCurrentMenuItem], a
+ ld [wLastMenuItem], a
+ ld a, A_BUTTON | B_BUTTON
+ ld [wMenuWatchedKeys], a
+ ld a, 3
+ ld [wMaxMenuItem], a
+ ld a, 2
+ ld [wTopMenuItemY], a
+ ld a, 1
+ ld [wTopMenuItemX], a
+.linkHelpLoop
+ ld hl, wd730
+ set 6, [hl]
+ coord hl, 0, 0
+ ld b, 8
+ ld c, 13
+ call TextBoxBorder
+ coord hl, 2, 2
+ ld de, HowToLinkText
+ call PlaceString
+ ld hl, LinkCableHelpText2
+ call PrintText
+ call HandleMenuInput
+ bit 1, a ; pressed b
+ jr nz, .exit
+ ld a, [wCurrentMenuItem]
+ cp 3 ; pressed a on "STOP READING"
+ jr z, .exit
+ ld hl, wd730
+ res 6, [hl]
+ ld hl, LinkCableInfoTexts
+ add a
+ ld d, 0
+ ld e, a
+ add hl, de
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ call PrintText
+ jp .linkHelpLoop
+.exit
+ ld hl, wd730
+ res 6, [hl]
+ call LoadScreenTilesFromBuffer1
+ jp TextScriptEnd
+
+LinkCableHelpText1:
+ TX_FAR _LinkCableHelpText1
+ db "@"
+
+LinkCableHelpText2:
+ TX_FAR _LinkCableHelpText2
+ db "@"
+
+HowToLinkText:
+ db "HOW TO LINK"
+ next "COLOSSEUM"
+ next "TRADE CENTER"
+ next "STOP READING@"
+
+LinkCableInfoTexts:
+ dw LinkCableInfoText1
+ dw LinkCableInfoText2
+ dw LinkCableInfoText3
+
+LinkCableInfoText1:
+ TX_FAR _LinkCableInfoText1
+ db "@"
+
+LinkCableInfoText2:
+ TX_FAR _LinkCableInfoText2
+ db "@"
+
+LinkCableInfoText3:
+ TX_FAR _LinkCableInfoText3
+ db "@"
+
+ViridianSchoolBlackboard:
+ TX_ASM
+ call SaveScreenTilesToBuffer1
+ ld hl, ViridianSchoolBlackboardText1
+ call PrintText
+ xor a
+ ld [wMenuItemOffset], a
+ ld [wCurrentMenuItem], a
+ ld [wLastMenuItem], a
+ ld a, D_LEFT | D_RIGHT | A_BUTTON | B_BUTTON
+ ld [wMenuWatchedKeys], a
+ ld a, 2
+ ld [wMaxMenuItem], a
+ ld a, 2
+ ld [wTopMenuItemY], a
+ ld a, 1
+ ld [wTopMenuItemX], a
+.blackboardLoop
+ ld hl, wd730
+ set 6, [hl]
+ coord hl, 0, 0
+ lb bc, 6, 10
+ call TextBoxBorder
+ coord hl, 1, 2
+ ld de, StatusAilmentText1
+ call PlaceString
+ coord hl, 6, 2
+ ld de, StatusAilmentText2
+ call PlaceString
+ ld hl, ViridianSchoolBlackboardText2
+ call PrintText
+ call HandleMenuInput ; pressing up and down is handled in here
+ bit 1, a ; pressed b
+ jr nz, .exitBlackboard
+ bit 4, a ; pressed right
+ jr z, .didNotPressRight
+ ; move cursor to right column
+ ld a, 2
+ ld [wMaxMenuItem], a
+ ld a, 2
+ ld [wTopMenuItemY], a
+ ld a, 6
+ ld [wTopMenuItemX], a
+ ld a, 3 ; in the the right column, use an offset to prevent overlap
+ ld [wMenuItemOffset], a
+ jr .blackboardLoop
+.didNotPressRight
+ bit 5, a ; pressed left
+ jr z, .didNotPressLeftOrRight
+ ; move cursor to left column
+ ld a, 2
+ ld [wMaxMenuItem], a
+ ld a, 2
+ ld [wTopMenuItemY], a
+ ld a, 1
+ ld [wTopMenuItemX], a
+ xor a
+ ld [wMenuItemOffset], a
+ jr .blackboardLoop
+.didNotPressLeftOrRight
+ ld a, [wCurrentMenuItem]
+ ld b, a
+ ld a, [wMenuItemOffset]
+ add b
+ cp 5 ; cursor is pointing to "QUIT"
+ jr z, .exitBlackboard
+ ; we must have pressed a on a status condition
+ ; so print the text
+ ld hl, wd730
+ res 6, [hl]
+ ld hl, ViridianBlackboardStatusPointers
+ add a
+ ld d, 0
+ ld e, a
+ add hl, de
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ call PrintText
+ jp .blackboardLoop
+.exitBlackboard
+ ld hl, wd730
+ res 6, [hl]
+ call LoadScreenTilesFromBuffer1
+ jp TextScriptEnd
+
+ViridianSchoolBlackboardText1:
+ TX_FAR _ViridianSchoolBlackboardText1
+ db "@"
+
+ViridianSchoolBlackboardText2:
+ TX_FAR _ViridianSchoolBlackboardText2
+ db "@"
+
+StatusAilmentText1:
+ db " SLP"
+ next " PSN"
+ next " PAR@"
+
+StatusAilmentText2:
+ db " BRN"
+ next " FRZ"
+ next " QUIT@@"
+
+ViridianBlackboardStatusPointers:
+ dw ViridianBlackboardSleepText
+ dw ViridianBlackboardPoisonText
+ dw ViridianBlackboardPrlzText
+ dw ViridianBlackboardBurnText
+ dw ViridianBlackboardFrozenText
+
+ViridianBlackboardSleepText:
+ TX_FAR _ViridianBlackboardSleepText
+ db "@"
+
+ViridianBlackboardPoisonText:
+ TX_FAR _ViridianBlackboardPoisonText
+ db "@"
+
+ViridianBlackboardPrlzText:
+ TX_FAR _ViridianBlackboardPrlzText
+ db "@"
+
+ViridianBlackboardBurnText:
+ TX_FAR _ViridianBlackboardBurnText
+ db "@"
+
+ViridianBlackboardFrozenText:
+ TX_FAR _ViridianBlackboardFrozenText
+ db "@"
+
+PrintTrashText:
+ call EnableAutoTextBoxDrawing
+ tx_pre_jump VermilionGymTrashText
+
+VermilionGymTrashText:
+ TX_FAR _VermilionGymTrashText
+ db "@"
+
+GymTrashScript:
+ call EnableAutoTextBoxDrawing
+ ld a, [wHiddenObjectFunctionArgument]
+ ld [wGymTrashCanIndex], a
+
+; Don't do the trash can puzzle if it's already been done.
+ CheckEvent EVENT_2ND_LOCK_OPENED
+ jr z, .ok
+
+ tx_pre_jump VermilionGymTrashText
+
+.ok
+ CheckEventReuseA EVENT_1ST_LOCK_OPENED
+ jr nz, .trySecondLock
+
+ ld a, [wFirstLockTrashCanIndex]
+ ld b, a
+ ld a, [wGymTrashCanIndex]
+ cp b
+ jr z, .openFirstLock
+
+ tx_pre_id VermilionGymTrashText
+ jr .done
+
+.openFirstLock
+; Next can is trying for the second switch.
+ SetEvent EVENT_1ST_LOCK_OPENED
+
+ ld hl, GymTrashCans
+ ld a, [wGymTrashCanIndex]
+ ; * 5
+ ld b, a
+ add a
+ add a
+ add b
+
+ ld d, 0
+ ld e, a
+ add hl, de
+ ld a, [hli]
+
+; There is a bug in this code. It should calculate a value in the range [0, 3]
+; but if the mask and random number don't have any 1 bits in common, then
+; the result of the AND will be 0. When 1 is subtracted from that, the value
+; will become $ff. This will result in 255 being added to hl, which will cause
+; hl to point to one of the zero bytes that pad the end of the ROM bank.
+; Trash can 0 was intended to be able to have the second lock only when the
+; first lock was in trash can 1 or 3. However, due to this bug, trash can 0 can
+; have the second lock regardless of which trash can had the first lock.
+
+ ld [hGymTrashCanRandNumMask], a
+ push hl
+ call Random
+ swap a
+ ld b, a
+ ld a, [hGymTrashCanRandNumMask]
+ and b
+ dec a
+ pop hl
+
+ ld d, 0
+ ld e, a
+ add hl, de
+ ld a, [hl]
+ and $f
+ ld [wSecondLockTrashCanIndex], a
+
+ tx_pre_id VermilionGymTrashSuccessText1
+ jr .done
+
+.trySecondLock
+ ld a, [wSecondLockTrashCanIndex]
+ ld b, a
+ ld a, [wGymTrashCanIndex]
+ cp b
+ jr z, .openSecondLock
+
+; Reset the cans.
+ ResetEvent EVENT_1ST_LOCK_OPENED
+ call Random
+
+ and $e
+ ld [wFirstLockTrashCanIndex], a
+
+ tx_pre_id VermilionGymTrashFailText
+ jr .done
+
+.openSecondLock
+; Completed the trash can puzzle.
+ SetEvent EVENT_2ND_LOCK_OPENED
+ ld hl, wCurrentMapScriptFlags
+ set 6, [hl]
+
+ tx_pre_id VermilionGymTrashSuccessText3
+
+.done
+ jp PrintPredefTextID
+
+GymTrashCans:
+; byte 0: mask for random number
+; bytes 1-4: indices of the trash cans that can have the second lock
+; (but see the comment above explaining a bug regarding this)
+; Note that the mask is simply the number of valid trash can indices that
+; follow. The remaining bytes are filled with 0 to pad the length of each entry
+; to 5 bytes.
+ db 2, 1, 3, 0, 0 ; 0
+ db 3, 0, 2, 4, 0 ; 1
+ db 2, 1, 5, 0, 0 ; 2
+ db 3, 0, 4, 6, 0 ; 3
+ db 4, 1, 3, 5, 7 ; 4
+ db 3, 2, 4, 8, 0 ; 5
+ db 3, 3, 7, 9, 0 ; 6
+ db 4, 4, 6, 8, 10 ; 7
+ db 3, 5, 7, 11, 0 ; 8
+ db 3, 6, 10, 12, 0 ; 9
+ db 4, 7, 9, 11, 13 ; 10
+ db 3, 8, 10, 14, 0 ; 11
+ db 2, 9, 13, 0, 0 ; 12
+ db 3, 10, 12, 14, 0 ; 13
+ db 2, 11, 13, 0, 0 ; 14
+
+VermilionGymTrashSuccessText1:
+ TX_FAR _VermilionGymTrashSuccessText1
+ TX_ASM
+ call WaitForSoundToFinish
+ ld a, SFX_SWITCH
+ call PlaySound
+ call WaitForSoundToFinish
+ jp TextScriptEnd
+
+; unused
+VermilionGymTrashSuccessText2:
+ TX_FAR _VermilionGymTrashSuccessText2
+ db "@"
+
+; unused
+VermilionGymTrashSuccesPlaySfx:
+ TX_ASM
+ call WaitForSoundToFinish
+ ld a, SFX_SWITCH
+ call PlaySound
+ call WaitForSoundToFinish
+ jp TextScriptEnd
+
+VermilionGymTrashSuccessText3:
+ TX_FAR _VermilionGymTrashSuccessText3
+ TX_ASM
+ call WaitForSoundToFinish
+ ld a, SFX_GO_INSIDE
+ call PlaySound
+ call WaitForSoundToFinish
+ jp TextScriptEnd
+
+VermilionGymTrashFailText:
+ TX_FAR _VermilionGymTrashFailText
+ TX_ASM
+ call WaitForSoundToFinish
+ ld a, SFX_DENIED
+ call PlaySound
+ call WaitForSoundToFinish
+ jp TextScriptEnd
diff --git a/en/engine/hidden_object_functions7.asm b/en/engine/hidden_object_functions7.asm
new file mode 100755
index 00000000..f04c1deb
--- /dev/null
+++ b/en/engine/hidden_object_functions7.asm
@@ -0,0 +1,467 @@
+PrintNewBikeText:
+ call EnableAutoTextBoxDrawing
+ tx_pre_jump NewBicycleText
+
+NewBicycleText:
+ TX_FAR _NewBicycleText
+ db "@"
+
+DisplayOakLabLeftPoster:
+ call EnableAutoTextBoxDrawing
+ tx_pre_jump PushStartText
+
+PushStartText:
+ TX_FAR _PushStartText
+ db "@"
+
+DisplayOakLabRightPoster:
+ call EnableAutoTextBoxDrawing
+ ld hl, wPokedexOwned
+ ld b, wPokedexOwnedEnd - wPokedexOwned
+ call CountSetBits
+ ld a, [wNumSetBits]
+ cp 2
+ tx_pre_id SaveOptionText
+ jr c, .ownLessThanTwo
+ ; own two or more mon
+ tx_pre_id StrengthsAndWeaknessesText
+.ownLessThanTwo
+ jp PrintPredefTextID
+
+SaveOptionText:
+ TX_FAR _SaveOptionText
+ db "@"
+
+StrengthsAndWeaknessesText:
+ TX_FAR _StrengthsAndWeaknessesText
+ db "@"
+
+SafariZoneCheck:
+ CheckEventHL EVENT_IN_SAFARI_ZONE ; if we are not in the Safari Zone,
+ jr z, SafariZoneGameStillGoing ; don't bother printing game over text
+ ld a, [wNumSafariBalls]
+ and a
+ jr z, SafariZoneGameOver
+ jr SafariZoneGameStillGoing
+
+SafariZoneCheckSteps:
+ ld a, [wSafariSteps]
+ ld b, a
+ ld a, [wSafariSteps + 1]
+ ld c, a
+ or b
+ jr z, SafariZoneGameOver
+ dec bc
+ ld a, b
+ ld [wSafariSteps], a
+ ld a, c
+ ld [wSafariSteps + 1], a
+SafariZoneGameStillGoing:
+ xor a
+ ld [wSafariZoneGameOver], a
+ ret
+
+SafariZoneGameOver:
+ call EnableAutoTextBoxDrawing
+ xor a
+ ld [wAudioFadeOutControl], a
+ dec a
+ call PlaySound
+ ld c, BANK(SFX_Safari_Zone_PA)
+ ld a, SFX_SAFARI_ZONE_PA
+ call PlayMusic
+.waitForMusicToPlay
+ ld a, [wChannelSoundIDs + Ch4]
+ cp SFX_SAFARI_ZONE_PA
+ jr nz, .waitForMusicToPlay
+ ld a, TEXT_SAFARI_GAME_OVER
+ ld [hSpriteIndexOrTextID], a
+ call DisplayTextID
+ xor a
+ ld [wPlayerMovingDirection], a
+ ld a, SAFARI_ZONE_ENTRANCE
+ ld [hWarpDestinationMap], a
+ ld a, $3
+ ld [wDestinationWarpID], a
+ ld a, $5
+ ld [wSafariZoneEntranceCurScript], a
+ SetEvent EVENT_SAFARI_GAME_OVER
+ ld a, 1
+ ld [wSafariZoneGameOver], a
+ ret
+
+PrintSafariGameOverText:
+ xor a
+ ld [wJoyIgnore], a
+ ld hl, SafariGameOverText
+ jp PrintText
+
+SafariGameOverText:
+ TX_ASM
+ ld a, [wNumSafariBalls]
+ and a
+ jr z, .noMoreSafariBalls
+ ld hl, TimesUpText
+ call PrintText
+.noMoreSafariBalls
+ ld hl, GameOverText
+ call PrintText
+ jp TextScriptEnd
+
+TimesUpText:
+ TX_FAR _TimesUpText
+ db "@"
+
+GameOverText:
+ TX_FAR _GameOverText
+ db "@"
+
+PrintCinnabarQuiz:
+ ld a, [wSpriteStateData1 + 9]
+ cp SPRITE_FACING_UP
+ ret nz
+ call EnableAutoTextBoxDrawing
+ tx_pre_jump CinnabarGymQuiz
+
+CinnabarGymQuiz:
+ TX_ASM
+ xor a
+ ld [wOpponentAfterWrongAnswer], a
+ ld a, [wHiddenObjectFunctionArgument]
+ push af
+ and $f
+ ld [hGymGateIndex], a
+ pop af
+ and $f0
+ swap a
+ ld [$ffdc], a
+ ld hl, CinnabarGymQuizIntroText
+ call PrintText
+ ld a, [hGymGateIndex]
+ dec a
+ add a
+ ld d, 0
+ ld e, a
+ ld hl, CinnabarQuizQuestions
+ add hl, de
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ call PrintText
+ ld a, 1
+ ld [wDoNotWaitForButtonPressAfterDisplayingText], a
+ call CinnabarGymQuiz_1ea92
+ jp TextScriptEnd
+
+CinnabarGymQuizIntroText:
+ TX_FAR _CinnabarGymQuizIntroText
+ db "@"
+
+CinnabarQuizQuestions:
+ dw CinnabarQuizQuestionsText1
+ dw CinnabarQuizQuestionsText2
+ dw CinnabarQuizQuestionsText3
+ dw CinnabarQuizQuestionsText4
+ dw CinnabarQuizQuestionsText5
+ dw CinnabarQuizQuestionsText6
+
+CinnabarQuizQuestionsText1:
+ TX_FAR _CinnabarQuizQuestionsText1
+ db "@"
+
+CinnabarQuizQuestionsText2:
+ TX_FAR _CinnabarQuizQuestionsText2
+ db "@"
+
+CinnabarQuizQuestionsText3:
+ TX_FAR _CinnabarQuizQuestionsText3
+ db "@"
+
+CinnabarQuizQuestionsText4:
+ TX_FAR _CinnabarQuizQuestionsText4
+ db "@"
+
+CinnabarQuizQuestionsText5:
+ TX_FAR _CinnabarQuizQuestionsText5
+ db "@"
+
+CinnabarQuizQuestionsText6:
+ TX_FAR _CinnabarQuizQuestionsText6
+ db "@"
+
+CinnabarGymGateFlagAction:
+ EventFlagAddress hl, EVENT_CINNABAR_GYM_GATE0_UNLOCKED
+ predef_jump FlagActionPredef
+
+CinnabarGymQuiz_1ea92:
+ call YesNoChoice
+ ld a, [$ffdc]
+ ld c, a
+ ld a, [wCurrentMenuItem]
+ cp c
+ jr nz, .wrongAnswer
+ ld hl, wCurrentMapScriptFlags
+ set 5, [hl]
+ ld a, [hGymGateIndex]
+ ld [$ffe0], a
+ ld hl, CinnabarGymQuizCorrectText
+ call PrintText
+ ld a, [$ffe0]
+ AdjustEventBit EVENT_CINNABAR_GYM_GATE0_UNLOCKED, 0
+ ld c, a
+ ld b, FLAG_SET
+ call CinnabarGymGateFlagAction
+ jp UpdateCinnabarGymGateTileBlocks_
+.wrongAnswer
+ call WaitForSoundToFinish
+ ld a, SFX_DENIED
+ call PlaySound
+ call WaitForSoundToFinish
+ ld hl, CinnabarGymQuizIncorrectText
+ call PrintText
+ ld a, [hGymGateIndex]
+ add $2
+ AdjustEventBit EVENT_BEAT_CINNABAR_GYM_TRAINER_0, 2
+ ld c, a
+ ld b, FLAG_TEST
+ EventFlagAddress hl, EVENT_BEAT_CINNABAR_GYM_TRAINER_0
+ predef FlagActionPredef
+ ld a, c
+ and a
+ ret nz
+ ld a, [hGymGateIndex]
+ add $2
+ ld [wOpponentAfterWrongAnswer], a
+ ret
+
+CinnabarGymQuizCorrectText:
+ TX_SFX_ITEM_1
+ TX_FAR _CinnabarGymQuizCorrectText
+ TX_BLINK
+ TX_ASM
+
+ ld a, [$ffe0]
+ AdjustEventBit EVENT_CINNABAR_GYM_GATE0_UNLOCKED, 0
+ ld c, a
+ ld b, FLAG_TEST
+ call CinnabarGymGateFlagAction
+ ld a, c
+ and a
+ jp nz, TextScriptEnd
+ call WaitForSoundToFinish
+ ld a, SFX_GO_INSIDE
+ call PlaySound
+ call WaitForSoundToFinish
+ jp TextScriptEnd
+
+CinnabarGymQuizIncorrectText:
+ TX_FAR _CinnabarGymQuizIncorrectText
+ db "@"
+
+UpdateCinnabarGymGateTileBlocks_:
+; Update the overworld map with open floor blocks or locked gate blocks
+; depending on event flags.
+ ld a, 6
+ ld [hGymGateIndex], a
+.loop
+ ld a, [hGymGateIndex]
+ dec a
+ add a
+ add a
+ ld d, 0
+ ld e, a
+ ld hl, CinnabarGymGateCoords
+ add hl, de
+ ld a, [hli]
+ ld b, [hl]
+ ld c, a
+ inc hl
+ ld a, [hl]
+ ld [wGymGateTileBlock], a
+ push bc
+ ld a, [hGymGateIndex]
+ ld [$ffe0], a
+ AdjustEventBit EVENT_CINNABAR_GYM_GATE0_UNLOCKED, 0
+ ld c, a
+ ld b, FLAG_TEST
+ call CinnabarGymGateFlagAction
+ ld a, c
+ and a
+ jr nz, .unlocked
+ ld a, [wGymGateTileBlock]
+ jr .next
+.unlocked
+ ld a, $e
+.next
+ pop bc
+ ld [wNewTileBlockID], a
+ predef ReplaceTileBlock
+ ld hl, hGymGateIndex
+ dec [hl]
+ jr nz, .loop
+ ret
+
+CinnabarGymGateCoords:
+ ; format: x-coord, y-coord, direction, padding
+ ; direction: $54 = horizontal gate, $5f = vertical gate
+ db $09,$03,$54,$00
+ db $06,$03,$54,$00
+ db $06,$06,$54,$00
+ db $03,$08,$5f,$00
+ db $02,$06,$54,$00
+ db $02,$03,$54,$00
+
+PrintMagazinesText:
+ call EnableAutoTextBoxDrawing
+ tx_pre MagazinesText
+ ret
+
+MagazinesText:
+ TX_FAR _MagazinesText
+ db "@"
+
+BillsHousePC:
+ call EnableAutoTextBoxDrawing
+ ld a, [wSpriteStateData1 + 9]
+ cp SPRITE_FACING_UP
+ ret nz
+ CheckEvent EVENT_LEFT_BILLS_HOUSE_AFTER_HELPING
+ jr nz, .displayBillsHousePokemonList
+ CheckEventReuseA EVENT_USED_CELL_SEPARATOR_ON_BILL
+ jr nz, .displayBillsHouseMonitorText
+ CheckEventReuseA EVENT_BILL_SAID_USE_CELL_SEPARATOR
+ jr nz, .doCellSeparator
+.displayBillsHouseMonitorText
+ tx_pre_jump BillsHouseMonitorText
+.doCellSeparator
+ ld a, $1
+ ld [wDoNotWaitForButtonPressAfterDisplayingText], a
+ tx_pre BillsHouseInitiatedText
+ ld c, 32
+ call DelayFrames
+ ld a, SFX_TINK
+ call PlaySound
+ call WaitForSoundToFinish
+ ld c, 80
+ call DelayFrames
+ ld a, SFX_SHRINK
+ call PlaySound
+ call WaitForSoundToFinish
+ ld c, 48
+ call DelayFrames
+ ld a, SFX_TINK
+ call PlaySound
+ call WaitForSoundToFinish
+ ld c, 32
+ call DelayFrames
+ ld a, SFX_GET_ITEM_1
+ call PlaySound
+ call WaitForSoundToFinish
+ call PlayDefaultMusic
+ SetEvent EVENT_USED_CELL_SEPARATOR_ON_BILL
+ ret
+.displayBillsHousePokemonList
+ ld a, $1
+ ld [wDoNotWaitForButtonPressAfterDisplayingText], a
+ tx_pre BillsHousePokemonList
+ ret
+
+BillsHouseMonitorText:
+ TX_FAR _BillsHouseMonitorText
+ db "@"
+
+BillsHouseInitiatedText:
+ TX_FAR _BillsHouseInitiatedText
+ TX_BLINK
+ TX_ASM
+ ld a, $ff
+ ld [wNewSoundID], a
+ call PlaySound
+ ld c, 16
+ call DelayFrames
+ ld a, SFX_SWITCH
+ call PlaySound
+ call WaitForSoundToFinish
+ ld c, 60
+ call DelayFrames
+ jp TextScriptEnd
+
+BillsHousePokemonList:
+ TX_ASM
+ call SaveScreenTilesToBuffer1
+ ld hl, BillsHousePokemonListText1
+ call PrintText
+ xor a
+ ld [wMenuItemOffset], a ; not used
+ ld [wCurrentMenuItem], a
+ ld [wLastMenuItem], a
+ ld a, A_BUTTON | B_BUTTON
+ ld [wMenuWatchedKeys], a
+ ld a, 4
+ ld [wMaxMenuItem], a
+ ld a, 2
+ ld [wTopMenuItemY], a
+ ld a, 1
+ ld [wTopMenuItemX], a
+.billsPokemonLoop
+ ld hl, wd730
+ set 6, [hl]
+ coord hl, 0, 0
+ ld b, 10
+ ld c, 9
+ call TextBoxBorder
+ coord hl, 2, 2
+ ld de, BillsMonListText
+ call PlaceString
+ ld hl, BillsHousePokemonListText2
+ call PrintText
+ call SaveScreenTilesToBuffer2
+ call HandleMenuInput
+ bit 1, a ; pressed b
+ jr nz, .cancel
+ ld a, [wCurrentMenuItem]
+ add EEVEE
+ cp EEVEE
+ jr z, .displayPokedex
+ cp FLAREON
+ jr z, .displayPokedex
+ cp JOLTEON
+ jr z, .displayPokedex
+ cp VAPOREON
+ jr z, .displayPokedex
+ jr .cancel
+.displayPokedex
+ call DisplayPokedex
+ call LoadScreenTilesFromBuffer2
+ jr .billsPokemonLoop
+.cancel
+ ld hl, wd730
+ res 6, [hl]
+ call LoadScreenTilesFromBuffer2
+ jp TextScriptEnd
+
+BillsHousePokemonListText1:
+ TX_FAR _BillsHousePokemonListText1
+ db "@"
+
+BillsMonListText:
+ db "EEVEE"
+ next "FLAREON"
+ next "JOLTEON"
+ next "VAPOREON"
+ next "CANCEL@"
+
+BillsHousePokemonListText2:
+ TX_FAR _BillsHousePokemonListText2
+ db "@"
+
+DisplayOakLabEmailText:
+ ld a, [wSpriteStateData1 + 9]
+ cp SPRITE_FACING_UP
+ ret nz
+ call EnableAutoTextBoxDrawing
+ tx_pre_jump OakLabEmailText
+
+OakLabEmailText:
+ TX_FAR _OakLabEmailText
+ db "@"
diff --git a/en/engine/items/items.asm b/en/engine/items/items.asm
new file mode 100755
index 00000000..3bb2ec9e
--- /dev/null
+++ b/en/engine/items/items.asm
@@ -0,0 +1,2989 @@
+UseItem_:
+ ld a, 1
+ ld [wActionResultOrTookBattleTurn], a ; initialise to success value
+ ld a, [wcf91] ;contains item_ID
+ cp HM_01
+ jp nc, ItemUseTMHM
+ ld hl, ItemUsePtrTable
+ dec a
+ add a
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ jp hl
+
+ItemUsePtrTable:
+ dw ItemUseBall ; MASTER_BALL
+ dw ItemUseBall ; ULTRA_BALL
+ dw ItemUseBall ; GREAT_BALL
+ dw ItemUseBall ; POKE_BALL
+ dw ItemUseTownMap ; TOWN_MAP
+ dw ItemUseBicycle ; BICYCLE
+ dw ItemUseSurfboard ; out-of-battle Surf effect
+ dw ItemUseBall ; SAFARI_BALL
+ dw ItemUsePokedex ; POKEDEX
+ dw ItemUseEvoStone ; MOON_STONE
+ dw ItemUseMedicine ; ANTIDOTE
+ dw ItemUseMedicine ; BURN_HEAL
+ dw ItemUseMedicine ; ICE_HEAL
+ dw ItemUseMedicine ; AWAKENING
+ dw ItemUseMedicine ; PARLYZ_HEAL
+ dw ItemUseMedicine ; FULL_RESTORE
+ dw ItemUseMedicine ; MAX_POTION
+ dw ItemUseMedicine ; HYPER_POTION
+ dw ItemUseMedicine ; SUPER_POTION
+ dw ItemUseMedicine ; POTION
+ dw ItemUseBait ; BOULDERBADGE
+ dw ItemUseRock ; CASCADEBADGE
+ dw UnusableItem ; THUNDERBADGE
+ dw UnusableItem ; RAINBOWBADGE
+ dw UnusableItem ; SOULBADGE
+ dw UnusableItem ; MARSHBADGE
+ dw UnusableItem ; VOLCANOBADGE
+ dw UnusableItem ; EARTHBADGE
+ dw ItemUseEscapeRope ; ESCAPE_ROPE
+ dw ItemUseRepel ; REPEL
+ dw UnusableItem ; OLD_AMBER
+ dw ItemUseEvoStone ; FIRE_STONE
+ dw ItemUseEvoStone ; THUNDER_STONE
+ dw ItemUseEvoStone ; WATER_STONE
+ dw ItemUseVitamin ; HP_UP
+ dw ItemUseVitamin ; PROTEIN
+ dw ItemUseVitamin ; IRON
+ dw ItemUseVitamin ; CARBOS
+ dw ItemUseVitamin ; CALCIUM
+ dw ItemUseVitamin ; RARE_CANDY
+ dw UnusableItem ; DOME_FOSSIL
+ dw UnusableItem ; HELIX_FOSSIL
+ dw UnusableItem ; SECRET_KEY
+ dw UnusableItem
+ dw UnusableItem ; BIKE_VOUCHER
+ dw ItemUseXAccuracy ; X_ACCURACY
+ dw ItemUseEvoStone ; LEAF_STONE
+ dw ItemUseCardKey ; CARD_KEY
+ dw UnusableItem ; NUGGET
+ dw UnusableItem ; ??? PP_UP
+ dw ItemUsePokedoll ; POKE_DOLL
+ dw ItemUseMedicine ; FULL_HEAL
+ dw ItemUseMedicine ; REVIVE
+ dw ItemUseMedicine ; MAX_REVIVE
+ dw ItemUseGuardSpec ; GUARD_SPEC
+ dw ItemUseSuperRepel ; SUPER_REPL
+ dw ItemUseMaxRepel ; MAX_REPEL
+ dw ItemUseDireHit ; DIRE_HIT
+ dw UnusableItem ; COIN
+ dw ItemUseMedicine ; FRESH_WATER
+ dw ItemUseMedicine ; SODA_POP
+ dw ItemUseMedicine ; LEMONADE
+ dw UnusableItem ; S_S_TICKET
+ dw UnusableItem ; GOLD_TEETH
+ dw ItemUseXStat ; X_ATTACK
+ dw ItemUseXStat ; X_DEFEND
+ dw ItemUseXStat ; X_SPEED
+ dw ItemUseXStat ; X_SPECIAL
+ dw ItemUseCoinCase ; COIN_CASE
+ dw ItemUseOaksParcel ; OAKS_PARCEL
+ dw ItemUseItemfinder ; ITEMFINDER
+ dw UnusableItem ; SILPH_SCOPE
+ dw ItemUsePokeflute ; POKE_FLUTE
+ dw UnusableItem ; LIFT_KEY
+ dw UnusableItem ; EXP_ALL
+ dw ItemUseOldRod ; OLD_ROD
+ dw ItemUseGoodRod ; GOOD_ROD
+ dw ItemUseSuperRod ; SUPER_ROD
+ dw ItemUsePPUp ; PP_UP (real one)
+ dw ItemUsePPRestore ; ETHER
+ dw ItemUsePPRestore ; MAX_ETHER
+ dw ItemUsePPRestore ; ELIXER
+ dw ItemUsePPRestore ; MAX_ELIXER
+
+ItemUseBall:
+
+; Balls can't be used out of battle.
+ ld a, [wIsInBattle]
+ and a
+ jp z, ItemUseNotTime
+
+; Balls can't catch trainers' Pokémon.
+ dec a
+ jp nz, ThrowBallAtTrainerMon
+
+; If this is for the old man battle, skip checking if the party & box are full.
+ ld a, [wBattleType]
+ dec a
+ jr z, .canUseBall
+
+ ld a, [wPartyCount] ; is party full?
+ cp PARTY_LENGTH
+ jr nz, .canUseBall
+ ld a, [wNumInBox] ; is box full?
+ cp MONS_PER_BOX
+ jp z, BoxFullCannotThrowBall
+
+.canUseBall
+ xor a
+ ld [wCapturedMonSpecies], a
+
+ ld a, [wBattleType]
+ cp BATTLE_TYPE_SAFARI
+ jr nz, .skipSafariZoneCode
+
+.safariZone
+ ld hl, wNumSafariBalls
+ dec [hl] ; remove a Safari Ball
+
+.skipSafariZoneCode
+ call RunDefaultPaletteCommand
+
+ ld a, $43 ; successful capture value
+ ld [wPokeBallAnimData], a
+
+ call LoadScreenTilesFromBuffer1
+ ld hl, ItemUseText00
+ call PrintText
+
+; If the player is fighting an unidentified ghost, set the value that indicates
+; the Pokémon can't be caught and skip the capture calculations.
+ callab IsGhostBattle
+ ld b, $10 ; can't be caught value
+ jp z, .setAnimData
+
+ ld a, [wBattleType]
+ dec a
+ jr nz, .notOldManBattle
+
+.oldManBattle
+ ld hl, wGrassRate
+ ld de, wPlayerName
+ ld bc, NAME_LENGTH
+ call CopyData ; save the player's name in the Wild Monster data (part of the Cinnabar Island Missingno. glitch)
+ jp .captured
+
+.notOldManBattle
+; If the player is fighting the ghost Marowak, set the value that indicates the
+; Pokémon can't be caught and skip the capture calculations.
+ ld a, [wCurMap]
+ cp POKEMONTOWER_6
+ jr nz, .loop
+ ld a, [wEnemyMonSpecies2]
+ cp MAROWAK
+ ld b, $10 ; can't be caught value
+ jp z, .setAnimData
+
+; Get the first random number. Let it be called Rand1.
+; Rand1 must be within a certain range according the kind of ball being thrown.
+; The ranges are as follows.
+; Poké Ball: [0, 255]
+; Great Ball: [0, 200]
+; Ultra/Safari Ball: [0, 150]
+; Loop until an acceptable number is found.
+
+.loop
+ call Random
+ ld b, a
+
+; Get the item ID.
+ ld hl, wcf91
+ ld a, [hl]
+
+; The Master Ball always succeeds.
+ cp MASTER_BALL
+ jp z, .captured
+
+; Anything will do for the basic Poké Ball.
+ cp POKE_BALL
+ jr z, .checkForAilments
+
+; If it's a Great/Ultra/Safari Ball and Rand1 is greater than 200, try again.
+ ld a, 200
+ cp b
+ jr c, .loop
+
+; Less than or equal to 200 is good enough for a Great Ball.
+ ld a, [hl]
+ cp GREAT_BALL
+ jr z, .checkForAilments
+
+; If it's an Ultra/Safari Ball and Rand1 is greater than 150, try again.
+ ld a, 150
+ cp b
+ jr c, .loop
+
+.checkForAilments
+; Pokémon can be caught more easily with a status ailment.
+; Depending on the status ailment, a certain value will be subtracted from
+; Rand1. Let this value be called Status.
+; The larger Status is, the more easily the Pokémon can be caught.
+; no status ailment: Status = 0
+; Burn/Paralysis/Poison: Status = 12
+; Freeze/Sleep: Status = 25
+; If Status is greater than Rand1, the Pokémon will be caught for sure.
+ ld a, [wEnemyMonStatus]
+ and a
+ jr z, .skipAilmentValueSubtraction ; no ailments
+ and 1 << FRZ | SLP
+ ld c, 12
+ jr z, .notFrozenOrAsleep
+ ld c, 25
+.notFrozenOrAsleep
+ ld a, b
+ sub c
+ jp c, .captured
+ ld b, a
+
+.skipAilmentValueSubtraction
+ push bc ; save (Rand1 - Status)
+
+; Calculate MaxHP * 255.
+ xor a
+ ld [H_MULTIPLICAND], a
+ ld hl, wEnemyMonMaxHP
+ ld a, [hli]
+ ld [H_MULTIPLICAND + 1], a
+ ld a, [hl]
+ ld [H_MULTIPLICAND + 2], a
+ ld a, 255
+ ld [H_MULTIPLIER], a
+ call Multiply
+
+; Determine BallFactor. It's 8 for Great Balls and 12 for the others.
+ ld a, [wcf91]
+ cp GREAT_BALL
+ ld a, 12
+ jr nz, .skip1
+ ld a, 8
+
+.skip1
+; Note that the results of all division operations are floored.
+
+; Calculate (MaxHP * 255) / BallFactor.
+ ld [H_DIVISOR], a
+ ld b, 4 ; number of bytes in dividend
+ call Divide
+
+; Divide the enemy's current HP by 4. HP is not supposed to exceed 999 so
+; the result should fit in a. If the division results in a quotient of 0,
+; change it to 1.
+ ld hl, wEnemyMonHP
+ ld a, [hli]
+ ld b, a
+ ld a, [hl]
+ srl b
+ rr a
+ srl b
+ rr a
+ and a
+ jr nz, .skip2
+ inc a
+
+.skip2
+; Let W = ((MaxHP * 255) / BallFactor) / max(HP / 4, 1). Calculate W.
+ ld [H_DIVISOR], a
+ ld b, 4
+ call Divide
+
+; If W > 255, store 255 in [H_QUOTIENT + 3].
+; Let X = min(W, 255) = [H_QUOTIENT + 3].
+ ld a, [H_QUOTIENT + 2]
+ and a
+ jr z, .skip3
+ ld a, 255
+ ld [H_QUOTIENT + 3], a
+
+.skip3
+ pop bc ; b = Rand1 - Status
+
+; If Rand1 - Status > CatchRate, the ball fails to capture the Pokémon.
+ ld a, [wEnemyMonActualCatchRate]
+ cp b
+ jr c, .failedToCapture
+
+; If W > 255, the ball captures the Pokémon.
+ ld a, [H_QUOTIENT + 2]
+ and a
+ jr nz, .captured
+
+ call Random ; Let this random number be called Rand2.
+
+; If Rand2 > X, the ball fails to capture the Pokémon.
+ ld b, a
+ ld a, [H_QUOTIENT + 3]
+ cp b
+ jr c, .failedToCapture
+
+.captured
+ jr .skipShakeCalculations
+
+.failedToCapture
+ ld a, [H_QUOTIENT + 3]
+ ld [wPokeBallCaptureCalcTemp], a ; Save X.
+
+; Calculate CatchRate * 100.
+ xor a
+ ld [H_MULTIPLICAND], a
+ ld [H_MULTIPLICAND + 1], a
+ ld a, [wEnemyMonActualCatchRate]
+ ld [H_MULTIPLICAND + 2], a
+ ld a, 100
+ ld [H_MULTIPLIER], a
+ call Multiply
+
+; Determine BallFactor2.
+; Poké Ball: BallFactor2 = 255
+; Great Ball: BallFactor2 = 200
+; Ultra/Safari Ball: BallFactor2 = 150
+ ld a, [wcf91]
+ ld b, 255
+ cp POKE_BALL
+ jr z, .skip4
+ ld b, 200
+ cp GREAT_BALL
+ jr z, .skip4
+ ld b, 150
+ cp ULTRA_BALL
+ jr z, .skip4
+
+.skip4
+; Let Y = (CatchRate * 100) / BallFactor2. Calculate Y.
+ ld a, b
+ ld [H_DIVISOR], a
+ ld b, 4
+ call Divide
+
+; If Y > 255, there are 3 shakes.
+; Note that this shouldn't be possible.
+; The maximum value of Y is (255 * 100) / 150 = 170.
+ ld a, [H_QUOTIENT + 2]
+ and a
+ ld b, $63 ; 3 shakes
+ jr nz, .setAnimData
+
+; Calculate X * Y.
+ ld a, [wPokeBallCaptureCalcTemp]
+ ld [H_MULTIPLIER], a
+ call Multiply
+
+; Calculate (X * Y) / 255.
+ ld a, 255
+ ld [H_DIVISOR], a
+ ld b, 4
+ call Divide
+
+; Determine Status2.
+; no status ailment: Status2 = 0
+; Burn/Paralysis/Poison: Status2 = 5
+; Freeze/Sleep: Status2 = 10
+ ld a, [wEnemyMonStatus]
+ and a
+ jr z, .skip5
+ and 1 << FRZ | SLP
+ ld b, 5
+ jr z, .addAilmentValue
+ ld b, 10
+
+.addAilmentValue
+; If the Pokémon has a status ailment, add Status2.
+ ld a, [H_QUOTIENT + 3]
+ add b
+ ld [H_QUOTIENT + 3], a
+
+.skip5
+; Finally determine the number of shakes.
+; Let Z = ((X * Y) / 255) + Status2 = [H_QUOTIENT + 3].
+; The number of shakes depend on the range Z is in.
+; 0 ≤ Z < 10: 0 shakes (the ball misses)
+; 10 ≤ Z < 30: 1 shake
+; 30 ≤ Z < 70: 2 shakes
+; 70 ≤ Z: 3 shakes
+ ld a, [H_QUOTIENT + 3]
+ cp 10
+ ld b, $20
+ jr c, .setAnimData
+ cp 30
+ ld b, $61
+ jr c, .setAnimData
+ cp 70
+ ld b, $62
+ jr c, .setAnimData
+ ld b, $63
+
+.setAnimData
+ ld a, b
+ ld [wPokeBallAnimData], a
+
+.skipShakeCalculations
+ ld c, 20
+ call DelayFrames
+
+; Do the animation.
+ ld a, TOSS_ANIM
+ ld [wAnimationID], a
+ xor a
+ ld [H_WHOSETURN], a
+ ld [wAnimationType], a
+ ld [wDamageMultipliers], a
+ ld a, [wWhichPokemon]
+ push af
+ ld a, [wcf91]
+ push af
+ predef MoveAnimation
+ pop af
+ ld [wcf91], a
+ pop af
+ ld [wWhichPokemon], a
+
+; Determine the message to display from the animation.
+ ld a, [wPokeBallAnimData]
+ cp $10
+ ld hl, ItemUseBallText00
+ jp z, .printMessage
+ cp $20
+ ld hl, ItemUseBallText01
+ jp z, .printMessage
+ cp $61
+ ld hl, ItemUseBallText02
+ jp z, .printMessage
+ cp $62
+ ld hl, ItemUseBallText03
+ jp z, .printMessage
+ cp $63
+ ld hl, ItemUseBallText04
+ jp z, .printMessage
+
+; Save current HP.
+ ld hl, wEnemyMonHP
+ ld a, [hli]
+ push af
+ ld a, [hli]
+ push af
+
+; Save status ailment.
+ inc hl
+ ld a, [hl]
+ push af
+
+ push hl
+
+; If the Pokémon is transformed, the Pokémon is assumed to be a Ditto.
+; This is a bug because a wild Pokémon could have used Transform via
+; Mirror Move even though the only wild Pokémon that knows Transform is Ditto.
+ ld hl, wEnemyBattleStatus3
+ bit TRANSFORMED, [hl]
+ jr z, .notTransformed
+ ld a, DITTO
+ ld [wEnemyMonSpecies2], a
+ jr .skip6
+
+.notTransformed
+; If the Pokémon is not transformed, set the transformed bit and copy the
+; DVs to wTransformedEnemyMonOriginalDVs so that LoadEnemyMonData won't generate
+; new DVs.
+ set TRANSFORMED, [hl]
+ ld hl, wTransformedEnemyMonOriginalDVs
+ ld a, [wEnemyMonDVs]
+ ld [hli], a
+ ld a, [wEnemyMonDVs + 1]
+ ld [hl], a
+
+.skip6
+ ld a, [wcf91]
+ push af
+ ld a, [wEnemyMonSpecies2]
+ ld [wcf91], a
+ ld a, [wEnemyMonLevel]
+ ld [wCurEnemyLVL], a
+ callab LoadEnemyMonData
+ pop af
+ ld [wcf91], a
+ pop hl
+ pop af
+ ld [hld], a
+ dec hl
+ pop af
+ ld [hld], a
+ pop af
+ ld [hl], a
+ ld a, [wEnemyMonSpecies]
+ ld [wCapturedMonSpecies], a
+ ld [wcf91], a
+ ld [wd11e], a
+ ld a, [wBattleType]
+ dec a ; is this the old man battle?
+ jr z, .oldManCaughtMon ; if so, don't give the player the caught Pokémon
+
+ ld hl, ItemUseBallText05
+ call PrintText
+
+; Add the caught Pokémon to the Pokédex.
+ predef IndexToPokedex
+ ld a, [wd11e]
+ dec a
+ ld c, a
+ ld b, FLAG_TEST
+ ld hl, wPokedexOwned
+ predef FlagActionPredef
+ ld a, c
+ push af
+ ld a, [wd11e]
+ dec a
+ ld c, a
+ ld b, FLAG_SET
+ predef FlagActionPredef
+ pop af
+
+ and a ; was the Pokémon already in the Pokédex?
+ jr nz, .skipShowingPokedexData ; if so, don't show the Pokédex data
+
+ ld hl, ItemUseBallText06
+ call PrintText
+ call ClearSprites
+ ld a, [wEnemyMonSpecies]
+ ld [wd11e], a
+ predef ShowPokedexData
+
+.skipShowingPokedexData
+ ld a, [wPartyCount]
+ cp PARTY_LENGTH ; is party full?
+ jr z, .sendToBox
+ xor a ; PLAYER_PARTY_DATA
+ ld [wMonDataLocation], a
+ call ClearSprites
+ call AddPartyMon
+ jr .done
+
+.sendToBox
+ call ClearSprites
+ call SendNewMonToBox
+ ld hl, ItemUseBallText07
+ CheckEvent EVENT_MET_BILL
+ jr nz, .printTransferredToPCText
+ ld hl, ItemUseBallText08
+.printTransferredToPCText
+ call PrintText
+ jr .done
+
+.oldManCaughtMon
+ ld hl, ItemUseBallText05
+
+.printMessage
+ call PrintText
+ call ClearSprites
+
+.done
+ ld a, [wBattleType]
+ and a ; is this the old man battle?
+ ret nz ; if so, don't remove a ball from the bag
+
+; Remove a ball from the bag.
+ ld hl, wNumBagItems
+ inc a
+ ld [wItemQuantity], a
+ jp RemoveItemFromInventory
+
+ItemUseBallText00:
+;"It dodged the thrown ball!"
+;"This pokemon can't be caught"
+ TX_FAR _ItemUseBallText00
+ db "@"
+ItemUseBallText01:
+;"You missed the pokemon!"
+ TX_FAR _ItemUseBallText01
+ db "@"
+ItemUseBallText02:
+;"Darn! The pokemon broke free!"
+ TX_FAR _ItemUseBallText02
+ db "@"
+ItemUseBallText03:
+;"Aww! It appeared to be caught!"
+ TX_FAR _ItemUseBallText03
+ db "@"
+ItemUseBallText04:
+;"Shoot! It was so close too!"
+ TX_FAR _ItemUseBallText04
+ db "@"
+ItemUseBallText05:
+;"All right! {MonName} was caught!"
+;play sound
+ TX_FAR _ItemUseBallText05
+ TX_SFX_CAUGHT_MON
+ TX_BLINK
+ db "@"
+ItemUseBallText07:
+;"X was transferred to Bill's PC"
+ TX_FAR _ItemUseBallText07
+ db "@"
+ItemUseBallText08:
+;"X was transferred to someone's PC"
+ TX_FAR _ItemUseBallText08
+ db "@"
+
+ItemUseBallText06:
+;"New DEX data will be added..."
+;play sound
+ TX_FAR _ItemUseBallText06
+ TX_SFX_DEX_PAGE_ADDED
+ TX_BLINK
+ db "@"
+
+ItemUseTownMap:
+ ld a, [wIsInBattle]
+ and a
+ jp nz, ItemUseNotTime
+ jpba DisplayTownMap
+
+ItemUseBicycle:
+ ld a, [wIsInBattle]
+ and a
+ jp nz, ItemUseNotTime
+ ld a, [wWalkBikeSurfState]
+ ld [wWalkBikeSurfStateCopy], a
+ cp 2 ; is the player surfing?
+ jp z, ItemUseNotTime
+ dec a ; is player already bicycling?
+ jr nz, .tryToGetOnBike
+.getOffBike
+ call ItemUseReloadOverworldData
+ xor a
+ ld [wWalkBikeSurfState], a ; change player state to walking
+ call PlayDefaultMusic ; play walking music
+ ld hl, GotOffBicycleText
+ jr .printText
+.tryToGetOnBike
+ call IsBikeRidingAllowed
+ jp nc, NoCyclingAllowedHere
+ call ItemUseReloadOverworldData
+ xor a ; no keys pressed
+ ld [hJoyHeld], a ; current joypad state
+ inc a
+ ld [wWalkBikeSurfState], a ; change player state to bicycling
+ ld hl, GotOnBicycleText
+ call PlayDefaultMusic ; play bike riding music
+.printText
+ jp PrintText
+
+; used for Surf out-of-battle effect
+ItemUseSurfboard:
+ ld a, [wWalkBikeSurfState]
+ ld [wWalkBikeSurfStateCopy], a
+ cp 2 ; is the player already surfing?
+ jr z, .tryToStopSurfing
+.tryToSurf
+ call IsNextTileShoreOrWater
+ jp c, SurfingAttemptFailed
+ ld hl, TilePairCollisionsWater
+ call CheckForTilePairCollisions
+ jp c, SurfingAttemptFailed
+.surf
+ call .makePlayerMoveForward
+ ld hl, wd730
+ set 7, [hl]
+ ld a, 2
+ ld [wWalkBikeSurfState], a ; change player state to surfing
+ call PlayDefaultMusic ; play surfing music
+ ld hl, SurfingGotOnText
+ jp PrintText
+.tryToStopSurfing
+ xor a
+ ld [hSpriteIndexOrTextID], a
+ ld d, 16 ; talking range in pixels (normal range)
+ call IsSpriteInFrontOfPlayer2
+ res 7, [hl]
+ ld a, [hSpriteIndexOrTextID]
+ and a ; is there a sprite in the way?
+ jr nz, .cannotStopSurfing
+ ld hl, TilePairCollisionsWater
+ call CheckForTilePairCollisions
+ jr c, .cannotStopSurfing
+ ld hl, wTilesetCollisionPtr ; pointer to list of passable tiles
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a ; hl now points to passable tiles
+ ld a, [wTileInFrontOfPlayer] ; tile in front of the player
+ ld b, a
+.passableTileLoop
+ ld a, [hli]
+ cp b
+ jr z, .stopSurfing
+ cp $ff
+ jr nz, .passableTileLoop
+.cannotStopSurfing
+ ld hl, SurfingNoPlaceToGetOffText
+ jp PrintText
+.stopSurfing
+ call .makePlayerMoveForward
+ ld hl, wd730
+ set 7, [hl]
+ xor a
+ ld [wWalkBikeSurfState], a ; change player state to walking
+ dec a
+ ld [wJoyIgnore], a
+ call PlayDefaultMusic ; play walking music
+ jp LoadWalkingPlayerSpriteGraphics
+; uses a simulated button press to make the player move forward
+.makePlayerMoveForward
+ ld a, [wPlayerDirection] ; direction the player is going
+ bit PLAYER_DIR_BIT_UP, a
+ ld b, D_UP
+ jr nz, .storeSimulatedButtonPress
+ bit PLAYER_DIR_BIT_DOWN, a
+ ld b, D_DOWN
+ jr nz, .storeSimulatedButtonPress
+ bit PLAYER_DIR_BIT_LEFT, a
+ ld b, D_LEFT
+ jr nz, .storeSimulatedButtonPress
+ ld b, D_RIGHT
+.storeSimulatedButtonPress
+ ld a, b
+ ld [wSimulatedJoypadStatesEnd], a
+ xor a
+ ld [wWastedByteCD39], a
+ inc a
+ ld [wSimulatedJoypadStatesIndex], a
+ ret
+
+SurfingGotOnText:
+ TX_FAR _SurfingGotOnText
+ db "@"
+
+SurfingNoPlaceToGetOffText:
+ TX_FAR _SurfingNoPlaceToGetOffText
+ db "@"
+
+ItemUsePokedex:
+ predef_jump ShowPokedexMenu
+
+ItemUseEvoStone:
+ ld a, [wIsInBattle]
+ and a
+ jp nz, ItemUseNotTime
+ ld a, [wWhichPokemon]
+ push af
+ ld a, [wcf91]
+ ld [wEvoStoneItemID], a
+ push af
+ ld a, EVO_STONE_PARTY_MENU
+ ld [wPartyMenuTypeOrMessageID], a
+ ld a, $ff
+ ld [wUpdateSpritesEnabled], a
+ call DisplayPartyMenu
+ pop bc
+ jr c, .canceledItemUse
+ ld a, b
+ ld [wcf91], a
+ ld a, $01
+ ld [wForceEvolution], a
+ ld a, SFX_HEAL_AILMENT
+ call PlaySoundWaitForCurrent
+ call WaitForSoundToFinish
+ callab TryEvolvingMon ; try to evolve pokemon
+ ld a, [wEvolutionOccurred]
+ and a
+ jr z, .noEffect
+ pop af
+ ld [wWhichPokemon], a
+ ld hl, wNumBagItems
+ ld a, 1 ; remove 1 stone
+ ld [wItemQuantity], a
+ jp RemoveItemFromInventory
+.noEffect
+ call ItemUseNoEffect
+.canceledItemUse
+ xor a
+ ld [wActionResultOrTookBattleTurn], a ; item not used
+ pop af
+ ret
+
+ItemUseVitamin:
+ ld a, [wIsInBattle]
+ and a
+ jp nz, ItemUseNotTime
+
+ItemUseMedicine:
+ ld a, [wPartyCount]
+ and a
+ jp z, .emptyParty
+ ld a, [wWhichPokemon]
+ push af
+ ld a, [wcf91]
+ push af
+ ld a, USE_ITEM_PARTY_MENU
+ ld [wPartyMenuTypeOrMessageID], a
+ ld a, $ff
+ ld [wUpdateSpritesEnabled], a
+ ld a, [wPseudoItemID]
+ and a ; using Softboiled?
+ jr z, .notUsingSoftboiled
+; if using softboiled
+ call GoBackToPartyMenu
+ jr .getPartyMonDataAddress
+.emptyParty
+ ld hl, .emptyPartyText
+ xor a
+ ld [wActionResultOrTookBattleTurn], a ; item use failed
+ jp PrintText
+.emptyPartyText
+ text "You don't have"
+ line "any #MON!"
+ prompt
+.notUsingSoftboiled
+ call DisplayPartyMenu
+.getPartyMonDataAddress
+ jp c, .canceledItemUse
+ ld hl, wPartyMons
+ ld bc, wPartyMon2 - wPartyMon1
+ ld a, [wWhichPokemon]
+ call AddNTimes
+ ld a, [wWhichPokemon]
+ ld [wUsedItemOnWhichPokemon], a
+ ld d, a
+ ld a, [wcf91]
+ ld e, a
+ ld [wd0b5], a
+ pop af
+ ld [wcf91], a
+ pop af
+ ld [wWhichPokemon], a
+ ld a, [wPseudoItemID]
+ and a ; using Softboiled?
+ jr z, .checkItemType
+; if using softboiled
+ ld a, [wWhichPokemon]
+ cp d ; is the pokemon trying to use softboiled on itself?
+ jr z, ItemUseMedicine ; if so, force another choice
+.checkItemType
+ ld a, [wcf91]
+ cp REVIVE
+ jr nc, .healHP ; if it's a Revive or Max Revive
+ cp FULL_HEAL
+ jr z, .cureStatusAilment ; if it's a Full Heal
+ cp HP_UP
+ jp nc, .useVitamin ; if it's a vitamin or Rare Candy
+ cp FULL_RESTORE
+ jr nc, .healHP ; if it's a Full Restore or one of the potions
+; fall through if it's one of the status-specific healing items
+.cureStatusAilment
+ ld bc, wPartyMon1Status - wPartyMon1
+ add hl, bc ; hl now points to status
+ ld a, [wcf91]
+ lb bc, ANTIDOTE_MSG, 1 << PSN
+ cp ANTIDOTE
+ jr z, .checkMonStatus
+ lb bc, BURN_HEAL_MSG, 1 << BRN
+ cp BURN_HEAL
+ jr z, .checkMonStatus
+ lb bc, ICE_HEAL_MSG, 1 << FRZ
+ cp ICE_HEAL
+ jr z, .checkMonStatus
+ lb bc, AWAKENING_MSG, SLP
+ cp AWAKENING
+ jr z, .checkMonStatus
+ lb bc, PARALYZ_HEAL_MSG, 1 << PAR
+ cp PARLYZ_HEAL
+ jr z, .checkMonStatus
+ lb bc, FULL_HEAL_MSG, $ff ; Full Heal
+.checkMonStatus
+ ld a, [hl] ; pokemon's status
+ and c ; does the pokemon have a status ailment the item can cure?
+ jp z, .healingItemNoEffect
+; if the pokemon has a status the item can heal
+ xor a
+ ld [hl], a ; remove the status ailment in the party data
+ ld a, b
+ ld [wPartyMenuTypeOrMessageID], a ; the message to display for the item used
+ ld a, [wPlayerMonNumber]
+ cp d ; is pokemon the item was used on active in battle?
+ jp nz, .doneHealing
+; if it is active in battle
+ xor a
+ ld [wBattleMonStatus], a ; remove the status ailment in the in-battle pokemon data
+ push hl
+ ld hl, wPlayerBattleStatus3
+ res BADLY_POISONED, [hl] ; heal Toxic status
+ pop hl
+ ld bc, wPartyMon1Stats - wPartyMon1Status
+ add hl, bc ; hl now points to party stats
+ ld de, wBattleMonStats
+ ld bc, NUM_STATS * 2
+ call CopyData ; copy party stats to in-battle stat data
+ predef DoubleOrHalveSelectedStats
+ jp .doneHealing
+.healHP
+ inc hl ; hl = address of current HP
+ ld a, [hli]
+ ld b, a
+ ld [wHPBarOldHP+1], a
+ ld a, [hl]
+ ld c, a
+ ld [wHPBarOldHP], a ; current HP stored at wHPBarOldHP (2 bytes, big-endian)
+ or b
+ jr nz, .notFainted
+.fainted
+ ld a, [wcf91]
+ cp REVIVE
+ jr z, .updateInBattleFaintedData
+ cp MAX_REVIVE
+ jr z, .updateInBattleFaintedData
+ jp .healingItemNoEffect
+.updateInBattleFaintedData
+ ld a, [wIsInBattle]
+ and a
+ jr z, .compareCurrentHPToMaxHP
+ push hl
+ push de
+ push bc
+ ld a, [wUsedItemOnWhichPokemon]
+ ld c, a
+ ld hl, wPartyFoughtCurrentEnemyFlags
+ ld b, FLAG_TEST
+ predef FlagActionPredef
+ ld a, c
+ and a
+ jr z, .next
+ ld a, [wUsedItemOnWhichPokemon]
+ ld c, a
+ ld hl, wPartyGainExpFlags
+ ld b, FLAG_SET
+ predef FlagActionPredef
+.next
+ pop bc
+ pop de
+ pop hl
+ jr .compareCurrentHPToMaxHP
+.notFainted
+ ld a, [wcf91]
+ cp REVIVE
+ jp z, .healingItemNoEffect
+ cp MAX_REVIVE
+ jp z, .healingItemNoEffect
+.compareCurrentHPToMaxHP
+ push hl
+ push bc
+ ld bc, wPartyMon1MaxHP - (wPartyMon1HP + 1)
+ add hl, bc ; hl now points to max HP
+ pop bc
+ ld a, [hli]
+ cp b
+ jr nz, .skipComparingLSB ; no need to compare the LSB's if the MSB's don't match
+ ld a, [hl]
+ cp c
+.skipComparingLSB
+ pop hl
+ jr nz, .notFullHP
+.fullHP ; if the pokemon's current HP equals its max HP
+ ld a, [wcf91]
+ cp FULL_RESTORE
+ jp nz, .healingItemNoEffect
+ inc hl
+ inc hl
+ ld a, [hld] ; status ailment
+ and a ; does the pokemon have a status ailment?
+ jp z, .healingItemNoEffect
+ ld a, FULL_HEAL
+ ld [wcf91], a
+ dec hl
+ dec hl
+ dec hl
+ jp .cureStatusAilment
+.notFullHP ; if the pokemon's current HP doesn't equal its max HP
+ xor a
+ ld [wLowHealthAlarm], a ;disable low health alarm
+ ld [wChannelSoundIDs + Ch4], a
+ push hl
+ push de
+ ld bc, wPartyMon1MaxHP - (wPartyMon1HP + 1)
+ add hl, bc ; hl now points to max HP
+ ld a, [hli]
+ ld [wHPBarMaxHP+1], a
+ ld a, [hl]
+ ld [wHPBarMaxHP], a ; max HP stored at wHPBarMaxHP (2 bytes, big-endian)
+ ld a, [wPseudoItemID]
+ and a ; using Softboiled?
+ jp z, .notUsingSoftboiled2
+; if using softboiled
+ ld hl, wHPBarMaxHP
+ ld a, [hli]
+ push af
+ ld a, [hli]
+ push af
+ ld a, [hli]
+ push af
+ ld a, [hl]
+ push af
+ ld hl, wPartyMon1MaxHP
+ ld a, [wWhichPokemon]
+ ld bc, wPartyMon2 - wPartyMon1
+ call AddNTimes
+ ld a, [hli]
+ ld [wHPBarMaxHP + 1], a
+ ld [H_DIVIDEND], a
+ ld a, [hl]
+ ld [wHPBarMaxHP], a
+ ld [H_DIVIDEND + 1], a
+ ld a, 5
+ ld [H_DIVISOR], a
+ ld b, 2 ; number of bytes
+ call Divide ; get 1/5 of max HP of pokemon that used Softboiled
+ ld bc, (wPartyMon1HP + 1) - (wPartyMon1MaxHP + 1)
+ add hl, bc ; hl now points to LSB of current HP of pokemon that used Softboiled
+; subtract 1/5 of max HP from current HP of pokemon that used Softboiled
+ ld a, [H_QUOTIENT + 3]
+ push af
+ ld b, a
+ ld a, [hl]
+ ld [wHPBarOldHP], a
+ sub b
+ ld [hld], a
+ ld [wHPBarNewHP], a
+ ld a, [H_QUOTIENT + 2]
+ ld b, a
+ ld a, [hl]
+ ld [wHPBarOldHP+1], a
+ sbc b
+ ld [hl], a
+ ld [wHPBarNewHP+1], a
+ coord hl, 4, 1
+ ld a, [wWhichPokemon]
+ ld bc, 2 * SCREEN_WIDTH
+ call AddNTimes ; calculate coordinates of HP bar of pokemon that used Softboiled
+ ld a, SFX_HEAL_HP
+ call PlaySoundWaitForCurrent
+ ld a, [hFlags_0xFFF6]
+ set 0, a
+ ld [hFlags_0xFFF6], a
+ ld a, $02
+ ld [wHPBarType], a
+ predef UpdateHPBar2 ; animate HP bar decrease of pokemon that used Softboiled
+ ld a, [hFlags_0xFFF6]
+ res 0, a
+ ld [hFlags_0xFFF6], a
+ pop af
+ ld b, a ; store heal amount (1/5 of max HP)
+ ld hl, wHPBarOldHP + 1
+ pop af
+ ld [hld], a
+ pop af
+ ld [hld], a
+ pop af
+ ld [hld], a
+ pop af
+ ld [hl], a
+ jr .addHealAmount
+.notUsingSoftboiled2
+ ld a, [wcf91]
+ cp SODA_POP
+ ld b, 60 ; Soda Pop heal amount
+ jr z, .addHealAmount
+ ld b, 80 ; Lemonade heal amount
+ jr nc, .addHealAmount
+ cp FRESH_WATER
+ ld b, 50 ; Fresh Water heal amount
+ jr z, .addHealAmount
+ cp SUPER_POTION
+ ld b, 200 ; Hyper Potion heal amount
+ jr c, .addHealAmount
+ ld b, 50 ; Super Potion heal amount
+ jr z, .addHealAmount
+ ld b, 20 ; Potion heal amount
+.addHealAmount
+ pop de
+ pop hl
+ ld a, [hl]
+ add b
+ ld [hld], a
+ ld [wHPBarNewHP], a
+ ld a, [hl]
+ ld [wHPBarNewHP+1], a
+ jr nc, .noCarry
+ inc [hl]
+ ld a, [hl]
+ ld [wHPBarNewHP + 1], a
+.noCarry
+ push de
+ inc hl
+ ld d, h
+ ld e, l ; de now points to current HP
+ ld hl, (wPartyMon1MaxHP + 1) - (wPartyMon1HP + 1)
+ add hl, de ; hl now points to max HP
+ ld a, [wcf91]
+ cp REVIVE
+ jr z, .setCurrentHPToHalfMaxHP
+ ld a, [hld]
+ ld b, a
+ ld a, [de]
+ sub b
+ dec de
+ ld b, [hl]
+ ld a, [de]
+ sbc b
+ jr nc, .setCurrentHPToMaxHp ; if current HP exceeds max HP after healing
+ ld a, [wcf91]
+ cp HYPER_POTION
+ jr c, .setCurrentHPToMaxHp ; if using a Full Restore or Max Potion
+ cp MAX_REVIVE
+ jr z, .setCurrentHPToMaxHp ; if using a Max Revive
+ jr .updateInBattleData
+.setCurrentHPToHalfMaxHP
+ dec hl
+ dec de
+ ld a, [hli]
+ srl a
+ ld [de], a
+ ld [wHPBarNewHP+1], a
+ ld a, [hl]
+ rr a
+ inc de
+ ld [de], a
+ ld [wHPBarNewHP], a
+ dec de
+ jr .doneHealingPartyHP
+.setCurrentHPToMaxHp
+ ld a, [hli]
+ ld [de], a
+ ld [wHPBarNewHP+1], a
+ inc de
+ ld a, [hl]
+ ld [de], a
+ ld [wHPBarNewHP], a
+ dec de
+.doneHealingPartyHP ; done updating the pokemon's current HP in the party data structure
+ ld a, [wcf91]
+ cp FULL_RESTORE
+ jr nz, .updateInBattleData
+ ld bc, wPartyMon1Status - (wPartyMon1MaxHP + 1)
+ add hl, bc
+ xor a
+ ld [hl], a ; remove the status ailment in the party data
+.updateInBattleData
+ ld h, d
+ ld l, e
+ pop de
+ ld a, [wPlayerMonNumber]
+ cp d ; is pokemon the item was used on active in battle?
+ jr nz, .calculateHPBarCoords
+; copy party HP to in-battle HP
+ ld a, [hli]
+ ld [wBattleMonHP], a
+ ld a, [hld]
+ ld [wBattleMonHP + 1], a
+ ld a, [wcf91]
+ cp FULL_RESTORE
+ jr nz, .calculateHPBarCoords
+ xor a
+ ld [wBattleMonStatus], a ; remove the status ailment in the in-battle pokemon data
+.calculateHPBarCoords
+ ld hl, wOAMBuffer + $90
+ ld bc, 2 * SCREEN_WIDTH
+ inc d
+.calculateHPBarCoordsLoop
+ add hl, bc
+ dec d
+ jr nz, .calculateHPBarCoordsLoop
+ jr .doneHealing
+.healingItemNoEffect
+ call ItemUseNoEffect
+ jp .done
+.doneHealing
+ ld a, [wPseudoItemID]
+ and a ; using Softboiled?
+ jr nz, .skipRemovingItem ; no item to remove if using Softboiled
+ push hl
+ call RemoveUsedItem
+ pop hl
+.skipRemovingItem
+ ld a, [wcf91]
+ cp FULL_RESTORE
+ jr c, .playStatusAilmentCuringSound
+ cp FULL_HEAL
+ jr z, .playStatusAilmentCuringSound
+ ld a, SFX_HEAL_HP
+ call PlaySoundWaitForCurrent
+ ld a, [hFlags_0xFFF6]
+ set 0, a
+ ld [hFlags_0xFFF6], a
+ ld a, $02
+ ld [wHPBarType], a
+ predef UpdateHPBar2 ; animate the HP bar lengthening
+ ld a, [hFlags_0xFFF6]
+ res 0, a
+ ld [hFlags_0xFFF6], a
+ ld a, REVIVE_MSG
+ ld [wPartyMenuTypeOrMessageID], a
+ ld a, [wcf91]
+ cp REVIVE
+ jr z, .showHealingItemMessage
+ cp MAX_REVIVE
+ jr z, .showHealingItemMessage
+ ld a, POTION_MSG
+ ld [wPartyMenuTypeOrMessageID], a
+ jr .showHealingItemMessage
+.playStatusAilmentCuringSound
+ ld a, SFX_HEAL_AILMENT
+ call PlaySoundWaitForCurrent
+.showHealingItemMessage
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call ClearScreen
+ dec a
+ ld [wUpdateSpritesEnabled], a
+ call RedrawPartyMenu ; redraws the party menu and displays the message
+ ld a, 1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ld c, 50
+ call DelayFrames
+ call WaitForTextScrollButtonPress
+ jr .done
+.canceledItemUse
+ xor a
+ ld [wActionResultOrTookBattleTurn], a ; item use failed
+ pop af
+ pop af
+.done
+ ld a, [wPseudoItemID]
+ and a ; using Softboiled?
+ ret nz ; if so, return
+ call GBPalWhiteOut
+ call z, RunDefaultPaletteCommand
+ ld a, [wIsInBattle]
+ and a
+ ret nz
+ jp ReloadMapData
+.useVitamin
+ push hl
+ ld a, [hl]
+ ld [wd0b5], a
+ ld [wd11e], a
+ ld bc, wPartyMon1Level - wPartyMon1
+ add hl, bc ; hl now points to level
+ ld a, [hl] ; a = level
+ ld [wCurEnemyLVL], a ; store level
+ call GetMonHeader
+ push de
+ ld a, d
+ ld hl, wPartyMonNicks
+ call GetPartyMonName
+ pop de
+ pop hl
+ ld a, [wcf91]
+ cp RARE_CANDY
+ jp z, .useRareCandy
+ push hl
+ sub HP_UP
+ add a
+ ld bc, wPartyMon1HPExp - wPartyMon1
+ add hl, bc
+ add l
+ ld l, a
+ jr nc, .noCarry2
+ inc h
+.noCarry2
+ ld a, 10
+ ld b, a
+ ld a, [hl] ; a = MSB of stat experience of the appropriate stat
+ cp 100 ; is there already at least 25600 (256 * 100) stat experience?
+ jr nc, .vitaminNoEffect ; if so, vitamins can't add any more
+ add b ; add 2560 (256 * 10) stat experience
+ jr nc, .noCarry3 ; a carry should be impossible here, so this will always jump
+ ld a, 255
+.noCarry3
+ ld [hl], a
+ pop hl
+ call .recalculateStats
+ ld hl, VitaminText
+ ld a, [wcf91]
+ sub HP_UP - 1
+ ld c, a
+.statNameLoop ; loop to get the address of the name of the stat the vitamin increases
+ dec c
+ jr z, .gotStatName
+.statNameInnerLoop
+ ld a, [hli]
+ ld b, a
+ ld a, $50
+ cp b
+ jr nz, .statNameInnerLoop
+ jr .statNameLoop
+.gotStatName
+ ld de, wcf4b
+ ld bc, 10
+ call CopyData ; copy the stat's name to wcf4b
+ ld a, SFX_HEAL_AILMENT
+ call PlaySound
+ ld hl, VitaminStatRoseText
+ call PrintText
+ jp RemoveUsedItem
+.vitaminNoEffect
+ pop hl
+ ld hl, VitaminNoEffectText
+ call PrintText
+ jp GBPalWhiteOut
+.recalculateStats
+ ld bc, wPartyMon1Stats - wPartyMon1
+ add hl, bc
+ ld d, h
+ ld e, l ; de now points to stats
+ ld bc, (wPartyMon1Exp + 2) - wPartyMon1Stats
+ add hl, bc ; hl now points to LSB of experience
+ ld b, 1
+ jp CalcStats ; recalculate stats
+.useRareCandy
+ push hl
+ ld bc, wPartyMon1Level - wPartyMon1
+ add hl, bc ; hl now points to level
+ ld a, [hl] ; a = level
+ cp MAX_LEVEL
+ jr z, .vitaminNoEffect ; can't raise level above 100
+ inc a
+ ld [hl], a ; store incremented level
+ ld [wCurEnemyLVL], a
+ push hl
+ push de
+ ld d, a
+ callab CalcExperience ; calculate experience for next level and store it at $ff96
+ pop de
+ pop hl
+ ld bc, wPartyMon1Exp - wPartyMon1Level
+ add hl, bc ; hl now points to MSB of experience
+; update experience to minimum for new level
+ ld a, [hExperience]
+ ld [hli], a
+ ld a, [hExperience + 1]
+ ld [hli], a
+ ld a, [hExperience + 2]
+ ld [hl], a
+ pop hl
+ ld a, [wWhichPokemon]
+ push af
+ ld a, [wcf91]
+ push af
+ push de
+ push hl
+ ld bc, wPartyMon1MaxHP - wPartyMon1
+ add hl, bc ; hl now points to MSB of max HP
+ ld a, [hli]
+ ld b, a
+ ld c, [hl]
+ pop hl
+ push bc
+ push hl
+ call .recalculateStats
+ pop hl
+ ld bc, (wPartyMon1MaxHP + 1) - wPartyMon1
+ add hl, bc ; hl now points to LSB of max HP
+ pop bc
+ ld a, [hld]
+ sub c
+ ld c, a
+ ld a, [hl]
+ sbc b
+ ld b, a ; bc = the amount of max HP gained from leveling up
+; add the amount gained to the current HP
+ ld de, (wPartyMon1HP + 1) - wPartyMon1MaxHP
+ add hl, de ; hl now points to LSB of current HP
+ ld a, [hl]
+ add c
+ ld [hld], a
+ ld a, [hl]
+ adc b
+ ld [hl], a
+ ld a, RARE_CANDY_MSG
+ ld [wPartyMenuTypeOrMessageID], a
+ call RedrawPartyMenu
+ pop de
+ ld a, d
+ ld [wWhichPokemon], a
+ ld a, e
+ ld [wd11e], a
+ xor a ; PLAYER_PARTY_DATA
+ ld [wMonDataLocation], a
+ call LoadMonData
+ ld d, $01
+ callab PrintStatsBox ; display new stats text box
+ call WaitForTextScrollButtonPress ; wait for button press
+ xor a ; PLAYER_PARTY_DATA
+ ld [wMonDataLocation], a
+ predef LearnMoveFromLevelUp ; learn level up move, if any
+ xor a
+ ld [wForceEvolution], a
+ callab TryEvolvingMon ; evolve pokemon, if appropriate
+ ld a, $01
+ ld [wUpdateSpritesEnabled], a
+ pop af
+ ld [wcf91], a
+ pop af
+ ld [wWhichPokemon], a
+ jp RemoveUsedItem
+
+VitaminStatRoseText:
+ TX_FAR _VitaminStatRoseText
+ db "@"
+
+VitaminNoEffectText:
+ TX_FAR _VitaminNoEffectText
+ db "@"
+
+VitaminText:
+ db "HEALTH@"
+ db "ATTACK@"
+ db "DEFENSE@"
+ db "SPEED@"
+ db "SPECIAL@"
+
+ItemUseBait:
+ ld hl, ThrewBaitText
+ call PrintText
+ ld hl, wEnemyMonActualCatchRate ; catch rate
+ srl [hl] ; halve catch rate
+ ld a, BAIT_ANIM
+ ld hl, wSafariBaitFactor ; bait factor
+ ld de, wSafariEscapeFactor ; escape factor
+ jr BaitRockCommon
+
+ItemUseRock:
+ ld hl, ThrewRockText
+ call PrintText
+ ld hl, wEnemyMonActualCatchRate ; catch rate
+ ld a, [hl]
+ add a ; double catch rate
+ jr nc, .noCarry
+ ld a, $ff
+.noCarry
+ ld [hl], a
+ ld a, ROCK_ANIM
+ ld hl, wSafariEscapeFactor ; escape factor
+ ld de, wSafariBaitFactor ; bait factor
+
+BaitRockCommon:
+ ld [wAnimationID], a
+ xor a
+ ld [wAnimationType], a
+ ld [H_WHOSETURN], a
+ ld [de], a ; zero escape factor (for bait), zero bait factor (for rock)
+.randomLoop ; loop until a random number less than 5 is generated
+ call Random
+ and 7
+ cp 5
+ jr nc, .randomLoop
+ inc a ; increment the random number, giving a range from 1 to 5 inclusive
+ ld b, a
+ ld a, [hl]
+ add b ; increase bait factor (for bait), increase escape factor (for rock)
+ jr nc, .noCarry
+ ld a, $ff
+.noCarry
+ ld [hl], a
+ predef MoveAnimation ; do animation
+ ld c, 70
+ jp DelayFrames
+
+ThrewBaitText:
+ TX_FAR _ThrewBaitText
+ db "@"
+
+ThrewRockText:
+ TX_FAR _ThrewRockText
+ db "@"
+
+; also used for Dig out-of-battle effect
+ItemUseEscapeRope:
+ ld a, [wIsInBattle]
+ and a
+ jr nz, .notUsable
+ ld a, [wCurMap]
+ cp AGATHAS_ROOM
+ jr z, .notUsable
+ ld a, [wCurMapTileset]
+ ld b, a
+ ld hl, EscapeRopeTilesets
+.loop
+ ld a, [hli]
+ cp $ff
+ jr z, .notUsable
+ cp b
+ jr nz, .loop
+ ld hl, wd732
+ set 3, [hl]
+ set 6, [hl]
+ ld hl, wd72e
+ res 4, [hl]
+ ResetEvent EVENT_IN_SAFARI_ZONE
+ xor a
+ ld [wNumSafariBalls], a
+ ld [wSafariZoneEntranceCurScript], a
+ inc a
+ ld [wEscapedFromBattle], a
+ ld [wActionResultOrTookBattleTurn], a ; item used
+ ld a, [wPseudoItemID]
+ and a ; using Dig?
+ ret nz ; if so, return
+ call ItemUseReloadOverworldData
+ ld c, 30
+ call DelayFrames
+ jp RemoveUsedItem
+.notUsable
+ jp ItemUseNotTime
+
+EscapeRopeTilesets:
+ db FOREST, CEMETERY, CAVERN, FACILITY, INTERIOR
+ db $ff ; terminator
+
+ItemUseRepel:
+ ld b, 100
+
+ItemUseRepelCommon:
+ ld a, [wIsInBattle]
+ and a
+ jp nz, ItemUseNotTime
+ ld a, b
+ ld [wRepelRemainingSteps], a
+ jp PrintItemUseTextAndRemoveItem
+
+; handles X Accuracy item
+ItemUseXAccuracy:
+ ld a, [wIsInBattle]
+ and a
+ jp z, ItemUseNotTime
+ ld hl, wPlayerBattleStatus2
+ set USING_X_ACCURACY, [hl] ; X Accuracy bit
+ jp PrintItemUseTextAndRemoveItem
+
+; This function is bugged and never works. It always jumps to ItemUseNotTime.
+; The Card Key is handled in a different way.
+ItemUseCardKey:
+ xor a
+ ld [wUnusedD71F], a
+ call GetTileAndCoordsInFrontOfPlayer
+ ld a, [GetTileAndCoordsInFrontOfPlayer]
+ cp $18
+ jr nz, .next0
+ ld hl, CardKeyTable1
+ jr .next1
+.next0
+ cp $24
+ jr nz, .next2
+ ld hl, CardKeyTable2
+ jr .next1
+.next2
+ cp $5e
+ jp nz, ItemUseNotTime
+ ld hl, CardKeyTable3
+.next1
+ ld a, [wCurMap]
+ ld b, a
+.loop
+ ld a, [hli]
+ cp $ff
+ jp z, ItemUseNotTime
+ cp b
+ jr nz, .nextEntry1
+ ld a, [hli]
+ cp d
+ jr nz, .nextEntry2
+ ld a, [hli]
+ cp e
+ jr nz, .nextEntry3
+ ld a, [hl]
+ ld [wUnusedD71F], a
+ jr .done
+.nextEntry1
+ inc hl
+.nextEntry2
+ inc hl
+.nextEntry3
+ inc hl
+ jr .loop
+.done
+ ld hl, ItemUseText00
+ call PrintText
+ ld hl, wd728
+ set 7, [hl]
+ ret
+
+; These tables are probably supposed to be door locations in Silph Co.,
+; but they are unused.
+; The reason there are 3 tables is unknown.
+
+; Format:
+; 00: Map ID
+; 01: Y
+; 02: X
+; 03: ID?
+
+CardKeyTable1:
+ db SILPH_CO_2F,$04,$04,$00
+ db SILPH_CO_2F,$04,$05,$01
+ db SILPH_CO_4F,$0C,$04,$02
+ db SILPH_CO_4F,$0C,$05,$03
+ db SILPH_CO_7F,$06,$0A,$04
+ db SILPH_CO_7F,$06,$0B,$05
+ db SILPH_CO_9F,$04,$12,$06
+ db SILPH_CO_9F,$04,$13,$07
+ db SILPH_CO_10F,$08,$0A,$08
+ db SILPH_CO_10F,$08,$0B,$09
+ db $ff
+
+CardKeyTable2:
+ db SILPH_CO_3F,$08,$09,$0A
+ db SILPH_CO_3F,$09,$09,$0B
+ db SILPH_CO_5F,$04,$07,$0C
+ db SILPH_CO_5F,$05,$07,$0D
+ db SILPH_CO_6F,$0C,$05,$0E
+ db SILPH_CO_6F,$0D,$05,$0F
+ db SILPH_CO_8F,$08,$07,$10
+ db SILPH_CO_8F,$09,$07,$11
+ db SILPH_CO_9F,$08,$03,$12
+ db SILPH_CO_9F,$09,$03,$13
+ db $ff
+
+CardKeyTable3:
+ db SILPH_CO_11F,$08,$09,$14
+ db SILPH_CO_11F,$09,$09,$15
+ db $ff
+
+ItemUsePokedoll:
+ ld a, [wIsInBattle]
+ dec a
+ jp nz, ItemUseNotTime
+ ld a, $01
+ ld [wEscapedFromBattle], a
+ jp PrintItemUseTextAndRemoveItem
+
+ItemUseGuardSpec:
+ ld a, [wIsInBattle]
+ and a
+ jp z, ItemUseNotTime
+ ld hl, wPlayerBattleStatus2
+ set PROTECTED_BY_MIST, [hl] ; Mist bit
+ jp PrintItemUseTextAndRemoveItem
+
+ItemUseSuperRepel:
+ ld b, 200
+ jp ItemUseRepelCommon
+
+ItemUseMaxRepel:
+ ld b, 250
+ jp ItemUseRepelCommon
+
+ItemUseDireHit:
+ ld a, [wIsInBattle]
+ and a
+ jp z, ItemUseNotTime
+ ld hl, wPlayerBattleStatus2
+ set GETTING_PUMPED, [hl] ; Focus Energy bit
+ jp PrintItemUseTextAndRemoveItem
+
+ItemUseXStat:
+ ld a, [wIsInBattle]
+ and a
+ jr nz, .inBattle
+ call ItemUseNotTime
+ ld a, 2
+ ld [wActionResultOrTookBattleTurn], a ; item not used
+ ret
+.inBattle
+ ld hl, wPlayerMoveNum
+ ld a, [hli]
+ push af ; save [wPlayerMoveNum]
+ ld a, [hl]
+ push af ; save [wPlayerMoveEffect]
+ push hl
+ ld a, [wcf91]
+ sub X_ATTACK - ATTACK_UP1_EFFECT
+ ld [hl], a ; store player move effect
+ call PrintItemUseTextAndRemoveItem
+ ld a, XSTATITEM_ANIM ; X stat item animation ID
+ ld [wPlayerMoveNum], a
+ call LoadScreenTilesFromBuffer1 ; restore saved screen
+ call Delay3
+ xor a
+ ld [H_WHOSETURN], a ; set turn to player's turn
+ callba StatModifierUpEffect ; do stat increase move
+ pop hl
+ pop af
+ ld [hld], a ; restore [wPlayerMoveEffect]
+ pop af
+ ld [hl], a ; restore [wPlayerMoveNum]
+ ret
+
+ItemUsePokeflute:
+ ld a, [wIsInBattle]
+ and a
+ jr nz, .inBattle
+; if not in battle
+ call ItemUseReloadOverworldData
+ ld a, [wCurMap]
+ cp ROUTE_12
+ jr nz, .notRoute12
+ CheckEvent EVENT_BEAT_ROUTE12_SNORLAX
+ jr nz, .noSnorlaxToWakeUp
+; if the player hasn't beaten Route 12 Snorlax
+ ld hl, Route12SnorlaxFluteCoords
+ call ArePlayerCoordsInArray
+ jr nc, .noSnorlaxToWakeUp
+ ld hl, PlayedFluteHadEffectText
+ call PrintText
+ SetEvent EVENT_FIGHT_ROUTE12_SNORLAX
+ ret
+.notRoute12
+ cp ROUTE_16
+ jr nz, .noSnorlaxToWakeUp
+ CheckEvent EVENT_BEAT_ROUTE16_SNORLAX
+ jr nz, .noSnorlaxToWakeUp
+; if the player hasn't beaten Route 16 Snorlax
+ ld hl, Route16SnorlaxFluteCoords
+ call ArePlayerCoordsInArray
+ jr nc, .noSnorlaxToWakeUp
+ ld hl, PlayedFluteHadEffectText
+ call PrintText
+ SetEvent EVENT_FIGHT_ROUTE16_SNORLAX
+ ret
+.noSnorlaxToWakeUp
+ ld hl, PlayedFluteNoEffectText
+ jp PrintText
+.inBattle
+ xor a
+ ld [wWereAnyMonsAsleep], a
+ ld b, ~SLP & $ff
+ ld hl, wPartyMon1Status
+ call WakeUpEntireParty
+ ld a, [wIsInBattle]
+ dec a ; is it a trainer battle?
+ jr z, .skipWakingUpEnemyParty
+; if it's a trainer battle
+ ld hl, wEnemyMon1Status
+ call WakeUpEntireParty
+.skipWakingUpEnemyParty
+ ld hl, wBattleMonStatus
+ ld a, [hl]
+ and b ; remove Sleep status
+ ld [hl], a
+ ld hl, wEnemyMonStatus
+ ld a, [hl]
+ and b ; remove Sleep status
+ ld [hl], a
+ call LoadScreenTilesFromBuffer2 ; restore saved screen
+ ld a, [wWereAnyMonsAsleep]
+ and a ; were any pokemon asleep before playing the flute?
+ ld hl, PlayedFluteNoEffectText
+ jp z, PrintText ; if no pokemon were asleep
+; if some pokemon were asleep
+ ld hl, PlayedFluteHadEffectText
+ call PrintText
+ ld a, [wLowHealthAlarm]
+ and $80
+ jr nz, .skipMusic
+ call WaitForSoundToFinish ; wait for sound to end
+ callba Music_PokeFluteInBattle ; play in-battle pokeflute music
+.musicWaitLoop ; wait for music to finish playing
+ ld a, [wChannelSoundIDs + Ch6]
+ and a ; music off?
+ jr nz, .musicWaitLoop
+.skipMusic
+ ld hl, FluteWokeUpText
+ jp PrintText
+
+; wakes up all party pokemon
+; INPUT:
+; hl must point to status of first pokemon in party (player's or enemy's)
+; b must equal ~SLP
+; [wWereAnyMonsAsleep] should be initialized to 0
+; OUTPUT:
+; [wWereAnyMonsAsleep]: set to 1 if any pokemon were asleep
+WakeUpEntireParty:
+ ld de, 44
+ ld c, 6
+.loop
+ ld a, [hl]
+ push af
+ and SLP ; is pokemon asleep?
+ jr z, .notAsleep
+ ld a, 1
+ ld [wWereAnyMonsAsleep], a ; indicate that a pokemon had to be woken up
+.notAsleep
+ pop af
+ and b ; remove Sleep status
+ ld [hl], a
+ add hl, de
+ dec c
+ jr nz, .loop
+ ret
+
+; Format:
+; 00: Y
+; 01: X
+Route12SnorlaxFluteCoords:
+ db 62,9 ; one space West of Snorlax
+ db 61,10 ; one space North of Snorlax
+ db 63,10 ; one space South of Snorlax
+ db 62,11 ; one space East of Snorlax
+ db $ff ; terminator
+
+; Format:
+; 00: Y
+; 01: X
+Route16SnorlaxFluteCoords:
+ db 10,27 ; one space East of Snorlax
+ db 10,25 ; one space West of Snorlax
+ db $ff ; terminator
+
+PlayedFluteNoEffectText:
+ TX_FAR _PlayedFluteNoEffectText
+ db "@"
+
+FluteWokeUpText:
+ TX_FAR _FluteWokeUpText
+ db "@"
+
+PlayedFluteHadEffectText:
+ TX_FAR _PlayedFluteHadEffectText
+ TX_BLINK
+ TX_ASM
+ ld a, [wIsInBattle]
+ and a
+ jr nz, .done
+; play out-of-battle pokeflute music
+ ld a, $ff
+ call PlaySound ; turn off music
+ ld a, SFX_POKEFLUTE
+ ld c, BANK(SFX_Pokeflute)
+ call PlayMusic
+.musicWaitLoop ; wait for music to finish playing
+ ld a, [wChannelSoundIDs + Ch2]
+ cp SFX_POKEFLUTE
+ jr z, .musicWaitLoop
+ call PlayDefaultMusic ; start playing normal music again
+.done
+ jp TextScriptEnd ; end text
+
+ItemUseCoinCase:
+ ld a, [wIsInBattle]
+ and a
+ jp nz, ItemUseNotTime
+ ld hl, CoinCaseNumCoinsText
+ jp PrintText
+
+CoinCaseNumCoinsText:
+ TX_FAR _CoinCaseNumCoinsText
+ db "@"
+
+ItemUseOldRod:
+ call FishingInit
+ jp c, ItemUseNotTime
+ lb bc, 5, MAGIKARP
+ ld a, $1 ; set bite
+ jr RodResponse
+
+ItemUseGoodRod:
+ call FishingInit
+ jp c, ItemUseNotTime
+.RandomLoop
+ call Random
+ srl a
+ jr c, .SetBite
+ and %11
+ cp 2
+ jr nc, .RandomLoop
+ ; choose which monster appears
+ ld hl, GoodRodMons
+ add a
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld b, [hl]
+ inc hl
+ ld c, [hl]
+ and a
+.SetBite
+ ld a, 0
+ rla
+ xor 1
+ jr RodResponse
+
+INCLUDE "data/good_rod.asm"
+
+ItemUseSuperRod:
+ call FishingInit
+ jp c, ItemUseNotTime
+ call ReadSuperRodData
+ ld a, e
+RodResponse:
+ ld [wRodResponse], a
+
+ dec a ; is there a bite?
+ jr nz, .next
+ ; if yes, store level and species data
+ ld a, 1
+ ld [wMoveMissed], a
+ ld a, b ; level
+ ld [wCurEnemyLVL], a
+ ld a, c ; species
+ ld [wCurOpponent], a
+
+.next
+ ld hl, wWalkBikeSurfState
+ ld a, [hl] ; store the value in a
+ push af
+ push hl
+ ld [hl], 0
+ callba FishingAnim
+ pop hl
+ pop af
+ ld [hl], a
+ ret
+
+; checks if fishing is possible and if so, runs initialization code common to all rods
+; unsets carry if fishing is possible, sets carry if not
+FishingInit:
+ ld a, [wIsInBattle]
+ and a
+ jr z, .notInBattle
+ scf ; can't fish during battle
+ ret
+.notInBattle
+ call IsNextTileShoreOrWater
+ ret c
+ ld a, [wWalkBikeSurfState]
+ cp 2 ; Surfing?
+ jr z, .surfing
+ call ItemUseReloadOverworldData
+ ld hl, ItemUseText00
+ call PrintText
+ ld a, SFX_HEAL_AILMENT
+ call PlaySound
+ ld c, 80
+ call DelayFrames
+ and a
+ ret
+.surfing
+ scf ; can't fish when surfing
+ ret
+
+ItemUseOaksParcel:
+ jp ItemUseNotYoursToUse
+
+ItemUseItemfinder:
+ ld a, [wIsInBattle]
+ and a
+ jp nz, ItemUseNotTime
+ call ItemUseReloadOverworldData
+ callba HiddenItemNear ; check for hidden items
+ ld hl, ItemfinderFoundNothingText
+ jr nc, .printText ; if no hidden items
+ ld c, 4
+.loop
+ ld a, SFX_HEALING_MACHINE
+ call PlaySoundWaitForCurrent
+ ld a, SFX_PURCHASE
+ call PlaySoundWaitForCurrent
+ dec c
+ jr nz, .loop
+ ld hl, ItemfinderFoundItemText
+.printText
+ jp PrintText
+
+ItemfinderFoundItemText:
+ TX_FAR _ItemfinderFoundItemText
+ db "@"
+
+ItemfinderFoundNothingText:
+ TX_FAR _ItemfinderFoundNothingText
+ db "@"
+
+ItemUsePPUp:
+ ld a, [wIsInBattle]
+ and a
+ jp nz, ItemUseNotTime
+
+ItemUsePPRestore:
+ ld a, [wWhichPokemon]
+ push af
+ ld a, [wcf91]
+ ld [wPPRestoreItem], a
+.chooseMon
+ xor a
+ ld [wUpdateSpritesEnabled], a
+ ld a, USE_ITEM_PARTY_MENU
+ ld [wPartyMenuTypeOrMessageID], a
+ call DisplayPartyMenu
+ jr nc, .chooseMove
+ jp .itemNotUsed
+.chooseMove
+ ld a, [wPPRestoreItem]
+ cp ELIXER
+ jp nc, .useElixir ; if Elixir or Max Elixir
+ ld a, $02
+ ld [wMoveMenuType], a
+ ld hl, RaisePPWhichTechniqueText
+ ld a, [wPPRestoreItem]
+ cp ETHER ; is it a PP Up?
+ jr c, .printWhichTechniqueMessage ; if so, print the raise PP message
+ ld hl, RestorePPWhichTechniqueText ; otherwise, print the restore PP message
+.printWhichTechniqueMessage
+ call PrintText
+ xor a
+ ld [wPlayerMoveListIndex], a
+ callab MoveSelectionMenu ; move selection menu
+ ld a, 0
+ ld [wPlayerMoveListIndex], a
+ jr nz, .chooseMon
+ ld hl, wPartyMon1Moves
+ ld bc, wPartyMon2 - wPartyMon1
+ call GetSelectedMoveOffset
+ push hl
+ ld a, [hl]
+ ld [wd11e], a
+ call GetMoveName
+ call CopyStringToCF4B ; copy name to wcf4b
+ pop hl
+ ld a, [wPPRestoreItem]
+ cp ETHER
+ jr nc, .useEther ; if Ether or Max Ether
+.usePPUp
+ ld bc, wPartyMon1PP - wPartyMon1Moves
+ add hl, bc
+ ld a, [hl] ; move PP
+ cp 3 << 6 ; have 3 PP Ups already been used?
+ jr c, .PPNotMaxedOut
+ ld hl, PPMaxedOutText
+ call PrintText
+ jr .chooseMove
+.PPNotMaxedOut
+ ld a, [hl]
+ add 1 << 6 ; increase PP Up count by 1
+ ld [hl], a
+ ld a, 1 ; 1 PP Up used
+ ld [wd11e], a
+ call RestoreBonusPP ; add the bonus PP to current PP
+ ld hl, PPIncreasedText
+ call PrintText
+.done
+ pop af
+ ld [wWhichPokemon], a
+ call GBPalWhiteOut
+ call RunDefaultPaletteCommand
+ jp RemoveUsedItem
+.afterRestoringPP ; after using a (Max) Ether/Elixir
+ ld a, [wWhichPokemon]
+ ld b, a
+ ld a, [wPlayerMonNumber]
+ cp b ; is the pokemon whose PP was restored active in battle?
+ jr nz, .skipUpdatingInBattleData
+ ld hl, wPartyMon1PP
+ ld bc, wPartyMon2 - wPartyMon1
+ call AddNTimes
+ ld de, wBattleMonPP
+ ld bc, 4
+ call CopyData ; copy party data to in-battle data
+.skipUpdatingInBattleData
+ ld a, SFX_HEAL_AILMENT
+ call PlaySound
+ ld hl, PPRestoredText
+ call PrintText
+ jr .done
+.useEther
+ call .restorePP
+ jr nz, .afterRestoringPP
+ jp .noEffect
+; unsets zero flag if PP was restored, sets zero flag if not
+; however, this is bugged for Max Ethers and Max Elixirs (see below)
+.restorePP
+ xor a ; PLAYER_PARTY_DATA
+ ld [wMonDataLocation], a
+ call GetMaxPP
+ ld hl, wPartyMon1Moves
+ ld bc, wPartyMon2 - wPartyMon1
+ call GetSelectedMoveOffset
+ ld bc, wPartyMon1PP - wPartyMon1Moves
+ add hl, bc ; hl now points to move's PP
+ ld a, [wMaxPP]
+ ld b, a
+ ld a, [wPPRestoreItem]
+ cp MAX_ETHER
+ jr z, .fullyRestorePP
+ ld a, [hl] ; move PP
+ and %00111111 ; lower 6 bit bits store current PP
+ cp b ; does current PP equal max PP?
+ ret z ; if so, return
+ add 10 ; increase current PP by 10
+; b holds the max PP amount and b will hold the new PP amount.
+; So, if the new amount meets or exceeds the max amount,
+; cap the amount to the max amount by leaving b unchanged.
+; Otherwise, store the new amount in b.
+ cp b ; does the new amount meet or exceed the maximum?
+ jr nc, .storeNewAmount
+ ld b, a
+.storeNewAmount
+ ld a, [hl] ; move PP
+ and %11000000 ; PP Up counter bits
+ add b
+ ld [hl], a
+ ret
+.fullyRestorePP
+ ld a, [hl] ; move PP
+; Note that this code has a bug. It doesn't mask out the upper two bits, which
+; are used to count how many PP Ups have been used on the move. So, Max Ethers
+; and Max Elixirs will not be detected as having no effect on a move with full
+; PP if the move has had any PP Ups used on it.
+ cp b ; does current PP equal max PP?
+ ret z
+ jr .storeNewAmount
+.useElixir
+; decrement the item ID so that ELIXER becomes ETHER and MAX_ELIXER becomes MAX_ETHER
+ ld hl, wPPRestoreItem
+ dec [hl]
+ dec [hl]
+ xor a
+ ld hl, wCurrentMenuItem
+ ld [hli], a
+ ld [hl], a ; zero the counter for number of moves that had their PP restored
+ ld b, 4
+; loop through each move and restore PP
+.elixirLoop
+ push bc
+ ld hl, wPartyMon1Moves
+ ld bc, wPartyMon2 - wPartyMon1
+ call GetSelectedMoveOffset
+ ld a, [hl]
+ and a ; does the current slot have a move?
+ jr z, .nextMove
+ call .restorePP
+ jr z, .nextMove
+; if some PP was restored
+ ld hl, wTileBehindCursor ; counter for number of moves that had their PP restored
+ inc [hl]
+.nextMove
+ ld hl, wCurrentMenuItem
+ inc [hl]
+ pop bc
+ dec b
+ jr nz, .elixirLoop
+ ld a, [wTileBehindCursor]
+ and a ; did any moves have their PP restored?
+ jp nz, .afterRestoringPP
+.noEffect
+ call ItemUseNoEffect
+.itemNotUsed
+ call GBPalWhiteOut
+ call RunDefaultPaletteCommand
+ pop af
+ xor a
+ ld [wActionResultOrTookBattleTurn], a ; item use failed
+ ret
+
+RaisePPWhichTechniqueText:
+ TX_FAR _RaisePPWhichTechniqueText
+ db "@"
+
+RestorePPWhichTechniqueText:
+ TX_FAR _RestorePPWhichTechniqueText
+ db "@"
+
+PPMaxedOutText:
+ TX_FAR _PPMaxedOutText
+ db "@"
+
+PPIncreasedText:
+ TX_FAR _PPIncreasedText
+ db "@"
+
+PPRestoredText:
+ TX_FAR _PPRestoredText
+ db "@"
+
+; for items that can't be used from the Item menu
+UnusableItem:
+ jp ItemUseNotTime
+
+ItemUseTMHM:
+ ld a, [wIsInBattle]
+ and a
+ jp nz, ItemUseNotTime
+ ld a, [wcf91]
+ sub TM_01
+ push af
+ jr nc, .skipAdding
+ add 55 ; if item is an HM, add 55
+.skipAdding
+ inc a
+ ld [wd11e], a
+ predef TMToMove ; get move ID from TM/HM ID
+ ld a, [wd11e]
+ ld [wMoveNum], a
+ call GetMoveName
+ call CopyStringToCF4B ; copy name to wcf4b
+ pop af
+ ld hl, BootedUpTMText
+ jr nc, .printBootedUpMachineText
+ ld hl, BootedUpHMText
+.printBootedUpMachineText
+ call PrintText
+ ld hl, TeachMachineMoveText
+ call PrintText
+ coord hl, 14, 7
+ lb bc, 8, 15
+ ld a, TWO_OPTION_MENU
+ ld [wTextBoxID], a
+ call DisplayTextBoxID ; yes/no menu
+ ld a, [wCurrentMenuItem]
+ and a
+ jr z, .useMachine
+ ld a, 2
+ ld [wActionResultOrTookBattleTurn], a ; item not used
+ ret
+.useMachine
+ ld a, [wWhichPokemon]
+ push af
+ ld a, [wcf91]
+ push af
+.chooseMon
+ ld hl, wcf4b
+ ld de, wTempMoveNameBuffer
+ ld bc, 14
+ call CopyData ; save the move name because DisplayPartyMenu will overwrite it
+ ld a, $ff
+ ld [wUpdateSpritesEnabled], a
+ ld a, TMHM_PARTY_MENU
+ ld [wPartyMenuTypeOrMessageID], a
+ call DisplayPartyMenu
+ push af
+ ld hl, wTempMoveNameBuffer
+ ld de, wcf4b
+ ld bc, 14
+ call CopyData
+ pop af
+ jr nc, .checkIfAbleToLearnMove
+; if the player canceled teaching the move
+ pop af
+ pop af
+ call GBPalWhiteOutWithDelay3
+ call ClearSprites
+ call RunDefaultPaletteCommand
+ jp LoadScreenTilesFromBuffer1 ; restore saved screen
+.checkIfAbleToLearnMove
+ predef CanLearnTM ; check if the pokemon can learn the move
+ push bc
+ ld a, [wWhichPokemon]
+ ld hl, wPartyMonNicks
+ call GetPartyMonName
+ pop bc
+ ld a, c
+ and a ; can the pokemon learn the move?
+ jr nz, .checkIfAlreadyLearnedMove
+; if the pokemon can't learn the move
+ ld a, SFX_DENIED
+ call PlaySoundWaitForCurrent
+ ld hl, MonCannotLearnMachineMoveText
+ call PrintText
+ jr .chooseMon
+.checkIfAlreadyLearnedMove
+ callab CheckIfMoveIsKnown ; check if the pokemon already knows the move
+ jr c, .chooseMon
+ predef LearnMove ; teach move
+ pop af
+ ld [wcf91], a
+ pop af
+ ld [wWhichPokemon], a
+ ld a, b
+ and a
+ ret z
+ ld a, [wcf91]
+ call IsItemHM
+ ret c
+ jp RemoveUsedItem
+
+BootedUpTMText:
+ TX_FAR _BootedUpTMText
+ db "@"
+
+BootedUpHMText:
+ TX_FAR _BootedUpHMText
+ db "@"
+
+TeachMachineMoveText:
+ TX_FAR _TeachMachineMoveText
+ db "@"
+
+MonCannotLearnMachineMoveText:
+ TX_FAR _MonCannotLearnMachineMoveText
+ db "@"
+
+PrintItemUseTextAndRemoveItem:
+ ld hl, ItemUseText00
+ call PrintText
+ ld a, SFX_HEAL_AILMENT
+ call PlaySound
+ call WaitForTextScrollButtonPress ; wait for button press
+
+RemoveUsedItem:
+ ld hl, wNumBagItems
+ ld a, 1 ; one item
+ ld [wItemQuantity], a
+ jp RemoveItemFromInventory
+
+ItemUseNoEffect:
+ ld hl, ItemUseNoEffectText
+ jr ItemUseFailed
+
+ItemUseNotTime:
+ ld hl, ItemUseNotTimeText
+ jr ItemUseFailed
+
+ItemUseNotYoursToUse:
+ ld hl, ItemUseNotYoursToUseText
+ jr ItemUseFailed
+
+ThrowBallAtTrainerMon:
+ call RunDefaultPaletteCommand
+ call LoadScreenTilesFromBuffer1 ; restore saved screen
+ call Delay3
+ ld a, TOSS_ANIM
+ ld [wAnimationID], a
+ predef MoveAnimation ; do animation
+ ld hl, ThrowBallAtTrainerMonText1
+ call PrintText
+ ld hl, ThrowBallAtTrainerMonText2
+ call PrintText
+ jr RemoveUsedItem
+
+NoCyclingAllowedHere:
+ ld hl, NoCyclingAllowedHereText
+ jr ItemUseFailed
+
+BoxFullCannotThrowBall:
+ ld hl, BoxFullCannotThrowBallText
+ jr ItemUseFailed
+
+SurfingAttemptFailed:
+ ld hl, NoSurfingHereText
+
+ItemUseFailed:
+ xor a
+ ld [wActionResultOrTookBattleTurn], a ; item use failed
+ jp PrintText
+
+ItemUseNotTimeText:
+ TX_FAR _ItemUseNotTimeText
+ db "@"
+
+ItemUseNotYoursToUseText:
+ TX_FAR _ItemUseNotYoursToUseText
+ db "@"
+
+ItemUseNoEffectText:
+ TX_FAR _ItemUseNoEffectText
+ db "@"
+
+ThrowBallAtTrainerMonText1:
+ TX_FAR _ThrowBallAtTrainerMonText1
+ db "@"
+
+ThrowBallAtTrainerMonText2:
+ TX_FAR _ThrowBallAtTrainerMonText2
+ db "@"
+
+NoCyclingAllowedHereText:
+ TX_FAR _NoCyclingAllowedHereText
+ db "@"
+
+NoSurfingHereText:
+ TX_FAR _NoSurfingHereText
+ db "@"
+
+BoxFullCannotThrowBallText:
+ TX_FAR _BoxFullCannotThrowBallText
+ db "@"
+
+ItemUseText00:
+ TX_FAR _ItemUseText001
+ TX_LINE
+ TX_FAR _ItemUseText002
+ db "@"
+
+GotOnBicycleText:
+ TX_FAR _GotOnBicycleText1
+ TX_LINE
+ TX_FAR _GotOnBicycleText2
+ db "@"
+
+GotOffBicycleText:
+ TX_FAR _GotOffBicycleText1
+ TX_LINE
+ TX_FAR _GotOffBicycleText2
+ db "@"
+
+; restores bonus PP (from PP Ups) when healing at a pokemon center
+; also, when a PP Up is used, it increases the current PP by one PP Up bonus
+; INPUT:
+; [wWhichPokemon] = index of pokemon in party
+; [wCurrentMenuItem] = index of move (when using a PP Up)
+RestoreBonusPP:
+ ld hl, wPartyMon1Moves
+ ld bc, wPartyMon2 - wPartyMon1
+ ld a, [wWhichPokemon]
+ call AddNTimes
+ push hl
+ ld de, wNormalMaxPPList - 1
+ predef LoadMovePPs ; loads the normal max PP of each of the pokemon's moves to wNormalMaxPPList
+ pop hl
+ ld c, wPartyMon1PP - wPartyMon1Moves
+ ld b, 0
+ add hl, bc ; hl now points to move 1 PP
+ ld de, wNormalMaxPPList
+ ld b, 0 ; initialize move counter to zero
+; loop through the pokemon's moves
+.loop
+ inc b
+ ld a, b
+ cp 5 ; reached the end of the pokemon's moves?
+ ret z ; if so, return
+ ld a, [wUsingPPUp]
+ dec a ; using a PP Up?
+ jr nz, .skipMenuItemIDCheck
+; if using a PP Up, check if this is the move it's being used on
+ ld a, [wCurrentMenuItem]
+ inc a
+ cp b
+ jr nz, .nextMove
+.skipMenuItemIDCheck
+ ld a, [hl]
+ and %11000000 ; have any PP Ups been used?
+ call nz, AddBonusPP ; if so, add bonus PP
+.nextMove
+ inc hl
+ inc de
+ jr .loop
+
+; adds bonus PP from PP Ups to current PP
+; 1/5 of normal max PP (capped at 7) is added for each PP Up
+; INPUT:
+; [de] = normal max PP
+; [hl] = move PP
+AddBonusPP:
+ push bc
+ ld a, [de] ; normal max PP of move
+ ld [H_DIVIDEND + 3], a
+ xor a
+ ld [H_DIVIDEND], a
+ ld [H_DIVIDEND + 1], a
+ ld [H_DIVIDEND + 2], a
+ ld a, 5
+ ld [H_DIVISOR], a
+ ld b, 4
+ call Divide
+ ld a, [hl] ; move PP
+ ld b, a
+ swap a
+ and %00001111
+ srl a
+ srl a
+ ld c, a ; c = number of PP Ups used
+.loop
+ ld a, [H_QUOTIENT + 3]
+ cp 8 ; is the amount greater than or equal to 8?
+ jr c, .addAmount
+ ld a, 7 ; cap the amount at 7
+.addAmount
+ add b
+ ld b, a
+ ld a, [wUsingPPUp]
+ dec a ; is the player using a PP Up right now?
+ jr z, .done ; if so, only add the bonus once
+ dec c
+ jr nz, .loop
+.done
+ ld [hl], b
+ pop bc
+ ret
+
+; gets max PP of a pokemon's move (including PP from PP Ups)
+; INPUT:
+; [wWhichPokemon] = index of pokemon within party/box
+; [wMonDataLocation] = pokemon source
+; 00: player's party
+; 01: enemy's party
+; 02: current box
+; 03: daycare
+; 04: player's in-battle pokemon
+; [wCurrentMenuItem] = move index
+; OUTPUT:
+; [wMaxPP] = max PP
+GetMaxPP:
+ ld a, [wMonDataLocation]
+ and a
+ ld hl, wPartyMon1Moves
+ ld bc, wPartyMon2 - wPartyMon1
+ jr z, .sourceWithMultipleMon
+ ld hl, wEnemyMon1Moves
+ dec a
+ jr z, .sourceWithMultipleMon
+ ld hl, wBoxMon1Moves
+ ld bc, wBoxMon2 - wBoxMon1
+ dec a
+ jr z, .sourceWithMultipleMon
+ ld hl, wDayCareMonMoves
+ dec a
+ jr z, .sourceWithOneMon
+ ld hl, wBattleMonMoves ; player's in-battle pokemon
+.sourceWithOneMon
+ call GetSelectedMoveOffset2
+ jr .next
+.sourceWithMultipleMon
+ call GetSelectedMoveOffset
+.next
+ ld a, [hl]
+ dec a
+ push hl
+ ld hl, Moves
+ ld bc, MoveEnd - Moves
+ call AddNTimes
+ ld de, wcd6d
+ ld a, BANK(Moves)
+ call FarCopyData
+ ld de, wcd6d + 5 ; PP is byte 5 of move data
+ ld a, [de]
+ ld b, a ; b = normal max PP
+ pop hl
+ push bc
+ ld bc, wPartyMon1PP - wPartyMon1Moves ; PP offset if not player's in-battle pokemon data
+ ld a, [wMonDataLocation]
+ cp 4 ; player's in-battle pokemon?
+ jr nz, .addPPOffset
+ ld bc, wBattleMonPP - wBattleMonMoves ; PP offset if player's in-battle pokemon data
+.addPPOffset
+ add hl, bc
+ ld a, [hl] ; a = current PP
+ and %11000000 ; get PP Up count
+ pop bc
+ or b ; place normal max PP in 6 lower bits of a
+ ld h, d
+ ld l, e
+ inc hl ; hl = wcd73
+ ld [hl], a
+ xor a ; add the bonus for the existing PP Up count
+ ld [wUsingPPUp], a
+ call AddBonusPP ; add bonus PP from PP Ups
+ ld a, [hl]
+ and %00111111 ; mask out the PP Up count
+ ld [wMaxPP], a ; store max PP
+ ret
+
+GetSelectedMoveOffset:
+ ld a, [wWhichPokemon]
+ call AddNTimes
+
+GetSelectedMoveOffset2:
+ ld a, [wCurrentMenuItem]
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ret
+
+; confirms the item toss and then tosses the item
+; INPUT:
+; hl = address of inventory (either wNumBagItems or wNumBoxItems)
+; [wcf91] = item ID
+; [wWhichPokemon] = index of item within inventory
+; [wItemQuantity] = quantity to toss
+; OUTPUT:
+; clears carry flag if the item is tossed, sets carry flag if not
+TossItem_:
+ push hl
+ ld a, [wcf91]
+ call IsItemHM
+ pop hl
+ jr c, .tooImportantToToss
+ push hl
+ call IsKeyItem_
+ ld a, [wIsKeyItem]
+ pop hl
+ and a
+ jr nz, .tooImportantToToss
+ push hl
+ ld a, [wcf91]
+ ld [wd11e], a
+ call GetItemName
+ call CopyStringToCF4B ; copy name to wcf4b
+ ld hl, IsItOKToTossItemText
+ call PrintText
+ coord hl, 14, 7
+ lb bc, 8, 15
+ ld a, TWO_OPTION_MENU
+ ld [wTextBoxID], a
+ call DisplayTextBoxID ; yes/no menu
+ ld a, [wMenuExitMethod]
+ cp CHOSE_SECOND_ITEM
+ pop hl
+ scf
+ ret z ; return if the player chose No
+; if the player chose Yes
+ push hl
+ ld a, [wWhichPokemon]
+ call RemoveItemFromInventory
+ ld a, [wcf91]
+ ld [wd11e], a
+ call GetItemName
+ call CopyStringToCF4B ; copy name to wcf4b
+ ld hl, ThrewAwayItemText
+ call PrintText
+ pop hl
+ and a
+ ret
+.tooImportantToToss
+ push hl
+ ld hl, TooImportantToTossText
+ call PrintText
+ pop hl
+ scf
+ ret
+
+ThrewAwayItemText:
+ TX_FAR _ThrewAwayItemText
+ db "@"
+
+IsItOKToTossItemText:
+ TX_FAR _IsItOKToTossItemText
+ db "@"
+
+TooImportantToTossText:
+ TX_FAR _TooImportantToTossText
+ db "@"
+
+; checks if an item is a key item
+; INPUT:
+; [wcf91] = item ID
+; OUTPUT:
+; [wIsKeyItem] = result
+; 00: item is not key item
+; 01: item is key item
+IsKeyItem_:
+ ld a, $01
+ ld [wIsKeyItem], a
+ ld a, [wcf91]
+ cp HM_01 ; is the item an HM or TM?
+ jr nc, .checkIfItemIsHM
+; if the item is not an HM or TM
+ push af
+ ld hl, KeyItemBitfield
+ ld de, wBuffer
+ ld bc, 15 ; only 11 bytes are actually used
+ call CopyData
+ pop af
+ dec a
+ ld c, a
+ ld hl, wBuffer
+ ld b, FLAG_TEST
+ predef FlagActionPredef
+ ld a, c
+ and a
+ ret nz
+.checkIfItemIsHM
+ ld a, [wcf91]
+ call IsItemHM
+ ret c
+ xor a
+ ld [wIsKeyItem], a
+ ret
+
+INCLUDE "data/key_items.asm"
+
+SendNewMonToBox:
+ ld de, wNumInBox
+ ld a, [de]
+ inc a
+ ld [de], a
+ ld a, [wcf91]
+ ld [wd0b5], a
+ ld c, a
+.asm_e7b1
+ inc de
+ ld a, [de]
+ ld b, a
+ ld a, c
+ ld c, b
+ ld [de], a
+ cp $ff
+ jr nz, .asm_e7b1
+ call GetMonHeader
+ ld hl, wBoxMonOT
+ ld bc, NAME_LENGTH
+ ld a, [wNumInBox]
+ dec a
+ jr z, .asm_e7ee
+ dec a
+ call AddNTimes
+ push hl
+ ld bc, NAME_LENGTH
+ add hl, bc
+ ld d, h
+ ld e, l
+ pop hl
+ ld a, [wNumInBox]
+ dec a
+ ld b, a
+.asm_e7db
+ push bc
+ push hl
+ ld bc, NAME_LENGTH
+ call CopyData
+ pop hl
+ ld d, h
+ ld e, l
+ ld bc, -NAME_LENGTH
+ add hl, bc
+ pop bc
+ dec b
+ jr nz, .asm_e7db
+.asm_e7ee
+ ld hl, wPlayerName
+ ld de, wBoxMonOT
+ ld bc, NAME_LENGTH
+ call CopyData
+ ld a, [wNumInBox]
+ dec a
+ jr z, .asm_e82a
+ ld hl, wBoxMonNicks
+ ld bc, NAME_LENGTH
+ dec a
+ call AddNTimes
+ push hl
+ ld bc, NAME_LENGTH
+ add hl, bc
+ ld d, h
+ ld e, l
+ pop hl
+ ld a, [wNumInBox]
+ dec a
+ ld b, a
+.asm_e817
+ push bc
+ push hl
+ ld bc, NAME_LENGTH
+ call CopyData
+ pop hl
+ ld d, h
+ ld e, l
+ ld bc, -NAME_LENGTH
+ add hl, bc
+ pop bc
+ dec b
+ jr nz, .asm_e817
+.asm_e82a
+ ld hl, wBoxMonNicks
+ ld a, NAME_MON_SCREEN
+ ld [wNamingScreenType], a
+ predef AskName
+ ld a, [wNumInBox]
+ dec a
+ jr z, .asm_e867
+ ld hl, wBoxMons
+ ld bc, wBoxMon2 - wBoxMon1
+ dec a
+ call AddNTimes
+ push hl
+ ld bc, wBoxMon2 - wBoxMon1
+ add hl, bc
+ ld d, h
+ ld e, l
+ pop hl
+ ld a, [wNumInBox]
+ dec a
+ ld b, a
+.asm_e854
+ push bc
+ push hl
+ ld bc, wBoxMon2 - wBoxMon1
+ call CopyData
+ pop hl
+ ld d, h
+ ld e, l
+ ld bc, wBoxMon1 - wBoxMon2
+ add hl, bc
+ pop bc
+ dec b
+ jr nz, .asm_e854
+.asm_e867
+ ld a, [wEnemyMonLevel]
+ ld [wEnemyMonBoxLevel], a
+ ld hl, wEnemyMon
+ ld de, wBoxMon1
+ ld bc, wEnemyMonDVs - wEnemyMon
+ call CopyData
+ ld hl, wPlayerID
+ ld a, [hli]
+ ld [de], a
+ inc de
+ ld a, [hl]
+ ld [de], a
+ inc de
+ push de
+ ld a, [wCurEnemyLVL]
+ ld d, a
+ callab CalcExperience
+ pop de
+ ld a, [hExperience]
+ ld [de], a
+ inc de
+ ld a, [hExperience + 1]
+ ld [de], a
+ inc de
+ ld a, [hExperience + 2]
+ ld [de], a
+ inc de
+ xor a
+ ld b, NUM_STATS * 2
+.asm_e89f
+ ld [de], a
+ inc de
+ dec b
+ jr nz, .asm_e89f
+ ld hl, wEnemyMonDVs
+ ld a, [hli]
+ ld [de], a
+ inc de
+ ld a, [hli]
+ ld [de], a
+ ld hl, wEnemyMonPP
+ ld b, NUM_MOVES
+.asm_e8b1
+ ld a, [hli]
+ inc de
+ ld [de], a
+ dec b
+ jr nz, .asm_e8b1
+ ret
+
+; checks if the tile in front of the player is a shore or water tile
+; used for surfing and fishing
+; unsets carry if it is, sets carry if not
+IsNextTileShoreOrWater:
+ ld a, [wCurMapTileset]
+ ld hl, WaterTilesets
+ ld de, 1
+ call IsInArray
+ jr nc, .notShoreOrWater
+ ld a, [wCurMapTileset]
+ cp SHIP_PORT ; Vermilion Dock tileset
+ ld a, [wTileInFrontOfPlayer] ; tile in front of player
+ jr z, .skipShoreTiles ; if it's the Vermilion Dock tileset
+ cp $48 ; eastern shore tile in Safari Zone
+ jr z, .shoreOrWater
+ cp $32 ; usual eastern shore tile
+ jr z, .shoreOrWater
+.skipShoreTiles
+ cp $14 ; water tile
+ jr z, .shoreOrWater
+.notShoreOrWater
+ scf
+ ret
+.shoreOrWater
+ and a
+ ret
+
+; tilesets with water
+WaterTilesets:
+ db OVERWORLD, FOREST, DOJO, GYM, SHIP, SHIP_PORT, CAVERN, FACILITY, PLATEAU
+ db $ff ; terminator
+
+ReadSuperRodData:
+; return e = 2 if no fish on this map
+; return e = 1 if a bite, bc = level,species
+; return e = 0 if no bite
+ ld a, [wCurMap]
+ ld de, 3 ; each fishing group is three bytes wide
+ ld hl, SuperRodData
+ call IsInArray
+ jr c, .ReadFishingGroup
+ ld e, $2 ; $2 if no fishing groups found
+ ret
+
+.ReadFishingGroup
+; hl points to the fishing group entry in the index
+ inc hl ; skip map id
+
+ ; read fishing group address
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+
+ ld b, [hl] ; how many mons in group
+ inc hl ; point to data
+ ld e, $0 ; no bite yet
+
+.RandomLoop
+ call Random
+ srl a
+ ret c ; 50% chance of no battle
+
+ and %11 ; 2-bit random number
+ cp b
+ jr nc, .RandomLoop ; if a is greater than the number of mons, regenerate
+
+ ; get the mon
+ add a
+ ld c, a
+ ld b, $0
+ add hl, bc
+ ld b, [hl] ; level
+ inc hl
+ ld c, [hl] ; species
+ ld e, $1 ; $1 if there's a bite
+ ret
+
+INCLUDE "data/super_rod.asm"
+
+; reloads map view and processes sprite data
+; for items that cause the overworld to be displayed
+ItemUseReloadOverworldData:
+ call LoadCurrentMapView
+ jp UpdateSprites
+
+; creates a list at wBuffer of maps where the mon in [wd11e] can be found.
+; this is used by the pokedex to display locations the mon can be found on the map.
+FindWildLocationsOfMon:
+ ld hl, WildDataPointers
+ ld de, wBuffer
+ ld c, $0
+.loop
+ inc hl
+ ld a, [hld]
+ inc a
+ jr z, .done
+ push hl
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld a, [hli]
+ and a
+ call nz, CheckMapForMon ; land
+ ld a, [hli]
+ and a
+ call nz, CheckMapForMon ; water
+ pop hl
+ inc hl
+ inc hl
+ inc c
+ jr .loop
+.done
+ ld a, $ff ; list terminator
+ ld [de], a
+ ret
+
+CheckMapForMon:
+ inc hl
+ ld b, $a
+.loop
+ ld a, [wd11e]
+ cp [hl]
+ jr nz, .nextEntry
+ ld a, c
+ ld [de], a
+ inc de
+.nextEntry
+ inc hl
+ inc hl
+ dec b
+ jr nz, .loop
+ dec hl
+ ret
diff --git a/en/engine/learn_move.asm b/en/engine/learn_move.asm
new file mode 100755
index 00000000..53c7f87e
--- /dev/null
+++ b/en/engine/learn_move.asm
@@ -0,0 +1,226 @@
+LearnMove:
+ call SaveScreenTilesToBuffer1
+ ld a, [wWhichPokemon]
+ ld hl, wPartyMonNicks
+ call GetPartyMonName
+ ld hl, wcd6d
+ ld de, wLearnMoveMonName
+ ld bc, NAME_LENGTH
+ call CopyData
+
+DontAbandonLearning:
+ ld hl, wPartyMon1Moves
+ ld bc, wPartyMon2Moves - wPartyMon1Moves
+ ld a, [wWhichPokemon]
+ call AddNTimes
+ ld d, h
+ ld e, l
+ ld b, NUM_MOVES
+.findEmptyMoveSlotLoop
+ ld a, [hl]
+ and a
+ jr z, .next
+ inc hl
+ dec b
+ jr nz, .findEmptyMoveSlotLoop
+ push de
+ call TryingToLearn
+ pop de
+ jp c, AbandonLearning
+ push hl
+ push de
+ ld [wd11e], a
+ call GetMoveName
+ ld hl, OneTwoAndText
+ call PrintText
+ pop de
+ pop hl
+.next
+ ld a, [wMoveNum]
+ ld [hl], a
+ ld bc, wPartyMon1PP - wPartyMon1Moves
+ add hl, bc
+ push hl
+ push de
+ dec a
+ ld hl, Moves
+ ld bc, MoveEnd - Moves
+ call AddNTimes
+ ld de, wBuffer
+ ld a, BANK(Moves)
+ call FarCopyData
+ ld a, [wBuffer + 5] ; a = move's max PP
+ pop de
+ pop hl
+ ld [hl], a
+ ld a, [wIsInBattle]
+ and a
+ jp z, PrintLearnedMove
+ ld a, [wWhichPokemon]
+ ld b, a
+ ld a, [wPlayerMonNumber]
+ cp b
+ jp nz, PrintLearnedMove
+ ld h, d
+ ld l, e
+ ld de, wBattleMonMoves
+ ld bc, NUM_MOVES
+ call CopyData
+ ld bc, wPartyMon1PP - wPartyMon1OTID
+ add hl, bc
+ ld de, wBattleMonPP
+ ld bc, NUM_MOVES
+ call CopyData
+ jp PrintLearnedMove
+
+AbandonLearning:
+ ld hl, AbandonLearningText
+ call PrintText
+ coord hl, 14, 7
+ lb bc, 8, 15
+ ld a, TWO_OPTION_MENU
+ ld [wTextBoxID], a
+ call DisplayTextBoxID ; yes/no menu
+ ld a, [wCurrentMenuItem]
+ and a
+ jp nz, DontAbandonLearning
+ ld hl, DidNotLearnText
+ call PrintText
+ ld b, 0
+ ret
+
+PrintLearnedMove:
+ ld hl, LearnedMove1Text
+ call PrintText
+ ld b, 1
+ ret
+
+TryingToLearn:
+ push hl
+ ld hl, TryingToLearnText
+ call PrintText
+ coord hl, 14, 7
+ lb bc, 8, 15
+ ld a, TWO_OPTION_MENU
+ ld [wTextBoxID], a
+ call DisplayTextBoxID ; yes/no menu
+ pop hl
+ ld a, [wCurrentMenuItem]
+ rra
+ ret c
+ ld bc, -NUM_MOVES
+ add hl, bc
+ push hl
+ ld de, wMoves
+ ld bc, NUM_MOVES
+ call CopyData
+ callab FormatMovesString
+ pop hl
+.loop
+ push hl
+ ld hl, WhichMoveToForgetText
+ call PrintText
+ coord hl, 4, 7
+ ld b, 4
+ ld c, 14
+ call TextBoxBorder
+ coord hl, 6, 8
+ 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
+ ld hl, wTopMenuItemY
+ ld a, 8
+ ld [hli], a ; wTopMenuItemY
+ ld a, 5
+ ld [hli], a ; wTopMenuItemX
+ xor a
+ ld [hli], a ; wCurrentMenuItem
+ inc hl
+ ld a, [wNumMovesMinusOne]
+ ld [hli], a ; wMaxMenuItem
+ ld a, A_BUTTON | B_BUTTON
+ ld [hli], a ; wMenuWatchedKeys
+ ld [hl], 0 ; wLastMenuItem
+ ld hl, hFlags_0xFFF6
+ set 1, [hl]
+ call HandleMenuInput
+ ld hl, hFlags_0xFFF6
+ res 1, [hl]
+ push af
+ call LoadScreenTilesFromBuffer1
+ pop af
+ pop hl
+ bit 1, a ; pressed b
+ jr nz, .cancel
+ push hl
+ ld a, [wCurrentMenuItem]
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld a, [hl]
+ push af
+ push bc
+ call IsMoveHM
+ pop bc
+ pop de
+ ld a, d
+ jr c, .hm
+ pop hl
+ add hl, bc
+ and a
+ ret
+.hm
+ ld hl, HMCantDeleteText
+ call PrintText
+ pop hl
+ jr .loop
+.cancel
+ scf
+ ret
+
+LearnedMove1Text:
+ TX_FAR _LearnedMove1Text
+ TX_SFX_ITEM_1 ; plays SFX_GET_ITEM_1 in the party menu (rare candy) and plays SFX_LEVEL_UP in battle
+ TX_BLINK
+ db "@"
+
+WhichMoveToForgetText:
+ TX_FAR _WhichMoveToForgetText
+ db "@"
+
+AbandonLearningText:
+ TX_FAR _AbandonLearningText
+ db "@"
+
+DidNotLearnText:
+ TX_FAR _DidNotLearnText
+ db "@"
+
+TryingToLearnText:
+ TX_FAR _TryingToLearnText
+ db "@"
+
+OneTwoAndText:
+ TX_FAR _OneTwoAndText
+ TX_DELAY
+ TX_ASM
+ ld a, SFX_SWAP
+ call PlaySoundWaitForCurrent
+ ld hl, PoofText
+ ret
+
+PoofText:
+ TX_FAR _PoofText
+ TX_DELAY
+ForgotAndText:
+ TX_FAR _ForgotAndText
+ db "@"
+
+HMCantDeleteText:
+ TX_FAR _HMCantDeleteText
+ db "@"
diff --git a/en/engine/menu/bills_pc.asm b/en/engine/menu/bills_pc.asm
new file mode 100644
index 00000000..85a546e1
--- /dev/null
+++ b/en/engine/menu/bills_pc.asm
@@ -0,0 +1,554 @@
+DisplayPCMainMenu::
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call SaveScreenTilesToBuffer2
+ ld a, [wNumHoFTeams]
+ and a
+ jr nz, .leaguePCAvailable
+ CheckEvent EVENT_GOT_POKEDEX
+ jr z, .noOaksPC
+ ld a, [wNumHoFTeams]
+ and a
+ jr nz, .leaguePCAvailable
+ coord hl, 0, 0
+ ld b, 8
+ ld c, 14
+ jr .next
+.noOaksPC
+ coord hl, 0, 0
+ ld b, 6
+ ld c, 14
+ jr .next
+.leaguePCAvailable
+ coord hl, 0, 0
+ ld b, 10
+ ld c, 14
+.next
+ call TextBoxBorder
+ call UpdateSprites
+ ld a, 3
+ ld [wMaxMenuItem], a
+ CheckEvent EVENT_MET_BILL
+ jr nz, .metBill
+ coord hl, 2, 2
+ ld de, SomeonesPCText
+ jr .next2
+.metBill
+ coord hl, 2, 2
+ ld de, BillsPCText
+.next2
+ call PlaceString
+ coord hl, 2, 4
+ ld de, wPlayerName
+ call PlaceString
+ ld l, c
+ ld h, b
+ ld de, PlayersPCText
+ call PlaceString
+ CheckEvent EVENT_GOT_POKEDEX
+ jr z, .noOaksPC2
+ coord hl, 2, 6
+ ld de, OaksPCText
+ call PlaceString
+ ld a, [wNumHoFTeams]
+ and a
+ jr z, .noLeaguePC
+ ld a, 4
+ ld [wMaxMenuItem], a
+ coord hl, 2, 8
+ ld de, PKMNLeaguePCText
+ call PlaceString
+ coord hl, 2, 10
+ ld de, LogOffPCText
+ jr .next3
+.noLeaguePC
+ coord hl, 2, 8
+ ld de, LogOffPCText
+ jr .next3
+.noOaksPC2
+ ld a, $2
+ ld [wMaxMenuItem], a
+ coord hl, 2, 6
+ ld de, LogOffPCText
+.next3
+ call PlaceString
+ ld a, A_BUTTON | B_BUTTON
+ ld [wMenuWatchedKeys], a
+ ld a, 2
+ ld [wTopMenuItemY], a
+ ld a, 1
+ ld [wTopMenuItemX], a
+ xor a
+ ld [wCurrentMenuItem], a
+ ld [wLastMenuItem], a
+ ld a, 1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ret
+
+SomeonesPCText: db "SOMEONE's PC@"
+BillsPCText: db "BILL's PC@"
+PlayersPCText: db "'s PC@"
+OaksPCText: db "PROF.OAK's PC@"
+PKMNLeaguePCText: db $4a, "LEAGUE@"
+LogOffPCText: db "LOG OFF@"
+
+BillsPC_::
+ ld hl, wd730
+ set 6, [hl]
+ xor a
+ ld [wParentMenuItem], a
+ inc a ; MONSTER_NAME
+ ld [wNameListType], a
+ call LoadHpBarAndStatusTilePatterns
+ ld a, [wListScrollOffset]
+ push af
+ ld a, [wFlags_0xcd60]
+ bit 3, a ; accessing Bill's PC through another PC?
+ jr nz, BillsPCMenu
+; accessing it directly
+ ld a, $99
+ call PlaySound
+ ld hl, SwitchOnText
+ call PrintText
+
+BillsPCMenu:
+ ld a, [wParentMenuItem]
+ ld [wCurrentMenuItem], a
+ ld hl, vChars2 + $780
+ ld de, PokeballTileGraphics
+ lb bc, BANK(PokeballTileGraphics), $01
+ call CopyVideoData
+ call LoadScreenTilesFromBuffer2DisableBGTransfer
+ coord hl, 0, 0
+ ld b, 10
+ ld c, 12
+ call TextBoxBorder
+ coord hl, 2, 2
+ ld de, BillsPCMenuText
+ call PlaceString
+ ld hl, wTopMenuItemY
+ ld a, 2
+ ld [hli], a ; wTopMenuItemY
+ dec a
+ ld [hli], a ; wTopMenuItemX
+ inc hl
+ inc hl
+ ld a, 4
+ ld [hli], a ; wMaxMenuItem
+ ld a, A_BUTTON | B_BUTTON
+ ld [hli], a ; wMenuWatchedKeys
+ xor a
+ ld [hli], a ; wLastMenuItem
+ ld [hli], a ; wPartyAndBillsPCSavedMenuItem
+ ld hl, wListScrollOffset
+ ld [hli], a ; wListScrollOffset
+ ld [hl], a ; wMenuWatchMovingOutOfBounds
+ ld [wPlayerMonNumber], a
+ ld hl, WhatText
+ call PrintText
+ coord hl, 9, 14
+ ld b, 2
+ ld c, 9
+ call TextBoxBorder
+ ld a, [wCurrentBoxNum]
+ and $7f
+ cp 9
+ jr c, .singleDigitBoxNum
+; two digit box num
+ sub 9
+ coord hl, 17, 16
+ ld [hl], "1"
+ add "0"
+ jr .next
+.singleDigitBoxNum
+ add "1"
+.next
+ Coorda 18, 16
+ coord hl, 10, 16
+ ld de, BoxNoPCText
+ call PlaceString
+ ld a, 1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call Delay3
+ call HandleMenuInput
+ bit 1, a
+ jp nz, ExitBillsPC ; b button
+ call PlaceUnfilledArrowMenuCursor
+ ld a, [wCurrentMenuItem]
+ ld [wParentMenuItem], a
+ and a
+ jp z, BillsPCWithdraw ; withdraw
+ cp $1
+ jp z, BillsPCDeposit ; deposit
+ cp $2
+ jp z, BillsPCRelease ; release
+ cp $3
+ jp z, BillsPCChangeBox ; change box
+
+ExitBillsPC:
+ ld a, [wFlags_0xcd60]
+ bit 3, a ; accessing Bill's PC through another PC?
+ jr nz, .next
+; accessing it directly
+ call LoadTextBoxTilePatterns
+ ld a, $9a
+ call PlaySound
+ call WaitForSoundToFinish
+.next
+ ld hl, wFlags_0xcd60
+ res 5, [hl]
+ call LoadScreenTilesFromBuffer2
+ pop af
+ ld [wListScrollOffset], a
+ ld hl, wd730
+ res 6, [hl]
+ ret
+
+BillsPCDeposit:
+ ld a, [wPartyCount]
+ dec a
+ jr nz, .partyLargeEnough
+ ld hl, CantDepositLastMonText
+ call PrintText
+ jp BillsPCMenu
+.partyLargeEnough
+ ld a, [wNumInBox]
+ cp MONS_PER_BOX
+ jr nz, .boxNotFull
+ ld hl, BoxFullText
+ call PrintText
+ jp BillsPCMenu
+.boxNotFull
+ ld hl, wPartyCount
+ call DisplayMonListMenu
+ jp c, BillsPCMenu
+ call DisplayDepositWithdrawMenu
+ jp nc, BillsPCMenu
+ ld a, [wcf91]
+ call GetCryData
+ call PlaySoundWaitForCurrent
+ ld a, PARTY_TO_BOX
+ ld [wMoveMonType], a
+ call MoveMon
+ xor a
+ ld [wRemoveMonFromBox], a
+ call RemovePokemon
+ call WaitForSoundToFinish
+ ld hl, wBoxNumString
+ ld a, [wCurrentBoxNum]
+ and $7f
+ cp 9
+ jr c, .singleDigitBoxNum
+ sub 9
+ ld [hl], "1"
+ inc hl
+ add "0"
+ jr .next
+.singleDigitBoxNum
+ add "1"
+.next
+ ld [hli], a
+ ld [hl], "@"
+ ld hl, MonWasStoredText
+ call PrintText
+ jp BillsPCMenu
+
+BillsPCWithdraw:
+ ld a, [wNumInBox]
+ and a
+ jr nz, .boxNotEmpty
+ ld hl, NoMonText
+ call PrintText
+ jp BillsPCMenu
+.boxNotEmpty
+ ld a, [wPartyCount]
+ cp PARTY_LENGTH
+ jr nz, .partyNotFull
+ ld hl, CantTakeMonText
+ call PrintText
+ jp BillsPCMenu
+.partyNotFull
+ ld hl, wNumInBox
+ call DisplayMonListMenu
+ jp c, BillsPCMenu
+ call DisplayDepositWithdrawMenu
+ jp nc, BillsPCMenu
+ ld a, [wWhichPokemon]
+ ld hl, wBoxMonNicks
+ call GetPartyMonName
+ ld a, [wcf91]
+ call GetCryData
+ call PlaySoundWaitForCurrent
+ xor a ; BOX_TO_PARTY
+ ld [wMoveMonType], a
+ call MoveMon
+ ld a, 1
+ ld [wRemoveMonFromBox], a
+ call RemovePokemon
+ call WaitForSoundToFinish
+ ld hl, MonIsTakenOutText
+ call PrintText
+ jp BillsPCMenu
+
+BillsPCRelease:
+ ld a, [wNumInBox]
+ and a
+ jr nz, .loop
+ ld hl, NoMonText
+ call PrintText
+ jp BillsPCMenu
+.loop
+ ld hl, wNumInBox
+ call DisplayMonListMenu
+ jp c, BillsPCMenu
+ ld hl, OnceReleasedText
+ call PrintText
+ call YesNoChoice
+ ld a, [wCurrentMenuItem]
+ and a
+ jr nz, .loop
+ inc a
+ ld [wRemoveMonFromBox], a
+ call RemovePokemon
+ call WaitForSoundToFinish
+ ld a, [wcf91]
+ call PlayCry
+ ld hl, MonWasReleasedText
+ call PrintText
+ jp BillsPCMenu
+
+BillsPCChangeBox:
+ callba ChangeBox
+ jp BillsPCMenu
+
+DisplayMonListMenu:
+ ld a, l
+ ld [wListPointer], a
+ ld a, h
+ ld [wListPointer + 1], a
+ xor a
+ ld [wPrintItemPrices], a
+ ld [wListMenuID], a
+ inc a ; MONSTER_NAME
+ ld [wNameListType], a
+ ld a, [wPartyAndBillsPCSavedMenuItem]
+ ld [wCurrentMenuItem], a
+ call DisplayListMenuID
+ ld a, [wCurrentMenuItem]
+ ld [wPartyAndBillsPCSavedMenuItem], a
+ ret
+
+BillsPCMenuText:
+ db "WITHDRAW ", $4a
+ next "DEPOSIT ", $4a
+ next "RELEASE ", $4a
+ next "CHANGE BOX"
+ next "SEE YA!"
+ db "@"
+
+BoxNoPCText:
+ db "BOX No.@"
+
+KnowsHMMove::
+; returns whether mon with party index [wWhichPokemon] knows an HM move
+ ld hl, wPartyMon1Moves
+ ld bc, wPartyMon2 - wPartyMon1
+ jr .next
+; unreachable
+ ld hl, wBoxMon1Moves
+ ld bc, wBoxMon2 - wBoxMon1
+.next
+ ld a, [wWhichPokemon]
+ call AddNTimes
+ ld b, NUM_MOVES
+.loop
+ ld a, [hli]
+ push hl
+ push bc
+ ld hl, HMMoveArray
+ ld de, 1
+ call IsInArray
+ pop bc
+ pop hl
+ ret c
+ dec b
+ jr nz, .loop
+ and a
+ ret
+
+HMMoveArray:
+ db CUT
+ db FLY
+ db SURF
+ db STRENGTH
+ db FLASH
+ db -1
+
+DisplayDepositWithdrawMenu:
+ coord hl, 9, 10
+ ld b, 6
+ ld c, 9
+ call TextBoxBorder
+ ld a, [wParentMenuItem]
+ and a ; was the Deposit or Withdraw item selected in the parent menu?
+ ld de, DepositPCText
+ jr nz, .next
+ ld de, WithdrawPCText
+.next
+ coord hl, 11, 12
+ call PlaceString
+ coord hl, 11, 14
+ ld de, StatsCancelPCText
+ call PlaceString
+ ld hl, wTopMenuItemY
+ ld a, 12
+ ld [hli], a ; wTopMenuItemY
+ ld a, 10
+ ld [hli], a ; wTopMenuItemX
+ xor a
+ ld [hli], a ; wCurrentMenuItem
+ inc hl
+ ld a, 2
+ ld [hli], a ; wMaxMenuItem
+ ld a, A_BUTTON | B_BUTTON
+ ld [hli], a ; wMenuWatchedKeys
+ xor a
+ ld [hl], a ; wLastMenuItem
+ ld hl, wListScrollOffset
+ ld [hli], a ; wListScrollOffset
+ ld [hl], a ; wMenuWatchMovingOutOfBounds
+ ld [wPlayerMonNumber], a
+ ld [wPartyAndBillsPCSavedMenuItem], a
+.loop
+ call HandleMenuInput
+ bit 1, a ; pressed B?
+ jr nz, .exit
+ ld a, [wCurrentMenuItem]
+ and a
+ jr z, .choseDepositWithdraw
+ dec a
+ jr z, .viewStats
+.exit
+ and a
+ ret
+.choseDepositWithdraw
+ scf
+ ret
+.viewStats
+ call SaveScreenTilesToBuffer1
+ ld a, [wParentMenuItem]
+ and a
+ ld a, PLAYER_PARTY_DATA
+ jr nz, .next2
+ ld a, BOX_DATA
+.next2
+ ld [wMonDataLocation], a
+ predef StatusScreen
+ predef StatusScreen2
+ call LoadScreenTilesFromBuffer1
+ call ReloadTilesetTilePatterns
+ call RunDefaultPaletteCommand
+ call LoadGBPal
+ jr .loop
+
+DepositPCText: db "DEPOSIT@"
+WithdrawPCText: db "WITHDRAW@"
+StatsCancelPCText:
+ db "STATS"
+ next "CANCEL@"
+
+SwitchOnText:
+ TX_FAR _SwitchOnText
+ db "@"
+
+WhatText:
+ TX_FAR _WhatText
+ db "@"
+
+DepositWhichMonText:
+ TX_FAR _DepositWhichMonText
+ db "@"
+
+MonWasStoredText:
+ TX_FAR _MonWasStoredText
+ db "@"
+
+CantDepositLastMonText:
+ TX_FAR _CantDepositLastMonText
+ db "@"
+
+BoxFullText:
+ TX_FAR _BoxFullText
+ db "@"
+
+MonIsTakenOutText:
+ TX_FAR _MonIsTakenOutText
+ db "@"
+
+NoMonText:
+ TX_FAR _NoMonText
+ db "@"
+
+CantTakeMonText:
+ TX_FAR _CantTakeMonText
+ db "@"
+
+ReleaseWhichMonText:
+ TX_FAR _ReleaseWhichMonText
+ db "@"
+
+OnceReleasedText:
+ TX_FAR _OnceReleasedText
+ db "@"
+
+MonWasReleasedText:
+ TX_FAR _MonWasReleasedText
+ db "@"
+
+CableClubLeftGameboy::
+ ld a, [hSerialConnectionStatus]
+ cp USING_EXTERNAL_CLOCK
+ ret z
+ ld a, [wSpriteStateData1 + 9] ; player's sprite facing direction
+ cp SPRITE_FACING_RIGHT
+ ret nz
+ ld a, [wCurMap]
+ cp TRADE_CENTER
+ ld a, LINK_STATE_START_TRADE
+ jr z, .next
+ inc a ; LINK_STATE_START_BATTLE
+.next
+ ld [wLinkState], a
+ call EnableAutoTextBoxDrawing
+ tx_pre_jump JustAMomentText
+
+CableClubRightGameboy::
+ ld a, [hSerialConnectionStatus]
+ cp USING_INTERNAL_CLOCK
+ ret z
+ ld a, [wSpriteStateData1 + 9] ; player's sprite facing direction
+ cp SPRITE_FACING_LEFT
+ ret nz
+ ld a, [wCurMap]
+ cp TRADE_CENTER
+ ld a, LINK_STATE_START_TRADE
+ jr z, .next
+ inc a ; LINK_STATE_START_BATTLE
+.next
+ ld [wLinkState], a
+ call EnableAutoTextBoxDrawing
+ tx_pre_jump JustAMomentText
+
+JustAMomentText::
+ TX_FAR _JustAMomentText
+ db "@"
+
+ ld a, [wSpriteStateData1 + 9] ; player's sprite facing direction
+ cp SPRITE_FACING_UP
+ ret nz
+ call EnableAutoTextBoxDrawing
+ tx_pre_jump OpenBillsPCText
+
+OpenBillsPCText::
+ db $FD ; FuncTX_BillsPC
+
diff --git a/en/engine/menu/diploma.asm b/en/engine/menu/diploma.asm
new file mode 100755
index 00000000..09ba123e
--- /dev/null
+++ b/en/engine/menu/diploma.asm
@@ -0,0 +1,113 @@
+DisplayDiploma:
+ call SaveScreenTilesToBuffer2
+ call GBPalWhiteOutWithDelay3
+ call ClearScreen
+ xor a
+ ld [wUpdateSpritesEnabled], a
+ ld hl, wd730
+ set 6, [hl]
+ call DisableLCD
+ ld hl, CircleTile
+ ld de, vChars2 + $700
+ ld bc, $0010
+ ld a, BANK(CircleTile)
+ call FarCopyData2
+ coord hl, 0, 0
+ lb bc, 16, 18
+ predef Diploma_TextBoxBorder
+ ld hl, DiplomaTextPointersAndCoords
+ ld c, $5
+.asm_56715
+ push bc
+ ld a, [hli]
+ ld e, a
+ ld a, [hli]
+ ld d, a
+ ld a, [hli]
+ push hl
+ ld h, [hl]
+ ld l, a
+ call PlaceString
+ pop hl
+ inc hl
+ pop bc
+ dec c
+ jr nz, .asm_56715
+ coord hl, 10, 4
+ ld de, wPlayerName
+ call PlaceString
+ callba DrawPlayerCharacter
+
+; Move the player 33 pixels right and set the priority bit so he appears
+; behind the background layer.
+ ld hl, wOAMBuffer + $01
+ lb bc, $80, $28
+.adjustPlayerGfxLoop
+ ld a, [hl] ; X
+ add 33
+ ld [hli], a
+ inc hl
+ ld a, b
+ ld [hli], a ; attributes
+ inc hl
+ dec c
+ jr nz, .adjustPlayerGfxLoop
+
+ call EnableLCD
+ callba LoadTrainerInfoTextBoxTiles
+ ld b, SET_PAL_GENERIC
+ call RunPaletteCommand
+ call Delay3
+ call GBPalNormal
+ ld a, $90
+ ld [rOBP0], a
+ call WaitForTextScrollButtonPress
+ ld hl, wd730
+ res 6, [hl]
+ call GBPalWhiteOutWithDelay3
+ call RestoreScreenTilesAndReloadTilePatterns
+ call Delay3
+ jp GBPalNormal
+
+UnusedPlayerNameLengthFunc:
+; Unused function that does a calculation involving the length of the player's
+; name.
+ ld hl, wPlayerName
+ ld bc, $ff00
+.loop
+ ld a, [hli]
+ cp "@"
+ ret z
+ dec c
+ jr .loop
+
+DiplomaTextPointersAndCoords:
+ dw DiplomaText
+ dwCoord 5, 2
+ dw DiplomaPlayer
+ dwCoord 3, 4
+ dw DiplomaEmptyText
+ dwCoord 15, 4
+ dw DiplomaCongrats
+ dwCoord 2, 6
+ dw DiplomaGameFreak
+ dwCoord 9, 16
+
+DiplomaText:
+ db $70,"Diploma",$70,"@"
+
+DiplomaPlayer:
+ db "Player@"
+
+DiplomaEmptyText:
+ db "@"
+
+DiplomaCongrats:
+ db "Congrats! This"
+ next "diploma certifies"
+ next "that you have"
+ next "completed your"
+ next "#DEX.@"
+
+DiplomaGameFreak:
+ db "GAME FREAK@"
diff --git a/en/engine/menu/draw_start_menu.asm b/en/engine/menu/draw_start_menu.asm
new file mode 100644
index 00000000..11777dc6
--- /dev/null
+++ b/en/engine/menu/draw_start_menu.asm
@@ -0,0 +1,89 @@
+; function that displays the start menu
+DrawStartMenu:
+ CheckEvent EVENT_GOT_POKEDEX
+; menu with pokedex
+ coord hl, 10, 0
+ ld b, $0e
+ ld c, $08
+ jr nz, .drawTextBoxBorder
+; shorter menu if the player doesn't have the pokedex
+ coord hl, 10, 0
+ ld b, $0c
+ ld c, $08
+.drawTextBoxBorder
+ call TextBoxBorder
+ ld a, D_DOWN | D_UP | START | B_BUTTON | A_BUTTON
+ ld [wMenuWatchedKeys], a
+ ld a, $02
+ ld [wTopMenuItemY], a ; Y position of first menu choice
+ ld a, $0b
+ ld [wTopMenuItemX], a ; X position of first menu choice
+ ld a, [wBattleAndStartSavedMenuItem] ; remembered menu selection from last time
+ ld [wCurrentMenuItem], a
+ ld [wLastMenuItem], a
+ xor a
+ ld [wMenuWatchMovingOutOfBounds], a
+ ld hl, wd730
+ set 6, [hl] ; no pauses between printing each letter
+ coord hl, 12, 2
+ CheckEvent EVENT_GOT_POKEDEX
+; case for not having pokedex
+ ld a, $06
+ jr z, .storeMenuItemCount
+; case for having pokedex
+ ld de, StartMenuPokedexText
+ call PrintStartMenuItem
+ ld a, $07
+.storeMenuItemCount
+ ld [wMaxMenuItem], a ; number of menu items
+ ld de, StartMenuPokemonText
+ call PrintStartMenuItem
+ ld de, StartMenuItemText
+ call PrintStartMenuItem
+ ld de, wPlayerName ; player's name
+ call PrintStartMenuItem
+ ld a, [wd72e]
+ bit 6, a ; is the player using the link feature?
+; case for not using link feature
+ ld de, StartMenuSaveText
+ jr z, .printSaveOrResetText
+; case for using link feature
+ ld de, StartMenuResetText
+.printSaveOrResetText
+ call PrintStartMenuItem
+ ld de, StartMenuOptionText
+ call PrintStartMenuItem
+ ld de, StartMenuExitText
+ call PlaceString
+ ld hl, wd730
+ res 6, [hl] ; turn pauses between printing letters back on
+ ret
+
+StartMenuPokedexText:
+ db "POKéDEX@"
+
+StartMenuPokemonText:
+ db "POKéMON@"
+
+StartMenuItemText:
+ db "ITEM@"
+
+StartMenuSaveText:
+ db "SAVE@"
+
+StartMenuResetText:
+ db "RESET@"
+
+StartMenuExitText:
+ db "EXIT@"
+
+StartMenuOptionText:
+ db "OPTION@"
+
+PrintStartMenuItem:
+ push hl
+ call PlaceString
+ pop hl
+ ld de, SCREEN_WIDTH * 2
+ add hl, de
+ ret
diff --git a/en/engine/menu/league_pc.asm b/en/engine/menu/league_pc.asm
new file mode 100755
index 00000000..170c0ef3
--- /dev/null
+++ b/en/engine/menu/league_pc.asm
@@ -0,0 +1,120 @@
+PKMNLeaguePC:
+ ld hl, AccessedHoFPCText
+ call PrintText
+ ld hl, wd730
+ set 6, [hl]
+ push hl
+ ld a, [wUpdateSpritesEnabled]
+ push af
+ ld a, [hTilesetType]
+ push af
+ xor a
+ ld [hTilesetType], a
+ ld [wSpriteFlipped], a
+ ld [wUpdateSpritesEnabled], a
+ ld [wHoFTeamIndex2], a
+ ld [wHoFTeamNo], a
+ ld a, [wNumHoFTeams]
+ ld b, a
+ cp HOF_TEAM_CAPACITY + 1
+ jr c, .loop
+; If the total number of hall of fame teams is greater than the storage
+; capacity, then calculate the number of the first team that is still recorded.
+ ld b, HOF_TEAM_CAPACITY
+ sub b
+ ld [wHoFTeamNo], a
+.loop
+ ld hl, wHoFTeamNo
+ inc [hl]
+ push bc
+ ld a, [wHoFTeamIndex2]
+ ld [wHoFTeamIndex], a
+ callba LoadHallOfFameTeams
+ call LeaguePCShowTeam
+ pop bc
+ jr c, .doneShowingTeams
+ ld hl, wHoFTeamIndex2
+ inc [hl]
+ ld a, [hl]
+ cp b
+ jr nz, .loop
+.doneShowingTeams
+ pop af
+ ld [hTilesetType], a
+ pop af
+ ld [wUpdateSpritesEnabled], a
+ pop hl
+ res 6, [hl]
+ call GBPalWhiteOutWithDelay3
+ call ClearScreen
+ call RunDefaultPaletteCommand
+ jp GBPalNormal
+
+LeaguePCShowTeam:
+ ld c, PARTY_LENGTH
+.loop
+ push bc
+ call LeaguePCShowMon
+ call WaitForTextScrollButtonPress
+ ld a, [hJoyHeld]
+ bit 1, a
+ jr nz, .exit
+ ld hl, wHallOfFame + HOF_MON
+ ld de, wHallOfFame
+ ld bc, HOF_TEAM - HOF_MON
+ call CopyData
+ pop bc
+ ld a, [wHallOfFame + 0]
+ cp $ff
+ jr z, .done
+ dec c
+ jr nz, .loop
+.done
+ and a
+ ret
+.exit
+ pop bc
+ scf
+ ret
+
+LeaguePCShowMon:
+ call GBPalWhiteOutWithDelay3
+ call ClearScreen
+ ld hl, wHallOfFame
+ ld a, [hli]
+ ld [wHoFMonSpecies], a
+ ld [wcf91], a
+ ld [wd0b5], a
+ ld [wBattleMonSpecies2], a
+ ld [wWholeScreenPaletteMonSpecies], a
+ ld a, [hli]
+ ld [wHoFMonLevel], a
+ ld de, wcd6d
+ ld bc, NAME_LENGTH
+ call CopyData
+ ld b, SET_PAL_POKEMON_WHOLE_SCREEN
+ ld c, 0
+ call RunPaletteCommand
+ coord hl, 12, 5
+ call GetMonHeader
+ call LoadFrontSpriteByMonIndex
+ call GBPalNormal
+ coord hl, 0, 13
+ ld b, 2
+ ld c, $12
+ call TextBoxBorder
+ coord hl, 1, 15
+ ld de, HallOfFameNoText
+ call PlaceString
+ coord hl, 16, 15
+ ld de, wHoFTeamNo
+ lb bc, 1, 3
+ call PrintNumber
+ jpba HoFDisplayMonInfo
+
+HallOfFameNoText:
+ db "HALL OF FAME No @"
+
+AccessedHoFPCText:
+ TX_FAR _AccessedHoFPCText
+ db "@"
diff --git a/en/engine/menu/main_menu.asm b/en/engine/menu/main_menu.asm
new file mode 100755
index 00000000..da2e98e4
--- /dev/null
+++ b/en/engine/menu/main_menu.asm
@@ -0,0 +1,712 @@
+MainMenu:
+; Check save file
+ call InitOptions
+ xor a
+ ld [wOptionsInitialized], a
+ inc a
+ ld [wSaveFileStatus], a
+ call CheckForPlayerNameInSRAM
+ jr nc, .mainMenuLoop
+
+ predef LoadSAV
+
+.mainMenuLoop
+ ld c, 20
+ call DelayFrames
+ xor a ; LINK_STATE_NONE
+ ld [wLinkState], a
+ ld hl, wPartyAndBillsPCSavedMenuItem
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ ld [hl], a
+ ld [wDefaultMap], a
+ ld hl, wd72e
+ res 6, [hl]
+ call ClearScreen
+ call RunDefaultPaletteCommand
+ call LoadTextBoxTilePatterns
+ call LoadFontTilePatterns
+ ld hl, wd730
+ set 6, [hl]
+ ld a, [wSaveFileStatus]
+ cp 1
+ jr z, .noSaveFile
+; there's a save file
+ coord hl, 0, 0
+ ld b, 6
+ ld c, 13
+ call TextBoxBorder
+ coord hl, 2, 2
+ ld de, ContinueText
+ call PlaceString
+ jr .next2
+.noSaveFile
+ coord hl, 0, 0
+ ld b, 4
+ ld c, 13
+ call TextBoxBorder
+ coord hl, 2, 2
+ ld de, NewGameText
+ call PlaceString
+.next2
+ ld hl, wd730
+ res 6, [hl]
+ call UpdateSprites
+ xor a
+ ld [wCurrentMenuItem], a
+ ld [wLastMenuItem], a
+ ld [wMenuJoypadPollCount], a
+ inc a
+ ld [wTopMenuItemX], a
+ inc a
+ ld [wTopMenuItemY], a
+ ld a, A_BUTTON | B_BUTTON | START
+ ld [wMenuWatchedKeys], a
+ ld a, [wSaveFileStatus]
+ ld [wMaxMenuItem], a
+ call HandleMenuInput
+ bit 1, a ; pressed B?
+ jp nz, DisplayTitleScreen ; if so, go back to the title screen
+ ld c, 20
+ call DelayFrames
+ ld a, [wCurrentMenuItem]
+ ld b, a
+ ld a, [wSaveFileStatus]
+ cp 2
+ jp z, .skipInc
+; If there's no save file, increment the current menu item so that the numbers
+; are the same whether or not there's a save file.
+ inc b
+.skipInc
+ ld a, b
+ and a
+ jr z, .choseContinue
+ cp 1
+ jp z, StartNewGame
+ call DisplayOptionMenu
+ ld a, 1
+ ld [wOptionsInitialized], a
+ jp .mainMenuLoop
+.choseContinue
+ call DisplayContinueGameInfo
+ ld hl, wCurrentMapScriptFlags
+ set 5, [hl]
+.inputLoop
+ xor a
+ ld [hJoyPressed], a
+ ld [hJoyReleased], a
+ ld [hJoyHeld], a
+ call Joypad
+ ld a, [hJoyHeld]
+ bit 0, a
+ jr nz, .pressedA
+ bit 1, a
+ jp nz, .mainMenuLoop ; pressed B
+ jr .inputLoop
+.pressedA
+ call GBPalWhiteOutWithDelay3
+ call ClearScreen
+ ld a, PLAYER_DIR_DOWN
+ ld [wPlayerDirection], a
+ ld c, 10
+ call DelayFrames
+ ld a, [wNumHoFTeams]
+ and a
+ jp z, SpecialEnterMap
+ ld a, [wCurMap] ; map ID
+ cp HALL_OF_FAME
+ jp nz, SpecialEnterMap
+ xor a
+ ld [wDestinationMap], a
+ ld hl, wd732
+ set 2, [hl] ; fly warp or dungeon warp
+ call SpecialWarpIn
+ jp SpecialEnterMap
+
+InitOptions:
+ ld a, 1 ; no delay
+ ld [wLetterPrintingDelayFlags], a
+ ld a, 3 ; medium speed
+ ld [wOptions], a
+ ret
+
+LinkMenu:
+ xor a
+ ld [wLetterPrintingDelayFlags], a
+ ld hl, wd72e
+ set 6, [hl]
+ ld hl, TextTerminator_6b20
+ call PrintText
+ call SaveScreenTilesToBuffer1
+ ld hl, WhereWouldYouLikeText
+ call PrintText
+ coord hl, 5, 5
+ ld b, $6
+ ld c, $d
+ call TextBoxBorder
+ call UpdateSprites
+ coord hl, 7, 7
+ ld de, CableClubOptionsText
+ call PlaceString
+ xor a
+ ld [wUnusedCD37], a
+ ld [wd72d], a
+ ld hl, wTopMenuItemY
+ ld a, $7
+ ld [hli], a
+ ld a, $6
+ ld [hli], a
+ xor a
+ ld [hli], a
+ inc hl
+ ld a, $2
+ ld [hli], a
+ inc a
+ ; ld a, A_BUTTON | B_BUTTON
+ ld [hli], a ; wMenuWatchedKeys
+ xor a
+ ld [hl], a
+.waitForInputLoop
+ call HandleMenuInput
+ and A_BUTTON | B_BUTTON
+ add a
+ add a
+ ld b, a
+ ld a, [wCurrentMenuItem]
+ add b
+ add $d0
+ ld [wLinkMenuSelectionSendBuffer], a
+ ld [wLinkMenuSelectionSendBuffer + 1], a
+.exchangeMenuSelectionLoop
+ call Serial_ExchangeLinkMenuSelection
+ ld a, [wLinkMenuSelectionReceiveBuffer]
+ ld b, a
+ and $f0
+ cp $d0
+ jr z, .asm_5c7d
+ ld a, [wLinkMenuSelectionReceiveBuffer + 1]
+ ld b, a
+ and $f0
+ cp $d0
+ jr nz, .exchangeMenuSelectionLoop
+.asm_5c7d
+ ld a, b
+ and $c ; did the enemy press A or B?
+ jr nz, .enemyPressedAOrB
+; the enemy didn't press A or B
+ ld a, [wLinkMenuSelectionSendBuffer]
+ and $c ; did the player press A or B?
+ jr z, .waitForInputLoop ; if neither the player nor the enemy pressed A or B, try again
+ jr .doneChoosingMenuSelection ; if the player pressed A or B but the enemy didn't, use the player's selection
+.enemyPressedAOrB
+ ld a, [wLinkMenuSelectionSendBuffer]
+ and $c ; did the player press A or B?
+ jr z, .useEnemyMenuSelection ; if the enemy pressed A or B but the player didn't, use the enemy's selection
+; the enemy and the player both pressed A or B
+; The gameboy that is clocking the connection wins.
+ ld a, [hSerialConnectionStatus]
+ cp USING_INTERNAL_CLOCK
+ jr z, .doneChoosingMenuSelection
+.useEnemyMenuSelection
+ ld a, b
+ ld [wLinkMenuSelectionSendBuffer], a
+ and $3
+ ld [wCurrentMenuItem], a
+.doneChoosingMenuSelection
+ ld a, [hSerialConnectionStatus]
+ cp USING_INTERNAL_CLOCK
+ jr nz, .skipStartingTransfer
+ call DelayFrame
+ call DelayFrame
+ ld a, START_TRANSFER_INTERNAL_CLOCK
+ ld [rSC], a
+.skipStartingTransfer
+ ld b, $7f
+ ld c, $7f
+ ld d, $ec
+ ld a, [wLinkMenuSelectionSendBuffer]
+ and (B_BUTTON << 2) ; was B button pressed?
+ jr nz, .updateCursorPosition
+; A button was pressed
+ ld a, [wCurrentMenuItem]
+ cp $2
+ jr z, .updateCursorPosition
+ ld c, d
+ ld d, b
+ dec a
+ jr z, .updateCursorPosition
+ ld b, c
+ ld c, d
+.updateCursorPosition
+ ld a, b
+ Coorda 6, 7
+ ld a, c
+ Coorda 6, 9
+ ld a, d
+ Coorda 6, 11
+ ld c, 40
+ call DelayFrames
+ call LoadScreenTilesFromBuffer1
+ ld a, [wLinkMenuSelectionSendBuffer]
+ and (B_BUTTON << 2) ; was B button pressed?
+ jr nz, .choseCancel ; cancel if B pressed
+ ld a, [wCurrentMenuItem]
+ cp $2
+ jr z, .choseCancel
+ xor a
+ ld [wWalkBikeSurfState], a ; start walking
+ ld a, [wCurrentMenuItem]
+ and a
+ ld a, COLOSSEUM
+ jr nz, .next
+ ld a, TRADE_CENTER
+.next
+ ld [wd72d], a
+ ld hl, PleaseWaitText
+ call PrintText
+ ld c, 50
+ call DelayFrames
+ ld hl, wd732
+ res 1, [hl]
+ ld a, [wDefaultMap]
+ ld [wDestinationMap], a
+ call SpecialWarpIn
+ ld c, 20
+ call DelayFrames
+ xor a
+ ld [wMenuJoypadPollCount], a
+ ld [wSerialExchangeNybbleSendData], a
+ inc a ; LINK_STATE_IN_CABLE_CLUB
+ ld [wLinkState], a
+ ld [wEnteringCableClub], a
+ jr SpecialEnterMap
+.choseCancel
+ xor a
+ ld [wMenuJoypadPollCount], a
+ call Delay3
+ call CloseLinkConnection
+ ld hl, LinkCanceledText
+ call PrintText
+ ld hl, wd72e
+ res 6, [hl]
+ ret
+
+WhereWouldYouLikeText:
+ TX_FAR _WhereWouldYouLikeText
+ db "@"
+
+PleaseWaitText:
+ TX_FAR _PleaseWaitText
+ db "@"
+
+LinkCanceledText:
+ TX_FAR _LinkCanceledText
+ db "@"
+
+StartNewGame:
+ ld hl, wd732
+ res 1, [hl]
+ call OakSpeech
+ ld c, 20
+ call DelayFrames
+
+; enter map after using a special warp or loading the game from the main menu
+SpecialEnterMap:
+ xor a
+ ld [hJoyPressed], a
+ ld [hJoyHeld], a
+ ld [hJoy5], a
+ ld [wd72d], a
+ ld hl, wd732
+ set 0, [hl] ; count play time
+ call ResetPlayerSpriteData
+ ld c, 20
+ call DelayFrames
+ ld a, [wEnteringCableClub]
+ and a
+ ret nz
+ jp EnterMap
+
+ContinueText:
+ db "CONTINUE", $4e
+
+NewGameText:
+ db "NEW GAME"
+ next "OPTION@"
+
+CableClubOptionsText:
+ db "TRADE CENTER"
+ next "COLOSSEUM"
+ next "CANCEL@"
+
+DisplayContinueGameInfo:
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ coord hl, 4, 7
+ ld b, 8
+ ld c, 14
+ call TextBoxBorder
+ coord hl, 5, 9
+ ld de, SaveScreenInfoText
+ call PlaceString
+ coord hl, 12, 9
+ ld de, wPlayerName
+ call PlaceString
+ coord hl, 17, 11
+ call PrintNumBadges
+ coord hl, 16, 13
+ call PrintNumOwnedMons
+ coord hl, 13, 15
+ call PrintPlayTime
+ ld a, 1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ld c, 30
+ jp DelayFrames
+
+PrintSaveScreenText:
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ coord hl, 4, 0
+ ld b, $8
+ ld c, $e
+ call TextBoxBorder
+ call LoadTextBoxTilePatterns
+ call UpdateSprites
+ coord hl, 5, 2
+ ld de, SaveScreenInfoText
+ call PlaceString
+ coord hl, 12, 2
+ ld de, wPlayerName
+ call PlaceString
+ coord hl, 17, 4
+ call PrintNumBadges
+ coord hl, 16, 6
+ call PrintNumOwnedMons
+ coord hl, 13, 8
+ call PrintPlayTime
+ ld a, $1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ld c, 30
+ jp DelayFrames
+
+PrintNumBadges:
+ push hl
+ ld hl, wObtainedBadges
+ ld b, $1
+ call CountSetBits
+ pop hl
+ ld de, wNumSetBits
+ lb bc, 1, 2
+ jp PrintNumber
+
+PrintNumOwnedMons:
+ push hl
+ ld hl, wPokedexOwned
+ ld b, wPokedexOwnedEnd - wPokedexOwned
+ call CountSetBits
+ pop hl
+ ld de, wNumSetBits
+ lb bc, 1, 3
+ jp PrintNumber
+
+PrintPlayTime:
+ ld de, wPlayTimeHours
+ lb bc, 1, 3
+ call PrintNumber
+ ld [hl], $6d
+ inc hl
+ ld de, wPlayTimeMinutes
+ lb bc, LEADING_ZEROES | 1, 2
+ jp PrintNumber
+
+SaveScreenInfoText:
+ db "PLAYER"
+ next "BADGES "
+ next "#DEX "
+ next "TIME@"
+
+DisplayOptionMenu:
+ coord hl, 0, 0
+ ld b, 3
+ ld c, 18
+ call TextBoxBorder
+ coord hl, 0, 5
+ ld b, 3
+ ld c, 18
+ call TextBoxBorder
+ coord hl, 0, 10
+ ld b, 3
+ ld c, 18
+ call TextBoxBorder
+ coord hl, 1, 1
+ ld de, TextSpeedOptionText
+ call PlaceString
+ coord hl, 1, 6
+ ld de, BattleAnimationOptionText
+ call PlaceString
+ coord hl, 1, 11
+ ld de, BattleStyleOptionText
+ call PlaceString
+ coord hl, 2, 16
+ ld de, OptionMenuCancelText
+ call PlaceString
+ xor a
+ ld [wCurrentMenuItem], a
+ ld [wLastMenuItem], a
+ inc a
+ ld [wLetterPrintingDelayFlags], a
+ ld [wUnusedCD40], a
+ ld a, 3 ; text speed cursor Y coordinate
+ ld [wTopMenuItemY], a
+ call SetCursorPositionsFromOptions
+ ld a, [wOptionsTextSpeedCursorX] ; text speed cursor X coordinate
+ ld [wTopMenuItemX], a
+ ld a, $01
+ ld [H_AUTOBGTRANSFERENABLED], a ; enable auto background transfer
+ call Delay3
+.loop
+ call PlaceMenuCursor
+ call SetOptionsFromCursorPositions
+.getJoypadStateLoop
+ call JoypadLowSensitivity
+ ld a, [hJoy5]
+ ld b, a
+ and A_BUTTON | B_BUTTON | START | D_RIGHT | D_LEFT | D_UP | D_DOWN ; any key besides select pressed?
+ jr z, .getJoypadStateLoop
+ bit 1, b ; B button pressed?
+ jr nz, .exitMenu
+ bit 3, b ; Start button pressed?
+ jr nz, .exitMenu
+ bit 0, b ; A button pressed?
+ jr z, .checkDirectionKeys
+ ld a, [wTopMenuItemY]
+ cp 16 ; is the cursor on Cancel?
+ jr nz, .loop
+.exitMenu
+ ld a, SFX_PRESS_AB
+ call PlaySound
+ ret
+.eraseOldMenuCursor
+ ld [wTopMenuItemX], a
+ call EraseMenuCursor
+ jp .loop
+.checkDirectionKeys
+ ld a, [wTopMenuItemY]
+ bit 7, b ; Down pressed?
+ jr nz, .downPressed
+ bit 6, b ; Up pressed?
+ jr nz, .upPressed
+ cp 8 ; cursor in Battle Animation section?
+ jr z, .cursorInBattleAnimation
+ cp 13 ; cursor in Battle Style section?
+ jr z, .cursorInBattleStyle
+ cp 16 ; cursor on Cancel?
+ jr z, .loop
+.cursorInTextSpeed
+ bit 5, b ; Left pressed?
+ jp nz, .pressedLeftInTextSpeed
+ jp .pressedRightInTextSpeed
+.downPressed
+ cp 16
+ ld b, -13
+ ld hl, wOptionsTextSpeedCursorX
+ jr z, .updateMenuVariables
+ ld b, 5
+ cp 3
+ inc hl
+ jr z, .updateMenuVariables
+ cp 8
+ inc hl
+ jr z, .updateMenuVariables
+ ld b, 3
+ inc hl
+ jr .updateMenuVariables
+.upPressed
+ cp 8
+ ld b, -5
+ ld hl, wOptionsTextSpeedCursorX
+ jr z, .updateMenuVariables
+ cp 13
+ inc hl
+ jr z, .updateMenuVariables
+ cp 16
+ ld b, -3
+ inc hl
+ jr z, .updateMenuVariables
+ ld b, 13
+ inc hl
+.updateMenuVariables
+ add b
+ ld [wTopMenuItemY], a
+ ld a, [hl]
+ ld [wTopMenuItemX], a
+ call PlaceUnfilledArrowMenuCursor
+ jp .loop
+.cursorInBattleAnimation
+ ld a, [wOptionsBattleAnimCursorX] ; battle animation cursor X coordinate
+ xor $0b ; toggle between 1 and 10
+ ld [wOptionsBattleAnimCursorX], a
+ jp .eraseOldMenuCursor
+.cursorInBattleStyle
+ ld a, [wOptionsBattleStyleCursorX] ; battle style cursor X coordinate
+ xor $0b ; toggle between 1 and 10
+ ld [wOptionsBattleStyleCursorX], a
+ jp .eraseOldMenuCursor
+.pressedLeftInTextSpeed
+ ld a, [wOptionsTextSpeedCursorX] ; text speed cursor X coordinate
+ cp 1
+ jr z, .updateTextSpeedXCoord
+ cp 7
+ jr nz, .fromSlowToMedium
+ sub 6
+ jr .updateTextSpeedXCoord
+.fromSlowToMedium
+ sub 7
+ jr .updateTextSpeedXCoord
+.pressedRightInTextSpeed
+ ld a, [wOptionsTextSpeedCursorX] ; text speed cursor X coordinate
+ cp 14
+ jr z, .updateTextSpeedXCoord
+ cp 7
+ jr nz, .fromFastToMedium
+ add 7
+ jr .updateTextSpeedXCoord
+.fromFastToMedium
+ add 6
+.updateTextSpeedXCoord
+ ld [wOptionsTextSpeedCursorX], a ; text speed cursor X coordinate
+ jp .eraseOldMenuCursor
+
+TextSpeedOptionText:
+ db "TEXT SPEED"
+ next " FAST MEDIUM SLOW@"
+
+BattleAnimationOptionText:
+ db "BATTLE ANIMATION"
+ next " ON OFF@"
+
+BattleStyleOptionText:
+ db "BATTLE STYLE"
+ next " SHIFT SET@"
+
+OptionMenuCancelText:
+ db "CANCEL@"
+
+; sets the options variable according to the current placement of the menu cursors in the options menu
+SetOptionsFromCursorPositions:
+ ld hl, TextSpeedOptionData
+ ld a, [wOptionsTextSpeedCursorX] ; text speed cursor X coordinate
+ ld c, a
+.loop
+ ld a, [hli]
+ cp c
+ jr z, .textSpeedMatchFound
+ inc hl
+ jr .loop
+.textSpeedMatchFound
+ ld a, [hl]
+ ld d, a
+ ld a, [wOptionsBattleAnimCursorX] ; battle animation cursor X coordinate
+ dec a
+ jr z, .battleAnimationOn
+.battleAnimationOff
+ set 7, d
+ jr .checkBattleStyle
+.battleAnimationOn
+ res 7, d
+.checkBattleStyle
+ ld a, [wOptionsBattleStyleCursorX] ; battle style cursor X coordinate
+ dec a
+ jr z, .battleStyleShift
+.battleStyleSet
+ set 6, d
+ jr .storeOptions
+.battleStyleShift
+ res 6, d
+.storeOptions
+ ld a, d
+ ld [wOptions], a
+ ret
+
+; reads the options variable and places menu cursors in the correct positions within the options menu
+SetCursorPositionsFromOptions:
+ ld hl, TextSpeedOptionData + 1
+ ld a, [wOptions]
+ ld c, a
+ and $3f
+ push bc
+ ld de, 2
+ call IsInArray
+ pop bc
+ dec hl
+ ld a, [hl]
+ ld [wOptionsTextSpeedCursorX], a ; text speed cursor X coordinate
+ coord hl, 0, 3
+ call .placeUnfilledRightArrow
+ sla c
+ ld a, 1 ; On
+ jr nc, .storeBattleAnimationCursorX
+ ld a, 10 ; Off
+.storeBattleAnimationCursorX
+ ld [wOptionsBattleAnimCursorX], a ; battle animation cursor X coordinate
+ coord hl, 0, 8
+ call .placeUnfilledRightArrow
+ sla c
+ ld a, 1
+ jr nc, .storeBattleStyleCursorX
+ ld a, 10
+.storeBattleStyleCursorX
+ ld [wOptionsBattleStyleCursorX], a ; battle style cursor X coordinate
+ coord hl, 0, 13
+ call .placeUnfilledRightArrow
+; cursor in front of Cancel
+ coord hl, 0, 16
+ ld a, 1
+.placeUnfilledRightArrow
+ ld e, a
+ ld d, 0
+ add hl, de
+ ld [hl], $ec ; unfilled right arrow menu cursor
+ ret
+
+; table that indicates how the 3 text speed options affect frame delays
+; Format:
+; 00: X coordinate of menu cursor
+; 01: delay after printing a letter (in frames)
+TextSpeedOptionData:
+ db 14,5 ; Slow
+ db 7,3 ; Medium
+ db 1,1 ; Fast
+ db 7 ; default X coordinate (Medium)
+ db $ff ; terminator
+
+CheckForPlayerNameInSRAM:
+; Check if the player name data in SRAM has a string terminator character
+; (indicating that a name may have been saved there) and return whether it does
+; in carry.
+ ld a, SRAM_ENABLE
+ ld [MBC1SRamEnable], a
+ ld a, $1
+ ld [MBC1SRamBankingMode], a
+ ld [MBC1SRamBank], a
+ ld b, NAME_LENGTH
+ ld hl, sPlayerName
+.loop
+ ld a, [hli]
+ cp "@"
+ jr z, .found
+ dec b
+ jr nz, .loop
+; not found
+ xor a
+ ld [MBC1SRamEnable], a
+ ld [MBC1SRamBankingMode], a
+ and a
+ ret
+.found
+ xor a
+ ld [MBC1SRamEnable], a
+ ld [MBC1SRamBankingMode], a
+ scf
+ ret
diff --git a/en/engine/menu/naming_screen.asm b/en/engine/menu/naming_screen.asm
new file mode 100755
index 00000000..4804d02a
--- /dev/null
+++ b/en/engine/menu/naming_screen.asm
@@ -0,0 +1,512 @@
+AskName:
+ call SaveScreenTilesToBuffer1
+ call GetPredefRegisters
+ push hl
+ ld a, [wIsInBattle]
+ dec a
+ coord hl, 0, 0
+ ld b, 4
+ ld c, 11
+ call z, ClearScreenArea ; only if in wild battle
+ ld a, [wcf91]
+ ld [wd11e], a
+ call GetMonName
+ ld hl, DoYouWantToNicknameText
+ call PrintText
+ coord hl, 14, 7
+ lb bc, 8, 15
+ ld a, TWO_OPTION_MENU
+ ld [wTextBoxID], a
+ call DisplayTextBoxID
+ pop hl
+ ld a, [wCurrentMenuItem]
+ and a
+ jr nz, .declinedNickname
+ ld a, [wUpdateSpritesEnabled]
+ push af
+ xor a
+ ld [wUpdateSpritesEnabled], a
+ push hl
+ ld a, NAME_MON_SCREEN
+ ld [wNamingScreenType], a
+ call DisplayNamingScreen
+ ld a, [wIsInBattle]
+ and a
+ jr nz, .inBattle
+ call ReloadMapSpriteTilePatterns
+.inBattle
+ call LoadScreenTilesFromBuffer1
+ pop hl
+ pop af
+ ld [wUpdateSpritesEnabled], a
+ ld a, [wcf4b]
+ cp "@"
+ ret nz
+.declinedNickname
+ ld d, h
+ ld e, l
+ ld hl, wcd6d
+ ld bc, NAME_LENGTH
+ jp CopyData
+
+DoYouWantToNicknameText:
+ TX_FAR _DoYouWantToNicknameText
+ db "@"
+
+DisplayNameRaterScreen:
+ ld hl, wBuffer
+ xor a
+ ld [wUpdateSpritesEnabled], a
+ ld a, NAME_MON_SCREEN
+ ld [wNamingScreenType], a
+ call DisplayNamingScreen
+ call GBPalWhiteOutWithDelay3
+ call RestoreScreenTilesAndReloadTilePatterns
+ call LoadGBPal
+ ld a, [wcf4b]
+ cp "@"
+ jr z, .playerCancelled
+ ld hl, wPartyMonNicks
+ ld bc, NAME_LENGTH
+ ld a, [wWhichPokemon]
+ call AddNTimes
+ ld e, l
+ ld d, h
+ ld hl, wBuffer
+ ld bc, NAME_LENGTH
+ call CopyData
+ and a
+ ret
+.playerCancelled
+ scf
+ ret
+
+DisplayNamingScreen:
+ push hl
+ ld hl, wd730
+ set 6, [hl]
+ call GBPalWhiteOutWithDelay3
+ call ClearScreen
+ call UpdateSprites
+ ld b, SET_PAL_GENERIC
+ call RunPaletteCommand
+ call LoadHpBarAndStatusTilePatterns
+ call LoadEDTile
+ callba LoadMonPartySpriteGfx
+ coord hl, 0, 4
+ ld b, 9
+ ld c, 18
+ call TextBoxBorder
+ call PrintNamingText
+ ld a, 3
+ ld [wTopMenuItemY], a
+ ld a, 1
+ ld [wTopMenuItemX], a
+ ld [wLastMenuItem], a
+ ld [wCurrentMenuItem], a
+ ld a, $ff
+ ld [wMenuWatchedKeys], a
+ ld a, 7
+ ld [wMaxMenuItem], a
+ ld a, "@"
+ ld [wcf4b], a
+ xor a
+ ld hl, wNamingScreenSubmitName
+ ld [hli], a
+ ld [hli], a
+ ld [wAnimCounter], a
+.selectReturnPoint
+ call PrintAlphabet
+ call GBPalNormal
+.ABStartReturnPoint
+ ld a, [wNamingScreenSubmitName]
+ and a
+ jr nz, .submitNickname
+ call PrintNicknameAndUnderscores
+.dPadReturnPoint
+ call PlaceMenuCursor
+.inputLoop
+ ld a, [wCurrentMenuItem]
+ push af
+ callba AnimatePartyMon_ForceSpeed1
+ pop af
+ ld [wCurrentMenuItem], a
+ call JoypadLowSensitivity
+ ld a, [hJoyPressed]
+ and a
+ jr z, .inputLoop
+ ld hl, .namingScreenButtonFunctions
+.checkForPressedButton
+ sla a
+ jr c, .foundPressedButton
+ inc hl
+ inc hl
+ inc hl
+ inc hl
+ jr .checkForPressedButton
+.foundPressedButton
+ ld a, [hli]
+ ld e, a
+ ld a, [hli]
+ ld d, a
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ push de
+ jp hl
+
+.submitNickname
+ pop de
+ ld hl, wcf4b
+ ld bc, NAME_LENGTH
+ call CopyData
+ call GBPalWhiteOutWithDelay3
+ call ClearScreen
+ call ClearSprites
+ call RunDefaultPaletteCommand
+ call GBPalNormal
+ xor a
+ ld [wAnimCounter], a
+ ld hl, wd730
+ res 6, [hl]
+ ld a, [wIsInBattle]
+ and a
+ jp z, LoadTextBoxTilePatterns
+ jpab LoadHudTilePatterns
+
+.namingScreenButtonFunctions
+ dw .dPadReturnPoint
+ dw .pressedDown
+ dw .dPadReturnPoint
+ dw .pressedUp
+ dw .dPadReturnPoint
+ dw .pressedLeft
+ dw .dPadReturnPoint
+ dw .pressedRight
+ dw .ABStartReturnPoint
+ dw .pressedStart
+ dw .selectReturnPoint
+ dw .pressedSelect
+ dw .ABStartReturnPoint
+ dw .pressedB
+ dw .ABStartReturnPoint
+ dw .pressedA
+
+.pressedA_changedCase
+ pop de
+ ld de, .selectReturnPoint
+ push de
+.pressedSelect
+ ld a, [wAlphabetCase]
+ xor $1
+ ld [wAlphabetCase], a
+ ret
+
+.pressedStart
+ ld a, 1
+ ld [wNamingScreenSubmitName], a
+ ret
+
+.pressedA
+ ld a, [wCurrentMenuItem]
+ cp $5 ; "ED" row
+ jr nz, .didNotPressED
+ ld a, [wTopMenuItemX]
+ cp $11 ; "ED" column
+ jr z, .pressedStart
+.didNotPressED
+ ld a, [wCurrentMenuItem]
+ cp $6 ; case switch row
+ jr nz, .didNotPressCaseSwtich
+ ld a, [wTopMenuItemX]
+ cp $1 ; case switch column
+ jr z, .pressedA_changedCase
+.didNotPressCaseSwtich
+ ld hl, wMenuCursorLocation
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ inc hl
+ ld a, [hl]
+ ld [wNamingScreenLetter], a
+ call CalcStringLength
+ ld a, [wNamingScreenLetter]
+ cp $e5
+ ld de, Dakutens
+ jr z, .dakutensAndHandakutens
+ cp $e4
+ ld de, Handakutens
+ jr z, .dakutensAndHandakutens
+ ld a, [wNamingScreenType]
+ cp NAME_MON_SCREEN
+ jr nc, .checkMonNameLength
+ ld a, [wNamingScreenNameLength]
+ cp $7 ; max length of player/rival names
+ jr .checkNameLength
+.checkMonNameLength
+ ld a, [wNamingScreenNameLength]
+ cp $a ; max length of pokemon nicknames
+.checkNameLength
+ jr c, .addLetter
+ ret
+
+.dakutensAndHandakutens
+ push hl
+ call DakutensAndHandakutens
+ pop hl
+ ret nc
+ dec hl
+.addLetter
+ ld a, [wNamingScreenLetter]
+ ld [hli], a
+ ld [hl], "@"
+ ld a, SFX_PRESS_AB
+ call PlaySound
+ ret
+.pressedB
+ ld a, [wNamingScreenNameLength]
+ and a
+ ret z
+ call CalcStringLength
+ dec hl
+ ld [hl], "@"
+ ret
+.pressedRight
+ ld a, [wCurrentMenuItem]
+ cp $6
+ ret z ; can't scroll right on bottom row
+ ld a, [wTopMenuItemX]
+ cp $11 ; max
+ jp z, .wrapToFirstColumn
+ inc a
+ inc a
+ jr .done
+.wrapToFirstColumn
+ ld a, $1
+ jr .done
+.pressedLeft
+ ld a, [wCurrentMenuItem]
+ cp $6
+ ret z ; can't scroll right on bottom row
+ ld a, [wTopMenuItemX]
+ dec a
+ jp z, .wrapToLastColumn
+ dec a
+ jr .done
+.wrapToLastColumn
+ ld a, $11 ; max
+ jr .done
+.pressedUp
+ ld a, [wCurrentMenuItem]
+ dec a
+ ld [wCurrentMenuItem], a
+ and a
+ ret nz
+ ld a, $6 ; wrap to bottom row
+ ld [wCurrentMenuItem], a
+ ld a, $1 ; force left column
+ jr .done
+.pressedDown
+ ld a, [wCurrentMenuItem]
+ inc a
+ ld [wCurrentMenuItem], a
+ cp $7
+ jr nz, .wrapToTopRow
+ ld a, $1
+ ld [wCurrentMenuItem], a
+ jr .done
+.wrapToTopRow
+ cp $6
+ ret nz
+ ld a, $1
+.done
+ ld [wTopMenuItemX], a
+ jp EraseMenuCursor
+
+LoadEDTile:
+ ld de, ED_Tile
+ ld hl, vFont + $700
+ ld bc, (ED_TileEnd - ED_Tile) / $8
+ ; to fix the graphical bug on poor emulators
+ ;lb bc, BANK(ED_Tile), (ED_TileEnd - ED_Tile) / $8
+ jp CopyVideoDataDouble
+
+ED_Tile:
+ binlang gfx/ED_tile.1bpp
+ED_TileEnd:
+
+PrintAlphabet:
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ld a, [wAlphabetCase]
+ and a
+ ld de, LowerCaseAlphabet
+ jr nz, .lowercase
+ ld de, UpperCaseAlphabet
+.lowercase
+ coord hl, 2, 5
+ lb bc, 5, 9 ; 5 rows, 9 columns
+.outerLoop
+ push bc
+.innerLoop
+ ld a, [de]
+ ld [hli], a
+ inc hl
+ inc de
+ dec c
+ jr nz, .innerLoop
+ ld bc, SCREEN_WIDTH + 2
+ add hl, bc
+ pop bc
+ dec b
+ jr nz, .outerLoop
+ call PlaceString
+ ld a, $1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ jp Delay3
+
+LowerCaseAlphabet:
+ db "abcdefghijklmnopqrstuvwxyz ×():;[]",$e1,$e2,"-?!♂♀/⠄,¥UPPER CASE@"
+
+UpperCaseAlphabet:
+ db "ABCDEFGHIJKLMNOPQRSTUVWXYZ ×():;[]",$e1,$e2,"-?!♂♀/⠄,¥lower case@"
+
+PrintNicknameAndUnderscores:
+ call CalcStringLength
+ ld a, c
+ ld [wNamingScreenNameLength], a
+ coord hl, 10, 2
+ lb bc, 1, 10
+ call ClearScreenArea
+ coord hl, 10, 2
+ ld de, wcf4b
+ call PlaceString
+ coord hl, 10, 3
+ ld a, [wNamingScreenType]
+ cp NAME_MON_SCREEN
+ jr nc, .pokemon1
+ ld b, 7 ; player or rival max name length
+ jr .playerOrRival1
+.pokemon1
+ ld b, 10 ; pokemon max name length
+.playerOrRival1
+ ld a, $76 ; underscore tile id
+.placeUnderscoreLoop
+ ld [hli], a
+ dec b
+ jr nz, .placeUnderscoreLoop
+ ld a, [wNamingScreenType]
+ cp NAME_MON_SCREEN
+ ld a, [wNamingScreenNameLength]
+ jr nc, .pokemon2
+ cp 7 ; player or rival max name length
+ jr .playerOrRival2
+.pokemon2
+ cp 10 ; pokemon max name length
+.playerOrRival2
+ jr nz, .emptySpacesRemaining
+ ; when all spaces are filled, force the cursor onto the ED tile
+ call EraseMenuCursor
+ ld a, $11 ; "ED" x coord
+ ld [wTopMenuItemX], a
+ ld a, $5 ; "ED" y coord
+ ld [wCurrentMenuItem], a
+ ld a, [wNamingScreenType]
+ cp NAME_MON_SCREEN
+ ld a, 9 ; keep the last underscore raised
+ jr nc, .pokemon3
+ ld a, 6 ; keep the last underscore raised
+.pokemon3
+.emptySpacesRemaining
+ ld c, a
+ ld b, $0
+ coord hl, 10, 3
+ add hl, bc
+ ld [hl], $77 ; raised underscore tile id
+ ret
+
+DakutensAndHandakutens:
+ push de
+ call CalcStringLength
+ dec hl
+ ld a, [hl]
+ pop hl
+ ld de, $2
+ call IsInArray
+ ret nc
+ inc hl
+ ld a, [hl]
+ ld [wNamingScreenLetter], a
+ ret
+
+Dakutens:
+ db "かが", "きぎ", "くぐ", "けげ", "こご"
+ db "さざ", "しじ", "すず", "せぜ", "そぞ"
+ db "ただ", "ちぢ", "つづ", "てで", "とど"
+ db "はば", "ひび", "ふぶ", "へべ", "ほぼ"
+ db "カガ", "キギ", "クグ", "ケゲ", "コゴ"
+ db "サザ", "シジ", "スズ", "セゼ", "ソゾ"
+ db "タダ", "チヂ", "ツヅ", "テデ", "トド"
+ db "ハバ", "ヒビ", "フブ", "へべ", "ホボ"
+ db $ff
+
+Handakutens:
+ db "はぱ", "ひぴ", "ふぷ", "へぺ", "ほぽ"
+ db "ハパ", "ヒピ", "フプ", "へぺ", "ホポ"
+ db $ff
+
+; calculates the length of the string at wcf4b and stores it in c
+CalcStringLength:
+ ld hl, wcf4b
+ ld c, $0
+.loop
+ ld a, [hl]
+ cp "@"
+ ret z
+ inc hl
+ inc c
+ jr .loop
+
+PrintNamingText:
+ coord hl, 0, 1
+ ld a, [wNamingScreenType]
+ ld de, YourTextString
+ and a
+ jr z, .notNickname
+ ld de, RivalsTextString
+ dec a
+ jr z, .notNickname
+ ld a, [wcf91]
+ ld [wMonPartySpriteSpecies], a
+ push af
+ callba WriteMonPartySpriteOAMBySpecies
+ pop af
+ ld [wd11e], a
+ call GetMonName
+ coord hl, 4, 1
+ call PlaceString
+ ld hl, $1
+ add hl, bc
+ ld [hl], $c9
+ coord hl, 1, 3
+ ld de, NicknameTextString
+ jr .placeString
+.notNickname
+ call PlaceString
+ ld l, c
+ ld h, b
+ ld de, NameTextString
+.placeString
+ jp PlaceString
+
+YourTextString:
+ db "YOUR @"
+
+RivalsTextString:
+ db "RIVAL's @"
+
+NameTextString:
+ db "NAME?@"
+
+NicknameTextString:
+ db "NICKNAME?@"
diff --git a/en/engine/menu/party_menu.asm b/en/engine/menu/party_menu.asm
new file mode 100755
index 00000000..8323d0eb
--- /dev/null
+++ b/en/engine/menu/party_menu.asm
@@ -0,0 +1,325 @@
+; [wPartyMenuTypeOrMessageID] = menu type / message ID
+; if less than $F0, it is a menu type
+; menu types:
+; 00: normal pokemon menu (e.g. Start menu)
+; 01: use healing item on pokemon menu
+; 02: in-battle switch pokemon menu
+; 03: learn TM/HM menu
+; 04: swap pokemon positions menu
+; 05: use evolution stone on pokemon menu
+; otherwise, it is a message ID
+; f0: poison healed
+; f1: burn healed
+; f2: freeze healed
+; f3: sleep healed
+; f4: paralysis healed
+; f5: HP healed
+; f6: health returned
+; f7: revitalized
+; f8: leveled up
+DrawPartyMenu_:
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call ClearScreen
+ call UpdateSprites
+ callba LoadMonPartySpriteGfxWithLCDDisabled ; load pokemon icon graphics
+
+RedrawPartyMenu_:
+ ld a, [wPartyMenuTypeOrMessageID]
+ cp SWAP_MONS_PARTY_MENU
+ jp z, .printMessage
+ call ErasePartyMenuCursors
+ callba InitPartyMenuBlkPacket
+ coord hl, 3, 0
+ ld de, wPartySpecies
+ xor a
+ ld c, a
+ ld [hPartyMonIndex], a
+ ld [wWhichPartyMenuHPBar], a
+.loop
+ ld a, [de]
+ cp $FF ; reached the terminator?
+ jp z, .afterDrawingMonEntries
+ push bc
+ push de
+ push hl
+ ld a, c
+ push hl
+ ld hl, wPartyMonNicks
+ call GetPartyMonName
+ pop hl
+ call PlaceString ; print the pokemon's name
+ callba WriteMonPartySpriteOAMByPartyIndex ; place the appropriate pokemon icon
+ ld a, [hPartyMonIndex]
+ ld [wWhichPokemon], a
+ inc a
+ ld [hPartyMonIndex], a
+ call LoadMonData
+ pop hl
+ push hl
+ ld a, [wMenuItemToSwap]
+ and a ; is the player swapping pokemon positions?
+ jr z, .skipUnfilledRightArrow
+; if the player is swapping pokemon positions
+ dec a
+ ld b, a
+ ld a, [wWhichPokemon]
+ cp b ; is the player swapping the current pokemon in the list?
+ jr nz, .skipUnfilledRightArrow
+; the player is swapping the current pokemon in the list
+ dec hl
+ dec hl
+ dec hl
+ ld a, "▷" ; unfilled right arrow menu cursor
+ ld [hli], a ; place the cursor
+ inc hl
+ inc hl
+.skipUnfilledRightArrow
+ ld a, [wPartyMenuTypeOrMessageID] ; menu type
+ cp TMHM_PARTY_MENU
+ jr z, .teachMoveMenu
+ cp EVO_STONE_PARTY_MENU
+ jr z, .evolutionStoneMenu
+ push hl
+ ld bc, 14 ; 14 columns to the right
+ add hl, bc
+ ld de, wLoadedMonStatus
+ call PrintStatusCondition
+ pop hl
+ push hl
+ ld bc, SCREEN_WIDTH + 1 ; down 1 row and right 1 column
+ ld a, [hFlags_0xFFF6]
+ set 0, a
+ ld [hFlags_0xFFF6], a
+ add hl, bc
+ predef DrawHP2 ; draw HP bar and prints current / max HP
+ ld a, [hFlags_0xFFF6]
+ res 0, a
+ ld [hFlags_0xFFF6], a
+ call SetPartyMenuHPBarColor ; color the HP bar (on SGB)
+ pop hl
+ jr .printLevel
+.teachMoveMenu
+ push hl
+ predef CanLearnTM ; check if the pokemon can learn the move
+ pop hl
+ ld de, .ableToLearnMoveText
+ ld a, c
+ and a
+ jr nz, .placeMoveLearnabilityString
+ ld de, .notAbleToLearnMoveText
+.placeMoveLearnabilityString
+ ld bc, 20 + 9 ; down 1 row and right 9 columns
+ push hl
+ add hl, bc
+ call PlaceString
+ pop hl
+.printLevel
+ ld bc, 10 ; move 10 columns to the right
+ add hl, bc
+ call PrintLevel
+ pop hl
+ pop de
+ inc de
+ ld bc, 2 * 20
+ add hl, bc
+ pop bc
+ inc c
+ jp .loop
+.ableToLearnMoveText
+ db "ABLE@"
+.notAbleToLearnMoveText
+ db "NOT ABLE@"
+.evolutionStoneMenu
+ push hl
+ ld hl, EvosMovesPointerTable
+ ld b, 0
+ ld a, [wLoadedMonSpecies]
+ dec a
+ add a
+ rl b
+ ld c, a
+ add hl, bc
+ ld de, wEvosMoves
+ ld a, BANK(EvosMovesPointerTable)
+ ld bc, 2
+ call FarCopyData
+ ld hl, wEvosMoves
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld de, wEvosMoves
+ ld a, BANK(EvosMovesPointerTable)
+ ld bc, wEvosMoves.end - wEvosMoves
+ call FarCopyData
+ ld hl, wEvosMoves
+ ld de, .notAbleToEvolveText
+; loop through the pokemon's evolution entries
+.checkEvolutionsLoop
+ ld a, [hli]
+ and a ; reached terminator?
+ jr z, .placeEvolutionStoneString ; if so, place the "NOT ABLE" string
+ inc hl
+ inc hl
+ cp EV_ITEM
+ jr nz, .checkEvolutionsLoop
+; if it's a stone evolution entry
+ dec hl
+ dec hl
+ ld b, [hl]
+ ld a, [wEvoStoneItemID] ; the stone the player used
+ inc hl
+ inc hl
+ inc hl
+ cp b ; does the player's stone match this evolution entry's stone?
+ jr nz, .checkEvolutionsLoop
+; if it does match
+ ld de, .ableToEvolveText
+.placeEvolutionStoneString
+ ld bc, 20 + 9 ; down 1 row and right 9 columns
+ pop hl
+ push hl
+ add hl, bc
+ call PlaceString
+ pop hl
+ jr .printLevel
+.ableToEvolveText
+ db "ABLE@"
+.notAbleToEvolveText
+ db "NOT ABLE@"
+.afterDrawingMonEntries
+ ld b, SET_PAL_PARTY_MENU
+ call RunPaletteCommand
+.printMessage
+ ld hl, wd730
+ ld a, [hl]
+ push af
+ push hl
+ set 6, [hl] ; turn off letter printing delay
+ ld a, [wPartyMenuTypeOrMessageID] ; message ID
+ cp $F0
+ jr nc, .printItemUseMessage
+ add a
+ ld hl, PartyMenuMessagePointers
+ ld b, 0
+ ld c, a
+ add hl, bc
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ call PrintText
+.done
+ pop hl
+ pop af
+ ld [hl], a
+ ld a, 1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call Delay3
+ jp GBPalNormal
+.printItemUseMessage
+ and $0F
+ ld hl, PartyMenuItemUseMessagePointers
+ add a
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ push hl
+ ld a, [wUsedItemOnWhichPokemon]
+ ld hl, wPartyMonNicks
+ call GetPartyMonName
+ pop hl
+ call PrintText
+ jr .done
+
+PartyMenuItemUseMessagePointers:
+ dw AntidoteText
+ dw BurnHealText
+ dw IceHealText
+ dw AwakeningText
+ dw ParlyzHealText
+ dw PotionText
+ dw FullHealText
+ dw ReviveText
+ dw RareCandyText
+
+PartyMenuMessagePointers:
+ dw PartyMenuNormalText
+ dw PartyMenuItemUseText
+ dw PartyMenuBattleText
+ dw PartyMenuUseTMText
+ dw PartyMenuSwapMonText
+ dw PartyMenuItemUseText
+
+PartyMenuNormalText:
+ TX_FAR _PartyMenuNormalText
+ db "@"
+
+PartyMenuItemUseText:
+ TX_FAR _PartyMenuItemUseText
+ db "@"
+
+PartyMenuBattleText:
+ TX_FAR _PartyMenuBattleText
+ db "@"
+
+PartyMenuUseTMText:
+ TX_FAR _PartyMenuUseTMText
+ db "@"
+
+PartyMenuSwapMonText:
+ TX_FAR _PartyMenuSwapMonText
+ db "@"
+
+PotionText:
+ TX_FAR _PotionText
+ db "@"
+
+AntidoteText:
+ TX_FAR _AntidoteText
+ db "@"
+
+ParlyzHealText:
+ TX_FAR _ParlyzHealText
+ db "@"
+
+BurnHealText:
+ TX_FAR _BurnHealText
+ db "@"
+
+IceHealText:
+ TX_FAR _IceHealText
+ db "@"
+
+AwakeningText:
+ TX_FAR _AwakeningText
+ db "@"
+
+FullHealText:
+ TX_FAR _FullHealText
+ db "@"
+
+ReviveText:
+ TX_FAR _ReviveText
+ db "@"
+
+RareCandyText:
+ TX_FAR _RareCandyText
+ TX_SFX_ITEM_1 ; probably supposed to play SFX_LEVEL_UP but the wrong music bank is loaded
+ TX_BLINK
+ db "@"
+
+SetPartyMenuHPBarColor:
+ ld hl, wPartyMenuHPBarColors
+ ld a, [wWhichPartyMenuHPBar]
+ ld c, a
+ ld b, 0
+ add hl, bc
+ call GetHealthBarColor
+ ld b, UPDATE_PARTY_MENU_BLK_PACKET
+ call RunPaletteCommand
+ ld hl, wWhichPartyMenuHPBar
+ inc [hl]
+ ret
diff --git a/en/engine/menu/players_pc.asm b/en/engine/menu/players_pc.asm
new file mode 100755
index 00000000..bc2be4ef
--- /dev/null
+++ b/en/engine/menu/players_pc.asm
@@ -0,0 +1,303 @@
+PlayerPC:
+ ld hl, wd730
+ set 6, [hl]
+ ld a, ITEM_NAME
+ ld [wNameListType], a
+ call SaveScreenTilesToBuffer1
+ xor a
+ ld [wBagSavedMenuItem], a
+ ld [wParentMenuItem], a
+ ld a, [wFlags_0xcd60]
+ bit 3, a ; accessing player's PC through another PC?
+ jr nz, PlayerPCMenu
+; accessing it directly
+ ld a, SFX_TURN_ON_PC
+ call PlaySound
+ ld hl, TurnedOnPC2Text
+ call PrintText
+
+PlayerPCMenu:
+ ld a, [wParentMenuItem]
+ ld [wCurrentMenuItem], a
+ ld hl, wFlags_0xcd60
+ set 5, [hl]
+ call LoadScreenTilesFromBuffer2
+ coord hl, 0, 0
+ ld b, $8
+ ld c, $e
+ call TextBoxBorder
+ call UpdateSprites
+ coord hl, 2, 2
+ ld de, PlayersPCMenuEntries
+ call PlaceString
+ ld hl, wTopMenuItemY
+ ld a, 2
+ ld [hli], a ; wTopMenuItemY
+ dec a
+ ld [hli], a ; wTopMenuItemX
+ inc hl
+ inc hl
+ ld a, 3
+ ld [hli], a ; wMaxMenuItem
+ ld a, A_BUTTON | B_BUTTON
+ ld [hli], a ; wMenuWatchedKeys
+ xor a
+ ld [hl], a
+ ld hl, wListScrollOffset
+ ld [hli], a ; wListScrollOffset
+ ld [hl], a ; wMenuWatchMovingOutOfBounds
+ ld [wPlayerMonNumber], a
+ ld hl, WhatDoYouWantText
+ call PrintText
+ call HandleMenuInput
+ bit 1, a
+ jp nz, ExitPlayerPC
+ call PlaceUnfilledArrowMenuCursor
+ ld a, [wCurrentMenuItem]
+ ld [wParentMenuItem], a
+ and a
+ jp z, PlayerPCWithdraw
+ dec a
+ jp z, PlayerPCDeposit
+ dec a
+ jp z, PlayerPCToss
+
+ExitPlayerPC:
+ ld a, [wFlags_0xcd60]
+ bit 3, a ; accessing player's PC through another PC?
+ jr nz, .next
+; accessing it directly
+ ld a, SFX_TURN_OFF_PC
+ call PlaySound
+ call WaitForSoundToFinish
+.next
+ ld hl, wFlags_0xcd60
+ res 5, [hl]
+ call LoadScreenTilesFromBuffer2
+ xor a
+ ld [wListScrollOffset], a
+ ld [wBagSavedMenuItem], a
+ ld hl, wd730
+ res 6, [hl]
+ xor a
+ ld [wDoNotWaitForButtonPressAfterDisplayingText], a
+ ret
+
+PlayerPCDeposit:
+ xor a
+ ld [wCurrentMenuItem], a
+ ld [wListScrollOffset], a
+ ld a, [wNumBagItems]
+ and a
+ jr nz, .loop
+ ld hl, NothingToDepositText
+ call PrintText
+ jp PlayerPCMenu
+.loop
+ ld hl, WhatToDepositText
+ call PrintText
+ ld hl, wNumBagItems
+ ld a, l
+ ld [wListPointer], a
+ ld a, h
+ ld [wListPointer + 1], a
+ xor a
+ ld [wPrintItemPrices], a
+ ld a, ITEMLISTMENU
+ ld [wListMenuID], a
+ call DisplayListMenuID
+ jp c, PlayerPCMenu
+ call IsKeyItem
+ ld a, 1
+ ld [wItemQuantity], a
+ ld a, [wIsKeyItem]
+ and a
+ jr nz, .next
+; if it's not a key item, there can be more than one of the item
+ ld hl, DepositHowManyText
+ call PrintText
+ call DisplayChooseQuantityMenu
+ cp $ff
+ jp z, .loop
+.next
+ ld hl, wNumBoxItems
+ call AddItemToInventory
+ jr c, .roomAvailable
+ ld hl, NoRoomToStoreText
+ call PrintText
+ jp .loop
+.roomAvailable
+ ld hl, wNumBagItems
+ call RemoveItemFromInventory
+ call WaitForSoundToFinish
+ ld a, SFX_WITHDRAW_DEPOSIT
+ call PlaySound
+ call WaitForSoundToFinish
+ ld hl, ItemWasStoredText
+ call PrintText
+ jp .loop
+
+PlayerPCWithdraw:
+ xor a
+ ld [wCurrentMenuItem], a
+ ld [wListScrollOffset], a
+ ld a, [wNumBoxItems]
+ and a
+ jr nz, .loop
+ ld hl, NothingStoredText
+ call PrintText
+ jp PlayerPCMenu
+.loop
+ ld hl, WhatToWithdrawText
+ call PrintText
+ ld hl, wNumBoxItems
+ ld a, l
+ ld [wListPointer], a
+ ld a, h
+ ld [wListPointer + 1], a
+ xor a
+ ld [wPrintItemPrices], a
+ ld a, ITEMLISTMENU
+ ld [wListMenuID], a
+ call DisplayListMenuID
+ jp c, PlayerPCMenu
+ call IsKeyItem
+ ld a, 1
+ ld [wItemQuantity], a
+ ld a, [wIsKeyItem]
+ and a
+ jr nz, .next
+; if it's not a key item, there can be more than one of the item
+ ld hl, WithdrawHowManyText
+ call PrintText
+ call DisplayChooseQuantityMenu
+ cp $ff
+ jp z, .loop
+.next
+ ld hl, wNumBagItems
+ call AddItemToInventory
+ jr c, .roomAvailable
+ ld hl, CantCarryMoreText
+ call PrintText
+ jp .loop
+.roomAvailable
+ ld hl, wNumBoxItems
+ call RemoveItemFromInventory
+ call WaitForSoundToFinish
+ ld a, SFX_WITHDRAW_DEPOSIT
+ call PlaySound
+ call WaitForSoundToFinish
+ ld hl, WithdrewItemText
+ call PrintText
+ jp .loop
+
+PlayerPCToss:
+ xor a
+ ld [wCurrentMenuItem], a
+ ld [wListScrollOffset], a
+ ld a, [wNumBoxItems]
+ and a
+ jr nz, .loop
+ ld hl, NothingStoredText
+ call PrintText
+ jp PlayerPCMenu
+.loop
+ ld hl, WhatToTossText
+ call PrintText
+ ld hl, wNumBoxItems
+ ld a, l
+ ld [wListPointer], a
+ ld a, h
+ ld [wListPointer + 1], a
+ xor a
+ ld [wPrintItemPrices], a
+ ld a, ITEMLISTMENU
+ ld [wListMenuID], a
+ push hl
+ call DisplayListMenuID
+ pop hl
+ jp c, PlayerPCMenu
+ push hl
+ call IsKeyItem
+ pop hl
+ ld a, 1
+ ld [wItemQuantity], a
+ ld a, [wIsKeyItem]
+ and a
+ jr nz, .next
+ ld a, [wcf91]
+ call IsItemHM
+ jr c, .next
+; if it's not a key item, there can be more than one of the item
+ push hl
+ ld hl, TossHowManyText
+ call PrintText
+ call DisplayChooseQuantityMenu
+ pop hl
+ cp $ff
+ jp z, .loop
+.next
+ call TossItem ; disallows tossing key items
+ jp .loop
+
+PlayersPCMenuEntries:
+ db "WITHDRAW ITEM"
+ next "DEPOSIT ITEM"
+ next "TOSS ITEM"
+ next "LOG OFF@"
+
+TurnedOnPC2Text:
+ TX_FAR _TurnedOnPC2Text
+ db "@"
+
+WhatDoYouWantText:
+ TX_FAR _WhatDoYouWantText
+ db "@"
+
+WhatToDepositText:
+ TX_FAR _WhatToDepositText
+ db "@"
+
+DepositHowManyText:
+ TX_FAR _DepositHowManyText
+ db "@"
+
+ItemWasStoredText:
+ TX_FAR _ItemWasStoredText
+ db "@"
+
+NothingToDepositText:
+ TX_FAR _NothingToDepositText
+ db "@"
+
+NoRoomToStoreText:
+ TX_FAR _NoRoomToStoreText
+ db "@"
+
+WhatToWithdrawText:
+ TX_FAR _WhatToWithdrawText
+ db "@"
+
+WithdrawHowManyText:
+ TX_FAR _WithdrawHowManyText
+ db "@"
+
+WithdrewItemText:
+ TX_FAR _WithdrewItemText
+ db "@"
+
+NothingStoredText:
+ TX_FAR _NothingStoredText
+ db "@"
+
+CantCarryMoreText:
+ TX_FAR _CantCarryMoreText
+ db "@"
+
+WhatToTossText:
+ TX_FAR _WhatToTossText
+ db "@"
+
+TossHowManyText:
+ TX_FAR _TossHowManyText
+ db "@"
diff --git a/en/engine/menu/pokedex.asm b/en/engine/menu/pokedex.asm
new file mode 100755
index 00000000..6a3ea959
--- /dev/null
+++ b/en/engine/menu/pokedex.asm
@@ -0,0 +1,665 @@
+ShowPokedexMenu:
+ call GBPalWhiteOut
+ call ClearScreen
+ call UpdateSprites
+ ld a, [wListScrollOffset]
+ push af
+ xor a
+ ld [wCurrentMenuItem], a
+ ld [wListScrollOffset], a
+ ld [wLastMenuItem], a
+ inc a
+ ld [wd11e], a
+ ld [hJoy7], a
+.setUpGraphics
+ ld b, SET_PAL_GENERIC
+ call RunPaletteCommand
+ callab LoadPokedexTilePatterns
+.doPokemonListMenu
+ ld hl, wTopMenuItemY
+ ld a, 3
+ ld [hli], a ; top menu item Y
+ xor a
+ ld [hli], a ; top menu item X
+ inc a
+ ld [wMenuWatchMovingOutOfBounds], a
+ inc hl
+ inc hl
+ ld a, 6
+ ld [hli], a ; max menu item ID
+ ld [hl], D_LEFT | D_RIGHT | B_BUTTON | A_BUTTON
+ call HandlePokedexListMenu
+ jr c, .goToSideMenu ; if the player chose a pokemon from the list
+.exitPokedex
+ xor a
+ ld [wMenuWatchMovingOutOfBounds], a
+ ld [wCurrentMenuItem], a
+ ld [wLastMenuItem], a
+ ld [hJoy7], a
+ ld [wWastedByteCD3A], a
+ ld [wOverrideSimulatedJoypadStatesMask], a
+ pop af
+ ld [wListScrollOffset], a
+ call GBPalWhiteOutWithDelay3
+ call RunDefaultPaletteCommand
+ jp ReloadMapData
+.goToSideMenu
+ call HandlePokedexSideMenu
+ dec b
+ jr z, .exitPokedex ; if the player chose Quit
+ dec b
+ jr z, .doPokemonListMenu ; if pokemon not seen or player pressed B button
+ jp .setUpGraphics ; if pokemon data or area was shown
+
+; handles the menu on the lower right in the pokedex screen
+; OUTPUT:
+; b = reason for exiting menu
+; 00: showed pokemon data or area
+; 01: the player chose Quit
+; 02: the pokemon has not been seen yet or the player pressed the B button
+HandlePokedexSideMenu:
+ call PlaceUnfilledArrowMenuCursor
+ ld a, [wCurrentMenuItem]
+ push af
+ ld b, a
+ ld a, [wLastMenuItem]
+ push af
+ ld a, [wListScrollOffset]
+ push af
+ add b
+ inc a
+ ld [wd11e], a
+ ld a, [wd11e]
+ push af
+ ld a, [wDexMaxSeenMon]
+ push af ; this doesn't need to be preserved
+ ld hl, wPokedexSeen
+ call IsPokemonBitSet
+ ld b, 2
+ jr z, .exitSideMenu
+ call PokedexToIndex
+ ld hl, wTopMenuItemY
+ ld a, 10
+ ld [hli], a ; top menu item Y
+ ld a, 15
+ ld [hli], a ; top menu item X
+ xor a
+ ld [hli], a ; current menu item ID
+ inc hl
+ ld a, 3
+ ld [hli], a ; max menu item ID
+ ;ld a, A_BUTTON | B_BUTTON
+ ld [hli], a ; menu watched keys (A button and B button)
+ xor a
+ ld [hli], a ; old menu item ID
+ ld [wMenuWatchMovingOutOfBounds], a
+.handleMenuInput
+ call HandleMenuInput
+ bit 1, a ; was the B button pressed?
+ ld b, 2
+ jr nz, .buttonBPressed
+ ld a, [wCurrentMenuItem]
+ and a
+ jr z, .choseData
+ dec a
+ jr z, .choseCry
+ dec a
+ jr z, .choseArea
+.choseQuit
+ ld b, 1
+.exitSideMenu
+ pop af
+ ld [wDexMaxSeenMon], a
+ pop af
+ ld [wd11e], a
+ pop af
+ ld [wListScrollOffset], a
+ pop af
+ ld [wLastMenuItem], a
+ pop af
+ ld [wCurrentMenuItem], a
+ push bc
+ coord hl, 0, 3
+ ld de, 20
+ lb bc, " ", 13
+ call DrawTileLine ; cover up the menu cursor in the pokemon list
+ pop bc
+ ret
+
+.buttonBPressed
+ push bc
+ coord hl, 15, 10
+ ld de, 20
+ lb bc, " ", 7
+ call DrawTileLine ; cover up the menu cursor in the side menu
+ pop bc
+ jr .exitSideMenu
+
+.choseData
+ call ShowPokedexDataInternal
+ ld b, 0
+ jr .exitSideMenu
+
+; play pokemon cry
+.choseCry
+ ld a, [wd11e]
+ call GetCryData
+ call PlaySound
+ jr .handleMenuInput
+
+.choseArea
+ predef LoadTownMap_Nest ; display pokemon areas
+ ld b, 0
+ jr .exitSideMenu
+
+; handles the list of pokemon on the left of the pokedex screen
+; sets carry flag if player presses A, unsets carry flag if player presses B
+HandlePokedexListMenu:
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+; draw the horizontal line separating the seen and owned amounts from the menu
+ coord hl, 15, 8
+ ld a, "─"
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ ld [hli], a
+ coord hl, 14, 0
+ ld [hl], $71 ; vertical line tile
+ coord hl, 14, 1
+ call DrawPokedexVerticalLine
+ coord hl, 14, 9
+ call DrawPokedexVerticalLine
+ ld hl, wPokedexSeen
+ ld b, wPokedexSeenEnd - wPokedexSeen
+ call CountSetBits
+ ld de, wNumSetBits
+ coord hl, 16, 3
+ lb bc, 1, 3
+ call PrintNumber ; print number of seen pokemon
+ ld hl, wPokedexOwned
+ ld b, wPokedexOwnedEnd - wPokedexOwned
+ call CountSetBits
+ ld de, wNumSetBits
+ coord hl, 16, 6
+ lb bc, 1, 3
+ call PrintNumber ; print number of owned pokemon
+ coord hl, 16, 2
+ ld de, PokedexSeenText
+ call PlaceString
+ coord hl, 16, 5
+ ld de, PokedexOwnText
+ call PlaceString
+ coord hl, 1, 1
+ ld de, PokedexContentsText
+ call PlaceString
+ coord hl, 16, 10
+ ld de, PokedexMenuItemsText
+ call PlaceString
+; find the highest pokedex number among the pokemon the player has seen
+ ld hl, wPokedexSeenEnd - 1
+ ld b, (wPokedexSeenEnd - wPokedexSeen) * 8 + 1
+.maxSeenPokemonLoop
+ ld a, [hld]
+ ld c, 8
+.maxSeenPokemonInnerLoop
+ dec b
+ sla a
+ jr c, .storeMaxSeenPokemon
+ dec c
+ jr nz, .maxSeenPokemonInnerLoop
+ jr .maxSeenPokemonLoop
+
+.storeMaxSeenPokemon
+ ld a, b
+ ld [wDexMaxSeenMon], a
+.loop
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ coord hl, 4, 2
+ lb bc, 14, 10
+ call ClearScreenArea
+ coord hl, 1, 3
+ ld a, [wListScrollOffset]
+ ld [wd11e], a
+ ld d, 7
+ ld a, [wDexMaxSeenMon]
+ cp 7
+ jr nc, .printPokemonLoop
+ ld d, a
+ dec a
+ ld [wMaxMenuItem], a
+; loop to print pokemon pokedex numbers and names
+; if the player has owned the pokemon, it puts a pokeball beside the name
+.printPokemonLoop
+ ld a, [wd11e]
+ inc a
+ ld [wd11e], a
+ push af
+ push de
+ push hl
+ ld de, -SCREEN_WIDTH
+ add hl, de
+ ld de, wd11e
+ lb bc, LEADING_ZEROES | 1, 3
+ call PrintNumber ; print the pokedex number
+ ld de, SCREEN_WIDTH
+ add hl, de
+ dec hl
+ push hl
+ ld hl, wPokedexOwned
+ call IsPokemonBitSet
+ pop hl
+ ld a, " "
+ jr z, .writeTile
+ ld a, $72 ; pokeball tile
+.writeTile
+ ld [hl], a ; put a pokeball next to pokemon that the player has owned
+ push hl
+ ld hl, wPokedexSeen
+ call IsPokemonBitSet
+ jr nz, .getPokemonName ; if the player has seen the pokemon
+ ld de, .dashedLine ; print a dashed line in place of the name if the player hasn't seen the pokemon
+ jr .skipGettingName
+.dashedLine ; for unseen pokemon in the list
+ db "----------@"
+.getPokemonName
+ call PokedexToIndex
+ call GetMonName
+.skipGettingName
+ pop hl
+ inc hl
+ call PlaceString
+ pop hl
+ ld bc, 2 * SCREEN_WIDTH
+ add hl, bc
+ pop de
+ pop af
+ ld [wd11e], a
+ dec d
+ jr nz, .printPokemonLoop
+ ld a, 01
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call Delay3
+ call GBPalNormal
+ call HandleMenuInput
+ bit 1, a ; was the B button pressed?
+ jp nz, .buttonBPressed
+.checkIfUpPressed
+ bit 6, a ; was Up pressed?
+ jr z, .checkIfDownPressed
+.upPressed ; scroll up one row
+ ld a, [wListScrollOffset]
+ and a
+ jp z, .loop
+ dec a
+ ld [wListScrollOffset], a
+ jp .loop
+.checkIfDownPressed
+ bit 7, a ; was Down pressed?
+ jr z, .checkIfRightPressed
+.downPressed ; scroll down one row
+ ld a, [wDexMaxSeenMon]
+ cp 7
+ jp c, .loop ; can't if the list is shorter than 7
+ sub 7
+ ld b, a
+ ld a, [wListScrollOffset]
+ cp b
+ jp z, .loop
+ inc a
+ ld [wListScrollOffset], a
+ jp .loop
+.checkIfRightPressed
+ bit 4, a ; was Right pressed?
+ jr z, .checkIfLeftPressed
+.rightPressed ; scroll down 7 rows
+ ld a, [wDexMaxSeenMon]
+ cp 7
+ jp c, .loop ; can't if the list is shorter than 7
+ sub 6
+ ld b, a
+ ld a, [wListScrollOffset]
+ add 7
+ ld [wListScrollOffset], a
+ cp b
+ jp c, .loop
+ dec b
+ ld a, b
+ ld [wListScrollOffset], a
+ jp .loop
+.checkIfLeftPressed ; scroll up 7 rows
+ bit 5, a ; was Left pressed?
+ jr z, .buttonAPressed
+.leftPressed
+ ld a, [wListScrollOffset]
+ sub 7
+ ld [wListScrollOffset], a
+ jp nc, .loop
+ xor a
+ ld [wListScrollOffset], a
+ jp .loop
+.buttonAPressed
+ scf
+ ret
+.buttonBPressed
+ and a
+ ret
+
+DrawPokedexVerticalLine:
+ ld c, 9 ; height of line
+ ld de, SCREEN_WIDTH
+ ld a, $71 ; vertical line tile
+.loop
+ ld [hl], a
+ add hl, de
+ xor 1 ; toggle between vertical line tile and box tile
+ dec c
+ jr nz, .loop
+ ret
+
+PokedexSeenText:
+ db "SEEN@"
+
+PokedexOwnText:
+ db "OWN@"
+
+PokedexContentsText:
+ db "CONTENTS@"
+
+PokedexMenuItemsText:
+ db "DATA"
+ next "CRY"
+ next "AREA"
+ next "QUIT@"
+
+; tests if a pokemon's bit is set in the seen or owned pokemon bit fields
+; INPUT:
+; [wd11e] = pokedex number
+; hl = address of bit field
+IsPokemonBitSet:
+ ld a, [wd11e]
+ dec a
+ ld c, a
+ ld b, FLAG_TEST
+ predef FlagActionPredef
+ ld a, c
+ and a
+ ret
+
+; function to display pokedex data from outside the pokedex
+ShowPokedexData:
+ call GBPalWhiteOutWithDelay3
+ call ClearScreen
+ call UpdateSprites
+ callab LoadPokedexTilePatterns ; load pokedex tiles
+
+; function to display pokedex data from inside the pokedex
+ShowPokedexDataInternal:
+ ld hl, wd72c
+ set 1, [hl]
+ ld a, $33 ; 3/7 volume
+ ld [rNR50], a
+ call GBPalWhiteOut ; zero all palettes
+ call ClearScreen
+ ld a, [wd11e] ; pokemon ID
+ ld [wcf91], a
+ push af
+ ld b, SET_PAL_POKEDEX
+ call RunPaletteCommand
+ pop af
+ ld [wd11e], a
+ ld a, [hTilesetType]
+ push af
+ xor a
+ ld [hTilesetType], a
+
+ coord hl, 0, 0
+ ld de, 1
+ lb bc, $64, SCREEN_WIDTH
+ call DrawTileLine ; draw top border
+
+ coord hl, 0, 17
+ ld b, $6f
+ call DrawTileLine ; draw bottom border
+
+ coord hl, 0, 1
+ ld de, 20
+ lb bc, $66, $10
+ call DrawTileLine ; draw left border
+
+ coord hl, 19, 1
+ ld b, $67
+ call DrawTileLine ; draw right border
+
+ ld a, $63 ; upper left corner tile
+ Coorda 0, 0
+ ld a, $65 ; upper right corner tile
+ Coorda 19, 0
+ ld a, $6c ; lower left corner tile
+ Coorda 0, 17
+ ld a, $6e ; lower right corner tile
+ Coorda 19, 17
+
+ coord hl, 0, 9
+ ld de, PokedexDataDividerLine
+ call PlaceString ; draw horizontal divider line
+
+ coord hl, 9, 6
+ ld de, HeightWeightText
+ call PlaceString
+
+ call GetMonName
+ coord hl, 9, 2
+ call PlaceString
+
+ ld hl, PokedexEntryPointers
+ ld a, [wd11e]
+ dec a
+ ld e, a
+ ld d, 0
+ add hl, de
+ add hl, de
+ ld a, [hli]
+ ld e, a
+ ld d, [hl] ; de = address of pokedex entry
+
+ coord hl, 9, 4
+ call PlaceString ; print species name
+
+ ld h, b
+ ld l, c
+ push de
+ ld a, [wd11e]
+ push af
+ call IndexToPokedex
+
+ coord hl, 2, 8
+ ld a, "№"
+ ld [hli], a
+ ld a, "⠄"
+ ld [hli], a
+ ld de, wd11e
+ lb bc, LEADING_ZEROES | 1, 3
+ call PrintNumber ; print pokedex number
+
+ ld hl, wPokedexOwned
+ call IsPokemonBitSet
+ pop af
+ ld [wd11e], a
+ ld a, [wcf91]
+ ld [wd0b5], a
+ pop de
+
+ push af
+ push bc
+ push de
+ push hl
+
+ call Delay3
+ call GBPalNormal
+ call GetMonHeader ; load pokemon picture location
+ coord hl, 1, 1
+ call LoadFlippedFrontSpriteByMonIndex ; draw pokemon picture
+ ld a, [wcf91]
+ call PlayCry ; play pokemon cry
+
+ pop hl
+ pop de
+ pop bc
+ pop af
+
+ ld a, c
+ and a
+ jp z, .waitForButtonPress ; if the pokemon has not been owned, don't print the height, weight, or description
+ inc de ; de = address of feet (height)
+ ld a, [de] ; reads feet, but a is overwritten without being used
+ coord hl, 12, 6
+ lb bc, 1, 2
+ call PrintNumber ; print feet (height)
+ ld a, $60 ; feet symbol tile (one tick)
+ ld [hl], a
+ inc de
+ inc de ; de = address of inches (height)
+ coord hl, 15, 6
+ lb bc, LEADING_ZEROES | 1, 2
+ call PrintNumber ; print inches (height)
+ ld a, $61 ; inches symbol tile (two ticks)
+ ld [hl], a
+; now print the weight (note that weight is stored in tenths of pounds internally)
+ inc de
+ inc de
+ inc de ; de = address of upper byte of weight
+ push de
+; put weight in big-endian order at hDexWeight
+ ld hl, hDexWeight
+ ld a, [hl] ; save existing value of [hDexWeight]
+ push af
+ ld a, [de] ; a = upper byte of weight
+ ld [hli], a ; store upper byte of weight in [hDexWeight]
+ ld a, [hl] ; save existing value of [hDexWeight + 1]
+ push af
+ dec de
+ ld a, [de] ; a = lower byte of weight
+ ld [hl], a ; store lower byte of weight in [hDexWeight + 1]
+ ld de, hDexWeight
+ coord hl, 11, 8
+ lb bc, 2, 5 ; 2 bytes, 5 digits
+ call PrintNumber ; print weight
+ coord hl, 14, 8
+ ld a, [hDexWeight + 1]
+ sub 10
+ ld a, [hDexWeight]
+ sbc 0
+ jr nc, .next
+ ld [hl], "0" ; if the weight is less than 10, put a 0 before the decimal point
+.next
+ inc hl
+ ld a, [hli]
+ ld [hld], a ; make space for the decimal point by moving the last digit forward one tile
+ ld [hl], "⠄" ; decimal point tile
+ pop af
+ ld [hDexWeight + 1], a ; restore original value of [hDexWeight + 1]
+ pop af
+ ld [hDexWeight], a ; restore original value of [hDexWeight]
+ pop hl
+ inc hl ; hl = address of pokedex description text
+ coord bc, 1, 11
+ ld a, 2
+ ld [$fff4], a
+ call TextCommandProcessor ; print pokedex description text
+ xor a
+ ld [$fff4], a
+.waitForButtonPress
+ call JoypadLowSensitivity
+ ld a, [hJoy5]
+ and A_BUTTON | B_BUTTON
+ jr z, .waitForButtonPress
+ pop af
+ ld [hTilesetType], a
+ call GBPalWhiteOut
+ call ClearScreen
+ call RunDefaultPaletteCommand
+ call LoadTextBoxTilePatterns
+ call GBPalNormal
+ ld hl, wd72c
+ res 1, [hl]
+ ld a, $77 ; max volume
+ ld [rNR50], a
+ ret
+
+HeightWeightText:
+ db "HT ?",$60,"??",$61
+ next "WT ???lb@"
+
+; XXX does anything point to this?
+PokeText:
+ db "#@"
+
+; horizontal line that divides the pokedex text description from the rest of the data
+PokedexDataDividerLine:
+ db $68,$69,$6B,$69,$6B
+ db $69,$6B,$69,$6B,$6B
+ db $6B,$6B,$69,$6B,$69
+ db $6B,$69,$6B,$69,$6A
+ db "@"
+
+; draws a line of tiles
+; INPUT:
+; b = tile ID
+; c = number of tile ID's to write
+; de = amount to destination address after each tile (1 for horizontal, 20 for vertical)
+; hl = destination address
+DrawTileLine:
+ push bc
+ push de
+.loop
+ ld [hl], b
+ add hl, de
+ dec c
+ jr nz, .loop
+ pop de
+ pop bc
+ ret
+
+ inclang data/pokedex_entries.asm
+
+PokedexToIndex:
+ ; converts the Pokédex number at wd11e to an index
+ push bc
+ push hl
+ ld a, [wd11e]
+ ld b, a
+ ld c, 0
+ ld hl, PokedexOrder
+
+.loop ; go through the list until we find an entry with a matching dex number
+ inc c
+ ld a, [hli]
+ cp b
+ jr nz, .loop
+
+ ld a, c
+ ld [wd11e], a
+ pop hl
+ pop bc
+ ret
+
+IndexToPokedex:
+ ; converts the index number at wd11e to a Pokédex number
+ push bc
+ push hl
+ ld a, [wd11e]
+ dec a
+ ld hl, PokedexOrder
+ ld b, 0
+ ld c, a
+ add hl, bc
+ ld a, [hl]
+ ld [wd11e], a
+ pop hl
+ pop bc
+ ret
+
+INCLUDE "data/pokedex_order.asm"
diff --git a/en/engine/menu/prize_menu.asm b/en/engine/menu/prize_menu.asm
new file mode 100755
index 00000000..86ebb6a8
--- /dev/null
+++ b/en/engine/menu/prize_menu.asm
@@ -0,0 +1,306 @@
+CeladonPrizeMenu:
+ ld b, COIN_CASE
+ call IsItemInBag
+ jr nz, .havingCoinCase
+ ld hl, RequireCoinCaseTextPtr
+ jp PrintText
+.havingCoinCase
+ ld hl, wd730
+ set 6, [hl] ; disable letter-printing delay
+ ld hl, ExchangeCoinsForPrizesTextPtr
+ call PrintText
+; the following are the menu settings
+ xor a
+ ld [wCurrentMenuItem], a
+ ld [wLastMenuItem], a
+ ld a, A_BUTTON | B_BUTTON
+ ld [wMenuWatchedKeys], a
+ ld a, $03
+ ld [wMaxMenuItem], a
+ ld a, $04
+ ld [wTopMenuItemY], a
+ ld a, $01
+ ld [wTopMenuItemX], a
+ call PrintPrizePrice
+ coord hl, 0, 2
+ ld b, 8
+ ld c, 16
+ call TextBoxBorder
+ call GetPrizeMenuId
+ call UpdateSprites
+ ld hl, WhichPrizeTextPtr
+ call PrintText
+ call HandleMenuInput ; menu choice handler
+ bit 1, a ; keypress = B (Cancel)
+ jr nz, .noChoice
+ ld a, [wCurrentMenuItem]
+ cp 3 ; "NO,THANKS" choice
+ jr z, .noChoice
+ call HandlePrizeChoice
+.noChoice
+ ld hl, wd730
+ res 6, [hl]
+ ret
+
+RequireCoinCaseTextPtr:
+ TX_FAR _RequireCoinCaseText
+ TX_WAIT
+ db "@"
+
+ExchangeCoinsForPrizesTextPtr:
+ TX_FAR _ExchangeCoinsForPrizesText
+ db "@"
+
+WhichPrizeTextPtr:
+ TX_FAR _WhichPrizeText
+ db "@"
+
+GetPrizeMenuId:
+; determine which one among the three
+; prize-texts has been selected
+; using the text ID (stored in [hSpriteIndexOrTextID])
+; load the three prizes at wd13d-wd13f
+; load the three prices at wd141-wd146
+; display the three prizes' names
+; (distinguishing between Pokemon names
+; and Items (specifically TMs) names)
+ ld a, [hSpriteIndexOrTextID]
+ sub 3 ; prize-texts' id are 3, 4 and 5
+ ld [wWhichPrizeWindow], a ; prize-texts' id (relative, i.e. 0, 1 or 2)
+ add a
+ add a
+ ld d, 0
+ ld e, a
+ ld hl, PrizeDifferentMenuPtrs
+ add hl, de
+ ld a, [hli]
+ ld d, [hl]
+ ld e, a
+ inc hl
+ push hl
+ ld hl, wPrize1
+ call CopyString
+ pop hl
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld de, wPrize1Price
+ ld bc, 6
+ call CopyData
+ ld a, [wWhichPrizeWindow]
+ cp 2 ;is TM_menu?
+ jr nz, .putMonName
+ ld a, [wPrize1]
+ ld [wd11e], a
+ call GetItemName
+ coord hl, 2, 4
+ call PlaceString
+ ld a, [wPrize2]
+ ld [wd11e], a
+ call GetItemName
+ coord hl, 2, 6
+ call PlaceString
+ ld a, [wPrize3]
+ ld [wd11e], a
+ call GetItemName
+ coord hl, 2, 8
+ call PlaceString
+ jr .putNoThanksText
+.putMonName
+ ld a, [wPrize1]
+ ld [wd11e], a
+ call GetMonName
+ coord hl, 2, 4
+ call PlaceString
+ ld a, [wPrize2]
+ ld [wd11e], a
+ call GetMonName
+ coord hl, 2, 6
+ call PlaceString
+ ld a, [wPrize3]
+ ld [wd11e], a
+ call GetMonName
+ coord hl, 2, 8
+ call PlaceString
+.putNoThanksText
+ coord hl, 2, 10
+ ld de, NoThanksText
+ call PlaceString
+; put prices on the right side of the textbox
+ ld de, wPrize1Price
+ coord hl, 13, 5
+; reg. c:
+; [low nybble] number of bytes
+; [bit 765 = %100] space-padding (not zero-padding)
+ ld c, (1 << 7 | 2)
+; Function $15CD displays BCD value (same routine
+; used by text-command $02)
+ call PrintBCDNumber
+ ld de, wPrize2Price
+ coord hl, 13, 7
+ ld c, (1 << 7 | 2)
+ call PrintBCDNumber
+ ld de, wPrize3Price
+ coord hl, 13, 9
+ ld c, (1 << 7 | 2)
+ jp PrintBCDNumber
+
+ inclang data/prizes.asm
+
+PrintPrizePrice:
+ coord hl, 11, 0
+ ld b, 1
+ ld c, 7
+ call TextBoxBorder
+ call UpdateSprites
+ coord hl, 12, 0
+ ld de, .CoinString
+ call PlaceString
+ coord hl, 13, 1
+ ld de, .SixSpacesString
+ call PlaceString
+ coord hl, 13, 1
+ ld de, wPlayerCoins
+ ld c, %10000010
+ call PrintBCDNumber
+ ret
+
+.CoinString:
+ db "COIN@"
+
+.SixSpacesString:
+ db " @"
+
+LoadCoinsToSubtract:
+ ld a, [wWhichPrize]
+ add a
+ ld d, 0
+ ld e, a
+ ld hl, wPrize1Price
+ add hl, de ; get selected prize's price
+ xor a
+ ld [hUnusedCoinsByte], a
+ ld a, [hli]
+ ld [hCoins], a
+ ld a, [hl]
+ ld [hCoins + 1], a
+ ret
+
+HandlePrizeChoice:
+ ld a, [wCurrentMenuItem]
+ ld [wWhichPrize], a
+ ld d, 0
+ ld e, a
+ ld hl, wPrize1
+ add hl, de
+ ld a, [hl]
+ ld [wd11e], a
+ ld a, [wWhichPrizeWindow]
+ cp 2 ; is prize a TM?
+ jr nz, .getMonName
+ call GetItemName
+ jr .givePrize
+.getMonName
+ call GetMonName
+.givePrize
+ ld hl, SoYouWantPrizeTextPtr
+ call PrintText
+ call YesNoChoice
+ ld a, [wCurrentMenuItem] ; yes/no answer (Y=0, N=1)
+ and a
+ jr nz, .printOhFineThen
+ call LoadCoinsToSubtract
+ call HasEnoughCoins
+ jr c, .notEnoughCoins
+ ld a, [wWhichPrizeWindow]
+ cp $02
+ jr nz, .giveMon
+ ld a, [wd11e]
+ ld b, a
+ ld a, 1
+ ld c, a
+ call GiveItem
+ jr nc, .bagFull
+ jr .subtractCoins
+.giveMon
+ ld a, [wd11e]
+ ld [wcf91], a
+ push af
+ call GetPrizeMonLevel
+ ld c, a
+ pop af
+ ld b, a
+ call GivePokemon
+
+; If either the party or box was full, wait after displaying message.
+ push af
+ ld a, [wAddedToParty]
+ and a
+ call z, WaitForTextScrollButtonPress
+ pop af
+
+; If the mon couldn't be given to the player (because both the party and box
+; were full), return without subtracting coins.
+ ret nc
+
+.subtractCoins
+ call LoadCoinsToSubtract
+ ld hl, hCoins + 1
+ ld de, wPlayerCoins + 1
+ ld c, $02 ; how many bytes
+ predef SubBCDPredef
+ jp PrintPrizePrice
+.bagFull
+ ld hl, PrizeRoomBagIsFullTextPtr
+ jp PrintText
+.notEnoughCoins
+ ld hl, SorryNeedMoreCoinsText
+ jp PrintText
+.printOhFineThen
+ ld hl, OhFineThenTextPtr
+ jp PrintText
+
+UnknownPrizeData:
+; XXX what's this?
+ db $00,$01,$00,$01,$00,$01,$00,$00,$01
+
+HereYouGoTextPtr:
+ TX_FAR _HereYouGoText
+ TX_WAIT
+ db "@"
+
+SoYouWantPrizeTextPtr:
+ TX_FAR _SoYouWantPrizeText
+ db "@"
+
+SorryNeedMoreCoinsText:
+ TX_FAR _SorryNeedMoreCoinsText
+ TX_WAIT
+ db "@"
+
+PrizeRoomBagIsFullTextPtr:
+ TX_FAR _OopsYouDontHaveEnoughRoomText
+ TX_WAIT
+ db "@"
+
+OhFineThenTextPtr:
+ TX_FAR _OhFineThenText
+ TX_WAIT
+ db "@"
+
+GetPrizeMonLevel:
+ ld a, [wcf91]
+ ld b, a
+ ld hl, PrizeMonLevelDictionary
+.loop
+ ld a, [hli]
+ cp b
+ jr z, .matchFound
+ inc hl
+ jr .loop
+.matchFound
+ ld a, [hl]
+ ld [wCurEnemyLVL], a
+ ret
+
+INCLUDE "data/prize_mon_levels.asm"
diff --git a/en/engine/menu/start_sub_menus.asm b/en/engine/menu/start_sub_menus.asm
new file mode 100755
index 00000000..947837e3
--- /dev/null
+++ b/en/engine/menu/start_sub_menus.asm
@@ -0,0 +1,854 @@
+StartMenu_Pokedex:
+ predef ShowPokedexMenu
+ call LoadScreenTilesFromBuffer2 ; restore saved screen
+ call Delay3
+ call LoadGBPal
+ call UpdateSprites
+ jp RedisplayStartMenu
+
+StartMenu_Pokemon:
+ ld a, [wPartyCount]
+ and a
+ jp z, RedisplayStartMenu
+ xor a
+ ld [wMenuItemToSwap], a
+ ld [wPartyMenuTypeOrMessageID], a
+ ld [wUpdateSpritesEnabled], a
+ call DisplayPartyMenu
+ jr .checkIfPokemonChosen
+.loop
+ xor a
+ ld [wMenuItemToSwap], a
+ ld [wPartyMenuTypeOrMessageID], a
+ call GoBackToPartyMenu
+.checkIfPokemonChosen
+ jr nc, .chosePokemon
+.exitMenu
+ call GBPalWhiteOutWithDelay3
+ call RestoreScreenTilesAndReloadTilePatterns
+ call LoadGBPal
+ jp RedisplayStartMenu
+.chosePokemon
+ call SaveScreenTilesToBuffer1
+ ld a, FIELD_MOVE_MON_MENU
+ ld [wTextBoxID], a
+ call DisplayTextBoxID ; display pokemon menu options
+ ld hl, wFieldMoves
+ lb bc, 2, 12 ; max menu item ID, top menu item Y
+ ld e, 5
+.adjustMenuVariablesLoop
+ dec e
+ jr z, .storeMenuVariables
+ ld a, [hli]
+ and a ; end of field moves?
+ jr z, .storeMenuVariables
+ inc b
+ dec c
+ dec c
+ jr .adjustMenuVariablesLoop
+.storeMenuVariables
+ ld hl, wTopMenuItemY
+ ld a, c
+ ld [hli], a ; top menu item Y
+ ld a, [hFieldMoveMonMenuTopMenuItemX]
+ ld [hli], a ; top menu item X
+ xor a
+ ld [hli], a ; current menu item ID
+ inc hl
+ ld a, b
+ ld [hli], a ; max menu item ID
+ ld a, A_BUTTON | B_BUTTON
+ ld [hli], a ; menu watched keys
+ xor a
+ ld [hl], a
+ call HandleMenuInput
+ push af
+ call LoadScreenTilesFromBuffer1 ; restore saved screen
+ pop af
+ bit 1, a ; was the B button pressed?
+ jp nz, .loop
+; if the B button wasn't pressed
+ ld a, [wMaxMenuItem]
+ ld b, a
+ ld a, [wCurrentMenuItem] ; menu selection
+ cp b
+ jp z, .exitMenu ; if the player chose Cancel
+ dec b
+ cp b
+ jr z, .choseSwitch
+ dec b
+ cp b
+ jp z, .choseStats
+ ld c, a
+ ld b, 0
+ ld hl, wFieldMoves
+ add hl, bc
+ jp .choseOutOfBattleMove
+.choseSwitch
+ ld a, [wPartyCount]
+ cp 2 ; is there more than one pokemon in the party?
+ jp c, StartMenu_Pokemon ; if not, no switching
+ call SwitchPartyMon_InitVarOrSwapData ; init [wMenuItemToSwap]
+ ld a, SWAP_MONS_PARTY_MENU
+ ld [wPartyMenuTypeOrMessageID], a
+ call GoBackToPartyMenu
+ jp .checkIfPokemonChosen
+.choseStats
+ call ClearSprites
+ xor a ; PLAYER_PARTY_DATA
+ ld [wMonDataLocation], a
+ predef StatusScreen
+ predef StatusScreen2
+ call ReloadMapData
+ jp StartMenu_Pokemon
+.choseOutOfBattleMove
+ push hl
+ ld a, [wWhichPokemon]
+ ld hl, wPartyMonNicks
+ call GetPartyMonName
+ pop hl
+ ld a, [hl]
+ dec a
+ add a
+ ld b, 0
+ ld c, a
+ ld hl, .outOfBattleMovePointers
+ add hl, bc
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld a, [wObtainedBadges] ; badges obtained
+ jp hl
+.outOfBattleMovePointers
+ dw .cut
+ dw .fly
+ dw .surf
+ dw .surf
+ dw .strength
+ dw .flash
+ dw .dig
+ dw .teleport
+ dw .softboiled
+.fly
+ bit 2, a ; does the player have the Thunder Badge?
+ jp z, .newBadgeRequired
+ call CheckIfInOutsideMap
+ jr z, .canFly
+ ld a, [wWhichPokemon]
+ ld hl, wPartyMonNicks
+ call GetPartyMonName
+ ld hl, .cannotFlyHereText
+ call PrintText
+ jp .loop
+.canFly
+ call ChooseFlyDestination
+ ld a, [wd732]
+ bit 3, a ; did the player decide to fly?
+ jp nz, .goBackToMap
+ call LoadFontTilePatterns
+ ld hl, wd72e
+ set 1, [hl]
+ jp StartMenu_Pokemon
+.cut
+ bit 1, a ; does the player have the Cascade Badge?
+ jp z, .newBadgeRequired
+ predef UsedCut
+ ld a, [wActionResultOrTookBattleTurn]
+ and a
+ jp z, .loop
+ jp CloseTextDisplay
+.surf
+ bit 4, a ; does the player have the Soul Badge?
+ jp z, .newBadgeRequired
+ callba IsSurfingAllowed
+ ld hl, wd728
+ bit 1, [hl]
+ res 1, [hl]
+ jp z, .loop
+ ld a, SURFBOARD
+ ld [wcf91], a
+ ld [wPseudoItemID], a
+ call UseItem
+ ld a, [wActionResultOrTookBattleTurn]
+ and a
+ jp z, .loop
+ call GBPalWhiteOutWithDelay3
+ jp .goBackToMap
+.strength
+ bit 3, a ; does the player have the Rainbow Badge?
+ jp z, .newBadgeRequired
+ predef PrintStrengthTxt
+ call GBPalWhiteOutWithDelay3
+ jp .goBackToMap
+.flash
+ bit 0, a ; does the player have the Boulder Badge?
+ jp z, .newBadgeRequired
+ xor a
+ ld [wMapPalOffset], a
+ ld hl, .flashLightsAreaText
+ call PrintText
+ call GBPalWhiteOutWithDelay3
+ jp .goBackToMap
+.flashLightsAreaText
+ TX_FAR _FlashLightsAreaText
+ db "@"
+.dig
+ ld a, ESCAPE_ROPE
+ ld [wcf91], a
+ ld [wPseudoItemID], a
+ call UseItem
+ ld a, [wActionResultOrTookBattleTurn]
+ and a
+ jp z, .loop
+ call GBPalWhiteOutWithDelay3
+ jp .goBackToMap
+.teleport
+ call CheckIfInOutsideMap
+ jr z, .canTeleport
+ ld a, [wWhichPokemon]
+ ld hl, wPartyMonNicks
+ call GetPartyMonName
+ ld hl, .cannotUseTeleportNowText
+ call PrintText
+ jp .loop
+.canTeleport
+ ld hl, .warpToLastPokemonCenterText
+ call PrintText
+ ld hl, wd732
+ set 3, [hl]
+ set 6, [hl]
+ ld hl, wd72e
+ set 1, [hl]
+ res 4, [hl]
+ ld c, 60
+ call DelayFrames
+ call GBPalWhiteOutWithDelay3
+ jp .goBackToMap
+.warpToLastPokemonCenterText
+ TX_FAR _WarpToLastPokemonCenterText
+ db "@"
+.cannotUseTeleportNowText
+ TX_FAR _CannotUseTeleportNowText
+ db "@"
+.cannotFlyHereText
+ TX_FAR _CannotFlyHereText
+ db "@"
+.softboiled
+ ld hl, wPartyMon1MaxHP
+ ld a, [wWhichPokemon]
+ ld bc, wPartyMon2 - wPartyMon1
+ call AddNTimes
+ ld a, [hli]
+ ld [H_DIVIDEND], a
+ ld a, [hl]
+ ld [H_DIVIDEND + 1], a
+ ld a, 5
+ ld [H_DIVISOR], a
+ ld b, 2 ; number of bytes
+ call Divide
+ ld bc, wPartyMon1HP - wPartyMon1MaxHP
+ add hl, bc
+ ld a, [hld]
+ ld b, a
+ ld a, [H_QUOTIENT + 3]
+ sub b
+ ld b, [hl]
+ ld a, [H_QUOTIENT + 2]
+ sbc b
+ jp nc, .notHealthyEnough
+ ld a, [wPartyAndBillsPCSavedMenuItem]
+ push af
+ ld a, POTION
+ ld [wcf91], a
+ ld [wPseudoItemID], a
+ call UseItem
+ pop af
+ ld [wPartyAndBillsPCSavedMenuItem], a
+ jp .loop
+.notHealthyEnough ; if current HP is less than 1/5 of max HP
+ ld hl, .notHealthyEnoughText
+ call PrintText
+ jp .loop
+.notHealthyEnoughText
+ TX_FAR _NotHealthyEnoughText
+ db "@"
+.goBackToMap
+ call RestoreScreenTilesAndReloadTilePatterns
+ jp CloseTextDisplay
+.newBadgeRequired
+ ld hl, .newBadgeRequiredText
+ call PrintText
+ jp .loop
+.newBadgeRequiredText
+ TX_FAR _NewBadgeRequiredText
+ db "@"
+
+; writes a blank tile to all possible menu cursor positions on the party menu
+ErasePartyMenuCursors:
+ coord hl, 0, 1
+ ld bc, 2 * 20 ; menu cursor positions are 2 rows apart
+ ld a, 6 ; 6 menu cursor positions
+.loop
+ ld [hl], " "
+ add hl, bc
+ dec a
+ jr nz, .loop
+ ret
+
+ItemMenuLoop:
+ call LoadScreenTilesFromBuffer2DisableBGTransfer ; restore saved screen
+ call RunDefaultPaletteCommand
+
+StartMenu_Item:
+ ld a, [wLinkState]
+ dec a ; is the player in the Colosseum or Trade Centre?
+ jr nz, .notInCableClubRoom
+ ld hl, CannotUseItemsHereText
+ call PrintText
+ jr .exitMenu
+.notInCableClubRoom
+ ld bc, wNumBagItems
+ ld hl, wListPointer
+ ld a, c
+ ld [hli], a
+ ld [hl], b ; store item bag pointer in wListPointer (for DisplayListMenuID)
+ 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
+ jr nc, .choseItem
+.exitMenu
+ call LoadScreenTilesFromBuffer2 ; restore saved screen
+ call LoadTextBoxTilePatterns
+ call UpdateSprites
+ jp RedisplayStartMenu
+.choseItem
+; erase menu cursor (blank each tile in front of an item name)
+ ld a, " "
+ Coorda 5, 4
+ Coorda 5, 6
+ Coorda 5, 8
+ Coorda 5, 10
+ call PlaceUnfilledArrowMenuCursor
+ xor a
+ ld [wMenuItemToSwap], a
+ ld a, [wcf91]
+ cp BICYCLE
+ jp z, .useOrTossItem
+.notBicycle1
+ ld a, USE_TOSS_MENU_TEMPLATE
+ ld [wTextBoxID], a
+ call DisplayTextBoxID
+ ld hl, wTopMenuItemY
+ ld a, 11
+ ld [hli], a ; top menu item Y
+ ld a, 14
+ ld [hli], a ; top menu item X
+ xor a
+ ld [hli], a ; current menu item ID
+ inc hl
+ inc a ; a = 1
+ ld [hli], a ; max menu item ID
+ ld a, A_BUTTON | B_BUTTON
+ ld [hli], a ; menu watched keys
+ xor a
+ ld [hl], a ; old menu item id
+ call HandleMenuInput
+ call PlaceUnfilledArrowMenuCursor
+ bit 1, a ; was the B button pressed?
+ jr z, .useOrTossItem
+ jp ItemMenuLoop
+.useOrTossItem ; if the player made the choice to use or toss the item
+ ld a, [wcf91]
+ ld [wd11e], a
+ call GetItemName
+ call CopyStringToCF4B ; copy name to wcf4b
+ ld a, [wcf91]
+ cp BICYCLE
+ jr nz, .notBicycle2
+ ld a, [wd732]
+ bit 5, a
+ jr z, .useItem_closeMenu
+ ld hl, CannotGetOffHereText
+ call PrintText
+ jp ItemMenuLoop
+.notBicycle2
+ ld a, [wCurrentMenuItem]
+ and a
+ jr nz, .tossItem
+; use item
+ ld [wPseudoItemID], a ; a must be 0 due to above conditional jump
+ ld a, [wcf91]
+ cp HM_01
+ jr nc, .useItem_partyMenu
+ ld hl, UsableItems_CloseMenu
+ ld de, 1
+ call IsInArray
+ jr c, .useItem_closeMenu
+ ld a, [wcf91]
+ ld hl, UsableItems_PartyMenu
+ ld de, 1
+ call IsInArray
+ jr c, .useItem_partyMenu
+ call UseItem
+ jp ItemMenuLoop
+.useItem_closeMenu
+ xor a
+ ld [wPseudoItemID], a
+ call UseItem
+ ld a, [wActionResultOrTookBattleTurn]
+ and a
+ jp z, ItemMenuLoop
+ jp CloseStartMenu
+.useItem_partyMenu
+ ld a, [wUpdateSpritesEnabled]
+ push af
+ call UseItem
+ ld a, [wActionResultOrTookBattleTurn]
+ cp $02
+ jp z, .partyMenuNotDisplayed
+ call GBPalWhiteOutWithDelay3
+ call RestoreScreenTilesAndReloadTilePatterns
+ pop af
+ ld [wUpdateSpritesEnabled], a
+ jp StartMenu_Item
+.partyMenuNotDisplayed
+ pop af
+ ld [wUpdateSpritesEnabled], a
+ jp ItemMenuLoop
+.tossItem
+ call IsKeyItem
+ ld a, [wIsKeyItem]
+ and a
+ jr nz, .skipAskingQuantity
+ ld a, [wcf91]
+ call IsItemHM
+ jr c, .skipAskingQuantity
+ call DisplayChooseQuantityMenu
+ inc a
+ jr z, .tossZeroItems
+.skipAskingQuantity
+ ld hl, wNumBagItems
+ call TossItem
+.tossZeroItems
+ jp ItemMenuLoop
+
+CannotUseItemsHereText:
+ TX_FAR _CannotUseItemsHereText
+ db "@"
+
+CannotGetOffHereText:
+ TX_FAR _CannotGetOffHereText
+ db "@"
+
+; items which bring up the party menu when used
+UsableItems_PartyMenu:
+ db MOON_STONE
+ db ANTIDOTE
+ db BURN_HEAL
+ db ICE_HEAL
+ db AWAKENING
+ db PARLYZ_HEAL
+ db FULL_RESTORE
+ db MAX_POTION
+ db HYPER_POTION
+ db SUPER_POTION
+ db POTION
+ db FIRE_STONE
+ db THUNDER_STONE
+ db WATER_STONE
+ db HP_UP
+ db PROTEIN
+ db IRON
+ db CARBOS
+ db CALCIUM
+ db RARE_CANDY
+ db LEAF_STONE
+ db FULL_HEAL
+ db REVIVE
+ db MAX_REVIVE
+ db FRESH_WATER
+ db SODA_POP
+ db LEMONADE
+ db X_ATTACK
+ db X_DEFEND
+ db X_SPEED
+ db X_SPECIAL
+ db PP_UP
+ db ETHER
+ db MAX_ETHER
+ db ELIXER
+ db MAX_ELIXER
+ db $ff
+
+; items which close the item menu when used
+UsableItems_CloseMenu:
+ db ESCAPE_ROPE
+ db ITEMFINDER
+ db POKE_FLUTE
+ db OLD_ROD
+ db GOOD_ROD
+ db SUPER_ROD
+ db $ff
+
+StartMenu_TrainerInfo:
+ call GBPalWhiteOut
+ call ClearScreen
+ call UpdateSprites
+ ld a, [hTilesetType]
+ push af
+ xor a
+ ld [hTilesetType], a
+ call DrawTrainerInfo
+ predef DrawBadges ; draw badges
+ ld b, SET_PAL_TRAINER_CARD
+ call RunPaletteCommand
+ call GBPalNormal
+ call WaitForTextScrollButtonPress ; wait for button press
+ call GBPalWhiteOut
+ call LoadFontTilePatterns
+ call LoadScreenTilesFromBuffer2 ; restore saved screen
+ call RunDefaultPaletteCommand
+ call ReloadMapData
+ call LoadGBPal
+ pop af
+ ld [hTilesetType], a
+ jp RedisplayStartMenu
+
+; loads tile patterns and draws everything except for gym leader faces / badges
+DrawTrainerInfo:
+ ld de, RedPicFront
+ lb bc, BANK(RedPicFront), $01
+ predef DisplayPicCenteredOrUpperRight
+ call DisableLCD
+ coord hl, 0, 2
+ ld a, " "
+ call TrainerInfo_DrawVerticalLine
+ coord hl, 1, 2
+ call TrainerInfo_DrawVerticalLine
+ ld hl, vChars2 + $70
+ ld de, vChars2
+ ld bc, $70 * 4
+ call CopyData
+ ld hl, TrainerInfoTextBoxTileGraphics ; trainer info text box tile patterns
+ ld de, vChars2 + $770
+ ld bc, $0080
+ push bc
+ call TrainerInfo_FarCopyData
+ ld hl, BlankLeaderNames
+ ld de, vChars2 + $600
+ ld bc, $0170
+ call TrainerInfo_FarCopyData
+ pop bc
+ ld hl, BadgeNumbersTileGraphics ; badge number tile patterns
+ ld de, vChars1 + $580
+ call TrainerInfo_FarCopyData
+ ld hl, GymLeaderFaceAndBadgeTileGraphics ; gym leader face and badge tile patterns
+ ld de, vChars2 + $200
+ ld bc, $0400
+ ld a, $03
+ call FarCopyData2
+ ld hl, TextBoxGraphics
+ ld de, $00d0
+ add hl, de ; hl = colon tile pattern
+ ld de, vChars1 + $560
+ ld bc, $0010
+ ld a, $04
+ push bc
+ call FarCopyData2
+ pop bc
+ ld hl, TrainerInfoTextBoxTileGraphics + $80 ; background tile pattern
+ ld de, vChars1 + $570
+ call TrainerInfo_FarCopyData
+ call EnableLCD
+ ld hl, wTrainerInfoTextBoxWidthPlus1
+ ld a, 18 + 1
+ ld [hli], a
+ dec a
+ ld [hli], a
+ ld [hl], 1
+ coord hl, 0, 0
+ call TrainerInfo_DrawTextBox
+ ld hl, wTrainerInfoTextBoxWidthPlus1
+ ld a, 16 + 1
+ ld [hli], a
+ dec a
+ ld [hli], a
+ ld [hl], 3
+ coord hl, 1, 10
+ call TrainerInfo_DrawTextBox
+ coord hl, 0, 10
+ ld a, $d7
+ call TrainerInfo_DrawVerticalLine
+ coord hl, 19, 10
+ call TrainerInfo_DrawVerticalLine
+ coord hl, 6, 9
+ ld de, TrainerInfo_BadgesText
+ call PlaceString
+ coord hl, 2, 2
+ ld de, TrainerInfo_NameMoneyTimeText
+ call PlaceString
+ coord hl, 7, 2
+ ld de, wPlayerName
+ call PlaceString
+ coord hl, 8, 4
+ ld de, wPlayerMoney
+ ld c, $e3
+ call PrintBCDNumber
+ coord hl, 9, 6
+ ld de, wPlayTimeHours ; hours
+ lb bc, LEFT_ALIGN | 1, 3
+ call PrintNumber
+ ld [hl], $d6 ; colon tile ID
+ inc hl
+ ld de, wPlayTimeMinutes ; minutes
+ lb bc, LEADING_ZEROES | 1, 2
+ jp PrintNumber
+
+TrainerInfo_FarCopyData:
+ ld a, BANK(TrainerInfoTextBoxTileGraphics)
+ jp FarCopyData2
+
+TrainerInfo_NameMoneyTimeText:
+ db "NAME/"
+ next "MONEY/"
+ next "TIME/@"
+
+; $76 is a circle tile
+TrainerInfo_BadgesText:
+ db $76,"BADGES",$76,"@"
+
+; draws a text box on the trainer info screen
+; height is always 6
+; INPUT:
+; hl = destination address
+; [wTrainerInfoTextBoxWidthPlus1] = width
+; [wTrainerInfoTextBoxWidth] = width - 1
+; [wTrainerInfoTextBoxNextRowOffset] = distance from the end of a text box row to the start of the next
+TrainerInfo_DrawTextBox:
+ ld a, $79 ; upper left corner tile ID
+ lb de, $7a, $7b ; top edge and upper right corner tile ID's
+ call TrainerInfo_DrawHorizontalEdge ; draw top edge
+ call TrainerInfo_NextTextBoxRow
+ ld a, [wTrainerInfoTextBoxWidthPlus1]
+ ld e, a
+ ld d, 0
+ ld c, 6 ; height of the text box
+.loop
+ ld [hl], $7c ; left edge tile ID
+ add hl, de
+ ld [hl], $78 ; right edge tile ID
+ call TrainerInfo_NextTextBoxRow
+ dec c
+ jr nz, .loop
+ ld a, $7d ; lower left corner tile ID
+ lb de, $77, $7e ; bottom edge and lower right corner tile ID's
+
+TrainerInfo_DrawHorizontalEdge:
+ ld [hli], a ; place left corner tile
+ ld a, [wTrainerInfoTextBoxWidth]
+ ld c, a
+ ld a, d
+.loop
+ ld [hli], a ; place edge tile
+ dec c
+ jr nz, .loop
+ ld a, e
+ ld [hl], a ; place right corner tile
+ ret
+
+TrainerInfo_NextTextBoxRow:
+ ld a, [wTrainerInfoTextBoxNextRowOffset] ; distance to the start of the next row
+.loop
+ inc hl
+ dec a
+ jr nz, .loop
+ ret
+
+; draws a vertical line
+; INPUT:
+; hl = address of top tile in the line
+; a = tile ID
+TrainerInfo_DrawVerticalLine:
+ ld de, SCREEN_WIDTH
+ ld c, 8
+.loop
+ ld [hl], a
+ add hl, de
+ dec c
+ jr nz, .loop
+ ret
+
+StartMenu_SaveReset:
+ ld a, [wd72e]
+ bit 6, a ; is the player using the link feature?
+ jp nz, Init
+ predef SaveSAV ; save the game
+ call LoadScreenTilesFromBuffer2 ; restore saved screen
+ jp HoldTextDisplayOpen
+
+StartMenu_Option:
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call ClearScreen
+ call UpdateSprites
+ callab DisplayOptionMenu
+ call LoadScreenTilesFromBuffer2 ; restore saved screen
+ call LoadTextBoxTilePatterns
+ call UpdateSprites
+ jp RedisplayStartMenu
+
+SwitchPartyMon:
+ call SwitchPartyMon_InitVarOrSwapData ; swap data
+ ld a, [wSwappedMenuItem]
+ call SwitchPartyMon_ClearGfx
+ ld a, [wCurrentMenuItem]
+ call SwitchPartyMon_ClearGfx
+ jp RedrawPartyMenu_
+
+SwitchPartyMon_ClearGfx:
+ push af
+ coord hl, 0, 0
+ ld bc, SCREEN_WIDTH * 2
+ call AddNTimes
+ ld c, SCREEN_WIDTH * 2
+ ld a, " "
+.clearMonBGLoop ; clear the mon's row in the party menu
+ ld [hli], a
+ dec c
+ jr nz, .clearMonBGLoop
+ pop af
+ ld hl, wOAMBuffer
+ ld bc, $10
+ call AddNTimes
+ ld de, $4
+ ld c, e
+.clearMonOAMLoop
+ ld [hl], $a0
+ add hl, de
+ dec c
+ jr nz, .clearMonOAMLoop
+ call WaitForSoundToFinish
+ ld a, SFX_SWAP
+ jp PlaySound
+
+SwitchPartyMon_InitVarOrSwapData:
+; This is used to initialise [wMenuItemToSwap] and to actually swap the data.
+ ld a, [wMenuItemToSwap]
+ and a ; has [wMenuItemToSwap] been initialised yet?
+ jr nz, .pickedMonsToSwap
+; If not, initialise [wMenuItemToSwap] so that it matches the current mon.
+ ld a, [wWhichPokemon]
+ inc a ; [wMenuItemToSwap] counts from 1
+ ld [wMenuItemToSwap], a
+ ret
+.pickedMonsToSwap
+ xor a
+ ld [wPartyMenuTypeOrMessageID], a
+ ld a, [wMenuItemToSwap]
+ dec a
+ ld b, a
+ ld a, [wCurrentMenuItem]
+ ld [wSwappedMenuItem], a
+ cp b ; swapping a mon with itself?
+ jr nz, .swappingDifferentMons
+; can't swap a mon with itself
+ xor a
+ ld [wMenuItemToSwap], a
+ ld [wPartyMenuTypeOrMessageID], a
+ ret
+.swappingDifferentMons
+ ld a, b
+ ld [wMenuItemToSwap], a
+ push hl
+ push de
+ ld hl, wPartySpecies
+ ld d, h
+ ld e, l
+ ld a, [wCurrentMenuItem]
+ add l
+ ld l, a
+ jr nc, .noCarry
+ inc h
+.noCarry
+ ld a, [wMenuItemToSwap]
+ add e
+ ld e, a
+ jr nc, .noCarry2
+ inc d
+.noCarry2
+ ld a, [hl]
+ ld [hSwapTemp], a
+ ld a, [de]
+ ld [hl], a
+ ld a, [hSwapTemp]
+ ld [de], a
+ ld hl, wPartyMons
+ ld bc, wPartyMon2 - wPartyMon1
+ ld a, [wCurrentMenuItem]
+ call AddNTimes
+ push hl
+ ld de, wSwitchPartyMonTempBuffer
+ ld bc, wPartyMon2 - wPartyMon1
+ call CopyData
+ ld hl, wPartyMons
+ ld bc, wPartyMon2 - wPartyMon1
+ ld a, [wMenuItemToSwap]
+ call AddNTimes
+ pop de
+ push hl
+ ld bc, wPartyMon2 - wPartyMon1
+ call CopyData
+ pop de
+ ld hl, wSwitchPartyMonTempBuffer
+ ld bc, wPartyMon2 - wPartyMon1
+ call CopyData
+ ld hl, wPartyMonOT
+ ld a, [wCurrentMenuItem]
+ call SkipFixedLengthTextEntries
+ push hl
+ ld de, wSwitchPartyMonTempBuffer
+ ld bc, NAME_LENGTH
+ call CopyData
+ ld hl, wPartyMonOT
+ ld a, [wMenuItemToSwap]
+ call SkipFixedLengthTextEntries
+ pop de
+ push hl
+ ld bc, NAME_LENGTH
+ call CopyData
+ pop de
+ ld hl, wSwitchPartyMonTempBuffer
+ ld bc, NAME_LENGTH
+ call CopyData
+ ld hl, wPartyMonNicks
+ ld a, [wCurrentMenuItem]
+ call SkipFixedLengthTextEntries
+ push hl
+ ld de, wSwitchPartyMonTempBuffer
+ ld bc, NAME_LENGTH
+ call CopyData
+ ld hl, wPartyMonNicks
+ ld a, [wMenuItemToSwap]
+ call SkipFixedLengthTextEntries
+ pop de
+ push hl
+ ld bc, NAME_LENGTH
+ call CopyData
+ pop de
+ ld hl, wSwitchPartyMonTempBuffer
+ ld bc, NAME_LENGTH
+ call CopyData
+ ld a, [wMenuItemToSwap]
+ ld [wSwappedMenuItem], a
+ xor a
+ ld [wMenuItemToSwap], a
+ ld [wPartyMenuTypeOrMessageID], a
+ pop de
+ pop hl
+ ret
diff --git a/en/engine/menu/status_screen.asm b/en/engine/menu/status_screen.asm
new file mode 100755
index 00000000..ef94f13c
--- /dev/null
+++ b/en/engine/menu/status_screen.asm
@@ -0,0 +1,481 @@
+DrawHP:
+; Draws the HP bar in the stats screen
+ call GetPredefRegisters
+ ld a, $1
+ jr DrawHP_
+
+DrawHP2:
+; Draws the HP bar in the party screen
+ call GetPredefRegisters
+ ld a, $2
+
+DrawHP_:
+ ld [wHPBarType], a
+ push hl
+ ld a, [wLoadedMonHP]
+ ld b, a
+ ld a, [wLoadedMonHP + 1]
+ ld c, a
+ or b
+ jr nz, .nonzeroHP
+ xor a
+ ld c, a
+ ld e, a
+ ld a, $6
+ ld d, a
+ jp .drawHPBarAndPrintFraction
+.nonzeroHP
+ ld a, [wLoadedMonMaxHP]
+ ld d, a
+ ld a, [wLoadedMonMaxHP + 1]
+ ld e, a
+ predef HPBarLength
+ ld a, $6
+ ld d, a
+ ld c, a
+.drawHPBarAndPrintFraction
+ pop hl
+ push de
+ push hl
+ push hl
+ call DrawHPBar
+ pop hl
+ ld a, [hFlags_0xFFF6]
+ bit 0, a
+ jr z, .printFractionBelowBar
+ ld bc, $9 ; right of bar
+ jr .printFraction
+.printFractionBelowBar
+ ld bc, SCREEN_WIDTH + 1 ; below bar
+.printFraction
+ add hl, bc
+ ld de, wLoadedMonHP
+ lb bc, 2, 3
+ call PrintNumber
+ ld a, "/"
+ ld [hli], a
+ ld de, wLoadedMonMaxHP
+ lb bc, 2, 3
+ call PrintNumber
+ pop hl
+ pop de
+ ret
+
+
+; Predef 0x37
+StatusScreen:
+ call LoadMonData
+ ld a, [wMonDataLocation]
+ cp BOX_DATA
+ jr c, .DontRecalculate
+; mon is in a box or daycare
+ ld a, [wLoadedMonBoxLevel]
+ ld [wLoadedMonLevel], a
+ ld [wCurEnemyLVL], a
+ ld hl, wLoadedMonHPExp - 1
+ ld de, wLoadedMonStats
+ ld b, $1
+ call CalcStats ; Recalculate stats
+.DontRecalculate
+ ld hl, wd72c
+ set 1, [hl]
+ ld a, $33
+ ld [rNR50], a ; Reduce the volume
+ call GBPalWhiteOutWithDelay3
+ call ClearScreen
+ call UpdateSprites
+ call LoadHpBarAndStatusTilePatterns
+ ld de, BattleHudTiles1 ; source
+ ld hl, vChars2 + $6d0 ; dest
+ lb bc, BANK(BattleHudTiles1), $03
+ call CopyVideoDataDouble ; ·│ :L and halfarrow line end
+ ld de, BattleHudTiles2
+ ld hl, vChars2 + $780
+ lb bc, BANK(BattleHudTiles2), $01
+ call CopyVideoDataDouble ; │
+ ld de, BattleHudTiles3
+ ld hl, vChars2 + $760
+ lb bc, BANK(BattleHudTiles3), $02
+ call CopyVideoDataDouble ; ─┘
+ ld de, PTile
+ ld hl, vChars2 + $720
+ lb bc, BANK(PTile), (PTileEnd - PTile) / $8
+ call CopyVideoDataDouble ; P (for PP), inline
+ ld a, [hTilesetType]
+ push af
+ xor a
+ ld [hTilesetType], a
+ coord hl, 19, 1
+ lb bc, 6, 10
+ call DrawLineBox ; Draws the box around name, HP and status
+ ld de, -6
+ add hl, de
+ ld [hl], "⠄" ; . after No ("." is a different one)
+ dec hl
+ ld [hl], "№"
+ coord hl, 19, 9
+ lb bc, 8, 6
+ call DrawLineBox ; Draws the box around types, ID No. and OT
+ coord hl, 10, 9
+ ld de, Type1Text
+ call PlaceString ; "TYPE1/"
+ coord hl, 11, 3
+ predef DrawHP
+ ld hl, wStatusScreenHPBarColor
+ call GetHealthBarColor
+ ld b, SET_PAL_STATUS_SCREEN
+ call RunPaletteCommand
+ coord hl, 16, 6
+ ld de, wLoadedMonStatus
+ call PrintStatusCondition
+ jr nz, .StatusWritten
+ coord hl, 16, 6
+ ld de, OKText
+ call PlaceString ; "OK"
+.StatusWritten
+ coord hl, 9, 6
+ ld de, StatusText
+ call PlaceString ; "STATUS/"
+ coord hl, 14, 2
+ call PrintLevel ; Pokémon level
+ ld a, [wMonHIndex]
+ ld [wd11e], a
+ ld [wd0b5], a
+ predef IndexToPokedex
+ coord hl, 3, 7
+ ld de, wd11e
+ lb bc, LEADING_ZEROES | 1, 3
+ call PrintNumber ; Pokémon no.
+ coord hl, 11, 10
+ predef PrintMonType
+ ld hl, NamePointers2
+ call .GetStringPointer
+ ld d, h
+ ld e, l
+ coord hl, 9, 1
+ call PlaceString ; Pokémon name
+ ld hl, OTPointers
+ call .GetStringPointer
+ ld d, h
+ ld e, l
+ coord hl, 12, 16
+ call PlaceString ; OT
+ coord hl, 12, 14
+ ld de, wLoadedMonOTID
+ lb bc, LEADING_ZEROES | 2, 5
+ call PrintNumber ; ID Number
+ ld d, $0
+ call PrintStatsBox
+ call Delay3
+ call GBPalNormal
+ coord hl, 1, 0
+ call LoadFlippedFrontSpriteByMonIndex ; draw Pokémon picture
+ ld a, [wcf91]
+ call PlayCry ; play Pokémon cry
+ call WaitForTextScrollButtonPress ; wait for button
+ pop af
+ ld [hTilesetType], a
+ ret
+
+.GetStringPointer
+ ld a, [wMonDataLocation]
+ add a
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld a, [wMonDataLocation]
+ cp DAYCARE_DATA
+ ret z
+ ld a, [wWhichPokemon]
+ jp SkipFixedLengthTextEntries
+
+OTPointers:
+ dw wPartyMonOT
+ dw wEnemyMonOT
+ dw wBoxMonOT
+ dw wDayCareMonOT
+
+NamePointers2:
+ dw wPartyMonNicks
+ dw wEnemyMonNicks
+ dw wBoxMonNicks
+ dw wDayCareMonName
+
+Type1Text:
+ db "TYPE1/", $4e
+
+Type2Text:
+ db "TYPE2/", $4e
+
+IDNoText:
+ db $73, "№/", $4e
+
+OTText:
+ db "OT/"
+ next "@"
+
+StatusText:
+ db "STATUS/@"
+
+OKText:
+ db "OK@"
+
+; Draws a line starting from hl high b and wide c
+DrawLineBox:
+ ld de, SCREEN_WIDTH ; New line
+.PrintVerticalLine
+ ld [hl], $78 ; │
+ add hl, de
+ dec b
+ jr nz, .PrintVerticalLine
+ ld [hl], $77 ; ┘
+ dec hl
+.PrintHorizLine
+ ld [hl], $76 ; ─
+ dec hl
+ dec c
+ jr nz, .PrintHorizLine
+ ld [hl], $6f ; ← (halfarrow ending)
+ ret
+
+PTile: ; This is a single 1bpp "P" tile
+ INCBIN "gfx/p_tile.1bpp"
+PTileEnd:
+
+PrintStatsBox:
+ ld a, d
+ and a ; a is 0 from the status screen
+ jr nz, .DifferentBox
+ coord hl, 0, 8
+ ld b, 8
+ ld c, 8
+ call TextBoxBorder ; Draws the box
+ coord hl, 1, 9 ; Start printing stats from here
+ ld bc, $0019 ; Number offset
+ jr .PrintStats
+.DifferentBox
+ coord hl, 9, 2
+ ld b, 8
+ ld c, 9
+ call TextBoxBorder
+ coord hl, 11, 3
+ ld bc, $0018
+.PrintStats
+ push bc
+ push hl
+ ld de, StatsText
+ call PlaceString
+ pop hl
+ pop bc
+ add hl, bc
+ ld de, wLoadedMonAttack
+ lb bc, 2, 3
+ call PrintStat
+ ld de, wLoadedMonDefense
+ call PrintStat
+ ld de, wLoadedMonSpeed
+ call PrintStat
+ ld de, wLoadedMonSpecial
+ jp PrintNumber
+PrintStat:
+ push hl
+ call PrintNumber
+ pop hl
+ ld de, SCREEN_WIDTH * 2
+ add hl, de
+ ret
+
+StatsText:
+ db "ATTACK"
+ next "DEFENSE"
+ next "SPEED"
+ next "SPECIAL@"
+
+StatusScreen2:
+ ld a, [hTilesetType]
+ push af
+ xor a
+ ld [hTilesetType], a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ld bc, NUM_MOVES + 1
+ ld hl, wMoves
+ call FillMemory
+ ld hl, wLoadedMonMoves
+ ld de, wMoves
+ ld bc, NUM_MOVES
+ call CopyData
+ callab FormatMovesString
+ coord hl, 9, 2
+ lb bc, 5, 10
+ call ClearScreenArea ; Clear under name
+ coord hl, 19, 3
+ ld [hl], $78
+ coord hl, 0, 8
+ ld b, 8
+ ld c, 18
+ call TextBoxBorder ; Draw move container
+ coord hl, 2, 9
+ ld de, wMovesString
+ call PlaceString ; Print moves
+ ld a, [wNumMovesMinusOne]
+ inc a
+ ld c, a
+ ld a, $4
+ sub c
+ ld b, a ; Number of moves ?
+ coord hl, 11, 10
+ ld de, SCREEN_WIDTH * 2
+ ld a, $72 ; special P tile id
+ call StatusScreen_PrintPP ; Print "PP"
+ ld a, b
+ and a
+ jr z, .InitPP
+ ld c, a
+ ld a, "-"
+ call StatusScreen_PrintPP ; Fill the rest with --
+.InitPP
+ ld hl, wLoadedMonMoves
+ coord de, 14, 10
+ ld b, 0
+.PrintPP
+ ld a, [hli]
+ and a
+ jr z, .PPDone
+ push bc
+ push hl
+ push de
+ ld hl, wCurrentMenuItem
+ ld a, [hl]
+ push af
+ ld a, b
+ ld [hl], a
+ push hl
+ callab GetMaxPP
+ pop hl
+ pop af
+ ld [hl], a
+ pop de
+ pop hl
+ push hl
+ ld bc, wPartyMon1PP - wPartyMon1Moves - 1
+ add hl, bc
+ ld a, [hl]
+ and $3f
+ ld [wStatusScreenCurrentPP], a
+ ld h, d
+ ld l, e
+ push hl
+ ld de, wStatusScreenCurrentPP
+ lb bc, 1, 2
+ call PrintNumber
+ ld a, "/"
+ ld [hli], a
+ ld de, wMaxPP
+ lb bc, 1, 2
+ call PrintNumber
+ pop hl
+ ld de, SCREEN_WIDTH * 2
+ add hl, de
+ ld d, h
+ ld e, l
+ pop hl
+ pop bc
+ inc b
+ ld a, b
+ cp $4
+ jr nz, .PrintPP
+.PPDone
+ coord hl, 9, 3
+ ld de, StatusScreenExpText
+ call PlaceString
+ ld a, [wLoadedMonLevel]
+ push af
+ cp MAX_LEVEL
+ jr z, .Level100
+ inc a
+ ld [wLoadedMonLevel], a ; Increase temporarily if not 100
+.Level100
+ coord hl, 14, 6
+ ld [hl], $70 ; 1-tile "to"
+ inc hl
+ inc hl
+ call PrintLevel
+ pop af
+ ld [wLoadedMonLevel], a
+ ld de, wLoadedMonExp
+ coord hl, 12, 4
+ lb bc, 3, 7
+ call PrintNumber ; exp
+ call CalcExpToLevelUp
+ ld de, wLoadedMonExp
+ coord hl, 7, 6
+ lb bc, 3, 7
+ call PrintNumber ; exp needed to level up
+ coord hl, 9, 0
+ call StatusScreen_ClearName
+ coord hl, 9, 1
+ call StatusScreen_ClearName
+ ld a, [wMonHIndex]
+ ld [wd11e], a
+ call GetMonName
+ coord hl, 9, 1
+ call PlaceString
+ ld a, $1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call Delay3
+ call WaitForTextScrollButtonPress ; wait for button
+ pop af
+ ld [hTilesetType], a
+ ld hl, wd72c
+ res 1, [hl]
+ ld a, $77
+ ld [rNR50], a
+ call GBPalWhiteOut
+ jp ClearScreen
+
+CalcExpToLevelUp:
+ ld a, [wLoadedMonLevel]
+ cp MAX_LEVEL
+ jr z, .atMaxLevel
+ inc a
+ ld d, a
+ callab CalcExperience
+ ld hl, wLoadedMonExp + 2
+ ld a, [hExperience + 2]
+ sub [hl]
+ ld [hld], a
+ ld a, [hExperience + 1]
+ sbc [hl]
+ ld [hld], a
+ ld a, [hExperience]
+ sbc [hl]
+ ld [hld], a
+ ret
+.atMaxLevel
+ ld hl, wLoadedMonExp
+ xor a
+ ld [hli], a
+ ld [hli], a
+ ld [hl], a
+ ret
+
+StatusScreenExpText:
+ db "EXP POINTS"
+ next "LEVEL UP@"
+
+StatusScreen_ClearName:
+ ld bc, 10
+ ld a, " "
+ jp FillMemory
+
+StatusScreen_PrintPP:
+; print PP or -- c times, going down two rows each time
+ ld [hli], a
+ ld [hld], a
+ add hl, de
+ dec c
+ jr nz, StatusScreen_PrintPP
+ ret
diff --git a/en/engine/menu/text_box.asm b/en/engine/menu/text_box.asm
new file mode 100644
index 00000000..b9a36652
--- /dev/null
+++ b/en/engine/menu/text_box.asm
@@ -0,0 +1,767 @@
+; function to draw various text boxes
+DisplayTextBoxID_:
+ ld a, [wTextBoxID]
+ cp TWO_OPTION_MENU
+ jp z, DisplayTwoOptionMenu
+ ld c, a
+ ld hl, TextBoxFunctionTable
+ ld de, 3
+ call SearchTextBoxTable
+ jr c, .functionTableMatch
+ ld hl, TextBoxCoordTable
+ ld de, 5
+ call SearchTextBoxTable
+ jr c, .coordTableMatch
+ ld hl, TextBoxTextAndCoordTable
+ ld de, 9
+ call SearchTextBoxTable
+ jr c, .textAndCoordTableMatch
+.done
+ ret
+.functionTableMatch
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a ; hl = address of function
+ ld de, .done
+ push de
+ jp hl ; jump to the function
+.coordTableMatch
+ call GetTextBoxIDCoords
+ call GetAddressOfScreenCoords
+ call TextBoxBorder
+ ret
+.textAndCoordTableMatch
+ call GetTextBoxIDCoords
+ push hl
+ call GetAddressOfScreenCoords
+ call TextBoxBorder
+ pop hl
+ call GetTextBoxIDText
+ ld a, [wd730]
+ push af
+ ld a, [wd730]
+ set 6, a ; no pauses between printing each letter
+ ld [wd730], a
+ call PlaceString
+ pop af
+ ld [wd730], a
+ call UpdateSprites
+ ret
+
+; function to search a table terminated with $ff for a byte matching c in increments of de
+; sets carry flag if a match is found and clears carry flag if not
+SearchTextBoxTable:
+ dec de
+.loop
+ ld a, [hli]
+ cp $ff
+ jr z, .notFound
+ cp c
+ jr z, .found
+ add hl, de
+ jr .loop
+.found
+ scf
+.notFound
+ ret
+
+; function to load coordinates from the TextBoxCoordTable or the TextBoxTextAndCoordTable
+; INPUT:
+; hl = address of coordinates
+; OUTPUT:
+; b = height
+; c = width
+; d = row of upper left corner
+; e = column of upper left corner
+GetTextBoxIDCoords:
+ ld a, [hli] ; column of upper left corner
+ ld e, a
+ ld a, [hli] ; row of upper left corner
+ ld d, a
+ ld a, [hli] ; column of lower right corner
+ sub e
+ dec a
+ ld c, a ; c = width
+ ld a, [hli] ; row of lower right corner
+ sub d
+ dec a
+ ld b, a ; b = height
+ ret
+
+; function to load a text address and text coordinates from the TextBoxTextAndCoordTable
+GetTextBoxIDText:
+ ld a, [hli]
+ ld e, a
+ ld a, [hli]
+ ld d, a ; de = address of text
+ push de ; save text address
+ ld a, [hli]
+ ld e, a ; column of upper left corner of text
+ ld a, [hl]
+ ld d, a ; row of upper left corner of text
+ call GetAddressOfScreenCoords
+ pop de ; restore text address
+ ret
+
+; function to point hl to the screen coordinates
+; INPUT:
+; d = row
+; e = column
+; OUTPUT:
+; hl = address of upper left corner of text box
+GetAddressOfScreenCoords:
+ push bc
+ coord hl, 0, 0
+ ld bc, 20
+.loop ; loop to add d rows to the base address
+ ld a, d
+ and a
+ jr z, .addedRows
+ add hl, bc
+ dec d
+ jr .loop
+.addedRows
+ pop bc
+ add hl, de
+ ret
+
+; Format:
+; 00: text box ID
+; 01-02: function address
+TextBoxFunctionTable:
+ dbw MONEY_BOX, DisplayMoneyBox
+ dbw BUY_SELL_QUIT_MENU, DoBuySellQuitMenu
+ dbw FIELD_MOVE_MON_MENU, DisplayFieldMoveMonMenu
+ db $ff ; terminator
+
+; Format:
+; 00: text box ID
+; 01: column of upper left corner
+; 02: row of upper left corner
+; 03: column of lower right corner
+; 04: row of lower right corner
+TextBoxCoordTable:
+ db MESSAGE_BOX, 0, 12, 19, 17
+ db $03, 0, 0, 19, 14
+ db $07, 0, 0, 11, 6
+ db LIST_MENU_BOX, 4, 2, 19, 12
+ db $10, 7, 0, 19, 17
+ db MON_SPRITE_POPUP, 6, 4, 14, 13
+ db $ff ; terminator
+
+; Format:
+; 00: text box ID
+; 01: column of upper left corner
+; 02: row of upper left corner
+; 03: column of lower right corner
+; 04: row of lower right corner
+; 05-06: address of text
+; 07: column of beginning of text
+; 08: row of beginning of text
+; table of window positions and corresponding text [key, start column, start row, end column, end row, text pointer [2 bytes], text column, text row]
+TextBoxTextAndCoordTable:
+ db JP_MOCHIMONO_MENU_TEMPLATE
+ db 0,0,14,17 ; text box coordinates
+ dw JapaneseMochimonoText
+ db 3,0 ; text coordinates
+
+ db USE_TOSS_MENU_TEMPLATE
+ db 13,10,19,14 ; text box coordinates
+ dw UseTossText
+ db 15,11 ; text coordinates
+
+ db JP_SAVE_MESSAGE_MENU_TEMPLATE
+ db 0,0,7,5 ; text box coordinates
+ dw JapaneseSaveMessageText
+ db 2,2 ; text coordinates
+
+ db JP_SPEED_OPTIONS_MENU_TEMPLATE
+ db 0,6,5,10 ; text box coordinates
+ dw JapaneseSpeedOptionsText
+ db 2,7 ; text coordinates
+
+ db BATTLE_MENU_TEMPLATE
+ db 8,12,19,17 ; text box coordinates
+ dw BattleMenuText
+ db 10,14 ; text coordinates
+
+ db SAFARI_BATTLE_MENU_TEMPLATE
+ db 0,12,19,17 ; text box coordinates
+ dw SafariZoneBattleMenuText
+ db 2,14 ; text coordinates
+
+ db SWITCH_STATS_CANCEL_MENU_TEMPLATE
+ db 11,11,19,17 ; text box coordinates
+ dw SwitchStatsCancelText
+ db 13,12 ; text coordinates
+
+ db BUY_SELL_QUIT_MENU_TEMPLATE
+ db 0,0,10,6 ; text box coordinates
+ dw BuySellQuitText
+ db 2,1 ; text coordinates
+
+ db MONEY_BOX_TEMPLATE
+ db 11,0,19,2 ; text box coordinates
+ dw MoneyText
+ db 13,0 ; text coordinates
+
+ db JP_AH_MENU_TEMPLATE
+ db 7,6,11,10 ; text box coordinates
+ dw JapaneseAhText
+ db 8,8 ; text coordinates
+
+ db JP_POKEDEX_MENU_TEMPLATE
+ db 11,8,19,17 ; text box coordinates
+ dw JapanesePokedexMenu
+ db 12,10 ; text coordinates
+
+; note that there is no terminator
+
+BuySellQuitText:
+ db "BUY"
+ next "SELL"
+ next "QUIT@@"
+
+UseTossText:
+ db "USE"
+ next "TOSS@"
+
+JapaneseSaveMessageText:
+ db "きろく"
+ next "メッセージ@"
+
+JapaneseSpeedOptionsText:
+ db "はやい"
+ next "おそい@"
+
+MoneyText:
+ db "MONEY@"
+
+JapaneseMochimonoText:
+ db "もちもの@"
+
+JapaneseMainMenuText:
+ db "つづきから"
+ next "さいしょから@"
+
+BattleMenuText:
+ db "FIGHT ",$E1,$E2
+ next "ITEM RUN@"
+
+SafariZoneBattleMenuText:
+ db "BALL× BAIT"
+ next "THROW ROCK RUN@"
+
+SwitchStatsCancelText:
+ db "SWITCH"
+ next "STATS"
+ next "CANCEL@"
+
+JapaneseAhText:
+ db "アッ!@"
+
+JapanesePokedexMenu:
+ db "データをみる"
+ next "なきごえ"
+ next "ぶんぷをみる"
+ next "キャンセル@"
+
+DisplayMoneyBox:
+ ld hl, wd730
+ set 6, [hl]
+ ld a, MONEY_BOX_TEMPLATE
+ ld [wTextBoxID], a
+ call DisplayTextBoxID
+ coord hl, 13, 1
+ ld b, 1
+ ld c, 6
+ call ClearScreenArea
+ coord hl, 12, 1
+ ld de, wPlayerMoney
+ ld c, $a3
+ call PrintBCDNumber
+ ld hl, wd730
+ res 6, [hl]
+ ret
+
+CurrencyString:
+ db " ¥@"
+
+DoBuySellQuitMenu:
+ ld a, [wd730]
+ set 6, a ; no printing delay
+ ld [wd730], a
+ xor a
+ ld [wChosenMenuItem], a
+ ld a, BUY_SELL_QUIT_MENU_TEMPLATE
+ ld [wTextBoxID], a
+ call DisplayTextBoxID
+ ld a, A_BUTTON | B_BUTTON
+ ld [wMenuWatchedKeys], a
+ ld a, $2
+ ld [wMaxMenuItem], a
+ ld a, $1
+ ld [wTopMenuItemY], a
+ ld a, $1
+ ld [wTopMenuItemX], a
+ xor a
+ ld [wCurrentMenuItem], a
+ ld [wLastMenuItem], a
+ ld [wMenuWatchMovingOutOfBounds], a
+ ld a, [wd730]
+ res 6, a ; turn on the printing delay
+ ld [wd730], a
+ call HandleMenuInput
+ call PlaceUnfilledArrowMenuCursor
+ bit 0, a ; was A pressed?
+ jr nz, .pressedA
+ bit 1, a ; was B pressed? (always true since only A/B are watched)
+ jr z, .pressedA
+ ld a, CANCELLED_MENU
+ ld [wMenuExitMethod], a
+ jr .quit
+.pressedA
+ ld a, CHOSE_MENU_ITEM
+ ld [wMenuExitMethod], a
+ ld a, [wCurrentMenuItem]
+ ld [wChosenMenuItem], a
+ ld b, a
+ ld a, [wMaxMenuItem]
+ cp b
+ jr z, .quit
+ ret
+.quit
+ ld a, CANCELLED_MENU
+ ld [wMenuExitMethod], a
+ ld a, [wCurrentMenuItem]
+ ld [wChosenMenuItem], a
+ scf
+ ret
+
+; displays a menu with two options to choose from
+; b = Y of upper left corner of text region
+; c = X of upper left corner of text region
+; hl = address where the text box border should be drawn
+DisplayTwoOptionMenu:
+ push hl
+ ld a, [wd730]
+ set 6, a ; no printing delay
+ ld [wd730], a
+
+; pointless because both values are overwritten before they are read
+ xor a
+ ld [wChosenMenuItem], a
+ ld [wMenuExitMethod], a
+
+ ld a, A_BUTTON | B_BUTTON
+ ld [wMenuWatchedKeys], a
+ ld a, $1
+ ld [wMaxMenuItem], a
+ ld a, b
+ ld [wTopMenuItemY], a
+ ld a, c
+ ld [wTopMenuItemX], a
+ xor a
+ ld [wLastMenuItem], a
+ ld [wMenuWatchMovingOutOfBounds], a
+ push hl
+ ld hl, wTwoOptionMenuID
+ bit 7, [hl] ; select second menu item by default?
+ res 7, [hl]
+ jr z, .storeCurrentMenuItem
+ inc a
+.storeCurrentMenuItem
+ ld [wCurrentMenuItem], a
+ pop hl
+ push hl
+ push hl
+ call TwoOptionMenu_SaveScreenTiles
+ ld a, [wTwoOptionMenuID]
+ ld hl, TwoOptionMenuStrings
+ ld e, a
+ ld d, $0
+ ld a, $5
+.menuStringLoop
+ add hl, de
+ dec a
+ jr nz, .menuStringLoop
+ ld a, [hli]
+ ld c, a
+ ld a, [hli]
+ ld b, a
+ ld e, l
+ ld d, h
+ pop hl
+ push de
+ ld a, [wTwoOptionMenuID]
+ cp TRADE_CANCEL_MENU
+ jr nz, .notTradeCancelMenu
+ call CableClub_TextBoxBorder
+ jr .afterTextBoxBorder
+.notTradeCancelMenu
+ call TextBoxBorder
+.afterTextBoxBorder
+ call UpdateSprites
+ pop hl
+ ld a, [hli]
+ and a ; put blank line before first menu item?
+ ld bc, 20 + 2
+ jr z, .noBlankLine
+ ld bc, 2 * 20 + 2
+.noBlankLine
+ ld a, [hli]
+ ld e, a
+ ld a, [hli]
+ ld d, a
+ pop hl
+ add hl, bc
+ call PlaceString
+ ld hl, wd730
+ res 6, [hl] ; turn on the printing delay
+ ld a, [wTwoOptionMenuID]
+ cp NO_YES_MENU
+ jr nz, .notNoYesMenu
+; No/Yes menu
+; this menu type ignores the B button
+; it only seems to be used when confirming the deletion of a save file
+ xor a
+ ld [wTwoOptionMenuID], a
+ ld a, [wFlags_0xcd60]
+ push af
+ push hl
+ ld hl, wFlags_0xcd60
+ bit 5, [hl]
+ set 5, [hl] ; don't play sound when A or B is pressed in menu
+ pop hl
+.noYesMenuInputLoop
+ call HandleMenuInput
+ bit 1, a ; A button pressed?
+ jr nz, .noYesMenuInputLoop ; try again if A was not pressed
+ pop af
+ pop hl
+ ld [wFlags_0xcd60], a
+ ld a, SFX_PRESS_AB
+ call PlaySound
+ jr .pressedAButton
+.notNoYesMenu
+ xor a
+ ld [wTwoOptionMenuID], a
+ call HandleMenuInput
+ pop hl
+ bit 1, a ; A button pressed?
+ jr nz, .choseSecondMenuItem ; automatically choose the second option if B is pressed
+.pressedAButton
+ ld a, [wCurrentMenuItem]
+ ld [wChosenMenuItem], a
+ and a
+ jr nz, .choseSecondMenuItem
+; chose first menu item
+ ld a, CHOSE_FIRST_ITEM
+ ld [wMenuExitMethod], a
+ ld c, 15
+ call DelayFrames
+ call TwoOptionMenu_RestoreScreenTiles
+ and a
+ ret
+.choseSecondMenuItem
+ ld a, 1
+ ld [wCurrentMenuItem], a
+ ld [wChosenMenuItem], a
+ ld a, CHOSE_SECOND_ITEM
+ ld [wMenuExitMethod], a
+ ld c, 15
+ call DelayFrames
+ call TwoOptionMenu_RestoreScreenTiles
+ scf
+ ret
+
+; Some of the wider/taller two option menus will not have the screen areas
+; they cover be fully saved/restored by the two functions below.
+; The bottom and right edges of the menu may remain after the function returns.
+
+TwoOptionMenu_SaveScreenTiles:
+ ld de, wBuffer
+ lb bc, 5, 6
+.loop
+ ld a, [hli]
+ ld [de], a
+ inc de
+ dec c
+ jr nz, .loop
+ push bc
+ ld bc, SCREEN_WIDTH - 6
+ add hl, bc
+ pop bc
+ ld c, $6
+ dec b
+ jr nz, .loop
+ ret
+
+TwoOptionMenu_RestoreScreenTiles:
+ ld de, wBuffer
+ lb bc, 5, 6
+.loop
+ ld a, [de]
+ inc de
+ ld [hli], a
+ dec c
+ jr nz, .loop
+ push bc
+ ld bc, SCREEN_WIDTH - 6
+ add hl, bc
+ pop bc
+ ld c, 6
+ dec b
+ jr nz, .loop
+ call UpdateSprites
+ ret
+
+; Format:
+; 00: byte width
+; 01: byte height
+; 02: byte put blank line before first menu item
+; 03: word text pointer
+TwoOptionMenuStrings:
+ db 4,3,0
+ dw .YesNoMenu
+ db 6,3,0
+ dw .NorthWestMenu
+ db 6,3,0
+ dw .SouthEastMenu
+ db 6,3,0
+ dw .YesNoMenu
+ db 6,3,0
+ dw .NorthEastMenu
+ db 7,3,0
+ dw .TradeCancelMenu
+ db 7,4,1
+ dw .HealCancelMenu
+ db 4,3,0
+ dw .NoYesMenu
+
+.NoYesMenu
+ db "NO"
+ next "YES@"
+.YesNoMenu
+ db "YES"
+ next "NO@"
+.NorthWestMenu
+ db "NORTH"
+ next "WEST@"
+.SouthEastMenu
+ db "SOUTH"
+ next "EAST@"
+.NorthEastMenu
+ db "NORTH"
+ next "EAST@"
+.TradeCancelMenu
+ db "TRADE"
+ next "CANCEL@"
+.HealCancelMenu
+ db "HEAL"
+ next "CANCEL@"
+
+DisplayFieldMoveMonMenu:
+ xor a
+ ld hl, wFieldMoves
+ ld [hli], a ; wFieldMoves
+ ld [hli], a ; wFieldMoves + 1
+ ld [hli], a ; wFieldMoves + 2
+ ld [hli], a ; wFieldMoves + 3
+ ld [hli], a ; wNumFieldMoves
+ ld [hl], 12 ; wFieldMovesLeftmostXCoord
+ call GetMonFieldMoves
+ ld a, [wNumFieldMoves]
+ and a
+ jr nz, .fieldMovesExist
+
+; no field moves
+ coord hl, 11, 11
+ ld b, 5
+ ld c, 7
+ call TextBoxBorder
+ call UpdateSprites
+ ld a, 12
+ ld [hFieldMoveMonMenuTopMenuItemX], a
+ coord hl, 13, 12
+ ld de, PokemonMenuEntries
+ jp PlaceString
+
+.fieldMovesExist
+ push af
+
+; Calculate the text box position and dimensions based on the leftmost X coord
+; of the field move names before adjusting for the number of field moves.
+ coord hl, 0, 11
+ ld a, [wFieldMovesLeftmostXCoord]
+ dec a
+ ld e, a
+ ld d, 0
+ add hl, de
+ ld b, 5
+ ld a, 18
+ sub e
+ ld c, a
+ pop af
+
+; For each field move, move the top of the text box up 2 rows while the leaving
+; the bottom of the text box at the bottom of the screen.
+ ld de, -SCREEN_WIDTH * 2
+.textBoxHeightLoop
+ add hl, de
+ inc b
+ inc b
+ dec a
+ jr nz, .textBoxHeightLoop
+
+; Make space for an extra blank row above the top field move.
+ ld de, -SCREEN_WIDTH
+ add hl, de
+ inc b
+
+ call TextBoxBorder
+ call UpdateSprites
+
+; Calculate the position of the first field move name to print.
+ coord hl, 0, 12
+ ld a, [wFieldMovesLeftmostXCoord]
+ inc a
+ ld e, a
+ ld d, 0
+ add hl, de
+ ld de, -SCREEN_WIDTH * 2
+ ld a, [wNumFieldMoves]
+.calcFirstFieldMoveYLoop
+ add hl, de
+ dec a
+ jr nz, .calcFirstFieldMoveYLoop
+
+ xor a
+ ld [wNumFieldMoves], a
+ ld de, wFieldMoves
+.printNamesLoop
+ push hl
+ ld hl, FieldMoveNames
+ ld a, [de]
+ and a
+ jr z, .donePrintingNames
+ inc de
+ ld b, a ; index of name
+.skipNamesLoop ; skip past names before the name we want
+ dec b
+ jr z, .reachedName
+.skipNameLoop ; skip past current name
+ ld a, [hli]
+ cp "@"
+ jr nz, .skipNameLoop
+ jr .skipNamesLoop
+.reachedName
+ ld b, h
+ ld c, l
+ pop hl
+ push de
+ ld d, b
+ ld e, c
+ call PlaceString
+ ld bc, SCREEN_WIDTH * 2
+ add hl, bc
+ pop de
+ jr .printNamesLoop
+
+.donePrintingNames
+ pop hl
+ ld a, [wFieldMovesLeftmostXCoord]
+ ld [hFieldMoveMonMenuTopMenuItemX], a
+ coord hl, 0, 12
+ ld a, [wFieldMovesLeftmostXCoord]
+ inc a
+ ld e, a
+ ld d, 0
+ add hl, de
+ ld de, PokemonMenuEntries
+ jp PlaceString
+
+FieldMoveNames:
+ db "CUT@"
+ db "FLY@"
+ db "@"
+ db "SURF@"
+ db "STRENGTH@"
+ db "FLASH@"
+ db "DIG@"
+ db "TELEPORT@"
+ db "SOFTBOILED@"
+
+PokemonMenuEntries:
+ db "STATS"
+ next "SWITCH"
+ next "CANCEL@"
+
+GetMonFieldMoves:
+ ld a, [wWhichPokemon]
+ ld hl, wPartyMon1Moves
+ ld bc, wPartyMon2 - wPartyMon1
+ call AddNTimes
+ ld d, h
+ ld e, l
+ ld c, NUM_MOVES + 1
+ ld hl, wFieldMoves
+.loop
+ push hl
+.nextMove
+ dec c
+ jr z, .done
+ ld a, [de] ; move ID
+ and a
+ jr z, .done
+ ld b, a
+ inc de
+ ld hl, FieldMoveDisplayData
+.fieldMoveLoop
+ ld a, [hli]
+ cp $ff
+ jr z, .nextMove ; if the move is not a field move
+ cp b
+ jr z, .foundFieldMove
+ inc hl
+ inc hl
+ jr .fieldMoveLoop
+.foundFieldMove
+ ld a, b
+ ld [wLastFieldMoveID], a
+ ld a, [hli] ; field move name index
+ ld b, [hl] ; field move leftmost X coordinate
+ pop hl
+ ld [hli], a ; store name index in wFieldMoves
+ ld a, [wNumFieldMoves]
+ inc a
+ ld [wNumFieldMoves], a
+ ld a, [wFieldMovesLeftmostXCoord]
+ cp b
+ jr c, .skipUpdatingLeftmostXCoord
+ ld a, b
+ ld [wFieldMovesLeftmostXCoord], a
+.skipUpdatingLeftmostXCoord
+ ld a, [wLastFieldMoveID]
+ ld b, a
+ jr .loop
+.done
+ pop hl
+ ret
+
+; Format: [Move id], [name index], [leftmost tile]
+; Move id = id of move
+; Name index = index of name in FieldMoveNames
+; Leftmost tile = -1 + tile column in which the first letter of the move's name should be displayed
+; "SOFTBOILED" is $08 because it has 4 more letters than "SURF", for example, whose value is $0C
+FieldMoveDisplayData:
+ db CUT, $01, $0C
+ db FLY, $02, $0C
+ db $B4, $03, $0C ; unused field move
+ db SURF, $04, $0C
+ db STRENGTH, $05, $0A
+ db FLASH, $06, $0C
+ db DIG, $07, $0C
+ db TELEPORT, $08, $0A
+ db SOFTBOILED, $09, $08
+ db $ff ; list terminator
diff --git a/en/engine/menu/vending_machine.asm b/en/engine/menu/vending_machine.asm
new file mode 100755
index 00000000..aab4adf4
--- /dev/null
+++ b/en/engine/menu/vending_machine.asm
@@ -0,0 +1,139 @@
+VendingMachineMenu:
+ ld hl, VendingMachineText1
+ call PrintText
+ ld a, MONEY_BOX
+ ld [wTextBoxID], a
+ call DisplayTextBoxID
+ xor a
+ ld [wCurrentMenuItem], a
+ ld [wLastMenuItem], a
+ ld a, A_BUTTON | B_BUTTON
+ ld [wMenuWatchedKeys], a
+ ld a, 3
+ ld [wMaxMenuItem], a
+ ld a, 5
+ ld [wTopMenuItemY], a
+ ld a, 1
+ ld [wTopMenuItemX], a
+ ld hl, wd730
+ set 6, [hl]
+ coord hl, 0, 3
+ ld b, 8
+ ld c, 12
+ call TextBoxBorder
+ call UpdateSprites
+ coord hl, 2, 5
+ ld de, DrinkText
+ call PlaceString
+ coord hl, 9, 6
+ ld de, DrinkPriceText
+ call PlaceString
+ ld hl, wd730
+ res 6, [hl]
+ call HandleMenuInput
+ bit 1, a ; pressed B?
+ jr nz, .notThirsty
+ ld a, [wCurrentMenuItem]
+ cp 3 ; chose Cancel?
+ jr z, .notThirsty
+ xor a
+ ld [hMoney], a
+ ld [hMoney + 2], a
+ ld a, $2
+ ld [hMoney + 1], a
+ call HasEnoughMoney
+ jr nc, .enoughMoney
+ ld hl, VendingMachineText4
+ jp PrintText
+.enoughMoney
+ call LoadVendingMachineItem
+ ld a, [hVendingMachineItem]
+ ld b, a
+ ld c, 1
+ call GiveItem
+ jr nc, .BagFull
+
+ ld b, 60 ; number of times to play the "brrrrr" sound
+.playDeliverySound
+ ld c, 2
+ call DelayFrames
+ push bc
+ ld a, SFX_PUSH_BOULDER
+ call PlaySound
+ pop bc
+ dec b
+ jr nz, .playDeliverySound
+
+ ld hl, VendingMachineText5
+ call PrintText
+ ld hl, hVendingMachinePrice + 2
+ ld de, wPlayerMoney + 2
+ ld c, $3
+ predef SubBCDPredef
+ ld a, MONEY_BOX
+ ld [wTextBoxID], a
+ jp DisplayTextBoxID
+.BagFull
+ ld hl, VendingMachineText6
+ jp PrintText
+.notThirsty
+ ld hl, VendingMachineText7
+ jp PrintText
+
+VendingMachineText1:
+ TX_FAR _VendingMachineText1
+ db "@"
+
+DrinkText:
+ db "FRESH WATER"
+ next "SODA POP"
+ next "LEMONADE"
+ next "CANCEL@"
+
+DrinkPriceText:
+ db "¥200"
+ next "¥300"
+ next "¥350"
+ next "@"
+
+VendingMachineText4:
+ TX_FAR _VendingMachineText4
+ db "@"
+
+VendingMachineText5:
+ TX_FAR _VendingMachineText5
+ db "@"
+
+VendingMachineText6:
+ TX_FAR _VendingMachineText6
+ db "@"
+
+VendingMachineText7:
+ TX_FAR _VendingMachineText7
+ db "@"
+
+LoadVendingMachineItem:
+ ld hl, VendingPrices
+ ld a, [wCurrentMenuItem]
+ add a
+ add a
+ ld d, 0
+ ld e, a
+ add hl, de
+ ld a, [hli]
+ ld [hVendingMachineItem], a
+ ld a, [hli]
+ ld [hVendingMachinePrice], a
+ ld a, [hli]
+ ld [hVendingMachinePrice + 1], a
+ ld a, [hl]
+ ld [hVendingMachinePrice + 2], a
+ ret
+
+VendingPrices:
+ db FRESH_WATER
+ money 200
+ db SODA_POP
+ money 300
+ db LEMONADE
+ money 350
diff --git a/en/engine/oak_speech2.asm b/en/engine/oak_speech2.asm
new file mode 100755
index 00000000..1258c59b
--- /dev/null
+++ b/en/engine/oak_speech2.asm
@@ -0,0 +1,272 @@
+ChoosePlayerName:
+ call OakSpeechSlidePicRight
+ ld de, DefaultNamesPlayer
+ call DisplayIntroNameTextBox
+ ld a, [wCurrentMenuItem]
+ and a
+ jr z, .customName
+ ld hl, DefaultNamesPlayerList
+ call GetDefaultName
+ ld de, wPlayerName
+ call OakSpeechSlidePicLeft
+ jr .done
+.customName
+ ld hl, wPlayerName
+ xor a ; NAME_PLAYER_SCREEN
+ ld [wNamingScreenType], a
+ call DisplayNamingScreen
+ ld a, [wcf4b]
+ cp "@"
+ jr z, .customName
+ call ClearScreen
+ call Delay3
+ ld de, RedPicFront
+ ld b, BANK(RedPicFront)
+ call IntroDisplayPicCenteredOrUpperRight
+.done
+ ld hl, YourNameIsText
+ jp PrintText
+
+YourNameIsText:
+ TX_FAR _YourNameIsText
+ db "@"
+
+ChooseRivalName:
+ call OakSpeechSlidePicRight
+ ld de, DefaultNamesRival
+ call DisplayIntroNameTextBox
+ ld a, [wCurrentMenuItem]
+ and a
+ jr z, .customName
+ ld hl, DefaultNamesRivalList
+ call GetDefaultName
+ ld de, wRivalName
+ call OakSpeechSlidePicLeft
+ jr .done
+.customName
+ ld hl, wRivalName
+ ld a, NAME_RIVAL_SCREEN
+ ld [wNamingScreenType], a
+ call DisplayNamingScreen
+ ld a, [wcf4b]
+ cp "@"
+ jr z, .customName
+ call ClearScreen
+ call Delay3
+ ld de, Rival1Pic
+ ld b, $13
+ call IntroDisplayPicCenteredOrUpperRight
+.done
+ ld hl, HisNameIsText
+ jp PrintText
+
+HisNameIsText:
+ TX_FAR _HisNameIsText
+ db "@"
+
+OakSpeechSlidePicLeft:
+ push de
+ coord hl, 0, 0
+ lb bc, 12, 11
+ call ClearScreenArea ; clear the name list text box
+ ld c, 10
+ call DelayFrames
+ pop de
+ ld hl, wcd6d
+ ld bc, NAME_LENGTH
+ call CopyData
+ call Delay3
+ coord hl, 12, 4
+ lb de, 6, 6 * SCREEN_WIDTH + 5
+ ld a, $ff
+ jr OakSpeechSlidePicCommon
+
+OakSpeechSlidePicRight:
+ coord hl, 5, 4
+ lb de, 6, 6 * SCREEN_WIDTH + 5
+ xor a
+
+OakSpeechSlidePicCommon:
+ push hl
+ push de
+ push bc
+ ld [hSlideDirection], a
+ ld a, d
+ ld [hSlideAmount], a
+ ld a, e
+ ld [hSlidingRegionSize], a
+ ld c, a
+ ld a, [hSlideDirection]
+ and a
+ jr nz, .next
+; If sliding right, point hl to the end of the pic's tiles.
+ ld d, 0
+ add hl, de
+.next
+ ld d, h
+ ld e, l
+.loop
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ld a, [hSlideDirection]
+ and a
+ jr nz, .slideLeft
+; sliding right
+ ld a, [hli]
+ ld [hld], a
+ dec hl
+ jr .next2
+.slideLeft
+ ld a, [hld]
+ ld [hli], a
+ inc hl
+.next2
+ dec c
+ jr nz, .loop
+ ld a, [hSlideDirection]
+ and a
+ jr z, .next3
+; If sliding left, we need to zero the last tile in the pic (there is no need
+; to take a corresponding action when sliding right because hl initially points
+; to a 0 tile in that case).
+ xor a
+ dec hl
+ ld [hl], a
+.next3
+ ld a, 1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call Delay3
+ ld a, [hSlidingRegionSize]
+ ld c, a
+ ld h, d
+ ld l, e
+ ld a, [hSlideDirection]
+ and a
+ jr nz, .slideLeft2
+ inc hl
+ jr .next4
+.slideLeft2
+ dec hl
+.next4
+ ld d, h
+ ld e, l
+ ld a, [hSlideAmount]
+ dec a
+ ld [hSlideAmount], a
+ jr nz, .loop
+ pop bc
+ pop de
+ pop hl
+ ret
+
+DisplayIntroNameTextBox:
+ push de
+ coord hl, 0, 0
+ ld b, $a
+ ld c, $9
+ call TextBoxBorder
+ coord hl, 3, 0
+ ld de, .namestring
+ call PlaceString
+ pop de
+ coord hl, 2, 2
+ call PlaceString
+ call UpdateSprites
+ xor a
+ ld [wCurrentMenuItem], a
+ ld [wLastMenuItem], a
+ inc a
+ ld [wTopMenuItemX], a
+ ld [wMenuWatchedKeys], a ; A_BUTTON
+ inc a
+ ld [wTopMenuItemY], a
+ inc a
+ ld [wMaxMenuItem], a
+ jp HandleMenuInput
+
+.namestring
+ db "NAME@"
+
+IF DEF(_RED)
+DefaultNamesPlayer:
+ db "NEW NAME"
+ next "RED"
+ next "ASH"
+ next "JACK"
+ db "@"
+
+DefaultNamesRival:
+ db "NEW NAME"
+ next "BLUE"
+ next "GARY"
+ next "JOHN"
+ db "@"
+ENDC
+
+IF DEF(_BLUE)
+DefaultNamesPlayer:
+ db "NEW NAME"
+ next "BLUE"
+ next "GARY"
+ next "JOHN"
+ db "@"
+
+DefaultNamesRival:
+ db "NEW NAME"
+ next "RED"
+ next "ASH"
+ next "JACK"
+ db "@"
+ENDC
+
+GetDefaultName:
+; a = name index
+; hl = name list
+ ld b, a
+ ld c, 0
+.loop
+ ld d, h
+ ld e, l
+.innerLoop
+ ld a, [hli]
+ cp "@"
+ jr nz, .innerLoop
+ ld a, b
+ cp c
+ jr z, .foundName
+ inc c
+ jr .loop
+.foundName
+ ld h, d
+ ld l, e
+ ld de, wcd6d
+ ld bc, $14
+ jp CopyData
+
+IF DEF(_RED)
+DefaultNamesPlayerList:
+ db "NEW NAME@"
+ db "RED@"
+ db "ASH@"
+ db "JACK@"
+DefaultNamesRivalList:
+ db "NEW NAME@"
+ db "BLUE@"
+ db "GARY@"
+ db "JOHN@"
+ENDC
+IF DEF(_BLUE)
+DefaultNamesPlayerList:
+ db "NEW NAME@"
+ db "BLUE@"
+ db "GARY@"
+ db "JOHN@"
+DefaultNamesRivalList:
+ db "NEW NAME@"
+ db "RED@"
+ db "ASH@"
+ db "JACK@"
+ENDC
+
+TextTerminator_6b20:
+ db "@"
diff --git a/en/engine/overworld/movement.asm b/en/engine/overworld/movement.asm
new file mode 100644
index 00000000..bbfbbc39
--- /dev/null
+++ b/en/engine/overworld/movement.asm
@@ -0,0 +1,880 @@
+UpdatePlayerSprite:
+ ld a, [wSpriteStateData2]
+ and a
+ jr z, .checkIfTextBoxInFrontOfSprite
+ cp $ff
+ jr z, .disableSprite
+ dec a
+ ld [wSpriteStateData2], a
+ jr .disableSprite
+; check if a text box is in front of the sprite by checking if the lower left
+; background tile the sprite is standing on is greater than $5F, which is
+; the maximum number for map tiles
+.checkIfTextBoxInFrontOfSprite
+ aCoord 8, 9
+ ld [hTilePlayerStandingOn], a
+ cp $60
+ jr c, .lowerLeftTileIsMapTile
+.disableSprite
+ ld a, $ff
+ ld [wSpriteStateData1 + 2], a
+ ret
+.lowerLeftTileIsMapTile
+ call DetectCollisionBetweenSprites
+ ld h, wSpriteStateData1 / $100
+ ld a, [wWalkCounter]
+ and a
+ jr nz, .moving
+ ld a, [wPlayerMovingDirection]
+; check if down
+ bit PLAYER_DIR_BIT_DOWN, a
+ jr z, .checkIfUp
+ xor a ; ld a, SPRITE_FACING_DOWN
+ jr .next
+.checkIfUp
+ bit PLAYER_DIR_BIT_UP, a
+ jr z, .checkIfLeft
+ ld a, SPRITE_FACING_UP
+ jr .next
+.checkIfLeft
+ bit PLAYER_DIR_BIT_LEFT, a
+ jr z, .checkIfRight
+ ld a, SPRITE_FACING_LEFT
+ jr .next
+.checkIfRight
+ bit PLAYER_DIR_BIT_RIGHT, a
+ jr z, .notMoving
+ ld a, SPRITE_FACING_RIGHT
+ jr .next
+.notMoving
+; zero the animation counters
+ xor a
+ ld [wSpriteStateData1 + 7], a
+ ld [wSpriteStateData1 + 8], a
+ jr .calcImageIndex
+.next
+ ld [wSpriteStateData1 + 9], a ; facing direction
+ ld a, [wFontLoaded]
+ bit 0, a
+ jr nz, .notMoving
+.moving
+ ld a, [wd736]
+ bit 7, a ; is the player sprite spinning due to a spin tile?
+ jr nz, .skipSpriteAnim
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $7
+ ld l, a
+ ld a, [hl]
+ inc a
+ ld [hl], a
+ cp 4
+ jr nz, .calcImageIndex
+ xor a
+ ld [hl], a
+ inc hl
+ ld a, [hl]
+ inc a
+ and $3
+ ld [hl], a
+.calcImageIndex
+ ld a, [wSpriteStateData1 + 8]
+ ld b, a
+ ld a, [wSpriteStateData1 + 9]
+ add b
+ ld [wSpriteStateData1 + 2], a
+.skipSpriteAnim
+; If the player is standing on a grass tile, make the player's sprite have
+; lower priority than the background so that it's partially obscured by the
+; grass. Only the lower half of the sprite is permitted to have the priority
+; bit set by later logic.
+ ld a, [hTilePlayerStandingOn]
+ ld c, a
+ ld a, [wGrassTile]
+ cp c
+ ld a, $0
+ jr nz, .next2
+ ld a, $80
+.next2
+ ld [wSpriteStateData2 + 7], a
+ ret
+
+UnusedReadSpriteDataFunction:
+ push bc
+ push af
+ ld a, [H_CURRENTSPRITEOFFSET]
+ ld c, a
+ pop af
+ add c
+ ld l, a
+ pop bc
+ ret
+
+UpdateNPCSprite:
+ ld a, [H_CURRENTSPRITEOFFSET]
+ swap a
+ dec a
+ add a
+ ld hl, wMapSpriteData
+ add l
+ ld l, a
+ ld a, [hl] ; read movement byte 2
+ ld [wCurSpriteMovement2], a
+ ld h, $c1
+ ld a, [H_CURRENTSPRITEOFFSET]
+ ld l, a
+ inc l
+ ld a, [hl] ; c1x1
+ and a
+ jp z, InitializeSpriteStatus
+ call CheckSpriteAvailability
+ ret c ; if sprite is invisible, on tile >=$60, in grass or player is currently walking
+ ld h, $c1
+ ld a, [H_CURRENTSPRITEOFFSET]
+ ld l, a
+ inc l
+ ld a, [hl] ; c1x1
+ bit 7, a ; is the face player flag set?
+ jp nz, MakeNPCFacePlayer
+ ld b, a
+ ld a, [wFontLoaded]
+ bit 0, a
+ jp nz, notYetMoving
+ ld a, b
+ cp $2
+ jp z, UpdateSpriteMovementDelay ; c1x1 == 2
+ cp $3
+ jp z, UpdateSpriteInWalkingAnimation ; c1x1 == 3
+ ld a, [wWalkCounter]
+ and a
+ ret nz ; don't do anything yet if player is currently moving (redundant, already tested in CheckSpriteAvailability)
+ call InitializeSpriteScreenPosition
+ ld h, $c2
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $6
+ ld l, a
+ ld a, [hl] ; c2x6: movement byte 1
+ inc a
+ jr z, .randomMovement ; value $FF
+ inc a
+ jr z, .randomMovement ; value $FE
+; scripted movement
+ dec a
+ ld [hl], a ; increment movement byte 1 (movement data index)
+ dec a
+ push hl
+ ld hl, wNPCNumScriptedSteps
+ dec [hl] ; decrement wNPCNumScriptedSteps
+ pop hl
+ ld de, wNPCMovementDirections
+ call LoadDEPlusA ; a = [wNPCMovementDirections + movement byte 1]
+ cp $e0
+ jp z, ChangeFacingDirection
+ cp STAY
+ jr nz, .next
+; reached end of wNPCMovementDirections list
+ ld [hl], a ; store $ff in movement byte 1, disabling scripted movement
+ ld hl, wd730
+ res 0, [hl]
+ xor a
+ ld [wSimulatedJoypadStatesIndex], a
+ ld [wWastedByteCD3A], a
+ ret
+.next
+ cp WALK
+ jr nz, .determineDirection
+; current NPC movement data is $fe. this seems buggy
+ ld [hl], $1 ; set movement byte 1 to $1
+ ld de, wNPCMovementDirections
+ call LoadDEPlusA ; a = [wNPCMovementDirections + $fe] (?)
+ jr .determineDirection
+.randomMovement
+ call GetTileSpriteStandsOn
+ call Random
+.determineDirection
+ ld b, a
+ ld a, [wCurSpriteMovement2]
+ cp $d0
+ jr z, .moveDown ; movement byte 2 = $d0 forces down
+ cp $d1
+ jr z, .moveUp ; movement byte 2 = $d1 forces up
+ cp $d2
+ jr z, .moveLeft ; movement byte 2 = $d2 forces left
+ cp $d3
+ jr z, .moveRight ; movement byte 2 = $d3 forces right
+ ld a, b
+ cp $40 ; a < $40: down (or left)
+ jr nc, .notDown
+ ld a, [wCurSpriteMovement2]
+ cp $2
+ jr z, .moveLeft ; movement byte 2 = $2 only allows left or right
+.moveDown
+ ld de, 2*SCREEN_WIDTH
+ add hl, de ; move tile pointer two rows down
+ lb de, 1, 0
+ lb bc, 4, SPRITE_FACING_DOWN
+ jr TryWalking
+.notDown
+ cp $80 ; $40 <= a < $80: up (or right)
+ jr nc, .notUp
+ ld a, [wCurSpriteMovement2]
+ cp $2
+ jr z, .moveRight ; movement byte 2 = $2 only allows left or right
+.moveUp
+ ld de, -2*SCREEN_WIDTH
+ add hl, de ; move tile pointer two rows up
+ lb de, -1, 0
+ lb bc, 8, SPRITE_FACING_UP
+ jr TryWalking
+.notUp
+ cp $c0 ; $80 <= a < $c0: left (or up)
+ jr nc, .notLeft
+ ld a, [wCurSpriteMovement2]
+ cp $1
+ jr z, .moveUp ; movement byte 2 = $1 only allows up or down
+.moveLeft
+ dec hl
+ dec hl ; move tile pointer two columns left
+ lb de, 0, -1
+ lb bc, 2, SPRITE_FACING_LEFT
+ jr TryWalking
+.notLeft ; $c0 <= a: right (or down)
+ ld a, [wCurSpriteMovement2]
+ cp $1
+ jr z, .moveDown ; movement byte 2 = $1 only allows up or down
+.moveRight
+ inc hl
+ inc hl ; move tile pointer two columns right
+ lb de, 0, 1
+ lb bc, 1, SPRITE_FACING_RIGHT
+ jr TryWalking
+
+; changes facing direction by zeroing the movement delta and calling TryWalking
+ChangeFacingDirection:
+ ld de, $0
+ ; fall through
+
+; b: direction (1,2,4 or 8)
+; c: new facing direction (0,4,8 or $c)
+; d: Y movement delta (-1, 0 or 1)
+; e: X movement delta (-1, 0 or 1)
+; hl: pointer to tile the sprite would walk onto
+; set carry on failure, clears carry on success
+TryWalking:
+ push hl
+ ld h, $c1
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $9
+ ld l, a
+ ld [hl], c ; c1x9 (update facing direction)
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $3
+ ld l, a
+ ld [hl], d ; c1x3 (update Y movement delta)
+ inc l
+ inc l
+ ld [hl], e ; c1x5 (update X movement delta)
+ pop hl
+ push de
+ ld c, [hl] ; read tile to walk onto
+ call CanWalkOntoTile
+ pop de
+ ret c ; cannot walk there (reinitialization of delay values already done)
+ ld h, $c2
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $4
+ ld l, a
+ ld a, [hl] ; c2x4: Y position
+ add d
+ ld [hli], a ; update Y position
+ ld a, [hl] ; c2x5: X position
+ add e
+ ld [hl], a ; update X position
+ ld a, [H_CURRENTSPRITEOFFSET]
+ ld l, a
+ ld [hl], $10 ; c2x0=16: walk animation counter
+ dec h
+ inc l
+ ld [hl], $3 ; c1x1: set movement status to walking
+ jp UpdateSpriteImage
+
+; update the walking animation parameters for a sprite that is currently walking
+UpdateSpriteInWalkingAnimation:
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $7
+ ld l, a
+ ld a, [hl] ; c1x7 (counter until next walk animation frame)
+ inc a
+ ld [hl], a ; c1x7 += 1
+ cp $4
+ jr nz, .noNextAnimationFrame
+ xor a
+ ld [hl], a ; c1x7 = 0
+ inc l
+ ld a, [hl] ; c1x8 (walk animation frame)
+ inc a
+ and $3
+ ld [hl], a ; advance to next animation frame every 4 ticks (16 ticks total for one step)
+.noNextAnimationFrame
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $3
+ ld l, a
+ ld a, [hli] ; c1x3 (movement Y delta)
+ ld b, a
+ ld a, [hl] ; c1x4 (screen Y position)
+ add b
+ ld [hli], a ; update screen Y position
+ ld a, [hli] ; c1x5 (movement X delta)
+ ld b, a
+ ld a, [hl] ; c1x6 (screen X position)
+ add b
+ ld [hl], a ; update screen X position
+ ld a, [H_CURRENTSPRITEOFFSET]
+ ld l, a
+ inc h
+ ld a, [hl] ; c2x0 (walk animation counter)
+ dec a
+ ld [hl], a ; update walk animation counter
+ ret nz
+ ld a, $6 ; walking finished, update state
+ add l
+ ld l, a
+ ld a, [hl] ; c2x6 (movement byte 1)
+ cp $fe
+ jr nc, .initNextMovementCounter ; values $fe and $ff
+ ld a, [H_CURRENTSPRITEOFFSET]
+ inc a
+ ld l, a
+ dec h
+ ld [hl], $1 ; c1x1 = 1 (movement status ready)
+ ret
+.initNextMovementCounter
+ call Random
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $8
+ ld l, a
+ ld a, [hRandomAdd]
+ and $7f
+ ld [hl], a ; c2x8: set next movement delay to a random value in [0,$7f]
+ dec h ; note that value 0 actually makes the delay $100 (bug?)
+ ld a, [H_CURRENTSPRITEOFFSET]
+ inc a
+ ld l, a
+ ld [hl], $2 ; c1x1 = 2 (movement status)
+ inc l
+ inc l
+ xor a
+ ld b, [hl] ; c1x3 (movement Y delta)
+ ld [hli], a ; reset movement Y delta
+ inc l
+ ld c, [hl] ; c1x5 (movement X delta)
+ ld [hl], a ; reset movement X delta
+ ret
+
+; update delay value (c2x8) for sprites in the delayed state (c1x1)
+UpdateSpriteMovementDelay:
+ ld h, $c2
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $6
+ ld l, a
+ ld a, [hl] ; c2x6: movement byte 1
+ inc l
+ inc l
+ cp $fe
+ jr nc, .tickMoveCounter ; values $fe or $ff
+ ld [hl], $0
+ jr .moving
+.tickMoveCounter
+ dec [hl] ; c2x8: frame counter until next movement
+ jr nz, notYetMoving
+.moving
+ dec h
+ ld a, [H_CURRENTSPRITEOFFSET]
+ inc a
+ ld l, a
+ ld [hl], $1 ; c1x1 = 1 (mark as ready to move)
+notYetMoving:
+ ld h, wSpriteStateData1 / $100
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $8
+ ld l, a
+ ld [hl], $0 ; c1x8 = 0 (walk animation frame)
+ jp UpdateSpriteImage
+
+MakeNPCFacePlayer:
+; Make an NPC face the player if the player has spoken to him or her.
+
+; Check if the behaviour of the NPC facing the player when spoken to is
+; disabled. This is only done when rubbing the S.S. Anne captain's back.
+ ld a, [wd72d]
+ bit 5, a
+ jr nz, notYetMoving
+ res 7, [hl]
+ ld a, [wPlayerDirection]
+ bit PLAYER_DIR_BIT_UP, a
+ jr z, .notFacingDown
+ ld c, SPRITE_FACING_DOWN
+ jr .facingDirectionDetermined
+.notFacingDown
+ bit PLAYER_DIR_BIT_DOWN, a
+ jr z, .notFacingUp
+ ld c, SPRITE_FACING_UP
+ jr .facingDirectionDetermined
+.notFacingUp
+ bit PLAYER_DIR_BIT_LEFT, a
+ jr z, .notFacingRight
+ ld c, SPRITE_FACING_RIGHT
+ jr .facingDirectionDetermined
+.notFacingRight
+ ld c, SPRITE_FACING_LEFT
+.facingDirectionDetermined
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $9
+ ld l, a
+ ld [hl], c ; c1x9: set facing direction
+ jr notYetMoving
+
+InitializeSpriteStatus:
+ ld [hl], $1 ; $c1x1: set movement status to ready
+ inc l
+ ld [hl], $ff ; $c1x2: set sprite image to $ff (invisible/off screen)
+ inc h
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $2
+ ld l, a
+ ld a, $8
+ ld [hli], a ; $c2x2: set Y displacement to 8
+ ld [hl], a ; $c2x3: set X displacement to 8
+ ret
+
+; calculates the sprite's screen position form its map position and the player position
+InitializeSpriteScreenPosition:
+ ld h, wSpriteStateData2 / $100
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $4
+ ld l, a
+ ld a, [wYCoord]
+ ld b, a
+ ld a, [hl] ; c2x4 (Y position + 4)
+ sub b ; relative to player position
+ swap a ; * 16
+ sub $4 ; - 4
+ dec h
+ ld [hli], a ; c1x4 (screen Y position)
+ inc h
+ ld a, [wXCoord]
+ ld b, a
+ ld a, [hli] ; c2x6 (X position + 4)
+ sub b ; relative to player position
+ swap a ; * 16
+ dec h
+ ld [hl], a ; c1x6 (screen X position)
+ ret
+
+; tests if sprite is off screen or otherwise unable to do anything
+CheckSpriteAvailability:
+ predef IsObjectHidden
+ ld a, [$ffe5]
+ and a
+ jp nz, .spriteInvisible
+ ld h, wSpriteStateData2 / $100
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $6
+ ld l, a
+ ld a, [hl] ; c2x6: movement byte 1
+ cp $fe
+ jr c, .skipXVisibilityTest ; movement byte 1 < $fe (i.e. the sprite's movement is scripted)
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $4
+ ld l, a
+ ld b, [hl] ; c2x4: Y pos (+4)
+ ld a, [wYCoord]
+ cp b
+ jr z, .skipYVisibilityTest
+ jr nc, .spriteInvisible ; above screen region
+ add $8 ; screen is 9 tiles high
+ cp b
+ jr c, .spriteInvisible ; below screen region
+.skipYVisibilityTest
+ inc l
+ ld b, [hl] ; c2x5: X pos (+4)
+ ld a, [wXCoord]
+ cp b
+ jr z, .skipXVisibilityTest
+ jr nc, .spriteInvisible ; left of screen region
+ add $9 ; screen is 10 tiles wide
+ cp b
+ jr c, .spriteInvisible ; right of screen region
+.skipXVisibilityTest
+; make the sprite invisible if a text box is in front of it
+; $5F is the maximum number for map tiles
+ call GetTileSpriteStandsOn
+ ld d, $60
+ ld a, [hli]
+ cp d
+ jr nc, .spriteInvisible ; standing on tile with ID >=$60 (bottom left tile)
+ ld a, [hld]
+ cp d
+ jr nc, .spriteInvisible ; standing on tile with ID >=$60 (bottom right tile)
+ ld bc, -20
+ add hl, bc ; go back one row of tiles
+ ld a, [hli]
+ cp d
+ jr nc, .spriteInvisible ; standing on tile with ID >=$60 (top left tile)
+ ld a, [hl]
+ cp d
+ jr c, .spriteVisible ; standing on tile with ID >=$60 (top right tile)
+.spriteInvisible
+ ld h, wSpriteStateData1 / $100
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $2
+ ld l, a
+ ld [hl], $ff ; c1x2
+ scf
+ jr .done
+.spriteVisible
+ ld c, a
+ ld a, [wWalkCounter]
+ and a
+ jr nz, .done ; if player is currently walking, we're done
+ call UpdateSpriteImage
+ inc h
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $7
+ ld l, a
+ ld a, [wGrassTile]
+ cp c
+ ld a, $0
+ jr nz, .notInGrass
+ ld a, $80
+.notInGrass
+ ld [hl], a ; c2x7
+ and a
+.done
+ ret
+
+UpdateSpriteImage:
+ ld h, $c1
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $8
+ ld l, a
+ ld a, [hli] ; c1x8: walk animation frame
+ ld b, a
+ ld a, [hl] ; c1x9: facing direction
+ add b
+ ld b, a
+ ld a, [$ff93] ; current sprite offset
+ add b
+ ld b, a
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $2
+ ld l, a
+ ld [hl], b ; c1x2: sprite to display
+ ret
+
+; tests if sprite can walk the specified direction
+; b: direction (1,2,4 or 8)
+; c: ID of tile the sprite would walk onto
+; d: Y movement delta (-1, 0 or 1)
+; e: X movement delta (-1, 0 or 1)
+; set carry on failure, clears carry on success
+CanWalkOntoTile:
+ ld h, wSpriteStateData2 / $100
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $6
+ ld l, a
+ ld a, [hl] ; c2x6 (movement byte 1)
+ cp $fe
+ jr nc, .notScripted ; values $fe and $ff
+; always allow walking if the movement is scripted
+ and a
+ ret
+.notScripted
+ ld a, [wTilesetCollisionPtr]
+ ld l, a
+ ld a, [wTilesetCollisionPtr+1]
+ ld h, a
+.tilePassableLoop
+ ld a, [hli]
+ cp $ff
+ jr z, .impassable
+ cp c
+ jr nz, .tilePassableLoop
+ ld h, $c2
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $6
+ ld l, a
+ ld a, [hl] ; $c2x6 (movement byte 1)
+ inc a
+ jr z, .impassable ; if $ff, no movement allowed (however, changing direction is)
+ ld h, wSpriteStateData1 / $100
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $4
+ ld l, a
+ ld a, [hli] ; c1x4 (screen Y pos)
+ add $4 ; align to blocks (Y pos is always 4 pixels off)
+ add d ; add Y delta
+ cp $80 ; if value is >$80, the destination is off screen (either $81 or $FF underflow)
+ jr nc, .impassable ; don't walk off screen
+ inc l
+ ld a, [hl] ; c1x6 (screen X pos)
+ add e ; add X delta
+ cp $90 ; if value is >$90, the destination is off screen (either $91 or $FF underflow)
+ jr nc, .impassable ; don't walk off screen
+ push de
+ push bc
+ call DetectCollisionBetweenSprites
+ pop bc
+ pop de
+ ld h, wSpriteStateData1 / $100
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $c
+ ld l, a
+ ld a, [hl] ; c1xc (directions in which sprite collision would occur)
+ and b ; check against chosen direction (1,2,4 or 8)
+ jr nz, .impassable ; collision between sprites, don't go there
+ ld h, wSpriteStateData2 / $100
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $2
+ ld l, a
+ ld a, [hli] ; c2x2 (sprite Y displacement, initialized at $8, keep track of where a sprite did go)
+ bit 7, d ; check if going upwards (d=$ff)
+ jr nz, .upwards
+ add d
+ cp $5
+ jr c, .impassable ; if c2x2+d < 5, don't go ;bug: this tests probably were supposed to prevent sprites
+ jr .checkHorizontal ; from walking out too far, but this line makes sprites get stuck
+.upwards ; whenever they walked upwards 5 steps
+ sub $1 ; on the other hand, the amount a sprite can walk out to the
+ jr c, .impassable ; if d2x2 == 0, don't go ; right of bottom is not limited (until the counter overflows)
+.checkHorizontal
+ ld d, a
+ ld a, [hl] ; c2x3 (sprite X displacement, initialized at $8, keep track of where a sprite did go)
+ bit 7, e ; check if going left (e=$ff)
+ jr nz, .left
+ add e
+ cp $5 ; compare, but no conditional jump like in the vertical check above (bug?)
+ jr .passable
+.left
+ sub $1
+ jr c, .impassable ; if d2x3 == 0, don't go
+.passable
+ ld [hld], a ; update c2x3
+ ld [hl], d ; update c2x2
+ and a ; clear carry (marking success)
+ ret
+.impassable
+ ld h, $c1
+ ld a, [H_CURRENTSPRITEOFFSET]
+ inc a
+ ld l, a
+ ld [hl], $2 ; c1x1 = 2 (set movement status to delayed)
+ inc l
+ inc l
+ xor a
+ ld [hli], a ; c1x3 = 0 (clear Y movement delta)
+ inc l
+ ld [hl], a ; c1x5 = 0 (clear X movement delta)
+ inc h
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $8
+ ld l, a
+ call Random
+ ld a, [hRandomAdd]
+ and $7f
+ ld [hl], a ; c2x8: set next movement delay to a random value in [0,$7f] (again with delay $100 if value is 0)
+ scf ; set carry (marking failure to walk)
+ ret
+
+; calculates the tile pointer pointing to the tile the current sprite stands on
+; this is always the lower left tile of the 2x2 tile blocks all sprites are snapped to
+; hl: output pointer
+GetTileSpriteStandsOn:
+ ld h, wSpriteStateData1 / $100
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $4
+ ld l, a
+ ld a, [hli] ; c1x4: screen Y position
+ add $4 ; align to 2*2 tile blocks (Y position is always off 4 pixels to the top)
+ and $f0 ; in case object is currently moving
+ srl a ; screen Y tile * 4
+ ld c, a
+ ld b, $0
+ inc l
+ ld a, [hl] ; c1x6: screen Y position
+ srl a
+ srl a
+ srl a ; screen X tile
+ add SCREEN_WIDTH ; screen X tile + 20
+ ld d, $0
+ ld e, a
+ coord hl, 0, 0
+ add hl, bc
+ add hl, bc
+ add hl, bc
+ add hl, bc
+ add hl, bc
+ add hl, de ; wTileMap + 20*(screen Y tile + 1) + screen X tile
+ ret
+
+; loads [de+a] into a
+LoadDEPlusA:
+ add e
+ ld e, a
+ jr nc, .noCarry
+ inc d
+.noCarry
+ ld a, [de]
+ ret
+
+DoScriptedNPCMovement:
+; This is an alternative method of scripting an NPC's movement and is only used
+; a few times in the game. It is used when the NPC and player must walk together
+; in sync, such as when the player is following the NPC somewhere. An NPC can't
+; be moved in sync with the player using the other method.
+ ld a, [wd730]
+ bit 7, a
+ ret z
+ ld hl, wd72e
+ bit 7, [hl]
+ set 7, [hl]
+ jp z, InitScriptedNPCMovement
+ ld hl, wNPCMovementDirections2
+ ld a, [wNPCMovementDirections2Index]
+ add l
+ ld l, a
+ jr nc, .noCarry
+ inc h
+.noCarry
+ ld a, [hl]
+; check if moving up
+ cp NPC_MOVEMENT_UP
+ jr nz, .checkIfMovingDown
+ call GetSpriteScreenYPointer
+ ld c, SPRITE_FACING_UP
+ ld a, -2
+ jr .move
+.checkIfMovingDown
+ cp NPC_MOVEMENT_DOWN
+ jr nz, .checkIfMovingLeft
+ call GetSpriteScreenYPointer
+ ld c, SPRITE_FACING_DOWN
+ ld a, 2
+ jr .move
+.checkIfMovingLeft
+ cp NPC_MOVEMENT_LEFT
+ jr nz, .checkIfMovingRight
+ call GetSpriteScreenXPointer
+ ld c, SPRITE_FACING_LEFT
+ ld a, -2
+ jr .move
+.checkIfMovingRight
+ cp NPC_MOVEMENT_RIGHT
+ jr nz, .noMatch
+ call GetSpriteScreenXPointer
+ ld c, SPRITE_FACING_RIGHT
+ ld a, 2
+ jr .move
+.noMatch
+ cp $ff
+ ret
+.move
+ ld b, a
+ ld a, [hl]
+ add b
+ ld [hl], a
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $9
+ ld l, a
+ ld a, c
+ ld [hl], a ; facing direction
+ call AnimScriptedNPCMovement
+ ld hl, wScriptedNPCWalkCounter
+ dec [hl]
+ ret nz
+ ld a, 8
+ ld [wScriptedNPCWalkCounter], a
+ ld hl, wNPCMovementDirections2Index
+ inc [hl]
+ ret
+
+InitScriptedNPCMovement:
+ xor a
+ ld [wNPCMovementDirections2Index], a
+ ld a, 8
+ ld [wScriptedNPCWalkCounter], a
+ jp AnimScriptedNPCMovement
+
+GetSpriteScreenYPointer:
+ ld a, $4
+ ld b, a
+ jr GetSpriteScreenXYPointerCommon
+
+GetSpriteScreenXPointer:
+ ld a, $6
+ ld b, a
+
+GetSpriteScreenXYPointerCommon:
+ ld hl, wSpriteStateData1
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add l
+ add b
+ ld l, a
+ ret
+
+AnimScriptedNPCMovement:
+ ld hl, wSpriteStateData2
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $e
+ ld l, a
+ ld a, [hl] ; VRAM slot
+ dec a
+ swap a
+ ld b, a
+ ld hl, wSpriteStateData1
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $9
+ ld l, a
+ ld a, [hl] ; facing direction
+ cp SPRITE_FACING_DOWN
+ jr z, .anim
+ cp SPRITE_FACING_UP
+ jr z, .anim
+ cp SPRITE_FACING_LEFT
+ jr z, .anim
+ cp SPRITE_FACING_RIGHT
+ jr z, .anim
+ ret
+.anim
+ add b
+ ld b, a
+ ld [hSpriteVRAMSlotAndFacing], a
+ call AdvanceScriptedNPCAnimFrameCounter
+ ld hl, wSpriteStateData1
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $2
+ ld l, a
+ ld a, [hSpriteVRAMSlotAndFacing]
+ ld b, a
+ ld a, [hSpriteAnimFrameCounter]
+ add b
+ ld [hl], a
+ ret
+
+AdvanceScriptedNPCAnimFrameCounter:
+ ld a, [H_CURRENTSPRITEOFFSET]
+ add $7
+ ld l, a
+ ld a, [hl] ; intra-animation frame counter
+ inc a
+ ld [hl], a
+ cp 4
+ ret nz
+ xor a
+ ld [hl], a ; reset intra-animation frame counter
+ inc l
+ ld a, [hl] ; animation frame counter
+ inc a
+ and $3
+ ld [hl], a
+ ld [hSpriteAnimFrameCounter], a
+ ret
diff --git a/en/engine/overworld/pokemart.asm b/en/engine/overworld/pokemart.asm
new file mode 100755
index 00000000..dc5b29b5
--- /dev/null
+++ b/en/engine/overworld/pokemart.asm
@@ -0,0 +1,272 @@
+DisplayPokemartDialogue_:
+ ld a, [wListScrollOffset]
+ ld [wSavedListScrollOffset], a
+ call UpdateSprites
+ xor a
+ ld [wBoughtOrSoldItemInMart], a
+.loop
+ xor a
+ ld [wListScrollOffset], a
+ ld [wCurrentMenuItem], a
+ ld [wPlayerMonNumber], a
+ inc a
+ ld [wPrintItemPrices], a
+ ld a, MONEY_BOX
+ ld [wTextBoxID], a
+ call DisplayTextBoxID
+ ld a, BUY_SELL_QUIT_MENU
+ ld [wTextBoxID], a
+ call DisplayTextBoxID
+
+; This code is useless. It copies the address of the pokemart's inventory to hl,
+; but the address is never used.
+ ld hl, wItemListPointer
+ ld a, [hli]
+ ld l, [hl]
+ ld h, a
+
+ ld a, [wMenuExitMethod]
+ cp CANCELLED_MENU
+ jp z, .done
+ ld a, [wChosenMenuItem]
+ and a ; buying?
+ jp z, .buyMenu
+ dec a ; selling?
+ jp z, .sellMenu
+ dec a ; quitting?
+ jp z, .done
+.sellMenu
+
+; the same variables are set again below, so this code has no effect
+ xor a
+ ld [wPrintItemPrices], a
+ ld a, INIT_BAG_ITEM_LIST
+ ld [wInitListType], a
+ callab InitList
+
+ ld a, [wNumBagItems]
+ and a
+ jp z, .bagEmpty
+ ld hl, PokemonSellingGreetingText
+ call PrintText
+ call SaveScreenTilesToBuffer1 ; save screen
+.sellMenuLoop
+ call LoadScreenTilesFromBuffer1 ; restore saved screen
+ ld a, MONEY_BOX
+ ld [wTextBoxID], a
+ call DisplayTextBoxID ; draw money text box
+ ld hl, wNumBagItems
+ ld a, l
+ ld [wListPointer], a
+ ld a, h
+ ld [wListPointer + 1], a
+ xor a
+ ld [wPrintItemPrices], a
+ ld [wCurrentMenuItem], a
+ ld a, ITEMLISTMENU
+ ld [wListMenuID], a
+ call DisplayListMenuID
+ jp c, .returnToMainPokemartMenu ; if the player closed the menu
+.confirmItemSale ; if the player is trying to sell a specific item
+ call IsKeyItem
+ ld a, [wIsKeyItem]
+ and a
+ jr nz, .unsellableItem
+ ld a, [wcf91]
+ call IsItemHM
+ jr c, .unsellableItem
+ ld a, PRICEDITEMLISTMENU
+ ld [wListMenuID], a
+ ld [hHalveItemPrices], a ; halve prices when selling
+ call DisplayChooseQuantityMenu
+ inc a
+ jr z, .sellMenuLoop ; if the player closed the choose quantity menu with the B button
+ ld hl, PokemartTellSellPriceText
+ lb bc, 14, 1 ; location that PrintText always prints to, this is useless
+ call PrintText
+ coord hl, 14, 7
+ lb bc, 8, 15
+ ld a, TWO_OPTION_MENU
+ ld [wTextBoxID], a
+ call DisplayTextBoxID ; yes/no menu
+ ld a, [wMenuExitMethod]
+ cp CHOSE_SECOND_ITEM
+ jr z, .sellMenuLoop ; if the player chose No or pressed the B button
+
+; The following code is supposed to check if the player chose No, but the above
+; check already catches it.
+ ld a, [wChosenMenuItem]
+ dec a
+ jr z, .sellMenuLoop
+
+.sellItem
+ ld a, [wBoughtOrSoldItemInMart]
+ and a
+ jr nz, .skipSettingFlag1
+ inc a
+ ld [wBoughtOrSoldItemInMart], a
+.skipSettingFlag1
+ call AddAmountSoldToMoney
+ ld hl, wNumBagItems
+ call RemoveItemFromInventory
+ jp .sellMenuLoop
+.unsellableItem
+ ld hl, PokemartUnsellableItemText
+ call PrintText
+ jp .returnToMainPokemartMenu
+.bagEmpty
+ ld hl, PokemartItemBagEmptyText
+ call PrintText
+ call SaveScreenTilesToBuffer1
+ jp .returnToMainPokemartMenu
+.buyMenu
+
+; the same variables are set again below, so this code has no effect
+ ld a, 1
+ ld [wPrintItemPrices], a
+ ld a, INIT_OTHER_ITEM_LIST
+ ld [wInitListType], a
+ callab InitList
+
+ ld hl, PokemartBuyingGreetingText
+ call PrintText
+ call SaveScreenTilesToBuffer1
+.buyMenuLoop
+ call LoadScreenTilesFromBuffer1
+ ld a, MONEY_BOX
+ ld [wTextBoxID], a
+ call DisplayTextBoxID
+ ld hl, wItemList
+ ld a, l
+ ld [wListPointer], a
+ ld a, h
+ ld [wListPointer + 1], a
+ xor a
+ ld [wCurrentMenuItem], a
+ inc a
+ ld [wPrintItemPrices], a
+ inc a ; a = 2 (PRICEDITEMLISTMENU)
+ ld [wListMenuID], a
+ call DisplayListMenuID
+ jr c, .returnToMainPokemartMenu ; if the player closed the menu
+ ld a, 99
+ ld [wMaxItemQuantity], a
+ xor a
+ ld [hHalveItemPrices], a ; don't halve item prices when buying
+ call DisplayChooseQuantityMenu
+ inc a
+ jr z, .buyMenuLoop ; if the player closed the choose quantity menu with the B button
+ ld a, [wcf91] ; item ID
+ ld [wd11e], a ; store item ID for GetItemName
+ call GetItemName
+ call CopyStringToCF4B ; copy name to wcf4b
+ ld hl, PokemartTellBuyPriceText
+ call PrintText
+ coord hl, 14, 7
+ lb bc, 8, 15
+ ld a, TWO_OPTION_MENU
+ ld [wTextBoxID], a
+ call DisplayTextBoxID ; yes/no menu
+ ld a, [wMenuExitMethod]
+ cp CHOSE_SECOND_ITEM
+ jp z, .buyMenuLoop ; if the player chose No or pressed the B button
+
+; The following code is supposed to check if the player chose No, but the above
+; check already catches it.
+ ld a, [wChosenMenuItem]
+ dec a
+ jr z, .buyMenuLoop
+
+.buyItem
+ call .isThereEnoughMoney
+ jr c, .notEnoughMoney
+ ld hl, wNumBagItems
+ call AddItemToInventory
+ jr nc, .bagFull
+ call SubtractAmountPaidFromMoney
+ ld a, [wBoughtOrSoldItemInMart]
+ and a
+ jr nz, .skipSettingFlag2
+ ld a, 1
+ ld [wBoughtOrSoldItemInMart], a
+.skipSettingFlag2
+ ld a, SFX_PURCHASE
+ call PlaySoundWaitForCurrent
+ call WaitForSoundToFinish
+ ld hl, PokemartBoughtItemText
+ call PrintText
+ jp .buyMenuLoop
+.returnToMainPokemartMenu
+ call LoadScreenTilesFromBuffer1
+ ld a, MONEY_BOX
+ ld [wTextBoxID], a
+ call DisplayTextBoxID
+ ld hl, PokemartAnythingElseText
+ call PrintText
+ jp .loop
+.isThereEnoughMoney
+ ld de, wPlayerMoney
+ ld hl, hMoney
+ ld c, 3 ; length of money in bytes
+ jp StringCmp
+.notEnoughMoney
+ ld hl, PokemartNotEnoughMoneyText
+ call PrintText
+ jr .returnToMainPokemartMenu
+.bagFull
+ ld hl, PokemartItemBagFullText
+ call PrintText
+ jr .returnToMainPokemartMenu
+.done
+ ld hl, PokemartThankYouText
+ call PrintText
+ ld a, 1
+ ld [wUpdateSpritesEnabled], a
+ call UpdateSprites
+ ld a, [wSavedListScrollOffset]
+ ld [wListScrollOffset], a
+ ret
+
+PokemartBuyingGreetingText:
+ TX_FAR _PokemartBuyingGreetingText
+ db "@"
+
+PokemartTellBuyPriceText:
+ TX_FAR _PokemartTellBuyPriceText
+ db "@"
+
+PokemartBoughtItemText:
+ TX_FAR _PokemartBoughtItemText
+ db "@"
+
+PokemartNotEnoughMoneyText:
+ TX_FAR _PokemartNotEnoughMoneyText
+ db "@"
+
+PokemartItemBagFullText:
+ TX_FAR _PokemartItemBagFullText
+ db "@"
+
+PokemonSellingGreetingText:
+ TX_FAR _PokemonSellingGreetingText
+ db "@"
+
+PokemartTellSellPriceText:
+ TX_FAR _PokemartTellSellPriceText
+ db "@"
+
+PokemartItemBagEmptyText:
+ TX_FAR _PokemartItemBagEmptyText
+ db "@"
+
+PokemartUnsellableItemText:
+ TX_FAR _PokemartUnsellableItemText
+ db "@"
+
+PokemartThankYouText:
+ TX_FAR _PokemartThankYouText
+ db "@"
+
+PokemartAnythingElseText:
+ TX_FAR _PokemartAnythingElseText
+ db "@"
diff --git a/en/engine/print_waiting_text.asm b/en/engine/print_waiting_text.asm
new file mode 100644
index 00000000..7a95da2a
--- /dev/null
+++ b/en/engine/print_waiting_text.asm
@@ -0,0 +1,20 @@
+PrintWaitingText:
+ coord hl, 3, 10
+ ld b, $1
+ ld c, $b
+ ld a, [wIsInBattle]
+ and a
+ jr z, .asm_4c17
+ call TextBoxBorder
+ jr .asm_4c1a
+.asm_4c17
+ call CableClub_TextBoxBorder
+.asm_4c1a
+ coord hl, 4, 11
+ ld de, WaitingText
+ call PlaceString
+ ld c, 50
+ jp DelayFrames
+
+WaitingText:
+ db "Waiting...!@"
diff --git a/en/engine/save.asm b/en/engine/save.asm
new file mode 100755
index 00000000..97935dbb
--- /dev/null
+++ b/en/engine/save.asm
@@ -0,0 +1,708 @@
+LoadSAV:
+;(if carry -> write
+;"the file data is destroyed")
+ call ClearScreen
+ call LoadFontTilePatterns
+ call LoadTextBoxTilePatterns
+ call LoadSAV0
+ jr c, .badsum
+ call LoadSAV1
+ jr c, .badsum
+ call LoadSAV2
+ jr c, .badsum
+ ld a, $2 ; good checksum
+ jr .goodsum
+.badsum
+ ld hl, wd730
+ push hl
+ set 6, [hl]
+ ld hl, FileDataDestroyedText
+ call PrintText
+ ld c, 100
+ call DelayFrames
+ pop hl
+ res 6, [hl]
+ ld a, $1 ; bad checksum
+.goodsum
+ ld [wSaveFileStatus], a
+ ret
+
+FileDataDestroyedText:
+ TX_FAR _FileDataDestroyedText
+ db "@"
+
+LoadSAV0:
+ ld a, SRAM_ENABLE
+ ld [MBC1SRamEnable], a
+ ld a, $1
+ ld [MBC1SRamBankingMode], a
+ ld [MBC1SRamBank], a
+ ld hl, sPlayerName ; hero name located in SRAM
+ ld bc, sMainDataCheckSum - sPlayerName ; but here checks the full SAV
+ call SAVCheckSum
+ ld c, a
+ ld a, [sMainDataCheckSum] ; SAV's checksum
+ cp c
+ jp z, .checkSumsMatched
+
+; If the computed checksum didn't match the saved on, try again.
+ ld hl, sPlayerName
+ ld bc, sMainDataCheckSum - sPlayerName
+ call SAVCheckSum
+ ld c, a
+ ld a, [sMainDataCheckSum] ; SAV's checksum
+ cp c
+ jp nz, SAVBadCheckSum
+
+.checkSumsMatched
+ ld hl, sPlayerName
+ ld de, wPlayerName
+ ld bc, NAME_LENGTH
+ call CopyData
+ ld hl, sMainData
+ ld de, wMainDataStart
+ ld bc, wMainDataEnd - wMainDataStart
+ call CopyData
+ ld hl, wCurMapTileset
+ set 7, [hl]
+ ld hl, sSpriteData
+ ld de, wSpriteDataStart
+ ld bc, wSpriteDataEnd - wSpriteDataStart
+ call CopyData
+ ld a, [sTilesetType]
+ ld [hTilesetType], a
+ ld hl, sCurBoxData
+ ld de, wBoxDataStart
+ ld bc, wBoxDataEnd - wBoxDataStart
+ call CopyData
+ and a
+ jp SAVGoodChecksum
+
+LoadSAV1:
+ ld a, SRAM_ENABLE
+ ld [MBC1SRamEnable], a
+ ld a, $1
+ ld [MBC1SRamBankingMode], a
+ ld [MBC1SRamBank], a
+ ld hl, sPlayerName ; hero name located in SRAM
+ ld bc, sMainDataCheckSum - sPlayerName ; but here checks the full SAV
+ call SAVCheckSum
+ ld c, a
+ ld a, [sMainDataCheckSum] ; SAV's checksum
+ cp c
+ jr nz, SAVBadCheckSum
+ ld hl, sCurBoxData
+ ld de, wBoxDataStart
+ ld bc, wBoxDataEnd - wBoxDataStart
+ call CopyData
+ and a
+ jp SAVGoodChecksum
+
+LoadSAV2:
+ ld a, SRAM_ENABLE
+ ld [MBC1SRamEnable], a
+ ld a, $1
+ ld [MBC1SRamBankingMode], a
+ ld [MBC1SRamBank], a
+ ld hl, sPlayerName ; hero name located in SRAM
+ ld bc, sMainDataCheckSum - sPlayerName ; but here checks the full SAV
+ call SAVCheckSum
+ ld c, a
+ ld a, [sMainDataCheckSum] ; SAV's checksum
+ cp c
+ jp nz, SAVBadCheckSum
+ ld hl, sPartyData
+ ld de, wPartyDataStart
+ ld bc, wPartyDataEnd - wPartyDataStart
+ call CopyData
+ ld hl, sMainData
+ ld de, wPokedexOwned
+ ld bc, wPokedexSeenEnd - wPokedexOwned
+ call CopyData
+ and a
+ jp SAVGoodChecksum
+
+SAVBadCheckSum:
+ scf
+
+SAVGoodChecksum:
+ ld a, $0
+ ld [MBC1SRamBankingMode], a
+ ld [MBC1SRamEnable], a
+ ret
+
+LoadSAVIgnoreBadCheckSum:
+; unused function that loads save data and ignores bad checksums
+ call LoadSAV0
+ call LoadSAV1
+ jp LoadSAV2
+
+SaveSAV:
+ callba PrintSaveScreenText
+ ld hl, WouldYouLikeToSaveText
+ call SaveSAVConfirm
+ and a ;|0 = Yes|1 = No|
+ ret nz
+ ld a, [wSaveFileStatus]
+ dec a
+ jr z, .save
+ call SAVCheckRandomID
+ jr z, .save
+ ld hl, OlderFileWillBeErasedText
+ call SaveSAVConfirm
+ and a
+ ret nz
+.save
+ call SaveSAVtoSRAM
+ coord hl, 1, 13
+ lb bc, 4, 18
+ call ClearScreenArea
+ coord hl, 1, 14
+ ld de, NowSavingString
+ call PlaceString
+ ld c, 120
+ call DelayFrames
+ ld hl, GameSavedText
+ call PrintText
+ ld a, SFX_SAVE
+ call PlaySoundWaitForCurrent
+ call WaitForSoundToFinish
+ ld c, 30
+ jp DelayFrames
+
+NowSavingString:
+ db "Now saving...@"
+
+SaveSAVConfirm:
+ call PrintText
+ coord hl, 0, 7
+ lb bc, 8, 1
+ ld a, TWO_OPTION_MENU
+ ld [wTextBoxID], a
+ call DisplayTextBoxID ; yes/no menu
+ ld a, [wCurrentMenuItem]
+ ret
+
+WouldYouLikeToSaveText:
+ TX_FAR _WouldYouLikeToSaveText
+ db "@"
+
+GameSavedText:
+ TX_FAR _GameSavedText
+ db "@"
+
+OlderFileWillBeErasedText:
+ TX_FAR _OlderFileWillBeErasedText
+ db "@"
+
+SaveSAVtoSRAM0:
+ ld a, SRAM_ENABLE
+ ld [MBC1SRamEnable], a
+ ld a, $1
+ ld [MBC1SRamBankingMode], a
+ ld [MBC1SRamBank], a
+ ld hl, wPlayerName
+ ld de, sPlayerName
+ ld bc, NAME_LENGTH
+ call CopyData
+ ld hl, wMainDataStart
+ ld de, sMainData
+ ld bc, wMainDataEnd - wMainDataStart
+ call CopyData
+ ld hl, wSpriteDataStart
+ ld de, sSpriteData
+ ld bc, wSpriteDataEnd - wSpriteDataStart
+ call CopyData
+ ld hl, wBoxDataStart
+ ld de, sCurBoxData
+ ld bc, wBoxDataEnd - wBoxDataStart
+ call CopyData
+ ld a, [hTilesetType]
+ ld [sTilesetType], a
+ ld hl, sPlayerName
+ ld bc, sMainDataCheckSum - sPlayerName
+ call SAVCheckSum
+ ld [sMainDataCheckSum], a
+ xor a
+ ld [MBC1SRamBankingMode], a
+ ld [MBC1SRamEnable], a
+ ret
+
+SaveSAVtoSRAM1:
+; stored pokémon
+ ld a, SRAM_ENABLE
+ ld [MBC1SRamEnable], a
+ ld a, $1
+ ld [MBC1SRamBankingMode], a
+ ld [MBC1SRamBank], a
+ ld hl, wBoxDataStart
+ ld de, sCurBoxData
+ ld bc, wBoxDataEnd - wBoxDataStart
+ call CopyData
+ ld hl, sPlayerName
+ ld bc, sMainDataCheckSum - sPlayerName
+ call SAVCheckSum
+ ld [sMainDataCheckSum], a
+ xor a
+ ld [MBC1SRamBankingMode], a
+ ld [MBC1SRamEnable], a
+ ret
+
+SaveSAVtoSRAM2:
+ ld a, SRAM_ENABLE
+ ld [MBC1SRamEnable], a
+ ld a, $1
+ ld [MBC1SRamBankingMode], a
+ ld [MBC1SRamBank], a
+ ld hl, wPartyDataStart
+ ld de, sPartyData
+ ld bc, wPartyDataEnd - wPartyDataStart
+ call CopyData
+ ld hl, wPokedexOwned ; pokédex only
+ ld de, sMainData
+ ld bc, wPokedexSeenEnd - wPokedexOwned
+ call CopyData
+ ld hl, sPlayerName
+ ld bc, sMainDataCheckSum - sPlayerName
+ call SAVCheckSum
+ ld [sMainDataCheckSum], a
+ xor a
+ ld [MBC1SRamBankingMode], a
+ ld [MBC1SRamEnable], a
+ ret
+
+SaveSAVtoSRAM:
+ ld a, $2
+ ld [wSaveFileStatus], a
+ call SaveSAVtoSRAM0
+ call SaveSAVtoSRAM1
+ jp SaveSAVtoSRAM2
+
+SAVCheckSum:
+;Check Sum (result[1 byte] is complemented)
+ ld d, 0
+.loop
+ ld a, [hli]
+ add d
+ ld d, a
+ dec bc
+ ld a, b
+ or c
+ jr nz, .loop
+ ld a, d
+ cpl
+ ret
+
+CalcIndividualBoxCheckSums:
+ ld hl, sBox1 ; sBox7
+ ld de, sBank2IndividualBoxChecksums ; sBank3IndividualBoxChecksums
+ ld b, NUM_BOXES / 2
+.loop
+ push bc
+ push de
+ ld bc, wBoxDataEnd - wBoxDataStart
+ call SAVCheckSum
+ pop de
+ ld [de], a
+ inc de
+ pop bc
+ dec b
+ jr nz, .loop
+ ret
+
+GetBoxSRAMLocation:
+; in: a = box num
+; out: b = box SRAM bank, hl = pointer to start of box
+ ld hl, BoxSRAMPointerTable
+ ld a, [wCurrentBoxNum]
+ and $7f
+ cp NUM_BOXES / 2
+ ld b, 2
+ jr c, .next
+ inc b
+ sub NUM_BOXES / 2
+.next
+ ld e, a
+ ld d, 0
+ add hl, de
+ add hl, de
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ret
+
+BoxSRAMPointerTable:
+ dw sBox1 ; sBox7
+ dw sBox2 ; sBox8
+ dw sBox3 ; sBox9
+ dw sBox4 ; sBox10
+ dw sBox5 ; sBox11
+ dw sBox6 ; sBox12
+
+ChangeBox::
+ ld hl, WhenYouChangeBoxText
+ call PrintText
+ call YesNoChoice
+ ld a, [wCurrentMenuItem]
+ and a
+ ret nz ; return if No was chosen
+ ld hl, wCurrentBoxNum
+ bit 7, [hl] ; is it the first time player is changing the box?
+ call z, EmptyAllSRAMBoxes ; if so, empty all boxes in SRAM
+ call DisplayChangeBoxMenu
+ call UpdateSprites
+ ld hl, hFlags_0xFFF6
+ set 1, [hl]
+ call HandleMenuInput
+ ld hl, hFlags_0xFFF6
+ res 1, [hl]
+ bit 1, a ; pressed b
+ ret nz
+ call GetBoxSRAMLocation
+ ld e, l
+ ld d, h
+ ld hl, wBoxDataStart
+ call CopyBoxToOrFromSRAM ; copy old box from WRAM to SRAM
+ ld a, [wCurrentMenuItem]
+ set 7, a
+ ld [wCurrentBoxNum], a
+ call GetBoxSRAMLocation
+ ld de, wBoxDataStart
+ call CopyBoxToOrFromSRAM ; copy new box from SRAM to WRAM
+ ld hl, wMapTextPtr
+ ld de, wChangeBoxSavedMapTextPointer
+ ld a, [hli]
+ ld [de], a
+ inc de
+ ld a, [hl]
+ ld [de], a
+ call RestoreMapTextPointer
+ call SaveSAVtoSRAM
+ ld hl, wChangeBoxSavedMapTextPointer
+ call SetMapTextPointer
+ ld a, SFX_SAVE
+ call PlaySoundWaitForCurrent
+ call WaitForSoundToFinish
+ ret
+
+WhenYouChangeBoxText:
+ TX_FAR _WhenYouChangeBoxText
+ db "@"
+
+CopyBoxToOrFromSRAM:
+; copy an entire box from hl to de with b as the SRAM bank
+ push hl
+ ld a, SRAM_ENABLE
+ ld [MBC1SRamEnable], a
+ ld a, $1
+ ld [MBC1SRamBankingMode], a
+ ld a, b
+ ld [MBC1SRamBank], a
+ ld bc, wBoxDataEnd - wBoxDataStart
+ call CopyData
+ pop hl
+
+; mark the memory that the box was copied from as am empty box
+ xor a
+ ld [hli], a
+ dec a
+ ld [hl], a
+
+ ld hl, sBox1 ; sBox7
+ ld bc, sBank2AllBoxesChecksum - sBox1
+ call SAVCheckSum
+ ld [sBank2AllBoxesChecksum], a ; sBank3AllBoxesChecksum
+ call CalcIndividualBoxCheckSums
+ xor a
+ ld [MBC1SRamBankingMode], a
+ ld [MBC1SRamEnable], a
+ ret
+
+DisplayChangeBoxMenu:
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ld a, A_BUTTON | B_BUTTON
+ ld [wMenuWatchedKeys], a
+ ld a, 11
+ ld [wMaxMenuItem], a
+ ld a, 1
+ ld [wTopMenuItemY], a
+ ld a, 12
+ ld [wTopMenuItemX], a
+ xor a
+ ld [wMenuWatchMovingOutOfBounds], a
+ ld a, [wCurrentBoxNum]
+ and $7f
+ ld [wCurrentMenuItem], a
+ ld [wLastMenuItem], a
+ coord hl, 0, 0
+ ld b, 2
+ ld c, 9
+ call TextBoxBorder
+ ld hl, ChooseABoxText
+ call PrintText
+ coord hl, 11, 0
+ ld b, 12
+ ld c, 7
+ call TextBoxBorder
+ ld hl, hFlags_0xFFF6
+ set 2, [hl]
+ ld de, BoxNames
+ coord hl, 13, 1
+ call PlaceString
+ ld hl, hFlags_0xFFF6
+ res 2, [hl]
+ ld a, [wCurrentBoxNum]
+ and $7f
+ cp 9
+ jr c, .singleDigitBoxNum
+ sub 9
+ coord hl, 8, 2
+ ld [hl], "1"
+ add "0"
+ jr .next
+.singleDigitBoxNum
+ add "1"
+.next
+ Coorda 9, 2
+ coord hl, 1, 2
+ ld de, BoxNoText
+ call PlaceString
+ call GetMonCountsForAllBoxes
+ coord hl, 18, 1
+ ld de, wBoxMonCounts
+ ld bc, SCREEN_WIDTH
+ ld a, $c
+.loop
+ push af
+ ld a, [de]
+ and a ; is the box empty?
+ jr z, .skipPlacingPokeball
+ ld [hl], $78 ; place pokeball tile next to box name if box not empty
+.skipPlacingPokeball
+ add hl, bc
+ inc de
+ pop af
+ dec a
+ jr nz, .loop
+ ld a, 1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ret
+
+ChooseABoxText:
+ TX_FAR _ChooseABoxText
+ db "@"
+
+BoxNames:
+ db "BOX 1"
+ next "BOX 2"
+ next "BOX 3"
+ next "BOX 4"
+ next "BOX 5"
+ next "BOX 6"
+ next "BOX 7"
+ next "BOX 8"
+ next "BOX 9"
+ next "BOX10"
+ next "BOX11"
+ next "BOX12@"
+
+BoxNoText:
+ db "BOX No.@"
+
+EmptyAllSRAMBoxes:
+; marks all boxes in SRAM as empty (initialisation for the first time the
+; player changes the box)
+ ld a, SRAM_ENABLE
+ ld [MBC1SRamEnable], a
+ ld a, $1
+ ld [MBC1SRamBankingMode], a
+ ld a, 2
+ ld [MBC1SRamBank], a
+ call EmptySRAMBoxesInBank
+ ld a, 3
+ ld [MBC1SRamBank], a
+ call EmptySRAMBoxesInBank
+ xor a
+ ld [MBC1SRamBankingMode], a
+ ld [MBC1SRamEnable], a
+ ret
+
+EmptySRAMBoxesInBank:
+; marks every box in the current SRAM bank as empty
+ ld hl, sBox1 ; sBox7
+ call EmptySRAMBox
+ ld hl, sBox2 ; sBox8
+ call EmptySRAMBox
+ ld hl, sBox3 ; sBox9
+ call EmptySRAMBox
+ ld hl, sBox4 ; sBox10
+ call EmptySRAMBox
+ ld hl, sBox5 ; sBox11
+ call EmptySRAMBox
+ ld hl, sBox6 ; sBox12
+ call EmptySRAMBox
+ ld hl, sBox1 ; sBox7
+ ld bc, sBank2AllBoxesChecksum - sBox1
+ call SAVCheckSum
+ ld [sBank2AllBoxesChecksum], a ; sBank3AllBoxesChecksum
+ call CalcIndividualBoxCheckSums
+ ret
+
+EmptySRAMBox:
+ xor a
+ ld [hli], a
+ dec a
+ ld [hl], a
+ ret
+
+GetMonCountsForAllBoxes:
+ ld hl, wBoxMonCounts
+ push hl
+ ld a, SRAM_ENABLE
+ ld [MBC1SRamEnable], a
+ ld a, $1
+ ld [MBC1SRamBankingMode], a
+ ld a, $2
+ ld [MBC1SRamBank], a
+ call GetMonCountsForBoxesInBank
+ ld a, $3
+ ld [MBC1SRamBank], a
+ call GetMonCountsForBoxesInBank
+ xor a
+ ld [MBC1SRamBankingMode], a
+ ld [MBC1SRamEnable], a
+ pop hl
+
+; copy the count for the current box from WRAM
+ ld a, [wCurrentBoxNum]
+ and $7f
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld a, [wNumInBox]
+ ld [hl], a
+
+ ret
+
+GetMonCountsForBoxesInBank:
+ ld a, [sBox1] ; sBox7
+ ld [hli], a
+ ld a, [sBox2] ; sBox8
+ ld [hli], a
+ ld a, [sBox3] ; sBox9
+ ld [hli], a
+ ld a, [sBox4] ; sBox10
+ ld [hli], a
+ ld a, [sBox5] ; sBox11
+ ld [hli], a
+ ld a, [sBox6] ; sBox12
+ ld [hli], a
+ ret
+
+SAVCheckRandomID:
+;checks if Sav file is the same by checking player's name 1st letter ($a598)
+; and the two random numbers generated at game beginning
+;(which are stored at wPlayerID)s
+ ld a, $0a
+ ld [MBC1SRamEnable], a
+ ld a, $01
+ ld [MBC1SRamBankingMode], a
+ ld [MBC1SRamBank], a
+ ld a, [sPlayerName]
+ and a
+ jr z, .next
+ ld hl, sPlayerName
+ ld bc, sMainDataCheckSum - sPlayerName
+ call SAVCheckSum
+ ld c, a
+ ld a, [sMainDataCheckSum]
+ cp c
+ jr nz, .next
+ ld hl, sMainData + (wPlayerID - wMainDataStart) ; player ID
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld a, [wPlayerID]
+ cp l
+ jr nz, .next
+ ld a, [wPlayerID + 1]
+ cp h
+.next
+ ld a, $00
+ ld [MBC1SRamBankingMode], a
+ ld [MBC1SRamEnable], a
+ ret
+
+SaveHallOfFameTeams:
+ ld a, [wNumHoFTeams]
+ dec a
+ cp HOF_TEAM_CAPACITY
+ jr nc, .shiftHOFTeams
+ ld hl, sHallOfFame
+ ld bc, HOF_TEAM
+ call AddNTimes
+ ld e, l
+ ld d, h
+ ld hl, wHallOfFame
+ ld bc, HOF_TEAM
+ jr HallOfFame_Copy
+
+.shiftHOFTeams
+; if the space designated for HOF teams is full, then shift all HOF teams to the next slot, making space for the new HOF team
+; this deletes the last HOF team though
+ ld hl, sHallOfFame + HOF_TEAM
+ ld de, sHallOfFame
+ ld bc, HOF_TEAM * (HOF_TEAM_CAPACITY - 1)
+ call HallOfFame_Copy
+ ld hl, wHallOfFame
+ ld de, sHallOfFame + HOF_TEAM * (HOF_TEAM_CAPACITY - 1)
+ ld bc, HOF_TEAM
+ jr HallOfFame_Copy
+
+LoadHallOfFameTeams:
+ ld hl, sHallOfFame
+ ld bc, HOF_TEAM
+ ld a, [wHoFTeamIndex]
+ call AddNTimes
+ ld de, wHallOfFame
+ ld bc, HOF_TEAM
+ ; fallthrough
+
+HallOfFame_Copy:
+ ld a, SRAM_ENABLE
+ ld [MBC1SRamEnable], a
+ ld a, $1
+ ld [MBC1SRamBankingMode], a
+ xor a
+ ld [MBC1SRamBank], a
+ call CopyData
+ xor a
+ ld [MBC1SRamBankingMode], a
+ ld [MBC1SRamEnable], a
+ ret
+
+ClearSAV:
+ ld a, SRAM_ENABLE
+ ld [MBC1SRamEnable], a
+ ld a, $1
+ ld [MBC1SRamBankingMode], a
+ xor a
+ call PadSRAM_FF
+ ld a, $1
+ call PadSRAM_FF
+ ld a, $2
+ call PadSRAM_FF
+ ld a, $3
+ call PadSRAM_FF
+ xor a
+ ld [MBC1SRamBankingMode], a
+ ld [MBC1SRamEnable], a
+ ret
+
+PadSRAM_FF:
+ ld [MBC1SRamBank], a
+ ld hl, $a000
+ ld bc, $2000
+ ld a, $ff
+ jp FillMemory
diff --git a/en/engine/slot_machine.asm b/en/engine/slot_machine.asm
new file mode 100755
index 00000000..cb9a3bfb
--- /dev/null
+++ b/en/engine/slot_machine.asm
@@ -0,0 +1,892 @@
+PromptUserToPlaySlots:
+ call SaveScreenTilesToBuffer2
+ ld a, BANK(DisplayTextIDInit)
+ ld [wAutoTextBoxDrawingControl], a
+ ld b, a
+ ld hl, DisplayTextIDInit
+ call Bankswitch
+ ld hl, PlaySlotMachineText
+ call PrintText
+ call YesNoChoice
+ ld a, [wCurrentMenuItem]
+ and a
+ jr nz, .done ; if player chose No
+ dec a
+ ld [wUpdateSpritesEnabled], a
+ ld hl, wSlotMachineRerollCounter
+ xor a
+ ld [hli], a
+ ld [hl], SMILE_BUBBLE
+ predef EmotionBubble
+ call GBPalWhiteOutWithDelay3
+ call LoadSlotMachineTiles
+ call LoadFontTilePatterns
+ ld b, SET_PAL_SLOTS
+ call RunPaletteCommand
+ call GBPalNormal
+ ld a, $e4
+ ld [rOBP0], a
+ ld hl, wd730
+ set 6, [hl]
+ xor a
+ ld [wSlotMachineAllowMatchesCounter], a
+ ld hl, wStoppingWhichSlotMachineWheel
+ ld bc, $0014
+ call FillMemory
+ call MainSlotMachineLoop
+ ld hl, wd730
+ res 6, [hl]
+ xor a
+ ld [wSlotMachineAllowMatchesCounter], a
+ call GBPalWhiteOutWithDelay3
+ ld a, $1
+ ld [wUpdateSpritesEnabled], a
+ call RunDefaultPaletteCommand
+ call ReloadMapSpriteTilePatterns
+ call ReloadTilesetTilePatterns
+.done
+ call LoadScreenTilesFromBuffer2
+ call Delay3
+ call GBPalNormal
+ ld a, [wSlotMachineSavedROMBank]
+ push af
+ jp CloseTextDisplay
+
+PlaySlotMachineText:
+ TX_FAR _PlaySlotMachineText
+ db "@"
+
+MainSlotMachineLoop:
+ call SlotMachine_PrintCreditCoins
+ xor a
+ ld hl, wPayoutCoins
+ ld [hli], a
+ ld [hl], a
+ call SlotMachine_PrintPayoutCoins
+ ld hl, BetHowManySlotMachineText
+ call PrintText
+ call SaveScreenTilesToBuffer1
+.loop
+ ld a, A_BUTTON | B_BUTTON
+ ld [wMenuWatchedKeys], a
+ ld a, 2
+ ld [wMaxMenuItem], a
+ ld a, 12
+ ld [wTopMenuItemY], a
+ ld a, 15
+ ld [wTopMenuItemX], a
+ xor a
+ ld [wCurrentMenuItem], a
+ ld [wLastMenuItem], a
+ ld [wMenuWatchMovingOutOfBounds], a
+ coord hl, 14, 11
+ ld b, 5
+ ld c, 4
+ call TextBoxBorder
+ coord hl, 16, 12
+ ld de, CoinMultiplierSlotMachineText
+ call PlaceString
+ call HandleMenuInput
+ and B_BUTTON
+ jp nz, LoadScreenTilesFromBuffer1
+ ld a, [wCurrentMenuItem]
+ ld b, a
+ ld a, 3
+ sub b
+ ld [wSlotMachineBet], a
+ ld hl, wPlayerCoins
+ ld c, a
+ ld a, [hli]
+ and a
+ jr nz, .skip1
+ ld a, [hl]
+ cp c
+ jr nc, .skip1
+ ld hl, NotEnoughCoinsSlotMachineText
+ call PrintText
+ jr .loop
+.skip1
+ call LoadScreenTilesFromBuffer1
+ call SlotMachine_SubtractBetFromPlayerCoins
+ call SlotMachine_LightBalls
+ call SlotMachine_SetFlags
+ ld a, 4
+ ld hl, wSlotMachineWheel1SlipCounter
+ ld [hli], a
+ ld [hli], a
+ ld [hl], a
+ call WaitForSoundToFinish
+ ld a, SFX_SLOTS_NEW_SPIN
+ call PlaySound
+ ld hl, StartSlotMachineText
+ call PrintText
+ call SlotMachine_SpinWheels
+ call SlotMachine_CheckForMatches
+ ld hl, wPlayerCoins
+ ld a, [hli]
+ or [hl]
+ jr nz, .skip2
+ ld hl, OutOfCoinsSlotMachineText
+ call PrintText
+ ld c, 60
+ jp DelayFrames
+.skip2
+ ld hl, OneMoreGoSlotMachineText
+ call PrintText
+ coord hl, 14, 12
+ lb bc, 13, 15
+ xor a ; YES_NO_MENU
+ ld [wTwoOptionMenuID], a
+ ld a, TWO_OPTION_MENU
+ ld [wTextBoxID], a
+ call DisplayTextBoxID
+ ld a, [wCurrentMenuItem]
+ and a
+ ret nz
+ call SlotMachine_PutOutLitBalls
+ jp MainSlotMachineLoop
+
+CoinMultiplierSlotMachineText:
+ db "×3"
+ next "×2"
+ next "×1@"
+
+OutOfCoinsSlotMachineText:
+ TX_FAR _OutOfCoinsSlotMachineText
+ db "@"
+
+BetHowManySlotMachineText:
+ TX_FAR _BetHowManySlotMachineText
+ db "@"
+
+StartSlotMachineText:
+ TX_FAR _StartSlotMachineText
+ db "@"
+
+NotEnoughCoinsSlotMachineText:
+ TX_FAR _NotEnoughCoinsSlotMachineText
+ db "@"
+
+OneMoreGoSlotMachineText:
+ TX_FAR _OneMoreGoSlotMachineText
+ db "@"
+
+SlotMachine_SetFlags:
+ ld hl, wSlotMachineFlags
+ bit 7, [hl]
+ ret nz
+ ld a, [wSlotMachineAllowMatchesCounter]
+ and a
+ jr nz, .allowMatches
+ call Random
+ and a
+ jr z, .setAllowMatchesCounter ; 1/256 (~0.4%) chance
+ ld b, a
+ ld a, [wSlotMachineSevenAndBarModeChance]
+ cp b
+ jr c, .allowSevenAndBarMatches
+ ld a, 210
+ cp b
+ jr c, .allowMatches ; 55/256 (~21.5%) chance
+ ld [hl], 0
+ ret
+.allowMatches
+ set 6, [hl]
+ ret
+.setAllowMatchesCounter
+ ld a, 60
+ ld [wSlotMachineAllowMatchesCounter], a
+ ret
+.allowSevenAndBarMatches
+ set 7, [hl]
+ ret
+
+SlotMachine_SpinWheels:
+ ld c, 20
+.loop1
+ push bc
+ call SlotMachine_AnimWheel1
+ call SlotMachine_AnimWheel2
+ call SlotMachine_AnimWheel3
+ ld c, 2
+ call DelayFrames
+ pop bc
+ dec c
+ jr nz, .loop1
+ xor a
+ ld [wStoppingWhichSlotMachineWheel], a
+.loop2
+ call SlotMachine_HandleInputWhileWheelsSpin
+ call SlotMachine_StopOrAnimWheel1
+ call SlotMachine_StopOrAnimWheel2
+ call SlotMachine_StopOrAnimWheel3
+ ret c
+ ld a, [wOnSGB]
+ xor $1
+ inc a
+ ld c, a
+ call DelayFrames
+ jr .loop2
+
+; Note that the wheels can only stop when a symbol is centred in the wheel
+; and thus 3 full symbols rather than 2 full symbols and 2 half symbols are
+; visible. The 3 functions below ensure this by checking if the wheel offset
+; is even before stopping the wheel.
+
+SlotMachine_StopOrAnimWheel1:
+ ld a, [wStoppingWhichSlotMachineWheel]
+ cp 1
+ jr c, .animWheel
+ ld de, wSlotMachineWheel1Offset
+ ld a, [de]
+ rra
+ jr nc, .animWheel ; check that a symbol is centred in the wheel
+ ld hl, wSlotMachineWheel1SlipCounter
+ ld a, [hl]
+ and a
+ ret z
+ dec [hl]
+ call SlotMachine_StopWheel1Early
+ ret nz
+.animWheel
+ jp SlotMachine_AnimWheel1
+
+SlotMachine_StopOrAnimWheel2:
+ ld a, [wStoppingWhichSlotMachineWheel]
+ cp 2
+ jr c, .animWheel
+ ld de, wSlotMachineWheel2Offset
+ ld a, [de]
+ rra
+ jr nc, .animWheel ; check that a symbol is centred in the wheel
+ ld hl, wSlotMachineWheel2SlipCounter
+ ld a, [hl]
+ and a
+ ret z
+ dec [hl]
+ call SlotMachine_StopWheel2Early
+ ret z
+.animWheel
+ jp SlotMachine_AnimWheel2
+
+SlotMachine_StopOrAnimWheel3:
+ ld a, [wStoppingWhichSlotMachineWheel]
+ cp 3
+ jr c, .animWheel
+ ld de, wSlotMachineWheel3Offset
+ ld a, [de]
+ rra
+ jr nc, .animWheel ; check that a symbol is centred in the wheel
+; wheel 3 stops as soon as possible
+ scf
+ ret
+.animWheel
+ call SlotMachine_AnimWheel3
+ and a
+ ret
+
+SlotMachine_StopWheel1Early:
+ call SlotMachine_GetWheel1Tiles
+ ld hl, wSlotMachineWheel1BottomTile
+ ld a, [wSlotMachineFlags]
+ and $80
+ jr nz, .sevenAndBarMode
+; Stop early if the middle symbol is not a cherry.
+ inc hl
+ ld a, [hl]
+ cp SLOTSCHERRY >> 8
+ jr nz, .stopWheel
+ ret
+; It looks like this was intended to make the wheel stop when a 7 symbol was
+; visible, but it has a bug and so the wheel stops randomly.
+.sevenAndBarMode
+ ld c, $3
+.loop
+ ld a, [hli]
+ cp SLOTS7 >> 8
+ jr c, .stopWheel ; condition never true
+ dec c
+ jr nz, .loop
+ ret
+.stopWheel
+ inc a
+ ld hl, wSlotMachineWheel1SlipCounter
+ ld [hl], 0
+ ret
+
+SlotMachine_StopWheel2Early:
+ call SlotMachine_GetWheel2Tiles
+ ld a, [wSlotMachineFlags]
+ and $80
+ jr nz, .sevenAndBarMode
+; Stop early if any symbols are lined up in the first two wheels.
+ call SlotMachine_FindWheel1Wheel2Matches
+ ret nz
+ jr .stopWheel
+; Stop early if two 7 symbols or two bar symbols are lined up in the first two
+; wheels OR if no symbols are lined up and the bottom symbol in wheel 2 is a
+; 7 symbol or bar symbol. The second part could be a bug or a way to reduce the
+; player's odds.
+.sevenAndBarMode
+ call SlotMachine_FindWheel1Wheel2Matches
+ ld a, [de]
+ cp (SLOTSBAR >> 8) + 1
+ ret nc
+.stopWheel
+ xor a
+ ld [wSlotMachineWheel2SlipCounter], a
+ ret
+
+SlotMachine_FindWheel1Wheel2Matches:
+; return whether wheel 1 and wheel 2's current positions allow a match (given
+; that wheel 3 stops in a good position) in Z
+ ld hl, wSlotMachineWheel1BottomTile
+ ld de, wSlotMachineWheel2BottomTile
+ ld a, [de]
+ cp [hl] ; wheel 1 bottom, wheel 2 bottom
+ ret z
+ inc de
+ ld a, [de]
+ cp [hl] ; wheel 1 bottom, wheel 2 middle
+ ret z
+ inc hl
+ cp [hl] ; wheel 1 middle, wheel 2 middle
+ ret z
+ inc hl
+ cp [hl] ; wheel 1 top, wheel 2 middle
+ ret z
+ inc de
+ ld a, [de]
+ cp [hl] ; wheel 1 top, wheel 2 top
+ ret z
+ dec de
+ dec de
+ ret
+
+SlotMachine_CheckForMatches:
+ call SlotMachine_GetWheel3Tiles
+ ld a, [wSlotMachineBet]
+ cp 2
+ jr z, .checkMatchesFor2CoinBet
+ cp 1
+ jr z, .checkMatchFor1CoinBet
+; 3 coin bet allows diagonal matches (plus the matches for 1/2 coin bets)
+ ld hl, wSlotMachineWheel1BottomTile
+ ld de, wSlotMachineWheel2MiddleTile
+ ld bc, wSlotMachineWheel3TopTile
+ call SlotMachine_CheckForMatch
+ jp z, .foundMatch
+ ld hl, wSlotMachineWheel1TopTile
+ ld de, wSlotMachineWheel2MiddleTile
+ ld bc, wSlotMachineWheel3BottomTile
+ call SlotMachine_CheckForMatch
+ jr z, .foundMatch
+; 2 coin bet allows top/bottom horizontal matches (plus the match for a 1 coin bet)
+.checkMatchesFor2CoinBet
+ ld hl, wSlotMachineWheel1TopTile
+ ld de, wSlotMachineWheel2TopTile
+ ld bc, wSlotMachineWheel3TopTile
+ call SlotMachine_CheckForMatch
+ jr z, .foundMatch
+ ld hl, wSlotMachineWheel1BottomTile
+ ld de, wSlotMachineWheel2BottomTile
+ ld bc, wSlotMachineWheel3BottomTile
+ call SlotMachine_CheckForMatch
+ jr z, .foundMatch
+; 1 coin bet only allows a middle horizontal match
+.checkMatchFor1CoinBet
+ ld hl, wSlotMachineWheel1MiddleTile
+ ld de, wSlotMachineWheel2MiddleTile
+ ld bc, wSlotMachineWheel3MiddleTile
+ call SlotMachine_CheckForMatch
+ jr z, .foundMatch
+ ld a, [wSlotMachineFlags]
+ and $c0
+ jr z, .noMatch
+ ld hl, wSlotMachineRerollCounter
+ dec [hl]
+ jr nz, .rollWheel3DownByOneSymbol
+.noMatch
+ ld hl, NotThisTimeText
+ call PrintText
+.done
+ xor a
+ ld [wMuteAudioAndPauseMusic], a
+ ret
+.rollWheel3DownByOneSymbol
+ call SlotMachine_AnimWheel3
+ call DelayFrame
+ call SlotMachine_AnimWheel3
+ call DelayFrame
+ jp SlotMachine_CheckForMatches
+.foundMatch
+ ld a, [wSlotMachineFlags]
+ and $c0
+ jr z, .rollWheel3DownByOneSymbol ; roll wheel if player isn't allowed to win
+ and $80
+ jr nz, .acceptMatch
+; if 7/bar matches aren't enabled and the match was a 7/bar symbol, roll wheel
+ ld a, [hl]
+ cp (SLOTSBAR >> 8) + 1
+ jr c, .rollWheel3DownByOneSymbol
+.acceptMatch
+ ld a, [hl]
+ sub $2
+ ld [wSlotMachineWinningSymbol], a
+ ld hl, SlotRewardPointers
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld a, [hli]
+ ld e, a
+ ld a, [hli]
+ ld d, a
+ push de
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld de, wcf4b
+ ld bc, 4
+ call CopyData
+ pop hl
+ ld de, .flashScreenLoop
+ push de
+ jp hl
+
+.flashScreenLoop
+ ld a, [rBGP]
+ xor $40
+ ld [rBGP], a
+ ld c, 5
+ call DelayFrames
+ dec b
+ jr nz, .flashScreenLoop
+ ld hl, wPayoutCoins
+ ld [hl], d
+ inc hl
+ ld [hl], e
+ call SlotMachine_PrintPayoutCoins
+ ld hl, SymbolLinedUpSlotMachineText
+ call PrintText
+ call WaitForTextScrollButtonPress
+ call SlotMachine_PayCoinsToPlayer
+ call SlotMachine_PrintPayoutCoins
+ ld a, $e4
+ ld [rOBP0], a
+ jp .done
+
+SymbolLinedUpSlotMachineText:
+ TX_ASM
+ push bc
+ call SlotMachine_PrintWinningSymbol
+ ld hl, LinedUpText
+ pop bc
+ inc bc
+ inc bc
+ inc bc
+ inc bc
+ ret
+
+LinedUpText:
+ TX_FAR _LinedUpText
+ db "@"
+
+SlotRewardPointers:
+ dw SlotReward300Func
+ dw SlotReward300Text
+ dw SlotReward100Func
+ dw SlotReward100Text
+ dw SlotReward8Func
+ dw SlotReward8Text
+ dw SlotReward15Func
+ dw SlotReward15Text
+ dw SlotReward15Func
+ dw SlotReward15Text
+ dw SlotReward15Func
+ dw SlotReward15Text
+
+SlotReward300Text:
+ db "300@"
+
+SlotReward100Text:
+ db "100@"
+
+SlotReward8Text:
+ db "8@"
+
+SlotReward15Text:
+ db "15@"
+
+NotThisTimeText:
+ TX_FAR _NotThisTimeText
+ db "@"
+
+; compares the slot machine tiles at bc, de, and hl
+SlotMachine_CheckForMatch:
+ ld a, [de]
+ cp [hl]
+ ret nz
+ ld a, [bc]
+ cp [hl]
+ ret
+
+SlotMachine_GetWheel3Tiles:
+ ld de, wSlotMachineWheel3BottomTile
+ ld hl, SlotMachineWheel3
+ ld a, [wSlotMachineWheel3Offset]
+ call SlotMachine_GetWheelTiles
+
+SlotMachine_GetWheel2Tiles:
+ ld de, wSlotMachineWheel2BottomTile
+ ld hl, SlotMachineWheel2
+ ld a, [wSlotMachineWheel2Offset]
+ call SlotMachine_GetWheelTiles
+
+SlotMachine_GetWheel1Tiles:
+ ld de, wSlotMachineWheel1BottomTile
+ ld hl, SlotMachineWheel1
+ ld a, [wSlotMachineWheel1Offset]
+
+SlotMachine_GetWheelTiles:
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld c, 3
+.loop
+ ld a, [hli]
+ ld [de], a
+ inc de
+ inc hl
+ dec c
+ jr nz, .loop
+ ret
+
+SlotReward8Func:
+ ld hl, wSlotMachineAllowMatchesCounter
+ ld a, [hl]
+ and a
+ jr z, .skip
+ dec [hl]
+.skip
+ ld b, $2
+ ld de, 8
+ ret
+
+SlotReward15Func:
+ ld hl, wSlotMachineAllowMatchesCounter
+ ld a, [hl]
+ and a
+ jr z, .skip
+ dec [hl]
+.skip
+ ld b, $4
+ ld de, 15
+ ret
+
+SlotReward100Func:
+ ld a, SFX_GET_KEY_ITEM
+ call PlaySound
+ xor a
+ ld [wSlotMachineFlags], a
+ ld b, $8
+ ld de, 100
+ ret
+
+SlotReward300Func:
+ ld hl, YeahText
+ call PrintText
+ ld a, SFX_GET_ITEM_2
+ call PlaySound
+ call Random
+ cp $80
+ ld a, $0
+ jr c, .skip
+ ld [wSlotMachineFlags], a
+.skip
+ ld [wSlotMachineAllowMatchesCounter], a
+ ld b, $14
+ ld de, 300
+ ret
+
+YeahText:
+ TX_FAR _YeahText
+ TX_DELAY
+ db "@"
+
+SlotMachine_PrintWinningSymbol:
+; prints winning symbol and down arrow in text box
+ coord hl, 2, 14
+ ld a, [wSlotMachineWinningSymbol]
+ add $25
+ ld [hli], a
+ inc a
+ ld [hld], a
+ inc a
+ ld de, -SCREEN_WIDTH
+ add hl, de
+ ld [hli], a
+ inc a
+ ld [hl], a
+ coord hl, 18, 16
+ ld [hl], "▼"
+ ret
+
+SlotMachine_SubtractBetFromPlayerCoins:
+ ld hl, wTempCoins2 + 1
+ ld a, [wSlotMachineBet]
+ ld [hld], a
+ xor a
+ ld [hli], a
+ ld de, wPlayerCoins + 1
+ ld c, $2
+ predef SubBCDPredef
+
+SlotMachine_PrintCreditCoins:
+ coord hl, 5, 1
+ ld de, wPlayerCoins
+ ld c, $2
+ jp PrintBCDNumber
+
+SlotMachine_PrintPayoutCoins:
+ coord hl, 11, 1
+ ld de, wPayoutCoins
+ lb bc, LEADING_ZEROES | 2, 4 ; 2 bytes, 4 digits
+ jp PrintNumber
+
+SlotMachine_PayCoinsToPlayer:
+ ld a, $1
+ ld [wMuteAudioAndPauseMusic], a
+ call WaitForSoundToFinish
+
+; Put 1 in the temp coins variable. This value is added to the player's coins
+; repeatedly so the player can watch the value go up 1 coin at a time.
+ ld hl, wTempCoins1
+ xor a
+ ld [hli], a
+ inc a
+ ld [hl], a
+
+ ld a, 5
+ ld [wAnimCounter], a
+
+; Subtract 1 from the payout amount and add 1 to the player's coins each
+; iteration until the payout amount reaches 0.
+.loop
+ ld a, [wPayoutCoins + 1]
+ ld l, a
+ ld a, [wPayoutCoins]
+ ld h, a
+ or l
+ ret z
+ ld de, -1
+ add hl, de
+ ld a, l
+ ld [wPayoutCoins + 1], a
+ ld a, h
+ ld [wPayoutCoins], a
+ ld hl, wTempCoins1 + 1
+ ld de, wPlayerCoins + 1
+ ld c, $2
+ predef AddBCDPredef
+ call SlotMachine_PrintCreditCoins
+ call SlotMachine_PrintPayoutCoins
+ ld a, SFX_SLOTS_REWARD
+ call PlaySound
+ ld a, [wAnimCounter]
+ dec a
+ jr nz, .skip1
+ ld a, [rOBP0]
+ xor $40 ; make the slot wheel symbols flash
+ ld [rOBP0], a
+ ld a, 5
+.skip1
+ ld [wAnimCounter], a
+ ld a, [wSlotMachineWinningSymbol]
+ cp (SLOTSBAR >> 8) + 1
+ ld c, 8
+ jr nc, .skip2
+ srl c ; c = 4 (make the the coins transfer faster if the symbol was 7 or bar)
+.skip2
+ call DelayFrames
+ jr .loop
+
+SlotMachine_PutOutLitBalls:
+ ld a, $23
+ ld [wNewSlotMachineBallTile], a
+ jr SlotMachine_UpdateThreeCoinBallTiles
+
+SlotMachine_LightBalls:
+ ld a, $14
+ ld [wNewSlotMachineBallTile], a
+ ld a, [wSlotMachineBet]
+ dec a
+ jr z, SlotMachine_UpdateOneCoinBallTiles
+ dec a
+ jr z, SlotMachine_UpdateTwoCoinBallTiles
+
+SlotMachine_UpdateThreeCoinBallTiles:
+ coord hl, 3, 2
+ call SlotMachine_UpdateBallTiles
+ coord hl, 3, 10
+ call SlotMachine_UpdateBallTiles
+
+SlotMachine_UpdateTwoCoinBallTiles:
+ coord hl, 3, 4
+ call SlotMachine_UpdateBallTiles
+ coord hl, 3, 8
+ call SlotMachine_UpdateBallTiles
+
+SlotMachine_UpdateOneCoinBallTiles:
+ coord hl, 3, 6
+
+SlotMachine_UpdateBallTiles:
+ ld a, [wNewSlotMachineBallTile]
+ ld [hl], a
+ ld bc, 13
+ add hl, bc
+ ld [hl], a
+ ld bc, 7
+ add hl, bc
+ inc a
+ ld [hl], a
+ ld bc, 13
+ add hl, bc
+ ld [hl], a
+ ret
+
+SlotMachine_AnimWheel1:
+ ld bc, SlotMachineWheel1
+ ld de, wSlotMachineWheel1Offset
+ ld hl, wOAMBuffer
+ ld a, $30
+ ld [wBaseCoordX], a
+ jr SlotMachine_AnimWheel
+
+SlotMachine_AnimWheel2:
+ ld bc, SlotMachineWheel2
+ ld de, wSlotMachineWheel2Offset
+ ld hl, wOAMBuffer + $30
+ ld a, $50
+ ld [wBaseCoordX], a
+ jr SlotMachine_AnimWheel
+
+SlotMachine_AnimWheel3:
+ ld bc, SlotMachineWheel3
+ ld de, wSlotMachineWheel3Offset
+ ld hl, wOAMBuffer + $60
+ ld a, $70
+ ld [wBaseCoordX], a
+
+SlotMachine_AnimWheel:
+ ld a, $58
+ ld [wBaseCoordY], a
+ push de
+ ld a, [de]
+ ld d, b
+ add c
+ ld e, a
+ jr nc, .loop
+ inc d
+.loop
+ ld a, [wBaseCoordY]
+ ld [hli], a
+ ld a, [wBaseCoordX]
+ ld [hli], a
+ ld a, [de]
+ ld [hli], a
+ ld a, $80
+ ld [hli], a
+ ld a, [wBaseCoordY]
+ ld [hli], a
+ ld a, [wBaseCoordX]
+ add $8
+ ld [hli], a
+ ld a, [de]
+ inc a
+ ld [hli], a
+ ld a, $80
+ ld [hli], a
+ inc de
+ ld a, [wBaseCoordY]
+ sub $8
+ ld [wBaseCoordY], a
+ cp $28
+ jr nz, .loop
+ pop de
+ ld a, [de]
+ inc a ; advance the offset so that the wheel animates
+ cp 30
+ jr nz, .skip
+ xor a ; wrap around to 0 when the offset reaches 30
+.skip
+ ld [de], a
+ ret
+
+SlotMachine_HandleInputWhileWheelsSpin:
+ call DelayFrame
+ call JoypadLowSensitivity
+ ld a, [hJoy5]
+ and A_BUTTON
+ ret z
+ ld hl, wStoppingWhichSlotMachineWheel
+ ld a, [hl]
+ dec a
+ ld de, wSlotMachineWheel1SlipCounter
+ jr z, .skip
+ dec a
+ ld de, wSlotMachineWheel2SlipCounter
+ jr z, .skip
+.loop
+ inc [hl]
+ ld a, SFX_SLOTS_STOP_WHEEL
+ jp PlaySound
+.skip
+ ld a, [de]
+ and a
+ ret nz
+ jr .loop
+
+LoadSlotMachineTiles:
+ call DisableLCD
+ ld hl, SlotMachineTiles2
+ ld de, vChars0
+ ld bc, $1c0
+ ld a, BANK(SlotMachineTiles2)
+ call FarCopyData2
+ ld hl, SlotMachineTiles1
+ ld de, vChars2
+ ld bc, $250
+ ld a, BANK(SlotMachineTiles1)
+ call FarCopyData2
+ ld hl, SlotMachineTiles2
+ ld de, vChars2 + $250
+ ld bc, $1c0
+ ld a, BANK(SlotMachineTiles2)
+ call FarCopyData2
+ ld hl, SlotMachineMap
+ coord de, 0, 0
+ ld bc, SlotMachineMapEnd - SlotMachineMap
+ call CopyData
+ call EnableLCD
+ ld hl, wSlotMachineWheel1Offset
+ ld a, $1c
+ ld [hli], a
+ ld [hli], a
+ ld [hl], a
+ call SlotMachine_AnimWheel1
+ call SlotMachine_AnimWheel2
+ jp SlotMachine_AnimWheel3
+
+SlotMachineMap:
+ INCBIN "gfx/tilemaps/slotmachine.map"
+SlotMachineMapEnd:
+
+INCLUDE "data/slot_machine_wheels.asm"
+
+SlotMachineTiles1:
+IF DEF(_RED)
+ binlang gfx/red/slotmachine1.2bpp
+ENDC
+IF DEF(_BLUE)
+ binlang gfx/blue/slotmachine1.2bpp
+ENDC
diff --git a/en/engine/status_ailments.asm b/en/engine/status_ailments.asm
new file mode 100755
index 00000000..787533c4
--- /dev/null
+++ b/en/engine/status_ailments.asm
@@ -0,0 +1,46 @@
+PrintStatusAilment:
+ ld a, [de]
+ bit PSN, a
+ jr nz, .psn
+ bit BRN, a
+ jr nz, .brn
+ bit FRZ, a
+ jr nz, .frz
+ bit PAR, a
+ jr nz, .par
+ and SLP
+ ret z
+ ld a, "S"
+ ld [hli], a
+ ld a, "L"
+ ld [hli], a
+ ld [hl], "P"
+ ret
+.psn
+ ld a, "P"
+ ld [hli], a
+ ld a, "S"
+ ld [hli], a
+ ld [hl], "N"
+ ret
+.brn
+ ld a, "B"
+ ld [hli], a
+ ld a, "R"
+ ld [hli], a
+ ld [hl], "N"
+ ret
+.frz
+ ld a, "F"
+ ld [hli], a
+ ld a, "R"
+ ld [hli], a
+ ld [hl], "Z"
+ ret
+.par
+ ld a, "P"
+ ld [hli], a
+ ld a, "A"
+ ld [hli], a
+ ld [hl], "R"
+ ret
diff --git a/en/engine/titlescreen.asm b/en/engine/titlescreen.asm
new file mode 100755
index 00000000..03a5832d
--- /dev/null
+++ b/en/engine/titlescreen.asm
@@ -0,0 +1,403 @@
+; copy text of fixed length NAME_LENGTH (like player name, rival name, mon names, ...)
+CopyFixedLengthText:
+ ld bc, NAME_LENGTH
+ jp CopyData
+
+SetDefaultNamesBeforeTitlescreen:
+ ld hl, NintenText
+ ld de, wPlayerName
+ call CopyFixedLengthText
+ ld hl, SonyText
+ ld de, wRivalName
+ call CopyFixedLengthText
+ xor a
+ ld [hWY], a
+ ld [wLetterPrintingDelayFlags], a
+ ld hl, wd732
+ ld [hli], a
+ ld [hli], a
+ ld [hl], a
+ ld a, BANK(Music_TitleScreen)
+ ld [wAudioROMBank], a
+ ld [wAudioSavedROMBank], a
+
+DisplayTitleScreen:
+ call GBPalWhiteOut
+ ld a, $1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ xor a
+ ld [hTilesetType], a
+ ld [hSCX], a
+ ld a, $40
+ ld [hSCY], a
+ ld a, $90
+ ld [hWY], a
+ call ClearScreen
+ call DisableLCD
+ call LoadFontTilePatterns
+ ld hl, NintendoCopyrightLogoGraphics
+ ld de, vTitleLogo2 + $100
+ ld bc, $50
+ ld a, BANK(NintendoCopyrightLogoGraphics)
+ call FarCopyData2
+ ld hl, GamefreakLogoGraphics
+ ld de, vTitleLogo2 + $100 + $50
+ ld bc, $90
+ ld a, BANK(GamefreakLogoGraphics)
+ call FarCopyData2
+ ld hl, PokemonLogoGraphics
+ ld de, vTitleLogo
+ ld bc, $600
+ ld a, BANK(PokemonLogoGraphics)
+ call FarCopyData2 ; first chunk
+ ld hl, PokemonLogoGraphics+$600
+ ld de, vTitleLogo2
+ ld bc, $100
+ ld a, BANK(PokemonLogoGraphics)
+ call FarCopyData2 ; second chunk
+ ld hl, Version_GFX
+ ld de, vChars2 + $600 - (Version_GFXEnd - Version_GFX - $50)
+ ld bc, Version_GFXEnd - Version_GFX
+ ld a, BANK(Version_GFX)
+ call FarCopyDataDouble
+ call ClearBothBGMaps
+
+; place tiles for pokemon logo (except for the last row)
+ coord hl, 2, 1
+ ld a, $80
+ ld de, SCREEN_WIDTH
+ ld c, 6
+.pokemonLogoTileLoop
+ ld b, $10
+ push hl
+.pokemonLogoTileRowLoop ; place tiles for one row
+ ld [hli], a
+ inc a
+ dec b
+ jr nz, .pokemonLogoTileRowLoop
+ pop hl
+ add hl, de
+ dec c
+ jr nz, .pokemonLogoTileLoop
+
+; place tiles for the last row of the pokemon logo
+ coord hl, 2, 7
+ ld a, $31
+ ld b, $10
+.pokemonLogoLastTileRowLoop
+ ld [hli], a
+ inc a
+ dec b
+ jr nz, .pokemonLogoLastTileRowLoop
+
+ call DrawPlayerCharacter
+
+; put a pokeball in the player's hand
+ ld hl, wOAMBuffer + $28
+ ld a, $74
+ ld [hl], a
+
+; place tiles for title screen copyright
+ coord hl, 2, 17
+ ld de, .tileScreenCopyrightTiles
+ ld b, $10
+.tileScreenCopyrightTilesLoop
+ ld a, [de]
+ ld [hli], a
+ inc de
+ dec b
+ jr nz, .tileScreenCopyrightTilesLoop
+
+ jr .next
+
+.tileScreenCopyrightTiles
+ db $41,$42,$43,$42,$44,$42,$45,$46,$47,$48,$49,$4A,$4B,$4C,$4D,$4E ; ©'95.'96.'98 GAME FREAK inc.
+
+.next
+ call SaveScreenTilesToBuffer2
+ call LoadScreenTilesFromBuffer2
+ call EnableLCD
+IF DEF(_RED)
+ ld a, CHARMANDER ; which Pokemon to show first on the title screen
+ENDC
+IF DEF(_BLUE)
+ ld a, SQUIRTLE ; which Pokemon to show first on the title screen
+ENDC
+
+ ld [wTitleMonSpecies], a
+ call LoadTitleMonSprite
+ ld a, (vBGMap0 + $300) / $100
+ call TitleScreenCopyTileMapToVRAM
+ call SaveScreenTilesToBuffer1
+ ld a, $40
+ ld [hWY], a
+ call LoadScreenTilesFromBuffer2
+ ld a, vBGMap0 / $100
+ call TitleScreenCopyTileMapToVRAM
+ ld b, SET_PAL_TITLE_SCREEN
+ call RunPaletteCommand
+ call GBPalNormal
+ ld a, %11100100
+ ld [rOBP0], a
+
+; make pokemon logo bounce up and down
+ ld bc, hSCY ; background scroll Y
+ ld hl, .TitleScreenPokemonLogoYScrolls
+.bouncePokemonLogoLoop
+ ld a, [hli]
+ and a
+ jr z, .finishedBouncingPokemonLogo
+ ld d, a
+ cp -3
+ jr nz, .skipPlayingSound
+ ld a, SFX_INTRO_CRASH
+ call PlaySound
+.skipPlayingSound
+ ld a, [hli]
+ ld e, a
+ call .ScrollTitleScreenPokemonLogo
+ jr .bouncePokemonLogoLoop
+
+.TitleScreenPokemonLogoYScrolls:
+; Controls the bouncing effect of the Pokemon logo on the title screen
+ db -4,16 ; y scroll amount, number of times to scroll
+ db 3,4
+ db -3,4
+ db 2,2
+ db -2,2
+ db 1,2
+ db -1,2
+ db 0 ; terminate list with 0
+
+.ScrollTitleScreenPokemonLogo:
+; Scrolls the Pokemon logo on the title screen to create the bouncing effect
+; Scrolls d pixels e times
+ call DelayFrame
+ ld a, [bc] ; background scroll Y
+ add d
+ ld [bc], a
+ dec e
+ jr nz, .ScrollTitleScreenPokemonLogo
+ ret
+
+.finishedBouncingPokemonLogo
+ call LoadScreenTilesFromBuffer1
+ ld c, 36
+ call DelayFrames
+ ld a, SFX_INTRO_WHOOSH
+ call PlaySound
+
+; scroll game version in from the right
+ call PrintGameVersionOnTitleScreen
+ ld a, SCREEN_HEIGHT_PIXELS
+ ld [hWY], a
+ ld d, 144
+.scrollTitleScreenGameVersionLoop
+ ld h, d
+ ld l, 64
+ call ScrollTitleScreenGameVersion
+ ld h, 0
+ ld l, 80
+ call ScrollTitleScreenGameVersion
+ ld a, d
+ add 4
+ ld d, a
+ and a
+ jr nz, .scrollTitleScreenGameVersionLoop
+
+ ld a, vBGMap1 / $100
+ call TitleScreenCopyTileMapToVRAM
+ call LoadScreenTilesFromBuffer2
+ call PrintGameVersionOnTitleScreen
+ call Delay3
+ call WaitForSoundToFinish
+ ld a, MUSIC_TITLE_SCREEN
+ ld [wNewSoundID], a
+ call PlaySound
+ xor a
+ ld [wUnusedCC5B], a
+
+; Keep scrolling in new mons indefinitely until the user performs input.
+.awaitUserInterruptionLoop
+ ld c, 200
+ call CheckForUserInterruption
+ jr c, .finishedWaiting
+ call TitleScreenScrollInMon
+ ld c, 1
+ call CheckForUserInterruption
+ jr c, .finishedWaiting
+ callba TitleScreenAnimateBallIfStarterOut
+ call TitleScreenPickNewMon
+ jr .awaitUserInterruptionLoop
+
+.finishedWaiting
+ ld a, [wTitleMonSpecies]
+ call PlayCry
+ call WaitForSoundToFinish
+ call GBPalWhiteOutWithDelay3
+ call ClearSprites
+ xor a
+ ld [hWY], a
+ inc a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call ClearScreen
+ ld a, vBGMap0 / $100
+ call TitleScreenCopyTileMapToVRAM
+ ld a, vBGMap1 / $100
+ call TitleScreenCopyTileMapToVRAM
+ call Delay3
+ call LoadGBPal
+ ld a, [hJoyHeld]
+ ld b, a
+ and D_UP | SELECT | B_BUTTON
+ cp D_UP | SELECT | B_BUTTON
+ jp z, .doClearSaveDialogue
+ jp MainMenu
+
+.doClearSaveDialogue
+ jpba DoClearSaveDialogue
+
+TitleScreenPickNewMon:
+ ld a, vBGMap0 / $100
+ call TitleScreenCopyTileMapToVRAM
+
+.loop
+; Keep looping until a mon different from the current one is picked.
+ call Random
+ and $f
+ ld c, a
+ ld b, 0
+ ld hl, TitleMons
+ add hl, bc
+ ld a, [hl]
+ ld hl, wTitleMonSpecies
+
+; Can't be the same as before.
+ cp [hl]
+ jr z, .loop
+
+ ld [hl], a
+ call LoadTitleMonSprite
+
+ ld a, $90
+ ld [hWY], a
+ ld d, 1 ; scroll out
+ callba TitleScroll
+ ret
+
+TitleScreenScrollInMon:
+ ld d, 0 ; scroll in
+ callba TitleScroll
+ xor a
+ ld [hWY], a
+ ret
+
+ScrollTitleScreenGameVersion:
+.wait
+ ld a, [rLY]
+ cp l
+ jr nz, .wait
+
+ ld a, h
+ ld [rSCX], a
+
+.wait2
+ ld a, [rLY]
+ cp h
+ jr z, .wait2
+ ret
+
+DrawPlayerCharacter:
+ ld hl, PlayerCharacterTitleGraphics
+ ld de, vSprites
+ ld bc, PlayerCharacterTitleGraphicsEnd - PlayerCharacterTitleGraphics
+ ld a, BANK(PlayerCharacterTitleGraphics)
+ call FarCopyData2
+ call ClearSprites
+ xor a
+ ld [wPlayerCharacterOAMTile], a
+ ld hl, wOAMBuffer
+ ld de, $605a
+ ld b, 7
+.loop
+ push de
+ ld c, 5
+.innerLoop
+ ld a, d
+ ld [hli], a ; Y
+ ld a, e
+ ld [hli], a ; X
+ add 8
+ ld e, a
+ ld a, [wPlayerCharacterOAMTile]
+ ld [hli], a ; tile
+ inc a
+ ld [wPlayerCharacterOAMTile], a
+ inc hl
+ dec c
+ jr nz, .innerLoop
+ pop de
+ ld a, 8
+ add d
+ ld d, a
+ dec b
+ jr nz, .loop
+ ret
+
+ClearBothBGMaps:
+ ld hl, vBGMap0
+ ld bc, $400 * 2
+ ld a, " "
+ jp FillMemory
+
+LoadTitleMonSprite:
+ ld [wcf91], a
+ ld [wd0b5], a
+ coord hl, 5, 10
+ call GetMonHeader
+ jp LoadFrontSpriteByMonIndex
+
+TitleScreenCopyTileMapToVRAM:
+ ld [H_AUTOBGTRANSFERDEST + 1], a
+ jp Delay3
+
+LoadCopyrightAndTextBoxTiles:
+ xor a
+ ld [hWY], a
+ call ClearScreen
+ call LoadTextBoxTilePatterns
+
+LoadCopyrightTiles:
+ ld de, NintendoCopyrightLogoGraphics
+ ld hl, vChars2 + $600
+ lb bc, BANK(NintendoCopyrightLogoGraphics), (GamefreakLogoGraphicsEnd - NintendoCopyrightLogoGraphics) / $10
+ call CopyVideoData
+ coord hl, 2, 7
+ ld de, CopyrightTextString
+ jp PlaceString
+
+CopyrightTextString:
+ db $60,$61,$62,$61,$63,$61,$64,$7F,$65,$66,$67,$68,$69,$6A ; ©'95.'96.'98 Nintendo
+ next $60,$61,$62,$61,$63,$61,$64,$7F,$6B,$6C,$6D,$6E,$6F,$70,$71,$72 ; ©'95.'96.'98 Creatures inc.
+ next $60,$61,$62,$61,$63,$61,$64,$7F,$73,$74,$75,$76,$77,$78,$79,$7A,$7B ; ©'95.'96.'98 GAME FREAK inc.
+ db "@"
+
+INCLUDE "data/title_mons.asm"
+
+; prints version text (red, blue)
+PrintGameVersionOnTitleScreen:
+ coord hl, 7, 8
+ ld de, VersionOnTitleScreenText
+ jp PlaceString
+
+; these point to special tiles specifically loaded for that purpose and are not usual text
+VersionOnTitleScreenText:
+IF DEF(_RED)
+ db $60,$61,$7F,$65,$66,$67,$68,$69,"@" ; "Red Version"
+ENDC
+IF DEF(_BLUE)
+ db $61,$62,$63,$64,$65,$66,$67,$68,"@" ; "Blue Version"
+ENDC
+
+NintenText: db "NINTEN@"
+SonyText: db "SONY@"
diff --git a/en/engine/town_map.asm b/en/engine/town_map.asm
new file mode 100755
index 00000000..62fba431
--- /dev/null
+++ b/en/engine/town_map.asm
@@ -0,0 +1,619 @@
+DisplayTownMap:
+ call LoadTownMap
+ ld hl, wUpdateSpritesEnabled
+ ld a, [hl]
+ push af
+ ld [hl], $ff
+ push hl
+ ld a, $1
+ ld [hJoy7], a
+ ld a, [wCurMap]
+ push af
+ ld b, $0
+ call DrawPlayerOrBirdSprite ; player sprite
+ coord hl, 1, 0
+ ld de, wcd6d
+ call PlaceString
+ ld hl, wOAMBuffer
+ ld de, wTileMapBackup
+ ld bc, $10
+ call CopyData
+ ld hl, vSprites + $40
+ ld de, TownMapCursor
+ lb bc, BANK(TownMapCursor), (TownMapCursorEnd - TownMapCursor) / $8
+ call CopyVideoDataDouble
+ xor a
+ ld [wWhichTownMapLocation], a
+ pop af
+ jr .enterLoop
+
+.townMapLoop
+ coord hl, 0, 0
+ lb bc, 1, 20
+ call ClearScreenArea
+ ld hl, TownMapOrder
+ ld a, [wWhichTownMapLocation]
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld a, [hl]
+.enterLoop
+ ld de, wTownMapCoords
+ call LoadTownMapEntry
+ ld a, [de]
+ push hl
+ call TownMapCoordsToOAMCoords
+ ld a, $4
+ ld [wOAMBaseTile], a
+ ld hl, wOAMBuffer + $10
+ call WriteTownMapSpriteOAM ; town map cursor sprite
+ pop hl
+ ld de, wcd6d
+.copyMapName
+ ld a, [hli]
+ ld [de], a
+ inc de
+ cp $50
+ jr nz, .copyMapName
+ coord hl, 1, 0
+ ld de, wcd6d
+ call PlaceString
+ ld hl, wOAMBuffer + $10
+ ld de, wTileMapBackup + 16
+ ld bc, $10
+ call CopyData
+.inputLoop
+ call TownMapSpriteBlinkingAnimation
+ call JoypadLowSensitivity
+ ld a, [hJoy5]
+ ld b, a
+ and A_BUTTON | B_BUTTON | D_UP | D_DOWN
+ jr z, .inputLoop
+ ld a, SFX_TINK
+ call PlaySound
+ bit 6, b
+ jr nz, .pressedUp
+ bit 7, b
+ jr nz, .pressedDown
+ xor a
+ ld [wTownMapSpriteBlinkingEnabled], a
+ ld [hJoy7], a
+ ld [wAnimCounter], a
+ call ExitTownMap
+ pop hl
+ pop af
+ ld [hl], a
+ ret
+.pressedUp
+ ld a, [wWhichTownMapLocation]
+ inc a
+ cp TownMapOrderEnd - TownMapOrder ; number of list items + 1
+ jr nz, .noOverflow
+ xor a
+.noOverflow
+ ld [wWhichTownMapLocation], a
+ jp .townMapLoop
+.pressedDown
+ ld a, [wWhichTownMapLocation]
+ dec a
+ cp -1
+ jr nz, .noUnderflow
+ ld a, TownMapOrderEnd - TownMapOrder - 1 ; number of list items
+.noUnderflow
+ ld [wWhichTownMapLocation], a
+ jp .townMapLoop
+
+INCLUDE "data/town_map_order.asm"
+
+TownMapCursor:
+ INCBIN "gfx/town_map_cursor.1bpp"
+TownMapCursorEnd:
+
+LoadTownMap_Nest:
+ call LoadTownMap
+ ld hl, wUpdateSpritesEnabled
+ ld a, [hl]
+ push af
+ ld [hl], $ff
+ push hl
+ call DisplayWildLocations
+ call GetMonName
+ coord hl, 1, 0
+ call PlaceString
+ ld h, b
+ ld l, c
+ ld de, MonsNestText
+ call PlaceString
+ call WaitForTextScrollButtonPress
+ call ExitTownMap
+ pop hl
+ pop af
+ ld [hl], a
+ ret
+
+MonsNestText:
+ db "'s NEST@"
+
+LoadTownMap_Fly:
+ call ClearSprites
+ call LoadTownMap
+ call LoadPlayerSpriteGraphics
+ call LoadFontTilePatterns
+ ld de, BirdSprite
+ ld hl, vSprites + $40
+ lb bc, BANK(BirdSprite), $c
+ call CopyVideoData
+ ld de, TownMapUpArrow
+ ld hl, vChars1 + $6d0
+ lb bc, BANK(TownMapUpArrow), (TownMapUpArrowEnd - TownMapUpArrow) / $8
+ call CopyVideoDataDouble
+ call BuildFlyLocationsList
+ ld hl, wUpdateSpritesEnabled
+ ld a, [hl]
+ push af
+ ld [hl], $ff
+ push hl
+ coord hl, 0, 0
+ ld de, ToText
+ call PlaceString
+ ld a, [wCurMap]
+ ld b, $0
+ call DrawPlayerOrBirdSprite
+ ld hl, wFlyLocationsList
+ coord de, 18, 0
+.townMapFlyLoop
+ ld a, " "
+ ld [de], a
+ push hl
+ push hl
+ coord hl, 3, 0
+ lb bc, 1, 15
+ call ClearScreenArea
+ pop hl
+ ld a, [hl]
+ ld b, $4
+ call DrawPlayerOrBirdSprite ; draw bird sprite
+ coord hl, 3, 0
+ ld de, wcd6d
+ call PlaceString
+ ld c, 15
+ call DelayFrames
+ coord hl, 18, 0
+ ld [hl], "▲"
+ coord hl, 19, 0
+ ld [hl], "▼"
+ pop hl
+.inputLoop
+ push hl
+ call DelayFrame
+ call JoypadLowSensitivity
+ ld a, [hJoy5]
+ ld b, a
+ pop hl
+ and A_BUTTON | B_BUTTON | D_UP | D_DOWN
+ jr z, .inputLoop
+ bit 0, b
+ jr nz, .pressedA
+ ld a, SFX_TINK
+ call PlaySound
+ bit 6, b
+ jr nz, .pressedUp
+ bit 7, b
+ jr nz, .pressedDown
+ jr .pressedB
+.pressedA
+ ld a, SFX_HEAL_AILMENT
+ call PlaySound
+ ld a, [hl]
+ ld [wDestinationMap], a
+ ld hl, wd732
+ set 3, [hl]
+ inc hl
+ set 7, [hl]
+.pressedB
+ xor a
+ ld [wTownMapSpriteBlinkingEnabled], a
+ call GBPalWhiteOutWithDelay3
+ pop hl
+ pop af
+ ld [hl], a
+ ret
+.pressedUp
+ coord de, 18, 0
+ inc hl
+ ld a, [hl]
+ cp $ff
+ jr z, .wrapToStartOfList
+ cp $fe
+ jr z, .pressedUp ; skip past unvisited towns
+ jp .townMapFlyLoop
+.wrapToStartOfList
+ ld hl, wFlyLocationsList
+ jp .townMapFlyLoop
+.pressedDown
+ coord de, 19, 0
+ dec hl
+ ld a, [hl]
+ cp $ff
+ jr z, .wrapToEndOfList
+ cp $fe
+ jr z, .pressedDown ; skip past unvisited towns
+ jp .townMapFlyLoop
+.wrapToEndOfList
+ ld hl, wFlyLocationsList + 11
+ jr .pressedDown
+
+ToText:
+ db "To@"
+
+BuildFlyLocationsList:
+ ld hl, wFlyLocationsList - 1
+ ld [hl], $ff
+ inc hl
+ ld a, [wTownVisitedFlag]
+ ld e, a
+ ld a, [wTownVisitedFlag + 1]
+ ld d, a
+ ld bc, SAFFRON_CITY + 1
+.loop
+ srl d
+ rr e
+ ld a, $fe ; store $fe if the town hasn't been visited
+ jr nc, .notVisited
+ ld a, b ; store the map number of the town if it has been visited
+.notVisited
+ ld [hl], a
+ inc hl
+ inc b
+ dec c
+ jr nz, .loop
+ ld [hl], $ff
+ ret
+
+TownMapUpArrow:
+ INCBIN "gfx/up_arrow.1bpp"
+TownMapUpArrowEnd:
+
+LoadTownMap:
+ call GBPalWhiteOutWithDelay3
+ call ClearScreen
+ call UpdateSprites
+ coord hl, 0, 0
+ ld b, $12
+ ld c, $12
+ call TextBoxBorder
+ call DisableLCD
+ ld hl, WorldMapTileGraphics
+ ld de, vChars2 + $600
+ ld bc, WorldMapTileGraphicsEnd - WorldMapTileGraphics
+ ld a, BANK(WorldMapTileGraphics)
+ call FarCopyData2
+ ld hl, MonNestIcon
+ ld de, vSprites + $40
+ ld bc, MonNestIconEnd - MonNestIcon
+ ld a, BANK(MonNestIcon)
+ call FarCopyDataDouble
+ coord hl, 0, 0
+ ld de, CompressedMap
+.nextTile
+ ld a, [de]
+ and a
+ jr z, .done
+ ld b, a
+ and $f
+ ld c, a
+ ld a, b
+ swap a
+ and $f
+ add $60
+.writeRunLoop
+ ld [hli], a
+ dec c
+ jr nz, .writeRunLoop
+ inc de
+ jr .nextTile
+.done
+ call EnableLCD
+ ld b, SET_PAL_TOWN_MAP
+ call RunPaletteCommand
+ call Delay3
+ call GBPalNormal
+ xor a
+ ld [wAnimCounter], a
+ inc a
+ ld [wTownMapSpriteBlinkingEnabled], a
+ ret
+
+CompressedMap:
+; you can decompress this file with the redrle program in the extras/ dir
+ INCBIN "gfx/town_map.rle"
+
+ExitTownMap:
+; clear town map graphics data and load usual graphics data
+ xor a
+ ld [wTownMapSpriteBlinkingEnabled], a
+ call GBPalWhiteOut
+ call ClearScreen
+ call ClearSprites
+ call LoadPlayerSpriteGraphics
+ call LoadFontTilePatterns
+ call UpdateSprites
+ jp RunDefaultPaletteCommand
+
+DrawPlayerOrBirdSprite:
+; a = map number
+; b = OAM base tile
+ push af
+ ld a, b
+ ld [wOAMBaseTile], a
+ pop af
+ ld de, wTownMapCoords
+ call LoadTownMapEntry
+ ld a, [de]
+ push hl
+ call TownMapCoordsToOAMCoords
+ call WritePlayerOrBirdSpriteOAM
+ pop hl
+ ld de, wcd6d
+.loop
+ ld a, [hli]
+ ld [de], a
+ inc de
+ cp "@"
+ jr nz, .loop
+ ld hl, wOAMBuffer
+ ld de, wTileMapBackup
+ ld bc, $a0
+ jp CopyData
+
+DisplayWildLocations:
+ callba FindWildLocationsOfMon
+ call ZeroOutDuplicatesInList
+ ld hl, wOAMBuffer
+ ld de, wTownMapCoords
+.loop
+ ld a, [de]
+ cp $ff
+ jr z, .exitLoop
+ and a
+ jr z, .nextEntry
+ push hl
+ call LoadTownMapEntry
+ pop hl
+ ld a, [de]
+ cp $19 ; Cerulean Cave's coordinates
+ jr z, .nextEntry ; skip Cerulean Cave
+ call TownMapCoordsToOAMCoords
+ ld a, $4 ; nest icon tile no.
+ ld [hli], a
+ xor a
+ ld [hli], a
+.nextEntry
+ inc de
+ jr .loop
+.exitLoop
+ ld a, l
+ and a ; were any OAM entries written?
+ jr nz, .drawPlayerSprite
+; if no OAM entries were written, print area unknown text
+ coord hl, 1, 7
+ ld b, 2
+ ld c, 15
+ call TextBoxBorder
+ coord hl, 2, 9
+ ld de, AreaUnknownText
+ call PlaceString
+ jr .done
+.drawPlayerSprite
+ ld a, [wCurMap]
+ ld b, $0
+ call DrawPlayerOrBirdSprite
+.done
+ ld hl, wOAMBuffer
+ ld de, wTileMapBackup
+ ld bc, $a0
+ jp CopyData
+
+AreaUnknownText:
+ db " AREA UNKNOWN@"
+
+TownMapCoordsToOAMCoords:
+; in: lower nybble of a = x, upper nybble of a = y
+; out: b and [hl] = (y * 8) + 24, c and [hl+1] = (x * 8) + 24
+ push af
+ and $f0
+ srl a
+ add 24
+ ld b, a
+ ld [hli], a
+ pop af
+ and $f
+ swap a
+ srl a
+ add 24
+ ld c, a
+ ld [hli], a
+ ret
+
+WritePlayerOrBirdSpriteOAM:
+ ld a, [wOAMBaseTile]
+ and a
+ ld hl, wOAMBuffer + $90 ; for player sprite
+ jr z, WriteTownMapSpriteOAM
+ ld hl, wOAMBuffer + $80 ; for bird sprite
+
+WriteTownMapSpriteOAM:
+ push hl
+
+; Subtract 4 from c (X coord) and 4 from b (Y coord). However, the carry from c
+; is added to b, so the net result is that only 3 is subtracted from b.
+ lb hl, -4, -4
+ add hl, bc
+
+ ld b, h
+ ld c, l
+ pop hl
+
+WriteAsymmetricMonPartySpriteOAM:
+; Writes 4 OAM blocks for a helix mon party sprite, since it does not have
+; a vertical line of symmetry.
+ lb de, 2, 2
+.loop
+ push de
+ push bc
+.innerLoop
+ ld a, b
+ ld [hli], a
+ ld a, c
+ ld [hli], a
+ ld a, [wOAMBaseTile]
+ ld [hli], a
+ inc a
+ ld [wOAMBaseTile], a
+ xor a
+ ld [hli], a
+ inc d
+ ld a, 8
+ add c
+ ld c, a
+ dec e
+ jr nz, .innerLoop
+ pop bc
+ pop de
+ ld a, 8
+ add b
+ ld b, a
+ dec d
+ jr nz, .loop
+ ret
+
+WriteSymmetricMonPartySpriteOAM:
+; Writes 4 OAM blocks for a mon party sprite other than a helix. All the
+; sprites other than the helix one have a vertical line of symmetry which allows
+; the X-flip OAM bit to be used so that only 2 rather than 4 tile patterns are
+; needed.
+ xor a
+ ld [wSymmetricSpriteOAMAttributes], a
+ lb de, 2, 2
+.loop
+ push de
+ push bc
+.innerLoop
+ ld a, b
+ ld [hli], a ; Y
+ ld a, c
+ ld [hli], a ; X
+ ld a, [wOAMBaseTile]
+ ld [hli], a ; tile
+ ld a, [wSymmetricSpriteOAMAttributes]
+ ld [hli], a ; attributes
+ xor (1 << OAM_X_FLIP)
+ ld [wSymmetricSpriteOAMAttributes], a
+ inc d
+ ld a, 8
+ add c
+ ld c, a
+ dec e
+ jr nz, .innerLoop
+ pop bc
+ pop de
+ push hl
+ ld hl, wOAMBaseTile
+ inc [hl]
+ inc [hl]
+ pop hl
+ ld a, 8
+ add b
+ ld b, a
+ dec d
+ jr nz, .loop
+ ret
+
+ZeroOutDuplicatesInList:
+; replace duplicate bytes in the list of wild pokemon locations with 0
+ ld de, wBuffer
+.loop
+ ld a, [de]
+ inc de
+ cp $ff
+ ret z
+ ld c, a
+ ld l, e
+ ld h, d
+.zeroDuplicatesLoop
+ ld a, [hl]
+ cp $ff
+ jr z, .loop
+ cp c
+ jr nz, .skipZeroing
+ xor a
+ ld [hl], a
+.skipZeroing
+ inc hl
+ jr .zeroDuplicatesLoop
+
+LoadTownMapEntry:
+; in: a = map number
+; out: lower nybble of [de] = x, upper nybble of [de] = y, hl = address of name
+ cp REDS_HOUSE_1F
+ jr c, .external
+ ld bc, 4
+ ld hl, InternalMapEntries
+.loop
+ cp [hl]
+ jr c, .foundEntry
+ add hl, bc
+ jr .loop
+.foundEntry
+ inc hl
+ jr .readEntry
+.external
+ ld hl, ExternalMapEntries
+ ld c, a
+ ld b, 0
+ add hl, bc
+ add hl, bc
+ add hl, bc
+.readEntry
+ ld a, [hli]
+ ld [de], a
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ret
+
+INCLUDE "data/town_map_entries.asm"
+
+ inclang text/map_names.asm
+
+MonNestIcon:
+ INCBIN "gfx/mon_nest_icon.1bpp"
+MonNestIconEnd:
+
+TownMapSpriteBlinkingAnimation:
+ ld a, [wAnimCounter]
+ inc a
+ cp 25
+ jr z, .hideSprites
+ cp 50
+ jr nz, .done
+; show sprites when the counter reaches 50
+ ld hl, wTileMapBackup
+ ld de, wOAMBuffer
+ ld bc, $90
+ call CopyData
+ xor a
+ jr .done
+.hideSprites
+ ld hl, wOAMBuffer
+ ld b, $24
+ ld de, $4
+.hideSpritesLoop
+ ld [hl], $a0
+ add hl, de
+ dec b
+ jr nz, .hideSpritesLoop
+ ld a, 25
+.done
+ ld [wAnimCounter], a
+ jp DelayFrame
diff --git a/en/engine/trade.asm b/en/engine/trade.asm
new file mode 100755
index 00000000..8bc8e3bc
--- /dev/null
+++ b/en/engine/trade.asm
@@ -0,0 +1,853 @@
+InternalClockTradeAnim:
+; Do the trading animation with the player's gameboy on the left.
+; In-game trades and internally clocked link cable trades use this.
+ ld a, [wTradedPlayerMonSpecies]
+ ld [wLeftGBMonSpecies], a
+ ld a, [wTradedEnemyMonSpecies]
+ ld [wRightGBMonSpecies], a
+ ld de, InternalClockTradeFuncSequence
+ jr TradeAnimCommon
+
+ExternalClockTradeAnim:
+; Do the trading animation with the player's gameboy on the right.
+; Externally clocked link cable trades use this.
+ ld a, [wTradedEnemyMonSpecies]
+ ld [wLeftGBMonSpecies], a
+ ld a, [wTradedPlayerMonSpecies]
+ ld [wRightGBMonSpecies], a
+ ld de, ExternalClockTradeFuncSequence
+
+TradeAnimCommon:
+ ld a, [wOptions]
+ push af
+ ld a, [hSCY]
+ push af
+ ld a, [hSCX]
+ push af
+ xor a
+ ld [wOptions], a
+ ld [hSCY], a
+ ld [hSCX], a
+ push de
+.loop
+ pop de
+ ld a, [de]
+ cp $ff
+ jr z, .done
+ inc de
+ push de
+ ld hl, TradeFuncPointerTable
+ add a
+ ld c, a
+ ld b, $0
+ add hl, bc
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld de, .loop
+ push de
+ jp hl ; call trade func, which will return to the top of the loop
+.done
+ pop af
+ ld [hSCX], a
+ pop af
+ ld [hSCY], a
+ pop af
+ ld [wOptions], a
+ ret
+
+addtradefunc: MACRO
+\1TradeFunc::
+ dw \1
+ ENDM
+
+tradefunc: MACRO
+ db (\1TradeFunc - TradeFuncPointerTable) / 2
+ ENDM
+
+; The functions in the sequences below are executed in order by TradeFuncCommon.
+; They are from opposite perspectives. The external clock one makes use of
+; Trade_SwapNames to swap the player and enemy names for some functions.
+
+InternalClockTradeFuncSequence:
+ tradefunc LoadTradingGFXAndMonNames
+ tradefunc Trade_ShowPlayerMon
+ tradefunc Trade_DrawOpenEndOfLinkCable
+ tradefunc Trade_AnimateBallEnteringLinkCable
+ tradefunc Trade_AnimLeftToRight
+ tradefunc Trade_Delay100
+ tradefunc Trade_ShowClearedWindow
+ tradefunc PrintTradeWentToText
+ tradefunc PrintTradeForSendsText
+ tradefunc PrintTradeFarewellText
+ tradefunc Trade_AnimRightToLeft
+ tradefunc Trade_ShowClearedWindow
+ tradefunc Trade_DrawOpenEndOfLinkCable
+ tradefunc Trade_ShowEnemyMon
+ tradefunc Trade_Delay100
+ tradefunc Trade_Cleanup
+ db $FF
+
+ExternalClockTradeFuncSequence:
+ tradefunc LoadTradingGFXAndMonNames
+ tradefunc Trade_ShowClearedWindow
+ tradefunc PrintTradeWillTradeText
+ tradefunc PrintTradeFarewellText
+ tradefunc Trade_SwapNames
+ tradefunc Trade_AnimLeftToRight
+ tradefunc Trade_SwapNames
+ tradefunc Trade_ShowClearedWindow
+ tradefunc Trade_DrawOpenEndOfLinkCable
+ tradefunc Trade_ShowEnemyMon
+ tradefunc Trade_SlideTextBoxOffScreen
+ tradefunc Trade_ShowPlayerMon
+ tradefunc Trade_DrawOpenEndOfLinkCable
+ tradefunc Trade_AnimateBallEnteringLinkCable
+ tradefunc Trade_SwapNames
+ tradefunc Trade_AnimRightToLeft
+ tradefunc Trade_SwapNames
+ tradefunc Trade_Delay100
+ tradefunc Trade_ShowClearedWindow
+ tradefunc PrintTradeWentToText
+ tradefunc Trade_Cleanup
+ db $FF
+
+TradeFuncPointerTable:
+ addtradefunc LoadTradingGFXAndMonNames
+ addtradefunc Trade_ShowPlayerMon
+ addtradefunc Trade_DrawOpenEndOfLinkCable
+ addtradefunc Trade_AnimateBallEnteringLinkCable
+ addtradefunc Trade_ShowEnemyMon
+ addtradefunc Trade_AnimLeftToRight
+ addtradefunc Trade_AnimRightToLeft
+ addtradefunc Trade_Delay100
+ addtradefunc Trade_ShowClearedWindow
+ addtradefunc PrintTradeWentToText
+ addtradefunc PrintTradeForSendsText
+ addtradefunc PrintTradeFarewellText
+ addtradefunc PrintTradeTakeCareText
+ addtradefunc PrintTradeWillTradeText
+ addtradefunc Trade_Cleanup
+ addtradefunc Trade_SlideTextBoxOffScreen
+ addtradefunc Trade_SwapNames
+
+Trade_Delay100:
+ ld c, 100
+ jp DelayFrames
+
+Trade_CopyTileMapToVRAM:
+ ld a, $1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call Delay3
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ret
+
+Trade_Delay80:
+ ld c, 80
+ jp DelayFrames
+
+Trade_ClearTileMap:
+ coord hl, 0, 0
+ ld bc, SCREEN_WIDTH * SCREEN_HEIGHT
+ ld a, " "
+ jp FillMemory
+
+LoadTradingGFXAndMonNames:
+ call Trade_ClearTileMap
+ call DisableLCD
+ ld hl, TradingAnimationGraphics
+ ld de, vChars2 + $310
+ ld bc, TradingAnimationGraphicsEnd - TradingAnimationGraphics
+ ld a, BANK(TradingAnimationGraphics)
+ call FarCopyData2
+ ld hl, TradingAnimationGraphics2
+ ld de, vSprites + $7c0
+ ld bc, TradingAnimationGraphics2End - TradingAnimationGraphics2
+ ld a, BANK(TradingAnimationGraphics2)
+ call FarCopyData2
+ ld hl, vBGMap0
+ ld bc, $800
+ ld a, " "
+ call FillMemory
+ call ClearSprites
+ ld a, $ff
+ ld [wUpdateSpritesEnabled], a
+ ld hl, wd730
+ set 6, [hl] ; turn on instant text printing
+ ld a, [wOnSGB]
+ and a
+ ld a, $e4 ; non-SGB OBP0
+ jr z, .next
+ ld a, $f0 ; SGB OBP0
+.next
+ ld [rOBP0], a
+ call EnableLCD
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ld a, [wTradedPlayerMonSpecies]
+ ld [wd11e], a
+ call GetMonName
+ ld hl, wcd6d
+ ld de, wcf4b
+ ld bc, NAME_LENGTH
+ call CopyData
+ ld a, [wTradedEnemyMonSpecies]
+ ld [wd11e], a
+ jp GetMonName
+
+Trade_LoadMonPartySpriteGfx:
+ ld a, %11010000
+ ld [rOBP1], a
+ jpba LoadMonPartySpriteGfx
+
+Trade_SwapNames:
+ ld hl, wPlayerName
+ ld de, wBuffer
+ ld bc, NAME_LENGTH
+ call CopyData
+ ld hl, wLinkEnemyTrainerName
+ ld de, wPlayerName
+ ld bc, NAME_LENGTH
+ call CopyData
+ ld hl, wBuffer
+ ld de, wLinkEnemyTrainerName
+ ld bc, NAME_LENGTH
+ jp CopyData
+
+Trade_Cleanup:
+ xor a
+ call LoadGBPal
+ ld hl, wd730
+ res 6, [hl] ; turn off instant text printing
+ ret
+
+Trade_ShowPlayerMon:
+ ld a, %10101011
+ ld [rLCDC], a
+ ld a, $50
+ ld [hWY], a
+ ld a, $86
+ ld [rWX], a
+ ld [hSCX], a
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ coord hl, 4, 0
+ ld b, 6
+ ld c, 10
+ call TextBoxBorder
+ call Trade_PrintPlayerMonInfoText
+ ld b, vBGMap0 / $100
+ call CopyScreenTileBufferToVRAM
+ call ClearScreen
+ ld a, [wTradedPlayerMonSpecies]
+ call Trade_LoadMonSprite
+ ld a, $7e
+.slideScreenLoop
+ push af
+ call DelayFrame
+ pop af
+ ld [rWX], a
+ ld [hSCX], a
+ dec a
+ dec a
+ and a
+ jr nz, .slideScreenLoop
+ call Trade_Delay80
+ ld a, TRADE_BALL_POOF_ANIM
+ call Trade_ShowAnimation
+ ld a, TRADE_BALL_DROP_ANIM
+ call Trade_ShowAnimation ; clears mon pic
+ ld a, [wTradedPlayerMonSpecies]
+ call PlayCry
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ret
+
+Trade_DrawOpenEndOfLinkCable:
+ call Trade_ClearTileMap
+ ld b, vBGMap0 / $100
+ call CopyScreenTileBufferToVRAM
+ ld b, SET_PAL_GENERIC
+ call RunPaletteCommand
+
+; This function call is pointless. It just copies blank tiles to VRAM that was
+; already filled with blank tiles.
+ ld hl, vBGMap1 + $8c
+ call Trade_CopyCableTilesOffScreen
+
+ ld a, $a0
+ ld [hSCX], a
+ call DelayFrame
+ ld a, %10001011
+ ld [rLCDC], a
+ coord hl, 6, 2
+ ld b, $7 ; open end of link cable tile ID list index
+ call CopyTileIDsFromList_ZeroBaseTileID
+ call Trade_CopyTileMapToVRAM
+ ld a, SFX_HEAL_HP
+ call PlaySound
+ ld c, 20
+.loop
+ ld a, [hSCX]
+ add 4
+ ld [hSCX], a
+ dec c
+ jr nz, .loop
+ ret
+
+Trade_AnimateBallEnteringLinkCable:
+ ld a, TRADE_BALL_SHAKE_ANIM
+ call Trade_ShowAnimation
+ ld c, 10
+ call DelayFrames
+ ld a, %11100100
+ ld [rOBP0], a
+ xor a
+ ld [wLinkCableAnimBulgeToggle], a
+ lb bc, $20, $60
+.moveBallInsideLinkCableLoop
+ push bc
+ xor a
+ ld de, Trade_BallInsideLinkCableOAM
+ call WriteOAMBlock
+ ld a, [wLinkCableAnimBulgeToggle]
+ xor $1
+ ld [wLinkCableAnimBulgeToggle], a
+ add $7e
+ ld hl, wOAMBuffer + $02
+ ld de, 4
+ ld c, e
+.cycleLinkCableBulgeTile
+ ld [hl], a
+ add hl, de
+ dec c
+ jr nz, .cycleLinkCableBulgeTile
+ call Delay3
+ pop bc
+ ld a, c
+ add $4
+ ld c, a
+ cp $a0
+ jr nc, .ballSpriteReachedEdgeOfScreen
+ ld a, SFX_TINK
+ call PlaySound
+ jr .moveBallInsideLinkCableLoop
+.ballSpriteReachedEdgeOfScreen
+ call ClearSprites
+ ld a, $1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call ClearScreen
+ ld b, $98
+ call CopyScreenTileBufferToVRAM
+ call Delay3
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ret
+
+Trade_BallInsideLinkCableOAM:
+ db $7E,$00,$7E,$20
+ db $7E,$40,$7E,$60
+
+Trade_ShowEnemyMon:
+ ld a, TRADE_BALL_TILT_ANIM
+ call Trade_ShowAnimation
+ call Trade_ShowClearedWindow
+ coord hl, 4, 10
+ ld b, 6
+ ld c, 10
+ call TextBoxBorder
+ call Trade_PrintEnemyMonInfoText
+ call Trade_CopyTileMapToVRAM
+ ld a, $1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ld a, [wTradedEnemyMonSpecies]
+ call Trade_LoadMonSprite
+ ld a, TRADE_BALL_POOF_ANIM
+ call Trade_ShowAnimation
+ ld a, $1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ld a, [wTradedEnemyMonSpecies]
+ call PlayCry
+ call Trade_Delay100
+ coord hl, 4, 10
+ lb bc, 8, 12
+ call ClearScreenArea
+ jp PrintTradeTakeCareText
+
+Trade_AnimLeftToRight:
+; Animates the mon moving from the left GB to the right one.
+ call Trade_InitGameboyTransferGfx
+ ld a, $1
+ ld [wTradedMonMovingRight], a
+ ld a, %11100100
+ ld [rOBP0], a
+ ld a, $54
+ ld [wBaseCoordX], a
+ ld a, $1c
+ ld [wBaseCoordY], a
+ ld a, [wLeftGBMonSpecies]
+ ld [wMonPartySpriteSpecies], a
+ call Trade_WriteCircledMonOAM
+ call Trade_DrawLeftGameboy
+ call Trade_CopyTileMapToVRAM
+ call Trade_DrawCableAcrossScreen
+ ld hl, vBGMap1 + $8c
+ call Trade_CopyCableTilesOffScreen
+ ld b, $6
+ call Trade_AnimMonMoveHorizontal
+ ld a, $1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call Trade_DrawCableAcrossScreen
+ ld b, $4
+ call Trade_AnimMonMoveHorizontal
+ call Trade_DrawRightGameboy
+ ld b, $6
+ call Trade_AnimMonMoveHorizontal
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call Trade_AnimMonMoveVertical
+ jp ClearSprites
+
+Trade_AnimRightToLeft:
+; Animates the mon moving from the right GB to the left one.
+ call Trade_InitGameboyTransferGfx
+ xor a
+ ld [wTradedMonMovingRight], a
+ ld a, $64
+ ld [wBaseCoordX], a
+ ld a, $44
+ ld [wBaseCoordY], a
+ ld a, [wRightGBMonSpecies]
+ ld [wMonPartySpriteSpecies], a
+ call Trade_WriteCircledMonOAM
+ call Trade_DrawRightGameboy
+ call Trade_CopyTileMapToVRAM
+ call Trade_DrawCableAcrossScreen
+ ld hl, vBGMap1 + $94
+ call Trade_CopyCableTilesOffScreen
+ call Trade_AnimMonMoveVertical
+ ld b, $6
+ call Trade_AnimMonMoveHorizontal
+ ld a, $1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call Trade_DrawCableAcrossScreen
+ ld b, $4
+ call Trade_AnimMonMoveHorizontal
+ call Trade_DrawLeftGameboy
+ ld b, $6
+ call Trade_AnimMonMoveHorizontal
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ jp ClearSprites
+
+Trade_InitGameboyTransferGfx:
+; Initialises the graphics for showing a mon moving between gameboys.
+ ld a, $1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call ClearScreen
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call Trade_LoadMonPartySpriteGfx
+ call DelayFrame
+ ld a, %10101011
+ ld [rLCDC], a
+ xor a
+ ld [hSCX], a
+ ld a, $90
+ ld [hWY], a
+ ret
+
+Trade_DrawLeftGameboy:
+ call Trade_ClearTileMap
+
+; draw link cable
+ coord hl, 11, 4
+ ld a, $5d
+ ld [hli], a
+ ld a, $5e
+ ld c, 8
+.loop
+ ld [hli], a
+ dec c
+ jr nz, .loop
+
+; draw gameboy pic
+ coord hl, 5, 3
+ ld b, $6
+ call CopyTileIDsFromList_ZeroBaseTileID
+
+; draw text box with player name below gameboy pic
+ coord hl, 4, 12
+ ld b, 2
+ ld c, 7
+ call TextBoxBorder
+ coord hl, 5, 14
+ ld de, wPlayerName
+ call PlaceString
+
+ jp DelayFrame
+
+Trade_DrawRightGameboy:
+ call Trade_ClearTileMap
+
+; draw horizontal segment of link cable
+ coord hl, 0, 4
+ ld a, $5e
+ ld c, $e
+.loop
+ ld [hli], a
+ dec c
+ jr nz, .loop
+
+; draw vertical segment of link cable
+ ld a, $5f
+ ld [hl], a
+ ld de, SCREEN_WIDTH
+ add hl, de
+ ld a, $61
+ ld [hl], a
+ add hl, de
+ ld [hl], a
+ add hl, de
+ ld [hl], a
+ add hl, de
+ ld [hl], a
+ add hl, de
+ ld a, $60
+ ld [hld], a
+ ld a, $5d
+ ld [hl], a
+
+; draw gameboy pic
+ coord hl, 7, 8
+ ld b, $6
+ call CopyTileIDsFromList_ZeroBaseTileID
+
+; draw text box with enemy name above link cable
+ coord hl, 6, 0
+ ld b, 2
+ ld c, 7
+ call TextBoxBorder
+ coord hl, 7, 2
+ ld de, wLinkEnemyTrainerName
+ call PlaceString
+
+ jp DelayFrame
+
+Trade_DrawCableAcrossScreen:
+; Draws the link cable across the screen.
+ call Trade_ClearTileMap
+ coord hl, 0, 4
+ ld a, $5e
+ ld c, SCREEN_WIDTH
+.loop
+ ld [hli], a
+ dec c
+ jr nz, .loop
+ ret
+
+Trade_CopyCableTilesOffScreen:
+; This is used to copy the link cable tiles off screen so that the cable
+; continues when the screen is scrolled.
+ push hl
+ coord hl, 0, 4
+ call CopyToRedrawRowOrColumnSrcTiles
+ pop hl
+ ld a, h
+ ld [hRedrawRowOrColumnDest + 1], a
+ ld a, l
+ ld [hRedrawRowOrColumnDest], a
+ ld a, REDRAW_ROW
+ ld [hRedrawRowOrColumnMode], a
+ ld c, 10
+ jp DelayFrames
+
+Trade_AnimMonMoveHorizontal:
+; Animates the mon going through the link cable horizontally over a distance of
+; b 16-pixel units.
+ ld a, [wTradedMonMovingRight]
+ ld e, a
+ ld d, $8
+.scrollLoop
+ ld a, e
+ dec a
+ jr z, .movingRight
+; moving left
+ ld a, [hSCX]
+ sub $2
+ jr .next
+.movingRight
+ ld a, [hSCX]
+ add $2
+.next
+ ld [hSCX], a
+ call DelayFrame
+ dec d
+ jr nz, .scrollLoop
+ call Trade_AnimCircledMon
+ dec b
+ jr nz, Trade_AnimMonMoveHorizontal
+ ret
+
+Trade_AnimCircledMon:
+; Cycles between the two animation frames of the mon party sprite, cycles
+; between a circle and an oval around the mon sprite, and makes the cable flash.
+ push de
+ push bc
+ push hl
+ ld a, [rBGP]
+ xor $3c ; make link cable flash
+ ld [rBGP], a
+ ld hl, wOAMBuffer + $02
+ ld de, $4
+ ld c, $14
+.loop
+ ld a, [hl]
+ xor $40
+ ld [hl], a
+ add hl, de
+ dec c
+ jr nz, .loop
+ pop hl
+ pop bc
+ pop de
+ ret
+
+Trade_WriteCircledMonOAM:
+ callba WriteMonPartySpriteOAMBySpecies
+ call Trade_WriteCircleOAM
+
+Trade_AddOffsetsToOAMCoords:
+ ld hl, wOAMBuffer
+ ld c, $14
+.loop
+ ld a, [wBaseCoordY]
+ add [hl]
+ ld [hli], a
+ ld a, [wBaseCoordX]
+ add [hl]
+ ld [hli], a
+ inc hl
+ inc hl
+ dec c
+ jr nz, .loop
+ ret
+
+Trade_AnimMonMoveVertical:
+; Animates the mon going through the link cable vertically as well as
+; horizontally for a bit. The last bit of horizontal movement (when moving
+; right) or the first bit of horizontal movement (when moving left) are done
+; here instead of Trade_AnimMonMoveHorizontal because this function moves the
+; sprite itself rather than scrolling the screen around the sprite. Moving the
+; sprite itself is necessary because the vertical segment of the link cable is
+; to the right of the screen position that the mon sprite has when
+; Trade_AnimMonMoveHorizontal is executing.
+ ld a, [wTradedMonMovingRight]
+ and a
+ jr z, .movingLeft
+; moving right
+ lb bc, 4, 0 ; move right
+ call .doAnim
+ lb bc, 0, 10 ; move down
+ jr .doAnim
+.movingLeft
+ lb bc, 0, -10 ; move up
+ call .doAnim
+ lb bc, -4, 0 ; move left
+.doAnim
+ ld a, b
+ ld [wBaseCoordX], a
+ ld a, c
+ ld [wBaseCoordY], a
+ ld d, $4
+.loop
+ call Trade_AddOffsetsToOAMCoords
+ call Trade_AnimCircledMon
+ ld c, 8
+ call DelayFrames
+ dec d
+ jr nz, .loop
+ ret
+
+Trade_WriteCircleOAM:
+; Writes the OAM blocks for the circle around the traded mon as it passes
+; the link cable.
+ ld hl, Trade_CircleOAMPointers
+ ld c, $4
+ xor a
+.loop
+ push bc
+ ld e, [hl]
+ inc hl
+ ld d, [hl]
+ inc hl
+ ld c, [hl]
+ inc hl
+ ld b, [hl]
+ inc hl
+ push hl
+ inc a
+ push af
+ call WriteOAMBlock
+ pop af
+ pop hl
+ pop bc
+ dec c
+ jr nz, .loop
+ ret
+
+Trade_CircleOAMPointers:
+ dw Trade_CircleOAM0
+ db $08,$08
+ dw Trade_CircleOAM1
+ db $18,$08
+ dw Trade_CircleOAM2
+ db $08,$18
+ dw Trade_CircleOAM3
+ db $18,$18
+
+Trade_CircleOAM0:
+ db $38,$10,$39,$10
+ db $3A,$10,$3B,$10
+
+Trade_CircleOAM1:
+ db $39,$30,$38,$30
+ db $3B,$30,$3A,$30
+
+Trade_CircleOAM2:
+ db $3A,$50,$3B,$50
+ db $38,$50,$39,$50
+
+Trade_CircleOAM3:
+ db $3B,$70,$3A,$70
+ db $39,$70,$38,$70
+
+; a = species
+Trade_LoadMonSprite:
+ ld [wcf91], a
+ ld [wd0b5], a
+ ld [wWholeScreenPaletteMonSpecies], a
+ ld b, SET_PAL_POKEMON_WHOLE_SCREEN
+ ld c, 0
+ call RunPaletteCommand
+ ld a, [H_AUTOBGTRANSFERENABLED]
+ xor $1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call GetMonHeader
+ coord hl, 7, 2
+ call LoadFlippedFrontSpriteByMonIndex
+ ld c, 10
+ jp DelayFrames
+
+Trade_ShowClearedWindow:
+; clears the window and covers the BG entirely with the window
+ ld a, $1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ call ClearScreen
+ ld a, %11100011
+ ld [rLCDC], a
+ ld a, $7
+ ld [rWX], a
+ xor a
+ ld [hWY], a
+ ld a, $90
+ ld [hSCX], a
+ ret
+
+Trade_SlideTextBoxOffScreen:
+; Slides the window right until it's off screen. The window usually just has
+; a text box at the bottom when this is called. However, when this is called
+; after Trade_ShowEnemyMon in the external clock sequence, there is a mon pic
+; above the text box and it is also scrolled off the screen.
+ ld c, 50
+ call DelayFrames
+.loop
+ call DelayFrame
+ ld a, [rWX]
+ inc a
+ inc a
+ ld [rWX], a
+ cp $a1
+ jr nz, .loop
+ call Trade_ClearTileMap
+ ld c, 10
+ call DelayFrames
+ ld a, $7
+ ld [rWX], a
+ ret
+
+PrintTradeWentToText:
+ ld hl, TradeWentToText
+ call PrintText
+ ld c, 200
+ call DelayFrames
+ jp Trade_SlideTextBoxOffScreen
+
+TradeWentToText:
+ TX_FAR _TradeWentToText
+ db "@"
+
+PrintTradeForSendsText:
+ ld hl, TradeForText
+ call PrintText
+ call Trade_Delay80
+ ld hl, TradeSendsText
+ call PrintText
+ jp Trade_Delay80
+
+TradeForText:
+ TX_FAR _TradeForText
+ db "@"
+
+TradeSendsText:
+ TX_FAR _TradeSendsText
+ db "@"
+
+PrintTradeFarewellText:
+ ld hl, TradeWavesFarewellText
+ call PrintText
+ call Trade_Delay80
+ ld hl, TradeTransferredText
+ call PrintText
+ call Trade_Delay80
+ jp Trade_SlideTextBoxOffScreen
+
+TradeWavesFarewellText:
+ TX_FAR _TradeWavesFarewellText
+ db "@"
+
+TradeTransferredText:
+ TX_FAR _TradeTransferredText
+ db "@"
+
+PrintTradeTakeCareText:
+ ld hl, TradeTakeCareText
+ call PrintText
+ jp Trade_Delay80
+
+TradeTakeCareText:
+ TX_FAR _TradeTakeCareText
+ db "@"
+
+PrintTradeWillTradeText:
+ ld hl, TradeWillTradeText
+ call PrintText
+ call Trade_Delay80
+ ld hl, TradeforText
+ call PrintText
+ jp Trade_Delay80
+
+TradeWillTradeText:
+ TX_FAR _TradeWillTradeText
+ db "@"
+
+TradeforText:
+ TX_FAR _TradeforText
+ db "@"
+
+Trade_ShowAnimation:
+ ld [wAnimationID], a
+ xor a
+ ld [wAnimationType], a
+ predef_jump MoveAnimation