Add Storm Grid Shift scene
This commit is contained in:
@@ -102,6 +102,7 @@
|
|||||||
<script src="./src/scenes/scene-lavalamp.js"></script>
|
<script src="./src/scenes/scene-lavalamp.js"></script>
|
||||||
<script src="./src/scenes/scene-relax.js"></script>
|
<script src="./src/scenes/scene-relax.js"></script>
|
||||||
<script src="./src/scenes/scene-swirl-arena.js"></script>
|
<script src="./src/scenes/scene-swirl-arena.js"></script>
|
||||||
|
<script src="./src/scenes/scene-storm-grid.js"></script>
|
||||||
<script src="./src/scenes/scene-stack-blocks.js"></script>
|
<script src="./src/scenes/scene-stack-blocks.js"></script>
|
||||||
<script src="./src/scenes/index.js"></script>
|
<script src="./src/scenes/index.js"></script>
|
||||||
<script src="./src/ui.js"></script>
|
<script src="./src/ui.js"></script>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"swirl-arena",
|
"swirl-arena",
|
||||||
"relax",
|
"relax",
|
||||||
"stack-blocks",
|
"stack-blocks",
|
||||||
|
"storm-grid",
|
||||||
];
|
];
|
||||||
const orderedScenes = desiredOrder
|
const orderedScenes = desiredOrder
|
||||||
.map((id) => scenes.find((s) => s.id === id))
|
.map((id) => scenes.find((s) => s.id === id))
|
||||||
|
|||||||
119
src/scenes/scene-storm-grid.js
Normal file
119
src/scenes/scene-storm-grid.js
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
(() => {
|
||||||
|
const { Bodies } = Matter;
|
||||||
|
const scenes = (window.PhysilinksSceneDefs =
|
||||||
|
window.PhysilinksSceneDefs || []);
|
||||||
|
|
||||||
|
const makeSquareBodies = (w, h, offset = 0, wallThickness = 24) => {
|
||||||
|
const squareSize = Math.min(w, h) * 0.82;
|
||||||
|
const left = (w - squareSize) / 2 + offset;
|
||||||
|
const top = (h - squareSize) / 2 + offset;
|
||||||
|
const render = {
|
||||||
|
fillStyle: "#0b1222",
|
||||||
|
strokeStyle: "#334155",
|
||||||
|
lineWidth: 2,
|
||||||
|
};
|
||||||
|
const floorHeight = Math.max(30, squareSize * 0.06);
|
||||||
|
return [
|
||||||
|
Bodies.rectangle(
|
||||||
|
left - wallThickness / 2,
|
||||||
|
h / 2,
|
||||||
|
wallThickness,
|
||||||
|
h + wallThickness * 2,
|
||||||
|
{ isStatic: true, render },
|
||||||
|
),
|
||||||
|
Bodies.rectangle(
|
||||||
|
left + squareSize + wallThickness / 2,
|
||||||
|
h / 2,
|
||||||
|
wallThickness,
|
||||||
|
h + wallThickness * 2,
|
||||||
|
{ isStatic: true, render },
|
||||||
|
),
|
||||||
|
Bodies.rectangle(
|
||||||
|
w / 2,
|
||||||
|
h + floorHeight / 2,
|
||||||
|
w + wallThickness * 2,
|
||||||
|
floorHeight,
|
||||||
|
{ isStatic: true, restitution: 0.1, render },
|
||||||
|
),
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
scenes.push({
|
||||||
|
id: "storm-grid",
|
||||||
|
name: "Storm Grid Shift",
|
||||||
|
config: {
|
||||||
|
gravity: 0.9,
|
||||||
|
spawnIntervalMs: 520,
|
||||||
|
autoSpawn: true,
|
||||||
|
minChain: 3,
|
||||||
|
palette: ["#38bdf8", "#f97316", "#facc15", "#22c55e"],
|
||||||
|
ballRadius: 18,
|
||||||
|
ballShape: "rect",
|
||||||
|
spawnColumns: 10,
|
||||||
|
sizeFromColumns: true,
|
||||||
|
initialRows: 3,
|
||||||
|
rowGapMultiplier: 1,
|
||||||
|
squarePlayArea: true,
|
||||||
|
requireClearSpawn: true,
|
||||||
|
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: 60, gravityX: 0, gravityY: 0.7, label: "Updraft" },
|
||||||
|
],
|
||||||
|
winCondition: { type: "timer", durationSec: 90, onWin: { shoveBalls: true } },
|
||||||
|
link: {
|
||||||
|
stiffness: 0.82,
|
||||||
|
lengthScale: 1.05,
|
||||||
|
damping: 0.08,
|
||||||
|
lineWidth: 3,
|
||||||
|
rope: true,
|
||||||
|
renderType: "line",
|
||||||
|
maxLengthMultiplier: 3.3,
|
||||||
|
},
|
||||||
|
onBeforeUpdate: ({ engine, width, height }) => {
|
||||||
|
const state = engine.plugin.stormState || {};
|
||||||
|
const now = (engine.timing?.timestamp || 0) / 1000;
|
||||||
|
if (!state.startTime) {
|
||||||
|
state.startTime = now;
|
||||||
|
state.nextIdx = 0;
|
||||||
|
engine.plugin.stormState = state;
|
||||||
|
}
|
||||||
|
const elapsed = now - state.startTime;
|
||||||
|
const steps = engine.world.plugin?.stormSteps || [];
|
||||||
|
const upcoming = steps[state.nextIdx];
|
||||||
|
if (upcoming && elapsed >= upcoming.seconds) {
|
||||||
|
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;
|
||||||
|
engine.world.plugin.squareOffset = offset;
|
||||||
|
state.nextIdx += 1;
|
||||||
|
if (typeof window?.PhysilinksUI?.create === "function" && window.PhysilinksUI?.instance) {
|
||||||
|
window.PhysilinksUI.instance.showFloatingMessage(
|
||||||
|
{ text: upcoming.label || "Gust incoming" },
|
||||||
|
{ durationMs: 2200 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
spawnInsets: ({ width, height, world }) => {
|
||||||
|
const wallThickness = Math.max(20, Math.min(width, height) * 0.02);
|
||||||
|
const offset = world?.plugin?.squareOffset || 0;
|
||||||
|
const squareSize = Math.min(width, height) * 0.82;
|
||||||
|
const left = (width - squareSize) / 2 + offset;
|
||||||
|
return { left, right: width - (left + squareSize) };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
createBodies: (w, h) => {
|
||||||
|
const offset = 0;
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
return walls;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
||||||
Reference in New Issue
Block a user