misc
This commit is contained in:
parent
f8e3d78332
commit
b9b0726506
6 changed files with 140 additions and 81 deletions
|
|
@ -48,8 +48,15 @@ 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()
|
||||
function clearRuntimeSaveData(roomName)
|
||||
local removed = 0
|
||||
if roomName then
|
||||
-- love.filesystem.remove only affects the writable save directory, never
|
||||
-- the shipped/source room with the same path.
|
||||
if love.filesystem.remove("rooms/" .. roomName .. ".sav") then removed = 1 end
|
||||
return removed
|
||||
end
|
||||
|
||||
local function removeTree(path)
|
||||
if love.filesystem.isDirectory(path) then
|
||||
for _, child in ipairs(love.filesystem.getDirectoryItems(path)) do
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ local editorRoomName = nil
|
|||
local roomNameField = nil
|
||||
local editorStatus = nil
|
||||
local clearSaveConfirmationExpires = 0
|
||||
local clearSaveConfirmationScope = nil
|
||||
local worldSeedConfirmationExpires = 0
|
||||
local worldMode = "place"
|
||||
local worldRoomButtons = {}
|
||||
|
|
@ -129,7 +130,8 @@ local function saveRoomFromEditor()
|
|||
return
|
||||
end
|
||||
|
||||
if not currentRoom:saveToSource(name) then
|
||||
local savePlayerPositions = love.keyboard.isDown("lshift", "rshift")
|
||||
if not currentRoom:saveToSource(name, savePlayerPositions, oldName) then
|
||||
editorStatus = "Could not save '" .. name .. "'."
|
||||
return
|
||||
end
|
||||
|
|
@ -146,20 +148,28 @@ local function saveRoomFromEditor()
|
|||
editorRoomName = name
|
||||
roomNameField:editText(name)
|
||||
activeField = nil
|
||||
editorStatus = "Saved source level '" .. name .. ".'"
|
||||
editorStatus = "Saved source level '" .. name .. "'" .. (savePlayerPositions and " with player positions." or ".")
|
||||
end
|
||||
|
||||
local function clearRuntimeSaves()
|
||||
local now = love.timer.getTime()
|
||||
if now > clearSaveConfirmationExpires then
|
||||
local clearAll = love.keyboard.isDown("lshift", "rshift")
|
||||
local roomName = currentRoom.name
|
||||
local scope = clearAll and "all" or roomName
|
||||
if now > clearSaveConfirmationExpires or clearSaveConfirmationScope ~= scope then
|
||||
clearSaveConfirmationExpires = now + 3
|
||||
editorStatus = "Click the clear-save icon again within 3 seconds to erase runtime saves."
|
||||
clearSaveConfirmationScope = scope
|
||||
if clearAll then
|
||||
editorStatus = "Shift-click the clear-save icon again within 3 seconds to erase runtime saves for every room."
|
||||
else
|
||||
editorStatus = "Click the clear-save icon again within 3 seconds to erase the runtime save for '" .. (roomName or "this room") .. "'."
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local removed = clearRuntimeSaveData()
|
||||
local removed = clearRuntimeSaveData(clearAll and nil or roomName)
|
||||
clearSaveConfirmationExpires = 0
|
||||
local roomName = currentRoom.name
|
||||
clearSaveConfirmationScope = nil
|
||||
if roomName then
|
||||
currentRoom = Room:new{ name = roomName }
|
||||
setEditorRoomName(currentRoom.name)
|
||||
|
|
@ -521,6 +531,7 @@ local function openWorldRoom(cellX, cellY)
|
|||
currentRoom = Room:new{ name = name }
|
||||
editorStatus = "Editing '" .. name .. ".'"
|
||||
end
|
||||
gameWorld:setLastRoom(currentRoom.name)
|
||||
setEditorRoomName(currentRoom.name)
|
||||
assetsView()
|
||||
end
|
||||
|
|
|
|||
3
main.lua
3
main.lua
|
|
@ -33,8 +33,9 @@ function love.load()
|
|||
inputTimer = Timer.new()
|
||||
|
||||
currentRoom = Room:new{
|
||||
name = "start" -- TEMP: load a migrated level to eyeball it; revert once the room picker lands
|
||||
name = gameWorld.lastRoom or "start"
|
||||
}
|
||||
gameWorld:setLastRoom(currentRoom.name)
|
||||
setEditorRoomName(currentRoom.name)
|
||||
|
||||
response = ""
|
||||
|
|
|
|||
38
room.lua
38
room.lua
|
|
@ -36,11 +36,6 @@ function Room:initialize(t)
|
|||
end
|
||||
self:setActiveColor(color, true)
|
||||
end
|
||||
elseif not next(self.player) then
|
||||
-- fresh load of a room with no saved player: default red spawn so it's
|
||||
-- playable (the player starts with only red)
|
||||
self:registerPlayer(Player:new(1, 1, "red"), "red")
|
||||
self:setActiveColor("red", true)
|
||||
end
|
||||
|
||||
self.name = t.name
|
||||
|
|
@ -702,13 +697,37 @@ 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)
|
||||
function Room:saveToSource(name, savePlayerPositions, playerSourceName)
|
||||
name = name or self.name or "test"
|
||||
local room = TSerial.pack(self:serialize(), nil, true)
|
||||
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)
|
||||
return writeToSource("rooms/" .. name .. ".sav", room)
|
||||
end
|
||||
|
||||
function Room:serialize()
|
||||
function Room:serialize(includePlayers)
|
||||
local objects = {}
|
||||
|
||||
local function emit(class, color, entity)
|
||||
|
|
@ -716,9 +735,11 @@ function Room:serialize()
|
|||
table.insert(objects, { class = class, color = color, x = pos.x, y = pos.y })
|
||||
end
|
||||
|
||||
if includePlayers ~= false then
|
||||
for color, player in pairs(self.player) do
|
||||
emit("player", color, player)
|
||||
end
|
||||
end
|
||||
|
||||
for color, collideableArray in pairs(self.collideables) do
|
||||
for _, collideable in pairs(collideableArray) do
|
||||
|
|
@ -769,6 +790,7 @@ function nextRoom(pos)
|
|||
x = newPos.x, y = newPos.y, activeColors = currentRoom:getActiveColors()
|
||||
}
|
||||
}
|
||||
if gameWorld then gameWorld:setLastRoom(currentRoom.name) end
|
||||
if currentRoom.name then setEditorRoomName(currentRoom.name) end
|
||||
|
||||
end
|
||||
|
|
|
|||
121
rooms/world.sav
121
rooms/world.sav
|
|
@ -1,151 +1,152 @@
|
|||
{
|
||||
height=18,
|
||||
lastRoom="2",
|
||||
rooms={
|
||||
{
|
||||
y=11,
|
||||
name="start",
|
||||
x=9
|
||||
x=9,
|
||||
y=11
|
||||
},
|
||||
{
|
||||
y=10,
|
||||
name="3",
|
||||
x=10
|
||||
x=10,
|
||||
y=10
|
||||
},
|
||||
{
|
||||
y=10,
|
||||
name="4",
|
||||
x=11
|
||||
x=11,
|
||||
y=10
|
||||
},
|
||||
{
|
||||
y=10,
|
||||
name="5",
|
||||
x=12
|
||||
x=12,
|
||||
y=10
|
||||
},
|
||||
{
|
||||
y=9,
|
||||
name="village_1",
|
||||
x=12
|
||||
x=12,
|
||||
y=9
|
||||
},
|
||||
{
|
||||
y=8,
|
||||
name="village_2",
|
||||
x=12
|
||||
x=12,
|
||||
y=8
|
||||
},
|
||||
{
|
||||
y=8,
|
||||
name="village_3",
|
||||
x=13
|
||||
x=13,
|
||||
y=8
|
||||
},
|
||||
{
|
||||
y=8,
|
||||
name="village_4",
|
||||
x=11
|
||||
x=11,
|
||||
y=8
|
||||
},
|
||||
{
|
||||
y=7,
|
||||
name="yellow_1",
|
||||
x=11
|
||||
x=11,
|
||||
y=7
|
||||
},
|
||||
{
|
||||
y=6,
|
||||
name="yellow_3",
|
||||
x=11
|
||||
x=11,
|
||||
y=6
|
||||
},
|
||||
{
|
||||
y=7,
|
||||
name="yellow_2",
|
||||
x=12
|
||||
x=12,
|
||||
y=7
|
||||
},
|
||||
{
|
||||
y=5,
|
||||
name="yellow_4",
|
||||
x=11
|
||||
x=11,
|
||||
y=5
|
||||
},
|
||||
{
|
||||
y=4,
|
||||
name="yellow_5",
|
||||
x=11
|
||||
x=11,
|
||||
y=4
|
||||
},
|
||||
{
|
||||
y=5,
|
||||
name="yellow_6",
|
||||
x=10
|
||||
x=10,
|
||||
y=5
|
||||
},
|
||||
{
|
||||
y=1,
|
||||
name="default",
|
||||
x=1
|
||||
x=1,
|
||||
y=1
|
||||
},
|
||||
{
|
||||
y=1,
|
||||
name="old_yellow_3",
|
||||
x=3
|
||||
x=3,
|
||||
y=1
|
||||
},
|
||||
{
|
||||
y=1,
|
||||
name="test_patt",
|
||||
x=5
|
||||
x=5,
|
||||
y=1
|
||||
},
|
||||
{
|
||||
y=1,
|
||||
name="test_patt_update",
|
||||
x=7
|
||||
x=7,
|
||||
y=1
|
||||
},
|
||||
{
|
||||
y=1,
|
||||
name="white_asymmetrical",
|
||||
x=9
|
||||
x=9,
|
||||
y=1
|
||||
},
|
||||
{
|
||||
y=1,
|
||||
name="white_ok",
|
||||
x=11
|
||||
x=11,
|
||||
y=1
|
||||
},
|
||||
{
|
||||
y=1,
|
||||
name="white_primary",
|
||||
x=13
|
||||
x=13,
|
||||
y=1
|
||||
},
|
||||
{
|
||||
y=1,
|
||||
name="yellow_arches",
|
||||
x=15
|
||||
x=15,
|
||||
y=1
|
||||
},
|
||||
{
|
||||
y=1,
|
||||
name="yellow_asymmetrical",
|
||||
x=17
|
||||
x=17,
|
||||
y=1
|
||||
},
|
||||
{
|
||||
y=3,
|
||||
name="yellow_cc_moustache",
|
||||
x=1
|
||||
x=1,
|
||||
y=3
|
||||
},
|
||||
{
|
||||
y=3,
|
||||
name="yellow_color_changing_levvel_1",
|
||||
x=3
|
||||
x=3,
|
||||
y=3
|
||||
},
|
||||
{
|
||||
y=3,
|
||||
name="yellow_skull",
|
||||
x=5
|
||||
x=5,
|
||||
y=3
|
||||
},
|
||||
{
|
||||
y=3,
|
||||
name="yellow_smile",
|
||||
x=7
|
||||
x=7,
|
||||
y=3
|
||||
},
|
||||
{
|
||||
y=10,
|
||||
name="2",
|
||||
x=9
|
||||
x=9,
|
||||
y=10
|
||||
},
|
||||
{
|
||||
y=9,
|
||||
name="room_11_09",
|
||||
x=11
|
||||
x=11,
|
||||
y=9
|
||||
}
|
||||
},
|
||||
width=18,
|
||||
height=18
|
||||
width=18
|
||||
}
|
||||
21
world.lua
21
world.lua
|
|
@ -8,6 +8,7 @@ function World:initialize()
|
|||
self.rooms = {}
|
||||
self.roomByName = {}
|
||||
self.previewCache = {}
|
||||
self.lastRoom = nil
|
||||
self:load()
|
||||
end
|
||||
|
||||
|
|
@ -26,6 +27,7 @@ function World:load()
|
|||
if not raw then return end
|
||||
local ok, save = pcall(TSerial.unpack, raw)
|
||||
if not ok or type(save) ~= "table" then return end
|
||||
self.lastRoom = save.lastRoom
|
||||
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)
|
||||
|
|
@ -38,7 +40,7 @@ function World:save()
|
|||
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))
|
||||
return writeToSource("rooms/world.sav", TSerial.pack({ width = self.width, height = self.height, lastRoom = self.lastRoom, rooms = rooms }, nil, true))
|
||||
end
|
||||
|
||||
function World:isInBounds(x, y)
|
||||
|
|
@ -87,6 +89,7 @@ function World:removeRoom(x, y)
|
|||
if room.x == x and room.y == y then
|
||||
self.roomByName[room.name] = nil
|
||||
table.remove(self.rooms, i)
|
||||
if self.lastRoom == room.name then self.lastRoom = nil end
|
||||
return self:save(), room.name
|
||||
end
|
||||
end
|
||||
|
|
@ -99,11 +102,20 @@ function World:renameRoom(oldName, newName)
|
|||
self.roomByName[oldName] = nil
|
||||
room.name = newName
|
||||
self.roomByName[newName] = room
|
||||
if self.lastRoom == oldName then self.lastRoom = newName end
|
||||
self:invalidatePreview(oldName)
|
||||
self:invalidatePreview(newName)
|
||||
return self:save()
|
||||
end
|
||||
|
||||
function World:setLastRoom(name)
|
||||
if name and self.lastRoom ~= name then
|
||||
self.lastRoom = name
|
||||
return self:save()
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function World:getNeighbor(name, exit)
|
||||
local room = self:roomLocation(name)
|
||||
if not room then return nil end
|
||||
|
|
@ -149,7 +161,12 @@ function World:drawRoomPreview(name, x, y, size)
|
|||
else
|
||||
local props = globalAssetProperties[object.class]
|
||||
if props and props.sprites then
|
||||
local colors = object.class == "door" and { "red", "green", "blue" } or { object.color }
|
||||
local colors
|
||||
if object.class == "door" then
|
||||
colors = object.color and { object.color } or { "red", "green", "blue" }
|
||||
else
|
||||
colors = { object.color }
|
||||
end
|
||||
for _, color in ipairs(colors) do
|
||||
local path = props.sprites[color]
|
||||
if path then
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue