From 8a3907bb0bed257bf5d6f40f39f15114e8345913 Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Fri, 24 Nov 2017 00:11:46 +0100 Subject: [PATCH] eating and drinking the aim is a bit off so the little guy starves in the end :/ --- src/blobs.cpp | 4 +- src/creature/Creature.hpp | 16 ++++ src/creature/Goal.hpp | 40 ++++++++++ src/creature/IngestNeed.hpp | 1 + src/creature/LocateResourceGoal.hpp | 27 +++++++ src/creature/Situation.hpp | 1 + src/creature/Steering.hpp | 48 ++++++++++++ src/creature/creature.cpp | 109 +++++++++++++++++++++++++++- src/creature/goal.cpp | 84 +++++++++++++++++++++ src/creature/need.cpp | 18 ++++- src/graphics/glm.hpp | 16 ++++ src/world/TileType.hpp | 2 + src/world/world.cpp | 11 +++ 13 files changed, 371 insertions(+), 6 deletions(-) create mode 100644 src/creature/Goal.hpp create mode 100644 src/creature/LocateResourceGoal.hpp create mode 100644 src/creature/Steering.hpp create mode 100644 src/creature/goal.cpp diff --git a/src/blobs.cpp b/src/blobs.cpp index 23ab012..18e62aa 100644 --- a/src/blobs.cpp +++ b/src/blobs.cpp @@ -80,9 +80,9 @@ int main(int argc, char *argv[]) { // sunset //.FirstPerson(3, glm::vec3(0.0f, 0.0f, 0.1f), glm::vec3(1.0f, -0.75f, 0.1f)) // from afar - //.MapView(0, glm::vec3(0.0f, 0.0f, 60.0f), 0.0f) + .MapView(0, glm::vec3(0.0f, 0.0f, 30.0f), 0.0f) // from afar, rotating - .Orbital(glm::vec3(-60.0f, 0.0f, 0.0f)) + //.Orbital(glm::vec3(-60.0f, 0.0f, 0.0f)) ; // system view //state.GetCamera() diff --git a/src/creature/Creature.hpp b/src/creature/Creature.hpp index 7c3d1ef..aaeb6cf 100644 --- a/src/creature/Creature.hpp +++ b/src/creature/Creature.hpp @@ -1,8 +1,10 @@ #ifndef BLOBS_CREATURE_CREATURE_HPP_ #define BLOBS_CREATURE_CREATURE_HPP_ +#include "Goal.hpp" #include "Need.hpp" #include "Situation.hpp" +#include "Steering.hpp" #include "../graphics/glm.hpp" #include "../graphics/SimpleVAO.hpp" @@ -47,11 +49,20 @@ public: void AddNeed(std::unique_ptr &&n) { needs.emplace_back(std::move(n)); } const std::vector> &Needs() const noexcept { return needs; } + void AddGoal(std::unique_ptr &&); + const std::vector> &Goals() const noexcept { return goals; } + void Tick(double dt); Situation &GetSituation() noexcept { return situation; } const Situation &GetSituation() const noexcept { return situation; } + Steering &GetSteering() noexcept { return steering; } + const Steering &GetSteering() const noexcept { return steering; } + + void Velocity(const glm::dvec3 &v) noexcept { vel = v; } + const glm::dvec3 &Velocity() const noexcept { return vel; } + glm::dmat4 LocalTransform() noexcept; void BuildVAO(); @@ -60,9 +71,14 @@ public: private: std::string name; double health; + std::vector> needs; + std::vector> goals; Situation situation; + Steering steering; + + glm::dvec3 vel; struct Attributes { glm::vec3 position; diff --git a/src/creature/Goal.hpp b/src/creature/Goal.hpp new file mode 100644 index 0000000..d664e6b --- /dev/null +++ b/src/creature/Goal.hpp @@ -0,0 +1,40 @@ +#ifndef BLOBS_CREATURE_GOAL_HPP_ +#define BLOBS_CREATURE_GOAL_HPP_ + +namespace blobs { +namespace creature { + +class Creature; + +class Goal { + +public: + Goal(); + virtual ~Goal() noexcept; + +public: + double Urgency() const noexcept { return urgency; } + void Urgency(double u) noexcept { urgency = u; } + + bool Interruptible() const noexcept { return interruptible; } + void Interruptible(bool i) noexcept { interruptible = i; } + + bool Complete() const noexcept { return complete; } + void Complete(bool i) noexcept { complete = i; } + +public: + virtual void Enable(Creature &) { } + virtual void Tick(double dt) { } + virtual void Action(Creature &) { } + +private: + double urgency; + bool interruptible; + bool complete; + +}; + +} +} + +#endif diff --git a/src/creature/IngestNeed.hpp b/src/creature/IngestNeed.hpp index 3ad3bf0..4244293 100644 --- a/src/creature/IngestNeed.hpp +++ b/src/creature/IngestNeed.hpp @@ -24,6 +24,7 @@ private: double speed; double damage; bool ingesting; + bool locating; }; diff --git a/src/creature/LocateResourceGoal.hpp b/src/creature/LocateResourceGoal.hpp new file mode 100644 index 0000000..b5d390c --- /dev/null +++ b/src/creature/LocateResourceGoal.hpp @@ -0,0 +1,27 @@ +#ifndef BLOBS_CREATURE_LOCATERESOURCEGOAL_HPP_ +#define BLOBS_CREATURE_LOCATERESOURCEGOAL_HPP_ + +namespace blobs { +namespace creature { + +class LocateResourceGoal +: public Goal { + +public: + explicit LocateResourceGoal(int resource); + ~LocateResourceGoal() noexcept override; + +public: + void Enable(Creature &) override; + void Tick(double dt) override; + void Action(Creature &) override; + +private: + int res; + +}; + +} +} + +#endif diff --git a/src/creature/Situation.hpp b/src/creature/Situation.hpp index d4a376a..12b7490 100644 --- a/src/creature/Situation.hpp +++ b/src/creature/Situation.hpp @@ -27,6 +27,7 @@ public: world::Tile &GetTile() const noexcept; const world::TileType &GetTileType() const noexcept; + void Move(const glm::dvec3 &dp) noexcept; void SetPlanetSurface(world::Planet &, int srf, const glm::dvec3 &pos) noexcept; public: diff --git a/src/creature/Steering.hpp b/src/creature/Steering.hpp new file mode 100644 index 0000000..6d5b40a --- /dev/null +++ b/src/creature/Steering.hpp @@ -0,0 +1,48 @@ +#ifndef BLOBS_CREATURE_STEERING_HPP_ +#define BLOBS_CREATURE_STEERING_HPP_ + +#include "../graphics/glm.hpp" + + +namespace blobs { +namespace creature { + +class Creature; + +class Steering { + +public: + Steering(); + ~Steering(); + +public: + void MaxAcceleration(double a) noexcept { max_accel = a; } + double MaxAcceleration() const noexcept { return max_accel; } + + void MaxSpeed(double s) noexcept { max_speed = s; } + double MaxSpeed() const noexcept { return max_speed; } + +public: + void Halt() noexcept; + void GoTo(const glm::dvec3 &) noexcept; + + glm::dvec3 Acceleration(Creature &) const noexcept; + +private: + bool SumForce(glm::dvec3 &out, const glm::dvec3 &in) const noexcept; + +private: + glm::dvec3 seek_target; + + double max_accel = 1.0; + double max_speed = 1.0; + + bool halting; + bool seeking; + +}; + +} +} + +#endif diff --git a/src/creature/creature.cpp b/src/creature/creature.cpp index de3a4b7..1278e26 100644 --- a/src/creature/creature.cpp +++ b/src/creature/creature.cpp @@ -1,6 +1,8 @@ #include "Creature.hpp" #include "Situation.hpp" +#include "Steering.hpp" +#include "Goal.hpp" #include "InhaleNeed.hpp" #include "IngestNeed.hpp" #include "Need.hpp" @@ -10,6 +12,7 @@ #include "../world/Simulation.hpp" #include "../world/TileType.hpp" +#include #include #include @@ -22,7 +25,10 @@ Creature::Creature() : name() , health(1.0) , needs() +, goals() , situation() +, steering() +, vel(0.0) , vao() { } @@ -33,15 +39,50 @@ void Creature::Hurt(double dt) noexcept { health = std::max(0.0, health - dt); } +void Creature::AddGoal(std::unique_ptr &&g) { + g->Enable(*this); + goals.emplace_back(std::move(g)); +} + +namespace { + +bool GoalCompare(const std::unique_ptr &a, const std::unique_ptr &b) { + return b->Urgency() < a->Urgency(); +} + +} + void Creature::Tick(double dt) { - // update needs + // TODO: better integration method + glm::dvec3 acc(steering.Acceleration(*this)); + situation.Move(vel * dt); + vel += acc * dt; + for (auto &need : needs) { need->Tick(dt); } + for (auto &goal : goals) { + goal->Tick(dt); + } // do background stuff for (auto &need : needs) { need->ApplyEffect(*this, dt); } + if (goals.empty()) { + return; + } + // if active goal can be interrupted, check priorities + if (goals.size() > 1 && goals[0]->Interruptible()) { + std::sort(goals.begin(), goals.end(), GoalCompare); + } + goals[0]->Action(*this); + for (auto goal = goals.begin(); goal != goals.end();) { + if ((*goal)->Complete()) { + goals.erase(goal); + } else { + ++goal; + } + } } glm::dmat4 Creature::LocalTransform() noexcept { @@ -229,6 +270,14 @@ const world::TileType &Situation::GetTileType() const noexcept { return planet->GetSimulation().TileTypes()[GetTile().type]; } +void Situation::Move(const glm::dvec3 &dp) noexcept { + position += dp; + if (OnSurface()) { + // enforce ground constraint + position.z = std::max(0.0, position.z); + } +} + void Situation::SetPlanetSurface(world::Planet &p, int srf, const glm::dvec3 &pos) noexcept { type = PLANET_SURFACE; planet = &p; @@ -236,5 +285,63 @@ void Situation::SetPlanetSurface(world::Planet &p, int srf, const glm::dvec3 &po position = pos; } + +Steering::Steering() +: seek_target(0.0) +, max_accel(1.0) +, max_speed(1.0) +, halting(false) +, seeking(false) { +} + +Steering::~Steering() { +} + +void Steering::Halt() noexcept { + halting = true; + seeking = false; +} + +void Steering::GoTo(const glm::dvec3 &t) noexcept { + seek_target = t; + halting = false; + seeking = true; +} + +glm::dvec3 Steering::Acceleration(Creature &c) const noexcept { + glm::dvec3 acc(0.0); + if (halting) { + SumForce(acc, c.Velocity() * -max_accel); + } + if (seeking) { + glm::dvec3 diff = seek_target - c.GetSituation().Position(); + if (!allzero(diff)) { + SumForce(acc, ((normalize(diff) * max_speed) - c.Velocity()) * max_accel); + } + } + return acc; +} + +bool Steering::SumForce(glm::dvec3 &out, const glm::dvec3 &in) const noexcept { + if (allzero(in) || anynan(in)) { + return false; + } + double cur = allzero(out) ? 0.0 : length(out); + double rem = max_accel - cur; + if (rem < 0.0) { + return true; + } + double add = length(in); + if (add > rem) { + // this method is off if in and out are in different + // directions, but gives okayish results + out += in * (1.0 / add); + return true; + } else { + out += in; + return false; + } +} + } } diff --git a/src/creature/goal.cpp b/src/creature/goal.cpp new file mode 100644 index 0000000..1b9d5d0 --- /dev/null +++ b/src/creature/goal.cpp @@ -0,0 +1,84 @@ +#include "Goal.hpp" +#include "LocateResourceGoal.hpp" + +#include "Creature.hpp" +#include "../world/Planet.hpp" +#include "../world/TileType.hpp" + +namespace blobs { +namespace creature { + +Goal::Goal() +: urgency(0.0) +, interruptible(true) +, complete(false) { +} + +Goal::~Goal() noexcept { +} + + +LocateResourceGoal::LocateResourceGoal(int res) +: res(res) { +} + +LocateResourceGoal::~LocateResourceGoal() noexcept { +} + +void LocateResourceGoal::Enable(Creature &c) { +} + +void LocateResourceGoal::Tick(double dt) { +} + +void LocateResourceGoal::Action(Creature &c) { + if (c.GetSituation().OnSurface()) { + const world::TileType &t = c.GetSituation().GetTileType(); + auto yield = t.FindResource(res); + if (yield != t.resources.cend()) { + // hoooray + c.GetSteering().Halt(); + Complete(true); + } else { + // go find somewhere else + const world::Planet &planet = c.GetSituation().GetPlanet(); + double side_length = planet.SideLength(); + double offset = side_length * 0.5; + glm::ivec2 loc = glm::ivec2(c.GetSituation().Position() + offset); + glm::ivec2 seek_radius(2); + glm::ivec2 begin(glm::max(glm::ivec2(0), loc - seek_radius)); + glm::ivec2 end(glm::min(glm::ivec2(side_length), loc + seek_radius)); + const world::TileType::Yield *best = nullptr; + glm::ivec2 best_pos; + double best_distance = 2 * side_length * side_length; + for (int y = begin.y; y < end.y; ++y) { + for (int x = begin.x; x < end.x; ++x) { + const world::TileType &type = planet.TypeAt(c.GetSituation().Surface(), x, y); + auto yield = type.FindResource(res); + if (yield != type.resources.cend()) { + double dist = glm::length2(glm::dvec3(x - offset + 0.5, y - offset + 0.5, 0.0) - c.GetSituation().Position()); + if (!best) { + best = &*yield; + best_pos = glm::ivec2(x, y); + best_distance = dist; + } else if (yield->ubiquity + (dist * 0.125) > best->ubiquity + (best_distance * 0.125)) { + best = &*yield; + best_pos = glm::ivec2(x, y); + best_distance = dist; + } + } + } + } + if (best) { + c.GetSteering().GoTo(glm::dvec3(best_pos.x - offset + 0.5, best_pos.y - offset + 0.5, 0.0)); + } else { + // oh crap + } + } + } else { + // well, what now? + } +} + +} +} diff --git a/src/creature/need.cpp b/src/creature/need.cpp index 8249631..60e7cb8 100644 --- a/src/creature/need.cpp +++ b/src/creature/need.cpp @@ -3,6 +3,7 @@ #include "IngestNeed.hpp" #include "Creature.hpp" +#include "LocateResourceGoal.hpp" #include "../world/Planet.hpp" #include "../world/TileType.hpp" @@ -30,7 +31,8 @@ IngestNeed::IngestNeed(int resource, double speed, double damage) : resource(resource) , speed(speed) , damage(damage) -, ingesting(false) { +, ingesting(false) +, locating(false) { } IngestNeed::~IngestNeed() { @@ -40,16 +42,26 @@ void IngestNeed::ApplyEffect(Creature &c, double dt) { if (!IsSatisfied()) { ingesting = true; } - if (!IsSatisfied()) { - // TODO: find resource and start ingest task + if (ingesting) { if (c.GetSituation().OnSurface()) { const world::TileType &t = c.GetSituation().GetTileType(); + bool found = false; for (auto &yield : t.resources) { if (yield.resource == resource) { + found = true; + // TODO: check if not busy with something else Decrease(std::min(yield.ubiquity, speed) * dt); + if (value == 0.0) { + ingesting = false; + locating = false; + } break; } } + if (!found && !locating) { + c.AddGoal(std::unique_ptr(new LocateResourceGoal(resource))); + locating = true; + } } } if (IsCritical()) { diff --git a/src/graphics/glm.hpp b/src/graphics/glm.hpp index 1c39ba5..8003f0b 100644 --- a/src/graphics/glm.hpp +++ b/src/graphics/glm.hpp @@ -5,7 +5,10 @@ # define GLM_FORCE_RADIANS 1 #endif +#include #include +#include +#include // GLM moved tvec[1234] from glm::detail to glm in 0.9.6 @@ -20,4 +23,17 @@ namespace glm { #endif +template +inline bool allzero(const T &v) noexcept { + return glm::length2(v) < + std::numeric_limits::epsilon() + * std::numeric_limits::epsilon(); +} + +template +inline bool anynan(const T &v) noexcept { + return glm::any(glm::isnan(v)); +} + + #endif diff --git a/src/world/TileType.hpp b/src/world/TileType.hpp index c4eadcf..b5561ff 100644 --- a/src/world/TileType.hpp +++ b/src/world/TileType.hpp @@ -22,6 +22,8 @@ struct TileType { }; std::vector resources; + std::vector::const_iterator FindResource(int) const; + }; } diff --git a/src/world/world.cpp b/src/world/world.cpp index 449307a..0f9cb1e 100644 --- a/src/world/world.cpp +++ b/src/world/world.cpp @@ -535,5 +535,16 @@ Sun::Sun() Sun::~Sun() { } + +std::vector::const_iterator TileType::FindResource(int r) const { + auto yield = resources.cbegin(); + for (; yield != resources.cend(); ++yield) { + if (yield->resource == r) { + break; + } + } + return yield; +} + } } -- 2.39.2