nordic-skating / local-littlejs / files / src / settings.ts
Branch local-littlejs · default branch · all branches
settings.ts
a8fd3f13 2026-10-10 Pavel Safronov | 1 | import { TIME_NAMES, type TimeName } from './render/sky.js' |
1ee103b4 2026-10-11 Pavel Safronov | 2 | import { PACE_NAMES, type Pace } from './skater.js' |
a8fd3f13 2026-10-10 Pavel Safronov | 3 | import { mulberry32 } from './world/lake.js' |
| 4 | ||
236f7d03 2026-10-10 Pavel Safronov | 5 | /** The weathers a lake can have. */ |
| 6 | export const WEATHERS = ['clear', 'snow', 'blizzard'] as const | |
| 7 | export type Weather = (typeof WEATHERS)[number] | |
| 8 | ||
| 9 | /** | |
| 10 | * What a weather is: how much snow is falling, from 0 to 1; how far the eye can see | |
| 11 | * through it, metres; how much of the sky is cloud; and how hard the wind blows, m/s, | |
| 12 | * at its lulls and in its gusts. | |
| 13 | */ | |
| 14 | export const WEATHER: Record<Weather, { snow: number; sight: number; overcast: number; wind: readonly [number, number] }> = { | |
| 15 | clear: { snow: 0, sight: 600, overcast: 0, wind: [0, 0] }, | |
| 16 | snow: { snow: 0.35, sight: 420, overcast: 0.7, wind: [0.5, 3] }, | |
| 17 | blizzard: { snow: 1, sight: 200, overcast: 0.95, wind: [7, 16] }, | |
| 18 | } | |
| 19 | ||
a8fd3f13 2026-10-10 Pavel Safronov | 20 | /** What the skater has chosen in the settings box, kept from one visit to the next. */ |
| 21 | export interface Settings { | |
| 22 | /** A time of day, or `surprise` for whichever the lake comes with. */ | |
| 23 | time: TimeName | 'surprise' | |
236f7d03 2026-10-10 Pavel Safronov | 24 | /** The weather, or `surprise` for whichever the lake comes with. */ |
| 25 | weather: Weather | 'surprise' | |
1ee103b4 2026-10-11 Pavel Safronov | 26 | /** The pace the skater keeps (skater.ts). */ |
| 27 | pace: Pace | |
a8fd3f13 2026-10-10 Pavel Safronov | 28 | /** Whether the lake has ice thinner than any ice could be (world/lake.ts). */ |
| 29 | impossibleIce: boolean | |
8876a48d 2026-10-10 Pavel Safronov | 30 | /** Whether tilting the phone steers (input.ts). */ |
| 31 | tilt: boolean | |
a8fd3f13 2026-10-10 Pavel Safronov | 32 | } |
| 33 | ||
1ee103b4 2026-10-11 Pavel Safronov | 34 | const DEFAULTS: Settings = { time: 'surprise', weather: 'surprise', pace: 'cruise', impossibleIce: false, tilt: false } |
a8fd3f13 2026-10-10 Pavel Safronov | 35 | const KEY = 'settings' |
| 36 | ||
| 37 | export function loadSettings(): Settings { | |
| 38 | try { | |
| 39 | const kept = JSON.parse(localStorage.getItem(KEY) ?? '{}') as Partial<Settings> | |
| 40 | return { | |
| 41 | time: kept.time === 'surprise' || TIME_NAMES.includes(kept.time as TimeName) ? kept.time! : DEFAULTS.time, | |
236f7d03 2026-10-10 Pavel Safronov | 42 | weather: kept.weather === 'surprise' || WEATHERS.includes(kept.weather as Weather) ? kept.weather! : DEFAULTS.weather, |
1ee103b4 2026-10-11 Pavel Safronov | 43 | pace: PACE_NAMES.includes(kept.pace as Pace) ? kept.pace! : DEFAULTS.pace, |
a8fd3f13 2026-10-10 Pavel Safronov | 44 | impossibleIce: kept.impossibleIce === true, |
8876a48d 2026-10-10 Pavel Safronov | 45 | tilt: kept.tilt === true, |
a8fd3f13 2026-10-10 Pavel Safronov | 46 | } |
| 47 | } catch { | |
| 48 | // No storage, or nonsense in it: the settings last for this visit only. | |
| 49 | return { ...DEFAULTS } | |
| 50 | } | |
| 51 | } | |
| 52 | ||
| 53 | export function saveSettings(settings: Settings): void { | |
| 54 | try { | |
| 55 | localStorage.setItem(KEY, JSON.stringify(settings)) | |
| 56 | } catch { | |
| 57 | // As above. | |
| 58 | } | |
| 59 | } | |
| 60 | ||
| 61 | /** A lake to skate that nobody has asked for by number: one of a million. */ | |
| 62 | export function anyLake(): number { | |
| 63 | return 1 + Math.floor(Math.random() * 999_999) | |
| 64 | } | |
| 65 | ||
| 66 | /** | |
| 67 | * The time of day lake number `seed` comes with. Part of the lake, so that a link to a | |
| 68 | * lake is a link to the same water under the same sky. Day is the likeliest, and the | |
| 69 | * aurora, as it should be, the least. | |
| 70 | */ | |
| 71 | export function timeOf(seed: number): TimeName { | |
| 72 | const r = mulberry32(seed ^ 0x5bd1e995)() | |
| 73 | return r < 0.35 ? 'day' : r < 0.55 ? 'dusk' : r < 0.82 ? 'night' : 'aurora' | |
| 74 | } | |
236f7d03 2026-10-10 Pavel Safronov | 75 | |
| 76 | /** | |
| 77 | * The weather lake number `seed` comes with. Most days are clear; snow falls on a | |
| 78 | * quarter of them, and a blizzard blows on one in ten. | |
| 79 | */ | |
| 80 | export function weatherOf(seed: number): Weather { | |
| 81 | const r = mulberry32(seed ^ 0x3c6ef372)() | |
| 82 | return r < 0.64 ? 'clear' : r < 0.9 ? 'snow' : 'blizzard' | |
| 83 | } | |
| 84 | ||
| 85 | /** Which way the wind blows over lake number `seed`, radians: 0 is down the lake, a quarter turn is across it to the right. */ | |
| 86 | export function windOf(seed: number): number { | |
| 87 | return mulberry32(seed ^ 0x1b873593)() * Math.PI * 2 | |
| 88 | } |