@almadar/ui 5.47.0 → 5.48.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.
@@ -10429,15 +10429,15 @@ function BattleBoard({
10429
10429
  const board = boardEntity(entity) ?? {};
10430
10430
  const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
10431
10431
  const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
10432
- const boardWidth = num(board.boardWidth, 8);
10433
- const boardHeight = num(board.boardHeight, 6);
10432
+ const boardWidth = num(board.gridWidth ?? board.boardWidth, 8);
10433
+ const boardHeight = num(board.gridHeight ?? board.boardHeight, 6);
10434
10434
  const assetManifest = propAssetManifest ?? board.assetManifest;
10435
10435
  const backgroundImage = board.backgroundImage;
10436
10436
  const units = rows(board.units);
10437
10437
  const selectedUnitId = board.selectedUnitId ?? null;
10438
10438
  const currentPhase = str(board.phase) || "observation";
10439
10439
  const currentTurn = num(board.turn, 1);
10440
- const gameResult = board.gameResult ?? null;
10440
+ const gameResult = board.result ?? null;
10441
10441
  const eventBus = useEventBus();
10442
10442
  const { t } = hooks.useTranslate();
10443
10443
  const [hoveredTile, setHoveredTile] = React77.useState(null);
@@ -10458,7 +10458,7 @@ function BattleBoard({
10458
10458
  const validMoves = React77.useMemo(() => {
10459
10459
  if (!selectedUnit || currentPhase !== "movement") return [];
10460
10460
  const moves = [];
10461
- const range = num(selectedUnit.movement);
10461
+ const range = num(board.movementRange, 2);
10462
10462
  const origin = unitPosition(selectedUnit);
10463
10463
  for (let dy = -range; dy <= range; dy++) {
10464
10464
  for (let dx = -range; dx <= range; dx++) {
@@ -18743,15 +18743,24 @@ function CastleBoard({
18743
18743
  featureClickEvent,
18744
18744
  unitClickEvent,
18745
18745
  tileClickEvent,
18746
+ playAgainEvent,
18746
18747
  className
18747
18748
  }) {
18748
18749
  const eventBus = useEventBus();
18750
+ const { t } = hooks.useTranslate();
18749
18751
  const resolved = boardEntity(entity);
18750
18752
  const tiles = propTiles ?? (Array.isArray(resolved?.tiles) ? resolved.tiles : []);
18751
18753
  const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
18752
18754
  const units = propUnits ?? (Array.isArray(resolved?.units) ? resolved.units : []);
18753
18755
  const assetManifest = propAssetManifest ?? resolved?.assetManifest;
18754
18756
  const backgroundImage = resolved?.backgroundImage;
18757
+ const gold = num(resolved?.gold);
18758
+ const health = num(resolved?.health);
18759
+ const maxHealth = num(resolved?.maxHealth);
18760
+ const wave = num(resolved?.wave);
18761
+ const tickCount = num(resolved?.tickCount);
18762
+ const buildings = rows(resolved?.buildings);
18763
+ const result = str(resolved?.result) || "none";
18755
18764
  const [hoveredTile, setHoveredTile] = React77.useState(null);
18756
18765
  const [selectedFeature, setSelectedFeature] = React77.useState(null);
18757
18766
  const hoveredFeature = React77.useMemo(() => {
@@ -18764,7 +18773,7 @@ function CastleBoard({
18764
18773
  (u) => u.position?.x === hoveredTile.x && u.position?.y === hoveredTile.y
18765
18774
  ) ?? null;
18766
18775
  }, [hoveredTile, units]);
18767
- const maxY = Math.max(...tiles.map((t) => t.y), 0);
18776
+ const maxY = Math.max(...tiles.map((t2) => t2.y), 0);
18768
18777
  const baseOffsetX = (maxY + 1) * (exports.TILE_WIDTH * scale / 2);
18769
18778
  const tileToScreen = React77.useCallback(
18770
18779
  (tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
@@ -18799,6 +18808,11 @@ function CastleBoard({
18799
18808
  }
18800
18809
  }, [units, onUnitClick, unitClickEvent, eventBus]);
18801
18810
  const clearSelection = React77.useCallback(() => setSelectedFeature(null), []);
18811
+ const handlePlayAgain = React77.useCallback(() => {
18812
+ if (playAgainEvent) {
18813
+ eventBus.emit(`UI:${playAgainEvent}`, {});
18814
+ }
18815
+ }, [playAgainEvent, eventBus]);
18802
18816
  const ctx = React77.useMemo(
18803
18817
  () => ({
18804
18818
  hoveredTile,
@@ -18807,11 +18821,18 @@ function CastleBoard({
18807
18821
  selectedFeature,
18808
18822
  clearSelection,
18809
18823
  tileToScreen,
18810
- scale
18824
+ scale,
18825
+ gold,
18826
+ health,
18827
+ maxHealth,
18828
+ wave,
18829
+ tickCount,
18830
+ buildings,
18831
+ result
18811
18832
  }),
18812
- [hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale]
18833
+ [hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale, gold, health, maxHealth, wave, tickCount, buildings, result]
18813
18834
  );
18814
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("castle-board min-h-screen flex flex-col bg-background", className), children: [
18835
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("castle-board relative min-h-screen flex flex-col bg-background", className), children: [
18815
18836
  header && header(ctx),
18816
18837
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
18817
18838
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-auto p-4 relative", children: [
@@ -18836,7 +18857,29 @@ function CastleBoard({
18836
18857
  ] }),
18837
18858
  sidePanel && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-96 shrink-0 border-l border-border bg-surface overflow-y-auto", children: sidePanel(ctx) })
18838
18859
  ] }),
18839
- footer && footer(ctx)
18860
+ footer && footer(ctx),
18861
+ result !== "none" && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { className: "text-center p-8", gap: "lg", children: [
18862
+ /* @__PURE__ */ jsxRuntime.jsx(
18863
+ exports.Typography,
18864
+ {
18865
+ variant: "h2",
18866
+ className: cn(
18867
+ "text-4xl font-black tracking-widest uppercase",
18868
+ result === "victory" ? "text-warning" : "text-error"
18869
+ ),
18870
+ children: result === "victory" ? t("battle.victory") : t("battle.defeat")
18871
+ }
18872
+ ),
18873
+ /* @__PURE__ */ jsxRuntime.jsx(
18874
+ exports.Button,
18875
+ {
18876
+ variant: "primary",
18877
+ className: "px-8 py-3 font-semibold",
18878
+ onClick: handlePlayAgain,
18879
+ children: t("battle.playAgain")
18880
+ }
18881
+ )
18882
+ ] }) })
18840
18883
  ] });
18841
18884
  }
18842
18885
  var init_CastleBoard = __esm({
@@ -18844,6 +18887,10 @@ var init_CastleBoard = __esm({
18844
18887
  "use client";
18845
18888
  init_cn();
18846
18889
  init_useEventBus();
18890
+ init_Box();
18891
+ init_Button();
18892
+ init_Typography();
18893
+ init_Stack();
18847
18894
  init_IsometricCanvas();
18848
18895
  init_boardEntity();
18849
18896
  init_isometric();
@@ -19766,22 +19813,16 @@ function ClassifierBoard({
19766
19813
  const { emit } = useEventBus();
19767
19814
  const { t } = hooks.useTranslate();
19768
19815
  const resolved = boardEntity(entity);
19769
- const [localAssignments, setLocalAssignments] = React77.useState({});
19770
19816
  const [headerError, setHeaderError] = React77.useState(false);
19771
- const [localSubmitted, setLocalSubmitted] = React77.useState(false);
19772
- const [localAttempts, setLocalAttempts] = React77.useState(0);
19773
- const [showHint, setShowHint] = React77.useState(false);
19774
19817
  const items = Array.isArray(resolved?.items) ? resolved.items : [];
19775
19818
  const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
19776
- const entityResult = str(resolved?.result);
19777
- const entityDrivesResult = entityResult.length > 0;
19778
- const entityHasAssignments = items.some((item) => item.assignedCategory != null);
19779
- const assignments = entityHasAssignments ? items.reduce((acc, item) => {
19780
- if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
19819
+ const result = str(resolved?.result);
19820
+ const submitted = result === "win";
19821
+ const attempts = num(resolved?.attempts);
19822
+ const assignments = items.reduce((acc, item) => {
19823
+ if (item.assignedCategory != null && item.assignedCategory !== "") acc[item.id] = item.assignedCategory;
19781
19824
  return acc;
19782
- }, {}) : localAssignments;
19783
- const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
19784
- const submitted = entityDrivesResult || localSubmitted;
19825
+ }, {});
19785
19826
  const unassignedItems = items.filter((item) => !assignments[item.id]);
19786
19827
  const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
19787
19828
  const results = submitted ? items.map((item) => ({
@@ -19789,54 +19830,25 @@ function ClassifierBoard({
19789
19830
  assigned: assignments[item.id],
19790
19831
  correct: assignments[item.id] === item.correctCategory
19791
19832
  })) : [];
19792
- const allCorrect = entityDrivesResult ? entityResult === "success" : results.length > 0 && results.every((r) => r.correct);
19833
+ const allCorrect = result === "win";
19793
19834
  const correctCount = results.filter((r) => r.correct).length;
19835
+ const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
19794
19836
  const handleAssign = (itemId, categoryId) => {
19795
19837
  if (submitted) return;
19796
- if (assignEvent) {
19797
- emit(`UI:${assignEvent}`, { itemId, categoryId });
19798
- }
19799
- if (!entityHasAssignments) {
19800
- setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
19801
- }
19838
+ if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId });
19802
19839
  };
19803
19840
  const handleUnassign = (itemId) => {
19804
19841
  if (submitted) return;
19805
- if (!entityHasAssignments) {
19806
- setLocalAssignments((prev) => {
19807
- const next = { ...prev };
19808
- delete next[itemId];
19809
- return next;
19810
- });
19811
- }
19842
+ if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId: "" });
19812
19843
  };
19813
- const handleSubmit = React77.useCallback(() => {
19814
- if (checkEvent) {
19815
- emit(`UI:${checkEvent}`, {});
19816
- }
19817
- if (!entityDrivesResult) {
19818
- setLocalSubmitted(true);
19819
- setLocalAttempts((a) => a + 1);
19820
- const correct = items.every((item) => assignments[item.id] === item.correctCategory);
19821
- if (correct) {
19822
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
19823
- }
19824
- }
19825
- }, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
19826
- const handleReset = () => {
19827
- if (!entityDrivesResult) setLocalSubmitted(false);
19828
- if (attempts >= 2 && str(resolved?.hint)) {
19829
- setShowHint(true);
19844
+ const handleSubmit = () => {
19845
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
19846
+ if (allAssigned && items.every((item) => assignments[item.id] === item.correctCategory)) {
19847
+ emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
19830
19848
  }
19831
19849
  };
19832
19850
  const handleFullReset = () => {
19833
- if (playAgainEvent) {
19834
- emit(`UI:${playAgainEvent}`, {});
19835
- }
19836
- setLocalAssignments({});
19837
- setLocalSubmitted(false);
19838
- setLocalAttempts(0);
19839
- setShowHint(false);
19851
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
19840
19852
  };
19841
19853
  if (!resolved) return null;
19842
19854
  const theme = resolved.theme ?? void 0;
@@ -19875,17 +19887,17 @@ function ClassifierBoard({
19875
19887
  /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { size: "sm", children: catItems.length })
19876
19888
  ] }),
19877
19889
  /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "xs", className: "flex-wrap min-h-[2rem]", children: catItems.map((item) => {
19878
- const result = results.find((r) => r.item.id === item.id);
19890
+ const result2 = results.find((r) => r.item.id === item.id);
19879
19891
  return /* @__PURE__ */ jsxRuntime.jsxs(
19880
19892
  exports.Badge,
19881
19893
  {
19882
19894
  size: "sm",
19883
- className: `cursor-pointer ${result ? result.correct ? "border-success bg-success/10" : "border-error bg-error/10" : ""}`,
19895
+ className: `cursor-pointer ${result2 ? result2.correct ? "border-success bg-success/10" : "border-error bg-error/10" : ""}`,
19884
19896
  onClick: () => handleUnassign(item.id),
19885
19897
  children: [
19886
19898
  item.iconUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: item.iconUrl, alt: "", className: "w-3 h-3 object-contain inline-block" }),
19887
19899
  item.label,
19888
- result && /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: result.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "xs", className: result.correct ? "text-success" : "text-error" })
19900
+ result2 && /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: result2.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "xs", className: result2.correct ? "text-success" : "text-error" })
19889
19901
  ]
19890
19902
  },
19891
19903
  item.id
@@ -19914,10 +19926,10 @@ function ClassifierBoard({
19914
19926
  ] }) }),
19915
19927
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", children: hint }) }),
19916
19928
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", justify: "center", children: [
19917
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
19929
+ !submitted && /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
19918
19930
  /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.Send, size: "sm" }),
19919
19931
  t("classifier.check")
19920
- ] }) : !allCorrect ? /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", onClick: handleReset, children: t("classifier.tryAgain") }) : null,
19932
+ ] }),
19921
19933
  /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "secondary", onClick: handleFullReset, children: [
19922
19934
  /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
19923
19935
  t("classifier.reset")
@@ -20204,6 +20216,7 @@ function getComboScale(combo) {
20204
20216
  return "";
20205
20217
  }
20206
20218
  function ComboCounter({
20219
+ assetUrl = DEFAULT_ASSET_URL,
20207
20220
  combo = 5,
20208
20221
  multiplier,
20209
20222
  streak,
@@ -20223,6 +20236,17 @@ function ComboCounter({
20223
20236
  className
20224
20237
  ),
20225
20238
  children: [
20239
+ assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
20240
+ "img",
20241
+ {
20242
+ src: assetUrl,
20243
+ alt: "combo",
20244
+ width: 24,
20245
+ height: 24,
20246
+ style: { imageRendering: "pixelated", objectFit: "contain" },
20247
+ className: "flex-shrink-0 mb-0.5"
20248
+ }
20249
+ ),
20226
20250
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-black tabular-nums leading-none", sizes.combo, getComboIntensity(combo)), children: combo }),
20227
20251
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-bold uppercase tracking-wider text-muted-foreground", sizes.label), children: "combo" }),
20228
20252
  multiplier != null && multiplier > 1 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("font-bold text-warning tabular-nums", sizes.multiplier), children: [
@@ -20237,10 +20261,11 @@ function ComboCounter({
20237
20261
  }
20238
20262
  );
20239
20263
  }
20240
- var sizeMap4;
20264
+ var DEFAULT_ASSET_URL, sizeMap4;
20241
20265
  var init_ComboCounter = __esm({
20242
20266
  "components/game/atoms/ComboCounter.tsx"() {
20243
20267
  init_cn();
20268
+ DEFAULT_ASSET_URL = "https://almadar-kflow-assets.web.app/shared/effects/flash/flash00.png";
20244
20269
  sizeMap4 = {
20245
20270
  sm: { combo: "text-lg", label: "text-xs", multiplier: "text-xs" },
20246
20271
  md: { combo: "text-2xl", label: "text-xs", multiplier: "text-sm" },
@@ -20649,7 +20674,7 @@ var init_CounterTemplate = __esm({
20649
20674
  }
20650
20675
  });
20651
20676
  function ItemSlot({
20652
- assetUrl = DEFAULT_ASSET_URL,
20677
+ assetUrl = DEFAULT_ASSET_URL2,
20653
20678
  icon = "sword",
20654
20679
  label = "Iron Sword",
20655
20680
  quantity,
@@ -20708,7 +20733,7 @@ function ItemSlot({
20708
20733
  }
20709
20734
  );
20710
20735
  }
20711
- var sizeMap5, rarityBorderMap, rarityGlowMap, DEFAULT_ASSET_URL, assetSizeMap;
20736
+ var sizeMap5, rarityBorderMap, rarityGlowMap, DEFAULT_ASSET_URL2, assetSizeMap;
20712
20737
  var init_ItemSlot = __esm({
20713
20738
  "components/game/atoms/ItemSlot.tsx"() {
20714
20739
  "use client";
@@ -20733,7 +20758,7 @@ var init_ItemSlot = __esm({
20733
20758
  epic: "shadow-lg",
20734
20759
  legendary: "shadow-lg"
20735
20760
  };
20736
- DEFAULT_ASSET_URL = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
20761
+ DEFAULT_ASSET_URL2 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
20737
20762
  assetSizeMap = {
20738
20763
  sm: 28,
20739
20764
  md: 40,
@@ -27289,6 +27314,7 @@ var init_InventoryGrid = __esm({
27289
27314
  }
27290
27315
  });
27291
27316
  function WaypointMarker({
27317
+ assetUrl = DEFAULT_ASSET_URL3,
27292
27318
  label,
27293
27319
  icon,
27294
27320
  active = true,
@@ -27327,7 +27353,17 @@ function WaypointMarker({
27327
27353
  active && !completed && "bg-info text-foreground",
27328
27354
  !active && !completed && "bg-muted"
27329
27355
  ),
27330
- children: completed ? checkIcon : icon && (typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon }))
27356
+ children: completed ? checkIcon : assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
27357
+ "img",
27358
+ {
27359
+ src: assetUrl,
27360
+ alt: label,
27361
+ width: sizes.img,
27362
+ height: sizes.img,
27363
+ style: { imageRendering: "pixelated", objectFit: "contain" },
27364
+ className: "flex-shrink-0"
27365
+ }
27366
+ ) : icon ? typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon }) : null
27331
27367
  }
27332
27368
  )
27333
27369
  ] }),
@@ -27344,15 +27380,16 @@ function WaypointMarker({
27344
27380
  )
27345
27381
  ] });
27346
27382
  }
27347
- var sizeMap12, checkIcon;
27383
+ var DEFAULT_ASSET_URL3, sizeMap12, checkIcon;
27348
27384
  var init_WaypointMarker = __esm({
27349
27385
  "components/game/atoms/WaypointMarker.tsx"() {
27350
27386
  init_cn();
27351
27387
  init_Icon();
27388
+ DEFAULT_ASSET_URL3 = "https://almadar-kflow-assets.web.app/shared/world-map/battle_marker.png";
27352
27389
  sizeMap12 = {
27353
- sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1" },
27354
- md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5" },
27355
- lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2" }
27390
+ sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1", img: 12 },
27391
+ md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5", img: 18 },
27392
+ lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2", img: 24 }
27356
27393
  };
27357
27394
  checkIcon = /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 3, className: "w-3/5 h-3/5", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 13l4 4L19 7", strokeLinecap: "round", strokeLinejoin: "round" }) });
27358
27395
  WaypointMarker.displayName = "WaypointMarker";
@@ -29624,7 +29661,7 @@ var init_MapView = __esm({
29624
29661
  shadowSize: [41, 41]
29625
29662
  });
29626
29663
  L.Marker.prototype.options.icon = defaultIcon;
29627
- const { useEffect: useEffect74, useRef: useRef71, useCallback: useCallback114, useState: useState105 } = React77__namespace.default;
29664
+ const { useEffect: useEffect74, useRef: useRef71, useCallback: useCallback113, useState: useState105 } = React77__namespace.default;
29628
29665
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
29629
29666
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
29630
29667
  function MapUpdater({ centerLat, centerLng, zoom }) {
@@ -29670,7 +29707,7 @@ var init_MapView = __esm({
29670
29707
  }) {
29671
29708
  const eventBus = useEventBus2();
29672
29709
  const [clickedPosition, setClickedPosition] = useState105(null);
29673
- const handleMapClick = useCallback114((lat, lng) => {
29710
+ const handleMapClick = useCallback113((lat, lng) => {
29674
29711
  if (showClickedPin) {
29675
29712
  setClickedPosition({ lat, lng });
29676
29713
  }
@@ -29679,7 +29716,7 @@ var init_MapView = __esm({
29679
29716
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
29680
29717
  }
29681
29718
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
29682
- const handleMarkerClick = useCallback114((marker) => {
29719
+ const handleMarkerClick = useCallback113((marker) => {
29683
29720
  onMarkerClick?.(marker);
29684
29721
  if (markerClickEvent) {
29685
29722
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -39122,44 +39159,27 @@ function DebuggerBoard({
39122
39159
  const { t } = hooks.useTranslate();
39123
39160
  const resolved = boardEntity(entity);
39124
39161
  const [headerError, setHeaderError] = React77.useState(false);
39125
- const [showHint, setShowHint] = React77.useState(false);
39126
39162
  const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
39127
- const result = resolved?.result ?? null;
39163
+ const result = str(resolved?.result) || "none";
39128
39164
  const attempts = num(resolved?.attempts);
39129
- const submitted = result != null;
39165
+ const submitted = result === "win";
39166
+ const showHint = !submitted && attempts >= 2 && Boolean(str(resolved?.hint));
39130
39167
  const bugLines = lines.filter((l) => l.isBug);
39131
39168
  const flaggedLines = lines.filter((l) => l.isFlagged);
39132
39169
  const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
39133
39170
  const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
39134
- const allCorrect = result === "win";
39171
+ const allCorrect = submitted;
39135
39172
  const toggleLine = (lineId) => {
39136
39173
  if (submitted) return;
39137
- if (toggleFlagEvent) {
39138
- emit(`UI:${toggleFlagEvent}`, { lineId });
39139
- }
39174
+ if (toggleFlagEvent) emit(`UI:${toggleFlagEvent}`, { lineId });
39140
39175
  };
39141
39176
  const handleSubmit = React77.useCallback(() => {
39142
- if (checkEvent) {
39143
- emit(`UI:${checkEvent}`, {});
39144
- }
39177
+ if (checkEvent) emit(`UI:${checkEvent}`, {});
39145
39178
  const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
39146
- if (correct) {
39147
- emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
39148
- }
39179
+ if (correct) emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
39149
39180
  }, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
39150
39181
  const handleReset = () => {
39151
- if (playAgainEvent) {
39152
- emit(`UI:${playAgainEvent}`, {});
39153
- }
39154
- if (attempts >= 2 && str(resolved?.hint)) {
39155
- setShowHint(true);
39156
- }
39157
- };
39158
- const handleFullReset = () => {
39159
- if (playAgainEvent) {
39160
- emit(`UI:${playAgainEvent}`, {});
39161
- }
39162
- setShowHint(false);
39182
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
39163
39183
  };
39164
39184
  if (!resolved) return null;
39165
39185
  const theme = resolved.theme ?? void 0;
@@ -39183,7 +39203,7 @@ function DebuggerBoard({
39183
39203
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
39184
39204
  ] }),
39185
39205
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", children: str(resolved.description) }),
39186
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(num(resolved.bugCount)) }) })
39206
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(lines.filter((l) => l.isBug).length) }) })
39187
39207
  ] }) }),
39188
39208
  /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(exports.VStack, { gap: "none", children: lines.map((line, i) => {
39189
39209
  const isFlagged = !!line.isFlagged;
@@ -39233,11 +39253,11 @@ function DebuggerBoard({
39233
39253
  ] }) }),
39234
39254
  showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", children: hint }) }),
39235
39255
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", justify: "center", children: [
39236
- !submitted ? /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
39256
+ !submitted && /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
39237
39257
  /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.Send, size: "sm" }),
39238
39258
  t("debugger.submit")
39239
- ] }) : !allCorrect ? /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", onClick: handleReset, children: t("debugger.tryAgain") }) : null,
39240
- /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "secondary", onClick: handleFullReset, children: [
39259
+ ] }),
39260
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "secondary", onClick: handleReset, children: [
39241
39261
  /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
39242
39262
  t("debugger.reset")
39243
39263
  ] })
