]> git.localhorst.tv Git - blobs.git/commitdiff
split creature when it's "ripe" lol
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Sun, 26 Nov 2017 21:30:59 +0000 (22:30 +0100)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Sun, 26 Nov 2017 21:30:59 +0000 (22:30 +0100)
first split is at 293s
spoiler alert: they get stuck in the ocean and starve

18 files changed:
src/app/states.cpp
src/blobs.cpp
src/creature/Creature.hpp
src/creature/Genome.hpp
src/creature/Goal.hpp
src/creature/IdleGoal.hpp [new file with mode: 0644]
src/creature/Need.hpp
src/creature/Situation.hpp
src/creature/creature.cpp
src/creature/goal.cpp
src/creature/need.cpp
src/graphics/shader.cpp
src/ui/CreaturePanel.hpp
src/ui/ui.cpp
src/ui/widgets.cpp
src/world/Simulation.hpp
src/world/sim.cpp
src/world/world.cpp

index 9cf6a9c92f878f1ca5e5702a69aa78812ca466ca..d98a0991cbc67c4ec2dbb9c0c68030d2cfc1bb87 100644 (file)
@@ -125,7 +125,7 @@ void MasterState::OnRender(graphics::Viewport &viewport) {
        // TODO: extend to nearby bodies as well
        for (auto c : cam.Reference().Creatures()) {
                assets.shaders.creature_skin.SetM(cam.Model(cam.Reference()) * glm::mat4(c->LocalTransform()));
-               c->Draw(assets, viewport);
+               c->Draw(viewport);
        }
 
        viewport.ClearDepth();
index b32c4ab22e31a463c2f90e250bff482cd32172f5..ec143763a029e6b504294b8dcbad1e1165b2b718 100644 (file)
 
 using namespace blobs;
 
+namespace {
+
+struct SwitchPanel {
+       SwitchPanel(world::Planet &p, app::Application &app, app::MasterState &state)
+       : planet(p), app(app), state(state) { }
+
+       void operator ()(creature::Creature &c) {
+               if (planet.Creatures().empty()) {
+                       std::cout << "no more creatures, game over" << std::endl;
+                       while (app.HasState()) {
+                               app.PopState();
+                       }
+               } else {
+                       for (auto a : planet.Creatures()) {
+                               if (a != &c) {
+                                       state.GetCreaturePanel().Show(*a);
+                                       a->OnDeath([&](creature::Creature &b) { (*this)(b); });
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       world::Planet &planet;
+       app::Application &app;
+       app::MasterState &state;
+};
+}
+
 int main(int argc, char *argv[]) {
        app::Init init(true, 8);
        app::Assets assets;
@@ -49,7 +78,7 @@ int main(int argc, char *argv[]) {
        second_planet.AxialTilt(glm::dvec2(PI * 0.95, 0.0));
        second_planet.AngularMomentum(1.0e8);
 
-       world::Simulation sim(sun, assets.data.resources, assets.data.tile_types);
+       world::Simulation sim(sun, assets);
        sim.AddSun(sun);
        sim.AddPlanet(planet);
        sim.AddPlanet(second_planet);
@@ -68,9 +97,9 @@ int main(int argc, char *argv[]) {
        std::cout << "moon cycles per year: " << (planet.OrbitalPeriod() / moon.OrbitalPeriod()) << std::endl;
 
        auto blob = new creature::Creature(sim);
-       Spawn(*blob, planet, assets);
-       blob->BuildVAO();
        blob->Name("Blob");
+       Spawn(*blob, planet);
+       blob->BuildVAO();
 
        app::MasterState state(assets, sim);
        state.GetCamera()
@@ -92,6 +121,8 @@ int main(int argc, char *argv[]) {
        state.GetCreaturePanel().Show(*blob);
 
        app::Application app(init.window, init.viewport);
+       SwitchPanel swp(planet, app, state);
+       blob->OnDeath([&](creature::Creature &c) { swp(c); });
        app.PushState(&state);
        app.Run();
 
index 2a40d510407e4ff791a3e16c3d9b718066ca2d93..6c918270e540594bc18d0e19bd0240f80af50c80 100644 (file)
@@ -30,6 +30,9 @@ namespace creature {
 
 class Creature {
 
+public:
+       using Callback = std::function<void(Creature &)>;
+
 public:
        explicit Creature(world::Simulation &);
        ~Creature();
@@ -50,15 +53,28 @@ public:
        Genome &GetGenome() noexcept { return genome; }
        const Genome &GetGenome() const noexcept { return genome; }
 
-       void Mass(double m) noexcept { mass = m; }
+       Genome::Properties<double> &GetProperties() noexcept { return properties; }
+       const Genome::Properties<double> &GetProperties() const noexcept { return properties; }
+
+       void Mass(double m) noexcept { mass = m; size = std::cbrt(mass / density); }
        double Mass() const noexcept { return mass; }
+       void Grow(double amount) noexcept;
 
-       void Size(double s) noexcept { size = s; }
-       double Size() const noexcept { return size; }
+       void Density(double d) noexcept { density = d; size = std::cbrt(mass / density); }
+       double Density() const noexcept { return density; }
+
+       double Size() const noexcept;
+       double Age() const noexcept;
+       // change of giving birth per tick
+       double Fertility() const noexcept;
 
        void Health(double h) noexcept { health = h; }
        double Health() const noexcept { return health; }
        void Hurt(double d) noexcept;
+       void Die() noexcept;
+       void OnDeath(Callback cb) noexcept { on_death = cb; }
+       void Remove() noexcept { removable = true; }
+       bool Removable() const noexcept { return removable; }
 
        void AddNeed(std::unique_ptr<Need> &&n) { needs.emplace_back(std::move(n)); }
        const std::vector<std::unique_ptr<Need>> &Needs() const noexcept { return needs; }
@@ -76,22 +92,28 @@ public:
 
        void Velocity(const glm::dvec3 &v) noexcept { vel = v; }
        const glm::dvec3 &Velocity() const noexcept { return vel; }
-       bool Moving() const noexcept { return glm::length2(vel) < 0.000001; }
+       bool Moving() const noexcept { return glm::length2(vel) < 0.00000001; }
 
        glm::dmat4 LocalTransform() noexcept;
 
        void BuildVAO();
-       void Draw(app::Assets &, graphics::Viewport &);
+       void Draw(graphics::Viewport &);
 
 private:
        world::Simulation &sim;
        std::string name;
 
        Genome genome;
+       Genome::Properties<double> properties;
 
        double mass;
+       double density;
        double size;
+
+       double birth;
        double health;
+       Callback on_death;
+       bool removable;
 
        std::vector<std::unique_ptr<Need>> needs;
        std::vector<std::unique_ptr<Goal>> goals;
@@ -111,7 +133,10 @@ private:
 };
 
 /// put creature on planet and configure it to (hopefully) survive
-void Spawn(Creature &, world::Planet &, app::Assets &);
+void Spawn(Creature &, world::Planet &);
+
+/// split the creature into two
+void Split(Creature &);
 
 }
 }
index 4437150a6ef6e2a15163698117a29f5aa3c72536..057c6609915abc0cece270fbdb197a426215eb41 100644 (file)
@@ -16,19 +16,37 @@ class Creature;
 
 struct Genome {
 
+       template<class T>
+       struct Properties {
+               T birth_mass;
+               T fertile_mass;
+               T max_mass;
+
+               T fertile_age;
+               T infertile_age;
+               T death_age;
+
+               T fertility;
+       };
+       Properties<math::Distribution> properties;
+
+
        struct Composition {
                // which resource
                int resource;
                // how much contained in the body
+               // relative value to determine average density
                math::Distribution mass;
                // how much to circulate
                math::Distribution intake;
                // how important for alive-being
                math::Distribution penalty;
+               // how much off the mass may stay in the body
+               math::Distribution growth;
        };
        std::vector<Composition> composition;
 
-       void Configure(app::Assets &, Creature &) const;
+       void Configure(Creature &) const;
 
 };
 
index 2ec03bf255e7baa23f792e7d52a3188364d2e6f5..9ffeb0f006878f08e96f044436a8ab6987204130 100644 (file)
@@ -6,6 +6,9 @@
 
 
 namespace blobs {
+namespace app {
+       struct Assets;
+}
 namespace creature {
 
 class Creature;
@@ -28,6 +31,8 @@ public:
        const Situation &GetSituation() const noexcept;
        Steering &GetSteering() noexcept;
        const Steering &GetSteering() const noexcept;
+       app::Assets &Assets() noexcept;
+       const app::Assets &Assets() const noexcept;
 
        double Urgency() const noexcept { return urgency; }
        void Urgency(double u) noexcept { urgency = u; }
diff --git a/src/creature/IdleGoal.hpp b/src/creature/IdleGoal.hpp
new file mode 100644 (file)
index 0000000..eb402ba
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef BLOBS_CREATURE_IDLEGOAL_HPP_
+#define BLOBS_CREATURE_IDLEGOAL_HPP_
+
+#include "Goal.hpp"
+
+
+namespace blobs {
+namespace creature {
+
+class IdleGoal
+: public Goal {
+
+public:
+       explicit IdleGoal(Creature &);
+       ~IdleGoal() override;
+
+public:
+       std::string Describe() const override;
+       void Enable() override;
+       void Tick(double dt) override;
+       void Action() override;
+
+};
+
+}
+}
+
+#endif
index 67abe3c496d9ceab645625a986920cdee3a29c84..295b00f7161448490a06088d7b2ba6fd6075e6a2 100644 (file)
@@ -22,6 +22,8 @@ struct Need {
        double inconvenient = 0.0;
        // the value at which this need starts to hurt
        double critical = 0.0;
+       // factor of the intake that may stay in the body
+       double growth = 0.0;
 
        virtual ~Need() noexcept;
 
index d421b690d694eb8fb0bac7d77c21bf87fccec57d..df7abf2b9f0dab475d8904a1a9037898f33d003d 100644 (file)
@@ -18,6 +18,12 @@ public:
        Situation();
        ~Situation();
 
+       Situation(const Situation &) = delete;
+       Situation &operator =(const Situation &) = delete;
+
+       Situation(Situation &&) = delete;
+       Situation &operator =(Situation &&) = delete;
+
 public:
        bool OnPlanet() const noexcept;
        world::Planet &GetPlanet() const noexcept { return *planet; }
index 0f25da8ab5bed76b10a28a2b821abf396023e61a..0aa2b396324cc477c75b2c4313866f3d738a51c8 100644 (file)
@@ -4,6 +4,7 @@
 #include "Steering.hpp"
 
 #include "Goal.hpp"
+#include "IdleGoal.hpp"
 #include "InhaleNeed.hpp"
 #include "IngestNeed.hpp"
 #include "Need.hpp"
@@ -28,8 +29,12 @@ Creature::Creature(world::Simulation &sim)
 , name()
 , genome()
 , mass(1.0)
+, density(1.0)
 , size(1.0)
+, birth(sim.Time())
 , health(1.0)
+, on_death()
+, removable(false)
 , needs()
 , goals()
 , situation()
@@ -41,12 +46,47 @@ Creature::Creature(world::Simulation &sim)
 Creature::~Creature() {
 }
 
+void Creature::Grow(double amount) noexcept {
+       Mass(std::min(properties.max_mass, mass + amount));
+}
+
 void Creature::Hurt(double dt) noexcept {
        health = std::max(0.0, health - dt);
+       if (health == 0.0) {
+               Die();
+       }
+}
+
+void Creature::Die() noexcept {
+       needs.clear();
+       goals.clear();
+       steering.Halt();
+       if (on_death) {
+               on_death(*this);
+       }
+       Remove();
+}
+
+double Creature::Size() const noexcept {
+       return size;
+}
+
+double Creature::Age() const noexcept {
+       return sim.Time() - birth;
+}
+
+double Creature::Fertility() const noexcept {
+       double age = Age();
+       if (mass < properties.fertile_mass
+               || age < properties.fertile_age
+               || age > properties.infertile_age) {
+               return 0.0;
+       }
+       return properties.fertility / 3600.0;
 }
 
 void Creature::AddGoal(std::unique_ptr<Goal> &&g) {
-       std::cout << "new goal: " << g->Describe() << std::endl;
+       std::cout << "[" << int(sim.Time()) << "s] " << name << " new goal: " << g->Describe() << std::endl;
        g->Enable();
        goals.emplace_back(std::move(g));
 }
@@ -84,14 +124,16 @@ void Creature::Tick(double dt) {
                std::sort(goals.begin(), goals.end(), GoalCompare);
                Goal *new_top = &*goals[0];
                if (new_top != old_top) {
-                       std::cout << "changing goal from " << old_top->Describe()
+                       std::cout << "[" << int(sim.Time()) << "s] " << name
+                               << " changing goal from " << old_top->Describe()
                                << " to " << new_top->Describe() << std::endl;
                }
        }
        goals[0]->Action();
        for (auto goal = goals.begin(); goal != goals.end();) {
                if ((*goal)->Complete()) {
-                       std::cout << "complete goal: " << (*goal)->Describe() << std::endl;
+                       std::cout << "[" << int(sim.Time()) << "s] " << name
+                               << " complete goal: " << (*goal)->Describe() << std::endl;
                        goals.erase(goal);
                } else {
                        ++goal;
@@ -189,13 +231,13 @@ void Creature::BuildVAO() {
        vao.Unbind();
 }
 
-void Creature::Draw(app::Assets &assets, graphics::Viewport &viewport) {
+void Creature::Draw(graphics::Viewport &viewport) {
        vao.Bind();
        vao.DrawTriangles(6 * 6);
 }
 
 
-void Spawn(Creature &c, world::Planet &p, app::Assets &assets) {
+void Spawn(Creature &c, world::Planet &p) {
        p.AddCreature(&c);
        c.GetSituation().SetPlanetSurface(p, 0, p.TileCenter(0, p.SideLength() / 2, p.SideLength() / 2));
 
@@ -205,7 +247,7 @@ void Spawn(Creature &c, world::Planet &p, app::Assets &assets) {
        std::map<int, double> yields;
        for (int y = start; y < end; ++y) {
                for (int x = start; x < end; ++x) {
-                       const world::TileType &t = assets.data.tile_types[p.TileAt(0, x, y).type];
+                       const world::TileType &t = p.TypeAt(0, x, y);
                        for (auto yield : t.resources) {
                                yields[yield.resource] += yield.ubiquity;
                        }
@@ -214,11 +256,11 @@ void Spawn(Creature &c, world::Planet &p, app::Assets &assets) {
        int liquid = -1;
        int solid = -1;
        for (auto e : yields) {
-               if (assets.data.resources[e.first].state == world::Resource::LIQUID) {
+               if (c.GetSimulation().Resources()[e.first].state == world::Resource::LIQUID) {
                        if (liquid < 0 || e.second > yields[liquid]) {
                                liquid = e.first;
                        }
-               } else if (assets.data.resources[e.first].state == world::Resource::SOLID) {
+               } else if (c.GetSimulation().Resources()[e.first].state == world::Resource::SOLID) {
                        if (solid < 0 || e.second > yields[solid]) {
                                solid = e.first;
                        }
@@ -226,12 +268,21 @@ void Spawn(Creature &c, world::Planet &p, app::Assets &assets) {
        }
 
        Genome genome;
+       genome.properties.birth_mass = { 0.5, 0.1 };
+       genome.properties.fertile_mass = { 1.0, 0.1 };
+       genome.properties.max_mass = { 1.2, 0.1 };
+       genome.properties.fertile_age = { 60.0, 5.0 };
+       genome.properties.infertile_age = { 700.0, 30.0 };
+       genome.properties.death_age = { 900.0, 90.0 };
+       genome.properties.fertility = { 0.5, 0.01 };
+
        if (p.HasAtmosphere()) {
                genome.composition.push_back({
                        p.Atmosphere(),    // resource
                        { 0.01, 0.00001 }, // mass
                        { 0.5,  0.001 },   // intake
-                       { 0.1,  0.0005 }   // penalty
+                       { 0.1,  0.0005 },  // penalty
+                       { 0.0,  0.0 },     // growth
                });
        }
        if (liquid > -1) {
@@ -239,55 +290,100 @@ void Spawn(Creature &c, world::Planet &p, app::Assets &assets) {
                        liquid,          // resource
                        { 0.6,  0.01 },  // mass
                        { 0.2,  0.001 }, // intake
-                       { 0.01, 0.002 }  // penalty
+                       { 0.01, 0.002 }, // penalty
+                       { 0.1, 0.0 },   // growth
                });
        }
        if (solid > -1) {
                genome.composition.push_back({
-                       solid,           // resource
-                       { 0.4,   0.01 },  // mass
-                       { 0.1,   0.001 }, // intake
-                       { 0.001, 0.0001 } // penalty
+                       solid,             // resource
+                       { 0.4,   0.01 },   // mass
+                       //{ 0.1,   0.001 },  // intake
+                       { 0.4,   0.001 },  // intake
+                       { 0.001, 0.0001 }, // penalty
+                       { 10.0,  0.002 },   // growth
                });
        }
 
-       genome.Configure(assets, c);
-       c.GetSteering().MaxAcceleration(1.4);
-       c.GetSteering().MaxSpeed(4.4);
+       genome.Configure(c);
 }
 
-void Genome::Configure(app::Assets &assets, Creature &c) const {
+void Genome::Configure(Creature &c) const {
        c.GetGenome() = *this;
+
+       math::GaloisLFSR &random = c.GetSimulation().Assets().random;
+
+       c.GetProperties().birth_mass = properties.birth_mass.FakeNormal(random.SNorm());
+       c.GetProperties().fertile_mass = properties.fertile_mass.FakeNormal(random.SNorm());
+       c.GetProperties().max_mass = properties.max_mass.FakeNormal(random.SNorm());
+       c.GetProperties().fertile_age = properties.fertile_age.FakeNormal(random.SNorm());
+       c.GetProperties().infertile_age = properties.infertile_age.FakeNormal(random.SNorm());
+       c.GetProperties().death_age = properties.death_age.FakeNormal(random.SNorm());
+       c.GetProperties().fertility = properties.fertility.FakeNormal(random.SNorm());
+
        double mass = 0.0;
        double volume = 0.0;
        for (const auto &comp : composition) {
-               double comp_mass = comp.mass.FakeNormal(assets.random.SNorm());
-               double intake = comp.intake.FakeNormal(assets.random.SNorm());
-               double penalty = comp.intake.FakeNormal(assets.random.SNorm());
+               double comp_mass = comp.mass.FakeNormal(random.SNorm());
+               double intake = comp.intake.FakeNormal(random.SNorm());
+               double penalty = comp.penalty.FakeNormal(random.SNorm());
 
                mass += comp_mass;
-               volume += comp_mass / assets.data.resources[comp.resource].density;
+               volume += comp_mass / c.GetSimulation().Resources()[comp.resource].density;
 
                std::unique_ptr<Need> need;
-               if (assets.data.resources[comp.resource].state == world::Resource::SOLID) {
+               if (c.GetSimulation().Resources()[comp.resource].state == world::Resource::SOLID) {
                        need.reset(new IngestNeed(comp.resource, intake, penalty));
                        need->gain = intake * 0.05;
-               } else if (assets.data.resources[comp.resource].state == world::Resource::LIQUID) {
+               } else if (c.GetSimulation().Resources()[comp.resource].state == world::Resource::LIQUID) {
                        need.reset(new IngestNeed(comp.resource, intake, penalty));
                        need->gain = intake * 0.1;
                } else {
                        need.reset(new InhaleNeed(comp.resource, intake, penalty));
                        need->gain = intake * 0.5;
                }
-               need->name = assets.data.resources[comp.resource].label;
+               need->name = c.GetSimulation().Resources()[comp.resource].label;
+               need->growth = comp.growth.FakeNormal(random.SNorm());
                need->inconvenient = 0.5;
                need->critical = 0.95;
                c.AddNeed(std::move(need));
        }
-       c.Mass(mass);
-       c.Size(std::cbrt(volume));
+
+       c.Mass(c.GetProperties().birth_mass);
+       c.Density(mass / volume);
+       c.GetSteering().MaxAcceleration(1.4);
+       c.GetSteering().MaxSpeed(4.4);
+       c.AddGoal(std::unique_ptr<Goal>(new IdleGoal(c)));
+}
+
+
+void Split(Creature &c) {
+       Creature *a = new Creature(c.GetSimulation());
+       const Situation &s = c.GetSituation();
+       // TODO: generate names
+       a->Name("Blobby");
+       // TODO: mutate
+       c.GetGenome().Configure(*a);
+       s.GetPlanet().AddCreature(a);
+       // TODO: duplicate situation somehow
+       a->GetSituation().SetPlanetSurface(
+               s.GetPlanet(), s.Surface(),
+               s.Position() + glm::dvec3(0.0, a->Size() * 0.51, 0.0));
+       a->BuildVAO();
+
+       Creature *b = new Creature(c.GetSimulation());
+       b->Name("Sir Blobalot");
+       c.GetGenome().Configure(*b);
+       s.GetPlanet().AddCreature(b);
+       b->GetSituation().SetPlanetSurface(
+               s.GetPlanet(), s.Surface(),
+               s.Position() + glm::dvec3(0.0, b->Size() * -0.51, 0.0));
+       b->BuildVAO();
+
+       c.Die();
 }
 
+
 Situation::Situation()
 : planet(nullptr)
 , position(0.0)
index 18714e49cb195637d82ae1abfc0824e756b962ea..229dffc8f3e38e04510e13a82fa1141093bf9e32 100644 (file)
@@ -1,7 +1,9 @@
 #include "Goal.hpp"
+#include "IdleGoal.hpp"
 #include "LocateResourceGoal.hpp"
 
 #include "Creature.hpp"
+#include "../app/Assets.hpp"
 #include "../world/Planet.hpp"
 #include "../world/Resource.hpp"
 #include "../world/Simulation.hpp"
@@ -41,6 +43,14 @@ const Steering &Goal::GetSteering() const noexcept {
        return c.GetSteering();
 }
 
+app::Assets &Goal::Assets() noexcept {
+       return c.GetSimulation().Assets();
+}
+
+const app::Assets &Goal::Assets() const noexcept {
+       return c.GetSimulation().Assets();
+}
+
 void Goal::SetComplete() noexcept {
        if (!complete) {
                complete = true;
@@ -58,6 +68,37 @@ void Goal::OnComplete(std::function<void(Goal &)> cb) noexcept {
 }
 
 
+IdleGoal::IdleGoal(Creature &c)
+: Goal(c) {
+       Urgency(-1.0);
+       Interruptible(true);
+}
+
+IdleGoal::~IdleGoal() {
+}
+
+std::string IdleGoal::Describe() const {
+       return "idle";
+}
+
+void IdleGoal::Enable() {
+}
+
+void IdleGoal::Tick(double dt) {
+}
+
+void IdleGoal::Action() {
+       double fert = GetCreature().Fertility();
+       double rand = Assets().random.UNorm();
+       if (fert > rand) {
+               std::cout << "[" << int(GetCreature().GetSimulation().Time())
+                       << "s] splitting " << GetCreature().Name()
+                       << " because " << fert << " > " << rand << std::endl;
+               Split(GetCreature());
+       }
+}
+
+
 LocateResourceGoal::LocateResourceGoal(Creature &c, int res)
 : Goal(c)
 , res(res)
@@ -152,7 +193,6 @@ void LocateResourceGoal::SearchVicinity() {
                target_srf = srf;
                target_tile = best_pos;
                GetSteering().GoTo(target_pos);
-               std::cout << "found resource at " << target_pos << std::endl;
        } else {
                // oh crap
        }
index 64ebf5bb060d80a7384aa8503acd93ae9b9337f7..1b832b7e17483df2ed44f2ab85891006cc54e834 100644 (file)
@@ -50,7 +50,9 @@ void IngestNeed::ApplyEffect(Creature &c, double dt) {
                                if (yield.resource == resource) {
                                        found = true;
                                        // TODO: check if not busy with something else
-                                       Decrease(std::min(yield.ubiquity, speed) * dt);
+                                       double amount = std::min(yield.ubiquity, speed) * dt;
+                                       c.Grow(amount * growth * dt);
+                                       Decrease(amount);
                                        if (value == 0.0) {
                                                ingesting = false;
                                                if (locate_goal) {
index 1b641b07107d8f2954dca055c7e249e3c3b410fa..dbdeeb15552efa28699b4b7b50893e73ae9e10d3 100644 (file)
@@ -538,7 +538,7 @@ CreatureSkin::CreatureSkin()
 
                "void main() {\n"
                        "vec3 tex_color = texture(tex_sampler, frag_tex_uv).rgb;\n"
-                       "vec3 total_light = tex_color * vec3(0.01, 0.01, 0.01);\n"
+                       "vec3 total_light = tex_color * vec3(0.1, 0.1, 0.1);\n"
                        "for (int i = 0; i < num_lights; ++i) {\n"
                                "vec3 to_light = light[i].position - vtx_viewspace;\n"
                                "float distance = length(to_light) + length(vtx_viewspace);\n"
index 3a33b8ccff1ba8006ac6f4e2aa42c5e18662970b..55c24086f80b615236a5d856eb8675f43b5a160d 100644 (file)
@@ -45,6 +45,9 @@ private:
        creature::Creature *c;
 
        Label *name;
+       Label *age;
+       Label *mass;
+       Label *goal;
        Panel *needs;
        Panel panel;
 
index cedbcd127be0273798ab294b699c2cd40d128403..79adbbb970031b751693f6b6bdcedff6aedf83d3 100644 (file)
@@ -7,6 +7,8 @@
 #include "../creature/Need.hpp"
 #include "../graphics/Viewport.hpp"
 
+#include <iomanip>
+#include <sstream>
 #include <glm/gtx/transform.hpp>
 
 
@@ -17,6 +19,9 @@ CreaturePanel::CreaturePanel(const app::Assets &assets)
 : assets(assets)
 , c(nullptr)
 , name(new Label(assets.fonts.large))
+, age(new Label(assets.fonts.medium))
+, mass(new Label(assets.fonts.medium))
+, goal(new Label(assets.fonts.medium))
 , needs(new Panel)
 , panel()
 , health_meter(new Meter)
@@ -33,9 +38,41 @@ CreaturePanel::CreaturePanel(const app::Assets &assets)
        health_panel
                ->Add(health_label)
                ->Add(health_meter)
+               ->Spacing(10.0f)
+               ->Direction(Panel::HORIZONTAL);
+
+       Label *age_label = new Label(assets.fonts.medium);
+       age_label->Text("Age");
+       Panel *age_panel = new Panel;
+       age_panel
+               ->Add(age_label)
+               ->Add(age)
+               ->Spacing(10.0f)
+               ->Direction(Panel::HORIZONTAL);
+
+       Label *mass_label = new Label(assets.fonts.medium);
+       mass_label->Text("Mass");
+       Panel *mass_panel = new Panel;
+       mass_panel
+               ->Add(mass_label)
+               ->Add(mass)
+               ->Spacing(10.0f)
                ->Direction(Panel::HORIZONTAL);
+
+       Label *goal_label = new Label(assets.fonts.medium);
+       goal_label->Text("Goal");
+       Panel *goal_panel = new Panel;
+       goal_panel
+               ->Add(goal_label)
+               ->Add(goal)
+               ->Spacing(10.0f)
+               ->Direction(Panel::HORIZONTAL);
+
        panel
                .Add(name)
+               ->Add(age_panel)
+               ->Add(mass_panel)
+               ->Add(goal_panel)
                ->Add(health_panel)
                ->Add(needs)
                ->Padding(glm::vec2(10.0f))
@@ -88,6 +125,17 @@ void CreaturePanel::Hide() noexcept {
 void CreaturePanel::Draw(app::Assets &assets, graphics::Viewport &viewport) noexcept {
        if (!c) return;
 
+       age->Text(std::to_string(int(c->Age())) + "s");
+       {
+               std::stringstream ss;
+               ss << std::fixed << std::setprecision(3) << c->Mass() << "kg";
+               mass->Text(ss.str());
+       }
+       if (c->Goals().empty()) {
+               goal->Text("none");
+       } else {
+               goal->Text(c->Goals()[0]->Describe());
+       }
        health_meter->Value(c->Health());
 
        if (need_meters.size() != c->Needs().size()) {
index 49fdb69ff9048e0249b2a6621e75c724136b677b..6d88ad71b46772d7b636c53c6c3e89beee853a30 100644 (file)
@@ -175,7 +175,9 @@ Panel *Panel::Direction(Dir d) {
 }
 
 glm::vec2 Panel::Size() {
-       return (2.0f * padding) + glm::vec2(0.0f, (widgets.size() - 1) * spacing) + size;
+       glm::vec2 space(0.0f);
+       space[dir] = (widgets.size() - 1) * spacing;
+       return (2.0f * padding) + space + size;
 }
 
 void Panel::Relayout() {
index 2f2239efe1d9c77b03b1bd10e7151a7c0c659a0c..ee497a8f97a47b1ded9176b85f973ea7f5a645a9 100644 (file)
@@ -2,6 +2,7 @@
 #define BLOBS_WORLD_SIMULATION_HPP_
 
 #include "Set.hpp"
+#include "../app/Assets.hpp"
 
 #include <set>
 
@@ -18,7 +19,7 @@ class TileType;
 class Simulation {
 
 public:
-       explicit Simulation(Body &root, const Set<Resource> &, const Set<TileType> &);
+       explicit Simulation(Body &root, app::Assets &);
        ~Simulation();
 
        Simulation(const Simulation &) = delete;
@@ -37,8 +38,10 @@ public:
        Body &Root() noexcept { return root; }
        const Body &Root() const noexcept { return root; }
 
-       const Set<Resource> &Resources() const noexcept { return resources; }
-       const Set<TileType> &TileTypes() const noexcept { return tile_types; }
+       app::Assets &Assets() noexcept { return assets; }
+       const app::Assets &Assets() const noexcept { return assets; }
+       const Set<Resource> &Resources() const noexcept { return assets.data.resources; }
+       const Set<TileType> &TileTypes() const noexcept { return assets.data.tile_types; }
 
        const std::set<Body *> &Bodies() const noexcept { return bodies; }
        const std::set<Planet *> &Planets() const noexcept { return planets; }
@@ -48,8 +51,7 @@ public:
 
 private:
        Body &root;
-       const Set<Resource> &resources;
-       const Set<TileType> &tile_types;
+       app::Assets &assets;
        std::set<Body *> bodies;
        std::set<Planet *> planets;
        std::set<Sun *> suns;
index 071ad17cb0ed5786a998334c6e0fc9cb03a3ac1f..b6a198d7365a7336be1d7e224b6fe4aaddd1656b 100644 (file)
@@ -8,10 +8,9 @@
 namespace blobs {
 namespace world {
 
-Simulation::Simulation(Body &r, const Set<Resource> &res, const Set<TileType> &tile)
+Simulation::Simulation(Body &r, app::Assets &assets)
 : root(r)
-, resources(res)
-, tile_types(tile)
+, assets(assets)
 , bodies()
 , planets()
 , suns()
index 4f98d9a63bdef3b241123e8cf2bbdf30b76ce3de..91f9bc6751fbaedcda6f67fd1378720c87eda680 100644 (file)
@@ -144,6 +144,14 @@ void Body::Tick(double dt) {
        for (creature::Creature *c : Creatures()) {
                c->Tick(dt);
        }
+       for (auto c = Creatures().begin(); c != Creatures().end();) {
+               if ((*c)->Removable()) {
+                       delete *c;
+                       c = Creatures().erase(c);
+               } else {
+                       ++c;
+               }
+       }
 }
 
 void Body::Cache() noexcept {