Преглед на файлове

feat(maps): rework the crêpes, add footholds to the round maps

Playtest feedback: the crêpe pile looked noisy and its chocolate and
caramel spreads were walls nobody could dig through, and the round maps
had nothing to stand on.

- Crêpes: thick crêpes with soft spreads only (jam, whipped cream), a
  gentler lean, a strawberry on top and a second small pile.
- Roulé: wafer sticks planted around the roll as ledges.
- Pièce montée: every chou has a flat glazed top (still glued only to
  the one below, so a column still falls as one).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Idleman преди 6 дни
родител
ревизия
57ada15b42

+ 8 - 8
assets/maps/crepes/map.json

@@ -16,24 +16,24 @@
   },
   "spawns": [
     [
-      1000,
+      880,
       250
     ],
     [
-      900,
-      250
+      720,
+      300
     ],
     [
-      400,
-      800
+      1510,
+      640
     ],
     [
-      1500,
-      800
+      350,
+      900
     ]
   ],
   "cherrySpawn": [
-    1000,
+    880,
     150
   ],
   "custard": {

BIN
assets/maps/crepes/terrain.png


BIN
assets/maps/crepes/thumb.png


BIN
assets/maps/piece-montee/terrain.png


BIN
assets/maps/piece-montee/thumb.png


BIN
assets/maps/roule/terrain.png


BIN
assets/maps/roule/thumb.png


+ 59 - 25
scripts/gen-maps.ts

@@ -129,6 +129,7 @@ function pieceMontee(): Canvas {
   // Nougatine base.
   c.rect(470, 905, 1578, 960, CARAMEL);
   const R = 40;
+  const FLAT_TOP = 0.8; // the top is cut flat at this fraction of the radius above the center
   const STEP_X = 82; // 2 px gap between neighbours: they never touch sideways
   const STEP_Y = 82;
   const base = 13;
@@ -138,10 +139,18 @@ function pieceMontee(): Canvas {
     const cy = 905 - R - 2 - row * STEP_Y;
     for (let i = 0; i < count; i++) {
       const cx = cx0 + (i - (count - 1) / 2) * STEP_X;
-      c.disc(cx, cy, R, SPONGE);
-      c.disc(cx, cy, R - 14, CREAM);
-      // Caramel glaze on top and the glue joint with what is below.
-      for (let a = -2.4; a <= -0.7; a += 0.02) c.disc(cx + Math.cos(a) * (R - 4), cy + Math.sin(a) * (R - 4), 5, CARAMEL);
+      // The top of each chou is flat (a disc of glaze): somewhere to stand on every step.
+      const flat = Math.round(cy - R * FLAT_TOP);
+      for (let y = flat; y <= cy + R; y++) {
+        for (let x = Math.floor(cx - R); x <= cx + R; x++) {
+          const d2 = (x - cx) ** 2 + (y - cy) ** 2;
+          if (d2 > R * R) continue;
+          c.put(x, y, d2 <= (R - 14) ** 2 ? CREAM : SPONGE);
+        }
+      }
+      // Caramel glaze on the flat top, and the glue joint with what is below.
+      const half = Math.sqrt(R * R - (flat - cy) ** 2);
+      c.rect(Math.round(cx - half + 3), flat, Math.round(cx + half - 2), flat + 5, CARAMEL);
       c.disc(cx, cy + R + 1, 11, CARAMEL);
     }
   }
@@ -162,6 +171,7 @@ function roule(): Canvas {
   c.rect(cx - 180, 900, cx + 181, 960, BISCUIT);
   const SPACING = 64; // distance between two turns of the spiral
   const JAM = 12;
+  const WAFER = 16;
   for (let y = cy - R; y <= cy + R; y++) {
     for (let x = cx - R; x <= cx + R; x++) {
       const dx = x - cx;
@@ -178,6 +188,21 @@ function roule(): Canvas {
       c.put(x, y, phase < JAM && r > 24 ? JELLY : SPONGE);
     }
   }
+  // Wafer sticks planted in the roll: flat ledges to stand on around the round sides.
+  for (const [side, dy] of [
+    [-1, -170],
+    [1, -90],
+    [-1, 60],
+    [1, 160],
+    [-1, 230],
+  ]) {
+    const y = cy + dy;
+    const edge = cx + side * Math.sqrt(R * R - dy * dy);
+    const inner = Math.round(edge - side * 50);
+    const outer = Math.round(edge + side * 120);
+    c.rect(Math.min(inner, outer), y, Math.max(inner, outer), y + WAFER, BISCUIT);
+    c.disc(outer, y + WAFER / 2 - 0.5, WAFER / 2, BISCUIT);
+  }
   return c;
 }
 
@@ -216,31 +241,40 @@ function tarteFruits(): Canvas {
 }
 
 /**
- * La Pile de Crêpes: dozens of thin crêpes with spreads in between (chocolate, jam, caramel),
- * stacked into a pile that leans. Side wind (modifier).
+ * La Pile de Crêpes: thick crêpes with soft spreads in between (jam and whipped cream, never
+ * chocolate: every layer can be dug through), stacked into a big pile that leans a little, and
+ * a small pile beside it. Side wind (modifier).
  */
 function crepes(): Canvas {
   const c = new Canvas();
   c.plate();
-  const SPREADS = [CHOCOLATE, JELLY, CARAMEL];
-  const CREPE = 9;
-  const SPREAD = 3;
-  const lean = 0.22; // horizontal shift per pixel of height
-  let layer = 0;
-  for (let top = 960; top > 340; top -= CREPE + SPREAD, layer++) {
-    const shift = Math.round((960 - top) * lean);
-    const halfWidth = 330 - Math.round(layer * 1.5) + Math.round(6 * Math.sin(layer * 1.7));
-    const left = 860 - halfWidth + shift;
-    const right = 860 + halfWidth + shift;
-    // Rounded crêpe ends: the sides read as a stack of pancakes, not a staircase.
-    const r = CREPE / 2;
-    c.rect(left + Math.floor(r), top - CREPE, right - Math.floor(r), top, SPONGE);
-    c.disc(left + r - 0.5, top - r - 0.5, r, SPONGE);
-    c.disc(right - r - 0.5, top - r - 0.5, r, SPONGE);
-    if (top - CREPE - SPREAD > 340) c.rect(left + 12, top - CREPE - SPREAD, right - 12, top - CREPE, SPREADS[layer % 3]);
-  }
-  // Whipped cream on top.
-  c.disc(860 + Math.round(620 * lean), 345, 40, CREAM);
+  const SPREADS = [JELLY, CREAM];
+  const CREPE = 20;
+  const SPREAD = 6;
+  const pile = (cx: number, bottom: number, top: number, halfWidth: number, lean: number): [number, number] => {
+    let layer = 0;
+    let y = bottom;
+    for (; y - CREPE > top; y -= CREPE + SPREAD, layer++) {
+      const shift = Math.round((bottom - y) * lean);
+      const half = halfWidth - layer * 3 + Math.round(8 * Math.sin(layer * 1.7));
+      const left = cx - half + shift;
+      const right = cx + half + shift;
+      // Rounded crêpe ends: the sides read as a stack of pancakes, not a staircase.
+      const r = CREPE / 2;
+      c.rect(left + r, y - CREPE, right - r, y, SPONGE);
+      c.disc(left + r - 0.5, y - r - 0.5, r, SPONGE);
+      c.disc(right - r - 0.5, y - r - 0.5, r, SPONGE);
+      // The spread oozes almost to the edge.
+      c.rect(left + 10, y - CREPE - SPREAD, right - 10, y - CREPE, SPREADS[layer % SPREADS.length]);
+      for (const x of [left + 10, right - 11]) c.disc(x, y - CREPE - SPREAD / 2 - 0.5, SPREAD / 2, SPREADS[layer % SPREADS.length]);
+    }
+    return [cx + Math.round((bottom - y) * lean), y - CREPE];
+  };
+  const [topX, topY] = pile(820, 960, 380, 330, 0.1);
+  // Whipped cream and a jam strawberry on top.
+  c.disc(topX, topY + 4, 46, CREAM);
+  c.disc(topX + 10, topY - 36, 20, JELLY);
+  pile(1520, 960, 720, 190, -0.06);
   return c;
 }