fix state thigns and port to love11

This commit is contained in:
Your Name 2026-08-07 20:56:42 -04:00
parent ff5abf6bcd
commit c92c0f6ff7
11 changed files with 130 additions and 96 deletions

View file

@ -58,7 +58,8 @@ function clearRuntimeSaveData(roomName)
end end
local function removeTree(path) local function removeTree(path)
if love.filesystem.isDirectory(path) then local info = love.filesystem.getInfo(path)
if info and info.type == "directory" then
for _, child in ipairs(love.filesystem.getDirectoryItems(path)) do for _, child in ipairs(love.filesystem.getDirectoryItems(path)) do
removeTree(path .. "/" .. child) removeTree(path .. "/" .. child)
end end
@ -109,16 +110,18 @@ function cellShapeFromBox(box)
return cells return cells
end end
-- Which 16x16 cells of a sprite are non-empty, for multi-cell collision shapes. -- Which 16x16 cells of an image are non-empty, for multi-cell collision
function getCellsFromSprite(sprite) -- shapes. LÖVE 11 no longer exposes Image:getData(), so inspect ImageData
local w, h = sprite:getWidth(), sprite:getHeight() -- directly instead of creating a GPU Image first.
function getCellsFromSprite(spritePath)
local img = love.image.newImageData(spritePath)
local w, h = img:getWidth(), img:getHeight()
local box = {w = w / 16, h = h / 16} local box = {w = w / 16, h = h / 16}
local boxCells = cellShapeFromBox(box) local boxCells = cellShapeFromBox(box)
if box.w < 1 or box.h < 1 then if box.w < 1 or box.h < 1 then
return boxCells return boxCells
end end
local img = sprite:getData()
local cellsToRemove = {} local cellsToRemove = {}
for _, cell in pairs(boxCells) do for _, cell in pairs(boxCells) do

View file

@ -4,10 +4,11 @@
Color = class("Color") Color = class("Color")
Color.static.list = { Color.static.list = {
red = {r = 255, g = 0, b = 0, a = 255}, -- LÖVE 11 represents color components as normalized floats, not bytes.
green = {r = 0, g = 255, b = 0, a = 255}, red = {r = 1, g = 0, b = 0, a = 1},
blue = {r = 0, g = 0, b = 255, a = 255}, green = {r = 0, g = 1, b = 0, a = 1},
white = {r = 255, g = 255, b = 255, a = 255} blue = {r = 0, g = 0, b = 1, a = 1},
white = {r = 1, g = 1, b = 1, a = 1}
} }
function Color:initialize(inColor) function Color:initialize(inColor)
@ -27,4 +28,4 @@ end
function randomColor() function randomColor()
local colors = {"red", "green", "blue"} local colors = {"red", "green", "blue"}
return colors[math.random(1, 3)] return colors[math.random(1, 3)]
end end

View file

@ -1,8 +1,12 @@
function love.conf(t) function love.conf(t)
-- This project uses the normalized-color and ImageData APIs introduced in
-- LÖVE 11. Pinning the intended runtime also gives a useful warning when a
-- player tries to launch it with an older installation.
t.version = "11.5"
t.window.width = 900 t.window.width = 900
t.window.height = 640 t.window.height = 640
t.window.title = "CHROMA SOLSTICE" t.window.title = "CHROMA SOLSTICE"
t.window.resizable = true t.window.resizable = true
t.window.minwidth = 700 t.window.minwidth = 700
t.window.minheight = 500 t.window.minheight = 500
end end

View file

@ -136,14 +136,14 @@ local function drawWorldSidebar()
love.graphics.rectangle("line", worldSearchBox.x, worldSearchBox.y, worldSearchBox.w, worldSearchBox.h) love.graphics.rectangle("line", worldSearchBox.x, worldSearchBox.y, worldSearchBox.w, worldSearchBox.h)
love.graphics.printf("SEARCH: " .. worldSearchText, worldSearchBox.x + 6, worldSearchBox.y + 7, worldSearchBox.w - 12, "left") love.graphics.printf("SEARCH: " .. worldSearchText, worldSearchBox.x + 6, worldSearchBox.y + 7, worldSearchBox.w - 12, "left")
for _, button in ipairs(worldRoomButtons) do 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.setColor(button.name == selectedRoom and 1 or 100 / 255, button.name == selectedRoom and 1 or 100 / 255, button.name == selectedRoom and 1 or 100 / 255, 1)
love.graphics.rectangle("line", button.x, button.y, button.w, button.h) love.graphics.rectangle("line", button.x, button.y, button.w, button.h)
gameWorld:drawRoomPreview(button.name, button.x + 4, button.y + 4, button.h - 8) gameWorld:drawRoomPreview(button.name, button.x + 4, button.y + 4, button.h - 8)
love.graphics.setColor(gColor.white:set()) 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") love.graphics.printf(button.name, button.x + button.h + 4, button.y + 21, button.w - button.h - 8, "left")
end end
for _, control in ipairs(worldControls) do for _, control in ipairs(worldControls) do
love.graphics.setColor(255, 255, 255, 255) love.graphics.setColor(1, 1, 1, 1)
love.graphics.rectangle("line", control.x, control.y, control.w, control.h) 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") love.graphics.printf(control.text, control.x, control.y + 9, control.w, "center")
end end

