we're back baby

This commit is contained in:
Your Name 2026-08-07 01:57:48 -04:00
parent febaaaacc7
commit e065ab83de
3 changed files with 575 additions and 577 deletions

View file

@ -2,53 +2,33 @@ require "entity"
Door = class("Door", Entity) Door = class("Door", Entity)
function Door:initialize(x, y) function Door:initialize(x, y, color)
self.doorColor = color
Entity.initialize(self, { Entity.initialize(self, {
x = x, x = x,
y = y, y = y,
color = "white", color = color,
collision = "unmoveable", collision = "unmoveable",
sprites = { sprite = loadSprite("art/game/door_" .. color:sub(1, 1) .. ".png"),
red = loadSprite("art/game/door_r.png"),
green = loadSprite("art/game/door_g.png"),
blue = loadSprite("art/game/door_b.png"),
},
}) })
self.locked = true self.locked = true
self.colors = {}
for _, color in pairs({"red", "green", "blue"}) do
self.colors[color] = true --these are currently loc_ed
end
self:refreshSprites() self:refreshSprites()
end end
-- keep the per-channel sprite map in sync with lock state so the shared -- A door is one normal, colour-layered entity. The editor can therefore place,
-- Entity:draw(channel) renders the door with no door-specific draw code -- delete, and serialize its red/green/blue instances independently.
function Door:refreshSprites() function Door:refreshSprites()
self.sprites = {} local sprite
for _, c in ipairs({"red", "green", "blue"}) do
if self.locked then if self.locked then
if self.colors[c] then self.sprites[c] = loadSprite("art/game/door_" .. c:sub(1, 1) .. ".png") end sprite = loadSprite("art/game/door_" .. self.doorColor:sub(1, 1) .. ".png")
else else
self.sprites[c] = loadSprite("art/game/door_unlocked.png") sprite = loadSprite("art/game/door_unlocked.png")
end
end end
self.sprites = { [self.doorColor] = sprite }
end end
function Door:setColors(colors) function Door:setColors(colors)
local offColors = {} if colors[self.doorColor] then
for color, off in pairs(colors) do
if off then
if self.colors[color] then self.colors[color] = false end
table.insert(offColors, color)
else
if not self.colors[color] then self.colors[color] = true end
end
end
if #offColors == 3 then
if self.locked then self:unlock() end if self.locked then self:unlock() end
else else
if not self.locked then self:lock() end if not self.locked then self:lock() end

View file

