3 #include <BLIB/AI/PathFinder.hpp>
4 #include <BLIB/Logging.hpp>
5 #include <BLIB/Math.hpp>
6 #include <BLIB/Util/Hashes.hpp>
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);
23 using PathFinder = bl::ai::PathFinder<bl::tmap::Position, PositionHash>;
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>();
36 void AI::update(std::mutex&,
float dt,
float,
float,
float) {
37 standing->forEach([](StandingRow& cs) {
42 spinning->forEach([dt](SpinRow& cs) {
43 cs.get<component::SpinBehavior>()->update(
44 *cs.get<bl::tmap::Position>(), *cs.get<component::Controllable>(), dt);
47 paths->forEach([](FixedPathRow& cs) {
48 cs.get<component::FixedPathBehavior>()->update(*cs.get<bl::tmap::Position>(),
49 *cs.get<component::Controllable>());
52 wandering->forEach([dt](WanderRow& cs) {
53 cs.get<component::WanderBehavior>()->update(
54 *cs.get<bl::tmap::Position>(), *cs.get<component::Controllable>(), dt);
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);
65 auto cs = ecs.getComponentSet<bl::ecs::Require<bl::tmap::Position, component::Movable>>(
68 BL_LOG_ERROR <<
"Cannot pathfind entity without Movable/Position component: "
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) {
80 bl::event::Dispatcher::dispatch<event::PathFindCompleted>({entity,
true});
83 bl::tmap::Position& cpos = *cs.get<bl::tmap::Position>();
84 const bl::tmap::Position& npos = path.path[path.step];
86 cpos.direction = bl::tmap::Position::facePosition(cpos, npos);
88 BL_LOG_INFO <<
"Entity path blocked, recomputing";
89 if (!findPath(entity, path)) {
90 BL_LOG_ERROR <<
"Failed to find new path, aborting";
101 switch (behavior.
type()) {
119 BL_LOG_ERROR <<
"Failed to add standing behavior to entity: " << e;
130 BL_LOG_ERROR <<
"Failed to add spinning behavior to entity: " << e;
140 BL_LOG_ERROR <<
"Failed to add fixed path behavior to entity: " << e;
149 const bl::tmap::Position* pos = owner.
engine().ecs().getComponent<bl::tmap::Position>(e);
151 BL_LOG_ERROR <<
"Cannot add wander behavior to entity (" << e
152 <<
") without position component";
157 e, {radius, sf::Vector2i(pos->position.x, pos->position.y)})) {
158 BL_LOG_ERROR <<
"Failed to add wander behavior to entity: " << e;
170 if (!findPath(entity, path)) {
186 bl::tmap::Position* start = owner.
engine().ecs().getComponent<bl::tmap::Position>(ent);
188 BL_LOG_ERROR <<
"Tried to pathfind an entity without a position: " << ent;
193 const Position& psys = owner.
position();
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);
204 for (
const bl::tmap::Direction dir : dirs) {
205 tpos.direction = dir;
207 const bl::tmap::Position apos = cmap.
adjacentTile(tpos, dir);
209 if (!psys.spaceFree(apos))
continue;
210 adjacent.emplace_back(apos, 1);
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);
221 return PathFinder::findPath(*start, path.
destination, getAdjacent, estDistance, path.
path);
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...
bl::tmap::Position destination
std::vector< bl::tmap::Position > path
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.
Spinning & spinning()
The data for spinning.
Type type() const
The type of behavior this is.
Wander & wander()
The data for wandering.
Standing & standing()
The data for standing still.
@ Wandering
The character is allowed to wander freely.
@ FollowingPath
The character will follow a preset path.
@ StandStill
The character will stand and face a given direction.
@ SpinInPlace
The character will spin in place.
Path & path()
The data for path following.
bl::tmap::Direction facedir
The direction to face.
enum core::file::Behavior::Spinning::Direction spinDir
Direction
The direction to spin.
Contains data for when the behavior type is following a path.
std::uint32_t radius
The radius to stay within, in tiles.
The primary map class that represents a usable map in the game.
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....
bool contains(const bl::tmap::Position &position) const
Returns whether or not the map contains the given position.
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...
AI(Systems &owner)
Construct a new AI system.
void removeAi(bl::ecs::Entity ent)
Removes any ai behavior from the given entity. Does not affect path finding.
bool makeSpinning(bl::ecs::Entity entity, file::Behavior::Spinning::Direction dir)
Makes the given entity spin in place. Replaces any existing behavior.
bool addBehavior(bl::ecs::Entity entity, const file::Behavior &behavior)
Helper function for adding the given behavior to the given entity.
bool makeStanding(bl::ecs::Entity entity, bl::tmap::Direction dir)
Makes the given entity stand in place. Replaces any existing behavior on the entity.
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....
bool makeFollowPath(bl::ecs::Entity entity, const file::Behavior::Path &path)
Make the entity follow a fixed path. Replaces existing behavior.
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...
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.
Owns all primary systems and a reference to the engine.
const bl::engine::Engine & engine() const
Const accessor for the Engine.
Movement & movement()
Returns a reference to the movement system.
Position & position()
Returns a reference to the position system.
Controllable & controllable()
Returns the controllable entity system.
World & world()
Modifiable accessor for the world system.
map::Map & activeMap()
Returns a reference to the active map.