X-Git-Url: https://git.localhorst.tv/?a=blobdiff_plain;f=src%2Fcreature%2Fcreature.cpp;h=1278e267135f4748fd125e9ddef6fe5844bb2ba5;hb=8a3907bb0bed257bf5d6f40f39f15114e8345913;hp=f279641ec445d4201f6670b326f0abd1221ae101;hpb=bcf776b6d51aeb9147bde32da8dd0768b10db993;p=blobs.git diff --git a/src/creature/creature.cpp b/src/creature/creature.cpp index f279641..1278e26 100644 --- a/src/creature/creature.cpp +++ b/src/creature/creature.cpp @@ -1,12 +1,18 @@ #include "Creature.hpp" -#include "Need.hpp" #include "Situation.hpp" +#include "Steering.hpp" +#include "Goal.hpp" +#include "InhaleNeed.hpp" +#include "IngestNeed.hpp" +#include "Need.hpp" #include "../app/Assets.hpp" #include "../world/Body.hpp" #include "../world/Planet.hpp" +#include "../world/Simulation.hpp" #include "../world/TileType.hpp" +#include #include #include @@ -19,19 +25,62 @@ Creature::Creature() : name() , health(1.0) , needs() +, goals() , situation() +, steering() +, vel(0.0) , vao() { } Creature::~Creature() { } +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) { - for (Need &need : needs) { - need.Tick(dt); - if (!need.IsSatisfied()) { - health = std::max(0.0, health - need.penalty * dt); + // 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; } } } @@ -142,7 +191,7 @@ void Spawn(Creature &c, world::Planet &p, app::Assets &assets) { std::map yields; for (int y = start; y < end; ++y) { for (int x = start; x < end; ++x) { - const world::TileType &t = assets.data.tiles[p.TileAt(0, x, y).type]; + const world::TileType &t = assets.data.tile_types[p.TileAt(0, x, y).type]; for (auto yield : t.resources) { yields[yield.resource] += yield.ubiquity; } @@ -164,39 +213,33 @@ void Spawn(Creature &c, world::Planet &p, app::Assets &assets) { if (p.HasAtmosphere()) { std::cout << "require breathing " << assets.data.resources[p.Atmosphere()].label << std::endl; - Need need; - need.resource = p.Atmosphere(); - need.gain = 0.25; - need.critical = 0.95; - need.penalty = 0.1; - c.AddNeed(need); + std::unique_ptr need(new InhaleNeed(p.Atmosphere(), 0.5, 0.1)); + need->name = assets.data.resources[p.Atmosphere()].label; + need->gain = 0.2; + need->inconvenient = 0.4; + need->critical = 0.95; + c.AddNeed(std::move(need)); } if (liquid > -1) { std::cout << "require drinking " << assets.data.resources[liquid].label << std::endl; - Need need; - need.resource = liquid; - need.gain = 0.0001; - need.critical = 0.95; - need.penalty = 0.01; - c.AddNeed(need); + std::unique_ptr need(new IngestNeed(liquid, 0.2, 0.01)); + need->name = assets.data.resources[liquid].label; + need->gain = 0.01; + need->inconvenient = 0.6; + need->critical = 0.95; + c.AddNeed(std::move(need)); } if (solid > -1) { std::cout << "require eating " << assets.data.resources[solid].label << std::endl; - Need need; - need.resource = solid; - need.gain = 0.00001; - need.critical = 0.95; - need.penalty = 0.001; - c.AddNeed(need); + std::unique_ptr need(new IngestNeed(solid, 0.1, 0.001)); + need->name = assets.data.resources[solid].label; + need->gain = 0.007; + need->inconvenient = 0.6; + need->critical = 0.95; + c.AddNeed(std::move(need)); } } - -void Need::Tick(double dt) noexcept { - value = std::min(1.0, value + gain * dt); -} - - Situation::Situation() : planet(nullptr) , position(0.0) @@ -211,6 +254,30 @@ bool Situation::OnPlanet() const noexcept { return type == PLANET_SURFACE; } +bool Situation::OnSurface() const noexcept { + return type == PLANET_SURFACE; +} + +world::Tile &Situation::GetTile() const noexcept { + double side_length = planet->SideLength(); + double offset = side_length * 0.5; + double x = std::max(0.0, std::min(side_length, position.x + offset)); + double y = std::max(0.0, std::min(side_length, position.y + offset)); + return planet->TileAt(surface, int(x), int(y)); +} + +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; @@ -218,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; + } +} + } }