# Modular Ships: Remove Ship Roles, Unify Capabilities as Modules ## Why Ships currently have a fixed role (combat, salvage, repair) baked into their definition. This limits ship customization — a ship is either a fighter or a salvage ship, never both. By moving weapon, salvage cargo, and repair tool capabilities into the module system, players can freely compose ship loadouts. A single hull can carry two weapons and a repair tool, or a weapon and a salvage bay, etc. ## What Changes ### Ship definitions lose role-specific sections `ShipDef` drops `std::optional`, `std::optional`, `std::optional`. Ships define only hull stats (HP, movement, sensor) and a layout grid. A new `default_modules` list is added per schematic for enemy wave ships (see below). ### Capability modules replace roles New module types in `modules.toml` provide capabilities. A module with base stat formulas (e.g. `damage_formula`) under a capability section (`[module.weapon]`, `[module.salvage]`, `[module.repair]`) is a **capability module** that creates a child entity. A module with only `added_*`/`multiplied_*` formulas is a **passive module** that modifies stats. Example capability module: ```toml [[module]] id = "laser_turret" [module.weapon] damage_formula = "5 + 2*x" # x = module's player_production_level attack_range_formula = "8 + x" attack_rate_formula = "1.5 + 0.1*x" ``` Example passive module boosting weapons: ```toml [[module]] id = "weapon_upgrade" [module.weapon] multiplied_damage_formula = "1.0 + 0.15 * x" ``` Example passive module boosting ship stats: ```toml [[module]] id = "armor_plate" [module.health] multiplied_hp_formula = "1.0 + 0.2 * x" ``` ### Capability modules become child entities Each placed capability module instance becomes its own entt entity with a `ModuleOwnerComponent { entt::entity ship }` linking it to the parent ship. This allows multiple instances of the same type (e.g. three weapons, each with independent stats, cooldown, and target). A new `ModuleOwnerComponent` is introduced: ```cpp struct ModuleOwnerComponent { entt::entity ship; }; ``` ### Passive modifiers apply to both ship and module entities During spawn, passive module modifiers are collected and routed by category: - `[module.health]`, `[module.movement]`, `[module.sensor]` modifiers apply to the ship entity's hull stats. - `[module.weapon]` modifiers apply to every weapon child entity on the ship. - `[module.repair]` modifiers apply to every repair child entity on the ship. - `[module.salvage]` modifiers apply to every salvage child entity on the ship. Capability module child entities must be created first, then passive modifiers are applied. The formula variable `x` is always the module's `player_production_level`. ### Behavior components stay on the ship entity `ThreatResponseBehaviorComponent`, `SalvageBehaviorComponent`, `RepairBehaviorComponent` remain on the ship entity (they drive movement). They are attached if the ship has at least one module of the corresponding type. ### Hybrid ships are allowed A ship may have modules of different capability types. Movement arbitration currently uses last-writer-wins (the last behavior system ticked sets the intent). This is acceptable for now; dynamic priority-based arbitration will be added later. ### Systems query module entities Weapon, repair, and salvage tick systems query for their component + `ModuleOwnerComponent` and resolve position from the owner ship. Each module instance ticks independently. ### Despawn cleans up child entities `ShipSystem::despawn` destroys the ship entity and all module entities whose `ModuleOwnerComponent::ship` matches it. ### Enemy wave ships use default modules Since weapons are now modules, enemy ships need modules to fight. Each ship schematic in `ships.toml` defines a `default_modules` list (same format as layout blueprints). Wave-spawned enemy ships are instantiated with this module layout. If `default_modules` is absent or empty, the ship spawns with no modules (and therefore no combat/salvage/repair capability). ### Visuals use per-schematic colors instead of per-role `visuals.toml` defines fill/outline colors and glyphs per ship schematic (e.g. fighter, sniper, gunship) rather than per role (combat, salvage, repair). Debug draw sensor circles use the schematic's outline color.