mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-22 05:26:45 +02:00
Cover the Gen 2 battle lines against pokegold's own labels
Drives a real turn per case and compares the emitted line to the label in data/text/battle.asm. Six of the eleven checks fail against the text as it stood. The tail case wraps each changed line through Chrome.wrap at the box's own width and holds it to the two rows printMessage draws. That is the check that caught SpikesText needing a third row, which is why Spikes is not in this change.
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
-- Five Gen 2 battle messages printed something the cart does not say. Each
|
||||
-- case below drives a real turn and compares the emitted line against the
|
||||
-- label in pokegold's data/text/battle.asm:
|
||||
--
|
||||
-- SuperEffectiveText :603
|
||||
-- NotVeryEffectiveText :608
|
||||
-- BattleText_TheresNoPPLeftForThisMove :315
|
||||
-- PlayerHitTimesText / EnemyHitTimesText:749, :755
|
||||
-- StartPerishText :986
|
||||
--
|
||||
-- The marker is the one src/import/RomExtractorGen2.lua decodes the cart's own
|
||||
-- $4e (`line`) into, so a line written here reads exactly as an extracted one
|
||||
-- would. The tail case pins the other half of that: printMessage draws at
|
||||
-- most TEXT_ROWS rows and cuts the rest, so a line has to fit two of them.
|
||||
--
|
||||
-- luajit tests/engine/gen2_battle_text_matches_cart.lua
|
||||
--
|
||||
-- ROM-free: the fixtures below are the extractor's shapes.
|
||||
|
||||
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||
|
||||
love = require("tests.love_stub")
|
||||
|
||||
local T = require("tests.harness")
|
||||
local Battle = require("src.battle.gen2.Battle")
|
||||
local Mon = require("src.battle.gen2.Mon")
|
||||
|
||||
local check = T.check
|
||||
|
||||
-- ---------------------------------------------------------------- fixtures
|
||||
|
||||
local TYPES = {
|
||||
NORMAL = { id = "NORMAL", index = 0, category = "physical" },
|
||||
GROUND = { id = "GROUND", index = 4, category = "physical" },
|
||||
ROCK = { id = "ROCK", index = 5, category = "physical" },
|
||||
FIRE = { id = "FIRE", index = 20, category = "special" },
|
||||
WATER = { id = "WATER", index = 21, category = "special" },
|
||||
}
|
||||
|
||||
local MATCHUPS = {
|
||||
{ attacker = "NORMAL", defender = "ROCK", multiplier = 5 },
|
||||
{ attacker = "WATER", defender = "FIRE", multiplier = 20 },
|
||||
}
|
||||
|
||||
local MOVES = {
|
||||
TACKLE = { id = "TACKLE", name = "TACKLE", power = 35, type = "NORMAL",
|
||||
accuracy = 100, pp = 35, effect = "EFFECT_NORMAL_HIT" },
|
||||
WATER_GUN = { id = "WATER_GUN", name = "WATER GUN", power = 40,
|
||||
type = "WATER", accuracy = 100, pp = 25, effect = "EFFECT_NORMAL_HIT" },
|
||||
DOUBLESLAP = { id = "DOUBLESLAP", name = "DOUBLESLAP", power = 15,
|
||||
type = "NORMAL", accuracy = 100, pp = 10, effect = "EFFECT_MULTI_HIT" },
|
||||
PERISH_SONG = { id = "PERISH_SONG", name = "PERISH SONG", power = 0,
|
||||
type = "NORMAL", accuracy = 100, pp = 5, effect = "EFFECT_PERISH_SONG" },
|
||||
}
|
||||
|
||||
local GROWTH = {
|
||||
GROWTH_MEDIUM_SLOW = { numerator = 6, denominator = 5, squared = -15,
|
||||
linear = 100, constant = 140 },
|
||||
}
|
||||
|
||||
local function species(id, index, types)
|
||||
return { id = id, index = index, name = id,
|
||||
baseStats = { hp = 50, attack = 60, defense = 50, speed = 50,
|
||||
specialAttack = 50, specialDefense = 50 },
|
||||
types = types, catchRate = 255, baseExp = 60,
|
||||
growthRate = "GROWTH_MEDIUM_SLOW", genderRatio = 31,
|
||||
levelMoves = { { level = 1, move = "TACKLE" } }, evolutions = {} }
|
||||
end
|
||||
|
||||
local POKEMON = {
|
||||
growthRates = GROWTH,
|
||||
CYNDAQUIL = species("CYNDAQUIL", 155, { "FIRE", "FIRE" }),
|
||||
GEODUDE = species("GEODUDE", 74, { "ROCK", "GROUND" }),
|
||||
}
|
||||
|
||||
local DATA = { pokemon = POKEMON, moves = MOVES,
|
||||
type_chart = { types = TYPES, matchups = MATCHUPS }, items = {} }
|
||||
|
||||
local perfect = { attack = 15, defense = 15, speed = 15, special = 15 }
|
||||
perfect.hp = Mon.hpDV(perfect)
|
||||
|
||||
-- The smallest roll that is neither a critical hit nor a miss.
|
||||
local function detRandom(n)
|
||||
if (n or 1) <= 1 then return 0 end
|
||||
return 1
|
||||
end
|
||||
|
||||
-- One turn of the player's move, returning every message line it printed.
|
||||
local function linesFrom(playerSpecies, playerMoves, wildSpecies, moveId)
|
||||
local player = Mon.new(DATA, playerSpecies, 20, { dvs = perfect })
|
||||
player.moves = playerMoves
|
||||
local wild = Mon.new(DATA, wildSpecies, 20, { dvs = perfect })
|
||||
wild.moves = { { id = "TACKLE", pp = 35, maxPp = 35 } }
|
||||
local battle = Battle.new({ data = DATA, party = { player }, wild = wild,
|
||||
random = detRandom })
|
||||
local said = {}
|
||||
for _, event in ipairs(battle:takeTurn({ kind = "move", move = moveId })) do
|
||||
if event.kind == "message" and event.text then
|
||||
said[#said + 1] = event.text
|
||||
end
|
||||
end
|
||||
return said
|
||||
end
|
||||
|
||||
local function saw(said, text)
|
||||
for _, line in ipairs(said) do
|
||||
if line == text then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function shown(said)
|
||||
return "printed: " .. table.concat(said, " | ")
|
||||
end
|
||||
|
||||
-- ---- the two effectiveness lines ------------------------------------------
|
||||
-- Both break across the box's two lines on the cart, and "super-" is
|
||||
-- hyphenated to make the break. The not-very line ends on the single
|
||||
-- ellipsis glyph the Gold charmap carries at $75, not on three periods.
|
||||
do
|
||||
local said = linesFrom("CYNDAQUIL",
|
||||
{ { id = "TACKLE", pp = 35, maxPp = 35 } }, "GEODUDE", "TACKLE")
|
||||
check(saw(said, "It's not very\neffective…"),
|
||||
"NotVeryEffectiveText prints as the cart writes it. " .. shown(said))
|
||||
|
||||
said = linesFrom("GEODUDE",
|
||||
{ { id = "WATER_GUN", pp = 25, maxPp = 25 } }, "CYNDAQUIL", "WATER_GUN")
|
||||
check(saw(said, "It's super-\neffective!"),
|
||||
"SuperEffectiveText keeps its hyphen and its break. " .. shown(said))
|
||||
end
|
||||
|
||||
-- ---- a move at zero PP ----------------------------------------------------
|
||||
do
|
||||
-- a second move with PP left, so the turn refuses the empty one rather
|
||||
-- than falling through to Struggle
|
||||
local said = linesFrom("CYNDAQUIL",
|
||||
{ { id = "TACKLE", pp = 0, maxPp = 35 },
|
||||
{ id = "WATER_GUN", pp = 25, maxPp = 25 } }, "GEODUDE", "TACKLE")
|
||||
check(saw(said, "There's no PP left\nfor this move!"),
|
||||
"the empty-PP refusal is the cart's sentence. " .. shown(said))
|
||||
end
|
||||
|
||||
-- ---- the multi-hit tally --------------------------------------------------
|
||||
-- "Hit @ times!" has no singular form on the cart, which is why the "(s)"
|
||||
-- this used to print is not a hedge the game ever makes. Gen 1 says it the
|
||||
-- same way (src/battle/EffectRegistry.lua, _HitXTimesText).
|
||||
do
|
||||
local said = linesFrom("CYNDAQUIL",
|
||||
{ { id = "DOUBLESLAP", pp = 10, maxPp = 10 } }, "GEODUDE", "DOUBLESLAP")
|
||||
local tally
|
||||
for _, line in ipairs(said) do
|
||||
if line:match("^Hit %d+ times!$") then tally = line end
|
||||
end
|
||||
check(tally ~= nil,
|
||||
"the multi-hit tally reads \"Hit N times!\". " .. shown(said))
|
||||
check(not table.concat(said, " "):find("time(s)", 1, true),
|
||||
"and no line hedges the plural with a parenthetical. " .. shown(said))
|
||||
end
|
||||
|
||||
-- ---- Perish Song ----------------------------------------------------------
|
||||
-- What shipped here was a sentence no cart prints. StartPerishText names
|
||||
-- both sides and counts in digits.
|
||||
do
|
||||
local said = linesFrom("CYNDAQUIL",
|
||||
{ { id = "PERISH_SONG", pp = 5, maxPp = 5 } }, "GEODUDE", "PERISH_SONG")
|
||||
check(saw(said, "Both POKéMON will\nfaint in 3 turns!"),
|
||||
"Perish Song prints StartPerishText. " .. shown(said))
|
||||
end
|
||||
|
||||
-- ---- and every one of them fits the box -----------------------------------
|
||||
-- printMessage draws Chrome.wrap(self.message, TEXT_WIDTH) and stops at
|
||||
-- TEXT_ROWS (home/text.asm:143, :397), so a third row is cut rather than
|
||||
-- spilled. A cart line that needs one, such as SpikesText's `cont` row
|
||||
-- carrying <TARGET>, cannot be told on this path at all: engine messages set
|
||||
-- self.message directly (src/ui/gen2/BattleState.lua) instead of going
|
||||
-- through showPages, so nothing paginates them. Hence Spikes is left alone
|
||||
-- above, and every line that IS changed is checked to fit here.
|
||||
do
|
||||
local Chrome = require("src.ui.gen2.Chrome")
|
||||
-- TEXT_WIDTH and TEXT_ROWS are file-locals in BattleState, so their values
|
||||
-- are repeated rather than required.
|
||||
local BOX_WIDTH, BOX_ROWS = 18, 2
|
||||
for _, line in ipairs({
|
||||
"It's super-\neffective!",
|
||||
"It's not very\neffective…",
|
||||
"There's no PP left\nfor this move!",
|
||||
"Hit 3 times!",
|
||||
"Both POKéMON will\nfaint in 3 turns!",
|
||||
}) do
|
||||
local rows = #Chrome.wrap(line, BOX_WIDTH)
|
||||
check(rows <= BOX_ROWS,
|
||||
("\"%s\" wraps to %d rows, and the box draws %d")
|
||||
:format((line:gsub("\n", "\\n")), rows, BOX_ROWS))
|
||||
end
|
||||
end
|
||||
|
||||
T.finish("gen2 battle text matches the cart")
|
||||
Reference in New Issue
Block a user