Replace box variants with a moveable adhesion property
Collapse the spiky/sticky/skewer box-variant assets into a single per-
instance `adhesion` property on any moveable block, chosen in the editor
via a dropdown next to the colour picker and stored in the .sav alongside
the class.
- spiky -> sticky: soft grab on every true edge (goopy shader)
- skewer -> gluey: rigid weld on every true edge (spiked outline)
- old trailing-face sticky is removed; its saves fold into new sticky
Move the spike geometry + drawGlueySprite into shaders.lua, fold drawing
and the adhesion flag into GenericMoveable, and delete the three
Generic{Sticky,Spiky,Skewer} classes and box_variants.lua. Room:createEntity
normalises legacy spiky_/skewer_/sticky_ class prefixes so old saves load,
and serialization emits `adhesion`. World/map previews and the movement
proxy follow the rename.
Backfill rooms/*.sav to the new format via tools/backfill_adhesion.lua
(41 objects across 8 files). Update tests to the two-mode model; the two
tests asserting the removed lateral-shear limitation are dropped and a
legacy-load migration test added. 27/27 pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
c348b81a93
commit
7d84f595e4
25 changed files with 4010 additions and 3797 deletions
58
shaders.lua
58
shaders.lua
|
|
@ -183,6 +183,64 @@ function drawGoopySprite(sprite, x, y, scale)
|
|||
love.graphics.setShader(previousShader)
|
||||
end
|
||||
|
||||
-- Gluey blocks draw teeth on every *true* edge of their collision shape: a cell
|
||||
-- edge only gets spikes when no sibling cell sits on the other side of it, so an
|
||||
-- L/notched shape spikes its real contour rather than its bounding box.
|
||||
local glueyEdgeDirections = {
|
||||
{x = 1, y = 0}, {x = -1, y = 0},
|
||||
{x = 0, y = 1}, {x = 0, y = -1},
|
||||
}
|
||||
|
||||
local function shapeHasCell(shape, x, y)
|
||||
for _, cell in ipairs(shape) do
|
||||
if cell.x == x and cell.y == y then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Draw two teeth on every exposed cell edge. The art remains synthetic: the
|
||||
-- underlying box sprite and its collision shape are still the authored base.
|
||||
function drawSpikes(cellShape, x, y, scale)
|
||||
cellShape = cellShape or {{x = 1, y = 1}}
|
||||
local cellSize = 16 * scale
|
||||
local inset = scale
|
||||
local reach = 3 * scale
|
||||
local halfBase = 1.5 * scale
|
||||
|
||||
for _, cell in ipairs(cellShape) do
|
||||
local left = x + (cell.x - 1) * cellSize
|
||||
local top = y + (cell.y - 1) * cellSize
|
||||
for _, direction in ipairs(glueyEdgeDirections) do
|
||||
if not shapeHasCell(cellShape, cell.x + direction.x, cell.y + direction.y) then
|
||||
for _, along in ipairs({5, 11}) do
|
||||
if direction.x ~= 0 then
|
||||
local edgeX = direction.x > 0 and left + cellSize - inset or left + inset
|
||||
local centerY = top + along * scale
|
||||
love.graphics.polygon("fill",
|
||||
edgeX, centerY - halfBase,
|
||||
edgeX + direction.x * reach, centerY,
|
||||
edgeX, centerY + halfBase
|
||||
)
|
||||
else
|
||||
local edgeY = direction.y > 0 and top + cellSize - inset or top + inset
|
||||
local centerX = left + along * scale
|
||||
love.graphics.polygon("fill",
|
||||
centerX - halfBase, edgeY,
|
||||
centerX, edgeY + direction.y * reach,
|
||||
centerX + halfBase, edgeY
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function drawGlueySprite(sprite, cellShape, x, y, scale)
|
||||
drawSpikes(cellShape, x, y, scale)
|
||||
love.graphics.draw(sprite, x, y, 0, scale, scale)
|
||||
end
|
||||
|
||||
-- Accessibility post-process. The puzzle's RGB channels are normally also
|
||||
-- its visual language, so this runs only after the entire scene has been
|
||||
-- composed. It leaves neutral UI/text alone and remaps saturated colours to
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue