mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-18 19:54:21 +02:00
G2 support
This commit is contained in:
@@ -0,0 +1,231 @@
|
||||
-- The player-name menu (pokegold engine/menus/intro_menu.asm NamePlayer,
|
||||
-- data/player_names.asm NameMenuHeader).
|
||||
--
|
||||
-- Transcribed from the header rather than laid out by eye, because almost
|
||||
-- every coordinate here comes out of one:
|
||||
--
|
||||
-- menu_coords 0, 0, 10, TEXTBOX_Y - 1 the box: (0,0) to (10,11)
|
||||
-- STATICMENU_CURSOR | STATICMENU_PLACE_TITLE | STATICMENU_DISABLE_B
|
||||
-- 5 items, default option 1, title indent 2, title "NAME"
|
||||
--
|
||||
-- GetMenuTextStartCoord turns those flags into the label origin: the border
|
||||
-- costs a row and a column, STATICMENU_CURSOR costs another column, and the
|
||||
-- absence of STATICMENU_NO_TOP_SPACING costs another row -- so the labels
|
||||
-- start at (2,2) and step TWO rows each (PlaceVerticalMenuItems adds
|
||||
-- 2 * SCREEN_WIDTH per item). The cursor sits one column left of the labels,
|
||||
-- at column 1. The title is placed from MenuBoxCoord2Tile plus the indent,
|
||||
-- which lands it ON the box's top border at (2,0).
|
||||
--
|
||||
-- The other half of the screen is the pic. NamePlayer opens by calling
|
||||
-- MovePlayerPicRight, which walks the 7x7 CAL frontpic OakSpeech left at
|
||||
-- hlcoord 6,4 one tile per frame until it sits at hlcoord 13,4 -- so the menu
|
||||
-- is not on its own, it slides in beside the player. Picking a preset runs
|
||||
-- MovePlayerPicLeft to walk it back before returning to the speech.
|
||||
--
|
||||
-- STATICMENU_DISABLE_B is why B does nothing here: there is no way out of
|
||||
-- this menu except choosing a name.
|
||||
|
||||
local Chrome = require("src.ui.gen2.Chrome")
|
||||
local Font = require("src.render.Font")
|
||||
local GbcPalette = require("src.render.GbcPalette")
|
||||
local Screens = require("src.ui.Screens")
|
||||
|
||||
local NamePick = {}
|
||||
NamePick.__index = NamePick
|
||||
NamePick.isOpaque = true
|
||||
|
||||
-- data/player_names.asm PlayerNameArray, Gold's half of the IF.
|
||||
local PRESETS = { "GOLD", "HIRO", "TAYLOR", "KARL" }
|
||||
|
||||
-- menu_coords 0, 0, 10, TEXTBOX_Y - 1 (TEXTBOX_Y = 12).
|
||||
local BOX_X1, BOX_Y1, BOX_X2, BOX_Y2 = 0, 0, 10, 11
|
||||
-- GetMenuTextStartCoord's answer for this header's flags.
|
||||
local TEXT_X, TEXT_Y = 2, 2
|
||||
local CURSOR_X = TEXT_X - 1
|
||||
local TITLE, TITLE_X, TITLE_Y = "NAME", 2, 0
|
||||
|
||||
-- Intro_PrepTrainerPic puts the 7x7 pic at hlcoord 6,4; MovePlayerPic walks
|
||||
-- it to 13,4 one tile per frame.
|
||||
local PIC_X_LEFT, PIC_X_RIGHT, PIC_Y = 6, 13, 4
|
||||
local PIC_TILES = 7
|
||||
|
||||
function NamePick:wantsFillScale() return true end
|
||||
function NamePick:drawsWidescreen() return true end
|
||||
|
||||
-- opts: onDone(name), font, pic (the CAL frontpic already loaded by the Oak
|
||||
-- speech), picColors, presets
|
||||
function NamePick.new(game, opts)
|
||||
opts = opts or {}
|
||||
local self = setmetatable({}, NamePick)
|
||||
self.game = game
|
||||
self.onDone = opts.onDone
|
||||
self.items = { "NEW NAME" }
|
||||
for _, name in ipairs(opts.presets or PRESETS) do
|
||||
self.items[#self.items + 1] = name
|
||||
end
|
||||
-- `db 1 ; default option`: the cursor starts on NEW NAME, not on a preset.
|
||||
self.cursor = 1
|
||||
self.pic = opts.pic
|
||||
self.picColors = opts.picColors
|
||||
self.fontOk = false
|
||||
local font = opts.font
|
||||
if font then
|
||||
self.fontOk = pcall(Font.load, { font = font })
|
||||
end
|
||||
-- The approach walk, and then the walk back once a preset is taken. picX
|
||||
-- is the pic's live tile column rather than a step count: MovePlayerPic
|
||||
-- walks it one tile per frame and both directions end where they end, so
|
||||
-- the column IS the state.
|
||||
self.picX = PIC_X_LEFT
|
||||
self.slide = "in"
|
||||
return self
|
||||
end
|
||||
|
||||
function NamePick:choose(name)
|
||||
if self.onDone then self.onDone(name) end
|
||||
end
|
||||
|
||||
-- NEW NAME opens Gold's own keyboard (src/ui/gen2/NamingScreen.lua), not
|
||||
-- Gen 1's: the two differ in grid size, the case switch, and where DEL/END sit.
|
||||
function NamePick:openNaming()
|
||||
local data = self.game and self.game.data or {}
|
||||
local sprites = data.gen2Sprites
|
||||
local chris = sprites and sprites.SPRITE_CHRIS
|
||||
local Palettes = require("src.world.gen2.Palettes")
|
||||
Screens.push(self.game, "Gen2NamingScreen", {
|
||||
type = "player",
|
||||
menuGfx = data.gen2MenuGfx,
|
||||
iconPath = chris and chris.image or nil,
|
||||
-- Chris is PAL_OW_RED; the naming screen is lit like day.
|
||||
iconColors = data.gen2Palettes
|
||||
and Palettes.spritePalette(data.gen2Palettes, "DAY", chris) or nil,
|
||||
onDone = function(name)
|
||||
-- An empty name keeps the default, the way ending entry with nothing
|
||||
-- typed leaves wPlayerName at its preset.
|
||||
self.game.stack:pop() -- the naming screen
|
||||
if name and #name > 0 then
|
||||
self:choose(name)
|
||||
else
|
||||
self:choose(self.items[2] or "GOLD")
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
function NamePick:update(_dt)
|
||||
-- MovePlayerPic is a blocking DelayFrame loop on the cart: nothing reads
|
||||
-- the joypad until the pic has finished walking.
|
||||
if self.slide == "in" then
|
||||
if self.picX < PIC_X_RIGHT then
|
||||
self.picX = self.picX + 1
|
||||
return
|
||||
end
|
||||
self.slide = nil
|
||||
return
|
||||
elseif self.slide == "out" then
|
||||
if self.picX > PIC_X_LEFT then
|
||||
self.picX = self.picX - 1
|
||||
return
|
||||
end
|
||||
self.slide = nil
|
||||
self:choose(self.pendingName)
|
||||
return
|
||||
end
|
||||
|
||||
local input = self.game.input
|
||||
if not input then return end
|
||||
if input:wasPressed("up") then
|
||||
self.cursor = self.cursor > 1 and self.cursor - 1 or #self.items
|
||||
elseif input:wasPressed("down") then
|
||||
self.cursor = self.cursor < #self.items and self.cursor + 1 or 1
|
||||
elseif input:wasPressed("a") or input:wasPressed("start") then
|
||||
-- `ld a, [wMenuCursorY]; dec a; jr z, .NewName`: item 1 is NEW NAME and
|
||||
-- everything else is a preset. B is deliberately not handled --
|
||||
-- STATICMENU_DISABLE_B.
|
||||
if self.cursor == 1 then
|
||||
if self.fontOk then
|
||||
self:openNaming()
|
||||
else
|
||||
self:choose(self.items[2] or "GOLD")
|
||||
end
|
||||
else
|
||||
-- A preset returns through MovePlayerPicLeft, so the pic walks back
|
||||
-- before the speech resumes.
|
||||
self.pendingName = self.items[self.cursor]
|
||||
self.slide = "out"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function NamePick:drawPic()
|
||||
if not self.pic then return end
|
||||
local G = love.graphics
|
||||
local w, h = self.pic:getDimensions()
|
||||
-- PlaceGraphic lays a 7x7 block from the coordinate; a pic smaller than
|
||||
-- that sits in the block's bottom-left, as PadFrontpic leaves it.
|
||||
local x = self.picX * 8
|
||||
local y = PIC_Y * 8 + (PIC_TILES - h / 8) * 8
|
||||
local function body()
|
||||
G.setColor(1, 1, 1, 1)
|
||||
G.draw(self.pic, x, y)
|
||||
end
|
||||
if self.picColors and GbcPalette.available() then
|
||||
GbcPalette.with(self.picColors, body)
|
||||
else
|
||||
body()
|
||||
end
|
||||
G.setColor(1, 1, 1, 1)
|
||||
end
|
||||
|
||||
function NamePick:drawPanel()
|
||||
local G = love.graphics
|
||||
Chrome.clear()
|
||||
self:drawPic()
|
||||
-- The menu is only up once the pic has finished walking over: MenuBox is
|
||||
-- drawn by ShowPlayerNamingChoices, which NamePlayer calls after the walk.
|
||||
if self.slide == "in" then return end
|
||||
if not self.fontOk then
|
||||
G.setColor(0, 0, 0, 1)
|
||||
for index, label in ipairs(self.items) do
|
||||
local prefix = (index == self.cursor) and "> " or " "
|
||||
G.print(prefix .. label, TEXT_X * 8, (TEXT_Y + (index - 1) * 2) * 8)
|
||||
end
|
||||
G.setColor(1, 1, 1, 1)
|
||||
return
|
||||
end
|
||||
-- MenuBox: GetMenuBoxDims then `dec b / dec c`, so the interior is one less
|
||||
-- than the span in each direction.
|
||||
Chrome.textbox(BOX_X1, BOX_Y1, BOX_X2 - BOX_X1 - 1, BOX_Y2 - BOX_Y1 - 1)
|
||||
-- PlaceString REPLACES the tilemap cells it lands on, and the title lands
|
||||
-- on the box's top border -- so the border tiles under it have to go, or
|
||||
-- the letters sit on a line the cart does not draw there.
|
||||
G.setColor(1, 1, 1, 1)
|
||||
G.rectangle("fill", TITLE_X * 8, TITLE_Y * 8, #TITLE * 8, 8)
|
||||
Chrome.print(TITLE, TITLE_X, TITLE_Y)
|
||||
for index, label in ipairs(self.items) do
|
||||
Chrome.print(label, TEXT_X, TEXT_Y + (index - 1) * 2)
|
||||
end
|
||||
Chrome.cursor(CURSOR_X, TEXT_Y + (self.cursor - 1) * 2)
|
||||
G.setColor(1, 1, 1, 1)
|
||||
end
|
||||
|
||||
function NamePick:draw()
|
||||
self:drawPanel()
|
||||
end
|
||||
|
||||
function NamePick:drawWidescreen(winW, winH)
|
||||
local G = love.graphics
|
||||
G.setColor(1, 1, 1, 1)
|
||||
G.rectangle("fill", 0, 0, winW, winH)
|
||||
local scale = Chrome.fitScale(winW, winH)
|
||||
G.push()
|
||||
G.translate(math.floor((winW - 160 * scale) / 2),
|
||||
math.floor((winH - 144 * scale) / 2))
|
||||
G.scale(scale, scale)
|
||||
self:drawPanel()
|
||||
G.pop()
|
||||
end
|
||||
|
||||
NamePick.PRESETS = PRESETS
|
||||
|
||||
return NamePick
|
||||
Reference in New Issue
Block a user