mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 08:21:02 +02:00
Run the HoF dex rating through the standard text box (#314)
HoFDisplayPlayerStats prints its three dex texts (seen/owned, the POKéDEX Rating: header, the tier line) through HoFPrintTextAndDelay -> PrintText, the standard two-row bottom box, each followed by 120 DelayFrames. The port hand-drew a 6-row box and flattened the tier text's cont (\v) rows into plain newlines, so a 3+ row rating painted its third row over the box's bottom border and dropped the rest. Push the three texts as a chain of auto-advancing TextBox states instead: the cont rows scroll inside the two-row box with the original's A/B wait (_ContText -> ManualTextScroll), and each box closes itself after the 120-frame hold. Adds tests/parity_hof_rating.lua: box order/content and the auto-close hold are asserted over a full headless induction.
This commit is contained in:
+42
-54
@@ -9,6 +9,7 @@
|
||||
|
||||
local Assets = require("src.render.Assets")
|
||||
local Font = require("src.render.Font")
|
||||
local TextBox = require("src.render.TextBox")
|
||||
local Music = require("src.core.Music")
|
||||
local Sound = require("src.core.Sound")
|
||||
local TypeChart = require("src.battle.TypeChart")
|
||||
@@ -64,17 +65,6 @@ local function dexRatingKey(owned)
|
||||
return ("_DexRatingText_Own%dTo%d"):format(lo, lo + 9)
|
||||
end
|
||||
|
||||
-- \n/\v/\f-marked extracted text, one Font.draw line at a time (same
|
||||
-- technique as DexEntryMenu.lua's dex-description block)
|
||||
local function drawTextBlock(text, x, y, maxY)
|
||||
for line in (text:gsub("\v", "\n"):gsub("\f", "\n") .. "\n"):gmatch("(.-)\n") do
|
||||
if maxY and y > maxY then break end
|
||||
Font.draw(line, x, y)
|
||||
y = y + 8
|
||||
end
|
||||
return y
|
||||
end
|
||||
|
||||
function HallOfFame.new(game, onDone)
|
||||
local self = setmetatable({}, HallOfFame)
|
||||
self.game = game
|
||||
@@ -182,26 +172,50 @@ function HallOfFame:update(dt)
|
||||
self.phase = "player_stats"
|
||||
self.timer = DEX_HOLD
|
||||
elseif self.phase == "player_stats" then
|
||||
-- name / play time / money boxes are up; then DexSeenOwnedText
|
||||
-- name / play time / money boxes are up; then the dex texts
|
||||
self.timer = self.timer - 1
|
||||
if skip or self.timer <= 0 then
|
||||
self.phase = "player_dex"
|
||||
self.timer = DEX_HOLD
|
||||
end
|
||||
elseif self.phase == "player_dex" then
|
||||
self.timer = self.timer - 1
|
||||
if skip or self.timer <= 0 then
|
||||
self.phase = "player_rating"
|
||||
self.timer = DEX_HOLD
|
||||
end
|
||||
elseif self.phase == "player_rating" then
|
||||
self.timer = self.timer - 1
|
||||
if skip or self.timer <= 0 then
|
||||
-- HoFFadeOutScreenAndMusic -> Credits lead-in (no A wait here)
|
||||
self.game.stack:pop()
|
||||
if self.onDone then self.onDone() end
|
||||
self:showDexTexts()
|
||||
end
|
||||
end
|
||||
-- player_dex / player_rating are driven by the TextBox chain that
|
||||
-- showDexTexts pushes; nothing is timed here
|
||||
end
|
||||
|
||||
-- HoFDisplayPlayerStats' three HoFPrintTextAndDelay calls: seen/owned,
|
||||
-- "POKéDEX Rating:", then the tier text DisplayDexRating copied into
|
||||
-- wDexRatingText -- each through PrintText (the standard two-row box, so
|
||||
-- the tier text's \v rows scroll inside it) followed by 120 DelayFrames
|
||||
-- with no button wait. Hand-drawing these flattened the \v scrolls and
|
||||
-- painted the third row over the box's bottom border (#314).
|
||||
function HallOfFame:showDexTexts()
|
||||
local game = self.game
|
||||
local text = game.data.text or {}
|
||||
local seen, owned = self:dexSeenOwned()
|
||||
local seenOwned = (text._DexSeenOwnedText
|
||||
or Strings("POKéDEX Seen:{NUM:wDexRatingNumMonsSeen, 1, 3}\n Owned:{NUM:wDexRatingNumMonsOwned, 1, 3}"))
|
||||
:gsub("{NUM:wDexRatingNumMonsSeen[^}]*}", tostring(seen))
|
||||
:gsub("{NUM:wDexRatingNumMonsOwned[^}]*}", tostring(owned))
|
||||
local header = (text._DexRatingText or Strings("POKéDEX Rating{COLON}"))
|
||||
:gsub("{COLON}", ":")
|
||||
local rating = text[dexRatingKey(owned)] or Strings("Keep it up!")
|
||||
local function finish()
|
||||
-- HoFFadeOutScreenAndMusic -> Credits lead-in (no A wait here)
|
||||
game.stack:pop()
|
||||
if self.onDone then self.onDone() end
|
||||
end
|
||||
local function showRating()
|
||||
game.stack:push(TextBox.new(game, rating, finish,
|
||||
{ auto = { delay = DEX_HOLD } }))
|
||||
end
|
||||
local function showHeader()
|
||||
self.phase = "player_rating"
|
||||
game.stack:push(TextBox.new(game, header, showRating,
|
||||
{ auto = { delay = DEX_HOLD } }))
|
||||
end
|
||||
game.stack:push(TextBox.new(game, seenOwned, showHeader,
|
||||
{ auto = { delay = DEX_HOLD } }))
|
||||
end
|
||||
|
||||
-- HoFDisplayMonInfo: TextBoxBorder (0,2) b=9,c=10 + LEVEL/TYPE labels
|
||||
@@ -263,28 +277,6 @@ function HallOfFame:drawPlayerStats()
|
||||
Font.draw(("¥%d"):format(save.money or 0), 4 * 8, 10 * 8)
|
||||
end
|
||||
|
||||
function HallOfFame:drawDexBox(kind)
|
||||
local save = self.game.save
|
||||
local text = self.game.data.text or {}
|
||||
Font.drawBox(0, 12, 20, 6)
|
||||
love.graphics.setColor(0, 0, 0, 1)
|
||||
if kind == "seen" then
|
||||
local seen, owned = self:dexSeenOwned()
|
||||
local seenOwned = text._DexSeenOwnedText
|
||||
or Strings("POKéDEX Seen:{NUM:wDexRatingNumMonsSeen, 1, 3}\n Owned:{NUM:wDexRatingNumMonsOwned, 1, 3}")
|
||||
seenOwned = seenOwned
|
||||
:gsub("{NUM:wDexRatingNumMonsSeen[^}]*}", tostring(seen))
|
||||
:gsub("{NUM:wDexRatingNumMonsOwned[^}]*}", tostring(owned))
|
||||
drawTextBlock(seenOwned, 1 * 8, 14 * 8, 17 * 8)
|
||||
else
|
||||
local _, owned = self:dexSeenOwned()
|
||||
local ratingHeader = (text._DexRatingText or Strings("POKéDEX Rating{COLON}")):gsub("{COLON}", ":")
|
||||
Font.draw(ratingHeader, 1 * 8, 14 * 8)
|
||||
local rating = text[dexRatingKey(owned)] or Strings("Keep it up!")
|
||||
drawTextBlock(rating, 1 * 8, 15 * 8, 17 * 8)
|
||||
end
|
||||
end
|
||||
|
||||
function HallOfFame:draw()
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.rectangle("fill", 0, 0, 160, 144)
|
||||
@@ -309,14 +301,10 @@ function HallOfFame:draw()
|
||||
elseif self.phase == "player_stats" then
|
||||
self:drawPic(self.playerPic)
|
||||
self:drawPlayerStats()
|
||||
elseif self.phase == "player_dex" then
|
||||
elseif self.phase == "player_dex" or self.phase == "player_rating" then
|
||||
-- the TextBox chain draws the dex texts over the stat boxes
|
||||
self:drawPic(self.playerPic)
|
||||
self:drawPlayerStats()
|
||||
self:drawDexBox("seen")
|
||||
elseif self.phase == "player_rating" then
|
||||
self:drawPic(self.playerPic)
|
||||
self:drawPlayerStats()
|
||||
self:drawDexBox("rating")
|
||||
end
|
||||
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
-- Parity test: the Hall of Fame dex rating runs through the standard
|
||||
-- PrintText text box (#314).
|
||||
-- Self-contained: run via `luajit tests/parity_hof_rating.lua`; also
|
||||
-- dofile'd by tests/run_tests.lua's aggregator.
|
||||
--
|
||||
-- pokered's HoFDisplayPlayerStats (engine/movie/hall_of_fame.asm) prints
|
||||
-- three texts back to back through HoFPrintTextAndDelay -> PrintText, each
|
||||
-- followed by 120 DelayFrames: _DexSeenOwnedText, _DexRatingText
|
||||
-- ("POKéDEX Rating:"), then the tier line copied into wDexRatingText by
|
||||
-- DisplayDexRating (engine/events/pokedex_rating.asm). PrintText is the
|
||||
-- standard two-row bottom box, so the tier text's `cont` (\v) rows scroll
|
||||
-- inside it -- each with the original's ▼ + A/B wait (_ContText ->
|
||||
-- ManualTextScroll), even mid-ceremony.
|
||||
--
|
||||
-- The port instead hand-drew a 6-row box and flattened \v into plain
|
||||
-- newlines with drawTextBlock, so a 3+ row rating ("You finally got at /
|
||||
-- least 50 species! / Be sure to get ...") painted its third row on top of
|
||||
-- the bottom border and dropped the rest (#314).
|
||||
--
|
||||
-- The invariant: after the stat boxes, the induction pushes TextBox states
|
||||
-- for the seen/owned counts, the "POKéDEX Rating:" header, and the tier
|
||||
-- text, in that order, and each closes itself after the 120-frame hold.
|
||||
|
||||
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||
if not _G.love then _G.love = require("tests.love_stub") end
|
||||
local Data = require("src.core.Data")
|
||||
if not (Data.maps and Data.maps.PALLET_TOWN) then Data:load() end
|
||||
local Font = require("src.render.Font")
|
||||
if not pcall(Font.encode, "A") then Font.load(Data) end
|
||||
local S = require("tests.harness").suite("parity HoF dex rating")
|
||||
local check, eq = S.check, S.eq
|
||||
|
||||
local SaveData = require("src.core.SaveData")
|
||||
local TextBox = require("src.render.TextBox")
|
||||
local HallOfFame = require("src.ui.HallOfFame")
|
||||
|
||||
local pressed = {}
|
||||
local fakeInput = { wasPressed = function(_, b) return pressed[b] or false end,
|
||||
isDown = function() return false end }
|
||||
|
||||
local stack = { states = {} }
|
||||
function stack:push(s, ...) table.insert(self.states, s) if s.enter then s:enter(...) end end
|
||||
function stack:pop() local s = table.remove(self.states) if s and s.exit then s:exit() end return s end
|
||||
function stack:top() return self.states[#self.states] end
|
||||
|
||||
local game = { data = Data, input = fakeInput, stack = stack,
|
||||
save = SaveData.newGame() }
|
||||
game.save.party = { { species = "PIKACHU", level = 81, hp = 100,
|
||||
stats = { hp = 100 },
|
||||
moves = { { id = "THUNDERBOLT", pp = 35 } } } }
|
||||
-- 55 owned lands in the Own50To59 tier from the issue's screenshot
|
||||
local seen, owned = {}, {}
|
||||
for i = 1, 55 do seen[i] = true; owned[i] = true end
|
||||
game.save.pokedex = { seen = seen, owned = owned }
|
||||
|
||||
-- record every TextBox the induction pushes, with its full text
|
||||
local shown = {}
|
||||
local realNew = TextBox.new
|
||||
TextBox.new = function(g, text, onDone, opts)
|
||||
local self = realNew(g, text, onDone, opts)
|
||||
local lines = {}
|
||||
for _, page in ipairs(self.pages) do
|
||||
for _, line in ipairs(page) do lines[#lines + 1] = line end
|
||||
end
|
||||
shown[#shown + 1] = { text = table.concat(lines, " "), opts = opts }
|
||||
return self
|
||||
end
|
||||
|
||||
local done = false
|
||||
stack:push(HallOfFame.new(game, function() done = true end))
|
||||
|
||||
-- drive the induction with A held: mashing skips the mon phases; the dex
|
||||
-- texts must still run their course on their own
|
||||
pressed.a = true
|
||||
local guard = 0
|
||||
while not done and guard < 30000 do
|
||||
guard = guard + 1
|
||||
local top = stack:top()
|
||||
if top and top.update then top:update(1 / 60) end
|
||||
end
|
||||
pressed.a = nil
|
||||
TextBox.new = realNew -- restore: run_tests dofiles all parity suites in one process
|
||||
|
||||
check(done, "induction runs to completion")
|
||||
eq(#shown, 3, "three text boxes follow the stat boxes (seen/owned, header, rating)")
|
||||
check(shown[1] ~= nil and shown[1].text:find("Seen", 1, true) ~= nil
|
||||
and shown[1].text:find("55", 1, true) ~= nil,
|
||||
"first box is the seen/owned count, got: " ..
|
||||
tostring(shown[1] and shown[1].text))
|
||||
check(shown[2] ~= nil and shown[2].text:find("Rating", 1, true) ~= nil,
|
||||
"second box is the POKéDEX Rating: header, got: " ..
|
||||
tostring(shown[2] and shown[2].text))
|
||||
check(shown[3] ~= nil and shown[3].text:find("least 50 species!", 1, true) ~= nil,
|
||||
"third box is the Own50To59 tier text, got: " ..
|
||||
tostring(shown[3] and shown[3].text))
|
||||
|
||||
-- HoFPrintTextAndDelay = PrintText + 120 DelayFrames: each box pops itself
|
||||
-- after the hold (the rating's `cont` rows still wait for A/B, exactly like
|
||||
-- pokered's _ContText -> ManualTextScroll -- the A held above walks them)
|
||||
for i, box in ipairs(shown) do
|
||||
check(box.opts and box.opts.auto ~= nil,
|
||||
("box %d auto-closes after its 120-frame hold"):format(i))
|
||||
end
|
||||
|
||||
S.finish()
|
||||
Reference in New Issue
Block a user