From 8c5a91acb224312ea511c7ff502c44bb9fe7452b Mon Sep 17 00:00:00 2001 From: cursed22bc Date: Wed, 11 Mar 2026 12:50:56 +0200 Subject: sfx and other polish --- .vscode/settings.json | 5 --- enemy.lua | 15 ++++++-- main.lua | 19 +++++++++ player.lua | 27 +++++++++++-- sfx.lua | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++ world.lua | 8 +++- 6 files changed, 167 insertions(+), 12 deletions(-) delete mode 100644 .vscode/settings.json create mode 100644 sfx.lua diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index a59b386..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "Lua.diagnostics.globals": [ - "love" - ] -} \ No newline at end of file diff --git a/enemy.lua b/enemy.lua index 152cadf..5c84a49 100644 --- a/enemy.lua +++ b/enemy.lua @@ -6,9 +6,12 @@ setmetatable(Enemy, { __index = Entity }) function Enemy.new(spawnX, spawnY, width, height, physicsWidth, physicsHeight) local self = setmetatable(Entity:new(spawnX or 0, spawnY or 0, width or 16, height or 16, physicsWidth or width or 8, physicsHeight or height or 8), Enemy) self.isEnemy = true + self.texture = love.graphics.newImage("assets/misc/enemy.png") + self.texture:setFilter("nearest", "nearest") self.direction = math.random(1, 2) == 1 and 1 or -1 + self.facing = self.direction self.speed = 20 - self.turnDelay = 0.35 + self.turnDelay = 1 self.turning = false self.turnTimer = 0 return self @@ -59,6 +62,8 @@ function Enemy:patrolPlatform(dt) self.turnTimer = self.turnDelay end + self.facing = self.direction + if self.body then local _, vy = self.body:getLinearVelocity() self.body:setLinearVelocity(self.speed * self.direction, vy) @@ -70,8 +75,12 @@ function Enemy:update(dt) end function Enemy:draw() - --bug: texture is drawn shifted by half width to the left - love.graphics.draw(self.texture, self.x + self.physicsWidth / 2, self.y) + local tw = self.texture:getWidth() + local th = self.texture:getHeight() + local sx = (self.facing == -1) and -1 or 1 + local cx = self.x + self.physicsWidth + local cy = self.y + self.physicsHeight /2 + love.graphics.draw(self.texture, cx, cy, 0, sx, 1, tw / 2, th / 2) end return Enemy \ No newline at end of file diff --git a/main.lua b/main.lua index 19c350d..6f2ee53 100644 --- a/main.lua +++ b/main.lua @@ -23,6 +23,7 @@ local cameraModule = require("camera") local camera = nil local fonts = require("fonts") local HUD = require("hud") +local sfx = require("sfx") local hud = nil local previousState = nil @@ -88,6 +89,8 @@ function states.game.load() gameFinishPhase = nil gameFinishTimer = 0 irisRadius = 0 + sfx.stop("UI_loop_song") + sfx.loop("game_ambient_loop") end function states.game.update(dt) @@ -167,12 +170,17 @@ function states.menu.load() menuTime = 0 love.mouse.setVisible(true) menuHover = nil + sfx.stop("game_ambient_loop") + sfx.stop("running") + sfx.setLiquidEffect(false) + sfx.loop("UI_loop_song") end function states.menu.update(dt) menuTime = menuTime + dt local mx, my = love.mouse.getPosition() local cx, cy = screenToCanvas(mx, my) + local prevHover = menuHover menuHover = nil for _, btn in ipairs(menuItems) do if cx >= btn.x and cx <= btn.x + btn.w and cy >= btn.y and cy <= btn.y + btn.h then @@ -180,6 +188,9 @@ function states.menu.update(dt) break end end + if menuHover and menuHover ~= prevHover then + sfx.play("UI") + end end function states.menu.draw() @@ -267,6 +278,8 @@ function love.load() canvas = love.graphics.newCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) canvas:setFilter("nearest", "nearest") + sfx.load() + local ok, shader = pcall(love.graphics.newShader, "shaders/smooth_camera.glsl") smoothCameraShader = ok and shader or nil if not smoothCameraShader then @@ -290,6 +303,10 @@ function love.load() local state = states[currentState] if state and state.load then state.load() end + + if currentState == "menu" then + sfx.loop("UI_loop_song") + end end function love.resize(w, h) @@ -325,10 +342,12 @@ function love.keypressed(key, scancode, isrepeat) previousState = currentState currentState = "hud" love.mouse.setVisible(true) + sfx.play("HUD") elseif currentState == "hud" then currentState = previousState or "game" previousState = nil love.mouse.setVisible(false) + sfx.play("HUD") end end if (key == "space" or key == "up" or key == "w") and not isrepeat then diff --git a/player.lua b/player.lua index 6e73691..42e56ad 100644 --- a/player.lua +++ b/player.lua @@ -1,5 +1,6 @@ local Entity = require("entity") local Animation = require("animation") +local sfx = require("sfx") local Player = {} Player.__index = Player @@ -27,7 +28,7 @@ function Player.new(world, spawnX, spawnY) self:enablePhysics(world, "dynamic") self.fixture:setFriction(0) - self.doubleJump = true + self.doubleJump = false self.jumpsUsed = 0 self.animations = { idle = Animation.new("assets/player/idle.png", 16, 16, 0.6), @@ -162,7 +163,9 @@ function Player:update(dt) self.body:applyForce(move * SWIM_SPEED, moveY * SWIM_SPEED) elseif onFloor or onWaterSurface then self.body:setLinearDamping(0) - self.jumpsUsed = 0 + if not prevOnFloor then + self.jumpsUsed = 0 + end if move ~= 0 then self.lastFacing = move self.body:setLinearVelocity(move * MOVE_SPEED, vy) @@ -203,6 +206,19 @@ function Player:update(dt) end end + if self.state ~= prevState then + if self.state == "running" then + sfx.loop("running") + elseif prevState == "running" then + sfx.stop("running") + end + if self.state == "ground_hit" then + sfx.play("land") + end + end + + sfx.setLiquidEffect(self.isInLiquid) + self.wasOnFloor = onFloor self.wasInLiquid = self.isInLiquid @@ -225,6 +241,8 @@ function Player:die(nx, ny) self.isDead = true self.state = "dead" self.respawnTimer = 4 + sfx.stop("running") + sfx.play("die") local len = math.sqrt(nx * nx + ny * ny) if len < 0.01 then @@ -260,7 +278,8 @@ function Player:jump() return false end - local maxJumps = self.doubleJump and 1 or 0 + self.doubleJump = (self.pickups["dbljump"] or 0) > 0 + local maxJumps = self.doubleJump and 2 or 1 if self.jumpsUsed >= maxJumps then return false @@ -272,6 +291,8 @@ function Player:jump() self.body:setLinearVelocity(self.body:getLinearVelocity(), JUMP_FORCE) + sfx.play("jump") + self.state = "jumping" self.animations.going_up:reset() diff --git a/sfx.lua b/sfx.lua new file mode 100644 index 0000000..c7e7377 --- /dev/null +++ b/sfx.lua @@ -0,0 +1,105 @@ +local sfx = {} + +sfx.sources = {} +sfx.volumes = {} +sfx.liquidActive = false + +local LIQUID_FILTER = { type = "lowpass", volume = 0.8, highgain = 0.15 } + +local MUSIC_SOURCES = { + game_ambient_loop = true, + UI_loop_song = true, +} + +local DEFAULT_VOLUMES = { + game_ambient_loop = 0.3, + UI_loop_song = 0.5, + running = 0.35, + die = 0.7, + dive = 0.6, + jump = 0.5, + land = 0.5, + HUD = 0.5, + pickup = 0.5, + UI = 0.5, +} + +function sfx.load() + local dir = "assets/sfx" + local ok, items = pcall(love.filesystem.getDirectoryItems, dir) + if not ok or not items then return end + + for _, filename in ipairs(items) do + local ext = filename:match("%.(%w+)$") + if ext == "ogg" or ext == "mp3" or ext == "wav" then + local name = filename:gsub("%.[^.]+$", "") + local sourceType = MUSIC_SOURCES[name] and "stream" or "static" + local success, src = pcall(love.audio.newSource, dir .. "/" .. filename, sourceType) + if success and src then + local vol = DEFAULT_VOLUMES[name] or 0.5 + src:setVolume(vol) + sfx.sources[name] = src + sfx.volumes[name] = vol + end + end + end +end + +function sfx.play(name) + local src = sfx.sources[name] + if not src then return end + src:stop() + src:setLooping(false) + if sfx.liquidActive and not MUSIC_SOURCES[name] then + pcall(src.setFilter, src, LIQUID_FILTER) + end + src:play() +end + +function sfx.loop(name) + local src = sfx.sources[name] + if not src then return end + if src:isPlaying() then return end + src:setLooping(true) + src:play() +end + +function sfx.stop(name) + local src = sfx.sources[name] + if not src then return end + src:stop() +end + +function sfx.isPlaying(name) + local src = sfx.sources[name] + return src and src:isPlaying() +end + +function sfx.setVolume(name, vol) + local src = sfx.sources[name] + if not src then return end + sfx.volumes[name] = vol + src:setVolume(vol) +end + +function sfx.setLiquidEffect(active) + if sfx.liquidActive == active then return end + sfx.liquidActive = active + for name, src in pairs(sfx.sources) do + if not MUSIC_SOURCES[name] then + if active then + pcall(src.setFilter, src, LIQUID_FILTER) + else + pcall(src.setFilter, src) + end + end + end +end + +function sfx.stopAll() + for _, src in pairs(sfx.sources) do + src:stop() + end +end + +return sfx diff --git a/world.lua b/world.lua index ff7a1ad..58dae39 100644 --- a/world.lua +++ b/world.lua @@ -6,6 +6,7 @@ local Textbox = require("textbox") local Enemy = require("enemy") local BloodParticle = require("bloodParticle") local DustSystem = require("dustParticle") +local sfx = require("sfx") local World = {} World.__index = World @@ -236,6 +237,7 @@ end function World:handlePickup(pickup, player) if pickup and player and pickup.isPickup and self:_isPlayerLike(player) then + if not pickup.collected then sfx.play("pickup") end pickup:pickup(self, player) end end @@ -250,7 +252,7 @@ function World:handleDoor(door, player) if keyCount >= needed then self.doorOpened = true self.playerTextbox:show( - "The door opens! You escaped!", + "Bye Bye!", { player.x + player.width / 2, player.y - 20, "center" }, "write", { life = 5, fontSize = 9, centeredText = true, wrapToFit = true } @@ -321,11 +323,15 @@ function World:_onBeginContact(udA, udB, nx, ny, contact) end if type(udA) == "table" and udA.type == "water" and self:_isTrackedEntity(udB) then + local wasInWater = (self.waterContacts[udB] or 0) > 0 self.waterContacts[udB] = (self.waterContacts[udB] or 0) + 1 doWaterSplash(udA, udB) + if not wasInWater and self:_isPlayerLike(udB) then sfx.play("dive") end elseif type(udB) == "table" and udB.type == "water" and self:_isTrackedEntity(udA) then + local wasInWater = (self.waterContacts[udA] or 0) > 0 self.waterContacts[udA] = (self.waterContacts[udA] or 0) + 1 doWaterSplash(udB, udA) + if not wasInWater and self:_isPlayerLike(udA) then sfx.play("dive") end end local playerEntity, hazardNx, hazardNy -- cgit v1.2.3