define ship roles via added modules and allow multiple weapons
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#include "AiSystem.h"
|
||||
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <QVector2D>
|
||||
@@ -14,6 +15,7 @@
|
||||
#include "HealthComponent.h"
|
||||
#include "HomeReturnBehaviorComponent.h"
|
||||
#include "HqProxyComponent.h"
|
||||
#include "ModuleOwnerComponent.h"
|
||||
#include "MovementIntentComponent.h"
|
||||
#include "PositionComponent.h"
|
||||
#include "RallyBehaviorComponent.h"
|
||||
@@ -224,13 +226,13 @@ void AiSystem::tickRepairBehavior(EntityAdmin& admin, BuildingSystem& buildings)
|
||||
}
|
||||
});
|
||||
|
||||
admin.forEach<RepairBehaviorComponent, RepairToolComponent, PositionComponent,
|
||||
admin.forEach<RepairBehaviorComponent, PositionComponent,
|
||||
FactionComponent, SensorRangeComponent, MovementIntentComponent>(
|
||||
[&](entt::entity e, RepairBehaviorComponent& rb, RepairToolComponent& rt,
|
||||
[&](entt::entity e, RepairBehaviorComponent& rb,
|
||||
PositionComponent& pos, FactionComponent& /*faction*/,
|
||||
SensorRangeComponent& sensor, MovementIntentComponent& intent)
|
||||
{
|
||||
const float repairRange = rt.range;
|
||||
const float repairRange = rb.maxRepairRange;
|
||||
|
||||
// Flee if enemy nearby.
|
||||
bool enemyNearby = false;
|
||||
@@ -303,17 +305,6 @@ void AiSystem::tickRepairBehavior(EntityAdmin& admin, BuildingSystem& buildings)
|
||||
targetPos = admin.get<PositionComponent>(target).value;
|
||||
}
|
||||
|
||||
const float distToTarget = (targetPos - pos.value).length();
|
||||
if (distToTarget <= repairRange)
|
||||
{
|
||||
if (admin.isValid(target) && admin.hasAll<HealthComponent>(target))
|
||||
{
|
||||
HealthComponent& targetHealth = admin.get<HealthComponent>(target);
|
||||
targetHealth.hp = std::min(targetHealth.hp + rt.ratePerTick,
|
||||
targetHealth.maxHp);
|
||||
}
|
||||
}
|
||||
|
||||
if (2 > intent.priority)
|
||||
{
|
||||
intent = MovementIntentComponent{2, targetPos};
|
||||
@@ -321,6 +312,33 @@ void AiSystem::tickRepairBehavior(EntityAdmin& admin, BuildingSystem& buildings)
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// tickRepairTools
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void AiSystem::tickRepairTools(EntityAdmin& admin)
|
||||
{
|
||||
admin.forEach<RepairToolComponent, ModuleOwnerComponent>(
|
||||
[&](entt::entity /*e*/, RepairToolComponent& rt, const ModuleOwnerComponent& owner)
|
||||
{
|
||||
if (!admin.hasAll<RepairBehaviorComponent>(owner.owner)) { return; }
|
||||
const RepairBehaviorComponent& rb =
|
||||
admin.get<RepairBehaviorComponent>(owner.owner);
|
||||
if (!rb.currentTarget) { return; }
|
||||
|
||||
const entt::entity target = *rb.currentTarget;
|
||||
if (!admin.isValid(target) || !admin.hasAll<HealthComponent>(target)) { return; }
|
||||
|
||||
const PositionComponent& ownerPos = admin.get<PositionComponent>(owner.owner);
|
||||
const PositionComponent& targetPos = admin.get<PositionComponent>(target);
|
||||
const float dist = (targetPos.value - ownerPos.value).length();
|
||||
if (dist > rt.range) { return; }
|
||||
|
||||
HealthComponent& targetHealth = admin.get<HealthComponent>(target);
|
||||
targetHealth.hp = std::min(targetHealth.hp + rt.ratePerTick, targetHealth.maxHp);
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// tickSalvageBehavior (priority 1)
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -344,15 +362,31 @@ void AiSystem::tickSalvageBehavior(EntityAdmin& admin, ScrapSystem& scraps,
|
||||
}
|
||||
});
|
||||
|
||||
// Aggregate cargo across all salvage-module children per owning ship.
|
||||
struct AggregatedCargo
|
||||
{
|
||||
int totalCurrent = 0;
|
||||
int totalCapacity = 0;
|
||||
};
|
||||
std::unordered_map<entt::entity, AggregatedCargo> cargoByShip;
|
||||
admin.forEach<SalvageCargoComponent, ModuleOwnerComponent>(
|
||||
[&](entt::entity /*ce*/, const SalvageCargoComponent& c, const ModuleOwnerComponent& o)
|
||||
{
|
||||
AggregatedCargo& agg = cargoByShip[o.owner];
|
||||
agg.totalCurrent += c.current;
|
||||
agg.totalCapacity += c.capacity;
|
||||
});
|
||||
|
||||
const std::vector<ScrapInfo> allScrap = scraps.allScrapInfo();
|
||||
|
||||
admin.forEach<SalvageBehaviorComponent, SalvageCargoComponent, PositionComponent,
|
||||
admin.forEach<SalvageBehaviorComponent, PositionComponent,
|
||||
SensorRangeComponent, MovementIntentComponent>(
|
||||
[&](entt::entity /*e*/, SalvageBehaviorComponent& salvageBehavior,
|
||||
SalvageCargoComponent& cargo, PositionComponent& pos,
|
||||
[&](entt::entity e, SalvageBehaviorComponent& salvageBehavior,
|
||||
PositionComponent& pos,
|
||||
SensorRangeComponent& sensor, MovementIntentComponent& intent)
|
||||
{
|
||||
const float collectRange = cargo.collectionRange;
|
||||
const float collectRange = salvageBehavior.maxCollectionRange;
|
||||
const AggregatedCargo& cargoState = cargoByShip[e];
|
||||
|
||||
// Assign nearest SalvageBay if needed.
|
||||
if (salvageBehavior.deliveryBay == kInvalidBuildingId)
|
||||
@@ -378,7 +412,8 @@ void AiSystem::tickSalvageBehavior(EntityAdmin& admin, ScrapSystem& scraps,
|
||||
}
|
||||
}
|
||||
|
||||
const bool cargoFull = (cargo.current >= cargo.capacity);
|
||||
const bool cargoFull = (cargoState.totalCurrent >= cargoState.totalCapacity
|
||||
&& cargoState.totalCapacity > 0);
|
||||
|
||||
if (cargoFull)
|
||||
{
|
||||
@@ -389,17 +424,26 @@ void AiSystem::tickSalvageBehavior(EntityAdmin& admin, ScrapSystem& scraps,
|
||||
if (bayId != kInvalidBuildingId
|
||||
&& (pos.value - bayPos).length() <= 1.0f)
|
||||
{
|
||||
if (buildings.deliverScrapToSalvageBay(bayId))
|
||||
{
|
||||
--cargo.current;
|
||||
}
|
||||
// Decrement first non-empty salvage child.
|
||||
bool delivered = false;
|
||||
admin.forEach<SalvageCargoComponent, ModuleOwnerComponent>(
|
||||
[&](entt::entity /*ce*/, SalvageCargoComponent& c,
|
||||
const ModuleOwnerComponent& o)
|
||||
{
|
||||
if (delivered || o.owner != e || c.current <= 0) { return; }
|
||||
if (buildings.deliverScrapToSalvageBay(bayId))
|
||||
{
|
||||
--c.current;
|
||||
delivered = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Retreat if enemy near and cargo empty.
|
||||
bool retreating = false;
|
||||
if (cargo.current == 0)
|
||||
if (cargoState.totalCurrent == 0)
|
||||
{
|
||||
for (const EnemyShipPos& enemy : enemyShips)
|
||||
{
|
||||
@@ -417,16 +461,24 @@ void AiSystem::tickSalvageBehavior(EntityAdmin& admin, ScrapSystem& scraps,
|
||||
}
|
||||
if (retreating) { return; }
|
||||
|
||||
// Collect nearby scrap.
|
||||
// Collect nearby scrap — increment first non-full salvage child.
|
||||
for (const ScrapInfo& si : allScrap)
|
||||
{
|
||||
if ((si.position - pos.value).length() <= collectRange)
|
||||
{
|
||||
if (scraps.consume(si.entity))
|
||||
{
|
||||
++cargo.current;
|
||||
salvageBehavior.scrapTarget = std::nullopt;
|
||||
}
|
||||
bool collected = false;
|
||||
admin.forEach<SalvageCargoComponent, ModuleOwnerComponent>(
|
||||
[&](entt::entity /*ce*/, SalvageCargoComponent& c,
|
||||
const ModuleOwnerComponent& o)
|
||||
{
|
||||
if (collected || o.owner != e || c.current >= c.capacity) { return; }
|
||||
if (scraps.consume(si.entity))
|
||||
{
|
||||
++c.current;
|
||||
salvageBehavior.scrapTarget = std::nullopt;
|
||||
collected = true;
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user