(() => { const { Bodies, Body } = Matter; const scenes = (window.PhysilinksSceneDefs = window.PhysilinksSceneDefs || []); scenes.push({ id: "low-g-terraces", name: "Low-G terraces", config: { gravity: 0.65, spawnIntervalMs: 600, minChain: 3, palette: ["#fb7185", "#fbbf24", "#34d399", "#38bdf8"], ballRadius: 22, winCondition: { type: "score", target: 50000, onWin: { shoveBalls: true }, }, link: { stiffness: 0.6, lengthScale: 1, damping: 0.01, lineWidth: 4, rope: false, renderType: "spring", maxLengthMultiplier: 4.7, }, }, createBodies: (w, h) => { const floorHeight = Math.max(70, h * 0.12); const wallThickness = Math.max(32, w * 0.05); const wallHeight = h * 1.8; const cogRadius = Math.max(40, Math.min(w, h) * 0.085); const cogRadiusSmall = Math.max(30, Math.min(w, h) * 0.065); const bumperRadius = Math.max(20, Math.min(w, h) * 0.05); const stickyWidth = Math.max(90, w * 0.12); const stickyHeight = Math.max(14, h * 0.02); const stickyAmplitude = w * 0.06; const bumperAmplitude = h * 0.08; const makeGear = (cx, cy, outerRadius, teeth, color, rotSpeed) => { const coreRadius = outerRadius * 1.5; const toothLength = outerRadius * 0.5; const toothWidth = Math.max(outerRadius * 0.14, 10); const parts = [ Bodies.circle(cx, cy, coreRadius, { isStatic: true, render: { fillStyle: color, strokeStyle: color }, }), ]; const step = (Math.PI * 2) / teeth; for (let i = 0; i < teeth; i += 1) { const angle = step * i; const tx = cx + Math.cos(angle) * (coreRadius + toothLength / 2); const ty = cy + Math.sin(angle) * (coreRadius + toothLength / 2); parts.push( Bodies.rectangle(tx, ty, toothWidth, toothLength, { isStatic: true, angle, render: { fillStyle: color, strokeStyle: color }, }), ); } const gear = Body.create({ isStatic: true, parts, plugin: { rotSpeed }, }); return gear; }; return [ Bodies.rectangle( w / 2, h + floorHeight / 2, w + wallThickness * 2, floorHeight, { isStatic: true, restitution: 0.9, 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" }, }, ), // Rotating cogs with teeth makeGear(w * 0.32, h * 0.38, cogRadius, 10, "#f97316", 1.2), makeGear(w * 0.64, h * 0.5, cogRadiusSmall, 12, "#a855f7", -1.6), makeGear(w * 0.5, h * 0.32, cogRadius * 0.85, 14, "#fb7185", 1.9), // Oscillating bumpers Bodies.circle(w * 0.2, h * 0.46, bumperRadius, { isStatic: true, restitution: 1.08, friction: 0.01, render: { fillStyle: "#14b8a6", strokeStyle: "#14b8a6" }, plugin: { oscillate: { axis: "y", amplitude: bumperAmplitude, speed: 1.4 }, }, }), Bodies.circle(w * 0.5, h * 0.62, bumperRadius * 0.9, { isStatic: true, restitution: 1.08, friction: 0.01, render: { fillStyle: "#fbbf24", strokeStyle: "#fbbf24" }, plugin: { oscillate: { axis: "x", amplitude: stickyAmplitude, speed: 1.1 }, }, }), Bodies.circle(w * 0.8, h * 0.38, bumperRadius * 1.05, { isStatic: true, restitution: 1.08, friction: 0.01, render: { fillStyle: "#38bdf8", strokeStyle: "#38bdf8" }, plugin: { oscillate: { axis: "y", amplitude: bumperAmplitude * 0.7, speed: 1.8, }, }, }), // Sticky moving pads Bodies.rectangle(w * 0.32, h * 0.72, stickyWidth, stickyHeight, { isStatic: true, angle: -0.08, friction: 1.3, render: { fillStyle: "#0ea5e9", strokeStyle: "#0ea5e9" }, plugin: { oscillate: { axis: "x", amplitude: stickyAmplitude, speed: 0.9 }, }, }), Bodies.rectangle(w * 0.72, h * 0.7, stickyWidth * 0.9, stickyHeight, { isStatic: true, angle: 0.06, friction: 1.3, render: { fillStyle: "#f472b6", strokeStyle: "#f472b6" }, plugin: { oscillate: { axis: "x", amplitude: stickyAmplitude * 0.8, speed: 1.3, }, }, }), // Random polygon obstacles Bodies.polygon(w * 0.18, h * 0.28, 5, bumperRadius * 0.9, { isStatic: true, angle: 0.2, render: { fillStyle: "#22c55e", strokeStyle: "#22c55e" }, plugin: { rotSpeed: -0.6 }, }), Bodies.polygon(w * 0.86, h * 0.62, 7, bumperRadius * 0.95, { isStatic: true, angle: -0.25, render: { fillStyle: "#c084fc", strokeStyle: "#c084fc" }, plugin: { rotSpeed: 0.9 }, }), ]; }, }); })();