1 #include "AIController.hpp"
2 #include "ChaseState.hpp"
3 #include "FleeState.hpp"
4 #include "IdleState.hpp"
5 #include "RoamState.hpp"
7 #include "../model/geometry.hpp"
8 #include "../rand/GaloisLFSR.hpp"
9 #include "../world/Entity.hpp"
10 #include "../world/World.hpp"
11 #include "../world/WorldCollision.hpp"
15 #include <glm/glm.hpp>
29 AIController::AIController(World &world, GaloisLFSR &rand)
36 , decision_timer(1.0f)
40 , flee_target(nullptr)
43 , seek_target(nullptr)
46 , evade_target(nullptr)
49 , pursuit_target(nullptr)
52 , wander_pos(1.0f, 0.0f, 0.0f)
61 AIController::~AIController() {
65 void AIController::SetState(const AIState &s) {
71 void AIController::Update(Entity &e, float dt) {
72 think_timer.Update(dt);
73 decision_timer.Update(dt);
74 state->Update(*this, e, dt);
77 glm::vec3 displacement(
78 random.SNorm() * wander_disp,
79 random.SNorm() * wander_disp,
80 random.SNorm() * wander_disp
82 if (!iszero(displacement)) {
83 wander_pos = normalize(wander_pos + displacement * dt) * wander_radius;
88 // orient head towards heading
89 glm::vec3 heading(e.Heading());
90 float tgt_pitch = std::atan(heading.y / length(glm::vec2(heading.x, heading.z)));
91 float tgt_yaw = std::atan2(-heading.x, -heading.z);
92 e.SetHead(tgt_pitch, tgt_yaw);
96 glm::vec3 AIController::ControlForce(const Entity &entity, const EntityState &state) const {
98 return GetHaltForce(entity, state);
100 glm::vec3 force(0.0f);
102 if (MaxOutForce(force, GetFleeForce(entity, state), entity.MaxControlForce())) {
107 if (MaxOutForce(force, GetSeekForce(entity, state), entity.MaxControlForce())) {
112 if (MaxOutForce(force, GetEvadeForce(entity, state), entity.MaxControlForce())) {
117 if (MaxOutForce(force, GetPursuitForce(entity, state), entity.MaxControlForce())) {
122 if (MaxOutForce(force, GetWanderForce(entity, state), entity.MaxControlForce())) {
129 Player *AIController::ClosestVisiblePlayer(const Entity &e) noexcept {
130 Player *target = nullptr;
131 float distance = sight_dist;
132 const glm::ivec3 &reference(e.ChunkCoords());
133 Ray aim(e.Aim(reference));
134 for (Player &p : world.Players()) {
135 const Entity &pe = p.GetEntity();
138 const glm::vec3 diff(pe.AbsoluteDifference(e));
139 float dist = length(diff);
140 if (dist > distance) continue;
142 // FOV test, 45° in each direction
143 if (dot(diff / dist, aim.dir) < sight_angle) {
147 // LOS test, assumes all entities are see-through
149 if (world.Intersection(aim, glm::mat4(1.0f), reference, col) && col.depth < dist) {
160 bool AIController::LineOfSight(const Entity &from, const Entity &to) const noexcept {
161 const glm::ivec3 &reference(from.ChunkCoords());
162 Ray aim(from.Aim(reference));
163 const glm::vec3 diff(to.AbsoluteDifference(from));
164 float dist = length(diff);
165 if (dist > sight_dist || dot(diff / dist, aim.dir) < sight_angle) {
169 if (world.Intersection(aim, glm::mat4(1.0f), reference, col) && col.depth < dist) {
177 bool AIController::MayThink() const noexcept {
178 return think_timer.Hit();
181 void AIController::SetThinkInterval(float i) noexcept {
182 think_timer = FineTimer(i);
188 void AIController::CueDecision(
192 decision_timer = FineTimer(minimum + variance * random.SNorm());
193 decision_timer.Start();
196 bool AIController::DecisionDue() const noexcept {
197 return decision_timer.HitOnce();
200 unsigned int AIController::Decide(unsigned int num_choices) noexcept {
201 return random.Next<unsigned int>() % num_choices;
206 void AIController::EnterHalt() noexcept {
210 void AIController::ExitHalt() noexcept {
214 bool AIController::IsHalted() const noexcept {
218 void AIController::SetHaltSpeed(float speed) noexcept {
222 glm::vec3 AIController::GetHaltForce(const Entity &, const EntityState &state) const noexcept {
223 return Halt(state, halt_speed);
228 void AIController::StartFleeing() noexcept {
232 void AIController::StopFleeing() noexcept {
235 flee_target->UnRef();
236 flee_target = nullptr;
240 bool AIController::IsFleeing() const noexcept {
241 return fleeing && flee_target;
244 void AIController::SetFleeTarget(Entity &e) noexcept {
246 flee_target->UnRef();
252 void AIController::SetFleeSpeed(float speed) noexcept {
256 Entity &AIController::GetFleeTarget() noexcept {
260 const Entity &AIController::GetFleeTarget() const noexcept {
264 glm::vec3 AIController::GetFleeForce(const Entity &, const EntityState &state) const noexcept {
265 return Flee(state, GetFleeTarget().GetState(), flee_speed, 2.0f);
270 void AIController::StartSeeking() noexcept {
274 void AIController::StopSeeking() noexcept {
277 seek_target->UnRef();
278 seek_target = nullptr;
282 bool AIController::IsSeeking() const noexcept {
283 return seeking && seek_target;
286 void AIController::SetSeekTarget(Entity &e) noexcept {
288 seek_target->UnRef();
294 void AIController::SetSeekSpeed(float speed) noexcept {
298 Entity &AIController::GetSeekTarget() noexcept {
302 const Entity &AIController::GetSeekTarget() const noexcept {
306 glm::vec3 AIController::GetSeekForce(const Entity &, const EntityState &state) const noexcept {
307 return Seek(state, GetSeekTarget().GetState(), seek_speed, 2.0f);
312 void AIController::StartEvading() noexcept {
316 void AIController::StopEvading() noexcept {
319 evade_target->UnRef();
320 evade_target = nullptr;
324 bool AIController::IsEvading() const noexcept {
325 return evading && evade_target;
328 void AIController::SetEvadeTarget(Entity &e) noexcept {
330 evade_target->UnRef();
336 void AIController::SetEvadeSpeed(float speed) noexcept {
340 Entity &AIController::GetEvadeTarget() noexcept {
341 return *evade_target;
344 const Entity &AIController::GetEvadeTarget() const noexcept {
345 return *evade_target;
348 glm::vec3 AIController::GetEvadeForce(const Entity &, const EntityState &state) const noexcept{
349 glm::vec3 cur_diff(state.Diff(GetEvadeTarget().GetState()));
350 float time_estimate = length(cur_diff) / evade_speed;
351 EntityState pred_state(GetEvadeTarget().GetState());
352 pred_state.block_pos += pred_state.velocity * time_estimate;
353 return Flee(state, pred_state, evade_speed, 2.0f);
358 void AIController::StartPursuing() noexcept {
362 void AIController::StopPursuing() noexcept {
364 if (pursuit_target) {
365 pursuit_target->UnRef();
366 pursuit_target = nullptr;
370 bool AIController::IsPursuing() const noexcept {
371 return pursuing && pursuit_target;
374 void AIController::SetPursuitTarget(Entity &e) noexcept {
375 if (pursuit_target) {
376 pursuit_target->UnRef();
379 pursuit_target->Ref();
382 void AIController::SetPursuitSpeed(float speed) noexcept {
383 pursuit_speed = speed;
386 Entity &AIController::GetPursuitTarget() noexcept {
387 return *pursuit_target;
390 const Entity &AIController::GetPursuitTarget() const noexcept {
391 return *pursuit_target;
394 glm::vec3 AIController::GetPursuitForce(const Entity &, const EntityState &state) const noexcept {
395 glm::vec3 cur_diff(state.Diff(GetPursuitTarget().GetState()));
396 float time_estimate = length(cur_diff) / pursuit_speed;
397 EntityState pred_state(GetPursuitTarget().GetState());
398 pred_state.block_pos += pred_state.velocity * time_estimate;
399 return Seek(state, pred_state, pursuit_speed, 2.0f);
404 void AIController::StartWandering() noexcept {
408 void AIController::StopWandering() noexcept {
412 bool AIController::IsWandering() const noexcept {
416 void AIController::SetWanderParams(
422 wander_speed = speed;
423 wander_dist = distance;
424 wander_radius = radius;
425 wander_disp = displacement;
428 glm::vec3 AIController::GetWanderForce(const Entity &e, const EntityState &state) const noexcept {
429 glm::vec3 wander_target(normalize(e.Heading() * wander_dist + wander_pos) * wander_speed);
430 return TargetVelocity(wander_target, state, 0.5f);
436 void ChaseState::Enter(AIController &ctrl) const {
437 ctrl.SetHaltSpeed(2.0f);
438 ctrl.SetPursuitSpeed(4.0f);
439 ctrl.StartPursuing();
442 void ChaseState::Update(AIController &ctrl, Entity &e, float dt) const {
443 // check if target still alive and in sight
444 if (ctrl.GetPursuitTarget().Dead()) {
448 if (!ctrl.LineOfSight(e, ctrl.GetPursuitTarget())) {
452 // halt if we're close enough, flee if we're too close
453 float dist_sq = length_squared(e.AbsoluteDifference(ctrl.GetPursuitTarget()));
454 if (dist_sq < 8.0f) {
455 ctrl.SetFleeTarget(ctrl.GetPursuitTarget());
457 } else if (dist_sq < 25.0f) {
464 void ChaseState::Exit(AIController &ctrl) const {
471 void FleeState::Enter(AIController &ctrl) const {
472 ctrl.CueDecision(6.0f, 3.0f);
473 ctrl.SetFleeSpeed(4.0f);
477 void FleeState::Update(AIController &ctrl, Entity &e, float dt) const {
478 if (!ctrl.DecisionDue()) return;
482 void FleeState::Exit(AIController &ctrl) const {
488 void IdleState::Enter(AIController &ctrl) const {
489 ctrl.SetHaltSpeed(0.5f);
491 ctrl.SetWanderParams(0.001f, 1.1f);
492 ctrl.CueDecision(10.0f, 5.0f);
495 void IdleState::Update(AIController &ctrl, Entity &e, float dt) const {
496 if (ctrl.MayThink()) {
497 const Player *player = ctrl.ClosestVisiblePlayer(e);
499 ctrl.SetPursuitTarget(player->GetEntity());
500 ctrl.SetState(chase);
505 if (!ctrl.DecisionDue()) return;
507 unsigned int d = ctrl.Decide(10);
509 // .2 chance to start going
512 // .3 chance of looking around
514 ctrl.StartWandering();
516 // .5 chance of doing nothing
517 ctrl.StopWandering();
520 ctrl.CueDecision(10.0f, 5.0f);
523 void IdleState::Exit(AIController &ctrl) const {
525 ctrl.StopWandering();
530 void RoamState::Enter(AIController &ctrl) const {
531 ctrl.SetWanderParams(1.0f);
532 ctrl.StartWandering();
533 ctrl.CueDecision(10.0f, 5.0f);
536 void RoamState::Update(AIController &ctrl, Entity &e, float dt) const {
537 if (ctrl.MayThink()) {
538 const Player *player = ctrl.ClosestVisiblePlayer(e);
540 ctrl.SetPursuitTarget(player->GetEntity());
541 ctrl.SetState(chase);
546 if (!ctrl.DecisionDue()) return;
548 unsigned int d = ctrl.Decide(10);
550 // .1 chance of idling
553 ctrl.CueDecision(10.0f, 5.0f);
556 void RoamState::Exit(AIController &ctrl) const {
557 ctrl.StopWandering();