implement ships depart in waves
This commit is contained in:
@@ -1,28 +1,29 @@
|
||||
[world]
|
||||
height_tiles = 60
|
||||
height_tiles = 30
|
||||
refund_percentage = 75
|
||||
starting_building_blocks = 100
|
||||
starting_building_blocks = 1000
|
||||
scrap_despawn_seconds = 30
|
||||
belt_speed_tiles_per_second = 2
|
||||
tunnel_max_distance = 10
|
||||
departure_interval_seconds = 20
|
||||
|
||||
[regions]
|
||||
asteroid_width = 40
|
||||
player_buffer_width = 10
|
||||
contest_zone_width = 30
|
||||
enemy_buffer_width = 15
|
||||
player_buffer_width = 20
|
||||
contest_zone_width = 60
|
||||
enemy_buffer_width = 20
|
||||
|
||||
[expansion]
|
||||
columns_per_expansion = 10
|
||||
cost_building_blocks = 200
|
||||
|
||||
[push]
|
||||
push_expand_columns = 20
|
||||
push_expand_columns = 10
|
||||
scaling_factor = 1.2
|
||||
|
||||
[waves]
|
||||
threat_rate_formula = "1*x - 30"
|
||||
ship_level_formula = "1 + x / 120"
|
||||
threat_rate_formula = "0.1*x - 60"
|
||||
ship_level_formula = "1"
|
||||
gap_min_seconds = 15
|
||||
gap_max_seconds = 45
|
||||
spawn_duration_seconds = 10
|
||||
|
||||
@@ -225,6 +225,7 @@ WorldConfig ConfigLoader::loadWorld(const std::string& path)
|
||||
cfg.scrapDespawnSeconds = requireDouble(tbl["world"]["scrap_despawn_seconds"], file, "world.scrap_despawn_seconds");
|
||||
cfg.beltSpeedTilesPerSecond = requireDouble(tbl["world"]["belt_speed_tiles_per_second"], file, "world.belt_speed_tiles_per_second");
|
||||
cfg.tunnelMaxDistance = static_cast<int>(requireInt(tbl["world"]["tunnel_max_distance"], file, "world.tunnel_max_distance"));
|
||||
cfg.departureIntervalSeconds = requireDouble(tbl["world"]["departure_interval_seconds"], file, "world.departure_interval_seconds");
|
||||
|
||||
cfg.regions.asteroidWidth = static_cast<int>(requireInt(tbl["regions"]["asteroid_width"], file, "regions.asteroid_width"));
|
||||
cfg.regions.playerBufferWidth = static_cast<int>(requireInt(tbl["regions"]["player_buffer_width"], file, "regions.player_buffer_width"));
|
||||
|
||||
@@ -43,6 +43,7 @@ struct WorldConfig
|
||||
double scrapDespawnSeconds; // REQ-RES-SCRAP-DROP
|
||||
double beltSpeedTilesPerSecond; // REQ-GW-BELT-SPEED
|
||||
int tunnelMaxDistance; // REQ-BLD-TUNNEL-PAIR
|
||||
double departureIntervalSeconds; // REQ-SHP-RALLY
|
||||
|
||||
WorldRegions regions;
|
||||
WorldExpansion expansion;
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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,14 +276,21 @@ 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)
|
||||
{
|
||||
if (s.rallyBehavior)
|
||||
{
|
||||
s.intent = MovementIntent{3, s.rallyBehavior->rallyPoint};
|
||||
}
|
||||
else
|
||||
{
|
||||
s.intent = MovementIntent{3, QVector2D(s.position.x() + 1000.0f,
|
||||
s.position.y())};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Enemy ship: target nearest player ship or player building.
|
||||
@@ -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)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -165,6 +165,7 @@ scrap_despawn_seconds = 30
|
||||
belt_speed_tiles_per_second = 2
|
||||
starting_building_blocks = 100
|
||||
tunnel_max_distance = 10
|
||||
departure_interval_seconds = 20
|
||||
|
||||
[regions]
|
||||
asteroid_width = 40
|
||||
@@ -211,6 +212,7 @@ scrap_despawn_seconds = 30
|
||||
belt_speed_tiles_per_second = 2
|
||||
starting_building_blocks = 100
|
||||
tunnel_max_distance = 10
|
||||
departure_interval_seconds = 20
|
||||
|
||||
[regions]
|
||||
asteroid_width = 40
|
||||
|
||||
@@ -5,6 +5,7 @@ starting_building_blocks = 100
|
||||
scrap_despawn_seconds = 30
|
||||
belt_speed_tiles_per_second = 2
|
||||
tunnel_max_distance = 10
|
||||
departure_interval_seconds = 20
|
||||
|
||||
[regions]
|
||||
asteroid_width = 40
|
||||
|
||||
Reference in New Issue
Block a user