]> git.localhorst.tv Git - blank.git/blob - src/ai/ai.cpp
more fun with AI/steering
[blank.git] / src / ai / ai.cpp
1 #include "AIController.hpp"
2 #include "ChaseState.hpp"
3 #include "FleeState.hpp"
4 #include "IdleState.hpp"
5 #include "RoamState.hpp"
6
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"
12
13 #include <cmath>
14 #include <limits>
15 #include <glm/glm.hpp>
16
17
18 namespace blank {
19
20 namespace {
21
22 ChaseState chase;
23 FleeState flee;
24 IdleState idle;
25 RoamState roam;
26
27 }
28
29 AIController::AIController(World &world, GaloisLFSR &rand)
30 : world(world)
31 , random(rand)
32 , state(&idle)
33 , sight_dist(64.0f)
34 , sight_angle(0.707f)
35 , think_timer(0.5f)
36 , decision_timer(1.0f)
37 , halted(false)
38 , halt_speed(1.0f)
39 , fleeing(false)
40 , flee_target(nullptr)
41 , flee_speed(5.0f)
42 , seeking(false)
43 , seek_target(nullptr)
44 , seek_speed(5.0f)
45 , evading(false)
46 , evade_target(nullptr)
47 , evade_speed(5.0f)
48 , pursuing(false)
49 , pursuit_target(nullptr)
50 , pursuit_speed(5.0f)
51 , wandering(false)
52 , wander_pos(1.0f, 0.0f, 0.0f)
53 , wander_speed(1.0f)
54 , wander_dist(2.0f)
55 , wander_radius(1.5f)
56 , wander_disp(1.0f) {
57         think_timer.Start();
58         state->Enter(*this);
59 }
60
61 AIController::~AIController() {
62         state->Exit(*this);
63 }
64
65 void AIController::SetState(const AIState &s) {
66         state->Exit(*this);
67         state = &s;
68         state->Enter(*this);
69 }
70
71 void AIController::Update(Entity &e, float dt) {
72         think_timer.Update(dt);
73         decision_timer.Update(dt);
74         state->Update(*this, e, dt);
75
76         if (wandering) {
77                 glm::vec3 displacement(
78                         random.SNorm() * wander_disp,
79                         random.SNorm() * wander_disp,
80                         random.SNorm() * wander_disp
81                 );
82                 if (!iszero(displacement)) {
83                         wander_pos = normalize(wander_pos + displacement * dt) * wander_radius;
84                 }
85         }
86
87         if (e.Moving()) {
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);
93         }
94 }
95
96 glm::vec3 AIController::ControlForce(const Entity &entity, const EntityState &state) const {
97         if (IsHalted()) {
98                 return GetHaltForce(state);
99         }
100         glm::vec3 force(0.0f);
101         if (IsFleeing()) {
102                 if (MaxOutForce(force, GetFleeForce(state), entity.MaxControlForce())) {
103                         return force;
104                 }
105         }
106         if (IsSeeking()) {
107                 if (MaxOutForce(force, GetSeekForce(state), entity.MaxControlForce())) {
108                         return force;
109                 }
110         }
111         if (IsEvading()) {
112                 if (MaxOutForce(force, GetEvadeForce(state), entity.MaxControlForce())) {
113                         return force;
114                 }
115         }
116         if (IsPursuing()) {
117                 if (MaxOutForce(force, GetPursuitForce(state), entity.MaxControlForce())) {
118                         return force;
119                 }
120         }
121         if (IsWandering()) {
122                 if (MaxOutForce(force, GetWanderForce(state), entity.MaxControlForce())) {
123                         return force;
124                 }
125         }
126         return force;
127 }
128
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);
132         } else {
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));
135         }
136 }
137
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();
145
146                 // distance test
147                 const glm::vec3 diff(pe.AbsoluteDifference(e));
148                 float dist = length_squared(diff);
149                 if (dist > distance) continue;
150
151                 // FOV test, 45° in each direction
152                 if (dot(normalize(diff), aim.dir) < sight_angle) {
153                         continue;
154                 }
155
156                 // LOS test, assumes all entities are see-through
157                 WorldCollision col;
158                 if (world.Intersection(aim, glm::mat4(1.0f), reference, col) && col.depth * col.depth < dist) {
159                         continue;
160                 }
161
162                 // we got a match
163                 target = &p;
164                 distance = dist;
165         }
166         return target;
167 }
168
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) {
175                 return false;
176         }
177         WorldCollision col;
178         if (world.Intersection(aim, glm::mat4(1.0f), reference, col) && col.depth < dist) {
179                 return false;
180         }
181         return true;
182 }
183
184 // think
185
186 bool AIController::MayThink() const noexcept {
187         return think_timer.Hit();
188 }
189
190 void AIController::SetThinkInterval(float i) noexcept {
191         think_timer = FineTimer(i);
192         think_timer.Start();
193 }
194
195 // decide
196
197 void AIController::CueDecision(
198         float minimum,
199         float variance
200 ) noexcept {
201         decision_timer = FineTimer(minimum + variance * random.SNorm());
202         decision_timer.Start();
203 }
204
205 bool AIController::DecisionDue() const noexcept {
206         return decision_timer.HitOnce();
207 }
208
209 unsigned int AIController::Decide(unsigned int num_choices) noexcept {
210         return random.Next<unsigned int>() % num_choices;
211 }
212
213 // halt
214
215 void AIController::EnterHalt() noexcept {
216         halted = true;
217 }
218
219 void AIController::ExitHalt() noexcept {
220         halted = false;
221 }
222
223 bool AIController::IsHalted() const noexcept {
224         return halted;
225 }
226
227 void AIController::SetHaltSpeed(float speed) noexcept {
228         halt_speed = speed;
229 }
230
231 glm::vec3 AIController::GetHaltForce(const EntityState &state) const noexcept {
232         return Halt(state, halt_speed);
233 }
234
235 // flee
236
237 void AIController::StartFleeing() noexcept {
238         fleeing = true;
239 }
240
241 void AIController::StopFleeing() noexcept {
242         fleeing = false;
243         if (flee_target) {
244                 flee_target->UnRef();
245                 flee_target = nullptr;
246         }
247 }
248
249 bool AIController::IsFleeing() const noexcept {
250         return fleeing && flee_target;
251 }
252
253 void AIController::SetFleeTarget(Entity &e) noexcept {
254         if (flee_target) {
255                 flee_target->UnRef();
256         }
257         flee_target = &e;
258         flee_target->Ref();
259 }
260
261 void AIController::SetFleeSpeed(float speed) noexcept {
262         flee_speed = speed;
263 }
264
265 Entity &AIController::GetFleeTarget() noexcept {
266         return *flee_target;
267 }
268
269 const Entity &AIController::GetFleeTarget() const noexcept {
270         return *flee_target;
271 }
272
273 glm::vec3 AIController::GetFleeForce(const EntityState &state) const noexcept {
274         return Flee(state, GetFleeTarget().GetState(), flee_speed, 2.0f);
275 }
276
277 // seek
278
279 void AIController::StartSeeking() noexcept {
280         seeking = true;
281 }
282
283 void AIController::StopSeeking() noexcept {
284         seeking = false;
285         if (seek_target) {
286                 seek_target->UnRef();
287                 seek_target = nullptr;
288         }
289 }
290
291 bool AIController::IsSeeking() const noexcept {
292         return seeking && seek_target;
293 }
294
295 void AIController::SetSeekTarget(Entity &e) noexcept {
296         if (seek_target) {
297                 seek_target->UnRef();
298         }
299         seek_target = &e;
300         seek_target->Ref();
301 }
302
303 void AIController::SetSeekSpeed(float speed) noexcept {
304         seek_speed = speed;
305 }
306
307 Entity &AIController::GetSeekTarget() noexcept {
308         return *seek_target;
309 }
310
311 const Entity &AIController::GetSeekTarget() const noexcept {
312         return *seek_target;
313 }
314
315 glm::vec3 AIController::GetSeekForce(const EntityState &state) const noexcept {
316         return Seek(state, GetSeekTarget().GetState(), seek_speed, 2.0f);
317 }
318
319 // evade
320
321 void AIController::StartEvading() noexcept {
322         evading = true;
323 }
324
325 void AIController::StopEvading() noexcept {
326         evading = false;
327         if (evade_target) {
328                 evade_target->UnRef();
329                 evade_target = nullptr;
330         }
331 }
332
333 bool AIController::IsEvading() const noexcept {
334         return evading && evade_target;
335 }
336
337 void AIController::SetEvadeTarget(Entity &e) noexcept {
338         if (evade_target) {
339                 evade_target->UnRef();
340         }
341         evade_target = &e;
342         evade_target->Ref();
343 }
344
345 void AIController::SetEvadeSpeed(float speed) noexcept {
346         evade_speed = speed;
347 }
348
349 Entity &AIController::GetEvadeTarget() noexcept {
350         return *evade_target;
351 }
352
353 const Entity &AIController::GetEvadeTarget() const noexcept {
354         return *evade_target;
355 }
356
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);
363 }
364
365 // pursuit
366
367 void AIController::StartPursuing() noexcept {
368         pursuing = true;
369 }
370
371 void AIController::StopPursuing() noexcept {
372         pursuing = false;
373         if (pursuit_target) {
374                 pursuit_target->UnRef();
375                 pursuit_target = nullptr;
376         }
377 }
378
379 bool AIController::IsPursuing() const noexcept {
380         return pursuing && pursuit_target;
381 }
382
383 void AIController::SetPursuitTarget(Entity &e) noexcept {
384         if (pursuit_target) {
385                 pursuit_target->UnRef();
386         }
387         pursuit_target = &e;
388         pursuit_target->Ref();
389 }
390
391 void AIController::SetPursuitSpeed(float speed) noexcept {
392         pursuit_speed = speed;
393 }
394
395 Entity &AIController::GetPursuitTarget() noexcept {
396         return *pursuit_target;
397 }
398
399 const Entity &AIController::GetPursuitTarget() const noexcept {
400         return *pursuit_target;
401 }
402
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);
409 }
410
411 // wander
412
413 void AIController::StartWandering() noexcept {
414         wandering = true;
415 }
416
417 void AIController::StopWandering() noexcept {
418         wandering = false;
419 }
420
421 bool AIController::IsWandering() const noexcept {
422         return wandering;
423 }
424
425 void AIController::SetWanderParams(
426         float speed,
427         float distance,
428         float radius,
429         float displacement
430 ) noexcept {
431         wander_speed = speed;
432         wander_dist = distance;
433         wander_radius = radius;
434         wander_disp = displacement;
435 }
436
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);
440 }
441
442
443 // chase
444
445 void ChaseState::Enter(AIController &ctrl) const {
446         ctrl.SetHaltSpeed(2.0f);
447         ctrl.SetPursuitSpeed(4.0f);
448         ctrl.StartPursuing();
449 }
450
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()) {
454                 ctrl.SetState(idle);
455                 return;
456         }
457         if (!ctrl.LineOfSight(e, ctrl.GetPursuitTarget())) {
458                 ctrl.SetState(idle);
459                 return;
460         }
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());
465                 ctrl.SetState(flee);
466         } else if (dist_sq < 25.0f) {
467                 ctrl.EnterHalt();
468         } else {
469                 ctrl.ExitHalt();
470         }
471 }
472
473 void ChaseState::Exit(AIController &ctrl) const {
474         ctrl.StopPursuing();
475         ctrl.ExitHalt();
476 }
477
478 // flee
479
480 void FleeState::Enter(AIController &ctrl) const {
481         ctrl.CueDecision(6.0f, 3.0f);
482         ctrl.SetFleeSpeed(4.0f);
483         ctrl.StartFleeing();
484 }
485
486 void FleeState::Update(AIController &ctrl, Entity &e, float dt) const {
487         if (!ctrl.DecisionDue()) return;
488         ctrl.SetState(idle);
489 }
490
491 void FleeState::Exit(AIController &ctrl) const {
492         ctrl.StopFleeing();
493 }
494
495 // idle
496
497 void IdleState::Enter(AIController &ctrl) const {
498         ctrl.SetHaltSpeed(0.5f);
499         ctrl.EnterHalt();
500         ctrl.SetWanderParams(0.001f, 1.1f);
501         ctrl.CueDecision(10.0f, 5.0f);
502 }
503
504 void IdleState::Update(AIController &ctrl, Entity &e, float dt) const {
505         if (ctrl.MayThink()) {
506                 const Player *player = ctrl.ClosestVisiblePlayer(e);
507                 if (player) {
508                         ctrl.SetPursuitTarget(player->GetEntity());
509                         ctrl.SetState(chase);
510                         return;
511                 }
512         }
513
514         if (!ctrl.DecisionDue()) return;
515
516         unsigned int d = ctrl.Decide(10);
517         if (d < 2) {
518                 // .2 chance to start going
519                 ctrl.SetState(roam);
520         } else if (d < 5) {
521                 // .3 chance of looking around
522                 ctrl.ExitHalt();
523                 ctrl.StartWandering();
524         } else {
525                 // .5 chance of doing nothing
526                 ctrl.StopWandering();
527                 ctrl.EnterHalt();
528         }
529         ctrl.CueDecision(10.0f, 5.0f);
530 }
531
532 void IdleState::Exit(AIController &ctrl) const {
533         ctrl.ExitHalt();
534         ctrl.StopWandering();
535 }
536
537 // roam
538
539 void RoamState::Enter(AIController &ctrl) const {
540         ctrl.SetWanderParams(1.0f);
541         ctrl.StartWandering();
542         ctrl.CueDecision(10.0f, 5.0f);
543 }
544
545 void RoamState::Update(AIController &ctrl, Entity &e, float dt) const {
546         if (ctrl.MayThink()) {
547                 const Player *player = ctrl.ClosestVisiblePlayer(e);
548                 if (player) {
549                         ctrl.SetPursuitTarget(player->GetEntity());
550                         ctrl.SetState(chase);
551                         return;
552                 }
553         }
554
555         if (!ctrl.DecisionDue()) return;
556
557         unsigned int d = ctrl.Decide(10);
558         if (d == 0) {
559                 // .1 chance of idling
560                 ctrl.SetState(idle);
561         }
562         ctrl.CueDecision(10.0f, 5.0f);
563 }
564
565 void RoamState::Exit(AIController &ctrl) const {
566         ctrl.StopWandering();
567 }
568
569 }