mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
190 lines
6.8 KiB
Lua
190 lines
6.8 KiB
Lua
#!/usr/bin/env luajit
|
|
-- pokefirered/src/event_object_movement.c:4830 GetCollisionAtCoords
|
|
-- pokefirered/src/event_object_movement.c:4889 IsMetatileDirectionallyImpassable
|
|
|
|
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 function finish()
|
|
if failed > 0 then
|
|
print("[test] FAILED " .. failed)
|
|
os.exit(1)
|
|
end
|
|
print("[test] all passed")
|
|
os.exit(0)
|
|
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_stitchseam_mod_collision_test: " .. tostring(Cache.reason))
|
|
finish()
|
|
end
|
|
print("[info] FireRed cache at " .. cacheRoot)
|
|
|
|
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 Runtime = require("src.core.game3.runtime")
|
|
local Map = require("src.core.game3.map")
|
|
local Gen3Compat = require("src.mods.Gen3Compat")
|
|
local WorldAPI = require("src.world.game3.WorldAPI")
|
|
|
|
local game = { data = {} }
|
|
Dataset.hydrate(game)
|
|
local session = { map = nil, flags = {}, vars = {} }
|
|
game.session = session
|
|
Runtime.session = session
|
|
Gen3Compat.bind(function() return game end)
|
|
|
|
-- pokefirered/include/constants/metatile_behaviors.h:41
|
|
local MB_IMPASSABLE_NORTH = 0x32
|
|
local BAND = "FR_VICTORY_ROAD_1F"
|
|
local NAOMI = 2
|
|
local BX, BY = 14, 5
|
|
|
|
local bandDef = game.data.maps[BAND]
|
|
check(bandDef ~= nil, BAND .. " is in the cache")
|
|
if not bandDef then finish() end
|
|
|
|
Field._game = game
|
|
session.map = BAND
|
|
Field._session = session
|
|
Field.running = true
|
|
Field.locked = false
|
|
Map.load(nil, game, BAND)
|
|
Player.reset(11, 9, "down")
|
|
|
|
eq(Map.current, BAND, "the real map loader put the engine on " .. BAND)
|
|
eq(Collision.behavior(BX, BY), MB_IMPASSABLE_NORTH,
|
|
string.format("(%d,%d) carries MB_IMPASSABLE_NORTH", BX, BY))
|
|
check(Collision.isWalkable(BX, BY - 1), "(14,4) north of it is open cave floor")
|
|
check(Collision.isWalkable(BX, BY + 1), "(14,6) south of it is open cave floor")
|
|
|
|
print("[test] 1. mod.world npc:canStep obeys the direction-blocked metatile")
|
|
|
|
local api = WorldAPI.new(game, "stitchseam-test")
|
|
-- pokefirered/src/scrcmd.c:1074 ScrCmd_setobjectxy
|
|
Objects.setObjectXY(NAOMI, BX, BY)
|
|
local handle, why = api:npc(BAND, NAOMI)
|
|
check(handle ~= nil, "mod.world:npc handed back a live object handle (" ..
|
|
tostring(why) .. ")")
|
|
if not handle then finish() end
|
|
eq(select(1, handle:position()), BX, "the handle stands on the banded cell (x)")
|
|
eq(select(2, handle:position()), BY, "the handle stands on the banded cell (y)")
|
|
|
|
eq(handle:canStep("up"), false,
|
|
"canStep refuses the northbound step off MB_IMPASSABLE_NORTH")
|
|
eq(handle:canStep("down"), true, "and allows the southbound step off it")
|
|
eq(Collision.behavior(BX - 1, BY), MB_IMPASSABLE_NORTH,
|
|
"(13,5) carries the same band, so the seal runs along the row")
|
|
eq(handle:canStep("left"), true,
|
|
"and the band is sealed north only, so she may still walk west along it")
|
|
|
|
print("[test] 2. and the enter-blocked side of the same rule")
|
|
|
|
Objects.setObjectXY(NAOMI, BX, BY - 1)
|
|
local above = api:npc(BAND, NAOMI)
|
|
check(above ~= nil, "the handle now stands north of the band")
|
|
if not above then finish() end
|
|
eq(above:canStep("down"), false,
|
|
"canStep refuses entering MB_IMPASSABLE_NORTH from the north")
|
|
|
|
print("[test] 3. a land object no longer borrows the player's surfing flag")
|
|
|
|
local SHORE = "FR_PALLET_TOWN"
|
|
local BEACH_X, BEACH_Y = 8, 16
|
|
local shoreDef = game.data.maps[SHORE]
|
|
check(shoreDef ~= nil, SHORE .. " is in the cache")
|
|
if not shoreDef then finish() end
|
|
|
|
session.map = SHORE
|
|
Map.load(nil, game, SHORE)
|
|
Player.reset(5, 14, "down")
|
|
eq(Map.current, SHORE, "the engine is on " .. SHORE)
|
|
check(Collision.isWater(BEACH_X, BEACH_Y + 1) == true, "(8,17) is the ocean")
|
|
check(Collision.isWater(BEACH_X, BEACH_Y) == false, "(8,16) is the beach above it")
|
|
|
|
Objects.setObjectXY(1, BEACH_X, BEACH_Y)
|
|
local walker = api:npc(SHORE, 1)
|
|
check(walker ~= nil, "a Pallet Town object handle is live")
|
|
if not walker then finish() end
|
|
|
|
-- pokefirered/src/event_object_movement.c:8346 IsElevationMismatchAt
|
|
Player.surfing = true
|
|
eq(walker:canStep("down"), false,
|
|
"canStep keeps the beach walker out of the sea while the player surfs")
|
|
Player.surfing = false
|
|
eq(walker:canStep("down"), false, "and keeps her out of it on land too")
|
|
eq(walker:canStep("up"), true, "while she may still walk north along the beach")
|
|
|
|
print("[test] 4. an object that stands on water keeps the swimmer's verdict")
|
|
|
|
Objects.setObjectXY(1, BEACH_X, BEACH_Y + 1)
|
|
local swimmer = api:npc(SHORE, 1)
|
|
check(swimmer ~= nil, "the same object now treads water at (8,17)")
|
|
if not swimmer then finish() end
|
|
eq(swimmer:canStep("down"), true, "it may swim further out to (8,18)")
|
|
eq(swimmer:canStep("up"), false, "and may not climb out onto the beach")
|
|
Player.surfing = true
|
|
eq(swimmer:canStep("up"), false, "not even while the player is surfing")
|
|
Player.surfing = false
|
|
|
|
print("[test] 5. the src.world.Collision facade passes the same context")
|
|
|
|
session.map = BAND
|
|
Map.load(nil, game, BAND)
|
|
Player.reset(11, 9, "down")
|
|
Objects.setObjectXY(NAOMI, 11, 12)
|
|
|
|
local Facade = Gen3Compat.resolve("src.world.Collision")
|
|
check(type(Facade) == "table" and type(Facade.canMove) == "function",
|
|
"mods reach Collision.canMove through the Gen 3 facade")
|
|
if not (Facade and Facade.canMove) then finish() end
|
|
|
|
local mover = { cellX = BX, cellY = BY, surfing = false }
|
|
local okUp, whyUp = Facade.canMove(nil, {}, mover, "up")
|
|
eq(okUp, false, "canMove refuses the northbound step off MB_IMPASSABLE_NORTH")
|
|
eq(whyUp, "tile", "and reports it as a tile refusal")
|
|
eq(Facade.canMove(nil, {}, mover, "down"), true,
|
|
"and allows the southbound step off it")
|
|
|
|
local moverAbove = { cellX = BX, cellY = BY - 1, surfing = false }
|
|
eq(Facade.canMove(nil, {}, moverAbove, "down"), false,
|
|
"canMove refuses entering MB_IMPASSABLE_NORTH from the north")
|
|
|
|
print("[test] 6. the facade still answers with the mover's own surfing flag")
|
|
|
|
session.map = SHORE
|
|
Map.load(nil, game, SHORE)
|
|
Player.reset(5, 14, "down")
|
|
Objects.setObjectXY(1, 5, 10)
|
|
|
|
local rider = { cellX = BEACH_X, cellY = BEACH_Y + 1, surfing = true }
|
|
eq(Facade.canMove(nil, {}, rider, "up"), true,
|
|
"a surfing mover may come ashore onto the beach")
|
|
local lander = { cellX = BEACH_X, cellY = BEACH_Y, surfing = false }
|
|
local okSea, whySea = Facade.canMove(nil, {}, lander, "down")
|
|
eq(okSea, false, "a walking mover may not step into the sea")
|
|
eq(whySea, "tile", "and the sea refusal reaches the mod as a tile refusal")
|
|
|
|
finish()
|