requirements clarifications

This commit is contained in:
2026-04-18 23:07:22 +02:00
parent 8d4fece87d
commit f29e2ba235
2 changed files with 30 additions and 15 deletions

View File

@@ -23,16 +23,11 @@ This split is enforced at the CMake target level (see below). Tests link only ag
The simulation advances in discrete ticks. All game quantities — production timers, belt item progress, threat accumulation, wave timers, ship cooldowns — are measured in ticks, not wall-clock seconds.
- Tick rate: fixed at 30 Hz.
- The outer loop advances `N` ticks per wall-clock frame, where `N` is derived from the selected game speed:
- 0× → 0 ticks/frame (pause)
- 0.5× → one tick every two frames
- 1× → one tick/frame
- 2× → two ticks/frame
- 4× → four ticks/frame
- Tick rate: fixed at 30 Hz; `tickDurationMs = 1000 / 30 ≈ 33.33`.
- Ticks are driven by an accumulator that is independent of the render rate. Each render frame, the driver adds `elapsedWallMs × gameSpeedMultiplier` to an accumulator and flushes one `tick()` per `tickDurationMs` of accumulated time (so multiple sim ticks may run between frames at high speeds, or a frame may run no ticks at low speeds). `gameSpeedMultiplier` ∈ {0, 0.5, 1, 2, 4} per REQ-UI-SPEED; 0× freezes the accumulator (pause). The concrete driver lives in the Rendering section.
- Config-level durations given in seconds (recipe durations, wave gap ranges, scrap despawn, etc.) are converted to ticks at config-load time.
Consequences: determinism, replayability, and the time-scale feature falls out for free.
Consequences: determinism, replayability, and the time-scale feature fall out for free. The simulation advances the same number of ticks over the same amount of game-time regardless of whether the game renders at 60 FPS, 30 FPS, or a stuttery mix.
## Config Loading
@@ -56,7 +51,7 @@ See REQ-GW-COORDS for the authoritative tile-coordinate convention. This section
Simulation types shared across subsystems:
- `EntityId` — strictly increasing integer handle, allocated centrally by the simulation. Used for ships and scrap drops. Buildings are addressed by their anchor tile, not by `EntityId`.
- `EntityId` — strictly increasing integer handle, allocated centrally by the simulation. Assigned to every targetable entity: ships, scrap drops, **and** buildings (including HQ and defence stations). Buildings additionally retain their anchor tile for spatial lookups and placement; the `EntityId` is the canonical reference used by ship-component target fields (`Weapon.currentTarget`, `RepairTool.currentTarget`, `ThreatResponse.currentTarget`, etc.), so a combat ship can target either another ship or a defence station uniformly.
- `Rotation` — enum `{ North, East, South, West }`. The rotation applied to a building's surface_mask when placed.
- `BuildingType` — enum covering every building type in requirements.md (Miner, Smelter, Assembler, ReprocessingPlant, Shipyard, SalvageBay, Belt, Splitter, Hq, PlayerDefenceStation, EnemyDefenceStation).
- `ItemType` — tagged id of every transportable material (ores, ingots, intermediates, building_blocks, scrap).
@@ -152,13 +147,17 @@ Buildings are plain structs with a fixed type determined at construction.
```cpp
struct Building {
EntityId id;
QPoint tile;
QSize footprint;
Rotation rotation;
BuildingType type;
float hp; // relevant for HQ and defence stations; ignored otherwise.
float maxHp;
InputBuffer inputBuffer;
OutputBuffer outputBuffer;
// Production timer and current recipe, where applicable.
// Production timer, the recipe currently running, and — for reprocessing
// plants — the output item picked at cycle start (REQ-MAT-CYCLE).
std::optional<Production> production;
};
```
@@ -167,6 +166,21 @@ struct Building {
- Belts and splitters are separate types owned by the belt subsystem, not general `Building` instances.
- No ECS for buildings. A miner is never also an assembler; there is no composition benefit to decomposing buildings into components.
## Scrap
Scrap is the only non-ship, non-building entity in the simulation:
```cpp
struct Scrap {
EntityId id;
QVector2D position; // world units, tile-fractional; ship-center convention
int amount;
Tick despawnAt; // absolute tick at which the scrap is removed
};
```
Created in tick step 9 (Deaths & loot) per REQ-RES-SCRAP-DROP, consumed by salvage ships in tick step 7 (ScrapCollector), and removed in tick step 11 when the current tick reaches `despawnAt`.
## Ships
Ships follow a component-composition model using `std::optional<Component>` members. Each orthogonal capability is a component; each behavior is also a component, ticked by its own system. A ship's "role" is just which components it has — not a class or an enum.