From b59e392461ffdda897860d48cd88dab9ffab0015 Mon Sep 17 00:00:00 2001 From: mlangkabel Date: Sun, 17 May 2026 22:51:44 +0200 Subject: [PATCH] add requirements for ship modules --- docs/requirements.md | 73 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/docs/requirements.md b/docs/requirements.md index ad58d4f..03cb0e6 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -7,7 +7,8 @@ Config files use the TOML format. The following config files drive game paramete - **world.toml** — world dimensions, region widths, expansion amounts, building refund percentage, wave timing, enemy ship level formula, belt speed, starting building blocks, departure interval. - **buildings.toml** — building block cost and construction time per building type. - **recipes.toml** — crafting recipes: inputs, outputs, quantities, durations, and reprocessing plant probabilities. -- **ships.toml** — per schematic: a human-readable display name (used in toasts and UI), ship stats (HP, speed, damage, attack range, attack rate, sensor range) as formulas of ship level, required build materials, threat cost formula, player production level, and whether the schematic is available from game start. +- **ships.toml** — per schematic: a human-readable display name (used in toasts and UI), ship stats (HP, speed, damage, attack range, attack rate, sensor range) as formulas of ship level, required build materials, threat cost formula, player production level, whether the schematic is available from game start, and a layout grid defining the ship's module slots. +- **modules.toml** — per module type: id, surface mask, materials list, player production level, production time, threat cost, fill color, glyph, and stat modifier formulas (additive and/or multiplicative per stat). - **stations.toml** — HP, damage, range, fire rate, and scrap drop for player and enemy defence stations, defined as formulas of station level. - **visuals.toml** — rendering-only config (not game parameters): fill and outline colors and glyphs for every building type, item type, ship role, and station type; beam color and width; overlay and toast colors. Loaded by the UI at startup; the simulation does not read it. @@ -27,6 +28,22 @@ Buildings in buildings.toml define a `surface_mask` — a list of strings that d Output port indicators are not building tiles themselves. A building may have more than one output port (e.g. a splitter uses `` to declare both a left and a right output on the same tile). +### Ship Layout Format + +Ships in `ships.toml` define a `layout` — a list of strings that describes the ship's module grid. Each character occupies one cell: + +- `O` — buildable cell; modules can be placed here. +- `X` — non-buildable cell; not part of the ship's interior. + +The layout grid determines which cells are available for module placement in the layout configuration dialog. + +### Module Surface Mask Format + +Modules in `modules.toml` define a `surface_mask` — a list of strings that describes the module's tile footprint within the ship layout grid. Each character occupies one cell: + +- `O` — module cell: must be placed on an unoccupied buildable cell (`O`) of the ship's layout. +- `X` — ignored cell: may overlap any cell (non-buildable, unoccupied buildable, or occupied buildable) or extend outside the layout grid entirely. + ## Game World - REQ-GW-COORDS: Tile coordinates are integer `(x, y)`. The origin `(0, 0)` is the first column of space — the tile immediately to the right of the asteroid's right edge at game start, at the top of the world. X grows right; Y grows down. All asteroid tiles have `x < 0`; asteroid left-expansions add tiles at increasingly negative X. The origin never shifts. @@ -73,7 +90,7 @@ Output port indicators are not building tiles themselves. A building may have mo - REQ-BLD-SMELTER: **Smelter** (2×2): Converts ore or scrap into basic materials. No recipe selection required. Inputs, outputs, and rates are defined in `recipes.toml [[recipe]]` entries with `building = "smelter"`. - REQ-BLD-ASSEMBLER: **Assembler** (3×3): The player selects a recipe from the config-defined crafting tree. Produces the selected output item at the rate defined in the corresponding `recipes.toml [[recipe]]` entry with `building = "assembler"`. - REQ-BLD-REPROCESSING: **Reprocessing Plant** (3×3): Consumes scrap per cycle and produces exactly one higher-level intermediate product per cycle via weighted random pick. The input quantity, possible output items, per-output weights, and amounts are defined in `recipes.toml [[recipe]]` entries with `building = "reprocessing_plant"` (`inputs`, `outputs[].item`, `outputs[].amount`, `outputs[].weight`). Weights are normalized at load time; their sum does not need to equal 1. The output is rolled at cycle start (see REQ-MAT-CYCLE). The output buffer holds at most one cycle's output — see REQ-MAT-OUTPUT-BUFFER-REPROCESSING. -- REQ-BLD-SHIPYARD: **Shipyard** (4×2): The player selects a schematic. When all required materials (`[ship.schematic].materials`) are present in its input buffer, the shipyard consumes them and begins a production cycle lasting `[ship.schematic].production_time_seconds` seconds (read from `ships.toml`). One ship of that type is spawned at `ships.toml [ship.schematic].player_production_level` (initial value 5, incremented by duplicate schematic drops per REQ-DEF-SCHEMATIC-DROP) when the cycle completes. The shipyard cannot start a new cycle while one is in progress. +- REQ-BLD-SHIPYARD: **Shipyard** (4×2): The player selects a schematic. When all required materials — the ship's base materials (`[ship.schematic].materials`) plus the materials of all modules in the configured layout (REQ-MOD-MATERIALS) — are present in its input buffer, the shipyard consumes them and begins a production cycle lasting the ship's base `[ship.schematic].production_time_seconds` plus the sum of production times contributed by all module instances in the configured layout (REQ-MOD-PRODUCTION-TIME). One ship of that type is spawned at `ships.toml [ship.schematic].player_production_level` (initial value 5, incremented by duplicate schematic drops per REQ-DEF-SCHEMATIC-DROP) with the configured modules when the cycle completes. The shipyard cannot start a new cycle while one is in progress. If the player confirms a layout change (REQ-MOD-UI-DIALOG) while a production cycle is in progress, the current cycle is cancelled and all consumed materials are discarded; the shipyard returns to idle with the new layout configuration. - REQ-BLD-SALVAGE-BAY: **Salvage Bay** (3×2): A dedicated drop-off point for salvage ships. Scrap delivered here is placed onto connected output belts. - REQ-BLD-BELT: **Belt** (1×1): Transports items. A belt tile has one direction (N, S, E, W) set at placement (modified by rotation). Curved belts are auto-derived: when a belt tile's outgoing direction leads into another belt whose direction is orthogonal, the downstream belt is rendered and behaves as a curve. Belt speed is defined in `world.toml [world].belt_speed_tiles_per_second` (REQ-GW-BELT-SPEED). - REQ-BLD-SPLITTER: **Splitter** (1×1): Distributes incoming items between two output directions. Each output can optionally have a filter (a list of item types), configurable via the selected building panel. Routing rules: @@ -111,7 +128,7 @@ Output port indicators are not building tiles themselves. A building may have mo ## Ships - REQ-SHP-AUTONOMOUS: Ships are produced by shipyards and are fully autonomous once produced. -- REQ-SHP-STATS: All ship stats are defined as formulas of ship level in `ships.toml`: HP (`[ship.health].hp_formula`), speed (`[ship.movement].speed_formula`), damage (`[ship.combat].damage_formula`), attack range (`[ship.combat].attack_range_formula`), attack rate (`[ship.combat].attack_rate_formula`), sensor range (`[ship.sensors].range_formula`). Required build materials (`[ship.schematic].materials`) and availability from game start (`[[ship]].available_from_start`) are also defined there. +- REQ-SHP-STATS: Base ship stats are defined as formulas of ship level in `ships.toml`: HP (`[ship.health].hp_formula`), speed (`[ship.movement].speed_formula`), damage (`[ship.combat].damage_formula`), attack range (`[ship.combat].attack_range_formula`), attack rate (`[ship.combat].attack_rate_formula`), sensor range (`[ship.sensors].range_formula`). Required build materials (`[ship.schematic].materials`) and availability from game start (`[[ship]].available_from_start`) are also defined there. Final ship stats incorporate module modifiers per REQ-MOD-STAT-CALC. - REQ-SHP-SPAWN-PLAYER: A ship produced by a shipyard spawns centered on the shipyard's output port tile. - REQ-SHP-SPAWN-ENEMY: Enemy ships spawn at a uniformly random position within the current enemy buffer zone — random X across the buffer's width and random Y across the world height. - REQ-SHP-MOVEMENT: Ships move in straight lines toward their current destination at the speed defined by their speed formula. Ship position refers to the ship's center for all range, sensor, and attack checks. @@ -129,6 +146,53 @@ Output port indicators are not building tiles themselves. A building may have mo - REQ-SHP-ENEMY-AI: **Enemy ships** — engage the closest valid target (player defence station, HQ, or player ship) within their sensor range. If no target is in sensor range, they move toward the asteroid (leftward in world coordinates). - REQ-SHP-SCHEMATICS: The player selects a schematic per shipyard by clicking it. New schematics are unlocked automatically when an enemy defence station set is destroyed (REQ-DEF-SCHEMATIC-DROP) — there is no physical loot to collect. +## Ship Modules + +### Module Configuration + +- REQ-MOD-CONFIG: Module types are defined in `modules.toml`. Each module entry specifies: + - `id` — unique identifier, also used as the display name in the UI. + - `surface_mask` — footprint within the ship layout grid (see Module Surface Mask Format). + - `materials` — list of materials required per instance (added to the ship's build cost). + - `player_production_level` — fixed level for this module type; used as `x` in its stat modifier formulas. + - `production_time_seconds` — time added to the ship's production cycle per instance. + - `threat_cost` — threat cost added to the ship's threat cost per instance. + - `fill_color` — fill color used to render this module's cells in the layout grid. + - `glyph` — single character rendered on this module's cells in the layout grid and preview widget. + - Zero or more stat modifier formulas (see REQ-MOD-STAT-CALC). + +- REQ-MOD-LAYOUT: Each ship in `ships.toml` defines a `layout` — a list of strings representing the ship's module grid (see Ship Layout Format). All ships define a layout. + +### Module Placement + +- REQ-MOD-PLACEMENT: In the layout configuration dialog (REQ-MOD-UI-DIALOG), the player places modules onto the ship's layout grid. Clicking a module button in the module selection grid enters module placement mode for that module type. While in placement mode, a ghost of the module's surface mask is rendered at the cell under the cursor. Clicking a valid position places one instance of the module. A position is valid if every `O` cell in the module's (rotated) surface mask coincides with an unoccupied buildable cell of the ship's layout. The player may place unlimited instances of the same module type. +- REQ-MOD-ROTATION: While in module placement mode, pressing Q rotates the module ghost 90° counter-clockwise and E rotates it 90° clockwise. Rotation transforms the surface mask grid identically to building rotation (REQ-BLD-ROTATE). +- REQ-MOD-REMOVE: The module selection grid includes a "Remove" button. Clicking it enters remove mode. In remove mode, clicking on a cell occupied by a placed module removes that entire module instance from the layout. Remove mode is exited by clicking the Remove button again or by selecting a module for placement. + +### Module Effects + +- REQ-MOD-MATERIALS: The total materials required to build a ship are the union of the ship's base `[ship.schematic].materials` and the `materials` of every module instance in the configured layout. Quantities of the same item type are summed. +- REQ-MOD-PRODUCTION-TIME: The total production time is the ship's base `[ship.schematic].production_time_seconds` plus the sum of `production_time_seconds` for every module instance in the configured layout. +- REQ-MOD-THREAT: The total threat cost of a ship is the ship's base `[ship.threat].cost_formula` evaluated at the ship's level, plus the sum of `threat_cost` for every module instance in the configured layout. +- REQ-MOD-STAT-CALC: For each ship stat, the final value is computed as: `final = base × total_multiplier + total_additive`, where: + - `base` is the ship's base stat formula evaluated at the ship's production level. + - `total_multiplier` = 1 + sum of (m_i − 1) for each multiplicative modifier m_i from all module instances. Each m_i is evaluated from the module's multiplicative formula at the module's `player_production_level`. + - `total_additive` = sum of all additive modifier values from all module instances. Each additive value is evaluated from the module's additive formula at the module's `player_production_level`. + + Module stat modifier formulas follow the naming convention: for a ship stat `ship.._formula`, a module may define `added__formula` (additive) and/or `multiplied__formula` (multiplicative) under `[module.]`. Example: for `ship.sensor.sensor_range_formula`, a module may define `module.sensor.added_sensor_range_formula` and/or `module.sensor.multiplied_sensor_range_formula`. + +### Module UI + +- REQ-MOD-UI-PREVIEW: When a schematic is selected in a shipyard's selected building panel, a small non-interactive **ship layout preview** widget is shown below the schematic dropdown. The preview renders the ship's layout grid at a reduced scale: buildable cells without a module are shown as white, non-buildable cells are shown as black, and cells occupied by a module are shown in that module's `fill_color` with the module's `glyph` character. Below the preview, a "Configure" button is shown. +- REQ-MOD-UI-DIALOG: Clicking the "Configure" button opens the **layout configuration dialog** as a modal. While the dialog is open, the game is paused (speed set to 0×). On close, the game speed is restored to what it was before the dialog was opened. + + The dialog contains: + - **Left side**: The ship's layout grid rendered at full scale. Buildable cells are white; non-buildable cells are black. Placed modules are rendered with their `fill_color` and `glyph`. The ghost of the currently selected module is shown at the cursor position when in placement mode. + - **Right side**: A grid of module selection buttons (one per module type defined in `modules.toml`) plus a "Remove" button. Each module button shows the module id and its glyph. + - **Bottom**: A "Confirm" button and a "Cancel" button. Cancel discards all changes made in this dialog session and closes the dialog. Confirm applies the changes: the shipyard's configured layout is updated, the required materials and cycle time displayed in the selected building panel are recalculated, and the ship layout preview is refreshed. + +- REQ-MOD-UI-LAYOUT-SIZE: Ship layouts are small enough to display in the layout configuration dialog without scrolling (maximum grid size fits within the dialog). + ## Defence Stations - REQ-DEF-PLAYER-PLACEMENT: 2 player defence stations are pre-placed in space at the start. Their positions are determined by `world.toml [regions].asteroid_width` and `player_buffer_width`. @@ -146,6 +210,7 @@ Output port indicators are not building tiles themselves. A building may have mo - REQ-WAV-GAP: At game start and immediately after each wave is triggered, a random inter-wave gap is drawn uniformly from [`world.toml [waves].gap_min_seconds`, `gap_max_seconds`]. - REQ-WAV-TRIGGER: When the gap expires, a wave is triggered. Ships are selected one at a time: from all schematics whose `threat.cost_formula` evaluates to > 0 at the current enemy ship level, uniformly randomly pick one whose cost fits the remaining threat budget. Repeat until no eligible schematic fits. Any remaining threat carries over to the next wave. A longer gap results in a larger wave. Because enemy ship level increases with time (REQ-WAV-SHIP-LEVEL), threat cost per ship rises naturally over the course of the game. - REQ-WAV-SHIP-LEVEL: Each wave's enemy ships are assigned a level determined by `world.toml [waves].ship_level_formula` where x is elapsed game time in seconds. This is the sole mechanism by which individual enemy ships become stronger over time. Wave *size* grows separately via threat accumulation (REQ-WAV-THREAT-RATE) and push scaling (REQ-PSH-ACCUMULATION). Per-ship stats and threat cost are computed from the ship level via the formulas in `ships.toml` (see REQ-SHP-STATS). +- REQ-WAV-NO-MODULES: Enemy ships spawned by waves have no modules. Their stats are computed from the base ship formulas only, with no module modifiers applied. - REQ-WAV-SPAWN-DURATION: Ships in a wave are spawned one at a time over `world.toml [waves].spawn_duration_seconds`. ## Push Scaling @@ -222,7 +287,7 @@ The screen is divided into three vertical sections: - REQ-UI-PRODUCTION-PROGRESS: For buildings that produce items or ships (miner, smelter, assembler, reprocessing plant, shipyard), the selected building panel also shows: (a) the cycle time of the currently selected recipe or schematic in seconds, and (b) the completion percentage of the active production cycle as an integer (e.g. `42%`), or the text `idle` when no production cycle is active. When no recipe or schematic is selected, neither the cycle time nor the progress indicator is shown. - REQ-UI-MULTI-SELECT: The player selects multiple buildings by box-drag or by Ctrl+clicking individual buildings to add or remove them from the selection. - REQ-UI-MULTI-SELECTION: When multiple buildings are selected, the panel shows how many of each building type are selected. No per-building detail is shown. -- REQ-UI-CONFIG-INLINE: Recipe, schematic, ship stance, and target priority configuration for a selected building is shown and changed inline within this panel. +- REQ-UI-CONFIG-INLINE: Recipe, schematic, ship stance, and target priority configuration for a selected building is shown and changed inline within this panel. For shipyards, the panel additionally shows the ship layout preview and "Configure" button below the schematic dropdown (REQ-MOD-UI-PREVIEW). - REQ-UI-BELT-CLEAR: When one or more belt, splitter, tunnel entry, or tunnel exit tiles are selected, the panel shows a "Clear" button that removes all items from the selected tiles. Clearing a tunnel entry or exit also discards all items currently in transit through that tunnel (REQ-BLD-TUNNEL-TRANSIT). This can be used to resolve stalled belts, splitters, and tunnels. ### Build Button Grid