@almadar/ui 5.48.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 (35) hide show
  1. package/dist/avl/index.cjs +2681 -148
  2. package/dist/avl/index.css +504 -0
  3. package/dist/avl/index.js +2681 -149
  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/ControlButton.d.ts +4 -2
  7. package/dist/components/game/atoms/DamageNumber.d.ts +4 -1
  8. package/dist/components/game/atoms/ScoreDisplay.d.ts +4 -1
  9. package/dist/components/game/atoms/TurnIndicator.d.ts +4 -1
  10. package/dist/components/game/molecules/EnemyPlate.d.ts +4 -1
  11. package/dist/components/game/molecules/StatBadge.d.ts +4 -1
  12. package/dist/components/game/molecules/three/index.cjs +1371 -283
  13. package/dist/components/game/molecules/three/index.js +1372 -284
  14. package/dist/components/game/organisms/GameBoard3D.d.ts +62 -0
  15. package/dist/components/game/organisms/PlatformerBoard.d.ts +51 -0
  16. package/dist/components/game/organisms/RoguelikeBoard.d.ts +59 -0
  17. package/dist/components/game/organisms/TowerDefenseBoard.d.ts +64 -0
  18. package/dist/components/game/organisms/index.d.ts +4 -0
  19. package/dist/components/game/templates/GameCanvas3DBattleTemplate.d.ts +22 -35
  20. package/dist/components/game/templates/GameCanvas3DCastleTemplate.d.ts +23 -23
  21. package/dist/components/game/templates/GameCanvas3DWorldMapTemplate.d.ts +27 -29
  22. package/dist/components/game/templates/PlatformerTemplate.d.ts +26 -0
  23. package/dist/components/game/templates/RoguelikeTemplate.d.ts +23 -0
  24. package/dist/components/game/templates/TowerDefenseTemplate.d.ts +43 -0
  25. package/dist/components/index.cjs +2674 -127
  26. package/dist/components/index.css +504 -0
  27. package/dist/components/index.js +2668 -129
  28. package/dist/docs/index.css +504 -0
  29. package/dist/providers/index.cjs +2664 -131
  30. package/dist/providers/index.css +504 -0
  31. package/dist/providers/index.js +2664 -132
  32. package/dist/runtime/index.cjs +2665 -132
  33. package/dist/runtime/index.css +504 -0
  34. package/dist/runtime/index.js +2665 -133
  35. package/package.json +1 -1
@@ -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
  });
@@ -20216,7 +20244,7 @@ function getComboScale(combo) {
20216
20244
  return "";
20217
20245
  }
