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

94 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Context
The existing `@suaveplan/day-night` package conflates gaming concerns (ECS components, gameplay modifiers, in-game clock) with pseudo-astronomy (hardcoded sinusoidal sun arc, 30-day moon cycle, no lat/lon). The astronomical computation layer is now provided by `@suaveplan/universe` and its provider plugins (`solar-system`, `stars`, `tides`). This design defines the two gaming-layer packages that consume that core.
## Goals / Non-Goals
**Goals:**
- Wrap `UniverseClient` with a configurable N× game clock that drives scene queries at game time.
- Emit semantically meaningful game events (sunrise, season change, eclipse) via `@suaveplan/events`.
- Replace hardcoded lighting presets with physically-derived sky colour and gameplay modifiers.
- Export plain TypeScript interfaces for ECS component shapes so consumers can wire into any ECS.
- Lock in `RenderPayload` as the canonical per-tick output for downstream render integrations.
- Achieve 100% test coverage, Zod schemas for all public options, OTel instrumentation, and `AbortSignal` on every async method.
**Non-Goals:**
- Astronomical calculations — delegated to `@suaveplan/universe` and its providers.
- R3F / Three.js rendering integration.
- Multiplayer synchronisation of game time across clients.
- `@suaveplan/weather` integration (locked interface but no implementation).
- Bundling texture assets — only identifiers pass through.
## Decisions
### 1. Two-package split: clock vs lighting
`gaming-universe` owns time and events. `gaming-environment-lighting` owns sky colour and modifiers. This separation allows consumers to use the clock without the lighting system, or replace lighting with a custom renderer while keeping the clock.
**Rationale**: Single-responsibility. The clock is a scheduler concern; lighting is a render-data concern.
### 2. `GamingUniverse` wraps `@suaveplan/timing`'s `AsyncScheduler`
Rather than inventing a scheduler, `GamingUniverse` applies a time-scale multiplier to `timing`'s ticks.
**Rationale**: Uniform cancellation, clock injection, and AbortSignal drain with the rest of the monorepo. `@suaveplan/game-loop` becomes the natural tick integration point.
### 3. Plain TypeScript interfaces for ECS component shapes
`gaming-environment-lighting` exports plain interfaces (`DayNightClock`, `LightState`, `DayNightAffected`) rather than registering ECS archetypes.
**Rationale**: Framework-agnostic. Consumers using bitecs, miniplex, or custom ECS map fields to their own storage. No `@suaveplan/ecs` dependency.
### 4. `RenderPayload` as canonical render output
A single `RenderPayload` interface is published each tick containing sky colour, lighting, atmosphere, textures, and weather.
**Rationale**: Prevents churn when R3F/Three.js integrations land. Downstream renderers depend on a stable data shape, not on the lighting package's internals.
### 5. Feature-module directory layout — Pattern B
Every feature module uses a dedicated folder containing a main implementation file, a barrel `index.ts` with explicit named re-exports, sibling `.md` docs, and co-located tests.
Example:
```text
src/lighting/
├── index.ts
├── lighting.ts
├── lighting.test.ts
└── lighting.md
```
The `package.json` exports map uses the repeated-name form (`./lighting/lighting`) pointing to the main implementation file, not to `index.ts`.
**Rationale**: The barrel is reviewable and explicit. The main file name makes stack traces and import paths self-describing. Tests and docs are co-located so they stay in sync.
### 6. Physically-derived everything
Sky colour uses Rayleigh/Mie scattering from `AtmosphereConfig`. Star visibility uses sun-altitude twilight thresholds. Moonlight uses phase, angular diameter, and albedo. Gameplay modifiers are continuous functions of `totalAmbientLight`.
**Rationale**: No hardcoded tables means the same code works for Earth, Mars, and game-defined planets without preset authoring.
## Risks / Trade-offs
| Risk | Mitigation |
|---|---|
| Rayleigh/Mie model is simplified (no spectral sampling) | Documented as approximate. Consumers needing spectral accuracy can override `skyColour` in `RenderPayload`. |
| NPC sleep probability sigmoid may not match game design | Exposed as a configurable function in advanced options, with a sensible default. |
| `gaming-universe` depends on `@suaveplan/timing` scheduler internals | `timing` is a foundation package with stable API. If scheduler changes, `gaming-universe` is a known dependent. |
| Two packages to migrate from one `day-night` | Migration guide documents which `day-night` exports map to which new package. |
## Migration Plan
1. Land `gaming-universe` after `universe` is complete.
2. Land `gaming-environment-lighting` after `gaming-universe` is complete.
3. Update `STATUS.md` and `SUMMARY.md` as each package completes.
4. Archive `@suaveplan/day-night` after `gaming-environment-lighting` ships.
5. Publish migration guide mapping `day-night` exports to new package exports.
## Open Questions
- None — all deferred items are explicitly scoped out with locked interfaces.