Files
aee-arcade/source/game/entities/entity.hpp
T

90 lines
3.6 KiB
C++

#pragma once
#include <SDL3/SDL.h>
#include <optional>
#include <string>
#include "game/entities/animation_component.hpp"
// Forward-declaracions: mantenim aquest header lleuger. `Player` viu a
// `source/game/entities/player.hpp` i només es referencia per referència
// dins `onPlayerContact`; `EventBus` és opcional a cada entitat i viu al
// seu header propi.
class Player;
class EventBus;
// Entitat genèrica del món. Actua com a dada en comú per a tots els actors
// del joc (decoratius, interactables, perills mòbils i estàtics): manté
// posició en tiles i píxels, un component d'animació opcional i flags de
// comportament (collides, interactable). Les subclasses intermèdies
// afegeixen semàntica (veure `decoration.hpp`, `interactable.hpp`,
// `hazard.hpp`) i les concretes materialitzen cada tipus (Sarcophagus,
// Torch, Mummy, etc.).
//
// Decisió de disseny: `Entity` NO hereta d'`AnimatedSprite`. L'animació és
// un component (composició), no una herència, per dues raons: (a) no totes
// les entitats en necessiten (trampes invisibles, triggers), i (b) lligar
// el cicle de vida al de `MovingSprite` imposaria semàntica de moviment
// contínu a actors estàtics. Quan s'hi cablege el render real, es podrà
// optar per tenir un `Sprite` intern també com a component opcional.
class Entity {
public:
struct TilePos {
int col{0};
int row{0};
};
Entity() = default;
Entity(std::string name, TilePos tile, SDL_FRect pixel_rect);
virtual ~Entity() = default;
Entity(const Entity&) = delete;
Entity(Entity&&) = delete;
auto operator=(const Entity&) -> Entity& = delete;
auto operator=(Entity&&) -> Entity& = delete;
// Avanç lògic per frame. La base només avança l'animació si existeix;
// les subclasses poden afegir IA, cicles, moviment, etc.
virtual void update(float delta_time);
// Render a la pantalla. La base és no-op: l'andamiatge no cableja encara
// amb el `Screen` real; un cop integrat al `GameScene`, les subclasses
// o un mòdul de render dedicat dibuixaran sprites segons
// `anim_.currentFrame()`.
virtual void render();
// El jugador ha xocat amb aquesta entitat (col·lisió o proximitat).
// Per defecte no fa res; `Hazard` aplica dany, `Door` oberta publica
// `"level_complete"`, etc. Es passa per punter (nullable) perquè els
// smoke tests poden disparar el contacte sense un Player real, i
// perquè en el futur podria disparar-se per triggers d'àrea sense
// jugador específic.
virtual void onPlayerContact(Player* player);
// Hook que el `EntityManager` invoca per a cada entitat cada vegada
// que el jugador entra en un tile nou. Només `Sarcophagus` ho consumeix
// (per marcar perímetre visitat); per defecte no-op.
virtual void notifyTileEntered(int col, int row);
[[nodiscard]] auto getName() const -> const std::string& { return name_; }
[[nodiscard]] auto getTilePos() const -> TilePos { return tile_; }
[[nodiscard]] auto getPixelRect() const -> SDL_FRect { return pixel_rect_; }
[[nodiscard]] auto collides() const -> bool { return collides_; }
[[nodiscard]] auto interactable() const -> bool { return interactable_; }
// Associar el bus una sola vegada (propietat externa, p.ex.
// `EntityManager`). Entitats que no publiquen events (Torch, trampes
// estàtiques de moment) el poden ignorar.
void setEventBus(EventBus* bus) { bus_ = bus; }
protected:
std::string name_;
TilePos tile_{};
SDL_FRect pixel_rect_{};
std::optional<AnimationComponent> anim_;
bool collides_{false};
bool interactable_{false};
EventBus* bus_{nullptr};
};