20218
20246
  function ComboCounter({
20219
- assetUrl = DEFAULT_ASSET_URL,
20247
+ assetUrl = DEFAULT_ASSET_URL3,
20220
20248
  combo = 5,
20221
20249
  multiplier,
20222
20250
  streak,
@@ -20261,11 +20289,11 @@ function ComboCounter({
20261
20289
  }
20262
20290
  );
20263
20291
  }
20264
- var DEFAULT_ASSET_URL, sizeMap4;
20292
+ var DEFAULT_ASSET_URL3, sizeMap4;
20265
20293
  var init_ComboCounter = __esm({
20266
20294
  "components/game/atoms/ComboCounter.tsx"() {
20267
20295
  init_cn();
20268
- DEFAULT_ASSET_URL = "https://almadar-kflow-assets.web.app/shared/effects/flash/flash00.png";
20296
+ DEFAULT_ASSET_URL3 = "https://almadar-kflow-assets.web.app/shared/effects/flash/flash00.png";
20269
20297
  sizeMap4 = {
20270
20298
  sm: { combo: "text-lg", label: "text-xs", multiplier: "text-xs" },
20271
20299
  md: { combo: "text-2xl", label: "text-xs", multiplier: "text-sm" },
@@ -20674,7 +20702,7 @@ var init_CounterTemplate = __esm({
20674
20702
  }
20675
20703
  });
20676
20704
  function ItemSlot({
20677
- assetUrl = DEFAULT_ASSET_URL2,
20705
+ assetUrl = DEFAULT_ASSET_URL4,
20678
20706
  icon = "sword",
20679
20707
  label = "Iron Sword",
20680
20708
  quantity,
@@ -20733,7 +20761,7 @@ function ItemSlot({
20733
20761
  }
20734
20762
  );
20735
20763
  }
20736
- var sizeMap5, rarityBorderMap, rarityGlowMap, DEFAULT_ASSET_URL2, assetSizeMap;
20764
+ var sizeMap5, rarityBorderMap, rarityGlowMap, DEFAULT_ASSET_URL4, assetSizeMap;
20737
20765
  var init_ItemSlot = __esm({
20738
20766
  "components/game/atoms/ItemSlot.tsx"() {
20739
20767
  "use client";
@@ -20758,7 +20786,7 @@ var init_ItemSlot = __esm({
20758
20786
  epic: "shadow-lg",
20759
20787
  legendary: "shadow-lg"
20760
20788
  };
20761
- DEFAULT_ASSET_URL2 = "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";
20762
20790
  assetSizeMap = {
20763
20791
  sm: 28,
20764
20792
  md: 40,
@@ -20937,6 +20965,7 @@ var init_DPad = __esm({
20937
20965
  }
20938
20966
  });
20939
20967
  function DamageNumber({
20968
+ assetUrl = DEFAULT_ASSET_URL5,
20940
20969
  value = 42,
20941
20970
  type = "damage",
20942
20971
  size = "md",
@@ -20945,22 +20974,35 @@ function DamageNumber({
20945
20974
  const displayText = type === "miss" ? "MISS" : type === "heal" ? `+${value}` : `${value}`;
20946
20975
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
20947
20976
  /* @__PURE__ */ jsxRuntime.jsx("style", { children: floatKeyframes }),
20948
- /* @__PURE__ */ jsxRuntime.jsx(
20977
+ /* @__PURE__ */ jsxRuntime.jsxs(
20949
20978
  "span",
20950
20979
  {
20951
20980
  className: cn(
20952
- "inline-block font-mono select-none pointer-events-none",
20981
+ "inline-flex items-center gap-0.5 font-mono select-none pointer-events-none",
20953
20982
  sizeMap7[size],
20954
20983
  typeStyles[type],
20955
20984
  className
20956
20985
  ),
20957
20986
  style: { animation: "damageFloat 1s ease-out forwards" },
20958
- 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
+ ]
20959
21001
  }
20960
21002
  )
20961
21003
  ] });
20962
21004
  }
20963
- var sizeMap7, typeStyles, floatKeyframes;
21005
+ var sizeMap7, typeStyles, floatKeyframes, DEFAULT_ASSET_URL5;
20964
21006
  var init_DamageNumber = __esm({
20965
21007
  "components/game/atoms/DamageNumber.tsx"() {
20966
21008
  init_cn();
@@ -20982,6 +21024,7 @@ var init_DamageNumber = __esm({
20982
21024
  100% { opacity: 0; transform: translateY(-32px) scale(0.8); }
20983
21025
  }
20984
21026
  `;
21027
+ DEFAULT_ASSET_URL5 = "https://almadar-kflow-assets.web.app/shared/effects/particles/spark_01.png";
20985
21028
  DamageNumber.displayName = "DamageNumber";
20986
21029
  }
20987
21030
  });
@@ -24802,7 +24845,7 @@ var init_Flex = __esm({
24802
24845
  basis,
24803
24846
  className,
24804
24847
  children,
24805
- as: Component = "div"
24848
+ as: Component2 = "div"
24806
24849
  }) => {
24807
24850
  const flexStyle = {};
24808
24851
  if (grow !== void 0 || shrink !== void 0 || basis !== void 0) {
@@ -24814,7 +24857,7 @@ var init_Flex = __esm({
24814
24857
  flexStyle.flexBasis = typeof basis === "number" ? `${basis}px` : basis;
24815
24858
  }
24816
24859
  }
24817
- const Comp = Component;
24860
+ const Comp = Component2;
24818
24861
  return /* @__PURE__ */ jsxRuntime.jsx(
24819
24862
  Comp,
24820
24863
  {
@@ -24934,11 +24977,11 @@ var init_Grid = __esm({
24934
24977
  className,
24935
24978
  style,
24936
24979
  children,
24937
- as: Component = "div"
24980
+ as: Component2 = "div"
24938
24981
  }) => {
24939
24982
  const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
24940
24983
  return React77__namespace.default.createElement(
24941
- Component,
24984
+ Component2,
24942
24985
  {
24943
24986
  className: cn(
24944
24987
  "grid",
@@ -27087,6 +27130,7 @@ var init_HealthBar = __esm({
27087
27130
  }
27088
27131
  });
27089
27132
  function ScoreDisplay({
27133
+ assetUrl = DEFAULT_ASSET_URL6,
27090
27134
  value,
27091
27135
  label,
27092
27136
  icon,
@@ -27133,14 +27177,24 @@ function ScoreDisplay({
27133
27177
  className
27134
27178
  ),
27135
27179
  children: [
27136
- 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,
27137
27191
  label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: label }),
27138
27192
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: formattedValue })
27139
27193
  ]
27140
27194
  }
27141
27195
  );
27142
27196
  }
27143
- var sizeMap10;
27197
+ var sizeMap10, DEFAULT_ASSET_URL6;
27144
27198
  var init_ScoreDisplay = __esm({
27145
27199
  "components/game/atoms/ScoreDisplay.tsx"() {
27146
27200
  "use client";
@@ -27152,10 +27206,12 @@ var init_ScoreDisplay = __esm({
27152
27206
  lg: "text-2xl",
27153
27207
  xl: "text-4xl"
27154
27208
  };
27209
+ DEFAULT_ASSET_URL6 = "https://almadar-kflow-assets.web.app/shared/effects/particles/star_01.png";
27155
27210
  ScoreDisplay.displayName = "ScoreDisplay";
27156
27211
  }
27157
27212
  });
27158
27213
  function StatBadge({
27214
+ assetUrl = DEFAULT_ASSET_URL7,
27159
27215
  label,
27160
27216
  value = 0,
27161
27217
  max,
@@ -27179,7 +27235,17 @@ function StatBadge({
27179
27235
  className
27180
27236
  ),
27181
27237
  children: [
27182
- 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,
27183
27249
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground font-medium", children: label }),
27184
27250
  format === "hearts" && max && /* @__PURE__ */ jsxRuntime.jsx(
27185
27251
  HealthBar,
@@ -27212,7 +27278,7 @@ function StatBadge({
27212
27278
  }
27213
27279
  );
27214
27280
  }
27215
- var sizeMap11, variantMap2;
27281
+ var sizeMap11, variantMap2, DEFAULT_ASSET_URL7;
27216
27282
  var init_StatBadge = __esm({
27217
27283
  "components/game/molecules/StatBadge.tsx"() {
27218
27284
  init_cn();
@@ -27231,6 +27297,7 @@ var init_StatBadge = __esm({
27231
27297
  warning: "bg-warning/15 border-warning/40 text-foreground",
27232
27298
  danger: "bg-error/15 border-error/40 text-foreground"
27233
27299
  };
27300
+ DEFAULT_ASSET_URL7 = "https://almadar-kflow-assets.web.app/shared/effects/particles/light_01.png";
27234
27301
  StatBadge.displayName = "StatBadge";
27235
27302
  }
27236
27303
  });
@@ -27314,7 +27381,7 @@ var init_InventoryGrid = __esm({
27314
27381
  }
27315
27382
  });
27316
27383
  function WaypointMarker({
27317
- assetUrl = DEFAULT_ASSET_URL3,
27384
+ assetUrl = DEFAULT_ASSET_URL8,
27318
27385
  label,
27319
27386
  icon,
27320
27387
  active = true,
@@ -27380,12 +27447,12 @@ function WaypointMarker({
27380
27447
  )
27381
27448
  ] });
27382
27449
  }
27383
- var DEFAULT_ASSET_URL3, sizeMap12, checkIcon;
27450
+ var DEFAULT_ASSET_URL8, sizeMap12, checkIcon;
27384
27451
  var init_WaypointMarker = __esm({
27385
27452
  "components/game/atoms/WaypointMarker.tsx"() {
27386
27453
  init_cn();
27387
27454
  init_Icon();
27388
- DEFAULT_ASSET_URL3 = "https://almadar-kflow-assets.web.app/shared/world-map/battle_marker.png";
27455
+ DEFAULT_ASSET_URL8 = "https://almadar-kflow-assets.web.app/shared/world-map/battle_marker.png";
27389
27456
  sizeMap12 = {
27390
27457
  sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1", img: 12 },
27391
27458
  md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5", img: 18 },
@@ -27903,6 +27970,7 @@ var init_ResourceBar = __esm({
27903
27970
  }
27904
27971
  });
27905
27972
  function TurnIndicator({
27973
+ assetUrl = DEFAULT_ASSET_URL9,
27906
27974
  currentTurn = 1,
27907
27975
  maxTurns,
27908
27976
  activeTeam,
@@ -27935,14 +28003,24 @@ function TurnIndicator({
27935
28003
  ] }),
27936
28004
  activeTeam && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
27937
28005
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "|" }),
27938
- /* @__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) }),
27939
28017
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-success", children: activeTeam })
27940
28018
  ] })
27941
28019
  ]
27942
28020
  }
27943
28021
  );
27944
28022
  }
27945
- var sizeMap14;
28023
+ var sizeMap14, DEFAULT_ASSET_URL9;
27946
28024
  var init_TurnIndicator = __esm({
27947
28025
  "components/game/atoms/TurnIndicator.tsx"() {
27948
28026
  init_cn();
@@ -27951,6 +28029,7 @@ var init_TurnIndicator = __esm({
27951
28029
  md: { wrapper: "text-sm gap-2 px-3 py-1", dot: "w-2 h-2" },
27952
28030
  lg: { wrapper: "text-base gap-2.5 px-4 py-1.5", dot: "w-2.5 h-2.5" }
27953
28031
  };
28032
+ DEFAULT_ASSET_URL9 = "https://almadar-kflow-assets.web.app/shared/effects/particles/symbol_01.png";
27954
28033
  TurnIndicator.displayName = "TurnIndicator";
27955
28034
  }
27956
28035
  });
@@ -28021,6 +28100,7 @@ var init_TurnPanel = __esm({
28021
28100
  }
28022
28101
  });
28023
28102
  function EnemyPlate({
28103
+ assetUrl = DEFAULT_ASSET_URL10,
28024
28104
  name = "Shadow Guard",
28025
28105
  health = 80,
28026
28106
  maxHealth = 100,
@@ -28038,15 +28118,28 @@ function EnemyPlate({
28038
28118
  ),
28039
28119
  children: [
28040
28120
  /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "flex items-center justify-between gap-2", children: [
28041
- /* @__PURE__ */ jsxRuntime.jsx(
28042
- exports.Typography,
28043
- {
28044
- variant: "caption",
28045
- weight: "bold",
28046
- className: "text-[var(--color-foreground)] truncate",
28047
- children: name
28048
- }
28049
- ),
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
+ ] }),
28050
28143
  level != null && /* @__PURE__ */ jsxRuntime.jsxs(exports.Badge, { variant: "neutral", size: "sm", children: [
28051
28144
  "Lv.",
28052
28145
  level
@@ -28091,7 +28184,7 @@ function EnemyPlate({
28091
28184
  }
28092
28185
  );
28093
28186
  }
28094
- var effectVariantMap2, DEFAULT_ENEMY_EFFECTS;
28187
+ var effectVariantMap2, DEFAULT_ENEMY_EFFECTS, DEFAULT_ASSET_URL10;
28095
28188
  var init_EnemyPlate = __esm({
28096
28189
  "components/game/molecules/EnemyPlate.tsx"() {
28097
28190
  "use client";
@@ -28108,6 +28201,7 @@ var init_EnemyPlate = __esm({
28108
28201
  DEFAULT_ENEMY_EFFECTS = [
28109
28202
  { icon: "flame", label: "Burn", variant: "debuff" }
28110
28203
  ];
28204
+ DEFAULT_ASSET_URL10 = "https://almadar-kflow-assets.web.app/shared/portraits/shadow-legion.png";
28111
28205
  EnemyPlate.displayName = "EnemyPlate";
28112
28206
  }
28113
28207
  });
@@ -29661,13 +29755,13 @@ var init_MapView = __esm({
29661
29755
  shadowSize: [41, 41]
29662
29756
  });
29663
29757
  L.Marker.prototype.options.icon = defaultIcon;
29664
- const { useEffect: useEffect74, useRef: useRef71, useCallback: useCallback113, useState: useState105 } = React77__namespace.default;
29758
+ const { useEffect: useEffect80, useRef: useRef77, useCallback: useCallback120, useState: useState110 } = React77__namespace.default;
29665
29759
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
29666
- const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
29760
+ const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
29667
29761
  function MapUpdater({ centerLat, centerLng, zoom }) {
29668
29762
  const map = useMap();
29669
- const prevRef = useRef71({ centerLat, centerLng, zoom });
29670
- useEffect74(() => {
29763
+ const prevRef = useRef77({ centerLat, centerLng, zoom });
29764
+ useEffect80(() => {
29671
29765
  const prev = prevRef.current;
29672
29766
  if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
29673
29767
  map.setView([centerLat, centerLng], zoom);
@@ -29678,7 +29772,7 @@ var init_MapView = __esm({
29678
29772
  }
29679
29773
  function MapClickHandler({ onMapClick }) {
29680
29774
  const map = useMap();
29681
- useEffect74(() => {
29775
+ useEffect80(() => {
29682
29776
  if (!onMapClick) return;
29683
29777
  const handler = (e) => {
29684
29778
  onMapClick(e.latlng.lat, e.latlng.lng);
@@ -29705,9 +29799,9 @@ var init_MapView = __esm({
29705
29799
  className,
29706
29800
  showAttribution = true
29707
29801
  }) {
29708
- const eventBus = useEventBus2();
29709
- const [clickedPosition, setClickedPosition] = useState105(null);
29710
- const handleMapClick = useCallback113((lat, lng) => {
29802
+ const eventBus = useEventBus3();
29803
+ const [clickedPosition, setClickedPosition] = useState110(null);
29804
+ const handleMapClick = useCallback120((lat, lng) => {
29711
29805
  if (showClickedPin) {
29712
29806
  setClickedPosition({ lat, lng });
29713
29807
  }
@@ -29716,7 +29810,7 @@ var init_MapView = __esm({
29716
29810
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
29717
29811
  }
29718
29812
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
29719
- const handleMarkerClick = useCallback113((marker) => {
29813
+ const handleMarkerClick = useCallback120((marker) => {
29720
29814
  onMarkerClick?.(marker);
29721
29815
  if (markerClickEvent) {
29722
29816
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -36843,10 +36937,10 @@ var init_Section = __esm({
36843
36937
  children,
36844
36938
  headerClassName,
36845
36939
  contentClassName,
36846
- as: Component = "section"
36940
+ as: Component2 = "section"
36847
36941
  }) => {
36848
36942
  const hasHeader = title || description || action;
36849
- const Comp = Component;
36943
+ const Comp = Component2;
36850
36944
  return /* @__PURE__ */ jsxRuntime.jsxs(
36851
36945
  Comp,
36852
36946
  {
@@ -39847,7 +39941,7 @@ var init_DrawerSlot = __esm({
39847
39941
  }
39848
39942
  });
39849
39943
  function StateIndicator({
39850
- assetUrl = DEFAULT_ASSET_URL4,
39944
+ assetUrl = DEFAULT_ASSET_URL11,
39851
39945
  state = "idle",
39852
39946
  label,
39853
39947
  size = "md",
@@ -39886,13 +39980,13 @@ function StateIndicator({
39886
39980
  }
39887
39981
  );
39888
39982
  }
39889
- var DEFAULT_ASSET_URL4, DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
39983
+ var DEFAULT_ASSET_URL11, DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
39890
39984
  var init_StateIndicator = __esm({
39891
39985
  "components/game/atoms/StateIndicator.tsx"() {
39892
39986
  init_Box();
39893
39987
  init_Icon();
39894
39988
  init_cn();
39895
- DEFAULT_ASSET_URL4 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
39989
+ DEFAULT_ASSET_URL11 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
39896
39990
  DEFAULT_STATE_STYLES = {
39897
39991
  idle: { icon: "\u23F8", bgClass: "bg-muted" },
39898
39992
  active: { icon: "\u25B6", bgClass: "bg-success" },
@@ -41738,6 +41832,1568 @@ var init_GameAudioToggle = __esm({
41738
41832
  GameAudioToggle.displayName = "GameAudioToggle";
41739
41833
  }
41740
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
+ });
41741
43397
  exports.GameShell = void 0;
41742
43398
  var init_GameShell = __esm({
41743
43399
  "components/game/templates/GameShell.tsx"() {
@@ -43388,6 +45044,236 @@ var init_NegotiatorBoard = __esm({
43388
45044
  NegotiatorBoard.displayName = "NegotiatorBoard";
43389
45045
  }
43390
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
+ });
43391
45277
  exports.PricingOrganism = void 0;
43392
45278
  var init_PricingOrganism = __esm({
43393
45279
  "components/marketing/organisms/PricingOrganism.tsx"() {
@@ -43511,7 +45397,7 @@ var init_PricingPageTemplate = __esm({
43511
45397
  }
43512
45398
  });
43513
45399
  function ResourceCounter({
43514
- assetUrl = DEFAULT_ASSET_URL5,
45400
+ assetUrl = DEFAULT_ASSET_URL12,
43515
45401
  icon,
43516
45402
  label = "Gold",
43517
45403
  value = 250,
@@ -43554,7 +45440,7 @@ function ResourceCounter({
43554
45440
  }
43555
45441
  );
43556
45442
  }
43557
- var colorTokenClasses2, DEFAULT_ASSET_URL5, sizeMap15;
45443
+ var colorTokenClasses2, DEFAULT_ASSET_URL12, sizeMap15;
43558
45444
  var init_ResourceCounter = __esm({
43559
45445
  "components/game/atoms/ResourceCounter.tsx"() {
43560
45446
  init_cn();
@@ -43567,7 +45453,7 @@ var init_ResourceCounter = __esm({
43567
45453
  error: "text-error",
43568
45454
  muted: "text-muted-foreground"
43569
45455
  };
43570
- DEFAULT_ASSET_URL5 = "https://almadar-kflow-assets.web.app/shared/world-map/gold_mine.png";
45456
+ DEFAULT_ASSET_URL12 = "https://almadar-kflow-assets.web.app/shared/world-map/gold_mine.png";
43571
45457
  sizeMap15 = {
43572
45458
  sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm", img: 16 },
43573
45459
  md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base", img: 20 },
@@ -43576,6 +45462,356 @@ var init_ResourceCounter = __esm({
43576
45462
  ResourceCounter.displayName = "ResourceCounter";
43577
45463
  }
43578
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
+ });
43579
45815
 
43580
45816
  // lib/traitRegistry.ts
43581
45817
  function getAllTraits() {
@@ -44810,7 +47046,7 @@ function getAllEvents(traits2) {
44810
47046
  function EventDispatcherTab({ traits: traits2, schema }) {
44811
47047
  const eventBus = useEventBus();
44812
47048
  const { t } = hooks.useTranslate();
44813
- const [log9, setLog] = React77__namespace.useState([]);
47049
+ const [log11, setLog] = React77__namespace.useState([]);
44814
47050
  const prevStatesRef = React77__namespace.useRef(/* @__PURE__ */ new Map());
44815
47051
  React77__namespace.useEffect(() => {
44816
47052
  for (const trait of traits2) {
@@ -44874,9 +47110,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
44874
47110
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.otherEvents") }),
44875
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)) })
44876
47112
  ] }),
44877
- log9.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
47113
+ log11.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
44878
47114
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.recentTransitions") }),
44879
- /* @__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: [
44880
47116
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-purple-400", children: entry.traitName }),
44881
47117
  " ",
44882
47118
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-500", children: entry.from }),
@@ -46908,9 +49144,9 @@ function VariablePanel({
46908
49144
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
46909
49145
  variables.map((v) => {
46910
49146
  const name = v.name == null ? "" : String(v.name);
46911
- const value = numField(v.value);
46912
- const max = numField(v.max, 100);
46913
- 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);
46914
49150
  const unit = v.unit == null ? "" : String(v.unit);
46915
49151
  const pct = Math.round((value - min) / (max - min) * 100);
46916
49152
  const isHigh = pct > 80;
@@ -46940,12 +49176,12 @@ function VariablePanel({
46940
49176
  })
46941
49177
  ] });
46942
49178
  }
46943
- var numField;
49179
+ var numField2;
46944
49180
  var init_VariablePanel = __esm({
46945
49181
  "components/game/organisms/puzzles/state-architect/VariablePanel.tsx"() {
46946
49182
  init_atoms2();
46947
49183
  init_cn();
46948
- numField = (v, fallback = 0) => {
49184
+ numField2 = (v, fallback = 0) => {
46949
49185
  const n = Number(v);
46950
49186
  return Number.isFinite(n) ? n : fallback;
46951
49187
  };
@@ -47372,7 +49608,7 @@ function formatDuration(seconds) {
47372
49608
  return `${Math.round(seconds)}s`;
47373
49609
  }
47374
49610
  function StatusEffect({
47375
- assetUrl = DEFAULT_ASSET_URL6,
49611
+ assetUrl = DEFAULT_ASSET_URL13,
47376
49612
  icon = "shield",
47377
49613
  label = "Shield",
47378
49614
  duration = 30,
@@ -47430,12 +49666,12 @@ function StatusEffect({
47430
49666
  label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground mt-0.5 text-center whitespace-nowrap", children: label })
47431
49667
  ] });
47432
49668
  }
47433
- var DEFAULT_ASSET_URL6, sizeMap16, variantStyles9;
49669
+ var DEFAULT_ASSET_URL13, sizeMap16, variantStyles9;
47434
49670
  var init_StatusEffect = __esm({
47435
49671
  "components/game/atoms/StatusEffect.tsx"() {
47436
49672
  init_cn();
47437
49673
  init_Icon();
47438
- DEFAULT_ASSET_URL6 = "https://almadar-kflow-assets.web.app/shared/effects/particles/flame_01.png";
49674
+ DEFAULT_ASSET_URL13 = "https://almadar-kflow-assets.web.app/shared/effects/particles/flame_01.png";
47439
49675
  sizeMap16 = {
47440
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 },
47441
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 },
@@ -47932,6 +50168,289 @@ var init_ToastSlot = __esm({
47932
50168
  exports.ToastSlot.displayName = "ToastSlot";
47933
50169
  }
47934
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
+ });
47935
50454
  function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
47936
50455
  const eventBus = useEventBus();
47937
50456
  const {
@@ -48441,48 +50960,48 @@ function WorldMapTemplate({
48441
50960
  }
48442
50961
  );
48443
50962
  }
48444
- 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;
48445
50964
  var init_WorldMapTemplate = __esm({
48446
50965
  "components/game/templates/WorldMapTemplate.tsx"() {
48447
50966
  init_WorldMapBoard();
48448
- CDN3 = "https://almadar-kflow-assets.web.app/shared";
50967
+ CDN7 = "https://almadar-kflow-assets.web.app/shared";
48449
50968
  DEFAULT_WORLDMAP_TILES = [
48450
- { x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
48451
- { x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
48452
- { x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
48453
- { x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
48454
- { x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
48455
- { x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
48456
- { x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
48457
- { x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
48458
- { x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
48459
- { x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
48460
- { x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
48461
- { x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
48462
- { x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
48463
- { x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
48464
- { x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
48465
- { x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
48466
- { x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
48467
- { x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
48468
- { x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
48469
- { x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
48470
- { x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
48471
- { x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
48472
- { x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
48473
- { x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN3}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
48474
- { 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` }
48475
50994
  ];
48476
50995
  DEFAULT_WORLDMAP_UNITS = [
48477
- { 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` },
48478
- { 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` }
48479
50998
  ];
48480
50999
  DEFAULT_WORLDMAP_FEATURES = [
48481
- { id: "f1", x: 2, y: 2, type: "capital", sprite: `${CDN3}/scenes/world/capital.png` },
48482
- { 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` }
48483
51002
  ];
48484
51003
  DEFAULT_WORLDMAP_MANIFEST = {
48485
- baseUrl: CDN3,
51004
+ baseUrl: CDN7,
48486
51005
  terrains: {
48487
51006
  grass: "/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png",
48488
51007
  water: "/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png",
@@ -48611,7 +51130,7 @@ function lazyThree(name, loader) {
48611
51130
  ThreeWrapper.displayName = `Lazy(${name})`;
48612
51131
  return ThreeWrapper;
48613
51132
  }
48614
- var ThreeBoundary, FeatureRenderer, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
51133
+ var ThreeBoundary, FeatureRenderer, GameCanvas3D2, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
48615
51134
  var init_component_registry_generated = __esm({
48616
51135
  "components/core/organisms/component-registry.generated.ts"() {
48617
51136
  init_AboutPageTemplate();
@@ -48721,6 +51240,7 @@ var init_component_registry_generated = __esm({
48721
51240
  init_FormSectionHeader();
48722
51241
  init_GameAudioProvider();
48723
51242
  init_GameAudioToggle();
51243
+ init_GameBoard3D();
48724
51244
  init_GameCanvas2D();
48725
51245
  init_GameHud();
48726
51246
  init_GameMenu();
@@ -48778,7 +51298,9 @@ var init_component_registry_generated = __esm({
48778
51298
  init_PageHeader();
48779
51299
  init_Pagination();
48780
51300
  init_PatternTile();
51301
+ init_PlatformerBoard();
48781
51302
  init_PlatformerCanvas();
51303
+ init_PlatformerTemplate();
48782
51304
  init_Popover();
48783
51305
  init_PositionedCanvas();
48784
51306
  init_PowerupSlots();
@@ -48802,6 +51324,8 @@ var init_component_registry_generated = __esm({
48802
51324
  init_ResourceBar();
48803
51325
  init_ResourceCounter();
48804
51326
  init_RichBlockEditor();
51327
+ init_RoguelikeBoard();
51328
+ init_RoguelikeTemplate();
48805
51329
  init_RuntimeDebugger2();
48806
51330
  init_ScaledDiagram();
48807
51331
  init_ScoreBoard();
@@ -48874,6 +51398,8 @@ var init_component_registry_generated = __esm({
48874
51398
  init_Toast();
48875
51399
  init_ToastSlot();
48876
51400
  init_Tooltip();
51401
+ init_TowerDefenseBoard();
51402
+ init_TowerDefenseTemplate();
48877
51403
  init_TraitFrame();
48878
51404
  init_TraitSlot();
48879
51405
  init_TrendIndicator();
@@ -48918,7 +51444,7 @@ var init_component_registry_generated = __esm({
48918
51444
  }
48919
51445
  };
48920
51446
  FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
48921
- GameCanvas3D = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
51447
+ GameCanvas3D2 = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
48922
51448
  GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
48923
51449
  GameCanvas3DCastleTemplate = lazyThree("GameCanvas3DCastleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
48924
51450
  GameCanvas3DWorldMapTemplate = lazyThree("GameCanvas3DWorldMapTemplate", () => import('@almadar/ui/components/molecules/game/three'));
@@ -49037,8 +51563,9 @@ var init_component_registry_generated = __esm({
49037
51563
  "FormSectionHeader": exports.FormSectionHeader,
49038
51564
  "GameAudioProvider": GameAudioProvider,
49039
51565
  "GameAudioToggle": GameAudioToggle,
51566
+ "GameBoard3D": GameBoard3D,
49040
51567
  "GameCanvas2D": GameCanvas2D,
49041
- "GameCanvas3D": GameCanvas3D,
51568
+ "GameCanvas3D": GameCanvas3D2,
49042
51569
  "GameCanvas3DBattleTemplate": GameCanvas3DBattleTemplate,
49043
51570
  "GameCanvas3DCastleTemplate": GameCanvas3DCastleTemplate,
49044
51571
  "GameCanvas3DWorldMapTemplate": GameCanvas3DWorldMapTemplate,
@@ -49100,7 +51627,9 @@ var init_component_registry_generated = __esm({
49100
51627
  "PageHeader": exports.PageHeader,
49101
51628
  "Pagination": exports.Pagination,
49102
51629
  "PatternTile": exports.PatternTile,
51630
+ "PlatformerBoard": PlatformerBoard,
49103
51631
  "PlatformerCanvas": PlatformerCanvas,
51632
+ "PlatformerTemplate": PlatformerTemplate,
49104
51633
  "Popover": exports.Popover,
49105
51634
  "PositionedCanvas": exports.PositionedCanvas,
49106
51635
  "PowerupSlots": PowerupSlots,
@@ -49124,6 +51653,8 @@ var init_component_registry_generated = __esm({
49124
51653
  "ResourceBar": ResourceBar,
49125
51654
  "ResourceCounter": ResourceCounter,
49126
51655
  "RichBlockEditor": exports.RichBlockEditor,
51656
+ "RoguelikeBoard": RoguelikeBoard,
51657
+ "RoguelikeTemplate": RoguelikeTemplate,
49127
51658
  "RuntimeDebugger": RuntimeDebugger,
49128
51659
  "ScaledDiagram": exports.ScaledDiagram,
49129
51660
  "ScoreBoard": ScoreBoard,
@@ -49199,6 +51730,8 @@ var init_component_registry_generated = __esm({
49199
51730
  "Toast": exports.Toast,
49200
51731
  "ToastSlot": exports.ToastSlot,
49201
51732
  "Tooltip": exports.Tooltip,
51733
+ "TowerDefenseBoard": TowerDefenseBoard,
51734
+ "TowerDefenseTemplate": TowerDefenseTemplate,
49202
51735
  "TraitFrame": TraitFrame,
49203
51736
  "TraitSlot": TraitSlot,
49204
51737
  "TrendIndicator": exports.TrendIndicator,
@@ -50761,6 +53294,10 @@ init_UncontrolledBattleBoard();
50761
53294
  init_useBattleState();
50762
53295
  init_WorldMapBoard();
50763
53296
  init_CastleBoard();
53297
+ init_PlatformerBoard();
53298
+ init_TowerDefenseBoard();
53299
+ init_RoguelikeBoard();
53300
+ init_GameBoard3D();
50764
53301
  init_TraitStateViewer();
50765
53302
  init_TraitSlot();
50766
53303
 
@@ -51333,6 +53870,9 @@ init_GameShell();
51333
53870
  init_BattleTemplate();
51334
53871
  init_CastleTemplate();
51335
53872
  init_WorldMapTemplate();
53873
+ init_PlatformerTemplate();
53874
+ init_TowerDefenseTemplate();
53875
+ init_RoguelikeTemplate();
51336
53876
  init_LandingPageTemplate();
51337
53877
  init_PricingPageTemplate();
51338
53878
  init_FeatureDetailPageTemplate();
@@ -51383,6 +53923,7 @@ exports.EventLog = EventLog;
51383
53923
  exports.FEATURE_TYPES = FEATURE_TYPES;
51384
53924
  exports.GameAudioProvider = GameAudioProvider;
51385
53925
  exports.GameAudioToggle = GameAudioToggle;
53926
+ exports.GameBoard3D = GameBoard3D;
51386
53927
  exports.GameCanvas2D = GameCanvas2D;
51387
53928
  exports.GameHud = GameHud;
51388
53929
  exports.GameMenu = GameMenu;
@@ -51401,11 +53942,15 @@ exports.NotifyListener = NotifyListener;
51401
53942
  exports.ObjectRulePanel = ObjectRulePanel;
51402
53943
  exports.OrbitalStateMachineView = exports.StateMachineView;
51403
53944
  exports.PhysicsManager = PhysicsManager;
53945
+ exports.PlatformerBoard = PlatformerBoard;
51404
53946
  exports.PlatformerCanvas = PlatformerCanvas;
53947
+ exports.PlatformerTemplate = PlatformerTemplate;
51405
53948
  exports.PowerupSlots = PowerupSlots;
51406
53949
  exports.QuestTracker = QuestTracker;
51407
53950
  exports.ResourceBar = ResourceBar;
51408
53951
  exports.ResourceCounter = ResourceCounter;
53952
+ exports.RoguelikeBoard = RoguelikeBoard;
53953
+ exports.RoguelikeTemplate = RoguelikeTemplate;
51409
53954
  exports.RuleEditor = RuleEditor;
51410
53955
  exports.RuntimeDebugger = RuntimeDebugger;
51411
53956
  exports.SHEET_COLUMNS = SHEET_COLUMNS;
@@ -51433,6 +53978,8 @@ exports.TableView = TableView;
51433
53978
  exports.TerrainPalette = TerrainPalette;
51434
53979
  exports.TimeSlotCell = TimeSlotCell;
51435
53980
  exports.TimerDisplay = TimerDisplay;
53981
+ exports.TowerDefenseBoard = TowerDefenseBoard;
53982
+ exports.TowerDefenseTemplate = TowerDefenseTemplate;
51436
53983
  exports.TraitFrame = TraitFrame;
51437
53984
  exports.TraitSlot = TraitSlot;
51438
53985
  exports.TraitStateViewer = TraitStateViewer;