summaryrefslogtreecommitdiff
path: root/engine
diff options
context:
space:
mode:
Diffstat (limited to 'engine')
-rwxr-xr-xengine/battle/hidden_power.asm108
-rwxr-xr-xengine/battle/misc.asm219
-rwxr-xr-xengine/events/magikarp.asm314
-rwxr-xr-xengine/events/name_rater.asm219
-rwxr-xr-xengine/events/play_slow_cry.asm30
-rwxr-xr-xengine/gfx/load_font.asm103
-rwxr-xr-xengine/link/time_capsule.asm140
-rwxr-xr-xengine/link/time_capsule_2.asm39
-rwxr-xr-xengine/pokedex/new_pokedex_entry.asm50
-rwxr-xr-xengine/pokedex/unown_dex.asm49
10 files changed, 1271 insertions, 0 deletions
diff --git a/engine/battle/hidden_power.asm b/engine/battle/hidden_power.asm
new file mode 100755
index 00000000..f7abd71f
--- /dev/null
+++ b/engine/battle/hidden_power.asm
@@ -0,0 +1,108 @@
+HiddenPowerDamage:
+; Override Hidden Power's type and power based on the user's DVs.
+
+ ld hl, wBattleMonDVs
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .got_dvs
+ ld hl, wEnemyMonDVs
+.got_dvs
+
+; Power:
+
+; Take the top bit from each stat
+
+ ; Attack
+ ld a, [hl]
+ swap a
+ and %1000
+
+ ; Defense
+ ld b, a
+ ld a, [hli]
+ and %1000
+ srl a
+ or b
+
+ ; Speed
+ ld b, a
+ ld a, [hl]
+ swap a
+ and %1000
+ srl a
+ srl a
+ or b
+
+ ; Special
+ ld b, a
+ ld a, [hl]
+ and %1000
+ srl a
+ srl a
+ srl a
+ or b
+
+; Multiply by 5
+ ld b, a
+ add a
+ add a
+ add b
+
+; Add Special & 3
+ ld b, a
+ ld a, [hld]
+ and %0011
+ add b
+
+; Divide by 2 and add 30 + 1
+ srl a
+ add 30
+ inc a
+
+ ld d, a
+
+; Type:
+
+ ; Def & 3
+ ld a, [hl]
+ and %0011
+ ld b, a
+
+ ; + (Atk & 3) << 2
+ ld a, [hl]
+ and %0011 << 4
+ swap a
+ sla a
+ sla a
+ or b
+
+; Skip Normal
+ inc a
+
+; Skip Bird
+ cp BIRD
+ jr c, .done
+ inc a
+
+; Skip unused types
+ cp UNUSED_TYPES
+ jr c, .done
+ add SPECIAL - UNUSED_TYPES
+
+.done
+
+; Overwrite the current move type.
+ push af
+ ld a, BATTLE_VARS_MOVE_TYPE
+ call GetBattleVarAddr
+ pop af
+ ld [hl], a
+
+; Get the rest of the damage formula variables
+; based on the new type, but keep base power.
+ ld a, d
+ push af
+ farcall BattleCommand_DamageStats ; damagestats
+ pop af
+ ld d, a
+ ret
diff --git a/engine/battle/misc.asm b/engine/battle/misc.asm
new file mode 100755
index 00000000..ae178a4b
--- /dev/null
+++ b/engine/battle/misc.asm
@@ -0,0 +1,219 @@
+_DisappearUser:
+ xor a
+ ldh [hBGMapMode], a
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .player
+ call GetEnemyFrontpicCoords
+ jr .okay
+.player
+ call GetPlayerBackpicCoords
+.okay
+ call ClearBox
+ jr FinishAppearDisappearUser
+
+_AppearUserRaiseSub:
+ farcall BattleCommand_RaiseSubNoAnim
+ jr AppearUser
+
+_AppearUserLowerSub:
+ farcall BattleCommand_LowerSubNoAnim
+
+AppearUser:
+ xor a
+ ldh [hBGMapMode], a
+ ldh a, [hBattleTurn]
+ and a
+ jr z, .player
+ call GetEnemyFrontpicCoords
+ xor a
+ jr .okay
+.player
+ call GetPlayerBackpicCoords
+ ld a, $31
+.okay
+ ldh [hGraphicStartTile], a
+ predef PlaceGraphic
+FinishAppearDisappearUser:
+ ld a, $1
+ ldh [hBGMapMode], a
+ ret
+
+GetEnemyFrontpicCoords:
+ hlcoord 12, 0
+ lb bc, 7, 7
+ ret
+
+GetPlayerBackpicCoords:
+ hlcoord 2, 6
+ lb bc, 6, 6
+ ret
+
+DoWeatherModifiers:
+ ld de, WeatherTypeModifiers
+ ld a, [wBattleWeather]
+ ld b, a
+ ld a, [wCurType]
+ ld c, a
+
+.CheckWeatherType:
+ ld a, [de]
+ inc de
+ cp -1
+ jr z, .done_weather_types
+
+ cp b
+ jr nz, .NextWeatherType
+
+ ld a, [de]
+ cp c
+ jr z, .ApplyModifier
+
+.NextWeatherType:
+ inc de
+ inc de
+ jr .CheckWeatherType
+
+.done_weather_types
+ ld de, WeatherMoveModifiers
+
+ ld a, BATTLE_VARS_MOVE_EFFECT
+ call GetBattleVar
+ ld c, a
+
+.CheckWeatherMove:
+ ld a, [de]
+ inc de
+ cp -1
+ jr z, .done
+
+ cp b
+ jr nz, .NextWeatherMove
+
+ ld a, [de]
+ cp c
+ jr z, .ApplyModifier
+
+.NextWeatherMove:
+ inc de
+ inc de
+ jr .CheckWeatherMove
+
+.ApplyModifier:
+ xor a
+ ldh [hMultiplicand + 0], a
+ ld a, [wCurDamage]
+ ldh [hMultiplicand + 1], a
+ ld a, [wCurDamage + 1]
+ ldh [hMultiplicand + 2], a
+
+ inc de
+ ld a, [de]
+ ldh [hMultiplier], a
+
+ call Multiply
+
+ ld a, 10
+ ldh [hDivisor], a
+ ld b, 4
+ call Divide
+
+ ldh a, [hQuotient + 1]
+ and a
+ ld bc, -1
+ jr nz, .Update
+
+ ldh a, [hQuotient + 2]
+ ld b, a
+ ldh a, [hQuotient + 3]
+ ld c, a
+ or b
+ jr nz, .Update
+
+ ld bc, 1
+
+.Update:
+ ld a, b
+ ld [wCurDamage], a
+ ld a, c
+ ld [wCurDamage + 1], a
+
+.done
+ ret
+
+INCLUDE "data/battle/weather_modifiers.asm"
+
+DoBadgeTypeBoosts:
+ ld a, [wLinkMode]
+ and a
+ ret nz
+
+ ldh a, [hBattleTurn]
+ and a
+ ret nz
+
+ push de
+ push bc
+
+ ld hl, BadgeTypeBoosts
+
+ ld a, [wKantoBadges]
+ ld b, a
+ ld a, [wJohtoBadges]
+ ld c, a
+
+.CheckBadge:
+ ld a, [hl]
+ cp -1
+ jr z, .done
+
+ srl b
+ rr c
+ jr nc, .NextBadge
+
+ ld a, [wCurType]
+ cp [hl]
+ jr z, .ApplyBoost
+
+.NextBadge:
+ inc hl
+ jr .CheckBadge
+
+.ApplyBoost:
+ ld a, [wCurDamage]
+ ld h, a
+ ld d, a
+ ld a, [wCurDamage + 1]
+ ld l, a
+ ld e, a
+
+ srl d
+ rr e
+ srl d
+ rr e
+ srl d
+ rr e
+
+ ld a, e
+ or d
+ jr nz, .done_min
+ ld e, 1
+
+.done_min
+ add hl, de
+ jr nc, .Update
+
+ ld hl, $ffff
+
+.Update:
+ ld a, h
+ ld [wCurDamage], a
+ ld a, l
+ ld [wCurDamage + 1], a
+
+.done
+ pop bc
+ pop de
+ ret
+
+INCLUDE "data/types/badge_type_boosts.asm"
diff --git a/engine/events/magikarp.asm b/engine/events/magikarp.asm
new file mode 100755
index 00000000..035148b5
--- /dev/null
+++ b/engine/events/magikarp.asm
@@ -0,0 +1,314 @@
+CheckMagikarpLength:
+ ; Returns 3 if you select a Magikarp that beats the previous record.
+ ; Returns 2 if you select a Magikarp, but the current record is longer.
+ ; Returns 1 if you press B in the Pokemon selection menu.
+ ; Returns 0 if the Pokemon you select is not a Magikarp.
+
+ ; Let's start by selecting a Magikarp.
+ farcall SelectMonFromParty
+ jr c, .declined
+ ld a, [wCurPartySpecies]
+ cp MAGIKARP
+ jr nz, .not_magikarp
+
+ ; Now let's compute its length based on its DVs and ID.
+ ld a, [wCurPartyMon]
+ ld hl, wPartyMon1Species
+ ld bc, PARTYMON_STRUCT_LENGTH
+ call AddNTimes
+ push hl
+ ld bc, MON_DVS
+ add hl, bc
+ ld d, h
+ ld e, l
+ pop hl
+ ld bc, MON_ID
+ add hl, bc
+ ld b, h
+ ld c, l
+ call CalcMagikarpLength
+ call PrintMagikarpLength
+ ld hl, .MagikarpGuruMeasureText
+ call PrintText
+
+ ; Did we beat the record?
+ ld hl, wMagikarpLength
+ ld de, wBestMagikarpLengthFeet
+ ld c, 2
+ call CompareBytes
+ jr nc, .not_long_enough
+
+ ; NEW RECORD!!! Let's save that.
+ ld hl, wMagikarpLength
+ ld de, wBestMagikarpLengthFeet
+ ld a, [hli]
+ ld [de], a
+ inc de
+ ld a, [hl]
+ ld [de], a
+ inc de
+ ld a, [wCurPartyMon]
+ ld hl, wPartyMonOT
+ call SkipNames
+ call CopyBytes
+ ld a, MAGIKARPLENGTH_BEAT_RECORD
+ ld [wScriptVar], a
+ ret
+
+.not_long_enough
+ ld a, MAGIKARPLENGTH_TOO_SHORT
+ ld [wScriptVar], a
+ ret
+
+.declined
+ ld a, MAGIKARPLENGTH_REFUSED
+ ld [wScriptVar], a
+ ret
+
+.not_magikarp
+ xor a ; MAGIKARPLENGTH_NOT_MAGIKARP
+ ld [wScriptVar], a
+ ret
+
+.MagikarpGuruMeasureText:
+ text_far _MagikarpGuruMeasureText
+ text_end
+
+Magikarp_LoadFeetInchesChars:
+ ld hl, vTiles2 tile "′" ; $6e
+ ld de, .feetinchchars
+ lb bc, BANK(.feetinchchars), 2
+ call Request2bpp
+ ret
+
+.feetinchchars
+INCBIN "gfx/font/feet_inches.2bpp"
+
+PrintMagikarpLength:
+ call Magikarp_LoadFeetInchesChars
+ ld hl, wStringBuffer1
+ ld de, wMagikarpLength
+ lb bc, PRINTNUM_LEFTALIGN | 1, 2
+ call PrintNum
+ ld [hl], "′"
+ inc hl
+ ld de, wMagikarpLength + 1
+ lb bc, PRINTNUM_LEFTALIGN | 1, 2
+ call PrintNum
+ ld [hl], "″"
+ inc hl
+ ld [hl], "@"
+ ret
+
+CalcMagikarpLength:
+; Return Magikarp's length (in feet and inches) at wMagikarpLength (big endian).
+;
+; input:
+; de: wEnemyMonDVs
+; bc: wPlayerID
+
+; This function is poorly commented.
+
+; In short, it generates a value between 190 and 1786 using
+; a Magikarp's DVs and its trainer ID. This value is further
+; filtered in LoadEnemyMon to make longer Magikarp even rarer.
+
+; The value is generated from a lookup table.
+; The index is determined by the dv xored with the player's trainer id.
+
+; bc = rrc(dv[0]) ++ rrc(dv[1]) ^ rrc(id)
+
+; if bc < 10: [wMagikarpLength] = c + 190
+; if bc ≥ $ff00: [wMagikarpLength] = c + 1370
+; else: [wMagikarpLength] = z * 100 + (bc - x) / y
+
+; X, Y, and Z depend on the value of b as follows:
+
+; if b = 0: x = 310, y = 2, z = 3
+; if b = 1: x = 710, y = 4, z = 4
+; if b = 2-9: x = 2710, y = 20, z = 5
+; if b = 10-29: x = 7710, y = 50, z = 6
+; if b = 30-68: x = 17710, y = 100, z = 7
+; if b = 69-126: x = 32710, y = 150, z = 8
+; if b = 127-185: x = 47710, y = 150, z = 9
+; if b = 186-224: x = 57710, y = 100, z = 10
+; if b = 225-243: x = 62710, y = 50, z = 11
+; if b = 244-251: x = 64710, y = 20, z = 12
+; if b = 252-253: x = 65210, y = 5, z = 13
+; if b = 254: x = 65410, y = 2, z = 14
+
+ ; bc = rrc(dv[0]) ++ rrc(dv[1]) ^ rrc(id)
+
+ ; id
+ ld h, b
+ ld l, c
+ ld a, [hli]
+ ld b, a
+ ld c, [hl]
+ rrc b
+ rrc c
+
+ ; dv
+ ld a, [de]
+ inc de
+ rrca
+ rrca
+ xor b
+ ld b, a
+
+ ld a, [de]
+ rrca
+ rrca
+ xor c
+ ld c, a
+
+ ; if bc < 10:
+ ; de = bc + 190
+ ; break
+
+ ld a, b
+ and a
+ jr nz, .no
+ ld a, c
+ cp 10
+ jr nc, .no
+
+ ld hl, 190
+ add hl, bc
+ ld d, h
+ ld e, l
+ jr .done
+
+.no
+
+ ld hl, MagikarpLengths
+ ld a, 2
+ ld [wTempByteValue], a
+
+.read
+ ld a, [hli]
+ ld e, a
+ ld a, [hli]
+ ld d, a
+ call .BCLessThanDE
+ jr nc, .next
+
+ ; c = (bc - de) / [hl]
+ call .BCMinusDE
+ ld a, b
+ ldh [hDividend + 0], a
+ ld a, c
+ ldh [hDividend + 1], a
+ ld a, [hl]
+ ldh [hDivisor], a
+ ld b, 2
+ call Divide
+ ldh a, [hQuotient + 3]
+ ld c, a
+
+ ; de = c + 100 × (2 + i)
+ xor a
+ ldh [hMultiplicand + 0], a
+ ldh [hMultiplicand + 1], a
+ ld a, 100
+ ldh [hMultiplicand + 2], a
+ ld a, [wTempByteValue]
+ ldh [hMultiplier], a
+ call Multiply
+ ld b, 0
+ ldh a, [hProduct + 3]
+ add c
+ ld e, a
+ ldh a, [hProduct + 2]
+ adc b
+ ld d, a
+ jr .done
+
+.next
+ inc hl ; align to next triplet
+ ld a, [wTempByteValue]
+ inc a
+ ld [wTempByteValue], a
+ cp 16
+ jr c, .read
+
+ call .BCMinusDE
+ ld hl, 1600
+ add hl, bc
+ ld d, h
+ ld e, l
+
+.done
+ ; convert from mm to feet and inches
+ ; in = mm / 25.4
+ ; ft = in / 12
+
+ ; hl = de × 10
+ ld h, d
+ ld l, e
+ add hl, hl
+ add hl, hl
+ add hl, de
+ add hl, hl
+
+ ; hl = hl / 254
+ ld de, -254
+ ld a, -1
+.div_254
+ inc a
+ add hl, de
+ jr c, .div_254
+
+ ; d, e = hl / 12, hl % 12
+ ld d, 0
+.mod_12
+ cp 12
+ jr c, .ok
+ sub 12
+ inc d
+ jr .mod_12
+.ok
+ ld e, a
+
+ ld hl, wMagikarpLength
+ ld [hl], d ; ft
+ inc hl
+ ld [hl], e ; in
+ ret
+
+.BCLessThanDE:
+; Intention: Return bc < de.
+; Reality: Return b < d.
+ ld a, b
+ cp d
+ ret c
+ ret nc ; whoops
+ ld a, c
+ cp e
+ ret
+
+.BCMinusDE:
+; bc -= de
+ ld a, c
+ sub e
+ ld c, a
+ ld a, b
+ sbc d
+ ld b, a
+ ret
+
+INCLUDE "data/events/magikarp_lengths.asm"
+
+MagikarpHouseSign:
+ ld a, [wBestMagikarpLengthFeet]
+ ld [wMagikarpLength], a
+ ld a, [wBestMagikarpLengthInches]
+ ld [wMagikarpLength + 1], a
+ call PrintMagikarpLength
+ ld hl, .KarpGuruRecordText
+ call PrintText
+ ret
+
+.KarpGuruRecordText:
+ text_far _KarpGuruRecordText
+ text_end
diff --git a/engine/events/name_rater.asm b/engine/events/name_rater.asm
new file mode 100755
index 00000000..e2b24c4c
--- /dev/null
+++ b/engine/events/name_rater.asm
@@ -0,0 +1,219 @@
+_NameRater:
+; Introduce himself
+ ld hl, NameRaterHelloText
+ call PrintText
+ call YesNoBox
+ jp c, .cancel
+; Select a Pokemon from your party
+ ld hl, NameRaterWhichMonText
+ call PrintText
+ farcall SelectMonFromParty
+ jr c, .cancel
+; He can't rename an egg...
+ ld a, [wCurPartySpecies]
+ cp EGG
+ jr z, .egg
+; ... or a Pokemon you got from a trade.
+ call GetCurNick
+ call CheckIfMonIsYourOT
+ jr c, .traded
+; This name is good, but we can do better. How about it?
+ ld hl, NameRaterBetterNameText
+ call PrintText
+ call YesNoBox
+ jr c, .cancel
+; What name shall I give it then?
+ ld hl, NameRaterWhatNameText
+ call PrintText
+; Load the new nickname into wStringBuffer2
+ xor a ; PARTYMON
+ ld [wMonType], a
+ ld a, [wCurPartySpecies]
+ ld [wNamedObjectIndexBuffer], a
+ ld [wCurSpecies], a
+ call GetBaseData
+ ld b, NAME_MON
+ ld de, wStringBuffer2
+ farcall _NamingScreen
+; If the new name is empty, treat it as unchanged.
+ call IsNewNameEmpty
+ ld hl, NameRaterSameNameText
+ jr c, .samename
+; If the new name is the same as the old name, treat it as unchanged.
+ call CompareNewToOld
+ ld hl, NameRaterSameNameText
+ jr c, .samename
+; Copy the new name from wStringBuffer2
+ ld hl, wPartyMonNicknames
+ ld bc, MON_NAME_LENGTH
+ ld a, [wCurPartyMon]
+ call AddNTimes
+ ld e, l
+ ld d, h
+ ld hl, wStringBuffer2
+ ld bc, MON_NAME_LENGTH
+ call CopyBytes
+ ld hl, NameRaterFinishedText
+
+.samename
+ push hl
+ call GetCurNick
+ ld hl, NameRaterNamedText
+ call PrintText
+ pop hl
+ jr .done
+
+.traded
+ ld hl, NameRaterPerfectNameText
+ jr .done
+
+.cancel
+ ld hl, NameRaterComeAgainText
+ jr .done
+
+.egg
+ ld hl, NameRaterEggText
+
+.done
+ call PrintText
+ ret
+
+CheckIfMonIsYourOT:
+; Checks to see if the partymon loaded in [wCurPartyMon] has the different OT as you. Returns carry if not.
+ ld hl, wPartyMonOT
+ ld bc, NAME_LENGTH
+ ld a, [wCurPartyMon]
+ call AddNTimes
+ ld de, wPlayerName
+ ld c, NAME_LENGTH
+ call .loop
+ jr c, .nope
+
+ ld hl, wPartyMon1ID
+ ld bc, PARTYMON_STRUCT_LENGTH
+ ld a, [wCurPartyMon]
+ call AddNTimes
+ ld de, wPlayerID
+ ld c, 2 ; number of bytes in which your ID is stored
+.loop
+ ld a, [de]
+ cp [hl]
+ jr nz, .nope
+ inc hl
+ inc de
+ dec c
+ jr nz, .loop
+ and a
+ ret
+
+.nope
+ scf
+ ret
+
+IsNewNameEmpty:
+; Checks to see if the nickname loaded in wStringBuffer2 is empty. If so, return carry.
+ ld hl, wStringBuffer2
+ ld c, MON_NAME_LENGTH - 1
+.loop
+ ld a, [hli]
+ cp "@"
+ jr z, .terminator
+ cp " "
+ jr nz, .nonspace
+ dec c
+ jr nz, .loop
+
+.terminator
+ scf
+ ret
+
+.nonspace
+ and a
+ ret
+
+CompareNewToOld:
+; Compares the nickname in wStringBuffer2 to the previous nickname. If they are the same, return carry.
+ ld hl, wPartyMonNicknames
+ ld bc, MON_NAME_LENGTH
+ ld a, [wCurPartyMon]
+ call AddNTimes
+ push hl
+ call GetNicknameLength
+ ld b, c
+ ld hl, wStringBuffer2
+ call GetNicknameLength
+ pop hl
+ ld a, c
+ cp b
+ jr nz, .different
+ ld de, wStringBuffer2
+.loop
+ ld a, [de]
+ cp "@"
+ jr z, .terminator
+ cp [hl]
+ jr nz, .different
+ inc hl
+ inc de
+ jr .loop
+
+.different
+ and a
+ ret
+
+.terminator
+ scf
+ ret
+
+GetNicknameLength:
+; Gets the length of the name starting at hl and returns it in c.
+ ld c, 0
+.loop
+ ld a, [hli]
+ cp "@"
+ ret z
+ inc c
+ ld a, c
+ cp MON_NAME_LENGTH - 1
+ jr nz, .loop
+ ret
+
+NameRaterHelloText:
+ text_far _NameRaterHelloText
+ text_end
+
+NameRaterWhichMonText:
+ text_far _NameRaterWhichMonText
+ text_end
+
+NameRaterBetterNameText:
+ text_far _NameRaterBetterNameText
+ text_end
+
+NameRaterWhatNameText:
+ text_far _NameRaterWhatNameText
+ text_end
+
+NameRaterFinishedText:
+ text_far _NameRaterFinishedText
+ text_end
+
+NameRaterComeAgainText:
+ text_far _NameRaterComeAgainText
+ text_end
+
+NameRaterPerfectNameText:
+ text_far _NameRaterPerfectNameText
+ text_end
+
+NameRaterEggText:
+ text_far _NameRaterEggText
+ text_end
+
+NameRaterSameNameText:
+ text_far _NameRaterSameNameText
+ text_end
+
+NameRaterNamedText:
+ text_far _NameRaterNamedText
+ text_end
diff --git a/engine/events/play_slow_cry.asm b/engine/events/play_slow_cry.asm
new file mode 100755
index 00000000..6839b396
--- /dev/null
+++ b/engine/events/play_slow_cry.asm
@@ -0,0 +1,30 @@
+PlaySlowCry:
+ ld a, [wScriptVar]
+ call LoadCry
+ jr c, .done
+
+ ld hl, wCryPitch
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld bc, -$140
+ add hl, bc
+ ld a, l
+ ld [wCryPitch], a
+ ld a, h
+ ld [wCryPitch + 1], a
+ ld hl, wCryLength
+ ld a, [hli]
+ ld h, [hl]
+ ld l, a
+ ld bc, $60
+ add hl, bc
+ ld a, l
+ ld [wCryLength], a
+ ld a, h
+ ld [wCryLength + 1], a
+ farcall _PlayCry
+ call WaitSFX
+
+.done
+ ret
diff --git a/engine/gfx/load_font.asm b/engine/gfx/load_font.asm
new file mode 100755
index 00000000..4e732211
--- /dev/null
+++ b/engine/gfx/load_font.asm
@@ -0,0 +1,103 @@
+_LoadStandardFont::
+ ld de, Font
+ ld hl, vTiles1
+ lb bc, BANK(Font), 128 ; "A" to "9"
+ jp Get1bpp
+
+_LoadFontsExtra::
+ ld de, FontsExtra_SolidBlackGFX
+ ld hl, vTiles2 tile "■" ; $60
+ lb bc, BANK(FontsExtra_SolidBlackGFX), 2
+ call Get1bpp
+ ld de, PokegearPhoneIconGFX
+ ld hl, vTiles2 tile "☎" ; $62
+ lb bc, BANK(PokegearPhoneIconGFX), 1
+ call Get2bpp
+ ld de, FontExtra + 3 tiles ; "<BOLD_D>"
+ ld hl, vTiles2 tile "<BOLD_D>"
+ lb bc, BANK(FontExtra), 22 ; "<BOLD_D>" to "ぉ"
+ call Get2bpp
+ jr LoadFrame
+
+_LoadFontsBattleExtra::
+ ld de, FontBattleExtra
+ ld hl, vTiles2 tile $60
+ lb bc, BANK(FontBattleExtra), 25
+ call Get2bpp
+ jr LoadFrame
+
+LoadFrame:
+ ld a, [wTextboxFrame]
+ maskbits NUM_FRAMES
+ ld bc, 6 * LEN_1BPP_TILE
+ ld hl, Frames
+ call AddNTimes
+ ld d, h
+ ld e, l
+ ld hl, vTiles2 tile "┌" ; $79
+ lb bc, BANK(Frames), 6 ; "┌" to "┘"
+ call Get1bpp
+ ld hl, vTiles2 tile " " ; $7f
+ ld de, TextboxSpaceGFX
+ lb bc, BANK(TextboxSpaceGFX), 1
+ call Get1bpp
+ ret
+
+LoadBattleFontsHPBar:
+ ld de, FontBattleExtra
+ ld hl, vTiles2 tile $60
+ lb bc, BANK(FontBattleExtra), 12
+ call Get2bpp
+ ld hl, vTiles2 tile $70
+ ld de, FontBattleExtra + 16 tiles ; "<DO>"
+ lb bc, BANK(FontBattleExtra), 3 ; "<DO>" to "『"
+ call Get2bpp
+ call LoadFrame
+
+LoadHPBar:
+ ld de, EnemyHPBarBorderGFX
+ ld hl, vTiles2 tile $6c
+ lb bc, BANK(EnemyHPBarBorderGFX), 4
+ call Get1bpp
+ ld de, HPExpBarBorderGFX
+ ld hl, vTiles2 tile $73
+ lb bc, BANK(HPExpBarBorderGFX), 6
+ call Get1bpp
+ ld de, ExpBarGFX
+ ld hl, vTiles2 tile $55
+ lb bc, BANK(ExpBarGFX), 9
+ call Get2bpp
+ ret
+
+StatsScreen_LoadFont::
+ call _LoadFontsBattleExtra
+ ld de, EnemyHPBarBorderGFX
+ ld hl, vTiles2 tile $6c
+ lb bc, BANK(EnemyHPBarBorderGFX), 4
+ call Get1bpp
+ ld de, HPExpBarBorderGFX
+ ld hl, vTiles2 tile $78
+ lb bc, BANK(HPExpBarBorderGFX), 1
+ call Get1bpp
+ ld de, HPExpBarBorderGFX + 3 * LEN_1BPP_TILE
+ ld hl, vTiles2 tile $76
+ lb bc, BANK(HPExpBarBorderGFX), 2
+ call Get1bpp
+ ld de, ExpBarGFX
+ ld hl, vTiles2 tile $55
+ lb bc, BANK(ExpBarGFX), 8
+ call Get2bpp
+LoadStatsScreenPageTilesGFX:
+ ld de, StatsScreenPageTilesGFX
+ ld hl, vTiles2 tile $31
+ lb bc, BANK(StatsScreenPageTilesGFX), 17
+ call Get2bpp
+ ret
+
+LoadFontsBattleLevel:
+ ld de, FontBattleExtra + 14 tiles
+ ld hl, vTiles2 tile "<LV>" ; $6e
+ lb bc, BANK(FontBattleExtra), 1
+ jp Get2bpp
+
+INCLUDE "gfx/font.asm"
diff --git a/engine/link/time_capsule.asm b/engine/link/time_capsule.asm
new file mode 100755
index 00000000..adbb3300
--- /dev/null
+++ b/engine/link/time_capsule.asm
@@ -0,0 +1,140 @@
+; These functions seem to be related to backwards compatibility
+
+ValidateOTTrademon:
+ ld a, [wceee]
+ ld hl, wOTPartyMon1Species
+ call GetPartyLocation
+ push hl
+ ld a, [wceee]
+ inc a
+ ld c, a
+ ld b, 0
+ ld hl, wOTPartyCount
+ add hl, bc
+ ld a, [hl]
+ pop hl
+ cp EGG
+ jr z, .matching_or_egg
+ cp [hl]
+ jr nz, .abnormal
+
+.matching_or_egg
+ ld b, h
+ ld c, l
+ ld hl, MON_LEVEL
+ add hl, bc
+ ld a, [hl]
+ cp MAX_LEVEL + 1
+ jr nc, .abnormal
+ ld a, [wLinkMode]
+ cp LINK_TIMECAPSULE
+ jr nz, .normal
+ ld hl, wOTPartySpecies
+ ld a, [wceee]
+ ld c, a
+ ld b, 0
+ add hl, bc
+ ld a, [hl]
+
+ ; Magnemite and Magneton's types changed
+ ; from Electric to Electric/Steel.
+ cp MAGNEMITE
+ jr z, .normal
+ cp MAGNETON
+ jr z, .normal
+
+ ld [wCurSpecies], a
+ call GetBaseData
+ ld hl, wLinkOTPartyMonTypes
+ add hl, bc
+ add hl, bc
+ ld a, [wBaseType1]
+ cp [hl]
+ jr nz, .abnormal
+ inc hl
+ ld a, [wBaseType2]
+ cp [hl]
+ jr nz, .abnormal
+
+.normal
+ and a
+ ret
+
+.abnormal
+ scf
+ ret
+
+Functionfb6ed:
+ ld a, [wceed]
+ ld d, a
+ ld a, [wPartyCount]
+ ld b, a
+ ld c, $0
+.loop
+ ld a, c
+ cp d
+ jr z, .next
+ push bc
+ ld a, c
+ ld hl, wPartyMon1HP
+ call GetPartyLocation
+ pop bc
+ ld a, [hli]
+ or [hl]
+ jr nz, .done
+
+.next
+ inc c
+ dec b
+ jr nz, .loop
+ ld a, [wceee]
+ ld hl, wOTPartyMon1HP
+ call GetPartyLocation
+ ld a, [hli]
+ or [hl]
+ jr nz, .done
+ scf
+ ret
+
+.done
+ and a
+ ret
+
+PlaceTradePartnerNamesAndParty:
+ hlcoord 2, 0
+ ld de, wPlayerName
+ call PlaceString
+ hlcoord 2, 8
+ ld de, wOTPlayerName
+ call PlaceString
+ hlcoord 7, 1
+ ld de, wPartySpecies
+ call .PlaceSpeciesNames
+ hlcoord 7, 9
+ ld de, wOTPartySpecies
+.PlaceSpeciesNames:
+ ld c, 0
+.loop
+ ld a, [de]
+ cp -1
+ ret z
+ ld [wNamedObjectIndexBuffer], a
+ push bc
+ push hl
+ push de
+ push hl
+ ld a, c
+ ldh [hProduct], a
+ call GetPokemonName
+ pop hl
+ call PlaceString
+ pop de
+ inc de
+ pop hl
+ ld bc, SCREEN_WIDTH
+ add hl, bc
+ pop bc
+ inc c
+ jr .loop
+
+INCLUDE "data/pokemon/gen1_base_special.asm"
diff --git a/engine/link/time_capsule_2.asm b/engine/link/time_capsule_2.asm
new file mode 100755
index 00000000..6640f6b0
--- /dev/null
+++ b/engine/link/time_capsule_2.asm
@@ -0,0 +1,39 @@
+ConvertMon_2to1:
+; Takes the Gen 2 Pokemon number stored in wTempSpecies,
+; finds it in the Pokered_MonIndices table,
+; and returns its index in wTempSpecies.
+ push bc
+ push hl
+ ld a, [wTempSpecies]
+ ld b, a
+ ld c, 0
+ ld hl, Pokered_MonIndices
+.loop
+ inc c
+ ld a, [hli]
+ cp b
+ jr nz, .loop
+ ld a, c
+ ld [wTempSpecies], a
+ pop hl
+ pop bc
+ ret
+
+ConvertMon_1to2:
+; Takes the Gen 1 Pokemon number stored in wTempSpecies
+; and returns the corresponding value from Pokered_MonIndices in wTempSpecies.
+ push bc
+ push hl
+ ld a, [wTempSpecies]
+ dec a
+ ld hl, Pokered_MonIndices
+ ld b, 0
+ ld c, a
+ add hl, bc
+ ld a, [hl]
+ ld [wTempSpecies], a
+ pop hl
+ pop bc
+ ret
+
+INCLUDE "data/pokemon/gen1_order.asm"
diff --git a/engine/pokedex/new_pokedex_entry.asm b/engine/pokedex/new_pokedex_entry.asm
new file mode 100755
index 00000000..844253c5
--- /dev/null
+++ b/engine/pokedex/new_pokedex_entry.asm
@@ -0,0 +1,50 @@
+NewPokedexEntry:
+ ldh a, [hMapAnims]
+ push af
+ xor a
+ ldh [hMapAnims], a
+ call LowVolume
+ call ClearBGPalettes
+ call ClearTilemap
+ call UpdateSprites
+ call ClearSprites
+ ld a, [wPokedexStatus]
+ push af
+ ldh a, [hSCX]
+ add POKEDEX_SCX
+ ldh [hSCX], a
+ xor a
+ ld [wPokedexStatus], a
+ farcall _NewPokedexEntry
+ call WaitPressAorB_BlinkCursor
+ ld a, 1 ; page 2
+ ld [wPokedexStatus], a
+ farcall DisplayDexEntry
+ call WaitPressAorB_BlinkCursor
+ pop af
+ ld [wPokedexStatus], a
+ call MaxVolume
+ call RotateThreePalettesRight
+ ldh a, [hSCX]
+ add -POKEDEX_SCX
+ ldh [hSCX], a
+ call .ReturnFromDexRegistration
+ pop af
+ ldh [hMapAnims], a
+ ret
+
+.ReturnFromDexRegistration:
+ call ClearTilemap
+ call LoadFontsExtra
+ call LoadStandardFont
+ farcall Pokedex_PlaceFrontpicTopLeftCorner
+ call WaitBGMap2
+ farcall GetEnemyMonDVs
+ ld a, [hli]
+ ld [wTempMonDVs], a
+ ld a, [hl]
+ ld [wTempMonDVs + 1], a
+ ld b, SCGB_TRAINER_OR_MON_FRONTPIC_PALS
+ call GetSGBLayout
+ call SetPalettes
+ ret
diff --git a/engine/pokedex/unown_dex.asm b/engine/pokedex/unown_dex.asm
new file mode 100755
index 00000000..1b66a4ac
--- /dev/null
+++ b/engine/pokedex/unown_dex.asm
@@ -0,0 +1,49 @@
+UpdateUnownDex:
+ ld a, [wUnownLetter]
+ ld c, a
+ ld b, NUM_UNOWN
+ ld hl, wUnownDex
+.loop
+ ld a, [hli]
+ and a
+ jr z, .done
+ cp c
+ ret z
+ dec b
+ jr nz, .loop
+ ret
+
+.done
+ dec hl
+ ld [hl], c
+ ret
+
+PrintUnownWord:
+ hlcoord 4, 15
+ ld bc, 12
+ ld a, " "
+ call ByteFill
+ ld a, [wDexCurUnownIndex]
+ ld e, a
+ ld d, 0
+ ld hl, wUnownDex
+ add hl, de
+ ld a, [hl]
+ ld e, a
+ ld d, 0
+ ld hl, UnownWords
+ add hl, de
+ add hl, de
+ ld a, [hli]
+ ld e, a
+ ld d, [hl]
+ hlcoord 4, 15
+.loop
+ ld a, [de]
+ cp -1
+ ret z
+ inc de
+ ld [hli], a
+ jr .loop
+
+INCLUDE "data/pokemon/unown_words.asm"