X-Git-Url: https://git.localhorst.tv/?a=blobdiff_plain;f=src%2Fcreature%2Fcreature.cpp;h=1278e267135f4748fd125e9ddef6fe5844bb2ba5;hb=8a3907bb0bed257bf5d6f40f39f15114e8345913;hp=ed8551ca49bd1e8330d0336cf0eae885ab97812f;hpb=343b4f7e7cdd53dc1eab7dc32196ae5fa604ba48;p=blobs.git diff --git a/src/creature/creature.cpp b/src/creature/creature.cpp index ed8551c..1278e26 100644 --- a/src/creature/creature.cpp +++ b/src/creature/creature.cpp @@ -1,11 +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 @@ -15,24 +22,65 @@ namespace blobs { namespace creature { Creature::Creature() -: body(nullptr) -, surface(0) -, position() -, name() +: 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; } } } @@ -40,7 +88,8 @@ void Creature::Tick(double dt) { glm::dmat4 Creature::LocalTransform() noexcept { // TODO: surface transform constexpr double half_height = 0.25; - return glm::translate(glm::dvec3(position.x, position.y, position.z + body->Radius() + half_height)) + const glm::dvec3 &pos = situation.Position(); + return glm::translate(glm::dvec3(pos.x, pos.y, pos.z + situation.GetPlanet().Radius() + half_height)) * glm::scale(glm::dvec3(half_height, half_height, half_height)); } @@ -134,8 +183,7 @@ void Creature::Draw(app::Assets &assets, graphics::Viewport &viewport) { void Spawn(Creature &c, world::Planet &p, app::Assets &assets) { p.AddCreature(&c); - c.Surface(0); - c.Position(glm::dvec3(0.0, 0.0, 0.0)); + c.GetSituation().SetPlanetSurface(p, 0, glm::dvec3(0.0, 0.0, 0.0)); // probe surrounding area for common resources int start = p.SideLength() / 2 - 2; @@ -143,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; } @@ -165,36 +213,134 @@ 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)); } } +Situation::Situation() +: planet(nullptr) +, position(0.0) +, surface(0) +, type(LOST) { +} -void Need::Tick(double dt) noexcept { - value = std::min(1.0, value + gain * dt); +Situation::~Situation() { +} + +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; + surface = srf; + 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; + } } }