-Subproject commit 7b1b41f192be522bc6812db7e91798c2fee0e5e1
+Subproject commit f2587b6b95d73b9b2d2d871e9a8f5b5dae702965
}
} else if (name == "base_color") {
in.ReadVec(data.resources[id].base_color);
+ } else if (name == "compatibility") {
+ in.Skip(io::Token::ANGLE_BRACKET_OPEN);
+ while (in.Peek().type != io::Token::ANGLE_BRACKET_CLOSE) {
+ in.ReadIdentifier(name);
+ int sub_id = 0;
+ if (data.resources.Has(name)) {
+ sub_id = data.resources[name].id;
+ } else {
+ world::Resource res;
+ res.name = name;
+ sub_id = data.resources.Add(res);
+ }
+ in.Skip(io::Token::COLON);
+ double value = in.GetDouble();
+ in.Skip(io::Token::SEMICOLON);
+ data.resources[id].compatibility[sub_id] = value;
+ }
+ in.Skip(io::Token::ANGLE_BRACKET_CLOSE);
} else {
throw std::runtime_error("unknown resource property '" + name + "'");
}
#ifndef BLOBLS_CREATURE_COMPOSITION_HPP_
#define BLOBLS_CREATURE_COMPOSITION_HPP_
+#include "../world/Set.hpp"
+
#include <vector>
namespace blobs {
+namespace world {
+ class Resource;
+}
namespace creature {
class Composition {
void Add(int res, double amount);
bool Has(int res) const noexcept;
double Get(int res) const noexcept;
+ double Proportion(int res) const noexcept;
+ double Compatibility(const world::Set<world::Resource> &, int res) const noexcept;
double TotalMass() const noexcept { return total_mass; }
public:
~LocateResourceGoal() noexcept override;
public:
+ void SetMinimum(double m) noexcept { minimum = m; }
void Accept(int resource, double attractiveness);
std::string Describe() const override;
glm::dvec3 target_pos;
bool searching;
double reevaluate;
+ double minimum;
};
return 0.0;
}
+double Composition::Proportion(int res) const noexcept {
+ return Get(res) / TotalMass();
+}
+
+double Composition::Compatibility(const world::Set<world::Resource> &resources, int res) const noexcept {
+ if (Has(res)) {
+ return Proportion(res);
+ }
+ double max_compat = -1.0;
+ double min_compat = 1.0;
+ for (const auto &c : components) {
+ double prop = c.value / TotalMass();
+ for (const auto &compat : resources[c.resource].compatibility) {
+ double value = compat.second * prop;
+ if (value > max_compat) {
+ max_compat = value;
+ }
+ if (value < min_compat) {
+ min_compat = value;
+ }
+ }
+ }
+ if (min_compat < 0.0) {
+ return min_compat;
+ } else {
+ return max_compat;
+ }
+}
+
Creature::Creature(world::Simulation &sim)
: sim(sim)
}
void Creature::Ingest(int res, double amount) noexcept {
- // TODO: check foreign materials
if (sim.Resources()[res].state == world::Resource::SOLID) {
- // 15% of solids stays in body
- AddMass(res, amount * 0.15);
+ // 30% of solids stays in body
+ AddMass(res, amount * 0.3 * composition.Compatibility(sim.Resources(), res));
} else {
// 10% of fluids stays in body
- AddMass(res, amount * 0.05);
+ AddMass(res, amount * 0.1 * composition.Compatibility(sim.Resources(), res));
}
math::GaloisLFSR &random = sim.Assets().random;
if (random.UNorm() < AdaptChance()) {
drink_subtask = new IngestGoal(GetCreature(), stats.Thirst());
for (const auto &cmp : GetCreature().GetComposition()) {
if (Assets().data.resources[cmp.resource].state == world::Resource::LIQUID) {
- drink_subtask->Accept(cmp.resource, 1.0);
+ double value = cmp.value / GetCreature().GetComposition().TotalMass();
+ drink_subtask->Accept(cmp.resource, value);
+ for (const auto &compat : Assets().data.resources[cmp.resource].compatibility) {
+ if (Assets().data.resources[compat.first].state == world::Resource::LIQUID) {
+ drink_subtask->Accept(compat.first, value * compat.second);
+ }
+ }
}
}
drink_subtask->WhenComplete([&](Goal &) { drink_subtask = nullptr; });
eat_subtask = new IngestGoal(GetCreature(), stats.Hunger());
for (const auto &cmp : GetCreature().GetComposition()) {
if (Assets().data.resources[cmp.resource].state == world::Resource::SOLID) {
- eat_subtask->Accept(cmp.resource, 1.0);
+ double value = cmp.value / GetCreature().GetComposition().TotalMass();
+ eat_subtask->Accept(cmp.resource, value);
+ for (const auto &compat : Assets().data.resources[cmp.resource].compatibility) {
+ if (Assets().data.resources[compat.first].state == world::Resource::SOLID) {
+ eat_subtask->Accept(compat.first, value * compat.second);
+ }
+ }
}
}
eat_subtask->WhenComplete([&](Goal &) { eat_subtask = nullptr; });
}
if (ingesting) {
if (OnSuitableTile() && !GetSituation().Moving()) {
- // TODO: determine satisfaction factor
- GetCreature().Ingest(resource, yield * dt);
+ GetCreature().Ingest(resource, yield * GetCreature().GetComposition().Compatibility(Assets().data.resources, resource) * dt);
stat.Add(-1.0 * yield * dt);
if (stat.Empty()) {
SetComplete();
for (const auto &c : accept) {
locate_subtask->Accept(c.resource, c.value);
}
+ locate_subtask->SetMinimum(stat.gain * -1.1);
locate_subtask->Urgency(Urgency() + 0.1);
locate_subtask->WhenComplete([&](Goal &){ locate_subtask = nullptr; });
GetCreature().AddGoal(std::unique_ptr<Goal>(locate_subtask));
, found(false)
, target_pos(0.0)
, searching(false)
-, reevaluate(0.0) {
+, reevaluate(0.0)
+, minimum(0.0) {
}
LocateResourceGoal::~LocateResourceGoal() noexcept {
}
}
- if (best_rating > 0.0) {
+ if (best_rating > minimum) {
found = true;
searching = false;
target_pos = glm::normalize(pos + (double(best_pos.x) * step_x) + (double(best_pos.y) * step_y)) * planet.Radius();
Label *born;
Label *age;
Label *mass;
+ Label *size;
Label *goal;
Label *pos;
Label *tile;
, born(new Label(assets.fonts.medium))
, age(new Label(assets.fonts.medium))
, mass(new Label(assets.fonts.medium))
+, size(new Label(assets.fonts.medium))
, goal(new Label(assets.fonts.medium))
, pos(new Label(assets.fonts.medium))
, tile(new Label(assets.fonts.medium))
age_label->Text("Age");
Label *mass_label = new Label(assets.fonts.medium);
mass_label->Text("Mass");
+ Label *size_label = new Label(assets.fonts.medium);
+ size_label->Text("Size");
Label *goal_label = new Label(assets.fonts.medium);
goal_label->Text("Goal");
->Add(born_label)
->Add(age_label)
->Add(mass_label)
+ ->Add(size_label)
->Add(goal_label);
Panel *info_value_panel = new Panel;
info_value_panel
->Add(born)
->Add(age)
->Add(mass)
+ ->Add(size)
->Add(goal);
Panel *info_panel = new Panel;
info_panel
void CreaturePanel::Show(creature::Creature &cr) {
c = &cr;
- name->Text(c->Name());
born->Text(TimeString(c->Born()));
if (c->Parents().empty()) {
void CreaturePanel::Draw(graphics::Viewport &viewport) noexcept {
if (!c) return;
+ name->Text(c->Name());
age->Text(TimeString(c->Age()));
mass->Text(MassString(c->Mass()));
+ size->Text(LengthString(c->Size()));
if (c->Goals().empty()) {
goal->Text("none");
} else {
}
}
- 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[0]->Text(DecimalString(c->Strength(), 2) + " / " + DecimalString(c->GetProperties().Strength(), 2));
+ props[1]->Text(DecimalString(c->Stamina(), 2) + " / " + DecimalString(c->GetProperties().Stamina(), 2));
+ props[2]->Text(DecimalString(c->Dexerty(), 2) + " / " + DecimalString(c->GetProperties().Dexerty(), 2));
+ props[3]->Text(DecimalString(c->Intelligence(), 2) + " / " + DecimalString(c->GetProperties().Intelligence(), 2));
props[4]->Text(TimeString(c->Lifetime()));
- props[5]->Text(PercentageString(c->Fertility()));
+ props[5]->Text(PercentageString(c->Fertility()) + " / " + PercentageString(c->GetProperties().Fertility()));
props[6]->Text(PercentageString(c->Mutability()));
props[7]->Text(PercentageString(c->Adaptability()));
props[8]->Text(MassString(c->OffspringMass()));
#include "../math/glm.hpp"
+#include <map>
#include <string>
glm::dvec3 base_color = glm::dvec3(1.0);
+ std::map<int, double> compatibility;
+
};
}