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
|
|
@ -1,37 +0,0 @@
|
||||||
local BoxVariants = {}
|
|
||||||
|
|
||||||
-- Add directional sticky-puller, rigid skewer, and soft all-edge spiky variants
|
|
||||||
-- for every authored moveable box. All share sprites/collision with their base.
|
|
||||||
function BoxVariants.addTo(assets)
|
|
||||||
local baseBoxes = {}
|
|
||||||
for name, properties in pairs(assets) do
|
|
||||||
if properties.class == "moveable" and name:match("^box") then
|
|
||||||
table.insert(baseBoxes, {name = name, properties = properties})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function addVariant(base, prefix, behavior, directory, visualFlag)
|
|
||||||
local name = prefix .. base.name
|
|
||||||
local properties = {
|
|
||||||
class = behavior,
|
|
||||||
directory = directory,
|
|
||||||
sprites = base.properties.sprites,
|
|
||||||
cells = base.properties.cells,
|
|
||||||
preload = base.properties.preload,
|
|
||||||
synthetic = true,
|
|
||||||
baseAsset = base.name,
|
|
||||||
}
|
|
||||||
properties[visualFlag] = true
|
|
||||||
assets[name] = properties
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, base in ipairs(baseBoxes) do
|
|
||||||
addVariant(base, "sticky_", "sticky", "sticky", "goopy")
|
|
||||||
addVariant(base, "skewer_", "skewer", "skewer", "skewer")
|
|
||||||
addVariant(base, "spiky_", "spiky", "spiky", "spiky")
|
|
||||||
end
|
|
||||||
|
|
||||||
return assets
|
|
||||||
end
|
|
||||||
|
|
||||||
return BoxVariants
|
|
||||||
247
docs/adhesion-refactor-plan.md
Normal file
247
docs/adhesion-refactor-plan.md
Normal file
|
|
@ -0,0 +1,247 @@
|
||||||
|
# Adhesion refactor — plan
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Today "spiky", "sticky", "skewer" are **separate box assets** (distinct `class`
|
||||||
|
names, generated by `box_variants.lua`). Replace that with a single per-instance
|
||||||
|
**adhesion property** on any *moveable* entity, chosen at placement time via a
|
||||||
|
dropdown in the level editor.
|
||||||
|
|
||||||
|
### Renames / semantics (final)
|
||||||
|
|
||||||
|
- **old `spiky` → new `sticky`** — soft grab on **every true edge**. When the block
|
||||||
|
moves, it *tries* to drag every orthogonally-adjacent block along; if a dragged
|
||||||
|
block is blocked, that branch just shears off (the move still succeeds). Renders
|
||||||
|
with the goopy shader.
|
||||||
|
- **old `skewer` → new `gluey`** — rigid weld on **every true edge**. Welded
|
||||||
|
neighbors move as one component; if any can't move, the whole move fails.
|
||||||
|
Renders with the spike/teeth outline.
|
||||||
|
- **old `sticky` (trailing-face-only grab) → DELETED.** Its distinct mechanic is
|
||||||
|
gone. Legacy `sticky_*` assets in saves are absorbed into new `sticky`.
|
||||||
|
- **regular** — plain moveable, no adhesion.
|
||||||
|
|
||||||
|
Adhesion is stored as `adhesion = "sticky" | "gluey"` (nil/absent = regular) and
|
||||||
|
is **only meaningful on moveable entities**.
|
||||||
|
|
||||||
|
### Contour requirement (already satisfied — no work)
|
||||||
|
|
||||||
|
Edges must follow the *true* profile of multi-cell shapes with holes.
|
||||||
|
- Spikes: `drawSpikes` (currently in `generic_skewer.lua`) already walks
|
||||||
|
`cellShape` and only draws teeth on an edge with no neighbor cell. Keep as-is.
|
||||||
|
- Goopy: `drawGoopySprite`/`goopShader` (`shaders.lua`) sample the sprite's own
|
||||||
|
alpha, so transparent cells stay transparent. Nothing to change.
|
||||||
|
|
||||||
|
## Rationale / approach
|
||||||
|
|
||||||
|
The behavior already keys off boolean flags read through `Entity` methods
|
||||||
|
(`isSticky`/`isSkewer`/`isSpiky`), and the three `Generic*` subclasses only differ
|
||||||
|
by which flag they set + which draw helper they call. So collapsing them into one
|
||||||
|
`GenericMoveable` carrying an `adhesion` string is a small, mechanical change. The
|
||||||
|
real surface area is: (a) the movement rules, (b) serialization + backfilling
|
||||||
|
existing `.sav`s, (c) deleting the synthetic box-variant assets, (d) the editor
|
||||||
|
dropdown. Do it in that order so tests stay runnable (`lua tests/test_movement.lua`)
|
||||||
|
throughout.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. `entity.lua`
|
||||||
|
|
||||||
|
- `initialize` (lines 17–19): delete the `self.sticky/skewer/spiky = ...` trio,
|
||||||
|
replace with:
|
||||||
|
```lua
|
||||||
|
-- Per-instance adhesion for moveable blocks: "sticky" (soft grab on every true
|
||||||
|
-- edge) or "gluey" (rigid weld). nil = plain moveable. See docs/adhesion-*.
|
||||||
|
self.adhesion = t.adhesion
|
||||||
|
```
|
||||||
|
- Methods (lines 140–150): replace the three predicates with:
|
||||||
|
```lua
|
||||||
|
function Entity:isSticky() return self.adhesion == "sticky" end
|
||||||
|
function Entity:isGluey() return self.adhesion == "gluey" end
|
||||||
|
```
|
||||||
|
Remove `isSkewer` and `isSpiky`.
|
||||||
|
|
||||||
|
## 2. `movement.lua`
|
||||||
|
|
||||||
|
Semantics: `sticky` = soft all-edge pull; `gluey` = rigid all-edge weld. The old
|
||||||
|
"trailing face only" behavior is deleted.
|
||||||
|
|
||||||
|
- Local helpers (13–27): keep `isSticky`; rename `isSkewer`→`isGluey`
|
||||||
|
(`entity.isGluey and entity:isGluey()`); delete `isSpiky`.
|
||||||
|
- `findRigidGroup` (line 72): `isSkewer(entity) or isSkewer(adjacentEntity)` →
|
||||||
|
`isGluey(entity) or isGluey(adjacentEntity)`. Update the comment (61–62) to say
|
||||||
|
"gluey welds every edge".
|
||||||
|
- Pull loop (102–118): remove the `trailing` computation and the old sticky
|
||||||
|
branch. `softContact` becomes just:
|
||||||
|
```lua
|
||||||
|
local softContact = adjacentEntity and
|
||||||
|
(isSticky(entity) or isSticky(adjacentEntity))
|
||||||
|
```
|
||||||
|
Update comments (2–3, 102–104) to describe sticky = soft on every edge, gluey =
|
||||||
|
rigid.
|
||||||
|
|
||||||
|
## 3. Draw helpers → `shaders.lua`
|
||||||
|
|
||||||
|
`drawGoopySprite` already lives here. Move the spike geometry here too so the
|
||||||
|
`Generic*` box files can be deleted:
|
||||||
|
- Move `edgeDirections`, `shapeHasCell`, `drawSpikes` from `generic_skewer.lua`
|
||||||
|
into `shaders.lua` (verbatim; they're pure geometry).
|
||||||
|
- Add `drawGlueySprite(sprite, cellShape, x, y, scale)` = old `drawSkewerSprite`
|
||||||
|
(calls `drawSpikes` then draws the sprite).
|
||||||
|
- Drop `drawSpikySprite` entirely (old spiky's draw just fell back to goopy; new
|
||||||
|
sticky uses `drawGoopySprite` directly).
|
||||||
|
|
||||||
|
## 4. `generic_moveable.lua` (the one surviving box class)
|
||||||
|
|
||||||
|
- `initialize(x, y, color, sprite, name, cellShape, adhesion)` — pass
|
||||||
|
`adhesion = adhesion` into `Entity.initialize`.
|
||||||
|
- Add a `draw(channel)` that branches:
|
||||||
|
```lua
|
||||||
|
function GenericMoveable:draw(channel)
|
||||||
|
local sprite = self.sprites[channel]; if not sprite then return end
|
||||||
|
love.graphics.setColor(gColor[channel]:set())
|
||||||
|
local p = self:getDrawPos()
|
||||||
|
local x = p.x + self.drawOffset.x * drawScale
|
||||||
|
local y = p.y + self.drawOffset.y * drawScale
|
||||||
|
if self.adhesion == "gluey" then
|
||||||
|
drawGlueySprite(sprite, self:getCellShape(), x, y, drawScale)
|
||||||
|
elseif self.adhesion == "sticky" and drawGoopySprite then
|
||||||
|
drawGoopySprite(sprite, x, y, drawScale)
|
||||||
|
else
|
||||||
|
love.graphics.draw(sprite, x, y, 0, drawScale, drawScale)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. Delete files + requires
|
||||||
|
|
||||||
|
- Delete `generic_sticky.lua`, `generic_spiky.lua`, `generic_skewer.lua`.
|
||||||
|
- `room.lua` (15–17): remove the three `require "generic_*"` lines (keep
|
||||||
|
`generic_moveable`).
|
||||||
|
- Delete `box_variants.lua`.
|
||||||
|
- `level_editor/object_attributes.lua`: remove line 3 `require "box_variants"`;
|
||||||
|
change line 80 `return BoxVariants.addTo(globalAssetProperties)` →
|
||||||
|
`return globalAssetProperties`.
|
||||||
|
|
||||||
|
## 6. `room.lua` `createEntity` (752–849)
|
||||||
|
|
||||||
|
Boxes are now always `behavior == "moveable"`; adhesion comes from `data.adhesion`.
|
||||||
|
|
||||||
|
- After the note/mark/npc early-returns and **before** `local props = ...`,
|
||||||
|
normalize legacy variant class names so old saves load:
|
||||||
|
```lua
|
||||||
|
-- Legacy: adhesion used to be baked into separate asset classes
|
||||||
|
-- (spiky_/skewer_/sticky_<box>). Collapse to the base asset + an adhesion string.
|
||||||
|
local adhesion = data.adhesion
|
||||||
|
local legacy = { spiky = "sticky", skewer = "gluey", sticky = "sticky" }
|
||||||
|
for prefix, mapped in pairs(legacy) do
|
||||||
|
local base = class:match("^" .. prefix .. "_(.+)")
|
||||||
|
if base then class = base; adhesion = adhesion or mapped; break end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
- In the sprite branch (831–841): keep only
|
||||||
|
```lua
|
||||||
|
elseif behavior == "moveable" then
|
||||||
|
entity = GenericMoveable:new(x, y, color, sprite, class, cellShape, adhesion)
|
||||||
|
```
|
||||||
|
Delete the `"sticky"`/`"skewer"`/`"spiky"` elseif branches. (adhesion is only
|
||||||
|
passed here, so it can never attach to a non-moveable.)
|
||||||
|
- `entity.saveClass = class` at 847 now stores the base box name — good.
|
||||||
|
|
||||||
|
## 7. Serialization
|
||||||
|
|
||||||
|
- `serialize` `emit` (1180–1185): add
|
||||||
|
```lua
|
||||||
|
if entity.adhesion then object.adhesion = entity.adhesion end
|
||||||
|
```
|
||||||
|
- `deserialize` (1114) already forwards the whole `obj` as `data`, so
|
||||||
|
`data.adhesion` is read automatically. No change.
|
||||||
|
|
||||||
|
## 8. Backfill existing `.sav`s (one-off script)
|
||||||
|
|
||||||
|
`rooms/*.sav` reference old class names (`spiky_box`, `skewer_box`, `sticky_box`,
|
||||||
|
plus shape suffixes like `spiky_box_2x1_11`). `TSerial` is pure Lua and runs under
|
||||||
|
plain `lua`. Write `tools/backfill_adhesion.lua`:
|
||||||
|
|
||||||
|
- For every file in `rooms/`, `TSerial.unpack` it, walk `saveTable.objects`, and for
|
||||||
|
each object whose `class` matches `^spiky_(.+)`/`^skewer_(.+)/^sticky_(.+)`:
|
||||||
|
set `object.class = base`, `object.adhesion = {spiky="sticky", skewer="gluey",
|
||||||
|
sticky="sticky"}[prefix]`.
|
||||||
|
- Re-emit with `TSerial.pack(saveTable, nil, true)` and overwrite the file.
|
||||||
|
- Affected files (from grep): `village_1/2/3.sav`, `world.sav`,
|
||||||
|
`spiky_test.sav`, `spiky_test_2.sav`, `spiky_test_3.sav`, `testing_sticky.sav`,
|
||||||
|
`testing_sticky_3.sav`, `testing_sticky_4.sav` (process all, harmless on others).
|
||||||
|
- Run `lua tools/backfill_adhesion.lua`; commit the rewritten saves. (Note: the
|
||||||
|
`.sav` filenames like `spiky_test` are just level names — leave them, or rename
|
||||||
|
separately; only the object `class` fields matter.)
|
||||||
|
|
||||||
|
## 9. Editor — adhesion dropdown next to the color picker
|
||||||
|
|
||||||
|
`level_editor/editor.lua`. Add module-level state near the other locals (~62):
|
||||||
|
```lua
|
||||||
|
local selectedAdhesion = "regular" -- "regular" | "sticky" | "gluey"
|
||||||
|
local adhesionMenuOpen = false
|
||||||
|
local adhesionRect = nil -- set each frame by layoutRoomControls
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Palette sections** `rebuildBoxPaletteSections` (134–148): drop the
|
||||||
|
`spiky/sticky/skewer` prefixes; keep only `{ "box" }` (+ the `barrier` section).
|
||||||
|
The single box section now lists just the base box shapes.
|
||||||
|
|
||||||
|
- **Layout** `layoutRoomControls` (284–338): the color row is
|
||||||
|
`placeRow({ "white","red","green","blue" }, 22)`. Put the dropdown to the right
|
||||||
|
of it, on the same 22px row (vertical budget is tight: `controlsH = 130`). Easiest:
|
||||||
|
before `placeRow` for colors, compute the color row's y, lay the 4 swatches, then
|
||||||
|
set `adhesionRect = { x = <right of swatches + gap>, y = rowY, w = 84, h = 22 }`.
|
||||||
|
If width is tight, bump `controlsH` to ~150 in `roomSections` (261) and add a
|
||||||
|
dedicated row instead — either is fine; "next to the color picker" is the intent.
|
||||||
|
|
||||||
|
- **Draw** (in `editorDraw`, room branch, after the control cluster ~1834): draw
|
||||||
|
`adhesionRect` as a labeled box showing `selectedAdhesion:upper()`. When
|
||||||
|
`adhesionMenuOpen`, draw the 3 options stacked **upward** from `adhesionRect`
|
||||||
|
(it's near the bottom edge): `regular`, `sticky`, `gluey`. Highlight the current.
|
||||||
|
|
||||||
|
- **Click** (in `editorMouseHandler`, room branch — add before the control-cluster
|
||||||
|
loop ~1520): if `adhesionMenuOpen` and click hits an option row → set
|
||||||
|
`selectedAdhesion`, close, return. If click hits `adhesionRect` → toggle
|
||||||
|
`adhesionMenuOpen`, return. Any other click closes the menu.
|
||||||
|
|
||||||
|
- **Placement threading**: `paintRoomCell` (88–94) calls
|
||||||
|
`attemptPlaceAtCell(selectedAsset, gameAssets[selectedAsset], cell, selectedColor)`.
|
||||||
|
Pass adhesion: add a 6th arg `selectedAdhesion ~= "regular" and selectedAdhesion or nil`.
|
||||||
|
- `room.lua Room:attemptPlaceAtCell(name, props, cell, color, text, adhesion)`
|
||||||
|
(1041): forward it into the per-channel `createEntity`:
|
||||||
|
`self:createEntity(name, c, cell.x, cell.y, { adhesion = adhesion })` (1087).
|
||||||
|
(adhesion is ignored by non-moveable classes since only the moveable branch
|
||||||
|
reads it.)
|
||||||
|
|
||||||
|
- **Cleanup of dead flag plumbing** (optional but tidy): `props.goopy/skewer/spiky`
|
||||||
|
no longer exist. Remove those branches in `drawPaletteAsset` (216–224) — draw the
|
||||||
|
sprite plainly (the palette shows base boxes; adhesion is a placement mode, not a
|
||||||
|
per-asset style). Remove `goopy/skewer/spiky/cells` args passed to
|
||||||
|
`createUIElement` at the asset-button/quick-button/search sites (403–414,
|
||||||
|
744–749, 820–836, 1147–1156) and the matching fields + draw branches in
|
||||||
|
`level_editor/gui.lua` (12–14, 103–116). Harmless if left (nil → falsy), so this
|
||||||
|
can be deferred.
|
||||||
|
|
||||||
|
## 10. Tests (`tests/test_movement.lua`)
|
||||||
|
|
||||||
|
- Helper `movable` (32–57): change signature to `movable(x, y, adhesion, shape)`;
|
||||||
|
store `self.adhesion`; replace `isSticky/isSkewer/isSpiky` with
|
||||||
|
`isSticky() -> adhesion=="sticky"`, `isGluey() -> adhesion=="gluey"`.
|
||||||
|
Update the existing callers that passed positional `sticky/skewer/spiky`.
|
||||||
|
- Rename the "spiky" test (536–552): call `movable(4, 4, "sticky")` for the grabby
|
||||||
|
block; assertions unchanged (soft shear-off behavior is identical).
|
||||||
|
- If there are `skewer`-based tests elsewhere, pass `"gluey"`.
|
||||||
|
- Delete the "every moveable base box gets ... variants" test (554–604) — that
|
||||||
|
feature (`box_variants.lua`) is gone. Update the final `print` (606) to drop
|
||||||
|
"block-variant".
|
||||||
|
- `lua tests/test_movement.lua` must pass.
|
||||||
|
|
||||||
|
## Sanity checklist
|
||||||
|
|
||||||
|
- [ ] `grep -rn "spiky\|skewer\|isSpiky\|isSkewer\|box_variants\|BoxVariants"` returns
|
||||||
|
nothing in `.lua` (except maybe level-name strings / this doc).
|
||||||
|
- [ ] `grep -rn "adhesion"` shows entity/movement/room/editor wired consistently.
|
||||||
|
- [ ] Old saves load (backfilled) and re-save with `adhesion` on the right objects.
|
||||||
|
- [ ] Placing with the dropdown on `sticky`/`gluey` produces the goopy/spiked look
|
||||||
|
and the correct push/pull behavior; on a non-moveable asset it's ignored.
|
||||||
17
entity.lua
17
entity.lua
|
|
@ -14,9 +14,10 @@ function Entity:initialize(t)
|
||||||
self.name = t.name
|
self.name = t.name
|
||||||
self.color = Color:new(t.color)
|
self.color = Color:new(t.color)
|
||||||
self.collision = t.collision --"none", "moveable", "unmoveable"
|
self.collision = t.collision --"none", "moveable", "unmoveable"
|
||||||
self.sticky = t.sticky or false
|
-- Per-instance adhesion for moveable blocks: "sticky" (soft grab on every true
|
||||||
self.skewer = t.skewer or false
|
-- edge) or "gluey" (rigid weld on every true edge). nil = plain moveable.
|
||||||
self.spiky = t.spiky or false
|
-- See docs/adhesion-refactor-plan.md.
|
||||||
|
self.adhesion = t.adhesion
|
||||||
self.sprite = repSprite
|
self.sprite = repSprite
|
||||||
self.shaAmount = t.sha or 1
|
self.shaAmount = t.sha or 1
|
||||||
-- onBump is a method (see Entity:onBump) so mixins like Dialogable can
|
-- onBump is a method (see Entity:onBump) so mixins like Dialogable can
|
||||||
|
|
@ -138,15 +139,11 @@ function Entity:canMove()
|
||||||
end
|
end
|
||||||
|
|
||||||
function Entity:isSticky()
|
function Entity:isSticky()
|
||||||
return self.sticky
|
return self.adhesion == "sticky"
|
||||||
end
|
end
|
||||||
|
|
||||||
function Entity:isSkewer()
|
function Entity:isGluey()
|
||||||
return self.skewer
|
return self.adhesion == "gluey"
|
||||||
end
|
|
||||||
|
|
||||||
function Entity:isSpiky()
|
|
||||||
return self.spiky
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Entity:canPassOver()
|
function Entity:canPassOver()
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ require "entity"
|
||||||
|
|
||||||
GenericMoveable = class("GenericMoveable", Entity)
|
GenericMoveable = class("GenericMoveable", Entity)
|
||||||
|
|
||||||
function GenericMoveable:initialize(x, y, color, sprite, name, cellShape)
|
function GenericMoveable:initialize(x, y, color, sprite, name, cellShape, adhesion)
|
||||||
Entity.initialize(self, {
|
Entity.initialize(self, {
|
||||||
name = name,
|
name = name,
|
||||||
x = x,
|
x = x,
|
||||||
|
|
@ -10,7 +10,8 @@ function GenericMoveable:initialize(x, y, color, sprite, name, cellShape)
|
||||||
color = color,
|
color = color,
|
||||||
collision = "moveable",
|
collision = "moveable",
|
||||||
sprite = sprite,
|
sprite = sprite,
|
||||||
cellShape = cellShape
|
cellShape = cellShape,
|
||||||
|
adhesion = adhesion
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -18,6 +19,25 @@ function GenericMoveable:update()
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Adhesion drives the look as well as the mechanic: gluey blocks get spiked
|
||||||
|
-- teeth on their true contour, sticky blocks get the wobbling goopy shader, and
|
||||||
|
-- plain moveables draw normally.
|
||||||
|
function GenericMoveable:draw(channel)
|
||||||
|
local sprite = self.sprites[channel]
|
||||||
|
if not sprite then return end
|
||||||
|
love.graphics.setColor(gColor[channel]:set())
|
||||||
|
local p = self:getDrawPos()
|
||||||
|
local x = p.x + self.drawOffset.x * drawScale
|
||||||
|
local y = p.y + self.drawOffset.y * drawScale
|
||||||
|
if self.adhesion == "gluey" and drawGlueySprite then
|
||||||
|
drawGlueySprite(sprite, self:getCellShape(), x, y, drawScale)
|
||||||
|
elseif self.adhesion == "sticky" and drawGoopySprite then
|
||||||
|
drawGoopySprite(sprite, x, y, drawScale)
|
||||||
|
else
|
||||||
|
love.graphics.draw(sprite, x, y, 0, drawScale, drawScale)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function GenericMoveable:tick()
|
function GenericMoveable:tick()
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
require "entity"
|
|
||||||
|
|
||||||
GenericSkewer = class("GenericSkewer", Entity)
|
|
||||||
|
|
||||||
local edgeDirections = {
|
|
||||||
{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(edgeDirections) 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 drawSkewerSprite(sprite, cellShape, x, y, scale)
|
|
||||||
drawSpikes(cellShape, x, y, scale)
|
|
||||||
love.graphics.draw(sprite, x, y, 0, scale, scale)
|
|
||||||
end
|
|
||||||
|
|
||||||
function GenericSkewer:initialize(x, y, color, sprite, name, cellShape)
|
|
||||||
Entity.initialize(self, {
|
|
||||||
name = name,
|
|
||||||
x = x,
|
|
||||||
y = y,
|
|
||||||
color = color,
|
|
||||||
collision = "moveable",
|
|
||||||
skewer = true,
|
|
||||||
sprite = sprite,
|
|
||||||
cellShape = cellShape
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
function GenericSkewer:update()
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
function GenericSkewer:draw(channel)
|
|
||||||
local sprite = self.sprites[channel]
|
|
||||||
if not sprite then return end
|
|
||||||
love.graphics.setColor(gColor[channel]:set())
|
|
||||||
local p = self:getDrawPos()
|
|
||||||
drawSkewerSprite(
|
|
||||||
sprite, self:getCellShape(),
|
|
||||||
p.x + self.drawOffset.x * drawScale,
|
|
||||||
p.y + self.drawOffset.y * drawScale,
|
|
||||||
drawScale
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
function GenericSkewer:tick()
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
require "entity"
|
|
||||||
require "generic_skewer"
|
|
||||||
|
|
||||||
GenericSpiky = class("GenericSpiky", Entity)
|
|
||||||
|
|
||||||
function drawSpikySprite(sprite, _, x, y, scale)
|
|
||||||
if drawGoopySprite then
|
|
||||||
drawGoopySprite(sprite, x, y, scale)
|
|
||||||
else
|
|
||||||
love.graphics.draw(sprite, x, y, 0, scale, scale)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function GenericSpiky:initialize(x, y, color, sprite, name, cellShape)
|
|
||||||
Entity.initialize(self, {
|
|
||||||
name = name,
|
|
||||||
x = x,
|
|
||||||
y = y,
|
|
||||||
color = color,
|
|
||||||
collision = "moveable",
|
|
||||||
spiky = true,
|
|
||||||
sprite = sprite,
|
|
||||||
cellShape = cellShape
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
function GenericSpiky:update()
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
function GenericSpiky:draw(channel)
|
|
||||||
local sprite = self.sprites[channel]
|
|
||||||
if not sprite then return end
|
|
||||||
love.graphics.setColor(gColor[channel]:set())
|
|
||||||
local p = self:getDrawPos()
|
|
||||||
drawSpikySprite(
|
|
||||||
sprite, self:getCellShape(),
|
|
||||||
p.x + self.drawOffset.x * drawScale,
|
|
||||||
p.y + self.drawOffset.y * drawScale,
|
|
||||||
drawScale
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
function GenericSpiky:tick()
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
require "entity"
|
|
||||||
|
|
||||||
GenericSticky = class("GenericSticky", Entity)
|
|
||||||
|
|
||||||
function GenericSticky:initialize(x, y, color, sprite, name, cellShape)
|
|
||||||
Entity.initialize(self, {
|
|
||||||
name = name,
|
|
||||||
x = x,
|
|
||||||
y = y,
|
|
||||||
color = color,
|
|
||||||
collision = "moveable",
|
|
||||||
sticky = true,
|
|
||||||
sprite = sprite,
|
|
||||||
cellShape = cellShape
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
function GenericSticky:update()
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
function GenericSticky:draw(channel)
|
|
||||||
local sprite = self.sprites[channel]
|
|
||||||
if not sprite then return end
|
|
||||||
if not drawGoopySprite then
|
|
||||||
Entity.draw(self, channel)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
love.graphics.setColor(gColor[channel]:set())
|
|
||||||
local p = self:getDrawPos()
|
|
||||||
drawGoopySprite(
|
|
||||||
sprite,
|
|
||||||
p.x + self.drawOffset.x * drawScale,
|
|
||||||
p.y + self.drawOffset.y * drawScale,
|
|
||||||
drawScale
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
function GenericSticky:tick()
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
@ -63,6 +63,15 @@ local boxPaletteOpen = false
|
||||||
local boxPaletteScroll = { scroll = 0 }
|
local boxPaletteScroll = { scroll = 0 }
|
||||||
local boxPaletteSections = {}
|
local boxPaletteSections = {}
|
||||||
|
|
||||||
|
-- Adhesion applied to any moveable block placed from the room view. "regular"
|
||||||
|
-- means no adhesion; the others attach as a per-instance property (see
|
||||||
|
-- Room:createEntity). `adhesionRect` is recomputed each frame in
|
||||||
|
-- layoutRoomControls so draw and hit-testing agree; the menu expands upward.
|
||||||
|
local adhesionOptions = { "regular", "sticky", "gluey" }
|
||||||
|
local selectedAdhesion = "regular"
|
||||||
|
local adhesionMenuOpen = false
|
||||||
|
local adhesionRect = nil
|
||||||
|
|
||||||
-- Note authoring state. `noteMode` is nil normally, "create" while placing a new
|
-- Note authoring state. `noteMode` is nil normally, "create" while placing a new
|
||||||
-- note (click a cell to drop it and finish), or "edit" while retyping an
|
-- note (click a cell to drop it and finish), or "edit" while retyping an
|
||||||
-- existing note (with Save / Discard). `editingNote` is the note being edited.
|
-- existing note (with Save / Discard). `editingNote` is the note being edited.
|
||||||
|
|
@ -87,7 +96,10 @@ end
|
||||||
|
|
||||||
local function paintRoomCell(cell, button)
|
local function paintRoomCell(cell, button)
|
||||||
if button == 1 and selectedAsset then
|
if button == 1 and selectedAsset then
|
||||||
currentRoom:attemptPlaceAtCell(selectedAsset, gameAssets[selectedAsset], cell, selectedColor)
|
-- adhesion is a no-op on non-moveable assets (Room:createEntity only reads
|
||||||
|
-- it in the moveable branch), so it is always safe to pass along.
|
||||||
|
local adhesion = selectedAdhesion ~= "regular" and selectedAdhesion or nil
|
||||||
|
currentRoom:attemptPlaceAtCell(selectedAsset, gameAssets[selectedAsset], cell, selectedColor, nil, adhesion)
|
||||||
elseif button == 2 then
|
elseif button == 2 then
|
||||||
currentRoom:attemptDeleteAtCell(cell, selectedColor)
|
currentRoom:attemptDeleteAtCell(cell, selectedColor)
|
||||||
end
|
end
|
||||||
|
|
@ -132,16 +144,16 @@ local function currentRoomList()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function rebuildBoxPaletteSections()
|
local function rebuildBoxPaletteSections()
|
||||||
|
-- Adhesion (sticky/gluey) is now a placement modifier chosen with the dropdown
|
||||||
|
-- next to the colour picker, not a separate asset, so the palette lists only
|
||||||
|
-- the base box shapes (plus the barrier).
|
||||||
boxPaletteSections = {}
|
boxPaletteSections = {}
|
||||||
for _, prefix in ipairs({ "box", "spiky", "sticky", "skewer" }) do
|
|
||||||
local names = {}
|
local names = {}
|
||||||
local pattern = prefix == "box" and "^box" or "^" .. prefix .. "_box"
|
|
||||||
for name in pairs(gameAssets) do
|
for name in pairs(gameAssets) do
|
||||||
if name:match(pattern) then table.insert(names, name) end
|
if name:match("^box") then table.insert(names, name) end
|
||||||
end
|
end
|
||||||
table.sort(names)
|
table.sort(names)
|
||||||
table.insert(boxPaletteSections, { prefix = prefix, names = names })
|
table.insert(boxPaletteSections, { prefix = "box", names = names })
|
||||||
end
|
|
||||||
if gameAssets.player_blocker then
|
if gameAssets.player_blocker then
|
||||||
table.insert(boxPaletteSections, { prefix = "barrier", names = {"player_blocker"} })
|
table.insert(boxPaletteSections, { prefix = "barrier", names = {"player_blocker"} })
|
||||||
end
|
end
|
||||||
|
|
@ -213,17 +225,9 @@ local function drawPaletteAsset(name, x, y, size)
|
||||||
for color, path in pairs(props.sprites) do
|
for color, path in pairs(props.sprites) do
|
||||||
local sprite = loadSprite(path)
|
local sprite = loadSprite(path)
|
||||||
love.graphics.setColor(gColor[color]:set())
|
love.graphics.setColor(gColor[color]:set())
|
||||||
if props.goopy and drawGoopySprite then
|
|
||||||
drawGoopySprite(sprite, drawX, drawY, scale)
|
|
||||||
elseif props.skewer and drawSkewerSprite then
|
|
||||||
drawSkewerSprite(sprite, props.cells and props.cells[color], drawX, drawY, scale)
|
|
||||||
elseif props.spiky and drawSpikySprite then
|
|
||||||
drawSpikySprite(sprite, props.cells and props.cells[color], drawX, drawY, scale)
|
|
||||||
else
|
|
||||||
love.graphics.draw(sprite, drawX, drawY, 0, scale, scale)
|
love.graphics.draw(sprite, drawX, drawY, 0, scale, scale)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
local function drawBoxPicker(picker)
|
local function drawBoxPicker(picker)
|
||||||
Layout.clip(picker.rect)
|
Layout.clip(picker.rect)
|
||||||
|
|
@ -332,11 +336,89 @@ local function layoutRoomControls(c, s)
|
||||||
y = y + target + vgap
|
y = y + target + vgap
|
||||||
end
|
end
|
||||||
|
|
||||||
placeRow({ "white", "red", "green", "blue" }, 22)
|
-- Colour swatches with the adhesion dropdown packed to their right, centered
|
||||||
|
-- as one group. adhesionRect feeds both the dropdown draw and its hit-test.
|
||||||
|
do
|
||||||
|
local target = 22
|
||||||
|
local dropdownW = 78
|
||||||
|
local btns, total = {}, 0
|
||||||
|
for _, name in ipairs({ "white", "red", "green", "blue" }) do
|
||||||
|
local b = sizeBtn(name, target)
|
||||||
|
if b then table.insert(btns, b); total = total + b.w end
|
||||||
|
end
|
||||||
|
total = total + hgap * #btns + dropdownW
|
||||||
|
local x = c.x + (c.w - total) / 2
|
||||||
|
for _, b in ipairs(btns) do
|
||||||
|
b.x = x
|
||||||
|
b.y = y + (target - b.h) / 2
|
||||||
|
x = x + b.w + hgap
|
||||||
|
end
|
||||||
|
adhesionRect = { x = x, y = y, w = dropdownW, h = target }
|
||||||
|
y = y + target + vgap
|
||||||
|
end
|
||||||
placeRow({ "quick_player", "quick_static_wall", "quick_switch", "quick_door", "quick_wired_switch", "quick_wired_door", "quick_box" }, 22)
|
placeRow({ "quick_player", "quick_static_wall", "quick_switch", "quick_door", "quick_wired_switch", "quick_wired_door", "quick_box" }, 22)
|
||||||
placeRow({ "save", "clearSaves", "world", "wire", "note", "mark" }, 28)
|
placeRow({ "save", "clearSaves", "world", "wire", "note", "mark" }, 28)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Stacked option rows for the open adhesion menu, expanding upward from the
|
||||||
|
-- closed control so it never spills past the bottom edge. Shared by draw + click.
|
||||||
|
local function adhesionMenuRects()
|
||||||
|
if not adhesionRect then return {} end
|
||||||
|
local rects = {}
|
||||||
|
for index, name in ipairs(adhesionOptions) do
|
||||||
|
-- topmost option is the last one drawn highest above the closed control
|
||||||
|
local offset = (#adhesionOptions - index + 1) * adhesionRect.h
|
||||||
|
rects[index] = {
|
||||||
|
name = name,
|
||||||
|
x = adhesionRect.x,
|
||||||
|
y = adhesionRect.y - offset,
|
||||||
|
w = adhesionRect.w,
|
||||||
|
h = adhesionRect.h,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
return rects
|
||||||
|
end
|
||||||
|
|
||||||
|
local function drawAdhesionDropdown()
|
||||||
|
if not adhesionRect then return end
|
||||||
|
local r = adhesionRect
|
||||||
|
love.graphics.setColor(gColor.white:set())
|
||||||
|
love.graphics.setLineWidth(2)
|
||||||
|
love.graphics.rectangle("line", r.x, r.y, r.w, r.h)
|
||||||
|
local caret = adhesionMenuOpen and " v" or " ^"
|
||||||
|
love.graphics.printf(selectedAdhesion:upper() .. caret, r.x, r.y + (r.h - font:getHeight()) / 2, r.w, "center")
|
||||||
|
if adhesionMenuOpen then
|
||||||
|
for _, option in ipairs(adhesionMenuRects()) do
|
||||||
|
love.graphics.setColor(0, 0, 0, 0.9)
|
||||||
|
love.graphics.rectangle("fill", option.x, option.y, option.w, option.h)
|
||||||
|
love.graphics.setColor(option.name == selectedAdhesion and 1 or 0.6, 0.85, option.name == selectedAdhesion and 0.2 or 0.6, 1)
|
||||||
|
love.graphics.rectangle("line", option.x, option.y, option.w, option.h)
|
||||||
|
love.graphics.setColor(gColor.white:set())
|
||||||
|
love.graphics.printf(option.name:upper(), option.x, option.y + (option.h - font:getHeight()) / 2, option.w, "center")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns true when the click was consumed by the dropdown (toggle or a choice).
|
||||||
|
local function adhesionDropdownClick(x, y)
|
||||||
|
if not adhesionRect then return false end
|
||||||
|
if adhesionMenuOpen then
|
||||||
|
for _, option in ipairs(adhesionMenuRects()) do
|
||||||
|
if Layout.hit(option, x, y) then
|
||||||
|
selectedAdhesion = option.name
|
||||||
|
adhesionMenuOpen = false
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if Layout.hit(adhesionRect, x, y) then
|
||||||
|
adhesionMenuOpen = not adhesionMenuOpen
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
if adhesionMenuOpen then adhesionMenuOpen = false end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
-- Build the full room-view layout (sections + a scroll list + a back/search
|
-- Build the full room-view layout (sections + a scroll list + a back/search
|
||||||
-- header) without drawing. Shared by editorDraw and editorMouseHandler.
|
-- header) without drawing. Shared by editorDraw and editorMouseHandler.
|
||||||
local function buildRoomLayout()
|
local function buildRoomLayout()
|
||||||
|
|
@ -402,10 +484,6 @@ local function rebuildAssetSearchButtons()
|
||||||
for color, location in pairs(gameAssets[asset].sprites) do sprites[color] = love.graphics.newImage(location) end
|
for color, location in pairs(gameAssets[asset].sprites) do sprites[color] = love.graphics.newImage(location) end
|
||||||
table.insert(assetSearchButtons, createUIElement{
|
table.insert(assetSearchButtons, createUIElement{
|
||||||
sprites = sprites,
|
sprites = sprites,
|
||||||
goopy = gameAssets[asset].goopy,
|
|
||||||
skewer = gameAssets[asset].skewer,
|
|
||||||
spiky = gameAssets[asset].spiky,
|
|
||||||
cells = gameAssets[asset].cells,
|
|
||||||
name = "asset",
|
name = "asset",
|
||||||
text = asset,
|
text = asset,
|
||||||
x = 30,
|
x = 30,
|
||||||
|
|
@ -739,10 +817,6 @@ function _initLevelEditor()
|
||||||
_addUIButton(createUIElement{
|
_addUIButton(createUIElement{
|
||||||
name = "quick_" .. asset,
|
name = "quick_" .. asset,
|
||||||
sprites = sprites,
|
sprites = sprites,
|
||||||
goopy = properties.goopy,
|
|
||||||
skewer = properties.skewer,
|
|
||||||
spiky = properties.spiky,
|
|
||||||
cells = properties.cells,
|
|
||||||
onClic = { left = function() selectQuickAsset(asset) end },
|
onClic = { left = function() selectQuickAsset(asset) end },
|
||||||
x = 0,
|
x = 0,
|
||||||
y = 0,
|
y = 0,
|
||||||
|
|
@ -819,10 +893,6 @@ function selectFolder(folder)
|
||||||
-- each frame from the list section (see buildRoomLayout).
|
-- each frame from the list section (see buildRoomLayout).
|
||||||
table.insert(assetListButtons, createUIElement{
|
table.insert(assetListButtons, createUIElement{
|
||||||
sprites = sprites,
|
sprites = sprites,
|
||||||
goopy = gameAssets[asset].goopy,
|
|
||||||
skewer = gameAssets[asset].skewer,
|
|
||||||
spiky = gameAssets[asset].spiky,
|
|
||||||
cells = gameAssets[asset].cells,
|
|
||||||
name = "asset",
|
name = "asset",
|
||||||
text = asset,
|
text = asset,
|
||||||
x = 30,
|
x = 30,
|
||||||
|
|
@ -1147,10 +1217,6 @@ function editAsset(asset)
|
||||||
local assetButton = createUIElement{
|
local assetButton = createUIElement{
|
||||||
name = asset,
|
name = asset,
|
||||||
sprites = assetSprites,
|
sprites = assetSprites,
|
||||||
goopy = assetProperties.goopy,
|
|
||||||
skewer = assetProperties.skewer,
|
|
||||||
spiky = assetProperties.spiky,
|
|
||||||
cells = assetProperties.cells,
|
|
||||||
x = sb.w / 2 - 18,
|
x = sb.w / 2 - 18,
|
||||||
y = 80,
|
y = 80,
|
||||||
}
|
}
|
||||||
|
|
@ -1517,6 +1583,10 @@ function editorMouseHandler(x, y, button)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- adhesion dropdown (sits next to the colour picker; its open menu
|
||||||
|
-- overlaps the area above the controls, so test it before the buttons)
|
||||||
|
if button == 1 and adhesionDropdownClick(x, y) then return end
|
||||||
|
|
||||||
-- control cluster (only the buttons that live there)
|
-- control cluster (only the buttons that live there)
|
||||||
for _, name in ipairs({ "roomName", "white", "red", "green", "blue", "quick_player", "quick_static_wall", "quick_switch", "quick_door", "quick_wired_switch", "quick_wired_door", "quick_box", "save", "clearSaves", "world", "wire", "note", "mark" }) do
|
for _, name in ipairs({ "roomName", "white", "red", "green", "blue", "quick_player", "quick_static_wall", "quick_switch", "quick_door", "quick_wired_switch", "quick_wired_door", "quick_box", "save", "clearSaves", "world", "wire", "note", "mark" }) do
|
||||||
local b = uiButtons[name]
|
local b = uiButtons[name]
|
||||||
|
|
@ -1833,6 +1903,9 @@ function editorDraw()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- adhesion dropdown, drawn last so its open menu sits above the controls
|
||||||
|
drawAdhesionDropdown()
|
||||||
|
|
||||||
-- status strip between the list and the controls: Save / Discard while
|
-- status strip between the list and the controls: Save / Discard while
|
||||||
-- editing an existing note, otherwise the ordinary status line.
|
-- editing an existing note, otherwise the ordinary status line.
|
||||||
if noteMode == "edit" then
|
if noteMode == "edit" then
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,6 @@ function GUI:initialize(t)
|
||||||
self.offset = {x = 0, y = 0}
|
self.offset = {x = 0, y = 0}
|
||||||
self.name = t.name
|
self.name = t.name
|
||||||
self.text = t.text
|
self.text = t.text
|
||||||
self.goopy = t.goopy or false
|
|
||||||
self.skewer = t.skewer or false
|
|
||||||
self.spiky = t.spiky or false
|
|
||||||
self.cells = t.cells
|
|
||||||
|
|
||||||
if not t.onClic then
|
if not t.onClic then
|
||||||
t.onClic = {}
|
t.onClic = {}
|
||||||
|
|
@ -103,17 +99,9 @@ function GUI:draw()
|
||||||
if self.sprites then
|
if self.sprites then
|
||||||
for color, sprite in pairs(self.sprites) do
|
for color, sprite in pairs(self.sprites) do
|
||||||
love.graphics.setColor(gColor[color]:set())
|
love.graphics.setColor(gColor[color]:set())
|
||||||
if self.goopy and drawGoopySprite then
|
|
||||||
drawGoopySprite(sprite, box.x, box.y, self.scale)
|
|
||||||
elseif self.skewer and drawSkewerSprite then
|
|
||||||
drawSkewerSprite(sprite, self.cells and self.cells[color], box.x, box.y, self.scale)
|
|
||||||
elseif self.spiky and drawSpikySprite then
|
|
||||||
drawSpikySprite(sprite, self.cells and self.cells[color], box.x, box.y, self.scale)
|
|
||||||
else
|
|
||||||
love.graphics.draw(sprite, box.x, box.y, 0, self.scale, self.scale)
|
love.graphics.draw(sprite, box.x, box.y, 0, self.scale, self.scale)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
if self.text then
|
if self.text then
|
||||||
love.graphics.setColor(gColor.white:set())
|
love.graphics.setColor(gColor.white:set())
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
require "libs/TSerial"
|
require "libs/TSerial"
|
||||||
|
|
||||||
local BoxVariants = require "box_variants"
|
|
||||||
|
|
||||||
local targetFolder = "art"
|
local targetFolder = "art"
|
||||||
local reanalyzeCells = true
|
local reanalyzeCells = true
|
||||||
|
|
||||||
|
|
@ -77,5 +75,5 @@ function getAllAssets()
|
||||||
synthetic("wired_switch", "switch", "wired_switch")
|
synthetic("wired_switch", "switch", "wired_switch")
|
||||||
synthetic("wired_door", "door", "wired_door")
|
synthetic("wired_door", "door", "wired_door")
|
||||||
|
|
||||||
return BoxVariants.addTo(globalAssetProperties)
|
return globalAssetProperties
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -217,12 +217,10 @@ function MapManager:drawRoomLayer(roomData, x, y, size, color, alpha)
|
||||||
love.graphics.setColor(r, g, b, alpha)
|
love.graphics.setColor(r, g, b, alpha)
|
||||||
local sprite = loadSprite(props.sprites[color])
|
local sprite = loadSprite(props.sprites[color])
|
||||||
local dx, dy = x + (object.x - 1) * 16 * scale, y + (object.y - 1) * 16 * scale
|
local dx, dy = x + (object.x - 1) * 16 * scale, y + (object.y - 1) * 16 * scale
|
||||||
if props.goopy and drawGoopySprite then
|
if object.adhesion == "gluey" and drawGlueySprite then
|
||||||
|
drawGlueySprite(sprite, props.cells and props.cells[color], dx, dy, scale)
|
||||||
|
elseif object.adhesion == "sticky" and drawGoopySprite then
|
||||||
drawGoopySprite(sprite, dx, dy, scale)
|
drawGoopySprite(sprite, dx, dy, scale)
|
||||||
elseif props.skewer and drawSkewerSprite then
|
|
||||||
drawSkewerSprite(sprite, props.cells and props.cells[color], dx, dy, scale)
|
|
||||||
elseif props.spiky and drawSpikySprite then
|
|
||||||
drawSpikySprite(sprite, props.cells and props.cells[color], dx, dy, scale)
|
|
||||||
else
|
else
|
||||||
love.graphics.draw(sprite, dx, dy, 0, scale, scale)
|
love.graphics.draw(sprite, dx, dy, 0, scale, scale)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
28
movement.lua
28
movement.lua
|
|
@ -1,6 +1,7 @@
|
||||||
-- Finds the complete set of blocks that must move together for one grid step.
|
-- 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
|
-- A block joins the set when it is in another block's destination (ordinary
|
||||||
-- pushing), is softly grabbed by sticky/spiky, or touches a rigid skewer.
|
-- pushing), is softly grabbed by a sticky block, or is rigidly welded to a
|
||||||
|
-- gluey block.
|
||||||
local Movement = {}
|
local Movement = {}
|
||||||
|
|
||||||
local adjacentDirections = {
|
local adjacentDirections = {
|
||||||
|
|
@ -18,15 +19,11 @@ local function isPassable(entity)
|
||||||
return entity.canPassOver and entity:canPassOver()
|
return entity.canPassOver and entity:canPassOver()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function isSkewer(entity)
|
local function isGluey(entity)
|
||||||
return entity.isSkewer and entity:isSkewer()
|
return entity.isGluey and entity:isGluey()
|
||||||
end
|
end
|
||||||
|
|
||||||
local function isSpiky(entity)
|
-- Build one rigid component: ordinary pushes are required, and gluey blocks weld
|
||||||
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
|
-- along every edge. `alreadyMoving` contains cells that will be vacated during
|
||||||
-- this same step, so a later pulled component may move into them.
|
-- this same step, so a later pulled component may move into them.
|
||||||
local function findRigidGroup(startCells, direction, entityAt, isInBounds, alreadyMoving)
|
local function findRigidGroup(startCells, direction, entityAt, isInBounds, alreadyMoving)
|
||||||
|
|
@ -59,7 +56,7 @@ local function findRigidGroup(startCells, direction, entityAt, isInBounds, alrea
|
||||||
table.insert(pendingCells, projectedCell)
|
table.insert(pendingCells, projectedCell)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Skewers make every edge contact part of this rigid component.
|
-- Gluey blocks make every edge contact part of this rigid component.
|
||||||
for _, occupiedCell in ipairs(entity:getOccupiedCells()) do
|
for _, occupiedCell in ipairs(entity:getOccupiedCells()) do
|
||||||
for _, adjacentDirection in ipairs(adjacentDirections) do
|
for _, adjacentDirection in ipairs(adjacentDirections) do
|
||||||
local adjacentCell = {
|
local adjacentCell = {
|
||||||
|
|
@ -69,7 +66,7 @@ local function findRigidGroup(startCells, direction, entityAt, isInBounds, alrea
|
||||||
if isInBounds(adjacentCell) then
|
if isInBounds(adjacentCell) then
|
||||||
local adjacentEntity = entityAt(adjacentCell)
|
local adjacentEntity = entityAt(adjacentCell)
|
||||||
local attached = adjacentEntity and (
|
local attached = adjacentEntity and (
|
||||||
isSkewer(entity) or isSkewer(adjacentEntity)
|
isGluey(entity) or isGluey(adjacentEntity)
|
||||||
)
|
)
|
||||||
if attached and adjacentEntity ~= entity and adjacentEntity:canMove() and
|
if attached and adjacentEntity ~= entity and adjacentEntity:canMove() and
|
||||||
not (alreadyMoving and alreadyMoving[adjacentEntity]) then
|
not (alreadyMoving and alreadyMoving[adjacentEntity]) then
|
||||||
|
|
@ -99,9 +96,9 @@ function Movement.findMovableGroup(startCells, direction, entityAt, isInBounds)
|
||||||
local entity = movingQueue[nextMover]
|
local entity = movingQueue[nextMover]
|
||||||
nextMover = nextMover + 1
|
nextMover = nextMover + 1
|
||||||
|
|
||||||
-- Sticky contact is soft only on the trailing face. Spiky contact is soft
|
-- Sticky contact is soft on every face: it tries to drag each neighbour
|
||||||
-- on every face. Either kind simply releases a blocked rigid component,
|
-- along, but simply releases a blocked rigid component without cancelling
|
||||||
-- without cancelling this entity or another independently pulled branch.
|
-- this entity or another independently pulled branch.
|
||||||
for _, occupiedCell in ipairs(entity:getOccupiedCells()) do
|
for _, occupiedCell in ipairs(entity:getOccupiedCells()) do
|
||||||
for _, adjacentDirection in ipairs(adjacentDirections) do
|
for _, adjacentDirection in ipairs(adjacentDirections) do
|
||||||
local adjacentCell = {
|
local adjacentCell = {
|
||||||
|
|
@ -110,11 +107,8 @@ function Movement.findMovableGroup(startCells, direction, entityAt, isInBounds)
|
||||||
}
|
}
|
||||||
if isInBounds(adjacentCell) then
|
if isInBounds(adjacentCell) then
|
||||||
local adjacentEntity = entityAt(adjacentCell)
|
local adjacentEntity = entityAt(adjacentCell)
|
||||||
local trailing = adjacentDirection.x == -direction.x and
|
|
||||||
adjacentDirection.y == -direction.y
|
|
||||||
local softContact = adjacentEntity and (
|
local softContact = adjacentEntity and (
|
||||||
isSpiky(entity) or isSpiky(adjacentEntity) or
|
isSticky(entity) or isSticky(adjacentEntity)
|
||||||
(trailing and (isSticky(entity) or isSticky(adjacentEntity)))
|
|
||||||
)
|
)
|
||||||
if softContact and adjacentEntity ~= entity and
|
if softContact and adjacentEntity ~= entity and
|
||||||
not entities[adjacentEntity] and not attemptedPulls[adjacentEntity] and
|
not entities[adjacentEntity] and not attemptedPulls[adjacentEntity] and
|
||||||
|
|
|
||||||
27
room.lua
27
room.lua
|
|
@ -12,9 +12,6 @@ require "wired_switch"
|
||||||
require "wired_door"
|
require "wired_door"
|
||||||
require "dialogue_manager"
|
require "dialogue_manager"
|
||||||
require "generic_moveable"
|
require "generic_moveable"
|
||||||
require "generic_sticky"
|
|
||||||
require "generic_skewer"
|
|
||||||
require "generic_spiky"
|
|
||||||
require "generic_unmoveable"
|
require "generic_unmoveable"
|
||||||
require "generic_player_blocker"
|
require "generic_player_blocker"
|
||||||
require "note"
|
require "note"
|
||||||
|
|
@ -772,6 +769,17 @@ function Room:createEntity(class, color, x, y, data)
|
||||||
return entity
|
return entity
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Legacy: adhesion used to be baked into separate asset classes
|
||||||
|
-- (spiky_/skewer_/sticky_<box>). Collapse them to the base asset carrying an
|
||||||
|
-- adhesion string so old .sav files still load. spiky/old-sticky both become
|
||||||
|
-- the new soft "sticky"; skewer becomes the rigid "gluey".
|
||||||
|
local adhesion = data.adhesion
|
||||||
|
local legacyAdhesion = { spiky = "sticky", skewer = "gluey", sticky = "sticky" }
|
||||||
|
for prefix, mapped in pairs(legacyAdhesion) do
|
||||||
|
local base = class:match("^" .. prefix .. "_(.+)")
|
||||||
|
if base then class = base; adhesion = adhesion or mapped; break end
|
||||||
|
end
|
||||||
|
|
||||||
local props = globalAssetProperties and globalAssetProperties[class]
|
local props = globalAssetProperties and globalAssetProperties[class]
|
||||||
local behavior = props and props.class or "immoveable"
|
local behavior = props and props.class or "immoveable"
|
||||||
local entity
|
local entity
|
||||||
|
|
@ -829,13 +837,7 @@ function Room:createEntity(class, color, x, y, data)
|
||||||
entity = GenericPlayerBlocker:new(x, y, color, sprite, cellShape)
|
entity = GenericPlayerBlocker:new(x, y, color, sprite, cellShape)
|
||||||
self:registerPlayerBlocker(entity, color)
|
self:registerPlayerBlocker(entity, color)
|
||||||
elseif behavior == "moveable" then
|
elseif behavior == "moveable" then
|
||||||
entity = GenericMoveable:new(x, y, color, sprite, class, cellShape)
|
entity = GenericMoveable:new(x, y, color, sprite, class, cellShape, adhesion)
|
||||||
elseif behavior == "sticky" then
|
|
||||||
entity = GenericSticky:new(x, y, color, sprite, class, cellShape)
|
|
||||||
elseif behavior == "skewer" then
|
|
||||||
entity = GenericSkewer:new(x, y, color, sprite, class, cellShape)
|
|
||||||
elseif behavior == "spiky" then
|
|
||||||
entity = GenericSpiky:new(x, y, color, sprite, class, cellShape)
|
|
||||||
else
|
else
|
||||||
entity = GenericUnmoveable:new(x, y, color, sprite, cellShape)
|
entity = GenericUnmoveable:new(x, y, color, sprite, cellShape)
|
||||||
end
|
end
|
||||||
|
|
@ -1038,7 +1040,7 @@ function Room:attemptDelete(mouseX, mouseY, color)
|
||||||
return self:attemptDeleteAtCell(self:getCellFromMousePos(mouseX, mouseY), color)
|
return self:attemptDeleteAtCell(self:getCellFromMousePos(mouseX, mouseY), color)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Room:attemptPlaceAtCell(name, props, cell, color, text)
|
function Room:attemptPlaceAtCell(name, props, cell, color, text, adhesion)
|
||||||
-- Notes are colourless, single-instance annotations (no per-channel sprites),
|
-- Notes are colourless, single-instance annotations (no per-channel sprites),
|
||||||
-- so they place once carrying their text rather than one copy per colour. One
|
-- so they place once carrying their text rather than one copy per colour. One
|
||||||
-- note per cell: drop any note already here before adding the new one.
|
-- note per cell: drop any note already here before adding the new one.
|
||||||
|
|
@ -1084,7 +1086,7 @@ function Room:attemptPlaceAtCell(name, props, cell, color, text)
|
||||||
-- additive blend combines the channels into full color
|
-- additive blend combines the channels into full color
|
||||||
for _, c in ipairs(colors) do
|
for _, c in ipairs(colors) do
|
||||||
if props.sprites[c] then
|
if props.sprites[c] then
|
||||||
self:createEntity(name, c, cell.x, cell.y)
|
self:createEntity(name, c, cell.x, cell.y, { adhesion = adhesion })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self:tick()
|
self:tick()
|
||||||
|
|
@ -1181,6 +1183,7 @@ function Room:serialize(includePlayers)
|
||||||
local pos = entity:getGridPos()
|
local pos = entity:getGridPos()
|
||||||
local object = { class = class, color = color, x = pos.x, y = pos.y }
|
local object = { class = class, color = color, x = pos.x, y = pos.y }
|
||||||
if entity.circuitId then object.circuit = entity.circuitId end
|
if entity.circuitId then object.circuit = entity.circuitId end
|
||||||
|
if entity.adhesion then object.adhesion = entity.adhesion end
|
||||||
table.insert(objects, object)
|
table.insert(objects, object)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
1907
rooms/spiky_test.sav
1907
rooms/spiky_test.sav
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -3,598 +3,605 @@ wires={
|
||||||
},
|
},
|
||||||
objects={
|
objects={
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=2,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=2
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=2,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=2
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=3,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=3
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=3,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=3
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=4,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=4
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=4,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=4
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=5,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=5
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=5,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=5
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=6,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=6
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=6,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=6
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=7,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=7
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=7,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=7
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=8,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=8
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=8,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=8
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=9,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=9
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=9,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=9
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=10,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=10
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=10,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=10
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=2,
|
x=2,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=3,
|
x=3,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=4,
|
x=4,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=5,
|
x=5,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=7,
|
x=7,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=8,
|
x=8,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=9,
|
x=9,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=10,
|
x=10,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=9,
|
x=9,
|
||||||
|
y=1,
|
||||||
class="box",
|
class="box",
|
||||||
y=1
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
x=7,
|
||||||
|
y=1,
|
||||||
|
adhesion="sticky",
|
||||||
color="red",
|
color="red",
|
||||||
x=7,
|
class="box_2x1_11"
|
||||||
class="spiky_box_2x1_11",
|
|
||||||
y=1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
x=5,
|
||||||
|
y=1,
|
||||||
|
adhesion="sticky",
|
||||||
color="red",
|
color="red",
|
||||||
x=5,
|
class="box_2x1_11"
|
||||||
class="spiky_box_2x1_11",
|
|
||||||
y=1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=2,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=2
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=2,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=2
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=3,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=3
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=3,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=3
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=4,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=4
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=4,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=4
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=5,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=5
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=5,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=5
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=6,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=6
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=6,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=6
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=7,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=7
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=7,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=7
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=8,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=8
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=8,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=8
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=9,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=9
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=9,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=9
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=10,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=10
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=10,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=10
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=2,
|
x=2,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=3,
|
x=3,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=4,
|
x=4,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=5,
|
x=5,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=7,
|
x=7,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=8,
|
x=8,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=9,
|
x=9,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=10,
|
x=10,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=2,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=2
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=2,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=2
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=3,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=3
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=3,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=3
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=4,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=4
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=4,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=4
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=5,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=5
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=6,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=6
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=6,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=6
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=7,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=7
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=7,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=7
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=8,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=8
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=8,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=8
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=9,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=9
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=9,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=9
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=10,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=10
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=10,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=10
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=2,
|
x=2,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=3,
|
x=3,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=4,
|
x=4,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=5,
|
x=5,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=7,
|
x=7,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=8,
|
x=8,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=9,
|
x=9,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=10,
|
x=10,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=9,
|
x=9,
|
||||||
class="spiky_box_2x1_11",
|
y=10,
|
||||||
y=10
|
adhesion="sticky",
|
||||||
|
color="blue",
|
||||||
|
class="box_2x1_11"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=7,
|
x=7,
|
||||||
class="spiky_box_2x1_11",
|
y=10,
|
||||||
y=10
|
adhesion="sticky",
|
||||||
|
color="blue",
|
||||||
|
class="box_2x1_11"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=9,
|
x=9,
|
||||||
class="spiky_box_2x1_11",
|
y=9,
|
||||||
y=9
|
adhesion="sticky",
|
||||||
|
color="blue",
|
||||||
|
class="box_2x1_11"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=8,
|
x=8,
|
||||||
|
y=8,
|
||||||
class="box_2x2_11-10",
|
class="box_2x2_11-10",
|
||||||
y=8
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=3,
|
x=3,
|
||||||
class="spiky_box_2x1_11",
|
y=10,
|
||||||
y=10
|
adhesion="sticky",
|
||||||
|
color="blue",
|
||||||
|
class="box_2x1_11"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=5,
|
x=5,
|
||||||
class="spiky_box_2x1_11",
|
y=10,
|
||||||
y=10
|
adhesion="sticky",
|
||||||
|
color="blue",
|
||||||
|
class="box_2x1_11"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=2,
|
x=2,
|
||||||
|
y=9,
|
||||||
class="box_2x2_11-10",
|
class="box_2x2_11-10",
|
||||||
y=9
|
color="blue"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,370 +1,383 @@
|
||||||
{
|
{
|
||||||
objects={
|
objects={
|
||||||
{
|
{
|
||||||
class="door",
|
|
||||||
color="blue",
|
|
||||||
x=6,
|
x=6,
|
||||||
y=1
|
y=1,
|
||||||
|
color="blue",
|
||||||
|
class="door"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="blue",
|
|
||||||
x=1,
|
x=1,
|
||||||
y=1
|
y=1,
|
||||||
|
color="blue",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="blue",
|
|
||||||
x=2,
|
x=2,
|
||||||
y=1
|
y=1,
|
||||||
|
color="blue",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="blue",
|
|
||||||
x=3,
|
x=3,
|
||||||
y=1
|
y=1,
|
||||||
|
color="blue",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="blue",
|
|
||||||
x=4,
|
x=4,
|
||||||
y=1
|
y=1,
|
||||||
|
color="blue",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="blue",
|
|
||||||
x=5,
|
x=5,
|
||||||
y=1
|
y=1,
|
||||||
|
color="blue",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="blue",
|
|
||||||
x=7,
|
x=7,
|
||||||
y=1
|
y=1,
|
||||||
|
color="blue",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="blue",
|
|
||||||
x=8,
|
x=8,
|
||||||
y=1
|
y=1,
|
||||||
|
color="blue",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="blue",
|
|
||||||
x=9,
|
x=9,
|
||||||
y=1
|
y=1,
|
||||||
|
color="blue",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="blue",
|
|
||||||
x=10,
|
x=10,
|
||||||
y=1
|
y=1,
|
||||||
|
color="blue",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
y=1
|
y=1,
|
||||||
|
color="blue",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="blue",
|
|
||||||
x=1,
|
x=1,
|
||||||
y=11
|
y=11,
|
||||||
|
color="blue",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
y=11
|
y=11,
|
||||||
|
color="blue",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="spiky_box_2x1_11",
|
|
||||||
color="blue",
|
|
||||||
x=1,
|
x=1,
|
||||||
y=6
|
y=6,
|
||||||
|
adhesion="sticky",
|
||||||
|
class="box_2x1_11",
|
||||||
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="box",
|
|
||||||
color="blue",
|
|
||||||
x=3,
|
x=3,
|
||||||
y=6
|
y=6,
|
||||||
|
color="blue",
|
||||||
|
class="box"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="box_1x2_1-1",
|
|
||||||
color="blue",
|
|
||||||
x=4,
|
x=4,
|
||||||
y=6
|
y=6,
|
||||||
|
color="blue",
|
||||||
|
class="box_1x2_1-1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="spiky_box_1x2_1-1",
|
|
||||||
color="blue",
|
|
||||||
x=5,
|
x=5,
|
||||||
y=6
|
y=6,
|
||||||
|
adhesion="sticky",
|
||||||
|
class="box_1x2_1-1",
|
||||||
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="box_2x2_11-10",
|
|
||||||
color="blue",
|
|
||||||
x=6,
|
x=6,
|
||||||
y=5
|
y=5,
|
||||||
|
color="blue",
|
||||||
|
class="box_2x2_11-10"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="spiky_box_2x1_11",
|
|
||||||
color="blue",
|
|
||||||
x=7,
|
x=7,
|
||||||
y=6
|
y=6,
|
||||||
|
adhesion="sticky",
|
||||||
|
class="box_2x1_11",
|
||||||
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="spiky_box_2x1_11",
|
|
||||||
color="blue",
|
|
||||||
x=9,
|
x=9,
|
||||||
y=6
|
y=6,
|
||||||
|
adhesion="sticky",
|
||||||
|
class="box_2x1_11",
|
||||||
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="door",
|
|
||||||
color="red",
|
|
||||||
x=6,
|
x=6,
|
||||||
y=1
|
y=1,
|
||||||
|
color="red",
|
||||||
|
class="door"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="red",
|
|
||||||
x=1,
|
x=1,
|
||||||
y=1
|
y=1,
|
||||||
|
color="red",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="red",
|
|
||||||
x=2,
|
x=2,
|
||||||
y=1
|
y=1,
|
||||||
|
color="red",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="red",
|
|
||||||
x=3,
|
x=3,
|
||||||
y=1
|
y=1,
|
||||||
|
color="red",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="red",
|
|
||||||
x=4,
|
x=4,
|
||||||
y=1
|
y=1,
|
||||||
|
color="red",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="red",
|
|
||||||
x=5,
|
x=5,
|
||||||
y=1
|
y=1,
|
||||||
|
color="red",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="red",
|
|
||||||
x=7,
|
x=7,
|
||||||
y=1
|
y=1,
|
||||||
|
color="red",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="red",
|
|
||||||
x=8,
|
x=8,
|
||||||
y=1
|
y=1,
|
||||||
|
color="red",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="red",
|
|
||||||
x=9,
|
x=9,
|
||||||
y=1
|
y=1,
|
||||||
|
color="red",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="red",
|
|
||||||
x=10,
|
x=10,
|
||||||
y=1
|
y=1,
|
||||||
|
color="red",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
y=1
|
y=1,
|
||||||
|
color="red",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="red",
|
|
||||||
x=1,
|
x=1,
|
||||||
y=11
|
y=11,
|
||||||
|
color="red",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
y=11
|
y=11,
|
||||||
|
color="red",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="spiky_box_2x1_11",
|
|
||||||
color="red",
|
|
||||||
x=1,
|
x=1,
|
||||||
y=6
|
y=6,
|
||||||
|
adhesion="sticky",
|
||||||
|
class="box_2x1_11",
|
||||||
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="box",
|
|
||||||
color="red",
|
|
||||||
x=3,
|
x=3,
|
||||||
y=6
|
y=6,
|
||||||
|
color="red",
|
||||||
|
class="box"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="box_1x2_1-1",
|
|
||||||
color="red",
|
|
||||||
x=4,
|
x=4,
|
||||||
y=6
|
y=6,
|
||||||
|
color="red",
|
||||||
|
class="box_1x2_1-1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="spiky_box_1x2_1-1",
|
|
||||||
color="red",
|
|
||||||
x=5,
|
x=5,
|
||||||
y=6
|
y=6,
|
||||||
|
adhesion="sticky",
|
||||||
|
class="box_1x2_1-1",
|
||||||
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="box_2x2_11-10",
|
|
||||||
color="red",
|
|
||||||
x=6,
|
x=6,
|
||||||
y=5
|
y=5,
|
||||||
|
color="red",
|
||||||
|
class="box_2x2_11-10"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="spiky_box_2x1_11",
|
|
||||||
color="red",
|
|
||||||
x=7,
|
x=7,
|
||||||
y=6
|
y=6,
|
||||||
|
adhesion="sticky",
|
||||||
|
class="box_2x1_11",
|
||||||
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="spiky_box_2x1_11",
|
|
||||||
color="red",
|
|
||||||
x=9,
|
x=9,
|
||||||
y=6
|
y=6,
|
||||||
|
adhesion="sticky",
|
||||||
|
class="box_2x1_11",
|
||||||
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="door",
|
|
||||||
color="green",
|
|
||||||
x=6,
|
x=6,
|
||||||
y=1
|
y=1,
|
||||||
|
color="green",
|
||||||
|
class="door"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="green",
|
|
||||||
x=1,
|
x=1,
|
||||||
y=1
|
y=1,
|
||||||
|
color="green",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="green",
|
|
||||||
x=2,
|
x=2,
|
||||||
y=1
|
y=1,
|
||||||
|
color="green",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="green",
|
|
||||||
x=3,
|
x=3,
|
||||||
y=1
|
y=1,
|
||||||
|
color="green",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="green",
|
|
||||||
x=4,
|
x=4,
|
||||||
y=1
|
y=1,
|
||||||
|
color="green",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="green",
|
|
||||||
x=5,
|
x=5,
|
||||||
y=1
|
y=1,
|
||||||
|
color="green",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="green",
|
|
||||||
x=7,
|
x=7,
|
||||||
y=1
|
y=1,
|
||||||
|
color="green",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="green",
|
|
||||||
x=8,
|
x=8,
|
||||||
y=1
|
y=1,
|
||||||
|
color="green",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="green",
|
|
||||||
x=9,
|
x=9,
|
||||||
y=1
|
y=1,
|
||||||
|
color="green",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="green",
|
|
||||||
x=10,
|
x=10,
|
||||||
y=1
|
y=1,
|
||||||
|
color="green",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
y=1
|
y=1,
|
||||||
|
color="green",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="green",
|
|
||||||
x=1,
|
x=1,
|
||||||
y=11
|
y=11,
|
||||||
|
color="green",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="static_wall",
|
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
y=11
|
y=11,
|
||||||
|
color="green",
|
||||||
|
class="static_wall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="spiky_box_2x1_11",
|
|
||||||
color="green",
|
|
||||||
x=3,
|
x=3,
|
||||||
y=6
|
y=6,
|
||||||
|
adhesion="sticky",
|
||||||
|
class="box_2x1_11",
|
||||||
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="spiky_box_2x1_11",
|
|
||||||
color="green",
|
|
||||||
x=1,
|
x=1,
|
||||||
y=6
|
y=6,
|
||||||
|
adhesion="sticky",
|
||||||
|
class="box_2x1_11",
|
||||||
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="box",
|
|
||||||
color="green",
|
|
||||||
x=5,
|
x=5,
|
||||||
y=6
|
y=6,
|
||||||
|
color="green",
|
||||||
|
class="box"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="box_1x2_1-1",
|
|
||||||
color="green",
|
|
||||||
x=6,
|
x=6,
|
||||||
y=6
|
y=6,
|
||||||
|
color="green",
|
||||||
|
class="box_1x2_1-1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="spiky_box_1x2_1-1",
|
|
||||||
color="green",
|
|
||||||
x=7,
|
x=7,
|
||||||
y=6
|
y=6,
|
||||||
|
adhesion="sticky",
|
||||||
|
class="box_1x2_1-1",
|
||||||
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="box_2x2_11-10",
|
|
||||||
color="green",
|
|
||||||
x=8,
|
x=8,
|
||||||
y=5
|
y=5,
|
||||||
|
color="green",
|
||||||
|
class="box_2x2_11-10"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="spiky_box_2x1_11",
|
|
||||||
color="green",
|
|
||||||
x=9,
|
x=9,
|
||||||
y=6
|
y=6,
|
||||||
|
adhesion="sticky",
|
||||||
|
class="box_2x1_11",
|
||||||
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
class="spiky_box_2x1_11",
|
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
y=6
|
y=6,
|
||||||
|
adhesion="sticky",
|
||||||
|
class="box_2x1_11",
|
||||||
|
color="green"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,585 +3,588 @@ wires={
|
||||||
},
|
},
|
||||||
objects={
|
objects={
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=2,
|
x=2,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=3,
|
x=3,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=4,
|
x=4,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=5,
|
x=5,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=6,
|
x=6,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=7,
|
x=7,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=8,
|
x=8,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=9,
|
x=9,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=10,
|
x=10,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=2,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=2
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=3,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=3
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=4,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=4
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=5,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=5
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=6,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=6
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=7,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=7
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=8,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=8
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=9,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=9
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=10,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=10
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=2,
|
x=2,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=3,
|
x=3,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=5,
|
x=5,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=6,
|
x=6,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=7,
|
x=7,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=8,
|
x=8,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=9,
|
x=9,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=10,
|
x=10,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=8,
|
x=8,
|
||||||
|
y=4,
|
||||||
class="box",
|
class="box",
|
||||||
y=4
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=7,
|
x=7,
|
||||||
class="spiky_box_2x1_11",
|
y=3,
|
||||||
y=3
|
adhesion="sticky",
|
||||||
|
color="red",
|
||||||
|
class="box_2x1_11"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=6,
|
x=6,
|
||||||
class="spiky_box_2x1_11",
|
y=4,
|
||||||
y=4
|
adhesion="sticky",
|
||||||
|
color="red",
|
||||||
|
class="box_2x1_11"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=4,
|
x=4,
|
||||||
class="spiky_box_2x1_11",
|
y=4,
|
||||||
y=4
|
adhesion="sticky",
|
||||||
|
color="red",
|
||||||
|
class="box_2x1_11"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="red",
|
|
||||||
x=3,
|
x=3,
|
||||||
|
y=3,
|
||||||
class="box_2x2_11-10",
|
class="box_2x2_11-10",
|
||||||
y=3
|
color="red"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=2,
|
x=2,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=3,
|
x=3,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=4,
|
x=4,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=5,
|
x=5,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=6,
|
x=6,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=7,
|
x=7,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=8,
|
x=8,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=9,
|
x=9,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=10,
|
x=10,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=2,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=2
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=3,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=3
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=4,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=4
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=5,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=5
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=6,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=6
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=7,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=7
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=8,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=8
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=9,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=9
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=10,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=10
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=2,
|
x=2,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=3,
|
x=3,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=5,
|
x=5,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=6,
|
x=6,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=7,
|
x=7,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=8,
|
x=8,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=9,
|
x=9,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=10,
|
x=10,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="green",
|
|
||||||
x=4,
|
x=4,
|
||||||
|
y=3,
|
||||||
class="box",
|
class="box",
|
||||||
y=3
|
color="green"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=2,
|
x=2,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=3,
|
x=3,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=4,
|
x=4,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=5,
|
x=5,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=6,
|
x=6,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=7,
|
x=7,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=8,
|
x=8,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=9,
|
x=9,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=10,
|
x=10,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=1,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=1
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=2,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=2
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=3,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=3
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=4,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=4
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=5,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=5
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=6,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=6
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=7,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=7
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=8,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=8
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=9,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=9
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=10,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=10
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=1,
|
x=1,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=2,
|
x=2,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=3,
|
x=3,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=5,
|
x=5,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=6,
|
x=6,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=7,
|
x=7,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=8,
|
x=8,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=9,
|
x=9,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=10,
|
x=10,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
color="blue",
|
|
||||||
x=11,
|
x=11,
|
||||||
|
y=11,
|
||||||
class="static_wall",
|
class="static_wall",
|
||||||
y=11
|
color="blue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=5,
|
x=5,
|
||||||
class="npc",
|
y=2,
|
||||||
y=2
|
class="npc"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
58
shaders.lua
58
shaders.lua
|
|
@ -183,6 +183,64 @@ function drawGoopySprite(sprite, x, y, scale)
|
||||||
love.graphics.setShader(previousShader)
|
love.graphics.setShader(previousShader)
|
||||||
end
|
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
|
-- Accessibility post-process. The puzzle's RGB channels are normally also
|
||||||
-- its visual language, so this runs only after the entire scene has been
|
-- 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
|
-- composed. It leaves neutral UI/text alone and remaps saturated colours to
|
||||||
|
|
|
||||||
|
|
@ -29,19 +29,16 @@ local function assertNotIn(group, entity, message)
|
||||||
if group[entity] then error(message or "expected entity outside movement group", 2) end
|
if group[entity] then error(message or "expected entity outside movement group", 2) end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function movable(x, y, sticky, shape, skewer, spiky)
|
local function movable(x, y, adhesion, shape)
|
||||||
local entity = {
|
local entity = {
|
||||||
x = x, y = y,
|
x = x, y = y,
|
||||||
sticky = sticky or false,
|
adhesion = adhesion,
|
||||||
skewer = skewer or false,
|
|
||||||
spiky = spiky or false,
|
|
||||||
shape = shape or {{x = 0, y = 0}},
|
shape = shape or {{x = 0, y = 0}},
|
||||||
}
|
}
|
||||||
function entity:canMove() return true end
|
function entity:canMove() return true end
|
||||||
function entity:canPassOver() return false end
|
function entity:canPassOver() return false end
|
||||||
function entity:isSticky() return self.sticky end
|
function entity:isSticky() return self.adhesion == "sticky" end
|
||||||
function entity:isSkewer() return self.skewer end
|
function entity:isGluey() return self.adhesion == "gluey" end
|
||||||
function entity:isSpiky() return self.spiky end
|
|
||||||
function entity:bump() end
|
function entity:bump() end
|
||||||
function entity:move(axis, amount) self[axis] = self[axis] + amount end
|
function entity:move(axis, amount) self[axis] = self[axis] + amount end
|
||||||
function entity:getGridPos() return {x = self.x, y = self.y} end
|
function entity:getGridPos() return {x = self.x, y = self.y} end
|
||||||
|
|
@ -94,7 +91,7 @@ end)
|
||||||
|
|
||||||
test("a trailing sticky block joins a horizontal pull", function()
|
test("a trailing sticky block joins a horizontal pull", function()
|
||||||
-- sticky, regular, player; moving right
|
-- sticky, regular, player; moving right
|
||||||
local sticky = movable(2, 3, true)
|
local sticky = movable(2, 3, "sticky")
|
||||||
local regular = movable(3, 3)
|
local regular = movable(3, 3)
|
||||||
local possible, group = plan({sticky, regular}, {x = 3, y = 3}, {x = 1, y = 0})
|
local possible, group = plan({sticky, regular}, {x = 3, y = 3}, {x = 1, y = 0})
|
||||||
assertTrue(possible)
|
assertTrue(possible)
|
||||||
|
|
@ -105,7 +102,7 @@ end)
|
||||||
test("a moving sticky block pulls a regular block behind it", function()
|
test("a moving sticky block pulls a regular block behind it", function()
|
||||||
-- regular, sticky, player; moving right
|
-- regular, sticky, player; moving right
|
||||||
local regular = movable(2, 3)
|
local regular = movable(2, 3)
|
||||||
local sticky = movable(3, 3, true)
|
local sticky = movable(3, 3, "sticky")
|
||||||
local possible, group = plan({regular, sticky}, {x = 3, y = 3}, {x = 1, y = 0})
|
local possible, group = plan({regular, sticky}, {x = 3, y = 3}, {x = 1, y = 0})
|
||||||
assertTrue(possible)
|
assertTrue(possible)
|
||||||
assertIn(group, sticky)
|
assertIn(group, sticky)
|
||||||
|
|
@ -114,7 +111,7 @@ end)
|
||||||
|
|
||||||
test("sticky pull chains work vertically", function()
|
test("sticky pull chains work vertically", function()
|
||||||
-- player moves down, pulling the two blocks above it
|
-- player moves down, pulling the two blocks above it
|
||||||
local sticky = movable(4, 2, true)
|
local sticky = movable(4, 2, "sticky")
|
||||||
local regular = movable(4, 3)
|
local regular = movable(4, 3)
|
||||||
local possible, group = plan({sticky, regular}, {x = 4, y = 3}, {x = 0, y = 1})
|
local possible, group = plan({sticky, regular}, {x = 4, y = 3}, {x = 0, y = 1})
|
||||||
assertTrue(possible)
|
assertTrue(possible)
|
||||||
|
|
@ -129,7 +126,7 @@ test("sticky pulls are symmetric in all four directions", function()
|
||||||
}) do
|
}) do
|
||||||
local player = {x = 4, y = 4}
|
local player = {x = 4, y = 4}
|
||||||
local regular = movable(player.x - direction.x, player.y - direction.y)
|
local regular = movable(player.x - direction.x, player.y - direction.y)
|
||||||
local sticky = movable(player.x - direction.x * 2, player.y - direction.y * 2, true)
|
local sticky = movable(player.x - direction.x * 2, player.y - direction.y * 2, "sticky")
|
||||||
local possible, group = plan(
|
local possible, group = plan(
|
||||||
{sticky, regular},
|
{sticky, regular},
|
||||||
{x = regular.x, y = regular.y},
|
{x = regular.x, y = regular.y},
|
||||||
|
|
@ -141,19 +138,20 @@ test("sticky pulls are symmetric in all four directions", function()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test("a sticky block shears off during orthogonal movement", function()
|
test("a sticky block grabs a free lateral block during orthogonal movement", function()
|
||||||
-- The regular block moves right; the sticky block above it stays behind.
|
-- The regular block moves right; the sticky block above it is soft-grabbed on
|
||||||
|
-- that lateral face (sticky pulls on every edge) and follows along.
|
||||||
local regular = movable(3, 4)
|
local regular = movable(3, 4)
|
||||||
local stickyBranch = movable(3, 3, true)
|
local stickyBranch = movable(3, 3, "sticky")
|
||||||
local possible, group = plan({regular, stickyBranch}, {x = 3, y = 4}, {x = 1, y = 0})
|
local possible, group = plan({regular, stickyBranch}, {x = 3, y = 4}, {x = 1, y = 0})
|
||||||
assertTrue(possible)
|
assertTrue(possible)
|
||||||
assertIn(group, regular)
|
assertIn(group, regular)
|
||||||
assertNotIn(group, stickyBranch)
|
assertIn(group, stickyBranch)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test("a blocked sheared sticky block does not stop movement", function()
|
test("a blocked sticky lateral grab does not stop movement", function()
|
||||||
local regular = movable(3, 4)
|
local regular = movable(3, 4)
|
||||||
local stickyBranch = movable(3, 3, true)
|
local stickyBranch = movable(3, 3, "sticky")
|
||||||
local blockingWall = wall(4, 3)
|
local blockingWall = wall(4, 3)
|
||||||
local possible, group = plan({regular, stickyBranch, blockingWall}, {x = 3, y = 4}, {x = 1, y = 0})
|
local possible, group = plan({regular, stickyBranch, blockingWall}, {x = 3, y = 4}, {x = 1, y = 0})
|
||||||
assertTrue(possible)
|
assertTrue(possible)
|
||||||
|
|
@ -161,49 +159,49 @@ test("a blocked sheared sticky block does not stop movement", function()
|
||||||
assertNotIn(group, stickyBranch)
|
assertNotIn(group, stickyBranch)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test("a skewer carries an orthogonal branch", function()
|
test("a gluey block welds an orthogonal branch", function()
|
||||||
local regular = movable(3, 4)
|
local regular = movable(3, 4)
|
||||||
local skewerBranch = movable(3, 3, false, nil, true)
|
local glueyBranch = movable(3, 3, "gluey")
|
||||||
local possible, group = plan({regular, skewerBranch}, {x = 3, y = 4}, {x = 1, y = 0})
|
local possible, group = plan({regular, glueyBranch}, {x = 3, y = 4}, {x = 1, y = 0})
|
||||||
assertTrue(possible)
|
assertTrue(possible)
|
||||||
assertIn(group, regular)
|
assertIn(group, regular)
|
||||||
assertIn(group, skewerBranch)
|
assertIn(group, glueyBranch)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test("a blocked skewer branch stops its rigid movement group", function()
|
test("a blocked gluey branch stops its rigid movement group", function()
|
||||||
local regular = movable(3, 4)
|
local regular = movable(3, 4)
|
||||||
local skewerBranch = movable(3, 3, false, nil, true)
|
local glueyBranch = movable(3, 3, "gluey")
|
||||||
local blockingWall = wall(4, 3)
|
local blockingWall = wall(4, 3)
|
||||||
local possible = plan({regular, skewerBranch, blockingWall}, {x = 3, y = 4}, {x = 1, y = 0})
|
local possible = plan({regular, glueyBranch, blockingWall}, {x = 3, y = 4}, {x = 1, y = 0})
|
||||||
assertFalse(possible)
|
assertFalse(possible)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test("a spiky block pulls both orthogonal side blocks", function()
|
test("a sticky block pulls both orthogonal side blocks", function()
|
||||||
local spiky = movable(4, 4, false, nil, false, true)
|
local sticky = movable(4, 4, "sticky")
|
||||||
local upper = movable(4, 3)
|
local upper = movable(4, 3)
|
||||||
local lower = movable(4, 5)
|
local lower = movable(4, 5)
|
||||||
local possible, group = plan(
|
local possible, group = plan(
|
||||||
{spiky, upper, lower}, {x = 4, y = 4}, {x = 1, y = 0}
|
{sticky, upper, lower}, {x = 4, y = 4}, {x = 1, y = 0}
|
||||||
)
|
)
|
||||||
|
|
||||||
assertTrue(possible)
|
assertTrue(possible)
|
||||||
assertIn(group, spiky)
|
assertIn(group, sticky)
|
||||||
assertIn(group, upper)
|
assertIn(group, upper)
|
||||||
assertIn(group, lower)
|
assertIn(group, lower)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test("a spiky block releases one blocked side while pulling the other", function()
|
test("a sticky block releases one blocked side while pulling the other", function()
|
||||||
local spiky = movable(4, 4, false, nil, false, true)
|
local sticky = movable(4, 4, "sticky")
|
||||||
local blockedUpper = movable(4, 3)
|
local blockedUpper = movable(4, 3)
|
||||||
local freeLower = movable(4, 5)
|
local freeLower = movable(4, 5)
|
||||||
local blockingWall = wall(5, 3)
|
local blockingWall = wall(5, 3)
|
||||||
local possible, group = plan(
|
local possible, group = plan(
|
||||||
{spiky, blockedUpper, freeLower, blockingWall},
|
{sticky, blockedUpper, freeLower, blockingWall},
|
||||||
{x = 4, y = 4}, {x = 1, y = 0}
|
{x = 4, y = 4}, {x = 1, y = 0}
|
||||||
)
|
)
|
||||||
|
|
||||||
assertTrue(possible)
|
assertTrue(possible)
|
||||||
assertIn(group, spiky)
|
assertIn(group, sticky)
|
||||||
assertNotIn(group, blockedUpper)
|
assertNotIn(group, blockedUpper)
|
||||||
assertIn(group, freeLower)
|
assertIn(group, freeLower)
|
||||||
end)
|
end)
|
||||||
|
|
@ -219,16 +217,16 @@ end)
|
||||||
|
|
||||||
test("diagonal blocks do not stick", function()
|
test("diagonal blocks do not stick", function()
|
||||||
local regular = movable(3, 3)
|
local regular = movable(3, 3)
|
||||||
local diagonalSticky = movable(2, 2, true)
|
local diagonalSticky = movable(2, 2, "sticky")
|
||||||
local possible, group = plan({regular, diagonalSticky}, {x = 3, y = 3}, {x = 1, y = 0})
|
local possible, group = plan({regular, diagonalSticky}, {x = 3, y = 3}, {x = 1, y = 0})
|
||||||
assertTrue(possible)
|
assertTrue(possible)
|
||||||
assertIn(group, regular)
|
assertIn(group, regular)
|
||||||
assertNotIn(group, diagonalSticky)
|
assertNotIn(group, diagonalSticky)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test("multi-cell sticky blocks pull from every trailing occupied cell", function()
|
test("multi-cell sticky blocks pull from every occupied cell", function()
|
||||||
local tallRegular = movable(3, 3, false, {{x = 0, y = 0}, {x = 0, y = 1}})
|
local tallRegular = movable(3, 3, nil, {{x = 0, y = 0}, {x = 0, y = 1}})
|
||||||
local sticky = movable(2, 4, true)
|
local sticky = movable(2, 4, "sticky")
|
||||||
local possible, group = plan({tallRegular, sticky}, {x = 3, y = 3}, {x = 1, y = 0})
|
local possible, group = plan({tallRegular, sticky}, {x = 3, y = 3}, {x = 1, y = 0})
|
||||||
assertTrue(possible)
|
assertTrue(possible)
|
||||||
assertIn(group, tallRegular)
|
assertIn(group, tallRegular)
|
||||||
|
|
@ -245,7 +243,7 @@ test("an L-shaped sticky pulls from its trailing face in every direction", funct
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, case in ipairs(cases) do
|
for _, case in ipairs(cases) do
|
||||||
local stickyL = movable(3, 3, true, lShape)
|
local stickyL = movable(3, 3, "sticky", lShape)
|
||||||
local trailing = movable(case.trailing.x, case.trailing.y)
|
local trailing = movable(case.trailing.x, case.trailing.y)
|
||||||
local possible, group = plan(
|
local possible, group = plan(
|
||||||
{stickyL, trailing}, {x = 3, y = 3}, case.direction
|
{stickyL, trailing}, {x = 3, y = 3}, case.direction
|
||||||
|
|
@ -259,10 +257,10 @@ end)
|
||||||
test("an L-shaped sticky can release one blocked pull while completing another", function()
|
test("an L-shaped sticky can release one blocked pull while completing another", function()
|
||||||
-- Moving right, the two exposed trailing cells touch separate objects. The
|
-- Moving right, the two exposed trailing cells touch separate objects. The
|
||||||
-- upper object is pinned against a wall; the lower one is free to follow.
|
-- upper object is pinned against a wall; the lower one is free to follow.
|
||||||
local stickyL = movable(3, 3, true, {
|
local stickyL = movable(3, 3, "sticky", {
|
||||||
{x = 0, y = 0}, {x = 1, y = 0}, {x = 0, y = 1},
|
{x = 0, y = 0}, {x = 1, y = 0}, {x = 0, y = 1},
|
||||||
})
|
})
|
||||||
local pinned = movable(2, 2, false, {{x = 0, y = 0}, {x = 0, y = 1}})
|
local pinned = movable(2, 2, nil, {{x = 0, y = 0}, {x = 0, y = 1}})
|
||||||
local free = movable(2, 4)
|
local free = movable(2, 4)
|
||||||
local blockingWall = wall(3, 2)
|
local blockingWall = wall(3, 2)
|
||||||
local possible, group = plan(
|
local possible, group = plan(
|
||||||
|
|
@ -278,7 +276,7 @@ end)
|
||||||
|
|
||||||
test("a T-shaped sticky pulls independently across a notched trailing face", function()
|
test("a T-shaped sticky pulls independently across a notched trailing face", function()
|
||||||
-- Moving up, three exposed lower faces pull into cells vacated by the T.
|
-- Moving up, three exposed lower faces pull into cells vacated by the T.
|
||||||
local stickyT = movable(3, 3, true, {
|
local stickyT = movable(3, 3, "sticky", {
|
||||||
{x = 0, y = 0}, {x = 1, y = 0}, {x = 2, y = 0},
|
{x = 0, y = 0}, {x = 1, y = 0}, {x = 2, y = 0},
|
||||||
{x = 1, y = 1},
|
{x = 1, y = 1},
|
||||||
})
|
})
|
||||||
|
|
@ -297,54 +295,46 @@ test("a T-shaped sticky pulls independently across a notched trailing face", fun
|
||||||
assertIn(group, trailingStem)
|
assertIn(group, trailingStem)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test("an L-shaped spiky block grabs moveables across several exposed faces", function()
|
test("an L-shaped sticky block grabs moveables across several exposed faces", function()
|
||||||
local spikyL = movable(3, 3, false, {
|
local stickyL = movable(3, 3, "sticky", {
|
||||||
{x = 0, y = 0}, {x = 1, y = 0}, {x = 0, y = 1},
|
{x = 0, y = 0}, {x = 1, y = 0}, {x = 0, y = 1},
|
||||||
}, false, true)
|
})
|
||||||
local above = movable(3, 2)
|
local above = movable(3, 2)
|
||||||
local below = movable(3, 5)
|
local below = movable(3, 5)
|
||||||
local behind = movable(2, 3)
|
local behind = movable(2, 3)
|
||||||
local possible, group = plan(
|
local possible, group = plan(
|
||||||
{spikyL, above, below, behind},
|
{stickyL, above, below, behind},
|
||||||
{x = 3, y = 3}, {x = 1, y = 0}
|
{x = 3, y = 3}, {x = 1, y = 0}
|
||||||
)
|
)
|
||||||
|
|
||||||
assertTrue(possible)
|
assertTrue(possible)
|
||||||
assertIn(group, spikyL)
|
assertIn(group, stickyL)
|
||||||
assertIn(group, above)
|
assertIn(group, above)
|
||||||
assertIn(group, below)
|
assertIn(group, below)
|
||||||
assertIn(group, behind)
|
assertIn(group, behind)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test("production sticky, skewer, and spiky blocks advertise distinct behavior", function()
|
test("production moveables advertise adhesion (regular, sticky, gluey)", function()
|
||||||
class = require "libs/middleclass"
|
class = require "libs/middleclass"
|
||||||
Color = class("TestColor")
|
Color = class("TestColor")
|
||||||
function Color:initialize(name) self.name = name end
|
function Color:initialize(name) self.name = name end
|
||||||
function Color:getname() return self.name end
|
function Color:getname() return self.name end
|
||||||
|
|
||||||
require "generic_moveable"
|
require "generic_moveable"
|
||||||
require "generic_sticky"
|
|
||||||
require "generic_skewer"
|
|
||||||
require "generic_spiky"
|
|
||||||
local sprite = {getWidth = function() return 16 end, getHeight = function() return 16 end}
|
local sprite = {getWidth = function() return 16 end, getHeight = function() return 16 end}
|
||||||
local regular = GenericMoveable:new(2, 2, "red", sprite, "box")
|
local regular = GenericMoveable:new(2, 2, "red", sprite, "box")
|
||||||
local sticky = GenericSticky:new(3, 2, "red", sprite, "sticky_box")
|
local sticky = GenericMoveable:new(3, 2, "red", sprite, "box", nil, "sticky")
|
||||||
local skewer = GenericSkewer:new(4, 2, "red", sprite, "skewer_box")
|
local gluey = GenericMoveable:new(4, 2, "red", sprite, "box", nil, "gluey")
|
||||||
local spiky = GenericSpiky:new(5, 2, "red", sprite, "spiky_box")
|
|
||||||
|
|
||||||
assertTrue(regular:canMove())
|
assertTrue(regular:canMove())
|
||||||
assertFalse(regular:isSticky())
|
assertFalse(regular:isSticky())
|
||||||
|
assertFalse(regular:isGluey())
|
||||||
assertTrue(sticky:canMove())
|
assertTrue(sticky:canMove())
|
||||||
assertTrue(sticky:isSticky())
|
assertTrue(sticky:isSticky())
|
||||||
assertFalse(sticky:isSkewer())
|
assertFalse(sticky:isGluey())
|
||||||
assertTrue(skewer:canMove())
|
assertTrue(gluey:canMove())
|
||||||
assertFalse(skewer:isSticky())
|
assertFalse(gluey:isSticky())
|
||||||
assertTrue(skewer:isSkewer())
|
assertTrue(gluey:isGluey())
|
||||||
assertFalse(skewer:isSpiky())
|
|
||||||
assertTrue(spiky:canMove())
|
|
||||||
assertFalse(spiky:isSticky())
|
|
||||||
assertFalse(spiky:isSkewer())
|
|
||||||
assertTrue(spiky:isSpiky())
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local function loadRoomClass()
|
local function loadRoomClass()
|
||||||
|
|
@ -442,7 +432,7 @@ end)
|
||||||
|
|
||||||
test("Room applies the sticky-regular-player pull", function()
|
test("Room applies the sticky-regular-player pull", function()
|
||||||
loadRoomClass()
|
loadRoomClass()
|
||||||
local sticky = movable(2, 3, true)
|
local sticky = movable(2, 3, "sticky")
|
||||||
local regular = movable(3, 3)
|
local regular = movable(3, 3)
|
||||||
local player = movable(4, 3)
|
local player = movable(4, 3)
|
||||||
local room = roomWith(player, {sticky, regular})
|
local room = roomWith(player, {sticky, regular})
|
||||||
|
|
@ -462,8 +452,8 @@ test("Room pulls sticky out of a pinned concave moveable", function()
|
||||||
-- mm_ mm_
|
-- mm_ mm_
|
||||||
local player = movable(1, 2)
|
local player = movable(1, 2)
|
||||||
local wall = wall(2, 2)
|
local wall = wall(2, 2)
|
||||||
local sticky = movable(1, 3, true)
|
local sticky = movable(1, 3, "sticky")
|
||||||
local concave = movable(1, 3, false, {
|
local concave = movable(1, 3, nil, {
|
||||||
{x = 1, y = 0},
|
{x = 1, y = 0},
|
||||||
{x = 0, y = 1}, {x = 1, y = 1},
|
{x = 0, y = 1}, {x = 1, y = 1},
|
||||||
})
|
})
|
||||||
|
|
@ -480,7 +470,7 @@ end)
|
||||||
test("Room moves an L-shaped sticky and both available trailing pulls", function()
|
test("Room moves an L-shaped sticky and both available trailing pulls", function()
|
||||||
loadRoomClass()
|
loadRoomClass()
|
||||||
local player = movable(3, 2)
|
local player = movable(3, 2)
|
||||||
local stickyL = movable(3, 3, true, {
|
local stickyL = movable(3, 3, "sticky", {
|
||||||
{x = 0, y = 0}, {x = 1, y = 0}, {x = 0, y = 1},
|
{x = 0, y = 0}, {x = 1, y = 0}, {x = 0, y = 1},
|
||||||
})
|
})
|
||||||
local trailingLeft = movable(3, 5)
|
local trailingLeft = movable(3, 5)
|
||||||
|
|
@ -495,27 +485,13 @@ test("Room moves an L-shaped sticky and both available trailing pulls", function
|
||||||
assertTrue(trailingRight.x == 4 and trailingRight.y == 3)
|
assertTrue(trailingRight.x == 4 and trailingRight.y == 3)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test("Room shears a lateral sticky contact", function()
|
|
||||||
loadRoomClass()
|
|
||||||
local sticky = movable(3, 2, true)
|
|
||||||
local regular = movable(3, 3)
|
|
||||||
local player = movable(4, 3)
|
|
||||||
local room = roomWith(player, {sticky, regular})
|
|
||||||
|
|
||||||
room:movePlayer("right")
|
|
||||||
|
|
||||||
assertTrue(sticky.x == 3, "lateral sticky block should shear and stay behind")
|
|
||||||
assertTrue(regular.x == 4)
|
|
||||||
assertTrue(player.x == 5)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("Room never moves a component found by push and pull twice", function()
|
test("Room never moves a component found by push and pull twice", function()
|
||||||
loadRoomClass()
|
loadRoomClass()
|
||||||
local player = movable(4, 4)
|
local player = movable(4, 4)
|
||||||
local front = movable(5, 4)
|
local front = movable(5, 4)
|
||||||
local bridgeRight = movable(5, 3, false, nil, true)
|
local bridgeRight = movable(5, 3, "gluey")
|
||||||
local bridgeMiddle = movable(4, 3, false, nil, true)
|
local bridgeMiddle = movable(4, 3, "gluey")
|
||||||
local bridgeLeft = movable(3, 3, false, nil, true)
|
local bridgeLeft = movable(3, 3, "gluey")
|
||||||
local rear = movable(3, 4)
|
local rear = movable(3, 4)
|
||||||
local component = {front, bridgeRight, bridgeMiddle, bridgeLeft, rear}
|
local component = {front, bridgeRight, bridgeMiddle, bridgeLeft, rear}
|
||||||
local room = roomWith(player, component)
|
local room = roomWith(player, component)
|
||||||
|
|
@ -533,74 +509,53 @@ test("Room never moves a component found by push and pull twice", function()
|
||||||
assertTrue(player.x == 5)
|
assertTrue(player.x == 5)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test("Room pushes spiky onward while a blocked side shears off", function()
|
test("Room pushes sticky onward while a blocked side shears off", function()
|
||||||
loadRoomClass()
|
loadRoomClass()
|
||||||
local player = movable(3, 4)
|
local player = movable(3, 4)
|
||||||
local spiky = movable(4, 4, false, nil, false, true)
|
local sticky = movable(4, 4, "sticky")
|
||||||
local blockedUpper = movable(4, 3)
|
local blockedUpper = movable(4, 3)
|
||||||
local freeLower = movable(4, 5)
|
local freeLower = movable(4, 5)
|
||||||
local blockingWall = wall(5, 3)
|
local blockingWall = wall(5, 3)
|
||||||
local room = roomWith(player, {spiky, blockedUpper, freeLower, blockingWall})
|
local room = roomWith(player, {sticky, blockedUpper, freeLower, blockingWall})
|
||||||
|
|
||||||
room:movePlayer("right")
|
room:movePlayer("right")
|
||||||
|
|
||||||
assertTrue(player.x == 4 and player.y == 4)
|
assertTrue(player.x == 4 and player.y == 4)
|
||||||
assertTrue(spiky.x == 5 and spiky.y == 4)
|
assertTrue(sticky.x == 5 and sticky.y == 4)
|
||||||
assertTrue(blockedUpper.x == 4 and blockedUpper.y == 3)
|
assertTrue(blockedUpper.x == 4 and blockedUpper.y == 3)
|
||||||
assertTrue(freeLower.x == 5 and freeLower.y == 5)
|
assertTrue(freeLower.x == 5 and freeLower.y == 5)
|
||||||
assertTrue(blockingWall.x == 5 and blockingWall.y == 3)
|
assertTrue(blockingWall.x == 5 and blockingWall.y == 3)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
test("every moveable base box gets all three synthetic behavior variants", function()
|
test("legacy box-variant classes load as adhesion on a plain moveable", function()
|
||||||
local BoxVariants = require "box_variants"
|
loadRoomClass()
|
||||||
local boxSprites = {red = "box_r.png"}
|
local oldAssets, oldLoadSprite = globalAssetProperties, loadSprite
|
||||||
local boxCells = {red = {{x = 1, y = 1}}}
|
local sprite = {getWidth = function() return 16 end, getHeight = function() return 16 end}
|
||||||
local shapeSprites = {blue = "box_shape_b.png"}
|
globalAssetProperties = {
|
||||||
local shapeCells = {blue = {{x = 1, y = 1}, {x = 2, y = 1}}}
|
box = { class = "moveable", sprites = {red = "box_r.png"}, cells = {red = {{x = 1, y = 1}}} },
|
||||||
local lSprites = {green = "box_l_g.png"}
|
|
||||||
local lCells = {green = {{x = 1, y = 1}, {x = 1, y = 2}, {x = 2, y = 2}}}
|
|
||||||
local assets = {
|
|
||||||
box = {class = "moveable", sprites = boxSprites, cells = boxCells},
|
|
||||||
["box_2x1_11"] = {class = "moveable", sprites = shapeSprites, cells = shapeCells},
|
|
||||||
["box_2x2_10-11"] = {class = "moveable", sprites = lSprites, cells = lCells},
|
|
||||||
box_statue = {class = "immoveable", sprites = {}},
|
|
||||||
beer_mug = {class = "moveable", sprites = {}},
|
|
||||||
}
|
}
|
||||||
|
loadSprite = function() return sprite end
|
||||||
|
local room = setmetatable({
|
||||||
|
collideables = {red = {}, green = {}, blue = {}},
|
||||||
|
playerBlockers = {red = {}, green = {}, blue = {}},
|
||||||
|
}, {__index = Room})
|
||||||
|
|
||||||
BoxVariants.addTo(assets)
|
-- Old .sav files baked adhesion into the class name; loading must collapse them
|
||||||
|
-- onto the base box carrying the new adhesion property.
|
||||||
|
local fromSpiky = room:createEntity("spiky_box", "red", 2, 2)
|
||||||
|
local fromSkewer = room:createEntity("skewer_box", "red", 3, 2)
|
||||||
|
local fromSticky = room:createEntity("sticky_box", "red", 4, 2)
|
||||||
|
|
||||||
assertTrue(assets.sticky_box.class == "sticky")
|
assertTrue(fromSpiky:isSticky(), "legacy spiky_ should load as sticky")
|
||||||
assertTrue(assets.sticky_box.directory == "sticky")
|
assertTrue(fromSkewer:isGluey(), "legacy skewer_ should load as gluey")
|
||||||
assertTrue(assets.sticky_box.synthetic)
|
assertTrue(fromSticky:isSticky(), "legacy sticky_ should load as sticky")
|
||||||
assertTrue(assets.sticky_box.goopy)
|
assertTrue(fromSpiky.saveClass == "box", "class should normalize to the base box")
|
||||||
assertTrue(assets.sticky_box.sprites == boxSprites, "variant should share base sprites")
|
|
||||||
assertTrue(assets.sticky_box.cells == boxCells, "variant should share collision cells")
|
-- An explicit adhesion in the object data wins and rides through on a plain box.
|
||||||
assertTrue(assets["sticky_box_2x1_11"].sprites == shapeSprites)
|
local placed = room:createEntity("box", "red", 5, 2, { adhesion = "gluey" })
|
||||||
assertTrue(assets["sticky_box_2x1_11"].cells == shapeCells)
|
assertTrue(placed:isGluey())
|
||||||
assertTrue(assets["sticky_box_2x2_10-11"].sprites == lSprites)
|
assertTrue(placed.saveClass == "box")
|
||||||
assertTrue(assets["sticky_box_2x2_10-11"].cells == lCells)
|
globalAssetProperties, loadSprite = oldAssets, oldLoadSprite
|
||||||
assertTrue(assets.skewer_box.class == "skewer")
|
|
||||||
assertTrue(assets.skewer_box.directory == "skewer")
|
|
||||||
assertTrue(assets.skewer_box.synthetic)
|
|
||||||
assertTrue(assets.skewer_box.skewer)
|
|
||||||
assertTrue(assets.skewer_box.sprites == boxSprites)
|
|
||||||
assertTrue(assets.skewer_box.cells == boxCells)
|
|
||||||
assertTrue(assets["skewer_box_2x1_11"].sprites == shapeSprites)
|
|
||||||
assertTrue(assets["skewer_box_2x2_10-11"].cells == lCells)
|
|
||||||
assertTrue(assets.spiky_box.class == "spiky")
|
|
||||||
assertTrue(assets.spiky_box.directory == "spiky")
|
|
||||||
assertTrue(assets.spiky_box.synthetic)
|
|
||||||
assertTrue(assets.spiky_box.spiky)
|
|
||||||
assertTrue(assets.spiky_box.sprites == boxSprites)
|
|
||||||
assertTrue(assets.spiky_box.cells == boxCells)
|
|
||||||
assertTrue(assets["spiky_box_2x1_11"].sprites == shapeSprites)
|
|
||||||
assertTrue(assets["spiky_box_2x2_10-11"].cells == lCells)
|
|
||||||
assertFalse(assets.sticky_box_statue)
|
|
||||||
assertFalse(assets.sticky_beer_mug)
|
|
||||||
assertFalse(assets.skewer_box_statue)
|
|
||||||
assertFalse(assets.skewer_beer_mug)
|
|
||||||
assertFalse(assets.spiky_box_statue)
|
|
||||||
assertFalse(assets.spiky_beer_mug)
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
print("movement/block-variant tests passed: " .. passed)
|
print("movement tests passed: " .. passed)
|
||||||
|
|
|
||||||
60
tools/backfill_adhesion.lua
Normal file
60
tools/backfill_adhesion.lua
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
-- One-off migration: rewrite rooms/*.sav from the old baked-in adhesion classes
|
||||||
|
-- (spiky_/skewer_/sticky_<box>) to the new model of a plain base class carrying
|
||||||
|
-- an `adhesion` property. spiky and old-sticky both become the new soft "sticky";
|
||||||
|
-- skewer becomes the rigid "gluey". Run once from the project root: lua tools/backfill_adhesion.lua
|
||||||
|
loadstring = loadstring or load -- TSerial.unpack still calls loadstring (removed in Lua 5.4+)
|
||||||
|
package.path = "./?.lua;" .. package.path
|
||||||
|
require "libs/TSerial"
|
||||||
|
|
||||||
|
local dir = "rooms"
|
||||||
|
local adhesionForPrefix = { spiky = "sticky", skewer = "gluey", sticky = "sticky" }
|
||||||
|
|
||||||
|
local function savFiles()
|
||||||
|
local files = {}
|
||||||
|
local pipe = io.popen('ls "' .. dir .. '"')
|
||||||
|
for name in pipe:lines() do
|
||||||
|
if name:match("%.sav$") then table.insert(files, name) end
|
||||||
|
end
|
||||||
|
pipe:close()
|
||||||
|
return files
|
||||||
|
end
|
||||||
|
|
||||||
|
local function readFile(path)
|
||||||
|
local handle = assert(io.open(path, "r"))
|
||||||
|
local contents = handle:read("*a")
|
||||||
|
handle:close()
|
||||||
|
return contents
|
||||||
|
end
|
||||||
|
|
||||||
|
local function writeFile(path, contents)
|
||||||
|
local handle = assert(io.open(path, "w"))
|
||||||
|
handle:write(contents)
|
||||||
|
handle:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
local totalFiles, totalObjects = 0, 0
|
||||||
|
for _, file in ipairs(savFiles()) do
|
||||||
|
local path = dir .. "/" .. file
|
||||||
|
local saveTable = TSerial.unpack(readFile(path))
|
||||||
|
local migrated = 0
|
||||||
|
for _, object in ipairs((saveTable and saveTable.objects) or {}) do
|
||||||
|
if type(object.class) == "string" then
|
||||||
|
for prefix, adhesion in pairs(adhesionForPrefix) do
|
||||||
|
local base = object.class:match("^" .. prefix .. "_(.+)")
|
||||||
|
if base then
|
||||||
|
object.class = base
|
||||||
|
if not object.adhesion then object.adhesion = adhesion end
|
||||||
|
migrated = migrated + 1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if migrated > 0 then
|
||||||
|
writeFile(path, TSerial.pack(saveTable, nil, true))
|
||||||
|
totalFiles = totalFiles + 1
|
||||||
|
totalObjects = totalObjects + migrated
|
||||||
|
print(string.format("%-28s migrated %d object(s)", file, migrated))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
print(string.format("done: %d object(s) across %d file(s)", totalObjects, totalFiles))
|
||||||
16
world.lua
16
world.lua
|
|
@ -637,8 +637,7 @@ function World:movementContext(room, color)
|
||||||
function proxy:canMove() return self.entity:canMove() end
|
function proxy:canMove() return self.entity:canMove() end
|
||||||
function proxy:canPassOver() return self.entity:canPassOver() end
|
function proxy:canPassOver() return self.entity:canPassOver() end
|
||||||
function proxy:isSticky() return self.entity:isSticky() end
|
function proxy:isSticky() return self.entity:isSticky() end
|
||||||
function proxy:isSkewer() return self.entity:isSkewer() end
|
function proxy:isGluey() return self.entity:isGluey() end
|
||||||
function proxy:isSpiky() return self.entity:isSpiky() end
|
|
||||||
function proxy:bump() return self.entity:bump() end
|
function proxy:bump() return self.entity:bump() end
|
||||||
function proxy:getOccupiedCells(offset)
|
function proxy:getOccupiedCells(offset)
|
||||||
offset = offset or {x = 0, y = 0}
|
offset = offset or {x = 0, y = 0}
|
||||||
|
|
@ -1110,14 +1109,13 @@ function World:drawRoomPreview(name, x, y, size, roomData)
|
||||||
if path then
|
if path then
|
||||||
love.graphics.setColor(gColor[color]:set())
|
love.graphics.setColor(gColor[color]:set())
|
||||||
local sprite = loadSprite(path)
|
local sprite = loadSprite(path)
|
||||||
if props.goopy and drawGoopySprite then
|
local dx, dy = x + (object.x - 1) * 16 * scale, y + (object.y - 1) * 16 * scale
|
||||||
drawGoopySprite(sprite, x + (object.x - 1) * 16 * scale, y + (object.y - 1) * 16 * scale, scale)
|
if object.adhesion == "gluey" and drawGlueySprite then
|
||||||
elseif props.skewer and drawSkewerSprite then
|
drawGlueySprite(sprite, props.cells and props.cells[color], dx, dy, scale)
|
||||||
drawSkewerSprite(sprite, props.cells and props.cells[color], x + (object.x - 1) * 16 * scale, y + (object.y - 1) * 16 * scale, scale)
|
elseif object.adhesion == "sticky" and drawGoopySprite then
|
||||||
elseif props.spiky and drawSpikySprite then
|
drawGoopySprite(sprite, dx, dy, scale)
|
||||||
drawSpikySprite(sprite, props.cells and props.cells[color], x + (object.x - 1) * 16 * scale, y + (object.y - 1) * 16 * scale, scale)
|
|
||||||
else
|
else
|
||||||
love.graphics.draw(sprite, x + (object.x - 1) * 16 * scale, y + (object.y - 1) * 16 * scale, 0, scale, scale)
|
love.graphics.draw(sprite, dx, dy, 0, scale, scale)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue