@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.js
CHANGED
|
@@ -7278,6 +7278,7 @@ var init_Sprite = __esm({
|
|
|
7278
7278
|
}
|
|
7279
7279
|
});
|
|
7280
7280
|
function StateIndicator({
|
|
7281
|
+
assetUrl = DEFAULT_ASSET_URL,
|
|
7281
7282
|
state = "idle",
|
|
7282
7283
|
label,
|
|
7283
7284
|
size = "md",
|
|
@@ -7300,18 +7301,29 @@ function StateIndicator({
|
|
|
7300
7301
|
className
|
|
7301
7302
|
),
|
|
7302
7303
|
children: [
|
|
7303
|
-
/* @__PURE__ */ jsx(Box, { as: "span", children:
|
|
7304
|
+
/* @__PURE__ */ jsx(Box, { as: "span", children: assetUrl ? /* @__PURE__ */ jsx(
|
|
7305
|
+
"img",
|
|
7306
|
+
{
|
|
7307
|
+
src: assetUrl,
|
|
7308
|
+
alt: displayLabel,
|
|
7309
|
+
width: 16,
|
|
7310
|
+
height: 16,
|
|
7311
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7312
|
+
className: "flex-shrink-0"
|
|
7313
|
+
}
|
|
7314
|
+
) : typeof config.icon === "string" ? /^[a-zA-Z0-9-]+$/.test(config.icon) ? /* @__PURE__ */ jsx(Icon, { name: config.icon }) : config.icon : /* @__PURE__ */ jsx(Icon, { icon: config.icon }) }),
|
|
7304
7315
|
/* @__PURE__ */ jsx(Box, { as: "span", children: displayLabel })
|
|
7305
7316
|
]
|
|
7306
7317
|
}
|
|
7307
7318
|
);
|
|
7308
7319
|
}
|
|
7309
|
-
var DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
7320
|
+
var DEFAULT_ASSET_URL, DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
7310
7321
|
var init_StateIndicator = __esm({
|
|
7311
7322
|
"components/game/atoms/StateIndicator.tsx"() {
|
|
7312
7323
|
init_Box();
|
|
7313
7324
|
init_Icon();
|
|
7314
7325
|
init_cn();
|
|
7326
|
+
DEFAULT_ASSET_URL = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
7315
7327
|
DEFAULT_STATE_STYLES = {
|
|
7316
7328
|
idle: { icon: "\u23F8", bgClass: "bg-muted" },
|
|
7317
7329
|
active: { icon: "\u25B6", bgClass: "bg-success" },
|
|
@@ -7386,6 +7398,7 @@ var init_TimerDisplay = __esm({
|
|
|
7386
7398
|
}
|
|
7387
7399
|
});
|
|
7388
7400
|
function ResourceCounter({
|
|
7401
|
+
assetUrl = DEFAULT_ASSET_URL2,
|
|
7389
7402
|
icon,
|
|
7390
7403
|
label = "Gold",
|
|
7391
7404
|
value = 250,
|
|
@@ -7405,7 +7418,17 @@ function ResourceCounter({
|
|
|
7405
7418
|
className
|
|
7406
7419
|
),
|
|
7407
7420
|
children: [
|
|
7408
|
-
|
|
7421
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
7422
|
+
"img",
|
|
7423
|
+
{
|
|
7424
|
+
src: assetUrl,
|
|
7425
|
+
alt: label,
|
|
7426
|
+
width: sizes.img,
|
|
7427
|
+
height: sizes.img,
|
|
7428
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7429
|
+
className: "flex-shrink-0"
|
|
7430
|
+
}
|
|
7431
|
+
) : icon ? /* @__PURE__ */ jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon }) : /* @__PURE__ */ jsx(Icon, { icon }) }) : null,
|
|
7409
7432
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label }),
|
|
7410
7433
|
/* @__PURE__ */ jsxs("span", { className: cn("font-bold tabular-nums", color && (color in colorTokenClasses2 ? colorTokenClasses2[color] : color)), children: [
|
|
7411
7434
|
value,
|
|
@@ -7418,7 +7441,7 @@ function ResourceCounter({
|
|
|
7418
7441
|
}
|
|
7419
7442
|
);
|
|
7420
7443
|
}
|
|
7421
|
-
var colorTokenClasses2, sizeMap5;
|
|
7444
|
+
var colorTokenClasses2, DEFAULT_ASSET_URL2, sizeMap5;
|
|
7422
7445
|
var init_ResourceCounter = __esm({
|
|
7423
7446
|
"components/game/atoms/ResourceCounter.tsx"() {
|
|
7424
7447
|
init_cn();
|
|
@@ -7431,16 +7454,17 @@ var init_ResourceCounter = __esm({
|
|
|
7431
7454
|
error: "text-error",
|
|
7432
7455
|
muted: "text-muted-foreground"
|
|
7433
7456
|
};
|
|
7457
|
+
DEFAULT_ASSET_URL2 = "https://almadar-kflow-assets.web.app/shared/world-map/gold_mine.png";
|
|
7434
7458
|
sizeMap5 = {
|
|
7435
|
-
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm" },
|
|
7436
|
-
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base" },
|
|
7437
|
-
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg" }
|
|
7459
|
+
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm", img: 16 },
|
|
7460
|
+
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base", img: 20 },
|
|
7461
|
+
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg", img: 28 }
|
|
7438
7462
|
};
|
|
7439
7463
|
ResourceCounter.displayName = "ResourceCounter";
|
|
7440
7464
|
}
|
|
7441
7465
|
});
|
|
7442
7466
|
function ItemSlot({
|
|
7443
|
-
assetUrl =
|
|
7467
|
+
assetUrl = DEFAULT_ASSET_URL3,
|
|
7444
7468
|
icon = "sword",
|
|
7445
7469
|
label = "Iron Sword",
|
|
7446
7470
|
quantity,
|
|
@@ -7499,7 +7523,7 @@ function ItemSlot({
|
|
|
7499
7523
|
}
|
|
7500
7524
|
);
|
|
7501
7525
|
}
|
|
7502
|
-
var sizeMap6, rarityBorderMap, rarityGlowMap,
|
|
7526
|
+
var sizeMap6, rarityBorderMap, rarityGlowMap, DEFAULT_ASSET_URL3, assetSizeMap;
|
|
7503
7527
|
var init_ItemSlot = __esm({
|
|
7504
7528
|
"components/game/atoms/ItemSlot.tsx"() {
|
|
7505
7529
|
"use client";
|
|
@@ -7524,7 +7548,7 @@ var init_ItemSlot = __esm({
|
|
|
7524
7548
|
epic: "shadow-lg",
|
|
7525
7549
|
legendary: "shadow-lg"
|
|
7526
7550
|
};
|
|
7527
|
-
|
|
7551
|
+
DEFAULT_ASSET_URL3 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
7528
7552
|
assetSizeMap = {
|
|
7529
7553
|
sm: 28,
|
|
7530
7554
|
md: 40,
|
|
@@ -7597,6 +7621,7 @@ function getComboScale(combo) {
|
|
|
7597
7621
|
return "";
|
|
7598
7622
|
}
|
|
7599
7623
|
function ComboCounter({
|
|
7624
|
+
assetUrl = DEFAULT_ASSET_URL4,
|
|
7600
7625
|
combo = 5,
|
|
7601
7626
|
multiplier,
|
|
7602
7627
|
streak,
|
|
@@ -7616,6 +7641,17 @@ function ComboCounter({
|
|
|
7616
7641
|
className
|
|
7617
7642
|
),
|
|
7618
7643
|
children: [
|
|
7644
|
+
assetUrl && /* @__PURE__ */ jsx(
|
|
7645
|
+
"img",
|
|
7646
|
+
{
|
|
7647
|
+
src: assetUrl,
|
|
7648
|
+
alt: "combo",
|
|
7649
|
+
width: 24,
|
|
7650
|
+
height: 24,
|
|
7651
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7652
|
+
className: "flex-shrink-0 mb-0.5"
|
|
7653
|
+
}
|
|
7654
|
+
),
|
|
7619
7655
|
/* @__PURE__ */ jsx("span", { className: cn("font-black tabular-nums leading-none", sizes.combo, getComboIntensity(combo)), children: combo }),
|
|
7620
7656
|
/* @__PURE__ */ jsx("span", { className: cn("font-bold uppercase tracking-wider text-muted-foreground", sizes.label), children: "combo" }),
|
|
7621
7657
|
multiplier != null && multiplier > 1 && /* @__PURE__ */ jsxs("span", { className: cn("font-bold text-warning tabular-nums", sizes.multiplier), children: [
|
|
@@ -7630,10 +7666,11 @@ function ComboCounter({
|
|
|
7630
7666
|
}
|
|
7631
7667
|
);
|
|
7632
7668
|
}
|
|
7633
|
-
var sizeMap8;
|
|
7669
|
+
var DEFAULT_ASSET_URL4, sizeMap8;
|
|
7634
7670
|
var init_ComboCounter = __esm({
|
|
7635
7671
|
"components/game/atoms/ComboCounter.tsx"() {
|
|
7636
7672
|
init_cn();
|
|
7673
|
+
DEFAULT_ASSET_URL4 = "https://almadar-kflow-assets.web.app/shared/effects/flash/flash00.png";
|
|
7637
7674
|
sizeMap8 = {
|
|
7638
7675
|
sm: { combo: "text-lg", label: "text-xs", multiplier: "text-xs" },
|
|
7639
7676
|
md: { combo: "text-2xl", label: "text-xs", multiplier: "text-sm" },
|
|
@@ -7727,6 +7764,7 @@ var init_XPBar = __esm({
|
|
|
7727
7764
|
}
|
|
7728
7765
|
});
|
|
7729
7766
|
function WaypointMarker({
|
|
7767
|
+
assetUrl = DEFAULT_ASSET_URL5,
|
|
7730
7768
|
label,
|
|
7731
7769
|
icon,
|
|
7732
7770
|
active = true,
|
|
@@ -7765,7 +7803,17 @@ function WaypointMarker({
|
|
|
7765
7803
|
active && !completed && "bg-info text-foreground",
|
|
7766
7804
|
!active && !completed && "bg-muted"
|
|
7767
7805
|
),
|
|
7768
|
-
children: completed ? checkIcon :
|
|
7806
|
+
children: completed ? checkIcon : assetUrl ? /* @__PURE__ */ jsx(
|
|
7807
|
+
"img",
|
|
7808
|
+
{
|
|
7809
|
+
src: assetUrl,
|
|
7810
|
+
alt: label,
|
|
7811
|
+
width: sizes.img,
|
|
7812
|
+
height: sizes.img,
|
|
7813
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7814
|
+
className: "flex-shrink-0"
|
|
7815
|
+
}
|
|
7816
|
+
) : icon ? typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon }) : /* @__PURE__ */ jsx(Icon, { icon }) : null
|
|
7769
7817
|
}
|
|
7770
7818
|
)
|
|
7771
7819
|
] }),
|
|
@@ -7782,15 +7830,16 @@ function WaypointMarker({
|
|
|
7782
7830
|
)
|
|
7783
7831
|
] });
|
|
7784
7832
|
}
|
|
7785
|
-
var sizeMap10, checkIcon;
|
|
7833
|
+
var DEFAULT_ASSET_URL5, sizeMap10, checkIcon;
|
|
7786
7834
|
var init_WaypointMarker = __esm({
|
|
7787
7835
|
"components/game/atoms/WaypointMarker.tsx"() {
|
|
7788
7836
|
init_cn();
|
|
7789
7837
|
init_Icon();
|
|
7838
|
+
DEFAULT_ASSET_URL5 = "https://almadar-kflow-assets.web.app/shared/world-map/battle_marker.png";
|
|
7790
7839
|
sizeMap10 = {
|
|
7791
|
-
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1" },
|
|
7792
|
-
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5" },
|
|
7793
|
-
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2" }
|
|
7840
|
+
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1", img: 12 },
|
|
7841
|
+
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5", img: 18 },
|
|
7842
|
+
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2", img: 24 }
|
|
7794
7843
|
};
|
|
7795
7844
|
checkIcon = /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 3, className: "w-3/5 h-3/5", children: /* @__PURE__ */ jsx("path", { d: "M5 13l4 4L19 7", strokeLinecap: "round", strokeLinejoin: "round" }) });
|
|
7796
7845
|
WaypointMarker.displayName = "WaypointMarker";
|
|
@@ -7803,6 +7852,7 @@ function formatDuration(seconds) {
|
|
|
7803
7852
|
return `${Math.round(seconds)}s`;
|
|
7804
7853
|
}
|
|
7805
7854
|
function StatusEffect({
|
|
7855
|
+
assetUrl = DEFAULT_ASSET_URL6,
|
|
7806
7856
|
icon = "shield",
|
|
7807
7857
|
label = "Shield",
|
|
7808
7858
|
duration = 30,
|
|
@@ -7823,7 +7873,17 @@ function StatusEffect({
|
|
|
7823
7873
|
),
|
|
7824
7874
|
title: label,
|
|
7825
7875
|
children: [
|
|
7826
|
-
/* @__PURE__ */ jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children:
|
|
7876
|
+
/* @__PURE__ */ jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children: assetUrl ? /* @__PURE__ */ jsx(
|
|
7877
|
+
"img",
|
|
7878
|
+
{
|
|
7879
|
+
src: assetUrl,
|
|
7880
|
+
alt: label,
|
|
7881
|
+
width: sizes.img,
|
|
7882
|
+
height: sizes.img,
|
|
7883
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7884
|
+
className: "flex-shrink-0"
|
|
7885
|
+
}
|
|
7886
|
+
) : typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, size: "sm" }) : /* @__PURE__ */ jsx(Icon, { icon, size: "sm" }) }),
|
|
7827
7887
|
duration !== void 0 && /* @__PURE__ */ jsx(
|
|
7828
7888
|
"span",
|
|
7829
7889
|
{
|
|
@@ -7850,15 +7910,16 @@ function StatusEffect({
|
|
|
7850
7910
|
label && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground mt-0.5 text-center whitespace-nowrap", children: label })
|
|
7851
7911
|
] });
|
|
7852
7912
|
}
|
|
7853
|
-
var sizeMap11, variantStyles7;
|
|
7913
|
+
var DEFAULT_ASSET_URL6, sizeMap11, variantStyles7;
|
|
7854
7914
|
var init_StatusEffect = __esm({
|
|
7855
7915
|
"components/game/atoms/StatusEffect.tsx"() {
|
|
7856
7916
|
init_cn();
|
|
7857
7917
|
init_Icon();
|
|
7918
|
+
DEFAULT_ASSET_URL6 = "https://almadar-kflow-assets.web.app/shared/effects/particles/flame_01.png";
|
|
7858
7919
|
sizeMap11 = {
|
|
7859
|
-
sm: { container: "w-8 h-8", icon: "text-sm", badge: "text-xs -top-1 -right-1 w-4 h-4", timer: "text-[9px]" },
|
|
7860
|
-
md: { container: "w-10 h-10", icon: "text-base", badge: "text-xs -top-1 -right-1 w-5 h-5", timer: "text-xs" },
|
|
7861
|
-
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" }
|
|
7920
|
+
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 },
|
|
7921
|
+
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 },
|
|
7922
|
+
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 }
|
|
7862
7923
|
};
|
|
7863
7924
|
variantStyles7 = {
|
|
7864
7925
|
buff: "border-success bg-success/20",
|
|
@@ -11427,15 +11488,15 @@ function BattleBoard({
|
|
|
11427
11488
|
const board = boardEntity(entity) ?? {};
|
|
11428
11489
|
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
|
|
11429
11490
|
const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
|
|
11430
|
-
const boardWidth = num(board.boardWidth, 8);
|
|
11431
|
-
const boardHeight = num(board.boardHeight, 6);
|
|
11491
|
+
const boardWidth = num(board.gridWidth ?? board.boardWidth, 8);
|
|
11492
|
+
const boardHeight = num(board.gridHeight ?? board.boardHeight, 6);
|
|
11432
11493
|
const assetManifest = propAssetManifest ?? board.assetManifest;
|
|
11433
11494
|
const backgroundImage = board.backgroundImage;
|
|
11434
11495
|
const units = rows(board.units);
|
|
11435
11496
|
const selectedUnitId = board.selectedUnitId ?? null;
|
|
11436
11497
|
const currentPhase = str(board.phase) || "observation";
|
|
11437
11498
|
const currentTurn = num(board.turn, 1);
|
|
11438
|
-
const gameResult = board.
|
|
11499
|
+
const gameResult = board.result ?? null;
|
|
11439
11500
|
const eventBus = useEventBus();
|
|
11440
11501
|
const { t } = useTranslate();
|
|
11441
11502
|
const [hoveredTile, setHoveredTile] = useState(null);
|
|
@@ -11456,7 +11517,7 @@ function BattleBoard({
|
|
|
11456
11517
|
const validMoves = useMemo(() => {
|
|
11457
11518
|
if (!selectedUnit || currentPhase !== "movement") return [];
|
|
11458
11519
|
const moves = [];
|
|
11459
|
-
const range = num(
|
|
11520
|
+
const range = num(board.movementRange, 2);
|
|
11460
11521
|
const origin = unitPosition(selectedUnit);
|
|
11461
11522
|
for (let dy = -range; dy <= range; dy++) {
|
|
11462
11523
|
for (let dx = -range; dx <= range; dx++) {
|
|
@@ -19741,15 +19802,24 @@ function CastleBoard({
|
|
|
19741
19802
|
featureClickEvent,
|
|
19742
19803
|
unitClickEvent,
|
|
19743
19804
|
tileClickEvent,
|
|
19805
|
+
playAgainEvent,
|
|
19744
19806
|
className
|
|
19745
19807
|
}) {
|
|
19746
19808
|
const eventBus = useEventBus();
|
|
19809
|
+
const { t } = useTranslate();
|
|
19747
19810
|
const resolved = boardEntity(entity);
|
|
19748
19811
|
const tiles = propTiles ?? (Array.isArray(resolved?.tiles) ? resolved.tiles : []);
|
|
19749
19812
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
19750
19813
|
const units = propUnits ?? (Array.isArray(resolved?.units) ? resolved.units : []);
|
|
19751
19814
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
19752
19815
|
const backgroundImage = resolved?.backgroundImage;
|
|
19816
|
+
const gold = num(resolved?.gold);
|
|
19817
|
+
const health = num(resolved?.health);
|
|
19818
|
+
const maxHealth = num(resolved?.maxHealth);
|
|
19819
|
+
const wave = num(resolved?.wave);
|
|
19820
|
+
const tickCount = num(resolved?.tickCount);
|
|
19821
|
+
const buildings = rows(resolved?.buildings);
|
|
19822
|
+
const result = str(resolved?.result) || "none";
|
|
19753
19823
|
const [hoveredTile, setHoveredTile] = useState(null);
|
|
19754
19824
|
const [selectedFeature, setSelectedFeature] = useState(null);
|
|
19755
19825
|
const hoveredFeature = useMemo(() => {
|
|
@@ -19762,7 +19832,7 @@ function CastleBoard({
|
|
|
19762
19832
|
(u) => u.position?.x === hoveredTile.x && u.position?.y === hoveredTile.y
|
|
19763
19833
|
) ?? null;
|
|
19764
19834
|
}, [hoveredTile, units]);
|
|
19765
|
-
const maxY = Math.max(...tiles.map((
|
|
19835
|
+
const maxY = Math.max(...tiles.map((t2) => t2.y), 0);
|
|
19766
19836
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
19767
19837
|
const tileToScreen = useCallback(
|
|
19768
19838
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
@@ -19797,6 +19867,11 @@ function CastleBoard({
|
|
|
19797
19867
|
}
|
|
19798
19868
|
}, [units, onUnitClick, unitClickEvent, eventBus]);
|
|
19799
19869
|
const clearSelection = useCallback(() => setSelectedFeature(null), []);
|
|
19870
|
+
const handlePlayAgain = useCallback(() => {
|
|
19871
|
+
if (playAgainEvent) {
|
|
19872
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
19873
|
+
}
|
|
19874
|
+
}, [playAgainEvent, eventBus]);
|
|
19800
19875
|
const ctx = useMemo(
|
|
19801
19876
|
() => ({
|
|
19802
19877
|
hoveredTile,
|
|
@@ -19805,11 +19880,18 @@ function CastleBoard({
|
|
|
19805
19880
|
selectedFeature,
|
|
19806
19881
|
clearSelection,
|
|
19807
19882
|
tileToScreen,
|
|
19808
|
-
scale
|
|
19883
|
+
scale,
|
|
19884
|
+
gold,
|
|
19885
|
+
health,
|
|
19886
|
+
maxHealth,
|
|
19887
|
+
wave,
|
|
19888
|
+
tickCount,
|
|
19889
|
+
buildings,
|
|
19890
|
+
result
|
|
19809
19891
|
}),
|
|
19810
|
-
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale]
|
|
19892
|
+
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale, gold, health, maxHealth, wave, tickCount, buildings, result]
|
|
19811
19893
|
);
|
|
19812
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("castle-board min-h-screen flex flex-col bg-background", className), children: [
|
|
19894
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("castle-board relative min-h-screen flex flex-col bg-background", className), children: [
|
|
19813
19895
|
header && header(ctx),
|
|
19814
19896
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
|
|
19815
19897
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-auto p-4 relative", children: [
|
|
@@ -19834,7 +19916,29 @@ function CastleBoard({
|
|
|
19834
19916
|
] }),
|
|
19835
19917
|
sidePanel && /* @__PURE__ */ jsx("div", { className: "w-96 shrink-0 border-l border-border bg-surface overflow-y-auto", children: sidePanel(ctx) })
|
|
19836
19918
|
] }),
|
|
19837
|
-
footer && footer(ctx)
|
|
19919
|
+
footer && footer(ctx),
|
|
19920
|
+
result !== "none" && /* @__PURE__ */ jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
19921
|
+
/* @__PURE__ */ jsx(
|
|
19922
|
+
Typography,
|
|
19923
|
+
{
|
|
19924
|
+
variant: "h2",
|
|
19925
|
+
className: cn(
|
|
19926
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
19927
|
+
result === "victory" ? "text-warning" : "text-error"
|
|
19928
|
+
),
|
|
19929
|
+
children: result === "victory" ? t("battle.victory") : t("battle.defeat")
|
|
19930
|
+
}
|
|
19931
|
+
),
|
|
19932
|
+
/* @__PURE__ */ jsx(
|
|
19933
|
+
Button,
|
|
19934
|
+
{
|
|
19935
|
+
variant: "primary",
|
|
19936
|
+
className: "px-8 py-3 font-semibold",
|
|
19937
|
+
onClick: handlePlayAgain,
|
|
19938
|
+
children: t("battle.playAgain")
|
|
19939
|
+
}
|
|
19940
|
+
)
|
|
19941
|
+
] }) })
|
|
19838
19942
|
] });
|
|
19839
19943
|
}
|
|
19840
19944
|
var init_CastleBoard = __esm({
|
|
@@ -19842,6 +19946,10 @@ var init_CastleBoard = __esm({
|
|
|
19842
19946
|
"use client";
|
|
19843
19947
|
init_cn();
|
|
19844
19948
|
init_useEventBus();
|
|
19949
|
+
init_Box();
|
|
19950
|
+
init_Button();
|
|
19951
|
+
init_Typography();
|
|
19952
|
+
init_Stack();
|
|
19845
19953
|
init_IsometricCanvas();
|
|
19846
19954
|
init_boardEntity();
|
|
19847
19955
|
init_isometric();
|
|
@@ -20718,22 +20826,16 @@ function ClassifierBoard({
|
|
|
20718
20826
|
const { emit } = useEventBus();
|
|
20719
20827
|
const { t } = useTranslate();
|
|
20720
20828
|
const resolved = boardEntity(entity);
|
|
20721
|
-
const [localAssignments, setLocalAssignments] = useState({});
|
|
20722
20829
|
const [headerError, setHeaderError] = useState(false);
|
|
20723
|
-
const [localSubmitted, setLocalSubmitted] = useState(false);
|
|
20724
|
-
const [localAttempts, setLocalAttempts] = useState(0);
|
|
20725
|
-
const [showHint, setShowHint] = useState(false);
|
|
20726
20830
|
const items = Array.isArray(resolved?.items) ? resolved.items : [];
|
|
20727
20831
|
const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
|
|
20728
|
-
const
|
|
20729
|
-
const
|
|
20730
|
-
const
|
|
20731
|
-
const assignments =
|
|
20732
|
-
if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
|
|
20832
|
+
const result = str(resolved?.result);
|
|
20833
|
+
const submitted = result === "win";
|
|
20834
|
+
const attempts = num(resolved?.attempts);
|
|
20835
|
+
const assignments = items.reduce((acc, item) => {
|
|
20836
|
+
if (item.assignedCategory != null && item.assignedCategory !== "") acc[item.id] = item.assignedCategory;
|
|
20733
20837
|
return acc;
|
|
20734
|
-
}, {})
|
|
20735
|
-
const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
|
|
20736
|
-
const submitted = entityDrivesResult || localSubmitted;
|
|
20838
|
+
}, {});
|
|
20737
20839
|
const unassignedItems = items.filter((item) => !assignments[item.id]);
|
|
20738
20840
|
const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
|
|
20739
20841
|
const results = submitted ? items.map((item) => ({
|
|
@@ -20741,54 +20843,25 @@ function ClassifierBoard({
|
|
|
20741
20843
|
assigned: assignments[item.id],
|
|
20742
20844
|
correct: assignments[item.id] === item.correctCategory
|
|
20743
20845
|
})) : [];
|
|
20744
|
-
const allCorrect =
|
|
20846
|
+
const allCorrect = result === "win";
|
|
20745
20847
|
const correctCount = results.filter((r) => r.correct).length;
|
|
20848
|
+
const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
|
|
20746
20849
|
const handleAssign = (itemId, categoryId) => {
|
|
20747
20850
|
if (submitted) return;
|
|
20748
|
-
if (assignEvent) {
|
|
20749
|
-
emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
20750
|
-
}
|
|
20751
|
-
if (!entityHasAssignments) {
|
|
20752
|
-
setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
|
|
20753
|
-
}
|
|
20851
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
20754
20852
|
};
|
|
20755
20853
|
const handleUnassign = (itemId) => {
|
|
20756
20854
|
if (submitted) return;
|
|
20757
|
-
if (
|
|
20758
|
-
setLocalAssignments((prev) => {
|
|
20759
|
-
const next = { ...prev };
|
|
20760
|
-
delete next[itemId];
|
|
20761
|
-
return next;
|
|
20762
|
-
});
|
|
20763
|
-
}
|
|
20855
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId: "" });
|
|
20764
20856
|
};
|
|
20765
|
-
const handleSubmit =
|
|
20766
|
-
if (checkEvent) {
|
|
20767
|
-
|
|
20768
|
-
|
|
20769
|
-
if (!entityDrivesResult) {
|
|
20770
|
-
setLocalSubmitted(true);
|
|
20771
|
-
setLocalAttempts((a) => a + 1);
|
|
20772
|
-
const correct = items.every((item) => assignments[item.id] === item.correctCategory);
|
|
20773
|
-
if (correct) {
|
|
20774
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
20775
|
-
}
|
|
20776
|
-
}
|
|
20777
|
-
}, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
|
|
20778
|
-
const handleReset = () => {
|
|
20779
|
-
if (!entityDrivesResult) setLocalSubmitted(false);
|
|
20780
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
20781
|
-
setShowHint(true);
|
|
20857
|
+
const handleSubmit = () => {
|
|
20858
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
20859
|
+
if (allAssigned && items.every((item) => assignments[item.id] === item.correctCategory)) {
|
|
20860
|
+
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
20782
20861
|
}
|
|
20783
20862
|
};
|
|
20784
20863
|
const handleFullReset = () => {
|
|
20785
|
-
if (playAgainEvent) {
|
|
20786
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
20787
|
-
}
|
|
20788
|
-
setLocalAssignments({});
|
|
20789
|
-
setLocalSubmitted(false);
|
|
20790
|
-
setLocalAttempts(0);
|
|
20791
|
-
setShowHint(false);
|
|
20864
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
20792
20865
|
};
|
|
20793
20866
|
if (!resolved) return null;
|
|
20794
20867
|
const theme = resolved.theme ?? void 0;
|
|
@@ -20827,17 +20900,17 @@ function ClassifierBoard({
|
|
|
20827
20900
|
/* @__PURE__ */ jsx(Badge, { size: "sm", children: catItems.length })
|
|
20828
20901
|
] }),
|
|
20829
20902
|
/* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-wrap min-h-[2rem]", children: catItems.map((item) => {
|
|
20830
|
-
const
|
|
20903
|
+
const result2 = results.find((r) => r.item.id === item.id);
|
|
20831
20904
|
return /* @__PURE__ */ jsxs(
|
|
20832
20905
|
Badge,
|
|
20833
20906
|
{
|
|
20834
20907
|
size: "sm",
|
|
20835
|
-
className: `cursor-pointer ${
|
|
20908
|
+
className: `cursor-pointer ${result2 ? result2.correct ? "border-success bg-success/10" : "border-error bg-error/10" : ""}`,
|
|
20836
20909
|
onClick: () => handleUnassign(item.id),
|
|
20837
20910
|
children: [
|
|
20838
20911
|
item.iconUrl && /* @__PURE__ */ jsx("img", { src: item.iconUrl, alt: "", className: "w-3 h-3 object-contain inline-block" }),
|
|
20839
20912
|
item.label,
|
|
20840
|
-
|
|
20913
|
+
result2 && /* @__PURE__ */ jsx(Icon, { icon: result2.correct ? CheckCircle : XCircle, size: "xs", className: result2.correct ? "text-success" : "text-error" })
|
|
20841
20914
|
]
|
|
20842
20915
|
},
|
|
20843
20916
|
item.id
|
|
@@ -20866,10 +20939,10 @@ function ClassifierBoard({
|
|
|
20866
20939
|
] }) }),
|
|
20867
20940
|
showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
|
|
20868
20941
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
20869
|
-
!submitted
|
|
20942
|
+
!submitted && /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
|
|
20870
20943
|
/* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
|
|
20871
20944
|
t("classifier.check")
|
|
20872
|
-
] })
|
|
20945
|
+
] }),
|
|
20873
20946
|
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
|
|
20874
20947
|
/* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
|
|
20875
20948
|
t("classifier.reset")
|
|
@@ -28898,7 +28971,7 @@ var init_MapView = __esm({
|
|
|
28898
28971
|
shadowSize: [41, 41]
|
|
28899
28972
|
});
|
|
28900
28973
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
28901
|
-
const { useEffect: useEffect74, useRef: useRef68, useCallback:
|
|
28974
|
+
const { useEffect: useEffect74, useRef: useRef68, useCallback: useCallback111, useState: useState107 } = React82__default;
|
|
28902
28975
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
28903
28976
|
const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
28904
28977
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
@@ -28944,7 +29017,7 @@ var init_MapView = __esm({
|
|
|
28944
29017
|
}) {
|
|
28945
29018
|
const eventBus = useEventBus3();
|
|
28946
29019
|
const [clickedPosition, setClickedPosition] = useState107(null);
|
|
28947
|
-
const handleMapClick =
|
|
29020
|
+
const handleMapClick = useCallback111((lat, lng) => {
|
|
28948
29021
|
if (showClickedPin) {
|
|
28949
29022
|
setClickedPosition({ lat, lng });
|
|
28950
29023
|
}
|
|
@@ -28953,7 +29026,7 @@ var init_MapView = __esm({
|
|
|
28953
29026
|
eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
|
|
28954
29027
|
}
|
|
28955
29028
|
}, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
|
|
28956
|
-
const handleMarkerClick =
|
|
29029
|
+
const handleMarkerClick = useCallback111((marker) => {
|
|
28957
29030
|
onMarkerClick?.(marker);
|
|
28958
29031
|
if (markerClickEvent) {
|
|
28959
29032
|
eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
|
|
@@ -38184,44 +38257,27 @@ function DebuggerBoard({
|
|
|
38184
38257
|
const { t } = useTranslate();
|
|
38185
38258
|
const resolved = boardEntity(entity);
|
|
38186
38259
|
const [headerError, setHeaderError] = useState(false);
|
|
38187
|
-
const [showHint, setShowHint] = useState(false);
|
|
38188
38260
|
const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
|
|
38189
|
-
const result = resolved?.result
|
|
38261
|
+
const result = str(resolved?.result) || "none";
|
|
38190
38262
|
const attempts = num(resolved?.attempts);
|
|
38191
|
-
const submitted = result
|
|
38263
|
+
const submitted = result === "win";
|
|
38264
|
+
const showHint = !submitted && attempts >= 2 && Boolean(str(resolved?.hint));
|
|
38192
38265
|
const bugLines = lines.filter((l) => l.isBug);
|
|
38193
38266
|
const flaggedLines = lines.filter((l) => l.isFlagged);
|
|
38194
38267
|
const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
|
|
38195
38268
|
const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
|
|
38196
|
-
const allCorrect =
|
|
38269
|
+
const allCorrect = submitted;
|
|
38197
38270
|
const toggleLine = (lineId) => {
|
|
38198
38271
|
if (submitted) return;
|
|
38199
|
-
if (toggleFlagEvent) {
|
|
38200
|
-
emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
38201
|
-
}
|
|
38272
|
+
if (toggleFlagEvent) emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
38202
38273
|
};
|
|
38203
38274
|
const handleSubmit = useCallback(() => {
|
|
38204
|
-
if (checkEvent) {
|
|
38205
|
-
emit(`UI:${checkEvent}`, {});
|
|
38206
|
-
}
|
|
38275
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
38207
38276
|
const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
|
|
38208
|
-
if (correct) {
|
|
38209
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
38210
|
-
}
|
|
38277
|
+
if (correct) emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
38211
38278
|
}, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
|
|
38212
38279
|
const handleReset = () => {
|
|
38213
|
-
if (playAgainEvent) {
|
|
38214
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
38215
|
-
}
|
|
38216
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
38217
|
-
setShowHint(true);
|
|
38218
|
-
}
|
|
38219
|
-
};
|
|
38220
|
-
const handleFullReset = () => {
|
|
38221
|
-
if (playAgainEvent) {
|
|
38222
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
38223
|
-
}
|
|
38224
|
-
setShowHint(false);
|
|
38280
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
38225
38281
|
};
|
|
38226
38282
|
if (!resolved) return null;
|
|
38227
38283
|
const theme = resolved.theme ?? void 0;
|
|
@@ -38245,7 +38301,7 @@ function DebuggerBoard({
|
|
|
38245
38301
|
/* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
|
|
38246
38302
|
] }),
|
|
38247
38303
|
/* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) }),
|
|
38248
|
-
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(
|
|
38304
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(lines.filter((l) => l.isBug).length) }) })
|
|
38249
38305
|
] }) }),
|
|
38250
38306
|
/* @__PURE__ */ jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsx(VStack, { gap: "none", children: lines.map((line, i) => {
|
|
38251
38307
|
const isFlagged = !!line.isFlagged;
|
|
@@ -38295,11 +38351,11 @@ function DebuggerBoard({
|
|
|
38295
38351
|
] }) }),
|
|
38296
38352
|
showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
|
|
38297
38353
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
38298
|
-
!submitted
|
|
38354
|
+
!submitted && /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
|
|
38299
38355
|
/* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
|
|
38300
38356
|
t("debugger.submit")
|
|
38301
|
-
] })
|
|
38302
|
-
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick:
|
|
38357
|
+
] }),
|
|
38358
|
+
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleReset, children: [
|
|
38303
38359
|
/* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
|
|
38304
38360
|
t("debugger.reset")
|
|
38305
38361
|
] })
|
|
@@ -39300,39 +39356,40 @@ function EventHandlerBoard({
|
|
|
39300
39356
|
stepDurationMs = 800,
|
|
39301
39357
|
playEvent,
|
|
39302
39358
|
completeEvent,
|
|
39359
|
+
editRuleEvent,
|
|
39360
|
+
playAgainEvent,
|
|
39303
39361
|
className
|
|
39304
39362
|
}) {
|
|
39305
39363
|
const { emit } = useEventBus();
|
|
39306
39364
|
const { t } = useTranslate();
|
|
39307
39365
|
const resolved = boardEntity(entity);
|
|
39308
|
-
const
|
|
39309
|
-
const
|
|
39366
|
+
const objects = rows(resolved?.objects);
|
|
39367
|
+
const entityResult = str(resolved?.result) || "none";
|
|
39368
|
+
const isSuccess = entityResult === "win";
|
|
39369
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
39310
39370
|
const [selectedObjectId, setSelectedObjectId] = useState(
|
|
39311
|
-
|
|
39371
|
+
objects[0] ? objId(objects[0]) : null
|
|
39312
39372
|
);
|
|
39313
39373
|
const [headerError, setHeaderError] = useState(false);
|
|
39314
|
-
const [
|
|
39374
|
+
const [isPlaying, setIsPlaying] = useState(false);
|
|
39315
39375
|
const [eventLog, setEventLog] = useState([]);
|
|
39316
|
-
const [attempts, setAttempts] = useState(0);
|
|
39317
39376
|
const timerRef = useRef(null);
|
|
39318
39377
|
const logIdCounter = useRef(0);
|
|
39319
39378
|
useEffect(() => () => {
|
|
39320
39379
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39321
39380
|
}, []);
|
|
39322
|
-
const selectedObject = objects.find((o) => objId(o) === selectedObjectId)
|
|
39381
|
+
const selectedObject = objects.find((o) => objId(o) === selectedObjectId) ?? null;
|
|
39323
39382
|
const handleRulesChange = useCallback((objectId, rules) => {
|
|
39324
|
-
|
|
39325
|
-
|
|
39326
|
-
));
|
|
39327
|
-
}, []);
|
|
39383
|
+
if (editRuleEvent) emit(`UI:${editRuleEvent}`, { objectId, rules });
|
|
39384
|
+
}, [editRuleEvent, emit]);
|
|
39328
39385
|
const addLogEntry = useCallback((icon, message, status = "done") => {
|
|
39329
39386
|
const id = `log-${logIdCounter.current++}`;
|
|
39330
39387
|
setEventLog((prev) => [...prev, { id, timestamp: Date.now(), icon, message, status }]);
|
|
39331
39388
|
}, []);
|
|
39332
39389
|
const handlePlay = useCallback(() => {
|
|
39333
|
-
if (
|
|
39390
|
+
if (isPlaying || isSuccess) return;
|
|
39334
39391
|
if (playEvent) emit(`UI:${playEvent}`, {});
|
|
39335
|
-
|
|
39392
|
+
setIsPlaying(true);
|
|
39336
39393
|
setEventLog([]);
|
|
39337
39394
|
const allRules = [];
|
|
39338
39395
|
objects.forEach((obj) => {
|
|
@@ -39348,15 +39405,8 @@ function EventHandlerBoard({
|
|
|
39348
39405
|
let goalReached = false;
|
|
39349
39406
|
const processNext = () => {
|
|
39350
39407
|
if (eventQueue.length === 0 || stepIdx > 20) {
|
|
39351
|
-
|
|
39352
|
-
|
|
39353
|
-
if (completeEvent) {
|
|
39354
|
-
emit(`UI:${completeEvent}`, { success: true });
|
|
39355
|
-
}
|
|
39356
|
-
} else {
|
|
39357
|
-
setAttempts((prev) => prev + 1);
|
|
39358
|
-
setPlayState("fail");
|
|
39359
|
-
}
|
|
39408
|
+
setIsPlaying(false);
|
|
39409
|
+
if (goalReached && completeEvent) emit(`UI:${completeEvent}`, { success: true });
|
|
39360
39410
|
return;
|
|
39361
39411
|
}
|
|
39362
39412
|
const currentEvent = eventQueue.shift();
|
|
@@ -39387,21 +39437,19 @@ function EventHandlerBoard({
|
|
|
39387
39437
|
addLogEntry("\u{1F3AC}", t("eventHandler.simulationStarted", { events: triggers.join(", ") }), "active");
|
|
39388
39438
|
}
|
|
39389
39439
|
timerRef.current = setTimeout(processNext, stepDurationMs);
|
|
39390
|
-
}, [
|
|
39440
|
+
}, [isPlaying, isSuccess, objects, resolved, stepDurationMs, playEvent, completeEvent, emit, addLogEntry, t]);
|
|
39391
39441
|
const handleTryAgain = useCallback(() => {
|
|
39392
39442
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39393
|
-
|
|
39443
|
+
setIsPlaying(false);
|
|
39394
39444
|
setEventLog([]);
|
|
39395
39445
|
}, []);
|
|
39396
39446
|
const handleReset = useCallback(() => {
|
|
39397
39447
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39398
|
-
|
|
39399
|
-
setObjects([...resetObjects]);
|
|
39400
|
-
setPlayState("editing");
|
|
39448
|
+
setIsPlaying(false);
|
|
39401
39449
|
setEventLog([]);
|
|
39402
|
-
setSelectedObjectId(
|
|
39403
|
-
|
|
39404
|
-
}, [
|
|
39450
|
+
setSelectedObjectId(objects[0] ? objId(objects[0]) : null);
|
|
39451
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
39452
|
+
}, [objects, playAgainEvent, emit]);
|
|
39405
39453
|
if (!resolved) return null;
|
|
39406
39454
|
const objectViewers = objects.map((obj) => {
|
|
39407
39455
|
const states = objStates(obj);
|
|
@@ -39470,12 +39518,12 @@ function EventHandlerBoard({
|
|
|
39470
39518
|
{
|
|
39471
39519
|
object: selectedObject,
|
|
39472
39520
|
onRulesChange: handleRulesChange,
|
|
39473
|
-
disabled:
|
|
39521
|
+
disabled: isPlaying
|
|
39474
39522
|
}
|
|
39475
39523
|
),
|
|
39476
39524
|
eventLog.length > 0 && /* @__PURE__ */ jsx(EventLog, { entries: eventLog }),
|
|
39477
|
-
|
|
39478
|
-
|
|
39525
|
+
isSuccess && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("eventHandler.chainComplete") }) }),
|
|
39526
|
+
!isPlaying && !isSuccess && attempts > 0 && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
39479
39527
|
/* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(encourageKey) }) }),
|
|
39480
39528
|
showHint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
|
|
39481
39529
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
@@ -39483,12 +39531,12 @@ function EventHandlerBoard({
|
|
|
39483
39531
|
] }) })
|
|
39484
39532
|
] }),
|
|
39485
39533
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
|
|
39486
|
-
|
|
39534
|
+
!isPlaying && !isSuccess && attempts > 0 ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsx(
|
|
39487
39535
|
Button,
|
|
39488
39536
|
{
|
|
39489
39537
|
variant: "primary",
|
|
39490
39538
|
onClick: handlePlay,
|
|
39491
|
-
disabled:
|
|
39539
|
+
disabled: isPlaying || isSuccess,
|
|
39492
39540
|
children: "\u25B6 " + t("game.play")
|
|
39493
39541
|
}
|
|
39494
39542
|
),
|
|
@@ -42041,21 +42089,6 @@ var init_ModalSlot = __esm({
|
|
|
42041
42089
|
ModalSlot.displayName = "ModalSlot";
|
|
42042
42090
|
}
|
|
42043
42091
|
});
|
|
42044
|
-
function getOpponentAction(strategy, actions, history) {
|
|
42045
|
-
const actionIds = actions.map((a) => a.id);
|
|
42046
|
-
switch (strategy) {
|
|
42047
|
-
case "always-cooperate":
|
|
42048
|
-
return actionIds[0];
|
|
42049
|
-
case "always-defect":
|
|
42050
|
-
return actionIds[actionIds.length - 1];
|
|
42051
|
-
case "tit-for-tat":
|
|
42052
|
-
if (history.length === 0) return actionIds[0];
|
|
42053
|
-
return history[history.length - 1].playerAction;
|
|
42054
|
-
case "random":
|
|
42055
|
-
default:
|
|
42056
|
-
return actionIds[Math.floor(Math.random() * actionIds.length)];
|
|
42057
|
-
}
|
|
42058
|
-
}
|
|
42059
42092
|
function NegotiatorBoard({
|
|
42060
42093
|
entity,
|
|
42061
42094
|
completeEvent = "PUZZLE_COMPLETE",
|
|
@@ -42077,28 +42110,35 @@ function NegotiatorBoard({
|
|
|
42077
42110
|
const playerTotal = num(resolved?.score);
|
|
42078
42111
|
const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
|
|
42079
42112
|
const won = result === "win";
|
|
42080
|
-
const
|
|
42113
|
+
const lastPlayerAction = str(resolved?.lastPlayerAction);
|
|
42114
|
+
const lastOpponentAction = str(resolved?.lastOpponentAction);
|
|
42115
|
+
const lastPayoff = num(resolved?.lastPayoff);
|
|
42081
42116
|
const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
|
|
42082
42117
|
const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
|
|
42118
|
+
const prevRoundRef = useRef(currentRound);
|
|
42119
|
+
useEffect(() => {
|
|
42120
|
+
if (currentRound > prevRoundRef.current && lastPlayerAction) {
|
|
42121
|
+
const opponentPayoffEntry = payoffMatrix.find(
|
|
42122
|
+
(p2) => p2.playerAction === lastPlayerAction && p2.opponentAction === lastOpponentAction
|
|
42123
|
+
);
|
|
42124
|
+
setHistory((prev) => [
|
|
42125
|
+
...prev,
|
|
42126
|
+
{
|
|
42127
|
+
round: currentRound,
|
|
42128
|
+
playerAction: lastPlayerAction,
|
|
42129
|
+
opponentAction: lastOpponentAction,
|
|
42130
|
+
playerPayoff: lastPayoff,
|
|
42131
|
+
opponentPayoff: opponentPayoffEntry?.opponentPayoff ?? 0
|
|
42132
|
+
}
|
|
42133
|
+
]);
|
|
42134
|
+
}
|
|
42135
|
+
prevRoundRef.current = currentRound;
|
|
42136
|
+
}, [currentRound, lastPlayerAction, lastOpponentAction, lastPayoff, payoffMatrix]);
|
|
42137
|
+
const opponentTotal = useMemo(() => history.reduce((s, r) => s + r.opponentPayoff, 0), [history]);
|
|
42083
42138
|
const handleAction = useCallback((actionId) => {
|
|
42084
42139
|
if (isComplete) return;
|
|
42085
|
-
const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
|
|
42086
|
-
const payoff = payoffMatrix.find(
|
|
42087
|
-
(p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
|
|
42088
|
-
);
|
|
42089
|
-
const playerPayoff = payoff?.playerPayoff ?? 0;
|
|
42090
|
-
setHistory((prev) => [
|
|
42091
|
-
...prev,
|
|
42092
|
-
{
|
|
42093
|
-
round: prev.length + 1,
|
|
42094
|
-
playerAction: actionId,
|
|
42095
|
-
opponentAction,
|
|
42096
|
-
playerPayoff,
|
|
42097
|
-
opponentPayoff: payoff?.opponentPayoff ?? 0
|
|
42098
|
-
}
|
|
42099
|
-
]);
|
|
42100
42140
|
if (playRoundEvent) {
|
|
42101
|
-
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff:
|
|
42141
|
+
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: 0 });
|
|
42102
42142
|
}
|
|
42103
42143
|
if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
|
|
42104
42144
|
if (finishEvent) {
|
|
@@ -42108,10 +42148,11 @@ function NegotiatorBoard({
|
|
|
42108
42148
|
setShowHint(true);
|
|
42109
42149
|
}
|
|
42110
42150
|
}
|
|
42111
|
-
}, [isComplete, resolved, totalRounds, currentRound,
|
|
42151
|
+
}, [isComplete, resolved, totalRounds, currentRound, playRoundEvent, finishEvent, emit]);
|
|
42112
42152
|
const handleReset = useCallback(() => {
|
|
42113
42153
|
setHistory([]);
|
|
42114
42154
|
setShowHint(false);
|
|
42155
|
+
prevRoundRef.current = 0;
|
|
42115
42156
|
if (playAgainEvent) {
|
|
42116
42157
|
emit(`UI:${playAgainEvent}`, {});
|
|
42117
42158
|
}
|
|
@@ -44466,17 +44507,24 @@ function SequencerBoard({
|
|
|
44466
44507
|
const { emit } = useEventBus();
|
|
44467
44508
|
const { t } = useTranslate();
|
|
44468
44509
|
const resolved = boardEntity(entity);
|
|
44469
|
-
const maxSlots = num(resolved?.maxSlots);
|
|
44510
|
+
const maxSlots = num(resolved?.maxSlots) || 3;
|
|
44470
44511
|
const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
|
|
44471
44512
|
const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
|
|
44472
44513
|
const allowDuplicates = resolved?.allowDuplicates !== false;
|
|
44514
|
+
const entitySlots = Array.isArray(resolved?.slots) ? resolved.slots : [];
|
|
44515
|
+
const entityResult = str(resolved?.result);
|
|
44516
|
+
const entityAttempts = num(resolved?.attempts);
|
|
44517
|
+
const entityCurrentStep = typeof resolved?.currentStep === "number" ? resolved.currentStep : -1;
|
|
44518
|
+
const slots = Array.from({ length: maxSlots }, (_, i) => {
|
|
44519
|
+
const entitySlot = entitySlots.find((s) => s.index === i);
|
|
44520
|
+
if (!entitySlot?.placedActionId) return void 0;
|
|
44521
|
+
return availableActions.find((a) => a.id === entitySlot.placedActionId);
|
|
44522
|
+
});
|
|
44523
|
+
const isSuccess = entityResult === "win";
|
|
44524
|
+
const attempts = entityAttempts;
|
|
44525
|
+
const isPlayingBack = entityCurrentStep >= 0 && !isSuccess;
|
|
44526
|
+
const currentStep = entityCurrentStep;
|
|
44473
44527
|
const [headerError, setHeaderError] = useState(false);
|
|
44474
|
-
const [slots, setSlots] = useState(
|
|
44475
|
-
() => Array.from({ length: maxSlots }, () => void 0)
|
|
44476
|
-
);
|
|
44477
|
-
const [playState, setPlayState] = useState("idle");
|
|
44478
|
-
const [currentStep, setCurrentStep] = useState(-1);
|
|
44479
|
-
const [attempts, setAttempts] = useState(0);
|
|
44480
44528
|
const [slotFeedback, setSlotFeedback] = useState(
|
|
44481
44529
|
() => Array.from({ length: maxSlots }, () => null)
|
|
44482
44530
|
);
|
|
@@ -44485,11 +44533,6 @@ function SequencerBoard({
|
|
|
44485
44533
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
44486
44534
|
}, []);
|
|
44487
44535
|
const handleSlotDrop = useCallback((index, item) => {
|
|
44488
|
-
setSlots((prev) => {
|
|
44489
|
-
const next = [...prev];
|
|
44490
|
-
next[index] = item;
|
|
44491
|
-
return next;
|
|
44492
|
-
});
|
|
44493
44536
|
setSlotFeedback((prev) => {
|
|
44494
44537
|
const next = [...prev];
|
|
44495
44538
|
next[index] = null;
|
|
@@ -44499,11 +44542,6 @@ function SequencerBoard({
|
|
|
44499
44542
|
if (placeEvent) emit(`UI:${placeEvent}`, { slotIndex: index, actionId: item.id });
|
|
44500
44543
|
}, [emit, placeEvent]);
|
|
44501
44544
|
const handleSlotRemove = useCallback((index) => {
|
|
44502
|
-
setSlots((prev) => {
|
|
44503
|
-
const next = [...prev];
|
|
44504
|
-
next[index] = void 0;
|
|
44505
|
-
return next;
|
|
44506
|
-
});
|
|
44507
44545
|
setSlotFeedback((prev) => {
|
|
44508
44546
|
const next = [...prev];
|
|
44509
44547
|
next[index] = null;
|
|
@@ -44514,48 +44552,34 @@ function SequencerBoard({
|
|
|
44514
44552
|
}, [emit, removeEvent]);
|
|
44515
44553
|
const handleReset = useCallback(() => {
|
|
44516
44554
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
44517
|
-
setSlots(Array.from({ length: maxSlots }, () => void 0));
|
|
44518
|
-
setPlayState("idle");
|
|
44519
|
-
setCurrentStep(-1);
|
|
44520
|
-
setAttempts(0);
|
|
44521
44555
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
44522
44556
|
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
44523
44557
|
}, [maxSlots, playAgainEvent, emit]);
|
|
44524
44558
|
const filledSlots = slots.filter((s) => !!s);
|
|
44525
|
-
const canPlay = filledSlots.length > 0 &&
|
|
44559
|
+
const canPlay = filledSlots.length > 0 && !isPlayingBack && !isSuccess;
|
|
44526
44560
|
const handlePlay = useCallback(() => {
|
|
44527
44561
|
if (!canPlay) return;
|
|
44528
44562
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
44529
44563
|
emit("UI:PLAY_SOUND", { key: "confirm" });
|
|
44530
44564
|
const sequence = slots.map((s) => s?.id || "");
|
|
44531
|
-
|
|
44532
|
-
|
|
44533
|
-
}
|
|
44534
|
-
setPlayState("playing");
|
|
44535
|
-
setCurrentStep(0);
|
|
44565
|
+
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
44566
|
+
if (playEvent) emit(`UI:${playEvent}`, { sequence });
|
|
44536
44567
|
let step = 0;
|
|
44537
44568
|
const advance = () => {
|
|
44538
44569
|
step++;
|
|
44570
|
+
emit("UI:STEP", { step });
|
|
44539
44571
|
if (step >= maxSlots) {
|
|
44572
|
+
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
44540
44573
|
const playerSeq = slots.map((s) => s?.id);
|
|
44541
|
-
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
44542
44574
|
const success = solutions.some(
|
|
44543
44575
|
(sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
|
|
44544
44576
|
);
|
|
44545
|
-
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
44546
44577
|
if (success) {
|
|
44547
|
-
setPlayState("success");
|
|
44548
|
-
setCurrentStep(-1);
|
|
44549
44578
|
emit("UI:PLAY_SOUND", { key: "levelComplete" });
|
|
44550
|
-
if (completeEvent) {
|
|
44551
|
-
emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
44552
|
-
}
|
|
44579
|
+
if (completeEvent) emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
44553
44580
|
} else {
|
|
44554
|
-
setAttempts((prev) => prev + 1);
|
|
44555
44581
|
const feedback = computeSlotFeedback(playerSeq, solutions);
|
|
44556
44582
|
setSlotFeedback(feedback);
|
|
44557
|
-
setPlayState("idle");
|
|
44558
|
-
setCurrentStep(-1);
|
|
44559
44583
|
emit("UI:PLAY_SOUND", { key: "fail" });
|
|
44560
44584
|
const correctCount2 = feedback.filter((f3) => f3 === "correct").length;
|
|
44561
44585
|
for (let ci = 0; ci < correctCount2; ci++) {
|
|
@@ -44565,7 +44589,6 @@ function SequencerBoard({
|
|
|
44565
44589
|
}
|
|
44566
44590
|
}
|
|
44567
44591
|
} else {
|
|
44568
|
-
setCurrentStep(step);
|
|
44569
44592
|
timerRef.current = setTimeout(advance, stepDurationMs);
|
|
44570
44593
|
}
|
|
44571
44594
|
};
|
|
@@ -44615,7 +44638,7 @@ function SequencerBoard({
|
|
|
44615
44638
|
/* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
|
|
44616
44639
|
/* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between", children: [
|
|
44617
44640
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
|
|
44618
|
-
hasFeedback &&
|
|
44641
|
+
hasFeedback && !isPlayingBack && !isSuccess && /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
|
|
44619
44642
|
`${correctCount}/${maxSlots} `,
|
|
44620
44643
|
"\u2705"
|
|
44621
44644
|
] })
|
|
@@ -44627,7 +44650,7 @@ function SequencerBoard({
|
|
|
44627
44650
|
maxSlots,
|
|
44628
44651
|
onSlotDrop: handleSlotDrop,
|
|
44629
44652
|
onSlotRemove: handleSlotRemove,
|
|
44630
|
-
playing:
|
|
44653
|
+
playing: isPlayingBack,
|
|
44631
44654
|
currentStep,
|
|
44632
44655
|
categoryColors,
|
|
44633
44656
|
slotFeedback,
|
|
@@ -44635,7 +44658,7 @@ function SequencerBoard({
|
|
|
44635
44658
|
}
|
|
44636
44659
|
)
|
|
44637
44660
|
] }),
|
|
44638
|
-
|
|
44661
|
+
!isPlayingBack && /* @__PURE__ */ jsx(
|
|
44639
44662
|
ActionPalette,
|
|
44640
44663
|
{
|
|
44641
44664
|
actions: availableActions,
|
|
@@ -44645,8 +44668,8 @@ function SequencerBoard({
|
|
|
44645
44668
|
label: t("sequencer.dragActions")
|
|
44646
44669
|
}
|
|
44647
44670
|
),
|
|
44648
|
-
hasFeedback &&
|
|
44649
|
-
|
|
44671
|
+
hasFeedback && !isPlayingBack && !isSuccess && attempts > 0 && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: t(encourageKey) }) }),
|
|
44672
|
+
isSuccess && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("sequencer.levelComplete") }) }),
|
|
44650
44673
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
|
|
44651
44674
|
/* @__PURE__ */ jsx(
|
|
44652
44675
|
Button,
|
|
@@ -45696,6 +45719,9 @@ function StateArchitectBoard({
|
|
|
45696
45719
|
stepDurationMs = 600,
|
|
45697
45720
|
testEvent,
|
|
45698
45721
|
completeEvent,
|
|
45722
|
+
addTransitionEvent,
|
|
45723
|
+
removeTransitionEvent,
|
|
45724
|
+
playAgainEvent,
|
|
45699
45725
|
className
|
|
45700
45726
|
}) {
|
|
45701
45727
|
const { emit } = useEventBus();
|
|
@@ -45708,14 +45734,16 @@ function StateArchitectBoard({
|
|
|
45708
45734
|
const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
|
|
45709
45735
|
const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
|
|
45710
45736
|
const entityVariables = rows(resolved?.variables);
|
|
45711
|
-
const
|
|
45737
|
+
const transitions = entityTransitions;
|
|
45738
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
45739
|
+
const entityResult = str(resolved?.result) || "none";
|
|
45740
|
+
const isSuccess = entityResult === "win";
|
|
45741
|
+
const [isTesting, setIsTesting] = useState(false);
|
|
45712
45742
|
const [headerError, setHeaderError] = useState(false);
|
|
45713
|
-
const [playState, setPlayState] = useState("editing");
|
|
45714
45743
|
const [currentState, setCurrentState] = useState(initialState);
|
|
45715
45744
|
const [selectedState, setSelectedState] = useState(null);
|
|
45716
45745
|
const [testResults, setTestResults] = useState([]);
|
|
45717
45746
|
const [variables, setVariables] = useState(() => [...entityVariables]);
|
|
45718
|
-
const [attempts, setAttempts] = useState(0);
|
|
45719
45747
|
const timerRef = useRef(null);
|
|
45720
45748
|
const [addingFrom, setAddingFrom] = useState(null);
|
|
45721
45749
|
useEffect(() => () => {
|
|
@@ -45725,7 +45753,7 @@ function StateArchitectBoard({
|
|
|
45725
45753
|
const GRAPH_H = 400;
|
|
45726
45754
|
const positions = useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
|
|
45727
45755
|
const handleStateClick = useCallback((state) => {
|
|
45728
|
-
if (
|
|
45756
|
+
if (isTesting) return;
|
|
45729
45757
|
if (addingFrom) {
|
|
45730
45758
|
if (addingFrom !== state) {
|
|
45731
45759
|
const event = availableEvents[0] || "EVENT";
|
|
@@ -45735,20 +45763,20 @@ function StateArchitectBoard({
|
|
|
45735
45763
|
to: state,
|
|
45736
45764
|
event
|
|
45737
45765
|
};
|
|
45738
|
-
|
|
45766
|
+
if (addTransitionEvent) emit(`UI:${addTransitionEvent}`, { id: newTrans.id, from: newTrans.from, to: newTrans.to, event: newTrans.event });
|
|
45739
45767
|
}
|
|
45740
45768
|
setAddingFrom(null);
|
|
45741
45769
|
} else {
|
|
45742
45770
|
setSelectedState(state);
|
|
45743
45771
|
}
|
|
45744
|
-
}, [
|
|
45772
|
+
}, [isTesting, addingFrom, availableEvents, addTransitionEvent, emit]);
|
|
45745
45773
|
const handleStartAddTransition = useCallback(() => {
|
|
45746
45774
|
if (!selectedState) return;
|
|
45747
45775
|
setAddingFrom(selectedState);
|
|
45748
45776
|
}, [selectedState]);
|
|
45749
45777
|
const handleRemoveTransition = useCallback((transId) => {
|
|
45750
|
-
|
|
45751
|
-
}, []);
|
|
45778
|
+
if (removeTransitionEvent) emit(`UI:${removeTransitionEvent}`, { id: transId });
|
|
45779
|
+
}, [removeTransitionEvent, emit]);
|
|
45752
45780
|
const machine = useMemo(() => ({
|
|
45753
45781
|
name: entityName,
|
|
45754
45782
|
description: str(resolved?.description),
|
|
@@ -45762,16 +45790,16 @@ function StateArchitectBoard({
|
|
|
45762
45790
|
}))
|
|
45763
45791
|
}), [entityName, resolved, entityStates, currentState, transitions]);
|
|
45764
45792
|
const handleTest = useCallback(() => {
|
|
45765
|
-
if (
|
|
45793
|
+
if (isTesting) return;
|
|
45766
45794
|
if (testEvent) emit(`UI:${testEvent}`, {});
|
|
45767
|
-
|
|
45795
|
+
setIsTesting(true);
|
|
45768
45796
|
setTestResults([]);
|
|
45769
45797
|
const results = [];
|
|
45770
45798
|
let testIdx = 0;
|
|
45771
45799
|
const runNextTest = () => {
|
|
45772
45800
|
if (testIdx >= testCases.length) {
|
|
45773
45801
|
const allPassed = results.every((r) => r.passed);
|
|
45774
|
-
|
|
45802
|
+
setIsTesting(false);
|
|
45775
45803
|
setTestResults(results);
|
|
45776
45804
|
if (allPassed && completeEvent) {
|
|
45777
45805
|
emit(`UI:${completeEvent}`, {
|
|
@@ -45779,9 +45807,6 @@ function StateArchitectBoard({
|
|
|
45779
45807
|
passedTests: results.filter((r) => r.passed).length
|
|
45780
45808
|
});
|
|
45781
45809
|
}
|
|
45782
|
-
if (!allPassed) {
|
|
45783
|
-
setAttempts((prev) => prev + 1);
|
|
45784
|
-
}
|
|
45785
45810
|
return;
|
|
45786
45811
|
}
|
|
45787
45812
|
const testCase = testCases[testIdx];
|
|
@@ -45804,24 +45829,23 @@ function StateArchitectBoard({
|
|
|
45804
45829
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
45805
45830
|
};
|
|
45806
45831
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
45807
|
-
}, [
|
|
45832
|
+
}, [isTesting, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
|
|
45808
45833
|
const handleTryAgain = useCallback(() => {
|
|
45809
45834
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
45810
|
-
|
|
45835
|
+
setIsTesting(false);
|
|
45811
45836
|
setCurrentState(initialState);
|
|
45812
45837
|
setTestResults([]);
|
|
45813
45838
|
}, [initialState]);
|
|
45814
45839
|
const handleReset = useCallback(() => {
|
|
45815
45840
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
45816
|
-
|
|
45817
|
-
setPlayState("editing");
|
|
45841
|
+
setIsTesting(false);
|
|
45818
45842
|
setCurrentState(initialState);
|
|
45819
45843
|
setTestResults([]);
|
|
45820
45844
|
setVariables([...entityVariables]);
|
|
45821
45845
|
setSelectedState(null);
|
|
45822
45846
|
setAddingFrom(null);
|
|
45823
|
-
|
|
45824
|
-
}, [
|
|
45847
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
45848
|
+
}, [initialState, entityVariables, playAgainEvent, emit]);
|
|
45825
45849
|
const codeData = useMemo(() => ({
|
|
45826
45850
|
name: entityName,
|
|
45827
45851
|
states: entityStates,
|
|
@@ -45917,7 +45941,7 @@ function StateArchitectBoard({
|
|
|
45917
45941
|
]
|
|
45918
45942
|
}
|
|
45919
45943
|
),
|
|
45920
|
-
|
|
45944
|
+
!isTesting && /* @__PURE__ */ jsx(HStack, { gap: "sm", children: /* @__PURE__ */ jsx(
|
|
45921
45945
|
Button,
|
|
45922
45946
|
{
|
|
45923
45947
|
variant: "ghost",
|
|
@@ -45939,7 +45963,7 @@ function StateArchitectBoard({
|
|
|
45939
45963
|
t2.guardHint,
|
|
45940
45964
|
")"
|
|
45941
45965
|
] }),
|
|
45942
|
-
|
|
45966
|
+
!isTesting && /* @__PURE__ */ jsx(
|
|
45943
45967
|
Button,
|
|
45944
45968
|
{
|
|
45945
45969
|
variant: "ghost",
|
|
@@ -45971,21 +45995,21 @@ function StateArchitectBoard({
|
|
|
45971
45995
|
resolved.showCodeView !== false && /* @__PURE__ */ jsx(StateJsonView, { data: codeData, label: "View Code" })
|
|
45972
45996
|
] })
|
|
45973
45997
|
] }),
|
|
45974
|
-
|
|
45975
|
-
|
|
45998
|
+
isSuccess && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("stateArchitect.allPassed") }) }),
|
|
45999
|
+
!isTesting && !isSuccess && testResults.some((r) => !r.passed) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
45976
46000
|
/* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(ENCOURAGEMENT_KEYS3[Math.min(attempts - 1, ENCOURAGEMENT_KEYS3.length - 1)] ?? ENCOURAGEMENT_KEYS3[0]) }) }),
|
|
45977
|
-
attempts >=
|
|
46001
|
+
!isSuccess && attempts >= 2 && Boolean(str(resolved?.hint)) && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
|
|
45978
46002
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
45979
46003
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
|
|
45980
46004
|
] }) })
|
|
45981
46005
|
] }),
|
|
45982
46006
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
|
|
45983
|
-
|
|
46007
|
+
!isTesting && !isSuccess && testResults.some((r) => !r.passed) ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsx(
|
|
45984
46008
|
Button,
|
|
45985
46009
|
{
|
|
45986
46010
|
variant: "primary",
|
|
45987
46011
|
onClick: handleTest,
|
|
45988
|
-
disabled:
|
|
46012
|
+
disabled: isTesting,
|
|
45989
46013
|
children: "\u25B6 " + t("game.runTests")
|
|
45990
46014
|
}
|
|
45991
46015
|
),
|
|
@@ -46689,17 +46713,12 @@ var init_UncontrolledBattleBoard = __esm({
|
|
|
46689
46713
|
}
|
|
46690
46714
|
});
|
|
46691
46715
|
function heroPosition(h) {
|
|
46716
|
+
if ("position" in h && h.position != null) return h.position;
|
|
46692
46717
|
return vec2(h.position);
|
|
46693
46718
|
}
|
|
46694
|
-
function heroOwner(h) {
|
|
46695
|
-
return str(h.owner);
|
|
46696
|
-
}
|
|
46697
46719
|
function heroMovement(h) {
|
|
46698
46720
|
return num(h.movement);
|
|
46699
46721
|
}
|
|
46700
|
-
function hexPassable(h) {
|
|
46701
|
-
return h.passable !== false;
|
|
46702
|
-
}
|
|
46703
46722
|
function defaultIsInRange(from, to, range) {
|
|
46704
46723
|
return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
|
|
46705
46724
|
}
|
|
@@ -46735,38 +46754,41 @@ function WorldMapBoard({
|
|
|
46735
46754
|
}) {
|
|
46736
46755
|
const eventBus = useEventBus();
|
|
46737
46756
|
const resolved = boardEntity(entity);
|
|
46738
|
-
const
|
|
46739
|
-
const
|
|
46757
|
+
const entityUnits = rows(resolved?.units);
|
|
46758
|
+
const entityTiles = rows(resolved?.tiles);
|
|
46740
46759
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
46741
46760
|
const selectedHeroId = resolved?.selectedHeroId ?? null;
|
|
46742
46761
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
46743
46762
|
const backgroundImage = resolved?.backgroundImage;
|
|
46744
46763
|
const [hoveredTile, setHoveredTile] = useState(null);
|
|
46745
|
-
const selectedHero = useMemo(
|
|
46746
|
-
() => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
|
|
46747
|
-
[heroes, selectedHeroId]
|
|
46748
|
-
);
|
|
46749
46764
|
const derivedTiles = useMemo(
|
|
46750
|
-
() =>
|
|
46751
|
-
x: num(
|
|
46752
|
-
y: num(
|
|
46753
|
-
terrain: str(
|
|
46754
|
-
terrainSprite:
|
|
46765
|
+
() => entityTiles.map((t) => ({
|
|
46766
|
+
x: num(t.x),
|
|
46767
|
+
y: num(t.y),
|
|
46768
|
+
terrain: str(t.terrain),
|
|
46769
|
+
terrainSprite: t.terrainSprite == null ? void 0 : str(t.terrainSprite),
|
|
46770
|
+
passable: t.passable !== false
|
|
46755
46771
|
})),
|
|
46756
|
-
[
|
|
46772
|
+
[entityTiles]
|
|
46757
46773
|
);
|
|
46758
46774
|
const tiles = propTiles ?? derivedTiles;
|
|
46759
46775
|
const baseUnits = useMemo(
|
|
46760
|
-
() => propUnits ??
|
|
46761
|
-
id: str(
|
|
46762
|
-
position: heroPosition(
|
|
46763
|
-
name: str(
|
|
46764
|
-
|
|
46765
|
-
|
|
46766
|
-
|
|
46767
|
-
|
|
46776
|
+
() => propUnits ?? entityUnits.map((u) => ({
|
|
46777
|
+
id: str(u.id),
|
|
46778
|
+
position: heroPosition(u),
|
|
46779
|
+
name: str(u.name),
|
|
46780
|
+
// lolo uses `team` field (not `owner`)
|
|
46781
|
+
team: str(u.team) === "enemy" ? "enemy" : "player",
|
|
46782
|
+
health: num(u.health) || 100,
|
|
46783
|
+
maxHealth: num(u.maxHealth) || 100,
|
|
46784
|
+
sprite: u.sprite == null ? void 0 : str(u.sprite)
|
|
46768
46785
|
})),
|
|
46769
|
-
[
|
|
46786
|
+
[entityUnits, propUnits]
|
|
46787
|
+
);
|
|
46788
|
+
const gameUnits = baseUnits;
|
|
46789
|
+
const selectedHero = useMemo(
|
|
46790
|
+
() => gameUnits.find((u) => u.id === selectedHeroId) ?? null,
|
|
46791
|
+
[gameUnits, selectedHeroId]
|
|
46770
46792
|
);
|
|
46771
46793
|
const MOVE_SPEED_MS_PER_TILE = 300;
|
|
46772
46794
|
const movementAnimRef = useRef(null);
|
|
@@ -46812,51 +46834,50 @@ function WorldMapBoard({
|
|
|
46812
46834
|
const validMoves = useMemo(() => {
|
|
46813
46835
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
46814
46836
|
const sp = heroPosition(selectedHero);
|
|
46815
|
-
const
|
|
46837
|
+
const sTeam = str(selectedHero.team);
|
|
46816
46838
|
const range = heroMovement(selectedHero);
|
|
46817
46839
|
const moves = [];
|
|
46818
|
-
|
|
46819
|
-
const
|
|
46820
|
-
const
|
|
46821
|
-
if (
|
|
46822
|
-
if (
|
|
46823
|
-
if (!isInRange(sp, { x:
|
|
46824
|
-
if (
|
|
46825
|
-
const
|
|
46826
|
-
return
|
|
46840
|
+
tiles.forEach((t) => {
|
|
46841
|
+
const tx = t.x;
|
|
46842
|
+
const ty = t.y;
|
|
46843
|
+
if (t.passable === false) return;
|
|
46844
|
+
if (tx === sp.x && ty === sp.y) return;
|
|
46845
|
+
if (!isInRange(sp, { x: tx, y: ty }, range)) return;
|
|
46846
|
+
if (gameUnits.some((u) => {
|
|
46847
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
46848
|
+
return up.x === tx && up.y === ty && str(u.team) === sTeam;
|
|
46827
46849
|
})) return;
|
|
46828
|
-
moves.push({ x:
|
|
46850
|
+
moves.push({ x: tx, y: ty });
|
|
46829
46851
|
});
|
|
46830
46852
|
return moves;
|
|
46831
|
-
}, [selectedHero,
|
|
46853
|
+
}, [selectedHero, tiles, gameUnits, isInRange]);
|
|
46832
46854
|
const attackTargets = useMemo(() => {
|
|
46833
46855
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
46834
46856
|
const sp = heroPosition(selectedHero);
|
|
46835
|
-
const
|
|
46857
|
+
const sTeam = str(selectedHero.team);
|
|
46836
46858
|
const range = heroMovement(selectedHero);
|
|
46837
|
-
return
|
|
46838
|
-
}, [selectedHero,
|
|
46839
|
-
const maxY = Math.max(...
|
|
46859
|
+
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 });
|
|
46860
|
+
}, [selectedHero, gameUnits, isInRange]);
|
|
46861
|
+
const maxY = Math.max(...tiles.map((t) => t.y), 0);
|
|
46840
46862
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
46841
46863
|
const tileToScreen = useCallback(
|
|
46842
46864
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
46843
46865
|
[scale, baseOffsetX]
|
|
46844
46866
|
);
|
|
46845
46867
|
const hoveredHex = useMemo(
|
|
46846
|
-
() => hoveredTile ?
|
|
46847
|
-
[hoveredTile,
|
|
46868
|
+
() => hoveredTile ? tiles.find((t) => t.x === hoveredTile.x && t.y === hoveredTile.y) ?? null : null,
|
|
46869
|
+
[hoveredTile, tiles]
|
|
46848
46870
|
);
|
|
46849
46871
|
const hoveredHero = useMemo(
|
|
46850
|
-
() => hoveredTile ?
|
|
46851
|
-
const
|
|
46852
|
-
return
|
|
46872
|
+
() => hoveredTile ? gameUnits.find((u) => {
|
|
46873
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
46874
|
+
return up.x === hoveredTile.x && up.y === hoveredTile.y;
|
|
46853
46875
|
}) ?? null : null,
|
|
46854
|
-
[hoveredTile,
|
|
46876
|
+
[hoveredTile, gameUnits]
|
|
46855
46877
|
);
|
|
46856
46878
|
const handleTileClick = useCallback((x, y) => {
|
|
46857
46879
|
if (movementAnimRef.current) return;
|
|
46858
|
-
const
|
|
46859
|
-
if (!hex) return;
|
|
46880
|
+
const tile = tiles.find((t) => t.x === x && t.y === y);
|
|
46860
46881
|
if (tileClickEvent) {
|
|
46861
46882
|
eventBus.emit(`UI:${tileClickEvent}`, { x, y });
|
|
46862
46883
|
}
|
|
@@ -46867,38 +46888,39 @@ function WorldMapBoard({
|
|
|
46867
46888
|
if (heroMoveEvent) {
|
|
46868
46889
|
eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
|
|
46869
46890
|
}
|
|
46870
|
-
const feature = str(
|
|
46891
|
+
const feature = tile ? str(tile.feature) : "";
|
|
46871
46892
|
if (feature && feature !== "none") {
|
|
46872
|
-
|
|
46893
|
+
const tileRow = tile;
|
|
46894
|
+
onFeatureEnter?.(heroId, tileRow);
|
|
46873
46895
|
if (featureEnterEvent) {
|
|
46874
|
-
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
|
|
46896
|
+
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex: tileRow });
|
|
46875
46897
|
}
|
|
46876
46898
|
}
|
|
46877
46899
|
});
|
|
46878
46900
|
return;
|
|
46879
46901
|
}
|
|
46880
|
-
const enemy =
|
|
46881
|
-
const
|
|
46882
|
-
return
|
|
46902
|
+
const enemy = gameUnits.find((u) => {
|
|
46903
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
46904
|
+
return up.x === x && up.y === y && str(u.team) === "enemy";
|
|
46883
46905
|
});
|
|
46884
46906
|
if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
|
|
46885
46907
|
const attackerId = str(selectedHero.id);
|
|
46886
|
-
const defenderId =
|
|
46908
|
+
const defenderId = enemy.id;
|
|
46887
46909
|
onBattleEncounter?.(attackerId, defenderId);
|
|
46888
46910
|
if (battleEncounterEvent) {
|
|
46889
46911
|
eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
|
|
46890
46912
|
}
|
|
46891
46913
|
}
|
|
46892
|
-
}, [
|
|
46914
|
+
}, [tiles, gameUnits, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
|
|
46893
46915
|
const handleUnitClick = useCallback((unitId) => {
|
|
46894
|
-
const
|
|
46895
|
-
if (
|
|
46916
|
+
const unit = gameUnits.find((u) => u.id === unitId);
|
|
46917
|
+
if (unit && (str(unit.team) === "player" || allowMoveAllHeroes)) {
|
|
46896
46918
|
onHeroSelect?.(unitId);
|
|
46897
46919
|
if (heroSelectEvent) {
|
|
46898
46920
|
eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
|
|
46899
46921
|
}
|
|
46900
46922
|
}
|
|
46901
|
-
}, [
|
|
46923
|
+
}, [gameUnits, onHeroSelect, allowMoveAllHeroes, eventBus, heroSelectEvent]);
|
|
46902
46924
|
const selectHero = useCallback((id) => {
|
|
46903
46925
|
onHeroSelect?.(id);
|
|
46904
46926
|
if (heroSelectEvent) {
|