Back to Sickman Survival
SOURCE / PINNED RELEASE

Made of little things.

Sickman Survival

Release
7335c9fdf0a8…
Author-recorded commit
cbd2eeafa70d…
License
LICENSE
Author’s source reference
nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.napplet.soy%2F/n-8804151df7f

Archive hash verified: 591bab4addcbe5fc…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

src/napplet-settings.ts
import { config } from '@napplet/sdk';
import { runtimeHasDomain } from './domain-availability.js';
import settingsSchema from '../config.schema.json';

export type TouchControlsMode = 'auto' | 'always' | 'never';

export interface GameSettings {
  screenShake: boolean;
  touchControls: TouchControlsMode;
  /** 0–1. */
  soundVolume: number;
  rumble: boolean;
}

/** Live settings; defaults match config.schema.json and apply when config is absent. */
export const gameSettings: GameSettings = { screenShake: true, touchControls: 'auto', soundVolume: 0.8, rumble: true };

const listeners = new Set<() => void>();

export function onSettingsChange(listener: () => void): void {
  listeners.add(listener);
}

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) => {
      gameSettings.screenShake = values.screenShake !== false;
      const mode = values.touchControls;
      gameSettings.touchControls = mode === 'always' || mode === 'never' ? mode : 'auto';
      const volume = Number(values.soundVolume);
      gameSettings.soundVolume = Number.isFinite(volume) ? Math.max(0, Math.min(100, volume)) / 100 : 0.8;
      gameSettings.rumble = values.rumble !== false;
      for (const listener of listeners) listener();
    });
    window.addEventListener('pagehide', () => subscription.close(), { once: true });
  })().catch(() => {
    // Optional operations may be unavailable even when a host exposes the domain.
    // The in-app defaults keep the game fully playable.
  });
}