94 lines
5.0 KiB
Markdown
94 lines
5.0 KiB
Markdown
## 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.
|