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(Heading(e.GetState()));
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(state);
100 glm::vec3 force(0.0f);
102 if (MaxOutForce(force, GetFleeForce(state), entity.MaxControlForce())) {
107 if (MaxOutForce(force, GetSeekForce(state), entity.MaxControlForce())) {
112 if (MaxOutForce(force, GetEvadeForce(state), entity.MaxControlForce())) {
117 if (MaxOutForce(force, GetPursuitForce(state), entity.MaxControlForce())) {
122 if (MaxOutForce(force, GetWanderForce(state), entity.MaxControlForce())) {
129 glm::vec3 AIController::Heading(const EntityState &state) noexcept {
130 if (dot(state.velocity, state.velocity) > std::numeric_limits<float>::epsilon()) {
131 return normalize(state.velocity);
133 float cp = std::cos(state.pitch);
134 return glm::vec3(std::cos(state.yaw) * cp, std::sin(state.yaw) * cp, std::sin(state.pitch));
138 Player *AIController::ClosestVisiblePlayer(const Entity &e) noexcept {
139 Player *target = nullptr;
140 float distance = sight_dist;
141 const glm::ivec3 &reference(e.ChunkCoords());
142 Ray aim(e.Aim(reference));
143 for (Player &p : world.Players()) {
144 const Entity &pe = p.GetEntity();
147 const glm::vec3 diff(pe.AbsoluteDifference(e));
148 float dist = length_squared(diff);
149 if (dist > distance) continue;
151 // FOV test, 45° in each direction
152 if (dot(normalize(diff), aim.dir) < sight_angle) {
156 // LOS test, assumes all entities are see-through
158 if (world.Intersection(aim, glm::mat4(1.0f), reference, col) && col.depth * col.depth < dist) {
169 bool AIController::LineOfSight(const Entity &from, const Entity &to) const noexcept {
170 const glm::ivec3 &reference(from.ChunkCoords());
171 Ray aim(from.Aim(reference));
172 const glm::vec3 diff(to.AbsoluteDifference(from));
173 float dist = length(diff);
174 if (dist > sight_dist || dot(diff / dist, aim.dir) < sight_angle) {
178 if (world.Intersection(aim, glm::mat4(1.0f), reference, col) && col.depth < dist) {
186 bool AIController::MayThink() const noexcept {
187 return think_timer.Hit();
190 void AIController::SetThinkInterval(float i) noexcept {
191 think_timer = FineTimer(i);
197 void AIController::CueDecision(
201 decision_timer = FineTimer(minimum + variance * random.SNorm());
202 decision_timer.Start();
205 bool AIController::DecisionDue() const noexcept {
206 return decision_timer.HitOnce();
209 unsigned int AIController::Decide(unsigned int num_choices) noexcept {
210 return random.Next<unsigned int>() % num_choices;
215 void AIController::EnterHalt() noexcept {
219 void AIController::ExitHalt() noexcept {
223 bool AIController::IsHalted() const noexcept {
227 void AIController::SetHaltSpeed(float speed) noexcept {
231 glm::vec3 AIController::GetHaltForce(const EntityState &state) const noexcept {
232 return Halt(state, halt_speed);
237 void AIController::StartFleeing() noexcept {
241 void AIController::StopFleeing() noexcept {
244 flee_target->UnRef();
245 flee_target = nullptr;
249 bool AIController::IsFleeing() const noexcept {
250 return fleeing && flee_target;
253 void AIController::SetFleeTarget(Entity &e) noexcept {
255 flee_target->UnRef();
261 void AIController::SetFleeSpeed(float speed) noexcept {
265 Entity &AIController::GetFleeTarget() noexcept {
269 const Entity &AIController::GetFleeTarget() const noexcept {
273 glm::vec3 AIController::GetFleeForce(const EntityState &state) const noexcept {
274 return Flee(state, GetFleeTarget().GetState(), flee_speed, 2.0f);
279 void AIController::StartSeeking() noexcept {
283 void AIController::StopSeeking() noexcept {
286 seek_target->UnRef();
287 seek_target = nullptr;
291 bool AIController::IsSeeking() const noexcept {
292 return seeking && seek_target;
295 void AIController::SetSeekTarget(Entity &e) noexcept {
297 seek_target->UnRef();
303 void AIController::SetSeekSpeed(float speed) noexcept {
307 Entity &AIController::GetSeekTarget() noexcept {
311 const Entity &AIController::GetSeekTarget() const noexcept {
315 glm::vec3 AIController::GetSeekForce(const EntityState &state) const noexcept {
316 return Seek(state, GetSeekTarget().GetState(), seek_speed, 2.0f);
321 void AIController::StartEvading() noexcept {
325 void AIController::StopEvading() noexcept {
328 evade_target->UnRef();
329 evade_target = nullptr;
333 bool AIController::IsEvading() const noexcept {
334 return evading && evade_target;
337 void AIController::SetEvadeTarget(Entity &e) noexcept {
339 evade_target->UnRef();
345 void AIController::SetEvadeSpeed(float speed) noexcept {
349 Entity &AIController::GetEvadeTarget() noexcept {
350 return *evade_target;
353 const Entity &AIController::GetEvadeTarget() const noexcept {
354 return *evade_target;
357 glm::vec3 AIController::GetEvadeForce(const EntityState &state) const noexcept{
358 glm::vec3 cur_diff(state.Diff(GetEvadeTarget().GetState()));
359 float time_estimate = length(cur_diff) / evade_speed;
360 EntityState pred_state(GetEvadeTarget().GetState());
361 pred_state.block_pos += pred_state.velocity * time_estimate;
362 return Flee(state, pred_state, evade_speed, 2.0f);
367 void AIController::StartPursuing() noexcept {
371 void AIController::StopPursuing() noexcept {
373 if (pursuit_target) {
374 pursuit_target->UnRef();
375 pursuit_target = nullptr;
379 bool AIController::IsPursuing() const noexcept {
380 return pursuing && pursuit_target;
383 void AIController::SetPursuitTarget(Entity &e) noexcept {
384 if (pursuit_target) {
385 pursuit_target->UnRef();
388 pursuit_target->Ref();
391 void AIController::SetPursuitSpeed(float speed) noexcept {
392 pursuit_speed = speed;
395 Entity &AIController::GetPursuitTarget() noexcept {
396 return *pursuit_target;
399 const Entity &AIController::GetPursuitTarget() const noexcept {
400 return *pursuit_target;
403 glm::vec3 AIController::GetPursuitForce(const EntityState &state) const noexcept {
404 glm::vec3 cur_diff(state.Diff(GetPursuitTarget().GetState()));
405 float time_estimate = length(cur_diff) / pursuit_speed;
406 EntityState pred_state(GetPursuitTarget().GetState());
407 pred_state.block_pos += pred_state.velocity * time_estimate;
408 return Seek(state, pred_state, pursuit_speed, 2.0f);
413 void AIController::StartWandering() noexcept {
417 void AIController::StopWandering() noexcept {
421 bool AIController::IsWandering() const noexcept {
425 void AIController::SetWanderParams(
431 wander_speed = speed;
432 wander_dist = distance;
433 wander_radius = radius;
434 wander_disp = displacement;
437 glm::vec3 AIController::GetWanderForce(const EntityState &state) const noexcept {
438 glm::vec3 wander_target(normalize(Heading(state) * wander_dist + wander_pos) * wander_speed);
439 return TargetVelocity(wander_target, state, 0.5f);
445 void ChaseState::Enter(AIController &ctrl) const {
446 ctrl.SetHaltSpeed(2.0f);
447 ctrl.SetPursuitSpeed(4.0f);
448 ctrl.StartPursuing();
451 void ChaseState::Update(AIController &ctrl, Entity &e, float dt) const {
452 // check if target still alive and in sight
453 if (ctrl.GetPursuitTarget().Dead()) {
457 if (!ctrl.LineOfSight(e, ctrl.GetPursuitTarget())) {
461 // halt if we're close enough, flee if we're too close
462 float dist_sq = length_squared(e.AbsoluteDifference(ctrl.GetPursuitTarget()));
463 if (dist_sq < 8.0f) {
464 ctrl.SetFleeTarget(ctrl.GetPursuitTarget());
466 } else if (dist_sq < 25.0f) {
473 void ChaseState::Exit(AIController &ctrl) const {
480 void FleeState::Enter(AIController &ctrl) const {
481 ctrl.CueDecision(6.0f, 3.0f);
482 ctrl.SetFleeSpeed(4.0f);
486 void FleeState::Update(AIController &ctrl, Entity &e, float dt) const {
487 if (!ctrl.DecisionDue()) return;
491 void FleeState::Exit(AIController &ctrl) const {
497 void IdleState::Enter(AIController &ctrl) const {
498 ctrl.SetHaltSpeed(0.5f);
500 ctrl.SetWanderParams(0.001f, 1.1f);
501 ctrl.CueDecision(10.0f, 5.0f);
504 void IdleState::Update(AIController &ctrl, Entity &e, float dt) const {
505 if (ctrl.MayThink()) {
506 const Player *player = ctrl.ClosestVisiblePlayer(e);
508 ctrl.SetPursuitTarget(player->GetEntity());
509 ctrl.SetState(chase);
514 if (!ctrl.DecisionDue()) return;
516 unsigned int d = ctrl.Decide(10);
518 // .2 chance to start going
521 // .3 chance of looking around
523 ctrl.StartWandering();
525 // .5 chance of doing nothing
526 ctrl.StopWandering();
529 ctrl.CueDecision(10.0f, 5.0f);
532 void IdleState::Exit(AIController &ctrl) const {
534 ctrl.StopWandering();
539 void RoamState::Enter(AIController &ctrl) const {
540 ctrl.SetWanderParams(1.0f);
541 ctrl.StartWandering();
542 ctrl.CueDecision(10.0f, 5.0f);
545 void RoamState::Update(AIController &ctrl, Entity &e, float dt) const {
546 if (ctrl.MayThink()) {
547 const Player *player = ctrl.ClosestVisiblePlayer(e);
549 ctrl.SetPursuitTarget(player->GetEntity());
550 ctrl.SetState(chase);
555 if (!ctrl.DecisionDue()) return;
557 unsigned int d = ctrl.Decide(10);
559 // .1 chance of idling
562 ctrl.CueDecision(10.0f, 5.0f);
565 void RoamState::Exit(AIController &ctrl) const {
566 ctrl.StopWandering();