@@ -39827,6 +39847,7 @@ var init_DrawerSlot = __esm({
39827
39847
  }
39828
39848
  });
39829
39849
  function StateIndicator({
39850
+ assetUrl = DEFAULT_ASSET_URL4,
39830
39851
  state = "idle",
39831
39852
  label,
39832
39853
  size = "md",
@@ -39849,18 +39870,29 @@ function StateIndicator({
39849
39870
  className
39850
39871
  ),
39851
39872
  children: [
39852
- /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { as: "span", children: typeof config.icon === "string" ? /^[a-zA-Z0-9-]+$/.test(config.icon) ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: config.icon }) : config.icon : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: config.icon }) }),
39873
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { as: "span", children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
39874
+ "img",
39875
+ {
39876
+ src: assetUrl,
39877
+ alt: displayLabel,
39878
+ width: 16,
39879
+ height: 16,
39880
+ style: { imageRendering: "pixelated", objectFit: "contain" },
39881
+ className: "flex-shrink-0"
39882
+ }
39883
+ ) : typeof config.icon === "string" ? /^[a-zA-Z0-9-]+$/.test(config.icon) ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: config.icon }) : config.icon : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: config.icon }) }),
39853
39884
  /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { as: "span", children: displayLabel })
39854
39885
  ]
