Files
gen1recomp/src/ui/gen2/HallOfFame.lua
T
1jamie df0be1cba6 Add Gold save-editor support, rearrange the launcher, and ship a patch-notes modal.
The save editor now routes Gold vs RBY through a generation adapter so boxes, items, events, and maps write the right fields. The launcher layout is shuffled a bit, and patch notes can come from the updater, a packed file, or the iOS sidecar.
2026-08-14 18:48:06 -05:00

677 lines
24 KiB
Lua

-- The Hall of Fame (pokegold engine/events/halloffame.asm), both halves of it:
-- the induction the champion script runs, and the roster viewer the PC opens.
--
-- Two entry points on the cart, one screen here because they share every
-- drawing routine:
--
-- AnimateHallOfFame each party member enters (AnimateHOFMonEntrance),
-- gets DisplayHOFMon plus "New Hall of Famer!", its cry
-- and 180 frames, then HOF_AnimatePlayerPic ends on the
-- player's own card
-- _HallOfFamePC LoadHOFTeam walks the roster newest first and
-- DisplayHOFMon shows one mon at a time; A is next mon,
-- START is next team, B leaves
--
-- THE ENTRANCE IS A SCROLL, not a sprite move. AnimateHOFMonEntrance blanks
-- the whole tilemap, lays ONE pic into it, and then animates hSCX and hSCY:
-- because nothing else is on screen, scrolling the background IS sliding the
-- pic, and that is how it is drawn here. The two loops are exact --
--
-- HOF_SlideBackpic hSCX $90, +4 a frame until it reads $70 (56 frames,
-- the long way round the byte)
-- HOF_SlideFrontpic hSCX -2 a frame until it reads 0
--
-- -- so the mon's BACK pic sweeps across the screen and off the left, and then
-- the pan reverses and its FRONT pic comes back in from the left and settles
-- at hlcoord 6, 5. hSCY goes $d0 -> $00 between the two, which is the same
-- wrap-around trick vertically.
--
-- COORDINATES are taken literally off the hlcoord lines, never laid out by
-- eye, and the placements are built as data so tests/gen2_halloffame_test.lua
-- can assert them without a graphics device (the same shape
-- src/ui/gen2/SummaryMenu.lua uses).
--
-- WHAT THE CACHE DOES NOT HAVE. GetTrainerPic for TRAINER_CLASS CAL -- the
-- 7x7 picture of the player HOF_AnimatePlayerPic ends on -- is not extracted.
-- The player's own 5x7 portrait from the trainer card is, and it is the same
-- character, so that stands in and is padded into the 7x7 block the way
-- PlaceGraphic would. ProfOaksPCRating, which the cart prints into the bottom
-- box afterwards, needs Oak's PC (still a stub in src/script/gen2/Specials.lua)
-- and so the box is drawn empty, exactly as it is before that farcall.
local Assets = require("src.render.Assets")
local Chrome = require("src.ui.gen2.Chrome")
local CommonText = require("src.core.gen2.CommonText")
local Core = require("src.core.gen2.HallOfFame")
local Font = require("src.render.Font")
local GbcPalette = require("src.render.GbcPalette")
local Music = require("src.core.Music")
local Palettes = require("src.world.gen2.Palettes")
local Sound = require("src.core.Sound")
local TileSheet = require("src.ui.gen2.TileSheet")
local Unown = require("src.core.gen2.Unown")
local HallOfFame = {}
HallOfFame.__index = HallOfFame
HallOfFame.isOpaque = true
local SCREEN_W, SCREEN_H = 160, 144
-- Where each pic lands. AnimateHOFMonEntrance puts the backpic at hlcoord
-- 6, 6 and the frontpic at hlcoord 6, 5; HOF_AnimatePlayerPic puts the
-- player's backpic at 6, 6 and the trainer pic at 12, 5.
local BACKPIC_X, BACKPIC_Y = 6, 6
local FRONTPIC_X, FRONTPIC_Y = 6, 5
local TRAINERPIC_X, TRAINERPIC_Y = 12, 5
local PIC_TILES = 7
-- The scroll registers the two slide loops walk.
local SCY_START = 0xd0
local BACKPIC_SCX_START, BACKPIC_SCX_END, BACKPIC_STEP = 0x90, 0x70, 4
local FRONTPIC_STEP = 2
local TRAINER_SCX_START = 0xc0
-- .DisplayNewHallOfFamer: `ld c, 180 / call DelayFrames` after the cry.
local FAMER_FRAMES = 180
-- AnimateHallOfFame .done: RotateThreePalettesRight, then `ld c, 8`.
local END_FRAMES = 8
local HOF_MUSIC = "Music_HallOfFame"
-- The three strings the two modes print at hlcoord 1, 2. Their leading
-- spaces are load bearing: PrintNum writes the count over the first of them
-- at hlcoord 2, 2, so " -Time Famer" becomes " 12-Time Famer".
HallOfFame.NEW_FAMER = "New Hall of Famer!"
HallOfFame.TIME_FAMER = " -Time Famer"
HallOfFame.HOF_MASTER = " HOF Master!"
-- ...and the labels they live under in the cart. All three are plain
-- `db "…@"` inside engine/events/halloffame.asm rather than text streams, so
-- nothing but the routine that prints them names them.
local LABELS = {
NEW_FAMER = "AnimateHallOfFame.String_NewHallOfFamer",
TIME_FAMER = "_HallOfFamePC.TimeFamer",
HOF_MASTER = "_HallOfFamePC.HOFMaster",
}
HallOfFame.LABELS = LABELS
-- PadFrontpic centres a 5x5 or 6x6 pic inside the 7x7 block, same table the
-- stats screen and the dex use.
local PIC_PAD = { [7] = { 0, 0 }, [6] = { 1, 1 }, [5] = { 1, 2 } }
local WHITE = { 255, 255, 255 }
--------------------------------------------------------------------------
-- Placements
--------------------------------------------------------------------------
local function put(list, text, x, y)
if text == nil then return list end
list[#list + 1] = { text = tostring(text), x = x, y = y }
return list
end
-- The text a placement list writes at a coordinate, or nil.
function HallOfFame.at(placements, x, y)
for _, entry in ipairs(placements or {}) do
if entry.x == x and entry.y == y then return entry.text end
end
return nil
end
local function levelText(level)
level = math.max(1, math.floor(tonumber(level) or 1))
-- PrintLevel: a three-digit level does `dec hl` first so the digits
-- overwrite the <LV> tile and the field starts at the same column.
if level >= 100 then return tostring(level) end
return "<LV>" .. tostring(level)
end
-- GetGender's three answers: carry for a genderless species (a space is
-- written), then non-zero male, zero female.
local function genderGlyph(gender)
if gender == "male" then return "" end
if gender == "female" then return "" end
return nil
end
-- DisplayHOFMon, hlcoord for hlcoord. `def` is the species row out of
-- pokemon.lua, for the dex number and the base name the routine gets from
-- GetBasePokemonName -- the roster keeps the NICKNAME, so the species name
-- has to be looked up rather than stored.
function HallOfFame.monPlacements(mon, def)
mon = mon or {}
local out = {}
-- `.print_id_no` is jumped to for an EGG, so everything above it is skipped
-- and only the ID line prints. The roster never stores an egg
-- (GetHallOfFameParty skips them), but the branch is the routine's.
if mon.species ~= "EGG" then
-- (1,13) '№' and (2,13) '.' are two `ld [hli]` writes.
put(out, "№.", 1, 13)
put(out, Chrome.number(def and def.dex or 0, 3, true), 3, 13)
put(out, (def and def.name) or mon.species, 7, 13)
put(out, genderGlyph(mon.gender), 18, 13)
-- (8,14) is a bare '/', so the nickname starts at (9,14).
put(out, "/", 8, 14)
put(out, mon.nickname or mon.name or mon.species, 9, 14)
put(out, levelText(mon.level), 1, 16)
end
-- '<ID>' '№' '/' at (7,16), (8,16), (9,16), then five digits at (10,16).
put(out, "<ID>№/", 7, 16)
put(out, Chrome.number(mon.otId or 0, 5, true), 10, 16)
return out
end
-- The header line, which is the only thing that differs between the induction
-- and the PC's viewer.
--
-- BUG (docs/bugs_and_glitches.md): "HOF Master!" is compared against
-- HOF_MASTER_COUNT + 1 while the counter itself stops AT HOF_MASTER_COUNT, so
-- the title can never print. Transcribed with the off-by-one intact.
--
-- `text` is data/generated/text.lua: the three strings are seeded into it by
-- name (RomExtractorGen2's NAMED_TEXT) because nothing points at them, and the
-- constants above are what a cache built before that seed falls back to.
function HallOfFame.headerPlacements(mode, winCount, text)
local out = {}
local function line(label, fallback)
local extracted = CommonText.get(text, label)
return extracted or fallback
end
if mode == "induct" then
put(out, line(LABELS.NEW_FAMER, HallOfFame.NEW_FAMER), 1, 2)
return out
end
winCount = tonumber(winCount) or 0
if winCount >= Core.MASTER_COUNT + 1 then
put(out, line(LABELS.HOF_MASTER, HallOfFame.HOF_MASTER), 1, 2)
return out
end
put(out, line(LABELS.TIME_FAMER, HallOfFame.TIME_FAMER), 1, 2)
put(out, Chrome.number(winCount, 3), 2, 2)
return out
end
-- HOF_AnimatePlayerPic's text, once both pics have finished sliding.
function HallOfFame.playerPlacements(save)
save = save or {}
local player = save.player or {}
local time = save.playTime or {}
local out = {}
put(out, player.name or "GOLD", 2, 4)
put(out, "<ID>№/", 1, 6)
put(out, Chrome.number(player.id or 0, 5, true), 4, 6)
put(out, "PLAY TIME", 1, 8)
-- `ld de, wGameTimeHours / lb bc, 2, 3`: a two-byte value in three columns,
-- then HALLOFFAME_COLON, then the minutes with leading zeros in two.
put(out, Chrome.number(time.hours or 0, 3), 3, 9)
put(out, ":", 6, 9)
put(out, Chrome.number(time.minutes or 0, 2, true), 7, 9)
return out
end
--------------------------------------------------------------------------
-- Construction
--------------------------------------------------------------------------
function HallOfFame:wantsFillScale() return true end
function HallOfFame:drawsWidescreen() return true end
-- opts:
-- mode "induct" (default) or "view"
-- save the record; the roster and the player card come off it
-- entry induct only: the row HallOfFame.induct just built. Passed in
-- rather than re-read so the screen shows the party that walked
-- in even if a later save rewrites the roster.
-- text text.lua, for the extracted header strings
-- onDone() induct: the credits follow. view: the PC menu comes back.
function HallOfFame.new(game, opts)
opts = opts or {}
local self = setmetatable({}, HallOfFame)
self.game = game
local data = (game and game.data) or {}
self.data = data
self.save = opts.save or (game and game.save)
self.mode = opts.mode or "induct"
self.onDone = opts.onDone
-- text.lua, for the three header strings. The induction is pushed from the
-- champion's script and the viewer from the PC, so the world is under both.
self.textData = opts.text or (game and game.world and game.world.text)
self.pokemon = opts.pokemon or data.pokemon
self.palettes = opts.palettes or data.gen2Palettes
self.picCache = {}
self.frames = 0
self.done = false
-- Hardware registers, the only state the entrance animation has.
self.scx, self.scy = 0, 0
-- The player's own two pictures. The backpic is a plain image (the battle
-- HUD's), the front is the trainer card's 5x7 portrait tile sheet.
local menuGfx = data.gen2MenuGfx or {}
-- player.sprite, the same hook Gen 1's Hall of Fame raises through
-- Sprites.playerPath (src/ui/HallOfFame.lua:100).
self.playerBackPath = require("src.pokemon.Sprites").playerPic(
(menuGfx.battleHud or {}).playerBack,
{ side = "back", kind = "hof", data = data })
local card = menuGfx.trainerCard
if card and card.card then
self.portrait = TileSheet.new({
path = card.card, wide = card.cardTilesWide or 16, firstTile = 0,
})
self.portraitWide = card.portraitWide or 5
self.portraitTiles = card.portraitTiles or 35
end
if self.mode == "view" then
-- _HallOfFamePC: wJumptableIndex is the team, wHallOfFameMonCounter the
-- mon inside it, and both start at zero. An empty roster makes LoadHOFTeam
-- return carry on its first call, which ends the screen -- but onDone
-- usually pops this state off the stack, and it has not been pushed yet, so
-- the callback is deferred to the first update.
self.team = 1
self.index = 1
self.constructing = true
self:enterView()
self.constructing = nil
else
self.entry = opts.entry or Core.team(self.save, 1)
self.index = 1
self:playMusic(HOF_MUSIC)
self:enterMon()
end
return self
end
--------------------------------------------------------------------------
-- Audio
--------------------------------------------------------------------------
function HallOfFame:playMusic(song)
local audio = self.data and self.data.audio
if audio and audio.songs and audio.songs[song] then
-- HallOfFame_PlayMusicDE plays MUSIC_NONE for a frame first, so nothing
-- of the previous song survives the switch.
Music.stop()
Music.play(self.data, song, true, { reason = "halloffame" })
end
end
function HallOfFame:playCry(species)
if not species then return end
local cries = self.data and self.data.audio and self.data.audio.cries
if cries and cries[species] then Sound.playCry(self.data, species) end
end
--------------------------------------------------------------------------
-- Phases
--------------------------------------------------------------------------
-- The mon the current phase is about, whichever mode is running.
function HallOfFame:currentMon()
local entry = self.entry
return entry and entry.mons and entry.mons[self.index] or nil
end
-- AnimateHOFMonEntrance: blank the screen, load the backpic, hSCY $d0 and
-- hSCX $90, then HOF_SlideBackpic.
function HallOfFame:enterMon()
local mon = self:currentMon()
if not mon then return self:enterPlayer() end
self.phase = "backpic"
self.scy = SCY_START
self.scx = BACKPIC_SCX_START
self.timer = 0
end
-- _HallOfFamePC .DisplayMonAndStrings: no entrance at all, the mon is simply
-- displayed. A team that has run out, or a roster that has, ends the screen.
function HallOfFame:enterView()
self.entry = Core.team(self.save, self.team)
if not self.entry then return self:finish() end
local mon = self:currentMon()
if not mon then
-- `.fail` -> carry -> .start_button -> the next team.
self.team = self.team + 1
self.index = 1
return self:enterView()
end
self.phase = "display"
self.scx, self.scy = 0, 0
self:playCry(mon.species)
end
function HallOfFame:enterDisplay()
self.phase = "display"
self.scx, self.scy = 0, 0
self.timer = FAMER_FRAMES
self:playCry((self:currentMon() or {}).species)
end
-- HOF_AnimatePlayerPic, which is where AnimateHallOfFame's .done arm lands
-- once the party has run out.
function HallOfFame:enterPlayer()
self.phase = "playerBack"
self.scy = SCY_START
self.scx = BACKPIC_SCX_START
-- `ld a, $4 / ld [wMusicFade], a` happens at .done, after the player pic;
-- the music runs under the whole card until then.
end
function HallOfFame:finish()
if self.done then return end
self.done = true
if self.constructing then
self.pendingDone = true
return
end
if self.onDone then self.onDone() end
end
--------------------------------------------------------------------------
-- Frame loop
--------------------------------------------------------------------------
-- One frame of whichever slide is running. Returns true when it is over.
local function slideBackpic(self)
if self.scx == BACKPIC_SCX_END then return true end
self.scx = (self.scx + BACKPIC_STEP) % 256
return self.scx == BACKPIC_SCX_END
end
local function slideFrontpic(self)
if self.scx == 0 then return true end
self.scx = (self.scx - FRONTPIC_STEP) % 256
return self.scx == 0
end
-- The induction's own step, one frame at a time. Split out so the driver and
-- the test can run the whole cinematic without a graphics device.
function HallOfFame:step()
if self.done then return true end
self.frames = self.frames + 1
if self.phase == "backpic" then
if slideBackpic(self) then
-- The frontpic is prepared and hSCY zeroed before HOF_SlideFrontpic.
self.phase = "frontpic"
self.scy = 0
end
return false
end
if self.phase == "frontpic" then
if slideFrontpic(self) then self:enterDisplay() end
return false
end
if self.phase == "display" then
if self.mode == "view" then return false end
self.timer = self.timer - 1
if self.timer > 0 then return false end
-- `inc [hl]` on wHallOfFameMonCounter, then round the loop; a counter that
-- reaches PARTY_LENGTH or a -1 species ends it.
self.index = self.index + 1
if self.index > Core.PARTY_LENGTH or not self:currentMon() then
self:enterPlayer()
else
self:enterMon()
end
return false
end
if self.phase == "playerBack" then
if slideBackpic(self) then
self.phase = "playerFront"
self.scy = 0
self.scx = TRAINER_SCX_START
end
return false
end
if self.phase == "playerFront" then
if slideFrontpic(self) then
self.phase = "player"
self.timer = END_FRAMES
-- wMusicFade = 4: the Hall of Fame theme rings out under the card.
Music.fadeOut(4)
end
return false
end
if self.phase == "player" then
self.timer = self.timer - 1
if self.timer <= 0 then self:finish() end
return self.done
end
return false
end
-- _HallOfFamePC's joypad arms. A is the next mon, START the next team, B out.
function HallOfFame:viewInput(input)
if not input then return end
if input:wasPressed("b") then return self:finish() end
if input:wasPressed("start") then
self.team = self.team + 1
self.index = 1
return self:enterView()
end
if input:wasPressed("a") then
self.index = self.index + 1
if self.index > Core.PARTY_LENGTH or not self:currentMon() then
self.team = self.team + 1
self.index = 1
end
return self:enterView()
end
end
function HallOfFame:update(_dt)
if self.pendingDone then
self.pendingDone = nil
if self.onDone then self.onDone() end
return
end
if self.done then return end
if self.mode == "view" then
self.frames = self.frames + 1
return self:viewInput(self.game and self.game.input)
end
self:step()
end
--------------------------------------------------------------------------
-- Drawing
--------------------------------------------------------------------------
function HallOfFame:image(path)
if not path then return nil end
local cached = self.picCache[path]
if cached == nil then
local ok, image = pcall(Assets.image, path)
cached = ok and image or false
self.picCache[path] = cached
end
return cached or nil
end
function HallOfFame:speciesDef(species)
return species and self.pokemon and self.pokemon[species] or nil
end
-- Takes the RECORD, not the species: the entry carries the two DV bytes
-- precisely so the viewer can name the form. HOF_ShowMonOrPlayer copies them
-- into wTempMonDVs and runs `predef GetUnownLetter` before GetMonBackpic
-- (engine/events/halloffame.asm:225-238); the frontpic path at :458-468 does
-- the same before _PrepMonFrontpic.
function HallOfFame:monPic(mon, back)
local def = self:speciesDef(mon and mon.species)
if not def then return nil end
local path = back and def.spriteBack or def.spriteFront
if mon.species == Unown.SPECIES then
path = Unown.formSprite(self.pokemon, Unown.monLetter(mon), back) or path
end
return self:image(path)
end
function HallOfFame:monColors(mon)
if not (self.palettes and mon and mon.species) then return nil end
return Palettes.monColors(self.palettes, mon.species, mon.shiny)
end
-- hSCX / hSCY applied to one screen-space pixel coordinate. The BG map wraps
-- every 256 pixels, so a pic mid-sweep can be visible on both edges at once
-- and both copies are drawn.
local function scrolled(base, register)
local value = (base - register) % 256
return value, value - 256
end
-- Draw an image at a tile coordinate through the current scroll, padded into
-- the 7x7 block the way PlaceGraphic pads it.
function HallOfFame:drawScrolled(image, tileX, tileY, colors)
if not image then return end
local G = love.graphics
local wide = math.floor(image:getWidth() / 8)
local pad = PIC_PAD[wide] or PIC_PAD[PIC_TILES]
local baseX = tileX * 8 + pad[1] * 8
local baseY = tileY * 8 + pad[2] * 8
local x1, x2 = scrolled(baseX, self.scx)
local y1, y2 = scrolled(baseY, self.scy)
G.setColor(1, 1, 1, 1)
local function body()
for _, x in ipairs({ x1, x2 }) do
for _, y in ipairs({ y1, y2 }) do
G.draw(image, x, y)
end
end
end
if colors and GbcPalette.available() then
GbcPalette.with(colors, body)
else
body()
end
end
-- The player's front picture: the trainer card's 5x7 portrait, laid out as the
-- run of tile ids TrainerCard_PrintTopHalfOfCard uses, translated by the
-- scroll rather than drawn at a tile coordinate.
function HallOfFame:drawPortrait(tileX, tileY)
if not (self.portrait and self.portrait:available()) then return end
local G = love.graphics
local wide = self.portraitWide
local high = math.floor((self.portraitTiles or 35) / wide)
-- A 5x7 portrait standing in for a 7x7 pic sits on the same ground line and
-- centred, which is what PadFrontpic would have done to it.
local padX = math.floor((PIC_TILES - wide) / 2)
local padY = PIC_TILES - high
local baseX = (tileX + padX) * 8
local baseY = (tileY + padY) * 8
local x1, x2 = scrolled(baseX, self.scx)
local y1, y2 = scrolled(baseY, self.scy)
for _, x in ipairs({ x1, x2 }) do
for _, y in ipairs({ y1, y2 }) do
G.push()
G.translate(x, y)
self.portrait:block(0, wide, high, 0, 0)
G.pop()
end
end
end
function HallOfFame:drawPlacements(list)
for _, entry in ipairs(list or {}) do
Chrome.print(entry.text, entry.x, entry.y)
end
end
-- The two boxes DisplayHOFMon draws: `lb bc, 3, SCREEN_WIDTH - 2` at (0,0) and
-- `lb bc, 4, 18` at (0,12). Textbox takes INTERIOR rows and columns, so those
-- are 20x5 and 20x6 on screen.
function HallOfFame:drawMonPanel()
local mon = self:currentMon()
Chrome.clear()
Chrome.textbox(0, 0, 18, 3)
Chrome.textbox(0, 12, 18, 4)
local def = mon and self:speciesDef(mon.species)
self:drawScrolled(self:monPic(mon, false),
FRONTPIC_X, FRONTPIC_Y, self:monColors(mon))
self:drawPlacements(HallOfFame.headerPlacements(self.mode,
self.entry and self.entry.winCount, self.textData))
self:drawPlacements(HallOfFame.monPlacements(mon, def))
end
-- HOF_AnimatePlayerPic's card: `lb bc, 8, 9` at (0,2) and `lb bc, 4, 18` at
-- (0,12). The bottom box is left empty; ProfOaksPCRating is what fills it.
function HallOfFame:drawPlayerPanel()
Chrome.clear()
Chrome.textbox(0, 2, 9, 8)
Chrome.textbox(0, 12, 18, 4)
self:drawPortrait(TRAINERPIC_X, TRAINERPIC_Y)
self:drawPlacements(HallOfFame.playerPlacements(self.save))
end
function HallOfFame:drawPanel()
local G = love.graphics
G.setColor(1, 1, 1, 1)
G.rectangle("fill", 0, 0, SCREEN_W, SCREEN_H)
-- Both entry points put FontBattleExtra in the $60 slot before anything is
-- placed: InitDisplayForHallOfFame (engine/movie/init_hof_credits.asm) for
-- the induction, LoadFontsBattleExtra at the top of _HallOfFamePC for the
-- viewer. '№' ($74) and '<ID>' ($73) are that sheet's glyphs, and on the
-- normal extra sheet those codes are a middle dot and a closing quote.
local wasBattle = Font.useBattleExtra(true)
if self.phase == "backpic" then
local mon = self:currentMon()
self:drawScrolled(self:monPic(mon, true),
BACKPIC_X, BACKPIC_Y, self:monColors(mon))
elseif self.phase == "frontpic" then
local mon = self:currentMon()
self:drawScrolled(self:monPic(mon, false),
FRONTPIC_X, FRONTPIC_Y, self:monColors(mon))
elseif self.phase == "display" then
self:drawMonPanel()
elseif self.phase == "playerBack" then
self:drawScrolled(self:image(self.playerBackPath), BACKPIC_X, BACKPIC_Y,
nil)
elseif self.phase == "playerFront" then
self:drawPortrait(TRAINERPIC_X, TRAINERPIC_Y)
elseif self.phase == "player" then
self:drawPlayerPanel()
end
Font.useBattleExtra(wasBattle)
G.setColor(1, 1, 1, 1)
end
function HallOfFame:draw()
self:drawPanel()
end
function HallOfFame:drawWidescreen(winW, winH)
local G = love.graphics
G.setColor(0, 0, 0, 1)
G.rectangle("fill", 0, 0, winW, winH)
local scale = Chrome.fitScale(winW, winH)
G.push()
G.translate(math.floor((winW - SCREEN_W * scale) / 2),
math.floor((winH - SCREEN_H * scale) / 2))
G.scale(scale, scale)
self:drawPanel()
G.pop()
end
HallOfFame.WHITE = WHITE
HallOfFame.PIC_PAD = PIC_PAD
HallOfFame.FAMER_FRAMES = FAMER_FRAMES
HallOfFame.END_FRAMES = END_FRAMES
HallOfFame.SCY_START = SCY_START
HallOfFame.BACKPIC_SCX_START = BACKPIC_SCX_START
HallOfFame.BACKPIC_SCX_END = BACKPIC_SCX_END
HallOfFame.TRAINER_SCX_START = TRAINER_SCX_START
return HallOfFame