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

227 lines
9.2 KiB
HTML

<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<meta name="mobile-web-app-capable" content="yes">
<meta name="theme-color" content="#7c5cff">
<link rel="manifest" href="manifest.webmanifest">
<link rel="apple-touch-icon" href="icons/icon-192.png">
<title>NEON SURVIVORS — Bullet Heaven</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🎮</text></svg>">
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<canvas id="game"></canvas>
<!-- FPS counter -->
<div id="fpsMeter" class="hidden">60</div>
<!-- Touch joystick -->
<div id="touchLayer" class="hidden">
<div id="stickBase"><div id="stickKnob"></div></div>
</div>
<!-- ===== UI OVERLAY ===== -->
<div id="ui">
<!-- Loading splash -->
<div class="screen" id="scr-loading">
<div class="logo-pop">NEON<span>SURVIVORS</span></div>
<div class="loading-bar"><i></i></div>
</div>
<!-- Main menu -->
<div class="screen hidden" id="scr-menu">
<div class="menu-wrap">
<header class="menu-head">
<h1 class="logo"><span class="l1">NEON</span><span class="l2">SURVIVORS</span></h1>
<p class="tagline" data-i18n="tagline"></p>
<div class="ver-chip">BULLET HEAVEN · v1.1</div>
</header>
<nav class="menu-btns">
<button class="mbtn primary" id="bt-play"><i></i><span data-i18n="btn_play"></span></button>
<button class="mbtn" id="bt-daily"><i></i><span data-i18n="btn_daily"></span></button>
<button class="mbtn" id="bt-shop"><i>🛒</i><span data-i18n="ms_title"></span></button>
<button class="mbtn" id="bt-codex"><i>📖</i><span data-i18n="btn_codex"></span></button>
<button class="mbtn" id="bt-history"><i>📜</i><span data-i18n="btn_history"></span></button>
<button class="mbtn" id="bt-board"><i>🥇</i><span data-i18n="btn_board"></span></button>
<button class="mbtn" id="bt-options"><i>⚙️</i><span data-i18n="btn_options"></span></button>
</nav>
<footer class="menu-foot">
<div class="menu-stats">
<span class="chip">🪙 <b id="menuGold">0</b></span>
<span class="chip">💀 <b id="menuKills">0</b></span>
<span class="chip">🏆 <b id="menuBest">00:00</b></span>
</div>
<p class="hint" data-i18n="tip_move"></p>
</footer>
</div>
</div>
<!-- Character select -->
<div class="screen hidden" id="scr-chars">
<header class="scr-head"><button class="btn btn-icon back-btn"></button><h2 data-i18n="scr_chars"></h2><span class="head-gold">🪙 <b>0</b></span></header>
<div class="card-grid" id="charGrid"></div>
</div>
<!-- Stage select -->
<div class="screen hidden" id="scr-stage">
<header class="scr-head"><button class="btn btn-icon back-btn"></button><h2 data-i18n="st_select"></h2><span class="head-gold">🪙 <b>0</b></span></header>
<div class="stage-grid" id="stageGrid"></div>
<div class="stage-opts" id="stageOpts"></div>
<div class="start-bar" style="position:absolute;bottom:1rem;left:0;right:0;display:flex;justify-content:center;z-index:5">
<button class="btn btn-primary btn-big" id="bt-stage-go"></button>
</div>
</div>
<!-- Run history -->
<div class="screen hidden" id="scr-history">
<header class="scr-head"><button class="btn btn-icon back-btn"></button><h2 data-i18n="btn_history"></h2><span></span></header>
<div class="hist-summary" id="histSummary"></div>
<div class="hist-list" id="histList"></div>
</div>
<!-- Meta shop -->
<div class="screen hidden" id="scr-shop">
<header class="scr-head"><button class="btn btn-icon back-btn"></button><h2 data-i18n="ms_title"></h2><span class="head-gold">🪙 <b>0</b></span></header>
<div class="shop-grid" id="shopGrid"></div>
</div>
<!-- Codex -->
<div class="screen hidden" id="scr-codex">
<header class="scr-head"><button class="btn btn-icon back-btn"></button><h2 data-i18n="btn_codex"></h2><span></span></header>
<nav class="tabs" id="codexTabs">
<button class="tab active" data-tab="weapons" data-i18n="cx_weapons"></button>
<button class="tab" data-tab="passives" data-i18n="cx_passives"></button>
<button class="tab" data-tab="enemies" data-i18n="cx_enemies"></button>
<button class="tab" data-tab="chars" data-i18n="cx_chars"></button>
<button class="tab" data-tab="ach">🏆 <span data-i18n="cx_ach"></span></button>
</nav>
<div class="codex-list" id="codexList"></div>
</div>
<!-- Options -->
<div class="screen hidden" id="scr-options">
<header class="scr-head"><button class="btn btn-icon back-btn"></button><h2 data-i18n="btn_options"></h2><span></span></header>
<div class="opt-panel" id="optPanel"></div>
</div>
<!-- HUD -->
<div class="hidden" id="hud">
<div id="xpBar"><i id="xpFill"></i><span id="lvlTag">LV 1</span></div>
<div id="hudTop">
<div id="hudPortrait"><span id="portraitSym">🧙</span><em id="hudLevel">1</em></div>
<div id="hudTimer">00:00</div>
<div id="hudRight"><span id="hudGold">🪙 0</span><span id="hudKills">💀 0</span></div>
</div>
<div id="bossBar" class="hidden"><label id="bossName"></label><div class="bb-track"><i id="bossFill"></i></div></div>
<div id="slotWeapons"></div>
<div id="slotPassives"></div>
</div>
<!-- Pause -->
<div class="modal hidden" id="md-pause">
<div class="panel">
<h2 data-i18n="scr_pause"></h2>
<div id="pauseStats" class="pause-stats"></div>
<div class="col-btns">
<button class="btn btn-primary" id="bt-resume" data-i18n="btn_resume"></button>
<button class="btn" id="bt-poptions" data-i18n="btn_options"></button>
<button class="btn btn-danger" id="bt-quit" data-i18n="btn_quitmenu"></button>
</div>
</div>
</div>
<!-- Level up -->
<div class="modal hidden" id="md-level">
<div class="panel panel-up">
<h2 data-i18n="lv_title"></h2>
<p class="sub" data-i18n="lv_choose"></p>
<div id="levelChoices" class="choice-list"></div>
<div class="row-btns">
<button class="btn btn-small" id="bt-reroll"></button>
<button class="btn btn-small" id="bt-skipLv"></button>
</div>
</div>
</div>
<!-- Chest -->
<div class="modal hidden" id="md-chest">
<div class="panel panel-chest">
<div class="chest-art" id="chestArt">🎁</div>
<h2 id="chestTitle"></h2>
<div id="chestBody"></div>
<button class="btn btn-primary" id="bt-chestOk" data-i18n="btn_continue"></button>
</div>
</div>
<!-- Game over / victory -->
<div class="modal hidden" id="md-over">
<div class="panel panel-over">
<h2 id="overTitle"></h2>
<p id="overRecord" class="record hidden" data-i18n="es_newrec"></p>
<div class="over-grid" id="overStats"></div>
<div class="col-btns">
<button class="btn btn-primary" id="bt-retry" data-i18n="btn_restart"></button>
<button class="btn" id="bt-tomenu" data-i18n="btn_quitmenu"></button>
</div>
</div>
</div>
<!-- Developer tools -->
<button id="devBtn" class="hidden" title="Developer Tools (`)">🔧</button>
<div class="modal hidden" id="md-dev">
<div class="panel panel-dev" id="devPanel"></div>
</div>
<!-- Save export / import -->
<div class="modal hidden" id="md-save">
<div class="panel panel-save">
<h3 data-i18n="save_title"></h3>
<textarea id="saveArea" rows="7" spellcheck="false"></textarea>
<div class="save-btns">
<button class="btn btn-small" id="bt-save-copy" data-i18n="save_copy"></button>
<button class="btn btn-small" id="bt-save-import" data-i18n="save_import"></button>
<button class="btn btn-small" id="bt-save-close" data-i18n="btn_close"></button>
</div>
</div>
</div>
<!-- Leaderboard -->
<div class="modal hidden" id="md-lb">
<div class="panel panel-lb">
<h3 data-i18n="lb_title"></h3>
<div class="seg lb-scopes" id="lbScopes"></div>
<div class="lb-list" id="lbList"></div>
<div class="save-btns"><button class="btn btn-small" id="bt-lb-close" data-i18n="btn_close"></button></div>
</div>
</div>
<!-- Tutorial bubble -->
<div id="tutTip" class="hidden">
<span id="tutText"></span>
<button id="bt-tut-skip"></button>
</div>
<!-- Toasts -->
<div id="toasts"></div>
</div>
<script src="js/util.js"></script>
<script src="js/i18n.js"></script>
<script src="js/save.js"></script>
<script src="js/audio.js"></script>
<script src="js/data.js"></script>
<script src="js/achievements.js"></script>
<script src="js/entities.js"></script>
<script src="js/systems.js"></script>
<script src="js/render.js"></script>
<script src="js/ui.js"></script>
<script src="js/game.js"></script>
<script src="js/main.js"></script>
</body>
</html>