wired switches and doors
This commit is contained in:
parent
ee7d36e72b
commit
bc4976b324
16 changed files with 2575 additions and 835 deletions
206
tests/test_wire.lua
Normal file
206
tests/test_wire.lua
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
package.path = "./?.lua;" .. package.path
|
||||
|
||||
class = require "libs/middleclass"
|
||||
local Wire = require "wire"
|
||||
require "world"
|
||||
|
||||
gridWidth, gridHeight = 2, 2
|
||||
|
||||
local passed = 0
|
||||
local function test(name, fn)
|
||||
local ok, err = pcall(fn)
|
||||
if not ok then
|
||||
io.stderr:write("FAIL: " .. name .. "\n" .. err .. "\n")
|
||||
os.exit(1)
|
||||
end
|
||||
passed = passed + 1
|
||||
end
|
||||
|
||||
local function assertTrue(value, message)
|
||||
if not value then error(message or "expected true", 2) end
|
||||
end
|
||||
|
||||
local function assertFalse(value, message)
|
||||
if value then error(message or "expected false", 2) end
|
||||
end
|
||||
|
||||
local function switch(color, x, y)
|
||||
local entity = {x = x, y = y, activated = false}
|
||||
entity.color = {getname = function() return color end}
|
||||
function entity:getGridPos() return {x = self.x, y = self.y} end
|
||||
function entity:activate() self.activated = true end
|
||||
function entity:deactivate() self.activated = false end
|
||||
return entity
|
||||
end
|
||||
|
||||
local function door(circuitId, x, y)
|
||||
local entity = {circuitId = circuitId, x = x, y = y, powered = false}
|
||||
function entity:getGridPos() return {x = self.x, y = self.y} end
|
||||
function entity:setPowered(powered) self.powered = powered end
|
||||
return entity
|
||||
end
|
||||
|
||||
local function makeWorld(secondSwitch)
|
||||
local blueSwitch = switch("blue", 2, 1)
|
||||
local wiredDoor = door("circuit_a", 1, 1)
|
||||
local roomA = {
|
||||
name = "a", player = {}, wiredSwitches = {}, wiredDoors = {wiredDoor},
|
||||
wires = {[1] = {
|
||||
[1] = {circuit_a = Wire.EAST},
|
||||
[2] = {circuit_a = Wire.WEST + Wire.EAST},
|
||||
}},
|
||||
}
|
||||
local roomB = {
|
||||
name = "b", player = {}, wiredSwitches = {blueSwitch}, wiredDoors = {},
|
||||
wires = {[1] = {
|
||||
[1] = {circuit_a = Wire.WEST + Wire.EAST},
|
||||
[2] = {circuit_a = Wire.WEST},
|
||||
}},
|
||||
}
|
||||
local function wireCircuitAt(room, cell)
|
||||
local tracks = room.wires[cell.y] and room.wires[cell.y][cell.x]
|
||||
for circuitId in pairs(tracks or {}) do return circuitId end
|
||||
end
|
||||
roomA.wireCircuitAt = wireCircuitAt
|
||||
roomB.wireCircuitAt = wireCircuitAt
|
||||
if secondSwitch then table.insert(roomB.wiredSwitches, secondSwitch) end
|
||||
|
||||
local instance = setmetatable({
|
||||
levels = {a = roomA, b = roomB},
|
||||
roomByName = {a = {name = "a", x = 1, y = 1}, b = {name = "b", x = 2, y = 1}},
|
||||
collidableMatrices = {red = {}, green = {}, blue = {}},
|
||||
}, {__index = World})
|
||||
function instance:roomCell(cell)
|
||||
if cell.x <= gridWidth then return roomA, {x = cell.x, y = cell.y} end
|
||||
return roomB, {x = cell.x - gridWidth, y = cell.y}
|
||||
end
|
||||
local function wireTracksAt(room, cell, create)
|
||||
local row = room.wires[cell.y]
|
||||
if not row and create then row = {}; room.wires[cell.y] = row end
|
||||
local tracks = row and row[cell.x]
|
||||
if not tracks and create then tracks = {}; row[cell.x] = tracks end
|
||||
return tracks
|
||||
end
|
||||
roomA.wireTracksAt = wireTracksAt
|
||||
roomB.wireTracksAt = wireTracksAt
|
||||
return instance, roomA, roomB, wiredDoor, blueSwitch
|
||||
end
|
||||
|
||||
test("direction masks add and remove without duplicating bits", function()
|
||||
local mask = Wire.add(0, Wire.EAST)
|
||||
mask = Wire.add(mask, Wire.EAST)
|
||||
assertTrue(mask == Wire.EAST)
|
||||
assertTrue(Wire.has(mask, Wire.EAST))
|
||||
assertFalse(Wire.has(mask, Wire.WEST))
|
||||
assertTrue(Wire.remove(mask, Wire.EAST) == 0)
|
||||
end)
|
||||
|
||||
test("orphaned and overlapping wire cells remain selectable without a door", function()
|
||||
local world, roomA = makeWorld()
|
||||
roomA.wires[1][1].orphan_b = Wire.SOUTH
|
||||
local top = world:circuitAt({x = 1, y = 1})
|
||||
assertTrue(top == "orphan_b", "top orphaned circuit was not selectable")
|
||||
local preferred = world:circuitAt({x = 1, y = 1}, "circuit_a")
|
||||
assertTrue(preferred == "circuit_a", "selected overlapping circuit was not preferred")
|
||||
end)
|
||||
|
||||
test("a white switch powers every layer of a white door", function()
|
||||
local world, roomA, roomB = makeWorld()
|
||||
roomA.wiredDoors = {
|
||||
door("circuit_a", 1, 1),
|
||||
door("old_green_id", 1, 1),
|
||||
door("old_blue_id", 1, 1),
|
||||
}
|
||||
roomB.wiredSwitches = {
|
||||
switch("red", 2, 1),
|
||||
switch("green", 2, 1),
|
||||
switch("blue", 2, 1),
|
||||
}
|
||||
world.collidableMatrices.red[1] = {[4] = {}}
|
||||
world.collidableMatrices.green[1] = {[4] = {}}
|
||||
world.collidableMatrices.blue[1] = {[4] = {}}
|
||||
world:resolveCircuits()
|
||||
|
||||
for _, wiredDoor in ipairs(roomA.wiredDoors) do
|
||||
assertTrue(wiredDoor.circuitId == "circuit_a", "door layer did not adopt terminal circuit")
|
||||
assertTrue(world.circuitState.doorPowered[wiredDoor], "white door layer remained locked")
|
||||
end
|
||||
local color = world:wireColorAt(roomA, {x = 1, y = 1}, "circuit_a")
|
||||
assertTrue(color[1] == 1 and color[2] == 1 and color[3] == 1, "white switch did not produce white wire")
|
||||
local redGreenOnly = world:wireColorAt(roomA, {x = 1, y = 1}, "circuit_a", {red = true, green = true})
|
||||
assertTrue(redGreenOnly[1] == 1 and redGreenOnly[2] == 1 and redGreenOnly[3] == 0,
|
||||
"white wire did not obey the room's visible player channels")
|
||||
end)
|
||||
|
||||
test("world matrices join a circuit across a room boundary", function()
|
||||
local world, _, _, wiredDoor, blueSwitch = makeWorld()
|
||||
world.collidableMatrices.blue[1] = {[4] = {}}
|
||||
world:resolveCircuits()
|
||||
assertTrue(world.wireMatrix[1][2].circuit_a ~= nil)
|
||||
assertTrue(world.wireMatrix[1][3].circuit_a ~= nil)
|
||||
assertTrue(world.wiredDoorMatrix[1][1][1].entity == wiredDoor)
|
||||
assertTrue(world.wiredSwitchMatrix[1][4][1].entity == blueSwitch)
|
||||
assertTrue(world.circuitState.switchPressed[blueSwitch])
|
||||
assertTrue(world.circuitState.doorPowered[wiredDoor])
|
||||
assertTrue(wiredDoor.powered)
|
||||
assertTrue(world:wireColorAt(world.levels.a, {x = 1, y = 1}, "circuit_a", {red = true, green = true}) == nil,
|
||||
"blue wire leaked into a room without a blue player channel")
|
||||
local visibleBlue = world:wireColorAt(world.levels.a, {x = 1, y = 1}, "circuit_a", {blue = true})
|
||||
assertTrue(visibleBlue[1] == 0 and visibleBlue[2] == 0 and visibleBlue[3] == 1,
|
||||
"blue wire did not render in a room with a blue player channel")
|
||||
end)
|
||||
|
||||
test("wire color is re-derived when its terminal switch changes", function()
|
||||
local world, roomA, roomB = makeWorld()
|
||||
world:resolveCircuits()
|
||||
local colors = world:wireSwitchColorsAt(roomA, {x = 1, y = 1}, "circuit_a")
|
||||
assertTrue(colors.blue and not colors.red)
|
||||
roomB.wiredSwitches = {switch("red", 2, 1)}
|
||||
world:resolveCircuits()
|
||||
colors = world:wireSwitchColorsAt(roomA, {x = 1, y = 1}, "circuit_a")
|
||||
assertTrue(colors.red and not colors.blue, "wire retained the deleted terminal switch color")
|
||||
end)
|
||||
|
||||
test("a switchless wire component has no gameplay color", function()
|
||||
local world, roomA, roomB = makeWorld()
|
||||
roomB.wiredSwitches = {}
|
||||
world:resolveCircuits()
|
||||
assertTrue(world:wireSwitchColorsAt(roomA, {x = 1, y = 1}, "circuit_a") == nil)
|
||||
assertTrue(world:wireColorAt(roomA, {x = 1, y = 1}, "circuit_a") == nil)
|
||||
end)
|
||||
|
||||
test("a door requires every independent switch run at its terminal", function()
|
||||
local greenSwitch = switch("green", 1, 2)
|
||||
local world, roomA, _, wiredDoor = makeWorld()
|
||||
table.insert(roomA.wiredSwitches, greenSwitch)
|
||||
roomA.wires[1][1].circuit_b = Wire.SOUTH
|
||||
roomA.wires[2] = {[1] = {circuit_b = Wire.NORTH}}
|
||||
world.collidableMatrices.blue[1] = {[4] = {}}
|
||||
world:resolveCircuits()
|
||||
assertFalse(world.circuitState.doorPowered[wiredDoor])
|
||||
world.collidableMatrices.green[2] = {[1] = {}}
|
||||
world:resolveCircuits()
|
||||
assertTrue(world.circuitState.doorPowered[wiredDoor])
|
||||
end)
|
||||
|
||||
test("one run cannot terminate at two different switches", function()
|
||||
local second = switch("red", 1, 2)
|
||||
local world, _, roomB = makeWorld(second)
|
||||
world:resolveCircuits()
|
||||
local rooms, err = world:addWireConnection({x = 3, y = 1}, {x = 3, y = 2}, "circuit_a")
|
||||
assertTrue(#rooms == 0 and err ~= nil, "second switch terminal was accepted")
|
||||
assertTrue(roomB.wires[2] == nil or roomB.wires[2][1] == nil,
|
||||
"rejected second terminal left a connected wire cell")
|
||||
local colors = world:wireSwitchColorsAt(world.levels.a, {x = 1, y = 1}, "circuit_a")
|
||||
assertTrue(colors.blue and not colors.red, "run stopped deriving color from its sole terminal switch")
|
||||
end)
|
||||
|
||||
test("matching IDs do not bridge a gap or one-sided room edge", function()
|
||||
local world, _, roomB, wiredDoor = makeWorld()
|
||||
world.collidableMatrices.blue[1] = {[4] = {}}
|
||||
roomB.wires[1][1].circuit_a = Wire.EAST -- no reciprocal WEST edge
|
||||
world:resolveCircuits()
|
||||
assertFalse(world.circuitState.doorPowered[wiredDoor])
|
||||
end)
|
||||
|
||||
io.write("PASS: " .. passed .. " wire tests\n")
|
||||
Loading…
Add table
Add a link
Reference in a new issue