]> git.localhorst.tv Git - blobs.git/blobdiff - src/creature/creature.cpp
slightly better physics
[blobs.git] / src / creature / creature.cpp
index d0000107b683e5b454d1864ba65c9c4c6dfe3873..b95b531a13097041c2b7e835d4d69483f3798446 100644 (file)
@@ -133,7 +133,9 @@ void Creature::Hurt(double amount) noexcept {
        stats.Damage().Add(amount);
        if (stats.Damage().Full()) {
                std::cout << "[" << int(sim.Time()) << "s] " << name << " ";
-               if (stats.Breath().Full()) {
+               if (stats.Exhaustion().Full()) {
+                       std::cout << "died of exhaustion";
+               } else if (stats.Breath().Full()) {
                        std::cout << "suffocated";
                } else if (stats.Thirst().Full()) {
                        std::cout << "died of thirst";
@@ -142,7 +144,34 @@ void Creature::Hurt(double amount) noexcept {
                } else {
                        std::cout << "succumed to wounds";
                }
-               std::cout << std::endl;
+               std::cout << " at an age of ";
+               {
+                       int age = int(Age());
+                       if (age >= 3600) {
+                               std::cout << (age / 3600) << "h ";
+                               age %= 3600;
+                       }
+                       if (age >= 60) {
+                               std::cout << (age / 60) << "m ";
+                               age %= 60;
+                       }
+                       std::cout << age << 's';
+               }
+               std::cout << " (" << int(Age() / properties.Lifetime() * 100)
+                       << "% of life expectancy of ";
+               {
+                       int lt = int(properties.Lifetime());
+                       if (lt >= 3600) {
+                               std::cout << (lt / 3600) << "h ";
+                               lt %= 3600;
+                       }
+                       if (lt >= 60) {
+                               std::cout << (lt / 60) << "m ";
+                               lt %= 60;
+                       }
+                       std::cout << lt << 's';
+               }
+               std::cout << ")" << std::endl;
                Die();
        }
 }
@@ -249,10 +278,11 @@ 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);
-               double turn_rate = PI * 0.5 * dt;
+               double turn_rate = PI * 0.75 * dt;
                if (ang < turn_rate) {
                        state.dir = normalize(state.vel);
                } else if (std::abs(ang - PI) < 0.001) {
@@ -262,16 +292,33 @@ void Creature::TickState(double dt) {
                }
        }
        situation.SetState(state);
-       stats.Exhaustion().Add(length(f.acc) * Mass() / Stamina() * dt);
+       stats.Exhaustion().Add(length(f.acc) * Mass() / Stamina() * 0.5 * dt);
 }
 
 Situation::Derivative Creature::Step(const Situation::Derivative &ds, double dt) const noexcept {
        Situation::State s = situation.GetState();
        s.pos += ds.vel * dt;
        s.vel += ds.acc * dt;
+       glm::dvec3 force(steering.Force(s));
+       // gravity = antinormal * mass * Gm / r²
+       double elevation = s.pos[(situation.Surface() + 2) % 3];
+       glm::dvec3 normal(world::Planet::SurfaceNormal(situation.Surface()));
+       force += glm::dvec3(
+               -normal
+               * Mass() * situation.GetPlanet().GravitationalParameter()
+               / (elevation * elevation));
+       // if net force is applied and in contact with surface
+       if (!allzero(force) && std::abs(std::abs(elevation) - situation.GetPlanet().Radius()) < 0.001) {
+               // apply friction = -|normal force| * tangential force * coefficient
+               glm::dvec3 fn(normal * dot(force, normal));
+               glm::dvec3 ft(force - fn);
+               double u = 0.4;
+               glm::dvec3 friction(-length(fn) * ft * u);
+               force += friction;
+       }
        return {
                s.vel,
-               steering.Force(s) / Mass()
+               force / Mass()
        };
 }
 
@@ -293,6 +340,10 @@ void Creature::TickStats(double dt) {
                constexpr double dps = 1.0 / 128.0;
                Hurt(dps * dt);
        }
+       if (!situation.Moving()) {
+               // double exhaustion recovery when standing still
+               stats.Exhaustion().Add(stats.Exhaustion().gain * dt);
+       }
 }
 
 void Creature::TickBrain(double dt) {
@@ -320,13 +371,22 @@ void Creature::TickBrain(double dt) {
        }
 }
 
