From: Daniel Karbach Date: Sun, 26 Nov 2017 21:30:59 +0000 (+0100) Subject: split creature when it's "ripe" lol X-Git-Url: http://git.localhorst.tv/?p=blobs.git;a=commitdiff_plain;h=42db7d9d2286e50896ad172e2e4a8fbe65c8a4a9 split creature when it's "ripe" lol first split is at 293s spoiler alert: they get stuck in the ocean and starve --- diff --git a/src/app/states.cpp b/src/app/states.cpp index 9cf6a9c..d98a099 100644 --- a/src/app/states.cpp +++ b/src/app/states.cpp @@ -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(); diff --git a/src/blobs.cpp b/src/blobs.cpp index b32c4ab..ec14376 100644 --- a/src/blobs.cpp +++ b/src/blobs.cpp @@ -16,6 +16,35 @@ 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(); diff --git a/src/creature/Creature.hpp b/src/creature/Creature.hpp index 2a40d51..6c91827 100644 --- a/src/creature/Creature.hpp +++ b/src/creature/Creature.hpp @@ -30,6 +30,9 @@ namespace creature { class Creature { +public: + using Callback = std::function; + 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 &GetProperties() noexcept { return properties; } + const Genome::Properties &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 &&n) { needs.emplace_back(std::move(n)); } const std::vector> &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 ∼ std::string name; Genome genome; + Genome::Properties properties; double mass; + double density; double size; + + double birth; double health; + Callback on_death; + bool removable; std::vector> needs; std::vector> 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 &); } } diff --git a/src/creature/Genome.hpp b/src/creature/Genome.hpp index 4437150..057c660 100644 --- a/src/creature/Genome.hpp +++ b/src/creature/Genome.hpp @@ -16,19 +16,37 @@ class Creature; struct Genome { + template + struct Properties { + T birth_mass; + T fertile_mass; + T max_mass; + + T fertile_age; + T infertile_age; + T death_age; + + T fertility; + }; + Properties 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; - void Configure(app::Assets &, Creature &) const; + void Configure(Creature &) const; }; diff --git a/src/creature/Goal.hpp b/src/creature/Goal.hpp index 2ec03bf..9ffeb0f 100644 --- a/src/creature/Goal.hpp +++ b/src/creature/Goal.hpp @@ -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 index 0000000..eb402ba --- /dev/null +++ b/src/creature/IdleGoal.hpp @@ -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 diff --git a/src/creature/Need.hpp b/src/creature/Need.hpp index 67abe3c..295b00f 100644 --- a/src/creature/Need.hpp +++ b/src/creature/Need.hpp @@ -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; diff --git a/src/creature/Situation.hpp b/src/creature/Situation.hpp index d421b69..df7abf2 100644 --- a/src/creature/Situation.hpp +++ b/src/creature/Situation.hpp @@ -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; } diff --git a/src/creature/creature.cpp b/src/creature/creature.cpp index 0f25da8..0aa2b39 100644 --- a/src/creature/creature.cpp +++ b/src/creature/creature.cpp @@ -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 &&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 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; - 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(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) diff --git a/src/creature/goal.cpp b/src/creature/goal.cpp index 18714e4..229dffc 100644 --- a/src/creature/goal.cpp +++ b/src/creature/goal.cpp @@ -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 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 } diff --git a/src/creature/need.cpp b/src/creature/need.cpp index 64ebf5b..1b832b7 100644 --- a/src/creature/need.cpp +++ b/src/creature/need.cpp @@ -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) { diff --git a/src/graphics/shader.cpp b/src/graphics/shader.cpp index 1b641b0..dbdeeb1 100644 --- a/src/graphics/shader.cpp +++ b/src/graphics/shader.cpp @@ -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" diff --git a/src/ui/CreaturePanel.hpp b/src/ui/CreaturePanel.hpp index 3a33b8c..55c2408 100644 --- a/src/ui/CreaturePanel.hpp +++ b/src/ui/CreaturePanel.hpp @@ -45,6 +45,9 @@ private: creature::Creature *c; Label *name; + Label *age; + Label *mass; + Label *goal; Panel *needs; Panel panel; diff --git a/src/ui/ui.cpp b/src/ui/ui.cpp index cedbcd1..79adbbb 100644 --- a/src/ui/ui.cpp +++ b/src/ui/ui.cpp @@ -7,6 +7,8 @@ #include "../creature/Need.hpp" #include "../graphics/Viewport.hpp" +#include +#include #include @@ -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()) { diff --git a/src/ui/widgets.cpp b/src/ui/widgets.cpp index 49fdb69..6d88ad7 100644 --- a/src/ui/widgets.cpp +++ b/src/ui/widgets.cpp @@ -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() { diff --git a/src/world/Simulation.hpp b/src/world/Simulation.hpp index 2f2239e..ee497a8 100644 --- a/src/world/Simulation.hpp +++ b/src/world/Simulation.hpp @@ -2,6 +2,7 @@ #define BLOBS_WORLD_SIMULATION_HPP_ #include "Set.hpp" +#include "../app/Assets.hpp" #include @@ -18,7 +19,7 @@ class TileType; class Simulation { public: - explicit Simulation(Body &root, const Set &, const Set &); + 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 &Resources() const noexcept { return resources; } - const Set &TileTypes() const noexcept { return tile_types; } + app::Assets &Assets() noexcept { return assets; } + const app::Assets &Assets() const noexcept { return assets; } + const Set &Resources() const noexcept { return assets.data.resources; } + const Set &TileTypes() const noexcept { return assets.data.tile_types; } const std::set &Bodies() const noexcept { return bodies; } const std::set &Planets() const noexcept { return planets; } @@ -48,8 +51,7 @@ public: private: Body &root; - const Set &resources; - const Set &tile_types; + app::Assets &assets; std::set bodies; std::set planets; std::set suns; diff --git a/src/world/sim.cpp b/src/world/sim.cpp index 071ad17..b6a198d 100644 --- a/src/world/sim.cpp +++ b/src/world/sim.cpp @@ -8,10 +8,9 @@ namespace blobs { namespace world { -Simulation::Simulation(Body &r, const Set &res, const Set &tile) +Simulation::Simulation(Body &r, app::Assets &assets) : root(r) -, resources(res) -, tile_types(tile) +, assets(assets) , bodies() , planets() , suns() diff --git a/src/world/world.cpp b/src/world/world.cpp index 4f98d9a..91f9bc6 100644 --- a/src/world/world.cpp +++ b/src/world/world.cpp @@ -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 {