diff --git a/.gitignore b/.gitignore index 4ea2a783..8da23996 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,7 @@ build/* docs/* emsdk/ build_web/* -wasm-python.py \ No newline at end of file +wasm-python.py +lua54* +include/* +save/* \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bd8bd46..989feb86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -122,10 +122,18 @@ set(SOURCES sources/System/Sound/PlayerSoundManagerSystem.hpp sources/System/Music/MusicSystem.hpp sources/System/Music/MusicSystem.cpp + sources/Parser/ParserYaml.hpp + sources/Parser/ParserYaml.cpp + sources/Exception/Error.hpp + sources/Exception/Error.cpp sources/System/Lobby/LobbySystem.cpp sources/System/Lobby/LobbySystem.hpp sources/Component/Lobby/LobbyComponent.cpp sources/Component/Lobby/LobbyComponent.hpp + sources/System/Lobby/ResumeLobbySystem.cpp + sources/System/Lobby/ResumeLobbySystem.hpp + sources/Component/Lobby/ResumeLobbyComponent.cpp + sources/Component/Lobby/ResumeLobbyComponent.hpp sources/Component/Gravity/GravityComponent.hpp sources/Component/Gravity/GravityComponent.cpp sources/System/Gravity/GravitySystem.hpp @@ -156,6 +164,12 @@ set(SOURCES sources/System/EndCondition/EndConditionSystem.hpp sources/System/EndCondition/EndConditionSystem.cpp sources/Runner/LobbyScene.cpp + sources/Runner/ResumeLobbyScene.cpp + sources/Runner/ScoreScene.cpp + sources/Parser/Node.cpp + sources/Parser/Node.hpp + sources/Utils/Utils.cpp + sources/Utils/Utils.hpp sources/Runner/HowToPlayScene.cpp sources/Runner/ScoreScene.cpp sources/System/Shaders/ShaderSystem.cpp @@ -180,6 +194,8 @@ set(SOURCES sources/Map/LuaMap.hpp sources/Component/Shaders/Items/AlphaCtxShaderComponent.cpp sources/Component/Shaders/Items/AlphaCtxShaderComponent.hpp + sources/Component/Speed/SpeedComponent.cpp + sources/Component/Speed/SpeedComponent.hpp ) add_executable(bomberman diff --git a/README.md b/README.md index c2c1003e..ab5eee3d 100644 --- a/README.md +++ b/README.md @@ -8,14 +8,17 @@ A recreation of the classic Bomberman arcade game Repository link: https://github.com/AnonymusRaccoon/Bomberman/ -## Demo - -Very soon :) +## Demo (Click to See on YouTube) +[![DEMO](http://img.youtube.com/vi/5tkaYtMpdKY/0.jpg)](http://www.youtube.com/watch?v=5tkaYtMpdKY "Indie Studio - Bomberman") ## Screenshots -Very soon :) +
+ + + +
## Run (compile from the sources) @@ -56,14 +59,7 @@ Enjoy ! ## Tech Stack -**Bomberman:** C++20, raylib, Catch2, CMake, Doxygen -## Contributing - -Contributions are always welcome! - -See `contributing.md` for ways to get started. - -Please adhere to this project's `code of conduct`. +**Bomberman:** C++20, raylib, Catch2, CMake, Doxygen, Lua ## Authors diff --git a/assets/ai_scripts/john.lua b/assets/ai_scripts/john.lua index f63076a5..b4b9116c 100644 --- a/assets/ai_scripts/john.lua +++ b/assets/ai_scripts/john.lua @@ -9,7 +9,7 @@ mapinfo.dist { } ------------ ------ Debug variables -local debug = true +local debug = false if not debug then log = function() end diff --git a/assets/buttons/button_resume_game.png b/assets/buttons/button_resume_game.png new file mode 100644 index 00000000..20cec8d2 Binary files /dev/null and b/assets/buttons/button_resume_game.png differ diff --git a/assets/buttons/button_resume_game_hovered.png b/assets/buttons/button_resume_game_hovered.png new file mode 100644 index 00000000..42719815 Binary files /dev/null and b/assets/buttons/button_resume_game_hovered.png differ diff --git a/assets/buttons/button_save.png b/assets/buttons/button_save.png new file mode 100644 index 00000000..0372a9b3 Binary files /dev/null and b/assets/buttons/button_save.png differ diff --git a/assets/buttons/button_save_hovered.png b/assets/buttons/button_save_hovered.png new file mode 100644 index 00000000..d66bc224 Binary files /dev/null and b/assets/buttons/button_save_hovered.png differ diff --git a/assets/images/game.png b/assets/images/game.png new file mode 100644 index 00000000..acbb2838 Binary files /dev/null and b/assets/images/game.png differ diff --git a/assets/images/lobby.png b/assets/images/lobby.png new file mode 100644 index 00000000..508bb087 Binary files /dev/null and b/assets/images/lobby.png differ diff --git a/assets/images/titlescreen.png b/assets/images/titlescreen.png new file mode 100644 index 00000000..68ec8902 Binary files /dev/null and b/assets/images/titlescreen.png differ diff --git a/images/game.png b/images/game.png new file mode 100644 index 00000000..acbb2838 Binary files /dev/null and b/images/game.png differ diff --git a/images/lobby.png b/images/lobby.png new file mode 100644 index 00000000..508bb087 Binary files /dev/null and b/images/lobby.png differ diff --git a/images/titlescreen.png b/images/titlescreen.png new file mode 100644 index 00000000..68ec8902 Binary files /dev/null and b/images/titlescreen.png differ diff --git a/lib/Ray/sources/Vector/Vector3.cpp b/lib/Ray/sources/Vector/Vector3.cpp index 8bf172c3..ca9ba556 100644 --- a/lib/Ray/sources/Vector/Vector3.cpp +++ b/lib/Ray/sources/Vector/Vector3.cpp @@ -31,4 +31,4 @@ RAY::Vector3::operator ::Vector3() const v.y = this->y; v.z = this->z; return v; -} \ No newline at end of file +} diff --git a/lib/wal/sources/Entity/Entity.cpp b/lib/wal/sources/Entity/Entity.cpp index c3f7196a..7bb19bf4 100644 --- a/lib/wal/sources/Entity/Entity.cpp +++ b/lib/wal/sources/Entity/Entity.cpp @@ -39,6 +39,11 @@ namespace WAL return this->_name; } + void Entity::setName(std::string &name) + { + this->_name = name; + } + bool Entity::isDisable() const { return this->_disabled; diff --git a/lib/wal/sources/Entity/Entity.hpp b/lib/wal/sources/Entity/Entity.hpp index 9d8b2d13..5bfc83f8 100644 --- a/lib/wal/sources/Entity/Entity.hpp +++ b/lib/wal/sources/Entity/Entity.hpp @@ -51,8 +51,10 @@ namespace WAL Scene &_scene; //! @brief Get the ID of the entity. unsigned getUid() const; - //! @brief Get the name fo the entity + //! @brief Get the name of the entity std::string getName() const; + //!@brief Set the name of the entity + void setName(std::string &name); //! @brief Used if the entity is disabled bool isDisable() const; diff --git a/sources/Component/BombHolder/BombHolderComponent.cpp b/sources/Component/BombHolder/BombHolderComponent.cpp index 13480cda..157f6946 100644 --- a/sources/Component/BombHolder/BombHolderComponent.cpp +++ b/sources/Component/BombHolder/BombHolderComponent.cpp @@ -12,9 +12,10 @@ namespace BBM : WAL::Component(entity) {} - BombHolderComponent::BombHolderComponent(WAL::Entity &entity, unsigned int maxCount) + BombHolderComponent::BombHolderComponent(WAL::Entity &entity, unsigned int maxCount, unsigned int bombExplosionRadius) : WAL::Component(entity), - maxBombCount(maxCount) + maxBombCount(maxCount), + explosionRadius(bombExplosionRadius) {} WAL::Component *BombHolderComponent::clone(WAL::Entity &entity) const diff --git a/sources/Component/BombHolder/BombHolderComponent.hpp b/sources/Component/BombHolder/BombHolderComponent.hpp index 43242338..e85e812f 100644 --- a/sources/Component/BombHolder/BombHolderComponent.hpp +++ b/sources/Component/BombHolder/BombHolderComponent.hpp @@ -26,7 +26,7 @@ namespace BBM //! @brief The number of nanosecond before the next bomb refill. std::chrono::nanoseconds nextBombRefill = refillRate; //! @brief The radius of the explosion. - float explosionRadius = 3; + unsigned int explosionRadius = 3; //! @brief The damage made by the explosion on an entity int damage = 1; @@ -37,7 +37,7 @@ namespace BBM explicit BombHolderComponent(WAL::Entity &entity); //! @brief Constructor - BombHolderComponent(WAL::Entity &entity, unsigned int maxBombCount); + BombHolderComponent(WAL::Entity &entity, unsigned int maxBombCount, unsigned int bombExplosionRadius = 3); //! @brief A component can't be instantiated, it should be derived. BombHolderComponent(const BombHolderComponent &) = default; diff --git a/sources/Component/Controllable/ControllableComponent.hpp b/sources/Component/Controllable/ControllableComponent.hpp index 66904b20..a545751f 100644 --- a/sources/Component/Controllable/ControllableComponent.hpp +++ b/sources/Component/Controllable/ControllableComponent.hpp @@ -40,8 +40,6 @@ namespace BBM bool bomb = false; //! @brief input value for pause bool pause = false; - //! @brief The speed applied to every controllable entities. - float speed = .15f; //! @brief The layout used for this controllable. Layout layout = NONE; //! @brief True if buttons should be triggered every frame where the key is down, false if the button should only be triggered once the key is released. diff --git a/sources/Component/Lobby/ResumeLobbyComponent.cpp b/sources/Component/Lobby/ResumeLobbyComponent.cpp new file mode 100644 index 00000000..6cc0440b --- /dev/null +++ b/sources/Component/Lobby/ResumeLobbyComponent.cpp @@ -0,0 +1,21 @@ +// +// Created by hbenjamin on 6/18/21. +// + +#include "ResumeLobbyComponent.hpp" + +namespace BBM +{ + ResumeLobbyComponent::ResumeLobbyComponent(WAL::Entity &entity, int playerNumber, WAL::Entity &button, WAL::Entity &tile, int pColor) + : WAL::Component(entity), + playerID(playerNumber), + playerColor(pColor), + readyButton(button), + coloredTile(tile) + {} + + WAL::Component *ResumeLobbyComponent::clone(WAL::Entity &entity) const + { + return new ResumeLobbyComponent(entity, this->playerID, this->readyButton, this->coloredTile, this->playerColor); + } +} \ No newline at end of file diff --git a/sources/Component/Lobby/ResumeLobbyComponent.hpp b/sources/Component/Lobby/ResumeLobbyComponent.hpp new file mode 100644 index 00000000..8656a472 --- /dev/null +++ b/sources/Component/Lobby/ResumeLobbyComponent.hpp @@ -0,0 +1,44 @@ +// +// Created by hbenjamin on 6/18/21. +// + +#pragma once + +#include +#include +#include +#include +#include + +namespace BBM +{ + class ResumeLobbyComponent : public WAL::Component + { + public: + //! @brief The layout used for this player. + ControllableComponent::Layout layout = ControllableComponent::NONE; + //! @brief The ID of the lobby player (from 0 to 3) + int playerID; + //! @brief The color of the player (as an index) + int playerColor; + //! @brief Is this player ready + bool ready = false; + //! @brief The entity containing the ready display. + WAL::Entity &readyButton; + //! @brief The colored rectangle behind the player. + WAL::Entity &coloredTile; + //! @brief The time of last input that this lobby player has made. + std::chrono::time_point lastInput; + + Component *clone(WAL::Entity &entity) const override; + + //! @brief Create a new lobby component. + explicit ResumeLobbyComponent(WAL::Entity &entity, int playerNumber, WAL::Entity &button, WAL::Entity &tile, int pColor); + //! @brief A lobby component is copyable. + ResumeLobbyComponent(const ResumeLobbyComponent &) = default; + //! @brief A default destructor + ~ResumeLobbyComponent() override = default; + //! @brief A lobby component is not assignable. + ResumeLobbyComponent &operator=(const ResumeLobbyComponent &) = delete; + }; +} diff --git a/sources/Component/Speed/SpeedComponent.cpp b/sources/Component/Speed/SpeedComponent.cpp new file mode 100644 index 00000000..2472d8c4 --- /dev/null +++ b/sources/Component/Speed/SpeedComponent.cpp @@ -0,0 +1,24 @@ +// +// Created by cbihan on 18/06/2021. +// + +#include "SpeedComponent.hpp" + +namespace BBM +{ + SpeedComponent::SpeedComponent(WAL::Entity &entity) : + WAL::Component(entity) + { + } + + WAL::Component *SpeedComponent::clone(WAL::Entity &entity) const + { + return new SpeedComponent(this->_entity, this->speed); + } + + SpeedComponent::SpeedComponent(WAL::Entity &entity, float entitySpeed) : + WAL::Component(entity), + speed(entitySpeed) + { + } +} \ No newline at end of file diff --git a/sources/Component/Speed/SpeedComponent.hpp b/sources/Component/Speed/SpeedComponent.hpp new file mode 100644 index 00000000..ced8ab3f --- /dev/null +++ b/sources/Component/Speed/SpeedComponent.hpp @@ -0,0 +1,34 @@ +// +// Created by cbihan on 18/06/2021. +// + +#pragma once + +#include + +namespace BBM +{ + class SpeedComponent : public WAL::Component + { + public: + //! @brief entity speed + float speed = .15f; + + //! @inherit + WAL::Component *clone(WAL::Entity &entity) const override; + + //! @brief Initialize a new controllable component. + explicit SpeedComponent(WAL::Entity &entity); + + //! @brief Initialize a new controllable component. + explicit SpeedComponent(WAL::Entity &entity, float entitySpeed); + //! @brief A Controllable component is copy constructable. + SpeedComponent(const SpeedComponent &) = default; + //! @brief default destructor + ~SpeedComponent() override = default; + //! @brief A Controllable component can't be assigned + SpeedComponent &operator=(const SpeedComponent &) = delete; + }; + + +} \ No newline at end of file diff --git a/sources/Component/Tag/TagComponent.hpp b/sources/Component/Tag/TagComponent.hpp index d84a9708..5bcd837f 100644 --- a/sources/Component/Tag/TagComponent.hpp +++ b/sources/Component/Tag/TagComponent.hpp @@ -61,4 +61,5 @@ namespace BBM constexpr const char Bumper[] = "Bumper"; // interact with bombs (getting damage etc) but doesn't stop explosion constexpr const char BlowablePass[] = "BlowablePass"; + constexpr const char Timer[] = "Timer"; } diff --git a/sources/Exception/Error.cpp b/sources/Exception/Error.cpp new file mode 100644 index 00000000..ed1d660c --- /dev/null +++ b/sources/Exception/Error.cpp @@ -0,0 +1,16 @@ +// +// Created by hbenjamin on 11/06/2021. +// + +#include "Error.hpp" + +namespace BBM +{ + Error::Error(const std::string &what) + : std::runtime_error(what) + {} + + ParserError::ParserError(const std::string &what) + : Error(what) + {} +} // namespace BBM \ No newline at end of file diff --git a/sources/Exception/Error.hpp b/sources/Exception/Error.hpp new file mode 100644 index 00000000..15f8a7d1 --- /dev/null +++ b/sources/Exception/Error.hpp @@ -0,0 +1,37 @@ +// +// Created by hbenjamin on 11/06/2021. +// + +#pragma once + +#include +#include +#include + +namespace BBM { + class Error : public std::runtime_error + { + public: + //! @brief Create a new exception + explicit Error(const std::string &what); + //! @brief An exception is copy constructable + Error(const Error &) = default; + //! @brief A default destructor + ~Error() override = default; + //! @brief A default assignment operator + Error &operator=(const Error &) = default; + }; + + class ParserError : public Error + { + public: + //! @brief Create a new parser exception + explicit ParserError(const std::string &what); + //! @brief A parser exception is copy constructable + ParserError(const ParserError &) = default; + //! @brief A default destructor + ~ParserError() override = default; + //! @brief A default assignment operator + ParserError &operator=(const ParserError &) = default; + }; +} diff --git a/sources/Items/Bonus.cpp b/sources/Items/Bonus.cpp index 66675585..60911acb 100644 --- a/sources/Items/Bonus.cpp +++ b/sources/Items/Bonus.cpp @@ -5,6 +5,7 @@ #include #include #include +#include "Component/Speed/SpeedComponent.hpp" #include #include "Component/Movable/MovableComponent.hpp" #include "Bonus.hpp" @@ -40,13 +41,13 @@ namespace BBM { { if (bonus.shouldDelete() || axis != CollisionComponent::CollidedAxis::ALL) return; - auto *controllable = player.tryGetComponent(); + auto *speed = player.tryGetComponent(); auto *playerBonus = player.tryGetComponent(); - if (!controllable || !playerBonus) + if (!speed || !playerBonus) return; - if (controllable->speed >= 0.4) + if (speed->speed >= 0.4) return; - controllable->speed += 0.025f; + speed->speed += 0.025f; const_cast(bonus).scheduleDeletion(); } diff --git a/sources/Items/Bonus.hpp b/sources/Items/Bonus.hpp index 2284b63f..02da9bf2 100644 --- a/sources/Items/Bonus.hpp +++ b/sources/Items/Bonus.hpp @@ -6,6 +6,7 @@ #pragma once #include "Entity/Entity.hpp" +#include "Component/Collision/CollisionComponent.hpp" namespace BBM { class Bonus { @@ -15,11 +16,6 @@ namespace BBM { //! @brief Apply bonus effect that allows players to carry one more bomb than before static void BombUpBonus(WAL::Entity &player, const WAL::Entity &bonus, CollisionComponent::CollidedAxis axis); - //! @param bonus bonus - //! @param player the entity on which the effect will be applied - //! @brief Apply bonus effect who increased the bomb damage - static void DamageIncreasedBonus(WAL::Entity &player, const WAL::Entity &bonus, CollisionComponent::CollidedAxis axis); - //! @param bonus bonus //! @param player the entity on which the effect will be applied //! @brief Apply bonus effect that expend the explosion range of the bomb diff --git a/sources/Map/Map.cpp b/sources/Map/Map.cpp index 9c464200..7197771c 100644 --- a/sources/Map/Map.cpp +++ b/sources/Map/Map.cpp @@ -19,6 +19,34 @@ using namespace std::chrono_literals; namespace BBM { + void MapGenerator::createBonus(WAL::Entity &entity, Vector3f position, Bonus::BonusType bonusType) { + static std::map> map = { + {Bonus::BonusType::BOMBSTOCK, {"Bonus Bomb Up", "assets/items/bombup"}}, + {Bonus::BonusType::SPEEDUP, {"Bonus Speed Up", "assets/items/speedup"}}, + {Bonus::BonusType::EXPLOSIONINC, {"Bonus Fire Up", "assets/items/fireup"}}, + {Bonus::BonusType::NOCLIP, {"Bonus Wallpass", "assets/items/wallpass"}} + }; + static std::vector> func = { + &Bonus::BombUpBonus, &Bonus::SpeedUpBonus, &Bonus::ExplosionRangeBonus, &Bonus::NoClipBonus + }; + + entity.addComponent(position) + .addComponent>() + .addComponent() + .addComponent(1, [](WAL::Entity &myEntity, WAL::Wal &wal) { + myEntity.scheduleDeletion(); + }) + .addComponent(position.y) + .addComponent([](WAL::Entity &bonus, const WAL::Entity &player, CollisionComponent::CollidedAxis axis) { + bonus.scheduleDeletion(); + }, func[bonusType - 1], 0.5, .5) + .addComponent(5s, [](WAL::Entity &bonus, WAL::Wal &wal){ + bonus.scheduleDeletion(); + }) + .addComponent(map.at(bonusType)[1] + ".obj", false, + std::make_pair(MAP_DIFFUSE, "assets/items/items.png")); + } + void MapGenerator::bumperCollide(WAL::Entity &entity, const WAL::Entity &wall, CollisionComponent::CollidedAxis collidedAxis) @@ -84,14 +112,11 @@ namespace BBM { entity.scheduleDeletion(); auto &position = entity.getComponent().position; - static std::map map = { - {Bonus::BonusType::BOMBSTOCK, "assets/items/bombup"}, - {Bonus::BonusType::SPEEDUP, "assets/items/speedup"}, - {Bonus::BonusType::EXPLOSIONINC, "assets/items/fireup"}, - {Bonus::BonusType::NOCLIP, "assets/items/wallpass"} - }; - static std::vector> func = { - &Bonus::BombUpBonus, &Bonus::SpeedUpBonus, &Bonus::ExplosionRangeBonus, &Bonus::NoClipBonus + static std::map> map = { + {Bonus::BonusType::BOMBSTOCK, {"Bonus Bomb Up", "assets/items/bombup"}}, + {Bonus::BonusType::SPEEDUP, {"Bonus Speed Up", "assets/items/speedup"}}, + {Bonus::BonusType::EXPLOSIONINC, {"Bonus Fire Up", "assets/items/fireup"}}, + {Bonus::BonusType::NOCLIP, {"Bonus Wallpass", "assets/items/wallpass"}} }; auto bonusType = Bonus::getRandomBonusType(); @@ -99,21 +124,7 @@ namespace BBM return; if (!map.contains(bonusType)) return; - wal.getScene()->scheduleNewEntity("Bonus") - .addComponent(position) - .addComponent>() - .addComponent() - .addComponent(1, [](WAL::Entity &myEntity, WAL::Wal &) { - myEntity.scheduleDeletion(); - }) - .addComponent(position.y) - .addComponent(WAL::Callback(), - func[bonusType - 1], 0.5, .5) - .addComponent(5s, [](WAL::Entity &bonus, WAL::Wal &){ - bonus.scheduleDeletion(); - }) - .addComponent(map.at(bonusType) + ".obj", false, - std::make_pair(MAP_DIFFUSE, "assets/items/items.png")); + createBonus(wal.getScene()->scheduleNewEntity(map.at(bonusType)[0]), position, bonusType); } const std::string MapGenerator::assetsPath = "./assets/"; @@ -227,7 +238,6 @@ namespace BBM {BREAKABLE, &createBreakable}, {UNBREAKABLE, &createUnbreakable}, {HOLE, &createHole}, - {FLOOR, &createFloor}, {BUMPER, &createBumper}, {UPPERFLOOR, &createUpperFloor}, }; @@ -254,16 +264,6 @@ namespace BBM .addComponent(breakableObj, false, std::make_pair(MAP_DIFFUSE, breakablePng)); } - void MapGenerator::createFloor(Vector3f coords, std::shared_ptr scene) - { - static const std::string floorObj = floorPath + objExtension; - static const std::string floorPng = floorPath + imageExtension; - - scene->addEntity("Floor") - .addComponent(Vector3f(coords)) - .addComponent(floorObj, false, std::make_pair(MAP_DIFFUSE, floorPng)); - } - void MapGenerator::createUpperFloor(Vector3f coords, std::shared_ptr scene) { static const std::string floorObj = secondFloorPath + objExtension; @@ -508,7 +508,7 @@ namespace BBM .addComponent(Vector3f(width / 2 - width / 4, 0, height / 2 - height / 4)) .addComponent( WAL::Callback(), - &MapGenerator::wallCollided, Vector3f(0.25, 0.25, 0.25),Vector3f(width / 2 + width / 4, 0.75, height / 2 + height / 4)); + &MapGenerator::wallCollided, Vector3f(0.25, 0.25, 0.25),Vector3f(width / 2 + 0.75, 0.75, height / 2 + 0.75)); } void MapGenerator::loadMap(int width, int height, MapBlock map, const std::shared_ptr &scene) diff --git a/sources/Map/Map.hpp b/sources/Map/Map.hpp index 0606cb54..3e8b60e4 100644 --- a/sources/Map/Map.hpp +++ b/sources/Map/Map.hpp @@ -22,7 +22,7 @@ #include "Component/Collision/CollisionComponent.hpp" #include "Component/Movable/MovableComponent.hpp" #include - +#include namespace BBM @@ -38,16 +38,54 @@ namespace BBM BREAKABLE, HOLE, UPPERFLOOR, - FLOOR, BUMPER, SPAWNER, - UNBREAKABLE + UNBREAKABLE, + INVISIBLE }; - private: - using MapElem = std::function scene)>; using MapBlock = std::map, BlockType>; + static void createBonus(WAL::Entity &entity, Vector3f position, Bonus::BonusType bonusType); + + static void wallCollision(WAL::Entity &entity, + const WAL::Entity &wall, + CollisionComponent::CollidedAxis collidedAxis); + static void wallCollided(WAL::Entity &entity, + const WAL::Entity &wall, + CollisionComponent::CollidedAxis collidedAxis); + static void wallDestroyed(WAL::Entity &entity, WAL::Wal &wal); + + static void holeCollide(WAL::Entity &entity, + const WAL::Entity &wall, + CollisionComponent::CollidedAxis collidedAxis); + + static void bumperCollide(WAL::Entity &entity, + const WAL::Entity &wall, + CollisionComponent::CollidedAxis collidedAxis); + + + + //! @param width Width of the map + //! @param height Height of the map + //! @brief Generate map of block to be loaded + static MapBlock createMap(int width, int height, bool isHeight = false, bool isNotClassic = false); + + //! @param width Width of the map + //! @param height Height of the map + //! @param map Map to load with block declared inside + //! @param scene Scene where the map is instanced + //! @brief Generate the map + static void loadMap(int width, int height, MapBlock map, const std::shared_ptr &scene); + + //! @param coords coords of the element + //! @param scene Scene where the map is instanced + //! @brief Create element of the map + static void createElement(Vector3f coords, std::shared_ptr scene, BlockType blockType); + private: + + using MapElem = std::function scene)>; + //! @brief Generate random block type static BlockType getRandomBlockType(bool = false); @@ -77,11 +115,6 @@ namespace BBM //! @brief Generate the floor of the map static void generateFloor(MapBlock map, int width, int height, std::shared_ptr scene); - //! @param coords coords of the element - //! @param scene Scene where the map is instanced - //! @brief Create element of the map - static void createElement(Vector3f coords, std::shared_ptr scene, BlockType blockType); - //! @param coords coords of the element //! @param scene Scene where the map is instanced //! @brief Create breakable of the map @@ -102,11 +135,6 @@ namespace BBM //! @brief Create bumper of the map static void createBumper(Vector3f coords, std::shared_ptr scene); - //! @param coords coords of the element - //! @param scene Scene where the map is instanced - //! @brief Create floor of the map - static void createFloor(Vector3f coords, std::shared_ptr scene); - //! @param coords coords of the element //! @param scene Scene where the map is instanced //! @brief Create upper floor of the map @@ -175,37 +203,5 @@ namespace BBM static const std::string holePath; static const std::string secondFloorHolePath; - - public: - - static void wallCollision(WAL::Entity &entity, - const WAL::Entity &wall, - CollisionComponent::CollidedAxis collidedAxis); - static void wallCollided(WAL::Entity &entity, - const WAL::Entity &wall, - CollisionComponent::CollidedAxis collidedAxis); - static void wallDestroyed(WAL::Entity &entity, WAL::Wal &wal); - - static void holeCollide(WAL::Entity &entity, - const WAL::Entity &wall, - CollisionComponent::CollidedAxis collidedAxis); - - static void bumperCollide(WAL::Entity &entity, - const WAL::Entity &wall, - CollisionComponent::CollidedAxis collidedAxis); - - - - //! @param width Width of the map - //! @param height Height of the map - //! @brief Generate map of block to be loaded - static MapBlock createMap(int width, int height, bool isHeight = false, bool isNotClassic = false); - - //! @param width Width of the map - //! @param height Height of the map - //! @param map Map to load with block declared inside - //! @param scene Scene where the map is instanced - //! @brief Generate the map - static void loadMap(int width, int height, MapBlock map, const std::shared_ptr &scene); }; } // namespace BBM \ No newline at end of file diff --git a/sources/Models/GameState.hpp b/sources/Models/GameState.hpp index a87f3c95..3132247a 100644 --- a/sources/Models/GameState.hpp +++ b/sources/Models/GameState.hpp @@ -24,6 +24,7 @@ namespace BBM SettingsScene, PauseMenuScene, LobbyScene, + ResumeLobbyScene, TitleScreenScene, CreditScene, HowToPlayScene, diff --git a/sources/Parser/Node.cpp b/sources/Parser/Node.cpp new file mode 100644 index 00000000..f4d23eab --- /dev/null +++ b/sources/Parser/Node.cpp @@ -0,0 +1,61 @@ +// +// Created by cbihan on 16/06/2021. +// + +#include "Node.hpp" + +namespace BBM +{ + + Node::Node(std::string name) : + _name(std::move(name)) + { + } + + void Node::setProperty(const std::string &propertyName, const std::string &propertyValue) + { + this->_properties[propertyName] = propertyValue; + } + + std::string Node::getProperty(const std::string &propertyName) const + { + return this->_properties.at(propertyName); + } + + std::string Node::getName() const + { + return this->_name; + } + + void Node::addChildNode(const Node &childNode) + { + this->_childNodes.emplace_back(childNode); + } + + std::vector Node::getChildNodes(const std::string &childNodeName) + { + std::vector childs; + + for (const auto &child : this->_childNodes) { + if (child.getName() == childNodeName) { + childs.emplace_back(child); + } + } + return childs; + } + + std::vector Node::getChildNodes(void) + { + return this->_childNodes; + } + + void Node::setName(const std::string &name) + { + this->_name = name; + } + + void Node::setProperty(const std::pair &propertyNameValue) + { + this->setProperty(propertyNameValue.first, propertyNameValue.second); + } +} \ No newline at end of file diff --git a/sources/Parser/Node.hpp b/sources/Parser/Node.hpp new file mode 100644 index 00000000..13056c12 --- /dev/null +++ b/sources/Parser/Node.hpp @@ -0,0 +1,51 @@ +// +// Created by cbihan on 16/06/2021. +// + +#pragma once + +#include +#include +#include + +namespace BBM +{ + class Node + { + private: + //! @brief Node name + std::string _name; + + //! @brief child nodes + std::vector _childNodes; + + //! @brief node's properties + std::map _properties; + + public: + + std::string getName() const; + + void setName(const std::string &name); + + void addChildNode(const Node &childNode); + + std::vector getChildNodes(const std::string &childNodeName); + std::vector getChildNodes(void); + + void setProperty(const std::string &propertyName, const std::string &propertyValue); + void setProperty(const std::pair &propertyNameValue); + + std::string getProperty(const std::string &propertyName) const; + + //! @brief ctor + explicit Node(std::string name); + //! @brief copy ctor + Node(const Node &) = default; + //! @brief dtor + ~Node() = default; + //! @brief assignment operator + Node &operator=(const Node &) = default; + }; +} + diff --git a/sources/Parser/ParserYaml.cpp b/sources/Parser/ParserYaml.cpp new file mode 100644 index 00000000..b3a6ce82 --- /dev/null +++ b/sources/Parser/ParserYaml.cpp @@ -0,0 +1,453 @@ +// +// Created by hbenjamin on 10/06/2021. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ParserYaml.hpp" +#include "Component/Speed/SpeedComponent.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Utils/Utils.hpp" + +namespace RAY3D = RAY::Drawables::Drawables3D; +namespace RAY2D = RAY::Drawables::Drawables2D; + +namespace BBM { + + const std::string ParserYAML::fileName = "save"; + std::stringstream ParserYAML::_block(""); + std::stringstream ParserYAML::_bonus(""); + std::stringstream ParserYAML::_player(""); + std::vector ParserYAML::playersInfos = {}; + + std::string ParserYAML::_getBlockType(const std::string& blockName) + { + static std::map map { + {"Upper Floor", MapGenerator::BlockType::UPPERFLOOR}, + {"Bumper Block", MapGenerator::BlockType::BUMPER}, + {"Breakable Block", MapGenerator::BlockType::BREAKABLE}, + {"Unbreakable Block", MapGenerator::BlockType::UNBREAKABLE}, + {"Hole Block", MapGenerator::BlockType::HOLE} + }; + + return (std::to_string(map.at(blockName))); + } + + std::string ParserYAML::_getBonusType(const std::string& bonusName) + { + static std::map map { + {"Bonus Bomb Up", Bonus::BonusType::BOMBSTOCK}, + {"Bonus Speed Up", Bonus::BonusType::SPEEDUP}, + {"Bonus Fire Up", Bonus::BonusType::EXPLOSIONINC} + }; + + return (std::to_string(map.at(bonusName))); + } + + void ParserYAML::_saveBonus(const WAL::Entity &entity) + { + auto *position = entity.tryGetComponent(); + auto name = entity.getName(); + + if (!position) + return; + std::replace(name.begin(), name.end(), ' ', '_'); + _bonus << std::endl << " " << name << ":" << std::endl << " "; + _bonus << std::string("bonus_type: ") << _getBonusType(entity.getName()) << std::endl << " "; + _bonus << "position: [" << std::to_string(position->getX()) << "," << std::to_string(position->getY()) << "," << std::to_string(position->getZ()) << "]"; + } + + void ParserYAML::_savePlayer(const WAL::Entity &entity) + { + auto *position = entity.tryGetComponent(); + auto *bombHolder = entity.tryGetComponent(); + auto *model = entity.tryGetComponent(); + auto *speed = entity.tryGetComponent(); + auto name = entity.getName(); + + if (!position || !bombHolder || !model || !speed) + return; + std::replace(name.begin(), name.end(), ' ', '_'); + _player << std::endl << " " << name << ":" << std::endl << " "; + _player << "texture_path: " << dynamic_cast(model->drawable.get())->getTextureByMaterial(MAP_DIFFUSE).getResourcePath() << std::endl << " "; + _player << "max_bomb: " << std::to_string(bombHolder->maxBombCount) << std::endl << " "; + _player << "explosion_radius: " << std::to_string(bombHolder->explosionRadius) << std::endl << " "; + _player << "speed: " << std::to_string(speed->speed) << std::endl << " "; + _player << "position: [" << std::to_string(position->getX()) << "," << std::to_string(position->getY()) << "," << std::to_string(position->getZ()) << "]"; + } + + void ParserYAML::_saveBlock(const WAL::Entity &entity) + { + auto *position = entity.tryGetComponent(); + auto name = entity.getName(); + + if (!position) + return; + std::replace(name.begin(), name.end(), ' ', '_'); + _block << std::endl << " " << name << ":" << std::endl << " "; + _block << std::string("block_type: ") << _getBlockType(entity.getName()) << std::endl << " "; + _block << "position: [" << std::to_string(position->getX()) << "," << std::to_string(position->getY()) << "," << std::to_string(position->getZ()) << "]"; + } + + void ParserYAML::save(std::shared_ptr scene) + { + std::string block = std::string("save/" + fileName + "_block.yml"); + std::string player = std::string("save/" + fileName + "_player.yml"); + std::string bonus = std::string("save/" + fileName + "_bonus.yml"); + std::map> savingGame = { + {"Bonus", &_saveBonus}, + {"Block", &_saveBlock}, + {"Upper Floor", &_saveBlock}, + {"Player", &_savePlayer} + }; + std::ofstream blockFile(block); + std::ofstream playerFile(player); + std::ofstream bonusFile(bonus); + auto &ret = scene->view, TimerComponent>(); + _block << "timer: " << ret.front().get().ringIn.count(); + + _player << "players:"; + _bonus << "bonuses:"; + _block << std::endl << "width: " << std::to_string(Runner::mapWidth); + _block << std::endl << "height: " + std::to_string(Runner::mapHeight); + _block << std::endl << "blocks:"; + for (const auto &entity : scene->getEntities()) { + for (const auto& type : savingGame) { + if (entity.getName().find(type.first) != std::string::npos) { + type.second(entity); + } + } + } + blockFile << _block.str() << std::endl; + playerFile << _player.str() << std::endl; + bonusFile << _bonus.str() << std::endl; + _block = std::stringstream(); + _player = std::stringstream(); + _bonus = std::stringstream(); + } + + void ParserYAML::_loadPlayer(std::shared_ptr scene, Node &node, int countPlayer) + { + std::string tmpAssets = node.getProperty("texture_path"); + std::map map = { + {"red", RED}, + {"blue", BLUE}, + {"yellow", YELLOW}, + {"green", GREEN} + }; + std::map colors = { + {"red", 1}, + {"blue", 0}, + {"yellow", 3}, + {"green", 2} + }; + + + playersInfos.emplace_back(PlayerInfos{ + node.getName(), + _parsePosition(node.getProperty("position")), + _parseMaxBomb(node.getProperty("max_bomb")), + _parseExplosionRadius(node.getProperty("explosion_radius")), + _parseSpeed(node.getProperty("speed")), + node.getProperty("texture_path") + }); + + if ((tmpAssets.find("red.png") == std::string::npos && tmpAssets.find("blue.png") == std::string::npos && + tmpAssets.find("green.png") == std::string::npos && tmpAssets.find("yellow.png") == std::string::npos && + tmpAssets.find("ai.png") == std::string::npos) || !std::filesystem::exists(tmpAssets)) { + throw (ParserError("One asset is invalid.")); + } + auto start = tmpAssets.find_last_of('/') + 1; + auto colorStr = tmpAssets.substr(start, tmpAssets.length() - start - 4); + auto color = map.at(colorStr); + auto resumeScene = Runner::gameState.loadedScenes[GameState::SceneID::ResumeLobbyScene]; + auto &playerTile = resumeScene->addEntity("player tile") + .addComponent(224 * (countPlayer + 1) + 200 * countPlayer, 1080 / 3, 0) + .addComponent(RAY::Vector2(224 * (countPlayer + 1) + 200 * countPlayer, 1080 / 3), RAY::Vector2(200, 200), color); + auto &playerLogo = resumeScene->addEntity("player") + .addComponent(224 * (countPlayer + 1) + 200 * countPlayer, 1080 / 3, 0) + .addComponent(tmpAssets.replace(tmpAssets.find("textures"), 8, "icons")); + auto &ready = resumeScene->addEntity("ready") + .addComponent(224 * (countPlayer + 1) + 200 * countPlayer, 1080 / 3, 0) + .addComponent(); + playerLogo.addComponent(countPlayer, ready, playerTile, colors.at(colorStr)); + } + + void ParserYAML::_loadPlayers(std::shared_ptr scene, Node &node) + { + int countPlayer = 0; + auto childNode = node.getChildNodes("players").at(0).getChildNodes(); + + if (childNode.size() < 2) + throw (ParserError("There isn't enough players to load this saved map.")); + for (auto &child : childNode) { + _loadPlayer(scene, child, countPlayer); + countPlayer++; + } + } + + void ParserYAML::_loadBlock(std::shared_ptr scene, Node child, MapGenerator::MapBlock &map) + { + Vector3f pos = _parsePosition(child.getProperty("position")); + MapGenerator::BlockType blockType = _parseBlockType(child.getProperty("block_type")); + + if (blockType == MapGenerator::NOTHING) + throw (ParserError("Invalid block_type field.")); + if (blockType == MapGenerator::HOLE) + pos.y += 1.0f; + map[std::make_tuple(pos.x, pos.y, pos.z)] = blockType; + } + + void ParserYAML::_loadBlocks(std::shared_ptr scene, Node &node) + { + MapGenerator::MapBlock map; + int size = -1; + + if (!Utils::tryParseInteger(node.getProperty("width"), size)) { + throw ParserError("width property must be an int"); + } + Runner::mapWidth = size; + if (!Utils::tryParseInteger(node.getProperty("height"), size)) { + throw ParserError("width property must be an int"); + } + Runner::mapHeight = size; + long timer = 0; + if (!Utils::tryParseLong(node.getProperty("timer"), timer)) { + throw ParserError("timer property must be a long"); + } + Runner::timerDelay = std::chrono::nanoseconds(timer); + + for (int i = 0; i < Runner::mapWidth; i++) + for (int j = 0; j < Runner::mapHeight; j++) + map[std::make_tuple(i, 0, j)] = MapGenerator::NOTHING; + auto childNode = node.getChildNodes("blocks").at(0).getChildNodes(); + for (const auto& child : childNode) + _loadBlock(scene, child, map); + MapGenerator::loadMap(Runner::mapWidth, Runner::mapHeight, map, scene); + } + + void ParserYAML::_loadBonus(std::shared_ptr scene, Node &node) + { + auto &entity = scene->addEntity(node.getName()); + Vector3f pos = _parsePosition(node.getProperty("position")); + Bonus::BonusType bonusType = _parseBonusType(node.getProperty("bonus_type")); + + if (bonusType == Bonus::NOTHING) { + entity.scheduleDeletion(); + return; + } + MapGenerator::createBonus(entity, pos, bonusType); + } + + void ParserYAML::_loadBonuses(std::shared_ptr scene, Node &node) + { + auto childNode = node.getChildNodes("bonuses").at(0).getChildNodes(); + for (auto child : childNode) + _loadBonus(scene, child); + } + + void ParserYAML::load(std::shared_ptr gameScene) + { + Node blocksInfos = parseFile("save/save_block.yml"); + Node bonusesInfos = parseFile("save/save_bonus.yml"); + Node playerInfos = parseFile("save/save_player.yml"); + _loadBlocks(gameScene, blocksInfos); + _loadBonuses(gameScene, bonusesInfos); + _loadPlayers(gameScene, playerInfos); + } + + Vector3f ParserYAML::_parsePosition(const std::string& line) + { + float x; + float y; + float z; + std::string subStr; + + try { + auto start = line.find('[') + 1; + auto end = line.find(']'); + if (line.front() != '[' || end == std::string::npos || line.back() != ']') { + throw ParserError("Error parsing position."); + } + subStr = line.substr(start, end - start); + auto pos = Utils::splitStr(subStr, ','); + if (pos.size() != 3) + throw (ParserError("Error parsing position.")); + if (!Utils::tryParseFloat(pos[0], x) || !Utils::tryParseFloat(pos[1], y) || !Utils::tryParseFloat(pos[2], z)) + throw (ParserError("Error parsing position.")); + } catch (const std::out_of_range &err) { + throw (ParserError("Error parsing position.")); + } + return Vector3f(x, y, z); + } + + int ParserYAML::_parseMaxBomb(const std::string &str) + { + int maxBomb = 0; + + if (str.find('-') != std::string::npos) + throw (ParserError("Couldn't parse max bomb.")); + if (!Utils::tryParseInteger(str, maxBomb)) + throw (ParserError("Couldn't parse max bomb.")); + return (maxBomb); + } + + int ParserYAML::_parseExplosionRadius(const std::string& line) + { + int explosionRadius = 0; + + if (line.find('-') != std::string::npos) + throw (ParserError("Couldn't parse explosion radius.")); + if (!Utils::tryParseInteger(line, explosionRadius)) + throw (ParserError("Couldn't parse explosion radius.")); + return (explosionRadius); + } + + float ParserYAML::_parseSpeed(const std::string& line) + { + float speed = 0; + + if (line.find('-') != std::string::npos) + throw (ParserError("Couldn't parse speed.")); + if (!Utils::tryParseFloat(line, speed)) + throw (ParserError("Couldn't parse speed.")); + return (speed); + } + + MapGenerator::BlockType ParserYAML::_parseBlockType(const std::string& blockType) + { + if (blockType.find('-') != std::string::npos) + throw (ParserError("Couldn't parse block type.")); + int block = 0; + if (!Utils::tryParseInteger(blockType, block)) + throw (ParserError("Couldn't parse block type.")); + return (static_cast(block)); + } + + Bonus::BonusType ParserYAML::_parseBonusType(const std::string& bonusType) + { + if (bonusType.find('-') != std::string::npos) + throw (ParserError("Couldn't parse bonus type.")); + int bonus = 0; + if (!Utils::tryParseInteger(bonusType, bonus)) + throw (ParserError("Couldn't parse bonus type.")); + return (static_cast(bonus)); + } + + std::string ParserYAML::parseHeader(const std::string &line) + { + std::stringstream ss(line); + std::string headerName; + std::string garbage; + + ss >> headerName >> garbage; + + + if (!garbage.empty()) { + throw ParserError("Ill formed header,\nline: " + Utils::trimCopy(line)); + } + if (headerName.back() != ':') { + throw ParserError("Header not ended with ':',\nline: " + Utils::trimCopy(line)); + } + headerName.pop_back(); + return headerName; + } + + std::pair ParserYAML::parseProperty(const std::string &line) + { + std::stringstream ss(line); + std::string propertyName; + std::string propertyValue; + std::string garbage; + + ss >> propertyName >> propertyValue >> garbage; + + if (!garbage.empty()) { + throw ParserError("ill formed property, \nline: " + Utils::trimCopy(line)); + } + if (propertyName.back() != ':') { + throw ParserError("property name not ended with ':', \nline: " + Utils::trimCopy(line)); + } + propertyName.pop_back(); + return std::make_pair(propertyName, propertyValue); + } + + bool ParserYAML::isHeader(const std::string &line) + { + return line.back() == ':'; + } + + Node ParserYAML::parseFile(const std::string &path) + { + std::ifstream file(path); + + if (!file.good()) + throw ParserError("Can't read file"); + return parseNode(file, "root"); + } + + Node ParserYAML::parseNode(std::ifstream &file, const std::string &nodeName, int indentLevel) + { + std::string line; + Node node(nodeName); + + while(std::getline(file, line)) { + if (line.empty()) + continue; + float lineIndentLevel = getIndent(line); + if (lineIndentLevel != static_cast(lineIndentLevel)) { + throw ParserError("Yaml only support 2 spaces as indent"); + } + if (lineIndentLevel > static_cast(indentLevel)) { + throw ParserError("Indent issue"); + } + if (lineIndentLevel < static_cast(indentLevel)) { + file.seekg(static_cast(file.tellg()) - (line.length() + endlNbChars)); + return node; + } + if (isHeader(line)) { + node.addChildNode(parseNode(file, parseHeader(line), indentLevel + 1)); + } else { + node.setProperty(parseProperty(line)); + } + } + return node; + } + + float ParserYAML::getIndent(const std::string &line) + { + int nb = 0; + for (const auto &c : line) { + if (!std::isspace(c)) + break; + if (c != ' ') + throw ParserError("Yaml only support 2 spaces as indent"); + nb++; + } + return static_cast(nb / 2.); + } +} \ No newline at end of file diff --git a/sources/Parser/ParserYaml.hpp b/sources/Parser/ParserYaml.hpp new file mode 100644 index 00000000..ff8a73f6 --- /dev/null +++ b/sources/Parser/ParserYaml.hpp @@ -0,0 +1,135 @@ +// +// Created by hbenjamin on 10/06/2021. +// + +#pragma once + +#include +#include "Items/Bonus.hpp" +#include "Node.hpp" +#include "Map/Map.hpp" +#include "Component/Controllable/ControllableComponent.hpp" + +namespace BBM { + class ParserYAML { + private: + + //! @brief The number of chars for endl + #ifdef __linux__ + static constexpr int endlNbChars = 1; + #elif _WIN32 + static constexpr int endlNbChars = 2; + #endif + + //!@brief file block of the parser + static std::stringstream _block; + //!@brief file bonus of the parser + static std::stringstream _bonus; + //!@brief file player of the parser + static std::stringstream _player; + + //!@param entity entity to save + //!@brief save block in _block + static void _saveBlock(const WAL::Entity &entity); + //!@param entity entity to save + //!@brief save bonus in _bonus + static void _saveBonus(const WAL::Entity &entity); + //!@param entity entity to save + //!@brief save player in _player + static void _savePlayer(const WAL::Entity &entity); + //!@param blockName block name + //!@brief transform block name + static std::string _getBlockType(const std::string& blockName); + //!@param blockName bonus name + //!@brief transform bonus name + static std::string _getBonusType(const std::string& bonusName); + + //!@param str to parse + //!@brief return max bomb parsed + static int _parseMaxBomb(const std::string& str); + //!@param line to parse + //!@brief return explosion radius parsed + static int _parseExplosionRadius(const std::string& line); + //!@param line to parse + //!@brief return speed parsed + static float _parseSpeed(const std::string& line); + //!@param line to parse + //!@brief return vector3f of position parsed + static Vector3f _parsePosition(const std::string& line); + //!@param blockType to parse + //!@brief return BlockType of type parsed + static MapGenerator::BlockType _parseBlockType(const std::string& blockType); + //!@param bonusType to parse + //!@brief return bonusType of type parsed + static Bonus::BonusType _parseBonusType(const std::string& bonusType); + + //!@param scene Scene to update + //!@param lines Lines of the file + //!@param index index of the vector + //!@brief add player into scene + static void _loadPlayer(std::shared_ptr scene, Node &node, int countPlayer); + //!@param scene Scene to update + //!@param lines Lines of the file + //!@param index index of the vector + //!@param map map of all the block + //!@brief add block into scene + static void _loadBlock(std::shared_ptr scene, Node child, MapGenerator::MapBlock &map); + //!@param scene Scene to update + //!@param lines Lines of the file + //!@param index index of the vector + //!@brief add bonus into scene + static void _loadBonus(std::shared_ptr scene, Node &node); + + //!@param scene Scene to update + //!@brief load all players into scene + static void _loadPlayers(std::shared_ptr scene, Node &node); + //!@param scene Scene to update + //!@brief load all blocks into scene + static void _loadBlocks(std::shared_ptr scene, Node &node); + //!@param scene Scene to update + //!@brief load all blocks into scene + static void _loadBonuses(std::shared_ptr scene, Node &node); + + static std::string parseHeader(const std::string &line); + + static std::pair parseProperty(const std::string &line); + + static bool isHeader(const std::string &line); + + static Node parseNode(std::ifstream &file, const std::string &nodeName, int indentLevel = 0); + + static float getIndent(const std::string &line); + + static constexpr const char* indent = " "; + + public: + + static Node parseFile(const std::string &path); + + struct PlayerInfos { + std::string name; + //! @brief Player position + Vector3f position; + //! @brief The amount of bomb a player had + int maxBombCount; + //! @brief The explosion range of a player + int explosionRange; + //! @brief The speed of a player + float speed; + //! @brief The assets of the player + std::string asset; + }; + + static std::vector playersInfos; + + //!@param scene Scene to update + //!@brief save yaml + static void save(std::shared_ptr scene); + //!@param scene Scene to update + //!@brief load yaml + static void load(std::shared_ptr scene); + + //! @brief save file name + static const std::string fileName; + }; +} \ No newline at end of file diff --git a/sources/Runner/GameScene.cpp b/sources/Runner/GameScene.cpp index 15b7be2c..d8a72f98 100644 --- a/sources/Runner/GameScene.cpp +++ b/sources/Runner/GameScene.cpp @@ -3,11 +3,14 @@ #include "Runner.hpp" #include #include "Component/Tag/TagComponent.hpp" +#include #include +#include #include "Component/Music/MusicComponent.hpp" #include "Component/Sound/SoundComponent.hpp" #include "Component/Controllable/ControllableComponent.hpp" #include "Component/Position/PositionComponent.hpp" +#include "Component/Timer/TimerComponent.hpp" #include "Component/Animator/AnimatorComponent.hpp" #include "Component/Animation/AnimationsComponent.hpp" #include "Component/Health/HealthComponent.hpp" @@ -18,6 +21,7 @@ #include "Component/Tag/TagComponent.hpp" #include "Component/Renderer/Drawable3DComponent.hpp" #include "Component/Shaders/Items/AlphaCtxShaderComponent.hpp" +#include "Component/Speed/SpeedComponent.hpp" #include #include "Component/Shaders/ShaderComponent.hpp" #include "Component/Gravity/GravityComponent.hpp" @@ -44,7 +48,6 @@ namespace BBM scene->addEntity("background image") .addComponent("assets/map/breakable_wall.obj", true, std::make_pair(MAP_DIFFUSE, "assets/backgrounds/gameWall.png"), Vector3f(50, 1, 50), -90, Vector3f(), Vector3f(1, 0, 0)) .addComponent(5, 14, 22); - MapGenerator::loadMap(16, 16, MapGenerator::createMap(16, 16, hasHeights), scene); return scene; } @@ -57,7 +60,7 @@ namespace BBM //{SoundComponent::DEATH, "assets/sounds/death.ogg"} }; - return scene.addEntity("player") + return scene.addEntity("Player") .addComponent() .addComponent("assets/player/player.iqm", true, std::nullopt, Vector3f(.75, .75, .75)) .addComponent() @@ -67,6 +70,7 @@ namespace BBM .addComponent(true) .addComponent>() .addComponent>() + .addComponent() .addComponent("assets/player/player.iqm", 3) .addComponent(BBM::Vector3f{0.25, 0, 0.25}, BBM::Vector3f{.6, 2, .6}) .addComponent() diff --git a/sources/Runner/MainMenuScene.cpp b/sources/Runner/MainMenuScene.cpp index 08497d25..c88aef1f 100644 --- a/sources/Runner/MainMenuScene.cpp +++ b/sources/Runner/MainMenuScene.cpp @@ -3,6 +3,8 @@ #include #include "Runner.hpp" #include +#include +#include #include "Component/Music/MusicComponent.hpp" #include "Component/Sound/SoundComponent.hpp" #include "Component/Controllable/ControllableComponent.hpp" @@ -34,7 +36,7 @@ namespace BBM .addComponent(1920 / 3, 180, 0) .addComponent("assets/logo_small.png"); auto &play = scene->addEntity("play button") - .addComponent(1920 / 2.5, 1080 - 540, 0) + .addComponent(1920 / 2.5, 1080 - 650, 0) .addComponent("assets/buttons/button_new_game.png") .addComponent([](WAL::Entity &entity, WAL::Wal &) { @@ -52,8 +54,42 @@ namespace BBM { gameState.nextScene = BBM::GameState::SceneID::LobbyScene; }); + auto &resume = scene->addEntity("resume button") + .addComponent(1920 / 2.5, 1080 - 540, 0) + .addComponent("assets/buttons/button_resume_game.png") + .addComponent([](WAL::Entity &entity, WAL::Wal &) + { + RAY::Texture *texture = dynamic_cast(entity.getComponent().drawable.get()); + + texture->use("assets/buttons/button_resume_game.png"); + }) + .addComponent([](WAL::Entity &entity, WAL::Wal &) + { + RAY::Texture *texture = dynamic_cast(entity.getComponent().drawable.get()); + + texture->use("assets/buttons/button_resume_game_hovered.png"); + }) + .addComponent([](WAL::Entity &entity, WAL::Wal &) + { + gameState.nextScene = BBM::GameState::SceneID::ResumeLobbyScene; + auto gameScene = Runner::loadGameScene(); + try { + ParserYAML::load(gameScene); + } catch (std::exception const &err) { + std::cout << err.what() << std::endl; + Runner::gameState.loadedScenes[GameState::SceneID::MainMenuScene]->addEntity("Error message parser") + .addComponent(1920 / 5, 2 * 1080 / 4.25, 0) + .addComponent(3s, [](WAL::Entity &myEntity, WAL::Wal &) { + myEntity.scheduleDeletion(); + }) + .addComponent("Could not load file: " + std::string(err.what()), 50, RAY::Vector2(), RED); + gameState.nextScene = BBM::GameState::SceneID::MainMenuScene; + return; + } + Runner::gameState.loadedScenes[GameState::SceneID::GameScene] = gameScene; + }); auto &settings = scene->addEntity("settings button") - .addComponent(1920 / 2.5, 1080 - 360, 0) + .addComponent(1920 / 2.5, 1080 - 430, 0) .addComponent("assets/buttons/button_settings.png") .addComponent([](WAL::Entity &entity, WAL::Wal &) { @@ -72,7 +108,7 @@ namespace BBM gameState.nextScene = BBM::GameState::SceneID::SettingsScene; }); auto &exit = scene->addEntity("exit button") - .addComponent(1920 / 2.5, 1080 - 180, 0) + .addComponent(1920 / 2.5, 1080 - 320, 0) .addComponent("assets/buttons/button_exit.png") .addComponent([](WAL::Entity &entity, WAL::Wal &) { @@ -109,8 +145,9 @@ namespace BBM { gameState.nextScene = BBM::GameState::SceneID::CreditScene; }); - play.getComponent().setButtonLinks(nullptr, &settings); - settings.getComponent().setButtonLinks(&play, &exit); + play.getComponent().setButtonLinks(nullptr, &resume); + resume.getComponent().setButtonLinks(&play, &settings); + settings.getComponent().setButtonLinks(&resume, &exit); exit.getComponent().setButtonLinks(&settings, &credits, nullptr, &credits); credits.getComponent().setButtonLinks(&exit, nullptr, &exit); return scene; diff --git a/sources/Runner/PauseMenuScene.cpp b/sources/Runner/PauseMenuScene.cpp index 263c2c1a..7a6d2aa9 100644 --- a/sources/Runner/PauseMenuScene.cpp +++ b/sources/Runner/PauseMenuScene.cpp @@ -7,6 +7,7 @@ #include "Component/Health/HealthComponent.hpp" #include "Component/Timer/TimerComponent.hpp" #include "Component/Tag/TagComponent.hpp" +#include #include "Component/Music/MusicComponent.hpp" #include "Component/Sound/SoundComponent.hpp" #include "Component/Controllable/ControllableComponent.hpp" @@ -14,6 +15,7 @@ #include "Component/Renderer/Drawable2DComponent.hpp" #include "Component/Button/ButtonComponent.hpp" #include "Drawables/2D/Text.hpp" +#include namespace RAY2D = RAY::Drawables::Drawables2D; @@ -75,10 +77,33 @@ namespace BBM entity.scheduleDeletion(); }) .addComponent(1920 / 2 - 2 * 30, 1080 / 2, 0) - .addComponent>() + .addComponent>() .addComponent("", 60, RAY::Vector2(), ORANGE); gameState.nextScene = BBM::GameState::SceneID::GameScene; }); + auto &save = scene->addEntity("save & quit button") + .addComponent(1920 / 2.5, 1080 - 240, 0) + .addComponent("assets/buttons/button_save.png") + .addComponent([](WAL::Entity &entity, WAL::Wal &) + { + RAY::Texture *texture = dynamic_cast(entity.getComponent().drawable.get()); + + texture->use("assets/buttons/button_save.png"); + }) + .addComponent([](WAL::Entity &entity, WAL::Wal &) + { + RAY::Texture *texture = dynamic_cast(entity.getComponent().drawable.get()); + + texture->use("assets/buttons/button_save_hovered.png"); + }) + .addComponent([](WAL::Entity &entity, WAL::Wal &wal) + { + if (!std::filesystem::exists("save")) + std::filesystem::create_directories("save"); + ParserYAML::save(Runner::gameState.loadedScenes[GameState::SceneID::GameScene]); + wal.getSystem().hasEnded = false; + gameState.nextScene = BBM::GameState::SceneID::MainMenuScene; + }); auto &settings = scene->addEntity("settings button") .addComponent(1920 / 2.5, 1080 - 360, 0) .addComponent("assets/buttons/button_settings.png") @@ -99,7 +124,7 @@ namespace BBM gameState.nextScene = BBM::GameState::SceneID::SettingsScene; }); auto &exit = scene->addEntity("exit button") - .addComponent(1920 / 1.5, 1080 - 360, 0) + .addComponent(1920 / 1.55, 1080 - 360, 0) .addComponent("assets/buttons/button_exit.png") .addComponent([](WAL::Entity &entity, WAL::Wal &) { @@ -120,9 +145,10 @@ namespace BBM }); //needed material //music - play.getComponent().setButtonLinks(nullptr, nullptr, nullptr, &settings); - settings.getComponent().setButtonLinks(nullptr, nullptr, &play, &exit); - exit.getComponent().setButtonLinks(nullptr, nullptr, &settings, nullptr); + save.getComponent().setButtonLinks(&settings); + play.getComponent().setButtonLinks(nullptr, &save, nullptr, &settings); + settings.getComponent().setButtonLinks(nullptr, &save, &play, &exit); + exit.getComponent().setButtonLinks(nullptr, &save, &settings, nullptr); return scene; } } \ No newline at end of file diff --git a/sources/Runner/ResumeLobbyScene.cpp b/sources/Runner/ResumeLobbyScene.cpp new file mode 100644 index 00000000..c839e459 --- /dev/null +++ b/sources/Runner/ResumeLobbyScene.cpp @@ -0,0 +1,97 @@ +// +// Created by hbenjamin on 15/06/2021. +// + +#include +#include "System/Movable/MovableSystem.hpp" +#include +#include +#include +#include +#include "Component/Button/ButtonComponent.hpp" +#include "Component/Renderer/CameraComponent.hpp" +#include "Component/Renderer/Drawable2DComponent.hpp" +#include "Runner.hpp" +#include "Models/GameState.hpp" +#include +#include +#include +#include +#include "System/Sound/PlayerSoundManagerSystem.hpp" +#include "System/Music/MusicSystem.hpp" +#include "System/Lobby/LobbySystem.hpp" +#include "Component/Lobby/LobbyComponent.hpp" + +namespace RAY3D = RAY::Drawables::Drawables3D; +namespace RAY2D = RAY::Drawables::Drawables2D; + +namespace BBM +{ + std::shared_ptr Runner::loadResumeLobbyScene() + { + static const std::map sounds = { + {SoundComponent::JUMP, "assets/sounds/click.ogg"} + }; + auto scene = std::make_shared(); + + addMenuControl(*scene); + scene->addEntity("Control entity") + .addComponent("assets/musics/music_player_select.ogg") + .addComponent(sounds); + scene->addEntity("background") + .addComponent() + .addComponent("assets/backgrounds/menu.png"); + scene->addEntity("white background") + .addComponent(200, 300, 0) + .addComponent(Vector2f(), Vector2f(1525, 325), RAY::Color(WHITE).setA(150)); + scene->addEntity("lobby text") + .addComponent(1920 / 2.75, 100, 0) + .addComponent("Get Ready", 120, RAY::Vector2(), ORANGE); + auto &play = scene->addEntity("play button") + .addComponent(1920 / 2.5, 1080 - 180, 0) + .addComponent("assets/buttons/button_new_game.png") + .addComponent([](WAL::Entity &entity, WAL::Wal &wal) + { + auto *texture = dynamic_cast(entity.getComponent().drawable.get()); + texture->use("assets/buttons/button_new_game.png"); + }) + .addComponent([](WAL::Entity &entity, WAL::Wal &wal) + { + auto *texture = dynamic_cast(entity.getComponent().drawable.get()); + texture->use("assets/buttons/button_new_game_hovered.png"); + }) + .addComponent([](WAL::Entity &entity, WAL::Wal &wal) + { + if (Runner::gameState.currentScene != GameState::ResumeLobbyScene + || !ResumeLobbySystem::playersAreReady(*wal.getScene())) + return; + ResumeLobbySystem::resumeToGame(wal); + }) + .addComponent>(); + auto &back = scene->addEntity("back to menu") + .addComponent(10, 1080 - 85, 0) + .addComponent("assets/buttons/button_back.png") + .addComponent([](WAL::Entity &entity, WAL::Wal &wal) + { + wal.getSystem().unloadLobbyFromResume(); + gameState.nextScene = BBM::GameState::SceneID::MainMenuScene; + }) + .addComponent([](WAL::Entity &entity, WAL::Wal &) + { + RAY::Texture *texture = dynamic_cast(entity.getComponent().drawable.get()); + texture->use("assets/buttons/button_back.png"); + }) + .addComponent([](WAL::Entity &entity, WAL::Wal &) + { + RAY::Texture *texture = dynamic_cast(entity.getComponent().drawable.get()); + + texture->use("assets/buttons/button_back_hovered.png"); + }); + scene->addEntity("camera") + .addComponent(8, 20, 7) + .addComponent(Vector3f(8, 0, 8)); + play.getComponent().setButtonLinks(nullptr, &back, &back); + back.getComponent().setButtonLinks(&play, nullptr, nullptr, &play); + return scene; + } +} \ No newline at end of file diff --git a/sources/Runner/Runner.cpp b/sources/Runner/Runner.cpp index 19019a33..6be7a515 100644 --- a/sources/Runner/Runner.cpp +++ b/sources/Runner/Runner.cpp @@ -33,6 +33,8 @@ #include "System/IAControllable/IAControllableSystem.hpp" #include "System/MenuControllable/MenuControllableSystem.hpp" #include +#include +#include #include "System/Sound/PlayerSoundManagerSystem.hpp" #include "System/Sound/MenuSoundManagerSystem.hpp" #include "System/Gravity/GravitySystem.hpp" @@ -46,7 +48,10 @@ namespace BBM { + std::chrono::nanoseconds Runner::timerDelay = std::chrono::minutes(3); GameState Runner::gameState; + int Runner::mapWidth = 16; + int Runner::mapHeight = 16; bool Runner::hasHeights = false; void Runner::updateState(WAL::Wal &engine, GameState &state) @@ -108,6 +113,7 @@ namespace BBM .addSystem() .addSystem() .addSystem() + .addSystem() .addSystem() .addSystem(); } @@ -152,6 +158,7 @@ namespace BBM gameState.loadedScenes[GameState::SceneID::CreditScene] = loadCreditScene(); gameState.loadedScenes[GameState::SceneID::SplashScreen] = loadSplashScreenScene(); gameState.loadedScenes[GameState::SceneID::LobbyScene] = loadLobbyScene(); + gameState.loadedScenes[GameState::SceneID::ResumeLobbyScene] = loadResumeLobbyScene(); gameState.loadedScenes[GameState::SceneID::HowToPlayScene] = loadHowToPlayScene(); } @@ -164,6 +171,7 @@ namespace BBM Runner::loadScenes(); wal.changeScene(Runner::gameState.loadedScenes[GameState::SceneID::SplashScreen]); wal.run(Runner::updateState, Runner::gameState); + gameState.loadedScenes.clear(); return 0; } } \ No newline at end of file diff --git a/sources/Runner/Runner.hpp b/sources/Runner/Runner.hpp index 3c1d9d04..475ea15b 100644 --- a/sources/Runner/Runner.hpp +++ b/sources/Runner/Runner.hpp @@ -5,6 +5,7 @@ #pragma once #include "Models/GameState.hpp" #include "Wal.hpp" +#include #include #include "Component/Sound/SoundComponent.hpp" @@ -15,7 +16,14 @@ namespace BBM //! @brief Has the map heights or is it disabled? static bool hasHeights; - //! @brief store current scenes information + //! @brief the width of the map + static int mapWidth; + //! @brief the height of the map + static int mapHeight; + //! @brief timer duration + static std::chrono::nanoseconds timerDelay; + + //! @brief store current scenes informations static GameState gameState; //! @brief Start the game and run a Bomberman. //! @return 0 on success, another value on error. @@ -55,6 +63,9 @@ namespace BBM //! @brief load all data related to lobby screen static std::shared_ptr loadLobbyScene(); + //! @brief load all data related to resume lobby screen + static std::shared_ptr loadResumeLobbyScene(); + //! @brief Create a player (without any controllable) and add it to the scene. //! @param scene The scene where to player should reside. //! @return A reference to the created player. diff --git a/sources/System/Controllable/ControllableSystem.cpp b/sources/System/Controllable/ControllableSystem.cpp index b823547f..c6d4258d 100644 --- a/sources/System/Controllable/ControllableSystem.cpp +++ b/sources/System/Controllable/ControllableSystem.cpp @@ -6,6 +6,7 @@ #include "ControllableSystem.hpp" #include "Component/Movable/MovableComponent.hpp" #include "Component/Controllable/ControllableComponent.hpp" +#include "Component/Speed/SpeedComponent.hpp" #include "Component/Health/HealthComponent.hpp" namespace BBM @@ -17,8 +18,10 @@ namespace BBM void ControllableSystem::onFixedUpdate(WAL::ViewEntity &entity) { auto &controllable = entity.get(); + // todo check why the .get doesn't work + auto &speed = entity->getComponent(); auto &movable = entity.get(); - Vector2f move = controllable.move.normalized() * controllable.speed; + Vector2f move = controllable.move.normalized() * speed.speed; movable.addForce(Vector3f(move.x, 0, move.y)); } diff --git a/sources/System/Lobby/LobbySystem.cpp b/sources/System/Lobby/LobbySystem.cpp index 8bf01496..0a10d8ae 100644 --- a/sources/System/Lobby/LobbySystem.cpp +++ b/sources/System/Lobby/LobbySystem.cpp @@ -6,6 +6,7 @@ #include "Component/Renderer/Drawable2DComponent.hpp" #include "System/Lobby/LobbySystem.hpp" #include "Component/Controllable/ControllableComponent.hpp" +#include "Component/Speed/SpeedComponent.hpp" #include "System/MenuControllable/MenuControllableSystem.hpp" #include "Component/Tag/TagComponent.hpp" #include @@ -15,12 +16,13 @@ #include "Component/IAControllable/IAControllableComponent.hpp" #include #include -#include +#include +#include +#include #include #include "Component/Color/ColorComponent.hpp" #include "Component/Stat/StatComponent.hpp" #include "Component/Bonus/PlayerBonusComponent.hpp" -#include "Component/BombHolder/BombHolderComponent.hpp" namespace RAY3D = RAY::Drawables::Drawables3D; namespace RAY2D = RAY::Drawables::Drawables2D; @@ -179,7 +181,7 @@ namespace BBM }); } - void LobbySystem::_addController(WAL::Entity &player, ControllableComponent::Layout layout) + void LobbySystem::addController(WAL::Entity &player, ControllableComponent::Layout layout) { switch (layout) { case ControllableComponent::KEYBOARD_0: @@ -207,35 +209,18 @@ namespace BBM player.getComponent().layout = layout; } - void LobbySystem::switchToGame(WAL::Wal &wal) + void LobbySystem::createTile(std::shared_ptr scene, WAL::Entity &player, int color, int playerCount) { - auto scene = Runner::loadGameScene(); - int mapWidth = 16; - int mapHeight = 16; - int playerCount = 0; - - for (auto &[_, lobby] : wal.getScene()->view()) { - if (lobby.layout == ControllableComponent::NONE) - continue; - auto &player = Runner::createPlayer(*scene); - _addController(player, lobby.layout); - player.getComponent().position = Vector3f(mapWidth * (playerCount % 2), - (Runner::hasHeights ? 1.01 : 0), - mapHeight * (!(playerCount % 3))); - auto *model = dynamic_cast(player.getComponent().drawable.get()); - model->setTextureToMaterial(MAP_DIFFUSE, "assets/player/textures/" + colors[lobby.color] + ".png"); - - - std::string texturePath = "assets/player/ui/" + colors[lobby.color] + ".png"; - int x = (playerCount % 2 == 0) ? 1920 - 10 - 320 : 10; - int y = (playerCount % 3 != 0) ? 1080 - 10 - 248 : 10; - scene->addEntity("player color tile") + std::string texturePath = "assets/player/ui/" + colors[color] + ".png"; + int x = (playerCount % 2 == 0) ? 1920 - 10 - 320 : 10; + int y = (playerCount % 3 != 0) ? 1080 - 10 - 248 : 10; + scene->addEntity("player color tile") .addComponent(x, y - 2, 0) - .addComponent(x, y, 320, 248, _rayColors[lobby.color]); - scene->addEntity("player ui tile") + .addComponent(x, y, 320, 248, RAY::Color(_rayColors[color]).setA(150)); + scene->addEntity("player ui tile") .addComponent(x, y, 0) .addComponent(texturePath); - scene->addEntity("player hide fireup") + scene->addEntity("player hide fireup") .addComponent(x + 220, y + 35, 0) .addComponent("", 20, x, y, WHITE) .addComponent([&player](Drawable2DComponent &drawble) { @@ -248,7 +233,7 @@ namespace BBM return; text->setText(std::to_string(static_cast(bonus->explosionRadius))); }); - scene->addEntity("player hide bombup") + scene->addEntity("player hide bombup") .addComponent(x + 220, y + 77, 0) .addComponent("", 20, x, y, WHITE) .addComponent([&player](Drawable2DComponent &drawble) { @@ -261,20 +246,20 @@ namespace BBM return; text->setText(std::to_string(bonus->bombCount) + " / " + std::to_string(bonus->maxBombCount)); }); - scene->addEntity("player hide speedup") + scene->addEntity("player hide speedup") .addComponent(x + 220, y + 122, 0) .addComponent("", 20, x, y, WHITE) .addComponent([&player](Drawable2DComponent &drawble) { - const ControllableComponent *bonus = player.tryGetComponent(); + auto *speed = player.tryGetComponent(); - if (!bonus) + if (!speed) return; RAY2D::Text *text = dynamic_cast(drawble.drawable.get()); if (!text) return; - text->setText(std::to_string(static_cast(bonus->speed * 100))); + text->setText(std::to_string(static_cast(speed->speed * 100))); }); - scene->addEntity("player hide wall") + scene->addEntity("player hide wall") .addComponent(x + 220, y + 161, 0) .addComponent("", 20, x, y, WHITE) .addComponent([&player](Drawable2DComponent &drawble) { @@ -287,9 +272,31 @@ namespace BBM return; text->setText(bonus->isNoClipOn ? "YES" : "NO"); }); + } + + void LobbySystem::switchToGame(WAL::Wal &wal) + { + auto scene = Runner::loadGameScene(); + int mapWidth = Runner::mapWidth; + int mapHeight = Runner::mapHeight; + int playerCount = 0; + + for (auto &[_, lobby] : wal.getScene()->view()) { + if (lobby.layout == ControllableComponent::NONE) + continue; + auto &player = Runner::createPlayer(*scene); + player.getComponent().position = Vector3f(mapWidth * (playerCount % 2), + (Runner::hasHeights ? 1.01 : 0), + mapHeight * (!(playerCount % 3))); + auto *model = dynamic_cast(player.getComponent().drawable.get()); + model->setTextureToMaterial(MAP_DIFFUSE, "assets/player/textures/" + colors[lobby.color] + ".png"); + + addController(player, lobby.layout); + createTile(scene, player, lobby.color, playerCount); playerCount++; } Runner::gameState.loadedScenes[GameState::SceneID::GameScene] = scene; + MapGenerator::loadMap(Runner::mapWidth, Runner::mapHeight, MapGenerator::createMap(Runner::mapWidth, Runner::mapHeight, Runner::hasHeights), scene); Runner::gameState.nextScene = BBM::GameState::SceneID::GameScene; wal.getSystem().unloadLobby(); } diff --git a/sources/System/Lobby/LobbySystem.hpp b/sources/System/Lobby/LobbySystem.hpp index b10641aa..67878d85 100644 --- a/sources/System/Lobby/LobbySystem.hpp +++ b/sources/System/Lobby/LobbySystem.hpp @@ -17,8 +17,6 @@ namespace BBM class LobbySystem : public WAL::System { private: - //! @brief Add a controller for the player. - static void _addController(WAL::Entity &player, ControllableComponent::Layout layout); void _nextColor(WAL::ViewEntity &entity); @@ -29,6 +27,12 @@ namespace BBM public: static std::array colors; + //! @brief Add a controller for the player. + static void addController(WAL::Entity &player, ControllableComponent::Layout layout); + + //! @brief Create ingame tile + static void createTile(std::shared_ptr scene, WAL::Entity &player, int color, int playerCount); + //! @inherit void onUpdate(WAL::ViewEntity &entity, std::chrono::nanoseconds dtime) override; diff --git a/sources/System/Lobby/ResumeLobbySystem.cpp b/sources/System/Lobby/ResumeLobbySystem.cpp new file mode 100644 index 00000000..d1528bfc --- /dev/null +++ b/sources/System/Lobby/ResumeLobbySystem.cpp @@ -0,0 +1,125 @@ +// +// Created by Zoe Roux on 6/11/21. +// + +#include "System/Event/EventSystem.hpp" +#include "Component/Renderer/Drawable2DComponent.hpp" +#include "System/Lobby/ResumeLobbySystem.hpp" +#include "Component/Controllable/ControllableComponent.hpp" +#include "Component/Speed/SpeedComponent.hpp" +#include "System/MenuControllable/MenuControllableSystem.hpp" +#include "Component/Tag/TagComponent.hpp" +#include +#include +#include +#include +#include "Component/IAControllable/IAControllableComponent.hpp" +#include +#include +#include +#include +#include +#include +#include "Component/Color/ColorComponent.hpp" +#include "Component/Stat/StatComponent.hpp" +#include "Component/Bonus/PlayerBonusComponent.hpp" +#include "System/Lobby/LobbySystem.hpp" + +namespace RAY3D = RAY::Drawables::Drawables3D; +namespace RAY2D = RAY::Drawables::Drawables2D; + +namespace BBM +{ + ResumeLobbySystem::ResumeLobbySystem(WAL::Wal &wal) + : System(wal) + {} + + void ResumeLobbySystem::onUpdate(WAL::ViewEntity &entity, std::chrono::nanoseconds dtime) + { + auto &lobby = entity.get(); + + auto lastTick = std::chrono::steady_clock::now(); + if (lastTick - lobby.lastInput < std::chrono::milliseconds(150)) + return; + + if (lobby.layout == ControllableComponent::NONE) { + for (auto &[_, ctrl] : this->_wal.getScene()->view()) { + auto &controller = ctrl; + if (controller.bomb) { + if (std::any_of(this->getView().begin(), this->getView().end(), [&controller](WAL::ViewEntity &view) { + return view.get().layout == controller.layout; + })) + return; + lobby.ready = true; + lobby.lastInput = lastTick; + lobby.layout = controller.layout; + controller.bomb = false; + this->_wal.getSystem().now = lastTick; + auto *texture = dynamic_cast(lobby.readyButton.getComponent().drawable.get()); + if (texture) + texture->use("assets/player/icons/ready.png"); + return; + } + } + } + } + + void ResumeLobbySystem::onSelfUpdate(std::chrono::nanoseconds dtime) + { + auto &view = this->_wal.getScene()->view, Drawable2DComponent>(); + if (view.size() == 0) + return; + auto *texture = dynamic_cast(view.front().get().drawable.get()); + if (!texture) + return; + if (playersAreReady(*this->_wal.getScene())) + texture->setColor(WHITE); + else + texture->setColor(GRAY); + } + + bool ResumeLobbySystem::playersAreReady(WAL::Scene &scene) + { + auto &lobby = scene.view(); + return std::all_of(lobby.begin(), lobby.end(), [](WAL::ViewEntity &entity) { + auto &lobbyPlayer = entity.get(); + return lobbyPlayer.ready && lobbyPlayer.layout != ControllableComponent::NONE; + }); } + + void ResumeLobbySystem::resumeToGame(WAL::Wal &wal) + { + auto scene = Runner::gameState.loadedScenes[GameState::SceneID::GameScene]; + int countPlayer = 0; + + for (auto &[_, lobby] : wal.getScene()->view()) { + if (lobby.layout == ControllableComponent::NONE) + continue; + auto &player = Runner::createPlayer(*scene); + player.setName(ParserYAML::playersInfos[countPlayer].name); + auto *position = player.tryGetComponent(); + auto *bombHolder = player.tryGetComponent(); + auto *model = player.tryGetComponent(); + auto *speed = player.tryGetComponent(); + if (position && bombHolder && model && speed) { + dynamic_cast(model->drawable.get())->setTextureToMaterial(MAP_DIFFUSE, + ParserYAML::playersInfos[countPlayer].asset); + position->position = ParserYAML::playersInfos[countPlayer].position; + bombHolder->explosionRadius = ParserYAML::playersInfos[countPlayer].explosionRange; + bombHolder->maxBombCount = ParserYAML::playersInfos[countPlayer].maxBombCount; + speed->speed = ParserYAML::playersInfos[countPlayer].speed; + } + LobbySystem::addController(player, lobby.layout); + LobbySystem::createTile(scene, player, lobby.playerColor, countPlayer); + countPlayer++; + } + Runner::gameState.nextScene = BBM::GameState::SceneID::GameScene; + wal.getSystem().unloadLobbyFromResume(); + } + + void ResumeLobbySystem::unloadLobbyFromResume() + { + for (auto &entity : this->getView()) { + entity->scheduleDeletion(); + } + } +} \ No newline at end of file diff --git a/sources/System/Lobby/ResumeLobbySystem.hpp b/sources/System/Lobby/ResumeLobbySystem.hpp new file mode 100644 index 00000000..10d0a487 --- /dev/null +++ b/sources/System/Lobby/ResumeLobbySystem.hpp @@ -0,0 +1,46 @@ +// +// Created by hbenjamin on 6/18/21. +// + +#pragma once + +#include "System/System.hpp" +#include "Component/Lobby/ResumeLobbyComponent.hpp" +#include "Component/Controllable/ControllableComponent.hpp" +#include "Entity/Entity.hpp" +#include +#include + +namespace BBM +{ + //! @brief A system to handle Health entities. + class ResumeLobbySystem : public WAL::System + { + public: + + //! @brief Add a controller for the player when we resume a game + static void resumeToGame(WAL::Wal &wal); + + //! @inherit + void onUpdate(WAL::ViewEntity &entity, std::chrono::nanoseconds dtime) override; + + //! @inherit + void onSelfUpdate(std::chrono::nanoseconds dtime) override; + + //! @brief Check if every player is ready. + //! @param scene The lobby scene containing lobby players. + static bool playersAreReady(WAL::Scene &scene); + + //! @brief Reset the resume lobby scene to it's default state. + void unloadLobbyFromResume(); + + //! @brief A default constructor + explicit ResumeLobbySystem(WAL::Wal &wal); + //! @brief A Lobby system is copy constructable + ResumeLobbySystem(const ResumeLobbySystem &) = default; + //! @brief A default destructor + ~ResumeLobbySystem() override = default; + //! @brief A system is not assignable. + ResumeLobbySystem &operator=(const ResumeLobbySystem &) = delete; + }; +} \ No newline at end of file diff --git a/sources/System/Renderer/CameraSystem.cpp b/sources/System/Renderer/CameraSystem.cpp index ca5eda77..6722d48f 100644 --- a/sources/System/Renderer/CameraSystem.cpp +++ b/sources/System/Renderer/CameraSystem.cpp @@ -32,12 +32,12 @@ namespace BBM .addComponent(1920 / 2 - 2 * 30 - 20, 28, 0) .addComponent(Vector2f(), Vector2f(150, 60), RAY::Color(BLACK).setA(150)); this->_wal.getScene()->scheduleNewEntity("Timer") - .addComponent(std::chrono::minutes (3), [](WAL::Entity &, WAL::Wal &engine) { + .addComponent(Runner::timerDelay, [](WAL::Entity &, WAL::Wal &engine) { engine.getSystem().hasEnded = false; Runner::gameState.nextScene = GameState::ScoreScene; }) - .addComponent>() .addComponent(1920 / 2 - 2 * 30, 30, 0) + .addComponent>() .addComponent("", 60, RAY::Vector2(), ORANGE); for (WAL::Entity &player : this->_wal.getScene()->view>()) player.getComponent().disabled = false; diff --git a/sources/System/Timer/TimerUISystem.cpp b/sources/System/Timer/TimerUISystem.cpp index c35f8155..194655e7 100644 --- a/sources/System/Timer/TimerUISystem.cpp +++ b/sources/System/Timer/TimerUISystem.cpp @@ -15,7 +15,7 @@ namespace BBM : System(wal) {} - void TimerUISystem::onUpdate(WAL::ViewEntity &entity, std::chrono::nanoseconds dtime) + void TimerUISystem::onUpdate(WAL::ViewEntity> &entity, std::chrono::nanoseconds dtime) { auto &timer = entity.get(); RAY2D::Text *text = dynamic_cast(entity.get().drawable.get()); diff --git a/sources/System/Timer/TimerUISystem.hpp b/sources/System/Timer/TimerUISystem.hpp index c21a0300..264d2bad 100644 --- a/sources/System/Timer/TimerUISystem.hpp +++ b/sources/System/Timer/TimerUISystem.hpp @@ -8,22 +8,23 @@ #include #include #include "Component/Renderer/Drawable2DComponent.hpp" +#include "Component/Tag/TagComponent.hpp" namespace BBM { - class TimerUISystem : public WAL::System + class TimerUISystem : public WAL::System> { public: //! @inherit - void onUpdate(WAL::ViewEntity &entity, std::chrono::nanoseconds dtime) override; + void onUpdate(WAL::ViewEntity> &entity, std::chrono::nanoseconds dtime) override; //! @brief A default constructor - TimerUISystem(WAL::Wal &); + explicit TimerUISystem(WAL::Wal &); //! @brief A timer system is copy constructable. TimerUISystem(const TimerUISystem &) = default; //! @brief A default destructor ~TimerUISystem() override = default; //! @breief A timer system is assignable. - TimerUISystem &operator=(const TimerUISystem &) = default; + TimerUISystem &operator=(const TimerUISystem &) = delete; }; } diff --git a/sources/Utils/Utils.cpp b/sources/Utils/Utils.cpp new file mode 100644 index 00000000..e0d46c87 --- /dev/null +++ b/sources/Utils/Utils.cpp @@ -0,0 +1,102 @@ +// +// Created by cbihan on 17/06/2021. +// + +#include +#include +#include +#include +#include +#include +#include +#include "Utils.hpp" + +namespace BBM +{ + void Utils::lTrim(std::string &s) + { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { + return !std::isspace(ch); + })); + } + + void Utils::rTrim(std::string &s) + { + s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { + return !std::isspace(ch); + }).base(), s.end()); + } + + void Utils::trim(std::string &s) + { + lTrim(s); + rTrim(s); + } + + std::string Utils::lTrimCopy(std::string s) + { + lTrim(s); + return s; + } + + std::string Utils::rTrimCopy(std::string s) + { + rTrim(s); + return s; + } + + std::string Utils::trimCopy(std::string s) + { + trim(s); + return s; + } + + int Utils::findFrequency(const std::string &s, const std::string &pattern) + { + std::regex c(pattern); + std::smatch m; + + ptrdiff_t numberOfMatches = std::distance(std::sregex_iterator(s.begin(), s.end(), c), std::sregex_iterator()); + return static_cast(numberOfMatches); + } + + bool Utils::tryParseInteger(const std::string &s, int &i) + { + std::istringstream iss(s); + + iss >> std::noskipws >> i; + return iss.eof() && !iss.fail(); + } + + bool Utils::tryParseFloat(const std::string &s, float &f) + { + std::istringstream iss(s); + + iss >> std::noskipws >> f; + return iss.eof() && !iss.fail(); + } + + std::vector Utils::splitStr(const std::string &str, char delim) + { + std::vector strings; + std::istringstream f(str); + std::string buffer; + + while (std::getline(f, buffer, delim)) {; + strings.push_back(buffer); + } + if (str.back() == delim) { + strings.emplace_back(""); + } + return strings; + } + + bool Utils::tryParseLong(const std::string &s, long &l) + { + std::istringstream iss(s); + + iss >> std::noskipws >> l; + return iss.eof() && !iss.fail(); + } + +} \ No newline at end of file diff --git a/sources/Utils/Utils.hpp b/sources/Utils/Utils.hpp new file mode 100644 index 00000000..64f43e44 --- /dev/null +++ b/sources/Utils/Utils.hpp @@ -0,0 +1,46 @@ +// +// Created by cbihan on 17/06/2021. +// + +#pragma once + +namespace BBM +{ + struct Utils + { + //! @brief trim left end + static void lTrim(std::string &s); + + //! @brief trim right end + static void rTrim(std::string &s); + + //! @brief trim from both ends + static void trim(std::string &s); + + //! @brief trim left end (copying) + static std::string lTrimCopy(std::string s); + + //! @brief trim right end (copying) + static std::string rTrimCopy(std::string s); + + //! @brief trim from both ends (copying) + static std::string trimCopy(std::string s); + + //! @brief find the frequency of a substring in a string + static int findFrequency(const std::string &s, const std::string &pattern); + + //! @brief return true if parsing has been successful result ill be in i + static bool tryParseInteger(const std::string &s, int &i); + + //! @brief return true if parsing has been successful result ill be in f + static bool tryParseFloat(const std::string &s, float &f); + + //! @brief return true if parsing has been successful result ill be in l + static bool tryParseLong(const std::string &s, long &l); + + //! @brief split a string with a delim char + static std::vector splitStr(const std::string &str, char delim); + }; + + +} \ No newline at end of file diff --git a/tests/MoveTests.cpp b/tests/MoveTests.cpp index 75ff2bbd..413332b8 100644 --- a/tests/MoveTests.cpp +++ b/tests/MoveTests.cpp @@ -12,6 +12,7 @@ #include "System/Movable/MovableSystem.hpp" #include #include +#include using namespace WAL; using namespace BBM; @@ -24,6 +25,7 @@ TEST_CASE("Move test", "[Component][System]") wal.getScene()->addEntity("player") .addComponent() .addComponent() + .addComponent() .addComponent(); Entity &entity = wal.getScene()->getEntities().front(); diff --git a/tests/ViewTest.cpp b/tests/ViewTest.cpp index aaa1bf36..2d7df652 100644 --- a/tests/ViewTest.cpp +++ b/tests/ViewTest.cpp @@ -61,6 +61,7 @@ TEST_CASE("View add entity", "[View]") .addComponent(); scene.applyChanges(); REQUIRE(scene.view().size() == 2); + (void)entity; } TEST_CASE("View remove entity", "[View]") @@ -75,6 +76,7 @@ TEST_CASE("View remove entity", "[View]") REQUIRE(scene.view().size() == 0); for (auto &it : scene.view()) REQUIRE(false); + (void)scene; } TEST_CASE("View cache switch", "[View]")