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)
function Door:initialize(x, y)
function Door:initialize(x, y, color)
self.doorColor = color
Entity.initialize(self, {
x = x,
y = y,
color = "white",
color = color,
collision = "unmoveable",
sprites = {
red = loadSprite("art/game/door_r.png"),
green = loadSprite("art/game/door_g.png"),
blue = loadSprite("art/game/door_b.png"),
},
sprite = loadSprite("art/game/door_" .. color:sub(1, 1) .. ".png"),
})
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()
end
-- keep the per-channel sprite map in sync with lock state so the shared
-- Entity:draw(channel) renders the door with no door-specific draw code
-- A door is one normal, colour-layered entity. The editor can therefore place,
-- delete, and serialize its red/green/blue instances independently.
function Door:refreshSprites()
self.sprites = {}
for _, c in ipairs({"red", "green", "blue"}) do
if self.locked then
if self.colors[c] then self.sprites[c] = loadSprite("art/game/door_" .. c:sub(1, 1) .. ".png") end
else
self.sprites[c] = loadSprite("art/game/door_unlocked.png")
end
local sprite
if self.locked then
sprite = loadSprite("art/game/door_" .. self.doorColor:sub(1, 1) .. ".png")
else
sprite = loadSprite("art/game/door_unlocked.png")
end
self.sprites = { [self.doorColor] = sprite }
end
function Door:setColors(colors)
local offColors = {}
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 colors[self.doorColor] then
if self.locked then self:unlock() end
else
if not self.locked then self:lock() end

View file

@ -23,7 +23,7 @@ function Room:initialize(t)
if t.name then
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)
if t.player then
@ -423,10 +423,6 @@ 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
@ -541,8 +537,16 @@ function Room:createEntity(class, color, x, y)
local entity
if behavior == "door" then
entity = Door:new(x, y)
self:registerDoor(entity)
-- Old migrated rooms stored a colourless shared door. Expand that legacy
-- 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
entity = Switch:new(x, y, color)
self:registerSwitch(entity, color)
@ -571,9 +575,9 @@ function Room:registerCollidable(entityPointer, colorArray)
end
end
function Room:registerDoor(entityPointer)
function Room:registerDoor(entityPointer, color)
table.insert(self.doors, entityPointer)
self:registerCollidable(entityPointer, {"red", "green", "blue"})
self:registerCollidable(entityPointer, color)
end
function Room:registerSwitch(entityPointer, color)
@ -607,6 +611,7 @@ function Room:attemptDelete(mouseX, mouseY, color)
colors = {color}
end
local removedEntities = {}
for _, c in ipairs(colors) do
-- player
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]
for index = #entityArray, 1, -1 do
if entityArray[index]:occupiesCell(cell) then
removedEntities[entityArray[index]] = true
table.remove(entityArray, index)
end
end
@ -631,6 +637,11 @@ function Room:attemptDelete(mouseX, mouseY, color)
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()
end
@ -656,11 +667,13 @@ function Room:attemptPlace(name, props, mouseX, mouseY, color)
self:tick()
end
function Room:load(name)
-- 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")
function Room:load(name, preferRuntime)
-- Editor/startup loads must use the project source level. Runtime saves are
-- only consulted for an in-progress room-to-room game transition, otherwise
-- an old runtime autosave can hide an editor Save after restart.
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
-- no save yet: start with a blank room to build in the editor
print("no save for '" .. name .. "', starting blank")
@ -704,19 +717,9 @@ function Room:serialize()
emit("player", color, player)
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 _, 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)
end
emit(collideable.saveClass or "unmoveable", color, collideable)
end
end
@ -758,6 +761,7 @@ function nextRoom(pos)
-- A placed neighbour gets the same carried player state at its opposite edge.
currentRoom = Room:new{
name = nextName or currentName,
runtime = true,
player = {
x = newPos.x, y = newPos.y, activeColors = currentRoom:getActiveColors()
}

File diff suppressed because it is too large Load diff