diff --git a/CMakeLists.txt b/CMakeLists.txt index 48dcf444..ff7ed3a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,6 +81,15 @@ set(SOURCES sources/Component/Animator/AnimatorComponent.hpp sources/System/Animator/AnimatorSystem.cpp sources/System/Animator/AnimatorSystem.hpp + sources/Component/Tag/TagComponent.hpp + sources/Component/Music/MusicComponent.cpp + sources/Component/Music/MusicComponent.hpp + sources/Component/Sound/SoundComponent.hpp + sources/Component/Sound/SoundComponent.cpp + sources/System/Sound/PlayerSoundManagerSystem.cpp + sources/System/Sound/PlayerSoundManagerSystem.hpp + sources/System/Music/MusicSystem.hpp + sources/System/Music/MusicSystem.cpp ) add_executable(bomberman sources/main.cpp @@ -92,6 +101,7 @@ target_link_libraries(bomberman PUBLIC wal ray) add_executable(unit_tests EXCLUDE_FROM_ALL ${SOURCES} + tests/CacheTest.cpp tests/EntityTests.cpp tests/MainTest.cpp tests/EngineTests.cpp diff --git a/assets/map/bumper.png b/assets/map/bumper.png index a86b67f2..63632844 100644 Binary files a/assets/map/bumper.png and b/assets/map/bumper.png differ diff --git a/assets/musics/music_win.ogg b/assets/musics/music_win.ogg index eadd8eff..ee5195e3 100644 Binary files a/assets/musics/music_win.ogg and b/assets/musics/music_win.ogg differ diff --git a/assets/sounds/bomb_drop.ogg b/assets/sounds/bomb_drop.ogg new file mode 100644 index 00000000..40288f0e Binary files /dev/null and b/assets/sounds/bomb_drop.ogg differ diff --git a/assets/sounds/death.ogg b/assets/sounds/death.ogg new file mode 100644 index 00000000..b5822604 Binary files /dev/null and b/assets/sounds/death.ogg differ diff --git a/assets/sounds/fuse.ogg b/assets/sounds/fuse.ogg new file mode 100644 index 00000000..1d3007e8 Binary files /dev/null and b/assets/sounds/fuse.ogg differ diff --git a/assets/sounds/jump.wav b/assets/sounds/jump.wav new file mode 100644 index 00000000..642ae89a Binary files /dev/null and b/assets/sounds/jump.wav differ diff --git a/assets/sounds/move.ogg b/assets/sounds/move.ogg new file mode 100644 index 00000000..fc035509 Binary files /dev/null and b/assets/sounds/move.ogg differ diff --git a/lib/Ray/sources/Audio/Music.cpp b/lib/Ray/sources/Audio/Music.cpp index db3dae78..0a9b2660 100644 --- a/lib/Ray/sources/Audio/Music.cpp +++ b/lib/Ray/sources/Audio/Music.cpp @@ -10,8 +10,8 @@ RAY::Cache<::Music> RAY::Audio::Music::_musicsCache(LoadMusicStream, UnloadMusicStream); -RAY::Audio::Music::Music(const std::string &path): - _music(this->_musicsCache.fetch(path.c_str())) +RAY::Audio::Music::Music(const std::string &path, bool lonely): + _music(this->_musicsCache.fetch(path, lonely)) { } @@ -55,3 +55,9 @@ RAY::Audio::Music &RAY::Audio::Music::setPitch(float pitch) SetMusicPitch(*_music, pitch); return *this; } + +RAY::Audio::Music &RAY::Audio::Music::updateMusicStream(void) +{ + UpdateMusicStream(*_music); + return *this; +} diff --git a/lib/Ray/sources/Audio/Music.hpp b/lib/Ray/sources/Audio/Music.hpp index c28e8089..1aca0cc3 100644 --- a/lib/Ray/sources/Audio/Music.hpp +++ b/lib/Ray/sources/Audio/Music.hpp @@ -19,7 +19,8 @@ namespace RAY::Audio public: //! @brief Load Music stream from file - Music(const std::string &path); + //! @param lonely: should be set to true if the entity's loaded data must be independant from others + Music(const std::string &path, bool lonely = false); //! @brief Default destructor ~Music() = default; @@ -51,6 +52,8 @@ namespace RAY::Audio // Set pitch for a Music (1.0 is base level) Music &setPitch(float pitch) override; + Music &updateMusicStream(void); + private: std::shared_ptr<::Music> _music; diff --git a/lib/Ray/sources/Audio/Sound.cpp b/lib/Ray/sources/Audio/Sound.cpp index da8df7ba..de1002bc 100644 --- a/lib/Ray/sources/Audio/Sound.cpp +++ b/lib/Ray/sources/Audio/Sound.cpp @@ -9,8 +9,8 @@ RAY::Cache<::Sound> RAY::Audio::Sound::_soundsCache(LoadSound, UnloadSound); -RAY::Audio::Sound::Sound(const std::string &path): - _sound(_soundsCache.fetch(path.c_str())) +RAY::Audio::Sound::Sound(const std::string &path, bool lonely): + _sound(_soundsCache.fetch(path, lonely)) { } diff --git a/lib/Ray/sources/Audio/Sound.hpp b/lib/Ray/sources/Audio/Sound.hpp index 00ba3ddc..0f7a7ff5 100644 --- a/lib/Ray/sources/Audio/Sound.hpp +++ b/lib/Ray/sources/Audio/Sound.hpp @@ -20,7 +20,8 @@ namespace RAY::Audio public: //! @brief Load Sound stream from file - Sound(const std::string &path); + //! @param lonely: should be set to true if the entity's loaded data must be independant from others + Sound(const std::string &path, bool lonely = false); //! @brief Default destructor ~Sound() = default; diff --git a/lib/Ray/sources/Drawables/Image.cpp b/lib/Ray/sources/Drawables/Image.cpp index 9676bc95..685aa376 100644 --- a/lib/Ray/sources/Drawables/Image.cpp +++ b/lib/Ray/sources/Drawables/Image.cpp @@ -12,9 +12,9 @@ namespace RAY { Cache<::Image> Image::_imagesCache(LoadImage, UnloadImage); - Image::Image(const std::string &filename): + Image::Image(const std::string &filename, bool lonely): Rectangle(Vector2(0, 0), Vector2(0, 0), WHITE), - _image(_imagesCache.fetch(filename)) + _image(_imagesCache.fetch(filename, lonely)) { this->_dimensions = Vector2(this->_image->width, this->_image->height); } diff --git a/lib/Ray/sources/Drawables/Image.hpp b/lib/Ray/sources/Drawables/Image.hpp index 840be518..911f1a96 100644 --- a/lib/Ray/sources/Drawables/Image.hpp +++ b/lib/Ray/sources/Drawables/Image.hpp @@ -21,7 +21,8 @@ namespace RAY public: //! @brief Create an image, loading a file //! @param filename: path to file to load - Image(const std::string &filename); + //! @param lonely: should be set to true if the entity's loaded data must be independant from others + Image(const std::string &filename, bool lonely = false); //! @brief A default copy constructor Image(const Image &image) = default; diff --git a/lib/Ray/sources/Drawables/Texture.cpp b/lib/Ray/sources/Drawables/Texture.cpp index befe8579..821a743d 100644 --- a/lib/Ray/sources/Drawables/Texture.cpp +++ b/lib/Ray/sources/Drawables/Texture.cpp @@ -11,8 +11,8 @@ namespace RAY { Cache<::Texture> Texture::_texturesCache(LoadTexture, UnloadTexture); - Texture::Texture(const std::string &filename): - _texture(_texturesCache.fetch(filename)), + Texture::Texture(const std::string &filename, bool lonely): + _texture(_texturesCache.fetch(filename, lonely)), _resourcePath(filename) { } diff --git a/lib/Ray/sources/Drawables/Texture.hpp b/lib/Ray/sources/Drawables/Texture.hpp index 6fb2be66..2952b58d 100644 --- a/lib/Ray/sources/Drawables/Texture.hpp +++ b/lib/Ray/sources/Drawables/Texture.hpp @@ -19,7 +19,8 @@ namespace RAY public: //! @brief Create an texture, loading a file //! @param filename: path to file to load - Texture(const std::string &filename); + //! @param lonely: should be set to true if the entity's loaded data must be independant from others + Texture(const std::string &filename, bool lonely = false); //! @brief A texture is copy constructable Texture(const Texture &) = default; diff --git a/lib/Ray/sources/Font.cpp b/lib/Ray/sources/Font.cpp index c4288463..89bf9517 100644 --- a/lib/Ray/sources/Font.cpp +++ b/lib/Ray/sources/Font.cpp @@ -9,8 +9,8 @@ RAY::Cache<::Font> RAY::Font::_fontsCache(LoadFont, UnloadFont); -RAY::Font::Font(const std::string &filename): - _font(_fontsCache.fetch(filename)) +RAY::Font::Font(const std::string &filename, bool lonely): + _font(_fontsCache.fetch(filename, lonely)) { } diff --git a/lib/Ray/sources/Font.hpp b/lib/Ray/sources/Font.hpp index a403dcb3..1aaeb173 100644 --- a/lib/Ray/sources/Font.hpp +++ b/lib/Ray/sources/Font.hpp @@ -19,7 +19,8 @@ namespace RAY public: //! @brief Create an font, loading a file //! @param filename: path to file to load - Font(const std::string &filename); + //! @param lonely: should be set to true if the entity's loaded data must be independant from others + Font(const std::string &filename, bool lonely = false); //! @brief A default copy constructor Font(const Font &) = default; diff --git a/lib/Ray/sources/Model/Model.cpp b/lib/Ray/sources/Model/Model.cpp index aab5fdc6..77951483 100644 --- a/lib/Ray/sources/Model/Model.cpp +++ b/lib/Ray/sources/Model/Model.cpp @@ -19,9 +19,9 @@ namespace RAY::Drawables::Drawables3D { const RAY::Vector3 &scale, const RAY::Vector3 &position, const RAY::Vector3 &rotationAxis, - float rotationAngle) + float rotationAngle, bool lonely) : ADrawable3D(position, WHITE), - _model(_modelsCache.fetch(filename)), + _model(_modelsCache.fetch(filename, lonely)), _rotationAxis(rotationAxis), _rotationAngle(rotationAngle), _scale(scale) diff --git a/lib/Ray/sources/Model/Model.hpp b/lib/Ray/sources/Model/Model.hpp index 7a7e3fdf..40c9a9dd 100644 --- a/lib/Ray/sources/Model/Model.hpp +++ b/lib/Ray/sources/Model/Model.hpp @@ -25,12 +25,13 @@ namespace RAY::Drawables::Drawables3D { //! @brief Create an model, loading a file //! @param filePath: path to file to load + //! @param lonely: should be set to true if the entity's loaded data must be independant from others Model(const std::string &filePath, std::optional> texture = std::nullopt, const RAY::Vector3 &scale = RAY::Vector3(1, 1, 1), const RAY::Vector3 &position = {0, 0, 0}, const RAY::Vector3 &rotationAxis = RAY::Vector3(0, 1, 0), - float rotationAngle = 0); + float rotationAngle = 0, bool lonely = false); //! @brief Create an model, loading a file //! @param mesh: mesh to load diff --git a/lib/Ray/sources/Utils/Cache.hpp b/lib/Ray/sources/Utils/Cache.hpp index 653933f5..cea5dd71 100644 --- a/lib/Ray/sources/Utils/Cache.hpp +++ b/lib/Ray/sources/Utils/Cache.hpp @@ -10,6 +10,10 @@ #include #include #include +#include +#include +#include +#include namespace RAY { //! @brief A templated class used to cache ressources, indexed with a string @@ -31,16 +35,28 @@ namespace RAY { Cache &operator=(const Cache &) = default; //! @param path path of the file + //! @param lonely: should be set to true if the loaded data must be held by no other active entites //! @return a newly loaded ressource if it hasn't be previously loaded, or one from cache - std::shared_ptrfetch(const std::string &path) + std::shared_ptrfetch(const std::string &path, bool lonely = false) { - if (this->_cache.find(path) == this->_cache.end()) - this->_cache.emplace(path, std::shared_ptr( - new T(this->_dataLoader(path.c_str())), [this](T *p) { - this->_dataUnloader(*p); - delete p; - })); - return _cache[path]; + if (!this->_cache.contains(path)) + this->_cache.emplace(path, std::vector>()); + std::vector> &matchingDataVector = this->_cache.at(path); + + if (matchingDataVector.size()) { + for (std::shared_ptr &i: matchingDataVector) { + if (!lonely) + return i; + if (lonely && i.use_count() == 1) + return i; + } + } + matchingDataVector.push_back(std::shared_ptr( + new T(this->_dataLoader(path.c_str())), [this](T *p) { + this->_dataUnloader(*p); + delete p; + })); + return matchingDataVector.back(); }; private: //! @brief function to call to load data @@ -50,7 +66,7 @@ namespace RAY { std::function _dataUnloader; //! @brief map storing shared ptr of caches - std::unordered_map> _cache; + std::unordered_map>> _cache; }; template<> diff --git a/lib/Ray/sources/Window.cpp b/lib/Ray/sources/Window.cpp index df332bb6..a9a1d9bf 100644 --- a/lib/Ray/sources/Window.cpp +++ b/lib/Ray/sources/Window.cpp @@ -49,6 +49,7 @@ bool RAY::Window::open(void) } InitWindow(this->_dimensions.x, this->_dimensions.y, this->_title.c_str()); this->_isOpen = true; + InitAudioDevice(); return true; } @@ -60,6 +61,7 @@ bool RAY::Window::shouldClose(void) const void RAY::Window::close(void) { CloseWindow(); + CloseAudioDevice(); } bool RAY::Window::isFocused(void) const diff --git a/sources/Component/Music/MusicComponent.cpp b/sources/Component/Music/MusicComponent.cpp new file mode 100644 index 00000000..40dc33b1 --- /dev/null +++ b/sources/Component/Music/MusicComponent.cpp @@ -0,0 +1,65 @@ +// +// Created by Tom Augier on 05/06/2021 +// + +#include +#include "MusicComponent.hpp" + +namespace BBM +{ + float MusicComponent::volume = 0.75; + + MusicComponent::MusicComponent(WAL::Entity &entity, const std::string &musicPath) + : WAL::Component(entity), + _musicPath(musicPath), + _music(RAY::Audio::Music(musicPath)) + { + } + + WAL::Component *MusicComponent::clone(WAL::Entity &entity) const + { + return new MusicComponent(entity, this->_musicPath); + } + + void MusicComponent::playMusic(void) + { + if (!this->_music.isPlaying()) { + this->_music.play(); + } + } + + void MusicComponent::stopMusic(void) + { + if (this->_music.isPlaying()) + this->_music.stop(); + } + + void MusicComponent::pauseMusic(void) + { + this->_music.pause(); + } + + void MusicComponent::setVolume(float &volumeUpdate) + { + if (volumeUpdate >= 0) { + this->volume = volumeUpdate; + this->_music.setVolume(this->volume); + } + } + + void MusicComponent::setPitch(float &pitch) + { + this->_music.setPitch(pitch); + } + + bool MusicComponent::isPlaying(void) + { + return (this->_music.isPlaying()); + } + + void MusicComponent::updateMusicStream(void) + { + this->_music.updateMusicStream(); + } + +} // namespace WAL diff --git a/sources/Component/Music/MusicComponent.hpp b/sources/Component/Music/MusicComponent.hpp new file mode 100644 index 00000000..7898fd6e --- /dev/null +++ b/sources/Component/Music/MusicComponent.hpp @@ -0,0 +1,55 @@ +// +// Created by Tom Augier on 05/06/2021 +// + +#pragma once + +#include "Component/Component.hpp" +#include +#include "Audio/Music.hpp" + +namespace BBM +{ + //! @brief A basic Music component + class MusicComponent : public WAL::Component + { + public: + //! @brief start music + void playMusic(); + + //! @brief stop music + void stopMusic(); + + //! @brief put music on hold + void pauseMusic(); + + //! @brief set music volume + void setVolume(float &); + + //! @brief set pitch volume + void setPitch(float &); + + //! @brief is music playing + bool isPlaying(void); + //! @brief update music stream + void updateMusicStream(void); + //! @inherit + WAL::Component *clone(WAL::Entity &entity) const override; + //! @brief Create a new MusicComponent at a certain Music + explicit MusicComponent(WAL::Entity &entity, const std::string &musicPath); + //! @brief A Music component is copy constructable + MusicComponent(const MusicComponent &) = default; + //! @brief A default destructor + ~MusicComponent() override = default; + //! @brief A Music component is not assignable + MusicComponent &operator=(const MusicComponent &) = delete; + //! @brief Volume of the muisc + static float volume; + private: + //! @brief music of this entity + RAY::Audio::Music _music; + //! @brief patht to the music assets + const std::string _musicPath; + }; + +} // namespace BBM \ No newline at end of file diff --git a/sources/Component/Sound/SoundComponent.cpp b/sources/Component/Sound/SoundComponent.cpp new file mode 100644 index 00000000..752efe9f --- /dev/null +++ b/sources/Component/Sound/SoundComponent.cpp @@ -0,0 +1,91 @@ +// +// Created by Tom Augier on 05/06/2021 +// + +#include +#include +#include "SoundComponent.hpp" + +namespace BBM +{ + float SoundComponent::volume = 0.75; + + SoundComponent::SoundComponent(WAL::Entity &entity, + const std::map &soundPath) + : WAL::Component(entity), + _soundIndex(IDLE), + _soundPath(soundPath) + { + for (int i = 0; i <= DEATH; i++) { + this->_isSoundLoad[static_cast(i)] = false; + } + for (auto &soundPath : soundPath) + { + this->_isSoundLoad[soundPath.first] = true; + this->_soundList[soundPath.first] = std::make_unique(soundPath.second); + } + } + + WAL::Component *SoundComponent::clone(WAL::Entity &entity) const + { + return new SoundComponent(entity, this->_soundPath); + } + + void SoundComponent::playSound() + { + if (!this->_isSoundLoad.at(this->_soundIndex)) + return; + if (!this->_soundList[this->_soundIndex].get()->isPlaying()) + this->_soundList[this->_soundIndex].get()->play(); + } + + void SoundComponent::stopSound() + { + if (!this->_isSoundLoad.at(this->_soundIndex)) + return; + if (this->_soundList[this->_soundIndex].get()->isPlaying()) + this->_soundList[this->_soundIndex].get()->stop(); + } + + void SoundComponent::pauseSound() + { + if (!this->_isSoundLoad.at(this->_soundIndex)) + return; + this->_soundList[this->_soundIndex].get()->pause(); + } + + void SoundComponent::setVolume(float &volumeUpdate) + { + if (!this->_isSoundLoad.at(this->_soundIndex)) + return; + if (volumeUpdate >= 0) { + this->volume = volumeUpdate; + this->_soundList[this->_soundIndex].get()->setVolume(this->volume); + } + } + + void SoundComponent::setPitch(float &pitch) + { + if (!this->_isSoundLoad.at(this->_soundIndex)) + return; + this->_soundList[this->_soundIndex].get()->setPitch(pitch); + } + + bool SoundComponent::isPlaying() + { + if (!this->_isSoundLoad.at(this->_soundIndex)) + return (false); + return (this->_soundList[this->_soundIndex].get()->isPlaying()); + } + + void SoundComponent::setIndex(SoundIndex index) + { + this->_soundIndex = index; + } + + SoundComponent::SoundIndex SoundComponent::getIndex() + { + return (this->_soundIndex); + } + +} // namespace WAL diff --git a/sources/Component/Sound/SoundComponent.hpp b/sources/Component/Sound/SoundComponent.hpp new file mode 100644 index 00000000..f5278114 --- /dev/null +++ b/sources/Component/Sound/SoundComponent.hpp @@ -0,0 +1,78 @@ +// +// Created by Tom Augier on 05/06/2021 +// + +#pragma once + +#include "Component/Component.hpp" +#include +#include "Audio/Sound.hpp" + +namespace BBM +{ + //! @brief A basic Sound component + class SoundComponent : public WAL::Component + { + public: + + //! @brief All sounds of the player + enum SoundIndex { + IDLE, + JUMP, + BOMB, + MOVE, + HURT, + THROW, + DEATH + }; + + //! @brief to set what sound should be played + void setIndex(SoundIndex index); + + //! @brief to know which sound is selected + SoundIndex getIndex(); + + //! @brief start sound + void playSound(); + + //! @brief stop sound + void stopSound(); + + //! @brief put Sound on hold + void pauseSound(); + + //! @brief set Sound volume + void setVolume(float &); + + //! @brief set pitch volume + void setPitch(float &); + + //! @brief is Sound playing + bool isPlaying(); + + //! @inherit + WAL::Component *clone(WAL::Entity &entity) const override; + //! @brief Create a new SoundComponent at a certain Sound + explicit SoundComponent(WAL::Entity &entity, const std::map &); + //! @brief A Sound component is copy constructable + SoundComponent(const SoundComponent &) = default; + //! @brief A default destructor + ~SoundComponent() override = default; + //! @brief A Sound component is not assignable + SoundComponent &operator=(const SoundComponent &) = delete; + //! @brief Volume of the sounds + static float volume; + + private: + //! @brief Sounds of this entity + std::map> _soundList; + //! @brief map to know if sound is loaded + std::map _isSoundLoad; + //! @brief All sounds path + const std::map _soundPath; + //! SoundIndex + SoundIndex _soundIndex; + + }; + +} // namespace BBM \ No newline at end of file diff --git a/sources/Component/Tag/TagComponent.hpp b/sources/Component/Tag/TagComponent.hpp new file mode 100644 index 00000000..00fa27d5 --- /dev/null +++ b/sources/Component/Tag/TagComponent.hpp @@ -0,0 +1,54 @@ +// +// Created by Zoe Roux on 6/9/21. +// + +#pragma once + +#include +#include + +namespace BBM +{ + template + struct StringLiteral + { + public: + char value[I]; + + //! @brief Implicitly convert an array of char to a string literal. + constexpr StringLiteral(const char (&str)[I]) // NOLINT(google-explicit-constructor) + : value() + { + std::copy_n(str, I, value); + } + //! @brief A string literal is copy constructable. + constexpr StringLiteral(const StringLiteral &) = default; + //! @brief A default destructor + constexpr ~StringLiteral() = default; + //! @brief A string literal is assignable. + constexpr StringLiteral &operator=(const StringLiteral &) = default; + }; + + template + class TagComponent : public WAL::Component + { + public: + Component *clone(WAL::Entity &entity) const override + { + return new TagComponent(entity); + } + + //! @brief Create a new empty tag component. + explicit TagComponent(WAL::Entity &entity) + : WAL::Component(entity) + {} + //! @brief A default copy constructor. + TagComponent(const TagComponent &) = default; + //! @brief A default destructor + ~TagComponent() override = default; + //! @brief A tag component is not assignable. + TagComponent &operator=(const TagComponent &) = delete; + }; + + constexpr const char Blowable[] = "Blowable"; +} diff --git a/sources/Map/Map.cpp b/sources/Map/Map.cpp index 5e8a9a2e..b5ca0268 100644 --- a/sources/Map/Map.cpp +++ b/sources/Map/Map.cpp @@ -6,7 +6,7 @@ #include "Component/Collision/CollisionComponent.hpp" #include "System/Collision/CollisionSystem.hpp" #include "Map.hpp" -#include +#include namespace RAY3D = RAY::Drawables::Drawables3D; @@ -23,7 +23,7 @@ namespace BBM if (collidedAxis & CollisionComponent::CollidedAxis::X) mov->_velocity.x = 0; if (collidedAxis & CollisionComponent::CollidedAxis::Y) - mov->_velocity.x = 0; + mov->_velocity.y = 0; if (collidedAxis & CollisionComponent::CollidedAxis::Z) mov->_velocity.z = 0; } @@ -56,6 +56,7 @@ namespace BBM if (!(i % 2) && !(j % 2)) { scene->addEntity("Unbreakable Wall") .addComponent(i, 0, j) + .addComponent>() .addComponent( WAL::Callback(), &MapGenerator::wallCollide, 0.25, .75) @@ -73,25 +74,28 @@ namespace BBM scene->addEntity("Bottom Wall") .addComponent(Vector3f((width + 1) / 2, 0, -1)) + .addComponent>() .addComponent( WAL::Callback(), - &MapGenerator::wallCollide, 0.25, .75) + &MapGenerator::wallCollide, Vector3f(-(width + 1) / 2 , 0.25, 0.25), Vector3f(width + 1, 2, 0.75)) .addComponent(unbreakableObj, std::make_pair(MAP_DIFFUSE, unbreakablePnj), RAY::Vector3(width + 3, 1, 1)); scene->addEntity("Upper Wall") .addComponent(Vector3f((width + 1) / 2, 0, height + 1)) + .addComponent>() .addComponent( WAL::Callback(), - &MapGenerator::wallCollide, 0.25, .75) + &MapGenerator::wallCollide, Vector3f(-(width + 1) / 2 , 0.25, 0.25), Vector3f(width + 1, 2, 0.75)) .addComponent(unbreakableObj, std::make_pair(MAP_DIFFUSE, unbreakablePnj), RAY::Vector3(width + 3, 1, 1)); scene->addEntity("Left Wall") .addComponent(Vector3f(width + 1, 0, height / 2)) + .addComponent>() .addComponent( WAL::Callback(), - &MapGenerator::wallCollide, 0.25, .75) + &MapGenerator::wallCollide, Vector3f(0.25, 0.25, -(height + 1) / 2 ), Vector3f(0.75, 2, height + 1)) .addComponent(unbreakableObj, std::make_pair(MAP_DIFFUSE, unbreakablePnj), RAY::Vector3(1, 1, height + 1)); @@ -99,7 +103,7 @@ namespace BBM .addComponent(Vector3f(-1, 0, height / 2)) .addComponent( WAL::Callback(), - &MapGenerator::wallCollide, 0.25, .75) + &MapGenerator::wallCollide, Vector3f(0.25, 0.25, -(height + 1) / 2 ), Vector3f(0.75, 2, height + 1)) .addComponent(unbreakableObj, std::make_pair(MAP_DIFFUSE, unbreakablePnj), RAY::Vector3(1, 1, height + 1)); @@ -129,7 +133,6 @@ namespace BBM {HOLE, &createHole}, {FLOOR, &createFloor}, {BUMPER, &createBumper}, - {STAIRS, &createStairs}, {UPPERFLOOR, &createUpperFloor}, }; @@ -148,6 +151,7 @@ namespace BBM scene->addEntity("Breakable Block") .addComponent(coords) + .addComponent>() .addComponent(1, &MapGenerator::wallDestroyed) .addComponent( WAL::Callback(), @@ -184,6 +188,7 @@ namespace BBM scene->addEntity("Unbreakable Block") .addComponent(coords) + .addComponent>() .addComponent( WAL::Callback(), &MapGenerator::wallCollide, 0.25, .75) @@ -231,17 +236,6 @@ namespace BBM }); */ } - void MapGenerator::createStairs(Vector3f coords, std::shared_ptr scene) - { - static const std::string stairsObj = stairsPath + objExtension; - static const std::string stairsPng = stairsPath + imageExtension; - - scene->addEntity("Stairs Block") - .addComponent(coords) - //.addComponent(1) - .addComponent(stairsObj, std::make_pair(MAP_DIFFUSE, stairsPng)); - } - bool MapGenerator::isCloseToBlockType(std::map, BlockType> map, int x, int y, int z, BlockType blockType) { @@ -273,10 +267,10 @@ namespace BBM map[std::make_tuple(i, 1, 0)] = map[std::make_tuple(i, 0, 0)]; map[std::make_tuple(i, 0, 0)] = UPPERFLOOR; } - map[std::make_tuple(0, 0, height - 1)] = STAIRS; - map[std::make_tuple(0, 0, 1)] = STAIRS; - map[std::make_tuple(width, 0, height - 1)] = STAIRS; - map[std::make_tuple(width, 0, 1)] = STAIRS; + map[std::make_tuple(0, -1, height - 1)] = BUMPER; + map[std::make_tuple(0, -1, 1)] = BUMPER; + map[std::make_tuple(width, -1, height - 1)] = BUMPER; + map[std::make_tuple(width, -1, 1)] = BUMPER; map[std::make_tuple(width / 2, -1, height - 1)] = BUMPER; map[std::make_tuple(width / 2, -1, 1)] = BUMPER; } @@ -289,10 +283,10 @@ namespace BBM map[std::make_tuple(i, 0, j)] = UPPERFLOOR; } } - map[std::make_tuple(width / 2 - width / 8, 0, height / 2 + height / 4 + 1)] = STAIRS; - map[std::make_tuple(width / 2 + width / 8, 0, height / 2 - height / 4 - 1)] = STAIRS; - map[std::make_tuple(width / 2 - width / 4 - 1, 0, height / 2 - height / 8)] = STAIRS; - map[std::make_tuple(width / 2 + width / 4 + 1, 0, height / 2 + height / 8)] = STAIRS; + map[std::make_tuple(width / 2 - width / 8, -1, height / 2 + height / 4 + 1)] = BUMPER; + map[std::make_tuple(width / 2 + width / 8, -1, height / 2 - height / 4 - 1)] = BUMPER; + map[std::make_tuple(width / 2 - width / 4 - 1, -1, height / 2 - height / 8)] = BUMPER; + map[std::make_tuple(width / 2 + width / 4 + 1, -1, height / 2 + height / 8)] = BUMPER; } return map; } @@ -311,8 +305,6 @@ namespace BBM { for (int i = 0; i < width + 1; i++) for (int j = 0; j < height; j++) { - if (map[std::make_tuple(i, 0, j)] == BREAKABLE && isCloseToBlockType(map, i, 0, j, STAIRS)) - map[std::make_tuple(i, 0, j)] = NOTHING; if (map[std::make_tuple(i, 0, j)] == BREAKABLE && map[std::make_tuple(i, -1, j)] == BUMPER) map[std::make_tuple(i, 0, j)] = NOTHING; } diff --git a/sources/Map/Map.hpp b/sources/Map/Map.hpp index 4a961cbc..f99fd550 100644 --- a/sources/Map/Map.hpp +++ b/sources/Map/Map.hpp @@ -37,7 +37,6 @@ namespace BBM UPPERFLOOR, FLOOR, BUMPER, - STAIRS, SPAWNER, UNBREAKABLE }; @@ -109,12 +108,6 @@ namespace BBM //! @brief Create upper floor of the map static void createUpperFloor(Vector3f coords, std::shared_ptr scene); - - //! @param coords coords of the element - //! @param scene Scene where the map is instanced - //! @brief Create stair of the map - static void createStairs(Vector3f coords, std::shared_ptr scene); - //! @param map Map to load with block declared inside //! @param width Width of the map //! @param height Height of the map diff --git a/sources/Models/Vector3.hpp b/sources/Models/Vector3.hpp index d4ba7337..a2266e07 100644 --- a/sources/Models/Vector3.hpp +++ b/sources/Models/Vector3.hpp @@ -168,6 +168,11 @@ namespace BBM return (point * this) / std::pow(this->magnitude(), 2) * this; } + Vector3 round() const requires(std::is_floating_point_v) + { + return Vector3(std::round(this->x), std::round(this->y), std::round(this->z)); + } + operator RAY::Vector3() const requires(std::is_same_v) { return RAY::Vector3(this->x, this->y, this->z); diff --git a/sources/Runner/Runner.cpp b/sources/Runner/Runner.cpp index 591ce261..c204005d 100644 --- a/sources/Runner/Runner.cpp +++ b/sources/Runner/Runner.cpp @@ -28,9 +28,14 @@ #include #include #include +#include #include "Component/Animation/AnimationsComponent.hpp" #include "System/Animation/AnimationsSystem.hpp" #include "Map/Map.hpp" +#include "Component/Music/MusicComponent.hpp" +#include "Component/Sound/SoundComponent.hpp" +#include "System/Sound/PlayerSoundManagerSystem.hpp" +#include "System/Music/MusicSystem.hpp" namespace RAY3D = RAY::Drawables::Drawables3D; @@ -56,7 +61,9 @@ namespace BBM .addSystem() .addSystem() .addSystem() - .addSystem(); + .addSystem() + .addSystem() + .addSystem(); } void enableRaylib(WAL::Wal &wal) @@ -71,16 +78,24 @@ namespace BBM std::shared_ptr loadGameScene() { auto scene = std::make_shared(); + std::map soundPath ={ + {SoundComponent::JUMP, "assets/sounds/jump.wav"}, + {SoundComponent::MOVE, "assets/sounds/move.ogg"}, + {SoundComponent::BOMB, "assets/sounds/bomb_drop.ogg"}, + {SoundComponent::DEATH, "assets/sounds/death.ogg"} + }; scene->addEntity("player") .addComponent() .addComponent("assets/player/player.iqm", std::make_pair(MAP_DIFFUSE, "assets/player/blue.png")) .addComponent() .addComponent() .addComponent() + .addComponent>() //.addComponent(0) .addComponent(RAY::ModelAnimations("assets/player/player.iqm"), 3) .addComponent(BBM::Vector3f{0.25, 0, 0.25}, BBM::Vector3f{.75, 2, .75}) .addComponent() + .addComponent(soundPath) .addComponent() .addComponent(1, [](WAL::Entity &entity) { auto &animation = entity.getComponent(); diff --git a/sources/System/BombHolder/BombHolderSystem.cpp b/sources/System/BombHolder/BombHolderSystem.cpp index b001950d..f36c982d 100644 --- a/sources/System/BombHolder/BombHolderSystem.cpp +++ b/sources/System/BombHolder/BombHolderSystem.cpp @@ -8,6 +8,10 @@ #include "Component/Renderer/Drawable3DComponent.hpp" #include "BombHolderSystem.hpp" #include "Component/Health/HealthComponent.hpp" +#include +#include +#include "Component/Collision/CollisionComponent.hpp" +#include "Component/Tag/TagComponent.hpp" using namespace std::chrono_literals; namespace RAY3D = RAY::Drawables::Drawables3D; @@ -15,34 +19,51 @@ namespace RAY3D = RAY::Drawables::Drawables3D; namespace BBM { std::chrono::nanoseconds BombHolderSystem::explosionTimer = 3s; - float BombHolderSystem::explosionRadius = 3; BombHolderSystem::BombHolderSystem(WAL::Wal &wal) : System(wal) {} + void BombHolderSystem::_dispatchExplosion(Vector3f position, WAL::Wal &wal, int count) + { + if (count <= 0) + return; + wal.scene->scheduleNewEntity("explosion") + .addComponent(position) + .addComponent(1s, [](WAL::Entity &explosion, WAL::Wal &wal) { + explosion.scheduleDeletion(); + }) + .addComponent("assets/bombs/explosion/explosion.glb", + std::make_pair(MAP_DIFFUSE, "assets/bombs/explosion/blast.png")); + wal.getSystem().dispatchEvent([position, count](WAL::Wal &wal) { + for (auto &[entity, pos, _] : wal.scene->view>()) { + if (pos.position.round() == position) { + if (auto *health = entity.tryGetComponent()) + health->takeDmg(1); + return; + } + } + _dispatchExplosion(position + Vector3f(1, 0, 0), wal, count - 1); + _dispatchExplosion(position + Vector3f(-1, 0, 0), wal, count - 1); + _dispatchExplosion(position + Vector3f(0, 0, 1), wal, count - 1); + _dispatchExplosion(position + Vector3f(0, 0, -1), wal, count - 1); + }); + } + void BombHolderSystem::_bombExplosion(WAL::Entity &bomb, WAL::Wal &wal) { bomb.scheduleDeletion(); - auto &bombPosition = bomb.getComponent(); - wal.getSystem().dispatchEvent([&bombPosition, &wal](WAL::Entity &entity){ - auto *health = entity.tryGetComponent(); - auto *pos = entity.tryGetComponent(); - - if (!health || !pos) - return; - if (pos->position.distance(bombPosition.position) > BombHolderSystem::explosionRadius) - return; - // TODO do a raycast here to only remove health to entities that are not behind others. - health->takeDmg(1); - }); + auto position = bomb.getComponent().position.round(); + _dispatchExplosion(position, wal, 3); } void BombHolderSystem::_spawnBomb(Vector3f position) { this->_wal.scene->scheduleNewEntity("Bomb") - .addComponent(position) + .addComponent(position.round()) .addComponent(BombHolderSystem::explosionTimer, &BombHolderSystem::_bombExplosion) +// .addComponent(WAL::Callback(), +// &MapGenerator::wallCollide, 0.25, .75) .addComponent("assets/bombs/bomb.obj", std::make_pair(MAP_DIFFUSE, "assets/bombs/bomb_normal.png")); } diff --git a/sources/System/BombHolder/BombHolderSystem.hpp b/sources/System/BombHolder/BombHolderSystem.hpp index fd3d4e78..105f9139 100644 --- a/sources/System/BombHolder/BombHolderSystem.hpp +++ b/sources/System/BombHolder/BombHolderSystem.hpp @@ -20,6 +20,9 @@ namespace BBM //! @brief Spawn a bomb at the specified position. void _spawnBomb(Vector3f position); + //! @brief Spawn a bomb at the specified position. + static void _dispatchExplosion(Vector3f position, WAL::Wal &, int count); + //! @brief The method triggered when the bomb explode. static void _bombExplosion(WAL::Entity &bomb, WAL::Wal &); public: diff --git a/sources/System/Event/EventSystem.cpp b/sources/System/Event/EventSystem.cpp index af20674f..da683dec 100644 --- a/sources/System/Event/EventSystem.cpp +++ b/sources/System/Event/EventSystem.cpp @@ -15,6 +15,11 @@ namespace BBM this->_events.emplace_back(event); } + void EventSystem::dispatchEvent(const std::function &event) + { + this->_globalEvents.emplace_back(event); + } + void EventSystem::onUpdate(WAL::ViewEntity<> &entity, std::chrono::nanoseconds) { for (auto &event : this->_events) @@ -23,6 +28,9 @@ namespace BBM void EventSystem::onSelfUpdate() { + for (auto &event : this->_globalEvents) + event(this->_wal); this->_events.clear(); + this->_globalEvents.clear(); } } \ No newline at end of file diff --git a/sources/System/Event/EventSystem.hpp b/sources/System/Event/EventSystem.hpp index 0a4c205f..bcdb14c0 100644 --- a/sources/System/Event/EventSystem.hpp +++ b/sources/System/Event/EventSystem.hpp @@ -14,11 +14,16 @@ namespace BBM { private: //! @brief The list of events that occurred in the last update. - std::vector> _events; + std::list> _events; + //! @brief The list of events that occurred in the last update. + std::list> _globalEvents; public: //! @brief Inform the system that a new event has occurred and it should run the given method on every entities. void dispatchEvent(const std::function& event); + //! @brief Inform the system that a new event has occurred and it should run the given method on every entities. + void dispatchEvent(const std::function& event); + //! @inherit void onUpdate(WAL::ViewEntity<> &entity, std::chrono::nanoseconds dtime) override; //! @inherit diff --git a/sources/System/Music/MusicSystem.cpp b/sources/System/Music/MusicSystem.cpp new file mode 100644 index 00000000..ea909d9a --- /dev/null +++ b/sources/System/Music/MusicSystem.cpp @@ -0,0 +1,24 @@ +// +// Created by Tom Augier on 05/06/2021 +// + +#include "MusicSystem.hpp" +#include + +namespace BBM { + + MusicSystem::MusicSystem(WAL::Wal &wal) + : System(wal) + {} + + void MusicSystem::onFixedUpdate(WAL::ViewEntity &entity) + { + auto &music = entity.get(); + + music.setVolume(music.volume); + if (!music.isPlaying()) { + music.playMusic(); + } + music.updateMusicStream(); + } +} \ No newline at end of file diff --git a/sources/System/Music/MusicSystem.hpp b/sources/System/Music/MusicSystem.hpp new file mode 100644 index 00000000..d524d300 --- /dev/null +++ b/sources/System/Music/MusicSystem.hpp @@ -0,0 +1,31 @@ +// +// Created by Tom Augier on 05/06/2021 +// + +#pragma once + +#include "System/System.hpp" +#include "Window.hpp" +#include "Component/Music/MusicComponent.hpp" +#include "Component/Health/HealthComponent.hpp" +#include +#include "Wal.hpp" + +namespace BBM +{ + class MusicSystem : public WAL::System + { + public: + //! @inherit + void onFixedUpdate(WAL::ViewEntity &entity) override; + + //! @brief ctor + MusicSystem(WAL::Wal &wal); + //! @brief Default copy ctor + MusicSystem(const MusicSystem &) = default; + //! @brief Default dtor + ~MusicSystem() override = default; + //! @brief A MusicManager screen system can't be assigned. + MusicSystem &operator=(const MusicSystem &) = delete; + }; +} diff --git a/sources/System/Sound/PlayerSoundManagerSystem.cpp b/sources/System/Sound/PlayerSoundManagerSystem.cpp new file mode 100644 index 00000000..dac45304 --- /dev/null +++ b/sources/System/Sound/PlayerSoundManagerSystem.cpp @@ -0,0 +1,34 @@ +// +// Created by Tom Augier on 05/06/2021 +// + +#include "PlayerSoundManagerSystem.hpp" +#include + +namespace BBM { + + PlayerSoundManagerSystem::PlayerSoundManagerSystem(WAL::Wal &wal) + : System(wal) + {} + + void PlayerSoundManagerSystem::onFixedUpdate(WAL::ViewEntity &entity) + { + const auto &controllable = entity.get(); + auto &sound = entity.get(); + auto &health = entity.get(); + + sound.setVolume(sound.volume); + std::map soundIndex = { + {health.getHealthPoint() <= 0, SoundComponent::DEATH}, + {controllable.bomb, SoundComponent::BOMB}, + {controllable.jump, SoundComponent::JUMP}, + {controllable.move.x != 0 || controllable.move.y != 0, SoundComponent::MOVE} + }; + for (auto &a : soundIndex) { + if (a.first) { + sound.setIndex(a.second); + sound.playSound(); + } + } + } +} \ No newline at end of file diff --git a/sources/System/Sound/PlayerSoundManagerSystem.hpp b/sources/System/Sound/PlayerSoundManagerSystem.hpp new file mode 100644 index 00000000..6e49763b --- /dev/null +++ b/sources/System/Sound/PlayerSoundManagerSystem.hpp @@ -0,0 +1,31 @@ +// +// Created by Tom Augier on 05/06/2021 +// + +#pragma once + +#include "System/System.hpp" +#include "Window.hpp" +#include "Component/Sound/SoundComponent.hpp" +#include "Component/Health/HealthComponent.hpp" +#include +#include "Wal.hpp" + +namespace BBM +{ + class PlayerSoundManagerSystem : public WAL::System + { + public: + //! @inherit + void onFixedUpdate(WAL::ViewEntity &entity) override; + + //! @brief ctor + PlayerSoundManagerSystem(WAL::Wal &wal); + //! @brief Default copy ctor + PlayerSoundManagerSystem(const PlayerSoundManagerSystem &) = default; + //! @brief Default dtor + ~PlayerSoundManagerSystem() override = default; + //! @brief A SoundManager screen system can't be assigned. + PlayerSoundManagerSystem &operator=(const PlayerSoundManagerSystem &) = delete; + }; +} diff --git a/tests/CacheTest.cpp b/tests/CacheTest.cpp new file mode 100644 index 00000000..7e30cd99 --- /dev/null +++ b/tests/CacheTest.cpp @@ -0,0 +1,57 @@ + +#include + +#define private public +#include +#include