39855
39886
  }
39856
39887
  );
39857
39888
  }
39858
- var DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
39889
+ var DEFAULT_ASSET_URL4, DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
39859
39890
  var init_StateIndicator = __esm({
39860
39891
  "components/game/atoms/StateIndicator.tsx"() {
39861
39892
  init_Box();
39862
39893
  init_Icon();
39863
39894
  init_cn();
39895
+ DEFAULT_ASSET_URL4 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
39864
39896
  DEFAULT_STATE_STYLES = {
39865
39897
  idle: { icon: "\u23F8", bgClass: "bg-muted" },
39866
39898
  active: { icon: "\u25B6", bgClass: "bg-success" },
@@ -40334,39 +40366,40 @@ function EventHandlerBoard({
40334
40366
  stepDurationMs = 800,
40335
40367
  playEvent,
40336
40368
  completeEvent,
40369
+ editRuleEvent,
40370
+ playAgainEvent,
40337
40371
  className
40338
40372
  }) {
40339
40373
  const { emit } = useEventBus();
40340
40374
  const { t } = hooks.useTranslate();
40341
40375
  const resolved = boardEntity(entity);
40342
- const entityObjects = rows(resolved?.objects);
40343
- const [objects, setObjects] = React77.useState(() => [...entityObjects]);
40376
+ const objects = rows(resolved?.objects);
40377
+ const entityResult = str(resolved?.result) || "none";
40378
+ const isSuccess = entityResult === "win";
40379
+ const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
40344
40380
  const [selectedObjectId, setSelectedObjectId] = React77.useState(
40345
- entityObjects[0] ? objId(entityObjects[0]) : null
40381
+ objects[0] ? objId(objects[0]) : null
40346
40382
  );
40347
40383
  const [headerError, setHeaderError] = React77.useState(false);
40348
- const [playState, setPlayState] = React77.useState("editing");
40384
+ const [isPlaying, setIsPlaying] = React77.useState(false);
40349
40385
  const [eventLog, setEventLog] = React77.useState([]);
40350
- const [attempts, setAttempts] = React77.useState(0);
40351
40386
  const timerRef = React77.useRef(null);
40352
40387
  const logIdCounter = React77.useRef(0);
40353
40388
  React77.useEffect(() => () => {
40354
40389
  if (timerRef.current) clearTimeout(timerRef.current);
40355
40390
  }, []);
40356
- const selectedObject = objects.find((o) => objId(o) === selectedObjectId) || null;
40391
+ const selectedObject = objects.find((o) => objId(o) === selectedObjectId) ?? null;
40357
40392
  const handleRulesChange = React77.useCallback((objectId, rules) => {
40358
- setObjects((prev) => prev.map(
40359
- (o) => objId(o) === objectId ? { ...o, rules } : o
40360
- ));
40361
- }, []);
40393
+ if (editRuleEvent) emit(`UI:${editRuleEvent}`, { objectId, rules });
40394
+ }, [editRuleEvent, emit]);
40362
40395
  const addLogEntry = React77.useCallback((icon, message, status = "done") => {
40363
40396
  const id = `log-${logIdCounter.current++}`;
40364
40397
  setEventLog((prev) => [...prev, { id, timestamp: Date.now(), icon, message, status }]);
40365
40398
  }, []);
40366
40399
  const handlePlay = React77.useCallback(() => {
40367
- if (playState !== "editing") return;
40400
+ if (isPlaying || isSuccess) return;
40368
40401
  if (playEvent) emit(`UI:${playEvent}`, {});
40369
- setPlayState("playing");
40402
+ setIsPlaying(true);
40370
40403
  setEventLog([]);
40371
40404
  const allRules = [];
40372
40405
  objects.forEach((obj) => {
@@ -40382,15 +40415,8 @@ function EventHandlerBoard({
40382
40415
  let goalReached = false;
40383
40416
  const processNext = () => {
40384
40417
  if (eventQueue.length === 0 || stepIdx > 20) {
40385
- if (goalReached) {
40386
- setPlayState("success");
40387
- if (completeEvent) {
40388
- emit(`UI:${completeEvent}`, { success: true });
40389
- }
40390
- } else {
40391
- setAttempts((prev) => prev + 1);
40392
- setPlayState("fail");
40393
- }
40418
+ setIsPlaying(false);
40419
+ if (goalReached && completeEvent) emit(`UI:${completeEvent}`, { success: true });
40394
40420
  return;
40395
40421
  }
40396
40422
  const currentEvent = eventQueue.shift();
@@ -40421,21 +40447,19 @@ function EventHandlerBoard({
40421
40447
  addLogEntry("\u{1F3AC}", t("eventHandler.simulationStarted", { events: triggers.join(", ") }), "active");
40422
40448
  }
40423
40449
  timerRef.current = setTimeout(processNext, stepDurationMs);
40424
- }, [playState, objects, resolved, stepDurationMs, playEvent, completeEvent, emit, addLogEntry, t]);
40450
+ }, [isPlaying, isSuccess, objects, resolved, stepDurationMs, playEvent, completeEvent, emit, addLogEntry, t]);
40425
40451
  const handleTryAgain = React77.useCallback(() => {
40426
40452
  if (timerRef.current) clearTimeout(timerRef.current);
40427
- setPlayState("editing");
40453
+ setIsPlaying(false);
40428
40454
  setEventLog([]);
40429
40455
  }, []);
40430
40456
  const handleReset = React77.useCallback(() => {
40431
40457
  if (timerRef.current) clearTimeout(timerRef.current);
40432
- const resetObjects = rows(resolved?.objects);
40433
- setObjects([...resetObjects]);
40434
- setPlayState("editing");
40458
+ setIsPlaying(false);
40435
40459
  setEventLog([]);
40436
- setSelectedObjectId(resetObjects[0] ? objId(resetObjects[0]) : null);
40437
- setAttempts(0);
40438
- }, [resolved?.objects]);
40460
+ setSelectedObjectId(objects[0] ? objId(objects[0]) : null);
40461
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
40462
+ }, [objects, playAgainEvent, emit]);
40439
40463
  if (!resolved) return null;
40440
40464
  const objectViewers = objects.map((obj) => {
40441
40465
  const states = objStates(obj);
@@ -40504,12 +40528,12 @@ function EventHandlerBoard({
40504
40528
  {
40505
40529
  object: selectedObject,
40506
40530
  onRulesChange: handleRulesChange,
40507
- disabled: playState !== "editing"
40531
+ disabled: isPlaying
40508
40532
  }
40509
40533
  ),
40510
40534
  eventLog.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(EventLog, { entries: eventLog }),
40511
- playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("eventHandler.chainComplete") }) }),
40512
- playState === "fail" && /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", children: [
40535
+ isSuccess && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("eventHandler.chainComplete") }) }),
40536
+ !isPlaying && !isSuccess && attempts > 0 && /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", children: [
40513
40537
  /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body1", className: "text-foreground font-medium", children: t(encourageKey) }) }),
