merge develop

This commit is contained in:
Bluub
2021-06-19 19:06:35 +02:00
54 changed files with 1583 additions and 170 deletions

5
.gitignore vendored
View File

@@ -7,4 +7,7 @@ build/*
docs/*
emsdk/
build_web/*
wasm-python.py
wasm-python.py
lua54*
include/*
save/*

View File

@@ -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

View File

@@ -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 :)
<div>
<img align="left" src="./assets/images/titlescreen.png" width="45%">
<img align="right" src="./assets/images/lobby.png" width="45%">
<img src="./assets/images/game.png">
</div>
## 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

View File

@@ -9,7 +9,7 @@ mapinfo.dist { }
------------
------ Debug variables
local debug = true
local debug = false
if not debug then
log = function() end

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
assets/images/game.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
assets/images/lobby.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 941 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
images/game.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
images/lobby.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 941 KiB

BIN
images/titlescreen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -31,4 +31,4 @@ RAY::Vector3::operator ::Vector3() const
v.y = this->y;
v.z = this->z;
return v;
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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.

View File

@@ -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);
}
}

View File

@@ -0,0 +1,44 @@
//
// Created by hbenjamin on 6/18/21.
//
#pragma once
#include <Component/Component.hpp>
#include <Entity/Entity.hpp>
#include <Color.hpp>
#include <Component/Controllable/ControllableComponent.hpp>
#include <chrono>
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<std::chrono::steady_clock> 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;
};
}

View File

@@ -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)
{
}
}

View File

@@ -0,0 +1,34 @@
//
// Created by cbihan on 18/06/2021.
//
#pragma once
#include <Component/Component.hpp>
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;
};
}

View File

@@ -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";
}

View File

@@ -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

View File

@@ -0,0 +1,37 @@
//
// Created by hbenjamin on 11/06/2021.
//
#pragma once
#include <exception>
#include <stdexcept>
#include <string>
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;
};
}

View File

@@ -5,6 +5,7 @@
#include <Component/Collision/CollisionComponent.hpp>
#include <Component/Collision/CollisionComponent.hpp>
#include <Component/Controllable/ControllableComponent.hpp>
#include "Component/Speed/SpeedComponent.hpp"
#include <Component/Bonus/PlayerBonusComponent.hpp>
#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<ControllableComponent>();
auto *speed = player.tryGetComponent<SpeedComponent>();
auto *playerBonus = player.tryGetComponent<PlayerBonusComponent>();
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<WAL::Entity &>(bonus).scheduleDeletion();
}

View File

@@ -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

View File

@@ -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<Bonus::BonusType, std::vector<std::string>> 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<std::function<void (WAL::Entity &, const WAL::Entity &, CollisionComponent::CollidedAxis)>> func = {
&Bonus::BombUpBonus, &Bonus::SpeedUpBonus, &Bonus::ExplosionRangeBonus, &Bonus::NoClipBonus
};
entity.addComponent<PositionComponent>(position)
.addComponent<TagComponent<Blowable>>()
.addComponent<MovableComponent>()
.addComponent<HealthComponent>(1, [](WAL::Entity &myEntity, WAL::Wal &wal) {
myEntity.scheduleDeletion();
})
.addComponent<LevitateComponent>(position.y)
.addComponent<CollisionComponent>([](WAL::Entity &bonus, const WAL::Entity &player, CollisionComponent::CollidedAxis axis) {
bonus.scheduleDeletion();
}, func[bonusType - 1], 0.5, .5)
.addComponent<TimerComponent>(5s, [](WAL::Entity &bonus, WAL::Wal &wal){
bonus.scheduleDeletion();
})
.addComponent<Drawable3DComponent, RAY3D::Model>(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<PositionComponent>().position;
static std::map<Bonus::BonusType, std::string> 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<std::function<void (WAL::Entity &, const WAL::Entity &, CollisionComponent::CollidedAxis)>> func = {
&Bonus::BombUpBonus, &Bonus::SpeedUpBonus, &Bonus::ExplosionRangeBonus, &Bonus::NoClipBonus
static std::map<Bonus::BonusType, std::vector<std::string>> 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<PositionComponent>(position)
.addComponent<TagComponent<BlowablePass>>()
.addComponent<MovableComponent>()
.addComponent<HealthComponent>(1, [](WAL::Entity &myEntity, WAL::Wal &) {
myEntity.scheduleDeletion();
})
.addComponent<LevitateComponent>(position.y)
.addComponent<CollisionComponent>(WAL::Callback<WAL::Entity &, const WAL::Entity &, CollisionComponent::CollidedAxis>(),
func[bonusType - 1], 0.5, .5)
.addComponent<TimerComponent>(5s, [](WAL::Entity &bonus, WAL::Wal &){
bonus.scheduleDeletion();
})
.addComponent<Drawable3DComponent, RAY3D::Model>(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<Drawable3DComponent, RAY3D::Model>(breakableObj, false, std::make_pair(MAP_DIFFUSE, breakablePng));
}
void MapGenerator::createFloor(Vector3f coords, std::shared_ptr<WAL::Scene> scene)
{
static const std::string floorObj = floorPath + objExtension;
static const std::string floorPng = floorPath + imageExtension;
scene->addEntity("Floor")
.addComponent<PositionComponent>(Vector3f(coords))
.addComponent<Drawable3DComponent, RAY3D::Model>(floorObj, false, std::make_pair(MAP_DIFFUSE, floorPng));
}
void MapGenerator::createUpperFloor(Vector3f coords, std::shared_ptr<WAL::Scene> scene)
{
static const std::string floorObj = secondFloorPath + objExtension;
@@ -508,7 +508,7 @@ namespace BBM
.addComponent<PositionComponent>(Vector3f(width / 2 - width / 4, 0, height / 2 - height / 4))
.addComponent<CollisionComponent>(
WAL::Callback<WAL::Entity &, const WAL::Entity &, CollisionComponent::CollidedAxis>(),
&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<WAL::Scene> &scene)

View File

@@ -22,7 +22,7 @@
#include "Component/Collision/CollisionComponent.hpp"
#include "Component/Movable/MovableComponent.hpp"
#include <chrono>
#include <Items/Bonus.hpp>
namespace BBM
@@ -38,16 +38,54 @@ namespace BBM
BREAKABLE,
HOLE,
UPPERFLOOR,
FLOOR,
BUMPER,
SPAWNER,
UNBREAKABLE
UNBREAKABLE,
INVISIBLE
};
private:
using MapElem = std::function<void(Vector3f coords, std::shared_ptr<WAL::Scene> scene)>;
using MapBlock = std::map<std::tuple<int, int, int>, 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<WAL::Scene> &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<WAL::Scene> scene, BlockType blockType);
private:
using MapElem = std::function<void(Vector3f coords, std::shared_ptr<WAL::Scene> 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<WAL::Scene> 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<WAL::Scene> 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<WAL::Scene> 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<WAL::Scene> 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<WAL::Scene> &scene);
};
} // namespace BBM

View File

@@ -24,6 +24,7 @@ namespace BBM
SettingsScene,
PauseMenuScene,
LobbyScene,
ResumeLobbyScene,
TitleScreenScene,
CreditScene,
HowToPlayScene,

61
sources/Parser/Node.cpp Normal file
View File

@@ -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> Node::getChildNodes(const std::string &childNodeName)
{
std::vector<Node> childs;
for (const auto &child : this->_childNodes) {
if (child.getName() == childNodeName) {
childs.emplace_back(child);
}
}
return childs;
}
std::vector<Node> Node::getChildNodes(void)
{
return this->_childNodes;
}
void Node::setName(const std::string &name)
{
this->_name = name;
}
void Node::setProperty(const std::pair<std::string, std::string> &propertyNameValue)
{
this->setProperty(propertyNameValue.first, propertyNameValue.second);
}
}

51
sources/Parser/Node.hpp Normal file
View File

@@ -0,0 +1,51 @@
//
// Created by cbihan on 16/06/2021.
//
#pragma once
#include <vector>
#include <map>
#include <string>
namespace BBM
{
class Node
{
private:
//! @brief Node name
std::string _name;
//! @brief child nodes
std::vector<Node> _childNodes;
//! @brief node's properties
std::map<std::string, std::string> _properties;
public:
std::string getName() const;
void setName(const std::string &name);
void addChildNode(const Node &childNode);
std::vector<Node> getChildNodes(const std::string &childNodeName);
std::vector<Node> getChildNodes(void);
void setProperty(const std::string &propertyName, const std::string &propertyValue);
void setProperty(const std::pair<std::string, std::string> &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;
};
}

View File

@@ -0,0 +1,453 @@
//
// Created by hbenjamin on 10/06/2021.
//
#include <fstream>
#include <map>
#include <Component/Position/PositionComponent.hpp>
#include <Component/Timer/TimerComponent.hpp>
#include <Map/Map.hpp>
#include <Component/BombHolder/BombHolderComponent.hpp>
#include <sstream>
#include <Component/Keyboard/KeyboardComponent.hpp>
#include <Component/Shaders/ShaderComponent.hpp>
#include <Component/Animator/AnimatorComponent.hpp>
#include <Component/Tag/TagComponent.hpp>
#include <Component/Animation/AnimationsComponent.hpp>
#include <Component/Sound/SoundComponent.hpp>
#include <Component/Bonus/PlayerBonusComponent.hpp>
#include <Component/Music/MusicComponent.hpp>
#include <Items/Bonus.hpp>
#include <Exception/Error.hpp>
#include "ParserYaml.hpp"
#include "Component/Speed/SpeedComponent.hpp"
#include <algorithm>
#include <Component/Levitate/LevitateComponent.hpp>
#include <Runner/Runner.hpp>
#include <cstring>
#include <sstream>
#include <Component/Gamepad/GamepadComponent.hpp>
#include <Component/Renderer/Drawable2DComponent.hpp>
#include <System/Lobby/LobbySystem.hpp>
#include <filesystem>
#include <Component/Lobby/ResumeLobbyComponent.hpp>
#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::PlayerInfos> ParserYAML::playersInfos = {};
std::string ParserYAML::_getBlockType(const std::string& blockName)
{
static std::map<std::string, MapGenerator::BlockType> 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<std::string, Bonus::BonusType> 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<PositionComponent>();
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<PositionComponent>();
auto *bombHolder = entity.tryGetComponent<BombHolderComponent>();
auto *model = entity.tryGetComponent<Drawable3DComponent>();
auto *speed = entity.tryGetComponent<SpeedComponent>();
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<RAY3D::Model *>(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<PositionComponent>();
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<WAL::Scene> 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<std::string, std::function<void (const WAL::Entity &)>> 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<TagComponent<Timer>, TimerComponent>();
_block << "timer: " << ret.front().get<TimerComponent>().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<WAL::Scene> scene, Node &node, int countPlayer)
{
std::string tmpAssets = node.getProperty("texture_path");
std::map<std::string, RAY::Color> map = {
{"red", RED},
{"blue", BLUE},
{"yellow", YELLOW},
{"green", GREEN}
};
std::map<std::string, int> 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<PositionComponent>(224 * (countPlayer + 1) + 200 * countPlayer, 1080 / 3, 0)
.addComponent<Drawable2DComponent, RAY2D::Rectangle>(RAY::Vector2(224 * (countPlayer + 1) + 200 * countPlayer, 1080 / 3), RAY::Vector2(200, 200), color);
auto &playerLogo = resumeScene->addEntity("player")
.addComponent<PositionComponent>(224 * (countPlayer + 1) + 200 * countPlayer, 1080 / 3, 0)
.addComponent<Drawable2DComponent, RAY::Texture>(tmpAssets.replace(tmpAssets.find("textures"), 8, "icons"));
auto &ready = resumeScene->addEntity("ready")
.addComponent<PositionComponent>(224 * (countPlayer + 1) + 200 * countPlayer, 1080 / 3, 0)
.addComponent<Drawable2DComponent, RAY::Texture>();
playerLogo.addComponent<ResumeLobbyComponent>(countPlayer, ready, playerTile, colors.at(colorStr));
}
void ParserYAML::_loadPlayers(std::shared_ptr<WAL::Scene> 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<WAL::Scene> 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<WAL::Scene> 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<WAL::Scene> 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<WAL::Scene> scene, Node &node)
{
auto childNode = node.getChildNodes("bonuses").at(0).getChildNodes();
for (auto child : childNode)
_loadBonus(scene, child);
}
void ParserYAML::load(std::shared_ptr<WAL::Scene> 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<MapGenerator::BlockType>(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::BonusType>(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<std::string, std::string> 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<int>(lineIndentLevel)) {
throw ParserError("Yaml only support 2 spaces as indent");
}
if (lineIndentLevel > static_cast<float>(indentLevel)) {
throw ParserError("Indent issue");
}
if (lineIndentLevel < static_cast<float>(indentLevel)) {
file.seekg(static_cast<size_t>(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<float>(nb / 2.);
}
}

View File

@@ -0,0 +1,135 @@
//
// Created by hbenjamin on 10/06/2021.
//
#pragma once
#include <Wal.hpp>
#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<WAL::Scene> 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<WAL::Scene> 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<WAL::Scene> scene, Node &node);
//!@param scene Scene to update
//!@brief load all players into scene
static void _loadPlayers(std::shared_ptr<WAL::Scene> scene, Node &node);
//!@param scene Scene to update
//!@brief load all blocks into scene
static void _loadBlocks(std::shared_ptr<WAL::Scene> scene, Node &node);
//!@param scene Scene to update
//!@brief load all blocks into scene
static void _loadBonuses(std::shared_ptr<WAL::Scene> scene, Node &node);
static std::string parseHeader(const std::string &line);
static std::pair<std::string ,std::string> 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<PlayerInfos> playersInfos;
//!@param scene Scene to update
//!@brief save yaml
static void save(std::shared_ptr<WAL::Scene> scene);
//!@param scene Scene to update
//!@brief load yaml
static void load(std::shared_ptr<WAL::Scene> scene);
//! @brief save file name
static const std::string fileName;
};
}

View File

@@ -3,11 +3,14 @@
#include "Runner.hpp"
#include <map>
#include "Component/Tag/TagComponent.hpp"
#include <Parser/ParserYaml.hpp>
#include <Component/Bonus/PlayerBonusComponent.hpp>
#include <Component/Renderer/Drawable2DComponent.hpp>
#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 <Drawables/Image.hpp>
#include "Component/Shaders/ShaderComponent.hpp"
#include "Component/Gravity/GravityComponent.hpp"
@@ -44,7 +48,6 @@ namespace BBM
scene->addEntity("background image")
.addComponent<Drawable3DComponent, RAY3D::Model>("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<PositionComponent>(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<PositionComponent>()
.addComponent<Drawable3DComponent, RAY3D::Model>("assets/player/player.iqm", true, std::nullopt, Vector3f(.75, .75, .75))
.addComponent<ScoreComponent>()
@@ -67,6 +70,7 @@ namespace BBM
.addComponent<ControllableComponent>(true)
.addComponent<TagComponent<BlowablePass>>()
.addComponent<TagComponent<Player>>()
.addComponent<SpeedComponent>()
.addComponent<AnimationsComponent>("assets/player/player.iqm", 3)
.addComponent<CollisionComponent>(BBM::Vector3f{0.25, 0, 0.25}, BBM::Vector3f{.6, 2, .6})
.addComponent<MovableComponent>()

View File

@@ -3,6 +3,8 @@
#include <Wal.hpp>
#include "Runner.hpp"
#include <map>
#include <Parser/ParserYaml.hpp>
#include <Component/Timer/TimerComponent.hpp>
#include "Component/Music/MusicComponent.hpp"
#include "Component/Sound/SoundComponent.hpp"
#include "Component/Controllable/ControllableComponent.hpp"
@@ -34,7 +36,7 @@ namespace BBM
.addComponent<PositionComponent>(1920 / 3, 180, 0)
.addComponent<Drawable2DComponent, RAY::Texture>("assets/logo_small.png");
auto &play = scene->addEntity("play button")
.addComponent<PositionComponent>(1920 / 2.5, 1080 - 540, 0)
.addComponent<PositionComponent>(1920 / 2.5, 1080 - 650, 0)
.addComponent<Drawable2DComponent, RAY::Texture>("assets/buttons/button_new_game.png")
.addComponent<OnIdleComponent>([](WAL::Entity &entity, WAL::Wal &)
{
@@ -52,8 +54,42 @@ namespace BBM
{
gameState.nextScene = BBM::GameState::SceneID::LobbyScene;
});
auto &resume = scene->addEntity("resume button")
.addComponent<PositionComponent>(1920 / 2.5, 1080 - 540, 0)
.addComponent<Drawable2DComponent, RAY::Texture>("assets/buttons/button_resume_game.png")
.addComponent<OnIdleComponent>([](WAL::Entity &entity, WAL::Wal &)
{
RAY::Texture *texture = dynamic_cast<RAY::Texture *>(entity.getComponent<Drawable2DComponent>().drawable.get());
texture->use("assets/buttons/button_resume_game.png");
})
.addComponent<OnHoverComponent>([](WAL::Entity &entity, WAL::Wal &)
{
RAY::Texture *texture = dynamic_cast<RAY::Texture *>(entity.getComponent<Drawable2DComponent>().drawable.get());
texture->use("assets/buttons/button_resume_game_hovered.png");
})
.addComponent<OnClickComponent>([](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<PositionComponent>(1920 / 5, 2 * 1080 / 4.25, 0)
.addComponent<TimerComponent>(3s, [](WAL::Entity &myEntity, WAL::Wal &) {
myEntity.scheduleDeletion();
})
.addComponent<Drawable2DComponent, RAY2D::Text>("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<PositionComponent>(1920 / 2.5, 1080 - 360, 0)
.addComponent<PositionComponent>(1920 / 2.5, 1080 - 430, 0)
.addComponent<Drawable2DComponent, RAY::Texture>("assets/buttons/button_settings.png")
.addComponent<OnIdleComponent>([](WAL::Entity &entity, WAL::Wal &)
{
@@ -72,7 +108,7 @@ namespace BBM
gameState.nextScene = BBM::GameState::SceneID::SettingsScene;
});
auto &exit = scene->addEntity("exit button")
.addComponent<PositionComponent>(1920 / 2.5, 1080 - 180, 0)
.addComponent<PositionComponent>(1920 / 2.5, 1080 - 320, 0)
.addComponent<Drawable2DComponent, RAY::Texture>("assets/buttons/button_exit.png")
.addComponent<OnIdleComponent>([](WAL::Entity &entity, WAL::Wal &)
{
@@ -109,8 +145,9 @@ namespace BBM
{
gameState.nextScene = BBM::GameState::SceneID::CreditScene;
});
play.getComponent<OnClickComponent>().setButtonLinks(nullptr, &settings);
settings.getComponent<OnClickComponent>().setButtonLinks(&play, &exit);
play.getComponent<OnClickComponent>().setButtonLinks(nullptr, &resume);
resume.getComponent<OnClickComponent>().setButtonLinks(&play, &settings);
settings.getComponent<OnClickComponent>().setButtonLinks(&resume, &exit);
exit.getComponent<OnClickComponent>().setButtonLinks(&settings, &credits, nullptr, &credits);
credits.getComponent<OnClickComponent>().setButtonLinks(&exit, nullptr, &exit);
return scene;

View File

@@ -7,6 +7,7 @@
#include "Component/Health/HealthComponent.hpp"
#include "Component/Timer/TimerComponent.hpp"
#include "Component/Tag/TagComponent.hpp"
#include <Parser/ParserYaml.hpp>
#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 <filesystem>
namespace RAY2D = RAY::Drawables::Drawables2D;
@@ -75,10 +77,33 @@ namespace BBM
entity.scheduleDeletion();
})
.addComponent<PositionComponent>(1920 / 2 - 2 * 30, 1080 / 2, 0)
.addComponent<TagComponent<"Timer">>()
.addComponent<TagComponent<Timer>>()
.addComponent<Drawable2DComponent, RAY2D::Text>("", 60, RAY::Vector2(), ORANGE);
gameState.nextScene = BBM::GameState::SceneID::GameScene;
});
auto &save = scene->addEntity("save & quit button")
.addComponent<PositionComponent>(1920 / 2.5, 1080 - 240, 0)
.addComponent<Drawable2DComponent, RAY::Texture>("assets/buttons/button_save.png")
.addComponent<OnIdleComponent>([](WAL::Entity &entity, WAL::Wal &)
{
RAY::Texture *texture = dynamic_cast<RAY::Texture *>(entity.getComponent<Drawable2DComponent>().drawable.get());
texture->use("assets/buttons/button_save.png");
})
.addComponent<OnHoverComponent>([](WAL::Entity &entity, WAL::Wal &)
{
RAY::Texture *texture = dynamic_cast<RAY::Texture *>(entity.getComponent<Drawable2DComponent>().drawable.get());
texture->use("assets/buttons/button_save_hovered.png");
})
.addComponent<OnClickComponent>([](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<CameraSystem>().hasEnded = false;
gameState.nextScene = BBM::GameState::SceneID::MainMenuScene;
});
auto &settings = scene->addEntity("settings button")
.addComponent<PositionComponent>(1920 / 2.5, 1080 - 360, 0)
.addComponent<Drawable2DComponent, RAY::Texture>("assets/buttons/button_settings.png")
@@ -99,7 +124,7 @@ namespace BBM
gameState.nextScene = BBM::GameState::SceneID::SettingsScene;
});
auto &exit = scene->addEntity("exit button")
.addComponent<PositionComponent>(1920 / 1.5, 1080 - 360, 0)
.addComponent<PositionComponent>(1920 / 1.55, 1080 - 360, 0)
.addComponent<Drawable2DComponent, RAY::Texture>("assets/buttons/button_exit.png")
.addComponent<OnIdleComponent>([](WAL::Entity &entity, WAL::Wal &)
{
@@ -120,9 +145,10 @@ namespace BBM
});
//needed material
//music
play.getComponent<OnClickComponent>().setButtonLinks(nullptr, nullptr, nullptr, &settings);
settings.getComponent<OnClickComponent>().setButtonLinks(nullptr, nullptr, &play, &exit);
exit.getComponent<OnClickComponent>().setButtonLinks(nullptr, nullptr, &settings, nullptr);
save.getComponent<OnClickComponent>().setButtonLinks(&settings);
play.getComponent<OnClickComponent>().setButtonLinks(nullptr, &save, nullptr, &settings);
settings.getComponent<OnClickComponent>().setButtonLinks(nullptr, &save, &play, &exit);
exit.getComponent<OnClickComponent>().setButtonLinks(nullptr, &save, &settings, nullptr);
return scene;
}
}

View File

@@ -0,0 +1,97 @@
//
// Created by hbenjamin on 15/06/2021.
//
#include <Wal.hpp>
#include "System/Movable/MovableSystem.hpp"
#include <Model/Model.hpp>
#include <Drawables/2D/Rectangle.hpp>
#include <Drawables/2D/Text.hpp>
#include <TraceLog.hpp>
#include "Component/Button/ButtonComponent.hpp"
#include "Component/Renderer/CameraComponent.hpp"
#include "Component/Renderer/Drawable2DComponent.hpp"
#include "Runner.hpp"
#include "Models/GameState.hpp"
#include <Component/Animator/AnimatorComponent.hpp>
#include <Component/Tag/TagComponent.hpp>
#include <Drawables/Texture.hpp>
#include <System/Lobby/ResumeLobbySystem.hpp>
#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<WAL::Scene> Runner::loadResumeLobbyScene()
{
static const std::map<SoundComponent::SoundIndex, std::string> sounds = {
{SoundComponent::JUMP, "assets/sounds/click.ogg"}
};
auto scene = std::make_shared<WAL::Scene>();
addMenuControl(*scene);
scene->addEntity("Control entity")
.addComponent<MusicComponent>("assets/musics/music_player_select.ogg")
.addComponent<SoundComponent>(sounds);
scene->addEntity("background")
.addComponent<PositionComponent>()
.addComponent<Drawable2DComponent, RAY::Texture>("assets/backgrounds/menu.png");
scene->addEntity("white background")
.addComponent<PositionComponent>(200, 300, 0)
.addComponent<Drawable2DComponent, RAY2D::Rectangle>(Vector2f(), Vector2f(1525, 325), RAY::Color(WHITE).setA(150));
scene->addEntity("lobby text")
.addComponent<PositionComponent>(1920 / 2.75, 100, 0)
.addComponent<Drawable2DComponent, RAY2D::Text>("Get Ready", 120, RAY::Vector2(), ORANGE);
auto &play = scene->addEntity("play button")
.addComponent<PositionComponent>(1920 / 2.5, 1080 - 180, 0)
.addComponent<Drawable2DComponent, RAY::Texture>("assets/buttons/button_new_game.png")
.addComponent<OnIdleComponent>([](WAL::Entity &entity, WAL::Wal &wal)
{
auto *texture = dynamic_cast<RAY::Texture *>(entity.getComponent<Drawable2DComponent>().drawable.get());
texture->use("assets/buttons/button_new_game.png");
})
.addComponent<OnHoverComponent>([](WAL::Entity &entity, WAL::Wal &wal)
{
auto *texture = dynamic_cast<RAY::Texture *>(entity.getComponent<Drawable2DComponent>().drawable.get());
texture->use("assets/buttons/button_new_game_hovered.png");
})
.addComponent<OnClickComponent>([](WAL::Entity &entity, WAL::Wal &wal)
{
if (Runner::gameState.currentScene != GameState::ResumeLobbyScene
|| !ResumeLobbySystem::playersAreReady(*wal.getScene()))
return;
ResumeLobbySystem::resumeToGame(wal);
})
.addComponent<TagComponent<"PlayButton">>();
auto &back = scene->addEntity("back to menu")
.addComponent<PositionComponent>(10, 1080 - 85, 0)
.addComponent<Drawable2DComponent, RAY::Texture>("assets/buttons/button_back.png")
.addComponent<OnClickComponent>([](WAL::Entity &entity, WAL::Wal &wal)
{
wal.getSystem<ResumeLobbySystem>().unloadLobbyFromResume();
gameState.nextScene = BBM::GameState::SceneID::MainMenuScene;
})
.addComponent<OnIdleComponent>([](WAL::Entity &entity, WAL::Wal &)
{
RAY::Texture *texture = dynamic_cast<RAY::Texture *>(entity.getComponent<Drawable2DComponent>().drawable.get());
texture->use("assets/buttons/button_back.png");
})
.addComponent<OnHoverComponent>([](WAL::Entity &entity, WAL::Wal &)
{
RAY::Texture *texture = dynamic_cast<RAY::Texture *>(entity.getComponent<Drawable2DComponent>().drawable.get());
texture->use("assets/buttons/button_back_hovered.png");
});
scene->addEntity("camera")
.addComponent<PositionComponent>(8, 20, 7)
.addComponent<CameraComponent>(Vector3f(8, 0, 8));
play.getComponent<OnClickComponent>().setButtonLinks(nullptr, &back, &back);
back.getComponent<OnClickComponent>().setButtonLinks(&play, nullptr, nullptr, &play);
return scene;
}
}

View File

@@ -33,6 +33,8 @@
#include "System/IAControllable/IAControllableSystem.hpp"
#include "System/MenuControllable/MenuControllableSystem.hpp"
#include <System/Bomb/BombSystem.hpp>
#include <Parser/ParserYaml.hpp>
#include <System/Lobby/ResumeLobbySystem.hpp>
#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<ShaderDrawable2DSystem>()
.addSystem<ScoreSystem>()
.addSystem<CameraSystem>()
.addSystem<ResumeLobbySystem>()
.addSystem<EndConditionSystem>()
.addSystem<MusicSystem>();
}
@@ -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<GameState>(Runner::updateState, Runner::gameState);
gameState.loadedScenes.clear();
return 0;
}
}

View File

@@ -5,6 +5,7 @@
#pragma once
#include "Models/GameState.hpp"
#include "Wal.hpp"
#include <chrono>
#include <map>
#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<WAL::Scene> loadLobbyScene();
//! @brief load all data related to resume lobby screen
static std::shared_ptr<WAL::Scene> 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.

View File

@@ -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<ControllableComponent, MovableComponent> &entity)
{
auto &controllable = entity.get<ControllableComponent>();
// todo check why the .get doesn't work
auto &speed = entity->getComponent<SpeedComponent>();
auto &movable = entity.get<MovableComponent>();
Vector2f move = controllable.move.normalized() * controllable.speed;
Vector2f move = controllable.move.normalized() * speed.speed;
movable.addForce(Vector3f(move.x, 0, move.y));
}

View File

@@ -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 <algorithm>
@@ -15,12 +16,13 @@
#include "Component/IAControllable/IAControllableComponent.hpp"
#include <Component/Position/PositionComponent.hpp>
#include <Component/Renderer/Drawable3DComponent.hpp>
#include <Drawables/2D/Text.hpp>
#include <Map/Map.hpp>
#include <Component/BombHolder/BombHolderComponent.hpp>
#include <Parser/ParserYaml.hpp>
#include <Drawables/2D/Text.hpp>
#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<ControllableComponent>().layout = layout;
}
void LobbySystem::switchToGame(WAL::Wal &wal)
void LobbySystem::createTile(std::shared_ptr<WAL::Scene> 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<LobbyComponent>()) {
if (lobby.layout == ControllableComponent::NONE)
continue;
auto &player = Runner::createPlayer(*scene);
_addController(player, lobby.layout);
player.getComponent<PositionComponent>().position = Vector3f(mapWidth * (playerCount % 2),
(Runner::hasHeights ? 1.01 : 0),
mapHeight * (!(playerCount % 3)));
auto *model = dynamic_cast<RAY3D::Model *>(player.getComponent<Drawable3DComponent>().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<PositionComponent>(x, y - 2, 0)
.addComponent<Drawable2DComponent, RAY2D::Rectangle>(x, y, 320, 248, _rayColors[lobby.color]);
scene->addEntity("player ui tile")
.addComponent<Drawable2DComponent, RAY2D::Rectangle>(x, y, 320, 248, RAY::Color(_rayColors[color]).setA(150));
scene->addEntity("player ui tile")
.addComponent<PositionComponent>(x, y, 0)
.addComponent<Drawable2DComponent, RAY::Texture>(texturePath);
scene->addEntity("player hide fireup")
scene->addEntity("player hide fireup")
.addComponent<PositionComponent>(x + 220, y + 35, 0)
.addComponent<Drawable2DComponent, RAY2D::Text>("", 20, x, y, WHITE)
.addComponent<StatComponent>([&player](Drawable2DComponent &drawble) {
@@ -248,7 +233,7 @@ namespace BBM
return;
text->setText(std::to_string(static_cast<int>(bonus->explosionRadius)));
});
scene->addEntity("player hide bombup")
scene->addEntity("player hide bombup")
.addComponent<PositionComponent>(x + 220, y + 77, 0)
.addComponent<Drawable2DComponent, RAY2D::Text>("", 20, x, y, WHITE)
.addComponent<StatComponent>([&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<PositionComponent>(x + 220, y + 122, 0)
.addComponent<Drawable2DComponent, RAY2D::Text>("", 20, x, y, WHITE)
.addComponent<StatComponent>([&player](Drawable2DComponent &drawble) {
const ControllableComponent *bonus = player.tryGetComponent<ControllableComponent>();
auto *speed = player.tryGetComponent<SpeedComponent>();
if (!bonus)
if (!speed)
return;
RAY2D::Text *text = dynamic_cast<RAY2D::Text *>(drawble.drawable.get());
if (!text)
return;
text->setText(std::to_string(static_cast<int>(bonus->speed * 100)));
text->setText(std::to_string(static_cast<int>(speed->speed * 100)));
});
scene->addEntity("player hide wall")
scene->addEntity("player hide wall")
.addComponent<PositionComponent>(x + 220, y + 161, 0)
.addComponent<Drawable2DComponent, RAY2D::Text>("", 20, x, y, WHITE)
.addComponent<StatComponent>([&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<LobbyComponent>()) {
if (lobby.layout == ControllableComponent::NONE)
continue;
auto &player = Runner::createPlayer(*scene);
player.getComponent<PositionComponent>().position = Vector3f(mapWidth * (playerCount % 2),
(Runner::hasHeights ? 1.01 : 0),
mapHeight * (!(playerCount % 3)));
auto *model = dynamic_cast<RAY3D::Model *>(player.getComponent<Drawable3DComponent>().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<LobbySystem>().unloadLobby();
}

View File

@@ -17,8 +17,6 @@ namespace BBM
class LobbySystem : public WAL::System<LobbyComponent, Drawable2DComponent>
{
private:
//! @brief Add a controller for the player.
static void _addController(WAL::Entity &player, ControllableComponent::Layout layout);
void _nextColor(WAL::ViewEntity<LobbyComponent, Drawable2DComponent> &entity);
@@ -29,6 +27,12 @@ namespace BBM
public:
static std::array<std::string, 4> 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<WAL::Scene> scene, WAL::Entity &player, int color, int playerCount);
//! @inherit
void onUpdate(WAL::ViewEntity<LobbyComponent, Drawable2DComponent> &entity, std::chrono::nanoseconds dtime) override;

View File

@@ -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 <algorithm>
#include <Runner/Runner.hpp>
#include <Component/Keyboard/KeyboardComponent.hpp>
#include <Component/Gamepad/GamepadComponent.hpp>
#include "Component/IAControllable/IAControllableComponent.hpp"
#include <Component/Position/PositionComponent.hpp>
#include <Component/Renderer/Drawable3DComponent.hpp>
#include <Map/Map.hpp>
#include <Component/BombHolder/BombHolderComponent.hpp>
#include <Parser/ParserYaml.hpp>
#include <Drawables/2D/Text.hpp>
#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<ResumeLobbyComponent, Drawable2DComponent> &entity, std::chrono::nanoseconds dtime)
{
auto &lobby = entity.get<ResumeLobbyComponent>();
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<ControllableComponent>()) {
auto &controller = ctrl;
if (controller.bomb) {
if (std::any_of(this->getView().begin(), this->getView().end(), [&controller](WAL::ViewEntity<ResumeLobbyComponent, Drawable2DComponent> &view) {
return view.get<ResumeLobbyComponent>().layout == controller.layout;
}))
return;
lobby.ready = true;
lobby.lastInput = lastTick;
lobby.layout = controller.layout;
controller.bomb = false;
this->_wal.getSystem<MenuControllableSystem>().now = lastTick;
auto *texture = dynamic_cast<RAY::Texture *>(lobby.readyButton.getComponent<Drawable2DComponent>().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<TagComponent<"PlayButton">, Drawable2DComponent>();
if (view.size() == 0)
return;
auto *texture = dynamic_cast<RAY::Texture *>(view.front().get<Drawable2DComponent>().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<ResumeLobbyComponent>();
return std::all_of(lobby.begin(), lobby.end(), [](WAL::ViewEntity<ResumeLobbyComponent> &entity) {
auto &lobbyPlayer = entity.get<ResumeLobbyComponent>();
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<ResumeLobbyComponent>()) {
if (lobby.layout == ControllableComponent::NONE)
continue;
auto &player = Runner::createPlayer(*scene);
player.setName(ParserYAML::playersInfos[countPlayer].name);
auto *position = player.tryGetComponent<PositionComponent>();
auto *bombHolder = player.tryGetComponent<BombHolderComponent>();
auto *model = player.tryGetComponent<Drawable3DComponent>();
auto *speed = player.tryGetComponent<SpeedComponent>();
if (position && bombHolder && model && speed) {
dynamic_cast<RAY3D::Model *>(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<ResumeLobbySystem>().unloadLobbyFromResume();
}
void ResumeLobbySystem::unloadLobbyFromResume()
{
for (auto &entity : this->getView()) {
entity->scheduleDeletion();
}
}
}

View File

@@ -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 <array>
#include <string>
namespace BBM
{
//! @brief A system to handle Health entities.
class ResumeLobbySystem : public WAL::System<ResumeLobbyComponent, Drawable2DComponent>
{
public:
//! @brief Add a controller for the player when we resume a game
static void resumeToGame(WAL::Wal &wal);
//! @inherit
void onUpdate(WAL::ViewEntity<ResumeLobbyComponent, Drawable2DComponent> &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;
};
}

View File

@@ -32,12 +32,12 @@ namespace BBM
.addComponent<PositionComponent>(1920 / 2 - 2 * 30 - 20, 28, 0)
.addComponent<Drawable2DComponent, RAY2D::Rectangle>(Vector2f(), Vector2f(150, 60), RAY::Color(BLACK).setA(150));
this->_wal.getScene()->scheduleNewEntity("Timer")
.addComponent<TimerComponent>(std::chrono::minutes (3), [](WAL::Entity &, WAL::Wal &engine) {
.addComponent<TimerComponent>(Runner::timerDelay, [](WAL::Entity &, WAL::Wal &engine) {
engine.getSystem<CameraSystem>().hasEnded = false;
Runner::gameState.nextScene = GameState::ScoreScene;
})
.addComponent<TagComponent<"Timer">>()
.addComponent<PositionComponent>(1920 / 2 - 2 * 30, 30, 0)
.addComponent<TagComponent<Timer>>()
.addComponent<Drawable2DComponent, RAY2D::Text>("", 60, RAY::Vector2(), ORANGE);
for (WAL::Entity &player : this->_wal.getScene()->view<TagComponent<Player>>())
player.getComponent<ControllableComponent>().disabled = false;

View File

@@ -15,7 +15,7 @@ namespace BBM
: System(wal)
{}
void TimerUISystem::onUpdate(WAL::ViewEntity<TimerComponent, Drawable2DComponent> &entity, std::chrono::nanoseconds dtime)
void TimerUISystem::onUpdate(WAL::ViewEntity<TimerComponent, Drawable2DComponent, TagComponent<Timer>> &entity, std::chrono::nanoseconds dtime)
{
auto &timer = entity.get<TimerComponent>();
RAY2D::Text *text = dynamic_cast<RAY2D::Text *>(entity.get<Drawable2DComponent>().drawable.get());

View File

@@ -8,22 +8,23 @@
#include <Wal.hpp>
#include <Component/Timer/TimerComponent.hpp>
#include "Component/Renderer/Drawable2DComponent.hpp"
#include "Component/Tag/TagComponent.hpp"
namespace BBM
{
class TimerUISystem : public WAL::System<TimerComponent, Drawable2DComponent>
class TimerUISystem : public WAL::System<TimerComponent, Drawable2DComponent, TagComponent<Timer>>
{
public:
//! @inherit
void onUpdate(WAL::ViewEntity<TimerComponent, Drawable2DComponent> &entity, std::chrono::nanoseconds dtime) override;
void onUpdate(WAL::ViewEntity<TimerComponent, Drawable2DComponent, TagComponent<Timer>> &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;
};
}

102
sources/Utils/Utils.cpp Normal file
View File

@@ -0,0 +1,102 @@
//
// Created by cbihan on 17/06/2021.
//
#include <algorithm>
#include <cctype>
#include <locale>
#include <string>
#include <regex>
#include <ios>
#include <sstream>
#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<int>(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<std::string> Utils::splitStr(const std::string &str, char delim)
{
std::vector<std::string> 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();
}
}

46
sources/Utils/Utils.hpp Normal file
View File

@@ -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<std::string> splitStr(const std::string &str, char delim);
};
}

View File

@@ -12,6 +12,7 @@
#include "System/Movable/MovableSystem.hpp"
#include <Component/Controllable/ControllableComponent.hpp>
#include <Component/Movable/MovableComponent.hpp>
#include <Component/Speed/SpeedComponent.hpp>
using namespace WAL;
using namespace BBM;
@@ -24,6 +25,7 @@ TEST_CASE("Move test", "[Component][System]")
wal.getScene()->addEntity("player")
.addComponent<ControllableComponent>()
.addComponent<MovableComponent>()
.addComponent<SpeedComponent>()
.addComponent<PositionComponent>();
Entity &entity = wal.getScene()->getEntities().front();

View File

@@ -61,6 +61,7 @@ TEST_CASE("View add entity", "[View]")
.addComponent<PositionComponent>();
scene.applyChanges();
REQUIRE(scene.view<PositionComponent>().size() == 2);
(void)entity;
}
TEST_CASE("View remove entity", "[View]")
@@ -75,6 +76,7 @@ TEST_CASE("View remove entity", "[View]")
REQUIRE(scene.view<PositionComponent>().size() == 0);
for (auto &it : scene.view<PositionComponent>())
REQUIRE(false);
(void)scene;
}
TEST_CASE("View cache switch", "[View]")