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-relax.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/index.js"></script>
|
||||
<script src="./src/ui.js"></script>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"swirl-arena",
|
||||
"relax",
|
||||
"stack-blocks",
|
||||
"storm-grid",
|
||||
];
|
||||
const orderedScenes = desiredOrder
|
||||
.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