@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/providers/index.js
CHANGED
|
@@ -7367,6 +7367,7 @@ var init_Sprite = __esm({
|
|
|
7367
7367
|
}
|
|
7368
7368
|
});
|
|
7369
7369
|
function StateIndicator({
|
|
7370
|
+
assetUrl = DEFAULT_ASSET_URL,
|
|
7370
7371
|
state = "idle",
|
|
7371
7372
|
label,
|
|
7372
7373
|
size = "md",
|
|
@@ -7389,18 +7390,29 @@ function StateIndicator({
|
|
|
7389
7390
|
className
|
|
7390
7391
|
),
|
|
7391
7392
|
children: [
|
|
7392
|
-
/* @__PURE__ */ jsx(Box, { as: "span", children:
|
|
7393
|
+
/* @__PURE__ */ jsx(Box, { as: "span", children: assetUrl ? /* @__PURE__ */ jsx(
|
|
7394
|
+
"img",
|
|
7395
|
+
{
|
|
7396
|
+
src: assetUrl,
|
|
7397
|
+
alt: displayLabel,
|
|
7398
|
+
width: 16,
|
|
7399
|
+
height: 16,
|
|
7400
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7401
|
+
className: "flex-shrink-0"
|
|
7402
|
+
}
|
|
7403
|
+
) : 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 }) }),
|
|
7393
7404
|
/* @__PURE__ */ jsx(Box, { as: "span", children: displayLabel })
|
|
7394
7405
|
]
|
|
7395
7406
|
}
|
|
7396
7407
|
);
|
|
7397
7408
|
}
|
|
7398
|
-
var DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
7409
|
+
var DEFAULT_ASSET_URL, DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
7399
7410
|
var init_StateIndicator = __esm({
|
|
7400
7411
|
"components/game/atoms/StateIndicator.tsx"() {
|
|
7401
7412
|
init_Box();
|
|
7402
7413
|
init_Icon();
|
|
7403
7414
|
init_cn();
|
|
7415
|
+
DEFAULT_ASSET_URL = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
7404
7416
|
DEFAULT_STATE_STYLES = {
|
|
7405
7417
|
idle: { icon: "\u23F8", bgClass: "bg-muted" },
|
|
7406
7418
|
active: { icon: "\u25B6", bgClass: "bg-success" },
|
|
@@ -7475,6 +7487,7 @@ var init_TimerDisplay = __esm({
|
|
|
7475
7487
|
}
|
|
7476
7488
|
});
|
|
7477
7489
|
function ResourceCounter({
|
|
7490
|
+
assetUrl = DEFAULT_ASSET_URL2,
|
|
7478
7491
|
icon,
|
|
7479
7492
|
label = "Gold",
|
|
7480
7493
|
value = 250,
|
|
@@ -7494,7 +7507,17 @@ function ResourceCounter({
|
|
|
7494
7507
|
className
|
|
7495
7508
|
),
|
|
7496
7509
|
children: [
|
|
7497
|
-
|
|
7510
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
7511
|
+
"img",
|
|
7512
|
+
{
|
|
7513
|
+
src: assetUrl,
|
|
7514
|
+
alt: label,
|
|
7515
|
+
width: sizes.img,
|
|
7516
|
+
height: sizes.img,
|
|
7517
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7518
|
+
className: "flex-shrink-0"
|
|
7519
|
+
}
|
|
7520
|
+
) : 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,
|
|
7498
7521
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label }),
|
|
7499
7522
|
/* @__PURE__ */ jsxs("span", { className: cn("font-bold tabular-nums", color && (color in colorTokenClasses2 ? colorTokenClasses2[color] : color)), children: [
|
|
7500
7523
|
value,
|
|
@@ -7507,7 +7530,7 @@ function ResourceCounter({
|
|
|
7507
7530
|
}
|
|
7508
7531
|
);
|
|
7509
7532
|
}
|
|
7510
|
-
var colorTokenClasses2, sizeMap5;
|
|
7533
|
+
var colorTokenClasses2, DEFAULT_ASSET_URL2, sizeMap5;
|
|
7511
7534
|
var init_ResourceCounter = __esm({
|
|
7512
7535
|
"components/game/atoms/ResourceCounter.tsx"() {
|
|
7513
7536
|
init_cn();
|
|
@@ -7520,16 +7543,17 @@ var init_ResourceCounter = __esm({
|
|
|
7520
7543
|
error: "text-error",
|
|
7521
7544
|
muted: "text-muted-foreground"
|
|
7522
7545
|
};
|
|
7546
|
+
DEFAULT_ASSET_URL2 = "https://almadar-kflow-assets.web.app/shared/world-map/gold_mine.png";
|
|
7523
7547
|
sizeMap5 = {
|
|
7524
|
-
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm" },
|
|
7525
|
-
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base" },
|
|
7526
|
-
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg" }
|
|
7548
|
+
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm", img: 16 },
|
|
7549
|
+
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base", img: 20 },
|
|
7550
|
+
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg", img: 28 }
|
|
7527
7551
|
};
|
|
7528
7552
|
ResourceCounter.displayName = "ResourceCounter";
|
|
7529
7553
|
}
|
|
7530
7554
|
});
|
|
7531
7555
|
function ItemSlot({
|
|
7532
|
-
assetUrl =
|
|
7556
|
+
assetUrl = DEFAULT_ASSET_URL3,
|
|
7533
7557
|
icon = "sword",
|
|
7534
7558
|
label = "Iron Sword",
|
|
7535
7559
|
quantity,
|
|
@@ -7588,7 +7612,7 @@ function ItemSlot({
|
|
|
7588
7612
|
}
|
|
7589
7613
|
);
|
|
7590
7614
|
}
|
|
7591
|
-
var sizeMap6, rarityBorderMap, rarityGlowMap,
|
|
7615
|
+
var sizeMap6, rarityBorderMap, rarityGlowMap, DEFAULT_ASSET_URL3, assetSizeMap;
|
|
7592
7616
|
var init_ItemSlot = __esm({
|
|
7593
7617
|
"components/game/atoms/ItemSlot.tsx"() {
|
|
7594
7618
|
"use client";
|
|
@@ -7613,7 +7637,7 @@ var init_ItemSlot = __esm({
|
|
|
7613
7637
|
epic: "shadow-lg",
|
|
7614
7638
|
legendary: "shadow-lg"
|
|
7615
7639
|
};
|
|
7616
|
-
|
|
7640
|
+
DEFAULT_ASSET_URL3 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
7617
7641
|
assetSizeMap = {
|
|
7618
7642
|
sm: 28,
|
|
7619
7643
|
md: 40,
|
|
@@ -7686,6 +7710,7 @@ function getComboScale(combo) {
|
|
|
7686
7710
|
return "";
|
|
7687
7711
|
}
|
|
7688
7712
|
function ComboCounter({
|
|
7713
|
+
assetUrl = DEFAULT_ASSET_URL4,
|
|
7689
7714
|
combo = 5,
|
|
7690
7715
|
multiplier,
|
|
7691
7716
|
streak,
|
|
@@ -7705,6 +7730,17 @@ function ComboCounter({
|
|
|
7705
7730
|
className
|
|
7706
7731
|
),
|
|
7707
7732
|
children: [
|
|
7733
|
+
assetUrl && /* @__PURE__ */ jsx(
|
|
7734
|
+
"img",
|
|
7735
|
+
{
|
|
7736
|
+
src: assetUrl,
|
|
7737
|
+
alt: "combo",
|
|
7738
|
+
width: 24,
|
|
7739
|
+
height: 24,
|
|
7740
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7741
|
+
className: "flex-shrink-0 mb-0.5"
|
|
7742
|
+
}
|
|
7743
|
+
),
|
|
7708
7744
|
/* @__PURE__ */ jsx("span", { className: cn("font-black tabular-nums leading-none", sizes.combo, getComboIntensity(combo)), children: combo }),
|
|
7709
7745
|
/* @__PURE__ */ jsx("span", { className: cn("font-bold uppercase tracking-wider text-muted-foreground", sizes.label), children: "combo" }),
|
|
7710
7746
|
multiplier != null && multiplier > 1 && /* @__PURE__ */ jsxs("span", { className: cn("font-bold text-warning tabular-nums", sizes.multiplier), children: [
|
|
@@ -7719,10 +7755,11 @@ function ComboCounter({
|
|
|
7719
7755
|
}
|
|
7720
7756
|
);
|
|
7721
7757
|
}
|
|
7722
|
-
var sizeMap8;
|
|
7758
|
+
var DEFAULT_ASSET_URL4, sizeMap8;
|
|
7723
7759
|
var init_ComboCounter = __esm({
|
|
7724
7760
|
"components/game/atoms/ComboCounter.tsx"() {
|
|
7725
7761
|
init_cn();
|
|
7762
|
+
DEFAULT_ASSET_URL4 = "https://almadar-kflow-assets.web.app/shared/effects/flash/flash00.png";
|
|
7726
7763
|
sizeMap8 = {
|
|
7727
7764
|
sm: { combo: "text-lg", label: "text-xs", multiplier: "text-xs" },
|
|
7728
7765
|
md: { combo: "text-2xl", label: "text-xs", multiplier: "text-sm" },
|
|
@@ -7816,6 +7853,7 @@ var init_XPBar = __esm({
|
|
|
7816
7853
|
}
|
|
7817
7854
|
});
|
|
7818
7855
|
function WaypointMarker({
|
|
7856
|
+
assetUrl = DEFAULT_ASSET_URL5,
|
|
7819
7857
|
label,
|
|
7820
7858
|
icon,
|
|
7821
7859
|
active = true,
|
|
@@ -7854,7 +7892,17 @@ function WaypointMarker({
|
|
|
7854
7892
|
active && !completed && "bg-info text-foreground",
|
|
7855
7893
|
!active && !completed && "bg-muted"
|
|
7856
7894
|
),
|
|
7857
|
-
children: completed ? checkIcon :
|
|
7895
|
+
children: completed ? checkIcon : assetUrl ? /* @__PURE__ */ jsx(
|
|
7896
|
+
"img",
|
|
7897
|
+
{
|
|
7898
|
+
src: assetUrl,
|
|
7899
|
+
alt: label,
|
|
7900
|
+
width: sizes.img,
|
|
7901
|
+
height: sizes.img,
|
|
7902
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7903
|
+
className: "flex-shrink-0"
|
|
7904
|
+
}
|
|
7905
|
+
) : icon ? typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon }) : /* @__PURE__ */ jsx(Icon, { icon }) : null
|
|
7858
7906
|
}
|
|
7859
7907
|
)
|
|
7860
7908
|
] }),
|
|
@@ -7871,15 +7919,16 @@ function WaypointMarker({
|
|
|
7871
7919
|
)
|
|
7872
7920
|
] });
|
|
7873
7921
|
}
|
|
7874
|
-
var sizeMap10, checkIcon;
|
|
7922
|
+
var DEFAULT_ASSET_URL5, sizeMap10, checkIcon;
|
|
7875
7923
|
var init_WaypointMarker = __esm({
|
|
7876
7924
|
"components/game/atoms/WaypointMarker.tsx"() {
|
|
7877
7925
|
init_cn();
|
|
7878
7926
|
init_Icon();
|
|
7927
|
+
DEFAULT_ASSET_URL5 = "https://almadar-kflow-assets.web.app/shared/world-map/battle_marker.png";
|
|
7879
7928
|
sizeMap10 = {
|
|
7880
|
-
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1" },
|
|
7881
|
-
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5" },
|
|
7882
|
-
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2" }
|
|
7929
|
+
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1", img: 12 },
|
|
7930
|
+
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5", img: 18 },
|
|
7931
|
+
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2", img: 24 }
|
|
7883
7932
|
};
|
|
7884
7933
|
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" }) });
|
|
7885
7934
|
WaypointMarker.displayName = "WaypointMarker";
|
|
@@ -7892,6 +7941,7 @@ function formatDuration(seconds) {
|
|
|
7892
7941
|
return `${Math.round(seconds)}s`;
|
|
7893
7942
|
}
|
|
7894
7943
|
function StatusEffect({
|
|
7944
|
+
assetUrl = DEFAULT_ASSET_URL6,
|
|
7895
7945
|
icon = "shield",
|
|
7896
7946
|
label = "Shield",
|
|
7897
7947
|
duration = 30,
|
|
@@ -7912,7 +7962,17 @@ function StatusEffect({
|
|
|
7912
7962
|
),
|
|
7913
7963
|
title: label,
|
|
7914
7964
|
children: [
|
|
7915
|
-
/* @__PURE__ */ jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children:
|
|
7965
|
+
/* @__PURE__ */ jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children: assetUrl ? /* @__PURE__ */ jsx(
|
|
7966
|
+
"img",
|
|
7967
|
+
{
|
|
7968
|
+
src: assetUrl,
|
|
7969
|
+
alt: label,
|
|
7970
|
+
width: sizes.img,
|
|
7971
|
+
height: sizes.img,
|
|
7972
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7973
|
+
className: "flex-shrink-0"
|
|
7974
|
+
}
|
|
7975
|
+
) : typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, size: "sm" }) : /* @__PURE__ */ jsx(Icon, { icon, size: "sm" }) }),
|
|
7916
7976
|
duration !== void 0 && /* @__PURE__ */ jsx(
|
|
7917
7977
|
"span",
|
|
7918
7978
|
{
|
|
@@ -7939,15 +7999,16 @@ function StatusEffect({
|
|
|
7939
7999
|
label && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground mt-0.5 text-center whitespace-nowrap", children: label })
|
|
7940
8000
|
] });
|
|
7941
8001
|
}
|
|
7942
|
-
var sizeMap11, variantStyles7;
|
|
8002
|
+
var DEFAULT_ASSET_URL6, sizeMap11, variantStyles7;
|
|
7943
8003
|
var init_StatusEffect = __esm({
|
|
7944
8004
|
"components/game/atoms/StatusEffect.tsx"() {
|
|
7945
8005
|
init_cn();
|
|
7946
8006
|
init_Icon();
|
|
8007
|
+
DEFAULT_ASSET_URL6 = "https://almadar-kflow-assets.web.app/shared/effects/particles/flame_01.png";
|
|
7947
8008
|
sizeMap11 = {
|
|
7948
|
-
sm: { container: "w-8 h-8", icon: "text-sm", badge: "text-xs -top-1 -right-1 w-4 h-4", timer: "text-[9px]" },
|
|
7949
|
-
md: { container: "w-10 h-10", icon: "text-base", badge: "text-xs -top-1 -right-1 w-5 h-5", timer: "text-xs" },
|
|
7950
|
-
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" }
|
|
8009
|
+
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 },
|
|
8010
|
+
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 },
|
|
8011
|
+
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 }
|
|
7951
8012
|
};
|
|
7952
8013
|
variantStyles7 = {
|
|
7953
8014
|
buff: "border-success bg-success/20",
|
|
@@ -11860,15 +11921,15 @@ function BattleBoard({
|
|
|
11860
11921
|
const board = boardEntity(entity) ?? {};
|
|
11861
11922
|
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
|
|
11862
11923
|
const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
|
|
11863
|
-
const boardWidth = num(board.boardWidth, 8);
|
|
11864
|
-
const boardHeight = num(board.boardHeight, 6);
|
|
11924
|
+
const boardWidth = num(board.gridWidth ?? board.boardWidth, 8);
|
|
11925
|
+
const boardHeight = num(board.gridHeight ?? board.boardHeight, 6);
|
|
11865
11926
|
const assetManifest = propAssetManifest ?? board.assetManifest;
|
|
11866
11927
|
const backgroundImage = board.backgroundImage;
|
|
11867
11928
|
const units = rows(board.units);
|
|
11868
11929
|
const selectedUnitId = board.selectedUnitId ?? null;
|
|
11869
11930
|
const currentPhase = str(board.phase) || "observation";
|
|
11870
11931
|
const currentTurn = num(board.turn, 1);
|
|
11871
|
-
const gameResult = board.
|
|
11932
|
+
const gameResult = board.result ?? null;
|
|
11872
11933
|
const eventBus = useEventBus();
|
|
11873
11934
|
const { t } = useTranslate();
|
|
11874
11935
|
const [hoveredTile, setHoveredTile] = useState(null);
|
|
@@ -11889,7 +11950,7 @@ function BattleBoard({
|
|
|
11889
11950
|
const validMoves = useMemo(() => {
|
|
11890
11951
|
if (!selectedUnit || currentPhase !== "movement") return [];
|
|
11891
11952
|
const moves = [];
|
|
11892
|
-
const range = num(
|
|
11953
|
+
const range = num(board.movementRange, 2);
|
|
11893
11954
|
const origin = unitPosition(selectedUnit);
|
|
11894
11955
|
for (let dy = -range; dy <= range; dy++) {
|
|
11895
11956
|
for (let dx = -range; dx <= range; dx++) {
|
|
@@ -20174,15 +20235,24 @@ function CastleBoard({
|
|
|
20174
20235
|
featureClickEvent,
|
|
20175
20236
|
unitClickEvent,
|
|
20176
20237
|
tileClickEvent,
|
|
20238
|
+
playAgainEvent,
|
|
20177
20239
|
className
|
|
20178
20240
|
}) {
|
|
20179
20241
|
const eventBus = useEventBus();
|
|
20242
|
+
const { t } = useTranslate();
|
|
20180
20243
|
const resolved = boardEntity(entity);
|
|
20181
20244
|
const tiles = propTiles ?? (Array.isArray(resolved?.tiles) ? resolved.tiles : []);
|
|
20182
20245
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
20183
20246
|
const units = propUnits ?? (Array.isArray(resolved?.units) ? resolved.units : []);
|
|
20184
20247
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
20185
20248
|
const backgroundImage = resolved?.backgroundImage;
|
|
20249
|
+
const gold = num(resolved?.gold);
|
|
20250
|
+
const health = num(resolved?.health);
|
|
20251
|
+
const maxHealth = num(resolved?.maxHealth);
|
|
20252
|
+
const wave = num(resolved?.wave);
|
|
20253
|
+
const tickCount = num(resolved?.tickCount);
|
|
20254
|
+
const buildings = rows(resolved?.buildings);
|
|
20255
|
+
const result = str(resolved?.result) || "none";
|
|
20186
20256
|
const [hoveredTile, setHoveredTile] = useState(null);
|
|
20187
20257
|
const [selectedFeature, setSelectedFeature] = useState(null);
|
|
20188
20258
|
const hoveredFeature = useMemo(() => {
|
|
@@ -20195,7 +20265,7 @@ function CastleBoard({
|
|
|
20195
20265
|
(u) => u.position?.x === hoveredTile.x && u.position?.y === hoveredTile.y
|
|
20196
20266
|
) ?? null;
|
|
20197
20267
|
}, [hoveredTile, units]);
|
|
20198
|
-
const maxY = Math.max(...tiles.map((
|
|
20268
|
+
const maxY = Math.max(...tiles.map((t2) => t2.y), 0);
|
|
20199
20269
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
20200
20270
|
const tileToScreen = useCallback(
|
|
20201
20271
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
@@ -20230,6 +20300,11 @@ function CastleBoard({
|
|
|
20230
20300
|
}
|
|
20231
20301
|
}, [units, onUnitClick, unitClickEvent, eventBus]);
|
|
20232
20302
|
const clearSelection = useCallback(() => setSelectedFeature(null), []);
|
|
20303
|
+
const handlePlayAgain = useCallback(() => {
|
|
20304
|
+
if (playAgainEvent) {
|
|
20305
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
20306
|
+
}
|
|
20307
|
+
}, [playAgainEvent, eventBus]);
|
|
20233
20308
|
const ctx = useMemo(
|
|
20234
20309
|
() => ({
|
|
20235
20310
|
hoveredTile,
|
|
@@ -20238,11 +20313,18 @@ function CastleBoard({
|
|
|
20238
20313
|
selectedFeature,
|
|
20239
20314
|
clearSelection,
|
|
20240
20315
|
tileToScreen,
|
|
20241
|
-
scale
|
|
20316
|
+
scale,
|
|
20317
|
+
gold,
|
|
20318
|
+
health,
|
|
20319
|
+
maxHealth,
|
|
20320
|
+
wave,
|
|
20321
|
+
tickCount,
|
|
20322
|
+
buildings,
|
|
20323
|
+
result
|
|
20242
20324
|
}),
|
|
20243
|
-
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale]
|
|
20325
|
+
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale, gold, health, maxHealth, wave, tickCount, buildings, result]
|
|
20244
20326
|
);
|
|
20245
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("castle-board min-h-screen flex flex-col bg-background", className), children: [
|
|
20327
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("castle-board relative min-h-screen flex flex-col bg-background", className), children: [
|
|
20246
20328
|
header && header(ctx),
|
|
20247
20329
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
|
|
20248
20330
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-auto p-4 relative", children: [
|
|
@@ -20267,7 +20349,29 @@ function CastleBoard({
|
|
|
20267
20349
|
] }),
|
|
20268
20350
|
sidePanel && /* @__PURE__ */ jsx("div", { className: "w-96 shrink-0 border-l border-border bg-surface overflow-y-auto", children: sidePanel(ctx) })
|
|
20269
20351
|
] }),
|
|
20270
|
-
footer && footer(ctx)
|
|
20352
|
+
footer && footer(ctx),
|
|
20353
|
+
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: [
|
|
20354
|
+
/* @__PURE__ */ jsx(
|
|
20355
|
+
Typography,
|
|
20356
|
+
{
|
|
20357
|
+
variant: "h2",
|
|
20358
|
+
className: cn(
|
|
20359
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
20360
|
+
result === "victory" ? "text-warning" : "text-error"
|
|
20361
|
+
),
|
|
20362
|
+
children: result === "victory" ? t("battle.victory") : t("battle.defeat")
|
|
20363
|
+
}
|
|
20364
|
+
),
|
|
20365
|
+
/* @__PURE__ */ jsx(
|
|
20366
|
+
Button,
|
|
20367
|
+
{
|
|
20368
|
+
variant: "primary",
|
|
20369
|
+
className: "px-8 py-3 font-semibold",
|
|
20370
|
+
onClick: handlePlayAgain,
|
|
20371
|
+
children: t("battle.playAgain")
|
|
20372
|
+
}
|
|
20373
|
+
)
|
|
20374
|
+
] }) })
|
|
20271
20375
|
] });
|
|
20272
20376
|
}
|
|
20273
20377
|
var init_CastleBoard = __esm({
|
|
@@ -20275,6 +20379,10 @@ var init_CastleBoard = __esm({
|
|
|
20275
20379
|
"use client";
|
|
20276
20380
|
init_cn();
|
|
20277
20381
|
init_useEventBus();
|
|
20382
|
+
init_Box();
|
|
20383
|
+
init_Button();
|
|
20384
|
+
init_Typography();
|
|
20385
|
+
init_Stack();
|
|
20278
20386
|
init_IsometricCanvas();
|
|
20279
20387
|
init_boardEntity();
|
|
20280
20388
|
init_isometric();
|
|
@@ -21151,22 +21259,16 @@ function ClassifierBoard({
|
|
|
21151
21259
|
const { emit } = useEventBus();
|
|
21152
21260
|
const { t } = useTranslate();
|
|
21153
21261
|
const resolved = boardEntity(entity);
|
|
21154
|
-
const [localAssignments, setLocalAssignments] = useState({});
|
|
21155
21262
|
const [headerError, setHeaderError] = useState(false);
|
|
21156
|
-
const [localSubmitted, setLocalSubmitted] = useState(false);
|
|
21157
|
-
const [localAttempts, setLocalAttempts] = useState(0);
|
|
21158
|
-
const [showHint, setShowHint] = useState(false);
|
|
21159
21263
|
const items = Array.isArray(resolved?.items) ? resolved.items : [];
|
|
21160
21264
|
const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
|
|
21161
|
-
const
|
|
21162
|
-
const
|
|
21163
|
-
const
|
|
21164
|
-
const assignments =
|
|
21165
|
-
if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
|
|
21265
|
+
const result = str(resolved?.result);
|
|
21266
|
+
const submitted = result === "win";
|
|
21267
|
+
const attempts = num(resolved?.attempts);
|
|
21268
|
+
const assignments = items.reduce((acc, item) => {
|
|
21269
|
+
if (item.assignedCategory != null && item.assignedCategory !== "") acc[item.id] = item.assignedCategory;
|
|
21166
21270
|
return acc;
|
|
21167
|
-
}, {})
|
|
21168
|
-
const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
|
|
21169
|
-
const submitted = entityDrivesResult || localSubmitted;
|
|
21271
|
+
}, {});
|
|
21170
21272
|
const unassignedItems = items.filter((item) => !assignments[item.id]);
|
|
21171
21273
|
const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
|
|
21172
21274
|
const results = submitted ? items.map((item) => ({
|
|
@@ -21174,54 +21276,25 @@ function ClassifierBoard({
|
|
|
21174
21276
|
assigned: assignments[item.id],
|
|
21175
21277
|
correct: assignments[item.id] === item.correctCategory
|
|
21176
21278
|
})) : [];
|
|
21177
|
-
const allCorrect =
|
|
21279
|
+
const allCorrect = result === "win";
|
|
21178
21280
|
const correctCount = results.filter((r) => r.correct).length;
|
|
21281
|
+
const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
|
|
21179
21282
|
const handleAssign = (itemId, categoryId) => {
|
|
21180
21283
|
if (submitted) return;
|
|
21181
|
-
if (assignEvent) {
|
|
21182
|
-
emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
21183
|
-
}
|
|
21184
|
-
if (!entityHasAssignments) {
|
|
21185
|
-
setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
|
|
21186
|
-
}
|
|
21284
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
21187
21285
|
};
|
|
21188
21286
|
const handleUnassign = (itemId) => {
|
|
21189
21287
|
if (submitted) return;
|
|
21190
|
-
if (
|
|
21191
|
-
setLocalAssignments((prev) => {
|
|
21192
|
-
const next = { ...prev };
|
|
21193
|
-
delete next[itemId];
|
|
21194
|
-
return next;
|
|
21195
|
-
});
|
|
21196
|
-
}
|
|
21288
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId: "" });
|
|
21197
21289
|
};
|
|
21198
|
-
const handleSubmit =
|
|
21199
|
-
if (checkEvent) {
|
|
21200
|
-
|
|
21201
|
-
|
|
21202
|
-
if (!entityDrivesResult) {
|
|
21203
|
-
setLocalSubmitted(true);
|
|
21204
|
-
setLocalAttempts((a) => a + 1);
|
|
21205
|
-
const correct = items.every((item) => assignments[item.id] === item.correctCategory);
|
|
21206
|
-
if (correct) {
|
|
21207
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
21208
|
-
}
|
|
21209
|
-
}
|
|
21210
|
-
}, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
|
|
21211
|
-
const handleReset = () => {
|
|
21212
|
-
if (!entityDrivesResult) setLocalSubmitted(false);
|
|
21213
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
21214
|
-
setShowHint(true);
|
|
21290
|
+
const handleSubmit = () => {
|
|
21291
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
21292
|
+
if (allAssigned && items.every((item) => assignments[item.id] === item.correctCategory)) {
|
|
21293
|
+
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
21215
21294
|
}
|
|
21216
21295
|
};
|
|
21217
21296
|
const handleFullReset = () => {
|
|
21218
|
-
if (playAgainEvent) {
|
|
21219
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
21220
|
-
}
|
|
21221
|
-
setLocalAssignments({});
|
|
21222
|
-
setLocalSubmitted(false);
|
|
21223
|
-
setLocalAttempts(0);
|
|
21224
|
-
setShowHint(false);
|
|
21297
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
21225
21298
|
};
|
|
21226
21299
|
if (!resolved) return null;
|
|
21227
21300
|
const theme = resolved.theme ?? void 0;
|
|
@@ -21260,17 +21333,17 @@ function ClassifierBoard({
|
|
|
21260
21333
|
/* @__PURE__ */ jsx(Badge, { size: "sm", children: catItems.length })
|
|
21261
21334
|
] }),
|
|
21262
21335
|
/* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-wrap min-h-[2rem]", children: catItems.map((item) => {
|
|
21263
|
-
const
|
|
21336
|
+
const result2 = results.find((r) => r.item.id === item.id);
|
|
21264
21337
|
return /* @__PURE__ */ jsxs(
|
|
21265
21338
|
Badge,
|
|
21266
21339
|
{
|
|
21267
21340
|
size: "sm",
|
|
21268
|
-
className: `cursor-pointer ${
|
|
21341
|
+
className: `cursor-pointer ${result2 ? result2.correct ? "border-success bg-success/10" : "border-error bg-error/10" : ""}`,
|
|
21269
21342
|
onClick: () => handleUnassign(item.id),
|
|
21270
21343
|
children: [
|
|
21271
21344
|
item.iconUrl && /* @__PURE__ */ jsx("img", { src: item.iconUrl, alt: "", className: "w-3 h-3 object-contain inline-block" }),
|
|
21272
21345
|
item.label,
|
|
21273
|
-
|
|
21346
|
+
result2 && /* @__PURE__ */ jsx(Icon, { icon: result2.correct ? CheckCircle : XCircle, size: "xs", className: result2.correct ? "text-success" : "text-error" })
|
|
21274
21347
|
]
|
|
21275
21348
|
},
|
|
21276
21349
|
item.id
|
|
@@ -21299,10 +21372,10 @@ function ClassifierBoard({
|
|
|
21299
21372
|
] }) }),
|
|
21300
21373
|
showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
|
|
21301
21374
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
21302
|
-
!submitted
|
|
21375
|
+
!submitted && /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
|
|
21303
21376
|
/* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
|
|
21304
21377
|
t("classifier.check")
|
|
21305
|
-
] })
|
|
21378
|
+
] }),
|
|
21306
21379
|
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
|
|
21307
21380
|
/* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
|
|
21308
21381
|
t("classifier.reset")
|
|
@@ -29331,7 +29404,7 @@ var init_MapView = __esm({
|
|
|
29331
29404
|
shadowSize: [41, 41]
|
|
29332
29405
|
});
|
|
29333
29406
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
29334
|
-
const { useEffect: useEffect73, useRef: useRef68, useCallback:
|
|
29407
|
+
const { useEffect: useEffect73, useRef: useRef68, useCallback: useCallback111, useState: useState104 } = React83__default;
|
|
29335
29408
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
29336
29409
|
const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
29337
29410
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
@@ -29377,7 +29450,7 @@ var init_MapView = __esm({
|
|
|
29377
29450
|
}) {
|
|
29378
29451
|
const eventBus = useEventBus2();
|
|
29379
29452
|
const [clickedPosition, setClickedPosition] = useState104(null);
|
|
29380
|
-
const handleMapClick =
|
|
29453
|
+
const handleMapClick = useCallback111((lat, lng) => {
|
|
29381
29454
|
if (showClickedPin) {
|
|
29382
29455
|
setClickedPosition({ lat, lng });
|
|
29383
29456
|
}
|
|
@@ -29386,7 +29459,7 @@ var init_MapView = __esm({
|
|
|
29386
29459
|
eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
|
|
29387
29460
|
}
|
|
29388
29461
|
}, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
|
|
29389
|
-
const handleMarkerClick =
|
|
29462
|
+
const handleMarkerClick = useCallback111((marker) => {
|
|
29390
29463
|
onMarkerClick?.(marker);
|
|
29391
29464
|
if (markerClickEvent) {
|
|
29392
29465
|
eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
|
|
@@ -38617,44 +38690,27 @@ function DebuggerBoard({
|
|
|
38617
38690
|
const { t } = useTranslate();
|
|
38618
38691
|
const resolved = boardEntity(entity);
|
|
38619
38692
|
const [headerError, setHeaderError] = useState(false);
|
|
38620
|
-
const [showHint, setShowHint] = useState(false);
|
|
38621
38693
|
const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
|
|
38622
|
-
const result = resolved?.result
|
|
38694
|
+
const result = str(resolved?.result) || "none";
|
|
38623
38695
|
const attempts = num(resolved?.attempts);
|
|
38624
|
-
const submitted = result
|
|
38696
|
+
const submitted = result === "win";
|
|
38697
|
+
const showHint = !submitted && attempts >= 2 && Boolean(str(resolved?.hint));
|
|
38625
38698
|
const bugLines = lines.filter((l) => l.isBug);
|
|
38626
38699
|
const flaggedLines = lines.filter((l) => l.isFlagged);
|
|
38627
38700
|
const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
|
|
38628
38701
|
const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
|
|
38629
|
-
const allCorrect =
|
|
38702
|
+
const allCorrect = submitted;
|
|
38630
38703
|
const toggleLine = (lineId) => {
|
|
38631
38704
|
if (submitted) return;
|
|
38632
|
-
if (toggleFlagEvent) {
|
|
38633
|
-
emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
38634
|
-
}
|
|
38705
|
+
if (toggleFlagEvent) emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
38635
38706
|
};
|
|
38636
38707
|
const handleSubmit = useCallback(() => {
|
|
38637
|
-
if (checkEvent) {
|
|
38638
|
-
emit(`UI:${checkEvent}`, {});
|
|
38639
|
-
}
|
|
38708
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
38640
38709
|
const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
|
|
38641
|
-
if (correct) {
|
|
38642
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
38643
|
-
}
|
|
38710
|
+
if (correct) emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
38644
38711
|
}, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
|
|
38645
38712
|
const handleReset = () => {
|
|
38646
|
-
if (playAgainEvent) {
|
|
38647
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
38648
|
-
}
|
|
38649
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
38650
|
-
setShowHint(true);
|
|
38651
|
-
}
|
|
38652
|
-
};
|
|
38653
|
-
const handleFullReset = () => {
|
|
38654
|
-
if (playAgainEvent) {
|
|
38655
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
38656
|
-
}
|
|
38657
|
-
setShowHint(false);
|
|
38713
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
38658
38714
|
};
|
|
38659
38715
|
if (!resolved) return null;
|
|
38660
38716
|
const theme = resolved.theme ?? void 0;
|
|
@@ -38678,7 +38734,7 @@ function DebuggerBoard({
|
|
|
38678
38734
|
/* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
|
|
38679
38735
|
] }),
|
|
38680
38736
|
/* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) }),
|
|
38681
|
-
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(
|
|
38737
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(lines.filter((l) => l.isBug).length) }) })
|
|
38682
38738
|
] }) }),
|
|
38683
38739
|
/* @__PURE__ */ jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsx(VStack, { gap: "none", children: lines.map((line, i) => {
|
|
38684
38740
|
const isFlagged = !!line.isFlagged;
|
|
@@ -38728,11 +38784,11 @@ function DebuggerBoard({
|
|
|
38728
38784
|
] }) }),
|
|
38729
38785
|
showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
|
|
38730
38786
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
38731
|
-
!submitted
|
|
38787
|
+
!submitted && /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
|
|
38732
38788
|
/* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
|
|
38733
38789
|
t("debugger.submit")
|
|
38734
|
-
] })
|
|
38735
|
-
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick:
|
|
38790
|
+
] }),
|
|
38791
|
+
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleReset, children: [
|
|
38736
38792
|
/* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
|
|
38737
38793
|
t("debugger.reset")
|
|
38738
38794
|
] })
|
|
@@ -39733,39 +39789,40 @@ function EventHandlerBoard({
|
|
|
39733
39789
|
stepDurationMs = 800,
|
|
39734
39790
|
playEvent,
|
|
39735
39791
|
completeEvent,
|
|
39792
|
+
editRuleEvent,
|
|
39793
|
+
playAgainEvent,
|
|
39736
39794
|
className
|
|
39737
39795
|
}) {
|
|
39738
39796
|
const { emit } = useEventBus();
|
|
39739
39797
|
const { t } = useTranslate();
|
|
39740
39798
|
const resolved = boardEntity(entity);
|
|
39741
|
-
const
|
|
39742
|
-
const
|
|
39799
|
+
const objects = rows(resolved?.objects);
|
|
39800
|
+
const entityResult = str(resolved?.result) || "none";
|
|
39801
|
+
const isSuccess = entityResult === "win";
|
|
39802
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
39743
39803
|
const [selectedObjectId, setSelectedObjectId] = useState(
|
|
39744
|
-
|
|
39804
|
+
objects[0] ? objId(objects[0]) : null
|
|
39745
39805
|
);
|
|
39746
39806
|
const [headerError, setHeaderError] = useState(false);
|
|
39747
|
-
const [
|
|
39807
|
+
const [isPlaying, setIsPlaying] = useState(false);
|
|
39748
39808
|
const [eventLog, setEventLog] = useState([]);
|
|
39749
|
-
const [attempts, setAttempts] = useState(0);
|
|
39750
39809
|
const timerRef = useRef(null);
|
|
39751
39810
|
const logIdCounter = useRef(0);
|
|
39752
39811
|
useEffect(() => () => {
|
|
39753
39812
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39754
39813
|
}, []);
|
|
39755
|
-
const selectedObject = objects.find((o) => objId(o) === selectedObjectId)
|
|
39814
|
+
const selectedObject = objects.find((o) => objId(o) === selectedObjectId) ?? null;
|
|
39756
39815
|
const handleRulesChange = useCallback((objectId, rules) => {
|
|
39757
|
-
|
|
39758
|
-
|
|
39759
|
-
));
|
|
39760
|
-
}, []);
|
|
39816
|
+
if (editRuleEvent) emit(`UI:${editRuleEvent}`, { objectId, rules });
|
|
39817
|
+
}, [editRuleEvent, emit]);
|
|
39761
39818
|
const addLogEntry = useCallback((icon, message, status = "done") => {
|
|
39762
39819
|
const id = `log-${logIdCounter.current++}`;
|
|
39763
39820
|
setEventLog((prev) => [...prev, { id, timestamp: Date.now(), icon, message, status }]);
|
|
39764
39821
|
}, []);
|
|
39765
39822
|
const handlePlay = useCallback(() => {
|
|
39766
|
-
if (
|
|
39823
|
+
if (isPlaying || isSuccess) return;
|
|
39767
39824
|
if (playEvent) emit(`UI:${playEvent}`, {});
|
|
39768
|
-
|
|
39825
|
+
setIsPlaying(true);
|
|
39769
39826
|
setEventLog([]);
|
|
39770
39827
|
const allRules = [];
|
|
39771
39828
|
objects.forEach((obj) => {
|
|
@@ -39781,15 +39838,8 @@ function EventHandlerBoard({
|
|
|
39781
39838
|
let goalReached = false;
|
|
39782
39839
|
const processNext = () => {
|
|
39783
39840
|
if (eventQueue.length === 0 || stepIdx > 20) {
|
|
39784
|
-
|
|
39785
|
-
|
|
39786
|
-
if (completeEvent) {
|
|
39787
|
-
emit(`UI:${completeEvent}`, { success: true });
|
|
39788
|
-
}
|
|
39789
|
-
} else {
|
|
39790
|
-
setAttempts((prev) => prev + 1);
|
|
39791
|
-
setPlayState("fail");
|
|
39792
|
-
}
|
|
39841
|
+
setIsPlaying(false);
|
|
39842
|
+
if (goalReached && completeEvent) emit(`UI:${completeEvent}`, { success: true });
|
|
39793
39843
|
return;
|
|
39794
39844
|
}
|
|
39795
39845
|
const currentEvent = eventQueue.shift();
|
|
@@ -39820,21 +39870,19 @@ function EventHandlerBoard({
|
|
|
39820
39870
|
addLogEntry("\u{1F3AC}", t("eventHandler.simulationStarted", { events: triggers.join(", ") }), "active");
|
|
39821
39871
|
}
|
|
39822
39872
|
timerRef.current = setTimeout(processNext, stepDurationMs);
|
|
39823
|
-
}, [
|
|
39873
|
+
}, [isPlaying, isSuccess, objects, resolved, stepDurationMs, playEvent, completeEvent, emit, addLogEntry, t]);
|
|
39824
39874
|
const handleTryAgain = useCallback(() => {
|
|
39825
39875
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39826
|
-
|
|
39876
|
+
setIsPlaying(false);
|
|
39827
39877
|
setEventLog([]);
|
|
39828
39878
|
}, []);
|
|
39829
39879
|
const handleReset = useCallback(() => {
|
|
39830
39880
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39831
|
-
|
|
39832
|
-
setObjects([...resetObjects]);
|
|
39833
|
-
setPlayState("editing");
|
|
39881
|
+
setIsPlaying(false);
|
|
39834
39882
|
setEventLog([]);
|
|
39835
|
-
setSelectedObjectId(
|
|
39836
|
-
|
|
39837
|
-
}, [
|
|
39883
|
+
setSelectedObjectId(objects[0] ? objId(objects[0]) : null);
|
|
39884
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
39885
|
+
}, [objects, playAgainEvent, emit]);
|
|
39838
39886
|
if (!resolved) return null;
|
|
39839
39887
|
const objectViewers = objects.map((obj) => {
|
|
39840
39888
|
const states = objStates(obj);
|
|
@@ -39903,12 +39951,12 @@ function EventHandlerBoard({
|
|
|
39903
39951
|
{
|
|
39904
39952
|
object: selectedObject,
|
|
39905
39953
|
onRulesChange: handleRulesChange,
|
|
39906
|
-
disabled:
|
|
39954
|
+
disabled: isPlaying
|
|
39907
39955
|
}
|
|
39908
39956
|
),
|
|
39909
39957
|
eventLog.length > 0 && /* @__PURE__ */ jsx(EventLog, { entries: eventLog }),
|
|
39910
|
-
|
|
39911
|
-
|
|
39958
|
+
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") }) }),
|
|
39959
|
+
!isPlaying && !isSuccess && attempts > 0 && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
39912
39960
|
/* @__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) }) }),
|
|
39913
39961
|
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: [
|
|
39914
39962
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
@@ -39916,12 +39964,12 @@ function EventHandlerBoard({
|
|
|
39916
39964
|
] }) })
|
|
39917
39965
|
] }),
|
|
39918
39966
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
|
|
39919
|
-
|
|
39967
|
+
!isPlaying && !isSuccess && attempts > 0 ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsx(
|
|
39920
39968
|
Button,
|
|
39921
39969
|
{
|
|
39922
39970
|
variant: "primary",
|
|
39923
39971
|
onClick: handlePlay,
|
|
39924
|
-
disabled:
|
|
39972
|
+
disabled: isPlaying || isSuccess,
|
|
39925
39973
|
children: "\u25B6 " + t("game.play")
|
|
39926
39974
|
}
|
|
39927
39975
|
),
|
|
@@ -42474,21 +42522,6 @@ var init_ModalSlot = __esm({
|
|
|
42474
42522
|
ModalSlot.displayName = "ModalSlot";
|
|
42475
42523
|
}
|
|
42476
42524
|
});
|
|
42477
|
-
function getOpponentAction(strategy, actions, history) {
|
|
42478
|
-
const actionIds = actions.map((a) => a.id);
|
|
42479
|
-
switch (strategy) {
|
|
42480
|
-
case "always-cooperate":
|
|
42481
|
-
return actionIds[0];
|
|
42482
|
-
case "always-defect":
|
|
42483
|
-
return actionIds[actionIds.length - 1];
|
|
42484
|
-
case "tit-for-tat":
|
|
42485
|
-
if (history.length === 0) return actionIds[0];
|
|
42486
|
-
return history[history.length - 1].playerAction;
|
|
42487
|
-
case "random":
|
|
42488
|
-
default:
|
|
42489
|
-
return actionIds[Math.floor(Math.random() * actionIds.length)];
|
|
42490
|
-
}
|
|
42491
|
-
}
|
|
42492
42525
|
function NegotiatorBoard({
|
|
42493
42526
|
entity,
|
|
42494
42527
|
completeEvent = "PUZZLE_COMPLETE",
|
|
@@ -42510,28 +42543,35 @@ function NegotiatorBoard({
|
|
|
42510
42543
|
const playerTotal = num(resolved?.score);
|
|
42511
42544
|
const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
|
|
42512
42545
|
const won = result === "win";
|
|
42513
|
-
const
|
|
42546
|
+
const lastPlayerAction = str(resolved?.lastPlayerAction);
|
|
42547
|
+
const lastOpponentAction = str(resolved?.lastOpponentAction);
|
|
42548
|
+
const lastPayoff = num(resolved?.lastPayoff);
|
|
42514
42549
|
const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
|
|
42515
42550
|
const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
|
|
42551
|
+
const prevRoundRef = useRef(currentRound);
|
|
42552
|
+
useEffect(() => {
|
|
42553
|
+
if (currentRound > prevRoundRef.current && lastPlayerAction) {
|
|
42554
|
+
const opponentPayoffEntry = payoffMatrix.find(
|
|
42555
|
+
(p2) => p2.playerAction === lastPlayerAction && p2.opponentAction === lastOpponentAction
|
|
42556
|
+
);
|
|
42557
|
+
setHistory((prev) => [
|
|
42558
|
+
...prev,
|
|
42559
|
+
{
|
|
42560
|
+
round: currentRound,
|
|
42561
|
+
playerAction: lastPlayerAction,
|
|
42562
|
+
opponentAction: lastOpponentAction,
|
|
42563
|
+
playerPayoff: lastPayoff,
|
|
42564
|
+
opponentPayoff: opponentPayoffEntry?.opponentPayoff ?? 0
|
|
42565
|
+
}
|
|
42566
|
+
]);
|
|
42567
|
+
}
|
|
42568
|
+
prevRoundRef.current = currentRound;
|
|
42569
|
+
}, [currentRound, lastPlayerAction, lastOpponentAction, lastPayoff, payoffMatrix]);
|
|
42570
|
+
const opponentTotal = useMemo(() => history.reduce((s, r) => s + r.opponentPayoff, 0), [history]);
|
|
42516
42571
|
const handleAction = useCallback((actionId) => {
|
|
42517
42572
|
if (isComplete) return;
|
|
42518
|
-
const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
|
|
42519
|
-
const payoff = payoffMatrix.find(
|
|
42520
|
-
(p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
|
|
42521
|
-
);
|
|
42522
|
-
const playerPayoff = payoff?.playerPayoff ?? 0;
|
|
42523
|
-
setHistory((prev) => [
|
|
42524
|
-
...prev,
|
|
42525
|
-
{
|
|
42526
|
-
round: prev.length + 1,
|
|
42527
|
-
playerAction: actionId,
|
|
42528
|
-
opponentAction,
|
|
42529
|
-
playerPayoff,
|
|
42530
|
-
opponentPayoff: payoff?.opponentPayoff ?? 0
|
|
42531
|
-
}
|
|
42532
|
-
]);
|
|
42533
42573
|
if (playRoundEvent) {
|
|
42534
|
-
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff:
|
|
42574
|
+
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: 0 });
|
|
42535
42575
|
}
|
|
42536
42576
|
if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
|
|
42537
42577
|
if (finishEvent) {
|
|
@@ -42541,10 +42581,11 @@ function NegotiatorBoard({
|
|
|
42541
42581
|
setShowHint(true);
|
|
42542
42582
|
}
|
|
42543
42583
|
}
|
|
42544
|
-
}, [isComplete, resolved, totalRounds, currentRound,
|
|
42584
|
+
}, [isComplete, resolved, totalRounds, currentRound, playRoundEvent, finishEvent, emit]);
|
|
42545
42585
|
const handleReset = useCallback(() => {
|
|
42546
42586
|
setHistory([]);
|
|
42547
42587
|
setShowHint(false);
|
|
42588
|
+
prevRoundRef.current = 0;
|
|
42548
42589
|
if (playAgainEvent) {
|
|
42549
42590
|
emit(`UI:${playAgainEvent}`, {});
|
|
42550
42591
|
}
|
|
@@ -44880,17 +44921,24 @@ function SequencerBoard({
|
|
|
44880
44921
|
const { emit } = useEventBus();
|
|
44881
44922
|
const { t } = useTranslate();
|
|
44882
44923
|
const resolved = boardEntity(entity);
|
|
44883
|
-
const maxSlots = num(resolved?.maxSlots);
|
|
44924
|
+
const maxSlots = num(resolved?.maxSlots) || 3;
|
|
44884
44925
|
const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
|
|
44885
44926
|
const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
|
|
44886
44927
|
const allowDuplicates = resolved?.allowDuplicates !== false;
|
|
44928
|
+
const entitySlots = Array.isArray(resolved?.slots) ? resolved.slots : [];
|
|
44929
|
+
const entityResult = str(resolved?.result);
|
|
44930
|
+
const entityAttempts = num(resolved?.attempts);
|
|
44931
|
+
const entityCurrentStep = typeof resolved?.currentStep === "number" ? resolved.currentStep : -1;
|
|
44932
|
+
const slots = Array.from({ length: maxSlots }, (_, i) => {
|
|
44933
|
+
const entitySlot = entitySlots.find((s) => s.index === i);
|
|
44934
|
+
if (!entitySlot?.placedActionId) return void 0;
|
|
44935
|
+
return availableActions.find((a) => a.id === entitySlot.placedActionId);
|
|
44936
|
+
});
|
|
44937
|
+
const isSuccess = entityResult === "win";
|
|
44938
|
+
const attempts = entityAttempts;
|
|
44939
|
+
const isPlayingBack = entityCurrentStep >= 0 && !isSuccess;
|
|
44940
|
+
const currentStep = entityCurrentStep;
|
|
44887
44941
|
const [headerError, setHeaderError] = useState(false);
|
|
44888
|
-
const [slots, setSlots] = useState(
|
|
44889
|
-
() => Array.from({ length: maxSlots }, () => void 0)
|
|
44890
|
-
);
|
|
44891
|
-
const [playState, setPlayState] = useState("idle");
|
|
44892
|
-
const [currentStep, setCurrentStep] = useState(-1);
|
|
44893
|
-
const [attempts, setAttempts] = useState(0);
|
|
44894
44942
|
const [slotFeedback, setSlotFeedback] = useState(
|
|
44895
44943
|
() => Array.from({ length: maxSlots }, () => null)
|
|
44896
44944
|
);
|
|
@@ -44899,11 +44947,6 @@ function SequencerBoard({
|
|
|
44899
44947
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
44900
44948
|
}, []);
|
|
44901
44949
|
const handleSlotDrop = useCallback((index, item) => {
|
|
44902
|
-
setSlots((prev) => {
|
|
44903
|
-
const next = [...prev];
|
|
44904
|
-
next[index] = item;
|
|
44905
|
-
return next;
|
|
44906
|
-
});
|
|
44907
44950
|
setSlotFeedback((prev) => {
|
|
44908
44951
|
const next = [...prev];
|
|
44909
44952
|
next[index] = null;
|
|
@@ -44913,11 +44956,6 @@ function SequencerBoard({
|
|
|
44913
44956
|
if (placeEvent) emit(`UI:${placeEvent}`, { slotIndex: index, actionId: item.id });
|
|
44914
44957
|
}, [emit, placeEvent]);
|
|
44915
44958
|
const handleSlotRemove = useCallback((index) => {
|
|
44916
|
-
setSlots((prev) => {
|
|
44917
|
-
const next = [...prev];
|
|
44918
|
-
next[index] = void 0;
|
|
44919
|
-
return next;
|
|
44920
|
-
});
|
|
44921
44959
|
setSlotFeedback((prev) => {
|
|
44922
44960
|
const next = [...prev];
|
|
44923
44961
|
next[index] = null;
|
|
@@ -44928,48 +44966,34 @@ function SequencerBoard({
|
|
|
44928
44966
|
}, [emit, removeEvent]);
|
|
44929
44967
|
const handleReset = useCallback(() => {
|
|
44930
44968
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
44931
|
-
setSlots(Array.from({ length: maxSlots }, () => void 0));
|
|
44932
|
-
setPlayState("idle");
|
|
44933
|
-
setCurrentStep(-1);
|
|
44934
|
-
setAttempts(0);
|
|
44935
44969
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
44936
44970
|
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
44937
44971
|
}, [maxSlots, playAgainEvent, emit]);
|
|
44938
44972
|
const filledSlots = slots.filter((s) => !!s);
|
|
44939
|
-
const canPlay = filledSlots.length > 0 &&
|
|
44973
|
+
const canPlay = filledSlots.length > 0 && !isPlayingBack && !isSuccess;
|
|
44940
44974
|
const handlePlay = useCallback(() => {
|
|
44941
44975
|
if (!canPlay) return;
|
|
44942
44976
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
44943
44977
|
emit("UI:PLAY_SOUND", { key: "confirm" });
|
|
44944
44978
|
const sequence = slots.map((s) => s?.id || "");
|
|
44945
|
-
|
|
44946
|
-
|
|
44947
|
-
}
|
|
44948
|
-
setPlayState("playing");
|
|
44949
|
-
setCurrentStep(0);
|
|
44979
|
+
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
44980
|
+
if (playEvent) emit(`UI:${playEvent}`, { sequence });
|
|
44950
44981
|
let step = 0;
|
|
44951
44982
|
const advance = () => {
|
|
44952
44983
|
step++;
|
|
44984
|
+
emit("UI:STEP", { step });
|
|
44953
44985
|
if (step >= maxSlots) {
|
|
44986
|
+
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
44954
44987
|
const playerSeq = slots.map((s) => s?.id);
|
|
44955
|
-
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
44956
44988
|
const success = solutions.some(
|
|
44957
44989
|
(sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
|
|
44958
44990
|
);
|
|
44959
|
-
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
44960
44991
|
if (success) {
|
|
44961
|
-
setPlayState("success");
|
|
44962
|
-
setCurrentStep(-1);
|
|
44963
44992
|
emit("UI:PLAY_SOUND", { key: "levelComplete" });
|
|
44964
|
-
if (completeEvent) {
|
|
44965
|
-
emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
44966
|
-
}
|
|
44993
|
+
if (completeEvent) emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
44967
44994
|
} else {
|
|
44968
|
-
setAttempts((prev) => prev + 1);
|
|
44969
44995
|
const feedback = computeSlotFeedback(playerSeq, solutions);
|
|
44970
44996
|
setSlotFeedback(feedback);
|
|
44971
|
-
setPlayState("idle");
|
|
44972
|
-
setCurrentStep(-1);
|
|
44973
44997
|
emit("UI:PLAY_SOUND", { key: "fail" });
|
|
44974
44998
|
const correctCount2 = feedback.filter((f3) => f3 === "correct").length;
|
|
44975
44999
|
for (let ci = 0; ci < correctCount2; ci++) {
|
|
@@ -44979,7 +45003,6 @@ function SequencerBoard({
|
|
|
44979
45003
|
}
|
|
44980
45004
|
}
|
|
44981
45005
|
} else {
|
|
44982
|
-
setCurrentStep(step);
|
|
44983
45006
|
timerRef.current = setTimeout(advance, stepDurationMs);
|
|
44984
45007
|
}
|
|
44985
45008
|
};
|
|
@@ -45029,7 +45052,7 @@ function SequencerBoard({
|
|
|
45029
45052
|
/* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
|
|
45030
45053
|
/* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between", children: [
|
|
45031
45054
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
|
|
45032
|
-
hasFeedback &&
|
|
45055
|
+
hasFeedback && !isPlayingBack && !isSuccess && /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
|
|
45033
45056
|
`${correctCount}/${maxSlots} `,
|
|
45034
45057
|
"\u2705"
|
|
45035
45058
|
] })
|
|
@@ -45041,7 +45064,7 @@ function SequencerBoard({
|
|
|
45041
45064
|
maxSlots,
|
|
45042
45065
|
onSlotDrop: handleSlotDrop,
|
|
45043
45066
|
onSlotRemove: handleSlotRemove,
|
|
45044
|
-
playing:
|
|
45067
|
+
playing: isPlayingBack,
|
|
45045
45068
|
currentStep,
|
|
45046
45069
|
categoryColors,
|
|
45047
45070
|
slotFeedback,
|
|
@@ -45049,7 +45072,7 @@ function SequencerBoard({
|
|
|
45049
45072
|
}
|
|
45050
45073
|
)
|
|
45051
45074
|
] }),
|
|
45052
|
-
|
|
45075
|
+
!isPlayingBack && /* @__PURE__ */ jsx(
|
|
45053
45076
|
ActionPalette,
|
|
45054
45077
|
{
|
|
45055
45078
|
actions: availableActions,
|
|
@@ -45059,8 +45082,8 @@ function SequencerBoard({
|
|
|
45059
45082
|
label: t("sequencer.dragActions")
|
|
45060
45083
|
}
|
|
45061
45084
|
),
|
|
45062
|
-
hasFeedback &&
|
|
45063
|
-
|
|
45085
|
+
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) }) }),
|
|
45086
|
+
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") }) }),
|
|
45064
45087
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
|
|
45065
45088
|
/* @__PURE__ */ jsx(
|
|
45066
45089
|
Button,
|
|
@@ -46110,6 +46133,9 @@ function StateArchitectBoard({
|
|
|
46110
46133
|
stepDurationMs = 600,
|
|
46111
46134
|
testEvent,
|
|
46112
46135
|
completeEvent,
|
|
46136
|
+
addTransitionEvent,
|
|
46137
|
+
removeTransitionEvent,
|
|
46138
|
+
playAgainEvent,
|
|
46113
46139
|
className
|
|
46114
46140
|
}) {
|
|
46115
46141
|
const { emit } = useEventBus();
|
|
@@ -46122,14 +46148,16 @@ function StateArchitectBoard({
|
|
|
46122
46148
|
const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
|
|
46123
46149
|
const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
|
|
46124
46150
|
const entityVariables = rows(resolved?.variables);
|
|
46125
|
-
const
|
|
46151
|
+
const transitions = entityTransitions;
|
|
46152
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
46153
|
+
const entityResult = str(resolved?.result) || "none";
|
|
46154
|
+
const isSuccess = entityResult === "win";
|
|
46155
|
+
const [isTesting, setIsTesting] = useState(false);
|
|
46126
46156
|
const [headerError, setHeaderError] = useState(false);
|
|
46127
|
-
const [playState, setPlayState] = useState("editing");
|
|
46128
46157
|
const [currentState, setCurrentState] = useState(initialState);
|
|
46129
46158
|
const [selectedState, setSelectedState] = useState(null);
|
|
46130
46159
|
const [testResults, setTestResults] = useState([]);
|
|
46131
46160
|
const [variables, setVariables] = useState(() => [...entityVariables]);
|
|
46132
|
-
const [attempts, setAttempts] = useState(0);
|
|
46133
46161
|
const timerRef = useRef(null);
|
|
46134
46162
|
const [addingFrom, setAddingFrom] = useState(null);
|
|
46135
46163
|
useEffect(() => () => {
|
|
@@ -46139,7 +46167,7 @@ function StateArchitectBoard({
|
|
|
46139
46167
|
const GRAPH_H = 400;
|
|
46140
46168
|
const positions = useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
|
|
46141
46169
|
const handleStateClick = useCallback((state) => {
|
|
46142
|
-
if (
|
|
46170
|
+
if (isTesting) return;
|
|
46143
46171
|
if (addingFrom) {
|
|
46144
46172
|
if (addingFrom !== state) {
|
|
46145
46173
|
const event = availableEvents[0] || "EVENT";
|
|
@@ -46149,20 +46177,20 @@ function StateArchitectBoard({
|
|
|
46149
46177
|
to: state,
|
|
46150
46178
|
event
|
|
46151
46179
|
};
|
|
46152
|
-
|
|
46180
|
+
if (addTransitionEvent) emit(`UI:${addTransitionEvent}`, { id: newTrans.id, from: newTrans.from, to: newTrans.to, event: newTrans.event });
|
|
46153
46181
|
}
|
|
46154
46182
|
setAddingFrom(null);
|
|
46155
46183
|
} else {
|
|
46156
46184
|
setSelectedState(state);
|
|
46157
46185
|
}
|
|
46158
|
-
}, [
|
|
46186
|
+
}, [isTesting, addingFrom, availableEvents, addTransitionEvent, emit]);
|
|
46159
46187
|
const handleStartAddTransition = useCallback(() => {
|
|
46160
46188
|
if (!selectedState) return;
|
|
46161
46189
|
setAddingFrom(selectedState);
|
|
46162
46190
|
}, [selectedState]);
|
|
46163
46191
|
const handleRemoveTransition = useCallback((transId) => {
|
|
46164
|
-
|
|
46165
|
-
}, []);
|
|
46192
|
+
if (removeTransitionEvent) emit(`UI:${removeTransitionEvent}`, { id: transId });
|
|
46193
|
+
}, [removeTransitionEvent, emit]);
|
|
46166
46194
|
const machine = useMemo(() => ({
|
|
46167
46195
|
name: entityName,
|
|
46168
46196
|
description: str(resolved?.description),
|
|
@@ -46176,16 +46204,16 @@ function StateArchitectBoard({
|
|
|
46176
46204
|
}))
|
|
46177
46205
|
}), [entityName, resolved, entityStates, currentState, transitions]);
|
|
46178
46206
|
const handleTest = useCallback(() => {
|
|
46179
|
-
if (
|
|
46207
|
+
if (isTesting) return;
|
|
46180
46208
|
if (testEvent) emit(`UI:${testEvent}`, {});
|
|
46181
|
-
|
|
46209
|
+
setIsTesting(true);
|
|
46182
46210
|
setTestResults([]);
|
|
46183
46211
|
const results = [];
|
|
46184
46212
|
let testIdx = 0;
|
|
46185
46213
|
const runNextTest = () => {
|
|
46186
46214
|
if (testIdx >= testCases.length) {
|
|
46187
46215
|
const allPassed = results.every((r) => r.passed);
|
|
46188
|
-
|
|
46216
|
+
setIsTesting(false);
|
|
46189
46217
|
setTestResults(results);
|
|
46190
46218
|
if (allPassed && completeEvent) {
|
|
46191
46219
|
emit(`UI:${completeEvent}`, {
|
|
@@ -46193,9 +46221,6 @@ function StateArchitectBoard({
|
|
|
46193
46221
|
passedTests: results.filter((r) => r.passed).length
|
|
46194
46222
|
});
|
|
46195
46223
|
}
|
|
46196
|
-
if (!allPassed) {
|
|
46197
|
-
setAttempts((prev) => prev + 1);
|
|
46198
|
-
}
|
|
46199
46224
|
return;
|
|
46200
46225
|
}
|
|
46201
46226
|
const testCase = testCases[testIdx];
|
|
@@ -46218,24 +46243,23 @@ function StateArchitectBoard({
|
|
|
46218
46243
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
46219
46244
|
};
|
|
46220
46245
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
46221
|
-
}, [
|
|
46246
|
+
}, [isTesting, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
|
|
46222
46247
|
const handleTryAgain = useCallback(() => {
|
|
46223
46248
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
46224
|
-
|
|
46249
|
+
setIsTesting(false);
|
|
46225
46250
|
setCurrentState(initialState);
|
|
46226
46251
|
setTestResults([]);
|
|
46227
46252
|
}, [initialState]);
|
|
46228
46253
|
const handleReset = useCallback(() => {
|
|
46229
46254
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
46230
|
-
|
|
46231
|
-
setPlayState("editing");
|
|
46255
|
+
setIsTesting(false);
|
|
46232
46256
|
setCurrentState(initialState);
|
|
46233
46257
|
setTestResults([]);
|
|
46234
46258
|
setVariables([...entityVariables]);
|
|
46235
46259
|
setSelectedState(null);
|
|
46236
46260
|
setAddingFrom(null);
|
|
46237
|
-
|
|
46238
|
-
}, [
|
|
46261
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
46262
|
+
}, [initialState, entityVariables, playAgainEvent, emit]);
|
|
46239
46263
|
const codeData = useMemo(() => ({
|
|
46240
46264
|
name: entityName,
|
|
46241
46265
|
states: entityStates,
|
|
@@ -46331,7 +46355,7 @@ function StateArchitectBoard({
|
|
|
46331
46355
|
]
|
|
46332
46356
|
}
|
|
46333
46357
|
),
|
|
46334
|
-
|
|
46358
|
+
!isTesting && /* @__PURE__ */ jsx(HStack, { gap: "sm", children: /* @__PURE__ */ jsx(
|
|
46335
46359
|
Button,
|
|
46336
46360
|
{
|
|
46337
46361
|
variant: "ghost",
|
|
@@ -46353,7 +46377,7 @@ function StateArchitectBoard({
|
|
|
46353
46377
|
t2.guardHint,
|
|
46354
46378
|
")"
|
|
46355
46379
|
] }),
|
|
46356
|
-
|
|
46380
|
+
!isTesting && /* @__PURE__ */ jsx(
|
|
46357
46381
|
Button,
|
|
46358
46382
|
{
|
|
46359
46383
|
variant: "ghost",
|
|
@@ -46385,21 +46409,21 @@ function StateArchitectBoard({
|
|
|
46385
46409
|
resolved.showCodeView !== false && /* @__PURE__ */ jsx(StateJsonView, { data: codeData, label: "View Code" })
|
|
46386
46410
|
] })
|
|
46387
46411
|
] }),
|
|
46388
|
-
|
|
46389
|
-
|
|
46412
|
+
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") }) }),
|
|
46413
|
+
!isTesting && !isSuccess && testResults.some((r) => !r.passed) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
46390
46414
|
/* @__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]) }) }),
|
|
46391
|
-
attempts >=
|
|
46415
|
+
!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: [
|
|
46392
46416
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
46393
46417
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
|
|
46394
46418
|
] }) })
|
|
46395
46419
|
] }),
|
|
46396
46420
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
|
|
46397
|
-
|
|
46421
|
+
!isTesting && !isSuccess && testResults.some((r) => !r.passed) ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsx(
|
|
46398
46422
|
Button,
|
|
46399
46423
|
{
|
|
46400
46424
|
variant: "primary",
|
|
46401
46425
|
onClick: handleTest,
|
|
46402
|
-
disabled:
|
|
46426
|
+
disabled: isTesting,
|
|
46403
46427
|
children: "\u25B6 " + t("game.runTests")
|
|
46404
46428
|
}
|
|
46405
46429
|
),
|
|
@@ -47103,17 +47127,12 @@ var init_UncontrolledBattleBoard = __esm({
|
|
|
47103
47127
|
}
|
|
47104
47128
|
});
|
|
47105
47129
|
function heroPosition(h) {
|
|
47130
|
+
if ("position" in h && h.position != null) return h.position;
|
|
47106
47131
|
return vec2(h.position);
|
|
47107
47132
|
}
|
|
47108
|
-
function heroOwner(h) {
|
|
47109
|
-
return str(h.owner);
|
|
47110
|
-
}
|
|
47111
47133
|
function heroMovement(h) {
|
|
47112
47134
|
return num(h.movement);
|
|
47113
47135
|
}
|
|
47114
|
-
function hexPassable(h) {
|
|
47115
|
-
return h.passable !== false;
|
|
47116
|
-
}
|
|
47117
47136
|
function defaultIsInRange(from, to, range) {
|
|
47118
47137
|
return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
|
|
47119
47138
|
}
|
|
@@ -47149,38 +47168,41 @@ function WorldMapBoard({
|
|
|
47149
47168
|
}) {
|
|
47150
47169
|
const eventBus = useEventBus();
|
|
47151
47170
|
const resolved = boardEntity(entity);
|
|
47152
|
-
const
|
|
47153
|
-
const
|
|
47171
|
+
const entityUnits = rows(resolved?.units);
|
|
47172
|
+
const entityTiles = rows(resolved?.tiles);
|
|
47154
47173
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
47155
47174
|
const selectedHeroId = resolved?.selectedHeroId ?? null;
|
|
47156
47175
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
47157
47176
|
const backgroundImage = resolved?.backgroundImage;
|
|
47158
47177
|
const [hoveredTile, setHoveredTile] = useState(null);
|
|
47159
|
-
const selectedHero = useMemo(
|
|
47160
|
-
() => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
|
|
47161
|
-
[heroes, selectedHeroId]
|
|
47162
|
-
);
|
|
47163
47178
|
const derivedTiles = useMemo(
|
|
47164
|
-
() =>
|
|
47165
|
-
x: num(
|
|
47166
|
-
y: num(
|
|
47167
|
-
terrain: str(
|
|
47168
|
-
terrainSprite:
|
|
47179
|
+
() => entityTiles.map((t) => ({
|
|
47180
|
+
x: num(t.x),
|
|
47181
|
+
y: num(t.y),
|
|
47182
|
+
terrain: str(t.terrain),
|
|
47183
|
+
terrainSprite: t.terrainSprite == null ? void 0 : str(t.terrainSprite),
|
|
47184
|
+
passable: t.passable !== false
|
|
47169
47185
|
})),
|
|
47170
|
-
[
|
|
47186
|
+
[entityTiles]
|
|
47171
47187
|
);
|
|
47172
47188
|
const tiles = propTiles ?? derivedTiles;
|
|
47173
47189
|
const baseUnits = useMemo(
|
|
47174
|
-
() => propUnits ??
|
|
47175
|
-
id: str(
|
|
47176
|
-
position: heroPosition(
|
|
47177
|
-
name: str(
|
|
47178
|
-
|
|
47179
|
-
|
|
47180
|
-
|
|
47181
|
-
|
|
47190
|
+
() => propUnits ?? entityUnits.map((u) => ({
|
|
47191
|
+
id: str(u.id),
|
|
47192
|
+
position: heroPosition(u),
|
|
47193
|
+
name: str(u.name),
|
|
47194
|
+
// lolo uses `team` field (not `owner`)
|
|
47195
|
+
team: str(u.team) === "enemy" ? "enemy" : "player",
|
|
47196
|
+
health: num(u.health) || 100,
|
|
47197
|
+
maxHealth: num(u.maxHealth) || 100,
|
|
47198
|
+
sprite: u.sprite == null ? void 0 : str(u.sprite)
|
|
47182
47199
|
})),
|
|
47183
|
-
[
|
|
47200
|
+
[entityUnits, propUnits]
|
|
47201
|
+
);
|
|
47202
|
+
const gameUnits = baseUnits;
|
|
47203
|
+
const selectedHero = useMemo(
|
|
47204
|
+
() => gameUnits.find((u) => u.id === selectedHeroId) ?? null,
|
|
47205
|
+
[gameUnits, selectedHeroId]
|
|
47184
47206
|
);
|
|
47185
47207
|
const MOVE_SPEED_MS_PER_TILE = 300;
|
|
47186
47208
|
const movementAnimRef = useRef(null);
|
|
@@ -47226,51 +47248,50 @@ function WorldMapBoard({
|
|
|
47226
47248
|
const validMoves = useMemo(() => {
|
|
47227
47249
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
47228
47250
|
const sp = heroPosition(selectedHero);
|
|
47229
|
-
const
|
|
47251
|
+
const sTeam = str(selectedHero.team);
|
|
47230
47252
|
const range = heroMovement(selectedHero);
|
|
47231
47253
|
const moves = [];
|
|
47232
|
-
|
|
47233
|
-
const
|
|
47234
|
-
const
|
|
47235
|
-
if (
|
|
47236
|
-
if (
|
|
47237
|
-
if (!isInRange(sp, { x:
|
|
47238
|
-
if (
|
|
47239
|
-
const
|
|
47240
|
-
return
|
|
47254
|
+
tiles.forEach((t) => {
|
|
47255
|
+
const tx = t.x;
|
|
47256
|
+
const ty = t.y;
|
|
47257
|
+
if (t.passable === false) return;
|
|
47258
|
+
if (tx === sp.x && ty === sp.y) return;
|
|
47259
|
+
if (!isInRange(sp, { x: tx, y: ty }, range)) return;
|
|
47260
|
+
if (gameUnits.some((u) => {
|
|
47261
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
47262
|
+
return up.x === tx && up.y === ty && str(u.team) === sTeam;
|
|
47241
47263
|
})) return;
|
|
47242
|
-
moves.push({ x:
|
|
47264
|
+
moves.push({ x: tx, y: ty });
|
|
47243
47265
|
});
|
|
47244
47266
|
return moves;
|
|
47245
|
-
}, [selectedHero,
|
|
47267
|
+
}, [selectedHero, tiles, gameUnits, isInRange]);
|
|
47246
47268
|
const attackTargets = useMemo(() => {
|
|
47247
47269
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
47248
47270
|
const sp = heroPosition(selectedHero);
|
|
47249
|
-
const
|
|
47271
|
+
const sTeam = str(selectedHero.team);
|
|
47250
47272
|
const range = heroMovement(selectedHero);
|
|
47251
|
-
return
|
|
47252
|
-
}, [selectedHero,
|
|
47253
|
-
const maxY = Math.max(...
|
|
47273
|
+
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 });
|
|
47274
|
+
}, [selectedHero, gameUnits, isInRange]);
|
|
47275
|
+
const maxY = Math.max(...tiles.map((t) => t.y), 0);
|
|
47254
47276
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
47255
47277
|
const tileToScreen = useCallback(
|
|
47256
47278
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
47257
47279
|
[scale, baseOffsetX]
|
|
47258
47280
|
);
|
|
47259
47281
|
const hoveredHex = useMemo(
|
|
47260
|
-
() => hoveredTile ?
|
|
47261
|
-
[hoveredTile,
|
|
47282
|
+
() => hoveredTile ? tiles.find((t) => t.x === hoveredTile.x && t.y === hoveredTile.y) ?? null : null,
|
|
47283
|
+
[hoveredTile, tiles]
|
|
47262
47284
|
);
|
|
47263
47285
|
const hoveredHero = useMemo(
|
|
47264
|
-
() => hoveredTile ?
|
|
47265
|
-
const
|
|
47266
|
-
return
|
|
47286
|
+
() => hoveredTile ? gameUnits.find((u) => {
|
|
47287
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
47288
|
+
return up.x === hoveredTile.x && up.y === hoveredTile.y;
|
|
47267
47289
|
}) ?? null : null,
|
|
47268
|
-
[hoveredTile,
|
|
47290
|
+
[hoveredTile, gameUnits]
|
|
47269
47291
|
);
|
|
47270
47292
|
const handleTileClick = useCallback((x, y) => {
|
|
47271
47293
|
if (movementAnimRef.current) return;
|
|
47272
|
-
const
|
|
47273
|
-
if (!hex) return;
|
|
47294
|
+
const tile = tiles.find((t) => t.x === x && t.y === y);
|
|
47274
47295
|
if (tileClickEvent) {
|
|
47275
47296
|
eventBus.emit(`UI:${tileClickEvent}`, { x, y });
|
|
47276
47297
|
}
|
|
@@ -47281,38 +47302,39 @@ function WorldMapBoard({
|
|
|
47281
47302
|
if (heroMoveEvent) {
|
|
47282
47303
|
eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
|
|
47283
47304
|
}
|
|
47284
|
-
const feature = str(
|
|
47305
|
+
const feature = tile ? str(tile.feature) : "";
|
|
47285
47306
|
if (feature && feature !== "none") {
|
|
47286
|
-
|
|
47307
|
+
const tileRow = tile;
|
|
47308
|
+
onFeatureEnter?.(heroId, tileRow);
|
|
47287
47309
|
if (featureEnterEvent) {
|
|
47288
|
-
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
|
|
47310
|
+
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex: tileRow });
|
|
47289
47311
|
}
|
|
47290
47312
|
}
|
|
47291
47313
|
});
|
|
47292
47314
|
return;
|
|
47293
47315
|
}
|
|
47294
|
-
const enemy =
|
|
47295
|
-
const
|
|
47296
|
-
return
|
|
47316
|
+
const enemy = gameUnits.find((u) => {
|
|
47317
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
47318
|
+
return up.x === x && up.y === y && str(u.team) === "enemy";
|
|
47297
47319
|
});
|
|
47298
47320
|
if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
|
|
47299
47321
|
const attackerId = str(selectedHero.id);
|
|
47300
|
-
const defenderId =
|
|
47322
|
+
const defenderId = enemy.id;
|
|
47301
47323
|
onBattleEncounter?.(attackerId, defenderId);
|
|
47302
47324
|
if (battleEncounterEvent) {
|
|
47303
47325
|
eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
|
|
47304
47326
|
}
|
|
47305
47327
|
}
|
|
47306
|
-
}, [
|
|
47328
|
+
}, [tiles, gameUnits, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
|
|
47307
47329
|
const handleUnitClick = useCallback((unitId) => {
|
|
47308
|
-
const
|
|
47309
|
-
if (
|
|
47330
|
+
const unit = gameUnits.find((u) => u.id === unitId);
|
|
47331
|
+
if (unit && (str(unit.team) === "player" || allowMoveAllHeroes)) {
|
|
47310
47332
|
onHeroSelect?.(unitId);
|
|
47311
47333
|
if (heroSelectEvent) {
|
|
47312
47334
|
eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
|
|
47313
47335
|
}
|
|
47314
47336
|
}
|
|
47315
|
-
}, [
|
|
47337
|
+
}, [gameUnits, onHeroSelect, allowMoveAllHeroes, eventBus, heroSelectEvent]);
|
|
47316
47338
|
const selectHero = useCallback((id) => {
|
|
47317
47339
|
onHeroSelect?.(id);
|
|
47318
47340
|
if (heroSelectEvent) {
|