544 lines
16 KiB
Markdown
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
|