mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
123feef3a1
CLOSES #2425, CLOSES #2428, CLOSES #2431, CLOSES #2432, CLOSES #2433, CLOSES #2435, CLOSES #2436, CLOSES #2437, CLOSES #2440
279 lines
11 KiB
Lua
279 lines
11 KiB
Lua
#!/usr/bin/env luajit
|
|
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 Vm = require("src.core.game3.scripting.vm")
|
|
local Flags = require("src.core.game3.scripting.flags")
|
|
local Std = require("src.core.game3.scripting.stdscripts")
|
|
local Natives = require("src.core.game3.scripting.natives")
|
|
local okSeag, Seagallop = pcall(require, "src.core.game3.scripting.natives_seagallop")
|
|
if not okSeag then Seagallop = { WARPS = {} } end
|
|
local Fade = require("src.ui.game3.fade")
|
|
|
|
local SEAGALLOP_VERMILION_CITY = 0
|
|
local SEAGALLOP_ONE_ISLAND = 1
|
|
local SEAGALLOP_TWO_ISLAND = 2
|
|
local SEAGALLOP_THREE_ISLAND = 3
|
|
local SEAGALLOP_FOUR_ISLAND = 4
|
|
local SEAGALLOP_FIVE_ISLAND = 5
|
|
local SEAGALLOP_SIX_ISLAND = 6
|
|
local SEAGALLOP_SEVEN_ISLAND = 7
|
|
local SEAGALLOP_CINNABAR_ISLAND = 8
|
|
local SEAGALLOP_NAVEL_ROCK = 9
|
|
local SEAGALLOP_BIRTH_ISLAND = 10
|
|
local SEAGALLOP_MORE = 254
|
|
local SCR_MENU_CANCEL = 127
|
|
|
|
-- pokefirered/data/scripts/seagallop.inc:102 EventScript_SetSail
|
|
local function setSailScript(origin, dest)
|
|
return {
|
|
{ op = "setvar", var = 0x8004, value = origin },
|
|
{ op = "setvar", var = 0x8006, value = dest },
|
|
{ op = "specialvar", [1] = 0x800D, [2] = Std.SPECIAL.GetSeagallopNumber },
|
|
{ op = "fadescreen", [1] = 1 },
|
|
{ op = "special", id = Std.SPECIAL.DoSeagallopFerryScene },
|
|
{ op = "waitstate" },
|
|
{ op = "copyvar", [1] = 0x4000, [2] = 0x8006 },
|
|
{ op = "end" },
|
|
}
|
|
end
|
|
|
|
local function newVm(scripts, adapters)
|
|
return Vm.new({
|
|
store = Flags.newStore(),
|
|
scripts = scripts,
|
|
adapters = adapters,
|
|
})
|
|
end
|
|
|
|
local function logAdapters(extra)
|
|
local a = {
|
|
log = function(m) print("[log] " .. tostring(m)) end,
|
|
fadeScreen = function(mode, speed, done)
|
|
Fade.begin(mode, speed, function() if done then done() end end)
|
|
Fade.t = (mode == 1 or mode == 3) and 16 or 0
|
|
Fade.active = false
|
|
local cb = Fade.doneCb
|
|
Fade.doneCb = nil
|
|
if cb then cb() end
|
|
end,
|
|
}
|
|
for k, v in pairs(extra or {}) do a[k] = v end
|
|
return a
|
|
end
|
|
|
|
print("[test] 1. special 0x17B is bound and is not logged as unknown")
|
|
Natives.resetLog()
|
|
local unknown = {}
|
|
do
|
|
local warps = {}
|
|
local a = logAdapters({
|
|
log = function(m)
|
|
if tostring(m):match("skip unknown") then unknown[#unknown + 1] = m end
|
|
end,
|
|
warp = function(g, n, w, x, y, done, kind)
|
|
local covered = not Fade.isActive() and (tonumber(Fade.t) or 0) >= 16
|
|
warps[#warps + 1] = { g, n, w, x, y, done, kind, covered }
|
|
if done then done() end
|
|
end,
|
|
})
|
|
local vm = newVm({ sail = setSailScript(SEAGALLOP_CINNABAR_ISLAND, SEAGALLOP_ONE_ISLAND) }, a)
|
|
vm:start("sail")
|
|
check(#unknown == 0, "nothing logged as an unknown special (got " .. #unknown .. ")")
|
|
check(Flags.getVar(vm.store, vm.ctx, 0x800D) == 1,
|
|
"GetSeagallopNumber = 1 for the Cinnabar run (got " ..
|
|
tostring(Flags.getVar(vm.store, vm.ctx, 0x800D)) .. ")")
|
|
|
|
print("[test] 2. waitstate holds the script while the ferry crosses")
|
|
check(vm:isRunning(), "the script is still alive after `special` + `waitstate`")
|
|
check(#warps == 0, "no warp on the frame the ferry leaves")
|
|
local ranFor = 0
|
|
for _ = 1, 400 do
|
|
if #warps > 0 then break end
|
|
ranFor = ranFor + 1
|
|
vm:tick()
|
|
end
|
|
check(#warps == 1, "the ferry warped exactly once (got " .. #warps .. ")")
|
|
check(ranFor >= 100, "waitstate suspended the script for the crossing, ticks=" .. ranFor)
|
|
local w = warps[1]
|
|
check(w and w[1] == 32 and w[2] == 4,
|
|
"warped to MAP_ONE_ISLAND_HARBOR group 32 num 4 (got " ..
|
|
tostring(w and w[1]) .. "/" .. tostring(w and w[2]) .. ")")
|
|
check(w and w[4] == 8 and w[5] == 5,
|
|
"landed on sSeag coords (8,5), got (" .. tostring(w and w[4]) .. "," .. tostring(w and w[5]) .. ")")
|
|
|
|
print("[test] 3. the ferry warps from a covered screen and leaves the fade-in to the warp")
|
|
check(w and w[7] == "seagallop", "the ferry warp is a seagallop warp (got " .. tostring(w and w[7]) .. ")")
|
|
check(w and w[8] == true, "the screen was covered when the ferry warped")
|
|
check(Fade.mode ~= Fade.MODE.FROM_BLACK and Fade.t >= 16,
|
|
"the ferry does not start its own FADE_FROM_BLACK (mode=" .. tostring(Fade.mode) .. ")")
|
|
|
|
print("[test] 4. the script finishes after the warp")
|
|
for _ = 1, 10 do vm:tick() end
|
|
check(not vm:isRunning(), "the VM reached `end` instead of hanging on waitstate")
|
|
end
|
|
|
|
print("[test] 5. every seagallop id has the pret warp row")
|
|
local WANT = {
|
|
[SEAGALLOP_VERMILION_CITY] = { 3, 5, 0x17, 0x20 },
|
|
[SEAGALLOP_ONE_ISLAND] = { 32, 4, 8, 5 },
|
|
[SEAGALLOP_TWO_ISLAND] = { 33, 4, 8, 5 },
|
|
[SEAGALLOP_THREE_ISLAND] = { 38, 0, 8, 5 },
|
|
[SEAGALLOP_FOUR_ISLAND] = { 35, 5, 8, 5 },
|
|
[SEAGALLOP_FIVE_ISLAND] = { 36, 2, 8, 5 },
|
|
[SEAGALLOP_SIX_ISLAND] = { 37, 2, 8, 5 },
|
|
[SEAGALLOP_SEVEN_ISLAND] = { 31, 6, 8, 5 },
|
|
[SEAGALLOP_CINNABAR_ISLAND] = { 3, 8, 0x15, 0x07 },
|
|
[SEAGALLOP_NAVEL_ROCK] = { 2, 59, 8, 5 },
|
|
[SEAGALLOP_BIRTH_ISLAND] = { 2, 58, 8, 5 },
|
|
}
|
|
for id, want in pairs(WANT) do
|
|
local got = Seagallop.WARPS[id]
|
|
check(got and got[1] == want[1] and got[2] == want[2] and got[3] == want[3] and got[4] == want[4],
|
|
string.format("seagallop %d -> %d.%d (%d,%d)", id, want[1], want[2], want[3], want[4]))
|
|
end
|
|
|
|
print("[test] 6. an out-of-range destination falls back to Vermilion")
|
|
do
|
|
local warps = {}
|
|
local a = logAdapters({
|
|
warp = function(g, n, w, x, y, done)
|
|
warps[#warps + 1] = { g, n, w, x, y }
|
|
if done then done() end
|
|
end,
|
|
})
|
|
local vm = newVm({ sail = setSailScript(SEAGALLOP_ONE_ISLAND, 40) }, a)
|
|
vm:start("sail")
|
|
for _ = 1, 400 do
|
|
if #warps > 0 then break end
|
|
vm:tick()
|
|
end
|
|
check(#warps == 1 and warps[1][1] == 3 and warps[1][2] == 5,
|
|
"dest 40 warped to Vermilion City 3.5")
|
|
for _ = 1, 5 do vm:tick() end
|
|
check(Flags.getVar(vm.store, vm.ctx, 0x4000) == SEAGALLOP_VERMILION_CITY,
|
|
"VAR_0x8006 was clamped to 0 (got " ..
|
|
tostring(Flags.getVar(vm.store, vm.ctx, 0x4000)) .. ")")
|
|
end
|
|
|
|
print("[test] 7. GetSeagallopNumber matches the pret cascade")
|
|
check(type(Seagallop.seagallopNumber) == "function", "the seagallop module is present")
|
|
if type(Seagallop.seagallopNumber) ~= "function" then
|
|
print("[test] " .. (failed + 1) .. " failed")
|
|
os.exit(1)
|
|
end
|
|
local NUM = {
|
|
{ SEAGALLOP_VERMILION_CITY, SEAGALLOP_ONE_ISLAND, 7 },
|
|
{ SEAGALLOP_CINNABAR_ISLAND, SEAGALLOP_ONE_ISLAND, 1 },
|
|
{ SEAGALLOP_ONE_ISLAND, SEAGALLOP_CINNABAR_ISLAND, 1 },
|
|
{ SEAGALLOP_VERMILION_CITY, SEAGALLOP_NAVEL_ROCK, 7 },
|
|
{ SEAGALLOP_ONE_ISLAND, SEAGALLOP_NAVEL_ROCK, 10 },
|
|
{ SEAGALLOP_ONE_ISLAND, SEAGALLOP_BIRTH_ISLAND, 12 },
|
|
{ SEAGALLOP_ONE_ISLAND, SEAGALLOP_THREE_ISLAND, 2 },
|
|
{ SEAGALLOP_FOUR_ISLAND, SEAGALLOP_FIVE_ISLAND, 3 },
|
|
{ SEAGALLOP_SIX_ISLAND, SEAGALLOP_SEVEN_ISLAND, 5 },
|
|
{ SEAGALLOP_THREE_ISLAND, SEAGALLOP_FOUR_ISLAND, 6 },
|
|
}
|
|
for _, row in ipairs(NUM) do
|
|
local got = Seagallop.seagallopNumber(row[1], row[2])
|
|
check(got == row[3], string.format("seagallop number %d -> %d is %d (got %s)",
|
|
row[1], row[2], row[3], tostring(got)))
|
|
end
|
|
|
|
print("[test] 8. the destination menu drops the port you are standing in")
|
|
if not require("tests.game3_cache").mount() then
|
|
print("[skip] sSeagallopDestStrings come from the ROM: " .. tostring(require("tests.game3_cache").reason))
|
|
else
|
|
local labels = Seagallop.destinationMenu(SEAGALLOP_ONE_ISLAND, 0)
|
|
check(#labels == 6, "page 0 has six rows (got " .. #labels .. ")")
|
|
check(labels[1] == "VERMILION" and labels[2] == "TWO ISLAND"
|
|
and labels[3] == "THREE ISLAND" and labels[4] == "FOUR ISLAND",
|
|
"ONE ISLAND is skipped: " .. table.concat(labels, "/"))
|
|
check(labels[5] == "OTHER" and labels[6] == "EXIT", "OTHER and EXIT close the page")
|
|
local p1 = Seagallop.destinationMenu(SEAGALLOP_ONE_ISLAND, 1)
|
|
check(#p1 == 5 and p1[1] == "FIVE ISLAND" and p1[2] == "SIX ISLAND"
|
|
and p1[3] == "SEVEN ISLAND", "page 1 from One Island: " .. table.concat(p1, "/"))
|
|
local p1b = Seagallop.destinationMenu(SEAGALLOP_SIX_ISLAND, 1)
|
|
check(p1b[1] == "FOUR ISLAND" and p1b[2] == "FIVE ISLAND" and p1b[3] == "SEVEN ISLAND",
|
|
"page 1 from Six Island: " .. table.concat(p1b, "/"))
|
|
end
|
|
|
|
print("[test] 9. GetSelectedSeagallopDestination maps the row back to a port")
|
|
do
|
|
local sel = Seagallop.selectedDestination
|
|
check(sel(SEAGALLOP_ONE_ISLAND, 0, 0) == SEAGALLOP_VERMILION_CITY, "page 0 row 0 = Vermilion")
|
|
check(sel(SEAGALLOP_ONE_ISLAND, 0, 1) == SEAGALLOP_TWO_ISLAND, "page 0 row 1 = Two Island")
|
|
check(sel(SEAGALLOP_ONE_ISLAND, 0, 4) == SEAGALLOP_MORE, "page 0 row 4 = OTHER")
|
|
check(sel(SEAGALLOP_ONE_ISLAND, 0, 5) == SCR_MENU_CANCEL, "page 0 row 5 = EXIT")
|
|
check(sel(SEAGALLOP_ONE_ISLAND, 0, SCR_MENU_CANCEL) == SCR_MENU_CANCEL, "B press cancels")
|
|
check(sel(SEAGALLOP_ONE_ISLAND, 1, 0) == SEAGALLOP_FIVE_ISLAND, "page 1 row 0 from One Island")
|
|
check(sel(SEAGALLOP_SIX_ISLAND, 1, 0) == SEAGALLOP_FOUR_ISLAND, "page 1 row 0 from Six Island")
|
|
check(sel(SEAGALLOP_SIX_ISLAND, 1, 2) == SEAGALLOP_SEVEN_ISLAND, "page 1 row 2 from Six Island")
|
|
check(sel(SEAGALLOP_ONE_ISLAND, 1, 3) == SEAGALLOP_MORE, "page 1 row 3 = OTHER")
|
|
check(sel(SEAGALLOP_ONE_ISLAND, 1, 4) == SCR_MENU_CANCEL, "page 1 row 4 = EXIT")
|
|
end
|
|
|
|
print("[test] 10. the menu special drives the shared multichoice and sets VAR_0x8006")
|
|
if not require("tests.game3_cache").mount() then
|
|
print("[skip] the ferry menu labels come from the ROM: " .. tostring(require("tests.game3_cache").reason))
|
|
else
|
|
local shown
|
|
local a = logAdapters({
|
|
multichoice = function(row, cb)
|
|
local Multi = require("src.core.game3.scripting.multichoice")
|
|
local entry = Multi.LISTS[row[3]]
|
|
shown = entry and entry.labels
|
|
cb(1)
|
|
end,
|
|
})
|
|
local vm = newVm({
|
|
page1 = {
|
|
{ op = "setvar", var = 0x8004, value = SEAGALLOP_ONE_ISLAND },
|
|
{ op = "setvar", var = 0x8005, value = 0 },
|
|
{ op = "special", id = Std.SPECIAL.DrawSeagallopDestinationMenu },
|
|
{ op = "waitstate" },
|
|
{ op = "specialvar", [1] = 0x8006, [2] = Std.SPECIAL.GetSelectedSeagallopDestination },
|
|
{ op = "copyvar", [1] = 0x4000, [2] = 0x8006 },
|
|
{ op = "end" },
|
|
},
|
|
}, a)
|
|
vm:start("page1")
|
|
for _ = 1, 10 do vm:tick() end
|
|
check(shown ~= nil and #shown == 6, "the ferry menu reached the multichoice adapter")
|
|
check(Flags.getVar(vm.store, vm.ctx, 0x4000) == SEAGALLOP_TWO_ISLAND,
|
|
"picking row 1 from One Island sails to Two Island (got " ..
|
|
tostring(Flags.getVar(vm.store, vm.ctx, 0x4000)) .. ")")
|
|
check(not vm:isRunning(), "the menu script finished")
|
|
end
|
|
|
|
print("[test] 11. waitstate with nothing pending is still a no-op")
|
|
do
|
|
local a = logAdapters({})
|
|
local vm = newVm({
|
|
plain = {
|
|
{ op = "waitstate" },
|
|
{ op = "setflag", [1] = 0x300 },
|
|
{ op = "end" },
|
|
},
|
|
}, a)
|
|
vm:start("plain")
|
|
check(Flags.getFlag(vm.store, nil, 0x300) == true, "a bare waitstate fell straight through")
|
|
check(not vm:isRunning(), "and the script ended in the same frame")
|
|
end
|
|
|
|
if failed == 0 then
|
|
print("[test] all passed")
|
|
os.exit(0)
|
|
end
|
|
print("[test] " .. failed .. " failed")
|
|
os.exit(1)
|