SOURCE / PINNED RELEASE
Made of little things.
Obsidian Breaker
- Release
- 44c589c52043…
- Author-recorded commit
- 9ee5b61ce1c9…
- License
- LICENSE
- Author’s source reference
- nostr://npub1dmmh0futyszl4445mv4xrygkgsgj8vpu79yx3qyy9qxmz4pemtzqlqmc87/wss%3A%2F%2Fgit.napplet.soy%2F/n-de5dc6a02e7
Archive hash verified: 240ec416a9cbfd9c…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
import { config } from '@napplet/sdk';
import { runtimeHasDomain } from './domain-availability.js';
import settingsSchema from '../config.schema.json';
export type Difficulty = 'chill' | 'standard' | 'ruthless';
export type AimPace = 'slow' | 'steady' | 'quick';
export interface GameSettings {
difficulty: Difficulty;
aimPace: AimPace;
showGuide: boolean;
screenShake: boolean;
}
export const DEFAULT_SETTINGS: GameSettings = {
difficulty: 'standard',
aimPace: 'steady',
showGuide: true,
screenShake: true,
};
type SettingsListener = (settings: GameSettings) => void;
const listeners = new Set<SettingsListener>();
let current: GameSettings = { ...DEFAULT_SETTINGS };
function normalize(values: Record<string, unknown>): GameSettings {
const difficulty =
values.difficulty === 'chill' || values.difficulty === 'ruthless'
? values.difficulty
: 'standard';
const aimPace =
values.aimPace === 'slow' || values.aimPace === 'quick' ? values.aimPace : 'steady';
return {
difficulty,
aimPace,
showGuide: typeof values.showGuide === 'boolean' ? values.showGuide : DEFAULT_SETTINGS.showGuide,
screenShake:
typeof values.screenShake === 'boolean' ? values.screenShake : DEFAULT_SETTINGS.screenShake,
};
}
export function gameSettings(): GameSettings {
return current;
}
export function onGameSettings(listener: SettingsListener): () => void {
listeners.add(listener);
listener(current);
return () => {
listeners.delete(listener);
};
}
// Config is optional here: the game ships with complete local defaults.
if (runtimeHasDomain('config')) {
void (async () => {
// Some hosts consume the embedded schema; others need standard registration.
// Do not replace a schema that the host already supplied.
if (!config.schema) {
await config.registerSchema(settingsSchema as Parameters<typeof config.registerSchema>[0]);
}
const subscription = config.subscribe((values) => {
current = normalize(values);
for (const listener of listeners) listener(current);
});
window.addEventListener('pagehide', () => subscription.close(), { once: true });
})().catch(() => {
// Optional operations may be unavailable even when a host exposes the domain.
// Keep the local defaults so the game still runs.
});
}
