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
|
-- Runtime saves live in LÖVE's write directory. This deliberately uses only
|
||||||
-- love.filesystem operations: they cannot delete files from the source tree.
|
-- love.filesystem operations: they cannot delete files from the source tree.
|
||||||
function clearRuntimeSaveData()
|
function clearRuntimeSaveData(roomName)
|
||||||
local removed = 0
|
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)
|
local function removeTree(path)
|
||||||
if love.filesystem.isDirectory(path) then
|
if love.filesystem.isDirectory(path) then
|
||||||
for _, child in ipairs(love.filesystem.getDirectoryItems(path)) do
|
for _, child in ipairs(love.filesystem.getDirectoryItems(path)) do
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ local editorRoomName = nil
|
||||||
local roomNameField = nil
|
local roomNameField = nil
|
||||||
local editorStatus = nil
|
local editorStatus = nil
|
||||||
local clearSaveConfirmationExpires = 0
|
local clearSaveConfirmationExpires = 0
|
||||||
|
local clearSaveConfirmationScope = nil
|
||||||
local worldSeedConfirmationExpires = 0
|
local worldSeedConfirmationExpires = 0
|
||||||
local worldMode = "place"
|
local worldMode = "place"
|
||||||
local worldRoomButtons = {}
|
local worldRoomButtons = {}
|
||||||
|
|
@ -129,7 +130,8 @@ local function saveRoomFromEditor()
|
||||||
return
|
return
|
||||||
end
|
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 .. "'."
|
editorStatus = "Could not save '" .. name .. "'."
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
@ -146,20 +148,28 @@ local function saveRoomFromEditor()
|
||||||
editorRoomName = name
|
editorRoomName = name
|
||||||
roomNameField:editText(name)
|
roomNameField:editText(name)
|
||||||
activeField = nil
|
activeField = nil
|
||||||
editorStatus = "Saved source level '" .. name .. ".'"
|
editorStatus = "Saved source level '" .. name .. "'" .. (savePlayerPositions and " with player positions." or ".")
|
||||||
end
|
end
|
||||||
|
|
||||||
local function clearRuntimeSaves()
|
local function clearRuntimeSaves()
|
||||||
local now = love.timer.getTime()
|
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
|
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
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local removed = clearRuntimeSaveData()
|
local removed = clearRuntimeSaveData(clearAll and nil or roomName)
|
||||||
clearSaveConfirmationExpires = 0
|
clearSaveConfirmationExpires = 0
|
||||||
local roomName = currentRoom.name
|
clearSaveConfirmationScope = nil
|
||||||
if roomName then
|
if roomName then
|
||||||
currentRoom = Room:new{ name = roomName }
|
currentRoom = Room:new{ name = roomName }
|
||||||
setEditorRoomName(currentRoom.name)
|
setEditorRoomName(currentRoom.name)
|
||||||
|
|
@ -521,6 +531,7 @@ local function openWorldRoom(cellX, cellY)
|
||||||
currentRoom = Room:new{ name = name }
|
currentRoom = Room:new{ name = name }
|
||||||
editorStatus = "Editing '" .. name .. ".'"
|
editorStatus = "Editing '" .. name .. ".'"
|
||||||
end
|
end
|
||||||
|
gameWorld:setLastRoom(currentRoom.name)
|
||||||
setEditorRoomName(currentRoom.name)
|
setEditorRoomName(currentRoom.name)
|
||||||
assetsView()
|
assetsView()
|
||||||
end
|
end
|
||||||
|
|
|
||||||
3
main.lua
3
main.lua
|
|
@ -33,8 +33,9 @@ function love.load()
|
||||||
inputTimer = Timer.new()
|
inputTimer = Timer.new()
|
||||||
|
|
||||||
currentRoom = Room: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)
|
setEditorRoomName(currentRoom.name)
|
||||||
|
|
||||||
response = ""
|
response = ""
|
||||||
|
|
|
||||||
38
room.lua
38
room.lua
|
|
@ -36,11 +36,6 @@ function Room:initialize(t)
|
||||||
end
|
end
|
||||||
self:setActiveColor(color, true)
|
self:setActiveColor(color, true)
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
self.name = t.name
|
self.name = t.name
|
||||||
|
|
@ -702,13 +697,37 @@ 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)
|
function Room:saveToSource(name, savePlayerPositions, playerSourceName)
|
||||||
name = name or self.name or "test"
|
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)
|
return writeToSource("rooms/" .. name .. ".sav", room)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Room:serialize()
|
function Room:serialize(includePlayers)
|
||||||
local objects = {}
|
local objects = {}
|
||||||
|
|
||||||
local function emit(class, color, entity)
|
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 })
|
table.insert(objects, { class = class, color = color, x = pos.x, y = pos.y })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if includePlayers ~= false then
|
||||||
for color, player in pairs(self.player) do
|
for color, player in pairs(self.player) do
|
||||||
emit("player", color, player)
|
emit("player", color, player)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
for color, collideableArray in pairs(self.collideables) do
|
for color, collideableArray in pairs(self.collideables) do
|
||||||
for _, collideable in pairs(collideableArray) do
|
for _, collideable in pairs(collideableArray) do
|
||||||
|
|
@ -769,6 +790,7 @@ function nextRoom(pos)
|
||||||
x = newPos.x, y = newPos.y, activeColors = currentRoom:getActiveColors()
|
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
|
if currentRoom.name then setEditorRoomName(currentRoom.name) end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
121
rooms/world.sav
121
rooms/world.sav
|
|
@ -1,151 +1,152 @@
|
||||||
{
|
{
|
||||||
|
height=18,
|
||||||
|
lastRoom="2",
|
||||||
rooms={
|
rooms={
|
||||||
{
|
{
|
||||||
y=11,
|
|
||||||
name="start",
|
name="start",
|
||||||
x=9
|
x=9,
|
||||||
|
y=11
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=10,
|
|
||||||
name="3",
|
name="3",
|
||||||
x=10
|
x=10,
|
||||||
|
y=10
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=10,
|
|
||||||
name="4",
|
name="4",
|
||||||
x=11
|
x=11,
|
||||||
|
y=10
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=10,
|
|
||||||
name="5",
|
name="5",
|
||||||
x=12
|
x=12,
|
||||||
|
y=10
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=9,
|
|
||||||
name="village_1",
|
name="village_1",
|
||||||
x=12
|
x=12,
|
||||||
|
y=9
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=8,
|
|
||||||
name="village_2",
|
name="village_2",
|
||||||
x=12
|
x=12,
|
||||||
|
y=8
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=8,
|
|
||||||
name="village_3",
|
name="village_3",
|
||||||
x=13
|
x=13,
|
||||||
|
y=8
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=8,
|
|
||||||
name="village_4",
|
name="village_4",
|
||||||
x=11
|
x=11,
|
||||||
|
y=8
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=7,
|
|
||||||
name="yellow_1",
|
name="yellow_1",
|
||||||
x=11
|
x=11,
|
||||||
|
y=7
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=6,
|
|
||||||
name="yellow_3",
|
name="yellow_3",
|
||||||
x=11
|
x=11,
|
||||||
|
y=6
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=7,
|
|
||||||
name="yellow_2",
|
name="yellow_2",
|
||||||
x=12
|
x=12,
|
||||||
|
y=7
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=5,
|
|
||||||
name="yellow_4",
|
name="yellow_4",
|
||||||
x=11
|
x=11,
|
||||||
|
y=5
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=4,
|
|
||||||
name="yellow_5",
|
name="yellow_5",
|
||||||
x=11
|
x=11,
|
||||||
|
y=4
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=5,
|
|
||||||
name="yellow_6",
|
name="yellow_6",
|
||||||
x=10
|
x=10,
|
||||||
|
y=5
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=1,
|
|
||||||
name="default",
|
name="default",
|
||||||
x=1
|
x=1,
|
||||||
|
y=1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=1,
|
|
||||||
name="old_yellow_3",
|
name="old_yellow_3",
|
||||||
x=3
|
x=3,
|
||||||
|
y=1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=1,
|
|
||||||
name="test_patt",
|
name="test_patt",
|
||||||
x=5
|
x=5,
|
||||||
|
y=1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=1,
|
|
||||||
name="test_patt_update",
|
name="test_patt_update",
|
||||||
x=7
|
x=7,
|
||||||
|
y=1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=1,
|
|
||||||
name="white_asymmetrical",
|
name="white_asymmetrical",
|
||||||
x=9
|
x=9,
|
||||||
|
y=1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=1,
|
|
||||||
name="white_ok",
|
name="white_ok",
|
||||||
x=11
|
x=11,
|
||||||
|
y=1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=1,
|
|
||||||
name="white_primary",
|
name="white_primary",
|
||||||
x=13
|
x=13,
|
||||||
|
y=1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=1,
|
|
||||||
name="yellow_arches",
|
name="yellow_arches",
|
||||||
x=15
|
x=15,
|
||||||
|
y=1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=1,
|
|
||||||
name="yellow_asymmetrical",
|
name="yellow_asymmetrical",
|
||||||
x=17
|
x=17,
|
||||||
|
y=1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=3,
|
|
||||||
name="yellow_cc_moustache",
|
name="yellow_cc_moustache",
|
||||||
x=1
|
x=1,
|
||||||
|
y=3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=3,
|
|
||||||
name="yellow_color_changing_levvel_1",
|
name="yellow_color_changing_levvel_1",
|
||||||
x=3
|
x=3,
|
||||||
|
y=3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=3,
|
|
||||||
name="yellow_skull",
|
name="yellow_skull",
|
||||||
x=5
|
x=5,
|
||||||
|
y=3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=3,
|
|
||||||
name="yellow_smile",
|
name="yellow_smile",
|
||||||
x=7
|
x=7,
|
||||||
|
y=3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=10,
|
|
||||||
name="2",
|
name="2",
|
||||||
x=9
|
x=9,
|
||||||
|
y=10
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
y=9,
|
|
||||||
name="room_11_09",
|
name="room_11_09",
|
||||||
x=11
|
x=11,
|
||||||
|
y=9
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
width=18,
|
width=18
|
||||||
height=18
|
|
||||||
}
|
}
|
||||||
21
world.lua
21
world.lua
|
|
@ -8,6 +8,7 @@ function World:initialize()
|
||||||
self.rooms = {}
|
self.rooms = {}
|
||||||
self.roomByName = {}
|
self.roomByName = {}
|
||||||
self.previewCache = {}
|
self.previewCache = {}
|
||||||
|
self.lastRoom = nil
|
||||||
self:load()
|
self:load()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -26,6 +27,7 @@ function World:load()
|
||||||
if not raw then return end
|
if not raw then return end
|
||||||
local ok, save = pcall(TSerial.unpack, raw)
|
local ok, save = pcall(TSerial.unpack, raw)
|
||||||
if not ok or type(save) ~= "table" then return end
|
if not ok or type(save) ~= "table" then return end
|
||||||
|
self.lastRoom = save.lastRoom
|
||||||
for _, room in ipairs(save.rooms or {}) do
|
for _, room in ipairs(save.rooms or {}) do
|
||||||
if room.name and self:isInBounds(room.x, room.y) then
|
if room.name and self:isInBounds(room.x, room.y) then
|
||||||
self:_putRoom(room.name, room.x, room.y)
|
self:_putRoom(room.name, room.x, room.y)
|
||||||
|
|
@ -38,7 +40,7 @@ function World:save()
|
||||||
for _, room in ipairs(self.rooms) do
|
for _, room in ipairs(self.rooms) do
|
||||||
table.insert(rooms, { name = room.name, x = room.x, y = room.y })
|
table.insert(rooms, { name = room.name, x = room.x, y = room.y })
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
function World:isInBounds(x, y)
|
function World:isInBounds(x, y)
|
||||||
|
|
@ -87,6 +89,7 @@ function World:removeRoom(x, y)
|
||||||
if room.x == x and room.y == y then
|
if room.x == x and room.y == y then
|
||||||
self.roomByName[room.name] = nil
|
self.roomByName[room.name] = nil
|
||||||
table.remove(self.rooms, i)
|
table.remove(self.rooms, i)
|
||||||
|
if self.lastRoom == room.name then self.lastRoom = nil end
|
||||||
return self:save(), room.name
|
return self:save(), room.name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -99,11 +102,20 @@ function World:renameRoom(oldName, newName)
|
||||||
self.roomByName[oldName] = nil
|
self.roomByName[oldName] = nil
|
||||||
room.name = newName
|
room.name = newName
|
||||||
self.roomByName[newName] = room
|
self.roomByName[newName] = room
|
||||||
|
if self.lastRoom == oldName then self.lastRoom = newName end
|
||||||
self:invalidatePreview(oldName)
|
self:invalidatePreview(oldName)
|
||||||
self:invalidatePreview(newName)
|
self:invalidatePreview(newName)
|
||||||
return self:save()
|
return self:save()
|
||||||
end
|
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)
|
function World:getNeighbor(name, exit)
|
||||||
local room = self:roomLocation(name)
|
local room = self:roomLocation(name)
|
||||||
if not room then return nil end
|
if not room then return nil end
|
||||||
|
|
@ -149,7 +161,12 @@ function World:drawRoomPreview(name, x, y, size)
|
||||||
else
|
else
|
||||||
local props = globalAssetProperties[object.class]
|
local props = globalAssetProperties[object.class]
|
||||||
if props and props.sprites then
|
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
|
for _, color in ipairs(colors) do
|
||||||
local path = props.sprites[color]
|
local path = props.sprites[color]
|
||||||
if path then
|
if path then
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue