1 #include "AIController.hpp"
2 #include "IdleState.hpp"
3 #include "RoamState.hpp"
5 #include "../model/geometry.hpp"
6 #include "../rand/GaloisLFSR.hpp"
7 #include "../world/Entity.hpp"
8 #include "../world/World.hpp"
9 #include "../world/WorldCollision.hpp"
13 #include <glm/glm.hpp>
25 AIController::AIController(GaloisLFSR &rand)
28 , decision_timer(1.0f)
31 , flee_target(nullptr)
33 , seek_target(nullptr)
36 , wander_pos(1.0f, 0.0f, 0.0f)
44 AIController::~AIController() {
48 void AIController::SetState(const AIState &s) {
54 void AIController::Update(Entity &e, float dt) {
55 decision_timer.Update(dt);
56 state->Update(*this, e, dt);
59 glm::vec3 displacement(
60 random.SNorm() * wander_disp,
61 random.SNorm() * wander_disp,
62 random.SNorm() * wander_disp
64 if (!iszero(displacement)) {
65 wander_pos = normalize(wander_pos + displacement * dt) * wander_radius;
70 // orient head towards heading
71 glm::vec3 heading(Heading(e.GetState()));
72 float tgt_pitch = std::atan(heading.y / length(glm::vec2(heading.x, heading.z)));
73 float tgt_yaw = std::atan2(-heading.x, -heading.z);
74 e.SetHead(tgt_pitch, tgt_yaw);
78 glm::vec3 AIController::ControlForce(const Entity &entity, const EntityState &state) const {
80 return Halt(state, halt_speed);
82 glm::vec3 force(0.0f);
84 glm::vec3 diff(GetFleeTarget().GetState().Diff(state));
85 if (MaxOutForce(force, TargetVelocity(normalize(diff) * flee_speed, state, 0.5f), entity.MaxControlForce())) {
90 glm::vec3 diff(state.Diff(GetSeekTarget().GetState()));
91 if (MaxOutForce(force, TargetVelocity(normalize(diff) * seek_speed, state, 0.5f), entity.MaxControlForce())) {
96 glm::vec3 wander_target(normalize(Heading(state) * wander_dist + wander_pos) * wander_speed);
97 if (MaxOutForce(force, TargetVelocity(wander_target, state, 2.0f), entity.MaxControlForce())) {
104 glm::vec3 AIController::Heading(const EntityState &state) noexcept {
105 if (dot(state.velocity, state.velocity) > std::numeric_limits<float>::epsilon()) {
106 return normalize(state.velocity);
108 float cp = std::cos(state.pitch);
109 return glm::vec3(std::cos(state.yaw) * cp, std::sin(state.yaw) * cp, std::sin(state.pitch));
113 void AIController::CueDecision(
117 decision_timer = FineTimer(minimum + variance * random.SNorm());
118 decision_timer.Start();
121 bool AIController::DecisionDue() const noexcept {
122 return decision_timer.HitOnce();
125 unsigned int AIController::Decide(unsigned int num_choices) noexcept {
126 return random.Next<unsigned int>() % num_choices;
129 void AIController::EnterHalt(float speed) noexcept {
134 void AIController::ExitHalt() noexcept {
138 bool AIController::IsHalted() const noexcept {
142 void AIController::StartFleeing(const Entity &e, float speed) noexcept {
147 void AIController::StopFleeing() noexcept {
148 flee_target = nullptr;
151 bool AIController::IsFleeing() const noexcept {
155 const Entity &AIController::GetFleeTarget() const noexcept {
159 void AIController::StartSeeking(const Entity &e, float speed) noexcept {
164 void AIController::StopSeeking() noexcept {
165 seek_target = nullptr;
168 bool AIController::IsSeeking() const noexcept {
172 const Entity &AIController::GetSeekTarget() const noexcept {
176 void AIController::StartWandering(
183 wander_speed = speed;
184 wander_dist = distance;
185 wander_radius = radius;
186 wander_disp = displacement;
189 void AIController::StopWandering() noexcept {
193 bool AIController::IsWandering() const noexcept {
198 void IdleState::Enter(AIController &ctrl) const {
199 ctrl.EnterHalt(2.0f);
200 ctrl.StartWandering(0.001f, 1.1f);
201 ctrl.CueDecision(10.0f, 5.0f);
204 void IdleState::Update(AIController &ctrl, Entity &e, float dt) const {
205 // TODO: check for players
206 if (!ctrl.DecisionDue()) return;
208 unsigned int d = ctrl.Decide(10);
210 // .1 chance to start going
213 // .2 chance of looking around
216 ctrl.EnterHalt(2.0f);
218 ctrl.CueDecision(10.0f, 5.0f);
221 void IdleState::Exit(AIController &ctrl) const {
223 ctrl.StopWandering();
226 void RoamState::Enter(AIController &ctrl) const {
227 ctrl.StartWandering(1.0f);
228 ctrl.CueDecision(10.0f, 5.0f);
231 void RoamState::Update(AIController &ctrl, Entity &e, float dt) const {
232 // TODO: check for players
233 if (!ctrl.DecisionDue()) return;
235 unsigned int d = ctrl.Decide(10);
237 // .1 chance of idling
240 ctrl.CueDecision(10.0f, 5.0f);
243 void RoamState::Exit(AIController &ctrl) const {
244 ctrl.StopWandering();