Compare commits
No commits in common. "0618cfc51db30120e1c2dfa7a36d1c1901ba0651" and "e51dc22ca1bfeda1f1082d76588dbfca376ef514" have entirely different histories.
0618cfc51d
...
e51dc22ca1
38 changed files with 1046 additions and 11485 deletions
|
|
@ -4,13 +4,12 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
||||||
|
|
||||||
## What this is
|
## What this is
|
||||||
|
|
||||||
CHROMA SOLSTICE — a grid-based color-puzzle game written in Lua for the [LÖVE](https://love2d.org) framework. There is no build step or package manifest; the whole game is the tree of `.lua` files loaded by `main.lua`.
|
CHROMA SOLSTICE — a grid-based color-puzzle game written in Lua for the [LÖVE](https://love2d.org) framework. There is no build step, no test suite, and no package manifest; the whole game is the tree of `.lua` files loaded by `main.lua`.
|
||||||
|
|
||||||
## Running
|
## Running
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
love . # run from the project root
|
love . # run from the project root
|
||||||
lua tests/test_movement.lua # run the pure matrix-movement tests
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`love` is not installed in this environment — you cannot run the game here; reason about the code statically.
|
`love` is not installed in this environment — you cannot run the game here; reason about the code statically.
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 KiB |
|
|
@ -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
|
|
||||||
15
entity.lua
15
entity.lua
|
|
@ -14,9 +14,6 @@ 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
|
|
||||||
self.skewer = t.skewer or false
|
|
||||||
self.spiky = t.spiky or false
|
|
||||||
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
|
||||||
|
|
@ -137,18 +134,6 @@ function Entity:canMove()
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function Entity:isSticky()
|
|
||||||
return self.sticky
|
|
||||||
end
|
|
||||||
|
|
||||||
function Entity:isSkewer()
|
|
||||||
return self.skewer
|
|
||||||
end
|
|
||||||
|
|
||||||
function Entity:isSpiky()
|
|
||||||
return self.spiky
|
|
||||||
end
|
|
||||||
|
|
||||||
function Entity:canPassOver()
|
function Entity:canPassOver()
|
||||||
if self:getCollision() == "none" then
|
if self:getCollision() == "none" then
|
||||||
return true
|
return true
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -3,7 +3,7 @@ require "level_editor/gui"
|
||||||
require "level_editor/layout"
|
require "level_editor/layout"
|
||||||
|
|
||||||
editorView = "room" --can be "off", "room", "world", "asset"
|
editorView = "room" --can be "off", "room", "world", "asset"
|
||||||
selectedColor = "white"
|
selectedColor = "red"
|
||||||
|
|
||||||
local bacSprite = love.graphics.newImage("art/_ui/bac.png")
|
local bacSprite = love.graphics.newImage("art/_ui/bac.png")
|
||||||
local folderSprite = love.graphics.newImage("art/_ui/folder.png")
|
local folderSprite = love.graphics.newImage("art/_ui/folder.png")
|
||||||
|
|
@ -34,6 +34,8 @@ local roomNameField = nil
|
||||||
local editorStatus = nil
|
local editorStatus = nil
|
||||||
local clearSaveConfirmationExpires = 0
|
local clearSaveConfirmationExpires = 0
|
||||||
local clearSaveConfirmationScope = nil
|
local clearSaveConfirmationScope = nil
|
||||||
|
local worldSeedConfirmationExpires = 0
|
||||||
|
local worldMode = "place"
|
||||||
local worldRoomButtons = {}
|
local worldRoomButtons = {}
|
||||||
local worldControls = {}
|
local worldControls = {}
|
||||||
local worldPage = 1
|
local worldPage = 1
|
||||||
|
|
@ -42,61 +44,6 @@ local assetSearchText = ""
|
||||||
local assetSearchButtons = {}
|
local assetSearchButtons = {}
|
||||||
local worldSearchText = ""
|
local worldSearchText = ""
|
||||||
local worldSearchBox = { x = 10, y = 34, w = 0, h = 28 }
|
local worldSearchBox = { x = 10, y = 34, w = 0, h = 28 }
|
||||||
local cloneSourceRoom = nil
|
|
||||||
local roomBrush = nil
|
|
||||||
local boxPaletteOpen = false
|
|
||||||
local boxPaletteScroll = { scroll = 0 }
|
|
||||||
local boxPaletteSections = {}
|
|
||||||
|
|
||||||
-- Convert a pointer position into a valid room cell. Keeping brush state in
|
|
||||||
-- cell coordinates means a click that jitters within a tile cannot place or
|
|
||||||
-- erase that tile more than once.
|
|
||||||
local function roomCellAt(x, y)
|
|
||||||
if x < 0 or x >= width or y < 0 or y >= height then return nil end
|
|
||||||
local cell = currentRoom:getCellFromMousePos(x, y)
|
|
||||||
cell.x = constrain(cell.x, 1, gridWidth)
|
|
||||||
cell.y = constrain(cell.y, 1, gridHeight)
|
|
||||||
return cell
|
|
||||||
end
|
|
||||||
|
|
||||||
local function paintRoomCell(cell, button)
|
|
||||||
-- Use the centre of the cell so Room's existing pointer-based edit helpers
|
|
||||||
-- perform the same operation as a normal click.
|
|
||||||
local x = (cell.x - .5) * width / gridWidth
|
|
||||||
local y = (cell.y - .5) * height / gridHeight
|
|
||||||
if button == 1 and selectedAsset then
|
|
||||||
currentRoom:attemptPlace(selectedAsset, gameAssets[selectedAsset], x, y, selectedColor)
|
|
||||||
elseif button == 2 then
|
|
||||||
currentRoom:attemptDelete(x, y, selectedColor)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function paintRoomLine(from, to, button)
|
|
||||||
-- Bresenham keeps a fast mouse movement from leaving holes between the
|
|
||||||
-- cells that happened to receive mouse-move events.
|
|
||||||
local x, y = from.x, from.y
|
|
||||||
local dx, dy = math.abs(to.x - x), math.abs(to.y - y)
|
|
||||||
local stepX = x < to.x and 1 or -1
|
|
||||||
local stepY = y < to.y and 1 or -1
|
|
||||||
local err = dx - dy
|
|
||||||
|
|
||||||
while true do
|
|
||||||
if x ~= from.x or y ~= from.y then paintRoomCell({ x = x, y = y }, button) end
|
|
||||||
if x == to.x and y == to.y then break end
|
|
||||||
local twiceErr = err * 2
|
|
||||||
if twiceErr > -dy then err = err - dy; x = x + stepX end
|
|
||||||
if twiceErr < dx then err = err + dx; y = y + stepY end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function beginRoomBrush(x, y, button)
|
|
||||||
if button ~= 1 and button ~= 2 then return end
|
|
||||||
if button == 1 and not selectedAsset then return end
|
|
||||||
local cell = roomCellAt(x, y)
|
|
||||||
if not cell then return end
|
|
||||||
paintRoomCell(cell, button)
|
|
||||||
roomBrush = { button = button, lastCell = cell }
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ── Room-view sidebar layout ────────────────────────────────────────────────
|
-- ── Room-view sidebar layout ────────────────────────────────────────────────
|
||||||
-- Declared up here (not next to editorDraw) so editorMouseHandler, defined
|
-- Declared up here (not next to editorDraw) so editorMouseHandler, defined
|
||||||
|
|
@ -109,120 +56,6 @@ local function currentRoomList()
|
||||||
else return assetListButtons end
|
else return assetListButtons end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function rebuildBoxPaletteSections()
|
|
||||||
boxPaletteSections = {}
|
|
||||||
for _, prefix in ipairs({ "box", "spiky", "sticky", "skewer" }) do
|
|
||||||
local names = {}
|
|
||||||
local pattern = prefix == "box" and "^box" or "^" .. prefix .. "_box"
|
|
||||||
for name in pairs(gameAssets) do
|
|
||||||
if name:match(pattern) then table.insert(names, name) end
|
|
||||||
end
|
|
||||||
table.sort(names)
|
|
||||||
table.insert(boxPaletteSections, { prefix = prefix, names = names })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function buildBoxPicker(rect)
|
|
||||||
local gap, cellSize, headerH = 4, 51, 18
|
|
||||||
local function contentMetrics(size)
|
|
||||||
local columns = math.max(1, math.floor((rect.w + gap) / (size + gap)))
|
|
||||||
local height = 0
|
|
||||||
for _, section in ipairs(boxPaletteSections) do
|
|
||||||
height = height + headerH + math.ceil(#section.names / columns) * (size + gap) + gap
|
|
||||||
end
|
|
||||||
return columns, height
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Keep the picker to one screen when the sidebar is tall enough, while
|
|
||||||
-- preserving a usable minimum tile size for unusually short sidebars.
|
|
||||||
local columns, contentHeight = contentMetrics(cellSize)
|
|
||||||
while cellSize > 20 and contentHeight > rect.h do
|
|
||||||
cellSize = cellSize - 1
|
|
||||||
columns, contentHeight = contentMetrics(cellSize)
|
|
||||||
end
|
|
||||||
local contentY, sections, items = 0, {}, {}
|
|
||||||
|
|
||||||
for _, section in ipairs(boxPaletteSections) do
|
|
||||||
table.insert(sections, { prefix = section.prefix, y = contentY })
|
|
||||||
contentY = contentY + headerH
|
|
||||||
for index, name in ipairs(section.names) do
|
|
||||||
local column = (index - 1) % columns
|
|
||||||
local row = math.floor((index - 1) / columns)
|
|
||||||
table.insert(items, {
|
|
||||||
name = name,
|
|
||||||
x = rect.x + column * (cellSize + gap),
|
|
||||||
y = contentY + row * (cellSize + gap),
|
|
||||||
w = cellSize,
|
|
||||||
h = cellSize,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
contentY = contentY + math.ceil(#section.names / columns) * (cellSize + gap) + gap
|
|
||||||
end
|
|
||||||
|
|
||||||
local maxScroll = math.max(0, contentY - rect.h)
|
|
||||||
boxPaletteScroll.scroll = math.max(0, math.min(boxPaletteScroll.scroll or 0, maxScroll))
|
|
||||||
local picker = { rect = rect, sections = sections, items = items, maxScroll = maxScroll, scroll = boxPaletteScroll.scroll }
|
|
||||||
function picker.at(x, y)
|
|
||||||
if not Layout.hit(rect, x, y) then return nil end
|
|
||||||
local contentYAtMouse = y - rect.y + picker.scroll
|
|
||||||
for _, item in ipairs(items) do
|
|
||||||
if x >= item.x and x < item.x + item.w and contentYAtMouse >= item.y and contentYAtMouse < item.y + item.h then
|
|
||||||
return item.name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return picker
|
|
||||||
end
|
|
||||||
|
|
||||||
local function drawPaletteAsset(name, x, y, size)
|
|
||||||
local props = gameAssets[name]
|
|
||||||
if not props or not props.sprites then return end
|
|
||||||
local maxW, maxH = 1, 1
|
|
||||||
for _, path in pairs(props.sprites) do
|
|
||||||
local sprite = loadSprite(path)
|
|
||||||
maxW, maxH = math.max(maxW, sprite:getWidth()), math.max(maxH, sprite:getHeight())
|
|
||||||
end
|
|
||||||
local scale = math.min((size - 6) / maxW, (size - 6) / maxH)
|
|
||||||
local drawX = x + (size - maxW * scale) / 2
|
|
||||||
local drawY = y + (size - maxH * scale) / 2
|
|
||||||
for color, path in pairs(props.sprites) do
|
|
||||||
local sprite = loadSprite(path)
|
|
||||||
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)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function drawBoxPicker(picker)
|
|
||||||
Layout.clip(picker.rect)
|
|
||||||
for _, section in ipairs(picker.sections) do
|
|
||||||
love.graphics.setColor(gColor.white:set())
|
|
||||||
love.graphics.print(section.prefix, picker.rect.x, picker.rect.y + section.y - picker.scroll)
|
|
||||||
end
|
|
||||||
for _, item in ipairs(picker.items) do
|
|
||||||
local y = picker.rect.y + item.y - picker.scroll
|
|
||||||
if y + item.h > picker.rect.y and y < picker.rect.y + picker.rect.h then
|
|
||||||
love.graphics.setColor(1, 1, 1, 0.45)
|
|
||||||
love.graphics.rectangle("line", item.x, y, item.w, item.h)
|
|
||||||
drawPaletteAsset(item.name, item.x, y, item.w)
|
|
||||||
if item.name == selectedAsset then
|
|
||||||
love.graphics.setColor(1, 0.85, 0.2, 1)
|
|
||||||
love.graphics.setLineWidth(2)
|
|
||||||
love.graphics.rectangle("line", item.x + 1, y + 1, item.w - 2, item.h - 2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Layout.unclip()
|
|
||||||
Layout.scrollbar(picker)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Carve the sidebar into fixed, non-overlapping "divs": a search header pinned
|
-- Carve the sidebar into fixed, non-overlapping "divs": a search header pinned
|
||||||
-- to the top, a control cluster pinned to the bottom, a status strip above it,
|
-- to the top, a control cluster pinned to the bottom, a status strip above it,
|
||||||
-- and a flexible list filling whatever is left. Recomputed every frame so it
|
-- and a flexible list filling whatever is left. Recomputed every frame so it
|
||||||
|
|
@ -230,7 +63,7 @@ end
|
||||||
local function roomSections()
|
local function roomSections()
|
||||||
local pad = 10
|
local pad = 10
|
||||||
local w = math.max(0, sb.w - pad * 2)
|
local w = math.max(0, sb.w - pad * 2)
|
||||||
local controlsH = 130
|
local controlsH = 104
|
||||||
local statusH = 44
|
local statusH = 44
|
||||||
local search = { x = pad, y = pad, w = w, h = 34 }
|
local search = { x = pad, y = pad, w = w, h = 34 }
|
||||||
local controls = { x = pad, y = sb.h - pad - controlsH, w = w, h = controlsH }
|
local controls = { x = pad, y = sb.h - pad - controlsH, w = w, h = controlsH }
|
||||||
|
|
@ -263,13 +96,6 @@ local function layoutRoomControls(c, s)
|
||||||
b.scale = target / b.sprite:getHeight()
|
b.scale = target / b.sprite:getHeight()
|
||||||
b.w = b.sprite:getWidth() * b.scale
|
b.w = b.sprite:getWidth() * b.scale
|
||||||
b.h = b.sprite:getHeight() * b.scale
|
b.h = b.sprite:getHeight() * b.scale
|
||||||
elseif b and b.sprites then
|
|
||||||
local spriteW, spriteH = 1, 1
|
|
||||||
for _, sprite in pairs(b.sprites) do
|
|
||||||
spriteW, spriteH = math.max(spriteW, sprite:getWidth()), math.max(spriteH, sprite:getHeight())
|
|
||||||
end
|
|
||||||
b.scale = math.min(target / spriteW, target / spriteH)
|
|
||||||
b.w, b.h = spriteW * b.scale, spriteH * b.scale
|
|
||||||
end
|
end
|
||||||
return b
|
return b
|
||||||
end
|
end
|
||||||
|
|
@ -293,7 +119,6 @@ local function layoutRoomControls(c, s)
|
||||||
end
|
end
|
||||||
|
|
||||||
placeRow({ "white", "red", "green", "blue" }, 22)
|
placeRow({ "white", "red", "green", "blue" }, 22)
|
||||||
placeRow({ "quick_player", "quick_static_wall", "quick_switch", "quick_door", "quick_box" }, 22)
|
|
||||||
placeRow({ "save", "clearSaves", "world" }, 28)
|
placeRow({ "save", "clearSaves", "world" }, 28)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -302,7 +127,7 @@ end
|
||||||
local function buildRoomLayout()
|
local function buildRoomLayout()
|
||||||
local s = roomSections()
|
local s = roomSections()
|
||||||
|
|
||||||
local showBack = assetSearchText ~= "" or selectedFolder or boxPaletteOpen
|
local showBack = assetSearchText ~= "" or selectedFolder
|
||||||
local sx, sw = s.search.x, s.search.w
|
local sx, sw = s.search.x, s.search.w
|
||||||
if showBack then
|
if showBack then
|
||||||
s.back = { x = s.search.x, y = s.search.y, w = s.search.h, h = s.search.h }
|
s.back = { x = s.search.x, y = s.search.y, w = s.search.h, h = s.search.h }
|
||||||
|
|
@ -311,12 +136,8 @@ local function buildRoomLayout()
|
||||||
end
|
end
|
||||||
s.searchBox = { x = sx, y = s.search.y, w = sw, h = s.search.h }
|
s.searchBox = { x = sx, y = s.search.y, w = sw, h = s.search.h }
|
||||||
|
|
||||||
if boxPaletteOpen then
|
|
||||||
s.boxPicker = buildBoxPicker(s.listRect)
|
|
||||||
else
|
|
||||||
s.items = currentRoomList()
|
s.items = currentRoomList()
|
||||||
s.list = Layout.list(listScroll, s.listRect, 40, #s.items, 2)
|
s.list = Layout.list(listScroll, s.listRect, 40, #s.items, 2)
|
||||||
end
|
|
||||||
|
|
||||||
layoutRoomControls(s.controls, s)
|
layoutRoomControls(s.controls, s)
|
||||||
return s
|
return s
|
||||||
|
|
@ -324,8 +145,7 @@ end
|
||||||
|
|
||||||
-- Mouse wheel scrolls the room list; clamped by Layout.list next frame.
|
-- Mouse wheel scrolls the room list; clamped by Layout.list next frame.
|
||||||
function editorScrollRoomList(dy)
|
function editorScrollRoomList(dy)
|
||||||
local scroll = boxPaletteOpen and boxPaletteScroll or listScroll
|
listScroll.scroll = math.max(0, (listScroll.scroll or 0) - dy * 40)
|
||||||
scroll.scroll = math.max(0, (scroll.scroll or 0) - dy * 40)
|
|
||||||
end
|
end
|
||||||
-- ────────────────────────────────────────────────────────────────────────────
|
-- ────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -362,10 +182,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,
|
||||||
|
|
@ -394,6 +210,21 @@ local function rebuildWorldRoomButtons()
|
||||||
table.insert(worldRoomButtons, { name = roomName, x = 10, y = 70 + row * 68, w = sb.w - 20, h = 60 })
|
table.insert(worldRoomButtons, { name = roomName, x = 10, y = 70 + row * 68, w = sb.w - 20, h = 60 })
|
||||||
end
|
end
|
||||||
worldControls = {
|
worldControls = {
|
||||||
|
{ text = worldMode == "place" and "MODE: PLACE ROOMS" or "MODE: OPEN LEVELS", x = 10, y = sb.h - 194, w = sb.w - 20, h = 34,
|
||||||
|
onClic = function() worldMode = worldMode == "place" and "jump" or "place"; rebuildWorldRoomButtons() end },
|
||||||
|
{ text = "RESEED OLD EXITS", x = 10, y = sb.h - 152, w = sb.w - 20, h = 34,
|
||||||
|
onClic = function()
|
||||||
|
local now = love.timer.getTime()
|
||||||
|
if now > worldSeedConfirmationExpires then
|
||||||
|
worldSeedConfirmationExpires = now + 3
|
||||||
|
editorStatus = "Click RESEED OLD EXITS again within 3 seconds to replace the current map."
|
||||||
|
return
|
||||||
|
end
|
||||||
|
worldSeedConfirmationExpires = 0
|
||||||
|
local result = seedWorldFromLegacyExits(gameWorld)
|
||||||
|
editorStatus = "Seeded " .. result.placed .. " rooms; " .. result.links .. " legacy links (" .. result.conflicts .. " conflicts kept separate)."
|
||||||
|
rebuildWorldRoomButtons()
|
||||||
|
end },
|
||||||
{ text = "< PAGE " .. worldPage .. "/" .. pages .. " >", x = 10, y = sb.h - 110, w = sb.w - 20, h = 34,
|
{ text = "< PAGE " .. worldPage .. "/" .. pages .. " >", x = 10, y = sb.h - 110, w = sb.w - 20, h = 34,
|
||||||
onClic = function(buttonX)
|
onClic = function(buttonX)
|
||||||
if buttonX < sb.w / 2 then worldPage = math.max(1, worldPage - 1) else worldPage = math.min(pages, worldPage + 1) end
|
if buttonX < sb.w / 2 then worldPage = math.max(1, worldPage - 1) else worldPage = math.min(pages, worldPage + 1) end
|
||||||
|
|
@ -404,20 +235,10 @@ end
|
||||||
|
|
||||||
local function drawWorldSidebar()
|
local function drawWorldSidebar()
|
||||||
love.graphics.setColor(gColor.white:set())
|
love.graphics.setColor(gColor.white:set())
|
||||||
|
love.graphics.printf("ROOMS — " .. (worldMode == "place" and "select to place; re-click to clear" or "click a map room to open"), 8, 6, sb.w - 16, "center")
|
||||||
worldSearchBox.w = sb.w - 20
|
worldSearchBox.w = sb.w - 20
|
||||||
if activeField == "cloneName" then
|
|
||||||
love.graphics.printf("CLONE '" .. cloneSourceRoom .. "' AS:", 8, 6, sb.w - 16, "center")
|
|
||||||
love.graphics.rectangle("line", worldSearchBox.x, worldSearchBox.y, worldSearchBox.w, worldSearchBox.h)
|
|
||||||
love.graphics.printf(inputText, worldSearchBox.x + 6, worldSearchBox.y + 7, sb.w - 32, "left")
|
|
||||||
love.graphics.printf("Press Enter to create and select the clone. Esc cancels.", 8, worldSearchBox.y + 34, sb.w - 16, "center")
|
|
||||||
else
|
|
||||||
local instructions = selectedRoom
|
|
||||||
and "placing '" .. selectedRoom .. "' — click a map cell"
|
|
||||||
or "click a map cell to open or create; select a room below to place it"
|
|
||||||
love.graphics.printf("ROOMS — " .. instructions, 8, 6, sb.w - 16, "center")
|
|
||||||
love.graphics.rectangle("line", worldSearchBox.x, worldSearchBox.y, worldSearchBox.w, worldSearchBox.h)
|
love.graphics.rectangle("line", worldSearchBox.x, worldSearchBox.y, worldSearchBox.w, worldSearchBox.h)
|
||||||
love.graphics.printf("SEARCH: " .. worldSearchText, worldSearchBox.x + 6, worldSearchBox.y + 7, worldSearchBox.w - 12, "left")
|
love.graphics.printf("SEARCH: " .. worldSearchText, worldSearchBox.x + 6, worldSearchBox.y + 7, worldSearchBox.w - 12, "left")
|
||||||
end
|
|
||||||
for _, button in ipairs(worldRoomButtons) do
|
for _, button in ipairs(worldRoomButtons) do
|
||||||
love.graphics.setColor(button.name == selectedRoom and 1 or 100 / 255, button.name == selectedRoom and 1 or 100 / 255, button.name == selectedRoom and 1 or 100 / 255, 1)
|
love.graphics.setColor(button.name == selectedRoom and 1 or 100 / 255, button.name == selectedRoom and 1 or 100 / 255, button.name == selectedRoom and 1 or 100 / 255, 1)
|
||||||
love.graphics.rectangle("line", button.x, button.y, button.w, button.h)
|
love.graphics.rectangle("line", button.x, button.y, button.w, button.h)
|
||||||
|
|
@ -469,8 +290,8 @@ local function saveRoomFromEditor()
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local savePlayerPositions = not love.keyboard.isDown("lshift", "rshift")
|
local savePlayerPositions = love.keyboard.isDown("lshift", "rshift")
|
||||||
if not currentRoom:saveToSource(name, savePlayerPositions) then
|
if not currentRoom:saveToSource(name, savePlayerPositions, oldName) then
|
||||||
editorStatus = "Could not save '" .. name .. "'."
|
editorStatus = "Could not save '" .. name .. "'."
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
@ -481,18 +302,13 @@ local function saveRoomFromEditor()
|
||||||
removeFromSource("rooms/" .. oldName .. ".sav")
|
removeFromSource("rooms/" .. oldName .. ".sav")
|
||||||
gameWorld:renameRoom(oldName, name)
|
gameWorld:renameRoom(oldName, name)
|
||||||
end
|
end
|
||||||
-- The project source is now authoritative. Drop any runtime copy so it
|
|
||||||
-- cannot shadow this editor save the next time the game starts.
|
|
||||||
clearRuntimeSaveData(name)
|
|
||||||
if oldName and oldName ~= name then clearRuntimeSaveData(oldName) end
|
|
||||||
suppressRuntimeAutosave = true
|
|
||||||
gameWorld:invalidatePreview(name)
|
gameWorld:invalidatePreview(name)
|
||||||
if editorView == "world" then rebuildWorldRoomButtons() end
|
if editorView == "world" then rebuildWorldRoomButtons() end
|
||||||
currentRoom.name = name
|
currentRoom.name = name
|
||||||
editorRoomName = name
|
editorRoomName = name
|
||||||
roomNameField:editText(name)
|
roomNameField:editText(name)
|
||||||
activeField = nil
|
activeField = nil
|
||||||
editorStatus = "Saved source level '" .. name .. "'" .. (savePlayerPositions and " with player positions." or " without player positions.")
|
editorStatus = "Saved source level '" .. name .. "'" .. (savePlayerPositions and " with player positions." or ".")
|
||||||
end
|
end
|
||||||
|
|
||||||
local function clearRuntimeSaves()
|
local function clearRuntimeSaves()
|
||||||
|
|
@ -512,7 +328,6 @@ local function clearRuntimeSaves()
|
||||||
end
|
end
|
||||||
|
|
||||||
local removed = clearRuntimeSaveData(clearAll and nil or roomName)
|
local removed = clearRuntimeSaveData(clearAll and nil or roomName)
|
||||||
suppressRuntimeAutosave = true
|
|
||||||
clearSaveConfirmationExpires = 0
|
clearSaveConfirmationExpires = 0
|
||||||
clearSaveConfirmationScope = nil
|
clearSaveConfirmationScope = nil
|
||||||
if roomName then
|
if roomName then
|
||||||
|
|
@ -527,7 +342,6 @@ end
|
||||||
|
|
||||||
function _initLevelEditor()
|
function _initLevelEditor()
|
||||||
gameAssets = getAllAssets()
|
gameAssets = getAllAssets()
|
||||||
rebuildBoxPaletteSections()
|
|
||||||
editorRoomName = ""
|
editorRoomName = ""
|
||||||
|
|
||||||
--create folder buttons
|
--create folder buttons
|
||||||
|
|
@ -626,25 +440,6 @@ function _initLevelEditor()
|
||||||
}, color)
|
}, color)
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, asset in ipairs({ "player", "static_wall", "switch", "door", "box" }) do
|
|
||||||
local properties = gameAssets[asset]
|
|
||||||
if properties then
|
|
||||||
local sprites = {}
|
|
||||||
for color, path in pairs(properties.sprites) do sprites[color] = love.graphics.newImage(path) end
|
|
||||||
_addUIButton(createUIElement{
|
|
||||||
name = "quick_" .. asset,
|
|
||||||
sprites = sprites,
|
|
||||||
goopy = properties.goopy,
|
|
||||||
skewer = properties.skewer,
|
|
||||||
spiky = properties.spiky,
|
|
||||||
cells = properties.cells,
|
|
||||||
onClic = { left = function() selectQuickAsset(asset) end },
|
|
||||||
x = 0,
|
|
||||||
y = 0,
|
|
||||||
}, "quick_" .. asset)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
roomNameField = createUIElement{
|
roomNameField = createUIElement{
|
||||||
name = "roomName",
|
name = "roomName",
|
||||||
text = editorRoomName,
|
text = editorRoomName,
|
||||||
|
|
@ -669,7 +464,6 @@ end
|
||||||
|
|
||||||
function selectFolder(folder)
|
function selectFolder(folder)
|
||||||
selectedFolder = folder
|
selectedFolder = folder
|
||||||
boxPaletteOpen = false
|
|
||||||
listScroll.scroll = 0
|
listScroll.scroll = 0
|
||||||
assetListButtons = {}
|
assetListButtons = {}
|
||||||
|
|
||||||
|
|
@ -684,19 +478,13 @@ 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,
|
||||||
y = 0,
|
y = 0,
|
||||||
onClic = {
|
onClic = {
|
||||||
left = function() selectAsset(asset) end,
|
left = function() selectAsset(asset) end,
|
||||||
right = function()
|
right = function() editAsset(asset) end,
|
||||||
if not gameAssets[asset].synthetic then editAsset(asset) end
|
|
||||||
end,
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
@ -708,12 +496,6 @@ function selectAsset(asset)
|
||||||
print(selectedAsset)
|
print(selectedAsset)
|
||||||
end
|
end
|
||||||
|
|
||||||
function selectQuickAsset(asset)
|
|
||||||
selectAsset(asset)
|
|
||||||
boxPaletteOpen = asset == "box"
|
|
||||||
boxPaletteScroll.scroll = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function editAsset(asset)
|
function editAsset(asset)
|
||||||
inputText = ""
|
inputText = ""
|
||||||
editorView = "asset"
|
editorView = "asset"
|
||||||
|
|
@ -759,10 +541,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,
|
||||||
}
|
}
|
||||||
|
|
@ -841,8 +619,7 @@ function worldView()
|
||||||
editorView = "world"
|
editorView = "world"
|
||||||
activeField = nil
|
activeField = nil
|
||||||
selectedFolder = nil
|
selectedFolder = nil
|
||||||
selectedRoom = nil
|
worldMode = "place"
|
||||||
cloneSourceRoom = nil
|
|
||||||
worldPage = 1
|
worldPage = 1
|
||||||
rebuildWorldRoomButtons()
|
rebuildWorldRoomButtons()
|
||||||
end
|
end
|
||||||
|
|
@ -857,36 +634,6 @@ local function newWorldRoomName(cellX, cellY)
|
||||||
return name
|
return name
|
||||||
end
|
end
|
||||||
|
|
||||||
local function cloneWorldRoom(sourceName, cloneName)
|
|
||||||
local source = readFromSource("rooms/" .. sourceName .. ".sav")
|
|
||||||
if not source then
|
|
||||||
editorStatus = "Could not find source level '" .. sourceName .. "'."
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
if readFromSource("rooms/" .. cloneName .. ".sav") then
|
|
||||||
editorStatus = "A level named '" .. cloneName .. "' already exists."
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
if not writeToSource("rooms/" .. cloneName .. ".sav", source) then
|
|
||||||
editorStatus = "Could not create clone '" .. cloneName .. "'."
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
gameWorld:invalidatePreview(cloneName)
|
|
||||||
selectedRoom = cloneName
|
|
||||||
rebuildWorldRoomButtons()
|
|
||||||
editorStatus = "Cloned '" .. sourceName .. "' as '" .. cloneName .. "'. Click a map cell to place it."
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
local function beginCloneDialog(sourceName)
|
|
||||||
cloneSourceRoom = sourceName
|
|
||||||
activeField = "cloneName"
|
|
||||||
inputText = ""
|
|
||||||
response = ""
|
|
||||||
editorStatus = "Enter a name for the clone."
|
|
||||||
end
|
|
||||||
|
|
||||||
local function openWorldRoom(cellX, cellY)
|
local function openWorldRoom(cellX, cellY)
|
||||||
local name = gameWorld:roomAt(cellX, cellY)
|
local name = gameWorld:roomAt(cellX, cellY)
|
||||||
if not name then
|
if not name then
|
||||||
|
|
@ -926,36 +673,24 @@ function editorMouseHandler(x, y, button)
|
||||||
rebuildAssetSearchButtons()
|
rebuildAssetSearchButtons()
|
||||||
elseif selectedFolder then
|
elseif selectedFolder then
|
||||||
folderView()
|
folderView()
|
||||||
elseif boxPaletteOpen then
|
|
||||||
boxPaletteOpen = false
|
|
||||||
boxPaletteScroll.scroll = 0
|
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if Layout.hit(s.searchBox, x, y) then
|
if Layout.hit(s.searchBox, x, y) then
|
||||||
boxPaletteOpen = false
|
|
||||||
beginSearch("assetSearch")
|
beginSearch("assetSearch")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if s.boxPicker then
|
|
||||||
local asset = s.boxPicker.at(x, y)
|
|
||||||
if asset then
|
|
||||||
if button == 1 then selectAsset(asset) end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- scrolling list item
|
-- scrolling list item
|
||||||
local i = s.list.at(x, y)
|
local i = s.list.at(x, y)
|
||||||
if i and s.items[i] then
|
if i and s.items[i] then
|
||||||
s.items[i]:onClic(button)
|
s.items[i]:onClic(button)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
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_box", "save", "clearSaves", "world" }) do
|
for _, name in ipairs({ "roomName", "white", "red", "green", "blue", "save", "clearSaves", "world" }) do
|
||||||
local b = uiButtons[name]
|
local b = uiButtons[name]
|
||||||
if b and b:clicIsInBox(x, y) then
|
if b and b:clicIsInBox(x, y) then
|
||||||
b:onClic(button)
|
b:onClic(button)
|
||||||
|
|
@ -969,20 +704,18 @@ function editorMouseHandler(x, y, button)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif editorView == "world" then
|
elseif editorView == "world" then
|
||||||
if activeField == "cloneName" then return end
|
|
||||||
if buttonContains(worldSearchBox, x, y) then
|
if buttonContains(worldSearchBox, x, y) then
|
||||||
beginSearch("worldSearch")
|
beginSearch("worldSearch")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
for _, roomButton in ipairs(worldRoomButtons) do
|
for _, roomButton in ipairs(worldRoomButtons) do
|
||||||
if buttonContains(roomButton, x, y) then
|
if buttonContains(roomButton, x, y) then
|
||||||
if button ~= 1 then return end
|
|
||||||
if selectedRoom == roomButton.name then
|
if selectedRoom == roomButton.name then
|
||||||
selectedRoom = nil
|
selectedRoom = nil
|
||||||
editorStatus = "Placement cancelled. Click the map to open or create a level."
|
editorStatus = "Room brush cleared. Click the map to open or create a level."
|
||||||
else
|
else
|
||||||
selectedRoom = roomButton.name
|
selectedRoom = roomButton.name
|
||||||
editorStatus = "Selected '" .. selectedRoom .. "'. Click a map cell to place it."
|
editorStatus = "Selected '" .. selectedRoom .. "' for placement. Click it again to clear."
|
||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
@ -997,53 +730,24 @@ function editorMouseHandler(x, y, button)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- click landed on the grid (game area): place the selected asset
|
-- click landed on the grid (game area): place the selected asset
|
||||||
if editorView == "room" then
|
if editorView == "room" and button == 1 and selectedAsset then
|
||||||
beginRoomBrush(x, y, button)
|
currentRoom:attemptPlace(selectedAsset, gameAssets[selectedAsset], x, y, selectedColor)
|
||||||
elseif editorView == "world" then
|
elseif editorView == "world" then
|
||||||
local cellX, cellY = gameWorld:getCellFromMousePos(x, y)
|
local cellX, cellY = gameWorld:getCellFromMousePos(x, y)
|
||||||
if not gameWorld:isInBounds(cellX, cellY) then return end
|
if not gameWorld:isInBounds(cellX, cellY) then return end
|
||||||
if button == 2 then
|
if button == 2 then
|
||||||
local name = gameWorld:roomAt(cellX, cellY)
|
local removed, name = gameWorld:removeRoom(cellX, cellY)
|
||||||
if name then beginCloneDialog(name) end
|
if removed then editorStatus = "Removed '" .. name .. "' from the world." end
|
||||||
elseif selectedRoom then
|
elseif worldMode == "jump" or not selectedRoom then
|
||||||
local placedRoom = selectedRoom
|
|
||||||
if gameWorld:placeRoom(placedRoom, cellX, cellY) then
|
|
||||||
selectedRoom = nil
|
|
||||||
editorStatus = "Placed '" .. placedRoom .. "' at " .. cellX .. ", " .. cellY .. "."
|
|
||||||
else
|
|
||||||
editorStatus = "Could not place '" .. placedRoom .. "'."
|
|
||||||
end
|
|
||||||
else
|
|
||||||
openWorldRoom(cellX, cellY)
|
openWorldRoom(cellX, cellY)
|
||||||
|
elseif selectedRoom then
|
||||||
|
gameWorld:placeRoom(selectedRoom, cellX, cellY)
|
||||||
|
editorStatus = "Placed '" .. selectedRoom .. "' at " .. cellX .. ", " .. cellY .. "."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function editorMouseMoved(x, y)
|
|
||||||
if not roomBrush then return end
|
|
||||||
if editorView ~= "room" then
|
|
||||||
roomBrush = nil
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local cell = roomCellAt(x, y)
|
|
||||||
if not cell then
|
|
||||||
-- Leaving the grid ends this stroke; re-entering should not draw a line
|
|
||||||
-- across the sidebar or outside of the level.
|
|
||||||
roomBrush = nil
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if cell.x == roomBrush.lastCell.x and cell.y == roomBrush.lastCell.y then return end
|
|
||||||
|
|
||||||
paintRoomLine(roomBrush.lastCell, cell, roomBrush.button)
|
|
||||||
roomBrush.lastCell = cell
|
|
||||||
end
|
|
||||||
|
|
||||||
function editorMouseReleased(_, _, button)
|
|
||||||
if roomBrush and roomBrush.button == button then roomBrush = nil end
|
|
||||||
end
|
|
||||||
|
|
||||||
function editorTextHandler(text)
|
function editorTextHandler(text)
|
||||||
if editorView == "asset" and activeField then
|
if editorView == "asset" and activeField then
|
||||||
inputText = text
|
inputText = text
|
||||||
|
|
@ -1060,8 +764,6 @@ function editorTextHandler(text)
|
||||||
worldSearchText = text
|
worldSearchText = text
|
||||||
worldPage = 1
|
worldPage = 1
|
||||||
rebuildWorldRoomButtons()
|
rebuildWorldRoomButtons()
|
||||||
elseif editorView == "world" and activeField == "cloneName" then
|
|
||||||
inputText = text
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -1074,32 +776,10 @@ function editorCompleteResponse()
|
||||||
assetSearchText = inputText
|
assetSearchText = inputText
|
||||||
elseif editorView == "world" and activeField == "worldSearch" then
|
elseif editorView == "world" and activeField == "worldSearch" then
|
||||||
worldSearchText = inputText
|
worldSearchText = inputText
|
||||||
elseif editorView == "world" and activeField == "cloneName" then
|
|
||||||
local cloneName, err = validRoomName(inputText)
|
|
||||||
if not cloneName then
|
|
||||||
editorStatus = err
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local sourceName = cloneSourceRoom
|
|
||||||
if cloneWorldRoom(sourceName, cloneName) then
|
|
||||||
cloneSourceRoom = nil
|
|
||||||
activeField = nil
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
activeField = nil
|
activeField = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function editorCancelResponse()
|
|
||||||
if editorView ~= "world" or activeField ~= "cloneName" then return false end
|
|
||||||
activeField = nil
|
|
||||||
cloneSourceRoom = nil
|
|
||||||
inputText = ""
|
|
||||||
response = ""
|
|
||||||
editorStatus = "Clone cancelled."
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function sortAllAssets()
|
function sortAllAssets()
|
||||||
setAll()
|
setAll()
|
||||||
end
|
end
|
||||||
|
|
@ -1116,51 +796,29 @@ function editorDraw()
|
||||||
-- header: optional back button + search box
|
-- header: optional back button + search box
|
||||||
if s.back then
|
if s.back then
|
||||||
love.graphics.rectangle("line", s.back.x, s.back.y, s.back.w, s.back.h)
|
love.graphics.rectangle("line", s.back.x, s.back.y, s.back.w, s.back.h)
|
||||||
if boxPaletteOpen then
|
|
||||||
love.graphics.printf("X", s.back.x, s.back.y + 9, s.back.w, "center")
|
|
||||||
else
|
|
||||||
local sc = (s.back.h - 8) / bacSprite:getHeight()
|
local sc = (s.back.h - 8) / bacSprite:getHeight()
|
||||||
love.graphics.draw(bacSprite, s.back.x + 4, s.back.y + 4, 0, sc, sc)
|
love.graphics.draw(bacSprite, s.back.x + 4, s.back.y + 4, 0, sc, sc)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
love.graphics.rectangle("line", s.searchBox.x, s.searchBox.y, s.searchBox.w, s.searchBox.h)
|
love.graphics.rectangle("line", s.searchBox.x, s.searchBox.y, s.searchBox.w, s.searchBox.h)
|
||||||
love.graphics.printf(boxPaletteOpen and "BOX VARIANTS" or "SEARCH: " .. assetSearchText, s.searchBox.x + 6, s.searchBox.y + 9, s.searchBox.w - 12, "left")
|
love.graphics.printf("SEARCH: " .. assetSearchText, s.searchBox.x + 6, s.searchBox.y + 9, s.searchBox.w - 12, "left")
|
||||||
|
|
||||||
if s.boxPicker then
|
-- scrolling list, clipped to its section so nothing bleeds into others
|
||||||
drawBoxPicker(s.boxPicker)
|
|
||||||
else
|
|
||||||
-- Scrolling list, clipped to its section so nothing bleeds into others.
|
|
||||||
Layout.clip(s.listRect)
|
Layout.clip(s.listRect)
|
||||||
for i, item in ipairs(s.items) do
|
for i, item in ipairs(s.items) do
|
||||||
if s.list.visible(i) then
|
if s.list.visible(i) then
|
||||||
item.x, item.y = s.list.pos(i)
|
item.x, item.y = s.list.pos(i)
|
||||||
item.w = s.listRect.w
|
item.w = s.listRect.w
|
||||||
item:draw()
|
item:draw()
|
||||||
if item.name == "asset" and item.text == selectedAsset then
|
|
||||||
love.graphics.setColor(1, 0.85, 0.2, 1)
|
|
||||||
love.graphics.setLineWidth(3)
|
|
||||||
love.graphics.rectangle("line", item.x + 1, item.y + 1, item.w - 2, item.h - 2)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Layout.unclip()
|
Layout.unclip()
|
||||||
Layout.scrollbar(s.list)
|
Layout.scrollbar(s.list)
|
||||||
end
|
|
||||||
|
|
||||||
-- control cluster
|
-- control cluster
|
||||||
love.graphics.setColor(gColor.white:set())
|
love.graphics.setColor(gColor.white:set())
|
||||||
love.graphics.printf("LEVEL NAME", s.controls.x, s.labelY, s.controls.w, "center")
|
love.graphics.printf("LEVEL NAME", s.controls.x, s.labelY, s.controls.w, "center")
|
||||||
for _, name in ipairs({ "roomName", "white", "red", "green", "blue", "quick_player", "quick_static_wall", "quick_switch", "quick_door", "quick_box", "save", "clearSaves", "world" }) do
|
for _, name in ipairs({ "roomName", "white", "red", "green", "blue", "save", "clearSaves", "world" }) do
|
||||||
local button = uiButtons[name]
|
if uiButtons[name] then uiButtons[name]:draw() end
|
||||||
if button then
|
|
||||||
button:draw()
|
|
||||||
local asset = name:match("^quick_(.+)$")
|
|
||||||
if asset and selectedAsset == asset then
|
|
||||||
love.graphics.setColor(1, 0.85, 0.2, 1)
|
|
||||||
love.graphics.setLineWidth(2)
|
|
||||||
love.graphics.rectangle("line", button.x - 1, button.y - 1, button.w + 2, button.h + 2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- status strip between the list and the controls
|
-- status strip between the list and the controls
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -59,5 +57,5 @@ function getAllAssets()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return BoxVariants.addTo(globalAssetProperties)
|
return globalAssetProperties
|
||||||
end
|
end
|
||||||
|
|
|
||||||
79
main.lua
79
main.lua
|
|
@ -23,7 +23,6 @@ require "level_editor/editor"
|
||||||
love.filesystem.setRequirePath(love.filesystem.getRequirePath() .. ";libs/narrator/?.lua")
|
love.filesystem.setRequirePath(love.filesystem.getRequirePath() .. ";libs/narrator/?.lua")
|
||||||
narrator = require "narrator.narrator"
|
narrator = require "narrator.narrator"
|
||||||
startSignBook = require "stories.start_sign"
|
startSignBook = require "stories.start_sign"
|
||||||
jorgeBook = require "stories.jorge"
|
|
||||||
|
|
||||||
function love.load()
|
function love.load()
|
||||||
globalAssetProperties = {} -- rebuilt from the filesystem (art/ + .meta) by getAllAssets
|
globalAssetProperties = {} -- rebuilt from the filesystem (art/ + .meta) by getAllAssets
|
||||||
|
|
@ -32,7 +31,6 @@ function love.load()
|
||||||
_initGridVars()
|
_initGridVars()
|
||||||
_initSounds()
|
_initSounds()
|
||||||
_initGlobalColors()
|
_initGlobalColors()
|
||||||
_initAccessibility()
|
|
||||||
gameWorld = World:new()
|
gameWorld = World:new()
|
||||||
_initLevelEditor()
|
_initLevelEditor()
|
||||||
|
|
||||||
|
|
@ -56,9 +54,7 @@ end
|
||||||
-- Autosave on close so a position reached by moving within a room (short of a
|
-- Autosave on close so a position reached by moving within a room (short of a
|
||||||
-- room-to-room transition, which already autosaves in nextRoom) survives.
|
-- room-to-room transition, which already autosaves in nextRoom) survives.
|
||||||
function love.quit()
|
function love.quit()
|
||||||
-- An explicit editor save/clear removes the runtime copy intentionally;
|
if currentRoom then
|
||||||
-- don't immediately recreate it while shutting down.
|
|
||||||
if currentRoom and not suppressRuntimeAutosave then
|
|
||||||
currentRoom:save()
|
currentRoom:save()
|
||||||
if gameWorld then gameWorld:setLastRoom(currentRoom.name) end
|
if gameWorld then gameWorld:setLastRoom(currentRoom.name) end
|
||||||
end
|
end
|
||||||
|
|
@ -120,42 +116,16 @@ function love.draw()
|
||||||
love.graphics.pop()
|
love.graphics.pop()
|
||||||
love.graphics.setCanvas()
|
love.graphics.setCanvas()
|
||||||
|
|
||||||
-- Collect every visible layer before the accessibility pass. Applying the
|
|
||||||
-- shader here also covers the editor sidebar, FPS display, and dialogue,
|
|
||||||
-- rather than only recolouring the room sprites.
|
|
||||||
love.graphics.setCanvas(presentationCanvas)
|
|
||||||
love.graphics.clear()
|
|
||||||
love.graphics.setBlendMode("alpha")
|
love.graphics.setBlendMode("alpha")
|
||||||
love.graphics.setColor(1, 1, 1, 1)
|
love.graphics.setColor(1, 1, 1, 1)
|
||||||
love.graphics.draw(finalCanvas, 0, 0)
|
love.graphics.draw(finalCanvas, 0, 0)
|
||||||
love.graphics.draw(sideBarCanvas, sideBarBox.x, sideBarBox.y)
|
love.graphics.draw(sideBarCanvas, sideBarBox.x, sideBarBox.y)
|
||||||
love.graphics.print("Current FPS: "..tostring(love.timer.getFPS( )), 10, 10)
|
love.graphics.print("Current FPS: "..tostring(love.timer.getFPS( )), 10, 10)
|
||||||
|
|
||||||
Dialogue.draw()
|
Dialogue.draw()
|
||||||
love.graphics.setCanvas()
|
|
||||||
|
|
||||||
love.graphics.setColor(1, 1, 1, 1)
|
|
||||||
local filter = accessibilityModes[accessibilityModeIndex]
|
|
||||||
if filter.shader then
|
|
||||||
filter.shader:send("accessibilityTime", love.timer.getTime())
|
|
||||||
love.graphics.setShader(filter.shader)
|
|
||||||
end
|
|
||||||
love.graphics.draw(presentationCanvas, 0, 0)
|
|
||||||
love.graphics.setShader()
|
|
||||||
|
|
||||||
if accessibilityMessage and love.timer.getTime() < accessibilityMessageExpires then
|
|
||||||
love.graphics.setColor(1, 1, 1, 1)
|
|
||||||
love.graphics.printf(accessibilityMessage, 0, 30, love.graphics.getWidth(), "center")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.keypressed(key)
|
function love.keypressed(key)
|
||||||
-- Available even while a dialogue is open, so the player never has to
|
|
||||||
-- dismiss something important just to change the colour treatment.
|
|
||||||
if key == "f4" then
|
|
||||||
cycleAccessibilityMode()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Talking to a sign captures input: navigate/choose, don't move the player.
|
-- Talking to a sign captures input: navigate/choose, don't move the player.
|
||||||
if Dialogue.isOpen() then
|
if Dialogue.isOpen() then
|
||||||
Dialogue.keypressed(key)
|
Dialogue.keypressed(key)
|
||||||
|
|
@ -180,8 +150,6 @@ function love.keypressed(key)
|
||||||
if key == "return" then
|
if key == "return" then
|
||||||
response = ""
|
response = ""
|
||||||
editorCompleteResponse()
|
editorCompleteResponse()
|
||||||
elseif key == "escape" and editorCancelResponse() then
|
|
||||||
return
|
|
||||||
elseif key == "backspace" then
|
elseif key == "backspace" then
|
||||||
local byteoffset = utf8.offset(response, -1)
|
local byteoffset = utf8.offset(response, -1)
|
||||||
|
|
||||||
|
|
@ -195,17 +163,12 @@ function love.keypressed(key)
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.mousepressed(x, y, button)
|
function love.mousepressed(x, y, button)
|
||||||
|
if editorView == "room" and button == 2 then
|
||||||
|
currentRoom:attemptDelete(x, y, selectedColor)
|
||||||
|
end
|
||||||
editorMouseHandler(x, y, button)
|
editorMouseHandler(x, y, button)
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.mousemoved(x, y)
|
|
||||||
editorMouseMoved(x, y)
|
|
||||||
end
|
|
||||||
|
|
||||||
function love.mousereleased(x, y, button)
|
|
||||||
editorMouseReleased(x, y, button)
|
|
||||||
end
|
|
||||||
|
|
||||||
function love.textinput(t)
|
function love.textinput(t)
|
||||||
response = response .. t
|
response = response .. t
|
||||||
editorTextHandler(response)
|
editorTextHandler(response)
|
||||||
|
|
@ -274,7 +237,6 @@ end
|
||||||
function _initCanvases()
|
function _initCanvases()
|
||||||
gameCanvas = love.graphics.newCanvas()
|
gameCanvas = love.graphics.newCanvas()
|
||||||
finalCanvas = love.graphics.newCanvas()
|
finalCanvas = love.graphics.newCanvas()
|
||||||
presentationCanvas = love.graphics.newCanvas()
|
|
||||||
sideBarCanvas = love.graphics.newCanvas(math.max(1, sideBarBox.w), math.max(1, sideBarBox.h))
|
sideBarCanvas = love.graphics.newCanvas(math.max(1, sideBarBox.w), math.max(1, sideBarBox.h))
|
||||||
sideBarCanvas:setFilter("nearest", "nearest") -- never average when sampled
|
sideBarCanvas:setFilter("nearest", "nearest") -- never average when sampled
|
||||||
end
|
end
|
||||||
|
|
@ -291,37 +253,6 @@ function _initGlobalColors()
|
||||||
gColor.red, gColor.green, gColor.blue, gColor.white = Color:new("red"), Color:new("green"), Color:new("blue"), Color:new("white")
|
gColor.red, gColor.green, gColor.blue, gColor.white = Color:new("red"), Color:new("green"), Color:new("blue"), Color:new("white")
|
||||||
end
|
end
|
||||||
|
|
||||||
function _initAccessibility()
|
|
||||||
accessibilityModes = {
|
|
||||||
{ id = "normal", label = "Normal", shader = nil },
|
|
||||||
{ id = "deuteranopia", label = "Deuteranopia", shader = accessibilityColorShader },
|
|
||||||
}
|
|
||||||
accessibilityModeIndex = 1
|
|
||||||
|
|
||||||
local savedMode = love.filesystem.read("accessibility_mode.txt")
|
|
||||||
if savedMode then
|
|
||||||
savedMode = savedMode:match("[%w_]+")
|
|
||||||
if savedMode == "deuteranopia_high_contrast" then savedMode = "deuteranopia" end
|
|
||||||
for index, mode in ipairs(accessibilityModes) do
|
|
||||||
if mode.id == savedMode then
|
|
||||||
accessibilityModeIndex = index
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local mode = accessibilityModes[accessibilityModeIndex]
|
|
||||||
accessibilityMessage = "Color filter: " .. mode.label .. " (F4 to change)"
|
|
||||||
accessibilityMessageExpires = love.timer.getTime() + 4
|
|
||||||
end
|
|
||||||
|
|
||||||
function cycleAccessibilityMode()
|
|
||||||
accessibilityModeIndex = accessibilityModeIndex % #accessibilityModes + 1
|
|
||||||
local mode = accessibilityModes[accessibilityModeIndex]
|
|
||||||
love.filesystem.write("accessibility_mode.txt", mode.id)
|
|
||||||
accessibilityMessage = "Color filter: " .. mode.label .. " (F4 to change)"
|
|
||||||
accessibilityMessageExpires = love.timer.getTime() + 2.5
|
|
||||||
end
|
|
||||||
|
|
||||||
function _initSounds()
|
function _initSounds()
|
||||||
staticWave = sfxr.newSound()
|
staticWave = sfxr.newSound()
|
||||||
staticWave.waveform = sfxr.WAVEFORM.NOISE -- was `wavetype = 3`; field renamed in this sfxr
|
staticWave.waveform = sfxr.WAVEFORM.NOISE -- was `wavetype = 3`; field renamed in this sfxr
|
||||||
|
|
|
||||||
143
movement.lua
143
movement.lua
|
|
@ -1,143 +0,0 @@
|
||||||
-- Finds the complete set of blocks that must move together for one grid step.
|
|
||||||
-- A block joins the set when it is in another block's destination (ordinary
|
|
||||||
-- pushing), is softly grabbed by sticky/spiky, or touches a rigid skewer.
|
|
||||||
local Movement = {}
|
|
||||||
|
|
||||||
local adjacentDirections = {
|
|
||||||
{x = 1, y = 0},
|
|
||||||
{x = -1, y = 0},
|
|
||||||
{x = 0, y = 1},
|
|
||||||
{x = 0, y = -1},
|
|
||||||
}
|
|
||||||
|
|
||||||
local function isSticky(entity)
|
|
||||||
return entity.isSticky and entity:isSticky()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function isPassable(entity)
|
|
||||||
return entity.canPassOver and entity:canPassOver()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function isSkewer(entity)
|
|
||||||
return entity.isSkewer and entity:isSkewer()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function isSpiky(entity)
|
|
||||||
return entity.isSpiky and entity:isSpiky()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Build one rigid component: ordinary pushes are required, and skewers join
|
|
||||||
-- along every edge. `alreadyMoving` contains cells that will be vacated during
|
|
||||||
-- this same step, so a later pulled component may move into them.
|
|
||||||
local function findRigidGroup(startCells, direction, entityAt, isInBounds, alreadyMoving)
|
|
||||||
local entities = {}
|
|
||||||
local pendingCells = {}
|
|
||||||
for _, cell in ipairs(startCells) do
|
|
||||||
table.insert(pendingCells, cell)
|
|
||||||
end
|
|
||||||
|
|
||||||
local nextCell = 1
|
|
||||||
while nextCell <= #pendingCells do
|
|
||||||
local cell = pendingCells[nextCell]
|
|
||||||
nextCell = nextCell + 1
|
|
||||||
|
|
||||||
if not isInBounds(cell) then
|
|
||||||
return false, entities
|
|
||||||
end
|
|
||||||
|
|
||||||
local entity = entityAt(cell)
|
|
||||||
if entity and not isPassable(entity) and not entities[entity] and
|
|
||||||
not (alreadyMoving and alreadyMoving[entity]) then
|
|
||||||
if not entity:canMove() then
|
|
||||||
return false, entities
|
|
||||||
end
|
|
||||||
|
|
||||||
entities[entity] = entity
|
|
||||||
|
|
||||||
-- Anything occupying this block's destination must be pushed too.
|
|
||||||
for _, projectedCell in ipairs(entity:getOccupiedCells(direction)) do
|
|
||||||
table.insert(pendingCells, projectedCell)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Skewers make every edge contact part of this rigid component.
|
|
||||||
for _, occupiedCell in ipairs(entity:getOccupiedCells()) do
|
|
||||||
for _, adjacentDirection in ipairs(adjacentDirections) do
|
|
||||||
local adjacentCell = {
|
|
||||||
x = occupiedCell.x + adjacentDirection.x,
|
|
||||||
y = occupiedCell.y + adjacentDirection.y,
|
|
||||||
}
|
|
||||||
if isInBounds(adjacentCell) then
|
|
||||||
local adjacentEntity = entityAt(adjacentCell)
|
|
||||||
local attached = adjacentEntity and (
|
|
||||||
isSkewer(entity) or isSkewer(adjacentEntity)
|
|
||||||
)
|
|
||||||
if attached and adjacentEntity ~= entity and adjacentEntity:canMove() and
|
|
||||||
not (alreadyMoving and alreadyMoving[adjacentEntity]) then
|
|
||||||
table.insert(pendingCells, adjacentCell)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return true, entities
|
|
||||||
end
|
|
||||||
|
|
||||||
function Movement.findMovableGroup(startCells, direction, entityAt, isInBounds)
|
|
||||||
local possible, entities = findRigidGroup(
|
|
||||||
startCells, direction, entityAt, isInBounds
|
|
||||||
)
|
|
||||||
if not possible then return false, entities end
|
|
||||||
|
|
||||||
local movingQueue = {}
|
|
||||||
for entity in pairs(entities) do table.insert(movingQueue, entity) end
|
|
||||||
local attemptedPulls = {}
|
|
||||||
local nextMover = 1
|
|
||||||
|
|
||||||
while nextMover <= #movingQueue do
|
|
||||||
local entity = movingQueue[nextMover]
|
|
||||||
nextMover = nextMover + 1
|
|
||||||
|
|
||||||
-- Sticky contact is soft only on the trailing face. Spiky contact is soft
|
|
||||||
-- on every face. Either kind simply releases a blocked rigid component,
|
|
||||||
-- without cancelling this entity or another independently pulled branch.
|
|
||||||
for _, occupiedCell in ipairs(entity:getOccupiedCells()) do
|
|
||||||
for _, adjacentDirection in ipairs(adjacentDirections) do
|
|
||||||
local adjacentCell = {
|
|
||||||
x = occupiedCell.x + adjacentDirection.x,
|
|
||||||
y = occupiedCell.y + adjacentDirection.y,
|
|
||||||
}
|
|
||||||
if isInBounds(adjacentCell) then
|
|
||||||
local adjacentEntity = entityAt(adjacentCell)
|
|
||||||
local trailing = adjacentDirection.x == -direction.x and
|
|
||||||
adjacentDirection.y == -direction.y
|
|
||||||
local softContact = adjacentEntity and (
|
|
||||||
isSpiky(entity) or isSpiky(adjacentEntity) or
|
|
||||||
(trailing and (isSticky(entity) or isSticky(adjacentEntity)))
|
|
||||||
)
|
|
||||||
if softContact and adjacentEntity ~= entity and
|
|
||||||
not entities[adjacentEntity] and not attemptedPulls[adjacentEntity] and
|
|
||||||
adjacentEntity:canMove() then
|
|
||||||
attemptedPulls[adjacentEntity] = true
|
|
||||||
local canPull, pulled = findRigidGroup(
|
|
||||||
{adjacentCell}, direction, entityAt, isInBounds, entities
|
|
||||||
)
|
|
||||||
if canPull then
|
|
||||||
for pulledEntity in pairs(pulled) do
|
|
||||||
if not entities[pulledEntity] then
|
|
||||||
entities[pulledEntity] = pulledEntity
|
|
||||||
table.insert(movingQueue, pulledEntity)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return true, entities
|
|
||||||
end
|
|
||||||
|
|
||||||
return Movement
|
|
||||||
18
npc.lua
18
npc.lua
|
|
@ -20,21 +20,3 @@ function Sign:initialize(x, y)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
Jorge = class("Jorge", Entity)
|
|
||||||
Jorge:include(Dialogable)
|
|
||||||
|
|
||||||
function Jorge:initialize(x, y)
|
|
||||||
self.book = jorgeBook
|
|
||||||
self.saveClass = "jorge"
|
|
||||||
Entity.initialize(self, {
|
|
||||||
x = x, y = y,
|
|
||||||
color = "red",
|
|
||||||
collision = "unmoveable",
|
|
||||||
sprites = {
|
|
||||||
red = loadSprite("art/npc/jorge_r.png"),
|
|
||||||
green = loadSprite("art/npc/jorge_g.png"),
|
|
||||||
blue = loadSprite("art/npc/jorge_b.png"),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
|
||||||
192
room.lua
192
room.lua
|
|
@ -10,13 +10,8 @@ require "switch"
|
||||||
require "door"
|
require "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"
|
||||||
|
|
||||||
local Movement = require "movement"
|
|
||||||
|
|
||||||
function Room:initialize(t)
|
function Room:initialize(t)
|
||||||
self.player = {}
|
self.player = {}
|
||||||
self.switches = { red = {}, green = {}, blue = {} }
|
self.switches = { red = {}, green = {}, blue = {} }
|
||||||
|
|
@ -207,7 +202,7 @@ function Room:movePlayer(key)
|
||||||
end
|
end
|
||||||
|
|
||||||
local move = true
|
local move = true
|
||||||
-- PUSH ---------------------------------------------------------------
|
-- PUSHHHHHHHHHHHHHHHHHHHHHH---------------------
|
||||||
|
|
||||||
local initialTargetCell = {
|
local initialTargetCell = {
|
||||||
x = pos.x + dir.x, y = pos.y + dir.y
|
x = pos.x + dir.x, y = pos.y + dir.y
|
||||||
|
|
@ -224,24 +219,104 @@ function Room:movePlayer(key)
|
||||||
if not self:isInBounds(initialTargetCell) then
|
if not self:isInBounds(initialTargetCell) then
|
||||||
-- let's us move out of bounds to Change Rooms without pulling a bloc_
|
-- let's us move out of bounds to Change Rooms without pulling a bloc_
|
||||||
move = true
|
move = true
|
||||||
|
elseif initialTargetEntity and initialTargetEntity:canPassOver() then
|
||||||
|
--we should put this in the else statement actually
|
||||||
|
move = true
|
||||||
else
|
else
|
||||||
move, entitiesToPush = Movement.findMovableGroup(
|
local targetCells
|
||||||
{initialTargetCell}, dir,
|
|
||||||
function(cell) return self:getCellInMatrix(self.collidableMatrices[color], cell) end,
|
targetCells = {initialTargetCell} -- ...we start with our attempted movement...
|
||||||
function(cell) return self:isInBounds(cell) end
|
local doneSearching
|
||||||
)
|
while not doneSearching do
|
||||||
|
nextTargetCells = {} -- ...then we create this array to save our next group of cells
|
||||||
|
local emptyCells = {}
|
||||||
|
for _, cell in pairs(targetCells) do
|
||||||
|
if self:isInBounds(cell) then
|
||||||
|
local entity = self:getCellInMatrix(self.collidableMatrices[color], cell)
|
||||||
|
if entity then
|
||||||
|
--something is here!!
|
||||||
|
if not entitiesToPush[entity] then --if the entity hasnt been found already
|
||||||
|
if entity:canMove() then
|
||||||
|
local projectedCells = entity:getOccupiedCells(dir) -- we project the movement here
|
||||||
|
entitiesToPush[entity] = entity
|
||||||
|
for _, projectedCell in ipairs(projectedCells) do
|
||||||
|
table.insert(nextTargetCells, projectedCell)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
move = false
|
||||||
|
doneSearching = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
--there's nothin there!!
|
||||||
|
table.insert(emptyCells, cell)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
--we are out of bounds now
|
||||||
|
move = false
|
||||||
|
doneSearching = true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- PULL ---------------------------------------------------------------
|
--if every projected cell is empty then we can do the move
|
||||||
|
if #emptyCells == #targetCells then
|
||||||
|
move = true
|
||||||
|
doneSearching = true
|
||||||
|
end
|
||||||
|
|
||||||
|
targetCells = nextTargetCells
|
||||||
|
end
|
||||||
|
--let's get out of this loop b/c if we can't move one color we can't move at all
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
---PULL CHEC
|
||||||
|
|
||||||
local initialTargetCell = {x = pos.x - dir.x, y = pos.y - dir.y}
|
local initialTargetCell = {x = pos.x - dir.x, y = pos.y - dir.y}
|
||||||
local pull = false
|
|
||||||
if self:isInBounds(initialTargetCell) then
|
local targetCells
|
||||||
pull, entitiesToPull = Movement.findMovableGroup(
|
|
||||||
{initialTargetCell}, dir,
|
local pull
|
||||||
function(cell) return self:getCellInMatrix(self.collidableMatrices[color], cell) end,
|
--for each color...
|
||||||
function(cell) return self:isInBounds(cell) end
|
targetCells = {initialTargetCell} -- ...we start with our attempted movement...
|
||||||
)
|
local doneSearching
|
||||||
|
while not doneSearching do
|
||||||
|
nextTargetCells = {} -- ...then we create this array to save our next group of cells
|
||||||
|
local emptyCells = {}
|
||||||
|
for _, cell in pairs(targetCells) do
|
||||||
|
if self:isInBounds(cell) then
|
||||||
|
local entity = self:getCellInMatrix(self.collidableMatrices[color], cell)
|
||||||
|
if entity then
|
||||||
|
--something is here!!
|
||||||
|
if not entitiesToPull[entity] then --if the entity hasnt been found already
|
||||||
|
if entity:canMove() then
|
||||||
|
local projectedCells = entity:getOccupiedCells(dir) -- we project the movement here
|
||||||
|
entitiesToPull[entity] = entity
|
||||||
|
for _, projectedCell in ipairs(projectedCells) do
|
||||||
|
table.insert(nextTargetCells, projectedCell)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
pull = false
|
||||||
|
doneSearching = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
--there's nothin there!!
|
||||||
|
table.insert(emptyCells, cell)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
--we are out of bounds now
|
||||||
|
doneSearching = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--if every projected cell is empty then we can do the move
|
||||||
|
if #emptyCells == #targetCells then
|
||||||
|
pull = true
|
||||||
|
doneSearching = true
|
||||||
|
end
|
||||||
|
|
||||||
|
targetCells = nextTargetCells
|
||||||
end
|
end
|
||||||
|
|
||||||
if move then
|
if move then
|
||||||
|
|
@ -250,13 +325,9 @@ function Room:movePlayer(key)
|
||||||
end
|
end
|
||||||
if pull == true then
|
if pull == true then
|
||||||
for _, entity in pairs(entitiesToPull) do
|
for _, entity in pairs(entitiesToPull) do
|
||||||
-- A connected component can wrap around the player and be found by
|
|
||||||
-- both searches. Every entity still moves exactly one grid cell.
|
|
||||||
if not entitiesToPush[entity] then
|
|
||||||
entity:move(axis, dir[axis])
|
entity:move(axis, dir[axis])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
self.player[color]:move(axis, dir[axis])
|
self.player[color]:move(axis, dir[axis])
|
||||||
moved = true
|
moved = true
|
||||||
end
|
end
|
||||||
|
|
@ -270,21 +341,21 @@ function Room:movePlayer(key)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Room:switchPlayerColor(color)
|
function Room:switchPlayerColor(color)
|
||||||
-- Number keys can request a colour the player has not collected yet. Do
|
local canSwitch
|
||||||
-- nothing in that case: there is no corresponding player object to switch
|
|
||||||
-- to (or use as the sound source).
|
|
||||||
local targetPlayer = self.player[color]
|
|
||||||
if not targetPlayer then return false end
|
|
||||||
|
|
||||||
local canSwitch = not self:onlyColorIsActive(color)
|
if self:onlyColorIsActive(color) then
|
||||||
if canSwitch then
|
canSwitch = false
|
||||||
for _, c in ipairs(self:getPossibleColors()) do
|
else
|
||||||
self:setActiveColor(c, c == color)
|
for _, c in pairs(self:getPossibleColors()) do
|
||||||
|
if c == color then
|
||||||
|
self:setActiveColor(c, true)
|
||||||
|
else
|
||||||
|
self:setActiveColor(c, false)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
targetPlayer:playSwitchSound(canSwitch)
|
self.player[color]:playSwitchSound(canSwitch)
|
||||||
return canSwitch
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Room:setActiveColor(color, bool)
|
function Room:setActiveColor(color, bool)
|
||||||
|
|
@ -466,7 +537,7 @@ end
|
||||||
|
|
||||||
-- Instantiate one object from the flat {class,color,x,y} schema. `class` names an
|
-- Instantiate one object from the flat {class,color,x,y} schema. `class` names an
|
||||||
-- art asset, and its .meta declares the behaviour ("door"/"switch"/"player"/
|
-- art asset, and its .meta declares the behaviour ("door"/"switch"/"player"/
|
||||||
-- "moveable"/"sticky"/"skewer"/"spiky"); anything without a .meta defaults to immoveable. npc is the one
|
-- "moveable"); anything without a .meta defaults to immoveable. npc is the one
|
||||||
-- keyword handled directly since it has no art.
|
-- keyword handled directly since it has no art.
|
||||||
function Room:createEntity(class, color, x, y)
|
function Room:createEntity(class, color, x, y)
|
||||||
if class == "npc" then
|
if class == "npc" then
|
||||||
|
|
@ -474,11 +545,6 @@ function Room:createEntity(class, color, x, y)
|
||||||
self:registerNpc(entity)
|
self:registerNpc(entity)
|
||||||
return entity
|
return entity
|
||||||
end
|
end
|
||||||
if class == "jorge" then
|
|
||||||
local entity = Jorge:new(x, y)
|
|
||||||
self:registerNpc(entity)
|
|
||||||
return entity
|
|
||||||
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"
|
||||||
|
|
@ -511,12 +577,6 @@ function Room:createEntity(class, color, x, y)
|
||||||
if cellShape and #cellShape == 0 then cellShape = nil end
|
if cellShape and #cellShape == 0 then cellShape = nil end
|
||||||
if behavior == "moveable" then
|
if behavior == "moveable" then
|
||||||
entity = GenericMoveable:new(x, y, color, sprite, class, cellShape)
|
entity = GenericMoveable:new(x, y, color, sprite, class, cellShape)
|
||||||
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
|
||||||
|
|
@ -604,25 +664,12 @@ function Room:attemptDelete(mouseX, mouseY, color)
|
||||||
for index = #self.doors, 1, -1 do
|
for index = #self.doors, 1, -1 do
|
||||||
if removedEntities[self.doors[index]] then table.remove(self.doors, index) end
|
if removedEntities[self.doors[index]] then table.remove(self.doors, index) end
|
||||||
end
|
end
|
||||||
-- NPCs are stamped into every collision layer, but are kept in their own
|
|
||||||
-- registry rather than in self.collideables. Remove them directly so a
|
|
||||||
-- right-click works no matter which editor colour is currently selected.
|
|
||||||
for index = #self.npcs, 1, -1 do
|
|
||||||
if self.npcs[index]:occupiesCell(cell) then table.remove(self.npcs, index) end
|
|
||||||
end
|
|
||||||
self:tick()
|
self:tick()
|
||||||
end
|
end
|
||||||
|
|
||||||
function Room:attemptPlace(name, props, mouseX, mouseY, color)
|
function Room:attemptPlace(name, props, mouseX, mouseY, color)
|
||||||
if not props or not props.sprites then return end
|
if not props or not props.sprites then return end
|
||||||
local cell = self:getCellFromMousePos(mouseX, mouseY)
|
local cell = self:getCellFromMousePos(mouseX, mouseY)
|
||||||
-- Jorge is one dialogable NPC with three display channels, not three
|
|
||||||
-- independently placeable color-layer entities.
|
|
||||||
if name == "jorge" then
|
|
||||||
self:createEntity(name, nil, cell.x, cell.y)
|
|
||||||
self:tick()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- "white" (or nil) means every channel; otherwise just the picked channel
|
-- "white" (or nil) means every channel; otherwise just the picked channel
|
||||||
local colors
|
local colors
|
||||||
|
|
@ -680,9 +727,32 @@ end
|
||||||
-- This is intentionally separate from Room:save. It is the editor's explicit
|
-- This is intentionally separate from Room:save. It is the editor's explicit
|
||||||
-- "write this level into the project" action; gameplay autosaves must never
|
-- "write this level into the project" action; gameplay autosaves must never
|
||||||
-- call it.
|
-- call it.
|
||||||
function Room:saveToSource(name, savePlayerPositions)
|
function Room:saveToSource(name, savePlayerPositions, playerSourceName)
|
||||||
name = name or self.name or "test"
|
name = name or self.name or "test"
|
||||||
local saveTable = self:serialize(savePlayerPositions ~= false)
|
local saveTable = self:serialize(savePlayerPositions ~= false)
|
||||||
|
if savePlayerPositions == false then
|
||||||
|
-- A normal editor save keeps the authored spawn positions from the
|
||||||
|
-- existing source level. During a rename that source is still oldName.
|
||||||
|
local sourceName = playerSourceName or name
|
||||||
|
local existing = readFromSource("rooms/" .. sourceName .. ".sav")
|
||||||
|
local keptPlayer = false
|
||||||
|
if existing then
|
||||||
|
local previous = TSerial.unpack(existing)
|
||||||
|
for _, object in ipairs(previous.objects or {}) do
|
||||||
|
if object.class == "player" then
|
||||||
|
table.insert(saveTable.objects, object)
|
||||||
|
keptPlayer = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- A newly named blank level has no prior source spawn to retain.
|
||||||
|
if not keptPlayer then
|
||||||
|
for color, player in pairs(self.player) do
|
||||||
|
local pos = player:getGridPos()
|
||||||
|
table.insert(saveTable.objects, { class = "player", color = color, x = pos.x, y = pos.y })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
local room = TSerial.pack(saveTable, nil, true)
|
local room = TSerial.pack(saveTable, nil, true)
|
||||||
return writeToSource("rooms/" .. name .. ".sav", room)
|
return writeToSource("rooms/" .. name .. ".sav", room)
|
||||||
end
|
end
|
||||||
|
|
@ -721,7 +791,7 @@ function Room:serialize(includePlayers)
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, npc in ipairs(self.npcs) do
|
for _, npc in ipairs(self.npcs) do
|
||||||
table.insert(objects, { class = npc.saveClass or "npc", x = npc.x, y = npc.y })
|
table.insert(objects, { class = "npc", x = npc.x, y = npc.y })
|
||||||
end
|
end
|
||||||
|
|
||||||
return { objects = objects }
|
return { objects = objects }
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1408
rooms/jorge2.sav
1408
rooms/jorge2.sav
File diff suppressed because it is too large
Load diff
760
rooms/ok.sav
760
rooms/ok.sav
|
|
@ -1,760 +0,0 @@
|
||||||
{
|
|
||||||
objects={
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=10,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=10,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=9,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=8,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=7,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=6,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=5,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=4,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=3,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=2,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=10,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=2,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=3,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=4,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=5,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=6,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=7,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=8,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=9,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=10,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=5,
|
|
||||||
y=7,
|
|
||||||
class="box",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=10,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=10,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=9,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=8,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=7,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=6,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=5,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=4,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=3,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=2,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=10,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=2,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=3,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=4,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=5,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=6,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=7,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=8,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=9,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=10,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=6,
|
|
||||||
y=6,
|
|
||||||
class="box",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=10,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=10,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=9,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=8,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=7,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=6,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=5,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=4,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=3,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=2,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=10,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=2,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=3,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=4,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=5,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=6,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=7,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=8,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=9,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=10,
|
|
||||||
y=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=11,
|
|
||||||
y=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=5,
|
|
||||||
y=6,
|
|
||||||
class="box",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=6,
|
|
||||||
y=8,
|
|
||||||
class="player",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=6,
|
|
||||||
y=8,
|
|
||||||
class="player",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=6,
|
|
||||||
y=8,
|
|
||||||
class="player",
|
|
||||||
color="red"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
{
|
|
||||||
objects={
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
{
|
|
||||||
objects={
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
{
|
|
||||||
objects={
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
{
|
|
||||||
objects={
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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
|
|
@ -1,994 +0,0 @@
|
||||||
{
|
|
||||||
objects={
|
|
||||||
{
|
|
||||||
y=10,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=9,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=8,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=7,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=4,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=10,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=4,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=8,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=9,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=10,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=10,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=4,
|
|
||||||
x=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=5,
|
|
||||||
class="box_3x2_111-010",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=11,
|
|
||||||
class="door",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=10,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=9,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=8,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=7,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=4,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=10,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=4,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=8,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=9,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=10,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=10,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=4,
|
|
||||||
x=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=5,
|
|
||||||
class="box_3x2_111-010",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=11,
|
|
||||||
class="door",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=10,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=9,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=8,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=7,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=4,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=10,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=4,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=8,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=9,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=10,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=10,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=4,
|
|
||||||
x=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=5,
|
|
||||||
class="box_3x2_111-010",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=11,
|
|
||||||
class="door",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=2,
|
|
||||||
class="switch",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=2,
|
|
||||||
class="switch",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=2,
|
|
||||||
class="switch",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=4,
|
|
||||||
x=4,
|
|
||||||
class="player",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=4,
|
|
||||||
x=4,
|
|
||||||
class="player",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=4,
|
|
||||||
x=4,
|
|
||||||
class="player",
|
|
||||||
color="blue"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,795 +0,0 @@
|
||||||
{
|
|
||||||
objects={
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=6,
|
|
||||||
class="door",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=9,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=10,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=4,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=10,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=8,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=10,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=10,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=7,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=7,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=4,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=10,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=9,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=8,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=6,
|
|
||||||
class="box",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=6,
|
|
||||||
class="door",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=9,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=10,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=10,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=4,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=10,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=8,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=10,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=7,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=9,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=7,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=4,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=8,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=6,
|
|
||||||
class="door",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=9,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=10,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=2,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=10,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=5,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=4,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=10,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=8,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=6,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=9,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=3,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=6,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=10,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=7,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=9,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=7,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=4,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=5,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=2,
|
|
||||||
x=1,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=7,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=1,
|
|
||||||
x=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=8,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=3,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=8,
|
|
||||||
x=11,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=11,
|
|
||||||
x=4,
|
|
||||||
class="static_wall",
|
|
||||||
color="blue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
y=4,
|
|
||||||
x=6,
|
|
||||||
class="switch",
|
|
||||||
color="red"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=4,
|
|
||||||
class="npc",
|
|
||||||
y=8
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=6,
|
|
||||||
color="red",
|
|
||||||
class="player",
|
|
||||||
y=9
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
188
rooms/world.sav
188
rooms/world.sav
|
|
@ -1,207 +1,157 @@
|
||||||
{
|
{
|
||||||
width=18,
|
|
||||||
height=18,
|
|
||||||
rooms={
|
rooms={
|
||||||
{
|
{
|
||||||
x=10,
|
name="start",
|
||||||
|
y=11,
|
||||||
|
x=9
|
||||||
|
},
|
||||||
|
{
|
||||||
name="3",
|
name="3",
|
||||||
y=10
|
y=10,
|
||||||
|
x=10
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=12,
|
|
||||||
name="5",
|
name="5",
|
||||||
y=10
|
y=10,
|
||||||
|
x=12
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=12,
|
|
||||||
name="village_1",
|
name="village_1",
|
||||||
y=9
|
y=9,
|
||||||
|
x=12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name="village_2",
|
||||||
|
y=8,
|
||||||
|
x=12
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=13,
|
|
||||||
name="village_3",
|
name="village_3",
|
||||||
y=8
|
y=8,
|
||||||
|
x=13
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=11,
|
|
||||||
name="village_4",
|
name="village_4",
|
||||||
y=8
|
y=8,
|
||||||
|
x=11
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=11,
|
|
||||||
name="yellow_1",
|
name="yellow_1",
|
||||||
y=7
|
y=7,
|
||||||
|
x=11
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=11,
|
|
||||||
name="yellow_3",
|
name="yellow_3",
|
||||||
y=6
|
y=6,
|
||||||
|
x=11
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=12,
|
|
||||||
name="yellow_2",
|
name="yellow_2",
|
||||||
y=7
|
y=7,
|
||||||
|
x=12
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=11,
|
|
||||||
name="yellow_4",
|
name="yellow_4",
|
||||||
y=5
|
y=5,
|
||||||
|
x=11
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=11,
|
|
||||||
name="yellow_5",
|
name="yellow_5",
|
||||||
y=4
|
y=4,
|
||||||
|
x=11
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=10,
|
|
||||||
name="yellow_6",
|
name="yellow_6",
|
||||||
y=5
|
y=5,
|
||||||
|
x=10
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=1,
|
|
||||||
name="default",
|
name="default",
|
||||||
y=1
|
y=1,
|
||||||
|
x=1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=3,
|
|
||||||
name="old_yellow_3",
|
name="old_yellow_3",
|
||||||
y=1
|
y=1,
|
||||||
|
x=3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=5,
|
|
||||||
name="test_patt",
|
name="test_patt",
|
||||||
y=1
|
y=1,
|
||||||
|
x=5
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=7,
|
|
||||||
name="test_patt_update",
|
name="test_patt_update",
|
||||||
y=1
|
y=1,
|
||||||
|
x=7
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=9,
|
|
||||||
name="white_asymmetrical",
|
name="white_asymmetrical",
|
||||||
y=1
|
y=1,
|
||||||
|
x=9
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=11,
|
|
||||||
name="white_ok",
|
name="white_ok",
|
||||||
y=1
|
y=1,
|
||||||
|
x=11
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=13,
|
|
||||||
name="white_primary",
|
name="white_primary",
|
||||||
y=1
|
y=1,
|
||||||
|
x=13
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=15,
|
|
||||||
name="yellow_arches",
|
name="yellow_arches",
|
||||||
y=1
|
y=1,
|
||||||
|
x=15
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=17,
|
|
||||||
name="yellow_asymmetrical",
|
name="yellow_asymmetrical",
|
||||||
y=1
|
y=1,
|
||||||
|
x=17
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=1,
|
|
||||||
name="yellow_cc_moustache",
|
name="yellow_cc_moustache",
|
||||||
y=3
|
y=3,
|
||||||
|
x=1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=3,
|
|
||||||
name="yellow_color_changing_levvel_1",
|
name="yellow_color_changing_levvel_1",
|
||||||
y=3
|
y=3,
|
||||||
|
x=3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=7,
|
|
||||||
name="yellow_smile",
|
name="yellow_smile",
|
||||||
y=3
|
y=3,
|
||||||
|
x=7
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=9,
|
|
||||||
name="2",
|
name="2",
|
||||||
y=10
|
y=10,
|
||||||
|
x=9
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=11,
|
|
||||||
name="room_11_09",
|
name="room_11_09",
|
||||||
y=9
|
y=9,
|
||||||
|
x=11
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=9,
|
|
||||||
name="yellow_skull",
|
name="yellow_skull",
|
||||||
y=3
|
y=3,
|
||||||
|
x=9
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=5,
|
|
||||||
name="jorge_room",
|
name="jorge_room",
|
||||||
y=5
|
y=5,
|
||||||
|
x=5
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
x=11,
|
|
||||||
name="4",
|
name="4",
|
||||||
y=10
|
y=10,
|
||||||
},
|
x=11
|
||||||
{
|
|
||||||
x=1,
|
|
||||||
name="room_01_05",
|
|
||||||
y=5
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=2,
|
|
||||||
name="ok",
|
|
||||||
y=8
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=5,
|
|
||||||
name="room_05_18",
|
|
||||||
y=18
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=9,
|
|
||||||
name="village_start",
|
|
||||||
y=11
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=4,
|
|
||||||
name="spiky_test",
|
|
||||||
y=8
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=5,
|
|
||||||
name="corner_trimino_test",
|
|
||||||
y=7
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=6,
|
|
||||||
name="tetromino_loop_t",
|
|
||||||
y=7
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=5,
|
|
||||||
name="corner_trimino_test_copy",
|
|
||||||
y=6
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=7,
|
|
||||||
name="room_07_07",
|
|
||||||
y=7
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=5,
|
|
||||||
name="spiky_test_2",
|
|
||||||
y=8
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=5,
|
|
||||||
name="spiky_test_3",
|
|
||||||
y=9
|
|
||||||
},
|
|
||||||
{
|
|
||||||
x=3,
|
|
||||||
name="jorge2",
|
|
||||||
y=14
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
lastRoom="jorge2"
|
lastRoom="yellow_4",
|
||||||
|
width=18,
|
||||||
|
height=18
|
||||||
}
|
}
|
||||||
118
shaders.lua
118
shaders.lua
|
|
@ -58,121 +58,3 @@ glow = love.graphics.newShader[[
|
||||||
return pixel_me;
|
return pixel_me;
|
||||||
}
|
}
|
||||||
]]
|
]]
|
||||||
|
|
||||||
-- Sticky boxes reuse the authored box textures. This shader only wobbles their
|
|
||||||
-- sampling; the draw helper adds a tiny squash-and-stretch bounce around center.
|
|
||||||
goopShader = love.graphics.newShader[[
|
|
||||||
extern number goopTime;
|
|
||||||
extern vec2 sourceSize;
|
|
||||||
|
|
||||||
float goopHash(float value) {
|
|
||||||
return fract(sin(value * 91.3458) * 47453.5453);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 effect(vec4 color, Image texture, vec2 textureCoords, vec2 screenCoords) {
|
|
||||||
float sourceY = textureCoords.y;
|
|
||||||
float pixelWidth = 1.0 / sourceSize.x;
|
|
||||||
float pixelHeight = 1.0 / sourceSize.y;
|
|
||||||
float column = floor(textureCoords.x * sourceSize.x / 2.0);
|
|
||||||
float seed = goopHash(column);
|
|
||||||
|
|
||||||
// Two gentle, out-of-phase waves make the surface feel soft and viscous.
|
|
||||||
// Both stay sub-pixel so the authored silhouette remains recognizable.
|
|
||||||
float horizontalWave = sin(goopTime * 1.7 + sourceY * 10.0 + seed * 6.2831);
|
|
||||||
float verticalWave = sin(goopTime * 1.3 + textureCoords.x * 12.0 + seed * 3.1415);
|
|
||||||
float sourceX = clamp(textureCoords.x + horizontalWave * pixelWidth * 0.45,
|
|
||||||
pixelWidth * 0.5, 1.0 - pixelWidth * 0.5);
|
|
||||||
float wobbledY = clamp(sourceY + verticalWave * pixelHeight * 0.28,
|
|
||||||
pixelHeight * 0.5, 1.0 - pixelHeight * 0.5);
|
|
||||||
|
|
||||||
return Texel(texture, vec2(sourceX, wobbledY)) * color;
|
|
||||||
}
|
|
||||||
]]
|
|
||||||
|
|
||||||
function drawGoopySprite(sprite, x, y, scale)
|
|
||||||
local w, h = sprite:getWidth(), sprite:getHeight()
|
|
||||||
local time = love.timer.getTime()
|
|
||||||
local bounce = math.sin(time * 2.4 + w * .17 + h * .11) * .035
|
|
||||||
local scaleX = scale * (1 + bounce)
|
|
||||||
local scaleY = scale * (1 - bounce)
|
|
||||||
local drawX = x - w * (scaleX - scale) / 2
|
|
||||||
local drawY = y - h * (scaleY - scale) / 2
|
|
||||||
|
|
||||||
goopShader:send("goopTime", time)
|
|
||||||
goopShader:send("sourceSize", {w, h})
|
|
||||||
local previousShader = love.graphics.getShader()
|
|
||||||
love.graphics.setShader(goopShader)
|
|
||||||
love.graphics.draw(sprite, drawX, drawY, 0, scaleX, scaleY)
|
|
||||||
love.graphics.setShader(previousShader)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Accessibility post-process. The puzzle's RGB channels are normally also
|
|
||||||
-- its visual language, so this runs only after the entire scene has been
|
|
||||||
-- composed. It leaves neutral UI/text alone and remaps saturated colours to
|
|
||||||
-- a palette whose red, green, and yellow accents remain distinct for people
|
|
||||||
-- with deuteranopia.
|
|
||||||
accessibilityColorShader = love.graphics.newShader[[
|
|
||||||
extern number accessibilityTime;
|
|
||||||
|
|
||||||
vec3 rgbToHsv(vec3 c) {
|
|
||||||
float maximum = max(c.r, max(c.g, c.b));
|
|
||||||
float minimum = min(c.r, min(c.g, c.b));
|
|
||||||
float delta = maximum - minimum;
|
|
||||||
float hue = 0.0;
|
|
||||||
|
|
||||||
if (delta > 0.0001) {
|
|
||||||
if (maximum == c.r) {
|
|
||||||
hue = mod((c.g - c.b) / delta, 6.0);
|
|
||||||
} else if (maximum == c.g) {
|
|
||||||
hue = ((c.b - c.r) / delta) + 2.0;
|
|
||||||
} else {
|
|
||||||
hue = ((c.r - c.g) / delta) + 4.0;
|
|
||||||
}
|
|
||||||
hue = hue / 6.0;
|
|
||||||
if (hue < 0.0) hue += 1.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
float saturation = maximum <= 0.0 ? 0.0 : delta / maximum;
|
|
||||||
return vec3(hue, saturation, maximum);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 effect(vec4 color, Image texture, vec2 texture_coords, vec2 screen_coords) {
|
|
||||||
vec4 pixel = Texel(texture, texture_coords) * color;
|
|
||||||
vec3 hsv = rgbToHsv(pixel.rgb);
|
|
||||||
|
|
||||||
// Preserve greys, white (including additive RGB overlap), and near-black
|
|
||||||
// background detail. Only colourful puzzle elements are recoloured.
|
|
||||||
if (hsv.y < 0.22 || hsv.z < 0.06) return pixel;
|
|
||||||
|
|
||||||
// Keep every additive pair in its own bucket. In particular, magenta
|
|
||||||
// (red + blue) must not become the same colour as red, and cyan
|
|
||||||
// (green + blue) must not become the same colour as green.
|
|
||||||
vec3 replacement;
|
|
||||||
float pattern = 0.0;
|
|
||||||
// Advance in whole pixels so the texture stays crisp instead of swimming.
|
|
||||||
float ditherStep = floor(accessibilityTime * 1.5);
|
|
||||||
if (hsv.x < 0.055 || hsv.x >= 0.94) {
|
|
||||||
replacement = vec3(0.93, 0.18, 0.50); // red: pink
|
|
||||||
} else if (hsv.x < 0.19) {
|
|
||||||
replacement = vec3(1.00, 0.78, 0.08); // red + green: gold
|
|
||||||
vec2 cell = mod(floor(screen_coords + vec2(ditherStep, 0.0)), vec2(9.0));
|
|
||||||
pattern = length(cell - vec2(4.0)) < 1.6 ? 1.0 : 0.0; // dithered dots
|
|
||||||
} else if (hsv.x < 0.43) {
|
|
||||||
replacement = vec3(0.00, 0.72, 0.62); // green: turquoise
|
|
||||||
} else if (hsv.x < 0.58) {
|
|
||||||
replacement = vec3(0.12, 0.55, 0.96); // green + blue: sky blue
|
|
||||||
pattern = mod(floor(screen_coords.x + screen_coords.y + ditherStep), 10.0) < 2.0 ? 1.0 : 0.0; // dithered slashes
|
|
||||||
} else if (hsv.x < 0.77) {
|
|
||||||
replacement = vec3(0.38, 0.32, 0.96); // blue: indigo
|
|
||||||
} else {
|
|
||||||
replacement = vec3(0.72, 0.25, 0.92); // red + blue: purple
|
|
||||||
pattern = mod(floor((screen_coords.x + ditherStep) / 4.0) + floor(screen_coords.y / 4.0), 2.0); // dithered checker
|
|
||||||
}
|
|
||||||
|
|
||||||
// Secondary colours get a gently animated dither cue, so they are
|
|
||||||
// identifiable even when their hue is hard to perceive. Lighting and
|
|
||||||
// fades remain.
|
|
||||||
replacement = mix(replacement, vec3(1.0), pattern * 0.55);
|
|
||||||
return vec4(replacement * hsv.z, pixel.a);
|
|
||||||
}
|
|
||||||
]]
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
Jorge: I'm Jorge. I'm LGBTQ+, and I spent too long hiding that.
|
|
||||||
|
|
||||||
* [Be kind] I'm glad you told me.
|
|
||||||
Jorge smiles. "Thank you. Being met with kindness makes this place feel safer."
|
|
||||||
* [Mistreat Jorge] That's not something I respect.
|
|
||||||
Jorge's smile fades. "You can choose cruelty, but you don't get to define me."
|
|
||||||
- -> END
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
return {["constants"]={},["tree"]={["_"]={["_"]={"Jorge: I'm Jorge. I'm LGBTQ+, and I spent too long hiding that.",{["node"]={"Jorge smiles. \"Thank you. Being met with kindness makes this place feel safer.\""},["choice"]="Be kind",["text"]="I'm glad you told me."},{["node"]={"Jorge's smile fades. \"You can choose cruelty, but you don't get to define me.\""},["choice"]="Mistreat Jorge",["text"]="That's not something I respect."},{["divert"]={["path"]="END",["tunnel"]=false}}}}},["inclusions"]={},["params"]={},["version"]={["tree"]=1,["engine"]=2},["variables"]={},["lists"]={}}
|
|
||||||
|
|
@ -1,548 +0,0 @@
|
||||||
package.path = "./?.lua;" .. package.path
|
|
||||||
|
|
||||||
local Movement = require "movement"
|
|
||||||
|
|
||||||
local passed = 0
|
|
||||||
|
|
||||||
local function test(name, fn)
|
|
||||||
local ok, err = pcall(fn)
|
|
||||||
if not ok then
|
|
||||||
io.stderr:write("FAIL: " .. name .. "\n" .. err .. "\n")
|
|
||||||
os.exit(1)
|
|
||||||
end
|
|
||||||
passed = passed + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local function assertTrue(value, message)
|
|
||||||
if not value then error(message or "expected true", 2) end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function assertFalse(value, message)
|
|
||||||
if value then error(message or "expected false", 2) end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function assertIn(group, entity, message)
|
|
||||||
if group[entity] ~= entity then error(message or "expected entity in movement group", 2) end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function assertNotIn(group, entity, message)
|
|
||||||
if group[entity] then error(message or "expected entity outside movement group", 2) end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function movable(x, y, sticky, shape, skewer, spiky)
|
|
||||||
local entity = {
|
|
||||||
x = x, y = y,
|
|
||||||
sticky = sticky or false,
|
|
||||||
skewer = skewer or false,
|
|
||||||
spiky = spiky or false,
|
|
||||||
shape = shape or {{x = 0, y = 0}},
|
|
||||||
}
|
|
||||||
function entity:canMove() return true end
|
|
||||||
function entity:canPassOver() return false end
|
|
||||||
function entity:isSticky() return self.sticky end
|
|
||||||
function entity:isSkewer() return self.skewer end
|
|
||||||
function entity:isSpiky() return self.spiky end
|
|
||||||
function entity:bump() 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:getOccupiedCells(offset)
|
|
||||||
offset = offset or {x = 0, y = 0}
|
|
||||||
local cells = {}
|
|
||||||
for _, cell in ipairs(self.shape) do
|
|
||||||
table.insert(cells, {x = self.x + cell.x + offset.x, y = self.y + cell.y + offset.y})
|
|
||||||
end
|
|
||||||
return cells
|
|
||||||
end
|
|
||||||
return entity
|
|
||||||
end
|
|
||||||
|
|
||||||
local function wall(x, y)
|
|
||||||
local entity = movable(x, y)
|
|
||||||
function entity:canMove() return false end
|
|
||||||
return entity
|
|
||||||
end
|
|
||||||
|
|
||||||
local function world(entities, width, height)
|
|
||||||
width, height = width or 8, height or 8
|
|
||||||
local cells = {}
|
|
||||||
for _, entity in ipairs(entities) do
|
|
||||||
for _, cell in ipairs(entity:getOccupiedCells()) do
|
|
||||||
cells[cell.y .. ":" .. cell.x] = entity
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return function(cell)
|
|
||||||
return cells[cell.y .. ":" .. cell.x]
|
|
||||||
end, function(cell)
|
|
||||||
return cell.x >= 1 and cell.x <= width and cell.y >= 1 and cell.y <= height
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function plan(entities, startCell, direction, width, height)
|
|
||||||
local entityAt, inBounds = world(entities, width, height)
|
|
||||||
return Movement.findMovableGroup({startCell}, direction, entityAt, inBounds)
|
|
||||||
end
|
|
||||||
|
|
||||||
test("ordinary pull only drags the block touching the player", function()
|
|
||||||
-- regular, regular, player; moving right
|
|
||||||
local rear = movable(2, 3)
|
|
||||||
local touching = movable(3, 3)
|
|
||||||
local possible, group = plan({rear, touching}, {x = 3, y = 3}, {x = 1, y = 0})
|
|
||||||
assertTrue(possible)
|
|
||||||
assertIn(group, touching)
|
|
||||||
assertNotIn(group, rear)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("a trailing sticky block joins a horizontal pull", function()
|
|
||||||
-- sticky, regular, player; moving right
|
|
||||||
local sticky = movable(2, 3, true)
|
|
||||||
local regular = movable(3, 3)
|
|
||||||
local possible, group = plan({sticky, regular}, {x = 3, y = 3}, {x = 1, y = 0})
|
|
||||||
assertTrue(possible)
|
|
||||||
assertIn(group, regular)
|
|
||||||
assertIn(group, sticky)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("a moving sticky block pulls a regular block behind it", function()
|
|
||||||
-- regular, sticky, player; moving right
|
|
||||||
local regular = movable(2, 3)
|
|
||||||
local sticky = movable(3, 3, true)
|
|
||||||
local possible, group = plan({regular, sticky}, {x = 3, y = 3}, {x = 1, y = 0})
|
|
||||||
assertTrue(possible)
|
|
||||||
assertIn(group, sticky)
|
|
||||||
assertIn(group, regular)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("sticky pull chains work vertically", function()
|
|
||||||
-- player moves down, pulling the two blocks above it
|
|
||||||
local sticky = movable(4, 2, true)
|
|
||||||
local regular = movable(4, 3)
|
|
||||||
local possible, group = plan({sticky, regular}, {x = 4, y = 3}, {x = 0, y = 1})
|
|
||||||
assertTrue(possible)
|
|
||||||
assertIn(group, regular)
|
|
||||||
assertIn(group, sticky)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("sticky pulls are symmetric in all four directions", function()
|
|
||||||
for _, direction in ipairs({
|
|
||||||
{x = 1, y = 0}, {x = -1, y = 0},
|
|
||||||
{x = 0, y = 1}, {x = 0, y = -1},
|
|
||||||
}) do
|
|
||||||
local player = {x = 4, y = 4}
|
|
||||||
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 possible, group = plan(
|
|
||||||
{sticky, regular},
|
|
||||||
{x = regular.x, y = regular.y},
|
|
||||||
direction
|
|
||||||
)
|
|
||||||
assertTrue(possible)
|
|
||||||
assertIn(group, regular)
|
|
||||||
assertIn(group, sticky)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("a sticky block shears off during orthogonal movement", function()
|
|
||||||
-- The regular block moves right; the sticky block above it stays behind.
|
|
||||||
local regular = movable(3, 4)
|
|
||||||
local stickyBranch = movable(3, 3, true)
|
|
||||||
local possible, group = plan({regular, stickyBranch}, {x = 3, y = 4}, {x = 1, y = 0})
|
|
||||||
assertTrue(possible)
|
|
||||||
assertIn(group, regular)
|
|
||||||
assertNotIn(group, stickyBranch)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("a blocked sheared sticky block does not stop movement", function()
|
|
||||||
local regular = movable(3, 4)
|
|
||||||
local stickyBranch = movable(3, 3, true)
|
|
||||||
local blockingWall = wall(4, 3)
|
|
||||||
local possible, group = plan({regular, stickyBranch, blockingWall}, {x = 3, y = 4}, {x = 1, y = 0})
|
|
||||||
assertTrue(possible)
|
|
||||||
assertIn(group, regular)
|
|
||||||
assertNotIn(group, stickyBranch)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("a skewer carries an orthogonal branch", function()
|
|
||||||
local regular = movable(3, 4)
|
|
||||||
local skewerBranch = movable(3, 3, false, nil, true)
|
|
||||||
local possible, group = plan({regular, skewerBranch}, {x = 3, y = 4}, {x = 1, y = 0})
|
|
||||||
assertTrue(possible)
|
|
||||||
assertIn(group, regular)
|
|
||||||
assertIn(group, skewerBranch)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("a blocked skewer branch stops its rigid movement group", function()
|
|
||||||
local regular = movable(3, 4)
|
|
||||||
local skewerBranch = movable(3, 3, false, nil, true)
|
|
||||||
local blockingWall = wall(4, 3)
|
|
||||||
local possible = plan({regular, skewerBranch, blockingWall}, {x = 3, y = 4}, {x = 1, y = 0})
|
|
||||||
assertFalse(possible)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("a spiky block pulls both orthogonal side blocks", function()
|
|
||||||
local spiky = movable(4, 4, false, nil, false, true)
|
|
||||||
local upper = movable(4, 3)
|
|
||||||
local lower = movable(4, 5)
|
|
||||||
local possible, group = plan(
|
|
||||||
{spiky, upper, lower}, {x = 4, y = 4}, {x = 1, y = 0}
|
|
||||||
)
|
|
||||||
|
|
||||||
assertTrue(possible)
|
|
||||||
assertIn(group, spiky)
|
|
||||||
assertIn(group, upper)
|
|
||||||
assertIn(group, lower)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("a spiky block releases one blocked side while pulling the other", function()
|
|
||||||
local spiky = movable(4, 4, false, nil, false, true)
|
|
||||||
local blockedUpper = movable(4, 3)
|
|
||||||
local freeLower = movable(4, 5)
|
|
||||||
local blockingWall = wall(5, 3)
|
|
||||||
local possible, group = plan(
|
|
||||||
{spiky, blockedUpper, freeLower, blockingWall},
|
|
||||||
{x = 4, y = 4}, {x = 1, y = 0}
|
|
||||||
)
|
|
||||||
|
|
||||||
assertTrue(possible)
|
|
||||||
assertIn(group, spiky)
|
|
||||||
assertNotIn(group, blockedUpper)
|
|
||||||
assertIn(group, freeLower)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("ordinary pushing still propagates through regular blocks", function()
|
|
||||||
local first = movable(3, 3)
|
|
||||||
local second = movable(4, 3)
|
|
||||||
local possible, group = plan({first, second}, {x = 3, y = 3}, {x = 1, y = 0})
|
|
||||||
assertTrue(possible)
|
|
||||||
assertIn(group, first)
|
|
||||||
assertIn(group, second)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("diagonal blocks do not stick", function()
|
|
||||||
local regular = movable(3, 3)
|
|
||||||
local diagonalSticky = movable(2, 2, true)
|
|
||||||
local possible, group = plan({regular, diagonalSticky}, {x = 3, y = 3}, {x = 1, y = 0})
|
|
||||||
assertTrue(possible)
|
|
||||||
assertIn(group, regular)
|
|
||||||
assertNotIn(group, diagonalSticky)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("multi-cell sticky blocks pull from every trailing occupied cell", function()
|
|
||||||
local tallRegular = movable(3, 3, false, {{x = 0, y = 0}, {x = 0, y = 1}})
|
|
||||||
local sticky = movable(2, 4, true)
|
|
||||||
local possible, group = plan({tallRegular, sticky}, {x = 3, y = 3}, {x = 1, y = 0})
|
|
||||||
assertTrue(possible)
|
|
||||||
assertIn(group, tallRegular)
|
|
||||||
assertIn(group, sticky)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("an L-shaped sticky pulls from its trailing face in every direction", function()
|
|
||||||
local lShape = {{x = 0, y = 0}, {x = 1, y = 0}, {x = 0, y = 1}}
|
|
||||||
local cases = {
|
|
||||||
{direction = {x = 1, y = 0}, trailing = {x = 2, y = 3}},
|
|
||||||
{direction = {x = -1, y = 0}, trailing = {x = 5, y = 3}},
|
|
||||||
{direction = {x = 0, y = 1}, trailing = {x = 3, y = 2}},
|
|
||||||
{direction = {x = 0, y = -1}, trailing = {x = 3, y = 5}},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, case in ipairs(cases) do
|
|
||||||
local stickyL = movable(3, 3, true, lShape)
|
|
||||||
local trailing = movable(case.trailing.x, case.trailing.y)
|
|
||||||
local possible, group = plan(
|
|
||||||
{stickyL, trailing}, {x = 3, y = 3}, case.direction
|
|
||||||
)
|
|
||||||
assertTrue(possible)
|
|
||||||
assertIn(group, stickyL)
|
|
||||||
assertIn(group, trailing)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
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
|
|
||||||
-- upper object is pinned against a wall; the lower one is free to follow.
|
|
||||||
local stickyL = movable(3, 3, true, {
|
|
||||||
{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 free = movable(2, 4)
|
|
||||||
local blockingWall = wall(3, 2)
|
|
||||||
local possible, group = plan(
|
|
||||||
{stickyL, pinned, free, blockingWall},
|
|
||||||
{x = 3, y = 3}, {x = 1, y = 0}
|
|
||||||
)
|
|
||||||
|
|
||||||
assertTrue(possible)
|
|
||||||
assertIn(group, stickyL)
|
|
||||||
assertIn(group, free)
|
|
||||||
assertNotIn(group, pinned)
|
|
||||||
end)
|
|
||||||
|
|
||||||
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.
|
|
||||||
local stickyT = movable(3, 3, true, {
|
|
||||||
{x = 0, y = 0}, {x = 1, y = 0}, {x = 2, y = 0},
|
|
||||||
{x = 1, y = 1},
|
|
||||||
})
|
|
||||||
local trailingLeft = movable(3, 4)
|
|
||||||
local trailingRight = movable(5, 4)
|
|
||||||
local trailingStem = movable(4, 5)
|
|
||||||
local possible, group = plan(
|
|
||||||
{stickyT, trailingLeft, trailingRight, trailingStem},
|
|
||||||
{x = 3, y = 3}, {x = 0, y = -1}
|
|
||||||
)
|
|
||||||
|
|
||||||
assertTrue(possible)
|
|
||||||
assertIn(group, stickyT)
|
|
||||||
assertIn(group, trailingLeft)
|
|
||||||
assertIn(group, trailingRight)
|
|
||||||
assertIn(group, trailingStem)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("an L-shaped spiky block grabs moveables across several exposed faces", function()
|
|
||||||
local spikyL = movable(3, 3, false, {
|
|
||||||
{x = 0, y = 0}, {x = 1, y = 0}, {x = 0, y = 1},
|
|
||||||
}, false, true)
|
|
||||||
local above = movable(3, 2)
|
|
||||||
local below = movable(3, 5)
|
|
||||||
local behind = movable(2, 3)
|
|
||||||
local possible, group = plan(
|
|
||||||
{spikyL, above, below, behind},
|
|
||||||
{x = 3, y = 3}, {x = 1, y = 0}
|
|
||||||
)
|
|
||||||
|
|
||||||
assertTrue(possible)
|
|
||||||
assertIn(group, spikyL)
|
|
||||||
assertIn(group, above)
|
|
||||||
assertIn(group, below)
|
|
||||||
assertIn(group, behind)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("production sticky, skewer, and spiky blocks advertise distinct behavior", function()
|
|
||||||
class = require "libs/middleclass"
|
|
||||||
Color = class("TestColor")
|
|
||||||
function Color:initialize(name) self.name = name end
|
|
||||||
function Color:getname() return self.name end
|
|
||||||
|
|
||||||
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 regular = GenericMoveable:new(2, 2, "red", sprite, "box")
|
|
||||||
local sticky = GenericSticky:new(3, 2, "red", sprite, "sticky_box")
|
|
||||||
local skewer = GenericSkewer:new(4, 2, "red", sprite, "skewer_box")
|
|
||||||
local spiky = GenericSpiky:new(5, 2, "red", sprite, "spiky_box")
|
|
||||||
|
|
||||||
assertTrue(regular:canMove())
|
|
||||||
assertFalse(regular:isSticky())
|
|
||||||
assertTrue(sticky:canMove())
|
|
||||||
assertTrue(sticky:isSticky())
|
|
||||||
assertFalse(sticky:isSkewer())
|
|
||||||
assertTrue(skewer:canMove())
|
|
||||||
assertFalse(skewer:isSticky())
|
|
||||||
assertTrue(skewer:isSkewer())
|
|
||||||
assertFalse(skewer:isSpiky())
|
|
||||||
assertTrue(spiky:canMove())
|
|
||||||
assertFalse(spiky:isSticky())
|
|
||||||
assertFalse(spiky:isSkewer())
|
|
||||||
assertTrue(spiky:isSpiky())
|
|
||||||
end)
|
|
||||||
|
|
||||||
local function loadRoomClass()
|
|
||||||
love = {
|
|
||||||
audio = {
|
|
||||||
newSource = function()
|
|
||||||
return {stop = function() end, play = function() end}
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
require "room"
|
|
||||||
end
|
|
||||||
|
|
||||||
local function roomWith(player, entities, width, height)
|
|
||||||
gridWidth, gridHeight = width or 8, height or 8
|
|
||||||
local matrix = {}
|
|
||||||
for y = 1, gridHeight do
|
|
||||||
matrix[y] = {}
|
|
||||||
for x = 1, gridWidth do matrix[y][x] = false end
|
|
||||||
end
|
|
||||||
for _, entity in ipairs(entities) do
|
|
||||||
for _, cell in ipairs(entity:getOccupiedCells()) do
|
|
||||||
matrix[cell.y][cell.x] = entity
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local room = setmetatable({
|
|
||||||
activeColors = {red = true},
|
|
||||||
colorsPlayerHas = {"red"},
|
|
||||||
player = {red = player},
|
|
||||||
collidableMatrices = {red = matrix},
|
|
||||||
}, {__index = Room})
|
|
||||||
room.playerJoinCheck = function() end
|
|
||||||
room.tick = function() end
|
|
||||||
return room
|
|
||||||
end
|
|
||||||
|
|
||||||
test("Room applies the sticky-regular-player pull", function()
|
|
||||||
loadRoomClass()
|
|
||||||
local sticky = movable(2, 3, true)
|
|
||||||
local regular = movable(3, 3)
|
|
||||||
local player = movable(4, 3)
|
|
||||||
local room = roomWith(player, {sticky, regular})
|
|
||||||
|
|
||||||
room:movePlayer("right")
|
|
||||||
|
|
||||||
assertTrue(sticky.x == 3, "sticky block should move one cell right")
|
|
||||||
assertTrue(regular.x == 4, "regular block should move one cell right")
|
|
||||||
assertTrue(player.x == 5, "player should move one cell right")
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("Room pulls sticky out of a pinned concave moveable", function()
|
|
||||||
loadRoomClass()
|
|
||||||
-- Before: ___ After: p__
|
|
||||||
-- px_ sx_
|
|
||||||
-- sm_ _m_
|
|
||||||
-- mm_ mm_
|
|
||||||
local player = movable(1, 2)
|
|
||||||
local wall = wall(2, 2)
|
|
||||||
local sticky = movable(1, 3, true)
|
|
||||||
local concave = movable(1, 3, false, {
|
|
||||||
{x = 1, y = 0},
|
|
||||||
{x = 0, y = 1}, {x = 1, y = 1},
|
|
||||||
})
|
|
||||||
local room = roomWith(player, {wall, sticky, concave})
|
|
||||||
|
|
||||||
room:movePlayer("up")
|
|
||||||
|
|
||||||
assertTrue(player.x == 1 and player.y == 1)
|
|
||||||
assertTrue(sticky.x == 1 and sticky.y == 2, "sticky should peel away from the concave block")
|
|
||||||
assertTrue(concave.x == 1 and concave.y == 3, "pinned concave block should stay put")
|
|
||||||
assertTrue(wall.x == 2 and wall.y == 2)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("Room moves an L-shaped sticky and both available trailing pulls", function()
|
|
||||||
loadRoomClass()
|
|
||||||
local player = movable(3, 2)
|
|
||||||
local stickyL = movable(3, 3, true, {
|
|
||||||
{x = 0, y = 0}, {x = 1, y = 0}, {x = 0, y = 1},
|
|
||||||
})
|
|
||||||
local trailingLeft = movable(3, 5)
|
|
||||||
local trailingRight = movable(4, 4)
|
|
||||||
local room = roomWith(player, {stickyL, trailingLeft, trailingRight})
|
|
||||||
|
|
||||||
room:movePlayer("up")
|
|
||||||
|
|
||||||
assertTrue(player.x == 3 and player.y == 1)
|
|
||||||
assertTrue(stickyL.x == 3 and stickyL.y == 2)
|
|
||||||
assertTrue(trailingLeft.x == 3 and trailingLeft.y == 4)
|
|
||||||
assertTrue(trailingRight.x == 4 and trailingRight.y == 3)
|
|
||||||
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()
|
|
||||||
loadRoomClass()
|
|
||||||
local player = movable(4, 4)
|
|
||||||
local front = movable(5, 4)
|
|
||||||
local bridgeRight = movable(5, 3, false, nil, true)
|
|
||||||
local bridgeMiddle = movable(4, 3, false, nil, true)
|
|
||||||
local bridgeLeft = movable(3, 3, false, nil, true)
|
|
||||||
local rear = movable(3, 4)
|
|
||||||
local component = {front, bridgeRight, bridgeMiddle, bridgeLeft, rear}
|
|
||||||
local room = roomWith(player, component)
|
|
||||||
|
|
||||||
room:movePlayer("right")
|
|
||||||
|
|
||||||
for _, entity in ipairs(component) do
|
|
||||||
assertTrue(entity.x <= 6, "an entity in the shared component moved more than once")
|
|
||||||
end
|
|
||||||
assertTrue(front.x == 6)
|
|
||||||
assertTrue(bridgeRight.x == 6)
|
|
||||||
assertTrue(bridgeMiddle.x == 5)
|
|
||||||
assertTrue(bridgeLeft.x == 4)
|
|
||||||
assertTrue(rear.x == 4)
|
|
||||||
assertTrue(player.x == 5)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("Room pushes spiky onward while a blocked side shears off", function()
|
|
||||||
loadRoomClass()
|
|
||||||
local player = movable(3, 4)
|
|
||||||
local spiky = movable(4, 4, false, nil, false, true)
|
|
||||||
local blockedUpper = movable(4, 3)
|
|
||||||
local freeLower = movable(4, 5)
|
|
||||||
local blockingWall = wall(5, 3)
|
|
||||||
local room = roomWith(player, {spiky, blockedUpper, freeLower, blockingWall})
|
|
||||||
|
|
||||||
room:movePlayer("right")
|
|
||||||
|
|
||||||
assertTrue(player.x == 4 and player.y == 4)
|
|
||||||
assertTrue(spiky.x == 5 and spiky.y == 4)
|
|
||||||
assertTrue(blockedUpper.x == 4 and blockedUpper.y == 3)
|
|
||||||
assertTrue(freeLower.x == 5 and freeLower.y == 5)
|
|
||||||
assertTrue(blockingWall.x == 5 and blockingWall.y == 3)
|
|
||||||
end)
|
|
||||||
|
|
||||||
test("every moveable base box gets all three synthetic behavior variants", function()
|
|
||||||
local BoxVariants = require "box_variants"
|
|
||||||
local boxSprites = {red = "box_r.png"}
|
|
||||||
local boxCells = {red = {{x = 1, y = 1}}}
|
|
||||||
local shapeSprites = {blue = "box_shape_b.png"}
|
|
||||||
local shapeCells = {blue = {{x = 1, y = 1}, {x = 2, 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 = {}},
|
|
||||||
}
|
|
||||||
|
|
||||||
BoxVariants.addTo(assets)
|
|
||||||
|
|
||||||
assertTrue(assets.sticky_box.class == "sticky")
|
|
||||||
assertTrue(assets.sticky_box.directory == "sticky")
|
|
||||||
assertTrue(assets.sticky_box.synthetic)
|
|
||||||
assertTrue(assets.sticky_box.goopy)
|
|
||||||
assertTrue(assets.sticky_box.sprites == boxSprites, "variant should share base sprites")
|
|
||||||
assertTrue(assets.sticky_box.cells == boxCells, "variant should share collision cells")
|
|
||||||
assertTrue(assets["sticky_box_2x1_11"].sprites == shapeSprites)
|
|
||||||
assertTrue(assets["sticky_box_2x1_11"].cells == shapeCells)
|
|
||||||
assertTrue(assets["sticky_box_2x2_10-11"].sprites == lSprites)
|
|
||||||
assertTrue(assets["sticky_box_2x2_10-11"].cells == lCells)
|
|
||||||
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)
|
|
||||||
|
|
||||||
print("movement/block-variant tests passed: " .. passed)
|
|
||||||
36
world.lua
36
world.lua
|
|
@ -171,16 +171,7 @@ function World:drawRoomPreview(name, x, y, size)
|
||||||
local path = props.sprites[color]
|
local path = props.sprites[color]
|
||||||
if path then
|
if path then
|
||||||
love.graphics.setColor(gColor[color]:set())
|
love.graphics.setColor(gColor[color]:set())
|
||||||
local sprite = loadSprite(path)
|
love.graphics.draw(loadSprite(path), x + (object.x - 1) * 16 * scale, y + (object.y - 1) * 16 * scale, 0, scale, scale)
|
||||||
if props.goopy and drawGoopySprite then
|
|
||||||
drawGoopySprite(sprite, x + (object.x - 1) * 16 * scale, y + (object.y - 1) * 16 * scale, scale)
|
|
||||||
elseif props.skewer and drawSkewerSprite then
|
|
||||||
drawSkewerSprite(sprite, props.cells and props.cells[color], x + (object.x - 1) * 16 * scale, y + (object.y - 1) * 16 * scale, scale)
|
|
||||||
elseif props.spiky and drawSpikySprite then
|
|
||||||
drawSpikySprite(sprite, props.cells and props.cells[color], x + (object.x - 1) * 16 * scale, y + (object.y - 1) * 16 * scale, scale)
|
|
||||||
else
|
|
||||||
love.graphics.draw(sprite, x + (object.x - 1) * 16 * scale, y + (object.y - 1) * 16 * scale, 0, scale, scale)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -189,30 +180,6 @@ function World:drawRoomPreview(name, x, y, size)
|
||||||
love.graphics.setScissor()
|
love.graphics.setScissor()
|
||||||
end
|
end
|
||||||
|
|
||||||
function World:drawHoveredRoomName()
|
|
||||||
local mouseX, mouseY = love.mouse.getPosition()
|
|
||||||
local cellX, cellY = self:getCellFromMousePos(mouseX, mouseY)
|
|
||||||
local name = self:isInBounds(cellX, cellY) and self:roomAt(cellX, cellY)
|
|
||||||
if not name then return end
|
|
||||||
|
|
||||||
local font = love.graphics.getFont()
|
|
||||||
local padding = 6
|
|
||||||
local tooltipW = math.min(font:getWidth(name) + padding * 2, width - 8)
|
|
||||||
local tooltipH = font:getHeight() + padding * 2
|
|
||||||
local tooltipX = math.min(mouseX + 14, width - tooltipW - 4)
|
|
||||||
local tooltipY = math.min(mouseY + 14, height - tooltipH - 4)
|
|
||||||
tooltipX = math.max(4, tooltipX)
|
|
||||||
tooltipY = math.max(4, tooltipY)
|
|
||||||
|
|
||||||
-- Keep the label anchored beside the pointer, while keeping it entirely in
|
|
||||||
-- the playable map rather than allowing it to spill into the editor panel.
|
|
||||||
love.graphics.setColor(0, 0, 0, 0.9)
|
|
||||||
love.graphics.rectangle("fill", tooltipX, tooltipY, tooltipW, tooltipH)
|
|
||||||
love.graphics.setColor(1, 1, 1, 1)
|
|
||||||
love.graphics.rectangle("line", tooltipX, tooltipY, tooltipW, tooltipH)
|
|
||||||
love.graphics.printf(name, tooltipX + padding, tooltipY + padding, tooltipW - padding * 2, "left")
|
|
||||||
end
|
|
||||||
|
|
||||||
function World:draw()
|
function World:draw()
|
||||||
local cell = width / self.width
|
local cell = width / self.width
|
||||||
love.graphics.setColor(12 / 255, 12 / 255, 20 / 255, 1)
|
love.graphics.setColor(12 / 255, 12 / 255, 20 / 255, 1)
|
||||||
|
|
@ -226,5 +193,4 @@ function World:draw()
|
||||||
love.graphics.rectangle("line", screenX, screenY, cell, cell)
|
love.graphics.rectangle("line", screenX, screenY, cell, cell)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
self:drawHoveredRoomName()
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue