Files
2026-05-27 23:29:53 +01:00

152 lines
4.2 KiB
Markdown
Raw Permalink 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.
# OpenSpec Change: enterprise-gaming-day-night
- **Change ID**: enterprise-gaming-day-night
- **Package**: `@suaveplan/day-night`
- **Category**: gaming/world
- **Target path**: `gaming/world/day-night`
- **Status**: PROPOSED
- **Date**: 2026-04-24
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/day-night.md`
---
## Summary
Deliver `@suaveplan/day-night` on the genesis branch from scratch at enterprise quality. The package provides a day/night cycle with configurable time scale, sun/moon position math, light schedule interpolation, and gameplay effect modifiers. `DayNightClock` advances in-game time at a configurable multiple of real time; `SkyPositionCalculator` computes sun and moon azimuth/elevation from in-game time using a simplified celestial model; `LightSchedule` keyframes ambient light color and intensity across the day; `GameplayModifiers` maps time-of-day segments to gameplay effect multipliers (spawn rate, visibility, economy). Genesis adds Zod schemas, structured errors, OTel time counter, injectable clock, and 100% coverage.
---
## Current-state audit (genesis)
Package does not exist on genesis yet — fresh build.
---
## Available workspace deps on genesis
| Package | Role |
|---|---|
| `@suaveplan/error` (`workspace:^`) | `DayNightError`; `DAY_NIGHT.*` codes |
| `@suaveplan/types` (`workspace:^`) | `Clock` |
| `@suaveplan/events` (`workspace:^`) | `time:dawn`, `time:noon`, `time:dusk`, `time:midnight` events |
| `zod` (`catalog:forms`) | `LightSchedule`, `GameplayModifiers` schemas |
| `@opentelemetry/api` (`catalog:observability`) | In-game time gauge |
---
## Public API surface
```ts
function createDayNightClock(options: DayNightClockOptions): DayNightClock
interface DayNightClockOptions {
timeScale?: number // real seconds per in-game minute, default 1 min = 1 sec
initialHour?: number // 0–24
clock?: Clock
}
interface DayNightClock {
tick(dt: number): void
readonly hour: number // 0–24 fractional
readonly dayCount: number
setHour(hour: number): void
pause(): void
resume(): void
readonly isPaused: boolean
}
interface SkyPosition {
azimuth: number // degrees
elevation: number // degrees (-90 to 90)
intensity: number // 0–1
}
function computeSunPosition(hour: number): SkyPosition
function computeMoonPosition(hour: number): SkyPosition
// Light schedule
interface LightKeyframe {
hour: number
ambientColor: [r: number, g: number, b: number]
ambientIntensity: number
fogDensity?: number
}
function createLightSchedule(keyframes: LightKeyframe[]): LightSchedule
interface LightSchedule {
evaluate(hour: number): LightKeyframe
}
// Gameplay modifiers
interface TimeModifier {
fromHour: number
toHour: number
spawnRateMultiplier?: number
visibilityMultiplier?: number
economyMultiplier?: number
label?: string
}
function createGameplayModifiers(modifiers: TimeModifier[]): GameplayModifiers
interface GameplayModifiers {
evaluate(hour: number): TimeModifier & Required<Omit<TimeModifier, 'label' | 'fromHour' | 'toHour'>>
}
```
### Error codes
```ts
type DayNightErrorCode =
| 'DAY_NIGHT_INVALID_HOUR'
| 'DAY_NIGHT_SCHEDULE_EMPTY'
| 'DAY_NIGHT_KEYFRAME_GAP'
```
---
## File layout
```text
gaming/world/day-night/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
constants/index.ts
types/common.ts
types/common.md
core/
day-night-clock/
day-night-clock.ts
day-night-clock.md
day-night-clock.types.ts
day-night-clock.test.ts
index.ts
light-schedule/
light-schedule.ts
light-schedule.md
light-schedule.types.ts
light-schedule.test.ts
index.ts
gameplay-modifiers/
gameplay-modifiers.ts
gameplay-modifiers.md
gameplay-modifiers.types.ts
gameplay-modifiers.test.ts
index.ts
utils/
sky-position.ts
sky-position.md
sky-position.test.ts
tests/
integration.test.ts
```
---
## Impact
**Downstream**: `@suaveplan/r3f-environment` uses sun/moon positions for directional light; `@suaveplan/weather` reads time for weather transitions. Prerequisites: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`.