diff --git a/assets/snake/apple.ascii b/assets/snake/apple.ascii new file mode 100644 index 0000000..60a89ed --- /dev/null +++ b/assets/snake/apple.ascii @@ -0,0 +1 @@ +O \ No newline at end of file diff --git a/assets/snake/apple.png b/assets/snake/apple.png new file mode 100644 index 0000000..1bb1a63 Binary files /dev/null and b/assets/snake/apple.png differ diff --git a/assets/snake/head.ascii b/assets/snake/head.ascii new file mode 100644 index 0000000..71c4a23 --- /dev/null +++ b/assets/snake/head.ascii @@ -0,0 +1 @@ +@@@@ \ No newline at end of file diff --git a/assets/snake/head.png b/assets/snake/head.png new file mode 100644 index 0000000..f369b2a Binary files /dev/null and b/assets/snake/head.png differ diff --git a/assets/snake/tail.ascii b/assets/snake/tail.ascii new file mode 100644 index 0000000..2afd973 --- /dev/null +++ b/assets/snake/tail.ascii @@ -0,0 +1,3 @@ +║═ +~~~~ +╝╚╗╔ \ No newline at end of file diff --git a/assets/snake/tail.png b/assets/snake/tail.png new file mode 100644 index 0000000..3910682 Binary files /dev/null and b/assets/snake/tail.png differ diff --git a/assets/snake/wall.ascii b/assets/snake/wall.ascii new file mode 100644 index 0000000..4287ca8 --- /dev/null +++ b/assets/snake/wall.ascii @@ -0,0 +1 @@ +# \ No newline at end of file diff --git a/assets/snake/wall.png b/assets/snake/wall.png new file mode 100644 index 0000000..e0f8498 Binary files /dev/null and b/assets/snake/wall.png differ diff --git a/core/src/Core.cpp b/core/src/Core.cpp index 48f4526..6cb8600 100644 --- a/core/src/Core.cpp +++ b/core/src/Core.cpp @@ -391,7 +391,7 @@ void Core::run() this->_initWindow(); while (this->_window->isOpen()) { auto currentTime = std::chrono::high_resolution_clock::now(); - auto deltaTime = std::chrono::duration_cast(previousTime - currentTime); + auto deltaTime = std::chrono::duration_cast(currentTime - previousTime); previousTime = currentTime; this->_game->compute(deltaTime); diff --git a/core/src/loader/Loader.cpp b/core/src/loader/Loader.cpp index 6ad23e5..09c0e4a 100644 --- a/core/src/loader/Loader.cpp +++ b/core/src/loader/Loader.cpp @@ -11,7 +11,10 @@ Loader::Loader() {} -Loader::~Loader() {} +Loader::~Loader() { + this->_gamesLibraries.clear(); + this->_graphicsLibraries.clear(); +} shared::types::LibraryType Loader::_getLibraryGetter(const std::string &filepath, std::shared_ptr dlLoader) { shared::types::LibraryTypeGetter getter = nullptr; diff --git a/games/common/CMakeLists.txt b/games/common/CMakeLists.txt new file mode 100644 index 0000000..87ef00d --- /dev/null +++ b/games/common/CMakeLists.txt @@ -0,0 +1,17 @@ +target_sources(${PROJECT_NAME} PUBLIC + game/AGame.cpp + game/AGame.hpp + entity/AEntity.cpp + entity/AEntity.hpp + components/AComponent.cpp + components/AComponent.hpp + components/PositionableComponent.cpp + components/PositionableComponent.hpp + components/ADisplayableComponent.cpp + components/ADisplayableComponent.hpp + components/CollidableComponent.hpp + components/CollidableComponent.cpp + components/TextureComponent.hpp + components/TextureComponent.cpp +) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/../..) diff --git a/games/common/components/AComponent.cpp b/games/common/components/AComponent.cpp new file mode 100644 index 0000000..792cd38 --- /dev/null +++ b/games/common/components/AComponent.cpp @@ -0,0 +1,24 @@ +/* +** EPITECH PROJECT, 2024 +** AComponent.cpp +** File description: +** AComponent class +*/ + +#include +#include "AComponent.hpp" + +using namespace arcade::games::common::components; + +AComponent::AComponent(shared::games::components::ComponentType type, shared::games::entity::IEntity &entity) : _parent( + entity) { + this->_type = type; +} + +const shared::games::components::ComponentType AComponent::getType() const noexcept { + return this->_type; +} + +const shared::games::entity::IEntity &AComponent::getEntity() noexcept { + return this->_parent; +} diff --git a/games/common/components/AComponent.hpp b/games/common/components/AComponent.hpp new file mode 100644 index 0000000..93d356d --- /dev/null +++ b/games/common/components/AComponent.hpp @@ -0,0 +1,44 @@ +/* +** EPITECH PROJECT, 2024 +** AComponent.hpp +** File description: +** AComponent class +*/ + +#pragma once + +#include "shared/games/components/IComponent.hpp" + +namespace arcade::games::common::components { + class AComponent; +} + +class arcade::games::common::components::AComponent : public virtual shared::games::components::IComponent { +public: + ~AComponent() override = default; + + /** + * @brief Get the type of the component + * + * @return Type of the component + */ + const shared::games::components::ComponentType getType() const noexcept override; + + /** + * @brief Get the parent entity of the component + * + * @return Entity of the component + */ + const shared::games::entity::IEntity &getEntity() noexcept override; + +protected: + /** + * @brief Create a component + * + * @param entity Entity parent of the component + */ + explicit AComponent(shared::games::components::ComponentType type, shared::games::entity::IEntity &entity); + + shared::games::entity::IEntity &_parent; + shared::games::components::ComponentType _type; +}; diff --git a/games/common/components/ADisplayableComponent.cpp b/games/common/components/ADisplayableComponent.cpp new file mode 100644 index 0000000..b0e7c75 --- /dev/null +++ b/games/common/components/ADisplayableComponent.cpp @@ -0,0 +1,30 @@ +/* +** EPITECH PROJECT, 2024 +** ADisplayableComponent.cpp +** File description: +** ADisplayableComponent class +*/ + +#include "ADisplayableComponent.hpp" + +using namespace arcade::games::common::components; +using namespace shared::games; + +ADisplayableComponent::ADisplayableComponent(entity::IEntity &entity, Vector2u size, unsigned int zindex) + : PositionableComponent(entity), + _size(size), _zindex(zindex) { +} + +Vector2u &ADisplayableComponent::getSize() noexcept { + return this->_size; +} + +unsigned int &ADisplayableComponent::getZIndex() noexcept { + return this->_zindex; +} + +void ADisplayableComponent::onMousePress(std::shared_ptr ctx) {} + +void ADisplayableComponent::onMouseHover(std::shared_ptr ctx) {} + +void ADisplayableComponent::onMouseRelease(std::shared_ptr ctx) {} diff --git a/games/common/components/ADisplayableComponent.hpp b/games/common/components/ADisplayableComponent.hpp new file mode 100644 index 0000000..a333f3c --- /dev/null +++ b/games/common/components/ADisplayableComponent.hpp @@ -0,0 +1,62 @@ +/* +** EPITECH PROJECT, 2024 +** ADisplayableComponent.hpp +** File description: +** ADisplayableComponent class +*/ + +#pragma once + +#include "shared/games/components/IDisplayableComponent.hpp" +#include "PositionableComponent.hpp" + +namespace arcade::games::common::components { + class ADisplayableComponent; +} + +class arcade::games::common::components::ADisplayableComponent : public virtual shared::games::components::IDisplayableComponent, public PositionableComponent { +public: + ~ADisplayableComponent() override = default; + + /** + * @brief Get size of the entity (tiles) + * + */ + Vector2u &getSize() noexcept override; + + /** + * @brief Get Z index that is usefull for display prioroty + * + */ + unsigned int &getZIndex() noexcept override; + + /** + * @brief On click event handler for the entity + * @param ctx Context of the game + */ + void onMousePress(std::shared_ptr ctx) override; + + /** + * @brief On release event handler for the entity + * @param ctx Context of the game + */ + void onMouseRelease(std::shared_ptr ctx) override; + + /** + * @brief On hover event handler for the entity + * @param ctx Context of the game + */ + void onMouseHover(std::shared_ptr ctx) override; + +protected: + /** + * Create a displayable component + * @param entity + * @param size + * @param zindex + */ + explicit ADisplayableComponent(shared::games::entity::IEntity &entity, Vector2u size, unsigned int zindex); + + Vector2u _size; + unsigned int _zindex; +}; diff --git a/games/common/components/CollidableComponent.cpp b/games/common/components/CollidableComponent.cpp new file mode 100644 index 0000000..65c0d81 --- /dev/null +++ b/games/common/components/CollidableComponent.cpp @@ -0,0 +1,21 @@ +/* +** EPITECH PROJECT, 2024 +** CollidableComponent.cpp +** File description: +** CollidableComponent class +*/ + +#include "CollidableComponent.hpp" +#include + +using namespace arcade::games::common::components; + +CollidableComponent::CollidableComponent(shared::games::entity::IEntity &entity, onCollideFunction function) : PositionableComponent(entity), _collideFunction(function) { + this->_type = shared::games::components::COLLIDABLE; +} + +void CollidableComponent::onCollide(std::shared_ptr ctx, + std::shared_ptr target) { + if (this->_collideFunction) + this->_collideFunction(ctx, target); +} diff --git a/games/common/components/CollidableComponent.hpp b/games/common/components/CollidableComponent.hpp new file mode 100644 index 0000000..82574c5 --- /dev/null +++ b/games/common/components/CollidableComponent.hpp @@ -0,0 +1,38 @@ +/* +** EPITECH PROJECT, 2024 +** CollidableComponent.hpp +** File description: +** CollidableComponent class +*/ + +#pragma once + +#include "shared/games/components/ICollidableComponent.hpp" +#include "PositionableComponent.hpp" + +namespace arcade::games::common::components { + class CollidableComponent; +} + +class arcade::games::common::components::CollidableComponent : public virtual shared::games::components::ICollidableComponent, public PositionableComponent { +public: + typedef void (*onCollideFunction)(std::shared_ptr ctx, std::shared_ptr target); + + ~CollidableComponent() override = default; + + /** + * @brief Create a position component + * @param entity + */ + explicit CollidableComponent(shared::games::entity::IEntity &entity, onCollideFunction function); + + /** + * @brief On collide event handler for the component + * @param ctx Context of the game + * @param target Target entity + */ + void onCollide(std::shared_ptr ctx, std::shared_ptr target) override; + +protected: + onCollideFunction _collideFunction; +}; diff --git a/games/common/components/PositionableComponent.cpp b/games/common/components/PositionableComponent.cpp new file mode 100644 index 0000000..2dd5be7 --- /dev/null +++ b/games/common/components/PositionableComponent.cpp @@ -0,0 +1,23 @@ +/* +** EPITECH PROJECT, 2024 +** PositionableComponent.cpp +** File description: +** PositionableComponent class +*/ + +#include "PositionableComponent.hpp" + +using namespace arcade::games::common::components; +using namespace shared::games::components; + +PositionableComponent::PositionableComponent(shared::games::entity::IEntity &entity) : AComponent(POSITIONABLE, entity), + _position(0, 0), _size(1, 1) { +} + +shared::types::Vector2i &PositionableComponent::getPosition() noexcept { + return this->_position; +} + +shared::types::Vector2u &PositionableComponent::getSize() noexcept { + return this->_size; +} diff --git a/games/common/components/PositionableComponent.hpp b/games/common/components/PositionableComponent.hpp new file mode 100644 index 0000000..557e4f5 --- /dev/null +++ b/games/common/components/PositionableComponent.hpp @@ -0,0 +1,43 @@ +/* +** EPITECH PROJECT, 2024 +** PositionableComponent.hpp +** File description: +** PositionableComponent class +*/ + +#pragma once + +#include "shared/games/components/IPositionableComponent.hpp" +#include "AComponent.hpp" + +namespace arcade::games::common::components { + class PositionableComponent; +} + +class arcade::games::common::components::PositionableComponent + : public AComponent, public virtual shared::games::components::IPositionableComponent { +public: + ~PositionableComponent() override = default; + + /** + * @brief Create a position component + * @param entity + */ + explicit PositionableComponent(shared::games::entity::IEntity &entity); + + /** + * @brief Get position of the entity (tiles) + * + */ + shared::types::Vector2i &getPosition() noexcept override; + + /** + * @brief Get size of the entity (tiles) + * + */ + shared::types::Vector2u &getSize() noexcept override; + +protected: + shared::types::Vector2i _position; + shared::types::Vector2u _size; +}; diff --git a/games/common/components/TextureComponent.cpp b/games/common/components/TextureComponent.cpp new file mode 100644 index 0000000..20a79cf --- /dev/null +++ b/games/common/components/TextureComponent.cpp @@ -0,0 +1,22 @@ +/* +** EPITECH PROJECT, 2024 +** TextureComponent.cpp +** File description: +** TextureComponent class +*/ + +#include "TextureComponent.hpp" + +using namespace arcade::games::common::components; + +TextureComponent::TextureComponent(shared::games::entity::IEntity &entity, + Vector2u size, unsigned int zindex, + shared::games::components::TextureProps &props) + : ADisplayableComponent(entity, size, zindex), + _textureProps(props) { + this->_type = shared::games::components::TEXTURE; +} + +shared::games::components::TextureProps &TextureComponent::getTextureProps() noexcept { + return this->_textureProps; +} diff --git a/games/common/components/TextureComponent.hpp b/games/common/components/TextureComponent.hpp new file mode 100644 index 0000000..647cd3b --- /dev/null +++ b/games/common/components/TextureComponent.hpp @@ -0,0 +1,33 @@ +/* +** EPITECH PROJECT, 2024 +** TextureComponent.hpp +** File description: +** TextureComponent class +*/ + +#pragma once + +#include "shared/games/components/ITextureComponent.hpp" +#include "ADisplayableComponent.hpp" + +namespace arcade::games::common::components { + class TextureComponent; +} + +class arcade::games::common::components::TextureComponent + : public virtual shared::games::components::ITextureComponent, public ADisplayableComponent { +public: + ~TextureComponent() override = default; + + explicit TextureComponent(shared::games::entity::IEntity &entity, Vector2u size, unsigned int zindex, + shared::games::components::TextureProps &props); + + /** + * @brief Get texture properties + * + */ + shared::games::components::TextureProps &getTextureProps() noexcept override; + +protected: + shared::games::components::TextureProps _textureProps; +}; diff --git a/games/common/entity/AEntity.cpp b/games/common/entity/AEntity.cpp new file mode 100644 index 0000000..8c73476 --- /dev/null +++ b/games/common/entity/AEntity.cpp @@ -0,0 +1,15 @@ +/* +** EPITECH PROJECT, 2024 +** AEntity.cpp +** File description: +** AEntity class +*/ + +#include "AEntity.hpp" + +using namespace arcade::games; +using namespace shared::games; + +const components::ComponentsMap &arcade::games::common::AEntity::getComponents() const noexcept { + return this->_components; +} diff --git a/games/common/entity/AEntity.hpp b/games/common/entity/AEntity.hpp new file mode 100644 index 0000000..3bc512d --- /dev/null +++ b/games/common/entity/AEntity.hpp @@ -0,0 +1,34 @@ +/* +** EPITECH PROJECT, 2024 +** AEntity.hpp +** File description: +** AEntity class +*/ + +#pragma once + +#include "shared/games/IEntity.hpp" + +namespace arcade::games::common { + class AEntity; +} + +class arcade::games::common::AEntity : public shared::games::entity::IEntity { +public: + ~AEntity() override = default; + + /** + * @brief Get the components of the entity + * + * @return Components of the entity + */ + const shared::games::components::ComponentsMap &getComponents() const noexcept override; + +protected: + /** + * @brief Create a entity + */ + explicit AEntity() = default; + + shared::games::components::ComponentsMap _components; +}; diff --git a/games/common/game/AGame.cpp b/games/common/game/AGame.cpp new file mode 100644 index 0000000..f8999bf --- /dev/null +++ b/games/common/game/AGame.cpp @@ -0,0 +1,32 @@ +/* +** EPITECH PROJECT, 2024 +** arcade +** File description: +** AGame.cpp +*/ + +#include "AGame.hpp" + +#include + +using namespace arcade::games; + +common::AGame::AGame(shared::types::Vector2u size, unsigned int fps) : _size(size), _fps(fps) { + this->_entities = {}; +} + +const shared::games::entity::EntitiesMap &common::AGame::getEntities() const { + return this->_entities; +} + +const shared::types::Vector2u common::AGame::getSize() const noexcept { + return this->_size; +} + +const unsigned int common::AGame::getFps() const noexcept { + return this->_fps; +} + +void common::AGame::_registerEntity(shared::games::entity::EntityPtr entity) { + this->_entities.push_back(std::move(entity)); +} diff --git a/games/common/game/AGame.hpp b/games/common/game/AGame.hpp new file mode 100644 index 0000000..db2dcd0 --- /dev/null +++ b/games/common/game/AGame.hpp @@ -0,0 +1,75 @@ +/* +** EPITECH PROJECT, 2024 +** arcade +** File description: +** AGame.hpp +*/ + +#pragma once + +#include +#include "shared/games/IGame.hpp" +#include "shared/types/Vector.hpp" + +namespace arcade::games::common { + class AGame; +} + +class arcade::games::common::AGame : public virtual shared::games::IGame { +public: + ~AGame() override = default; + + /** + * @brief Get map of entity + * + * @return Map of entity + */ + const shared::games::entity::EntitiesMap &getEntities() const override; + + /** + * @brief Get game size (in tiles) + * + * @return Size of the game + */ + const shared::types::Vector2u getSize() const noexcept override; + + /** + * @brief Get fps of the game + * + * @return The number of frame per seconds of the game + */ + const unsigned int getFps() const noexcept override; + +protected: + /** + * @brief Construct a new AGame object + * + * @param size Size of the game + * @param fps Fps of the game + */ + explicit AGame(shared::types::Vector2u size, unsigned int fps); + + /** + * @brief List of game entity + */ + shared::games::entity::EntitiesMap _entities; + + /** + * @brief Game size + */ + shared::types::Vector2u _size; + + /** + * @brief Game frame per seconds + */ + unsigned int _fps; + + /** + * @brief Add an entity to the game + * + * @param entity Entity to add + * @return Id of the entity + * @warning This method is moving ownership of the entity + */ + void _registerEntity(shared::games::entity::EntityPtr entity); +}; diff --git a/games/snake/CMakeLists.txt b/games/snake/CMakeLists.txt index 619422c..1c99fa6 100644 --- a/games/snake/CMakeLists.txt +++ b/games/snake/CMakeLists.txt @@ -1,5 +1,24 @@ -add_library(snake SHARED +project(snake) +add_library(${PROJECT_NAME} SHARED export.cpp + src/SnakeGameProvider.cpp + src/SnakeGame.cpp + src/entities/snake/Snake.cpp + src/entities/snake/Snake.hpp + src/entities/snake/TailEntity.cpp + src/entities/snake/TailEntity.hpp + src/entities/snake/HeadEntity.cpp + src/entities/snake/HeadEntity.hpp + src/entities/snake/components/HeadKeyboardComponent.cpp + src/entities/snake/components/HeadKeyboardComponent.hpp + src/entities/wall/WallEntity.cpp + src/entities/wall/WallEntity.hpp + src/entities/background/BackgroundEntity.cpp + src/entities/background/BackgroundEntity.hpp + src/entities/apple/AppleEntity.cpp + src/entities/apple/AppleEntity.hpp ) -target_include_directories(snake PUBLIC ${CMAKE_CURRENT_LIST_DIR}/src) -target_include_directories(snake PUBLIC ${CMAKE_CURRENT_LIST_DIR}/../..) +add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../common PRIVATE) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/src) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/../..) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_LIST_DIR}/..) diff --git a/games/snake/export.cpp b/games/snake/export.cpp index f2f7054..3be0c08 100644 --- a/games/snake/export.cpp +++ b/games/snake/export.cpp @@ -5,17 +5,18 @@ ** export */ -#include "shared/games/IGameProvider.hpp" +#include "SnakeGameProvider.hpp" #include "shared/types/Libraries.hpp" +using namespace shared::games; +using namespace shared::types; + extern "C" { - shared::types::LibraryType SHARED_LIBRARY_TYPE_GETTER_NAME(void) - { - return shared::types::LibraryType::GAME; - } +LibraryType SHARED_LIBRARY_TYPE_GETTER_NAME(void) { + return LibraryType::GAME; +} -/* std::shared_ptr SHARED_GAME_PROVIDER_LOADER_NAME(void) - { - return std::make_shared(...) - }*/ +IGameProvider *SHARED_GAME_PROVIDER_GETTER_NAME(void) { + return new arcade::games::snake::SnakeGameProvider(); +} } diff --git a/games/snake/src/SnakeGame.cpp b/games/snake/src/SnakeGame.cpp new file mode 100644 index 0000000..0bd6501 --- /dev/null +++ b/games/snake/src/SnakeGame.cpp @@ -0,0 +1,104 @@ +/* +** EPITECH PROJECT, 2024 +** arcade +** File description: +** SnakeGame.cpp +*/ + +#include +#include "SnakeGame.hpp" +#include "entities/wall/WallEntity.hpp" +#include "entities/background/BackgroundEntity.hpp" +#include "entities/apple/AppleEntity.hpp" +#include "common/components/TextureComponent.hpp" +#include "entities/snake/components/HeadKeyboardComponent.hpp" + +using namespace arcade::games; + +const shared::games::GameManifest snake::SnakeGame::manifest = { + .name = "snake", + .description = "The snake original game", + .version = "1.0.0", + .authors = { + { + .name = "TekMath", + .email = "matheo.coquet@epitech.eu", + .website = "https://github.com/tekmath" + }, + { + .name = "Flavien Chenu", + .email = "flavienchenu@epitech.eu", + .website = "https://github.com/flavien-chenu" + } + } +}; + +snake::SnakeGame::SnakeGame() : common::AGame(Vector2u(20, 20), 60) { + this->_snake = std::make_unique(4); + this->_registerEntity(this->_snake->head); + + for (auto &tail: this->_snake->getTails()) { + this->_registerEntity(tail); + } + + this->_registerEntity(std::make_unique(this->getSize())); + this->_registerEntity(std::make_unique(this->getSize())); + + this->_apple = std::make_unique(this->getSize()); + this->_registerEntity(this->_apple); + + this->_clock = std::chrono::milliseconds(0); + this->_looseGame = false; + this->speedTime = 100; + this->speedBoost = 0; +} + +const shared::games::GameManifest &snake::SnakeGame::getManifest() const noexcept { + return SnakeGame::manifest; +} + +void snake::SnakeGame::compute(shared::games::DeltaTime dt) { + unsigned int speed = this->speedTime; + this->_clock += dt; + + if (this->_looseGame) { + return this->_loose(); + } + if (this->speedBoost > 0) { + speed = 0; + this->speedBoost -= 1; + } + if (this->_clock > std::chrono::milliseconds(speed) + this->_snake->lastMove) { + this->_snake->lastMove = this->_clock; + this->_snake->forward(); + } +} + +void snake::SnakeGame::_loose() { + this->_clock = std::chrono::milliseconds(0); + this->_snake->lastMove = std::chrono::milliseconds(900); + + this->_entities.erase(std::remove_if(this->_entities.begin(), this->_entities.end(), [](const shared::games::entity::EntityPtr& entity) { + auto tail = std::dynamic_pointer_cast(entity); + return !(tail == nullptr); + }), this->_entities.end()); + + this->_snake->reset(); + for (auto &tail: this->_snake->getTails()) { + this->_registerEntity(tail); + } + + this->_looseGame = false; +} + +void snake::SnakeGame::setLooseGame(bool state) { + this->_looseGame = state; +} + +void snake::SnakeGame::addNewPoint() { + std::shared_ptr newTail = this->_snake->addTail(); + + this->_registerEntity(newTail); + this->_apple->generateApple(); + this->speedTime -= 2; +} diff --git a/games/snake/src/SnakeGame.hpp b/games/snake/src/SnakeGame.hpp new file mode 100644 index 0000000..24d6e3c --- /dev/null +++ b/games/snake/src/SnakeGame.hpp @@ -0,0 +1,70 @@ +/* +** EPITECH PROJECT, 2024 +** arcade +** File description: +** SnakeGame.hpp +*/ + +#pragma once + +#include "common/game/AGame.hpp" +#include "games/snake/src/entities/snake/Snake.hpp" +#include "entities/apple/AppleEntity.hpp" + +namespace arcade::games::snake { + class SnakeGame; +} + +class arcade::games::snake::SnakeGame : public arcade::games::common::AGame { +public: + SnakeGame(); + + ~SnakeGame() override = default; + + /** + * @brief Game manifest + * + */ + static const shared::games::GameManifest manifest; + + /** + * @brief Get the manifest object + * + * @return const shared::games::GameManifest& + */ + const shared::games::GameManifest &getManifest() const noexcept override; + + /** + * @brief Allow game possible actions + * + * @param dt Delta time from last frame + */ + void compute(shared::games::DeltaTime dt) override; + + /** + * @brief Set loose game state + * + * @param state If the game is loose or not + */ + void setLooseGame(bool state); + + /** + * @brief Add new point to player and re-generate an apple + */ + void addNewPoint(); + + unsigned int speedTime; + + unsigned int speedBoost; + +protected: + /** + * @brief Execute the process of the end of the game when the player _loose + */ + void _loose(); + + std::unique_ptr _snake; + std::shared_ptr _apple; + shared::games::DeltaTime _clock; + bool _looseGame; +}; diff --git a/games/snake/src/SnakeGameProvider.cpp b/games/snake/src/SnakeGameProvider.cpp new file mode 100644 index 0000000..33c7b54 --- /dev/null +++ b/games/snake/src/SnakeGameProvider.cpp @@ -0,0 +1,19 @@ +/* +** EPITECH PROJECT, 2024 +** arcade +** File description: +** SnakeGameProvider.cpp +*/ + +#include "SnakeGame.hpp" +#include "SnakeGameProvider.hpp" + +using namespace arcade::games; + +const shared::games::GameManifest &snake::SnakeGameProvider::getManifest() const noexcept { + return SnakeGame::manifest; +} + +std::shared_ptr snake::SnakeGameProvider::createInstance() { + return std::make_shared(); +} diff --git a/games/snake/src/SnakeGameProvider.hpp b/games/snake/src/SnakeGameProvider.hpp new file mode 100644 index 0000000..7523c7f --- /dev/null +++ b/games/snake/src/SnakeGameProvider.hpp @@ -0,0 +1,35 @@ +/* +** EPITECH PROJECT, 2024 +** arcade +** File description: +** SnakeGameProvider.hpp +*/ + +#pragma once + +#include "shared/games/IGameProvider.hpp" + +namespace arcade::games::snake { + class SnakeGameProvider; +} + +class arcade::games::snake::SnakeGameProvider : public shared::games::IGameProvider { +public: + SnakeGameProvider() = default; + + ~SnakeGameProvider() override = default; + + /** + * @brief Provides the game manifest + * + * @return Manifest of current game + */ + const shared::games::GameManifest &getManifest() const noexcept override; + + /** + * @brief Provides a new instance of the game + * + * @return Created game instance + */ + std::shared_ptr createInstance() override; +}; diff --git a/games/snake/src/entities/apple/AppleEntity.cpp b/games/snake/src/entities/apple/AppleEntity.cpp new file mode 100644 index 0000000..651e611 --- /dev/null +++ b/games/snake/src/entities/apple/AppleEntity.cpp @@ -0,0 +1,65 @@ +/* +** EPITECH PROJECT, 2024 +** AppleEntity.cpp +** File description: +** AppleEntity class +*/ + +#include +#include "AppleEntity.hpp" +#include "common/components/CollidableComponent.hpp" +#include "common/components/TextureComponent.hpp" +#include "../snake/HeadEntity.hpp" +#include "../snake/TailEntity.hpp" +#include "../../SnakeGame.hpp" + +using namespace arcade::games::snake; +using namespace arcade::games::common::components; + +arcade::games::snake::AppleEntity::AppleEntity(shared::types::Vector2u size): _mapSize(size) { + this->_create(); + this->generateApple(); +} + +void AppleEntity::generateApple() { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution dis(1, this->_mapSize.x - 2); + Vector2i randomPosition = Vector2i(dis(gen), dis(gen)); + + for (auto &component: this->_components) { + auto posCmp = std::dynamic_pointer_cast(component); + if (posCmp == nullptr) continue; + + posCmp->getPosition().x = randomPosition.x; + posCmp->getPosition().y = randomPosition.y; + } +} + +void AppleEntity::_create() { + shared::games::components::TextureProps textureProps = { + .sources = { + .ascii = "assets/snake/apple.ascii", + .bin = "assets/snake/apple.png", + .binTileSize = Vector2f(40, 40) + }, + .origin = Vector2u(0, 0) + }; + std::shared_ptr collision = std::make_shared(*this, this->_onCollide); + std::shared_ptr texture = std::make_shared(*this, Vector2u(1, 1), 9, + textureProps); + + this->_components.push_back(collision); + this->_components.push_back(texture); +} + +void arcade::games::snake::AppleEntity::_onCollide(std::shared_ptr ctx, + std::shared_ptr target) { + auto game = std::dynamic_pointer_cast(ctx); + + if (!game) + return; + if (dynamic_cast(&target->getEntity()) || dynamic_cast(&target->getEntity())) { + game->addNewPoint(); + } +} diff --git a/games/snake/src/entities/apple/AppleEntity.hpp b/games/snake/src/entities/apple/AppleEntity.hpp new file mode 100644 index 0000000..f0a125f --- /dev/null +++ b/games/snake/src/entities/apple/AppleEntity.hpp @@ -0,0 +1,52 @@ +/* +** EPITECH PROJECT, 2024 +** AppleEntity.hpp +** File description: +** AppleEntity class +*/ + +#pragma once + +#include "common/entity/AEntity.hpp" +#include "shared/types/Vector.hpp" +#include "shared/games/components/ICollidableComponent.hpp" + +namespace arcade::games::snake { + class AppleEntity; +} + +class arcade::games::snake::AppleEntity : public common::AEntity { +public: + ~AppleEntity() override = default; + + /** + * @brief Create the apple entity + * @param size Size of the map + */ + explicit AppleEntity(shared::types::Vector2u + size); + + /** + * @brief Update the position of the apple on the map + * (As a new apple reward for the player) + */ + void generateApple(); + +protected: + /** + * @brief Create the apple components + */ + void _create(); + + /** + * @brief Represent the function that will be executed + * when the apple will collide with an other collidable component + * @param ctx Context of the game + * @param target Target component + */ + static void _onCollide(std::shared_ptr ctx, + std::shared_ptr target); + + + shared::types::Vector2u _mapSize; +}; diff --git a/games/snake/src/entities/background/BackgroundEntity.cpp b/games/snake/src/entities/background/BackgroundEntity.cpp new file mode 100644 index 0000000..3a35c04 --- /dev/null +++ b/games/snake/src/entities/background/BackgroundEntity.cpp @@ -0,0 +1,43 @@ +/* +** EPITECH PROJECT, 2024 +** BackgroundEntity.cpp +** File description: +** BackgroundEntity class +*/ + +#include "BackgroundEntity.hpp" +#include "common/components/TextureComponent.hpp" + +using namespace arcade::games::snake; +using namespace arcade::games::common::components; + +arcade::games::snake::BackgroundEntity::BackgroundEntity(shared::types::Vector2u size) { + unsigned int textureOriginX = 1; + unsigned int textureOriginY = 1; + + for (std::size_t x = 1; x < size.x - 1; x++) { + for (std::size_t y = 1; y < size.y - 1; y++) { + this->_addColor(Vector2i(x, y), Vector2u(textureOriginY, 0)); + textureOriginY = textureOriginY == 1 ? 2 : 1; + } + textureOriginX = textureOriginX == 1 ? 2 : 1; + textureOriginY = textureOriginX; + } +} + +void arcade::games::snake::BackgroundEntity::_addColor(shared::types::Vector2i position, shared::types::Vector2u origin) { + shared::games::components::TextureProps textureProps = { + .sources = { + .ascii = "assets/snake/wall.ascii", + .bin = "assets/snake/wall.png", + .binTileSize = Vector2f(40, 40) + }, + .origin = origin + }; + std::shared_ptr texture = std::make_shared(*this, Vector2u(1, 1), 2, + textureProps); + + texture->getPosition().x = position.x; + texture->getPosition().y = position.y; + this->_components.push_back(texture); +} diff --git a/games/snake/src/entities/background/BackgroundEntity.hpp b/games/snake/src/entities/background/BackgroundEntity.hpp new file mode 100644 index 0000000..4c75ac3 --- /dev/null +++ b/games/snake/src/entities/background/BackgroundEntity.hpp @@ -0,0 +1,35 @@ +/* +** EPITECH PROJECT, 2024 +** WallEntity.hpp +** File description: +** WallEntity class +*/ + +#pragma once + +#include +#include "common/entity/AEntity.hpp" +#include "shared/types/Vector.hpp" + +namespace arcade::games::snake { + class BackgroundEntity; +} + +class arcade::games::snake::BackgroundEntity : public common::AEntity { +public: + ~BackgroundEntity() override = default; + + /** + * @brief Create a background to the game + * @param size Size of the game + */ + explicit BackgroundEntity(shared::types::Vector2u size); + +protected: + /** + * @brief Add a color to the background (create a texture composent) + * @param position Position of the wall + * @param origin Origin of the texture + */ + void _addColor(shared::types::Vector2i position, shared::types::Vector2u origin); +}; diff --git a/games/snake/src/entities/snake/HeadEntity.cpp b/games/snake/src/entities/snake/HeadEntity.cpp new file mode 100644 index 0000000..e744697 --- /dev/null +++ b/games/snake/src/entities/snake/HeadEntity.cpp @@ -0,0 +1,89 @@ +/* +** EPITECH PROJECT, 2024 +** HeadEntity.cpp +** File description: +** HeadEntity class +*/ + +#include "SnakeGame.hpp" +#include "HeadEntity.hpp" +#include "../apple/AppleEntity.hpp" +#include "../wall/WallEntity.hpp" +#include "components/HeadKeyboardComponent.hpp" + +using namespace arcade::games::common::components; +using namespace shared::games::components; + +arcade::games::snake::HeadEntity::HeadEntity() : _textureProps( + arcade::games::snake::HeadEntity::_defaultTextureProps()), + direction(1, 0), + position(6, 10) { + std::shared_ptr collide = std::make_shared(*this, HeadEntity::_onCollide); + std::shared_ptr texture = std::make_shared(*this, Vector2u(1, 1), 10, + this->_textureProps); + std::shared_ptr keyboard = std::make_shared( + *this); + + this->_components.push_back(collide); + this->_components.push_back(texture); + this->_components.push_back(keyboard); + this->reset(); +} + +shared::games::components::TextureProps arcade::games::snake::HeadEntity::_defaultTextureProps() { + return { + .sources = { + .ascii = "assets/snake/head.ascii", + .bin = "assets/snake/head.png", + .binTileSize = Vector2f(40, 40) + }, + .origin = Vector2u(3, 0) + }; +} + +void arcade::games::snake::HeadEntity::forward() { + this->position.x += this->direction.x; + this->position.y += this->direction.y; + + for (auto &component: this->_components) { + auto posCmp = std::dynamic_pointer_cast(component); + if (posCmp == nullptr) continue; + + posCmp->getPosition().x += this->direction.x; + posCmp->getPosition().y += this->direction.y; + + auto textureCmp = std::dynamic_pointer_cast(component); + if (textureCmp == nullptr) continue; + + textureCmp->getTextureProps().origin = Vector2u(0, 0); + if (this->direction.y == 0) + textureCmp->getTextureProps().origin.x = 2; + if (this->direction.x > 0) + textureCmp->getTextureProps().origin.x += 1; + if (this->direction.y > 0) + textureCmp->getTextureProps().origin.x += 1; + } +} + +void arcade::games::snake::HeadEntity::_onCollide(std::shared_ptr ctx, + std::shared_ptr target) { + auto game = std::dynamic_pointer_cast(ctx); + + if (!game) + return; + if (dynamic_cast(&target->getEntity()) || dynamic_cast(&target->getEntity())) { + game->setLooseGame(true); + } +} + +void arcade::games::snake::HeadEntity::reset() { + this->direction = Vector2i(1, 0); + this->position = Vector2i(6, 10); + for (auto &component: this->_components) { + std::shared_ptr posCmp = std::dynamic_pointer_cast(component); + if (posCmp == nullptr) continue; + + posCmp->getPosition().x = this->position.x; + posCmp->getPosition().y = this->position.y; + } +} diff --git a/games/snake/src/entities/snake/HeadEntity.hpp b/games/snake/src/entities/snake/HeadEntity.hpp new file mode 100644 index 0000000..758e5a4 --- /dev/null +++ b/games/snake/src/entities/snake/HeadEntity.hpp @@ -0,0 +1,66 @@ +/* +** EPITECH PROJECT, 2024 +** HeadEntity.hpp +** File description: +** HeadEntity class +*/ + +#pragma once + +#include "common/entity/AEntity.hpp" +#include "common/components/CollidableComponent.hpp" +#include "common/components/TextureComponent.hpp" + +namespace arcade::games::snake { + class SnakeGame; + + class HeadEntity; +} + +class arcade::games::snake::HeadEntity : public common::AEntity { +public: + ~HeadEntity() override = default; + + /** + * @brief Create the head of a snake + */ + explicit HeadEntity(); + + /** + * @brief Update the position of the head of the snake + */ + void forward(); + + /** + * @brief Direction of the snake head + */ + Vector2i direction; + + /** + * @brief Position of the snake head + */ + Vector2i position; + + /** + * @brief Set the head at default position + */ + void reset(); + +protected: + /** + * @brief Get default texture props + * @return Texture props + */ + static shared::games::components::TextureProps _defaultTextureProps(); + + /** + * @brief Represent the function that will be executed + * when the snake will collide with an other collidable component + * @param ctx Context of the game + * @param target Target component + */ + static void _onCollide(std::shared_ptr ctx, + std::shared_ptr target); + + shared::games::components::TextureProps _textureProps; +}; diff --git a/games/snake/src/entities/snake/Snake.cpp b/games/snake/src/entities/snake/Snake.cpp new file mode 100644 index 0000000..3c3547e --- /dev/null +++ b/games/snake/src/entities/snake/Snake.cpp @@ -0,0 +1,75 @@ +/* +** EPITECH PROJECT, 2024 +** arcade +** File description: +** snake.cpp +*/ + +#include "Snake.hpp" +#include "TailEntity.hpp" +#include "HeadEntity.hpp" + +using namespace shared::games::entity; +using namespace arcade::games::snake; + +Snake::Snake(unsigned int tails) { + this->lastMove = std::chrono::milliseconds(900); + this->head = std::make_shared(); + this->_baseTails = tails; + + this->reset(); +} + +Snake::~Snake() = default; + +std::vector> &Snake::getTails() { + return this->_tails; +} + +std::shared_ptr Snake::addTail() { + std::shared_ptr newTail = std::make_shared(); + + this->_tails.push_back(newTail); + return newTail; +} + +void Snake::forward() { + Vector2i oldPosition = this->head->position; + Vector2i tempOldPosition = oldPosition; + int at = -1; + + this->head->forward(); + for (auto &tail: this->_tails) { + at += 1; + tempOldPosition = tail->getPosition(); + tail->setPosition(oldPosition); + oldPosition = tempOldPosition; + + Vector2i old = at > 0 ? this->_tails.at(at - 1)->getPosition() : this->head->position; + if (tail == this->_tails.back()) { + if (tail->getPosition().y < old.y) + tail->setTextureOrigin(Vector2u(0, 1)); + if (tail->getPosition().y > old.y) + tail->setTextureOrigin(Vector2u(1, 1)); + if (tail->getPosition().x < old.x) + tail->setTextureOrigin(Vector2u(2, 1)); + if (tail->getPosition().x > old.x) + tail->setTextureOrigin(Vector2u(3, 1)); + continue; + } + if (tail->getPosition().y == old.y) + tail->setTextureOrigin(Vector2u(1, 0)); + else + tail->setTextureOrigin(Vector2u(0, 0)); + } +} + +void Snake::reset() { + this->head->reset(); + this->_tails.clear(); + + for (size_t i = 0; i < this->_baseTails; i++) { + this->addTail(); + this->forward(); + } +} diff --git a/games/snake/src/entities/snake/Snake.hpp b/games/snake/src/entities/snake/Snake.hpp new file mode 100644 index 0000000..75f5ed7 --- /dev/null +++ b/games/snake/src/entities/snake/Snake.hpp @@ -0,0 +1,67 @@ +/* +** EPITECH PROJECT, 2024 +** arcade +** File description: +** snake.hpp +*/ + +#pragma once + +#include +#include +#include + +#include "common/game/AGame.hpp" +#include "shared/games/IEntity.hpp" +#include "HeadEntity.hpp" +#include "TailEntity.hpp" + +namespace arcade::games::snake { + class Snake; +} + +class arcade::games::snake::Snake { +public: + explicit Snake(unsigned int tails); + ~Snake(); + + /** + * @brief Head of the snake + * + */ + std::shared_ptr head; + + /** + * @brief Add a tail to the snake body + * + * @return The entity to the new tail + */ + std::shared_ptr addTail(); + + /** + * Get tails of the snake + * + * @return Vector of tails + */ + std::vector> &getTails(); + + /** + * @brief Reset the snake + */ + void reset(); + + /** + * @brief Update the position of the snake + */ + void forward(); + + shared::games::DeltaTime lastMove; +protected: + /** + * @brief Entities that compose the snake + * + */ + std::vector> _tails; + + unsigned int _baseTails; +}; diff --git a/games/snake/src/entities/snake/TailEntity.cpp b/games/snake/src/entities/snake/TailEntity.cpp new file mode 100644 index 0000000..55f2ee3 --- /dev/null +++ b/games/snake/src/entities/snake/TailEntity.cpp @@ -0,0 +1,56 @@ +/* +** EPITECH PROJECT, 2024 +** TailEntity.cpp +** File description: +** TailEntity class +*/ + +#include "TailEntity.hpp" + +using namespace arcade::games::common::components; +using namespace shared::games::components; + +arcade::games::snake::TailEntity::TailEntity() : _textureProps( + arcade::games::snake::TailEntity::_defaultTextureProps()), _position(0, 0) { + std::shared_ptr collide = std::make_shared(*this, nullptr); + std::shared_ptr texture = std::make_shared(*this, Vector2u(1, 1), 10, + this->_textureProps); + this->_components.push_back(collide); + this->_components.push_back(texture); +} + +shared::games::components::TextureProps arcade::games::snake::TailEntity::_defaultTextureProps() { + return { + .sources = { + .ascii = "assets/snake/tail.ascii", + .bin = "assets/snake/tail.png", + .binTileSize = Vector2f(40, 40) + }, + .origin = Vector2u(0, 0) + }; +} + +void arcade::games::snake::TailEntity::setPosition(Vector2i position) { + this->_position = position; + + for (auto &component: this->_components) { + auto posCmp = std::dynamic_pointer_cast(component); + if (posCmp == nullptr) continue; + + posCmp->getPosition().x = position.x; + posCmp->getPosition().y = position.y; + } +} + +void arcade::games::snake::TailEntity::setTextureOrigin(shared::types::Vector2u origin) { + for (auto &component: this->_components) { + auto txCmp = std::dynamic_pointer_cast(component); + if (txCmp == nullptr) continue; + + txCmp->getTextureProps().origin = origin; + } +} + +Vector2i arcade::games::snake::TailEntity::getPosition() { + return this->_position; +} diff --git a/games/snake/src/entities/snake/TailEntity.hpp b/games/snake/src/entities/snake/TailEntity.hpp new file mode 100644 index 0000000..2134205 --- /dev/null +++ b/games/snake/src/entities/snake/TailEntity.hpp @@ -0,0 +1,51 @@ +/* +** EPITECH PROJECT, 2024 +** TailEntity.hpp +** File description: +** TailEntity class +*/ + +#pragma once + +#include "common/entity/AEntity.hpp" +#include "common/components/CollidableComponent.hpp" +#include "common/components/TextureComponent.hpp" + +namespace arcade::games::snake { + class TailEntity; +} + +class arcade::games::snake::TailEntity : public common::AEntity { +public: + ~TailEntity() override = default; + + explicit TailEntity(); + + /** + * @brief Set position of the tail + * @param position + */ + void setPosition(Vector2i position); + + /** + * @brief Set texture origin for direction of the tail + * @param origin + */ + void setTextureOrigin(Vector2u origin); + + /** + * @brief Get position of the tail + * @return Vector of the position + */ + Vector2i getPosition(); + +protected: + /** + * @brief Get default texture props + * @return Texture props + */ + static shared::games::components::TextureProps _defaultTextureProps(); + + shared::games::components::TextureProps _textureProps; + Vector2i _position; +}; diff --git a/games/snake/src/entities/snake/components/HeadKeyboardComponent.cpp b/games/snake/src/entities/snake/components/HeadKeyboardComponent.cpp new file mode 100644 index 0000000..f0f0efd --- /dev/null +++ b/games/snake/src/entities/snake/components/HeadKeyboardComponent.cpp @@ -0,0 +1,54 @@ +/* +** EPITECH PROJECT, 2024 +** KeyboardComponent.cpp +** File description: +** HeadKeyboardComponent class +*/ + +#include "HeadKeyboardComponent.hpp" +#include "../../../SnakeGame.hpp" + +using namespace arcade::games::snake::components; +using namespace shared::games::components; + +HeadKeyboardComponent::HeadKeyboardComponent(HeadEntity &entity) : AComponent(KEYBOARD, entity), _parent(entity) {} + +void HeadKeyboardComponent::onKeyPress(std::shared_ptr ctx, + shared::games::components::IKeyboardComponent::KeyData keyData) { + if (keyData.type == ARROW) { + if (keyData.code.arrow == UP && this->_parent.direction.y != 1) { + this->_parent.direction = Vector2i(0, -1); + } + if (keyData.code.arrow == DOWN && this->_parent.direction.y != -1) { + this->_parent.direction = Vector2i(0, 1); + } + if (keyData.code.arrow == LEFT && this->_parent.direction.x != 1) { + this->_parent.direction = Vector2i(-1, 0); + } + if (keyData.code.arrow == RIGHT && this->_parent.direction.x != -1) { + this->_parent.direction = Vector2i(1, 0); + } + } + if (keyData.type == CHAR) { + if (keyData.code.character == 'z' && this->_parent.direction.y != 1) { + this->_parent.direction = Vector2i(0, -1); + } + if (keyData.code.character == 's' && this->_parent.direction.y != -1) { + this->_parent.direction = Vector2i(0, 1); + } + if (keyData.code.character == 'q' && this->_parent.direction.x != 1) { + this->_parent.direction = Vector2i(-1, 0); + } + if (keyData.code.character == 'd' && this->_parent.direction.x != -1) { + this->_parent.direction = Vector2i(1, 0); + } + } + if (keyData.type == CHAR && keyData.code.character == ' ') { + auto game = std::dynamic_pointer_cast(ctx); + + game->speedBoost = 3; + } +} + +void HeadKeyboardComponent::onKeyRelease(std::shared_ptr ctx, + shared::games::components::IKeyboardComponent::KeyData keyData) {} diff --git a/games/snake/src/entities/snake/components/HeadKeyboardComponent.hpp b/games/snake/src/entities/snake/components/HeadKeyboardComponent.hpp new file mode 100644 index 0000000..5f42113 --- /dev/null +++ b/games/snake/src/entities/snake/components/HeadKeyboardComponent.hpp @@ -0,0 +1,45 @@ +/* +** EPITECH PROJECT, 2024 +** HeadKeyboardComponent.hpp +** File description: +** HeadKeyboardComponent class +*/ + +#pragma once + +#include "shared/games/components/IKeyboardComponent.hpp" +#include "common/components/AComponent.hpp" +#include "../HeadEntity.hpp" + +namespace arcade::games::snake::components { + class HeadKeyboardComponent; +} + +class arcade::games::snake::components::HeadKeyboardComponent + : public common::components::AComponent, public virtual shared::games::components::IKeyboardComponent { +public: + ~HeadKeyboardComponent() override = default; + + /** + * @brief Create a keyboard component + * @param entity + */ + explicit HeadKeyboardComponent(HeadEntity &entity); + + /** + * @brief On key pressed event handler for the entity + * @param ctx Context of the game + * @param keyData Key data of key pressed + */ + void onKeyPress(std::shared_ptr ctx, shared::games::components::IKeyboardComponent::KeyData keyData) override; + + /** + * @brief On key release event handler for the entity + * @param ctx Context of the game + * @param keyData Key data of key released + */ + void onKeyRelease(std::shared_ptr ctx, shared::games::components::IKeyboardComponent::KeyData keyData) override; + +protected: + HeadEntity &_parent; +}; diff --git a/games/snake/src/entities/wall/WallEntity.cpp b/games/snake/src/entities/wall/WallEntity.cpp new file mode 100644 index 0000000..e554340 --- /dev/null +++ b/games/snake/src/entities/wall/WallEntity.cpp @@ -0,0 +1,56 @@ +/* +** EPITECH PROJECT, 2024 +** WallEntity.cpp +** File description: +** WallEntity class +*/ + +#include "WallEntity.hpp" +#include "common/components/CollidableComponent.hpp" +#include "common/components/TextureComponent.hpp" + +using namespace arcade::games::snake; +using namespace arcade::games::common::components; + +WallEntity::WallEntity(shared::types::Vector2u size) { + if (size.x < 3 || size.y < 3) + throw WallException("Invalid size of map"); + + for (std::size_t y = 0; y < size.y; y++) { + this->_createWall(Vector2i(0, y)); + } + for (std::size_t x = 1; x < size.x - 1; x++) { + this->_createWall(Vector2i(x, 0)); + this->_createWall(Vector2i(x, size.y - 1)); + } + for (std::size_t y = 0; y < size.y; y++) { + this->_createWall(Vector2i(size.x - 1, y)); + } +} + +void WallEntity::_createWall(shared::types::Vector2i position) { + shared::games::components::TextureProps textureProps = { + .sources = { + .ascii = "assets/snake/wall.ascii", + .bin = "assets/snake/wall.png", + .binTileSize = Vector2f(40, 40) + }, + .origin = Vector2u(0, 0) + }; + std::shared_ptr collision = std::make_shared(*this, nullptr); + std::shared_ptr texture = std::make_shared(*this, Vector2u(1, 1), 11, + textureProps); + + collision->getPosition().x = position.x; + collision->getPosition().y = position.y; + this->_components.push_back(collision); + texture->getPosition().x = position.x; + texture->getPosition().y = position.y; + this->_components.push_back(texture); +} + +WallEntity::WallException::WallException(const std::string &message) : _message(message) {} + +const char *WallEntity::WallException::what() const noexcept { + return this->_message.c_str(); +} diff --git a/games/snake/src/entities/wall/WallEntity.hpp b/games/snake/src/entities/wall/WallEntity.hpp new file mode 100644 index 0000000..7c3dc5c --- /dev/null +++ b/games/snake/src/entities/wall/WallEntity.hpp @@ -0,0 +1,40 @@ +/* +** EPITECH PROJECT, 2024 +** WallEntity.hpp +** File description: +** WallEntity class +*/ + +#pragma once + +#include +#include "common/entity/AEntity.hpp" +#include "shared/types/Vector.hpp" + +namespace arcade::games::snake { + class WallEntity; +} + +class arcade::games::snake::WallEntity : public common::AEntity { +public: + ~WallEntity() override = default; + + explicit WallEntity(shared::types::Vector2u size); + + class WallException : public std::exception { + public: + WallException(const std::string &message); + + const char *what() const noexcept override; + + private: + const std::string &_message; + }; + +protected: + /** + * @brief Create a wall (part of) (Component creation) + * @param position Position of the wall + */ + void _createWall(shared::types::Vector2i position); +}; diff --git a/graphics/sfml/src/GraphicsProvider.cpp b/graphics/sfml/src/GraphicsProvider.cpp index 20031d4..97e842e 100644 --- a/graphics/sfml/src/GraphicsProvider.cpp +++ b/graphics/sfml/src/GraphicsProvider.cpp @@ -30,6 +30,11 @@ const shared::graphics::GraphicsManifest GraphicsProvider::_manifest = { .name = "Flavien Chenu", .email = "flavien.chenu@epitech.eu", .website = "https://github.com/flavien-chenu" + }, + { + .name = "Yann Masson", + .email = "yann.masson@epitech.eu", + .website = "yannmasson.fr" } } }; diff --git a/graphics/sfml/src/window/EventsHandler.cpp b/graphics/sfml/src/window/EventsHandler.cpp index 877a4d8..d499aa3 100644 --- a/graphics/sfml/src/window/EventsHandler.cpp +++ b/graphics/sfml/src/window/EventsHandler.cpp @@ -28,7 +28,7 @@ EventsHandler::EventHandler EventsHandler::_getHandler(sf::Event::EventType type return handler != handlers.end() ? handler->second : nullptr; } -EventsHandler::EventsHandler(Window &window): _window(window) {} +EventsHandler::EventsHandler(Window &window) : _window(window) {} std::vector EventsHandler::handleEvents() { std::vector events; @@ -46,7 +46,7 @@ std::vector EventsHandler::handleEvents() { bool EventsHandler::_handleControlKey( sf::Event::KeyEvent event, - IKeyEvent::KeyCode &code + unused IKeyEvent::KeyCode &code ) { switch (event.code) { case sf::Keyboard::LControl: @@ -87,8 +87,7 @@ bool EventsHandler::_handleArrowKey( bool EventsHandler::_handleFunctionKey( sf::Event::KeyEvent event, IKeyEvent::KeyCode &code -) -{ +) { if (event.code >= sf::Keyboard::F1 && event.code <= sf::Keyboard::F12) { code.func = event.code - sf::Keyboard::F1 + 1; return true; @@ -102,26 +101,26 @@ bool EventsHandler::_handleCharKey( IKeyEvent::KeyCode &code ) { static const std::map specials = { - {sf::Keyboard::Space, ' '}, - {sf::Keyboard::LBracket, '['}, - {sf::Keyboard::RBracket, ']'}, + {sf::Keyboard::Space, ' '}, + {sf::Keyboard::LBracket, '['}, + {sf::Keyboard::RBracket, ']'}, {sf::Keyboard::Semicolon, ';'}, - {sf::Keyboard::Comma, ','}, - {sf::Keyboard::Period, '.'}, - {sf::Keyboard::Quote, '\''}, - {sf::Keyboard::Slash, '/'}, + {sf::Keyboard::Comma, ','}, + {sf::Keyboard::Period, '.'}, + {sf::Keyboard::Quote, '\''}, + {sf::Keyboard::Slash, '/'}, {sf::Keyboard::Backslash, '\\'}, - {sf::Keyboard::Tilde, '~'}, - {sf::Keyboard::Equal, '='}, - {sf::Keyboard::Hyphen, '-'}, - {sf::Keyboard::Enter, '\n'}, + {sf::Keyboard::Tilde, '~'}, + {sf::Keyboard::Equal, '='}, + {sf::Keyboard::Hyphen, '-'}, + {sf::Keyboard::Enter, '\n'}, {sf::Keyboard::Backspace, '\b'}, - {sf::Keyboard::Tab, '\t'}, - {sf::Keyboard::Escape, 0x1B}, - {sf::Keyboard::Add, '+'}, - {sf::Keyboard::Subtract, '-'}, - {sf::Keyboard::Multiply, '*'}, - {sf::Keyboard::Divide, '/'} + {sf::Keyboard::Tab, '\t'}, + {sf::Keyboard::Escape, 0x1B}, + {sf::Keyboard::Add, '+'}, + {sf::Keyboard::Subtract, '-'}, + {sf::Keyboard::Multiply, '*'}, + {sf::Keyboard::Divide, '/'} }; if (event.code >= sf::Keyboard::A && event.code <= sf::Keyboard::Z) { @@ -186,6 +185,7 @@ EventPtr EventsHandler::_handleWindowResizeEvent( unused sf::Event &event, Window &window ) { + window.onResize(); return std::make_shared(); } @@ -193,35 +193,48 @@ EventPtr EventsHandler::_handleMouseMoveEvent( sf::Event &event, Window &window ) { - return std::make_shared( - window.pixelsToTiles(Vector2i(event.mouseMove.x, event.mouseMove.y)) - ); + auto pos = window.mapPositionToTile({ + event.mouseMove.x, + event.mouseMove.y + }); + + return pos.x >= 0 && pos.y >= 0 + ? std::make_shared(pos) + : nullptr; } EventPtr EventsHandler::_handleMouseButtonPressEvent( sf::Event &event, Window &window ) { - Vector2i pos = window.pixelsToTiles(Vector2i(event.mouseButton.x, event.mouseButton.y)); + Vector2i pos = window.mapPositionToTile({ + event.mouseMove.x, + event.mouseMove.y + }); + if (pos.x < 0 || pos.y < 0) + return nullptr; if (event.mouseButton.button == sf::Mouse::Button::Left) return std::make_shared(pos, IMouseButtonEvent::MouseButton::LEFT); else if (event.mouseButton.button == sf::Mouse::Button::Right) return std::make_shared(pos, IMouseButtonEvent::MouseButton::RIGHT); - else - return nullptr; + return nullptr; } EventPtr EventsHandler::_handleMouseBtnReleaseEvent( sf::Event &event, unused Window &window ) { - Vector2i pos = window.pixelsToTiles(Vector2i(event.mouseButton.x, event.mouseButton.y)); + auto pos = window.mapPositionToTile({ + event.mouseMove.x, + event.mouseMove.y + }); + if (pos.x < 0 || pos.y < 0) + return nullptr; if (event.mouseButton.button == sf::Mouse::Button::Left) return std::make_shared(pos, IMouseButtonEvent::MouseButton::LEFT); else if (event.mouseButton.button == sf::Mouse::Button::Right) return std::make_shared(pos, IMouseButtonEvent::MouseButton::RIGHT); - else - return nullptr; + return nullptr; } diff --git a/graphics/sfml/src/window/Renderer.cpp b/graphics/sfml/src/window/Renderer.cpp index 3ba9489..5b93ce3 100644 --- a/graphics/sfml/src/window/Renderer.cpp +++ b/graphics/sfml/src/window/Renderer.cpp @@ -21,8 +21,8 @@ Renderer::Renderer(Window &window) : _window(window), _layer(_window.getInnerWin void Renderer::render(const shared::graphics::TextProps &props) { auto font = _castOrThrow(props.font); - auto entityPosition = _entityPixelsPosition(props.position); - auto entitySize = _window.tilesToPixels(props.size); + auto entityPosition = _entityPixels(props.position); + auto entitySize = _entityPixels(props.size); _reset(_text); _text.setFont(font->getInnerFont()); @@ -78,7 +78,7 @@ void Renderer::_textAdjustPosition() { void Renderer::render(const shared::graphics::TextureProps &props) { auto texture = _castOrThrow(props.texture); - auto entityPosition = _entityPixelsPosition(props.position); + auto entityPosition = _entityPixels(props.position); _reset(_sprite); _sprite.setTexture(texture->getInnerTexture()); @@ -88,7 +88,7 @@ void Renderer::render(const shared::graphics::TextureProps &props) { } void Renderer::_setTextureRectAndScale(const shared::graphics::TextureProps &props) { - auto size = _window.tilesToPixels(props.size); + auto size = _entityPixels(props.size); float width = static_cast(props.size.x) * props.binTileSize.x; float height = static_cast(props.size.y) * props.binTileSize.y; float left = static_cast(props.origin.x) * props.binTileSize.x; @@ -125,11 +125,22 @@ void Renderer::_reset(sf::Sprite &sprite) { sprite.setOrigin(0, 0); } -Vector2f Renderer::_entityPixelsPosition(const Vector2i &position) { - auto pixels = _window.tilesToPixels(position); +Vector2f Renderer::_entityPixels(const Vector2i &position) { + auto realSize = this->_window.getSize(); + Vector2u originalPixels = this->_window.getPixelSizeFromTiles(realSize); return { - static_cast(pixels.x), - static_cast(pixels.y) + static_cast(position.x * originalPixels.x / realSize.x), + static_cast(position.y * originalPixels.y / realSize.y) + }; +} + +Vector2i Renderer::_entityPixels(const Vector2u &position) { + auto realSize = this->_window.getSize(); + Vector2u originalPixels = this->_window.getPixelSizeFromTiles(realSize); + + return { + static_cast(position.x * originalPixels.x / realSize.x), + static_cast(position.y * originalPixels.y / realSize.y) }; } diff --git a/graphics/sfml/src/window/Renderer.hpp b/graphics/sfml/src/window/Renderer.hpp index 21c4998..f7db849 100644 --- a/graphics/sfml/src/window/Renderer.hpp +++ b/graphics/sfml/src/window/Renderer.hpp @@ -60,7 +60,14 @@ class arcade::graphics::sfml::window::Renderer { * @param position Tile position * @return Pixel position */ - Vector2f _entityPixelsPosition(const Vector2i &position); + Vector2f _entityPixels(const Vector2i &position); + + /** + * @brief Convert a tile size to pixel size + * @param size Tile size + * @return Pixel size + */ + Vector2i _entityPixels(const Vector2u &position); /** * @brief Align vertically the text diff --git a/graphics/sfml/src/window/Window.cpp b/graphics/sfml/src/window/Window.cpp index c63269e..1eedc47 100644 --- a/graphics/sfml/src/window/Window.cpp +++ b/graphics/sfml/src/window/Window.cpp @@ -17,10 +17,11 @@ const Vector2u Window::tileSize = { 12, 12 }; Window::Window(const IWindow::WindowInitProps &props): _size(props.size), + _initialSize(0, 0), _renderer(*this), _eventsHandler(*this) { - auto size = _getPixelSizeFromTiles(props.size); + auto size = getPixelSizeFromTiles(props.size); _mode = props.mode; _fps = props.fps; @@ -29,6 +30,12 @@ Window::Window(const IWindow::WindowInitProps &props): props.title ); Window::setIcon(props.icon); + _view.setSize(static_cast(size.x), static_cast(size.y)); + _view.setCenter(static_cast(size.x) / 2, static_cast(size.y) / 2); + _initialSize = { + static_cast(_window.getSize().x), + static_cast(_window.getSize().y) + }; } Window::~Window() @@ -45,7 +52,7 @@ void Window::setTitle(const std::string &title) { } void Window::setSize(shared::types::Vector2u size) { - auto real = _getPixelSizeFromTiles(size); + auto real = getPixelSizeFromTiles(size); _size = size; _window.setSize(sf::Vector2u(real.x, real.y)); @@ -131,7 +138,7 @@ std::vector Window::getEvents() { return _eventsHandler.handleEvents(); } -Vector2u Window::_getPixelSizeFromTiles(const Vector2u &size) { +Vector2u Window::getPixelSizeFromTiles(const Vector2u &size) { auto mode = sf::VideoMode::getDesktopMode(); Vector2u real(mode.width, mode.height); @@ -142,30 +149,42 @@ Vector2u Window::_getPixelSizeFromTiles(const Vector2u &size) { return real; } -Vector2i Window::pixelsToTiles(const shared::types::Vector2i &position) const { - auto realSize = _window.getSize(); +Vector2i Window::mapPositionToTile(const Vector2i &position) const { + auto pixelsPosition = _window.mapPixelToCoords({ + position.x, + position.y + }); + Vector2f size = { static_cast(_size.x), static_cast(_size.y) }; + Vector2f initialSize = { static_cast(_initialSize.x), static_cast(_initialSize.y) }; - return { - static_cast(position.x * _size.x / realSize.x), - static_cast(position.y * _size.y / realSize.y) + Vector2f tilesPosition = { + pixelsPosition.x * size.x / initialSize.x, + pixelsPosition.y * size.y / initialSize.y }; -} - -Vector2i Window::tilesToPixels(const Vector2i &position) const { - auto realSize = _window.getSize(); + if (tilesPosition.x >= size.x || tilesPosition.y >= size.y || pixelsPosition.x < 0) + return {-1, -1}; return { - static_cast(position.x * realSize.x / _size.x), - static_cast(position.y * realSize.y / _size.y) + static_cast(tilesPosition.x), + static_cast(tilesPosition.y) }; } -Vector2i Window::tilesToPixels(const Vector2u &position) const { - auto realSize = _window.getSize(); +void Window::onResize() +{ + Vector2u originalPixels = getPixelSizeFromTiles(_size); + auto size = _window.getSize(); + auto width = static_cast(size.x); + auto height = static_cast(size.y); - return { - static_cast(position.x * realSize.x / _size.x), - static_cast(position.y * realSize.y / _size.y) - }; + _view.setSize(width, height); + _view.setCenter( static_cast(originalPixels.x) / 2, + static_cast(originalPixels.y) / 2 + ); + if (width < height) { + _view.zoom(static_cast(originalPixels.x) / width); + } else { + _view.zoom(static_cast(originalPixels.y) / height); + } + _window.setView(_view); } - diff --git a/graphics/sfml/src/window/Window.hpp b/graphics/sfml/src/window/Window.hpp index 5ff6fdd..6ac8d02 100644 --- a/graphics/sfml/src/window/Window.hpp +++ b/graphics/sfml/src/window/Window.hpp @@ -137,29 +137,27 @@ class arcade::graphics::sfml::window::Window: public shared::graphics::IWindow { * @brief Convert a position in pixels to a position in tiles * @return Converted position */ - Vector2i pixelsToTiles(const Vector2i &position) const; + Vector2i mapPositionToTile(const Vector2i &pixelsPosition) const; /** - * @brief Convert a position in tiles to a position in pixels - * @return Converted position + * @brief Get the size of a tile + * @return Size of a tile */ - Vector2i tilesToPixels(const Vector2i &position) const; + static const Vector2u tileSize; /** - * @brief Convert a position in tiles to a position in pixels - * @return Converted position + * @brief Resize the view of the window */ - Vector2i tilesToPixels(const Vector2u &position) const; + void onResize(); /** - * @brief Get the size of a tile - * @return Size of a tile + * @brief Get the size of the window in pixels + * @param size Size of the window in tiles + * @return Size of the window in pixels */ - static const Vector2u tileSize; + static Vector2u getPixelSizeFromTiles(const Vector2u &size); private: - static Vector2u _getPixelSizeFromTiles(const Vector2u &size); - EventsHandler _eventsHandler; Renderer _renderer; sf::RenderWindow _window; @@ -168,4 +166,6 @@ class arcade::graphics::sfml::window::Window: public shared::graphics::IWindow { WindowMode _mode; sf::Image _icon; Vector2u _size; + Vector2u _initialSize; + sf::View _view; }; diff --git a/shared/games/components/ICollidableComponent.hpp b/shared/games/components/ICollidableComponent.hpp index ba65ba0..feec546 100644 --- a/shared/games/components/ICollidableComponent.hpp +++ b/shared/games/components/ICollidableComponent.hpp @@ -25,5 +25,5 @@ class shared::games::components::ICollidableComponent: public virtual IPositiona * @param ctx Context of the game * @param target Target entity */ - virtual void onCollide(std::shared_ptr &ctx, std::shared_ptr target) = 0; + virtual void onCollide(std::shared_ptr ctx, std::shared_ptr target) = 0; }; diff --git a/shared/games/components/IDisplayableComponent.hpp b/shared/games/components/IDisplayableComponent.hpp index 8732895..df1f6b1 100644 --- a/shared/games/components/IDisplayableComponent.hpp +++ b/shared/games/components/IDisplayableComponent.hpp @@ -29,17 +29,17 @@ class shared::games::components::IDisplayableComponent : public virtual IPositio * @brief On click event handler for the entity * @param ctx Context of the game */ - virtual void onMousePress(std::shared_ptr &ctx) = 0; + virtual void onMousePress(std::shared_ptr ctx) = 0; /** * @brief On release event handler for the entity * @param ctx Context of the game */ - virtual void onMouseRelease(std::shared_ptr &ctx) = 0; + virtual void onMouseRelease(std::shared_ptr ctx) = 0; /** * @brief On hover event handler for the entity * @param ctx Context of the game */ - virtual void onMouseHover(std::shared_ptr &ctx) = 0; + virtual void onMouseHover(std::shared_ptr ctx) = 0; }; diff --git a/shared/games/components/IKeyboardComponent.hpp b/shared/games/components/IKeyboardComponent.hpp index f2f474a..a6bb8f8 100644 --- a/shared/games/components/IKeyboardComponent.hpp +++ b/shared/games/components/IKeyboardComponent.hpp @@ -62,12 +62,12 @@ class shared::games::components::IKeyboardComponent: public virtual IComponent * @param ctx Context of the game * @param keyData Key data of key pressed */ - virtual void onKeyPress(std::shared_ptr &ctx, KeyData keyData) = 0; + virtual void onKeyPress(std::shared_ptr ctx, KeyData keyData) = 0; /** * @brief On key release event handler for the entity * @param ctx Context of the game * @param keyData Key data of key released */ - virtual void onKeyRelease(std::shared_ptr &ctx, KeyData keyData) = 0; + virtual void onKeyRelease(std::shared_ptr ctx, KeyData keyData) = 0; }; diff --git a/shared/games/components/IPositionableComponent.hpp b/shared/games/components/IPositionableComponent.hpp index bc51501..6eac1cc 100644 --- a/shared/games/components/IPositionableComponent.hpp +++ b/shared/games/components/IPositionableComponent.hpp @@ -23,11 +23,11 @@ class shared::games::components::IPositionableComponent: public virtual ICompone * @brief Get position of the entity (tiles) * */ - virtual types::Vector2i &getPosition(void) noexcept = 0; + virtual types::Vector2i &getPosition() noexcept = 0; /** * @brief Get size of the entity (tiles) * */ - virtual types::Vector2u &getSize(void) noexcept = 0; + virtual types::Vector2u &getSize() noexcept = 0; }; diff --git a/shared/games/components/ISoundComponent.hpp b/shared/games/components/ISoundComponent.hpp index fd4ab9c..43d6f02 100644 --- a/shared/games/components/ISoundComponent.hpp +++ b/shared/games/components/ISoundComponent.hpp @@ -34,28 +34,28 @@ class shared::games::components::ISoundComponent: public virtual IComponent * * @return Sound path */ - virtual const std::string &getPath(void) const noexcept = 0; + virtual const std::string &getPath() const noexcept = 0; /** * @brief Get state of the sound * * @return Sound state */ - virtual SoundState &getState(void) noexcept = 0; + virtual SoundState &getState() noexcept = 0; /** * @brief Get volume of the sound * * @return Sound volume */ - virtual SoundVolume &getVolume(void) noexcept = 0; + virtual SoundVolume &getVolume() noexcept = 0; /** * @brief Get loop of the sound * * @return Sound loop */ - virtual bool &getLoop(void) noexcept = 0; + virtual bool &getLoop() noexcept = 0; /** * @brief On state change event handler for the component @@ -63,5 +63,5 @@ class shared::games::components::ISoundComponent: public virtual IComponent * @param ctx Context of the game * @param state New state of the sound */ - virtual void onStateChange(std::shared_ptr &ctx, SoundState state) = 0; + virtual void onStateChange(std::shared_ptr ctx, SoundState state) = 0; };