X-Git-Url: http://git.localhorst.tv/?a=blobdiff_plain;f=src%2Fai%2Fai.cpp;h=f24d0694e0838222d457471ba1f254a76417471c;hb=0ab149c70b3f984b2cc0c7a122b4aa347bc5fd79;hp=d50aee5b6219aa06f170bc86b3f03eb0fb1cf7e3;hpb=68f47f2824989b21ff9a480a367a6d0a41804f41;p=blank.git diff --git a/src/ai/ai.cpp b/src/ai/ai.cpp index d50aee5..f24d069 100644 --- a/src/ai/ai.cpp +++ b/src/ai/ai.cpp @@ -1,123 +1,165 @@ -#include "Chaser.hpp" -#include "Controller.hpp" -#include "RandomWalk.hpp" +#include "AIController.hpp" +#include "IdleState.hpp" #include "../model/geometry.hpp" +#include "../rand/GaloisLFSR.hpp" #include "../world/Entity.hpp" #include "../world/World.hpp" #include "../world/WorldCollision.hpp" +#include +#include #include namespace blank { -Chaser::Chaser(World &world, Entity &ctrl, Entity &tgt) noexcept -: Controller(ctrl) -, world(world) -, tgt(tgt) -, chase_speed(0.002f) -, flee_speed(-0.005f) -, stop_dist(10) -, flee_dist(5) { - tgt.Ref(); +namespace { + +IdleState idle; + +} + +AIController::AIController(GaloisLFSR &rand) +: random(rand) +, state(&idle) +, flee_target(nullptr) +, flee_speed(5.0f) +, seek_target(nullptr) +, seek_speed(5.0f) +, wandering(false) +, wander_pos(1.0f, 0.0f, 0.0f) +, wander_speed(1.0f) +, wander_dist(2.0f) +, wander_radius(1.5f) +, wander_disp(1.0f) { + state->Enter(*this); +} + +AIController::~AIController() { + state->Exit(*this); } -Chaser::~Chaser() { - tgt.UnRef(); +void AIController::SetState(const AIState &s) { + state->Exit(*this); + state = &s; + state->Enter(*this); } -void Chaser::Update(int dt) { - if (Target().Dead()) { - Controlled().Kill(); - return; +void AIController::Update(Entity &e, float dt) { + // movement: for now, wander only + if (wandering) { + glm::vec3 displacement( + random.SNorm() * wander_disp, + random.SNorm() * wander_disp, + random.SNorm() * wander_disp + ); + if (!iszero(displacement)) { + wander_pos = normalize(wander_pos + displacement * dt) * wander_radius; + } } - glm::vec3 diff(Target().AbsoluteDifference(Controlled())); - float dist = length(diff); - if (dist < std::numeric_limits::epsilon()) { - Controlled().Velocity(glm::vec3(0.0f)); - return; + if (e.Moving()) { + // orient head towards heading + glm::vec3 heading(Heading(e.GetState())); + float tgt_pitch = std::atan(heading.y / length(glm::vec2(heading.x, heading.z))); + float tgt_yaw = std::atan2(-heading.x, -heading.z); + e.SetHead(tgt_pitch, tgt_yaw); } - glm::vec3 norm_diff(diff / dist); +} - bool line_of_sight = true; - Ray aim{Target().Position() - diff, norm_diff}; - WorldCollision coll; - if (world.Intersection(aim, glm::mat4(1.0f), Target().ChunkCoords(), coll)) { - line_of_sight = coll.depth > dist; +glm::vec3 AIController::ControlForce(const Entity &entity, const EntityState &state) const { + glm::vec3 force(0.0f); + if (IsFleeing()) { + glm::vec3 diff(GetFleeTarget().GetState().Diff(state)); + if (MaxOutForce(force, TargetVelocity(normalize(diff) * flee_speed, state, 0.5f), entity.MaxControlForce())) { + return force; + } + } + if (IsSeeking()) { + glm::vec3 diff(state.Diff(GetSeekTarget().GetState())); + if (MaxOutForce(force, TargetVelocity(normalize(diff) * seek_speed, state, 0.5f), entity.MaxControlForce())) { + return force; + } } + if (wandering) { + 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; + } + } + return force; +} - if (!line_of_sight) { - Controlled().Velocity(glm::vec3(0.0f)); - } else if (dist > stop_dist) { - Controlled().Velocity(norm_diff * chase_speed); - } else if (dist < flee_dist) { - Controlled().Velocity(norm_diff * flee_speed); +glm::vec3 AIController::Heading(const EntityState &state) noexcept { + if (dot(state.velocity, state.velocity) > std::numeric_limits::epsilon()) { + return normalize(state.velocity); } else { - Controlled().Velocity(glm::vec3(0.0f)); + float cp = std::cos(state.pitch); + return glm::vec3(std::cos(state.yaw) * cp, std::sin(state.yaw) * cp, std::sin(state.pitch)); } } +void AIController::StartFleeing(const Entity &e, float speed) noexcept { + flee_target = &e; + flee_speed = speed; +} -Controller::Controller(Entity &e) noexcept -: entity(e) { - entity.Ref(); +void AIController::StopFleeing() noexcept { + flee_target = nullptr; } -Controller::~Controller() { - entity.UnRef(); +bool AIController::IsFleeing() const noexcept { + return flee_target; } +const Entity &AIController::GetFleeTarget() const noexcept { + return *flee_target; +} -RandomWalk::RandomWalk(Entity &e, std::uint64_t seed) noexcept -: Controller(e) -, random(seed) -, start_vel(e.Velocity()) -, target_vel(start_vel) -, start_rot(e.AngularVelocity()) -, target_rot(start_rot) -, switch_time(0) -, lerp_max(1.0f) -, lerp_time(0.0f) { +void AIController::StartSeeking(const Entity &e, float speed) noexcept { + seek_target = &e; + seek_speed = speed; +} +void AIController::StopSeeking() noexcept { + seek_target = nullptr; } -RandomWalk::~RandomWalk() { +bool AIController::IsSeeking() const noexcept { + return seek_target; +} +const Entity &AIController::GetSeekTarget() const noexcept { + return *seek_target; } -void RandomWalk::Update(int dt) { - switch_time -= dt; - lerp_time -= dt; - if (switch_time < 0) { - switch_time += 2500 + (random.Next() % 5000); - lerp_max = 1500 + (random.Next() % 1000); - lerp_time = lerp_max; - Change(); - } else if (lerp_time > 0) { - float a = std::min(lerp_time / lerp_max, 1.0f); - Controlled().Velocity(mix(target_vel, start_vel, a)); - Controlled().AngularVelocity(mix(target_rot, start_rot, a)); - } else { - Controlled().Velocity(target_vel); - Controlled().AngularVelocity(target_rot); - } +void AIController::StartWandering( + float speed, + float distance, + float radius, + float displacement +) noexcept { + wandering = true; + wander_speed = speed; + wander_dist = distance; + wander_radius = radius; + wander_disp = displacement; +} +void AIController::StopWandering() noexcept { + wandering = false; } -void RandomWalk::Change() noexcept { - start_vel = target_vel; - start_rot = target_rot; - constexpr float base = 0.000001f; +void IdleState::Enter(AIController &ctrl) const { + ctrl.StartWandering(1.0f); +} - target_vel.x = base * (random.Next() % 1024); - target_vel.y = base * (random.Next() % 1024); - target_vel.z = base * (random.Next() % 1024); +void IdleState::Update(AIController &ctrl, Entity &e, float dt) const { +} - target_rot.x = base * (random.Next() % 1024); - target_rot.y = base * (random.Next() % 1024); - target_rot.z = base * (random.Next() % 1024); +void IdleState::Exit(AIController &ctrl) const { + ctrl.StopWandering(); } }