diff --git a/_helpers.lua b/_helpers.lua index ccf5465..b48b25d 100644 --- a/_helpers.lua +++ b/_helpers.lua @@ -32,6 +32,41 @@ function writeToSource(relPath, contents) return love.filesystem.write(relPath, contents) end +-- The editor occasionally needs to rename a source level after it has written +-- the replacement. Keep that operation alongside writeToSource so packaged +-- games still fall back to LÖVE's writable save directory. +function removeFromSource(relPath) + local base = love.filesystem.getSource() + local path = base .. "/" .. relPath + local file = io.open(path, "r") + if file then + file:close() + return os.remove(path) + end + return love.filesystem.remove(relPath) +end + +-- Runtime saves live in LÖVE's write directory. This deliberately uses only +-- love.filesystem operations: they cannot delete files from the source tree. +function clearRuntimeSaveData() + local removed = 0 + local function removeTree(path) + if love.filesystem.isDirectory(path) then + for _, child in ipairs(love.filesystem.getDirectoryItems(path)) do + removeTree(path .. "/" .. child) + end + end + if love.filesystem.remove(path) then removed = removed + 1 end + end + + -- These are the only files this game writes through love.filesystem: room + -- autosaves and the generated asset cache. Source levels are read-only to + -- this API and cannot be removed here. + removeTree("rooms") + if love.filesystem.remove("assets.prop") then removed = removed + 1 end + return removed +end + -- Read from the project tree directly so a stale copy in LÖVE's save directory -- can't shadow the real source file. Falls back to love.filesystem for a -- packaged .love. diff --git a/art/_ui/clear_saves.png b/art/_ui/clear_saves.png new file mode 100644 index 0000000..7bdd342 Binary files /dev/null and b/art/_ui/clear_saves.png differ diff --git a/art/_ui/clear_saves.svg b/art/_ui/clear_saves.svg new file mode 100644 index 0000000..1cc349d --- /dev/null +++ b/art/_ui/clear_saves.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/level_editor/editor.lua b/level_editor/editor.lua index b41b004..d1fca84 100644 --- a/level_editor/editor.lua +++ b/level_editor/editor.lua @@ -9,6 +9,7 @@ local forwardSprite = love.graphics.newImage("art/_ui/forward.png") local folderSprite = love.graphics.newImage("art/_ui/folder.png") local cancelSprite = love.graphics.newImage("art/_ui/cancel.png") local confirmSprite = love.graphics.newImage("art/_ui/confirm.png") +local clearSavesSprite = love.graphics.newImage("art/_ui/clear_saves.png") local selectedFolder = nil local selectedAsset = nil @@ -28,10 +29,150 @@ local assetButtons = { {} } --table for rows so we can do stuff local editingAssetButtons = {} local labels = {} +local editorRoomName = nil +local roomNameField = nil +local editorStatus = nil +local clearSaveConfirmationExpires = 0 +local worldSeedConfirmationExpires = 0 +local worldMode = "place" +local worldRoomButtons = {} +local worldControls = {} +local worldPage = 1 +local worldPageSize = 5 + +local function buttonContains(button, x, y) + return x > button.x and x < button.x + button.w and y > button.y and y < button.y + button.h +end + +local function rebuildWorldRoomButtons() + worldRoomButtons = {} + local names = gameWorld:listRoomNames() + local pages = math.max(1, math.ceil(#names / worldPageSize)) + worldPage = constrain(worldPage, 1, pages) + local first = (worldPage - 1) * worldPageSize + 1 + for index = first, math.min(first + worldPageSize - 1, #names) do + local roomName = names[index] + local row = index - first + table.insert(worldRoomButtons, { name = roomName, x = 10, y = 36 + row * 68, w = sideBarBox.w - 20, h = 60 }) + end + worldControls = { + { text = worldMode == "place" and "MODE: PLACE ROOMS" or "MODE: OPEN LEVELS", x = 10, y = sideBarBox.h - 194, w = sideBarBox.w - 20, h = 34, + onClic = function() worldMode = worldMode == "place" and "jump" or "place"; rebuildWorldRoomButtons() end }, + { text = "RESEED OLD EXITS", x = 10, y = sideBarBox.h - 152, w = sideBarBox.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 = sideBarBox.h - 110, w = sideBarBox.w - 20, h = 34, + onClic = function(buttonX) + if buttonX < sideBarBox.w / 2 then worldPage = math.max(1, worldPage - 1) else worldPage = math.min(pages, worldPage + 1) end + rebuildWorldRoomButtons() + end }, + } +end + +local function drawWorldSidebar() + 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, 10, sideBarBox.w - 16, "center") + for _, button in ipairs(worldRoomButtons) do + love.graphics.setColor(button.name == selectedRoom and 255 or 100, button.name == selectedRoom and 255 or 100, button.name == selectedRoom and 255 or 100, 255) + love.graphics.rectangle("line", button.x, button.y, button.w, button.h) + gameWorld:drawRoomPreview(button.name, button.x + 4, button.y + 4, button.h - 8) + love.graphics.setColor(gColor.white:set()) + love.graphics.printf(button.name, button.x + button.h + 4, button.y + 21, button.w - button.h - 8, "left") + end + for _, control in ipairs(worldControls) do + love.graphics.setColor(255, 255, 255, 255) + love.graphics.rectangle("line", control.x, control.y, control.w, control.h) + love.graphics.printf(control.text, control.x, control.y + 9, control.w, "center") + end + uiButtons.world:draw() +end + +local function validRoomName(name) + name = (name or ""):match("^%s*(.-)%s*$") + if name == "" then return nil, "Enter a level name." end + if not name:match("^[%w][%w_-]*$") then + return nil, "Use letters, numbers, _ or - for level names." + end + return name +end + +local function beginRoomNameEdit() + activeField = "roomName" + inputText = "" + response = "" + roomNameField:editText("") + editorStatus = "Type a name, press Enter, then Save." +end + +local function saveRoomFromEditor() + local requestedName = editorRoomName or currentRoom.name + if activeField == "roomName" then requestedName = inputText end + local name, err = validRoomName(requestedName) + if not name then + editorStatus = err + return + end + + local oldName = currentRoom.name + if name ~= oldName and readFromSource("rooms/" .. name .. ".sav") then + editorStatus = "A source level named '" .. name .. "' already exists." + return + end + + if not currentRoom:saveToSource(name) then + editorStatus = "Could not save '" .. name .. "'." + return + end + + -- A changed name is a rename, not an accidental duplicate. Do this only + -- after the new source file is safely written. + if oldName and oldName ~= name then + removeFromSource("rooms/" .. oldName .. ".sav") + gameWorld:renameRoom(oldName, name) + end + gameWorld:invalidatePreview(name) + if editorView == "world" then rebuildWorldRoomButtons() end + currentRoom.name = name + editorRoomName = name + roomNameField:editText(name) + activeField = nil + editorStatus = "Saved source level '" .. name .. ".'" +end + +local function clearRuntimeSaves() + local now = love.timer.getTime() + if now > clearSaveConfirmationExpires then + clearSaveConfirmationExpires = now + 3 + editorStatus = "Click the clear-save icon again within 3 seconds to erase runtime saves." + return + end + + local removed = clearRuntimeSaveData() + clearSaveConfirmationExpires = 0 + local roomName = currentRoom.name + if roomName then + currentRoom = Room:new{ name = roomName } + setEditorRoomName(currentRoom.name) + editorStatus = "Cleared " .. removed .. " runtime save item(s) and reloaded '" .. roomName .. ".'" + else + editorStatus = "Cleared " .. removed .. " runtime save item(s). No named level to reload." + end +end function _initLevelEditor() gameAssets = getAllAssets() + editorRoomName = "" --create folder buttons local counter = 1 @@ -70,8 +211,7 @@ function _initLevelEditor() onClic = { left = function() if editorView == "room" then - print("saveing!!") - currentRoom:save() + saveRoomFromEditor() end end, right = function() @@ -87,11 +227,7 @@ function _initLevelEditor() sprite = love.graphics.newImage("art/_ui/world.png"), onClic = { left = function() - print(editorView) - if editorView == "room" then - print("saveing!!") - currentRoom:save() - end + if editorView == "world" then assetsView() else worldView() end end, right = function() @@ -99,6 +235,17 @@ function _initLevelEditor() }, x = sideBarBox.w * (3 / 4), y = sideBarBox.h * (9 / 10) + }, "world") + + _addUIButton(createUIElement{ + name = "clearSaves", + sprite = clearSavesSprite, + onClic = { + left = clearRuntimeSaves, + }, + x = sideBarBox.w / 2, + y = sideBarBox.h * (9 / 10), + scale = drawScale / 3 }) for _, color in ipairs({"white", "red", "green", "blue"}) do @@ -120,6 +267,22 @@ function _initLevelEditor() }) end + roomNameField = createUIElement{ + name = "roomName", + text = editorRoomName, + x = 25, + y = sideBarBox.h * (7 / 10), + onClic = { + left = beginRoomNameEdit, + } + } + _addUIButton(roomNameField, "roomName") + +end + +function setEditorRoomName(name) + editorRoomName = name or "" + if roomNameField then roomNameField:editText(editorRoomName) end end function setSelectedColor(color) @@ -325,7 +488,41 @@ function assetsView() end function worldView() + editorView = "world" + activeField = nil + selectedFolder = nil + worldMode = "place" + worldPage = 1 + rebuildWorldRoomButtons() +end +local function newWorldRoomName(cellX, cellY) + local base = string.format("room_%02d_%02d", cellX, cellY) + local name, suffix = base, 2 + while gameWorld:roomLocation(name) or readFromSource("rooms/" .. name .. ".sav") do + name = base .. "_" .. suffix + suffix = suffix + 1 + end + return name +end + +local function openWorldRoom(cellX, cellY) + local name = gameWorld:roomAt(cellX, cellY) + if not name then + name = newWorldRoomName(cellX, cellY) + currentRoom = Room:new{ name = name } + -- Creating from an empty cell is a real level creation, not merely a + -- transient map reference, so it immediately joins the room picker. + currentRoom:saveToSource(name) + gameWorld:placeRoom(name, cellX, cellY) + gameWorld:invalidatePreview(name) + editorStatus = "Created and opened '" .. name .. ".'" + else + currentRoom = Room:new{ name = name } + editorStatus = "Editing '" .. name .. ".'" + end + setEditorRoomName(currentRoom.name) + assetsView() end function editorMouseHandler(x, y, button) @@ -361,11 +558,43 @@ function editorMouseHandler(x, y, button) uiElement:onClic(button) end end + elseif editorView == "world" then + for _, roomButton in ipairs(worldRoomButtons) do + if buttonContains(roomButton, x, y) then + if selectedRoom == roomButton.name then + selectedRoom = nil + editorStatus = "Room brush cleared. Click the map to open or create a level." + else + selectedRoom = roomButton.name + editorStatus = "Selected '" .. selectedRoom .. "' for placement. Click it again to clear." + end + return + end + end + for _, control in ipairs(worldControls) do + if buttonContains(control, x, y) then + control:onClic(x) + return + end + end + if uiButtons.world:clicIsInBox(x, y) then uiButtons.world:onClic(button) end end else -- click landed on the grid (game area): place the selected asset if editorView == "room" and button == 1 and selectedAsset then currentRoom:attemptPlace(selectedAsset, gameAssets[selectedAsset], x, y, selectedColor) + elseif editorView == "world" then + local cellX, cellY = gameWorld:getCellFromMousePos(x, y) + if not gameWorld:isInBounds(cellX, cellY) then return end + if button == 2 then + local removed, name = gameWorld:removeRoom(cellX, cellY) + if removed then editorStatus = "Removed '" .. name .. "' from the world." end + elseif worldMode == "jump" or not selectedRoom then + openWorldRoom(cellX, cellY) + elseif selectedRoom then + gameWorld:placeRoom(selectedRoom, cellX, cellY) + editorStatus = "Placed '" .. selectedRoom .. "' at " .. cellX .. ", " .. cellY .. "." + end end end end @@ -374,15 +603,18 @@ function editorTextHandler(text) if editorView == "asset" and activeField then inputText = text editingAssetButtons[activeField]:editText(text) + elseif editorView == "room" and activeField == "roomName" then + inputText = text + roomNameField:editText(text) end end function editorCompleteResponse() - if editorView == "asset" then + if editorView == "asset" and activeField then assetProperties[activeField] = inputText - print(assetProperties[activeField], "BLELERLLLRLRLRR") + elseif editorView == "room" and activeField == "roomName" then + editorRoomName = inputText end - print(activeField, assetProperties[activeField], "ass") activeField = nil end @@ -396,6 +628,8 @@ end function editorDraw() if editorView == "room" then + love.graphics.setColor(gColor.white:set()) + love.graphics.printf("LEVEL NAME", 0, sideBarBox.h * (7 / 10) - 24, sideBarBox.w, "center") for _, button in pairs(uiButtons) do button:draw() end @@ -412,9 +646,15 @@ function editorDraw() for _, button in pairs(editingAssetButtons) do button:draw() end + elseif editorView == "world" then + drawWorldSidebar() end for _, label in pairs(labels) do label:draw() end + if editorStatus then + love.graphics.setColor(gColor.white:set()) + love.graphics.printf(editorStatus, 10, sideBarBox.h * (6 / 10), sideBarBox.w - 20, "center") + end end diff --git a/level_editor/gui.lua b/level_editor/gui.lua index 7086c7f..e7264f4 100644 --- a/level_editor/gui.lua +++ b/level_editor/gui.lua @@ -26,7 +26,7 @@ function GUI:initialize(t) if t.sprite then print(self.name) - self.scale = drawScale * 2 / 3 + self.scale = t.scale or drawScale * 2 / 3 self.w = t.sprite:getWidth() * self.scale self.h = t.sprite:getHeight() * self.scale self.sprite = t.sprite @@ -126,4 +126,4 @@ end function GUI:sha() return math.random(-worldSha, worldSha) * .5 -end \ No newline at end of file +end diff --git a/main.lua b/main.lua index 24d7dc8..519e7fa 100644 --- a/main.lua +++ b/main.lua @@ -10,6 +10,8 @@ require "_helpers" require "input" require "color" require "room" +require "world" +require "world_seed" require "shaders" require "level_editor/editor" @@ -20,6 +22,7 @@ function love.load() _initGridVars() _initSounds() _initGlobalColors() + gameWorld = World:new() _initLevelEditor() gameCanvas = love.graphics.newCanvas() @@ -32,6 +35,7 @@ function love.load() currentRoom = Room:new{ name = "start" -- TEMP: load a migrated level to eyeball it; revert once the room picker lands } + setEditorRoomName(currentRoom.name) response = "" end @@ -48,7 +52,11 @@ function love.draw() love.graphics.setCanvas(gameCanvas) love.graphics.clear() love.graphics.setBlendMode("screen") - currentRoom:draw() + if editorView == "world" then + gameWorld:draw() + else + currentRoom:draw() + end love.graphics.setCanvas(finalCanvas) love.graphics.clear() @@ -85,9 +93,9 @@ function love.draw() end function love.keypressed(key) - if Input.isPlayerMove(key) then + if editorView ~= "world" and Input.isPlayerMove(key) then currentRoom:movePlayer(key) - elseif Input.isPlayerSwitch(key) then + elseif editorView ~= "world" and Input.isPlayerSwitch(key) then local c = Input.playerColor(key) currentRoom:switchPlayerColor(c) end @@ -115,7 +123,7 @@ function love.keypressed(key) end function love.mousepressed(x, y, button) - if button == 2 then + if editorView == "room" and button == 2 then currentRoom:attemptDelete(x, y, selectedColor) end editorMouseHandler(x, y, button) @@ -175,4 +183,3 @@ function _initSounds() static:setVolume(.15) static:play() end - diff --git a/room.lua b/room.lua index 8a7e695..0d44721 100644 --- a/room.lua +++ b/room.lua @@ -423,6 +423,10 @@ function Room:tick() for _, door in pairs(self.doors) do door:setColors(setDoorColors) end + -- Door:setColors can change collision from unmoveable to none. Rebuild once + -- more after that state change so an open-looking door is passable on the + -- very next input, rather than leaving the previous tick's solid cell here. + self:createCollideablesMatrix() self:nextRoomCheck() end @@ -653,7 +657,10 @@ function Room:attemptPlace(name, props, mouseX, mouseY, color) end function Room:load(name) - local room = readFromSource("rooms/" .. name .. ".sav") + -- Runtime rooms are game state: let LÖVE resolve them from its save + -- directory, where Room:save writes autosaves. Source files remain the + -- shipped/editor versions until the editor explicitly saves one. + local room = love.filesystem.read("rooms/" .. name .. ".sav") if not room then -- no save yet: start with a blank room to build in the editor print("no save for '" .. name .. "', starting blank") @@ -672,10 +679,19 @@ function Room:save() local name = self.name or "test" local room = TSerial.pack(self:serialize(), nil, true) - local success = writeToSource("rooms/" .. name .. ".sav", room) + local success = love.filesystem.write("rooms/" .. name .. ".sav", room) if not success then error "great job u fool" end end +-- This is intentionally separate from Room:save. It is the editor's explicit +-- "write this level into the project" action; gameplay autosaves must never +-- call it. +function Room:saveToSource(name) + name = name or self.name or "test" + local room = TSerial.pack(self:serialize(), nil, true) + return writeToSource("rooms/" .. name .. ".sav", room) +end + function Room:serialize() local objects = {} @@ -734,14 +750,18 @@ function nextRoom(pos) newPos.y = gridHeight end + local currentName = currentRoom.name + local nextName = gameWorld and currentName and gameWorld:getNeighbor(currentName, exit) currentRoom:save() - currentRoom = Room:new{ - -- name = "test", + -- Missing map neighbours intentionally wrap back into the room just exited. + -- A placed neighbour gets the same carried player state at its opposite edge. + currentRoom = Room:new{ + name = nextName or currentName, player = { x = newPos.x, y = newPos.y, activeColors = currentRoom:getActiveColors() } } + if currentRoom.name then setEditorRoomName(currentRoom.name) end end - diff --git a/rooms/2.sav b/rooms/2.sav index 81d646c..dc5990c 100644 --- a/rooms/2.sav +++ b/rooms/2.sav @@ -1,868 +1,872 @@ { objects={ { - y=6, + x=1, + class="player", + color="red", + y=1 + }, + { + x=11, class="door", - x=11, - color="red" + y=6 }, { - y=11, + x=6, class="door", - x=6, - color="green" + y=11 }, { - y=1, - class="static_wall", x=1, - color="blue" + class="static_wall", + color="green", + y=1 }, { - y=1, - class="static_wall", - x=1, - color="green" - }, - { - y=1, - class="static_wall", - x=1, - color="red" - }, - { - y=1, - class="static_wall", x=2, - color="red" + class="static_wall", + color="green", + y=1 }, { - y=1, + x=3, class="static_wall", + color="green", + y=1 + }, + { + x=4, + class="static_wall", + color="green", + y=1 + }, + { + x=5, + class="static_wall", + color="green", + y=1 + }, + { + x=6, + class="static_wall", + color="green", + y=1 + }, + { + x=7, + class="static_wall", + color="green", + y=1 + }, + { + x=8, + class="static_wall", + color="green", + y=1 + }, + { + x=9, + class="static_wall", + color="green", + y=1 + }, + { + x=10, + class="static_wall", + color="green", + y=1 + }, + { + x=11, + class="static_wall", + color="green", + y=1 + }, + { + x=1, + class="static_wall", + color="green", + y=2 + }, + { + x=11, + class="static_wall", + color="green", + y=2 + }, + { + x=1, + class="static_wall", + color="green", + y=3 + }, + { + x=11, + class="static_wall", + color="green", + y=3 + }, + { + x=1, + class="static_wall", + color="green", + y=4 + }, + { + x=3, + class="static_wall", + color="green", + y=4 + }, + { + x=9, + class="static_wall", + color="green", + y=4 + }, + { + x=11, + class="static_wall", + color="green", + y=4 + }, + { + x=1, + class="static_wall", + color="green", + y=5 + }, + { + x=4, + class="static_wall", + color="green", + y=5 + }, + { + x=5, + class="static_wall", + color="green", + y=5 + }, + { + x=7, + class="static_wall", + color="green", + y=5 + }, + { + x=8, + class="static_wall", + color="green", + y=5 + }, + { + x=11, + class="static_wall", + color="green", + y=5 + }, + { + x=1, + class="static_wall", + color="green", + y=6 + }, + { + x=1, + class="static_wall", + color="green", + y=7 + }, + { + x=4, + class="static_wall", + color="green", + y=7 + }, + { + x=5, + class="static_wall", + color="green", + y=7 + }, + { + x=7, + class="static_wall", + color="green", + y=7 + }, + { + x=8, + class="static_wall", + color="green", + y=7 + }, + { + x=11, + class="static_wall", + color="green", + y=7 + }, + { + x=1, + class="static_wall", + color="green", + y=8 + }, + { + x=3, + class="static_wall", + color="green", + y=8 + }, + { + x=9, + class="static_wall", + color="green", + y=8 + }, + { + x=11, + class="static_wall", + color="green", + y=8 + }, + { + x=1, + class="static_wall", + color="green", + y=9 + }, + { + x=11, + class="static_wall", + color="green", + y=9 + }, + { + x=1, + class="static_wall", + color="green", + y=10 + }, + { + x=11, + class="static_wall", + color="green", + y=10 + }, + { + x=1, + class="static_wall", + color="green", + y=11 + }, + { x=2, - color="blue" + class="static_wall", + color="green", + y=11 }, { - y=1, + 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=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=11 + }, + { + x=6, + class="box", + color="green", + y=5 + }, + { + x=9, + class="box", + color="green", + y=5 + }, + { + x=3, + class="box", + color="green", + y=7 + }, + { + x=6, + class="box", + color="green", + y=7 + }, + { + x=1, + class="static_wall", + color="blue", + y=1 + }, + { x=2, - color="green" + class="static_wall", + color="blue", + y=1 }, { - y=1, - class="static_wall", x=3, - color="green" + class="static_wall", + color="blue", + y=1 }, { - y=1, - class="static_wall", - x=3, - color="blue" - }, - { - y=1, - class="static_wall", - x=3, - color="red" - }, - { - y=1, - class="static_wall", x=4, - color="blue" + class="static_wall", + color="blue", + y=1 }, { - y=1, - class="static_wall", - x=4, - color="red" - }, - { - y=1, - class="static_wall", - x=4, - color="green" - }, - { - y=1, - class="static_wall", x=5, - color="blue" + class="static_wall", + color="blue", + y=1 }, { - y=1, - class="static_wall", - x=5, - color="green" - }, - { - y=1, - class="static_wall", - x=5, - color="red" - }, - { - y=1, - class="static_wall", x=6, - color="blue" + class="static_wall", + color="blue", + y=1 }, { - y=1, - class="static_wall", - x=6, - color="green" - }, - { - y=1, - class="static_wall", - x=6, - color="red" - }, - { - y=1, - class="static_wall", x=7, - color="green" + class="static_wall", + color="blue", + y=1 }, { - y=1, - class="static_wall", - x=7, - color="blue" - }, - { - y=1, - class="static_wall", - x=7, - color="red" - }, - { - y=1, - class="static_wall", x=8, - color="red" + class="static_wall", + color="blue", + y=1 }, { - y=1, - class="static_wall", - x=8, - color="green" - }, - { - y=1, - class="static_wall", - x=8, - color="blue" - }, - { - y=1, - class="static_wall", x=9, - color="green" + class="static_wall", + color="blue", + y=1 }, { - y=1, - class="static_wall", - x=9, - color="red" - }, - { - y=1, - class="static_wall", - x=9, - color="blue" - }, - { - y=1, - class="static_wall", x=10, - color="green" + class="static_wall", + color="blue", + y=1 }, { - y=1, - class="static_wall", - x=10, - color="blue" - }, - { - y=1, - class="static_wall", - x=10, - color="red" - }, - { - y=1, - class="static_wall", x=11, - color="green" + class="static_wall", + color="blue", + y=1 }, { - y=1, + x=1, class="static_wall", + color="blue", + y=2 + }, + { x=11, - color="red" + class="static_wall", + color="blue", + y=2 }, { - y=1, + x=1, class="static_wall", + color="blue", + y=3 + }, + { x=11, - color="blue" + class="static_wall", + color="blue", + y=3 }, { - y=2, - class="static_wall", x=1, - color="blue" + class="static_wall", + color="blue", + y=4 }, { - y=2, - class="static_wall", - x=1, - color="red" - }, - { - y=2, - class="static_wall", - x=1, - color="green" - }, - { - y=2, - class="static_wall", - x=5, - color="red" - }, - { - y=2, - class="static_wall", - x=7, - color="red" - }, - { - y=2, - class="static_wall", x=11, - color="blue" + class="static_wall", + color="blue", + y=4 }, { - y=2, + x=1, class="static_wall", + color="blue", + y=5 + }, + { x=11, - color="red" + class="static_wall", + color="blue", + y=5 }, { - y=2, + x=1, class="static_wall", + color="blue", + y=6 + }, + { + x=1, + class="static_wall", + color="blue", + y=7 + }, + { x=11, - color="green" + class="static_wall", + color="blue", + y=7 }, { - y=3, - class="static_wall", x=1, - color="red" + class="static_wall", + color="blue", + y=8 }, { - y=3, - class="static_wall", - x=1, - color="blue" - }, - { - y=3, - class="static_wall", - x=1, - color="green" - }, - { - y=3, - class="static_wall", - x=5, - color="red" - }, - { - y=3, - class="static_wall", - x=7, - color="red" - }, - { - y=3, - class="static_wall", x=11, - color="red" + class="static_wall", + color="blue", + y=8 }, { - y=3, + x=1, class="static_wall", + color="blue", + y=9 + }, + { x=11, - color="green" + class="static_wall", + color="blue", + y=9 }, { - y=3, + x=1, class="static_wall", + color="blue", + y=10 + }, + { x=11, - color="blue" + class="static_wall", + color="blue", + y=10 }, { - y=4, - class="static_wall", x=1, - color="green" + class="static_wall", + color="blue", + y=11 }, { - y=4, - class="static_wall", - x=1, - color="blue" - }, - { - y=4, - class="static_wall", - x=1, - color="red" - }, - { - y=4, - class="static_wall", - x=3, - color="green" - }, - { - y=4, - class="static_wall", - x=9, - color="green" - }, - { - y=4, - class="static_wall", - x=11, - color="red" - }, - { - y=4, - class="static_wall", - x=11, - color="blue" - }, - { - y=4, - class="static_wall", - x=11, - color="green" - }, - { - y=5, - class="static_wall", - x=1, - color="blue" - }, - { - y=5, - class="static_wall", - x=1, - color="red" - }, - { - y=5, - class="static_wall", - x=1, - color="green" - }, - { - y=5, - class="static_wall", - x=4, - color="green" - }, - { - y=5, - class="static_wall", - x=5, - color="green" - }, - { - y=5, - class="static_wall", - x=7, - color="green" - }, - { - y=5, - class="static_wall", - x=8, - color="green" - }, - { - y=5, - class="static_wall", - x=11, - color="blue" - }, - { - y=5, - class="static_wall", - x=11, - color="green" - }, - { - y=5, - class="static_wall", - x=11, - color="red" - }, - { - y=6, - class="static_wall", - x=1, - color="green" - }, - { - y=6, - class="static_wall", - x=1, - color="red" - }, - { - y=6, - class="static_wall", - x=1, - color="blue" - }, - { - y=7, - class="static_wall", - x=1, - color="green" - }, - { - y=7, - class="static_wall", - x=1, - color="red" - }, - { - y=7, - class="static_wall", - x=1, - color="blue" - }, - { - y=7, - class="static_wall", - x=4, - color="green" - }, - { - y=7, - class="static_wall", - x=5, - color="green" - }, - { - y=7, - class="static_wall", - x=7, - color="green" - }, - { - y=7, - class="static_wall", - x=8, - color="green" - }, - { - y=7, - class="static_wall", - x=11, - color="red" - }, - { - y=7, - class="static_wall", - x=11, - color="green" - }, - { - y=7, - class="static_wall", - x=11, - color="blue" - }, - { - y=8, - class="static_wall", - x=1, - color="red" - }, - { - y=8, - class="static_wall", - x=1, - color="green" - }, - { - y=8, - class="static_wall", - x=1, - color="blue" - }, - { - y=8, - class="static_wall", - x=3, - color="green" - }, - { - y=8, - class="static_wall", - x=9, - color="green" - }, - { - y=8, - class="static_wall", - x=11, - color="green" - }, - { - y=8, - class="static_wall", - x=11, - color="red" - }, - { - y=8, - class="static_wall", - x=11, - color="blue" - }, - { - y=9, - class="static_wall", - x=1, - color="blue" - }, - { - y=9, - class="static_wall", - x=1, - color="green" - }, - { - y=9, - class="static_wall", - x=1, - color="red" - }, - { - y=9, - class="static_wall", - x=11, - color="red" - }, - { - y=9, - class="static_wall", - x=11, - color="green" - }, - { - y=9, - class="static_wall", - x=11, - color="blue" - }, - { - y=10, - class="static_wall", - x=1, - color="blue" - }, - { - y=10, - class="static_wall", - x=1, - color="red" - }, - { - y=10, - class="static_wall", - x=1, - color="green" - }, - { - y=10, - class="static_wall", - x=11, - color="red" - }, - { - y=10, - class="static_wall", - x=11, - color="blue" - }, - { - y=10, - class="static_wall", - x=11, - color="green" - }, - { - y=11, - class="static_wall", - x=1, - color="blue" - }, - { - y=11, - class="static_wall", - x=1, - color="red" - }, - { - y=11, - class="static_wall", - x=1, - color="green" - }, - { - y=11, - class="static_wall", x=2, - color="blue" + class="static_wall", + color="blue", + y=11 }, { - 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=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=11 + }, + { + x=1, + class="static_wall", + color="red", + y=1 + }, + { x=2, - color="green" + class="static_wall", + color="red", + y=1 }, { - y=11, + x=3, class="static_wall", + color="red", + y=1 + }, + { + x=4, + class="static_wall", + color="red", + y=1 + }, + { + x=5, + class="static_wall", + color="red", + y=1 + }, + { + x=6, + class="static_wall", + color="red", + y=1 + }, + { + x=7, + class="static_wall", + color="red", + y=1 + }, + { + x=8, + class="static_wall", + color="red", + y=1 + }, + { + x=9, + class="static_wall", + color="red", + y=1 + }, + { + x=10, + class="static_wall", + color="red", + y=1 + }, + { + x=11, + class="static_wall", + color="red", + y=1 + }, + { + x=1, + class="static_wall", + color="red", + y=2 + }, + { + x=5, + class="static_wall", + color="red", + y=2 + }, + { + x=7, + class="static_wall", + color="red", + y=2 + }, + { + x=11, + class="static_wall", + color="red", + y=2 + }, + { + x=1, + class="static_wall", + color="red", + y=3 + }, + { + x=5, + class="static_wall", + color="red", + y=3 + }, + { + x=7, + class="static_wall", + color="red", + y=3 + }, + { + x=11, + class="static_wall", + color="red", + y=3 + }, + { + x=1, + class="static_wall", + color="red", + y=4 + }, + { + x=11, + class="static_wall", + color="red", + y=4 + }, + { + x=1, + class="static_wall", + color="red", + y=5 + }, + { + x=11, + class="static_wall", + color="red", + y=5 + }, + { + x=1, + class="static_wall", + color="red", + y=6 + }, + { + x=1, + class="static_wall", + color="red", + y=7 + }, + { + x=11, + class="static_wall", + color="red", + y=7 + }, + { + x=1, + class="static_wall", + color="red", + y=8 + }, + { + x=11, + class="static_wall", + color="red", + y=8 + }, + { + x=1, + class="static_wall", + color="red", + y=9 + }, + { + x=11, + class="static_wall", + color="red", + y=9 + }, + { + x=1, + class="static_wall", + color="red", + y=10 + }, + { + x=11, + class="static_wall", + color="red", + y=10 + }, + { + x=1, + class="static_wall", + color="red", + y=11 + }, + { x=2, - color="red" + class="static_wall", + color="red", + y=11 }, { - y=11, - class="static_wall", x=3, - color="red" + class="static_wall", + color="red", + y=11 }, { - y=11, - class="static_wall", - x=3, - color="green" - }, - { - y=11, - class="static_wall", - x=3, - color="blue" - }, - { - y=11, - class="static_wall", x=4, - color="blue" + class="static_wall", + color="red", + y=11 }, { - y=11, - class="static_wall", - x=4, - color="green" - }, - { - y=11, - class="static_wall", - x=4, - color="red" - }, - { - y=11, - class="static_wall", x=5, - color="red" + class="static_wall", + color="red", + y=11 }, { - y=11, - class="static_wall", - x=5, - color="green" - }, - { - y=11, - class="static_wall", - x=5, - color="blue" - }, - { - y=11, - class="static_wall", x=7, - color="blue" + class="static_wall", + color="red", + y=11 }, { - y=11, - class="static_wall", - x=7, - color="green" - }, - { - y=11, - class="static_wall", - x=7, - color="red" - }, - { - y=11, - class="static_wall", x=8, - color="green" + class="static_wall", + color="red", + y=11 }, { - y=11, - class="static_wall", - x=8, - color="red" - }, - { - y=11, - class="static_wall", - x=8, - color="blue" - }, - { - y=11, - class="static_wall", x=9, - color="green" + class="static_wall", + color="red", + y=11 }, { - y=11, - class="static_wall", - x=9, - color="blue" - }, - { - y=11, - class="static_wall", - x=9, - color="red" - }, - { - y=11, - class="static_wall", x=10, - color="blue" + class="static_wall", + color="red", + y=11 }, { - y=11, - class="static_wall", - x=10, - color="red" - }, - { - y=11, - class="static_wall", - x=10, - color="green" - }, - { - y=11, - class="static_wall", x=11, - color="blue" - }, - { - y=11, class="static_wall", - x=11, - color="green" + color="red", + y=11 }, { - y=11, - class="static_wall", - x=11, - color="red" + x=3, + class="box", + color="red", + y=3 }, { - y=2, - class="switch", - x=6, - color="red" + x=9, + class="box", + color="red", + y=3 }, { - y=6, - class="switch", x=4, - color="green" + class="switch", + color="green", + y=6 }, { - y=6, - class="switch", x=5, - color="green" + class="switch", + color="green", + y=6 }, { - y=6, - class="switch", - x=6, - color="red" - }, - { - y=6, - class="switch", x=7, - color="green" - }, - { - y=6, class="switch", + color="green", + y=6 + }, + { x=8, - color="green" + class="switch", + color="green", + y=6 }, { - y=3, - class="box", - x=3, - color="red" - }, - { - y=3, - class="box", - x=9, - color="red" - }, - { - y=5, - class="box", x=6, - color="green" + class="switch", + color="red", + y=2 }, { - y=5, - class="box", - x=9, - color="green" - }, - { - y=7, - class="box", - x=3, - color="green" - }, - { - y=7, - class="box", x=6, - color="green" + class="switch", + color="red", + y=6 } } } \ No newline at end of file diff --git a/rooms/start.sav b/rooms/start.sav index da4d7a2..363e52d 100644 --- a/rooms/start.sav +++ b/rooms/start.sav @@ -1,800 +1,782 @@ { objects={ { - y=-1, - x=5, class="player", - color="red" - }, - { - y=1, - x=6, - class="door" - }, - { - y=1, - x=1, - class="static_wall", - color="red" - }, - { - y=1, - x=2, - class="static_wall", - color="red" - }, - { - y=1, - x=3, - class="static_wall", - color="red" - }, - { - y=1, - x=4, - class="static_wall", - color="red" - }, - { - y=1, - x=5, - class="static_wall", - color="red" - }, - { - y=1, - x=7, - class="static_wall", - color="red" - }, - { - y=1, - x=8, - class="static_wall", - color="red" - }, - { - y=1, - x=9, - class="static_wall", - color="red" - }, - { - y=1, - x=10, - class="static_wall", - color="red" - }, - { - y=1, - x=11, - class="static_wall", - color="red" - }, - { - y=2, - x=1, - class="static_wall", - color="red" - }, - { - y=2, - x=11, - class="static_wall", - color="red" - }, - { - y=3, - x=1, - class="static_wall", - color="red" - }, - { - y=3, - x=11, - class="static_wall", - color="red" - }, - { - y=4, - x=1, - class="static_wall", - color="red" - }, - { - y=4, - x=11, - class="static_wall", - color="red" - }, - { - y=5, - x=1, - class="static_wall", - color="red" - }, - { - y=5, - x=11, - class="static_wall", - color="red" - }, - { - y=6, - x=1, - class="static_wall", - color="red" - }, - { - y=6, - x=2, - class="static_wall", - color="red" - }, - { - y=6, - x=3, - class="static_wall", - color="red" - }, - { - y=6, - x=4, - class="static_wall", - color="red" - }, - { - y=6, - x=5, - class="static_wall", - color="red" - }, - { - y=6, - x=7, - class="static_wall", - color="red" - }, - { - y=6, - x=8, - class="static_wall", - color="red" - }, - { - y=6, - x=9, - class="static_wall", - color="red" - }, - { - y=6, - x=10, - class="static_wall", - color="red" - }, - { - y=6, - x=11, - class="static_wall", - color="red" - }, - { - y=7, - x=1, - class="static_wall", - color="red" - }, - { - y=7, - x=11, - class="static_wall", - color="red" - }, - { - y=8, - x=1, - class="static_wall", - color="red" - }, - { - y=8, - x=11, - class="static_wall", - color="red" - }, - { y=9, - x=1, - class="static_wall", - color="red" + color="red", + x=6 }, { + class="door", + y=1, + x=6 + }, + { + class="static_wall", y=9, - x=11, - class="static_wall", - color="red" + color="red", + x=1 }, { + class="static_wall", + y=11, + color="red", + x=2 + }, + { + class="static_wall", + y=6, + color="red", + x=5 + }, + { + class="static_wall", + y=1, + color="red", + x=1 + }, + { + class="static_wall", + y=3, + color="red", + x=1 + }, + { + class="static_wall", + y=1, + color="red", + x=3 + }, + { + class="static_wall", y=10, - x=1, - class="static_wall", - color="red" + color="red", + x=1 }, { - y=10, - x=11, class="static_wall", - color="red" + y=1, + color="red", + x=2 }, { + class="static_wall", + y=1, + color="red", + x=4 + }, + { + class="static_wall", + y=3, + color="red", + x=11 + }, + { + class="static_wall", + y=1, + color="red", + x=5 + }, + { + class="static_wall", y=11, - x=1, - class="static_wall", - color="red" + color="red", + x=3 }, { + class="static_wall", y=11, - x=2, - class="static_wall", - color="red" + color="red", + x=5 }, { - 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=4, - x=6, + color="red", + x=11 + }, + { + class="static_wall", + y=1, + color="red", + x=7 + }, + { + class="static_wall", + y=10, + color="red", + x=11 + }, + { + class="static_wall", + y=8, + color="red", + x=1 + }, + { + class="static_wall", + y=1, + color="red", + x=9 + }, + { + class="static_wall", + y=11, + color="red", + x=11 + }, + { + class="static_wall", + y=5, + color="red", + x=11 + }, + { + class="static_wall", + y=11, + color="red", + x=9 + }, + { + class="static_wall", + y=6, + color="red", + x=8 + }, + { + class="static_wall", + y=11, + color="red", + x=6 + }, + { + class="static_wall", + y=6, + color="red", + x=7 + }, + { + class="static_wall", + y=1, + color="red", + x=8 + }, + { + class="static_wall", + y=11, + color="red", + x=1 + }, + { + class="static_wall", + y=6, + color="red", + x=11 + }, + { + class="static_wall", + y=6, + color="red", + x=2 + }, + { + class="static_wall", + y=11, + color="red", + x=10 + }, + { + class="static_wall", + y=6, + color="red", + x=1 + }, + { + class="static_wall", + y=6, + color="red", + x=10 + }, + { + class="static_wall", + y=7, + color="red", + x=1 + }, + { + class="static_wall", + y=6, + color="red", + x=9 + }, + { + class="static_wall", + y=6, + color="red", + x=3 + }, + { + class="static_wall", + y=11, + color="red", + x=8 + }, + { + class="static_wall", + y=7, + color="red", + x=11 + }, + { + class="static_wall", + y=4, + color="red", + x=1 + }, + { + class="static_wall", + y=5, + color="red", + x=1 + }, + { + class="static_wall", + y=2, + color="red", + x=1 + }, + { + class="static_wall", + y=6, + color="red", + x=4 + }, + { + class="static_wall", + y=2, + color="red", + x=11 + }, + { + class="static_wall", + y=11, + color="red", + x=7 + }, + { + class="static_wall", + y=1, + color="red", + x=11 + }, + { + class="static_wall", + y=1, + color="red", + x=10 + }, + { + class="static_wall", + y=9, + color="red", + x=11 + }, + { + class="static_wall", + y=8, + color="red", + x=11 + }, + { + class="static_wall", + y=11, + color="red", + x=4 + }, + { class="box", - color="red" - }, - { - y=3, - x=9, - class="books_stack", - color="red" - }, - { - y=1, - x=1, - class="static_wall", - color="green" - }, - { - y=1, - x=2, - class="static_wall", - color="green" - }, - { - y=1, - x=3, - class="static_wall", - color="green" - }, - { - y=1, - x=4, - class="static_wall", - color="green" - }, - { - y=1, - x=5, - class="static_wall", - color="green" - }, - { - y=1, - x=7, - class="static_wall", - color="green" - }, - { - y=1, - x=8, - class="static_wall", - color="green" - }, - { - y=1, - x=9, - class="static_wall", - color="green" - }, - { - y=1, - x=10, - class="static_wall", - color="green" - }, - { - y=1, - x=11, - class="static_wall", - color="green" - }, - { - y=2, - x=1, - class="static_wall", - color="green" - }, - { - y=2, - x=11, - class="static_wall", - color="green" - }, - { - y=3, - x=1, - class="static_wall", - color="green" - }, - { - y=3, - x=11, - class="static_wall", - color="green" - }, - { - y=4, - x=1, - class="static_wall", - color="green" - }, - { - y=4, - x=11, - class="static_wall", - color="green" - }, - { - y=5, - x=1, - class="static_wall", - color="green" - }, - { - y=5, - x=11, - class="static_wall", - color="green" - }, - { y=6, - x=1, - class="static_wall", - color="green" + color="red", + x=6 }, { - y=6, - x=11, - class="static_wall", - color="green" - }, - { - y=7, - x=1, - class="static_wall", - color="green" - }, - { - y=7, - x=11, - class="static_wall", - color="green" - }, - { - y=8, - x=1, - class="static_wall", - color="green" - }, - { - y=8, - x=11, - class="static_wall", - color="green" - }, - { - y=9, - x=1, - class="static_wall", - color="green" - }, - { - y=9, - x=11, - class="static_wall", - color="green" - }, - { - y=10, - x=1, - class="static_wall", - color="green" - }, - { - y=10, - x=11, - 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=3, - x=9, - class="books_stack", - color="green" - }, - { - y=1, - x=1, - class="static_wall", - color="blue" - }, - { - y=1, - x=2, - class="static_wall", - color="blue" - }, - { - y=1, - x=3, - class="static_wall", - color="blue" - }, - { - y=1, - x=4, - class="static_wall", - color="blue" - }, - { - y=1, - x=5, - class="static_wall", - color="blue" - }, - { - y=1, - x=7, - class="static_wall", - color="blue" - }, - { - y=1, - x=8, - class="static_wall", - color="blue" - }, - { - y=1, - x=9, - class="static_wall", - color="blue" - }, - { - y=1, - x=10, - class="static_wall", - color="blue" - }, - { - y=1, - x=11, - class="static_wall", - color="blue" - }, - { - y=2, - x=1, - class="static_wall", - color="blue" - }, - { - y=2, - x=11, - class="static_wall", - color="blue" - }, - { - y=3, - x=1, - class="static_wall", - color="blue" - }, - { - y=3, - x=11, - class="static_wall", - color="blue" - }, - { - y=4, - x=1, - class="static_wall", - color="blue" - }, - { - y=4, - x=11, - class="static_wall", - color="blue" - }, - { - y=5, - x=1, - class="static_wall", - color="blue" - }, - { - y=5, - x=11, - class="static_wall", - color="blue" - }, - { - y=6, - x=1, - class="static_wall", - color="blue" - }, - { - y=6, - x=11, - class="static_wall", - color="blue" - }, - { - y=7, - x=1, - class="static_wall", - color="blue" - }, - { - y=7, - x=11, - class="static_wall", - color="blue" - }, - { - y=8, - x=1, - class="static_wall", - color="blue" - }, - { - y=8, - x=11, - class="static_wall", - color="blue" - }, - { - y=9, - x=1, - class="static_wall", - color="blue" - }, - { - y=9, - x=11, - class="static_wall", - color="blue" - }, - { - y=10, - x=1, - class="static_wall", - color="blue" - }, - { - y=10, - x=11, - 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=3, - x=9, - class="books_stack", - color="blue" - }, - { - y=4, - x=6, class="switch", - color="red" + y=4, + color="red", + x=6 + }, + { + class="static_wall", + y=9, + color="green", + x=1 + }, + { + class="static_wall", + y=11, + color="green", + x=2 + }, + { + class="static_wall", + y=1, + color="green", + x=1 + }, + { + class="static_wall", + y=2, + color="green", + x=11 + }, + { + class="static_wall", + y=10, + color="green", + x=1 + }, + { + class="static_wall", + y=1, + color="green", + x=2 + }, + { + class="static_wall", + y=1, + color="green", + x=4 + }, + { + class="static_wall", + y=3, + color="green", + x=11 + }, + { + class="static_wall", + y=1, + color="green", + x=5 + }, + { + class="static_wall", + y=1, + color="green", + x=10 + }, + { + class="static_wall", + y=11, + color="green", + x=5 + }, + { + class="static_wall", + y=4, + color="green", + x=11 + }, + { + class="static_wall", + y=1, + color="green", + x=7 + }, + { + class="static_wall", + y=10, + color="green", + x=11 + }, + { + class="static_wall", + y=8, + color="green", + x=1 + }, + { + class="static_wall", + y=11, + color="green", + x=11 + }, + { + class="static_wall", + y=5, + color="green", + x=11 + }, + { + class="static_wall", + y=11, + color="green", + x=9 + }, + { + class="static_wall", + y=11, + color="green", + x=6 + }, + { + class="static_wall", + y=1, + color="green", + x=9 + }, + { + class="static_wall", + y=3, + color="green", + x=1 + }, + { + class="static_wall", + y=11, + color="green", + x=1 + }, + { + class="static_wall", + y=6, + color="green", + x=11 + }, + { + class="static_wall", + y=6, + color="green", + x=1 + }, + { + class="static_wall", + y=11, + color="green", + x=10 + }, + { + class="static_wall", + y=1, + color="green", + x=11 + }, + { + class="static_wall", + y=7, + color="green", + x=1 + }, + { + class="static_wall", + y=9, + color="green", + x=11 + }, + { + class="static_wall", + y=7, + color="green", + x=11 + }, + { + class="static_wall", + y=4, + color="green", + x=1 + }, + { + class="static_wall", + y=5, + color="green", + x=1 + }, + { + class="static_wall", + y=2, + color="green", + x=1 + }, + { + class="static_wall", + y=1, + color="green", + x=3 + }, + { + class="static_wall", + y=11, + color="green", + x=7 + }, + { + class="static_wall", + y=1, + color="green", + x=8 + }, + { + class="static_wall", + y=11, + color="green", + x=8 + }, + { + class="static_wall", + y=11, + color="green", + x=3 + }, + { + class="static_wall", + y=8, + color="green", + x=11 + }, + { + class="static_wall", + y=11, + color="green", + x=4 + }, + { + class="static_wall", + y=9, + color="blue", + x=1 + }, + { + class="static_wall", + y=11, + color="blue", + x=2 + }, + { + class="static_wall", + y=1, + color="blue", + x=1 + }, + { + class="static_wall", + y=2, + color="blue", + x=11 + }, + { + class="static_wall", + y=10, + color="blue", + x=1 + }, + { + class="static_wall", + y=1, + color="blue", + x=2 + }, + { + class="static_wall", + y=1, + color="blue", + x=4 + }, + { + class="static_wall", + y=3, + color="blue", + x=11 + }, + { + class="static_wall", + y=1, + color="blue", + x=5 + }, + { + class="static_wall", + y=1, + color="blue", + x=10 + }, + { + class="static_wall", + y=11, + color="blue", + x=5 + }, + { + class="static_wall", + y=4, + color="blue", + x=11 + }, + { + class="static_wall", + y=1, + color="blue", + x=7 + }, + { + class="static_wall", + y=10, + color="blue", + x=11 + }, + { + class="static_wall", + y=8, + color="blue", + x=1 + }, + { + class="static_wall", + y=11, + color="blue", + x=11 + }, + { + class="static_wall", + y=5, + color="blue", + x=11 + }, + { + class="static_wall", + y=11, + color="blue", + x=9 + }, + { + class="static_wall", + y=11, + color="blue", + x=6 + }, + { + class="static_wall", + y=1, + color="blue", + x=9 + }, + { + class="static_wall", + y=3, + color="blue", + x=1 + }, + { + class="static_wall", + y=11, + color="blue", + x=1 + }, + { + class="static_wall", + y=6, + color="blue", + x=11 + }, + { + class="static_wall", + y=6, + color="blue", + x=1 + }, + { + class="static_wall", + y=11, + color="blue", + x=10 + }, + { + class="static_wall", + y=1, + color="blue", + x=11 + }, + { + class="static_wall", + y=7, + color="blue", + x=1 + }, + { + class="static_wall", + y=9, + color="blue", + x=11 + }, + { + class="static_wall", + y=7, + color="blue", + x=11 + }, + { + class="static_wall", + y=4, + color="blue", + x=1 + }, + { + class="static_wall", + y=5, + color="blue", + x=1 + }, + { + class="static_wall", + y=2, + color="blue", + x=1 + }, + { + class="static_wall", + y=1, + color="blue", + x=3 + }, + { + class="static_wall", + y=11, + color="blue", + x=7 + }, + { + class="static_wall", + y=1, + color="blue", + x=8 + }, + { + class="static_wall", + y=11, + color="blue", + x=8 + }, + { + class="static_wall", + y=11, + color="blue", + x=3 + }, + { + class="static_wall", + y=8, + color="blue", + x=11 + }, + { + class="static_wall", + y=11, + color="blue", + x=4 }, { - x=4, class="npc", - y=8 + y=8, + x=4 } } } \ No newline at end of file diff --git a/rooms/world.sav b/rooms/world.sav new file mode 100644 index 0000000..cb5d72e --- /dev/null +++ b/rooms/world.sav @@ -0,0 +1,146 @@ +{ +width=18, +height=18, +rooms={ + { + y=11, + x=9, + name="start" + }, + { + y=10, + x=10, + name="3" + }, + { + y=10, + x=11, + name="4" + }, + { + y=10, + x=12, + name="5" + }, + { + y=9, + x=12, + name="village_1" + }, + { + y=8, + x=12, + name="village_2" + }, + { + y=8, + x=13, + name="village_3" + }, + { + y=8, + x=11, + name="village_4" + }, + { + y=7, + x=11, + name="yellow_1" + }, + { + y=6, + x=11, + name="yellow_3" + }, + { + y=7, + x=12, + name="yellow_2" + }, + { + y=5, + x=11, + name="yellow_4" + }, + { + y=4, + x=11, + name="yellow_5" + }, + { + y=5, + x=10, + name="yellow_6" + }, + { + y=1, + x=1, + name="default" + }, + { + y=1, + x=3, + name="old_yellow_3" + }, + { + y=1, + x=5, + name="test_patt" + }, + { + y=1, + x=7, + name="test_patt_update" + }, + { + y=1, + x=9, + name="white_asymmetrical" + }, + { + y=1, + x=11, + name="white_ok" + }, + { + y=1, + x=13, + name="white_primary" + }, + { + y=1, + x=15, + name="yellow_arches" + }, + { + y=1, + x=17, + name="yellow_asymmetrical" + }, + { + y=3, + x=1, + name="yellow_cc_moustache" + }, + { + y=3, + x=3, + name="yellow_color_changing_levvel_1" + }, + { + y=3, + x=5, + name="yellow_skull" + }, + { + y=3, + x=7, + name="yellow_smile" + }, + { + y=10, + x=9, + name="2" + } +} +} \ No newline at end of file diff --git a/tools/migrate_legacy_room.lua b/tools/migrate_legacy_room.lua new file mode 100644 index 0000000..9f31be7 --- /dev/null +++ b/tools/migrate_legacy_room.lua @@ -0,0 +1,43 @@ +-- Convert a pre-flat-schema room save into the current { objects = { ... } } +-- format. Usage: +-- lua tools/migrate_legacy_room.lua legacy.sav migrated.sav + +local inputPath, outputPath = arg[1], arg[2] +assert(inputPath and outputPath, "usage: lua tools/migrate_legacy_room.lua legacy.sav migrated.sav") + +local input = assert(io.open(inputPath, "r")) +local chunk, err = load("return " .. input:read("*a")) +input:close() +assert(chunk, err) +local legacy = chunk() + +local objects = {} +local function append(class, object, includeColor) + local converted = { + class = class, + x = object.x, + y = object.y, + } + if includeColor ~= false then converted.color = object.color end + table.insert(objects, converted) +end + +for _, color in ipairs({ "red", "green", "blue" }) do + for _, object in ipairs(legacy.players[color] or {}) do append("player", object) end + -- Doors are shared between color layers in the current schema, so they have + -- no color field when serialized. + for _, object in ipairs(legacy.doors[color] or {}) do append("door", object, false) end + for _, object in ipairs(legacy.static_walls[color] or {}) do append("static_wall", object) end + -- The legacy wall sprite was renamed to box when assets became data-driven. + for _, object in ipairs(legacy.walls[color] or {}) do append("box", object) end + for _, object in ipairs(legacy.switches[color] or {}) do append("switch", object) end +end + +for _, sage in pairs(legacy.sages or {}) do + table.insert(objects, { class = "npc", x = sage.x, y = sage.y }) +end + +require "libs/TSerial" +local output = assert(io.open(outputPath, "w")) +output:write(TSerial.pack({ objects = objects }, nil, true)) +output:close() diff --git a/world.lua b/world.lua new file mode 100644 index 0000000..4499049 --- /dev/null +++ b/world.lua @@ -0,0 +1,179 @@ +World = class("World") + +-- The world is deliberately only a layout: rooms continue to own their puzzle +-- state. Keeping the two separate lets the editor move a room without +-- rewriting its level file. +function World:initialize() + self.width, self.height = 18, 18 + self.rooms = {} + self.roomByName = {} + self.previewCache = {} + self:load() +end + +function World:listRoomNames() + local names = {} + for _, file in ipairs(love.filesystem.getDirectoryItems("rooms")) do + local name = file:match("^(.+)%.sav$") + if name and name ~= "world" then table.insert(names, name) end + end + table.sort(names) + return names +end + +function World:load() + local raw = readFromSource("rooms/world.sav") + if not raw then return end + local ok, save = pcall(TSerial.unpack, raw) + if not ok or type(save) ~= "table" then return end + for _, room in ipairs(save.rooms or {}) do + if room.name and self:isInBounds(room.x, room.y) then + self:_putRoom(room.name, room.x, room.y) + end + end +end + +function World:save() + local rooms = {} + for _, room in ipairs(self.rooms) do + table.insert(rooms, { name = room.name, x = room.x, y = room.y }) + end + return writeToSource("rooms/world.sav", TSerial.pack({ width = self.width, height = self.height, rooms = rooms }, nil, true)) +end + +function World:isInBounds(x, y) + return x and y and x >= 1 and x <= self.width and y >= 1 and y <= self.height +end + +function World:roomAt(x, y) + for _, room in ipairs(self.rooms) do + if room.x == x and room.y == y then return room.name end + end + return nil +end + +function World:roomLocation(name) + return self.roomByName[name] +end + +function World:_putRoom(name, x, y) + local existing = self.roomByName[name] + if existing then + for i = #self.rooms, 1, -1 do + if self.rooms[i] == existing then table.remove(self.rooms, i) end + end + end + for i = #self.rooms, 1, -1 do + if self.rooms[i].x == x and self.rooms[i].y == y then + self.roomByName[self.rooms[i].name] = nil + table.remove(self.rooms, i) + end + end + local room = { name = name, x = x, y = y } + table.insert(self.rooms, room) + self.roomByName[name] = room + return room +end + +function World:placeRoom(name, x, y) + if not name or not self:isInBounds(x, y) then return false end + self:_putRoom(name, x, y) + return self:save() +end + +function World:removeRoom(x, y) + for i = #self.rooms, 1, -1 do + local room = self.rooms[i] + if room.x == x and room.y == y then + self.roomByName[room.name] = nil + table.remove(self.rooms, i) + return self:save(), room.name + end + end + return false +end + +function World:renameRoom(oldName, newName) + local room = self:roomLocation(oldName) + if not room or oldName == newName then return false end + self.roomByName[oldName] = nil + room.name = newName + self.roomByName[newName] = room + self:invalidatePreview(oldName) + self:invalidatePreview(newName) + return self:save() +end + +function World:getNeighbor(name, exit) + local room = self:roomLocation(name) + if not room then return nil end + local directions = { + left = { x = -1, y = 0 }, right = { x = 1, y = 0 }, + top = { x = 0, y = -1 }, bottom = { x = 0, y = 1 }, + } + local direction = directions[exit] + if not direction then return nil end + return self:roomAt(room.x + direction.x, room.y + direction.y) +end + +function World:getCellFromMousePos(mouseX, mouseY) + return math.floor(mouseX / (width / self.width)) + 1, math.floor(mouseY / (height / self.height)) + 1 +end + +function World:getRoomData(name) + -- The current room is live: its preview reflects unsaved edits immediately. + if currentRoom and currentRoom.name == name then return currentRoom:serialize() end + if not self.previewCache[name] then + local raw = readFromSource("rooms/" .. name .. ".sav") + if not raw then return nil end + local ok, room = pcall(TSerial.unpack, raw) + if not ok then return nil end + self.previewCache[name] = room + end + return self.previewCache[name] +end + +function World:invalidatePreview(name) + self.previewCache[name] = nil +end + +function World:drawRoomPreview(name, x, y, size) + local room = self:getRoomData(name) + if not room then return end + local scale = size / (gridWidth * 16) + love.graphics.setScissor(x, y, size, size) + for _, object in ipairs(room.objects or {}) do + if object.class == "npc" then + love.graphics.setColor(255, 255, 255, 255) + love.graphics.print("?", x + (object.x - 1) * 16 * scale, y + (object.y - 1) * 16 * scale, 0, scale, scale) + else + local props = globalAssetProperties[object.class] + if props and props.sprites then + local colors = object.class == "door" and { "red", "green", "blue" } or { object.color } + for _, color in ipairs(colors) do + local path = props.sprites[color] + if path then + love.graphics.setColor(gColor[color]:set()) + love.graphics.draw(loadSprite(path), x + (object.x - 1) * 16 * scale, y + (object.y - 1) * 16 * scale, 0, scale, scale) + end + end + end + end + end + love.graphics.setScissor() +end + +function World:draw() + local cell = width / self.width + love.graphics.setColor(12, 12, 20, 255) + love.graphics.rectangle("fill", 0, 0, width, height) + for y = 1, self.height do + for x = 1, self.width do + local screenX, screenY = (x - 1) * cell, (y - 1) * cell + local name = self:roomAt(x, y) + if name then self:drawRoomPreview(name, screenX, screenY, cell) end + love.graphics.setColor(name and 255 or 70, name and 255 or 70, name and 255 or 90, 255) + love.graphics.rectangle("line", screenX, screenY, cell, cell) + end + end +end diff --git a/world_seed.lua b/world_seed.lua new file mode 100644 index 0000000..3c38df5 --- /dev/null +++ b/world_seed.lua @@ -0,0 +1,70 @@ +-- Make a first 18x18 layout from the exit links preserved in the legacy saves. +-- Some old test rooms share the same one-way exit, so a grid cannot represent +-- every such link at once. Those rooms are still placed, but isolated, rather +-- than silently overwriting a room that already occupies that neighbour. +function seedWorldFromLegacyExits(world) + local available = {} + for _, name in ipairs(world:listRoomNames()) do available[name] = true end + local legacy = {} + for name in pairs(available) do + local raw = readFromSource("rooms/_pre_migration_backup/" .. name .. ".sav") + if raw then + local ok, save = pcall(TSerial.unpack, raw) + if ok and type(save) == "table" then legacy[name] = save.exits or {} end + end + end + + world.rooms, world.roomByName = {}, {} + local root = available.start and "start" or world:listRoomNames()[1] + if not root then return { placed = 0, links = 0, conflicts = 0 } end + world:_putRoom(root, 9, 11) + local queue, queued = { root }, { [root] = true } + local directions = { + top = { x = 0, y = -1 }, right = { x = 1, y = 0 }, + bottom = { x = 0, y = 1 }, left = { x = -1, y = 0 }, + } + local order = { "top", "right", "bottom", "left" } + local links, conflicts = 0, 0 + local index = 1 + while index <= #queue do + local name = queue[index] + local source = world:roomLocation(name) + for _, exit in ipairs(order) do + local target = legacy[name] and legacy[name][exit] + target = target and tostring(target) + local direction = directions[exit] + if target and available[target] then + local x, y = source.x + direction.x, source.y + direction.y + local occupied, known = world:roomAt(x, y), world:roomLocation(target) + if world:isInBounds(x, y) and (not occupied or occupied == target) and (not known or (known.x == x and known.y == y)) then + if not known then world:_putRoom(target, x, y) end + links = links + 1 + if not queued[target] then table.insert(queue, target); queued[target] = true end + else + conflicts = conflicts + 1 + end + end + end + index = index + 1 + end + + -- Keep every migrated room visible/selectable without creating accidental + -- exits between the disconnected legacy variants. + for _, name in ipairs(world:listRoomNames()) do + if not world:roomLocation(name) then + local placed = false + for y = 1, world.height do + for x = 1, world.width do + if not world:roomAt(x, y) then + local nearby = false + for dy = -1, 1 do for dx = -1, 1 do if world:roomAt(x + dx, y + dy) then nearby = true end end end + if not nearby then world:_putRoom(name, x, y); placed = true; break end + end + end + if placed then break end + end + end + end + world:save() + return { placed = #world.rooms, links = links, conflicts = conflicts } +end