player blocker

This commit is contained in:
Your Name 2026-08-11 20:29:35 -04:00
parent 0618cfc51d
commit a60f46d585
12 changed files with 1141 additions and 89 deletions

View file

@ -14,6 +14,7 @@ require "generic_sticky"
require "generic_skewer"
require "generic_spiky"
require "generic_unmoveable"
require "generic_player_blocker"
local Movement = require "movement"
@ -22,6 +23,7 @@ function Room:initialize(t)
self.switches = { red = {}, green = {}, blue = {} }
self.doors = {}
self.collideables = { red = {}, green = {}, blue = {} }
self.playerBlockers = { red = {}, green = {}, blue = {} }
self.npcs = {}
self.activeColors = {red = false, green = false, blue = false}
self.colorsPlayerHas = {}
@ -104,8 +106,10 @@ function Room:initialize(t)
self.switchMatrices = {}
self.collidableMatrices = {} --{red = {{...}{...}{...}}, ...
self.playerBlockerMatrices = {}
self:createSwitchMatrix()
self:createCollideablesMatrix()
self:createPlayerBlockerMatrix()
-- Resolve switches/doors up front so the very first frame renders correct
-- lock state: a block already sitting on a switch presses it before anything
@ -128,6 +132,7 @@ function Room:draw()
for _, channel in ipairs(self.colorsPlayerHas) do
if self.player[channel] then self.player[channel]:draw(channel) end
for _, entity in pairs(self.collideables[channel]) do entity:draw(channel) end
for _, blocker in pairs(self.playerBlockers[channel]) do blocker:draw(channel) end
for _, switch in pairs(self.switches[channel]) do switch:draw(channel) end
for _, npc in ipairs(self.npcs) do npc:draw(channel) end
end
@ -153,6 +158,12 @@ function Room:createSwitchMatrix()
end
end
function Room:createPlayerBlockerMatrix()
for color, blockers in pairs(self.playerBlockers) do
self.playerBlockerMatrices[color] = self:createEntityMatrix(blockers)
end
end
function Room:createEntityMatrix(entities)
local matrix = {}
@ -220,10 +231,16 @@ function Room:movePlayer(key)
end
if initialTargetEntity then initialTargetEntity:bump() end
local playerBlocker
if self:isInBounds(initialTargetCell) and self.playerBlockerMatrices and self.playerBlockerMatrices[color] then
playerBlocker = self:getCellInMatrix(self.playerBlockerMatrices[color], initialTargetCell)
end
if not self:isInBounds(initialTargetCell) then
-- let's us move out of bounds to Change Rooms without pulling a bloc_
move = true
elseif playerBlocker and playerBlocker:blocksPlayer() then
move = false
else
move, entitiesToPush = Movement.findMovableGroup(
{initialTargetCell}, dir,
@ -351,6 +368,7 @@ function Room:tick()
end
end
self:createCollideablesMatrix()
self:createPlayerBlockerMatrix()
self:createSwitchMatrix()
self:resolveSwitchesAndDoors()
self:nextRoomCheck()
@ -466,7 +484,7 @@ end
-- Instantiate one object from the flat {class,color,x,y} schema. `class` names an
-- art asset, and its .meta declares the behaviour ("door"/"switch"/"player"/
-- "moveable"/"sticky"/"skewer"/"spiky"); anything without a .meta defaults to immoveable. npc is the one
-- "moveable"/"sticky"/"skewer"/"spiky"/"player_blocker"); anything without a .meta defaults to immoveable. npc is the one
-- keyword handled directly since it has no art.
function Room:createEntity(class, color, x, y)
if class == "npc" then
@ -509,7 +527,10 @@ function Room:createEntity(class, color, x, y)
-- collides on its real cells and its gaps read as empty space.
local cellShape = props.cells and props.cells[color]
if cellShape and #cellShape == 0 then cellShape = nil end
if behavior == "moveable" then
if behavior == "player_blocker" then
entity = GenericPlayerBlocker:new(x, y, color, sprite, cellShape)
self:registerPlayerBlocker(entity, color)
elseif behavior == "moveable" then
entity = GenericMoveable:new(x, y, color, sprite, class, cellShape)
elseif behavior == "sticky" then
entity = GenericSticky:new(x, y, color, sprite, class, cellShape)
@ -520,7 +541,9 @@ function Room:createEntity(class, color, x, y)
else
entity = GenericUnmoveable:new(x, y, color, sprite, cellShape)
end
self:registerCollidable(entity, {color})
if behavior ~= "player_blocker" then
self:registerCollidable(entity, {color})
end
end
if entity then entity.saveClass = class end
@ -534,6 +557,10 @@ function Room:registerCollidable(entityPointer, colorArray)
end
end
function Room:registerPlayerBlocker(entityPointer, color)
table.insert(self.playerBlockers[color], entityPointer)
end
function Room:registerDoor(entityPointer, color)
table.insert(self.doors, entityPointer)
self:registerCollidable(entityPointer, color)
@ -591,6 +618,14 @@ function Room:attemptDelete(mouseX, mouseY, color)
table.remove(entityArray, index)
end
end
-- Player-only blockers are rendered entities, but use their own collision
-- matrix so blocks can overlap them.
local blockerArray = self.playerBlockers[c]
for index = #blockerArray, 1, -1 do
if blockerArray[index]:occupiesCell(cell) then
table.remove(blockerArray, index)
end
end
-- switches
local switchArray = self.switches[c]
for index = #switchArray, 1, -1 do
@ -714,6 +749,12 @@ function Room:serialize(includePlayers)
end
end
for color, blockerArray in pairs(self.playerBlockers) do
for _, blocker in pairs(blockerArray) do
emit(blocker.saveClass or "player_blocker", color, blocker)
end
end
for color, switchArray in pairs(self.switches) do
for _, switch in ipairs(switchArray) do
emit("switch", color, switch)