sound and other misc fixes

This commit is contained in:
Your Name 2026-08-12 12:27:52 -04:00
parent 38eaba2fa6
commit 176216df8e
6 changed files with 97 additions and 15 deletions

View file

@ -6,6 +6,10 @@
Dialogable = {}
function Dialogable:onBump()
if self.book then Dialogue.open(self.book) end
if self.book then
local wasOpen = Dialogue.isOpen()
Dialogue.open(self.book)
if Sound and not wasOpen then Sound.jabber() end
end
return true
end

View file

@ -41,6 +41,7 @@ end
function Door:unlock()
self.locked = false
self.collision = "none"
if Sound then Sound.play("doorUnlock") end
end
function Door:lock()

View file

@ -10,6 +10,7 @@ require "_helpers"
require "input"
require "profiler"
require "color"
require "sound"
require "room"
require "world"
require "world_seed"
@ -26,6 +27,13 @@ narrator = require "narrator.narrator"
startSignBook = require "stories.start_sign"
jorgeBook = require "stories.jorge"
-- DEV/DEBUG mode. When on, the level editor is available and rooms with no
-- player copy present still render every channel, so whole levels stay visible
-- instead of a black screen. Turn it off for a player-facing build; both the
-- editor and that all-channels fallback then disappear. Override at launch with
-- the DEBUG env var (DEBUG=0 to force off, DEBUG=1 to force on).
DEBUG = os.getenv("DEBUG") ~= "0"
function love.load()
globalAssetProperties = {} -- rebuilt from the filesystem (art/ + .meta) by getAllAssets
@ -95,9 +103,9 @@ function love.draw()
love.graphics.setCanvas(gameCanvas)
love.graphics.clear()
love.graphics.setBlendMode("screen")
if editorView == "world" then
if DEBUG and editorView == "world" then
gameWorld:draw()
elseif editorView == "wire" then
elseif DEBUG and editorView == "wire" then
gameWorld:drawWireEditor(wireEditorCamera())
else
currentRoom:draw()
@ -127,17 +135,20 @@ function love.draw()
-- Render the sidebar at native panel resolution: the canvas is panel-sized,
-- and editorDraw works in the fixed virtual space, scaled up by a transform.
-- Blitting the canvas 1:1 afterwards means no rescaling of the final image
-- (crisp, nearest-neighbor throughout — nothing is averaged).
love.graphics.setCanvas(sideBarCanvas)
-- The gameplay view now intentionally extends beyond its old room bounds.
-- Keep the editor panel opaque so that neighbourhood preview never shows
-- through empty areas of the UI.
love.graphics.clear(0, 0, 0, 1)
love.graphics.push()
love.graphics.scale(editorRenderScale, editorRenderScale)
editorDraw()
love.graphics.pop()
love.graphics.setCanvas()
-- (crisp, nearest-neighbor throughout — nothing is averaged). The editor is a
-- DEBUG-only surface, so skip the whole panel in a player-facing build.
if DEBUG then
love.graphics.setCanvas(sideBarCanvas)
-- The gameplay view now intentionally extends beyond its old room bounds.
-- Keep the editor panel opaque so that neighbourhood preview never shows
-- through empty areas of the UI.
love.graphics.clear(0, 0, 0, 1)
love.graphics.push()
love.graphics.scale(editorRenderScale, editorRenderScale)
editorDraw()
love.graphics.pop()
love.graphics.setCanvas()
end
-- Collect every visible layer before the accessibility pass. Applying the
-- shader here also covers the editor sidebar, FPS display, and dialogue,
@ -147,7 +158,7 @@ function love.draw()
love.graphics.setBlendMode("alpha")
love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(finalCanvas, 0, 0)
love.graphics.draw(sideBarCanvas, sideBarBox.x, sideBarBox.y)
if DEBUG then love.graphics.draw(sideBarCanvas, sideBarBox.x, sideBarBox.y) end
love.graphics.print("Current FPS: "..tostring(love.timer.getFPS( )), 10, 10)
PerfProfiler:draw()
Dialogue.draw()
@ -208,6 +219,9 @@ function love.keypressed(key)
-- worldSha = worldSha - 1
-- end
-- Editor text-field controls (room-name entry etc.): DEBUG-only.
if not DEBUG then return end
if key == "return" then
response = ""
editorCompleteResponse()
@ -225,24 +239,31 @@ function love.keypressed(key)
end
end
-- Every mouse/text callback below only ever drives the editor, so they are
-- inert outside DEBUG: a player-facing build is keyboard-only.
function love.mousepressed(x, y, button)
if not DEBUG then return end
editorMouseHandler(x, y, button)
end
function love.mousemoved(x, y)
if not DEBUG then return end
editorMouseMoved(x, y)
end
function love.mousereleased(x, y, button)
if not DEBUG then return end
editorMouseReleased(x, y, button)
end
function love.textinput(t)
if not DEBUG then return end
response = response .. t
editorTextHandler(response)
end
function love.wheelmoved(dx, dy)
if not DEBUG then return end
-- Scroll the editor's asset/folder list when the cursor is over the sidebar.
if editorView == "room" and love.mouse.getX() > width then
editorScrollRoomList(dy)
@ -358,6 +379,8 @@ function cycleAccessibilityMode()
end
function _initSounds()
Sound.init()
staticWave = sfxr.newSound()
staticWave.waveform = sfxr.WAVEFORM.NOISE -- was `wavetype = 3`; field renamed in this sfxr
staticWave.frequency.start = .1

