Snarky\'s
LOADING
Arrows / WASDFlyZ / SpaceShootXSmart bombCHyperspaceEscPause
shooter#shooter#scrolling#rescue#wave-based#retro

Hold the ridge. Save every human.

A side-scrolling base-defense shooter: fly over a wrapping alien ridgeline, blast descending landers before they haul your humans to the top and mutate, and catch anyone falling before they splat. Lose all ten humans and the planet is destroyed — survive three mutant waves to win it back. Smart bombs and hyperspace help in a pinch, but use them wisely.

0 plays0 players0 remixesPublished 5d ago
Open source

Made by Frank Force. Released under the MIT license — original source.

Changes for this platform: Rewrote every ../templates/ script src to ./ (engineLoader.js, textureGenerator.js, gameFx.js, menus.js) and vendored littlejs.release.js from ../dist/ into this folder, since the platform's self-contained game folders don't share a repo-root dist/ or templates/ the way the upstream arcade cabinet does. Replaced templates/engineLoader.js with a 3-line-patched copy (documented in its own header comment): engine path ../dist/ -> ./; littlejsBuild()'s console-debug localStorage calls wrapped in try/catch; and a new inline script that routes readSaveData/writeSaveData through window.ArcadeSDK.storage when present (falls back to upstream's own try/catch-guarded localStorage when the game runs standalone outside the platform). Renamed the internal saveDataInit('defender') save-slot key to saveDataInit('protector') for cleanliness (fresh save, no migration concern; it was never player-visible). Changed <title> and the window title from "LittleJS Protector" to "Protector". Reworded the one player-visible HELP-menu line that named the trademarked arcade title it was inspired by ("...in this Defender-style shooter") to a genre description ("...in this side-scrolling base-defense shooter"); also renamed a code comment (// Defender constants -> // Protector constants, never player-visible either way). Added arcade-bridge.js (new file, loaded after menus.js and before the game's own inline <script>): fires SDK.ready() the first time menus.js's addPlayingListener callback runs (always false/title on registration); wraps the shared window.showGameOverDialog function (which the game's doGameOver() at the end of the file already calls directly with {score, extraLines, onContinue}) to also call SDK.gameOver({score, won}) and SDK.submitScore(score) before invoking the original, which still shows the WIN/GAME OVER modal and runs the engine's own submitBestScore(); and on SDK.onPause calls showMenu('pause') when isPlaying() is true, which (via installAutoPause(() => isPlaying()) already wired in gameInit) sets the engine's paused global. touch.scheme is 'native' because the game enables LittleJS's own on-screen touch gamepad (touchGamepadEnable/Analog/ButtonCount, set in gameInit to an analog stick + 1 fire button) — the platform shim draws no .arc-pad controls for 'native' so there is no double gamepad. One line patched in the vendored littlejs.release.js itself (the only change to that file, so it is otherwise verbatim from upstream dist/): its WebGL2 context creation, `glCanvas.getContext('webgl2', {antialias:glAntialias})`, now also passes `preserveDrawingBuffer:true`. LittleJS's WebGL canvas (the first <canvas> in the DOM; a second, mostly-unused 2D canvas handles HUD text) does not opt into a persistent backbuffer by default, so a pixel read-back performed from outside the exact task that rendered a frame — which is exactly what an automated headless check's canvas.drawImage() probe does — can see an already-cleared buffer and read back blank, even though the canvas renders correctly on screen for a real player (the identical class of bug this platform's HexGL port hit and fixed the same way; see that port's own CREDITS.md). No visual or behavioral change for players. No other vendored template file (textureGenerator.js, gameFx.js, menus.js) and no gameplay code were modified from upstream.

Comments 0

Log in to comment.

  • Loading…