implement ships depart in waves

This commit is contained in:
2026-04-27 21:31:04 +02:00
parent 7b67093540
commit ed6b503767
10 changed files with 78 additions and 11 deletions

View File

@@ -62,6 +62,11 @@ struct HomeReturn
QVector2D homePos;
};
struct RallyBehavior
{
QVector2D rallyPoint;
};
// ---------------------------------------------------------------------------
// Ship
// ---------------------------------------------------------------------------
@@ -86,6 +91,7 @@ struct Ship
std::optional<ScrapCollector> scrapCollector;
std::optional<RepairBehavior> repairBehavior;
std::optional<HomeReturn> homeReturn;
std::optional<RallyBehavior> rallyBehavior;
// Cleared at the start of the behavior step each tick; the highest-priority
// write from behavior systems wins (architecture.md §Movement Arbitration).

View File

@@ -64,6 +64,11 @@ EntityId ShipSystem::spawn(const std::string& schematicId, int level, QVector2D
ThreatResponse tr;
tr.engagementRange = w.range;
ship.threatResponse = tr;
if (!isEnemy)
{
ship.rallyBehavior = RallyBehavior{m_rallyPoint};
}
}
if (def->salvage)
@@ -271,11 +276,18 @@ void ShipSystem::tickThreatResponse(const BuildingSystem& buildings)
}
else
{
// No target: patrol rightward (aggressive).
// No target: gather at rally point or patrol rightward once departed.
if (3 > s.intent.priority)
{
s.intent = MovementIntent{3, QVector2D(s.position.x() + 1000.0f,
s.position.y())};
if (s.rallyBehavior)
{
s.intent = MovementIntent{3, s.rallyBehavior->rallyPoint};
}
else
{
s.intent = MovementIntent{3, QVector2D(s.position.x() + 1000.0f,
s.position.y())};
}
}
}
}
@@ -636,6 +648,26 @@ void ShipSystem::tickScrapCollector(ScrapSystem& scraps, const BuildingSystem& b
}
}
// ---------------------------------------------------------------------------
// Rally point management (REQ-SHP-RALLY)
// ---------------------------------------------------------------------------
void ShipSystem::setRallyPoint(QVector2D point)
{
m_rallyPoint = point;
}
void ShipSystem::triggerRallyDeparture()
{
for (Ship& s : m_ships)
{
if (!s.isEnemy)
{
s.rallyBehavior = std::nullopt;
}
}
}
// ---------------------------------------------------------------------------
// tickMovement (tick-order step 10)
// ---------------------------------------------------------------------------

View File

@@ -47,6 +47,12 @@ public:
// -- Movement (tick-order step 10) ---------------------------------------
void tickMovement();
// Set the rally point that newly spawned player combat ships will loiter at.
void setRallyPoint(QVector2D point);
// Release all gathered player combat ships to advance toward the enemy.
void triggerRallyDeparture();
// Reduce ship HP by amount. Does not remove the ship; step 9 handles death.
// Returns false if ship not found.
bool damageShip(EntityId id, float amount);
@@ -66,4 +72,5 @@ private:
const GameConfig& m_config;
std::function<EntityId()> m_allocateId;
std::vector<Ship> m_ships;
QVector2D m_rallyPoint;
};

View File

@@ -13,6 +13,7 @@ Simulation::Simulation(GameConfig config, unsigned int seed)
: m_config(std::move(config))
, m_rng(seed)
, m_currentTick(0)
, m_nextDepartureTick(secondsToTicks(m_config.world.departureIntervalSeconds))
, m_nextId(1)
, m_buildingBlocksStock(m_config.world.startingBuildingBlocks)
, m_gameOver(false)
@@ -73,6 +74,7 @@ void Simulation::reset(unsigned int seed)
{
m_rng.seed(seed);
m_currentTick = 0;
m_nextDepartureTick = secondsToTicks(m_config.world.departureIntervalSeconds);
m_nextId = 1;
m_buildingBlocksStock = m_config.world.startingBuildingBlocks;
m_gameOver = false;
@@ -139,6 +141,14 @@ void Simulation::tick()
m_beltSystem.tick(); // step 6
// Step 7: ship behavior systems (movement arbitration via intent priority)
// Departure timer: release gathered combat ships on a fixed interval (REQ-SHP-RALLY).
if (m_currentTick >= m_nextDepartureTick)
{
m_shipSystem->triggerRallyDeparture();
m_nextDepartureTick += secondsToTicks(m_config.world.departureIntervalSeconds);
}
m_shipSystem->clearMovementIntents();
m_shipSystem->tickHomeReturn(); // priority 4
m_shipSystem->tickThreatResponse(*m_buildingSystem); // priority 3
@@ -217,6 +227,11 @@ void Simulation::placeInitialStructures()
QPoint(psAnchorX, ps2Y), Rotation::East, psHp, psHp);
m_buildingSystem->initStationWeapon(m_playerStation2Id, psWeapon);
// Rally point: center of the player defence stations' X column, world vertical midpoint.
const float rallyX = static_cast<float>(psAnchorX) + psParsed.footprint.width() / 2.0f;
const float rallyY = static_cast<float>(m_config.world.heightTiles) / 2.0f;
m_shipSystem->setRallyPoint(QVector2D(rallyX, rallyY));
// Enemy defence stations — generation 0 (initial set).
placeEnemyStationSet(0);
}

View File

@@ -92,6 +92,7 @@ private:
std::mt19937 m_rng;
Tick m_currentTick;
Tick m_nextDepartureTick;
EntityId m_nextId;
int m_buildingBlocksStock;
bool m_gameOver = false;