Namespace Core.Units
Classes
- CreateUnitCommand
Command to create a new unit.
VALIDATION:
- Province must be owned by the country
- Country must have sufficient resources (checked by game layer command factory)
- Unit type must be valid
EXECUTION:
- Deduct resources (handled by game layer wrapper)
- Create unit in UnitSystem
- Emit UnitCreatedEvent
- DisbandUnitCommand
Command to disband a unit.
VALIDATION:
- Unit must exist
- Unit must be owned by the executing country (checked by game layer)
EXECUTION:
- Remove unit from UnitSystem
- Refund partial resources (handled by game layer wrapper)
- Emit UnitDestroyedEvent
- MoveUnitCommand
Command to move a unit to a new province using time-based movement (EU4-style). Supports pathfinding for multi-province journeys.
VALIDATION:
- Unit must exist
- Unit must be owned by the executing country
- Pathfinding system must be initialized
- Unit must not already be moving (warning, but allowed)
EXECUTION:
- Calculate path using PathfindingSystem
- Add unit to movement queue with full path
- Unit will automatically hop through waypoints
- Emit UnitMovementStartedEvent
- UnitColdData
Cold data for units - rarely accessed, stored separately from hot data.
DESIGN:
- NOT in NativeArray (uses managed types)
- Loaded on-demand (Dictionary lookup)
- Stores optional/rare data (custom names, history, etc.)
EXAMPLES:
- Custom unit names ("The Old Guard")
- Combat history (battles participated in)
- Achievement tracking (most kills, longest march, etc.)
- UnitMovementQueue
Tracks units that are currently in transit between provinces. Implements EU4-style time-based movement: units take X days to move.
DESIGN:
- Dictionary tracks moving units: unitID → MovementState
- Each day, decrement daysRemaining for all moving units
- When daysRemaining reaches 0, move unit to destination
- Units can cancel movement mid-transit (return to origin)
PERFORMANCE:
- Sparse storage: Only tracks moving units (not all units)
- Daily tick processes O(n) where n = units currently moving
- Typically 10-100 units moving at once (not 10k)
- ZERO ALLOCATIONS: Pre-allocated buffers for daily tick processing
- UnitSystem
Central manager for all units in the game.
ARCHITECTURE:
- Hot data: NativeArray of UnitState (8 bytes each)
- Sparse mapping: Province → Unit IDs (scales with actual units, not possible units)
- Cold data: Dictionary for rare data (custom names, history, etc.)
- Movement data: Separate NativeArray for movement points (2 bytes per unit)
PERFORMANCE:
- 10k units × 8 bytes = 80KB hot data
- 10k units × 2 bytes = 20KB movement data
- Sparse collections scale with usage (not possibility)
- GetUnitsInProvince() is O(m) where m = units in province (typically 1-10)
PERSISTENCE:
- SaveState/LoadState for all data
- Atomic unit ID assignment (deterministic)
- Command pattern ensures multiplayer safety
Structs
- UnitCountChangedEvent
Emitted when unit count changes (combat, reinforcement, etc.) RISK-style: Simple number changes instead of percentage-based strength/morale
- UnitCreatedEvent
Emitted when a new unit is created. UI can subscribe to show notifications, update displays, etc.
- UnitDestroyedEvent
Emitted when a unit is destroyed (disbanded, killed in combat, etc.)
- UnitMovedEvent
Emitted when a unit moves to a new province
- UnitMovementQueue.MovementState
State for a unit currently in transit
- UnitState
8-byte hot data for a single military unit.
DESIGN:
- Fixed size (8 bytes) for cache efficiency and network transmission
- No visual data (positions, sprites) - presentation layer responsibility
- provinceID instead of coordinates - simulation layer doesn't know positions
- RISK-style: Simple unit count instead of percentage-based strength/morale
MULTIPLAYER:
- Deterministic layout (explicit struct layout)
- No managed references (NativeArray compatible)
- Serializable for network sync
Enums
- DestructionReason
Reason for unit destruction (for statistics, events, etc.)