Files
neon-survivors-dev 796880375e NEON SURVIVORS v1.1 — full-featured bullet-heaven survivor game
Vanilla JS + Canvas, zero dependencies, offline-first PWA.

Gameplay:
- 11 weapons x8 levels + 11 evolutions (chest-based), incl. timed mines
- 13 passives, crit system with directional hit-sparks & hit-stop
- 10 characters w/ unique mods + unlock conditions, gold cosmetic skins
- 3 biomes (Neon Graveyard / Frozen Hollow / Magma Rift) each with own
  spawn tables, boss plans and music flavor; Endless mode + surges;
  4 difficulty grades; breakable crystal-lamp props
- Elite random affixes (Swift/Sturdy/Volatile), 4 bosses, win flow
- Achievements (23) w/ gold rewards, run history, daily seeded challenge

Tech:
- Cinematic canvas main-menu scene, game-feel FX suite (trails, muzzle,
  status tints, low-HP pulse), viewport culling + particle pooling
- WebAudio synth SFX + generative per-biome soundtrack
- Gamepad support, remappable keys, touch joystick, fullscreen
- i18n VI/EN, localStorage saves w/ export-import codes
- Cloudflare Workers leaderboard scaffold (KV) w/ signed submits
- Headless integrity test-suite (node test/integrity.js)
2026-08-23 07:01:22 +00:00

52 lines
1.3 KiB
JavaScript

/* Neon Survivors — service worker (cache-first, versioned) */
'use strict';
const CACHE = 'neon-survivors-v1';
const ASSETS = [
'./',
'./index.html',
'./manifest.webmanifest',
'./css/style.css',
'./icons/icon-192.png',
'./icons/icon-512.png',
'./js/util.js',
'./js/i18n.js',
'./js/save.js',
'./js/audio.js',
'./js/data.js',
'./js/entities.js',
'./js/systems.js',
'./js/render.js',
'./js/ui.js',
'./js/game.js',
'./js/main.js'
];
self.addEventListener('install', (e) => {
e.waitUntil(
caches.open(CACHE).then(c => c.addAll(ASSETS)).then(() => self.skipWaiting())
);
});
self.addEventListener('activate', (e) => {
e.waitUntil(
caches.keys()
.then(keys => Promise.all(keys.filter(k => k !== CACHE).map(k => caches.delete(k))))
.then(() => self.clients.claim())
);
});
self.addEventListener('fetch', (e) => {
if (e.request.method !== 'GET') return;
const url = new URL(e.request.url);
if (url.origin !== location.origin) return;
e.respondWith(
caches.match(e.request, { ignoreSearch: true }).then(hit =>
hit || fetch(e.request).then(res => {
const copy = res.clone();
caches.open(CACHE).then(c => c.put(e.request, copy));
return res;
}).catch(() => hit)
)
);
});