mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
218 lines
8.3 KiB
Lua
218 lines
8.3 KiB
Lua
#!/usr/bin/env luajit
|
|
-- Test Seafoam Islands B3F / B4F boulder puzzle, current stopping, and hole falling.
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local failed = 0
|
|
local function check(cond, msg)
|
|
if cond then
|
|
print("[ok] " .. msg)
|
|
else
|
|
failed = failed + 1
|
|
print("[FAIL] " .. msg)
|
|
end
|
|
end
|
|
|
|
local function eq(a, b, msg)
|
|
check(a == b, string.format("%s (%s == %s)", msg, tostring(a), tostring(b)))
|
|
end
|
|
|
|
local GameVersion = require("src.core.GameVersion")
|
|
GameVersion.set("firered")
|
|
|
|
local Cache = require("tests.game3_cache")
|
|
local cacheRoot = Cache.mount("scripts/events.lua", { native = true })
|
|
if not cacheRoot then
|
|
print("[skip] game3_seafoam_puzzle_test: " .. tostring(Cache.reason))
|
|
os.exit(0)
|
|
end
|
|
|
|
local Schema = require("src.core.game3.save_schema_firered")
|
|
local Dataset = require("src.core.game3.dataset")
|
|
local Collision = require("src.core.game3.collision")
|
|
local Objects = require("src.core.game3.objects")
|
|
local Player = require("src.core.game3.player")
|
|
local Field = require("src.core.game3.field")
|
|
local FieldMoves = require("src.core.game3.field_moves")
|
|
local Space = require("src.core.game3.scripting.space")
|
|
local Flags = require("src.core.game3.scripting.flags")
|
|
local Runtime = require("src.core.game3.runtime")
|
|
local Warp = require("src.core.game3.warp")
|
|
|
|
local game = { data = {} }
|
|
Dataset.hydrate(game)
|
|
|
|
local session = Schema.newGame({ name = "RED" })
|
|
game.session = session
|
|
Runtime.session = session
|
|
package.loaded["src.core.game3.runtime"] = {
|
|
getSession = function() return session end,
|
|
isActive = function() return true end,
|
|
_game = game,
|
|
}
|
|
|
|
local B3F = "FR_SEAFOAM_ISLANDS_B3F"
|
|
local B4F = "FR_SEAFOAM_ISLANDS_B4F"
|
|
|
|
local FLAG_HIDE_SEAFOAM_B3F_BOULDER_1 = 0x46 -- 70
|
|
local FLAG_HIDE_SEAFOAM_B3F_BOULDER_2 = 0x47 -- 71
|
|
local FLAG_HIDE_SEAFOAM_B3F_BOULDER_3 = 0x48 -- 72
|
|
local FLAG_HIDE_SEAFOAM_B3F_BOULDER_4 = 0x49 -- 73
|
|
local FLAG_HIDE_SEAFOAM_B3F_BOULDER_5 = 0x4A -- 74
|
|
local FLAG_HIDE_SEAFOAM_B3F_BOULDER_6 = 0x4B -- 75
|
|
local FLAG_HIDE_SEAFOAM_B4F_BOULDER_1 = 0x4C -- 76
|
|
local FLAG_HIDE_SEAFOAM_B4F_BOULDER_2 = 0x4D -- 77
|
|
local FLAG_STOPPED_SEAFOAM_B3F_CURRENT = 0x2D2 -- 722
|
|
local FLAG_STOPPED_SEAFOAM_B4F_CURRENT = 0x2D3 -- 723
|
|
|
|
local function enterMap(mapId)
|
|
local def = game.data.maps[mapId]
|
|
if not def then return nil end
|
|
Field._game = game
|
|
session.map = mapId
|
|
Field._session = session
|
|
Field.running = true
|
|
Field.locked = false
|
|
Field.clearMetatiles()
|
|
Space.activate(nil, mapId, game, nil)
|
|
Collision.bindMap(game, mapId, def)
|
|
Objects.loadMap(game, mapId, def)
|
|
Space.runEnterScripts(nil, mapId, game, nil)
|
|
return def
|
|
end
|
|
|
|
local function standAt(x, y, facing)
|
|
Player.moving = false
|
|
Player.progress = 0
|
|
Player.surfing = false
|
|
Player.biking = false
|
|
Player.cellX, Player.cellY = x, y
|
|
Player.targetX, Player.targetY = x, y
|
|
Player.px, Player.py = x * 16, y * 16
|
|
Player.facing = facing or "down"
|
|
end
|
|
|
|
local function pumpVm(limit)
|
|
local vm = Space.vm
|
|
if not vm then return 0 end
|
|
local n = 0
|
|
while vm:isRunning() and n < (limit or 512) do
|
|
if Player and Player.tick then
|
|
Player.tick(game)
|
|
end
|
|
if Objects and Objects.update then
|
|
Objects.update(game)
|
|
end
|
|
vm:tick()
|
|
n = n + 1
|
|
end
|
|
return n
|
|
end
|
|
|
|
-- pokefirered/src/field_player_avatar.c:1445 DoBoulderFinish
|
|
local function pumpPush(frames)
|
|
for _ = 1, frames or 33 do
|
|
Objects.update(game)
|
|
Player.update(game, nil)
|
|
end
|
|
end
|
|
|
|
print("[test] 1. New game initial Seafoam flag state")
|
|
check(Flags.getFlag(session, nil, FLAG_HIDE_SEAFOAM_B3F_BOULDER_1) == false, "B3F boulder 1 is not hidden by the new-game reset")
|
|
check(Flags.getFlag(session, nil, FLAG_HIDE_SEAFOAM_B4F_BOULDER_1) == false, "B4F boulder 1 is not hidden by the new-game reset")
|
|
-- data/maps/Route20/scripts.inc:5
|
|
enterMap("FR_ROUTE_20")
|
|
pumpVm()
|
|
check(Flags.getFlag(Space.store, nil, FLAG_HIDE_SEAFOAM_B4F_BOULDER_1) == true, "B4F boulder 1 starts HIDDEN")
|
|
check(Flags.getFlag(Space.store, nil, FLAG_HIDE_SEAFOAM_B4F_BOULDER_2) == true, "B4F boulder 2 starts HIDDEN")
|
|
check(Flags.getFlag(Space.store, nil, FLAG_HIDE_SEAFOAM_B3F_BOULDER_1) == true, "B3F boulder 1 starts HIDDEN")
|
|
check(Flags.getFlag(Space.store, nil, FLAG_HIDE_SEAFOAM_B3F_BOULDER_2) == true, "B3F boulder 2 starts HIDDEN")
|
|
check(Flags.getFlag(session, nil, FLAG_STOPPED_SEAFOAM_B3F_CURRENT) == false, "B3F current starts ACTIVE")
|
|
check(Flags.getFlag(session, nil, FLAG_STOPPED_SEAFOAM_B4F_CURRENT) == false, "B4F current starts ACTIVE")
|
|
|
|
print("[test] 2. Enter B4F before solving puzzle: current remains ACTIVE, no boulders in water")
|
|
enterMap(B4F)
|
|
pumpVm()
|
|
check(Flags.getFlag(session, nil, FLAG_STOPPED_SEAFOAM_B4F_CURRENT) == false, "B4F current NOT stopped on enter")
|
|
local b4fB1 = Objects.find(1)
|
|
local b4fB2 = Objects.find(2)
|
|
check(b4fB1 == nil or b4fB1.visible == false, "B4F boulder 1 object is not visible in water")
|
|
check(b4fB2 == nil or b4fB2.visible == false, "B4F boulder 2 object is not visible in water")
|
|
|
|
print("[test] 3. Enter B3F before solving puzzle: pushable boulders visible, dropped boulders hidden")
|
|
enterMap(B3F)
|
|
pumpVm()
|
|
check(Flags.getFlag(session, nil, FLAG_STOPPED_SEAFOAM_B3F_CURRENT) == false, "B3F current NOT stopped on enter")
|
|
local b3fB3 = Objects.find(3)
|
|
local b3fB6 = Objects.find(6)
|
|
check(b3fB3 ~= nil and b3fB3.visible ~= false, "B3F pushable boulder 3 is visible")
|
|
check(b3fB6 ~= nil and b3fB6.visible ~= false, "B3F pushable boulder 6 is visible")
|
|
|
|
print("[test] 4. Push both boulders on B3F into holes (drops to B4F)")
|
|
Flags.setFlag(Space.store, nil, FieldMoves.SYS_FLAGS.USE_STRENGTH, true)
|
|
|
|
-- Move boulder 6 (at 6,17) into hole at (6,18)
|
|
standAt(6, 16, "down")
|
|
local r1 = Player.tryMove("down", game, false)
|
|
eq(r1, "push", "boulder 6 pushed into hole")
|
|
pumpPush()
|
|
check(Flags.getFlag(Space.store, nil, FLAG_HIDE_SEAFOAM_B4F_BOULDER_1) == false, "B4F boulder 1 is revealed (unhidden)")
|
|
|
|
-- Position boulder 3 at (9,17) and push into hole at (9,18)
|
|
Objects.setObjectXY(3, 9, 17)
|
|
standAt(9, 16, "down")
|
|
local r2 = Player.tryMove("down", game, false)
|
|
eq(r2, "push", "boulder 3 pushed into hole")
|
|
pumpPush()
|
|
check(Flags.getFlag(Space.store, nil, FLAG_HIDE_SEAFOAM_B4F_BOULDER_2) == false, "B4F boulder 2 is revealed (unhidden)")
|
|
|
|
print("[test] 5. Enter B4F after dropping both boulders: current STOPS, calm water layout applied")
|
|
enterMap(B4F)
|
|
pumpVm()
|
|
check(Flags.getFlag(Space.store, nil, FLAG_STOPPED_SEAFOAM_B4F_CURRENT) == true, "B4F current is now STOPPED")
|
|
local b4fB1_after = Objects.find(1)
|
|
local b4fB2_after = Objects.find(2)
|
|
check(b4fB1_after ~= nil and b4fB1_after.visible ~= false, "B4F boulder 1 is now visible in water")
|
|
check(b4fB2_after ~= nil and b4fB2_after.visible ~= false, "B4F boulder 2 is now visible in water")
|
|
|
|
print("[test] 6. Falling through hole to B4F when current is active triggers sweep onFrame")
|
|
-- Reset flags to test falling when current is active
|
|
Flags.setFlag(Space.store, nil, FLAG_STOPPED_SEAFOAM_B4F_CURRENT, false)
|
|
Flags.setFlag(Space.store, nil, FLAG_HIDE_SEAFOAM_B4F_BOULDER_1, true)
|
|
Flags.setFlag(Space.store, nil, FLAG_HIDE_SEAFOAM_B4F_BOULDER_2, true)
|
|
|
|
enterMap(B4F)
|
|
Flags.setVar(Space.store, Space.vm.ctx, 0x4001, 1)
|
|
Player.surfing = true
|
|
Player.cellX, Player.cellY = 8, 17 -- Actual drop hole landing position
|
|
|
|
-- Trigger onFrame script
|
|
Space.runOnFrame()
|
|
pumpVm()
|
|
-- Current sweep script executes SeafoamIslandsB4F_CurrentDumpsPlayerOnLand
|
|
eq(Player.surfing, false, "player was dumped on land and is no longer surfing")
|
|
eq(Player.facing, "up", "player facing snapped to up (North)")
|
|
eq(Player.cellY, 12, "player jumped up onto the stairs/land (y=12)")
|
|
|
|
print("[test] 7. Legacy save repair: empty flags restored with correct defaults")
|
|
local legacySave = {
|
|
schemaVersion = 1,
|
|
name = "ASH",
|
|
flags = {}, -- completely unseeded legacy flags
|
|
vars = {},
|
|
}
|
|
local restoredSession = Schema.fromSaveTable(legacySave)
|
|
check(Flags.getFlag(restoredSession, nil, 0x02C) == true, "Legacy save: Pallet Town Oak is hidden")
|
|
check(Flags.getFlag(restoredSession, nil, 0x033) == true, "Legacy save: Bill (human) is hidden")
|
|
check(Flags.getFlag(restoredSession, nil, 0x092) == true, "Legacy save: Pewter running shoes aide is hidden")
|
|
check(Flags.getFlag(restoredSession, nil, 0x035) == true, "Legacy save: Mr. Fuji in house is hidden")
|
|
check(Flags.getFlag(restoredSession, nil, 0x05A) == true, "Legacy save: Oak in champ room is hidden")
|
|
|
|
if failed > 0 then
|
|
print("[FAIL] " .. failed .. " tests failed")
|
|
os.exit(1)
|
|
end
|
|
print("[test] ALL SEAFOAM PUZZLE AND SAVE REPAIR TESTS PASSED!")
|
|
os.exit(0)
|
|
|