]> git.localhorst.tv Git - blank.git/blobdiff - src/ai/ai.cpp
experiments with ai states and steering
[blank.git] / src / ai / ai.cpp
index f24d0694e0838222d457471ba1f254a76417471c..343bacf774aebe56d0828e3ab74a079149779f7a 100644 (file)
@@ -1,5 +1,6 @@
 #include "AIController.hpp"
 #include "IdleState.hpp"
+#include "RoamState.hpp"
 
 #include "../model/geometry.hpp"
 #include "../rand/GaloisLFSR.hpp"
@@ -17,12 +18,16 @@ namespace blank {
 namespace {
 
 IdleState idle;
+RoamState roam;
 
 }
 
 AIController::AIController(GaloisLFSR &rand)
 : random(rand)
 , state(&idle)
+, decision_timer(1.0f)
+, halted(false)
+, halt_speed(1.0f)
 , flee_target(nullptr)
 , flee_speed(5.0f)
 , seek_target(nullptr)
@@ -47,7 +52,9 @@ void AIController::SetState(const AIState &s) {
 }
 
 void AIController::Update(Entity &e, float dt) {
-       // movement: for now, wander only
+       decision_timer.Update(dt);
+       state->Update(*this, e, dt);
+
        if (wandering) {
                glm::vec3 displacement(
                        random.SNorm() * wander_disp,
@@ -69,6 +76,9 @@ void AIController::Update(Entity &e, float dt) {
 }
 
 glm::vec3 AIController::ControlForce(const Entity &entity, const EntityState &state) const {
+       if (IsHalted()) {
+               return Halt(state, halt_speed);
+       }
        glm::vec3 force(0.0f);
        if (IsFleeing()) {
                glm::vec3 diff(GetFleeTarget().GetState().Diff(state));
@@ -82,7 +92,7 @@ glm::vec3 AIController::ControlForce(const Entity &entity, const EntityState &st
                        return force;
                }
        }
-       if (wandering) {
+       if (IsWandering()) {
                glm::vec3 wander_target(normalize(Heading(state) * wander_dist + wander_pos) * wander_speed);
                if (MaxOutForce(force, TargetVelocity(wander_target, state, 2.0f), entity.MaxControlForce())) {
                        return force;
@@ -100,6 +110,35 @@ glm::vec3 AIController::Heading(const EntityState &state) noexcept {
        }
 }
 
+void AIController::CueDecision(
+       float minimum,
+       float variance
+) noexcept {
+       decision_timer = FineTimer(minimum + variance * random.SNorm());
+       decision_timer.Start();
+}
+
+bool AIController::DecisionDue() const noexcept {
+       return decision_timer.HitOnce();
+}
+
+unsigned int AIController::Decide(unsigned int num_choices) noexcept {
+       return random.Next<unsigned int>() % num_choices;
+}
+
+void AIController::EnterHalt(float speed) noexcept {
+       halted = true;
+       halt_speed = speed;
+}
+
+void AIController::ExitHalt() noexcept {
+       halted = false;
+}
+
+bool AIController::IsHalted() const noexcept {
+       return halted;
+}
+
 void AIController::StartFleeing(const Entity &e, float speed) noexcept {
        flee_target = &e;
        flee_speed = speed;
@@ -146,19 +185,62 @@ void AIController::StartWandering(
        wander_radius = radius;
        wander_disp = displacement;
 }
+
 void AIController::StopWandering() noexcept {
        wandering = false;
 }
 
+bool AIController::IsWandering() const noexcept {
+       return wandering;
+}
+
 
 void IdleState::Enter(AIController &ctrl) const {
-       ctrl.StartWandering(1.0f);
+       ctrl.EnterHalt(2.0f);
+       ctrl.StartWandering(0.001f, 1.1f);
+       ctrl.CueDecision(10.0f, 5.0f);
 }
 
 void IdleState::Update(AIController &ctrl, Entity &e, float dt) const {
+       // TODO: check for players
+       if (!ctrl.DecisionDue()) return;
+
+       unsigned int d = ctrl.Decide(10);
+       if (d == 0) {
+               // .1 chance to start going
+               ctrl.SetState(roam);
+       } else if (d < 3) {
+               // .2 chance of looking around
+               ctrl.ExitHalt();
+       } else {
+               ctrl.EnterHalt(2.0f);
+       }
+       ctrl.CueDecision(10.0f, 5.0f);
 }
 
 void IdleState::Exit(AIController &ctrl) const {
+       ctrl.ExitHalt();
+       ctrl.StopWandering();
+}
+
+void RoamState::Enter(AIController &ctrl) const {
+       ctrl.StartWandering(1.0f);
+       ctrl.CueDecision(10.0f, 5.0f);
+}
+
+void RoamState::Update(AIController &ctrl, Entity &e, float dt) const {
+       // TODO: check for players
+       if (!ctrl.DecisionDue()) return;
+
+       unsigned int d = ctrl.Decide(10);
+       if (d == 0) {
+               // .1 chance of idling
+               ctrl.SetState(idle);
+       }
+       ctrl.CueDecision(10.0f, 5.0f);
+}
+
+void RoamState::Exit(AIController &ctrl) const {
        ctrl.StopWandering();
 }