mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-21 13:09:54 +02:00
Merge pull request #332 from johnjohto/fix-hof-dex-rating
Run the HoF dex rating through the standard text box (#314)
This commit is contained in:
+38
-50
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
local Assets = require("src.render.Assets")
|
local Assets = require("src.render.Assets")
|
||||||
local Font = require("src.render.Font")
|
local Font = require("src.render.Font")
|
||||||
|
local TextBox = require("src.render.TextBox")
|
||||||
local Music = require("src.core.Music")
|
local Music = require("src.core.Music")
|
||||||
local Sound = require("src.core.Sound")
|
local Sound = require("src.core.Sound")
|
||||||
local TypeChart = require("src.battle.TypeChart")
|
local TypeChart = require("src.battle.TypeChart")
|
||||||
@@ -64,17 +65,6 @@ local function dexRatingKey(owned)
|
|||||||
return ("_DexRatingText_Own%dTo%d"):format(lo, lo + 9)
|
return ("_DexRatingText_Own%dTo%d"):format(lo, lo + 9)
|
||||||
end
|
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)
|
function HallOfFame.new(game, onDone)
|
||||||
local self = setmetatable({}, HallOfFame)
|
local self = setmetatable({}, HallOfFame)
|
||||||
self.game = game
|
self.game = game
|
||||||
@@ -182,26 +172,50 @@ function HallOfFame:update(dt)
|
|||||||
self.phase = "player_stats"
|
self.phase = "player_stats"
|
||||||
self.timer = DEX_HOLD
|
self.timer = DEX_HOLD
|
||||||
elseif self.phase == "player_stats" then
|
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
|
self.timer = self.timer - 1
|
||||||
if skip or self.timer <= 0 then
|
if skip or self.timer <= 0 then
|
||||||
self.phase = "player_dex"
|
self.phase = "player_dex"
|
||||||
self.timer = DEX_HOLD
|
self:showDexTexts()
|
||||||
end
|
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
|
end
|
||||||
elseif self.phase == "player_rating" then
|
-- player_dex / player_rating are driven by the TextBox chain that
|
||||||
self.timer = self.timer - 1
|
-- showDexTexts pushes; nothing is timed here
|
||||||
if skip or self.timer <= 0 then
|
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)
|
-- HoFFadeOutScreenAndMusic -> Credits lead-in (no A wait here)
|
||||||
self.game.stack:pop()
|
game.stack:pop()
|
||||||
if self.onDone then self.onDone() end
|
if self.onDone then self.onDone() end
|
||||||
end
|
end
|
||||||
|
local function showRating()
|
||||||
|
game.stack:push(TextBox.new(game, rating, finish,
|
||||||
|
{ auto = { delay = DEX_HOLD } }))
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
-- HoFDisplayMonInfo: TextBoxBorder (0,2) b=9,c=10 + LEVEL/TYPE labels
|
-- 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)
|
Font.draw(("¥%d"):format(save.money or 0), 4 * 8, 10 * 8)
|
||||||
end
|
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()
|
function HallOfFame:draw()
|
||||||
love.graphics.setColor(1, 1, 1, 1)
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
love.graphics.rectangle("fill", 0, 0, 160, 144)
|
love.graphics.rectangle("fill", 0, 0, 160, 144)
|
||||||
@@ -309,14 +301,10 @@ function HallOfFame:draw()
|
|||||||
elseif self.phase == "player_stats" then
|
elseif self.phase == "player_stats" then
|
||||||
self:drawPic(self.playerPic)
|
self:drawPic(self.playerPic)
|
||||||
self:drawPlayerStats()
|
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:drawPic(self.playerPic)
|
||||||
self:drawPlayerStats()
|
self:drawPlayerStats()
|
||||||
self:drawDexBox("seen")
|
|
||||||
elseif self.phase == "player_rating" then
|
|
||||||
self:drawPic(self.playerPic)
|
|
||||||
self:drawPlayerStats()
|
|
||||||
self:drawDexBox("rating")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
love.graphics.setColor(1, 1, 1, 1)
|
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