40514
40538
  showHint && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { className: "items-start", gap: "xs", children: [
40515
40539
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
@@ -40517,12 +40541,12 @@ function EventHandlerBoard({
40517
40541
  ] }) })
40518
40542
  ] }),
40519
40543
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", children: [
40520
- playState === "fail" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsxRuntime.jsx(
40544
+ !isPlaying && !isSuccess && attempts > 0 ? /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsxRuntime.jsx(
40521
40545
  exports.Button,
40522
40546
  {
40523
40547
  variant: "primary",
40524
40548
  onClick: handlePlay,
40525
- disabled: playState !== "editing",
40549
+ disabled: isPlaying || isSuccess,
40526
40550
  children: "\u25B6 " + t("game.play")
40527
40551
  }
40528
40552
  ),
@@ -43190,21 +43214,6 @@ var init_ModalSlot = __esm({
43190
43214
  exports.ModalSlot.displayName = "ModalSlot";
43191
43215
  }
43192
43216
  });
43193
- function getOpponentAction(strategy, actions, history) {
43194
- const actionIds = actions.map((a) => a.id);
43195
- switch (strategy) {
43196
- case "always-cooperate":
43197
- return actionIds[0];
43198
- case "always-defect":
43199
- return actionIds[actionIds.length - 1];
43200
- case "tit-for-tat":
43201
- if (history.length === 0) return actionIds[0];
43202
- return history[history.length - 1].playerAction;
43203
- case "random":
43204
- default:
43205
- return actionIds[Math.floor(Math.random() * actionIds.length)];
43206
- }
43207
- }
43208
43217
  function NegotiatorBoard({
43209
43218
  entity,
43210
43219
  completeEvent = "PUZZLE_COMPLETE",
@@ -43226,28 +43235,35 @@ function NegotiatorBoard({
43226
43235
  const playerTotal = num(resolved?.score);
43227
43236
  const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
43228
43237
  const won = result === "win";
43229
- const opponentTotal = history.reduce((s, r) => s + r.opponentPayoff, 0);
43238
+ const lastPlayerAction = str(resolved?.lastPlayerAction);
43239
+ const lastOpponentAction = str(resolved?.lastOpponentAction);
43240
+ const lastPayoff = num(resolved?.lastPayoff);
43230
43241
  const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
43231
43242
  const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
43243
+ const prevRoundRef = React77.useRef(currentRound);
43244
+ React77.useEffect(() => {
43245
+ if (currentRound > prevRoundRef.current && lastPlayerAction) {
43246
+ const opponentPayoffEntry = payoffMatrix.find(
43247
+ (p2) => p2.playerAction === lastPlayerAction && p2.opponentAction === lastOpponentAction
43248
+ );
43249
+ setHistory((prev) => [
43250
+ ...prev,
43251
+ {
43252
+ round: currentRound,
43253
+ playerAction: lastPlayerAction,
43254
+ opponentAction: lastOpponentAction,
43255
+ playerPayoff: lastPayoff,
43256
+ opponentPayoff: opponentPayoffEntry?.opponentPayoff ?? 0
43257
+ }
43258
+ ]);
43259
+ }
43260
+ prevRoundRef.current = currentRound;
43261
+ }, [currentRound, lastPlayerAction, lastOpponentAction, lastPayoff, payoffMatrix]);
43262
+ const opponentTotal = React77.useMemo(() => history.reduce((s, r) => s + r.opponentPayoff, 0), [history]);
43232
43263
  const handleAction = React77.useCallback((actionId) => {
43233
43264
  if (isComplete) return;
43234
- const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
43235
- const payoff = payoffMatrix.find(
43236
- (p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
43237
- );
43238
- const playerPayoff = payoff?.playerPayoff ?? 0;
43239
- setHistory((prev) => [
43240
- ...prev,
43241
- {
43242
- round: prev.length + 1,
43243
- playerAction: actionId,
43244
- opponentAction,
43245
- playerPayoff,
43246
- opponentPayoff: payoff?.opponentPayoff ?? 0
43247
- }
43248
- ]);
43249
43265
  if (playRoundEvent) {
43250
- emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: playerPayoff });
43266
+ emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: 0 });
43251
43267
  }
43252
43268
  if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
43253
43269
  if (finishEvent) {
@@ -43257,10 +43273,11 @@ function NegotiatorBoard({
43257
43273
  setShowHint(true);
43258
43274
  }
43259
43275
  }
43260
- }, [isComplete, resolved, totalRounds, currentRound, actions, payoffMatrix, history, playRoundEvent, finishEvent, emit]);
43276
+ }, [isComplete, resolved, totalRounds, currentRound, playRoundEvent, finishEvent, emit]);
43261
43277
  const handleReset = React77.useCallback(() => {
43262
43278
  setHistory([]);
43263
43279
  setShowHint(false);
43280
+ prevRoundRef.current = 0;
43264
43281
  if (playAgainEvent) {
43265
43282
  emit(`UI:${playAgainEvent}`, {});
43266
43283
  }
@@ -43494,6 +43511,7 @@ var init_PricingPageTemplate = __esm({
43494
43511
  }
43495
43512
  });
43496
43513
  function ResourceCounter({
43514
+ assetUrl = DEFAULT_ASSET_URL5,
43497
43515
  icon,
43498
43516
  label = "Gold",
43499
43517
  value = 250,
@@ -43513,7 +43531,17 @@ function ResourceCounter({
43513
43531
  className
43514
43532
  ),
43515
43533
  children: [
43516
- icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon }) }),
43534
+ assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
43535
+ "img",
43536
+ {
43537
+ src: assetUrl,
43538
+ alt: label,
43539
+ width: sizes.img,
43540
+ height: sizes.img,
43541
+ style: { imageRendering: "pixelated", objectFit: "contain" },
43542
+ className: "flex-shrink-0"
43543
+ }
43544
+ ) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon }) }) : null,
43517
43545
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: label }),
43518
43546
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("font-bold tabular-nums", color && (color in colorTokenClasses2 ? colorTokenClasses2[color] : color)), children: [
43519
43547
  value,
@@ -43526,7 +43554,7 @@ function ResourceCounter({
43526
43554
  }
43527
43555
  );
43528
43556
  }
