From 53126329b32eea1ee8b1945d734eb90e952384be Mon Sep 17 00:00:00 2001 From: daniel baker Date: Tue, 8 Oct 2024 18:28:17 +0100 Subject: [PATCH 01/38] feat(wip): cover first testcase --- .gitignore | 3 + app/CMakeLists.txt | 1 + app/include/game_entity.hpp | 3 + lib/CMakeLists.txt | 1 + lib/include/core/component_array.hpp | 75 ++++++++++++ lib/include/core/component_manager.hpp | 82 +++++++++++++ lib/include/core/coordinator.hpp | 121 +++++++++++++++++++ lib/include/core/entity_manager.hpp | 61 ++++++++++ lib/include/core/event.hpp | 39 ++++++ lib/include/core/event_manager.hpp | 42 +++++++ lib/include/core/system.hpp | 14 +++ lib/include/core/system_manager.hpp | 70 +++++++++++ lib/include/core/types.hpp | 16 +++ tests/CMakeLists.txt | 1 + tests/src/status_condition_systems_tests.cpp | 11 ++ 15 files changed, 540 insertions(+) create mode 100644 lib/include/core/component_array.hpp create mode 100644 lib/include/core/component_manager.hpp create mode 100644 lib/include/core/coordinator.hpp create mode 100644 lib/include/core/entity_manager.hpp create mode 100644 lib/include/core/event.hpp create mode 100644 lib/include/core/event_manager.hpp create mode 100644 lib/include/core/system.hpp create mode 100644 lib/include/core/system_manager.hpp create mode 100644 lib/include/core/types.hpp create mode 100644 tests/src/status_condition_systems_tests.cpp diff --git a/.gitignore b/.gitignore index 3b12b76..c96e59a 100644 --- a/.gitignore +++ b/.gitignore @@ -125,3 +125,6 @@ saves/ game.sav build_web/ docs/ + +# clang +.cache/ diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index d8f0488..716a10d 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -5,6 +5,7 @@ target_include_directories(${PROJECT_NAME} # Ensure the C++20 standard is available. target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20) +set(CMAKE_EXPORT_COMPILE_COMMANDS 1) # Enforce UTF-8 encoding on MSVC. if (MSVC) diff --git a/app/include/game_entity.hpp b/app/include/game_entity.hpp index 9eaf420..617d040 100644 --- a/app/include/game_entity.hpp +++ b/app/include/game_entity.hpp @@ -6,6 +6,7 @@ #include "basic_ai_component.hpp" #include "types/world_fwd.hpp" +#include <../../lib/include/core/types.hpp> namespace cpprl { @@ -19,6 +20,8 @@ namespace cpprl { class Entity { private: + SupaRL::Entity id_; + std::string name_; bool blocker_; std::unique_ptr transformComponent_; diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 1dd83da..9995c34 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -6,6 +6,7 @@ file( "./include/*.hpp" "./include/*.h" ) +set(CMAKE_EXPORT_COMPILE_COMMANDS 1) add_library(cpprl ${cpprl_SOURCE_FILES}) diff --git a/lib/include/core/component_array.hpp b/lib/include/core/component_array.hpp new file mode 100644 index 0000000..cc6e09f --- /dev/null +++ b/lib/include/core/component_array.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include "types.hpp" +#include +#include +#include + +namespace SupaRL { + class IComponentArray + { + public: + virtual ~IComponentArray() = default; + virtual void EntityDestroyed(Entity entity) = 0; + }; + + + template + class ComponentArray : public IComponentArray + { + public: + void InsertData(Entity entity, T component) + { + assert(mEntityToIndexMap.find(entity) == mEntityToIndexMap.end() && "Component added to same entity more than once."); + + // Put new entry at end + size_t newIndex = mSize; + mEntityToIndexMap[entity] = newIndex; + mIndexToEntityMap[newIndex] = entity; + mComponentArray[newIndex] = component; + ++mSize; + } + + void RemoveData(Entity entity) + { + assert(mEntityToIndexMap.find(entity) != mEntityToIndexMap.end() && "Removing non-existent component."); + + // Copy element at end into deleted element's place to maintain density + size_t indexOfRemovedEntity = mEntityToIndexMap[entity]; + size_t indexOfLastElement = mSize - 1; + mComponentArray[indexOfRemovedEntity] = mComponentArray[indexOfLastElement]; + + // Update map to point to moved spot + Entity entityOfLastElement = mIndexToEntityMap[indexOfLastElement]; + mEntityToIndexMap[entityOfLastElement] = indexOfRemovedEntity; + mIndexToEntityMap[indexOfRemovedEntity] = entityOfLastElement; + + mEntityToIndexMap.erase(entity); + mIndexToEntityMap.erase(indexOfLastElement); + + --mSize; + } + + T& GetData(Entity entity) + { + assert(mEntityToIndexMap.find(entity) != mEntityToIndexMap.end() && "Retrieving non-existent component."); + + return mComponentArray[mEntityToIndexMap[entity]]; + } + + void EntityDestroyed(Entity entity) override + { + if (mEntityToIndexMap.find(entity) != mEntityToIndexMap.end()) + { + RemoveData(entity); + } + } + + private: + std::array mComponentArray{}; + std::unordered_map mEntityToIndexMap{}; + std::unordered_map mIndexToEntityMap{}; + size_t mSize{}; + }; + +} diff --git a/lib/include/core/component_manager.hpp b/lib/include/core/component_manager.hpp new file mode 100644 index 0000000..f27f678 --- /dev/null +++ b/lib/include/core/component_manager.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include "component_array.hpp" +#include "types.hpp" +#include +#include +#include + + +namespace SupaRL +{ + class ComponentManager + { + public: + template + void RegisterComponent() + { + const char* typeName = typeid(T).name(); + + assert(mComponentTypes.find(typeName) == mComponentTypes.end() && "Registering component type more than once."); + + mComponentTypes.insert({typeName, mNextComponentType}); + mComponentArrays.insert({typeName, std::make_shared>()}); + + ++mNextComponentType; + } + + template + ComponentType GetComponentType() + { + const char* typeName = typeid(T).name(); + + assert(mComponentTypes.find(typeName) != mComponentTypes.end() && "Component not registered before use."); + + return mComponentTypes[typeName]; + } + + template + void AddComponent(Entity entity, T component) + { + GetComponentArray()->InsertData(entity, component); + } + + template + void RemoveComponent(Entity entity) + { + GetComponentArray()->RemoveData(entity); + } + + template + T& GetComponent(Entity entity) + { + return GetComponentArray()->GetData(entity); + } + + void EntityDestroyed(Entity entity) + { + for (auto const& pair : mComponentArrays) + { + auto const& component = pair.second; + + component->EntityDestroyed(entity); + } + } + + private: + std::unordered_map mComponentTypes{}; + std::unordered_map> mComponentArrays{}; + ComponentType mNextComponentType{}; + + + template + std::shared_ptr> GetComponentArray() + { + const char* typeName = typeid(T).name(); + + assert(mComponentTypes.find(typeName) != mComponentTypes.end() && "Component not registered before use."); + + return std::static_pointer_cast>(mComponentArrays[typeName]); + } + }; +} diff --git a/lib/include/core/coordinator.hpp b/lib/include/core/coordinator.hpp new file mode 100644 index 0000000..3233fd6 --- /dev/null +++ b/lib/include/core/coordinator.hpp @@ -0,0 +1,121 @@ +#pragma once + +#include "component_manager.hpp" +#include "entity_manager.hpp" +#include "event_manager.hpp" +#include "system_manager.hpp" +#include "types.hpp" +#include + + +namespace SupaRL +{ +class Coordinator +{ +public: + void Init() + { + mComponentManager = std::make_unique(); + mEntityManager = std::make_unique(); + mEventManager = std::make_unique(); + mSystemManager = std::make_unique(); + } + + + // Entity methods + Entity CreateEntity() + { + return mEntityManager->CreateEntity(); + } + + void DestroyEntity(Entity entity) + { + mEntityManager->DestroyEntity(entity); + + mComponentManager->EntityDestroyed(entity); + + mSystemManager->EntityDestroyed(entity); + } + + + // Component methods + template + void RegisterComponent() + { + mComponentManager->RegisterComponent(); + } + + template + void AddComponent(Entity entity, T component) + { + mComponentManager->AddComponent(entity, component); + + auto signature = mEntityManager->GetSignature(entity); + signature.set(mComponentManager->GetComponentType(), true); + mEntityManager->SetSignature(entity, signature); + + mSystemManager->EntitySignatureChanged(entity, signature); + } + + template + void RemoveComponent(Entity entity) + { + mComponentManager->RemoveComponent(entity); + + auto signature = mEntityManager->GetSignature(entity); + signature.set(mComponentManager->GetComponentType(), false); + mEntityManager->SetSignature(entity, signature); + + mSystemManager->EntitySignatureChanged(entity, signature); + } + + template + T& GetComponent(Entity entity) + { + return mComponentManager->GetComponent(entity); + } + + template + ComponentType GetComponentType() + { + return mComponentManager->GetComponentType(); + } + + + // System methods + template + std::shared_ptr RegisterSystem() + { + return mSystemManager->RegisterSystem(); + } + + template + void SetSystemSignature(Signature signature) + { + mSystemManager->SetSignature(signature); + } + + + // Event methods + void AddEventListener(EventId eventId, std::function const& listener) + { + mEventManager->AddListener(eventId, listener); + } + + void SendEvent(Event& event) + { + mEventManager->SendEvent(event); + } + + void SendEvent(EventId eventId) + { + mEventManager->SendEvent(eventId); + } + +private: + std::unique_ptr mComponentManager; + std::unique_ptr mEntityManager; + std::unique_ptr mEventManager; + std::unique_ptr mSystemManager; +}; +} diff --git a/lib/include/core/entity_manager.hpp b/lib/include/core/entity_manager.hpp new file mode 100644 index 0000000..dffad41 --- /dev/null +++ b/lib/include/core/entity_manager.hpp @@ -0,0 +1,61 @@ +#pragma once + +#include "types.hpp" +#include +#include +#include + + +namespace SupaRL { + class EntityManager + { + public: + EntityManager() + { + for (Entity entity = 0; entity < MAX_ENTITIES; ++entity) + { + mAvailableEntities.push(entity); + } + } + + Entity CreateEntity() + { + assert(mLivingEntityCount < MAX_ENTITIES && "Too many entities in existence."); + + Entity id = mAvailableEntities.front(); + mAvailableEntities.pop(); + ++mLivingEntityCount; + + return id; + } + + void DestroyEntity(Entity entity) + { + assert(entity < MAX_ENTITIES && "Entity out of range."); + + mSignatures[entity].reset(); + mAvailableEntities.push(entity); + --mLivingEntityCount; + } + + void SetSignature(Entity entity, Signature signature) + { + assert(entity < MAX_ENTITIES && "Entity out of range."); + + mSignatures[entity] = signature; + } + + Signature GetSignature(Entity entity) + { + assert(entity < MAX_ENTITIES && "Entity out of range."); + + return mSignatures[entity]; + } + + private: + std::queue mAvailableEntities{}; + std::array mSignatures{}; + uint32_t mLivingEntityCount{}; + }; + +} diff --git a/lib/include/core/event.hpp b/lib/include/core/event.hpp new file mode 100644 index 0000000..017d394 --- /dev/null +++ b/lib/include/core/event.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include "types.hpp" +#include +#include + +namespace SupaRL { + + class Event + { + public: + Event() = delete; + + explicit Event(EventId type) + : mType(type) + {} + + template + void SetParam(EventId id, T value) + { + mData[id] = value; + } + + template + T GetParam(EventId id) + { + return std::any_cast(mData[id]); + } + + EventId GetType() const + { + return mType; + } + + private: + EventId mType{}; + std::unordered_map mData{}; + }; +} diff --git a/lib/include/core/event_manager.hpp b/lib/include/core/event_manager.hpp new file mode 100644 index 0000000..bac2f0b --- /dev/null +++ b/lib/include/core/event_manager.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include "event.hpp" +#include "types.hpp" +#include +#include +#include + +namespace SupaRL +{ + class EventManager + { + public: + void AddListener(EventId eventId, std::function const& listener) + { + listeners[eventId].push_back(listener); + } + + void SendEvent(Event& event) + { + uint32_t type = event.GetType(); + + for (auto const& listener : listeners[type]) + { + listener(event); + } + } + + void SendEvent(EventId eventId) + { + Event event(eventId); + + for (auto const& listener : listeners[eventId]) + { + listener(event); + } + } + + private: + std::unordered_map>> listeners; + }; +} diff --git a/lib/include/core/system.hpp b/lib/include/core/system.hpp new file mode 100644 index 0000000..62b14d2 --- /dev/null +++ b/lib/include/core/system.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include "types.hpp" +#include + + +namespace SupaRL { + class System + { + public: + std::set mEntities; + }; +} + diff --git a/lib/include/core/system_manager.hpp b/lib/include/core/system_manager.hpp new file mode 100644 index 0000000..1540bbb --- /dev/null +++ b/lib/include/core/system_manager.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include "system.hpp" +#include "types.hpp" +#include +#include +#include + +namespace SupaRL +{ + class SystemManager + { + public: + template + std::shared_ptr RegisterSystem() + { + const char* typeName = typeid(T).name(); + + assert(mSystems.find(typeName) == mSystems.end() && "Registering system more than once."); + + auto system = std::make_shared(); + mSystems.insert({typeName, system}); + return system; + } + + template + void SetSignature(Signature signature) + { + const char* typeName = typeid(T).name(); + + assert(mSystems.find(typeName) != mSystems.end() && "System used before registered."); + + mSignatures.insert({typeName, signature}); + } + + void EntityDestroyed(Entity entity) + { + for (auto const& pair : mSystems) + { + auto const& system = pair.second; + + + system->mEntities.erase(entity); + } + } + + void EntitySignatureChanged(Entity entity, Signature entitySignature) + { + for (auto const& pair : mSystems) + { + auto const& type = pair.first; + auto const& system = pair.second; + auto const& systemSignature = mSignatures[type]; + + if ((entitySignature & systemSignature) == systemSignature) + { + system->mEntities.insert(entity); + } + else + { + system->mEntities.erase(entity); + } + } + } + + private: + std::unordered_map mSignatures{}; + std::unordered_map> mSystems{}; + }; +} diff --git a/lib/include/core/types.hpp b/lib/include/core/types.hpp new file mode 100644 index 0000000..294929b --- /dev/null +++ b/lib/include/core/types.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +namespace SupaRL { + using Entity = std::uint32_t; + const Entity MAX_ENTITIES = 5000; + using ComponentType = std::uint8_t; + const ComponentType MAX_COMPONENTS = 32; + using Signature = std::bitset; + + // Events + using EventId = std::uint32_t; + +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 807751f..1b3ebb7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,6 +5,7 @@ FetchContent_Declare( ) # For Windows: Prevent overriding the parent project's compiler/linker settings set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +set(CMAKE_EXPORT_COMPILE_COMMANDS 1) FetchContent_MakeAvailable(googletest) enable_testing() diff --git a/tests/src/status_condition_systems_tests.cpp b/tests/src/status_condition_systems_tests.cpp new file mode 100644 index 0000000..fdf0cad --- /dev/null +++ b/tests/src/status_condition_systems_tests.cpp @@ -0,0 +1,11 @@ + +/** + * @file status_condition_systems_tests.cpp + */ + +/** + * It should remove 1 hp from the player when afflicted with + * a status condition that deals damage. + */ + + From 4b83202fcb1b274043fd7d50979ab87c12113e5a Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Tue, 8 Oct 2024 20:11:59 +0100 Subject: [PATCH 02/38] fix: include broken tests --- app/include/components.hpp | 542 +++++++++---------- lib/include/components/components.hpp | 16 + tests/CMakeLists.txt | 1 + tests/src/alltests.cpp | 8 +- tests/src/status_condition_systems_tests.cpp | 25 +- 5 files changed, 317 insertions(+), 275 deletions(-) create mode 100644 lib/include/components/components.hpp diff --git a/app/include/components.hpp b/app/include/components.hpp index 924ddb2..7c93c41 100644 --- a/app/include/components.hpp +++ b/app/include/components.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include "game_entity.hpp" #include "types/action_result.hpp" @@ -13,277 +12,276 @@ namespace cpprl { -class AttackComponent { - public: - AttackComponent() = default; - explicit AttackComponent(int damage) : damage_(damage) {} - virtual ~AttackComponent() = default; - int get_damage() const { return damage_; } - void boost_damage(int amount) { damage_ += amount; } - - template - void serialize(Archive& archive) { - archive(damage_); - } - - private: - int damage_; -}; - -class DefenseComponent { - public: - DefenseComponent() = default; - DefenseComponent(int defense, int maxHp) - : defense_(defense), hp_(maxHp), max_hp_(maxHp) {} - virtual ~DefenseComponent() = default; - - int get_hp() const { return hp_; } - int get_max_hp() const { return max_hp_; } - int get_defense() const { return defense_; } - void boost_defense(int amount) { defense_ += amount; } - - void take_damage(int damage) { hp_ -= damage; } - int heal(int amount); - bool is_dead() const { return hp_ <= 0; } - bool is_not_dead() const { return !is_dead(); } - void die(Entity& owner) const; - - template - void serialize(Archive& archive) { - archive(defense_, hp_, max_hp_); - } - - private: - int defense_; - int hp_; - int max_hp_; -}; - -class TransformComponent { - public: - TransformComponent() = default; - TransformComponent(int x, int y) : position_({x, y}) {} - virtual ~TransformComponent() = default; - Vector2D get_position() const { return position_; } - void move(Vector2D new_position) { position_ = new_position; } - - template - void serialize(Archive& archive) { - archive(position_); - } - - private: - Vector2D position_; -}; - -class ASCIIComponent { - public: - ASCIIComponent() = default; - ASCIIComponent(std::string_view symbol, tcod::ColorRGB colour, int layer) - : symbol_(symbol), colour_(colour), layer_(layer) {} - virtual ~ASCIIComponent() = default; - - std::string_view get_symbol() const { return symbol_; } - tcod::ColorRGB get_colour() const { return colour_; } - int get_layer() const { return layer_; } - - template - void serialize(Archive& archive) { - archive(symbol_, colour_, layer_); - } - - private: - std::string symbol_; - tcod::ColorRGB colour_; - int layer_; -}; - -class Container { - private: - size_t size_; - std::vector inventory_; - - public: - Container() = default; - explicit Container(int size); - virtual ~Container() = default; - bool add(Entity* actor); - void remove(const Entity* actor); - std::vector get_inventory() const { return inventory_; } - size_t get_size() const { return size_; } - - template - void save(Archive& archive) const { - archive(size_); - archive(inventory_.size()); - for (auto& item : inventory_) { - item->pack(archive); - } - } - template - void load(Archive& archive) { - archive(size_); - int nb_items; - archive(nb_items); - for (int i = 0; i < nb_items; i++) { - Entity* entity = new Entity("", false, nullptr, nullptr); - entity->unpack(archive); - inventory_.emplace_back(entity); - } - } -}; - -class ConsumableComponent { - public: - ConsumableComponent() = default; - virtual ~ConsumableComponent() = default; - // TODO: should also be an action result - ActionResult pick_up(Entity* owner, Entity* wearer); - ActionResult drop(Entity* owner, Entity* wearer); - virtual ActionResult use(Entity* owner, Entity* wearer, World& world); - - template - void serialize(Archive&) const { - // nothing to archive - } - - protected: - enum class ConsumableType { HEALER, LIGHTNING_BOLT, CONFUSER, FIREBALL }; -}; - -class HealingConsumable final : public ConsumableComponent { - public: - HealingConsumable() = default; - explicit HealingConsumable(int amount); - ~HealingConsumable() override = default; - ActionResult use(Entity* owner, Entity* wearer, World& world) override; - template - void serialize(Archive& archive) { - archive(cereal::base_class(this), amount_); - } - - private: - int amount_; -}; - -class LightningBolt final : public ConsumableComponent { - private: - float range_; - float damage_; - - public: - LightningBolt() = default; - LightningBolt(float range, float damage) : range_(range), damage_(damage) {} - ~LightningBolt() override = default; - ActionResult use(Entity* owner, Entity* wearer, World& world) override; - - template - void serialize(Archive& archive) { - archive(cereal::base_class(this), range_, damage_); - } -}; - -class FireSpell final : public ConsumableComponent { - private: - float max_range_; - float aoe_; - float damage_; - - public: - FireSpell() = default; - FireSpell(float max_range, float aoe, float damage) - : max_range_(max_range), aoe_(aoe), damage_(damage) {} - ~FireSpell() override = default; - - ActionResult use(Entity* owner, Entity* Wearer, World& world) override; - template - void serialize(Archive& archive) { - archive( - cereal::base_class(this), - max_range_, - aoe_, - damage_); - } -}; - -class ConfusionSpell final : public ConsumableComponent { - private: - int num_turns_; - int max_range_; - - public: - ConfusionSpell() = default; - ConfusionSpell(int num_turns, int max_range) - : num_turns_(num_turns), max_range_(max_range) {} - ~ConfusionSpell() override = default; - - ActionResult use(Entity* owner, Entity* wearer, World& world) override; - - template - void serialize(Archive& archive) { - archive( - cereal::base_class(this), num_turns_, max_range_); - } -}; - -/** - * @brief StatsData - * Simple data structure to hold stats data - */ -struct StatsData { - int xp_; - int level_; - int level_up_base_; - int level_up_factor_; - int stats_points_; - - template - void serialize(Archive& archive) { - archive(xp_, level_, level_up_base_, level_up_factor_, stats_points_); - } -}; - -/** - * @brief StatsComponent - * Component used to manipulate stats data. - */ -class StatsComponent { - public: - StatsComponent() = default; - explicit StatsComponent(StatsData stats_data) : stats_data_(stats_data) {} - StatsComponent( - int xp, - int level, - int level_up_base, - int level_up_factor, - int stats_points) - : stats_data_{xp, level, level_up_base, level_up_factor, stats_points} {} - virtual ~StatsComponent() = default; - - int get_xp() const { return stats_data_.xp_; } - int get_level() const { return stats_data_.level_; } - int get_level_up_base() const { return stats_data_.level_up_base_; } - int get_level_up_factor() const { return stats_data_.level_up_factor_; } - void reduce_stats_points(int amount) { stats_data_.stats_points_ -= amount; } - int get_stats_points() const { return stats_data_.stats_points_; } - int get_next_level_xp() const { - return stats_data_.level_up_base_ + - stats_data_.level_ * stats_data_.level_up_factor_; - } - - void add_xp(int xp); - void level_up(); - - template - void serialize(Archive& archive) { - archive(stats_data_); - } - - private: - StatsData stats_data_; -}; - -} // namespace cpprl + class AttackComponent { + public: + AttackComponent() = default; + explicit AttackComponent(int damage) : damage_(damage) {} + virtual ~AttackComponent() = default; + int get_damage() const { return damage_; } + void boost_damage(int amount) { damage_ += amount; } + + template + void serialize(Archive& archive) { + archive(damage_); + } + + private: + int damage_; + }; + + class DefenseComponent { + public: + DefenseComponent() = default; + DefenseComponent(int defense, int maxHp) + : defense_(defense), hp_(maxHp), max_hp_(maxHp) {} + virtual ~DefenseComponent() = default; + + int get_hp() const { return hp_; } + int get_max_hp() const { return max_hp_; } + int get_defense() const { return defense_; } + void boost_defense(int amount) { defense_ += amount; } + + void take_damage(int damage) { hp_ -= damage; } + int heal(int amount); + bool is_dead() const { return hp_ <= 0; } + bool is_not_dead() const { return !is_dead(); } + void die(Entity& owner) const; + + template + void serialize(Archive& archive) { + archive(defense_, hp_, max_hp_); + } + + private: + int defense_; + int hp_; + int max_hp_; + }; + + class TransformComponent { + public: + TransformComponent() = default; + TransformComponent(int x, int y) : position_({x, y}) {} + virtual ~TransformComponent() = default; + Vector2D get_position() const { return position_; } + void move(Vector2D new_position) { position_ = new_position; } + + template + void serialize(Archive& archive) { + archive(position_); + } + + private: + Vector2D position_; + }; + + class ASCIIComponent { + public: + ASCIIComponent() = default; + ASCIIComponent(std::string_view symbol, tcod::ColorRGB colour, int layer) + : symbol_(symbol), colour_(colour), layer_(layer) {} + virtual ~ASCIIComponent() = default; + + std::string_view get_symbol() const { return symbol_; } + tcod::ColorRGB get_colour() const { return colour_; } + int get_layer() const { return layer_; } + + template + void serialize(Archive& archive) { + archive(symbol_, colour_, layer_); + } + + private: + std::string symbol_; + tcod::ColorRGB colour_; + int layer_; + }; + + class Container { + private: + size_t size_; + std::vector inventory_; + + public: + Container() = default; + explicit Container(int size); + virtual ~Container() = default; + bool add(Entity* actor); + void remove(const Entity* actor); + std::vector get_inventory() const { return inventory_; } + size_t get_size() const { return size_; } + + template + void save(Archive& archive) const { + archive(size_); + archive(inventory_.size()); + for (auto& item : inventory_) { + item->pack(archive); + } + } + template + void load(Archive& archive) { + archive(size_); + int nb_items; + archive(nb_items); + for (int i = 0; i < nb_items; i++) { + Entity* entity = new Entity("", false, nullptr, nullptr); + entity->unpack(archive); + inventory_.emplace_back(entity); + } + } + }; + + class ConsumableComponent { + public: + ConsumableComponent() = default; + virtual ~ConsumableComponent() = default; + // TODO: should also be an action result + ActionResult pick_up(Entity* owner, Entity* wearer); + ActionResult drop(Entity* owner, Entity* wearer); + virtual ActionResult use(Entity* owner, Entity* wearer, World& world); + + template + void serialize(Archive&) const { + // nothing to archive + } + + protected: + enum class ConsumableType { HEALER, LIGHTNING_BOLT, CONFUSER, FIREBALL }; + }; + + class HealingConsumable final : public ConsumableComponent { + public: + HealingConsumable() = default; + explicit HealingConsumable(int amount); + ~HealingConsumable() override = default; + ActionResult use(Entity* owner, Entity* wearer, World& world) override; + template + void serialize(Archive& archive) { + archive(cereal::base_class(this), amount_); + } + + private: + int amount_; + }; + + class LightningBolt final : public ConsumableComponent { + private: + float range_; + float damage_; + + public: + LightningBolt() = default; + LightningBolt(float range, float damage) : range_(range), damage_(damage) {} + ~LightningBolt() override = default; + ActionResult use(Entity* owner, Entity* wearer, World& world) override; + + template + void serialize(Archive& archive) { + archive(cereal::base_class(this), range_, damage_); + } + }; + + class FireSpell final : public ConsumableComponent { + private: + float max_range_; + float aoe_; + float damage_; + + public: + FireSpell() = default; + FireSpell(float max_range, float aoe, float damage) + : max_range_(max_range), aoe_(aoe), damage_(damage) {} + ~FireSpell() override = default; + + ActionResult use(Entity* owner, Entity* Wearer, World& world) override; + template + void serialize(Archive& archive) { + archive( + cereal::base_class(this), + max_range_, + aoe_, + damage_); + } + }; + + class ConfusionSpell final : public ConsumableComponent { + private: + int num_turns_; + int max_range_; + + public: + ConfusionSpell() = default; + ConfusionSpell(int num_turns, int max_range) + : num_turns_(num_turns), max_range_(max_range) {} + ~ConfusionSpell() override = default; + + ActionResult use(Entity* owner, Entity* wearer, World& world) override; + + template + void serialize(Archive& archive) { + archive( + cereal::base_class(this), num_turns_, max_range_); + } + }; + + /** + * @brief StatsData + * Simple data structure to hold stats data + */ + struct StatsData { + int xp_; + int level_; + int level_up_base_; + int level_up_factor_; + int stats_points_; + + template + void serialize(Archive& archive) { + archive(xp_, level_, level_up_base_, level_up_factor_, stats_points_); + } + }; + + /** + * @brief StatsComponent + * Component used to manipulate stats data. + */ + class StatsComponent { + public: + StatsComponent() = default; + explicit StatsComponent(StatsData stats_data) : stats_data_(stats_data) {} + StatsComponent( + int xp, + int level, + int level_up_base, + int level_up_factor, + int stats_points) + : stats_data_{xp, level, level_up_base, level_up_factor, stats_points} {} + virtual ~StatsComponent() = default; + + int get_xp() const { return stats_data_.xp_; } + int get_level() const { return stats_data_.level_; } + int get_level_up_base() const { return stats_data_.level_up_base_; } + int get_level_up_factor() const { return stats_data_.level_up_factor_; } + void reduce_stats_points(int amount) { stats_data_.stats_points_ -= amount; } + int get_stats_points() const { return stats_data_.stats_points_; } + int get_next_level_xp() const { + return stats_data_.level_up_base_ + + stats_data_.level_ * stats_data_.level_up_factor_; + } + + void add_xp(int xp); + void level_up(); + + template + void serialize(Archive& archive) { + archive(stats_data_); + } + + private: + StatsData stats_data_; + }; +} CEREAL_REGISTER_TYPE(cpprl::HealingConsumable); CEREAL_REGISTER_TYPE(cpprl::LightningBolt); diff --git a/lib/include/components/components.hpp b/lib/include/components/components.hpp new file mode 100644 index 0000000..edc4918 --- /dev/null +++ b/lib/include/components/components.hpp @@ -0,0 +1,16 @@ +#pragma once +#include + +namespace SupaRL { + + /** + * @brief The StatusConditionComponent struct + * This struct is used to store the status condition of an entity. + */ + struct StatusConditionComponent { + short damage_per_turn_; + short turns_remaining_; + short turns_until_update_; + std::string name_; + }; +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1b3ebb7..53ac1e6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -22,6 +22,7 @@ add_executable(roguelike_tests ${cpprl_tests_SOURCE_FILES}) target_link_libraries(roguelike_tests PRIVATE GTest::gtest_main + roguelike cpprl) include(GoogleTest) diff --git a/tests/src/alltests.cpp b/tests/src/alltests.cpp index 1659be8..1b8894e 100644 --- a/tests/src/alltests.cpp +++ b/tests/src/alltests.cpp @@ -3,4 +3,10 @@ int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); -} \ No newline at end of file +} + +TEST(Addition, PositiveNumbers) { + EXPECT_EQ(2, 1 + 1); + EXPECT_EQ(3, 1 + 2); + EXPECT_EQ(4, 2 + 2); +} diff --git a/tests/src/status_condition_systems_tests.cpp b/tests/src/status_condition_systems_tests.cpp index fdf0cad..f7ab7b6 100644 --- a/tests/src/status_condition_systems_tests.cpp +++ b/tests/src/status_condition_systems_tests.cpp @@ -1,11 +1,32 @@ +#include "gtest/gtest.h" +#include "components.hpp" /** * @file status_condition_systems_tests.cpp */ /** - * It should remove 1 hp from the player when afflicted with - * a status condition that deals damage. + * Given an Entity + * And the entity has a StatusConditionComponent + * When the StatusConditionSystem is updated + * Then the entity should take damage from the status condition */ +TEST(StatusConditionSystem, TakeDamage) { + // Setup + auto entity = 1; + SupaRL::StatusConditionComponent statusConditionComponent { +damege_per_turn_: 1, +turns_remaining_: 1, +turns_until_update_: 1, +name_: "Bleed" + }; + // Add the StatusConditionComponent to the entity + // AddEntityComponent(entity, statusConditionComponent); + // Update the StatusConditionSystem + // StatusConditionSystem::Update(); + + // Verify the entity has taken damage + // EXPECT_EQ(GetEntityHealth(entity), 99); +} From 030e83f6d4423e133c5b3e41a2fd07cccebbcd62 Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Tue, 8 Oct 2024 20:16:04 +0100 Subject: [PATCH 03/38] fix: struct init --- tests/src/status_condition_systems_tests.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/src/status_condition_systems_tests.cpp b/tests/src/status_condition_systems_tests.cpp index f7ab7b6..33ecf15 100644 --- a/tests/src/status_condition_systems_tests.cpp +++ b/tests/src/status_condition_systems_tests.cpp @@ -1,5 +1,5 @@ #include "gtest/gtest.h" -#include "components.hpp" +#include "../../lib/include/components/components.hpp" /** * @file status_condition_systems_tests.cpp @@ -15,11 +15,11 @@ TEST(StatusConditionSystem, TakeDamage) { // Setup auto entity = 1; - SupaRL::StatusConditionComponent statusConditionComponent { -damege_per_turn_: 1, -turns_remaining_: 1, -turns_until_update_: 1, -name_: "Bleed" + SupaRL::StatusConditionComponent statusConditionComponent = { + .damege_per_turn_= 1, + .turns_remaining_= 1, + .turns_until_update_= 1, + .name_= "Bleed" }; // Add the StatusConditionComponent to the entity // AddEntityComponent(entity, statusConditionComponent); From 59ac6e26dbf1222abff4069682f3a88ecb2012b8 Mon Sep 17 00:00:00 2001 From: daniel baker Date: Wed, 9 Oct 2024 14:42:26 +0100 Subject: [PATCH 04/38] fix: working build and ecs. --- app/include/combat_system.hpp | 40 +-- app/include/engine.hpp | 4 +- app/include/entity_manager.hpp | 3 + app/include/game_entity.hpp | 3 + app/src/engine.cpp | 258 ++++++++++-------- app/src/entity_factory.cpp | 140 +++++++--- app/src/entity_manager.cpp | 4 + app/src/events/command.cpp | 9 +- app/src/world.cpp | 189 +++++++------ lib/include/components/attack.hpp | 7 + lib/include/components/defence.hpp | 9 + lib/include/components/status_condition.hpp | 12 + lib/include/components/transform.hpp | 9 + lib/include/components/velocity.hpp | 9 + lib/include/core/component_array.hpp | 58 ++-- lib/include/core/component_manager.hpp | 48 ++-- lib/include/core/coordinator.hpp | 214 +++++++-------- lib/include/core/entity_manager.hpp | 38 +-- lib/include/core/event.hpp | 18 +- lib/include/core/event_manager.hpp | 16 +- lib/include/core/math.hpp | 32 +++ lib/include/core/system.hpp | 3 +- lib/include/core/system_manager.hpp | 39 +-- lib/include/systems/combat_system.hpp | 16 ++ lib/include/systems/physics_system.hpp | 15 + .../systems/status_condition_system.hpp | 16 ++ lib/include/utils/logger.hpp | 25 ++ lib/src/systems/combat_system.cpp | 15 + lib/src/systems/physics_system.cpp | 28 ++ lib/src/systems/status_condition_system.cpp | 33 +++ tests/CMakeLists.txt | 1 + tests/src/status_condition_systems_tests.cpp | 5 + 32 files changed, 840 insertions(+), 476 deletions(-) create mode 100644 lib/include/components/attack.hpp create mode 100644 lib/include/components/defence.hpp create mode 100644 lib/include/components/status_condition.hpp create mode 100644 lib/include/components/transform.hpp create mode 100644 lib/include/components/velocity.hpp create mode 100644 lib/include/core/math.hpp create mode 100644 lib/include/systems/combat_system.hpp create mode 100644 lib/include/systems/physics_system.hpp create mode 100644 lib/include/systems/status_condition_system.hpp create mode 100644 lib/include/utils/logger.hpp create mode 100644 lib/src/systems/combat_system.cpp create mode 100644 lib/src/systems/physics_system.cpp create mode 100644 lib/src/systems/status_condition_system.cpp diff --git a/app/include/combat_system.hpp b/app/include/combat_system.hpp index 22a16bd..4ce1606 100644 --- a/app/include/combat_system.hpp +++ b/app/include/combat_system.hpp @@ -1,28 +1,28 @@ -#ifndef INCLUDE_COMBAT_SYSTEM_HPP_ -#define INCLUDE_COMBAT_SYSTEM_HPP_ +#pragma once #include "components.hpp" #include "game_entity.hpp" namespace cpprl::combat_system { -inline auto handle_attack = [](Entity& attacker, Entity& target) -> int { - int damage = attacker.get_attack_component().get_damage() - - target.get_defense_component().get_defense(); - if (damage > 0) { - target.get_defense_component().take_damage(damage); - return damage; - } - return 0; -}; + inline auto handle_attack = [](Entity& attacker, Entity& target) -> int { + int damage = attacker.get_attack_component().get_damage() - + target.get_defense_component().get_defense(); + if (damage > 0) { + target.get_defense_component().take_damage(damage); + return damage; + } + return 0; + }; -inline auto handle_spell = [](int power, Entity& target) -> int { - int damage = power - target.get_defense_component().get_defense(); - if (damage > 0) { - target.get_defense_component().take_damage(damage); - return damage; - } - return 0; -}; + inline auto handle_spell = [](int power, Entity& target) -> int { + int damage = power - target.get_defense_component().get_defense(); + if (damage > 0) { + target.get_defense_component().take_damage(damage); + return damage; + } + return 0; + }; } -#endif // INCLUDE_COMBAT_SYSTEM_HPP_ + + diff --git a/app/include/engine.hpp b/app/include/engine.hpp index 2c8916a..9e32b46 100644 --- a/app/include/engine.hpp +++ b/app/include/engine.hpp @@ -12,6 +12,7 @@ #include "gui.hpp" #include "message_log.hpp" #include "rendering.hpp" +#include "systems/physics_system.hpp" namespace cpprl { @@ -21,13 +22,14 @@ class GameActor; class Map; class State; -// singleton engine class Engine { private: std::unique_ptr renderer_; tcod::Context context_; std::unique_ptr world_; std::unique_ptr engine_state_; + std::shared_ptr physics_system_; + int argc_; char** argv_; diff --git a/app/include/entity_manager.hpp b/app/include/entity_manager.hpp index c36b1c6..dc89c32 100644 --- a/app/include/entity_manager.hpp +++ b/app/include/entity_manager.hpp @@ -3,6 +3,7 @@ #include #include #include +#include "../../lib/include/core/entity_manager.hpp" #include "colours.hpp" #include "entity_factory.hpp" @@ -19,6 +20,7 @@ class EntityManager { EntityManager() { orc_factory_ = std::make_unique(); troll_factory_ = std::make_unique(); + entity_manager_ = SupaRL::EntityManager(); }; EntityManager( @@ -82,6 +84,7 @@ class EntityManager { private: std::vector entities_; + SupaRL::EntityManager entity_manager_; std::unique_ptr orc_factory_; std::unique_ptr troll_factory_; }; diff --git a/app/include/game_entity.hpp b/app/include/game_entity.hpp index 617d040..cf17c47 100644 --- a/app/include/game_entity.hpp +++ b/app/include/game_entity.hpp @@ -42,6 +42,9 @@ namespace cpprl { ~Entity() = default; + void set_id(SupaRL::Entity id) { id_ = id; }; + SupaRL::Entity get_id() { return id_; }; + TransformComponent& get_transform_component() { return *transformComponent_; }; diff --git a/app/src/engine.cpp b/app/src/engine.cpp index 34322a0..c37731b 100644 --- a/app/src/engine.cpp +++ b/app/src/engine.cpp @@ -7,141 +7,185 @@ #include #include #include -#include #include #include #include "engine.hpp" -#include "events/command.hpp" #include "exceptions.hpp" #include "serialization/json_serializer_strategy.hpp" #include "state.hpp" -#include "types/math.hpp" #include "types/state_result.hpp" #include "world.hpp" +#include "core/types.hpp" +#include "core/coordinator.hpp" +#include "components/defence.hpp" +#include "components/attack.hpp" +#include "components/transform.hpp" +#include "components/status_condition.hpp" +#include "components/velocity.hpp" +#include "systems/status_condition_system.hpp" +#include "systems/combat_system.hpp" +#include "core/types.hpp" + +SupaRL::Coordinator g_coordinator; namespace cpprl { -Engine::Engine(){}; -Engine::~Engine(){}; - -Engine& Engine::get_instance() { - static Engine instance_; - return instance_; -} - -void Engine::init(int argc, char** argv) { - argc_ = argc; - argv_ = argv; - renderer_ = std::make_unique(argc, argv); - engine_state_ = std::make_unique( - *world_, new MainMenuWindow(60, 35, {0, 0})); - engine_state_->on_enter(); -} - -void Engine::save() { - std::filesystem::create_directories(std::filesystem::path("saves")); - if (world_->get_player()->get_defense_component().is_dead()) { - std::filesystem::remove(std::filesystem::path("saves/game.sav")); - - } else { - // TODO: this doesn't work with binary serialization - // but it works fine for JSON ?! - serialization::JsonSerializerStrategy serializer("saves/game.sav"); - serializer.serialize(*world_); + Engine::Engine(){}; + Engine::~Engine(){}; + + + Engine& Engine::get_instance() { + static Engine instance_; + return instance_; } + + void Engine::init(int argc, char** argv) { + argc_ = argc; + argv_ = argv; + renderer_ = std::make_unique(argc, argv); + engine_state_ = std::make_unique( + *world_, new MainMenuWindow(60, 35, {0, 0})); + engine_state_->on_enter(); + g_coordinator.init(); + + g_coordinator.register_component(); + g_coordinator.register_component(); + g_coordinator.register_component(); + g_coordinator.register_component(); + g_coordinator.register_component(); + + /*g_coordinator.register_system();*/ + /*{*/ + /* SupaRL::Signature signature;*/ + /* signature.set(g_coordinator.get_component_type());*/ + /* signature.set(g_coordinator.get_component_type());*/ + /* g_coordinator.set_system_signature(signature);*/ + /*}*/ + /**/ + /*g_coordinator.register_system();*/ + /*{*/ + /* SupaRL::Signature signature;*/ + /* signature.set(g_coordinator.get_component_type());*/ + /* signature.set(g_coordinator.get_component_type());*/ + /* g_coordinator.set_system_signature(signature);*/ + /*}*/ + + physics_system_ = g_coordinator.register_system(); + { + SupaRL::Signature signature; + signature.set(g_coordinator.get_component_type()); + signature.set(g_coordinator.get_component_type()); + g_coordinator.set_system_signature(signature); + } + std::cout << "Engine initialized" << std::endl; + } + + void Engine::save() { + std::filesystem::create_directories(std::filesystem::path("saves")); + if (world_->get_player()->get_defense_component().is_dead()) { + std::filesystem::remove(std::filesystem::path("saves/game.sav")); + + } else { + // TODO: this doesn't work with binary serialization + // but it works fine for JSON ?! + serialization::JsonSerializerStrategy serializer("saves/game.sav"); + serializer.serialize(*world_); + } #ifdef __EMSCRIPTEN__ - // clang-format off - EM_ASM( - FS.syncfs(false, function (err) { - assert(!err); - console.log("SyncFS finished."); - }); - ); - // clang-format on + // clang-format off + EM_ASM( + FS.syncfs(false, function (err) { + assert(!err); + console.log("SyncFS finished."); + }); + ); + // clang-format on #endif -} + } -void Engine::load() { - if (std::filesystem::exists(std::filesystem::path("saves/game.sav"))) { - // TODO: maybe I can use the build flags to use a - // different strategy for web vs native? - serialization::JsonSerializerStrategy serializer("saves/game.sav"); - world_ = std::make_unique(); - // TODO: Web serialization is completely broken. Get a memory error - // when trying to load. Can't even inspect the file in the browser. - // get's as far as setting the dungeon seed and then blows up. - serializer.deserialize(*world_); + void Engine::load() { + if (std::filesystem::exists(std::filesystem::path("saves/game.sav"))) { + // TODO: maybe I can use the build flags to use a + // different strategy for web vs native? + serialization::JsonSerializerStrategy serializer("saves/game.sav"); + world_ = std::make_unique(); + // TODO: Web serialization is completely broken. Get a memory error + // when trying to load. Can't even inspect the file in the browser. + // gets as far as setting the dungeon seed and then blows up. + serializer.deserialize(*world_); - engine_state_->on_exit(); - engine_state_ = std::make_unique(*world_); - engine_state_->on_enter(); - } else { - init(argc_, argv_); + engine_state_->on_exit(); + engine_state_ = std::make_unique(*world_); + engine_state_->on_enter(); + } else { + init(argc_, argv_); + } } -} -void Engine::handle_events() { - SDL_Event event; + void Engine::handle_events() { + SDL_Event event; #ifndef __EMSCRIPTEN__ - // Block until events exist. This conserves resources well but isn't - // compatible with animations or Emscripten. - SDL_WaitEvent(nullptr); + // Block until events exist. This conserves resources well but isn't + // compatible with animations or Emscripten. + SDL_WaitEvent(nullptr); #endif - while (SDL_PollEvent(&event)) { - // call on_update of state which can return change - if (event.type == SDL_KEYDOWN || event.type == SDL_MOUSEMOTION || - event.type == SDL_MOUSEBUTTONDOWN) { - try { - StateResult result = engine_state_->on_update(event); - if (std::holds_alternative(result)) { - } else if (std::holds_alternative(result)) { - engine_state_->on_exit(); - engine_state_ = std::move(std::get(result).next_state); - engine_state_->on_enter(); - } else if (std::holds_alternative(result)) { - reset_game(); - } else if (std::holds_alternative(result)) { - load(); - } else if (std::holds_alternative(result)) { - world_->handle_enemy_turns(); - if (world_->get_player()->get_defense_component().is_dead()) { + while (SDL_PollEvent(&event)) { + // call on_update of state which can return change + if (event.type == SDL_KEYDOWN || event.type == SDL_MOUSEMOTION || + event.type == SDL_MOUSEBUTTONDOWN) { + try { + StateResult result = engine_state_->on_update(event); + if (std::holds_alternative(result)) { + } else if (std::holds_alternative(result)) { engine_state_->on_exit(); - engine_state_ = std::make_unique(*world_); + engine_state_ = std::move(std::get(result).next_state); engine_state_->on_enter(); + } else if (std::holds_alternative(result)) { + reset_game(); + } else if (std::holds_alternative(result)) { + load(); + } else if (std::holds_alternative(result)) { + world_->handle_enemy_turns(); + if (world_->get_player()->get_defense_component().is_dead()) { + engine_state_->on_exit(); + engine_state_ = std::make_unique(*world_); + engine_state_->on_enter(); + } + physics_system_->update(); + std::cout << "End turn" << std::endl; + } else if (std::holds_alternative(result)) { + // TODO: there's a bug here. We should only save + // when exiting the game, not when quitting to the main menu. + save(); + std::exit(EXIT_SUCCESS); + } else if (std::holds_alternative(result)) { + world_->get_message_log().add_message( + std::get(result).message, WHITE); } - } else if (std::holds_alternative(result)) { - // TODO: there's a bug here. We should only save - // when exiting the game, not when quitting to the main menu. - save(); - std::exit(EXIT_SUCCESS); - } else if (std::holds_alternative(result)) { - world_->get_message_log().add_message( - std::get(result).message, WHITE); + } catch (Impossible& e) { + world_->get_message_log().add_message(e.what(), RED); } - } catch (Impossible& e) { - world_->get_message_log().add_message(e.what(), RED); + } else if (event.type == SDL_QUIT) { + std::exit(EXIT_SUCCESS); } - } else if (event.type == SDL_QUIT) { - std::exit(EXIT_SUCCESS); } } -} -void Engine::render() { - if (world_) { - world_->render(*renderer_); + void Engine::render() { + if (world_) { + world_->render(*renderer_); + } + engine_state_->render(*renderer_); + g_context.present(g_console); + } + + void Engine::reset_game() { + world_ = std::make_unique(); + engine_state_->on_exit(); + engine_state_ = std::make_unique(*world_); + world_->generate_map(80, 35, true); + world_->spawn_player(); + engine_state_->on_enter(); } - engine_state_->render(*renderer_); - g_context.present(g_console); -} - -void Engine::reset_game() { - world_ = std::make_unique(); - engine_state_->on_exit(); - engine_state_ = std::make_unique(*world_); - world_->generate_map(80, 35, true); - world_->spawn_player(); - engine_state_->on_enter(); -} } // namespace cpprl diff --git a/app/src/entity_factory.cpp b/app/src/entity_factory.cpp index 574bb75..493cc8a 100644 --- a/app/src/entity_factory.cpp +++ b/app/src/entity_factory.cpp @@ -3,50 +3,102 @@ #include "basic_ai_component.hpp" #include "colours.hpp" #include "components.hpp" +#include "core/coordinator.hpp" +#include "components/defence.hpp" +#include "components/attack.hpp" +#include "components/transform.hpp" +#include "components/velocity.hpp" + +extern SupaRL::Coordinator g_coordinator; namespace cpprl { -Entity* AbstractEntityFactory::create_base( - const std::string& name, tcod::ColorRGB color, std::string_view symbol) { - return new Entity( - name, - true, - std::make_unique(0, 0), - std::make_unique(symbol, color, 1)); -} - -Entity* OrcFactory::create() { - Entity* entity = create_base("Orc", DARK_GREEN, "o"); - - entity->set_attack_component(std::make_unique(3)); - entity->set_defense_component(std::make_unique(0, 10)); - entity->set_ai_component(std::make_unique()); - entity->set_stats_component( - std::make_unique(10, 1, 10, 10, 2)); - - return entity; -} - -Entity* TrollFactory::create() { - Entity* entity = create_base("Troll", DARK_GREEN, "T"); - - entity->set_attack_component(std::make_unique(4)); - entity->set_defense_component(std::make_unique(1, 16)); - entity->set_ai_component(std::make_unique()); - entity->set_stats_component( - std::make_unique(20, 1, 10, 20, 2)); - - return entity; -} - -Entity* PlayerFactory::create() { - Entity* entity = create_base("Player", TEAL, "@"); - - entity->set_attack_component(std::make_unique(5)); - entity->set_defense_component(std::make_unique(2, 30)); - entity->set_container(std::make_unique(26)); - entity->set_stats_component( - std::make_unique(0, 1, 150, 150, 2)); - - return entity; -} + Entity* AbstractEntityFactory::create_base( + const std::string& name, tcod::ColorRGB color, std::string_view symbol) { + return new Entity( + name, + true, + std::make_unique(0, 0), + std::make_unique(symbol, color, 1)); + } + + Entity* OrcFactory::create() { + Entity* entity = create_base("Orc", DARK_GREEN, "o"); + + entity->set_attack_component(std::make_unique(3)); + entity->set_defense_component(std::make_unique(0, 10)); + entity->set_ai_component(std::make_unique()); + entity->set_stats_component( + std::make_unique(10, 1, 10, 10, 2)); + + SupaRL::Entity entity_id = g_coordinator.create_entity(); + entity->set_id(entity_id); + + g_coordinator.add_component(entity_id, SupaRL::DefenceComponent{ + .defence_ = 0, + .hp_ = 10, + .max_hp_ = 10}); + g_coordinator.add_component(entity_id, SupaRL::AttackComponent{ + .damage_ = 3}); + g_coordinator.add_component(entity_id, SupaRL::TransformComponent{ + .position_ = {0, 0}}); + g_coordinator.add_component( + entity_id, SupaRL::VelocityComponent{ + .velocity_ = {0,0}}); + + return entity; + } + + Entity* TrollFactory::create() { + Entity* entity = create_base("Troll", DARK_GREEN, "T"); + + entity->set_attack_component(std::make_unique(4)); + entity->set_defense_component(std::make_unique(1, 16)); + entity->set_ai_component(std::make_unique()); + entity->set_stats_component( + std::make_unique(20, 1, 10, 20, 2)); + + SupaRL::Entity entity_id = g_coordinator.create_entity(); + entity->set_id(entity_id); + + g_coordinator.add_component(entity_id, SupaRL::DefenceComponent{ + .defence_ = 1, + .hp_ = 16, + .max_hp_ = 16}); + g_coordinator.add_component(entity_id, SupaRL::AttackComponent{ + .damage_ = 4}); + g_coordinator.add_component(entity_id, SupaRL::TransformComponent{ + .position_ = {0, 0}}); + g_coordinator.add_component( + entity_id, SupaRL::VelocityComponent{ + .velocity_ = {0,0}}); + + return entity; + } + + Entity* PlayerFactory::create() { + Entity* entity = create_base("Player", TEAL, "@"); + + entity->set_attack_component(std::make_unique(5)); + entity->set_defense_component(std::make_unique(2, 30)); + entity->set_container(std::make_unique(26)); + entity->set_stats_component( + std::make_unique(0, 1, 150, 150, 2)); + + SupaRL::Entity entity_id = g_coordinator.create_entity(); + entity->set_id(entity_id); + + g_coordinator.add_component(entity_id, SupaRL::DefenceComponent{ + .defence_ = 2, + .hp_ = 30, + .max_hp_ = 30}); + g_coordinator.add_component(entity_id, SupaRL::AttackComponent{ + .damage_ = 5}); + g_coordinator.add_component(entity_id, SupaRL::TransformComponent{ + .position_ = {0, 0}}); + g_coordinator.add_component( + entity_id, SupaRL::VelocityComponent{ + .velocity_ = {0,0}}); + + return entity; + } } // namespace cpprl diff --git a/app/src/entity_manager.cpp b/app/src/entity_manager.cpp index d5c616c..a5ef0b6 100644 --- a/app/src/entity_manager.cpp +++ b/app/src/entity_manager.cpp @@ -11,7 +11,11 @@ #include "consumable_factory.hpp" #include "util.hpp" +#include "../../lib/include/core/coordinator.hpp" + + namespace cpprl { +extern SupaRL::Coordinator g_coordinator; void EntityManager::clear() { entities_.clear(); } diff --git a/app/src/events/command.cpp b/app/src/events/command.cpp index 962275b..1dc4da6 100644 --- a/app/src/events/command.cpp +++ b/app/src/events/command.cpp @@ -4,13 +4,15 @@ #include "combat_system.hpp" #include "entity_manager.hpp" -#include "exceptions.hpp" #include "game_entity.hpp" #include "gui.hpp" #include "input_handler.hpp" #include "state.hpp" #include "world.hpp" +#include "core/coordinator.hpp" +#include "components/velocity.hpp" +extern SupaRL::Coordinator g_coordinator; namespace cpprl { StateResult PickupCommand::execute() { @@ -213,7 +215,12 @@ namespace cpprl { } if (map.is_walkable(new_position)) { + entity_->get_transform_component().move(new_position); + + g_coordinator.get_component( + entity_->get_id()).velocity_ = {move_vector_.x, move_vector_.y}; + } else { return NoOp{"You can't walk on that."}; } diff --git a/app/src/world.cpp b/app/src/world.cpp index fcdc8f8..8472c79 100644 --- a/app/src/world.cpp +++ b/app/src/world.cpp @@ -6,115 +6,124 @@ #include "dungeon.hpp" #include "entity_factory.hpp" #include "entity_manager.hpp" -#include "exceptions.hpp" #include "game_entity.hpp" -#include "health_bar.hpp" -#include "ui/dungeon_level.hpp" +#include "core/coordinator.hpp" +#include "components/transform.hpp" -namespace cpprl { -// TODO: When loading from state this constructor is not called -// need to ensure the orc and troll factories are set. -World::World() { - entities_ = std::make_unique( - std::make_unique(), std::make_unique()); - controller_ = std::make_unique(); - ui_ = std::make_unique(dungeon_); - message_log_.add_message("Welcome to your eternal doom!", WHITE); - // TODO: add help menu - // message_log_.add_message("Press '?' for help.", WHITE); - message_log_.add_message("Press 'ESC' to quit.", WHITE); - message_log_.add_message("Press 'i' to open your inventory.", WHITE); - message_log_.add_message("Press 'g' to pick up items.", WHITE); - message_log_.add_message("Press 'd' in the inventory to drop items.", WHITE); - message_log_.add_message("Press 'w' 'a' 's' 'd' to move.", WHITE); - message_log_.add_message("Press 'v' to view your adventure log", WHITE); - message_log_.add_message("Now, fly you fool!", WHITE); -} -void World::generate_map(int width, int height, bool with_entities) { - // TODO: will need to pass the seed here - dungeon_.generate(DungeonConfig{30, 6, 10, width, height, 2}); +extern SupaRL::Coordinator g_coordinator; - if (!with_entities) { - return; +namespace cpprl { + // TODO: When loading from state this constructor is not called + // need to ensure the orc and troll factories are set. + World::World() { + entities_ = std::make_unique( + std::make_unique(), std::make_unique()); + controller_ = std::make_unique(); + ui_ = std::make_unique(dungeon_); + message_log_.add_message("Welcome to your eternal doom!", WHITE); + // TODO: add help menu + // message_log_.add_message("Press '?' for help.", WHITE); + message_log_.add_message("Press 'ESC' to quit.", WHITE); + message_log_.add_message("Press 'i' to open your inventory.", WHITE); + message_log_.add_message("Press 'g' to pick up items.", WHITE); + message_log_.add_message("Press 'd' in the inventory to drop items.", WHITE); + message_log_.add_message("Press 'w' 'a' 's' 'd' to move.", WHITE); + message_log_.add_message("Press 'v' to view your adventure log", WHITE); + message_log_.add_message("Now, fly you fool!", WHITE); } + void World::generate_map(int width, int height, bool with_entities) { + // TODO: will need to pass the seed here + dungeon_.generate(DungeonConfig{30, 6, 10, width, height, 2}); + + if (!with_entities) { + return; + } - std::vector rooms = dungeon_.get_map().get_rooms(); - size_t room_count = rooms.size(); - entities_->reserve(room_count * 2); - for (auto it = rooms.begin() + 1; it != rooms.end(); ++it) { - entities_->place_entities(*it, 2, 1); + std::vector rooms = dungeon_.get_map().get_rooms(); + size_t room_count = rooms.size(); + entities_->reserve(room_count * 2); + for (auto it = rooms.begin() + 1; it != rooms.end(); ++it) { + entities_->place_entities(*it, 2, 1); + } } -} -void World::render(Renderer& renderer) { - dungeon_.get_map().compute_fov( - player_->get_transform_component().get_position(), 10); - dungeon_.get_map().render(g_console); + void World::render(Renderer& renderer) { + dungeon_.get_map().compute_fov( + player_->get_transform_component().get_position(), 10); + dungeon_.get_map().render(g_console); - for (const auto& entity : *entities_) { - if (dungeon_.get_map().is_in_fov( + for (const auto& entity : *entities_) { + if (dungeon_.get_map().is_in_fov( entity->get_transform_component().get_position())) { - renderer.render( - entity->get_sprite_component(), entity->get_transform_component()); + renderer.render( + entity->get_sprite_component(), entity->get_transform_component()); + } } - } - ui_->render(g_console); - - message_log_.render(g_console, 23, 35, 45, 5); - // TODO: this is not working since cursor is not being set - auto entities_at = entities_->get_entities_at(controller_->cursor); - if (!entities_at.empty()) { - std::string names; - for (std::reference_wrapper entity_reference_wrapper : - entities_at) { - const Entity& entity = entity_reference_wrapper.get(); - names += entity.get_name() + ", "; - tcod::print_rect( - g_console, - {controller_->cursor.x, controller_->cursor.y - 1, 20, 1}, - names, - WHITE, - std::nullopt, - TCOD_LEFT); + ui_->render(g_console); + + message_log_.render(g_console, 23, 35, 45, 5); + // TODO: this is not working since cursor is not being set + auto entities_at = entities_->get_entities_at(controller_->cursor); + if (!entities_at.empty()) { + std::string names; + for (std::reference_wrapper entity_reference_wrapper : + entities_at) { + const Entity& entity = entity_reference_wrapper.get(); + names += entity.get_name() + ", "; + tcod::print_rect( + g_console, + {controller_->cursor.x, controller_->cursor.y - 1, 20, 1}, + names, + WHITE, + std::nullopt, + TCOD_LEFT); + } } } -} -void World::handle_enemy_turns() { - for (const auto& entity : *entities_) { - const std::optional> ai_component = + void World::handle_enemy_turns() { + for (const auto& entity : *entities_) { + const std::optional> ai_component = entity->get_ai_component(); - if (ai_component.has_value() && - entity->get_defense_component().is_not_dead()) { - entity->update(*this); + if (ai_component.has_value() && + entity->get_defense_component().is_not_dead()) { + entity->update(*this); + } } } -} -void World::spawn_player() { - auto player_factory_ = std::make_unique(); - Entity* player = player_factory_->create(); - player->get_transform_component().move( - dungeon_.get_map().get_rooms().at(0).get_center()); - World::spawn_player(player); -} + void World::spawn_player() { + auto player_factory_ = std::make_unique(); + Entity* player = player_factory_->create(); -void World::spawn_player(Entity* player) { - player_ = entities_->spawn(player); - dungeon_.get_map().compute_fov( - player_->get_transform_component().get_position(), 4); - DefenseComponent& player_defense = player_->get_defense_component(); - ui_->set_health_bar(player_defense); - ui_->set_xp_bar(player_->get_stats_component().value().get()); - entities_->shrink_to_fit(); -} + auto transform = g_coordinator.get_component( + player->get_id()); -void World::reset() { entities_->clear(); } + transform.position_ = SupaRL::Vector2D{ + .x = dungeon_.get_map().get_rooms().at(0).get_center().x, + .y = dungeon_.get_map().get_rooms().at(0).get_center().y + }; + player->get_transform_component().move( + dungeon_.get_map().get_rooms().at(0).get_center()); + World::spawn_player(player); + } -void World::scroll_current_view(int scroll_amount) { - if (current_window_) { - current_window_->set_cursor(current_window_->get_cursor() + scroll_amount); + void World::spawn_player(Entity* player) { + player_ = entities_->spawn(player); + dungeon_.get_map().compute_fov( + player_->get_transform_component().get_position(), 4); + DefenseComponent& player_defense = player_->get_defense_component(); + ui_->set_health_bar(player_defense); + ui_->set_xp_bar(player_->get_stats_component().value().get()); + entities_->shrink_to_fit(); } -} -} // namespace cpprl + void World::reset() { entities_->clear(); } + + void World::scroll_current_view(int scroll_amount) { + if (current_window_) { + current_window_->set_cursor(current_window_->get_cursor() + scroll_amount); + } + } + +} diff --git a/lib/include/components/attack.hpp b/lib/include/components/attack.hpp new file mode 100644 index 0000000..548504f --- /dev/null +++ b/lib/include/components/attack.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace SupaRL{ + struct AttackComponent { + short damage_; + }; +} diff --git a/lib/include/components/defence.hpp b/lib/include/components/defence.hpp new file mode 100644 index 0000000..18ab457 --- /dev/null +++ b/lib/include/components/defence.hpp @@ -0,0 +1,9 @@ +#pragma once + +namespace SupaRL{ + struct DefenceComponent { + short defence_; + short hp_; + short max_hp_; + }; +} diff --git a/lib/include/components/status_condition.hpp b/lib/include/components/status_condition.hpp new file mode 100644 index 0000000..cfbd5f3 --- /dev/null +++ b/lib/include/components/status_condition.hpp @@ -0,0 +1,12 @@ +#pragma once +#include + +namespace SupaRL{ + struct StatusConditionComponent { + short damage_per_tick_; + short duration_; + short ticks_; + std::string name_; + }; + +} diff --git a/lib/include/components/transform.hpp b/lib/include/components/transform.hpp new file mode 100644 index 0000000..5ec7831 --- /dev/null +++ b/lib/include/components/transform.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include "core/math.hpp" + +namespace SupaRL { + struct TransformComponent { + Vector2D position_; + }; +} diff --git a/lib/include/components/velocity.hpp b/lib/include/components/velocity.hpp new file mode 100644 index 0000000..4f9aee4 --- /dev/null +++ b/lib/include/components/velocity.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include "core/math.hpp" + +namespace SupaRL{ + struct VelocityComponent { + Vector2D velocity_; + }; +} diff --git a/lib/include/core/component_array.hpp b/lib/include/core/component_array.hpp index cc6e09f..02b4df4 100644 --- a/lib/include/core/component_array.hpp +++ b/lib/include/core/component_array.hpp @@ -10,7 +10,7 @@ namespace SupaRL { { public: virtual ~IComponentArray() = default; - virtual void EntityDestroyed(Entity entity) = 0; + virtual void entity_destroyed(Entity entity) = 0; }; @@ -18,58 +18,58 @@ namespace SupaRL { class ComponentArray : public IComponentArray { public: - void InsertData(Entity entity, T component) + void insert_data(Entity entity, T component) { - assert(mEntityToIndexMap.find(entity) == mEntityToIndexMap.end() && "Component added to same entity more than once."); + assert(entity_to_index_map_.find(entity) == entity_to_index_map_.end() && "Component added to same entity more than once."); // Put new entry at end - size_t newIndex = mSize; - mEntityToIndexMap[entity] = newIndex; - mIndexToEntityMap[newIndex] = entity; - mComponentArray[newIndex] = component; - ++mSize; + size_t newIndex = size_; + entity_to_index_map_[entity] = newIndex; + index_to_entity_map_[newIndex] = entity; + component_array_[newIndex] = component; + ++size_; } - void RemoveData(Entity entity) + void remove_data(Entity entity) { - assert(mEntityToIndexMap.find(entity) != mEntityToIndexMap.end() && "Removing non-existent component."); + assert(entity_to_index_map_.find(entity) != entity_to_index_map_.end() && "Removing non-existent component."); // Copy element at end into deleted element's place to maintain density - size_t indexOfRemovedEntity = mEntityToIndexMap[entity]; - size_t indexOfLastElement = mSize - 1; - mComponentArray[indexOfRemovedEntity] = mComponentArray[indexOfLastElement]; + size_t indexOfRemovedEntity = entity_to_index_map_[entity]; + size_t indexOfLastElement = size_ - 1; + component_array_[indexOfRemovedEntity] = component_array_[indexOfLastElement]; // Update map to point to moved spot - Entity entityOfLastElement = mIndexToEntityMap[indexOfLastElement]; - mEntityToIndexMap[entityOfLastElement] = indexOfRemovedEntity; - mIndexToEntityMap[indexOfRemovedEntity] = entityOfLastElement; + Entity entityOfLastElement = index_to_entity_map_[indexOfLastElement]; + entity_to_index_map_[entityOfLastElement] = indexOfRemovedEntity; + index_to_entity_map_[indexOfRemovedEntity] = entityOfLastElement; - mEntityToIndexMap.erase(entity); - mIndexToEntityMap.erase(indexOfLastElement); + entity_to_index_map_.erase(entity); + index_to_entity_map_.erase(indexOfLastElement); - --mSize; + --size_; } - T& GetData(Entity entity) + T& get_data(Entity entity) { - assert(mEntityToIndexMap.find(entity) != mEntityToIndexMap.end() && "Retrieving non-existent component."); + assert(entity_to_index_map_.find(entity) != entity_to_index_map_.end() && "Retrieving non-existent component."); - return mComponentArray[mEntityToIndexMap[entity]]; + return component_array_[entity_to_index_map_[entity]]; } - void EntityDestroyed(Entity entity) override + void entity_destroyed(Entity entity) override { - if (mEntityToIndexMap.find(entity) != mEntityToIndexMap.end()) + if (entity_to_index_map_.find(entity) != entity_to_index_map_.end()) { - RemoveData(entity); + remove_data(entity); } } private: - std::array mComponentArray{}; - std::unordered_map mEntityToIndexMap{}; - std::unordered_map mIndexToEntityMap{}; - size_t mSize{}; + std::array component_array_{}; + std::unordered_map entity_to_index_map_{}; + std::unordered_map index_to_entity_map_{}; + size_t size_{}; }; } diff --git a/lib/include/core/component_manager.hpp b/lib/include/core/component_manager.hpp index f27f678..1902aa5 100644 --- a/lib/include/core/component_manager.hpp +++ b/lib/include/core/component_manager.hpp @@ -2,7 +2,7 @@ #include "component_array.hpp" #include "types.hpp" -#include +#include #include #include @@ -13,70 +13,70 @@ namespace SupaRL { public: template - void RegisterComponent() + void register_component() { const char* typeName = typeid(T).name(); - assert(mComponentTypes.find(typeName) == mComponentTypes.end() && "Registering component type more than once."); + assert(component_types_.find(typeName) == component_types_.end() && "Registering component type more than once."); - mComponentTypes.insert({typeName, mNextComponentType}); - mComponentArrays.insert({typeName, std::make_shared>()}); + component_types_.insert({typeName, next_component_type}); + component_arrays_.insert({typeName, std::make_shared>()}); - ++mNextComponentType; + ++next_component_type; } template - ComponentType GetComponentType() + ComponentType get_component_type() { const char* typeName = typeid(T).name(); - assert(mComponentTypes.find(typeName) != mComponentTypes.end() && "Component not registered before use."); + assert(component_types_.find(typeName) != component_types_.end() && "Component not registered before use."); - return mComponentTypes[typeName]; + return component_types_[typeName]; } template - void AddComponent(Entity entity, T component) + void add_component(Entity entity, T component) { - GetComponentArray()->InsertData(entity, component); + get_component_array()->insert_data(entity, component); } template - void RemoveComponent(Entity entity) + void remove_component(Entity entity) { - GetComponentArray()->RemoveData(entity); + get_component_array()->remove_data(entity); } template - T& GetComponent(Entity entity) + T& get_component(Entity entity) { - return GetComponentArray()->GetData(entity); + return get_component_array()->get_data(entity); } - void EntityDestroyed(Entity entity) + void entity_destroyed(Entity entity) { - for (auto const& pair : mComponentArrays) + for (auto const& pair : component_arrays_) { auto const& component = pair.second; - component->EntityDestroyed(entity); + component->entity_destroyed(entity); } } private: - std::unordered_map mComponentTypes{}; - std::unordered_map> mComponentArrays{}; - ComponentType mNextComponentType{}; + std::unordered_map component_types_{}; + std::unordered_map> component_arrays_{}; + ComponentType next_component_type{}; template - std::shared_ptr> GetComponentArray() + std::shared_ptr> get_component_array() { const char* typeName = typeid(T).name(); - assert(mComponentTypes.find(typeName) != mComponentTypes.end() && "Component not registered before use."); + assert(component_types_.find(typeName) != component_types_.end() && "Component not registered before use."); - return std::static_pointer_cast>(mComponentArrays[typeName]); + return std::static_pointer_cast>(component_arrays_[typeName]); } }; } diff --git a/lib/include/core/coordinator.hpp b/lib/include/core/coordinator.hpp index 3233fd6..0e7802b 100644 --- a/lib/include/core/coordinator.hpp +++ b/lib/include/core/coordinator.hpp @@ -7,115 +7,113 @@ #include "types.hpp" #include +#include namespace SupaRL { -class Coordinator -{ -public: - void Init() - { - mComponentManager = std::make_unique(); - mEntityManager = std::make_unique(); - mEventManager = std::make_unique(); - mSystemManager = std::make_unique(); - } - - - // Entity methods - Entity CreateEntity() - { - return mEntityManager->CreateEntity(); - } - - void DestroyEntity(Entity entity) - { - mEntityManager->DestroyEntity(entity); - - mComponentManager->EntityDestroyed(entity); - - mSystemManager->EntityDestroyed(entity); - } - - - // Component methods - template - void RegisterComponent() - { - mComponentManager->RegisterComponent(); - } - - template - void AddComponent(Entity entity, T component) - { - mComponentManager->AddComponent(entity, component); - - auto signature = mEntityManager->GetSignature(entity); - signature.set(mComponentManager->GetComponentType(), true); - mEntityManager->SetSignature(entity, signature); - - mSystemManager->EntitySignatureChanged(entity, signature); - } - - template - void RemoveComponent(Entity entity) - { - mComponentManager->RemoveComponent(entity); - - auto signature = mEntityManager->GetSignature(entity); - signature.set(mComponentManager->GetComponentType(), false); - mEntityManager->SetSignature(entity, signature); - - mSystemManager->EntitySignatureChanged(entity, signature); - } - - template - T& GetComponent(Entity entity) - { - return mComponentManager->GetComponent(entity); - } - - template - ComponentType GetComponentType() - { - return mComponentManager->GetComponentType(); - } - - - // System methods - template - std::shared_ptr RegisterSystem() - { - return mSystemManager->RegisterSystem(); - } - - template - void SetSystemSignature(Signature signature) - { - mSystemManager->SetSignature(signature); - } - - - // Event methods - void AddEventListener(EventId eventId, std::function const& listener) - { - mEventManager->AddListener(eventId, listener); - } - - void SendEvent(Event& event) - { - mEventManager->SendEvent(event); - } - - void SendEvent(EventId eventId) - { - mEventManager->SendEvent(eventId); - } - -private: - std::unique_ptr mComponentManager; - std::unique_ptr mEntityManager; - std::unique_ptr mEventManager; - std::unique_ptr mSystemManager; -}; + class Coordinator + { + public: + void init() + { + component_manager_ = std::make_unique(); + entity_manager_ = std::make_unique(); + event_manager_ = std::make_unique(); + system_manager_ = std::make_unique(); + } + + // Entity methods + Entity create_entity() + { + return entity_manager_->create_entity(); + } + + void destroy_entity(Entity entity) + { + entity_manager_->destroy_entity(entity); + + component_manager_->entity_destroyed(entity); + + system_manager_->entity_destroyed(entity); + } + + // Component methods + template + void register_component() + { + component_manager_->register_component(); + } + + template + void add_component(Entity entity, T component) + { + component_manager_->add_component(entity, component); + + auto signature = entity_manager_->get_signature(entity); + signature.set(component_manager_->get_component_type(), true); + entity_manager_->set_signature(entity, signature); + + system_manager_->entity_signature_changed(entity, signature); + } + + template + void remove_component(Entity entity) + { + component_manager_->remove_component(entity); + + auto signature = entity_manager_->get_signature(entity); + signature.set(component_manager_->get_component_type(), false); + entity_manager_->set_signature(entity, signature); + + system_manager_->entity_signature_changed(entity, signature); + } + + template + T& get_component(Entity entity) + { + return component_manager_->get_component(entity); + } + + template + ComponentType get_component_type() + { + return component_manager_->get_component_type(); + } + + + // System methods + template + std::shared_ptr register_system() + { + return system_manager_->register_system(); + } + + template + void set_system_signature(Signature signature) + { + system_manager_->set_signature(signature); + } + + // Event methods + void add_event_listener(EventId eventId, std::function const& listener) + { + event_manager_->add_listener(eventId, listener); + } + + void send_event(Event& event) + { + event_manager_->send_event(event); + } + + void send_event(EventId eventId) + { + event_manager_->send_event(eventId); + } + + private: + std::unique_ptr component_manager_; + std::unique_ptr entity_manager_; + std::unique_ptr event_manager_; + std::unique_ptr system_manager_; + }; } diff --git a/lib/include/core/entity_manager.hpp b/lib/include/core/entity_manager.hpp index dffad41..09b1ffb 100644 --- a/lib/include/core/entity_manager.hpp +++ b/lib/include/core/entity_manager.hpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace SupaRL { @@ -14,48 +15,51 @@ namespace SupaRL { { for (Entity entity = 0; entity < MAX_ENTITIES; ++entity) { - mAvailableEntities.push(entity); + available_entities_.push(entity); } } - Entity CreateEntity() + Entity create_entity() { - assert(mLivingEntityCount < MAX_ENTITIES && "Too many entities in existence."); + assert(living_entity_count_ < MAX_ENTITIES && "Too many entities in existence."); - Entity id = mAvailableEntities.front(); - mAvailableEntities.pop(); - ++mLivingEntityCount; + Entity id = available_entities_.front(); + available_entities_.pop(); + ++living_entity_count_; return id; } - void DestroyEntity(Entity entity) + void destroy_entity(Entity entity) { assert(entity < MAX_ENTITIES && "Entity out of range."); - mSignatures[entity].reset(); - mAvailableEntities.push(entity); - --mLivingEntityCount; + signatures_[entity].reset(); + available_entities_.push(entity); + --living_entity_count_; } - void SetSignature(Entity entity, Signature signature) + void set_signature(Entity entity, Signature signature) { + std::cout << "Setting signature for entity " << entity << std::endl; + std::cout << "Num Signatures: " << signatures_.size() << std::endl; + std::cout << "Signature: " << signatures_[entity] << std::endl; assert(entity < MAX_ENTITIES && "Entity out of range."); - mSignatures[entity] = signature; + signatures_[entity] = signature; } - Signature GetSignature(Entity entity) + Signature get_signature(Entity entity) { assert(entity < MAX_ENTITIES && "Entity out of range."); - return mSignatures[entity]; + return signatures_[entity]; } private: - std::queue mAvailableEntities{}; - std::array mSignatures{}; - uint32_t mLivingEntityCount{}; + std::queue available_entities_{}; + std::array signatures_{}; + uint32_t living_entity_count_{}; }; } diff --git a/lib/include/core/event.hpp b/lib/include/core/event.hpp index 017d394..fc2dc75 100644 --- a/lib/include/core/event.hpp +++ b/lib/include/core/event.hpp @@ -12,28 +12,28 @@ namespace SupaRL { Event() = delete; explicit Event(EventId type) - : mType(type) + : type_(type) {} template - void SetParam(EventId id, T value) + void set_param(EventId id, T value) { - mData[id] = value; + data_[id] = value; } template - T GetParam(EventId id) + T get_param(EventId id) { - return std::any_cast(mData[id]); + return std::any_cast(data_[id]); } - EventId GetType() const + EventId get_type() const { - return mType; + return type_; } private: - EventId mType{}; - std::unordered_map mData{}; + EventId type_{}; + std::unordered_map data_{}; }; } diff --git a/lib/include/core/event_manager.hpp b/lib/include/core/event_manager.hpp index bac2f0b..b2573d0 100644 --- a/lib/include/core/event_manager.hpp +++ b/lib/include/core/event_manager.hpp @@ -11,32 +11,32 @@ namespace SupaRL class EventManager { public: - void AddListener(EventId eventId, std::function const& listener) + void add_listener(EventId eventId, std::function const& listener) { - listeners[eventId].push_back(listener); + listeners_[eventId].push_back(listener); } - void SendEvent(Event& event) + void send_event(Event& event) { - uint32_t type = event.GetType(); + uint32_t type = event.get_type(); - for (auto const& listener : listeners[type]) + for (auto const& listener : listeners_[type]) { listener(event); } } - void SendEvent(EventId eventId) + void send_event(EventId eventId) { Event event(eventId); - for (auto const& listener : listeners[eventId]) + for (auto const& listener : listeners_[eventId]) { listener(event); } } private: - std::unordered_map>> listeners; + std::unordered_map>> listeners_; }; } diff --git a/lib/include/core/math.hpp b/lib/include/core/math.hpp new file mode 100644 index 0000000..6d21b16 --- /dev/null +++ b/lib/include/core/math.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +namespace SupaRL { + struct Vector2D { + operator const std::array() const noexcept { return {x, y}; } + + Vector2D operator+(const Vector2D& other) const noexcept { return {x + other.x, y + other.y}; } + Vector2D operator+=(const Vector2D& other) noexcept { + x += other.x; + y += other.y; + return *this; + } + Vector2D operator-(const Vector2D& other) const noexcept { return {x - other.x, y - other.y}; } + Vector2D operator/(int scalar) const noexcept { return {x / scalar, y / scalar}; } + bool operator==(const Vector2D& other) const noexcept { return x == other.x && y == other.y; } + bool operator!=(const Vector2D& other) const noexcept { return !(*this == other); } + float distance_to(const Vector2D& other) const noexcept { + return std::sqrt(std::pow(x - other.x, 2) + std::pow(y - other.y, 2)); + } + + int x; + int y; + + template + void serialize(Archive& archive) { + archive(x, y); + } + }; +} diff --git a/lib/include/core/system.hpp b/lib/include/core/system.hpp index 62b14d2..1a8573b 100644 --- a/lib/include/core/system.hpp +++ b/lib/include/core/system.hpp @@ -3,12 +3,11 @@ #include "types.hpp" #include - namespace SupaRL { class System { public: - std::set mEntities; + std::set entities_; }; } diff --git a/lib/include/core/system_manager.hpp b/lib/include/core/system_manager.hpp index 1540bbb..89bcd51 100644 --- a/lib/include/core/system_manager.hpp +++ b/lib/include/core/system_manager.hpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace SupaRL { @@ -12,59 +13,65 @@ namespace SupaRL { public: template - std::shared_ptr RegisterSystem() + std::shared_ptr register_system() { const char* typeName = typeid(T).name(); - assert(mSystems.find(typeName) == mSystems.end() && "Registering system more than once."); + assert(systems_.find(typeName) == systems_.end() && "Registering system more than once."); auto system = std::make_shared(); - mSystems.insert({typeName, system}); + systems_.insert({typeName, system}); return system; } template - void SetSignature(Signature signature) + void set_signature(Signature signature) { const char* typeName = typeid(T).name(); - assert(mSystems.find(typeName) != mSystems.end() && "System used before registered."); + assert(systems_.find(typeName) != systems_.end() && "System used before registered."); - mSignatures.insert({typeName, signature}); + signatures_.insert({typeName, signature}); } - void EntityDestroyed(Entity entity) + void entity_destroyed(Entity entity) { - for (auto const& pair : mSystems) + for (auto const& pair : systems_) { auto const& system = pair.second; - system->mEntities.erase(entity); + system->entities_.erase(entity); } } - void EntitySignatureChanged(Entity entity, Signature entitySignature) + void entity_signature_changed(Entity entity, Signature entitySignature) { - for (auto const& pair : mSystems) + std::cout << "Entity signature changed " << entity << std::endl; + for (auto const& pair : systems_) { auto const& type = pair.first; auto const& system = pair.second; - auto const& systemSignature = mSignatures[type]; + std::cout << "System: " << type << std::endl; + std::cout << "System: " << system << std::endl; + auto const& systemSignature = signatures_[type]; + std::cout << "Entities: " << system->entities_.size() << std::endl; if ((entitySignature & systemSignature) == systemSignature) { - system->mEntities.insert(entity); + system->entities_.insert(entity); } else { - system->mEntities.erase(entity); + std::cout << "Erasing entity" << std::endl; + system->entities_.erase(entity); + std::cout << "Entities: " << system->entities_.size() << std::endl; } } } private: - std::unordered_map mSignatures{}; - std::unordered_map> mSystems{}; + std::unordered_map signatures_{}; + std::unordered_map> systems_{}; }; } diff --git a/lib/include/systems/combat_system.hpp b/lib/include/systems/combat_system.hpp new file mode 100644 index 0000000..f66410f --- /dev/null +++ b/lib/include/systems/combat_system.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "core/system.hpp" + +namespace SupaRL +{ + class CombatSystem : public System + { + public: + CombatSystem() = default; + ~CombatSystem() = default; + + void init(); + void update(); + }; +} diff --git a/lib/include/systems/physics_system.hpp b/lib/include/systems/physics_system.hpp new file mode 100644 index 0000000..6039445 --- /dev/null +++ b/lib/include/systems/physics_system.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "core/system.hpp" + +namespace SupaRL { + class PhysicsSystem : public System + { + public: + PhysicsSystem() = default; + ~PhysicsSystem() = default; + + void init(); + void update(); + }; +} diff --git a/lib/include/systems/status_condition_system.hpp b/lib/include/systems/status_condition_system.hpp new file mode 100644 index 0000000..27846d7 --- /dev/null +++ b/lib/include/systems/status_condition_system.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "core/system.hpp" + +namespace SupaRL +{ + class StatusConditionSystem : public System + { + public: + StatusConditionSystem() = default; + ~StatusConditionSystem() = default; + + void init(); + void update(); + }; +} diff --git a/lib/include/utils/logger.hpp b/lib/include/utils/logger.hpp new file mode 100644 index 0000000..d0b0d36 --- /dev/null +++ b/lib/include/utils/logger.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include + +namespace SupaRL +{ + class ConsoleLogger + { + public: + void debug(const char* message) + { +#ifdef DEBUG + std::cout << message << std::endl; +#endif + } + + void debug(const char* message, int value) + { +#ifdef DEBUG + std::cout << message << value << std::endl; +#endif + } + + }; +} diff --git a/lib/src/systems/combat_system.cpp b/lib/src/systems/combat_system.cpp new file mode 100644 index 0000000..de96960 --- /dev/null +++ b/lib/src/systems/combat_system.cpp @@ -0,0 +1,15 @@ +#include "systems/combat_system.hpp" +#include + +namespace SupaRL +{ + void CombatSystem::init() + { + std::cout << "Combat System Init" << std::endl; + } + + void CombatSystem::update() + { + std::cout << "Combat System Update" << std::endl; + } +} diff --git a/lib/src/systems/physics_system.cpp b/lib/src/systems/physics_system.cpp new file mode 100644 index 0000000..c4cfeeb --- /dev/null +++ b/lib/src/systems/physics_system.cpp @@ -0,0 +1,28 @@ +#include "systems/physics_system.hpp" + +#include "core/coordinator.hpp" +#include "components/transform.hpp" +#include "components/velocity.hpp" +#include + +extern SupaRL::Coordinator g_coordinator; + +namespace SupaRL { + void PhysicsSystem::init() { + } + + void PhysicsSystem::update() { + auto entities_to_remove = std::vector(); + for (auto const& entity : entities_) { + std::cout << "Updating physics for entity " << entity << std::endl; + auto& transform = g_coordinator.get_component(entity); + auto& velocity = g_coordinator.get_component(entity); + + transform.position_.x += velocity.velocity_.x; + transform.position_.y += velocity.velocity_.y; + + velocity.velocity_.x = 0; + velocity.velocity_.y = 0; + } + } +} diff --git a/lib/src/systems/status_condition_system.cpp b/lib/src/systems/status_condition_system.cpp new file mode 100644 index 0000000..164a10e --- /dev/null +++ b/lib/src/systems/status_condition_system.cpp @@ -0,0 +1,33 @@ +#include "systems/status_condition_system.hpp" +#include "core/coordinator.hpp" +#include "components/defence.hpp" +#include "components/status_condition.hpp" + +extern SupaRL::Coordinator g_coordinator; + +namespace SupaRL +{ + + void StatusConditionSystem::init() + { + } + + void StatusConditionSystem::update() + { + for (auto const& entity : entities_) + { + auto& status_condition = g_coordinator.get_component(entity); + auto& defence = g_coordinator.get_component(entity); + + status_condition.ticks_++; + if (status_condition.ticks_ % 10 == 0) + { + defence.hp_ -= status_condition.damage_per_tick_; + if (defence.hp_ <= 0) + { + // DIE. + } + } + } + } +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1b3ebb7..410965c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -6,6 +6,7 @@ FetchContent_Declare( # For Windows: Prevent overriding the parent project's compiler/linker settings set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) set(CMAKE_EXPORT_COMPILE_COMMANDS 1) + FetchContent_MakeAvailable(googletest) enable_testing() diff --git a/tests/src/status_condition_systems_tests.cpp b/tests/src/status_condition_systems_tests.cpp index fdf0cad..aa79700 100644 --- a/tests/src/status_condition_systems_tests.cpp +++ b/tests/src/status_condition_systems_tests.cpp @@ -1,3 +1,4 @@ +#include "gtest/gtest.h" /** * @file status_condition_systems_tests.cpp @@ -8,4 +9,8 @@ * a status condition that deals damage. */ +TEST(StatusConditionSystems, BleedDamage) +{ + ASSERT_EQ(1, 1); +} From dffd3479f40c129572bab987c7855c64795d845f Mon Sep 17 00:00:00 2001 From: daniel baker Date: Thu, 10 Oct 2024 10:16:25 +0100 Subject: [PATCH 05/38] feat: I can't believe it's working. Kinda. --- app/include/components.hpp | 12 +- app/include/controller.hpp | 14 +- app/include/dungeon.hpp | 71 +++-- app/include/entity_manager.hpp | 135 +++++---- app/include/events/command.hpp | 440 ++++++++++++++-------------- app/include/game_entity.hpp | 12 +- app/include/gui.hpp | 256 ++++++++-------- app/include/health_bar.hpp | 9 +- app/include/input_handler.hpp | 253 ++++++++-------- app/include/rectangular_room.hpp | 39 ++- app/include/rendering.hpp | 71 +++-- app/include/state.hpp | 218 +++++++------- app/include/types/action_result.hpp | 24 +- app/include/types/map.hpp | 217 +++++++------- app/include/types/math.hpp | 90 ++---- app/include/types/nparray.hpp | 97 +++--- app/include/ui/dungeon_level.hpp | 19 +- app/include/util.hpp | 52 ++-- app/include/world.hpp | 140 +++++---- app/include/xp_bar.hpp | 23 +- app/src/TCODRenderer.cpp | 18 +- app/src/basic_ai_component.cpp | 13 +- app/src/components.cpp | 11 +- app/src/consumable_factory.cpp | 63 ++-- app/src/dungeon.cpp | 15 +- app/src/entity_factory.cpp | 2 +- app/src/entity_manager.cpp | 257 ++++++++-------- app/src/events/command.cpp | 12 +- app/src/game_entity.cpp | 6 - app/src/input_handler.cpp | 27 +- app/src/rectangular_room.cpp | 24 +- app/src/state.cpp | 166 +++++------ app/src/types/map.cpp | 19 +- app/src/ui/dungeon_level.cpp | 18 +- app/src/ui/health_bar.cpp | 50 ++-- app/src/ui/message_log.cpp | 88 +++--- app/src/ui/ui.cpp | 33 ++- app/src/ui/xp_bar.cpp | 49 ++-- app/src/world.cpp | 10 +- 39 files changed, 1511 insertions(+), 1562 deletions(-) diff --git a/app/include/components.hpp b/app/include/components.hpp index 7c93c41..a044911 100644 --- a/app/include/components.hpp +++ b/app/include/components.hpp @@ -1,5 +1,4 @@ -#ifndef INCLUDE_COMPONENTS_HPP_ -#define INCLUDE_COMPONENTS_HPP_ +#pragma once #include #include @@ -7,7 +6,7 @@ #include "game_entity.hpp" #include "types/action_result.hpp" -#include "types/math.hpp" +#include #include "types/world_fwd.hpp" namespace cpprl { @@ -63,8 +62,8 @@ namespace cpprl { TransformComponent() = default; TransformComponent(int x, int y) : position_({x, y}) {} virtual ~TransformComponent() = default; - Vector2D get_position() const { return position_; } - void move(Vector2D new_position) { position_ = new_position; } + SupaRL::Vector2D get_position() const { return position_; } + void move(SupaRL::Vector2D new_position) { position_ = new_position; } template void serialize(Archive& archive) { @@ -72,7 +71,7 @@ namespace cpprl { } private: - Vector2D position_; + SupaRL::Vector2D position_; }; class ASCIIComponent { @@ -289,4 +288,3 @@ CEREAL_REGISTER_TYPE(cpprl::FireSpell); CEREAL_REGISTER_TYPE(cpprl::ConfusionSpell); CEREAL_REGISTER_TYPE(cpprl::StatsComponent); CEREAL_REGISTER_TYPE(cpprl::AttackComponent); -#endif diff --git a/app/include/controller.hpp b/app/include/controller.hpp index 14b4b31..0876fa1 100644 --- a/app/include/controller.hpp +++ b/app/include/controller.hpp @@ -1,13 +1,11 @@ -#ifndef INCLUDE_CONTROLLER_HPP_ -#define INCLUDE_CONTROLLER_HPP_ +#pragma once -#include "types/math.hpp" +#include "core/math.hpp" namespace cpprl { -struct Controller { - Vector2D cursor = {0, 0}; -}; + struct Controller { + SupaRL::Vector2D cursor = {0, 0}; + }; -} // namespace cpprl +} -#endif // INCLUDE_CONTROLLER_HPP_ diff --git a/app/include/dungeon.hpp b/app/include/dungeon.hpp index aa882d6..f5880fc 100644 --- a/app/include/dungeon.hpp +++ b/app/include/dungeon.hpp @@ -1,47 +1,44 @@ -#ifndef DUNGEON_HPP -#define DUNGEON_HPP +#pragma once #include -#include "game_entity.hpp" #include "types/map.hpp" -#include "types/tile.hpp" +#include namespace cpprl { -struct DungeonConfig { - int max_rooms; - int room_min_size; - int room_max_size; - int map_width; - int map_height; - int max_monsters_per_room; -}; - -class Dungeon { - private: - std::vector l_tunnel_between(Vector2D start, Vector2D end); - TCODRandom rng_; - int seed_; - int level_ = 0; - std::unique_ptr current_map_; - - public: - Dungeon() { - seed_ = TCODRandom::getInstance()->getInt(0, 0x7FFFFFFF); + struct DungeonConfig { + int max_rooms; + int room_min_size; + int room_max_size; + int map_width; + int map_height; + int max_monsters_per_room; }; - virtual ~Dungeon() = default; - void generate(DungeonConfig config); - int get_level() const { return level_; } - int increase_level() { return ++level_; } - Map& get_map() const { return *current_map_; } - - template - void serialize(Archive& archive) { - archive(seed_, level_); - } -}; + class Dungeon { + private: + std::vector l_tunnel_between(SupaRL::Vector2D start, SupaRL::Vector2D end); + TCODRandom rng_; + int seed_; + int level_ = 0; + std::unique_ptr current_map_; + + public: + Dungeon() { + seed_ = TCODRandom::getInstance()->getInt(0, 0x7FFFFFFF); + }; + virtual ~Dungeon() = default; + + void generate(DungeonConfig config); + int get_level() const { return level_; } + int increase_level() { return ++level_; } + Map& get_map() const { return *current_map_; } + + template + void serialize(Archive& archive) { + archive(seed_, level_); + } + }; -} // namespace cpprl +} -#endif diff --git a/app/include/entity_manager.hpp b/app/include/entity_manager.hpp index dc89c32..7663475 100644 --- a/app/include/entity_manager.hpp +++ b/app/include/entity_manager.hpp @@ -3,89 +3,88 @@ #include #include #include -#include "../../lib/include/core/entity_manager.hpp" +#include +#include -#include "colours.hpp" #include "entity_factory.hpp" #include "game_entity.hpp" #include "rectangular_room.hpp" -#include "types/map.hpp" namespace cpprl { -typedef std::optional> OptEntityRef; + typedef std::optional> OptEntityRef; -class EntityManager { - public: - EntityManager() { - orc_factory_ = std::make_unique(); - troll_factory_ = std::make_unique(); - entity_manager_ = SupaRL::EntityManager(); - }; + class EntityManager { + public: + EntityManager() { + orc_factory_ = std::make_unique(); + troll_factory_ = std::make_unique(); + entity_manager_ = SupaRL::EntityManager(); + }; - EntityManager( - std::unique_ptr orc_factory, - std::unique_ptr troll_factory) - : orc_factory_(std::move(orc_factory)), + EntityManager( + std::unique_ptr orc_factory, + std::unique_ptr troll_factory) + : orc_factory_(std::move(orc_factory)), troll_factory_(std::move(troll_factory)){}; - void clear(); - void clear_except_player(); - std::optional> get_blocking_entity_at( - Vector2D position); - std::optional> get_non_blocking_entity_at( - Vector2D position); - std::optional> get_closest_living_monster( - Vector2D position, float range) const; - std::vector> get_entities_at( - Vector2D position); - void place_entities( - RectangularRoom room, int max_monsters_per_room, int max_items_per_room); - Entity* spawn(Entity* entity); - Entity* spawn(Entity* entity, Vector2D position); - void reserve(size_t size) { entities_.reserve(size); } - void shrink_to_fit() { entities_.shrink_to_fit(); } - void remove(const Entity* entity); - size_t size() const { return entities_.size(); } + void clear(); + void clear_except_player(); + std::optional> get_blocking_entity_at( + SupaRL::Vector2D position); + std::optional> get_non_blocking_entity_at( + SupaRL::Vector2D position); + std::optional> get_closest_living_monster( + SupaRL::Vector2D position, float range) const; + std::vector> get_entities_at( + SupaRL::Vector2D position); + void place_entities( + RectangularRoom room, int max_monsters_per_room, int max_items_per_room); + Entity* spawn(Entity* entity); + Entity* spawn(Entity* entity, SupaRL::Vector2D position); + void reserve(size_t size) { entities_.reserve(size); } + void shrink_to_fit() { entities_.shrink_to_fit(); } + void remove(const Entity* entity); + size_t size() const { return entities_.size(); } - using iterator = std::vector::iterator; - using const_iterator = std::vector::const_iterator; - // Iterator for EntityManager + using iterator = std::vector::iterator; + using const_iterator = std::vector::const_iterator; + // Iterator for EntityManager - iterator begin() { return entities_.begin(); } + iterator begin() { return entities_.begin(); } - iterator end() { return entities_.end(); } + iterator end() { return entities_.end(); } - const_iterator begin() const { return entities_.begin(); } + const_iterator begin() const { return entities_.begin(); } - const_iterator end() const { return entities_.end(); } + const_iterator end() const { return entities_.end(); } - template - void save(Archive& archive) const { - archive(entities_.size() - 1); - for (auto& entity : entities_) { - if (entity->get_name() == "Player") { - continue; - } - entity->pack(archive); - } - } + template + void save(Archive& archive) const { + archive(entities_.size() - 1); + for (auto& entity : entities_) { + if (entity->get_name() == "Player") { + continue; + } + entity->pack(archive); + } + } - template - void load(Archive& archive) { - size_t size; - archive(size); - entities_.reserve(size); - for (size_t i = 0; i < size; i++) { - auto entity = new Entity("", false, nullptr, nullptr); - entity->unpack(archive); - entities_.emplace_back(entity); - } - } + template + void load(Archive& archive) { + size_t size; + archive(size); + entities_.reserve(size); + for (size_t i = 0; i < size; i++) { + auto entity = new Entity("", false, nullptr, nullptr); + entity->unpack(archive); + entities_.emplace_back(entity); + } + } - private: - std::vector entities_; - SupaRL::EntityManager entity_manager_; - std::unique_ptr orc_factory_; - std::unique_ptr troll_factory_; -}; -} // namespace cpprl + private: + std::vector entities_; + SupaRL::EntityManager entity_manager_; + std::unique_ptr orc_factory_; + std::unique_ptr troll_factory_; + }; +} diff --git a/app/include/events/command.hpp b/app/include/events/command.hpp index cc51828..481eb26 100644 --- a/app/include/events/command.hpp +++ b/app/include/events/command.hpp @@ -1,231 +1,229 @@ -#ifndef COMMAND_H -#define COMMAND_H +#pragma once #include "types/entity_fwd.hpp" -#include "types/math.hpp" +#include #include "types/state_result.hpp" #include "types/world_fwd.hpp" namespace cpprl { -class UiWindow; - -class EngineEvent { - protected: - World& world_; - - public: - explicit EngineEvent(World& world) : world_(world) {} - virtual ~EngineEvent() {} - virtual StateResult execute() = 0; -}; - -class Command : public EngineEvent { - protected: - Entity* entity_; - - public: - Command(World& world, Entity* entity) : EngineEvent(world), entity_(entity) {} - virtual ~Command() {} - virtual StateResult execute() = 0; -}; - -class ScrollCommand : public EngineEvent { - public: - ScrollCommand(World& world, UiWindow& ui_window, int scroll_amount) - : EngineEvent(world), + class UiWindow; + + class EngineEvent { + protected: + World& world_; + + public: + explicit EngineEvent(World& world) : world_(world) {} + virtual ~EngineEvent() {} + virtual StateResult execute() = 0; + }; + + class Command : public EngineEvent { + protected: + Entity* entity_; + + public: + Command(World& world, Entity* entity) : EngineEvent(world), entity_(entity) {} + virtual ~Command() {} + virtual StateResult execute() = 0; + }; + + class ScrollCommand : public EngineEvent { + public: + ScrollCommand(World& world, UiWindow& ui_window, int scroll_amount) + : EngineEvent(world), ui_window_(ui_window), scroll_amount_(scroll_amount){}; - virtual StateResult execute(); - - private: - UiWindow& ui_window_; - int scroll_amount_; -}; - -class ViewHistoryCommand : public EngineEvent { - public: - ViewHistoryCommand(World& world) : EngineEvent(world){}; - virtual StateResult execute(); -}; - -class PickupCommand : public Command { - public: - PickupCommand(World& world, Entity* entity) : Command(world, entity){}; - StateResult execute() override; -}; - -// TODO: base class from useItemCommand -class DropItemCommand final : public Command { - private: - int item_index_; - - public: - DropItemCommand(World& world, Entity* entity, int item_index) - : Command(world, entity), item_index_(item_index){}; - ~DropItemCommand() override = default; - StateResult execute() override; -}; - -class InventoryCommand final : public Command { - public: - InventoryCommand(World& world, Entity* entity) : Command(world, entity) {} - StateResult execute() override; -}; - -class CharacterMenuCommand final : public Command { - public: - using Command::Command; - StateResult execute() override; -}; - -class MainMenuCommand final : public EngineEvent { - public: - MainMenuCommand(World& world) : EngineEvent(world) {} - StateResult execute() override; -}; - -class UseCommand final : public EngineEvent { - private: - Vector2D position_; - - public: - UseCommand(World& world, Vector2D position) - : EngineEvent(world), position_(position) {} - StateResult execute() override; -}; - -enum ItemSubCommand { USE_ITEM, DROP_ITEM }; -class SelectItemCommand final : public Command { - private: - ItemSubCommand sub_command_; - UiWindow& ui_window_; - - public: - SelectItemCommand( - World& world, - Entity* entity, - UiWindow& ui_window, - ItemSubCommand sub_command) - : Command(world, entity), + virtual StateResult execute(); + + private: + UiWindow& ui_window_; + int scroll_amount_; + }; + + class ViewHistoryCommand : public EngineEvent { + public: + ViewHistoryCommand(World& world) : EngineEvent(world){}; + virtual StateResult execute(); + }; + + class PickupCommand : public Command { + public: + PickupCommand(World& world, Entity* entity) : Command(world, entity){}; + StateResult execute() override; + }; + + // TODO: base class from useItemCommand + class DropItemCommand final : public Command { + private: + int item_index_; + + public: + DropItemCommand(World& world, Entity* entity, int item_index) + : Command(world, entity), item_index_(item_index){}; + ~DropItemCommand() override = default; + StateResult execute() override; + }; + + class InventoryCommand final : public Command { + public: + InventoryCommand(World& world, Entity* entity) : Command(world, entity) {} + StateResult execute() override; + }; + + class CharacterMenuCommand final : public Command { + public: + using Command::Command; + StateResult execute() override; + }; + + class MainMenuCommand final : public EngineEvent { + public: + MainMenuCommand(World& world) : EngineEvent(world) {} + StateResult execute() override; + }; + + class UseCommand final : public EngineEvent { + private: + SupaRL::Vector2D position_; + + public: + UseCommand(World& world, SupaRL::Vector2D position) + : EngineEvent(world), position_(position) {} + StateResult execute() override; + }; + + enum ItemSubCommand { USE_ITEM, DROP_ITEM }; + class SelectItemCommand final : public Command { + private: + ItemSubCommand sub_command_; + UiWindow& ui_window_; + + public: + SelectItemCommand( + World& world, + Entity* entity, + UiWindow& ui_window, + ItemSubCommand sub_command) + : Command(world, entity), sub_command_(sub_command), ui_window_(ui_window) {} - StateResult execute() override; -}; - -enum MenuSubCommand { NEW_GAME, CONTINUE, QUIT }; -class SelectMenuItemCommand final : public EngineEvent { - private: - UiWindow& ui_window_; - - public: - SelectMenuItemCommand(World& world, UiWindow& ui_window) - : EngineEvent(world), ui_window_(ui_window) {} - StateResult execute() override; -}; - -class UseItemCommand final : public Command { - private: - int item_index_; - - public: - UseItemCommand(World& world, Entity* entity, int item_index) - : Command(world, entity), item_index_(item_index) {} - StateResult execute() override; -}; - -class BoostStatCommand final : public EngineEvent { - private: - UiWindow& ui_window_; - - public: - BoostStatCommand(World& world, UiWindow& ui_window) - : EngineEvent(world), ui_window_(ui_window) {} - StateResult execute() override; -}; - -class CloseViewCommand final : public EngineEvent { - public: - CloseViewCommand(World& world) : EngineEvent(world){}; - virtual StateResult execute() override; -}; - -class DieEvent : public EngineEvent { - public: - DieEvent(World& world, Entity* entity) - : EngineEvent(world), entity_(entity) {} - virtual ~DieEvent() = default; - virtual StateResult execute() override; - - private: - Entity* entity_; -}; - -class DirectionalCommand : public Command { - protected: - Vector2D move_vector_; - - public: - DirectionalCommand(World& world, Entity* entity, Vector2D move_vector) - : Command(world, entity), move_vector_(move_vector){}; - virtual StateResult execute(); -}; - -class NoOpEvent : public EngineEvent { - public: - NoOpEvent(World& world) : EngineEvent(world) {} - StateResult execute() override { return {}; } -}; - -class ResetGameCommand : public EngineEvent { - public: - ResetGameCommand(World& world) : EngineEvent(world) {} - StateResult execute() override { return Reset{}; } -}; - -class MouseInputEvent final : public EngineEvent { - private: - Vector2D position_; - - public: - MouseInputEvent(World& world, Vector2D position) - : EngineEvent(world), position_(position) {} - StateResult execute() override; -}; - -class MouseClickEvent final : public EngineEvent { - // TODO: remove this? - private: - Vector2D position_; - - public: - MouseClickEvent(World& world, Vector2D position) - : EngineEvent(world), position_(position) {} - StateResult execute() override; -}; - -class ExitTargetingModeCommand final : public EngineEvent { - public: - ExitTargetingModeCommand(World& world) : EngineEvent(world) {} - StateResult execute() override; -}; - -class MeleeCommand : DirectionalCommand { - public: - MeleeCommand(World& world, Entity* entity, Vector2D target_vector) - : DirectionalCommand(world, entity, target_vector){}; - StateResult execute(); -}; - -class MovementCommand : public DirectionalCommand { - public: - MovementCommand(World& world, Entity* entity, Vector2D move_vector) - : DirectionalCommand(world, entity, move_vector){}; - virtual StateResult execute(); -}; -class QuitCommand : public EngineEvent { - public: - QuitCommand(World& world) : EngineEvent(world){}; - virtual StateResult execute(); -}; -} // namespace cpprl -#endif + StateResult execute() override; + }; + + enum MenuSubCommand { NEW_GAME, CONTINUE, QUIT }; + class SelectMenuItemCommand final : public EngineEvent { + private: + UiWindow& ui_window_; + + public: + SelectMenuItemCommand(World& world, UiWindow& ui_window) + : EngineEvent(world), ui_window_(ui_window) {} + StateResult execute() override; + }; + + class UseItemCommand final : public Command { + private: + int item_index_; + + public: + UseItemCommand(World& world, Entity* entity, int item_index) + : Command(world, entity), item_index_(item_index) {} + StateResult execute() override; + }; + + class BoostStatCommand final : public EngineEvent { + private: + UiWindow& ui_window_; + + public: + BoostStatCommand(World& world, UiWindow& ui_window) + : EngineEvent(world), ui_window_(ui_window) {} + StateResult execute() override; + }; + + class CloseViewCommand final : public EngineEvent { + public: + CloseViewCommand(World& world) : EngineEvent(world){}; + virtual StateResult execute() override; + }; + + class DieEvent : public EngineEvent { + public: + DieEvent(World& world, Entity* entity) + : EngineEvent(world), entity_(entity) {} + virtual ~DieEvent() = default; + virtual StateResult execute() override; + + private: + Entity* entity_; + }; + + class DirectionalCommand : public Command { + protected: + SupaRL::Vector2D move_vector_; + + public: + DirectionalCommand(World& world, Entity* entity, SupaRL::Vector2D move_vector) + : Command(world, entity), move_vector_(move_vector){}; + virtual StateResult execute(); + }; + + class NoOpEvent : public EngineEvent { + public: + NoOpEvent(World& world) : EngineEvent(world) {} + StateResult execute() override { return {}; } + }; + + class ResetGameCommand : public EngineEvent { + public: + ResetGameCommand(World& world) : EngineEvent(world) {} + StateResult execute() override { return Reset{}; } + }; + + class MouseInputEvent final : public EngineEvent { + private: + SupaRL::Vector2D position_; + + public: + MouseInputEvent(World& world, SupaRL::Vector2D position) + : EngineEvent(world), position_(position) {} + StateResult execute() override; + }; + + class MouseClickEvent final : public EngineEvent { + // TODO: remove this? + private: + SupaRL::Vector2D position_; + + public: + MouseClickEvent(World& world, SupaRL::Vector2D position) + : EngineEvent(world), position_(position) {} + StateResult execute() override; + }; + + class ExitTargetingModeCommand final : public EngineEvent { + public: + ExitTargetingModeCommand(World& world) : EngineEvent(world) {} + StateResult execute() override; + }; + + class MeleeCommand : DirectionalCommand { + public: + MeleeCommand(World& world, Entity* entity, SupaRL::Vector2D target_vector) + : DirectionalCommand(world, entity, target_vector){}; + StateResult execute(); + }; + + class MovementCommand : public DirectionalCommand { + public: + MovementCommand(World& world, Entity* entity, SupaRL::Vector2D move_vector) + : DirectionalCommand(world, entity, move_vector){}; + virtual StateResult execute(); + }; + class QuitCommand : public EngineEvent { + public: + QuitCommand(World& world) : EngineEvent(world){}; + virtual StateResult execute(); + }; +} diff --git a/app/include/game_entity.hpp b/app/include/game_entity.hpp index cf17c47..94054ae 100644 --- a/app/include/game_entity.hpp +++ b/app/include/game_entity.hpp @@ -6,7 +6,11 @@ #include "basic_ai_component.hpp" #include "types/world_fwd.hpp" -#include <../../lib/include/core/types.hpp> +#include +#include +#include + +extern SupaRL::Coordinator g_coordinator; namespace cpprl { @@ -45,9 +49,11 @@ namespace cpprl { void set_id(SupaRL::Entity id) { id_ = id; }; SupaRL::Entity get_id() { return id_; }; - TransformComponent& get_transform_component() { - return *transformComponent_; + SupaRL::TransformComponent& get_transform_component() { + auto& transform = g_coordinator.get_component(id_); + return transform; }; + ASCIIComponent& get_sprite_component() { return *asciiComponent_; }; AttackComponent& get_attack_component() { return *attackComponent_; }; DefenseComponent& get_defense_component() { return *defenseComponent_; }; diff --git a/app/include/gui.hpp b/app/include/gui.hpp index 250ea6f..5805dcb 100644 --- a/app/include/gui.hpp +++ b/app/include/gui.hpp @@ -1,5 +1,4 @@ -#ifndef INCLUDE_HISTORY_WINDOW_HPP_ -#define INCLUDE_HISTORY_WINDOW_HPP_ +#pragma once #include #include @@ -7,17 +6,17 @@ #include "game_entity.hpp" #include "message_log.hpp" -#include "types/math.hpp" +#include namespace cpprl { -static constexpr int BORDER_TOP_LEFT = 0x250C; // '┌' in Unicode -static constexpr int BORDER_HORIZONTAL = 0x2500; // '─' in Unicode -static constexpr int BORDER_TOP_RIGHT = 0x2510; // '┐' in Unicode -static constexpr int BORDER_VERTICAL = 0x2502; // '│' in Unicode -static constexpr int BORDER_SPACE = 0x0020; // Space character -static constexpr int BORDER_BOTTOM_LEFT = 0x2514; // '└' in Unicode -static constexpr int BORDER_BOTTOM_RIGHT = 0x2518; // '┘' in Unicode cursor_e -static constexpr std::array LEGEND = { + static constexpr int BORDER_TOP_LEFT = 0x250C; // '┌' in Unicode + static constexpr int BORDER_HORIZONTAL = 0x2500; // '─' in Unicode + static constexpr int BORDER_TOP_RIGHT = 0x2510; // '┐' in Unicode + static constexpr int BORDER_VERTICAL = 0x2502; // '│' in Unicode + static constexpr int BORDER_SPACE = 0x0020; // Space character + static constexpr int BORDER_BOTTOM_LEFT = 0x2514; // '└' in Unicode + static constexpr int BORDER_BOTTOM_RIGHT = 0x2518; // '┘' in Unicode cursor_e + static constexpr std::array LEGEND = { BORDER_TOP_LEFT, BORDER_HORIZONTAL, BORDER_TOP_RIGHT, @@ -28,135 +27,132 @@ static constexpr std::array LEGEND = { BORDER_HORIZONTAL, BORDER_BOTTOM_RIGHT}; -class UiWindow { - protected: - int width_; - int height_; - Vector2D position_; - std::unique_ptr console_; - int cursor_; - std::string title_; - - public: - UiWindow( - std::size_t width, - std::size_t height, - Vector2D position, - std::string title = "") - : width_(width), + class UiWindow { + protected: + int width_; + int height_; + SupaRL::Vector2D position_; + std::unique_ptr console_; + int cursor_; + std::string title_; + + public: + UiWindow( + std::size_t width, + std::size_t height, + SupaRL::Vector2D position, + std::string title = "") + : width_(width), height_(height), position_(position), console_(new TCODConsole(width, height)), cursor_(0), title_(title){}; - virtual ~UiWindow() = default; - - virtual void add_frame() const; - virtual void render(tcod::Console& parent_console); - void set_cursor(int cursor) { - if (cursor < 0) { - cursor_ = 0; - } else { - cursor_ = cursor; - } - } - int get_cursor() const { return cursor_; } -}; - -class HistoryWindow : public UiWindow { - private: - MessageLog& message_log_; - int log_size_; - - public: - HistoryWindow( - std::size_t width, - std::size_t height, - Vector2D position, - MessageLog& message_log, - std::string title = "") - : UiWindow(width, height, position, title), + virtual ~UiWindow() = default; + + virtual void add_frame() const; + virtual void render(tcod::Console& parent_console); + void set_cursor(int cursor) { + if (cursor < 0) { + cursor_ = 0; + } else { + cursor_ = cursor; + } + } + int get_cursor() const { return cursor_; } + }; + + class HistoryWindow : public UiWindow { + private: + MessageLog& message_log_; + int log_size_; + + public: + HistoryWindow( + std::size_t width, + std::size_t height, + SupaRL::Vector2D position, + MessageLog& message_log, + std::string title = "") + : UiWindow(width, height, position, title), message_log_(message_log), log_size_(message_log_.get_messages().size()){}; - virtual void render(tcod::Console& parent_console) override; -}; - -class InventoryWindow final : public UiWindow { - private: - Entity* entity_; - - public: - InventoryWindow( - std::size_t width, - std::size_t height, - Vector2D position, - Entity* entity, - std::string title = "") - : UiWindow(width, height, position, title), entity_(entity) { - UiWindow::set_cursor(1); + virtual void render(tcod::Console& parent_console) override; + }; + + class InventoryWindow final : public UiWindow { + private: + Entity* entity_; + + public: + InventoryWindow( + std::size_t width, + std::size_t height, + SupaRL::Vector2D position, + Entity* entity, + std::string title = "") + : UiWindow(width, height, position, title), entity_(entity) { + UiWindow::set_cursor(1); + }; + + virtual void render(tcod::Console& parent_console) override; + }; + + class CharacterMenuWindow final : public UiWindow { + private: + Entity* entity_; + + public: + CharacterMenuWindow( + std::size_t width, + std::size_t height, + SupaRL::Vector2D position, + Entity* entity, + std::string title = "") + : UiWindow(width, height, position, title), entity_(entity) { + UiWindow::set_cursor(1); + }; + + virtual void render(tcod::Console& parent_console) override; }; - virtual void render(tcod::Console& parent_console) override; -}; - -class CharacterMenuWindow final : public UiWindow { - private: - Entity* entity_; - - public: - CharacterMenuWindow( - std::size_t width, - std::size_t height, - Vector2D position, - Entity* entity, - std::string title = "") - : UiWindow(width, height, position, title), entity_(entity) { - UiWindow::set_cursor(1); + class GameOverWindow final : public UiWindow { + private: + std::string message_; + + public: + GameOverWindow( + std::size_t width, + std::size_t height, + SupaRL::Vector2D position, + std::string message, + std::string title = "") + : UiWindow(width, height, position, title), message_(message) { + UiWindow::set_cursor(1); + }; + virtual void render(tcod::Console& parent_console) override; }; - virtual void render(tcod::Console& parent_console) override; -}; - -class GameOverWindow final : public UiWindow { - private: - std::string message_; - - public: - GameOverWindow( - std::size_t width, - std::size_t height, - Vector2D position, - std::string message, - std::string title = "") - : UiWindow(width, height, position, title), message_(message) { - UiWindow::set_cursor(1); + class MainMenuWindow final : public UiWindow { + private: + // This could be a map of MenuItems + // Where menu item is a name and a + // sub command + std::map menu_items_; + + public: + MainMenuWindow( + std::size_t width, + std::size_t height, + SupaRL::Vector2D position, + std::string title = "Main Menu") + : UiWindow(width, height, position, title) { + menu_items_[1] = "New Game"; + menu_items_[2] = "Continue"; + menu_items_[3] = "Quit"; + UiWindow::set_cursor(1); + } + void render(tcod::Console& parent_console) override; }; - virtual void render(tcod::Console& parent_console) override; -}; - -class MainMenuWindow final : public UiWindow { - private: - // This could be a map of MenuItems - // Where menu item is a name and a - // sub command - std::map menu_items_; - - public: - MainMenuWindow( - std::size_t width, - std::size_t height, - Vector2D position, - std::string title = "Main Menu") - : UiWindow(width, height, position, title) { - menu_items_[1] = "New Game"; - menu_items_[2] = "Continue"; - menu_items_[3] = "Quit"; - UiWindow::set_cursor(1); - } - void render(tcod::Console& parent_console) override; -}; - -} // namespace cpprl - -#endif +} diff --git a/app/include/health_bar.hpp b/app/include/health_bar.hpp index 346bfa2..f3dd439 100644 --- a/app/include/health_bar.hpp +++ b/app/include/health_bar.hpp @@ -1,8 +1,8 @@ -#ifndef INCLUDE_HEALTH_BAR_HPP_ -#define INCLUDE_HEALTH_BAR_HPP_ +#pragma once #include "components.hpp" #include "gui.hpp" +#include namespace cpprl { @@ -12,10 +12,9 @@ class HealthBar : public UiWindow { public: HealthBar( - int width, int height, Vector2D position, DefenseComponent& defense); + int width, int height, SupaRL::Vector2D position, DefenseComponent& defense); void render(tcod::Console& console) override; }; -} // namespace cpprl -#endif +} diff --git a/app/include/input_handler.hpp b/app/include/input_handler.hpp index f9b7660..c9ba3c5 100644 --- a/app/include/input_handler.hpp +++ b/app/include/input_handler.hpp @@ -1,151 +1,148 @@ -#ifndef INPUT_HANDLER_H -#define INPUT_HANDLER_H +#pragma once + #include -#include #include "events/command.hpp" -#include "globals.hpp" #include "gui.hpp" #include "types/world_fwd.hpp" namespace cpprl { -class Entity; + class Entity; -class EventHandler { - public: - EventHandler(World& world) - : world_(world), + class EventHandler { + public: + EventHandler(World& world) + : world_(world), noop_(std::make_unique(world)), quitCommand_(std::make_unique(world)){}; - virtual ~EventHandler() = default; - virtual EngineEvent& handle_sdl_event(SDL_Event event) noexcept = 0; - - protected: - World& world_; - std::unique_ptr noop_; - - private: - std::unique_ptr quitCommand_; -}; - -class TargetingInputHandler final : public EventHandler { - private: - std::unique_ptr exit_targeting_mode_command_; - std::unique_ptr mouse_input_event_; - std::unique_ptr mouse_click_event_; - - public: - TargetingInputHandler(World& world) - : EventHandler(world), + virtual ~EventHandler() = default; + virtual EngineEvent& handle_sdl_event(SDL_Event event) noexcept = 0; + + protected: + World& world_; + std::unique_ptr noop_; + + private: + std::unique_ptr quitCommand_; + }; + + class TargetingInputHandler final : public EventHandler { + private: + std::unique_ptr exit_targeting_mode_command_; + std::unique_ptr mouse_input_event_; + std::unique_ptr mouse_click_event_; + + public: + TargetingInputHandler(World& world) + : EventHandler(world), exit_targeting_mode_command_( std::make_unique(world)){}; - ~TargetingInputHandler(){}; - EngineEvent& handle_sdl_event(SDL_Event event) noexcept override; -}; - -class GameInputHandler final : public EventHandler { - private: - std::unique_ptr button_right_; - std::unique_ptr button_up_; - std::unique_ptr button_down_; - std::unique_ptr button_up_right_; - std::unique_ptr button_up_left_; - std::unique_ptr button_left_; - std::unique_ptr button_down_right_; - std::unique_ptr button_down_left_; - std::unique_ptr view_history_command_; - std::unique_ptr pick_up_command_; - std::unique_ptr inventory_command_; - std::unique_ptr main_menu_command_; - std::unique_ptr use_command_; - std::unique_ptr character_menu_command_; - Entity* controllable_entity_; - - public: - GameInputHandler(World& world, Entity* controllable_entity); - ~GameInputHandler() = default; - - EngineEvent& handle_sdl_event(SDL_Event event) noexcept override; -}; - -class MenuInputHandler final : public EventHandler { - private: - std::unique_ptr resetGameCommand_; - - public: - MenuInputHandler(World& world) - : EventHandler(world), + ~TargetingInputHandler(){}; + EngineEvent& handle_sdl_event(SDL_Event event) noexcept override; + }; + + class GameInputHandler final : public EventHandler { + private: + std::unique_ptr button_right_; + std::unique_ptr button_up_; + std::unique_ptr button_down_; + std::unique_ptr button_up_right_; + std::unique_ptr button_up_left_; + std::unique_ptr button_left_; + std::unique_ptr button_down_right_; + std::unique_ptr button_down_left_; + std::unique_ptr view_history_command_; + std::unique_ptr pick_up_command_; + std::unique_ptr inventory_command_; + std::unique_ptr main_menu_command_; + std::unique_ptr use_command_; + std::unique_ptr character_menu_command_; + Entity* controllable_entity_; + + public: + GameInputHandler(World& world, Entity* controllable_entity); + ~GameInputHandler() = default; + + EngineEvent& handle_sdl_event(SDL_Event event) noexcept override; + }; + + class MenuInputHandler final : public EventHandler { + private: + std::unique_ptr resetGameCommand_; + + public: + MenuInputHandler(World& world) + : EventHandler(world), resetGameCommand_(std::make_unique(world)){}; - ~MenuInputHandler() = default; - EngineEvent& handle_sdl_event(SDL_Event event) noexcept override; -}; - -class GuiInputHandler : public EventHandler { - protected: - std::unique_ptr closeViewCommand_; - std::unique_ptr scrollDownCommand_; - std::unique_ptr scrollUpCommand_; - std::unique_ptr jumpUpCommand_; - std::unique_ptr jumpDownCommand_; - std::unique_ptr jumpToHome_; - - public: - GuiInputHandler(World& world, UiWindow& ui_window); - ~GuiInputHandler() = default; - - EngineEvent& handle_sdl_event(SDL_Event event) noexcept override; -}; - -class HistoryViewInputHandler final : public GuiInputHandler { - public: - HistoryViewInputHandler(World& world, UiWindow& ui_window) - : GuiInputHandler(world, ui_window){}; - ~HistoryViewInputHandler() = default; - - EngineEvent& handle_sdl_event(SDL_Event event) noexcept override; -}; - -class InventoryInputHandler final : public GuiInputHandler { - private: - std::unique_ptr selectItemCommand_; - std::unique_ptr dropItemCommand_; - - public: - InventoryInputHandler(World& world, Entity* entity, UiWindow& ui_window) - : GuiInputHandler(world, ui_window), + ~MenuInputHandler() = default; + EngineEvent& handle_sdl_event(SDL_Event event) noexcept override; + }; + + class GuiInputHandler : public EventHandler { + protected: + std::unique_ptr closeViewCommand_; + std::unique_ptr scrollDownCommand_; + std::unique_ptr scrollUpCommand_; + std::unique_ptr jumpUpCommand_; + std::unique_ptr jumpDownCommand_; + std::unique_ptr jumpToHome_; + + public: + GuiInputHandler(World& world, UiWindow& ui_window); + ~GuiInputHandler() = default; + + EngineEvent& handle_sdl_event(SDL_Event event) noexcept override; + }; + + class HistoryViewInputHandler final : public GuiInputHandler { + public: + HistoryViewInputHandler(World& world, UiWindow& ui_window) + : GuiInputHandler(world, ui_window){}; + ~HistoryViewInputHandler() = default; + + EngineEvent& handle_sdl_event(SDL_Event event) noexcept override; + }; + + class InventoryInputHandler final : public GuiInputHandler { + private: + std::unique_ptr selectItemCommand_; + std::unique_ptr dropItemCommand_; + + public: + InventoryInputHandler(World& world, Entity* entity, UiWindow& ui_window) + : GuiInputHandler(world, ui_window), selectItemCommand_(std::make_unique( - world, entity, ui_window, ItemSubCommand::USE_ITEM)), + world, entity, ui_window, ItemSubCommand::USE_ITEM)), dropItemCommand_(std::make_unique( - world, entity, ui_window, ItemSubCommand::DROP_ITEM)){}; - ~InventoryInputHandler() = default; - EngineEvent& handle_sdl_event(SDL_Event event) noexcept override; -}; - -class CharacterMenuInputHandler final : public GuiInputHandler { - private: - std::unique_ptr boost_stat_command_; - - public: - CharacterMenuInputHandler(World& world, UiWindow& ui_window) - : GuiInputHandler(world, ui_window), + world, entity, ui_window, ItemSubCommand::DROP_ITEM)){}; + ~InventoryInputHandler() = default; + EngineEvent& handle_sdl_event(SDL_Event event) noexcept override; + }; + + class CharacterMenuInputHandler final : public GuiInputHandler { + private: + std::unique_ptr boost_stat_command_; + + public: + CharacterMenuInputHandler(World& world, UiWindow& ui_window) + : GuiInputHandler(world, ui_window), boost_stat_command_( std::make_unique(world, ui_window)){}; - EngineEvent& handle_sdl_event(SDL_Event event) noexcept override; -}; + EngineEvent& handle_sdl_event(SDL_Event event) noexcept override; + }; -class MainMenuInputHandler final : public GuiInputHandler { - private: - std::unique_ptr selectMenuItemCommand_; + class MainMenuInputHandler final : public GuiInputHandler { + private: + std::unique_ptr selectMenuItemCommand_; - public: - MainMenuInputHandler(World& world, UiWindow& ui_window) - : GuiInputHandler(world, ui_window), + public: + MainMenuInputHandler(World& world, UiWindow& ui_window) + : GuiInputHandler(world, ui_window), selectMenuItemCommand_( std::make_unique(world, ui_window)){}; - virtual ~MainMenuInputHandler() = default; - EngineEvent& handle_sdl_event(SDL_Event event) noexcept override; -}; -} // namespace cpprl -#endif + virtual ~MainMenuInputHandler() = default; + EngineEvent& handle_sdl_event(SDL_Event event) noexcept override; + }; +} diff --git a/app/include/rectangular_room.hpp b/app/include/rectangular_room.hpp index f87fc06..fbbb9fe 100644 --- a/app/include/rectangular_room.hpp +++ b/app/include/rectangular_room.hpp @@ -1,29 +1,28 @@ #pragma once #include +#include #include -#include "types/math.hpp" - namespace cpprl { -class RectangularRoom { - private: - Vector2D bottomLeft_, topRight_, center_; + class RectangularRoom { + private: + SupaRL::Vector2D bottomLeft_, topRight_, center_; - public: - RectangularRoom(Vector2D bottomLeft, int width, int height) { - bottomLeft_ = bottomLeft; - topRight_ = {bottomLeft.x + width, bottomLeft.y + height}; - center_ = (bottomLeft_ + topRight_) / 2; - }; - ~RectangularRoom(){}; + public: + RectangularRoom(SupaRL::Vector2D bottomLeft, int width, int height) { + bottomLeft_ = bottomLeft; + topRight_ = {bottomLeft.x + width, bottomLeft.y + height}; + center_ = (bottomLeft_ + topRight_) / 2; + }; + ~RectangularRoom(){}; - /** Return the innerBounds area of this room as a 2D array index */ - std::tuple innerBounds() const; - bool intersects(RectangularRoom other) const; - Vector2D get_bottom_left() const { return bottomLeft_; } - Vector2D get_top_right() const { return topRight_; } - Vector2D get_center() { return center_; } -}; -} // namespace cpprl + /** Return the innerBounds area of this room as a 2D array index */ + std::tuple innerBounds() const; + bool intersects(RectangularRoom other) const; + SupaRL::Vector2D get_bottom_left() const { return bottomLeft_; } + SupaRL::Vector2D get_top_right() const { return topRight_; } + SupaRL::Vector2D get_center() { return center_; } + }; +} diff --git a/app/include/rendering.hpp b/app/include/rendering.hpp index 3a6e39e..f96de96 100644 --- a/app/include/rendering.hpp +++ b/app/include/rendering.hpp @@ -1,5 +1,4 @@ -#ifndef RENDERING_HPP -#define RENDERING_HPP +#pragma once #include @@ -7,42 +6,42 @@ #include "globals.hpp" #include "util.hpp" +#include namespace cpprl { -class ASCIIComponent; -class TransformComponent; -class Renderer { - public: - Renderer() {} - virtual ~Renderer() {} + class ASCIIComponent; + class TransformComponent; + class Renderer { + public: + Renderer() {} + virtual ~Renderer() {} - virtual void render( - ASCIIComponent& sprite, TransformComponent& transform) = 0; -}; - -class TCODRenderer : public Renderer { - public: - TCODRenderer(int argc, char** argv) : Renderer() { - g_console = tcod::Console{80, 40}; - auto params = TCOD_ContextParams{}; - params.tcod_version = TCOD_COMPILEDVERSION; - params.argc = argc; - params.argv = argv; - params.renderer_type = TCOD_RENDERER_SDL2; - params.vsync = 1; - params.sdl_window_flags = SDL_WINDOW_RESIZABLE; - params.window_title = "rogue_like"; - auto tileset = tcod::load_tilesheet( - cpprl::util::get_data_dir() / "dejavu16x16_gs_tc.png", - {32, 8}, - tcod::CHARMAP_TCOD); - params.tileset = tileset.get(); - params.console = g_console.get(); - g_context = tcod::Context{params}; + virtual void render( + ASCIIComponent& sprite, SupaRL::TransformComponent& transform) = 0; }; - ~TCODRenderer() = default; - virtual void render(ASCIIComponent& sprite, TransformComponent& transform); -}; -} // namespace cpprl -#endif + class TCODRenderer : public Renderer { + public: + TCODRenderer(int argc, char** argv) : Renderer() { + g_console = tcod::Console{80, 40}; + auto params = TCOD_ContextParams{}; + params.tcod_version = TCOD_COMPILEDVERSION; + params.argc = argc; + params.argv = argv; + params.renderer_type = TCOD_RENDERER_SDL2; + params.vsync = 1; + params.sdl_window_flags = SDL_WINDOW_RESIZABLE; + params.window_title = "rogue_like"; + auto tileset = tcod::load_tilesheet( + cpprl::util::get_data_dir() / "dejavu16x16_gs_tc.png", + {32, 8}, + tcod::CHARMAP_TCOD); + params.tileset = tileset.get(); + params.console = g_console.get(); + g_context = tcod::Context{params}; + }; + ~TCODRenderer() = default; + + virtual void render(ASCIIComponent& sprite, SupaRL::TransformComponent& transform); + }; +} diff --git a/app/include/state.hpp b/app/include/state.hpp index 98f15b4..b8c130f 100644 --- a/app/include/state.hpp +++ b/app/include/state.hpp @@ -1,121 +1,117 @@ -#ifndef STATE_HPP -#define STATE_HPP +#pragma once #include -#include "components.hpp" #include "input_handler.hpp" -#include "types/engine_fwd.hpp" #include "types/state_result.hpp" namespace cpprl { -class Renderer; -class UiWindow; - -class State { - protected: - World& world_; - std::unique_ptr input_handler_; - - public: - State(World& world) : world_(world), input_handler_(nullptr){}; - virtual ~State() = default; - virtual void on_enter(){}; - virtual StateResult on_update(SDL_Event& sdl_event); - virtual void render(Renderer&){}; - virtual void on_exit(){}; -}; - -class InGameState final : public State { - public: - InGameState(World& world) : State(world) {} - void on_enter() override; -}; - -class NextLevelState final : public State { - public: - NextLevelState(World& world) : State(world) {} - void on_enter() override; - StateResult on_update(SDL_Event& sdl_event) override; -}; - -class PickTileState : public State { - protected: - int max_range_; - std::function on_pick_; - - public: - PickTileState(World& world, std::function on_pick, int max_range) - : State(world), max_range_(max_range), on_pick_(on_pick) {} - ~PickTileState() override = default; - - void on_enter() override; - StateResult on_update(SDL_Event& sdl_event) override; - void render(Renderer& renderer) override; - void on_exit() override; -}; - -class PickTileAOEState final : public PickTileState { - private: - int aoe_; - int aoe_squared_; - - public: - PickTileAOEState( - World& world, - std::function const& on_pick, - int max_range_, - int aoe) - : PickTileState(world, on_pick, max_range_), + class Renderer; + class UiWindow; + + class State { + protected: + World& world_; + std::unique_ptr input_handler_; + + public: + State(World& world) : world_(world), input_handler_(nullptr){}; + virtual ~State() = default; + virtual void on_enter(){}; + virtual StateResult on_update(SDL_Event& sdl_event); + virtual void render(Renderer&){}; + virtual void on_exit(){}; + }; + + class InGameState final : public State { + public: + InGameState(World& world) : State(world) {} + void on_enter() override; + }; + + class NextLevelState final : public State { + public: + NextLevelState(World& world) : State(world) {} + void on_enter() override; + StateResult on_update(SDL_Event& sdl_event) override; + }; + + class PickTileState : public State { + protected: + int max_range_; + std::function on_pick_; + + public: + PickTileState(World& world, std::function on_pick, int max_range) + : State(world), max_range_(max_range), on_pick_(on_pick) {} + ~PickTileState() override = default; + + void on_enter() override; + StateResult on_update(SDL_Event& sdl_event) override; + void render(Renderer& renderer) override; + void on_exit() override; + }; + + class PickTileAOEState final : public PickTileState { + private: + int aoe_; + int aoe_squared_; + + public: + PickTileAOEState( + World& world, + std::function const& on_pick, + int max_range_, + int aoe) + : PickTileState(world, on_pick, max_range_), aoe_(aoe), aoe_squared_(aoe_ * aoe_) {} - void render(Renderer& renderer) override; -}; - -class GuiViewState : public State { - protected: - UiWindow* window_; - - public: - GuiViewState(World& world, UiWindow* window) - : State(world), window_(window) {} - void render(Renderer& renderer) override; -}; - -class ViewMessageHistoryState final : public GuiViewState { - public: - ViewMessageHistoryState(World& world, UiWindow* window) - : GuiViewState(world, window) {} - void on_enter() override; -}; - -class ViewInventoryState final : public GuiViewState { - public: - using GuiViewState::GuiViewState; - void on_enter() override; -}; - -class GameOverState final : public GuiViewState { - public: - GameOverState(World& world) : GuiViewState(world, nullptr) {} - void on_enter() override; -}; - -class MainMenuState final : public GuiViewState { - public: - MainMenuState(World& world, UiWindow* window) : GuiViewState(world, window) {} - ~MainMenuState() { delete window_; } - void on_enter() override; -}; - -class CharacterMenuState final : public GuiViewState { - public: - CharacterMenuState(World& world, UiWindow* window) - : GuiViewState(world, window) {} - void on_enter() override; -}; - -} // namespace cpprl - -#endif + void render(Renderer& renderer) override; + }; + + class GuiViewState : public State { + protected: + UiWindow* window_; + + public: + GuiViewState(World& world, UiWindow* window) + : State(world), window_(window) {} + void render(Renderer& renderer) override; + }; + + class ViewMessageHistoryState final : public GuiViewState { + public: + ViewMessageHistoryState(World& world, UiWindow* window) + : GuiViewState(world, window) {} + void on_enter() override; + }; + + class ViewInventoryState final : public GuiViewState { + public: + using GuiViewState::GuiViewState; + void on_enter() override; + }; + + class GameOverState final : public GuiViewState { + public: + GameOverState(World& world) : GuiViewState(world, nullptr) {} + void on_enter() override; + }; + + class MainMenuState final : public GuiViewState { + public: + MainMenuState(World& world, UiWindow* window) : GuiViewState(world, window) {} + ~MainMenuState() { delete window_; } + void on_enter() override; + }; + + class CharacterMenuState final : public GuiViewState { + public: + CharacterMenuState(World& world, UiWindow* window) + : GuiViewState(world, window) {} + void on_enter() override; + }; + +} + diff --git a/app/include/types/action_result.hpp b/app/include/types/action_result.hpp index a21fa53..b2b4926 100644 --- a/app/include/types/action_result.hpp +++ b/app/include/types/action_result.hpp @@ -1,5 +1,4 @@ -#ifndef ACTION_RESULT_HPP -#define ACTION_RESULT_HPP +#pragma once #include #include @@ -8,17 +7,16 @@ #include "state.hpp" namespace cpprl { -class State; -struct Success {}; + class State; + struct Success {}; -struct Failure { - std::string message; -}; + struct Failure { + std::string message; + }; -struct Poll { - std::unique_ptr new_state; -}; + struct Poll { + std::unique_ptr new_state; + }; -using ActionResult = std::variant; -} // namespace cpprl -#endif + using ActionResult = std::variant; +} diff --git a/app/include/types/map.hpp b/app/include/types/map.hpp index d75b72e..a882b34 100644 --- a/app/include/types/map.hpp +++ b/app/include/types/map.hpp @@ -1,129 +1,130 @@ #pragma once + #include #include "../game_entity.hpp" #include "../rectangular_room.hpp" -#include "math.hpp" +#include #include "nparray.hpp" #include "types/tile.hpp" namespace cpprl { -template -inline void with_indexes(int width, int height, Func func) { - for (int y{0}; y < height; ++y) { - for (int x{0}; x < width; ++x) { - func(x, y); + template + inline void with_indexes(int width, int height, Func func) { + for (int y{0}; y < height; ++y) { + for (int x{0}; x < width; ++x) { + func(x, y); + } + } } - } -} -template -inline void with_indexes(const Array& array, F func) { - with_indexes(array.get_width(), array.get_height(), func); -} - -class Map { - public: - Map() : Map(0, 0){}; - Map(int width, int height); - ~Map() = default; - int get_height() const { return height_; } - int get_width() const { return width_; } - bool is_in_bounds(Vector2D position) const; - bool is_not_in_bounds(Vector2D position) const { - return !is_in_bounds(position); - } - bool is_explored(Vector2D position); - void compute_fov(Vector2D position, int radius); - bool is_in_fov(Vector2D position); - /** Sets the tile at position to explored. */ - void set_is_explored(Vector2D position) { - tiles_.at(position).explored = true; - } - bool is_walkable(Vector2D position) const; - Array2D& get_tiles() { return tiles_; } - void set_tiles_range(std::tuple bounds, Tile tile); - void set_rooms(std::vector rooms) { _rooms = rooms; } - void set_down_stairs_location(Vector2D position) { - down_stairs_location_ = position; - } - RectangularRoom get_first_room() { return _rooms.front(); } - std::vector get_rooms() { return _rooms; } - void set_tiles_at(Vector2D position, Tile tile); - /** Returns the wall tile for this map */ - TileGraphic& get_wall_tile() { return wall_tile_; } - /** Returns the floor tile for this map */ - TileGraphic& get_floor_tile() { return floor_tile_; } - - /** - * @brief Returns the tile graphic for the given tile type. - * @param type The tile type. - * @return The tile graphic. - */ - TileGraphic& get_tile_graphic(TileType type) { - if (type == TileType::wall) { - return wall_tile_; - } else if (type == TileType::floor) { - return floor_tile_; - } else if (type == TileType::down_stairs) { - return downstairs_tile_; - } else { - return wall_tile_; + template + inline void with_indexes(const Array& array, F func) { + with_indexes(array.get_width(), array.get_height(), func); } - } - /** Render the map */ - void render(tcod::Console& console); - void set_highlight_tile(Vector2D position); - void toggle_target_mode(float max_range) { - max_range_ = max_range; - target_mode_ = !target_mode_; - } - bool set_target_tile(Vector2D position, Entity& player); - Vector2D get_highlight_tile() { return target_tile_; } + class Map { + public: + Map() : Map(0, 0){}; + Map(int width, int height); + ~Map() = default; + int get_height() const { return height_; } + int get_width() const { return width_; } + bool is_in_bounds(SupaRL::Vector2D position) const; + bool is_not_in_bounds(SupaRL::Vector2D position) const { + return !is_in_bounds(position); + } + bool is_explored(SupaRL::Vector2D position); + void compute_fov(SupaRL::Vector2D position, int radius); + bool is_in_fov(SupaRL::Vector2D position); + /** Sets the tile at position to explored. */ + void set_is_explored(SupaRL::Vector2D position) { + tiles_.at(position).explored = true; + } + bool is_walkable(SupaRL::Vector2D position) const; + Array2D& get_tiles() { return tiles_; } + void set_tiles_range(std::tuple bounds, Tile tile); + void set_rooms(std::vector rooms) { _rooms = rooms; } + void set_down_stairs_location(SupaRL::Vector2D position) { + down_stairs_location_ = position; + } + RectangularRoom get_first_room() { return _rooms.front(); } + std::vector get_rooms() { return _rooms; } + void set_tiles_at(SupaRL::Vector2D position, Tile tile); + /** Returns the wall tile for this map */ + TileGraphic& get_wall_tile() { return wall_tile_; } + /** Returns the floor tile for this map */ + TileGraphic& get_floor_tile() { return floor_tile_; } - template - void save(Archive& archive) const { - archive(width_, height_); - for (int y{0}; y < get_height(); ++y) { - for (int x{0}; x < get_width(); ++x) { - archive(tiles_.at({x, y}).explored); + /** + * @brief Returns the tile graphic for the given tile type. + * @param type The tile type. + * @return The tile graphic. + */ + TileGraphic& get_tile_graphic(TileType type) { + if (type == TileType::wall) { + return wall_tile_; + } else if (type == TileType::floor) { + return floor_tile_; + } else if (type == TileType::down_stairs) { + return downstairs_tile_; + } else { + return wall_tile_; + } } - } - } - template - void load(Archive& archive) { - archive(width_, height_); - // TODO: Failing here because map is 0,0 - for (int y{0}; y < get_height(); ++y) { - for (int x{0}; x < get_width(); ++x) { - archive(tiles_.at({x, y}).explored); + /** Render the map */ + void render(tcod::Console& console); + void set_highlight_tile(SupaRL::Vector2D position); + void toggle_target_mode(float max_range) { + max_range_ = max_range; + target_mode_ = !target_mode_; } - } - } + bool set_target_tile(SupaRL::Vector2D position, Entity& player); + SupaRL::Vector2D get_highlight_tile() { return target_tile_; } - private: - /** The wall tile */ - TileGraphic wall_tile_; - /** The floor tile */ - TileGraphic floor_tile_; - TileGraphic downstairs_tile_; - /** The width and height of this map. */ - int width_, height_; - /** This maps tiles */ - Array2D tiles_; + template + void save(Archive& archive) const { + archive(width_, height_); + for (int y{0}; y < get_height(); ++y) { + for (int x{0}; x < get_width(); ++x) { + archive(tiles_.at({x, y}).explored); + } + } + } - /** - * @brief tcod map used for fov calculations - */ - TCODMap tcod_map_; - std::vector _rooms; - Vector2D target_tile_ = {0, 0}; - bool target_mode_ = false; - float max_range_ = 0.0f; - Vector2D down_stairs_location_{0, 0}; -}; + template + void load(Archive& archive) { + archive(width_, height_); + // TODO: Failing here because map is 0,0 + for (int y{0}; y < get_height(); ++y) { + for (int x{0}; x < get_width(); ++x) { + archive(tiles_.at({x, y}).explored); + } + } + } -} // namespace cpprl + private: + /** The wall tile */ + TileGraphic wall_tile_; + /** The floor tile */ + TileGraphic floor_tile_; + TileGraphic downstairs_tile_; + /** The width and height of this map. */ + int width_, height_; + /** This maps tiles */ + Array2D tiles_; + + /** + * @brief tcod map used for fov calculations + */ + TCODMap tcod_map_; + std::vector _rooms; + SupaRL::Vector2D target_tile_ = {0, 0}; + bool target_mode_ = false; + float max_range_ = 0.0f; + SupaRL::Vector2D down_stairs_location_{0, 0}; + }; + +} diff --git a/app/include/types/math.hpp b/app/include/types/math.hpp index e584e7c..795ad8a 100644 --- a/app/include/types/math.hpp +++ b/app/include/types/math.hpp @@ -1,68 +1,42 @@ -#ifndef TYPES_MATH_HPP -#define TYPES_MATH_HPP +#pragma once #include + #include +#include namespace cpprl { -struct Vector2D { - operator const std::array() const noexcept { return {x, y}; } - - Vector2D operator+(const Vector2D& other) const noexcept { return {x + other.x, y + other.y}; } - Vector2D operator+=(const Vector2D& other) noexcept { - x += other.x; - y += other.y; - return *this; - } - Vector2D operator-(const Vector2D& other) const noexcept { return {x - other.x, y - other.y}; } - Vector2D operator/(int scalar) const noexcept { return {x / scalar, y / scalar}; } - bool operator==(const Vector2D& other) const noexcept { return x == other.x && y == other.y; } - bool operator!=(const Vector2D& other) const noexcept { return !(*this == other); } - float distance_to(const Vector2D& other) const noexcept { - return std::sqrt(std::pow(x - other.x, 2) + std::pow(y - other.y, 2)); - } - - int x; - int y; - - template - void serialize(Archive& archive) { - archive(x, y); - } -}; - -struct Quadrilateral { - operator const std::array() const noexcept { - return {position.x, position.y, width, height}; + struct Quadrilateral { + operator const std::array() const noexcept { + return {position.x, position.y, width, height}; + } + + bool operator==(const Quadrilateral& other) const noexcept { + return position == other.position && width == other.width && + height == other.height; + } + bool operator!=(const Quadrilateral& other) const noexcept { + return !(*this == other); + } + + SupaRL::Vector2D position; + int width; + int height; + + template + void serialize(Archive& archive) { + archive(position, width, height); + } + }; + + template + inline T euclidean_squared(T x, T y) { + return x * x + y * y; + } + inline int euclidean_squared(SupaRL::Vector2D vec) { + return euclidean_squared(vec.x, vec.y); } - bool operator==(const Quadrilateral& other) const noexcept { - return position == other.position && width == other.width && - height == other.height; - } - bool operator!=(const Quadrilateral& other) const noexcept { - return !(*this == other); - } - - Vector2D position; - int width; - int height; - - template - void serialize(Archive& archive) { - archive(position, width, height); - } -}; - -template -inline T euclidean_squared(T x, T y) { - return x * x + y * y; } -inline int euclidean_squared(Vector2D vec) { - return euclidean_squared(vec.x, vec.y); -} - -} // namespace cpprl -#endif // TYPES_MATH_HPP diff --git a/app/include/types/nparray.hpp b/app/include/types/nparray.hpp index b0e5bb7..ba7cc3a 100644 --- a/app/include/types/nparray.hpp +++ b/app/include/types/nparray.hpp @@ -1,55 +1,52 @@ -#ifndef CPPRL_TYPES_NPARRAY_HPP_ -#define CPPRL_TYPES_NPARRAY_HPP_ +#pragma once #include -#include "math.hpp" +#include namespace cpprl { -template -class Array2D { - public: - Array2D(int rows, int cols, T initialValue) : rows_(rows), cols_(cols) { - data_.resize(rows_, std::vector(cols_, initialValue)); - } - - [[deprecated("Use at(Vector2D position) instead")]] T& at(int row, int col) { return at({row, col}); } - - const T& at(Vector2D position) const { - int row = position.x; - int col = position.y; - if (row < 0 || row >= rows_ || col < 0 || col >= cols_) { - throw std::out_of_range("Array2D index out of bounds"); - } - return data_[row][col]; - } - - T& at(Vector2D position) { - return const_cast(static_cast(this)->at(position)); - } - - void set(Vector2D position, T value) { data_[position.x][position.y] = value; } - - void set_range(std::tuple bounds, T value) { - Vector2D bottom_left, top_right; - std::tie(bottom_left, top_right) = bounds; - for (int x = bottom_left.x; x < top_right.x; x++) { - for (int y = bottom_left.y; y < top_right.y; y++) { - set({x, y}, value); - } - } - } - - int getRows() const { return rows_; } - - int getCols() const { return cols_; } - - private: - int rows_; - int cols_; - std::vector> data_; -}; - -} // namespace cpprl - -#endif // CPPRL_TYPES_NPARRAY_HPP_ + template + class Array2D { + public: + Array2D(int rows, int cols, T initialValue) : rows_(rows), cols_(cols) { + data_.resize(rows_, std::vector(cols_, initialValue)); + } + + [[deprecated("Use at(SupaRL::Vector2D position) instead")]] T& at(int row, int col) { return at({row, col}); } + + const T& at(SupaRL::Vector2D position) const { + int row = position.x; + int col = position.y; + if (row < 0 || row >= rows_ || col < 0 || col >= cols_) { + throw std::out_of_range("Array2D index out of bounds"); + } + return data_[row][col]; + } + + T& at(SupaRL::Vector2D position) { + return const_cast(static_cast(this)->at(position)); + } + + void set(SupaRL::Vector2D position, T value) { data_[position.x][position.y] = value; } + + void set_range(std::tuple bounds, T value) { + SupaRL::Vector2D bottom_left, top_right; + std::tie(bottom_left, top_right) = bounds; + for (int x = bottom_left.x; x < top_right.x; x++) { + for (int y = bottom_left.y; y < top_right.y; y++) { + set({x, y}, value); + } + } + } + + int getRows() const { return rows_; } + + int getCols() const { return cols_; } + + private: + int rows_; + int cols_; + std::vector> data_; + }; +} + diff --git a/app/include/ui/dungeon_level.hpp b/app/include/ui/dungeon_level.hpp index 8187fc6..9ccefd2 100644 --- a/app/include/ui/dungeon_level.hpp +++ b/app/include/ui/dungeon_level.hpp @@ -2,16 +2,17 @@ #include "dungeon.hpp" #include "gui.hpp" +#include namespace cpprl { -class DungeonLevel final : public UiWindow { - private: - Dungeon& dungeon_; + class DungeonLevel final : public UiWindow { + private: + Dungeon& dungeon_; - public: - DungeonLevel(int width, int height, Vector2D position, Dungeon& dungeon) - : UiWindow(width, height, position), dungeon_(dungeon) {} + public: + DungeonLevel(int width, int height, SupaRL::Vector2D position, Dungeon& dungeon) + : UiWindow(width, height, position), dungeon_(dungeon) {} - void render(tcod::Console& console) override; -}; -} // namespace cpprl + void render(tcod::Console& console) override; + }; +} diff --git a/app/include/util.hpp b/app/include/util.hpp index 3b8e68b..d2d044c 100644 --- a/app/include/util.hpp +++ b/app/include/util.hpp @@ -1,38 +1,36 @@ -#ifndef UTIL_HPP -#define UTIL_HPP +#pragma once #include #include "components.hpp" #include "types/math.hpp" namespace cpprl::util { -template -inline auto find_entity_at(std::vector& vec, int x, int y) { - auto iterator = std::find_if(vec.begin(), vec.end(), [x, y](T& entity) { - return entity->get_transform_component().get_position() == Vector2D{x, y}; - }); - return iterator; -} + template + inline auto find_entity_at(std::vector& vec, int x, int y) { + auto iterator = std::find_if(vec.begin(), vec.end(), [x, y](T& entity) { + return entity->get_transform_component().position_ == SupaRL::Vector2D{x, y}; + }); + return iterator; + } -inline auto get_data_dir() -> std::filesystem::path { - static auto root_directory = + inline auto get_data_dir() -> std::filesystem::path { + static auto root_directory = std::filesystem::path{"."}; // Begin at the working directory. - while (!std::filesystem::exists(root_directory / "data")) { - // If the current working directory is missing the data dir then it will - // assume it exists in any parent directory. - root_directory /= ".."; - if (!std::filesystem::exists(root_directory)) { - throw std::runtime_error("Could not find the data directory."); + while (!std::filesystem::exists(root_directory / "data")) { + // If the current working directory is missing the data dir then it will + // assume it exists in any parent directory. + root_directory /= ".."; + if (!std::filesystem::exists(root_directory)) { + throw std::runtime_error("Could not find the data directory."); + } } - } - return root_directory / "data"; -}; + return root_directory / "data"; + }; -inline std::string capitalize(const std::string& string) { - auto ret = string; - auto ch = ret[0]; - ret[0] = std::toupper(ch); - return ret; + inline std::string capitalize(const std::string& string) { + auto ret = string; + auto ch = ret[0]; + ret[0] = std::toupper(ch); + return ret; + } } -} // namespace cpprl::util -#endif // UTIL_HPP diff --git a/app/include/world.hpp b/app/include/world.hpp index f8f362b..0e1be15 100644 --- a/app/include/world.hpp +++ b/app/include/world.hpp @@ -1,5 +1,4 @@ -#ifndef WORLD_HPP -#define WORLD_HPP +#pragma once #include @@ -11,79 +10,78 @@ #include "ui/ui.hpp" namespace cpprl { -class EntityManager; -class Entity; -class UiWindow; -struct Controller; + class EntityManager; + class Entity; + class UiWindow; + struct Controller; -class World { - private: - std::unique_ptr entities_; - Dungeon dungeon_; - MessageLog message_log_; - std::unique_ptr ui_; - std::unique_ptr controller_; - UiWindow* current_window_; - Entity* player_; + class World { + private: + std::unique_ptr entities_; + Dungeon dungeon_; + MessageLog message_log_; + std::unique_ptr ui_; + std::unique_ptr controller_; + UiWindow* current_window_; + Entity* player_; - public: - World(); - virtual ~World() = default; + public: + World(); + virtual ~World() = default; - MessageLog& get_message_log() { return message_log_; } - Map& get_map() { return dungeon_.get_map(); } - EntityManager& get_entities() { return *entities_; } - void reset(); + MessageLog& get_message_log() { return message_log_; } + Map& get_map() { return dungeon_.get_map(); } + EntityManager& get_entities() { return *entities_; } + void reset(); - void generate_map(int width, int height, bool with_entities = false); - Dungeon& get_dungeon() { return dungeon_; } - void render(Renderer& renderer); - void handle_enemy_turns(); - void scroll_current_view(int scroll_amount); - void handle_player_death(); - void set_targeting_tile( - float max_range = 0.0f, std::function callback = nullptr); - Entity* get_player() const { return player_; } - void spawn_player(); - void spawn_player(Entity* player); + void generate_map(int width, int height, bool with_entities = false); + Dungeon& get_dungeon() { return dungeon_; } + void render(Renderer& renderer); + void handle_enemy_turns(); + void scroll_current_view(int scroll_amount); + void handle_player_death(); + void set_targeting_tile( + float max_range = 0.0f, std::function callback = nullptr); + Entity* get_player() const { return player_; } + void spawn_player(); + void spawn_player(Entity* player); - template - void save(Archive& archive) const { - archive(dungeon_); - int width = dungeon_.get_map().get_width(); - int height = dungeon_.get_map().get_height(); - archive(width, height); - for (int y{0}; y < dungeon_.get_map().get_height(); ++y) { - for (int x{0}; x < dungeon_.get_map().get_width(); ++x) { - archive(dungeon_.get_map().get_tiles().at({x, y}).explored); - } - } - archive(entities_); - player_->pack(archive); - archive(message_log_); - } + template + void save(Archive& archive) const { + archive(dungeon_); + int width = dungeon_.get_map().get_width(); + int height = dungeon_.get_map().get_height(); + archive(width, height); + for (int y{0}; y < dungeon_.get_map().get_height(); ++y) { + for (int x{0}; x < dungeon_.get_map().get_width(); ++x) { + archive(dungeon_.get_map().get_tiles().at({x, y}).explored); + } + } + archive(entities_); + player_->pack(archive); + archive(message_log_); + } - template - void load(Archive& archive) { - archive(dungeon_); - int width; - int height; - archive(width, height); - generate_map(width, height, false); - for (int y{0}; y < dungeon_.get_map().get_height(); ++y) { - for (int x{0}; x < dungeon_.get_map().get_width(); ++x) { - archive(dungeon_.get_map().get_tiles().at({x, y}).explored); - } - } - archive(entities_); - player_ = new Entity("", false, nullptr, nullptr); - // TODO: If player is confused, quitting and reopening the game removes the - // confused state - player_->unpack(archive); - spawn_player(player_); - archive(message_log_); - } -}; -} // namespace cpprl + template + void load(Archive& archive) { + archive(dungeon_); + int width; + int height; + archive(width, height); + generate_map(width, height, false); + for (int y{0}; y < dungeon_.get_map().get_height(); ++y) { + for (int x{0}; x < dungeon_.get_map().get_width(); ++x) { + archive(dungeon_.get_map().get_tiles().at({x, y}).explored); + } + } + archive(entities_); + player_ = new Entity("", false, nullptr, nullptr); + // TODO: If player is confused, quitting and reopening the game removes the + // confused state + player_->unpack(archive); + spawn_player(player_); + archive(message_log_); + } + }; +} -#endif diff --git a/app/include/xp_bar.hpp b/app/include/xp_bar.hpp index e54337b..cec5ce2 100644 --- a/app/include/xp_bar.hpp +++ b/app/include/xp_bar.hpp @@ -1,19 +1,16 @@ -#ifndef INCLUDE_XP_BAR_HPP_ -#define INCLUDE_XP_BAR_HPP_ - +#pragma once #include "components.hpp" #include "gui.hpp" +#include namespace cpprl { + class XPBar : public UiWindow { + private: + StatsComponent& stats_; -class XPBar : public UiWindow { - private: - StatsComponent& stats_; - - public: - XPBar(int width, int height, Vector2D position, StatsComponent& stats); - void render(tcod::Console& console) override; -}; + public: + XPBar(int width, int height, SupaRL::Vector2D position, StatsComponent& stats); + void render(tcod::Console& console) override; + }; -} // namespace cpprl -#endif // INCLUDE_XP_BAR_HPP_ +} diff --git a/app/src/TCODRenderer.cpp b/app/src/TCODRenderer.cpp index 0170a69..68bce3e 100644 --- a/app/src/TCODRenderer.cpp +++ b/app/src/TCODRenderer.cpp @@ -2,13 +2,13 @@ #include "rendering.hpp" namespace cpprl { -void TCODRenderer::render( - ASCIIComponent& sprite, TransformComponent& transform) { - tcod::print( - g_console, - transform.get_position(), - sprite.get_symbol(), - sprite.get_colour(), - std::nullopt); + void TCODRenderer::render( + ASCIIComponent& sprite, SupaRL::TransformComponent& transform) { + tcod::print( + g_console, + transform.position_, + sprite.get_symbol(), + sprite.get_colour(), + std::nullopt); + } } -} // namespace cpprl diff --git a/app/src/basic_ai_component.cpp b/app/src/basic_ai_component.cpp index 6a79c44..c76845f 100644 --- a/app/src/basic_ai_component.cpp +++ b/app/src/basic_ai_component.cpp @@ -1,14 +1,13 @@ #include "basic_ai_component.hpp" #include -#include #include -#include "entity_manager.hpp" #include "events/command.hpp" #include "game_entity.hpp" -#include "types/map.hpp" +#include "entity_manager.hpp" #include "world.hpp" +#include namespace cpprl { @@ -23,11 +22,11 @@ bool can_path_to_target(tcod::BresenhamLine& path, World& world) { } void HostileAI::update(World& world, Entity* entity) { - Vector2D position = entity->get_transform_component().get_position(); + SupaRL::Vector2D position = entity->get_transform_component().position_; if (world.get_map().is_in_fov(position)) { Entity* player = world.get_player(); - Vector2D player_position = player->get_transform_component().get_position(); - Vector2D delta = player_position - position; + SupaRL::Vector2D player_position = player->get_transform_component().position_; + SupaRL::Vector2D delta = player_position - position; int distance = std::max(std::abs(delta.x), std::abs(delta.y)); if (distance <= 1) { @@ -40,7 +39,7 @@ void HostileAI::update(World& world, Entity* entity) { if (can_path_to_target(path, world)) { auto dest = path[0]; - auto destination = Vector2D{dest[0], dest[1]} - position; + auto destination = SupaRL::Vector2D{dest[0], dest[1]} - position; auto action = MovementCommand(world, entity, destination); action.execute(); diff --git a/app/src/components.cpp b/app/src/components.cpp index eb78424..ae27531 100644 --- a/app/src/components.cpp +++ b/app/src/components.cpp @@ -9,9 +9,7 @@ #include "combat_system.hpp" #include "entity_manager.hpp" #include "events/command.hpp" -#include "exceptions.hpp" #include "game_entity.hpp" -#include "globals.hpp" #include "state.hpp" #include "world.hpp" @@ -73,8 +71,9 @@ namespace cpprl { ActionResult ConsumableComponent::drop(Entity* owner, Entity* wearer) { if (auto* container = &wearer->get_container(); container) { container->remove(owner); - owner->get_transform_component().move( - wearer->get_transform_component().get_position()); + // TODO: This would then use the global coordinator to + // get the transform of each entity and set the positions. + owner->get_transform_component().position_ = wearer->get_transform_component().position_; return Success{}; } return Failure{}; @@ -112,7 +111,7 @@ namespace cpprl { ActionResult LightningBolt::use(Entity* owner, Entity* wearer, World& world) { std::optional> optional_closest_monster_ref = world.get_entities().get_closest_living_monster( - wearer->get_transform_component().get_position(), range_); + wearer->get_transform_component().position_, range_); if (!optional_closest_monster_ref.has_value()) { return Failure{"No enemy is close enough to strike."}; } @@ -148,7 +147,7 @@ namespace cpprl { for (Entity* entity : world.get_entities()) { if (const auto* defense_component = &entity->get_defense_component(); defense_component && defense_component->is_not_dead() && - entity->get_transform_component().get_position().distance_to( + entity->get_transform_component().position_.distance_to( world.get_map().get_highlight_tile()) <= aoe_) { world.get_message_log().add_message( fmt::format( diff --git a/app/src/consumable_factory.cpp b/app/src/consumable_factory.cpp index 04f1b5d..1e62240 100644 --- a/app/src/consumable_factory.cpp +++ b/app/src/consumable_factory.cpp @@ -4,37 +4,44 @@ #include "components.hpp" +extern SupaRL::Coordinator g_coordinator; namespace cpprl { -Entity* AbstractConsumableFactory::create_base( - std::string name, tcod::ColorRGB color, std::string_view symbol) { - return new Entity( - name, - false, - std::make_unique(0, 0), - std::make_unique(symbol, color, 1)); -} + Entity* AbstractConsumableFactory::create_base( + std::string name, tcod::ColorRGB color, std::string_view symbol) { + Entity* new_entity = new Entity( + name, + false, + std::make_unique(0, 0), + std::make_unique(symbol, color, 1)); + auto entity_id = g_coordinator.create_entity(); + new_entity->set_id(entity_id); + g_coordinator.add_component(entity_id, SupaRL::TransformComponent{ + .position_ = {0, 0}}); + return new_entity; -Entity* HealthPotionFactory::create() { - Entity* entity = create_base("Health Potion", RED, "!"); - entity->set_consumable_component(std::make_unique(10)); - return entity; -} + } -Entity* LightningScrollFactory::create() { - Entity* entity = create_base("Lightning Scroll", DARK_RED, "#"); - entity->set_consumable_component(std::make_unique(5, 20)); - return entity; -} + Entity* HealthPotionFactory::create() { + Entity* entity = create_base("Health Potion", RED, "!"); + entity->set_consumable_component(std::make_unique(10)); + return entity; + } -Entity* FireballScrollFactory::create() { - Entity* entity = create_base("Fireball Scroll", DARK_RED, "#"); - entity->set_consumable_component(std::make_unique(3, 5, 20)); - return entity; -} + Entity* LightningScrollFactory::create() { + Entity* entity = create_base("Lightning Scroll", DARK_RED, "#"); + entity->set_consumable_component(std::make_unique(5, 20)); + return entity; + } + + Entity* FireballScrollFactory::create() { + Entity* entity = create_base("Fireball Scroll", DARK_RED, "#"); + entity->set_consumable_component(std::make_unique(3, 5, 20)); + return entity; + } -Entity* ConfusionScrollFactory::create() { - Entity* entity = create_base("Confusion Scroll", DARK_RED, "#"); - entity->set_consumable_component(std::make_unique(5, 0)); - return entity; + Entity* ConfusionScrollFactory::create() { + Entity* entity = create_base("Confusion Scroll", DARK_RED, "#"); + entity->set_consumable_component(std::make_unique(5, 0)); + return entity; + } } -} // namespace cpprl diff --git a/app/src/dungeon.cpp b/app/src/dungeon.cpp index b2ebecf..e06c067 100644 --- a/app/src/dungeon.cpp +++ b/app/src/dungeon.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "rectangular_room.hpp" #include "types/map.hpp" @@ -15,7 +16,7 @@ void Dungeon::generate(DungeonConfig dungeon_config) { dungeon_config.map_width, dungeon_config.map_height); auto rooms = std::vector{}; - Vector2D last_room_center = {0, 0}; + SupaRL::Vector2D last_room_center = {0, 0}; for (int i = 0; i < dungeon_config.max_rooms; i++) { int room_width = rng_.getInt(dungeon_config.room_min_size, dungeon_config.room_max_size); @@ -37,11 +38,11 @@ void Dungeon::generate(DungeonConfig dungeon_config) { last_room_center = new_room.get_center(); if (!rooms.empty()) { - Vector2D previous_room_center = rooms.back().get_center(); - std::vector tunnel = + SupaRL::Vector2D previous_room_center = rooms.back().get_center(); + std::vector tunnel = l_tunnel_between(previous_room_center, new_room.get_center()); - for (const Vector2D position : tunnel) { + for (const SupaRL::Vector2D position : tunnel) { current_map_->set_tiles_at(position, FLOOR_TILE); } } @@ -53,8 +54,8 @@ void Dungeon::generate(DungeonConfig dungeon_config) { } constexpr float half_chance = 0.5F; -std::vector Dungeon::l_tunnel_between(Vector2D start, Vector2D end) { - Vector2D corner{0, 0}; +std::vector Dungeon::l_tunnel_between(SupaRL::Vector2D start, SupaRL::Vector2D end) { + SupaRL::Vector2D corner{0, 0}; if (rng_.get(0.0f, 1.0f) < half_chance) { corner = {end.x, start.y}; @@ -62,7 +63,7 @@ std::vector Dungeon::l_tunnel_between(Vector2D start, Vector2D end) { corner = {start.x, end.y}; } - std::vector tunnel{}; + std::vector tunnel{}; for (const auto&& [x, y] : tcod::BresenhamLine({start.x, start.y}, {corner.x, corner.y})) { diff --git a/app/src/entity_factory.cpp b/app/src/entity_factory.cpp index 493cc8a..ca639a8 100644 --- a/app/src/entity_factory.cpp +++ b/app/src/entity_factory.cpp @@ -101,4 +101,4 @@ namespace cpprl { return entity; } -} // namespace cpprl +} diff --git a/app/src/entity_manager.cpp b/app/src/entity_manager.cpp index a5ef0b6..5777d89 100644 --- a/app/src/entity_manager.cpp +++ b/app/src/entity_manager.cpp @@ -11,161 +11,160 @@ #include "consumable_factory.hpp" #include "util.hpp" -#include "../../lib/include/core/coordinator.hpp" - +#include "core/coordinator.hpp" namespace cpprl { -extern SupaRL::Coordinator g_coordinator; + extern SupaRL::Coordinator g_coordinator; -void EntityManager::clear() { entities_.clear(); } + void EntityManager::clear() { entities_.clear(); } -void EntityManager::clear_except_player() { - entities_.erase( - std::begin(std::ranges::remove_if( - entities_, - [](const Entity* entity) { return entity->get_name() != "Player"; })), - entities_.end()); -} + void EntityManager::clear_except_player() { + entities_.erase( + std::begin(std::ranges::remove_if( + entities_, + [](const Entity* entity) { return entity->get_name() != "Player"; })), + entities_.end()); + } -void EntityManager::place_entities( - RectangularRoom room, int max_monsters_per_room, int max_items_per_room) { - auto* random = TCODRandom::getInstance(); - int number_of_monsters = random->getInt(0, max_monsters_per_room); - int number_of_items = random->getInt(0, max_items_per_room); - - for (int i = 0; i < number_of_monsters; i++) { - Vector2D bottom_left; - Vector2D top_right; - std::tie(bottom_left, top_right) = room.innerBounds(); - int x = random->getInt(bottom_left.x + 1, top_right.x - 1); - int y = random->getInt(bottom_left.y + 1, top_right.y - 1); - - if (auto iterator = util::find_entity_at(entities_, x, y); - iterator != entities_.end()) { - continue; - } + void EntityManager::place_entities( + RectangularRoom room, int max_monsters_per_room, int max_items_per_room) { + auto* random = TCODRandom::getInstance(); + int number_of_monsters = random->getInt(0, max_monsters_per_room); + int number_of_items = random->getInt(0, max_items_per_room); + + for (int i = 0; i < number_of_monsters; i++) { + SupaRL::Vector2D bottom_left; + SupaRL::Vector2D top_right; + std::tie(bottom_left, top_right) = room.innerBounds(); + int x = random->getInt(bottom_left.x + 1, top_right.x - 1); + int y = random->getInt(bottom_left.y + 1, top_right.y - 1); + + if (auto iterator = util::find_entity_at(entities_, x, y); + iterator != entities_.end()) { + continue; + } - if (random->getFloat(0.0f, 1.0f) < 0.8f) { - Entity* entity = orc_factory_->create(); - entity->get_transform_component().move({x, y}); - spawn(std::move(entity)); - } else { - Entity* entity = troll_factory_->create(); - spawn(std::move(entity), {x, y}); + if (random->getFloat(0.0f, 1.0f) < 0.8f) { + Entity* entity = orc_factory_->create(); + entity->get_transform_component().position_ = {x, y}; + spawn(std::move(entity)); + } else { + Entity* entity = troll_factory_->create(); + spawn(std::move(entity), {x, y}); + } } - } - for (int i = 0; i < number_of_items; i++) { - Vector2D bottom_left; - Vector2D top_right; - std::tie(bottom_left, top_right) = room.innerBounds(); - int x = random->getInt(bottom_left.x + 1, top_right.x - 1); - int y = random->getInt(bottom_left.y + 1, top_right.y - 1); + for (int i = 0; i < number_of_items; i++) { + SupaRL::Vector2D bottom_left; + SupaRL::Vector2D top_right; + std::tie(bottom_left, top_right) = room.innerBounds(); + int x = random->getInt(bottom_left.x + 1, top_right.x - 1); + int y = random->getInt(bottom_left.y + 1, top_right.y - 1); - if (auto iterator = util::find_entity_at(entities_, x, y); - iterator != entities_.end()) { - continue; - } + if (auto iterator = util::find_entity_at(entities_, x, y); + iterator != entities_.end()) { + continue; + } - float dice = random->getFloat(.0f, 1.0f); - if (dice <= 0.7f) { - auto health_potion_factory = std::make_unique(); - Entity* entity = health_potion_factory->create(); - spawn(std::move(entity), {x, y}); - } else if (dice <= .8f) { - auto lighting_scroll_factory = std::make_unique(); - Entity* entity = lighting_scroll_factory->create(); - spawn(std::move(entity), {x, y}); - } else if (dice <= .9f) { - auto fireball_scroll_factory = std::make_unique(); - Entity* entity = fireball_scroll_factory->create(); - spawn(std::move(entity), {x, y}); - } else if (dice <= 1.0f) { - auto confusion_scroll_factory = + float dice = random->getFloat(.0f, 1.0f); + if (dice <= 0.7f) { + auto health_potion_factory = std::make_unique(); + Entity* entity = health_potion_factory->create(); + spawn(std::move(entity), {x, y}); + } else if (dice <= .8f) { + auto lighting_scroll_factory = std::make_unique(); + Entity* entity = lighting_scroll_factory->create(); + spawn(std::move(entity), {x, y}); + } else if (dice <= .9f) { + auto fireball_scroll_factory = std::make_unique(); + Entity* entity = fireball_scroll_factory->create(); + spawn(std::move(entity), {x, y}); + } else if (dice <= 1.0f) { + auto confusion_scroll_factory = std::make_unique(); - Entity* entity = confusion_scroll_factory->create(); - spawn(std::move(entity), {x, y}); + Entity* entity = confusion_scroll_factory->create(); + spawn(std::move(entity), {x, y}); + } } } -} - -Entity* EntityManager::spawn(Entity* src) { - return entities_.emplace_back(src); -} - -Entity* EntityManager::spawn(Entity* src, Vector2D position) { - Entity* entity = spawn(src); - if (position != entity->get_transform_component().get_position()) { - entity->get_transform_component().move(position); + Entity* EntityManager::spawn(Entity* src) { + return entities_.emplace_back(src); } - return entity; -} + Entity* EntityManager::spawn(Entity* src, SupaRL::Vector2D position) { + Entity* entity = spawn(src); -std::vector> EntityManager::get_entities_at( - Vector2D position) { - std::vector> entities_at_position; - // At max there can be 3? things at a position. - // Corpse, Item, Actor... - entities_at_position.reserve(3); - for (auto& entity : entities_) { - if (entity->get_transform_component().get_position() == position) { - entities_at_position.push_back(*entity); + if (position != entity->get_transform_component().position_) { + entity->get_transform_component().position_ = position; } - } - entities_at_position.shrink_to_fit(); - return entities_at_position; -} -std::optional> -EntityManager::get_blocking_entity_at(Vector2D position) { - for (const auto& entity : entities_) { - if (entity->is_blocking() && - entity->get_transform_component().get_position() == position) { - return std::reference_wrapper(*entity); - } + return entity; } - return std::nullopt; -} -std::optional> -EntityManager::get_non_blocking_entity_at(Vector2D position) { - for (const auto& entity : entities_) { - if (!entity->is_blocking() && - entity->get_transform_component().get_position() == position) { - return std::reference_wrapper(*entity); + std::vector> EntityManager::get_entities_at( + SupaRL::Vector2D position) { + std::vector> entities_at_position; + // At max there can be 3? things at a position. + // Corpse, Item, Actor... + entities_at_position.reserve(3); + for (auto& entity : entities_) { + if (entity->get_transform_component().position_ == position) { + entities_at_position.push_back(*entity); + } } + entities_at_position.shrink_to_fit(); + return entities_at_position; } - return std::nullopt; -} -void EntityManager::remove(const Entity* entity) { - const auto condition = [&entity](const Entity* e) { return e == entity; }; - entities_.erase( - std::begin(std::ranges::remove_if(entities_, condition)), - std::end(entities_)); -} + std::optional> + EntityManager::get_blocking_entity_at(SupaRL::Vector2D position) { + for (const auto& entity : entities_) { + if (entity->is_blocking() && + entity->get_transform_component().position_ == position) { + return std::reference_wrapper(*entity); + } + } + return std::nullopt; + } -std::optional> -EntityManager::get_closest_living_monster( - Vector2D position, float range) const { - std::optional> closest = std::nullopt; - float best_distance = 1E6f; - for (const auto& entity : entities_) { - const auto* defense_component = &entity->get_defense_component(); - const std::optional> ai_component = - entity->get_ai_component(); - - if (ai_component.has_value() && defense_component) { - float distance = position.distance_to( - entity->get_transform_component().get_position()); - if (distance < best_distance && (distance <= range || range == 0.0f)) { - best_distance = distance; - closest = *entity; + std::optional> + EntityManager::get_non_blocking_entity_at(SupaRL::Vector2D position) { + for (const auto& entity : entities_) { + if (!entity->is_blocking() && + entity->get_transform_component().position_ == position) { + return std::reference_wrapper(*entity); + } } + return std::nullopt; } + + void EntityManager::remove(const Entity* entity) { + const auto condition = [&entity](const Entity* e) { return e == entity; }; + entities_.erase( + std::begin(std::ranges::remove_if(entities_, condition)), + std::end(entities_)); } - return closest; + + std::optional> + EntityManager::get_closest_living_monster( + SupaRL::Vector2D position, float range) const { + std::optional> closest = std::nullopt; + float best_distance = 1E6f; + for (const auto& entity : entities_) { + const auto* defense_component = &entity->get_defense_component(); + const std::optional> ai_component = + entity->get_ai_component(); + + if (ai_component.has_value() && defense_component) { + float distance = position.distance_to( + entity->get_transform_component().position_); + if (distance < best_distance && (distance <= range || range == 0.0f)) { + best_distance = distance; + closest = *entity; + } + } + } + return closest; + } } -} // namespace cpprl diff --git a/app/src/events/command.cpp b/app/src/events/command.cpp index 1dc4da6..15bba0e 100644 --- a/app/src/events/command.cpp +++ b/app/src/events/command.cpp @@ -18,7 +18,7 @@ namespace cpprl { StateResult PickupCommand::execute() { std::optional> optional_item_ref = world_.get_entities().get_non_blocking_entity_at( - entity_->get_transform_component().get_position()); + entity_->get_transform_component().position_); if(!optional_item_ref.has_value()) { return NoOp{"There is nothing here to pick up."}; } @@ -139,7 +139,7 @@ namespace cpprl { StateResult DirectionalCommand::execute() { auto targetPos = - entity_->get_transform_component().get_position() + move_vector_; + entity_->get_transform_component().position_ + move_vector_; if (world_.get_entities().get_blocking_entity_at(targetPos)) { auto action = MeleeCommand(world_, entity_, move_vector_); @@ -165,7 +165,7 @@ namespace cpprl { StateResult MeleeCommand::execute() { auto targetPos = - entity_->get_transform_component().get_position() + move_vector_; + entity_->get_transform_component().position_ + move_vector_; std::optional> target = world_.get_entities().get_blocking_entity_at(targetPos); @@ -202,8 +202,8 @@ namespace cpprl { }; StateResult MovementCommand::execute() { - Vector2D new_position = - entity_->get_transform_component().get_position() + move_vector_; + SupaRL::Vector2D new_position = + entity_->get_transform_component().position_ + move_vector_; auto& map = world_.get_map(); if (map.is_not_in_bounds(new_position)) { @@ -216,7 +216,7 @@ namespace cpprl { if (map.is_walkable(new_position)) { - entity_->get_transform_component().move(new_position); + entity_->get_transform_component().position_ = new_position; g_coordinator.get_component( entity_->get_id()).velocity_ = {move_vector_.x, move_vector_.y}; diff --git a/app/src/game_entity.cpp b/app/src/game_entity.cpp index c0c8976..9431a9c 100644 --- a/app/src/game_entity.cpp +++ b/app/src/game_entity.cpp @@ -20,12 +20,6 @@ namespace cpprl { void Entity::update(World& world) { aiComponent_->update(world, this); } - // TODO: not sure this belongs here - float Entity::get_distance_to(Entity* other) const { - return transformComponent_->get_position().distance_to( - other->get_transform_component().get_position()); - }; - void Entity::set_ascii_component( std::unique_ptr asciiComponent) { asciiComponent_ = std::move(asciiComponent); diff --git a/app/src/input_handler.cpp b/app/src/input_handler.cpp index e20c6ba..cf70f7f 100644 --- a/app/src/input_handler.cpp +++ b/app/src/input_handler.cpp @@ -1,6 +1,7 @@ #include "input_handler.hpp" #include +#include #include "engine.hpp" #include "events/command.hpp" @@ -21,16 +22,16 @@ EngineEvent& TargetingInputHandler::handle_sdl_event(SDL_Event event) noexcept { if (event.type == SDL_MOUSEMOTION) { g_context.convert_event_coordinates(event); mouse_input_event_ = std::make_unique( - world_, Vector2D{event.motion.x, event.motion.y}); + world_, SupaRL::Vector2D{event.motion.x, event.motion.y}); return *mouse_input_event_; } else if (event.type == SDL_MOUSEBUTTONDOWN) { g_context.convert_event_coordinates(event); mouse_click_event_ = std::make_unique( - world_, Vector2D{event.motion.x, event.motion.y}); + world_, SupaRL::Vector2D{event.motion.x, event.motion.y}); return *mouse_click_event_; } else if (event.type == SDL_KEYDOWN) { mouse_click_event_ = std::make_unique( - world_, Vector2D{event.motion.x, event.motion.y}); + world_, SupaRL::Vector2D{event.motion.x, event.motion.y}); SDL_Keycode key = event.key.keysym.sym; @@ -52,30 +53,30 @@ EngineEvent& TargetingInputHandler::handle_sdl_event(SDL_Event event) noexcept { GameInputHandler::GameInputHandler(World& world, Entity* controllable_entity) : EventHandler(world), button_right_(std::make_unique( - world_, controllable_entity, Vector2D{1, 0})), + world_, controllable_entity, SupaRL::Vector2D{1, 0})), button_up_(std::make_unique( - world_, controllable_entity, Vector2D{0, -1})), + world_, controllable_entity, SupaRL::Vector2D{0, -1})), button_down_(std::make_unique( - world_, controllable_entity, Vector2D{0, 1})), + world_, controllable_entity, SupaRL::Vector2D{0, 1})), button_up_right_(std::make_unique( - world_, controllable_entity, Vector2D{1, -1})), + world_, controllable_entity, SupaRL::Vector2D{1, -1})), button_up_left_(std::make_unique( - world_, controllable_entity, Vector2D{-1, -1})), + world_, controllable_entity, SupaRL::Vector2D{-1, -1})), button_left_(std::make_unique( - world_, controllable_entity, Vector2D{-1, 0})), + world_, controllable_entity, SupaRL::Vector2D{-1, 0})), button_down_right_(std::make_unique( - world_, controllable_entity, Vector2D{1, 1})), + world_, controllable_entity, SupaRL::Vector2D{1, 1})), button_down_left_(std::make_unique( - world_, controllable_entity, Vector2D{-1, 1})), + world_, controllable_entity, SupaRL::Vector2D{-1, 1})), view_history_command_(std::make_unique(world_)), pick_up_command_( std::make_unique(world, controllable_entity)), inventory_command_( std::make_unique(world, controllable_entity)), main_menu_command_(std::make_unique(world)), + use_command_(nullptr), character_menu_command_( std::make_unique(world, controllable_entity)), - use_command_(nullptr), controllable_entity_(controllable_entity){}; EngineEvent& GameInputHandler::handle_sdl_event(SDL_Event event) noexcept { @@ -118,7 +119,7 @@ EngineEvent& GameInputHandler::handle_sdl_event(SDL_Event event) noexcept { return *character_menu_command_; } else if (key == SDLK_LEFTBRACKET) { use_command_ = std::make_unique( - world_, controllable_entity_->get_transform_component().get_position()); + world_, controllable_entity_->get_transform_component().position_); return *use_command_; } else { return EventHandler::handle_sdl_event(event); diff --git a/app/src/rectangular_room.cpp b/app/src/rectangular_room.cpp index ffe1882..9fc72fa 100644 --- a/app/src/rectangular_room.cpp +++ b/app/src/rectangular_room.cpp @@ -1,17 +1,17 @@ -#include "../include/rectangular_room.hpp" +#include "rectangular_room.hpp" namespace cpprl { -std::tuple RectangularRoom::innerBounds() const { - Vector2D innerBottomLeft, innerTopRight; - innerBottomLeft = {bottomLeft_.x + 1, bottomLeft_.y + 1}; - innerTopRight = {topRight_.x - 1, topRight_.y - 1}; - return std::tuple(innerBottomLeft, innerTopRight); -} + std::tuple RectangularRoom::innerBounds() const { + SupaRL::Vector2D innerBottomLeft, innerTopRight; + innerBottomLeft = {bottomLeft_.x + 1, bottomLeft_.y + 1}; + innerTopRight = {topRight_.x - 1, topRight_.y - 1}; + return std::tuple(innerBottomLeft, innerTopRight); + } -bool RectangularRoom::intersects(RectangularRoom other) const { - return ( - bottomLeft_.x <= other.topRight_.x && topRight_.x >= other.bottomLeft_.x && bottomLeft_.y <= other.topRight_.y && - topRight_.y >= other.bottomLeft_.y); + bool RectangularRoom::intersects(RectangularRoom other) const { + return ( + bottomLeft_.x <= other.topRight_.x && topRight_.x >= other.bottomLeft_.x && bottomLeft_.y <= other.topRight_.y && + topRight_.y >= other.bottomLeft_.y); + } } -} // namespace cpprl diff --git a/app/src/state.cpp b/app/src/state.cpp index c77b0e3..ab6cc8a 100644 --- a/app/src/state.cpp +++ b/app/src/state.cpp @@ -8,106 +8,106 @@ namespace cpprl { -StateResult State::on_update(SDL_Event& event) { - try { - EngineEvent& command = input_handler_->handle_sdl_event(event); - return command.execute(); - } catch (const Impossible& impossible) { - world_.get_message_log().add_message(impossible.what(), RED); - return {}; + StateResult State::on_update(SDL_Event& event) { + try { + EngineEvent& command = input_handler_->handle_sdl_event(event); + return command.execute(); + } catch (const Impossible& impossible) { + world_.get_message_log().add_message(impossible.what(), RED); + return {}; + } } -} -void InGameState::on_enter() { - input_handler_ = + void InGameState::on_enter() { + input_handler_ = std::make_unique(world_, world_.get_player()); -} + } -void GuiViewState::render(Renderer&) { window_->render(g_console); } + void GuiViewState::render(Renderer&) { window_->render(g_console); } -void ViewMessageHistoryState::on_enter() { - input_handler_ = std::make_unique(world_, *window_); -} + void ViewMessageHistoryState::on_enter() { + input_handler_ = std::make_unique(world_, *window_); + } -void ViewInventoryState::on_enter() { - input_handler_ = std::make_unique( - world_, world_.get_player(), *window_); -} + void ViewInventoryState::on_enter() { + input_handler_ = std::make_unique( + world_, world_.get_player(), *window_); + } -void NextLevelState::on_enter() { - // gen a new dungeon? - world_.get_message_log().add_message( - "You take a moment to rest to recover your strength.", WHITE); - world_.get_player()->get_defense_component().heal( - world_.get_player()->get_defense_component().get_max_hp() / 2); - world_.get_message_log().add_message( - "After a rare moment of peace, you descend deeper into the heart of the " - "dungeon...", - WHITE); - - world_.get_entities().clear(); - // TODO: Should this go in on exit? - world_.get_dungeon().increase_level(); - world_.generate_map(80, 35, true); - // Add the player back to the entities. - world_.get_entities().spawn( - world_.get_player(), world_.get_map().get_rooms().at(0).get_center()); -} + void NextLevelState::on_enter() { + // gen a new dungeon? + world_.get_message_log().add_message( + "You take a moment to rest to recover your strength.", WHITE); + world_.get_player()->get_defense_component().heal( + world_.get_player()->get_defense_component().get_max_hp() / 2); + world_.get_message_log().add_message( + "After a rare moment of peace, you descend deeper into the heart of the " + "dungeon...", + WHITE); + + world_.get_entities().clear(); + // TODO: Should this go in on exit? + world_.get_dungeon().increase_level(); + world_.generate_map(80, 35, true); + // Add the player back to the entities. + world_.get_entities().spawn( + world_.get_player(), world_.get_map().get_rooms().at(0).get_center()); + } -StateResult NextLevelState::on_update(SDL_Event&) { - return Change{std::make_unique(world_)}; -} + StateResult NextLevelState::on_update(SDL_Event&) { + return Change{std::make_unique(world_)}; + } -void PickTileState::on_enter() { - input_handler_ = std::make_unique(world_); -} + void PickTileState::on_enter() { + input_handler_ = std::make_unique(world_); + } -StateResult PickTileState::on_update(SDL_Event& event) { - try { - EngineEvent& command = input_handler_->handle_sdl_event(event); - StateResult result = command.execute(); - return result; - } catch (const Impossible& impossible) { - world_.get_message_log().add_message(impossible.what(), RED); - return {}; + StateResult PickTileState::on_update(SDL_Event& event) { + try { + EngineEvent& command = input_handler_->handle_sdl_event(event); + StateResult result = command.execute(); + return result; + } catch (const Impossible& impossible) { + world_.get_message_log().add_message(impossible.what(), RED); + return {}; + } } -} -void PickTileState::on_exit() { on_pick_(); } + void PickTileState::on_exit() { on_pick_(); } -void PickTileState::render(Renderer&) { - auto& map = world_.get_map(); - Vector2D position = map.get_highlight_tile(); - auto& tile = g_console.at(position); - tile = {tile.ch, tcod::ColorRGB{0, 0, 0}, tcod::ColorRGB{255, 255, 255}}; -} + void PickTileState::render(Renderer&) { + auto& map = world_.get_map(); + SupaRL::Vector2D position = map.get_highlight_tile(); + auto& tile = g_console.at(position); + tile = {tile.ch, tcod::ColorRGB{0, 0, 0}, tcod::ColorRGB{255, 255, 255}}; + } -void PickTileAOEState::render(Renderer&) { - auto& map = world_.get_map(); - if (map.get_highlight_tile() != Vector2D{0, 0}) { - with_indexes(map, [&, pos = map.get_highlight_tile()](int x, int y) { - if (euclidean_squared(Vector2D{x, y} - pos) >= aoe_squared_) return; - if (!g_console.in_bounds({x, y})) return; - auto& tile = g_console.at({x, y}); - tile = {tile.ch, tcod::ColorRGB{0, 0, 0}, tcod::ColorRGB{255, 255, 255}}; - }); + void PickTileAOEState::render(Renderer&) { + auto& map = world_.get_map(); + if (map.get_highlight_tile() != SupaRL::Vector2D{0, 0}) { + with_indexes(map, [&, pos = map.get_highlight_tile()](int x, int y) { + if (euclidean_squared(SupaRL::Vector2D{x, y} - pos) >= aoe_squared_) return; + if (!g_console.in_bounds({x, y})) return; + auto& tile = g_console.at({x, y}); + tile = {tile.ch, tcod::ColorRGB{0, 0, 0}, tcod::ColorRGB{255, 255, 255}}; + }); + } } -} -void GameOverState::on_enter() { - input_handler_ = std::make_unique(world_); - window_ = new GameOverWindow(20, 10, {0, 0}, "Game Over"); -} + void GameOverState::on_enter() { + input_handler_ = std::make_unique(world_); + window_ = new GameOverWindow(20, 10, {0, 0}, "Game Over"); + } -void MainMenuState::on_enter() { - window_ = new MainMenuWindow(20, 10, {0, 0}, "Main Menu"); - input_handler_ = std::make_unique(world_, *window_); -} + void MainMenuState::on_enter() { + window_ = new MainMenuWindow(20, 10, {0, 0}, "Main Menu"); + input_handler_ = std::make_unique(world_, *window_); + } -void CharacterMenuState::on_enter() { - window_ = new CharacterMenuWindow( - 20, 10, {0, 0}, world_.get_player(), "Character Menu"); - input_handler_ = + void CharacterMenuState::on_enter() { + window_ = new CharacterMenuWindow( + 20, 10, {0, 0}, world_.get_player(), "Character Menu"); + input_handler_ = std::make_unique(world_, *window_); + } } -} // namespace cpprl diff --git a/app/src/types/map.cpp b/app/src/types/map.cpp index 98cd8f7..8d09a7f 100644 --- a/app/src/types/map.cpp +++ b/app/src/types/map.cpp @@ -2,6 +2,7 @@ #include "colours.hpp" #include "components.hpp" +#include namespace cpprl { Map::Map(int width, int height) @@ -17,13 +18,13 @@ Map::Map(int width, int height) downstairs_tile_.dark = TCOD_ConsoleTile{'<', GREY, BLACK}; } -bool Map::is_in_bounds(Vector2D position) const { +bool Map::is_in_bounds(SupaRL::Vector2D position) const { return position.x >= 0 && position.x < width_ && position.y >= 0 && position.y < height_; } -void Map::set_tiles_range(std::tuple bounds, Tile tile) { - Vector2D bottom_left, top_right; +void Map::set_tiles_range(std::tuple bounds, Tile tile) { + SupaRL::Vector2D bottom_left, top_right; std::tie(bottom_left, top_right) = bounds; for (int x = bottom_left.x; x < top_right.x; x++) { for (int y = bottom_left.y; y < top_right.y; y++) { @@ -33,26 +34,26 @@ void Map::set_tiles_range(std::tuple bounds, Tile tile) { } } } -void Map::set_tiles_at(Vector2D position, Tile tile) { +void Map::set_tiles_at(SupaRL::Vector2D position, Tile tile) { tcod_map_.setProperties( position.x, position.y, tile.is_transparent, !tile.blocking); tiles_.set(position, tile); } -bool Map::is_in_fov(Vector2D position) { +bool Map::is_in_fov(SupaRL::Vector2D position) { return tcod_map_.isInFov(position.x, position.y); } -bool Map::is_walkable(Vector2D position) const { +bool Map::is_walkable(SupaRL::Vector2D position) const { return tcod_map_.isWalkable(position.x, position.y); } -void Map::compute_fov(Vector2D position, int radius) { +void Map::compute_fov(SupaRL::Vector2D position, int radius) { tcod_map_.computeFov( position.x, position.y, radius, true, FOV_SYMMETRIC_SHADOWCAST); } -bool Map::is_explored(Vector2D position) { +bool Map::is_explored(SupaRL::Vector2D position) { return tiles_.at(position).explored; } @@ -91,6 +92,6 @@ void Map::render(tcod::Console& console) { } } -void Map::set_highlight_tile(Vector2D position) { target_tile_ = position; } +void Map::set_highlight_tile(SupaRL::Vector2D position) { target_tile_ = position; } } // namespace cpprl diff --git a/app/src/ui/dungeon_level.cpp b/app/src/ui/dungeon_level.cpp index 835f417..ed42868 100644 --- a/app/src/ui/dungeon_level.cpp +++ b/app/src/ui/dungeon_level.cpp @@ -8,13 +8,13 @@ #include "colours.hpp" namespace cpprl { -void DungeonLevel::render(tcod::Console& console) { - tcod::print_rect( - console, - {position_.x, position_.y, width_, height_}, - fmt::format("Level: {}", dungeon_.get_level()), - WHITE, - std::nullopt, - TCOD_CENTER); + void DungeonLevel::render(tcod::Console& console) { + tcod::print_rect( + console, + {position_.x, position_.y, width_, height_}, + fmt::format("Level: {}", dungeon_.get_level()), + WHITE, + std::nullopt, + TCOD_CENTER); + } } -} // namespace cpprl diff --git a/app/src/ui/health_bar.cpp b/app/src/ui/health_bar.cpp index ed20703..2c61531 100644 --- a/app/src/ui/health_bar.cpp +++ b/app/src/ui/health_bar.cpp @@ -9,32 +9,32 @@ namespace cpprl { -HealthBar::HealthBar( - int width, int height, Vector2D position, DefenseComponent& defense) + HealthBar::HealthBar( + int width, int height, SupaRL::Vector2D position, DefenseComponent& defense) : UiWindow(width, height, position), health_(defense) {} -void HealthBar::render(tcod::Console& console) { - const auto bar_width = (int)((float)health_.get_hp() / - (float)health_.get_max_hp() * (float)width_); - tcod::draw_rect( - console, - {position_.x, position_.y, width_, height_}, - 0, - std::nullopt, - DARK_RED); - tcod::draw_rect( - console, - {position_.x, position_.y, bar_width, height_}, - 0, - std::nullopt, - DARK_GREEN); + void HealthBar::render(tcod::Console& console) { + const auto bar_width = (int)((float)health_.get_hp() / + (float)health_.get_max_hp() * (float)width_); + tcod::draw_rect( + console, + {position_.x, position_.y, width_, height_}, + 0, + std::nullopt, + DARK_RED); + tcod::draw_rect( + console, + {position_.x, position_.y, bar_width, height_}, + 0, + std::nullopt, + DARK_GREEN); - tcod::print_rect( - console, - {position_.x, position_.y, width_, height_}, - fmt::format("HP: {}/{}", health_.get_hp(), health_.get_max_hp()), - WHITE, - std::nullopt, - TCOD_CENTER); + tcod::print_rect( + console, + {position_.x, position_.y, width_, height_}, + fmt::format("HP: {}/{}", health_.get_hp(), health_.get_max_hp()), + WHITE, + std::nullopt, + TCOD_CENTER); + } } -} // namespace cpprl diff --git a/app/src/ui/message_log.cpp b/app/src/ui/message_log.cpp index f88f6e2..9fcea1d 100644 --- a/app/src/ui/message_log.cpp +++ b/app/src/ui/message_log.cpp @@ -4,56 +4,56 @@ namespace cpprl { -void MessageLog::add_message(Message message, bool stack) { - if (stack && !messages_.empty()) { - Message& last_message = messages_.back(); - if (last_message.text_ == message.text_) { - last_message.count_ += message.count_; - return; + void MessageLog::add_message(Message message, bool stack) { + if (stack && !messages_.empty()) { + Message& last_message = messages_.back(); + if (last_message.text_ == message.text_) { + last_message.count_ += message.count_; + return; + } } - } - messages_.push_back(message); - if (messages_.size() > max_messages_) { - messages_.erase(messages_.begin()); + messages_.push_back(message); + if (messages_.size() > max_messages_) { + messages_.erase(messages_.begin()); + } } -} - -void MessageLog::add_message( - std::string text, tcod::ColorRGB color, bool stack) { - add_message(Message{text, color, 1}, stack); -} - -void MessageLog::render( - tcod::Console& console, int x, int y, int width, int height) const { - render_messages(console, x, y, width, height); -} -// TODO: Bug where it's not overlaying correctly -void MessageLog::render_messages( - tcod::Console& console, int x, int y, int width, int height) const { - int y_offset = 0; - for (auto it = messages_.rbegin(); it != messages_.rend(); ++it) { - const Message& message = *it; - int line_height = tcod::get_height_rect(width, it->full_text()); + void MessageLog::add_message( + std::string text, tcod::ColorRGB color, bool stack) { + add_message(Message{text, color, 1}, stack); + } - if (line_height > 1) { - y_offset += line_height - 1; - } + void MessageLog::render( + tcod::Console& console, int x, int y, int width, int height) const { + render_messages(console, x, y, width, height); + } - if (y_offset >= height) { - break; + // TODO: Bug where it's not overlaying correctly + void MessageLog::render_messages( + tcod::Console& console, int x, int y, int width, int height) const { + int y_offset = 0; + for (auto it = messages_.rbegin(); it != messages_.rend(); ++it) { + const Message& message = *it; + int line_height = tcod::get_height_rect(width, it->full_text()); + + if (line_height > 1) { + y_offset += line_height - 1; + } + + if (y_offset >= height) { + break; + } + std::string text = message.full_text(); + tcod::print_rect( + console, + {x, y + y_offset, width, line_height}, + message.full_text(), + message.colour_, + BLACK_DARK, + TCOD_LEFT); + y_offset++; } - std::string text = message.full_text(); - tcod::print_rect( - console, - {x, y + y_offset, width, line_height}, - message.full_text(), - message.colour_, - BLACK_DARK, - TCOD_LEFT); - y_offset++; } -} -} // namespace cpprl +} diff --git a/app/src/ui/ui.cpp b/app/src/ui/ui.cpp index 1ec9464..7fc7b05 100644 --- a/app/src/ui/ui.cpp +++ b/app/src/ui/ui.cpp @@ -1,25 +1,26 @@ #include "ui/ui.hpp" +#include #include namespace cpprl { -UI::UI(Dungeon& dungeon) { - dungeon_level_ = - std::make_unique(20, 1, Vector2D{2, 35}, dungeon); -} + UI::UI(Dungeon& dungeon) { + dungeon_level_ = + std::make_unique(20, 1, SupaRL::Vector2D{2, 35}, dungeon); + } -void UI::set_health_bar(DefenseComponent& defense_component) { - health_bar_ = - std::make_unique(20, 1, Vector2D{2, 36}, defense_component); -} + void UI::set_health_bar(DefenseComponent& defense_component) { + health_bar_ = + std::make_unique(20, 1, SupaRL::Vector2D{2, 36}, defense_component); + } -void UI::set_xp_bar(StatsComponent& stats_component) { - xp_bar_ = std::make_unique(20, 1, Vector2D{2, 37}, stats_component); -} + void UI::set_xp_bar(StatsComponent& stats_component) { + xp_bar_ = std::make_unique(20, 1, SupaRL::Vector2D{2, 37}, stats_component); + } -void UI::render(tcod::Console& console) { - health_bar_->render(console); - dungeon_level_->render(console); - xp_bar_->render(console); + void UI::render(tcod::Console& console) { + health_bar_->render(console); + dungeon_level_->render(console); + xp_bar_->render(console); + } } -} // namespace cpprl diff --git a/app/src/ui/xp_bar.cpp b/app/src/ui/xp_bar.cpp index dc2ce1c..2b80b45 100644 --- a/app/src/ui/xp_bar.cpp +++ b/app/src/ui/xp_bar.cpp @@ -6,35 +6,36 @@ #include #include "colours.hpp" +#include namespace cpprl { -XPBar::XPBar(int width, int height, Vector2D position, StatsComponent& stats) + XPBar::XPBar(int width, int height, SupaRL::Vector2D position, StatsComponent& stats) : UiWindow(width, height, position), stats_(stats) {} -void XPBar::render(tcod::Console& console) { - const auto bar_width = + void XPBar::render(tcod::Console& console) { + const auto bar_width = (int)((float)stats_.get_xp() / (float)stats_.get_next_level_xp() * - (float)width_); - tcod::draw_rect( - console, - {position_.x, position_.y, width_, height_}, - 0, - std::nullopt, - DARK_GREY); - tcod::draw_rect( - console, - {position_.x, position_.y, bar_width, height_}, - 0, - std::nullopt, - LIGHT_BLUE); + (float)width_); + tcod::draw_rect( + console, + {position_.x, position_.y, width_, height_}, + 0, + std::nullopt, + DARK_GREY); + tcod::draw_rect( + console, + {position_.x, position_.y, bar_width, height_}, + 0, + std::nullopt, + LIGHT_BLUE); - tcod::print_rect( - console, - {position_.x, position_.y, width_, height_}, - fmt::format("XP: {}/{}", stats_.get_xp(), stats_.get_next_level_xp()), - WHITE, - std::nullopt, - TCOD_CENTER); + tcod::print_rect( + console, + {position_.x, position_.y, width_, height_}, + fmt::format("XP: {}/{}", stats_.get_xp(), stats_.get_next_level_xp()), + WHITE, + std::nullopt, + TCOD_CENTER); + } } -} // namespace cpprl diff --git a/app/src/world.cpp b/app/src/world.cpp index 8472c79..4ce1919 100644 --- a/app/src/world.cpp +++ b/app/src/world.cpp @@ -49,12 +49,12 @@ namespace cpprl { void World::render(Renderer& renderer) { dungeon_.get_map().compute_fov( - player_->get_transform_component().get_position(), 10); + player_->get_transform_component().position_, 10); dungeon_.get_map().render(g_console); for (const auto& entity : *entities_) { if (dungeon_.get_map().is_in_fov( - entity->get_transform_component().get_position())) { + entity->get_transform_component().position_)) { renderer.render( entity->get_sprite_component(), entity->get_transform_component()); } @@ -103,15 +103,15 @@ namespace cpprl { .x = dungeon_.get_map().get_rooms().at(0).get_center().x, .y = dungeon_.get_map().get_rooms().at(0).get_center().y }; - player->get_transform_component().move( - dungeon_.get_map().get_rooms().at(0).get_center()); + player->get_transform_component().position_ = + dungeon_.get_map().get_rooms().at(0).get_center(); World::spawn_player(player); } void World::spawn_player(Entity* player) { player_ = entities_->spawn(player); dungeon_.get_map().compute_fov( - player_->get_transform_component().get_position(), 4); + player_->get_transform_component().position_, 4); DefenseComponent& player_defense = player_->get_defense_component(); ui_->set_health_bar(player_defense); ui_->set_xp_bar(player_->get_stats_component().value().get()); From 0ef5105cc0a601c1e4000b3f3faf014e94be49fe Mon Sep 17 00:00:00 2001 From: daniel baker Date: Thu, 10 Oct 2024 10:27:42 +0100 Subject: [PATCH 06/38] feat: clean up remove usage of iostream in most places switch to pragma once remove useless comments --- app/include/exceptions.hpp | 23 ++--- app/include/message_log.hpp | 133 ++++++++++++------------- app/include/tile.hpp | 17 ++-- app/src/events/command.cpp | 2 - lib/include/core/component_manager.hpp | 1 - lib/include/core/coordinator.hpp | 2 - lib/include/core/entity_manager.hpp | 2 - lib/include/core/system_manager.hpp | 1 - lib/include/utils/logger.hpp | 1 - lib/src/systems/combat_system.cpp | 3 - lib/src/systems/physics_system.cpp | 2 - 11 files changed, 82 insertions(+), 105 deletions(-) diff --git a/app/include/exceptions.hpp b/app/include/exceptions.hpp index a02329b..e1e5244 100644 --- a/app/include/exceptions.hpp +++ b/app/include/exceptions.hpp @@ -1,17 +1,14 @@ -#ifndef EXCEPTIONS_H -#define EXCEPTIONS_H +#pragma once -#include -#include +#include namespace cpprl { -class Impossible : public std::exception { - private: - std::string message_; + class Impossible : public std::exception { + private: + std::string message_; - public: - Impossible(const char* message) : message_(message) {} - const char* what() const noexcept override { return message_.c_str(); } -}; -} // namespace cpprl -#endif + public: + Impossible(const char* message) : message_(message) {} + const char* what() const noexcept override { return message_.c_str(); } + }; +} diff --git a/app/include/message_log.hpp b/app/include/message_log.hpp index ce8ce61..49d9ae6 100644 --- a/app/include/message_log.hpp +++ b/app/include/message_log.hpp @@ -1,83 +1,80 @@ -#ifndef INCLUDE_MESSAGE_LOG_HPP_ -#define INCLUDE_MESSAGE_LOG_HPP_ +#pragma once #include #include #include #include -#include #include #include "colours.hpp" namespace cpprl { -struct Message { - std::string text_; - tcod::ColorRGB colour_; - int count_; + struct Message { + std::string text_; + tcod::ColorRGB colour_; + int count_; - Message() = default; - Message(std::string text, tcod::ColorRGB color = WHITE, int count = 1) + Message() = default; + Message(std::string text, tcod::ColorRGB color = WHITE, int count = 1) : text_(text), colour_(color), count_(count) {} - std::string full_text() const { - return count_ > 1 ? fmt::format("{} (x{})", text_, count_) : text_; - } - - template - void serialize(Archive& archive) { - archive(text_, colour_, count_); - } -}; - -class MessageLog { - private: - /** - * The stored messages. - */ - std::vector messages_; - - /** - * Max look back of messages we should store. - */ - size_t max_messages_; - - /** - * renders a list of messages in reverse order - */ - void render_messages( - tcod::Console& console, int x, int y, int width, int height) const; - - public: - MessageLog(int max_messages = 1024) : max_messages_(max_messages) { - messages_.reserve(max_messages_); - } - virtual ~MessageLog() = default; - void add_message(Message message, bool stack); - void add_message( - std::string text, tcod::ColorRGB color = WHITE, bool stack = true); - - /** - * Get the messages in this log - */ - std::vector get_messages() const { return messages_; } - - /** - * Renders a log message in the given area. - */ - void render( - tcod::Console& console, int x, int y, int width, int height) const; - - template - void serialize(Archive& archive) { - // TODO: this is where we blow up - // when using binary serialisation (tested on a mac m1) - archive(messages_, max_messages_); - } -}; - -} // namespace cpprl - -#endif + std::string full_text() const { + return count_ > 1 ? fmt::format("{} (x{})", text_, count_) : text_; + } + + template + void serialize(Archive& archive) { + archive(text_, colour_, count_); + } + }; + + class MessageLog { + private: + /** + * The stored messages. + */ + std::vector messages_; + + /** + * Max look back of messages we should store. + */ + size_t max_messages_; + + /** + * renders a list of messages in reverse order + */ + void render_messages( + tcod::Console& console, int x, int y, int width, int height) const; + + public: + MessageLog(int max_messages = 1024) : max_messages_(max_messages) { + messages_.reserve(max_messages_); + } + virtual ~MessageLog() = default; + void add_message(Message message, bool stack); + void add_message( + std::string text, tcod::ColorRGB color = WHITE, bool stack = true); + + /** + * Get the messages in this log + */ + std::vector get_messages() const { return messages_; } + + /** + * Renders a log message in the given area. + */ + void render( + tcod::Console& console, int x, int y, int width, int height) const; + + template + void serialize(Archive& archive) { + // TODO: this is where we blow up + // when using binary serialisation (tested on a mac m1) + archive(messages_, max_messages_); + } + }; + +} + diff --git a/app/include/tile.hpp b/app/include/tile.hpp index da15332..ad356f1 100644 --- a/app/include/tile.hpp +++ b/app/include/tile.hpp @@ -1,16 +1,13 @@ -#ifndef TILE_HPP -#define TILE_HPP +#pragma once -#include #include namespace cpprl { -struct Tile { - std::string_view name; - int character; - tcod::ColorRGB foreground; -} + struct Tile { + std::string_view name; + int character; + tcod::ColorRGB foreground; + }; -} // namespace cpprl -#endif +} diff --git a/app/src/events/command.cpp b/app/src/events/command.cpp index 15bba0e..9e7c583 100644 --- a/app/src/events/command.cpp +++ b/app/src/events/command.cpp @@ -216,8 +216,6 @@ namespace cpprl { if (map.is_walkable(new_position)) { - entity_->get_transform_component().position_ = new_position; - g_coordinator.get_component( entity_->get_id()).velocity_ = {move_vector_.x, move_vector_.y}; diff --git a/lib/include/core/component_manager.hpp b/lib/include/core/component_manager.hpp index 1902aa5..3f264e6 100644 --- a/lib/include/core/component_manager.hpp +++ b/lib/include/core/component_manager.hpp @@ -2,7 +2,6 @@ #include "component_array.hpp" #include "types.hpp" -#include #include #include diff --git a/lib/include/core/coordinator.hpp b/lib/include/core/coordinator.hpp index 0e7802b..202331d 100644 --- a/lib/include/core/coordinator.hpp +++ b/lib/include/core/coordinator.hpp @@ -7,8 +7,6 @@ #include "types.hpp" #include -#include - namespace SupaRL { class Coordinator diff --git a/lib/include/core/entity_manager.hpp b/lib/include/core/entity_manager.hpp index 09b1ffb..82412f6 100644 --- a/lib/include/core/entity_manager.hpp +++ b/lib/include/core/entity_manager.hpp @@ -4,8 +4,6 @@ #include #include #include -#include - namespace SupaRL { class EntityManager diff --git a/lib/include/core/system_manager.hpp b/lib/include/core/system_manager.hpp index 89bcd51..01dbe99 100644 --- a/lib/include/core/system_manager.hpp +++ b/lib/include/core/system_manager.hpp @@ -5,7 +5,6 @@ #include #include #include -#include namespace SupaRL { diff --git a/lib/include/utils/logger.hpp b/lib/include/utils/logger.hpp index d0b0d36..93c4cfa 100644 --- a/lib/include/utils/logger.hpp +++ b/lib/include/utils/logger.hpp @@ -20,6 +20,5 @@ namespace SupaRL std::cout << message << value << std::endl; #endif } - }; } diff --git a/lib/src/systems/combat_system.cpp b/lib/src/systems/combat_system.cpp index de96960..7254208 100644 --- a/lib/src/systems/combat_system.cpp +++ b/lib/src/systems/combat_system.cpp @@ -1,15 +1,12 @@ #include "systems/combat_system.hpp" -#include namespace SupaRL { void CombatSystem::init() { - std::cout << "Combat System Init" << std::endl; } void CombatSystem::update() { - std::cout << "Combat System Update" << std::endl; } } diff --git a/lib/src/systems/physics_system.cpp b/lib/src/systems/physics_system.cpp index c4cfeeb..0c57551 100644 --- a/lib/src/systems/physics_system.cpp +++ b/lib/src/systems/physics_system.cpp @@ -3,7 +3,6 @@ #include "core/coordinator.hpp" #include "components/transform.hpp" #include "components/velocity.hpp" -#include extern SupaRL::Coordinator g_coordinator; @@ -14,7 +13,6 @@ namespace SupaRL { void PhysicsSystem::update() { auto entities_to_remove = std::vector(); for (auto const& entity : entities_) { - std::cout << "Updating physics for entity " << entity << std::endl; auto& transform = g_coordinator.get_component(entity); auto& velocity = g_coordinator.get_component(entity); From 00f4491c21087aa13c39211d3ffc36ccad653460 Mon Sep 17 00:00:00 2001 From: daniel baker Date: Fri, 11 Oct 2024 09:43:30 +0100 Subject: [PATCH 07/38] feat: transform in ecs --- app/include/components.hpp | 2 +- app/include/consumable_factory.hpp | 58 +++++++++++++-------------- app/include/entity_factory.hpp | 62 ++++++++++++++--------------- app/include/entity_manager.hpp | 2 +- app/include/game_entity.hpp | 14 ------- app/include/util.hpp | 5 ++- app/include/world.hpp | 2 +- app/src/basic_ai_component.cpp | 5 ++- app/src/components.cpp | 14 +++++-- app/src/consumable_factory.cpp | 21 +++++----- app/src/engine.cpp | 1 - app/src/entity_factory.cpp | 13 +++--- app/src/entity_manager.cpp | 52 +++++++++++------------- app/src/events/command.cpp | 16 ++++++-- app/src/game_entity.cpp | 2 - app/src/input_handler.cpp | 6 ++- app/src/state.cpp | 6 ++- app/src/world.cpp | 28 ++++++------- lib/include/core/entity_manager.hpp | 3 -- lib/include/core/system_manager.hpp | 6 --- 20 files changed, 152 insertions(+), 166 deletions(-) diff --git a/app/include/components.hpp b/app/include/components.hpp index a044911..3bf8699 100644 --- a/app/include/components.hpp +++ b/app/include/components.hpp @@ -124,7 +124,7 @@ namespace cpprl { int nb_items; archive(nb_items); for (int i = 0; i < nb_items; i++) { - Entity* entity = new Entity("", false, nullptr, nullptr); + Entity* entity = new Entity("", false, nullptr); entity->unpack(archive); inventory_.emplace_back(entity); } diff --git a/app/include/consumable_factory.hpp b/app/include/consumable_factory.hpp index 11c3361..567a0b9 100644 --- a/app/include/consumable_factory.hpp +++ b/app/include/consumable_factory.hpp @@ -1,42 +1,40 @@ -#ifndef CONSUMABLE_FACTORY_HPP -#define CONSUMABLE_FACTORY_HPP +#pragma once #include -#include +#include namespace cpprl { -class Entity; + class Entity; -class AbstractConsumableFactory { - public: - virtual ~AbstractConsumableFactory() = default; + class AbstractConsumableFactory { + public: + virtual ~AbstractConsumableFactory() = default; - virtual Entity* create() = 0; + virtual Entity* create(SupaRL::Vector2D at_position) = 0; - protected: - Entity* create_base( - std::string name, tcod::ColorRGB color, std::string_view symbol); -}; + protected: + Entity* create_base( + std::string name, tcod::ColorRGB color, std::string_view symbol,SupaRL::Vector2D at_position); + }; -class HealthPotionFactory : public AbstractConsumableFactory { - public: - Entity* create() override; -}; + class HealthPotionFactory : public AbstractConsumableFactory { + public: + Entity* create(SupaRL::Vector2D at_position) override; + }; -class FireballScrollFactory : public AbstractConsumableFactory { - public: - Entity* create() override; -}; + class FireballScrollFactory : public AbstractConsumableFactory { + public: + Entity* create(SupaRL::Vector2D at_position) override; + }; -class LightningScrollFactory : public AbstractConsumableFactory { - public: - Entity* create() override; -}; + class LightningScrollFactory : public AbstractConsumableFactory { + public: + Entity* create(SupaRL::Vector2D at_position) override; + }; -class ConfusionScrollFactory : public AbstractConsumableFactory { - public: - Entity* create() override; -}; -} // namespace cpprl + class ConfusionScrollFactory : public AbstractConsumableFactory { + public: + Entity* create(SupaRL::Vector2D at_position) override; + }; +} -#endif diff --git a/app/include/entity_factory.hpp b/app/include/entity_factory.hpp index c309214..80501a5 100644 --- a/app/include/entity_factory.hpp +++ b/app/include/entity_factory.hpp @@ -1,36 +1,34 @@ -#ifndef ENTITY_FACTORY_HPP -#define ENTITY_FACTORY_HPP +#pragma once #include -#include +#include namespace cpprl { -class Entity; -class AbstractEntityFactory { - public: - virtual ~AbstractEntityFactory() = default; - - virtual Entity* create() = 0; - - protected: - Entity* create_base( - const std::string& name, tcod::ColorRGB color, std::string_view symbol); -}; - -class OrcFactory : public AbstractEntityFactory { - public: - Entity* create() override; -}; - -class TrollFactory : public AbstractEntityFactory { - public: - Entity* create() override; -}; - -class PlayerFactory : public AbstractEntityFactory { - public: - Entity* create() override; -}; -} // namespace cpprl - -#endif + class Entity; + class AbstractEntityFactory { + public: + virtual ~AbstractEntityFactory() = default; + + virtual Entity* create(SupaRL::Vector2D at_position) = 0; + + protected: + Entity* create_base( + const std::string& name, tcod::ColorRGB color, std::string_view symbol); + }; + + class OrcFactory : public AbstractEntityFactory { + public: + Entity* create(SupaRL::Vector2D at_position) override; + }; + + class TrollFactory : public AbstractEntityFactory { + public: + Entity* create(SupaRL::Vector2D at_position) override; + }; + + class PlayerFactory : public AbstractEntityFactory { + public: + Entity* create(SupaRL::Vector2D at_position) override; + }; +} + diff --git a/app/include/entity_manager.hpp b/app/include/entity_manager.hpp index 7663475..a39f7ab 100644 --- a/app/include/entity_manager.hpp +++ b/app/include/entity_manager.hpp @@ -75,7 +75,7 @@ namespace cpprl { archive(size); entities_.reserve(size); for (size_t i = 0; i < size; i++) { - auto entity = new Entity("", false, nullptr, nullptr); + auto entity = new Entity("", false, nullptr); entity->unpack(archive); entities_.emplace_back(entity); } diff --git a/app/include/game_entity.hpp b/app/include/game_entity.hpp index 94054ae..0c5f31d 100644 --- a/app/include/game_entity.hpp +++ b/app/include/game_entity.hpp @@ -28,7 +28,6 @@ namespace cpprl { std::string name_; bool blocker_; - std::unique_ptr transformComponent_; std::unique_ptr asciiComponent_; std::unique_ptr attackComponent_; std::unique_ptr defenseComponent_; @@ -41,7 +40,6 @@ namespace cpprl { Entity( std::string const& name, bool blocker, - std::unique_ptr transformComponent, std::unique_ptr asciiComponent); ~Entity() = default; @@ -49,11 +47,6 @@ namespace cpprl { void set_id(SupaRL::Entity id) { id_ = id; }; SupaRL::Entity get_id() { return id_; }; - SupaRL::TransformComponent& get_transform_component() { - auto& transform = g_coordinator.get_component(id_); - return transform; - }; - ASCIIComponent& get_sprite_component() { return *asciiComponent_; }; AttackComponent& get_attack_component() { return *attackComponent_; }; DefenseComponent& get_defense_component() { return *defenseComponent_; }; @@ -99,7 +92,6 @@ namespace cpprl { template void pack(Archive& archive) { archive(name_, blocker_); - archive(transformComponent_ != nullptr); archive(defenseComponent_ != nullptr); archive(attackComponent_ != nullptr); archive(consumableComponent_ != nullptr); @@ -107,7 +99,6 @@ namespace cpprl { archive(aiComponent_ != nullptr); archive(container_ != nullptr); archive(statsComponent_ != nullptr); - if (transformComponent_) archive(transformComponent_); if (asciiComponent_) archive(asciiComponent_); if (attackComponent_) archive(attackComponent_); @@ -120,7 +111,6 @@ namespace cpprl { template void unpack(Archive& archive) { - bool hasTransformComponent; bool hasDefenseComponent; bool hasAttackComponent; bool hasConsumableComponent; @@ -130,7 +120,6 @@ namespace cpprl { bool hasStatsComponent; archive(name_, blocker_); - archive(hasTransformComponent); archive(hasDefenseComponent); archive(hasAttackComponent); archive(hasConsumableComponent); @@ -139,9 +128,6 @@ namespace cpprl { archive(hasContainer); archive(hasStatsComponent); - if (hasTransformComponent) { - archive(transformComponent_); - } if (hasAsciiComponent) { archive(asciiComponent_); } diff --git a/app/include/util.hpp b/app/include/util.hpp index d2d044c..4640d0c 100644 --- a/app/include/util.hpp +++ b/app/include/util.hpp @@ -4,11 +4,14 @@ #include "components.hpp" #include "types/math.hpp" +extern SupaRL::Coordinator g_coordinator; namespace cpprl::util { template inline auto find_entity_at(std::vector& vec, int x, int y) { auto iterator = std::find_if(vec.begin(), vec.end(), [x, y](T& entity) { - return entity->get_transform_component().position_ == SupaRL::Vector2D{x, y}; + auto entity_position = g_coordinator.get_component( + entity->get_id()).position_; + return entity_position == SupaRL::Vector2D{x, y}; }); return iterator; } diff --git a/app/include/world.hpp b/app/include/world.hpp index 0e1be15..8180ecb 100644 --- a/app/include/world.hpp +++ b/app/include/world.hpp @@ -75,7 +75,7 @@ namespace cpprl { } } archive(entities_); - player_ = new Entity("", false, nullptr, nullptr); + player_ = new Entity("", false, nullptr); // TODO: If player is confused, quitting and reopening the game removes the // confused state player_->unpack(archive); diff --git a/app/src/basic_ai_component.cpp b/app/src/basic_ai_component.cpp index c76845f..a72b8df 100644 --- a/app/src/basic_ai_component.cpp +++ b/app/src/basic_ai_component.cpp @@ -9,6 +9,7 @@ #include "world.hpp" #include +extern SupaRL::Coordinator g_coordinator; namespace cpprl { bool can_path_to_target(tcod::BresenhamLine& path, World& world) { @@ -22,10 +23,10 @@ bool can_path_to_target(tcod::BresenhamLine& path, World& world) { } void HostileAI::update(World& world, Entity* entity) { - SupaRL::Vector2D position = entity->get_transform_component().position_; + auto position = g_coordinator.get_component(entity->get_id()).position_; if (world.get_map().is_in_fov(position)) { Entity* player = world.get_player(); - SupaRL::Vector2D player_position = player->get_transform_component().position_; + auto player_position = g_coordinator.get_component(player->get_id()).position_; SupaRL::Vector2D delta = player_position - position; int distance = std::max(std::abs(delta.x), std::abs(delta.y)); diff --git a/app/src/components.cpp b/app/src/components.cpp index ae27531..64375cd 100644 --- a/app/src/components.cpp +++ b/app/src/components.cpp @@ -73,7 +73,11 @@ namespace cpprl { container->remove(owner); // TODO: This would then use the global coordinator to // get the transform of each entity and set the positions. - owner->get_transform_component().position_ = wearer->get_transform_component().position_; + auto owner_transform = g_coordinator.get_component( + owner->get_id()); + auto wearer_position = g_coordinator.get_component( + wearer->get_id()).position_; + owner_transform.position_ = wearer_position; return Success{}; } return Failure{}; @@ -109,9 +113,11 @@ namespace cpprl { } ActionResult LightningBolt::use(Entity* owner, Entity* wearer, World& world) { + auto wearer_position = g_coordinator.get_component( + wearer->get_id()).position_; std::optional> optional_closest_monster_ref = world.get_entities().get_closest_living_monster( - wearer->get_transform_component().position_, range_); + wearer_position, range_); if (!optional_closest_monster_ref.has_value()) { return Failure{"No enemy is close enough to strike."}; } @@ -145,9 +151,11 @@ namespace cpprl { auto on_pick = [&, owner, wearer]() { ConsumableComponent::use(owner, wearer, world); for (Entity* entity : world.get_entities()) { + auto entity_position = g_coordinator.get_component( + entity->get_id()).position_; if (const auto* defense_component = &entity->get_defense_component(); defense_component && defense_component->is_not_dead() && - entity->get_transform_component().position_.distance_to( + entity_position.distance_to( world.get_map().get_highlight_tile()) <= aoe_) { world.get_message_log().add_message( fmt::format( diff --git a/app/src/consumable_factory.cpp b/app/src/consumable_factory.cpp index 1e62240..967a1a0 100644 --- a/app/src/consumable_factory.cpp +++ b/app/src/consumable_factory.cpp @@ -7,40 +7,39 @@ extern SupaRL::Coordinator g_coordinator; namespace cpprl { Entity* AbstractConsumableFactory::create_base( - std::string name, tcod::ColorRGB color, std::string_view symbol) { + std::string name, tcod::ColorRGB color, std::string_view symbol,SupaRL::Vector2D at_position) { Entity* new_entity = new Entity( name, false, - std::make_unique(0, 0), std::make_unique(symbol, color, 1)); auto entity_id = g_coordinator.create_entity(); new_entity->set_id(entity_id); g_coordinator.add_component(entity_id, SupaRL::TransformComponent{ - .position_ = {0, 0}}); + .position_ = at_position}); return new_entity; } - Entity* HealthPotionFactory::create() { - Entity* entity = create_base("Health Potion", RED, "!"); + Entity* HealthPotionFactory::create(SupaRL::Vector2D at_position) { + Entity* entity = create_base("Health Potion", RED, "!", at_position); entity->set_consumable_component(std::make_unique(10)); return entity; } - Entity* LightningScrollFactory::create() { - Entity* entity = create_base("Lightning Scroll", DARK_RED, "#"); + Entity* LightningScrollFactory::create(SupaRL::Vector2D at_position) { + Entity* entity = create_base("Lightning Scroll", DARK_RED, "#", at_position); entity->set_consumable_component(std::make_unique(5, 20)); return entity; } - Entity* FireballScrollFactory::create() { - Entity* entity = create_base("Fireball Scroll", DARK_RED, "#"); + Entity* FireballScrollFactory::create(SupaRL::Vector2D at_position) { + Entity* entity = create_base("Fireball Scroll", DARK_RED, "#", at_position); entity->set_consumable_component(std::make_unique(3, 5, 20)); return entity; } - Entity* ConfusionScrollFactory::create() { - Entity* entity = create_base("Confusion Scroll", DARK_RED, "#"); + Entity* ConfusionScrollFactory::create(SupaRL::Vector2D at_position) { + Entity* entity = create_base("Confusion Scroll", DARK_RED, "#", at_position); entity->set_consumable_component(std::make_unique(5, 0)); return entity; } diff --git a/app/src/engine.cpp b/app/src/engine.cpp index c37731b..e8ae3f4 100644 --- a/app/src/engine.cpp +++ b/app/src/engine.cpp @@ -153,7 +153,6 @@ namespace cpprl { engine_state_->on_enter(); } physics_system_->update(); - std::cout << "End turn" << std::endl; } else if (std::holds_alternative(result)) { // TODO: there's a bug here. We should only save // when exiting the game, not when quitting to the main menu. diff --git a/app/src/entity_factory.cpp b/app/src/entity_factory.cpp index ca639a8..4cf291e 100644 --- a/app/src/entity_factory.cpp +++ b/app/src/entity_factory.cpp @@ -17,11 +17,10 @@ namespace cpprl { return new Entity( name, true, - std::make_unique(0, 0), std::make_unique(symbol, color, 1)); } - Entity* OrcFactory::create() { + Entity* OrcFactory::create(SupaRL::Vector2D at_position) { Entity* entity = create_base("Orc", DARK_GREEN, "o"); entity->set_attack_component(std::make_unique(3)); @@ -40,7 +39,7 @@ namespace cpprl { g_coordinator.add_component(entity_id, SupaRL::AttackComponent{ .damage_ = 3}); g_coordinator.add_component(entity_id, SupaRL::TransformComponent{ - .position_ = {0, 0}}); + .position_ = at_position}); g_coordinator.add_component( entity_id, SupaRL::VelocityComponent{ .velocity_ = {0,0}}); @@ -48,7 +47,7 @@ namespace cpprl { return entity; } - Entity* TrollFactory::create() { + Entity* TrollFactory::create(SupaRL::Vector2D at_position) { Entity* entity = create_base("Troll", DARK_GREEN, "T"); entity->set_attack_component(std::make_unique(4)); @@ -67,7 +66,7 @@ namespace cpprl { g_coordinator.add_component(entity_id, SupaRL::AttackComponent{ .damage_ = 4}); g_coordinator.add_component(entity_id, SupaRL::TransformComponent{ - .position_ = {0, 0}}); + .position_ = at_position}); g_coordinator.add_component( entity_id, SupaRL::VelocityComponent{ .velocity_ = {0,0}}); @@ -75,7 +74,7 @@ namespace cpprl { return entity; } - Entity* PlayerFactory::create() { + Entity* PlayerFactory::create(SupaRL::Vector2D at_position) { Entity* entity = create_base("Player", TEAL, "@"); entity->set_attack_component(std::make_unique(5)); @@ -94,7 +93,7 @@ namespace cpprl { g_coordinator.add_component(entity_id, SupaRL::AttackComponent{ .damage_ = 5}); g_coordinator.add_component(entity_id, SupaRL::TransformComponent{ - .position_ = {0, 0}}); + .position_ = at_position}); g_coordinator.add_component( entity_id, SupaRL::VelocityComponent{ .velocity_ = {0,0}}); diff --git a/app/src/entity_manager.cpp b/app/src/entity_manager.cpp index 5777d89..4a5c6c8 100644 --- a/app/src/entity_manager.cpp +++ b/app/src/entity_manager.cpp @@ -13,9 +13,8 @@ #include "core/coordinator.hpp" +extern SupaRL::Coordinator g_coordinator; namespace cpprl { - extern SupaRL::Coordinator g_coordinator; - void EntityManager::clear() { entities_.clear(); } void EntityManager::clear_except_player() { @@ -45,12 +44,11 @@ namespace cpprl { } if (random->getFloat(0.0f, 1.0f) < 0.8f) { - Entity* entity = orc_factory_->create(); - entity->get_transform_component().position_ = {x, y}; + Entity* entity = orc_factory_->create({x,y}); spawn(std::move(entity)); } else { - Entity* entity = troll_factory_->create(); - spawn(std::move(entity), {x, y}); + Entity* entity = troll_factory_->create({x,y}); + spawn(std::move(entity)); } } @@ -69,21 +67,21 @@ namespace cpprl { float dice = random->getFloat(.0f, 1.0f); if (dice <= 0.7f) { auto health_potion_factory = std::make_unique(); - Entity* entity = health_potion_factory->create(); - spawn(std::move(entity), {x, y}); + Entity* entity = health_potion_factory->create({x,y}); + spawn(std::move(entity)); } else if (dice <= .8f) { auto lighting_scroll_factory = std::make_unique(); - Entity* entity = lighting_scroll_factory->create(); - spawn(std::move(entity), {x, y}); + Entity* entity = lighting_scroll_factory->create({x,y}); + spawn(std::move(entity)); } else if (dice <= .9f) { auto fireball_scroll_factory = std::make_unique(); - Entity* entity = fireball_scroll_factory->create(); - spawn(std::move(entity), {x, y}); + Entity* entity = fireball_scroll_factory->create({x,y}); + spawn(std::move(entity)); } else if (dice <= 1.0f) { auto confusion_scroll_factory = std::make_unique(); - Entity* entity = confusion_scroll_factory->create(); - spawn(std::move(entity), {x, y}); + Entity* entity = confusion_scroll_factory->create({x,y}); + spawn(std::move(entity)); } } } @@ -92,16 +90,6 @@ namespace cpprl { return entities_.emplace_back(src); } - Entity* EntityManager::spawn(Entity* src, SupaRL::Vector2D position) { - Entity* entity = spawn(src); - - if (position != entity->get_transform_component().position_) { - entity->get_transform_component().position_ = position; - } - - return entity; - } - std::vector> EntityManager::get_entities_at( SupaRL::Vector2D position) { std::vector> entities_at_position; @@ -109,7 +97,9 @@ namespace cpprl { // Corpse, Item, Actor... entities_at_position.reserve(3); for (auto& entity : entities_) { - if (entity->get_transform_component().position_ == position) { + auto entity_position = g_coordinator.get_component( + entity->get_id()).position_; + if (entity_position == position) { entities_at_position.push_back(*entity); } } @@ -120,8 +110,10 @@ namespace cpprl { std::optional> EntityManager::get_blocking_entity_at(SupaRL::Vector2D position) { for (const auto& entity : entities_) { + auto entity_position = g_coordinator.get_component( + entity->get_id()).position_; if (entity->is_blocking() && - entity->get_transform_component().position_ == position) { + entity_position == position) { return std::reference_wrapper(*entity); } } @@ -131,8 +123,10 @@ namespace cpprl { std::optional> EntityManager::get_non_blocking_entity_at(SupaRL::Vector2D position) { for (const auto& entity : entities_) { + auto entity_position = g_coordinator.get_component( + entity->get_id()).position_; if (!entity->is_blocking() && - entity->get_transform_component().position_ == position) { + entity_position == position) { return std::reference_wrapper(*entity); } } @@ -157,8 +151,10 @@ namespace cpprl { entity->get_ai_component(); if (ai_component.has_value() && defense_component) { + auto entity_position = g_coordinator.get_component( + entity->get_id()).position_; float distance = position.distance_to( - entity->get_transform_component().position_); + entity_position); if (distance < best_distance && (distance <= range || range == 0.0f)) { best_distance = distance; closest = *entity; diff --git a/app/src/events/command.cpp b/app/src/events/command.cpp index 9e7c583..7241b54 100644 --- a/app/src/events/command.cpp +++ b/app/src/events/command.cpp @@ -16,9 +16,11 @@ extern SupaRL::Coordinator g_coordinator; namespace cpprl { StateResult PickupCommand::execute() { + auto entity_position = g_coordinator.get_component( + entity_->get_id()).position_; std::optional> optional_item_ref = world_.get_entities().get_non_blocking_entity_at( - entity_->get_transform_component().position_); + entity_position); if(!optional_item_ref.has_value()) { return NoOp{"There is nothing here to pick up."}; } @@ -138,8 +140,10 @@ namespace cpprl { } StateResult DirectionalCommand::execute() { + auto entity_position = g_coordinator.get_component( + entity_->get_id()).position_; auto targetPos = - entity_->get_transform_component().position_ + move_vector_; + entity_position + move_vector_; if (world_.get_entities().get_blocking_entity_at(targetPos)) { auto action = MeleeCommand(world_, entity_, move_vector_); @@ -164,8 +168,10 @@ namespace cpprl { } StateResult MeleeCommand::execute() { + auto entity_position = g_coordinator.get_component( + entity_->get_id()).position_; auto targetPos = - entity_->get_transform_component().position_ + move_vector_; + entity_position + move_vector_; std::optional> target = world_.get_entities().get_blocking_entity_at(targetPos); @@ -202,8 +208,10 @@ namespace cpprl { }; StateResult MovementCommand::execute() { + auto entity_position = g_coordinator.get_component( + entity_->get_id()).position_; SupaRL::Vector2D new_position = - entity_->get_transform_component().position_ + move_vector_; + entity_position + move_vector_; auto& map = world_.get_map(); if (map.is_not_in_bounds(new_position)) { diff --git a/app/src/game_entity.cpp b/app/src/game_entity.cpp index 9431a9c..1f07e2d 100644 --- a/app/src/game_entity.cpp +++ b/app/src/game_entity.cpp @@ -11,11 +11,9 @@ namespace cpprl { Entity::Entity( std::string const& name, bool blocker, - std::unique_ptr transformComponent, std::unique_ptr asciiComponent) : name_(name), blocker_(blocker), - transformComponent_(std::move(transformComponent)), asciiComponent_(std::move(asciiComponent)) {} void Entity::update(World& world) { aiComponent_->update(world, this); } diff --git a/app/src/input_handler.cpp b/app/src/input_handler.cpp index cf70f7f..6329481 100644 --- a/app/src/input_handler.cpp +++ b/app/src/input_handler.cpp @@ -8,6 +8,8 @@ #include "gui.hpp" #include "world.hpp" +extern SupaRL::Coordinator g_coordinator; + namespace cpprl { EngineEvent& EventHandler::handle_sdl_event(SDL_Event event) noexcept { SDL_Keycode key = event.key.keysym.sym; @@ -118,8 +120,10 @@ EngineEvent& GameInputHandler::handle_sdl_event(SDL_Event event) noexcept { } else if (key == SDLK_PERIOD) { return *character_menu_command_; } else if (key == SDLK_LEFTBRACKET) { + auto entity_position = g_coordinator.get_component( + controllable_entity_->get_id()).position_; use_command_ = std::make_unique( - world_, controllable_entity_->get_transform_component().position_); + world_, entity_position); return *use_command_; } else { return EventHandler::handle_sdl_event(event); diff --git a/app/src/state.cpp b/app/src/state.cpp index ab6cc8a..bc15811 100644 --- a/app/src/state.cpp +++ b/app/src/state.cpp @@ -50,8 +50,10 @@ namespace cpprl { world_.get_dungeon().increase_level(); world_.generate_map(80, 35, true); // Add the player back to the entities. - world_.get_entities().spawn( - world_.get_player(), world_.get_map().get_rooms().at(0).get_center()); + auto player_transform = g_coordinator.get_component( + world_.get_player()->get_id()); + player_transform.position_ = world_.get_map().get_rooms().at(0).get_center(); + world_.get_entities().spawn(world_.get_player()); } StateResult NextLevelState::on_update(SDL_Event&) { diff --git a/app/src/world.cpp b/app/src/world.cpp index 4ce1919..f460765 100644 --- a/app/src/world.cpp +++ b/app/src/world.cpp @@ -48,15 +48,19 @@ namespace cpprl { } void World::render(Renderer& renderer) { + auto player_position = g_coordinator.get_component( + player_->get_id()).position_; dungeon_.get_map().compute_fov( - player_->get_transform_component().position_, 10); + player_position, 10); dungeon_.get_map().render(g_console); for (const auto& entity : *entities_) { + auto entity_transform = g_coordinator.get_component( + entity->get_id()); if (dungeon_.get_map().is_in_fov( - entity->get_transform_component().position_)) { + entity_transform.position_)) { renderer.render( - entity->get_sprite_component(), entity->get_transform_component()); + entity->get_sprite_component(), entity_transform); } } ui_->render(g_console); @@ -94,24 +98,17 @@ namespace cpprl { void World::spawn_player() { auto player_factory_ = std::make_unique(); - Entity* player = player_factory_->create(); - - auto transform = g_coordinator.get_component( - player->get_id()); - - transform.position_ = SupaRL::Vector2D{ - .x = dungeon_.get_map().get_rooms().at(0).get_center().x, - .y = dungeon_.get_map().get_rooms().at(0).get_center().y - }; - player->get_transform_component().position_ = - dungeon_.get_map().get_rooms().at(0).get_center(); + auto spawn_position = dungeon_.get_map().get_rooms().at(0).get_center(); + Entity* player = player_factory_->create(spawn_position); World::spawn_player(player); } void World::spawn_player(Entity* player) { player_ = entities_->spawn(player); + SupaRL::Vector2D player_position= g_coordinator.get_component( + player_->get_id()).position_; dungeon_.get_map().compute_fov( - player_->get_transform_component().position_, 4); + player_position, 4); DefenseComponent& player_defense = player_->get_defense_component(); ui_->set_health_bar(player_defense); ui_->set_xp_bar(player_->get_stats_component().value().get()); @@ -125,5 +122,4 @@ namespace cpprl { current_window_->set_cursor(current_window_->get_cursor() + scroll_amount); } } - } diff --git a/lib/include/core/entity_manager.hpp b/lib/include/core/entity_manager.hpp index 82412f6..7e0937f 100644 --- a/lib/include/core/entity_manager.hpp +++ b/lib/include/core/entity_manager.hpp @@ -39,9 +39,6 @@ namespace SupaRL { void set_signature(Entity entity, Signature signature) { - std::cout << "Setting signature for entity " << entity << std::endl; - std::cout << "Num Signatures: " << signatures_.size() << std::endl; - std::cout << "Signature: " << signatures_[entity] << std::endl; assert(entity < MAX_ENTITIES && "Entity out of range."); signatures_[entity] = signature; diff --git a/lib/include/core/system_manager.hpp b/lib/include/core/system_manager.hpp index 01dbe99..ce54ce4 100644 --- a/lib/include/core/system_manager.hpp +++ b/lib/include/core/system_manager.hpp @@ -46,15 +46,11 @@ namespace SupaRL void entity_signature_changed(Entity entity, Signature entitySignature) { - std::cout << "Entity signature changed " << entity << std::endl; for (auto const& pair : systems_) { auto const& type = pair.first; auto const& system = pair.second; - std::cout << "System: " << type << std::endl; - std::cout << "System: " << system << std::endl; auto const& systemSignature = signatures_[type]; - std::cout << "Entities: " << system->entities_.size() << std::endl; if ((entitySignature & systemSignature) == systemSignature) { @@ -62,9 +58,7 @@ namespace SupaRL } else { - std::cout << "Erasing entity" << std::endl; system->entities_.erase(entity); - std::cout << "Entities: " << system->entities_.size() << std::endl; } } } From 825ab8df78fe76e5d7a13880df712a9e19f5bda8 Mon Sep 17 00:00:00 2001 From: daniel baker Date: Fri, 11 Oct 2024 09:50:46 +0100 Subject: [PATCH 08/38] feat: remove unused class --- app/include/components.hpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/app/include/components.hpp b/app/include/components.hpp index 3bf8699..eb1fdec 100644 --- a/app/include/components.hpp +++ b/app/include/components.hpp @@ -57,23 +57,6 @@ namespace cpprl { int max_hp_; }; - class TransformComponent { - public: - TransformComponent() = default; - TransformComponent(int x, int y) : position_({x, y}) {} - virtual ~TransformComponent() = default; - SupaRL::Vector2D get_position() const { return position_; } - void move(SupaRL::Vector2D new_position) { position_ = new_position; } - - template - void serialize(Archive& archive) { - archive(position_); - } - - private: - SupaRL::Vector2D position_; - }; - class ASCIIComponent { public: ASCIIComponent() = default; From 5949af70505568dcaa5aff4a7349ca39aef85b5e Mon Sep 17 00:00:00 2001 From: daniel baker Date: Fri, 11 Oct 2024 10:48:26 +0100 Subject: [PATCH 09/38] feat: move ascii comp to ecs --- app/include/colours.hpp | 39 +++++++++++++++--------------- app/include/components.hpp | 25 +------------------ app/include/consumable_factory.hpp | 2 +- app/include/entity_factory.hpp | 2 +- app/include/entity_manager.hpp | 2 +- app/include/game_entity.hpp | 14 +---------- app/include/rendering.hpp | 7 +++--- app/include/world.hpp | 2 +- app/src/TCODRenderer.cpp | 14 ++++++++--- app/src/components.cpp | 13 ++++++++-- app/src/consumable_factory.cpp | 11 ++++++--- app/src/engine.cpp | 3 +++ app/src/entity_factory.cpp | 28 ++++++++++++--------- app/src/events/command.cpp | 3 --- app/src/game_entity.cpp | 11 ++------- app/src/state.cpp | 2 +- app/src/world.cpp | 4 ++- lib/include/components/ascii.hpp | 17 +++++++++++++ 18 files changed, 100 insertions(+), 99 deletions(-) create mode 100644 lib/include/components/ascii.hpp diff --git a/app/include/colours.hpp b/app/include/colours.hpp index eeabea4..d1e2d9a 100644 --- a/app/include/colours.hpp +++ b/app/include/colours.hpp @@ -1,27 +1,26 @@ - #pragma once #include namespace cpprl { -static constexpr auto WHITE = tcod::ColorRGB{200, 200, 200}; -static constexpr auto RED = tcod::ColorRGB{255, 0, 0}; -static constexpr auto DARK_RED = tcod::ColorRGB{191, 0, 0}; -static constexpr auto BLACK = tcod::ColorRGB{40, 42, 54}; -static constexpr auto BLACK_DARK = tcod::ColorRGB{18, 19, 18}; -static constexpr auto GREY = tcod::ColorRGB{128, 128, 128}; -static constexpr auto GREEN = tcod::ColorRGB{0, 255, 0}; -static constexpr auto DARK_GREEN = tcod::ColorRGB{0, 191, 0}; -static constexpr auto BLUE = tcod::ColorRGB{0, 0, 255}; -static constexpr auto DARK_BLUE = tcod::ColorRGB{0, 0, 191}; -static constexpr auto TEAL = tcod::ColorRGB{0, 128, 128}; -static constexpr auto DARK_GREY = tcod::ColorRGB{64, 64, 64}; -static constexpr auto LIGHT_BLUE = tcod::ColorRGB{0, 128, 255}; + static constexpr auto WHITE = tcod::ColorRGB{200, 200, 200}; + static constexpr auto RED = tcod::ColorRGB{255, 0, 0}; + static constexpr auto DARK_RED = tcod::ColorRGB{191, 0, 0}; + static constexpr auto BLACK = tcod::ColorRGB{40, 42, 54}; + static constexpr auto BLACK_DARK = tcod::ColorRGB{18, 19, 18}; + static constexpr auto GREY = tcod::ColorRGB{128, 128, 128}; + static constexpr auto GREEN = tcod::ColorRGB{0, 255, 0}; + static constexpr auto DARK_GREEN = tcod::ColorRGB{0, 191, 0}; + static constexpr auto BLUE = tcod::ColorRGB{0, 0, 255}; + static constexpr auto DARK_BLUE = tcod::ColorRGB{0, 0, 191}; + static constexpr auto TEAL = tcod::ColorRGB{0, 128, 128}; + static constexpr auto DARK_GREY = tcod::ColorRGB{64, 64, 64}; + static constexpr auto LIGHT_BLUE = tcod::ColorRGB{0, 128, 255}; -static constexpr auto BAR_TEXT = WHITE; -static constexpr auto INVALID = tcod::ColorRGB{0xFF, 0xFF, 0x00}; -static constexpr auto IMPOSSIBLE = tcod::ColorRGB(0x80, 0x80, 0x80); -static constexpr auto ERROR = tcod::ColorRGB(0xFF, 0x40, 0x40); -static constexpr auto HEALTH_RECOVERED = tcod::ColorRGB(0x0, 0xFF, 0x0); + static constexpr auto BAR_TEXT = WHITE; + static constexpr auto INVALID = tcod::ColorRGB{0xFF, 0xFF, 0x00}; + static constexpr auto IMPOSSIBLE = tcod::ColorRGB(0x80, 0x80, 0x80); + static constexpr auto ERROR = tcod::ColorRGB(0xFF, 0x40, 0x40); + static constexpr auto HEALTH_RECOVERED = tcod::ColorRGB(0x0, 0xFF, 0x0); -} // namespace cpprl +} diff --git a/app/include/components.hpp b/app/include/components.hpp index eb1fdec..00ad788 100644 --- a/app/include/components.hpp +++ b/app/include/components.hpp @@ -2,7 +2,6 @@ #include #include -#include #include "game_entity.hpp" #include "types/action_result.hpp" @@ -57,28 +56,6 @@ namespace cpprl { int max_hp_; }; - class ASCIIComponent { - public: - ASCIIComponent() = default; - ASCIIComponent(std::string_view symbol, tcod::ColorRGB colour, int layer) - : symbol_(symbol), colour_(colour), layer_(layer) {} - virtual ~ASCIIComponent() = default; - - std::string_view get_symbol() const { return symbol_; } - tcod::ColorRGB get_colour() const { return colour_; } - int get_layer() const { return layer_; } - - template - void serialize(Archive& archive) { - archive(symbol_, colour_, layer_); - } - - private: - std::string symbol_; - tcod::ColorRGB colour_; - int layer_; - }; - class Container { private: size_t size_; @@ -107,7 +84,7 @@ namespace cpprl { int nb_items; archive(nb_items); for (int i = 0; i < nb_items; i++) { - Entity* entity = new Entity("", false, nullptr); + Entity* entity = new Entity("", false); entity->unpack(archive); inventory_.emplace_back(entity); } diff --git a/app/include/consumable_factory.hpp b/app/include/consumable_factory.hpp index 567a0b9..8fe245b 100644 --- a/app/include/consumable_factory.hpp +++ b/app/include/consumable_factory.hpp @@ -14,7 +14,7 @@ namespace cpprl { protected: Entity* create_base( - std::string name, tcod::ColorRGB color, std::string_view symbol,SupaRL::Vector2D at_position); + std::string name, tcod::ColorRGB color, std::string symbol,SupaRL::Vector2D at_position); }; class HealthPotionFactory : public AbstractConsumableFactory { diff --git a/app/include/entity_factory.hpp b/app/include/entity_factory.hpp index 80501a5..5a28be5 100644 --- a/app/include/entity_factory.hpp +++ b/app/include/entity_factory.hpp @@ -13,7 +13,7 @@ namespace cpprl { protected: Entity* create_base( - const std::string& name, tcod::ColorRGB color, std::string_view symbol); + const std::string& name, tcod::ColorRGB color, std::string symbol); }; class OrcFactory : public AbstractEntityFactory { diff --git a/app/include/entity_manager.hpp b/app/include/entity_manager.hpp index a39f7ab..135c56c 100644 --- a/app/include/entity_manager.hpp +++ b/app/include/entity_manager.hpp @@ -75,7 +75,7 @@ namespace cpprl { archive(size); entities_.reserve(size); for (size_t i = 0; i < size; i++) { - auto entity = new Entity("", false, nullptr); + auto entity = new Entity("", false); entity->unpack(archive); entities_.emplace_back(entity); } diff --git a/app/include/game_entity.hpp b/app/include/game_entity.hpp index 0c5f31d..9fb87fa 100644 --- a/app/include/game_entity.hpp +++ b/app/include/game_entity.hpp @@ -15,7 +15,6 @@ extern SupaRL::Coordinator g_coordinator; namespace cpprl { class TransformComponent; - class ASCIIComponent; class AttackComponent; class DefenseComponent; class ConsumableComponent; @@ -28,7 +27,6 @@ namespace cpprl { std::string name_; bool blocker_; - std::unique_ptr asciiComponent_; std::unique_ptr attackComponent_; std::unique_ptr defenseComponent_; std::unique_ptr consumableComponent_; @@ -39,15 +37,13 @@ namespace cpprl { public: Entity( std::string const& name, - bool blocker, - std::unique_ptr asciiComponent); + bool blocker); ~Entity() = default; void set_id(SupaRL::Entity id) { id_ = id; }; SupaRL::Entity get_id() { return id_; }; - ASCIIComponent& get_sprite_component() { return *asciiComponent_; }; AttackComponent& get_attack_component() { return *attackComponent_; }; DefenseComponent& get_defense_component() { return *defenseComponent_; }; ConsumableComponent& get_consumable_component() { @@ -79,7 +75,6 @@ namespace cpprl { void update(World& world); void set_blocking(bool blocker) { blocker_ = blocker; }; void set_name(std::string_view name) { name_ = name; }; - void set_ascii_component(std::unique_ptr asciiComponent); void set_defense_component( std::unique_ptr defenseComponent); void set_attack_component(std::unique_ptr attackComponent); @@ -95,12 +90,10 @@ namespace cpprl { archive(defenseComponent_ != nullptr); archive(attackComponent_ != nullptr); archive(consumableComponent_ != nullptr); - archive(asciiComponent_ != nullptr); archive(aiComponent_ != nullptr); archive(container_ != nullptr); archive(statsComponent_ != nullptr); - if (asciiComponent_) archive(asciiComponent_); if (attackComponent_) archive(attackComponent_); if (defenseComponent_) archive(defenseComponent_); if (consumableComponent_) archive(consumableComponent_); @@ -114,7 +107,6 @@ namespace cpprl { bool hasDefenseComponent; bool hasAttackComponent; bool hasConsumableComponent; - bool hasAsciiComponent; bool hasAIComponent; bool hasContainer; bool hasStatsComponent; @@ -123,14 +115,10 @@ namespace cpprl { archive(hasDefenseComponent); archive(hasAttackComponent); archive(hasConsumableComponent); - archive(hasAsciiComponent); archive(hasAIComponent); archive(hasContainer); archive(hasStatsComponent); - if (hasAsciiComponent) { - archive(asciiComponent_); - } if (hasAttackComponent) { archive(attackComponent_); } diff --git a/app/include/rendering.hpp b/app/include/rendering.hpp index f96de96..693e93a 100644 --- a/app/include/rendering.hpp +++ b/app/include/rendering.hpp @@ -7,17 +7,16 @@ #include "globals.hpp" #include "util.hpp" #include +#include namespace cpprl { - class ASCIIComponent; - class TransformComponent; class Renderer { public: Renderer() {} virtual ~Renderer() {} virtual void render( - ASCIIComponent& sprite, SupaRL::TransformComponent& transform) = 0; + SupaRL::AsciiComponent& sprite, SupaRL::TransformComponent& transform) = 0; }; class TCODRenderer : public Renderer { @@ -42,6 +41,6 @@ namespace cpprl { }; ~TCODRenderer() = default; - virtual void render(ASCIIComponent& sprite, SupaRL::TransformComponent& transform); + virtual void render(SupaRL::AsciiComponent& sprite, SupaRL::TransformComponent& transform); }; } diff --git a/app/include/world.hpp b/app/include/world.hpp index 8180ecb..2039546 100644 --- a/app/include/world.hpp +++ b/app/include/world.hpp @@ -75,7 +75,7 @@ namespace cpprl { } } archive(entities_); - player_ = new Entity("", false, nullptr); + player_ = new Entity("", false); // TODO: If player is confused, quitting and reopening the game removes the // confused state player_->unpack(archive); diff --git a/app/src/TCODRenderer.cpp b/app/src/TCODRenderer.cpp index 68bce3e..b0b9170 100644 --- a/app/src/TCODRenderer.cpp +++ b/app/src/TCODRenderer.cpp @@ -1,14 +1,20 @@ -#include "components.hpp" #include "rendering.hpp" +#include +#include namespace cpprl { void TCODRenderer::render( - ASCIIComponent& sprite, SupaRL::TransformComponent& transform) { + SupaRL::AsciiComponent& sprite, SupaRL::TransformComponent& transform) { + tcod::print( g_console, transform.position_, - sprite.get_symbol(), - sprite.get_colour(), + sprite.symbol_, + tcod::ColorRGB{ + sprite.colour_.r, + sprite.colour_.g, + sprite.colour_.b + }, std::nullopt); } } diff --git a/app/src/components.cpp b/app/src/components.cpp index 64375cd..5338dbf 100644 --- a/app/src/components.cpp +++ b/app/src/components.cpp @@ -13,6 +13,8 @@ #include "state.hpp" #include "world.hpp" +extern SupaRL::Coordinator g_coordinator; + namespace cpprl { int DefenseComponent::heal(int amount) { if (hp_ == max_hp_) { @@ -31,8 +33,15 @@ namespace cpprl { } void DefenseComponent::die(Entity& the_deceased) const { - the_deceased.set_name("corpse of " + the_deceased.get_name()); - the_deceased.set_ascii_component(std::make_unique("%", RED, -1)); + the_deceased.set_name("Corpse of " + the_deceased.get_name()); + + auto& ascii_comp = g_coordinator.get_component( + the_deceased.get_id()); + + ascii_comp.symbol_ = "%"; + ascii_comp.colour_ = SupaRL::ColorRGB{RED.r, RED.g, RED.b}; + ascii_comp.layer_ = -1; + the_deceased.set_blocking(false); the_deceased.set_ai_component(nullptr); } diff --git a/app/src/consumable_factory.cpp b/app/src/consumable_factory.cpp index 967a1a0..725d94d 100644 --- a/app/src/consumable_factory.cpp +++ b/app/src/consumable_factory.cpp @@ -3,19 +3,24 @@ #include #include "components.hpp" +#include +#include extern SupaRL::Coordinator g_coordinator; namespace cpprl { Entity* AbstractConsumableFactory::create_base( - std::string name, tcod::ColorRGB color, std::string_view symbol,SupaRL::Vector2D at_position) { + std::string name, tcod::ColorRGB color, std::string symbol,SupaRL::Vector2D at_position) { Entity* new_entity = new Entity( name, - false, - std::make_unique(symbol, color, 1)); + false); auto entity_id = g_coordinator.create_entity(); new_entity->set_id(entity_id); g_coordinator.add_component(entity_id, SupaRL::TransformComponent{ .position_ = at_position}); + g_coordinator.add_component(entity_id, SupaRL::AsciiComponent{ + .symbol_ = symbol, + .colour_ = SupaRL::ColorRGB{.r = color.r, .g = color.g, .b = color.b}, + .layer_ = 1}); return new_entity; } diff --git a/app/src/engine.cpp b/app/src/engine.cpp index e8ae3f4..50b25e1 100644 --- a/app/src/engine.cpp +++ b/app/src/engine.cpp @@ -23,10 +23,12 @@ #include "components/transform.hpp" #include "components/status_condition.hpp" #include "components/velocity.hpp" +#include "components/ascii.hpp" #include "systems/status_condition_system.hpp" #include "systems/combat_system.hpp" #include "core/types.hpp" +// TODO: Service Locator pattern? SupaRL::Coordinator g_coordinator; namespace cpprl { @@ -54,6 +56,7 @@ namespace cpprl { g_coordinator.register_component(); g_coordinator.register_component(); g_coordinator.register_component(); + g_coordinator.register_component(); /*g_coordinator.register_system();*/ /*{*/ diff --git a/app/src/entity_factory.cpp b/app/src/entity_factory.cpp index 4cf291e..1763897 100644 --- a/app/src/entity_factory.cpp +++ b/app/src/entity_factory.cpp @@ -8,20 +8,30 @@ #include "components/attack.hpp" #include "components/transform.hpp" #include "components/velocity.hpp" +#include "components/ascii.hpp" extern SupaRL::Coordinator g_coordinator; namespace cpprl { Entity* AbstractEntityFactory::create_base( - const std::string& name, tcod::ColorRGB color, std::string_view symbol) { - return new Entity( + const std::string& name, tcod::ColorRGB color, std::string symbol) { + + auto entity = new Entity( name, - true, - std::make_unique(symbol, color, 1)); + true); + SupaRL::Entity entity_id = g_coordinator.create_entity(); + entity->set_id(entity_id); + g_coordinator.add_component(entity_id, SupaRL::AsciiComponent{ + .symbol_ = symbol, + .colour_ = SupaRL::ColorRGB{.r = color.r, .g = color.g, .b = color.b}, + .layer_ = 1}); + + return entity; } Entity* OrcFactory::create(SupaRL::Vector2D at_position) { Entity* entity = create_base("Orc", DARK_GREEN, "o"); + auto entity_id = entity->get_id(); entity->set_attack_component(std::make_unique(3)); entity->set_defense_component(std::make_unique(0, 10)); @@ -29,9 +39,6 @@ namespace cpprl { entity->set_stats_component( std::make_unique(10, 1, 10, 10, 2)); - SupaRL::Entity entity_id = g_coordinator.create_entity(); - entity->set_id(entity_id); - g_coordinator.add_component(entity_id, SupaRL::DefenceComponent{ .defence_ = 0, .hp_ = 10, @@ -56,8 +63,7 @@ namespace cpprl { entity->set_stats_component( std::make_unique(20, 1, 10, 20, 2)); - SupaRL::Entity entity_id = g_coordinator.create_entity(); - entity->set_id(entity_id); + auto entity_id = entity->get_id(); g_coordinator.add_component(entity_id, SupaRL::DefenceComponent{ .defence_ = 1, @@ -77,14 +83,14 @@ namespace cpprl { Entity* PlayerFactory::create(SupaRL::Vector2D at_position) { Entity* entity = create_base("Player", TEAL, "@"); + entity->set_attack_component(std::make_unique(5)); entity->set_defense_component(std::make_unique(2, 30)); entity->set_container(std::make_unique(26)); entity->set_stats_component( std::make_unique(0, 1, 150, 150, 2)); - SupaRL::Entity entity_id = g_coordinator.create_entity(); - entity->set_id(entity_id); + auto entity_id = entity->get_id(); g_coordinator.add_component(entity_id, SupaRL::DefenceComponent{ .defence_ = 2, diff --git a/app/src/events/command.cpp b/app/src/events/command.cpp index 7241b54..6392d00 100644 --- a/app/src/events/command.cpp +++ b/app/src/events/command.cpp @@ -26,9 +26,6 @@ namespace cpprl { } Entity& item = optional_item_ref.value().get(); - if (item.get_name().find("corpse") != std::string::npos) { - return NoOp{"There is nothing here to pick up."}; - } world_.get_message_log().add_message( "You pick up the " + item.get_name() + ".", WHITE); diff --git a/app/src/game_entity.cpp b/app/src/game_entity.cpp index 1f07e2d..b948303 100644 --- a/app/src/game_entity.cpp +++ b/app/src/game_entity.cpp @@ -10,19 +10,12 @@ namespace cpprl { Entity::Entity( std::string const& name, - bool blocker, - std::unique_ptr asciiComponent) + bool blocker) : name_(name), - blocker_(blocker), - asciiComponent_(std::move(asciiComponent)) {} + blocker_(blocker) {} void Entity::update(World& world) { aiComponent_->update(world, this); } - void Entity::set_ascii_component( - std::unique_ptr asciiComponent) { - asciiComponent_ = std::move(asciiComponent); - }; - void Entity::set_attack_component( std::unique_ptr attackComponent) { attackComponent_ = std::move(attackComponent); diff --git a/app/src/state.cpp b/app/src/state.cpp index bc15811..89e183a 100644 --- a/app/src/state.cpp +++ b/app/src/state.cpp @@ -50,7 +50,7 @@ namespace cpprl { world_.get_dungeon().increase_level(); world_.generate_map(80, 35, true); // Add the player back to the entities. - auto player_transform = g_coordinator.get_component( + auto& player_transform = g_coordinator.get_component( world_.get_player()->get_id()); player_transform.position_ = world_.get_map().get_rooms().at(0).get_center(); world_.get_entities().spawn(world_.get_player()); diff --git a/app/src/world.cpp b/app/src/world.cpp index f460765..e1811b9 100644 --- a/app/src/world.cpp +++ b/app/src/world.cpp @@ -59,8 +59,10 @@ namespace cpprl { entity->get_id()); if (dungeon_.get_map().is_in_fov( entity_transform.position_)) { + auto entity_sprite = g_coordinator.get_component( + entity->get_id()); renderer.render( - entity->get_sprite_component(), entity_transform); + entity_sprite, entity_transform); } } ui_->render(g_console); diff --git a/lib/include/components/ascii.hpp b/lib/include/components/ascii.hpp new file mode 100644 index 0000000..dd3efb7 --- /dev/null +++ b/lib/include/components/ascii.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace SupaRL{ + struct ColorRGB { + uint8_t r; + uint8_t g; + uint8_t b; + }; + + struct AsciiComponent { + std::string symbol_; + ColorRGB colour_; + int layer_; + }; +} From 01d2a7f4146bbbdac7b0cc8a0b2e015380372a89 Mon Sep 17 00:00:00 2001 From: daniel baker Date: Fri, 11 Oct 2024 11:16:49 +0100 Subject: [PATCH 10/38] feat: id and physique components --- app/include/components.hpp | 2 +- app/include/entity_manager.hpp | 5 +---- app/include/game_entity.hpp | 15 ++------------- app/include/world.hpp | 2 +- app/src/components.cpp | 26 +++++++++++++++++++------- app/src/consumable_factory.cpp | 10 +++++++--- app/src/engine.cpp | 4 ++++ app/src/entity_factory.cpp | 10 +++++++--- app/src/entity_manager.cpp | 23 +++++++++++++++-------- app/src/events/command.cpp | 25 +++++++++++++++++-------- app/src/game_entity.cpp | 5 ----- app/src/gui.cpp | 5 ++++- app/src/world.cpp | 7 +++++-- lib/include/components/identity.hpp | 9 +++++++++ lib/include/components/physique.hpp | 7 +++++++ 15 files changed, 99 insertions(+), 56 deletions(-) create mode 100644 lib/include/components/identity.hpp create mode 100644 lib/include/components/physique.hpp diff --git a/app/include/components.hpp b/app/include/components.hpp index 00ad788..ec5e8a6 100644 --- a/app/include/components.hpp +++ b/app/include/components.hpp @@ -84,7 +84,7 @@ namespace cpprl { int nb_items; archive(nb_items); for (int i = 0; i < nb_items; i++) { - Entity* entity = new Entity("", false); + Entity* entity = new Entity(); entity->unpack(archive); inventory_.emplace_back(entity); } diff --git a/app/include/entity_manager.hpp b/app/include/entity_manager.hpp index 135c56c..70942d2 100644 --- a/app/include/entity_manager.hpp +++ b/app/include/entity_manager.hpp @@ -62,9 +62,6 @@ namespace cpprl { void save(Archive& archive) const { archive(entities_.size() - 1); for (auto& entity : entities_) { - if (entity->get_name() == "Player") { - continue; - } entity->pack(archive); } } @@ -75,7 +72,7 @@ namespace cpprl { archive(size); entities_.reserve(size); for (size_t i = 0; i < size; i++) { - auto entity = new Entity("", false); + auto entity = new Entity(); entity->unpack(archive); entities_.emplace_back(entity); } diff --git a/app/include/game_entity.hpp b/app/include/game_entity.hpp index 9fb87fa..45abda4 100644 --- a/app/include/game_entity.hpp +++ b/app/include/game_entity.hpp @@ -25,8 +25,6 @@ namespace cpprl { private: SupaRL::Entity id_; - std::string name_; - bool blocker_; std::unique_ptr attackComponent_; std::unique_ptr defenseComponent_; std::unique_ptr consumableComponent_; @@ -35,14 +33,11 @@ namespace cpprl { std::unique_ptr statsComponent_; public: - Entity( - std::string const& name, - bool blocker); - + Entity() = default; ~Entity() = default; void set_id(SupaRL::Entity id) { id_ = id; }; - SupaRL::Entity get_id() { return id_; }; + SupaRL::Entity get_id() const { return id_; }; AttackComponent& get_attack_component() { return *attackComponent_; }; DefenseComponent& get_defense_component() { return *defenseComponent_; }; @@ -69,12 +64,8 @@ namespace cpprl { Container& get_container() { return *container_; }; float get_distance_to(Entity* other) const; - bool is_blocking() const { return blocker_; }; - std::string get_name() const { return name_; }; void update(World& world); - void set_blocking(bool blocker) { blocker_ = blocker; }; - void set_name(std::string_view name) { name_ = name; }; void set_defense_component( std::unique_ptr defenseComponent); void set_attack_component(std::unique_ptr attackComponent); @@ -86,7 +77,6 @@ namespace cpprl { template void pack(Archive& archive) { - archive(name_, blocker_); archive(defenseComponent_ != nullptr); archive(attackComponent_ != nullptr); archive(consumableComponent_ != nullptr); @@ -111,7 +101,6 @@ namespace cpprl { bool hasContainer; bool hasStatsComponent; - archive(name_, blocker_); archive(hasDefenseComponent); archive(hasAttackComponent); archive(hasConsumableComponent); diff --git a/app/include/world.hpp b/app/include/world.hpp index 2039546..04b7e48 100644 --- a/app/include/world.hpp +++ b/app/include/world.hpp @@ -75,7 +75,7 @@ namespace cpprl { } } archive(entities_); - player_ = new Entity("", false); + player_ = new Entity(); // TODO: If player is confused, quitting and reopening the game removes the // confused state player_->unpack(archive); diff --git a/app/src/components.cpp b/app/src/components.cpp index 5338dbf..3caebf0 100644 --- a/app/src/components.cpp +++ b/app/src/components.cpp @@ -12,6 +12,8 @@ #include "game_entity.hpp" #include "state.hpp" #include "world.hpp" +#include +#include extern SupaRL::Coordinator g_coordinator; @@ -33,16 +35,20 @@ namespace cpprl { } void DefenseComponent::die(Entity& the_deceased) const { - the_deceased.set_name("Corpse of " + the_deceased.get_name()); + auto& identity_comp = g_coordinator.get_component( + the_deceased.get_id()); + identity_comp.name_ = "Corpse of " + identity_comp.name_; auto& ascii_comp = g_coordinator.get_component( the_deceased.get_id()); - ascii_comp.symbol_ = "%"; ascii_comp.colour_ = SupaRL::ColorRGB{RED.r, RED.g, RED.b}; ascii_comp.layer_ = -1; - the_deceased.set_blocking(false); + auto& physique_component = g_coordinator.get_component( + the_deceased.get_id()); + + physique_component.is_blocking_ = false; the_deceased.set_ai_component(nullptr); } @@ -135,12 +141,14 @@ namespace cpprl { int inflicted = combat_system::handle_spell(damage_, closest_living_monster); ConsumableComponent::use(owner, wearer, world); + auto& entity_name = g_coordinator.get_component( + closest_living_monster.get_id()).name_; if (inflicted > 0) { world.get_message_log().add_message( fmt::format( "A lightning bolt strikes the {} with a loud " "thunder! The damage is {} hit points.", - closest_living_monster.get_name(), + entity_name, damage_), GREEN); @@ -152,7 +160,7 @@ namespace cpprl { } else { return Failure{fmt::format( "The lightning bolt hits the {} but does no damage.", - closest_living_monster.get_name())}; + entity_name)}; } } @@ -162,6 +170,8 @@ namespace cpprl { for (Entity* entity : world.get_entities()) { auto entity_position = g_coordinator.get_component( entity->get_id()).position_; + auto entity_name = g_coordinator.get_component( + entity->get_id()).name_; if (const auto* defense_component = &entity->get_defense_component(); defense_component && defense_component->is_not_dead() && entity_position.distance_to( @@ -169,7 +179,7 @@ namespace cpprl { world.get_message_log().add_message( fmt::format( "The {} gets burned for {} hit points.", - entity->get_name(), + entity_name, damage_), RED); int inflicted = combat_system::handle_spell(damage_, *entity); @@ -194,6 +204,8 @@ namespace cpprl { world.get_map().get_highlight_tile()); if (optional_ref_target.has_value()) { auto& target = optional_ref_target.value().get(); + auto& entity_name = g_coordinator.get_component( + target.get_id()).name_; std::unique_ptr old_ai = target.transfer_ai_component(); std::unique_ptr confusion_ai = @@ -203,7 +215,7 @@ namespace cpprl { fmt::format( "The eyes of the {} look vacant, as it starts to " "stumble around!", - target.get_name()), + entity_name), GREEN); ConsumableComponent::use(owner, wearer, world); return {}; diff --git a/app/src/consumable_factory.cpp b/app/src/consumable_factory.cpp index 725d94d..e5f955a 100644 --- a/app/src/consumable_factory.cpp +++ b/app/src/consumable_factory.cpp @@ -5,14 +5,14 @@ #include "components.hpp" #include #include +#include +#include extern SupaRL::Coordinator g_coordinator; namespace cpprl { Entity* AbstractConsumableFactory::create_base( std::string name, tcod::ColorRGB color, std::string symbol,SupaRL::Vector2D at_position) { - Entity* new_entity = new Entity( - name, - false); + Entity* new_entity = new Entity(); auto entity_id = g_coordinator.create_entity(); new_entity->set_id(entity_id); g_coordinator.add_component(entity_id, SupaRL::TransformComponent{ @@ -21,6 +21,10 @@ namespace cpprl { .symbol_ = symbol, .colour_ = SupaRL::ColorRGB{.r = color.r, .g = color.g, .b = color.b}, .layer_ = 1}); + g_coordinator.add_component(entity_id, SupaRL::IdentityComponent{ + .name_ = name}); + g_coordinator.add_component(entity_id, SupaRL::PhysiqueComponent{ + .is_blocking_ = false}); return new_entity; } diff --git a/app/src/engine.cpp b/app/src/engine.cpp index 50b25e1..970be63 100644 --- a/app/src/engine.cpp +++ b/app/src/engine.cpp @@ -24,6 +24,8 @@ #include "components/status_condition.hpp" #include "components/velocity.hpp" #include "components/ascii.hpp" +#include "components/physique.hpp" +#include "components/identity.hpp" #include "systems/status_condition_system.hpp" #include "systems/combat_system.hpp" #include "core/types.hpp" @@ -57,6 +59,8 @@ namespace cpprl { g_coordinator.register_component(); g_coordinator.register_component(); g_coordinator.register_component(); + g_coordinator.register_component(); + g_coordinator.register_component(); /*g_coordinator.register_system();*/ /*{*/ diff --git a/app/src/entity_factory.cpp b/app/src/entity_factory.cpp index 1763897..1bf33fb 100644 --- a/app/src/entity_factory.cpp +++ b/app/src/entity_factory.cpp @@ -9,6 +9,8 @@ #include "components/transform.hpp" #include "components/velocity.hpp" #include "components/ascii.hpp" +#include +#include extern SupaRL::Coordinator g_coordinator; @@ -16,9 +18,7 @@ namespace cpprl { Entity* AbstractEntityFactory::create_base( const std::string& name, tcod::ColorRGB color, std::string symbol) { - auto entity = new Entity( - name, - true); + auto entity = new Entity(); SupaRL::Entity entity_id = g_coordinator.create_entity(); entity->set_id(entity_id); g_coordinator.add_component(entity_id, SupaRL::AsciiComponent{ @@ -26,6 +26,10 @@ namespace cpprl { .colour_ = SupaRL::ColorRGB{.r = color.r, .g = color.g, .b = color.b}, .layer_ = 1}); + g_coordinator.add_component(entity_id, SupaRL::IdentityComponent{ + .name_ = name}); + g_coordinator.add_component(entity_id, SupaRL::PhysiqueComponent{ + .is_blocking_ = true}); return entity; } diff --git a/app/src/entity_manager.cpp b/app/src/entity_manager.cpp index 4a5c6c8..c03aed2 100644 --- a/app/src/entity_manager.cpp +++ b/app/src/entity_manager.cpp @@ -6,12 +6,12 @@ #include #include "basic_ai_component.hpp" -#include "colours.hpp" -#include "components.hpp" #include "consumable_factory.hpp" #include "util.hpp" -#include "core/coordinator.hpp" +#include +#include +#include extern SupaRL::Coordinator g_coordinator; namespace cpprl { @@ -21,7 +21,10 @@ namespace cpprl { entities_.erase( std::begin(std::ranges::remove_if( entities_, - [](const Entity* entity) { return entity->get_name() != "Player"; })), + [](const Entity* entity) { + auto& entity_name = g_coordinator.get_component( + entity->get_id()).name_; + return entity_name != "Player"; })), entities_.end()); } @@ -110,9 +113,11 @@ namespace cpprl { std::optional> EntityManager::get_blocking_entity_at(SupaRL::Vector2D position) { for (const auto& entity : entities_) { - auto entity_position = g_coordinator.get_component( + auto& entity_position = g_coordinator.get_component( entity->get_id()).position_; - if (entity->is_blocking() && + auto entity_is_blocking = g_coordinator.get_component( + entity->get_id()).is_blocking_; + if (entity_is_blocking && entity_position == position) { return std::reference_wrapper(*entity); } @@ -123,9 +128,11 @@ namespace cpprl { std::optional> EntityManager::get_non_blocking_entity_at(SupaRL::Vector2D position) { for (const auto& entity : entities_) { - auto entity_position = g_coordinator.get_component( + auto& entity_position = g_coordinator.get_component( entity->get_id()).position_; - if (!entity->is_blocking() && + auto entity_is_not_blocking = !g_coordinator.get_component( + entity->get_id()).is_blocking_; + if (entity_is_not_blocking && entity_position == position) { return std::reference_wrapper(*entity); } diff --git a/app/src/events/command.cpp b/app/src/events/command.cpp index 6392d00..4fc3a19 100644 --- a/app/src/events/command.cpp +++ b/app/src/events/command.cpp @@ -11,6 +11,7 @@ #include "world.hpp" #include "core/coordinator.hpp" #include "components/velocity.hpp" +#include extern SupaRL::Coordinator g_coordinator; namespace cpprl { @@ -26,9 +27,11 @@ namespace cpprl { } Entity& item = optional_item_ref.value().get(); + auto& item_name = g_coordinator.get_component( + item.get_id()).name_; world_.get_message_log().add_message( - "You pick up the " + item.get_name() + ".", WHITE); + "You pick up the " + item_name + ".", WHITE); entity_->get_container().add(&item); world_.get_entities().remove(&item); @@ -116,11 +119,13 @@ namespace cpprl { } StateResult DieEvent::execute() { + auto& entity_name = g_coordinator.get_component( + entity_->get_id()).name_; world_.get_message_log().add_message( - fmt::format("{} has died!", util::capitalize(entity_->get_name()))); + fmt::format("{} has died!", util::capitalize(entity_name))); entity_->get_defense_component().die(*entity_); - if (entity_->get_name() != "player") { + if (entity_name != "player") { const std::optional> stats_component = world_.get_player()->get_stats_component(); assert(stats_component.has_value()); @@ -167,23 +172,27 @@ namespace cpprl { StateResult MeleeCommand::execute() { auto entity_position = g_coordinator.get_component( entity_->get_id()).position_; + auto entity_name = g_coordinator.get_component( + entity_->get_id()).name_; auto targetPos = entity_position + move_vector_; std::optional> target = world_.get_entities().get_blocking_entity_at(targetPos); tcod::ColorRGB attack_colour = WHITE; - if (entity_->get_name() != "player") { + if (entity_name != "player") { attack_colour = RED; } if (target.has_value()) { int damage = combat_system::handle_attack(*entity_, target.value().get()); + auto& target_name = g_coordinator.get_component( + target.value().get().get_id()).name_; if (damage > 0) { std::string message = fmt::format( "{} attacks {} for {} hit points.", - util::capitalize(entity_->get_name()), - util::capitalize(target.value().get().get_name()), + util::capitalize(entity_name), + util::capitalize(target_name), damage); world_.get_message_log().add_message(message, attack_colour, true); @@ -195,8 +204,8 @@ namespace cpprl { } else { std::string message = fmt::format( "{} attacks {} but does no damage.", - util::capitalize(entity_->get_name()), - util::capitalize(target.value().get().get_name())); + util::capitalize(entity_name), + util::capitalize(target_name)); world_.get_message_log().add_message(message, attack_colour, true); } diff --git a/app/src/game_entity.cpp b/app/src/game_entity.cpp index b948303..c921b0a 100644 --- a/app/src/game_entity.cpp +++ b/app/src/game_entity.cpp @@ -8,11 +8,6 @@ namespace cpprl { - Entity::Entity( - std::string const& name, - bool blocker) - : name_(name), - blocker_(blocker) {} void Entity::update(World& world) { aiComponent_->update(world, this); } diff --git a/app/src/gui.cpp b/app/src/gui.cpp index a00a2c6..fe1032a 100644 --- a/app/src/gui.cpp +++ b/app/src/gui.cpp @@ -5,6 +5,7 @@ #include "colours.hpp" #include "components.hpp" #include "game_entity.hpp" +#include namespace cpprl { @@ -92,10 +93,12 @@ void InventoryWindow::render(tcod::Console& parent_console) { } else { tcod::print(*console_, {1, cursor_}, ">", WHITE, std::nullopt, TCOD_LEFT); for (auto it = items.begin(); it != items.end(); ++it) { + auto& entity_name = g_coordinator.get_component( + (*it)->get_id()).name_; tcod::print_rect( *console_, {2, y_offset, console_->getWidth() - 1, 1}, - (*it)->get_name(), + entity_name, WHITE, std::nullopt, TCOD_LEFT); diff --git a/app/src/world.cpp b/app/src/world.cpp index e1811b9..8960e49 100644 --- a/app/src/world.cpp +++ b/app/src/world.cpp @@ -8,7 +8,8 @@ #include "entity_manager.hpp" #include "game_entity.hpp" #include "core/coordinator.hpp" -#include "components/transform.hpp" +#include +#include extern SupaRL::Coordinator g_coordinator; @@ -75,7 +76,9 @@ namespace cpprl { for (std::reference_wrapper entity_reference_wrapper : entities_at) { const Entity& entity = entity_reference_wrapper.get(); - names += entity.get_name() + ", "; + auto& entity_name = g_coordinator.get_component( + entity.get_id()).name_; + names += entity_name + ", "; tcod::print_rect( g_console, {controller_->cursor.x, controller_->cursor.y - 1, 20, 1}, diff --git a/lib/include/components/identity.hpp b/lib/include/components/identity.hpp new file mode 100644 index 0000000..f518148 --- /dev/null +++ b/lib/include/components/identity.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace SupaRL { + struct IdentityComponent { + std::string name_; + }; +} diff --git a/lib/include/components/physique.hpp b/lib/include/components/physique.hpp new file mode 100644 index 0000000..7e68acc --- /dev/null +++ b/lib/include/components/physique.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace SupaRL { + struct PhysiqueComponent { + bool is_blocking_; + }; +} From bef854df549967c241576b58ed9d42a1d81845a2 Mon Sep 17 00:00:00 2001 From: daniel baker Date: Fri, 11 Oct 2024 11:17:31 +0100 Subject: [PATCH 11/38] feat: formatting --- app/src/entity_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/entity_manager.cpp b/app/src/entity_manager.cpp index c03aed2..4ba4994 100644 --- a/app/src/entity_manager.cpp +++ b/app/src/entity_manager.cpp @@ -130,7 +130,7 @@ namespace cpprl { for (const auto& entity : entities_) { auto& entity_position = g_coordinator.get_component( entity->get_id()).position_; - auto entity_is_not_blocking = !g_coordinator.get_component( + auto entity_is_not_blocking = !g_coordinator.get_component( entity->get_id()).is_blocking_; if (entity_is_not_blocking && entity_position == position) { From c5d675425b717985ba8e2732ad2ccd0e535c7048 Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Sat, 12 Oct 2024 17:43:28 +0100 Subject: [PATCH 12/38] feat: remove old file --- CMakeLists.txt.old | 78 ---------------------------------------------- 1 file changed, 78 deletions(-) delete mode 100644 CMakeLists.txt.old diff --git a/CMakeLists.txt.old b/CMakeLists.txt.old deleted file mode 100644 index eeec2d9..0000000 --- a/CMakeLists.txt.old +++ /dev/null @@ -1,78 +0,0 @@ -cmake_minimum_required (VERSION 3.13...3.21) - -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) - set(CMAKE_TOOLCHAIN_FILE - "${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake" - CACHE STRING "Vcpkg toolchain file") -endif() - -project( - roguelike - LANGUAGES C CXX -) - -# Static analysis. -# set(CMAKE_CXX_CLANG_TIDY "clang-tidy;-checks=*") -set(CMAKE_CXX_ANALYZE_ON_COMPILE True) - -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") # Keep all runtime files in one directory. -set(CMAKE_EXPORT_COMPILE_COMMANDS 1) # Export compile commands for use with clangd. - -file( - GLOB_RECURSE SOURCE_FILES - CONFIGURE_DEPENDS # Automatically reconfigure if source files are added/removed. - ${PROJECT_SOURCE_DIR}/src/*.cpp - ${PROJECT_SOURCE_DIR}/src/*.hpp - ${PROJECT_SOURCE_DIR}/src/*.h -) -add_executable(${PROJECT_NAME} ${SOURCE_FILES}) -target_include_directories(${PROJECT_NAME} - PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include -) - - -# Ensure the C++20 standard is available. -target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20) - -# Enforce UTF-8 encoding on MSVC. -if (MSVC) - target_compile_options(${PROJECT_NAME} PRIVATE /utf-8) -endif() - -# Enable warnings recommended for new projects. -if (MSVC) - target_compile_options(${PROJECT_NAME} PRIVATE /W4) -else() - target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra) -endif() - -if (EMSCRIPTEN) - # Attach data folder to Emscripten builds. - target_link_options(${PROJECT_NAME} PRIVATE --preload-file "${CMAKE_CURRENT_SOURCE_DIR}/data@data" -fexceptions -lidbfs.js) - target_compile_options(${PROJECT_NAME} PRIVATE -fexceptions -std=c++20) - # Set output to html to generate preview pages, otherwise you'll have to make your own html. - configure_file( - ${PROJECT_SOURCE_DIR}/emscripten/index.html - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/index.html - ) - # set_target_properties( - # ${PROJECT_NAME} - # PROPERTIES - # SUFFIX ".html" - # ) -endif() - - -find_package(fmt CONFIG REQUIRED) -find_package(SDL2 CONFIG REQUIRED) -find_package(libtcod CONFIG REQUIRED) -find_package(cereal CONFIG REQUIRED) -target_link_libraries( - ${PROJECT_NAME} - PRIVATE - SDL2::SDL2 - SDL2::SDL2main - libtcod::libtcod - cereal::cereal - fmt::fmt -) From da9f5c8afc986f62eb7c44e46279cb30ab7e28d3 Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Sat, 12 Oct 2024 18:11:59 +0100 Subject: [PATCH 13/38] fix: hover text --- app/include/controller.hpp | 1 - app/include/input_handler.hpp | 1 + app/include/world.hpp | 1 + app/src/events/command.cpp | 3 +++ app/src/game_entity.cpp | 3 --- app/src/input_handler.cpp | 9 ++++----- app/src/world.cpp | 2 ++ 7 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/include/controller.hpp b/app/include/controller.hpp index 0876fa1..c9b1ed0 100644 --- a/app/include/controller.hpp +++ b/app/include/controller.hpp @@ -6,6 +6,5 @@ namespace cpprl { struct Controller { SupaRL::Vector2D cursor = {0, 0}; }; - } diff --git a/app/include/input_handler.hpp b/app/include/input_handler.hpp index c9ba3c5..5ccfd1f 100644 --- a/app/include/input_handler.hpp +++ b/app/include/input_handler.hpp @@ -59,6 +59,7 @@ namespace cpprl { std::unique_ptr main_menu_command_; std::unique_ptr use_command_; std::unique_ptr character_menu_command_; + std::unique_ptr mouse_input_event_; Entity* controllable_entity_; public: diff --git a/app/include/world.hpp b/app/include/world.hpp index 04b7e48..efb35c7 100644 --- a/app/include/world.hpp +++ b/app/include/world.hpp @@ -45,6 +45,7 @@ namespace cpprl { Entity* get_player() const { return player_; } void spawn_player(); void spawn_player(Entity* player); + Controller& get_controller() { return *controller_; } template void save(Archive& archive) const { diff --git a/app/src/events/command.cpp b/app/src/events/command.cpp index 4fc3a19..069f658 100644 --- a/app/src/events/command.cpp +++ b/app/src/events/command.cpp @@ -8,6 +8,7 @@ #include "gui.hpp" #include "input_handler.hpp" #include "state.hpp" +#include "controller.hpp" #include "world.hpp" #include "core/coordinator.hpp" #include "components/velocity.hpp" @@ -158,6 +159,8 @@ namespace cpprl { StateResult MouseInputEvent::execute() { world_.get_map().set_highlight_tile(position_); + std::cout << "position_: " << position_.x << ", " << position_.y << std::endl; + world_.get_controller().cursor = position_; return {}; } diff --git a/app/src/game_entity.cpp b/app/src/game_entity.cpp index c921b0a..9a225db 100644 --- a/app/src/game_entity.cpp +++ b/app/src/game_entity.cpp @@ -7,8 +7,6 @@ #include "world.hpp" namespace cpprl { - - void Entity::update(World& world) { aiComponent_->update(world, this); } void Entity::set_attack_component( @@ -42,5 +40,4 @@ namespace cpprl { std::unique_ptr Entity::transfer_ai_component() { return std::move(aiComponent_); }; - } diff --git a/app/src/input_handler.cpp b/app/src/input_handler.cpp index 6329481..8ebc05d 100644 --- a/app/src/input_handler.cpp +++ b/app/src/input_handler.cpp @@ -82,13 +82,12 @@ GameInputHandler::GameInputHandler(World& world, Entity* controllable_entity) controllable_entity_(controllable_entity){}; EngineEvent& GameInputHandler::handle_sdl_event(SDL_Event event) noexcept { - // TODO: Move this to its own handler. - // probably want an event handler which has - // input handler for keyboard and another for mouse if (event.type == SDL_MOUSEMOTION) { g_context.convert_event_coordinates(event); - world_.get_map().set_highlight_tile({event.motion.x, event.motion.y}); - return *noop_; + mouse_input_event_ = std::make_unique( + world_, SupaRL::Vector2D{event.motion.x, event.motion.y}); + /*world_.get_map().set_highlight_tile({event.motion.x, event.motion.y});*/ + return *mouse_input_event_; } SDL_Keycode key = event.key.keysym.sym; diff --git a/app/src/world.cpp b/app/src/world.cpp index 8960e49..897c519 100644 --- a/app/src/world.cpp +++ b/app/src/world.cpp @@ -32,6 +32,7 @@ namespace cpprl { message_log_.add_message("Press 'v' to view your adventure log", WHITE); message_log_.add_message("Now, fly you fool!", WHITE); } + void World::generate_map(int width, int height, bool with_entities) { // TODO: will need to pass the seed here dungeon_.generate(DungeonConfig{30, 6, 10, width, height, 2}); @@ -70,6 +71,7 @@ namespace cpprl { message_log_.render(g_console, 23, 35, 45, 5); // TODO: this is not working since cursor is not being set + std::cout << "cursor: " << controller_->cursor.x << ", " << controller_->cursor.y << std::endl; auto entities_at = entities_->get_entities_at(controller_->cursor); if (!entities_at.empty()) { std::string names; From 9b8d70df513ac048f554459aa158e3879b5f050e Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Mon, 14 Oct 2024 20:29:41 +0100 Subject: [PATCH 14/38] feat:(wip): building systems --- app/include/engine.hpp | 78 ++++++++++----------- app/src/engine.cpp | 15 ++-- lib/include/components/status_condition.hpp | 3 +- lib/src/systems/status_condition_system.cpp | 6 +- 4 files changed, 49 insertions(+), 53 deletions(-) diff --git a/app/include/engine.hpp b/app/include/engine.hpp index 9e32b46..570ac8b 100644 --- a/app/include/engine.hpp +++ b/app/include/engine.hpp @@ -1,5 +1,4 @@ -#ifndef ENGINE_HPP -#define ENGINE_HPP +#pragma once #include #include @@ -13,43 +12,44 @@ #include "message_log.hpp" #include "rendering.hpp" #include "systems/physics_system.hpp" +#include "systems/status_condition_system.hpp" namespace cpprl { -class EventHandler; -class GameInputHandler; -class GameActor; -class Map; -class State; - -class Engine { - private: - std::unique_ptr renderer_; - tcod::Context context_; - std::unique_ptr world_; - std::unique_ptr engine_state_; - std::shared_ptr physics_system_; - - int argc_; - char** argv_; - - void generate_map(int width, int height); - void handle_enemy_turns(); - - public: - Engine(); - Engine(const Engine&) = delete; - Engine& operator=(const Engine&) = delete; - ~Engine(); - - static Engine& get_instance(); - void handle_events(); - void render(); - void handle_player_death(); - void reset_game(); - void load(); - void save(); - void init(int argc, char** argv); -}; -} // namespace cpprl -#endif + class EventHandler; + class GameInputHandler; + class GameActor; + class Map; + class State; + + class Engine { + private: + std::unique_ptr renderer_; + tcod::Context context_; + std::unique_ptr world_; + std::unique_ptr engine_state_; + std::shared_ptr physics_system_; + std::shared_ptr status_condition_system_; + + int argc_; + char** argv_; + + void generate_map(int width, int height); + void handle_enemy_turns(); + + public: + Engine(); + Engine(const Engine&) = delete; + Engine& operator=(const Engine&) = delete; + ~Engine(); + + static Engine& get_instance(); + void handle_events(); + void render(); + void handle_player_death(); + void reset_game(); + void load(); + void save(); + void init(int argc, char** argv); + }; +} diff --git a/app/src/engine.cpp b/app/src/engine.cpp index 970be63..bba7367 100644 --- a/app/src/engine.cpp +++ b/app/src/engine.cpp @@ -62,13 +62,13 @@ namespace cpprl { g_coordinator.register_component(); g_coordinator.register_component(); - /*g_coordinator.register_system();*/ - /*{*/ - /* SupaRL::Signature signature;*/ - /* signature.set(g_coordinator.get_component_type());*/ - /* signature.set(g_coordinator.get_component_type());*/ - /* g_coordinator.set_system_signature(signature);*/ - /*}*/ + status_condition_system_ = g_coordinator.register_system(); + { + SupaRL::Signature signature; + signature.set(g_coordinator.get_component_type()); + signature.set(g_coordinator.get_component_type()); + g_coordinator.set_system_signature(signature); + } /**/ /*g_coordinator.register_system();*/ /*{*/ @@ -153,6 +153,7 @@ namespace cpprl { } else if (std::holds_alternative(result)) { load(); } else if (std::holds_alternative(result)) { + status_condition_system_->update(); world_->handle_enemy_turns(); if (world_->get_player()->get_defense_component().is_dead()) { engine_state_->on_exit(); diff --git a/lib/include/components/status_condition.hpp b/lib/include/components/status_condition.hpp index cfbd5f3..24059c3 100644 --- a/lib/include/components/status_condition.hpp +++ b/lib/include/components/status_condition.hpp @@ -4,9 +4,8 @@ namespace SupaRL{ struct StatusConditionComponent { short damage_per_tick_; - short duration_; + short max_ticks_; short ticks_; std::string name_; }; - } diff --git a/lib/src/systems/status_condition_system.cpp b/lib/src/systems/status_condition_system.cpp index 164a10e..8e06e4f 100644 --- a/lib/src/systems/status_condition_system.cpp +++ b/lib/src/systems/status_condition_system.cpp @@ -20,13 +20,9 @@ namespace SupaRL auto& defence = g_coordinator.get_component(entity); status_condition.ticks_++; - if (status_condition.ticks_ % 10 == 0) + if (status_condition.ticks_ == status_condition.max_ticks_) { defence.hp_ -= status_condition.damage_per_tick_; - if (defence.hp_ <= 0) - { - // DIE. - } } } } From 3b76eb605ebb03d99d0c0497f3028ead097c8640 Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Mon, 14 Oct 2024 20:30:25 +0100 Subject: [PATCH 15/38] feat: remove log --- app/src/world.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/world.cpp b/app/src/world.cpp index 897c519..3a25201 100644 --- a/app/src/world.cpp +++ b/app/src/world.cpp @@ -71,7 +71,6 @@ namespace cpprl { message_log_.render(g_console, 23, 35, 45, 5); // TODO: this is not working since cursor is not being set - std::cout << "cursor: " << controller_->cursor.x << ", " << controller_->cursor.y << std::endl; auto entities_at = entities_->get_entities_at(controller_->cursor); if (!entities_at.empty()) { std::string names; From b058c994a87e709c8a31630171fafbdde9a361e6 Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Tue, 15 Oct 2024 20:47:14 +0100 Subject: [PATCH 16/38] feat(wip): Apply a status condition --- app/include/combat_system.hpp | 23 +++++++++++++++++++++ app/src/entity_factory.cpp | 6 ++++++ lib/src/systems/status_condition_system.cpp | 11 +++++++++- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/app/include/combat_system.hpp b/app/include/combat_system.hpp index 4ce1606..9b6bcc1 100644 --- a/app/include/combat_system.hpp +++ b/app/include/combat_system.hpp @@ -1,7 +1,13 @@ #pragma once +#include +#include #include "components.hpp" #include "game_entity.hpp" +#include "libtcod.hpp" +#include + +extern SupaRL::Coordinator g_coordinator; namespace cpprl::combat_system { inline auto handle_attack = [](Entity& attacker, Entity& target) -> int { @@ -9,6 +15,21 @@ namespace cpprl::combat_system { target.get_defense_component().get_defense(); if (damage > 0) { target.get_defense_component().take_damage(damage); + auto* rng = TCODRandom::getInstance(); + int rand = rng->getInt(0, 1); + std::cout << "Rand: " << rand << std::endl; + if (rand == 1) { + auto& status_condition = g_coordinator.get_component( + target.get_id()); + + status_condition.damage_per_tick_ = 1; + status_condition.max_ticks_ = 10; + status_condition.ticks_ = 0; + status_condition.name_ = "Bleeding"; + auto entity_name = g_coordinator.get_component( + target.get_id()).name_; + std::cout << entity_name << " is bleeding profusely!" << std::endl; + } return damage; } return 0; @@ -17,7 +38,9 @@ namespace cpprl::combat_system { inline auto handle_spell = [](int power, Entity& target) -> int { int damage = power - target.get_defense_component().get_defense(); if (damage > 0) { + target.get_defense_component().take_damage(damage); + return damage; } return 0; diff --git a/app/src/entity_factory.cpp b/app/src/entity_factory.cpp index 1bf33fb..37f1dde 100644 --- a/app/src/entity_factory.cpp +++ b/app/src/entity_factory.cpp @@ -11,6 +11,7 @@ #include "components/ascii.hpp" #include #include +#include extern SupaRL::Coordinator g_coordinator; @@ -30,6 +31,11 @@ namespace cpprl { .name_ = name}); g_coordinator.add_component(entity_id, SupaRL::PhysiqueComponent{ .is_blocking_ = true}); + g_coordinator.add_component(entity_id, SupaRL::StatusConditionComponent{ + .damage_per_tick_ = 0, + .max_ticks_ = 0, + .ticks_ = 0, + .name_ = "none"}); return entity; } diff --git a/lib/src/systems/status_condition_system.cpp b/lib/src/systems/status_condition_system.cpp index 8e06e4f..0d6429e 100644 --- a/lib/src/systems/status_condition_system.cpp +++ b/lib/src/systems/status_condition_system.cpp @@ -2,6 +2,7 @@ #include "core/coordinator.hpp" #include "components/defence.hpp" #include "components/status_condition.hpp" +#include extern SupaRL::Coordinator g_coordinator; @@ -19,10 +20,18 @@ namespace SupaRL auto& status_condition = g_coordinator.get_component(entity); auto& defence = g_coordinator.get_component(entity); + + std::cout << "Status condition: " << status_condition.name_ << std::endl; + + status_condition.ticks_++; - if (status_condition.ticks_ == status_condition.max_ticks_) + if (status_condition.ticks_ <= status_condition.max_ticks_) { + std::cout << "Ticks: " << status_condition.ticks_ << std::endl; + + std::cout << "HP: " << defence.hp_ << std::endl; defence.hp_ -= status_condition.damage_per_tick_; + std::cout << "HP: " << defence.hp_ << std::endl; } } } From d77635e732aba08bec386da76957f576937e4c82 Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Wed, 16 Oct 2024 19:49:01 +0100 Subject: [PATCH 17/38] feat(wip): move defence component --- app/include/game_entity.hpp | 10 ------- app/src/components.cpp | 17 ----------- app/src/entity_factory.cpp | 1 + app/src/events/command.cpp | 9 ++++-- lib/include/components/ascii.hpp | 10 ++----- lib/include/core/colour.hpp | 14 ++++++++++ lib/include/systems/death_system.hpp | 15 ++++++++++ lib/src/systems/death_system.cpp | 42 ++++++++++++++++++++++++++++ 8 files changed, 81 insertions(+), 37 deletions(-) create mode 100644 lib/include/core/colour.hpp create mode 100644 lib/include/systems/death_system.hpp create mode 100644 lib/src/systems/death_system.cpp diff --git a/app/include/game_entity.hpp b/app/include/game_entity.hpp index 45abda4..4e2d9eb 100644 --- a/app/include/game_entity.hpp +++ b/app/include/game_entity.hpp @@ -16,7 +16,6 @@ namespace cpprl { class TransformComponent; class AttackComponent; - class DefenseComponent; class ConsumableComponent; class StatsComponent; class Container; @@ -26,7 +25,6 @@ namespace cpprl { SupaRL::Entity id_; std::unique_ptr attackComponent_; - std::unique_ptr defenseComponent_; std::unique_ptr consumableComponent_; std::unique_ptr aiComponent_; std::unique_ptr container_; @@ -40,7 +38,6 @@ namespace cpprl { SupaRL::Entity get_id() const { return id_; }; AttackComponent& get_attack_component() { return *attackComponent_; }; - DefenseComponent& get_defense_component() { return *defenseComponent_; }; ConsumableComponent& get_consumable_component() { return *consumableComponent_; }; @@ -66,8 +63,6 @@ namespace cpprl { void update(World& world); - void set_defense_component( - std::unique_ptr defenseComponent); void set_attack_component(std::unique_ptr attackComponent); void set_consumable_component( std::unique_ptr consumableComponent); @@ -77,7 +72,6 @@ namespace cpprl { template void pack(Archive& archive) { - archive(defenseComponent_ != nullptr); archive(attackComponent_ != nullptr); archive(consumableComponent_ != nullptr); archive(aiComponent_ != nullptr); @@ -85,7 +79,6 @@ namespace cpprl { archive(statsComponent_ != nullptr); if (attackComponent_) archive(attackComponent_); - if (defenseComponent_) archive(defenseComponent_); if (consumableComponent_) archive(consumableComponent_); if (aiComponent_) archive(aiComponent_); if (container_) archive(container_); @@ -111,9 +104,6 @@ namespace cpprl { if (hasAttackComponent) { archive(attackComponent_); } - if (hasDefenseComponent) { - archive(defenseComponent_); - } if (hasConsumableComponent) { archive(consumableComponent_); } diff --git a/app/src/components.cpp b/app/src/components.cpp index 3caebf0..f8250dc 100644 --- a/app/src/components.cpp +++ b/app/src/components.cpp @@ -34,23 +34,6 @@ namespace cpprl { return healed; } - void DefenseComponent::die(Entity& the_deceased) const { - auto& identity_comp = g_coordinator.get_component( - the_deceased.get_id()); - identity_comp.name_ = "Corpse of " + identity_comp.name_; - - auto& ascii_comp = g_coordinator.get_component( - the_deceased.get_id()); - ascii_comp.symbol_ = "%"; - ascii_comp.colour_ = SupaRL::ColorRGB{RED.r, RED.g, RED.b}; - ascii_comp.layer_ = -1; - - auto& physique_component = g_coordinator.get_component( - the_deceased.get_id()); - - physique_component.is_blocking_ = false; - the_deceased.set_ai_component(nullptr); - } Container::Container(int size) : size_(size), inventory_({}) { inventory_.reserve(size); diff --git a/app/src/entity_factory.cpp b/app/src/entity_factory.cpp index 37f1dde..bc16cac 100644 --- a/app/src/entity_factory.cpp +++ b/app/src/entity_factory.cpp @@ -32,6 +32,7 @@ namespace cpprl { g_coordinator.add_component(entity_id, SupaRL::PhysiqueComponent{ .is_blocking_ = true}); g_coordinator.add_component(entity_id, SupaRL::StatusConditionComponent{ + // Should this be an array of status conditions? .damage_per_tick_ = 0, .max_ticks_ = 0, .ticks_ = 0, diff --git a/app/src/events/command.cpp b/app/src/events/command.cpp index 069f658..8993625 100644 --- a/app/src/events/command.cpp +++ b/app/src/events/command.cpp @@ -11,8 +11,9 @@ #include "controller.hpp" #include "world.hpp" #include "core/coordinator.hpp" -#include "components/velocity.hpp" +#include #include +#include extern SupaRL::Coordinator g_coordinator; namespace cpprl { @@ -124,7 +125,7 @@ namespace cpprl { entity_->get_id()).name_; world_.get_message_log().add_message( fmt::format("{} has died!", util::capitalize(entity_name))); - entity_->get_defense_component().die(*entity_); + /*entity_->get_defense_component().die(*entity_);*/ if (entity_name != "player") { const std::optional> @@ -299,7 +300,9 @@ namespace cpprl { if (cursor == 3) { player->get_attack_component().boost_damage(1); } else if (cursor == 4) { - player->get_defense_component().boost_defense(1); + auto& defence_component = g_coordiator.get_component( + player->get_id()); + defence_component.defense_ += 1; } stats.reduce_stats_points(1); return EndTurn{}; diff --git a/lib/include/components/ascii.hpp b/lib/include/components/ascii.hpp index dd3efb7..c63b91a 100644 --- a/lib/include/components/ascii.hpp +++ b/lib/include/components/ascii.hpp @@ -2,16 +2,12 @@ #include -namespace SupaRL{ - struct ColorRGB { - uint8_t r; - uint8_t g; - uint8_t b; - }; +#include "core/colour.hpp" +namespace SupaRL{ struct AsciiComponent { std::string symbol_; - ColorRGB colour_; + ColourRGB colour_; int layer_; }; } diff --git a/lib/include/core/colour.hpp b/lib/include/core/colour.hpp new file mode 100644 index 0000000..23a722f --- /dev/null +++ b/lib/include/core/colour.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace SupaRL { + struct ColourRGB { + int r; + int g; + int b; + }; + + static constexpr ColourRGB WHITE = {255, 255, 255}; + static constexpr ColourRGB GREEN = {0, 255, 0}; + static constexpr ColourRGB BLUE = {0, 0, 255}; + static constexpr ColourRGB RED = {255, 0, 0}; +} diff --git a/lib/include/systems/death_system.hpp b/lib/include/systems/death_system.hpp new file mode 100644 index 0000000..ea8f5e8 --- /dev/null +++ b/lib/include/systems/death_system.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "core/system.hpp" + +namespace SupaRL { + + class DeathSystem : public System + { + public: + DeathSystem() = default; + ~DeathSystem() = default; + + void update(); + }; +} diff --git a/lib/src/systems/death_system.cpp b/lib/src/systems/death_system.cpp new file mode 100644 index 0000000..fdff0db --- /dev/null +++ b/lib/src/systems/death_system.cpp @@ -0,0 +1,42 @@ +#include "systems/death_system.hpp" + +#include "core/coordinator.hpp" +#include "core/colour.hpp" +#include "components/identity.hpp" +#include "components/physique.hpp" +#include "components/ascii.hpp" +#include "components/defence.hpp" + +extern SupaRL::Coordinator g_coordinator; + +namespace SupaRL { + + void DeathSystem::update() { + for(auto& entity : entities_) { + auto& defence_component = g_coordinator.get_component( + entity); + + if(defence_component.hp_ > 0) { + continue; + } + + auto& identity_comp = g_coordinator.get_component( + entity); + identity_comp.name_ = "Corpse of " + identity_comp.name_; + + auto& ascii_comp = g_coordinator.get_component( + entity); + ascii_comp.symbol_ = "%"; + ascii_comp.colour_ = RED; + ascii_comp.layer_ = -1; + + auto& physique_component = g_coordinator.get_component( + entity); + + physique_component.is_blocking_ = false; + // TODO: Move the AI component to ECS and remove it here + /*g_coordinator.remove_component(entity);*/ + } + } +} + From 9d634803c759b9c1215194fbd8117cbddeac757b Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Wed, 16 Oct 2024 20:30:06 +0100 Subject: [PATCH 18/38] feat(wip): messenger system --- app/include/combat_system.hpp | 34 ++++++++---------------- lib/include/core/event.hpp | 4 +-- lib/include/core/types.hpp | 37 +++++++++++++++++++++++++++ lib/include/systems/combat_system.hpp | 33 +++++++++++++++++++----- lib/src/systems/combat_system.cpp | 12 --------- 5 files changed, 76 insertions(+), 44 deletions(-) delete mode 100644 lib/src/systems/combat_system.cpp diff --git a/app/include/combat_system.hpp b/app/include/combat_system.hpp index 9b6bcc1..5695cd6 100644 --- a/app/include/combat_system.hpp +++ b/app/include/combat_system.hpp @@ -3,39 +3,27 @@ #include #include #include "components.hpp" +#include +#include #include "game_entity.hpp" #include "libtcod.hpp" -#include extern SupaRL::Coordinator g_coordinator; namespace cpprl::combat_system { inline auto handle_attack = [](Entity& attacker, Entity& target) -> int { - int damage = attacker.get_attack_component().get_damage() - - target.get_defense_component().get_defense(); - if (damage > 0) { - target.get_defense_component().take_damage(damage); - auto* rng = TCODRandom::getInstance(); - int rand = rng->getInt(0, 1); - std::cout << "Rand: " << rand << std::endl; - if (rand == 1) { - auto& status_condition = g_coordinator.get_component( - target.get_id()); - - status_condition.damage_per_tick_ = 1; - status_condition.max_ticks_ = 10; - status_condition.ticks_ = 0; - status_condition.name_ = "Bleeding"; - auto entity_name = g_coordinator.get_component( - target.get_id()).name_; - std::cout << entity_name << " is bleeding profusely!" << std::endl; - } - return damage; - } - return 0; + auto attack_event = SupaRL::Event(SupaRL::Events::Combat::ATTACK); + attack_event.set_param<>(SupaRL::Events::Combat::Attack::ATTACKER, attacker.get_id()); + attack_event.set_param<>(SupaRL::Events::Combat::Attack::DEFENDER, target.get_id()); + g_coordinator.send_event(attack_event); }; inline auto handle_spell = [](int power, Entity& target) -> int { + auto spell_event = SupaRL::Event(SupaRL::Events::Combat::SPELL); + spell_event.set_param<>(SupaRL::Events::Combat::Spell::TARGET, target.get_id()); + spell_event.set_param<>(SupaRL::Events::Combat::Spell::POWER, power); + g_coordinator.send_event(spell_event); + int damage = power - target.get_defense_component().get_defense(); if (damage > 0) { diff --git a/lib/include/core/event.hpp b/lib/include/core/event.hpp index fc2dc75..da2c110 100644 --- a/lib/include/core/event.hpp +++ b/lib/include/core/event.hpp @@ -16,13 +16,13 @@ namespace SupaRL { {} template - void set_param(EventId id, T value) + void set_param(ParamId id, T value) { data_[id] = value; } template - T get_param(EventId id) + T get_param(ParamId id) { return std::any_cast(data_[id]); } diff --git a/lib/include/core/types.hpp b/lib/include/core/types.hpp index 294929b..91c164b 100644 --- a/lib/include/core/types.hpp +++ b/lib/include/core/types.hpp @@ -4,6 +4,24 @@ #include namespace SupaRL { + + //TODO: Create an inline function to handle combat and register it as an event listener + // see: https://code.austinmorlan.com/austin/2019-ecs/src/branch/master/Source/Main.cpp#L37 + // or method: https://code.austinmorlan.com/austin/2019-ecs/src/branch/master/Source/Systems/RenderSystem.cpp#L16 +#define METHOD_LISTENER(EventType, Listener) EventType, std::bind(&Listener, this, std::placeholders::_1) +#define FUNCTION_LISTENER(EventType, Listener) EventType, std::bind(&Listener, std::placeholders::_1) + + // Source: https://gist.github.com/Lee-R/3839813 + constexpr std::uint32_t fnv1a_32(char const* s, std::size_t count) + { + return ((count ? fnv1a_32(s, count - 1) : 2166136261u) ^ s[count]) * 16777619u; // NOLINT (hicpp-signed-bitwise) + } + + constexpr std::uint32_t operator "" _hash(char const* s, std::size_t count) + { + return fnv1a_32(s, count); + } + using Entity = std::uint32_t; const Entity MAX_ENTITIES = 5000; using ComponentType = std::uint8_t; @@ -12,5 +30,24 @@ namespace SupaRL { // Events using EventId = std::uint32_t; + using ParamId = std::uint32_t; + + // TODO: Make these easier to define and use (macro?) + // TODO: Add some kind of enforcement/automation that a SetParam type and a GetParam type match + + namespace Events::Combat { + const EventId ATTACK = "Events::Combat::ATTACK"_hash; + const EventID SPELL = "Events::Combat::SPELL"_hash; + } + + namespace Events::Combat::Attack{ + const ParamId ATTACKER = "Events::Combat::Attack::Attacker"_hash; + const ParamId DEFENDER = "Events::Combat::Attack::Defender"_hash; + } + + namespace Events::Combat::Spell{ + const ParamId TARGET = "Events::Combat::Spell::Target"_hash; + const ParamId POWER = "Events::Combat::Spell::Power"_hash; + } } diff --git a/lib/include/systems/combat_system.hpp b/lib/include/systems/combat_system.hpp index f66410f..bcb9d28 100644 --- a/lib/include/systems/combat_system.hpp +++ b/lib/include/systems/combat_system.hpp @@ -1,16 +1,35 @@ #pragma once #include "core/system.hpp" +#include "core/coordinator.hpp" +#include "core/types.hpp" +#include "core/event.hpp" +extern SupaRL::Coordinator g_coordinator; namespace SupaRL { - class CombatSystem : public System - { - public: - CombatSystem() = default; - ~CombatSystem() = default; + inline auto attack_event_listener = [](Event& event) -> void { + Entity& attacker event.get_param(SupaRL::Events::Combat::Attack::ATTACKER); + Entity& target = event.get_param(SupaRL::Events::Combat::Attack::DEFENDER); + int damage = attacker.get_attack_component().get_damage() - + target.get_defense_component().get_defense(); + if (damage > 0) { + target.get_defense_component().take_damage(damage); + auto* rng = TCODRandom::getInstance(); + int rand = rng->getInt(0, 1); + if (rand == 1) { + auto& status_condition = g_coordinator.get_component( + target.get_id()); - void init(); - void update(); + status_condition.damage_per_tick_ = 1; + status_condition.max_ticks_ = 10; + status_condition.ticks_ = 0; + status_condition.name_ = "Bleeding"; + auto entity_name = g_coordinator.get_component( + target.get_id()).name_; + } + // TODO: Fire combat resolved event with damage dealt + } + // TODO: Fire combat resolved event with damage dealt (0) }; } diff --git a/lib/src/systems/combat_system.cpp b/lib/src/systems/combat_system.cpp deleted file mode 100644 index 7254208..0000000 --- a/lib/src/systems/combat_system.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "systems/combat_system.hpp" - -namespace SupaRL -{ - void CombatSystem::init() - { - } - - void CombatSystem::update() - { - } -} From 8c2841115eb5771502e76b88c9e8efd859eacc54 Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Thu, 17 Oct 2024 20:32:05 +0100 Subject: [PATCH 19/38] feat(ecs): combat messages --- app/include/combat_system.hpp | 9 --- app/include/game_entity.hpp | 3 +- app/src/entity_factory.cpp | 14 ++--- app/src/game_entity.cpp | 10 --- lib/include/core/colour.hpp | 8 ++- lib/include/core/types.hpp | 29 +++++++-- lib/include/systems/combat_system.hpp | 91 +++++++++++++++++++++++---- 7 files changed, 116 insertions(+), 48 deletions(-) diff --git a/app/include/combat_system.hpp b/app/include/combat_system.hpp index 5695cd6..698ff4c 100644 --- a/app/include/combat_system.hpp +++ b/app/include/combat_system.hpp @@ -23,15 +23,6 @@ namespace cpprl::combat_system { spell_event.set_param<>(SupaRL::Events::Combat::Spell::TARGET, target.get_id()); spell_event.set_param<>(SupaRL::Events::Combat::Spell::POWER, power); g_coordinator.send_event(spell_event); - - int damage = power - target.get_defense_component().get_defense(); - if (damage > 0) { - - target.get_defense_component().take_damage(damage); - - return damage; - } - return 0; }; } diff --git a/app/include/game_entity.hpp b/app/include/game_entity.hpp index 4e2d9eb..d4e173f 100644 --- a/app/include/game_entity.hpp +++ b/app/include/game_entity.hpp @@ -35,9 +35,8 @@ namespace cpprl { ~Entity() = default; void set_id(SupaRL::Entity id) { id_ = id; }; - SupaRL::Entity get_id() const { return id_; }; + SupaRL::Entity get_id() const { return id_; }; - AttackComponent& get_attack_component() { return *attackComponent_; }; ConsumableComponent& get_consumable_component() { return *consumableComponent_; }; diff --git a/app/src/entity_factory.cpp b/app/src/entity_factory.cpp index bc16cac..1fc78d4 100644 --- a/app/src/entity_factory.cpp +++ b/app/src/entity_factory.cpp @@ -24,7 +24,7 @@ namespace cpprl { entity->set_id(entity_id); g_coordinator.add_component(entity_id, SupaRL::AsciiComponent{ .symbol_ = symbol, - .colour_ = SupaRL::ColorRGB{.r = color.r, .g = color.g, .b = color.b}, + .colour_ = SupaRL::ColourRGB{.r = color.r, .g = color.g, .b = color.b }, .layer_ = 1}); g_coordinator.add_component(entity_id, SupaRL::IdentityComponent{ @@ -44,12 +44,12 @@ namespace cpprl { Entity* entity = create_base("Orc", DARK_GREEN, "o"); auto entity_id = entity->get_id(); - entity->set_attack_component(std::make_unique(3)); - entity->set_defense_component(std::make_unique(0, 10)); entity->set_ai_component(std::make_unique()); entity->set_stats_component( std::make_unique(10, 1, 10, 10, 2)); + g_coordinator,add_component(entity_id, SupaRL::AttackComponent{ + .damage_ = 3}); g_coordinator.add_component(entity_id, SupaRL::DefenceComponent{ .defence_ = 0, .hp_ = 10, @@ -69,13 +69,14 @@ namespace cpprl { Entity* entity = create_base("Troll", DARK_GREEN, "T"); entity->set_attack_component(std::make_unique(4)); - entity->set_defense_component(std::make_unique(1, 16)); entity->set_ai_component(std::make_unique()); entity->set_stats_component( std::make_unique(20, 1, 10, 20, 2)); auto entity_id = entity->get_id(); + g_coordinator.add_component(entity_id, SupaRL::AttackComponent{ + .damage_ = 4}); g_coordinator.add_component(entity_id, SupaRL::DefenceComponent{ .defence_ = 1, .hp_ = 16, @@ -94,15 +95,14 @@ namespace cpprl { Entity* PlayerFactory::create(SupaRL::Vector2D at_position) { Entity* entity = create_base("Player", TEAL, "@"); - - entity->set_attack_component(std::make_unique(5)); - entity->set_defense_component(std::make_unique(2, 30)); entity->set_container(std::make_unique(26)); entity->set_stats_component( std::make_unique(0, 1, 150, 150, 2)); auto entity_id = entity->get_id(); + g_coordinator.add_component(entity_id, SupaRL::AttackComponent{ + .damage_ = 5}); g_coordinator.add_component(entity_id, SupaRL::DefenceComponent{ .defence_ = 2, .hp_ = 30, diff --git a/app/src/game_entity.cpp b/app/src/game_entity.cpp index 9a225db..14d918d 100644 --- a/app/src/game_entity.cpp +++ b/app/src/game_entity.cpp @@ -9,16 +9,6 @@ namespace cpprl { void Entity::update(World& world) { aiComponent_->update(world, this); } - void Entity::set_attack_component( - std::unique_ptr attackComponent) { - attackComponent_ = std::move(attackComponent); - }; - - void Entity::set_defense_component( - std::unique_ptr defenseComponent) { - defenseComponent_ = std::move(defenseComponent); - }; - void Entity::set_consumable_component( std::unique_ptr consumableComponent) { consumableComponent_ = std::move(consumableComponent); diff --git a/lib/include/core/colour.hpp b/lib/include/core/colour.hpp index 23a722f..3a697a8 100644 --- a/lib/include/core/colour.hpp +++ b/lib/include/core/colour.hpp @@ -1,10 +1,12 @@ #pragma once +#include + namespace SupaRL { struct ColourRGB { - int r; - int g; - int b; + uint8_t r; + uint8_t g; + uint8_t b; }; static constexpr ColourRGB WHITE = {255, 255, 255}; diff --git a/lib/include/core/types.hpp b/lib/include/core/types.hpp index 91c164b..8d02a48 100644 --- a/lib/include/core/types.hpp +++ b/lib/include/core/types.hpp @@ -34,20 +34,39 @@ namespace SupaRL { // TODO: Make these easier to define and use (macro?) // TODO: Add some kind of enforcement/automation that a SetParam type and a GetParam type match - namespace Events::Combat { const EventId ATTACK = "Events::Combat::ATTACK"_hash; - const EventID SPELL = "Events::Combat::SPELL"_hash; - } - - namespace Events::Combat::Attack{ + const EventId SPELL = "Events::Combat::SPELL"_hash; const ParamId ATTACKER = "Events::Combat::Attack::Attacker"_hash; const ParamId DEFENDER = "Events::Combat::Attack::Defender"_hash; } + namespace Events::Combat::Damage{ + const EventId DEALT = "Events::Combat::Damage::DEALT"_hash; + const ParamId DAMAGE = "Events::Combat::Damage::Damage"_hash; + const ParamId ATTACKER = "Events::Combat::Damage::Attacker"_hash; + const ParamId DEFENDER = "Events::Combat::Damage::Defender"_hash; + } + + namespace Events::Combat::Die{ + const EventId DIE = "Events::Combat::Die::DIE"_hash; + const ParamId ENTITY = "Events::Combat::Die::Entity"_hash; + } + namespace Events::Combat::Spell{ const ParamId TARGET = "Events::Combat::Spell::Target"_hash; const ParamId POWER = "Events::Combat::Spell::Power"_hash; } + namespace Events::StatusCondition { + const EventId APPLY = "Events::StatusCondition::APPLY"_hash; + const EventId REMOVE = "Events::StatusCondition::REMOVE"_hash; + } + + namespace Events::StatusCondition { + const ParamId NAME = "Events::StatusCondition::Name"_hash; + const ParamId DAMAGE_PER_TICK = "Events::StatusCondition::DamagePerTick"_hash; + const ParamId CONDITION = "Events::StatusCondition::Condition"_hash; + } + } diff --git a/lib/include/systems/combat_system.hpp b/lib/include/systems/combat_system.hpp index bcb9d28..187b3ea 100644 --- a/lib/include/systems/combat_system.hpp +++ b/lib/include/systems/combat_system.hpp @@ -2,34 +2,101 @@ #include "core/system.hpp" #include "core/coordinator.hpp" +#include "components/attack.hpp" +#include "components/defence.hpp" +#include "components/status_condition.hpp" +#include "components/identity.hpp" #include "core/types.hpp" #include "core/event.hpp" +#include + extern SupaRL::Coordinator g_coordinator; namespace SupaRL { + static auto inline entity_is_dead(int hp) -> bool { + return hp <= 0; + } + + static auto inline send_die_event(Entity entity) -> void { + auto die_event = SupaRL::Event(SupaRL::Events::Combat::Die::DIE); + die_event.set_param<>(SupaRL::Events::Combat::Die::ENTITY, entity); + g_coordinator.send_event(die_event); + } + + static auto inline send_damage_event(Entity attacker, Entity target, int damage) -> void { + auto damage_event = SupaRL::Event(SupaRL::Events::Combat::Damage::DEALT); + damage_event.set_param<>(SupaRL::Events::Combat::Damage::DAMAGE, damage); + damage_event.set_param<>(SupaRL::Events::Combat::Damage::ATTACKER, attacker); + damage_event.set_param<>(SupaRL::Events::Combat::Damage::DEFENDER, target); + g_coordinator.send_event(damage_event); + } + inline auto attack_event_listener = [](Event& event) -> void { - Entity& attacker event.get_param(SupaRL::Events::Combat::Attack::ATTACKER); - Entity& target = event.get_param(SupaRL::Events::Combat::Attack::DEFENDER); - int damage = attacker.get_attack_component().get_damage() - - target.get_defense_component().get_defense(); + Entity& attacker = event.get_param(SupaRL::Events::Combat::ATTACKER); + Entity& target = event.get_param(SupaRL::Events::Combat::DEFENDER); + + const auto& attacker_attack = g_coordinator + .get_component(attacker); + + auto& target_defence = g_coordinator + .get_component(target); + + int damage = attacker_attack.damage_ - target_defence.defence_; + if (damage > 0) { - target.get_defense_component().take_damage(damage); - auto* rng = TCODRandom::getInstance(); - int rand = rng->getInt(0, 1); - if (rand == 1) { + target_defence.hp_ -= damage; + + std::random_device dev; + std::mt19937 rng(dev()); + std::uniform_int_distribution dist_fifty_fifty(0,1); // distribution in range [1, 6] + + if (dist_fifty_fifty(rng) == 1) { auto& status_condition = g_coordinator.get_component( - target.get_id()); + target); status_condition.damage_per_tick_ = 1; status_condition.max_ticks_ = 10; status_condition.ticks_ = 0; status_condition.name_ = "Bleeding"; auto entity_name = g_coordinator.get_component( - target.get_id()).name_; + target).name_; + + auto status_event = SupaRL::Event(SupaRL::Events::StatusCondition::APPLY); + status_event.set_param<>(SupaRL::Events::StatusCondition::NAME, entity_name); + status_event.set_param<>(SupaRL::Events::StatusCondition::DAMAGE_PER_TICK, status_condition.damage_per_tick_); + status_event.set_param<>(SupaRL::Events::StatusCondition::CONDITION, "bleeding"); + g_coordinator.send_event(status_event); + + } + + if(entity_is_dead(target_defence.hp_)) { + send_die_event(target); } - // TODO: Fire combat resolved event with damage dealt + + }; + }; + + inline auto spell_event_listener = [](Event& event) -> void { + Entity& attacker = event.get_param(SupaRL::Events::Combat::ATTACKER); + Entity& target = event.get_param(SupaRL::Events::Combat::DEFENDER); + int spell_power = event.get_param(SupaRL::Events::Combat::Spell::POWER); + + auto& target_defence = g_coordinator + .get_component(target); + + int damage = spell_power - target_defence.defence_; + + if (damage > 0) { + target_defence.hp_ -= damage; + } + + if(entity_is_dead(target_defence.hp_)) { + send_die_event(target); } - // TODO: Fire combat resolved event with damage dealt (0) + + send_damage_event(attacker, target, damage); }; + + } From ea7f8f0d4f545d17459840a9b5654fd2c8c42b47 Mon Sep 17 00:00:00 2001 From: daniel baker Date: Fri, 18 Oct 2024 15:57:40 +0100 Subject: [PATCH 20/38] feat(defence): move defence comps out --- app/include/combat_system.hpp | 9 +- app/src/components.cpp | 140 +++++++++++++++-------------- app/src/engine.cpp | 8 +- app/src/events/command.cpp | 5 +- app/src/state.cpp | 10 ++- lib/include/components/defence.hpp | 18 ++++ lib/include/core/types.hpp | 6 ++ 7 files changed, 121 insertions(+), 75 deletions(-) diff --git a/app/include/combat_system.hpp b/app/include/combat_system.hpp index 698ff4c..d22d408 100644 --- a/app/include/combat_system.hpp +++ b/app/include/combat_system.hpp @@ -2,7 +2,6 @@ #include #include -#include "components.hpp" #include #include #include "game_entity.hpp" @@ -11,14 +10,14 @@ extern SupaRL::Coordinator g_coordinator; namespace cpprl::combat_system { - inline auto handle_attack = [](Entity& attacker, Entity& target) -> int { + inline auto handle_attack = [](Entity& attacker, Entity& target) -> void { auto attack_event = SupaRL::Event(SupaRL::Events::Combat::ATTACK); - attack_event.set_param<>(SupaRL::Events::Combat::Attack::ATTACKER, attacker.get_id()); - attack_event.set_param<>(SupaRL::Events::Combat::Attack::DEFENDER, target.get_id()); + attack_event.set_param<>(SupaRL::Events::Combat::ATTACKER, attacker.get_id()); + attack_event.set_param<>(SupaRL::Events::Combat::DEFENDER, target.get_id()); g_coordinator.send_event(attack_event); }; - inline auto handle_spell = [](int power, Entity& target) -> int { + inline auto handle_spell = [](int power, Entity& target) -> void { auto spell_event = SupaRL::Event(SupaRL::Events::Combat::SPELL); spell_event.set_param<>(SupaRL::Events::Combat::Spell::TARGET, target.get_id()); spell_event.set_param<>(SupaRL::Events::Combat::Spell::POWER, power); diff --git a/app/src/components.cpp b/app/src/components.cpp index f8250dc..a316536 100644 --- a/app/src/components.cpp +++ b/app/src/components.cpp @@ -8,32 +8,17 @@ #include "basic_ai_component.hpp" #include "combat_system.hpp" #include "entity_manager.hpp" -#include "events/command.hpp" #include "game_entity.hpp" #include "state.hpp" #include "world.hpp" +#include #include #include +#include extern SupaRL::Coordinator g_coordinator; namespace cpprl { - int DefenseComponent::heal(int amount) { - if (hp_ == max_hp_) { - return 0; - }; - int new_hp = hp_ + amount; - if (new_hp > max_hp_) { - new_hp = max_hp_; - } - - int healed = new_hp - hp_; - - hp_ = new_hp; - - return healed; - } - Container::Container(int size) : size_(size), inventory_({}) { inventory_.reserve(size); @@ -93,21 +78,37 @@ namespace cpprl { ActionResult HealingConsumable::use( Entity* owner, Entity* wearer, World& world) { - if (const DefenseComponent* defense_component = - &wearer->get_defense_component(); - defense_component == nullptr) { - return Failure{"There's nothing to heal."}; - } + auto& wearer_defence = g_coordinator.get_component( + wearer->get_id()); - if (const int amount_healed = wearer->get_defense_component().heal(amount_); + // Shouldn't hit this any more + /*if (const DefenseComponent* defense_component =*/ + /* &wearer->get_defense_component();*/ + /* defense_component == nullptr) {*/ + /* return Failure{"There's nothing to heal."};*/ + /*}*/ + + ; + + if (const int amount_healed = wearer_defence.heal(amount_); amount_healed > 0) { ConsumableComponent::use(owner, wearer, world); - std::string message = fmt::format("You healed for {} HP.", amount_healed); - world.get_message_log().add_message(message, GREEN); + auto healed_event = SupaRL::Event(SupaRL::Events::Heal::HEALED); + healed_event.set_param(SupaRL::Events::Heal::AMOUNT, amount_healed); + healed_event.set_param(SupaRL::Events::Heal::ENTITY, wearer->get_id()); + g_coordinator.send_event(healed_event); + /*std::string message = fmt::format("You healed for {} HP.", amount_healed);*/ + /*world.get_message_log().add_message(message, GREEN);*/ return Success{}; } - return Failure{"You are already at full health."}; + auto healed_event = SupaRL::Event(SupaRL::Events::Heal::HEALED); + healed_event.set_param(SupaRL::Events::Heal::AMOUNT, 0); + healed_event.set_param(SupaRL::Events::Heal::ENTITY, wearer->get_id()); + g_coordinator.send_event(healed_event); + + /*return Failure{"You are already at full health."};*/ + return Failure{}; } ActionResult LightningBolt::use(Entity* owner, Entity* wearer, World& world) { @@ -122,57 +123,66 @@ namespace cpprl { Entity& closest_living_monster = optional_closest_monster_ref.value().get(); - int inflicted = combat_system::handle_spell(damage_, closest_living_monster); + /*int inflicted = combat_system::handle_spell(damage_, closest_living_monster);*/ + combat_system::handle_spell(damage_, closest_living_monster); ConsumableComponent::use(owner, wearer, world); - auto& entity_name = g_coordinator.get_component( - closest_living_monster.get_id()).name_; - if (inflicted > 0) { - world.get_message_log().add_message( - fmt::format( - "A lightning bolt strikes the {} with a loud " - "thunder! The damage is {} hit points.", - entity_name, - damage_), - GREEN); - - if (closest_living_monster.get_defense_component().is_dead()) { - auto action = DieEvent(world, &closest_living_monster); - action.execute(); - } + /*auto& entity_name = g_coordinator.get_component(*/ + /* closest_living_monster.get_id()).name_;*/ return Success{}; - } else { - return Failure{fmt::format( - "The lightning bolt hits the {} but does no damage.", - entity_name)}; - } + /*if (inflicted > 0) {*/ + /* world.get_message_log().add_message(*/ + /* fmt::format(*/ + /* "A lightning bolt strikes the {} with a loud "*/ + /* "thunder! The damage is {} hit points.",*/ + /* entity_name,*/ + /* damage_),*/ + /* GREEN);*/ + /**/ + /* // TODO: this should be handled by spell cast*/ + /* if (closest_living_monster.get_defense_component().is_dead()) {*/ + /* auto action = DieEvent(world, &closest_living_monster);*/ + /* action.execute();*/ + /* }*/ + /* return Success{};*/ + /*} else {*/ + /* return Failure{fmt::format(*/ + /* "The lightning bolt hits the {} but does no damage.",*/ + /* entity_name)};*/ + /*}*/ } ActionResult FireSpell::use(Entity* owner, Entity* wearer, World& world) { auto on_pick = [&, owner, wearer]() { ConsumableComponent::use(owner, wearer, world); for (Entity* entity : world.get_entities()) { - auto entity_position = g_coordinator.get_component( + + const auto entity_position = g_coordinator.get_component( entity->get_id()).position_; - auto entity_name = g_coordinator.get_component( + const auto entity_name = g_coordinator.get_component( entity->get_id()).name_; - if (const auto* defense_component = &entity->get_defense_component(); - defense_component && defense_component->is_not_dead() && + auto& defence_component = g_coordinator.get_component( + entity->get_id()); + + if (defence_component.is_not_dead() && entity_position.distance_to( world.get_map().get_highlight_tile()) <= aoe_) { - world.get_message_log().add_message( - fmt::format( - "The {} gets burned for {} hit points.", - entity_name, - damage_), - RED); - int inflicted = combat_system::handle_spell(damage_, *entity); - if (inflicted > 0) { - // TODO: this is repeated everywhere. Put it in take_damage - if (entity->get_defense_component().is_dead()) { - auto action = DieEvent(world, entity); - action.execute(); - } - } + // TODO: Send this as a message + /*world.get_message_log().add_message(*/ + /* fmt::format(*/ + /* "The {} gets burned for {} hit points.",*/ + /* entity_name,*/ + /* damage_),*/ + /* RED);*/ + /*int inflicted = combat_system::handle_spell(damage_, *entity);*/ + combat_system::handle_spell(damage_, *entity); + // Combat system should handle firing the die event on spell damage. + /*if (inflicted > 0) {*/ + /* // TODO: this is repeated everywhere. Put it in take_damage*/ + /* if (entity->get_defense_component().is_dead()) {*/ + /* auto action = DieEvent(world, entity);*/ + /* action.execute();*/ + /* }*/ + /*}*/ } } }; diff --git a/app/src/engine.cpp b/app/src/engine.cpp index bba7367..75ea5ae 100644 --- a/app/src/engine.cpp +++ b/app/src/engine.cpp @@ -90,7 +90,9 @@ namespace cpprl { void Engine::save() { std::filesystem::create_directories(std::filesystem::path("saves")); - if (world_->get_player()->get_defense_component().is_dead()) { + auto player_id = world_->get_player()->get_id(); + auto& player_defense = g_coordinator.get_component(player_id); + if (player_defense.is_dead()) { std::filesystem::remove(std::filesystem::path("saves/game.sav")); } else { @@ -155,7 +157,9 @@ namespace cpprl { } else if (std::holds_alternative(result)) { status_condition_system_->update(); world_->handle_enemy_turns(); - if (world_->get_player()->get_defense_component().is_dead()) { + auto playerId = world_->get_player()->get_id(); + auto& player_defense = g_coordinator.get_component(playerId); + if (player_defense.is_dead()) { engine_state_->on_exit(); engine_state_ = std::make_unique(*world_); engine_state_->on_enter(); diff --git a/app/src/events/command.cpp b/app/src/events/command.cpp index 8993625..ef4b762 100644 --- a/app/src/events/command.cpp +++ b/app/src/events/command.cpp @@ -13,6 +13,7 @@ #include "core/coordinator.hpp" #include #include +#include #include extern SupaRL::Coordinator g_coordinator; @@ -298,7 +299,9 @@ namespace cpprl { } if (cursor == 3) { - player->get_attack_component().boost_damage(1); + auto& attack_component = g_coordiator.get_component( + player->get_id()); + attack_component.damage_ += 1; } else if (cursor == 4) { auto& defence_component = g_coordiator.get_component( player->get_id()); diff --git a/app/src/state.cpp b/app/src/state.cpp index 89e183a..4f05301 100644 --- a/app/src/state.cpp +++ b/app/src/state.cpp @@ -5,6 +5,9 @@ #include "gui.hpp" #include "rendering.hpp" #include "world.hpp" +#include + +extern SupaRL::Coordinator g_coordinator; namespace cpprl { @@ -38,8 +41,11 @@ namespace cpprl { // gen a new dungeon? world_.get_message_log().add_message( "You take a moment to rest to recover your strength.", WHITE); - world_.get_player()->get_defense_component().heal( - world_.get_player()->get_defense_component().get_max_hp() / 2); + auto player_id = world_.get_player()->get_id(); + auto& player_defence = g_coordinator.get_component(player_id); + + player_defence.heal(player_defence.max_hp_ / 2); + world_.get_message_log().add_message( "After a rare moment of peace, you descend deeper into the heart of the " "dungeon...", diff --git a/lib/include/components/defence.hpp b/lib/include/components/defence.hpp index 18ab457..1eaaccc 100644 --- a/lib/include/components/defence.hpp +++ b/lib/include/components/defence.hpp @@ -5,5 +5,23 @@ namespace SupaRL{ short defence_; short hp_; short max_hp_; + + bool is_dead() { return hp_ <= 0; } + bool is_not_dead() { return !is_dead(); } + int heal(short amount) { + if (hp_ == max_hp_) { + return 0; + }; + int new_hp = hp_ + amount; + if (new_hp > max_hp_) { + new_hp = max_hp_; + } + + int healed = new_hp - hp_; + + hp_ = new_hp; + + return healed; + } }; } diff --git a/lib/include/core/types.hpp b/lib/include/core/types.hpp index 8d02a48..ebdb0bb 100644 --- a/lib/include/core/types.hpp +++ b/lib/include/core/types.hpp @@ -53,6 +53,12 @@ namespace SupaRL { const ParamId ENTITY = "Events::Combat::Die::Entity"_hash; } + namespace Events::Heal{ + const EventId HEALED = "Events::Heal::HEALED"_hash; + const ParamId AMOUNT = "Events::Heal::Amount"_hash; + const ParamId ENTITY = "Events::Heal::Entity"_hash; + } + namespace Events::Combat::Spell{ const ParamId TARGET = "Events::Combat::Spell::Target"_hash; const ParamId POWER = "Events::Combat::Spell::Power"_hash; From 2179c7ef264a64315cb70e3d85e7ad18e094a3a5 Mon Sep 17 00:00:00 2001 From: daniel baker Date: Sat, 19 Oct 2024 16:20:07 +0100 Subject: [PATCH 21/38] feat(ecs): remove old code --- app/include/components.hpp | 31 +------------------------------ app/include/health_bar.hpp | 7 ++++--- app/include/ui/ui.hpp | 3 ++- app/src/entity_manager.cpp | 6 ++++-- app/src/ui/ui.cpp | 4 ++-- app/src/world.cpp | 8 ++++++-- 6 files changed, 19 insertions(+), 40 deletions(-) diff --git a/app/include/components.hpp b/app/include/components.hpp index ec5e8a6..720459c 100644 --- a/app/include/components.hpp +++ b/app/include/components.hpp @@ -27,36 +27,7 @@ namespace cpprl { int damage_; }; - class DefenseComponent { - public: - DefenseComponent() = default; - DefenseComponent(int defense, int maxHp) - : defense_(defense), hp_(maxHp), max_hp_(maxHp) {} - virtual ~DefenseComponent() = default; - - int get_hp() const { return hp_; } - int get_max_hp() const { return max_hp_; } - int get_defense() const { return defense_; } - void boost_defense(int amount) { defense_ += amount; } - - void take_damage(int damage) { hp_ -= damage; } - int heal(int amount); - bool is_dead() const { return hp_ <= 0; } - bool is_not_dead() const { return !is_dead(); } - void die(Entity& owner) const; - - template - void serialize(Archive& archive) { - archive(defense_, hp_, max_hp_); - } - - private: - int defense_; - int hp_; - int max_hp_; - }; - - class Container { + class Container { private: size_t size_; std::vector inventory_; diff --git a/app/include/health_bar.hpp b/app/include/health_bar.hpp index f3dd439..f04c3a7 100644 --- a/app/include/health_bar.hpp +++ b/app/include/health_bar.hpp @@ -1,18 +1,19 @@ #pragma once -#include "components.hpp" #include "gui.hpp" #include +#include + namespace cpprl { class HealthBar : public UiWindow { private: - DefenseComponent& health_; + SupaRL::DefenseComponent& health_; public: HealthBar( - int width, int height, SupaRL::Vector2D position, DefenseComponent& defense); + int width, int height, SupaRL::Vector2D position, SupaRL::DefenceComponent& defense); void render(tcod::Console& console) override; }; diff --git a/app/include/ui/ui.hpp b/app/include/ui/ui.hpp index f923e57..c9c7b25 100644 --- a/app/include/ui/ui.hpp +++ b/app/include/ui/ui.hpp @@ -2,6 +2,7 @@ #include "health_bar.hpp" #include "ui/dungeon_level.hpp" +#include #include "xp_bar.hpp" namespace cpprl { @@ -14,7 +15,7 @@ class UI { public: explicit UI(Dungeon& dungeon); - void set_health_bar(DefenseComponent& defense_component); + void set_health_bar(SupaRL::DefenceComponent& defense_component); void set_xp_bar(StatsComponent& stats_component); void render(tcod::Console& console); diff --git a/app/src/entity_manager.cpp b/app/src/entity_manager.cpp index 4ba4994..254e3e9 100644 --- a/app/src/entity_manager.cpp +++ b/app/src/entity_manager.cpp @@ -11,6 +11,7 @@ #include #include +#include #include extern SupaRL::Coordinator g_coordinator; @@ -153,11 +154,12 @@ namespace cpprl { std::optional> closest = std::nullopt; float best_distance = 1E6f; for (const auto& entity : entities_) { - const auto* defense_component = &entity->get_defense_component(); + const auto& defense_component = g_coordinator.get_component( + entity->get_id()); const std::optional> ai_component = entity->get_ai_component(); - if (ai_component.has_value() && defense_component) { + if (ai_component.has_value() && defence_component.is_not_dead()) { auto entity_position = g_coordinator.get_component( entity->get_id()).position_; float distance = position.distance_to( diff --git a/app/src/ui/ui.cpp b/app/src/ui/ui.cpp index 7fc7b05..78190c3 100644 --- a/app/src/ui/ui.cpp +++ b/app/src/ui/ui.cpp @@ -9,9 +9,9 @@ namespace cpprl { std::make_unique(20, 1, SupaRL::Vector2D{2, 35}, dungeon); } - void UI::set_health_bar(DefenseComponent& defense_component) { + void UI::set_health_bar(SupaRL::DefenceComponent& defence_component) { health_bar_ = - std::make_unique(20, 1, SupaRL::Vector2D{2, 36}, defense_component); + std::make_unique(20, 1, SupaRL::Vector2D{2, 36}, defence_component); } void UI::set_xp_bar(StatsComponent& stats_component) { diff --git a/app/src/world.cpp b/app/src/world.cpp index 3a25201..84dbc21 100644 --- a/app/src/world.cpp +++ b/app/src/world.cpp @@ -10,6 +10,7 @@ #include "core/coordinator.hpp" #include #include +#include extern SupaRL::Coordinator g_coordinator; @@ -95,8 +96,10 @@ namespace cpprl { for (const auto& entity : *entities_) { const std::optional> ai_component = entity->get_ai_component(); + iconst auto& defence_component = g_coordinator.get_component( + entity->get_id()); if (ai_component.has_value() && - entity->get_defense_component().is_not_dead()) { + defence_component.is_not_dead()) { entity->update(*this); } } @@ -115,7 +118,8 @@ namespace cpprl { player_->get_id()).position_; dungeon_.get_map().compute_fov( player_position, 4); - DefenseComponent& player_defense = player_->get_defense_component(); + const auto& player_defense = g_coordinator.get_component( + player_->get_id()); ui_->set_health_bar(player_defense); ui_->set_xp_bar(player_->get_stats_component().value().get()); entities_->shrink_to_fit(); From 28f26b4357d75010dfa46ff6bac6f4b8046619ce Mon Sep 17 00:00:00 2001 From: daniel baker Date: Sat, 19 Oct 2024 16:55:38 +0100 Subject: [PATCH 22/38] fix(ecs): typoo --- app/include/health_bar.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/include/health_bar.hpp b/app/include/health_bar.hpp index f04c3a7..fc3695b 100644 --- a/app/include/health_bar.hpp +++ b/app/include/health_bar.hpp @@ -9,7 +9,7 @@ namespace cpprl { class HealthBar : public UiWindow { private: - SupaRL::DefenseComponent& health_; + SupaRL::DefenceComponent& health_; public: HealthBar( From 280d7c823714709b2bd843950e494358b2cdab1b Mon Sep 17 00:00:00 2001 From: daniel baker Date: Sun, 20 Oct 2024 17:04:43 +0100 Subject: [PATCH 23/38] feat(ecs): remove ref attack defense --- app/src/gui.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app/src/gui.cpp b/app/src/gui.cpp index fe1032a..ceb731d 100644 --- a/app/src/gui.cpp +++ b/app/src/gui.cpp @@ -6,6 +6,10 @@ #include "components.hpp" #include "game_entity.hpp" #include +#include +#include + +extern SupaRL::Coordinator gcoordinator; namespace cpprl { @@ -172,8 +176,10 @@ void CharacterMenuWindow::render(tcod::Console& parent_console) { tcod::print(*console_, {1, cursor_}, ">", WHITE, std::nullopt, TCOD_LEFT); - int entity_damage_ = entity_->get_attack_component().get_damage(); - int entity_defense_ = entity_->get_defense_component().get_defense(); + auto& entity_defence = gcoordinator.get_component( + entity_->get_id()); + auto& entity_attack = gcoordinator.get_component( + entity_->get_id()); auto stats = entity_->get_stats_component().value().get(); tcod::print_rect( @@ -197,7 +203,7 @@ void CharacterMenuWindow::render(tcod::Console& parent_console) { tcod::print_rect( *console_, {2, 3, console_->getWidth() - 1, 1}, - fmt::format("Damage: {}", entity_damage_), + fmt::format("Damage: {}", entity_attack.damage_), // "Damage: " + entity_damage_, WHITE, std::nullopt, @@ -206,7 +212,7 @@ void CharacterMenuWindow::render(tcod::Console& parent_console) { tcod::print_rect( *console_, {2, 4, console_->getWidth() - 1, 1}, - fmt::format("Defense: {}", entity_defense_), + fmt::format("Defense: {}", entity_defence.defence_), // "Defense: " + entity_defense_, WHITE, std::nullopt, From 8332ac821d883d56efc1f4d60321f5f3a56f98e1 Mon Sep 17 00:00:00 2001 From: daniel baker Date: Mon, 21 Oct 2024 16:14:26 +0100 Subject: [PATCH 24/38] fix: typo --- app/src/entity_factory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/entity_factory.cpp b/app/src/entity_factory.cpp index 1fc78d4..8e42513 100644 --- a/app/src/entity_factory.cpp +++ b/app/src/entity_factory.cpp @@ -48,7 +48,7 @@ namespace cpprl { entity->set_stats_component( std::make_unique(10, 1, 10, 10, 2)); - g_coordinator,add_component(entity_id, SupaRL::AttackComponent{ + g_coordinator.add_component(entity_id, SupaRL::AttackComponent{ .damage_ = 3}); g_coordinator.add_component(entity_id, SupaRL::DefenceComponent{ .defence_ = 0, From 903d233fb0620e35ed1d43f3841039b62f7cf7b3 Mon Sep 17 00:00:00 2001 From: daniel baker Date: Mon, 21 Oct 2024 16:14:57 +0100 Subject: [PATCH 25/38] feat(ecs): remove deleted gets --- app/src/events/command.cpp | 51 +++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/app/src/events/command.cpp b/app/src/events/command.cpp index ef4b762..a8a10b6 100644 --- a/app/src/events/command.cpp +++ b/app/src/events/command.cpp @@ -126,7 +126,6 @@ namespace cpprl { entity_->get_id()).name_; world_.get_message_log().add_message( fmt::format("{} has died!", util::capitalize(entity_name))); - /*entity_->get_defense_component().die(*entity_);*/ if (entity_name != "player") { const std::optional> @@ -190,31 +189,33 @@ namespace cpprl { } if (target.has_value()) { - int damage = combat_system::handle_attack(*entity_, target.value().get()); - auto& target_name = g_coordinator.get_component( - target.value().get().get_id()).name_; - if (damage > 0) { - std::string message = fmt::format( - "{} attacks {} for {} hit points.", - util::capitalize(entity_name), - util::capitalize(target_name), - damage); - - world_.get_message_log().add_message(message, attack_colour, true); - - if (target.value().get().get_defense_component().is_dead()) { - auto action = DieEvent(world_, &target.value().get()); - return action.execute(); - } - } else { - std::string message = fmt::format( - "{} attacks {} but does no damage.", - util::capitalize(entity_name), - util::capitalize(target_name)); - - world_.get_message_log().add_message(message, attack_colour, true); + combat_system::handle_attack(*entity_, target.value().get()); + /*if (damage > 0) {*/ + /* std::string message = fmt::format(*/ + /* "{} attacks {} for {} hit points.",*/ + /* util::capitalize(entity_name),*/ + /* util::capitalize(target_name),*/ + /* damage);*/ + /**/ + /* world_.get_message_log().add_message(message, attack_colour, true);*/ + /**/ + /* auto& target_defence = g_coordinator.get_component(*/ + /* target.value().get().get_id());*/ + /* if (target_defence.is_dead()) {*/ + /* auto action = DieEvent(world_, &target.value().get());*/ + /* return action.execute();*/ + /* }*/ + /*} else {*/ + /*auto& target_name = g_coordinator.get_component(*/ + /* target.value().get().get_id()).name_;*/ + /* std::string message = fmt::format(*/ + /* "{} attacks {} but does no damage.",*/ + /* util::capitalize(entity_name),*/ + /* util::capitalize(target_name));*/ + /**/ + /* world_.get_message_log().add_message(message, attack_colour, true);*/ } - } + /*}*/ return EndTurn{}; }; From fdbc24e89237d910d434c2a3e07a77f112f39db9 Mon Sep 17 00:00:00 2001 From: daniel baker Date: Mon, 21 Oct 2024 16:16:28 +0100 Subject: [PATCH 26/38] fix: remove const --- app/src/world.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/world.cpp b/app/src/world.cpp index 84dbc21..19d64b9 100644 --- a/app/src/world.cpp +++ b/app/src/world.cpp @@ -96,7 +96,7 @@ namespace cpprl { for (const auto& entity : *entities_) { const std::optional> ai_component = entity->get_ai_component(); - iconst auto& defence_component = g_coordinator.get_component( + auto& defence_component = g_coordinator.get_component( entity->get_id()); if (ai_component.has_value() && defence_component.is_not_dead()) { @@ -118,9 +118,9 @@ namespace cpprl { player_->get_id()).position_; dungeon_.get_map().compute_fov( player_position, 4); - const auto& player_defense = g_coordinator.get_component( + auto& player_defence = g_coordinator.get_component( player_->get_id()); - ui_->set_health_bar(player_defense); + ui_->set_health_bar(player_defence); ui_->set_xp_bar(player_->get_stats_component().value().get()); entities_->shrink_to_fit(); } From cf9bf2d26c73795ea3733af3fcef81d5a671e16d Mon Sep 17 00:00:00 2001 From: daniel baker Date: Mon, 21 Oct 2024 16:16:53 +0100 Subject: [PATCH 27/38] feat: remove comment --- app/src/world.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/world.cpp b/app/src/world.cpp index 19d64b9..de3c4e1 100644 --- a/app/src/world.cpp +++ b/app/src/world.cpp @@ -71,7 +71,6 @@ namespace cpprl { ui_->render(g_console); message_log_.render(g_console, 23, 35, 45, 5); - // TODO: this is not working since cursor is not being set auto entities_at = entities_->get_entities_at(controller_->cursor); if (!entities_at.empty()) { std::string names; From 6149f17eae41820ca2df6d0926bfd498fa828e46 Mon Sep 17 00:00:00 2001 From: daniel baker Date: Tue, 22 Oct 2024 15:46:36 +0100 Subject: [PATCH 28/38] feat(ecs); it builds!!! :tada: --- app/src/consumable_factory.cpp | 2 +- app/src/engine.cpp | 8 ++++---- app/src/entity_factory.cpp | 7 ------- app/src/entity_manager.cpp | 2 +- app/src/events/command.cpp | 6 +++--- app/src/gui.cpp | 6 +++--- app/src/ui/health_bar.cpp | 8 ++++---- app/src/world.cpp | 5 +++++ 8 files changed, 21 insertions(+), 23 deletions(-) diff --git a/app/src/consumable_factory.cpp b/app/src/consumable_factory.cpp index e5f955a..c055a11 100644 --- a/app/src/consumable_factory.cpp +++ b/app/src/consumable_factory.cpp @@ -19,7 +19,7 @@ namespace cpprl { .position_ = at_position}); g_coordinator.add_component(entity_id, SupaRL::AsciiComponent{ .symbol_ = symbol, - .colour_ = SupaRL::ColorRGB{.r = color.r, .g = color.g, .b = color.b}, + .colour_ = SupaRL::ColourRGB{.r = color.r, .g = color.g, .b = color.b}, .layer_ = 1}); g_coordinator.add_component(entity_id, SupaRL::IdentityComponent{ .name_ = name}); diff --git a/app/src/engine.cpp b/app/src/engine.cpp index 75ea5ae..28bf1cb 100644 --- a/app/src/engine.cpp +++ b/app/src/engine.cpp @@ -91,8 +91,8 @@ namespace cpprl { void Engine::save() { std::filesystem::create_directories(std::filesystem::path("saves")); auto player_id = world_->get_player()->get_id(); - auto& player_defense = g_coordinator.get_component(player_id); - if (player_defense.is_dead()) { + auto& player_defence = g_coordinator.get_component(player_id); + if (player_defence.is_dead()) { std::filesystem::remove(std::filesystem::path("saves/game.sav")); } else { @@ -158,8 +158,8 @@ namespace cpprl { status_condition_system_->update(); world_->handle_enemy_turns(); auto playerId = world_->get_player()->get_id(); - auto& player_defense = g_coordinator.get_component(playerId); - if (player_defense.is_dead()) { + auto& player_defence = g_coordinator.get_component(playerId); + if (player_defence.is_dead()) { engine_state_->on_exit(); engine_state_ = std::make_unique(*world_); engine_state_->on_enter(); diff --git a/app/src/entity_factory.cpp b/app/src/entity_factory.cpp index 8e42513..6e243af 100644 --- a/app/src/entity_factory.cpp +++ b/app/src/entity_factory.cpp @@ -54,8 +54,6 @@ namespace cpprl { .defence_ = 0, .hp_ = 10, .max_hp_ = 10}); - g_coordinator.add_component(entity_id, SupaRL::AttackComponent{ - .damage_ = 3}); g_coordinator.add_component(entity_id, SupaRL::TransformComponent{ .position_ = at_position}); g_coordinator.add_component( @@ -68,7 +66,6 @@ namespace cpprl { Entity* TrollFactory::create(SupaRL::Vector2D at_position) { Entity* entity = create_base("Troll", DARK_GREEN, "T"); - entity->set_attack_component(std::make_unique(4)); entity->set_ai_component(std::make_unique()); entity->set_stats_component( std::make_unique(20, 1, 10, 20, 2)); @@ -81,8 +78,6 @@ namespace cpprl { .defence_ = 1, .hp_ = 16, .max_hp_ = 16}); - g_coordinator.add_component(entity_id, SupaRL::AttackComponent{ - .damage_ = 4}); g_coordinator.add_component(entity_id, SupaRL::TransformComponent{ .position_ = at_position}); g_coordinator.add_component( @@ -107,8 +102,6 @@ namespace cpprl { .defence_ = 2, .hp_ = 30, .max_hp_ = 30}); - g_coordinator.add_component(entity_id, SupaRL::AttackComponent{ - .damage_ = 5}); g_coordinator.add_component(entity_id, SupaRL::TransformComponent{ .position_ = at_position}); g_coordinator.add_component( diff --git a/app/src/entity_manager.cpp b/app/src/entity_manager.cpp index 254e3e9..8e6228e 100644 --- a/app/src/entity_manager.cpp +++ b/app/src/entity_manager.cpp @@ -154,7 +154,7 @@ namespace cpprl { std::optional> closest = std::nullopt; float best_distance = 1E6f; for (const auto& entity : entities_) { - const auto& defense_component = g_coordinator.get_component( + auto& defence_component = g_coordinator.get_component( entity->get_id()); const std::optional> ai_component = entity->get_ai_component(); diff --git a/app/src/events/command.cpp b/app/src/events/command.cpp index a8a10b6..ec7684a 100644 --- a/app/src/events/command.cpp +++ b/app/src/events/command.cpp @@ -300,13 +300,13 @@ namespace cpprl { } if (cursor == 3) { - auto& attack_component = g_coordiator.get_component( + auto& attack_component = g_coordinator.get_component( player->get_id()); attack_component.damage_ += 1; } else if (cursor == 4) { - auto& defence_component = g_coordiator.get_component( + auto& defence_component = g_coordinator.get_component( player->get_id()); - defence_component.defense_ += 1; + defence_component.defence_ += 1; } stats.reduce_stats_points(1); return EndTurn{}; diff --git a/app/src/gui.cpp b/app/src/gui.cpp index ceb731d..cd88aeb 100644 --- a/app/src/gui.cpp +++ b/app/src/gui.cpp @@ -9,7 +9,7 @@ #include #include -extern SupaRL::Coordinator gcoordinator; +extern SupaRL::Coordinator g_coordinator; namespace cpprl { @@ -176,9 +176,9 @@ void CharacterMenuWindow::render(tcod::Console& parent_console) { tcod::print(*console_, {1, cursor_}, ">", WHITE, std::nullopt, TCOD_LEFT); - auto& entity_defence = gcoordinator.get_component( + auto& entity_defence = g_coordinator.get_component( entity_->get_id()); - auto& entity_attack = gcoordinator.get_component( + auto& entity_attack = g_coordinator.get_component( entity_->get_id()); auto stats = entity_->get_stats_component().value().get(); diff --git a/app/src/ui/health_bar.cpp b/app/src/ui/health_bar.cpp index 2c61531..da25bb5 100644 --- a/app/src/ui/health_bar.cpp +++ b/app/src/ui/health_bar.cpp @@ -10,12 +10,12 @@ namespace cpprl { HealthBar::HealthBar( - int width, int height, SupaRL::Vector2D position, DefenseComponent& defense) + int width, int height, SupaRL::Vector2D position, SupaRL::DefenceComponent& defense) : UiWindow(width, height, position), health_(defense) {} void HealthBar::render(tcod::Console& console) { - const auto bar_width = (int)((float)health_.get_hp() / - (float)health_.get_max_hp() * (float)width_); + const auto bar_width = (int)((float)health_.hp_ / + (float)health_.max_hp_ * (float)width_); tcod::draw_rect( console, {position_.x, position_.y, width_, height_}, @@ -32,7 +32,7 @@ namespace cpprl { tcod::print_rect( console, {position_.x, position_.y, width_, height_}, - fmt::format("HP: {}/{}", health_.get_hp(), health_.get_max_hp()), + fmt::format("HP: {}/{}", health_.hp_, health_.max_hp_), WHITE, std::nullopt, TCOD_CENTER); diff --git a/app/src/world.cpp b/app/src/world.cpp index de3c4e1..8187c5c 100644 --- a/app/src/world.cpp +++ b/app/src/world.cpp @@ -95,12 +95,17 @@ namespace cpprl { for (const auto& entity : *entities_) { const std::optional> ai_component = entity->get_ai_component(); + try { auto& defence_component = g_coordinator.get_component( entity->get_id()); if (ai_component.has_value() && defence_component.is_not_dead()) { entity->update(*this); } + } catch (std::exception& e) { + // TODO: this is a hack until the enemy turn system is in place. + continue; + } } } From 05d142579e0e1fb0f7c0535dff1c0284a0d67530 Mon Sep 17 00:00:00 2001 From: daniel baker Date: Tue, 22 Oct 2024 15:55:35 +0100 Subject: [PATCH 29/38] feat(ecs): broke build. Ai component. --- app/include/systems/enemy_turns.hpp | 8 ++++++++ app/src/game_entity.cpp | 1 + app/src/systems/enemy_turns.cpp | 21 +++++++++++++++++++++ lib/include/components/ai.hpp | 14 ++++++++++++++ 4 files changed, 44 insertions(+) create mode 100644 app/include/systems/enemy_turns.hpp create mode 100644 app/src/systems/enemy_turns.cpp create mode 100644 lib/include/components/ai.hpp diff --git a/app/include/systems/enemy_turns.hpp b/app/include/systems/enemy_turns.hpp new file mode 100644 index 0000000..5b83bfd --- /dev/null +++ b/app/include/systems/enemy_turns.hpp @@ -0,0 +1,8 @@ +#pragma once +#include + +class EnemyTurnsSystem : public SupaRL::System +{ + public: + void update(); +}; diff --git a/app/src/game_entity.cpp b/app/src/game_entity.cpp index 14d918d..63e9893 100644 --- a/app/src/game_entity.cpp +++ b/app/src/game_entity.cpp @@ -7,6 +7,7 @@ #include "world.hpp" namespace cpprl { + // Cry. We need to move this to a component. void Entity::update(World& world) { aiComponent_->update(world, this); } void Entity::set_consumable_component( diff --git a/app/src/systems/enemy_turns.cpp b/app/src/systems/enemy_turns.cpp new file mode 100644 index 0000000..5bd0255 --- /dev/null +++ b/app/src/systems/enemy_turns.cpp @@ -0,0 +1,21 @@ +#include "systems/enemy_turns.hpp" +#include "core/coordinator.hpp" +#include "components/defence.hpp" + +extern SupaRL::Coordinator g_coordinator; + +void EnemyTurnsSystem::update() { + for (const auto& entity : entities_) { + // so this was used to get all the moveable entities + // that were controlled by the engine. We'll need to create a new + // ai component so that this system runs on only things that have an AI + // component and a defence component. + /*const std::optional> ai_component =*/ + /* entity->get_ai_component();*/ + auto& defence_component = g_coordinator.get_component( + entity->get_id()); + if (defence_component.is_not_dead()) { + entity->update(*this); + } + } +} diff --git a/lib/include/components/ai.hpp b/lib/include/components/ai.hpp new file mode 100644 index 0000000..87922d2 --- /dev/null +++ b/lib/include/components/ai.hpp @@ -0,0 +1,14 @@ +#pragma once + +namespace SupaRL { + enum class AIType { + HOSTILE, + CONFUSION + }; + + // AI component + struct AIComponent { + AIType type_; + AIType prevuious_type_; + }; +} From 69c8a5f62eccd7392c83dbd3fdcf2e1cdaa99ff4 Mon Sep 17 00:00:00 2001 From: daniel baker Date: Wed, 23 Oct 2024 07:41:20 +0100 Subject: [PATCH 30/38] feat(ai): [wip] extract to system --- app/include/basic_ai_component.hpp | 65 -------------------------- {lib => app}/include/components/ai.hpp | 2 +- app/include/game_entity.hpp | 31 ------------ app/include/systems/ai_system.hpp | 22 +++++++++ app/src/basic_ai_component.cpp | 28 ----------- app/src/entity_manager.cpp | 1 - app/src/systems/ai_system.cpp | 59 +++++++++++++++++++++++ 7 files changed, 82 insertions(+), 126 deletions(-) delete mode 100644 app/include/basic_ai_component.hpp rename {lib => app}/include/components/ai.hpp (89%) create mode 100644 app/include/systems/ai_system.hpp create mode 100644 app/src/systems/ai_system.cpp diff --git a/app/include/basic_ai_component.hpp b/app/include/basic_ai_component.hpp deleted file mode 100644 index 1d506ea..0000000 --- a/app/include/basic_ai_component.hpp +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef INCLUDE_BASIC_AI_COMPONENT_HPP_ -#define INCLUDE_BASIC_AI_COMPONENT_HPP_ - -#include // for defer -#include -#include -#include - -#include "types/engine_fwd.hpp" -#include "types/entity_fwd.hpp" -#include "types/world_fwd.hpp" - -namespace cpprl { - -class AIComponent { - public: - AIComponent() = default; - virtual ~AIComponent() = default; - - virtual void update(World& world, Entity* entity) = 0; - - template - void serialize(Archive&) {} -}; - -class HostileAI final : public AIComponent { - public: - HostileAI() = default; - virtual ~HostileAI() = default; - - void update(World& world, Entity* entity) override; - - template - void serialize(Archive& archive) { - archive(cereal::base_class(this)); - } -}; - -class ConfusionAI final : public AIComponent { - private: - int num_turns_; - std::unique_ptr old_ai_; - - public: - ConfusionAI() = default; - ConfusionAI(int num_turns, std::unique_ptr old_ai); - virtual ~ConfusionAI() = default; - - void update(World& world, Entity* entity) override; - - template - void serialize(Archive& archive) { - archive(cereal::base_class(this), num_turns_, old_ai_); - } -}; -} // namespace cpprl -// Include any archives you plan on using with your type before you register it -// Note that this could be done in any other location so long as it was prior -// to this file being included -// #include -// #include -// #include -CEREAL_REGISTER_TYPE(cpprl::HostileAI); -CEREAL_REGISTER_TYPE(cpprl::ConfusionAI); -#endif diff --git a/lib/include/components/ai.hpp b/app/include/components/ai.hpp similarity index 89% rename from lib/include/components/ai.hpp rename to app/include/components/ai.hpp index 87922d2..118b0d4 100644 --- a/lib/include/components/ai.hpp +++ b/app/include/components/ai.hpp @@ -1,6 +1,6 @@ #pragma once -namespace SupaRL { +namespace cpprl { enum class AIType { HOSTILE, CONFUSION diff --git a/app/include/game_entity.hpp b/app/include/game_entity.hpp index d4e173f..2848854 100644 --- a/app/include/game_entity.hpp +++ b/app/include/game_entity.hpp @@ -4,7 +4,6 @@ #include #include -#include "basic_ai_component.hpp" #include "types/world_fwd.hpp" #include #include @@ -14,8 +13,6 @@ extern SupaRL::Coordinator g_coordinator; namespace cpprl { - class TransformComponent; - class AttackComponent; class ConsumableComponent; class StatsComponent; class Container; @@ -24,9 +21,7 @@ namespace cpprl { private: SupaRL::Entity id_; - std::unique_ptr attackComponent_; std::unique_ptr consumableComponent_; - std::unique_ptr aiComponent_; std::unique_ptr container_; std::unique_ptr statsComponent_; @@ -40,13 +35,6 @@ namespace cpprl { ConsumableComponent& get_consumable_component() { return *consumableComponent_; }; - std::optional> get_ai_component() { - if (aiComponent_) { - return std::ref(*aiComponent_); - } else { - return std::nullopt; - } - }; std::optional> get_stats_component() { if (statsComponent_) { @@ -56,59 +44,40 @@ namespace cpprl { } }; - std::unique_ptr transfer_ai_component(); Container& get_container() { return *container_; }; float get_distance_to(Entity* other) const; void update(World& world); - void set_attack_component(std::unique_ptr attackComponent); void set_consumable_component( std::unique_ptr consumableComponent); - void set_ai_component(std::unique_ptr aiComponent); void set_container(std::unique_ptr container); void set_stats_component(std::unique_ptr aiComponent); template void pack(Archive& archive) { - archive(attackComponent_ != nullptr); archive(consumableComponent_ != nullptr); - archive(aiComponent_ != nullptr); archive(container_ != nullptr); archive(statsComponent_ != nullptr); - if (attackComponent_) archive(attackComponent_); if (consumableComponent_) archive(consumableComponent_); - if (aiComponent_) archive(aiComponent_); if (container_) archive(container_); if (statsComponent_) archive(statsComponent_); } template void unpack(Archive& archive) { - bool hasDefenseComponent; - bool hasAttackComponent; bool hasConsumableComponent; - bool hasAIComponent; bool hasContainer; bool hasStatsComponent; - archive(hasDefenseComponent); - archive(hasAttackComponent); archive(hasConsumableComponent); - archive(hasAIComponent); archive(hasContainer); archive(hasStatsComponent); - if (hasAttackComponent) { - archive(attackComponent_); - } if (hasConsumableComponent) { archive(consumableComponent_); } - if (hasAIComponent) { - archive(aiComponent_); - } if (hasContainer) { archive(container_); } diff --git a/app/include/systems/ai_system.hpp b/app/include/systems/ai_system.hpp new file mode 100644 index 0000000..41eb1c2 --- /dev/null +++ b/app/include/systems/ai_system.hpp @@ -0,0 +1,22 @@ +#pragma once +#include "world.hpp" + +#include + +#include + +namespace cpprl { + class AISystem : public SupaRL::System + { + private: + SupaRL::Entity player_; + std::shared_ptr world_; + + public: + // implicit copy constructor when passing reference + void set_world(std::shared_ptr world) { world_ = world; } + void set_player(SupaRL::Entity player) { player_ = player; } + void update(); + }; + +} diff --git a/app/src/basic_ai_component.cpp b/app/src/basic_ai_component.cpp index a72b8df..661f497 100644 --- a/app/src/basic_ai_component.cpp +++ b/app/src/basic_ai_component.cpp @@ -23,34 +23,6 @@ bool can_path_to_target(tcod::BresenhamLine& path, World& world) { } void HostileAI::update(World& world, Entity* entity) { - auto position = g_coordinator.get_component(entity->get_id()).position_; - if (world.get_map().is_in_fov(position)) { - Entity* player = world.get_player(); - auto player_position = g_coordinator.get_component(player->get_id()).position_; - SupaRL::Vector2D delta = player_position - position; - - int distance = std::max(std::abs(delta.x), std::abs(delta.y)); - if (distance <= 1) { - auto melee_command = MeleeCommand(world, entity, delta); - melee_command.execute(); - } - - tcod::BresenhamLine path = - tcod::BresenhamLine({position.x, position.y}, {player_position.x, player_position.y}).without_endpoints(); - - if (can_path_to_target(path, world)) { - auto dest = path[0]; - auto destination = SupaRL::Vector2D{dest[0], dest[1]} - position; - - auto action = MovementCommand(world, entity, destination); - action.execute(); - - return; - } - - auto action = NoOpEvent(world); - action.execute(); - } } ConfusionAI::ConfusionAI(int num_turns, std::unique_ptr old_ai) diff --git a/app/src/entity_manager.cpp b/app/src/entity_manager.cpp index 8e6228e..6302bf7 100644 --- a/app/src/entity_manager.cpp +++ b/app/src/entity_manager.cpp @@ -5,7 +5,6 @@ #include #include -#include "basic_ai_component.hpp" #include "consumable_factory.hpp" #include "util.hpp" diff --git a/app/src/systems/ai_system.cpp b/app/src/systems/ai_system.cpp new file mode 100644 index 0000000..12d67cb --- /dev/null +++ b/app/src/systems/ai_system.cpp @@ -0,0 +1,59 @@ +#include "systems/ai_system.hpp" +#include +#include +#include +#include "events/command.hpp" + +extern SupaRL::Coordinator g_coordinator; + +namespace cpprl { + // TODO: implement Bresenham line + bool can_path_to_target(tcod::BresenhamLine& path, World& world) { + for (const auto [x, y] : path) { + if (world.get_entities().get_blocking_entity_at({x, y})) { + return false; + } + } + + return true; +} + + void AISystem::update() { + for (const auto& entity : entities_) { + auto& ai_component = g_coordinator.get_component( + entity); + if (ai_component.type_ == AIType::HOSTILE) { + auto position = g_coordinator.get_component(entity).position_; + // we need to know where the player is. + if (world_->get_map().is_in_fov(position)) { + auto& player_position = g_coordinator.get_component(player_).position_; + SupaRL::Vector2D delta = player_position - position; + + int distance = std::max(std::abs(delta.x), std::abs(delta.y)); + if (distance <= 1) { + auto melee_command = MeleeCommand(*world_, entity, delta); + melee_command.execute(); + } + + tcod::BresenhamLine path = + tcod::BresenhamLine({position.x, position.y}, {player_position.x, player_position.y}).without_endpoints(); + + if (can_path_to_target(path, world_)) { + auto dest = path[0]; + auto destination = SupaRL::Vector2D{dest[0], dest[1]} - position; + + auto action = MovementCommand(world_, entity, destination); + action.execute(); + + return; + } + + auto action = NoOpEvent(world_); + action.execute(); + } + } + } + } + + +} From af26f93a3a492b22aec60b453c8cdef41e48febd Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Wed, 23 Oct 2024 21:17:00 +0200 Subject: [PATCH 31/38] feat(ecs): add conversion to commands --- .clang-format | 2 +- app/include/entity_manager.hpp | 1 + app/include/events/command.hpp | 2 ++ app/src/entity_manager.cpp | 11 +++++++++++ app/src/events/command.cpp | 7 +++++++ app/src/systems/ai_system.cpp | 16 ++++++++-------- 6 files changed, 30 insertions(+), 9 deletions(-) diff --git a/.clang-format b/.clang-format index 106b9a1..3bb0ddc 100644 --- a/.clang-format +++ b/.clang-format @@ -8,7 +8,7 @@ DerivePointerAlignment: false PointerAlignment: Left # Less-compact, but more Git friendly style options. -AlignAfterOpenBracket: AlwaysBreak +AlignAfterOpenBracket: AldestinationwaysBreak BinPackArguments: false BinPackParameters: false AlignTrailingComments: false diff --git a/app/include/entity_manager.hpp b/app/include/entity_manager.hpp index 70942d2..55176b0 100644 --- a/app/include/entity_manager.hpp +++ b/app/include/entity_manager.hpp @@ -37,6 +37,7 @@ namespace cpprl { SupaRL::Vector2D position, float range) const; std::vector> get_entities_at( SupaRL::Vector2D position); + std::optional> get_entity(SupaRL::Entity entity); void place_entities( RectangularRoom room, int max_monsters_per_room, int max_items_per_room); Entity* spawn(Entity* entity); diff --git a/app/include/events/command.hpp b/app/include/events/command.hpp index 481eb26..26e2b2a 100644 --- a/app/include/events/command.hpp +++ b/app/include/events/command.hpp @@ -4,6 +4,7 @@ #include #include "types/state_result.hpp" #include "types/world_fwd.hpp" +#include namespace cpprl { class UiWindow; @@ -24,6 +25,7 @@ namespace cpprl { public: Command(World& world, Entity* entity) : EngineEvent(world), entity_(entity) {} + Command(World& world, SupaRL::Entity entity); virtual ~Command() {} virtual StateResult execute() = 0; }; diff --git a/app/src/entity_manager.cpp b/app/src/entity_manager.cpp index 6302bf7..3f034b9 100644 --- a/app/src/entity_manager.cpp +++ b/app/src/entity_manager.cpp @@ -110,6 +110,17 @@ namespace cpprl { return entities_at_position; } + std::optional> + EntityManager::get_entity(SupaRL::Entity entity_) { + for( const auto& entity : entities_) { + const auto& entity_id = entity->get_id(); + if(entity_id == entity_) { + return std::reference_wrapper(*entity); + } + } + return std::nullopt; + } + std::optional> EntityManager::get_blocking_entity_at(SupaRL::Vector2D position) { for (const auto& entity : entities_) { diff --git a/app/src/events/command.cpp b/app/src/events/command.cpp index ec7684a..5dc7d9a 100644 --- a/app/src/events/command.cpp +++ b/app/src/events/command.cpp @@ -18,6 +18,13 @@ extern SupaRL::Coordinator g_coordinator; namespace cpprl { + Command::Command(World& world, SupaRL::Entity entity) : EngineEvent(world) { + auto& entity_manager = world.get_entities(); + auto entity_obj = entity_manager.get_entity(entity); + if(entity_obj.has_value()) { + // TODO: Convert from SupaRL::Entity to Entity from cpprl + } + } StateResult PickupCommand::execute() { auto entity_position = g_coordinator.get_component( diff --git a/app/src/systems/ai_system.cpp b/app/src/systems/ai_system.cpp index 12d67cb..008040b 100644 --- a/app/src/systems/ai_system.cpp +++ b/app/src/systems/ai_system.cpp @@ -9,14 +9,14 @@ extern SupaRL::Coordinator g_coordinator; namespace cpprl { // TODO: implement Bresenham line bool can_path_to_target(tcod::BresenhamLine& path, World& world) { - for (const auto [x, y] : path) { - if (world.get_entities().get_blocking_entity_at({x, y})) { - return false; + for (const auto [x, y] : path) { + if (world.get_entities().get_blocking_entity_at({x, y})) { + return false; + } } - } - return true; -} + return true; + } void AISystem::update() { for (const auto& entity : entities_) { @@ -38,11 +38,11 @@ namespace cpprl { tcod::BresenhamLine path = tcod::BresenhamLine({position.x, position.y}, {player_position.x, player_position.y}).without_endpoints(); - if (can_path_to_target(path, world_)) { + if (can_path_to_target(path, *world_)) { auto dest = path[0]; auto destination = SupaRL::Vector2D{dest[0], dest[1]} - position; - auto action = MovementCommand(world_, entity, destination); + auto action = MovementCommand(*world_, entity, destination); action.execute(); return; From d1e22125fd6d1bca38c61939a4a4819edb50b498 Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Thu, 24 Oct 2024 18:14:08 +0100 Subject: [PATCH 32/38] feat(ecs): removing old ai comp --- app/include/components/ai.hpp | 5 ++-- app/include/events/command.hpp | 19 +++++++++++++++ app/src/basic_ai_component.cpp | 42 ---------------------------------- app/src/components.cpp | 1 - app/src/entity_factory.cpp | 13 ++++++++--- app/src/entity_manager.cpp | 4 +--- app/src/events/command.cpp | 2 ++ 7 files changed, 35 insertions(+), 51 deletions(-) delete mode 100644 app/src/basic_ai_component.cpp diff --git a/app/include/components/ai.hpp b/app/include/components/ai.hpp index 118b0d4..1fdf5bc 100644 --- a/app/include/components/ai.hpp +++ b/app/include/components/ai.hpp @@ -3,12 +3,13 @@ namespace cpprl { enum class AIType { HOSTILE, - CONFUSION + CONFUSION, + NONE }; // AI component struct AIComponent { AIType type_; - AIType prevuious_type_; + AIType previous_type_; }; } diff --git a/app/include/events/command.hpp b/app/include/events/command.hpp index 26e2b2a..277bfe2 100644 --- a/app/include/events/command.hpp +++ b/app/include/events/command.hpp @@ -52,6 +52,8 @@ namespace cpprl { class PickupCommand : public Command { public: PickupCommand(World& world, Entity* entity) : Command(world, entity){}; + PickupCommand(World& world, SupaRL::Entity entity) + : Command(world, entity){}; StateResult execute() override; }; @@ -63,6 +65,8 @@ namespace cpprl { public: DropItemCommand(World& world, Entity* entity, int item_index) : Command(world, entity), item_index_(item_index){}; + DropItemCommand(World& world, SupaRL::Entity entity, int item_index) + : Command(world, entity), item_index_(item_index) {}; ~DropItemCommand() override = default; StateResult execute() override; }; @@ -70,6 +74,7 @@ namespace cpprl { class InventoryCommand final : public Command { public: InventoryCommand(World& world, Entity* entity) : Command(world, entity) {} + InventoryCommand(World& world, SupaRL::Entity entity) : Command(world, entity){}; StateResult execute() override; }; @@ -110,6 +115,14 @@ namespace cpprl { : Command(world, entity), sub_command_(sub_command), ui_window_(ui_window) {} + SelectItemCommand( + World& world, + SupaRL::Entity entity, + UiWindow& ui_window, + ItemSubCommand sub_command) + : Command(world, entity), + sub_command_(sub_command), + ui_window_(ui_window){}; StateResult execute() override; }; @@ -131,6 +144,8 @@ namespace cpprl { public: UseItemCommand(World& world, Entity* entity, int item_index) : Command(world, entity), item_index_(item_index) {} + UseItemCommand(World& world, SupaRL::Entity entity, int item_index) + : Command(world, entity), item_index_(item_index){}; StateResult execute() override; }; @@ -168,6 +183,8 @@ namespace cpprl { public: DirectionalCommand(World& world, Entity* entity, SupaRL::Vector2D move_vector) : Command(world, entity), move_vector_(move_vector){}; + DirectionalCommand(World& world, SupaRL::Entity entity, SupaRL::Vector2D move_vector) + : Command(world, entity), move_vector_(move_vector){}; virtual StateResult execute(); }; @@ -221,6 +238,8 @@ namespace cpprl { public: MovementCommand(World& world, Entity* entity, SupaRL::Vector2D move_vector) : DirectionalCommand(world, entity, move_vector){}; + MovementCommand(World& world, SupaRL::Entity entity, SupaRL::Vector2D move_vector) + : DirectionalCommand(world, entity, move_vector){}; virtual StateResult execute(); }; class QuitCommand : public EngineEvent { diff --git a/app/src/basic_ai_component.cpp b/app/src/basic_ai_component.cpp deleted file mode 100644 index 661f497..0000000 --- a/app/src/basic_ai_component.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "basic_ai_component.hpp" - -#include -#include - -#include "events/command.hpp" -#include "game_entity.hpp" -#include "entity_manager.hpp" -#include "world.hpp" -#include - -extern SupaRL::Coordinator g_coordinator; -namespace cpprl { - -bool can_path_to_target(tcod::BresenhamLine& path, World& world) { - for (const auto [x, y] : path) { - if (world.get_entities().get_blocking_entity_at({x, y})) { - return false; - } - } - - return true; -} - -void HostileAI::update(World& world, Entity* entity) { -} - -ConfusionAI::ConfusionAI(int num_turns, std::unique_ptr old_ai) - : num_turns_(num_turns), old_ai_(std::move(old_ai)) {} -void ConfusionAI::update(World& world, Entity* entity) { - TCODRandom* random = TCODRandom::getInstance(); - int dx = random->getInt(-1, 1); - int dy = random->getInt(-1, 1); - if ((dx != 0 || dy != 0) && num_turns_ > 0) { - auto action = DirectionalCommand(world, entity, {dx, dy}); - action.execute(); - --num_turns_; - } else { - entity->set_ai_component(std::move(old_ai_)); - } -} -} // namespace cpprl diff --git a/app/src/components.cpp b/app/src/components.cpp index a316536..af8520f 100644 --- a/app/src/components.cpp +++ b/app/src/components.cpp @@ -5,7 +5,6 @@ #include #include -#include "basic_ai_component.hpp" #include "combat_system.hpp" #include "entity_manager.hpp" #include "game_entity.hpp" diff --git a/app/src/entity_factory.cpp b/app/src/entity_factory.cpp index 6e243af..4d8a40e 100644 --- a/app/src/entity_factory.cpp +++ b/app/src/entity_factory.cpp @@ -1,8 +1,8 @@ #include "entity_factory.hpp" -#include "basic_ai_component.hpp" #include "colours.hpp" #include "components.hpp" +#include "components/ai.hpp" #include "core/coordinator.hpp" #include "components/defence.hpp" #include "components/attack.hpp" @@ -22,6 +22,7 @@ namespace cpprl { auto entity = new Entity(); SupaRL::Entity entity_id = g_coordinator.create_entity(); entity->set_id(entity_id); + g_coordinator.add_component(entity_id, SupaRL::AsciiComponent{ .symbol_ = symbol, .colour_ = SupaRL::ColourRGB{.r = color.r, .g = color.g, .b = color.b }, @@ -44,10 +45,13 @@ namespace cpprl { Entity* entity = create_base("Orc", DARK_GREEN, "o"); auto entity_id = entity->get_id(); - entity->set_ai_component(std::make_unique()); entity->set_stats_component( std::make_unique(10, 1, 10, 10, 2)); + g_coordinator.add_component(entity_id, AIComponent{ + .type_ = AIType::HOSTILE, + .previous_type_ = AIType::NONE}); + g_coordinator.add_component(entity_id, SupaRL::AttackComponent{ .damage_ = 3}); g_coordinator.add_component(entity_id, SupaRL::DefenceComponent{ @@ -66,12 +70,15 @@ namespace cpprl { Entity* TrollFactory::create(SupaRL::Vector2D at_position) { Entity* entity = create_base("Troll", DARK_GREEN, "T"); - entity->set_ai_component(std::make_unique()); entity->set_stats_component( std::make_unique(20, 1, 10, 20, 2)); auto entity_id = entity->get_id(); + g_coordinator.add_component(entity_id, AIComponent{ + .type_ = AIType::HOSTILE, + .previous_type_ = AIType::NONE}); + g_coordinator.add_component(entity_id, SupaRL::AttackComponent{ .damage_ = 4}); g_coordinator.add_component(entity_id, SupaRL::DefenceComponent{ diff --git a/app/src/entity_manager.cpp b/app/src/entity_manager.cpp index 3f034b9..205e890 100644 --- a/app/src/entity_manager.cpp +++ b/app/src/entity_manager.cpp @@ -166,10 +166,8 @@ namespace cpprl { for (const auto& entity : entities_) { auto& defence_component = g_coordinator.get_component( entity->get_id()); - const std::optional> ai_component = - entity->get_ai_component(); - if (ai_component.has_value() && defence_component.is_not_dead()) { + if (defence_component.is_not_dead()) { auto entity_position = g_coordinator.get_component( entity->get_id()).position_; float distance = position.distance_to( diff --git a/app/src/events/command.cpp b/app/src/events/command.cpp index 5dc7d9a..77854ca 100644 --- a/app/src/events/command.cpp +++ b/app/src/events/command.cpp @@ -21,8 +21,10 @@ namespace cpprl { Command::Command(World& world, SupaRL::Entity entity) : EngineEvent(world) { auto& entity_manager = world.get_entities(); auto entity_obj = entity_manager.get_entity(entity); + if(entity_obj.has_value()) { // TODO: Convert from SupaRL::Entity to Entity from cpprl + entity_ = &entity_obj.value().get(); } } From 8f5d10663a8413228ce76dc9e141f07036fdbb4c Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Thu, 24 Oct 2024 18:36:50 +0100 Subject: [PATCH 33/38] feat(ecs): remove refs to old ai comp --- app/include/events/command.hpp | 2 ++ app/src/components.cpp | 16 ++++++++++++---- app/src/game_entity.cpp | 12 ------------ app/src/systems/ai_system.cpp | 2 ++ app/src/systems/enemy_turns.cpp | 21 --------------------- 5 files changed, 16 insertions(+), 37 deletions(-) delete mode 100644 app/src/systems/enemy_turns.cpp diff --git a/app/include/events/command.hpp b/app/include/events/command.hpp index 277bfe2..d25948f 100644 --- a/app/include/events/command.hpp +++ b/app/include/events/command.hpp @@ -231,6 +231,8 @@ namespace cpprl { public: MeleeCommand(World& world, Entity* entity, SupaRL::Vector2D target_vector) : DirectionalCommand(world, entity, target_vector){}; + MeleeCommand(World& world, SupaRL::Entity entity, SupaRL::Vector2D target_vector) + : DirectionalCommand(world, entity, target_vector){}; StateResult execute(); }; diff --git a/app/src/components.cpp b/app/src/components.cpp index af8520f..7cdd077 100644 --- a/app/src/components.cpp +++ b/app/src/components.cpp @@ -10,6 +10,7 @@ #include "game_entity.hpp" #include "state.hpp" #include "world.hpp" +#include "components/ai.hpp" #include #include #include @@ -198,11 +199,18 @@ namespace cpprl { auto& target = optional_ref_target.value().get(); auto& entity_name = g_coordinator.get_component( target.get_id()).name_; - std::unique_ptr old_ai = target.transfer_ai_component(); - std::unique_ptr confusion_ai = - std::make_unique(num_turns_, std::move(old_ai)); - target.set_ai_component(std::move(confusion_ai)); + auto& ai_component = g_coordinator.get_component( + target.get_id()); + + auto& status_condition = g_coordinator.get_component( + target.get_id()); + + status_condition.max_ticks_ = num_turns_; + status_condition.name_ = "confused"; + ai_component.previous_type_ = ai_component.type_; + ai_component.type_ = AIType::CONFUSION; + world.get_message_log().add_message( fmt::format( "The eyes of the {} look vacant, as it starts to " diff --git a/app/src/game_entity.cpp b/app/src/game_entity.cpp index 63e9893..c7e82a3 100644 --- a/app/src/game_entity.cpp +++ b/app/src/game_entity.cpp @@ -2,23 +2,15 @@ #include -#include "basic_ai_component.hpp" #include "components.hpp" #include "world.hpp" namespace cpprl { - // Cry. We need to move this to a component. - void Entity::update(World& world) { aiComponent_->update(world, this); } - void Entity::set_consumable_component( std::unique_ptr consumableComponent) { consumableComponent_ = std::move(consumableComponent); }; - void Entity::set_ai_component(std::unique_ptr aiComponent) { - aiComponent_ = std::move(aiComponent); - }; - void Entity::set_stats_component( std::unique_ptr statsComponent) { statsComponent_ = std::move(statsComponent); @@ -27,8 +19,4 @@ namespace cpprl { void Entity::set_container(std::unique_ptr container) { container_ = std::move(container); }; - - std::unique_ptr Entity::transfer_ai_component() { - return std::move(aiComponent_); - }; } diff --git a/app/src/systems/ai_system.cpp b/app/src/systems/ai_system.cpp index 008040b..3cc1f07 100644 --- a/app/src/systems/ai_system.cpp +++ b/app/src/systems/ai_system.cpp @@ -1,4 +1,6 @@ #include "systems/ai_system.hpp" +#include "entity_manager.hpp" +#include "world.hpp" #include #include #include diff --git a/app/src/systems/enemy_turns.cpp b/app/src/systems/enemy_turns.cpp deleted file mode 100644 index 5bd0255..0000000 --- a/app/src/systems/enemy_turns.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "systems/enemy_turns.hpp" -#include "core/coordinator.hpp" -#include "components/defence.hpp" - -extern SupaRL::Coordinator g_coordinator; - -void EnemyTurnsSystem::update() { - for (const auto& entity : entities_) { - // so this was used to get all the moveable entities - // that were controlled by the engine. We'll need to create a new - // ai component so that this system runs on only things that have an AI - // component and a defence component. - /*const std::optional> ai_component =*/ - /* entity->get_ai_component();*/ - auto& defence_component = g_coordinator.get_component( - entity->get_id()); - if (defence_component.is_not_dead()) { - entity->update(*this); - } - } -} From c67f506953b9c8dfb7b1039f5fcf1e73c58d3a6f Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Fri, 25 Oct 2024 20:11:06 +0100 Subject: [PATCH 34/38] feat(ecs): remove get ai comp from world --- app/src/world.cpp | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/app/src/world.cpp b/app/src/world.cpp index 8187c5c..451c726 100644 --- a/app/src/world.cpp +++ b/app/src/world.cpp @@ -91,24 +91,6 @@ namespace cpprl { } } - void World::handle_enemy_turns() { - for (const auto& entity : *entities_) { - const std::optional> ai_component = - entity->get_ai_component(); - try { - auto& defence_component = g_coordinator.get_component( - entity->get_id()); - if (ai_component.has_value() && - defence_component.is_not_dead()) { - entity->update(*this); - } - } catch (std::exception& e) { - // TODO: this is a hack until the enemy turn system is in place. - continue; - } - } - } - void World::spawn_player() { auto player_factory_ = std::make_unique(); auto spawn_position = dungeon_.get_map().get_rooms().at(0).get_center(); From b81713349b8fd7886068cfb3759d0c6abf1c3594 Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Fri, 25 Oct 2024 20:18:14 +0100 Subject: [PATCH 35/38] feat(ecs): Setup ai system --- app/include/engine.hpp | 3 ++- app/include/systems/ai_system.hpp | 4 ++-- app/include/world.hpp | 1 - app/src/engine.cpp | 15 ++++++++++++++- app/src/systems/ai_system.cpp | 2 +- 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/app/include/engine.hpp b/app/include/engine.hpp index 570ac8b..28c4e11 100644 --- a/app/include/engine.hpp +++ b/app/include/engine.hpp @@ -13,6 +13,7 @@ #include "rendering.hpp" #include "systems/physics_system.hpp" #include "systems/status_condition_system.hpp" +#include "systems/ai_system.hpp" namespace cpprl { @@ -30,12 +31,12 @@ namespace cpprl { std::unique_ptr engine_state_; std::shared_ptr physics_system_; std::shared_ptr status_condition_system_; + std::shared_ptr ai_system_; int argc_; char** argv_; void generate_map(int width, int height); - void handle_enemy_turns(); public: Engine(); diff --git a/app/include/systems/ai_system.hpp b/app/include/systems/ai_system.hpp index 41eb1c2..e6c0204 100644 --- a/app/include/systems/ai_system.hpp +++ b/app/include/systems/ai_system.hpp @@ -10,11 +10,11 @@ namespace cpprl { { private: SupaRL::Entity player_; - std::shared_ptr world_; + World& world_; public: // implicit copy constructor when passing reference - void set_world(std::shared_ptr world) { world_ = world; } + void set_world(World& world) { world_ = world; } void set_player(SupaRL::Entity player) { player_ = player; } void update(); }; diff --git a/app/include/world.hpp b/app/include/world.hpp index efb35c7..cff7934 100644 --- a/app/include/world.hpp +++ b/app/include/world.hpp @@ -37,7 +37,6 @@ namespace cpprl { void generate_map(int width, int height, bool with_entities = false); Dungeon& get_dungeon() { return dungeon_; } void render(Renderer& renderer); - void handle_enemy_turns(); void scroll_current_view(int scroll_amount); void handle_player_death(); void set_targeting_tile( diff --git a/app/src/engine.cpp b/app/src/engine.cpp index 28bf1cb..1035f79 100644 --- a/app/src/engine.cpp +++ b/app/src/engine.cpp @@ -26,8 +26,10 @@ #include "components/ascii.hpp" #include "components/physique.hpp" #include "components/identity.hpp" +#include "components/ai.hpp" #include "systems/status_condition_system.hpp" #include "systems/combat_system.hpp" +#include "systems/ai_system.hpp" #include "core/types.hpp" // TODO: Service Locator pattern? @@ -61,6 +63,7 @@ namespace cpprl { g_coordinator.register_component(); g_coordinator.register_component(); g_coordinator.register_component(); + g_coordinator.register_component(); status_condition_system_ = g_coordinator.register_system(); { @@ -69,6 +72,14 @@ namespace cpprl { signature.set(g_coordinator.get_component_type()); g_coordinator.set_system_signature(signature); } + + ai_system_ = g_coordinator.register_system(); + { + SupaRL::Signature signature; + signature.set(g_coordinator.get_component_type()); + signature.set(g_coordinator.get_component_type()); + g_coordinator.set_system_signature(signature); + } /**/ /*g_coordinator.register_system();*/ /*{*/ @@ -123,6 +134,8 @@ namespace cpprl { // when trying to load. Can't even inspect the file in the browser. // gets as far as setting the dungeon seed and then blows up. serializer.deserialize(*world_); + ai_system_->set_world(world_.get()); + ai_system_->set_player(world_->get_player()); engine_state_->on_exit(); engine_state_ = std::make_unique(*world_); @@ -156,7 +169,6 @@ namespace cpprl { load(); } else if (std::holds_alternative(result)) { status_condition_system_->update(); - world_->handle_enemy_turns(); auto playerId = world_->get_player()->get_id(); auto& player_defence = g_coordinator.get_component(playerId); if (player_defence.is_dead()) { @@ -165,6 +177,7 @@ namespace cpprl { engine_state_->on_enter(); } physics_system_->update(); + ai_system_->update(); } else if (std::holds_alternative(result)) { // TODO: there's a bug here. We should only save // when exiting the game, not when quitting to the main menu. diff --git a/app/src/systems/ai_system.cpp b/app/src/systems/ai_system.cpp index 3cc1f07..68f21dc 100644 --- a/app/src/systems/ai_system.cpp +++ b/app/src/systems/ai_system.cpp @@ -50,7 +50,7 @@ namespace cpprl { return; } - auto action = NoOpEvent(world_); + auto action = NoOpEvent(*world_); action.execute(); } } From 7ca4dbb09d5c1885fb8f1cac1ec9487ac7598323 Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Sat, 26 Oct 2024 20:08:26 +0100 Subject: [PATCH 36/38] feat(ecs): pass expected params --- app/src/dungeon.cpp | 1 - app/src/engine.cpp | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/dungeon.cpp b/app/src/dungeon.cpp index e06c067..1ceafbc 100644 --- a/app/src/dungeon.cpp +++ b/app/src/dungeon.cpp @@ -1,7 +1,6 @@ #include "dungeon.hpp" #include -#include #include #include "rectangular_room.hpp" diff --git a/app/src/engine.cpp b/app/src/engine.cpp index 1035f79..f4582b0 100644 --- a/app/src/engine.cpp +++ b/app/src/engine.cpp @@ -134,8 +134,8 @@ namespace cpprl { // when trying to load. Can't even inspect the file in the browser. // gets as far as setting the dungeon seed and then blows up. serializer.deserialize(*world_); - ai_system_->set_world(world_.get()); - ai_system_->set_player(world_->get_player()); + ai_system_->set_world(*world_.get()); + ai_system_->set_player(world_->get_player()->get_id()); engine_state_->on_exit(); engine_state_ = std::make_unique(*world_); From b3a793f3cbc59dc5c332d5fca89e686d4ad46fbb Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Sat, 26 Oct 2024 20:09:26 +0100 Subject: [PATCH 37/38] fix(engine): remove get() --- app/src/engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/engine.cpp b/app/src/engine.cpp index f4582b0..0377326 100644 --- a/app/src/engine.cpp +++ b/app/src/engine.cpp @@ -134,7 +134,7 @@ namespace cpprl { // when trying to load. Can't even inspect the file in the browser. // gets as far as setting the dungeon seed and then blows up. serializer.deserialize(*world_); - ai_system_->set_world(*world_.get()); + ai_system_->set_world(*world_); ai_system_->set_player(world_->get_player()->get_id()); engine_state_->on_exit(); From eead35c388e8b909472953ad4eb994c5abb6d833 Mon Sep 17 00:00:00 2001 From: Daniel Baker Date: Sun, 27 Oct 2024 19:04:40 +0000 Subject: [PATCH 38/38] feat(ecs): try to remove ref to world --- app/include/systems/ai_system.hpp | 4 +--- app/src/systems/ai_system.cpp | 28 ++++++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/app/include/systems/ai_system.hpp b/app/include/systems/ai_system.hpp index e6c0204..aa836b6 100644 --- a/app/include/systems/ai_system.hpp +++ b/app/include/systems/ai_system.hpp @@ -10,11 +10,9 @@ namespace cpprl { { private: SupaRL::Entity player_; - World& world_; public: - // implicit copy constructor when passing reference - void set_world(World& world) { world_ = world; } + AISystem(SupaRL::Entity player) : player_(player) {} void set_player(SupaRL::Entity player) { player_ = player; } void update(); }; diff --git a/app/src/systems/ai_system.cpp b/app/src/systems/ai_system.cpp index 68f21dc..e6fb249 100644 --- a/app/src/systems/ai_system.cpp +++ b/app/src/systems/ai_system.cpp @@ -4,15 +4,39 @@ #include #include #include +#include #include "events/command.hpp" extern SupaRL::Coordinator g_coordinator; namespace cpprl { + // TODO: this does't belogn here + /** + * @brief Get the blocking entity at a given position + * If there is no blocking entity at the given position, return -1 + * + * @param entities + * @param position + * @return SupaRL::Entity + */ + SupaRL::Entity get_blocking_entity_at(std::set entities, SupaRL::Vector2D position) { + for (const auto& entity : entities) { + auto& entity_position = g_coordinator.get_component( + entity).position_; + auto entity_is_blocking = g_coordinator.get_component( + entity).is_blocking_; + if (entity_is_blocking && + entity_position == position) { + return entity; + } + } + return -1; + } + // TODO: implement Bresenham line - bool can_path_to_target(tcod::BresenhamLine& path, World& world) { + bool can_path_to_target(tcod::BresenhamLine& path, std::set entities) { for (const auto [x, y] : path) { - if (world.get_entities().get_blocking_entity_at({x, y})) { + if (get_blocking_entity_at(entities, {x,y}) >= 0) { return false; } }