-Subproject commit 3c715b39e3aece21fc1f0fd6042611e79bfbeb44
+Subproject commit 7b1b41f192be522bc6812db7e91798c2fee0e5e1
in.ReadString(data.resources[id].label);
} else if (name == "density") {
data.resources[id].density = in.GetDouble();
+ } else if (name == "energy") {
+ data.resources[id].energy = in.GetDouble();
+ data.resources[id].inverse_energy = 1.0 / data.resources[id].energy;
} else if (name == "state") {
in.ReadIdentifier(name);
if (name == "solid") {
Composition();
~Composition();
- Composition(const Composition &) = delete;
- Composition &operator =(const Composition &) = delete;
+ Composition(const Composition &) = default;
+ Composition &operator =(const Composition &) = default;
- Composition(Composition &&) = delete;
- Composition &operator =(Composition &&) = delete;
+ Composition(Composition &&) = default;
+ Composition &operator =(Composition &&) = default;
public:
void Add(int res, double amount);
double Mass() const noexcept { return mass; }
void Ingest(int res, double amount) noexcept;
+ void DoWork(double amount) noexcept;
+
void Size(double s) noexcept { size = s; }
double Size() const noexcept { return size; }
/// age-depended multiplier, peak being the maximum in lifetime [0,1]
double AgeFactor(double peak) const noexcept;
+ double EnergyEfficiency() const noexcept;
double ExhaustionFactor() const noexcept;
double FatigueFactor() const noexcept;
const State &GetState() const noexcept { return state; }
const glm::dvec3 &Velocity() const noexcept { return state.vel; }
- bool Moving() const noexcept { return glm::length2(state.vel) > 0.0000001; }
+ bool Moving() const noexcept { return glm::length2(state.vel) > 0.000001; }
void Move(const glm::dvec3 &dp) noexcept;
void Accelerate(const glm::dvec3 &dv) noexcept;
void EnforceConstraints(State &) noexcept;
void Composition::Add(int res, double amount) {
bool found = false;
- for (auto &c : components) {
- if (c.resource == res) {
- c.value += amount;
+ for (auto c = components.begin(); c != components.end(); ++c) {
+ if (c->resource == res) {
+ c->value += amount;
+ if (c->value <= 0.0) {
+ components.erase(c);
+ }
found = true;
break;
}
}
- if (!found) {
+ if (!found && amount > 0.0) {
components.emplace_back(res, amount);
}
std::sort(components.begin(), components.end(), CompositionCompare);
}
}
+void Creature::DoWork(double amount) noexcept {
+ stats.Exhaustion().Add(amount / Stamina());
+ // burn resources proportional to composition
+ // factor = 1/total * 1/efficiency * amount * -1
+ double factor = -amount / (composition.TotalMass() * EnergyEfficiency());
+ // make a copy to total remains constant and
+ // no entries disappear during iteration
+ Composition comp(composition);
+ for (auto &cmp : comp) {
+ double value = cmp.value * factor * sim.Resources()[cmp.resource].inverse_energy;
+ AddMass(cmp.resource, value);
+ }
+}
+
void Creature::Hurt(double amount) noexcept {
stats.Damage().Add(amount);
if (stats.Damage().Full()) {
// shifted inverse hermite, y = 1 - (3t² - 2t³) with t = normalized age - peak
// goes negative below -0.5 and starts to rise again above 1.0
double t = glm::clamp((Age() / properties.Lifetime()) - peak, -0.5, 1.0);
- return 1.0 - (3.0 * t * t) + (2.0 * t * t * t);
+ // guarantee at least 1%
+ return std::max(0.01, 1.0 - (3.0 * t * t) + (2.0 * t * t * t));
+}
+
+double Creature::EnergyEfficiency() const noexcept {
+ return 0.25 * AgeFactor(0.05);
}
double Creature::ExhaustionFactor() const noexcept {
}
}
situation.SetState(state);
- stats.Exhaustion().Add(length(f.acc) * Mass() / Stamina() * 0.5 * dt);
+ // work is force times distance
+ DoWork(length(f.acc) * Mass() * length(f.vel) * dt);
}
Situation::Derivative Creature::Step(const Situation::Derivative &ds, double dt) const noexcept {
SumForce(result, repulse, force);
}
if (halting) {
- SumForce(result, s.vel * -force, force);
+ // break twice as hard
+ SumForce(result, s.vel * force * -2.0, force);
}
if (seeking) {
glm::dvec3 diff = target - s.pos;
#include "Creature.hpp"
#include "../app/Assets.hpp"
+#include "../ui/String.hpp"
#include "../world/Planet.hpp"
#include "../world/Resource.hpp"
#include "../world/Simulation.hpp"
// 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
+ // maintain ~1% gas composition
double gas_amount = GetCreature().GetComposition().Get(gas);
- if (gas_amount < GetCreature().GetComposition().TotalMass() * 0.025) {
+ if (gas_amount < GetCreature().GetComposition().TotalMass() * 0.01) {
double add = std::min(GetCreature().GetComposition().TotalMass() * 0.025 - gas_amount, -amount * dt);
GetCreature().Ingest(gas, add);
}
void BlobBackgroundTask::CheckSplit() {
if (GetCreature().Mass() > GetCreature().OffspringMass() * 2.0
&& GetCreature().OffspringChance() > Assets().random.UNorm()) {
- std::cout << "[" << int(GetCreature().GetSimulation().Time())
- << "s] " << GetCreature().Name() << " split" << std::endl;
+ std::cout << "[" << ui::TimeString(GetCreature().GetSimulation().Time())
+ << "] " << GetCreature().Name() << " split" << std::endl;
Split(GetCreature());
return;
}
std::string label = "";
double density = 1.0;
+ double energy = 1.0;
+ double inverse_energy = 1.0;
int id = -1;