(() => { const { Bodies, Body } = Matter; const scenes = (window.PhysilinksSceneDefs = window.PhysilinksSceneDefs || []); scenes.push({ id: "stack-blocks-chaos", name: "Stack Blocks Chaos", config: { gravity: 2.85, spawnIntervalMs: 750, autoSpawn: true, minChain: 3, palette: ["#fb7185", "#8b5cf6", "#22d3ee"], ballRadius: 18, ballShape: "rect", spawnColumns: 10, sizeFromColumns: true, initialRows: 3, requireClearSpawn: true, squarePlayArea: true, rowGapMultiplier: 1, spawnInsets: ({ width }) => { const wallThickness = Math.max(20, width * 0.02); return { left: 0, right: 0 }; }, winCondition: { type: "score", target: 20000, }, link: { stiffness: 0.85, lengthScale: 1.05, damping: 0.08, lineWidth: 3, rope: true, renderType: "line", maxLengthMultiplier: 2.5, }, chaosSurge: { intervalMs: 19000, jitterMs: 2500, pushRows: 1, columnJitter: 10, }, onBeforeUpdate: ({ engine, width, height, state, spawnSystem, config, }) => { if ( !state || !spawnSystem || state.paused || state.gameOver || state.levelWon ) { return; } const surgeCfg = config.chaosSurge || {}; const interval = surgeCfg.intervalMs ?? 9000; const jitter = surgeCfg.jitterMs ?? 0; const now = engine.timing?.timestamp ?? Date.now(); engine.plugin ??= {}; const chaosState = engine.plugin.stackBlocksChaos || {}; if (chaosState.sceneId !== "stack-blocks-chaos") { chaosState.sceneId = "stack-blocks-chaos"; chaosState.nextSurgeMs = now + interval + Math.random() * jitter; } if (!chaosState.nextSurgeMs) { chaosState.nextSurgeMs = now + interval + Math.random() * jitter; } if (now < chaosState.nextSurgeMs) { engine.plugin.stackBlocksChaos = chaosState; return; } chaosState.nextSurgeMs = now + interval + Math.random() * jitter; engine.plugin.stackBlocksChaos = chaosState; const rowGap = config.ballRadius * 2 * (config.rowGapMultiplier ?? 1); const pushDistance = Math.min( rowGap * (surgeCfg.pushRows ?? 1), height * 0.35, ); const dampen = 0.6; state.balls.forEach((ball) => { Body.translate(ball, { x: 0, y: -pushDistance }); Body.setVelocity(ball, { x: ball.velocity.x * dampen, y: Math.min(ball.velocity.y, 0), }); }); const squareSize = Math.min(width, height); const playTop = (height - squareSize) / 2; const baseY = playTop + squareSize - config.ballRadius * 1.2; spawnSystem.spawnRowAtY({ y: baseY, jitter: surgeCfg.columnJitter ?? config.ballRadius * 0.4, markEntered: true, }); }, }, createBodies: (w, h) => { const squareSize = Math.min(w, h); const left = (w - squareSize) / 2; const top = (h - squareSize) / 2; const wallThickness = Math.max(20, w * 0.02); const floorHeight = Math.max(30, squareSize * 0.06); const wallRender = { fillStyle: "#1e293b", strokeStyle: "#94a3b8", lineWidth: 2, }; return [ Bodies.rectangle( left - wallThickness / 2, h / 2, wallThickness, h + wallThickness * 2, { isStatic: true, render: wallRender, }, ), Bodies.rectangle( left + squareSize + wallThickness / 2, h / 2, wallThickness, h + wallThickness * 2, { isStatic: true, render: wallRender, }, ), Bodies.rectangle( w / 2, h + floorHeight / 2, w + wallThickness * 2, floorHeight, { isStatic: true, restitution: 0.2, render: wallRender, }, ), ]; }, }); })();