+glm::vec3 AIController::GetSeekForce(const EntityState &state) const noexcept {
+ return Seek(state, GetSeekTarget().GetState(), seek_speed, 2.0f);
+}
+
+// evade
+
+void AIController::StartEvading() noexcept {
+ evading = true;
+}
+
+void AIController::StopEvading() noexcept {
+ evading = false;
+ if (evade_target) {
+ evade_target->UnRef();
+ evade_target = nullptr;
+ }
+}
+
+bool AIController::IsEvading() const noexcept {
+ return evading && evade_target;
+}
+
+void AIController::SetEvadeTarget(Entity &e) noexcept {
+ if (evade_target) {
+ evade_target->UnRef();
+ }
+ evade_target = &e;
+ evade_target->Ref();
+}
+
+void AIController::SetEvadeSpeed(float speed) noexcept {
+ evade_speed = speed;
+}
+
+Entity &AIController::GetEvadeTarget() noexcept {
+ return *evade_target;
+}
+
+const Entity &AIController::GetEvadeTarget() const noexcept {
+ return *evade_target;
+}
+
+glm::vec3 AIController::GetEvadeForce(const EntityState &state) const noexcept{
+ glm::vec3 cur_diff(state.Diff(GetEvadeTarget().GetState()));
+ float time_estimate = length(cur_diff) / evade_speed;
+ EntityState pred_state(GetEvadeTarget().GetState());
+ pred_state.block_pos += pred_state.velocity * time_estimate;
+ return Flee(state, pred_state, evade_speed, 2.0f);
+}
+
+// pursuit
+
+void AIController::StartPursuing() noexcept {
+ pursuing = true;
+}
+
+void AIController::StopPursuing() noexcept {
+ pursuing = false;
+ if (pursuit_target) {
+ pursuit_target->UnRef();
+ pursuit_target = nullptr;
+ }
+}
+
+bool AIController::IsPursuing() const noexcept {
+ return pursuing && pursuit_target;
+}
+
+void AIController::SetPursuitTarget(Entity &e) noexcept {
+ if (pursuit_target) {
+ pursuit_target->UnRef();
+ }
+ pursuit_target = &e;
+ pursuit_target->Ref();
+}
+
+void AIController::SetPursuitSpeed(float speed) noexcept {
+ pursuit_speed = speed;
+}
+
+Entity &AIController::GetPursuitTarget() noexcept {
+ return *pursuit_target;
+}
+
+const Entity &AIController::GetPursuitTarget() const noexcept {
+ return *pursuit_target;
+}
+
+glm::vec3 AIController::GetPursuitForce(const EntityState &state) const noexcept {
+ glm::vec3 cur_diff(state.Diff(GetPursuitTarget().GetState()));
+ float time_estimate = length(cur_diff) / pursuit_speed;
+ EntityState pred_state(GetPursuitTarget().GetState());
+ pred_state.block_pos += pred_state.velocity * time_estimate;
+ return Seek(state, pred_state, pursuit_speed, 2.0f);
+}
+
+// wander
+
+void AIController::StartWandering() noexcept {
+ wandering = true;
+}
+
+void AIController::StopWandering() noexcept {
+ wandering = false;
+}
+
+bool AIController::IsWandering() const noexcept {
+ return wandering;
+}
+
+void AIController::SetWanderParams(