]> git.localhorst.tv Git - blobs.git/commitdiff
track a few things
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Sun, 3 Dec 2017 18:00:02 +0000 (19:00 +0100)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Sun, 3 Dec 2017 18:00:02 +0000 (19:00 +0100)
19 files changed:
src/app/MasterState.hpp
src/app/states.cpp
src/blobs.cpp
src/creature/Composition.hpp
src/creature/Creature.hpp
src/creature/Memory.hpp
src/creature/creature.cpp
src/creature/goal.cpp
src/ui/CreaturePanel.hpp
src/ui/Label.hpp
src/ui/RecordsPanel.hpp [new file with mode: 0644]
src/ui/TimePanel.hpp [new file with mode: 0644]
src/ui/string.hpp [new file with mode: 0644]
src/ui/ui.cpp
src/ui/widgets.cpp
src/world/Record.hpp [new file with mode: 0644]
src/world/Simulation.hpp
src/world/sim.cpp
src/world/world.cpp

index 24a29e696463e960918708f1be4151e4429b2de7..8290e86886b53d59c246ea36d076c34acadf4dfd 100644 (file)
@@ -6,6 +6,8 @@
 #include "Assets.hpp"
 #include "../graphics/Camera.hpp"
 #include "../ui/CreaturePanel.hpp"
+#include "../ui/RecordsPanel.hpp"
+#include "../ui/TimePanel.hpp"
 
 
 namespace blobs {
@@ -35,6 +37,12 @@ public:
        ui::CreaturePanel &GetCreaturePanel() noexcept { return cp; }
        const ui::CreaturePanel &GetCreaturePanel() const noexcept { return cp; }
 
+       ui::RecordsPanel &GetRecordsPanel() noexcept { return rp; }
+       const ui::RecordsPanel &GetRecordsPanel() const noexcept { return rp; }
+
+       ui::TimePanel &GetTimePanel() noexcept { return tp; }
+       const ui::TimePanel &GetTimePanel() const noexcept { return tp; }
+
 private:
        void OnResize(int w, int h) override;
 
@@ -61,6 +69,8 @@ private:
        bool cam_dragging;
 
        ui::CreaturePanel cp;
+       ui::RecordsPanel rp;
+       ui::TimePanel tp;
 
        int remain;
        int thirds;
index 49ee8e23ac1793b88709cb6862fcb42d2d08a01c..4951c9f220c5afe1308b13e504ed33d981d068e6 100644 (file)
@@ -24,6 +24,8 @@ MasterState::MasterState(Assets &assets, world::Simulation &sim) noexcept
 , cam_orient(PI * 0.375, PI * 0.25, 0.0)
 , cam_dragging(false)
 , cp(assets)
+, rp(sim)
+, tp(sim)
 , remain(0)
 , thirds(0)
 , paused(false) {
@@ -192,7 +194,9 @@ void MasterState::OnRender(graphics::Viewport &viewport) {
        }
 
        viewport.ClearDepth();
-       cp.Draw(assets, viewport);
+       cp.Draw(viewport);
+       rp.Draw(viewport);
+       tp.Draw(viewport);
 }
 
 }
index 9a94b96e0f55b87a4855efff5d1d434e0300fa92..d6a6fede8469f0b81518b291316d6def3356a9bc 100644 (file)
@@ -115,6 +115,7 @@ int main(int argc, char *argv[]) {
        //      .Orbital(glm::vec3(-500.0f, 500.0f, 500.0f))
        //;
        state.GetCreaturePanel().Show(*blob);
+       state.GetTimePanel().SetBody(planet);
 
        app::Application app(init.window, init.viewport);
        SwitchPanel swp(planet, app, state);
index 06b1c744b51b2a94e2b961feb8222c72563c813d..621f6406f77dbd696bbdf248f77f31854e9471b0 100644 (file)
@@ -31,8 +31,10 @@ public:
        void Add(int res, double amount);
        bool Has(int res) const noexcept;
        double Get(int res) const noexcept;
+       double TotalMass() const noexcept { return total_mass; }
 
 public:
+       std::vector<Component>::size_type size() const noexcept { return components.size(); }
        std::vector<Component>::iterator begin() noexcept { return components.begin(); }
        std::vector<Component>::iterator end() noexcept { return components.end(); }
        std::vector<Component>::const_iterator begin() const noexcept { return components.begin(); }
@@ -42,6 +44,7 @@ public:
 
 private:
        std::vector<Component> components;
+       double total_mass;
 
 };
 
index 13716878b06f2c6141ffe577e814b2fb2e489edd..3f86670eba4a9aa885e8056c5b3b1e7dd15a7c08 100644 (file)
@@ -134,8 +134,12 @@ public:
        void Hurt(double d) noexcept;
        void Die() noexcept;
        void OnDeath(Callback cb) noexcept { on_death = cb; }
-       void Remove() noexcept { removable = true; }
+       void Remove() noexcept;
        bool Removable() const noexcept { return removable; }
+       void Removed() noexcept;
+
+       void AddParent(Creature &);
+       const std::vector<Creature *> &Parents() const noexcept { return parents; }
 
        Stats &GetStats() noexcept { return stats; }
        const Stats &GetStats() const noexcept { return stats; }
@@ -164,6 +168,7 @@ public:
        glm::dmat4 LocalTransform() noexcept;
 
        void BuildVAO();
+       void KillVAO();
        void Draw(graphics::Viewport &);
 
 private:
@@ -187,9 +192,12 @@ private:
        double size;
 
        double birth;
+       double death;
        Callback on_death;
        bool removable;
 
+       std::vector<Creature *> parents;
+
        Stats stats;
        Memory memory;
 
@@ -204,7 +212,7 @@ private:
                glm::vec3 normal;
                glm::vec3 texture;
        };
-       graphics::SimpleVAO<Attributes, unsigned short> vao;
+       std::unique_ptr<graphics::SimpleVAO<Attributes, unsigned short>> vao;
 
 };
 
