From 27b8c23a82924d94a5a527d6d906e0651a235781 Mon Sep 17 00:00:00 2001 From: Brian Fertig Date: Tue, 28 Jul 2026 07:27:13 -0600 Subject: [PATCH] refactor: consolidate path rendering styles into shared PATH_STYLE constant Move path band widths, colors, and groove properties out of ZumaGame hardcoded constants and into a single PATH_STYLE config in ZumaLogic. Both ZumaGame and ZumaEditor now draw the path using the same nested band strokes for a consistent concave-channel appearance. --- src/games/zuma/ZumaEditor.js | 5 ++--- src/games/zuma/ZumaGame.js | 35 ++++++++++++++--------------------- src/games/zuma/ZumaLogic.js | 23 +++++++++++++++++++++++ 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/games/zuma/ZumaEditor.js b/src/games/zuma/ZumaEditor.js index 6150e3a..a481745 100644 --- a/src/games/zuma/ZumaEditor.js +++ b/src/games/zuma/ZumaEditor.js @@ -17,7 +17,7 @@ import { GAME_WIDTH, GAME_HEIGHT, COLORS } from '../../config.js'; import { Button } from '../../ui/Button.js'; import { playSound, SFX } from '../../ui/Sounds.js'; import { - TUNING, BALL_COLORS, LEVEL_BOUNDS, buildPath, validateLevel, validateLevelParams, + TUNING, BALL_COLORS, LEVEL_BOUNDS, PATH_STYLE, buildPath, validateLevel, validateLevelParams, } from './ZumaLogic.js'; const FONT = 'm6x11, "Julius Sans One"'; @@ -483,8 +483,7 @@ export default class ZumaEditor extends Phaser.Scene { } g.strokePath(); }; - stroke(80, 0x241b0e); - stroke(67, 0x4a3a26); + for (const { w, color } of PATH_STYLE.bands) stroke(w, color); // Ghost chain: the whole quota packed back from the hole — the shape of // the moment before you lose. Makes "does this path hold the marbles I diff --git a/src/games/zuma/ZumaGame.js b/src/games/zuma/ZumaGame.js index 95794e2..bf2d5b0 100644 --- a/src/games/zuma/ZumaGame.js +++ b/src/games/zuma/ZumaGame.js @@ -5,12 +5,10 @@ import { MusicPlayer } from '../../ui/MusicPlayer.js'; import { playSound, playScifiWoosh, playScifiExplode, SFX } from '../../ui/Sounds.js'; import { api } from '../../services/api.js'; import { - TUNING, BALL_COLORS, createLevel, step, fireBall, swapBalls, rayHit, + TUNING, BALL_COLORS, PATH_STYLE, createLevel, step, fireBall, swapBalls, rayHit, } from './ZumaLogic.js'; -const BG = 0x0d1a10; // deep jungle green -const PATH_RIM = 0x241b0e; -const PATH_BED = 0x4a3a26; +const BG = 0x0d1a10; // deep jungle green // The launcher is layered around the marble it holds: frog frame 0 sits under // the ready marble, frame 1 (the disc with the mouth slot cut out) sits over @@ -44,9 +42,6 @@ const TUNE = { INSERT_MS: 120, // squeeze-in tween for a landed shot POP_FX_MS: 320, // particle burst lifetime LASER_ALPHA: 0.55, - PATH_W_RIM: 80, - PATH_W_BED: 67, - GROOVE_STEP: 48, // px between center-groove dots NEXT_SCALE: 0.55, // the on-deck marble shown in the frog's belly }; @@ -548,23 +543,21 @@ export default class ZumaGame extends Phaser.Scene { drawPath() { const samples = this.state.path.samples; const g = this.add.graphics().setDepth(D.path); - g.lineStyle(TUNE.PATH_W_RIM, PATH_RIM, 1); - g.beginPath(); - g.moveTo(samples[0].x, samples[0].y); - for (const s of samples) g.lineTo(s.x, s.y); - g.strokePath(); - g.lineStyle(TUNE.PATH_W_BED, PATH_BED, 1); - g.beginPath(); - g.moveTo(samples[0].x, samples[0].y); - for (const s of samples) g.lineTo(s.x, s.y); - g.strokePath(); - // dotted center groove - g.fillStyle(0x6b5638, 0.55); + for (const { w, color } of PATH_STYLE.bands) { + g.lineStyle(w, color, 1); + g.beginPath(); + g.moveTo(samples[0].x, samples[0].y); + for (const s of samples) g.lineTo(s.x, s.y); + g.strokePath(); + } + // dotted center groove — the seam the ball rolls along, sunk into the + // bright core band above + g.fillStyle(PATH_STYLE.grooveColor, PATH_STYLE.grooveAlpha); let nextDot = 0; for (const s of samples) { if (s.s >= nextDot) { - g.fillCircle(s.x, s.y, 4); - nextDot += TUNE.GROOVE_STEP; + g.fillCircle(s.x, s.y, PATH_STYLE.grooveRadius); + nextDot += PATH_STYLE.grooveStep; } } this.layer.add(g); diff --git a/src/games/zuma/ZumaLogic.js b/src/games/zuma/ZumaLogic.js index 9487a66..4a6d93e 100644 --- a/src/games/zuma/ZumaLogic.js +++ b/src/games/zuma/ZumaLogic.js @@ -56,6 +56,29 @@ export const POWER_KINDS = ['slow', 'reverse', 'accuracy', 'explosion']; // first N of these. Lives here so the scene and the editor share one list. export const BALL_COLORS = [0xd9403a, 0xeec23d, 0x3f7fdb, 0x43b059, 0x9b59d0, 0xd9dde3]; +// The path is drawn as nested strokes on one centerline, widest first — each +// narrower band paints over the middle of the last, leaving only its outer +// edge showing as a ring. Ordered outer to inner it fakes a concave channel's +// cross-section (dark contrast border -> lit embankment lip -> shadowed wall +// -> the floor's own shadow/lit/core bands) with plain solid-color strokes, +// no per-sample normals needed since the profile is symmetric across the +// centerline. Shared by ZumaGame (draw) and ZumaEditor (preview, scaled by K). +export const PATH_STYLE = { + bands: [ + { w: 96, color: 0x0a0704 }, // outer contrast border, pops off any bg + { w: 84, color: 0x362615 }, // embankment lip catching light + { w: 80, color: 0x241b0e }, // embankment wall, in shadow + { w: 71, color: 0x2e2313 }, // shadow where the wall meets the floor + { w: 67, color: 0x4a3a26 }, // main floor + { w: 42, color: 0x6c5735 }, // floor lit by bounced light + { w: 20, color: 0x8f7a52 }, // pale core along the concave bottom + ], + grooveStep: 48, // px between center-groove dots + grooveColor: 0x4a3a1f, + grooveAlpha: 0.4, + grooveRadius: 4, +}; + // ── Seeded RNG (mulberry32, matches genRushHour.js) ───────────────────────── export function makeRng(seed) { let a = seed >>> 0;