97 lines
2.7 KiB
C++
97 lines
2.7 KiB
C++
#pragma once
|
|
|
|
#include "GameContext.hpp"
|
|
|
|
#include <concepts>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <vector>
|
|
|
|
struct Entity;
|
|
class Component;
|
|
|
|
/**
|
|
* Base interface that every component derives from. Each component keeps a
|
|
* backref to its owning entity plus the shared game context so it can read
|
|
* global state (scroll offset, meter, entity pointers, reset flags, etc.). The
|
|
* lifecycle follows a `Setup` / `Update` / `Cleanup` pattern invoked by the
|
|
* owning entity.
|
|
*/
|
|
class Component {
|
|
public:
|
|
Entity *entity = nullptr;
|
|
GameContext *context = nullptr;
|
|
|
|
virtual void Setup() = 0;
|
|
virtual void Update(float dt) = 0;
|
|
virtual void Cleanup() = 0;
|
|
|
|
virtual ~Component() = default;
|
|
};
|
|
|
|
struct Entity {
|
|
std::vector<std::shared_ptr<Component>> components;
|
|
GameContext *context = nullptr;
|
|
|
|
/**
|
|
* Injects the global game context/state so the entity and its components can
|
|
* stay in sync with the global scroll/meter/reset state and known
|
|
* entity references.
|
|
*
|
|
* @param ctx Current game-wide context containing globals like scroll, entity handles, and
|
|
* reset flags.
|
|
*/
|
|
void SetContext(GameContext *ctx) {
|
|
context = ctx;
|
|
for (auto &component : components) {
|
|
component->context = ctx;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds a component of type T to the entity, wiring ownership, the game
|
|
* context, and calling `Setup` immediately.
|
|
*
|
|
* @tparam T Component subclass type to add.
|
|
* @return Reference to the newly created component.
|
|
*/
|
|
template <std::derived_from<Component> T> T &AddComponent() {
|
|
auto &ptr = components.emplace_back(std::make_shared<T>());
|
|
ptr->entity = this;
|
|
ptr->context = context;
|
|
ptr->Setup();
|
|
return static_cast<T &>(*ptr);
|
|
}
|
|
|
|
/**
|
|
* Finds the first component of type T in this entity and returns a
|
|
* reference_wrapper if found.
|
|
*
|
|
* @tparam T Component subclass to get.
|
|
* @return Optional ref wrapper to the component if found, otherwise empty.
|
|
*/
|
|
template <std::derived_from<Component> T>
|
|
std::optional<std::reference_wrapper<T>> GetComponent() const {
|
|
for (auto &c : components) {
|
|
T *cast = dynamic_cast<T *>(c.get());
|
|
if (cast) {
|
|
return *cast;
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
/**
|
|
* Propagates the delta time to all owned components so they can run their
|
|
* per-frame logic while sharing the same global context state.
|
|
*
|
|
* @param dt Time elapsed since the last frame.
|
|
*/
|
|
void Update(float dt) {
|
|
for (auto &c : components) {
|
|
c->Update(dt);
|
|
}
|
|
}
|
|
};
|