@almadar/ui 5.46.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 +89 -80
- package/dist/components/game/molecules/three/index.css +0 -5
- package/dist/components/game/molecules/three/index.js +89 -80
- 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/avl/index.cjs
CHANGED
|
@@ -11063,6 +11063,7 @@ var init_Sprite = __esm({
|
|
|
11063
11063
|
}
|
|
11064
11064
|
});
|
|
11065
11065
|
function StateIndicator({
|
|
11066
|
+
assetUrl = DEFAULT_ASSET_URL,
|
|
11066
11067
|
state = "idle",
|
|
11067
11068
|
label,
|
|
11068
11069
|
size = "md",
|
|
@@ -11085,18 +11086,29 @@ function StateIndicator({
|
|
|
11085
11086
|
className
|
|
11086
11087
|
),
|
|
11087
11088
|
children: [
|
|
11088
|
-
/* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", children:
|
|
11089
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
11090
|
+
"img",
|
|
11091
|
+
{
|
|
11092
|
+
src: assetUrl,
|
|
11093
|
+
alt: displayLabel,
|
|
11094
|
+
width: 16,
|
|
11095
|
+
height: 16,
|
|
11096
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11097
|
+
className: "flex-shrink-0"
|
|
11098
|
+
}
|
|
11099
|
+
) : 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 }) }),
|
|
11089
11100
|
/* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", children: displayLabel })
|
|
11090
11101
|
]
|
|
11091
11102
|
}
|
|
11092
11103
|
);
|
|
11093
11104
|
}
|
|
11094
|
-
var DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
11105
|
+
var DEFAULT_ASSET_URL, DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
11095
11106
|
var init_StateIndicator = __esm({
|
|
11096
11107
|
"components/game/atoms/StateIndicator.tsx"() {
|
|
11097
11108
|
init_Box();
|
|
11098
11109
|
init_Icon();
|
|
11099
11110
|
init_cn();
|
|
11111
|
+
DEFAULT_ASSET_URL = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
11100
11112
|
DEFAULT_STATE_STYLES = {
|
|
11101
11113
|
idle: { icon: "\u23F8", bgClass: "bg-muted" },
|
|
11102
11114
|
active: { icon: "\u25B6", bgClass: "bg-success" },
|
|
@@ -11171,6 +11183,7 @@ var init_TimerDisplay = __esm({
|
|
|
11171
11183
|
}
|
|
11172
11184
|
});
|
|
11173
11185
|
function ResourceCounter({
|
|
11186
|
+
assetUrl = DEFAULT_ASSET_URL2,
|
|
11174
11187
|
icon,
|
|
11175
11188
|
label = "Gold",
|
|
11176
11189
|
value = 250,
|
|
@@ -11190,7 +11203,17 @@ function ResourceCounter({
|
|
|
11190
11203
|
className
|
|
11191
11204
|
),
|
|
11192
11205
|
children: [
|
|
11193
|
-
|
|
11206
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
11207
|
+
"img",
|
|
11208
|
+
{
|
|
11209
|
+
src: assetUrl,
|
|
11210
|
+
alt: label,
|
|
11211
|
+
width: sizes.img,
|
|
11212
|
+
height: sizes.img,
|
|
11213
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11214
|
+
className: "flex-shrink-0"
|
|
11215
|
+
}
|
|
11216
|
+
) : 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,
|
|
11194
11217
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: label }),
|
|
11195
11218
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("font-bold tabular-nums", color && (color in colorTokenClasses2 ? colorTokenClasses2[color] : color)), children: [
|
|
11196
11219
|
value,
|
|
@@ -11203,7 +11226,7 @@ function ResourceCounter({
|
|
|
11203
11226
|
}
|
|
11204
11227
|
);
|
|
11205
11228
|
}
|
|
11206
|
-
var colorTokenClasses2, sizeMap5;
|
|
11229
|
+
var colorTokenClasses2, DEFAULT_ASSET_URL2, sizeMap5;
|
|
11207
11230
|
var init_ResourceCounter = __esm({
|
|
11208
11231
|
"components/game/atoms/ResourceCounter.tsx"() {
|
|
11209
11232
|
init_cn();
|
|
@@ -11216,16 +11239,17 @@ var init_ResourceCounter = __esm({
|
|
|
11216
11239
|
error: "text-error",
|
|
11217
11240
|
muted: "text-muted-foreground"
|
|
11218
11241
|
};
|
|
11242
|
+
DEFAULT_ASSET_URL2 = "https://almadar-kflow-assets.web.app/shared/world-map/gold_mine.png";
|
|
11219
11243
|
sizeMap5 = {
|
|
11220
|
-
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm" },
|
|
11221
|
-
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base" },
|
|
11222
|
-
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg" }
|
|
11244
|
+
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm", img: 16 },
|
|
11245
|
+
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base", img: 20 },
|
|
11246
|
+
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg", img: 28 }
|
|
11223
11247
|
};
|
|
11224
11248
|
ResourceCounter.displayName = "ResourceCounter";
|
|
11225
11249
|
}
|
|
11226
11250
|
});
|
|
11227
11251
|
function ItemSlot({
|
|
11228
|
-
assetUrl =
|
|
11252
|
+
assetUrl = DEFAULT_ASSET_URL3,
|
|
11229
11253
|
icon = "sword",
|
|
11230
11254
|
label = "Iron Sword",
|
|
11231
11255
|
quantity,
|
|
@@ -11284,7 +11308,7 @@ function ItemSlot({
|
|
|
11284
11308
|
}
|
|
11285
11309
|
);
|
|
11286
11310
|
}
|
|
11287
|
-
var sizeMap6, rarityBorderMap, rarityGlowMap,
|
|
11311
|
+
var sizeMap6, rarityBorderMap, rarityGlowMap, DEFAULT_ASSET_URL3, assetSizeMap;
|
|
11288
11312
|
var init_ItemSlot = __esm({
|
|
11289
11313
|
"components/game/atoms/ItemSlot.tsx"() {
|
|
11290
11314
|
"use client";
|
|
@@ -11309,7 +11333,7 @@ var init_ItemSlot = __esm({
|
|
|
11309
11333
|
epic: "shadow-lg",
|
|
11310
11334
|
legendary: "shadow-lg"
|
|
11311
11335
|
};
|
|
11312
|
-
|
|
11336
|
+
DEFAULT_ASSET_URL3 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
11313
11337
|
assetSizeMap = {
|
|
11314
11338
|
sm: 28,
|
|
11315
11339
|
md: 40,
|
|
@@ -11382,6 +11406,7 @@ function getComboScale(combo) {
|
|
|
11382
11406
|
return "";
|
|
11383
11407
|
}
|
|
11384
11408
|
function ComboCounter({
|
|
11409
|
+
assetUrl = DEFAULT_ASSET_URL4,
|
|
11385
11410
|
combo = 5,
|
|
11386
11411
|
multiplier,
|
|
11387
11412
|
streak,
|
|
@@ -11401,6 +11426,17 @@ function ComboCounter({
|
|
|
11401
11426
|
className
|
|
11402
11427
|
),
|
|
11403
11428
|
children: [
|
|
11429
|
+
assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11430
|
+
"img",
|
|
11431
|
+
{
|
|
11432
|
+
src: assetUrl,
|
|
11433
|
+
alt: "combo",
|
|
11434
|
+
width: 24,
|
|
11435
|
+
height: 24,
|
|
11436
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11437
|
+
className: "flex-shrink-0 mb-0.5"
|
|
11438
|
+
}
|
|
11439
|
+
),
|
|
11404
11440
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-black tabular-nums leading-none", sizes.combo, getComboIntensity(combo)), children: combo }),
|
|
11405
11441
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-bold uppercase tracking-wider text-muted-foreground", sizes.label), children: "combo" }),
|
|
11406
11442
|
multiplier != null && multiplier > 1 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("font-bold text-warning tabular-nums", sizes.multiplier), children: [
|
|
@@ -11415,10 +11451,11 @@ function ComboCounter({
|
|
|
11415
11451
|
}
|
|
11416
11452
|
);
|
|
11417
11453
|
}
|
|
11418
|
-
var sizeMap8;
|
|
11454
|
+
var DEFAULT_ASSET_URL4, sizeMap8;
|
|
11419
11455
|
var init_ComboCounter = __esm({
|
|
11420
11456
|
"components/game/atoms/ComboCounter.tsx"() {
|
|
11421
11457
|
init_cn();
|
|
11458
|
+
DEFAULT_ASSET_URL4 = "https://almadar-kflow-assets.web.app/shared/effects/flash/flash00.png";
|
|
11422
11459
|
sizeMap8 = {
|
|
11423
11460
|
sm: { combo: "text-lg", label: "text-xs", multiplier: "text-xs" },
|
|
11424
11461
|
md: { combo: "text-2xl", label: "text-xs", multiplier: "text-sm" },
|
|
@@ -11512,6 +11549,7 @@ var init_XPBar = __esm({
|
|
|
11512
11549
|
}
|
|
11513
11550
|
});
|
|
11514
11551
|
function WaypointMarker({
|
|
11552
|
+
assetUrl = DEFAULT_ASSET_URL5,
|
|
11515
11553
|
label,
|
|
11516
11554
|
icon,
|
|
11517
11555
|
active = true,
|
|
@@ -11550,7 +11588,17 @@ function WaypointMarker({
|
|
|
11550
11588
|
active && !completed && "bg-info text-foreground",
|
|
11551
11589
|
!active && !completed && "bg-muted"
|
|
11552
11590
|
),
|
|
11553
|
-
children: completed ? checkIcon :
|
|
11591
|
+
children: completed ? checkIcon : assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
11592
|
+
"img",
|
|
11593
|
+
{
|
|
11594
|
+
src: assetUrl,
|
|
11595
|
+
alt: label,
|
|
11596
|
+
width: sizes.img,
|
|
11597
|
+
height: sizes.img,
|
|
11598
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11599
|
+
className: "flex-shrink-0"
|
|
11600
|
+
}
|
|
11601
|
+
) : icon ? typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon }) : null
|
|
11554
11602
|
}
|
|
11555
11603
|
)
|
|
11556
11604
|
] }),
|
|
@@ -11567,15 +11615,16 @@ function WaypointMarker({
|
|
|
11567
11615
|
)
|
|
11568
11616
|
] });
|
|
11569
11617
|
}
|
|
11570
|
-
var sizeMap10, checkIcon;
|
|
11618
|
+
var DEFAULT_ASSET_URL5, sizeMap10, checkIcon;
|
|
11571
11619
|
var init_WaypointMarker = __esm({
|
|
11572
11620
|
"components/game/atoms/WaypointMarker.tsx"() {
|
|
11573
11621
|
init_cn();
|
|
11574
11622
|
init_Icon();
|
|
11623
|
+
DEFAULT_ASSET_URL5 = "https://almadar-kflow-assets.web.app/shared/world-map/battle_marker.png";
|
|
11575
11624
|
sizeMap10 = {
|
|
11576
|
-
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1" },
|
|
11577
|
-
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5" },
|
|
11578
|
-
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2" }
|
|
11625
|
+
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1", img: 12 },
|
|
11626
|
+
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5", img: 18 },
|
|
11627
|
+
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2", img: 24 }
|
|
11579
11628
|
};
|
|
11580
11629
|
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" }) });
|
|
11581
11630
|
WaypointMarker.displayName = "WaypointMarker";
|
|
@@ -11588,6 +11637,7 @@ function formatDuration(seconds) {
|
|
|
11588
11637
|
return `${Math.round(seconds)}s`;
|
|
11589
11638
|
}
|
|
11590
11639
|
function StatusEffect({
|
|
11640
|
+
assetUrl = DEFAULT_ASSET_URL6,
|
|
11591
11641
|
icon = "shield",
|
|
11592
11642
|
label = "Shield",
|
|
11593
11643
|
duration = 30,
|
|
@@ -11608,7 +11658,17 @@ function StatusEffect({
|
|
|
11608
11658
|
),
|
|
11609
11659
|
title: label,
|
|
11610
11660
|
children: [
|
|
11611
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children:
|
|
11661
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
11662
|
+
"img",
|
|
11663
|
+
{
|
|
11664
|
+
src: assetUrl,
|
|
11665
|
+
alt: label,
|
|
11666
|
+
width: sizes.img,
|
|
11667
|
+
height: sizes.img,
|
|
11668
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11669
|
+
className: "flex-shrink-0"
|
|
11670
|
+
}
|
|
11671
|
+
) : typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, size: "sm" }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, size: "sm" }) }),
|
|
11612
11672
|
duration !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11613
11673
|
"span",
|
|
11614
11674
|
{
|
|
@@ -11635,15 +11695,16 @@ function StatusEffect({
|
|
|
11635
11695
|
label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground mt-0.5 text-center whitespace-nowrap", children: label })
|
|
11636
11696
|
] });
|
|
11637
11697
|
}
|
|
11638
|
-
var sizeMap11, variantStyles7;
|
|
11698
|
+
var DEFAULT_ASSET_URL6, sizeMap11, variantStyles7;
|
|
11639
11699
|
var init_StatusEffect = __esm({
|
|
11640
11700
|
"components/game/atoms/StatusEffect.tsx"() {
|
|
11641
11701
|
init_cn();
|
|
11642
11702
|
init_Icon();
|
|
11703
|
+
DEFAULT_ASSET_URL6 = "https://almadar-kflow-assets.web.app/shared/effects/particles/flame_01.png";
|
|
11643
11704
|
sizeMap11 = {
|
|
11644
|
-
sm: { container: "w-8 h-8", icon: "text-sm", badge: "text-xs -top-1 -right-1 w-4 h-4", timer: "text-[9px]" },
|
|
11645
|
-
md: { container: "w-10 h-10", icon: "text-base", badge: "text-xs -top-1 -right-1 w-5 h-5", timer: "text-xs" },
|
|
11646
|
-
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" }
|
|
11705
|
+
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 },
|
|
11706
|
+
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 },
|
|
11707
|
+
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 }
|
|
11647
11708
|
};
|
|
11648
11709
|
variantStyles7 = {
|
|
11649
11710
|
buff: "border-success bg-success/20",
|
|
@@ -15212,15 +15273,15 @@ function BattleBoard({
|
|
|
15212
15273
|
const board = boardEntity(entity) ?? {};
|
|
15213
15274
|
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
|
|
15214
15275
|
const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
|
|
15215
|
-
const boardWidth = num(board.boardWidth, 8);
|
|
15216
|
-
const boardHeight = num(board.boardHeight, 6);
|
|
15276
|
+
const boardWidth = num(board.gridWidth ?? board.boardWidth, 8);
|
|
15277
|
+
const boardHeight = num(board.gridHeight ?? board.boardHeight, 6);
|
|
15217
15278
|
const assetManifest = propAssetManifest ?? board.assetManifest;
|
|
15218
15279
|
const backgroundImage = board.backgroundImage;
|
|
15219
15280
|
const units = rows(board.units);
|
|
15220
15281
|
const selectedUnitId = board.selectedUnitId ?? null;
|
|
15221
15282
|
const currentPhase = str(board.phase) || "observation";
|
|
15222
15283
|
const currentTurn = num(board.turn, 1);
|
|
15223
|
-
const gameResult = board.
|
|
15284
|
+
const gameResult = board.result ?? null;
|
|
15224
15285
|
const eventBus = useEventBus();
|
|
15225
15286
|
const { t } = hooks.useTranslate();
|
|
15226
15287
|
const [hoveredTile, setHoveredTile] = React91.useState(null);
|
|
@@ -15241,7 +15302,7 @@ function BattleBoard({
|
|
|
15241
15302
|
const validMoves = React91.useMemo(() => {
|
|
15242
15303
|
if (!selectedUnit || currentPhase !== "movement") return [];
|
|
15243
15304
|
const moves = [];
|
|
15244
|
-
const range = num(
|
|
15305
|
+
const range = num(board.movementRange, 2);
|
|
15245
15306
|
const origin = unitPosition(selectedUnit);
|
|
15246
15307
|
for (let dy = -range; dy <= range; dy++) {
|
|
15247
15308
|
for (let dx = -range; dx <= range; dx++) {
|
|
@@ -22697,15 +22758,24 @@ function CastleBoard({
|
|
|
22697
22758
|
featureClickEvent,
|
|
22698
22759
|
unitClickEvent,
|
|
22699
22760
|
tileClickEvent,
|
|
22761
|
+
playAgainEvent,
|
|
22700
22762
|
className
|
|
22701
22763
|
}) {
|
|
22702
22764
|
const eventBus = useEventBus();
|
|
22765
|
+
const { t } = hooks.useTranslate();
|
|
22703
22766
|
const resolved = boardEntity(entity);
|
|
22704
22767
|
const tiles = propTiles ?? (Array.isArray(resolved?.tiles) ? resolved.tiles : []);
|
|
22705
22768
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
22706
22769
|
const units = propUnits ?? (Array.isArray(resolved?.units) ? resolved.units : []);
|
|
22707
22770
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
22708
22771
|
const backgroundImage = resolved?.backgroundImage;
|
|
22772
|
+
const gold = num(resolved?.gold);
|
|
22773
|
+
const health = num(resolved?.health);
|
|
22774
|
+
const maxHealth = num(resolved?.maxHealth);
|
|
22775
|
+
const wave = num(resolved?.wave);
|
|
22776
|
+
const tickCount = num(resolved?.tickCount);
|
|
22777
|
+
const buildings = rows(resolved?.buildings);
|
|
22778
|
+
const result = str(resolved?.result) || "none";
|
|
22709
22779
|
const [hoveredTile, setHoveredTile] = React91.useState(null);
|
|
22710
22780
|
const [selectedFeature, setSelectedFeature] = React91.useState(null);
|
|
22711
22781
|
const hoveredFeature = React91.useMemo(() => {
|
|
@@ -22718,7 +22788,7 @@ function CastleBoard({
|
|
|
22718
22788
|
(u) => u.position?.x === hoveredTile.x && u.position?.y === hoveredTile.y
|
|
22719
22789
|
) ?? null;
|
|
22720
22790
|
}, [hoveredTile, units]);
|
|
22721
|
-
const maxY = Math.max(...tiles.map((
|
|
22791
|
+
const maxY = Math.max(...tiles.map((t2) => t2.y), 0);
|
|
22722
22792
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
22723
22793
|
const tileToScreen = React91.useCallback(
|
|
22724
22794
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
@@ -22753,6 +22823,11 @@ function CastleBoard({
|
|
|
22753
22823
|
}
|
|
22754
22824
|
}, [units, onUnitClick, unitClickEvent, eventBus]);
|
|
22755
22825
|
const clearSelection = React91.useCallback(() => setSelectedFeature(null), []);
|
|
22826
|
+
const handlePlayAgain = React91.useCallback(() => {
|
|
22827
|
+
if (playAgainEvent) {
|
|
22828
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
22829
|
+
}
|
|
22830
|
+
}, [playAgainEvent, eventBus]);
|
|
22756
22831
|
const ctx = React91.useMemo(
|
|
22757
22832
|
() => ({
|
|
22758
22833
|
hoveredTile,
|
|
@@ -22761,11 +22836,18 @@ function CastleBoard({
|
|
|
22761
22836
|
selectedFeature,
|
|
22762
22837
|
clearSelection,
|
|
22763
22838
|
tileToScreen,
|
|
22764
|
-
scale
|
|
22839
|
+
scale,
|
|
22840
|
+
gold,
|
|
22841
|
+
health,
|
|
22842
|
+
maxHealth,
|
|
22843
|
+
wave,
|
|
22844
|
+
tickCount,
|
|
22845
|
+
buildings,
|
|
22846
|
+
result
|
|
22765
22847
|
}),
|
|
22766
|
-
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale]
|
|
22848
|
+
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale, gold, health, maxHealth, wave, tickCount, buildings, result]
|
|
22767
22849
|
);
|
|
22768
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("castle-board min-h-screen flex flex-col bg-background", className), children: [
|
|
22850
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("castle-board relative min-h-screen flex flex-col bg-background", className), children: [
|
|
22769
22851
|
header && header(ctx),
|
|
22770
22852
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
|
|
22771
22853
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-auto p-4 relative", children: [
|
|
@@ -22790,7 +22872,29 @@ function CastleBoard({
|
|
|
22790
22872
|
] }),
|
|
22791
22873
|
sidePanel && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-96 shrink-0 border-l border-border bg-surface overflow-y-auto", children: sidePanel(ctx) })
|
|
22792
22874
|
] }),
|
|
22793
|
-
footer && footer(ctx)
|
|
22875
|
+
footer && footer(ctx),
|
|
22876
|
+
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: [
|
|
22877
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
22878
|
+
Typography,
|
|
22879
|
+
{
|
|
22880
|
+
variant: "h2",
|
|
22881
|
+
className: cn(
|
|
22882
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
22883
|
+
result === "victory" ? "text-warning" : "text-error"
|
|
22884
|
+
),
|
|
22885
|
+
children: result === "victory" ? t("battle.victory") : t("battle.defeat")
|
|
22886
|
+
}
|
|
22887
|
+
),
|
|
22888
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
22889
|
+
Button,
|
|
22890
|
+
{
|
|
22891
|
+
variant: "primary",
|
|
22892
|
+
className: "px-8 py-3 font-semibold",
|
|
22893
|
+
onClick: handlePlayAgain,
|
|
22894
|
+
children: t("battle.playAgain")
|
|
22895
|
+
}
|
|
22896
|
+
)
|
|
22897
|
+
] }) })
|
|
22794
22898
|
] });
|
|
22795
22899
|
}
|
|
22796
22900
|
var init_CastleBoard = __esm({
|
|
@@ -22798,6 +22902,10 @@ var init_CastleBoard = __esm({
|
|
|
22798
22902
|
"use client";
|
|
22799
22903
|
init_cn();
|
|
22800
22904
|
init_useEventBus();
|
|
22905
|
+
init_Box();
|
|
22906
|
+
init_Button();
|
|
22907
|
+
init_Typography();
|
|
22908
|
+
init_Stack();
|
|
22801
22909
|
init_IsometricCanvas();
|
|
22802
22910
|
init_boardEntity();
|
|
22803
22911
|
init_isometric();
|
|
@@ -23674,22 +23782,16 @@ function ClassifierBoard({
|
|
|
23674
23782
|
const { emit } = useEventBus();
|
|
23675
23783
|
const { t } = hooks.useTranslate();
|
|
23676
23784
|
const resolved = boardEntity(entity);
|
|
23677
|
-
const [localAssignments, setLocalAssignments] = React91.useState({});
|
|
23678
23785
|
const [headerError, setHeaderError] = React91.useState(false);
|
|
23679
|
-
const [localSubmitted, setLocalSubmitted] = React91.useState(false);
|
|
23680
|
-
const [localAttempts, setLocalAttempts] = React91.useState(0);
|
|
23681
|
-
const [showHint, setShowHint] = React91.useState(false);
|
|
23682
23786
|
const items = Array.isArray(resolved?.items) ? resolved.items : [];
|
|
23683
23787
|
const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
|
|
23684
|
-
const
|
|
23685
|
-
const
|
|
23686
|
-
const
|
|
23687
|
-
const assignments =
|
|
23688
|
-
if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
|
|
23788
|
+
const result = str(resolved?.result);
|
|
23789
|
+
const submitted = result === "win";
|
|
23790
|
+
const attempts = num(resolved?.attempts);
|
|
23791
|
+
const assignments = items.reduce((acc, item) => {
|
|
23792
|
+
if (item.assignedCategory != null && item.assignedCategory !== "") acc[item.id] = item.assignedCategory;
|
|
23689
23793
|
return acc;
|
|
23690
|
-
}, {})
|
|
23691
|
-
const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
|
|
23692
|
-
const submitted = entityDrivesResult || localSubmitted;
|
|
23794
|
+
}, {});
|
|
23693
23795
|
const unassignedItems = items.filter((item) => !assignments[item.id]);
|
|
23694
23796
|
const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
|
|
23695
23797
|
const results = submitted ? items.map((item) => ({
|
|
@@ -23697,54 +23799,25 @@ function ClassifierBoard({
|
|
|
23697
23799
|
assigned: assignments[item.id],
|
|
23698
23800
|
correct: assignments[item.id] === item.correctCategory
|
|
23699
23801
|
})) : [];
|
|
23700
|
-
const allCorrect =
|
|
23802
|
+
const allCorrect = result === "win";
|
|
23701
23803
|
const correctCount = results.filter((r2) => r2.correct).length;
|
|
23804
|
+
const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
|
|
23702
23805
|
const handleAssign = (itemId, categoryId) => {
|
|
23703
23806
|
if (submitted) return;
|
|
23704
|
-
if (assignEvent) {
|
|
23705
|
-
emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
23706
|
-
}
|
|
23707
|
-
if (!entityHasAssignments) {
|
|
23708
|
-
setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
|
|
23709
|
-
}
|
|
23807
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
23710
23808
|
};
|
|
23711
23809
|
const handleUnassign = (itemId) => {
|
|
23712
23810
|
if (submitted) return;
|
|
23713
|
-
if (
|
|
23714
|
-
setLocalAssignments((prev) => {
|
|
23715
|
-
const next = { ...prev };
|
|
23716
|
-
delete next[itemId];
|
|
23717
|
-
return next;
|
|
23718
|
-
});
|
|
23719
|
-
}
|
|
23811
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId: "" });
|
|
23720
23812
|
};
|
|
23721
|
-
const handleSubmit =
|
|
23722
|
-
if (checkEvent) {
|
|
23723
|
-
|
|
23724
|
-
|
|
23725
|
-
if (!entityDrivesResult) {
|
|
23726
|
-
setLocalSubmitted(true);
|
|
23727
|
-
setLocalAttempts((a) => a + 1);
|
|
23728
|
-
const correct = items.every((item) => assignments[item.id] === item.correctCategory);
|
|
23729
|
-
if (correct) {
|
|
23730
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
23731
|
-
}
|
|
23732
|
-
}
|
|
23733
|
-
}, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
|
|
23734
|
-
const handleReset = () => {
|
|
23735
|
-
if (!entityDrivesResult) setLocalSubmitted(false);
|
|
23736
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
23737
|
-
setShowHint(true);
|
|
23813
|
+
const handleSubmit = () => {
|
|
23814
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
23815
|
+
if (allAssigned && items.every((item) => assignments[item.id] === item.correctCategory)) {
|
|
23816
|
+
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
23738
23817
|
}
|
|
23739
23818
|
};
|
|
23740
23819
|
const handleFullReset = () => {
|
|
23741
|
-
if (playAgainEvent) {
|
|
23742
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
23743
|
-
}
|
|
23744
|
-
setLocalAssignments({});
|
|
23745
|
-
setLocalSubmitted(false);
|
|
23746
|
-
setLocalAttempts(0);
|
|
23747
|
-
setShowHint(false);
|
|
23820
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
23748
23821
|
};
|
|
23749
23822
|
if (!resolved) return null;
|
|
23750
23823
|
const theme = resolved.theme ?? void 0;
|
|
@@ -23783,17 +23856,17 @@ function ClassifierBoard({
|
|
|
23783
23856
|
/* @__PURE__ */ jsxRuntime.jsx(Badge, { size: "sm", children: catItems.length })
|
|
23784
23857
|
] }),
|
|
23785
23858
|
/* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-wrap min-h-[2rem]", children: catItems.map((item) => {
|
|
23786
|
-
const
|
|
23859
|
+
const result2 = results.find((r2) => r2.item.id === item.id);
|
|
23787
23860
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
23788
23861
|
Badge,
|
|
23789
23862
|
{
|
|
23790
23863
|
size: "sm",
|
|
23791
|
-
className: `cursor-pointer ${
|
|
23864
|
+
className: `cursor-pointer ${result2 ? result2.correct ? "border-success bg-success/10" : "border-error bg-error/10" : ""}`,
|
|
23792
23865
|
onClick: () => handleUnassign(item.id),
|
|
23793
23866
|
children: [
|
|
23794
23867
|
item.iconUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: item.iconUrl, alt: "", className: "w-3 h-3 object-contain inline-block" }),
|
|
23795
23868
|
item.label,
|
|
23796
|
-
|
|
23869
|
+
result2 && /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: result2.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "xs", className: result2.correct ? "text-success" : "text-error" })
|
|
23797
23870
|
]
|
|
23798
23871
|
},
|
|
23799
23872
|
item.id
|
|
@@ -23822,10 +23895,10 @@ function ClassifierBoard({
|
|
|
23822
23895
|
] }) }),
|
|
23823
23896
|
showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
|
|
23824
23897
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
23825
|
-
!submitted
|
|
23898
|
+
!submitted && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
|
|
23826
23899
|
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
|
|
23827
23900
|
t("classifier.check")
|
|
23828
|
-
] })
|
|
23901
|
+
] }),
|
|
23829
23902
|
/* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
|
|
23830
23903
|
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
|
|
23831
23904
|
t("classifier.reset")
|
|
@@ -31854,7 +31927,7 @@ var init_MapView = __esm({
|
|
|
31854
31927
|
shadowSize: [41, 41]
|
|
31855
31928
|
});
|
|
31856
31929
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
31857
|
-
const { useEffect: useEffect78, useRef: useRef70, useCallback:
|
|
31930
|
+
const { useEffect: useEffect78, useRef: useRef70, useCallback: useCallback117, useState: useState115 } = React91__namespace.default;
|
|
31858
31931
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
31859
31932
|
const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
31860
31933
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
@@ -31900,7 +31973,7 @@ var init_MapView = __esm({
|
|
|
31900
31973
|
}) {
|
|
31901
31974
|
const eventBus = useEventBus3();
|
|
31902
31975
|
const [clickedPosition, setClickedPosition] = useState115(null);
|
|
31903
|
-
const handleMapClick =
|
|
31976
|
+
const handleMapClick = useCallback117((lat, lng) => {
|
|
31904
31977
|
if (showClickedPin) {
|
|
31905
31978
|
setClickedPosition({ lat, lng });
|
|
31906
31979
|
}
|
|
@@ -31909,7 +31982,7 @@ var init_MapView = __esm({
|
|
|
31909
31982
|
eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
|
|
31910
31983
|
}
|
|
31911
31984
|
}, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
|
|
31912
|
-
const handleMarkerClick =
|
|
31985
|
+
const handleMarkerClick = useCallback117((marker) => {
|
|
31913
31986
|
onMarkerClick?.(marker);
|
|
31914
31987
|
if (markerClickEvent) {
|
|
31915
31988
|
eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
|
|
@@ -40731,44 +40804,27 @@ function DebuggerBoard({
|
|
|
40731
40804
|
const { t } = hooks.useTranslate();
|
|
40732
40805
|
const resolved = boardEntity(entity);
|
|
40733
40806
|
const [headerError, setHeaderError] = React91.useState(false);
|
|
40734
|
-
const [showHint, setShowHint] = React91.useState(false);
|
|
40735
40807
|
const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
|
|
40736
|
-
const result = resolved?.result
|
|
40808
|
+
const result = str(resolved?.result) || "none";
|
|
40737
40809
|
const attempts = num(resolved?.attempts);
|
|
40738
|
-
const submitted = result
|
|
40810
|
+
const submitted = result === "win";
|
|
40811
|
+
const showHint = !submitted && attempts >= 2 && Boolean(str(resolved?.hint));
|
|
40739
40812
|
const bugLines = lines.filter((l) => l.isBug);
|
|
40740
40813
|
const flaggedLines = lines.filter((l) => l.isFlagged);
|
|
40741
40814
|
const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
|
|
40742
40815
|
const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
|
|
40743
|
-
const allCorrect =
|
|
40816
|
+
const allCorrect = submitted;
|
|
40744
40817
|
const toggleLine = (lineId) => {
|
|
40745
40818
|
if (submitted) return;
|
|
40746
|
-
if (toggleFlagEvent) {
|
|
40747
|
-
emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
40748
|
-
}
|
|
40819
|
+
if (toggleFlagEvent) emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
40749
40820
|
};
|
|
40750
40821
|
const handleSubmit = React91.useCallback(() => {
|
|
40751
|
-
if (checkEvent) {
|
|
40752
|
-
emit(`UI:${checkEvent}`, {});
|
|
40753
|
-
}
|
|
40822
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
40754
40823
|
const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
|
|
40755
|
-
if (correct) {
|
|
40756
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
40757
|
-
}
|
|
40824
|
+
if (correct) emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
40758
40825
|
}, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
|
|
40759
40826
|
const handleReset = () => {
|
|
40760
|
-
if (playAgainEvent) {
|
|
40761
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
40762
|
-
}
|
|
40763
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
40764
|
-
setShowHint(true);
|
|
40765
|
-
}
|
|
40766
|
-
};
|
|
40767
|
-
const handleFullReset = () => {
|
|
40768
|
-
if (playAgainEvent) {
|
|
40769
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
40770
|
-
}
|
|
40771
|
-
setShowHint(false);
|
|
40827
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
40772
40828
|
};
|
|
40773
40829
|
if (!resolved) return null;
|
|
40774
40830
|
const theme = resolved.theme ?? void 0;
|
|
@@ -40792,7 +40848,7 @@ function DebuggerBoard({
|
|
|
40792
40848
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
|
|
40793
40849
|
] }),
|
|
40794
40850
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str(resolved.description) }),
|
|
40795
|
-
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(
|
|
40851
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(lines.filter((l) => l.isBug).length) }) })
|
|
40796
40852
|
] }) }),
|
|
40797
40853
|
/* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "none", children: lines.map((line, i) => {
|
|
40798
40854
|
const isFlagged = !!line.isFlagged;
|
|
@@ -40842,11 +40898,11 @@ function DebuggerBoard({
|
|
|
40842
40898
|
] }) }),
|
|
40843
40899
|
showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
|
|
40844
40900
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
40845
|
-
!submitted
|
|
40901
|
+
!submitted && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
|
|
40846
40902
|
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
|
|
40847
40903
|
t("debugger.submit")
|
|
40848
|
-
] })
|
|
40849
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick:
|
|
40904
|
+
] }),
|
|
40905
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handleReset, children: [
|
|
40850
40906
|
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
|
|
40851
40907
|
t("debugger.reset")
|
|
40852
40908
|
] })
|
|
@@ -41847,39 +41903,40 @@ function EventHandlerBoard({
|
|
|
41847
41903
|
stepDurationMs = 800,
|
|
41848
41904
|
playEvent,
|
|
41849
41905
|
completeEvent,
|
|
41906
|
+
editRuleEvent,
|
|
41907
|
+
playAgainEvent,
|
|
41850
41908
|
className
|
|
41851
41909
|
}) {
|
|
41852
41910
|
const { emit } = useEventBus();
|
|
41853
41911
|
const { t } = hooks.useTranslate();
|
|
41854
41912
|
const resolved = boardEntity(entity);
|
|
41855
|
-
const
|
|
41856
|
-
const
|
|
41913
|
+
const objects = rows(resolved?.objects);
|
|
41914
|
+
const entityResult = str(resolved?.result) || "none";
|
|
41915
|
+
const isSuccess = entityResult === "win";
|
|
41916
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
41857
41917
|
const [selectedObjectId, setSelectedObjectId] = React91.useState(
|
|
41858
|
-
|
|
41918
|
+
objects[0] ? objId(objects[0]) : null
|
|
41859
41919
|
);
|
|
41860
41920
|
const [headerError, setHeaderError] = React91.useState(false);
|
|
41861
|
-
const [
|
|
41921
|
+
const [isPlaying, setIsPlaying] = React91.useState(false);
|
|
41862
41922
|
const [eventLog, setEventLog] = React91.useState([]);
|
|
41863
|
-
const [attempts, setAttempts] = React91.useState(0);
|
|
41864
41923
|
const timerRef = React91.useRef(null);
|
|
41865
41924
|
const logIdCounter = React91.useRef(0);
|
|
41866
41925
|
React91.useEffect(() => () => {
|
|
41867
41926
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
41868
41927
|
}, []);
|
|
41869
|
-
const selectedObject = objects.find((o) => objId(o) === selectedObjectId)
|
|
41928
|
+
const selectedObject = objects.find((o) => objId(o) === selectedObjectId) ?? null;
|
|
41870
41929
|
const handleRulesChange = React91.useCallback((objectId, rules) => {
|
|
41871
|
-
|
|
41872
|
-
|
|
41873
|
-
));
|
|
41874
|
-
}, []);
|
|
41930
|
+
if (editRuleEvent) emit(`UI:${editRuleEvent}`, { objectId, rules });
|
|
41931
|
+
}, [editRuleEvent, emit]);
|
|
41875
41932
|
const addLogEntry = React91.useCallback((icon, message, status = "done") => {
|
|
41876
41933
|
const id = `log-${logIdCounter.current++}`;
|
|
41877
41934
|
setEventLog((prev) => [...prev, { id, timestamp: Date.now(), icon, message, status }]);
|
|
41878
41935
|
}, []);
|
|
41879
41936
|
const handlePlay = React91.useCallback(() => {
|
|
41880
|
-
if (
|
|
41937
|
+
if (isPlaying || isSuccess) return;
|
|
41881
41938
|
if (playEvent) emit(`UI:${playEvent}`, {});
|
|
41882
|
-
|
|
41939
|
+
setIsPlaying(true);
|
|
41883
41940
|
setEventLog([]);
|
|
41884
41941
|
const allRules = [];
|
|
41885
41942
|
objects.forEach((obj) => {
|
|
@@ -41895,15 +41952,8 @@ function EventHandlerBoard({
|
|
|
41895
41952
|
let goalReached = false;
|
|
41896
41953
|
const processNext = () => {
|
|
41897
41954
|
if (eventQueue.length === 0 || stepIdx > 20) {
|
|
41898
|
-
|
|
41899
|
-
|
|
41900
|
-
if (completeEvent) {
|
|
41901
|
-
emit(`UI:${completeEvent}`, { success: true });
|
|
41902
|
-
}
|
|
41903
|
-
} else {
|
|
41904
|
-
setAttempts((prev) => prev + 1);
|
|
41905
|
-
setPlayState("fail");
|
|
41906
|
-
}
|
|
41955
|
+
setIsPlaying(false);
|
|
41956
|
+
if (goalReached && completeEvent) emit(`UI:${completeEvent}`, { success: true });
|
|
41907
41957
|
return;
|
|
41908
41958
|
}
|
|
41909
41959
|
const currentEvent = eventQueue.shift();
|
|
@@ -41934,21 +41984,19 @@ function EventHandlerBoard({
|
|
|
41934
41984
|
addLogEntry("\u{1F3AC}", t("eventHandler.simulationStarted", { events: triggers.join(", ") }), "active");
|
|
41935
41985
|
}
|
|
41936
41986
|
timerRef.current = setTimeout(processNext, stepDurationMs);
|
|
41937
|
-
}, [
|
|
41987
|
+
}, [isPlaying, isSuccess, objects, resolved, stepDurationMs, playEvent, completeEvent, emit, addLogEntry, t]);
|
|
41938
41988
|
const handleTryAgain = React91.useCallback(() => {
|
|
41939
41989
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
41940
|
-
|
|
41990
|
+
setIsPlaying(false);
|
|
41941
41991
|
setEventLog([]);
|
|
41942
41992
|
}, []);
|
|
41943
41993
|
const handleReset = React91.useCallback(() => {
|
|
41944
41994
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
41945
|
-
|
|
41946
|
-
setObjects([...resetObjects]);
|
|
41947
|
-
setPlayState("editing");
|
|
41995
|
+
setIsPlaying(false);
|
|
41948
41996
|
setEventLog([]);
|
|
41949
|
-
setSelectedObjectId(
|
|
41950
|
-
|
|
41951
|
-
}, [
|
|
41997
|
+
setSelectedObjectId(objects[0] ? objId(objects[0]) : null);
|
|
41998
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
41999
|
+
}, [objects, playAgainEvent, emit]);
|
|
41952
42000
|
if (!resolved) return null;
|
|
41953
42001
|
const objectViewers = objects.map((obj) => {
|
|
41954
42002
|
const states = objStates(obj);
|
|
@@ -42017,12 +42065,12 @@ function EventHandlerBoard({
|
|
|
42017
42065
|
{
|
|
42018
42066
|
object: selectedObject,
|
|
42019
42067
|
onRulesChange: handleRulesChange,
|
|
42020
|
-
disabled:
|
|
42068
|
+
disabled: isPlaying
|
|
42021
42069
|
}
|
|
42022
42070
|
),
|
|
42023
42071
|
eventLog.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(EventLog, { entries: eventLog }),
|
|
42024
|
-
|
|
42025
|
-
|
|
42072
|
+
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") }) }),
|
|
42073
|
+
!isPlaying && !isSuccess && attempts > 0 && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
42026
42074
|
/* @__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) }) }),
|
|
42027
42075
|
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: [
|
|
42028
42076
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
@@ -42030,12 +42078,12 @@ function EventHandlerBoard({
|
|
|
42030
42078
|
] }) })
|
|
42031
42079
|
] }),
|
|
42032
42080
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
|
|
42033
|
-
|
|
42081
|
+
!isPlaying && !isSuccess && attempts > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
42034
42082
|
Button,
|
|
42035
42083
|
{
|
|
42036
42084
|
variant: "primary",
|
|
42037
42085
|
onClick: handlePlay,
|
|
42038
|
-
disabled:
|
|
42086
|
+
disabled: isPlaying || isSuccess,
|
|
42039
42087
|
children: "\u25B6 " + t("game.play")
|
|
42040
42088
|
}
|
|
42041
42089
|
),
|
|
@@ -44588,21 +44636,6 @@ var init_ModalSlot = __esm({
|
|
|
44588
44636
|
ModalSlot.displayName = "ModalSlot";
|
|
44589
44637
|
}
|
|
44590
44638
|
});
|
|
44591
|
-
function getOpponentAction(strategy, actions, history) {
|
|
44592
|
-
const actionIds = actions.map((a) => a.id);
|
|
44593
|
-
switch (strategy) {
|
|
44594
|
-
case "always-cooperate":
|
|
44595
|
-
return actionIds[0];
|
|
44596
|
-
case "always-defect":
|
|
44597
|
-
return actionIds[actionIds.length - 1];
|
|
44598
|
-
case "tit-for-tat":
|
|
44599
|
-
if (history.length === 0) return actionIds[0];
|
|
44600
|
-
return history[history.length - 1].playerAction;
|
|
44601
|
-
case "random":
|
|
44602
|
-
default:
|
|
44603
|
-
return actionIds[Math.floor(Math.random() * actionIds.length)];
|
|
44604
|
-
}
|
|
44605
|
-
}
|
|
44606
44639
|
function NegotiatorBoard({
|
|
44607
44640
|
entity,
|
|
44608
44641
|
completeEvent = "PUZZLE_COMPLETE",
|
|
@@ -44624,28 +44657,35 @@ function NegotiatorBoard({
|
|
|
44624
44657
|
const playerTotal = num(resolved?.score);
|
|
44625
44658
|
const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
|
|
44626
44659
|
const won = result === "win";
|
|
44627
|
-
const
|
|
44660
|
+
const lastPlayerAction = str(resolved?.lastPlayerAction);
|
|
44661
|
+
const lastOpponentAction = str(resolved?.lastOpponentAction);
|
|
44662
|
+
const lastPayoff = num(resolved?.lastPayoff);
|
|
44628
44663
|
const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
|
|
44629
44664
|
const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
|
|
44665
|
+
const prevRoundRef = React91.useRef(currentRound);
|
|
44666
|
+
React91.useEffect(() => {
|
|
44667
|
+
if (currentRound > prevRoundRef.current && lastPlayerAction) {
|
|
44668
|
+
const opponentPayoffEntry = payoffMatrix.find(
|
|
44669
|
+
(p2) => p2.playerAction === lastPlayerAction && p2.opponentAction === lastOpponentAction
|
|
44670
|
+
);
|
|
44671
|
+
setHistory((prev) => [
|
|
44672
|
+
...prev,
|
|
44673
|
+
{
|
|
44674
|
+
round: currentRound,
|
|
44675
|
+
playerAction: lastPlayerAction,
|
|
44676
|
+
opponentAction: lastOpponentAction,
|
|
44677
|
+
playerPayoff: lastPayoff,
|
|
44678
|
+
opponentPayoff: opponentPayoffEntry?.opponentPayoff ?? 0
|
|
44679
|
+
}
|
|
44680
|
+
]);
|
|
44681
|
+
}
|
|
44682
|
+
prevRoundRef.current = currentRound;
|
|
44683
|
+
}, [currentRound, lastPlayerAction, lastOpponentAction, lastPayoff, payoffMatrix]);
|
|
44684
|
+
const opponentTotal = React91.useMemo(() => history.reduce((s, r2) => s + r2.opponentPayoff, 0), [history]);
|
|
44630
44685
|
const handleAction = React91.useCallback((actionId) => {
|
|
44631
44686
|
if (isComplete) return;
|
|
44632
|
-
const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
|
|
44633
|
-
const payoff = payoffMatrix.find(
|
|
44634
|
-
(p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
|
|
44635
|
-
);
|
|
44636
|
-
const playerPayoff = payoff?.playerPayoff ?? 0;
|
|
44637
|
-
setHistory((prev) => [
|
|
44638
|
-
...prev,
|
|
44639
|
-
{
|
|
44640
|
-
round: prev.length + 1,
|
|
44641
|
-
playerAction: actionId,
|
|
44642
|
-
opponentAction,
|
|
44643
|
-
playerPayoff,
|
|
44644
|
-
opponentPayoff: payoff?.opponentPayoff ?? 0
|
|
44645
|
-
}
|
|
44646
|
-
]);
|
|
44647
44687
|
if (playRoundEvent) {
|
|
44648
|
-
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff:
|
|
44688
|
+
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: 0 });
|
|
44649
44689
|
}
|
|
44650
44690
|
if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
|
|
44651
44691
|
if (finishEvent) {
|
|
@@ -44655,10 +44695,11 @@ function NegotiatorBoard({
|
|
|
44655
44695
|
setShowHint(true);
|
|
44656
44696
|
}
|
|
44657
44697
|
}
|
|
44658
|
-
}, [isComplete, resolved, totalRounds, currentRound,
|
|
44698
|
+
}, [isComplete, resolved, totalRounds, currentRound, playRoundEvent, finishEvent, emit]);
|
|
44659
44699
|
const handleReset = React91.useCallback(() => {
|
|
44660
44700
|
setHistory([]);
|
|
44661
44701
|
setShowHint(false);
|
|
44702
|
+
prevRoundRef.current = 0;
|
|
44662
44703
|
if (playAgainEvent) {
|
|
44663
44704
|
emit(`UI:${playAgainEvent}`, {});
|
|
44664
44705
|
}
|
|
@@ -47013,17 +47054,24 @@ function SequencerBoard({
|
|
|
47013
47054
|
const { emit } = useEventBus();
|
|
47014
47055
|
const { t } = hooks.useTranslate();
|
|
47015
47056
|
const resolved = boardEntity(entity);
|
|
47016
|
-
const maxSlots = num(resolved?.maxSlots);
|
|
47057
|
+
const maxSlots = num(resolved?.maxSlots) || 3;
|
|
47017
47058
|
const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
|
|
47018
47059
|
const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
|
|
47019
47060
|
const allowDuplicates = resolved?.allowDuplicates !== false;
|
|
47061
|
+
const entitySlots = Array.isArray(resolved?.slots) ? resolved.slots : [];
|
|
47062
|
+
const entityResult = str(resolved?.result);
|
|
47063
|
+
const entityAttempts = num(resolved?.attempts);
|
|
47064
|
+
const entityCurrentStep = typeof resolved?.currentStep === "number" ? resolved.currentStep : -1;
|
|
47065
|
+
const slots = Array.from({ length: maxSlots }, (_, i) => {
|
|
47066
|
+
const entitySlot = entitySlots.find((s) => s.index === i);
|
|
47067
|
+
if (!entitySlot?.placedActionId) return void 0;
|
|
47068
|
+
return availableActions.find((a) => a.id === entitySlot.placedActionId);
|
|
47069
|
+
});
|
|
47070
|
+
const isSuccess = entityResult === "win";
|
|
47071
|
+
const attempts = entityAttempts;
|
|
47072
|
+
const isPlayingBack = entityCurrentStep >= 0 && !isSuccess;
|
|
47073
|
+
const currentStep = entityCurrentStep;
|
|
47020
47074
|
const [headerError, setHeaderError] = React91.useState(false);
|
|
47021
|
-
const [slots, setSlots] = React91.useState(
|
|
47022
|
-
() => Array.from({ length: maxSlots }, () => void 0)
|
|
47023
|
-
);
|
|
47024
|
-
const [playState, setPlayState] = React91.useState("idle");
|
|
47025
|
-
const [currentStep, setCurrentStep] = React91.useState(-1);
|
|
47026
|
-
const [attempts, setAttempts] = React91.useState(0);
|
|
47027
47075
|
const [slotFeedback, setSlotFeedback] = React91.useState(
|
|
47028
47076
|
() => Array.from({ length: maxSlots }, () => null)
|
|
47029
47077
|
);
|
|
@@ -47032,11 +47080,6 @@ function SequencerBoard({
|
|
|
47032
47080
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
47033
47081
|
}, []);
|
|
47034
47082
|
const handleSlotDrop = React91.useCallback((index, item) => {
|
|
47035
|
-
setSlots((prev) => {
|
|
47036
|
-
const next = [...prev];
|
|
47037
|
-
next[index] = item;
|
|
47038
|
-
return next;
|
|
47039
|
-
});
|
|
47040
47083
|
setSlotFeedback((prev) => {
|
|
47041
47084
|
const next = [...prev];
|
|
47042
47085
|
next[index] = null;
|
|
@@ -47046,11 +47089,6 @@ function SequencerBoard({
|
|
|
47046
47089
|
if (placeEvent) emit(`UI:${placeEvent}`, { slotIndex: index, actionId: item.id });
|
|
47047
47090
|
}, [emit, placeEvent]);
|
|
47048
47091
|
const handleSlotRemove = React91.useCallback((index) => {
|
|
47049
|
-
setSlots((prev) => {
|
|
47050
|
-
const next = [...prev];
|
|
47051
|
-
next[index] = void 0;
|
|
47052
|
-
return next;
|
|
47053
|
-
});
|
|
47054
47092
|
setSlotFeedback((prev) => {
|
|
47055
47093
|
const next = [...prev];
|
|
47056
47094
|
next[index] = null;
|
|
@@ -47061,48 +47099,34 @@ function SequencerBoard({
|
|
|
47061
47099
|
}, [emit, removeEvent]);
|
|
47062
47100
|
const handleReset = React91.useCallback(() => {
|
|
47063
47101
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
47064
|
-
setSlots(Array.from({ length: maxSlots }, () => void 0));
|
|
47065
|
-
setPlayState("idle");
|
|
47066
|
-
setCurrentStep(-1);
|
|
47067
|
-
setAttempts(0);
|
|
47068
47102
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
47069
47103
|
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
47070
47104
|
}, [maxSlots, playAgainEvent, emit]);
|
|
47071
47105
|
const filledSlots = slots.filter((s) => !!s);
|
|
47072
|
-
const canPlay = filledSlots.length > 0 &&
|
|
47106
|
+
const canPlay = filledSlots.length > 0 && !isPlayingBack && !isSuccess;
|
|
47073
47107
|
const handlePlay = React91.useCallback(() => {
|
|
47074
47108
|
if (!canPlay) return;
|
|
47075
47109
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
47076
47110
|
emit("UI:PLAY_SOUND", { key: "confirm" });
|
|
47077
47111
|
const sequence = slots.map((s) => s?.id || "");
|
|
47078
|
-
|
|
47079
|
-
|
|
47080
|
-
}
|
|
47081
|
-
setPlayState("playing");
|
|
47082
|
-
setCurrentStep(0);
|
|
47112
|
+
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
47113
|
+
if (playEvent) emit(`UI:${playEvent}`, { sequence });
|
|
47083
47114
|
let step = 0;
|
|
47084
47115
|
const advance = () => {
|
|
47085
47116
|
step++;
|
|
47117
|
+
emit("UI:STEP", { step });
|
|
47086
47118
|
if (step >= maxSlots) {
|
|
47119
|
+
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
47087
47120
|
const playerSeq = slots.map((s) => s?.id);
|
|
47088
|
-
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
47089
47121
|
const success = solutions.some(
|
|
47090
47122
|
(sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
|
|
47091
47123
|
);
|
|
47092
|
-
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
47093
47124
|
if (success) {
|
|
47094
|
-
setPlayState("success");
|
|
47095
|
-
setCurrentStep(-1);
|
|
47096
47125
|
emit("UI:PLAY_SOUND", { key: "levelComplete" });
|
|
47097
|
-
if (completeEvent) {
|
|
47098
|
-
emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
47099
|
-
}
|
|
47126
|
+
if (completeEvent) emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
47100
47127
|
} else {
|
|
47101
|
-
setAttempts((prev) => prev + 1);
|
|
47102
47128
|
const feedback = computeSlotFeedback(playerSeq, solutions);
|
|
47103
47129
|
setSlotFeedback(feedback);
|
|
47104
|
-
setPlayState("idle");
|
|
47105
|
-
setCurrentStep(-1);
|
|
47106
47130
|
emit("UI:PLAY_SOUND", { key: "fail" });
|
|
47107
47131
|
const correctCount2 = feedback.filter((f3) => f3 === "correct").length;
|
|
47108
47132
|
for (let ci = 0; ci < correctCount2; ci++) {
|
|
@@ -47112,7 +47136,6 @@ function SequencerBoard({
|
|
|
47112
47136
|
}
|
|
47113
47137
|
}
|
|
47114
47138
|
} else {
|
|
47115
|
-
setCurrentStep(step);
|
|
47116
47139
|
timerRef.current = setTimeout(advance, stepDurationMs);
|
|
47117
47140
|
}
|
|
47118
47141
|
};
|
|
@@ -47162,7 +47185,7 @@ function SequencerBoard({
|
|
|
47162
47185
|
/* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
|
|
47163
47186
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center justify-between", children: [
|
|
47164
47187
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
|
|
47165
|
-
hasFeedback &&
|
|
47188
|
+
hasFeedback && !isPlayingBack && !isSuccess && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
|
|
47166
47189
|
`${correctCount}/${maxSlots} `,
|
|
47167
47190
|
"\u2705"
|
|
47168
47191
|
] })
|
|
@@ -47174,7 +47197,7 @@ function SequencerBoard({
|
|
|
47174
47197
|
maxSlots,
|
|
47175
47198
|
onSlotDrop: handleSlotDrop,
|
|
47176
47199
|
onSlotRemove: handleSlotRemove,
|
|
47177
|
-
playing:
|
|
47200
|
+
playing: isPlayingBack,
|
|
47178
47201
|
currentStep,
|
|
47179
47202
|
categoryColors,
|
|
47180
47203
|
slotFeedback,
|
|
@@ -47182,7 +47205,7 @@ function SequencerBoard({
|
|
|
47182
47205
|
}
|
|
47183
47206
|
)
|
|
47184
47207
|
] }),
|
|
47185
|
-
|
|
47208
|
+
!isPlayingBack && /* @__PURE__ */ jsxRuntime.jsx(
|
|
47186
47209
|
ActionPalette,
|
|
47187
47210
|
{
|
|
47188
47211
|
actions: availableActions,
|
|
@@ -47192,8 +47215,8 @@ function SequencerBoard({
|
|
|
47192
47215
|
label: t("sequencer.dragActions")
|
|
47193
47216
|
}
|
|
47194
47217
|
),
|
|
47195
|
-
hasFeedback &&
|
|
47196
|
-
|
|
47218
|
+
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) }) }),
|
|
47219
|
+
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") }) }),
|
|
47197
47220
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
|
|
47198
47221
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
47199
47222
|
Button,
|
|
@@ -48243,6 +48266,9 @@ function StateArchitectBoard({
|
|
|
48243
48266
|
stepDurationMs = 600,
|
|
48244
48267
|
testEvent,
|
|
48245
48268
|
completeEvent,
|
|
48269
|
+
addTransitionEvent,
|
|
48270
|
+
removeTransitionEvent,
|
|
48271
|
+
playAgainEvent,
|
|
48246
48272
|
className
|
|
48247
48273
|
}) {
|
|
48248
48274
|
const { emit } = useEventBus();
|
|
@@ -48255,14 +48281,16 @@ function StateArchitectBoard({
|
|
|
48255
48281
|
const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
|
|
48256
48282
|
const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
|
|
48257
48283
|
const entityVariables = rows(resolved?.variables);
|
|
48258
|
-
const
|
|
48284
|
+
const transitions = entityTransitions;
|
|
48285
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
48286
|
+
const entityResult = str(resolved?.result) || "none";
|
|
48287
|
+
const isSuccess = entityResult === "win";
|
|
48288
|
+
const [isTesting, setIsTesting] = React91.useState(false);
|
|
48259
48289
|
const [headerError, setHeaderError] = React91.useState(false);
|
|
48260
|
-
const [playState, setPlayState] = React91.useState("editing");
|
|
48261
48290
|
const [currentState, setCurrentState] = React91.useState(initialState);
|
|
48262
48291
|
const [selectedState, setSelectedState] = React91.useState(null);
|
|
48263
48292
|
const [testResults, setTestResults] = React91.useState([]);
|
|
48264
48293
|
const [variables, setVariables] = React91.useState(() => [...entityVariables]);
|
|
48265
|
-
const [attempts, setAttempts] = React91.useState(0);
|
|
48266
48294
|
const timerRef = React91.useRef(null);
|
|
48267
48295
|
const [addingFrom, setAddingFrom] = React91.useState(null);
|
|
48268
48296
|
React91.useEffect(() => () => {
|
|
@@ -48272,7 +48300,7 @@ function StateArchitectBoard({
|
|
|
48272
48300
|
const GRAPH_H = 400;
|
|
48273
48301
|
const positions = React91.useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
|
|
48274
48302
|
const handleStateClick = React91.useCallback((state) => {
|
|
48275
|
-
if (
|
|
48303
|
+
if (isTesting) return;
|
|
48276
48304
|
if (addingFrom) {
|
|
48277
48305
|
if (addingFrom !== state) {
|
|
48278
48306
|
const event = availableEvents[0] || "EVENT";
|
|
@@ -48282,20 +48310,20 @@ function StateArchitectBoard({
|
|
|
48282
48310
|
to: state,
|
|
48283
48311
|
event
|
|
48284
48312
|
};
|
|
48285
|
-
|
|
48313
|
+
if (addTransitionEvent) emit(`UI:${addTransitionEvent}`, { id: newTrans.id, from: newTrans.from, to: newTrans.to, event: newTrans.event });
|
|
48286
48314
|
}
|
|
48287
48315
|
setAddingFrom(null);
|
|
48288
48316
|
} else {
|
|
48289
48317
|
setSelectedState(state);
|
|
48290
48318
|
}
|
|
48291
|
-
}, [
|
|
48319
|
+
}, [isTesting, addingFrom, availableEvents, addTransitionEvent, emit]);
|
|
48292
48320
|
const handleStartAddTransition = React91.useCallback(() => {
|
|
48293
48321
|
if (!selectedState) return;
|
|
48294
48322
|
setAddingFrom(selectedState);
|
|
48295
48323
|
}, [selectedState]);
|
|
48296
48324
|
const handleRemoveTransition = React91.useCallback((transId) => {
|
|
48297
|
-
|
|
48298
|
-
}, []);
|
|
48325
|
+
if (removeTransitionEvent) emit(`UI:${removeTransitionEvent}`, { id: transId });
|
|
48326
|
+
}, [removeTransitionEvent, emit]);
|
|
48299
48327
|
const machine = React91.useMemo(() => ({
|
|
48300
48328
|
name: entityName,
|
|
48301
48329
|
description: str(resolved?.description),
|
|
@@ -48309,16 +48337,16 @@ function StateArchitectBoard({
|
|
|
48309
48337
|
}))
|
|
48310
48338
|
}), [entityName, resolved, entityStates, currentState, transitions]);
|
|
48311
48339
|
const handleTest = React91.useCallback(() => {
|
|
48312
|
-
if (
|
|
48340
|
+
if (isTesting) return;
|
|
48313
48341
|
if (testEvent) emit(`UI:${testEvent}`, {});
|
|
48314
|
-
|
|
48342
|
+
setIsTesting(true);
|
|
48315
48343
|
setTestResults([]);
|
|
48316
48344
|
const results = [];
|
|
48317
48345
|
let testIdx = 0;
|
|
48318
48346
|
const runNextTest = () => {
|
|
48319
48347
|
if (testIdx >= testCases.length) {
|
|
48320
48348
|
const allPassed = results.every((r2) => r2.passed);
|
|
48321
|
-
|
|
48349
|
+
setIsTesting(false);
|
|
48322
48350
|
setTestResults(results);
|
|
48323
48351
|
if (allPassed && completeEvent) {
|
|
48324
48352
|
emit(`UI:${completeEvent}`, {
|
|
@@ -48326,9 +48354,6 @@ function StateArchitectBoard({
|
|
|
48326
48354
|
passedTests: results.filter((r2) => r2.passed).length
|
|
48327
48355
|
});
|
|
48328
48356
|
}
|
|
48329
|
-
if (!allPassed) {
|
|
48330
|
-
setAttempts((prev) => prev + 1);
|
|
48331
|
-
}
|
|
48332
48357
|
return;
|
|
48333
48358
|
}
|
|
48334
48359
|
const testCase = testCases[testIdx];
|
|
@@ -48351,24 +48376,23 @@ function StateArchitectBoard({
|
|
|
48351
48376
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
48352
48377
|
};
|
|
48353
48378
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
48354
|
-
}, [
|
|
48379
|
+
}, [isTesting, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
|
|
48355
48380
|
const handleTryAgain = React91.useCallback(() => {
|
|
48356
48381
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
48357
|
-
|
|
48382
|
+
setIsTesting(false);
|
|
48358
48383
|
setCurrentState(initialState);
|
|
48359
48384
|
setTestResults([]);
|
|
48360
48385
|
}, [initialState]);
|
|
48361
48386
|
const handleReset = React91.useCallback(() => {
|
|
48362
48387
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
48363
|
-
|
|
48364
|
-
setPlayState("editing");
|
|
48388
|
+
setIsTesting(false);
|
|
48365
48389
|
setCurrentState(initialState);
|
|
48366
48390
|
setTestResults([]);
|
|
48367
48391
|
setVariables([...entityVariables]);
|
|
48368
48392
|
setSelectedState(null);
|
|
48369
48393
|
setAddingFrom(null);
|
|
48370
|
-
|
|
48371
|
-
}, [
|
|
48394
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
48395
|
+
}, [initialState, entityVariables, playAgainEvent, emit]);
|
|
48372
48396
|
const codeData = React91.useMemo(() => ({
|
|
48373
48397
|
name: entityName,
|
|
48374
48398
|
states: entityStates,
|
|
@@ -48464,7 +48488,7 @@ function StateArchitectBoard({
|
|
|
48464
48488
|
]
|
|
48465
48489
|
}
|
|
48466
48490
|
),
|
|
48467
|
-
|
|
48491
|
+
!isTesting && /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "sm", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
48468
48492
|
Button,
|
|
48469
48493
|
{
|
|
48470
48494
|
variant: "ghost",
|
|
@@ -48486,7 +48510,7 @@ function StateArchitectBoard({
|
|
|
48486
48510
|
t2.guardHint,
|
|
48487
48511
|
")"
|
|
48488
48512
|
] }),
|
|
48489
|
-
|
|
48513
|
+
!isTesting && /* @__PURE__ */ jsxRuntime.jsx(
|
|
48490
48514
|
Button,
|
|
48491
48515
|
{
|
|
48492
48516
|
variant: "ghost",
|
|
@@ -48518,21 +48542,21 @@ function StateArchitectBoard({
|
|
|
48518
48542
|
resolved.showCodeView !== false && /* @__PURE__ */ jsxRuntime.jsx(StateJsonView, { data: codeData, label: "View Code" })
|
|
48519
48543
|
] })
|
|
48520
48544
|
] }),
|
|
48521
|
-
|
|
48522
|
-
|
|
48545
|
+
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") }) }),
|
|
48546
|
+
!isTesting && !isSuccess && testResults.some((r2) => !r2.passed) && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
48523
48547
|
/* @__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]) }) }),
|
|
48524
|
-
attempts >=
|
|
48548
|
+
!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: [
|
|
48525
48549
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
48526
48550
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
|
|
48527
48551
|
] }) })
|
|
48528
48552
|
] }),
|
|
48529
48553
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
|
|
48530
|
-
|
|
48554
|
+
!isTesting && !isSuccess && testResults.some((r2) => !r2.passed) ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
48531
48555
|
Button,
|
|
48532
48556
|
{
|
|
48533
48557
|
variant: "primary",
|
|
48534
48558
|
onClick: handleTest,
|
|
48535
|
-
disabled:
|
|
48559
|
+
disabled: isTesting,
|
|
48536
48560
|
children: "\u25B6 " + t("game.runTests")
|
|
48537
48561
|
}
|
|
48538
48562
|
),
|
|
@@ -49236,17 +49260,12 @@ var init_UncontrolledBattleBoard = __esm({
|
|
|
49236
49260
|
}
|
|
49237
49261
|
});
|
|
49238
49262
|
function heroPosition(h) {
|
|
49263
|
+
if ("position" in h && h.position != null) return h.position;
|
|
49239
49264
|
return vec2(h.position);
|
|
49240
49265
|
}
|
|
49241
|
-
function heroOwner(h) {
|
|
49242
|
-
return str(h.owner);
|
|
49243
|
-
}
|
|
49244
49266
|
function heroMovement(h) {
|
|
49245
49267
|
return num(h.movement);
|
|
49246
49268
|
}
|
|
49247
|
-
function hexPassable(h) {
|
|
49248
|
-
return h.passable !== false;
|
|
49249
|
-
}
|
|
49250
49269
|
function defaultIsInRange(from, to, range) {
|
|
49251
49270
|
return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
|
|
49252
49271
|
}
|
|
@@ -49282,38 +49301,41 @@ function WorldMapBoard({
|
|
|
49282
49301
|
}) {
|
|
49283
49302
|
const eventBus = useEventBus();
|
|
49284
49303
|
const resolved = boardEntity(entity);
|
|
49285
|
-
const
|
|
49286
|
-
const
|
|
49304
|
+
const entityUnits = rows(resolved?.units);
|
|
49305
|
+
const entityTiles = rows(resolved?.tiles);
|
|
49287
49306
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
49288
49307
|
const selectedHeroId = resolved?.selectedHeroId ?? null;
|
|
49289
49308
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
49290
49309
|
const backgroundImage = resolved?.backgroundImage;
|
|
49291
49310
|
const [hoveredTile, setHoveredTile] = React91.useState(null);
|
|
49292
|
-
const selectedHero = React91.useMemo(
|
|
49293
|
-
() => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
|
|
49294
|
-
[heroes, selectedHeroId]
|
|
49295
|
-
);
|
|
49296
49311
|
const derivedTiles = React91.useMemo(
|
|
49297
|
-
() =>
|
|
49298
|
-
x: num(
|
|
49299
|
-
y: num(
|
|
49300
|
-
terrain: str(
|
|
49301
|
-
terrainSprite:
|
|
49312
|
+
() => entityTiles.map((t) => ({
|
|
49313
|
+
x: num(t.x),
|
|
49314
|
+
y: num(t.y),
|
|
49315
|
+
terrain: str(t.terrain),
|
|
49316
|
+
terrainSprite: t.terrainSprite == null ? void 0 : str(t.terrainSprite),
|
|
49317
|
+
passable: t.passable !== false
|
|
49302
49318
|
})),
|
|
49303
|
-
[
|
|
49319
|
+
[entityTiles]
|
|
49304
49320
|
);
|
|
49305
49321
|
const tiles = propTiles ?? derivedTiles;
|
|
49306
49322
|
const baseUnits = React91.useMemo(
|
|
49307
|
-
() => propUnits ??
|
|
49308
|
-
id: str(
|
|
49309
|
-
position: heroPosition(
|
|
49310
|
-
name: str(
|
|
49311
|
-
|
|
49312
|
-
|
|
49313
|
-
|
|
49314
|
-
|
|
49323
|
+
() => propUnits ?? entityUnits.map((u) => ({
|
|
49324
|
+
id: str(u.id),
|
|
49325
|
+
position: heroPosition(u),
|
|
49326
|
+
name: str(u.name),
|
|
49327
|
+
// lolo uses `team` field (not `owner`)
|
|
49328
|
+
team: str(u.team) === "enemy" ? "enemy" : "player",
|
|
49329
|
+
health: num(u.health) || 100,
|
|
49330
|
+
maxHealth: num(u.maxHealth) || 100,
|
|
49331
|
+
sprite: u.sprite == null ? void 0 : str(u.sprite)
|
|
49315
49332
|
})),
|
|
49316
|
-
[
|
|
49333
|
+
[entityUnits, propUnits]
|
|
49334
|
+
);
|
|
49335
|
+
const gameUnits = baseUnits;
|
|
49336
|
+
const selectedHero = React91.useMemo(
|
|
49337
|
+
() => gameUnits.find((u) => u.id === selectedHeroId) ?? null,
|
|
49338
|
+
[gameUnits, selectedHeroId]
|
|
49317
49339
|
);
|
|
49318
49340
|
const MOVE_SPEED_MS_PER_TILE = 300;
|
|
49319
49341
|
const movementAnimRef = React91.useRef(null);
|
|
@@ -49359,51 +49381,50 @@ function WorldMapBoard({
|
|
|
49359
49381
|
const validMoves = React91.useMemo(() => {
|
|
49360
49382
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
49361
49383
|
const sp = heroPosition(selectedHero);
|
|
49362
|
-
const
|
|
49384
|
+
const sTeam = str(selectedHero.team);
|
|
49363
49385
|
const range = heroMovement(selectedHero);
|
|
49364
49386
|
const moves = [];
|
|
49365
|
-
|
|
49366
|
-
const
|
|
49367
|
-
const
|
|
49368
|
-
if (
|
|
49369
|
-
if (
|
|
49370
|
-
if (!isInRange(sp, { x:
|
|
49371
|
-
if (
|
|
49372
|
-
const
|
|
49373
|
-
return
|
|
49387
|
+
tiles.forEach((t) => {
|
|
49388
|
+
const tx = t.x;
|
|
49389
|
+
const ty = t.y;
|
|
49390
|
+
if (t.passable === false) return;
|
|
49391
|
+
if (tx === sp.x && ty === sp.y) return;
|
|
49392
|
+
if (!isInRange(sp, { x: tx, y: ty }, range)) return;
|
|
49393
|
+
if (gameUnits.some((u) => {
|
|
49394
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
49395
|
+
return up.x === tx && up.y === ty && str(u.team) === sTeam;
|
|
49374
49396
|
})) return;
|
|
49375
|
-
moves.push({ x:
|
|
49397
|
+
moves.push({ x: tx, y: ty });
|
|
49376
49398
|
});
|
|
49377
49399
|
return moves;
|
|
49378
|
-
}, [selectedHero,
|
|
49400
|
+
}, [selectedHero, tiles, gameUnits, isInRange]);
|
|
49379
49401
|
const attackTargets = React91.useMemo(() => {
|
|
49380
49402
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
49381
49403
|
const sp = heroPosition(selectedHero);
|
|
49382
|
-
const
|
|
49404
|
+
const sTeam = str(selectedHero.team);
|
|
49383
49405
|
const range = heroMovement(selectedHero);
|
|
49384
|
-
return
|
|
49385
|
-
}, [selectedHero,
|
|
49386
|
-
const maxY = Math.max(...
|
|
49406
|
+
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 });
|
|
49407
|
+
}, [selectedHero, gameUnits, isInRange]);
|
|
49408
|
+
const maxY = Math.max(...tiles.map((t) => t.y), 0);
|
|
49387
49409
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
49388
49410
|
const tileToScreen = React91.useCallback(
|
|
49389
49411
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
49390
49412
|
[scale, baseOffsetX]
|
|
49391
49413
|
);
|
|
49392
49414
|
const hoveredHex = React91.useMemo(
|
|
49393
|
-
() => hoveredTile ?
|
|
49394
|
-
[hoveredTile,
|
|
49415
|
+
() => hoveredTile ? tiles.find((t) => t.x === hoveredTile.x && t.y === hoveredTile.y) ?? null : null,
|
|
49416
|
+
[hoveredTile, tiles]
|
|
49395
49417
|
);
|
|
49396
49418
|
const hoveredHero = React91.useMemo(
|
|
49397
|
-
() => hoveredTile ?
|
|
49398
|
-
const
|
|
49399
|
-
return
|
|
49419
|
+
() => hoveredTile ? gameUnits.find((u) => {
|
|
49420
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
49421
|
+
return up.x === hoveredTile.x && up.y === hoveredTile.y;
|
|
49400
49422
|
}) ?? null : null,
|
|
49401
|
-
[hoveredTile,
|
|
49423
|
+
[hoveredTile, gameUnits]
|
|
49402
49424
|
);
|
|
49403
49425
|
const handleTileClick = React91.useCallback((x, y) => {
|
|
49404
49426
|
if (movementAnimRef.current) return;
|
|
49405
|
-
const
|
|
49406
|
-
if (!hex) return;
|
|
49427
|
+
const tile = tiles.find((t) => t.x === x && t.y === y);
|
|
49407
49428
|
if (tileClickEvent) {
|
|
49408
49429
|
eventBus.emit(`UI:${tileClickEvent}`, { x, y });
|
|
49409
49430
|
}
|
|
@@ -49414,38 +49435,39 @@ function WorldMapBoard({
|
|
|
49414
49435
|
if (heroMoveEvent) {
|
|
49415
49436
|
eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
|
|
49416
49437
|
}
|
|
49417
|
-
const feature = str(
|
|
49438
|
+
const feature = tile ? str(tile.feature) : "";
|
|
49418
49439
|
if (feature && feature !== "none") {
|
|
49419
|
-
|
|
49440
|
+
const tileRow = tile;
|
|
49441
|
+
onFeatureEnter?.(heroId, tileRow);
|
|
49420
49442
|
if (featureEnterEvent) {
|
|
49421
|
-
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
|
|
49443
|
+
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex: tileRow });
|
|
49422
49444
|
}
|
|
49423
49445
|
}
|
|
49424
49446
|
});
|
|
49425
49447
|
return;
|
|
49426
49448
|
}
|
|
49427
|
-
const enemy =
|
|
49428
|
-
const
|
|
49429
|
-
return
|
|
49449
|
+
const enemy = gameUnits.find((u) => {
|
|
49450
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
49451
|
+
return up.x === x && up.y === y && str(u.team) === "enemy";
|
|
49430
49452
|
});
|
|
49431
49453
|
if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
|
|
49432
49454
|
const attackerId = str(selectedHero.id);
|
|
49433
|
-
const defenderId =
|
|
49455
|
+
const defenderId = enemy.id;
|
|
49434
49456
|
onBattleEncounter?.(attackerId, defenderId);
|
|
49435
49457
|
if (battleEncounterEvent) {
|
|
49436
49458
|
eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
|
|
49437
49459
|
}
|
|
49438
49460
|
}
|
|
49439
|
-
}, [
|
|
49461
|
+
}, [tiles, gameUnits, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
|
|
49440
49462
|
const handleUnitClick = React91.useCallback((unitId) => {
|
|
49441
|
-
const
|
|
49442
|
-
if (
|
|
49463
|
+
const unit = gameUnits.find((u) => u.id === unitId);
|
|
49464
|
+
if (unit && (str(unit.team) === "player" || allowMoveAllHeroes)) {
|
|
49443
49465
|
onHeroSelect?.(unitId);
|
|
49444
49466
|
if (heroSelectEvent) {
|
|
49445
49467
|
eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
|
|
49446
49468
|
}
|
|
49447
49469
|
}
|
|
49448
|
-
}, [
|
|
49470
|
+
}, [gameUnits, onHeroSelect, allowMoveAllHeroes, eventBus, heroSelectEvent]);
|
|
49449
49471
|
const selectHero = React91.useCallback((id) => {
|
|
49450
49472
|
onHeroSelect?.(id);
|
|
49451
49473
|
if (heroSelectEvent) {
|