Hold the doorway. Raise the dead. Outlast the king.
Norman stands his ground in a crumbling corridor, flinging bone magic at waves of villagers, archers and knights. Every kill drops a corpse you can raise into a skeleton ally with Resurrect, and the souls you collect buy rituals between floors that twist the run in your favor. Ten floors deep, the King is waiting.
Made by Dan Prince. Released under the Unlicense license — original source.
Changes for this platform: Switched index.ts's keyboard handling from the legacy numeric event.which to event.code, since the platform's touch shim synthesizes KeyboardEvents that only set .key/.code, not the legacy fields (same category of fix as hexgl-racer's ShipControls.js change). Replaced Player()'s onDeath handler in objects.ts -- upstream hard-reset the whole page on death via `window.location = window.location`, an instant, feedback-free restart that bypassed the game's own (declared but previously unused) LOSE state -- with code that sets game.state = LOSE and shows a 'Norman has fallen...' message; a tap on that screen now reloads the frame instead, same end result, with a visible beat first. Added a small window.__norman bridge hook in index.ts exposing pause()/resume() (closured `paused` state arcade-bridge.js can't otherwise reach) and status() returning [state, level, souls] as a plain array -- deliberately not a named object, since terser's production property-mangling (vite.config.ts) renames unusual property names like `souls` but happens to spare ones that collide with built-in DOM properties (state, level, pause, resume), which isn't safe to depend on. Added KeyM mute, which upstream never had, wired through a new exported setMuted() in sounds.ts that drives the existing masterGain node. Guarded the LOSE/WIN states in onpointerup so a tap there means 'restart' instead of firing another spell. Added arcade-bridge.js (new file, not a patch to the original game): calls ArcadeSDK.ready() immediately since the title dialogue renders on the game's own canvas from the first frame, polls window.__norman.status() once a second to report score (souls) and fire gameOver()/submitScore() exactly once when the run ends, reloads the frame on a post-game tap, and forwards ArcadeSDK.onPause/onResume plus a visibilitychange fallback into the game's pause flag. Wrapped the Vite single-file build output (which Vite/the project's own 'vite:single-file' plugin deliberately emits without <html>/<head>/</body> tags, as a js13k byte-golfing trick) in a real <html><head>...</head> document with a charset, title, viewport meta and an html,body{margin:0;height:100%;overflow:hidden;background:#000} base style, all required by docs/GAME_RUNTIME.md's hard rules and absent from the raw js13k entry; none of the inlined game script itself was touched by this. vite.config.ts already built with base: './', so the Vite output never had the absolute '/assets/...' path problem other Vite imports can hit; verified by grepping the built index.html for '="/' (no matches).
Comments 0
Log in to comment.