Add spiky box variants
This commit is contained in:
parent
761214fe3e
commit
c3a2692351
10 changed files with 340 additions and 19 deletions
82
movement.lua
82
movement.lua
|
|
@ -1,6 +1,6 @@
|
|||
-- Finds the complete set of blocks that must move together for one grid step.
|
||||
-- A block joins the set when it is in another block's destination (ordinary
|
||||
-- pushing), is behind a directional sticky puller, or touches a skewer block.
|
||||
-- pushing), is softly grabbed by sticky/spiky, or touches a rigid skewer.
|
||||
local Movement = {}
|
||||
|
||||
local adjacentDirections = {
|
||||
|
|
@ -22,7 +22,14 @@ local function isSkewer(entity)
|
|||
return entity.isSkewer and entity:isSkewer()
|
||||
end
|
||||
|
||||
function Movement.findMovableGroup(startCells, direction, entityAt, isInBounds)
|
||||
local function isSpiky(entity)
|
||||
return entity.isSpiky and entity:isSpiky()
|
||||
end
|
||||
|
||||
-- Build one rigid component: ordinary pushes are required, and skewers join
|
||||
-- along every edge. `alreadyMoving` contains cells that will be vacated during
|
||||
-- this same step, so a later pulled component may move into them.
|
||||
local function findRigidGroup(startCells, direction, entityAt, isInBounds, alreadyMoving)
|
||||
local entities = {}
|
||||
local pendingCells = {}
|
||||
for _, cell in ipairs(startCells) do
|
||||
|
|
@ -39,7 +46,8 @@ function Movement.findMovableGroup(startCells, direction, entityAt, isInBounds)
|
|||
end
|
||||
|
||||
local entity = entityAt(cell)
|
||||
if entity and not isPassable(entity) and not entities[entity] then
|
||||
if entity and not isPassable(entity) and not entities[entity] and
|
||||
not (alreadyMoving and alreadyMoving[entity]) then
|
||||
if not entity:canMove() then
|
||||
return false, entities
|
||||
end
|
||||
|
|
@ -51,9 +59,7 @@ function Movement.findMovableGroup(startCells, direction, entityAt, isInBounds)
|
|||
table.insert(pendingCells, projectedCell)
|
||||
end
|
||||
|
||||
-- Sticky blocks pull only from the trailing face, like the player. A
|
||||
-- lateral move therefore shears the contact. Skewers retain the older
|
||||
-- all-edge behaviour and make everything they touch one rigid group.
|
||||
-- Skewers make every edge contact part of this rigid component.
|
||||
for _, occupiedCell in ipairs(entity:getOccupiedCells()) do
|
||||
for _, adjacentDirection in ipairs(adjacentDirections) do
|
||||
local adjacentCell = {
|
||||
|
|
@ -62,13 +68,11 @@ function Movement.findMovableGroup(startCells, direction, entityAt, isInBounds)
|
|||
}
|
||||
if isInBounds(adjacentCell) then
|
||||
local adjacentEntity = entityAt(adjacentCell)
|
||||
local trailing = adjacentDirection.x == -direction.x and
|
||||
adjacentDirection.y == -direction.y
|
||||
local attached = adjacentEntity and (
|
||||
isSkewer(entity) or isSkewer(adjacentEntity) or
|
||||
(trailing and (isSticky(entity) or isSticky(adjacentEntity)))
|
||||
isSkewer(entity) or isSkewer(adjacentEntity)
|
||||
)
|
||||
if attached and adjacentEntity ~= entity and adjacentEntity:canMove() then
|
||||
if attached and adjacentEntity ~= entity and adjacentEntity:canMove() and
|
||||
not (alreadyMoving and alreadyMoving[adjacentEntity]) then
|
||||
table.insert(pendingCells, adjacentCell)
|
||||
end
|
||||
end
|
||||
|
|
@ -80,4 +84,60 @@ function Movement.findMovableGroup(startCells, direction, entityAt, isInBounds)
|
|||
return true, entities
|
||||
end
|
||||
|
||||
function Movement.findMovableGroup(startCells, direction, entityAt, isInBounds)
|
||||
local possible, entities = findRigidGroup(
|
||||
startCells, direction, entityAt, isInBounds
|
||||
)
|
||||
if not possible then return false, entities end
|
||||
|
||||
local movingQueue = {}
|
||||
for entity in pairs(entities) do table.insert(movingQueue, entity) end
|
||||
local attemptedPulls = {}
|
||||
local nextMover = 1
|
||||
|
||||
while nextMover <= #movingQueue do
|
||||
local entity = movingQueue[nextMover]
|
||||
nextMover = nextMover + 1
|
||||
|
||||
-- Sticky contact is soft only on the trailing face. Spiky contact is soft
|
||||
-- on every face. Either kind simply releases a blocked rigid component,
|
||||
-- without cancelling this entity or another independently pulled branch.
|
||||
for _, occupiedCell in ipairs(entity:getOccupiedCells()) do
|
||||
for _, adjacentDirection in ipairs(adjacentDirections) do
|
||||
local adjacentCell = {
|
||||
x = occupiedCell.x + adjacentDirection.x,
|
||||
y = occupiedCell.y + adjacentDirection.y,
|
||||
}
|
||||
if isInBounds(adjacentCell) then
|
||||
local adjacentEntity = entityAt(adjacentCell)
|
||||
local trailing = adjacentDirection.x == -direction.x and
|
||||
adjacentDirection.y == -direction.y
|
||||
local softContact = adjacentEntity and (
|
||||
isSpiky(entity) or isSpiky(adjacentEntity) or
|
||||
(trailing and (isSticky(entity) or isSticky(adjacentEntity)))
|
||||
)
|
||||
if softContact and adjacentEntity ~= entity and
|
||||
not entities[adjacentEntity] and not attemptedPulls[adjacentEntity] and
|
||||
adjacentEntity:canMove() then
|
||||
attemptedPulls[adjacentEntity] = true
|
||||
local canPull, pulled = findRigidGroup(
|
||||
{adjacentCell}, direction, entityAt, isInBounds, entities
|
||||
)
|
||||
if canPull then
|
||||
for pulledEntity in pairs(pulled) do
|
||||
if not entities[pulledEntity] then
|
||||
entities[pulledEntity] = pulledEntity
|
||||
table.insert(movingQueue, pulledEntity)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true, entities
|
||||
end
|
||||
|
||||
return Movement
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue