@almadar/ui 5.47.0 → 5.49.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 +2998 -443
- package/dist/avl/index.css +504 -0
- package/dist/avl/index.js +2998 -444
- package/dist/components/core/templates/index.d.ts +3 -0
- package/dist/components/game/atoms/ActionButton.d.ts +4 -1
- package/dist/components/game/atoms/ComboCounter.d.ts +4 -1
- package/dist/components/game/atoms/ControlButton.d.ts +4 -2
- package/dist/components/game/atoms/DamageNumber.d.ts +4 -1
- package/dist/components/game/atoms/ResourceCounter.d.ts +4 -1
- package/dist/components/game/atoms/ScoreDisplay.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/TurnIndicator.d.ts +4 -1
- package/dist/components/game/atoms/WaypointMarker.d.ts +4 -1
- package/dist/components/game/molecules/EnemyPlate.d.ts +4 -1
- package/dist/components/game/molecules/StatBadge.d.ts +4 -1
- package/dist/components/game/molecules/three/index.cjs +1428 -346
- package/dist/components/game/molecules/three/index.css +0 -5
- package/dist/components/game/molecules/three/index.js +1429 -347
- package/dist/components/game/organisms/CastleBoard.d.ts +10 -1
- package/dist/components/game/organisms/GameBoard3D.d.ts +62 -0
- package/dist/components/game/organisms/PlatformerBoard.d.ts +51 -0
- package/dist/components/game/organisms/RoguelikeBoard.d.ts +59 -0
- package/dist/components/game/organisms/TowerDefenseBoard.d.ts +64 -0
- package/dist/components/game/organisms/index.d.ts +4 -0
- 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/game/templates/GameCanvas3DBattleTemplate.d.ts +22 -35
- package/dist/components/game/templates/GameCanvas3DCastleTemplate.d.ts +23 -23
- package/dist/components/game/templates/GameCanvas3DWorldMapTemplate.d.ts +27 -29
- package/dist/components/game/templates/PlatformerTemplate.d.ts +26 -0
- package/dist/components/game/templates/RoguelikeTemplate.d.ts +23 -0
- package/dist/components/game/templates/TowerDefenseTemplate.d.ts +43 -0
- package/dist/components/index.cjs +2991 -422
- package/dist/components/index.css +504 -0
- package/dist/components/index.js +2985 -424
- package/dist/docs/index.css +504 -0
- package/dist/providers/index.cjs +2981 -426
- package/dist/providers/index.css +504 -0
- package/dist/providers/index.js +2981 -427
- package/dist/runtime/index.cjs +2982 -427
- package/dist/runtime/index.css +504 -0
- package/dist/runtime/index.js +2982 -428
- package/package.json +1 -1
- package/tailwind-preset.cjs +1 -0
package/dist/avl/index.cjs
CHANGED
|
@@ -39,6 +39,12 @@ var core = require('@almadar/core');
|
|
|
39
39
|
var core$1 = require('@dnd-kit/core');
|
|
40
40
|
var sortable = require('@dnd-kit/sortable');
|
|
41
41
|
var utilities = require('@dnd-kit/utilities');
|
|
42
|
+
var THREE = require('three');
|
|
43
|
+
var GLTFLoader_js = require('three/examples/jsm/loaders/GLTFLoader.js');
|
|
44
|
+
var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
|
|
45
|
+
var GLTFLoader = require('three/examples/jsm/loaders/GLTFLoader');
|
|
46
|
+
var fiber = require('@react-three/fiber');
|
|
47
|
+
var drei = require('@react-three/drei');
|
|
42
48
|
var patterns = require('@almadar/patterns');
|
|
43
49
|
var OrbitalServerRuntime = require('@almadar/runtime/OrbitalServerRuntime');
|
|
44
50
|
var runtime = require('@almadar/runtime');
|
|
@@ -88,6 +94,7 @@ var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
|
|
|
88
94
|
var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
|
|
89
95
|
var remarkMath__default = /*#__PURE__*/_interopDefault(remarkMath);
|
|
90
96
|
var rehypeKatex__default = /*#__PURE__*/_interopDefault(rehypeKatex);
|
|
97
|
+
var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
|
|
91
98
|
|
|
92
99
|
var __defProp = Object.defineProperty;
|
|
93
100
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -3219,8 +3226,8 @@ var init_Typography = __esm({
|
|
|
3219
3226
|
children
|
|
3220
3227
|
}) => {
|
|
3221
3228
|
const variant = variantProp ?? (level ? `h${level}` : "body1");
|
|
3222
|
-
const
|
|
3223
|
-
const Comp =
|
|
3229
|
+
const Component2 = as || defaultElements[variant];
|
|
3230
|
+
const Comp = Component2;
|
|
3224
3231
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3225
3232
|
Comp,
|
|
3226
3233
|
{
|
|
@@ -3529,7 +3536,7 @@ var init_Box = __esm({
|
|
|
3529
3536
|
position,
|
|
3530
3537
|
className,
|
|
3531
3538
|
children,
|
|
3532
|
-
as:
|
|
3539
|
+
as: Component2 = "div",
|
|
3533
3540
|
action,
|
|
3534
3541
|
actionPayload,
|
|
3535
3542
|
hoverEvent,
|
|
@@ -3561,7 +3568,7 @@ var init_Box = __esm({
|
|
|
3561
3568
|
}, [hoverEvent, eventBus, onMouseLeave]);
|
|
3562
3569
|
const isClickable = action || onClick;
|
|
3563
3570
|
return React91__namespace.default.createElement(
|
|
3564
|
-
|
|
3571
|
+
Component2,
|
|
3565
3572
|
{
|
|
3566
3573
|
ref,
|
|
3567
3574
|
className: cn(
|
|
@@ -3637,7 +3644,7 @@ var init_Stack = __esm({
|
|
|
3637
3644
|
className,
|
|
3638
3645
|
style,
|
|
3639
3646
|
children,
|
|
3640
|
-
as:
|
|
3647
|
+
as: Component2 = "div",
|
|
3641
3648
|
onClick,
|
|
3642
3649
|
onKeyDown,
|
|
3643
3650
|
role,
|
|
@@ -3655,7 +3662,7 @@ var init_Stack = __esm({
|
|
|
3655
3662
|
};
|
|
3656
3663
|
const isHorizontal = direction === "horizontal";
|
|
3657
3664
|
const directionClass = responsive && isHorizontal ? reverse ? "flex-col-reverse md:flex-row-reverse" : "flex-col md:flex-row" : isHorizontal ? reverse ? "flex-row-reverse" : "flex-row" : reverse ? "flex-col-reverse" : "flex-col";
|
|
3658
|
-
const Comp =
|
|
3665
|
+
const Comp = Component2;
|
|
3659
3666
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3660
3667
|
Comp,
|
|
3661
3668
|
{
|
|
@@ -4569,9 +4576,9 @@ function resolvePhosphor(name, weight, family) {
|
|
|
4569
4576
|
(lib) => {
|
|
4570
4577
|
const PhosphorComp = lib[target];
|
|
4571
4578
|
if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
|
|
4572
|
-
const
|
|
4579
|
+
const Component2 = PhosphorComp;
|
|
4573
4580
|
const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4574
|
-
|
|
4581
|
+
Component2,
|
|
4575
4582
|
{
|
|
4576
4583
|
weight,
|
|
4577
4584
|
className: props.className,
|
|
@@ -4595,9 +4602,9 @@ function resolveTabler(name, family) {
|
|
|
4595
4602
|
(lib) => {
|
|
4596
4603
|
const TablerComp = lib[target];
|
|
4597
4604
|
if (!TablerComp || typeof TablerComp !== "object") return null;
|
|
4598
|
-
const
|
|
4605
|
+
const Component2 = TablerComp;
|
|
4599
4606
|
const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4600
|
-
|
|
4607
|
+
Component2,
|
|
4601
4608
|
{
|
|
4602
4609
|
stroke: props.strokeWidth ?? 1.5,
|
|
4603
4610
|
className: props.className,
|
|
@@ -4621,9 +4628,9 @@ function resolveFa(name, family) {
|
|
|
4621
4628
|
(lib) => {
|
|
4622
4629
|
const FaComp = lib[target];
|
|
4623
4630
|
if (!FaComp || typeof FaComp !== "function") return null;
|
|
4624
|
-
const
|
|
4631
|
+
const Component2 = FaComp;
|
|
4625
4632
|
const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4626
|
-
|
|
4633
|
+
Component2,
|
|
4627
4634
|
{
|
|
4628
4635
|
className: props.className,
|
|
4629
4636
|
style: props.style,
|
|
@@ -8015,10 +8022,10 @@ var init_Center = __esm({
|
|
|
8015
8022
|
className,
|
|
8016
8023
|
style,
|
|
8017
8024
|
children,
|
|
8018
|
-
as:
|
|
8025
|
+
as: Component2 = "div"
|
|
8019
8026
|
}) => {
|
|
8020
8027
|
const mergedStyle = minHeight ? { minHeight, ...style } : style;
|
|
8021
|
-
const Comp =
|
|
8028
|
+
const Comp = Component2;
|
|
8022
8029
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8023
8030
|
Comp,
|
|
8024
8031
|
{
|
|
@@ -10812,6 +10819,7 @@ var init_HealthBar = __esm({
|
|
|
10812
10819
|
}
|
|
10813
10820
|
});
|
|
10814
10821
|
function ScoreDisplay({
|
|
10822
|
+
assetUrl = DEFAULT_ASSET_URL,
|
|
10815
10823
|
value,
|
|
10816
10824
|
label,
|
|
10817
10825
|
icon,
|
|
@@ -10858,14 +10866,24 @@ function ScoreDisplay({
|
|
|
10858
10866
|
className
|
|
10859
10867
|
),
|
|
10860
10868
|
children: [
|
|
10861
|
-
|
|
10869
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
10870
|
+
"img",
|
|
10871
|
+
{
|
|
10872
|
+
src: assetUrl,
|
|
10873
|
+
alt: "",
|
|
10874
|
+
width: 20,
|
|
10875
|
+
height: 20,
|
|
10876
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
10877
|
+
className: "flex-shrink-0"
|
|
10878
|
+
}
|
|
10879
|
+
) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon }) }) : null,
|
|
10862
10880
|
label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: label }),
|
|
10863
10881
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: formattedValue })
|
|
10864
10882
|
]
|
|
10865
10883
|
}
|
|
10866
10884
|
);
|
|
10867
10885
|
}
|
|
10868
|
-
var sizeMap2;
|
|
10886
|
+
var sizeMap2, DEFAULT_ASSET_URL;
|
|
10869
10887
|
var init_ScoreDisplay = __esm({
|
|
10870
10888
|
"components/game/atoms/ScoreDisplay.tsx"() {
|
|
10871
10889
|
"use client";
|
|
@@ -10877,10 +10895,12 @@ var init_ScoreDisplay = __esm({
|
|
|
10877
10895
|
lg: "text-2xl",
|
|
10878
10896
|
xl: "text-4xl"
|
|
10879
10897
|
};
|
|
10898
|
+
DEFAULT_ASSET_URL = "https://almadar-kflow-assets.web.app/shared/effects/particles/star_01.png";
|
|
10880
10899
|
ScoreDisplay.displayName = "ScoreDisplay";
|
|
10881
10900
|
}
|
|
10882
10901
|
});
|
|
10883
10902
|
function ControlButton({
|
|
10903
|
+
assetUrl = DEFAULT_ASSET_URL2,
|
|
10884
10904
|
label,
|
|
10885
10905
|
icon,
|
|
10886
10906
|
size = "md",
|
|
@@ -10927,7 +10947,7 @@ function ControlButton({
|
|
|
10927
10947
|
},
|
|
10928
10948
|
[isPressed, releaseEvent, eventBus, onRelease]
|
|
10929
10949
|
);
|
|
10930
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
10950
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10931
10951
|
"button",
|
|
10932
10952
|
{
|
|
10933
10953
|
type: "button",
|
|
@@ -10948,20 +10968,27 @@ function ControlButton({
|
|
|
10948
10968
|
disabled && "opacity-50 cursor-not-allowed",
|
|
10949
10969
|
className
|
|
10950
10970
|
),
|
|
10951
|
-
children:
|
|
10952
|
-
|
|
10953
|
-
|
|
10954
|
-
|
|
10955
|
-
|
|
10956
|
-
|
|
10957
|
-
|
|
10958
|
-
|
|
10959
|
-
|
|
10960
|
-
|
|
10971
|
+
children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
10972
|
+
"img",
|
|
10973
|
+
{
|
|
10974
|
+
src: assetUrl,
|
|
10975
|
+
alt: "",
|
|
10976
|
+
width: 24,
|
|
10977
|
+
height: 24,
|
|
10978
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
10979
|
+
className: "flex-shrink-0"
|
|
10980
|
+
}
|
|
10981
|
+
) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-2xl", children: typeof icon === "string" ? /^[a-zA-Z0-9-]+$/.test(icon) ? (() => {
|
|
10982
|
+
const I = resolveIcon(icon);
|
|
10983
|
+
return I ? /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-6 h-6" }) : null;
|
|
10984
|
+
})() : icon : /* @__PURE__ */ (() => {
|
|
10985
|
+
const I = icon;
|
|
10986
|
+
return /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-6 h-6" });
|
|
10987
|
+
})() }) : label ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: label }) : null
|
|
10961
10988
|
}
|
|
10962
10989
|
);
|
|
10963
10990
|
}
|
|
10964
|
-
var sizeMap3, shapeMap, variantMap;
|
|
10991
|
+
var sizeMap3, shapeMap, variantMap, DEFAULT_ASSET_URL2;
|
|
10965
10992
|
var init_ControlButton = __esm({
|
|
10966
10993
|
"components/game/atoms/ControlButton.tsx"() {
|
|
10967
10994
|
"use client";
|
|
@@ -10984,6 +11011,7 @@ var init_ControlButton = __esm({
|
|
|
10984
11011
|
secondary: "bg-secondary text-secondary-foreground border-border hover:bg-secondary-hover",
|
|
10985
11012
|
ghost: "bg-transparent text-foreground border-border hover:bg-muted"
|
|
10986
11013
|
};
|
|
11014
|
+
DEFAULT_ASSET_URL2 = "https://almadar-kflow-assets.web.app/shared/effects/particles/circle_01.png";
|
|
10987
11015
|
ControlButton.displayName = "ControlButton";
|
|
10988
11016
|
}
|
|
10989
11017
|
});
|
|
@@ -11063,6 +11091,7 @@ var init_Sprite = __esm({
|
|
|
11063
11091
|
}
|
|
11064
11092
|
});
|
|
11065
11093
|
function StateIndicator({
|
|
11094
|
+
assetUrl = DEFAULT_ASSET_URL3,
|
|
11066
11095
|
state = "idle",
|
|
11067
11096
|
label,
|
|
11068
11097
|
size = "md",
|
|
@@ -11085,18 +11114,29 @@ function StateIndicator({
|
|
|
11085
11114
|
className
|
|
11086
11115
|
),
|
|
11087
11116
|
children: [
|
|
11088
|
-
/* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", children:
|
|
11117
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
11118
|
+
"img",
|
|
11119
|
+
{
|
|
11120
|
+
src: assetUrl,
|
|
11121
|
+
alt: displayLabel,
|
|
11122
|
+
width: 16,
|
|
11123
|
+
height: 16,
|
|
11124
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11125
|
+
className: "flex-shrink-0"
|
|
11126
|
+
}
|
|
11127
|
+
) : typeof config.icon === "string" ? /^[a-zA-Z0-9-]+$/.test(config.icon) ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: config.icon }) : config.icon : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: config.icon }) }),
|
|
11089
11128
|
/* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", children: displayLabel })
|
|
11090
11129
|
]
|
|
11091
11130
|
}
|
|
11092
11131
|
);
|
|
11093
11132
|
}
|
|
11094
|
-
var DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
11133
|
+
var DEFAULT_ASSET_URL3, DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
11095
11134
|
var init_StateIndicator = __esm({
|
|
11096
11135
|
"components/game/atoms/StateIndicator.tsx"() {
|
|
11097
11136
|
init_Box();
|
|
11098
11137
|
init_Icon();
|
|
11099
11138
|
init_cn();
|
|
11139
|
+
DEFAULT_ASSET_URL3 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
11100
11140
|
DEFAULT_STATE_STYLES = {
|
|
11101
11141
|
idle: { icon: "\u23F8", bgClass: "bg-muted" },
|
|
11102
11142
|
active: { icon: "\u25B6", bgClass: "bg-success" },
|
|
@@ -11171,6 +11211,7 @@ var init_TimerDisplay = __esm({
|
|
|
11171
11211
|
}
|
|
11172
11212
|
});
|
|
11173
11213
|
function ResourceCounter({
|
|
11214
|
+
assetUrl = DEFAULT_ASSET_URL4,
|
|
11174
11215
|
icon,
|
|
11175
11216
|
label = "Gold",
|
|
11176
11217
|
value = 250,
|
|
@@ -11190,7 +11231,17 @@ function ResourceCounter({
|
|
|
11190
11231
|
className
|
|
11191
11232
|
),
|
|
11192
11233
|
children: [
|
|
11193
|
-
|
|
11234
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
11235
|
+
"img",
|
|
11236
|
+
{
|
|
11237
|
+
src: assetUrl,
|
|
11238
|
+
alt: label,
|
|
11239
|
+
width: sizes.img,
|
|
11240
|
+
height: sizes.img,
|
|
11241
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11242
|
+
className: "flex-shrink-0"
|
|
11243
|
+
}
|
|
11244
|
+
) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon }) }) : null,
|
|
11194
11245
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: label }),
|
|
11195
11246
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("font-bold tabular-nums", color && (color in colorTokenClasses2 ? colorTokenClasses2[color] : color)), children: [
|
|
11196
11247
|
value,
|
|
@@ -11203,7 +11254,7 @@ function ResourceCounter({
|
|
|
11203
11254
|
}
|
|
11204
11255
|
);
|
|
11205
11256
|
}
|
|
11206
|
-
var colorTokenClasses2, sizeMap5;
|
|
11257
|
+
var colorTokenClasses2, DEFAULT_ASSET_URL4, sizeMap5;
|
|
11207
11258
|
var init_ResourceCounter = __esm({
|
|
11208
11259
|
"components/game/atoms/ResourceCounter.tsx"() {
|
|
11209
11260
|
init_cn();
|
|
@@ -11216,16 +11267,17 @@ var init_ResourceCounter = __esm({
|
|
|
11216
11267
|
error: "text-error",
|
|
11217
11268
|
muted: "text-muted-foreground"
|
|
11218
11269
|
};
|
|
11270
|
+
DEFAULT_ASSET_URL4 = "https://almadar-kflow-assets.web.app/shared/world-map/gold_mine.png";
|
|
11219
11271
|
sizeMap5 = {
|
|
11220
|
-
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm" },
|
|
11221
|
-
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base" },
|
|
11222
|
-
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg" }
|
|
11272
|
+
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm", img: 16 },
|
|
11273
|
+
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base", img: 20 },
|
|
11274
|
+
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg", img: 28 }
|
|
11223
11275
|
};
|
|
11224
11276
|
ResourceCounter.displayName = "ResourceCounter";
|
|
11225
11277
|
}
|
|
11226
11278
|
});
|
|
11227
11279
|
function ItemSlot({
|
|
11228
|
-
assetUrl =
|
|
11280
|
+
assetUrl = DEFAULT_ASSET_URL5,
|
|
11229
11281
|
icon = "sword",
|
|
11230
11282
|
label = "Iron Sword",
|
|
11231
11283
|
quantity,
|
|
@@ -11284,7 +11336,7 @@ function ItemSlot({
|
|
|
11284
11336
|
}
|
|
11285
11337
|
);
|
|
11286
11338
|
}
|
|
11287
|
-
var sizeMap6, rarityBorderMap, rarityGlowMap,
|
|
11339
|
+
var sizeMap6, rarityBorderMap, rarityGlowMap, DEFAULT_ASSET_URL5, assetSizeMap;
|
|
11288
11340
|
var init_ItemSlot = __esm({
|
|
11289
11341
|
"components/game/atoms/ItemSlot.tsx"() {
|
|
11290
11342
|
"use client";
|
|
@@ -11309,7 +11361,7 @@ var init_ItemSlot = __esm({
|
|
|
11309
11361
|
epic: "shadow-lg",
|
|
11310
11362
|
legendary: "shadow-lg"
|
|
11311
11363
|
};
|
|
11312
|
-
|
|
11364
|
+
DEFAULT_ASSET_URL5 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
11313
11365
|
assetSizeMap = {
|
|
11314
11366
|
sm: 28,
|
|
11315
11367
|
md: 40,
|
|
@@ -11319,6 +11371,7 @@ var init_ItemSlot = __esm({
|
|
|
11319
11371
|
}
|
|
11320
11372
|
});
|
|
11321
11373
|
function TurnIndicator({
|
|
11374
|
+
assetUrl = DEFAULT_ASSET_URL6,
|
|
11322
11375
|
currentTurn = 1,
|
|
11323
11376
|
maxTurns,
|
|
11324
11377
|
activeTeam,
|
|
@@ -11351,14 +11404,24 @@ function TurnIndicator({
|
|
|
11351
11404
|
] }),
|
|
11352
11405
|
activeTeam && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
11353
11406
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "|" }),
|
|
11354
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11407
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
11408
|
+
"img",
|
|
11409
|
+
{
|
|
11410
|
+
src: assetUrl,
|
|
11411
|
+
alt: "",
|
|
11412
|
+
width: 12,
|
|
11413
|
+
height: 12,
|
|
11414
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11415
|
+
className: "flex-shrink-0"
|
|
11416
|
+
}
|
|
11417
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("rounded-full bg-success/20", sizes.dot) }),
|
|
11355
11418
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-success", children: activeTeam })
|
|
11356
11419
|
] })
|
|
11357
11420
|
]
|
|
11358
11421
|
}
|
|
11359
11422
|
);
|
|
11360
11423
|
}
|
|
11361
|
-
var sizeMap7;
|
|
11424
|
+
var sizeMap7, DEFAULT_ASSET_URL6;
|
|
11362
11425
|
var init_TurnIndicator = __esm({
|
|
11363
11426
|
"components/game/atoms/TurnIndicator.tsx"() {
|
|
11364
11427
|
init_cn();
|
|
@@ -11367,6 +11430,7 @@ var init_TurnIndicator = __esm({
|
|
|
11367
11430
|
md: { wrapper: "text-sm gap-2 px-3 py-1", dot: "w-2 h-2" },
|
|
11368
11431
|
lg: { wrapper: "text-base gap-2.5 px-4 py-1.5", dot: "w-2.5 h-2.5" }
|
|
11369
11432
|
};
|
|
11433
|
+
DEFAULT_ASSET_URL6 = "https://almadar-kflow-assets.web.app/shared/effects/particles/symbol_01.png";
|
|
11370
11434
|
TurnIndicator.displayName = "TurnIndicator";
|
|
11371
11435
|
}
|
|
11372
11436
|
});
|
|
@@ -11382,6 +11446,7 @@ function getComboScale(combo) {
|
|
|
11382
11446
|
return "";
|
|
11383
11447
|
}
|
|
11384
11448
|
function ComboCounter({
|
|
11449
|
+
assetUrl = DEFAULT_ASSET_URL7,
|
|
11385
11450
|
combo = 5,
|
|
11386
11451
|
multiplier,
|
|
11387
11452
|
streak,
|
|
@@ -11401,6 +11466,17 @@ function ComboCounter({
|
|
|
11401
11466
|
className
|
|
11402
11467
|
),
|
|
11403
11468
|
children: [
|
|
11469
|
+
assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11470
|
+
"img",
|
|
11471
|
+
{
|
|
11472
|
+
src: assetUrl,
|
|
11473
|
+
alt: "combo",
|
|
11474
|
+
width: 24,
|
|
11475
|
+
height: 24,
|
|
11476
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11477
|
+
className: "flex-shrink-0 mb-0.5"
|
|
11478
|
+
}
|
|
11479
|
+
),
|
|
11404
11480
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-black tabular-nums leading-none", sizes.combo, getComboIntensity(combo)), children: combo }),
|
|
11405
11481
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-bold uppercase tracking-wider text-muted-foreground", sizes.label), children: "combo" }),
|
|
11406
11482
|
multiplier != null && multiplier > 1 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("font-bold text-warning tabular-nums", sizes.multiplier), children: [
|
|
@@ -11415,10 +11491,11 @@ function ComboCounter({
|
|
|
11415
11491
|
}
|
|
11416
11492
|
);
|
|
11417
11493
|
}
|
|
11418
|
-
var sizeMap8;
|
|
11494
|
+
var DEFAULT_ASSET_URL7, sizeMap8;
|
|
11419
11495
|
var init_ComboCounter = __esm({
|
|
11420
11496
|
"components/game/atoms/ComboCounter.tsx"() {
|
|
11421
11497
|
init_cn();
|
|
11498
|
+
DEFAULT_ASSET_URL7 = "https://almadar-kflow-assets.web.app/shared/effects/flash/flash00.png";
|
|
11422
11499
|
sizeMap8 = {
|
|
11423
11500
|
sm: { combo: "text-lg", label: "text-xs", multiplier: "text-xs" },
|
|
11424
11501
|
md: { combo: "text-2xl", label: "text-xs", multiplier: "text-sm" },
|
|
@@ -11512,6 +11589,7 @@ var init_XPBar = __esm({
|
|
|
11512
11589
|
}
|
|
11513
11590
|
});
|
|
11514
11591
|
function WaypointMarker({
|
|
11592
|
+
assetUrl = DEFAULT_ASSET_URL8,
|
|
11515
11593
|
label,
|
|
11516
11594
|
icon,
|
|
11517
11595
|
active = true,
|
|
@@ -11550,7 +11628,17 @@ function WaypointMarker({
|
|
|
11550
11628
|
active && !completed && "bg-info text-foreground",
|
|
11551
11629
|
!active && !completed && "bg-muted"
|
|
11552
11630
|
),
|
|
11553
|
-
children: completed ? checkIcon :
|
|
11631
|
+
children: completed ? checkIcon : assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
11632
|
+
"img",
|
|
11633
|
+
{
|
|
11634
|
+
src: assetUrl,
|
|
11635
|
+
alt: label,
|
|
11636
|
+
width: sizes.img,
|
|
11637
|
+
height: sizes.img,
|
|
11638
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11639
|
+
className: "flex-shrink-0"
|
|
11640
|
+
}
|
|
11641
|
+
) : icon ? typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon }) : null
|
|
11554
11642
|
}
|
|
11555
11643
|
)
|
|
11556
11644
|
] }),
|
|
@@ -11567,15 +11655,16 @@ function WaypointMarker({
|
|
|
11567
11655
|
)
|
|
11568
11656
|
] });
|
|
11569
11657
|
}
|
|
11570
|
-
var sizeMap10, checkIcon;
|
|
11658
|
+
var DEFAULT_ASSET_URL8, sizeMap10, checkIcon;
|
|
11571
11659
|
var init_WaypointMarker = __esm({
|
|
11572
11660
|
"components/game/atoms/WaypointMarker.tsx"() {
|
|
11573
11661
|
init_cn();
|
|
11574
11662
|
init_Icon();
|
|
11663
|
+
DEFAULT_ASSET_URL8 = "https://almadar-kflow-assets.web.app/shared/world-map/battle_marker.png";
|
|
11575
11664
|
sizeMap10 = {
|
|
11576
|
-
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1" },
|
|
11577
|
-
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5" },
|
|
11578
|
-
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2" }
|
|
11665
|
+
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1", img: 12 },
|
|
11666
|
+
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5", img: 18 },
|
|
11667
|
+
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2", img: 24 }
|
|
11579
11668
|
};
|
|
11580
11669
|
checkIcon = /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 3, className: "w-3/5 h-3/5", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 13l4 4L19 7", strokeLinecap: "round", strokeLinejoin: "round" }) });
|
|
11581
11670
|
WaypointMarker.displayName = "WaypointMarker";
|
|
@@ -11588,6 +11677,7 @@ function formatDuration(seconds) {
|
|
|
11588
11677
|
return `${Math.round(seconds)}s`;
|
|
11589
11678
|
}
|
|
11590
11679
|
function StatusEffect({
|
|
11680
|
+
assetUrl = DEFAULT_ASSET_URL9,
|
|
11591
11681
|
icon = "shield",
|
|
11592
11682
|
label = "Shield",
|
|
11593
11683
|
duration = 30,
|
|
@@ -11608,7 +11698,17 @@ function StatusEffect({
|
|
|
11608
11698
|
),
|
|
11609
11699
|
title: label,
|
|
11610
11700
|
children: [
|
|
11611
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children:
|
|
11701
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
11702
|
+
"img",
|
|
11703
|
+
{
|
|
11704
|
+
src: assetUrl,
|
|
11705
|
+
alt: label,
|
|
11706
|
+
width: sizes.img,
|
|
11707
|
+
height: sizes.img,
|
|
11708
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11709
|
+
className: "flex-shrink-0"
|
|
11710
|
+
}
|
|
11711
|
+
) : typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, size: "sm" }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, size: "sm" }) }),
|
|
11612
11712
|
duration !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11613
11713
|
"span",
|
|
11614
11714
|
{
|
|
@@ -11635,15 +11735,16 @@ function StatusEffect({
|
|
|
11635
11735
|
label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground mt-0.5 text-center whitespace-nowrap", children: label })
|
|
11636
11736
|
] });
|
|
11637
11737
|
}
|
|
11638
|
-
var sizeMap11, variantStyles7;
|
|
11738
|
+
var DEFAULT_ASSET_URL9, sizeMap11, variantStyles7;
|
|
11639
11739
|
var init_StatusEffect = __esm({
|
|
11640
11740
|
"components/game/atoms/StatusEffect.tsx"() {
|
|
11641
11741
|
init_cn();
|
|
11642
11742
|
init_Icon();
|
|
11743
|
+
DEFAULT_ASSET_URL9 = "https://almadar-kflow-assets.web.app/shared/effects/particles/flame_01.png";
|
|
11643
11744
|
sizeMap11 = {
|
|
11644
|
-
sm: { container: "w-8 h-8", icon: "text-sm", badge: "text-xs -top-1 -right-1 w-4 h-4", timer: "text-[9px]" },
|
|
11645
|
-
md: { container: "w-10 h-10", icon: "text-base", badge: "text-xs -top-1 -right-1 w-5 h-5", timer: "text-xs" },
|
|
11646
|
-
lg: { container: "w-12 h-12", icon: "text-lg", badge: "text-sm -top-1.5 -right-1.5 w-6 h-6", timer: "text-xs" }
|
|
11745
|
+
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 },
|
|
11746
|
+
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 },
|
|
11747
|
+
lg: { container: "w-12 h-12", icon: "text-lg", badge: "text-sm -top-1.5 -right-1.5 w-6 h-6", timer: "text-xs", img: 36 }
|
|
11647
11748
|
};
|
|
11648
11749
|
variantStyles7 = {
|
|
11649
11750
|
buff: "border-success bg-success/20",
|
|
@@ -11654,6 +11755,7 @@ var init_StatusEffect = __esm({
|
|
|
11654
11755
|
}
|
|
11655
11756
|
});
|
|
11656
11757
|
function DamageNumber({
|
|
11758
|
+
assetUrl = DEFAULT_ASSET_URL10,
|
|
11657
11759
|
value = 42,
|
|
11658
11760
|
type = "damage",
|
|
11659
11761
|
size = "md",
|
|
@@ -11662,22 +11764,35 @@ function DamageNumber({
|
|
|
11662
11764
|
const displayText = type === "miss" ? "MISS" : type === "heal" ? `+${value}` : `${value}`;
|
|
11663
11765
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
11664
11766
|
/* @__PURE__ */ jsxRuntime.jsx("style", { children: floatKeyframes }),
|
|
11665
|
-
/* @__PURE__ */ jsxRuntime.
|
|
11767
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
11666
11768
|
"span",
|
|
11667
11769
|
{
|
|
11668
11770
|
className: cn(
|
|
11669
|
-
"inline-
|
|
11771
|
+
"inline-flex items-center gap-0.5 font-mono select-none pointer-events-none",
|
|
11670
11772
|
sizeMap12[size],
|
|
11671
11773
|
typeStyles[type],
|
|
11672
11774
|
className
|
|
11673
11775
|
),
|
|
11674
11776
|
style: { animation: "damageFloat 1s ease-out forwards" },
|
|
11675
|
-
children:
|
|
11777
|
+
children: [
|
|
11778
|
+
assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11779
|
+
"img",
|
|
11780
|
+
{
|
|
11781
|
+
src: assetUrl,
|
|
11782
|
+
alt: "",
|
|
11783
|
+
width: 14,
|
|
11784
|
+
height: 14,
|
|
11785
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11786
|
+
className: "flex-shrink-0"
|
|
11787
|
+
}
|
|
11788
|
+
),
|
|
11789
|
+
displayText
|
|
11790
|
+
]
|
|
11676
11791
|
}
|
|
11677
11792
|
)
|
|
11678
11793
|
] });
|
|
11679
11794
|
}
|
|
11680
|
-
var sizeMap12, typeStyles, floatKeyframes;
|
|
11795
|
+
var sizeMap12, typeStyles, floatKeyframes, DEFAULT_ASSET_URL10;
|
|
11681
11796
|
var init_DamageNumber = __esm({
|
|
11682
11797
|
"components/game/atoms/DamageNumber.tsx"() {
|
|
11683
11798
|
init_cn();
|
|
@@ -11699,6 +11814,7 @@ var init_DamageNumber = __esm({
|
|
|
11699
11814
|
100% { opacity: 0; transform: translateY(-32px) scale(0.8); }
|
|
11700
11815
|
}
|
|
11701
11816
|
`;
|
|
11817
|
+
DEFAULT_ASSET_URL10 = "https://almadar-kflow-assets.web.app/shared/effects/particles/spark_01.png";
|
|
11702
11818
|
DamageNumber.displayName = "DamageNumber";
|
|
11703
11819
|
}
|
|
11704
11820
|
});
|
|
@@ -11787,6 +11903,7 @@ var init_ChoiceButton = __esm({
|
|
|
11787
11903
|
}
|
|
11788
11904
|
});
|
|
11789
11905
|
function ActionButton({
|
|
11906
|
+
assetUrl = DEFAULT_ASSET_URL11,
|
|
11790
11907
|
label = "Attack",
|
|
11791
11908
|
icon,
|
|
11792
11909
|
cooldown = 0,
|
|
@@ -11826,13 +11943,23 @@ function ActionButton({
|
|
|
11826
11943
|
}
|
|
11827
11944
|
}
|
|
11828
11945
|
),
|
|
11829
|
-
|
|
11946
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
11947
|
+
"img",
|
|
11948
|
+
{
|
|
11949
|
+
src: assetUrl,
|
|
11950
|
+
alt: "",
|
|
11951
|
+
width: 16,
|
|
11952
|
+
height: 16,
|
|
11953
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11954
|
+
className: cn("flex-shrink-0", sizes.icon)
|
|
11955
|
+
}
|
|
11956
|
+
) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? (() => {
|
|
11830
11957
|
const I = resolveIcon(icon);
|
|
11831
11958
|
return I ? /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-4 h-4" }) : null;
|
|
11832
11959
|
})() : /* @__PURE__ */ (() => {
|
|
11833
11960
|
const I = icon;
|
|
11834
11961
|
return /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-4 h-4" });
|
|
11835
|
-
})() }),
|
|
11962
|
+
})() }) : null,
|
|
11836
11963
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative z-10", children: label }),
|
|
11837
11964
|
hotkey && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11838
11965
|
"span",
|
|
@@ -11848,7 +11975,7 @@ function ActionButton({
|
|
|
11848
11975
|
}
|
|
11849
11976
|
);
|
|
11850
11977
|
}
|
|
11851
|
-
var sizeMap13, variantStyles8;
|
|
11978
|
+
var sizeMap13, variantStyles8, DEFAULT_ASSET_URL11;
|
|
11852
11979
|
var init_ActionButton = __esm({
|
|
11853
11980
|
"components/game/atoms/ActionButton.tsx"() {
|
|
11854
11981
|
init_cn();
|
|
@@ -11863,6 +11990,7 @@ var init_ActionButton = __esm({
|
|
|
11863
11990
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary-hover border-border",
|
|
11864
11991
|
danger: "bg-error text-error-foreground hover:bg-error/90 border-error"
|
|
11865
11992
|
};
|
|
11993
|
+
DEFAULT_ASSET_URL11 = "https://almadar-kflow-assets.web.app/shared/effects/particles/slash_01.png";
|
|
11866
11994
|
ActionButton.displayName = "ActionButton";
|
|
11867
11995
|
}
|
|
11868
11996
|
});
|
|
@@ -12375,10 +12503,10 @@ var init_Container = __esm({
|
|
|
12375
12503
|
center = true,
|
|
12376
12504
|
className,
|
|
12377
12505
|
children,
|
|
12378
|
-
as:
|
|
12506
|
+
as: Component2 = "div"
|
|
12379
12507
|
}) => {
|
|
12380
12508
|
const resolvedSize = maxWidth ?? size ?? "lg";
|
|
12381
|
-
const Comp =
|
|
12509
|
+
const Comp = Component2;
|
|
12382
12510
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
12383
12511
|
Comp,
|
|
12384
12512
|
{
|
|
@@ -15212,15 +15340,15 @@ function BattleBoard({
|
|
|
15212
15340
|
const board = boardEntity(entity) ?? {};
|
|
15213
15341
|
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
|
|
15214
15342
|
const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
|
|
15215
|
-
const boardWidth = num(board.boardWidth, 8);
|
|
15216
|
-
const boardHeight = num(board.boardHeight, 6);
|
|
15343
|
+
const boardWidth = num(board.gridWidth ?? board.boardWidth, 8);
|
|
15344
|
+
const boardHeight = num(board.gridHeight ?? board.boardHeight, 6);
|
|
15217
15345
|
const assetManifest = propAssetManifest ?? board.assetManifest;
|
|
15218
15346
|
const backgroundImage = board.backgroundImage;
|
|
15219
15347
|
const units = rows(board.units);
|
|
15220
15348
|
const selectedUnitId = board.selectedUnitId ?? null;
|
|
15221
15349
|
const currentPhase = str(board.phase) || "observation";
|
|
15222
15350
|
const currentTurn = num(board.turn, 1);
|
|
15223
|
-
const gameResult = board.
|
|
15351
|
+
const gameResult = board.result ?? null;
|
|
15224
15352
|
const eventBus = useEventBus();
|
|
15225
15353
|
const { t } = hooks.useTranslate();
|
|
15226
15354
|
const [hoveredTile, setHoveredTile] = React91.useState(null);
|
|
@@ -15241,7 +15369,7 @@ function BattleBoard({
|
|
|
15241
15369
|
const validMoves = React91.useMemo(() => {
|
|
15242
15370
|
if (!selectedUnit || currentPhase !== "movement") return [];
|
|
15243
15371
|
const moves = [];
|
|
15244
|
-
const range = num(
|
|
15372
|
+
const range = num(board.movementRange, 2);
|
|
15245
15373
|
const origin = unitPosition(selectedUnit);
|
|
15246
15374
|
for (let dy = -range; dy <= range; dy++) {
|
|
15247
15375
|
for (let dx = -range; dx <= range; dx++) {
|
|
@@ -22697,15 +22825,24 @@ function CastleBoard({
|
|
|
22697
22825
|
featureClickEvent,
|
|
22698
22826
|
unitClickEvent,
|
|
22699
22827
|
tileClickEvent,
|
|
22828
|
+
playAgainEvent,
|
|
22700
22829
|
className
|
|
22701
22830
|
}) {
|
|
22702
22831
|
const eventBus = useEventBus();
|
|
22832
|
+
const { t } = hooks.useTranslate();
|
|
22703
22833
|
const resolved = boardEntity(entity);
|
|
22704
22834
|
const tiles = propTiles ?? (Array.isArray(resolved?.tiles) ? resolved.tiles : []);
|
|
22705
22835
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
22706
22836
|
const units = propUnits ?? (Array.isArray(resolved?.units) ? resolved.units : []);
|
|
22707
22837
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
22708
22838
|
const backgroundImage = resolved?.backgroundImage;
|
|
22839
|
+
const gold = num(resolved?.gold);
|
|
22840
|
+
const health = num(resolved?.health);
|
|
22841
|
+
const maxHealth = num(resolved?.maxHealth);
|
|
22842
|
+
const wave = num(resolved?.wave);
|
|
22843
|
+
const tickCount = num(resolved?.tickCount);
|
|
22844
|
+
const buildings = rows(resolved?.buildings);
|
|
22845
|
+
const result = str(resolved?.result) || "none";
|
|
22709
22846
|
const [hoveredTile, setHoveredTile] = React91.useState(null);
|
|
22710
22847
|
const [selectedFeature, setSelectedFeature] = React91.useState(null);
|
|
22711
22848
|
const hoveredFeature = React91.useMemo(() => {
|
|
@@ -22718,7 +22855,7 @@ function CastleBoard({
|
|
|
22718
22855
|
(u) => u.position?.x === hoveredTile.x && u.position?.y === hoveredTile.y
|
|
22719
22856
|
) ?? null;
|
|
22720
22857
|
}, [hoveredTile, units]);
|
|
22721
|
-
const maxY = Math.max(...tiles.map((
|
|
22858
|
+
const maxY = Math.max(...tiles.map((t2) => t2.y), 0);
|
|
22722
22859
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
22723
22860
|
const tileToScreen = React91.useCallback(
|
|
22724
22861
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
@@ -22753,6 +22890,11 @@ function CastleBoard({
|
|
|
22753
22890
|
}
|
|
22754
22891
|
}, [units, onUnitClick, unitClickEvent, eventBus]);
|
|
22755
22892
|
const clearSelection = React91.useCallback(() => setSelectedFeature(null), []);
|
|
22893
|
+
const handlePlayAgain = React91.useCallback(() => {
|
|
22894
|
+
if (playAgainEvent) {
|
|
22895
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
22896
|
+
}
|
|
22897
|
+
}, [playAgainEvent, eventBus]);
|
|
22756
22898
|
const ctx = React91.useMemo(
|
|
22757
22899
|
() => ({
|
|
22758
22900
|
hoveredTile,
|
|
@@ -22761,11 +22903,18 @@ function CastleBoard({
|
|
|
22761
22903
|
selectedFeature,
|
|
22762
22904
|
clearSelection,
|
|
22763
22905
|
tileToScreen,
|
|
22764
|
-
scale
|
|
22906
|
+
scale,
|
|
22907
|
+
gold,
|
|
22908
|
+
health,
|
|
22909
|
+
maxHealth,
|
|
22910
|
+
wave,
|
|
22911
|
+
tickCount,
|
|
22912
|
+
buildings,
|
|
22913
|
+
result
|
|
22765
22914
|
}),
|
|
22766
|
-
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale]
|
|
22915
|
+
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale, gold, health, maxHealth, wave, tickCount, buildings, result]
|
|
22767
22916
|
);
|
|
22768
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("castle-board min-h-screen flex flex-col bg-background", className), children: [
|
|
22917
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("castle-board relative min-h-screen flex flex-col bg-background", className), children: [
|
|
22769
22918
|
header && header(ctx),
|
|
22770
22919
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
|
|
22771
22920
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-auto p-4 relative", children: [
|
|
@@ -22790,7 +22939,29 @@ function CastleBoard({
|
|
|
22790
22939
|
] }),
|
|
22791
22940
|
sidePanel && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-96 shrink-0 border-l border-border bg-surface overflow-y-auto", children: sidePanel(ctx) })
|
|
22792
22941
|
] }),
|
|
22793
|
-
footer && footer(ctx)
|
|
22942
|
+
footer && footer(ctx),
|
|
22943
|
+
result !== "none" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
22944
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
22945
|
+
Typography,
|
|
22946
|
+
{
|
|
22947
|
+
variant: "h2",
|
|
22948
|
+
className: cn(
|
|
22949
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
22950
|
+
result === "victory" ? "text-warning" : "text-error"
|
|
22951
|
+
),
|
|
22952
|
+
children: result === "victory" ? t("battle.victory") : t("battle.defeat")
|
|
22953
|
+
}
|
|
22954
|
+
),
|
|
22955
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
22956
|
+
Button,
|
|
22957
|
+
{
|
|
22958
|
+
variant: "primary",
|
|
22959
|
+
className: "px-8 py-3 font-semibold",
|
|
22960
|
+
onClick: handlePlayAgain,
|
|
22961
|
+
children: t("battle.playAgain")
|
|
22962
|
+
}
|
|
22963
|
+
)
|
|
22964
|
+
] }) })
|
|
22794
22965
|
] });
|
|
22795
22966
|
}
|
|
22796
22967
|
var init_CastleBoard = __esm({
|
|
@@ -22798,6 +22969,10 @@ var init_CastleBoard = __esm({
|
|
|
22798
22969
|
"use client";
|
|
22799
22970
|
init_cn();
|
|
22800
22971
|
init_useEventBus();
|
|
22972
|
+
init_Box();
|
|
22973
|
+
init_Button();
|
|
22974
|
+
init_Typography();
|
|
22975
|
+
init_Stack();
|
|
22801
22976
|
init_IsometricCanvas();
|
|
22802
22977
|
init_boardEntity();
|
|
22803
22978
|
init_isometric();
|
|
@@ -23674,22 +23849,16 @@ function ClassifierBoard({
|
|
|
23674
23849
|
const { emit } = useEventBus();
|
|
23675
23850
|
const { t } = hooks.useTranslate();
|
|
23676
23851
|
const resolved = boardEntity(entity);
|
|
23677
|
-
const [localAssignments, setLocalAssignments] = React91.useState({});
|
|
23678
23852
|
const [headerError, setHeaderError] = React91.useState(false);
|
|
23679
|
-
const [localSubmitted, setLocalSubmitted] = React91.useState(false);
|
|
23680
|
-
const [localAttempts, setLocalAttempts] = React91.useState(0);
|
|
23681
|
-
const [showHint, setShowHint] = React91.useState(false);
|
|
23682
23853
|
const items = Array.isArray(resolved?.items) ? resolved.items : [];
|
|
23683
23854
|
const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
|
|
23684
|
-
const
|
|
23685
|
-
const
|
|
23686
|
-
const
|
|
23687
|
-
const assignments =
|
|
23688
|
-
if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
|
|
23855
|
+
const result = str(resolved?.result);
|
|
23856
|
+
const submitted = result === "win";
|
|
23857
|
+
const attempts = num(resolved?.attempts);
|
|
23858
|
+
const assignments = items.reduce((acc, item) => {
|
|
23859
|
+
if (item.assignedCategory != null && item.assignedCategory !== "") acc[item.id] = item.assignedCategory;
|
|
23689
23860
|
return acc;
|
|
23690
|
-
}, {})
|
|
23691
|
-
const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
|
|
23692
|
-
const submitted = entityDrivesResult || localSubmitted;
|
|
23861
|
+
}, {});
|
|
23693
23862
|
const unassignedItems = items.filter((item) => !assignments[item.id]);
|
|
23694
23863
|
const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
|
|
23695
23864
|
const results = submitted ? items.map((item) => ({
|
|
@@ -23697,54 +23866,25 @@ function ClassifierBoard({
|
|
|
23697
23866
|
assigned: assignments[item.id],
|
|
23698
23867
|
correct: assignments[item.id] === item.correctCategory
|
|
23699
23868
|
})) : [];
|
|
23700
|
-
const allCorrect =
|
|
23869
|
+
const allCorrect = result === "win";
|
|
23701
23870
|
const correctCount = results.filter((r2) => r2.correct).length;
|
|
23871
|
+
const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
|
|
23702
23872
|
const handleAssign = (itemId, categoryId) => {
|
|
23703
23873
|
if (submitted) return;
|
|
23704
|
-
if (assignEvent) {
|
|
23705
|
-
emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
23706
|
-
}
|
|
23707
|
-
if (!entityHasAssignments) {
|
|
23708
|
-
setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
|
|
23709
|
-
}
|
|
23874
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
23710
23875
|
};
|
|
23711
23876
|
const handleUnassign = (itemId) => {
|
|
23712
23877
|
if (submitted) return;
|
|
23713
|
-
if (
|
|
23714
|
-
setLocalAssignments((prev) => {
|
|
23715
|
-
const next = { ...prev };
|
|
23716
|
-
delete next[itemId];
|
|
23717
|
-
return next;
|
|
23718
|
-
});
|
|
23719
|
-
}
|
|
23878
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId: "" });
|
|
23720
23879
|
};
|
|
23721
|
-
const handleSubmit =
|
|
23722
|
-
if (checkEvent) {
|
|
23723
|
-
|
|
23724
|
-
|
|
23725
|
-
if (!entityDrivesResult) {
|
|
23726
|
-
setLocalSubmitted(true);
|
|
23727
|
-
setLocalAttempts((a) => a + 1);
|
|
23728
|
-
const correct = items.every((item) => assignments[item.id] === item.correctCategory);
|
|
23729
|
-
if (correct) {
|
|
23730
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
23731
|
-
}
|
|
23732
|
-
}
|
|
23733
|
-
}, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
|
|
23734
|
-
const handleReset = () => {
|
|
23735
|
-
if (!entityDrivesResult) setLocalSubmitted(false);
|
|
23736
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
23737
|
-
setShowHint(true);
|
|
23880
|
+
const handleSubmit = () => {
|
|
23881
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
23882
|
+
if (allAssigned && items.every((item) => assignments[item.id] === item.correctCategory)) {
|
|
23883
|
+
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
23738
23884
|
}
|
|
23739
23885
|
};
|
|
23740
23886
|
const handleFullReset = () => {
|
|
23741
|
-
if (playAgainEvent) {
|
|
23742
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
23743
|
-
}
|
|
23744
|
-
setLocalAssignments({});
|
|
23745
|
-
setLocalSubmitted(false);
|
|
23746
|
-
setLocalAttempts(0);
|
|
23747
|
-
setShowHint(false);
|
|
23887
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
23748
23888
|
};
|
|
23749
23889
|
if (!resolved) return null;
|
|
23750
23890
|
const theme = resolved.theme ?? void 0;
|
|
@@ -23783,17 +23923,17 @@ function ClassifierBoard({
|
|
|
23783
23923
|
/* @__PURE__ */ jsxRuntime.jsx(Badge, { size: "sm", children: catItems.length })
|
|
23784
23924
|
] }),
|
|
23785
23925
|
/* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-wrap min-h-[2rem]", children: catItems.map((item) => {
|
|
23786
|
-
const
|
|
23926
|
+
const result2 = results.find((r2) => r2.item.id === item.id);
|
|
23787
23927
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
23788
23928
|
Badge,
|
|
23789
23929
|
{
|
|
23790
23930
|
size: "sm",
|
|
23791
|
-
className: `cursor-pointer ${
|
|
23931
|
+
className: `cursor-pointer ${result2 ? result2.correct ? "border-success bg-success/10" : "border-error bg-error/10" : ""}`,
|
|
23792
23932
|
onClick: () => handleUnassign(item.id),
|
|
23793
23933
|
children: [
|
|
23794
23934
|
item.iconUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: item.iconUrl, alt: "", className: "w-3 h-3 object-contain inline-block" }),
|
|
23795
23935
|
item.label,
|
|
23796
|
-
|
|
23936
|
+
result2 && /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: result2.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "xs", className: result2.correct ? "text-success" : "text-error" })
|
|
23797
23937
|
]
|
|
23798
23938
|
},
|
|
23799
23939
|
item.id
|
|
@@ -23822,10 +23962,10 @@ function ClassifierBoard({
|
|
|
23822
23962
|
] }) }),
|
|
23823
23963
|
showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
|
|
23824
23964
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
23825
|
-
!submitted
|
|
23965
|
+
!submitted && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
|
|
23826
23966
|
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
|
|
23827
23967
|
t("classifier.check")
|
|
23828
|
-
] })
|
|
23968
|
+
] }),
|
|
23829
23969
|
/* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
|
|
23830
23970
|
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
|
|
23831
23971
|
t("classifier.reset")
|
|
@@ -27543,7 +27683,7 @@ var init_Flex = __esm({
|
|
|
27543
27683
|
basis,
|
|
27544
27684
|
className,
|
|
27545
27685
|
children,
|
|
27546
|
-
as:
|
|
27686
|
+
as: Component2 = "div"
|
|
27547
27687
|
}) => {
|
|
27548
27688
|
const flexStyle = {};
|
|
27549
27689
|
if (grow !== void 0 || shrink !== void 0 || basis !== void 0) {
|
|
@@ -27555,7 +27695,7 @@ var init_Flex = __esm({
|
|
|
27555
27695
|
flexStyle.flexBasis = typeof basis === "number" ? `${basis}px` : basis;
|
|
27556
27696
|
}
|
|
27557
27697
|
}
|
|
27558
|
-
const Comp =
|
|
27698
|
+
const Comp = Component2;
|
|
27559
27699
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
27560
27700
|
Comp,
|
|
27561
27701
|
{
|
|
@@ -27675,11 +27815,11 @@ var init_Grid = __esm({
|
|
|
27675
27815
|
className,
|
|
27676
27816
|
style,
|
|
27677
27817
|
children,
|
|
27678
|
-
as:
|
|
27818
|
+
as: Component2 = "div"
|
|
27679
27819
|
}) => {
|
|
27680
27820
|
const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
|
|
27681
27821
|
return React91__namespace.default.createElement(
|
|
27682
|
-
|
|
27822
|
+
Component2,
|
|
27683
27823
|
{
|
|
27684
27824
|
className: cn(
|
|
27685
27825
|
"grid",
|
|
@@ -29505,6 +29645,7 @@ var init_ProgressDots = __esm({
|
|
|
29505
29645
|
}
|
|
29506
29646
|
});
|
|
29507
29647
|
function StatBadge({
|
|
29648
|
+
assetUrl = DEFAULT_ASSET_URL12,
|
|
29508
29649
|
label,
|
|
29509
29650
|
value = 0,
|
|
29510
29651
|
max,
|
|
@@ -29528,7 +29669,17 @@ function StatBadge({
|
|
|
29528
29669
|
className
|
|
29529
29670
|
),
|
|
29530
29671
|
children: [
|
|
29531
|
-
|
|
29672
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
29673
|
+
"img",
|
|
29674
|
+
{
|
|
29675
|
+
src: assetUrl,
|
|
29676
|
+
alt: "",
|
|
29677
|
+
width: 16,
|
|
29678
|
+
height: 16,
|
|
29679
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
29680
|
+
className: "flex-shrink-0"
|
|
29681
|
+
}
|
|
29682
|
+
) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0 text-lg", children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, className: "w-4 h-4" }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, className: "w-4 h-4" }) }) : null,
|
|
29532
29683
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground font-medium", children: label }),
|
|
29533
29684
|
format === "hearts" && max && /* @__PURE__ */ jsxRuntime.jsx(
|
|
29534
29685
|
HealthBar,
|
|
@@ -29561,7 +29712,7 @@ function StatBadge({
|
|
|
29561
29712
|
}
|
|
29562
29713
|
);
|
|
29563
29714
|
}
|
|
29564
|
-
var sizeMap17, variantMap2;
|
|
29715
|
+
var sizeMap17, variantMap2, DEFAULT_ASSET_URL12;
|
|
29565
29716
|
var init_StatBadge = __esm({
|
|
29566
29717
|
"components/game/molecules/StatBadge.tsx"() {
|
|
29567
29718
|
init_cn();
|
|
@@ -29580,6 +29731,7 @@ var init_StatBadge = __esm({
|
|
|
29580
29731
|
warning: "bg-warning/15 border-warning/40 text-foreground",
|
|
29581
29732
|
danger: "bg-error/15 border-error/40 text-foreground"
|
|
29582
29733
|
};
|
|
29734
|
+
DEFAULT_ASSET_URL12 = "https://almadar-kflow-assets.web.app/shared/effects/particles/light_01.png";
|
|
29583
29735
|
StatBadge.displayName = "StatBadge";
|
|
29584
29736
|
}
|
|
29585
29737
|
});
|
|
@@ -30236,6 +30388,7 @@ var init_TurnPanel = __esm({
|
|
|
30236
30388
|
}
|
|
30237
30389
|
});
|
|
30238
30390
|
function EnemyPlate({
|
|
30391
|
+
assetUrl = DEFAULT_ASSET_URL13,
|
|
30239
30392
|
name = "Shadow Guard",
|
|
30240
30393
|
health = 80,
|
|
30241
30394
|
maxHealth = 100,
|
|
@@ -30253,15 +30406,28 @@ function EnemyPlate({
|
|
|
30253
30406
|
),
|
|
30254
30407
|
children: [
|
|
30255
30408
|
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center justify-between gap-2", children: [
|
|
30256
|
-
/* @__PURE__ */ jsxRuntime.
|
|
30257
|
-
|
|
30258
|
-
|
|
30259
|
-
|
|
30260
|
-
|
|
30261
|
-
|
|
30262
|
-
|
|
30263
|
-
|
|
30264
|
-
|
|
30409
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-1.5 min-w-0", children: [
|
|
30410
|
+
assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
30411
|
+
"img",
|
|
30412
|
+
{
|
|
30413
|
+
src: assetUrl,
|
|
30414
|
+
alt: name,
|
|
30415
|
+
width: 24,
|
|
30416
|
+
height: 24,
|
|
30417
|
+
style: { objectFit: "cover", borderRadius: "50%" },
|
|
30418
|
+
className: "flex-shrink-0"
|
|
30419
|
+
}
|
|
30420
|
+
),
|
|
30421
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
30422
|
+
Typography,
|
|
30423
|
+
{
|
|
30424
|
+
variant: "caption",
|
|
30425
|
+
weight: "bold",
|
|
30426
|
+
className: "text-[var(--color-foreground)] truncate",
|
|
30427
|
+
children: name
|
|
30428
|
+
}
|
|
30429
|
+
)
|
|
30430
|
+
] }),
|
|
30265
30431
|
level != null && /* @__PURE__ */ jsxRuntime.jsxs(Badge, { variant: "neutral", size: "sm", children: [
|
|
30266
30432
|
"Lv.",
|
|
30267
30433
|
level
|
|
@@ -30306,7 +30472,7 @@ function EnemyPlate({
|
|
|
30306
30472
|
}
|
|
30307
30473
|
);
|
|
30308
30474
|
}
|
|
30309
|
-
var effectVariantMap2, DEFAULT_ENEMY_EFFECTS;
|
|
30475
|
+
var effectVariantMap2, DEFAULT_ENEMY_EFFECTS, DEFAULT_ASSET_URL13;
|
|
30310
30476
|
var init_EnemyPlate = __esm({
|
|
30311
30477
|
"components/game/molecules/EnemyPlate.tsx"() {
|
|
30312
30478
|
"use client";
|
|
@@ -30323,6 +30489,7 @@ var init_EnemyPlate = __esm({
|
|
|
30323
30489
|
DEFAULT_ENEMY_EFFECTS = [
|
|
30324
30490
|
{ icon: "flame", label: "Burn", variant: "debuff" }
|
|
30325
30491
|
];
|
|
30492
|
+
DEFAULT_ASSET_URL13 = "https://almadar-kflow-assets.web.app/shared/portraits/shadow-legion.png";
|
|
30326
30493
|
EnemyPlate.displayName = "EnemyPlate";
|
|
30327
30494
|
}
|
|
30328
30495
|
});
|
|
@@ -31854,13 +32021,13 @@ var init_MapView = __esm({
|
|
|
31854
32021
|
shadowSize: [41, 41]
|
|
31855
32022
|
});
|
|
31856
32023
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
31857
|
-
const { useEffect:
|
|
32024
|
+
const { useEffect: useEffect84, useRef: useRef76, useCallback: useCallback124, useState: useState120 } = React91__namespace.default;
|
|
31858
32025
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
31859
|
-
const { useEventBus:
|
|
32026
|
+
const { useEventBus: useEventBus4 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
31860
32027
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
31861
32028
|
const map = useMap();
|
|
31862
|
-
const prevRef =
|
|
31863
|
-
|
|
32029
|
+
const prevRef = useRef76({ centerLat, centerLng, zoom });
|
|
32030
|
+
useEffect84(() => {
|
|
31864
32031
|
const prev = prevRef.current;
|
|
31865
32032
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
31866
32033
|
map.setView([centerLat, centerLng], zoom);
|
|
@@ -31871,7 +32038,7 @@ var init_MapView = __esm({
|
|
|
31871
32038
|
}
|
|
31872
32039
|
function MapClickHandler({ onMapClick }) {
|
|
31873
32040
|
const map = useMap();
|
|
31874
|
-
|
|
32041
|
+
useEffect84(() => {
|
|
31875
32042
|
if (!onMapClick) return;
|
|
31876
32043
|
const handler = (e) => {
|
|
31877
32044
|
onMapClick(e.latlng.lat, e.latlng.lng);
|
|
@@ -31898,9 +32065,9 @@ var init_MapView = __esm({
|
|
|
31898
32065
|
className,
|
|
31899
32066
|
showAttribution = true
|
|
31900
32067
|
}) {
|
|
31901
|
-
const eventBus =
|
|
31902
|
-
const [clickedPosition, setClickedPosition] =
|
|
31903
|
-
const handleMapClick =
|
|
32068
|
+
const eventBus = useEventBus4();
|
|
32069
|
+
const [clickedPosition, setClickedPosition] = useState120(null);
|
|
32070
|
+
const handleMapClick = useCallback124((lat, lng) => {
|
|
31904
32071
|
if (showClickedPin) {
|
|
31905
32072
|
setClickedPosition({ lat, lng });
|
|
31906
32073
|
}
|
|
@@ -31909,7 +32076,7 @@ var init_MapView = __esm({
|
|
|
31909
32076
|
eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
|
|
31910
32077
|
}
|
|
31911
32078
|
}, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
|
|
31912
|
-
const handleMarkerClick =
|
|
32079
|
+
const handleMarkerClick = useCallback124((marker) => {
|
|
31913
32080
|
onMarkerClick?.(marker);
|
|
31914
32081
|
if (markerClickEvent) {
|
|
31915
32082
|
eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
|
|
@@ -38627,10 +38794,10 @@ var init_Section = __esm({
|
|
|
38627
38794
|
children,
|
|
38628
38795
|
headerClassName,
|
|
38629
38796
|
contentClassName,
|
|
38630
|
-
as:
|
|
38797
|
+
as: Component2 = "section"
|
|
38631
38798
|
}) => {
|
|
38632
38799
|
const hasHeader = title || description || action;
|
|
38633
|
-
const Comp =
|
|
38800
|
+
const Comp = Component2;
|
|
38634
38801
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
38635
38802
|
Comp,
|
|
38636
38803
|
{
|
|
@@ -40731,44 +40898,27 @@ function DebuggerBoard({
|
|
|
40731
40898
|
const { t } = hooks.useTranslate();
|
|
40732
40899
|
const resolved = boardEntity(entity);
|
|
40733
40900
|
const [headerError, setHeaderError] = React91.useState(false);
|
|
40734
|
-
const [showHint, setShowHint] = React91.useState(false);
|
|
40735
40901
|
const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
|
|
40736
|
-
const result = resolved?.result
|
|
40902
|
+
const result = str(resolved?.result) || "none";
|
|
40737
40903
|
const attempts = num(resolved?.attempts);
|
|
40738
|
-
const submitted = result
|
|
40904
|
+
const submitted = result === "win";
|
|
40905
|
+
const showHint = !submitted && attempts >= 2 && Boolean(str(resolved?.hint));
|
|
40739
40906
|
const bugLines = lines.filter((l) => l.isBug);
|
|
40740
40907
|
const flaggedLines = lines.filter((l) => l.isFlagged);
|
|
40741
40908
|
const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
|
|
40742
40909
|
const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
|
|
40743
|
-
const allCorrect =
|
|
40910
|
+
const allCorrect = submitted;
|
|
40744
40911
|
const toggleLine = (lineId) => {
|
|
40745
40912
|
if (submitted) return;
|
|
40746
|
-
if (toggleFlagEvent) {
|
|
40747
|
-
emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
40748
|
-
}
|
|
40913
|
+
if (toggleFlagEvent) emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
40749
40914
|
};
|
|
40750
40915
|
const handleSubmit = React91.useCallback(() => {
|
|
40751
|
-
if (checkEvent) {
|
|
40752
|
-
emit(`UI:${checkEvent}`, {});
|
|
40753
|
-
}
|
|
40916
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
40754
40917
|
const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
|
|
40755
|
-
if (correct) {
|
|
40756
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
40757
|
-
}
|
|
40918
|
+
if (correct) emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
40758
40919
|
}, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
|
|
40759
40920
|
const handleReset = () => {
|
|
40760
|
-
if (playAgainEvent) {
|
|
40761
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
40762
|
-
}
|
|
40763
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
40764
|
-
setShowHint(true);
|
|
40765
|
-
}
|
|
40766
|
-
};
|
|
40767
|
-
const handleFullReset = () => {
|
|
40768
|
-
if (playAgainEvent) {
|
|
40769
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
40770
|
-
}
|
|
40771
|
-
setShowHint(false);
|
|
40921
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
40772
40922
|
};
|
|
40773
40923
|
if (!resolved) return null;
|
|
40774
40924
|
const theme = resolved.theme ?? void 0;
|
|
@@ -40792,7 +40942,7 @@ function DebuggerBoard({
|
|
|
40792
40942
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
|
|
40793
40943
|
] }),
|
|
40794
40944
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str(resolved.description) }),
|
|
40795
|
-
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(
|
|
40945
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(lines.filter((l) => l.isBug).length) }) })
|
|
40796
40946
|
] }) }),
|
|
40797
40947
|
/* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "none", children: lines.map((line, i) => {
|
|
40798
40948
|
const isFlagged = !!line.isFlagged;
|
|
@@ -40842,11 +40992,11 @@ function DebuggerBoard({
|
|
|
40842
40992
|
] }) }),
|
|
40843
40993
|
showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
|
|
40844
40994
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
40845
|
-
!submitted
|
|
40995
|
+
!submitted && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
|
|
40846
40996
|
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
|
|
40847
40997
|
t("debugger.submit")
|
|
40848
|
-
] })
|
|
40849
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick:
|
|
40998
|
+
] }),
|
|
40999
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handleReset, children: [
|
|
40850
41000
|
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
|
|
40851
41001
|
t("debugger.reset")
|
|
40852
41002
|
] })
|
|
@@ -41847,39 +41997,40 @@ function EventHandlerBoard({
|
|
|
41847
41997
|
stepDurationMs = 800,
|
|
41848
41998
|
playEvent,
|
|
41849
41999
|
completeEvent,
|
|
42000
|
+
editRuleEvent,
|
|
42001
|
+
playAgainEvent,
|
|
41850
42002
|
className
|
|
41851
42003
|
}) {
|
|
41852
42004
|
const { emit } = useEventBus();
|
|
41853
42005
|
const { t } = hooks.useTranslate();
|
|
41854
42006
|
const resolved = boardEntity(entity);
|
|
41855
|
-
const
|
|
41856
|
-
const
|
|
42007
|
+
const objects = rows(resolved?.objects);
|
|
42008
|
+
const entityResult = str(resolved?.result) || "none";
|
|
42009
|
+
const isSuccess = entityResult === "win";
|
|
42010
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
41857
42011
|
const [selectedObjectId, setSelectedObjectId] = React91.useState(
|
|
41858
|
-
|
|
42012
|
+
objects[0] ? objId(objects[0]) : null
|
|
41859
42013
|
);
|
|
41860
42014
|
const [headerError, setHeaderError] = React91.useState(false);
|
|
41861
|
-
const [
|
|
42015
|
+
const [isPlaying, setIsPlaying] = React91.useState(false);
|
|
41862
42016
|
const [eventLog, setEventLog] = React91.useState([]);
|
|
41863
|
-
const [attempts, setAttempts] = React91.useState(0);
|
|
41864
42017
|
const timerRef = React91.useRef(null);
|
|
41865
42018
|
const logIdCounter = React91.useRef(0);
|
|
41866
42019
|
React91.useEffect(() => () => {
|
|
41867
42020
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
41868
42021
|
}, []);
|
|
41869
|
-
const selectedObject = objects.find((o) => objId(o) === selectedObjectId)
|
|
42022
|
+
const selectedObject = objects.find((o) => objId(o) === selectedObjectId) ?? null;
|
|
41870
42023
|
const handleRulesChange = React91.useCallback((objectId, rules) => {
|
|
41871
|
-
|
|
41872
|
-
|
|
41873
|
-
));
|
|
41874
|
-
}, []);
|
|
42024
|
+
if (editRuleEvent) emit(`UI:${editRuleEvent}`, { objectId, rules });
|
|
42025
|
+
}, [editRuleEvent, emit]);
|
|
41875
42026
|
const addLogEntry = React91.useCallback((icon, message, status = "done") => {
|
|
41876
42027
|
const id = `log-${logIdCounter.current++}`;
|
|
41877
42028
|
setEventLog((prev) => [...prev, { id, timestamp: Date.now(), icon, message, status }]);
|
|
41878
42029
|
}, []);
|
|
41879
42030
|
const handlePlay = React91.useCallback(() => {
|
|
41880
|
-
if (
|
|
42031
|
+
if (isPlaying || isSuccess) return;
|
|
41881
42032
|
if (playEvent) emit(`UI:${playEvent}`, {});
|
|
41882
|
-
|
|
42033
|
+
setIsPlaying(true);
|
|
41883
42034
|
setEventLog([]);
|
|
41884
42035
|
const allRules = [];
|
|
41885
42036
|
objects.forEach((obj) => {
|
|
@@ -41895,15 +42046,8 @@ function EventHandlerBoard({
|
|
|
41895
42046
|
let goalReached = false;
|
|
41896
42047
|
const processNext = () => {
|
|
41897
42048
|
if (eventQueue.length === 0 || stepIdx > 20) {
|
|
41898
|
-
|
|
41899
|
-
|
|
41900
|
-
if (completeEvent) {
|
|
41901
|
-
emit(`UI:${completeEvent}`, { success: true });
|
|
41902
|
-
}
|
|
41903
|
-
} else {
|
|
41904
|
-
setAttempts((prev) => prev + 1);
|
|
41905
|
-
setPlayState("fail");
|
|
41906
|
-
}
|
|
42049
|
+
setIsPlaying(false);
|
|
42050
|
+
if (goalReached && completeEvent) emit(`UI:${completeEvent}`, { success: true });
|
|
41907
42051
|
return;
|
|
41908
42052
|
}
|
|
41909
42053
|
const currentEvent = eventQueue.shift();
|
|
@@ -41934,21 +42078,19 @@ function EventHandlerBoard({
|
|
|
41934
42078
|
addLogEntry("\u{1F3AC}", t("eventHandler.simulationStarted", { events: triggers.join(", ") }), "active");
|
|
41935
42079
|
}
|
|
41936
42080
|
timerRef.current = setTimeout(processNext, stepDurationMs);
|
|
41937
|
-
}, [
|
|
42081
|
+
}, [isPlaying, isSuccess, objects, resolved, stepDurationMs, playEvent, completeEvent, emit, addLogEntry, t]);
|
|
41938
42082
|
const handleTryAgain = React91.useCallback(() => {
|
|
41939
42083
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
41940
|
-
|
|
42084
|
+
setIsPlaying(false);
|
|
41941
42085
|
setEventLog([]);
|
|
41942
42086
|
}, []);
|
|
41943
42087
|
const handleReset = React91.useCallback(() => {
|
|
41944
42088
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
41945
|
-
|
|
41946
|
-
setObjects([...resetObjects]);
|
|
41947
|
-
setPlayState("editing");
|
|
42089
|
+
setIsPlaying(false);
|
|
41948
42090
|
setEventLog([]);
|
|
41949
|
-
setSelectedObjectId(
|
|
41950
|
-
|
|
41951
|
-
}, [
|
|
42091
|
+
setSelectedObjectId(objects[0] ? objId(objects[0]) : null);
|
|
42092
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
42093
|
+
}, [objects, playAgainEvent, emit]);
|
|
41952
42094
|
if (!resolved) return null;
|
|
41953
42095
|
const objectViewers = objects.map((obj) => {
|
|
41954
42096
|
const states = objStates(obj);
|
|
@@ -42017,12 +42159,12 @@ function EventHandlerBoard({
|
|
|
42017
42159
|
{
|
|
42018
42160
|
object: selectedObject,
|
|
42019
42161
|
onRulesChange: handleRulesChange,
|
|
42020
|
-
disabled:
|
|
42162
|
+
disabled: isPlaying
|
|
42021
42163
|
}
|
|
42022
42164
|
),
|
|
42023
42165
|
eventLog.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(EventLog, { entries: eventLog }),
|
|
42024
|
-
|
|
42025
|
-
|
|
42166
|
+
isSuccess && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("eventHandler.chainComplete") }) }),
|
|
42167
|
+
!isPlaying && !isSuccess && attempts > 0 && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
42026
42168
|
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(encourageKey) }) }),
|
|
42027
42169
|
showHint && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-start", gap: "xs", children: [
|
|
42028
42170
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
@@ -42030,12 +42172,12 @@ function EventHandlerBoard({
|
|
|
42030
42172
|
] }) })
|
|
42031
42173
|
] }),
|
|
42032
42174
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
|
|
42033
|
-
|
|
42175
|
+
!isPlaying && !isSuccess && attempts > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
42034
42176
|
Button,
|
|
42035
42177
|
{
|
|
42036
42178
|
variant: "primary",
|
|
42037
42179
|
onClick: handlePlay,
|
|
42038
|
-
disabled:
|
|
42180
|
+
disabled: isPlaying || isSuccess,
|
|
42039
42181
|
children: "\u25B6 " + t("game.play")
|
|
42040
42182
|
}
|
|
42041
42183
|
),
|
|
@@ -43220,6 +43362,1568 @@ var init_GameAudioToggle = __esm({
|
|
|
43220
43362
|
GameAudioToggle.displayName = "GameAudioToggle";
|
|
43221
43363
|
}
|
|
43222
43364
|
});
|
|
43365
|
+
function detectAssetRoot(modelUrl) {
|
|
43366
|
+
const idx = modelUrl.indexOf("/3d/");
|
|
43367
|
+
if (idx !== -1) {
|
|
43368
|
+
return modelUrl.substring(0, idx + 4);
|
|
43369
|
+
}
|
|
43370
|
+
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
43371
|
+
}
|
|
43372
|
+
function createGLTFLoaderForUrl(url) {
|
|
43373
|
+
const loader = new GLTFLoader_js.GLTFLoader();
|
|
43374
|
+
loader.setResourcePath(detectAssetRoot(url));
|
|
43375
|
+
return loader;
|
|
43376
|
+
}
|
|
43377
|
+
var AssetLoader;
|
|
43378
|
+
var init_AssetLoader = __esm({
|
|
43379
|
+
"components/game/molecules/three/loaders/AssetLoader.ts"() {
|
|
43380
|
+
AssetLoader = class {
|
|
43381
|
+
constructor() {
|
|
43382
|
+
__publicField(this, "objLoader");
|
|
43383
|
+
__publicField(this, "textureLoader");
|
|
43384
|
+
__publicField(this, "modelCache");
|
|
43385
|
+
__publicField(this, "textureCache");
|
|
43386
|
+
__publicField(this, "loadingPromises");
|
|
43387
|
+
this.objLoader = new OBJLoader_js.OBJLoader();
|
|
43388
|
+
this.textureLoader = new THREE__namespace.TextureLoader();
|
|
43389
|
+
this.modelCache = /* @__PURE__ */ new Map();
|
|
43390
|
+
this.textureCache = /* @__PURE__ */ new Map();
|
|
43391
|
+
this.loadingPromises = /* @__PURE__ */ new Map();
|
|
43392
|
+
}
|
|
43393
|
+
/**
|
|
43394
|
+
* Load a GLB/GLTF model
|
|
43395
|
+
* @param url - URL to the .glb or .gltf file
|
|
43396
|
+
* @returns Promise with loaded model scene and animations
|
|
43397
|
+
*/
|
|
43398
|
+
async loadModel(url) {
|
|
43399
|
+
if (this.modelCache.has(url)) {
|
|
43400
|
+
return this.modelCache.get(url);
|
|
43401
|
+
}
|
|
43402
|
+
if (this.loadingPromises.has(url)) {
|
|
43403
|
+
return this.loadingPromises.get(url);
|
|
43404
|
+
}
|
|
43405
|
+
const loader = createGLTFLoaderForUrl(url);
|
|
43406
|
+
const loadPromise = loader.loadAsync(url).then((gltf) => {
|
|
43407
|
+
const result = {
|
|
43408
|
+
scene: gltf.scene,
|
|
43409
|
+
animations: gltf.animations || []
|
|
43410
|
+
};
|
|
43411
|
+
this.modelCache.set(url, result);
|
|
43412
|
+
this.loadingPromises.delete(url);
|
|
43413
|
+
return result;
|
|
43414
|
+
}).catch((error) => {
|
|
43415
|
+
this.loadingPromises.delete(url);
|
|
43416
|
+
throw new Error(`Failed to load model ${url}: ${error.message}`);
|
|
43417
|
+
});
|
|
43418
|
+
this.loadingPromises.set(url, loadPromise);
|
|
43419
|
+
return loadPromise;
|
|
43420
|
+
}
|
|
43421
|
+
/**
|
|
43422
|
+
* Load an OBJ model (fallback for non-GLB assets)
|
|
43423
|
+
* @param url - URL to the .obj file
|
|
43424
|
+
* @returns Promise with loaded object group
|
|
43425
|
+
*/
|
|
43426
|
+
async loadOBJ(url) {
|
|
43427
|
+
if (this.modelCache.has(url)) {
|
|
43428
|
+
return this.modelCache.get(url).scene;
|
|
43429
|
+
}
|
|
43430
|
+
if (this.loadingPromises.has(url)) {
|
|
43431
|
+
const result = await this.loadingPromises.get(url);
|
|
43432
|
+
return result.scene;
|
|
43433
|
+
}
|
|
43434
|
+
const loadPromise = this.objLoader.loadAsync(url).then((group) => {
|
|
43435
|
+
const result = {
|
|
43436
|
+
scene: group,
|
|
43437
|
+
animations: []
|
|
43438
|
+
};
|
|
43439
|
+
this.modelCache.set(url, result);
|
|
43440
|
+
this.loadingPromises.delete(url);
|
|
43441
|
+
return result;
|
|
43442
|
+
}).catch((error) => {
|
|
43443
|
+
this.loadingPromises.delete(url);
|
|
43444
|
+
throw new Error(`Failed to load OBJ ${url}: ${error.message}`);
|
|
43445
|
+
});
|
|
43446
|
+
this.loadingPromises.set(url, loadPromise);
|
|
43447
|
+
return (await loadPromise).scene;
|
|
43448
|
+
}
|
|
43449
|
+
/**
|
|
43450
|
+
* Load a texture
|
|
43451
|
+
* @param url - URL to the texture image
|
|
43452
|
+
* @returns Promise with loaded texture
|
|
43453
|
+
*/
|
|
43454
|
+
async loadTexture(url) {
|
|
43455
|
+
if (this.textureCache.has(url)) {
|
|
43456
|
+
return this.textureCache.get(url);
|
|
43457
|
+
}
|
|
43458
|
+
if (this.loadingPromises.has(`texture:${url}`)) {
|
|
43459
|
+
return this.loadingPromises.get(`texture:${url}`);
|
|
43460
|
+
}
|
|
43461
|
+
const loadPromise = this.textureLoader.loadAsync(url).then((texture) => {
|
|
43462
|
+
texture.colorSpace = THREE__namespace.SRGBColorSpace;
|
|
43463
|
+
this.textureCache.set(url, texture);
|
|
43464
|
+
this.loadingPromises.delete(`texture:${url}`);
|
|
43465
|
+
return texture;
|
|
43466
|
+
}).catch((error) => {
|
|
43467
|
+
this.loadingPromises.delete(`texture:${url}`);
|
|
43468
|
+
throw new Error(`Failed to load texture ${url}: ${error.message}`);
|
|
43469
|
+
});
|
|
43470
|
+
this.loadingPromises.set(`texture:${url}`, loadPromise);
|
|
43471
|
+
return loadPromise;
|
|
43472
|
+
}
|
|
43473
|
+
/**
|
|
43474
|
+
* Preload multiple assets
|
|
43475
|
+
* @param urls - Array of asset URLs to preload
|
|
43476
|
+
* @returns Promise that resolves when all assets are loaded
|
|
43477
|
+
*/
|
|
43478
|
+
async preload(urls) {
|
|
43479
|
+
const promises = urls.map((url) => {
|
|
43480
|
+
if (url.endsWith(".glb") || url.endsWith(".gltf")) {
|
|
43481
|
+
return this.loadModel(url).catch(() => null);
|
|
43482
|
+
} else if (url.endsWith(".obj")) {
|
|
43483
|
+
return this.loadOBJ(url).catch(() => null);
|
|
43484
|
+
} else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
|
|
43485
|
+
return this.loadTexture(url).catch(() => null);
|
|
43486
|
+
}
|
|
43487
|
+
return Promise.resolve(null);
|
|
43488
|
+
});
|
|
43489
|
+
await Promise.all(promises);
|
|
43490
|
+
}
|
|
43491
|
+
/**
|
|
43492
|
+
* Check if a model is cached
|
|
43493
|
+
* @param url - Model URL
|
|
43494
|
+
*/
|
|
43495
|
+
hasModel(url) {
|
|
43496
|
+
return this.modelCache.has(url);
|
|
43497
|
+
}
|
|
43498
|
+
/**
|
|
43499
|
+
* Check if a texture is cached
|
|
43500
|
+
* @param url - Texture URL
|
|
43501
|
+
*/
|
|
43502
|
+
hasTexture(url) {
|
|
43503
|
+
return this.textureCache.has(url);
|
|
43504
|
+
}
|
|
43505
|
+
/**
|
|
43506
|
+
* Get cached model (throws if not cached)
|
|
43507
|
+
* @param url - Model URL
|
|
43508
|
+
*/
|
|
43509
|
+
getModel(url) {
|
|
43510
|
+
const model = this.modelCache.get(url);
|
|
43511
|
+
if (!model) {
|
|
43512
|
+
throw new Error(`Model ${url} not in cache`);
|
|
43513
|
+
}
|
|
43514
|
+
return model;
|
|
43515
|
+
}
|
|
43516
|
+
/**
|
|
43517
|
+
* Get cached texture (throws if not cached)
|
|
43518
|
+
* @param url - Texture URL
|
|
43519
|
+
*/
|
|
43520
|
+
getTexture(url) {
|
|
43521
|
+
const texture = this.textureCache.get(url);
|
|
43522
|
+
if (!texture) {
|
|
43523
|
+
throw new Error(`Texture ${url} not in cache`);
|
|
43524
|
+
}
|
|
43525
|
+
return texture;
|
|
43526
|
+
}
|
|
43527
|
+
/**
|
|
43528
|
+
* Clear all caches
|
|
43529
|
+
*/
|
|
43530
|
+
clearCache() {
|
|
43531
|
+
this.textureCache.forEach((texture) => {
|
|
43532
|
+
texture.dispose();
|
|
43533
|
+
});
|
|
43534
|
+
this.modelCache.forEach((model) => {
|
|
43535
|
+
model.scene.traverse((child) => {
|
|
43536
|
+
if (child instanceof THREE__namespace.Mesh) {
|
|
43537
|
+
child.geometry.dispose();
|
|
43538
|
+
if (Array.isArray(child.material)) {
|
|
43539
|
+
child.material.forEach((m) => m.dispose());
|
|
43540
|
+
} else {
|
|
43541
|
+
child.material.dispose();
|
|
43542
|
+
}
|
|
43543
|
+
}
|
|
43544
|
+
});
|
|
43545
|
+
});
|
|
43546
|
+
this.modelCache.clear();
|
|
43547
|
+
this.textureCache.clear();
|
|
43548
|
+
this.loadingPromises.clear();
|
|
43549
|
+
}
|
|
43550
|
+
/**
|
|
43551
|
+
* Get cache statistics
|
|
43552
|
+
*/
|
|
43553
|
+
getStats() {
|
|
43554
|
+
return {
|
|
43555
|
+
models: this.modelCache.size,
|
|
43556
|
+
textures: this.textureCache.size,
|
|
43557
|
+
loading: this.loadingPromises.size
|
|
43558
|
+
};
|
|
43559
|
+
}
|
|
43560
|
+
};
|
|
43561
|
+
new AssetLoader();
|
|
43562
|
+
}
|
|
43563
|
+
});
|
|
43564
|
+
function useAssetLoader(options = {}) {
|
|
43565
|
+
const { preloadUrls = [], loader: customLoader } = options;
|
|
43566
|
+
const loaderRef = React91.useRef(customLoader || new AssetLoader());
|
|
43567
|
+
const [state, setState] = React91.useState({
|
|
43568
|
+
isLoading: false,
|
|
43569
|
+
progress: 0,
|
|
43570
|
+
loaded: 0,
|
|
43571
|
+
total: 0,
|
|
43572
|
+
errors: []
|
|
43573
|
+
});
|
|
43574
|
+
React91.useEffect(() => {
|
|
43575
|
+
if (preloadUrls.length > 0) {
|
|
43576
|
+
preload(preloadUrls);
|
|
43577
|
+
}
|
|
43578
|
+
}, []);
|
|
43579
|
+
const updateProgress = React91.useCallback((loaded, total) => {
|
|
43580
|
+
setState((prev) => ({
|
|
43581
|
+
...prev,
|
|
43582
|
+
loaded,
|
|
43583
|
+
total,
|
|
43584
|
+
progress: total > 0 ? Math.round(loaded / total * 100) : 0
|
|
43585
|
+
}));
|
|
43586
|
+
}, []);
|
|
43587
|
+
const loadModel = React91.useCallback(
|
|
43588
|
+
async (url) => {
|
|
43589
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
43590
|
+
try {
|
|
43591
|
+
const model = await loaderRef.current.loadModel(url);
|
|
43592
|
+
setState((prev) => ({
|
|
43593
|
+
...prev,
|
|
43594
|
+
isLoading: false,
|
|
43595
|
+
loaded: prev.loaded + 1
|
|
43596
|
+
}));
|
|
43597
|
+
return model;
|
|
43598
|
+
} catch (error) {
|
|
43599
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
43600
|
+
setState((prev) => ({
|
|
43601
|
+
...prev,
|
|
43602
|
+
isLoading: false,
|
|
43603
|
+
errors: [...prev.errors, errorMsg]
|
|
43604
|
+
}));
|
|
43605
|
+
throw error;
|
|
43606
|
+
}
|
|
43607
|
+
},
|
|
43608
|
+
[]
|
|
43609
|
+
);
|
|
43610
|
+
const loadOBJ = React91.useCallback(
|
|
43611
|
+
async (url) => {
|
|
43612
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
43613
|
+
try {
|
|
43614
|
+
const model = await loaderRef.current.loadOBJ(url);
|
|
43615
|
+
setState((prev) => ({
|
|
43616
|
+
...prev,
|
|
43617
|
+
isLoading: false,
|
|
43618
|
+
loaded: prev.loaded + 1
|
|
43619
|
+
}));
|
|
43620
|
+
return model;
|
|
43621
|
+
} catch (error) {
|
|
43622
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
43623
|
+
setState((prev) => ({
|
|
43624
|
+
...prev,
|
|
43625
|
+
isLoading: false,
|
|
43626
|
+
errors: [...prev.errors, errorMsg]
|
|
43627
|
+
}));
|
|
43628
|
+
throw error;
|
|
43629
|
+
}
|
|
43630
|
+
},
|
|
43631
|
+
[]
|
|
43632
|
+
);
|
|
43633
|
+
const loadTexture = React91.useCallback(
|
|
43634
|
+
async (url) => {
|
|
43635
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
43636
|
+
try {
|
|
43637
|
+
const texture = await loaderRef.current.loadTexture(url);
|
|
43638
|
+
setState((prev) => ({
|
|
43639
|
+
...prev,
|
|
43640
|
+
isLoading: false,
|
|
43641
|
+
loaded: prev.loaded + 1
|
|
43642
|
+
}));
|
|
43643
|
+
return texture;
|
|
43644
|
+
} catch (error) {
|
|
43645
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
43646
|
+
setState((prev) => ({
|
|
43647
|
+
...prev,
|
|
43648
|
+
isLoading: false,
|
|
43649
|
+
errors: [...prev.errors, errorMsg]
|
|
43650
|
+
}));
|
|
43651
|
+
throw error;
|
|
43652
|
+
}
|
|
43653
|
+
},
|
|
43654
|
+
[]
|
|
43655
|
+
);
|
|
43656
|
+
const preload = React91.useCallback(
|
|
43657
|
+
async (urls) => {
|
|
43658
|
+
setState((prev) => ({
|
|
43659
|
+
...prev,
|
|
43660
|
+
isLoading: true,
|
|
43661
|
+
total: urls.length,
|
|
43662
|
+
loaded: 0,
|
|
43663
|
+
errors: []
|
|
43664
|
+
}));
|
|
43665
|
+
let completed = 0;
|
|
43666
|
+
const errors = [];
|
|
43667
|
+
await Promise.all(
|
|
43668
|
+
urls.map(async (url) => {
|
|
43669
|
+
try {
|
|
43670
|
+
if (url.endsWith(".glb") || url.endsWith(".gltf")) {
|
|
43671
|
+
await loaderRef.current.loadModel(url);
|
|
43672
|
+
} else if (url.endsWith(".obj")) {
|
|
43673
|
+
await loaderRef.current.loadOBJ(url);
|
|
43674
|
+
} else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
|
|
43675
|
+
await loaderRef.current.loadTexture(url);
|
|
43676
|
+
}
|
|
43677
|
+
completed++;
|
|
43678
|
+
updateProgress(completed, urls.length);
|
|
43679
|
+
} catch (error) {
|
|
43680
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
43681
|
+
errors.push(`${url}: ${errorMsg}`);
|
|
43682
|
+
completed++;
|
|
43683
|
+
updateProgress(completed, urls.length);
|
|
43684
|
+
}
|
|
43685
|
+
})
|
|
43686
|
+
);
|
|
43687
|
+
setState((prev) => ({
|
|
43688
|
+
...prev,
|
|
43689
|
+
isLoading: false,
|
|
43690
|
+
errors
|
|
43691
|
+
}));
|
|
43692
|
+
},
|
|
43693
|
+
[updateProgress]
|
|
43694
|
+
);
|
|
43695
|
+
const hasModel = React91.useCallback((url) => {
|
|
43696
|
+
return loaderRef.current.hasModel(url);
|
|
43697
|
+
}, []);
|
|
43698
|
+
const hasTexture = React91.useCallback((url) => {
|
|
43699
|
+
return loaderRef.current.hasTexture(url);
|
|
43700
|
+
}, []);
|
|
43701
|
+
const getModel = React91.useCallback((url) => {
|
|
43702
|
+
try {
|
|
43703
|
+
return loaderRef.current.getModel(url);
|
|
43704
|
+
} catch {
|
|
43705
|
+
return void 0;
|
|
43706
|
+
}
|
|
43707
|
+
}, []);
|
|
43708
|
+
const getTexture = React91.useCallback((url) => {
|
|
43709
|
+
try {
|
|
43710
|
+
return loaderRef.current.getTexture(url);
|
|
43711
|
+
} catch {
|
|
43712
|
+
return void 0;
|
|
43713
|
+
}
|
|
43714
|
+
}, []);
|
|
43715
|
+
const clearCache = React91.useCallback(() => {
|
|
43716
|
+
loaderRef.current.clearCache();
|
|
43717
|
+
setState({
|
|
43718
|
+
isLoading: false,
|
|
43719
|
+
progress: 0,
|
|
43720
|
+
loaded: 0,
|
|
43721
|
+
total: 0,
|
|
43722
|
+
errors: []
|
|
43723
|
+
});
|
|
43724
|
+
}, []);
|
|
43725
|
+
return {
|
|
43726
|
+
...state,
|
|
43727
|
+
loadModel,
|
|
43728
|
+
loadOBJ,
|
|
43729
|
+
loadTexture,
|
|
43730
|
+
preload,
|
|
43731
|
+
hasModel,
|
|
43732
|
+
hasTexture,
|
|
43733
|
+
getModel,
|
|
43734
|
+
getTexture,
|
|
43735
|
+
clearCache
|
|
43736
|
+
};
|
|
43737
|
+
}
|
|
43738
|
+
var init_useAssetLoader = __esm({
|
|
43739
|
+
"components/game/molecules/three/hooks/useAssetLoader.ts"() {
|
|
43740
|
+
"use client";
|
|
43741
|
+
init_AssetLoader();
|
|
43742
|
+
}
|
|
43743
|
+
});
|
|
43744
|
+
function useGameCanvas3DEvents(options) {
|
|
43745
|
+
const {
|
|
43746
|
+
tileClickEvent,
|
|
43747
|
+
unitClickEvent,
|
|
43748
|
+
featureClickEvent,
|
|
43749
|
+
canvasClickEvent,
|
|
43750
|
+
tileHoverEvent,
|
|
43751
|
+
tileLeaveEvent,
|
|
43752
|
+
unitAnimationEvent,
|
|
43753
|
+
cameraChangeEvent,
|
|
43754
|
+
onTileClick,
|
|
43755
|
+
onUnitClick,
|
|
43756
|
+
onFeatureClick,
|
|
43757
|
+
onCanvasClick,
|
|
43758
|
+
onTileHover,
|
|
43759
|
+
onUnitAnimation
|
|
43760
|
+
} = options;
|
|
43761
|
+
const emit = useEmitEvent();
|
|
43762
|
+
const optionsRef = React91.useRef(options);
|
|
43763
|
+
optionsRef.current = options;
|
|
43764
|
+
const handleTileClick = React91.useCallback(
|
|
43765
|
+
(tile, event) => {
|
|
43766
|
+
if (tileClickEvent) {
|
|
43767
|
+
emit(tileClickEvent, {
|
|
43768
|
+
tileId: tile.id,
|
|
43769
|
+
x: tile.x,
|
|
43770
|
+
z: tile.z ?? tile.y ?? 0,
|
|
43771
|
+
type: tile.type,
|
|
43772
|
+
terrain: tile.terrain,
|
|
43773
|
+
elevation: tile.elevation
|
|
43774
|
+
});
|
|
43775
|
+
}
|
|
43776
|
+
optionsRef.current.onTileClick?.(tile, event);
|
|
43777
|
+
},
|
|
43778
|
+
[tileClickEvent, emit]
|
|
43779
|
+
);
|
|
43780
|
+
const handleUnitClick = React91.useCallback(
|
|
43781
|
+
(unit, event) => {
|
|
43782
|
+
if (unitClickEvent) {
|
|
43783
|
+
emit(unitClickEvent, {
|
|
43784
|
+
unitId: unit.id,
|
|
43785
|
+
x: unit.x,
|
|
43786
|
+
z: unit.z ?? unit.y ?? 0,
|
|
43787
|
+
unitType: unit.unitType,
|
|
43788
|
+
name: unit.name,
|
|
43789
|
+
team: unit.team,
|
|
43790
|
+
faction: unit.faction,
|
|
43791
|
+
health: unit.health,
|
|
43792
|
+
maxHealth: unit.maxHealth
|
|
43793
|
+
});
|
|
43794
|
+
}
|
|
43795
|
+
optionsRef.current.onUnitClick?.(unit, event);
|
|
43796
|
+
},
|
|
43797
|
+
[unitClickEvent, emit]
|
|
43798
|
+
);
|
|
43799
|
+
const handleFeatureClick = React91.useCallback(
|
|
43800
|
+
(feature, event) => {
|
|
43801
|
+
if (featureClickEvent) {
|
|
43802
|
+
emit(featureClickEvent, {
|
|
43803
|
+
featureId: feature.id,
|
|
43804
|
+
x: feature.x,
|
|
43805
|
+
z: feature.z ?? feature.y ?? 0,
|
|
43806
|
+
type: feature.type,
|
|
43807
|
+
elevation: feature.elevation
|
|
43808
|
+
});
|
|
43809
|
+
}
|
|
43810
|
+
optionsRef.current.onFeatureClick?.(feature, event);
|
|
43811
|
+
},
|
|
43812
|
+
[featureClickEvent, emit]
|
|
43813
|
+
);
|
|
43814
|
+
const handleCanvasClick = React91.useCallback(
|
|
43815
|
+
(event) => {
|
|
43816
|
+
if (canvasClickEvent) {
|
|
43817
|
+
emit(canvasClickEvent, {
|
|
43818
|
+
clientX: event.clientX,
|
|
43819
|
+
clientY: event.clientY,
|
|
43820
|
+
button: event.button
|
|
43821
|
+
});
|
|
43822
|
+
}
|
|
43823
|
+
optionsRef.current.onCanvasClick?.(event);
|
|
43824
|
+
},
|
|
43825
|
+
[canvasClickEvent, emit]
|
|
43826
|
+
);
|
|
43827
|
+
const handleTileHover = React91.useCallback(
|
|
43828
|
+
(tile, event) => {
|
|
43829
|
+
if (tile) {
|
|
43830
|
+
if (tileHoverEvent) {
|
|
43831
|
+
emit(tileHoverEvent, {
|
|
43832
|
+
tileId: tile.id,
|
|
43833
|
+
x: tile.x,
|
|
43834
|
+
z: tile.z ?? tile.y ?? 0,
|
|
43835
|
+
type: tile.type
|
|
43836
|
+
});
|
|
43837
|
+
}
|
|
43838
|
+
} else {
|
|
43839
|
+
if (tileLeaveEvent) {
|
|
43840
|
+
emit(tileLeaveEvent, {});
|
|
43841
|
+
}
|
|
43842
|
+
}
|
|
43843
|
+
optionsRef.current.onTileHover?.(tile, event);
|
|
43844
|
+
},
|
|
43845
|
+
[tileHoverEvent, tileLeaveEvent, emit]
|
|
43846
|
+
);
|
|
43847
|
+
const handleUnitAnimation = React91.useCallback(
|
|
43848
|
+
(unitId, state) => {
|
|
43849
|
+
if (unitAnimationEvent) {
|
|
43850
|
+
emit(unitAnimationEvent, {
|
|
43851
|
+
unitId,
|
|
43852
|
+
state,
|
|
43853
|
+
timestamp: Date.now()
|
|
43854
|
+
});
|
|
43855
|
+
}
|
|
43856
|
+
optionsRef.current.onUnitAnimation?.(unitId, state);
|
|
43857
|
+
},
|
|
43858
|
+
[unitAnimationEvent, emit]
|
|
43859
|
+
);
|
|
43860
|
+
const handleCameraChange = React91.useCallback(
|
|
43861
|
+
(position) => {
|
|
43862
|
+
if (cameraChangeEvent) {
|
|
43863
|
+
emit(cameraChangeEvent, {
|
|
43864
|
+
position,
|
|
43865
|
+
timestamp: Date.now()
|
|
43866
|
+
});
|
|
43867
|
+
}
|
|
43868
|
+
},
|
|
43869
|
+
[cameraChangeEvent, emit]
|
|
43870
|
+
);
|
|
43871
|
+
return {
|
|
43872
|
+
handleTileClick,
|
|
43873
|
+
handleUnitClick,
|
|
43874
|
+
handleFeatureClick,
|
|
43875
|
+
handleCanvasClick,
|
|
43876
|
+
handleTileHover,
|
|
43877
|
+
handleUnitAnimation,
|
|
43878
|
+
handleCameraChange
|
|
43879
|
+
};
|
|
43880
|
+
}
|
|
43881
|
+
var init_useGameCanvas3DEvents = __esm({
|
|
43882
|
+
"components/game/molecules/three/hooks/useGameCanvas3DEvents.ts"() {
|
|
43883
|
+
"use client";
|
|
43884
|
+
init_useEventBus();
|
|
43885
|
+
}
|
|
43886
|
+
});
|
|
43887
|
+
|
|
43888
|
+
// components/game/molecules/three/components/Canvas3DLoadingState.css
|
|
43889
|
+
var init_Canvas3DLoadingState = __esm({
|
|
43890
|
+
"components/game/molecules/three/components/Canvas3DLoadingState.css"() {
|
|
43891
|
+
}
|
|
43892
|
+
});
|
|
43893
|
+
function Canvas3DLoadingState({
|
|
43894
|
+
progress = 0,
|
|
43895
|
+
loaded = 0,
|
|
43896
|
+
total = 0,
|
|
43897
|
+
message = "Loading 3D Scene...",
|
|
43898
|
+
details,
|
|
43899
|
+
showSpinner = true,
|
|
43900
|
+
className
|
|
43901
|
+
}) {
|
|
43902
|
+
const clampedProgress = Math.max(0, Math.min(100, progress));
|
|
43903
|
+
const hasProgress = total > 0;
|
|
43904
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `canvas-3d-loading ${className || ""}`, children: [
|
|
43905
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__content", children: [
|
|
43906
|
+
showSpinner && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__spinner", children: [
|
|
43907
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "spinner__ring" }),
|
|
43908
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "spinner__ring spinner__ring--secondary" })
|
|
43909
|
+
] }),
|
|
43910
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__message", children: message }),
|
|
43911
|
+
details && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__details", children: details }),
|
|
43912
|
+
hasProgress && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__progress", children: [
|
|
43913
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "progress__bar", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
43914
|
+
"div",
|
|
43915
|
+
{
|
|
43916
|
+
className: "progress__fill",
|
|
43917
|
+
style: { width: `${clampedProgress}%` }
|
|
43918
|
+
}
|
|
43919
|
+
) }),
|
|
43920
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "progress__text", children: [
|
|
43921
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "progress__percentage", children: [
|
|
43922
|
+
clampedProgress,
|
|
43923
|
+
"%"
|
|
43924
|
+
] }),
|
|
43925
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "progress__count", children: [
|
|
43926
|
+
"(",
|
|
43927
|
+
loaded,
|
|
43928
|
+
"/",
|
|
43929
|
+
total,
|
|
43930
|
+
")"
|
|
43931
|
+
] })
|
|
43932
|
+
] })
|
|
43933
|
+
] })
|
|
43934
|
+
] }),
|
|
43935
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__background", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg__grid" }) })
|
|
43936
|
+
] });
|
|
43937
|
+
}
|
|
43938
|
+
var init_Canvas3DLoadingState2 = __esm({
|
|
43939
|
+
"components/game/molecules/three/components/Canvas3DLoadingState.tsx"() {
|
|
43940
|
+
init_Canvas3DLoadingState();
|
|
43941
|
+
}
|
|
43942
|
+
});
|
|
43943
|
+
|
|
43944
|
+
// components/game/molecules/three/components/Canvas3DErrorBoundary.css
|
|
43945
|
+
var init_Canvas3DErrorBoundary = __esm({
|
|
43946
|
+
"components/game/molecules/three/components/Canvas3DErrorBoundary.css"() {
|
|
43947
|
+
}
|
|
43948
|
+
});
|
|
43949
|
+
var log9, Canvas3DErrorBoundary;
|
|
43950
|
+
var init_Canvas3DErrorBoundary2 = __esm({
|
|
43951
|
+
"components/game/molecules/three/components/Canvas3DErrorBoundary.tsx"() {
|
|
43952
|
+
init_Canvas3DErrorBoundary();
|
|
43953
|
+
log9 = logger.createLogger("almadar:ui:game:canvas3d:error-boundary");
|
|
43954
|
+
Canvas3DErrorBoundary = class extends React91.Component {
|
|
43955
|
+
constructor(props) {
|
|
43956
|
+
super(props);
|
|
43957
|
+
__publicField(this, "handleReset", () => {
|
|
43958
|
+
this.setState({
|
|
43959
|
+
hasError: false,
|
|
43960
|
+
error: null,
|
|
43961
|
+
errorInfo: null
|
|
43962
|
+
});
|
|
43963
|
+
this.props.onReset?.();
|
|
43964
|
+
});
|
|
43965
|
+
this.state = {
|
|
43966
|
+
hasError: false,
|
|
43967
|
+
error: null,
|
|
43968
|
+
errorInfo: null
|
|
43969
|
+
};
|
|
43970
|
+
}
|
|
43971
|
+
static getDerivedStateFromError(error) {
|
|
43972
|
+
return {
|
|
43973
|
+
hasError: true,
|
|
43974
|
+
error,
|
|
43975
|
+
errorInfo: null
|
|
43976
|
+
};
|
|
43977
|
+
}
|
|
43978
|
+
componentDidCatch(error, errorInfo) {
|
|
43979
|
+
this.setState({ errorInfo });
|
|
43980
|
+
this.props.onError?.(error, errorInfo);
|
|
43981
|
+
log9.error("Error caught", { error });
|
|
43982
|
+
log9.error("Component stack", { componentStack: errorInfo.componentStack ?? "<none>" });
|
|
43983
|
+
}
|
|
43984
|
+
render() {
|
|
43985
|
+
if (this.state.hasError) {
|
|
43986
|
+
if (this.props.fallback) {
|
|
43987
|
+
return this.props.fallback;
|
|
43988
|
+
}
|
|
43989
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-error", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-error__content", children: [
|
|
43990
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-error__icon", children: "\u26A0\uFE0F" }),
|
|
43991
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "canvas-3d-error__title", children: "3D Scene Error" }),
|
|
43992
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "canvas-3d-error__message", children: "Something went wrong while rendering the 3D scene." }),
|
|
43993
|
+
this.state.error && /* @__PURE__ */ jsxRuntime.jsxs("details", { className: "canvas-3d-error__details", children: [
|
|
43994
|
+
/* @__PURE__ */ jsxRuntime.jsx("summary", { children: "Error Details" }),
|
|
43995
|
+
/* @__PURE__ */ jsxRuntime.jsxs("pre", { className: "error__stack", children: [
|
|
43996
|
+
this.state.error.message,
|
|
43997
|
+
"\n",
|
|
43998
|
+
this.state.error.stack
|
|
43999
|
+
] }),
|
|
44000
|
+
this.state.errorInfo && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "error__component-stack", children: this.state.errorInfo.componentStack })
|
|
44001
|
+
] }),
|
|
44002
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-error__actions", children: [
|
|
44003
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44004
|
+
"button",
|
|
44005
|
+
{
|
|
44006
|
+
className: "error__button error__button--primary",
|
|
44007
|
+
onClick: this.handleReset,
|
|
44008
|
+
children: "Try Again"
|
|
44009
|
+
}
|
|
44010
|
+
),
|
|
44011
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44012
|
+
"button",
|
|
44013
|
+
{
|
|
44014
|
+
className: "error__button error__button--secondary",
|
|
44015
|
+
onClick: () => window.location.reload(),
|
|
44016
|
+
children: "Reload Page"
|
|
44017
|
+
}
|
|
44018
|
+
)
|
|
44019
|
+
] })
|
|
44020
|
+
] }) });
|
|
44021
|
+
}
|
|
44022
|
+
return this.props.children;
|
|
44023
|
+
}
|
|
44024
|
+
};
|
|
44025
|
+
}
|
|
44026
|
+
});
|
|
44027
|
+
function detectAssetRoot2(modelUrl) {
|
|
44028
|
+
const idx = modelUrl.indexOf("/3d/");
|
|
44029
|
+
if (idx !== -1) {
|
|
44030
|
+
return modelUrl.substring(0, idx + 4);
|
|
44031
|
+
}
|
|
44032
|
+
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
44033
|
+
}
|
|
44034
|
+
function useGLTFModel(url, resourceBasePath) {
|
|
44035
|
+
const [state, setState] = React91.useState({
|
|
44036
|
+
model: null,
|
|
44037
|
+
isLoading: false,
|
|
44038
|
+
error: null
|
|
44039
|
+
});
|
|
44040
|
+
React91.useEffect(() => {
|
|
44041
|
+
if (!url) {
|
|
44042
|
+
setState({ model: null, isLoading: false, error: null });
|
|
44043
|
+
return;
|
|
44044
|
+
}
|
|
44045
|
+
log10.debug("Loading", { url });
|
|
44046
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
44047
|
+
const assetRoot = resourceBasePath || detectAssetRoot2(url);
|
|
44048
|
+
const loader = new GLTFLoader.GLTFLoader();
|
|
44049
|
+
loader.setResourcePath(assetRoot);
|
|
44050
|
+
loader.load(
|
|
44051
|
+
url,
|
|
44052
|
+
(gltf) => {
|
|
44053
|
+
log10.debug("Loaded", { url });
|
|
44054
|
+
setState({
|
|
44055
|
+
model: gltf.scene,
|
|
44056
|
+
isLoading: false,
|
|
44057
|
+
error: null
|
|
44058
|
+
});
|
|
44059
|
+
},
|
|
44060
|
+
void 0,
|
|
44061
|
+
(err) => {
|
|
44062
|
+
log10.warn("Failed", { url, error: err instanceof Error ? err : String(err) });
|
|
44063
|
+
setState({
|
|
44064
|
+
model: null,
|
|
44065
|
+
isLoading: false,
|
|
44066
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
44067
|
+
});
|
|
44068
|
+
}
|
|
44069
|
+
);
|
|
44070
|
+
}, [url, resourceBasePath]);
|
|
44071
|
+
return state;
|
|
44072
|
+
}
|
|
44073
|
+
function ModelLoader({
|
|
44074
|
+
url,
|
|
44075
|
+
position = [0, 0, 0],
|
|
44076
|
+
scale = 1,
|
|
44077
|
+
rotation = [0, 0, 0],
|
|
44078
|
+
isSelected = false,
|
|
44079
|
+
isHovered = false,
|
|
44080
|
+
onClick,
|
|
44081
|
+
onHover,
|
|
44082
|
+
fallbackGeometry = "box",
|
|
44083
|
+
castShadow = true,
|
|
44084
|
+
receiveShadow = true,
|
|
44085
|
+
resourceBasePath
|
|
44086
|
+
}) {
|
|
44087
|
+
const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
|
|
44088
|
+
const model = React91.useMemo(() => {
|
|
44089
|
+
if (!loadedModel) return null;
|
|
44090
|
+
const cloned = loadedModel.clone();
|
|
44091
|
+
cloned.traverse((child) => {
|
|
44092
|
+
if (child instanceof THREE__namespace.Mesh) {
|
|
44093
|
+
child.castShadow = castShadow;
|
|
44094
|
+
child.receiveShadow = receiveShadow;
|
|
44095
|
+
}
|
|
44096
|
+
});
|
|
44097
|
+
return cloned;
|
|
44098
|
+
}, [loadedModel, castShadow, receiveShadow]);
|
|
44099
|
+
const scaleArray = React91.useMemo(() => {
|
|
44100
|
+
if (typeof scale === "number") {
|
|
44101
|
+
return [scale, scale, scale];
|
|
44102
|
+
}
|
|
44103
|
+
return scale;
|
|
44104
|
+
}, [scale]);
|
|
44105
|
+
const rotationRad = React91.useMemo(() => {
|
|
44106
|
+
return [
|
|
44107
|
+
rotation[0] * Math.PI / 180,
|
|
44108
|
+
rotation[1] * Math.PI / 180,
|
|
44109
|
+
rotation[2] * Math.PI / 180
|
|
44110
|
+
];
|
|
44111
|
+
}, [rotation]);
|
|
44112
|
+
if (isLoading) {
|
|
44113
|
+
return /* @__PURE__ */ jsxRuntime.jsx("group", { position, children: /* @__PURE__ */ jsxRuntime.jsxs("mesh", { rotation: [Math.PI / 2, 0, 0], children: [
|
|
44114
|
+
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.3, 0.35, 16] }),
|
|
44115
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#4a90d9", transparent: true, opacity: 0.8 })
|
|
44116
|
+
] }) });
|
|
44117
|
+
}
|
|
44118
|
+
if (error || !model) {
|
|
44119
|
+
if (fallbackGeometry === "none") {
|
|
44120
|
+
return /* @__PURE__ */ jsxRuntime.jsx("group", { position });
|
|
44121
|
+
}
|
|
44122
|
+
const fallbackProps = {
|
|
44123
|
+
onClick,
|
|
44124
|
+
onPointerOver: () => onHover?.(true),
|
|
44125
|
+
onPointerOut: () => onHover?.(false)
|
|
44126
|
+
};
|
|
44127
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("group", { position, children: [
|
|
44128
|
+
(isSelected || isHovered) && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
44129
|
+
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
|
|
44130
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44131
|
+
"meshBasicMaterial",
|
|
44132
|
+
{
|
|
44133
|
+
color: isSelected ? 16755200 : 16777215,
|
|
44134
|
+
transparent: true,
|
|
44135
|
+
opacity: 0.5
|
|
44136
|
+
}
|
|
44137
|
+
)
|
|
44138
|
+
] }),
|
|
44139
|
+
fallbackGeometry === "box" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
44140
|
+
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.8, 0.8, 0.8] }),
|
|
44141
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
44142
|
+
] }),
|
|
44143
|
+
fallbackGeometry === "sphere" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
44144
|
+
/* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [0.4, 16, 16] }),
|
|
44145
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
44146
|
+
] }),
|
|
44147
|
+
fallbackGeometry === "cylinder" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
44148
|
+
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.3, 0.3, 0.8, 16] }),
|
|
44149
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
44150
|
+
] })
|
|
44151
|
+
] });
|
|
44152
|
+
}
|
|
44153
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
44154
|
+
"group",
|
|
44155
|
+
{
|
|
44156
|
+
position,
|
|
44157
|
+
rotation: rotationRad,
|
|
44158
|
+
onClick,
|
|
44159
|
+
onPointerOver: () => onHover?.(true),
|
|
44160
|
+
onPointerOut: () => onHover?.(false),
|
|
44161
|
+
children: [
|
|
44162
|
+
(isSelected || isHovered) && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
44163
|
+
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
|
|
44164
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44165
|
+
"meshBasicMaterial",
|
|
44166
|
+
{
|
|
44167
|
+
color: isSelected ? 16755200 : 16777215,
|
|
44168
|
+
transparent: true,
|
|
44169
|
+
opacity: 0.5
|
|
44170
|
+
}
|
|
44171
|
+
)
|
|
44172
|
+
] }),
|
|
44173
|
+
/* @__PURE__ */ jsxRuntime.jsx("primitive", { object: model, scale: scaleArray })
|
|
44174
|
+
]
|
|
44175
|
+
}
|
|
44176
|
+
);
|
|
44177
|
+
}
|
|
44178
|
+
var log10;
|
|
44179
|
+
var init_ModelLoader = __esm({
|
|
44180
|
+
"components/game/molecules/three/components/ModelLoader.tsx"() {
|
|
44181
|
+
"use client";
|
|
44182
|
+
log10 = logger.createLogger("almadar:ui:game:model-loader");
|
|
44183
|
+
}
|
|
44184
|
+
});
|
|
44185
|
+
|
|
44186
|
+
// components/game/molecules/GameCanvas3D.css
|
|
44187
|
+
var init_GameCanvas3D = __esm({
|
|
44188
|
+
"components/game/molecules/GameCanvas3D.css"() {
|
|
44189
|
+
}
|
|
44190
|
+
});
|
|
44191
|
+
function CameraController({
|
|
44192
|
+
onCameraChange
|
|
44193
|
+
}) {
|
|
44194
|
+
const { camera } = fiber.useThree();
|
|
44195
|
+
React91.useEffect(() => {
|
|
44196
|
+
if (onCameraChange) {
|
|
44197
|
+
onCameraChange({
|
|
44198
|
+
x: camera.position.x,
|
|
44199
|
+
y: camera.position.y,
|
|
44200
|
+
z: camera.position.z
|
|
44201
|
+
});
|
|
44202
|
+
}
|
|
44203
|
+
}, [camera.position, onCameraChange]);
|
|
44204
|
+
return null;
|
|
44205
|
+
}
|
|
44206
|
+
var DEFAULT_GRID_CONFIG, GameCanvas3D;
|
|
44207
|
+
var init_GameCanvas3D2 = __esm({
|
|
44208
|
+
"components/game/molecules/GameCanvas3D.tsx"() {
|
|
44209
|
+
"use client";
|
|
44210
|
+
init_AssetLoader();
|
|
44211
|
+
init_useAssetLoader();
|
|
44212
|
+
init_useGameCanvas3DEvents();
|
|
44213
|
+
init_Canvas3DLoadingState2();
|
|
44214
|
+
init_Canvas3DErrorBoundary2();
|
|
44215
|
+
init_ModelLoader();
|
|
44216
|
+
init_cn();
|
|
44217
|
+
init_GameCanvas3D();
|
|
44218
|
+
DEFAULT_GRID_CONFIG = {
|
|
44219
|
+
cellSize: 1,
|
|
44220
|
+
offsetX: 0,
|
|
44221
|
+
offsetZ: 0
|
|
44222
|
+
};
|
|
44223
|
+
GameCanvas3D = React91.forwardRef(
|
|
44224
|
+
({
|
|
44225
|
+
tiles = [],
|
|
44226
|
+
units = [],
|
|
44227
|
+
features = [],
|
|
44228
|
+
events: events2 = [],
|
|
44229
|
+
orientation = "standard",
|
|
44230
|
+
cameraMode = "isometric",
|
|
44231
|
+
showGrid = true,
|
|
44232
|
+
showCoordinates = false,
|
|
44233
|
+
showTileInfo = false,
|
|
44234
|
+
overlay = "default",
|
|
44235
|
+
shadows = true,
|
|
44236
|
+
backgroundColor = "#1a1a2e",
|
|
44237
|
+
onTileClick,
|
|
44238
|
+
onUnitClick,
|
|
44239
|
+
onFeatureClick,
|
|
44240
|
+
onCanvasClick,
|
|
44241
|
+
onTileHover,
|
|
44242
|
+
onUnitAnimation,
|
|
44243
|
+
assetLoader: customAssetLoader,
|
|
44244
|
+
tileRenderer: CustomTileRenderer,
|
|
44245
|
+
unitRenderer: CustomUnitRenderer,
|
|
44246
|
+
featureRenderer: CustomFeatureRenderer,
|
|
44247
|
+
className,
|
|
44248
|
+
isLoading: externalLoading,
|
|
44249
|
+
error: externalError,
|
|
44250
|
+
entity,
|
|
44251
|
+
preloadAssets = [],
|
|
44252
|
+
tileClickEvent,
|
|
44253
|
+
unitClickEvent,
|
|
44254
|
+
featureClickEvent,
|
|
44255
|
+
canvasClickEvent,
|
|
44256
|
+
tileHoverEvent,
|
|
44257
|
+
tileLeaveEvent,
|
|
44258
|
+
unitAnimationEvent,
|
|
44259
|
+
cameraChangeEvent,
|
|
44260
|
+
loadingMessage = "Loading 3D Scene...",
|
|
44261
|
+
useInstancing = true,
|
|
44262
|
+
validMoves = [],
|
|
44263
|
+
attackTargets = [],
|
|
44264
|
+
selectedTileIds = [],
|
|
44265
|
+
selectedUnitId = null,
|
|
44266
|
+
children
|
|
44267
|
+
}, ref) => {
|
|
44268
|
+
const containerRef = React91.useRef(null);
|
|
44269
|
+
const controlsRef = React91.useRef(null);
|
|
44270
|
+
const [hoveredTile, setHoveredTile] = React91.useState(null);
|
|
44271
|
+
const [internalError, setInternalError] = React91.useState(null);
|
|
44272
|
+
const { isLoading: assetsLoading, progress, loaded, total } = useAssetLoader({
|
|
44273
|
+
preloadUrls: preloadAssets,
|
|
44274
|
+
loader: customAssetLoader
|
|
44275
|
+
});
|
|
44276
|
+
const eventHandlers = useGameCanvas3DEvents({
|
|
44277
|
+
tileClickEvent,
|
|
44278
|
+
unitClickEvent,
|
|
44279
|
+
featureClickEvent,
|
|
44280
|
+
canvasClickEvent,
|
|
44281
|
+
tileHoverEvent,
|
|
44282
|
+
tileLeaveEvent,
|
|
44283
|
+
unitAnimationEvent,
|
|
44284
|
+
cameraChangeEvent,
|
|
44285
|
+
onTileClick,
|
|
44286
|
+
onUnitClick,
|
|
44287
|
+
onFeatureClick,
|
|
44288
|
+
onCanvasClick,
|
|
44289
|
+
onTileHover,
|
|
44290
|
+
onUnitAnimation
|
|
44291
|
+
});
|
|
44292
|
+
const gridBounds = React91.useMemo(() => {
|
|
44293
|
+
if (tiles.length === 0) {
|
|
44294
|
+
return { minX: 0, maxX: 10, minZ: 0, maxZ: 10 };
|
|
44295
|
+
}
|
|
44296
|
+
const xs = tiles.map((t) => t.x);
|
|
44297
|
+
const zs = tiles.map((t) => t.z || t.y || 0);
|
|
44298
|
+
return {
|
|
44299
|
+
minX: Math.min(...xs),
|
|
44300
|
+
maxX: Math.max(...xs),
|
|
44301
|
+
minZ: Math.min(...zs),
|
|
44302
|
+
maxZ: Math.max(...zs)
|
|
44303
|
+
};
|
|
44304
|
+
}, [tiles]);
|
|
44305
|
+
const cameraTarget = React91.useMemo(() => {
|
|
44306
|
+
return [
|
|
44307
|
+
(gridBounds.minX + gridBounds.maxX) / 2,
|
|
44308
|
+
0,
|
|
44309
|
+
(gridBounds.minZ + gridBounds.maxZ) / 2
|
|
44310
|
+
];
|
|
44311
|
+
}, [gridBounds]);
|
|
44312
|
+
const gridConfig = React91.useMemo(
|
|
44313
|
+
() => ({
|
|
44314
|
+
...DEFAULT_GRID_CONFIG,
|
|
44315
|
+
offsetX: -(gridBounds.maxX - gridBounds.minX) / 2,
|
|
44316
|
+
offsetZ: -(gridBounds.maxZ - gridBounds.minZ) / 2
|
|
44317
|
+
}),
|
|
44318
|
+
[gridBounds]
|
|
44319
|
+
);
|
|
44320
|
+
const gridToWorld = React91.useCallback(
|
|
44321
|
+
(x, z, y = 0) => {
|
|
44322
|
+
const worldX = (x - gridBounds.minX) * gridConfig.cellSize;
|
|
44323
|
+
const worldZ = (z - gridBounds.minZ) * gridConfig.cellSize;
|
|
44324
|
+
return [worldX, y * gridConfig.cellSize, worldZ];
|
|
44325
|
+
},
|
|
44326
|
+
[gridBounds, gridConfig]
|
|
44327
|
+
);
|
|
44328
|
+
React91.useImperativeHandle(ref, () => ({
|
|
44329
|
+
getCameraPosition: () => {
|
|
44330
|
+
if (controlsRef.current) {
|
|
44331
|
+
const pos = controlsRef.current.object.position;
|
|
44332
|
+
return new THREE__namespace.Vector3(pos.x, pos.y, pos.z);
|
|
44333
|
+
}
|
|
44334
|
+
return null;
|
|
44335
|
+
},
|
|
44336
|
+
setCameraPosition: (x, y, z) => {
|
|
44337
|
+
if (controlsRef.current) {
|
|
44338
|
+
controlsRef.current.object.position.set(x, y, z);
|
|
44339
|
+
controlsRef.current.update();
|
|
44340
|
+
}
|
|
44341
|
+
},
|
|
44342
|
+
lookAt: (x, y, z) => {
|
|
44343
|
+
if (controlsRef.current) {
|
|
44344
|
+
controlsRef.current.target.set(x, y, z);
|
|
44345
|
+
controlsRef.current.update();
|
|
44346
|
+
}
|
|
44347
|
+
},
|
|
44348
|
+
resetCamera: () => {
|
|
44349
|
+
if (controlsRef.current) {
|
|
44350
|
+
controlsRef.current.reset();
|
|
44351
|
+
}
|
|
44352
|
+
},
|
|
44353
|
+
screenshot: () => {
|
|
44354
|
+
const canvas = containerRef.current?.querySelector("canvas");
|
|
44355
|
+
if (canvas) {
|
|
44356
|
+
return canvas.toDataURL("image/png");
|
|
44357
|
+
}
|
|
44358
|
+
return null;
|
|
44359
|
+
},
|
|
44360
|
+
export: () => ({
|
|
44361
|
+
tiles,
|
|
44362
|
+
units,
|
|
44363
|
+
features
|
|
44364
|
+
})
|
|
44365
|
+
}));
|
|
44366
|
+
const handleTileClick = React91.useCallback(
|
|
44367
|
+
(tile, event) => {
|
|
44368
|
+
eventHandlers.handleTileClick(tile, event);
|
|
44369
|
+
},
|
|
44370
|
+
[eventHandlers]
|
|
44371
|
+
);
|
|
44372
|
+
const handleUnitClick = React91.useCallback(
|
|
44373
|
+
(unit, event) => {
|
|
44374
|
+
eventHandlers.handleUnitClick(unit, event);
|
|
44375
|
+
},
|
|
44376
|
+
[eventHandlers]
|
|
44377
|
+
);
|
|
44378
|
+
const handleFeatureClick = React91.useCallback(
|
|
44379
|
+
(feature, event) => {
|
|
44380
|
+
if (event) {
|
|
44381
|
+
eventHandlers.handleFeatureClick(feature, event);
|
|
44382
|
+
}
|
|
44383
|
+
},
|
|
44384
|
+
[eventHandlers]
|
|
44385
|
+
);
|
|
44386
|
+
const handleTileHover = React91.useCallback(
|
|
44387
|
+
(tile, event) => {
|
|
44388
|
+
setHoveredTile(tile);
|
|
44389
|
+
if (event) {
|
|
44390
|
+
eventHandlers.handleTileHover(tile, event);
|
|
44391
|
+
}
|
|
44392
|
+
},
|
|
44393
|
+
[eventHandlers]
|
|
44394
|
+
);
|
|
44395
|
+
const cameraConfig = React91.useMemo(() => {
|
|
44396
|
+
const size = Math.max(
|
|
44397
|
+
gridBounds.maxX - gridBounds.minX,
|
|
44398
|
+
gridBounds.maxZ - gridBounds.minZ
|
|
44399
|
+
);
|
|
44400
|
+
const distance = size * 1.5;
|
|
44401
|
+
switch (cameraMode) {
|
|
44402
|
+
case "isometric":
|
|
44403
|
+
return {
|
|
44404
|
+
position: [distance, distance * 0.8, distance],
|
|
44405
|
+
fov: 45
|
|
44406
|
+
};
|
|
44407
|
+
case "top-down":
|
|
44408
|
+
return {
|
|
44409
|
+
position: [0, distance * 2, 0],
|
|
44410
|
+
fov: 45
|
|
44411
|
+
};
|
|
44412
|
+
case "perspective":
|
|
44413
|
+
default:
|
|
44414
|
+
return {
|
|
44415
|
+
position: [distance, distance, distance],
|
|
44416
|
+
fov: 45
|
|
44417
|
+
};
|
|
44418
|
+
}
|
|
44419
|
+
}, [cameraMode, gridBounds]);
|
|
44420
|
+
const DefaultTileRenderer = React91.useCallback(
|
|
44421
|
+
({ tile, position }) => {
|
|
44422
|
+
const isSelected = tile.id ? selectedTileIds.includes(tile.id) : false;
|
|
44423
|
+
const isHovered = hoveredTile?.id === tile.id;
|
|
44424
|
+
const isValidMove = validMoves.some(
|
|
44425
|
+
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
44426
|
+
);
|
|
44427
|
+
const isAttackTarget = attackTargets.some(
|
|
44428
|
+
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
44429
|
+
);
|
|
44430
|
+
let color = 8421504;
|
|
44431
|
+
if (tile.type === "water") color = 4491468;
|
|
44432
|
+
else if (tile.type === "grass") color = 4500036;
|
|
44433
|
+
else if (tile.type === "sand") color = 14535816;
|
|
44434
|
+
else if (tile.type === "rock") color = 8947848;
|
|
44435
|
+
else if (tile.type === "snow") color = 15658734;
|
|
44436
|
+
let emissive = 0;
|
|
44437
|
+
if (isSelected) emissive = 4473924;
|
|
44438
|
+
else if (isAttackTarget) emissive = 4456448;
|
|
44439
|
+
else if (isValidMove) emissive = 17408;
|
|
44440
|
+
else if (isHovered) emissive = 2236962;
|
|
44441
|
+
if (tile.modelUrl) {
|
|
44442
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
44443
|
+
"group",
|
|
44444
|
+
{
|
|
44445
|
+
position,
|
|
44446
|
+
onClick: (e) => handleTileClick(tile, e),
|
|
44447
|
+
onPointerEnter: (e) => handleTileHover(tile, e),
|
|
44448
|
+
onPointerLeave: (e) => handleTileHover(null, e),
|
|
44449
|
+
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
44450
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
44451
|
+
ModelLoader,
|
|
44452
|
+
{
|
|
44453
|
+
url: tile.modelUrl,
|
|
44454
|
+
scale: 0.95,
|
|
44455
|
+
fallbackGeometry: "box",
|
|
44456
|
+
castShadow: true,
|
|
44457
|
+
receiveShadow: true
|
|
44458
|
+
}
|
|
44459
|
+
)
|
|
44460
|
+
}
|
|
44461
|
+
);
|
|
44462
|
+
}
|
|
44463
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
44464
|
+
"mesh",
|
|
44465
|
+
{
|
|
44466
|
+
position,
|
|
44467
|
+
onClick: (e) => handleTileClick(tile, e),
|
|
44468
|
+
onPointerEnter: (e) => handleTileHover(tile, e),
|
|
44469
|
+
onPointerLeave: (e) => handleTileHover(null, e),
|
|
44470
|
+
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
44471
|
+
children: [
|
|
44472
|
+
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.95, 0.2, 0.95] }),
|
|
44473
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color, emissive })
|
|
44474
|
+
]
|
|
44475
|
+
}
|
|
44476
|
+
);
|
|
44477
|
+
},
|
|
44478
|
+
[selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover]
|
|
44479
|
+
);
|
|
44480
|
+
const DefaultUnitRenderer = React91.useCallback(
|
|
44481
|
+
({ unit, position }) => {
|
|
44482
|
+
const isSelected = selectedUnitId === unit.id;
|
|
44483
|
+
const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
|
|
44484
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
44485
|
+
"group",
|
|
44486
|
+
{
|
|
44487
|
+
position,
|
|
44488
|
+
onClick: (e) => handleUnitClick(unit, e),
|
|
44489
|
+
userData: { type: "unit", unitId: unit.id },
|
|
44490
|
+
children: [
|
|
44491
|
+
isSelected && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.05, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
44492
|
+
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
|
|
44493
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
|
|
44494
|
+
] }),
|
|
44495
|
+
unit.modelUrl ? (
|
|
44496
|
+
/* GLB unit model (box fallback while loading / on error) */
|
|
44497
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44498
|
+
ModelLoader,
|
|
44499
|
+
{
|
|
44500
|
+
url: unit.modelUrl,
|
|
44501
|
+
scale: 0.5,
|
|
44502
|
+
fallbackGeometry: "box",
|
|
44503
|
+
castShadow: true
|
|
44504
|
+
}
|
|
44505
|
+
)
|
|
44506
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
44507
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.3, 0], children: [
|
|
44508
|
+
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.3, 0.3, 0.1, 8] }),
|
|
44509
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
44510
|
+
] }),
|
|
44511
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.6, 0], children: [
|
|
44512
|
+
/* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [0.2, 0.4, 4, 8] }),
|
|
44513
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
44514
|
+
] }),
|
|
44515
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
44516
|
+
/* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [0.12, 8, 8] }),
|
|
44517
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
44518
|
+
] })
|
|
44519
|
+
] }),
|
|
44520
|
+
unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("group", { position: [0, 1.2, 0], children: [
|
|
44521
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [-0.25, 0, 0], children: [
|
|
44522
|
+
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.5, 0.05] }),
|
|
44523
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: 3355443 })
|
|
44524
|
+
] }),
|
|
44525
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
44526
|
+
"mesh",
|
|
44527
|
+
{
|
|
44528
|
+
position: [
|
|
44529
|
+
-0.25 + 0.5 * (unit.health / unit.maxHealth) / 2,
|
|
44530
|
+
0,
|
|
44531
|
+
0.01
|
|
44532
|
+
],
|
|
44533
|
+
children: [
|
|
44534
|
+
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.5 * (unit.health / unit.maxHealth), 0.05] }),
|
|
44535
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44536
|
+
"meshBasicMaterial",
|
|
44537
|
+
{
|
|
44538
|
+
color: unit.health / unit.maxHealth > 0.5 ? 4500036 : unit.health / unit.maxHealth > 0.25 ? 11184708 : 16729156
|
|
44539
|
+
}
|
|
44540
|
+
)
|
|
44541
|
+
]
|
|
44542
|
+
}
|
|
44543
|
+
)
|
|
44544
|
+
] })
|
|
44545
|
+
]
|
|
44546
|
+
}
|
|
44547
|
+
);
|
|
44548
|
+
},
|
|
44549
|
+
[selectedUnitId, handleUnitClick]
|
|
44550
|
+
);
|
|
44551
|
+
const DefaultFeatureRenderer = React91.useCallback(
|
|
44552
|
+
({
|
|
44553
|
+
feature,
|
|
44554
|
+
position
|
|
44555
|
+
}) => {
|
|
44556
|
+
if (feature.assetUrl) {
|
|
44557
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
44558
|
+
ModelLoader,
|
|
44559
|
+
{
|
|
44560
|
+
url: feature.assetUrl,
|
|
44561
|
+
position,
|
|
44562
|
+
scale: 0.5,
|
|
44563
|
+
rotation: [0, feature.rotation ?? 0, 0],
|
|
44564
|
+
onClick: () => handleFeatureClick(feature, null),
|
|
44565
|
+
fallbackGeometry: "box"
|
|
44566
|
+
},
|
|
44567
|
+
feature.id
|
|
44568
|
+
);
|
|
44569
|
+
}
|
|
44570
|
+
if (feature.type === "tree") {
|
|
44571
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
44572
|
+
"group",
|
|
44573
|
+
{
|
|
44574
|
+
position,
|
|
44575
|
+
onClick: (e) => handleFeatureClick(feature, e),
|
|
44576
|
+
userData: { type: "feature", featureId: feature.id },
|
|
44577
|
+
children: [
|
|
44578
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.4, 0], children: [
|
|
44579
|
+
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.1, 0.15, 0.8, 6] }),
|
|
44580
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 9127187 })
|
|
44581
|
+
] }),
|
|
44582
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
44583
|
+
/* @__PURE__ */ jsxRuntime.jsx("coneGeometry", { args: [0.5, 0.8, 8] }),
|
|
44584
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 2263842 })
|
|
44585
|
+
] })
|
|
44586
|
+
]
|
|
44587
|
+
}
|
|
44588
|
+
);
|
|
44589
|
+
}
|
|
44590
|
+
if (feature.type === "rock") {
|
|
44591
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
44592
|
+
"mesh",
|
|
44593
|
+
{
|
|
44594
|
+
position: [position[0], position[1] + 0.3, position[2]],
|
|
44595
|
+
onClick: (e) => handleFeatureClick(feature, e),
|
|
44596
|
+
userData: { type: "feature", featureId: feature.id },
|
|
44597
|
+
children: [
|
|
44598
|
+
/* @__PURE__ */ jsxRuntime.jsx("dodecahedronGeometry", { args: [0.3, 0] }),
|
|
44599
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 8421504 })
|
|
44600
|
+
]
|
|
44601
|
+
}
|
|
44602
|
+
);
|
|
44603
|
+
}
|
|
44604
|
+
return null;
|
|
44605
|
+
},
|
|
44606
|
+
[handleFeatureClick]
|
|
44607
|
+
);
|
|
44608
|
+
if (externalLoading || assetsLoading && preloadAssets.length > 0) {
|
|
44609
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
44610
|
+
Canvas3DLoadingState,
|
|
44611
|
+
{
|
|
44612
|
+
progress,
|
|
44613
|
+
loaded,
|
|
44614
|
+
total,
|
|
44615
|
+
message: loadingMessage,
|
|
44616
|
+
className
|
|
44617
|
+
}
|
|
44618
|
+
);
|
|
44619
|
+
}
|
|
44620
|
+
const displayError = externalError || internalError;
|
|
44621
|
+
if (displayError) {
|
|
44622
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Canvas3DErrorBoundary, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "game-canvas-3d game-canvas-3d--error", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__error", children: [
|
|
44623
|
+
"Error: ",
|
|
44624
|
+
displayError
|
|
44625
|
+
] }) }) });
|
|
44626
|
+
}
|
|
44627
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
44628
|
+
Canvas3DErrorBoundary,
|
|
44629
|
+
{
|
|
44630
|
+
onError: (err) => setInternalError(err.message),
|
|
44631
|
+
onReset: () => setInternalError(null),
|
|
44632
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
44633
|
+
"div",
|
|
44634
|
+
{
|
|
44635
|
+
ref: containerRef,
|
|
44636
|
+
className: cn("game-canvas-3d relative w-full h-full min-h-[85vh] overflow-hidden", className),
|
|
44637
|
+
"data-orientation": orientation,
|
|
44638
|
+
"data-camera-mode": cameraMode,
|
|
44639
|
+
"data-overlay": overlay,
|
|
44640
|
+
children: [
|
|
44641
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
44642
|
+
fiber.Canvas,
|
|
44643
|
+
{
|
|
44644
|
+
shadows,
|
|
44645
|
+
camera: {
|
|
44646
|
+
position: cameraConfig.position,
|
|
44647
|
+
fov: cameraConfig.fov,
|
|
44648
|
+
near: 0.1,
|
|
44649
|
+
far: 1e3
|
|
44650
|
+
},
|
|
44651
|
+
style: { background: backgroundColor },
|
|
44652
|
+
onClick: (e) => {
|
|
44653
|
+
if (e.target === e.currentTarget) {
|
|
44654
|
+
eventHandlers.handleCanvasClick(e);
|
|
44655
|
+
}
|
|
44656
|
+
},
|
|
44657
|
+
children: [
|
|
44658
|
+
/* @__PURE__ */ jsxRuntime.jsx(CameraController, { onCameraChange: eventHandlers.handleCameraChange }),
|
|
44659
|
+
/* @__PURE__ */ jsxRuntime.jsx("ambientLight", { intensity: 0.6 }),
|
|
44660
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44661
|
+
"directionalLight",
|
|
44662
|
+
{
|
|
44663
|
+
position: [10, 20, 10],
|
|
44664
|
+
intensity: 0.8,
|
|
44665
|
+
castShadow: shadows,
|
|
44666
|
+
"shadow-mapSize": [2048, 2048]
|
|
44667
|
+
}
|
|
44668
|
+
),
|
|
44669
|
+
/* @__PURE__ */ jsxRuntime.jsx("hemisphereLight", { intensity: 0.3, color: "#87ceeb", groundColor: "#362d1d" }),
|
|
44670
|
+
showGrid && /* @__PURE__ */ jsxRuntime.jsx(
|
|
44671
|
+
drei.Grid,
|
|
44672
|
+
{
|
|
44673
|
+
args: [
|
|
44674
|
+
Math.max(gridBounds.maxX - gridBounds.minX + 2, 10),
|
|
44675
|
+
Math.max(gridBounds.maxZ - gridBounds.minZ + 2, 10)
|
|
44676
|
+
],
|
|
44677
|
+
position: [
|
|
44678
|
+
(gridBounds.maxX - gridBounds.minX) / 2 - 0.5,
|
|
44679
|
+
0,
|
|
44680
|
+
(gridBounds.maxZ - gridBounds.minZ) / 2 - 0.5
|
|
44681
|
+
],
|
|
44682
|
+
cellSize: 1,
|
|
44683
|
+
cellThickness: 1,
|
|
44684
|
+
cellColor: "#444444",
|
|
44685
|
+
sectionSize: 5,
|
|
44686
|
+
sectionThickness: 1.5,
|
|
44687
|
+
sectionColor: "#666666",
|
|
44688
|
+
fadeDistance: 50,
|
|
44689
|
+
fadeStrength: 1
|
|
44690
|
+
}
|
|
44691
|
+
),
|
|
44692
|
+
tiles.map((tile, index) => {
|
|
44693
|
+
const position = gridToWorld(
|
|
44694
|
+
tile.x,
|
|
44695
|
+
tile.z ?? tile.y ?? 0,
|
|
44696
|
+
tile.elevation ?? 0
|
|
44697
|
+
);
|
|
44698
|
+
const Renderer = CustomTileRenderer || DefaultTileRenderer;
|
|
44699
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
|
|
44700
|
+
}),
|
|
44701
|
+
features.map((feature, index) => {
|
|
44702
|
+
const position = gridToWorld(
|
|
44703
|
+
feature.x,
|
|
44704
|
+
feature.z ?? feature.y ?? 0,
|
|
44705
|
+
(feature.elevation ?? 0) + 0.5
|
|
44706
|
+
);
|
|
44707
|
+
const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
|
|
44708
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
|
|
44709
|
+
}),
|
|
44710
|
+
units.map((unit) => {
|
|
44711
|
+
const position = gridToWorld(
|
|
44712
|
+
unit.x ?? 0,
|
|
44713
|
+
unit.z ?? unit.y ?? 0,
|
|
44714
|
+
(unit.elevation ?? 0) + 0.5
|
|
44715
|
+
);
|
|
44716
|
+
const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
|
|
44717
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { unit, position }, unit.id);
|
|
44718
|
+
}),
|
|
44719
|
+
children,
|
|
44720
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44721
|
+
drei.OrbitControls,
|
|
44722
|
+
{
|
|
44723
|
+
ref: controlsRef,
|
|
44724
|
+
target: cameraTarget,
|
|
44725
|
+
enableDamping: true,
|
|
44726
|
+
dampingFactor: 0.05,
|
|
44727
|
+
minDistance: 2,
|
|
44728
|
+
maxDistance: 100,
|
|
44729
|
+
maxPolarAngle: Math.PI / 2 - 0.1
|
|
44730
|
+
}
|
|
44731
|
+
)
|
|
44732
|
+
]
|
|
44733
|
+
}
|
|
44734
|
+
),
|
|
44735
|
+
showCoordinates && hoveredTile && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__coordinates", children: [
|
|
44736
|
+
"X: ",
|
|
44737
|
+
hoveredTile.x,
|
|
44738
|
+
", Z: ",
|
|
44739
|
+
hoveredTile.z ?? hoveredTile.y ?? 0
|
|
44740
|
+
] }),
|
|
44741
|
+
showTileInfo && hoveredTile && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__tile-info", children: [
|
|
44742
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "tile-info__type", children: hoveredTile.type }),
|
|
44743
|
+
hoveredTile.terrain && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tile-info__terrain", children: hoveredTile.terrain })
|
|
44744
|
+
] })
|
|
44745
|
+
]
|
|
44746
|
+
}
|
|
44747
|
+
)
|
|
44748
|
+
}
|
|
44749
|
+
);
|
|
44750
|
+
}
|
|
44751
|
+
);
|
|
44752
|
+
GameCanvas3D.displayName = "GameCanvas3D";
|
|
44753
|
+
}
|
|
44754
|
+
});
|
|
44755
|
+
function GameBoard3D({
|
|
44756
|
+
entity,
|
|
44757
|
+
tiles = [],
|
|
44758
|
+
features = [],
|
|
44759
|
+
cameraMode = "perspective",
|
|
44760
|
+
backgroundColor = "#2a1a1a",
|
|
44761
|
+
tileClickEvent,
|
|
44762
|
+
unitClickEvent,
|
|
44763
|
+
attackEvent,
|
|
44764
|
+
endTurnEvent,
|
|
44765
|
+
cancelEvent,
|
|
44766
|
+
playAgainEvent,
|
|
44767
|
+
gameEndEvent,
|
|
44768
|
+
className
|
|
44769
|
+
}) {
|
|
44770
|
+
const row = boardEntity(entity);
|
|
44771
|
+
const eventBus = useEventBus();
|
|
44772
|
+
const entityUnits = row ? rows(row.units) : [];
|
|
44773
|
+
const units = entityUnits;
|
|
44774
|
+
const selectedUnitId = row ? str(row.selectedUnitId) || null : null;
|
|
44775
|
+
const phase = row ? str(row.phase) : "observation";
|
|
44776
|
+
const result = row ? str(row.result) : "none";
|
|
44777
|
+
const validMoves = row && Array.isArray(row.validMoves) ? row.validMoves : [];
|
|
44778
|
+
const attackTargets = row && Array.isArray(row.attackTargets) ? row.attackTargets : [];
|
|
44779
|
+
const turn = row ? num(row.turn) : 0;
|
|
44780
|
+
const currentTeam = row ? str(row.currentTeam) : "player";
|
|
44781
|
+
const isGameOver = result !== "none";
|
|
44782
|
+
const gameEndEmittedRef = React91.useRef(false);
|
|
44783
|
+
React91.useEffect(() => {
|
|
44784
|
+
if ((result === "victory" || result === "defeat") && gameEndEvent) {
|
|
44785
|
+
if (!gameEndEmittedRef.current) {
|
|
44786
|
+
gameEndEmittedRef.current = true;
|
|
44787
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
44788
|
+
}
|
|
44789
|
+
} else {
|
|
44790
|
+
gameEndEmittedRef.current = false;
|
|
44791
|
+
}
|
|
44792
|
+
}, [result, gameEndEvent, eventBus]);
|
|
44793
|
+
const checkGameEnd = React91.useCallback(() => {
|
|
44794
|
+
const alivePlayer = entityUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
|
|
44795
|
+
const aliveEnemy = entityUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
|
|
44796
|
+
if (alivePlayer.length === 0 && gameEndEvent) {
|
|
44797
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result: "defeat" });
|
|
44798
|
+
} else if (aliveEnemy.length === 0 && gameEndEvent) {
|
|
44799
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result: "victory" });
|
|
44800
|
+
}
|
|
44801
|
+
}, [entityUnits, gameEndEvent, eventBus]);
|
|
44802
|
+
const handleUnitClickCallback = React91.useCallback((isoUnit) => {
|
|
44803
|
+
if (phase !== "action" || !selectedUnitId || !attackEvent) return;
|
|
44804
|
+
const attackerRow = entityUnits.find((u) => str(u.id) === selectedUnitId);
|
|
44805
|
+
const targetRow = entityUnits.find((u) => str(u.id) === isoUnit.id);
|
|
44806
|
+
if (!attackerRow || !targetRow) return;
|
|
44807
|
+
if (unitTeam(targetRow) !== "enemy" || unitHealth(targetRow) <= 0) return;
|
|
44808
|
+
const ap = unitPosition(attackerRow);
|
|
44809
|
+
const tp = unitPosition(targetRow);
|
|
44810
|
+
const dx = Math.abs(ap.x - tp.x);
|
|
44811
|
+
const dy = Math.abs(ap.y - tp.y);
|
|
44812
|
+
if (dx <= 1 && dy <= 1 && dx + dy > 0) {
|
|
44813
|
+
const damage = Math.max(1, num(attackerRow.attack) - num(targetRow.defense));
|
|
44814
|
+
eventBus.emit(`UI:${attackEvent}`, {
|
|
44815
|
+
attackerId: str(attackerRow.id),
|
|
44816
|
+
targetId: str(targetRow.id),
|
|
44817
|
+
damage
|
|
44818
|
+
});
|
|
44819
|
+
setTimeout(checkGameEnd, 100);
|
|
44820
|
+
}
|
|
44821
|
+
}, [phase, selectedUnitId, entityUnits, attackEvent, eventBus, checkGameEnd]);
|
|
44822
|
+
const handleEndTurn = React91.useCallback(() => {
|
|
44823
|
+
if (endTurnEvent) eventBus.emit(`UI:${endTurnEvent}`, {});
|
|
44824
|
+
}, [endTurnEvent, eventBus]);
|
|
44825
|
+
const handleCancel = React91.useCallback(() => {
|
|
44826
|
+
if (cancelEvent) eventBus.emit(`UI:${cancelEvent}`, {});
|
|
44827
|
+
}, [cancelEvent, eventBus]);
|
|
44828
|
+
const handlePlayAgain = React91.useCallback(() => {
|
|
44829
|
+
if (playAgainEvent) eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
44830
|
+
}, [playAgainEvent, eventBus]);
|
|
44831
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("game-board-3d block w-full min-h-[85vh] relative", className), children: [
|
|
44832
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-board-3d__status absolute top-3 left-3 z-10 flex gap-2 items-center", children: [
|
|
44833
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "status__phase capitalize", children: phase.replace("_", " ") }),
|
|
44834
|
+
currentTeam && !isGameOver && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", color: "muted", className: "status__team", children: [
|
|
44835
|
+
"\u2014 ",
|
|
44836
|
+
currentTeam === "player" ? "Your Turn" : "Enemy's Turn"
|
|
44837
|
+
] }),
|
|
44838
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", color: "muted", className: "status__turn", children: [
|
|
44839
|
+
"Turn ",
|
|
44840
|
+
turn
|
|
44841
|
+
] })
|
|
44842
|
+
] }),
|
|
44843
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44844
|
+
GameCanvas3D,
|
|
44845
|
+
{
|
|
44846
|
+
tiles,
|
|
44847
|
+
units,
|
|
44848
|
+
features,
|
|
44849
|
+
cameraMode,
|
|
44850
|
+
showGrid: true,
|
|
44851
|
+
showCoordinates: false,
|
|
44852
|
+
showTileInfo: false,
|
|
44853
|
+
shadows: true,
|
|
44854
|
+
backgroundColor,
|
|
44855
|
+
tileClickEvent,
|
|
44856
|
+
unitClickEvent,
|
|
44857
|
+
onUnitClick: handleUnitClickCallback,
|
|
44858
|
+
selectedUnitId,
|
|
44859
|
+
validMoves,
|
|
44860
|
+
attackTargets,
|
|
44861
|
+
className: "game-board-3d__canvas w-full min-h-[85vh]"
|
|
44862
|
+
}
|
|
44863
|
+
),
|
|
44864
|
+
!isGameOver && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "fixed bottom-6 right-6 z-50", gap: "sm", children: [
|
|
44865
|
+
(phase === "selection" || phase === "movement" || phase === "action") && /* @__PURE__ */ jsxRuntime.jsx(
|
|
44866
|
+
Button,
|
|
44867
|
+
{
|
|
44868
|
+
variant: "secondary",
|
|
44869
|
+
className: "shadow-xl",
|
|
44870
|
+
onClick: handleCancel,
|
|
44871
|
+
children: "Cancel"
|
|
44872
|
+
}
|
|
44873
|
+
),
|
|
44874
|
+
phase !== "enemy_turn" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
44875
|
+
Button,
|
|
44876
|
+
{
|
|
44877
|
+
variant: "primary",
|
|
44878
|
+
className: "shadow-xl",
|
|
44879
|
+
onClick: handleEndTurn,
|
|
44880
|
+
children: "End Turn"
|
|
44881
|
+
}
|
|
44882
|
+
)
|
|
44883
|
+
] }),
|
|
44884
|
+
isGameOver && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "game-board-3d__overlay absolute inset-0 z-20 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { align: "center", gap: "md", className: "overlay__card p-8 rounded-xl bg-gray-900/90 shadow-2xl", children: [
|
|
44885
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44886
|
+
Typography,
|
|
44887
|
+
{
|
|
44888
|
+
variant: "h2",
|
|
44889
|
+
className: cn(
|
|
44890
|
+
"overlay__result",
|
|
44891
|
+
result === "victory" ? "text-yellow-400" : "text-red-500"
|
|
44892
|
+
),
|
|
44893
|
+
children: result === "victory" ? "Victory!" : "Defeat"
|
|
44894
|
+
}
|
|
44895
|
+
),
|
|
44896
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "body", color: "muted", className: "overlay__turn-count", children: [
|
|
44897
|
+
"Completed in ",
|
|
44898
|
+
turn,
|
|
44899
|
+
" turn",
|
|
44900
|
+
turn !== 1 ? "s" : ""
|
|
44901
|
+
] }),
|
|
44902
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44903
|
+
Button,
|
|
44904
|
+
{
|
|
44905
|
+
variant: "primary",
|
|
44906
|
+
className: "px-8 py-3 font-semibold",
|
|
44907
|
+
onClick: handlePlayAgain,
|
|
44908
|
+
children: "Play Again"
|
|
44909
|
+
}
|
|
44910
|
+
)
|
|
44911
|
+
] }) })
|
|
44912
|
+
] });
|
|
44913
|
+
}
|
|
44914
|
+
var init_GameBoard3D = __esm({
|
|
44915
|
+
"components/game/organisms/GameBoard3D.tsx"() {
|
|
44916
|
+
"use client";
|
|
44917
|
+
init_cn();
|
|
44918
|
+
init_Button();
|
|
44919
|
+
init_Typography();
|
|
44920
|
+
init_Stack();
|
|
44921
|
+
init_useEventBus();
|
|
44922
|
+
init_GameCanvas3D2();
|
|
44923
|
+
init_boardEntity();
|
|
44924
|
+
GameBoard3D.displayName = "GameBoard3D";
|
|
44925
|
+
}
|
|
44926
|
+
});
|
|
43223
44927
|
var GameShell;
|
|
43224
44928
|
var init_GameShell = __esm({
|
|
43225
44929
|
"components/game/templates/GameShell.tsx"() {
|
|
@@ -44588,21 +46292,6 @@ var init_ModalSlot = __esm({
|
|
|
44588
46292
|
ModalSlot.displayName = "ModalSlot";
|
|
44589
46293
|
}
|
|
44590
46294
|
});
|
|
44591
|
-
function getOpponentAction(strategy, actions, history) {
|
|
44592
|
-
const actionIds = actions.map((a) => a.id);
|
|
44593
|
-
switch (strategy) {
|
|
44594
|
-
case "always-cooperate":
|
|
44595
|
-
return actionIds[0];
|
|
44596
|
-
case "always-defect":
|
|
44597
|
-
return actionIds[actionIds.length - 1];
|
|
44598
|
-
case "tit-for-tat":
|
|
44599
|
-
if (history.length === 0) return actionIds[0];
|
|
44600
|
-
return history[history.length - 1].playerAction;
|
|
44601
|
-
case "random":
|
|
44602
|
-
default:
|
|
44603
|
-
return actionIds[Math.floor(Math.random() * actionIds.length)];
|
|
44604
|
-
}
|
|
44605
|
-
}
|
|
44606
46295
|
function NegotiatorBoard({
|
|
44607
46296
|
entity,
|
|
44608
46297
|
completeEvent = "PUZZLE_COMPLETE",
|
|
@@ -44624,28 +46313,35 @@ function NegotiatorBoard({
|
|
|
44624
46313
|
const playerTotal = num(resolved?.score);
|
|
44625
46314
|
const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
|
|
44626
46315
|
const won = result === "win";
|
|
44627
|
-
const
|
|
46316
|
+
const lastPlayerAction = str(resolved?.lastPlayerAction);
|
|
46317
|
+
const lastOpponentAction = str(resolved?.lastOpponentAction);
|
|
46318
|
+
const lastPayoff = num(resolved?.lastPayoff);
|
|
44628
46319
|
const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
|
|
44629
46320
|
const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
|
|
46321
|
+
const prevRoundRef = React91.useRef(currentRound);
|
|
46322
|
+
React91.useEffect(() => {
|
|
46323
|
+
if (currentRound > prevRoundRef.current && lastPlayerAction) {
|
|
46324
|
+
const opponentPayoffEntry = payoffMatrix.find(
|
|
46325
|
+
(p2) => p2.playerAction === lastPlayerAction && p2.opponentAction === lastOpponentAction
|
|
46326
|
+
);
|
|
46327
|
+
setHistory((prev) => [
|
|
46328
|
+
...prev,
|
|
46329
|
+
{
|
|
46330
|
+
round: currentRound,
|
|
46331
|
+
playerAction: lastPlayerAction,
|
|
46332
|
+
opponentAction: lastOpponentAction,
|
|
46333
|
+
playerPayoff: lastPayoff,
|
|
46334
|
+
opponentPayoff: opponentPayoffEntry?.opponentPayoff ?? 0
|
|
46335
|
+
}
|
|
46336
|
+
]);
|
|
46337
|
+
}
|
|
46338
|
+
prevRoundRef.current = currentRound;
|
|
46339
|
+
}, [currentRound, lastPlayerAction, lastOpponentAction, lastPayoff, payoffMatrix]);
|
|
46340
|
+
const opponentTotal = React91.useMemo(() => history.reduce((s, r2) => s + r2.opponentPayoff, 0), [history]);
|
|
44630
46341
|
const handleAction = React91.useCallback((actionId) => {
|
|
44631
46342
|
if (isComplete) return;
|
|
44632
|
-
const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
|
|
44633
|
-
const payoff = payoffMatrix.find(
|
|
44634
|
-
(p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
|
|
44635
|
-
);
|
|
44636
|
-
const playerPayoff = payoff?.playerPayoff ?? 0;
|
|
44637
|
-
setHistory((prev) => [
|
|
44638
|
-
...prev,
|
|
44639
|
-
{
|
|
44640
|
-
round: prev.length + 1,
|
|
44641
|
-
playerAction: actionId,
|
|
44642
|
-
opponentAction,
|
|
44643
|
-
playerPayoff,
|
|
44644
|
-
opponentPayoff: payoff?.opponentPayoff ?? 0
|
|
44645
|
-
}
|
|
44646
|
-
]);
|
|
44647
46343
|
if (playRoundEvent) {
|
|
44648
|
-
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff:
|
|
46344
|
+
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: 0 });
|
|
44649
46345
|
}
|
|
44650
46346
|
if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
|
|
44651
46347
|
if (finishEvent) {
|
|
@@ -44655,10 +46351,11 @@ function NegotiatorBoard({
|
|
|
44655
46351
|
setShowHint(true);
|
|
44656
46352
|
}
|
|
44657
46353
|
}
|
|
44658
|
-
}, [isComplete, resolved, totalRounds, currentRound,
|
|
46354
|
+
}, [isComplete, resolved, totalRounds, currentRound, playRoundEvent, finishEvent, emit]);
|
|
44659
46355
|
const handleReset = React91.useCallback(() => {
|
|
44660
46356
|
setHistory([]);
|
|
44661
46357
|
setShowHint(false);
|
|
46358
|
+
prevRoundRef.current = 0;
|
|
44662
46359
|
if (playAgainEvent) {
|
|
44663
46360
|
emit(`UI:${playAgainEvent}`, {});
|
|
44664
46361
|
}
|
|
@@ -44769,6 +46466,236 @@ var init_NegotiatorBoard = __esm({
|
|
|
44769
46466
|
NegotiatorBoard.displayName = "NegotiatorBoard";
|
|
44770
46467
|
}
|
|
44771
46468
|
});
|
|
46469
|
+
function resolveEntity(entity) {
|
|
46470
|
+
if (!entity) return {};
|
|
46471
|
+
if (Array.isArray(entity)) return entity[0] ?? {};
|
|
46472
|
+
return entity;
|
|
46473
|
+
}
|
|
46474
|
+
function numField(row, key, fallback) {
|
|
46475
|
+
const v = row[key];
|
|
46476
|
+
return typeof v === "number" ? v : fallback;
|
|
46477
|
+
}
|
|
46478
|
+
function strField(row, key, fallback) {
|
|
46479
|
+
const v = row[key];
|
|
46480
|
+
return typeof v === "string" ? v : fallback;
|
|
46481
|
+
}
|
|
46482
|
+
function PlatformerBoard({
|
|
46483
|
+
entity,
|
|
46484
|
+
player: propPlayer,
|
|
46485
|
+
platforms: propPlatforms,
|
|
46486
|
+
result: propResult,
|
|
46487
|
+
lives: propLives,
|
|
46488
|
+
score: propScore,
|
|
46489
|
+
level: propLevel,
|
|
46490
|
+
worldWidth = 800,
|
|
46491
|
+
worldHeight = 400,
|
|
46492
|
+
canvasWidth = 800,
|
|
46493
|
+
canvasHeight = 400,
|
|
46494
|
+
playerSprite,
|
|
46495
|
+
tileSprites,
|
|
46496
|
+
bgColor,
|
|
46497
|
+
leftEvent = "LEFT",
|
|
46498
|
+
rightEvent = "RIGHT",
|
|
46499
|
+
jumpEvent = "JUMP",
|
|
46500
|
+
stopEvent = "STOP",
|
|
46501
|
+
playAgainEvent = "PLAY_AGAIN",
|
|
46502
|
+
gameEndEvent = "GAME_END",
|
|
46503
|
+
className
|
|
46504
|
+
}) {
|
|
46505
|
+
const row = resolveEntity(entity);
|
|
46506
|
+
const eventBus = useEventBus();
|
|
46507
|
+
const { t } = hooks.useTranslate();
|
|
46508
|
+
const result = propResult ?? strField(row, "result", "none");
|
|
46509
|
+
propLives ?? numField(row, "lives", 3);
|
|
46510
|
+
const score = propScore ?? numField(row, "score", 0);
|
|
46511
|
+
const level = propLevel ?? numField(row, "level", 1);
|
|
46512
|
+
const entityPlayer = row["player"];
|
|
46513
|
+
const player = propPlayer ?? entityPlayer ?? {
|
|
46514
|
+
x: 80,
|
|
46515
|
+
y: 320,
|
|
46516
|
+
width: 32,
|
|
46517
|
+
height: 48,
|
|
46518
|
+
vx: 0,
|
|
46519
|
+
vy: 0,
|
|
46520
|
+
grounded: false,
|
|
46521
|
+
facingRight: true
|
|
46522
|
+
};
|
|
46523
|
+
const entityPlatforms = Array.isArray(row["platforms"]) ? row["platforms"] : void 0;
|
|
46524
|
+
const platforms = propPlatforms ?? entityPlatforms ?? [
|
|
46525
|
+
{ x: 0, y: 368, width: 800, height: 32, type: "ground" },
|
|
46526
|
+
{ x: 150, y: 280, width: 160, height: 16, type: "platform" },
|
|
46527
|
+
{ x: 420, y: 220, width: 160, height: 16, type: "platform" },
|
|
46528
|
+
{ x: 580, y: 300, width: 80, height: 16, type: "hazard" },
|
|
46529
|
+
{ x: 700, y: 340, width: 64, height: 28, type: "goal" }
|
|
46530
|
+
];
|
|
46531
|
+
const handleRestart = React91.useCallback(() => {
|
|
46532
|
+
if (playAgainEvent) {
|
|
46533
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
46534
|
+
}
|
|
46535
|
+
}, [playAgainEvent, eventBus]);
|
|
46536
|
+
const isGameOver = result === "won" || result === "lost";
|
|
46537
|
+
const gameEndEmittedRef = React91.useRef(false);
|
|
46538
|
+
React91.useEffect(() => {
|
|
46539
|
+
if ((result === "won" || result === "lost") && gameEndEvent) {
|
|
46540
|
+
if (!gameEndEmittedRef.current) {
|
|
46541
|
+
gameEndEmittedRef.current = true;
|
|
46542
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
46543
|
+
}
|
|
46544
|
+
} else {
|
|
46545
|
+
gameEndEmittedRef.current = false;
|
|
46546
|
+
}
|
|
46547
|
+
}, [result, gameEndEvent, eventBus]);
|
|
46548
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
46549
|
+
VStack,
|
|
46550
|
+
{
|
|
46551
|
+
className: cn("platformer-board relative bg-background", className),
|
|
46552
|
+
gap: "none",
|
|
46553
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative", children: [
|
|
46554
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
46555
|
+
PlatformerCanvas,
|
|
46556
|
+
{
|
|
46557
|
+
player,
|
|
46558
|
+
platforms,
|
|
46559
|
+
worldWidth,
|
|
46560
|
+
worldHeight,
|
|
46561
|
+
canvasWidth,
|
|
46562
|
+
canvasHeight,
|
|
46563
|
+
followCamera: true,
|
|
46564
|
+
bgColor,
|
|
46565
|
+
playerSprite,
|
|
46566
|
+
tileSprites,
|
|
46567
|
+
leftEvent,
|
|
46568
|
+
rightEvent,
|
|
46569
|
+
jumpEvent,
|
|
46570
|
+
stopEvent
|
|
46571
|
+
}
|
|
46572
|
+
),
|
|
46573
|
+
isGameOver && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
46574
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
46575
|
+
Typography,
|
|
46576
|
+
{
|
|
46577
|
+
variant: "h2",
|
|
46578
|
+
className: cn(
|
|
46579
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
46580
|
+
result === "won" ? "text-warning" : "text-error"
|
|
46581
|
+
),
|
|
46582
|
+
children: result === "won" ? t("platformer.won") : t("platformer.lost")
|
|
46583
|
+
}
|
|
46584
|
+
),
|
|
46585
|
+
result === "won" && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "body1", className: "text-muted-foreground", children: [
|
|
46586
|
+
t("platformer.level"),
|
|
46587
|
+
" ",
|
|
46588
|
+
level,
|
|
46589
|
+
" \u2014 ",
|
|
46590
|
+
t("platformer.score"),
|
|
46591
|
+
": ",
|
|
46592
|
+
score
|
|
46593
|
+
] }),
|
|
46594
|
+
result === "lost" && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: t("platformer.noLives") }),
|
|
46595
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
46596
|
+
Button,
|
|
46597
|
+
{
|
|
46598
|
+
variant: "primary",
|
|
46599
|
+
className: "px-8 py-3 font-semibold",
|
|
46600
|
+
onClick: handleRestart,
|
|
46601
|
+
children: result === "won" ? t("platformer.nextLevel") : t("platformer.playAgain")
|
|
46602
|
+
}
|
|
46603
|
+
)
|
|
46604
|
+
] }) })
|
|
46605
|
+
] })
|
|
46606
|
+
}
|
|
46607
|
+
);
|
|
46608
|
+
}
|
|
46609
|
+
var init_PlatformerBoard = __esm({
|
|
46610
|
+
"components/game/organisms/PlatformerBoard.tsx"() {
|
|
46611
|
+
"use client";
|
|
46612
|
+
init_cn();
|
|
46613
|
+
init_useEventBus();
|
|
46614
|
+
init_Box();
|
|
46615
|
+
init_Button();
|
|
46616
|
+
init_Typography();
|
|
46617
|
+
init_Stack();
|
|
46618
|
+
init_PlatformerCanvas();
|
|
46619
|
+
PlatformerBoard.displayName = "PlatformerBoard";
|
|
46620
|
+
}
|
|
46621
|
+
});
|
|
46622
|
+
function PlatformerTemplate({
|
|
46623
|
+
entity,
|
|
46624
|
+
platforms = DEFAULT_PLATFORMS,
|
|
46625
|
+
worldWidth = 800,
|
|
46626
|
+
worldHeight = 400,
|
|
46627
|
+
canvasWidth = 800,
|
|
46628
|
+
canvasHeight = 400,
|
|
46629
|
+
playerSprite = `${CDN3}/characters/platformChar_idle.png`,
|
|
46630
|
+
tileSprites = DEFAULT_TILE_SPRITES,
|
|
46631
|
+
bgColor = "#5c94fc",
|
|
46632
|
+
gameEndEvent = "GAME_END",
|
|
46633
|
+
className
|
|
46634
|
+
}) {
|
|
46635
|
+
const row = entity && !Array.isArray(entity) ? entity : void 0;
|
|
46636
|
+
const lives = typeof row?.["lives"] === "number" ? row["lives"] : 3;
|
|
46637
|
+
const score = typeof row?.["score"] === "number" ? row["score"] : 0;
|
|
46638
|
+
const level = typeof row?.["level"] === "number" ? row["level"] : 1;
|
|
46639
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("platformer-template w-full", className), gap: "none", children: [
|
|
46640
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
46641
|
+
GameHud,
|
|
46642
|
+
{
|
|
46643
|
+
position: "top",
|
|
46644
|
+
stats: [
|
|
46645
|
+
{ label: "Lives", value: lives, format: "number", variant: "danger" },
|
|
46646
|
+
{ label: "Level", value: level, format: "number", variant: "default" }
|
|
46647
|
+
]
|
|
46648
|
+
}
|
|
46649
|
+
) }),
|
|
46650
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
46651
|
+
PlatformerBoard,
|
|
46652
|
+
{
|
|
46653
|
+
entity,
|
|
46654
|
+
platforms,
|
|
46655
|
+
worldWidth,
|
|
46656
|
+
worldHeight,
|
|
46657
|
+
canvasWidth,
|
|
46658
|
+
canvasHeight,
|
|
46659
|
+
playerSprite,
|
|
46660
|
+
tileSprites,
|
|
46661
|
+
bgColor,
|
|
46662
|
+
leftEvent: "LEFT",
|
|
46663
|
+
rightEvent: "RIGHT",
|
|
46664
|
+
jumpEvent: "JUMP",
|
|
46665
|
+
stopEvent: "STOP",
|
|
46666
|
+
playAgainEvent: "PLAY_AGAIN",
|
|
46667
|
+
gameEndEvent
|
|
46668
|
+
}
|
|
46669
|
+
),
|
|
46670
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-4 py-2", children: /* @__PURE__ */ jsxRuntime.jsx(ScoreBoard, { score, level }) })
|
|
46671
|
+
] });
|
|
46672
|
+
}
|
|
46673
|
+
var CDN3, DEFAULT_TILE_SPRITES, DEFAULT_PLATFORMS;
|
|
46674
|
+
var init_PlatformerTemplate = __esm({
|
|
46675
|
+
"components/game/templates/PlatformerTemplate.tsx"() {
|
|
46676
|
+
init_cn();
|
|
46677
|
+
init_Box();
|
|
46678
|
+
init_Stack();
|
|
46679
|
+
init_PlatformerBoard();
|
|
46680
|
+
init_GameHud();
|
|
46681
|
+
init_ScoreBoard();
|
|
46682
|
+
CDN3 = "https://almadar-kflow-assets.web.app/shared/platformer";
|
|
46683
|
+
DEFAULT_TILE_SPRITES = {
|
|
46684
|
+
ground: `${CDN3}/tiles/platformPack_tile001.png`,
|
|
46685
|
+
platform: `${CDN3}/tiles/platformPack_tile004.png`,
|
|
46686
|
+
hazard: `${CDN3}/tiles/platformPack_tile017.png`,
|
|
46687
|
+
goal: `${CDN3}/tiles/platformPack_tile007.png`
|
|
46688
|
+
};
|
|
46689
|
+
DEFAULT_PLATFORMS = [
|
|
46690
|
+
{ x: 0, y: 368, width: 800, height: 32, type: "ground" },
|
|
46691
|
+
{ x: 150, y: 280, width: 160, height: 16, type: "platform" },
|
|
46692
|
+
{ x: 420, y: 220, width: 160, height: 16, type: "platform" },
|
|
46693
|
+
{ x: 580, y: 300, width: 80, height: 16, type: "hazard" },
|
|
46694
|
+
{ x: 700, y: 340, width: 64, height: 28, type: "goal" }
|
|
46695
|
+
];
|
|
46696
|
+
PlatformerTemplate.displayName = "PlatformerTemplate";
|
|
46697
|
+
}
|
|
46698
|
+
});
|
|
44772
46699
|
var PricingOrganism;
|
|
44773
46700
|
var init_PricingOrganism = __esm({
|
|
44774
46701
|
"components/marketing/organisms/PricingOrganism.tsx"() {
|
|
@@ -44891,6 +46818,356 @@ var init_PricingPageTemplate = __esm({
|
|
|
44891
46818
|
PricingPageTemplate.displayName = "PricingPageTemplate";
|
|
44892
46819
|
}
|
|
44893
46820
|
});
|
|
46821
|
+
function RoguelikeBoard({
|
|
46822
|
+
entity,
|
|
46823
|
+
tiles: propTiles,
|
|
46824
|
+
enemies: propEnemies,
|
|
46825
|
+
items: propItems,
|
|
46826
|
+
player: propPlayer,
|
|
46827
|
+
playerHp: propPlayerHp,
|
|
46828
|
+
playerMaxHp: propPlayerMaxHp,
|
|
46829
|
+
playerAttack: propPlayerAttack,
|
|
46830
|
+
depth: propDepth,
|
|
46831
|
+
result: propResult,
|
|
46832
|
+
phase: propPhase,
|
|
46833
|
+
stairsX: propStairsX,
|
|
46834
|
+
stairsY: propStairsY,
|
|
46835
|
+
assetManifest: propAssetManifest,
|
|
46836
|
+
scale = 0.45,
|
|
46837
|
+
unitScale = 1,
|
|
46838
|
+
moveEvent,
|
|
46839
|
+
playAgainEvent,
|
|
46840
|
+
gameEndEvent,
|
|
46841
|
+
className
|
|
46842
|
+
}) {
|
|
46843
|
+
const board = boardEntity(entity) ?? {};
|
|
46844
|
+
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : DEFAULT_TILES2);
|
|
46845
|
+
const enemies = propEnemies ?? (Array.isArray(board.enemies) ? board.enemies : DEFAULT_ENEMIES);
|
|
46846
|
+
const items = propItems ?? (Array.isArray(board.items) ? board.items : DEFAULT_ITEMS);
|
|
46847
|
+
const player = propPlayer ?? board.player ?? { x: 1, y: 1 };
|
|
46848
|
+
const playerHp = propPlayerHp ?? num(board.playerHp, 10);
|
|
46849
|
+
const playerMaxHp = propPlayerMaxHp ?? num(board.playerMaxHp, 10);
|
|
46850
|
+
const playerAttack = propPlayerAttack ?? num(board.playerAttack, 3);
|
|
46851
|
+
const depth = propDepth ?? num(board.depth, 1);
|
|
46852
|
+
const result = propResult ?? (str(board.result) || "none");
|
|
46853
|
+
const phase = propPhase ?? (str(board.phase) || "player_turn");
|
|
46854
|
+
propStairsX ?? num(board.stairsX, 3);
|
|
46855
|
+
propStairsY ?? num(board.stairsY, 3);
|
|
46856
|
+
const eventBus = useEventBus();
|
|
46857
|
+
const { t } = hooks.useTranslate();
|
|
46858
|
+
const [hoveredTile, setHoveredTile] = React91.useState(null);
|
|
46859
|
+
const emitMove = React91.useCallback((dx, dy) => {
|
|
46860
|
+
if (!moveEvent) return;
|
|
46861
|
+
eventBus.emit(`UI:${moveEvent}`, { dx, dy });
|
|
46862
|
+
}, [moveEvent, eventBus]);
|
|
46863
|
+
React91.useEffect(() => {
|
|
46864
|
+
const handler = (e) => {
|
|
46865
|
+
if (result !== "none" || phase !== "player_turn") return;
|
|
46866
|
+
if (e.key === "ArrowUp" || e.key === "w") emitMove(0, -1);
|
|
46867
|
+
if (e.key === "ArrowDown" || e.key === "s") emitMove(0, 1);
|
|
46868
|
+
if (e.key === "ArrowLeft" || e.key === "a") emitMove(-1, 0);
|
|
46869
|
+
if (e.key === "ArrowRight" || e.key === "d") emitMove(1, 0);
|
|
46870
|
+
};
|
|
46871
|
+
window.addEventListener("keydown", handler);
|
|
46872
|
+
return () => window.removeEventListener("keydown", handler);
|
|
46873
|
+
}, [result, phase, emitMove]);
|
|
46874
|
+
React91.useEffect(() => {
|
|
46875
|
+
if ((result === "won" || result === "lost") && gameEndEvent) {
|
|
46876
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
46877
|
+
}
|
|
46878
|
+
}, [result, gameEndEvent, eventBus]);
|
|
46879
|
+
const handleTileClick = React91.useCallback((x, y) => {
|
|
46880
|
+
if (result !== "none" || phase !== "player_turn") return;
|
|
46881
|
+
const dx = x - player.x;
|
|
46882
|
+
const dy = y - player.y;
|
|
46883
|
+
if (Math.abs(dx) + Math.abs(dy) === 1) {
|
|
46884
|
+
emitMove(dx, dy);
|
|
46885
|
+
}
|
|
46886
|
+
}, [result, phase, player, emitMove]);
|
|
46887
|
+
const handleReset = React91.useCallback(() => {
|
|
46888
|
+
if (!playAgainEvent) return;
|
|
46889
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
46890
|
+
}, [playAgainEvent, eventBus]);
|
|
46891
|
+
const isoUnits = React91.useMemo(() => {
|
|
46892
|
+
const liveEnemies = enemies.filter((e) => e.hp > 0);
|
|
46893
|
+
return [
|
|
46894
|
+
{
|
|
46895
|
+
id: "player",
|
|
46896
|
+
position: player,
|
|
46897
|
+
name: t("roguelike.player") || "Hero",
|
|
46898
|
+
team: "player",
|
|
46899
|
+
health: playerHp,
|
|
46900
|
+
maxHealth: playerMaxHp,
|
|
46901
|
+
unitType: "player",
|
|
46902
|
+
sprite: `${CDN4}/isometric-dungeon/Characters/Male/Male_0_Idle0.png`
|
|
46903
|
+
},
|
|
46904
|
+
...liveEnemies.map((e) => ({
|
|
46905
|
+
id: e.id,
|
|
46906
|
+
position: { x: e.x, y: e.y },
|
|
46907
|
+
name: t("roguelike.enemy") || "Enemy",
|
|
46908
|
+
team: "enemy",
|
|
46909
|
+
health: e.hp,
|
|
46910
|
+
maxHealth: 6,
|
|
46911
|
+
unitType: "enemy",
|
|
46912
|
+
sprite: `${CDN4}/sprite-sheets/shadow-legion-sprite-sheet-sw.png`
|
|
46913
|
+
}))
|
|
46914
|
+
];
|
|
46915
|
+
}, [player, enemies, playerHp, playerMaxHp, t]);
|
|
46916
|
+
const isoFeatures = React91.useMemo(
|
|
46917
|
+
() => items.map((it) => ({
|
|
46918
|
+
id: it.id,
|
|
46919
|
+
x: it.x,
|
|
46920
|
+
y: it.y,
|
|
46921
|
+
type: it.kind,
|
|
46922
|
+
sprite: FEATURE_SPRITE[it.kind] ?? FEATURE_SPRITE.health_potion
|
|
46923
|
+
})),
|
|
46924
|
+
[items]
|
|
46925
|
+
);
|
|
46926
|
+
const validMoves = React91.useMemo(() => {
|
|
46927
|
+
if (result !== "none" || phase !== "player_turn") return [];
|
|
46928
|
+
const dirs = [{ dx: 0, dy: -1 }, { dx: 0, dy: 1 }, { dx: -1, dy: 0 }, { dx: 1, dy: 0 }];
|
|
46929
|
+
return dirs.map(({ dx, dy }) => ({ x: player.x + dx, y: player.y + dy })).filter(({ x, y }) => {
|
|
46930
|
+
const tile = tiles.find((t2) => t2.x === x && t2.y === y);
|
|
46931
|
+
return tile?.passable ?? false;
|
|
46932
|
+
});
|
|
46933
|
+
}, [result, phase, player, tiles]);
|
|
46934
|
+
const isGameOver = result === "won" || result === "lost";
|
|
46935
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("roguelike-board relative min-h-[600px] bg-background", className), gap: "none", children: [
|
|
46936
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center justify-between px-4 py-2 border-b border-border", gap: "md", children: [
|
|
46937
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
46938
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.depth") || "Depth" }),
|
|
46939
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "font-bold tabular-nums", children: depth })
|
|
46940
|
+
] }),
|
|
46941
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
46942
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.hp") || "HP" }),
|
|
46943
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
46944
|
+
Typography,
|
|
46945
|
+
{
|
|
46946
|
+
variant: "body1",
|
|
46947
|
+
className: cn(
|
|
46948
|
+
"font-bold tabular-nums",
|
|
46949
|
+
playerHp <= Math.floor(playerMaxHp * 0.25) ? "text-error" : "text-success"
|
|
46950
|
+
),
|
|
46951
|
+
children: [
|
|
46952
|
+
playerHp,
|
|
46953
|
+
" / ",
|
|
46954
|
+
playerMaxHp
|
|
46955
|
+
]
|
|
46956
|
+
}
|
|
46957
|
+
)
|
|
46958
|
+
] }),
|
|
46959
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
46960
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.atk") || "ATK" }),
|
|
46961
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "font-bold tabular-nums text-foreground", children: playerAttack })
|
|
46962
|
+
] }),
|
|
46963
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
46964
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.phase") || "Phase" }),
|
|
46965
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "capitalize text-foreground", children: phase.replace("_", " ") })
|
|
46966
|
+
] })
|
|
46967
|
+
] }),
|
|
46968
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "relative flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
46969
|
+
IsometricCanvas_default,
|
|
46970
|
+
{
|
|
46971
|
+
tiles,
|
|
46972
|
+
units: isoUnits,
|
|
46973
|
+
features: isoFeatures,
|
|
46974
|
+
selectedUnitId: null,
|
|
46975
|
+
validMoves,
|
|
46976
|
+
attackTargets: [],
|
|
46977
|
+
hoveredTile,
|
|
46978
|
+
onTileClick: handleTileClick,
|
|
46979
|
+
onUnitClick: () => void 0,
|
|
46980
|
+
onTileHover: (x, y) => setHoveredTile({ x, y }),
|
|
46981
|
+
onTileLeave: () => setHoveredTile(null),
|
|
46982
|
+
scale,
|
|
46983
|
+
assetBaseUrl: propAssetManifest?.baseUrl ?? CDN4,
|
|
46984
|
+
assetManifest: propAssetManifest,
|
|
46985
|
+
unitScale
|
|
46986
|
+
}
|
|
46987
|
+
) }),
|
|
46988
|
+
!isGameOver && /* @__PURE__ */ jsxRuntime.jsx(HStack, { className: "justify-center gap-2 p-3", gap: "none", children: [
|
|
46989
|
+
{ label: "\u2191", dx: 0, dy: -1 },
|
|
46990
|
+
{ label: "\u2193", dx: 0, dy: 1 },
|
|
46991
|
+
{ label: "\u2190", dx: -1, dy: 0 },
|
|
46992
|
+
{ label: "\u2192", dx: 1, dy: 0 }
|
|
46993
|
+
].map(({ label, dx, dy }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
46994
|
+
Button,
|
|
46995
|
+
{
|
|
46996
|
+
variant: "secondary",
|
|
46997
|
+
className: "w-9 h-9 p-0 text-base font-mono",
|
|
46998
|
+
onClick: () => emitMove(dx, dy),
|
|
46999
|
+
disabled: phase !== "player_turn",
|
|
47000
|
+
children: label
|
|
47001
|
+
},
|
|
47002
|
+
label
|
|
47003
|
+
)) }),
|
|
47004
|
+
isGameOver && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/75 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
47005
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
47006
|
+
Typography,
|
|
47007
|
+
{
|
|
47008
|
+
variant: "h2",
|
|
47009
|
+
className: cn(
|
|
47010
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
47011
|
+
result === "won" ? "text-warning" : "text-error"
|
|
47012
|
+
),
|
|
47013
|
+
children: result === "won" ? t("roguelike.victory") || "Victory!" : t("roguelike.defeat") || "Defeated!"
|
|
47014
|
+
}
|
|
47015
|
+
),
|
|
47016
|
+
result === "won" && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: t("roguelike.cleared") || `Dungeon cleared! Depth reached: ${depth}` }),
|
|
47017
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
47018
|
+
Button,
|
|
47019
|
+
{
|
|
47020
|
+
variant: "primary",
|
|
47021
|
+
className: "px-8 py-3 font-semibold",
|
|
47022
|
+
onClick: handleReset,
|
|
47023
|
+
children: t("roguelike.playAgain") || "Play Again"
|
|
47024
|
+
}
|
|
47025
|
+
)
|
|
47026
|
+
] }) })
|
|
47027
|
+
] });
|
|
47028
|
+
}
|
|
47029
|
+
var CDN4, DEFAULT_TILES2, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
|
|
47030
|
+
var init_RoguelikeBoard = __esm({
|
|
47031
|
+
"components/game/organisms/RoguelikeBoard.tsx"() {
|
|
47032
|
+
"use client";
|
|
47033
|
+
init_cn();
|
|
47034
|
+
init_useEventBus();
|
|
47035
|
+
init_Box();
|
|
47036
|
+
init_Button();
|
|
47037
|
+
init_Typography();
|
|
47038
|
+
init_Stack();
|
|
47039
|
+
init_IsometricCanvas();
|
|
47040
|
+
init_boardEntity();
|
|
47041
|
+
CDN4 = "https://almadar-kflow-assets.web.app/shared";
|
|
47042
|
+
DEFAULT_TILES2 = [
|
|
47043
|
+
{ x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47044
|
+
{ x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47045
|
+
{ x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47046
|
+
{ x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47047
|
+
{ x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47048
|
+
{ x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47049
|
+
{ x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47050
|
+
{ x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47051
|
+
{ x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47052
|
+
{ x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47053
|
+
{ x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47054
|
+
{ x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
47055
|
+
{ x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
47056
|
+
{ x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47057
|
+
{ x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47058
|
+
{ x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47059
|
+
{ x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47060
|
+
{ x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
47061
|
+
{ x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` },
|
|
47062
|
+
{ x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47063
|
+
{ x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47064
|
+
{ x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47065
|
+
{ x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47066
|
+
{ x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47067
|
+
{ x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` }
|
|
47068
|
+
];
|
|
47069
|
+
DEFAULT_ENEMIES = [
|
|
47070
|
+
{ id: "e1", x: 3, y: 1, hp: 5, attack: 2 },
|
|
47071
|
+
{ id: "e2", x: 1, y: 3, hp: 4, attack: 1 }
|
|
47072
|
+
];
|
|
47073
|
+
DEFAULT_ITEMS = [
|
|
47074
|
+
{ id: "i1", x: 2, y: 2, kind: "health_potion" }
|
|
47075
|
+
];
|
|
47076
|
+
FEATURE_SPRITE = {
|
|
47077
|
+
health_potion: `${CDN4}/isometric-dungeon/Isometric/chestClosed_E.png`,
|
|
47078
|
+
sword: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`,
|
|
47079
|
+
shield: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`
|
|
47080
|
+
};
|
|
47081
|
+
RoguelikeBoard.displayName = "RoguelikeBoard";
|
|
47082
|
+
}
|
|
47083
|
+
});
|
|
47084
|
+
function RoguelikeTemplate({
|
|
47085
|
+
entity,
|
|
47086
|
+
scale = 0.45,
|
|
47087
|
+
unitScale = 1,
|
|
47088
|
+
tiles = DEFAULT_ROGUELIKE_TILES,
|
|
47089
|
+
enemies,
|
|
47090
|
+
items,
|
|
47091
|
+
stairsX,
|
|
47092
|
+
stairsY,
|
|
47093
|
+
assetManifest = DEFAULT_ROGUELIKE_MANIFEST,
|
|
47094
|
+
gameEndEvent,
|
|
47095
|
+
className
|
|
47096
|
+
}) {
|
|
47097
|
+
const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
|
|
47098
|
+
if (!resolved && !tiles && !assetManifest) return null;
|
|
47099
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
47100
|
+
RoguelikeBoard,
|
|
47101
|
+
{
|
|
47102
|
+
entity: resolved,
|
|
47103
|
+
tiles,
|
|
47104
|
+
enemies,
|
|
47105
|
+
items,
|
|
47106
|
+
stairsX,
|
|
47107
|
+
stairsY,
|
|
47108
|
+
assetManifest,
|
|
47109
|
+
scale,
|
|
47110
|
+
unitScale,
|
|
47111
|
+
moveEvent: "MOVE",
|
|
47112
|
+
playAgainEvent: "PLAY_AGAIN",
|
|
47113
|
+
gameEndEvent: gameEndEvent ?? "GAME_END",
|
|
47114
|
+
className
|
|
47115
|
+
}
|
|
47116
|
+
);
|
|
47117
|
+
}
|
|
47118
|
+
var CDN5, DEFAULT_ROGUELIKE_TILES, DEFAULT_ROGUELIKE_MANIFEST;
|
|
47119
|
+
var init_RoguelikeTemplate = __esm({
|
|
47120
|
+
"components/game/templates/RoguelikeTemplate.tsx"() {
|
|
47121
|
+
init_RoguelikeBoard();
|
|
47122
|
+
CDN5 = "https://almadar-kflow-assets.web.app/shared";
|
|
47123
|
+
DEFAULT_ROGUELIKE_TILES = [
|
|
47124
|
+
{ x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47125
|
+
{ x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47126
|
+
{ x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47127
|
+
{ x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47128
|
+
{ x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47129
|
+
{ x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47130
|
+
{ x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47131
|
+
{ x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47132
|
+
{ x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47133
|
+
{ x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47134
|
+
{ x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47135
|
+
{ x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
47136
|
+
{ x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
47137
|
+
{ x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47138
|
+
{ x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47139
|
+
{ x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47140
|
+
{ x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47141
|
+
{ x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
47142
|
+
{ x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stairs_E.png` },
|
|
47143
|
+
{ x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47144
|
+
{ x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47145
|
+
{ x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47146
|
+
{ x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47147
|
+
{ x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47148
|
+
{ x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` }
|
|
47149
|
+
];
|
|
47150
|
+
DEFAULT_ROGUELIKE_MANIFEST = {
|
|
47151
|
+
baseUrl: CDN5,
|
|
47152
|
+
terrains: {
|
|
47153
|
+
floor: "/isometric-dungeon/Isometric/dirt_E.png",
|
|
47154
|
+
wall: "/isometric-dungeon/Isometric/stoneInset_E.png",
|
|
47155
|
+
stairs: "/isometric-dungeon/Isometric/stairs_E.png",
|
|
47156
|
+
planks: "/isometric-dungeon/Isometric/planks_E.png"
|
|
47157
|
+
},
|
|
47158
|
+
units: {
|
|
47159
|
+
player: "/isometric-dungeon/Characters/Male/Male_0_Idle0.png",
|
|
47160
|
+
enemy: "/sprite-sheets/shadow-legion-sprite-sheet-sw.png"
|
|
47161
|
+
},
|
|
47162
|
+
features: {
|
|
47163
|
+
health_potion: "/isometric-dungeon/Isometric/chestClosed_E.png",
|
|
47164
|
+
sword: "/isometric-dungeon/Isometric/barrel_E.png",
|
|
47165
|
+
shield: "/isometric-dungeon/Isometric/barrel_E.png"
|
|
47166
|
+
}
|
|
47167
|
+
};
|
|
47168
|
+
RoguelikeTemplate.displayName = "RoguelikeTemplate";
|
|
47169
|
+
}
|
|
47170
|
+
});
|
|
44894
47171
|
|
|
44895
47172
|
// lib/traitRegistry.ts
|
|
44896
47173
|
function notifyListeners2() {
|
|
@@ -46144,7 +48421,7 @@ function getAllEvents(traits2) {
|
|
|
46144
48421
|
function EventDispatcherTab({ traits: traits2, schema }) {
|
|
46145
48422
|
const eventBus = useEventBus();
|
|
46146
48423
|
const { t } = hooks.useTranslate();
|
|
46147
|
-
const [
|
|
48424
|
+
const [log15, setLog] = React91__namespace.useState([]);
|
|
46148
48425
|
const prevStatesRef = React91__namespace.useRef(/* @__PURE__ */ new Map());
|
|
46149
48426
|
React91__namespace.useEffect(() => {
|
|
46150
48427
|
for (const trait of traits2) {
|
|
@@ -46208,9 +48485,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
|
|
|
46208
48485
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.otherEvents") }),
|
|
46209
48486
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1", children: unavailableEvents.map((event) => /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", className: "opacity-50", children: event }, event)) })
|
|
46210
48487
|
] }),
|
|
46211
|
-
|
|
48488
|
+
log15.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
46212
48489
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.recentTransitions") }),
|
|
46213
|
-
/* @__PURE__ */ jsxRuntime.jsx(Stack, { gap: "xs", children:
|
|
48490
|
+
/* @__PURE__ */ jsxRuntime.jsx(Stack, { gap: "xs", children: log15.map((entry, i) => /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
|
|
46214
48491
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-purple-400", children: entry.traitName }),
|
|
46215
48492
|
" ",
|
|
46216
48493
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-500", children: entry.from }),
|
|
@@ -47013,17 +49290,24 @@ function SequencerBoard({
|
|
|
47013
49290
|
const { emit } = useEventBus();
|
|
47014
49291
|
const { t } = hooks.useTranslate();
|
|
47015
49292
|
const resolved = boardEntity(entity);
|
|
47016
|
-
const maxSlots = num(resolved?.maxSlots);
|
|
49293
|
+
const maxSlots = num(resolved?.maxSlots) || 3;
|
|
47017
49294
|
const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
|
|
47018
49295
|
const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
|
|
47019
49296
|
const allowDuplicates = resolved?.allowDuplicates !== false;
|
|
49297
|
+
const entitySlots = Array.isArray(resolved?.slots) ? resolved.slots : [];
|
|
49298
|
+
const entityResult = str(resolved?.result);
|
|
49299
|
+
const entityAttempts = num(resolved?.attempts);
|
|
49300
|
+
const entityCurrentStep = typeof resolved?.currentStep === "number" ? resolved.currentStep : -1;
|
|
49301
|
+
const slots = Array.from({ length: maxSlots }, (_, i) => {
|
|
49302
|
+
const entitySlot = entitySlots.find((s) => s.index === i);
|
|
49303
|
+
if (!entitySlot?.placedActionId) return void 0;
|
|
49304
|
+
return availableActions.find((a) => a.id === entitySlot.placedActionId);
|
|
49305
|
+
});
|
|
49306
|
+
const isSuccess = entityResult === "win";
|
|
49307
|
+
const attempts = entityAttempts;
|
|
49308
|
+
const isPlayingBack = entityCurrentStep >= 0 && !isSuccess;
|
|
49309
|
+
const currentStep = entityCurrentStep;
|
|
47020
49310
|
const [headerError, setHeaderError] = React91.useState(false);
|
|
47021
|
-
const [slots, setSlots] = React91.useState(
|
|
47022
|
-
() => Array.from({ length: maxSlots }, () => void 0)
|
|
47023
|
-
);
|
|
47024
|
-
const [playState, setPlayState] = React91.useState("idle");
|
|
47025
|
-
const [currentStep, setCurrentStep] = React91.useState(-1);
|
|
47026
|
-
const [attempts, setAttempts] = React91.useState(0);
|
|
47027
49311
|
const [slotFeedback, setSlotFeedback] = React91.useState(
|
|
47028
49312
|
() => Array.from({ length: maxSlots }, () => null)
|
|
47029
49313
|
);
|
|
@@ -47032,11 +49316,6 @@ function SequencerBoard({
|
|
|
47032
49316
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
47033
49317
|
}, []);
|
|
47034
49318
|
const handleSlotDrop = React91.useCallback((index, item) => {
|
|
47035
|
-
setSlots((prev) => {
|
|
47036
|
-
const next = [...prev];
|
|
47037
|
-
next[index] = item;
|
|
47038
|
-
return next;
|
|
47039
|
-
});
|
|
47040
49319
|
setSlotFeedback((prev) => {
|
|
47041
49320
|
const next = [...prev];
|
|
47042
49321
|
next[index] = null;
|
|
@@ -47046,11 +49325,6 @@ function SequencerBoard({
|
|
|
47046
49325
|
if (placeEvent) emit(`UI:${placeEvent}`, { slotIndex: index, actionId: item.id });
|
|
47047
49326
|
}, [emit, placeEvent]);
|
|
47048
49327
|
const handleSlotRemove = React91.useCallback((index) => {
|
|
47049
|
-
setSlots((prev) => {
|
|
47050
|
-
const next = [...prev];
|
|
47051
|
-
next[index] = void 0;
|
|
47052
|
-
return next;
|
|
47053
|
-
});
|
|
47054
49328
|
setSlotFeedback((prev) => {
|
|
47055
49329
|
const next = [...prev];
|
|
47056
49330
|
next[index] = null;
|
|
@@ -47061,48 +49335,34 @@ function SequencerBoard({
|
|
|
47061
49335
|
}, [emit, removeEvent]);
|
|
47062
49336
|
const handleReset = React91.useCallback(() => {
|
|
47063
49337
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
47064
|
-
setSlots(Array.from({ length: maxSlots }, () => void 0));
|
|
47065
|
-
setPlayState("idle");
|
|
47066
|
-
setCurrentStep(-1);
|
|
47067
|
-
setAttempts(0);
|
|
47068
49338
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
47069
49339
|
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
47070
49340
|
}, [maxSlots, playAgainEvent, emit]);
|
|
47071
49341
|
const filledSlots = slots.filter((s) => !!s);
|
|
47072
|
-
const canPlay = filledSlots.length > 0 &&
|
|
49342
|
+
const canPlay = filledSlots.length > 0 && !isPlayingBack && !isSuccess;
|
|
47073
49343
|
const handlePlay = React91.useCallback(() => {
|
|
47074
49344
|
if (!canPlay) return;
|
|
47075
49345
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
47076
49346
|
emit("UI:PLAY_SOUND", { key: "confirm" });
|
|
47077
49347
|
const sequence = slots.map((s) => s?.id || "");
|
|
47078
|
-
|
|
47079
|
-
|
|
47080
|
-
}
|
|
47081
|
-
setPlayState("playing");
|
|
47082
|
-
setCurrentStep(0);
|
|
49348
|
+
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
49349
|
+
if (playEvent) emit(`UI:${playEvent}`, { sequence });
|
|
47083
49350
|
let step = 0;
|
|
47084
49351
|
const advance = () => {
|
|
47085
49352
|
step++;
|
|
49353
|
+
emit("UI:STEP", { step });
|
|
47086
49354
|
if (step >= maxSlots) {
|
|
49355
|
+
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
47087
49356
|
const playerSeq = slots.map((s) => s?.id);
|
|
47088
|
-
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
47089
49357
|
const success = solutions.some(
|
|
47090
49358
|
(sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
|
|
47091
49359
|
);
|
|
47092
|
-
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
47093
49360
|
if (success) {
|
|
47094
|
-
setPlayState("success");
|
|
47095
|
-
setCurrentStep(-1);
|
|
47096
49361
|
emit("UI:PLAY_SOUND", { key: "levelComplete" });
|
|
47097
|
-
if (completeEvent) {
|
|
47098
|
-
emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
47099
|
-
}
|
|
49362
|
+
if (completeEvent) emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
47100
49363
|
} else {
|
|
47101
|
-
setAttempts((prev) => prev + 1);
|
|
47102
49364
|
const feedback = computeSlotFeedback(playerSeq, solutions);
|
|
47103
49365
|
setSlotFeedback(feedback);
|
|
47104
|
-
setPlayState("idle");
|
|
47105
|
-
setCurrentStep(-1);
|
|
47106
49366
|
emit("UI:PLAY_SOUND", { key: "fail" });
|
|
47107
49367
|
const correctCount2 = feedback.filter((f3) => f3 === "correct").length;
|
|
47108
49368
|
for (let ci = 0; ci < correctCount2; ci++) {
|
|
@@ -47112,7 +49372,6 @@ function SequencerBoard({
|
|
|
47112
49372
|
}
|
|
47113
49373
|
}
|
|
47114
49374
|
} else {
|
|
47115
|
-
setCurrentStep(step);
|
|
47116
49375
|
timerRef.current = setTimeout(advance, stepDurationMs);
|
|
47117
49376
|
}
|
|
47118
49377
|
};
|
|
@@ -47162,7 +49421,7 @@ function SequencerBoard({
|
|
|
47162
49421
|
/* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
|
|
47163
49422
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center justify-between", children: [
|
|
47164
49423
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
|
|
47165
|
-
hasFeedback &&
|
|
49424
|
+
hasFeedback && !isPlayingBack && !isSuccess && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
|
|
47166
49425
|
`${correctCount}/${maxSlots} `,
|
|
47167
49426
|
"\u2705"
|
|
47168
49427
|
] })
|
|
@@ -47174,7 +49433,7 @@ function SequencerBoard({
|
|
|
47174
49433
|
maxSlots,
|
|
47175
49434
|
onSlotDrop: handleSlotDrop,
|
|
47176
49435
|
onSlotRemove: handleSlotRemove,
|
|
47177
|
-
playing:
|
|
49436
|
+
playing: isPlayingBack,
|
|
47178
49437
|
currentStep,
|
|
47179
49438
|
categoryColors,
|
|
47180
49439
|
slotFeedback,
|
|
@@ -47182,7 +49441,7 @@ function SequencerBoard({
|
|
|
47182
49441
|
}
|
|
47183
49442
|
)
|
|
47184
49443
|
] }),
|
|
47185
|
-
|
|
49444
|
+
!isPlayingBack && /* @__PURE__ */ jsxRuntime.jsx(
|
|
47186
49445
|
ActionPalette,
|
|
47187
49446
|
{
|
|
47188
49447
|
actions: availableActions,
|
|
@@ -47192,8 +49451,8 @@ function SequencerBoard({
|
|
|
47192
49451
|
label: t("sequencer.dragActions")
|
|
47193
49452
|
}
|
|
47194
49453
|
),
|
|
47195
|
-
hasFeedback &&
|
|
47196
|
-
|
|
49454
|
+
hasFeedback && !isPlayingBack && !isSuccess && attempts > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: t(encourageKey) }) }),
|
|
49455
|
+
isSuccess && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("sequencer.levelComplete") }) }),
|
|
47197
49456
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
|
|
47198
49457
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
47199
49458
|
Button,
|
|
@@ -48149,9 +50408,9 @@ function VariablePanel({
|
|
|
48149
50408
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
|
|
48150
50409
|
variables.map((v) => {
|
|
48151
50410
|
const name = v.name == null ? "" : String(v.name);
|
|
48152
|
-
const value =
|
|
48153
|
-
const max =
|
|
48154
|
-
const min =
|
|
50411
|
+
const value = numField2(v.value);
|
|
50412
|
+
const max = numField2(v.max, 100);
|
|
50413
|
+
const min = numField2(v.min, 0);
|
|
48155
50414
|
const unit = v.unit == null ? "" : String(v.unit);
|
|
48156
50415
|
const pct = Math.round((value - min) / (max - min) * 100);
|
|
48157
50416
|
const isHigh = pct > 80;
|
|
@@ -48181,12 +50440,12 @@ function VariablePanel({
|
|
|
48181
50440
|
})
|
|
48182
50441
|
] });
|
|
48183
50442
|
}
|
|
48184
|
-
var
|
|
50443
|
+
var numField2;
|
|
48185
50444
|
var init_VariablePanel = __esm({
|
|
48186
50445
|
"components/game/organisms/puzzles/state-architect/VariablePanel.tsx"() {
|
|
48187
50446
|
init_atoms2();
|
|
48188
50447
|
init_cn();
|
|
48189
|
-
|
|
50448
|
+
numField2 = (v, fallback = 0) => {
|
|
48190
50449
|
const n = Number(v);
|
|
48191
50450
|
return Number.isFinite(n) ? n : fallback;
|
|
48192
50451
|
};
|
|
@@ -48243,6 +50502,9 @@ function StateArchitectBoard({
|
|
|
48243
50502
|
stepDurationMs = 600,
|
|
48244
50503
|
testEvent,
|
|
48245
50504
|
completeEvent,
|
|
50505
|
+
addTransitionEvent,
|
|
50506
|
+
removeTransitionEvent,
|
|
50507
|
+
playAgainEvent,
|
|
48246
50508
|
className
|
|
48247
50509
|
}) {
|
|
48248
50510
|
const { emit } = useEventBus();
|
|
@@ -48255,14 +50517,16 @@ function StateArchitectBoard({
|
|
|
48255
50517
|
const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
|
|
48256
50518
|
const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
|
|
48257
50519
|
const entityVariables = rows(resolved?.variables);
|
|
48258
|
-
const
|
|
50520
|
+
const transitions = entityTransitions;
|
|
50521
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
50522
|
+
const entityResult = str(resolved?.result) || "none";
|
|
50523
|
+
const isSuccess = entityResult === "win";
|
|
50524
|
+
const [isTesting, setIsTesting] = React91.useState(false);
|
|
48259
50525
|
const [headerError, setHeaderError] = React91.useState(false);
|
|
48260
|
-
const [playState, setPlayState] = React91.useState("editing");
|
|
48261
50526
|
const [currentState, setCurrentState] = React91.useState(initialState);
|
|
48262
50527
|
const [selectedState, setSelectedState] = React91.useState(null);
|
|
48263
50528
|
const [testResults, setTestResults] = React91.useState([]);
|
|
48264
50529
|
const [variables, setVariables] = React91.useState(() => [...entityVariables]);
|
|
48265
|
-
const [attempts, setAttempts] = React91.useState(0);
|
|
48266
50530
|
const timerRef = React91.useRef(null);
|
|
48267
50531
|
const [addingFrom, setAddingFrom] = React91.useState(null);
|
|
48268
50532
|
React91.useEffect(() => () => {
|
|
@@ -48272,7 +50536,7 @@ function StateArchitectBoard({
|
|
|
48272
50536
|
const GRAPH_H = 400;
|
|
48273
50537
|
const positions = React91.useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
|
|
48274
50538
|
const handleStateClick = React91.useCallback((state) => {
|
|
48275
|
-
if (
|
|
50539
|
+
if (isTesting) return;
|
|
48276
50540
|
if (addingFrom) {
|
|
48277
50541
|
if (addingFrom !== state) {
|
|
48278
50542
|
const event = availableEvents[0] || "EVENT";
|
|
@@ -48282,20 +50546,20 @@ function StateArchitectBoard({
|
|
|
48282
50546
|
to: state,
|
|
48283
50547
|
event
|
|
48284
50548
|
};
|
|
48285
|
-
|
|
50549
|
+
if (addTransitionEvent) emit(`UI:${addTransitionEvent}`, { id: newTrans.id, from: newTrans.from, to: newTrans.to, event: newTrans.event });
|
|
48286
50550
|
}
|
|
48287
50551
|
setAddingFrom(null);
|
|
48288
50552
|
} else {
|
|
48289
50553
|
setSelectedState(state);
|
|
48290
50554
|
}
|
|
48291
|
-
}, [
|
|
50555
|
+
}, [isTesting, addingFrom, availableEvents, addTransitionEvent, emit]);
|
|
48292
50556
|
const handleStartAddTransition = React91.useCallback(() => {
|
|
48293
50557
|
if (!selectedState) return;
|
|
48294
50558
|
setAddingFrom(selectedState);
|
|
48295
50559
|
}, [selectedState]);
|
|
48296
50560
|
const handleRemoveTransition = React91.useCallback((transId) => {
|
|
48297
|
-
|
|
48298
|
-
}, []);
|
|
50561
|
+
if (removeTransitionEvent) emit(`UI:${removeTransitionEvent}`, { id: transId });
|
|
50562
|
+
}, [removeTransitionEvent, emit]);
|
|
48299
50563
|
const machine = React91.useMemo(() => ({
|
|
48300
50564
|
name: entityName,
|
|
48301
50565
|
description: str(resolved?.description),
|
|
@@ -48309,16 +50573,16 @@ function StateArchitectBoard({
|
|
|
48309
50573
|
}))
|
|
48310
50574
|
}), [entityName, resolved, entityStates, currentState, transitions]);
|
|
48311
50575
|
const handleTest = React91.useCallback(() => {
|
|
48312
|
-
if (
|
|
50576
|
+
if (isTesting) return;
|
|
48313
50577
|
if (testEvent) emit(`UI:${testEvent}`, {});
|
|
48314
|
-
|
|
50578
|
+
setIsTesting(true);
|
|
48315
50579
|
setTestResults([]);
|
|
48316
50580
|
const results = [];
|
|
48317
50581
|
let testIdx = 0;
|
|
48318
50582
|
const runNextTest = () => {
|
|
48319
50583
|
if (testIdx >= testCases.length) {
|
|
48320
50584
|
const allPassed = results.every((r2) => r2.passed);
|
|
48321
|
-
|
|
50585
|
+
setIsTesting(false);
|
|
48322
50586
|
setTestResults(results);
|
|
48323
50587
|
if (allPassed && completeEvent) {
|
|
48324
50588
|
emit(`UI:${completeEvent}`, {
|
|
@@ -48326,9 +50590,6 @@ function StateArchitectBoard({
|
|
|
48326
50590
|
passedTests: results.filter((r2) => r2.passed).length
|
|
48327
50591
|
});
|
|
48328
50592
|
}
|
|
48329
|
-
if (!allPassed) {
|
|
48330
|
-
setAttempts((prev) => prev + 1);
|
|
48331
|
-
}
|
|
48332
50593
|
return;
|
|
48333
50594
|
}
|
|
48334
50595
|
const testCase = testCases[testIdx];
|
|
@@ -48351,24 +50612,23 @@ function StateArchitectBoard({
|
|
|
48351
50612
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
48352
50613
|
};
|
|
48353
50614
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
48354
|
-
}, [
|
|
50615
|
+
}, [isTesting, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
|
|
48355
50616
|
const handleTryAgain = React91.useCallback(() => {
|
|
48356
50617
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
48357
|
-
|
|
50618
|
+
setIsTesting(false);
|
|
48358
50619
|
setCurrentState(initialState);
|
|
48359
50620
|
setTestResults([]);
|
|
48360
50621
|
}, [initialState]);
|
|
48361
50622
|
const handleReset = React91.useCallback(() => {
|
|
48362
50623
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
48363
|
-
|
|
48364
|
-
setPlayState("editing");
|
|
50624
|
+
setIsTesting(false);
|
|
48365
50625
|
setCurrentState(initialState);
|
|
48366
50626
|
setTestResults([]);
|
|
48367
50627
|
setVariables([...entityVariables]);
|
|
48368
50628
|
setSelectedState(null);
|
|
48369
50629
|
setAddingFrom(null);
|
|
48370
|
-
|
|
48371
|
-
}, [
|
|
50630
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
50631
|
+
}, [initialState, entityVariables, playAgainEvent, emit]);
|
|
48372
50632
|
const codeData = React91.useMemo(() => ({
|
|
48373
50633
|
name: entityName,
|
|
48374
50634
|
states: entityStates,
|
|
@@ -48464,7 +50724,7 @@ function StateArchitectBoard({
|
|
|
48464
50724
|
]
|
|
48465
50725
|
}
|
|
48466
50726
|
),
|
|
48467
|
-
|
|
50727
|
+
!isTesting && /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "sm", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
48468
50728
|
Button,
|
|
48469
50729
|
{
|
|
48470
50730
|
variant: "ghost",
|
|
@@ -48486,7 +50746,7 @@ function StateArchitectBoard({
|
|
|
48486
50746
|
t2.guardHint,
|
|
48487
50747
|
")"
|
|
48488
50748
|
] }),
|
|
48489
|
-
|
|
50749
|
+
!isTesting && /* @__PURE__ */ jsxRuntime.jsx(
|
|
48490
50750
|
Button,
|
|
48491
50751
|
{
|
|
48492
50752
|
variant: "ghost",
|
|
@@ -48518,21 +50778,21 @@ function StateArchitectBoard({
|
|
|
48518
50778
|
resolved.showCodeView !== false && /* @__PURE__ */ jsxRuntime.jsx(StateJsonView, { data: codeData, label: "View Code" })
|
|
48519
50779
|
] })
|
|
48520
50780
|
] }),
|
|
48521
|
-
|
|
48522
|
-
|
|
50781
|
+
isSuccess && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("stateArchitect.allPassed") }) }),
|
|
50782
|
+
!isTesting && !isSuccess && testResults.some((r2) => !r2.passed) && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
48523
50783
|
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(ENCOURAGEMENT_KEYS3[Math.min(attempts - 1, ENCOURAGEMENT_KEYS3.length - 1)] ?? ENCOURAGEMENT_KEYS3[0]) }) }),
|
|
48524
|
-
attempts >=
|
|
50784
|
+
!isSuccess && attempts >= 2 && Boolean(str(resolved?.hint)) && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-start", gap: "xs", children: [
|
|
48525
50785
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
48526
50786
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
|
|
48527
50787
|
] }) })
|
|
48528
50788
|
] }),
|
|
48529
50789
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
|
|
48530
|
-
|
|
50790
|
+
!isTesting && !isSuccess && testResults.some((r2) => !r2.passed) ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
48531
50791
|
Button,
|
|
48532
50792
|
{
|
|
48533
50793
|
variant: "primary",
|
|
48534
50794
|
onClick: handleTest,
|
|
48535
|
-
disabled:
|
|
50795
|
+
disabled: isTesting,
|
|
48536
50796
|
children: "\u25B6 " + t("game.runTests")
|
|
48537
50797
|
}
|
|
48538
50798
|
),
|
|
@@ -49038,6 +51298,289 @@ var init_ToastSlot = __esm({
|
|
|
49038
51298
|
ToastSlot.displayName = "ToastSlot";
|
|
49039
51299
|
}
|
|
49040
51300
|
});
|
|
51301
|
+
function tilesToIso(tiles) {
|
|
51302
|
+
return tiles.map((t) => ({
|
|
51303
|
+
x: t.x,
|
|
51304
|
+
y: t.y,
|
|
51305
|
+
terrain: t.terrain,
|
|
51306
|
+
terrainSprite: t.terrainSprite ?? TERRAIN_SPRITES[t.terrain ?? "ground"],
|
|
51307
|
+
passable: t.passable
|
|
51308
|
+
}));
|
|
51309
|
+
}
|
|
51310
|
+
function towersToUnits(towers) {
|
|
51311
|
+
return towers.map((t) => ({
|
|
51312
|
+
id: t.id,
|
|
51313
|
+
position: { x: t.x, y: t.y },
|
|
51314
|
+
name: "Tower",
|
|
51315
|
+
team: "player",
|
|
51316
|
+
sprite: TOWER_SPRITE,
|
|
51317
|
+
unitType: "guardian",
|
|
51318
|
+
health: 1,
|
|
51319
|
+
maxHealth: 1
|
|
51320
|
+
}));
|
|
51321
|
+
}
|
|
51322
|
+
function creepsToUnits(creeps) {
|
|
51323
|
+
return creeps.map((c) => ({
|
|
51324
|
+
id: c.id,
|
|
51325
|
+
position: { x: c.x, y: c.y },
|
|
51326
|
+
name: "Creep",
|
|
51327
|
+
team: "enemy",
|
|
51328
|
+
sprite: CREEP_SPRITE,
|
|
51329
|
+
unitType: "scrapper",
|
|
51330
|
+
health: c.hp,
|
|
51331
|
+
maxHealth: c.maxHp
|
|
51332
|
+
}));
|
|
51333
|
+
}
|
|
51334
|
+
function pathToFeatures(path) {
|
|
51335
|
+
return path.map((p2, i) => ({
|
|
51336
|
+
id: `path-${i}`,
|
|
51337
|
+
x: p2.x,
|
|
51338
|
+
y: p2.y,
|
|
51339
|
+
type: "path-marker",
|
|
51340
|
+
sprite: `${CDN6}world-map/road_straight.png`
|
|
51341
|
+
}));
|
|
51342
|
+
}
|
|
51343
|
+
function TowerDefenseBoard({
|
|
51344
|
+
entity,
|
|
51345
|
+
tiles: propTiles,
|
|
51346
|
+
path: propPath,
|
|
51347
|
+
towers: propTowers,
|
|
51348
|
+
creeps: propCreeps,
|
|
51349
|
+
gold: propGold,
|
|
51350
|
+
lives: propLives,
|
|
51351
|
+
wave: propWave,
|
|
51352
|
+
maxWaves: propMaxWaves,
|
|
51353
|
+
result: propResult,
|
|
51354
|
+
waveActive: propWaveActive,
|
|
51355
|
+
towerCost = 25,
|
|
51356
|
+
scale = 0.45,
|
|
51357
|
+
placeTowerEvent,
|
|
51358
|
+
startWaveEvent,
|
|
51359
|
+
playAgainEvent,
|
|
51360
|
+
gameEndEvent,
|
|
51361
|
+
className
|
|
51362
|
+
}) {
|
|
51363
|
+
const board = boardEntity(entity) ?? {};
|
|
51364
|
+
const eventBus = useEventBus();
|
|
51365
|
+
const { t } = hooks.useTranslate();
|
|
51366
|
+
const tiles = propTiles ?? rows(board.tiles);
|
|
51367
|
+
const path = propPath ?? rows(board.path);
|
|
51368
|
+
const towers = propTowers ?? rows(board.towers);
|
|
51369
|
+
const creeps = propCreeps ?? rows(board.creeps);
|
|
51370
|
+
const gold = propGold ?? num(board.gold, 100);
|
|
51371
|
+
const lives = propLives ?? num(board.lives, 20);
|
|
51372
|
+
const wave = propWave ?? num(board.wave, 1);
|
|
51373
|
+
const maxWaves = propMaxWaves ?? num(board.maxWaves, 5);
|
|
51374
|
+
const result = propResult ?? (str(board.result) || "none");
|
|
51375
|
+
const waveActive = propWaveActive ?? Boolean(board.waveActive);
|
|
51376
|
+
const [hoveredTile, setHoveredTile] = React91.useState(null);
|
|
51377
|
+
const emittedGameEnd = React91.useRef(false);
|
|
51378
|
+
if (result !== "none" && !emittedGameEnd.current) {
|
|
51379
|
+
emittedGameEnd.current = true;
|
|
51380
|
+
if (gameEndEvent) {
|
|
51381
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
51382
|
+
}
|
|
51383
|
+
}
|
|
51384
|
+
if (result === "none") {
|
|
51385
|
+
emittedGameEnd.current = false;
|
|
51386
|
+
}
|
|
51387
|
+
const towerPositions = React91.useMemo(() => new Set(towers.map((t2) => `${t2.x},${t2.y}`)), [towers]);
|
|
51388
|
+
const pathPositions = React91.useMemo(() => new Set(path.map((p2) => `${p2.x},${p2.y}`)), [path]);
|
|
51389
|
+
const validMoves = React91.useMemo(() => {
|
|
51390
|
+
if (result !== "none" || gold < towerCost) return [];
|
|
51391
|
+
return tiles.filter(
|
|
51392
|
+
(t2) => t2.passable !== false && !towerPositions.has(`${t2.x},${t2.y}`) && !pathPositions.has(`${t2.x},${t2.y}`)
|
|
51393
|
+
).map((t2) => ({ x: t2.x, y: t2.y }));
|
|
51394
|
+
}, [tiles, towerPositions, pathPositions, result, gold, towerCost]);
|
|
51395
|
+
const isoTiles = React91.useMemo(() => tilesToIso(tiles), [tiles]);
|
|
51396
|
+
const towerUnits = React91.useMemo(() => towersToUnits(towers), [towers]);
|
|
51397
|
+
const creepUnits = React91.useMemo(() => creepsToUnits(creeps), [creeps]);
|
|
51398
|
+
const isoUnits = React91.useMemo(() => [...towerUnits, ...creepUnits], [towerUnits, creepUnits]);
|
|
51399
|
+
const pathFeatures = React91.useMemo(() => pathToFeatures(path), [path]);
|
|
51400
|
+
const handleTileClick = React91.useCallback((x, y) => {
|
|
51401
|
+
if (result !== "none") return;
|
|
51402
|
+
if (pathPositions.has(`${x},${y}`)) return;
|
|
51403
|
+
if (towerPositions.has(`${x},${y}`)) return;
|
|
51404
|
+
if (gold < towerCost) return;
|
|
51405
|
+
if (placeTowerEvent) {
|
|
51406
|
+
eventBus.emit(`UI:${placeTowerEvent}`, { x, y });
|
|
51407
|
+
}
|
|
51408
|
+
}, [result, pathPositions, towerPositions, gold, towerCost, placeTowerEvent, eventBus]);
|
|
51409
|
+
const handleStartWave = React91.useCallback(() => {
|
|
51410
|
+
if (startWaveEvent) {
|
|
51411
|
+
eventBus.emit(`UI:${startWaveEvent}`, { wave });
|
|
51412
|
+
}
|
|
51413
|
+
}, [startWaveEvent, wave, eventBus]);
|
|
51414
|
+
const handlePlayAgain = React91.useCallback(() => {
|
|
51415
|
+
if (playAgainEvent) {
|
|
51416
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
51417
|
+
}
|
|
51418
|
+
}, [playAgainEvent, eventBus]);
|
|
51419
|
+
const canPlaceTower = gold >= towerCost && result === "none";
|
|
51420
|
+
const canStartWave = !waveActive && result === "none";
|
|
51421
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("tower-defense-board relative min-h-[600px] bg-background", className), gap: "none", children: [
|
|
51422
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
51423
|
+
HStack,
|
|
51424
|
+
{
|
|
51425
|
+
className: "px-4 py-2 border-b border-border bg-surface",
|
|
51426
|
+
gap: "lg",
|
|
51427
|
+
align: "center",
|
|
51428
|
+
children: [
|
|
51429
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
51430
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "muted", children: t("td.gold") ?? "Gold" }),
|
|
51431
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", weight: "bold", className: "text-warning", children: gold })
|
|
51432
|
+
] }),
|
|
51433
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
51434
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "muted", children: t("td.lives") ?? "Lives" }),
|
|
51435
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", weight: "bold", className: lives <= 5 ? "text-error" : "text-success", children: lives })
|
|
51436
|
+
] }),
|
|
51437
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
51438
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "muted", children: t("td.wave") ?? "Wave" }),
|
|
51439
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "body2", weight: "bold", children: [
|
|
51440
|
+
wave,
|
|
51441
|
+
" / ",
|
|
51442
|
+
maxWaves
|
|
51443
|
+
] })
|
|
51444
|
+
] }),
|
|
51445
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "ml-auto", children: [
|
|
51446
|
+
canStartWave && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", size: "sm", onClick: handleStartWave, children: t("td.startWave") ?? "Start Wave" }),
|
|
51447
|
+
waveActive && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "text-warning animate-pulse", children: t("td.waveInProgress") ?? "Wave in progress\u2026" })
|
|
51448
|
+
] })
|
|
51449
|
+
]
|
|
51450
|
+
}
|
|
51451
|
+
),
|
|
51452
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative flex-1", children: [
|
|
51453
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51454
|
+
IsometricCanvas_default,
|
|
51455
|
+
{
|
|
51456
|
+
tiles: isoTiles,
|
|
51457
|
+
units: isoUnits,
|
|
51458
|
+
features: pathFeatures,
|
|
51459
|
+
validMoves,
|
|
51460
|
+
hoveredTile,
|
|
51461
|
+
onTileClick: handleTileClick,
|
|
51462
|
+
onTileHover: (x, y) => setHoveredTile({ x, y }),
|
|
51463
|
+
onTileLeave: () => setHoveredTile(null),
|
|
51464
|
+
scale
|
|
51465
|
+
}
|
|
51466
|
+
),
|
|
51467
|
+
hoveredTile && canPlaceTower && validMoves.some((m) => m.x === hoveredTile.x && m.y === hoveredTile.y) && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "absolute bottom-4 left-1/2 -translate-x-1/2 z-10 bg-background/80 rounded px-3 py-1 backdrop-blur-sm pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", children: t("td.placeTower") ?? `Place tower (${towerCost} gold)` }) })
|
|
51468
|
+
] }),
|
|
51469
|
+
result !== "none" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
51470
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51471
|
+
Typography,
|
|
51472
|
+
{
|
|
51473
|
+
variant: "h2",
|
|
51474
|
+
className: cn(
|
|
51475
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
51476
|
+
result === "won" ? "text-warning" : "text-error"
|
|
51477
|
+
),
|
|
51478
|
+
children: result === "won" ? t("td.victory") ?? "Victory!" : t("td.defeat") ?? "Defeat!"
|
|
51479
|
+
}
|
|
51480
|
+
),
|
|
51481
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: result === "won" ? t("td.survivedAllWaves") ?? `Survived all ${maxWaves} waves!` : t("td.livesReachedZero") ?? "Your base was overrun." }),
|
|
51482
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51483
|
+
Button,
|
|
51484
|
+
{
|
|
51485
|
+
variant: "primary",
|
|
51486
|
+
className: "px-8 py-3 font-semibold",
|
|
51487
|
+
onClick: handlePlayAgain,
|
|
51488
|
+
children: t("td.playAgain") ?? "Play Again"
|
|
51489
|
+
}
|
|
51490
|
+
)
|
|
51491
|
+
] }) })
|
|
51492
|
+
] });
|
|
51493
|
+
}
|
|
51494
|
+
var CDN6, TERRAIN_SPRITES, TOWER_SPRITE, CREEP_SPRITE;
|
|
51495
|
+
var init_TowerDefenseBoard = __esm({
|
|
51496
|
+
"components/game/organisms/TowerDefenseBoard.tsx"() {
|
|
51497
|
+
"use client";
|
|
51498
|
+
init_cn();
|
|
51499
|
+
init_useEventBus();
|
|
51500
|
+
init_Box();
|
|
51501
|
+
init_Button();
|
|
51502
|
+
init_Typography();
|
|
51503
|
+
init_Stack();
|
|
51504
|
+
init_IsometricCanvas();
|
|
51505
|
+
init_boardEntity();
|
|
51506
|
+
CDN6 = "https://almadar-kflow-assets.web.app/shared/";
|
|
51507
|
+
TERRAIN_SPRITES = {
|
|
51508
|
+
ground: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_01.png`,
|
|
51509
|
+
path: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_09.png`,
|
|
51510
|
+
wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`
|
|
51511
|
+
};
|
|
51512
|
+
TOWER_SPRITE = `${CDN6}units/guardian.png`;
|
|
51513
|
+
CREEP_SPRITE = `${CDN6}units/scrapper.png`;
|
|
51514
|
+
TowerDefenseBoard.displayName = "TowerDefenseBoard";
|
|
51515
|
+
}
|
|
51516
|
+
});
|
|
51517
|
+
function TowerDefenseTemplate({
|
|
51518
|
+
entity,
|
|
51519
|
+
title = "Tower Defense",
|
|
51520
|
+
tiles,
|
|
51521
|
+
path,
|
|
51522
|
+
towers,
|
|
51523
|
+
gold,
|
|
51524
|
+
lives,
|
|
51525
|
+
maxWaves,
|
|
51526
|
+
towerCost,
|
|
51527
|
+
scale,
|
|
51528
|
+
placeTowerEvent,
|
|
51529
|
+
startWaveEvent,
|
|
51530
|
+
playAgainEvent,
|
|
51531
|
+
gameEndEvent,
|
|
51532
|
+
className
|
|
51533
|
+
}) {
|
|
51534
|
+
const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
|
|
51535
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
51536
|
+
Box,
|
|
51537
|
+
{
|
|
51538
|
+
display: "flex",
|
|
51539
|
+
fullHeight: true,
|
|
51540
|
+
className: cn("tower-defense-template flex-col", className),
|
|
51541
|
+
children: [
|
|
51542
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51543
|
+
HStack,
|
|
51544
|
+
{
|
|
51545
|
+
gap: "sm",
|
|
51546
|
+
align: "center",
|
|
51547
|
+
className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
|
|
51548
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title })
|
|
51549
|
+
}
|
|
51550
|
+
),
|
|
51551
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
51552
|
+
TowerDefenseBoard,
|
|
51553
|
+
{
|
|
51554
|
+
entity: resolved,
|
|
51555
|
+
tiles,
|
|
51556
|
+
path,
|
|
51557
|
+
towers,
|
|
51558
|
+
gold,
|
|
51559
|
+
lives,
|
|
51560
|
+
maxWaves,
|
|
51561
|
+
towerCost,
|
|
51562
|
+
scale,
|
|
51563
|
+
placeTowerEvent,
|
|
51564
|
+
startWaveEvent,
|
|
51565
|
+
playAgainEvent,
|
|
51566
|
+
gameEndEvent,
|
|
51567
|
+
className: "h-full"
|
|
51568
|
+
}
|
|
51569
|
+
) })
|
|
51570
|
+
]
|
|
51571
|
+
}
|
|
51572
|
+
);
|
|
51573
|
+
}
|
|
51574
|
+
var init_TowerDefenseTemplate = __esm({
|
|
51575
|
+
"components/game/templates/TowerDefenseTemplate.tsx"() {
|
|
51576
|
+
init_cn();
|
|
51577
|
+
init_Box();
|
|
51578
|
+
init_Stack();
|
|
51579
|
+
init_Typography();
|
|
51580
|
+
init_TowerDefenseBoard();
|
|
51581
|
+
TowerDefenseTemplate.displayName = "TowerDefenseTemplate";
|
|
51582
|
+
}
|
|
51583
|
+
});
|
|
49041
51584
|
function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
|
|
49042
51585
|
const eventBus = useEventBus();
|
|
49043
51586
|
const {
|
|
@@ -49236,17 +51779,12 @@ var init_UncontrolledBattleBoard = __esm({
|
|
|
49236
51779
|
}
|
|
49237
51780
|
});
|
|
49238
51781
|
function heroPosition(h) {
|
|
51782
|
+
if ("position" in h && h.position != null) return h.position;
|
|
49239
51783
|
return vec2(h.position);
|
|
49240
51784
|
}
|
|
49241
|
-
function heroOwner(h) {
|
|
49242
|
-
return str(h.owner);
|
|
49243
|
-
}
|
|
49244
51785
|
function heroMovement(h) {
|
|
49245
51786
|
return num(h.movement);
|
|
49246
51787
|
}
|
|
49247
|
-
function hexPassable(h) {
|
|
49248
|
-
return h.passable !== false;
|
|
49249
|
-
}
|
|
49250
51788
|
function defaultIsInRange(from, to, range) {
|
|
49251
51789
|
return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
|
|
49252
51790
|
}
|
|
@@ -49282,38 +51820,41 @@ function WorldMapBoard({
|
|
|
49282
51820
|
}) {
|
|
49283
51821
|
const eventBus = useEventBus();
|
|
49284
51822
|
const resolved = boardEntity(entity);
|
|
49285
|
-
const
|
|
49286
|
-
const
|
|
51823
|
+
const entityUnits = rows(resolved?.units);
|
|
51824
|
+
const entityTiles = rows(resolved?.tiles);
|
|
49287
51825
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
49288
51826
|
const selectedHeroId = resolved?.selectedHeroId ?? null;
|
|
49289
51827
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
49290
51828
|
const backgroundImage = resolved?.backgroundImage;
|
|
49291
51829
|
const [hoveredTile, setHoveredTile] = React91.useState(null);
|
|
49292
|
-
const selectedHero = React91.useMemo(
|
|
49293
|
-
() => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
|
|
49294
|
-
[heroes, selectedHeroId]
|
|
49295
|
-
);
|
|
49296
51830
|
const derivedTiles = React91.useMemo(
|
|
49297
|
-
() =>
|
|
49298
|
-
x: num(
|
|
49299
|
-
y: num(
|
|
49300
|
-
terrain: str(
|
|
49301
|
-
terrainSprite:
|
|
51831
|
+
() => entityTiles.map((t) => ({
|
|
51832
|
+
x: num(t.x),
|
|
51833
|
+
y: num(t.y),
|
|
51834
|
+
terrain: str(t.terrain),
|
|
51835
|
+
terrainSprite: t.terrainSprite == null ? void 0 : str(t.terrainSprite),
|
|
51836
|
+
passable: t.passable !== false
|
|
49302
51837
|
})),
|
|
49303
|
-
[
|
|
51838
|
+
[entityTiles]
|
|
49304
51839
|
);
|
|
49305
51840
|
const tiles = propTiles ?? derivedTiles;
|
|
49306
51841
|
const baseUnits = React91.useMemo(
|
|
49307
|
-
() => propUnits ??
|
|
49308
|
-
id: str(
|
|
49309
|
-
position: heroPosition(
|
|
49310
|
-
name: str(
|
|
49311
|
-
|
|
49312
|
-
|
|
49313
|
-
|
|
49314
|
-
|
|
51842
|
+
() => propUnits ?? entityUnits.map((u) => ({
|
|
51843
|
+
id: str(u.id),
|
|
51844
|
+
position: heroPosition(u),
|
|
51845
|
+
name: str(u.name),
|
|
51846
|
+
// lolo uses `team` field (not `owner`)
|
|
51847
|
+
team: str(u.team) === "enemy" ? "enemy" : "player",
|
|
51848
|
+
health: num(u.health) || 100,
|
|
51849
|
+
maxHealth: num(u.maxHealth) || 100,
|
|
51850
|
+
sprite: u.sprite == null ? void 0 : str(u.sprite)
|
|
49315
51851
|
})),
|
|
49316
|
-
[
|
|
51852
|
+
[entityUnits, propUnits]
|
|
51853
|
+
);
|
|
51854
|
+
const gameUnits = baseUnits;
|
|
51855
|
+
const selectedHero = React91.useMemo(
|
|
51856
|
+
() => gameUnits.find((u) => u.id === selectedHeroId) ?? null,
|
|
51857
|
+
[gameUnits, selectedHeroId]
|
|
49317
51858
|
);
|
|
49318
51859
|
const MOVE_SPEED_MS_PER_TILE = 300;
|
|
49319
51860
|
const movementAnimRef = React91.useRef(null);
|
|
@@ -49359,51 +51900,50 @@ function WorldMapBoard({
|
|
|
49359
51900
|
const validMoves = React91.useMemo(() => {
|
|
49360
51901
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
49361
51902
|
const sp = heroPosition(selectedHero);
|
|
49362
|
-
const
|
|
51903
|
+
const sTeam = str(selectedHero.team);
|
|
49363
51904
|
const range = heroMovement(selectedHero);
|
|
49364
51905
|
const moves = [];
|
|
49365
|
-
|
|
49366
|
-
const
|
|
49367
|
-
const
|
|
49368
|
-
if (
|
|
49369
|
-
if (
|
|
49370
|
-
if (!isInRange(sp, { x:
|
|
49371
|
-
if (
|
|
49372
|
-
const
|
|
49373
|
-
return
|
|
51906
|
+
tiles.forEach((t) => {
|
|
51907
|
+
const tx = t.x;
|
|
51908
|
+
const ty = t.y;
|
|
51909
|
+
if (t.passable === false) return;
|
|
51910
|
+
if (tx === sp.x && ty === sp.y) return;
|
|
51911
|
+
if (!isInRange(sp, { x: tx, y: ty }, range)) return;
|
|
51912
|
+
if (gameUnits.some((u) => {
|
|
51913
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
51914
|
+
return up.x === tx && up.y === ty && str(u.team) === sTeam;
|
|
49374
51915
|
})) return;
|
|
49375
|
-
moves.push({ x:
|
|
51916
|
+
moves.push({ x: tx, y: ty });
|
|
49376
51917
|
});
|
|
49377
51918
|
return moves;
|
|
49378
|
-
}, [selectedHero,
|
|
51919
|
+
}, [selectedHero, tiles, gameUnits, isInRange]);
|
|
49379
51920
|
const attackTargets = React91.useMemo(() => {
|
|
49380
51921
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
49381
51922
|
const sp = heroPosition(selectedHero);
|
|
49382
|
-
const
|
|
51923
|
+
const sTeam = str(selectedHero.team);
|
|
49383
51924
|
const range = heroMovement(selectedHero);
|
|
49384
|
-
return
|
|
49385
|
-
}, [selectedHero,
|
|
49386
|
-
const maxY = Math.max(...
|
|
51925
|
+
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 });
|
|
51926
|
+
}, [selectedHero, gameUnits, isInRange]);
|
|
51927
|
+
const maxY = Math.max(...tiles.map((t) => t.y), 0);
|
|
49387
51928
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
49388
51929
|
const tileToScreen = React91.useCallback(
|
|
49389
51930
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
49390
51931
|
[scale, baseOffsetX]
|
|
49391
51932
|
);
|
|
49392
51933
|
const hoveredHex = React91.useMemo(
|
|
49393
|
-
() => hoveredTile ?
|
|
49394
|
-
[hoveredTile,
|
|
51934
|
+
() => hoveredTile ? tiles.find((t) => t.x === hoveredTile.x && t.y === hoveredTile.y) ?? null : null,
|
|
51935
|
+
[hoveredTile, tiles]
|
|
49395
51936
|
);
|
|
49396
51937
|
const hoveredHero = React91.useMemo(
|
|
49397
|
-
() => hoveredTile ?
|
|
49398
|
-
const
|
|
49399
|
-
return
|
|
51938
|
+
() => hoveredTile ? gameUnits.find((u) => {
|
|
51939
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
51940
|
+
return up.x === hoveredTile.x && up.y === hoveredTile.y;
|
|
49400
51941
|
}) ?? null : null,
|
|
49401
|
-
[hoveredTile,
|
|
51942
|
+
[hoveredTile, gameUnits]
|
|
49402
51943
|
);
|
|
49403
51944
|
const handleTileClick = React91.useCallback((x, y) => {
|
|
49404
51945
|
if (movementAnimRef.current) return;
|
|
49405
|
-
const
|
|
49406
|
-
if (!hex) return;
|
|
51946
|
+
const tile = tiles.find((t) => t.x === x && t.y === y);
|
|
49407
51947
|
if (tileClickEvent) {
|
|
49408
51948
|
eventBus.emit(`UI:${tileClickEvent}`, { x, y });
|
|
49409
51949
|
}
|
|
@@ -49414,38 +51954,39 @@ function WorldMapBoard({
|
|
|
49414
51954
|
if (heroMoveEvent) {
|
|
49415
51955
|
eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
|
|
49416
51956
|
}
|
|
49417
|
-
const feature = str(
|
|
51957
|
+
const feature = tile ? str(tile.feature) : "";
|
|
49418
51958
|
if (feature && feature !== "none") {
|
|
49419
|
-
|
|
51959
|
+
const tileRow = tile;
|
|
51960
|
+
onFeatureEnter?.(heroId, tileRow);
|
|
49420
51961
|
if (featureEnterEvent) {
|
|
49421
|
-
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
|
|
51962
|
+
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex: tileRow });
|
|
49422
51963
|
}
|
|
49423
51964
|
}
|
|
49424
51965
|
});
|
|
49425
51966
|
return;
|
|
49426
51967
|
}
|
|
49427
|
-
const enemy =
|
|
49428
|
-
const
|
|
49429
|
-
return
|
|
51968
|
+
const enemy = gameUnits.find((u) => {
|
|
51969
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
51970
|
+
return up.x === x && up.y === y && str(u.team) === "enemy";
|
|
49430
51971
|
});
|
|
49431
51972
|
if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
|
|
49432
51973
|
const attackerId = str(selectedHero.id);
|
|
49433
|
-
const defenderId =
|
|
51974
|
+
const defenderId = enemy.id;
|
|
49434
51975
|
onBattleEncounter?.(attackerId, defenderId);
|
|
49435
51976
|
if (battleEncounterEvent) {
|
|
49436
51977
|
eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
|
|
49437
51978
|
}
|
|
49438
51979
|
}
|
|
49439
|
-
}, [
|
|
51980
|
+
}, [tiles, gameUnits, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
|
|
49440
51981
|
const handleUnitClick = React91.useCallback((unitId) => {
|
|
49441
|
-
const
|
|
49442
|
-
if (
|
|
51982
|
+
const unit = gameUnits.find((u) => u.id === unitId);
|
|
51983
|
+
if (unit && (str(unit.team) === "player" || allowMoveAllHeroes)) {
|
|
49443
51984
|
onHeroSelect?.(unitId);
|
|
49444
51985
|
if (heroSelectEvent) {
|
|
49445
51986
|
eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
|
|
49446
51987
|
}
|
|
49447
51988
|
}
|
|
49448
|
-
}, [
|
|
51989
|
+
}, [gameUnits, onHeroSelect, allowMoveAllHeroes, eventBus, heroSelectEvent]);
|
|
49449
51990
|
const selectHero = React91.useCallback((id) => {
|
|
49450
51991
|
onHeroSelect?.(id);
|
|
49451
51992
|
if (heroSelectEvent) {
|
|
@@ -49549,48 +52090,48 @@ function WorldMapTemplate({
|
|
|
49549
52090
|
}
|
|
49550
52091
|
);
|
|
49551
52092
|
}
|
|
49552
|
-
var
|
|
52093
|
+
var CDN7, DEFAULT_WORLDMAP_TILES, DEFAULT_WORLDMAP_UNITS, DEFAULT_WORLDMAP_FEATURES, DEFAULT_WORLDMAP_MANIFEST;
|
|
49553
52094
|
var init_WorldMapTemplate = __esm({
|
|
49554
52095
|
"components/game/templates/WorldMapTemplate.tsx"() {
|
|
49555
52096
|
init_WorldMapBoard();
|
|
49556
|
-
|
|
52097
|
+
CDN7 = "https://almadar-kflow-assets.web.app/shared";
|
|
49557
52098
|
DEFAULT_WORLDMAP_TILES = [
|
|
49558
|
-
{ x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49559
|
-
{ x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49560
|
-
{ x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${
|
|
49561
|
-
{ x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49562
|
-
{ x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49563
|
-
{ x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49564
|
-
{ x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
49565
|
-
{ x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
49566
|
-
{ x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
49567
|
-
{ x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${
|
|
49568
|
-
{ x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${
|
|
49569
|
-
{ x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
49570
|
-
{ x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
49571
|
-
{ x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
49572
|
-
{ x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49573
|
-
{ x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49574
|
-
{ x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
49575
|
-
{ x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
49576
|
-
{ x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
49577
|
-
{ x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49578
|
-
{ x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49579
|
-
{ x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${
|
|
49580
|
-
{ x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${
|
|
49581
|
-
{ x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49582
|
-
{ x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
52099
|
+
{ x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52100
|
+
{ x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52101
|
+
{ x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
52102
|
+
{ x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52103
|
+
{ x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52104
|
+
{ x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52105
|
+
{ x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52106
|
+
{ x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52107
|
+
{ x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52108
|
+
{ x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
52109
|
+
{ x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
52110
|
+
{ x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52111
|
+
{ x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
|
|
52112
|
+
{ x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52113
|
+
{ x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52114
|
+
{ x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52115
|
+
{ x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52116
|
+
{ x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52117
|
+
{ x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
|
|
52118
|
+
{ x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52119
|
+
{ x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52120
|
+
{ x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
52121
|
+
{ x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52122
|
+
{ x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52123
|
+
{ x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` }
|
|
49583
52124
|
];
|
|
49584
52125
|
DEFAULT_WORLDMAP_UNITS = [
|
|
49585
|
-
{ id: "h1", position: { x: 1, y: 1 }, unitType: "hero", name: "Amir", team: "player", health: 10, maxHealth: 10, sprite: `${
|
|
49586
|
-
{ id: "h2", position: { x: 3, y: 3 }, unitType: "scout", name: "Archivist", team: "player", health: 10, maxHealth: 10, sprite: `${
|
|
52126
|
+
{ id: "h1", position: { x: 1, y: 1 }, unitType: "hero", name: "Amir", team: "player", health: 10, maxHealth: 10, sprite: `${CDN7}/sprite-sheets/amir-sprite-sheet-se.png` },
|
|
52127
|
+
{ id: "h2", position: { x: 3, y: 3 }, unitType: "scout", name: "Archivist", team: "player", health: 10, maxHealth: 10, sprite: `${CDN7}/sprite-sheets/archivist-sprite-sheet-se.png` }
|
|
49587
52128
|
];
|
|
49588
52129
|
DEFAULT_WORLDMAP_FEATURES = [
|
|
49589
|
-
{ id: "f1", x: 2, y: 2, type: "capital", sprite: `${
|
|
49590
|
-
{ id: "f2", x: 4, y: 2, type: "power_node", sprite: `${
|
|
52130
|
+
{ id: "f1", x: 2, y: 2, type: "capital", sprite: `${CDN7}/scenes/world/capital.png` },
|
|
52131
|
+
{ id: "f2", x: 4, y: 2, type: "power_node", sprite: `${CDN7}/world-map/power_node.png` }
|
|
49591
52132
|
];
|
|
49592
52133
|
DEFAULT_WORLDMAP_MANIFEST = {
|
|
49593
|
-
baseUrl:
|
|
52134
|
+
baseUrl: CDN7,
|
|
49594
52135
|
terrains: {
|
|
49595
52136
|
grass: "/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png",
|
|
49596
52137
|
water: "/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png",
|
|
@@ -49635,7 +52176,7 @@ function lazyThree(name, loader) {
|
|
|
49635
52176
|
ThreeWrapper.displayName = `Lazy(${name})`;
|
|
49636
52177
|
return ThreeWrapper;
|
|
49637
52178
|
}
|
|
49638
|
-
var ThreeBoundary, FeatureRenderer,
|
|
52179
|
+
var ThreeBoundary, FeatureRenderer, GameCanvas3D2, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
|
|
49639
52180
|
var init_component_registry_generated = __esm({
|
|
49640
52181
|
"components/core/organisms/component-registry.generated.ts"() {
|
|
49641
52182
|
init_AboutPageTemplate();
|
|
@@ -49745,6 +52286,7 @@ var init_component_registry_generated = __esm({
|
|
|
49745
52286
|
init_FormSectionHeader();
|
|
49746
52287
|
init_GameAudioProvider();
|
|
49747
52288
|
init_GameAudioToggle();
|
|
52289
|
+
init_GameBoard3D();
|
|
49748
52290
|
init_GameCanvas2D();
|
|
49749
52291
|
init_GameHud();
|
|
49750
52292
|
init_GameMenu();
|
|
@@ -49802,7 +52344,9 @@ var init_component_registry_generated = __esm({
|
|
|
49802
52344
|
init_PageHeader();
|
|
49803
52345
|
init_Pagination();
|
|
49804
52346
|
init_PatternTile();
|
|
52347
|
+
init_PlatformerBoard();
|
|
49805
52348
|
init_PlatformerCanvas();
|
|
52349
|
+
init_PlatformerTemplate();
|
|
49806
52350
|
init_Popover();
|
|
49807
52351
|
init_PositionedCanvas();
|
|
49808
52352
|
init_PowerupSlots();
|
|
@@ -49826,6 +52370,8 @@ var init_component_registry_generated = __esm({
|
|
|
49826
52370
|
init_ResourceBar();
|
|
49827
52371
|
init_ResourceCounter();
|
|
49828
52372
|
init_RichBlockEditor();
|
|
52373
|
+
init_RoguelikeBoard();
|
|
52374
|
+
init_RoguelikeTemplate();
|
|
49829
52375
|
init_RuntimeDebugger2();
|
|
49830
52376
|
init_ScaledDiagram();
|
|
49831
52377
|
init_ScoreBoard();
|
|
@@ -49898,6 +52444,8 @@ var init_component_registry_generated = __esm({
|
|
|
49898
52444
|
init_Toast();
|
|
49899
52445
|
init_ToastSlot();
|
|
49900
52446
|
init_Tooltip();
|
|
52447
|
+
init_TowerDefenseBoard();
|
|
52448
|
+
init_TowerDefenseTemplate();
|
|
49901
52449
|
init_TraitFrame();
|
|
49902
52450
|
init_TraitSlot();
|
|
49903
52451
|
init_TrendIndicator();
|
|
@@ -49942,7 +52490,7 @@ var init_component_registry_generated = __esm({
|
|
|
49942
52490
|
}
|
|
49943
52491
|
};
|
|
49944
52492
|
FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
|
|
49945
|
-
|
|
52493
|
+
GameCanvas3D2 = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
|
|
49946
52494
|
GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
49947
52495
|
GameCanvas3DCastleTemplate = lazyThree("GameCanvas3DCastleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
49948
52496
|
GameCanvas3DWorldMapTemplate = lazyThree("GameCanvas3DWorldMapTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
@@ -50061,8 +52609,9 @@ var init_component_registry_generated = __esm({
|
|
|
50061
52609
|
"FormSectionHeader": FormSectionHeader,
|
|
50062
52610
|
"GameAudioProvider": GameAudioProvider,
|
|
50063
52611
|
"GameAudioToggle": GameAudioToggle,
|
|
52612
|
+
"GameBoard3D": GameBoard3D,
|
|
50064
52613
|
"GameCanvas2D": GameCanvas2D,
|
|
50065
|
-
"GameCanvas3D":
|
|
52614
|
+
"GameCanvas3D": GameCanvas3D2,
|
|
50066
52615
|
"GameCanvas3DBattleTemplate": GameCanvas3DBattleTemplate,
|
|
50067
52616
|
"GameCanvas3DCastleTemplate": GameCanvas3DCastleTemplate,
|
|
50068
52617
|
"GameCanvas3DWorldMapTemplate": GameCanvas3DWorldMapTemplate,
|
|
@@ -50124,7 +52673,9 @@ var init_component_registry_generated = __esm({
|
|
|
50124
52673
|
"PageHeader": PageHeader,
|
|
50125
52674
|
"Pagination": Pagination,
|
|
50126
52675
|
"PatternTile": PatternTile,
|
|
52676
|
+
"PlatformerBoard": PlatformerBoard,
|
|
50127
52677
|
"PlatformerCanvas": PlatformerCanvas,
|
|
52678
|
+
"PlatformerTemplate": PlatformerTemplate,
|
|
50128
52679
|
"Popover": Popover,
|
|
50129
52680
|
"PositionedCanvas": PositionedCanvas,
|
|
50130
52681
|
"PowerupSlots": PowerupSlots,
|
|
@@ -50148,6 +52699,8 @@ var init_component_registry_generated = __esm({
|
|
|
50148
52699
|
"ResourceBar": ResourceBar,
|
|
50149
52700
|
"ResourceCounter": ResourceCounter,
|
|
50150
52701
|
"RichBlockEditor": RichBlockEditor,
|
|
52702
|
+
"RoguelikeBoard": RoguelikeBoard,
|
|
52703
|
+
"RoguelikeTemplate": RoguelikeTemplate,
|
|
50151
52704
|
"RuntimeDebugger": RuntimeDebugger,
|
|
50152
52705
|
"ScaledDiagram": ScaledDiagram,
|
|
50153
52706
|
"ScoreBoard": ScoreBoard,
|
|
@@ -50223,6 +52776,8 @@ var init_component_registry_generated = __esm({
|
|
|
50223
52776
|
"Toast": Toast,
|
|
50224
52777
|
"ToastSlot": ToastSlot,
|
|
50225
52778
|
"Tooltip": Tooltip,
|
|
52779
|
+
"TowerDefenseBoard": TowerDefenseBoard,
|
|
52780
|
+
"TowerDefenseTemplate": TowerDefenseTemplate,
|
|
50226
52781
|
"TraitFrame": TraitFrame,
|
|
50227
52782
|
"TraitSlot": TraitSlot,
|
|
50228
52783
|
"TrendIndicator": TrendIndicator,
|
|
@@ -54709,7 +57264,7 @@ function convertObjectProps(props) {
|
|
|
54709
57264
|
function convertFnFormLambdasInProps(props) {
|
|
54710
57265
|
return convertObjectProps(props);
|
|
54711
57266
|
}
|
|
54712
|
-
var
|
|
57267
|
+
var log11 = logger.createLogger("almadar:ui:effects:client-handlers");
|
|
54713
57268
|
function createClientEffectHandlers(options) {
|
|
54714
57269
|
const { eventBus, slotSetter, navigate, notify, callService } = options;
|
|
54715
57270
|
return {
|
|
@@ -54718,10 +57273,10 @@ function createClientEffectHandlers(options) {
|
|
|
54718
57273
|
eventBus.emit(prefixedEvent, payload);
|
|
54719
57274
|
},
|
|
54720
57275
|
persist: async () => {
|
|
54721
|
-
|
|
57276
|
+
log11.warn("persist is server-side only, ignored on client");
|
|
54722
57277
|
},
|
|
54723
57278
|
set: () => {
|
|
54724
|
-
|
|
57279
|
+
log11.warn("set is server-side only, ignored on client");
|
|
54725
57280
|
},
|
|
54726
57281
|
callService: async (service, action, params) => {
|
|
54727
57282
|
if (callService) return callService(service, action, params);
|
|
@@ -54750,10 +57305,10 @@ function createClientEffectHandlers(options) {
|
|
|
54750
57305
|
slotSetter.addPattern(slot, pattern, props);
|
|
54751
57306
|
},
|
|
54752
57307
|
navigate: navigate ?? ((path) => {
|
|
54753
|
-
|
|
57308
|
+
log11.warn("No navigate handler, ignoring", { path });
|
|
54754
57309
|
}),
|
|
54755
57310
|
notify: notify ?? ((msg, type) => {
|
|
54756
|
-
|
|
57311
|
+
log11.debug("notify", { type, message: msg });
|
|
54757
57312
|
})
|
|
54758
57313
|
};
|
|
54759
57314
|
}
|
|
@@ -55918,7 +58473,7 @@ OrbitalThemeProvider.displayName = "OrbitalThemeProvider";
|
|
|
55918
58473
|
init_navigation();
|
|
55919
58474
|
init_verificationRegistry();
|
|
55920
58475
|
var PERF_NAMESPACE = "almadar:perf:canvas";
|
|
55921
|
-
var
|
|
58476
|
+
var log12 = logger.createLogger(PERF_NAMESPACE);
|
|
55922
58477
|
var RING_SIZE = 50;
|
|
55923
58478
|
var ring = [];
|
|
55924
58479
|
var writeIdx = 0;
|
|
@@ -55969,7 +58524,7 @@ function perfEnd(name, startToken, detail) {
|
|
|
55969
58524
|
}
|
|
55970
58525
|
}
|
|
55971
58526
|
push({ name, durationMs, ts: endTs, detail });
|
|
55972
|
-
|
|
58527
|
+
log12.debug(name, () => ({ durationMs, ...detail ?? {} }));
|
|
55973
58528
|
}
|
|
55974
58529
|
var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, commitTime) => {
|
|
55975
58530
|
if (!isEnabled()) return;
|
|
@@ -55979,7 +58534,7 @@ var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, com
|
|
|
55979
58534
|
ts: commitTime,
|
|
55980
58535
|
detail: { baseDuration }
|
|
55981
58536
|
});
|
|
55982
|
-
|
|
58537
|
+
log12.debug(`profiler:${id}:${phase}`, () => ({ actualDuration, baseDuration }));
|
|
55983
58538
|
};
|
|
55984
58539
|
|
|
55985
58540
|
// runtime/prepareSchemaForPreview.ts
|
|
@@ -56622,7 +59177,7 @@ init_useEventBus();
|
|
|
56622
59177
|
// components/avl/molecules/useCanvasDnd.tsx
|
|
56623
59178
|
init_useEventBus();
|
|
56624
59179
|
init_useAlmadarDndCollision();
|
|
56625
|
-
var
|
|
59180
|
+
var log13 = logger.createLogger("almadar:ui:canvas-dnd");
|
|
56626
59181
|
function useCanvasDraggable({
|
|
56627
59182
|
id,
|
|
56628
59183
|
payload,
|
|
@@ -56661,7 +59216,7 @@ function defaultEmit(eventBus, drop) {
|
|
|
56661
59216
|
if (payload.kind === "pattern") {
|
|
56662
59217
|
const patternType = payload.data.type;
|
|
56663
59218
|
if (typeof patternType !== "string") {
|
|
56664
|
-
|
|
59219
|
+
log13.warn("default-emit:pattern:missing-type");
|
|
56665
59220
|
return;
|
|
56666
59221
|
}
|
|
56667
59222
|
const out = { patternType, containerNode: target.containerNode };
|
|
@@ -56670,23 +59225,23 @@ function defaultEmit(eventBus, drop) {
|
|
|
56670
59225
|
out.index = resolved.index;
|
|
56671
59226
|
}
|
|
56672
59227
|
eventBus.emit("UI:PATTERN_DROP", out);
|
|
56673
|
-
|
|
59228
|
+
log13.info("default-emit:pattern", { patternType, level: target.level });
|
|
56674
59229
|
return;
|
|
56675
59230
|
}
|
|
56676
59231
|
if (payload.kind === "behavior") {
|
|
56677
59232
|
const behaviorName = payload.data.name;
|
|
56678
59233
|
if (typeof behaviorName !== "string") {
|
|
56679
|
-
|
|
59234
|
+
log13.warn("default-emit:behavior:missing-name");
|
|
56680
59235
|
return;
|
|
56681
59236
|
}
|
|
56682
59237
|
eventBus.emit("UI:BEHAVIOR_DROP", {
|
|
56683
59238
|
behaviorName,
|
|
56684
59239
|
containerNode: target.containerNode
|
|
56685
59240
|
});
|
|
56686
|
-
|
|
59241
|
+
log13.info("default-emit:behavior", { behaviorName, level: target.level });
|
|
56687
59242
|
return;
|
|
56688
59243
|
}
|
|
56689
|
-
|
|
59244
|
+
log13.debug("default-emit:unhandled-kind", { kind: payload.kind });
|
|
56690
59245
|
}
|
|
56691
59246
|
function CanvasDndProvider({
|
|
56692
59247
|
children,
|
|
@@ -56702,9 +59257,9 @@ function CanvasDndProvider({
|
|
|
56702
59257
|
if (payload) {
|
|
56703
59258
|
setActivePayload(payload);
|
|
56704
59259
|
eventBus.emit("UI:DRAG_START", { kind: payload.kind, data: payload.data });
|
|
56705
|
-
|
|
59260
|
+
log13.info("dragStart", { id: e.active.id, kind: payload.kind });
|
|
56706
59261
|
} else {
|
|
56707
|
-
|
|
59262
|
+
log13.warn("dragStart:missing-payload", { id: e.active.id });
|
|
56708
59263
|
}
|
|
56709
59264
|
}, [eventBus]);
|
|
56710
59265
|
const handleDragEnd = React91__namespace.default.useCallback((e) => {
|
|
@@ -56714,7 +59269,7 @@ function CanvasDndProvider({
|
|
|
56714
59269
|
const overData = e.over?.data.current;
|
|
56715
59270
|
const target = overData?.target;
|
|
56716
59271
|
const accepts = overData?.accepts;
|
|
56717
|
-
|
|
59272
|
+
log13.info("dragEnd", {
|
|
56718
59273
|
activeId: e.active.id,
|
|
56719
59274
|
overId: e.over?.id,
|
|
56720
59275
|
hasPayload: !!payload,
|
|
@@ -56726,7 +59281,7 @@ function CanvasDndProvider({
|
|
|
56726
59281
|
}
|
|
56727
59282
|
if (!payload || !target) return;
|
|
56728
59283
|
if (accepts && !accepts.includes(payload.kind)) {
|
|
56729
|
-
|
|
59284
|
+
log13.debug("dragEnd:rejected:kind", { kind: payload.kind, accepts: [...accepts] });
|
|
56730
59285
|
return;
|
|
56731
59286
|
}
|
|
56732
59287
|
const activator = e.activatorEvent;
|
|
@@ -56738,7 +59293,7 @@ function CanvasDndProvider({
|
|
|
56738
59293
|
}, [eventBus, onDrop]);
|
|
56739
59294
|
const handleDragCancel = React91__namespace.default.useCallback(() => {
|
|
56740
59295
|
setActivePayload(null);
|
|
56741
|
-
|
|
59296
|
+
log13.info("dragCancel");
|
|
56742
59297
|
}, []);
|
|
56743
59298
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
56744
59299
|
core$1.DndContext,
|
|
@@ -58553,7 +61108,7 @@ init_AvlTransitionLane();
|
|
|
58553
61108
|
init_AvlSwimLane();
|
|
58554
61109
|
init_types();
|
|
58555
61110
|
init_avl_elk_layout();
|
|
58556
|
-
var
|
|
61111
|
+
var log14 = logger.createLogger("almadar:ui:avl:trait-scene");
|
|
58557
61112
|
var SWIM_GUTTER2 = 120;
|
|
58558
61113
|
var CENTER_W2 = 360;
|
|
58559
61114
|
var AvlTraitScene = ({
|
|
@@ -58566,7 +61121,7 @@ var AvlTraitScene = ({
|
|
|
58566
61121
|
const dataKey = React91.useMemo(() => JSON.stringify(data), [data]);
|
|
58567
61122
|
React91.useEffect(() => {
|
|
58568
61123
|
computeTraitLayout(data).then(setLayout).catch((error) => {
|
|
58569
|
-
|
|
61124
|
+
log14.error("computeTraitLayout failed", { error: error instanceof Error ? error : String(error) });
|
|
58570
61125
|
});
|
|
58571
61126
|
}, [dataKey]);
|
|
58572
61127
|
if (!layout) {
|