From 9a68214c8dc7b316703df3fe2278d4f550108006 Mon Sep 17 00:00:00 2001 From: Daddy32 Date: Mon, 15 Dec 2025 16:43:13 +0100 Subject: [PATCH] refactor; level reorder --- index.html | 1 + src/main.js | 75 +++++++++------------------------- src/scenes/index.js | 6 +-- src/scenes/scene-storm-grid.js | 32 ++++++++++++--- src/storage.js | 51 +++++++++++++++++++++++ src/ui.js | 24 +++++++++-- styles.css | 4 +- 7 files changed, 123 insertions(+), 70 deletions(-) create mode 100644 src/storage.js diff --git a/index.html b/index.html index 350d6b2..9fb0488 100644 --- a/index.html +++ b/index.html @@ -106,6 +106,7 @@ + diff --git a/src/main.js b/src/main.js index db01b59..78a65d0 100644 --- a/src/main.js +++ b/src/main.js @@ -151,55 +151,12 @@ let lastTimerDisplay = null; let dragConstraint = null; - const makeStorageKey = (sceneId) => `physilinks-highscore-${sceneId}`; - const makeChainKey = (sceneId) => `physilinks-longestchain-${sceneId}`; - - const loadHighScore = (sceneId) => { - try { - const raw = localStorage.getItem(makeStorageKey(sceneId)); - const parsed = parseInt(raw, 10); - return Number.isFinite(parsed) ? parsed : 0; - } catch (err) { - console.error("Failed to load high score", { sceneId, err }); - return 0; - } - }; - - const loadLongestChain = (sceneId) => { - try { - const raw = localStorage.getItem(makeChainKey(sceneId)); - const parsed = parseInt(raw, 10); - return Number.isFinite(parsed) ? parsed : 0; - } catch (err) { - console.error("Failed to load longest chain", { sceneId, err }); - return 0; - } - }; - - const saveHighScore = () => { - try { - localStorage.setItem(makeStorageKey(currentScene.id), String(highScore)); - } catch (err) { - console.error("Failed to save high score", { - sceneId: currentScene.id, - err, - }); - } - }; - - const saveLongestChain = () => { - try { - localStorage.setItem( - makeChainKey(currentScene.id), - String(longestChainRecord), - ); - } catch (err) { - console.error("Failed to save longest chain", { - sceneId: currentScene.id, - err, - }); - } - }; + const { + loadHighScore = () => 0, + loadLongestChain = () => 0, + saveHighScore = () => {}, + saveLongestChain = () => {}, + } = window.PhysilinksStorage || {}; const applyScene = (sceneId) => { const next = getSceneById(sceneId) || scenes[0]; @@ -234,6 +191,13 @@ : defaultMessageConfig.colors, }; ui.setMessageDefaults(config.messages); + world.plugin = world.plugin || {}; + world.plugin.stormSteps = Array.isArray(next.config?.spawnIntervals) + ? next.config.spawnIntervals + : null; + world.plugin.squareOffset = 0; + engine.plugin = engine.plugin || {}; + engine.plugin.stormState = null; engine.gravity.scale = typeof next.config.gravityScale === "number" ? next.config.gravityScale @@ -432,8 +396,9 @@ const getSquarePlayArea = () => { if (!currentScene?.config?.squarePlayArea) return null; const size = Math.min(width, height); - const left = (width - size) / 2; - const top = (height - size) / 2; + const offset = world?.plugin?.squareOffset || 0; + const left = (width - size) / 2 + offset; + const top = (height - size) / 2 + offset; return { size, left, top }; }; @@ -447,7 +412,7 @@ } if (typeof currentScene?.config?.spawnInsets === "function") { try { - const res = currentScene.config.spawnInsets({ width, height }); + const res = currentScene.config.spawnInsets({ width, height, world }); if (res && Number.isFinite(res.left)) left = res.left; if (res && Number.isFinite(res.right)) right = res.right; } catch (err) { @@ -764,7 +729,7 @@ const chainLength = chain.bodies.length; if (chainLength > longestChainRecord) { longestChainRecord = chainLength; - saveLongestChain(); + saveLongestChain(currentScene.id, longestChainRecord); console.log( "New longest chain record", chainLength, @@ -794,7 +759,7 @@ clearedCount += chain.bodies.length; if (score > highScore) { highScore = score; - saveHighScore(); + saveHighScore(currentScene.id, highScore); } ui.spawnScorePopup(releasePoint || chain.pointer, gain, chain.color); chain.constraints.forEach((c) => World.remove(world, c)); @@ -1135,7 +1100,7 @@ const side = config.ballRadius * 2; const body = Bodies.rectangle(x, y, side, side, { ...commonOpts, - chamfer: 4, + chamfer: 0, }); body.plugin = { color, diff --git a/src/scenes/index.js b/src/scenes/index.js index 9ef3988..847dec6 100644 --- a/src/scenes/index.js +++ b/src/scenes/index.js @@ -2,13 +2,13 @@ const scenes = window.PhysilinksSceneDefs || []; const desiredOrder = [ "scene-grid", - "low-g-terraces", - "fast-drop-maze", "balanced", + "stack-blocks", "scene-lava", "swirl-arena", "relax", - "stack-blocks", + "low-g-terraces", + "fast-drop-maze", "storm-grid", ]; const orderedScenes = desiredOrder diff --git a/src/scenes/scene-storm-grid.js b/src/scenes/scene-storm-grid.js index 74cd12a..fdb1e59 100644 --- a/src/scenes/scene-storm-grid.js +++ b/src/scenes/scene-storm-grid.js @@ -58,11 +58,25 @@ spawnInset: 0, spawnIntervals: [ { seconds: 0, gravityX: 0, gravityY: 0.9, label: "Calm start" }, - { seconds: 20, gravityX: 0.2, gravityY: 0.88, label: "Gust: East pull" }, - { seconds: 40, gravityX: -0.25, gravityY: 0.85, label: "Gust: West pull" }, + { + seconds: 20, + gravityX: 0.2, + gravityY: 0.88, + label: "Gust: East pull", + }, + { + seconds: 40, + gravityX: -0.25, + gravityY: 0.85, + label: "Gust: West pull", + }, { seconds: 60, gravityX: 0, gravityY: 0.7, label: "Updraft" }, ], - winCondition: { type: "timer", durationSec: 90, onWin: { shoveBalls: true } }, + winCondition: { + type: "timer", + durationSec: 90, + onWin: { shoveBalls: true }, + }, link: { stiffness: 0.82, lengthScale: 1.05, @@ -87,10 +101,11 @@ engine.gravity.x = upcoming.gravityX; engine.gravity.y = upcoming.gravityY; // Nudge play area offset to keep things lively. - const offset = ((state.nextIdx % 2 === 0 ? 1 : -1) * Math.min(width, height)) / 50; + const offset = + ((state.nextIdx % 2 === 0 ? 1 : -1) * Math.min(width, height)) / 50; engine.world.plugin.squareOffset = offset; state.nextIdx += 1; - if (typeof window?.PhysilinksUI?.create === "function" && window.PhysilinksUI?.instance) { + if (window?.PhysilinksUI?.instance?.showFloatingMessage) { window.PhysilinksUI.instance.showFloatingMessage( { text: upcoming.label || "Gust incoming" }, { durationMs: 2200 }, @@ -108,7 +123,12 @@ }, createBodies: (w, h) => { const offset = 0; - const walls = makeSquareBodies(w, h, offset, Math.max(20, Math.min(w, h) * 0.02)); + const walls = makeSquareBodies( + w, + h, + offset, + Math.max(20, Math.min(w, h) * 0.02), + ); walls.forEach((b) => { b.plugin = b.plugin || {}; b.plugin.stormWall = true; diff --git a/src/storage.js b/src/storage.js new file mode 100644 index 0000000..c0c14b3 --- /dev/null +++ b/src/storage.js @@ -0,0 +1,51 @@ +(() => { + const makeStorageKey = (sceneId) => `physilinks-highscore-${sceneId}`; + const makeChainKey = (sceneId) => `physilinks-longestchain-${sceneId}`; + + const loadHighScore = (sceneId) => { + try { + const raw = localStorage.getItem(makeStorageKey(sceneId)); + const parsed = parseInt(raw, 10); + return Number.isFinite(parsed) ? parsed : 0; + } catch (err) { + console.error("Failed to load high score", { sceneId, err }); + return 0; + } + }; + + const loadLongestChain = (sceneId) => { + try { + const raw = localStorage.getItem(makeChainKey(sceneId)); + const parsed = parseInt(raw, 10); + return Number.isFinite(parsed) ? parsed : 0; + } catch (err) { + console.error("Failed to load longest chain", { sceneId, err }); + return 0; + } + }; + + const saveHighScore = (sceneId, highScore) => { + try { + localStorage.setItem(makeStorageKey(sceneId), String(highScore)); + } catch (err) { + console.error("Failed to save high score", { sceneId, err }); + } + }; + + const saveLongestChain = (sceneId, longestChain) => { + try { + localStorage.setItem(makeChainKey(sceneId), String(longestChain)); + } catch (err) { + console.error("Failed to save longest chain", { sceneId, err }); + } + }; + + window.PhysilinksStorage = { + makeStorageKey, + makeChainKey, + loadHighScore, + loadLongestChain, + saveHighScore, + saveLongestChain, + }; +})(); diff --git a/src/ui.js b/src/ui.js index 6a139ef..2f835ee 100644 --- a/src/ui.js +++ b/src/ui.js @@ -232,6 +232,8 @@ el.appendChild(textSpan); }; + const activeMessages = []; + const showFloatingMessage = (message, options = {}) => { if (!floatingMessagesEl) return; const msgObj = @@ -250,18 +252,24 @@ el.className = "floating-message"; renderFloatingMessage(el, text, colors); el.style.left = `${position.xPercent ?? 50}%`; - el.style.top = `${position.yPercent ?? 10}%`; + const verticalOffset = activeMessages.length * 34; + el.style.top = `calc(${position.yPercent ?? 10}% + ${verticalOffset}px)`; floatingMessagesEl.appendChild(el); requestAnimationFrame(() => { el.classList.add("visible"); }); setTimeout(() => { el.classList.remove("visible"); - setTimeout(() => el.remove(), 260); + setTimeout(() => { + el.remove(); + const idx = activeMessages.indexOf(el); + if (idx >= 0) activeMessages.splice(idx, 1); + }, 260); }, durationMs); + activeMessages.push(el); }; - return { + const api = { sceneEl, updateHud, buildLegend, @@ -278,7 +286,15 @@ showFloatingMessage, setMessageDefaults, }; + return api; }; - window.PhysilinksUI = { create }; + window.PhysilinksUI = { + create: (...args) => { + const instance = create(...args); + window.PhysilinksUI.instance = instance; + return instance; + }, + instance: null, + }; })(); diff --git a/styles.css b/styles.css index f1faa80..34b9a44 100644 --- a/styles.css +++ b/styles.css @@ -208,11 +208,11 @@ canvas { transform: translate(-50%, 0) scale(0.98); background: rgba(15, 23, 42, 0.76); color: #f8fafc; - padding: 15px 20px; + padding: 16px 22px; border-radius: 14px; border: 1px solid rgba(226, 232, 240, 0.16); font-weight: 800; - font-size: 19px; + font-size: 21px; letter-spacing: 0.4px; text-shadow: 0 10px 30px rgba(0, 0, 0, 0.45),