Peoplemon  0.1.0
Peoplemon 3 game source documentation
AI.cpp
Go to the documentation of this file.
1 #include <Core/Systems/AI.hpp>
2 
3 #include <BLIB/AI/PathFinder.hpp>
4 #include <BLIB/Logging.hpp>
5 #include <BLIB/Math.hpp>
6 #include <BLIB/Util/Hashes.hpp>
9 
10 namespace core
11 {
12 namespace system
13 {
14 namespace
15 {
16 struct PositionHash {
17  std::size_t operator()(const bl::tmap::Position& pos) const {
18  const std::size_t tileHash = bl::util::Vector2Hash<int>()({pos.position.x, pos.position.y});
19  return bl::util::hashCombine(tileHash, pos.level);
20  }
21 };
22 
23 using PathFinder = bl::ai::PathFinder<bl::tmap::Position, PositionHash>;
24 } // namespace
25 
27 : owner(o) {}
28 
29 void AI::init(bl::engine::Engine&) {
30  standing = owner.engine().ecs().getOrCreateView<StandingTypes>();
31  spinning = owner.engine().ecs().getOrCreateView<SpinTypes>();
32  paths = owner.engine().ecs().getOrCreateView<FixedPathTypes>();
33  wandering = owner.engine().ecs().getOrCreateView<WanderTypes>();
34 }
35 
36 void AI::update(std::mutex&, float dt, float, float, float) {
37  standing->forEach([](StandingRow& cs) {
38  cs.get<component::StandingBehavior>()->update(*cs.get<bl::tmap::Position>(),
39  *cs.get<component::Controllable>());
40  });
41 
42  spinning->forEach([dt](SpinRow& cs) {
43  cs.get<component::SpinBehavior>()->update(
44  *cs.get<bl::tmap::Position>(), *cs.get<component::Controllable>(), dt);
45  });
46 
47  paths->forEach([](FixedPathRow& cs) {
48  cs.get<component::FixedPathBehavior>()->update(*cs.get<bl::tmap::Position>(),
49  *cs.get<component::Controllable>());
50  });
51 
52  wandering->forEach([dt](WanderRow& cs) {
53  cs.get<component::WanderBehavior>()->update(
54  *cs.get<bl::tmap::Position>(), *cs.get<component::Controllable>(), dt);
55  });
56 
57  auto& ecs = owner.engine().ecs();
58  ecs.getAllComponents<component::PathFinder>().forEachWithWrites(
59  [this, &ecs](bl::ecs::Entity entity, component::PathFinder& path) {
60  const auto cleanup = [this, entity, &ecs]() {
61  ecs.removeComponent<component::PathFinder>(entity);
62  owner.controllable().resetEntityLock(entity);
63  };
64 
65  auto cs = ecs.getComponentSet<bl::ecs::Require<bl::tmap::Position, component::Movable>>(
66  entity);
67  if (!cs.isValid()) {
68  BL_LOG_ERROR << "Cannot pathfind entity without Movable/Position component: "
69  << entity;
70  cleanup();
71  return;
72  }
73 
74  if (!cs.get<component::Movable>()->moving()) {
75  if (path.step == path.path.size()) {
76  if (cs.get<bl::tmap::Position>()->direction != path.destination.direction) {
77  owner.movement().moveEntity(entity, path.destination.direction, false);
78  }
79  cleanup();
80  bl::event::Dispatcher::dispatch<event::PathFindCompleted>({entity, true});
81  }
82  else {
83  bl::tmap::Position& cpos = *cs.get<bl::tmap::Position>();
84  const bl::tmap::Position& npos = path.path[path.step];
85  ++path.step;
86  cpos.direction = bl::tmap::Position::facePosition(cpos, npos);
87  if (!owner.movement().moveEntity(entity, cpos.direction, false)) {
88  BL_LOG_INFO << "Entity path blocked, recomputing";
89  if (!findPath(entity, path)) {
90  BL_LOG_ERROR << "Failed to find new path, aborting";
91  cleanup();
92  }
93  // continue and let the motion start on the next frame
94  }
95  }
96  }
97  });
98 }
99 
100 bool AI::addBehavior(bl::ecs::Entity e, const file::Behavior& behavior) {
101  switch (behavior.type()) {
103  return makeStanding(e, behavior.standing().facedir);
105  return makeSpinning(e, behavior.spinning().spinDir);
107  return makeFollowPath(e, behavior.path());
109  return makeWander(e, behavior.wander().radius);
110  default:
111  return false;
112  }
113 }
114 
115 bool AI::makeStanding(bl::ecs::Entity e, bl::tmap::Direction dir) {
116  removeAi(e);
117 
118  if (!owner.engine().ecs().addComponent<component::StandingBehavior>(e, {dir})) {
119  BL_LOG_ERROR << "Failed to add standing behavior to entity: " << e;
120  return false;
121  }
122 
123  return true;
124 }
125 
127  removeAi(e);
128 
129  if (!owner.engine().ecs().addComponent<component::SpinBehavior>(e, {dir})) {
130  BL_LOG_ERROR << "Failed to add spinning behavior to entity: " << e;
131  return false;
132  }
133  return true;
134 }
135 
136 bool AI::makeFollowPath(bl::ecs::Entity e, const file::Behavior::Path& path) {
137  removeAi(e);
138 
139  if (!owner.engine().ecs().addComponent<component::FixedPathBehavior>(e, {path})) {
140  BL_LOG_ERROR << "Failed to add fixed path behavior to entity: " << e;
141  return false;
142  }
143  return true;
144 }
145 
146 bool AI::makeWander(bl::ecs::Entity e, unsigned int radius) {
147  removeAi(e);
148 
149  const bl::tmap::Position* pos = owner.engine().ecs().getComponent<bl::tmap::Position>(e);
150  if (!pos) {
151  BL_LOG_ERROR << "Cannot add wander behavior to entity (" << e
152  << ") without position component";
153  return false;
154  }
155 
156  if (!owner.engine().ecs().addComponent<component::WanderBehavior>(
157  e, {radius, sf::Vector2i(pos->position.x, pos->position.y)})) {
158  BL_LOG_ERROR << "Failed to add wander behavior to entity: " << e;
159  return false;
160  }
161  return true;
162 }
163 
164 bool AI::moveToPosition(bl::ecs::Entity entity, const bl::tmap::Position& dest) {
166  owner.engine().ecs().getComponent<component::Controllable>(entity);
167  if (ctrl) { ctrl->setLocked(true); }
168 
169  component::PathFinder path(dest);
170  if (!findPath(entity, path)) {
171  if (ctrl) { ctrl->resetLock(); }
172  return false;
173  }
174  owner.engine().ecs().emplaceComponent<component::PathFinder>(entity, std::move(path));
175  return true;
176 }
177 
178 void AI::removeAi(bl::ecs::Entity ent) {
179  owner.engine().ecs().removeComponent<component::StandingBehavior>(ent);
180  owner.engine().ecs().removeComponent<component::SpinBehavior>(ent);
181  owner.engine().ecs().removeComponent<component::FixedPathBehavior>(ent);
182  owner.engine().ecs().removeComponent<component::WanderBehavior>(ent);
183 }
184 
185 bool AI::findPath(bl::ecs::Entity ent, component::PathFinder& path) {
186  bl::tmap::Position* start = owner.engine().ecs().getComponent<bl::tmap::Position>(ent);
187  if (!start) {
188  BL_LOG_ERROR << "Tried to pathfind an entity without a position: " << ent;
189  return false;
190  }
191 
192  const map::Map& cmap = owner.world().activeMap();
193  const Position& psys = owner.position();
194 
195  const auto getAdjacent = [&cmap,
196  &psys](const bl::tmap::Position& pos,
197  std::vector<std::pair<bl::tmap::Position, int>>& adjacent) {
198  const std::array<bl::tmap::Direction, 4> dirs{bl::tmap::Direction::Up,
199  bl::tmap::Direction::Right,
200  bl::tmap::Direction::Down,
201  bl::tmap::Direction::Left};
202  bl::tmap::Position tpos(pos.level, pos.position, pos.direction);
203  ;
204  for (const bl::tmap::Direction dir : dirs) {
205  tpos.direction = dir;
206  if (!cmap.movePossible(tpos, dir)) continue;
207  const bl::tmap::Position apos = cmap.adjacentTile(tpos, dir);
208  if (!cmap.contains(apos)) continue;
209  if (!psys.spaceFree(apos)) continue;
210  adjacent.emplace_back(apos, 1);
211  }
212  };
213 
214  const auto estDistance = [](const bl::tmap::Position& p1, const bl::tmap::Position& p2) -> int {
215  const auto diff = p1.position - p2.position;
216  const int tdist = std::abs(diff.x) + std::abs(diff.y);
217  return tdist + (p1.level >= p2.level ? p1.level - p2.level : p2.level - p1.level);
218  };
219 
220  path.step = 0;
221  return PathFinder::findPath(*start, path.destination, getAdjacent, estDistance, path.path);
222 }
223 
224 } // namespace system
225 } // namespace core
Core classes and functionality for both the editor and game.
Adding this component to an entity allows it to be controlled.
void setLocked(bool lock, bool preserve=true)
Locks the controllable and prevents any commands from being processed. Optionally remembers the previ...
void resetLock()
Resets the lock state back to the last preserved state.
Add this component to an entity to make it follow a fixed path.
Component that dynamically navigates an entity to a specific tile. The entities Controllable componen...
Definition: PathFinder.hpp:18
bl::tmap::Position destination
Definition: PathFinder.hpp:28
std::vector< bl::tmap::Position > path
Definition: PathFinder.hpp:29
Add this to an entity to make it spin in place.
AI behavior for standing in place facing a fixed direction.
Add this component to an entity to make it wander around.
Set of behaviors for NPCs and trainers.
Definition: Behavior.hpp:19
Spinning & spinning()
The data for spinning.
Definition: Behavior.cpp:106
Type type() const
The type of behavior this is.
Definition: Behavior.cpp:75
Wander & wander()
The data for wandering.
Definition: Behavior.cpp:114
Standing & standing()
The data for standing still.
Definition: Behavior.cpp:102
@ Wandering
The character is allowed to wander freely.
Definition: Behavior.hpp:33
@ FollowingPath
The character will follow a preset path.
Definition: Behavior.hpp:30
@ StandStill
The character will stand and face a given direction.
Definition: Behavior.hpp:24
@ SpinInPlace
The character will spin in place.
Definition: Behavior.hpp:27
Path & path()
The data for path following.
Definition: Behavior.cpp:110
bl::tmap::Direction facedir
The direction to face.
Definition: Behavior.hpp:39
enum core::file::Behavior::Spinning::Direction spinDir
Direction
The direction to spin.
Definition: Behavior.hpp:51
Contains data for when the behavior type is following a path.
Definition: Behavior.hpp:70
std::uint32_t radius
The radius to stay within, in tiles.
Definition: Behavior.hpp:107
The primary map class that represents a usable map in the game.
Definition: Map.hpp:49
bl::tmap::Position adjacentTile(const bl::tmap::Position &pos, bl::tmap::Direction dir) const
Returns the adjacent position to the given position when moving in the given direction....
Definition: Map.cpp:276
bool contains(const bl::tmap::Position &position) const
Returns whether or not the map contains the given position.
Definition: Map.cpp:271
bool movePossible(const bl::tmap::Position &position, bl::tmap::Direction dir) const
Returns whether or not a particular movement is possible. Does not take into account entities blockin...
Definition: Map.cpp:329
AI(Systems &owner)
Construct a new AI system.
Definition: AI.cpp:26
void removeAi(bl::ecs::Entity ent)
Removes any ai behavior from the given entity. Does not affect path finding.
Definition: AI.cpp:178
bool makeSpinning(bl::ecs::Entity entity, file::Behavior::Spinning::Direction dir)
Makes the given entity spin in place. Replaces any existing behavior.
Definition: AI.cpp:126
bool addBehavior(bl::ecs::Entity entity, const file::Behavior &behavior)
Helper function for adding the given behavior to the given entity.
Definition: AI.cpp:100
bool makeStanding(bl::ecs::Entity entity, bl::tmap::Direction dir)
Makes the given entity stand in place. Replaces any existing behavior on the entity.
Definition: AI.cpp:115
bool moveToPosition(bl::ecs::Entity entity, const bl::tmap::Position &dest)
Uses a pathfinder to navigate the entity to the given position and end up facing the given direction....
Definition: AI.cpp:164
bool makeFollowPath(bl::ecs::Entity entity, const file::Behavior::Path &path)
Make the entity follow a fixed path. Replaces existing behavior.
Definition: AI.cpp:136
bool makeWander(bl::ecs::Entity entity, unsigned int radius)
Makes the given entity wander around within the given radius. Replaces any existing behavior the enti...
Definition: AI.cpp:146
bool resetEntityLock(bl::ecs::Entity entity)
Resets the given entity's lock state to the last remembered value.
bool moveEntity(bl::ecs::Entity entity, bl::tmap::Direction direction, bool fast)
Moves an entity in the given direction using either its fast or slow speed.
Definition: Movement.cpp:27
Owns all primary systems and a reference to the engine.
Definition: Systems.hpp:47
const bl::engine::Engine & engine() const
Const accessor for the Engine.
Definition: Systems.cpp:35
Movement & movement()
Returns a reference to the movement system.
Definition: Systems.cpp:51
Position & position()
Returns a reference to the position system.
Definition: Systems.cpp:47
Controllable & controllable()
Returns the controllable entity system.
Definition: Systems.cpp:63
World & world()
Modifiable accessor for the world system.
Definition: Systems.cpp:43
map::Map & activeMap()
Returns a reference to the active map.
Definition: World.cpp:84