View file

@ -140,6 +140,9 @@ function Room:getRenderChannels()
for _, color in ipairs({"red", "green", "blue"}) do
if self.player[color] then table.insert(channels, color) end
end
-- No player copy lives here, so the view would be black. In debug/dev mode
-- render every channel instead, letting whole (player-less) levels be seen.
if #channels == 0 and DEBUG then return {"red", "green", "blue"} end
return channels
end
@ -456,6 +459,7 @@ function Room:movePlayerLocal(key)
end
if moved then
if Sound then Sound.play("playerMove") end
if useWorld then gameWorld:playerJoinCheck(self, previousPositions, newPositions)
else self:playerJoinCheck(previousPositions, newPositions) end
self:tick()
@ -1119,6 +1123,7 @@ function nextRoom(pos)
local currentName = currentRoom.name
local nextName = gameWorld and currentName and gameWorld:getNeighbor(currentName, exit)
if Sound and nextName and nextName ~= currentName then Sound.play("levelChange") end
currentRoom:save()
-- Missing map neighbours intentionally wrap back into the room just exited.

47
sound.lua Normal file
View file

@ -0,0 +1,47 @@
-- Central sound-effect registry. One preloaded Source per named effect; the game
-- calls Sound.play("name") to (re)trigger it. Ambient static + music live in
-- main.lua's _initSounds; this covers the one-shot gameplay/UI feedback.
--
-- Top level does NO audio calls on purpose: requiring this under the test love
-- stub (which never runs Sound.init) must be side-effect free. Until Sound.init
-- runs, Sound.sfx is empty and Sound.play is a harmless no-op.
Sound = {}
Sound.sfx = {} -- name -> Source
Sound.jabbers = {} -- pool of npc chatter Sources, picked at random
local function load(path)
return love.audio.newSource(path, "static")
end
function Sound.init()
Sound.sfx = {
playerMove = load("sounds/player_move.ogg"),
switchFail = load("sounds/switch_fail.wav"),
switchSuccess = load("sounds/switch_success.wav"),
doorUnlock = load("sounds/door_unlock.ogg"),
levelChange = load("sounds/level_change.ogg"),
uiButton = load("sounds/ui/button.wav"),
}
Sound.jabbers = {
load("sounds/jabber_1.ogg"),
load("sounds/jabber_2.ogg"),
}
end
-- Restart-and-play a named effect. A static Source only plays one instance at a
-- time, so stopping first makes rapid retriggers (e.g. mashing move) audible.
function Sound.play(name)
local src = Sound.sfx[name]
if not src then return end
src:stop()
src:play()
end
-- Pick a random npc jabber clip and play it. Used when a Dialogable starts talking.
function Sound.jabber()
if #Sound.jabbers == 0 then return end
local src = Sound.jabbers[love.math.random(#Sound.jabbers)]
src:stop()
src:play()
end

View file

@ -707,6 +707,7 @@ function World:movePlayer(room, key)
current[color] = player:getGridPos()
end
if moved then
if Sound then Sound.play("playerMove") end
self:markDirty(room)
self:playerJoinCheck(room, previous, current)
self:tick(room)
@ -811,6 +812,7 @@ function World:nextRoom(room, pos)
elseif pos.y > gridHeight then exit, entry.y = "bottom", 1
elseif pos.y < 1 then exit, entry.y = "top", gridHeight end
local nextName = self:getNeighbor(room.name, exit) or room.name
if Sound and nextName ~= room.name then Sound.play("levelChange") end
local sourceLocation = self:roomLocation(room.name)
local destinationLocation = self:roomLocation(nextName)
local activeColors = room:getActiveColors()