(() => { const { Bodies, Composites } = Matter; const scenes = (window.PhysilinksSceneDefs = window.PhysilinksSceneDefs || []); scenes.push({ id: "relax", name: "Relax drift", config: { gravity: 0.08, spawnIntervalMs: 850, spawnBatchMin: 3, spawnBatchMax: 5, spawnFrom: "bottom", autoSpawn: true, minChain: 2, palette: ["#38bdf8", "#f472b6", "#fbbf24", "#22c55e", "#a855f7"], ballRadius: 20, blobBalls: false, noGameOver: true, relaxMode: true, messages: { position: { xPercent: 50, yPercent: 16 }, }, winCondition: { type: "timer", durationSec: 120, onWin: { setGravity: -0.4, swirlBalls: true }, }, link: { stiffness: 0.6, lengthScale: 1.1, damping: 0.1, lineWidth: 3, rope: true, renderType: "line", maxLengthMultiplier: 7.8, }, }, createBodies: (w, h) => { const wallThickness = Math.max(30, w * 0.04); const wallHeight = h + wallThickness * 2; const floorHeight = Math.max(40, h * 0.08); const bumperRadius = Math.max(30, Math.min(w, h) * 0.04); const makeSoft = (cx, cy, cols, rows, radius, color) => { const particleOpts = { friction: 0.02, frictionStatic: 0.04, restitution: 0.02, render: { fillStyle: color, strokeStyle: color }, plugin: { draggable: true, nonLinkable: true }, }; const constraintOpts = { stiffness: 0.08, damping: 0.35, render: { visible: false, type: "line", anchors: false }, }; const comp = Composites.softBody( cx - cols * radius * 1.1, cy - rows * radius * 1.1, cols, rows, 0, 0, true, radius, particleOpts, constraintOpts, ); comp.bodies.forEach((b) => { b.plugin = b.plugin || {}; b.plugin.draggable = true; b.plugin.nonLinkable = true; }); comp.constraints.forEach((c) => { c.plugin = { soft: true }; }); return comp; }; const softRadius = Math.max(18, w * 0.025); const softA = makeSoft(w * 0.32, h * 0.38, 3, 3, softRadius, "#38bdf8"); const softB = makeSoft(w * 0.65, h * 0.55, 3, 3, softRadius, "#f472b6"); const softC = makeSoft(w * 0.5, h * 0.32, 3, 3, softRadius, "#fbbf24"); return [ Bodies.rectangle( w / 2, h + floorHeight / 2, w + wallThickness * 2, floorHeight, { isStatic: true, restitution: 0.8, render: { fillStyle: "#0ea5e9", strokeStyle: "#0ea5e9" }, }, ), Bodies.rectangle( w / 2, -wallThickness / 2, w + wallThickness * 2, wallThickness, { isStatic: true, render: { fillStyle: "#0ea5e9", strokeStyle: "#0ea5e9" }, }, ), Bodies.rectangle(-wallThickness / 2, h / 2, wallThickness, wallHeight, { isStatic: true, render: { fillStyle: "#7c3aed", strokeStyle: "#7c3aed" }, }), Bodies.rectangle( w + wallThickness / 2, h / 2, wallThickness, wallHeight, { isStatic: true, render: { fillStyle: "#7c3aed", strokeStyle: "#7c3aed" }, }, ), ...softA.bodies, ...softA.constraints, ...softB.bodies, ...softB.constraints, ...softC.bodies, ...softC.constraints, ]; }, }); })();