From: Daniel Karbach Date: Wed, 13 Dec 2017 01:28:42 +0000 (+0100) Subject: aggression X-Git-Url: https://git.localhorst.tv/?a=commitdiff_plain;h=cead4f0686af352cdbac1f2c2df9b6a21ad9faec;p=blobs.git aggression --- diff --git a/src/creature/AttackGoal.hpp b/src/creature/AttackGoal.hpp new file mode 100644 index 0000000..ab8056f --- /dev/null +++ b/src/creature/AttackGoal.hpp @@ -0,0 +1,35 @@ +#ifndef BLOBS_CREATURE_ATTACKGOAL_HPP_ +#define BLOBS_CREATURE_ATTACKGOAL_HPP_ + +#include "Goal.hpp" + +namespace blobs { +namespace creature { + +class AttackGoal +: public Goal { + +public: + AttackGoal(Creature &self, Creature &target); + ~AttackGoal() override; + +public: + std::string Describe() const override; + void Tick(double dt) override; + void Action() override; + void OnBackground() override; + + void SetDamageTarget(double t) noexcept { damage_target = t; } + +private: + Creature ⌖ + double damage_target; + double damage_dealt; + double cooldown; + +}; + +} +} + +#endif diff --git a/src/creature/Composition.hpp b/src/creature/Composition.hpp index 969407f..243c079 100644 --- a/src/creature/Composition.hpp +++ b/src/creature/Composition.hpp @@ -41,6 +41,8 @@ public: double StateProportion(int res) const noexcept; double Compatibility(int res) const noexcept; double TotalMass() const noexcept { return total_mass; } + double TotalVolume() const noexcept { return total_volume; } + double TotalDensity() const noexcept { return total_mass / total_volume; } double StateMass(world::Resource::State s) const noexcept { return state_mass[s]; } public: @@ -56,6 +58,7 @@ private: const world::Set &resources; std::vector components; double total_mass; + double total_volume; double state_mass[4]; }; diff --git a/src/creature/Creature.hpp b/src/creature/Creature.hpp index d55ab6f..f22f3da 100644 --- a/src/creature/Creature.hpp +++ b/src/creature/Creature.hpp @@ -180,6 +180,8 @@ public: math::AABB CollisionBounds() const noexcept; glm::dmat4 CollisionTransform() const noexcept; + void OnCollide(Creature &other); + glm::dmat4 LocalTransform() noexcept; void BuildVAO(); diff --git a/src/creature/Memory.hpp b/src/creature/Memory.hpp index 1d72c75..192d03a 100644 --- a/src/creature/Memory.hpp +++ b/src/creature/Memory.hpp @@ -35,6 +35,8 @@ public: /// location of the best fitting resource bool RememberLocation(const Composition &, glm::dvec3 &pos) const noexcept; + void TrackCollision(Creature &); + void Tick(double dt); private: @@ -52,6 +54,11 @@ private: double time_spent; }; std::map known_types; + struct Profile { + double annoyance = 0.0; + double familiarity = 0.0; + }; + std::map known_creatures; }; diff --git a/src/creature/creature.cpp b/src/creature/creature.cpp index 1cf4a3a..7ad0657 100644 --- a/src/creature/creature.cpp +++ b/src/creature/creature.cpp @@ -6,6 +6,7 @@ #include "Situation.hpp" #include "Steering.hpp" +#include "AttackGoal.hpp" #include "BlobBackgroundTask.hpp" #include "Goal.hpp" #include "IdleGoal.hpp" @@ -33,6 +34,7 @@ Composition::Composition(const world::Set &resources) : resources(resources) , components() , total_mass(0.0) +, total_volume(0.0) , state_mass{0.0} { } @@ -64,6 +66,7 @@ void Composition::Add(int res, double amount) { std::sort(components.begin(), components.end(), CompositionCompare); state_mass[resources[res].state] += amount; total_mass += amount; + total_volume += amount / resources[res].density; } bool Composition::Has(int res) const noexcept { @@ -591,6 +594,10 @@ glm::dmat4 Creature::CollisionTransform() const noexcept { * glm::translate(glm::dvec3(0.0, half_size, 0.0)); } +void Creature::OnCollide(Creature &other) { + memory.TrackCollision(other); +} + glm::dmat4 Creature::LocalTransform() noexcept { const double half_size = size * 0.5; return CollisionTransform() @@ -844,6 +851,7 @@ Memory::~Memory() { void Memory::Erase() { known_types.clear(); + known_creatures.clear(); } bool Memory::RememberLocation(const Composition &accept, glm::dvec3 &pos) const noexcept { @@ -877,6 +885,21 @@ bool Memory::RememberLocation(const Composition &accept, glm::dvec3 &pos) const } } +void Memory::TrackCollision(Creature &other) { + // TODO: find out whose fault it was + // TODO: source values from personality + Profile &p = known_creatures[&other]; + p.annoyance += 0.1; + const double annoy_fact = p.annoyance / (p.annoyance + 1.0); + if (c.GetSimulation().Assets().random.UNorm() > annoy_fact * 0.1 * (1.0 - c.GetStats().Damage().value)) { + AttackGoal *g = new AttackGoal(c, other); + g->SetDamageTarget(annoy_fact); + g->Urgency(annoy_fact); + c.AddGoal(std::unique_ptr(g)); + p.annoyance *= 0.5; + } +} + void Memory::Tick(double dt) { Situation &s = c.GetSituation(); if (s.OnSurface()) { diff --git a/src/creature/goal.cpp b/src/creature/goal.cpp index abac467..41f91f3 100644 --- a/src/creature/goal.cpp +++ b/src/creature/goal.cpp @@ -1,3 +1,4 @@ +#include "AttackGoal.hpp" #include "BlobBackgroundTask.hpp" #include "Goal.hpp" #include "IdleGoal.hpp" @@ -22,6 +23,78 @@ namespace blobs { namespace creature { +AttackGoal::AttackGoal(Creature &self, Creature &target) +: Goal(self) +, target(target) +, damage_target(0.25) +, damage_dealt(0.0) +, cooldown(0.0) { +} + +AttackGoal::~AttackGoal() { +} + +std::string AttackGoal::Describe() const { + return "attack " + target.Name(); +} + +void AttackGoal::Tick(double dt) { + cooldown -= dt; +} + +void AttackGoal::Action() { + if (target.Dead() || !GetCreature().PerceptionTest(target.GetSituation().Position())) { + SetComplete(); + return; + } + const glm::dvec3 diff(GetCreature().GetSituation().Position() - target.GetSituation().Position()); + const double hit_range = GetCreature().Size() * 0.5 * GetCreature().DexertyFactor(); + const double hit_dist = hit_range + (0.5 * GetCreature().Size()) + 0.5 * (target.Size()); + if (GetCreature().GetStats().Damage().Critical()) { + // flee + GetCreature().GetSteering().Pass(diff * 5.0); + GetCreature().GetSteering().DontSeparate(); + GetCreature().GetSteering().Haste(1.0); + } else if (glm::length2(diff) > hit_dist * hit_dist) { + // full throttle chase + GetCreature().GetSteering().Pass(target.GetSituation().Position()); + GetCreature().GetSteering().DontSeparate(); + GetCreature().GetSteering().Haste(1.0); + } else { + // attack + GetCreature().GetSteering().Halt(); + GetCreature().GetSteering().DontSeparate(); + GetCreature().GetSteering().Haste(1.0); + if (cooldown <= 0.0) { + constexpr double impulse = 0.05; + const double force = GetCreature().Strength(); + const double damage = + force * impulse + * (GetCreature().GetComposition().TotalDensity() / target.GetComposition().TotalDensity()) + * (GetCreature().Mass() / target.Mass()) + / target.Mass(); + GetCreature().DoWork(force * impulse * glm::length(diff)); + target.Hurt(damage); + target.GetSituation().Accelerate(glm::normalize(diff) * force * -impulse); + damage_dealt += damage; + if (damage_dealt >= damage_target || target.Dead()) { + SetComplete(); + if (target.Dead()) { + GetCreature().GetSimulation().Log() << GetCreature().Name() + << " killed " << target.Name() << std::endl; + } + } + cooldown = 1.0 + (4.0 * (1.0 - GetCreature().DexertyFactor())); + } + } +} + +void AttackGoal::OnBackground() { + // abort if something more important comes up + SetComplete(); +} + + BlobBackgroundTask::BlobBackgroundTask(Creature &c) : Goal(c) , breathing(false) diff --git a/src/world/world.cpp b/src/world/world.cpp index 2f0d130..1758c5e 100644 --- a/src/world/world.cpp +++ b/src/world/world.cpp @@ -208,11 +208,12 @@ void Body::CheckCollision() noexcept { } } for (auto &c : collisions) { + c.A().OnCollide(c.B()); + c.B().OnCollide(c.A()); c.A().GetSituation().Move(c.Normal() * (c.Depth() * -0.5)); c.B().GetSituation().Move(c.Normal() * (c.Depth() * 0.5)); c.A().GetSituation().Accelerate(c.Normal() * -glm::dot(c.Normal(), c.AVel())); c.B().GetSituation().Accelerate(c.Normal() * -glm::dot(c.Normal(), c.BVel())); - // TODO: notify participants so they can be annoyed } }