entity.hpp 5.06 KB
Newer Older
1 2 3
#ifndef __ENTITY_HPP__
# define __ENTITY_HPP__

louiz’'s avatar
louiz’ committed
4
#include <world/components.hpp>
louiz’'s avatar
louiz’ committed
5
#include <world/status.hpp>
louiz’'s avatar
louiz’ committed
6 7
#include <world/brain.hpp>
#include <world/work.hpp>
louiz’'s avatar
louiz’ committed
8

louiz’'s avatar
louiz’ committed
9
#include <cassert>
louiz’'s avatar
louiz’ committed
10 11 12
#include <cstdint>
#include <memory>
#include <list>
louiz’'s avatar
louiz’ committed
13

louiz’'s avatar
louiz’ committed
14
using EntityId = uint16_t;
louiz’'s avatar
louiz’ committed
15
using EntityType = uint16_t;
16 17

class Camera;
18
class World;
louiz’'s avatar
louiz’ committed
19
class Work;
20
class Unit;
21

louiz’'s avatar
louiz’ committed
22
class Entity
23 24
{
  friend class Camera;
25 26
  friend class World;

27
public:
louiz’'s avatar
louiz’ committed
28 29 30 31 32 33 34 35
  Entity(const EntityType& type):
    id(++Entity::current_id),
    type(type),
    to_be_deleted(false),
    manipulable(false),
    brain(std::make_unique<Brain>())
  { }

36
  ~Entity();
37

louiz’'s avatar
louiz’ committed
38
  EntityId get_id() const { return this->id; }
39
  EntityType get_type() const { return this->type; }
louiz’'s avatar
louiz’ committed
40

41
  /**
42
   * Regularly update the entity.
43
   */
44
  void tick(World*);
45

louiz’'s avatar
louiz’ committed
46
  void clear_works();
louiz’'s avatar
louiz’ committed
47 48
  void set_work(std::unique_ptr<Work>);
  void queue_work(std::unique_ptr<Work>);
49 50
  void interrupt();
  Work* get_current_work();
louiz’'s avatar
louiz’ committed
51
  const Work* get_current_work() const;
52

louiz’'s avatar
louiz’ committed
53 54 55
  template <typename ComponentClass>
  ComponentClass* get() const
  {
56 57 58 59
    static_assert(std::is_base_of<Component, ComponentClass>::value,
                  "ComponentClass must be a Component.");
    static_assert(ComponentClass::component_type != ComponentType::Invalid,
                  "ComponentClass must set its own type.");
60
    auto index = static_cast<std::size_t>(ComponentClass::component_type);
61
    return static_cast<ComponentClass*>(this->components[index].get());
louiz’'s avatar
louiz’ committed
62 63 64 65 66
  }

  template <typename ComponentClass>
  void add_component(ComponentClass&& pointer)
  {
67 68
    auto index = static_cast<std::size_t>(ComponentClass::element_type::component_type);
    this->components[index] = std::move(pointer);
louiz’'s avatar
louiz’ committed
69
  }
louiz’'s avatar
louiz’ committed
70 71 72
  template <typename StatusType, typename... ArgsType>
  void add_status(World* world, ArgsType&&... args)
  {
73 74 75 76
    auto status = std::make_unique<StatusType>(this, world,
                                               std::forward<ArgsType>(args)...);
    status->apply();
    this->status.push_back(std::move(status));
louiz’'s avatar
louiz’ committed
77
  }
louiz’'s avatar
louiz’ committed
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
  template <typename BrainType, typename... ArgsType>
  void set_brain(World* world, ArgsType&&... args)
  {
    this->brain = std::make_unique<BrainType>(this, world,
                                              std::forward<ArgsType>(args)...);
  }
  template <typename T>
  const T* get_task() const
  {
    auto work = this->get_current_work();
    if (!work)
      return nullptr;
    auto task = work->get_task();
    if (!task)
      return nullptr;
    auto res = dynamic_cast<const T*>(task);
    assert(res);
    // TODO in non-debug build, do not use dynamic cast, use this instead:
    // return static_cast<const T*>(task);
    return res;
  }
99 100 101 102 103
  /**
   * Mark this entity to be removed from the world.
   */
  void kill();
  bool is_dead() const;
louiz’'s avatar
louiz’ committed
104 105 106 107 108 109
  /**
   * Unless this is called, the entity can not be selected or given orders
   * by any player (for example projectiles, ground AOEs etc)
   */
  void make_manipulable();
  bool is_manipulable() const;
louiz’'s avatar
louiz’ committed
110

111
private:
112 113 114 115 116
  Entity& operator=(const Entity&) = delete;
  Entity& operator=(Entity&&) = delete;
  Entity(const Entity&) = delete;
  Entity(Entity&&) = delete;

117
public:
louiz’'s avatar
louiz’ committed
118 119 120
  /**
   * Incremented each type we create a new entity, and used as Entity::id.
   */
louiz’'s avatar
louiz’ committed
121
  static EntityId current_id;
122 123 124
  /**
   * An uniq id for the entity.
   */
louiz’'s avatar
louiz’ committed
125
  EntityId id;
126
  /**
louiz’'s avatar
louiz’ committed
127
   * The type of the entity
128
   */
louiz’'s avatar
louiz’ committed
129
  EntityType type;
130 131 132 133 134
  /**
   * Whether or not the entity should be deleted from the World on the next
   * cleanup.
   */
  bool to_be_deleted;
135
  /**
136
   * The entity name
137
   */
138
  std::string name;
louiz’'s avatar
louiz’ committed
139 140 141 142 143
  /**
   * Whether or not this entity can be manipulated by a player (and thus
   * selected)
   */
  bool manipulable;
louiz’'s avatar
louiz’ committed
144 145 146 147 148 149 150 151 152
  /**
   * A serie of works that the entity has to execute.  For example
   * World::do_path will add a PathWork to the entity, which contains a path
   * and a pointer to Unit::follow_path, receiving that PathWork structure
   * as an argument. World::do_build will push a PathWork (to go to the cell
   * to build) and a BuildWork.
   *
   * Once the Work is done (destination reached, or building complete, or
   * anything), we pop that work. Many works can be queued. For example if
153 154 155 156 157 158
   * we do a shift-move, the PathWork is added at the end of the list. If
   * not, it just replaces all the works in the list.
   *
   * A list is used instead of a queue, though a queue would be almost
   * perfect (push back, pop front only), but we need to be able to traverse
   * the list to check their value etc. So we just use a list and push_back
louiz’'s avatar
louiz’ committed
159
   * and pop_front instead. Should be almost as efficient.
louiz’'s avatar
louiz’ committed
160
   */
louiz’'s avatar
louiz’ committed
161
  std::list<std::unique_ptr<Work>> works;
louiz’'s avatar
louiz’ committed
162 163 164 165 166 167 168 169
  /**
   * A map of components that define what the entity can do. Without a
   * component, it's basically empty and can do nothing in the world.
   *
   * For example an entity that has a world position and can move has a
   * PositionComponent, which tracks the position of the entity and has
   * methods to alter this position.
   */
170
  std::array<std::unique_ptr<Component>, ComponentTypeCount> components;
louiz’'s avatar
louiz’ committed
171 172 173 174
  /**
   * The list of Status that currently affect this entity.
   */
  std::vector<std::unique_ptr<Status>> status;
175 176 177
};

#endif // __ENTITY_HPP__