View file

@ -1,6 +1,6 @@
GUI = class("level_editor/gui") GUI = class("level_editor/gui")
GUI.static.pressSound = love.audio.newSource("sounds/ui/button.wav") GUI.static.pressSound = love.audio.newSource("sounds/ui/button.wav", "static")
GUI.static.defaultCallbac = function() print("this button has not been configured") end GUI.static.defaultCallbac = function() print("this button has not been configured") end

View file

@ -34,7 +34,7 @@ function getAllAssets()
-- class comes from a <name>.meta sitting next to the art -- class comes from a <name>.meta sitting next to the art
-- (authoritative); default to immoveable if none exists -- (authoritative); default to immoveable if none exists
local metaPath = topFolder .. "/" .. folder .. "/" .. name .. ".meta" local metaPath = topFolder .. "/" .. folder .. "/" .. name .. ".meta"
if love.filesystem.exists(metaPath) then if love.filesystem.getInfo(metaPath, "file") then
globalAssetProperties[name].class = TSerial.unpack(love.filesystem.read(metaPath)).class globalAssetProperties[name].class = TSerial.unpack(love.filesystem.read(metaPath)).class
elseif not globalAssetProperties[name].class then elseif not globalAssetProperties[name].class then
globalAssetProperties[name].class = "immoveable" globalAssetProperties[name].class = "immoveable"
@ -45,7 +45,7 @@ function getAllAssets()
if not globalAssetProperties[name].cells or reanalyzeCells then if not globalAssetProperties[name].cells or reanalyzeCells then
globalAssetProperties[name].cells = {} globalAssetProperties[name].cells = {}
globalAssetProperties[name].cells[color] = getCellsFromSprite(love.graphics.newImage(sprite)) globalAssetProperties[name].cells[color] = getCellsFromSprite(sprite)
end end
end end
end end
@ -54,4 +54,4 @@ function getAllAssets()
end end
return globalAssetProperties return globalAssetProperties
end end

View file

@ -29,8 +29,12 @@ function love.load()
inputTimer = Timer.new() inputTimer = Timer.new()
-- Resume the game: prefer the runtime autosave so the last saved position is
-- restored, falling back to the authored source level. Editor room-opens go
-- through their own (source-only) load path, so this only affects resume.
currentRoom = Room:new{ currentRoom = Room:new{
name = gameWorld.lastRoom or "start" name = gameWorld.lastRoom or "start",
runtime = true
} }
gameWorld:setLastRoom(currentRoom.name) gameWorld:setLastRoom(currentRoom.name)
setEditorRoomName(currentRoom.name) setEditorRoomName(currentRoom.name)
@ -38,6 +42,15 @@ function love.load()
response = "" response = ""
end end
-- Autosave on close so a position reached by moving within a room (short of a
-- room-to-room transition, which already autosaves in nextRoom) survives.
function love.quit()
if currentRoom then
currentRoom:save()
if gameWorld then gameWorld:setLastRoom(currentRoom.name) end
end
end
function love.update(dt) function love.update(dt)
if editorView == "world" then if editorView == "world" then
Input:stopMoveRepeat() Input:stopMoveRepeat()
@ -70,7 +83,7 @@ function love.draw()
love.graphics.push() love.graphics.push()
for i = 1, samples do for i = 1, samples do
local c = 65 / (i * 1.75) local c = 65 / 255 / (i * 1.75)
love.graphics.setColor(c, c, c) love.graphics.setColor(c, c, c)
love.graphics.scale(scaleCoefficient, scaleCoefficient) love.graphics.scale(scaleCoefficient, scaleCoefficient)
love.graphics.translate((width / 2 - (width * scaleCoefficient) / 2) + 2, (width / 2 - (height * scaleCoefficient) / 2) + 2) love.graphics.translate((width / 2 - (width * scaleCoefficient) / 2) + 2, (width / 2 - (height * scaleCoefficient) / 2) + 2)
@ -78,7 +91,7 @@ function love.draw()
end end
love.graphics.pop() love.graphics.pop()
love.graphics.setColor(255, 255, 255) love.graphics.setColor(1, 1, 1)
love.graphics.draw(gameCanvas, 0, 0) love.graphics.draw(gameCanvas, 0, 0)
love.graphics.setCanvas() love.graphics.setCanvas()
@ -88,7 +101,7 @@ function love.draw()
love.graphics.setCanvas() love.graphics.setCanvas()
love.graphics.setBlendMode("alpha") love.graphics.setBlendMode("alpha")
love.graphics.setColor(255, 255, 255, 255) love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(finalCanvas, 0, 0) love.graphics.draw(finalCanvas, 0, 0)
love.graphics.draw(sideBarCanvas, sideBarBox.x, sideBarBox.y) love.graphics.draw(sideBarCanvas, sideBarBox.x, sideBarBox.y)
love.graphics.print("Current FPS: "..tostring(love.timer.getFPS( )), 10, 10) love.graphics.print("Current FPS: "..tostring(love.timer.getFPS( )), 10, 10)

