X-Git-Url: http://git.localhorst.tv/?a=blobdiff_plain;f=src%2Fcreature%2Fcreature.cpp;h=085d70f000554bec57537f0b419b62f474ccb3eb;hb=3989da924c4e33c52f500aead5ae62bb40294781;hp=468967887ee96a1d62b7547c2d7a2ad8cff04461;hpb=75398ab9230c15215e7a378a26d2d55de67b47f0;p=blobs.git diff --git a/src/creature/creature.cpp b/src/creature/creature.cpp index 4689678..085d70f 100644 --- a/src/creature/creature.cpp +++ b/src/creature/creature.cpp @@ -29,9 +29,11 @@ namespace blobs { namespace creature { -Composition::Composition() -: components() -, total_mass(0.0) { +Composition::Composition(const world::Set &resources) +: resources(resources) +, components() +, total_mass(0.0) +, state_mass{0.0} { } Composition::~Composition() { @@ -49,6 +51,7 @@ void Composition::Add(int res, double amount) { if (c->resource == res) { c->value += amount; if (c->value <= 0.0) { + amount += c->value; components.erase(c); } found = true; @@ -59,6 +62,7 @@ void Composition::Add(int res, double amount) { components.emplace_back(res, amount); } std::sort(components.begin(), components.end(), CompositionCompare); + state_mass[resources[res].state] += amount; total_mass += amount; } @@ -80,13 +84,46 @@ double Composition::Get(int res) const noexcept { return 0.0; } +double Composition::Proportion(int res) const noexcept { + return Get(res) / TotalMass(); +} + +double Composition::StateProportion(int res) const noexcept { + return Get(res) / StateMass(resources[res].state); +} + +double Composition::Compatibility(int res) const noexcept { + if (Has(res)) { + return StateProportion(res); + } + double max_compat = -1.0; + double min_compat = 1.0; + for (const auto &c : components) { + double prop = c.value / StateMass(resources[res].state); + 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) , name() , genome() , properties() -, composition() +, composition(sim.Resources()) , base_color(1.0) , highlight_color(0.0, 0.0, 0.0, 1.0) , mass(1.0) @@ -116,8 +153,8 @@ void Creature::AddMass(int res, double amount) { double nonsolid = 0.0; double volume = 0.0; for (const auto &c : composition) { - volume += c.value / sim.Assets().data.resources[c.resource].density; - if (sim.Assets().data.resources[c.resource].state != world::Resource::SOLID) { + volume += c.value / sim.Resources()[c.resource].density; + if (sim.Resources()[c.resource].state != world::Resource::SOLID) { nonsolid += c.value; } } @@ -131,13 +168,12 @@ void Creature::HighlightColor(const glm::dvec3 &c) noexcept { } 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(res)); } else { - // 10% of fluids stays in body - AddMass(res, amount * 0.05); + // 5% of fluids stays in body + AddMass(res, amount * 0.05 * composition.Compatibility(res)); } math::GaloisLFSR &random = sim.Assets().random; if (random.UNorm() < AdaptChance()) { @@ -266,7 +302,7 @@ void Creature::AddParent(Creature &p) { } double Creature::Age() const noexcept { - return sim.Time() - birth; + return Dead() ? death - birth : sim.Time() - birth; } double Creature::AgeFactor(double peak) const noexcept { @@ -294,18 +330,34 @@ double Creature::Strength() const noexcept { return properties.Strength() * ExhaustionFactor() * AgeFactor(0.25); } +double Creature::StrengthFactor() const noexcept { + return Strength() / (Strength() + 1.0); +} + double Creature::Stamina() const noexcept { return properties.Stamina() * ExhaustionFactor() * AgeFactor(0.25); } +double Creature::StaminaFactor() const noexcept { + return Stamina() / (Stamina() + 1.0); +} + double Creature::Dexerty() const noexcept { return properties.Dexerty() * ExhaustionFactor() * AgeFactor(0.25); } +double Creature::DexertyFactor() const noexcept { + return Dexerty() / (Dexerty() + 1.0); +} + double Creature::Intelligence() const noexcept { return properties.Intelligence() * FatigueFactor() * AgeFactor(0.25); } +double Creature::IntelligenceFactor() const noexcept { + return Intelligence() / (Intelligence() + 1.0); +} + double Creature::Lifetime() const noexcept { return properties.Lifetime(); } @@ -327,25 +379,25 @@ double Creature::OffspringMass() const noexcept { } double Creature::PerceptionRange() const noexcept { - return 3.0 * (Dexerty() / (Dexerty() + 1)) + Size(); + return 3.0 * DexertyFactor() + Size(); } double Creature::PerceptionOmniRange() const noexcept { - return 0.5 * (Dexerty() / (Dexerty() + 1)) + Size(); + return 0.5 * DexertyFactor() + Size(); } double Creature::PerceptionField() const noexcept { // this is the cosine of half the angle, so 1.0 is none, -1.0 is perfect - return 0.8 - (Dexerty() / (Dexerty() + 1)); + return 0.8 - DexertyFactor(); } bool Creature::PerceptionTest(const glm::dvec3 &p) const noexcept { const glm::dvec3 diff(p - situation.Position()); double omni_range = PerceptionOmniRange(); - if (length2(diff) < omni_range * omni_range) return true; + if (glm::length2(diff) < omni_range * omni_range) return true; double range = PerceptionRange(); - if (length2(diff) > range * range) return false; - return dot(normalize(diff), situation.Heading()) > PerceptionField(); + if (glm::length2(diff) > range * range) return false; + return glm::dot(glm::normalize(diff), situation.Heading()) > PerceptionField(); } double Creature::OffspringChance() const noexcept { @@ -397,21 +449,21 @@ void Creature::TickState(double dt) { state.pos += f.vel * dt; state.vel += f.acc * dt; situation.EnforceConstraints(state); - if (length2(state.vel) > 0.000001) { - glm::dvec3 nvel(normalize(state.vel)); - double ang = angle(nvel, state.dir); + if (glm::length2(state.vel) > 0.000001) { + glm::dvec3 nvel(glm::normalize(state.vel)); + double ang = glm::angle(nvel, state.dir); double turn_rate = PI * 0.75 * dt; if (ang < turn_rate) { - state.dir = normalize(state.vel); + state.dir = glm::normalize(state.vel); } else if (std::abs(ang - PI) < 0.001) { - state.dir = rotate(state.dir, turn_rate, situation.GetPlanet().NormalAt(state.pos)); + state.dir = glm::rotate(state.dir, turn_rate, situation.GetPlanet().NormalAt(state.pos)); } else { - state.dir = rotate(state.dir, turn_rate, normalize(cross(state.dir, nvel))); + state.dir = glm::rotate(state.dir, turn_rate, glm::normalize(glm::cross(state.dir, nvel))); } } situation.SetState(state); // work is force times distance - DoWork(length(f.acc) * Mass() * length(f.vel) * dt); + DoWork(glm::length(f.acc) * Mass() * glm::length(f.vel) * dt); } Situation::Derivative Creature::Step(const Situation::Derivative &ds, double dt) const noexcept { @@ -420,19 +472,18 @@ Situation::Derivative Creature::Step(const Situation::Derivative &ds, double dt) s.vel += ds.acc * dt; glm::dvec3 force(steering.Force(s)); // gravity = antinormal * mass * Gm / r² - double elevation = situation.GetPlanet().DistanceAt(s.pos); glm::dvec3 normal(situation.GetPlanet().NormalAt(s.pos)); force += glm::dvec3( -normal * Mass() * situation.GetPlanet().GravitationalParameter() - / (elevation * elevation)); + / glm::length2(s.pos)); // if net force is applied and in contact with surface - if (!allzero(force) && std::abs(std::abs(elevation) - situation.GetPlanet().Radius()) < 0.001) { + if (!allzero(force) && glm::length2(s.pos) < (situation.GetPlanet().Radius() + 0.01) * (situation.GetPlanet().Radius() + 0.01)) { // apply friction = -|normal force| * tangential force * coefficient - glm::dvec3 fn(normal * dot(force, normal)); + glm::dvec3 fn(normal * glm::dot(force, normal)); glm::dvec3 ft(force - fn); double u = 0.4; - glm::dvec3 friction(-length(fn) * ft * u); + glm::dvec3 friction(-glm::length(fn) * ft * u); force += friction; } return { @@ -498,7 +549,7 @@ void Creature::TickBrain(double dt) { } } -math::AABB Creature::CollisionBox() const noexcept { +math::AABB Creature::CollisionBounds() const noexcept { return { glm::dvec3(size * -0.5), glm::dvec3(size * 0.5) }; } @@ -508,11 +559,11 @@ glm::dmat4 Creature::CollisionTransform() const noexcept { glm::dmat3 orient; orient[1] = situation.GetPlanet().NormalAt(pos); orient[2] = situation.Heading(); - if (std::abs(dot(orient[1], orient[2])) > 0.999) { + if (std::abs(glm::dot(orient[1], orient[2])) > 0.999) { orient[2] = glm::dvec3(orient[1].z, orient[1].x, orient[1].y); } - orient[0] = normalize(cross(orient[1], orient[2])); - orient[2] = normalize(cross(orient[0], orient[1])); + orient[0] = glm::normalize(glm::cross(orient[1], orient[2])); + orient[2] = glm::normalize(glm::cross(orient[0], orient[1])); return glm::translate(glm::dvec3(pos.x, pos.y, pos.z)) * glm::dmat4(orient) * glm::translate(glm::dvec3(0.0, half_size, 0.0)); @@ -740,7 +791,7 @@ void Split(Creature &c) { // TODO: duplicate situation somehow a->GetSituation().SetPlanetSurface( s.GetPlanet(), - s.Position() + glm::dvec3(0.0, 0.55 * a->Size(), 0.0)); + s.Position() + glm::rotate(s.Heading() * a->Size() * 0.6, PI * 0.5, s.SurfaceNormal())); a->BuildVAO(); c.GetSimulation().Log() << a->Name() << " was born" << std::endl; @@ -754,7 +805,7 @@ void Split(Creature &c) { s.GetPlanet().AddCreature(b); b->GetSituation().SetPlanetSurface( s.GetPlanet(), - s.Position() - glm::dvec3(0.0, 0.55 * b->Size(), 0.0)); + s.Position() + glm::rotate(s.Heading() * b->Size() * 0.6, PI * -0.5, s.SurfaceNormal())); b->BuildVAO(); c.GetSimulation().Log() << b->Name() << " was born" << std::endl; @@ -773,11 +824,43 @@ void Memory::Erase() { known_types.clear(); } +bool Memory::RememberLocation(const Composition &accept, glm::dvec3 &pos) const noexcept { + double best_rating = -1.0; + for (const auto &k : known_types) { + const world::TileType &t = c.GetSimulation().TileTypes()[k.first]; + auto entry = t.FindBestResource(accept); + if (entry != t.resources.end()) { + double rating = entry->ubiquity / std::max(0.125, 0.25 * glm::length2(c.GetSituation().Position() - k.second.first_loc.position)); + if (rating > best_rating) { + best_rating = rating; + pos = k.second.first_loc.position; + } + rating = entry->ubiquity / std::max(0.125, 0.25 * glm::length2(c.GetSituation().Position() - k.second.last_loc.position)); + if (rating > best_rating) { + best_rating = rating; + pos = k.second.last_loc.position; + } + } + } + if (best_rating > 0.0) { + glm::dvec3 error( + c.GetSimulation().Assets().random.SNorm(), + c.GetSimulation().Assets().random.SNorm(), + c.GetSimulation().Assets().random.SNorm()); + pos += error * (2.0 * (1.0 - c.IntelligenceFactor())); + pos = glm::normalize(pos) * c.GetSituation().GetPlanet().Radius(); + return true; + } else { + return false; + } +} + void Memory::Tick(double dt) { Situation &s = c.GetSituation(); if (s.OnSurface()) { TrackStay({ &s.GetPlanet(), s.Position() }, dt); } + // TODO: forget } void Memory::TrackStay(const Location &l, double t) { @@ -843,6 +926,14 @@ bool Situation::OnSurface() const noexcept { return type == PLANET_SURFACE; } +bool Situation::OnGround() const noexcept { + return OnSurface() && glm::length2(state.pos) < (planet->Radius() + 0.05) * (planet->Radius() + 0.05); +} + +glm::dvec3 Situation::SurfaceNormal() const noexcept { + return planet->NormalAt(state.pos); +} + world::Tile &Situation::GetTile() const noexcept { return planet->TileAt(state.pos); } @@ -864,8 +955,8 @@ void Situation::Accelerate(const glm::dvec3 &dv) noexcept { void Situation::EnforceConstraints(State &s) noexcept { if (OnSurface()) { double r = GetPlanet().Radius(); - if (length2(s.pos) < r * r) { - s.pos = normalize(s.pos) * r; + if (glm::length2(s.pos) < r * r) { + s.pos = glm::normalize(s.pos) * r; } } } @@ -947,32 +1038,36 @@ glm::dvec3 Steering::Force(const Situation::State &s) const noexcept { for (auto &other : s.GetPlanet().Creatures()) { if (&*other == &c) continue; glm::dvec3 diff = s.Position() - other->GetSituation().Position(); - if (length2(diff) > max_look * max_look) continue; + if (glm::length2(diff) > max_look * max_look) continue; if (!c.PerceptionTest(other->GetSituation().Position())) continue; - double sep = glm::clamp(length(diff) - other->Size() * 0.707 - c.Size() * 0.707, 0.0, min_dist); - repulse += normalize(diff) * (1.0 - sep / min_dist) * force; + double sep = glm::clamp(glm::length(diff) - other->Size() * 0.707 - c.Size() * 0.707, 0.0, min_dist); + repulse += glm::normalize(diff) * (1.0 - sep / min_dist) * force; } result += repulse; } if (halting) { - // break twice as hard - result += -2.0 * s.vel * force; + // brake hard + result += -5.0 * s.vel * force; } if (seeking) { glm::dvec3 diff = target - s.pos; if (!allzero(diff)) { - result += TargetVelocity(s, (normalize(diff) * speed), force); + result += TargetVelocity(s, (glm::normalize(diff) * speed), force); } } if (arriving) { glm::dvec3 diff = target - s.pos; - double dist = length(diff); + double dist = glm::length(diff); if (!allzero(diff) && dist > std::numeric_limits::epsilon()) { result += TargetVelocity(s, diff * std::min(dist * force, speed) / dist, force); } } - if (length2(result) > max_force * max_force) { - result = normalize(result) * max_force; + // remove vertical component, if any + const glm::dvec3 normal(c.GetSituation().GetPlanet().NormalAt(s.pos)); + result += normal * glm::dot(normal, result); + // clamp to max + if (glm::length2(result) > max_force * max_force) { + result = glm::normalize(result) * max_force; } return result; }