43529
- var colorTokenClasses2, sizeMap15;
43557
+ var colorTokenClasses2, DEFAULT_ASSET_URL5, sizeMap15;
43530
43558
  var init_ResourceCounter = __esm({
43531
43559
  "components/game/atoms/ResourceCounter.tsx"() {
43532
43560
  init_cn();
@@ -43539,10 +43567,11 @@ var init_ResourceCounter = __esm({
43539
43567
  error: "text-error",
43540
43568
  muted: "text-muted-foreground"
43541
43569
  };
43570
+ DEFAULT_ASSET_URL5 = "https://almadar-kflow-assets.web.app/shared/world-map/gold_mine.png";
43542
43571
  sizeMap15 = {
43543
- sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm" },
43544
- md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base" },
43545
- lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg" }
43572
+ sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm", img: 16 },
43573
+ md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base", img: 20 },
43574
+ lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg", img: 28 }
43546
43575
  };
43547
43576
  ResourceCounter.displayName = "ResourceCounter";
43548
43577
  }
@@ -45650,17 +45679,24 @@ function SequencerBoard({
45650
45679
  const { emit } = useEventBus();
45651
45680
  const { t } = hooks.useTranslate();
45652
45681
  const resolved = boardEntity(entity);
45653
- const maxSlots = num(resolved?.maxSlots);
45682
+ const maxSlots = num(resolved?.maxSlots) || 3;
45654
45683
  const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
45655
45684
  const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
45656
45685
  const allowDuplicates = resolved?.allowDuplicates !== false;
45686
+ const entitySlots = Array.isArray(resolved?.slots) ? resolved.slots : [];
45687
+ const entityResult = str(resolved?.result);
45688
+ const entityAttempts = num(resolved?.attempts);
45689
+ const entityCurrentStep = typeof resolved?.currentStep === "number" ? resolved.currentStep : -1;
45690
+ const slots = Array.from({ length: maxSlots }, (_, i) => {
45691
+ const entitySlot = entitySlots.find((s) => s.index === i);
45692
+ if (!entitySlot?.placedActionId) return void 0;
45693
+ return availableActions.find((a) => a.id === entitySlot.placedActionId);
45694
+ });
45695
+ const isSuccess = entityResult === "win";
45696
+ const attempts = entityAttempts;
45697
+ const isPlayingBack = entityCurrentStep >= 0 && !isSuccess;
45698
+ const currentStep = entityCurrentStep;
45657
45699
  const [headerError, setHeaderError] = React77.useState(false);
45658
- const [slots, setSlots] = React77.useState(
45659
- () => Array.from({ length: maxSlots }, () => void 0)
45660
- );
45661
- const [playState, setPlayState] = React77.useState("idle");
45662
- const [currentStep, setCurrentStep] = React77.useState(-1);
45663
- const [attempts, setAttempts] = React77.useState(0);
45664
45700
  const [slotFeedback, setSlotFeedback] = React77.useState(
45665
45701
  () => Array.from({ length: maxSlots }, () => null)
45666
45702
  );
@@ -45669,11 +45705,6 @@ function SequencerBoard({
45669
45705
  if (timerRef.current) clearTimeout(timerRef.current);
45670
45706
  }, []);
45671
45707
  const handleSlotDrop = React77.useCallback((index, item) => {
45672
- setSlots((prev) => {
45673
- const next = [...prev];
45674
- next[index] = item;
45675
- return next;
45676
- });
45677
45708
  setSlotFeedback((prev) => {
45678
45709
  const next = [...prev];
45679
45710
  next[index] = null;
@@ -45683,11 +45714,6 @@ function SequencerBoard({
45683
45714
  if (placeEvent) emit(`UI:${placeEvent}`, { slotIndex: index, actionId: item.id });
45684
45715
  }, [emit, placeEvent]);
45685
45716
  const handleSlotRemove = React77.useCallback((index) => {
45686
- setSlots((prev) => {
45687
- const next = [...prev];
45688
- next[index] = void 0;
45689
- return next;
45690
- });
45691
45717
  setSlotFeedback((prev) => {
45692
45718
  const next = [...prev];
45693
45719
  next[index] = null;
@@ -45698,48 +45724,34 @@ function SequencerBoard({
45698
45724
  }, [emit, removeEvent]);
45699
45725
  const handleReset = React77.useCallback(() => {
45700
45726
  if (timerRef.current) clearTimeout(timerRef.current);
45701
- setSlots(Array.from({ length: maxSlots }, () => void 0));
45702
- setPlayState("idle");
45703
- setCurrentStep(-1);
45704
- setAttempts(0);
45705
45727
  setSlotFeedback(Array.from({ length: maxSlots }, () => null));
45706
45728
  if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
45707
45729
  }, [maxSlots, playAgainEvent, emit]);
45708
45730
  const filledSlots = slots.filter((s) => !!s);
45709
- const canPlay = filledSlots.length > 0 && playState === "idle";
45731
+ const canPlay = filledSlots.length > 0 && !isPlayingBack && !isSuccess;
45710
45732
  const handlePlay = React77.useCallback(() => {
45711
45733
  if (!canPlay) return;
45712
45734
  setSlotFeedback(Array.from({ length: maxSlots }, () => null));
45713
45735
  emit("UI:PLAY_SOUND", { key: "confirm" });
45714
45736
  const sequence = slots.map((s) => s?.id || "");
45715
- if (playEvent) {
45716
- emit(`UI:${playEvent}`, { sequence });
45717
- }
45718
- setPlayState("playing");
45719
- setCurrentStep(0);
45737
+ const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
45738
+ if (playEvent) emit(`UI:${playEvent}`, { sequence });
45720
45739
  let step = 0;
45721
45740
  const advance = () => {
45722
45741
  step++;
45742
+ emit("UI:STEP", { step });
45723
45743
  if (step >= maxSlots) {
45744
+ if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
45724
45745
  const playerSeq = slots.map((s) => s?.id);
45725
- const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
45726
45746
  const success = solutions.some(
45727
45747
  (sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
45728
45748
  );
45729
- if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
45730
45749
  if (success) {
45731
- setPlayState("success");
45732
- setCurrentStep(-1);
45733
45750
  emit("UI:PLAY_SOUND", { key: "levelComplete" });
45734
- if (completeEvent) {
45735
- emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
45736
- }
45751
+ if (completeEvent) emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
45737
45752
  } else {
45738
- setAttempts((prev) => prev + 1);
45739
45753
  const feedback = computeSlotFeedback(playerSeq, solutions);
45740
45754
  setSlotFeedback(feedback);
45741
- setPlayState("idle");
45742
- setCurrentStep(-1);
45743
45755
  emit("UI:PLAY_SOUND", { key: "fail" });
45744
45756
  const correctCount2 = feedback.filter((f3) => f3 === "correct").length;
45745
45757
  for (let ci = 0; ci < correctCount2; ci++) {
@@ -45749,7 +45761,6 @@ function SequencerBoard({
45749
45761
  }
45750
45762
  }
45751
45763
  } else {
45752
- setCurrentStep(step);
45753
45764
  timerRef.current = setTimeout(advance, stepDurationMs);
45754
45765
  }
45755
45766
  };
@@ -45799,7 +45810,7 @@ function SequencerBoard({
45799
45810
  /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", children: [
45800
45811
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { className: "items-center justify-between", children: [
45801
45812
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
45802
- hasFeedback && playState === "idle" && /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: [
45813
+ hasFeedback && !isPlayingBack && !isSuccess && /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: [
45803
45814
  `${correctCount}/${maxSlots} `,
45804
45815
  "\u2705"
45805
45816
  ] })
@@ -45811,7 +45822,7 @@ function SequencerBoard({
45811
45822
  maxSlots,
45812
45823
  onSlotDrop: handleSlotDrop,
45813
45824
  onSlotRemove: handleSlotRemove,
45814
- playing: playState === "playing",
45825
+ playing: isPlayingBack,
45815
45826
  currentStep,
45816
45827
  categoryColors,
45817
45828
  slotFeedback,
@@ -45819,7 +45830,7 @@ function SequencerBoard({
45819
45830
  }
45820
45831
  )
45821
45832
  ] }),
45822
- playState !== "playing" && /* @__PURE__ */ jsxRuntime.jsx(
45833
+ !isPlayingBack && /* @__PURE__ */ jsxRuntime.jsx(
45823
45834
  ActionPalette,
45824
45835
  {
45825
45836
  actions: availableActions,
@@ -45829,8 +45840,8 @@ function SequencerBoard({
45829
45840
  label: t("sequencer.dragActions")
45830
45841
  }
45831
45842
  ),
45832
- hasFeedback && playState === "idle" && attempts > 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-3 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-foreground", children: t(encourageKey) }) }),
45833
- playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("sequencer.levelComplete") }) }),
45843
+ hasFeedback && !isPlayingBack && !isSuccess && attempts > 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-3 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-foreground", children: t(encourageKey) }) }),
45844
+ isSuccess && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("sequencer.levelComplete") }) }),
45834
45845
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", children: [
45835
45846
  /* @__PURE__ */ jsxRuntime.jsx(
45836
45847
  exports.Button,
@@ -46991,6 +47002,9 @@ function StateArchitectBoard({
46991
47002
  stepDurationMs = 600,
46992
47003
  testEvent,
46993
47004
  completeEvent,
47005
+ addTransitionEvent,
47006
+ removeTransitionEvent,
47007
+ playAgainEvent,
46994
47008
  className
46995
47009
  }) {
46996
47010
  const { emit } = useEventBus();
@@ -47003,14 +47017,16 @@ function StateArchitectBoard({
47003
47017
  const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
47004
47018
  const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
47005
47019
  const entityVariables = rows(resolved?.variables);
47006
- const [transitions, setTransitions] = React77.useState(entityTransitions);
47020
+ const transitions = entityTransitions;
47021
+ const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
47022
+ const entityResult = str(resolved?.result) || "none";
47023
+ const isSuccess = entityResult === "win";
47024
+ const [isTesting, setIsTesting] = React77.useState(false);
47007
47025
  const [headerError, setHeaderError] = React77.useState(false);
47008
- const [playState, setPlayState] = React77.useState("editing");
47009
47026
  const [currentState, setCurrentState] = React77.useState(initialState);
47010
47027
  const [selectedState, setSelectedState] = React77.useState(null);
47011
47028
  const [testResults, setTestResults] = React77.useState([]);
47012
47029
  const [variables, setVariables] = React77.useState(() => [...entityVariables]);
47013
- const [attempts, setAttempts] = React77.useState(0);
47014
47030
  const timerRef = React77.useRef(null);
47015
47031
  const [addingFrom, setAddingFrom] = React77.useState(null);
47016
47032
  React77.useEffect(() => () => {
@@ -47020,7 +47036,7 @@ function StateArchitectBoard({
47020
47036
  const GRAPH_H = 400;
47021
47037
  const positions = React77.useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
47022
47038
  const handleStateClick = React77.useCallback((state) => {
47023
- if (playState !== "editing") return;
47039
+ if (isTesting) return;
47024
47040
  if (addingFrom) {
47025
47041
  if (addingFrom !== state) {
47026
47042
  const event = availableEvents[0] || "EVENT";
@@ -47030,20 +47046,20 @@ function StateArchitectBoard({
47030
47046
  to: state,
47031
47047
  event
47032
47048
  };
47033
- setTransitions((prev) => [...prev, newTrans]);
47049
+ if (addTransitionEvent) emit(`UI:${addTransitionEvent}`, { id: newTrans.id, from: newTrans.from, to: newTrans.to, event: newTrans.event });
47034
47050
  }
47035
47051
  setAddingFrom(null);
47036
47052
  } else {
47037
47053
  setSelectedState(state);
47038
47054
  }
47039
- }, [playState, addingFrom, availableEvents]);
47055
+ }, [isTesting, addingFrom, availableEvents, addTransitionEvent, emit]);
47040
47056
  const handleStartAddTransition = React77.useCallback(() => {
47041
47057
  if (!selectedState) return;
47042
47058
  setAddingFrom(selectedState);
47043
47059
  }, [selectedState]);
47044
47060
  const handleRemoveTransition = React77.useCallback((transId) => {
47045
- setTransitions((prev) => prev.filter((t2) => t2.id !== transId));
47046
- }, []);
47061
+ if (removeTransitionEvent) emit(`UI:${removeTransitionEvent}`, { id: transId });
47062
+ }, [removeTransitionEvent, emit]);
47047
47063
  const machine = React77.useMemo(() => ({
47048
47064
  name: entityName,
47049
47065
  description: str(resolved?.description),
@@ -47057,16 +47073,16 @@ function StateArchitectBoard({
47057
47073
  }))
47058
47074
  }), [entityName, resolved, entityStates, currentState, transitions]);
47059
47075
  const handleTest = React77.useCallback(() => {
47060
- if (playState !== "editing") return;
47076
+ if (isTesting) return;
47061
47077
  if (testEvent) emit(`UI:${testEvent}`, {});
47062
- setPlayState("testing");
47078
+ setIsTesting(true);
47063
47079
  setTestResults([]);
47064
47080
  const results = [];
47065
47081
  let testIdx = 0;
47066
47082
  const runNextTest = () => {
47067
47083
  if (testIdx >= testCases.length) {
47068
47084
  const allPassed = results.every((r) => r.passed);
47069
- setPlayState(allPassed ? "success" : "fail");
47085
+ setIsTesting(false);
47070
47086
  setTestResults(results);
47071
47087
  if (allPassed && completeEvent) {
47072
47088
  emit(`UI:${completeEvent}`, {
@@ -47074,9 +47090,6 @@ function StateArchitectBoard({
47074
47090
  passedTests: results.filter((r) => r.passed).length
47075
47091
  });
47076
47092
  }
47077
- if (!allPassed) {
47078
- setAttempts((prev) => prev + 1);
47079
- }
47080
47093
  return;
47081
47094
  }
47082
47095
  const testCase = testCases[testIdx];
@@ -47099,24 +47112,23 @@ function StateArchitectBoard({
47099
47112
  timerRef.current = setTimeout(runNextTest, stepDurationMs);
47100
47113
  };
47101
47114
  timerRef.current = setTimeout(runNextTest, stepDurationMs);
47102
- }, [playState, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
47115
+ }, [isTesting, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
47103
47116
  const handleTryAgain = React77.useCallback(() => {
47104
47117
  if (timerRef.current) clearTimeout(timerRef.current);
47105
- setPlayState("editing");
47118
+ setIsTesting(false);
47106
47119
  setCurrentState(initialState);
47107
47120
  setTestResults([]);
47108
47121
  }, [initialState]);
47109
47122
  const handleReset = React77.useCallback(() => {
47110
47123
  if (timerRef.current) clearTimeout(timerRef.current);
47111
- setTransitions(entityTransitions);
47112
- setPlayState("editing");
47124
+ setIsTesting(false);
47113
47125
  setCurrentState(initialState);
47114
47126
  setTestResults([]);
47115
47127
  setVariables([...entityVariables]);
47116
47128
  setSelectedState(null);
47117
47129
  setAddingFrom(null);
47118
- setAttempts(0);
47119
- }, [entityTransitions, initialState, entityVariables]);
47130
+ if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
47131
+ }, [initialState, entityVariables, playAgainEvent, emit]);
47120
47132
  const codeData = React77.useMemo(() => ({
47121
47133
  name: entityName,
47122
47134
  states: entityStates,
@@ -47212,7 +47224,7 @@ function StateArchitectBoard({
47212
47224
  ]
47213
47225
  }
47214
47226
  ),
47215
- playState === "editing" && /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "sm", children: /* @__PURE__ */ jsxRuntime.jsx(
47227
+ !isTesting && /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "sm", children: /* @__PURE__ */ jsxRuntime.jsx(
47216
47228
  exports.Button,
47217
47229
  {
47218
47230
  variant: "ghost",
@@ -47234,7 +47246,7 @@ function StateArchitectBoard({
47234
47246
  t2.guardHint,
47235
47247
  ")"
47236
47248
  ] }),
47237
- playState === "editing" && /* @__PURE__ */ jsxRuntime.jsx(
47249
+ !isTesting && /* @__PURE__ */ jsxRuntime.jsx(
47238
47250
  exports.Button,
47239
47251
  {
47240
47252
  variant: "ghost",
@@ -47266,21 +47278,21 @@ function StateArchitectBoard({
47266
47278
  resolved.showCodeView !== false && /* @__PURE__ */ jsxRuntime.jsx(StateJsonView, { data: codeData, label: "View Code" })
47267
47279
  ] })
47268
47280
  ] }),
47269
- playState === "success" && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("stateArchitect.allPassed") }) }),
47270
- playState === "fail" && /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", children: [
47281
+ isSuccess && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("stateArchitect.allPassed") }) }),
47282
+ !isTesting && !isSuccess && testResults.some((r) => !r.passed) && /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", children: [
47271
47283
  /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body1", className: "text-foreground font-medium", children: t(ENCOURAGEMENT_KEYS3[Math.min(attempts - 1, ENCOURAGEMENT_KEYS3.length - 1)] ?? ENCOURAGEMENT_KEYS3[0]) }) }),
47272
- attempts >= 3 && hint && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { className: "items-start", gap: "xs", children: [
47284
+ !isSuccess && attempts >= 2 && Boolean(str(resolved?.hint)) && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { className: "items-start", gap: "xs", children: [
47273
47285
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
47274
47286
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-foreground", children: hint })
47275
47287
  ] }) })
47276
47288
  ] }),
47277
47289
  /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", children: [
47278
- playState === "fail" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsxRuntime.jsx(
47290
+ !isTesting && !isSuccess && testResults.some((r) => !r.passed) ? /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsxRuntime.jsx(
47279
47291
  exports.Button,
47280
47292
  {
47281
47293
  variant: "primary",
47282
47294
  onClick: handleTest,
47283
- disabled: playState !== "editing",
47295
+ disabled: isTesting,
47284
47296
  children: "\u25B6 " + t("game.runTests")
47285
47297
  }
47286
47298
  ),
@@ -47360,6 +47372,7 @@ function formatDuration(seconds) {
47360
47372
  return `${Math.round(seconds)}s`;
47361
47373
  }
47362
47374
  function StatusEffect({
47375
+ assetUrl = DEFAULT_ASSET_URL6,
47363
47376
  icon = "shield",
47364
47377
  label = "Shield",
47365
47378
  duration = 30,
@@ -47380,7 +47393,17 @@ function StatusEffect({
47380
47393
  ),
47381
47394
  title: label,
47382
47395
  children: [
47383
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon, size: "sm" }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon, size: "sm" }) }),
47396
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
47397
+ "img",
47398
+ {
47399
+ src: assetUrl,
47400
+ alt: label,
47401
+ width: sizes.img,
47402
+ height: sizes.img,
47403
+ style: { imageRendering: "pixelated", objectFit: "contain" },
47404
+ className: "flex-shrink-0"
47405
+ }
47406
+ ) : typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon, size: "sm" }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon, size: "sm" }) }),
47384
47407
  duration !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
47385
47408
  "span",
47386
47409
  {
@@ -47407,15 +47430,16 @@ function StatusEffect({
47407
47430
  label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground mt-0.5 text-center whitespace-nowrap", children: label })
47408
47431
  ] });
47409
47432
  }
47410
- var sizeMap16, variantStyles9;
47433
+ var DEFAULT_ASSET_URL6, sizeMap16, variantStyles9;
47411
47434
  var init_StatusEffect = __esm({
47412
47435
  "components/game/atoms/StatusEffect.tsx"() {
47413
47436
  init_cn();
47414
47437
  init_Icon();
47438
+ DEFAULT_ASSET_URL6 = "https://almadar-kflow-assets.web.app/shared/effects/particles/flame_01.png";
47415
47439
  sizeMap16 = {
47416
- sm: { container: "w-8 h-8", icon: "text-sm", badge: "text-xs -top-1 -right-1 w-4 h-4", timer: "text-[9px]" },
47417
- md: { container: "w-10 h-10", icon: "text-base", badge: "text-xs -top-1 -right-1 w-5 h-5", timer: "text-xs" },
47418
- lg: { container: "w-12 h-12", icon: "text-lg", badge: "text-sm -top-1.5 -right-1.5 w-6 h-6", timer: "text-xs" }
47440
+ 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
+ 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 },
47442
+ lg: { container: "w-12 h-12", icon: "text-lg", badge: "text-sm -top-1.5 -right-1.5 w-6 h-6", timer: "text-xs", img: 36 }
47419
47443
  };
47420
47444
  variantStyles9 = {
47421
47445
  buff: "border-success bg-success/20",
@@ -48106,17 +48130,12 @@ var init_UncontrolledBattleBoard = __esm({
48106
48130
  }
48107
48131
  });
48108
48132
  function heroPosition(h) {
48133
+ if ("position" in h && h.position != null) return h.position;
48109
48134
  return vec2(h.position);
48110
48135
  }
48111
- function heroOwner(h) {
48112
- return str(h.owner);
48113
- }
48114
48136
  function heroMovement(h) {
48115
48137
  return num(h.movement);
48116
48138
  }
48117
- function hexPassable(h) {
48118
- return h.passable !== false;
48119
- }
48120
48139
  function defaultIsInRange(from, to, range) {
48121
48140
  return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
48122
48141
  }
@@ -48152,38 +48171,41 @@ function WorldMapBoard({
48152
48171
  }) {
48153
48172
  const eventBus = useEventBus();
48154
48173
  const resolved = boardEntity(entity);
48155
- const hexes = rows(resolved?.hexes);
48156
- const heroes = rows(resolved?.heroes);
48174
+ const entityUnits = rows(resolved?.units);
48175
+ const entityTiles = rows(resolved?.tiles);
48157
48176
  const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
48158
48177
  const selectedHeroId = resolved?.selectedHeroId ?? null;
48159
48178
  const assetManifest = propAssetManifest ?? resolved?.assetManifest;
48160
48179
  const backgroundImage = resolved?.backgroundImage;
48161
48180
  const [hoveredTile, setHoveredTile] = React77.useState(null);
48162
- const selectedHero = React77.useMemo(
48163
- () => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
48164
- [heroes, selectedHeroId]
48165
- );
48166
48181
  const derivedTiles = React77.useMemo(
48167
- () => hexes.map((hex) => ({
48168
- x: num(hex.x),
48169
- y: num(hex.y),
48170
- terrain: str(hex.terrain),
48171
- terrainSprite: hex.terrainSprite == null ? void 0 : str(hex.terrainSprite)
48182
+ () => entityTiles.map((t) => ({
48183
+ x: num(t.x),
48184
+ y: num(t.y),
48185
+ terrain: str(t.terrain),
48186
+ terrainSprite: t.terrainSprite == null ? void 0 : str(t.terrainSprite),
48187
+ passable: t.passable !== false
48172
48188
  })),
48173
- [hexes]
48189
+ [entityTiles]
48174
48190
  );
48175
48191
  const tiles = propTiles ?? derivedTiles;
48176
48192
  const baseUnits = React77.useMemo(
48177
- () => propUnits ?? heroes.map((hero) => ({
48178
- id: str(hero.id),
48179
- position: heroPosition(hero),
48180
- name: str(hero.name),
48181
- team: heroOwner(hero) === "enemy" ? "enemy" : "player",
48182
- health: 100,
48183
- maxHealth: 100,
48184
- sprite: hero.sprite == null ? void 0 : str(hero.sprite)
48193
+ () => propUnits ?? entityUnits.map((u) => ({
48194
+ id: str(u.id),
48195
+ position: heroPosition(u),
48196
+ name: str(u.name),
48197
+ // lolo uses `team` field (not `owner`)
48198
+ team: str(u.team) === "enemy" ? "enemy" : "player",
48199
+ health: num(u.health) || 100,
48200
+ maxHealth: num(u.maxHealth) || 100,
48201
+ sprite: u.sprite == null ? void 0 : str(u.sprite)
48185
48202
  })),
48186
- [heroes, propUnits]
48203
+ [entityUnits, propUnits]
48204
+ );
48205
+ const gameUnits = baseUnits;
48206
+ const selectedHero = React77.useMemo(
48207
+ () => gameUnits.find((u) => u.id === selectedHeroId) ?? null,
48208
+ [gameUnits, selectedHeroId]
48187
48209
  );
48188
48210
  const MOVE_SPEED_MS_PER_TILE = 300;
48189
48211
  const movementAnimRef = React77.useRef(null);
@@ -48229,51 +48251,50 @@ function WorldMapBoard({
48229
48251
  const validMoves = React77.useMemo(() => {
48230
48252
  if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
48231
48253
  const sp = heroPosition(selectedHero);
48232
- const sOwner = heroOwner(selectedHero);
48254
+ const sTeam = str(selectedHero.team);
48233
48255
  const range = heroMovement(selectedHero);
48234
48256
  const moves = [];
48235
- hexes.forEach((hex) => {
48236
- const hx = num(hex.x);
48237
- const hy = num(hex.y);
48238
- if (!hexPassable(hex)) return;
48239
- if (hx === sp.x && hy === sp.y) return;
48240
- if (!isInRange(sp, { x: hx, y: hy }, range)) return;
48241
- if (heroes.some((h) => {
48242
- const hp = heroPosition(h);
48243
- return hp.x === hx && hp.y === hy && heroOwner(h) === sOwner;
48257
+ tiles.forEach((t) => {
48258
+ const tx = t.x;
48259
+ const ty = t.y;
48260
+ if (t.passable === false) return;
48261
+ if (tx === sp.x && ty === sp.y) return;
48262
+ if (!isInRange(sp, { x: tx, y: ty }, range)) return;
48263
+ if (gameUnits.some((u) => {
48264
+ const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
48265
+ return up.x === tx && up.y === ty && str(u.team) === sTeam;
48244
48266
  })) return;
48245
- moves.push({ x: hx, y: hy });
48267
+ moves.push({ x: tx, y: ty });
48246
48268
  });
48247
48269
  return moves;
48248
- }, [selectedHero, hexes, heroes, isInRange]);
48270
+ }, [selectedHero, tiles, gameUnits, isInRange]);
48249
48271
  const attackTargets = React77.useMemo(() => {
48250
48272
  if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
48251
48273
  const sp = heroPosition(selectedHero);
48252
- const sOwner = heroOwner(selectedHero);
48274
+ const sTeam = str(selectedHero.team);
48253
48275
  const range = heroMovement(selectedHero);
48254
- return heroes.filter((h) => heroOwner(h) !== sOwner).filter((h) => isInRange(sp, heroPosition(h), range)).map((h) => heroPosition(h));
48255
- }, [selectedHero, heroes, isInRange]);
48256
- const maxY = Math.max(...hexes.map((h) => num(h.y)), 0);
48276
+ return gameUnits.filter((u) => str(u.team) !== sTeam).filter((u) => isInRange(sp, u.position ?? { x: u.x ?? -1, y: u.y ?? -1 }, range)).map((u) => u.position ?? { x: u.x ?? -1, y: u.y ?? -1 });
48277
+ }, [selectedHero, gameUnits, isInRange]);
48278
+ const maxY = Math.max(...tiles.map((t) => t.y), 0);
48257
48279
  const baseOffsetX = (maxY + 1) * (exports.TILE_WIDTH * scale / 2);
48258
48280
  const tileToScreen = React77.useCallback(
48259
48281
  (tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
48260
48282
  [scale, baseOffsetX]
48261
48283
  );
48262
48284
  const hoveredHex = React77.useMemo(
48263
- () => hoveredTile ? hexes.find((h) => num(h.x) === hoveredTile.x && num(h.y) === hoveredTile.y) ?? null : null,
48264
- [hoveredTile, hexes]
48285
+ () => hoveredTile ? tiles.find((t) => t.x === hoveredTile.x && t.y === hoveredTile.y) ?? null : null,
48286
+ [hoveredTile, tiles]
48265
48287
  );
48266
48288
  const hoveredHero = React77.useMemo(
48267
- () => hoveredTile ? heroes.find((h) => {
48268
- const hp = heroPosition(h);
48269
- return hp.x === hoveredTile.x && hp.y === hoveredTile.y;
48289
+ () => hoveredTile ? gameUnits.find((u) => {
48290
+ const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
48291
+ return up.x === hoveredTile.x && up.y === hoveredTile.y;
48270
48292
  }) ?? null : null,
48271
- [hoveredTile, heroes]
48293
+ [hoveredTile, gameUnits]
48272
48294
  );
48273
48295
  const handleTileClick = React77.useCallback((x, y) => {
48274
48296
  if (movementAnimRef.current) return;
48275
- const hex = hexes.find((h) => num(h.x) === x && num(h.y) === y);
48276
- if (!hex) return;
48297
+ const tile = tiles.find((t) => t.x === x && t.y === y);
48277
48298
  if (tileClickEvent) {
48278
48299
  eventBus.emit(`UI:${tileClickEvent}`, { x, y });
48279
48300
  }
@@ -48284,38 +48305,39 @@ function WorldMapBoard({
48284
48305
  if (heroMoveEvent) {
48285
48306
  eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
48286
48307
  }
48287
- const feature = str(hex.feature);
48308
+ const feature = tile ? str(tile.feature) : "";
48288
48309
  if (feature && feature !== "none") {
48289
- onFeatureEnter?.(heroId, hex);
48310
+ const tileRow = tile;
48311
+ onFeatureEnter?.(heroId, tileRow);
48290
48312
  if (featureEnterEvent) {
48291
- eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
48313
+ eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex: tileRow });
48292
48314
  }
48293
48315
  }
48294
48316
  });
48295
48317
  return;
48296
48318
  }
48297
- const enemy = heroes.find((h) => {
48298
- const hp = heroPosition(h);
48299
- return hp.x === x && hp.y === y && heroOwner(h) === "enemy";
48319
+ const enemy = gameUnits.find((u) => {
48320
+ const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
48321
+ return up.x === x && up.y === y && str(u.team) === "enemy";
48300
48322
  });
48301
48323
  if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
48302
48324
  const attackerId = str(selectedHero.id);
48303
- const defenderId = str(enemy.id);
48325
+ const defenderId = enemy.id;
48304
48326
  onBattleEncounter?.(attackerId, defenderId);
48305
48327
  if (battleEncounterEvent) {
48306
48328
  eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
48307
48329
  }
48308
48330
  }
48309
- }, [hexes, heroes, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
48331
+ }, [tiles, gameUnits, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
48310
48332
  const handleUnitClick = React77.useCallback((unitId) => {
48311
- const hero = heroes.find((h) => str(h.id) === unitId);
48312
- if (hero && (heroOwner(hero) === "player" || allowMoveAllHeroes)) {
48333
+ const unit = gameUnits.find((u) => u.id === unitId);
48334
+ if (unit && (str(unit.team) === "player" || allowMoveAllHeroes)) {
48313
48335
  onHeroSelect?.(unitId);
48314
48336
  if (heroSelectEvent) {
48315
48337
  eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
48316
48338
  }
48317
48339
  }
48318
- }, [heroes, onHeroSelect, allowMoveAllHeroes, eventBus, heroSelectEvent]);
48340
+ }, [gameUnits, onHeroSelect, allowMoveAllHeroes, eventBus, heroSelectEvent]);
48319
48341
  const selectHero = React77.useCallback((id) => {
48320
48342
  onHeroSelect?.(id);
48321
48343
  if (heroSelectEvent) {