Files
space-game/STATES.md

544 lines
16 KiB
Markdown

# States, Orders, Behaviours, Assignments
This document defines the intended gameplay model for the project: a multiplayer X4-inspired RTS with strong automation and strong direct control.
It is no longer purely aspirational. The current runtime now uses the same top-level control layers described here:
1. `order`
2. `defaultBehavior`
3. `assignment`
4. `controllerTask`
5. `state`
What is still incomplete is scope, not structure.
## Design Goals
The game should support both:
- RTS-style direct control: `move-to`, `mine-this`, `hold-here`, `dock-there`, `attack-this`
- X4-style automation: `auto-mine`, `patrol`, `escort`, `trade`, `defend-area`
The intended player experience is:
- you can micro units at any time
- automation resumes when direct intervention is over
- fleets, stations, carriers, and sectors can provide reusable command structure
## Control Layers
Each ship should be modeled with five layers:
1. `order`
2. `defaultBehavior`
3. `assignment`
4. `controllerTask`
5. `state`
### `order`
A direct player-issued instruction with highest priority.
Properties:
- one-shot unless explicitly marked persistent
- can preempt automation
- may complete, fail, cancel, or timeout
- after resolution, ship falls back to `defaultBehavior`
Current active examples:
- `move-to(destination)`
- `mine-this(node, refinery)`
- `dock-at(target)`
### `defaultBehavior`
Persistent automation the ship should run when no direct order is active.
Properties:
- long-lived
- self-replanning
- normally loops or maintains a standing objective
- resumes automatically after one-shot order resolution
Current active examples:
- `idle`
- `auto-mine(area, refinery)`
- `patrol(route)`
- `escort-assigned`
### `assignment`
The command relationship the ship belongs to.
Examples:
- assigned to fleet
- assigned to carrier
- assigned to station
- assigned to sector command
- assigned to mining group
Assignment does not necessarily tell the ship what exact task to do every second. It provides authority, context, operating area, logistics ownership, and fallback doctrine.
Current active examples:
- `unassigned`
- `commander-subordinate(commanderId, role)`
- `station-based(stationId, role)`
- `mining-group(controllerId)`
### `controllerTask`
The immediate executable action.
Examples:
- travel
- dock
- undock
- mine/extract
- unload
- follow
- hold-position
Current active examples:
- `idle`
- `travel(destination, threshold)`
- `dock(host, bay)`
- `undock(host)`
- `extract(node)`
- `unload(target)`
- `follow(target, offset)`
### `state`
The physical/runtime state of the unit.
Examples:
- idle
- spooling-warp
- warping
- docking
- docked
- mining
- attacking
- holding
## Precedence Rules
Control precedence should be:
1. `order`
2. `defaultBehavior`
3. `assignment doctrine`
4. safety/failsafe logic
Interpretation:
- if a one-shot order exists, it drives planning
- if no order exists, the ship executes its default behavior
- if no explicit default behavior exists, assignment may supply one
- if none of the above apply, ship uses idle/safe fallback
This is the active planner model in the codebase.
## Order Lifecycle
One-shot orders are the RTS-facing layer.
Each order should move through:
1. `queued`
2. `accepted`
3. `planning`
4. `executing`
5. one of `completed`, `failed`, `cancelled`, `blocked`
Recommended behavior:
- `completed`: clear order and resume `defaultBehavior`
- `cancelled`: clear order immediately and resume `defaultBehavior`
- `failed`: clear order or retry depending on order policy
- `blocked`: wait, reroute, or escalate depending on order policy
Current implementation notes:
- direct orders already move through `queued`, `accepted`, `planning`, `executing`
- `completed`, `failed`, and `blocked` are already represented
- full queued-order chains do not exist yet
## Command Categories
### One-shot direct orders
These are the core RTS commands.
| Order | Intent | Status |
| --- | --- | --- |
| `move-to(destination)` | go to a position or object | active |
| `mine-this(node)` | mine this specific node | active |
| `dock-at(target)` | dock at target | active |
| `undock` | leave current host | not first-class yet |
| `hold-here(position)` | stay at current or assigned point | not implemented |
| `attack(target)` | attack a specific unit/object | not implemented as direct order |
| `intercept(target)` | chase and engage moving target | not implemented |
| `escort(target)` | follow and defend a specific target | not implemented as direct one-shot order |
| `transfer-cargo(target, item, amount)` | move cargo between entities | not implemented |
| `build-here(site)` | construct at location | not implemented |
| `salvage-this(target)` | recover specific wreck/resource | not implemented |
| `retreat-to(destination)` | disengage and move to safety | not implemented |
### Persistent default behaviours
These are the automation layer.
| Behavior | Intent | Status |
| --- | --- | --- |
| `idle` | do nothing beyond safety drift / local maintenance | active |
| `auto-mine(area, refinery)` | find nodes, mine, deliver, repeat | active |
| `patrol(route or area)` | move across checkpoints and react to threats | active |
| `escort-assigned` | stay with assigned commander or protected ship | active |
| `defend-area(area)` | hold area and engage threats inside rules | not implemented |
| `trade(route or policy)` | acquire, move, and deliver goods automatically | not implemented |
| `resupply(host or fleet)` | fetch and deliver fuel/ammo/cargo | not implemented |
| `ferry-units(host)` | shuttle units between hosts or waypoints | not implemented |
| `harvest(area)` | collect local resources automatically | not implemented |
| `hold-area(center, radius)` | remain in zone with low autonomy | not implemented |
### Assignments
Assignments define command context.
| Assignment | Meaning | Status |
| --- | --- | --- |
| `unassigned` | independent unit | active |
| `fleet-member(fleetId, role)` | unit belongs to a fleet structure | not implemented |
| `commander-subordinate(commanderId, role)` | subordinate follows commander doctrine | active |
| `carrier-wing(carrierId, role)` | launch/recover/escort under carrier | not implemented as distinct type |
| `station-based(stationId, role)` | station-owned logistics or defense unit | active |
| `sector-command(sectorId, role)` | unit operates inside a sector mandate | not implemented |
| `mining-group(controllerId)` | industrial unit tied to a mining controller | active |
Assignments can supply default behavior if the unit has none explicitly set.
## Planner Model
Planning works like this:
1. if `order` exists, derive `controllerTask` from it
2. else if `defaultBehavior` exists, derive `controllerTask` from it
3. else if `assignment` implies doctrine, derive `defaultBehavior`
4. else run idle fallback
This preserves RTS responsiveness while keeping X4-style automation.
Current implementation detail:
- mining and patrol progress are stored directly on `order` or `defaultBehavior`
- there is no separate high-level “captain” intent layer anymore
## Physical States
These are the intended physical states for ships.
| State | Meaning | Status |
| --- | --- | --- |
| `idle` | no active movement or operation | active |
| `holding` | maintaining current position/formation/anchor | reserved |
| `spooling-warp` | charging local fast-travel | active |
| `warping` | high-speed in-system travel | active |
| `spooling-ftl` | charging inter-system travel | active |
| `ftl` | inter-system travel | active |
| `arriving` | final approach after warp/ftl | active |
| `approaching` | standard approach to target | reserved |
| `docking-approach` | approach to docking lane or bay | active |
| `docking` | docking procedure in progress | active |
| `docked` | physically docked | active |
| `undocking` | undocking procedure in progress | active |
| `mining-approach` | aligning to resource node | active |
| `mining` | active extraction | active |
| `transferring` | moving cargo, fuel, or units | active |
| `building` | performing construction | reserved |
| `repairing` | performing repair actions | reserved |
| `patrolling` | movement as part of patrol | active |
| `escorting` | movement as part of escort | active |
| `attacking` | active weapons engagement | reserved |
| `retreating` | disengaging toward safety | reserved |
| `disabled` | cannot act | reserved |
| `destroyed` | removed from play | active outcome |
## Controller Tasks
These are the low-level tasks the planner can issue.
| Task | Purpose | Status |
| --- | --- | --- |
| `idle` | no task | active |
| `travel(destination, threshold)` | move to destination | active |
| `hold(position, radius)` | maintain a point | not implemented |
| `dock(host, bay)` | dock at host | active |
| `undock(host)` | undock from host | active |
| `extract(node)` | mine/extract resource | active |
| `unload(target, item, amount?)` | move cargo out | active |
| `load(target, item, amount?)` | move cargo in | not implemented |
| `follow(target, offset)` | maintain formation | active |
| `attack(target)` | engage target | not implemented as controller task |
| `intercept(target)` | chase moving target | not implemented |
| `orbit(target, radius)` | remain in orbit around target | not implemented |
| `build(site)` | perform construction | not implemented |
| `repair(target)` | perform repair | not implemented |
| `scan(target)` | gather intel | not implemented |
## Events
Events should be explicit and usable by both gameplay and debug tools.
### Order events
| Event | Meaning | Status |
| --- | --- | --- |
| `order-issued` | player or AI created an order | partially implicit |
| `order-accepted` | unit accepted order | represented in order status |
| `order-rejected` | unit cannot accept order | not explicit yet |
| `order-started` | execution began | represented in order status |
| `order-blocked` | execution cannot proceed for now | represented in order status |
| `order-completed` | order finished successfully | represented and logged |
| `order-failed` | order failed | represented and logged |
| `order-cancelled` | order was manually cleared or overridden | reserved |
### Behavior events
| Event | Meaning | Status |
| --- | --- | --- |
| `behavior-set` | default behavior assigned | represented in debug history |
| `behavior-cleared` | default behavior removed | represented in debug history |
| `behavior-resumed` | resumed after order completion | implicit |
| `behavior-paused` | temporarily suspended | implicit |
| `behavior-phase-changed` | automation phase updated | represented in debug history |
### Assignment events
| Event | Meaning | Status |
| --- | --- | --- |
| `assignment-set` | unit assigned | represented in debug history |
| `assignment-cleared` | assignment removed | represented in debug history |
| `assignment-role-changed` | subordinate role changed | partially represented |
| `commander-lost` | assigned leader unavailable | implicit |
### Controller/runtime events
| Event | Meaning | Status |
| --- | --- | --- |
| `arrived` | destination reached | active |
| `docking-clearance-granted` | docking permission accepted | active via history |
| `docking-clearance-denied` | docking permission denied | active via history |
| `docking-begin` | docking procedure started | active |
| `docked` | docking complete | active |
| `undocked` | undocking complete | active |
| `cargo-full` | cargo reached capacity | active via history |
| `cargo-empty` | cargo emptied | active via history |
| `target-lost` | attack/escort target unavailable | not explicit yet |
| `hostile-detected` | threat found | not explicit yet |
| `resource-depleted` | node exhausted | not explicit yet |
| `damaged` | ship took damage | not explicit yet |
| `destroyed` | ship destroyed | active outcome |
## Transition Rules
### Core fallback rule
When a direct order resolves:
- clear `order`
- emit terminal order event
- resume `defaultBehavior`
- if no explicit `defaultBehavior`, ask `assignment` for fallback
- if none exists, become `idle`
This is the main rule that keeps automation and RTS control compatible.
### Example: direct move order
1. player issues `move-to`
2. ship order becomes `move-to(destination)`
3. planner emits `travel(destination)`
4. ship moves through travel states
5. on `arrived`, order completes
6. ship resumes previous default behavior, such as `patrol` or `auto-mine`
### Example: direct mine-this order
1. player issues `mine-this(node)`
2. order overrides `auto-mine`
3. planner travels to node
4. planner extracts until cargo is full
5. order completes
6. unit returns to `auto-mine`
Current limitation:
- the one-shot `mine-this` order does not yet auto-deliver once before completing
### Example: assignment-driven escort
1. fighter assigned to `commander-subordinate(commanderId, escort)`
2. assignment implies default behavior `escort-assigned`
3. on no direct order, fighter follows commander
4. when direct order ends, fighter resumes escort automatically
## Blocking and Waiting
Blocked execution should be explicit, not hidden inside `idle`.
Common blocked reasons:
- no docking bay free
- no path to destination
- no cargo space
- no valid mining target
- no ammo/fuel/energy
- target out of command scope
- waiting for commander or carrier
Recommended blocked substate or status:
- `blocked(waiting-for-bay)`
- `blocked(waiting-for-target)`
- `blocked(waiting-for-resource)`
- `blocked(waiting-for-order-scope)`
Current implementation:
- blocked status exists on `order`
- blocked reason metadata does not yet exist
## Queue and Override Rules
For RTS feel, orders should support:
- immediate replace
- optional shift-queue
- cancel current order
- clear all queued orders
Recommended semantics:
- normal click command replaces current direct order
- shift-command appends to order queue
- automation only runs when order queue is empty
Current implementation:
- immediate replace behavior exists
- shift queues do not exist yet
## Suggested Data Shape
Current runtime shape is effectively:
```ts
type ShipMind = {
order?: ShipOrder;
defaultBehavior: ShipBehavior;
assignment: ShipAssignment;
controllerTask: ControllerTask;
state: UnitState;
};
```
Future expansion can add:
```ts
type ShipMindExtensions = {
queuedOrders?: ShipOrder[];
blockedReason?: BlockedReason;
stance?: CombatStance;
};
```
## Mapping To Current Project
Current runtime concepts map like this:
| Runtime concept | Current implementation |
| --- | --- |
| direct move | one-shot `order = move-to` |
| direct mining | one-shot `order = mine-this` |
| direct docking | one-shot `order = dock-at` |
| automation mining | `defaultBehavior = auto-mine` |
| automation patrol | `defaultBehavior = patrol` |
| assignment escort | `assignment = commander-subordinate` plus `defaultBehavior = escort-assigned` |
| execution | `controllerTask` |
| physical movement / interaction | `state` |
## Recommended Next Scope
To move the game forward without overbuilding, the highest-value next steps are:
### Direct orders
- `hold-here`
- `attack`
- `escort` as a first-class direct order
### Default behaviors
- `defend-area`
- `trade`
- `resupply`
### Assignments
- `fleet-member`
- `carrier-wing`
- `sector-command`
### Runtime support
- blocked reason handling
- optional order queue
- better event emission
- more explicit threat / target events
### Faction strategy
- reactive shipbuilding priorities
- better pirate harassment of industrial targets
- clearer economic pressure and throughput visibility
## Non-Goals
This model should not force:
- full economic AI before direct control feels good
- full hierarchy simulation before ship orders work
- perfect X4 parity
The target remains a practical hybrid:
- faster and clearer than a pure sim
- deeper and more autonomous than a pure RTS
## Summary
The active design is now:
- one-shot orders for direct RTS control
- default behaviors for persistent automation
- assignments for organizational structure
- controller tasks for immediate execution
- physical states for movement and interaction
That structure supports the gameplay goal cleanly:
- players can micro ships directly
- ships remain useful when not microed
- factions can scale through automation
- the game can feel like an RTS without losing simulation depth