87 lines
3.2 KiB
C++
87 lines
3.2 KiB
C++
#include "RetreatEvaluator.h"
|
|
|
|
#include <vector>
|
|
|
|
#include <QVector2D>
|
|
|
|
#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"
|
|
#include "tracing.h"
|
|
|
|
void RetreatEvaluator::evaluate(EntityAdmin& admin)
|
|
{
|
|
TRACE();
|
|
|
|
// Snapshot enemy ship positions for threat detection.
|
|
std::vector<QVector2D> enemyShips;
|
|
admin.forEach<ShipIdentityComponent, PositionComponent, FactionComponent>(
|
|
[&enemyShips](entt::entity /*e*/, const ShipIdentityComponent& /*si*/,
|
|
const PositionComponent& pos, const FactionComponent& f)
|
|
{
|
|
if (f.isEnemy) { enemyShips.push_back(pos.value); }
|
|
});
|
|
|
|
// Snapshot repairables so weaponless repair ships can decide whether there is
|
|
// still a damaged ally worth holding ground for.
|
|
const std::vector<RepairableInfo> repairables = buildRepairables(admin);
|
|
|
|
admin.forEach<RetreatBehavior, PositionComponent, HealthComponent,
|
|
SensorRangeComponent, FactionComponent>(
|
|
[&](entt::entity e, RetreatBehavior& retreat, const PositionComponent& pos,
|
|
const HealthComponent& health, const SensorRangeComponent& sensor,
|
|
const FactionComponent& faction)
|
|
{
|
|
const bool lowHp = (health.maxHp > 0.0f)
|
|
&& (health.hp / health.maxHp < retreat.retreatHpFraction);
|
|
|
|
bool threatened = false;
|
|
const bool hasWeapons = admin.hasAll<AttackBehavior>(e);
|
|
if (!hasWeapons)
|
|
{
|
|
bool enemyInRange = false;
|
|
for (const QVector2D& enemy : enemyShips)
|
|
{
|
|
if ((enemy - pos.value).length() <= sensor.value_tiles)
|
|
{
|
|
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<RepairBehavior>(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)
|
|
? BehaviorScores::kRetreat
|
|
: BehaviorScores::kInactive;
|
|
});
|
|
}
|