requirements iteration

This commit is contained in:
2026-04-18 22:19:12 +02:00
parent 5bd97ddccd
commit 1796d3771c
2 changed files with 82 additions and 26 deletions

View File

@@ -14,7 +14,7 @@ This document captures the architectural decisions for the project. It is a comp
A strict separation between the game simulation and the Qt Widgets UI.
- The **simulation** is a pure C++ library that depends only on Qt Core (QPoint, QVector3D, QRect, etc., as required by the coding guidelines), toml++, and tinyexpr. It contains no QtWidgets, no painting, and no QApplication.
- The **simulation** is a pure C++ library that depends only on Qt Core (QPoint, QVector2D, QRect, etc., as required by the coding guidelines), toml++, and tinyexpr. It contains no QtWidgets, no painting, and no QApplication.
- The **UI** reads simulation state and renders it. It owns all widgets, painting, and input handling, and drives the simulation via a small command interface (place building, demolish, clear belt tiles, change recipe, set game speed, etc.).
This split is enforced at the CMake target level (see below). Tests link only against the simulation library and run without a display server.
@@ -23,7 +23,7 @@ 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 (30 or 60 Hz, TBD; choose once and document here).
- 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
@@ -42,6 +42,44 @@ Config files (`world.toml`, `buildings.toml`, `recipes.toml`, `ships.toml`, `sta
- Formula strings (e.g., threat accumulation, enemy ship level as a function of `t`, per-ship stats as functions of `level`, station stats as functions of generation) are compiled once via `tinyexpr` at load time and stored as callable objects. They are never re-parsed during simulation.
- Configs are immutable after load. Any formula that fails to parse, or any required field that is missing or malformed, aborts startup with a clear error message — never mid-game.
## Coordinate System
See REQ-GW-COORDS for the authoritative tile-coordinate convention. This section captures the programming-level conventions that follow from it.
- Tile coordinates are `QPoint(x, y)`. Origin `(0, 0)` is the first space tile (just right of the asteroid's right edge at game start). X grows right; Y grows down.
- Asteroid tiles have `x < 0`. Asteroid left-expansions add tiles at increasingly negative X; the origin never shifts, so existing tile coordinates remain stable across expansions.
- Continuous world positions (ship centers, scrap drops, projectiles) use `QVector2D` in tile units — one tile = 1.0 world unit. A ship center at `QVector2D(-3.5, 4.0)` sits at the center of the tile 3.5 tiles left of the asteroid's right edge and 4 tiles down from the top.
- Rendering multiplies world units by the tile size in pixels (20) at draw time.
- Ship position always refers to the ship's center — this is the point used for sensor, attack-range, and hit-detection checks.
## Core Types
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`.
- `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).
- `Item``struct Item { ItemType type; }`. Items on belts have no persistent identity across ticks.
- `Port``struct Port { QPoint tile; Rotation direction; }`. Identifies a belt-adjacent cell and the direction of flow across that cell.
- `MovementIntent``struct MovementIntent { int priority; QVector2D target; }`. Priority follows the order declared under Movement Arbitration. Cleared at the start of each tick; the highest-priority write wins; `tickMovement` reads the winner.
## Tick Order
Within a single simulation tick, subsystems run in this fixed order. The order is load-bearing for determinism and for avoiding one-tick-delay artifacts (e.g., items landing on a belt but not advancing in the same tick).
1. **Wave scheduler** — advance wave timer; on trigger, compute wave composition per REQ-WAV-TRIGGER and schedule spawn times across REQ-WAV-SPAWN-DURATION; spawn any enemy ships whose scheduled time has arrived this tick.
2. **Threat accumulation** — add `max(0, threat_rate_formula(t))` × tick_dt to threat level (REQ-WAV-THREAT-RATE).
3. **Belt → building pull** — buildings drain eligible items from adjacent belt tiles into per-material input buffers (REQ-MAT-INPUT-PORTS).
4. **Building production** — advance production timers; start new cycles when inputs and output-buffer space permit (REQ-MAT-CYCLE); on completion, deposit output.
5. **Building → belt push** — buildings push items from output buffer onto the belt tile at their output port (REQ-MAT-OUTPUT-PORT).
6. **Belt tick** — advance items along belt tiles; apply splitter routing (REQ-BLD-SPLITTER).
7. **Ship behavior systems** — clear `MovementIntent` on each ship, then run `tickThreatResponse`, `tickScrapCollector`, `tickRepairBehavior`, `tickHomeReturn` in any order (arbitration is via intent priority).
8. **Combat resolution** — ships and defence stations acquire targets, fire, apply damage; queue deaths.
9. **Deaths & loot** — process queued deaths: drop scrap (REQ-RES-SCRAP-DROP), drop blueprints (REQ-DEF-BLUEPRINT-DROP), remove entities.
10. **`tickMovement`** — advance ship positions based on final `MovementIntent`.
11. **Scrap despawn** — decrement scrap timers; remove expired scrap (REQ-RES-SCRAP-DROP).
## CMake Target Layout
Three product targets plus tests:
@@ -133,10 +171,10 @@ Behaviors are decomposed, not bundled into per-role monolithic AIs. This is the
struct ThreatResponse { float engagementRange; CombatStance stance;
CombatTargetPriority priority;
std::optional<EntityId> currentTarget; };
struct ScrapCollector { std::optional<QVector3D> scrapTarget; EntityId deliveryBay; };
struct ScrapCollector { std::optional<QVector2D> scrapTarget; EntityId deliveryBay; };
struct RepairBehavior { RepairTargetPriority priority;
std::optional<EntityId> currentTarget; };
struct HomeReturn { float retreatHpFraction; QVector3D homePos; };
struct HomeReturn { float retreatHpFraction; QVector2D homePos; };
```
### Ship
@@ -144,8 +182,8 @@ struct HomeReturn { float retreatHpFraction; QVector3D homePos; };
```cpp
struct Ship {
EntityId id;
QVector3D position;
QVector3D velocity;
QVector2D position;
QVector2D velocity;
float hp;
float maxHp;
int level;