@almadar/ui 5.47.0 → 5.49.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.
Files changed (45) hide show
  1. package/dist/avl/index.cjs +2998 -443
  2. package/dist/avl/index.css +504 -0
  3. package/dist/avl/index.js +2998 -444
  4. package/dist/components/core/templates/index.d.ts +3 -0
  5. package/dist/components/game/atoms/ActionButton.d.ts +4 -1
  6. package/dist/components/game/atoms/ComboCounter.d.ts +4 -1
  7. package/dist/components/game/atoms/ControlButton.d.ts +4 -2
  8. package/dist/components/game/atoms/DamageNumber.d.ts +4 -1
  9. package/dist/components/game/atoms/ResourceCounter.d.ts +4 -1
  10. package/dist/components/game/atoms/ScoreDisplay.d.ts +4 -1
  11. package/dist/components/game/atoms/StateIndicator.d.ts +4 -7
  12. package/dist/components/game/atoms/StatusEffect.d.ts +4 -1
  13. package/dist/components/game/atoms/TurnIndicator.d.ts +4 -1
  14. package/dist/components/game/atoms/WaypointMarker.d.ts +4 -1
  15. package/dist/components/game/molecules/EnemyPlate.d.ts +4 -1
  16. package/dist/components/game/molecules/StatBadge.d.ts +4 -1
  17. package/dist/components/game/molecules/three/index.cjs +1428 -346
  18. package/dist/components/game/molecules/three/index.css +0 -5
  19. package/dist/components/game/molecules/three/index.js +1429 -347
  20. package/dist/components/game/organisms/CastleBoard.d.ts +10 -1
  21. package/dist/components/game/organisms/GameBoard3D.d.ts +62 -0
  22. package/dist/components/game/organisms/PlatformerBoard.d.ts +51 -0
  23. package/dist/components/game/organisms/RoguelikeBoard.d.ts +59 -0
  24. package/dist/components/game/organisms/TowerDefenseBoard.d.ts +64 -0
  25. package/dist/components/game/organisms/index.d.ts +4 -0
  26. package/dist/components/game/organisms/puzzles/event-handler/EventHandlerBoard.d.ts +9 -1
  27. package/dist/components/game/organisms/puzzles/state-architect/StateArchitectBoard.d.ts +14 -1
  28. package/dist/components/game/templates/GameCanvas3DBattleTemplate.d.ts +22 -35
  29. package/dist/components/game/templates/GameCanvas3DCastleTemplate.d.ts +23 -23
  30. package/dist/components/game/templates/GameCanvas3DWorldMapTemplate.d.ts +27 -29
  31. package/dist/components/game/templates/PlatformerTemplate.d.ts +26 -0
  32. package/dist/components/game/templates/RoguelikeTemplate.d.ts +23 -0
  33. package/dist/components/game/templates/TowerDefenseTemplate.d.ts +43 -0
  34. package/dist/components/index.cjs +2991 -422
  35. package/dist/components/index.css +504 -0
  36. package/dist/components/index.js +2985 -424
  37. package/dist/docs/index.css +504 -0
  38. package/dist/providers/index.cjs +2981 -426
  39. package/dist/providers/index.css +504 -0
  40. package/dist/providers/index.js +2981 -427
  41. package/dist/runtime/index.cjs +2982 -427
  42. package/dist/runtime/index.css +504 -0
  43. package/dist/runtime/index.js +2982 -428
  44. package/package.json +1 -1
  45. package/tailwind-preset.cjs +1 -0
@@ -40,6 +40,12 @@ var core = require('@dnd-kit/core');
40
40
  var sortable = require('@dnd-kit/sortable');
41
41
  var utilities = require('@dnd-kit/utilities');
42
42
  var react = require('@xyflow/react');
43
+ var THREE = require('three');
44
+ var GLTFLoader_js = require('three/examples/jsm/loaders/GLTFLoader.js');
45
+ var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
46
+ var GLTFLoader = require('three/examples/jsm/loaders/GLTFLoader');
47
+ var fiber = require('@react-three/fiber');
48
+ var drei = require('@react-three/drei');
43
49
  var context = require('@almadar/ui/context');
44
50
  var patterns = require('@almadar/patterns');
45
51
 
@@ -88,6 +94,7 @@ var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
88
94
  var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
89
95
  var remarkMath__default = /*#__PURE__*/_interopDefault(remarkMath);
90
96
  var rehypeKatex__default = /*#__PURE__*/_interopDefault(rehypeKatex);
97
+ var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
91
98
 
92
99
  var __defProp = Object.defineProperty;
