make repair ships standby with rest of fleet if there is no one to repair (instead of advancing towards the enemy stations)
This commit is contained in:
@@ -173,11 +173,12 @@ Modules in `modules.toml` define a `surface_mask` — a list of strings that des
|
||||
Each salvage module instance operates independently: it has its own cargo hold (`cargo_capacity`), collection range (`collection_range`), and collection rate (`collection_rate`, in collections per second). After collecting a piece of scrap, the module cannot collect again until `1 / collection_rate` seconds have elapsed. A ship with multiple salvage modules can therefore collect multiple pieces of scrap per tick (one per ready module), and installs of different module types may have different ranges and rates. The ship navigates based on the maximum collection range across all installed salvage modules.
|
||||
|
||||
Salvage collection and delivery are world-state changes performed every tick regardless of which behavior the ship is currently executing; the salvage behavior only governs where the ship navigates (toward scrap, toward a Salvage Bay, or — when retreating — toward the rally point).
|
||||
- REQ-SHP-REPAIR: Ships with at least one **repair module** (player) — patrol by moving forward (rightward, away from the asteroid) while searching sensor range. If a damaged player defence station or player ship enters sensor range, navigate toward it by orbiting it at the repair orbit radius (REQ-SHP-ORBIT) and repair. If an enemy ship enters sensor range, the ship retreats (REQ-SHP-RETREAT) until no enemy is in sensor range, then resumes patrol. The player can configure the target priority per shipyard:
|
||||
- REQ-SHP-REPAIR: Ships with at least one **repair module** (player) — when no more urgent behavior applies, hold with the fleet (REQ-SHP-STANDBY) rather than charging the enemy, so damaged allies stay within sensor range. If a damaged player defence station or player ship enters sensor range, navigate toward it by orbiting it at the repair orbit radius (REQ-SHP-ORBIT) and repair. If an enemy ship enters sensor range, the ship retreats (REQ-SHP-RETREAT) until no enemy is in sensor range — except that it holds its ground and keeps repairing while a damaged friendly remains within sensor range (REQ-SHP-RETREAT), retreating only once there is nothing left to repair — then resumes patrol. The player can configure the target priority per shipyard:
|
||||
- Defence stations first / ships first / nearest target.
|
||||
|
||||
Each repair module instance operates independently: it has its own repair rate (`repair_rate`) and repair range (`repair_range`). On each tick, a module first attempts to heal the ship's current behavior-level navigation target if that target is within the module's `repair_range` and is damaged (HP above zero and below maximum HP). If those conditions are not met — because the target is out of the module's `repair_range`, already at full health, or destroyed — the module independently searches for the nearest damaged friendly (player ship or player defence station) within its own `repair_range` and heals that instead. If no valid target is found within range, the module idles. A ship with multiple repair modules can therefore heal different targets simultaneously. Navigation is driven solely by the behavior-level target; individual module fallback targets do not affect which direction the ship moves. Repair healing is a world-state change applied every tick regardless of which behavior the ship is currently executing.
|
||||
- REQ-SHP-RETREAT: **Player ships retreat to the rally point (REQ-SHP-RALLY) when threatened.** A ship retreats while either condition holds: (a) its HP is below a low-HP threshold (currently 30% of its maximum HP); or (b) it has no weapon modules and an enemy ship is within its sensor range. Retreating takes priority over the ship's other behaviors and moves it toward the rally point; the ship resumes its normal behavior once neither condition holds. Enemy ships never retreat (REQ-SHP-ENEMY-AI).
|
||||
- REQ-SHP-STANDBY: **Ships with at least one repair module hold with their fleet when idle**, whether or not they also carry weapon modules. Standby is a low-priority fallback — above the baseline forward advance (REQ-SHP-COMBAT/REQ-SHP-ENEMY-AI advance) but below rally (REQ-SHP-RALLY), so it only wins when no attack, repair, salvage, rally, or retreat behavior applies. A standing-by ship navigates toward the centroid of its other same-faction ships, falling back to the centroid of its own defence stations, and holding position when it has no allies. This keeps repair ships among the allies they exist to heal instead of advancing alone into the enemy. Armed repair ships therefore still rally and depart on the normal schedule (REQ-SHP-RALLY); standby only governs them once rally no longer applies.
|
||||
- REQ-SHP-RETREAT: **Player ships retreat to the rally point (REQ-SHP-RALLY) when threatened.** A ship retreats while either condition holds: (a) its HP is below a low-HP threshold (currently 30% of its maximum HP); or (b) it has no weapon modules and an enemy ship is within its sensor range — with one exception: a weaponless ship that has at least one repair module does **not** retreat under condition (b) while a damaged friendly (player ship or player defence station, excluding itself) is within its sensor range, so it can keep repairing under fire; it retreats only when no such repair target remains in range. Condition (a) still forces a low-HP repair ship to retreat regardless of available repair targets. Retreating takes priority over the ship's other behaviors and moves it toward the rally point; the ship resumes its normal behavior once neither condition holds. Enemy ships never retreat (REQ-SHP-ENEMY-AI).
|
||||
- REQ-SHP-ENEMY-AI: **Enemy ships** — engage the closest valid target (player defence station, HQ, or player ship) within their sensor range, orbiting the engaged target at the combat orbit radius (REQ-SHP-ORBIT). If no target is in sensor range, they move toward the asteroid (leftward in world coordinates).
|
||||
- REQ-SHP-TARGET-SELECT: **Combat target selection.** Both player combat ships (REQ-SHP-COMBAT) and enemy ships (REQ-SHP-ENEMY-AI) pick which hostile to engage by scoring every valid target (an opposing-faction ship, defence station, or HQ) within sensor range and engaging the highest-scoring one. A target's score is the product of a **base desirability** and an **overclaim penalty** (REQ-SHP-TARGET-CLAIM). The base desirability is `world.toml [targeting].target_score_formula` evaluated with `x` set to the target's distance from the ship divided by the ship's maximum weapon `attack_range` (falling back to sensor range for a ship with no weapon), clamped to a minimum of 0. The default formula `1 / (1 + x)` decreases with distance, so — absent any claims — the nearest target is chosen, realizing the closest-target priority referenced by REQ-SHP-COMBAT and REQ-SHP-ENEMY-AI. A ship engages at most one target at a time; all of its weapons fire on that target subject to their own range and rate checks (REQ-SHP-FIRING).
|
||||
- REQ-SHP-TARGET-CLAIM: **Overclaim penalty.** To stop every ship from dogpiling the same hostile, each target a ship is currently engaging counts as a **claim** on that target. When scoring a candidate, its base desirability (REQ-SHP-TARGET-SELECT) is multiplied by `world.toml [targeting].overclaim_penalty_formula` evaluated with `x` set to the number of ships currently claiming that candidate — a ship never counts its own claim against the target it already holds — clamped to the range [0, 1]. The penalty is 1 (no reduction) at zero claims and decreases as claims accumulate, so heavily-claimed targets become less attractive and ships spread across the available hostiles. The default formula `max(0.5, 1 - 0.1*x)` reduces desirability by 0.1 per claim down to a floor of 0.5. Because claims reflect the previous tick's engagements, target distribution converges over successive ticks rather than instantaneously.
|
||||
|
||||
@@ -6,6 +6,7 @@ enum class BehaviorKind
|
||||
{
|
||||
None,
|
||||
Advance,
|
||||
Standby,
|
||||
Rally,
|
||||
Retreat,
|
||||
Attack,
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
// Score bands for ship-behavior evaluation. The AiSystem selection pass picks
|
||||
// the behavior with the highest score per ship; these constants define a single
|
||||
// comparable scale so the desired priority falls out:
|
||||
// Retreat > Attack > Repair / Salvage / Deliver > Rally > Advance.
|
||||
// Retreat > Attack > Repair / Salvage / Deliver > Rally > Standby > Advance.
|
||||
// Evaluators may return kInactive when their behavior does not apply this tick.
|
||||
namespace BehaviorScores
|
||||
{
|
||||
constexpr float kInactive = 0.0f;
|
||||
constexpr float kAdvance = 0.05f; // baseline fallback; always present
|
||||
constexpr float kStandby = 0.10f; // repair-capable ships; hold with the fleet
|
||||
constexpr float kRally = 0.20f;
|
||||
constexpr float kDeliver = 0.50f; // cargo full
|
||||
constexpr float kRepair = 0.55f;
|
||||
|
||||
@@ -23,6 +23,7 @@ SET(HDRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SelectedBehaviorComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/SensorRangeComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ShipIdentityComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/StandbyBehavior.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/StationBodyComponent.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/WeaponComponent.h
|
||||
PARENT_SCOPE
|
||||
|
||||
11
src/lib/ecs/component/StandbyBehavior.h
Normal file
11
src/lib/ecs/component/StandbyBehavior.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
// Fallback for ships with a repair capability: instead of charging the enemy
|
||||
// like AdvanceBehavior, the ship holds with its fleet so damaged allies stay in
|
||||
// sensor range and it can heal them. Scored just above Advance and below Rally,
|
||||
// so it only wins when no more urgent behavior applies. The executor decides the
|
||||
// destination (StandbyExecutor).
|
||||
struct StandbyBehavior
|
||||
{
|
||||
float score = 0.0f;
|
||||
};
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "RetreatBehavior.h"
|
||||
#include "SalvageScrapBehavior.h"
|
||||
#include "SelectedBehaviorComponent.h"
|
||||
#include "StandbyBehavior.h"
|
||||
#include "tracing.h"
|
||||
|
||||
namespace
|
||||
@@ -48,6 +49,7 @@ void AiSystem::tick(EntityAdmin& admin, const BuildingSystem& buildings,
|
||||
|
||||
// Phase 1: evaluators score behaviors and set their target data.
|
||||
m_advanceEvaluator.evaluate(admin);
|
||||
m_standbyEvaluator.evaluate(admin);
|
||||
m_rallyEvaluator.evaluate(admin);
|
||||
m_retreatEvaluator.evaluate(admin);
|
||||
m_attackEvaluator.evaluate(admin);
|
||||
@@ -60,6 +62,7 @@ void AiSystem::tick(EntityAdmin& admin, const BuildingSystem& buildings,
|
||||
|
||||
// Phase 3: executors run for the winning behavior.
|
||||
m_advanceExecutor.execute(admin);
|
||||
m_standbyExecutor.execute(admin);
|
||||
m_rallyExecutor.execute(admin);
|
||||
m_retreatExecutor.execute(admin);
|
||||
m_attackExecutor.execute(admin);
|
||||
@@ -85,5 +88,6 @@ void AiSystem::selectWinningBehaviors(EntityAdmin& admin)
|
||||
consider<SalvageScrapBehavior>(admin, BehaviorKind::SalvageScrap);
|
||||
consider<DeliverScrapBehavior>(admin, BehaviorKind::DeliverScrap);
|
||||
consider<RallyBehavior>(admin, BehaviorKind::Rally);
|
||||
consider<StandbyBehavior>(admin, BehaviorKind::Standby);
|
||||
consider<AdvanceBehavior>(admin, BehaviorKind::Advance);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include "RetreatExecutor.h"
|
||||
#include "SalvageScrapEvaluator.h"
|
||||
#include "SalvageScrapExecutor.h"
|
||||
#include "StandbyEvaluator.h"
|
||||
#include "StandbyExecutor.h"
|
||||
|
||||
class BuildingSystem;
|
||||
class EntityAdmin;
|
||||
@@ -38,6 +40,7 @@ private:
|
||||
void selectWinningBehaviors(EntityAdmin& admin);
|
||||
|
||||
AdvanceEvaluator m_advanceEvaluator;
|
||||
StandbyEvaluator m_standbyEvaluator;
|
||||
RallyEvaluator m_rallyEvaluator;
|
||||
RetreatEvaluator m_retreatEvaluator;
|
||||
AttackEvaluator m_attackEvaluator;
|
||||
@@ -46,6 +49,7 @@ private:
|
||||
DeliverScrapEvaluator m_deliverScrapEvaluator;
|
||||
|
||||
AdvanceExecutor m_advanceExecutor;
|
||||
StandbyExecutor m_standbyExecutor;
|
||||
RallyExecutor m_rallyExecutor;
|
||||
RetreatExecutor m_retreatExecutor;
|
||||
AttackExecutor m_attackExecutor;
|
||||
|
||||
@@ -15,6 +15,8 @@ SET(HDRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ai/RetreatExecutor.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ai/SalvageScrapEvaluator.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ai/SalvageScrapExecutor.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ai/StandbyEvaluator.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ai/StandbyExecutor.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/AiSystem.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/CombatSystem.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DynamicBodySystem.h
|
||||
@@ -43,6 +45,8 @@ SET(SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ai/RetreatExecutor.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ai/SalvageScrapEvaluator.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ai/SalvageScrapExecutor.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ai/StandbyEvaluator.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ai/StandbyExecutor.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/AiSystem.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/CombatSystem.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/DynamicBodySystem.cpp
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "SalvageScrapBehavior.h"
|
||||
#include "SelectedBehaviorComponent.h"
|
||||
#include "SensorRangeComponent.h"
|
||||
#include "StandbyBehavior.h"
|
||||
#include "Tick.h"
|
||||
#include "tracing.h"
|
||||
#include "WeaponComponent.h"
|
||||
@@ -401,6 +402,11 @@ entt::entity ShipSystem::spawn(const std::string& schematicId, int level,
|
||||
repair.orbitRadius_tiles =
|
||||
maxRepairRange * static_cast<float>(m_config.world.orbitFactor);
|
||||
m_admin.addComponent<RepairBehavior>(entity, repair);
|
||||
|
||||
// Repair-capable ships hold with the fleet (REQ-SHP-STANDBY) instead of
|
||||
// charging the enemy when no more urgent behavior applies; this applies
|
||||
// whether or not the ship also carries weapons.
|
||||
m_admin.addComponent<StandbyBehavior>(entity, StandbyBehavior{});
|
||||
}
|
||||
|
||||
return entity;
|
||||
|
||||
16
src/lib/ecs/system/ai/StandbyEvaluator.cpp
Normal file
16
src/lib/ecs/system/ai/StandbyEvaluator.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "StandbyEvaluator.h"
|
||||
|
||||
#include "BehaviorScores.h"
|
||||
#include "EntityAdmin.h"
|
||||
#include "StandbyBehavior.h"
|
||||
#include "tracing.h"
|
||||
|
||||
void StandbyEvaluator::evaluate(EntityAdmin& admin)
|
||||
{
|
||||
TRACE();
|
||||
admin.forEach<StandbyBehavior>(
|
||||
[](entt::entity /*e*/, StandbyBehavior& standby)
|
||||
{
|
||||
standby.score = BehaviorScores::kStandby;
|
||||
});
|
||||
}
|
||||
12
src/lib/ecs/system/ai/StandbyEvaluator.h
Normal file
12
src/lib/ecs/system/ai/StandbyEvaluator.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
class EntityAdmin;
|
||||
|
||||
// Constant low-priority fallback for repair-capable ships: gives a fixed score
|
||||
// just above Advance so a repair ship with nothing more urgent to do holds with
|
||||
// its fleet (StandbyExecutor) instead of charging the enemy.
|
||||
class StandbyEvaluator
|
||||
{
|
||||
public:
|
||||
void evaluate(EntityAdmin& admin);
|
||||
};
|
||||
101
src/lib/ecs/system/ai/StandbyExecutor.cpp
Normal file
101
src/lib/ecs/system/ai/StandbyExecutor.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
#include "StandbyExecutor.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <QVector2D>
|
||||
|
||||
#include "BehaviorKind.h"
|
||||
#include "EntityAdmin.h"
|
||||
#include "FactionComponent.h"
|
||||
#include "HealthComponent.h"
|
||||
#include "MovementIntentComponent.h"
|
||||
#include "PositionComponent.h"
|
||||
#include "SelectedBehaviorComponent.h"
|
||||
#include "ShipIdentityComponent.h"
|
||||
#include "StandbyBehavior.h"
|
||||
#include "StationBodyComponent.h"
|
||||
#include "tracing.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
// Accumulates positions to produce their centroid (the center between them).
|
||||
struct Centroid
|
||||
{
|
||||
QVector2D sum;
|
||||
int count = 0;
|
||||
|
||||
void add(const QVector2D& point)
|
||||
{
|
||||
sum += point;
|
||||
count += 1;
|
||||
}
|
||||
|
||||
std::optional<QVector2D> value() const
|
||||
{
|
||||
if (count == 0) { return std::nullopt; }
|
||||
return sum / static_cast<float>(count);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void StandbyExecutor::execute(EntityAdmin& admin)
|
||||
{
|
||||
TRACE();
|
||||
|
||||
// Centroid of each faction's alive ships; a standing-by ship steers toward the
|
||||
// center of its other same-faction ships so it stays among potential patients.
|
||||
Centroid enemyShips;
|
||||
Centroid playerShips;
|
||||
admin.forEach<ShipIdentityComponent, PositionComponent, FactionComponent, HealthComponent>(
|
||||
[&enemyShips, &playerShips](entt::entity /*e*/, const ShipIdentityComponent& /*si*/,
|
||||
const PositionComponent& pos, const FactionComponent& faction,
|
||||
const HealthComponent& health)
|
||||
{
|
||||
if (health.hp <= 0.0f) { return; }
|
||||
Centroid& centroid = faction.isEnemy ? enemyShips : playerShips;
|
||||
centroid.add(pos.value);
|
||||
});
|
||||
|
||||
// Fallback per faction: the centroid of that side's own alive defence stations.
|
||||
Centroid enemyStations;
|
||||
Centroid playerStations;
|
||||
admin.forEach<StationBodyComponent, PositionComponent, FactionComponent, HealthComponent>(
|
||||
[&enemyStations, &playerStations](entt::entity /*e*/,
|
||||
const StationBodyComponent& /*sb*/, const PositionComponent& pos,
|
||||
const FactionComponent& faction, const HealthComponent& health)
|
||||
{
|
||||
if (health.hp <= 0.0f) { return; }
|
||||
Centroid& centroid = faction.isEnemy ? enemyStations : playerStations;
|
||||
centroid.add(pos.value);
|
||||
});
|
||||
|
||||
const std::optional<QVector2D> enemyStationCenter = enemyStations.value();
|
||||
const std::optional<QVector2D> playerStationCenter = playerStations.value();
|
||||
|
||||
admin.forEach<StandbyBehavior, SelectedBehaviorComponent, PositionComponent,
|
||||
FactionComponent, MovementIntentComponent>(
|
||||
[&](entt::entity /*e*/, const StandbyBehavior& /*standby*/,
|
||||
const SelectedBehaviorComponent& selected, const PositionComponent& pos,
|
||||
const FactionComponent& faction, MovementIntentComponent& intent)
|
||||
{
|
||||
if (selected.winner != BehaviorKind::Standby) { return; }
|
||||
|
||||
const Centroid& allyShips = faction.isEnemy ? enemyShips : playerShips;
|
||||
const std::optional<QVector2D>& friendlyStationCenter =
|
||||
faction.isEnemy ? enemyStationCenter : playerStationCenter;
|
||||
|
||||
// Aim at the centroid of the other allied ships (excluding self), then
|
||||
// fall back to the friendly stations, then hold position when alone.
|
||||
QVector2D target = pos.value;
|
||||
if (allyShips.count > 1)
|
||||
{
|
||||
target = (allyShips.sum - pos.value)
|
||||
/ static_cast<float>(allyShips.count - 1);
|
||||
}
|
||||
else if (friendlyStationCenter)
|
||||
{
|
||||
target = *friendlyStationCenter;
|
||||
}
|
||||
intent = MovementIntentComponent{true, target};
|
||||
});
|
||||
}
|
||||
12
src/lib/ecs/system/ai/StandbyExecutor.h
Normal file
12
src/lib/ecs/system/ai/StandbyExecutor.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
class EntityAdmin;
|
||||
|
||||
// Moves a standing-by ship toward the centroid of its other same-faction ships
|
||||
// (its fleet) so it stays among allies that may need repair, falling back to its
|
||||
// own defence stations and then to holding position when it has no allies.
|
||||
class StandbyExecutor
|
||||
{
|
||||
public:
|
||||
void execute(EntityAdmin& admin);
|
||||
};
|
||||
@@ -563,6 +563,33 @@ TEST_CASE("BehaviorSystem: repair ship orbits damaged friendly ship",
|
||||
REQUIRE(intent(f.admin, repairShip).orbitRadius_tiles == Approx(orbitRadius));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// StandbyBehavior (repair ships hold with the fleet when idle)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
TEST_CASE("BehaviorSystem: idle repair ship stands by with the fleet instead of charging the enemy",
|
||||
"[behavior]")
|
||||
{
|
||||
Fixture f;
|
||||
const ShipLayoutConfig repairLayout = makeSingleModuleLayout("repair_tool");
|
||||
const entt::entity repairShip = f.ships.spawn("repair_ship", 1, QVector2D(0.0f, 0.0f),
|
||||
false, repairLayout);
|
||||
// A healthy ally (nothing to repair) and a far enemy station (no threat in range).
|
||||
const entt::entity ally = f.ships.spawn("interceptor", 1, QVector2D(50.0f, 0.0f));
|
||||
const std::vector<QPoint> body{QPoint(0, 0)};
|
||||
f.admin.spawnStation(QPoint(1000, 0), QSize(1, 1), body, 100.0f, 100.0f, /*isEnemy=*/true);
|
||||
|
||||
f.decide();
|
||||
|
||||
// With no damaged ally and no enemy in sensor range the repair ship neither
|
||||
// repairs nor retreats: it stands by (REQ-SHP-STANDBY), steering toward its
|
||||
// fleet (the ally) rather than charging the distant enemy station.
|
||||
REQUIRE(winnerOf(f.admin, repairShip) == BehaviorKind::Standby);
|
||||
REQUIRE(intent(f.admin, repairShip).active);
|
||||
REQUIRE(intent(f.admin, repairShip).target.x() == Approx(pos(f.admin, ally).value.x()));
|
||||
REQUIRE(intent(f.admin, repairShip).target.y() == Approx(pos(f.admin, ally).value.y()));
|
||||
}
|
||||
|
||||
TEST_CASE("BehaviorSystem: repair ship heals damaged ally within repair range",
|
||||
"[behavior]")
|
||||
{
|
||||
@@ -1114,8 +1141,11 @@ TEST_CASE("SensorRange: repair ship does not retreat from enemy beyond sensor ra
|
||||
|
||||
f.decide();
|
||||
|
||||
// Beyond sensor range the enemy is no threat, so the repair ship does not flee;
|
||||
// with nothing to repair it holds with the fleet (REQ-SHP-STANDBY) rather than
|
||||
// retreating or charging the enemy.
|
||||
REQUIRE(winnerOf(f.admin, repairShip) != BehaviorKind::Retreat);
|
||||
REQUIRE(intent(f.admin, repairShip).target.x() > pos(f.admin, repairShip).value.x());
|
||||
REQUIRE(winnerOf(f.admin, repairShip) == BehaviorKind::Standby);
|
||||
}
|
||||
|
||||
TEST_CASE("SensorRange: repair ship does not acquire damaged ally beyond sensor range", "[sensor]")
|
||||
|
||||
Reference in New Issue
Block a user