From abab2bbb6e3d80b9b39567056a7aa1c86663147f Mon Sep 17 00:00:00 2001 From: mlangkabel Date: Wed, 17 Jun 2026 22:40:58 +0200 Subject: [PATCH] make repair ships not retreat if someone needs help --- src/lib/ecs/system/ai/RetreatEvaluator.cpp | 36 ++++++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/lib/ecs/system/ai/RetreatEvaluator.cpp b/src/lib/ecs/system/ai/RetreatEvaluator.cpp index 67d19d9..29bb135 100644 --- a/src/lib/ecs/system/ai/RetreatEvaluator.cpp +++ b/src/lib/ecs/system/ai/RetreatEvaluator.cpp @@ -6,10 +6,12 @@ #include "AttackBehavior.h" #include "BehaviorScores.h" +#include "BehaviorTargeting.h" #include "EntityAdmin.h" #include "FactionComponent.h" #include "HealthComponent.h" #include "PositionComponent.h" +#include "RepairBehavior.h" #include "RetreatBehavior.h" #include "SensorRangeComponent.h" #include "ShipIdentityComponent.h" @@ -28,9 +30,15 @@ void RetreatEvaluator::evaluate(EntityAdmin& admin) if (f.isEnemy) { enemyShips.push_back(pos.value); } }); - admin.forEach( + // Snapshot repairables so weaponless repair ships can decide whether there is + // still a damaged ally worth holding ground for. + const std::vector repairables = buildRepairables(admin); + + admin.forEach( [&](entt::entity e, RetreatBehavior& retreat, const PositionComponent& pos, - const HealthComponent& health, const SensorRangeComponent& sensor) + const HealthComponent& health, const SensorRangeComponent& sensor, + const FactionComponent& faction) { const bool lowHp = (health.maxHp > 0.0f) && (health.hp / health.maxHp < retreat.retreatHpFraction); @@ -39,14 +47,36 @@ void RetreatEvaluator::evaluate(EntityAdmin& admin) const bool hasWeapons = admin.hasAll(e); if (!hasWeapons) { + bool enemyInRange = false; for (const QVector2D& enemy : enemyShips) { if ((enemy - pos.value).length() <= sensor.value_tiles) { - threatened = true; + enemyInRange = true; break; } } + + // A weaponless ship with a repair tool holds its ground while a + // damaged ally remains within sensor range; it only flees once + // there is nothing left to repair. + bool repairTargetInRange = false; + if (enemyInRange && admin.hasAll(e)) + { + for (const RepairableInfo& r : repairables) + { + if (r.entity == e) { continue; } + if (r.isEnemy != faction.isEnemy) { continue; } + if (r.hp <= 0.0f || r.hp >= r.maxHp) { continue; } + if ((r.position - pos.value).length() <= sensor.value_tiles) + { + repairTargetInRange = true; + break; + } + } + } + + threatened = enemyInRange && !repairTargetInRange; } retreat.score = (lowHp || threatened)