-glm::dmat4 Creature::LocalTransform() noexcept {
+math::AABB Creature::CollisionBox() const noexcept {
+       return { glm::dvec3(size * -0.5), glm::dvec3(size * 0.5) };
+}
+
+glm::dmat4 Creature::CollisionTransform() const noexcept {
        const double half_size = size * 0.5;
        const glm::dvec3 &pos = situation.Position();
        const glm::dmat3 srf(world::Planet::SurfaceOrientation(situation.Surface()));
        return glm::translate(glm::dvec3(pos.x, pos.y, pos.z + half_size))
                * glm::rotate(glm::orientedAngle(-srf[2], situation.Heading(), srf[1]), srf[1])
-               * glm::dmat4(srf)
+               * glm::dmat4(srf);
+}
+
+glm::dmat4 Creature::LocalTransform() noexcept {
+       const double half_size = size * 0.5;
+       return CollisionTransform()
                * glm::scale(glm::dvec3(half_size, half_size, half_size));
 }
 
@@ -451,7 +511,7 @@ void Spawn(Creature &c, world::Planet &p) {
 
        Genome genome;
        genome.properties.Strength() = { 2.0, 0.1 };
-       genome.properties.Stamina() = { 4.0, 0.1 };
+       genome.properties.Stamina() = { 2.0, 0.1 };
        genome.properties.Dexerty() = { 2.0, 0.1 };
        genome.properties.Intelligence() = { 1.0, 0.1 };
        genome.properties.Lifetime() = { 480.0, 60.0 };
@@ -534,7 +594,7 @@ void Split(Creature &c) {
        // TODO: duplicate situation somehow
        a->GetSituation().SetPlanetSurface(
                s.GetPlanet(), s.Surface(),
-               s.Position() + glm::dvec3(0.0, a->Size() + 0.1, 0.0));
+               s.Position() + glm::dvec3(0.0, 0.55 * a->Size(), 0.0));
        a->BuildVAO();
        std::cout << "[" << int(c.GetSimulation().Time()) << "s] "
                << a->Name() << " was born" << std::endl;
@@ -548,7 +608,7 @@ void Split(Creature &c) {
        s.GetPlanet().AddCreature(b);
        b->GetSituation().SetPlanetSurface(
                s.GetPlanet(), s.Surface(),
-               s.Position() + glm::dvec3(0.0, b->Size() - 0.1, 0.0));
+               s.Position() - glm::dvec3(0.0, 0.55 * b->Size(), 0.0));
        b->BuildVAO();
        std::cout << "[" << int(c.GetSimulation().Time()) << "s] "
                << b->Name() << " was born" << std::endl;
@@ -566,7 +626,7 @@ Memory::~Memory() {
 
 void Memory::Tick(double dt) {
        Situation &s = c.GetSituation();
-       if (s.OnSurface()) {
+       if (s.OnTile()) {
                TrackStay({ &s.GetPlanet(), s.Surface(), s.SurfacePosition() }, dt);
        }
 }
@@ -658,12 +718,26 @@ const world::TileType &Situation::GetTileType() const noexcept {
 
 void Situation::Move(const glm::dvec3 &dp) noexcept {
        state.pos += dp;
+       EnforceConstraints(state);
+}
+
+void Situation::Accelerate(const glm::dvec3 &dv) noexcept {
+       state.vel += dv;
+       EnforceConstraints(state);
+}
+
+void Situation::EnforceConstraints(State &s) noexcept {
        if (OnSurface()) {
-               // enforce ground constraint
                if (Surface() < 3) {
-                       state.pos[(Surface() + 2) % 3] = std::max(0.0, state.pos[(Surface() + 2) % 3]);
+                       if (s.pos[(Surface() + 2) % 3] < GetPlanet().Radius()) {
+                               s.pos[(Surface() + 2) % 3] = GetPlanet().Radius();
+                               s.vel[(Surface() + 2) % 3] = std::max(0.0, s.vel[(Surface() + 2) % 3]);
+                       }
                } else {
-                       state.pos[(Surface() + 2) % 3] = std::min(0.0, state.pos[(Surface() + 2) % 3]);
+                       if (s.pos[(Surface() + 2) % 3] > -GetPlanet().Radius()) {
+                               s.pos[(Surface() + 2) % 3] = -GetPlanet().Radius();
+                               s.vel[(Surface() + 2) % 3] = std::min(0.0, s.vel[(Surface() + 2) % 3]);
+                       }
                }
        }
 }
@@ -673,6 +747,7 @@ void Situation::SetPlanetSurface(world::Planet &p, int srf, const glm::dvec3 &po
        planet = &p;
        surface = srf;
        state.pos = pos;
+       EnforceConstraints(state);
 }
 
 
@@ -703,6 +778,10 @@ void Steering::DontSeparate() noexcept {
        separating = false;
 }
 
+void Steering::ResumeSeparate() noexcept {
+       separating = true;
+}
+
 void Steering::Halt() noexcept {
        halting = true;
        seeking = false;