mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 00:10:56 +02:00
86 lines
3.3 KiB
Lua
86 lines
3.3 KiB
Lua
-- SPRITEMOVEDATA_SPINCLOCKWISE / _SPINCOUNTERCLOCKWISE, the two spins that are
|
|
-- not random.
|
|
--
|
|
-- POKEPORT_GAME=gold POKEPORT_DRIVER=tests/drivers/gold_spin_clockwise.lua love .
|
|
--
|
|
-- MovementFunction_SpinClockwise / _SpinCounterclockwise
|
|
-- (engine/overworld/map_objects.asm:790-843) hold OBJECT_STEP_DURATION $10 --
|
|
-- sixteen frames -- on each quarter and then take the next facing out of a
|
|
-- FIXED table: clockwise is down -> left -> up -> right, counterclockwise is
|
|
-- down -> right -> up -> left. No Random is called anywhere in the loop, which
|
|
-- is the point: the Rocket base's guards are a puzzle, and a puzzle has to be
|
|
-- predictable.
|
|
--
|
|
-- The port used to answer both rows with "stand", so Route 32's Youngster
|
|
-- Gordon, Route 35's Firebreather Walt, RadioTower4F's GruntM10 and the Route
|
|
-- 40/41 swimmers never turned at all.
|
|
local U = require("tests.drivers.util")
|
|
|
|
local CLOCKWISE = { down = "left", up = "right", left = "up", right = "down" }
|
|
local COUNTER = { down = "right", up = "left", left = "down", right = "up" }
|
|
|
|
return function(game)
|
|
local out = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/gold-spin"
|
|
|
|
U.wait(45)
|
|
local world = game.world
|
|
assert(world and world.map, "gold world did not boot")
|
|
|
|
-- Route 32 has Youngster Gordon on SPRITEMOVEDATA_SPINCLOCKWISE ($1f); the
|
|
-- driver takes whatever spinners the map actually carries rather than naming
|
|
-- an object index, so a cache rebuild cannot silently retarget it.
|
|
world:setMap("ROUTE_32", 9, 12, "down")
|
|
U.wait(30)
|
|
|
|
local found = {}
|
|
for _, npc in ipairs(world.npcs) do
|
|
local m = npc.def and npc.def.movement
|
|
if m == 0x1f or m == 0x1e then
|
|
found[#found + 1] = { npc = npc, cw = (m == 0x1f) }
|
|
end
|
|
end
|
|
print(("[driver] %d fixed-spin objects on ROUTE_32"):format(#found))
|
|
assert(#found > 0, "ROUTE_32 has no SPINCLOCKWISE / SPINCOUNTERCLOCKWISE object")
|
|
|
|
U.shot(game, out .. "/00-before.png")
|
|
|
|
-- Sixteen frames a quarter, so 200 frames is a dozen turns even allowing for
|
|
-- the initial timer NPC.new seeds.
|
|
local seen = {}
|
|
for _, e in ipairs(found) do seen[e.npc] = { [e.npc.facing] = true } end
|
|
local order = {}
|
|
for _, e in ipairs(found) do order[e.npc] = {} end
|
|
for _ = 1, 200 do
|
|
for _, e in ipairs(found) do
|
|
local f = e.npc.facing
|
|
local trail = order[e.npc]
|
|
if trail[#trail] ~= f then trail[#trail + 1] = f end
|
|
seen[e.npc][f] = true
|
|
end
|
|
U.wait(1)
|
|
end
|
|
U.shot(game, out .. "/01-after.png")
|
|
|
|
for i, e in ipairs(found) do
|
|
local n = 0
|
|
for _ in pairs(seen[e.npc]) do n = n + 1 end
|
|
local trail = order[e.npc]
|
|
print(("[driver] object %d (%s): %d distinct facings over %d turns: %s")
|
|
:format(i, e.cw and "clockwise" or "counterclockwise", n, #trail - 1,
|
|
table.concat(trail, ">")))
|
|
assert(n == 4,
|
|
"a fixed spinner only reached " .. n .. " facings -- it is not turning")
|
|
-- Every consecutive pair has to be the table's own successor: a RANDOM
|
|
-- spin would pass the count above and fail here.
|
|
local want = e.cw and CLOCKWISE or COUNTER
|
|
for j = 2, #trail do
|
|
assert(trail[j] == want[trail[j - 1]],
|
|
("turn %d went %s -> %s, wanted %s"):format(j - 1, trail[j - 1],
|
|
trail[j], tostring(want[trail[j - 1]])))
|
|
end
|
|
end
|
|
|
|
print("[driver] PASS gold fixed-order spinners in " .. out)
|
|
love.event.quit()
|
|
end
|