View file

@ -3,8 +3,8 @@ require "entity"
Player = class("Player", Entity) Player = class("Player", Entity)
Player.static.sounds = { Player.static.sounds = {
switchFail = love.audio.newSource("sounds/switch_fail.wav"), switchFail = love.audio.newSource("sounds/switch_fail.wav", "static"),
switchSuccess = love.audio.newSource("sounds/switch_success.wav") switchSuccess = love.audio.newSource("sounds/switch_success.wav", "static")
} }
function Player:initialize(x, y, color) function Player:initialize(x, y, color)

View file

@ -130,7 +130,7 @@ function Room:draw()
-- npcs (old "sages") are markers for now: a "?" tinted to the player's channels -- npcs (old "sages") are markers for now: a "?" tinted to the player's channels
local cellW, cellH = width / gridWidth, height / gridHeight local cellW, cellH = width / gridWidth, height / gridHeight
love.graphics.setColor(has.red and 255 or 0, has.green and 255 or 0, has.blue and 255 or 0) love.graphics.setColor(has.red and 1 or 0, has.green and 1 or 0, has.blue and 1 or 0)
for _, npc in ipairs(self.npcs) do for _, npc in ipairs(self.npcs) do
love.graphics.print("?", (npc.x - 1) * cellW + cellW * 0.3, (npc.y - 1) * cellH + cellH * 0.1, 0, 2, 2) love.graphics.print("?", (npc.x - 1) * cellW + cellW * 0.3, (npc.y - 1) * cellH + cellH * 0.1, 0, 2, 2)
end end
@ -698,6 +698,12 @@ end
function Room:deserialize(saveTable) function Room:deserialize(saveTable)
for _, obj in ipairs(saveTable.objects or {}) do for _, obj in ipairs(saveTable.objects or {}) do
self:createEntity(obj.class, obj.color, obj.x, obj.y) self:createEntity(obj.class, obj.color, obj.x, obj.y)
-- createEntity turns every loaded player on; honour a saved active flag so
-- copies that were off at save time stay off (older savs lack the field and
-- keep the old all-on behaviour).
if obj.class == "player" and obj.active ~= nil then
self:setActiveColor(obj.color, obj.active)
end
end end
end end
@ -752,7 +758,14 @@ function Room:serialize(includePlayers)
if includePlayers ~= false then if includePlayers ~= false then
for color, player in pairs(self.player) do for color, player in pairs(self.player) do
emit("player", color, player) local pos = player:getGridPos()
-- Persist which copies were active so a resume restores exactly the
-- channels that were on at save time. A colour deactivated during play
-- (see playerJoinCheck) must come back as an inactive copy, not turn on.
table.insert(objects, {
class = "player", color = color, x = pos.x, y = pos.y,
active = self.activeColors[color] and true or false,
})
end end
end end

View file