@ -23,7 +23,7 @@ function Room:initialize(t)
if t.name then if t.name then
print("loaded!!") print("loaded!!")
self:load(t.name) -- registers objects (and any saved players) from the .sav self:load(t.name, t.runtime) -- registers objects (and any saved players) from the .sav
print(t.name) print(t.name)
if t.player then if t.player then
@ -423,10 +423,6 @@ function Room:tick()
for _, door in pairs(self.doors) do for _, door in pairs(self.doors) do
door:setColors(setDoorColors) door:setColors(setDoorColors)
end 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() self:nextRoomCheck()
end end
@ -541,8 +537,16 @@ function Room:createEntity(class, color, x, y)
local entity local entity
if behavior == "door" then if behavior == "door" then
entity = Door:new(x, y) -- Old migrated rooms stored a colourless shared door. Expand that legacy
self:registerDoor(entity) -- record into ordinary colour-layer door objects on load.
if not color then
for _, doorColor in ipairs({"red", "green", "blue"}) do
if props.sprites[doorColor] then self:createEntity(class, doorColor, x, y) end
end
return
end
entity = Door:new(x, y, color)
self:registerDoor(entity, color)
elseif behavior == "switch" then elseif behavior == "switch" then
entity = Switch:new(x, y, color) entity = Switch:new(x, y, color)
self:registerSwitch(entity, color) self:registerSwitch(entity, color)
@ -571,9 +575,9 @@ function Room:registerCollidable(entityPointer, colorArray)
end end
end end
function Room:registerDoor(entityPointer) function Room:registerDoor(entityPointer, color)
table.insert(self.doors, entityPointer) table.insert(self.doors, entityPointer)
self:registerCollidable(entityPointer, {"red", "green", "blue"}) self:registerCollidable(entityPointer, color)
end end
function Room:registerSwitch(entityPointer, color) function Room:registerSwitch(entityPointer, color)
@ -607,6 +611,7 @@ function Room:attemptDelete(mouseX, mouseY, color)
colors = {color} colors = {color}
end end
local removedEntities = {}
for _, c in ipairs(colors) do for _, c in ipairs(colors) do
-- player -- player
if self.player[c] and self.player[c]:occupiesCell(cell) then if self.player[c] and self.player[c]:occupiesCell(cell) then
@ -620,6 +625,7 @@ function Room:attemptDelete(mouseX, mouseY, color)
local entityArray = self.collideables[c] local entityArray = self.collideables[c]
for index = #entityArray, 1, -1 do for index = #entityArray, 1, -1 do
if entityArray[index]:occupiesCell(cell) then if entityArray[index]:occupiesCell(cell) then
removedEntities[entityArray[index]] = true
table.remove(entityArray, index) table.remove(entityArray, index)
end end
end end
@ -631,6 +637,11 @@ function Room:attemptDelete(mouseX, mouseY, color)
end end
end end
end end
-- Behaviour registries hold references to the same objects as the colour
-- layers. Clean references by identity; no class gets bespoke delete rules.
for index = #self.doors, 1, -1 do
if removedEntities[self.doors[index]] then table.remove(self.doors, index) end
end
self:tick() self:tick()
end end
@ -656,11 +667,13 @@ function Room:attemptPlace(name, props, mouseX, mouseY, color)
self:tick() self:tick()
end end
function Room:load(name) function Room:load(name, preferRuntime)
-- Runtime rooms are game state: let LÖVE resolve them from its save -- Editor/startup loads must use the project source level. Runtime saves are
-- directory, where Room:save writes autosaves. Source files remain the -- only consulted for an in-progress room-to-room game transition, otherwise
-- shipped/editor versions until the editor explicitly saves one. -- an old runtime autosave can hide an editor Save after restart.
local room = love.filesystem.read("rooms/" .. name .. ".sav") local room
if preferRuntime then room = love.filesystem.read("rooms/" .. name .. ".sav") end
if not room then room = readFromSource("rooms/" .. name .. ".sav") end
if not room then if not room then
-- no save yet: start with a blank room to build in the editor -- no save yet: start with a blank room to build in the editor
print("no save for '" .. name .. "', starting blank") print("no save for '" .. name .. "', starting blank")
@ -704,21 +717,11 @@ function Room:serialize()
emit("player", color, player) emit("player", color, player)
end end
-- collideables hold walls/static_walls/generics on one color each, plus
-- doors registered across all three colors -> dedupe doors, emit once
local seenDoor = {}
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
if collideable:isInstanceOf(Door) then
if not seenDoor[collideable] then
seenDoor[collideable] = true
emit("door", nil, collideable)
end
else
emit(collideable.saveClass or "unmoveable", color, collideable) emit(collideable.saveClass or "unmoveable", color, collideable)
end end
end end
end
for color, switchArray in pairs(self.switches) do for color, switchArray in pairs(self.switches) do
for _, switch in ipairs(switchArray) do for _, switch in ipairs(switchArray) do
@ -758,6 +761,7 @@ function nextRoom(pos)
-- A placed neighbour gets the same carried player state at its opposite edge. -- A placed neighbour gets the same carried player state at its opposite edge.
currentRoom = Room:new{ currentRoom = Room:new{
name = nextName or currentName, name = nextName or currentName,
runtime = true,
player = { player = {
x = newPos.x, y = newPos.y, activeColors = currentRoom:getActiveColors() x = newPos.x, y = newPos.y, activeColors = currentRoom:getActiveColors()
} }

File diff suppressed because it is too large Load diff