index a6910cc196b6cab64be32c68daf074a55d3a6e74..3bc39082b0741017ecae5bebcb69fd8776867eb8 100644 (file)
@@ -30,6 +30,8 @@ public:
 public:
        void Tick(double dt);
 
+       void Erase();
+
 private:
        /// track time spent on a tile
        void TrackStay(const Location &, double t);
index b95b531a13097041c2b7e835d4d69483f3798446..fb6e9f6540b2872152bc390d07623cf779cfe26b 100644 (file)
@@ -11,6 +11,7 @@
 #include "IdleGoal.hpp"
 #include "../app/Assets.hpp"
 #include "../math/const.hpp"
+#include "../ui/string.hpp"
 #include "../world/Body.hpp"
 #include "../world/Planet.hpp"
 #include "../world/Simulation.hpp"
@@ -29,7 +30,8 @@ namespace blobs {
 namespace creature {
 
 Composition::Composition()
-: components() {
+: components()
+, total_mass(0.0) {
 }
 
 Composition::~Composition() {
@@ -54,6 +56,7 @@ void Composition::Add(int res, double amount) {
                components.emplace_back(res, amount);
        }
        std::sort(components.begin(), components.end(), CompositionCompare);
+       total_mass += amount;
 }
 
 bool Composition::Has(int res) const noexcept {
@@ -86,8 +89,10 @@ Creature::Creature(world::Simulation &sim)
 , mass(1.0)
 , size(1.0)
 , birth(sim.Time())
+, death(0.0)
 , on_death()
 , removable(false)
+, parents()
 , stats()
 , memory(*this)
 , bg_task()
@@ -95,6 +100,7 @@ Creature::Creature(world::Simulation &sim)
 , situation()
 , steering(*this)
 , vao() {
+       sim.SetAlive(this);
        // all creatures avoid each other for now
        steering.Separate(0.1, 1.5);
 }
@@ -104,19 +110,17 @@ Creature::~Creature() {
 
 void Creature::AddMass(int res, double amount) {
        composition.Add(res, amount);
-       double mass = 0.0;
        double nonsolid = 0.0;
        double volume = 0.0;
        for (const auto &c : composition) {
-               mass += c.value;
                volume += c.value / sim.Assets().data.resources[c.resource].density;
                if (sim.Assets().data.resources[c.resource].state != world::Resource::SOLID) {
                        nonsolid += c.value;
                }
        }
-       Mass(mass);
+       Mass(composition.TotalMass());
        Size(std::cbrt(volume));
-       highlight_color.a = nonsolid / mass;
+       highlight_color.a = nonsolid / composition.TotalMass();
 }
 
 void Creature::HighlightColor(const glm::dvec3 &c) noexcept {
@@ -125,14 +129,19 @@ void Creature::HighlightColor(const glm::dvec3 &c) noexcept {
 
 void Creature::Ingest(int res, double amount) noexcept {
        // TODO: check foreign materials
-       // 10% stays in body
-       AddMass(res, amount * 0.1);
+       if (sim.Resources()[res].state == world::Resource::SOLID) {
+               // 15% of solids stays in body
+               AddMass(res, amount * 0.15);
+       } else {
+               // 10% of fluids stays in body
+               AddMass(res, amount * 0.05);
+       }
 }
 
 void Creature::Hurt(double amount) noexcept {
        stats.Damage().Add(amount);
        if (stats.Damage().Full()) {
-               std::cout << "[" << int(sim.Time()) << "s] " << name << " ";
+               std::cout << "[" << ui::TimeString(sim.Time()) << "] " << name << " ";
                if (stats.Exhaustion().Full()) {
                        std::cout << "died of exhaustion";
                } else if (stats.Breath().Full()) {
@@ -144,40 +153,17 @@ void Creature::Hurt(double amount) noexcept {
                } else {
                        std::cout << "succumed to wounds";
                }
-               std::cout << " at an age of ";
-               {
-                       int age = int(Age());
-                       if (age >= 3600) {
-                               std::cout << (age / 3600) << "h ";
-                               age %= 3600;
-                       }
-                       if (age >= 60) {
-                               std::cout << (age / 60) << "m ";
-                               age %= 60;
-                       }
-                       std::cout << age << 's';
-               }
-               std::cout << " (" << int(Age() / properties.Lifetime() * 100)
-                       << "% of life expectancy of ";
-               {
-                       int lt = int(properties.Lifetime());
-                       if (lt >= 3600) {
-                               std::cout << (lt / 3600) << "h ";
-                               lt %= 3600;
-                       }
-                       if (lt >= 60) {
-                               std::cout << (lt / 60) << "m ";
-                               lt %= 60;
-                       }
-                       std::cout << lt << 's';
-               }
-               std::cout << ")" << std::endl;
+               std::cout << " at an age of " << ui::TimeString(Age())
+                       << " (" << ui::PercentageString(Age() / properties.Lifetime())
+                       << "% of life expectancy of " << ui::TimeString(properties.Lifetime())
+                       << ")" << std::endl;
                Die();
        }
 }
 
 void Creature::Die() noexcept {
-       goals.clear();
+       sim.SetDead(this);
+       death = sim.Time();
        steering.Halt();
        if (on_death) {
                on_death(*this);
@@ -185,6 +171,21 @@ void Creature::Die() noexcept {
        Remove();
 }
 
+void Creature::Remove() noexcept {
+       removable = true;
+}
+
+void Creature::Removed() noexcept {
+       bg_task.reset();
+       goals.clear();
+       memory.Erase();
+       KillVAO();
+}
+
+void Creature::AddParent(Creature &p) {
+       parents.push_back(&p);
+}
+
 double Creature::Age() const noexcept {
        return sim.Time() - birth;
 }
@@ -391,17 +392,18 @@ glm::dmat4 Creature::LocalTransform() noexcept {
 }
 
 void Creature::BuildVAO() {
-       vao.Bind();
-       vao.BindAttributes();
-       vao.EnableAttribute(0);
-       vao.EnableAttribute(1);
-       vao.EnableAttribute(2);
-       vao.AttributePointer<glm::vec3>(0, false, offsetof(Attributes, position));
-       vao.AttributePointer<glm::vec3>(1, false, offsetof(Attributes, normal));
-       vao.AttributePointer<glm::vec3>(2, false, offsetof(Attributes, texture));
-       vao.ReserveAttributes(6 * 4, GL_STATIC_DRAW);
+       vao.reset(new graphics::SimpleVAO<Attributes, unsigned short>);
+       vao->Bind();
+       vao->BindAttributes();
+       vao->EnableAttribute(0);
+       vao->EnableAttribute(1);
+       vao->EnableAttribute(2);
+       vao->AttributePointer<glm::vec3>(0, false, offsetof(Attributes, position));
+       vao->AttributePointer<glm::vec3>(1, false, offsetof(Attributes, normal));
+       vao->AttributePointer<glm::vec3>(2, false, offsetof(Attributes, texture));
+       vao->ReserveAttributes(6 * 4, GL_STATIC_DRAW);
        {
-               auto attrib = vao.MapAttributes(GL_WRITE_ONLY);
+               auto attrib = vao->MapAttributes(GL_WRITE_ONLY);
                const float offset = 1.0f;
                for (int surface = 0; surface < 6; ++surface) {
                        const float tex_u_begin = surface < 3 ? 1.0f : 0.0f;
@@ -448,10 +450,10 @@ void Creature::BuildVAO() {
                        attrib[4 * surface + 3].texture.z = surface;
                }
        }
-       vao.BindElements();
-       vao.ReserveElements(6 * 6, GL_STATIC_DRAW);
+       vao->BindElements();
+       vao->ReserveElements(6 * 6, GL_STATIC_DRAW);
        {
-               auto element = vao.MapElements(GL_WRITE_ONLY);
+               auto element = vao->MapElements(GL_WRITE_ONLY);
                for (int surface = 0; surface < 3; ++surface) {
                        element[6 * surface + 0] = 4 * surface + 0;
                        element[6 * surface + 1] = 4 * surface + 2;
@@ -469,12 +471,17 @@ void Creature::BuildVAO() {
                        element[6 * surface + 5] = 4 * surface + 3;
                }
        }
-       vao.Unbind();
+       vao->Unbind();
+}
+
+void Creature::KillVAO() {
+       vao.reset();
 }
 
 void Creature::Draw(graphics::Viewport &viewport) {
-       vao.Bind();
-       vao.DrawTriangles(6 * 6);
+       if (!vao) return;
+       vao->Bind();
+       vao->DrawTriangles(6 * 6);
 }
 
 
@@ -585,6 +592,7 @@ void Genome::Configure(Creature &c) const {
 void Split(Creature &c) {
        Creature *a = new Creature(c.GetSimulation());
        const Situation &s = c.GetSituation();
+       a->AddParent(c);
        a->Name(c.GetSimulation().Assets().name.Sequential());
        c.GetGenome().Configure(*a);
        for (const auto &cmp : c.GetComposition()) {
@@ -596,10 +604,11 @@ void Split(Creature &c) {
                s.GetPlanet(), s.Surface(),
                s.Position() + glm::dvec3(0.0, 0.55 * a->Size(), 0.0));
        a->BuildVAO();
-       std::cout << "[" << int(c.GetSimulation().Time()) << "s] "
+       std::cout << "[" << ui::TimeString(c.GetSimulation().Time()) << "] "
                << a->Name() << " was born" << std::endl;
 
        Creature *b = new Creature(c.GetSimulation());
+       b->AddParent(c);
        b->Name(c.GetSimulation().Assets().name.Sequential());
        c.GetGenome().Configure(*b);
        for (const auto &cmp : c.GetComposition()) {
@@ -610,7 +619,7 @@ void Split(Creature &c) {
                s.GetPlanet(), s.Surface(),
                s.Position() - glm::dvec3(0.0, 0.55 * b->Size(), 0.0));
        b->BuildVAO();
-       std::cout << "[" << int(c.GetSimulation().Time()) << "s] "
+       std::cout << "[" << ui::TimeString(c.GetSimulation().Time()) << "] "
                << b->Name() << " was born" << std::endl;
 
        c.Die();
@@ -624,6 +633,10 @@ Memory::Memory(Creature &c)
 Memory::~Memory() {
 }
 
+void Memory::Erase() {
+       known_types.clear();
+}
+
 void Memory::Tick(double dt) {
        Situation &s = c.GetSituation();
        if (s.OnTile()) {
index 06a689f2e0d6d1cebbe29e3c75c7224470bed6ab..f3e0891c1e1ca5cf7e6b98846f4ed43aca0688f2 100644 (file)
@@ -37,8 +37,16 @@ std::string BlobBackgroundTask::Describe() const {
 void BlobBackgroundTask::Tick(double dt) {
        if (breathing) {
                // TODO: derive breathing ability
+               int gas = Assets().data.resources["air"].id;
+               // TODO: check if in compatible atmosphere
                double amount = GetCreature().GetStats().Breath().gain * -(1.5 + 0.5 * GetCreature().ExhaustionFactor());
                GetCreature().GetStats().Breath().Add(amount * dt);
+               // maintain ~2.5% gas composition
+               double gas_amount = GetCreature().GetComposition().Get(gas);
+               if (gas_amount < GetCreature().GetComposition().TotalMass() * 0.025) {
+                       double add = std::min(GetCreature().GetComposition().TotalMass() * 0.025 - gas_amount, -amount * dt);
+                       GetCreature().Ingest(gas, add);
+               }
                if (GetCreature().GetStats().Breath().Empty()) {
                        breathing = false;
                }
index 967e03914a4c023fffb92eaefa907554b7452942..573804dff897ba09dcf926da90ffaee5c48d2d2c 100644 (file)
@@ -3,6 +3,8 @@
 
 #include "Panel.hpp"
 
+#include <vector>
+
 
 namespace blobs {
 namespace app {
@@ -22,7 +24,7 @@ class Meter;
 class CreaturePanel {
 
 public:
-       explicit CreaturePanel(const app::Assets &);
+       explicit CreaturePanel(app::Assets &);
        ~CreaturePanel();
 
        CreaturePanel(const CreaturePanel &) = delete;
@@ -38,24 +40,20 @@ public:
        bool Shown() const noexcept { return c; }
        const creature::Creature &GetCreature() const noexcept { return *c; }
 
-       void Draw(app::Assets &, graphics::Viewport &) noexcept;
-
-private:
-       void CreateNeeds();
+       void Draw(graphics::Viewport &) noexcept;
 
 private:
-       const app::Assets &assets;
+       app::Assets &assets;
        creature::Creature *c;
 
        Label *name;
+       Label *parents;
        Label *born;
        Label *age;
        Label *mass;
-       Label *pos;
-       Label *vel;
-       Label *dir;
-       Label *tile;
        Label *goal;
+       Panel *composition;
+       std::vector<Label *> components;
        Meter *stats[7];
        Label *props[8];
        Panel panel;
index 267cc639c6bc1b39470593f57db496a0d86aae68..1bc7e86bc4f0de8e733818c5eb3bd427b556bf1c 100644 (file)
@@ -22,11 +22,6 @@ public:
 
 public:
        Label *Text(const std::string &);
-       Label *Decimal(double n, int prec = 2);
-       Label *Length(double m);
-       Label *Mass(double kg);
-       Label *Percentage(double n);
-       Label *Time(double s);
        Label *Font(const graphics::Font &);
        Label *Foreground(const glm::vec4 &);
        Label *Background(const glm::vec4 &);
diff --git a/src/ui/RecordsPanel.hpp b/src/ui/RecordsPanel.hpp
new file mode 100644 (file)
index 0000000..032ad56
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef BLOBS_UI_RECORDSPANEL_HPP_
+#define BLOBS_UI_RECORDSPANEL_HPP_
+
+#include "Panel.hpp"
+
+
+namespace blobs {
+namespace graphics {
+       class Viewport;
+}
+namespace world {
+       class Simulation;
+}
+namespace ui {
+
+class Label;
+
+class RecordsPanel {
+
+public:
+       explicit RecordsPanel(world::Simulation &);
+       ~RecordsPanel();
+
+public:
+       void Draw(graphics::Viewport &) noexcept;
+
+private:
+       world::Simulation &sim;
+       Label *live;
+       std::vector<Label *> records;
+       std::vector<Label *> holders;
+       Panel panel;
+
+};
+
+}
+}
+
+#endif
diff --git a/src/ui/TimePanel.hpp b/src/ui/TimePanel.hpp
new file mode 100644 (file)
index 0000000..a150cf3
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef BLOBS_UI_TIMEPANEL_HPP_
+#define BLOBS_UI_TIMEPANEL_HPP_
+
+#include "Panel.hpp"
+
+
+namespace blobs {
+namespace graphics {
+       class Viewport;
+}
+namespace world {
+       class Body;
+       class Simulation;
+}
+namespace ui {
+
+class Label;
+
+class TimePanel {
+
+public:
+       explicit TimePanel(world::Simulation &);
+       ~TimePanel();
+
+public:
+       void SetBody(world::Body &b) noexcept { body = &b; }
+       void UnsetBody() noexcept { body = nullptr; }
+       void Draw(graphics::Viewport &) noexcept;
+
+private:
+       world::Simulation &sim;
+       world::Body *body;
+       Label *time;
+       Label *clock;
+       Panel panel;
+
+};
+
+}
+}
+
+#endif
diff --git a/src/ui/string.hpp b/src/ui/string.hpp
new file mode 100644 (file)
index 0000000..ff20ec1
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef BLOBS_UI_STRING_HPP_
+#define BLOBS_UI_STRING_HPP_
+
+#include "../math/glm.hpp"
+
+#include <string>
+
+namespace blobs {
+namespace ui {
+
+std::string DecimalString(double n, int p);
+std::string LengthString(double m);
+std::string MassString(double kg);
+std::string NumberString(int n);
+std::string PercentageString(double n);
+std::string TimeString(double s);
+std::string VectorString(const glm::dvec3 &, int p);
+std::string VectorString(const glm::ivec2 &);
+
+}
+}
+
+#endif
index becfb9813297002ac0d638b5d84e3bea2051f887..4cd660624cdeebd25f0f0f9c4c1a79278ca2c7bb 100644 (file)
@@ -1,10 +1,15 @@
 #include "CreaturePanel.hpp"
+#include "RecordsPanel.hpp"
+#include "string.hpp"
+#include "TimePanel.hpp"
 
 #include "Label.hpp"
 #include "Meter.hpp"
 #include "../app/Assets.hpp"
 #include "../creature/Creature.hpp"
 #include "../graphics/Viewport.hpp"
+#include "../world/Body.hpp"
+#include "../world/Simulation.hpp"
 
 #include <iomanip>
 #include <iostream>
 namespace blobs {
 namespace ui {
 
-CreaturePanel::CreaturePanel(const app::Assets &assets)
+CreaturePanel::CreaturePanel(app::Assets &assets)
 : assets(assets)
 , c(nullptr)
 , name(new Label(assets.fonts.large))
+, parents(new Label(assets.fonts.medium))
 , born(new Label(assets.fonts.medium))
 , age(new Label(assets.fonts.medium))
 , mass(new Label(assets.fonts.medium))
-, pos(new Label(assets.fonts.medium))
-, vel(new Label(assets.fonts.medium))
-, dir(new Label(assets.fonts.medium))
-, tile(new Label(assets.fonts.medium))
 , goal(new Label(assets.fonts.medium))
+, composition(new Panel)
 , stats{nullptr}
 , props{nullptr}
 , panel() {
-       born->Text("00h 00m 00s");
+       Label *parents_label = new Label(assets.fonts.medium);
+       parents_label->Text("Parents");
        Label *born_label = new Label(assets.fonts.medium);
        born_label->Text("Born");
-       Panel *born_panel = new Panel;
-       born_panel
-               ->Add(born_label)
-               ->Add(born)
-               ->Spacing(10.0f)
-               ->Direction(Panel::HORIZONTAL);
-
-       age->Text("00h 00m 00s");
        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);
-
-       mass->Text("00.000kg");
        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);
-
-       pos->Text("<00.0, 00.0, 00.0>");
-       Label *pos_label = new Label(assets.fonts.medium);
-       pos_label->Text("Pos");
-       Panel *pos_panel = new Panel;
-       pos_panel
-               ->Add(pos_label)
-               ->Add(pos)
-               ->Spacing(10.0f)
-               ->Direction(Panel::HORIZONTAL);
-
-       vel->Text("<00.0, 00.0, 00.0>");
-       Label *vel_label = new Label(assets.fonts.medium);
-       vel_label->Text("Vel");
-       Panel *vel_panel = new Panel;
-       vel_panel
-               ->Add(vel_label)
-               ->Add(vel)
-               ->Spacing(10.0f)
-               ->Direction(Panel::HORIZONTAL);
-
-       dir->Text("<0.00, 0.00, 0.00>");
-       Label *dir_label = new Label(assets.fonts.medium);
-       dir_label->Text("Dir");
-       Panel *dir_panel = new Panel;
-       dir_panel
-               ->Add(dir_label)
-               ->Add(dir)
-               ->Spacing(10.0f)
-               ->Direction(Panel::HORIZONTAL);
-
-       tile->Text("<00, 00> (mountains)");
-       Label *tile_label = new Label(assets.fonts.medium);
-       tile_label->Text("Tile");
-       Panel *tile_panel = new Panel;
-       tile_panel
-               ->Add(tile_label)
-               ->Add(tile)
-               ->Spacing(10.0f)
-               ->Direction(Panel::HORIZONTAL);
-
-       goal->Text("long goal description");
        Label *goal_label = new Label(assets.fonts.medium);
        goal_label->Text("Goal");
-       Panel *goal_panel = new Panel;
-       goal_panel
-               ->Add(goal_label)
-               ->Add(goal)
+
+       Panel *info_label_panel = new Panel;
+       info_label_panel
+               ->Direction(Panel::VERTICAL)
+               ->Add(parents_label)
+               ->Add(born_label)
+               ->Add(age_label)
+               ->Add(mass_label)
+               ->Add(goal_label);
+       Panel *info_value_panel = new Panel;
+       info_value_panel
+               ->Direction(Panel::VERTICAL)
+               ->Add(parents)
+               ->Add(born)
+               ->Add(age)
+               ->Add(mass)
+               ->Add(goal);
+       Panel *info_panel = new Panel;
+       info_panel
+               ->Direction(Panel::HORIZONTAL)
                ->Spacing(10.0f)
-               ->Direction(Panel::HORIZONTAL);
+               ->Add(info_label_panel)
+               ->Add(info_value_panel);
 
        Label *stat_label[7];
        for (int i = 0; i < 7; ++i) {
@@ -183,14 +140,8 @@ CreaturePanel::CreaturePanel(const app::Assets &assets)
 
        panel
                .Add(name)
-               ->Add(age_panel)
-               ->Add(born_panel)
-               ->Add(mass_panel)
-               ->Add(pos_panel)
-               ->Add(vel_panel)
-               ->Add(dir_panel)
-               ->Add(tile_panel)
-               ->Add(goal_panel)
+               ->Add(info_panel)
+               ->Add(composition)
                ->Add(stat_panel)
                ->Add(prop_panel)
                ->Padding(glm::vec2(10.0f))
@@ -206,53 +157,66 @@ CreaturePanel::~CreaturePanel() {
 void CreaturePanel::Show(creature::Creature &cr) {
        c = &cr;
        name->Text(c->Name());
-       born->Time(c->Born());
+       born->Text(TimeString(c->Born()));
+
+       if (c->Parents().empty()) {
+               parents->Text("none");
+       } else {
+               std::string parent_string;
+               bool first = true;
+               for (auto p : c->Parents()) {
+                       if (first) {
+                               first = false;
+                       } else {
+                               parent_string += " and ";
+                       }
+                       parent_string += p->Name();
+               }
+               parents->Text(parent_string);
+       }
 }
 
 void CreaturePanel::Hide() noexcept {
        c = nullptr;
 }
 
-void CreaturePanel::Draw(app::Assets &assets, graphics::Viewport &viewport) noexcept {
+void CreaturePanel::Draw(graphics::Viewport &viewport) noexcept {
        if (!c) return;
 
-       age->Time(c->Age());
-       mass->Mass(c->Mass());
-       {
-               const glm::dvec3 &p = c->GetSituation().Position();
-               std::stringstream ss;
-               ss << std::fixed << std::setprecision(1)
-                       << "<" << p.x << ", " << p.y << ", " << p.z << ">";
-               pos->Text(ss.str());
-       }
-       {
-               const glm::dvec3 &v = c->GetSituation().Velocity();
-               std::stringstream ss;
-               ss << std::fixed << std::setprecision(1)
-                       << "<" << v.x << ", " << v.y << ", " << v.z << ">";
-               vel->Text(ss.str());
-       }
-       {
-               const glm::dvec3 &d = c->GetSituation().GetState().dir;
-               std::stringstream ss;
-               ss << std::fixed << std::setprecision(2)
-                       << "<" << d.x << ", " << d.y << ", " << d.z << ">";
-               dir->Text(ss.str());
-       }
-       {
-               glm::ivec2 t = c->GetSituation().SurfacePosition();
-               std::stringstream ss;
-               ss << std::fixed << std::setprecision(1)
-                       << "<" << t.x << ", " << t.y
-                       << "> (" << c->GetSituation().GetTileType().label << ")";
-               tile->Text(ss.str());
-       }
+       age->Text(TimeString(c->Age()));
+       mass->Text(MassString(c->Mass()));
        if (c->Goals().empty()) {
                goal->Text("none");
        } else {
                goal->Text(c->Goals()[0]->Describe());
        }
 
+       const creature::Composition &comp = c->GetComposition();
+       if (comp.size() < components.size()) {
+               composition->Clear();
+               while (comp.size() < components.size()) {
+                       delete components.back();
+                       components.pop_back();
+               }
+               for (auto l : components) {
+                       composition->Add(l);
+               }
+       } else {
+               while (comp.size() > components.size()) {
+                       components.emplace_back(new Label(assets.fonts.medium));
+                       composition->Add(components.back());
+               }
+       }
+       {
+               int i = 0;
+               for (auto &cmp : comp) {
+                       components[i]->Text(
+                               PercentageString(cmp.value / comp.TotalMass())
+                               + " " + assets.data.resources[cmp.resource].label);
+                       ++i;
+               }
+       }
+
        for (int i = 0; i < 7; ++i) {
                stats[i]->Value(c->GetStats().stat[i].value);
                if (c->GetStats().stat[i].Okay()) {
@@ -264,20 +228,239 @@ void CreaturePanel::Draw(app::Assets &assets, graphics::Viewport &viewport) noex
                }
        }
 
-       props[0]->Decimal(c->Strength());
-       props[1]->Decimal(c->Stamina());
-       props[2]->Decimal(c->Dexerty());
-       props[3]->Decimal(c->Intelligence());
-       props[4]->Time(c->Lifetime());
-       props[5]->Percentage(c->Fertility());
-       props[6]->Percentage(c->Mutability());
-       props[7]->Mass(c->OffspringMass());
+       props[0]->Text(DecimalString(c->Strength(), 2));
+       props[1]->Text(DecimalString(c->Stamina(), 2));
+       props[2]->Text(DecimalString(c->Dexerty(), 2));
+       props[3]->Text(DecimalString(c->Intelligence(), 2));
+       props[4]->Text(TimeString(c->Lifetime()));
+       props[5]->Text(PercentageString(c->Fertility()));
+       props[6]->Text(PercentageString(c->Mutability()));
+       props[7]->Text(MassString(c->OffspringMass()));
 
        const glm::vec2 margin(20.0f);
-
        panel.Position(glm::vec2(viewport.Width() - margin.x - panel.Size().x, margin.y));
        panel.Draw(assets, viewport);
 }
 
+
+RecordsPanel::RecordsPanel(world::Simulation &sim)
+: sim(sim)
+, live(new Label(sim.Assets().fonts.medium))
+, records()
+, holders()
+, panel() {
+       Label *live_label = new Label(sim.Assets().fonts.medium);
+       live_label->Text("Creatures alive");
+
+       Panel *label_panel = new Panel;
+       label_panel
+               ->Direction(Panel::VERTICAL)
+               ->Add(live_label);
+
+       Panel *value_panel = new Panel;
+       value_panel
+               ->Direction(Panel::VERTICAL)
+               ->Add(live);
+
+       Label *holder_label = new Label(sim.Assets().fonts.medium);
+       holder_label->Text("Holder");
+       Panel *holder_panel = new Panel;
+       holder_panel
+               ->Direction(Panel::VERTICAL)
+               ->Add(holder_label);
+
+       records.reserve(sim.Records().size());
+       for (const auto &r : sim.Records()) {
+               Label *label = new Label(sim.Assets().fonts.medium);
+               label->Text(r.name + " record");
+               label_panel->Add(label);
+               Label *value = new Label(sim.Assets().fonts.medium);
+               value->Text("none");
+               value_panel->Add(value);
+               records.push_back(value);
+               Label *holder = new Label(sim.Assets().fonts.medium);
+               holder->Text("nobody");
+               holder_panel->Add(holder);
+               holders.push_back(holder);
+       }
+
+       panel
+               .Direction(Panel::HORIZONTAL)
+               ->Padding(glm::vec2(10.0f))
+               ->Spacing(10.0f)
+               ->Background(glm::vec4(0.7f, 0.7f, 0.7f, 1.0f))
+               ->Add(label_panel)
+               ->Add(value_panel)
+               ->Add(holder_panel);
+}
+
+RecordsPanel::~RecordsPanel() {
+}
+
+void RecordsPanel::Draw(graphics::Viewport &viewport) noexcept {
+       live->Text(NumberString(sim.LiveCreatures().size()));
+       int i = 0;
+       for (const auto &r : sim.Records()) {
+               if (!r) continue;
+               switch (r.type) {
+                       default:
+                       case world::Record::VALUE:
+                               records[i]->Text(DecimalString(r.value, 2));
+                               break;
+                       case world::Record::LENGTH:
+                               records[i]->Text(LengthString(r.value));
+                               break;
+                       case world::Record::MASS:
+                               records[i]->Text(MassString(r.value));
+                               break;
+                       case world::Record::PERCENTAGE:
+                               records[i]->Text(PercentageString(r.value));
+                               break;
+                       case world::Record::TIME:
+                               records[i]->Text(TimeString(r.value));
+                               break;
+               }
+               std::string str(r.holder->Name());
+               bool first = true;
+               for (auto p : r.holder->Parents()) {
+                       if (first) {
+                               first = false;
+                               str += " of ";
+                       } else {
+                               str += " and ";
+                       }
+                       str += p->Name();
+               }
+               holders[i]->Text(str);
+               ++i;
+       }
+
+       const glm::vec2 margin(20.0f);
+       panel.Position(glm::vec2(margin.x, margin.y));
+       panel.Draw(sim.Assets(), viewport);
+}
+
+
+TimePanel::TimePanel(world::Simulation &sim)
+: sim(sim)
+, body(nullptr)
+, time(new Label(sim.Assets().fonts.medium))
+, clock(new Label(sim.Assets().fonts.medium))
+, panel() {
+       Label *time_label = new Label(sim.Assets().fonts.medium);
+       time_label->Text("Time");
+       Label *clock_label = new Label(sim.Assets().fonts.medium);
+       clock_label->Text("Clock");
+
+       Panel *label_panel = new Panel;
+       label_panel
+               ->Direction(Panel::VERTICAL)
+               ->Add(time_label)
+               ->Add(clock_label);
+
+       Panel *value_panel = new Panel;
+       value_panel
+               ->Direction(Panel::VERTICAL)
+               ->Add(time)
+               ->Add(clock);
+
+       panel
+               .Direction(Panel::HORIZONTAL)
+               ->Padding(glm::vec2(10.0f))
+               ->Spacing(10.0f)
+               ->Background(glm::vec4(0.7f, 0.7f, 0.7f, 1.0f))
+               ->Add(label_panel)
+               ->Add(value_panel);
+}
+
+TimePanel::~TimePanel() {
+}
+
+void TimePanel::Draw(graphics::Viewport &viewport) noexcept {
+       time->Text(TimeString(sim.Time()));
+       if (body) {
+               clock->Text(TimeString(std::fmod(sim.Time(), body->RotationalPeriod())));
+       } else {
+               clock->Text("no reference");
+       }
+
+       const glm::vec2 margin(20.0f);
+       panel.Position(glm::vec2(margin.x, viewport.Height() - margin.y - panel.Size().y));
+       panel.Draw(sim.Assets(), viewport);
+}
+
+
+std::string DecimalString(double n, int p) {
+       std::stringstream s;
+       s << std::fixed << std::setprecision(p) << n;
+       return s.str();
+}
+
+std::string LengthString(double m) {
+       std::stringstream s;
+       s << std::fixed << std::setprecision(3);
+       if (m > 1500.0) {
+               s << (m * 0.001) << "km";
+       } else if (m < 0.1) {
+               s << (m * 1000.0) << "mm";
+       } else {
+               s << m << "m";
+       }
+       return s.str();
+}
+
+std::string MassString(double kg) {
+       std::stringstream s;
+       s << std::fixed << std::setprecision(3);
+       if (kg > 1500.0) {
+               s << (kg * 0.001) << "t";
+       } else if (kg < 1.0) {
+               s << (kg * 1000.0) << "g";
+       } else if (kg < 0.001) {
+               s << (kg * 1.0e6) << "mg";
+       } else {
+               s << kg << "kg";
+       }
+       return s.str();
+}
+
+std::string NumberString(int n) {
+       return std::to_string(n);
+}
+
+std::string PercentageString(double n) {
+       std::stringstream s;
+       s << std::fixed << std::setprecision(1) << (n * 100.0) << '%';
+       return s.str();
+}
+
+std::string TimeString(double s) {
+       int is = int(s);
+       std::stringstream ss;
+       if (is >= 3600) {
+               ss << (is / 3600) << "h ";
+               is %= 3600;
+       }
+       if (is >= 60) {
+               ss << (is / 60) << "m ";
+               is %= 60;
+       }
+       ss << is << 's';
+       return ss.str();
+}
+
+std::string VectorString(const glm::dvec3 &v, int p) {
+       std::stringstream ss;
+       ss << std::fixed << std::setprecision(p)
+               << "<" << v.x << ", " << v.y << ", " << v.z << ">";
+       return ss.str();
+}
+
+std::string VectorString(const glm::ivec2 &v) {
+       std::stringstream ss;
+       ss << "<" << v.x << ", " << v.y << ">";
+       return ss.str();
+}
+
 }
 }
index b0d44e20969fa23a55bbfe589e76d4b0fa3332be..a81defc0e549cc983795808a71c45988c154e4f7 100644 (file)
@@ -35,61 +35,6 @@ Label *Label::Text(const std::string &t) {
        return this;
 }
 
-Label *Label::Decimal(double n, int prec) {
-       std::stringstream s;
-       s << std::fixed << std::setprecision(prec) << n;
-       return Text(s.str());
-}
-
-Label *Label::Length(double m) {
-       std::stringstream s;
-       s << std::fixed << std::setprecision(3);
-       if (m > 1500.0) {
-               s << (m * 0.001) << "km";
-       } else if (m < 0.1) {
-               s << (m * 1000.0) << "mm";
-       } else {
-               s << m << "m";
-       }
-       return Text(s.str());
-}
-
-Label *Label::Mass(double kg) {
-       std::stringstream s;
-       s << std::fixed << std::setprecision(3);
-       if (kg > 1500.0) {
-               s << (kg * 0.001) << "t";
-       } else if (kg < 0.1) {
-               s << (kg * 1000.0) << "g";
-       } else if (kg < 0.0001) {
-               s << (kg * 1.0e6) << "mg";
-       } else {
-               s << kg << "kg";
-       }
-       return Text(s.str());
-}
-
-Label *Label::Percentage(double n) {
-       std::stringstream s;
-       s << std::fixed << std::setprecision(1) << (n * 100.0) << '%';
-       return Text(s.str());
-}
-
-Label *Label::Time(double s) {
-       int is = int(s);
-       std::stringstream ss;
-       if (is >= 3600) {
-               ss << (is / 3600) << "h ";
-               is %= 3600;
-       }
-       if (is >= 60) {
-               ss << (is / 60) << "m ";
-               is %= 60;
-       }
-       ss << is << 's';
-       return Text(ss.str());
-}
-
 Label *Label::Font(const graphics::Font &f) {
        if (font != &f) {
                dirty = true;
diff --git a/src/world/Record.hpp b/src/world/Record.hpp
new file mode 100644 (file)
index 0000000..3436cbd
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef BLOBS_WORLD_RECORD_HPP_
+#define BLOBS_WORLD_RECORD_HPP_
+
+#include <string>
+
+
+namespace blobs {
+namespace creature {
+       class Creature;
+}
+namespace world {
+
+struct Record {
+
+       std::string name = "";
+       creature::Creature *holder = nullptr;
+       double value = 0.0;
+       double time = 0.0;
+       enum Type {
+               VALUE,
+               LENGTH,
+               MASS,
+               PERCENTAGE,
+               TIME,
+       } type = VALUE;
+
+       operator bool() const noexcept { return holder; }
+
+};
+
+}
+}
+
+#endif
index 31489c6de7aef91e20c93c1164771260eb251e62..5df595fb26d0eab0c3d0b799a93f333b4bcf6270 100644 (file)
@@ -1,13 +1,18 @@
 #ifndef BLOBS_WORLD_SIMULATION_HPP_
 #define BLOBS_WORLD_SIMULATION_HPP_
 
+#include "Record.hpp"
 #include "Set.hpp"
 #include "../app/Assets.hpp"
 
 #include <set>
+#include <vector>
 
 
 namespace blobs {
+namespace creature {
+       class Creature;
+}
 namespace world {
 
 class Body;
@@ -31,10 +36,6 @@ public:
 public:
        void Tick(double dt);
 
-       void AddBody(Body &);
-       void AddPlanet(Planet &);
-       void AddSun(Sun &);
-
        Body &Root() noexcept { return root; }
        const Body &Root() const noexcept { return root; }
 
@@ -43,19 +44,40 @@ public:
        const Set<Resource> &Resources() const noexcept { return assets.data.resources; }
        const Set<TileType> &TileTypes() const noexcept { return assets.data.tile_types; }
 
+       void AddBody(Body &);
+       void AddPlanet(Planet &);
+       void AddSun(Sun &);
+
        const std::set<Body *> &Bodies() const noexcept { return bodies; }
        const std::set<Planet *> &Planets() const noexcept { return planets; }
        const std::set<Sun *> &Suns() const noexcept { return suns; }
 
+       void SetAlive(creature::Creature *);
+       std::vector<creature::Creature *> &LiveCreatures() noexcept { return alive; }
+       const std::vector<creature::Creature *> &LiveCreatures() const noexcept { return alive; }
+
+       void SetDead(creature::Creature *);
+       std::vector<creature::Creature *> &DeadCreatures() noexcept { return dead; }
+       const std::vector<creature::Creature *> &DeadCreatures() const noexcept { return dead; }
+
        double Time() const noexcept { return time; }
 
+       const std::vector<Record> &Records() const noexcept { return records; }
+       void CheckRecords(creature::Creature &) noexcept;
+
 private:
        Body &root;
        app::Assets &assets;
+
        std::set<Body *> bodies;
        std::set<Planet *> planets;
        std::set<Sun *> suns;
+
+       std::vector<creature::Creature *> alive;
+       std::vector<creature::Creature *> dead;
+
        double time;
+       std::vector<Record> records;
 
 };
 
index 8c3749f347bff8963783cb2f322c60f22da37683..edf7bfe4f02f88954c2fd140161e07bff785d7fa 100644 (file)
@@ -3,6 +3,9 @@
 #include "Body.hpp"
 #include "Planet.hpp"
 #include "Sun.hpp"
+#include "../creature/Creature.hpp"
+
+#include <algorithm>
 
 
 namespace blobs {
@@ -14,13 +17,41 @@ Simulation::Simulation(Body &r, app::Assets &assets)
 , bodies()
 , planets()
 , suns()
-, time(0.0) {
+, alive()
+, dead()
+, time(0.0)
+, records(7) {
        AddBody(r);
+       records[0].name = "Age";
+       records[0].type = Record::TIME;
+       records[1].name = "Mass";
+       records[1].type = Record::MASS;
+       records[2].name = "Size";
+       records[2].type = Record::LENGTH;
+       records[3].name = "Strength";
+       records[4].name = "Stamina";
+       records[5].name = "Dexerty";
+       records[6].name = "Intelligence";
 }
 
 Simulation::~Simulation() {
+       for (auto c : alive) {
+               delete c;
+       }
+       for (auto c : dead) {
+               delete c;
+       }
 }
 
+void Simulation::Tick(double dt) {
+       time += dt;
+       for (auto body : bodies) {
+               body->Tick(dt);
+       }
+       for (auto c : alive) {
+               CheckRecords(*c);
+       }
+}
 
 void Simulation::AddBody(Body &b) {
        b.SetSimulation(*this);
@@ -37,10 +68,54 @@ void Simulation::AddSun(Sun &s) {
        suns.insert(&s);
 }
 
-void Simulation::Tick(double dt) {
-       time += dt;
-       for (auto body : bodies) {
-               body->Tick(dt);
+void Simulation::SetAlive(creature::Creature *c) {
+       alive.push_back(c);
+}
+
+void Simulation::SetDead(creature::Creature *c) {
+       auto entry = std::find(alive.begin(), alive.end(), c);
+       if (entry != alive.end()) {
+               alive.erase(entry);
+       }
+       dead.push_back(c);
+       CheckRecords(*c);
+}
+
+void Simulation::CheckRecords(creature::Creature &c) noexcept {
+       if (c.Age() > records[0].value) {
+               records[0].value = c.Age();
+               records[0].time = Time();
+               records[0].holder = &c;
+       }
+       if (c.Mass() > records[1].value) {
+               records[1].value = c.Mass();
+               records[1].time = Time();
+               records[1].holder = &c;
+       }
+       if (c.Size() > records[2].value) {
+               records[2].value = c.Size();
+               records[2].time = Time();
+               records[2].holder = &c;
+       }
+       if (c.Strength() > records[3].value) {
+               records[3].value = c.Strength();
+               records[3].time = Time();
+               records[3].holder = &c;
+       }
+       if (c.Stamina() > records[4].value) {
+               records[4].value = c.Stamina();
+               records[4].time = Time();
+               records[4].holder = &c;
+       }
+       if (c.Dexerty() > records[5].value) {
+               records[5].value = c.Dexerty();
+               records[5].time = Time();
+               records[5].holder = &c;
+       }
+       if (c.Intelligence() > records[6].value) {
+               records[6].value = c.Intelligence();
+               records[6].time = Time();
+               records[6].holder = &c;
        }
 }
 
index d7e293a5509d2d456c88cbca5b09daa44fdb49ba..b80be094129011d029098b23b5986753e61d5f2d 100644 (file)
@@ -58,9 +58,6 @@ Body::Body()
 }
 
 Body::~Body() {
-       for (creature::Creature *c : creatures) {
-               delete c;
-       }
 }
 
 void Body::SetSimulation(Simulation &s) noexcept {
@@ -156,7 +153,7 @@ void Body::Tick(double dt) {
        // first remove creatures so they don't collide
        for (auto c = Creatures().begin(); c != Creatures().end();) {
                if ((*c)->Removable()) {
-                       delete *c;
+                       (*c)->Removed();
                        c = Creatures().erase(c);
                } else {
                        ++c;