@ -1,152 +1,152 @@
{ {
width=18,
height=18,
rooms={ rooms={
{ {
x=9,
name="start", name="start",
y=11 y=11,
x=9
}, },
{ {
x=10,
name="3", name="3",
y=10 y=10,
x=10
}, },
{ {
x=11,
name="4",
y=10
},
{
x=12,
name="5", name="5",
y=10 y=10,
x=12
}, },
{ {
x=12,
name="village_1", name="village_1",
y=9 y=9,
x=12
}, },
{ {
x=12,
name="village_2", name="village_2",
y=8 y=8,
x=12
}, },
{ {
x=13,
name="village_3", name="village_3",
y=8 y=8,
x=13
}, },
{ {
x=11,
name="village_4", name="village_4",
y=8 y=8,
x=11
}, },
{ {
x=11,
name="yellow_1", name="yellow_1",
y=7 y=7,
x=11
}, },
{ {
x=11,
name="yellow_3", name="yellow_3",
y=6 y=6,
x=11
}, },
{ {
x=12,
name="yellow_2", name="yellow_2",
y=7 y=7,
x=12
}, },
{ {
x=11,
name="yellow_4", name="yellow_4",
y=5 y=5,
x=11
}, },
{ {
x=11,
name="yellow_5", name="yellow_5",
y=4 y=4,
x=11
}, },
{ {
x=10,
name="yellow_6", name="yellow_6",
y=5 y=5,
x=10
}, },
{ {
x=1,
name="default", name="default",
y=1 y=1,
x=1
}, },
{ {
x=3,
name="old_yellow_3", name="old_yellow_3",
y=1 y=1,
x=3
}, },
{ {
x=5,
name="test_patt", name="test_patt",
y=1 y=1,
x=5
}, },
{ {
x=7,
name="test_patt_update", name="test_patt_update",
y=1 y=1,
x=7
}, },
{ {
x=9,
name="white_asymmetrical", name="white_asymmetrical",
y=1 y=1,
x=9
}, },
{ {
x=11,
name="white_ok", name="white_ok",
y=1 y=1,
x=11
}, },
{ {
x=13,
name="white_primary", name="white_primary",
y=1 y=1,
x=13
}, },
{ {
x=15,
name="yellow_arches", name="yellow_arches",
y=1 y=1,
x=15
}, },
{ {
x=17,
name="yellow_asymmetrical", name="yellow_asymmetrical",
y=1 y=1,
x=17
}, },
{ {
x=1,
name="yellow_cc_moustache", name="yellow_cc_moustache",
y=3 y=3,
x=1
}, },
{ {
x=3,
name="yellow_color_changing_levvel_1", name="yellow_color_changing_levvel_1",
y=3 y=3,
x=3
}, },
{ {
x=5,
name="yellow_skull",
y=3
},
{
x=7,
name="yellow_smile", name="yellow_smile",
y=3 y=3,
x=7
}, },
{ {
x=9,
name="2", name="2",
y=10 y=10,
x=9
}, },
{ {
x=11,
name="room_11_09", name="room_11_09",
y=9 y=9,
x=11
},
{
name="4",
y=3,
x=5
},
{
name="yellow_skull",
y=3,
x=9
} }
}, },
lastRoom="yellow_skull" lastRoom="start",
} width=18,
height=18
}

View file

@ -156,7 +156,7 @@ function World:drawRoomPreview(name, x, y, size)
love.graphics.setScissor(x, y, size, size) love.graphics.setScissor(x, y, size, size)
for _, object in ipairs(room.objects or {}) do for _, object in ipairs(room.objects or {}) do
if object.class == "npc" then if object.class == "npc" then
love.graphics.setColor(255, 255, 255, 255) love.graphics.setColor(1, 1, 1, 1)
love.graphics.print("?", x + (object.x - 1) * 16 * scale, y + (object.y - 1) * 16 * scale, 0, scale, scale) love.graphics.print("?", x + (object.x - 1) * 16 * scale, y + (object.y - 1) * 16 * scale, 0, scale, scale)
else else
local props = globalAssetProperties[object.class] local props = globalAssetProperties[object.class]
@ -182,14 +182,14 @@ end
function World:draw() function World:draw()
local cell = width / self.width local cell = width / self.width
love.graphics.setColor(12, 12, 20, 255) love.graphics.setColor(12 / 255, 12 / 255, 20 / 255, 1)
love.graphics.rectangle("fill", 0, 0, width, height) love.graphics.rectangle("fill", 0, 0, width, height)
for y = 1, self.height do for y = 1, self.height do
for x = 1, self.width do for x = 1, self.width do
local screenX, screenY = (x - 1) * cell, (y - 1) * cell local screenX, screenY = (x - 1) * cell, (y - 1) * cell
local name = self:roomAt(x, y) local name = self:roomAt(x, y)
if name then self:drawRoomPreview(name, screenX, screenY, cell) end 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.setColor(name and 1 or 70 / 255, name and 1 or 70 / 255, name and 1 or 90 / 255, 1)
love.graphics.rectangle("line", screenX, screenY, cell, cell) love.graphics.rectangle("line", screenX, screenY, cell, cell)
end end
end end