mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 05:32:07 -04:00
276 lines
9.8 KiB
Lua
276 lines
9.8 KiB
Lua
#!/usr/bin/env luajit
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local GameVersion = require("src.core.GameVersion")
|
|
GameVersion.set("firered")
|
|
|
|
local Flags = require("src.core.game3.scripting.flags")
|
|
local Vm = require("src.core.game3.scripting.vm")
|
|
local Adapters = require("src.core.game3.scripting.adapters")
|
|
local Player = require("src.core.game3.player")
|
|
local Objects = require("src.core.game3.objects")
|
|
|
|
local failed = 0
|
|
local function check(cond, msg)
|
|
if cond then
|
|
print("[ok] " .. msg)
|
|
else
|
|
failed = failed + 1
|
|
print("[FAIL] " .. msg)
|
|
end
|
|
end
|
|
|
|
local TOWER = "FR_POKEMON_TOWER_7F"
|
|
local FUJI = 10
|
|
local FLAG_HIDE_TOWER_FUJI = 0x34
|
|
local FLAG_HIDE_POKEHOUSE_FUJI = 0x35
|
|
local FLAG_RESCUED_MR_FUJI = 0x23C
|
|
|
|
local function towerDef()
|
|
return {
|
|
midLayout = { width = 20, height = 40 },
|
|
objects = {
|
|
{ localId = FUJI, graphicsId = 30, x = 11, y = 4, movementType = 8, flag = FLAG_HIDE_TOWER_FUJI },
|
|
{ localId = 2, graphicsId = 40, x = 11, y = 34, movementType = 8, flag = 0x3F0 },
|
|
{ localId = 3, graphicsId = 40, x = 5, y = 5, movementType = 8, flag = 0x3F1 },
|
|
},
|
|
}
|
|
end
|
|
|
|
local function placePlayer(x, y)
|
|
Player.cellX, Player.cellY = x, y
|
|
Player.targetX, Player.targetY = x, y
|
|
Player.px, Player.py = x * 16, y * 16
|
|
Player.moving = false
|
|
end
|
|
|
|
local function fresh()
|
|
Objects.reset()
|
|
Objects.loadMap(nil, TOWER, towerDef())
|
|
end
|
|
|
|
print("[test] 1. setflag on an on-screen object's hide flag leaves it visible")
|
|
fresh()
|
|
placePlayer(11, 5)
|
|
Objects.syncFlagVisibility(FLAG_HIDE_TOWER_FUJI, true)
|
|
local fuji = Objects.find(FUJI)
|
|
check(fuji and fuji.visible and not fuji.hidden, "Fuji still visible after his hide flag is set")
|
|
|
|
print("[test] 2. an object outside the camera box is culled and stays gone")
|
|
Objects.syncFlagVisibility(0x3F0, true)
|
|
local far = Objects.find(2)
|
|
check(far and far.hidden and not far.visible, "far object (11,34) hidden")
|
|
|
|
print("[test] 3. clearflag still respawns a removed object")
|
|
Objects.removeObject(3)
|
|
check(Objects.find(3).hidden, "object 3 removed")
|
|
Objects.syncFlagVisibility(0x3F1, false)
|
|
check(Objects.find(3).visible and not Objects.find(3).hidden, "clearflag brings object 3 back")
|
|
|
|
print("[test] 4. forced sync (mod SDK) still hides on-screen objects")
|
|
fresh()
|
|
placePlayer(11, 5)
|
|
Objects.syncFlagVisibility(FLAG_HIDE_TOWER_FUJI, true, true)
|
|
check(Objects.find(FUJI).hidden, "forced hide removes Fuji")
|
|
|
|
print("[test] 5. Mr Fuji's script leaves him on screen through the msgbox")
|
|
fresh()
|
|
placePlayer(11, 5)
|
|
local store = Flags.newStore()
|
|
Flags.setFlag(store, nil, FLAG_HIDE_POKEHOUSE_FUJI, true)
|
|
local vm = Vm.new({
|
|
store = store,
|
|
adapters = Adapters.host(nil, nil, nil),
|
|
scripts = {
|
|
fuji = {
|
|
{ op = "setflag", [1] = FLAG_HIDE_TOWER_FUJI },
|
|
{ op = "clearflag", [1] = FLAG_HIDE_POKEHOUSE_FUJI },
|
|
{ op = "setflag", [1] = FLAG_RESCUED_MR_FUJI },
|
|
{ op = "end" },
|
|
},
|
|
},
|
|
})
|
|
vm:start("fuji")
|
|
for _ = 1, 16 do
|
|
if not vm:isRunning() then break end
|
|
vm:tick()
|
|
end
|
|
check(Flags.getFlag(store, vm.ctx, FLAG_HIDE_TOWER_FUJI), "FLAG_HIDE_TOWER_FUJI set")
|
|
check(Flags.getFlag(store, vm.ctx, FLAG_RESCUED_MR_FUJI), "FLAG_RESCUED_MR_FUJI set")
|
|
check(not Flags.getFlag(store, vm.ctx, FLAG_HIDE_POKEHOUSE_FUJI), "FLAG_HIDE_POKEHOUSE_FUJI cleared")
|
|
fuji = Objects.find(FUJI)
|
|
check(fuji and fuji.visible and not fuji.hidden, "Fuji still drawn after the script's setflag")
|
|
local drawn = false
|
|
for _, eo in ipairs(Objects.forDraw()) do
|
|
if eo.localId == FUJI then drawn = true end
|
|
end
|
|
check(drawn, "Fuji in forDraw")
|
|
|
|
print("[test] 6. ON_TRANSITION / ON_LOAD hides run before spawn: in-view objects are gone")
|
|
local Space = require("src.core.game3.scripting.space")
|
|
local VERMILION = "FR_VERMILION_CITY"
|
|
local AIDE = 8
|
|
local FLAG_HIDE_AIDE = 0x0A1
|
|
local FLAG_HIDE_LOAD = 0x3F2
|
|
local function vermilionDef()
|
|
return {
|
|
midLayout = { width = 50, height = 40 },
|
|
objects = {
|
|
{ localId = AIDE, graphicsId = 30, x = 25, y = 7, movementType = 8, flag = FLAG_HIDE_AIDE },
|
|
{ localId = 9, graphicsId = 40, x = 16, y = 8, movementType = 8, flag = FLAG_HIDE_LOAD },
|
|
},
|
|
}
|
|
end
|
|
local enterStore = Flags.newStore()
|
|
local saved = {
|
|
bundle = Space.bundle, vm = Space.vm, store = Space.store,
|
|
mapId = Space.mapId, active = Space.active,
|
|
}
|
|
Space.bundle = {
|
|
events = {
|
|
[VERMILION] = { mapScripts = { onTransition = "vermilion_transition", onLoad = "vermilion_load" } },
|
|
},
|
|
}
|
|
Space.store = enterStore
|
|
Space.vm = Vm.new({
|
|
store = enterStore,
|
|
adapters = Adapters.host(nil, nil, nil),
|
|
scripts = {
|
|
-- data/maps/VermilionCity/scripts.inc:28
|
|
vermilion_transition = {
|
|
{ op = "setflag", [1] = FLAG_HIDE_AIDE },
|
|
{ op = "end" },
|
|
},
|
|
vermilion_load = {
|
|
{ op = "setflag", [1] = FLAG_HIDE_LOAD },
|
|
{ op = "end" },
|
|
},
|
|
later = {
|
|
{ op = "setflag", [1] = FLAG_HIDE_AIDE },
|
|
{ op = "end" },
|
|
},
|
|
},
|
|
})
|
|
Space.mapId = VERMILION
|
|
Space.active = true
|
|
Objects.reset()
|
|
Objects.loadMap(nil, VERMILION, vermilionDef())
|
|
placePlayer(15, 7)
|
|
local okEnter, errEnter = pcall(Space.runEnterScripts, nil, VERMILION, nil, nil, { enterVia = "warp" })
|
|
check(okEnter, "runEnterScripts ran" .. (okEnter and "" or (": " .. tostring(errEnter))))
|
|
check(Flags.getFlag(enterStore, nil, FLAG_HIDE_AIDE), "ON_TRANSITION set the aide hide flag")
|
|
local aide = Objects.find(AIDE)
|
|
check(aide and aide.hidden and not aide.visible, "aide at (25,7) hidden though in view of (15,7)")
|
|
check(Objects.find(9) and Objects.find(9).hidden, "ON_LOAD setflag hides an in-view object")
|
|
check(not Space._inTransition, "transition window closed after the drain")
|
|
|
|
print("[test] 7. setflag after the entry drain keeps cart semantics")
|
|
Objects.syncFlagVisibility(FLAG_HIDE_AIDE, false)
|
|
check(Objects.find(AIDE).visible, "aide respawned by clearflag")
|
|
Flags.setFlag(enterStore, nil, FLAG_HIDE_AIDE, false)
|
|
Space.vm:start("later")
|
|
for _ = 1, 16 do
|
|
if not Space.vm:isRunning() then break end
|
|
Space.vm:tick()
|
|
end
|
|
check(Objects.find(AIDE).visible, "mid-map setflag leaves the in-view aide drawn")
|
|
|
|
Space.bundle, Space.vm, Space.store = saved.bundle, saved.vm, saved.store
|
|
Space.mapId, Space.active = saved.mapId, saved.active
|
|
|
|
local PEWTER = "FR_PEWTER_CITY"
|
|
local GYM_GUIDE, MUSEUM_GUIDE = 5, 2
|
|
local FLAG_HIDE_GYM_GUIDE, FLAG_HIDE_MUSEUM_GUIDE = 0x2E, 0x2F
|
|
local function pewterDef()
|
|
return {
|
|
midLayout = { width = 50, height = 40 },
|
|
objects = {
|
|
{ localId = MUSEUM_GUIDE, graphicsId = 30, x = 33, y = 17, movementType = 8, flag = FLAG_HIDE_MUSEUM_GUIDE },
|
|
{ localId = GYM_GUIDE, graphicsId = 30, x = 42, y = 20, movementType = 8, flag = FLAG_HIDE_GYM_GUIDE },
|
|
},
|
|
}
|
|
end
|
|
local function freshPewter()
|
|
Objects.reset()
|
|
Objects.loadMap(nil, PEWTER, pewterDef())
|
|
end
|
|
local function strand(lid, x, y, facing)
|
|
local eo = Objects.find(lid)
|
|
eo.cellX, eo.cellY, eo.targetX, eo.targetY = x, y, x, y
|
|
eo.px, eo.py = x * 16, y * 16
|
|
eo.facing = facing
|
|
eo.frozen, eo.scriptBusy = true, true
|
|
if eo.def then eo.def.x, eo.def.y = x, y end
|
|
Objects._tracks[lid] = { done = false }
|
|
return eo
|
|
end
|
|
local function atTemplate(lid, x, y, label)
|
|
local eo = Objects.find(lid)
|
|
check(eo and eo.visible and not eo.hidden, label .. ": visible")
|
|
check(eo and eo.cellX == x and eo.cellY == y, label .. string.format(": at template (%d,%d), got (%s,%s)",
|
|
x, y, tostring(eo and eo.cellX), tostring(eo and eo.cellY)))
|
|
check(eo and eo.homeX == x and eo.homeY == y and eo.px == x * 16 and eo.py == y * 16, label .. ": home/px reset")
|
|
check(eo and eo.facing == "down", label .. ": template facing down, got " .. tostring(eo and eo.facing))
|
|
check(eo and not eo.moving and not eo.frozen and not eo.scriptBusy, label .. ": idle")
|
|
check(Objects._tracks[lid] == nil, label .. ": no stale track")
|
|
end
|
|
|
|
print("[test] 8. removeobject + clearflag respawns the Pewter gym guide at his template cell")
|
|
freshPewter()
|
|
placePlayer(15, 18)
|
|
local stale = strand(GYM_GUIDE, 7, 19, "left")
|
|
Objects.removeObject(GYM_GUIDE)
|
|
Objects.syncFlagVisibility(FLAG_HIDE_GYM_GUIDE, false)
|
|
atTemplate(GYM_GUIDE, 42, 20, "gym guide")
|
|
check(Objects.find(GYM_GUIDE) ~= stale, "gym guide is a fresh object")
|
|
|
|
print("[test] 9. museum guide sibling")
|
|
freshPewter()
|
|
placePlayer(20, 17)
|
|
strand(MUSEUM_GUIDE, 24, 15, "up")
|
|
Objects.removeObject(MUSEUM_GUIDE)
|
|
Objects.syncFlagVisibility(FLAG_HIDE_MUSEUM_GUIDE, false)
|
|
atTemplate(MUSEUM_GUIDE, 33, 17, "museum guide")
|
|
|
|
print("[test] 10. removeobject + addobject respawns from the template too")
|
|
freshPewter()
|
|
placePlayer(15, 18)
|
|
strand(GYM_GUIDE, 7, 19, "left")
|
|
Objects.removeObject(GYM_GUIDE)
|
|
check(Objects.addObject(GYM_GUIDE), "addobject ok")
|
|
atTemplate(GYM_GUIDE, 42, 20, "addobject gym guide")
|
|
local n = 0
|
|
for _, id in ipairs(Objects._order) do if id == GYM_GUIDE then n = n + 1 end end
|
|
check(n == 1, "gym guide listed once in draw order")
|
|
|
|
print("[test] 11. a perm row still places the respawn (Oak's lab shape)")
|
|
freshPewter()
|
|
placePlayer(15, 18)
|
|
strand(GYM_GUIDE, 7, 19, "left")
|
|
Objects.removeObject(GYM_GUIDE)
|
|
Objects.setObjectXY(GYM_GUIDE, 6, 3)
|
|
Objects.setMovementType(GYM_GUIDE, 8)
|
|
Objects.syncFlagVisibility(FLAG_HIDE_GYM_GUIDE, false)
|
|
local permEo = Objects.find(GYM_GUIDE)
|
|
check(permEo.visible and permEo.cellX == 6 and permEo.cellY == 3 and permEo.facing == "down",
|
|
"perm row wins over the template")
|
|
|
|
print("[test] 12. clearflag leaves an active object where it is")
|
|
freshPewter()
|
|
placePlayer(15, 18)
|
|
local live = strand(GYM_GUIDE, 16, 18, "left")
|
|
live.frozen, live.scriptBusy = false, false
|
|
Objects._tracks[GYM_GUIDE] = nil
|
|
Objects.rememberPerm(PEWTER, GYM_GUIDE, { x = 6, y = 3, movementType = 7 })
|
|
Objects.syncFlagVisibility(FLAG_HIDE_GYM_GUIDE, false)
|
|
check(Objects.find(GYM_GUIDE) == live and live.cellX == 16 and live.cellY == 18 and live.facing == "left",
|
|
"active gym guide untouched by clearflag")
|
|
check(Objects.addObject(GYM_GUIDE) and Objects.find(GYM_GUIDE) == live, "addobject on an active object is a no-op")
|
|
|
|
if failed > 0 then
|
|
print("[test] FAILED " .. failed)
|
|
os.exit(1)
|
|
end
|
|
print("[test] all passed")
|