Files
gen1recomp/tests/gen2_variable_sprites_test.lua
T
bryanthaboi ae6cac89e1 G2 support
2026-08-11 11:53:30 -04:00

87 lines
3.5 KiB
Lua

-- The new-game seed has to fill wVariableSprites, not just the event flags.
--
-- luajit tests/gen2_variable_sprites_test.lua
--
-- Found by the Gold route bot (tests/drivers/gold_bot.lua): it walked to the
-- Sudowoodo on Route 36 with the SQUIRTBOTTLE in the bag, faced (35,9), pressed
-- A and nothing happened -- because there was nothing there.
--
-- SPRITE_WEIRD_TREE is $f4, and everything from $f0 up is a wVariableSprites
-- SLOT rather than a sheet (constants/sprite_constants.asm:147). An object
-- carrying one is extracted with a NUMBER in `sprite`, World:resolveSprite
-- answers nil until the slot is filled, and World:pooledNpc spawns nothing --
-- which is faithful, because on the cart the slots are filled by
-- InitializeEventsScript, whose last nine commands are `variablesprite`.
--
-- The port's seed replayed that script's `setevent` list and dropped the rest,
-- so a new game had no Sudowoodo -- and therefore no TM08 ROCK SMASH, no
-- Burned Tower, no Morty, no FOGBADGE and no SURF -- plus no Olivine rival, no
-- Azalea Rocket, no Fuchsia Gym Janines, no Copycat and no Janine
-- impersonator.
package.path = "./?.lua;./?/init.lua;" .. package.path
local S = require("tests.harness").suite("gen2 variable sprites")
local check, eq = S.check, S.eq
local World = require("src.world.gen2.World")
-- The recovery path, in isolation: given the seed's flag list and a scripts
-- table containing the script those flags came from, find its variablesprite
-- rows. This is what lets a cache written before the extractor learned to
-- record them still put the tree on the map.
do
local w = setmetatable({
initialEvents = { 10, 20, 30 },
scripts = {
-- A decoy with some of the flags and a sprite row: must NOT match.
["a:1"] = {
{ op = "setevent", event = 10 },
{ op = "variablesprite", args = { 4, 99 } },
},
-- The real one: every seed flag, then the sprite assignments.
["b:1"] = {
{ op = "setevent", event = 10 },
{ op = "setevent", event = 20 },
{ op = "setevent", event = 30 },
{ op = "variablesprite", args = { 4, 55 } }, -- SPRITE_WEIRD_TREE
{ op = "variablesprite", args = { 5, 56 } },
{ op = "end" },
},
},
}, { __index = World })
local rows = w:findInitialSprites()
eq(#rows, 2, "found both variablesprite rows")
local bySlot = {}
for _, row in ipairs(rows) do bySlot[row.slot] = row.sprite end
eq(bySlot[4], 55, "slot 4 ($f4 SPRITE_WEIRD_TREE) takes its sprite")
eq(bySlot[5], 56, "slot 5 too")
check(bySlot[4] ~= 99, "the decoy script, which lacks two of the flags, lost")
end
-- No seed list means no guessing: a cache with no flags must not match some
-- arbitrary script that happens to contain a variablesprite.
do
local w = setmetatable({
initialEvents = {},
scripts = { ["a:1"] = { { op = "variablesprite", args = { 4, 99 } } } },
}, { __index = World })
eq(#w:findInitialSprites(), 0, "an empty seed list matches nothing")
end
-- resolveSprite is the half that reads it back, and the reason an unfilled
-- slot is invisible rather than merely wrong.
do
local w = setmetatable({
variableSprites = {},
constants = { spriteOrder = { [55] = "SPRITE_SUDOWOODO" } },
}, { __index = World })
eq(w:resolveSprite(0xf4), nil, "an unfilled slot resolves to nothing")
w.variableSprites[0xf4 - 0xf0] = 55
eq(w:resolveSprite(0xf4), "SPRITE_SUDOWOODO", "a filled slot names a sheet")
eq(w:resolveSprite("SPRITE_NURSE"), "SPRITE_NURSE",
"an ordinary sprite name passes straight through")
end
S.finish()