From 0bc834416fe72a1ba1714c691c3647446cbd74d0 Mon Sep 17 00:00:00 2001 From: Daddy32 Date: Mon, 15 Dec 2025 17:29:39 +0100 Subject: [PATCH] Add reference scene template --- AGENTS.md | 1 + README.md | 1 + src/scenes/scene-template.js | 135 +++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 src/scenes/scene-template.js diff --git a/AGENTS.md b/AGENTS.md index d1ee36d..3953071 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,6 +5,7 @@ - `styles.css` holds layout, HUD, overlays, and popup styling. - `src/main.js` runs the physics loop, spawning, scoring, and scene application; `src/ui.js` wires DOM/HUD controls and overlays. - `src/scenes/*.js` defines scene presets (`id`, `name`, `config`, `createBodies(width, height)`); `src/scenes/index.js` registers them to `window.PhysilinksScenes`. +- `src/scenes/scene-template.js` is a reference-only template documenting all supported scene config options; it is not loaded by default. - `src/decomp-setup.js` configures `poly-decomp` for concave shapes; `src/storage.js` reads/writes per-scene highscores/records in `localStorage`. ## Build, Test, and Development Commands diff --git a/README.md b/README.md index 14faa50..fb09767 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ Physilinks is a browser-based physics linking game built with Matter.js. Match a - `index.html`: Shell layout and HUD overlays; loads Matter.js plus game scripts. - `styles.css`: Styling for canvas, HUD, overlays, and score popups. - `src/scenes/`: Scene presets split per file (`scene-*.js`) plus `index.js` that registers them to `window.PhysilinksScenes` (e.g., zero-G grid, balanced, low-G, fast drop, lava drift). +- `src/scenes/scene-template.js`: Reference-only template documenting every scene config option; not loaded by default. - `src/decomp-setup.js`: Registers `poly-decomp` with Matter to allow concave shapes (stars, blobs) built via `Bodies.fromVertices`. - `src/ui.js`: DOM access, HUD updates, overlays, popups, and control/selector wiring. - `src/main.js`: Physics setup, state machine, chain interaction, spawning, scene application, and pause/restart logic. diff --git a/src/scenes/scene-template.js b/src/scenes/scene-template.js new file mode 100644 index 0000000..b723d21 --- /dev/null +++ b/src/scenes/scene-template.js @@ -0,0 +1,135 @@ +(() => { + const { Bodies } = Matter; + const scenes = (window.PhysilinksSceneDefs = + window.PhysilinksSceneDefs || []); + + // Template scene example for authors. This file is NOT referenced by index.html, + // so it will not load into the game unless you add a script tag for it. + scenes.push({ + id: "your-scene-id", + name: "Template Example (not loaded)", + config: { + // Core physics and pacing + gravity: 0.9, // vertical gravity (positive pulls down) + gravityScale: 1, // multiplier on world gravity; set per scene + timeScale: 1, // global step speed multiplier; slows/speeds simulation + spawnIntervalMs: 520, // auto-spawn cadence when enabled + autoSpawn: true, // set false for grid/manual spawn scenes + spawnLimit: null, // number; stop spawning once reached + + // Spawn placement + spawnOrigin: "edge", // "edge" (random along top) or "center" (random around middle) + spawnJitter: 60, // radius of randomness when origin is "center" + spawnFrom: "top", // "top" (default) or "bottom" for rising spawns + spawnColumns: null, // number of columns to align spawns; enables grid-like lanes + spawnInset: 0, // uniform horizontal inset (pixels) applied to spawn area + spawnInsets: ({ width, height, world }) => { + // optional function returning { left, right } in pixels; can react to world.plugin + return { left: 0, right: 0 }; + }, + squarePlayArea: false, // true constrains spawn/fit to centered square (uses world.plugin.squareOffset if set) + sizeFromColumns: false, // true derives ball radius from available width and spawnColumns + spawnBatchMin: 1, // minimum balls per spawn tick + spawnBatchMax: 1, // maximum balls per spawn tick + initialSpawnCount: 0, // number of balls to drop once at start (non-grid scenes) + initialRows: null, // with spawnColumns: seed rows x columns grid at start + rowGapMultiplier: 1.05, // spacing between seeded rows when initialRows is used + requireClearSpawn: false, // end run if spawn point is blocked + spawnIntervals: [ + // Optional timed gravity schedule; main.js stores on world.plugin.stormSteps + // { seconds, gravityX, gravityY, label } + ], + + // Ball appearance/behavior + palette: ["#ff595e", "#ffca3a", "#8ac926", "#1982c4", "#6a4c93"], // colors used for balls (and legend) + ballRadius: 18, // baseline radius; may be rescaled per viewport + ballShape: "circle", // "circle" (default) or "rect" + blobBalls: false, // "soft" for soft-body clusters, "jagged" for rough polygons, or false for singles + + // Chain/link rules + minChain: 3, // minimum balls to clear a chain + relaxMode: false, // true lets player link different colors + negativeScoreColors: [], // colors that invert score gain + negativeProgressColors: [], // colors that undo win-condition progress when cleared + link: { + stiffness: 0.85, // Matter constraint stiffness for links + lengthScale: 1.05, // stretch target relative to ball distance + damping: 0.08, // constraint damping + lineWidth: 3, // rendered link width + rope: true, // rope behavior: slack when short, taut when stretched + renderType: "line", // Matter render.type for constraints + maxLengthMultiplier: 3.1, // max link length as multiple of ball radius + maxLinkLength: null, // optional absolute pixel cap for link reach + }, + + // Messaging overlays (goal hints / milestones) + messages: { + durationMs: 4200, // how long floating messages persist + position: { xPercent: 50, yPercent: 10 }, // viewport-relative placement + text: null, // optional override for intro/progress text; falls back to goal-based text + colors: null, // optional array of gradient colors for message text + }, + + // Grid mode (used when gridLayouts is provided) + gridLayouts: [ + // Each layout is an array of 8-character strings; non-space/period letters map to colors. + // Example: "AABBCCDD" + ], + gridPadding: 0.08, // fraction of viewport used as padding on each side for grid playfield + gridBallScale: 0.36, // fraction of grid cell size to use as ball radius + gridLegend: { + // map letter -> hex color; falls back to palette when missing + // A: "#ff595e", + }, + + // Win conditions + winCondition: { + // Supported types: + // - "timer": survive durationSec seconds + // - "clearCount": clear target balls + // - "score": reach target score + // - "colorClear": clear specified counts per color in targets array + type: "score", + durationSec: 90, // timer length for "timer" + target: 10000, // score/clear target for "score" or "clearCount" + targets: [ + // for "colorClear": { color, count } + // { color: "#ff595e", count: 12 }, + ], + onWin: { + // Optional celebratory effects applied on win: + shoveBalls: true, // random impulse burst + swirlBalls: false, // upward swirling nudge + disableGravity: false, // zero out gravity + setGravity: null, // number; set vertical gravity to value + removeCurves: false, // remove bodies tagged with plugin.curve + }, + }, + + // Scoring/persistence + noGameOver: false, // true disables game-over-on-blocked-entry checks + }, + createBodies: (w, h) => { + // Return an array of Matter bodies that make up scene obstacles/boundaries. + // Bodies can use plugin flags consumed by main.js: + // - plugin.rotSpeed: radians/sec; body will rotate each tick + // - plugin.oscillate: { axis: "x"|"y", amplitude, speed, phase } for sinusoidal motion + // - plugin.draggable: true to allow pointer dragging + // - plugin.curve: truthy marks for removal when winCondition.onWin.removeCurves is set + // - plugin.blobId: used automatically for soft/jagged blobs to clear as a group + const floorHeight = Math.max(60, h * 0.12); + return [ + Bodies.rectangle( + w / 2, + h + floorHeight / 2, + w * 1.2, + floorHeight, + { + isStatic: true, + render: { fillStyle: "#0b1222", strokeStyle: "#0b1222" }, + }, + ), + ]; + }, + }); +})();