93
100
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -1321,9 +1328,9 @@ function resolvePhosphor(name, weight, family) {
1321
1328
  (lib) => {
1322
1329
  const PhosphorComp = lib[target];
1323
1330
  if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
1324
- const Component = PhosphorComp;
1331
+ const Component2 = PhosphorComp;
1325
1332
  const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
1326
- Component,
1333
+ Component2,
1327
1334
  {
1328
1335
  weight,
1329
1336
  className: props.className,
@@ -1347,9 +1354,9 @@ function resolveTabler(name, family) {
1347
1354
  (lib) => {
1348
1355
  const TablerComp = lib[target];
1349
1356
  if (!TablerComp || typeof TablerComp !== "object") return null;
1350
- const Component = TablerComp;
1357
+ const Component2 = TablerComp;
1351
1358
  const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
1352
- Component,
1359
+ Component2,
1353
1360
  {
1354
1361
  stroke: props.strokeWidth ?? 1.5,
1355
1362
  className: props.className,
@@ -1373,9 +1380,9 @@ function resolveFa(name, family) {
1373
1380
  (lib) => {
1374
1381
  const FaComp = lib[target];
1375
1382
  if (!FaComp || typeof FaComp !== "function") return null;
1376
- const Component = FaComp;
1383
+ const Component2 = FaComp;
1377
1384
  const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
1378
- Component,
1385
+ Component2,
1379
1386
  {
1380
1387
  className: props.className,
1381
1388
  style: props.style,
@@ -3368,7 +3375,7 @@ var init_Box = __esm({
3368
3375
  position,
3369
3376
  className,
3370
3377
  children,
3371
- as: Component = "div",
3378
+ as: Component2 = "div",
3372
3379
  action,
3373
3380
  actionPayload,
3374
3381
  hoverEvent,
@@ -3400,7 +3407,7 @@ var init_Box = __esm({
3400
3407
  }, [hoverEvent, eventBus, onMouseLeave]);
3401
3408
  const isClickable = action || onClick;
3402
3409
  return React77__namespace.default.createElement(
3403
- Component,
3410
+ Component2,
3404
3411
  {
3405
3412
  ref,
3406
3413
  className: cn(
@@ -3449,10 +3456,10 @@ var init_Center = __esm({
3449
3456
  className,
3450
3457
  style,
3451
3458
  children,
3452
- as: Component = "div"
3459
+ as: Component2 = "div"
3453
3460
  }) => {
3454
3461
  const mergedStyle = minHeight ? { minHeight, ...style } : style;
3455
- const Comp = Component;
3462
+ const Comp = Component2;
3456
3463
  return /* @__PURE__ */ jsxRuntime.jsx(
3457
3464
  Comp,
3458
3465
  {
@@ -4083,7 +4090,7 @@ var init_Stack = __esm({
4083
4090
  className,
4084
4091
  style,
4085
4092
  children,
4086
- as: Component = "div",
4093
+ as: Component2 = "div",
4087
4094
  onClick,
4088
4095
  onKeyDown,
4089
4096
  role,
@@ -4101,7 +4108,7 @@ var init_Stack = __esm({
4101
4108
  };
4102
4109
  const isHorizontal = direction === "horizontal";
4103
4110
  const directionClass = responsive && isHorizontal ? reverse ? "flex-col-reverse md:flex-row-reverse" : "flex-col md:flex-row" : isHorizontal ? reverse ? "flex-row-reverse" : "flex-row" : reverse ? "flex-col-reverse" : "flex-col";
4104
- const Comp = Component;
4111
+ const Comp = Component2;
4105
4112
  return /* @__PURE__ */ jsxRuntime.jsx(
4106
4113
  Comp,
4107
4114
  {
@@ -4292,8 +4299,8 @@ var init_Typography = __esm({
4292
4299
  children
4293
4300
  }) => {
4294
4301
  const variant = variantProp ?? (level ? `h${level}` : "body1");
4295
- const Component = as || defaultElements[variant];
4296
- const Comp = Component;
4302
+ const Component2 = as || defaultElements[variant];
4303
+ const Comp = Component2;
4297
4304
  return /* @__PURE__ */ jsxRuntime.jsx(
4298
4305
  Comp,
4299
4306
  {
@@ -7506,10 +7513,10 @@ var init_Container = __esm({
7506
7513
  center = true,
7507
7514
  className,
7508
7515
  children,
7509
- as: Component = "div"
7516
+ as: Component2 = "div"
7510
7517
  }) => {
7511
7518
  const resolvedSize = maxWidth ?? size ?? "lg";
7512
- const Comp = Component;
7519
+ const Comp = Component2;
7513
7520
  return /* @__PURE__ */ jsxRuntime.jsx(
7514
7521
  Comp,
7515
7522
  {
@@ -8224,6 +8231,7 @@ var init_Accordion = __esm({
8224
8231
  }
8225
8232
  });
8226
8233
  function ActionButton({
8234
+ assetUrl = DEFAULT_ASSET_URL,
8227
8235
  label = "Attack",
8228
8236
  icon,
8229
8237
  cooldown = 0,
@@ -8263,13 +8271,23 @@ function ActionButton({
8263
8271
  }
8264
8272
  }
8265
8273
  ),
8266
- icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? (() => {
8274
+ assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
8275
+ "img",
8276
+ {
8277
+ src: assetUrl,
8278
+ alt: "",
8279
+ width: 16,
8280
+ height: 16,
8281
+ style: { imageRendering: "pixelated", objectFit: "contain" },
8282
+ className: cn("flex-shrink-0", sizes.icon)
8283
+ }
8284
+ ) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? (() => {
8267
8285
  const I = resolveIcon(icon);
8268
8286
  return I ? /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-4 h-4" }) : null;
8269
8287
  })() : /* @__PURE__ */ (() => {
8270
8288
  const I = icon;
8271
8289
  return /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-4 h-4" });
8272
- })() }),
8290
+ })() }) : null,
8273
8291
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative z-10", children: label }),
8274
8292
  hotkey && /* @__PURE__ */ jsxRuntime.jsx(
8275
8293
  "span",
@@ -8285,7 +8303,7 @@ function ActionButton({
8285
8303
  }
8286
8304
  );
8287
8305
  }
8288
- var sizeMap, variantStyles7;
8306
+ var sizeMap, variantStyles7, DEFAULT_ASSET_URL;
8289
8307
  var init_ActionButton = __esm({
8290
8308
  "components/game/atoms/ActionButton.tsx"() {
8291
8309
  init_cn();
@@ -8300,10 +8318,12 @@ var init_ActionButton = __esm({
8300
8318
  secondary: "bg-secondary text-secondary-foreground hover:bg-secondary-hover border-border",
8301
8319
  danger: "bg-error text-error-foreground hover:bg-error/90 border-error"
8302
8320
  };
8321
+ DEFAULT_ASSET_URL = "https://almadar-kflow-assets.web.app/shared/effects/particles/slash_01.png";
8303
8322
  ActionButton.displayName = "ActionButton";
8304
8323
  }
8305
8324
  });
8306
8325
  function ControlButton({
8326
+ assetUrl = DEFAULT_ASSET_URL2,
8307
8327
  label,
8308
8328
  icon,
8309
8329
  size = "md",
@@ -8350,7 +8370,7 @@ function ControlButton({
8350
8370
  },
8351
8371
  [isPressed, releaseEvent, eventBus, onRelease]
8352
8372
  );
8353
- return /* @__PURE__ */ jsxRuntime.jsxs(
8373
+ return /* @__PURE__ */ jsxRuntime.jsx(
8354
8374
  "button",
8355
8375
  {
8356
8376
  type: "button",
@@ -8371,20 +8391,27 @@ function ControlButton({
8371
8391
  disabled && "opacity-50 cursor-not-allowed",
8372
8392
  className
8373
8393
  ),
8374
- children: [
8375
- icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-2xl", children: typeof icon === "string" ? /^[a-zA-Z0-9-]+$/.test(icon) ? (() => {
8376
- const I = resolveIcon(icon);
8377
- return I ? /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-6 h-6" }) : null;
8378
- })() : icon : /* @__PURE__ */ (() => {
8379
- const I = icon;
8380
- return /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-6 h-6" });
8381
- })() }),
8382
- label && !icon && /* @__PURE__ */ jsxRuntime.jsx("span", { children: label })
8383
- ]
8394
+ children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
8395
+ "img",
8396
+ {
8397
+ src: assetUrl,
8398
+ alt: "",
8399
+ width: 24,
8400
+ height: 24,
8401
+ style: { imageRendering: "pixelated", objectFit: "contain" },
8402
+ className: "flex-shrink-0"
8403
+ }
8404
+ ) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-2xl", children: typeof icon === "string" ? /^[a-zA-Z0-9-]+$/.test(icon) ? (() => {
8405
+ const I = resolveIcon(icon);
8406
+ return I ? /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-6 h-6" }) : null;
8407
+ })() : icon : /* @__PURE__ */ (() => {
8408
+ const I = icon;
8409
+ return /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-6 h-6" });
8410
+ })() }) : label ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: label }) : null
8384
8411
  }
8385
8412
  );
8386
8413
  }
8387
- var sizeMap2, shapeMap, variantMap;
8414
+ var sizeMap2, shapeMap, variantMap, DEFAULT_ASSET_URL2;
8388
8415
  var init_ControlButton = __esm({
8389
8416
  "components/game/atoms/ControlButton.tsx"() {
8390
8417
  "use client";
@@ -8407,6 +8434,7 @@ var init_ControlButton = __esm({
8407
8434
  secondary: "bg-secondary text-secondary-foreground border-border hover:bg-secondary-hover",
8408
8435
  ghost: "bg-transparent text-foreground border-border hover:bg-muted"
8409
8436
  };
8437
+ DEFAULT_ASSET_URL2 = "https://almadar-kflow-assets.web.app/shared/effects/particles/circle_01.png";
8410
8438
  ControlButton.displayName = "ControlButton";
8411
8439
  }
8412
8440
  });
@@ -10429,15 +10457,15 @@ function BattleBoard({
10429
10457
  const board = boardEntity(entity) ?? {};
10430
10458
  const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
10431
10459
  const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
10432
- const boardWidth = num(board.boardWidth, 8);
10433
- const boardHeight = num(board.boardHeight, 6);
10460
+ const boardWidth = num(board.gridWidth ?? board.boardWidth, 8);
10461
+ const boardHeight = num(board.gridHeight ?? board.boardHeight, 6);
10434
10462
  const assetManifest = propAssetManifest ?? board.assetManifest;
10435
10463
  const backgroundImage = board.backgroundImage;
10436
10464
  const units = rows(board.units);
10437
10465
  const selectedUnitId = board.selectedUnitId ?? null;
10438
10466
  const currentPhase = str(board.phase) || "observation";
10439
10467
  const currentTurn = num(board.turn, 1);
10440
- const gameResult = board.gameResult ?? null;
10468
+ const gameResult = board.result ?? null;
10441
10469
  const eventBus = useEventBus();
10442
10470
  const { t } = hooks.useTranslate();
10443
10471
  const [hoveredTile, setHoveredTile] = React77.useState(null);
@@ -10458,7 +10486,7 @@ function BattleBoard({
10458
10486
  const validMoves = React77.useMemo(() => {
10459
10487
  if (!selectedUnit || currentPhase !== "movement") return [];
10460
10488
  const moves = [];
10461
- const range = num(selectedUnit.movement);
10489
+ const range = num(board.movementRange, 2);
10462
10490
  const origin = unitPosition(selectedUnit);
10463
10491
  for (let dy = -range; dy <= range; dy++) {
10464
10492
  for (let dx = -range; dx <= range; dx++) {
@@ -18743,15 +18771,24 @@ function CastleBoard({
18743
18771
  featureClickEvent,
18744
18772
  unitClickEvent,
18745
18773
  tileClickEvent,
18774
+ playAgainEvent,
18746
18775
  className
18747
18776
  }) {
18748
18777
  const eventBus = useEventBus();
18778
+ const { t } = hooks.useTranslate();
18749
18779
  const resolved = boardEntity(entity);
18750
18780
  const tiles = propTiles ?? (Array.isArray(resolved?.tiles) ? resolved.tiles : []);
18751
18781
  const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
18752
18782
  const units = propUnits ?? (Array.isArray(resolved?.units) ? resolved.units : []);
18753
18783
  const assetManifest = propAssetManifest ?? resolved?.assetManifest;
18754
18784
  const backgroundImage = resolved?.backgroundImage;
18785
+ const gold = num(resolved?.gold);
18786
+ const health = num(resolved?.health);
18787
+ const maxHealth = num(resolved?.maxHealth);
18788
+ const wave = num(resolved?.wave);
18789
+ const tickCount = num(resolved?.tickCount);
18790
+ const buildings = rows(resolved?.buildings);
18791
+ const result = str(resolved?.result) || "none";
18755
18792
  const [hoveredTile, setHoveredTile] = React77.useState(null);
18756
18793
  const [selectedFeature, setSelectedFeature] = React77.useState(null);
18757
18794
  const hoveredFeature = React77.useMemo(() => {
@@ -18764,7 +18801,7 @@ function CastleBoard({
18764
18801
  (u) => u.position?.x === hoveredTile.x && u.position?.y === hoveredTile.y
18765
18802
  ) ?? null;
18766
18803
  }, [hoveredTile, units]);
18767
- const maxY = Math.max(...tiles.map((t) => t.y), 0);
18804
+ const maxY = Math.max(...tiles.map((t2) => t2.y), 0);
18768
18805
  const baseOffsetX = (maxY + 1) * (exports.TILE_WIDTH * scale / 2);
18769
18806
  const tileToScreen = React77.useCallback(
18770
18807
  (tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
@@ -18799,6 +18836,11 @@ function CastleBoard({
18799
18836
  }
18800
18837
  }, [units, onUnitClick, unitClickEvent, eventBus]);
18801
18838
  const clearSelection = React77.useCallback(() => setSelectedFeature(null), []);
18839
+ const handlePlayAgain = React77.useCallback(() => {
18840
+ if (playAgainEvent) {
18841
+ eventBus.emit(`UI:${playAgainEvent}`, {});
18842
+ }
18843
+ }, [playAgainEvent, eventBus]);
18802
18844
  const ctx = React77.useMemo(
18803
18845
  () => ({
18804
18846
  hoveredTile,
@@ -18807,11 +18849,18 @@ function CastleBoard({
18807
18849
  selectedFeature,
18808
18850
  clearSelection,
18809
18851
  tileToScreen,
18810
- scale
18852
+ scale,
18853
+ gold,
18854
+ health,
18855
+ maxHealth,
18856
+ wave,
18857
+ tickCount,
18858
+ buildings,
18859
+ result
18811
18860
  }),
18812
- [hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale]
18861
+ [hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale, gold, health, maxHealth, wave, tickCount, buildings, result]
18813
18862
  );
18814
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("castle-board min-h-screen flex flex-col bg-background", className), children: [
18863
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("castle-board relative min-h-screen flex flex-col bg-background", className), children: [
18815
18864
  header && header(ctx),
18816
18865
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
18817
18866
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-auto p-4 relative", children: [
@@ -18836,7 +18885,29 @@ function CastleBoard({
18836
18885
  ] }),
18837
18886
  sidePanel && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-96 shrink-0 border-l border-border bg-surface overflow-y-auto", children: sidePanel(ctx) })
18838
18887
  ] }),
18839
- footer && footer(ctx)
18888
+ footer && footer(ctx),
18889
+ result !== "none" && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { className: "text-center p-8", gap: "lg", children: [
18890
+ /* @__PURE__ */ jsxRuntime.jsx(
18891
+ exports.Typography,
18892
+ {
18893
+ variant: "h2",
18894
+ className: cn(
18895
+ "text-4xl font-black tracking-widest uppercase",
18896
+ result === "victory" ? "text-warning" : "text-error"
18897
+ ),
18898
+ children: result === "victory" ? t("battle.victory") : t("battle.defeat")
18899
+ }
18900
+ ),
18901
+ /* @__PURE__ */ jsxRuntime.jsx(
18902
+ exports.Button,
18903
+ {
18904
+ variant: "primary",
18905
+ className: "px-8 py-3 font-semibold",
18906
+ onClick: handlePlayAgain,
18907
+ children: t("battle.playAgain")
18908
+ }
18909
+ )
18910
+ ] }) })
18840
18911
  ] });
18841
18912
  }
18842
18913
  var init_CastleBoard = __esm({
@@ -18844,6 +18915,10 @@ var init_CastleBoard = __esm({
18844
18915
  "use client";
18845
18916
  init_cn();
18846
18917
  init_useEventBus();
18918
+ init_Box();
18919
+ init_Button();
18920
+ init_Typography();
18921
+ init_Stack();
18847
18922
  init_IsometricCanvas();
18848
18923
  init_boardEntity();
18849
18924
  init_isometric();
@@ -19766,22 +19841,16 @@ function ClassifierBoard({
19766
19841
  const { emit } = useEventBus();
19767
19842
  const { t } = hooks.useTranslate();
19768
19843
  const resolved = boardEntity(entity);
19769
- const [localAssignments, setLocalAssignments] = React77.useState({});
19770
19844
  const [headerError, setHeaderError] = React77.useState(false);
19771
- const [localSubmitted, setLocalSubmitted] = React77.useState(false);
19772
- const [localAttempts, setLocalAttempts] = React77.useState(0);
19773
- const [showHint, setShowHint] = React77.useState(false);
19774
19845
  const items = Array.isArray(resolved?.items) ? resolved.items : [];
19775
19846
  const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
19776
- const entityResult = str(resolved?.result);
19777
- const entityDrivesResult = entityResult.length > 0;
19778
- const entityHasAssignments = items.some((item) => item.assignedCategory != null);
19779
- const assignments = entityHasAssignments ? items.reduce((acc, item) => {
19780
- if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
19847
+ const result = str(resolved?.result);
19848
+ const submitted = result === "win";
19849
+ const attempts = num(resolved?.attempts);
19850
+ const assignments = items.reduce((acc, item) => {
19851
+ if (item.assignedCategory != null && item.assignedCategory !== "") acc[item.id] = item.assignedCategory;
19781
19852
  return acc;
19782
- }, {}) : localAssignments;
19783
- const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
19784
- const submitted = entityDrivesResult || localSubmitted;
19853
+ }, {});
19785
19854
  const unassignedItems = items.filter((item) => !assignments[item.id]);
19786
19855
  const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
19787
19856
  const results = submitted ? items.map((item) => ({
@@ -19789,54 +19858,25 @@ function ClassifierBoard({
19789
19858
  assigned: assignments[item.id],
19790
19859
  correct: assignments[item.id] === item.correctCategory
19791
19860
  })) : [];
19792
- const allCorrect = entityDrivesResult ? entityResult === "success" : results.length > 0 && results.every((r) => r.correct);
19861
+ const allCorrect = result === "win";
19793
19862
  const correctCount = results.filter((r) => r.correct).length;
19863
+ const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
19794
19864
  const handleAssign = (itemId, categoryId) => {
19795
19865
  if (submitted) return;
19796
- if (assignEvent) {
19797
- emit(`UI:${assignEvent}`, { itemId, categoryId });
19798
- }
19799
- if (!entityHasAssignments) {
19800
- setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
19801
- }
19866
+ if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId });
19802
19867
  };
19803
19868
  const handleUnassign = (itemId) => {
19804
19869
  if (submitted) return;
19805
- if (!entityHasAssignments) {
19806
- setLocalAssignments((prev) => {
19807
- const next = { ...prev };
19808
- delete next[itemId];
19809
- return next;
19810
- });
19811
- }
19870
+ if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId: "" });
19812
19871
  };
19813
- const handleSubmit = React77.useCallback(() => {
19814
- if (checkEvent) {
19815
- emit(`UI:${checkEvent}`, {});
19816
- }
19817
- if (!entityDrivesResult) {
19818
- setLocalSubmitted(true);
19819
- setLocalAttempts((a) => a + 1);
19820
- const correct = items.every((item) => assignments[item.id] === item.correctCategory);
19821
- if (correct) {
19822
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
19823
- }
19824
- }
19825
- }, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
19826
- const handleReset = () => {
19827
- if (!entityDrivesResult) setLocalSubmitted(false);
19828
- if (attempts >= 2 && str(resolved?.hint)) {
19829
- setShowHint(true);
19872
+ const handleSubmit = () => {
19873
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
19874
+ if (allAssigned && items.every((item) => assignments[item.id] === item.correctCategory)) {
19875
+ emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
19830
19876
  }
19831
19877
  };
19832
19878
  const handleFullReset = () => {
19833
- if (playAgainEvent) {
19834
- emit(`UI:${playAgainEvent}`, {});
19835
- }
19836
- setLocalAssignments({});
19837
- setLocalSubmitted(false);
19838
- setLocalAttempts(0);
19839
- setShowHint(false);
19879
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
19840
19880
  };
19841
19881
  if (!resolved) return null;
19842
19882
  const theme = resolved.theme ?? void 0;
@@ -19875,17 +19915,17 @@ function ClassifierBoard({
19875
19915
  /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { size: "sm", children: catItems.length })
19876
19916
  ] }),
19877
19917
  /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "xs", className: "flex-wrap min-h-[2rem]", children: catItems.map((item) => {
19878
- const result = results.find((r) => r.item.id === item.id);
19918
+ const result2 = results.find((r) => r.item.id === item.id);
19879
19919
  return /* @__PURE__ */ jsxRuntime.jsxs(
19880
19920
  exports.Badge,
19881
19921
  {
19882
19922
  size: "sm",
19883
- className: `cursor-pointer ${result ? result.correct ? "border-success bg-success/10" : "border-error bg-error/10" : ""}`,
19923
+ className: `cursor-pointer ${result2 ? result2.correct ? "border-success bg-success/10" : "border-error bg-error/10" : ""}`,
19884
19924
  onClick: () => handleUnassign(item.id),
19885
19925
  children: [
19886
19926
  item.iconUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: item.iconUrl, alt: "", className: "w-3 h-3 object-contain inline-block" }),
19887
19927
  item.label,
19888
- result && /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: result.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "xs", className: result.correct ? "text-success" : "text-error" })
19928
+ result2 && /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: result2.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "xs", className: result2.correct ? "text-success" : "text-error" })
19889
19929
  ]
19890
19930
  },
19891
19931
  item.id
@@ -19914,10 +19954,10 @@ function ClassifierBoard({
19914
19954
  ] }) }),
19915
19955
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", children: hint }) }),
19916
19956
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", justify: "center", children: [
19917
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
19957
+ !submitted && /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
19918
19958
  /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.Send, size: "sm" }),
19919
19959
  t("classifier.check")
19920
- ] }) : !allCorrect ? /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", onClick: handleReset, children: t("classifier.tryAgain") }) : null,
19960
+ ] }),
19921
19961
  /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "secondary", onClick: handleFullReset, children: [
19922
19962
  /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
19923
19963
  t("classifier.reset")
@@ -20204,6 +20244,7 @@ function getComboScale(combo) {
20204
20244
  return "";
20205
20245
  }
20206
20246
  function ComboCounter({
20247
+ assetUrl = DEFAULT_ASSET_URL3,
20207
20248
  combo = 5,
20208
20249
  multiplier,
20209
20250
  streak,
@@ -20223,6 +20264,17 @@ function ComboCounter({
20223
20264
  className
20224
20265
  ),
20225
20266
  children: [
20267
+ assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
20268
+ "img",
20269
+ {
20270
+ src: assetUrl,
20271
+ alt: "combo",
20272
+ width: 24,
20273
+ height: 24,
20274
+ style: { imageRendering: "pixelated", objectFit: "contain" },
20275
+ className: "flex-shrink-0 mb-0.5"
20276
+ }
20277
+ ),
20226
20278
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-black tabular-nums leading-none", sizes.combo, getComboIntensity(combo)), children: combo }),
20227
20279
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-bold uppercase tracking-wider text-muted-foreground", sizes.label), children: "combo" }),
20228
20280
  multiplier != null && multiplier > 1 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("font-bold text-warning tabular-nums", sizes.multiplier), children: [
@@ -20237,10 +20289,11 @@ function ComboCounter({
20237
20289
  }
20238
20290
  );
20239
20291
  }
20240
- var sizeMap4;
20292
+ var DEFAULT_ASSET_URL3, sizeMap4;
20241
20293
  var init_ComboCounter = __esm({
20242
20294
  "components/game/atoms/ComboCounter.tsx"() {
20243
20295
  init_cn();
20296
+ DEFAULT_ASSET_URL3 = "https://almadar-kflow-assets.web.app/shared/effects/flash/flash00.png";
20244
20297
  sizeMap4 = {
20245
20298
  sm: { combo: "text-lg", label: "text-xs", multiplier: "text-xs" },
20246
20299
  md: { combo: "text-2xl", label: "text-xs", multiplier: "text-sm" },
@@ -20649,7 +20702,7 @@ var init_CounterTemplate = __esm({
20649
20702
  }
20650
20703
  });
20651
20704
  function ItemSlot({
20652
- assetUrl = DEFAULT_ASSET_URL,
20705
+ assetUrl = DEFAULT_ASSET_URL4,
20653
20706
  icon = "sword",
20654
20707
  label = "Iron Sword",
20655
20708
  quantity,
@@ -20708,7 +20761,7 @@ function ItemSlot({
20708
20761
  }
20709
20762
  );
20710
20763
  }
20711
- var sizeMap5, rarityBorderMap, rarityGlowMap, DEFAULT_ASSET_URL, assetSizeMap;
20764
+ var sizeMap5, rarityBorderMap, rarityGlowMap, DEFAULT_ASSET_URL4, assetSizeMap;
20712
20765
  var init_ItemSlot = __esm({
20713
20766
  "components/game/atoms/ItemSlot.tsx"() {
20714
20767
  "use client";
@@ -20733,7 +20786,7 @@ var init_ItemSlot = __esm({
20733
20786
  epic: "shadow-lg",
20734
20787
  legendary: "shadow-lg"
20735
20788
  };
20736
- DEFAULT_ASSET_URL = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
20789
+ DEFAULT_ASSET_URL4 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
20737
20790
  assetSizeMap = {
20738
20791
  sm: 28,
20739
20792
  md: 40,
@@ -20912,6 +20965,7 @@ var init_DPad = __esm({
20912
20965
  }
20913
20966
  });
20914
20967
  function DamageNumber({
20968
+ assetUrl = DEFAULT_ASSET_URL5,
20915
20969
  value = 42,
20916
20970
  type = "damage",
20917
20971
  size = "md",
@@ -20920,22 +20974,35 @@ function DamageNumber({
20920
20974
  const displayText = type === "miss" ? "MISS" : type === "heal" ? `+${value}` : `${value}`;
20921
20975
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
20922
20976
  /* @__PURE__ */ jsxRuntime.jsx("style", { children: floatKeyframes }),
20923
- /* @__PURE__ */ jsxRuntime.jsx(
20977
+ /* @__PURE__ */ jsxRuntime.jsxs(
20924
20978
  "span",
20925
20979
  {
20926
20980
  className: cn(
20927
- "inline-block font-mono select-none pointer-events-none",
20981
+ "inline-flex items-center gap-0.5 font-mono select-none pointer-events-none",
20928
20982
  sizeMap7[size],
20929
20983
  typeStyles[type],
20930
20984
  className
20931
20985
  ),
20932
20986
  style: { animation: "damageFloat 1s ease-out forwards" },
20933
- children: displayText
20987
+ children: [
20988
+ assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
20989
+ "img",
20990
+ {
20991
+ src: assetUrl,
20992
+ alt: "",
20993
+ width: 14,
20994
+ height: 14,
20995
+ style: { imageRendering: "pixelated", objectFit: "contain" },
20996
+ className: "flex-shrink-0"
20997
+ }
20998
+ ),
20999
+ displayText
21000
+ ]
20934
21001
  }
20935
21002
  )
20936
21003
  ] });
20937
21004
  }
20938
- var sizeMap7, typeStyles, floatKeyframes;
21005
+ var sizeMap7, typeStyles, floatKeyframes, DEFAULT_ASSET_URL5;
20939
21006
  var init_DamageNumber = __esm({
20940
21007
  "components/game/atoms/DamageNumber.tsx"() {
20941
21008
  init_cn();
@@ -20957,6 +21024,7 @@ var init_DamageNumber = __esm({
20957
21024
  100% { opacity: 0; transform: translateY(-32px) scale(0.8); }
20958
21025
  }
20959
21026
  `;
21027
+ DEFAULT_ASSET_URL5 = "https://almadar-kflow-assets.web.app/shared/effects/particles/spark_01.png";
20960
21028
  DamageNumber.displayName = "DamageNumber";
20961
21029
  }
20962
21030
  });
@@ -24777,7 +24845,7 @@ var init_Flex = __esm({
24777
24845
  basis,
24778
24846
  className,
24779
24847
  children,
24780
- as: Component = "div"
24848
+ as: Component2 = "div"
24781
24849
  }) => {
24782
24850
  const flexStyle = {};
24783
24851
  if (grow !== void 0 || shrink !== void 0 || basis !== void 0) {
@@ -24789,7 +24857,7 @@ var init_Flex = __esm({
24789
24857
  flexStyle.flexBasis = typeof basis === "number" ? `${basis}px` : basis;
24790
24858
  }
24791
24859
  }
24792
- const Comp = Component;
24860
+ const Comp = Component2;
24793
24861
  return /* @__PURE__ */ jsxRuntime.jsx(
24794
24862
  Comp,
24795
24863
  {
@@ -24909,11 +24977,11 @@ var init_Grid = __esm({
24909
24977
  className,
24910
24978
  style,
24911
24979
  children,
24912
- as: Component = "div"
24980
+ as: Component2 = "div"
24913
24981
  }) => {
24914
24982
  const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
24915
24983
  return React77__namespace.default.createElement(
24916
- Component,
24984
+ Component2,
24917
24985
  {
24918
24986
  className: cn(
24919
24987
  "grid",
@@ -27062,6 +27130,7 @@ var init_HealthBar = __esm({
27062
27130
  }
27063
27131
  });
27064
27132
  function ScoreDisplay({
27133
+ assetUrl = DEFAULT_ASSET_URL6,
27065
27134
  value,
27066
27135
  label,
27067
27136
  icon,
@@ -27108,14 +27177,24 @@ function ScoreDisplay({
27108
27177
  className
27109
27178
  ),
27110
27179
  children: [
27111
- icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon }) }),
27180
+ assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
27181
+ "img",
27182
+ {
27183
+ src: assetUrl,
27184
+ alt: "",
27185
+ width: 20,
27186
+ height: 20,
27187
+ style: { imageRendering: "pixelated", objectFit: "contain" },
27188
+ className: "flex-shrink-0"
27189
+ }
27190
+ ) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon }) }) : null,
27112
27191
  label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: label }),
27113
27192
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: formattedValue })
27114
27193
  ]
27115
27194
  }
27116
27195
  );
27117
27196
  }
27118
- var sizeMap10;
27197
+ var sizeMap10, DEFAULT_ASSET_URL6;
27119
27198
  var init_ScoreDisplay = __esm({
27120
27199
  "components/game/atoms/ScoreDisplay.tsx"() {
27121
27200
  "use client";
@@ -27127,10 +27206,12 @@ var init_ScoreDisplay = __esm({
27127
27206
  lg: "text-2xl",
27128
27207
  xl: "text-4xl"
27129
27208
  };
27209
+ DEFAULT_ASSET_URL6 = "https://almadar-kflow-assets.web.app/shared/effects/particles/star_01.png";
27130
27210
  ScoreDisplay.displayName = "ScoreDisplay";
27131
27211
  }
27132
27212
  });
27133
27213
  function StatBadge({
27214
+ assetUrl = DEFAULT_ASSET_URL7,
27134
27215
  label,
27135
27216
  value = 0,
27136
27217
  max,
@@ -27154,7 +27235,17 @@ function StatBadge({
27154
27235
  className
27155
27236
  ),
27156
27237
  children: [
27157
- icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0 text-lg", children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon, className: "w-4 h-4" }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon, className: "w-4 h-4" }) }),
27238
+ assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
27239
+ "img",
27240
+ {
27241
+ src: assetUrl,
27242
+ alt: "",
27243
+ width: 16,
27244
+ height: 16,
27245
+ style: { imageRendering: "pixelated", objectFit: "contain" },
27246
+ className: "flex-shrink-0"
27247
+ }
27248
+ ) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0 text-lg", children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon, className: "w-4 h-4" }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon, className: "w-4 h-4" }) }) : null,
27158
27249
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground font-medium", children: label }),
27159
27250
  format === "hearts" && max && /* @__PURE__ */ jsxRuntime.jsx(
27160
27251
  HealthBar,
@@ -27187,7 +27278,7 @@ function StatBadge({
27187
27278
  }
27188
27279
  );
27189
27280
  }
27190
- var sizeMap11, variantMap2;
27281
+ var sizeMap11, variantMap2, DEFAULT_ASSET_URL7;
27191
27282
  var init_StatBadge = __esm({
27192
27283
  "components/game/molecules/StatBadge.tsx"() {
27193
27284
  init_cn();
@@ -27206,6 +27297,7 @@ var init_StatBadge = __esm({
27206
27297
  warning: "bg-warning/15 border-warning/40 text-foreground",
27207
27298
  danger: "bg-error/15 border-error/40 text-foreground"
27208
27299
  };
27300
+ DEFAULT_ASSET_URL7 = "https://almadar-kflow-assets.web.app/shared/effects/particles/light_01.png";
27209
27301
  StatBadge.displayName = "StatBadge";
27210
27302
  }
27211
27303
  });
@@ -27289,6 +27381,7 @@ var init_InventoryGrid = __esm({
27289
27381
  }
27290
27382
  });
27291
27383
  function WaypointMarker({
27384
+ assetUrl = DEFAULT_ASSET_URL8,
27292
27385
  label,
27293
27386
  icon,
27294
27387
  active = true,
@@ -27327,7 +27420,17 @@ function WaypointMarker({
27327
27420
  active && !completed && "bg-info text-foreground",
27328
27421
  !active && !completed && "bg-muted"
27329
27422
  ),
27330
- children: completed ? checkIcon : icon && (typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon }))
27423
+ children: completed ? checkIcon : assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
27424
+ "img",
27425
+ {
27426
+ src: assetUrl,
27427
+ alt: label,
27428
+ width: sizes.img,
27429
+ height: sizes.img,
27430
+ style: { imageRendering: "pixelated", objectFit: "contain" },
27431
+ className: "flex-shrink-0"
27432
+ }
27433
+ ) : icon ? typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon }) : null
27331
27434
  }
27332
27435
  )
27333
27436
  ] }),
@@ -27344,15 +27447,16 @@ function WaypointMarker({
27344
27447
  )
27345
27448
  ] });
27346
27449
  }
27347
- var sizeMap12, checkIcon;
27450
+ var DEFAULT_ASSET_URL8, sizeMap12, checkIcon;
27348
27451
  var init_WaypointMarker = __esm({
27349
27452
  "components/game/atoms/WaypointMarker.tsx"() {
27350
27453
  init_cn();
27351
27454
  init_Icon();
27455
+ DEFAULT_ASSET_URL8 = "https://almadar-kflow-assets.web.app/shared/world-map/battle_marker.png";
27352
27456
  sizeMap12 = {
27353
- sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1" },
27354
- md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5" },
27355
- lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2" }
27457
+ sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1", img: 12 },
27458
+ md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5", img: 18 },
27459
+ lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2", img: 24 }
27356
27460
  };
27357
27461
  checkIcon = /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 3, className: "w-3/5 h-3/5", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 13l4 4L19 7", strokeLinecap: "round", strokeLinejoin: "round" }) });
27358
27462
  WaypointMarker.displayName = "WaypointMarker";
@@ -27866,6 +27970,7 @@ var init_ResourceBar = __esm({
27866
27970
  }
27867
27971
  });
27868
27972
  function TurnIndicator({
27973
+ assetUrl = DEFAULT_ASSET_URL9,
27869
27974
  currentTurn = 1,
27870
27975
  maxTurns,
27871
27976
  activeTeam,
@@ -27898,14 +28003,24 @@ function TurnIndicator({
27898
28003
  ] }),
27899
28004
  activeTeam && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
27900
28005
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "|" }),
27901
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("rounded-full bg-success/20", sizes.dot) }),
28006
+ assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
28007
+ "img",
28008
+ {
28009
+ src: assetUrl,
28010
+ alt: "",
28011
+ width: 12,
28012
+ height: 12,
28013
+ style: { imageRendering: "pixelated", objectFit: "contain" },
28014
+ className: "flex-shrink-0"
28015
+ }
28016
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("rounded-full bg-success/20", sizes.dot) }),
27902
28017
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-success", children: activeTeam })
27903
28018
  ] })
27904
28019
  ]
27905
28020
  }
27906
28021
  );
27907
28022
  }
27908
- var sizeMap14;
28023
+ var sizeMap14, DEFAULT_ASSET_URL9;
27909
28024
  var init_TurnIndicator = __esm({
27910
28025
  "components/game/atoms/TurnIndicator.tsx"() {
27911
28026
  init_cn();
@@ -27914,6 +28029,7 @@ var init_TurnIndicator = __esm({
27914
28029
  md: { wrapper: "text-sm gap-2 px-3 py-1", dot: "w-2 h-2" },
27915
28030
  lg: { wrapper: "text-base gap-2.5 px-4 py-1.5", dot: "w-2.5 h-2.5" }
27916
28031
  };
28032
+ DEFAULT_ASSET_URL9 = "https://almadar-kflow-assets.web.app/shared/effects/particles/symbol_01.png";
27917
28033
  TurnIndicator.displayName = "TurnIndicator";
27918
28034
  }
27919
28035
  });
@@ -27984,6 +28100,7 @@ var init_TurnPanel = __esm({
27984
28100
  }
27985
28101
  });
27986
28102
  function EnemyPlate({
28103
+ assetUrl = DEFAULT_ASSET_URL10,
27987
28104
  name = "Shadow Guard",
27988
28105
  health = 80,
27989
28106
  maxHealth = 100,
@@ -28001,15 +28118,28 @@ function EnemyPlate({
28001
28118
  ),
28002
28119
  children: [
28003
28120
  /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "flex items-center justify-between gap-2", children: [
28004
- /* @__PURE__ */ jsxRuntime.jsx(
28005
- exports.Typography,
28006
- {
28007
- variant: "caption",
28008
- weight: "bold",
28009
- className: "text-[var(--color-foreground)] truncate",
28010
- children: name
28011
- }
28012
- ),
28121
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "flex items-center gap-1.5 min-w-0", children: [
28122
+ assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
28123
+ "img",
28124
+ {
28125
+ src: assetUrl,
28126
+ alt: name,
28127
+ width: 24,
28128
+ height: 24,
28129
+ style: { objectFit: "cover", borderRadius: "50%" },
28130
+ className: "flex-shrink-0"
28131
+ }
28132
+ ),
28133
+ /* @__PURE__ */ jsxRuntime.jsx(
28134
+ exports.Typography,
28135
+ {
28136
+ variant: "caption",
28137
+ weight: "bold",
28138
+ className: "text-[var(--color-foreground)] truncate",
28139
+ children: name
28140
+ }
28141
+ )
28142
+ ] }),
28013
28143
  level != null && /* @__PURE__ */ jsxRuntime.jsxs(exports.Badge, { variant: "neutral", size: "sm", children: [
28014
28144
  "Lv.",
28015
28145
  level
@@ -28054,7 +28184,7 @@ function EnemyPlate({
28054
28184
  }
28055
28185
  );
28056
28186
  }
28057
- var effectVariantMap2, DEFAULT_ENEMY_EFFECTS;
28187
+ var effectVariantMap2, DEFAULT_ENEMY_EFFECTS, DEFAULT_ASSET_URL10;
28058
28188
  var init_EnemyPlate = __esm({
28059
28189
  "components/game/molecules/EnemyPlate.tsx"() {
28060
28190
  "use client";
@@ -28071,6 +28201,7 @@ var init_EnemyPlate = __esm({
28071
28201
  DEFAULT_ENEMY_EFFECTS = [
28072
28202
  { icon: "flame", label: "Burn", variant: "debuff" }
28073
28203
  ];
28204
+ DEFAULT_ASSET_URL10 = "https://almadar-kflow-assets.web.app/shared/portraits/shadow-legion.png";
28074
28205
  EnemyPlate.displayName = "EnemyPlate";
28075
28206
  }
28076
28207
  });
@@ -29624,13 +29755,13 @@ var init_MapView = __esm({
29624
29755
  shadowSize: [41, 41]
29625
29756
  });
29626
29757
  L.Marker.prototype.options.icon = defaultIcon;
29627
- const { useEffect: useEffect74, useRef: useRef71, useCallback: useCallback114, useState: useState105 } = React77__namespace.default;
29758
+ const { useEffect: useEffect80, useRef: useRef77, useCallback: useCallback120, useState: useState110 } = React77__namespace.default;
29628
29759
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
29629
- const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
29760
+ const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
29630
29761
  function MapUpdater({ centerLat, centerLng, zoom }) {
29631
29762
  const map = useMap();
29632
- const prevRef = useRef71({ centerLat, centerLng, zoom });
29633
- useEffect74(() => {
29763
+ const prevRef = useRef77({ centerLat, centerLng, zoom });
29764
+ useEffect80(() => {
29634
29765
  const prev = prevRef.current;
29635
29766
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
29636
29767
  map.setView([centerLat, centerLng], zoom);
@@ -29641,7 +29772,7 @@ var init_MapView = __esm({
29641
29772
  }
29642
29773
  function MapClickHandler({ onMapClick }) {
29643
29774
  const map = useMap();
29644
- useEffect74(() => {
29775
+ useEffect80(() => {
29645
29776
  if (!onMapClick) return;
29646
29777
  const handler = (e) => {
29647
29778
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -29668,9 +29799,9 @@ var init_MapView = __esm({
29668
29799
  className,
29669
29800
  showAttribution = true
29670
29801
  }) {
29671
- const eventBus = useEventBus2();
29672
- const [clickedPosition, setClickedPosition] = useState105(null);
29673
- const handleMapClick = useCallback114((lat, lng) => {
29802
+ const eventBus = useEventBus3();
29803
+ const [clickedPosition, setClickedPosition] = useState110(null);
29804
+ const handleMapClick = useCallback120((lat, lng) => {
29674
29805
  if (showClickedPin) {
29675
29806
  setClickedPosition({ lat, lng });
29676
29807
  }
@@ -29679,7 +29810,7 @@ var init_MapView = __esm({
29679
29810
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
29680
29811
  }
29681
29812
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
29682
- const handleMarkerClick = useCallback114((marker) => {
29813
+ const handleMarkerClick = useCallback120((marker) => {
29683
29814
  onMarkerClick?.(marker);
29684
29815
  if (markerClickEvent) {
29685
29816
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -36806,10 +36937,10 @@ var init_Section = __esm({
36806
36937
  children,
36807
36938
  headerClassName,
36808
36939
  contentClassName,
36809
- as: Component = "section"
36940
+ as: Component2 = "section"
36810
36941
  }) => {
36811
36942
  const hasHeader = title || description || action;
36812
- const Comp = Component;
36943
+ const Comp = Component2;
36813
36944
  return /* @__PURE__ */ jsxRuntime.jsxs(
36814
36945
  Comp,
36815
36946
  {
@@ -39122,44 +39253,27 @@ function DebuggerBoard({
39122
39253
  const { t } = hooks.useTranslate();
39123
39254
  const resolved = boardEntity(entity);
39124
39255
  const [headerError, setHeaderError] = React77.useState(false);
39125
- const [showHint, setShowHint] = React77.useState(false);
39126
39256
  const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
39127
- const result = resolved?.result ?? null;
39257
+ const result = str(resolved?.result) || "none";
39128
39258
  const attempts = num(resolved?.attempts);
39129
- const submitted = result != null;
39259
+ const submitted = result === "win";
39260
+ const showHint = !submitted && attempts >= 2 && Boolean(str(resolved?.hint));
39130
39261
  const bugLines = lines.filter((l) => l.isBug);
39131
39262
  const flaggedLines = lines.filter((l) => l.isFlagged);
39132
39263
  const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
39133
39264
  const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
39134
- const allCorrect = result === "win";
39265
+ const allCorrect = submitted;
39135
39266
  const toggleLine = (lineId) => {
39136
39267
  if (submitted) return;
39137
- if (toggleFlagEvent) {
39138
- emit(`UI:${toggleFlagEvent}`, { lineId });
39139
- }
39268
+ if (toggleFlagEvent) emit(`UI:${toggleFlagEvent}`, { lineId });
39140
39269
  };
39141
39270
  const handleSubmit = React77.useCallback(() => {
39142
- if (checkEvent) {
39143
- emit(`UI:${checkEvent}`, {});
39144
- }
39271
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
39145
39272
  const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
39146
- if (correct) {
39147
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
39148
- }
39273
+ if (correct) emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
39149
39274
  }, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
39150
39275
  const handleReset = () => {
39151
- if (playAgainEvent) {
39152
- emit(`UI:${playAgainEvent}`, {});
39153
- }
39154
- if (attempts >= 2 && str(resolved?.hint)) {
39155
- setShowHint(true);
39156
- }
39157
- };
39158
- const handleFullReset = () => {
39159
- if (playAgainEvent) {
39160
- emit(`UI:${playAgainEvent}`, {});
39161
- }
39162
- setShowHint(false);
39276
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
39163
39277
  };
39164
39278
  if (!resolved) return null;
39165
39279
  const theme = resolved.theme ?? void 0;
@@ -39183,7 +39297,7 @@ function DebuggerBoard({
39183
39297
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
39184
39298
  ] }),
39185
39299
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", children: str(resolved.description) }),
39186
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(num(resolved.bugCount)) }) })
39300
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(lines.filter((l) => l.isBug).length) }) })
39187
39301
  ] }) }),
39188
39302
  /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(exports.VStack, { gap: "none", children: lines.map((line, i) => {
39189
39303
  const isFlagged = !!line.isFlagged;
@@ -39233,11 +39347,11 @@ function DebuggerBoard({
39233
39347
  ] }) }),
39234
39348
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", children: hint }) }),
39235
39349
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", justify: "center", children: [
39236
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
39350
+ !submitted && /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
39237
39351
  /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.Send, size: "sm" }),
39238
39352
  t("debugger.submit")
39239
- ] }) : !allCorrect ? /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", onClick: handleReset, children: t("debugger.tryAgain") }) : null,
39240
- /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "secondary", onClick: handleFullReset, children: [
39353
+ ] }),
39354
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "secondary", onClick: handleReset, children: [
39241
39355
  /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
39242
39356
  t("debugger.reset")
39243
39357
  ] })
@@ -39827,6 +39941,7 @@ var init_DrawerSlot = __esm({
39827
39941
  }
39828
39942
  });
39829
39943
  function StateIndicator({
39944
+ assetUrl = DEFAULT_ASSET_URL11,
39830
39945
  state = "idle",
39831
39946
  label,
39832
39947
  size = "md",
@@ -39849,18 +39964,29 @@ function StateIndicator({
39849
39964
  className
39850
39965
  ),
39851
39966
  children: [
39852
- /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { as: "span", children: typeof config.icon === "string" ? /^[a-zA-Z0-9-]+$/.test(config.icon) ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: config.icon }) : config.icon : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: config.icon }) }),
39967
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { as: "span", children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
39968
+ "img",
39969
+ {
39970
+ src: assetUrl,
39971
+ alt: displayLabel,
39972
+ width: 16,
39973
+ height: 16,
39974
+ style: { imageRendering: "pixelated", objectFit: "contain" },
39975
+ className: "flex-shrink-0"
39976
+ }
39977
+ ) : typeof config.icon === "string" ? /^[a-zA-Z0-9-]+$/.test(config.icon) ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: config.icon }) : config.icon : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: config.icon }) }),
39853
39978
  /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { as: "span", children: displayLabel })
39854
39979
  ]
39855
39980
  }
39856
39981
  );
39857
39982
  }
39858
- var DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
39983
+ var DEFAULT_ASSET_URL11, DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
39859
39984
  var init_StateIndicator = __esm({
39860
39985
  "components/game/atoms/StateIndicator.tsx"() {
39861
39986
  init_Box();
39862
39987
  init_Icon();
39863
39988
  init_cn();
39989
+ DEFAULT_ASSET_URL11 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
39864
39990
  DEFAULT_STATE_STYLES = {
39865
39991
  idle: { icon: "\u23F8", bgClass: "bg-muted" },
39866
39992
  active: { icon: "\u25B6", bgClass: "bg-success" },
@@ -40334,39 +40460,40 @@ function EventHandlerBoard({
40334
40460
  stepDurationMs = 800,
40335
40461
  playEvent,
40336
40462
  completeEvent,
40463
+ editRuleEvent,
40464
+ playAgainEvent,
40337
40465
  className
40338
40466
  }) {
40339
40467
  const { emit } = useEventBus();
40340
40468
  const { t } = hooks.useTranslate();
40341
40469
  const resolved = boardEntity(entity);
40342
- const entityObjects = rows(resolved?.objects);
40343
- const [objects, setObjects] = React77.useState(() => [...entityObjects]);
40470
+ const objects = rows(resolved?.objects);
40471
+ const entityResult = str(resolved?.result) || "none";
40472
+ const isSuccess = entityResult === "win";
40473
+ const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
40344
40474
  const [selectedObjectId, setSelectedObjectId] = React77.useState(
40345
- entityObjects[0] ? objId(entityObjects[0]) : null
40475
+ objects[0] ? objId(objects[0]) : null
40346
40476
  );
40347
40477
  const [headerError, setHeaderError] = React77.useState(false);
40348
- const [playState, setPlayState] = React77.useState("editing");
40478
+ const [isPlaying, setIsPlaying] = React77.useState(false);
40349
40479
  const [eventLog, setEventLog] = React77.useState([]);
40350
- const [attempts, setAttempts] = React77.useState(0);
40351
40480
  const timerRef = React77.useRef(null);
40352
40481
  const logIdCounter = React77.useRef(0);
40353
40482
  React77.useEffect(() => () => {
40354
40483
  if (timerRef.current) clearTimeout(timerRef.current);
40355
40484
  }, []);
40356
- const selectedObject = objects.find((o) => objId(o) === selectedObjectId) || null;
40485
+ const selectedObject = objects.find((o) => objId(o) === selectedObjectId) ?? null;
40357
40486
  const handleRulesChange = React77.useCallback((objectId, rules) => {
40358
- setObjects((prev) => prev.map(
40359
- (o) => objId(o) === objectId ? { ...o, rules } : o
40360
- ));
40361
- }, []);
40487
+ if (editRuleEvent) emit(`UI:${editRuleEvent}`, { objectId, rules });
40488
+ }, [editRuleEvent, emit]);
40362
40489
  const addLogEntry = React77.useCallback((icon, message, status = "done") => {
40363
40490
  const id = `log-${logIdCounter.current++}`;
40364
40491
  setEventLog((prev) => [...prev, { id, timestamp: Date.now(), icon, message, status }]);
40365
40492
  }, []);
40366
40493
  const handlePlay = React77.useCallback(() => {
40367
- if (playState !== "editing") return;
40494
+ if (isPlaying || isSuccess) return;
40368
40495
  if (playEvent) emit(`UI:${playEvent}`, {});
40369
- setPlayState("playing");
40496
+ setIsPlaying(true);
40370
40497
  setEventLog([]);
40371
40498
  const allRules = [];
40372
40499
  objects.forEach((obj) => {
@@ -40382,15 +40509,8 @@ function EventHandlerBoard({
40382
40509
  let goalReached = false;
40383
40510
  const processNext = () => {
40384
40511
  if (eventQueue.length === 0 || stepIdx > 20) {
40385
- if (goalReached) {
40386
- setPlayState("success");
40387
- if (completeEvent) {
40388
- emit(`UI:${completeEvent}`, { success: true });
40389
- }
40390
- } else {
40391
- setAttempts((prev) => prev + 1);
40392
- setPlayState("fail");
40393
- }
40512
+ setIsPlaying(false);
40513
+ if (goalReached && completeEvent) emit(`UI:${completeEvent}`, { success: true });
40394
40514
  return;
40395
40515
  }
40396
40516
  const currentEvent = eventQueue.shift();
@@ -40421,21 +40541,19 @@ function EventHandlerBoard({
40421
40541
  addLogEntry("\u{1F3AC}", t("eventHandler.simulationStarted", { events: triggers.join(", ") }), "active");
40422
40542
  }
40423
40543
  timerRef.current = setTimeout(processNext, stepDurationMs);
40424
- }, [playState, objects, resolved, stepDurationMs, playEvent, completeEvent, emit, addLogEntry, t]);
40544
+ }, [isPlaying, isSuccess, objects, resolved, stepDurationMs, playEvent, completeEvent, emit, addLogEntry, t]);
40425
40545
  const handleTryAgain = React77.useCallback(() => {
40426
40546
  if (timerRef.current) clearTimeout(timerRef.current);
40427
- setPlayState("editing");
40547
+ setIsPlaying(false);
40428
40548
  setEventLog([]);
40429
40549
  }, []);
40430
40550
  const handleReset = React77.useCallback(() => {
40431
40551
  if (timerRef.current) clearTimeout(timerRef.current);
40432
- const resetObjects = rows(resolved?.objects);
40433
- setObjects([...resetObjects]);
40434
- setPlayState("editing");
40552
+ setIsPlaying(false);
40435
40553
  setEventLog([]);
40436
- setSelectedObjectId(resetObjects[0] ? objId(resetObjects[0]) : null);
40437
- setAttempts(0);
40438
- }, [resolved?.objects]);
40554
+ setSelectedObjectId(objects[0] ? objId(objects[0]) : null);
40555
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
40556
+ }, [objects, playAgainEvent, emit]);
40439
40557
  if (!resolved) return null;
40440
40558
  const objectViewers = objects.map((obj) => {
40441
40559
  const states = objStates(obj);
@@ -40504,12 +40622,12 @@ function EventHandlerBoard({
40504
40622
  {
40505
40623
  object: selectedObject,
40506
40624
  onRulesChange: handleRulesChange,
40507
- disabled: playState !== "editing"
40625
+ disabled: isPlaying
40508
40626
  }
40509
40627
  ),
40510
40628
  eventLog.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(EventLog, { entries: eventLog }),
40511
- playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("eventHandler.chainComplete") }) }),
40512
- playState === "fail" && /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", children: [
40629
+ isSuccess && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("eventHandler.chainComplete") }) }),
40630
+ !isPlaying && !isSuccess && attempts > 0 && /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", children: [
40513
40631
  /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body1", className: "text-foreground font-medium", children: t(encourageKey) }) }),
40514
40632
  showHint && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { className: "items-start", gap: "xs", children: [
40515
40633
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
@@ -40517,12 +40635,12 @@ function EventHandlerBoard({
40517
40635
  ] }) })
40518
40636
  ] }),
40519
40637
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", children: [
40520
- playState === "fail" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsxRuntime.jsx(
40638
+ !isPlaying && !isSuccess && attempts > 0 ? /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsxRuntime.jsx(
40521
40639
  exports.Button,
40522
40640
  {
40523
40641
  variant: "primary",
40524
40642
  onClick: handlePlay,
40525
- disabled: playState !== "editing",
40643
+ disabled: isPlaying || isSuccess,
40526
40644
  children: "\u25B6 " + t("game.play")
40527
40645
  }
40528
40646
  ),
@@ -41714,6 +41832,1568 @@ var init_GameAudioToggle = __esm({
41714
41832
  GameAudioToggle.displayName = "GameAudioToggle";
41715
41833
  }
41716
41834
  });
41835
+ function detectAssetRoot(modelUrl) {
41836
+ const idx = modelUrl.indexOf("/3d/");
41837
+ if (idx !== -1) {
41838
+ return modelUrl.substring(0, idx + 4);
41839
+ }
41840
+ return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
41841
+ }
41842
+ function createGLTFLoaderForUrl(url) {
41843
+ const loader = new GLTFLoader_js.GLTFLoader();
41844
+ loader.setResourcePath(detectAssetRoot(url));
41845
+ return loader;
41846
+ }
41847
+ var AssetLoader;
41848
+ var init_AssetLoader = __esm({
41849
+ "components/game/molecules/three/loaders/AssetLoader.ts"() {
41850
+ AssetLoader = class {
41851
+ constructor() {
41852
+ __publicField(this, "objLoader");
41853
+ __publicField(this, "textureLoader");
41854
+ __publicField(this, "modelCache");
41855
+ __publicField(this, "textureCache");
41856
+ __publicField(this, "loadingPromises");
41857
+ this.objLoader = new OBJLoader_js.OBJLoader();
41858
+ this.textureLoader = new THREE__namespace.TextureLoader();
41859
+ this.modelCache = /* @__PURE__ */ new Map();
41860
+ this.textureCache = /* @__PURE__ */ new Map();
41861
+ this.loadingPromises = /* @__PURE__ */ new Map();
41862
+ }
41863
+ /**
41864
+ * Load a GLB/GLTF model
41865
+ * @param url - URL to the .glb or .gltf file
41866
+ * @returns Promise with loaded model scene and animations
41867
+ */
41868
+ async loadModel(url) {
41869
+ if (this.modelCache.has(url)) {
41870
+ return this.modelCache.get(url);
41871
+ }
41872
+ if (this.loadingPromises.has(url)) {
41873
+ return this.loadingPromises.get(url);
41874
+ }
41875
+ const loader = createGLTFLoaderForUrl(url);
41876
+ const loadPromise = loader.loadAsync(url).then((gltf) => {
41877
+ const result = {
41878
+ scene: gltf.scene,
41879
+ animations: gltf.animations || []
41880
+ };
41881
+ this.modelCache.set(url, result);
41882
+ this.loadingPromises.delete(url);
41883
+ return result;
41884
+ }).catch((error) => {
41885
+ this.loadingPromises.delete(url);
41886
+ throw new Error(`Failed to load model ${url}: ${error.message}`);
41887
+ });
41888
+ this.loadingPromises.set(url, loadPromise);
41889
+ return loadPromise;
41890
+ }
41891
+ /**
41892
+ * Load an OBJ model (fallback for non-GLB assets)
41893
+ * @param url - URL to the .obj file
41894
+ * @returns Promise with loaded object group
41895
+ */
41896
+ async loadOBJ(url) {
41897
+ if (this.modelCache.has(url)) {
41898
+ return this.modelCache.get(url).scene;
41899
+ }
41900
+ if (this.loadingPromises.has(url)) {
41901
+ const result = await this.loadingPromises.get(url);
41902
+ return result.scene;
41903
+ }
41904
+ const loadPromise = this.objLoader.loadAsync(url).then((group) => {
41905
+ const result = {
41906
+ scene: group,
41907
+ animations: []
41908
+ };
41909
+ this.modelCache.set(url, result);
41910
+ this.loadingPromises.delete(url);
41911
+ return result;
41912
+ }).catch((error) => {
41913
+ this.loadingPromises.delete(url);
41914
+ throw new Error(`Failed to load OBJ ${url}: ${error.message}`);
41915
+ });
41916
+ this.loadingPromises.set(url, loadPromise);
41917
+ return (await loadPromise).scene;
41918
+ }
41919
+ /**
41920
+ * Load a texture
41921
+ * @param url - URL to the texture image
41922
+ * @returns Promise with loaded texture
41923
+ */
41924
+ async loadTexture(url) {
41925
+ if (this.textureCache.has(url)) {
41926
+ return this.textureCache.get(url);
41927
+ }
41928
+ if (this.loadingPromises.has(`texture:${url}`)) {
41929
+ return this.loadingPromises.get(`texture:${url}`);
41930
+ }
41931
+ const loadPromise = this.textureLoader.loadAsync(url).then((texture) => {
41932
+ texture.colorSpace = THREE__namespace.SRGBColorSpace;
41933
+ this.textureCache.set(url, texture);
41934
+ this.loadingPromises.delete(`texture:${url}`);
41935
+ return texture;
41936
+ }).catch((error) => {
41937
+ this.loadingPromises.delete(`texture:${url}`);
41938
+ throw new Error(`Failed to load texture ${url}: ${error.message}`);
41939
+ });
41940
+ this.loadingPromises.set(`texture:${url}`, loadPromise);
41941
+ return loadPromise;
41942
+ }
41943
+ /**
41944
+ * Preload multiple assets
41945
+ * @param urls - Array of asset URLs to preload
41946
+ * @returns Promise that resolves when all assets are loaded
41947
+ */
41948
+ async preload(urls) {
41949
+ const promises = urls.map((url) => {
41950
+ if (url.endsWith(".glb") || url.endsWith(".gltf")) {
41951
+ return this.loadModel(url).catch(() => null);
41952
+ } else if (url.endsWith(".obj")) {
41953
+ return this.loadOBJ(url).catch(() => null);
41954
+ } else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
41955
+ return this.loadTexture(url).catch(() => null);
41956
+ }
41957
+ return Promise.resolve(null);
41958
+ });
41959
+ await Promise.all(promises);
41960
+ }
41961
+ /**
41962
+ * Check if a model is cached
41963
+ * @param url - Model URL
41964
+ */
41965
+ hasModel(url) {
41966
+ return this.modelCache.has(url);
41967
+ }
41968
+ /**
41969
+ * Check if a texture is cached
41970
+ * @param url - Texture URL
41971
+ */
41972
+ hasTexture(url) {
41973
+ return this.textureCache.has(url);
41974
+ }
41975
+ /**
41976
+ * Get cached model (throws if not cached)
41977
+ * @param url - Model URL
41978
+ */
41979
+ getModel(url) {
41980
+ const model = this.modelCache.get(url);
41981
+ if (!model) {
41982
+ throw new Error(`Model ${url} not in cache`);
41983
+ }
41984
+ return model;
41985
+ }
41986
+ /**
41987
+ * Get cached texture (throws if not cached)
41988
+ * @param url - Texture URL
41989
+ */
41990
+ getTexture(url) {
41991
+ const texture = this.textureCache.get(url);
41992
+ if (!texture) {
41993
+ throw new Error(`Texture ${url} not in cache`);
41994
+ }
41995
+ return texture;
41996
+ }
41997
+ /**
41998
+ * Clear all caches
41999
+ */
42000
+ clearCache() {
42001
+ this.textureCache.forEach((texture) => {
42002
+ texture.dispose();
42003
+ });
42004
+ this.modelCache.forEach((model) => {
42005
+ model.scene.traverse((child) => {
42006
+ if (child instanceof THREE__namespace.Mesh) {
42007
+ child.geometry.dispose();
42008
+ if (Array.isArray(child.material)) {
42009
+ child.material.forEach((m) => m.dispose());
42010
+ } else {
42011
+ child.material.dispose();
42012
+ }
42013
+ }
42014
+ });
42015
+ });
42016
+ this.modelCache.clear();
42017
+ this.textureCache.clear();
42018
+ this.loadingPromises.clear();
42019
+ }
42020
+ /**
42021
+ * Get cache statistics
42022
+ */
42023
+ getStats() {
42024
+ return {
42025
+ models: this.modelCache.size,
42026
+ textures: this.textureCache.size,
42027
+ loading: this.loadingPromises.size
42028
+ };
42029
+ }
42030
+ };
42031
+ new AssetLoader();
42032
+ }
42033
+ });
42034
+ function useAssetLoader(options = {}) {
42035
+ const { preloadUrls = [], loader: customLoader } = options;
42036
+ const loaderRef = React77.useRef(customLoader || new AssetLoader());
42037
+ const [state, setState] = React77.useState({
42038
+ isLoading: false,
42039
+ progress: 0,
42040
+ loaded: 0,
42041
+ total: 0,
42042
+ errors: []
42043
+ });
42044
+ React77.useEffect(() => {
42045
+ if (preloadUrls.length > 0) {
42046
+ preload(preloadUrls);
42047
+ }
42048
+ }, []);
42049
+ const updateProgress = React77.useCallback((loaded, total) => {
42050
+ setState((prev) => ({
42051
+ ...prev,
42052
+ loaded,
42053
+ total,
42054
+ progress: total > 0 ? Math.round(loaded / total * 100) : 0
42055
+ }));
42056
+ }, []);
42057
+ const loadModel = React77.useCallback(
42058
+ async (url) => {
42059
+ setState((prev) => ({ ...prev, isLoading: true }));
42060
+ try {
42061
+ const model = await loaderRef.current.loadModel(url);
42062
+ setState((prev) => ({
42063
+ ...prev,
42064
+ isLoading: false,
42065
+ loaded: prev.loaded + 1
42066
+ }));
42067
+ return model;
42068
+ } catch (error) {
42069
+ const errorMsg = error instanceof Error ? error.message : String(error);
42070
+ setState((prev) => ({
42071
+ ...prev,
42072
+ isLoading: false,
42073
+ errors: [...prev.errors, errorMsg]
42074
+ }));
42075
+ throw error;
42076
+ }
42077
+ },
42078
+ []
42079
+ );
42080
+ const loadOBJ = React77.useCallback(
42081
+ async (url) => {
42082
+ setState((prev) => ({ ...prev, isLoading: true }));
42083
+ try {
42084
+ const model = await loaderRef.current.loadOBJ(url);
42085
+ setState((prev) => ({
42086
+ ...prev,
42087
+ isLoading: false,
42088
+ loaded: prev.loaded + 1
42089
+ }));
42090
+ return model;
42091
+ } catch (error) {
42092
+ const errorMsg = error instanceof Error ? error.message : String(error);
42093
+ setState((prev) => ({
42094
+ ...prev,
42095
+ isLoading: false,
42096
+ errors: [...prev.errors, errorMsg]
42097
+ }));
42098
+ throw error;
42099
+ }
42100
+ },
42101
+ []
42102
+ );
42103
+ const loadTexture = React77.useCallback(
42104
+ async (url) => {
42105
+ setState((prev) => ({ ...prev, isLoading: true }));
42106
+ try {
42107
+ const texture = await loaderRef.current.loadTexture(url);
42108
+ setState((prev) => ({
42109
+ ...prev,
42110
+ isLoading: false,
42111
+ loaded: prev.loaded + 1
42112
+ }));
42113
+ return texture;
42114
+ } catch (error) {
42115
+ const errorMsg = error instanceof Error ? error.message : String(error);
42116
+ setState((prev) => ({
42117
+ ...prev,
42118
+ isLoading: false,
42119
+ errors: [...prev.errors, errorMsg]
42120
+ }));
42121
+ throw error;
42122
+ }
42123
+ },
42124
+ []
42125
+ );
42126
+ const preload = React77.useCallback(
42127
+ async (urls) => {
42128
+ setState((prev) => ({
42129
+ ...prev,
42130
+ isLoading: true,
42131
+ total: urls.length,
42132
+ loaded: 0,
42133
+ errors: []
42134
+ }));
42135
+ let completed = 0;
42136
+ const errors = [];
42137
+ await Promise.all(
42138
+ urls.map(async (url) => {
42139
+ try {
42140
+ if (url.endsWith(".glb") || url.endsWith(".gltf")) {
42141
+ await loaderRef.current.loadModel(url);
42142
+ } else if (url.endsWith(".obj")) {
42143
+ await loaderRef.current.loadOBJ(url);
42144
+ } else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
42145
+ await loaderRef.current.loadTexture(url);
42146
+ }
42147
+ completed++;
42148
+ updateProgress(completed, urls.length);
42149
+ } catch (error) {
42150
+ const errorMsg = error instanceof Error ? error.message : String(error);
42151
+ errors.push(`${url}: ${errorMsg}`);
42152
+ completed++;
42153
+ updateProgress(completed, urls.length);
42154
+ }
42155
+ })
42156
+ );
42157
+ setState((prev) => ({
42158
+ ...prev,
42159
+ isLoading: false,
42160
+ errors
42161
+ }));
42162
+ },
42163
+ [updateProgress]
42164
+ );
42165
+ const hasModel = React77.useCallback((url) => {
42166
+ return loaderRef.current.hasModel(url);
42167
+ }, []);
42168
+ const hasTexture = React77.useCallback((url) => {
42169
+ return loaderRef.current.hasTexture(url);
42170
+ }, []);
42171
+ const getModel = React77.useCallback((url) => {
42172
+ try {
42173
+ return loaderRef.current.getModel(url);
42174
+ } catch {
42175
+ return void 0;
42176
+ }
42177
+ }, []);
42178
+ const getTexture = React77.useCallback((url) => {
42179
+ try {
42180
+ return loaderRef.current.getTexture(url);
42181
+ } catch {
42182
+ return void 0;
42183
+ }
42184
+ }, []);
42185
+ const clearCache = React77.useCallback(() => {
42186
+ loaderRef.current.clearCache();
42187
+ setState({
42188
+ isLoading: false,
42189
+ progress: 0,
42190
+ loaded: 0,
42191
+ total: 0,
42192
+ errors: []
42193
+ });
42194
+ }, []);
42195
+ return {
42196
+ ...state,
42197
+ loadModel,
42198
+ loadOBJ,
42199
+ loadTexture,
42200
+ preload,
42201
+ hasModel,
42202
+ hasTexture,
42203
+ getModel,
42204
+ getTexture,
42205
+ clearCache
42206
+ };
42207
+ }
42208
+ var init_useAssetLoader = __esm({
42209
+ "components/game/molecules/three/hooks/useAssetLoader.ts"() {
42210
+ "use client";
42211
+ init_AssetLoader();
42212
+ }
42213
+ });
42214
+ function useGameCanvas3DEvents(options) {
42215
+ const {
42216
+ tileClickEvent,
42217
+ unitClickEvent,
42218
+ featureClickEvent,
42219
+ canvasClickEvent,
42220
+ tileHoverEvent,
42221
+ tileLeaveEvent,
42222
+ unitAnimationEvent,
42223
+ cameraChangeEvent,
42224
+ onTileClick,
42225
+ onUnitClick,
42226
+ onFeatureClick,
42227
+ onCanvasClick,
42228
+ onTileHover,
42229
+ onUnitAnimation
42230
+ } = options;
42231
+ const emit = useEmitEvent();
42232
+ const optionsRef = React77.useRef(options);
42233
+ optionsRef.current = options;
42234
+ const handleTileClick = React77.useCallback(
42235
+ (tile, event) => {
42236
+ if (tileClickEvent) {
42237
+ emit(tileClickEvent, {
42238
+ tileId: tile.id,
42239
+ x: tile.x,
42240
+ z: tile.z ?? tile.y ?? 0,
42241
+ type: tile.type,
42242
+ terrain: tile.terrain,
42243
+ elevation: tile.elevation
42244
+ });
42245
+ }
42246
+ optionsRef.current.onTileClick?.(tile, event);
42247
+ },
42248
+ [tileClickEvent, emit]
42249
+ );
42250
+ const handleUnitClick = React77.useCallback(
42251
+ (unit, event) => {
42252
+ if (unitClickEvent) {
42253
+ emit(unitClickEvent, {
42254
+ unitId: unit.id,
42255
+ x: unit.x,
42256
+ z: unit.z ?? unit.y ?? 0,
42257
+ unitType: unit.unitType,
42258
+ name: unit.name,
42259
+ team: unit.team,
42260
+ faction: unit.faction,
42261
+ health: unit.health,
42262
+ maxHealth: unit.maxHealth
42263
+ });
42264
+ }
42265
+ optionsRef.current.onUnitClick?.(unit, event);
42266
+ },
42267
+ [unitClickEvent, emit]
42268
+ );
42269
+ const handleFeatureClick = React77.useCallback(
42270
+ (feature, event) => {
42271
+ if (featureClickEvent) {
42272
+ emit(featureClickEvent, {
42273
+ featureId: feature.id,
42274
+ x: feature.x,
42275
+ z: feature.z ?? feature.y ?? 0,
42276
+ type: feature.type,
42277
+ elevation: feature.elevation
42278
+ });
42279
+ }
42280
+ optionsRef.current.onFeatureClick?.(feature, event);
42281
+ },
42282
+ [featureClickEvent, emit]
42283
+ );
42284
+ const handleCanvasClick = React77.useCallback(
42285
+ (event) => {
42286
+ if (canvasClickEvent) {
42287
+ emit(canvasClickEvent, {
42288
+ clientX: event.clientX,
42289
+ clientY: event.clientY,
42290
+ button: event.button
42291
+ });
42292
+ }
42293
+ optionsRef.current.onCanvasClick?.(event);
42294
+ },
42295
+ [canvasClickEvent, emit]
42296
+ );
42297
+ const handleTileHover = React77.useCallback(
42298
+ (tile, event) => {
42299
+ if (tile) {
42300
+ if (tileHoverEvent) {
42301
+ emit(tileHoverEvent, {
42302
+ tileId: tile.id,
42303
+ x: tile.x,
42304
+ z: tile.z ?? tile.y ?? 0,
42305
+ type: tile.type
42306
+ });
42307
+ }
42308
+ } else {
42309
+ if (tileLeaveEvent) {
42310
+ emit(tileLeaveEvent, {});
42311
+ }
42312
+ }
42313
+ optionsRef.current.onTileHover?.(tile, event);
42314
+ },
42315
+ [tileHoverEvent, tileLeaveEvent, emit]
42316
+ );
42317
+ const handleUnitAnimation = React77.useCallback(
42318
+ (unitId, state) => {
42319
+ if (unitAnimationEvent) {
42320
+ emit(unitAnimationEvent, {
42321
+ unitId,
42322
+ state,
42323
+ timestamp: Date.now()
42324
+ });
42325
+ }
42326
+ optionsRef.current.onUnitAnimation?.(unitId, state);
42327
+ },
42328
+ [unitAnimationEvent, emit]
42329
+ );
42330
+ const handleCameraChange = React77.useCallback(
42331
+ (position) => {
42332
+ if (cameraChangeEvent) {
42333
+ emit(cameraChangeEvent, {
42334
+ position,
42335
+ timestamp: Date.now()
42336
+ });
42337
+ }
42338
+ },
42339
+ [cameraChangeEvent, emit]
42340
+ );
42341
+ return {
42342
+ handleTileClick,
42343
+ handleUnitClick,
42344
+ handleFeatureClick,
42345
+ handleCanvasClick,
42346
+ handleTileHover,
42347
+ handleUnitAnimation,
42348
+ handleCameraChange
42349
+ };
42350
+ }
42351
+ var init_useGameCanvas3DEvents = __esm({
42352
+ "components/game/molecules/three/hooks/useGameCanvas3DEvents.ts"() {
42353
+ "use client";
42354
+ init_useEventBus();
42355
+ }
42356
+ });
42357
+
42358
+ // components/game/molecules/three/components/Canvas3DLoadingState.css
42359
+ var init_Canvas3DLoadingState = __esm({
42360
+ "components/game/molecules/three/components/Canvas3DLoadingState.css"() {
42361
+ }
42362
+ });
42363
+ function Canvas3DLoadingState({
42364
+ progress = 0,
42365
+ loaded = 0,
42366
+ total = 0,
42367
+ message = "Loading 3D Scene...",
42368
+ details,
42369
+ showSpinner = true,
42370
+ className
42371
+ }) {
42372
+ const clampedProgress = Math.max(0, Math.min(100, progress));
42373
+ const hasProgress = total > 0;
42374
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `canvas-3d-loading ${className || ""}`, children: [
42375
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__content", children: [
42376
+ showSpinner && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__spinner", children: [
42377
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "spinner__ring" }),
42378
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "spinner__ring spinner__ring--secondary" })
42379
+ ] }),
42380
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__message", children: message }),
42381
+ details && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__details", children: details }),
42382
+ hasProgress && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__progress", children: [
42383
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "progress__bar", children: /* @__PURE__ */ jsxRuntime.jsx(
42384
+ "div",
42385
+ {
42386
+ className: "progress__fill",
42387
+ style: { width: `${clampedProgress}%` }
42388
+ }
42389
+ ) }),
42390
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "progress__text", children: [
42391
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "progress__percentage", children: [
42392
+ clampedProgress,
42393
+ "%"
42394
+ ] }),
42395
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "progress__count", children: [
42396
+ "(",
42397
+ loaded,
42398
+ "/",
42399
+ total,
42400
+ ")"
42401
+ ] })
42402
+ ] })
42403
+ ] })
42404
+ ] }),
42405
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__background", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg__grid" }) })
42406
+ ] });
42407
+ }
42408
+ var init_Canvas3DLoadingState2 = __esm({
42409
+ "components/game/molecules/three/components/Canvas3DLoadingState.tsx"() {
42410
+ init_Canvas3DLoadingState();
42411
+ }
42412
+ });
42413
+
42414
+ // components/game/molecules/three/components/Canvas3DErrorBoundary.css
42415
+ var init_Canvas3DErrorBoundary = __esm({
42416
+ "components/game/molecules/three/components/Canvas3DErrorBoundary.css"() {
42417
+ }
42418
+ });
42419
+ var log9, Canvas3DErrorBoundary;
42420
+ var init_Canvas3DErrorBoundary2 = __esm({
42421
+ "components/game/molecules/three/components/Canvas3DErrorBoundary.tsx"() {
42422
+ init_Canvas3DErrorBoundary();
42423
+ log9 = logger.createLogger("almadar:ui:game:canvas3d:error-boundary");
42424
+ Canvas3DErrorBoundary = class extends React77.Component {
42425
+ constructor(props) {
42426
+ super(props);
42427
+ __publicField(this, "handleReset", () => {
42428
+ this.setState({
42429
+ hasError: false,
42430
+ error: null,
42431
+ errorInfo: null
42432
+ });
42433
+ this.props.onReset?.();
42434
+ });
42435
+ this.state = {
42436
+ hasError: false,
42437
+ error: null,
42438
+ errorInfo: null
42439
+ };
42440
+ }
42441
+ static getDerivedStateFromError(error) {
42442
+ return {
42443
+ hasError: true,
42444
+ error,
42445
+ errorInfo: null
42446
+ };
42447
+ }
42448
+ componentDidCatch(error, errorInfo) {
42449
+ this.setState({ errorInfo });
42450
+ this.props.onError?.(error, errorInfo);
42451
+ log9.error("Error caught", { error });
42452
+ log9.error("Component stack", { componentStack: errorInfo.componentStack ?? "<none>" });
42453
+ }
42454
+ render() {
42455
+ if (this.state.hasError) {
42456
+ if (this.props.fallback) {
42457
+ return this.props.fallback;
42458
+ }
42459
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-error", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-error__content", children: [
42460
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-error__icon", children: "\u26A0\uFE0F" }),
42461
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "canvas-3d-error__title", children: "3D Scene Error" }),
42462
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "canvas-3d-error__message", children: "Something went wrong while rendering the 3D scene." }),
42463
+ this.state.error && /* @__PURE__ */ jsxRuntime.jsxs("details", { className: "canvas-3d-error__details", children: [
42464
+ /* @__PURE__ */ jsxRuntime.jsx("summary", { children: "Error Details" }),
42465
+ /* @__PURE__ */ jsxRuntime.jsxs("pre", { className: "error__stack", children: [
42466
+ this.state.error.message,
42467
+ "\n",
42468
+ this.state.error.stack
42469
+ ] }),
42470
+ this.state.errorInfo && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "error__component-stack", children: this.state.errorInfo.componentStack })
42471
+ ] }),
42472
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-error__actions", children: [
42473
+ /* @__PURE__ */ jsxRuntime.jsx(
42474
+ "button",
42475
+ {
42476
+ className: "error__button error__button--primary",
42477
+ onClick: this.handleReset,
42478
+ children: "Try Again"
42479
+ }
42480
+ ),
42481
+ /* @__PURE__ */ jsxRuntime.jsx(
42482
+ "button",
42483
+ {
42484
+ className: "error__button error__button--secondary",
42485
+ onClick: () => window.location.reload(),
42486
+ children: "Reload Page"
42487
+ }
42488
+ )
42489
+ ] })
42490
+ ] }) });
42491
+ }
42492
+ return this.props.children;
42493
+ }
42494
+ };
42495
+ }
42496
+ });
42497
+ function detectAssetRoot2(modelUrl) {
42498
+ const idx = modelUrl.indexOf("/3d/");
42499
+ if (idx !== -1) {
42500
+ return modelUrl.substring(0, idx + 4);
42501
+ }
42502
+ return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
42503
+ }
42504
+ function useGLTFModel(url, resourceBasePath) {
42505
+ const [state, setState] = React77.useState({
42506
+ model: null,
42507
+ isLoading: false,
42508
+ error: null
42509
+ });
42510
+ React77.useEffect(() => {
42511
+ if (!url) {
42512
+ setState({ model: null, isLoading: false, error: null });
42513
+ return;
42514
+ }
42515
+ log10.debug("Loading", { url });
42516
+ setState((prev) => ({ ...prev, isLoading: true, error: null }));
42517
+ const assetRoot = resourceBasePath || detectAssetRoot2(url);
42518
+ const loader = new GLTFLoader.GLTFLoader();
42519
+ loader.setResourcePath(assetRoot);
42520
+ loader.load(
42521
+ url,
42522
+ (gltf) => {
42523
+ log10.debug("Loaded", { url });
42524
+ setState({
42525
+ model: gltf.scene,
42526
+ isLoading: false,
42527
+ error: null
42528
+ });
42529
+ },
42530
+ void 0,
42531
+ (err) => {
42532
+ log10.warn("Failed", { url, error: err instanceof Error ? err : String(err) });
42533
+ setState({
42534
+ model: null,
42535
+ isLoading: false,
42536
+ error: err instanceof Error ? err : new Error(String(err))
42537
+ });
42538
+ }
42539
+ );
42540
+ }, [url, resourceBasePath]);
42541
+ return state;
42542
+ }
42543
+ function ModelLoader({
42544
+ url,
42545
+ position = [0, 0, 0],
42546
+ scale = 1,
42547
+ rotation = [0, 0, 0],
42548
+ isSelected = false,
42549
+ isHovered = false,
42550
+ onClick,
42551
+ onHover,
42552
+ fallbackGeometry = "box",
42553
+ castShadow = true,
42554
+ receiveShadow = true,
42555
+ resourceBasePath
42556
+ }) {
42557
+ const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
42558
+ const model = React77.useMemo(() => {
42559
+ if (!loadedModel) return null;
42560
+ const cloned = loadedModel.clone();
42561
+ cloned.traverse((child) => {
42562
+ if (child instanceof THREE__namespace.Mesh) {
42563
+ child.castShadow = castShadow;
42564
+ child.receiveShadow = receiveShadow;
42565
+ }
42566
+ });
42567
+ return cloned;
42568
+ }, [loadedModel, castShadow, receiveShadow]);
42569
+ const scaleArray = React77.useMemo(() => {
42570
+ if (typeof scale === "number") {
42571
+ return [scale, scale, scale];
42572
+ }
42573
+ return scale;
42574
+ }, [scale]);
42575
+ const rotationRad = React77.useMemo(() => {
42576
+ return [
42577
+ rotation[0] * Math.PI / 180,
42578
+ rotation[1] * Math.PI / 180,
42579
+ rotation[2] * Math.PI / 180
42580
+ ];
42581
+ }, [rotation]);
42582
+ if (isLoading) {
42583
+ return /* @__PURE__ */ jsxRuntime.jsx("group", { position, children: /* @__PURE__ */ jsxRuntime.jsxs("mesh", { rotation: [Math.PI / 2, 0, 0], children: [
42584
+ /* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.3, 0.35, 16] }),
42585
+ /* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#4a90d9", transparent: true, opacity: 0.8 })
42586
+ ] }) });
42587
+ }
42588
+ if (error || !model) {
42589
+ if (fallbackGeometry === "none") {
42590
+ return /* @__PURE__ */ jsxRuntime.jsx("group", { position });
42591
+ }
42592
+ const fallbackProps = {
42593
+ onClick,
42594
+ onPointerOver: () => onHover?.(true),
42595
+ onPointerOut: () => onHover?.(false)
42596
+ };
42597
+ return /* @__PURE__ */ jsxRuntime.jsxs("group", { position, children: [
42598
+ (isSelected || isHovered) && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
42599
+ /* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
42600
+ /* @__PURE__ */ jsxRuntime.jsx(
42601
+ "meshBasicMaterial",
42602
+ {
42603
+ color: isSelected ? 16755200 : 16777215,
42604
+ transparent: true,
42605
+ opacity: 0.5
42606
+ }
42607
+ )
42608
+ ] }),
42609
+ fallbackGeometry === "box" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
42610
+ /* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.8, 0.8, 0.8] }),
42611
+ /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
42612
+ ] }),
42613
+ fallbackGeometry === "sphere" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
42614
+ /* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [0.4, 16, 16] }),
42615
+ /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
42616
+ ] }),
42617
+ fallbackGeometry === "cylinder" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
42618
+ /* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.3, 0.3, 0.8, 16] }),
42619
+ /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
42620
+ ] })
42621
+ ] });
42622
+ }
42623
+ return /* @__PURE__ */ jsxRuntime.jsxs(
42624
+ "group",
42625
+ {
42626
+ position,
42627
+ rotation: rotationRad,
42628
+ onClick,
42629
+ onPointerOver: () => onHover?.(true),
42630
+ onPointerOut: () => onHover?.(false),
42631
+ children: [
42632
+ (isSelected || isHovered) && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
42633
+ /* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
42634
+ /* @__PURE__ */ jsxRuntime.jsx(
42635
+ "meshBasicMaterial",
42636
+ {
42637
+ color: isSelected ? 16755200 : 16777215,
42638
+ transparent: true,
42639
+ opacity: 0.5
42640
+ }
42641
+ )
42642
+ ] }),
42643
+ /* @__PURE__ */ jsxRuntime.jsx("primitive", { object: model, scale: scaleArray })
42644
+ ]
42645
+ }
42646
+ );
42647
+ }
42648
+ var log10;
42649
+ var init_ModelLoader = __esm({
42650
+ "components/game/molecules/three/components/ModelLoader.tsx"() {
42651
+ "use client";
42652
+ log10 = logger.createLogger("almadar:ui:game:model-loader");
42653
+ }
42654
+ });
42655
+
42656
+ // components/game/molecules/GameCanvas3D.css
42657
+ var init_GameCanvas3D = __esm({
42658
+ "components/game/molecules/GameCanvas3D.css"() {
42659
+ }
42660
+ });
42661
+ function CameraController({
42662
+ onCameraChange
42663
+ }) {
42664
+ const { camera } = fiber.useThree();
42665
+ React77.useEffect(() => {
42666
+ if (onCameraChange) {
42667
+ onCameraChange({
42668
+ x: camera.position.x,
42669
+ y: camera.position.y,
42670
+ z: camera.position.z
42671
+ });
42672
+ }
42673
+ }, [camera.position, onCameraChange]);
42674
+ return null;
42675
+ }
42676
+ var DEFAULT_GRID_CONFIG, GameCanvas3D;
42677
+ var init_GameCanvas3D2 = __esm({
42678
+ "components/game/molecules/GameCanvas3D.tsx"() {
42679
+ "use client";
42680
+ init_AssetLoader();
42681
+ init_useAssetLoader();
42682
+ init_useGameCanvas3DEvents();
42683
+ init_Canvas3DLoadingState2();
42684
+ init_Canvas3DErrorBoundary2();
42685
+ init_ModelLoader();
42686
+ init_cn();
42687
+ init_GameCanvas3D();
42688
+ DEFAULT_GRID_CONFIG = {
42689
+ cellSize: 1,
42690
+ offsetX: 0,
42691
+ offsetZ: 0
42692
+ };
42693
+ GameCanvas3D = React77.forwardRef(
42694
+ ({
42695
+ tiles = [],
42696
+ units = [],
42697
+ features = [],
42698
+ events: events2 = [],
42699
+ orientation = "standard",
42700
+ cameraMode = "isometric",
42701
+ showGrid = true,
42702
+ showCoordinates = false,
42703
+ showTileInfo = false,
42704
+ overlay = "default",
42705
+ shadows = true,
42706
+ backgroundColor = "#1a1a2e",
42707
+ onTileClick,
42708
+ onUnitClick,
42709
+ onFeatureClick,
42710
+ onCanvasClick,
42711
+ onTileHover,
42712
+ onUnitAnimation,
42713
+ assetLoader: customAssetLoader,
42714
+ tileRenderer: CustomTileRenderer,
42715
+ unitRenderer: CustomUnitRenderer,
42716
+ featureRenderer: CustomFeatureRenderer,
42717
+ className,
42718
+ isLoading: externalLoading,
42719
+ error: externalError,
42720
+ entity,
42721
+ preloadAssets = [],
42722
+ tileClickEvent,
42723
+ unitClickEvent,
42724
+ featureClickEvent,
42725
+ canvasClickEvent,
42726
+ tileHoverEvent,
42727
+ tileLeaveEvent,
42728
+ unitAnimationEvent,
42729
+ cameraChangeEvent,
42730
+ loadingMessage = "Loading 3D Scene...",
42731
+ useInstancing = true,
42732
+ validMoves = [],
42733
+ attackTargets = [],
42734
+ selectedTileIds = [],
42735
+ selectedUnitId = null,
42736
+ children
42737
+ }, ref) => {
42738
+ const containerRef = React77.useRef(null);
42739
+ const controlsRef = React77.useRef(null);
42740
+ const [hoveredTile, setHoveredTile] = React77.useState(null);
42741
+ const [internalError, setInternalError] = React77.useState(null);
42742
+ const { isLoading: assetsLoading, progress, loaded, total } = useAssetLoader({
42743
+ preloadUrls: preloadAssets,
42744
+ loader: customAssetLoader
42745
+ });
42746
+ const eventHandlers = useGameCanvas3DEvents({
42747
+ tileClickEvent,
42748
+ unitClickEvent,
42749
+ featureClickEvent,
42750
+ canvasClickEvent,
42751
+ tileHoverEvent,
42752
+ tileLeaveEvent,
42753
+ unitAnimationEvent,
42754
+ cameraChangeEvent,
42755
+ onTileClick,
42756
+ onUnitClick,
42757
+ onFeatureClick,
42758
+ onCanvasClick,
42759
+ onTileHover,
42760
+ onUnitAnimation
42761
+ });
42762
+ const gridBounds = React77.useMemo(() => {
42763
+ if (tiles.length === 0) {
42764
+ return { minX: 0, maxX: 10, minZ: 0, maxZ: 10 };
42765
+ }
42766
+ const xs = tiles.map((t) => t.x);
42767
+ const zs = tiles.map((t) => t.z || t.y || 0);
42768
+ return {
42769
+ minX: Math.min(...xs),
42770
+ maxX: Math.max(...xs),
42771
+ minZ: Math.min(...zs),
42772
+ maxZ: Math.max(...zs)
42773
+ };
42774
+ }, [tiles]);
42775
+ const cameraTarget = React77.useMemo(() => {
42776
+ return [
42777
+ (gridBounds.minX + gridBounds.maxX) / 2,
42778
+ 0,
42779
+ (gridBounds.minZ + gridBounds.maxZ) / 2
42780
+ ];
42781
+ }, [gridBounds]);
42782
+ const gridConfig = React77.useMemo(
42783
+ () => ({
42784
+ ...DEFAULT_GRID_CONFIG,
42785
+ offsetX: -(gridBounds.maxX - gridBounds.minX) / 2,
42786
+ offsetZ: -(gridBounds.maxZ - gridBounds.minZ) / 2
42787
+ }),
42788
+ [gridBounds]
42789
+ );
42790
+ const gridToWorld = React77.useCallback(
42791
+ (x, z, y = 0) => {
42792
+ const worldX = (x - gridBounds.minX) * gridConfig.cellSize;
42793
+ const worldZ = (z - gridBounds.minZ) * gridConfig.cellSize;
42794
+ return [worldX, y * gridConfig.cellSize, worldZ];
42795
+ },
42796
+ [gridBounds, gridConfig]
42797
+ );
42798
+ React77.useImperativeHandle(ref, () => ({
42799
+ getCameraPosition: () => {
42800
+ if (controlsRef.current) {
42801
+ const pos = controlsRef.current.object.position;
42802
+ return new THREE__namespace.Vector3(pos.x, pos.y, pos.z);
42803
+ }
42804
+ return null;
42805
+ },
42806
+ setCameraPosition: (x, y, z) => {
42807
+ if (controlsRef.current) {
42808
+ controlsRef.current.object.position.set(x, y, z);
42809
+ controlsRef.current.update();
42810
+ }
42811
+ },
42812
+ lookAt: (x, y, z) => {
42813
+ if (controlsRef.current) {
42814
+ controlsRef.current.target.set(x, y, z);
42815
+ controlsRef.current.update();
42816
+ }
42817
+ },
42818
+ resetCamera: () => {
42819
+ if (controlsRef.current) {
42820
+ controlsRef.current.reset();
42821
+ }
42822
+ },
42823
+ screenshot: () => {
42824
+ const canvas = containerRef.current?.querySelector("canvas");
42825
+ if (canvas) {
42826
+ return canvas.toDataURL("image/png");
42827
+ }
42828
+ return null;
42829
+ },
42830
+ export: () => ({
42831
+ tiles,
42832
+ units,
42833
+ features
42834
+ })
42835
+ }));
42836
+ const handleTileClick = React77.useCallback(
42837
+ (tile, event) => {
42838
+ eventHandlers.handleTileClick(tile, event);
42839
+ },
42840
+ [eventHandlers]
42841
+ );
42842
+ const handleUnitClick = React77.useCallback(
42843
+ (unit, event) => {
42844
+ eventHandlers.handleUnitClick(unit, event);
42845
+ },
42846
+ [eventHandlers]
42847
+ );
42848
+ const handleFeatureClick = React77.useCallback(
42849
+ (feature, event) => {
42850
+ if (event) {
42851
+ eventHandlers.handleFeatureClick(feature, event);
42852
+ }
42853
+ },
42854
+ [eventHandlers]
42855
+ );
42856
+ const handleTileHover = React77.useCallback(
42857
+ (tile, event) => {
42858
+ setHoveredTile(tile);
42859
+ if (event) {
42860
+ eventHandlers.handleTileHover(tile, event);
42861
+ }
42862
+ },
42863
+ [eventHandlers]
42864
+ );
42865
+ const cameraConfig = React77.useMemo(() => {
42866
+ const size = Math.max(
42867
+ gridBounds.maxX - gridBounds.minX,
42868
+ gridBounds.maxZ - gridBounds.minZ
42869
+ );
42870
+ const distance = size * 1.5;
42871
+ switch (cameraMode) {
42872
+ case "isometric":
42873
+ return {
42874
+ position: [distance, distance * 0.8, distance],
42875
+ fov: 45
42876
+ };
42877
+ case "top-down":
42878
+ return {
42879
+ position: [0, distance * 2, 0],
42880
+ fov: 45
42881
+ };
42882
+ case "perspective":
42883
+ default:
42884
+ return {
42885
+ position: [distance, distance, distance],
42886
+ fov: 45
42887
+ };
42888
+ }
42889
+ }, [cameraMode, gridBounds]);
42890
+ const DefaultTileRenderer = React77.useCallback(
42891
+ ({ tile, position }) => {
42892
+ const isSelected = tile.id ? selectedTileIds.includes(tile.id) : false;
42893
+ const isHovered = hoveredTile?.id === tile.id;
42894
+ const isValidMove = validMoves.some(
42895
+ (m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
42896
+ );
42897
+ const isAttackTarget = attackTargets.some(
42898
+ (m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
42899
+ );
42900
+ let color = 8421504;
42901
+ if (tile.type === "water") color = 4491468;
42902
+ else if (tile.type === "grass") color = 4500036;
42903
+ else if (tile.type === "sand") color = 14535816;
42904
+ else if (tile.type === "rock") color = 8947848;
42905
+ else if (tile.type === "snow") color = 15658734;
42906
+ let emissive = 0;
42907
+ if (isSelected) emissive = 4473924;
42908
+ else if (isAttackTarget) emissive = 4456448;
42909
+ else if (isValidMove) emissive = 17408;
42910
+ else if (isHovered) emissive = 2236962;
42911
+ if (tile.modelUrl) {
42912
+ return /* @__PURE__ */ jsxRuntime.jsx(
42913
+ "group",
42914
+ {
42915
+ position,
42916
+ onClick: (e) => handleTileClick(tile, e),
42917
+ onPointerEnter: (e) => handleTileHover(tile, e),
42918
+ onPointerLeave: (e) => handleTileHover(null, e),
42919
+ userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
42920
+ children: /* @__PURE__ */ jsxRuntime.jsx(
42921
+ ModelLoader,
42922
+ {
42923
+ url: tile.modelUrl,
42924
+ scale: 0.95,
42925
+ fallbackGeometry: "box",
42926
+ castShadow: true,
42927
+ receiveShadow: true
42928
+ }
42929
+ )
42930
+ }
42931
+ );
42932
+ }
42933
+ return /* @__PURE__ */ jsxRuntime.jsxs(
42934
+ "mesh",
42935
+ {
42936
+ position,
42937
+ onClick: (e) => handleTileClick(tile, e),
42938
+ onPointerEnter: (e) => handleTileHover(tile, e),
42939
+ onPointerLeave: (e) => handleTileHover(null, e),
42940
+ userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
42941
+ children: [
42942
+ /* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.95, 0.2, 0.95] }),
42943
+ /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color, emissive })
42944
+ ]
42945
+ }
42946
+ );
42947
+ },
42948
+ [selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover]
42949
+ );
42950
+ const DefaultUnitRenderer = React77.useCallback(
42951
+ ({ unit, position }) => {
42952
+ const isSelected = selectedUnitId === unit.id;
42953
+ const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
42954
+ return /* @__PURE__ */ jsxRuntime.jsxs(
42955
+ "group",
42956
+ {
42957
+ position,
42958
+ onClick: (e) => handleUnitClick(unit, e),
42959
+ userData: { type: "unit", unitId: unit.id },
42960
+ children: [
42961
+ isSelected && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.05, 0], rotation: [-Math.PI / 2, 0, 0], children: [
42962
+ /* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
42963
+ /* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
42964
+ ] }),
42965
+ unit.modelUrl ? (
42966
+ /* GLB unit model (box fallback while loading / on error) */
42967
+ /* @__PURE__ */ jsxRuntime.jsx(
42968
+ ModelLoader,
42969
+ {
42970
+ url: unit.modelUrl,
42971
+ scale: 0.5,
42972
+ fallbackGeometry: "box",
42973
+ castShadow: true
42974
+ }
42975
+ )
42976
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
42977
+ /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.3, 0], children: [
42978
+ /* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.3, 0.3, 0.1, 8] }),
42979
+ /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
42980
+ ] }),
42981
+ /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.6, 0], children: [
42982
+ /* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [0.2, 0.4, 4, 8] }),
42983
+ /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
42984
+ ] }),
42985
+ /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
42986
+ /* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [0.12, 8, 8] }),
42987
+ /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
42988
+ ] })
42989
+ ] }),
42990
+ unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("group", { position: [0, 1.2, 0], children: [
42991
+ /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [-0.25, 0, 0], children: [
42992
+ /* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.5, 0.05] }),
42993
+ /* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: 3355443 })
42994
+ ] }),
42995
+ /* @__PURE__ */ jsxRuntime.jsxs(
42996
+ "mesh",
42997
+ {
42998
+ position: [
42999
+ -0.25 + 0.5 * (unit.health / unit.maxHealth) / 2,
43000
+ 0,
43001
+ 0.01
43002
+ ],
43003
+ children: [
43004
+ /* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.5 * (unit.health / unit.maxHealth), 0.05] }),
43005
+ /* @__PURE__ */ jsxRuntime.jsx(
43006
+ "meshBasicMaterial",
43007
+ {
43008
+ color: unit.health / unit.maxHealth > 0.5 ? 4500036 : unit.health / unit.maxHealth > 0.25 ? 11184708 : 16729156
43009
+ }
43010
+ )
43011
+ ]
43012
+ }
43013
+ )
43014
+ ] })
43015
+ ]
43016
+ }
43017
+ );
43018
+ },
43019
+ [selectedUnitId, handleUnitClick]
43020
+ );
43021
+ const DefaultFeatureRenderer = React77.useCallback(
43022
+ ({
43023
+ feature,
43024
+ position
43025
+ }) => {
43026
+ if (feature.assetUrl) {
43027
+ return /* @__PURE__ */ jsxRuntime.jsx(
43028
+ ModelLoader,
43029
+ {
43030
+ url: feature.assetUrl,
43031
+ position,
43032
+ scale: 0.5,
43033
+ rotation: [0, feature.rotation ?? 0, 0],
43034
+ onClick: () => handleFeatureClick(feature, null),
43035
+ fallbackGeometry: "box"
43036
+ },
43037
+ feature.id
43038
+ );
43039
+ }
43040
+ if (feature.type === "tree") {
43041
+ return /* @__PURE__ */ jsxRuntime.jsxs(
43042
+ "group",
43043
+ {
43044
+ position,
43045
+ onClick: (e) => handleFeatureClick(feature, e),
43046
+ userData: { type: "feature", featureId: feature.id },
43047
+ children: [
43048
+ /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.4, 0], children: [
43049
+ /* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.1, 0.15, 0.8, 6] }),
43050
+ /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 9127187 })
43051
+ ] }),
43052
+ /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
43053
+ /* @__PURE__ */ jsxRuntime.jsx("coneGeometry", { args: [0.5, 0.8, 8] }),
43054
+ /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 2263842 })
43055
+ ] })
43056
+ ]
43057
+ }
43058
+ );
43059
+ }
43060
+ if (feature.type === "rock") {
43061
+ return /* @__PURE__ */ jsxRuntime.jsxs(
43062
+ "mesh",
43063
+ {
43064
+ position: [position[0], position[1] + 0.3, position[2]],
43065
+ onClick: (e) => handleFeatureClick(feature, e),
43066
+ userData: { type: "feature", featureId: feature.id },
43067
+ children: [
43068
+ /* @__PURE__ */ jsxRuntime.jsx("dodecahedronGeometry", { args: [0.3, 0] }),
43069
+ /* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 8421504 })
43070
+ ]
43071
+ }
43072
+ );
43073
+ }
43074
+ return null;
43075
+ },
43076
+ [handleFeatureClick]
43077
+ );
43078
+ if (externalLoading || assetsLoading && preloadAssets.length > 0) {
43079
+ return /* @__PURE__ */ jsxRuntime.jsx(
43080
+ Canvas3DLoadingState,
43081
+ {
43082
+ progress,
43083
+ loaded,
43084
+ total,
43085
+ message: loadingMessage,
43086
+ className
43087
+ }
43088
+ );
43089
+ }
43090
+ const displayError = externalError || internalError;
43091
+ if (displayError) {
43092
+ return /* @__PURE__ */ jsxRuntime.jsx(Canvas3DErrorBoundary, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "game-canvas-3d game-canvas-3d--error", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__error", children: [
43093
+ "Error: ",
43094
+ displayError
43095
+ ] }) }) });
43096
+ }
43097
+ return /* @__PURE__ */ jsxRuntime.jsx(
43098
+ Canvas3DErrorBoundary,
43099
+ {
43100
+ onError: (err) => setInternalError(err.message),
43101
+ onReset: () => setInternalError(null),
43102
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
43103
+ "div",
43104
+ {
43105
+ ref: containerRef,
43106
+ className: cn("game-canvas-3d relative w-full h-full min-h-[85vh] overflow-hidden", className),
43107
+ "data-orientation": orientation,
43108
+ "data-camera-mode": cameraMode,
43109
+ "data-overlay": overlay,
43110
+ children: [
43111
+ /* @__PURE__ */ jsxRuntime.jsxs(
43112
+ fiber.Canvas,
43113
+ {
43114
+ shadows,
43115
+ camera: {
43116
+ position: cameraConfig.position,
43117
+ fov: cameraConfig.fov,
43118
+ near: 0.1,
43119
+ far: 1e3
43120
+ },
43121
+ style: { background: backgroundColor },
43122
+ onClick: (e) => {
43123
+ if (e.target === e.currentTarget) {
43124
+ eventHandlers.handleCanvasClick(e);
43125
+ }
43126
+ },
43127
+ children: [
43128
+ /* @__PURE__ */ jsxRuntime.jsx(CameraController, { onCameraChange: eventHandlers.handleCameraChange }),
43129
+ /* @__PURE__ */ jsxRuntime.jsx("ambientLight", { intensity: 0.6 }),
43130
+ /* @__PURE__ */ jsxRuntime.jsx(
43131
+ "directionalLight",
43132
+ {
43133
+ position: [10, 20, 10],
43134
+ intensity: 0.8,
43135
+ castShadow: shadows,
43136
+ "shadow-mapSize": [2048, 2048]
43137
+ }
43138
+ ),
43139
+ /* @__PURE__ */ jsxRuntime.jsx("hemisphereLight", { intensity: 0.3, color: "#87ceeb", groundColor: "#362d1d" }),
43140
+ showGrid && /* @__PURE__ */ jsxRuntime.jsx(
43141
+ drei.Grid,
43142
+ {
43143
+ args: [
43144
+ Math.max(gridBounds.maxX - gridBounds.minX + 2, 10),
43145
+ Math.max(gridBounds.maxZ - gridBounds.minZ + 2, 10)
43146
+ ],
43147
+ position: [
43148
+ (gridBounds.maxX - gridBounds.minX) / 2 - 0.5,
43149
+ 0,
43150
+ (gridBounds.maxZ - gridBounds.minZ) / 2 - 0.5
43151
+ ],
43152
+ cellSize: 1,
43153
+ cellThickness: 1,
43154
+ cellColor: "#444444",
43155
+ sectionSize: 5,
43156
+ sectionThickness: 1.5,
43157
+ sectionColor: "#666666",
43158
+ fadeDistance: 50,
43159
+ fadeStrength: 1
43160
+ }
43161
+ ),
43162
+ tiles.map((tile, index) => {
43163
+ const position = gridToWorld(
43164
+ tile.x,
43165
+ tile.z ?? tile.y ?? 0,
43166
+ tile.elevation ?? 0
43167
+ );
43168
+ const Renderer = CustomTileRenderer || DefaultTileRenderer;
43169
+ return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
43170
+ }),
43171
+ features.map((feature, index) => {
43172
+ const position = gridToWorld(
43173
+ feature.x,
43174
+ feature.z ?? feature.y ?? 0,
43175
+ (feature.elevation ?? 0) + 0.5
43176
+ );
43177
+ const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
43178
+ return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
43179
+ }),
43180
+ units.map((unit) => {
43181
+ const position = gridToWorld(
43182
+ unit.x ?? 0,
43183
+ unit.z ?? unit.y ?? 0,
43184
+ (unit.elevation ?? 0) + 0.5
43185
+ );
43186
+ const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
43187
+ return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { unit, position }, unit.id);
43188
+ }),
43189
+ children,
43190
+ /* @__PURE__ */ jsxRuntime.jsx(
43191
+ drei.OrbitControls,
43192
+ {
43193
+ ref: controlsRef,
43194
+ target: cameraTarget,
43195
+ enableDamping: true,
43196
+ dampingFactor: 0.05,
43197
+ minDistance: 2,
43198
+ maxDistance: 100,
43199
+ maxPolarAngle: Math.PI / 2 - 0.1
43200
+ }
43201
+ )
43202
+ ]
43203
+ }
43204
+ ),
43205
+ showCoordinates && hoveredTile && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__coordinates", children: [
43206
+ "X: ",
43207
+ hoveredTile.x,
43208
+ ", Z: ",
43209
+ hoveredTile.z ?? hoveredTile.y ?? 0
43210
+ ] }),
43211
+ showTileInfo && hoveredTile && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__tile-info", children: [
43212
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tile-info__type", children: hoveredTile.type }),
43213
+ hoveredTile.terrain && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tile-info__terrain", children: hoveredTile.terrain })
43214
+ ] })
43215
+ ]
43216
+ }
43217
+ )
43218
+ }
43219
+ );
43220
+ }
43221
+ );
43222
+ GameCanvas3D.displayName = "GameCanvas3D";
43223
+ }
43224
+ });
43225
+ function GameBoard3D({
43226
+ entity,
43227
+ tiles = [],
43228
+ features = [],
43229
+ cameraMode = "perspective",
43230
+ backgroundColor = "#2a1a1a",
43231
+ tileClickEvent,
43232
+ unitClickEvent,
43233
+ attackEvent,
43234
+ endTurnEvent,
43235
+ cancelEvent,
43236
+ playAgainEvent,
43237
+ gameEndEvent,
43238
+ className
43239
+ }) {
43240
+ const row = boardEntity(entity);
43241
+ const eventBus = useEventBus();
43242
+ const entityUnits = row ? rows(row.units) : [];
43243
+ const units = entityUnits;
43244
+ const selectedUnitId = row ? str(row.selectedUnitId) || null : null;
43245
+ const phase = row ? str(row.phase) : "observation";
43246
+ const result = row ? str(row.result) : "none";
43247
+ const validMoves = row && Array.isArray(row.validMoves) ? row.validMoves : [];
43248
+ const attackTargets = row && Array.isArray(row.attackTargets) ? row.attackTargets : [];
43249
+ const turn = row ? num(row.turn) : 0;
43250
+ const currentTeam = row ? str(row.currentTeam) : "player";
43251
+ const isGameOver = result !== "none";
43252
+ const gameEndEmittedRef = React77.useRef(false);
43253
+ React77.useEffect(() => {
43254
+ if ((result === "victory" || result === "defeat") && gameEndEvent) {
43255
+ if (!gameEndEmittedRef.current) {
43256
+ gameEndEmittedRef.current = true;
43257
+ eventBus.emit(`UI:${gameEndEvent}`, { result });
43258
+ }
43259
+ } else {
43260
+ gameEndEmittedRef.current = false;
43261
+ }
43262
+ }, [result, gameEndEvent, eventBus]);
43263
+ const checkGameEnd = React77.useCallback(() => {
43264
+ const alivePlayer = entityUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
43265
+ const aliveEnemy = entityUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
43266
+ if (alivePlayer.length === 0 && gameEndEvent) {
43267
+ eventBus.emit(`UI:${gameEndEvent}`, { result: "defeat" });
43268
+ } else if (aliveEnemy.length === 0 && gameEndEvent) {
43269
+ eventBus.emit(`UI:${gameEndEvent}`, { result: "victory" });
43270
+ }
43271
+ }, [entityUnits, gameEndEvent, eventBus]);
43272
+ const handleUnitClickCallback = React77.useCallback((isoUnit) => {
43273
+ if (phase !== "action" || !selectedUnitId || !attackEvent) return;
43274
+ const attackerRow = entityUnits.find((u) => str(u.id) === selectedUnitId);
43275
+ const targetRow = entityUnits.find((u) => str(u.id) === isoUnit.id);
43276
+ if (!attackerRow || !targetRow) return;
43277
+ if (unitTeam(targetRow) !== "enemy" || unitHealth(targetRow) <= 0) return;
43278
+ const ap = unitPosition(attackerRow);
43279
+ const tp = unitPosition(targetRow);
43280
+ const dx = Math.abs(ap.x - tp.x);
43281
+ const dy = Math.abs(ap.y - tp.y);
43282
+ if (dx <= 1 && dy <= 1 && dx + dy > 0) {
43283
+ const damage = Math.max(1, num(attackerRow.attack) - num(targetRow.defense));
43284
+ eventBus.emit(`UI:${attackEvent}`, {
43285
+ attackerId: str(attackerRow.id),
43286
+ targetId: str(targetRow.id),
43287
+ damage
43288
+ });
43289
+ setTimeout(checkGameEnd, 100);
43290
+ }
43291
+ }, [phase, selectedUnitId, entityUnits, attackEvent, eventBus, checkGameEnd]);
43292
+ const handleEndTurn = React77.useCallback(() => {
43293
+ if (endTurnEvent) eventBus.emit(`UI:${endTurnEvent}`, {});
43294
+ }, [endTurnEvent, eventBus]);
43295
+ const handleCancel = React77.useCallback(() => {
43296
+ if (cancelEvent) eventBus.emit(`UI:${cancelEvent}`, {});
43297
+ }, [cancelEvent, eventBus]);
43298
+ const handlePlayAgain = React77.useCallback(() => {
43299
+ if (playAgainEvent) eventBus.emit(`UI:${playAgainEvent}`, {});
43300
+ }, [playAgainEvent, eventBus]);
43301
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { className: cn("game-board-3d block w-full min-h-[85vh] relative", className), children: [
43302
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-board-3d__status absolute top-3 left-3 z-10 flex gap-2 items-center", children: [
43303
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "status__phase capitalize", children: phase.replace("_", " ") }),
43304
+ currentTeam && !isGameOver && /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "small", color: "muted", className: "status__team", children: [
43305
+ "\u2014 ",
43306
+ currentTeam === "player" ? "Your Turn" : "Enemy's Turn"
43307
+ ] }),
43308
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "small", color: "muted", className: "status__turn", children: [
43309
+ "Turn ",
43310
+ turn
43311
+ ] })
43312
+ ] }),
43313
+ /* @__PURE__ */ jsxRuntime.jsx(
43314
+ GameCanvas3D,
43315
+ {
43316
+ tiles,
43317
+ units,
43318
+ features,
43319
+ cameraMode,
43320
+ showGrid: true,
43321
+ showCoordinates: false,
43322
+ showTileInfo: false,
43323
+ shadows: true,
43324
+ backgroundColor,
43325
+ tileClickEvent,
43326
+ unitClickEvent,
43327
+ onUnitClick: handleUnitClickCallback,
43328
+ selectedUnitId,
43329
+ validMoves,
43330
+ attackTargets,
43331
+ className: "game-board-3d__canvas w-full min-h-[85vh]"
43332
+ }
43333
+ ),
43334
+ !isGameOver && /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { className: "fixed bottom-6 right-6 z-50", gap: "sm", children: [
43335
+ (phase === "selection" || phase === "movement" || phase === "action") && /* @__PURE__ */ jsxRuntime.jsx(
43336
+ exports.Button,
43337
+ {
43338
+ variant: "secondary",
43339
+ className: "shadow-xl",
43340
+ onClick: handleCancel,
43341
+ children: "Cancel"
43342
+ }
43343
+ ),
43344
+ phase !== "enemy_turn" && /* @__PURE__ */ jsxRuntime.jsx(
43345
+ exports.Button,
43346
+ {
43347
+ variant: "primary",
43348
+ className: "shadow-xl",
43349
+ onClick: handleEndTurn,
43350
+ children: "End Turn"
43351
+ }
43352
+ )
43353
+ ] }),
43354
+ isGameOver && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "game-board-3d__overlay absolute inset-0 z-20 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { align: "center", gap: "md", className: "overlay__card p-8 rounded-xl bg-gray-900/90 shadow-2xl", children: [
43355
+ /* @__PURE__ */ jsxRuntime.jsx(
43356
+ exports.Typography,
43357
+ {
43358
+ variant: "h2",
43359
+ className: cn(
43360
+ "overlay__result",
43361
+ result === "victory" ? "text-yellow-400" : "text-red-500"
43362
+ ),
43363
+ children: result === "victory" ? "Victory!" : "Defeat"
43364
+ }
43365
+ ),
43366
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "body", color: "muted", className: "overlay__turn-count", children: [
43367
+ "Completed in ",
43368
+ turn,
43369
+ " turn",
43370
+ turn !== 1 ? "s" : ""
43371
+ ] }),
43372
+ /* @__PURE__ */ jsxRuntime.jsx(
43373
+ exports.Button,
43374
+ {
43375
+ variant: "primary",
43376
+ className: "px-8 py-3 font-semibold",
43377
+ onClick: handlePlayAgain,
43378
+ children: "Play Again"
43379
+ }
43380
+ )
43381
+ ] }) })
43382
+ ] });
43383
+ }
43384
+ var init_GameBoard3D = __esm({
43385
+ "components/game/organisms/GameBoard3D.tsx"() {
43386
+ "use client";
43387
+ init_cn();
43388
+ init_Button();
43389
+ init_Typography();
43390
+ init_Stack();
43391
+ init_useEventBus();
43392
+ init_GameCanvas3D2();
43393
+ init_boardEntity();
43394
+ GameBoard3D.displayName = "GameBoard3D";
43395
+ }
43396
+ });
41717
43397
  exports.GameShell = void 0;
41718
43398
  var init_GameShell = __esm({
41719
43399
  "components/game/templates/GameShell.tsx"() {
@@ -43190,21 +44870,6 @@ var init_ModalSlot = __esm({
43190
44870
  exports.ModalSlot.displayName = "ModalSlot";
43191
44871
  }
43192
44872
  });
43193
- function getOpponentAction(strategy, actions, history) {
43194
- const actionIds = actions.map((a) => a.id);
43195
- switch (strategy) {
43196
- case "always-cooperate":
43197
- return actionIds[0];
43198
- case "always-defect":
43199
- return actionIds[actionIds.length - 1];
43200
- case "tit-for-tat":
43201
- if (history.length === 0) return actionIds[0];
43202
- return history[history.length - 1].playerAction;
43203
- case "random":
43204
- default:
43205
- return actionIds[Math.floor(Math.random() * actionIds.length)];
43206
- }
43207
- }
43208
44873
  function NegotiatorBoard({
43209
44874
  entity,
43210
44875
  completeEvent = "PUZZLE_COMPLETE",
@@ -43226,28 +44891,35 @@ function NegotiatorBoard({
43226
44891
  const playerTotal = num(resolved?.score);
43227
44892
  const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
43228
44893
  const won = result === "win";
43229
- const opponentTotal = history.reduce((s, r) => s + r.opponentPayoff, 0);
44894
+ const lastPlayerAction = str(resolved?.lastPlayerAction);
44895
+ const lastOpponentAction = str(resolved?.lastOpponentAction);
44896
+ const lastPayoff = num(resolved?.lastPayoff);
43230
44897
  const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
43231
44898
  const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
44899
+ const prevRoundRef = React77.useRef(currentRound);
44900
+ React77.useEffect(() => {
44901
+ if (currentRound > prevRoundRef.current && lastPlayerAction) {
44902
+ const opponentPayoffEntry = payoffMatrix.find(
44903
+ (p2) => p2.playerAction === lastPlayerAction && p2.opponentAction === lastOpponentAction
44904
+ );
44905
+ setHistory((prev) => [
44906
+ ...prev,
44907
+ {
44908
+ round: currentRound,
44909
+ playerAction: lastPlayerAction,
44910
+ opponentAction: lastOpponentAction,
44911
+ playerPayoff: lastPayoff,
44912
+ opponentPayoff: opponentPayoffEntry?.opponentPayoff ?? 0
44913
+ }
44914
+ ]);
44915
+ }
44916
+ prevRoundRef.current = currentRound;
44917
+ }, [currentRound, lastPlayerAction, lastOpponentAction, lastPayoff, payoffMatrix]);
44918
+ const opponentTotal = React77.useMemo(() => history.reduce((s, r) => s + r.opponentPayoff, 0), [history]);
43232
44919
  const handleAction = React77.useCallback((actionId) => {
43233
44920
  if (isComplete) return;
43234
- const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
43235
- const payoff = payoffMatrix.find(
43236
- (p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
43237
- );
43238
- const playerPayoff = payoff?.playerPayoff ?? 0;
43239
- setHistory((prev) => [
43240
- ...prev,
43241
- {
43242
- round: prev.length + 1,
43243
- playerAction: actionId,
43244
- opponentAction,
43245
- playerPayoff,
43246
- opponentPayoff: payoff?.opponentPayoff ?? 0
43247
- }
43248
- ]);
43249
44921
  if (playRoundEvent) {
43250
- emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: playerPayoff });
44922
+ emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: 0 });
43251
44923
  }
43252
44924
  if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
43253
44925
  if (finishEvent) {
@@ -43257,10 +44929,11 @@ function NegotiatorBoard({
43257
44929
  setShowHint(true);
43258
44930
  }
43259
44931
  }
43260
- }, [isComplete, resolved, totalRounds, currentRound, actions, payoffMatrix, history, playRoundEvent, finishEvent, emit]);
44932
+ }, [isComplete, resolved, totalRounds, currentRound, playRoundEvent, finishEvent, emit]);
43261
44933
  const handleReset = React77.useCallback(() => {
43262
44934
  setHistory([]);
43263
44935
  setShowHint(false);
44936
+ prevRoundRef.current = 0;
43264
44937
  if (playAgainEvent) {
43265
44938
  emit(`UI:${playAgainEvent}`, {});
43266
44939
  }
@@ -43371,6 +45044,236 @@ var init_NegotiatorBoard = __esm({
43371
45044
  NegotiatorBoard.displayName = "NegotiatorBoard";
43372
45045
  }
43373
45046
  });
45047
+ function resolveEntity(entity) {
45048
+ if (!entity) return {};
45049
+ if (Array.isArray(entity)) return entity[0] ?? {};
45050
+ return entity;
45051
+ }
45052
+ function numField(row, key, fallback) {
45053
+ const v = row[key];
45054
+ return typeof v === "number" ? v : fallback;
45055
+ }
45056
+ function strField(row, key, fallback) {
45057
+ const v = row[key];
45058
+ return typeof v === "string" ? v : fallback;
45059
+ }
45060
+ function PlatformerBoard({
45061
+ entity,
45062
+ player: propPlayer,
45063
+ platforms: propPlatforms,
45064
+ result: propResult,
45065
+ lives: propLives,
45066
+ score: propScore,
45067
+ level: propLevel,
45068
+ worldWidth = 800,
45069
+ worldHeight = 400,
45070
+ canvasWidth = 800,
45071
+ canvasHeight = 400,
45072
+ playerSprite,
45073
+ tileSprites,
45074
+ bgColor,
45075
+ leftEvent = "LEFT",
45076
+ rightEvent = "RIGHT",
45077
+ jumpEvent = "JUMP",
45078
+ stopEvent = "STOP",
45079
+ playAgainEvent = "PLAY_AGAIN",
45080
+ gameEndEvent = "GAME_END",
45081
+ className
45082
+ }) {
45083
+ const row = resolveEntity(entity);
45084
+ const eventBus = useEventBus();
45085
+ const { t } = hooks.useTranslate();
45086
+ const result = propResult ?? strField(row, "result", "none");
45087
+ propLives ?? numField(row, "lives", 3);
45088
+ const score = propScore ?? numField(row, "score", 0);
45089
+ const level = propLevel ?? numField(row, "level", 1);
45090
+ const entityPlayer = row["player"];
45091
+ const player = propPlayer ?? entityPlayer ?? {
45092
+ x: 80,
45093
+ y: 320,
45094
+ width: 32,
45095
+ height: 48,
45096
+ vx: 0,
45097
+ vy: 0,
45098
+ grounded: false,
45099
+ facingRight: true
45100
+ };
45101
+ const entityPlatforms = Array.isArray(row["platforms"]) ? row["platforms"] : void 0;
45102
+ const platforms = propPlatforms ?? entityPlatforms ?? [
45103
+ { x: 0, y: 368, width: 800, height: 32, type: "ground" },
45104
+ { x: 150, y: 280, width: 160, height: 16, type: "platform" },
45105
+ { x: 420, y: 220, width: 160, height: 16, type: "platform" },
45106
+ { x: 580, y: 300, width: 80, height: 16, type: "hazard" },
45107
+ { x: 700, y: 340, width: 64, height: 28, type: "goal" }
45108
+ ];
45109
+ const handleRestart = React77.useCallback(() => {
45110
+ if (playAgainEvent) {
45111
+ eventBus.emit(`UI:${playAgainEvent}`, {});
45112
+ }
45113
+ }, [playAgainEvent, eventBus]);
45114
+ const isGameOver = result === "won" || result === "lost";
45115
+ const gameEndEmittedRef = React77.useRef(false);
45116
+ React77.useEffect(() => {
45117
+ if ((result === "won" || result === "lost") && gameEndEvent) {
45118
+ if (!gameEndEmittedRef.current) {
45119
+ gameEndEmittedRef.current = true;
45120
+ eventBus.emit(`UI:${gameEndEvent}`, { result });
45121
+ }
45122
+ } else {
45123
+ gameEndEmittedRef.current = false;
45124
+ }
45125
+ }, [result, gameEndEvent, eventBus]);
45126
+ return /* @__PURE__ */ jsxRuntime.jsx(
45127
+ exports.VStack,
45128
+ {
45129
+ className: cn("platformer-board relative bg-background", className),
45130
+ gap: "none",
45131
+ children: /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "relative", children: [
45132
+ /* @__PURE__ */ jsxRuntime.jsx(
45133
+ PlatformerCanvas,
45134
+ {
45135
+ player,
45136
+ platforms,
45137
+ worldWidth,
45138
+ worldHeight,
45139
+ canvasWidth,
45140
+ canvasHeight,
45141
+ followCamera: true,
45142
+ bgColor,
45143
+ playerSprite,
45144
+ tileSprites,
45145
+ leftEvent,
45146
+ rightEvent,
45147
+ jumpEvent,
45148
+ stopEvent
45149
+ }
45150
+ ),
45151
+ isGameOver && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { className: "text-center p-8", gap: "lg", children: [
45152
+ /* @__PURE__ */ jsxRuntime.jsx(
45153
+ exports.Typography,
45154
+ {
45155
+ variant: "h2",
45156
+ className: cn(
45157
+ "text-4xl font-black tracking-widest uppercase",
45158
+ result === "won" ? "text-warning" : "text-error"
45159
+ ),
45160
+ children: result === "won" ? t("platformer.won") : t("platformer.lost")
45161
+ }
45162
+ ),
45163
+ result === "won" && /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "body1", className: "text-muted-foreground", children: [
45164
+ t("platformer.level"),
45165
+ " ",
45166
+ level,
45167
+ " \u2014 ",
45168
+ t("platformer.score"),
45169
+ ": ",
45170
+ score
45171
+ ] }),
45172
+ result === "lost" && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body1", className: "text-muted-foreground", children: t("platformer.noLives") }),
45173
+ /* @__PURE__ */ jsxRuntime.jsx(
45174
+ exports.Button,
45175
+ {
45176
+ variant: "primary",
45177
+ className: "px-8 py-3 font-semibold",
45178
+ onClick: handleRestart,
45179
+ children: result === "won" ? t("platformer.nextLevel") : t("platformer.playAgain")
45180
+ }
45181
+ )
45182
+ ] }) })
45183
+ ] })
45184
+ }
45185
+ );
45186
+ }
45187
+ var init_PlatformerBoard = __esm({
45188
+ "components/game/organisms/PlatformerBoard.tsx"() {
45189
+ "use client";
45190
+ init_cn();
45191
+ init_useEventBus();
45192
+ init_Box();
45193
+ init_Button();
45194
+ init_Typography();
45195
+ init_Stack();
45196
+ init_PlatformerCanvas();
45197
+ PlatformerBoard.displayName = "PlatformerBoard";
45198
+ }
45199
+ });
45200
+ function PlatformerTemplate({
45201
+ entity,
45202
+ platforms = DEFAULT_PLATFORMS,
45203
+ worldWidth = 800,
45204
+ worldHeight = 400,
45205
+ canvasWidth = 800,
45206
+ canvasHeight = 400,
45207
+ playerSprite = `${CDN3}/characters/platformChar_idle.png`,
45208
+ tileSprites = DEFAULT_TILE_SPRITES,
45209
+ bgColor = "#5c94fc",
45210
+ gameEndEvent = "GAME_END",
45211
+ className
45212
+ }) {
45213
+ const row = entity && !Array.isArray(entity) ? entity : void 0;
45214
+ const lives = typeof row?.["lives"] === "number" ? row["lives"] : 3;
45215
+ const score = typeof row?.["score"] === "number" ? row["score"] : 0;
45216
+ const level = typeof row?.["level"] === "number" ? row["level"] : 1;
45217
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { className: cn("platformer-template w-full", className), gap: "none", children: [
45218
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsx(
45219
+ GameHud,
45220
+ {
45221
+ position: "top",
45222
+ stats: [
45223
+ { label: "Lives", value: lives, format: "number", variant: "danger" },
45224
+ { label: "Level", value: level, format: "number", variant: "default" }
45225
+ ]
45226
+ }
45227
+ ) }),
45228
+ /* @__PURE__ */ jsxRuntime.jsx(
45229
+ PlatformerBoard,
45230
+ {
45231
+ entity,
45232
+ platforms,
45233
+ worldWidth,
45234
+ worldHeight,
45235
+ canvasWidth,
45236
+ canvasHeight,
45237
+ playerSprite,
45238
+ tileSprites,
45239
+ bgColor,
45240
+ leftEvent: "LEFT",
45241
+ rightEvent: "RIGHT",
45242
+ jumpEvent: "JUMP",
45243
+ stopEvent: "STOP",
45244
+ playAgainEvent: "PLAY_AGAIN",
45245
+ gameEndEvent
45246
+ }
45247
+ ),
45248
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "px-4 py-2", children: /* @__PURE__ */ jsxRuntime.jsx(ScoreBoard, { score, level }) })
45249
+ ] });
45250
+ }
45251
+ var CDN3, DEFAULT_TILE_SPRITES, DEFAULT_PLATFORMS;
45252
+ var init_PlatformerTemplate = __esm({
45253
+ "components/game/templates/PlatformerTemplate.tsx"() {
45254
+ init_cn();
45255
+ init_Box();
45256
+ init_Stack();
45257
+ init_PlatformerBoard();
45258
+ init_GameHud();
45259
+ init_ScoreBoard();
45260
+ CDN3 = "https://almadar-kflow-assets.web.app/shared/platformer";
45261
+ DEFAULT_TILE_SPRITES = {
45262
+ ground: `${CDN3}/tiles/platformPack_tile001.png`,
45263
+ platform: `${CDN3}/tiles/platformPack_tile004.png`,
45264
+ hazard: `${CDN3}/tiles/platformPack_tile017.png`,
45265
+ goal: `${CDN3}/tiles/platformPack_tile007.png`
45266
+ };
45267
+ DEFAULT_PLATFORMS = [
45268
+ { x: 0, y: 368, width: 800, height: 32, type: "ground" },
45269
+ { x: 150, y: 280, width: 160, height: 16, type: "platform" },
45270
+ { x: 420, y: 220, width: 160, height: 16, type: "platform" },
45271
+ { x: 580, y: 300, width: 80, height: 16, type: "hazard" },
45272
+ { x: 700, y: 340, width: 64, height: 28, type: "goal" }
45273
+ ];
45274
+ PlatformerTemplate.displayName = "PlatformerTemplate";
45275
+ }
45276
+ });
43374
45277
  exports.PricingOrganism = void 0;
43375
45278
  var init_PricingOrganism = __esm({
43376
45279
  "components/marketing/organisms/PricingOrganism.tsx"() {
@@ -43494,6 +45397,7 @@ var init_PricingPageTemplate = __esm({
43494
45397
  }
43495
45398
  });
43496
45399
  function ResourceCounter({
45400
+ assetUrl = DEFAULT_ASSET_URL12,
43497
45401
  icon,
43498
45402
  label = "Gold",
43499
45403
  value = 250,
@@ -43513,7 +45417,17 @@ function ResourceCounter({
43513
45417
  className
43514
45418
  ),
43515
45419
  children: [
43516
- icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon }) }),
45420
+ assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
45421
+ "img",
45422
+ {
45423
+ src: assetUrl,
45424
+ alt: label,
45425
+ width: sizes.img,
45426
+ height: sizes.img,
45427
+ style: { imageRendering: "pixelated", objectFit: "contain" },
45428
+ className: "flex-shrink-0"
45429
+ }
45430
+ ) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon }) }) : null,
43517
45431
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: label }),
43518
45432
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("font-bold tabular-nums", color && (color in colorTokenClasses2 ? colorTokenClasses2[color] : color)), children: [
43519
45433
  value,
@@ -43526,7 +45440,7 @@ function ResourceCounter({
43526
45440
  }
43527
45441
  );
43528
45442
  }
43529
- var colorTokenClasses2, sizeMap15;
45443
+ var colorTokenClasses2, DEFAULT_ASSET_URL12, sizeMap15;
43530
45444
  var init_ResourceCounter = __esm({
43531
45445
  "components/game/atoms/ResourceCounter.tsx"() {
43532
45446
  init_cn();
@@ -43539,14 +45453,365 @@ var init_ResourceCounter = __esm({
43539
45453
  error: "text-error",
43540
45454
  muted: "text-muted-foreground"
43541
45455
  };
45456
+ DEFAULT_ASSET_URL12 = "https://almadar-kflow-assets.web.app/shared/world-map/gold_mine.png";
43542
45457
  sizeMap15 = {
43543
- sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm" },
43544
- md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base" },
43545
- lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg" }
45458
+ sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm", img: 16 },
45459
+ md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base", img: 20 },
45460
+ lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg", img: 28 }
43546
45461
  };
43547
45462
  ResourceCounter.displayName = "ResourceCounter";
43548
45463
  }
43549
45464
  });
45465
+ function RoguelikeBoard({
45466
+ entity,
45467
+ tiles: propTiles,
45468
+ enemies: propEnemies,
45469
+ items: propItems,
45470
+ player: propPlayer,
45471
+ playerHp: propPlayerHp,
45472
+ playerMaxHp: propPlayerMaxHp,
45473
+ playerAttack: propPlayerAttack,
45474
+ depth: propDepth,
45475
+ result: propResult,
45476
+ phase: propPhase,
45477
+ stairsX: propStairsX,
45478
+ stairsY: propStairsY,
45479
+ assetManifest: propAssetManifest,
45480
+ scale = 0.45,
45481
+ unitScale = 1,
45482
+ moveEvent,
45483
+ playAgainEvent,
45484
+ gameEndEvent,
45485
+ className
45486
+ }) {
45487
+ const board = boardEntity(entity) ?? {};
45488
+ const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : DEFAULT_TILES2);
45489
+ const enemies = propEnemies ?? (Array.isArray(board.enemies) ? board.enemies : DEFAULT_ENEMIES);
45490
+ const items = propItems ?? (Array.isArray(board.items) ? board.items : DEFAULT_ITEMS);
45491
+ const player = propPlayer ?? board.player ?? { x: 1, y: 1 };
45492
+ const playerHp = propPlayerHp ?? num(board.playerHp, 10);
45493
+ const playerMaxHp = propPlayerMaxHp ?? num(board.playerMaxHp, 10);
45494
+ const playerAttack = propPlayerAttack ?? num(board.playerAttack, 3);
45495
+ const depth = propDepth ?? num(board.depth, 1);
45496
+ const result = propResult ?? (str(board.result) || "none");
45497
+ const phase = propPhase ?? (str(board.phase) || "player_turn");
45498
+ propStairsX ?? num(board.stairsX, 3);
45499
+ propStairsY ?? num(board.stairsY, 3);
45500
+ const eventBus = useEventBus();
45501
+ const { t } = hooks.useTranslate();
45502
+ const [hoveredTile, setHoveredTile] = React77.useState(null);
45503
+ const emitMove = React77.useCallback((dx, dy) => {
45504
+ if (!moveEvent) return;
45505
+ eventBus.emit(`UI:${moveEvent}`, { dx, dy });
45506
+ }, [moveEvent, eventBus]);
45507
+ React77.useEffect(() => {
45508
+ const handler = (e) => {
45509
+ if (result !== "none" || phase !== "player_turn") return;
45510
+ if (e.key === "ArrowUp" || e.key === "w") emitMove(0, -1);
45511
+ if (e.key === "ArrowDown" || e.key === "s") emitMove(0, 1);
45512
+ if (e.key === "ArrowLeft" || e.key === "a") emitMove(-1, 0);
45513
+ if (e.key === "ArrowRight" || e.key === "d") emitMove(1, 0);
45514
+ };
45515
+ window.addEventListener("keydown", handler);
45516
+ return () => window.removeEventListener("keydown", handler);
45517
+ }, [result, phase, emitMove]);
45518
+ React77.useEffect(() => {
45519
+ if ((result === "won" || result === "lost") && gameEndEvent) {
45520
+ eventBus.emit(`UI:${gameEndEvent}`, { result });
45521
+ }
45522
+ }, [result, gameEndEvent, eventBus]);
45523
+ const handleTileClick = React77.useCallback((x, y) => {
45524
+ if (result !== "none" || phase !== "player_turn") return;
45525
+ const dx = x - player.x;
45526
+ const dy = y - player.y;
45527
+ if (Math.abs(dx) + Math.abs(dy) === 1) {
45528
+ emitMove(dx, dy);
45529
+ }
45530
+ }, [result, phase, player, emitMove]);
45531
+ const handleReset = React77.useCallback(() => {
45532
+ if (!playAgainEvent) return;
45533
+ eventBus.emit(`UI:${playAgainEvent}`, {});
45534
+ }, [playAgainEvent, eventBus]);
45535
+ const isoUnits = React77.useMemo(() => {
45536
+ const liveEnemies = enemies.filter((e) => e.hp > 0);
45537
+ return [
45538
+ {
45539
+ id: "player",
45540
+ position: player,
45541
+ name: t("roguelike.player") || "Hero",
45542
+ team: "player",
45543
+ health: playerHp,
45544
+ maxHealth: playerMaxHp,
45545
+ unitType: "player",
45546
+ sprite: `${CDN4}/isometric-dungeon/Characters/Male/Male_0_Idle0.png`
45547
+ },
45548
+ ...liveEnemies.map((e) => ({
45549
+ id: e.id,
45550
+ position: { x: e.x, y: e.y },
45551
+ name: t("roguelike.enemy") || "Enemy",
45552
+ team: "enemy",
45553
+ health: e.hp,
45554
+ maxHealth: 6,
45555
+ unitType: "enemy",
45556
+ sprite: `${CDN4}/sprite-sheets/shadow-legion-sprite-sheet-sw.png`
45557
+ }))
45558
+ ];
45559
+ }, [player, enemies, playerHp, playerMaxHp, t]);
45560
+ const isoFeatures = React77.useMemo(
45561
+ () => items.map((it) => ({
45562
+ id: it.id,
45563
+ x: it.x,
45564
+ y: it.y,
45565
+ type: it.kind,
45566
+ sprite: FEATURE_SPRITE[it.kind] ?? FEATURE_SPRITE.health_potion
45567
+ })),
45568
+ [items]
45569
+ );
45570
+ const validMoves = React77.useMemo(() => {
45571
+ if (result !== "none" || phase !== "player_turn") return [];
45572
+ const dirs = [{ dx: 0, dy: -1 }, { dx: 0, dy: 1 }, { dx: -1, dy: 0 }, { dx: 1, dy: 0 }];
45573
+ return dirs.map(({ dx, dy }) => ({ x: player.x + dx, y: player.y + dy })).filter(({ x, y }) => {
45574
+ const tile = tiles.find((t2) => t2.x === x && t2.y === y);
45575
+ return tile?.passable ?? false;
45576
+ });
45577
+ }, [result, phase, player, tiles]);
45578
+ const isGameOver = result === "won" || result === "lost";
45579
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { className: cn("roguelike-board relative min-h-[600px] bg-background", className), gap: "none", children: [
45580
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { className: "items-center justify-between px-4 py-2 border-b border-border", gap: "md", children: [
45581
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", className: "items-center", children: [
45582
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.depth") || "Depth" }),
45583
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body1", className: "font-bold tabular-nums", children: depth })
45584
+ ] }),
45585
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", className: "items-center", children: [
45586
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.hp") || "HP" }),
45587
+ /* @__PURE__ */ jsxRuntime.jsxs(
45588
+ exports.Typography,
45589
+ {
45590
+ variant: "body1",
45591
+ className: cn(
45592
+ "font-bold tabular-nums",
45593
+ playerHp <= Math.floor(playerMaxHp * 0.25) ? "text-error" : "text-success"
45594
+ ),
45595
+ children: [
45596
+ playerHp,
45597
+ " / ",
45598
+ playerMaxHp
45599
+ ]
45600
+ }
45601
+ )
45602
+ ] }),
45603
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", className: "items-center", children: [
45604
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.atk") || "ATK" }),
45605
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body1", className: "font-bold tabular-nums text-foreground", children: playerAttack })
45606
+ ] }),
45607
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", className: "items-center", children: [
45608
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.phase") || "Phase" }),
45609
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "capitalize text-foreground", children: phase.replace("_", " ") })
45610
+ ] })
45611
+ ] }),
45612
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "relative flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
45613
+ IsometricCanvas_default,
45614
+ {
45615
+ tiles,
45616
+ units: isoUnits,
45617
+ features: isoFeatures,
45618
+ selectedUnitId: null,
45619
+ validMoves,
45620
+ attackTargets: [],
45621
+ hoveredTile,
45622
+ onTileClick: handleTileClick,
45623
+ onUnitClick: () => void 0,
45624
+ onTileHover: (x, y) => setHoveredTile({ x, y }),
45625
+ onTileLeave: () => setHoveredTile(null),
45626
+ scale,
45627
+ assetBaseUrl: propAssetManifest?.baseUrl ?? CDN4,
45628
+ assetManifest: propAssetManifest,
45629
+ unitScale
45630
+ }
45631
+ ) }),
45632
+ !isGameOver && /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { className: "justify-center gap-2 p-3", gap: "none", children: [
45633
+ { label: "\u2191", dx: 0, dy: -1 },
45634
+ { label: "\u2193", dx: 0, dy: 1 },
45635
+ { label: "\u2190", dx: -1, dy: 0 },
45636
+ { label: "\u2192", dx: 1, dy: 0 }
45637
+ ].map(({ label, dx, dy }) => /* @__PURE__ */ jsxRuntime.jsx(
45638
+ exports.Button,
45639
+ {
45640
+ variant: "secondary",
45641
+ className: "w-9 h-9 p-0 text-base font-mono",
45642
+ onClick: () => emitMove(dx, dy),
45643
+ disabled: phase !== "player_turn",
45644
+ children: label
45645
+ },
45646
+ label
45647
+ )) }),
45648
+ isGameOver && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/75 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { className: "text-center p-8", gap: "lg", children: [
45649
+ /* @__PURE__ */ jsxRuntime.jsx(
45650
+ exports.Typography,
45651
+ {
45652
+ variant: "h2",
45653
+ className: cn(
45654
+ "text-4xl font-black tracking-widest uppercase",
45655
+ result === "won" ? "text-warning" : "text-error"
45656
+ ),
45657
+ children: result === "won" ? t("roguelike.victory") || "Victory!" : t("roguelike.defeat") || "Defeated!"
45658
+ }
45659
+ ),
45660
+ result === "won" && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body1", className: "text-muted-foreground", children: t("roguelike.cleared") || `Dungeon cleared! Depth reached: ${depth}` }),
45661
+ /* @__PURE__ */ jsxRuntime.jsx(
45662
+ exports.Button,
45663
+ {
45664
+ variant: "primary",
45665
+ className: "px-8 py-3 font-semibold",
45666
+ onClick: handleReset,
45667
+ children: t("roguelike.playAgain") || "Play Again"
45668
+ }
45669
+ )
45670
+ ] }) })
45671
+ ] });
45672
+ }
45673
+ var CDN4, DEFAULT_TILES2, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
45674
+ var init_RoguelikeBoard = __esm({
45675
+ "components/game/organisms/RoguelikeBoard.tsx"() {
45676
+ "use client";
45677
+ init_cn();
45678
+ init_useEventBus();
45679
+ init_Box();
45680
+ init_Button();
45681
+ init_Typography();
45682
+ init_Stack();
45683
+ init_IsometricCanvas();
45684
+ init_boardEntity();
45685
+ CDN4 = "https://almadar-kflow-assets.web.app/shared";
45686
+ DEFAULT_TILES2 = [
45687
+ { x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45688
+ { x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45689
+ { x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45690
+ { x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45691
+ { x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45692
+ { x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45693
+ { x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
45694
+ { x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
45695
+ { x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
45696
+ { x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45697
+ { x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45698
+ { x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45699
+ { x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45700
+ { x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
45701
+ { x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45702
+ { x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45703
+ { x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
45704
+ { x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45705
+ { x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` },
45706
+ { x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45707
+ { x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45708
+ { x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45709
+ { x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45710
+ { x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
45711
+ { x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` }
45712
+ ];
45713
+ DEFAULT_ENEMIES = [
45714
+ { id: "e1", x: 3, y: 1, hp: 5, attack: 2 },
45715
+ { id: "e2", x: 1, y: 3, hp: 4, attack: 1 }
45716
+ ];
45717
+ DEFAULT_ITEMS = [
45718
+ { id: "i1", x: 2, y: 2, kind: "health_potion" }
45719
+ ];
45720
+ FEATURE_SPRITE = {
45721
+ health_potion: `${CDN4}/isometric-dungeon/Isometric/chestClosed_E.png`,
45722
+ sword: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`,
45723
+ shield: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`
45724
+ };
45725
+ RoguelikeBoard.displayName = "RoguelikeBoard";
45726
+ }
45727
+ });
45728
+ function RoguelikeTemplate({
45729
+ entity,
45730
+ scale = 0.45,
45731
+ unitScale = 1,
45732
+ tiles = DEFAULT_ROGUELIKE_TILES,
45733
+ enemies,
45734
+ items,
45735
+ stairsX,
45736
+ stairsY,
45737
+ assetManifest = DEFAULT_ROGUELIKE_MANIFEST,
45738
+ gameEndEvent,
45739
+ className
45740
+ }) {
45741
+ const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
45742
+ if (!resolved && !tiles && !assetManifest) return null;
45743
+ return /* @__PURE__ */ jsxRuntime.jsx(
45744
+ RoguelikeBoard,
45745
+ {
45746
+ entity: resolved,
45747
+ tiles,
45748
+ enemies,
45749
+ items,
45750
+ stairsX,
45751
+ stairsY,
45752
+ assetManifest,
45753
+ scale,
45754
+ unitScale,
45755
+ moveEvent: "MOVE",
45756
+ playAgainEvent: "PLAY_AGAIN",
45757
+ gameEndEvent: gameEndEvent ?? "GAME_END",
45758
+ className
45759
+ }
45760
+ );
45761
+ }
45762
+ var CDN5, DEFAULT_ROGUELIKE_TILES, DEFAULT_ROGUELIKE_MANIFEST;
45763
+ var init_RoguelikeTemplate = __esm({
45764
+ "components/game/templates/RoguelikeTemplate.tsx"() {
45765
+ init_RoguelikeBoard();
45766
+ CDN5 = "https://almadar-kflow-assets.web.app/shared";
45767
+ DEFAULT_ROGUELIKE_TILES = [
45768
+ { x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45769
+ { x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45770
+ { x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45771
+ { x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45772
+ { x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45773
+ { x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45774
+ { x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
45775
+ { x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
45776
+ { x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
45777
+ { x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45778
+ { x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45779
+ { x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45780
+ { x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45781
+ { x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
45782
+ { x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45783
+ { x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45784
+ { x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
45785
+ { x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
45786
+ { x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stairs_E.png` },
45787
+ { x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45788
+ { x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45789
+ { x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45790
+ { x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45791
+ { x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
45792
+ { x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` }
45793
+ ];
45794
+ DEFAULT_ROGUELIKE_MANIFEST = {
45795
+ baseUrl: CDN5,
45796
+ terrains: {
45797
+ floor: "/isometric-dungeon/Isometric/dirt_E.png",
45798
+ wall: "/isometric-dungeon/Isometric/stoneInset_E.png",
45799
+ stairs: "/isometric-dungeon/Isometric/stairs_E.png",
45800
+ planks: "/isometric-dungeon/Isometric/planks_E.png"
45801
+ },
45802
+ units: {
45803
+ player: "/isometric-dungeon/Characters/Male/Male_0_Idle0.png",
45804
+ enemy: "/sprite-sheets/shadow-legion-sprite-sheet-sw.png"
45805
+ },
45806
+ features: {
45807
+ health_potion: "/isometric-dungeon/Isometric/chestClosed_E.png",
45808
+ sword: "/isometric-dungeon/Isometric/barrel_E.png",
45809
+ shield: "/isometric-dungeon/Isometric/barrel_E.png"
45810
+ }
45811
+ };
45812
+ RoguelikeTemplate.displayName = "RoguelikeTemplate";
45813
+ }
45814
+ });
43550
45815
 
43551
45816
  // lib/traitRegistry.ts
43552
45817
  function getAllTraits() {
@@ -44781,7 +47046,7 @@ function getAllEvents(traits2) {
44781
47046
  function EventDispatcherTab({ traits: traits2, schema }) {
44782
47047
  const eventBus = useEventBus();
44783
47048
  const { t } = hooks.useTranslate();
44784
- const [log9, setLog] = React77__namespace.useState([]);
47049
+ const [log11, setLog] = React77__namespace.useState([]);
44785
47050
  const prevStatesRef = React77__namespace.useRef(/* @__PURE__ */ new Map());
44786
47051
  React77__namespace.useEffect(() => {
44787
47052
  for (const trait of traits2) {
@@ -44845,9 +47110,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
44845
47110
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.otherEvents") }),
44846
47111
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1", children: unavailableEvents.map((event) => /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: "default", size: "sm", className: "opacity-50", children: event }, event)) })
44847
47112
  ] }),
44848
- log9.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
47113
+ log11.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
44849
47114
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.recentTransitions") }),
44850
- /* @__PURE__ */ jsxRuntime.jsx(exports.Stack, { gap: "xs", children: log9.map((entry, i) => /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "small", className: "font-mono text-xs", children: [
47115
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Stack, { gap: "xs", children: log11.map((entry, i) => /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "small", className: "font-mono text-xs", children: [
44851
47116
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-purple-400", children: entry.traitName }),
44852
47117
  " ",
44853
47118
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-500", children: entry.from }),
@@ -45650,17 +47915,24 @@ function SequencerBoard({
45650
47915
  const { emit } = useEventBus();
45651
47916
  const { t } = hooks.useTranslate();
45652
47917
  const resolved = boardEntity(entity);
45653
- const maxSlots = num(resolved?.maxSlots);
47918
+ const maxSlots = num(resolved?.maxSlots) || 3;
45654
47919
  const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
45655
47920
  const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
45656
47921
  const allowDuplicates = resolved?.allowDuplicates !== false;
47922
+ const entitySlots = Array.isArray(resolved?.slots) ? resolved.slots : [];
47923
+ const entityResult = str(resolved?.result);
47924
+ const entityAttempts = num(resolved?.attempts);
47925
+ const entityCurrentStep = typeof resolved?.currentStep === "number" ? resolved.currentStep : -1;
47926
+ const slots = Array.from({ length: maxSlots }, (_, i) => {
47927
+ const entitySlot = entitySlots.find((s) => s.index === i);
47928
+ if (!entitySlot?.placedActionId) return void 0;
47929
+ return availableActions.find((a) => a.id === entitySlot.placedActionId);
47930
+ });
47931
+ const isSuccess = entityResult === "win";
47932
+ const attempts = entityAttempts;
47933
+ const isPlayingBack = entityCurrentStep >= 0 && !isSuccess;
47934
+ const currentStep = entityCurrentStep;
45657
47935
  const [headerError, setHeaderError] = React77.useState(false);
45658
- const [slots, setSlots] = React77.useState(
45659
- () => Array.from({ length: maxSlots }, () => void 0)
45660
- );
45661
- const [playState, setPlayState] = React77.useState("idle");
45662
- const [currentStep, setCurrentStep] = React77.useState(-1);
45663
- const [attempts, setAttempts] = React77.useState(0);
45664
47936
  const [slotFeedback, setSlotFeedback] = React77.useState(
45665
47937
  () => Array.from({ length: maxSlots }, () => null)
45666
47938
  );
@@ -45669,11 +47941,6 @@ function SequencerBoard({
45669
47941
  if (timerRef.current) clearTimeout(timerRef.current);
45670
47942
  }, []);
45671
47943
  const handleSlotDrop = React77.useCallback((index, item) => {
45672
- setSlots((prev) => {
45673
- const next = [...prev];
45674
- next[index] = item;
45675
- return next;
45676
- });
45677
47944
  setSlotFeedback((prev) => {
45678
47945
  const next = [...prev];
45679
47946
  next[index] = null;
@@ -45683,11 +47950,6 @@ function SequencerBoard({
45683
47950
  if (placeEvent) emit(`UI:${placeEvent}`, { slotIndex: index, actionId: item.id });
45684
47951
  }, [emit, placeEvent]);
45685
47952
  const handleSlotRemove = React77.useCallback((index) => {
45686
- setSlots((prev) => {
45687
- const next = [...prev];
45688
- next[index] = void 0;
45689
- return next;
45690
- });
45691
47953
  setSlotFeedback((prev) => {
45692
47954
  const next = [...prev];
45693
47955
  next[index] = null;
@@ -45698,48 +47960,34 @@ function SequencerBoard({
45698
47960
  }, [emit, removeEvent]);
45699
47961
  const handleReset = React77.useCallback(() => {
45700
47962
  if (timerRef.current) clearTimeout(timerRef.current);
45701
- setSlots(Array.from({ length: maxSlots }, () => void 0));
45702
- setPlayState("idle");
45703
- setCurrentStep(-1);
45704
- setAttempts(0);
45705
47963
  setSlotFeedback(Array.from({ length: maxSlots }, () => null));
45706
47964
  if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
45707
47965
  }, [maxSlots, playAgainEvent, emit]);
45708
47966
  const filledSlots = slots.filter((s) => !!s);
45709
- const canPlay = filledSlots.length > 0 && playState === "idle";
47967
+ const canPlay = filledSlots.length > 0 && !isPlayingBack && !isSuccess;
45710
47968
  const handlePlay = React77.useCallback(() => {
45711
47969
  if (!canPlay) return;
45712
47970
  setSlotFeedback(Array.from({ length: maxSlots }, () => null));
45713
47971
  emit("UI:PLAY_SOUND", { key: "confirm" });
45714
47972
  const sequence = slots.map((s) => s?.id || "");
45715
- if (playEvent) {
45716
- emit(`UI:${playEvent}`, { sequence });
45717
- }
45718
- setPlayState("playing");
45719
- setCurrentStep(0);
47973
+ const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
47974
+ if (playEvent) emit(`UI:${playEvent}`, { sequence });
45720
47975
  let step = 0;
45721
47976
  const advance = () => {
45722
47977
  step++;
47978
+ emit("UI:STEP", { step });
45723
47979
  if (step >= maxSlots) {
47980
+ if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
45724
47981
  const playerSeq = slots.map((s) => s?.id);
45725
- const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
45726
47982
  const success = solutions.some(
45727
47983
  (sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
45728
47984
  );
45729
- if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
45730
47985
  if (success) {
45731
- setPlayState("success");
45732
- setCurrentStep(-1);
45733
47986
  emit("UI:PLAY_SOUND", { key: "levelComplete" });
45734
- if (completeEvent) {
45735
- emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
45736
- }
47987
+ if (completeEvent) emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
45737
47988
  } else {
45738
- setAttempts((prev) => prev + 1);
45739
47989
  const feedback = computeSlotFeedback(playerSeq, solutions);
45740
47990
  setSlotFeedback(feedback);
45741
- setPlayState("idle");
45742
- setCurrentStep(-1);
45743
47991
  emit("UI:PLAY_SOUND", { key: "fail" });
45744
47992
  const correctCount2 = feedback.filter((f3) => f3 === "correct").length;
45745
47993
  for (let ci = 0; ci < correctCount2; ci++) {
@@ -45749,7 +47997,6 @@ function SequencerBoard({
45749
47997
  }
45750
47998
  }
45751
47999
  } else {
45752
- setCurrentStep(step);
45753
48000
  timerRef.current = setTimeout(advance, stepDurationMs);
45754
48001
  }
45755
48002
  };
@@ -45799,7 +48046,7 @@ function SequencerBoard({
45799
48046
  /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", children: [
45800
48047
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { className: "items-center justify-between", children: [
45801
48048
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
45802
- hasFeedback && playState === "idle" && /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: [
48049
+ hasFeedback && !isPlayingBack && !isSuccess && /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: [
45803
48050
  `${correctCount}/${maxSlots} `,
45804
48051
  "\u2705"
45805
48052
  ] })
@@ -45811,7 +48058,7 @@ function SequencerBoard({
45811
48058
  maxSlots,
45812
48059
  onSlotDrop: handleSlotDrop,
45813
48060
  onSlotRemove: handleSlotRemove,
45814
- playing: playState === "playing",
48061
+ playing: isPlayingBack,
45815
48062
  currentStep,
45816
48063
  categoryColors,
45817
48064
  slotFeedback,
@@ -45819,7 +48066,7 @@ function SequencerBoard({
45819
48066
  }
45820
48067
  )
45821
48068
  ] }),
45822
- playState !== "playing" && /* @__PURE__ */ jsxRuntime.jsx(
48069
+ !isPlayingBack && /* @__PURE__ */ jsxRuntime.jsx(
45823
48070
  ActionPalette,
45824
48071
  {
45825
48072
  actions: availableActions,
@@ -45829,8 +48076,8 @@ function SequencerBoard({
45829
48076
  label: t("sequencer.dragActions")
45830
48077
  }
45831
48078
  ),
45832
- hasFeedback && playState === "idle" && attempts > 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-3 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-foreground", children: t(encourageKey) }) }),
45833
- playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("sequencer.levelComplete") }) }),
48079
+ hasFeedback && !isPlayingBack && !isSuccess && attempts > 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-3 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-foreground", children: t(encourageKey) }) }),
48080
+ isSuccess && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("sequencer.levelComplete") }) }),
45834
48081
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", children: [
45835
48082
  /* @__PURE__ */ jsxRuntime.jsx(
45836
48083
  exports.Button,
@@ -46897,9 +49144,9 @@ function VariablePanel({
46897
49144
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
46898
49145
  variables.map((v) => {
46899
49146
  const name = v.name == null ? "" : String(v.name);
46900
- const value = numField(v.value);
46901
- const max = numField(v.max, 100);
46902
- const min = numField(v.min, 0);
49147
+ const value = numField2(v.value);
49148
+ const max = numField2(v.max, 100);
49149
+ const min = numField2(v.min, 0);
46903
49150
  const unit = v.unit == null ? "" : String(v.unit);
46904
49151
  const pct = Math.round((value - min) / (max - min) * 100);
46905
49152
  const isHigh = pct > 80;
@@ -46929,12 +49176,12 @@ function VariablePanel({
46929
49176
  })
46930
49177
  ] });
46931
49178
  }
46932
- var numField;
49179
+ var numField2;
46933
49180
  var init_VariablePanel = __esm({
46934
49181
  "components/game/organisms/puzzles/state-architect/VariablePanel.tsx"() {
46935
49182
  init_atoms2();
46936
49183
  init_cn();
46937
- numField = (v, fallback = 0) => {
49184
+ numField2 = (v, fallback = 0) => {
46938
49185
  const n = Number(v);
46939
49186
  return Number.isFinite(n) ? n : fallback;
46940
49187
  };
@@ -46991,6 +49238,9 @@ function StateArchitectBoard({
46991
49238
  stepDurationMs = 600,
46992
49239
  testEvent,
46993
49240
  completeEvent,
49241
+ addTransitionEvent,
49242
+ removeTransitionEvent,
49243
+ playAgainEvent,
46994
49244
  className
46995
49245
  }) {
46996
49246
  const { emit } = useEventBus();
@@ -47003,14 +49253,16 @@ function StateArchitectBoard({
47003
49253
  const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
47004
49254
  const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
47005
49255
  const entityVariables = rows(resolved?.variables);
47006
- const [transitions, setTransitions] = React77.useState(entityTransitions);
49256
+ const transitions = entityTransitions;
49257
+ const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
49258
+ const entityResult = str(resolved?.result) || "none";
49259
+ const isSuccess = entityResult === "win";
49260
+ const [isTesting, setIsTesting] = React77.useState(false);
47007
49261
  const [headerError, setHeaderError] = React77.useState(false);
47008
- const [playState, setPlayState] = React77.useState("editing");
47009
49262
  const [currentState, setCurrentState] = React77.useState(initialState);
47010
49263
  const [selectedState, setSelectedState] = React77.useState(null);
47011
49264
  const [testResults, setTestResults] = React77.useState([]);
47012
49265
  const [variables, setVariables] = React77.useState(() => [...entityVariables]);
47013
- const [attempts, setAttempts] = React77.useState(0);
47014
49266
  const timerRef = React77.useRef(null);
47015
49267
  const [addingFrom, setAddingFrom] = React77.useState(null);
47016
49268
  React77.useEffect(() => () => {
@@ -47020,7 +49272,7 @@ function StateArchitectBoard({
47020
49272
  const GRAPH_H = 400;
47021
49273
  const positions = React77.useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
47022
49274
  const handleStateClick = React77.useCallback((state) => {
47023
- if (playState !== "editing") return;
49275
+ if (isTesting) return;
47024
49276
  if (addingFrom) {
47025
49277
  if (addingFrom !== state) {
47026
49278
  const event = availableEvents[0] || "EVENT";
@@ -47030,20 +49282,20 @@ function StateArchitectBoard({
47030
49282
  to: state,
47031
49283
  event
47032
49284
  };
47033
- setTransitions((prev) => [...prev, newTrans]);
49285
+ if (addTransitionEvent) emit(`UI:${addTransitionEvent}`, { id: newTrans.id, from: newTrans.from, to: newTrans.to, event: newTrans.event });
47034
49286
  }
47035
49287
  setAddingFrom(null);
47036
49288
  } else {
47037
49289
  setSelectedState(state);
47038
49290
  }
47039
- }, [playState, addingFrom, availableEvents]);
49291
+ }, [isTesting, addingFrom, availableEvents, addTransitionEvent, emit]);
47040
49292
  const handleStartAddTransition = React77.useCallback(() => {
47041
49293
  if (!selectedState) return;
47042
49294
  setAddingFrom(selectedState);
47043
49295
  }, [selectedState]);
47044
49296
  const handleRemoveTransition = React77.useCallback((transId) => {
47045
- setTransitions((prev) => prev.filter((t2) => t2.id !== transId));
47046
- }, []);
49297
+ if (removeTransitionEvent) emit(`UI:${removeTransitionEvent}`, { id: transId });
49298
+ }, [removeTransitionEvent, emit]);
47047
49299
  const machine = React77.useMemo(() => ({
47048
49300
  name: entityName,
47049
49301
  description: str(resolved?.description),
@@ -47057,16 +49309,16 @@ function StateArchitectBoard({
47057
49309
  }))
47058
49310
  }), [entityName, resolved, entityStates, currentState, transitions]);
47059
49311
  const handleTest = React77.useCallback(() => {
47060
- if (playState !== "editing") return;
49312
+ if (isTesting) return;
47061
49313
  if (testEvent) emit(`UI:${testEvent}`, {});
47062
- setPlayState("testing");
49314
+ setIsTesting(true);
47063
49315
  setTestResults([]);
47064
49316
  const results = [];
47065
49317
  let testIdx = 0;
47066
49318
  const runNextTest = () => {
47067
49319
  if (testIdx >= testCases.length) {
47068
49320
  const allPassed = results.every((r) => r.passed);
47069
- setPlayState(allPassed ? "success" : "fail");
49321
+ setIsTesting(false);
47070
49322
  setTestResults(results);
47071
49323
  if (allPassed && completeEvent) {
47072
49324
  emit(`UI:${completeEvent}`, {
@@ -47074,9 +49326,6 @@ function StateArchitectBoard({
47074
49326
  passedTests: results.filter((r) => r.passed).length
47075
49327
  });
47076
49328
  }
47077
- if (!allPassed) {
47078
- setAttempts((prev) => prev + 1);
47079
- }
47080
49329
  return;
47081
49330
  }
47082
49331
  const testCase = testCases[testIdx];
@@ -47099,24 +49348,23 @@ function StateArchitectBoard({
47099
49348
  timerRef.current = setTimeout(runNextTest, stepDurationMs);
47100
49349
  };
47101
49350
  timerRef.current = setTimeout(runNextTest, stepDurationMs);
47102
- }, [playState, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
49351
+ }, [isTesting, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
47103
49352
  const handleTryAgain = React77.useCallback(() => {
47104
49353
  if (timerRef.current) clearTimeout(timerRef.current);
47105
- setPlayState("editing");
49354
+ setIsTesting(false);
47106
49355
  setCurrentState(initialState);
47107
49356
  setTestResults([]);
47108
49357
  }, [initialState]);
47109
49358
  const handleReset = React77.useCallback(() => {
47110
49359
  if (timerRef.current) clearTimeout(timerRef.current);
47111
- setTransitions(entityTransitions);
47112
- setPlayState("editing");
49360
+ setIsTesting(false);
47113
49361
  setCurrentState(initialState);
47114
49362
  setTestResults([]);
47115
49363
  setVariables([...entityVariables]);
47116
49364
  setSelectedState(null);
47117
49365
  setAddingFrom(null);
47118
- setAttempts(0);
47119
- }, [entityTransitions, initialState, entityVariables]);
49366
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
49367
+ }, [initialState, entityVariables, playAgainEvent, emit]);
47120
49368
  const codeData = React77.useMemo(() => ({
47121
49369
  name: entityName,
47122
49370
  states: entityStates,
@@ -47212,7 +49460,7 @@ function StateArchitectBoard({
47212
49460
  ]
47213
49461
  }
47214
49462
  ),
47215
- playState === "editing" && /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "sm", children: /* @__PURE__ */ jsxRuntime.jsx(
49463
+ !isTesting && /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "sm", children: /* @__PURE__ */ jsxRuntime.jsx(
47216
49464
  exports.Button,
47217
49465
  {
47218
49466
  variant: "ghost",
@@ -47234,7 +49482,7 @@ function StateArchitectBoard({
47234
49482
  t2.guardHint,
47235
49483
  ")"
47236
49484
  ] }),
47237
- playState === "editing" && /* @__PURE__ */ jsxRuntime.jsx(
49485
+ !isTesting && /* @__PURE__ */ jsxRuntime.jsx(
47238
49486
  exports.Button,
47239
49487
  {
47240
49488
  variant: "ghost",
@@ -47266,21 +49514,21 @@ function StateArchitectBoard({
47266
49514
  resolved.showCodeView !== false && /* @__PURE__ */ jsxRuntime.jsx(StateJsonView, { data: codeData, label: "View Code" })
47267
49515
  ] })
47268
49516
  ] }),
47269
- playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("stateArchitect.allPassed") }) }),
47270
- playState === "fail" && /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", children: [
49517
+ isSuccess && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("stateArchitect.allPassed") }) }),
49518
+ !isTesting && !isSuccess && testResults.some((r) => !r.passed) && /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", children: [
47271
49519
  /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body1", className: "text-foreground font-medium", children: t(ENCOURAGEMENT_KEYS3[Math.min(attempts - 1, ENCOURAGEMENT_KEYS3.length - 1)] ?? ENCOURAGEMENT_KEYS3[0]) }) }),
47272
- attempts >= 3 && hint && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { className: "items-start", gap: "xs", children: [
49520
+ !isSuccess && attempts >= 2 && Boolean(str(resolved?.hint)) && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { className: "items-start", gap: "xs", children: [
47273
49521
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
47274
49522
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-foreground", children: hint })
47275
49523
  ] }) })
47276
49524
  ] }),
47277
49525
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", children: [
47278
- playState === "fail" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsxRuntime.jsx(
49526
+ !isTesting && !isSuccess && testResults.some((r) => !r.passed) ? /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsxRuntime.jsx(
47279
49527
  exports.Button,
47280
49528
  {
47281
49529
  variant: "primary",
47282
49530
  onClick: handleTest,
47283
- disabled: playState !== "editing",
49531
+ disabled: isTesting,
47284
49532
  children: "\u25B6 " + t("game.runTests")
47285
49533
  }
47286
49534
  ),
@@ -47360,6 +49608,7 @@ function formatDuration(seconds) {
47360
49608
  return `${Math.round(seconds)}s`;
47361
49609
  }
47362
49610
  function StatusEffect({
49611
+ assetUrl = DEFAULT_ASSET_URL13,
47363
49612
  icon = "shield",
47364
49613
  label = "Shield",
47365
49614
  duration = 30,
@@ -47380,7 +49629,17 @@ function StatusEffect({
47380
49629
  ),
47381
49630
  title: label,
47382
49631
  children: [
47383
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon, size: "sm" }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon, size: "sm" }) }),
49632
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
49633
+ "img",
49634
+ {
49635
+ src: assetUrl,
49636
+ alt: label,
49637
+ width: sizes.img,
49638
+ height: sizes.img,
49639
+ style: { imageRendering: "pixelated", objectFit: "contain" },
49640
+ className: "flex-shrink-0"
49641
+ }
49642
+ ) : typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon, size: "sm" }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon, size: "sm" }) }),
47384
49643
  duration !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
47385
49644
  "span",
47386
49645
  {
@@ -47407,15 +49666,16 @@ function StatusEffect({
47407
49666
  label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground mt-0.5 text-center whitespace-nowrap", children: label })
47408
49667
  ] });
47409
49668
  }
47410
- var sizeMap16, variantStyles9;
49669
+ var DEFAULT_ASSET_URL13, sizeMap16, variantStyles9;
47411
49670
  var init_StatusEffect = __esm({
47412
49671
  "components/game/atoms/StatusEffect.tsx"() {
47413
49672
  init_cn();
47414
49673
  init_Icon();
49674
+ DEFAULT_ASSET_URL13 = "https://almadar-kflow-assets.web.app/shared/effects/particles/flame_01.png";
47415
49675
  sizeMap16 = {
47416
- sm: { container: "w-8 h-8", icon: "text-sm", badge: "text-xs -top-1 -right-1 w-4 h-4", timer: "text-[9px]" },
47417
- md: { container: "w-10 h-10", icon: "text-base", badge: "text-xs -top-1 -right-1 w-5 h-5", timer: "text-xs" },
47418
- lg: { container: "w-12 h-12", icon: "text-lg", badge: "text-sm -top-1.5 -right-1.5 w-6 h-6", timer: "text-xs" }
49676
+ sm: { container: "w-8 h-8", icon: "text-sm", badge: "text-xs -top-1 -right-1 w-4 h-4", timer: "text-[9px]", img: 20 },
49677
+ md: { container: "w-10 h-10", icon: "text-base", badge: "text-xs -top-1 -right-1 w-5 h-5", timer: "text-xs", img: 28 },
49678
+ lg: { container: "w-12 h-12", icon: "text-lg", badge: "text-sm -top-1.5 -right-1.5 w-6 h-6", timer: "text-xs", img: 36 }
47419
49679
  };
47420
49680
  variantStyles9 = {
47421
49681
  buff: "border-success bg-success/20",
@@ -47908,6 +50168,289 @@ var init_ToastSlot = __esm({
47908
50168
  exports.ToastSlot.displayName = "ToastSlot";
47909
50169
  }
47910
50170
  });
50171
+ function tilesToIso(tiles) {
50172
+ return tiles.map((t) => ({
50173
+ x: t.x,
50174
+ y: t.y,
50175
+ terrain: t.terrain,
50176
+ terrainSprite: t.terrainSprite ?? TERRAIN_SPRITES[t.terrain ?? "ground"],
50177
+ passable: t.passable
50178
+ }));
50179
+ }
50180
+ function towersToUnits(towers) {
50181
+ return towers.map((t) => ({
50182
+ id: t.id,
50183
+ position: { x: t.x, y: t.y },
50184
+ name: "Tower",
50185
+ team: "player",
50186
+ sprite: TOWER_SPRITE,
50187
+ unitType: "guardian",
50188
+ health: 1,
50189
+ maxHealth: 1
50190
+ }));
50191
+ }
50192
+ function creepsToUnits(creeps) {
50193
+ return creeps.map((c) => ({
50194
+ id: c.id,
50195
+ position: { x: c.x, y: c.y },
50196
+ name: "Creep",
50197
+ team: "enemy",
50198
+ sprite: CREEP_SPRITE,
50199
+ unitType: "scrapper",
50200
+ health: c.hp,
50201
+ maxHealth: c.maxHp
50202
+ }));
50203
+ }
50204
+ function pathToFeatures(path) {
50205
+ return path.map((p2, i) => ({
50206
+ id: `path-${i}`,
50207
+ x: p2.x,
50208
+ y: p2.y,
50209
+ type: "path-marker",
50210
+ sprite: `${CDN6}world-map/road_straight.png`
50211
+ }));
50212
+ }
50213
+ function TowerDefenseBoard({
50214
+ entity,
50215
+ tiles: propTiles,
50216
+ path: propPath,
50217
+ towers: propTowers,
50218
+ creeps: propCreeps,
50219
+ gold: propGold,
50220
+ lives: propLives,
50221
+ wave: propWave,
50222
+ maxWaves: propMaxWaves,
50223
+ result: propResult,
50224
+ waveActive: propWaveActive,
50225
+ towerCost = 25,
50226
+ scale = 0.45,
50227
+ placeTowerEvent,
50228
+ startWaveEvent,
50229
+ playAgainEvent,
50230
+ gameEndEvent,
50231
+ className
50232
+ }) {
50233
+ const board = boardEntity(entity) ?? {};
50234
+ const eventBus = useEventBus();
50235
+ const { t } = hooks.useTranslate();
50236
+ const tiles = propTiles ?? rows(board.tiles);
50237
+ const path = propPath ?? rows(board.path);
50238
+ const towers = propTowers ?? rows(board.towers);
50239
+ const creeps = propCreeps ?? rows(board.creeps);
50240
+ const gold = propGold ?? num(board.gold, 100);
50241
+ const lives = propLives ?? num(board.lives, 20);
50242
+ const wave = propWave ?? num(board.wave, 1);
50243
+ const maxWaves = propMaxWaves ?? num(board.maxWaves, 5);
50244
+ const result = propResult ?? (str(board.result) || "none");
50245
+ const waveActive = propWaveActive ?? Boolean(board.waveActive);
50246
+ const [hoveredTile, setHoveredTile] = React77.useState(null);
50247
+ const emittedGameEnd = React77.useRef(false);
50248
+ if (result !== "none" && !emittedGameEnd.current) {
50249
+ emittedGameEnd.current = true;
50250
+ if (gameEndEvent) {
50251
+ eventBus.emit(`UI:${gameEndEvent}`, { result });
50252
+ }
50253
+ }
50254
+ if (result === "none") {
50255
+ emittedGameEnd.current = false;
50256
+ }
50257
+ const towerPositions = React77.useMemo(() => new Set(towers.map((t2) => `${t2.x},${t2.y}`)), [towers]);
50258
+ const pathPositions = React77.useMemo(() => new Set(path.map((p2) => `${p2.x},${p2.y}`)), [path]);
50259
+ const validMoves = React77.useMemo(() => {
50260
+ if (result !== "none" || gold < towerCost) return [];
50261
+ return tiles.filter(
50262
+ (t2) => t2.passable !== false && !towerPositions.has(`${t2.x},${t2.y}`) && !pathPositions.has(`${t2.x},${t2.y}`)
50263
+ ).map((t2) => ({ x: t2.x, y: t2.y }));
50264
+ }, [tiles, towerPositions, pathPositions, result, gold, towerCost]);
50265
+ const isoTiles = React77.useMemo(() => tilesToIso(tiles), [tiles]);
50266
+ const towerUnits = React77.useMemo(() => towersToUnits(towers), [towers]);
50267
+ const creepUnits = React77.useMemo(() => creepsToUnits(creeps), [creeps]);
50268
+ const isoUnits = React77.useMemo(() => [...towerUnits, ...creepUnits], [towerUnits, creepUnits]);
50269
+ const pathFeatures = React77.useMemo(() => pathToFeatures(path), [path]);
50270
+ const handleTileClick = React77.useCallback((x, y) => {
50271
+ if (result !== "none") return;
50272
+ if (pathPositions.has(`${x},${y}`)) return;
50273
+ if (towerPositions.has(`${x},${y}`)) return;
50274
+ if (gold < towerCost) return;
50275
+ if (placeTowerEvent) {
50276
+ eventBus.emit(`UI:${placeTowerEvent}`, { x, y });
50277
+ }
50278
+ }, [result, pathPositions, towerPositions, gold, towerCost, placeTowerEvent, eventBus]);
50279
+ const handleStartWave = React77.useCallback(() => {
50280
+ if (startWaveEvent) {
50281
+ eventBus.emit(`UI:${startWaveEvent}`, { wave });
50282
+ }
50283
+ }, [startWaveEvent, wave, eventBus]);
50284
+ const handlePlayAgain = React77.useCallback(() => {
50285
+ if (playAgainEvent) {
50286
+ eventBus.emit(`UI:${playAgainEvent}`, {});
50287
+ }
50288
+ }, [playAgainEvent, eventBus]);
50289
+ const canPlaceTower = gold >= towerCost && result === "none";
50290
+ const canStartWave = !waveActive && result === "none";
50291
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { className: cn("tower-defense-board relative min-h-[600px] bg-background", className), gap: "none", children: [
50292
+ /* @__PURE__ */ jsxRuntime.jsxs(
50293
+ exports.HStack,
50294
+ {
50295
+ className: "px-4 py-2 border-b border-border bg-surface",
50296
+ gap: "lg",
50297
+ align: "center",
50298
+ children: [
50299
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", children: [
50300
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "muted", children: t("td.gold") ?? "Gold" }),
50301
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", weight: "bold", className: "text-warning", children: gold })
50302
+ ] }),
50303
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", children: [
50304
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "muted", children: t("td.lives") ?? "Lives" }),
50305
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", weight: "bold", className: lives <= 5 ? "text-error" : "text-success", children: lives })
50306
+ ] }),
50307
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", children: [
50308
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "muted", children: t("td.wave") ?? "Wave" }),
50309
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "body2", weight: "bold", children: [
50310
+ wave,
50311
+ " / ",
50312
+ maxWaves
50313
+ ] })
50314
+ ] }),
50315
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "ml-auto", children: [
50316
+ canStartWave && /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", size: "sm", onClick: handleStartWave, children: t("td.startWave") ?? "Start Wave" }),
50317
+ waveActive && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "text-warning animate-pulse", children: t("td.waveInProgress") ?? "Wave in progress\u2026" })
50318
+ ] })
50319
+ ]
50320
+ }
50321
+ ),
50322
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "relative flex-1", children: [
50323
+ /* @__PURE__ */ jsxRuntime.jsx(
50324
+ IsometricCanvas_default,
50325
+ {
50326
+ tiles: isoTiles,
50327
+ units: isoUnits,
50328
+ features: pathFeatures,
50329
+ validMoves,
50330
+ hoveredTile,
50331
+ onTileClick: handleTileClick,
50332
+ onTileHover: (x, y) => setHoveredTile({ x, y }),
50333
+ onTileLeave: () => setHoveredTile(null),
50334
+ scale
50335
+ }
50336
+ ),
50337
+ hoveredTile && canPlaceTower && validMoves.some((m) => m.x === hoveredTile.x && m.y === hoveredTile.y) && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "absolute bottom-4 left-1/2 -translate-x-1/2 z-10 bg-background/80 rounded px-3 py-1 backdrop-blur-sm pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", children: t("td.placeTower") ?? `Place tower (${towerCost} gold)` }) })
50338
+ ] }),
50339
+ result !== "none" && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { className: "text-center p-8", gap: "lg", children: [
50340
+ /* @__PURE__ */ jsxRuntime.jsx(
50341
+ exports.Typography,
50342
+ {
50343
+ variant: "h2",
50344
+ className: cn(
50345
+ "text-4xl font-black tracking-widest uppercase",
50346
+ result === "won" ? "text-warning" : "text-error"
50347
+ ),
50348
+ children: result === "won" ? t("td.victory") ?? "Victory!" : t("td.defeat") ?? "Defeat!"
50349
+ }
50350
+ ),
50351
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body1", className: "text-muted-foreground", children: result === "won" ? t("td.survivedAllWaves") ?? `Survived all ${maxWaves} waves!` : t("td.livesReachedZero") ?? "Your base was overrun." }),
50352
+ /* @__PURE__ */ jsxRuntime.jsx(
50353
+ exports.Button,
50354
+ {
50355
+ variant: "primary",
50356
+ className: "px-8 py-3 font-semibold",
50357
+ onClick: handlePlayAgain,
50358
+ children: t("td.playAgain") ?? "Play Again"
50359
+ }
50360
+ )
50361
+ ] }) })
50362
+ ] });
50363
+ }
50364
+ var CDN6, TERRAIN_SPRITES, TOWER_SPRITE, CREEP_SPRITE;
50365
+ var init_TowerDefenseBoard = __esm({
50366
+ "components/game/organisms/TowerDefenseBoard.tsx"() {
50367
+ "use client";
50368
+ init_cn();
50369
+ init_useEventBus();
50370
+ init_Box();
50371
+ init_Button();
50372
+ init_Typography();
50373
+ init_Stack();
50374
+ init_IsometricCanvas();
50375
+ init_boardEntity();
50376
+ CDN6 = "https://almadar-kflow-assets.web.app/shared/";
50377
+ TERRAIN_SPRITES = {
50378
+ ground: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_01.png`,
50379
+ path: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_09.png`,
50380
+ wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`
50381
+ };
50382
+ TOWER_SPRITE = `${CDN6}units/guardian.png`;
50383
+ CREEP_SPRITE = `${CDN6}units/scrapper.png`;
50384
+ TowerDefenseBoard.displayName = "TowerDefenseBoard";
50385
+ }
50386
+ });
50387
+ function TowerDefenseTemplate({
50388
+ entity,
50389
+ title = "Tower Defense",
50390
+ tiles,
50391
+ path,
50392
+ towers,
50393
+ gold,
50394
+ lives,
50395
+ maxWaves,
50396
+ towerCost,
50397
+ scale,
50398
+ placeTowerEvent,
50399
+ startWaveEvent,
50400
+ playAgainEvent,
50401
+ gameEndEvent,
50402
+ className
50403
+ }) {
50404
+ const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
50405
+ return /* @__PURE__ */ jsxRuntime.jsxs(
50406
+ exports.Box,
50407
+ {
50408
+ display: "flex",
50409
+ fullHeight: true,
50410
+ className: cn("tower-defense-template flex-col", className),
50411
+ children: [
50412
+ /* @__PURE__ */ jsxRuntime.jsx(
50413
+ exports.HStack,
50414
+ {
50415
+ gap: "sm",
50416
+ align: "center",
50417
+ className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
50418
+ children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h4", children: title })
50419
+ }
50420
+ ),
50421
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
50422
+ TowerDefenseBoard,
50423
+ {
50424
+ entity: resolved,
50425
+ tiles,
50426
+ path,
50427
+ towers,
50428
+ gold,
50429
+ lives,
50430
+ maxWaves,
50431
+ towerCost,
50432
+ scale,
50433
+ placeTowerEvent,
50434
+ startWaveEvent,
50435
+ playAgainEvent,
50436
+ gameEndEvent,
50437
+ className: "h-full"
50438
+ }
50439
+ ) })
50440
+ ]
50441
+ }
50442
+ );
50443
+ }
50444
+ var init_TowerDefenseTemplate = __esm({
50445
+ "components/game/templates/TowerDefenseTemplate.tsx"() {
50446
+ init_cn();
50447
+ init_Box();
50448
+ init_Stack();
50449
+ init_Typography();
50450
+ init_TowerDefenseBoard();
50451
+ TowerDefenseTemplate.displayName = "TowerDefenseTemplate";
50452
+ }
50453
+ });
47911
50454
  function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
47912
50455
  const eventBus = useEventBus();
47913
50456
  const {
@@ -48106,17 +50649,12 @@ var init_UncontrolledBattleBoard = __esm({
48106
50649
  }
48107
50650
  });
48108
50651
  function heroPosition(h) {
50652
+ if ("position" in h && h.position != null) return h.position;
48109
50653
  return vec2(h.position);
48110
50654
  }
48111
- function heroOwner(h) {
48112
- return str(h.owner);
48113
- }
48114
50655
  function heroMovement(h) {
48115
50656
  return num(h.movement);
48116
50657
  }
48117
- function hexPassable(h) {
48118
- return h.passable !== false;
48119
- }
48120
50658
  function defaultIsInRange(from, to, range) {
48121
50659
  return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
48122
50660
  }
@@ -48152,38 +50690,41 @@ function WorldMapBoard({
48152
50690
  }) {
48153
50691
  const eventBus = useEventBus();
48154
50692
  const resolved = boardEntity(entity);
48155
- const hexes = rows(resolved?.hexes);
48156
- const heroes = rows(resolved?.heroes);
50693
+ const entityUnits = rows(resolved?.units);
50694
+ const entityTiles = rows(resolved?.tiles);
48157
50695
  const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
48158
50696
  const selectedHeroId = resolved?.selectedHeroId ?? null;
48159
50697
  const assetManifest = propAssetManifest ?? resolved?.assetManifest;
48160
50698
  const backgroundImage = resolved?.backgroundImage;
48161
50699
  const [hoveredTile, setHoveredTile] = React77.useState(null);
48162
- const selectedHero = React77.useMemo(
48163
- () => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
48164
- [heroes, selectedHeroId]
48165
- );
48166
50700
  const derivedTiles = React77.useMemo(
48167
- () => hexes.map((hex) => ({
48168
- x: num(hex.x),
48169
- y: num(hex.y),
48170
- terrain: str(hex.terrain),
48171
- terrainSprite: hex.terrainSprite == null ? void 0 : str(hex.terrainSprite)
50701
+ () => entityTiles.map((t) => ({
50702
+ x: num(t.x),
50703
+ y: num(t.y),
50704
+ terrain: str(t.terrain),
50705
+ terrainSprite: t.terrainSprite == null ? void 0 : str(t.terrainSprite),
50706
+ passable: t.passable !== false
48172
50707
  })),
48173
- [hexes]
50708
+ [entityTiles]
48174
50709
  );
48175
50710
  const tiles = propTiles ?? derivedTiles;
48176
50711
  const baseUnits = React77.useMemo(
48177
- () => propUnits ?? heroes.map((hero) => ({
48178
- id: str(hero.id),
48179
- position: heroPosition(hero),
48180
- name: str(hero.name),
48181
- team: heroOwner(hero) === "enemy" ? "enemy" : "player",
48182
- health: 100,
48183
- maxHealth: 100,
48184
- sprite: hero.sprite == null ? void 0 : str(hero.sprite)
50712
+ () => propUnits ?? entityUnits.map((u) => ({
50713
+ id: str(u.id),
50714
+ position: heroPosition(u),
50715
+ name: str(u.name),
50716
+ // lolo uses `team` field (not `owner`)
50717
+ team: str(u.team) === "enemy" ? "enemy" : "player",
50718
+ health: num(u.health) || 100,
50719
+ maxHealth: num(u.maxHealth) || 100,
50720
+ sprite: u.sprite == null ? void 0 : str(u.sprite)
48185
50721
  })),
48186
- [heroes, propUnits]
50722
+ [entityUnits, propUnits]
50723
+ );
50724
+ const gameUnits = baseUnits;
50725
+ const selectedHero = React77.useMemo(
50726
+ () => gameUnits.find((u) => u.id === selectedHeroId) ?? null,
50727
+ [gameUnits, selectedHeroId]
48187
50728
  );
48188
50729
  const MOVE_SPEED_MS_PER_TILE = 300;
48189
50730
  const movementAnimRef = React77.useRef(null);
@@ -48229,51 +50770,50 @@ function WorldMapBoard({
48229
50770
  const validMoves = React77.useMemo(() => {
48230
50771
  if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
48231
50772
  const sp = heroPosition(selectedHero);
48232
- const sOwner = heroOwner(selectedHero);
50773
+ const sTeam = str(selectedHero.team);
48233
50774
  const range = heroMovement(selectedHero);
48234
50775
  const moves = [];
48235
- hexes.forEach((hex) => {
48236
- const hx = num(hex.x);
48237
- const hy = num(hex.y);
48238
- if (!hexPassable(hex)) return;
48239
- if (hx === sp.x && hy === sp.y) return;
48240
- if (!isInRange(sp, { x: hx, y: hy }, range)) return;
48241
- if (heroes.some((h) => {
48242
- const hp = heroPosition(h);
48243
- return hp.x === hx && hp.y === hy && heroOwner(h) === sOwner;
50776
+ tiles.forEach((t) => {
50777
+ const tx = t.x;
50778
+ const ty = t.y;
50779
+ if (t.passable === false) return;
50780
+ if (tx === sp.x && ty === sp.y) return;
50781
+ if (!isInRange(sp, { x: tx, y: ty }, range)) return;
50782
+ if (gameUnits.some((u) => {
50783
+ const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
50784
+ return up.x === tx && up.y === ty && str(u.team) === sTeam;
48244
50785
  })) return;
48245
- moves.push({ x: hx, y: hy });
50786
+ moves.push({ x: tx, y: ty });
48246
50787
  });
48247
50788
  return moves;
48248
- }, [selectedHero, hexes, heroes, isInRange]);
50789
+ }, [selectedHero, tiles, gameUnits, isInRange]);
48249
50790
  const attackTargets = React77.useMemo(() => {
48250
50791
  if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
48251
50792
  const sp = heroPosition(selectedHero);
48252
- const sOwner = heroOwner(selectedHero);
50793
+ const sTeam = str(selectedHero.team);
48253
50794
  const range = heroMovement(selectedHero);
48254
- return heroes.filter((h) => heroOwner(h) !== sOwner).filter((h) => isInRange(sp, heroPosition(h), range)).map((h) => heroPosition(h));
48255
- }, [selectedHero, heroes, isInRange]);
48256
- const maxY = Math.max(...hexes.map((h) => num(h.y)), 0);
50795
+ return gameUnits.filter((u) => str(u.team) !== sTeam).filter((u) => isInRange(sp, u.position ?? { x: u.x ?? -1, y: u.y ?? -1 }, range)).map((u) => u.position ?? { x: u.x ?? -1, y: u.y ?? -1 });
50796
+ }, [selectedHero, gameUnits, isInRange]);
50797
+ const maxY = Math.max(...tiles.map((t) => t.y), 0);
48257
50798
  const baseOffsetX = (maxY + 1) * (exports.TILE_WIDTH * scale / 2);
48258
50799
  const tileToScreen = React77.useCallback(
48259
50800
  (tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
48260
50801
  [scale, baseOffsetX]
48261
50802
  );
48262
50803
  const hoveredHex = React77.useMemo(
48263
- () => hoveredTile ? hexes.find((h) => num(h.x) === hoveredTile.x && num(h.y) === hoveredTile.y) ?? null : null,
48264
- [hoveredTile, hexes]
50804
+ () => hoveredTile ? tiles.find((t) => t.x === hoveredTile.x && t.y === hoveredTile.y) ?? null : null,
50805
+ [hoveredTile, tiles]
48265
50806
  );
48266
50807
  const hoveredHero = React77.useMemo(
48267
- () => hoveredTile ? heroes.find((h) => {
48268
- const hp = heroPosition(h);
48269
- return hp.x === hoveredTile.x && hp.y === hoveredTile.y;
50808
+ () => hoveredTile ? gameUnits.find((u) => {
50809
+ const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
50810
+ return up.x === hoveredTile.x && up.y === hoveredTile.y;
48270
50811
  }) ?? null : null,
48271
- [hoveredTile, heroes]
50812
+ [hoveredTile, gameUnits]
48272
50813
  );
48273
50814
  const handleTileClick = React77.useCallback((x, y) => {
48274
50815
  if (movementAnimRef.current) return;
48275
- const hex = hexes.find((h) => num(h.x) === x && num(h.y) === y);
48276
- if (!hex) return;
50816
+ const tile = tiles.find((t) => t.x === x && t.y === y);
48277
50817
  if (tileClickEvent) {
48278
50818
  eventBus.emit(`UI:${tileClickEvent}`, { x, y });
48279
50819
  }
@@ -48284,38 +50824,39 @@ function WorldMapBoard({
48284
50824
  if (heroMoveEvent) {
48285
50825
  eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
48286
50826
  }
48287
- const feature = str(hex.feature);
50827
+ const feature = tile ? str(tile.feature) : "";
48288
50828
  if (feature && feature !== "none") {
48289
- onFeatureEnter?.(heroId, hex);
50829
+ const tileRow = tile;
50830
+ onFeatureEnter?.(heroId, tileRow);
48290
50831
  if (featureEnterEvent) {
48291
- eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
50832
+ eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex: tileRow });
48292
50833
  }
48293
50834
  }
48294
50835
  });
48295
50836
  return;
48296
50837
  }
48297
- const enemy = heroes.find((h) => {
48298
- const hp = heroPosition(h);
48299
- return hp.x === x && hp.y === y && heroOwner(h) === "enemy";
50838
+ const enemy = gameUnits.find((u) => {
50839
+ const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
50840
+ return up.x === x && up.y === y && str(u.team) === "enemy";
48300
50841
  });
48301
50842
  if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
48302
50843
  const attackerId = str(selectedHero.id);
48303
- const defenderId = str(enemy.id);
50844
+ const defenderId = enemy.id;
48304
50845
  onBattleEncounter?.(attackerId, defenderId);
48305
50846
  if (battleEncounterEvent) {
48306
50847
  eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
48307
50848
  }
48308
50849
  }
48309
- }, [hexes, heroes, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
50850
+ }, [tiles, gameUnits, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
48310
50851
  const handleUnitClick = React77.useCallback((unitId) => {
48311
- const hero = heroes.find((h) => str(h.id) === unitId);
48312
- if (hero && (heroOwner(hero) === "player" || allowMoveAllHeroes)) {
50852
+ const unit = gameUnits.find((u) => u.id === unitId);
50853
+ if (unit && (str(unit.team) === "player" || allowMoveAllHeroes)) {
48313
50854
  onHeroSelect?.(unitId);
48314
50855
  if (heroSelectEvent) {
48315
50856
  eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
48316
50857
  }
48317
50858
  }
48318
- }, [heroes, onHeroSelect, allowMoveAllHeroes, eventBus, heroSelectEvent]);
50859
+ }, [gameUnits, onHeroSelect, allowMoveAllHeroes, eventBus, heroSelectEvent]);
48319
50860
  const selectHero = React77.useCallback((id) => {
48320
50861
  onHeroSelect?.(id);
48321
50862
  if (heroSelectEvent) {
@@ -48419,48 +50960,48 @@ function WorldMapTemplate({
48419
50960
  }
48420
50961
  );
48421
50962
  }
48422
- var CDN3, DEFAULT_WORLDMAP_TILES, DEFAULT_WORLDMAP_UNITS, DEFAULT_WORLDMAP_FEATURES, DEFAULT_WORLDMAP_MANIFEST;
50963
+ var CDN7, DEFAULT_WORLDMAP_TILES, DEFAULT_WORLDMAP_UNITS, DEFAULT_WORLDMAP_FEATURES, DEFAULT_WORLDMAP_MANIFEST;
48423
50964
  var init_WorldMapTemplate = __esm({
48424
50965
  "components/game/templates/WorldMapTemplate.tsx"() {
48425
50966
  init_WorldMapBoard();
48426
- CDN3 = "https://almadar-kflow-assets.web.app/shared";
50967
+ CDN7 = "https://almadar-kflow-assets.web.app/shared";
48427
50968
  DEFAULT_WORLDMAP_TILES = [
48428
- { x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
48429
- { x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
48430
- { x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
48431
- { x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
48432
- { x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
48433
- { x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
48434
- { x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
48435
- { x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
48436
- { x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
48437
- { x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
48438
- { x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
48439
- { x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
48440
- { x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
48441
- { x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
48442
- { x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
48443
- { x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
48444
- { x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
48445
- { x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
48446
- { x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
48447
- { x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
48448
- { x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
48449
- { x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
48450
- { x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
48451
- { x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
48452
- { x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` }
50969
+ { x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50970
+ { x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50971
+ { x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
50972
+ { x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50973
+ { x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50974
+ { x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50975
+ { x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50976
+ { x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50977
+ { x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50978
+ { x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
50979
+ { x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
50980
+ { x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50981
+ { x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
50982
+ { x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50983
+ { x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50984
+ { x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50985
+ { x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50986
+ { x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50987
+ { x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
50988
+ { x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50989
+ { x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50990
+ { x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
50991
+ { x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
50992
+ { x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
50993
+ { x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` }
48453
50994
  ];
48454
50995
  DEFAULT_WORLDMAP_UNITS = [
48455
- { id: "h1", position: { x: 1, y: 1 }, unitType: "hero", name: "Amir", team: "player", health: 10, maxHealth: 10, sprite: `${CDN3}/sprite-sheets/amir-sprite-sheet-se.png` },
48456
- { id: "h2", position: { x: 3, y: 3 }, unitType: "scout", name: "Archivist", team: "player", health: 10, maxHealth: 10, sprite: `${CDN3}/sprite-sheets/archivist-sprite-sheet-se.png` }
50996
+ { id: "h1", position: { x: 1, y: 1 }, unitType: "hero", name: "Amir", team: "player", health: 10, maxHealth: 10, sprite: `${CDN7}/sprite-sheets/amir-sprite-sheet-se.png` },
50997
+ { id: "h2", position: { x: 3, y: 3 }, unitType: "scout", name: "Archivist", team: "player", health: 10, maxHealth: 10, sprite: `${CDN7}/sprite-sheets/archivist-sprite-sheet-se.png` }
48457
50998
  ];
48458
50999
  DEFAULT_WORLDMAP_FEATURES = [
48459
- { id: "f1", x: 2, y: 2, type: "capital", sprite: `${CDN3}/scenes/world/capital.png` },
48460
- { id: "f2", x: 4, y: 2, type: "power_node", sprite: `${CDN3}/world-map/power_node.png` }
51000
+ { id: "f1", x: 2, y: 2, type: "capital", sprite: `${CDN7}/scenes/world/capital.png` },
51001
+ { id: "f2", x: 4, y: 2, type: "power_node", sprite: `${CDN7}/world-map/power_node.png` }
48461
51002
  ];
48462
51003
  DEFAULT_WORLDMAP_MANIFEST = {
48463
- baseUrl: CDN3,
51004
+ baseUrl: CDN7,
48464
51005
  terrains: {
48465
51006
  grass: "/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png",
48466
51007
  water: "/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png",
@@ -48589,7 +51130,7 @@ function lazyThree(name, loader) {
48589
51130
  ThreeWrapper.displayName = `Lazy(${name})`;
48590
51131
  return ThreeWrapper;
48591
51132
  }
48592
- var ThreeBoundary, FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
51133
+ var ThreeBoundary, FeatureRenderer, GameCanvas3D2, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
48593
51134
  var init_component_registry_generated = __esm({
48594
51135
  "components/core/organisms/component-registry.generated.ts"() {
48595
51136
  init_AboutPageTemplate();
@@ -48699,6 +51240,7 @@ var init_component_registry_generated = __esm({
48699
51240
  init_FormSectionHeader();
48700
51241
  init_GameAudioProvider();
48701
51242
  init_GameAudioToggle();
51243
+ init_GameBoard3D();
48702
51244
  init_GameCanvas2D();
48703
51245
  init_GameHud();
48704
51246
  init_GameMenu();
@@ -48756,7 +51298,9 @@ var init_component_registry_generated = __esm({
48756
51298
  init_PageHeader();
48757
51299
  init_Pagination();
48758
51300
  init_PatternTile();
51301
+ init_PlatformerBoard();
48759
51302
  init_PlatformerCanvas();
51303
+ init_PlatformerTemplate();
48760
51304
  init_Popover();
48761
51305
  init_PositionedCanvas();
48762
51306
  init_PowerupSlots();
@@ -48780,6 +51324,8 @@ var init_component_registry_generated = __esm({
48780
51324
  init_ResourceBar();
48781
51325
  init_ResourceCounter();
48782
51326
  init_RichBlockEditor();
51327
+ init_RoguelikeBoard();
51328
+ init_RoguelikeTemplate();
48783
51329
  init_RuntimeDebugger2();
48784
51330
  init_ScaledDiagram();
48785
51331
  init_ScoreBoard();
@@ -48852,6 +51398,8 @@ var init_component_registry_generated = __esm({
48852
51398
  init_Toast();
48853
51399
  init_ToastSlot();
48854
51400
  init_Tooltip();
51401
+ init_TowerDefenseBoard();
51402
+ init_TowerDefenseTemplate();
48855
51403
  init_TraitFrame();
48856
51404
  init_TraitSlot();
48857
51405
  init_TrendIndicator();
@@ -48896,7 +51444,7 @@ var init_component_registry_generated = __esm({
48896
51444
  }
48897
51445
  };
48898
51446
  FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
48899
- GameCanvas3D = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
51447
+ GameCanvas3D2 = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
48900
51448
  GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
48901
51449
  GameCanvas3DCastleTemplate = lazyThree("GameCanvas3DCastleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
48902
51450
  GameCanvas3DWorldMapTemplate = lazyThree("GameCanvas3DWorldMapTemplate", () => import('@almadar/ui/components/molecules/game/three'));
@@ -49015,8 +51563,9 @@ var init_component_registry_generated = __esm({
49015
51563
  "FormSectionHeader": exports.FormSectionHeader,
49016
51564
  "GameAudioProvider": GameAudioProvider,
49017
51565
  "GameAudioToggle": GameAudioToggle,
51566
+ "GameBoard3D": GameBoard3D,
49018
51567
  "GameCanvas2D": GameCanvas2D,
49019
- "GameCanvas3D": GameCanvas3D,
51568
+ "GameCanvas3D": GameCanvas3D2,
49020
51569
  "GameCanvas3DBattleTemplate": GameCanvas3DBattleTemplate,
49021
51570
  "GameCanvas3DCastleTemplate": GameCanvas3DCastleTemplate,
49022
51571
  "GameCanvas3DWorldMapTemplate": GameCanvas3DWorldMapTemplate,
@@ -49078,7 +51627,9 @@ var init_component_registry_generated = __esm({
49078
51627
  "PageHeader": exports.PageHeader,
49079
51628
  "Pagination": exports.Pagination,
49080
51629
  "PatternTile": exports.PatternTile,
51630
+ "PlatformerBoard": PlatformerBoard,
49081
51631
  "PlatformerCanvas": PlatformerCanvas,
51632
+ "PlatformerTemplate": PlatformerTemplate,
49082
51633
  "Popover": exports.Popover,
49083
51634
  "PositionedCanvas": exports.PositionedCanvas,
49084
51635
  "PowerupSlots": PowerupSlots,
@@ -49102,6 +51653,8 @@ var init_component_registry_generated = __esm({
49102
51653
  "ResourceBar": ResourceBar,
49103
51654
  "ResourceCounter": ResourceCounter,
49104
51655
  "RichBlockEditor": exports.RichBlockEditor,
51656
+ "RoguelikeBoard": RoguelikeBoard,
51657
+ "RoguelikeTemplate": RoguelikeTemplate,
49105
51658
  "RuntimeDebugger": RuntimeDebugger,
49106
51659
  "ScaledDiagram": exports.ScaledDiagram,
49107
51660
  "ScoreBoard": ScoreBoard,
@@ -49177,6 +51730,8 @@ var init_component_registry_generated = __esm({
49177
51730
  "Toast": exports.Toast,
49178
51731
  "ToastSlot": exports.ToastSlot,
49179
51732
  "Tooltip": exports.Tooltip,
51733
+ "TowerDefenseBoard": TowerDefenseBoard,
51734
+ "TowerDefenseTemplate": TowerDefenseTemplate,
49180
51735
  "TraitFrame": TraitFrame,
49181
51736
  "TraitSlot": TraitSlot,
49182
51737
  "TrendIndicator": exports.TrendIndicator,
@@ -50739,6 +53294,10 @@ init_UncontrolledBattleBoard();
50739
53294
  init_useBattleState();
50740
53295
  init_WorldMapBoard();
50741
53296
  init_CastleBoard();
53297
+ init_PlatformerBoard();
53298
+ init_TowerDefenseBoard();
53299
+ init_RoguelikeBoard();
53300
+ init_GameBoard3D();
50742
53301
  init_TraitStateViewer();
50743
53302
  init_TraitSlot();
50744
53303
 
@@ -51311,6 +53870,9 @@ init_GameShell();
51311
53870
  init_BattleTemplate();
51312
53871
  init_CastleTemplate();
51313
53872
  init_WorldMapTemplate();
53873
+ init_PlatformerTemplate();
53874
+ init_TowerDefenseTemplate();
53875
+ init_RoguelikeTemplate();
51314
53876
  init_LandingPageTemplate();
51315
53877
  init_PricingPageTemplate();
51316
53878
  init_FeatureDetailPageTemplate();
@@ -51361,6 +53923,7 @@ exports.EventLog = EventLog;
51361
53923
  exports.FEATURE_TYPES = FEATURE_TYPES;
51362
53924
  exports.GameAudioProvider = GameAudioProvider;
51363
53925
  exports.GameAudioToggle = GameAudioToggle;
53926
+ exports.GameBoard3D = GameBoard3D;
51364
53927
  exports.GameCanvas2D = GameCanvas2D;
51365
53928
  exports.GameHud = GameHud;
51366
53929
  exports.GameMenu = GameMenu;
@@ -51379,11 +53942,15 @@ exports.NotifyListener = NotifyListener;
51379
53942
  exports.ObjectRulePanel = ObjectRulePanel;
51380
53943
  exports.OrbitalStateMachineView = exports.StateMachineView;
51381
53944
  exports.PhysicsManager = PhysicsManager;
53945
+ exports.PlatformerBoard = PlatformerBoard;
51382
53946
  exports.PlatformerCanvas = PlatformerCanvas;
53947
+ exports.PlatformerTemplate = PlatformerTemplate;
51383
53948
  exports.PowerupSlots = PowerupSlots;
51384
53949
  exports.QuestTracker = QuestTracker;
51385
53950
  exports.ResourceBar = ResourceBar;
51386
53951
  exports.ResourceCounter = ResourceCounter;
53952
+ exports.RoguelikeBoard = RoguelikeBoard;
53953
+ exports.RoguelikeTemplate = RoguelikeTemplate;
51387
53954
  exports.RuleEditor = RuleEditor;
51388
53955
  exports.RuntimeDebugger = RuntimeDebugger;
51389
53956
  exports.SHEET_COLUMNS = SHEET_COLUMNS;
@@ -51411,6 +53978,8 @@ exports.TableView = TableView;
51411
53978
  exports.TerrainPalette = TerrainPalette;
51412
53979
  exports.TimeSlotCell = TimeSlotCell;
51413
53980
  exports.TimerDisplay = TimerDisplay;
53981
+ exports.TowerDefenseBoard = TowerDefenseBoard;
53982
+ exports.TowerDefenseTemplate = TowerDefenseTemplate;
51414
53983
  exports.TraitFrame = TraitFrame;
51415
53984
  exports.TraitSlot = TraitSlot;
51416
53985
  exports.TraitStateViewer = TraitStateViewer;