cs381/as6/Entity.hpp

114 lines
3.1 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;
bool queuedForFree = false;
/**
* 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);
}
}
/**
* Marks the entity for deferred destruction at the end of a systems tick.
* Inspired by Godot!
*/
void QueueFree() { queuedForFree = true; }
/**
* Propagates cleanup call to all owned components so they can release any
* resources or references before the entity is destroyed.
*/
void Cleanup() {
for (auto &c : components) {
c->Cleanup();
}
}
};