Prevent doors closing through occupants
This commit is contained in:
parent
bf09a8844a
commit
9731202739
5 changed files with 108 additions and 6 deletions
5
door.lua
5
door.lua
|
|
@ -11,6 +11,7 @@ function Door:initialize(x, y, color)
|
|||
collision = "unmoveable",
|
||||
sprite = loadSprite("art/game/door_" .. color:sub(1, 1) .. ".png"),
|
||||
})
|
||||
self.isDoor = true
|
||||
self.locked = true
|
||||
self:refreshSprites()
|
||||
end
|
||||
|
|
@ -27,10 +28,10 @@ function Door:refreshSprites()
|
|||
self.sprites = { [self.doorColor] = sprite }
|
||||
end
|
||||
|
||||
function Door:setColors(colors)
|
||||
function Door:setColors(colors, canClose)
|
||||
if colors[self.doorColor] then
|
||||
if self.locked then self:unlock() end
|
||||
else
|
||||
elseif canClose ~= false then
|
||||
if not self.locked then self:lock() end
|
||||
end
|
||||
|
||||
|
|
|
|||
21
room.lua
21
room.lua
|
|
@ -538,10 +538,29 @@ end
|
|||
function Room:resolveSwitchesAndDoors()
|
||||
local setDoorColors = self:switchCheck()
|
||||
for _, door in pairs(self.doors) do
|
||||
door:setColors(setDoorColors)
|
||||
door:setColors(setDoorColors, self:doorCanClose(door))
|
||||
end
|
||||
end
|
||||
|
||||
-- Match World:doorCanClose for ordinary room-local doors. All colour layers
|
||||
-- count, while other layers of the same doorway do not obstruct each other.
|
||||
function Room:doorCanClose(door)
|
||||
for _, cell in ipairs(door:getOccupiedCells()) do
|
||||
for _, player in pairs(self.player or {}) do
|
||||
if player:occupiesCell(cell) then return false end
|
||||
end
|
||||
for _, entities in pairs(self.collideables or {}) do
|
||||
for _, entity in ipairs(entities) do
|
||||
if entity ~= door and not entity.isDoor and entity:occupiesCell(cell) and
|
||||
not (entity.canPassOver and entity:canPassOver()) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function Room:playerJoinCheck(previousPositions, newPositions)
|
||||
local activate = {}
|
||||
local deactivate = {}
|
||||
|
|
|
|||
|
|
@ -192,6 +192,66 @@ test("an orphaned stub at a door does not block a valid switch run", function()
|
|||
assertTrue(wiredDoor.powered)
|
||||
end)
|
||||
|
||||
test("real wired and regular doors stay open while any player or collidable blocks the doorway", function()
|
||||
local oldLoadSprite = loadSprite
|
||||
loadSprite = function()
|
||||
return {getWidth = function() return 16 end, getHeight = function() return 16 end}
|
||||
end
|
||||
require "color"
|
||||
require "wired_door"
|
||||
|
||||
local function occupant(x, y)
|
||||
local entity = {x = x, y = y}
|
||||
function entity:occupiesCell(cell) return self.x == cell.x and self.y == cell.y end
|
||||
function entity:canPassOver() return false end
|
||||
return entity
|
||||
end
|
||||
|
||||
local wired = WiredDoor:new(2, 2, "blue", "circuit_a")
|
||||
local ordinary = Door:new(4, 2, "red")
|
||||
local room = {
|
||||
player = {green = occupant(2, 2)},
|
||||
collideables = {
|
||||
red = {ordinary}, green = {occupant(4, 2)}, blue = {wired},
|
||||
},
|
||||
}
|
||||
local world = setmetatable({}, {__index = World})
|
||||
|
||||
wired:setPowered(true)
|
||||
assertFalse(wired.locked, "wired door did not open")
|
||||
assertFalse(world:doorCanClose(room, wired), "green player did not block blue wired door")
|
||||
wired:setPowered(false, world:doorCanClose(room, wired))
|
||||
assertFalse(wired.locked, "wired door shut on player")
|
||||
|
||||
ordinary:setColors({red = true})
|
||||
assertFalse(ordinary.locked, "regular door did not open")
|
||||
assertFalse(world:doorCanClose(room, ordinary), "green collidable did not block red regular door")
|
||||
ordinary:setColors({red = false}, world:doorCanClose(room, ordinary))
|
||||
assertFalse(ordinary.locked, "regular door shut on collidable")
|
||||
|
||||
room.player.green.x, room.player.green.y = 1, 1
|
||||
room.collideables.green[1].x, room.collideables.green[1].y = 1, 1
|
||||
wired:setPowered(false, world:doorCanClose(room, wired))
|
||||
ordinary:setColors({red = false}, world:doorCanClose(room, ordinary))
|
||||
assertTrue(wired.locked, "wired door did not close once clear")
|
||||
assertTrue(ordinary.locked, "regular door did not close once clear")
|
||||
loadSprite = oldLoadSprite
|
||||
end)
|
||||
|
||||
test("circuit resolution passes doorway occupancy to a wired door", function()
|
||||
local world, roomA, _, wiredDoor = makeWorld()
|
||||
local player = {x = 1, y = 1}
|
||||
function player:occupiesCell(cell) return self.x == cell.x and self.y == cell.y end
|
||||
roomA.player.green = player
|
||||
function wiredDoor:setPowered(powered, canClose)
|
||||
self.powered, self.canClose = powered, canClose
|
||||
end
|
||||
|
||||
world:resolveCircuits()
|
||||
assertFalse(wiredDoor.powered, "unpressed switch unexpectedly powered door")
|
||||
assertFalse(wiredDoor.canClose, "resolver omitted player doorway obstruction")
|
||||
end)
|
||||
|
||||
test("selecting a parked colour changes to its room", function()
|
||||
local function player()
|
||||
local entity = {switched = false}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ function WiredDoor:initialize(x, y, color, circuitId)
|
|||
self.wired = true
|
||||
end
|
||||
|
||||
function WiredDoor:setPowered(powered)
|
||||
function WiredDoor:setPowered(powered, canClose)
|
||||
if powered then
|
||||
if self.locked then self:unlock() end
|
||||
elseif not self.locked then
|
||||
elseif canClose ~= false and not self.locked then
|
||||
self:lock()
|
||||
end
|
||||
self:refreshSprites()
|
||||
|
|
|
|||
24
world.lua
24
world.lua
|
|
@ -284,6 +284,28 @@ function World:isSwitchPressed(room, switch)
|
|||
return target ~= nil or (player and player:occupiesCell(switch:getGridPos())) or false
|
||||
end
|
||||
|
||||
-- Check every rendered/colliding colour layer. A red or green player/box can
|
||||
-- visibly occupy a blue door cell, so checking only the door's own layer lets
|
||||
-- wired doors close through an apparent occupant. Other door layers at the
|
||||
-- same location are intentionally ignored: they are one layered doorway.
|
||||
function World:doorCanClose(room, door)
|
||||
local cells = door.getOccupiedCells and door:getOccupiedCells() or {door:getGridPos()}
|
||||
for _, cell in ipairs(cells) do
|
||||
for _, player in pairs(room.player or {}) do
|
||||
if player:occupiesCell(cell) then return false end
|
||||
end
|
||||
for _, entities in pairs(room.collideables or {}) do
|
||||
for _, entity in ipairs(entities) do
|
||||
if entity ~= door and not entity.isDoor and entity:occupiesCell(cell) and
|
||||
not (entity.canPassOver and entity:canPassOver()) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function World:rebuildCircuitMatrices()
|
||||
self.wireMatrix = {}
|
||||
self.wiredSwitchMatrix = {}
|
||||
|
|
@ -448,7 +470,7 @@ function World:resolveCircuits()
|
|||
doorPowered[door] = powered
|
||||
-- locked/collision is a projection of world state onto the physical
|
||||
-- room entity so the existing movement renderer can consume it.
|
||||
door:setPowered(powered)
|
||||
door:setPowered(powered, self:doorCanClose(device.room, door))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue