diff --git a/assets/audio/music/level1/bass/bass.ogg b/assets/audio/music/level1/bass/bass.ogg new file mode 100644 index 0000000..6e2b785 Binary files /dev/null and b/assets/audio/music/level1/bass/bass.ogg differ diff --git a/assets/audio/music/level1/battle.ogg b/assets/audio/music/level1/battle.ogg new file mode 100644 index 0000000..0984bb9 Binary files /dev/null and b/assets/audio/music/level1/battle.ogg differ diff --git a/assets/audio/music/level1/choral.ogg b/assets/audio/music/level1/choral.ogg new file mode 100644 index 0000000..eea732b Binary files /dev/null and b/assets/audio/music/level1/choral.ogg differ diff --git a/assets/audio/music/level1/drums.ogg b/assets/audio/music/level1/drums.ogg new file mode 100644 index 0000000..731cb32 Binary files /dev/null and b/assets/audio/music/level1/drums.ogg differ diff --git a/assets/audio/music/level1/flute.ogg b/assets/audio/music/level1/flute.ogg new file mode 100644 index 0000000..777de17 Binary files /dev/null and b/assets/audio/music/level1/flute.ogg differ diff --git a/assets/audio/music/level1/guitar.ogg b/assets/audio/music/level1/guitar.ogg new file mode 100644 index 0000000..47ce20b Binary files /dev/null and b/assets/audio/music/level1/guitar.ogg differ diff --git a/assets/audio/music/level1/violin.ogg b/assets/audio/music/level1/violin.ogg new file mode 100644 index 0000000..d216526 Binary files /dev/null and b/assets/audio/music/level1/violin.ogg differ diff --git a/assets/audio/sounds/hurt.ogg b/assets/audio/sounds/hurt.ogg new file mode 100644 index 0000000..7b921cf Binary files /dev/null and b/assets/audio/sounds/hurt.ogg differ diff --git a/lib/audio.lua b/lib/audio.lua new file mode 100644 index 0000000..3331ab5 --- /dev/null +++ b/lib/audio.lua @@ -0,0 +1,91 @@ +local ease = require "lib.utils.easing" +local AnimationNode = require "lib.animation_node" + +--- @alias SourceFilter { type: "bandpass"|"highpass"|"lowpass", volume: number, highgain: number, lowgain: number } + +--- @class Audio +--- @field musicVolume number +--- @field soundVolume number +--- @field looped boolean +--- @field animationNode AnimationNode? +--- @field from love.Source? +--- @field to love.Source? +audio = {} +audio.__index = audio + +--- здесь мы должны выгружать значения из файлика с сохранением настроек +local function new(musicVolume, soundVolume) + return setmetatable({ + musicVolume = musicVolume, + soundVolume = soundVolume, + looped = true + }, audio) +end + +function audio:update(dt) + if self.animationNode and self.animationNode.state == "running" then + self.animationNode:update(dt) + self.from:setVolume(self.musicVolume - self.animationNode:getValue() * self.musicVolume) + self.to:setVolume(self.animationNode:getValue() * self.musicVolume) + -- print(self.animationNode.t) + elseif self.animationNode and self.animationNode.state == "finished" then + self.from:stop() + self.animationNode:finish() + self.animationNode = nil + end +end + +--- if from is nil, than we have fade in to; +--- if to is nil, than we have fade out from +--- +--- also we should guarantee, that from and to have the same volume +--- @param from love.Source +--- @param to love.Source +--- @param ms number? in milliseconds +function audio:crossfade(from, to, ms) + print("[Audio]: Triggered crossfade") + self:play(to) + to:setVolume(0) + self.from = from + self.to = to + self.animationNode = AnimationNode { + function(node) end, + onEnd = function() + self.from:setVolume(0) + self.to:setVolume(self.musicVolume) + print("[Audio]: Crossfade done") + end, + duration = ms or 1000, + easing = ease.easeOutCubic, + } + self.animationNode:run() +end + +--- @param source love.Source +--- @param settings SourceFilter? +--- @param effectName string? +function audio:play(source, settings, effectName) + if settings then + source:setFilter(settings) + end + if effectName then + source:setEffect(effectName, true) + end + if source:getType() == "stream" then + source:setLooping(self.looped) + source:setVolume(self.musicVolume) + return source:play() + end + source:setVolume(self.soundVolume) + return source:play() +end + +function audio:setMusicVolume(volume) + self.musicVolume = volume +end + +function audio:setSoundVolume(volume) + self.soundVolume = volume +end + +return { new = new } diff --git a/lib/level/level.lua b/lib/level/level.lua index 17ddb34..9e6950c 100644 --- a/lib/level/level.lua +++ b/lib/level/level.lua @@ -19,6 +19,9 @@ level.__index = level local function new(type, template) local size = Vec3 { 30, 30 } -- magic numbers for testing purposes only print(type, template, size) + + Tree.audio:play(Tree.assets.files.audio.music.level1.battle) + return setmetatable({ size = size, characters = {}, diff --git a/lib/music.lua b/lib/music.lua new file mode 100644 index 0000000..2c51874 --- /dev/null +++ b/lib/music.lua @@ -0,0 +1,52 @@ +-- --- @class Music +-- --- @field source table audio streams, that supports multitrack (kind of) +-- --- @field offset number +-- music = {} +-- music.__index = music + +-- --- @param path string accepts path to dir with some music files (example: "main_ambient"; "player/theme1" and etc etc) +-- local function new(path) +-- local dir = Tree.assets.files.audio.music[path] +-- --- @type table +-- local source = {} +-- print(dir) + +-- for _, v in pairs(dir) do +-- print(v.filename) +-- source[v.filename] = v.source +-- print(v.filename) +-- end + +-- print('[music]: new source: ', table.concat(source, ' ')) + +-- return setmetatable({ source = source, offset = 0 }, music) +-- end + +-- function music:update() +-- for _, v in ipairs(self.source) do +-- v:seek() +-- end +-- end + +-- --- pause stemfile or music at all +-- --- @param filename? string +-- function music:pause(filename) +-- if filename then +-- self.source[filename]:pause() +-- else +-- for _, v in pairs(self.source) do +-- v:pause() +-- end +-- end +-- end + +-- --- play music stemfile by his name +-- --- @param filename string +-- --- @return boolean +-- function music:play(filename) +-- print('[music]: ', table.concat(self.source, ' ')) +-- self.source[filename]:seek(self.offset, "seconds") +-- return self.source[filename]:play() +-- end + +-- return { new = new } diff --git a/lib/sound.lua b/lib/sound.lua new file mode 100644 index 0000000..74b554f --- /dev/null +++ b/lib/sound.lua @@ -0,0 +1,9 @@ +-- --- @class Sound +-- --- @field source love.Source just a sound +-- sound = {} + +-- local function new() +-- return setmetatable({}, sound) +-- end + +-- return { new } diff --git a/lib/spellbook.lua b/lib/spellbook.lua index 51b0fa6..e328371 100644 --- a/lib/spellbook.lua +++ b/lib/spellbook.lua @@ -52,7 +52,9 @@ function walk:cast(caster, target) local sprite = caster:has(Tree.behaviors.sprite) if not sprite then return true end AnimationNode { - function(node) caster:has(Tree.behaviors.tiled):followPath(path, node) end, + function(node) + caster:has(Tree.behaviors.tiled):followPath(path, node) + end, onEnd = function() caster:has(Tree.behaviors.spellcaster):endCast() end, }:run() @@ -100,7 +102,10 @@ function regenerateMana:cast(caster, target) } AnimationNode { function(node) + local audioPath = Tree.assets.files.audio sprite:animate("hurt", node) + Tree.audio:crossfade(audioPath.music.level1.battle, + audioPath.music.level1.choral, 5000) end, onEnd = function() caster:has(Tree.behaviors.spellcaster):endCast() end }:run() @@ -163,7 +168,16 @@ function attack:cast(caster, target) children = { AnimationNode { function(node) + local audioPath = Tree.assets.files.audio targetSprite:animate("hurt", node) + --- @type SourceFilter + local settings = { + type = "bandpass", + volume = 1, + highgain = 0.1, + lowgain = 0.1 + } + Tree.audio:play(audioPath.sounds.hurt, settings) end } } diff --git a/lib/tree.lua b/lib/tree.lua index a023fdc..ea17840 100644 --- a/lib/tree.lua +++ b/lib/tree.lua @@ -9,9 +9,11 @@ Tree = { Tree.fonts = (require "lib.utils.font_manager"):load("WDXL_Lubrifont_TC"):loadTheme("Roboto_Mono") -- дефолтный шрифт Tree.panning = require "lib/panning" Tree.controls = require "lib.controls" +Tree.audio = (require "lib.audio").new(1, 1) Tree.level = (require "lib.level.level").new("procedural", "flower_plains") -- для теста у нас только один уровень, который сразу же загружен Tree.behaviors = (require "lib.utils.behavior_loader")("lib/character/behaviors") --- @todo написать нормальную загрузку поведений +-- Tree.audio = (require "lib.audio").new(1, 1) -- Tree.behaviors.map = require "lib.character.behaviors.map" -- Tree.behaviors.spellcaster = require "lib.character.behaviors.spellcaster" -- Tree.behaviors.sprite = require "lib.character.behaviors.sprite" diff --git a/lib/utils/asset_bundle.lua b/lib/utils/asset_bundle.lua index b8e355b..5db9cde 100644 --- a/lib/utils/asset_bundle.lua +++ b/lib/utils/asset_bundle.lua @@ -50,6 +50,10 @@ function AssetBundle.loadFile(path) return love.graphics.newShader(path); elseif (ext == "lua") then return require(string.gsub(path, ".lua", "")) + elseif (ext == "ogg") and string.find(path, "sounds") then + return love.audio.newSource(path, 'static') + elseif (ext == "ogg") and string.find(path, "music") then + return love.audio.newSource(path, 'stream') end return filedata end diff --git a/main.lua b/main.lua index 26d002d..3cd3ddd 100644 --- a/main.lua +++ b/main.lua @@ -52,6 +52,7 @@ function love.update(dt) testLayout:update(dt) -- потом UI, потому что нужно перехватить жесты и не пустить их дальше Tree.panning:update(dt) Tree.level:update(dt) + Tree.audio:update(dt) Tree.controls:cache()