diff --git a/assets/config.json b/assets/config.json index 90d30b7..5cb049b 100644 --- a/assets/config.json +++ b/assets/config.json @@ -1,4 +1,9 @@ { + "sounds": { + "sword_1": "assets/sword.1.ogg", + "monster_16": "assets/monster-16.ogg", + "monster_1": "assets/monster-1.ogg" + }, "sprites": { "armored_knight": "assets/armored_knight_1-256.png", "sword": "assets/cinqueda_1-512.png", diff --git a/assets/enemies.json b/assets/enemies.json index 9238eae..bf48e8c 100644 --- a/assets/enemies.json +++ b/assets/enemies.json @@ -23,7 +23,8 @@ {"_type": "Motion", "dx": 0, "dy": 0, "random": false}, {"_type": "EnemyConfig", "hearing_distance": 5}, {"_type": "Animation", "scale": 0.2, "simple": true, "frames": 10, "speed": 0.3}, - {"_type": "Sprite", "name": "armored_knight"} + {"_type": "Sprite", "name": "armored_knight"}, + {"_type": "Sound", "attack": "monster_1", "death": "monster_16"} ] }, "AXE_RANGER": { @@ -36,7 +37,8 @@ {"_type": "Motion", "dx": 0, "dy": 0, "random": true}, {"_type": "EnemyConfig", "hearing_distance": 5}, {"_type": "Sprite", "name": "axe_ranger"}, - {"_type": "Animation", "scale": 0.2, "simple": false, "frames": 10, "speed": 0.6} + {"_type": "Animation", "scale": 0.2, "simple": false, "frames": 10, "speed": 0.6}, + {"_type": "Sound", "attack": "sword_1", "death": "monster_16"} ] }, "EVIL_EYE": { @@ -49,7 +51,8 @@ {"_type": "Motion", "dx": 0, "dy": 0, "random": false}, {"_type": "EnemyConfig", "hearing_distance": 5}, {"_type": "Sprite", "name": "evil_eye"}, - {"_type": "Animation", "scale": 0.2, "simple": false, "frames": 10, "speed": 0.3} + {"_type": "Animation", "scale": 0.2, "simple": false, "frames": 10, "speed": 0.3}, + {"_type": "Sound", "attack": "monster_1", "death": "monster_16"} ] }, "RAT_GIANT": { @@ -62,7 +65,8 @@ {"_type": "Motion", "dx": 0, "dy": 0, "random": false}, {"_type": "EnemyConfig", "hearing_distance": 10}, {"_type": "Animation", "scale": 0.2, "simple": true, "frames": 10, "speed": 1.0}, - {"_type": "Sprite", "name": "rat_with_sword"} + {"_type": "Sprite", "name": "rat_with_sword"}, + {"_type": "Sound", "attack": "sword_1", "death": "monster_16"} ] }, "SPIDER_GIANT_HAIRY": { @@ -75,7 +79,8 @@ {"_type": "Motion", "dx": 0, "dy": 0, "random": false}, {"_type": "EnemyConfig", "hearing_distance": 10}, {"_type": "Animation", "scale": 0.2, "simple": true, "frames": 10, "speed": 1.0}, - {"_type": "Sprite", "name": "hairy_spider"} + {"_type": "Sprite", "name": "hairy_spider"}, + {"_type": "Sound", "attack": "sword_1", "death": "monster_16"} ] } } diff --git a/assets/monster-16.ogg b/assets/monster-16.ogg new file mode 100644 index 0000000..e723c3c Binary files /dev/null and b/assets/monster-16.ogg differ diff --git a/assets/sword.1.ogg b/assets/sword.1.ogg new file mode 100644 index 0000000..84c5423 Binary files /dev/null and b/assets/sword.1.ogg differ diff --git a/components.cpp b/components.cpp index 446f8fb..b5bde3b 100644 --- a/components.cpp +++ b/components.cpp @@ -13,6 +13,7 @@ namespace components { ENROLL_COMPONENT(Device, config, events); ENROLL_COMPONENT(Sprite, name); ENROLL_COMPONENT(Animation, scale, simple, frames, speed); + ENROLL_COMPONENT(Sound, attack, death); void configure_entity(const ComponentMap& component_map, DinkyECS::World& world, DinkyECS::Entity ent, json& data) { for (auto &i : data) { @@ -35,6 +36,7 @@ namespace components { components::enroll(component_map); components::enroll(component_map); components::enroll(component_map); + components::enroll(component_map); } void Animation::play() { diff --git a/components.hpp b/components.hpp index f93cb53..cdbc2b9 100644 --- a/components.hpp +++ b/components.hpp @@ -86,6 +86,11 @@ namespace components { string name; }; + struct Sound { + std::string attack; + std::string death; + }; + struct Animation { float scale = 0.0f; bool simple = true; diff --git a/main.cpp b/main.cpp index 9ff9d67..316fc5b 100644 --- a/main.cpp +++ b/main.cpp @@ -1,8 +1,10 @@ #include "gui_fsm.hpp" #include "textures.hpp" +#include "sound.hpp" int main() { textures::init(); + sound::init(); gui::FSM main; main.event(gui::Event::STARTED); diff --git a/meson.build b/meson.build index 9733cbb..c908058 100644 --- a/meson.build +++ b/meson.build @@ -79,6 +79,7 @@ sources = [ 'render.cpp', 'save.cpp', 'shiterator.hpp', + 'sound.cpp', 'spatialmap.cpp', 'stats.cpp', 'status_ui.cpp', @@ -104,6 +105,7 @@ executable('runtests', sources + [ 'tests/map.cpp', 'tests/matrix.cpp', 'tests/pathing.cpp', + 'tests/sound.cpp', 'tests/spatialmap.cpp', 'tests/textures.cpp', 'tests/tilemap.cpp', diff --git a/sound.cpp b/sound.cpp new file mode 100644 index 0000000..d278ec3 --- /dev/null +++ b/sound.cpp @@ -0,0 +1,54 @@ +#include "sound.hpp" +#include "dbc.hpp" +#include +#include "config.hpp" + +namespace sound { + static SoundManager SMGR; + static bool initialized = false; + + using namespace fmt; + using std::make_shared; + namespace fs = std::filesystem; + + void init() { + if(!initialized) { + Config assets("assets/config.json"); + + for(auto& el : assets["sounds"].items()) { + load(el.key(), el.value()); + } + initialized = true; + } + } + + void load(const std::string name, const std::string sound_path) { + dbc::check(fs::exists(sound_path), fmt::format("sound file {} does not exist", sound_path)); + + // create the buffer and keep in the buffer map + auto buffer = make_shared(sound_path); + + // set it on the sound and keep in the sound map + auto sound = make_shared(*buffer); + sound->setRelativeToListener(false); + sound->setPosition({0.0f, 0.0f, 1.0f}); + + SMGR.sounds.try_emplace(name, buffer, sound); + } + + void play(const std::string name) { + dbc::check(initialized, "You need to call sound::init() first"); + dbc::check(SMGR.sounds.contains(name), fmt::format("sound {} is not loaded in map", name)); + // get the sound from the sound map + auto pair = SMGR.sounds.at(name); + // play it + pair.sound->play(); + } + + void play_at(const std::string name, float x, float y, float z) { + dbc::check(initialized, "You need to call sound::init() first"); + auto pair = SMGR.sounds.at(name); + pair.sound->setPosition({x, y, z}); + pair.sound->play(); + } +} diff --git a/sound.hpp b/sound.hpp new file mode 100644 index 0000000..720a5cc --- /dev/null +++ b/sound.hpp @@ -0,0 +1,22 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace sound { + struct SoundPair { + std::shared_ptr buffer; + std::shared_ptr sound; + }; + + struct SoundManager { + std::unordered_map sounds; + }; + + void init(); + void load(const std::string name, const std::string path); + void play(const std::string name); + void play_at(const std::string name, float x, float y, float z); +} diff --git a/systems.cpp b/systems.cpp index 12c0cf8..b43b6c4 100644 --- a/systems.cpp +++ b/systems.cpp @@ -8,6 +8,7 @@ #include "lights.hpp" #include "inventory.hpp" #include "events.hpp" +#include "sound.hpp" using std::string; using namespace fmt; @@ -133,6 +134,10 @@ void System::death(GameLevel &level, components::ComponentMap& components) { world.remove(ent); world.remove(ent); + if(auto snd = world.get_if(ent)) { + sound::play(snd->death); + } + auto entity_data = config.items["GRAVE_STONE"]; components::configure_entity(components, world, ent, entity_data["components"]); if(entity_data["inventory_count"] > 0) { @@ -167,6 +172,10 @@ void System::combat(GameLevel &level) { animation.play(); } + if(auto snd = world.get_if(entity)) { + sound::play(snd->attack); + } + world.send(Events::GUI::COMBAT, entity, result); } } diff --git a/tests/sound.cpp b/tests/sound.cpp new file mode 100644 index 0000000..8279141 --- /dev/null +++ b/tests/sound.cpp @@ -0,0 +1,15 @@ +#include +#include +#include +#include "sound.hpp" +#include "levelmanager.hpp" + +using namespace fmt; + +TEST_CASE("test sound manager", "[sound]") { + sound::init(); + + sound::play("sword_1"); + + sound::play_at("sword_1", 0.1, 0.1, 0.1); +}