Files
gen1recomp/src/ui/DexEntryMenu.lua
T

298 lines
10 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- Pokédex entry page: front sprite, kind, height/weight and the real
-- dex description (data/pokemon/dex_entries.asm + dex_text.asm).
--
-- `species` may be a species id string, or a table
-- `{ species = id, forceOwned = true }`. forceOwned mirrors pret's
-- StarterDex (engine/events/starter_dex.asm), which temporarily sets the
-- owned bit so Oak's lab ball previews show height/weight/description
-- without permanently marking the mon owned.
--
-- `onDone` (optional) runs right after the page pops itself, the way a
-- TextBox onDone does; map scripts use it to continue once the player
-- closes the entry (the Fighting Dojo prize balls chain their take-it
-- prompt off it).
local Font = require("src.render.Font")
local Strings = require("src.core.Strings")
local Theme = require("src.ui.Theme")
local DexEntryMenu = {}
DexEntryMenu.__index = DexEntryMenu
DexEntryMenu.isOpaque = true
-- SGB: PalPacket_Pokedex (BROWNMON) + the mon pic zone in its palette
function DexEntryMenu:sgbPalettes(game)
local P = require("src.render.PaletteFX")
local base = P.pal(game.data, "BROWNMON")
if not base then return nil end
return { P.whole(base),
P.zone(P.monPal(game.data, self.def and self.def.id), 1, 1, 8, 8) }
end
local function resolveArgs(speciesOrOpts)
if type(speciesOrOpts) == "table" then
return speciesOrOpts.species or speciesOrOpts[1],
speciesOrOpts.forceOwned and true or false
end
return speciesOrOpts, false
end
local function ownedFor(game, def, forceOwned)
return forceOwned
or (game.save.pokedex and game.save.pokedex.owned[def.id]) or false
end
-- home/text.asm:245 (<PAGE>), home/text.asm:204 (<DEXEND>)
local function descPages(game, def, forceOwned)
local e = def.dexEntry or {}
local owned = ownedFor(game, def, forceOwned)
local text = owned and e.text and game.data.text[e.text] or nil
if not text then return nil end
local pages = {}
for chunk in (text .. "\f"):gmatch("(.-)\f") do
local lines = {}
for line in (chunk:gsub("\v", "\n") .. "\n"):gmatch("(.-)\n") do
lines[#lines + 1] = line
end
while #lines > 0 and lines[#lines] == "" do table.remove(lines) end
if #lines > 0 then pages[#pages + 1] = lines end
end
if #pages == 0 then return nil end
local last = pages[#pages]
last[#last] = last[#last] .. "."
return pages
end
-- engine/gfx/load_pokedex_tiles.asm: gfx/pokedex/pokedex.png, codes $60..$71
local frameCache = {}
local function frameSheet(game)
local fx = game.data.field and game.data.field.overworldFx
local def = fx and fx.pokedexFrame
local path = def and def.path
if not path then return nil end
local hit = frameCache[path]
if hit ~= nil then return hit or nil end
local ok, img = pcall(love.graphics.newImage, path)
if not ok or not img then
frameCache[path] = false
return nil
end
local iw, ih = img:getDimensions()
local quads = {}
for i = 0, 17 do
quads[i] = love.graphics.newQuad((i % 3) * 8,
math.floor(i / 3) * 8, 8, 8, iw, ih)
end
frameCache[path] = { img = img, quads = quads }
return frameCache[path]
end
-- engine/gfx/load_pokedex_tiles.asm:8-11: PokeballTileGraphics -> tile $72
local ballCache
local function ballSheet()
if ballCache == nil then
local ok, img = pcall(love.graphics.newImage,
"assets/generated/battle/balls.png")
if ok and img then
ballCache = { img = img,
quad = love.graphics.newQuad(0, 0, 8, 8,
img:getDimensions()) }
else
ballCache = false
end
end
return ballCache or nil
end
-- one tile of the sheet, for the CONTENTS screen's divider column
-- (engine/menus/pokedex.asm:350, DrawPokedexVerticalLine)
function DexEntryMenu.tile(game, code, tx, ty)
if code == 0x72 then
local ball = ballSheet()
if ball then love.graphics.draw(ball.img, ball.quad, tx * 8, ty * 8) end
return
end
local frame = frameSheet(game)
if not (frame and code) then return end
local quad = frame.quads[code - 0x60]
if quad then love.graphics.draw(frame.img, quad, tx * 8, ty * 8) end
end
-- engine/menus/pokedex.asm:601
local DIVIDER = {
0x68, 0x69, 0x6B, 0x69, 0x6B, 0x69, 0x6B, 0x69, 0x6B, 0x6B,
0x6B, 0x6B, 0x69, 0x6B, 0x69, 0x6B, 0x69, 0x6B, 0x69, 0x6A,
}
-- engine/menus/pokedex.asm:500-504
local PIC_DELAY = 36
function DexEntryMenu.new(game, speciesOrOpts, onDone)
local species, forceOwned = resolveArgs(speciesOrOpts)
local self = setmetatable({ game = game, forceOwned = forceOwned,
onDone = onDone }, DexEntryMenu)
self.def = game.data.pokemon[species]
local path, trueColor = require("src.pokemon.Sprites").path(
game.data, species, "front", { kind = "dex" })
-- pcall's second return has to survive the guard (#307)
local ok, img = false, nil
if path then ok, img = pcall(love.graphics.newImage, path) end
self.sprite = ok and img or nil
self.spriteTrueColor = self.sprite and trueColor or false
self.page = 1
self.blink = 0
local pages = descPages(game, self.def, forceOwned)
self.pageCount = pages and #pages or 1
self.species = species
self.picDelay = PIC_DELAY
return self
end
-- home/pokemon.asm:148 (jp WaitForSoundToFinish)
function DexEntryMenu:crying()
local src = self.crySrc
if not src then return false end
local ok, playing = pcall(src.isPlaying, src)
if ok and playing then return true end
self.crySrc = nil
return false
end
function DexEntryMenu:update(dt)
local input = self.game.input
self.blink = ((self.blink or 0) + 1) % 60
if (self.picDelay or 0) > 0 then
self.picDelay = self.picDelay - 1
if self.picDelay == 0 then
-- engine/menus/pokedex.asm:504-506, home/pokemon.asm:145-148
self.crySrc = require("src.core.Sound").playCry(self.game.data,
self.species)
end
return
end
if self:crying() then return end
if input:wasPressed("a") or input:wasPressed("b") then
-- home/text.asm:245
if self.page < (self.pageCount or 1) then
self.page = self.page + 1
return
end
self.game.stack:pop()
if self.onDone then self.onDone() end
end
end
function DexEntryMenu:draw()
local waiting = (self.picDelay or 0) > 0
DexEntryMenu.render(self.game, self.def, not waiting and self.sprite or nil,
self.forceOwned, self.spriteTrueColor, self.page,
{ crying = self:crying(), waiting = waiting,
arrow = (self.blink or 0) < 30 })
end
-- Static entry-page renderer, shared with the printer stand-in
-- (src/core/Printer.lua renders the same page into a PNG the way
-- PrintPokedexEntry rendered it to the Game Boy Printer).
-- engine/menus/pokedex.asm:399
function DexEntryMenu.render(game, def, sprite, forceOwned, trueColor, page, state)
page = page or 1
state = state or {}
love.graphics.setColor(1, 1, 1, 1)
love.graphics.rectangle("fill", 0, 0, 160, 144)
local frame = frameSheet(game)
if frame then
local function tile(code, tx, ty)
love.graphics.draw(frame.img, frame.quads[code - 0x60], tx * 8, ty * 8)
end
-- engine/menus/pokedex.asm:418
for tx = 1, 18 do
tile(0x64, tx, 0)
tile(0x6f, tx, 17)
end
for ty = 1, 16 do
tile(0x66, 0, ty)
tile(0x67, 19, ty)
end
tile(0x63, 0, 0)
tile(0x65, 19, 0)
tile(0x6c, 0, 17)
tile(0x6e, 19, 17)
-- engine/menus/pokedex.asm:445
for tx = 0, 19 do
tile(DIVIDER[tx + 1], tx, 9)
end
end
if sprite then
-- engine/menus/pokedex.asm:503, home/pokemon.asm:96 (flipped)
local w, h = sprite:getDimensions()
local x = 8 + math.floor((8 - w / 8) / 2) * 8
local y = 8 + (7 - h / 8) * 8
love.graphics.draw(sprite, x + w, y, 0, -1, 1)
-- the unshaded pass needs the pic bounds (#350)
if trueColor then
require("src.render.PaletteFX").markTrueColor(x, y, w, h)
end
end
love.graphics.setColor(0, 0, 0, 1)
-- engine/menus/pokedex.asm:454
Font.draw(def.name, 72, 16)
local e = def.dexEntry or {}
-- engine/menus/pokedex.asm:468, kind string only (PokeText is unreferenced)
Font.draw(e.kind or "?", 72, 32)
-- same number width as the list (constants.dexDigits), so a dex past 999
-- prints the extra digit everywhere at once
local digits = (game.data.constants or {}).dexDigits or 3
-- engine/menus/pokedex.asm:478
Font.draw(Strings("No.") .. ("%0" .. digits .. "d"):format(def.dex or 0),
16, 64)
local owned = ownedFor(game, def, forceOwned)
local metric = e.heightM ~= nil
local numbers = owned and e.heightFt
-- engine/menus/pokedex.asm:449
if not metric then
Font.draw(Strings("HT"), 72, 48)
Font.draw("′", 112, 48)
Font.draw("″", 136, 48)
Font.draw(Strings("WT"), 72, 64)
Font.draw(Strings("lb"), 136, 64)
-- engine/menus/pokedex.asm:449-450, overwritten at :518-520
if not numbers or state.crying or state.waiting then
Font.draw("?", 104, 48)
Font.draw("??", 120, 48)
Font.draw("???", 112, 64)
end
end
-- engine/menus/pokedex.asm:516: everything below the divider waits on the
-- cry the line above it started
if state.crying or state.waiting then
love.graphics.setColor(1, 1, 1, 1)
return
end
-- engine/menus/pokedex.asm:515-566
if numbers then
if metric then
Font.draw((Strings("GR. %.1fm", e.heightM):gsub("(%d)%.(%d)", "%1,%2")), 72, 48)
Font.draw((Strings("GEW. %.1fkg", e.weightKg or 0):gsub("(%d)%.(%d)", "%1,%2")), 72, 64)
else
Font.draw(("%2d"):format(e.heightFt), 96, 48)
Font.draw(("%02d"):format(e.heightIn or 0), 120, 48)
Font.draw(("%6.1f"):format((e.weight or 0) / 10), 88, 64)
end
end
local pages = descPages(game, def, forceOwned)
if pages then
-- engine/menus/pokedex.asm:568
local lines = pages[page] or pages[#pages]
for i, line in ipairs(lines) do
Font.draw(line, 8, 72 + i * 16)
end
-- home/text.asm:245 places the '▼' and home/joypad2.asm:55-83 blinks it
if page < #pages and state.arrow ~= false then
Font.drawCode(Theme.moreArrow, 144, 128)
end
end
love.graphics.setColor(1, 1, 1, 1)
end
return DexEntryMenu