@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.
- package/dist/avl/index.cjs +338 -316
- package/dist/avl/index.js +338 -316
- package/dist/components/game/atoms/ComboCounter.d.ts +4 -1
- package/dist/components/game/atoms/ResourceCounter.d.ts +4 -1
- package/dist/components/game/atoms/StateIndicator.d.ts +4 -7
- package/dist/components/game/atoms/StatusEffect.d.ts +4 -1
- package/dist/components/game/atoms/WaypointMarker.d.ts +4 -1
- package/dist/components/game/molecules/three/index.cjs +7 -13
- package/dist/components/game/molecules/three/index.css +0 -5
- package/dist/components/game/molecules/three/index.js +7 -13
- package/dist/components/game/organisms/CastleBoard.d.ts +10 -1
- package/dist/components/game/organisms/puzzles/event-handler/EventHandlerBoard.d.ts +9 -1
- package/dist/components/game/organisms/puzzles/state-architect/StateArchitectBoard.d.ts +14 -1
- package/dist/components/index.cjs +338 -316
- package/dist/components/index.js +338 -316
- package/dist/providers/index.cjs +338 -316
- package/dist/providers/index.js +338 -316
- package/dist/runtime/index.cjs +338 -316
- package/dist/runtime/index.js +338 -316
- package/package.json +1 -1
- package/tailwind-preset.cjs +1 -0
package/dist/runtime/index.cjs
CHANGED
|
@@ -7324,6 +7324,7 @@ var init_Sprite = __esm({
|
|
|
7324
7324
|
}
|
|
7325
7325
|
});
|
|
7326
7326
|
function StateIndicator({
|
|
7327
|
+
assetUrl = DEFAULT_ASSET_URL,
|
|
7327
7328
|
state = "idle",
|
|
7328
7329
|
label,
|
|
7329
7330
|
size = "md",
|
|
@@ -7346,18 +7347,29 @@ function StateIndicator({
|
|
|
7346
7347
|
className
|
|
7347
7348
|
),
|
|
7348
7349
|
children: [
|
|
7349
|
-
/* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", children:
|
|
7350
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7351
|
+
"img",
|
|
7352
|
+
{
|
|
7353
|
+
src: assetUrl,
|
|
7354
|
+
alt: displayLabel,
|
|
7355
|
+
width: 16,
|
|
7356
|
+
height: 16,
|
|
7357
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7358
|
+
className: "flex-shrink-0"
|
|
7359
|
+
}
|
|
7360
|
+
) : typeof config.icon === "string" ? /^[a-zA-Z0-9-]+$/.test(config.icon) ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: config.icon }) : config.icon : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: config.icon }) }),
|
|
7350
7361
|
/* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", children: displayLabel })
|
|
7351
7362
|
]
|
|
7352
7363
|
}
|
|
7353
7364
|
);
|
|
7354
7365
|
}
|
|
7355
|
-
var DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
7366
|
+
var DEFAULT_ASSET_URL, DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
7356
7367
|
var init_StateIndicator = __esm({
|
|
7357
7368
|
"components/game/atoms/StateIndicator.tsx"() {
|
|
7358
7369
|
init_Box();
|
|
7359
7370
|
init_Icon();
|
|
7360
7371
|
init_cn();
|
|
7372
|
+
DEFAULT_ASSET_URL = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
7361
7373
|
DEFAULT_STATE_STYLES = {
|
|
7362
7374
|
idle: { icon: "\u23F8", bgClass: "bg-muted" },
|
|
7363
7375
|
active: { icon: "\u25B6", bgClass: "bg-success" },
|
|
@@ -7432,6 +7444,7 @@ var init_TimerDisplay = __esm({
|
|
|
7432
7444
|
}
|
|
7433
7445
|
});
|
|
7434
7446
|
function ResourceCounter({
|
|
7447
|
+
assetUrl = DEFAULT_ASSET_URL2,
|
|
7435
7448
|
icon,
|
|
7436
7449
|
label = "Gold",
|
|
7437
7450
|
value = 250,
|
|
@@ -7451,7 +7464,17 @@ function ResourceCounter({
|
|
|
7451
7464
|
className
|
|
7452
7465
|
),
|
|
7453
7466
|
children: [
|
|
7454
|
-
|
|
7467
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7468
|
+
"img",
|
|
7469
|
+
{
|
|
7470
|
+
src: assetUrl,
|
|
7471
|
+
alt: label,
|
|
7472
|
+
width: sizes.img,
|
|
7473
|
+
height: sizes.img,
|
|
7474
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7475
|
+
className: "flex-shrink-0"
|
|
7476
|
+
}
|
|
7477
|
+
) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon }) }) : null,
|
|
7455
7478
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: label }),
|
|
7456
7479
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("font-bold tabular-nums", color && (color in colorTokenClasses2 ? colorTokenClasses2[color] : color)), children: [
|
|
7457
7480
|
value,
|
|
@@ -7464,7 +7487,7 @@ function ResourceCounter({
|
|
|
7464
7487
|
}
|
|
7465
7488
|
);
|
|
7466
7489
|
}
|
|
7467
|
-
var colorTokenClasses2, sizeMap5;
|
|
7490
|
+
var colorTokenClasses2, DEFAULT_ASSET_URL2, sizeMap5;
|
|
7468
7491
|
var init_ResourceCounter = __esm({
|
|
7469
7492
|
"components/game/atoms/ResourceCounter.tsx"() {
|
|
7470
7493
|
init_cn();
|
|
@@ -7477,16 +7500,17 @@ var init_ResourceCounter = __esm({
|
|
|
7477
7500
|
error: "text-error",
|
|
7478
7501
|
muted: "text-muted-foreground"
|
|
7479
7502
|
};
|
|
7503
|
+
DEFAULT_ASSET_URL2 = "https://almadar-kflow-assets.web.app/shared/world-map/gold_mine.png";
|
|
7480
7504
|
sizeMap5 = {
|
|
7481
|
-
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm" },
|
|
7482
|
-
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base" },
|
|
7483
|
-
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg" }
|
|
7505
|
+
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm", img: 16 },
|
|
7506
|
+
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base", img: 20 },
|
|
7507
|
+
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg", img: 28 }
|
|
7484
7508
|
};
|
|
7485
7509
|
ResourceCounter.displayName = "ResourceCounter";
|
|
7486
7510
|
}
|
|
7487
7511
|
});
|
|
7488
7512
|
function ItemSlot({
|
|
7489
|
-
assetUrl =
|
|
7513
|
+
assetUrl = DEFAULT_ASSET_URL3,
|
|
7490
7514
|
icon = "sword",
|
|
7491
7515
|
label = "Iron Sword",
|
|
7492
7516
|
quantity,
|
|
@@ -7545,7 +7569,7 @@ function ItemSlot({
|
|
|
7545
7569
|
}
|
|
7546
7570
|
);
|
|
7547
7571
|
}
|
|
7548
|
-
var sizeMap6, rarityBorderMap, rarityGlowMap,
|
|
7572
|
+
var sizeMap6, rarityBorderMap, rarityGlowMap, DEFAULT_ASSET_URL3, assetSizeMap;
|
|
7549
7573
|
var init_ItemSlot = __esm({
|
|
7550
7574
|
"components/game/atoms/ItemSlot.tsx"() {
|
|
7551
7575
|
"use client";
|
|
@@ -7570,7 +7594,7 @@ var init_ItemSlot = __esm({
|
|
|
7570
7594
|
epic: "shadow-lg",
|
|
7571
7595
|
legendary: "shadow-lg"
|
|
7572
7596
|
};
|
|
7573
|
-
|
|
7597
|
+
DEFAULT_ASSET_URL3 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
7574
7598
|
assetSizeMap = {
|
|
7575
7599
|
sm: 28,
|
|
7576
7600
|
md: 40,
|
|
@@ -7643,6 +7667,7 @@ function getComboScale(combo) {
|
|
|
7643
7667
|
return "";
|
|
7644
7668
|
}
|
|
7645
7669
|
function ComboCounter({
|
|
7670
|
+
assetUrl = DEFAULT_ASSET_URL4,
|
|
7646
7671
|
combo = 5,
|
|
7647
7672
|
multiplier,
|
|
7648
7673
|
streak,
|
|
@@ -7662,6 +7687,17 @@ function ComboCounter({
|
|
|
7662
7687
|
className
|
|
7663
7688
|
),
|
|
7664
7689
|
children: [
|
|
7690
|
+
assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7691
|
+
"img",
|
|
7692
|
+
{
|
|
7693
|
+
src: assetUrl,
|
|
7694
|
+
alt: "combo",
|
|
7695
|
+
width: 24,
|
|
7696
|
+
height: 24,
|
|
7697
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7698
|
+
className: "flex-shrink-0 mb-0.5"
|
|
7699
|
+
}
|
|
7700
|
+
),
|
|
7665
7701
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-black tabular-nums leading-none", sizes.combo, getComboIntensity(combo)), children: combo }),
|
|
7666
7702
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-bold uppercase tracking-wider text-muted-foreground", sizes.label), children: "combo" }),
|
|
7667
7703
|
multiplier != null && multiplier > 1 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("font-bold text-warning tabular-nums", sizes.multiplier), children: [
|
|
@@ -7676,10 +7712,11 @@ function ComboCounter({
|
|
|
7676
7712
|
}
|
|
7677
7713
|
);
|
|
7678
7714
|
}
|
|
7679
|
-
var sizeMap8;
|
|
7715
|
+
var DEFAULT_ASSET_URL4, sizeMap8;
|
|
7680
7716
|
var init_ComboCounter = __esm({
|
|
7681
7717
|
"components/game/atoms/ComboCounter.tsx"() {
|
|
7682
7718
|
init_cn();
|
|
7719
|
+
DEFAULT_ASSET_URL4 = "https://almadar-kflow-assets.web.app/shared/effects/flash/flash00.png";
|
|
7683
7720
|
sizeMap8 = {
|
|
7684
7721
|
sm: { combo: "text-lg", label: "text-xs", multiplier: "text-xs" },
|
|
7685
7722
|
md: { combo: "text-2xl", label: "text-xs", multiplier: "text-sm" },
|
|
@@ -7773,6 +7810,7 @@ var init_XPBar = __esm({
|
|
|
7773
7810
|
}
|
|
7774
7811
|
});
|
|
7775
7812
|
function WaypointMarker({
|
|
7813
|
+
assetUrl = DEFAULT_ASSET_URL5,
|
|
7776
7814
|
label,
|
|
7777
7815
|
icon,
|
|
7778
7816
|
active = true,
|
|
@@ -7811,7 +7849,17 @@ function WaypointMarker({
|
|
|
7811
7849
|
active && !completed && "bg-info text-foreground",
|
|
7812
7850
|
!active && !completed && "bg-muted"
|
|
7813
7851
|
),
|
|
7814
|
-
children: completed ? checkIcon :
|
|
7852
|
+
children: completed ? checkIcon : assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7853
|
+
"img",
|
|
7854
|
+
{
|
|
7855
|
+
src: assetUrl,
|
|
7856
|
+
alt: label,
|
|
7857
|
+
width: sizes.img,
|
|
7858
|
+
height: sizes.img,
|
|
7859
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7860
|
+
className: "flex-shrink-0"
|
|
7861
|
+
}
|
|
7862
|
+
) : icon ? typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon }) : null
|
|
7815
7863
|
}
|
|
7816
7864
|
)
|
|
7817
7865
|
] }),
|
|
@@ -7828,15 +7876,16 @@ function WaypointMarker({
|
|
|
7828
7876
|
)
|
|
7829
7877
|
] });
|
|
7830
7878
|
}
|
|
7831
|
-
var sizeMap10, checkIcon;
|
|
7879
|
+
var DEFAULT_ASSET_URL5, sizeMap10, checkIcon;
|
|
7832
7880
|
var init_WaypointMarker = __esm({
|
|
7833
7881
|
"components/game/atoms/WaypointMarker.tsx"() {
|
|
7834
7882
|
init_cn();
|
|
7835
7883
|
init_Icon();
|
|
7884
|
+
DEFAULT_ASSET_URL5 = "https://almadar-kflow-assets.web.app/shared/world-map/battle_marker.png";
|
|
7836
7885
|
sizeMap10 = {
|
|
7837
|
-
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1" },
|
|
7838
|
-
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5" },
|
|
7839
|
-
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2" }
|
|
7886
|
+
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1", img: 12 },
|
|
7887
|
+
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5", img: 18 },
|
|
7888
|
+
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2", img: 24 }
|
|
7840
7889
|
};
|
|
7841
7890
|
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" }) });
|
|
7842
7891
|
WaypointMarker.displayName = "WaypointMarker";
|
|
@@ -7849,6 +7898,7 @@ function formatDuration(seconds) {
|
|
|
7849
7898
|
return `${Math.round(seconds)}s`;
|
|
7850
7899
|
}
|
|
7851
7900
|
function StatusEffect({
|
|
7901
|
+
assetUrl = DEFAULT_ASSET_URL6,
|
|
7852
7902
|
icon = "shield",
|
|
7853
7903
|
label = "Shield",
|
|
7854
7904
|
duration = 30,
|
|
@@ -7869,7 +7919,17 @@ function StatusEffect({
|
|
|
7869
7919
|
),
|
|
7870
7920
|
title: label,
|
|
7871
7921
|
children: [
|
|
7872
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children:
|
|
7922
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7923
|
+
"img",
|
|
7924
|
+
{
|
|
7925
|
+
src: assetUrl,
|
|
7926
|
+
alt: label,
|
|
7927
|
+
width: sizes.img,
|
|
7928
|
+
height: sizes.img,
|
|
7929
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7930
|
+
className: "flex-shrink-0"
|
|
7931
|
+
}
|
|
7932
|
+
) : typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, size: "sm" }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, size: "sm" }) }),
|
|
7873
7933
|
duration !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7874
7934
|
"span",
|
|
7875
7935
|
{
|
|
@@ -7896,15 +7956,16 @@ function StatusEffect({
|
|
|
7896
7956
|
label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground mt-0.5 text-center whitespace-nowrap", children: label })
|
|
7897
7957
|
] });
|
|
7898
7958
|
}
|
|
7899
|
-
var sizeMap11, variantStyles7;
|
|
7959
|
+
var DEFAULT_ASSET_URL6, sizeMap11, variantStyles7;
|
|
7900
7960
|
var init_StatusEffect = __esm({
|
|
7901
7961
|
"components/game/atoms/StatusEffect.tsx"() {
|
|
7902
7962
|
init_cn();
|
|
7903
7963
|
init_Icon();
|
|
7964
|
+
DEFAULT_ASSET_URL6 = "https://almadar-kflow-assets.web.app/shared/effects/particles/flame_01.png";
|
|
7904
7965
|
sizeMap11 = {
|
|
7905
|
-
sm: { container: "w-8 h-8", icon: "text-sm", badge: "text-xs -top-1 -right-1 w-4 h-4", timer: "text-[9px]" },
|
|
7906
|
-
md: { container: "w-10 h-10", icon: "text-base", badge: "text-xs -top-1 -right-1 w-5 h-5", timer: "text-xs" },
|
|
7907
|
-
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" }
|
|
7966
|
+
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 },
|
|
7967
|
+
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 },
|
|
7968
|
+
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 }
|
|
7908
7969
|
};
|
|
7909
7970
|
variantStyles7 = {
|
|
7910
7971
|
buff: "border-success bg-success/20",
|
|
@@ -11473,15 +11534,15 @@ function BattleBoard({
|
|
|
11473
11534
|
const board = boardEntity(entity) ?? {};
|
|
11474
11535
|
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
|
|
11475
11536
|
const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
|
|
11476
|
-
const boardWidth = num(board.boardWidth, 8);
|
|
11477
|
-
const boardHeight = num(board.boardHeight, 6);
|
|
11537
|
+
const boardWidth = num(board.gridWidth ?? board.boardWidth, 8);
|
|
11538
|
+
const boardHeight = num(board.gridHeight ?? board.boardHeight, 6);
|
|
11478
11539
|
const assetManifest = propAssetManifest ?? board.assetManifest;
|
|
11479
11540
|
const backgroundImage = board.backgroundImage;
|
|
11480
11541
|
const units = rows(board.units);
|
|
11481
11542
|
const selectedUnitId = board.selectedUnitId ?? null;
|
|
11482
11543
|
const currentPhase = str(board.phase) || "observation";
|
|
11483
11544
|
const currentTurn = num(board.turn, 1);
|
|
11484
|
-
const gameResult = board.
|
|
11545
|
+
const gameResult = board.result ?? null;
|
|
11485
11546
|
const eventBus = useEventBus();
|
|
11486
11547
|
const { t } = hooks.useTranslate();
|
|
11487
11548
|
const [hoveredTile, setHoveredTile] = React82.useState(null);
|
|
@@ -11502,7 +11563,7 @@ function BattleBoard({
|
|
|
11502
11563
|
const validMoves = React82.useMemo(() => {
|
|
11503
11564
|
if (!selectedUnit || currentPhase !== "movement") return [];
|
|
11504
11565
|
const moves = [];
|
|
11505
|
-
const range = num(
|
|
11566
|
+
const range = num(board.movementRange, 2);
|
|
11506
11567
|
const origin = unitPosition(selectedUnit);
|
|
11507
11568
|
for (let dy = -range; dy <= range; dy++) {
|
|
11508
11569
|
for (let dx = -range; dx <= range; dx++) {
|
|
@@ -19787,15 +19848,24 @@ function CastleBoard({
|
|
|
19787
19848
|
featureClickEvent,
|
|
19788
19849
|
unitClickEvent,
|
|
19789
19850
|
tileClickEvent,
|
|
19851
|
+
playAgainEvent,
|
|
19790
19852
|
className
|
|
19791
19853
|
}) {
|
|
19792
19854
|
const eventBus = useEventBus();
|
|
19855
|
+
const { t } = hooks.useTranslate();
|
|
19793
19856
|
const resolved = boardEntity(entity);
|
|
19794
19857
|
const tiles = propTiles ?? (Array.isArray(resolved?.tiles) ? resolved.tiles : []);
|
|
19795
19858
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
19796
19859
|
const units = propUnits ?? (Array.isArray(resolved?.units) ? resolved.units : []);
|
|
19797
19860
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
19798
19861
|
const backgroundImage = resolved?.backgroundImage;
|
|
19862
|
+
const gold = num(resolved?.gold);
|
|
19863
|
+
const health = num(resolved?.health);
|
|
19864
|
+
const maxHealth = num(resolved?.maxHealth);
|
|
19865
|
+
const wave = num(resolved?.wave);
|
|
19866
|
+
const tickCount = num(resolved?.tickCount);
|
|
19867
|
+
const buildings = rows(resolved?.buildings);
|
|
19868
|
+
const result = str(resolved?.result) || "none";
|
|
19799
19869
|
const [hoveredTile, setHoveredTile] = React82.useState(null);
|
|
19800
19870
|
const [selectedFeature, setSelectedFeature] = React82.useState(null);
|
|
19801
19871
|
const hoveredFeature = React82.useMemo(() => {
|
|
@@ -19808,7 +19878,7 @@ function CastleBoard({
|
|
|
19808
19878
|
(u) => u.position?.x === hoveredTile.x && u.position?.y === hoveredTile.y
|
|
19809
19879
|
) ?? null;
|
|
19810
19880
|
}, [hoveredTile, units]);
|
|
19811
|
-
const maxY = Math.max(...tiles.map((
|
|
19881
|
+
const maxY = Math.max(...tiles.map((t2) => t2.y), 0);
|
|
19812
19882
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
19813
19883
|
const tileToScreen = React82.useCallback(
|
|
19814
19884
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
@@ -19843,6 +19913,11 @@ function CastleBoard({
|
|
|
19843
19913
|
}
|
|
19844
19914
|
}, [units, onUnitClick, unitClickEvent, eventBus]);
|
|
19845
19915
|
const clearSelection = React82.useCallback(() => setSelectedFeature(null), []);
|
|
19916
|
+
const handlePlayAgain = React82.useCallback(() => {
|
|
19917
|
+
if (playAgainEvent) {
|
|
19918
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
19919
|
+
}
|
|
19920
|
+
}, [playAgainEvent, eventBus]);
|
|
19846
19921
|
const ctx = React82.useMemo(
|
|
19847
19922
|
() => ({
|
|
19848
19923
|
hoveredTile,
|
|
@@ -19851,11 +19926,18 @@ function CastleBoard({
|
|
|
19851
19926
|
selectedFeature,
|
|
19852
19927
|
clearSelection,
|
|
19853
19928
|
tileToScreen,
|
|
19854
|
-
scale
|
|
19929
|
+
scale,
|
|
19930
|
+
gold,
|
|
19931
|
+
health,
|
|
19932
|
+
maxHealth,
|
|
19933
|
+
wave,
|
|
19934
|
+
tickCount,
|
|
19935
|
+
buildings,
|
|
19936
|
+
result
|
|
19855
19937
|
}),
|
|
19856
|
-
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale]
|
|
19938
|
+
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale, gold, health, maxHealth, wave, tickCount, buildings, result]
|
|
19857
19939
|
);
|
|
19858
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("castle-board min-h-screen flex flex-col bg-background", className), children: [
|
|
19940
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("castle-board relative min-h-screen flex flex-col bg-background", className), children: [
|
|
19859
19941
|
header && header(ctx),
|
|
19860
19942
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
|
|
19861
19943
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-auto p-4 relative", children: [
|
|
@@ -19880,7 +19962,29 @@ function CastleBoard({
|
|
|
19880
19962
|
] }),
|
|
19881
19963
|
sidePanel && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-96 shrink-0 border-l border-border bg-surface overflow-y-auto", children: sidePanel(ctx) })
|
|
19882
19964
|
] }),
|
|
19883
|
-
footer && footer(ctx)
|
|
19965
|
+
footer && footer(ctx),
|
|
19966
|
+
result !== "none" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
19967
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
19968
|
+
Typography,
|
|
19969
|
+
{
|
|
19970
|
+
variant: "h2",
|
|
19971
|
+
className: cn(
|
|
19972
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
19973
|
+
result === "victory" ? "text-warning" : "text-error"
|
|
19974
|
+
),
|
|
19975
|
+
children: result === "victory" ? t("battle.victory") : t("battle.defeat")
|
|
19976
|
+
}
|
|
19977
|
+
),
|
|
19978
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
19979
|
+
Button,
|
|
19980
|
+
{
|
|
19981
|
+
variant: "primary",
|
|
19982
|
+
className: "px-8 py-3 font-semibold",
|
|
19983
|
+
onClick: handlePlayAgain,
|
|
19984
|
+
children: t("battle.playAgain")
|
|
19985
|
+
}
|
|
19986
|
+
)
|
|
19987
|
+
] }) })
|
|
19884
19988
|
] });
|
|
19885
19989
|
}
|
|
19886
19990
|
var init_CastleBoard = __esm({
|
|
@@ -19888,6 +19992,10 @@ var init_CastleBoard = __esm({
|
|
|
19888
19992
|
"use client";
|
|
19889
19993
|
init_cn();
|
|
19890
19994
|
init_useEventBus();
|
|
19995
|
+
init_Box();
|
|
19996
|
+
init_Button();
|
|
19997
|
+
init_Typography();
|
|
19998
|
+
init_Stack();
|
|
19891
19999
|
init_IsometricCanvas();
|
|
19892
20000
|
init_boardEntity();
|
|
19893
20001
|
init_isometric();
|
|
@@ -20764,22 +20872,16 @@ function ClassifierBoard({
|
|
|
20764
20872
|
const { emit } = useEventBus();
|
|
20765
20873
|
const { t } = hooks.useTranslate();
|
|
20766
20874
|
const resolved = boardEntity(entity);
|
|
20767
|
-
const [localAssignments, setLocalAssignments] = React82.useState({});
|
|
20768
20875
|
const [headerError, setHeaderError] = React82.useState(false);
|
|
20769
|
-
const [localSubmitted, setLocalSubmitted] = React82.useState(false);
|
|
20770
|
-
const [localAttempts, setLocalAttempts] = React82.useState(0);
|
|
20771
|
-
const [showHint, setShowHint] = React82.useState(false);
|
|
20772
20876
|
const items = Array.isArray(resolved?.items) ? resolved.items : [];
|
|
20773
20877
|
const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
|
|
20774
|
-
const
|
|
20775
|
-
const
|
|
20776
|
-
const
|
|
20777
|
-
const assignments =
|
|
20778
|
-
if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
|
|
20878
|
+
const result = str(resolved?.result);
|
|
20879
|
+
const submitted = result === "win";
|
|
20880
|
+
const attempts = num(resolved?.attempts);
|
|
20881
|
+
const assignments = items.reduce((acc, item) => {
|
|
20882
|
+
if (item.assignedCategory != null && item.assignedCategory !== "") acc[item.id] = item.assignedCategory;
|
|
20779
20883
|
return acc;
|
|
20780
|
-
}, {})
|
|
20781
|
-
const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
|
|
20782
|
-
const submitted = entityDrivesResult || localSubmitted;
|
|
20884
|
+
}, {});
|
|
20783
20885
|
const unassignedItems = items.filter((item) => !assignments[item.id]);
|
|
20784
20886
|
const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
|
|
20785
20887
|
const results = submitted ? items.map((item) => ({
|
|
@@ -20787,54 +20889,25 @@ function ClassifierBoard({
|
|
|
20787
20889
|
assigned: assignments[item.id],
|
|
20788
20890
|
correct: assignments[item.id] === item.correctCategory
|
|
20789
20891
|
})) : [];
|
|
20790
|
-
const allCorrect =
|
|
20892
|
+
const allCorrect = result === "win";
|
|
20791
20893
|
const correctCount = results.filter((r) => r.correct).length;
|
|
20894
|
+
const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
|
|
20792
20895
|
const handleAssign = (itemId, categoryId) => {
|
|
20793
20896
|
if (submitted) return;
|
|
20794
|
-
if (assignEvent) {
|
|
20795
|
-
emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
20796
|
-
}
|
|
20797
|
-
if (!entityHasAssignments) {
|
|
20798
|
-
setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
|
|
20799
|
-
}
|
|
20897
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
20800
20898
|
};
|
|
20801
20899
|
const handleUnassign = (itemId) => {
|
|
20802
20900
|
if (submitted) return;
|
|
20803
|
-
if (
|
|
20804
|
-
setLocalAssignments((prev) => {
|
|
20805
|
-
const next = { ...prev };
|
|
20806
|
-
delete next[itemId];
|
|
20807
|
-
return next;
|
|
20808
|
-
});
|
|
20809
|
-
}
|
|
20901
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId: "" });
|
|
20810
20902
|
};
|
|
20811
|
-
const handleSubmit =
|
|
20812
|
-
if (checkEvent) {
|
|
20813
|
-
|
|
20814
|
-
|
|
20815
|
-
if (!entityDrivesResult) {
|
|
20816
|
-
setLocalSubmitted(true);
|
|
20817
|
-
setLocalAttempts((a) => a + 1);
|
|
20818
|
-
const correct = items.every((item) => assignments[item.id] === item.correctCategory);
|
|
20819
|
-
if (correct) {
|
|
20820
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
20821
|
-
}
|
|
20822
|
-
}
|
|
20823
|
-
}, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
|
|
20824
|
-
const handleReset = () => {
|
|
20825
|
-
if (!entityDrivesResult) setLocalSubmitted(false);
|
|
20826
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
20827
|
-
setShowHint(true);
|
|
20903
|
+
const handleSubmit = () => {
|
|
20904
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
20905
|
+
if (allAssigned && items.every((item) => assignments[item.id] === item.correctCategory)) {
|
|
20906
|
+
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
20828
20907
|
}
|
|
20829
20908
|
};
|
|
20830
20909
|
const handleFullReset = () => {
|
|
20831
|
-
if (playAgainEvent) {
|
|
20832
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
20833
|
-
}
|
|
20834
|
-
setLocalAssignments({});
|
|
20835
|
-
setLocalSubmitted(false);
|
|
20836
|
-
setLocalAttempts(0);
|
|
20837
|
-
setShowHint(false);
|
|
20910
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
20838
20911
|
};
|
|
20839
20912
|
if (!resolved) return null;
|
|
20840
20913
|
const theme = resolved.theme ?? void 0;
|
|
@@ -20873,17 +20946,17 @@ function ClassifierBoard({
|
|
|
20873
20946
|
/* @__PURE__ */ jsxRuntime.jsx(Badge, { size: "sm", children: catItems.length })
|
|
20874
20947
|
] }),
|
|
20875
20948
|
/* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-wrap min-h-[2rem]", children: catItems.map((item) => {
|
|
20876
|
-
const
|
|
20949
|
+
const result2 = results.find((r) => r.item.id === item.id);
|
|
20877
20950
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
20878
20951
|
Badge,
|
|
20879
20952
|
{
|
|
20880
20953
|
size: "sm",
|
|
20881
|
-
className: `cursor-pointer ${
|
|
20954
|
+
className: `cursor-pointer ${result2 ? result2.correct ? "border-success bg-success/10" : "border-error bg-error/10" : ""}`,
|
|
20882
20955
|
onClick: () => handleUnassign(item.id),
|
|
20883
20956
|
children: [
|
|
20884
20957
|
item.iconUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: item.iconUrl, alt: "", className: "w-3 h-3 object-contain inline-block" }),
|
|
20885
20958
|
item.label,
|
|
20886
|
-
|
|
20959
|
+
result2 && /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: result2.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "xs", className: result2.correct ? "text-success" : "text-error" })
|
|
20887
20960
|
]
|
|
20888
20961
|
},
|
|
20889
20962
|
item.id
|
|
@@ -20912,10 +20985,10 @@ function ClassifierBoard({
|
|
|
20912
20985
|
] }) }),
|
|
20913
20986
|
showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
|
|
20914
20987
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
20915
|
-
!submitted
|
|
20988
|
+
!submitted && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
|
|
20916
20989
|
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
|
|
20917
20990
|
t("classifier.check")
|
|
20918
|
-
] })
|
|
20991
|
+
] }),
|
|
20919
20992
|
/* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
|
|
20920
20993
|
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
|
|
20921
20994
|
t("classifier.reset")
|
|
@@ -28944,7 +29017,7 @@ var init_MapView = __esm({
|
|
|
28944
29017
|
shadowSize: [41, 41]
|
|
28945
29018
|
});
|
|
28946
29019
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
28947
|
-
const { useEffect: useEffect74, useRef: useRef68, useCallback:
|
|
29020
|
+
const { useEffect: useEffect74, useRef: useRef68, useCallback: useCallback111, useState: useState107 } = React82__namespace.default;
|
|
28948
29021
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
28949
29022
|
const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
28950
29023
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
@@ -28990,7 +29063,7 @@ var init_MapView = __esm({
|
|
|
28990
29063
|
}) {
|
|
28991
29064
|
const eventBus = useEventBus3();
|
|
28992
29065
|
const [clickedPosition, setClickedPosition] = useState107(null);
|
|
28993
|
-
const handleMapClick =
|
|
29066
|
+
const handleMapClick = useCallback111((lat, lng) => {
|
|
28994
29067
|
if (showClickedPin) {
|
|
28995
29068
|
setClickedPosition({ lat, lng });
|
|
28996
29069
|
}
|
|
@@ -28999,7 +29072,7 @@ var init_MapView = __esm({
|
|
|
28999
29072
|
eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
|
|
29000
29073
|
}
|
|
29001
29074
|
}, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
|
|
29002
|
-
const handleMarkerClick =
|
|
29075
|
+
const handleMarkerClick = useCallback111((marker) => {
|
|
29003
29076
|
onMarkerClick?.(marker);
|
|
29004
29077
|
if (markerClickEvent) {
|
|
29005
29078
|
eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
|
|
@@ -38230,44 +38303,27 @@ function DebuggerBoard({
|
|
|
38230
38303
|
const { t } = hooks.useTranslate();
|
|
38231
38304
|
const resolved = boardEntity(entity);
|
|
38232
38305
|
const [headerError, setHeaderError] = React82.useState(false);
|
|
38233
|
-
const [showHint, setShowHint] = React82.useState(false);
|
|
38234
38306
|
const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
|
|
38235
|
-
const result = resolved?.result
|
|
38307
|
+
const result = str(resolved?.result) || "none";
|
|
38236
38308
|
const attempts = num(resolved?.attempts);
|
|
38237
|
-
const submitted = result
|
|
38309
|
+
const submitted = result === "win";
|
|
38310
|
+
const showHint = !submitted && attempts >= 2 && Boolean(str(resolved?.hint));
|
|
38238
38311
|
const bugLines = lines.filter((l) => l.isBug);
|
|
38239
38312
|
const flaggedLines = lines.filter((l) => l.isFlagged);
|
|
38240
38313
|
const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
|
|
38241
38314
|
const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
|
|
38242
|
-
const allCorrect =
|
|
38315
|
+
const allCorrect = submitted;
|
|
38243
38316
|
const toggleLine = (lineId) => {
|
|
38244
38317
|
if (submitted) return;
|
|
38245
|
-
if (toggleFlagEvent) {
|
|
38246
|
-
emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
38247
|
-
}
|
|
38318
|
+
if (toggleFlagEvent) emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
38248
38319
|
};
|
|
38249
38320
|
const handleSubmit = React82.useCallback(() => {
|
|
38250
|
-
if (checkEvent) {
|
|
38251
|
-
emit(`UI:${checkEvent}`, {});
|
|
38252
|
-
}
|
|
38321
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
38253
38322
|
const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
|
|
38254
|
-
if (correct) {
|
|
38255
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
38256
|
-
}
|
|
38323
|
+
if (correct) emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
38257
38324
|
}, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
|
|
38258
38325
|
const handleReset = () => {
|
|
38259
|
-
if (playAgainEvent) {
|
|
38260
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
38261
|
-
}
|
|
38262
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
38263
|
-
setShowHint(true);
|
|
38264
|
-
}
|
|
38265
|
-
};
|
|
38266
|
-
const handleFullReset = () => {
|
|
38267
|
-
if (playAgainEvent) {
|
|
38268
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
38269
|
-
}
|
|
38270
|
-
setShowHint(false);
|
|
38326
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
38271
38327
|
};
|
|
38272
38328
|
if (!resolved) return null;
|
|
38273
38329
|
const theme = resolved.theme ?? void 0;
|
|
@@ -38291,7 +38347,7 @@ function DebuggerBoard({
|
|
|
38291
38347
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
|
|
38292
38348
|
] }),
|
|
38293
38349
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str(resolved.description) }),
|
|
38294
|
-
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(
|
|
38350
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(lines.filter((l) => l.isBug).length) }) })
|
|
38295
38351
|
] }) }),
|
|
38296
38352
|
/* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "none", children: lines.map((line, i) => {
|
|
38297
38353
|
const isFlagged = !!line.isFlagged;
|
|
@@ -38341,11 +38397,11 @@ function DebuggerBoard({
|
|
|
38341
38397
|
] }) }),
|
|
38342
38398
|
showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
|
|
38343
38399
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
38344
|
-
!submitted
|
|
38400
|
+
!submitted && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
|
|
38345
38401
|
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
|
|
38346
38402
|
t("debugger.submit")
|
|
38347
|
-
] })
|
|
38348
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick:
|
|
38403
|
+
] }),
|
|
38404
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handleReset, children: [
|
|
38349
38405
|
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
|
|
38350
38406
|
t("debugger.reset")
|
|
38351
38407
|
] })
|
|
@@ -39346,39 +39402,40 @@ function EventHandlerBoard({
|
|
|
39346
39402
|
stepDurationMs = 800,
|
|
39347
39403
|
playEvent,
|
|
39348
39404
|
completeEvent,
|
|
39405
|
+
editRuleEvent,
|
|
39406
|
+
playAgainEvent,
|
|
39349
39407
|
className
|
|
39350
39408
|
}) {
|
|
39351
39409
|
const { emit } = useEventBus();
|
|
39352
39410
|
const { t } = hooks.useTranslate();
|
|
39353
39411
|
const resolved = boardEntity(entity);
|
|
39354
|
-
const
|
|
39355
|
-
const
|
|
39412
|
+
const objects = rows(resolved?.objects);
|
|
39413
|
+
const entityResult = str(resolved?.result) || "none";
|
|
39414
|
+
const isSuccess = entityResult === "win";
|
|
39415
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
39356
39416
|
const [selectedObjectId, setSelectedObjectId] = React82.useState(
|
|
39357
|
-
|
|
39417
|
+
objects[0] ? objId(objects[0]) : null
|
|
39358
39418
|
);
|
|
39359
39419
|
const [headerError, setHeaderError] = React82.useState(false);
|
|
39360
|
-
const [
|
|
39420
|
+
const [isPlaying, setIsPlaying] = React82.useState(false);
|
|
39361
39421
|
const [eventLog, setEventLog] = React82.useState([]);
|
|
39362
|
-
const [attempts, setAttempts] = React82.useState(0);
|
|
39363
39422
|
const timerRef = React82.useRef(null);
|
|
39364
39423
|
const logIdCounter = React82.useRef(0);
|
|
39365
39424
|
React82.useEffect(() => () => {
|
|
39366
39425
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39367
39426
|
}, []);
|
|
39368
|
-
const selectedObject = objects.find((o) => objId(o) === selectedObjectId)
|
|
39427
|
+
const selectedObject = objects.find((o) => objId(o) === selectedObjectId) ?? null;
|
|
39369
39428
|
const handleRulesChange = React82.useCallback((objectId, rules) => {
|
|
39370
|
-
|
|
39371
|
-
|
|
39372
|
-
));
|
|
39373
|
-
}, []);
|
|
39429
|
+
if (editRuleEvent) emit(`UI:${editRuleEvent}`, { objectId, rules });
|
|
39430
|
+
}, [editRuleEvent, emit]);
|
|
39374
39431
|
const addLogEntry = React82.useCallback((icon, message, status = "done") => {
|
|
39375
39432
|
const id = `log-${logIdCounter.current++}`;
|
|
39376
39433
|
setEventLog((prev) => [...prev, { id, timestamp: Date.now(), icon, message, status }]);
|
|
39377
39434
|
}, []);
|
|
39378
39435
|
const handlePlay = React82.useCallback(() => {
|
|
39379
|
-
if (
|
|
39436
|
+
if (isPlaying || isSuccess) return;
|
|
39380
39437
|
if (playEvent) emit(`UI:${playEvent}`, {});
|
|
39381
|
-
|
|
39438
|
+
setIsPlaying(true);
|
|
39382
39439
|
setEventLog([]);
|
|
39383
39440
|
const allRules = [];
|
|
39384
39441
|
objects.forEach((obj) => {
|
|
@@ -39394,15 +39451,8 @@ function EventHandlerBoard({
|
|
|
39394
39451
|
let goalReached = false;
|
|
39395
39452
|
const processNext = () => {
|
|
39396
39453
|
if (eventQueue.length === 0 || stepIdx > 20) {
|
|
39397
|
-
|
|
39398
|
-
|
|
39399
|
-
if (completeEvent) {
|
|
39400
|
-
emit(`UI:${completeEvent}`, { success: true });
|
|
39401
|
-
}
|
|
39402
|
-
} else {
|
|
39403
|
-
setAttempts((prev) => prev + 1);
|
|
39404
|
-
setPlayState("fail");
|
|
39405
|
-
}
|
|
39454
|
+
setIsPlaying(false);
|
|
39455
|
+
if (goalReached && completeEvent) emit(`UI:${completeEvent}`, { success: true });
|
|
39406
39456
|
return;
|
|
39407
39457
|
}
|
|
39408
39458
|
const currentEvent = eventQueue.shift();
|
|
@@ -39433,21 +39483,19 @@ function EventHandlerBoard({
|
|
|
39433
39483
|
addLogEntry("\u{1F3AC}", t("eventHandler.simulationStarted", { events: triggers.join(", ") }), "active");
|
|
39434
39484
|
}
|
|
39435
39485
|
timerRef.current = setTimeout(processNext, stepDurationMs);
|
|
39436
|
-
}, [
|
|
39486
|
+
}, [isPlaying, isSuccess, objects, resolved, stepDurationMs, playEvent, completeEvent, emit, addLogEntry, t]);
|
|
39437
39487
|
const handleTryAgain = React82.useCallback(() => {
|
|
39438
39488
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39439
|
-
|
|
39489
|
+
setIsPlaying(false);
|
|
39440
39490
|
setEventLog([]);
|
|
39441
39491
|
}, []);
|
|
39442
39492
|
const handleReset = React82.useCallback(() => {
|
|
39443
39493
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39444
|
-
|
|
39445
|
-
setObjects([...resetObjects]);
|
|
39446
|
-
setPlayState("editing");
|
|
39494
|
+
setIsPlaying(false);
|
|
39447
39495
|
setEventLog([]);
|
|
39448
|
-
setSelectedObjectId(
|
|
39449
|
-
|
|
39450
|
-
}, [
|
|
39496
|
+
setSelectedObjectId(objects[0] ? objId(objects[0]) : null);
|
|
39497
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
39498
|
+
}, [objects, playAgainEvent, emit]);
|
|
39451
39499
|
if (!resolved) return null;
|
|
39452
39500
|
const objectViewers = objects.map((obj) => {
|
|
39453
39501
|
const states = objStates(obj);
|
|
@@ -39516,12 +39564,12 @@ function EventHandlerBoard({
|
|
|
39516
39564
|
{
|
|
39517
39565
|
object: selectedObject,
|
|
39518
39566
|
onRulesChange: handleRulesChange,
|
|
39519
|
-
disabled:
|
|
39567
|
+
disabled: isPlaying
|
|
39520
39568
|
}
|
|
39521
39569
|
),
|
|
39522
39570
|
eventLog.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(EventLog, { entries: eventLog }),
|
|
39523
|
-
|
|
39524
|
-
|
|
39571
|
+
isSuccess && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("eventHandler.chainComplete") }) }),
|
|
39572
|
+
!isPlaying && !isSuccess && attempts > 0 && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
39525
39573
|
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(encourageKey) }) }),
|
|
39526
39574
|
showHint && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-start", gap: "xs", children: [
|
|
39527
39575
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
@@ -39529,12 +39577,12 @@ function EventHandlerBoard({
|
|
|
39529
39577
|
] }) })
|
|
39530
39578
|
] }),
|
|
39531
39579
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
|
|
39532
|
-
|
|
39580
|
+
!isPlaying && !isSuccess && attempts > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
39533
39581
|
Button,
|
|
39534
39582
|
{
|
|
39535
39583
|
variant: "primary",
|
|
39536
39584
|
onClick: handlePlay,
|
|
39537
|
-
disabled:
|
|
39585
|
+
disabled: isPlaying || isSuccess,
|
|
39538
39586
|
children: "\u25B6 " + t("game.play")
|
|
39539
39587
|
}
|
|
39540
39588
|
),
|
|
@@ -42087,21 +42135,6 @@ var init_ModalSlot = __esm({
|
|
|
42087
42135
|
ModalSlot.displayName = "ModalSlot";
|
|
42088
42136
|
}
|
|
42089
42137
|
});
|
|
42090
|
-
function getOpponentAction(strategy, actions, history) {
|
|
42091
|
-
const actionIds = actions.map((a) => a.id);
|
|
42092
|
-
switch (strategy) {
|
|
42093
|
-
case "always-cooperate":
|
|
42094
|
-
return actionIds[0];
|
|
42095
|
-
case "always-defect":
|
|
42096
|
-
return actionIds[actionIds.length - 1];
|
|
42097
|
-
case "tit-for-tat":
|
|
42098
|
-
if (history.length === 0) return actionIds[0];
|
|
42099
|
-
return history[history.length - 1].playerAction;
|
|
42100
|
-
case "random":
|
|
42101
|
-
default:
|
|
42102
|
-
return actionIds[Math.floor(Math.random() * actionIds.length)];
|
|
42103
|
-
}
|
|
42104
|
-
}
|
|
42105
42138
|
function NegotiatorBoard({
|
|
42106
42139
|
entity,
|
|
42107
42140
|
completeEvent = "PUZZLE_COMPLETE",
|
|
@@ -42123,28 +42156,35 @@ function NegotiatorBoard({
|
|
|
42123
42156
|
const playerTotal = num(resolved?.score);
|
|
42124
42157
|
const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
|
|
42125
42158
|
const won = result === "win";
|
|
42126
|
-
const
|
|
42159
|
+
const lastPlayerAction = str(resolved?.lastPlayerAction);
|
|
42160
|
+
const lastOpponentAction = str(resolved?.lastOpponentAction);
|
|
42161
|
+
const lastPayoff = num(resolved?.lastPayoff);
|
|
42127
42162
|
const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
|
|
42128
42163
|
const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
|
|
42164
|
+
const prevRoundRef = React82.useRef(currentRound);
|
|
42165
|
+
React82.useEffect(() => {
|
|
42166
|
+
if (currentRound > prevRoundRef.current && lastPlayerAction) {
|
|
42167
|
+
const opponentPayoffEntry = payoffMatrix.find(
|
|
42168
|
+
(p2) => p2.playerAction === lastPlayerAction && p2.opponentAction === lastOpponentAction
|
|
42169
|
+
);
|
|
42170
|
+
setHistory((prev) => [
|
|
42171
|
+
...prev,
|
|
42172
|
+
{
|
|
42173
|
+
round: currentRound,
|
|
42174
|
+
playerAction: lastPlayerAction,
|
|
42175
|
+
opponentAction: lastOpponentAction,
|
|
42176
|
+
playerPayoff: lastPayoff,
|
|
42177
|
+
opponentPayoff: opponentPayoffEntry?.opponentPayoff ?? 0
|
|
42178
|
+
}
|
|
42179
|
+
]);
|
|
42180
|
+
}
|
|
42181
|
+
prevRoundRef.current = currentRound;
|
|
42182
|
+
}, [currentRound, lastPlayerAction, lastOpponentAction, lastPayoff, payoffMatrix]);
|
|
42183
|
+
const opponentTotal = React82.useMemo(() => history.reduce((s, r) => s + r.opponentPayoff, 0), [history]);
|
|
42129
42184
|
const handleAction = React82.useCallback((actionId) => {
|
|
42130
42185
|
if (isComplete) return;
|
|
42131
|
-
const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
|
|
42132
|
-
const payoff = payoffMatrix.find(
|
|
42133
|
-
(p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
|
|
42134
|
-
);
|
|
42135
|
-
const playerPayoff = payoff?.playerPayoff ?? 0;
|
|
42136
|
-
setHistory((prev) => [
|
|
42137
|
-
...prev,
|
|
42138
|
-
{
|
|
42139
|
-
round: prev.length + 1,
|
|
42140
|
-
playerAction: actionId,
|
|
42141
|
-
opponentAction,
|
|
42142
|
-
playerPayoff,
|
|
42143
|
-
opponentPayoff: payoff?.opponentPayoff ?? 0
|
|
42144
|
-
}
|
|
42145
|
-
]);
|
|
42146
42186
|
if (playRoundEvent) {
|
|
42147
|
-
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff:
|
|
42187
|
+
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: 0 });
|
|
42148
42188
|
}
|
|
42149
42189
|
if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
|
|
42150
42190
|
if (finishEvent) {
|
|
@@ -42154,10 +42194,11 @@ function NegotiatorBoard({
|
|
|
42154
42194
|
setShowHint(true);
|
|
42155
42195
|
}
|
|
42156
42196
|
}
|
|
42157
|
-
}, [isComplete, resolved, totalRounds, currentRound,
|
|
42197
|
+
}, [isComplete, resolved, totalRounds, currentRound, playRoundEvent, finishEvent, emit]);
|
|
42158
42198
|
const handleReset = React82.useCallback(() => {
|
|
42159
42199
|
setHistory([]);
|
|
42160
42200
|
setShowHint(false);
|
|
42201
|
+
prevRoundRef.current = 0;
|
|
42161
42202
|
if (playAgainEvent) {
|
|
42162
42203
|
emit(`UI:${playAgainEvent}`, {});
|
|
42163
42204
|
}
|
|
@@ -44512,17 +44553,24 @@ function SequencerBoard({
|
|
|
44512
44553
|
const { emit } = useEventBus();
|
|
44513
44554
|
const { t } = hooks.useTranslate();
|
|
44514
44555
|
const resolved = boardEntity(entity);
|
|
44515
|
-
const maxSlots = num(resolved?.maxSlots);
|
|
44556
|
+
const maxSlots = num(resolved?.maxSlots) || 3;
|
|
44516
44557
|
const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
|
|
44517
44558
|
const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
|
|
44518
44559
|
const allowDuplicates = resolved?.allowDuplicates !== false;
|
|
44560
|
+
const entitySlots = Array.isArray(resolved?.slots) ? resolved.slots : [];
|
|
44561
|
+
const entityResult = str(resolved?.result);
|
|
44562
|
+
const entityAttempts = num(resolved?.attempts);
|
|
44563
|
+
const entityCurrentStep = typeof resolved?.currentStep === "number" ? resolved.currentStep : -1;
|
|
44564
|
+
const slots = Array.from({ length: maxSlots }, (_, i) => {
|
|
44565
|
+
const entitySlot = entitySlots.find((s) => s.index === i);
|
|
44566
|
+
if (!entitySlot?.placedActionId) return void 0;
|
|
44567
|
+
return availableActions.find((a) => a.id === entitySlot.placedActionId);
|
|
44568
|
+
});
|
|
44569
|
+
const isSuccess = entityResult === "win";
|
|
44570
|
+
const attempts = entityAttempts;
|
|
44571
|
+
const isPlayingBack = entityCurrentStep >= 0 && !isSuccess;
|
|
44572
|
+
const currentStep = entityCurrentStep;
|
|
44519
44573
|
const [headerError, setHeaderError] = React82.useState(false);
|
|
44520
|
-
const [slots, setSlots] = React82.useState(
|
|
44521
|
-
() => Array.from({ length: maxSlots }, () => void 0)
|
|
44522
|
-
);
|
|
44523
|
-
const [playState, setPlayState] = React82.useState("idle");
|
|
44524
|
-
const [currentStep, setCurrentStep] = React82.useState(-1);
|
|
44525
|
-
const [attempts, setAttempts] = React82.useState(0);
|
|
44526
44574
|
const [slotFeedback, setSlotFeedback] = React82.useState(
|
|
44527
44575
|
() => Array.from({ length: maxSlots }, () => null)
|
|
44528
44576
|
);
|
|
@@ -44531,11 +44579,6 @@ function SequencerBoard({
|
|
|
44531
44579
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
44532
44580
|
}, []);
|
|
44533
44581
|
const handleSlotDrop = React82.useCallback((index, item) => {
|
|
44534
|
-
setSlots((prev) => {
|
|
44535
|
-
const next = [...prev];
|
|
44536
|
-
next[index] = item;
|
|
44537
|
-
return next;
|
|
44538
|
-
});
|
|
44539
44582
|
setSlotFeedback((prev) => {
|
|
44540
44583
|
const next = [...prev];
|
|
44541
44584
|
next[index] = null;
|
|
@@ -44545,11 +44588,6 @@ function SequencerBoard({
|
|
|
44545
44588
|
if (placeEvent) emit(`UI:${placeEvent}`, { slotIndex: index, actionId: item.id });
|
|
44546
44589
|
}, [emit, placeEvent]);
|
|
44547
44590
|
const handleSlotRemove = React82.useCallback((index) => {
|
|
44548
|
-
setSlots((prev) => {
|
|
44549
|
-
const next = [...prev];
|
|
44550
|
-
next[index] = void 0;
|
|
44551
|
-
return next;
|
|
44552
|
-
});
|
|
44553
44591
|
setSlotFeedback((prev) => {
|
|
44554
44592
|
const next = [...prev];
|
|
44555
44593
|
next[index] = null;
|
|
@@ -44560,48 +44598,34 @@ function SequencerBoard({
|
|
|
44560
44598
|
}, [emit, removeEvent]);
|
|
44561
44599
|
const handleReset = React82.useCallback(() => {
|
|
44562
44600
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
44563
|
-
setSlots(Array.from({ length: maxSlots }, () => void 0));
|
|
44564
|
-
setPlayState("idle");
|
|
44565
|
-
setCurrentStep(-1);
|
|
44566
|
-
setAttempts(0);
|
|
44567
44601
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
44568
44602
|
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
44569
44603
|
}, [maxSlots, playAgainEvent, emit]);
|
|
44570
44604
|
const filledSlots = slots.filter((s) => !!s);
|
|
44571
|
-
const canPlay = filledSlots.length > 0 &&
|
|
44605
|
+
const canPlay = filledSlots.length > 0 && !isPlayingBack && !isSuccess;
|
|
44572
44606
|
const handlePlay = React82.useCallback(() => {
|
|
44573
44607
|
if (!canPlay) return;
|
|
44574
44608
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
44575
44609
|
emit("UI:PLAY_SOUND", { key: "confirm" });
|
|
44576
44610
|
const sequence = slots.map((s) => s?.id || "");
|
|
44577
|
-
|
|
44578
|
-
|
|
44579
|
-
}
|
|
44580
|
-
setPlayState("playing");
|
|
44581
|
-
setCurrentStep(0);
|
|
44611
|
+
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
44612
|
+
if (playEvent) emit(`UI:${playEvent}`, { sequence });
|
|
44582
44613
|
let step = 0;
|
|
44583
44614
|
const advance = () => {
|
|
44584
44615
|
step++;
|
|
44616
|
+
emit("UI:STEP", { step });
|
|
44585
44617
|
if (step >= maxSlots) {
|
|
44618
|
+
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
44586
44619
|
const playerSeq = slots.map((s) => s?.id);
|
|
44587
|
-
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
44588
44620
|
const success = solutions.some(
|
|
44589
44621
|
(sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
|
|
44590
44622
|
);
|
|
44591
|
-
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
44592
44623
|
if (success) {
|
|
44593
|
-
setPlayState("success");
|
|
44594
|
-
setCurrentStep(-1);
|
|
44595
44624
|
emit("UI:PLAY_SOUND", { key: "levelComplete" });
|
|
44596
|
-
if (completeEvent) {
|
|
44597
|
-
emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
44598
|
-
}
|
|
44625
|
+
if (completeEvent) emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
44599
44626
|
} else {
|
|
44600
|
-
setAttempts((prev) => prev + 1);
|
|
44601
44627
|
const feedback = computeSlotFeedback(playerSeq, solutions);
|
|
44602
44628
|
setSlotFeedback(feedback);
|
|
44603
|
-
setPlayState("idle");
|
|
44604
|
-
setCurrentStep(-1);
|
|
44605
44629
|
emit("UI:PLAY_SOUND", { key: "fail" });
|
|
44606
44630
|
const correctCount2 = feedback.filter((f3) => f3 === "correct").length;
|
|
44607
44631
|
for (let ci = 0; ci < correctCount2; ci++) {
|
|
@@ -44611,7 +44635,6 @@ function SequencerBoard({
|
|
|
44611
44635
|
}
|
|
44612
44636
|
}
|
|
44613
44637
|
} else {
|
|
44614
|
-
setCurrentStep(step);
|
|
44615
44638
|
timerRef.current = setTimeout(advance, stepDurationMs);
|
|
44616
44639
|
}
|
|
44617
44640
|
};
|
|
@@ -44661,7 +44684,7 @@ function SequencerBoard({
|
|
|
44661
44684
|
/* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
|
|
44662
44685
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center justify-between", children: [
|
|
44663
44686
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
|
|
44664
|
-
hasFeedback &&
|
|
44687
|
+
hasFeedback && !isPlayingBack && !isSuccess && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
|
|
44665
44688
|
`${correctCount}/${maxSlots} `,
|
|
44666
44689
|
"\u2705"
|
|
44667
44690
|
] })
|
|
@@ -44673,7 +44696,7 @@ function SequencerBoard({
|
|
|
44673
44696
|
maxSlots,
|
|
44674
44697
|
onSlotDrop: handleSlotDrop,
|
|
44675
44698
|
onSlotRemove: handleSlotRemove,
|
|
44676
|
-
playing:
|
|
44699
|
+
playing: isPlayingBack,
|
|
44677
44700
|
currentStep,
|
|
44678
44701
|
categoryColors,
|
|
44679
44702
|
slotFeedback,
|
|
@@ -44681,7 +44704,7 @@ function SequencerBoard({
|
|
|
44681
44704
|
}
|
|
44682
44705
|
)
|
|
44683
44706
|
] }),
|
|
44684
|
-
|
|
44707
|
+
!isPlayingBack && /* @__PURE__ */ jsxRuntime.jsx(
|
|
44685
44708
|
ActionPalette,
|
|
44686
44709
|
{
|
|
44687
44710
|
actions: availableActions,
|
|
@@ -44691,8 +44714,8 @@ function SequencerBoard({
|
|
|
44691
44714
|
label: t("sequencer.dragActions")
|
|
44692
44715
|
}
|
|
44693
44716
|
),
|
|
44694
|
-
hasFeedback &&
|
|
44695
|
-
|
|
44717
|
+
hasFeedback && !isPlayingBack && !isSuccess && attempts > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: t(encourageKey) }) }),
|
|
44718
|
+
isSuccess && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("sequencer.levelComplete") }) }),
|
|
44696
44719
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
|
|
44697
44720
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44698
44721
|
Button,
|
|
@@ -45742,6 +45765,9 @@ function StateArchitectBoard({
|
|
|
45742
45765
|
stepDurationMs = 600,
|
|
45743
45766
|
testEvent,
|
|
45744
45767
|
completeEvent,
|
|
45768
|
+
addTransitionEvent,
|
|
45769
|
+
removeTransitionEvent,
|
|
45770
|
+
playAgainEvent,
|
|
45745
45771
|
className
|
|
45746
45772
|
}) {
|
|
45747
45773
|
const { emit } = useEventBus();
|
|
@@ -45754,14 +45780,16 @@ function StateArchitectBoard({
|
|
|
45754
45780
|
const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
|
|
45755
45781
|
const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
|
|
45756
45782
|
const entityVariables = rows(resolved?.variables);
|
|
45757
|
-
const
|
|
45783
|
+
const transitions = entityTransitions;
|
|
45784
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
45785
|
+
const entityResult = str(resolved?.result) || "none";
|
|
45786
|
+
const isSuccess = entityResult === "win";
|
|
45787
|
+
const [isTesting, setIsTesting] = React82.useState(false);
|
|
45758
45788
|
const [headerError, setHeaderError] = React82.useState(false);
|
|
45759
|
-
const [playState, setPlayState] = React82.useState("editing");
|
|
45760
45789
|
const [currentState, setCurrentState] = React82.useState(initialState);
|
|
45761
45790
|
const [selectedState, setSelectedState] = React82.useState(null);
|
|
45762
45791
|
const [testResults, setTestResults] = React82.useState([]);
|
|
45763
45792
|
const [variables, setVariables] = React82.useState(() => [...entityVariables]);
|
|
45764
|
-
const [attempts, setAttempts] = React82.useState(0);
|
|
45765
45793
|
const timerRef = React82.useRef(null);
|
|
45766
45794
|
const [addingFrom, setAddingFrom] = React82.useState(null);
|
|
45767
45795
|
React82.useEffect(() => () => {
|
|
@@ -45771,7 +45799,7 @@ function StateArchitectBoard({
|
|
|
45771
45799
|
const GRAPH_H = 400;
|
|
45772
45800
|
const positions = React82.useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
|
|
45773
45801
|
const handleStateClick = React82.useCallback((state) => {
|
|
45774
|
-
if (
|
|
45802
|
+
if (isTesting) return;
|
|
45775
45803
|
if (addingFrom) {
|
|
45776
45804
|
if (addingFrom !== state) {
|
|
45777
45805
|
const event = availableEvents[0] || "EVENT";
|
|
@@ -45781,20 +45809,20 @@ function StateArchitectBoard({
|
|
|
45781
45809
|
to: state,
|
|
45782
45810
|
event
|
|
45783
45811
|
};
|
|
45784
|
-
|
|
45812
|
+
if (addTransitionEvent) emit(`UI:${addTransitionEvent}`, { id: newTrans.id, from: newTrans.from, to: newTrans.to, event: newTrans.event });
|
|
45785
45813
|
}
|
|
45786
45814
|
setAddingFrom(null);
|
|
45787
45815
|
} else {
|
|
45788
45816
|
setSelectedState(state);
|
|
45789
45817
|
}
|
|
45790
|
-
}, [
|
|
45818
|
+
}, [isTesting, addingFrom, availableEvents, addTransitionEvent, emit]);
|
|
45791
45819
|
const handleStartAddTransition = React82.useCallback(() => {
|
|
45792
45820
|
if (!selectedState) return;
|
|
45793
45821
|
setAddingFrom(selectedState);
|
|
45794
45822
|
}, [selectedState]);
|
|
45795
45823
|
const handleRemoveTransition = React82.useCallback((transId) => {
|
|
45796
|
-
|
|
45797
|
-
}, []);
|
|
45824
|
+
if (removeTransitionEvent) emit(`UI:${removeTransitionEvent}`, { id: transId });
|
|
45825
|
+
}, [removeTransitionEvent, emit]);
|
|
45798
45826
|
const machine = React82.useMemo(() => ({
|
|
45799
45827
|
name: entityName,
|
|
45800
45828
|
description: str(resolved?.description),
|
|
@@ -45808,16 +45836,16 @@ function StateArchitectBoard({
|
|
|
45808
45836
|
}))
|
|
45809
45837
|
}), [entityName, resolved, entityStates, currentState, transitions]);
|
|
45810
45838
|
const handleTest = React82.useCallback(() => {
|
|
45811
|
-
if (
|
|
45839
|
+
if (isTesting) return;
|
|
45812
45840
|
if (testEvent) emit(`UI:${testEvent}`, {});
|
|
45813
|
-
|
|
45841
|
+
setIsTesting(true);
|
|
45814
45842
|
setTestResults([]);
|
|
45815
45843
|
const results = [];
|
|
45816
45844
|
let testIdx = 0;
|
|
45817
45845
|
const runNextTest = () => {
|
|
45818
45846
|
if (testIdx >= testCases.length) {
|
|
45819
45847
|
const allPassed = results.every((r) => r.passed);
|
|
45820
|
-
|
|
45848
|
+
setIsTesting(false);
|
|
45821
45849
|
setTestResults(results);
|
|
45822
45850
|
if (allPassed && completeEvent) {
|
|
45823
45851
|
emit(`UI:${completeEvent}`, {
|
|
@@ -45825,9 +45853,6 @@ function StateArchitectBoard({
|
|
|
45825
45853
|
passedTests: results.filter((r) => r.passed).length
|
|
45826
45854
|
});
|
|
45827
45855
|
}
|
|
45828
|
-
if (!allPassed) {
|
|
45829
|
-
setAttempts((prev) => prev + 1);
|
|
45830
|
-
}
|
|
45831
45856
|
return;
|
|
45832
45857
|
}
|
|
45833
45858
|
const testCase = testCases[testIdx];
|
|
@@ -45850,24 +45875,23 @@ function StateArchitectBoard({
|
|
|
45850
45875
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
45851
45876
|
};
|
|
45852
45877
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
45853
|
-
}, [
|
|
45878
|
+
}, [isTesting, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
|
|
45854
45879
|
const handleTryAgain = React82.useCallback(() => {
|
|
45855
45880
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
45856
|
-
|
|
45881
|
+
setIsTesting(false);
|
|
45857
45882
|
setCurrentState(initialState);
|
|
45858
45883
|
setTestResults([]);
|
|
45859
45884
|
}, [initialState]);
|
|
45860
45885
|
const handleReset = React82.useCallback(() => {
|
|
45861
45886
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
45862
|
-
|
|
45863
|
-
setPlayState("editing");
|
|
45887
|
+
setIsTesting(false);
|
|
45864
45888
|
setCurrentState(initialState);
|
|
45865
45889
|
setTestResults([]);
|
|
45866
45890
|
setVariables([...entityVariables]);
|
|
45867
45891
|
setSelectedState(null);
|
|
45868
45892
|
setAddingFrom(null);
|
|
45869
|
-
|
|
45870
|
-
}, [
|
|
45893
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
45894
|
+
}, [initialState, entityVariables, playAgainEvent, emit]);
|
|
45871
45895
|
const codeData = React82.useMemo(() => ({
|
|
45872
45896
|
name: entityName,
|
|
45873
45897
|
states: entityStates,
|
|
@@ -45963,7 +45987,7 @@ function StateArchitectBoard({
|
|
|
45963
45987
|
]
|
|
45964
45988
|
}
|
|
45965
45989
|
),
|
|
45966
|
-
|
|
45990
|
+
!isTesting && /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "sm", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
45967
45991
|
Button,
|
|
45968
45992
|
{
|
|
45969
45993
|
variant: "ghost",
|
|
@@ -45985,7 +46009,7 @@ function StateArchitectBoard({
|
|
|
45985
46009
|
t2.guardHint,
|
|
45986
46010
|
")"
|
|
45987
46011
|
] }),
|
|
45988
|
-
|
|
46012
|
+
!isTesting && /* @__PURE__ */ jsxRuntime.jsx(
|
|
45989
46013
|
Button,
|
|
45990
46014
|
{
|
|
45991
46015
|
variant: "ghost",
|
|
@@ -46017,21 +46041,21 @@ function StateArchitectBoard({
|
|
|
46017
46041
|
resolved.showCodeView !== false && /* @__PURE__ */ jsxRuntime.jsx(StateJsonView, { data: codeData, label: "View Code" })
|
|
46018
46042
|
] })
|
|
46019
46043
|
] }),
|
|
46020
|
-
|
|
46021
|
-
|
|
46044
|
+
isSuccess && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("stateArchitect.allPassed") }) }),
|
|
46045
|
+
!isTesting && !isSuccess && testResults.some((r) => !r.passed) && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
46022
46046
|
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(ENCOURAGEMENT_KEYS3[Math.min(attempts - 1, ENCOURAGEMENT_KEYS3.length - 1)] ?? ENCOURAGEMENT_KEYS3[0]) }) }),
|
|
46023
|
-
attempts >=
|
|
46047
|
+
!isSuccess && attempts >= 2 && Boolean(str(resolved?.hint)) && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-start", gap: "xs", children: [
|
|
46024
46048
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
46025
46049
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
|
|
46026
46050
|
] }) })
|
|
46027
46051
|
] }),
|
|
46028
46052
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
|
|
46029
|
-
|
|
46053
|
+
!isTesting && !isSuccess && testResults.some((r) => !r.passed) ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
46030
46054
|
Button,
|
|
46031
46055
|
{
|
|
46032
46056
|
variant: "primary",
|
|
46033
46057
|
onClick: handleTest,
|
|
46034
|
-
disabled:
|
|
46058
|
+
disabled: isTesting,
|
|
46035
46059
|
children: "\u25B6 " + t("game.runTests")
|
|
46036
46060
|
}
|
|
46037
46061
|
),
|
|
@@ -46735,17 +46759,12 @@ var init_UncontrolledBattleBoard = __esm({
|
|
|
46735
46759
|
}
|
|
46736
46760
|
});
|
|
46737
46761
|
function heroPosition(h) {
|
|
46762
|
+
if ("position" in h && h.position != null) return h.position;
|
|
46738
46763
|
return vec2(h.position);
|
|
46739
46764
|
}
|
|
46740
|
-
function heroOwner(h) {
|
|
46741
|
-
return str(h.owner);
|
|
46742
|
-
}
|
|
46743
46765
|
function heroMovement(h) {
|
|
46744
46766
|
return num(h.movement);
|
|
46745
46767
|
}
|
|
46746
|
-
function hexPassable(h) {
|
|
46747
|
-
return h.passable !== false;
|
|
46748
|
-
}
|
|
46749
46768
|
function defaultIsInRange(from, to, range) {
|
|
46750
46769
|
return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
|
|
46751
46770
|
}
|
|
@@ -46781,38 +46800,41 @@ function WorldMapBoard({
|
|
|
46781
46800
|
}) {
|
|
46782
46801
|
const eventBus = useEventBus();
|
|
46783
46802
|
const resolved = boardEntity(entity);
|
|
46784
|
-
const
|
|
46785
|
-
const
|
|
46803
|
+
const entityUnits = rows(resolved?.units);
|
|
46804
|
+
const entityTiles = rows(resolved?.tiles);
|
|
46786
46805
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
46787
46806
|
const selectedHeroId = resolved?.selectedHeroId ?? null;
|
|
46788
46807
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
46789
46808
|
const backgroundImage = resolved?.backgroundImage;
|
|
46790
46809
|
const [hoveredTile, setHoveredTile] = React82.useState(null);
|
|
46791
|
-
const selectedHero = React82.useMemo(
|
|
46792
|
-
() => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
|
|
46793
|
-
[heroes, selectedHeroId]
|
|
46794
|
-
);
|
|
46795
46810
|
const derivedTiles = React82.useMemo(
|
|
46796
|
-
() =>
|
|
46797
|
-
x: num(
|
|
46798
|
-
y: num(
|
|
46799
|
-
terrain: str(
|
|
46800
|
-
terrainSprite:
|
|
46811
|
+
() => entityTiles.map((t) => ({
|
|
46812
|
+
x: num(t.x),
|
|
46813
|
+
y: num(t.y),
|
|
46814
|
+
terrain: str(t.terrain),
|
|
46815
|
+
terrainSprite: t.terrainSprite == null ? void 0 : str(t.terrainSprite),
|
|
46816
|
+
passable: t.passable !== false
|
|
46801
46817
|
})),
|
|
46802
|
-
[
|
|
46818
|
+
[entityTiles]
|
|
46803
46819
|
);
|
|
46804
46820
|
const tiles = propTiles ?? derivedTiles;
|
|
46805
46821
|
const baseUnits = React82.useMemo(
|
|
46806
|
-
() => propUnits ??
|
|
46807
|
-
id: str(
|
|
46808
|
-
position: heroPosition(
|
|
46809
|
-
name: str(
|
|
46810
|
-
|
|
46811
|
-
|
|
46812
|
-
|
|
46813
|
-
|
|
46822
|
+
() => propUnits ?? entityUnits.map((u) => ({
|
|
46823
|
+
id: str(u.id),
|
|
46824
|
+
position: heroPosition(u),
|
|
46825
|
+
name: str(u.name),
|
|
46826
|
+
// lolo uses `team` field (not `owner`)
|
|
46827
|
+
team: str(u.team) === "enemy" ? "enemy" : "player",
|
|
46828
|
+
health: num(u.health) || 100,
|
|
46829
|
+
maxHealth: num(u.maxHealth) || 100,
|
|
46830
|
+
sprite: u.sprite == null ? void 0 : str(u.sprite)
|
|
46814
46831
|
})),
|
|
46815
|
-
[
|
|
46832
|
+
[entityUnits, propUnits]
|
|
46833
|
+
);
|
|
46834
|
+
const gameUnits = baseUnits;
|
|
46835
|
+
const selectedHero = React82.useMemo(
|
|
46836
|
+
() => gameUnits.find((u) => u.id === selectedHeroId) ?? null,
|
|
46837
|
+
[gameUnits, selectedHeroId]
|
|
46816
46838
|
);
|
|
46817
46839
|
const MOVE_SPEED_MS_PER_TILE = 300;
|
|
46818
46840
|
const movementAnimRef = React82.useRef(null);
|
|
@@ -46858,51 +46880,50 @@ function WorldMapBoard({
|
|
|
46858
46880
|
const validMoves = React82.useMemo(() => {
|
|
46859
46881
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
46860
46882
|
const sp = heroPosition(selectedHero);
|
|
46861
|
-
const
|
|
46883
|
+
const sTeam = str(selectedHero.team);
|
|
46862
46884
|
const range = heroMovement(selectedHero);
|
|
46863
46885
|
const moves = [];
|
|
46864
|
-
|
|
46865
|
-
const
|
|
46866
|
-
const
|
|
46867
|
-
if (
|
|
46868
|
-
if (
|
|
46869
|
-
if (!isInRange(sp, { x:
|
|
46870
|
-
if (
|
|
46871
|
-
const
|
|
46872
|
-
return
|
|
46886
|
+
tiles.forEach((t) => {
|
|
46887
|
+
const tx = t.x;
|
|
46888
|
+
const ty = t.y;
|
|
46889
|
+
if (t.passable === false) return;
|
|
46890
|
+
if (tx === sp.x && ty === sp.y) return;
|
|
46891
|
+
if (!isInRange(sp, { x: tx, y: ty }, range)) return;
|
|
46892
|
+
if (gameUnits.some((u) => {
|
|
46893
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
46894
|
+
return up.x === tx && up.y === ty && str(u.team) === sTeam;
|
|
46873
46895
|
})) return;
|
|
46874
|
-
moves.push({ x:
|
|
46896
|
+
moves.push({ x: tx, y: ty });
|
|
46875
46897
|
});
|
|
46876
46898
|
return moves;
|
|
46877
|
-
}, [selectedHero,
|
|
46899
|
+
}, [selectedHero, tiles, gameUnits, isInRange]);
|
|
46878
46900
|
const attackTargets = React82.useMemo(() => {
|
|
46879
46901
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
46880
46902
|
const sp = heroPosition(selectedHero);
|
|
46881
|
-
const
|
|
46903
|
+
const sTeam = str(selectedHero.team);
|
|
46882
46904
|
const range = heroMovement(selectedHero);
|
|
46883
|
-
return
|
|
46884
|
-
}, [selectedHero,
|
|
46885
|
-
const maxY = Math.max(...
|
|
46905
|
+
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 });
|
|
46906
|
+
}, [selectedHero, gameUnits, isInRange]);
|
|
46907
|
+
const maxY = Math.max(...tiles.map((t) => t.y), 0);
|
|
46886
46908
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
46887
46909
|
const tileToScreen = React82.useCallback(
|
|
46888
46910
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
46889
46911
|
[scale, baseOffsetX]
|
|
46890
46912
|
);
|
|
46891
46913
|
const hoveredHex = React82.useMemo(
|
|
46892
|
-
() => hoveredTile ?
|
|
46893
|
-
[hoveredTile,
|
|
46914
|
+
() => hoveredTile ? tiles.find((t) => t.x === hoveredTile.x && t.y === hoveredTile.y) ?? null : null,
|
|
46915
|
+
[hoveredTile, tiles]
|
|
46894
46916
|
);
|
|
46895
46917
|
const hoveredHero = React82.useMemo(
|
|
46896
|
-
() => hoveredTile ?
|
|
46897
|
-
const
|
|
46898
|
-
return
|
|
46918
|
+
() => hoveredTile ? gameUnits.find((u) => {
|
|
46919
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
46920
|
+
return up.x === hoveredTile.x && up.y === hoveredTile.y;
|
|
46899
46921
|
}) ?? null : null,
|
|
46900
|
-
[hoveredTile,
|
|
46922
|
+
[hoveredTile, gameUnits]
|
|
46901
46923
|
);
|
|
46902
46924
|
const handleTileClick = React82.useCallback((x, y) => {
|
|
46903
46925
|
if (movementAnimRef.current) return;
|
|
46904
|
-
const
|
|
46905
|
-
if (!hex) return;
|
|
46926
|
+
const tile = tiles.find((t) => t.x === x && t.y === y);
|
|
46906
46927
|
if (tileClickEvent) {
|
|
46907
46928
|
eventBus.emit(`UI:${tileClickEvent}`, { x, y });
|
|
46908
46929
|
}
|
|
@@ -46913,38 +46934,39 @@ function WorldMapBoard({
|
|
|
46913
46934
|
if (heroMoveEvent) {
|
|
46914
46935
|
eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
|
|
46915
46936
|
}
|
|
46916
|
-
const feature = str(
|
|
46937
|
+
const feature = tile ? str(tile.feature) : "";
|
|
46917
46938
|
if (feature && feature !== "none") {
|
|
46918
|
-
|
|
46939
|
+
const tileRow = tile;
|
|
46940
|
+
onFeatureEnter?.(heroId, tileRow);
|
|
46919
46941
|
if (featureEnterEvent) {
|
|
46920
|
-
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
|
|
46942
|
+
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex: tileRow });
|
|
46921
46943
|
}
|
|
46922
46944
|
}
|
|
46923
46945
|
});
|
|
46924
46946
|
return;
|
|
46925
46947
|
}
|
|
46926
|
-
const enemy =
|
|
46927
|
-
const
|
|
46928
|
-
return
|
|
46948
|
+
const enemy = gameUnits.find((u) => {
|
|
46949
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
46950
|
+
return up.x === x && up.y === y && str(u.team) === "enemy";
|
|
46929
46951
|
});
|
|
46930
46952
|
if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
|
|
46931
46953
|
const attackerId = str(selectedHero.id);
|
|
46932
|
-
const defenderId =
|
|
46954
|
+
const defenderId = enemy.id;
|
|
46933
46955
|
onBattleEncounter?.(attackerId, defenderId);
|
|
46934
46956
|
if (battleEncounterEvent) {
|
|
46935
46957
|
eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
|
|
46936
46958
|
}
|
|
46937
46959
|
}
|
|
46938
|
-
}, [
|
|
46960
|
+
}, [tiles, gameUnits, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
|
|
46939
46961
|
const handleUnitClick = React82.useCallback((unitId) => {
|
|
46940
|
-
const
|
|
46941
|
-
if (
|
|
46962
|
+
const unit = gameUnits.find((u) => u.id === unitId);
|
|
46963
|
+
if (unit && (str(unit.team) === "player" || allowMoveAllHeroes)) {
|
|
46942
46964
|
onHeroSelect?.(unitId);
|
|
46943
46965
|
if (heroSelectEvent) {
|
|
46944
46966
|
eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
|
|
46945
46967
|
}
|
|
46946
46968
|
}
|
|
46947
|
-
}, [
|
|
46969
|
+
}, [gameUnits, onHeroSelect, allowMoveAllHeroes, eventBus, heroSelectEvent]);
|
|
46948
46970
|
const selectHero = React82.useCallback((id) => {
|
|
46949
46971
|
onHeroSelect?.(id);
|
|
46950
46972
|
if (heroSelectEvent) {
|