Files
suaveplan/openspec/changes/enterprise-gaming-universe/specs/gaming-universe/spec.md
2026-05-27 23:29:53 +01:00

87 lines
2.8 KiB
Markdown

## ADDED Requirements
### Requirement: GamingUniverse construction
The system SHALL export `GamingUniverse` accepting `startTime`, `timeScale`, `UniverseClient`, and optional `clock`.
#### Scenario: Construction
- **WHEN** `new GamingUniverse({ startTime, timeScale: 3600, universe })` is called
- **THEN** the instance is created with `getGameTime()` returning `startTime`
### Requirement: Time scale control
The system SHALL provide `setTimeScale`, `pause`, `resume`, and `isPaused`.
#### Scenario: Pause and resume
- **WHEN** `pause()` is called
- **THEN** `isPaused()` returns `true`
- **AND** `getGameTime()` stops advancing
- **AND** after `resume()`, `isPaused()` returns `false`
### Requirement: Set game time directly
The system SHALL provide `setGameTime` to override the clock to a specific date.
#### Scenario: Direct time set
- **WHEN** `setGameTime(futureDate)` is called
- **THEN** `getGameTime()` returns `futureDate`
### Requirement: Scene queries at game time
The system SHALL delegate `getScene` to `UniverseClient.getScene` using the current game time.
#### Scenario: Scene query
- **WHEN** `getScene(observer)` is called while paused
- **THEN** the query uses the paused game time, not real time
### Requirement: AsyncScheduler integration
The system SHALL wrap `@suaveplan/timing`'s `AsyncScheduler` to drive game time ticks.
#### Scenario: Scheduler usage
- **WHEN** `GamingUniverse` is constructed
- **THEN** it creates or references an `AsyncScheduler` from `@suaveplan/timing`
- **AND** `dispose()` stops the scheduler
### Requirement: Event emission
The system SHALL emit events via `@suaveplan/events` for `hourChanged`, `dayChanged`, `seasonChanged`, `sunriseStarted`, `sunsetStarted`, `moonriseStarted`, `moonsetStarted`, and `eclipseStarted`.
#### Scenario: Day change event
- **WHEN** game time advances across a midnight boundary
- **THEN** a `dayChanged` event is emitted with the new `gameTime` and `dayNumber`
### Requirement: Signal cancellation
The system SHALL pass `AbortSignal` through to `UniverseClient.getScene` and cancel in-flight queries.
#### Scenario: Abort in-flight
- **WHEN** `getScene` is called and the signal is aborted
- **THEN** the promise rejects with an abort error
### Requirement: Dispose idempotency
The system SHALL provide `dispose()` that stops the clock and cancels all in-flight queries, callable multiple times safely.
#### Scenario: Double dispose
- **WHEN** `dispose()` is called twice
- **THEN** no error is thrown
- **AND** the instance is fully shut down after the first call
### Requirement: UniverseClient exposure
The system SHALL expose the underlying `UniverseClient` via a readonly `universe` property.
#### Scenario: Access underlying client
- **WHEN** `gamingUniverse.universe` is accessed
- **THEN** it returns the `UniverseClient` passed at construction