mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
149 lines
5.2 KiB
Lua
149 lines
5.2 KiB
Lua
-- CanObjectMoveInDirection (engine/overworld/npc_movement.asm:1
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local T = require("tests.harness")
|
|
local check, eq = T.check, T.eq
|
|
|
|
love = require("tests.love_stub")
|
|
|
|
local Permissions = require("src.world.gen2.Permissions")
|
|
local Map = require("src.world.gen2.Map")
|
|
local World = require("src.world.gen2.World")
|
|
local Runtime = require("src.mods.Runtime")
|
|
|
|
local FLOOR = 0x00
|
|
local UP_WALL = 0xb2
|
|
local RIGHT_WALL = 0xb0
|
|
local UP_BUOY = 0xc2
|
|
local WALL = 0x07
|
|
|
|
local function permitted(from, to, dir)
|
|
return Permissions.objectStepPermitted(from, to, dir) == true
|
|
end
|
|
|
|
do
|
|
check(not permitted(FLOOR, UP_WALL, "down"), "DOWN onto an UP_WALL is refused")
|
|
check(permitted(FLOOR, UP_WALL, "up"), "UP onto one is not")
|
|
check(permitted(FLOOR, UP_WALL, "left"), "nor LEFT")
|
|
check(permitted(UP_WALL, FLOOR, "down"), "DOWN off an UP_WALL is fine")
|
|
check(not permitted(UP_WALL, FLOOR, "up"), "UP off one is CanObjectLeaveTile")
|
|
|
|
check(not permitted(FLOOR, RIGHT_WALL, "left"), "LEFT onto a RIGHT_WALL is refused")
|
|
check(permitted(FLOOR, RIGHT_WALL, "right"), "RIGHT onto one is not")
|
|
check(not permitted(RIGHT_WALL, FLOOR, "right"), "RIGHT off one is refused")
|
|
|
|
check(not permitted(FLOOR, UP_BUOY, "down"), "DOWN onto an UP_BUOY is refused")
|
|
check(not permitted(FLOOR, UP_BUOY, "up"), "and so is UP")
|
|
check(not permitted(UP_BUOY, FLOOR, "up"), "leaving one upward is the mask")
|
|
check(permitted(UP_BUOY, FLOOR, "down"), "leaving one downward is not")
|
|
|
|
for _, dir in ipairs({ "up", "down", "left", "right" }) do
|
|
check(not permitted(FLOOR, WALL, dir), "no direction enters a WALL tile")
|
|
end
|
|
check(permitted(FLOOR, FLOOR, "down"), "plain floor to plain floor moves")
|
|
end
|
|
|
|
-- entryBlocks is the leave row's mirror, npc_movement.asm:116 against :142
|
|
do
|
|
local MIRROR = { up = "down", down = "up", left = "right", right = "left" }
|
|
for _, base in ipairs({ 0xb0, 0xc0 }) do
|
|
for coll = base, base + 7 do
|
|
local leave = Permissions.sideBlocks(coll)
|
|
local enter = Permissions.entryBlocks(coll)
|
|
local ok = true
|
|
for d in pairs(leave) do if not enter[MIRROR[d]] then ok = false end end
|
|
for d in pairs(enter) do if not leave[MIRROR[d]] then ok = false end end
|
|
check(ok, ("$%02x's entry row mirrors its exit row"):format(coll))
|
|
end
|
|
end
|
|
check(Permissions.entryBlocks(FLOOR) == nil, "a plain floor has no entry row")
|
|
end
|
|
|
|
local MAP_ID = "MOUNT_MORTAR_B1F"
|
|
|
|
local function lipMap()
|
|
local def = {
|
|
id = MAP_ID, width = 1, height = 2,
|
|
blocks = { 1, 2 }, warps = {}, connections = {},
|
|
}
|
|
local tileset = {
|
|
collision = {
|
|
{ 0xff, 0xff, 0xff, 0xff },
|
|
{ FLOOR, FLOOR, FLOOR, FLOOR },
|
|
{ UP_WALL, UP_WALL, FLOOR, FLOOR },
|
|
},
|
|
}
|
|
return Map.new(def, tileset)
|
|
end
|
|
|
|
local function boulderWorld()
|
|
local map = lipMap()
|
|
local world = World.new({ data = {}, save = { player = {} } })
|
|
world.map = map
|
|
world.entities = {}
|
|
world.strengthActive = true
|
|
local boulder = {
|
|
def = { index = 0, movement = 0x19 }, cellX = 0, cellY = 0, steps = 0,
|
|
}
|
|
boulder.scriptStep = function(self, dir)
|
|
local d = Map.DELTA[dir]
|
|
self.cellX, self.cellY = self.cellX + d[1], self.cellY + d[2]
|
|
self.steps = self.steps + 1
|
|
end
|
|
world.npcs = { boulder }
|
|
world.entities = { boulder }
|
|
return world, boulder, map
|
|
end
|
|
|
|
do
|
|
local map = lipMap()
|
|
eq(map:cellCollision(0, 0), FLOOR, "the column decodes floor at the top")
|
|
eq(map:cellCollision(0, 2), UP_WALL, "the lip sits at y=2")
|
|
eq(map:cellCollision(0, 3), FLOOR, "and the lower floor under it")
|
|
check(not map:objectStepPermitted(0, 1, "down"), "the lip refuses a step down")
|
|
check(map:objectStepPermitted(0, 0, "down"), "the step above it does not")
|
|
check(not map:objectStepPermitted(0, 3, "down"), "off-map is GetCoordTileCollision $ff")
|
|
end
|
|
|
|
do
|
|
local world, boulder = boulderWorld()
|
|
check(world:tryPushBoulder("down", 0, 0), "the first push moves the boulder")
|
|
eq(boulder.cellY, 1, "onto the cell above the lip")
|
|
check(not world:tryPushBoulder("down", 0, 1), "the second push is refused")
|
|
eq(boulder.cellY, 1, "and the boulder has not moved")
|
|
eq(boulder.steps, 1, "one scriptStep in all")
|
|
end
|
|
|
|
do
|
|
local world, boulder = boulderWorld()
|
|
boulder.cellX, boulder.cellY = 0, 2
|
|
check(world:tryPushBoulder("down", 0, 2), "DOWN off the lip is still allowed")
|
|
eq(boulder.cellY, 3, "onto the lower floor")
|
|
end
|
|
|
|
do
|
|
local seen = {}
|
|
local saved = Runtime.events
|
|
Runtime.events = {
|
|
listeners = { ["world.boulder_moved"] = true },
|
|
emit = function(_, name, payload) seen[name] = payload end,
|
|
}
|
|
local world, boulder = boulderWorld()
|
|
check(world:tryPushBoulder("down", 0, 0), "a floor-to-floor push still moves")
|
|
eq(boulder.cellY, 1, "the boulder stepped")
|
|
local p = seen["world.boulder_moved"]
|
|
check(p ~= nil, "and world.boulder_moved still fires")
|
|
eq(p and p.mapId, MAP_ID, "with the map it moved on")
|
|
eq(p and p.npcId, 1, "the one-based object id")
|
|
eq(p and p.x, 0, "the destination x")
|
|
eq(p and p.y, 1, "and the destination y")
|
|
|
|
seen = {}
|
|
check(not world:tryPushBoulder("down", 0, 1), "the refused push does not move")
|
|
check(seen["world.boulder_moved"] == nil, "and emits nothing")
|
|
Runtime.events = saved
|
|
end
|
|
|
|
T.finish("gen2 boulder side wall bug1936")
|