aboutsummaryrefslogtreecommitdiffstats
path: root/engine/pokemon
diff options
context:
space:
mode:
Diffstat (limited to 'engine/pokemon')
-rw-r--r--engine/pokemon/add_mon.asm516
-rw-r--r--engine/pokemon/bills_pc.asm548
-rwxr-xr-xengine/pokemon/evos_moves.asm513
-rwxr-xr-xengine/pokemon/experience.asm149
-rwxr-xr-xengine/pokemon/learn_move.asm226
-rw-r--r--engine/pokemon/load_mon_data.asm49
-rw-r--r--engine/pokemon/remove_mon.asm95
-rwxr-xr-xengine/pokemon/set_types.asm15
-rwxr-xr-xengine/pokemon/status_ailments.asm46
-rwxr-xr-xengine/pokemon/status_screen.asm481
10 files changed, 2638 insertions, 0 deletions
diff --git a/engine/pokemon/add_mon.asm b/engine/pokemon/add_mon.asm
new file mode 100644
index 00000000..7627136b
--- /dev/null
+++ b/engine/pokemon/add_mon.asm
@@ -0,0 +1,516 @@
+_AddPartyMon::
+; Adds a new mon to the player's or enemy's party.
+; [wMonDataLocation] is used in an unusual way in this function.
+; If the lower nybble is 0, the mon is added to the player's party, else the enemy's.
+; If the entire value is 0, then the player is allowed to name the mon.
+ ld de, wPartyCount
+ ld a, [wMonDataLocation]
+ and $f
+ jr z, .next
+ ld de, wEnemyPartyCount
+.next
+ ld a, [de]
+ inc a
+ cp PARTY_LENGTH + 1
+ ret nc ; return if the party is already full
+ ld [de], a
+ ld a, [de]
+ ld [hNewPartyLength], a
+ add e
+ ld e, a
+ jr nc, .noCarry
+ inc d
+.noCarry
+ ld a, [wcf91]
+ ld [de], a ; write species of new mon in party list
+ inc de
+ ld a, $ff ; terminator
+ ld [de], a
+ ld hl, wPartyMonOT
+ ld a, [wMonDataLocation]
+ and $f
+ jr z, .next2
+ ld hl, wEnemyMonOT
+.next2
+ ld a, [hNewPartyLength]
+ dec a
+ call SkipFixedLengthTextEntries
+ ld d, h
+ ld e, l
+ ld hl, wPlayerName
+ ld bc, NAME_LENGTH
+ call CopyData
+ ld a, [wMonDataLocation]
+ and a
+ jr nz, .skipNaming
+ ld hl, wPartyMonNicks
+ ld a, [hNewPartyLength]
+ dec a
+ call SkipFixedLengthTextEntries
+ ld a, NAME_MON_SCREEN
+ ld [wNamingScreenType], a
+ predef AskName
+.skipNaming
+ ld hl, wPartyMons
+ ld a, [wMonDataLocation]
+ and $f
+ jr z, .next3
+ ld hl, wEnemyMons
+.next3
+ ld a, [hNewPartyLength]
+ dec a
+ ld bc, wPartyMon2 - wPartyMon1
+ call AddNTimes
+ ld e, l
+ ld d, h
+ push hl
+ ld a, [wcf91]
+ ld [wd0b5], a
+ call GetMonHeader
+ ld hl, wMonHeader
+ ld a, [hli]
+ ld [de], a ; species
+ inc de
+ pop hl
+ push hl
+ ld a, [wMonDataLocation]
+ and $f
+ ld a, $98 ; set enemy trainer mon IVs to fixed average values
+ ld b, $88
+ jr nz, .next4
+
+; If the mon is being added to the player's party, update the pokedex.
+ ld a, [wcf91]
+ ld [wd11e], a
+ push de
+ predef IndexToPokedex
+ pop de
+ ld a, [wd11e]
+ dec a
+ ld c, a
+ ld b, FLAG_TEST
+ ld hl, wPokedexOwned
+ call FlagAction
+ ld a, c ; whether the mon was already flagged as owned
+ ld [wUnusedD153], a ; not read
+ ld a, [wd11e]
+ dec a
+ ld c, a
+ ld b, FLAG_SET
+ push bc
+ call FlagAction
+ pop bc
+ ld hl, wPokedexSeen
+ call FlagAction
+
+ pop hl
+ push hl
+
+ ld a, [wIsInBattle]
+ and a ; is this a wild mon caught in battle?
+ jr nz, .copyEnemyMonData
+
+; Not wild.
+ call Random ; generate random IVs
+ ld b, a
+ call Random
+
+.next4
+ push bc
+ ld bc, wPartyMon1DVs - wPartyMon1
+ add hl, bc
+ pop bc
+ ld [hli], a
+ ld [hl], b ; write IVs
+ ld bc, (wPartyMon1HPExp - 1) - (wPartyMon1DVs + 1)
+ add hl, bc
+ ld a, 1
+ ld c, a
+ xor a
+ ld b, a
+ call CalcStat ; calc HP stat (set cur Hp to max HP)
+ ld a, [H_MULTIPLICAND+1]
+ ld [de], a
+ inc de
+ ld a, [H_MULTIPLICAND+2]
+ ld [de], a
+ inc de
+ xor a
+ ld [de], a ; box level
+ inc de
+ ld [de], a ; status ailments
+ inc de
+ jr .copyMonTypesAndMoves
+.copyEnemyMonData
+ ld bc, wEnemyMon1DVs - wEnemyMon1
+ add hl, bc
+ ld a, [wEnemyMonDVs] ; copy IVs from cur enemy mon
+ ld [hli], a
+ ld a, [wEnemyMonDVs + 1]
+ ld [hl], a
+ ld a, [wEnemyMonHP] ; copy HP from cur enemy mon
+ ld [de], a
+ inc de
+ ld a, [wEnemyMonHP+1]
+ ld [de], a
+ inc de
+ xor a
+ ld [de], a ; box level
+ inc de
+ ld a, [wEnemyMonStatus] ; copy status ailments from cur enemy mon
+ ld [de], a
+ inc de
+.copyMonTypesAndMoves
+ ld hl, wMonHTypes
+ ld a, [hli] ; type 1
+ ld [de], a
+ inc de
+ ld a, [hli] ; type 2
+ ld [de], a
+ inc de
+ ld a, [hli] ; catch rate (held item in gen 2)
+ ld [de], a
+ ld hl, wMonHMoves
+ ld a, [hli]
+ inc de
+ push de
+ ld [de], a
+ ld a, [hli]
+ inc de
+ ld [de], a
+ ld a, [hli]
+ inc de
+ ld [de], a
+ ld a, [hli]
+ inc de
+ ld [de], a
+ push de
+ dec de
+ dec de
+ dec de
+ xor a
+ ld [wLearningMovesFromDayCare], a
+ predef WriteMonMoves
+ pop de
+ ld a, [wPlayerID] ; set trainer ID to player ID
+ inc de
+ ld [de], a
+ ld a, [wPlayerID + 1]
+ inc de
+ ld [de], a
+ push de
+ ld a, [wCurEnemyLVL]
+ ld d, a
+ callab CalcExperience
+ pop de
+ inc de
+ ld a, [hExperience] ; write experience
+ ld [de], a
+ inc de
+ ld a, [hExperience + 1]
+ ld [de], a
+ inc de
+ ld a, [hExperience + 2]
+ ld [de], a
+ xor a
+ ld b, NUM_STATS * 2
+.writeEVsLoop ; set all EVs to 0
+ inc de
+ ld [de], a
+ dec b
+ jr nz, .writeEVsLoop
+ inc de
+ inc de
+ pop hl
+ call AddPartyMon_WriteMovePP
+ inc de
+ ld a, [wCurEnemyLVL]
+ ld [de], a
+ inc de
+ ld a, [wIsInBattle]
+ dec a
+ jr nz, .calcFreshStats
+ ld hl, wEnemyMonMaxHP
+ ld bc, $a
+ call CopyData ; copy stats of cur enemy mon
+ pop hl
+ jr .done
+.calcFreshStats
+ pop hl
+ ld bc, wPartyMon1HPExp - 1 - wPartyMon1
+ add hl, bc
+ ld b, $0
+ call CalcStats ; calculate fresh set of stats
+.done
+ scf
+ ret
+
+LoadMovePPs:
+ call GetPredefRegisters
+ ; fallthrough
+AddPartyMon_WriteMovePP:
+ ld b, NUM_MOVES
+.pploop
+ ld a, [hli] ; read move ID
+ and a
+ jr z, .empty
+ dec a
+ push hl
+ push de
+ push bc
+ ld hl, Moves
+ ld bc, MoveEnd - Moves
+ call AddNTimes
+ ld de, wcd6d
+ ld a, BANK(Moves)
+ call FarCopyData
+ pop bc
+ pop de
+ pop hl
+ ld a, [wcd6d + 5] ; PP is byte 5 of move data
+.empty
+ inc de
+ ld [de], a
+ dec b
+ jr nz, .pploop ; there are still moves to read
+ ret
+
+; adds enemy mon [wcf91] (at position [wWhichPokemon] in enemy list) to own party
+; used in the cable club trade center
+_AddEnemyMonToPlayerParty::
+ ld hl, wPartyCount
+ ld a, [hl]
+ cp PARTY_LENGTH
+ scf
+ ret z ; party full, return failure
+ inc a
+ ld [hl], a ; add 1 to party members
+ ld c, a
+ ld b, $0
+ add hl, bc
+ ld a, [wcf91]
+ ld [hli], a ; add mon as last list entry
+ ld [hl], $ff ; write new sentinel
+ ld hl, wPartyMons
+ ld a, [wPartyCount]
+ dec a
+ ld bc, wPartyMon2 - wPartyMon1
+ call AddNTimes
+ ld e, l
+ ld d, h
+ ld hl, wLoadedMon
+ call CopyData ; write new mon's data (from wLoadedMon)
+ ld hl, wPartyMonOT
+ ld a, [wPartyCount]
+ dec a
+ call SkipFixedLengthTextEntries
+ ld d, h
+ ld e, l
+ ld hl, wEnemyMonOT
+ ld a, [wWhichPokemon]
+ call SkipFixedLengthTextEntries
+ ld bc, NAME_LENGTH
+ call CopyData ; write new mon's OT name (from an enemy mon)
+ ld hl, wPartyMonNicks
+ ld a, [wPartyCount]
+ dec a
+ call SkipFixedLengthTextEntries
+ ld d, h
+ ld e, l
+ ld hl, wEnemyMonNicks
+ ld a, [wWhichPokemon]
+ call SkipFixedLengthTextEntries
+ ld bc, NAME_LENGTH
+ call CopyData ; write new mon's nickname (from an enemy mon)
+ ld a, [wcf91]
+ ld [wd11e], a
+ predef IndexToPokedex
+ ld a, [wd11e]
+ dec a
+ ld c, a
+ ld b, FLAG_SET
+ ld hl, wPokedexOwned
+ push bc
+ call FlagAction ; add to owned pokemon
+ pop bc
+ ld hl, wPokedexSeen
+ call FlagAction ; add to seen pokemon
+ and a
+ ret ; return success
+
+_MoveMon::
+ ld a, [wMoveMonType]
+ and a ; BOX_TO_PARTY
+ jr z, .checkPartyMonSlots
+ cp DAYCARE_TO_PARTY
+ jr z, .checkPartyMonSlots
+ cp PARTY_TO_DAYCARE
+ ld hl, wDayCareMon
+ jr z, .findMonDataSrc
+ ; else it's PARTY_TO_BOX
+ ld hl, wNumInBox
+ ld a, [hl]
+ cp MONS_PER_BOX
+ jr nz, .partyOrBoxNotFull
+ jr .boxFull
+.checkPartyMonSlots
+ ld hl, wPartyCount
+ ld a, [hl]
+ cp PARTY_LENGTH
+ jr nz, .partyOrBoxNotFull
+.boxFull
+ scf
+ ret
+.partyOrBoxNotFull
+ inc a
+ ld [hl], a ; increment number of mons in party/box
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld a, [wMoveMonType]
+ cp DAYCARE_TO_PARTY
+ ld a, [wDayCareMon]
+ jr z, .copySpecies
+ ld a, [wcf91]
+.copySpecies
+ ld [hli], a ; write new mon ID
+ ld [hl], $ff ; write new sentinel
+.findMonDataDest
+ ld a, [wMoveMonType]
+ dec a
+ ld hl, wPartyMons
+ ld bc, wPartyMon2 - wPartyMon1 ; $2c
+ ld a, [wPartyCount]
+ jr nz, .addMonOffset
+ ; if it's PARTY_TO_BOX
+ ld hl, wBoxMons
+ ld bc, wBoxMon2 - wBoxMon1 ; $21
+ ld a, [wNumInBox]
+.addMonOffset
+ dec a
+ call AddNTimes
+.findMonDataSrc
+ push hl
+ ld e, l
+ ld d, h
+ ld a, [wMoveMonType]
+ and a
+ ld hl, wBoxMons
+ ld bc, wBoxMon2 - wBoxMon1 ; $21
+ jr z, .addMonOffset2
+ cp DAYCARE_TO_PARTY
+ ld hl, wDayCareMon
+ jr z, .copyMonData
+ ld hl, wPartyMons
+ ld bc, wPartyMon2 - wPartyMon1 ; $2c
+.addMonOffset2
+ ld a, [wWhichPokemon]
+ call AddNTimes
+.copyMonData
+ push hl
+ push de
+ ld bc, wBoxMon2 - wBoxMon1
+ call CopyData
+ pop de
+ pop hl
+ ld a, [wMoveMonType]
+ and a ; BOX_TO_PARTY
+ jr z, .findOTdest
+ cp DAYCARE_TO_PARTY
+ jr z, .findOTdest
+ ld bc, wBoxMon2 - wBoxMon1
+ add hl, bc
+ ld a, [hl] ; hl = Level
+ inc de
+ inc de
+ inc de
+ ld [de], a ; de = BoxLevel
+.findOTdest
+ ld a, [wMoveMonType]
+ cp PARTY_TO_DAYCARE
+ ld de, wDayCareMonOT
+ jr z, .findOTsrc
+ dec a
+ ld hl, wPartyMonOT
+ ld a, [wPartyCount]
+ jr nz, .addOToffset
+ ld hl, wBoxMonOT
+ ld a, [wNumInBox]
+.addOToffset
+ dec a
+ call SkipFixedLengthTextEntries
+ ld d, h
+ ld e, l
+.findOTsrc
+ ld hl, wBoxMonOT
+ ld a, [wMoveMonType]
+ and a
+ jr z, .addOToffset2
+ ld hl, wDayCareMonOT
+ cp DAYCARE_TO_PARTY
+ jr z, .copyOT
+ ld hl, wPartyMonOT
+.addOToffset2
+ ld a, [wWhichPokemon]
+ call SkipFixedLengthTextEntries
+.copyOT
+ ld bc, NAME_LENGTH
+ call CopyData
+ ld a, [wMoveMonType]
+.findNickDest
+ cp PARTY_TO_DAYCARE
+ ld de, wDayCareMonName
+ jr z, .findNickSrc
+ dec a
+ ld hl, wPartyMonNicks
+ ld a, [wPartyCount]
+ jr nz, .addNickOffset
+ ld hl, wBoxMonNicks
+ ld a, [wNumInBox]
+.addNickOffset
+ dec a
+ call SkipFixedLengthTextEntries
+ ld d, h
+ ld e, l
+.findNickSrc
+ ld hl, wBoxMonNicks
+ ld a, [wMoveMonType]
+ and a
+ jr z, .addNickOffset2
+ ld hl, wDayCareMonName
+ cp DAYCARE_TO_PARTY
+ jr z, .copyNick
+ ld hl, wPartyMonNicks
+.addNickOffset2
+ ld a, [wWhichPokemon]
+ call SkipFixedLengthTextEntries
+.copyNick
+ ld bc, NAME_LENGTH
+ call CopyData
+ pop hl
+ ld a, [wMoveMonType]
+ cp PARTY_TO_BOX
+ jr z, .done
+ cp PARTY_TO_DAYCARE
+ jr z, .done
+ push hl
+ srl a
+ add $2
+ ld [wMonDataLocation], a
+ call LoadMonData
+ callba CalcLevelFromExperience
+ ld a, d
+ ld [wCurEnemyLVL], a
+ pop hl
+ ld bc, wBoxMon2 - wBoxMon1
+ add hl, bc
+ ld [hli], a
+ ld d, h
+ ld e, l
+ ld bc, -18
+ add hl, bc
+ ld b, $1
+ call CalcStats
+.done
+ and a
+ ret
diff --git a/engine/pokemon/bills_pc.asm b/engine/pokemon/bills_pc.asm
new file mode 100644
index 00000000..e5855f1d
--- /dev/null
+++ b/engine/pokemon/bills_pc.asm
@@ -0,0 +1,548 @@
+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 "<pkmn>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, SFX_TURN_ON_PC
+ 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, SFX_TURN_OFF_PC
+ 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 <pkmn>"
+ next "DEPOSIT <pkmn>"
+ next "RELEASE <pkmn>"
+ 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
+
+INCLUDE "data/hm_moves.asm"
+
+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::
+ TX_BILLS_PC
+
diff --git a/engine/pokemon/evos_moves.asm b/engine/pokemon/evos_moves.asm
new file mode 100755
index 00000000..f50f8081
--- /dev/null
+++ b/engine/pokemon/evos_moves.asm
@@ -0,0 +1,513 @@
+; try to evolve the mon in [wWhichPokemon]
+TryEvolvingMon:
+ ld hl, wCanEvolveFlags
+ xor a
+ ld [hl], a
+ ld a, [wWhichPokemon]
+ ld c, a
+ ld b, FLAG_SET
+ call Evolution_FlagAction
+
+; this is only called after battle
+; it is supposed to do level up evolutions, though there is a bug that allows item evolutions to occur
+EvolutionAfterBattle:
+ ld a, [hTilesetType]
+ push af
+ xor a
+ ld [wEvolutionOccurred], a
+ dec a
+ ld [wWhichPokemon], a
+ push hl
+ push bc
+ push de
+ ld hl, wPartyCount
+ push hl
+
+Evolution_PartyMonLoop: ; loop over party mons
+ ld hl, wWhichPokemon
+ inc [hl]
+ pop hl
+ inc hl
+ ld a, [hl]
+ cp $ff ; have we reached the end of the party?
+ jp z, .done
+ ld [wEvoOldSpecies], a
+ push hl
+ ld a, [wWhichPokemon]
+ ld c, a
+ ld hl, wCanEvolveFlags
+ ld b, FLAG_TEST
+ call Evolution_FlagAction
+ ld a, c
+ and a ; is the mon's bit set?
+ jp z, Evolution_PartyMonLoop ; if not, go to the next mon
+ ld a, [wEvoOldSpecies]
+ dec a
+ ld b, 0
+ ld hl, EvosMovesPointerTable
+ add a
+ rl b
+ ld c, a
+ add hl, bc
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ push hl
+ ld a, [wcf91]
+ push af
+ xor a ; PLAYER_PARTY_DATA
+ ld [wMonDataLocation], a
+ call LoadMonData
+ pop af
+ ld [wcf91], a
+ pop hl
+
+.evoEntryLoop ; loop over evolution entries
+ ld a, [hli]
+ and a ; have we reached the end of the evolution data?
+ jr z, Evolution_PartyMonLoop
+ ld b, a ; evolution type
+ cp EV_TRADE
+ jr z, .checkTradeEvo
+; not trade evolution
+ ld a, [wLinkState]
+ cp LINK_STATE_TRADING
+ jr z, Evolution_PartyMonLoop ; if trading, go the next mon
+ ld a, b
+ cp EV_ITEM
+ jr z, .checkItemEvo
+ ld a, [wForceEvolution]
+ and a
+ jr nz, Evolution_PartyMonLoop
+ ld a, b
+ cp EV_LEVEL
+ jr z, .checkLevel
+.checkTradeEvo
+ ld a, [wLinkState]
+ cp LINK_STATE_TRADING
+ jp nz, .nextEvoEntry1 ; if not trading, go to the next evolution entry
+ ld a, [hli] ; level requirement
+ ld b, a
+ ld a, [wLoadedMonLevel]
+ cp b ; is the mon's level greater than the evolution requirement?
+ jp c, Evolution_PartyMonLoop ; if so, go the next mon
+ jr .doEvolution
+.checkItemEvo
+ ld a, [hli]
+ ld b, a ; evolution item
+ ld a, [wcf91] ; this is supposed to be the last item used, but it is also used to hold species numbers
+ cp b ; was the evolution item in this entry used?
+ jp nz, .nextEvoEntry1 ; if not, go to the next evolution entry
+.checkLevel
+ ld a, [hli] ; level requirement
+ ld b, a
+ ld a, [wLoadedMonLevel]
+ cp b ; is the mon's level greater than the evolution requirement?
+ jp c, .nextEvoEntry2 ; if so, go the next evolution entry
+.doEvolution
+ ld [wCurEnemyLVL], a
+ ld a, 1
+ ld [wEvolutionOccurred], a
+ push hl
+ ld a, [hl]
+ ld [wEvoNewSpecies], a
+ ld a, [wWhichPokemon]
+ ld hl, wPartyMonNicks
+ call GetPartyMonName
+ call CopyStringToCF4B
+ ld hl, IsEvolvingText
+ call PrintText
+ ld c, 50
+ call DelayFrames
+ xor a
+ ld [H_AUTOBGTRANSFERENABLED], a
+ coord hl, 0, 0
+ lb bc, 12, 20
+ call ClearScreenArea
+ ld a, $1
+ ld [H_AUTOBGTRANSFERENABLED], a
+ ld a, $ff
+ ld [wUpdateSpritesEnabled], a
+ call ClearSprites
+ callab EvolveMon
+ jp c, CancelledEvolution
+ ld hl, EvolvedText
+ call PrintText
+ pop hl
+ ld a, [hl]
+ ld [wd0b5], a
+ ld [wLoadedMonSpecies], a
+ ld [wEvoNewSpecies], a
+ ld a, MONSTER_NAME
+ ld [wNameListType], a
+ ld a, BANK(TrainerNames) ; bank is not used for monster names
+ ld [wPredefBank], a
+ call GetName
+ push hl
+ ld hl, IntoText
+ call PrintText_NoCreatingTextBox
+ ld a, SFX_GET_ITEM_2
+ call PlaySoundWaitForCurrent
+ call WaitForSoundToFinish
+ ld c, 40
+ call DelayFrames
+ call ClearScreen
+ call RenameEvolvedMon
+ ld a, [wd11e]
+ push af
+ ld a, [wd0b5]
+ ld [wd11e], a
+ predef IndexToPokedex
+ ld a, [wd11e]
+ dec a
+ ld hl, BaseStats
+ ld bc, MonBaseStatsEnd - MonBaseStats
+ call AddNTimes
+ ld de, wMonHeader
+ call CopyData
+ ld a, [wd0b5]
+ ld [wMonHIndex], a
+ pop af
+ ld [wd11e], a
+ ld hl, wLoadedMonHPExp - 1
+ ld de, wLoadedMonStats
+ ld b, $1
+ call CalcStats
+ ld a, [wWhichPokemon]
+ ld hl, wPartyMon1
+ ld bc, wPartyMon2 - wPartyMon1
+ call AddNTimes
+ ld e, l
+ ld d, h
+ push hl
+ push bc
+ ld bc, wPartyMon1MaxHP - wPartyMon1
+ add hl, bc
+ ld a, [hli]
+ ld b, a
+ ld c, [hl]
+ ld hl, wLoadedMonMaxHP + 1
+ ld a, [hld]
+ sub c
+ ld c, a
+ ld a, [hl]
+ sbc b
+ ld b, a
+ ld hl, wLoadedMonHP + 1
+ ld a, [hl]
+ add c
+ ld [hld], a
+ ld a, [hl]
+ adc b
+ ld [hl], a
+ dec hl
+ pop bc
+ call CopyData
+ ld a, [wd0b5]
+ ld [wd11e], a
+ xor a
+ ld [wMonDataLocation], a
+ call LearnMoveFromLevelUp
+ pop hl
+ predef SetPartyMonTypes
+ ld a, [wIsInBattle]
+ and a
+ call z, Evolution_ReloadTilesetTilePatterns
+ predef IndexToPokedex
+ ld a, [wd11e]
+ dec a
+ ld c, a
+ ld b, FLAG_SET
+ ld hl, wPokedexOwned
+ push bc
+ call Evolution_FlagAction
+ pop bc
+ ld hl, wPokedexSeen
+ call Evolution_FlagAction
+ pop de
+ pop hl
+ ld a, [wLoadedMonSpecies]
+ ld [hl], a
+ push hl
+ ld l, e
+ ld h, d
+ jr .nextEvoEntry2
+
+.nextEvoEntry1
+ inc hl
+
+.nextEvoEntry2
+ inc hl
+ jp .evoEntryLoop
+
+.done
+ pop de
+ pop bc
+ pop hl
+ pop af
+ ld [hTilesetType], a
+ ld a, [wLinkState]
+ cp LINK_STATE_TRADING
+ ret z
+ ld a, [wIsInBattle]
+ and a
+ ret nz
+ ld a, [wEvolutionOccurred]
+ and a
+ call nz, PlayDefaultMusic
+ ret
+
+RenameEvolvedMon:
+; Renames the mon to its new, evolved form's standard name unless it had a
+; nickname, in which case the nickname is kept.
+ ld a, [wd0b5]
+ push af
+ ld a, [wMonHIndex]
+ ld [wd0b5], a
+ call GetName
+ pop af
+ ld [wd0b5], a
+ ld hl, wcd6d
+ ld de, wcf4b
+.compareNamesLoop
+ ld a, [de]
+ inc de
+ cp [hl]
+ inc hl
+ ret nz
+ cp "@"
+ jr nz, .compareNamesLoop
+ ld a, [wWhichPokemon]
+ ld bc, NAME_LENGTH
+ ld hl, wPartyMonNicks
+ call AddNTimes
+ push hl
+ call GetName
+ ld hl, wcd6d
+ pop de
+ jp CopyData
+
+CancelledEvolution:
+ ld hl, StoppedEvolvingText
+ call PrintText
+ call ClearScreen
+ pop hl
+ call Evolution_ReloadTilesetTilePatterns
+ jp Evolution_PartyMonLoop
+
+EvolvedText:
+ TX_FAR _EvolvedText
+ db "@"
+
+IntoText:
+ TX_FAR _IntoText
+ db "@"
+
+StoppedEvolvingText:
+ TX_FAR _StoppedEvolvingText
+ db "@"
+
+IsEvolvingText:
+ TX_FAR _IsEvolvingText
+ db "@"
+
+Evolution_ReloadTilesetTilePatterns:
+ ld a, [wLinkState]
+ cp LINK_STATE_TRADING
+ ret z
+ jp ReloadTilesetTilePatterns
+
+LearnMoveFromLevelUp:
+ ld hl, EvosMovesPointerTable
+ ld a, [wd11e] ; species
+ ld [wcf91], a
+ dec a
+ ld bc, 0
+ ld hl, EvosMovesPointerTable
+ add a
+ rl b
+ ld c, a
+ add hl, bc
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+.skipEvolutionDataLoop ; loop to skip past the evolution data, which comes before the move data
+ ld a, [hli]
+ and a ; have we reached the end of the evolution data?
+ jr nz, .skipEvolutionDataLoop ; if not, jump back up
+.learnSetLoop ; loop over the learn set until we reach a move that is learnt at the current level or the end of the list
+ ld a, [hli]
+ and a ; have we reached the end of the learn set?
+ jr z, .done ; if we've reached the end of the learn set, jump
+ ld b, a ; level the move is learnt at
+ ld a, [wCurEnemyLVL]
+ cp b ; is the move learnt at the mon's current level?
+ ld a, [hli] ; move ID
+ jr nz, .learnSetLoop
+ ld d, a ; ID of move to learn
+ ld a, [wMonDataLocation]
+ and a
+ jr nz, .next
+; If [wMonDataLocation] is 0 (PLAYER_PARTY_DATA), get the address of the mon's
+; current moves in party data. Every call to this function sets
+; [wMonDataLocation] to 0 because other data locations are not supported.
+; If it is not 0, this function will not work properly.
+ ld hl, wPartyMon1Moves
+ ld a, [wWhichPokemon]
+ ld bc, wPartyMon2 - wPartyMon1
+ call AddNTimes
+.next
+ ld b, NUM_MOVES
+.checkCurrentMovesLoop ; check if the move to learn is already known
+ ld a, [hli]
+ cp d
+ jr z, .done ; if already known, jump
+ dec b
+ jr nz, .checkCurrentMovesLoop
+ ld a, d
+ ld [wMoveNum], a
+ ld [wd11e], a
+ call GetMoveName
+ call CopyStringToCF4B
+ predef LearnMove
+.done
+ ld a, [wcf91]
+ ld [wd11e], a
+ ret
+
+; writes the moves a mon has at level [wCurEnemyLVL] to [de]
+; move slots are being filled up sequentially and shifted if all slots are full
+WriteMonMoves:
+ call GetPredefRegisters
+ push hl
+ push de
+ push bc
+ ld hl, EvosMovesPointerTable
+ ld b, 0
+ ld a, [wcf91] ; cur mon ID
+ dec a
+ add a
+ rl b
+ ld c, a
+ add hl, bc
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+.skipEvoEntriesLoop
+ ld a, [hli]
+ and a
+ jr nz, .skipEvoEntriesLoop
+ jr .firstMove
+.nextMove
+ pop de
+.nextMove2
+ inc hl
+.firstMove
+ ld a, [hli] ; read level of next move in learnset
+ and a
+ jp z, .done ; end of list
+ ld b, a
+ ld a, [wCurEnemyLVL]
+ cp b
+ jp c, .done ; mon level < move level (assumption: learnset is sorted by level)
+ ld a, [wLearningMovesFromDayCare]
+ and a
+ jr z, .skipMinLevelCheck
+ ld a, [wDayCareStartLevel]
+ cp b
+ jr nc, .nextMove2 ; min level >= move level
+
+.skipMinLevelCheck
+
+; check if the move is already known
+ push de
+ ld c, NUM_MOVES
+.alreadyKnowsCheckLoop
+ ld a, [de]
+ inc de
+ cp [hl]
+ jr z, .nextMove
+ dec c
+ jr nz, .alreadyKnowsCheckLoop
+
+; try to find an empty move slot
+ pop de
+ push de
+ ld c, NUM_MOVES
+.findEmptySlotLoop
+ ld a, [de]
+ and a
+ jr z, .writeMoveToSlot2
+ inc de
+ dec c
+ jr nz, .findEmptySlotLoop
+
+; no empty move slots found
+ pop de
+ push de
+ push hl
+ ld h, d
+ ld l, e
+ call WriteMonMoves_ShiftMoveData ; shift all moves one up (deleting move 1)
+ ld a, [wLearningMovesFromDayCare]
+ and a
+ jr z, .writeMoveToSlot
+
+; shift PP as well if learning moves from day care
+ push de
+ ld bc, wPartyMon1PP - (wPartyMon1Moves + 3)
+ add hl, bc
+ ld d, h
+ ld e, l
+ call WriteMonMoves_ShiftMoveData ; shift all move PP data one up
+ pop de
+
+.writeMoveToSlot
+ pop hl
+.writeMoveToSlot2
+ ld a, [hl]
+ ld [de], a
+ ld a, [wLearningMovesFromDayCare]
+ and a
+ jr z, .nextMove
+
+; write move PP value if learning moves from day care
+ push hl
+ ld a, [hl]
+ ld hl, wPartyMon1PP - wPartyMon1Moves
+ add hl, de
+ push hl
+ dec a
+ ld hl, Moves
+ ld bc, MoveEnd - Moves
+ call AddNTimes
+ ld de, wBuffer
+ ld a, BANK(Moves)
+ call FarCopyData
+ ld a, [wBuffer + 5]
+ pop hl
+ ld [hl], a
+ pop hl
+ jr .nextMove
+
+.done
+ pop bc
+ pop de
+ pop hl
+ ret
+
+; shifts all move data one up (freeing 4th move slot)
+WriteMonMoves_ShiftMoveData:
+ ld c, NUM_MOVES - 1
+.loop
+ inc de
+ ld a, [de]
+ ld [hli], a
+ dec c
+ jr nz, .loop
+ ret
+
+Evolution_FlagAction:
+ predef_jump FlagActionPredef
+
+INCLUDE "data/evos_moves.asm"
diff --git a/engine/pokemon/experience.asm b/engine/pokemon/experience.asm
new file mode 100755
index 00000000..3ee4b2a8
--- /dev/null
+++ b/engine/pokemon/experience.asm
@@ -0,0 +1,149 @@
+; calculates the level a mon should be based on its current exp
+CalcLevelFromExperience::
+ ld a, [wLoadedMonSpecies]
+ ld [wd0b5], a
+ call GetMonHeader
+ ld d, $1 ; init level to 1
+.loop
+ inc d ; increment level
+ call CalcExperience
+ push hl
+ ld hl, wLoadedMonExp + 2 ; current exp
+; compare exp needed for level d with current exp
+ ld a, [hExperience + 2]
+ ld c, a
+ ld a, [hld]
+ sub c
+ ld a, [hExperience + 1]
+ ld c, a
+ ld a, [hld]
+ sbc c
+ ld a, [hExperience]
+ ld c, a
+ ld a, [hl]
+ sbc c
+ pop hl
+ jr nc, .loop ; if exp needed for level d is not greater than exp, try the next level
+ dec d ; since the exp was too high on the last loop iteration, go back to the previous value and return
+ ret
+
+; calculates the amount of experience needed for level d
+CalcExperience::
+ ld a, [wMonHGrowthRate]
+ add a
+ add a
+ ld c, a
+ ld b, 0
+ ld hl, GrowthRateTable
+ add hl, bc
+ call CalcDSquared
+ ld a, d
+ ld [H_MULTIPLIER], a
+ call Multiply
+ ld a, [hl]
+ and $f0
+ swap a
+ ld [H_MULTIPLIER], a
+ call Multiply
+ ld a, [hli]
+ and $f
+ ld [H_DIVISOR], a
+ ld b, $4
+ call Divide
+ ld a, [H_QUOTIENT + 1]
+ push af
+ ld a, [H_QUOTIENT + 2]
+ push af
+ ld a, [H_QUOTIENT + 3]
+ push af
+ call CalcDSquared
+ ld a, [hl]
+ and $7f
+ ld [H_MULTIPLIER], a
+ call Multiply
+ ld a, [H_PRODUCT + 1]
+ push af
+ ld a, [H_PRODUCT + 2]
+ push af
+ ld a, [H_PRODUCT + 3]
+ push af
+ ld a, [hli]
+ push af
+ xor a
+ ld [H_MULTIPLICAND], a
+ ld [H_MULTIPLICAND + 1], a
+ ld a, d
+ ld [H_MULTIPLICAND + 2], a
+ ld a, [hli]
+ ld [H_MULTIPLIER], a
+ call Multiply
+ ld b, [hl]
+ ld a, [H_PRODUCT + 3]
+ sub b
+ ld [H_PRODUCT + 3], a
+ ld b, $0
+ ld a, [H_PRODUCT + 2]
+ sbc b
+ ld [H_PRODUCT + 2], a
+ ld a, [H_PRODUCT + 1]
+ sbc b
+ ld [H_PRODUCT + 1], a
+; The difference of the linear term and the constant term consists of 3 bytes
+; starting at H_PRODUCT + 1. Below, hExperience (an alias of that address) will
+; be used instead for the further work of adding or subtracting the squared
+; term and adding the cubed term.
+ pop af
+ and $80
+ jr nz, .subtractSquaredTerm ; check sign
+ pop bc
+ ld a, [hExperience + 2]
+ add b
+ ld [hExperience + 2], a
+ pop bc
+ ld a, [hExperience + 1]
+ adc b
+ ld [hExperience + 1], a
+ pop bc
+ ld a, [hExperience]
+ adc b
+ ld [hExperience], a
+ jr .addCubedTerm
+.subtractSquaredTerm
+ pop bc
+ ld a, [hExperience + 2]
+ sub b
+ ld [hExperience + 2], a
+ pop bc
+ ld a, [hExperience + 1]
+ sbc b
+ ld [hExperience + 1], a
+ pop bc
+ ld a, [hExperience]
+ sbc b
+ ld [hExperience], a
+.addCubedTerm
+ pop bc
+ ld a, [hExperience + 2]
+ add b
+ ld [hExperience + 2], a
+ pop bc
+ ld a, [hExperience + 1]
+ adc b
+ ld [hExperience + 1], a
+ pop bc
+ ld a, [hExperience]
+ adc b
+ ld [hExperience], a
+ ret
+
+; calculates d*d
+CalcDSquared:
+ xor a
+ ld [H_MULTIPLICAND], a
+ ld [H_MULTIPLICAND + 1], a
+ ld a, d
+ ld [H_MULTIPLICAND + 2], a
+ ld [H_MULTIPLIER], a
+ jp Multiply
+
+INCLUDE "data/growth_rates.asm"
diff --git a/engine/pokemon/learn_move.asm b/engine/pokemon/learn_move.asm
new file mode 100755
index 00000000..53c7f87e
--- /dev/null
+++ b/engine/pokemon/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/engine/pokemon/load_mon_data.asm b/engine/pokemon/load_mon_data.asm
new file mode 100644
index 00000000..480ab1ca
--- /dev/null
+++ b/engine/pokemon/load_mon_data.asm
@@ -0,0 +1,49 @@
+LoadMonData_::
+; Load monster [wWhichPokemon] from list [wMonDataLocation]:
+; 0: partymon
+; 1: enemymon
+; 2: boxmon
+; 3: daycaremon
+; Return monster id at wcf91 and its data at wLoadedMon.
+; Also load base stats at wMonHeader for convenience.
+
+ ld a, [wDayCareMonSpecies]
+ ld [wcf91], a
+ ld a, [wMonDataLocation]
+ cp DAYCARE_DATA
+ jr z, .GetMonHeader
+
+ ld a, [wWhichPokemon]
+ ld e, a
+ callab GetMonSpecies
+
+.GetMonHeader
+ ld a, [wcf91]
+ ld [wd0b5], a ; input for GetMonHeader
+ call GetMonHeader
+
+ ld hl, wPartyMons
+ ld bc, wPartyMon2 - wPartyMon1
+ ld a, [wMonDataLocation]
+ cp ENEMY_PARTY_DATA
+ jr c, .getMonEntry
+
+ ld hl, wEnemyMons
+ jr z, .getMonEntry
+
+ cp 2
+ ld hl, wBoxMons
+ ld bc, wBoxMon2 - wBoxMon1
+ jr z, .getMonEntry
+
+ ld hl, wDayCareMon
+ jr .copyMonData
+
+.getMonEntry
+ ld a, [wWhichPokemon]
+ call AddNTimes
+
+.copyMonData
+ ld de, wLoadedMon
+ ld bc, wPartyMon2 - wPartyMon1
+ jp CopyData
diff --git a/engine/pokemon/remove_mon.asm b/engine/pokemon/remove_mon.asm
new file mode 100644
index 00000000..6f7e91d6
--- /dev/null
+++ b/engine/pokemon/remove_mon.asm
@@ -0,0 +1,95 @@
+_RemovePokemon::
+ ld hl, wPartyCount
+ ld a, [wRemoveMonFromBox]
+ and a
+ jr z, .asm_7b74
+ ld hl, wNumInBox
+.asm_7b74
+ ld a, [hl]
+ dec a
+ ld [hli], a
+ ld a, [wWhichPokemon]
+ ld c, a
+ ld b, $0
+ add hl, bc
+ ld e, l
+ ld d, h
+ inc de
+.asm_7b81
+ ld a, [de]
+ inc de
+ ld [hli], a
+ inc a
+ jr nz, .asm_7b81
+ ld hl, wPartyMonOT
+ ld d, $5
+ ld a, [wRemoveMonFromBox]
+ and a
+ jr z, .asm_7b97
+ ld hl, wBoxMonOT
+ ld d, $13
+.asm_7b97
+ ld a, [wWhichPokemon]
+ call SkipFixedLengthTextEntries
+ ld a, [wWhichPokemon]
+ cp d
+ jr nz, .asm_7ba6
+ ld [hl], $ff
+ ret
+.asm_7ba6
+ ld d, h
+ ld e, l
+ ld bc, NAME_LENGTH
+ add hl, bc
+ ld bc, wPartyMonNicks
+ ld a, [wRemoveMonFromBox]
+ and a
+ jr z, .asm_7bb8
+ ld bc, wBoxMonNicks
+.asm_7bb8
+ call CopyDataUntil
+ ld hl, wPartyMons
+ ld bc, wPartyMon2 - wPartyMon1
+ ld a, [wRemoveMonFromBox]
+ and a
+ jr z, .asm_7bcd
+ ld hl, wBoxMons
+ ld bc, wBoxMon2 - wBoxMon1
+.asm_7bcd
+ ld a, [wWhichPokemon]
+ call AddNTimes
+ ld d, h
+ ld e, l
+ ld a, [wRemoveMonFromBox]
+ and a
+ jr z, .asm_7be4
+ ld bc, wBoxMon2 - wBoxMon1
+ add hl, bc
+ ld bc, wBoxMonOT
+ jr .asm_7beb
+.asm_7be4
+ ld bc, wPartyMon2 - wPartyMon1
+ add hl, bc
+ ld bc, wPartyMonOT
+.asm_7beb
+ call CopyDataUntil
+ ld hl, wPartyMonNicks
+ ld a, [wRemoveMonFromBox]
+ and a
+ jr z, .asm_7bfa
+ ld hl, wBoxMonNicks
+.asm_7bfa
+ ld bc, NAME_LENGTH
+ ld a, [wWhichPokemon]
+ call AddNTimes
+ ld d, h
+ ld e, l
+ ld bc, NAME_LENGTH
+ add hl, bc
+ ld bc, wPokedexOwned
+ ld a, [wRemoveMonFromBox]
+ and a
+ jr z, .asm_7c15
+ ld bc, wBoxMonNicksEnd
+.asm_7c15
+ jp CopyDataUntil
diff --git a/engine/pokemon/set_types.asm b/engine/pokemon/set_types.asm
new file mode 100755
index 00000000..e9235f13
--- /dev/null
+++ b/engine/pokemon/set_types.asm
@@ -0,0 +1,15 @@
+; updates the types of a party mon (pointed to in hl) to the ones of the mon specified in wd11e
+SetPartyMonTypes:
+ call GetPredefRegisters
+ ld bc, wPartyMon1Type - wPartyMon1 ; $5
+ add hl, bc
+ ld a, [wd11e]
+ ld [wd0b5], a
+ push hl
+ call GetMonHeader
+ pop hl
+ ld a, [wMonHType1]
+ ld [hli], a
+ ld a, [wMonHType2]
+ ld [hl], a
+ ret
diff --git a/engine/pokemon/status_ailments.asm b/engine/pokemon/status_ailments.asm
new file mode 100755
index 00000000..3da1fc43
--- /dev/null
+++ b/engine/pokemon/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/engine/pokemon/status_screen.asm b/engine/pokemon/status_screen.asm
new file mode 100755
index 00000000..20bee0ee
--- /dev/null
+++ b/engine/pokemon/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:
+ INCBIN "gfx/font/P.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