(() => { const { Bodies, Body, Vertices } = Matter; const scenes = (window.PhysilinksSceneDefs = window.PhysilinksSceneDefs || []); scenes.push({ id: "christmas-calm", name: "Christmas calm", config: { gravity: 0.5, spawnIntervalMs: 720, spawnBatchMin: 1, spawnBatchMax: 2, minChain: 3, palette: ["#b91c1c", "#15803d", "#f59e0b", "#7dd3fc", "#fda4af"], ballRadius: 20, ballShape: "gift", giftRibbonColor: "#f8fafc", debugSpawn: true, initialSpawnCount: 10, initialSpawnArea: ({ width, height }) => ({ xMin: width * 0.28, xMax: width * 0.72, yMin: height * 0.76, yMax: height * 0.88, }), winCondition: { type: "score", target: 12000, onWin: { shoveBalls: true }, }, messages: { text: "Cozy winter glow", position: { xPercent: 50, yPercent: 12 }, }, link: { stiffness: 0.7, lengthScale: 1.05, damping: 0.05, lineWidth: 4, rope: true, renderType: "line", maxLengthMultiplier: 5.4, }, backdrop: { colors: ["#0f172a", "#14532d", "#1f2937"], opacity: 0.35, blur: 26, speedSec: 34, }, }, createBodies: (w, h) => { const wallThickness = Math.max(32, w * 0.045); const wallHeight = h * 1.7; const floorHeight = Math.max(70, h * 0.12); const treeBaseRadius = Math.min(w, h) * 0.22; const treeMidRadius = Math.min(w, h) * 0.16; const treeTopRadius = Math.min(w, h) * 0.11; const trunkWidth = treeBaseRadius * 0.3; const trunkHeight = treeBaseRadius * 0.35; const treeCenterX = w * 0.5; const treeBaseY = h * 0.62; const treeMidY = h * 0.52; const treeTopY = h * 0.43; const treeColor = "#166534"; const trunkColor = "#7c2d12"; const makeTriangle = (x, y, radius) => { const points = [ { x: 0, y: -radius }, { x: radius, y: radius }, { x: -radius, y: radius }, ]; return Bodies.fromVertices( x, y, [points], { isStatic: true, render: { fillStyle: treeColor, strokeStyle: treeColor }, }, true, ); }; const makeComet = (x, y, size) => { const baseStar = Vertices.fromPath( "50 0 63 38 100 38 69 59 82 100 50 75 18 100 31 59 0 38 37 38", ); const scale = (size || 50) / 50; const points = baseStar.map((p) => ({ x: (p.x - 50) * scale, y: (p.y - 50) * scale, })); const star = Bodies.fromVertices( x, y, [points], { isStatic: true, render: { fillStyle: "#facc15", strokeStyle: "#facc15" }, }, true, ); const tail = Bodies.rectangle( x + size * 1.1, y + size * 0.2, size * 2.1, size * 0.45, { isStatic: true, angle: 0.35, render: { fillStyle: "#fde68a", strokeStyle: "#fde68a" }, }, ); const starParts = Array.isArray(star.parts) && star.parts.length > 1 ? star.parts : [star]; return Body.create({ isStatic: true, parts: [...starParts, tail], render: { fillStyle: "#facc15", strokeStyle: "#facc15" }, plugin: { rotSpeed: 0.35 }, }); }; return [ Bodies.rectangle( w / 2, h + floorHeight / 2, w + wallThickness * 2, floorHeight, { isStatic: true, restitution: 0.7, render: { fillStyle: "#e2e8f0", strokeStyle: "#e2e8f0" }, }, ), Bodies.rectangle(-wallThickness / 2, h / 2, wallThickness, wallHeight, { isStatic: true, render: { fillStyle: "#0f766e", strokeStyle: "#0f766e" }, }), Bodies.rectangle( w + wallThickness / 2, h / 2, wallThickness, wallHeight, { isStatic: true, render: { fillStyle: "#0f766e", strokeStyle: "#0f766e" }, }, ), makeTriangle(treeCenterX, treeBaseY, treeBaseRadius), makeTriangle(treeCenterX, treeMidY, treeMidRadius), makeTriangle(treeCenterX, treeTopY, treeTopRadius), Bodies.rectangle( treeCenterX, treeBaseY + treeBaseRadius * 0.65, trunkWidth, trunkHeight, { isStatic: true, render: { fillStyle: trunkColor, strokeStyle: trunkColor }, }, ), makeComet(w * 0.2, h * 0.25, Math.max(18, w * 0.025)), ]; }, }); })();