Back to Big Rig Rush
SOURCE / PINNED RELEASE

Made of little things.

Big Rig Rush

Release
ec9b6549f752…
Author-recorded commit
74b729258b1a…
License
LICENSE
Author’s source reference
nostr://npub1sj2vketepqw9v6fmge5fdzuj7atj9sv6qv2jss05n2z6rl64lcfqumdsnt/wss%3A%2F%2Fgit.napplet.soy%2F/n-0a99f0cae40

Archive hash verified: 900e22810340ccb1…. 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';

// The upstream Vite plugin embeds config.schema.json into the signed build.
// Config is optional: without it the game runs on these defaults.

export type TrafficLevel = 'light' | 'normal' | 'rush';
export type TouchControlsMode = 'auto' | 'always' | 'never';

export interface GameSettings {
  traffic: TrafficLevel;
  rudeness: number;
  touchControls: TouchControlsMode;
  screenShake: boolean;
  soundVolume: number;
}

export const settings: GameSettings = {
  traffic: 'normal',
  rudeness: 45,
  touchControls: 'auto',
  screenShake: true,
  soundVolume: 80,
};

const listeners = new Set<(value: GameSettings) => void>();

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

function applyValues(values: Record<string, unknown>): void {
  if (values.traffic === 'light' || values.traffic === 'normal' || values.traffic === 'rush') {
    settings.traffic = values.traffic;
  }
  if (typeof values.rudeness === 'number' && Number.isFinite(values.rudeness)) {
    settings.rudeness = Math.max(0, Math.min(100, values.rudeness));
  }
  if (values.touchControls === 'auto' || values.touchControls === 'always' || values.touchControls === 'never') {
    settings.touchControls = values.touchControls;
  }
  if (typeof values.screenShake === 'boolean') settings.screenShake = values.screenShake;
  if (typeof values.soundVolume === 'number' && Number.isFinite(values.soundVolume)) {
    settings.soundVolume = Math.max(0, Math.min(100, values.soundVolume));
  }
  for (const listener of listeners) listener(settings);
}

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) => applyValues(values as Record<string, unknown>));
    window.addEventListener('pagehide', () => subscription.close(), { once: true });
  })().catch(() => {
    // Optional operations may be unavailable even when a host exposes the domain.
  });
}