+void Genome::Configure(Creature &c) const {
+ c.GetGenome() = *this;
+
+ math::GaloisLFSR &random = c.GetSimulation().Assets().random;
+
+ c.GetProperties() = Instantiate(properties, random);
+
+ // TODO: derive stats from properties
+ c.GetStats().Damage().gain = (-1.0 / 100.0);
+ c.GetStats().Breath().gain = (1.0 / 5.0);
+ c.GetStats().Thirst().gain = (1.0 / 60.0);
+ c.GetStats().Hunger().gain = (1.0 / 200.0);
+ c.GetStats().Exhaustion().gain = (-1.0 / 100.0);
+ c.GetStats().Fatigue().gain = (-1.0 / 100.0);
+ c.GetStats().Boredom().gain = (1.0 / 300.0);
+
+ glm::dvec3 base_color(
+ std::fmod(base_hue.FakeNormal(random.SNorm()) + 1.0, 1.0),
+ glm::clamp(base_saturation.FakeNormal(random.SNorm()), 0.0, 1.0),
+ glm::clamp(base_lightness.FakeNormal(random.SNorm()), 0.0, 1.0)
+ );
+ glm::dvec3 highlight_color(
+ std::fmod(base_color.x + 0.5, 1.0),
+ 1.0 - base_color.y,
+ 1.0 - base_color.z
+ );
+ c.BaseColor(hsl2rgb(base_color));
+ c.HighlightColor(hsl2rgb(highlight_color));
+ c.SetBackgroundTask(std::unique_ptr<Goal>(new BlobBackgroundTask(c)));
+ c.AddGoal(std::unique_ptr<Goal>(new IdleGoal(c)));
+}
+
+
+void Split(Creature &c) {
+ Creature *a = new Creature(c.GetSimulation());
+ const Situation &s = c.GetSituation();
+ a->Name(c.GetSimulation().Assets().name.Sequential());
+ c.GetGenome().Configure(*a);
+ for (const auto &cmp : c.GetComposition()) {
+ a->AddMass(cmp.resource, cmp.value * 0.5);
+ }
+ s.GetPlanet().AddCreature(a);
+ // TODO: duplicate situation somehow
+ a->GetSituation().SetPlanetSurface(
+ s.GetPlanet(), s.Surface(),
+ s.Position() + glm::dvec3(0.0, a->Size() + 0.1, 0.0));
+ a->BuildVAO();
+ std::cout << "[" << int(c.GetSimulation().Time()) << "s] "
+ << a->Name() << " was born" << std::endl;
+
+ Creature *b = new Creature(c.GetSimulation());
+ b->Name(c.GetSimulation().Assets().name.Sequential());
+ c.GetGenome().Configure(*b);
+ for (const auto &cmp : c.GetComposition()) {
+ b->AddMass(cmp.resource, cmp.value * 0.5);
+ }
+ s.GetPlanet().AddCreature(b);
+ b->GetSituation().SetPlanetSurface(
+ s.GetPlanet(), s.Surface(),
+ s.Position() + glm::dvec3(0.0, b->Size() - 0.1, 0.0));
+ b->BuildVAO();
+ std::cout << "[" << int(c.GetSimulation().Time()) << "s] "
+ << b->Name() << " was born" << std::endl;
+
+ c.Die();
+}
+
+
+Memory::Memory(Creature &c)
+: c(c) {
+}
+
+Memory::~Memory() {
+}
+
+void Memory::Tick(double dt) {
+ Situation &s = c.GetSituation();
+ if (s.OnSurface()) {
+ TrackStay({ &s.GetPlanet(), s.Surface(), s.SurfacePosition() }, dt);
+ }
+}
+
+void Memory::TrackStay(const Location &l, double t) {
+ const world::TileType &type = l.planet->TypeAt(l.surface, l.coords.x, l.coords.y);
+ auto entry = known_types.find(type.id);
+ if (entry != known_types.end()) {
+ if (c.GetSimulation().Time() - entry->second.last_been > c.GetProperties().Lifetime() * 0.1) {
+ // "it's been ages"
+ if (entry->second.time_spent > c.Age() * 0.25) {
+ // the place is very familiar
+ c.GetStats().Boredom().Add(-0.2);
+ } else {
+ // infrequent stays
+ c.GetStats().Boredom().Add(-0.1);
+ }
+ }
+ entry->second.last_been = c.GetSimulation().Time();
+ entry->second.last_loc = l;
+ entry->second.time_spent += t;
+ } else {
+ known_types.emplace(type.id, Stay{
+ c.GetSimulation().Time(),
+ l,
+ c.GetSimulation().Time(),
+ l,
+ t
+ });
+ // completely new place, interesting
+ // TODO: scale by personality trait
+ c.GetStats().Boredom().Add(-0.25);
+ }
+}
+
+
+NameGenerator::NameGenerator()
+: counter(0) {
+}
+
+NameGenerator::~NameGenerator() {
+}
+
+std::string NameGenerator::Sequential() {
+ std::stringstream ss;
+ ss << "Blob " << ++counter;
+ return ss.str();
+}
+
+