Spec unlock groups: config-driven grouped unlocks
Replace the per-config unlock fields (unlock_at_station_level, unlock_requires) with a first-class unlock-group concept defined in a new unlocks.toml. An unlock group is now the unit of loot: awarding one grants all its members (ships, modules, buildings, assembler recipes) at once, so a building and a module can be unlocked together. - Buildings become lockable (REQ-LOCK-BUILDING); locked types are hidden from the build grid and skipped in blueprint placement. - Items absent from every unlock group start unlocked (replaces -1). - New unlocked_at_start flag on assembler recipes for graph-unreachable base recipes (e.g. building blocks). - Group display name derived from id for now; no display_name field yet. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y7N59FsLA5e2kuVdqe4Uhc
This commit is contained in:
@@ -5,10 +5,11 @@
|
|||||||
Config files use the TOML format. The following config files drive game parameters:
|
Config files use the TOML format. The following config files drive game parameters:
|
||||||
|
|
||||||
- **world.toml** — world dimensions, region widths, expansion amounts, building refund percentage, wave timing, boss wave timing, belt speed, starting building blocks, departure interval, ship orbit factor, rally orbit radius, scrap-per-threat conversion, combat target-selection parameters (target score formula, overclaim penalty formula, target hysteresis), artifact chance formula, artifact win count, view pan speeds (slow and fast horizontal pan speed and pan ramp band width), an optional building blocks tooltip string (shown as the header bar's building blocks stock hover tooltip, REQ-UI-BLOCKS-TOOLTIP; omitted when unset), and an optional artifact tooltip string (shown as the header bar's artifact count hover tooltip, REQ-UI-ARTIFACTS-TOOLTIP; omitted when unset).
|
- **world.toml** — world dimensions, region widths, expansion amounts, building refund percentage, wave timing, boss wave timing, belt speed, starting building blocks, departure interval, ship orbit factor, rally orbit radius, scrap-per-threat conversion, combat target-selection parameters (target score formula, overclaim penalty formula, target hysteresis), artifact chance formula, artifact win count, view pan speeds (slow and fast horizontal pan speed and pan ramp band width), an optional building blocks tooltip string (shown as the header bar's building blocks stock hover tooltip, REQ-UI-BLOCKS-TOOLTIP; omitted when unset), and an optional artifact tooltip string (shown as the header bar's artifact count hover tooltip, REQ-UI-ARTIFACTS-TOOLTIP; omitted when unset).
|
||||||
- **buildings.toml** — building block cost and construction time per building type, plus an optional tooltip description string per building type (shown as the build button's hover tooltip, REQ-UI-BUILD-TOOLTIP; omitted when unset).
|
- **buildings.toml** — building block cost and construction time per building type, plus an optional tooltip description string per building type (shown as the build button's hover tooltip, REQ-UI-BUILD-TOOLTIP; omitted when unset). Whether a building type is available from game start or must be unlocked during play is not defined here but in **unlocks.toml** (REQ-LOCK-EXPLICIT): a building type granted by an unlock group starts locked and is hidden from the build menu until its group is awarded (REQ-LOCK-BUILDING).
|
||||||
- **recipes.toml** — crafting recipes: inputs, outputs, quantities, durations, and reprocessing plant probabilities. Assembler recipe entries may optionally define `unlock_at_station_level` (integer): -1 means the recipe is explicitly unlocked at game start; a value ≥ 0 means the recipe starts locked and a schematic for it can be awarded via defence station destruction (see REQ-LOCK-EXPLICIT, REQ-DEF-SCHEMATIC-DROP). An assembler recipe schematic entry may also define an optional `unlock_requires` list of prerequisite schematic ids (REQ-LOCK-PREREQ).
|
- **recipes.toml** — crafting recipes: inputs, outputs, quantities, durations, and reprocessing plant probabilities. Assembler recipe entries may optionally define `unlocked_at_start` (boolean, default false): when true the recipe is available from game start regardless of the implicit item graph — used for base recipes that no schematic's materials reach (such as building blocks; see REQ-LOCK-IMPLICIT). Which assembler recipes must instead be awarded during play (explicitly gated) is defined in **unlocks.toml**, not here (REQ-LOCK-EXPLICIT); every remaining assembler recipe is implicitly unlocked through the item graph (REQ-LOCK-IMPLICIT).
|
||||||
- **ships.toml** — per schematic: a human-readable display name (used in the UI), hull stats (HP, max linear speed, sensor range, main acceleration, maneuvering acceleration, angular acceleration, max rotation speed) as plain values, required build materials, the station level at which the schematic becomes available for unlock (`unlock_at_station_level`; -1 means the player starts with the schematic already unlocked), an optional `unlock_requires` prerequisite list (REQ-LOCK-PREREQ), a layout grid defining the ship's module slots, and a `default_modules` list used for enemy wave ships (see REQ-WAV-DEFAULT-MODULES).
|
- **ships.toml** — per schematic: a human-readable display name (used in the UI), hull stats (HP, max linear speed, sensor range, main acceleration, maneuvering acceleration, angular acceleration, max rotation speed) as plain values, required build materials, a layout grid defining the ship's module slots, and a `default_modules` list used for enemy wave ships (see REQ-WAV-DEFAULT-MODULES). Whether a ship schematic is available from game start or must be unlocked during play is defined in **unlocks.toml** (REQ-LOCK-EXPLICIT), not here.
|
||||||
- **modules.toml** — per module type: id, surface mask, materials list, production time, fill color, glyph, an optional tooltip description string (shown as the module selection button's hover tooltip, REQ-MOD-UI-MODULE-TOOLTIP; omitted when unset), the station level at which the schematic becomes available for unlock (`unlock_at_station_level`; -1 means the player starts with the module schematic already unlocked), an optional `unlock_requires` prerequisite list (REQ-LOCK-PREREQ), and an optional capability section and/or stat modifier formulas. A module with a capability section (`[module.weapon]`, `[module.salvage]`, or `[module.repair]`) containing base stat formulas is a **capability module** that grants the ship a weapon, salvage bay, or repair tool per instance (see REQ-MOD-CONFIG for the full list of formulas per capability type). A module with only `added_*`/`multiplied_*` formulas is a **passive module** that modifies stats on the ship or on capability module instances (see REQ-MOD-STAT-CALC).
|
- **modules.toml** — per module type: id, surface mask, materials list, production time, fill color, glyph, an optional tooltip description string (shown as the module selection button's hover tooltip, REQ-MOD-UI-MODULE-TOOLTIP; omitted when unset), and an optional capability section and/or stat modifier formulas. Whether a module schematic is available from game start or must be unlocked during play is defined in **unlocks.toml** (REQ-LOCK-EXPLICIT), not here. A module with a capability section (`[module.weapon]`, `[module.salvage]`, or `[module.repair]`) containing base stat formulas is a **capability module** that grants the ship a weapon, salvage bay, or repair tool per instance (see REQ-MOD-CONFIG for the full list of formulas per capability type). A module with only `added_*`/`multiplied_*` formulas is a **passive module** that modifies stats on the ship or on capability module instances (see REQ-MOD-STAT-CALC).
|
||||||
|
- **unlocks.toml** — unlock groups: each `[[unlock]]` entry names a group of ship schematics, module schematics, building types, and/or assembler recipes that are awarded together from a single defence station drop (see Unlock Group Format, REQ-LOCK-EXPLICIT, REQ-DEF-SCHEMATIC-DROP). Anything not granted by any unlock group is available from game start.
|
||||||
- **stations.toml** — HP, damage, range, fire rate, and scrap drop for player and enemy defence stations, defined as formulas of station level.
|
- **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 schematic, and station type; a distinct beam color per tool type (weapon, repair, salvage) and beam width; overlay and toast colors; and building status light colors (grey, green, red, and yellow fills plus the outline color, REQ-UI-STATUS-LIGHT). Loaded by the UI at startup; the simulation does not read it.
|
- **visuals.toml** — rendering-only config (not game parameters): fill and outline colors and glyphs for every building type, item type, ship schematic, and station type; a distinct beam color per tool type (weapon, repair, salvage) and beam width; overlay and toast colors; and building status light colors (grey, green, red, and yellow fills plus the outline color, REQ-UI-STATUS-LIGHT). Loaded by the UI at startup; the simulation does not read it.
|
||||||
- **ship_layouts.toml** — named layout blueprints per ship type; written and read by the application to persist the layout blueprint panel (REQ-MOD-UI-BLUEPRINT-PANEL through REQ-MOD-UI-BLUEPRINT-FILE-LOAD). Not a game parameter file; the simulation does not read it.
|
- **ship_layouts.toml** — named layout blueprints per ship type; written and read by the application to persist the layout blueprint panel (REQ-MOD-UI-BLUEPRINT-PANEL through REQ-MOD-UI-BLUEPRINT-FILE-LOAD). Not a game parameter file; the simulation does not read it.
|
||||||
@@ -65,6 +66,28 @@ Modules in `modules.toml` define a `surface_mask` — a list of strings that des
|
|||||||
- `O` — module cell: must be placed on an unoccupied buildable cell (`O`) of the ship's layout.
|
- `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.
|
- `X` — ignored cell: may overlap any cell (non-buildable, unoccupied buildable, or occupied buildable) or extend outside the layout grid entirely.
|
||||||
|
|
||||||
|
### Unlock Group Format
|
||||||
|
|
||||||
|
Unlock groups in `unlocks.toml` define what the player can be awarded from defence station drops (REQ-DEF-SCHEMATIC-DROP); by their absence they also define what is available from game start (REQ-LOCK-EXPLICIT). Each entry:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[[unlock]]
|
||||||
|
id = "salvage_operations" # unique unlock-group id
|
||||||
|
station_level = 2 # eligible once a destroyed station set's level >= this
|
||||||
|
requires = [] # prerequisite unlock-group ids (REQ-LOCK-PREREQ); default empty
|
||||||
|
ships = [] # ship schematic ids granted (from ships.toml)
|
||||||
|
modules = ["salvager"] # module schematic ids granted (from modules.toml)
|
||||||
|
buildings = ["salvage_bay"] # building type ids granted (from buildings.toml)
|
||||||
|
recipes = [] # assembler recipe ids granted (from recipes.toml)
|
||||||
|
```
|
||||||
|
|
||||||
|
- `id` — unique identifier of the unlock group; referenced by other groups' `requires`. Its display name in the schematic choice dialog is derived from the id (same convention as building, module, and recipe ids); there is no separate name field for now.
|
||||||
|
- `station_level` — the minimum destroyed enemy defence station level at which this group becomes eligible to drop (REQ-DEF-SCHEMATIC-DROP).
|
||||||
|
- `requires` — optional list of prerequisite unlock-group ids that must already have been awarded before this group can drop (REQ-LOCK-PREREQ). Defaults to empty.
|
||||||
|
- `ships`, `modules`, `buildings`, `recipes` — the ids granted when this group is awarded. Each list defaults to empty, but a group must grant at least one item overall. Every id must resolve to a definition in the corresponding config file, and `recipes` ids must name **assembler** recipes. Each grantable id (ship, module, building, or assembler recipe) may be granted by **at most one** unlock group; violations fail config load (REQ-LOCK-EXPLICIT).
|
||||||
|
|
||||||
|
Any ship, module, building, or assembler recipe id that appears in no unlock group's grant lists is available from game start (REQ-LOCK-EXPLICIT).
|
||||||
|
|
||||||
## Game World
|
## 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.
|
- 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.
|
||||||
@@ -193,7 +216,7 @@ Modules in `modules.toml` define a `surface_mask` — a list of strings that des
|
|||||||
## Ships
|
## Ships
|
||||||
|
|
||||||
- REQ-SHP-AUTONOMOUS: Ships are produced by shipyards and are fully autonomous once produced.
|
- REQ-SHP-AUTONOMOUS: Ships are produced by shipyards and are fully autonomous once produced.
|
||||||
- REQ-SHP-STATS: Base hull stats are defined as plain values in `ships.toml`: HP (`[ship.health].hp`), max linear speed (`[ship.movement].speed`), sensor range (`[ship.sensors].range`), main acceleration (`[ship.movement].main_acceleration`, tiles/s²), maneuvering acceleration (`[ship.movement].maneuvering_acceleration`, tiles/s²), angular acceleration (`[ship.movement].angular_acceleration`, rad/s²), max rotation speed (`[ship.movement].max_rotation_speed`, rad/s). Required build materials (`[ship.schematic].materials`) and the station level at which the schematic becomes available for unlock (`[[ship]].unlock_at_station_level`; -1 = player starts with the schematic already unlocked) are also defined there. Combat, salvage, and repair capabilities are provided by modules (see REQ-MOD-CONFIG). Final hull stats incorporate passive module modifiers per REQ-MOD-STAT-CALC.
|
- REQ-SHP-STATS: Base hull stats are defined as plain values in `ships.toml`: HP (`[ship.health].hp`), max linear speed (`[ship.movement].speed`), sensor range (`[ship.sensors].range`), main acceleration (`[ship.movement].main_acceleration`, tiles/s²), maneuvering acceleration (`[ship.movement].maneuvering_acceleration`, tiles/s²), angular acceleration (`[ship.movement].angular_acceleration`, rad/s²), max rotation speed (`[ship.movement].max_rotation_speed`, rad/s). Required build materials (`[ship.schematic].materials`) are also defined there; whether the schematic starts unlocked or must be awarded during play is defined in `unlocks.toml` (REQ-LOCK-EXPLICIT). Combat, salvage, and repair capabilities are provided by modules (see REQ-MOD-CONFIG). Final hull stats incorporate passive 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-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-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 using a physics-based model. Each ship has a velocity and a facing direction, both updated each tick. The main acceleration (`main_acceleration`) is applied along the ship's current facing direction only. The maneuvering acceleration (`maneuvering_acceleration`) can be applied in any direction independently of the facing direction, enabling lateral or braking movement without rotating. The angular acceleration (`angular_acceleration`) controls how quickly the ship rotates. Linear speed is capped at the ship's `speed` value; rotation rate is capped at the ship's `max_rotation_speed` value. Ship position refers to the ship's center for all range, sensor, and attack checks.
|
- REQ-SHP-MOVEMENT: Ships move using a physics-based model. Each ship has a velocity and a facing direction, both updated each tick. The main acceleration (`main_acceleration`) is applied along the ship's current facing direction only. The maneuvering acceleration (`maneuvering_acceleration`) can be applied in any direction independently of the facing direction, enabling lateral or braking movement without rotating. The angular acceleration (`angular_acceleration`) controls how quickly the ship rotates. Linear speed is capped at the ship's `speed` value; rotation rate is capped at the ship's `max_rotation_speed` value. Ship position refers to the ship's center for all range, sensor, and attack checks.
|
||||||
@@ -234,8 +257,6 @@ Modules in `modules.toml` define a `surface_mask` — a list of strings that des
|
|||||||
- `id` — unique identifier, also used as the display name in the UI.
|
- `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).
|
- `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).
|
- `materials` — list of materials required per instance (added to the ship's build cost).
|
||||||
- `unlock_at_station_level` — the enemy defence station level at which this module's schematic becomes available for unlock; -1 means the player starts with the module schematic already unlocked.
|
|
||||||
- `unlock_requires` — optional list of prerequisite schematic ids that must already be unlocked before this module's schematic can drop (REQ-LOCK-PREREQ). Defaults to empty.
|
|
||||||
- `production_time_seconds` — time added to the ship's production cycle per instance.
|
- `production_time_seconds` — time added to the ship's production cycle per instance.
|
||||||
- `fill_color` — fill color used to render this module's cells in the layout grid.
|
- `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.
|
- `glyph` — single character rendered on this module's cells in the layout grid and preview widget.
|
||||||
@@ -349,35 +370,27 @@ Modules in `modules.toml` define a `surface_mask` — a list of strings that des
|
|||||||
- REQ-DEF-ENEMY-FIRE: Enemy defence stations automatically fire at player ships within range.
|
- REQ-DEF-ENEMY-FIRE: Enemy defence stations automatically fire at player ships within range.
|
||||||
- REQ-DEF-NO-CROSSFIRE: Enemy and player defence stations are never in each other's firing range.
|
- REQ-DEF-NO-CROSSFIRE: Enemy and player defence stations are never in each other's firing range.
|
||||||
- REQ-DEF-PUSH: When both enemy defence stations in a set are destroyed, the boss countdown is advanced (REQ-WAV-BOSS-ADVANCE), the scrollable area is extended (REQ-GW-PUSH-EXPAND), a new set of enemy defence stations is placed at the new boundary, and exactly one schematic drop is awarded for the destroyed set (REQ-DEF-SCHEMATIC-DROP).
|
- REQ-DEF-PUSH: When both enemy defence stations in a set are destroyed, the boss countdown is advanced (REQ-WAV-BOSS-ADVANCE), the scrollable area is extended (REQ-GW-PUSH-EXPAND), a new set of enemy defence stations is placed at the new boundary, and exactly one schematic drop is awarded for the destroyed set (REQ-DEF-SCHEMATIC-DROP).
|
||||||
- REQ-DEF-SCHEMATIC-DROP: Each destroyed set of enemy defence stations awards exactly one schematic drop (not one per station). The drop opens a **schematic choice dialog** — a modal dialog that pauses the game (speed set to 0×; on close, speed is restored to what it was before the dialog opened). Before drawing schematic picks, an artifact roll is made: evaluate `world.toml [world].artifact_chance_formula` with `x` set to the level of the destroyed station set, clamp the result to [0, 1], then compare against a uniform random value in [0, 1). If the roll succeeds, the dialog presents one **artifact option** plus two schematic picks drawn from the eligible pool; otherwise it presents three schematic picks. Up to three (or two, if an artifact option is present) schematic options are drawn uniformly at random **without replacement** from the eligible drop pool. If the pool contains fewer than the required number of entries, only that many schematic options are shown (the artifact option is always shown if the roll succeeded). The eligible drop pool contains:
|
- REQ-DEF-SCHEMATIC-DROP: Each destroyed set of enemy defence stations awards exactly one drop (not one per station). The drop opens a **schematic choice dialog** — a modal dialog that pauses the game (speed set to 0×; on close, speed is restored to what it was before the dialog opened). Before drawing picks, an artifact roll is made: evaluate `world.toml [world].artifact_chance_formula` with `x` set to the level of the destroyed station set, clamp the result to [0, 1], then compare against a uniform random value in [0, 1). If the roll succeeds, the dialog presents one **artifact option** plus two unlock picks drawn from the eligible pool; otherwise it presents three unlock picks. Up to three (or two, if an artifact option is present) unlock options are drawn uniformly at random **without replacement** from the eligible pool. If the pool contains fewer than the required number of entries, only that many unlock options are shown (the artifact option is always shown if the roll succeeded).
|
||||||
- All **ship schematics** and **module schematics** whose `unlock_at_station_level` is ≥ 0 and ≤ the level of the destroyed station set, and which have not yet been unlocked.
|
|
||||||
- All **assembler recipe schematics** whose `unlock_at_station_level` is ≥ 0 and ≤ the level of the destroyed station set, whose output item is currently implicitly unlocked (REQ-LOCK-IMPLICIT), and which have not yet been awarded.
|
|
||||||
|
|
||||||
In addition to the conditions above, a schematic is included in the eligible drop pool only when every prerequisite in its optional `unlock_requires` list is currently satisfied (REQ-LOCK-PREREQ). Because the pool is rebuilt for each drop, a schematic gated behind prerequisites first appears only after all of its prerequisites have themselves been unlocked.
|
The eligible pool contains every **unlock group** (REQ-LOCK-EXPLICIT) that (a) has not yet been awarded, (b) whose `station_level` is ≤ the level of the destroyed station set, and (c) every prerequisite in its `requires` list is currently satisfied (REQ-LOCK-PREREQ). Because the pool is rebuilt for each drop, an unlock group gated behind prerequisites first appears only after all of its prerequisites have themselves been awarded.
|
||||||
|
|
||||||
Each option in the dialog displays: the schematic name (ship `id` from `ships.toml`, module `id` from `modules.toml`, or assembler recipe `id` from `recipes.toml`) and the schematic type (ship, module, or assembler recipe). The artifact option (if present) is displayed as a distinct entry with the name "Artifact".
|
Each option in the dialog displays the unlock group's display name — derived from its `id` (same display convention as building, module, and recipe ids) — and the list of items it would grant: its ship, module, building, and assembler-recipe ids (each shown with the same display convention as its respective selection dialog). The artifact option (if present) is displayed as a distinct entry with the name "Artifact".
|
||||||
|
|
||||||
Each option additionally displays a vertical list of recipe names labeled "Unlocks recipes:", showing which miner and assembler recipes would newly become implicitly unlocked (REQ-LOCK-IMPLICIT) if this option were selected — specifically, the miner recipes and assembler recipes (without `unlock_at_station_level`) that are not currently implicitly unlocked but would become so after applying this option's effect:
|
Each option additionally displays a vertical list of recipe names labeled "Unlocks recipes:", showing which miner and assembler recipes would newly become implicitly unlocked (REQ-LOCK-IMPLICIT) if this option were selected — specifically, the miner recipes and implicitly-gated assembler recipes that are not currently implicitly unlocked but would become so after applying this option's effect. To compute this, all `materials` of the group's granted ship and module schematics are added to the base set per REQ-LOCK-IMPLICIT step 1a, and the output items of the group's granted assembler recipes are added per step 1b, before recomputation.
|
||||||
- For a ship or module schematic, its `materials` are added to the base set per REQ-LOCK-IMPLICIT step 1a before recomputation.
|
|
||||||
- For an assembler recipe schematic, its output item is added to the base set per REQ-LOCK-IMPLICIT step 1b before recomputation.
|
|
||||||
|
|
||||||
Each recipe is listed by its `id` (using the same display convention as the assembler recipe-selection dialog), sorted alphabetically. Hovering a recipe in this list displays the recipe info tooltip described for a recipe in REQ-UI-SELECT-TOOLTIP (the recipe name; the name and quantity of each input item; the completion time; and the name and quantity of the produced output item). If no recipes would be newly unlocked, the list shows "None".
|
Each recipe is listed by its `id` (using the same display convention as the assembler recipe-selection dialog), sorted alphabetically. Hovering a recipe in this list displays the recipe info tooltip described for a recipe in REQ-UI-SELECT-TOOLTIP (the recipe name; the name and quantity of each input item; the completion time; and the name and quantity of the produced output item). If no recipes would be newly unlocked, the list shows "None".
|
||||||
|
|
||||||
The player selects one option by clicking it. If the player selects the artifact option, the player's artifact count is incremented by 1 (REQ-WIN-ARTIFACT-COUNT) and the dialog closes; no schematic is applied. Otherwise, the selected schematic is applied and the dialog closes:
|
The player selects one option by clicking it. If the player selects the artifact option, the player's artifact count is incremented by 1 (REQ-WIN-ARTIFACT-COUNT) and the dialog closes; no unlock is applied. Otherwise the selected unlock group is awarded and the dialog closes: every ship, module, building, and assembler recipe the group grants becomes unlocked at once — ship schematics unlock the corresponding shipyard selection; module schematics unlock the module type for placement in the layout configuration dialog (REQ-MOD-UI-DIALOG); building types become available in the build menu (REQ-LOCK-BUILDING); assembler recipes become available in the assembler recipe-selection dialog (subject to REQ-LOCK-UI-RECIPE). The unlock group is removed from the pool permanently (REQ-LOCK-EXPLICIT), and the implicit unlock set is recomputed (REQ-LOCK-IMPLICIT).
|
||||||
|
|
||||||
For a **ship or module schematic**: it is unlocked (ship schematics unlock the corresponding shipyard selection; module schematics unlock the module type for placement in the layout configuration dialog (REQ-MOD-UI-DIALOG)).
|
|
||||||
|
|
||||||
For an **assembler recipe schematic**: the recipe is explicitly unlocked and becomes available in the assembler recipe-selection dialog (subject to REQ-LOCK-UI-RECIPE). The schematic is removed from the drop pool permanently (REQ-LOCK-EXPLICIT). The implicit unlock set is recomputed (REQ-LOCK-IMPLICIT).
|
|
||||||
|
|
||||||
## Progression & Locking
|
## Progression & Locking
|
||||||
|
|
||||||
- REQ-LOCK-EXPLICIT: Ship schematics, module schematics, and **assembler recipe schematics** (assembler recipes in `recipes.toml` that define `unlock_at_station_level`) are **explicitly** locked or unlocked. A schematic starts unlocked if its `unlock_at_station_level` is -1; all others start locked. Locked schematics are unlocked only by REQ-DEF-SCHEMATIC-DROP. Once unlocked, a schematic is never re-locked within a run; lock states reset to their initial values on Restart (REQ-CFG-RELOAD). Unlike ship and module schematics, an assembler recipe schematic is removed from the drop pool permanently once awarded and cannot be dropped again.
|
- REQ-LOCK-EXPLICIT: The unit of unlocking is an **unlock group**, defined by an `[[unlock]]` entry in `unlocks.toml` (see Unlock Group Format). Each unlock group grants a set of ship schematics, module schematics, building types, and/or assembler recipes. A ship, module, building, or assembler recipe is **locked at game start if and only if some unlock group grants it**; anything not granted by any unlock group starts unlocked. (For assembler recipes this "starts unlocked" is further governed by implicit gating — see REQ-LOCK-IMPLICIT; an assembler recipe granted by an unlock group is explicitly gated and never subject to implicit unlocking, while one flagged `unlocked_at_start` is always available.) A locked item is unlocked only by awarding its unlock group via REQ-DEF-SCHEMATIC-DROP, which grants all of the group's members at once. Once awarded, an unlock group and its members are never re-locked within a run, and the group is removed from the drop pool permanently; lock states reset to their initial values on Restart (REQ-CFG-RELOAD). Each grantable id may be granted by **at most one** unlock group; a grant id that names no defined ship/module/building/assembler-recipe, that names a non-assembler recipe, or that is granted by more than one unlock group, is a configuration error that fails config load with a descriptive message (REQ-CFG-RELOAD).
|
||||||
|
|
||||||
- REQ-LOCK-PREREQ: A ship schematic, module schematic, or assembler recipe schematic may optionally define `unlock_requires` — a list of prerequisite schematic ids (a ship `id`, module `id`, or assembler recipe `id`) that must already be unlocked before this schematic may enter the drop pool. A prerequisite is **satisfied** only when the schematic it names is currently **explicitly unlocked** (REQ-LOCK-EXPLICIT) — that is, the referenced schematic either started unlocked with `unlock_at_station_level = -1` or has been awarded via a drop. This prerequisite check is applied in addition to the per-schematic conditions in REQ-DEF-SCHEMATIC-DROP: a schematic enters the eligible drop pool only when its `unlock_at_station_level` condition is met, it has not yet been unlocked/awarded, and every id in its `unlock_requires` is satisfied. `unlock_requires` defaults to empty (no prerequisites), which reproduces the prior behaviour. The check is re-evaluated against the current explicit-unlock set every time a drop pool is built (after each REQ-DEF-SCHEMATIC-DROP and on Restart per REQ-CFG-RELOAD), so a gated schematic becomes eligible in the first drop after its last prerequisite is unlocked. Every id listed in any `unlock_requires` must resolve to a schematic that is itself explicitly unlockable (a ship, module, or assembler recipe schematic defined in config); an id that names no such schematic is a configuration error that fails config load with a descriptive message (config is loaded at startup and reloaded on Restart, REQ-CFG-RELOAD). A schematic that lists itself, or a cycle of mutually dependent prerequisites, is not a load error but can never become eligible, since no schematic in the cycle can be the first to unlock.
|
- REQ-LOCK-PREREQ: An unlock group may optionally define `requires` — a list of prerequisite **unlock-group ids** that must already have been awarded before this group may enter the drop pool. A prerequisite is **satisfied** only when the unlock group it names has been awarded (REQ-LOCK-EXPLICIT). This check is applied in addition to the conditions in REQ-DEF-SCHEMATIC-DROP: a group enters the eligible pool only when its `station_level` condition is met, it has not yet been awarded, and every id in its `requires` is satisfied. `requires` defaults to empty (no prerequisites). The check is re-evaluated against the current set of awarded unlock groups every time a drop pool is built (after each REQ-DEF-SCHEMATIC-DROP and on Restart per REQ-CFG-RELOAD), so a gated group becomes eligible in the first drop after its last prerequisite is awarded. Every id listed in any `requires` must resolve to an unlock group defined in `unlocks.toml`; an id that names no such group is a configuration error that fails config load with a descriptive message (config is loaded at startup and reloaded on Restart, REQ-CFG-RELOAD). An unlock group that lists itself, or a cycle of mutually dependent prerequisites, is not a load error but can never become eligible, since no group in the cycle can be the first to be awarded.
|
||||||
|
|
||||||
- REQ-LOCK-IMPLICIT: Item types and miner/assembler recipes are **implicitly** unlocked or locked based on the current set of unlocked ship, module, and assembler recipe schematics. The implicit unlock set is recomputed whenever any schematic changes lock state (on Restart or after REQ-DEF-SCHEMATIC-DROP). Computation:
|
- REQ-LOCK-IMPLICIT: Item types and miner/assembler recipes are **implicitly** unlocked or locked based on the current set of unlocked ship, module, and assembler recipe schematics. The implicit unlock set is recomputed whenever any schematic changes lock state (on Restart or after REQ-DEF-SCHEMATIC-DROP). Computation:
|
||||||
1. Start with the union of: (a) all item types listed in `materials` across all currently unlocked ship schematics and all currently unlocked module schematics, and (b) the output item type of every currently explicitly unlocked assembler recipe schematic (REQ-LOCK-EXPLICIT).
|
1. Start with the union of: (a) all item types listed in `materials` across all currently unlocked ship schematics and all currently unlocked module schematics, and (b) the output item type of every assembler recipe that is currently **explicitly available** — that is, either flagged `unlocked_at_start` in `recipes.toml`, or granted by an unlock group that has been awarded (REQ-LOCK-EXPLICIT).
|
||||||
2. For each item type in the current set: for every recipe (miner, smelter, or assembler) that produces it — skipping any assembler recipe schematic that defines `unlock_at_station_level` and is not yet explicitly unlocked — add each of that recipe's input item types to the set. If the recipe is a miner recipe or an assembler recipe that does not define `unlock_at_station_level`, mark it as implicitly unlocked. Explicitly unlocked assembler recipe schematics are available in the assembler recipe-selection dialog by virtue of REQ-LOCK-EXPLICIT; their inputs are also added to the implicit set in this step.
|
2. For each item type in the current set: for every recipe (miner, smelter, or assembler) that produces it — skipping any assembler recipe that is granted by an unlock group whose group has not yet been awarded — add each of that recipe's input item types to the set. If the recipe is a miner recipe, or an assembler recipe that is not granted by any unlock group, mark it as implicitly unlocked. Assembler recipes that are explicitly available (flagged `unlocked_at_start`, or granted by an awarded unlock group) are available in the assembler recipe-selection dialog by virtue of REQ-LOCK-EXPLICIT; their inputs are also added to the implicit set in this step.
|
||||||
3. Repeat step 2 until no new item types are added.
|
3. Repeat step 2 until no new item types are added.
|
||||||
Item types and miner/assembler recipes not reached by this process (and not explicitly unlocked) are locked. Smelter recipes participate in the traversal to propagate unlocking to their inputs but are never themselves shown in any UI dropdown.
|
Item types and miner/assembler recipes not reached by this process (and not explicitly unlocked) are locked. Smelter recipes participate in the traversal to propagate unlocking to their inputs but are never themselves shown in any UI dropdown.
|
||||||
|
|
||||||
@@ -387,9 +400,11 @@ Modules in `modules.toml` define a `surface_mask` — a list of strings that des
|
|||||||
|
|
||||||
- REQ-LOCK-UI-SCHEMATIC: Locked ship schematics are not shown in the shipyard's schematic-selection dialog (REQ-UI-SELECT-BUTTON).
|
- REQ-LOCK-UI-SCHEMATIC: Locked ship schematics are not shown in the shipyard's schematic-selection dialog (REQ-UI-SELECT-BUTTON).
|
||||||
|
|
||||||
|
- REQ-LOCK-BUILDING: A building type granted by an unlock group (REQ-LOCK-EXPLICIT) is **locked** until that group is awarded. A locked building type has no button in the build button grid (REQ-UI-BUILD-GRID) and cannot be placed, selected as a build tool, or triggered by its build hotkey (REQ-UI-HOTKEYS); its button appears in the grid only once the building type is unlocked. Building types not granted by any unlock group are available from game start. Lock state resets on Restart (REQ-CFG-RELOAD).
|
||||||
|
|
||||||
- REQ-LOCK-UI-SPLITTER: Item types that are not implicitly unlocked are excluded from splitter filter dropdowns (REQ-BLD-SPLITTER).
|
- REQ-LOCK-UI-SPLITTER: Item types that are not implicitly unlocked are excluded from splitter filter dropdowns (REQ-BLD-SPLITTER).
|
||||||
|
|
||||||
- REQ-LOCK-UI-BLUEPRINT: When a blueprint is placed (REQ-UI-BLUEPRINT-PLACE): if a stored recipe ID for a miner or assembler is currently locked, that building's recipe is left unset rather than applied; if a stored splitter filter entry refers to a locked item type, that entry is silently removed. (The analogous rule for locked ship schematics is defined in REQ-UI-BLUEPRINT-PLACE.)
|
- REQ-LOCK-UI-BLUEPRINT: When a blueprint is placed (REQ-UI-BLUEPRINT-PLACE): if a building in the blueprint is of a currently locked building type (REQ-LOCK-BUILDING), that building is silently skipped — no ghost, no validity check, no construction site, and its cost is excluded from the total — exactly as if it were not part of the blueprint; if a stored recipe ID for a miner or assembler is currently locked, that building's recipe is left unset rather than applied; if a stored splitter filter entry refers to a locked item type, that entry is silently removed. (The analogous rule for locked ship schematics is defined in REQ-UI-BLUEPRINT-PLACE.)
|
||||||
|
|
||||||
## Threat Level & Enemy Waves
|
## Threat Level & Enemy Waves
|
||||||
|
|
||||||
@@ -553,9 +568,9 @@ The screen is divided into two columns: a main column (75% width) containing the
|
|||||||
|
|
||||||
- REQ-UI-BLUEPRINT-BUTTON: Each blueprint entry consists of a blueprint button and a dedicated delete icon ("×") placed to the right of the button. The blueprint button displays the blueprint name and, below it, the total building block cost of the blueprint (sum of the individual costs of all constituent buildings). A blueprint button is disabled when the player cannot afford the total cost. Clicking an enabled blueprint button enters blueprint placement mode for that blueprint. The delete icon is always enabled regardless of whether the player can afford the blueprint.
|
- REQ-UI-BLUEPRINT-BUTTON: Each blueprint entry consists of a blueprint button and a dedicated delete icon ("×") placed to the right of the button. The blueprint button displays the blueprint name and, below it, the total building block cost of the blueprint (sum of the individual costs of all constituent buildings). A blueprint button is disabled when the player cannot afford the total cost. Clicking an enabled blueprint button enters blueprint placement mode for that blueprint. The delete icon is always enabled regardless of whether the player can afford the blueprint.
|
||||||
|
|
||||||
- REQ-UI-BLUEPRINT-MODE: In blueprint placement mode a ghost is rendered for every building in the blueprint at the position determined by its stored tile offset from the bounding-box center, which is anchored to the tile under the cursor. Each ghost is rendered individually as valid or invalid, applying REQ-BLD-PLACE-VALID conditions (a) and (b) per building (the other ghosts in the same blueprint do not count as existing buildings for the overlap check). A valid ghost uses its building type's semi-transparent per-building coloring (REQ-BLD-GHOST); an invalid ghost uses the distinct "invalid" color, as in single-building builder mode. Pressing R / Shift+R rotates the entire constellation 90° counter-clockwise / clockwise: each building's tile offset is rotated around the bounding-box center and each building's own rotation is updated, consistent with REQ-BLD-ROTATE. Blueprint placement mode is exited by right-clicking in the game world. Clicking a different blueprint button exits the current mode and enters blueprint placement mode for the newly clicked blueprint.
|
- REQ-UI-BLUEPRINT-MODE: In blueprint placement mode a ghost is rendered for every building in the blueprint (excluding any of a currently locked building type, REQ-LOCK-BUILDING, which is omitted entirely per REQ-LOCK-UI-BLUEPRINT) at the position determined by its stored tile offset from the bounding-box center, which is anchored to the tile under the cursor. Each ghost is rendered individually as valid or invalid, applying REQ-BLD-PLACE-VALID conditions (a) and (b) per building (the other ghosts in the same blueprint do not count as existing buildings for the overlap check). A valid ghost uses its building type's semi-transparent per-building coloring (REQ-BLD-GHOST); an invalid ghost uses the distinct "invalid" color, as in single-building builder mode. Pressing R / Shift+R rotates the entire constellation 90° counter-clockwise / clockwise: each building's tile offset is rotated around the bounding-box center and each building's own rotation is updated, consistent with REQ-BLD-ROTATE. Blueprint placement mode is exited by right-clicking in the game world. Clicking a different blueprint button exits the current mode and enters blueprint placement mode for the newly clicked blueprint.
|
||||||
|
|
||||||
- REQ-UI-BLUEPRINT-PLACE: Left-clicking in blueprint placement mode places the blueprint if (a) every building in the constellation satisfies REQ-BLD-PLACE-VALID conditions (a) and (b) at its resolved tile, and (b) the player has enough building blocks to afford the total cost. If both conditions are met, a construction site is added to the build queue for each building in the blueprint and the full total cost is deducted from the global building blocks stock in one transaction. If a recipe ID is stored for a building, it is applied to the construction site immediately. If a schematic ID is stored, it is applied only if that schematic is currently unlocked; if it is not unlocked, the shipyard's schematic is left unset. If splitter output filters are stored, they are applied to the construction site immediately and carry over when it finishes building (REQ-BLD-SITE-CONFIG). Locked recipe IDs and splitter filter entries for locked item types are handled on placement per REQ-LOCK-UI-BLUEPRINT. After a successful placement the game remains in blueprint placement mode, allowing the player to place the same blueprint again immediately.
|
- REQ-UI-BLUEPRINT-PLACE: Buildings of a currently locked building type (REQ-LOCK-BUILDING) are first excluded from the blueprint for this placement, per REQ-LOCK-UI-BLUEPRINT — they are not ghosted, not validity-checked, not placed, and their cost is excluded from the total. Left-clicking in blueprint placement mode then places the (remaining) blueprint if (a) every building in the constellation satisfies REQ-BLD-PLACE-VALID conditions (a) and (b) at its resolved tile, and (b) the player has enough building blocks to afford the total cost. If both conditions are met, a construction site is added to the build queue for each building in the blueprint and the full total cost is deducted from the global building blocks stock in one transaction. If a recipe ID is stored for a building, it is applied to the construction site immediately. If a schematic ID is stored, it is applied only if that schematic is currently unlocked; if it is not unlocked, the shipyard's schematic is left unset. If splitter output filters are stored, they are applied to the construction site immediately and carry over when it finishes building (REQ-BLD-SITE-CONFIG). Locked recipe IDs and splitter filter entries for locked item types are handled on placement per REQ-LOCK-UI-BLUEPRINT. After a successful placement the game remains in blueprint placement mode, allowing the player to place the same blueprint again immediately.
|
||||||
|
|
||||||
- REQ-UI-BLUEPRINT-DELETE: Clicking the delete icon ("×") on a blueprint entry immediately removes that blueprint from the list. If the deleted blueprint was active in blueprint placement mode, that mode is exited.
|
- REQ-UI-BLUEPRINT-DELETE: Clicking the delete icon ("×") on a blueprint entry immediately removes that blueprint from the list. If the deleted blueprint was active in blueprint placement mode, that mode is exited.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user