@almadar/ui 5.104.0 → 5.105.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16361,7 +16361,7 @@ function screenToIso(screenX, screenY, scale, baseOffsetX, layout = "isometric")
16361
16361
  const tileY = (screenY / (scaledFloorHeight / 2) - adjustedX / (scaledTileWidth / 2)) / 2;
16362
16362
  return { x: Math.round(tileX), y: Math.round(tileY) };
16363
16363
  }
16364
- exports.TILE_WIDTH = void 0; exports.TILE_HEIGHT = void 0; exports.FLOOR_HEIGHT = void 0; exports.DIAMOND_TOP_Y = void 0; exports.FEATURE_COLORS = void 0;
16364
+ exports.TILE_WIDTH = void 0; exports.TILE_HEIGHT = void 0; exports.FLOOR_HEIGHT = void 0; exports.DIAMOND_TOP_Y = void 0; exports.FEATURE_COLORS = void 0; var TERRAIN_COLORS, TEAM_COLORS, TEAM_SHADOW_COLORS, HIGHLIGHT_COLORS, SELECTION_RING_COLOR, GRID_STROKE_COLOR, BACKGROUND_FALLBACK_COLOR, MINIMAP_TERRAIN_COLORS, MINIMAP_UNIT_COLORS, UNIT_LABEL_BG_COLORS, SIDE_PLATFORM_COLORS, SIDE_PLAYER_COLOR, SIDE_PLAYER_EYE_COLOR, SIDE_SKY_GRADIENT_TOP, SIDE_SKY_GRADIENT_BOTTOM, SIDE_GRID_COLOR, SIDE_FX_COLOR, SIDE_PLATFORM_BEVEL_COLOR, SIDE_PLATFORM_SHADOW_COLOR, SIDE_HAZARD_STRIPE_COLOR, SIDE_GOAL_GLOW_COLOR, DEFAULT_BG_COLOR;
16365
16365
  var init_isometric = __esm({
16366
16366
  "components/game/shared/isometric.ts"() {
16367
16367
  exports.TILE_WIDTH = 256;
@@ -16378,12 +16378,70 @@ var init_isometric = __esm({
16378
16378
  mountain: "#78716c",
16379
16379
  default: "#9ca3af"
16380
16380
  };
16381
+ TERRAIN_COLORS = {
16382
+ water: "#3b82f6",
16383
+ mountain: "#78716c",
16384
+ stone: "#9ca3af",
16385
+ default: "#4ade80"
16386
+ };
16387
+ TEAM_COLORS = {
16388
+ player: "#3b82f6",
16389
+ enemy: "#ef4444",
16390
+ neutral: "#6b7280",
16391
+ default: "#6b7280"
16392
+ };
16393
+ TEAM_SHADOW_COLORS = {
16394
+ player: "rgba(0,150,255,0.6)",
16395
+ default: "rgba(255,50,50,0.6)"
16396
+ };
16397
+ HIGHLIGHT_COLORS = {
16398
+ hover: "rgba(255,255,255,0.15)",
16399
+ validMove: "rgba(74,222,128,0.25)",
16400
+ attack: "rgba(239,68,68,0.35)"
16401
+ };
16402
+ SELECTION_RING_COLOR = "rgba(0,200,255,0.8)";
16403
+ GRID_STROKE_COLOR = "rgba(0,0,0,0.2)";
16404
+ BACKGROUND_FALLBACK_COLOR = "#1a1a2e";
16405
+ MINIMAP_TERRAIN_COLORS = {
16406
+ water: "#3b82f6",
16407
+ mountain: "#78716c",
16408
+ default: "#4ade80"
16409
+ };
16410
+ MINIMAP_UNIT_COLORS = {
16411
+ player: "#60a5fa",
16412
+ enemy: "#f87171",
16413
+ neutral: "#9ca3af",
16414
+ default: "#9ca3af"
16415
+ };
16416
+ UNIT_LABEL_BG_COLORS = {
16417
+ player: "rgba(59,130,246,0.9)",
16418
+ enemy: "rgba(239,68,68,0.9)",
16419
+ neutral: "rgba(107,114,128,0.9)",
16420
+ default: "rgba(107,114,128,0.9)"
16421
+ };
16422
+ SIDE_PLATFORM_COLORS = {
16423
+ ground: "#4a7c59",
16424
+ platform: "#7c6b4a",
16425
+ hazard: "#c0392b",
16426
+ goal: "#f1c40f"
16427
+ };
16428
+ SIDE_PLAYER_COLOR = "#3498db";
16429
+ SIDE_PLAYER_EYE_COLOR = "#ffffff";
16430
+ SIDE_SKY_GRADIENT_TOP = "#1a1a2e";
16431
+ SIDE_SKY_GRADIENT_BOTTOM = "#16213e";
16432
+ SIDE_GRID_COLOR = "rgba(255, 255, 255, 0.03)";
16433
+ SIDE_FX_COLOR = "#ffe066";
16434
+ SIDE_PLATFORM_BEVEL_COLOR = "rgba(255,255,255,0.15)";
16435
+ SIDE_PLATFORM_SHADOW_COLOR = "rgba(0,0,0,0.3)";
16436
+ SIDE_HAZARD_STRIPE_COLOR = "#e74c3c";
16437
+ SIDE_GOAL_GLOW_COLOR = "rgba(241, 196, 15, 0.5)";
16438
+ DEFAULT_BG_COLOR = "#5c94fc";
16381
16439
  }
16382
16440
  });
16383
16441
  function normalizeBackdrop(bg) {
16384
16442
  return typeof bg === "string" ? { url: bg, role: "decoration", category: "background" } : bg;
16385
16443
  }
16386
- function SideView({
16444
+ function SideCanvas2D({
16387
16445
  player,
16388
16446
  platforms,
16389
16447
  worldWidth,
@@ -16404,24 +16462,18 @@ function SideView({
16404
16462
  const canvasRef = React75.useRef(null);
16405
16463
  const eventBus = useEventBus();
16406
16464
  const keysRef = React75.useRef(/* @__PURE__ */ new Set());
16407
- const imageCache2 = React75.useRef(/* @__PURE__ */ new Map());
16408
- const [loadedImages, setLoadedImages] = React75.useState(/* @__PURE__ */ new Set());
16409
- const loadImage = React75.useCallback((url) => {
16410
- if (!url) return null;
16411
- const cached = imageCache2.current.get(url);
16412
- if (cached?.complete && cached.naturalWidth > 0) {
16413
- if (!loadedImages.has(url)) setLoadedImages((prev) => new Set(prev).add(url));
16414
- return cached;
16415
- }
16416
- if (!cached) {
16417
- const img = new Image();
16418
- img.crossOrigin = "anonymous";
16419
- img.src = url;
16420
- img.onload = () => setLoadedImages((prev) => new Set(prev).add(url));
16421
- imageCache2.current.set(url, img);
16465
+ const spriteUrls = React75.useMemo(() => {
16466
+ const urls = [];
16467
+ if (backgroundImage?.url) urls.push(backgroundImage.url);
16468
+ if (playerSprite?.url) urls.push(playerSprite.url);
16469
+ if (tileSprites) {
16470
+ for (const asset of Object.values(tileSprites)) {
16471
+ if (asset?.url) urls.push(asset.url);
16472
+ }
16422
16473
  }
16423
- return null;
16424
- }, [loadedImages]);
16474
+ return urls;
16475
+ }, [backgroundImage, playerSprite, tileSprites]);
16476
+ const { getImage } = useImageCache(spriteUrls);
16425
16477
  React75.useEffect(() => {
16426
16478
  if (typeof window === "undefined") return;
16427
16479
  const canvas = canvasRef.current;
@@ -16534,7 +16586,7 @@ function SideView({
16534
16586
  camX = Math.max(0, Math.min(px - cw / 2, ww - cw));
16535
16587
  camY = Math.max(0, Math.min(py - ch / 2 - 50, wh - ch));
16536
16588
  }
16537
- const bgImage = bgImg ? loadImage(bgImg.url) : null;
16589
+ const bgImage = bgImg ? getImage(bgImg.url) : void 0;
16538
16590
  const bgSrc = bgImage ? resolveAssetSource(bgImage, bgImg, NOOP) : null;
16539
16591
  if (bgSrc) {
16540
16592
  blit(ctx, bgSrc, 0, 0, cw, ch);
@@ -16545,12 +16597,12 @@ function SideView({
16545
16597
  ctx.fillRect(0, 0, cw, ch);
16546
16598
  } else {
16547
16599
  const grad = ctx.createLinearGradient(0, 0, 0, ch);
16548
- grad.addColorStop(0, SKY_GRADIENT_TOP);
16549
- grad.addColorStop(1, SKY_GRADIENT_BOTTOM);
16600
+ grad.addColorStop(0, SIDE_SKY_GRADIENT_TOP);
16601
+ grad.addColorStop(1, SIDE_SKY_GRADIENT_BOTTOM);
16550
16602
  ctx.fillStyle = grad;
16551
16603
  ctx.fillRect(0, 0, cw, ch);
16552
16604
  }
16553
- ctx.strokeStyle = GRID_COLOR;
16605
+ ctx.strokeStyle = SIDE_GRID_COLOR;
16554
16606
  ctx.lineWidth = 1;
16555
16607
  const gridSize = 32;
16556
16608
  for (let gx = -camX % gridSize; gx < cw; gx += gridSize) {
@@ -16570,7 +16622,7 @@ function SideView({
16570
16622
  const platY = plat.y - camY;
16571
16623
  const platType = plat.type ?? "ground";
16572
16624
  const spriteAsset = tSprites?.[platType];
16573
- const tileImg = spriteAsset ? loadImage(spriteAsset.url) : null;
16625
+ const tileImg = spriteAsset ? getImage(spriteAsset.url) : void 0;
16574
16626
  const tileSrc = tileImg ? resolveAssetSource(tileImg, spriteAsset, NOOP) : null;
16575
16627
  if (tileSrc) {
16576
16628
  const originX = tileSrc.rect ? tileSrc.rect.sx : 0;
@@ -16585,15 +16637,15 @@ function SideView({
16585
16637
  ctx.drawImage(tileImg, originX, originY, srcW, tileH, platX + tx, platY, drawW, plat.height);
16586
16638
  }
16587
16639
  } else {
16588
- const color = PLATFORM_COLORS[platType] ?? PLATFORM_COLORS.ground;
16640
+ const color = SIDE_PLATFORM_COLORS[platType] ?? SIDE_PLATFORM_COLORS.ground;
16589
16641
  ctx.fillStyle = color;
16590
16642
  ctx.fillRect(platX, platY, plat.width, plat.height);
16591
- ctx.fillStyle = "rgba(255, 255, 255, 0.15)";
16643
+ ctx.fillStyle = SIDE_PLATFORM_BEVEL_COLOR;
16592
16644
  ctx.fillRect(platX, platY, plat.width, 3);
16593
- ctx.fillStyle = "rgba(0, 0, 0, 0.3)";
16645
+ ctx.fillStyle = SIDE_PLATFORM_SHADOW_COLOR;
16594
16646
  ctx.fillRect(platX, platY + plat.height - 2, plat.width, 2);
16595
16647
  if (platType === "hazard") {
16596
- ctx.strokeStyle = "#e74c3c";
16648
+ ctx.strokeStyle = SIDE_HAZARD_STRIPE_COLOR;
16597
16649
  ctx.lineWidth = 2;
16598
16650
  for (let sx = platX; sx < platX + plat.width; sx += 12) {
16599
16651
  ctx.beginPath();
@@ -16603,7 +16655,7 @@ function SideView({
16603
16655
  }
16604
16656
  }
16605
16657
  if (platType === "goal") {
16606
- ctx.fillStyle = "rgba(241, 196, 15, 0.5)";
16658
+ ctx.fillStyle = SIDE_GOAL_GLOW_COLOR;
16607
16659
  ctx.beginPath();
16608
16660
  ctx.arc(platX + plat.width / 2, platY - 10, 8, 0, Math.PI * 2);
16609
16661
  ctx.fill();
@@ -16615,7 +16667,7 @@ function SideView({
16615
16667
  const ppx = px - camX;
16616
16668
  const ppy = py - camY;
16617
16669
  const facingRight = auth.facingRight ?? true;
16618
- const playerImg = pSprite ? loadImage(pSprite.url) : null;
16670
+ const playerImg = pSprite ? getImage(pSprite.url) : void 0;
16619
16671
  const playerSrc = playerImg ? resolveAssetSource(playerImg, pSprite, NOOP) : null;
16620
16672
  if (playerSrc) {
16621
16673
  ctx.save();
@@ -16628,7 +16680,7 @@ function SideView({
16628
16680
  }
16629
16681
  ctx.restore();
16630
16682
  } else {
16631
- ctx.fillStyle = PLAYER_COLOR;
16683
+ ctx.fillStyle = SIDE_PLAYER_COLOR;
16632
16684
  const radius = Math.min(pw, ph) * 0.25;
16633
16685
  ctx.beginPath();
16634
16686
  ctx.moveTo(ppx + radius, ppy);
@@ -16644,7 +16696,7 @@ function SideView({
16644
16696
  const eyeY = ppy + ph * 0.3;
16645
16697
  const eyeSize = 3;
16646
16698
  const eyeOffsetX = facingRight ? pw * 0.55 : pw * 0.2;
16647
- ctx.fillStyle = PLAYER_EYE_COLOR;
16699
+ ctx.fillStyle = SIDE_PLAYER_EYE_COLOR;
16648
16700
  ctx.beginPath();
16649
16701
  ctx.arc(ppx + eyeOffsetX, eyeY, eyeSize, 0, Math.PI * 2);
16650
16702
  ctx.fill();
@@ -16658,7 +16710,7 @@ function SideView({
16658
16710
  const alpha = Math.min(1, fx.ttl / 4);
16659
16711
  const prev = ctx.globalAlpha;
16660
16712
  ctx.globalAlpha = alpha;
16661
- ctx.fillStyle = "#ffe066";
16713
+ ctx.fillStyle = SIDE_FX_COLOR;
16662
16714
  ctx.beginPath();
16663
16715
  ctx.arc(fxScreenX, fxScreenY, 12, 0, Math.PI * 2);
16664
16716
  ctx.fill();
@@ -16666,7 +16718,7 @@ function SideView({
16666
16718
  }
16667
16719
  };
16668
16720
  return interp.startLoop(drawFrame);
16669
- }, [interp.startLoop, loadImage]);
16721
+ }, [interp.startLoop, getImage]);
16670
16722
  return /* @__PURE__ */ jsxRuntime.jsx(
16671
16723
  "canvas",
16672
16724
  {
@@ -16678,6 +16730,33 @@ function SideView({
16678
16730
  }
16679
16731
  );
16680
16732
  }
16733
+ var NOOP;
16734
+ var init_SideCanvas2D = __esm({
16735
+ "components/game/2d/molecules/SideCanvas2D.tsx"() {
16736
+ "use client";
16737
+ init_cn();
16738
+ init_useEventBus();
16739
+ init_useImageCache();
16740
+ init_atlasSlice();
16741
+ init_useRenderInterpolation();
16742
+ init_verificationRegistry();
16743
+ init_isometric();
16744
+ NOOP = () => {
16745
+ };
16746
+ SideCanvas2D.displayName = "SideCanvas2D";
16747
+ }
16748
+ });
16749
+ function normalizeBackdrop2(bg) {
16750
+ return typeof bg === "string" ? { url: bg, role: "decoration", category: "background" } : bg;
16751
+ }
16752
+ function resolveTileFallbackColor(tile) {
16753
+ const override = tile.color;
16754
+ return override ?? TERRAIN_COLORS[tile.terrain ?? ""] ?? TERRAIN_COLORS.default;
16755
+ }
16756
+ function resolveUnitTeamColor(unit) {
16757
+ const override = unit.tint;
16758
+ return override ?? TEAM_COLORS[unit.team ?? ""] ?? TEAM_COLORS.default;
16759
+ }
16681
16760
  function Canvas2D({
16682
16761
  // Closed-circuit
16683
16762
  className,
@@ -16727,13 +16806,13 @@ function Canvas2D({
16727
16806
  // Side-view asset resolution
16728
16807
  playerSprite,
16729
16808
  tileSprites,
16730
- bgColor = "#5c94fc",
16809
+ bgColor = DEFAULT_BG_COLOR,
16731
16810
  worldWidth = 800,
16732
16811
  worldHeight = 400,
16733
16812
  // Remote asset loading
16734
16813
  assetManifest
16735
16814
  }) {
16736
- const backgroundImage = normalizeBackdrop(backgroundImageRaw);
16815
+ const backgroundImage = normalizeBackdrop2(backgroundImageRaw);
16737
16816
  const isSide = projection === "side";
16738
16817
  const isFree = projection === "free";
16739
16818
  const flatLike = projection === "hex" || projection === "flat" || isFree;
@@ -16917,7 +16996,7 @@ function Canvas2D({
16917
16996
  return sortedTiles.map((t2) => ({
16918
16997
  x: t2.x,
16919
16998
  y: t2.y,
16920
- color: t2.terrain === "water" ? "#3b82f6" : t2.terrain === "mountain" ? "#78716c" : "#4ade80"
16999
+ color: MINIMAP_TERRAIN_COLORS[t2.terrain ?? ""] ?? MINIMAP_TERRAIN_COLORS.default
16921
17000
  }));
16922
17001
  }, [showMinimap, sortedTiles]);
16923
17002
  const miniMapUnits = React75.useMemo(() => {
@@ -16925,7 +17004,7 @@ function Canvas2D({
16925
17004
  return units.filter((u) => u.position).map((u) => ({
16926
17005
  x: u.position.x,
16927
17006
  y: u.position.y,
16928
- color: u.team === "player" ? "#60a5fa" : u.team === "enemy" ? "#f87171" : "#9ca3af",
17007
+ color: MINIMAP_UNIT_COLORS[u.team ?? ""] ?? MINIMAP_UNIT_COLORS.default,
16929
17008
  isPlayer: u.team === "player"
16930
17009
  }));
16931
17010
  }, [showMinimap, units]);
@@ -16962,7 +17041,7 @@ function Canvas2D({
16962
17041
  }
16963
17042
  }
16964
17043
  } else {
16965
- ctx.fillStyle = "#1a1a2e";
17044
+ ctx.fillStyle = BACKGROUND_FALLBACK_COLOR;
16966
17045
  ctx.fillRect(0, 0, viewportSize.width, viewportSize.height);
16967
17046
  }
16968
17047
  if (sortedTiles.length === 0 && units.length === 0 && features.length === 0) return;
@@ -16992,15 +17071,15 @@ function Canvas2D({
16992
17071
  } else if (img && img.naturalWidth === 0) {
16993
17072
  ctx.drawImage(img, pos.x, pos.y, scaledTileWidth, scaledTileHeight);
16994
17073
  } else if (squareGrid) {
16995
- ctx.fillStyle = tile.terrain === "water" ? "#3b82f6" : tile.terrain === "mountain" ? "#78716c" : tile.terrain === "stone" ? "#9ca3af" : "#4ade80";
17074
+ ctx.fillStyle = resolveTileFallbackColor(tile);
16996
17075
  ctx.fillRect(pos.x, pos.y, scaledTileWidth, scaledTileWidth);
16997
- ctx.strokeStyle = "rgba(0,0,0,0.2)";
17076
+ ctx.strokeStyle = GRID_STROKE_COLOR;
16998
17077
  ctx.lineWidth = 1;
16999
17078
  ctx.strokeRect(pos.x, pos.y, scaledTileWidth, scaledTileWidth);
17000
17079
  } else {
17001
17080
  const centerX = pos.x + scaledTileWidth / 2;
17002
17081
  const topY = pos.y + scaledDiamondTopY;
17003
- ctx.fillStyle = tile.terrain === "water" ? "#3b82f6" : tile.terrain === "mountain" ? "#78716c" : tile.terrain === "stone" ? "#9ca3af" : "#4ade80";
17082
+ ctx.fillStyle = resolveTileFallbackColor(tile);
17004
17083
  ctx.beginPath();
17005
17084
  ctx.moveTo(centerX, topY);
17006
17085
  ctx.lineTo(pos.x + scaledTileWidth, topY + scaledFloorHeight / 2);
@@ -17008,7 +17087,7 @@ function Canvas2D({
17008
17087
  ctx.lineTo(pos.x, topY + scaledFloorHeight / 2);
17009
17088
  ctx.closePath();
17010
17089
  ctx.fill();
17011
- ctx.strokeStyle = "rgba(0,0,0,0.2)";
17090
+ ctx.strokeStyle = GRID_STROKE_COLOR;
17012
17091
  ctx.lineWidth = 1;
17013
17092
  ctx.stroke();
17014
17093
  }
@@ -17029,11 +17108,11 @@ function Canvas2D({
17029
17108
  ctx.fill();
17030
17109
  };
17031
17110
  if (hoveredTile && hoveredTile.x === tile.x && hoveredTile.y === tile.y) {
17032
- drawHighlight("rgba(255, 255, 255, 0.15)");
17111
+ drawHighlight(HIGHLIGHT_COLORS.hover);
17033
17112
  }
17034
17113
  const tileKey = `${tile.x},${tile.y}`;
17035
- if (validMoveSet.has(tileKey)) drawHighlight("rgba(74, 222, 128, 0.25)");
17036
- if (attackTargetSet.has(tileKey)) drawHighlight("rgba(239, 68, 68, 0.35)");
17114
+ if (validMoveSet.has(tileKey)) drawHighlight(HIGHLIGHT_COLORS.validMove);
17115
+ if (attackTargetSet.has(tileKey)) drawHighlight(HIGHLIGHT_COLORS.attack);
17037
17116
  if (debug2) {
17038
17117
  const centerX = pos.x + scaledTileWidth / 2;
17039
17118
  const centerY = squareGrid ? pos.y + scaledTileWidth / 2 : pos.y + scaledFloorHeight / 2 + scaledDiamondTopY;
@@ -17107,9 +17186,9 @@ function Canvas2D({
17107
17186
  const maxUnitW = scaledTileWidth * spriteMaxWidthRatio * unitScale;
17108
17187
  const unitIsSheet = unit.spriteSheet?.url !== void 0;
17109
17188
  const unitSrc = img && !unitIsSheet ? resolveAssetSource(img, unitAsset, bumpAtlas) : null;
17110
- const SHEET_ROWS = 5;
17111
17189
  const sheetFrameW = img ? img.naturalWidth / exports.SHEET_COLUMNS : 0;
17112
- const sheetFrameH = img ? img.naturalHeight / SHEET_ROWS : 0;
17190
+ const sheetFrameH = img ? img.naturalHeight / LEGACY_SHEET_ROWS : 0;
17191
+ const legacyFrame = frameRect(0, 0, exports.SHEET_COLUMNS, sheetFrameW, sheetFrameH);
17113
17192
  const frameW = unitIsSheet ? sheetFrameW : img?.naturalWidth ?? 1;
17114
17193
  const frameH = unitIsSheet ? sheetFrameH : img?.naturalHeight ?? 1;
17115
17194
  const ar = unitIsSheet ? sheetFrameW / (sheetFrameH || 1) : unitSrc ? unitSrc.aspect : frameW / (frameH || 1);
@@ -17127,17 +17206,16 @@ function Canvas2D({
17127
17206
  ctx.globalAlpha = 0.25;
17128
17207
  if (img) {
17129
17208
  if (unitIsSheet) {
17130
- ctx.drawImage(img, 0, 0, sheetFrameW, sheetFrameH, ghostCenterX - drawW / 2, ghostGroundY - drawH, drawW, drawH);
17209
+ ctx.drawImage(img, legacyFrame.sx, legacyFrame.sy, legacyFrame.sw, legacyFrame.sh, ghostCenterX - drawW / 2, ghostGroundY - drawH, drawW, drawH);
17131
17210
  } else if (unitSrc) {
17132
17211
  blit(ctx, unitSrc, ghostCenterX - drawW / 2, ghostGroundY - drawH, drawW, drawH);
17133
17212
  } else {
17134
17213
  ctx.drawImage(img, ghostCenterX - drawW / 2, ghostGroundY - drawH, drawW, drawH);
17135
17214
  }
17136
17215
  } else {
17137
- const color = unit.team === "player" ? "#3b82f6" : unit.team === "enemy" ? "#ef4444" : "#6b7280";
17138
17216
  ctx.beginPath();
17139
17217
  ctx.arc(ghostCenterX, ghostGroundY - 16 * scale, 20 * scale, 0, Math.PI * 2);
17140
- ctx.fillStyle = color;
17218
+ ctx.fillStyle = resolveUnitTeamColor(unit);
17141
17219
  ctx.fill();
17142
17220
  }
17143
17221
  ctx.restore();
@@ -17145,7 +17223,7 @@ function Canvas2D({
17145
17223
  if (isSelected) {
17146
17224
  ctx.beginPath();
17147
17225
  ctx.ellipse(centerX, groundY, drawW / 2 + 4 * scale, 12 * scale, 0, 0, Math.PI * 2);
17148
- ctx.strokeStyle = "rgba(0, 200, 255, 0.8)";
17226
+ ctx.strokeStyle = SELECTION_RING_COLOR;
17149
17227
  ctx.lineWidth = 3;
17150
17228
  ctx.stroke();
17151
17229
  }
@@ -17162,7 +17240,7 @@ function Canvas2D({
17162
17240
  const spriteY = groundY - fDrawH - (frame.applyBreathing ? breatheOffset : 0);
17163
17241
  ctx.save();
17164
17242
  if (unit.team) {
17165
- ctx.shadowColor = unit.team === "player" ? "rgba(0, 150, 255, 0.6)" : "rgba(255, 50, 50, 0.6)";
17243
+ ctx.shadowColor = TEAM_SHADOW_COLORS[unit.team] ?? TEAM_SHADOW_COLORS.default;
17166
17244
  ctx.shadowBlur = 12 * scale;
17167
17245
  }
17168
17246
  if (frame.flipX) {
@@ -17177,7 +17255,7 @@ function Canvas2D({
17177
17255
  const spriteY = groundY - drawH - breatheOffset;
17178
17256
  const drawUnit = (x) => {
17179
17257
  if (unitIsSheet) {
17180
- ctx.drawImage(img, 0, 0, sheetFrameW, sheetFrameH, x, spriteY, drawW, drawH);
17258
+ ctx.drawImage(img, legacyFrame.sx, legacyFrame.sy, legacyFrame.sw, legacyFrame.sh, x, spriteY, drawW, drawH);
17181
17259
  } else if (unitSrc) {
17182
17260
  blit(ctx, unitSrc, x, spriteY, drawW, drawH);
17183
17261
  } else {
@@ -17186,7 +17264,7 @@ function Canvas2D({
17186
17264
  };
17187
17265
  if (unit.team) {
17188
17266
  ctx.save();
17189
- ctx.shadowColor = unit.team === "player" ? "rgba(0, 150, 255, 0.6)" : "rgba(255, 50, 50, 0.6)";
17267
+ ctx.shadowColor = TEAM_SHADOW_COLORS[unit.team] ?? TEAM_SHADOW_COLORS.default;
17190
17268
  ctx.shadowBlur = 12 * scale;
17191
17269
  drawUnit(centerX - drawW / 2);
17192
17270
  ctx.restore();
@@ -17194,10 +17272,9 @@ function Canvas2D({
17194
17272
  drawUnit(centerX - drawW / 2);
17195
17273
  }
17196
17274
  } else {
17197
- const color = unit.team === "player" ? "#3b82f6" : unit.team === "enemy" ? "#ef4444" : "#6b7280";
17198
17275
  ctx.beginPath();
17199
17276
  ctx.arc(centerX, groundY - 20 * scale - breatheOffset, 20 * scale, 0, Math.PI * 2);
17200
- ctx.fillStyle = color;
17277
+ ctx.fillStyle = resolveUnitTeamColor(unit);
17201
17278
  ctx.fill();
17202
17279
  ctx.strokeStyle = "rgba(255,255,255,0.8)";
17203
17280
  ctx.lineWidth = 2;
@@ -17436,7 +17513,7 @@ function Canvas2D({
17436
17513
  }
17437
17514
  if (isSide) {
17438
17515
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { ref: containerRef, className: cn("relative overflow-hidden w-full h-full", className), children: /* @__PURE__ */ jsxRuntime.jsx(
17439
- SideView,
17516
+ SideCanvas2D,
17440
17517
  {
17441
17518
  player,
17442
17519
  platforms: platforms && platforms.length ? platforms : DEFAULT_PLATFORMS,
@@ -17534,7 +17611,7 @@ function Canvas2D({
17534
17611
  {
17535
17612
  as: "span",
17536
17613
  className: "text-white text-xs font-bold px-1.5 py-0.5 rounded mb-0.5 whitespace-nowrap block",
17537
- style: { background: unit.team === "player" ? "rgba(59,130,246,0.9)" : unit.team === "enemy" ? "rgba(239,68,68,0.9)" : "rgba(107,114,128,0.9)" },
17614
+ style: { background: UNIT_LABEL_BG_COLORS[unit.team ?? ""] ?? UNIT_LABEL_BG_COLORS.default },
17538
17615
  children: unit.name
17539
17616
  }
17540
17617
  ),
@@ -17558,7 +17635,7 @@ function Canvas2D({
17558
17635
  }
17559
17636
  );
17560
17637
  }
17561
- var PLATFORM_COLORS, NOOP, PLAYER_COLOR, PLAYER_EYE_COLOR, SKY_GRADIENT_TOP, SKY_GRADIENT_BOTTOM, GRID_COLOR, DEFAULT_PLATFORMS;
17638
+ var LEGACY_SHEET_ROWS, DEFAULT_PLATFORMS;
17562
17639
  var init_Canvas2D = __esm({
17563
17640
  "components/game/2d/molecules/Canvas2D.tsx"() {
17564
17641
  "use client";
@@ -17580,19 +17657,9 @@ var init_Canvas2D = __esm({
17580
17657
  init_verificationRegistry();
17581
17658
  init_isometric();
17582
17659
  init_spriteSheetConstants();
17583
- PLATFORM_COLORS = {
17584
- ground: "#4a7c59",
17585
- platform: "#7c6b4a",
17586
- hazard: "#c0392b",
17587
- goal: "#f1c40f"
17588
- };
17589
- NOOP = () => {
17590
- };
17591
- PLAYER_COLOR = "#3498db";
17592
- PLAYER_EYE_COLOR = "#ffffff";
17593
- SKY_GRADIENT_TOP = "#1a1a2e";
17594
- SKY_GRADIENT_BOTTOM = "#16213e";
17595
- GRID_COLOR = "rgba(255, 255, 255, 0.03)";
17660
+ init_spriteAnimation();
17661
+ init_SideCanvas2D();
17662
+ LEGACY_SHEET_ROWS = Object.keys(exports.SPRITE_SHEET_LAYOUT).length;
17596
17663
  DEFAULT_PLATFORMS = [
17597
17664
  { x: 0, y: 368, width: 800, height: 32, type: "ground" },
17598
17665
  { x: 150, y: 280, width: 160, height: 16, type: "platform" },
@@ -32214,13 +32281,13 @@ var init_MapView = __esm({
32214
32281
  shadowSize: [41, 41]
32215
32282
  });
32216
32283
  L.Marker.prototype.options.icon = defaultIcon;
32217
- const { useEffect: useEffect70, useRef: useRef66, useCallback: useCallback113, useState: useState108 } = React75__namespace.default;
32284
+ const { useEffect: useEffect71, useRef: useRef67, useCallback: useCallback114, useState: useState108 } = React75__namespace.default;
32218
32285
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
32219
32286
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
32220
32287
  function MapUpdater({ centerLat, centerLng, zoom }) {
32221
32288
  const map = useMap();
32222
- const prevRef = useRef66({ centerLat, centerLng, zoom });
32223
- useEffect70(() => {
32289
+ const prevRef = useRef67({ centerLat, centerLng, zoom });
32290
+ useEffect71(() => {
32224
32291
  const prev = prevRef.current;
32225
32292
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
32226
32293
  map.setView([centerLat, centerLng], zoom);
@@ -32231,7 +32298,7 @@ var init_MapView = __esm({
32231
32298
  }
32232
32299
  function MapClickHandler({ onMapClick }) {
32233
32300
  const map = useMap();
32234
- useEffect70(() => {
32301
+ useEffect71(() => {
32235
32302
  if (!onMapClick) return;
32236
32303
  const handler = (e) => {
32237
32304
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -32260,7 +32327,7 @@ var init_MapView = __esm({
32260
32327
  }) {
32261
32328
  const eventBus = useEventBus2();
32262
32329
  const [clickedPosition, setClickedPosition] = useState108(null);
32263
- const handleMapClick = useCallback113((lat, lng) => {
32330
+ const handleMapClick = useCallback114((lat, lng) => {
32264
32331
  if (showClickedPin) {
32265
32332
  setClickedPosition({ lat, lng });
32266
32333
  }
@@ -32269,7 +32336,7 @@ var init_MapView = __esm({
32269
32336
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
32270
32337
  }
32271
32338
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
32272
- const handleMarkerClick = useCallback113((marker) => {
32339
+ const handleMarkerClick = useCallback114((marker) => {
32273
32340
  onMarkerClick?.(marker);
32274
32341
  if (markerClickEvent) {
32275
32342
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });