@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/providers/index.cjs
CHANGED
|
@@ -41,6 +41,12 @@ var core$1 = require('@dnd-kit/core');
|
|
|
41
41
|
var sortable = require('@dnd-kit/sortable');
|
|
42
42
|
var utilities = require('@dnd-kit/utilities');
|
|
43
43
|
var react = require('@xyflow/react');
|
|
44
|
+
var THREE = require('three');
|
|
45
|
+
var GLTFLoader_js = require('three/examples/jsm/loaders/GLTFLoader.js');
|
|
46
|
+
var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
|
|
47
|
+
var GLTFLoader = require('three/examples/jsm/loaders/GLTFLoader');
|
|
48
|
+
var fiber = require('@react-three/fiber');
|
|
49
|
+
var drei = require('@react-three/drei');
|
|
44
50
|
var patterns = require('@almadar/patterns');
|
|
45
51
|
|
|
46
52
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -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;
|
|
@@ -719,7 +726,7 @@ var init_Box = __esm({
|
|
|
719
726
|
position,
|
|
720
727
|
className,
|
|
721
728
|
children,
|
|
722
|
-
as:
|
|
729
|
+
as: Component2 = "div",
|
|
723
730
|
action,
|
|
724
731
|
actionPayload,
|
|
725
732
|
hoverEvent,
|
|
@@ -751,7 +758,7 @@ var init_Box = __esm({
|
|
|
751
758
|
}, [hoverEvent, eventBus, onMouseLeave]);
|
|
752
759
|
const isClickable = action || onClick;
|
|
753
760
|
return React83__namespace.default.createElement(
|
|
754
|
-
|
|
761
|
+
Component2,
|
|
755
762
|
{
|
|
756
763
|
ref,
|
|
757
764
|
className: cn(
|
|
@@ -889,9 +896,9 @@ function resolvePhosphor(name, weight, family) {
|
|
|
889
896
|
(lib) => {
|
|
890
897
|
const PhosphorComp = lib[target];
|
|
891
898
|
if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
|
|
892
|
-
const
|
|
899
|
+
const Component2 = PhosphorComp;
|
|
893
900
|
const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
894
|
-
|
|
901
|
+
Component2,
|
|
895
902
|
{
|
|
896
903
|
weight,
|
|
897
904
|
className: props.className,
|
|
@@ -915,9 +922,9 @@ function resolveTabler(name, family) {
|
|
|
915
922
|
(lib) => {
|
|
916
923
|
const TablerComp = lib[target];
|
|
917
924
|
if (!TablerComp || typeof TablerComp !== "object") return null;
|
|
918
|
-
const
|
|
925
|
+
const Component2 = TablerComp;
|
|
919
926
|
const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
920
|
-
|
|
927
|
+
Component2,
|
|
921
928
|
{
|
|
922
929
|
stroke: props.strokeWidth ?? 1.5,
|
|
923
930
|
className: props.className,
|
|
@@ -941,9 +948,9 @@ function resolveFa(name, family) {
|
|
|
941
948
|
(lib) => {
|
|
942
949
|
const FaComp = lib[target];
|
|
943
950
|
if (!FaComp || typeof FaComp !== "function") return null;
|
|
944
|
-
const
|
|
951
|
+
const Component2 = FaComp;
|
|
945
952
|
const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
946
|
-
|
|
953
|
+
Component2,
|
|
947
954
|
{
|
|
948
955
|
className: props.className,
|
|
949
956
|
style: props.style,
|
|
@@ -1898,8 +1905,8 @@ var init_Typography = __esm({
|
|
|
1898
1905
|
children
|
|
1899
1906
|
}) => {
|
|
1900
1907
|
const variant = variantProp ?? (level ? `h${level}` : "body1");
|
|
1901
|
-
const
|
|
1902
|
-
const Comp =
|
|
1908
|
+
const Component2 = as || defaultElements[variant];
|
|
1909
|
+
const Comp = Component2;
|
|
1903
1910
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1904
1911
|
Comp,
|
|
1905
1912
|
{
|
|
@@ -4454,10 +4461,10 @@ var init_Center = __esm({
|
|
|
4454
4461
|
className,
|
|
4455
4462
|
style,
|
|
4456
4463
|
children,
|
|
4457
|
-
as:
|
|
4464
|
+
as: Component2 = "div"
|
|
4458
4465
|
}) => {
|
|
4459
4466
|
const mergedStyle = minHeight ? { minHeight, ...style } : style;
|
|
4460
|
-
const Comp =
|
|
4467
|
+
const Comp = Component2;
|
|
4461
4468
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4462
4469
|
Comp,
|
|
4463
4470
|
{
|
|
@@ -5088,7 +5095,7 @@ var init_Stack = __esm({
|
|
|
5088
5095
|
className,
|
|
5089
5096
|
style,
|
|
5090
5097
|
children,
|
|
5091
|
-
as:
|
|
5098
|
+
as: Component2 = "div",
|
|
5092
5099
|
onClick,
|
|
5093
5100
|
onKeyDown,
|
|
5094
5101
|
role,
|
|
@@ -5106,7 +5113,7 @@ var init_Stack = __esm({
|
|
|
5106
5113
|
};
|
|
5107
5114
|
const isHorizontal = direction === "horizontal";
|
|
5108
5115
|
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";
|
|
5109
|
-
const Comp =
|
|
5116
|
+
const Comp = Component2;
|
|
5110
5117
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5111
5118
|
Comp,
|
|
5112
5119
|
{
|
|
@@ -7162,6 +7169,7 @@ var init_HealthBar = __esm({
|
|
|
7162
7169
|
}
|
|
7163
7170
|
});
|
|
7164
7171
|
function ScoreDisplay({
|
|
7172
|
+
assetUrl = DEFAULT_ASSET_URL,
|
|
7165
7173
|
value,
|
|
7166
7174
|
label,
|
|
7167
7175
|
icon,
|
|
@@ -7208,14 +7216,24 @@ function ScoreDisplay({
|
|
|
7208
7216
|
className
|
|
7209
7217
|
),
|
|
7210
7218
|
children: [
|
|
7211
|
-
|
|
7219
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7220
|
+
"img",
|
|
7221
|
+
{
|
|
7222
|
+
src: assetUrl,
|
|
7223
|
+
alt: "",
|
|
7224
|
+
width: 20,
|
|
7225
|
+
height: 20,
|
|
7226
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7227
|
+
className: "flex-shrink-0"
|
|
7228
|
+
}
|
|
7229
|
+
) : 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,
|
|
7212
7230
|
label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: label }),
|
|
7213
7231
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: formattedValue })
|
|
7214
7232
|
]
|
|
7215
7233
|
}
|
|
7216
7234
|
);
|
|
7217
7235
|
}
|
|
7218
|
-
var sizeMap2;
|
|
7236
|
+
var sizeMap2, DEFAULT_ASSET_URL;
|
|
7219
7237
|
var init_ScoreDisplay = __esm({
|
|
7220
7238
|
"components/game/atoms/ScoreDisplay.tsx"() {
|
|
7221
7239
|
"use client";
|
|
@@ -7227,10 +7245,12 @@ var init_ScoreDisplay = __esm({
|
|
|
7227
7245
|
lg: "text-2xl",
|
|
7228
7246
|
xl: "text-4xl"
|
|
7229
7247
|
};
|
|
7248
|
+
DEFAULT_ASSET_URL = "https://almadar-kflow-assets.web.app/shared/effects/particles/star_01.png";
|
|
7230
7249
|
ScoreDisplay.displayName = "ScoreDisplay";
|
|
7231
7250
|
}
|
|
7232
7251
|
});
|
|
7233
7252
|
function ControlButton({
|
|
7253
|
+
assetUrl = DEFAULT_ASSET_URL2,
|
|
7234
7254
|
label,
|
|
7235
7255
|
icon,
|
|
7236
7256
|
size = "md",
|
|
@@ -7277,7 +7297,7 @@ function ControlButton({
|
|
|
7277
7297
|
},
|
|
7278
7298
|
[isPressed, releaseEvent, eventBus, onRelease]
|
|
7279
7299
|
);
|
|
7280
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
7300
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7281
7301
|
"button",
|
|
7282
7302
|
{
|
|
7283
7303
|
type: "button",
|
|
@@ -7298,20 +7318,27 @@ function ControlButton({
|
|
|
7298
7318
|
disabled && "opacity-50 cursor-not-allowed",
|
|
7299
7319
|
className
|
|
7300
7320
|
),
|
|
7301
|
-
children:
|
|
7302
|
-
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
7309
|
-
|
|
7310
|
-
|
|
7321
|
+
children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7322
|
+
"img",
|
|
7323
|
+
{
|
|
7324
|
+
src: assetUrl,
|
|
7325
|
+
alt: "",
|
|
7326
|
+
width: 24,
|
|
7327
|
+
height: 24,
|
|
7328
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7329
|
+
className: "flex-shrink-0"
|
|
7330
|
+
}
|
|
7331
|
+
) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-2xl", children: typeof icon === "string" ? /^[a-zA-Z0-9-]+$/.test(icon) ? (() => {
|
|
7332
|
+
const I = resolveIcon(icon);
|
|
7333
|
+
return I ? /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-6 h-6" }) : null;
|
|
7334
|
+
})() : icon : /* @__PURE__ */ (() => {
|
|
7335
|
+
const I = icon;
|
|
7336
|
+
return /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-6 h-6" });
|
|
7337
|
+
})() }) : label ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: label }) : null
|
|
7311
7338
|
}
|
|
7312
7339
|
);
|
|
7313
7340
|
}
|
|
7314
|
-
var sizeMap3, shapeMap, variantMap;
|
|
7341
|
+
var sizeMap3, shapeMap, variantMap, DEFAULT_ASSET_URL2;
|
|
7315
7342
|
var init_ControlButton = __esm({
|
|
7316
7343
|
"components/game/atoms/ControlButton.tsx"() {
|
|
7317
7344
|
"use client";
|
|
@@ -7334,6 +7361,7 @@ var init_ControlButton = __esm({
|
|
|
7334
7361
|
secondary: "bg-secondary text-secondary-foreground border-border hover:bg-secondary-hover",
|
|
7335
7362
|
ghost: "bg-transparent text-foreground border-border hover:bg-muted"
|
|
7336
7363
|
};
|
|
7364
|
+
DEFAULT_ASSET_URL2 = "https://almadar-kflow-assets.web.app/shared/effects/particles/circle_01.png";
|
|
7337
7365
|
ControlButton.displayName = "ControlButton";
|
|
7338
7366
|
}
|
|
7339
7367
|
});
|
|
@@ -7413,6 +7441,7 @@ var init_Sprite = __esm({
|
|
|
7413
7441
|
}
|
|
7414
7442
|
});
|
|
7415
7443
|
function StateIndicator({
|
|
7444
|
+
assetUrl = DEFAULT_ASSET_URL3,
|
|
7416
7445
|
state = "idle",
|
|
7417
7446
|
label,
|
|
7418
7447
|
size = "md",
|
|
@@ -7435,18 +7464,29 @@ function StateIndicator({
|
|
|
7435
7464
|
className
|
|
7436
7465
|
),
|
|
7437
7466
|
children: [
|
|
7438
|
-
/* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", children:
|
|
7467
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7468
|
+
"img",
|
|
7469
|
+
{
|
|
7470
|
+
src: assetUrl,
|
|
7471
|
+
alt: displayLabel,
|
|
7472
|
+
width: 16,
|
|
7473
|
+
height: 16,
|
|
7474
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7475
|
+
className: "flex-shrink-0"
|
|
7476
|
+
}
|
|
7477
|
+
) : 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 }) }),
|
|
7439
7478
|
/* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", children: displayLabel })
|
|
7440
7479
|
]
|
|
7441
7480
|
}
|
|
7442
7481
|
);
|
|
7443
7482
|
}
|
|
7444
|
-
var DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
7483
|
+
var DEFAULT_ASSET_URL3, DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
7445
7484
|
var init_StateIndicator = __esm({
|
|
7446
7485
|
"components/game/atoms/StateIndicator.tsx"() {
|
|
7447
7486
|
init_Box();
|
|
7448
7487
|
init_Icon();
|
|
7449
7488
|
init_cn();
|
|
7489
|
+
DEFAULT_ASSET_URL3 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
7450
7490
|
DEFAULT_STATE_STYLES = {
|
|
7451
7491
|
idle: { icon: "\u23F8", bgClass: "bg-muted" },
|
|
7452
7492
|
active: { icon: "\u25B6", bgClass: "bg-success" },
|
|
@@ -7521,6 +7561,7 @@ var init_TimerDisplay = __esm({
|
|
|
7521
7561
|
}
|
|
7522
7562
|
});
|
|
7523
7563
|
function ResourceCounter({
|
|
7564
|
+
assetUrl = DEFAULT_ASSET_URL4,
|
|
7524
7565
|
icon,
|
|
7525
7566
|
label = "Gold",
|
|
7526
7567
|
value = 250,
|
|
@@ -7540,7 +7581,17 @@ function ResourceCounter({
|
|
|
7540
7581
|
className
|
|
7541
7582
|
),
|
|
7542
7583
|
children: [
|
|
7543
|
-
|
|
7584
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7585
|
+
"img",
|
|
7586
|
+
{
|
|
7587
|
+
src: assetUrl,
|
|
7588
|
+
alt: label,
|
|
7589
|
+
width: sizes.img,
|
|
7590
|
+
height: sizes.img,
|
|
7591
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7592
|
+
className: "flex-shrink-0"
|
|
7593
|
+
}
|
|
7594
|
+
) : 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,
|
|
7544
7595
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: label }),
|
|
7545
7596
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("font-bold tabular-nums", color && (color in colorTokenClasses2 ? colorTokenClasses2[color] : color)), children: [
|
|
7546
7597
|
value,
|
|
@@ -7553,7 +7604,7 @@ function ResourceCounter({
|
|
|
7553
7604
|
}
|
|
7554
7605
|
);
|
|
7555
7606
|
}
|
|
7556
|
-
var colorTokenClasses2, sizeMap5;
|
|
7607
|
+
var colorTokenClasses2, DEFAULT_ASSET_URL4, sizeMap5;
|
|
7557
7608
|
var init_ResourceCounter = __esm({
|
|
7558
7609
|
"components/game/atoms/ResourceCounter.tsx"() {
|
|
7559
7610
|
init_cn();
|
|
@@ -7566,16 +7617,17 @@ var init_ResourceCounter = __esm({
|
|
|
7566
7617
|
error: "text-error",
|
|
7567
7618
|
muted: "text-muted-foreground"
|
|
7568
7619
|
};
|
|
7620
|
+
DEFAULT_ASSET_URL4 = "https://almadar-kflow-assets.web.app/shared/world-map/gold_mine.png";
|
|
7569
7621
|
sizeMap5 = {
|
|
7570
|
-
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm" },
|
|
7571
|
-
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base" },
|
|
7572
|
-
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg" }
|
|
7622
|
+
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm", img: 16 },
|
|
7623
|
+
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base", img: 20 },
|
|
7624
|
+
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg", img: 28 }
|
|
7573
7625
|
};
|
|
7574
7626
|
ResourceCounter.displayName = "ResourceCounter";
|
|
7575
7627
|
}
|
|
7576
7628
|
});
|
|
7577
7629
|
function ItemSlot({
|
|
7578
|
-
assetUrl =
|
|
7630
|
+
assetUrl = DEFAULT_ASSET_URL5,
|
|
7579
7631
|
icon = "sword",
|
|
7580
7632
|
label = "Iron Sword",
|
|
7581
7633
|
quantity,
|
|
@@ -7634,7 +7686,7 @@ function ItemSlot({
|
|
|
7634
7686
|
}
|
|
7635
7687
|
);
|
|
7636
7688
|
}
|
|
7637
|
-
var sizeMap6, rarityBorderMap, rarityGlowMap,
|
|
7689
|
+
var sizeMap6, rarityBorderMap, rarityGlowMap, DEFAULT_ASSET_URL5, assetSizeMap;
|
|
7638
7690
|
var init_ItemSlot = __esm({
|
|
7639
7691
|
"components/game/atoms/ItemSlot.tsx"() {
|
|
7640
7692
|
"use client";
|
|
@@ -7659,7 +7711,7 @@ var init_ItemSlot = __esm({
|
|
|
7659
7711
|
epic: "shadow-lg",
|
|
7660
7712
|
legendary: "shadow-lg"
|
|
7661
7713
|
};
|
|
7662
|
-
|
|
7714
|
+
DEFAULT_ASSET_URL5 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
7663
7715
|
assetSizeMap = {
|
|
7664
7716
|
sm: 28,
|
|
7665
7717
|
md: 40,
|
|
@@ -7669,6 +7721,7 @@ var init_ItemSlot = __esm({
|
|
|
7669
7721
|
}
|
|
7670
7722
|
});
|
|
7671
7723
|
function TurnIndicator({
|
|
7724
|
+
assetUrl = DEFAULT_ASSET_URL6,
|
|
7672
7725
|
currentTurn = 1,
|
|
7673
7726
|
maxTurns,
|
|
7674
7727
|
activeTeam,
|
|
@@ -7701,14 +7754,24 @@ function TurnIndicator({
|
|
|
7701
7754
|
] }),
|
|
7702
7755
|
activeTeam && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7703
7756
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "|" }),
|
|
7704
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7757
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7758
|
+
"img",
|
|
7759
|
+
{
|
|
7760
|
+
src: assetUrl,
|
|
7761
|
+
alt: "",
|
|
7762
|
+
width: 12,
|
|
7763
|
+
height: 12,
|
|
7764
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7765
|
+
className: "flex-shrink-0"
|
|
7766
|
+
}
|
|
7767
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("rounded-full bg-success/20", sizes.dot) }),
|
|
7705
7768
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-success", children: activeTeam })
|
|
7706
7769
|
] })
|
|
7707
7770
|
]
|
|
7708
7771
|
}
|
|
7709
7772
|
);
|
|
7710
7773
|
}
|
|
7711
|
-
var sizeMap7;
|
|
7774
|
+
var sizeMap7, DEFAULT_ASSET_URL6;
|
|
7712
7775
|
var init_TurnIndicator = __esm({
|
|
7713
7776
|
"components/game/atoms/TurnIndicator.tsx"() {
|
|
7714
7777
|
init_cn();
|
|
@@ -7717,6 +7780,7 @@ var init_TurnIndicator = __esm({
|
|
|
7717
7780
|
md: { wrapper: "text-sm gap-2 px-3 py-1", dot: "w-2 h-2" },
|
|
7718
7781
|
lg: { wrapper: "text-base gap-2.5 px-4 py-1.5", dot: "w-2.5 h-2.5" }
|
|
7719
7782
|
};
|
|
7783
|
+
DEFAULT_ASSET_URL6 = "https://almadar-kflow-assets.web.app/shared/effects/particles/symbol_01.png";
|
|
7720
7784
|
TurnIndicator.displayName = "TurnIndicator";
|
|
7721
7785
|
}
|
|
7722
7786
|
});
|
|
@@ -7732,6 +7796,7 @@ function getComboScale(combo) {
|
|
|
7732
7796
|
return "";
|
|
7733
7797
|
}
|
|
7734
7798
|
function ComboCounter({
|
|
7799
|
+
assetUrl = DEFAULT_ASSET_URL7,
|
|
7735
7800
|
combo = 5,
|
|
7736
7801
|
multiplier,
|
|
7737
7802
|
streak,
|
|
@@ -7751,6 +7816,17 @@ function ComboCounter({
|
|
|
7751
7816
|
className
|
|
7752
7817
|
),
|
|
7753
7818
|
children: [
|
|
7819
|
+
assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7820
|
+
"img",
|
|
7821
|
+
{
|
|
7822
|
+
src: assetUrl,
|
|
7823
|
+
alt: "combo",
|
|
7824
|
+
width: 24,
|
|
7825
|
+
height: 24,
|
|
7826
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7827
|
+
className: "flex-shrink-0 mb-0.5"
|
|
7828
|
+
}
|
|
7829
|
+
),
|
|
7754
7830
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-black tabular-nums leading-none", sizes.combo, getComboIntensity(combo)), children: combo }),
|
|
7755
7831
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-bold uppercase tracking-wider text-muted-foreground", sizes.label), children: "combo" }),
|
|
7756
7832
|
multiplier != null && multiplier > 1 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("font-bold text-warning tabular-nums", sizes.multiplier), children: [
|
|
@@ -7765,10 +7841,11 @@ function ComboCounter({
|
|
|
7765
7841
|
}
|
|
7766
7842
|
);
|
|
7767
7843
|
}
|
|
7768
|
-
var sizeMap8;
|
|
7844
|
+
var DEFAULT_ASSET_URL7, sizeMap8;
|
|
7769
7845
|
var init_ComboCounter = __esm({
|
|
7770
7846
|
"components/game/atoms/ComboCounter.tsx"() {
|
|
7771
7847
|
init_cn();
|
|
7848
|
+
DEFAULT_ASSET_URL7 = "https://almadar-kflow-assets.web.app/shared/effects/flash/flash00.png";
|
|
7772
7849
|
sizeMap8 = {
|
|
7773
7850
|
sm: { combo: "text-lg", label: "text-xs", multiplier: "text-xs" },
|
|
7774
7851
|
md: { combo: "text-2xl", label: "text-xs", multiplier: "text-sm" },
|
|
@@ -7862,6 +7939,7 @@ var init_XPBar = __esm({
|
|
|
7862
7939
|
}
|
|
7863
7940
|
});
|
|
7864
7941
|
function WaypointMarker({
|
|
7942
|
+
assetUrl = DEFAULT_ASSET_URL8,
|
|
7865
7943
|
label,
|
|
7866
7944
|
icon,
|
|
7867
7945
|
active = true,
|
|
@@ -7900,7 +7978,17 @@ function WaypointMarker({
|
|
|
7900
7978
|
active && !completed && "bg-info text-foreground",
|
|
7901
7979
|
!active && !completed && "bg-muted"
|
|
7902
7980
|
),
|
|
7903
|
-
children: completed ? checkIcon :
|
|
7981
|
+
children: completed ? checkIcon : assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7982
|
+
"img",
|
|
7983
|
+
{
|
|
7984
|
+
src: assetUrl,
|
|
7985
|
+
alt: label,
|
|
7986
|
+
width: sizes.img,
|
|
7987
|
+
height: sizes.img,
|
|
7988
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7989
|
+
className: "flex-shrink-0"
|
|
7990
|
+
}
|
|
7991
|
+
) : icon ? typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon }) : null
|
|
7904
7992
|
}
|
|
7905
7993
|
)
|
|
7906
7994
|
] }),
|
|
@@ -7917,15 +8005,16 @@ function WaypointMarker({
|
|
|
7917
8005
|
)
|
|
7918
8006
|
] });
|
|
7919
8007
|
}
|
|
7920
|
-
var sizeMap10, checkIcon;
|
|
8008
|
+
var DEFAULT_ASSET_URL8, sizeMap10, checkIcon;
|
|
7921
8009
|
var init_WaypointMarker = __esm({
|
|
7922
8010
|
"components/game/atoms/WaypointMarker.tsx"() {
|
|
7923
8011
|
init_cn();
|
|
7924
8012
|
init_Icon();
|
|
8013
|
+
DEFAULT_ASSET_URL8 = "https://almadar-kflow-assets.web.app/shared/world-map/battle_marker.png";
|
|
7925
8014
|
sizeMap10 = {
|
|
7926
|
-
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1" },
|
|
7927
|
-
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5" },
|
|
7928
|
-
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2" }
|
|
8015
|
+
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1", img: 12 },
|
|
8016
|
+
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5", img: 18 },
|
|
8017
|
+
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2", img: 24 }
|
|
7929
8018
|
};
|
|
7930
8019
|
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" }) });
|
|
7931
8020
|
WaypointMarker.displayName = "WaypointMarker";
|
|
@@ -7938,6 +8027,7 @@ function formatDuration(seconds) {
|
|
|
7938
8027
|
return `${Math.round(seconds)}s`;
|
|
7939
8028
|
}
|
|
7940
8029
|
function StatusEffect({
|
|
8030
|
+
assetUrl = DEFAULT_ASSET_URL9,
|
|
7941
8031
|
icon = "shield",
|
|
7942
8032
|
label = "Shield",
|
|
7943
8033
|
duration = 30,
|
|
@@ -7958,7 +8048,17 @@ function StatusEffect({
|
|
|
7958
8048
|
),
|
|
7959
8049
|
title: label,
|
|
7960
8050
|
children: [
|
|
7961
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children:
|
|
8051
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
8052
|
+
"img",
|
|
8053
|
+
{
|
|
8054
|
+
src: assetUrl,
|
|
8055
|
+
alt: label,
|
|
8056
|
+
width: sizes.img,
|
|
8057
|
+
height: sizes.img,
|
|
8058
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
8059
|
+
className: "flex-shrink-0"
|
|
8060
|
+
}
|
|
8061
|
+
) : typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, size: "sm" }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, size: "sm" }) }),
|
|
7962
8062
|
duration !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7963
8063
|
"span",
|
|
7964
8064
|
{
|
|
@@ -7985,15 +8085,16 @@ function StatusEffect({
|
|
|
7985
8085
|
label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground mt-0.5 text-center whitespace-nowrap", children: label })
|
|
7986
8086
|
] });
|
|
7987
8087
|
}
|
|
7988
|
-
var sizeMap11, variantStyles7;
|
|
8088
|
+
var DEFAULT_ASSET_URL9, sizeMap11, variantStyles7;
|
|
7989
8089
|
var init_StatusEffect = __esm({
|
|
7990
8090
|
"components/game/atoms/StatusEffect.tsx"() {
|
|
7991
8091
|
init_cn();
|
|
7992
8092
|
init_Icon();
|
|
8093
|
+
DEFAULT_ASSET_URL9 = "https://almadar-kflow-assets.web.app/shared/effects/particles/flame_01.png";
|
|
7993
8094
|
sizeMap11 = {
|
|
7994
|
-
sm: { container: "w-8 h-8", icon: "text-sm", badge: "text-xs -top-1 -right-1 w-4 h-4", timer: "text-[9px]" },
|
|
7995
|
-
md: { container: "w-10 h-10", icon: "text-base", badge: "text-xs -top-1 -right-1 w-5 h-5", timer: "text-xs" },
|
|
7996
|
-
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" }
|
|
8095
|
+
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 },
|
|
8096
|
+
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 },
|
|
8097
|
+
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 }
|
|
7997
8098
|
};
|
|
7998
8099
|
variantStyles7 = {
|
|
7999
8100
|
buff: "border-success bg-success/20",
|
|
@@ -8004,6 +8105,7 @@ var init_StatusEffect = __esm({
|
|
|
8004
8105
|
}
|
|
8005
8106
|
});
|
|
8006
8107
|
function DamageNumber({
|
|
8108
|
+
assetUrl = DEFAULT_ASSET_URL10,
|
|
8007
8109
|
value = 42,
|
|
8008
8110
|
type = "damage",
|
|
8009
8111
|
size = "md",
|
|
@@ -8012,22 +8114,35 @@ function DamageNumber({
|
|
|
8012
8114
|
const displayText = type === "miss" ? "MISS" : type === "heal" ? `+${value}` : `${value}`;
|
|
8013
8115
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
8014
8116
|
/* @__PURE__ */ jsxRuntime.jsx("style", { children: floatKeyframes }),
|
|
8015
|
-
/* @__PURE__ */ jsxRuntime.
|
|
8117
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
8016
8118
|
"span",
|
|
8017
8119
|
{
|
|
8018
8120
|
className: cn(
|
|
8019
|
-
"inline-
|
|
8121
|
+
"inline-flex items-center gap-0.5 font-mono select-none pointer-events-none",
|
|
8020
8122
|
sizeMap12[size],
|
|
8021
8123
|
typeStyles[type],
|
|
8022
8124
|
className
|
|
8023
8125
|
),
|
|
8024
8126
|
style: { animation: "damageFloat 1s ease-out forwards" },
|
|
8025
|
-
children:
|
|
8127
|
+
children: [
|
|
8128
|
+
assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8129
|
+
"img",
|
|
8130
|
+
{
|
|
8131
|
+
src: assetUrl,
|
|
8132
|
+
alt: "",
|
|
8133
|
+
width: 14,
|
|
8134
|
+
height: 14,
|
|
8135
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
8136
|
+
className: "flex-shrink-0"
|
|
8137
|
+
}
|
|
8138
|
+
),
|
|
8139
|
+
displayText
|
|
8140
|
+
]
|
|
8026
8141
|
}
|
|
8027
8142
|
)
|
|
8028
8143
|
] });
|
|
8029
8144
|
}
|
|
8030
|
-
var sizeMap12, typeStyles, floatKeyframes;
|
|
8145
|
+
var sizeMap12, typeStyles, floatKeyframes, DEFAULT_ASSET_URL10;
|
|
8031
8146
|
var init_DamageNumber = __esm({
|
|
8032
8147
|
"components/game/atoms/DamageNumber.tsx"() {
|
|
8033
8148
|
init_cn();
|
|
@@ -8049,6 +8164,7 @@ var init_DamageNumber = __esm({
|
|
|
8049
8164
|
100% { opacity: 0; transform: translateY(-32px) scale(0.8); }
|
|
8050
8165
|
}
|
|
8051
8166
|
`;
|
|
8167
|
+
DEFAULT_ASSET_URL10 = "https://almadar-kflow-assets.web.app/shared/effects/particles/spark_01.png";
|
|
8052
8168
|
DamageNumber.displayName = "DamageNumber";
|
|
8053
8169
|
}
|
|
8054
8170
|
});
|
|
@@ -8137,6 +8253,7 @@ var init_ChoiceButton = __esm({
|
|
|
8137
8253
|
}
|
|
8138
8254
|
});
|
|
8139
8255
|
function ActionButton({
|
|
8256
|
+
assetUrl = DEFAULT_ASSET_URL11,
|
|
8140
8257
|
label = "Attack",
|
|
8141
8258
|
icon,
|
|
8142
8259
|
cooldown = 0,
|
|
@@ -8176,13 +8293,23 @@ function ActionButton({
|
|
|
8176
8293
|
}
|
|
8177
8294
|
}
|
|
8178
8295
|
),
|
|
8179
|
-
|
|
8296
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
8297
|
+
"img",
|
|
8298
|
+
{
|
|
8299
|
+
src: assetUrl,
|
|
8300
|
+
alt: "",
|
|
8301
|
+
width: 16,
|
|
8302
|
+
height: 16,
|
|
8303
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
8304
|
+
className: cn("flex-shrink-0", sizes.icon)
|
|
8305
|
+
}
|
|
8306
|
+
) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? (() => {
|
|
8180
8307
|
const I = resolveIcon(icon);
|
|
8181
8308
|
return I ? /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-4 h-4" }) : null;
|
|
8182
8309
|
})() : /* @__PURE__ */ (() => {
|
|
8183
8310
|
const I = icon;
|
|
8184
8311
|
return /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-4 h-4" });
|
|
8185
|
-
})() }),
|
|
8312
|
+
})() }) : null,
|
|
8186
8313
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative z-10", children: label }),
|
|
8187
8314
|
hotkey && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8188
8315
|
"span",
|
|
@@ -8198,7 +8325,7 @@ function ActionButton({
|
|
|
8198
8325
|
}
|
|
8199
8326
|
);
|
|
8200
8327
|
}
|
|
8201
|
-
var sizeMap13, variantStyles8;
|
|
8328
|
+
var sizeMap13, variantStyles8, DEFAULT_ASSET_URL11;
|
|
8202
8329
|
var init_ActionButton = __esm({
|
|
8203
8330
|
"components/game/atoms/ActionButton.tsx"() {
|
|
8204
8331
|
init_cn();
|
|
@@ -8213,6 +8340,7 @@ var init_ActionButton = __esm({
|
|
|
8213
8340
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary-hover border-border",
|
|
8214
8341
|
danger: "bg-error text-error-foreground hover:bg-error/90 border-error"
|
|
8215
8342
|
};
|
|
8343
|
+
DEFAULT_ASSET_URL11 = "https://almadar-kflow-assets.web.app/shared/effects/particles/slash_01.png";
|
|
8216
8344
|
ActionButton.displayName = "ActionButton";
|
|
8217
8345
|
}
|
|
8218
8346
|
});
|
|
@@ -9081,10 +9209,10 @@ var init_Container = __esm({
|
|
|
9081
9209
|
center = true,
|
|
9082
9210
|
className,
|
|
9083
9211
|
children,
|
|
9084
|
-
as:
|
|
9212
|
+
as: Component2 = "div"
|
|
9085
9213
|
}) => {
|
|
9086
9214
|
const resolvedSize = maxWidth ?? size ?? "lg";
|
|
9087
|
-
const Comp =
|
|
9215
|
+
const Comp = Component2;
|
|
9088
9216
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9089
9217
|
Comp,
|
|
9090
9218
|
{
|
|
@@ -11906,15 +12034,15 @@ function BattleBoard({
|
|
|
11906
12034
|
const board = boardEntity(entity) ?? {};
|
|
11907
12035
|
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
|
|
11908
12036
|
const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
|
|
11909
|
-
const boardWidth = num(board.boardWidth, 8);
|
|
11910
|
-
const boardHeight = num(board.boardHeight, 6);
|
|
12037
|
+
const boardWidth = num(board.gridWidth ?? board.boardWidth, 8);
|
|
12038
|
+
const boardHeight = num(board.gridHeight ?? board.boardHeight, 6);
|
|
11911
12039
|
const assetManifest = propAssetManifest ?? board.assetManifest;
|
|
11912
12040
|
const backgroundImage = board.backgroundImage;
|
|
11913
12041
|
const units = rows(board.units);
|
|
11914
12042
|
const selectedUnitId = board.selectedUnitId ?? null;
|
|
11915
12043
|
const currentPhase = str(board.phase) || "observation";
|
|
11916
12044
|
const currentTurn = num(board.turn, 1);
|
|
11917
|
-
const gameResult = board.
|
|
12045
|
+
const gameResult = board.result ?? null;
|
|
11918
12046
|
const eventBus = useEventBus();
|
|
11919
12047
|
const { t } = hooks.useTranslate();
|
|
11920
12048
|
const [hoveredTile, setHoveredTile] = React83.useState(null);
|
|
@@ -11935,7 +12063,7 @@ function BattleBoard({
|
|
|
11935
12063
|
const validMoves = React83.useMemo(() => {
|
|
11936
12064
|
if (!selectedUnit || currentPhase !== "movement") return [];
|
|
11937
12065
|
const moves = [];
|
|
11938
|
-
const range = num(
|
|
12066
|
+
const range = num(board.movementRange, 2);
|
|
11939
12067
|
const origin = unitPosition(selectedUnit);
|
|
11940
12068
|
for (let dy = -range; dy <= range; dy++) {
|
|
11941
12069
|
for (let dx = -range; dx <= range; dx++) {
|
|
@@ -20220,15 +20348,24 @@ function CastleBoard({
|
|
|
20220
20348
|
featureClickEvent,
|
|
20221
20349
|
unitClickEvent,
|
|
20222
20350
|
tileClickEvent,
|
|
20351
|
+
playAgainEvent,
|
|
20223
20352
|
className
|
|
20224
20353
|
}) {
|
|
20225
20354
|
const eventBus = useEventBus();
|
|
20355
|
+
const { t } = hooks.useTranslate();
|
|
20226
20356
|
const resolved = boardEntity(entity);
|
|
20227
20357
|
const tiles = propTiles ?? (Array.isArray(resolved?.tiles) ? resolved.tiles : []);
|
|
20228
20358
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
20229
20359
|
const units = propUnits ?? (Array.isArray(resolved?.units) ? resolved.units : []);
|
|
20230
20360
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
20231
20361
|
const backgroundImage = resolved?.backgroundImage;
|
|
20362
|
+
const gold = num(resolved?.gold);
|
|
20363
|
+
const health = num(resolved?.health);
|
|
20364
|
+
const maxHealth = num(resolved?.maxHealth);
|
|
20365
|
+
const wave = num(resolved?.wave);
|
|
20366
|
+
const tickCount = num(resolved?.tickCount);
|
|
20367
|
+
const buildings = rows(resolved?.buildings);
|
|
20368
|
+
const result = str(resolved?.result) || "none";
|
|
20232
20369
|
const [hoveredTile, setHoveredTile] = React83.useState(null);
|
|
20233
20370
|
const [selectedFeature, setSelectedFeature] = React83.useState(null);
|
|
20234
20371
|
const hoveredFeature = React83.useMemo(() => {
|
|
@@ -20241,7 +20378,7 @@ function CastleBoard({
|
|
|
20241
20378
|
(u) => u.position?.x === hoveredTile.x && u.position?.y === hoveredTile.y
|
|
20242
20379
|
) ?? null;
|
|
20243
20380
|
}, [hoveredTile, units]);
|
|
20244
|
-
const maxY = Math.max(...tiles.map((
|
|
20381
|
+
const maxY = Math.max(...tiles.map((t2) => t2.y), 0);
|
|
20245
20382
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
20246
20383
|
const tileToScreen = React83.useCallback(
|
|
20247
20384
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
@@ -20276,6 +20413,11 @@ function CastleBoard({
|
|
|
20276
20413
|
}
|
|
20277
20414
|
}, [units, onUnitClick, unitClickEvent, eventBus]);
|
|
20278
20415
|
const clearSelection = React83.useCallback(() => setSelectedFeature(null), []);
|
|
20416
|
+
const handlePlayAgain = React83.useCallback(() => {
|
|
20417
|
+
if (playAgainEvent) {
|
|
20418
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
20419
|
+
}
|
|
20420
|
+
}, [playAgainEvent, eventBus]);
|
|
20279
20421
|
const ctx = React83.useMemo(
|
|
20280
20422
|
() => ({
|
|
20281
20423
|
hoveredTile,
|
|
@@ -20284,11 +20426,18 @@ function CastleBoard({
|
|
|
20284
20426
|
selectedFeature,
|
|
20285
20427
|
clearSelection,
|
|
20286
20428
|
tileToScreen,
|
|
20287
|
-
scale
|
|
20429
|
+
scale,
|
|
20430
|
+
gold,
|
|
20431
|
+
health,
|
|
20432
|
+
maxHealth,
|
|
20433
|
+
wave,
|
|
20434
|
+
tickCount,
|
|
20435
|
+
buildings,
|
|
20436
|
+
result
|
|
20288
20437
|
}),
|
|
20289
|
-
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale]
|
|
20438
|
+
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale, gold, health, maxHealth, wave, tickCount, buildings, result]
|
|
20290
20439
|
);
|
|
20291
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("castle-board min-h-screen flex flex-col bg-background", className), children: [
|
|
20440
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("castle-board relative min-h-screen flex flex-col bg-background", className), children: [
|
|
20292
20441
|
header && header(ctx),
|
|
20293
20442
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
|
|
20294
20443
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-auto p-4 relative", children: [
|
|
@@ -20313,7 +20462,29 @@ function CastleBoard({
|
|
|
20313
20462
|
] }),
|
|
20314
20463
|
sidePanel && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-96 shrink-0 border-l border-border bg-surface overflow-y-auto", children: sidePanel(ctx) })
|
|
20315
20464
|
] }),
|
|
20316
|
-
footer && footer(ctx)
|
|
20465
|
+
footer && footer(ctx),
|
|
20466
|
+
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: [
|
|
20467
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
20468
|
+
Typography,
|
|
20469
|
+
{
|
|
20470
|
+
variant: "h2",
|
|
20471
|
+
className: cn(
|
|
20472
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
20473
|
+
result === "victory" ? "text-warning" : "text-error"
|
|
20474
|
+
),
|
|
20475
|
+
children: result === "victory" ? t("battle.victory") : t("battle.defeat")
|
|
20476
|
+
}
|
|
20477
|
+
),
|
|
20478
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
20479
|
+
Button,
|
|
20480
|
+
{
|
|
20481
|
+
variant: "primary",
|
|
20482
|
+
className: "px-8 py-3 font-semibold",
|
|
20483
|
+
onClick: handlePlayAgain,
|
|
20484
|
+
children: t("battle.playAgain")
|
|
20485
|
+
}
|
|
20486
|
+
)
|
|
20487
|
+
] }) })
|
|
20317
20488
|
] });
|
|
20318
20489
|
}
|
|
20319
20490
|
var init_CastleBoard = __esm({
|
|
@@ -20321,6 +20492,10 @@ var init_CastleBoard = __esm({
|
|
|
20321
20492
|
"use client";
|
|
20322
20493
|
init_cn();
|
|
20323
20494
|
init_useEventBus();
|
|
20495
|
+
init_Box();
|
|
20496
|
+
init_Button();
|
|
20497
|
+
init_Typography();
|
|
20498
|
+
init_Stack();
|
|
20324
20499
|
init_IsometricCanvas();
|
|
20325
20500
|
init_boardEntity();
|
|
20326
20501
|
init_isometric();
|
|
@@ -21197,22 +21372,16 @@ function ClassifierBoard({
|
|
|
21197
21372
|
const { emit } = useEventBus();
|
|
21198
21373
|
const { t } = hooks.useTranslate();
|
|
21199
21374
|
const resolved = boardEntity(entity);
|
|
21200
|
-
const [localAssignments, setLocalAssignments] = React83.useState({});
|
|
21201
21375
|
const [headerError, setHeaderError] = React83.useState(false);
|
|
21202
|
-
const [localSubmitted, setLocalSubmitted] = React83.useState(false);
|
|
21203
|
-
const [localAttempts, setLocalAttempts] = React83.useState(0);
|
|
21204
|
-
const [showHint, setShowHint] = React83.useState(false);
|
|
21205
21376
|
const items = Array.isArray(resolved?.items) ? resolved.items : [];
|
|
21206
21377
|
const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
|
|
21207
|
-
const
|
|
21208
|
-
const
|
|
21209
|
-
const
|
|
21210
|
-
const assignments =
|
|
21211
|
-
if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
|
|
21378
|
+
const result = str(resolved?.result);
|
|
21379
|
+
const submitted = result === "win";
|
|
21380
|
+
const attempts = num(resolved?.attempts);
|
|
21381
|
+
const assignments = items.reduce((acc, item) => {
|
|
21382
|
+
if (item.assignedCategory != null && item.assignedCategory !== "") acc[item.id] = item.assignedCategory;
|
|
21212
21383
|
return acc;
|
|
21213
|
-
}, {})
|
|
21214
|
-
const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
|
|
21215
|
-
const submitted = entityDrivesResult || localSubmitted;
|
|
21384
|
+
}, {});
|
|
21216
21385
|
const unassignedItems = items.filter((item) => !assignments[item.id]);
|
|
21217
21386
|
const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
|
|
21218
21387
|
const results = submitted ? items.map((item) => ({
|
|
@@ -21220,54 +21389,25 @@ function ClassifierBoard({
|
|
|
21220
21389
|
assigned: assignments[item.id],
|
|
21221
21390
|
correct: assignments[item.id] === item.correctCategory
|
|
21222
21391
|
})) : [];
|
|
21223
|
-
const allCorrect =
|
|
21392
|
+
const allCorrect = result === "win";
|
|
21224
21393
|
const correctCount = results.filter((r) => r.correct).length;
|
|
21394
|
+
const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
|
|
21225
21395
|
const handleAssign = (itemId, categoryId) => {
|
|
21226
21396
|
if (submitted) return;
|
|
21227
|
-
if (assignEvent) {
|
|
21228
|
-
emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
21229
|
-
}
|
|
21230
|
-
if (!entityHasAssignments) {
|
|
21231
|
-
setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
|
|
21232
|
-
}
|
|
21397
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
21233
21398
|
};
|
|
21234
21399
|
const handleUnassign = (itemId) => {
|
|
21235
21400
|
if (submitted) return;
|
|
21236
|
-
if (
|
|
21237
|
-
setLocalAssignments((prev) => {
|
|
21238
|
-
const next = { ...prev };
|
|
21239
|
-
delete next[itemId];
|
|
21240
|
-
return next;
|
|
21241
|
-
});
|
|
21242
|
-
}
|
|
21401
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId: "" });
|
|
21243
21402
|
};
|
|
21244
|
-
const handleSubmit =
|
|
21245
|
-
if (checkEvent) {
|
|
21246
|
-
|
|
21247
|
-
|
|
21248
|
-
if (!entityDrivesResult) {
|
|
21249
|
-
setLocalSubmitted(true);
|
|
21250
|
-
setLocalAttempts((a) => a + 1);
|
|
21251
|
-
const correct = items.every((item) => assignments[item.id] === item.correctCategory);
|
|
21252
|
-
if (correct) {
|
|
21253
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
21254
|
-
}
|
|
21255
|
-
}
|
|
21256
|
-
}, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
|
|
21257
|
-
const handleReset = () => {
|
|
21258
|
-
if (!entityDrivesResult) setLocalSubmitted(false);
|
|
21259
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
21260
|
-
setShowHint(true);
|
|
21403
|
+
const handleSubmit = () => {
|
|
21404
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
21405
|
+
if (allAssigned && items.every((item) => assignments[item.id] === item.correctCategory)) {
|
|
21406
|
+
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
21261
21407
|
}
|
|
21262
21408
|
};
|
|
21263
21409
|
const handleFullReset = () => {
|
|
21264
|
-
if (playAgainEvent) {
|
|
21265
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
21266
|
-
}
|
|
21267
|
-
setLocalAssignments({});
|
|
21268
|
-
setLocalSubmitted(false);
|
|
21269
|
-
setLocalAttempts(0);
|
|
21270
|
-
setShowHint(false);
|
|
21410
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
21271
21411
|
};
|
|
21272
21412
|
if (!resolved) return null;
|
|
21273
21413
|
const theme = resolved.theme ?? void 0;
|
|
@@ -21306,17 +21446,17 @@ function ClassifierBoard({
|
|
|
21306
21446
|
/* @__PURE__ */ jsxRuntime.jsx(Badge, { size: "sm", children: catItems.length })
|
|
21307
21447
|
] }),
|
|
21308
21448
|
/* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-wrap min-h-[2rem]", children: catItems.map((item) => {
|
|
21309
|
-
const
|
|
21449
|
+
const result2 = results.find((r) => r.item.id === item.id);
|
|
21310
21450
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
21311
21451
|
Badge,
|
|
21312
21452
|
{
|
|
21313
21453
|
size: "sm",
|
|
21314
|
-
className: `cursor-pointer ${
|
|
21454
|
+
className: `cursor-pointer ${result2 ? result2.correct ? "border-success bg-success/10" : "border-error bg-error/10" : ""}`,
|
|
21315
21455
|
onClick: () => handleUnassign(item.id),
|
|
21316
21456
|
children: [
|
|
21317
21457
|
item.iconUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: item.iconUrl, alt: "", className: "w-3 h-3 object-contain inline-block" }),
|
|
21318
21458
|
item.label,
|
|
21319
|
-
|
|
21459
|
+
result2 && /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: result2.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "xs", className: result2.correct ? "text-success" : "text-error" })
|
|
21320
21460
|
]
|
|
21321
21461
|
},
|
|
21322
21462
|
item.id
|
|
@@ -21345,10 +21485,10 @@ function ClassifierBoard({
|
|
|
21345
21485
|
] }) }),
|
|
21346
21486
|
showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
|
|
21347
21487
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
21348
|
-
!submitted
|
|
21488
|
+
!submitted && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
|
|
21349
21489
|
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
|
|
21350
21490
|
t("classifier.check")
|
|
21351
|
-
] })
|
|
21491
|
+
] }),
|
|
21352
21492
|
/* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
|
|
21353
21493
|
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
|
|
21354
21494
|
t("classifier.reset")
|
|
@@ -25066,7 +25206,7 @@ var init_Flex = __esm({
|
|
|
25066
25206
|
basis,
|
|
25067
25207
|
className,
|
|
25068
25208
|
children,
|
|
25069
|
-
as:
|
|
25209
|
+
as: Component2 = "div"
|
|
25070
25210
|
}) => {
|
|
25071
25211
|
const flexStyle = {};
|
|
25072
25212
|
if (grow !== void 0 || shrink !== void 0 || basis !== void 0) {
|
|
@@ -25078,7 +25218,7 @@ var init_Flex = __esm({
|
|
|
25078
25218
|
flexStyle.flexBasis = typeof basis === "number" ? `${basis}px` : basis;
|
|
25079
25219
|
}
|
|
25080
25220
|
}
|
|
25081
|
-
const Comp =
|
|
25221
|
+
const Comp = Component2;
|
|
25082
25222
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
25083
25223
|
Comp,
|
|
25084
25224
|
{
|
|
@@ -25198,11 +25338,11 @@ var init_Grid = __esm({
|
|
|
25198
25338
|
className,
|
|
25199
25339
|
style,
|
|
25200
25340
|
children,
|
|
25201
|
-
as:
|
|
25341
|
+
as: Component2 = "div"
|
|
25202
25342
|
}) => {
|
|
25203
25343
|
const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
|
|
25204
25344
|
return React83__namespace.default.createElement(
|
|
25205
|
-
|
|
25345
|
+
Component2,
|
|
25206
25346
|
{
|
|
25207
25347
|
className: cn(
|
|
25208
25348
|
"grid",
|
|
@@ -27028,6 +27168,7 @@ var init_ProgressDots = __esm({
|
|
|
27028
27168
|
}
|
|
27029
27169
|
});
|
|
27030
27170
|
function StatBadge({
|
|
27171
|
+
assetUrl = DEFAULT_ASSET_URL12,
|
|
27031
27172
|
label,
|
|
27032
27173
|
value = 0,
|
|
27033
27174
|
max,
|
|
@@ -27051,7 +27192,17 @@ function StatBadge({
|
|
|
27051
27192
|
className
|
|
27052
27193
|
),
|
|
27053
27194
|
children: [
|
|
27054
|
-
|
|
27195
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
27196
|
+
"img",
|
|
27197
|
+
{
|
|
27198
|
+
src: assetUrl,
|
|
27199
|
+
alt: "",
|
|
27200
|
+
width: 16,
|
|
27201
|
+
height: 16,
|
|
27202
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
27203
|
+
className: "flex-shrink-0"
|
|
27204
|
+
}
|
|
27205
|
+
) : 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,
|
|
27055
27206
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground font-medium", children: label }),
|
|
27056
27207
|
format === "hearts" && max && /* @__PURE__ */ jsxRuntime.jsx(
|
|
27057
27208
|
HealthBar,
|
|
@@ -27084,7 +27235,7 @@ function StatBadge({
|
|
|
27084
27235
|
}
|
|
27085
27236
|
);
|
|
27086
27237
|
}
|
|
27087
|
-
var sizeMap17, variantMap2;
|
|
27238
|
+
var sizeMap17, variantMap2, DEFAULT_ASSET_URL12;
|
|
27088
27239
|
var init_StatBadge = __esm({
|
|
27089
27240
|
"components/game/molecules/StatBadge.tsx"() {
|
|
27090
27241
|
init_cn();
|
|
@@ -27103,6 +27254,7 @@ var init_StatBadge = __esm({
|
|
|
27103
27254
|
warning: "bg-warning/15 border-warning/40 text-foreground",
|
|
27104
27255
|
danger: "bg-error/15 border-error/40 text-foreground"
|
|
27105
27256
|
};
|
|
27257
|
+
DEFAULT_ASSET_URL12 = "https://almadar-kflow-assets.web.app/shared/effects/particles/light_01.png";
|
|
27106
27258
|
StatBadge.displayName = "StatBadge";
|
|
27107
27259
|
}
|
|
27108
27260
|
});
|
|
@@ -27759,6 +27911,7 @@ var init_TurnPanel = __esm({
|
|
|
27759
27911
|
}
|
|
27760
27912
|
});
|
|
27761
27913
|
function EnemyPlate({
|
|
27914
|
+
assetUrl = DEFAULT_ASSET_URL13,
|
|
27762
27915
|
name = "Shadow Guard",
|
|
27763
27916
|
health = 80,
|
|
27764
27917
|
maxHealth = 100,
|
|
@@ -27776,15 +27929,28 @@ function EnemyPlate({
|
|
|
27776
27929
|
),
|
|
27777
27930
|
children: [
|
|
27778
27931
|
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center justify-between gap-2", children: [
|
|
27779
|
-
/* @__PURE__ */ jsxRuntime.
|
|
27780
|
-
|
|
27781
|
-
|
|
27782
|
-
|
|
27783
|
-
|
|
27784
|
-
|
|
27785
|
-
|
|
27786
|
-
|
|
27787
|
-
|
|
27932
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-1.5 min-w-0", children: [
|
|
27933
|
+
assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
27934
|
+
"img",
|
|
27935
|
+
{
|
|
27936
|
+
src: assetUrl,
|
|
27937
|
+
alt: name,
|
|
27938
|
+
width: 24,
|
|
27939
|
+
height: 24,
|
|
27940
|
+
style: { objectFit: "cover", borderRadius: "50%" },
|
|
27941
|
+
className: "flex-shrink-0"
|
|
27942
|
+
}
|
|
27943
|
+
),
|
|
27944
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
27945
|
+
Typography,
|
|
27946
|
+
{
|
|
27947
|
+
variant: "caption",
|
|
27948
|
+
weight: "bold",
|
|
27949
|
+
className: "text-[var(--color-foreground)] truncate",
|
|
27950
|
+
children: name
|
|
27951
|
+
}
|
|
27952
|
+
)
|
|
27953
|
+
] }),
|
|
27788
27954
|
level != null && /* @__PURE__ */ jsxRuntime.jsxs(Badge, { variant: "neutral", size: "sm", children: [
|
|
27789
27955
|
"Lv.",
|
|
27790
27956
|
level
|
|
@@ -27829,7 +27995,7 @@ function EnemyPlate({
|
|
|
27829
27995
|
}
|
|
27830
27996
|
);
|
|
27831
27997
|
}
|
|
27832
|
-
var effectVariantMap2, DEFAULT_ENEMY_EFFECTS;
|
|
27998
|
+
var effectVariantMap2, DEFAULT_ENEMY_EFFECTS, DEFAULT_ASSET_URL13;
|
|
27833
27999
|
var init_EnemyPlate = __esm({
|
|
27834
28000
|
"components/game/molecules/EnemyPlate.tsx"() {
|
|
27835
28001
|
"use client";
|
|
@@ -27846,6 +28012,7 @@ var init_EnemyPlate = __esm({
|
|
|
27846
28012
|
DEFAULT_ENEMY_EFFECTS = [
|
|
27847
28013
|
{ icon: "flame", label: "Burn", variant: "debuff" }
|
|
27848
28014
|
];
|
|
28015
|
+
DEFAULT_ASSET_URL13 = "https://almadar-kflow-assets.web.app/shared/portraits/shadow-legion.png";
|
|
27849
28016
|
EnemyPlate.displayName = "EnemyPlate";
|
|
27850
28017
|
}
|
|
27851
28018
|
});
|
|
@@ -29377,13 +29544,13 @@ var init_MapView = __esm({
|
|
|
29377
29544
|
shadowSize: [41, 41]
|
|
29378
29545
|
});
|
|
29379
29546
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
29380
|
-
const { useEffect:
|
|
29547
|
+
const { useEffect: useEffect79, useRef: useRef74, useCallback: useCallback118, useState: useState109 } = React83__namespace.default;
|
|
29381
29548
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
29382
|
-
const { useEventBus:
|
|
29549
|
+
const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
29383
29550
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
29384
29551
|
const map = useMap();
|
|
29385
|
-
const prevRef =
|
|
29386
|
-
|
|
29552
|
+
const prevRef = useRef74({ centerLat, centerLng, zoom });
|
|
29553
|
+
useEffect79(() => {
|
|
29387
29554
|
const prev = prevRef.current;
|
|
29388
29555
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
29389
29556
|
map.setView([centerLat, centerLng], zoom);
|
|
@@ -29394,7 +29561,7 @@ var init_MapView = __esm({
|
|
|
29394
29561
|
}
|
|
29395
29562
|
function MapClickHandler({ onMapClick }) {
|
|
29396
29563
|
const map = useMap();
|
|
29397
|
-
|
|
29564
|
+
useEffect79(() => {
|
|
29398
29565
|
if (!onMapClick) return;
|
|
29399
29566
|
const handler = (e) => {
|
|
29400
29567
|
onMapClick(e.latlng.lat, e.latlng.lng);
|
|
@@ -29421,9 +29588,9 @@ var init_MapView = __esm({
|
|
|
29421
29588
|
className,
|
|
29422
29589
|
showAttribution = true
|
|
29423
29590
|
}) {
|
|
29424
|
-
const eventBus =
|
|
29425
|
-
const [clickedPosition, setClickedPosition] =
|
|
29426
|
-
const handleMapClick =
|
|
29591
|
+
const eventBus = useEventBus3();
|
|
29592
|
+
const [clickedPosition, setClickedPosition] = useState109(null);
|
|
29593
|
+
const handleMapClick = useCallback118((lat, lng) => {
|
|
29427
29594
|
if (showClickedPin) {
|
|
29428
29595
|
setClickedPosition({ lat, lng });
|
|
29429
29596
|
}
|
|
@@ -29432,7 +29599,7 @@ var init_MapView = __esm({
|
|
|
29432
29599
|
eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
|
|
29433
29600
|
}
|
|
29434
29601
|
}, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
|
|
29435
|
-
const handleMarkerClick =
|
|
29602
|
+
const handleMarkerClick = useCallback118((marker) => {
|
|
29436
29603
|
onMarkerClick?.(marker);
|
|
29437
29604
|
if (markerClickEvent) {
|
|
29438
29605
|
eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
|
|
@@ -36559,10 +36726,10 @@ var init_Section = __esm({
|
|
|
36559
36726
|
children,
|
|
36560
36727
|
headerClassName,
|
|
36561
36728
|
contentClassName,
|
|
36562
|
-
as:
|
|
36729
|
+
as: Component2 = "section"
|
|
36563
36730
|
}) => {
|
|
36564
36731
|
const hasHeader = title || description || action;
|
|
36565
|
-
const Comp =
|
|
36732
|
+
const Comp = Component2;
|
|
36566
36733
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
36567
36734
|
Comp,
|
|
36568
36735
|
{
|
|
@@ -38663,44 +38830,27 @@ function DebuggerBoard({
|
|
|
38663
38830
|
const { t } = hooks.useTranslate();
|
|
38664
38831
|
const resolved = boardEntity(entity);
|
|
38665
38832
|
const [headerError, setHeaderError] = React83.useState(false);
|
|
38666
|
-
const [showHint, setShowHint] = React83.useState(false);
|
|
38667
38833
|
const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
|
|
38668
|
-
const result = resolved?.result
|
|
38834
|
+
const result = str(resolved?.result) || "none";
|
|
38669
38835
|
const attempts = num(resolved?.attempts);
|
|
38670
|
-
const submitted = result
|
|
38836
|
+
const submitted = result === "win";
|
|
38837
|
+
const showHint = !submitted && attempts >= 2 && Boolean(str(resolved?.hint));
|
|
38671
38838
|
const bugLines = lines.filter((l) => l.isBug);
|
|
38672
38839
|
const flaggedLines = lines.filter((l) => l.isFlagged);
|
|
38673
38840
|
const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
|
|
38674
38841
|
const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
|
|
38675
|
-
const allCorrect =
|
|
38842
|
+
const allCorrect = submitted;
|
|
38676
38843
|
const toggleLine = (lineId) => {
|
|
38677
38844
|
if (submitted) return;
|
|
38678
|
-
if (toggleFlagEvent) {
|
|
38679
|
-
emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
38680
|
-
}
|
|
38845
|
+
if (toggleFlagEvent) emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
38681
38846
|
};
|
|
38682
38847
|
const handleSubmit = React83.useCallback(() => {
|
|
38683
|
-
if (checkEvent) {
|
|
38684
|
-
emit(`UI:${checkEvent}`, {});
|
|
38685
|
-
}
|
|
38848
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
38686
38849
|
const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
|
|
38687
|
-
if (correct) {
|
|
38688
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
38689
|
-
}
|
|
38850
|
+
if (correct) emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
38690
38851
|
}, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
|
|
38691
38852
|
const handleReset = () => {
|
|
38692
|
-
if (playAgainEvent) {
|
|
38693
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
38694
|
-
}
|
|
38695
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
38696
|
-
setShowHint(true);
|
|
38697
|
-
}
|
|
38698
|
-
};
|
|
38699
|
-
const handleFullReset = () => {
|
|
38700
|
-
if (playAgainEvent) {
|
|
38701
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
38702
|
-
}
|
|
38703
|
-
setShowHint(false);
|
|
38853
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
38704
38854
|
};
|
|
38705
38855
|
if (!resolved) return null;
|
|
38706
38856
|
const theme = resolved.theme ?? void 0;
|
|
@@ -38724,7 +38874,7 @@ function DebuggerBoard({
|
|
|
38724
38874
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
|
|
38725
38875
|
] }),
|
|
38726
38876
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str(resolved.description) }),
|
|
38727
|
-
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(
|
|
38877
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(lines.filter((l) => l.isBug).length) }) })
|
|
38728
38878
|
] }) }),
|
|
38729
38879
|
/* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "none", children: lines.map((line, i) => {
|
|
38730
38880
|
const isFlagged = !!line.isFlagged;
|
|
@@ -38774,11 +38924,11 @@ function DebuggerBoard({
|
|
|
38774
38924
|
] }) }),
|
|
38775
38925
|
showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
|
|
38776
38926
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
38777
|
-
!submitted
|
|
38927
|
+
!submitted && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
|
|
38778
38928
|
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
|
|
38779
38929
|
t("debugger.submit")
|
|
38780
|
-
] })
|
|
38781
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick:
|
|
38930
|
+
] }),
|
|
38931
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handleReset, children: [
|
|
38782
38932
|
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
|
|
38783
38933
|
t("debugger.reset")
|
|
38784
38934
|
] })
|
|
@@ -39779,39 +39929,40 @@ function EventHandlerBoard({
|
|
|
39779
39929
|
stepDurationMs = 800,
|
|
39780
39930
|
playEvent,
|
|
39781
39931
|
completeEvent,
|
|
39932
|
+
editRuleEvent,
|
|
39933
|
+
playAgainEvent,
|
|
39782
39934
|
className
|
|
39783
39935
|
}) {
|
|
39784
39936
|
const { emit } = useEventBus();
|
|
39785
39937
|
const { t } = hooks.useTranslate();
|
|
39786
39938
|
const resolved = boardEntity(entity);
|
|
39787
|
-
const
|
|
39788
|
-
const
|
|
39939
|
+
const objects = rows(resolved?.objects);
|
|
39940
|
+
const entityResult = str(resolved?.result) || "none";
|
|
39941
|
+
const isSuccess = entityResult === "win";
|
|
39942
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
39789
39943
|
const [selectedObjectId, setSelectedObjectId] = React83.useState(
|
|
39790
|
-
|
|
39944
|
+
objects[0] ? objId(objects[0]) : null
|
|
39791
39945
|
);
|
|
39792
39946
|
const [headerError, setHeaderError] = React83.useState(false);
|
|
39793
|
-
const [
|
|
39947
|
+
const [isPlaying, setIsPlaying] = React83.useState(false);
|
|
39794
39948
|
const [eventLog, setEventLog] = React83.useState([]);
|
|
39795
|
-
const [attempts, setAttempts] = React83.useState(0);
|
|
39796
39949
|
const timerRef = React83.useRef(null);
|
|
39797
39950
|
const logIdCounter = React83.useRef(0);
|
|
39798
39951
|
React83.useEffect(() => () => {
|
|
39799
39952
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39800
39953
|
}, []);
|
|
39801
|
-
const selectedObject = objects.find((o) => objId(o) === selectedObjectId)
|
|
39954
|
+
const selectedObject = objects.find((o) => objId(o) === selectedObjectId) ?? null;
|
|
39802
39955
|
const handleRulesChange = React83.useCallback((objectId, rules) => {
|
|
39803
|
-
|
|
39804
|
-
|
|
39805
|
-
));
|
|
39806
|
-
}, []);
|
|
39956
|
+
if (editRuleEvent) emit(`UI:${editRuleEvent}`, { objectId, rules });
|
|
39957
|
+
}, [editRuleEvent, emit]);
|
|
39807
39958
|
const addLogEntry = React83.useCallback((icon, message, status = "done") => {
|
|
39808
39959
|
const id = `log-${logIdCounter.current++}`;
|
|
39809
39960
|
setEventLog((prev) => [...prev, { id, timestamp: Date.now(), icon, message, status }]);
|
|
39810
39961
|
}, []);
|
|
39811
39962
|
const handlePlay = React83.useCallback(() => {
|
|
39812
|
-
if (
|
|
39963
|
+
if (isPlaying || isSuccess) return;
|
|
39813
39964
|
if (playEvent) emit(`UI:${playEvent}`, {});
|
|
39814
|
-
|
|
39965
|
+
setIsPlaying(true);
|
|
39815
39966
|
setEventLog([]);
|
|
39816
39967
|
const allRules = [];
|
|
39817
39968
|
objects.forEach((obj) => {
|
|
@@ -39827,15 +39978,8 @@ function EventHandlerBoard({
|
|
|
39827
39978
|
let goalReached = false;
|
|
39828
39979
|
const processNext = () => {
|
|
39829
39980
|
if (eventQueue.length === 0 || stepIdx > 20) {
|
|
39830
|
-
|
|
39831
|
-
|
|
39832
|
-
if (completeEvent) {
|
|
39833
|
-
emit(`UI:${completeEvent}`, { success: true });
|
|
39834
|
-
}
|
|
39835
|
-
} else {
|
|
39836
|
-
setAttempts((prev) => prev + 1);
|
|
39837
|
-
setPlayState("fail");
|
|
39838
|
-
}
|
|
39981
|
+
setIsPlaying(false);
|
|
39982
|
+
if (goalReached && completeEvent) emit(`UI:${completeEvent}`, { success: true });
|
|
39839
39983
|
return;
|
|
39840
39984
|
}
|
|
39841
39985
|
const currentEvent = eventQueue.shift();
|
|
@@ -39866,21 +40010,19 @@ function EventHandlerBoard({
|
|
|
39866
40010
|
addLogEntry("\u{1F3AC}", t("eventHandler.simulationStarted", { events: triggers.join(", ") }), "active");
|
|
39867
40011
|
}
|
|
39868
40012
|
timerRef.current = setTimeout(processNext, stepDurationMs);
|
|
39869
|
-
}, [
|
|
40013
|
+
}, [isPlaying, isSuccess, objects, resolved, stepDurationMs, playEvent, completeEvent, emit, addLogEntry, t]);
|
|
39870
40014
|
const handleTryAgain = React83.useCallback(() => {
|
|
39871
40015
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39872
|
-
|
|
40016
|
+
setIsPlaying(false);
|
|
39873
40017
|
setEventLog([]);
|
|
39874
40018
|
}, []);
|
|
39875
40019
|
const handleReset = React83.useCallback(() => {
|
|
39876
40020
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39877
|
-
|
|
39878
|
-
setObjects([...resetObjects]);
|
|
39879
|
-
setPlayState("editing");
|
|
40021
|
+
setIsPlaying(false);
|
|
39880
40022
|
setEventLog([]);
|
|
39881
|
-
setSelectedObjectId(
|
|
39882
|
-
|
|
39883
|
-
}, [
|
|
40023
|
+
setSelectedObjectId(objects[0] ? objId(objects[0]) : null);
|
|
40024
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
40025
|
+
}, [objects, playAgainEvent, emit]);
|
|
39884
40026
|
if (!resolved) return null;
|
|
39885
40027
|
const objectViewers = objects.map((obj) => {
|
|
39886
40028
|
const states = objStates(obj);
|
|
@@ -39949,12 +40091,12 @@ function EventHandlerBoard({
|
|
|
39949
40091
|
{
|
|
39950
40092
|
object: selectedObject,
|
|
39951
40093
|
onRulesChange: handleRulesChange,
|
|
39952
|
-
disabled:
|
|
40094
|
+
disabled: isPlaying
|
|
39953
40095
|
}
|
|
39954
40096
|
),
|
|
39955
40097
|
eventLog.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(EventLog, { entries: eventLog }),
|
|
39956
|
-
|
|
39957
|
-
|
|
40098
|
+
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") }) }),
|
|
40099
|
+
!isPlaying && !isSuccess && attempts > 0 && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
39958
40100
|
/* @__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) }) }),
|
|
39959
40101
|
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: [
|
|
39960
40102
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
@@ -39962,12 +40104,12 @@ function EventHandlerBoard({
|
|
|
39962
40104
|
] }) })
|
|
39963
40105
|
] }),
|
|
39964
40106
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
|
|
39965
|
-
|
|
40107
|
+
!isPlaying && !isSuccess && attempts > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
39966
40108
|
Button,
|
|
39967
40109
|
{
|
|
39968
40110
|
variant: "primary",
|
|
39969
40111
|
onClick: handlePlay,
|
|
39970
|
-
disabled:
|
|
40112
|
+
disabled: isPlaying || isSuccess,
|
|
39971
40113
|
children: "\u25B6 " + t("game.play")
|
|
39972
40114
|
}
|
|
39973
40115
|
),
|
|
@@ -41152,6 +41294,1568 @@ var init_GameAudioToggle = __esm({
|
|
|
41152
41294
|
GameAudioToggle.displayName = "GameAudioToggle";
|
|
41153
41295
|
}
|
|
41154
41296
|
});
|
|
41297
|
+
function detectAssetRoot(modelUrl) {
|
|
41298
|
+
const idx = modelUrl.indexOf("/3d/");
|
|
41299
|
+
if (idx !== -1) {
|
|
41300
|
+
return modelUrl.substring(0, idx + 4);
|
|
41301
|
+
}
|
|
41302
|
+
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
41303
|
+
}
|
|
41304
|
+
function createGLTFLoaderForUrl(url) {
|
|
41305
|
+
const loader = new GLTFLoader_js.GLTFLoader();
|
|
41306
|
+
loader.setResourcePath(detectAssetRoot(url));
|
|
41307
|
+
return loader;
|
|
41308
|
+
}
|
|
41309
|
+
var AssetLoader;
|
|
41310
|
+
var init_AssetLoader = __esm({
|
|
41311
|
+
"components/game/molecules/three/loaders/AssetLoader.ts"() {
|
|
41312
|
+
AssetLoader = class {
|
|
41313
|
+
constructor() {
|
|
41314
|
+
__publicField(this, "objLoader");
|
|
41315
|
+
__publicField(this, "textureLoader");
|
|
41316
|
+
__publicField(this, "modelCache");
|
|
41317
|
+
__publicField(this, "textureCache");
|
|
41318
|
+
__publicField(this, "loadingPromises");
|
|
41319
|
+
this.objLoader = new OBJLoader_js.OBJLoader();
|
|
41320
|
+
this.textureLoader = new THREE__namespace.TextureLoader();
|
|
41321
|
+
this.modelCache = /* @__PURE__ */ new Map();
|
|
41322
|
+
this.textureCache = /* @__PURE__ */ new Map();
|
|
41323
|
+
this.loadingPromises = /* @__PURE__ */ new Map();
|
|
41324
|
+
}
|
|
41325
|
+
/**
|
|
41326
|
+
* Load a GLB/GLTF model
|
|
41327
|
+
* @param url - URL to the .glb or .gltf file
|
|
41328
|
+
* @returns Promise with loaded model scene and animations
|
|
41329
|
+
*/
|
|
41330
|
+
async loadModel(url) {
|
|
41331
|
+
if (this.modelCache.has(url)) {
|
|
41332
|
+
return this.modelCache.get(url);
|
|
41333
|
+
}
|
|
41334
|
+
if (this.loadingPromises.has(url)) {
|
|
41335
|
+
return this.loadingPromises.get(url);
|
|
41336
|
+
}
|
|
41337
|
+
const loader = createGLTFLoaderForUrl(url);
|
|
41338
|
+
const loadPromise = loader.loadAsync(url).then((gltf) => {
|
|
41339
|
+
const result = {
|
|
41340
|
+
scene: gltf.scene,
|
|
41341
|
+
animations: gltf.animations || []
|
|
41342
|
+
};
|
|
41343
|
+
this.modelCache.set(url, result);
|
|
41344
|
+
this.loadingPromises.delete(url);
|
|
41345
|
+
return result;
|
|
41346
|
+
}).catch((error) => {
|
|
41347
|
+
this.loadingPromises.delete(url);
|
|
41348
|
+
throw new Error(`Failed to load model ${url}: ${error.message}`);
|
|
41349
|
+
});
|
|
41350
|
+
this.loadingPromises.set(url, loadPromise);
|
|
41351
|
+
return loadPromise;
|
|
41352
|
+
}
|
|
41353
|
+
/**
|
|
41354
|
+
* Load an OBJ model (fallback for non-GLB assets)
|
|
41355
|
+
* @param url - URL to the .obj file
|
|
41356
|
+
* @returns Promise with loaded object group
|
|
41357
|
+
*/
|
|
41358
|
+
async loadOBJ(url) {
|
|
41359
|
+
if (this.modelCache.has(url)) {
|
|
41360
|
+
return this.modelCache.get(url).scene;
|
|
41361
|
+
}
|
|
41362
|
+
if (this.loadingPromises.has(url)) {
|
|
41363
|
+
const result = await this.loadingPromises.get(url);
|
|
41364
|
+
return result.scene;
|
|
41365
|
+
}
|
|
41366
|
+
const loadPromise = this.objLoader.loadAsync(url).then((group) => {
|
|
41367
|
+
const result = {
|
|
41368
|
+
scene: group,
|
|
41369
|
+
animations: []
|
|
41370
|
+
};
|
|
41371
|
+
this.modelCache.set(url, result);
|
|
41372
|
+
this.loadingPromises.delete(url);
|
|
41373
|
+
return result;
|
|
41374
|
+
}).catch((error) => {
|
|
41375
|
+
this.loadingPromises.delete(url);
|
|
41376
|
+
throw new Error(`Failed to load OBJ ${url}: ${error.message}`);
|
|
41377
|
+
});
|
|
41378
|
+
this.loadingPromises.set(url, loadPromise);
|
|
41379
|
+
return (await loadPromise).scene;
|
|
41380
|
+
}
|
|
41381
|
+
/**
|
|
41382
|
+
* Load a texture
|
|
41383
|
+
* @param url - URL to the texture image
|
|
41384
|
+
* @returns Promise with loaded texture
|
|
41385
|
+
*/
|
|
41386
|
+
async loadTexture(url) {
|
|
41387
|
+
if (this.textureCache.has(url)) {
|
|
41388
|
+
return this.textureCache.get(url);
|
|
41389
|
+
}
|
|
41390
|
+
if (this.loadingPromises.has(`texture:${url}`)) {
|
|
41391
|
+
return this.loadingPromises.get(`texture:${url}`);
|
|
41392
|
+
}
|
|
41393
|
+
const loadPromise = this.textureLoader.loadAsync(url).then((texture) => {
|
|
41394
|
+
texture.colorSpace = THREE__namespace.SRGBColorSpace;
|
|
41395
|
+
this.textureCache.set(url, texture);
|
|
41396
|
+
this.loadingPromises.delete(`texture:${url}`);
|
|
41397
|
+
return texture;
|
|
41398
|
+
}).catch((error) => {
|
|
41399
|
+
this.loadingPromises.delete(`texture:${url}`);
|
|
41400
|
+
throw new Error(`Failed to load texture ${url}: ${error.message}`);
|
|
41401
|
+
});
|
|
41402
|
+
this.loadingPromises.set(`texture:${url}`, loadPromise);
|
|
41403
|
+
return loadPromise;
|
|
41404
|
+
}
|
|
41405
|
+
/**
|
|
41406
|
+
* Preload multiple assets
|
|
41407
|
+
* @param urls - Array of asset URLs to preload
|
|
41408
|
+
* @returns Promise that resolves when all assets are loaded
|
|
41409
|
+
*/
|
|
41410
|
+
async preload(urls) {
|
|
41411
|
+
const promises = urls.map((url) => {
|
|
41412
|
+
if (url.endsWith(".glb") || url.endsWith(".gltf")) {
|
|
41413
|
+
return this.loadModel(url).catch(() => null);
|
|
41414
|
+
} else if (url.endsWith(".obj")) {
|
|
41415
|
+
return this.loadOBJ(url).catch(() => null);
|
|
41416
|
+
} else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
|
|
41417
|
+
return this.loadTexture(url).catch(() => null);
|
|
41418
|
+
}
|
|
41419
|
+
return Promise.resolve(null);
|
|
41420
|
+
});
|
|
41421
|
+
await Promise.all(promises);
|
|
41422
|
+
}
|
|
41423
|
+
/**
|
|
41424
|
+
* Check if a model is cached
|
|
41425
|
+
* @param url - Model URL
|
|
41426
|
+
*/
|
|
41427
|
+
hasModel(url) {
|
|
41428
|
+
return this.modelCache.has(url);
|
|
41429
|
+
}
|
|
41430
|
+
/**
|
|
41431
|
+
* Check if a texture is cached
|
|
41432
|
+
* @param url - Texture URL
|
|
41433
|
+
*/
|
|
41434
|
+
hasTexture(url) {
|
|
41435
|
+
return this.textureCache.has(url);
|
|
41436
|
+
}
|
|
41437
|
+
/**
|
|
41438
|
+
* Get cached model (throws if not cached)
|
|
41439
|
+
* @param url - Model URL
|
|
41440
|
+
*/
|
|
41441
|
+
getModel(url) {
|
|
41442
|
+
const model = this.modelCache.get(url);
|
|
41443
|
+
if (!model) {
|
|
41444
|
+
throw new Error(`Model ${url} not in cache`);
|
|
41445
|
+
}
|
|
41446
|
+
return model;
|
|
41447
|
+
}
|
|
41448
|
+
/**
|
|
41449
|
+
* Get cached texture (throws if not cached)
|
|
41450
|
+
* @param url - Texture URL
|
|
41451
|
+
*/
|
|
41452
|
+
getTexture(url) {
|
|
41453
|
+
const texture = this.textureCache.get(url);
|
|
41454
|
+
if (!texture) {
|
|
41455
|
+
throw new Error(`Texture ${url} not in cache`);
|
|
41456
|
+
}
|
|
41457
|
+
return texture;
|
|
41458
|
+
}
|
|
41459
|
+
/**
|
|
41460
|
+
* Clear all caches
|
|
41461
|
+
*/
|
|
41462
|
+
clearCache() {
|
|
41463
|
+
this.textureCache.forEach((texture) => {
|
|
41464
|
+
texture.dispose();
|
|
41465
|
+
});
|
|
41466
|
+
this.modelCache.forEach((model) => {
|
|
41467
|
+
model.scene.traverse((child) => {
|
|
41468
|
+
if (child instanceof THREE__namespace.Mesh) {
|
|
41469
|
+
child.geometry.dispose();
|
|
41470
|
+
if (Array.isArray(child.material)) {
|
|
41471
|
+
child.material.forEach((m) => m.dispose());
|
|
41472
|
+
} else {
|
|
41473
|
+
child.material.dispose();
|
|
41474
|
+
}
|
|
41475
|
+
}
|
|
41476
|
+
});
|
|
41477
|
+
});
|
|
41478
|
+
this.modelCache.clear();
|
|
41479
|
+
this.textureCache.clear();
|
|
41480
|
+
this.loadingPromises.clear();
|
|
41481
|
+
}
|
|
41482
|
+
/**
|
|
41483
|
+
* Get cache statistics
|
|
41484
|
+
*/
|
|
41485
|
+
getStats() {
|
|
41486
|
+
return {
|
|
41487
|
+
models: this.modelCache.size,
|
|
41488
|
+
textures: this.textureCache.size,
|
|
41489
|
+
loading: this.loadingPromises.size
|
|
41490
|
+
};
|
|
41491
|
+
}
|
|
41492
|
+
};
|
|
41493
|
+
new AssetLoader();
|
|
41494
|
+
}
|
|
41495
|
+
});
|
|
41496
|
+
function useAssetLoader(options = {}) {
|
|
41497
|
+
const { preloadUrls = [], loader: customLoader } = options;
|
|
41498
|
+
const loaderRef = React83.useRef(customLoader || new AssetLoader());
|
|
41499
|
+
const [state, setState] = React83.useState({
|
|
41500
|
+
isLoading: false,
|
|
41501
|
+
progress: 0,
|
|
41502
|
+
loaded: 0,
|
|
41503
|
+
total: 0,
|
|
41504
|
+
errors: []
|
|
41505
|
+
});
|
|
41506
|
+
React83.useEffect(() => {
|
|
41507
|
+
if (preloadUrls.length > 0) {
|
|
41508
|
+
preload(preloadUrls);
|
|
41509
|
+
}
|
|
41510
|
+
}, []);
|
|
41511
|
+
const updateProgress = React83.useCallback((loaded, total) => {
|
|
41512
|
+
setState((prev) => ({
|
|
41513
|
+
...prev,
|
|
41514
|
+
loaded,
|
|
41515
|
+
total,
|
|
41516
|
+
progress: total > 0 ? Math.round(loaded / total * 100) : 0
|
|
41517
|
+
}));
|
|
41518
|
+
}, []);
|
|
41519
|
+
const loadModel = React83.useCallback(
|
|
41520
|
+
async (url) => {
|
|
41521
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
41522
|
+
try {
|
|
41523
|
+
const model = await loaderRef.current.loadModel(url);
|
|
41524
|
+
setState((prev) => ({
|
|
41525
|
+
...prev,
|
|
41526
|
+
isLoading: false,
|
|
41527
|
+
loaded: prev.loaded + 1
|
|
41528
|
+
}));
|
|
41529
|
+
return model;
|
|
41530
|
+
} catch (error) {
|
|
41531
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
41532
|
+
setState((prev) => ({
|
|
41533
|
+
...prev,
|
|
41534
|
+
isLoading: false,
|
|
41535
|
+
errors: [...prev.errors, errorMsg]
|
|
41536
|
+
}));
|
|
41537
|
+
throw error;
|
|
41538
|
+
}
|
|
41539
|
+
},
|
|
41540
|
+
[]
|
|
41541
|
+
);
|
|
41542
|
+
const loadOBJ = React83.useCallback(
|
|
41543
|
+
async (url) => {
|
|
41544
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
41545
|
+
try {
|
|
41546
|
+
const model = await loaderRef.current.loadOBJ(url);
|
|
41547
|
+
setState((prev) => ({
|
|
41548
|
+
...prev,
|
|
41549
|
+
isLoading: false,
|
|
41550
|
+
loaded: prev.loaded + 1
|
|
41551
|
+
}));
|
|
41552
|
+
return model;
|
|
41553
|
+
} catch (error) {
|
|
41554
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
41555
|
+
setState((prev) => ({
|
|
41556
|
+
...prev,
|
|
41557
|
+
isLoading: false,
|
|
41558
|
+
errors: [...prev.errors, errorMsg]
|
|
41559
|
+
}));
|
|
41560
|
+
throw error;
|
|
41561
|
+
}
|
|
41562
|
+
},
|
|
41563
|
+
[]
|
|
41564
|
+
);
|
|
41565
|
+
const loadTexture = React83.useCallback(
|
|
41566
|
+
async (url) => {
|
|
41567
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
41568
|
+
try {
|
|
41569
|
+
const texture = await loaderRef.current.loadTexture(url);
|
|
41570
|
+
setState((prev) => ({
|
|
41571
|
+
...prev,
|
|
41572
|
+
isLoading: false,
|
|
41573
|
+
loaded: prev.loaded + 1
|
|
41574
|
+
}));
|
|
41575
|
+
return texture;
|
|
41576
|
+
} catch (error) {
|
|
41577
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
41578
|
+
setState((prev) => ({
|
|
41579
|
+
...prev,
|
|
41580
|
+
isLoading: false,
|
|
41581
|
+
errors: [...prev.errors, errorMsg]
|
|
41582
|
+
}));
|
|
41583
|
+
throw error;
|
|
41584
|
+
}
|
|
41585
|
+
},
|
|
41586
|
+
[]
|
|
41587
|
+
);
|
|
41588
|
+
const preload = React83.useCallback(
|
|
41589
|
+
async (urls) => {
|
|
41590
|
+
setState((prev) => ({
|
|
41591
|
+
...prev,
|
|
41592
|
+
isLoading: true,
|
|
41593
|
+
total: urls.length,
|
|
41594
|
+
loaded: 0,
|
|
41595
|
+
errors: []
|
|
41596
|
+
}));
|
|
41597
|
+
let completed = 0;
|
|
41598
|
+
const errors = [];
|
|
41599
|
+
await Promise.all(
|
|
41600
|
+
urls.map(async (url) => {
|
|
41601
|
+
try {
|
|
41602
|
+
if (url.endsWith(".glb") || url.endsWith(".gltf")) {
|
|
41603
|
+
await loaderRef.current.loadModel(url);
|
|
41604
|
+
} else if (url.endsWith(".obj")) {
|
|
41605
|
+
await loaderRef.current.loadOBJ(url);
|
|
41606
|
+
} else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
|
|
41607
|
+
await loaderRef.current.loadTexture(url);
|
|
41608
|
+
}
|
|
41609
|
+
completed++;
|
|
41610
|
+
updateProgress(completed, urls.length);
|
|
41611
|
+
} catch (error) {
|
|
41612
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
41613
|
+
errors.push(`${url}: ${errorMsg}`);
|
|
41614
|
+
completed++;
|
|
41615
|
+
updateProgress(completed, urls.length);
|
|
41616
|
+
}
|
|
41617
|
+
})
|
|
41618
|
+
);
|
|
41619
|
+
setState((prev) => ({
|
|
41620
|
+
...prev,
|
|
41621
|
+
isLoading: false,
|
|
41622
|
+
errors
|
|
41623
|
+
}));
|
|
41624
|
+
},
|
|
41625
|
+
[updateProgress]
|
|
41626
|
+
);
|
|
41627
|
+
const hasModel = React83.useCallback((url) => {
|
|
41628
|
+
return loaderRef.current.hasModel(url);
|
|
41629
|
+
}, []);
|
|
41630
|
+
const hasTexture = React83.useCallback((url) => {
|
|
41631
|
+
return loaderRef.current.hasTexture(url);
|
|
41632
|
+
}, []);
|
|
41633
|
+
const getModel = React83.useCallback((url) => {
|
|
41634
|
+
try {
|
|
41635
|
+
return loaderRef.current.getModel(url);
|
|
41636
|
+
} catch {
|
|
41637
|
+
return void 0;
|
|
41638
|
+
}
|
|
41639
|
+
}, []);
|
|
41640
|
+
const getTexture = React83.useCallback((url) => {
|
|
41641
|
+
try {
|
|
41642
|
+
return loaderRef.current.getTexture(url);
|
|
41643
|
+
} catch {
|
|
41644
|
+
return void 0;
|
|
41645
|
+
}
|
|
41646
|
+
}, []);
|
|
41647
|
+
const clearCache = React83.useCallback(() => {
|
|
41648
|
+
loaderRef.current.clearCache();
|
|
41649
|
+
setState({
|
|
41650
|
+
isLoading: false,
|
|
41651
|
+
progress: 0,
|
|
41652
|
+
loaded: 0,
|
|
41653
|
+
total: 0,
|
|
41654
|
+
errors: []
|
|
41655
|
+
});
|
|
41656
|
+
}, []);
|
|
41657
|
+
return {
|
|
41658
|
+
...state,
|
|
41659
|
+
loadModel,
|
|
41660
|
+
loadOBJ,
|
|
41661
|
+
loadTexture,
|
|
41662
|
+
preload,
|
|
41663
|
+
hasModel,
|
|
41664
|
+
hasTexture,
|
|
41665
|
+
getModel,
|
|
41666
|
+
getTexture,
|
|
41667
|
+
clearCache
|
|
41668
|
+
};
|
|
41669
|
+
}
|
|
41670
|
+
var init_useAssetLoader = __esm({
|
|
41671
|
+
"components/game/molecules/three/hooks/useAssetLoader.ts"() {
|
|
41672
|
+
"use client";
|
|
41673
|
+
init_AssetLoader();
|
|
41674
|
+
}
|
|
41675
|
+
});
|
|
41676
|
+
function useGameCanvas3DEvents(options) {
|
|
41677
|
+
const {
|
|
41678
|
+
tileClickEvent,
|
|
41679
|
+
unitClickEvent,
|
|
41680
|
+
featureClickEvent,
|
|
41681
|
+
canvasClickEvent,
|
|
41682
|
+
tileHoverEvent,
|
|
41683
|
+
tileLeaveEvent,
|
|
41684
|
+
unitAnimationEvent,
|
|
41685
|
+
cameraChangeEvent,
|
|
41686
|
+
onTileClick,
|
|
41687
|
+
onUnitClick,
|
|
41688
|
+
onFeatureClick,
|
|
41689
|
+
onCanvasClick,
|
|
41690
|
+
onTileHover,
|
|
41691
|
+
onUnitAnimation
|
|
41692
|
+
} = options;
|
|
41693
|
+
const emit = useEmitEvent();
|
|
41694
|
+
const optionsRef = React83.useRef(options);
|
|
41695
|
+
optionsRef.current = options;
|
|
41696
|
+
const handleTileClick = React83.useCallback(
|
|
41697
|
+
(tile, event) => {
|
|
41698
|
+
if (tileClickEvent) {
|
|
41699
|
+
emit(tileClickEvent, {
|
|
41700
|
+
tileId: tile.id,
|
|
41701
|
+
x: tile.x,
|
|
41702
|
+
z: tile.z ?? tile.y ?? 0,
|
|
41703
|
+
type: tile.type,
|
|
41704
|
+
terrain: tile.terrain,
|
|
41705
|
+
elevation: tile.elevation
|
|
41706
|
+
});
|
|
41707
|
+
}
|
|
41708
|
+
optionsRef.current.onTileClick?.(tile, event);
|
|
41709
|
+
},
|
|
41710
|
+
[tileClickEvent, emit]
|
|
41711
|
+
);
|
|
41712
|
+
const handleUnitClick = React83.useCallback(
|
|
41713
|
+
(unit, event) => {
|
|
41714
|
+
if (unitClickEvent) {
|
|
41715
|
+
emit(unitClickEvent, {
|
|
41716
|
+
unitId: unit.id,
|
|
41717
|
+
x: unit.x,
|
|
41718
|
+
z: unit.z ?? unit.y ?? 0,
|
|
41719
|
+
unitType: unit.unitType,
|
|
41720
|
+
name: unit.name,
|
|
41721
|
+
team: unit.team,
|
|
41722
|
+
faction: unit.faction,
|
|
41723
|
+
health: unit.health,
|
|
41724
|
+
maxHealth: unit.maxHealth
|
|
41725
|
+
});
|
|
41726
|
+
}
|
|
41727
|
+
optionsRef.current.onUnitClick?.(unit, event);
|
|
41728
|
+
},
|
|
41729
|
+
[unitClickEvent, emit]
|
|
41730
|
+
);
|
|
41731
|
+
const handleFeatureClick = React83.useCallback(
|
|
41732
|
+
(feature, event) => {
|
|
41733
|
+
if (featureClickEvent) {
|
|
41734
|
+
emit(featureClickEvent, {
|
|
41735
|
+
featureId: feature.id,
|
|
41736
|
+
x: feature.x,
|
|
41737
|
+
z: feature.z ?? feature.y ?? 0,
|
|
41738
|
+
type: feature.type,
|
|
41739
|
+
elevation: feature.elevation
|
|
41740
|
+
});
|
|
41741
|
+
}
|
|
41742
|
+
optionsRef.current.onFeatureClick?.(feature, event);
|
|
41743
|
+
},
|
|
41744
|
+
[featureClickEvent, emit]
|
|
41745
|
+
);
|
|
41746
|
+
const handleCanvasClick = React83.useCallback(
|
|
41747
|
+
(event) => {
|
|
41748
|
+
if (canvasClickEvent) {
|
|
41749
|
+
emit(canvasClickEvent, {
|
|
41750
|
+
clientX: event.clientX,
|
|
41751
|
+
clientY: event.clientY,
|
|
41752
|
+
button: event.button
|
|
41753
|
+
});
|
|
41754
|
+
}
|
|
41755
|
+
optionsRef.current.onCanvasClick?.(event);
|
|
41756
|
+
},
|
|
41757
|
+
[canvasClickEvent, emit]
|
|
41758
|
+
);
|
|
41759
|
+
const handleTileHover = React83.useCallback(
|
|
41760
|
+
(tile, event) => {
|
|
41761
|
+
if (tile) {
|
|
41762
|
+
if (tileHoverEvent) {
|
|
41763
|
+
emit(tileHoverEvent, {
|
|
41764
|
+
tileId: tile.id,
|
|
41765
|
+
x: tile.x,
|
|
41766
|
+
z: tile.z ?? tile.y ?? 0,
|
|
41767
|
+
type: tile.type
|
|
41768
|
+
});
|
|
41769
|
+
}
|
|
41770
|
+
} else {
|
|
41771
|
+
if (tileLeaveEvent) {
|
|
41772
|
+
emit(tileLeaveEvent, {});
|
|
41773
|
+
}
|
|
41774
|
+
}
|
|
41775
|
+
optionsRef.current.onTileHover?.(tile, event);
|
|
41776
|
+
},
|
|
41777
|
+
[tileHoverEvent, tileLeaveEvent, emit]
|
|
41778
|
+
);
|
|
41779
|
+
const handleUnitAnimation = React83.useCallback(
|
|
41780
|
+
(unitId, state) => {
|
|
41781
|
+
if (unitAnimationEvent) {
|
|
41782
|
+
emit(unitAnimationEvent, {
|
|
41783
|
+
unitId,
|
|
41784
|
+
state,
|
|
41785
|
+
timestamp: Date.now()
|
|
41786
|
+
});
|
|
41787
|
+
}
|
|
41788
|
+
optionsRef.current.onUnitAnimation?.(unitId, state);
|
|
41789
|
+
},
|
|
41790
|
+
[unitAnimationEvent, emit]
|
|
41791
|
+
);
|
|
41792
|
+
const handleCameraChange = React83.useCallback(
|
|
41793
|
+
(position) => {
|
|
41794
|
+
if (cameraChangeEvent) {
|
|
41795
|
+
emit(cameraChangeEvent, {
|
|
41796
|
+
position,
|
|
41797
|
+
timestamp: Date.now()
|
|
41798
|
+
});
|
|
41799
|
+
}
|
|
41800
|
+
},
|
|
41801
|
+
[cameraChangeEvent, emit]
|
|
41802
|
+
);
|
|
41803
|
+
return {
|
|
41804
|
+
handleTileClick,
|
|
41805
|
+
handleUnitClick,
|
|
41806
|
+
handleFeatureClick,
|
|
41807
|
+
handleCanvasClick,
|
|
41808
|
+
handleTileHover,
|
|
41809
|
+
handleUnitAnimation,
|
|
41810
|
+
handleCameraChange
|
|
41811
|
+
};
|
|
41812
|
+
}
|
|
41813
|
+
var init_useGameCanvas3DEvents = __esm({
|
|
41814
|
+
"components/game/molecules/three/hooks/useGameCanvas3DEvents.ts"() {
|
|
41815
|
+
"use client";
|
|
41816
|
+
init_useEventBus();
|
|
41817
|
+
}
|
|
41818
|
+
});
|
|
41819
|
+
|
|
41820
|
+
// components/game/molecules/three/components/Canvas3DLoadingState.css
|
|
41821
|
+
var init_Canvas3DLoadingState = __esm({
|
|
41822
|
+
"components/game/molecules/three/components/Canvas3DLoadingState.css"() {
|
|
41823
|
+
}
|
|
41824
|
+
});
|
|
41825
|
+
function Canvas3DLoadingState({
|
|
41826
|
+
progress = 0,
|
|
41827
|
+
loaded = 0,
|
|
41828
|
+
total = 0,
|
|
41829
|
+
message = "Loading 3D Scene...",
|
|
41830
|
+
details,
|
|
41831
|
+
showSpinner = true,
|
|
41832
|
+
className
|
|
41833
|
+
}) {
|
|
41834
|
+
const clampedProgress = Math.max(0, Math.min(100, progress));
|
|
41835
|
+
const hasProgress = total > 0;
|
|
41836
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `canvas-3d-loading ${className || ""}`, children: [
|
|
41837
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__content", children: [
|
|
41838
|
+
showSpinner && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__spinner", children: [
|
|
41839
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "spinner__ring" }),
|
|
41840
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "spinner__ring spinner__ring--secondary" })
|
|
41841
|
+
] }),
|
|
41842
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__message", children: message }),
|
|
41843
|
+
details && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__details", children: details }),
|
|
41844
|
+
hasProgress && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__progress", children: [
|
|
41845
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "progress__bar", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
41846
|
+
"div",
|
|
41847
|
+
{
|
|
41848
|
+
className: "progress__fill",
|
|
41849
|
+
style: { width: `${clampedProgress}%` }
|
|
41850
|
+
}
|
|
41851
|
+
) }),
|
|
41852
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "progress__text", children: [
|
|
41853
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "progress__percentage", children: [
|
|
41854
|
+
clampedProgress,
|
|
41855
|
+
"%"
|
|
41856
|
+
] }),
|
|
41857
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "progress__count", children: [
|
|
41858
|
+
"(",
|
|
41859
|
+
loaded,
|
|
41860
|
+
"/",
|
|
41861
|
+
total,
|
|
41862
|
+
")"
|
|
41863
|
+
] })
|
|
41864
|
+
] })
|
|
41865
|
+
] })
|
|
41866
|
+
] }),
|
|
41867
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__background", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg__grid" }) })
|
|
41868
|
+
] });
|
|
41869
|
+
}
|
|
41870
|
+
var init_Canvas3DLoadingState2 = __esm({
|
|
41871
|
+
"components/game/molecules/three/components/Canvas3DLoadingState.tsx"() {
|
|
41872
|
+
init_Canvas3DLoadingState();
|
|
41873
|
+
}
|
|
41874
|
+
});
|
|
41875
|
+
|
|
41876
|
+
// components/game/molecules/three/components/Canvas3DErrorBoundary.css
|
|
41877
|
+
var init_Canvas3DErrorBoundary = __esm({
|
|
41878
|
+
"components/game/molecules/three/components/Canvas3DErrorBoundary.css"() {
|
|
41879
|
+
}
|
|
41880
|
+
});
|
|
41881
|
+
var log11, Canvas3DErrorBoundary;
|
|
41882
|
+
var init_Canvas3DErrorBoundary2 = __esm({
|
|
41883
|
+
"components/game/molecules/three/components/Canvas3DErrorBoundary.tsx"() {
|
|
41884
|
+
init_Canvas3DErrorBoundary();
|
|
41885
|
+
log11 = logger.createLogger("almadar:ui:game:canvas3d:error-boundary");
|
|
41886
|
+
Canvas3DErrorBoundary = class extends React83.Component {
|
|
41887
|
+
constructor(props) {
|
|
41888
|
+
super(props);
|
|
41889
|
+
__publicField(this, "handleReset", () => {
|
|
41890
|
+
this.setState({
|
|
41891
|
+
hasError: false,
|
|
41892
|
+
error: null,
|
|
41893
|
+
errorInfo: null
|
|
41894
|
+
});
|
|
41895
|
+
this.props.onReset?.();
|
|
41896
|
+
});
|
|
41897
|
+
this.state = {
|
|
41898
|
+
hasError: false,
|
|
41899
|
+
error: null,
|
|
41900
|
+
errorInfo: null
|
|
41901
|
+
};
|
|
41902
|
+
}
|
|
41903
|
+
static getDerivedStateFromError(error) {
|
|
41904
|
+
return {
|
|
41905
|
+
hasError: true,
|
|
41906
|
+
error,
|
|
41907
|
+
errorInfo: null
|
|
41908
|
+
};
|
|
41909
|
+
}
|
|
41910
|
+
componentDidCatch(error, errorInfo) {
|
|
41911
|
+
this.setState({ errorInfo });
|
|
41912
|
+
this.props.onError?.(error, errorInfo);
|
|
41913
|
+
log11.error("Error caught", { error });
|
|
41914
|
+
log11.error("Component stack", { componentStack: errorInfo.componentStack ?? "<none>" });
|
|
41915
|
+
}
|
|
41916
|
+
render() {
|
|
41917
|
+
if (this.state.hasError) {
|
|
41918
|
+
if (this.props.fallback) {
|
|
41919
|
+
return this.props.fallback;
|
|
41920
|
+
}
|
|
41921
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-error", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-error__content", children: [
|
|
41922
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-error__icon", children: "\u26A0\uFE0F" }),
|
|
41923
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "canvas-3d-error__title", children: "3D Scene Error" }),
|
|
41924
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "canvas-3d-error__message", children: "Something went wrong while rendering the 3D scene." }),
|
|
41925
|
+
this.state.error && /* @__PURE__ */ jsxRuntime.jsxs("details", { className: "canvas-3d-error__details", children: [
|
|
41926
|
+
/* @__PURE__ */ jsxRuntime.jsx("summary", { children: "Error Details" }),
|
|
41927
|
+
/* @__PURE__ */ jsxRuntime.jsxs("pre", { className: "error__stack", children: [
|
|
41928
|
+
this.state.error.message,
|
|
41929
|
+
"\n",
|
|
41930
|
+
this.state.error.stack
|
|
41931
|
+
] }),
|
|
41932
|
+
this.state.errorInfo && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "error__component-stack", children: this.state.errorInfo.componentStack })
|
|
41933
|
+
] }),
|
|
41934
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-error__actions", children: [
|
|
41935
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
41936
|
+
"button",
|
|
41937
|
+
{
|
|
41938
|
+
className: "error__button error__button--primary",
|
|
41939
|
+
onClick: this.handleReset,
|
|
41940
|
+
children: "Try Again"
|
|
41941
|
+
}
|
|
41942
|
+
),
|
|
41943
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
41944
|
+
"button",
|
|
41945
|
+
{
|
|
41946
|
+
className: "error__button error__button--secondary",
|
|
41947
|
+
onClick: () => window.location.reload(),
|
|
41948
|
+
children: "Reload Page"
|
|
41949
|
+
}
|
|
41950
|
+
)
|
|
41951
|
+
] })
|
|
41952
|
+
] }) });
|
|
41953
|
+
}
|
|
41954
|
+
return this.props.children;
|
|
41955
|
+
}
|
|
41956
|
+
};
|
|
41957
|
+
}
|
|
41958
|
+
});
|
|
41959
|
+
function detectAssetRoot2(modelUrl) {
|
|
41960
|
+
const idx = modelUrl.indexOf("/3d/");
|
|
41961
|
+
if (idx !== -1) {
|
|
41962
|
+
return modelUrl.substring(0, idx + 4);
|
|
41963
|
+
}
|
|
41964
|
+
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
41965
|
+
}
|
|
41966
|
+
function useGLTFModel(url, resourceBasePath) {
|
|
41967
|
+
const [state, setState] = React83.useState({
|
|
41968
|
+
model: null,
|
|
41969
|
+
isLoading: false,
|
|
41970
|
+
error: null
|
|
41971
|
+
});
|
|
41972
|
+
React83.useEffect(() => {
|
|
41973
|
+
if (!url) {
|
|
41974
|
+
setState({ model: null, isLoading: false, error: null });
|
|
41975
|
+
return;
|
|
41976
|
+
}
|
|
41977
|
+
log12.debug("Loading", { url });
|
|
41978
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
41979
|
+
const assetRoot = resourceBasePath || detectAssetRoot2(url);
|
|
41980
|
+
const loader = new GLTFLoader.GLTFLoader();
|
|
41981
|
+
loader.setResourcePath(assetRoot);
|
|
41982
|
+
loader.load(
|
|
41983
|
+
url,
|
|
41984
|
+
(gltf) => {
|
|
41985
|
+
log12.debug("Loaded", { url });
|
|
41986
|
+
setState({
|
|
41987
|
+
model: gltf.scene,
|
|
41988
|
+
isLoading: false,
|
|
41989
|
+
error: null
|
|
41990
|
+
});
|
|
41991
|
+
},
|
|
41992
|
+
void 0,
|
|
41993
|
+
(err) => {
|
|
41994
|
+
log12.warn("Failed", { url, error: err instanceof Error ? err : String(err) });
|
|
41995
|
+
setState({
|
|
41996
|
+
model: null,
|
|
41997
|
+
isLoading: false,
|
|
41998
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
41999
|
+
});
|
|
42000
|
+
}
|
|
42001
|
+
);
|
|
42002
|
+
}, [url, resourceBasePath]);
|
|
42003
|
+
return state;
|
|
42004
|
+
}
|
|
42005
|
+
function ModelLoader({
|
|
42006
|
+
url,
|
|
42007
|
+
position = [0, 0, 0],
|
|
42008
|
+
scale = 1,
|
|
42009
|
+
rotation = [0, 0, 0],
|
|
42010
|
+
isSelected = false,
|
|
42011
|
+
isHovered = false,
|
|
42012
|
+
onClick,
|
|
42013
|
+
onHover,
|
|
42014
|
+
fallbackGeometry = "box",
|
|
42015
|
+
castShadow = true,
|
|
42016
|
+
receiveShadow = true,
|
|
42017
|
+
resourceBasePath
|
|
42018
|
+
}) {
|
|
42019
|
+
const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
|
|
42020
|
+
const model = React83.useMemo(() => {
|
|
42021
|
+
if (!loadedModel) return null;
|
|
42022
|
+
const cloned = loadedModel.clone();
|
|
42023
|
+
cloned.traverse((child) => {
|
|
42024
|
+
if (child instanceof THREE__namespace.Mesh) {
|
|
42025
|
+
child.castShadow = castShadow;
|
|
42026
|
+
child.receiveShadow = receiveShadow;
|
|
42027
|
+
}
|
|
42028
|
+
});
|
|
42029
|
+
return cloned;
|
|
42030
|
+
}, [loadedModel, castShadow, receiveShadow]);
|
|
42031
|
+
const scaleArray = React83.useMemo(() => {
|
|
42032
|
+
if (typeof scale === "number") {
|
|
42033
|
+
return [scale, scale, scale];
|
|
42034
|
+
}
|
|
42035
|
+
return scale;
|
|
42036
|
+
}, [scale]);
|
|
42037
|
+
const rotationRad = React83.useMemo(() => {
|
|
42038
|
+
return [
|
|
42039
|
+
rotation[0] * Math.PI / 180,
|
|
42040
|
+
rotation[1] * Math.PI / 180,
|
|
42041
|
+
rotation[2] * Math.PI / 180
|
|
42042
|
+
];
|
|
42043
|
+
}, [rotation]);
|
|
42044
|
+
if (isLoading) {
|
|
42045
|
+
return /* @__PURE__ */ jsxRuntime.jsx("group", { position, children: /* @__PURE__ */ jsxRuntime.jsxs("mesh", { rotation: [Math.PI / 2, 0, 0], children: [
|
|
42046
|
+
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.3, 0.35, 16] }),
|
|
42047
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#4a90d9", transparent: true, opacity: 0.8 })
|
|
42048
|
+
] }) });
|
|
42049
|
+
}
|
|
42050
|
+
if (error || !model) {
|
|
42051
|
+
if (fallbackGeometry === "none") {
|
|
42052
|
+
return /* @__PURE__ */ jsxRuntime.jsx("group", { position });
|
|
42053
|
+
}
|
|
42054
|
+
const fallbackProps = {
|
|
42055
|
+
onClick,
|
|
42056
|
+
onPointerOver: () => onHover?.(true),
|
|
42057
|
+
onPointerOut: () => onHover?.(false)
|
|
42058
|
+
};
|
|
42059
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("group", { position, children: [
|
|
42060
|
+
(isSelected || isHovered) && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
42061
|
+
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
|
|
42062
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42063
|
+
"meshBasicMaterial",
|
|
42064
|
+
{
|
|
42065
|
+
color: isSelected ? 16755200 : 16777215,
|
|
42066
|
+
transparent: true,
|
|
42067
|
+
opacity: 0.5
|
|
42068
|
+
}
|
|
42069
|
+
)
|
|
42070
|
+
] }),
|
|
42071
|
+
fallbackGeometry === "box" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
42072
|
+
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.8, 0.8, 0.8] }),
|
|
42073
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
42074
|
+
] }),
|
|
42075
|
+
fallbackGeometry === "sphere" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
42076
|
+
/* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [0.4, 16, 16] }),
|
|
42077
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
42078
|
+
] }),
|
|
42079
|
+
fallbackGeometry === "cylinder" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
42080
|
+
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.3, 0.3, 0.8, 16] }),
|
|
42081
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
42082
|
+
] })
|
|
42083
|
+
] });
|
|
42084
|
+
}
|
|
42085
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
42086
|
+
"group",
|
|
42087
|
+
{
|
|
42088
|
+
position,
|
|
42089
|
+
rotation: rotationRad,
|
|
42090
|
+
onClick,
|
|
42091
|
+
onPointerOver: () => onHover?.(true),
|
|
42092
|
+
onPointerOut: () => onHover?.(false),
|
|
42093
|
+
children: [
|
|
42094
|
+
(isSelected || isHovered) && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
42095
|
+
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
|
|
42096
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42097
|
+
"meshBasicMaterial",
|
|
42098
|
+
{
|
|
42099
|
+
color: isSelected ? 16755200 : 16777215,
|
|
42100
|
+
transparent: true,
|
|
42101
|
+
opacity: 0.5
|
|
42102
|
+
}
|
|
42103
|
+
)
|
|
42104
|
+
] }),
|
|
42105
|
+
/* @__PURE__ */ jsxRuntime.jsx("primitive", { object: model, scale: scaleArray })
|
|
42106
|
+
]
|
|
42107
|
+
}
|
|
42108
|
+
);
|
|
42109
|
+
}
|
|
42110
|
+
var log12;
|
|
42111
|
+
var init_ModelLoader = __esm({
|
|
42112
|
+
"components/game/molecules/three/components/ModelLoader.tsx"() {
|
|
42113
|
+
"use client";
|
|
42114
|
+
log12 = logger.createLogger("almadar:ui:game:model-loader");
|
|
42115
|
+
}
|
|
42116
|
+
});
|
|
42117
|
+
|
|
42118
|
+
// components/game/molecules/GameCanvas3D.css
|
|
42119
|
+
var init_GameCanvas3D = __esm({
|
|
42120
|
+
"components/game/molecules/GameCanvas3D.css"() {
|
|
42121
|
+
}
|
|
42122
|
+
});
|
|
42123
|
+
function CameraController({
|
|
42124
|
+
onCameraChange
|
|
42125
|
+
}) {
|
|
42126
|
+
const { camera } = fiber.useThree();
|
|
42127
|
+
React83.useEffect(() => {
|
|
42128
|
+
if (onCameraChange) {
|
|
42129
|
+
onCameraChange({
|
|
42130
|
+
x: camera.position.x,
|
|
42131
|
+
y: camera.position.y,
|
|
42132
|
+
z: camera.position.z
|
|
42133
|
+
});
|
|
42134
|
+
}
|
|
42135
|
+
}, [camera.position, onCameraChange]);
|
|
42136
|
+
return null;
|
|
42137
|
+
}
|
|
42138
|
+
var DEFAULT_GRID_CONFIG, GameCanvas3D;
|
|
42139
|
+
var init_GameCanvas3D2 = __esm({
|
|
42140
|
+
"components/game/molecules/GameCanvas3D.tsx"() {
|
|
42141
|
+
"use client";
|
|
42142
|
+
init_AssetLoader();
|
|
42143
|
+
init_useAssetLoader();
|
|
42144
|
+
init_useGameCanvas3DEvents();
|
|
42145
|
+
init_Canvas3DLoadingState2();
|
|
42146
|
+
init_Canvas3DErrorBoundary2();
|
|
42147
|
+
init_ModelLoader();
|
|
42148
|
+
init_cn();
|
|
42149
|
+
init_GameCanvas3D();
|
|
42150
|
+
DEFAULT_GRID_CONFIG = {
|
|
42151
|
+
cellSize: 1,
|
|
42152
|
+
offsetX: 0,
|
|
42153
|
+
offsetZ: 0
|
|
42154
|
+
};
|
|
42155
|
+
GameCanvas3D = React83.forwardRef(
|
|
42156
|
+
({
|
|
42157
|
+
tiles = [],
|
|
42158
|
+
units = [],
|
|
42159
|
+
features = [],
|
|
42160
|
+
events: events2 = [],
|
|
42161
|
+
orientation = "standard",
|
|
42162
|
+
cameraMode = "isometric",
|
|
42163
|
+
showGrid = true,
|
|
42164
|
+
showCoordinates = false,
|
|
42165
|
+
showTileInfo = false,
|
|
42166
|
+
overlay = "default",
|
|
42167
|
+
shadows = true,
|
|
42168
|
+
backgroundColor = "#1a1a2e",
|
|
42169
|
+
onTileClick,
|
|
42170
|
+
onUnitClick,
|
|
42171
|
+
onFeatureClick,
|
|
42172
|
+
onCanvasClick,
|
|
42173
|
+
onTileHover,
|
|
42174
|
+
onUnitAnimation,
|
|
42175
|
+
assetLoader: customAssetLoader,
|
|
42176
|
+
tileRenderer: CustomTileRenderer,
|
|
42177
|
+
unitRenderer: CustomUnitRenderer,
|
|
42178
|
+
featureRenderer: CustomFeatureRenderer,
|
|
42179
|
+
className,
|
|
42180
|
+
isLoading: externalLoading,
|
|
42181
|
+
error: externalError,
|
|
42182
|
+
entity,
|
|
42183
|
+
preloadAssets = [],
|
|
42184
|
+
tileClickEvent,
|
|
42185
|
+
unitClickEvent,
|
|
42186
|
+
featureClickEvent,
|
|
42187
|
+
canvasClickEvent,
|
|
42188
|
+
tileHoverEvent,
|
|
42189
|
+
tileLeaveEvent,
|
|
42190
|
+
unitAnimationEvent,
|
|
42191
|
+
cameraChangeEvent,
|
|
42192
|
+
loadingMessage = "Loading 3D Scene...",
|
|
42193
|
+
useInstancing = true,
|
|
42194
|
+
validMoves = [],
|
|
42195
|
+
attackTargets = [],
|
|
42196
|
+
selectedTileIds = [],
|
|
42197
|
+
selectedUnitId = null,
|
|
42198
|
+
children
|
|
42199
|
+
}, ref) => {
|
|
42200
|
+
const containerRef = React83.useRef(null);
|
|
42201
|
+
const controlsRef = React83.useRef(null);
|
|
42202
|
+
const [hoveredTile, setHoveredTile] = React83.useState(null);
|
|
42203
|
+
const [internalError, setInternalError] = React83.useState(null);
|
|
42204
|
+
const { isLoading: assetsLoading, progress, loaded, total } = useAssetLoader({
|
|
42205
|
+
preloadUrls: preloadAssets,
|
|
42206
|
+
loader: customAssetLoader
|
|
42207
|
+
});
|
|
42208
|
+
const eventHandlers = useGameCanvas3DEvents({
|
|
42209
|
+
tileClickEvent,
|
|
42210
|
+
unitClickEvent,
|
|
42211
|
+
featureClickEvent,
|
|
42212
|
+
canvasClickEvent,
|
|
42213
|
+
tileHoverEvent,
|
|
42214
|
+
tileLeaveEvent,
|
|
42215
|
+
unitAnimationEvent,
|
|
42216
|
+
cameraChangeEvent,
|
|
42217
|
+
onTileClick,
|
|
42218
|
+
onUnitClick,
|
|
42219
|
+
onFeatureClick,
|
|
42220
|
+
onCanvasClick,
|
|
42221
|
+
onTileHover,
|
|
42222
|
+
onUnitAnimation
|
|
42223
|
+
});
|
|
42224
|
+
const gridBounds = React83.useMemo(() => {
|
|
42225
|
+
if (tiles.length === 0) {
|
|
42226
|
+
return { minX: 0, maxX: 10, minZ: 0, maxZ: 10 };
|
|
42227
|
+
}
|
|
42228
|
+
const xs = tiles.map((t) => t.x);
|
|
42229
|
+
const zs = tiles.map((t) => t.z || t.y || 0);
|
|
42230
|
+
return {
|
|
42231
|
+
minX: Math.min(...xs),
|
|
42232
|
+
maxX: Math.max(...xs),
|
|
42233
|
+
minZ: Math.min(...zs),
|
|
42234
|
+
maxZ: Math.max(...zs)
|
|
42235
|
+
};
|
|
42236
|
+
}, [tiles]);
|
|
42237
|
+
const cameraTarget = React83.useMemo(() => {
|
|
42238
|
+
return [
|
|
42239
|
+
(gridBounds.minX + gridBounds.maxX) / 2,
|
|
42240
|
+
0,
|
|
42241
|
+
(gridBounds.minZ + gridBounds.maxZ) / 2
|
|
42242
|
+
];
|
|
42243
|
+
}, [gridBounds]);
|
|
42244
|
+
const gridConfig = React83.useMemo(
|
|
42245
|
+
() => ({
|
|
42246
|
+
...DEFAULT_GRID_CONFIG,
|
|
42247
|
+
offsetX: -(gridBounds.maxX - gridBounds.minX) / 2,
|
|
42248
|
+
offsetZ: -(gridBounds.maxZ - gridBounds.minZ) / 2
|
|
42249
|
+
}),
|
|
42250
|
+
[gridBounds]
|
|
42251
|
+
);
|
|
42252
|
+
const gridToWorld = React83.useCallback(
|
|
42253
|
+
(x, z, y = 0) => {
|
|
42254
|
+
const worldX = (x - gridBounds.minX) * gridConfig.cellSize;
|
|
42255
|
+
const worldZ = (z - gridBounds.minZ) * gridConfig.cellSize;
|
|
42256
|
+
return [worldX, y * gridConfig.cellSize, worldZ];
|
|
42257
|
+
},
|
|
42258
|
+
[gridBounds, gridConfig]
|
|
42259
|
+
);
|
|
42260
|
+
React83.useImperativeHandle(ref, () => ({
|
|
42261
|
+
getCameraPosition: () => {
|
|
42262
|
+
if (controlsRef.current) {
|
|
42263
|
+
const pos = controlsRef.current.object.position;
|
|
42264
|
+
return new THREE__namespace.Vector3(pos.x, pos.y, pos.z);
|
|
42265
|
+
}
|
|
42266
|
+
return null;
|
|
42267
|
+
},
|
|
42268
|
+
setCameraPosition: (x, y, z) => {
|
|
42269
|
+
if (controlsRef.current) {
|
|
42270
|
+
controlsRef.current.object.position.set(x, y, z);
|
|
42271
|
+
controlsRef.current.update();
|
|
42272
|
+
}
|
|
42273
|
+
},
|
|
42274
|
+
lookAt: (x, y, z) => {
|
|
42275
|
+
if (controlsRef.current) {
|
|
42276
|
+
controlsRef.current.target.set(x, y, z);
|
|
42277
|
+
controlsRef.current.update();
|
|
42278
|
+
}
|
|
42279
|
+
},
|
|
42280
|
+
resetCamera: () => {
|
|
42281
|
+
if (controlsRef.current) {
|
|
42282
|
+
controlsRef.current.reset();
|
|
42283
|
+
}
|
|
42284
|
+
},
|
|
42285
|
+
screenshot: () => {
|
|
42286
|
+
const canvas = containerRef.current?.querySelector("canvas");
|
|
42287
|
+
if (canvas) {
|
|
42288
|
+
return canvas.toDataURL("image/png");
|
|
42289
|
+
}
|
|
42290
|
+
return null;
|
|
42291
|
+
},
|
|
42292
|
+
export: () => ({
|
|
42293
|
+
tiles,
|
|
42294
|
+
units,
|
|
42295
|
+
features
|
|
42296
|
+
})
|
|
42297
|
+
}));
|
|
42298
|
+
const handleTileClick = React83.useCallback(
|
|
42299
|
+
(tile, event) => {
|
|
42300
|
+
eventHandlers.handleTileClick(tile, event);
|
|
42301
|
+
},
|
|
42302
|
+
[eventHandlers]
|
|
42303
|
+
);
|
|
42304
|
+
const handleUnitClick = React83.useCallback(
|
|
42305
|
+
(unit, event) => {
|
|
42306
|
+
eventHandlers.handleUnitClick(unit, event);
|
|
42307
|
+
},
|
|
42308
|
+
[eventHandlers]
|
|
42309
|
+
);
|
|
42310
|
+
const handleFeatureClick = React83.useCallback(
|
|
42311
|
+
(feature, event) => {
|
|
42312
|
+
if (event) {
|
|
42313
|
+
eventHandlers.handleFeatureClick(feature, event);
|
|
42314
|
+
}
|
|
42315
|
+
},
|
|
42316
|
+
[eventHandlers]
|
|
42317
|
+
);
|
|
42318
|
+
const handleTileHover = React83.useCallback(
|
|
42319
|
+
(tile, event) => {
|
|
42320
|
+
setHoveredTile(tile);
|
|
42321
|
+
if (event) {
|
|
42322
|
+
eventHandlers.handleTileHover(tile, event);
|
|
42323
|
+
}
|
|
42324
|
+
},
|
|
42325
|
+
[eventHandlers]
|
|
42326
|
+
);
|
|
42327
|
+
const cameraConfig = React83.useMemo(() => {
|
|
42328
|
+
const size = Math.max(
|
|
42329
|
+
gridBounds.maxX - gridBounds.minX,
|
|
42330
|
+
gridBounds.maxZ - gridBounds.minZ
|
|
42331
|
+
);
|
|
42332
|
+
const distance = size * 1.5;
|
|
42333
|
+
switch (cameraMode) {
|
|
42334
|
+
case "isometric":
|
|
42335
|
+
return {
|
|
42336
|
+
position: [distance, distance * 0.8, distance],
|
|
42337
|
+
fov: 45
|
|
42338
|
+
};
|
|
42339
|
+
case "top-down":
|
|
42340
|
+
return {
|
|
42341
|
+
position: [0, distance * 2, 0],
|
|
42342
|
+
fov: 45
|
|
42343
|
+
};
|
|
42344
|
+
case "perspective":
|
|
42345
|
+
default:
|
|
42346
|
+
return {
|
|
42347
|
+
position: [distance, distance, distance],
|
|
42348
|
+
fov: 45
|
|
42349
|
+
};
|
|
42350
|
+
}
|
|
42351
|
+
}, [cameraMode, gridBounds]);
|
|
42352
|
+
const DefaultTileRenderer = React83.useCallback(
|
|
42353
|
+
({ tile, position }) => {
|
|
42354
|
+
const isSelected = tile.id ? selectedTileIds.includes(tile.id) : false;
|
|
42355
|
+
const isHovered = hoveredTile?.id === tile.id;
|
|
42356
|
+
const isValidMove = validMoves.some(
|
|
42357
|
+
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
42358
|
+
);
|
|
42359
|
+
const isAttackTarget = attackTargets.some(
|
|
42360
|
+
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
42361
|
+
);
|
|
42362
|
+
let color = 8421504;
|
|
42363
|
+
if (tile.type === "water") color = 4491468;
|
|
42364
|
+
else if (tile.type === "grass") color = 4500036;
|
|
42365
|
+
else if (tile.type === "sand") color = 14535816;
|
|
42366
|
+
else if (tile.type === "rock") color = 8947848;
|
|
42367
|
+
else if (tile.type === "snow") color = 15658734;
|
|
42368
|
+
let emissive = 0;
|
|
42369
|
+
if (isSelected) emissive = 4473924;
|
|
42370
|
+
else if (isAttackTarget) emissive = 4456448;
|
|
42371
|
+
else if (isValidMove) emissive = 17408;
|
|
42372
|
+
else if (isHovered) emissive = 2236962;
|
|
42373
|
+
if (tile.modelUrl) {
|
|
42374
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
42375
|
+
"group",
|
|
42376
|
+
{
|
|
42377
|
+
position,
|
|
42378
|
+
onClick: (e) => handleTileClick(tile, e),
|
|
42379
|
+
onPointerEnter: (e) => handleTileHover(tile, e),
|
|
42380
|
+
onPointerLeave: (e) => handleTileHover(null, e),
|
|
42381
|
+
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
42382
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
42383
|
+
ModelLoader,
|
|
42384
|
+
{
|
|
42385
|
+
url: tile.modelUrl,
|
|
42386
|
+
scale: 0.95,
|
|
42387
|
+
fallbackGeometry: "box",
|
|
42388
|
+
castShadow: true,
|
|
42389
|
+
receiveShadow: true
|
|
42390
|
+
}
|
|
42391
|
+
)
|
|
42392
|
+
}
|
|
42393
|
+
);
|
|
42394
|
+
}
|
|
42395
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
42396
|
+
"mesh",
|
|
42397
|
+
{
|
|
42398
|
+
position,
|
|
42399
|
+
onClick: (e) => handleTileClick(tile, e),
|
|
42400
|
+
onPointerEnter: (e) => handleTileHover(tile, e),
|
|
42401
|
+
onPointerLeave: (e) => handleTileHover(null, e),
|
|
42402
|
+
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
42403
|
+
children: [
|
|
42404
|
+
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.95, 0.2, 0.95] }),
|
|
42405
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color, emissive })
|
|
42406
|
+
]
|
|
42407
|
+
}
|
|
42408
|
+
);
|
|
42409
|
+
},
|
|
42410
|
+
[selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover]
|
|
42411
|
+
);
|
|
42412
|
+
const DefaultUnitRenderer = React83.useCallback(
|
|
42413
|
+
({ unit, position }) => {
|
|
42414
|
+
const isSelected = selectedUnitId === unit.id;
|
|
42415
|
+
const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
|
|
42416
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
42417
|
+
"group",
|
|
42418
|
+
{
|
|
42419
|
+
position,
|
|
42420
|
+
onClick: (e) => handleUnitClick(unit, e),
|
|
42421
|
+
userData: { type: "unit", unitId: unit.id },
|
|
42422
|
+
children: [
|
|
42423
|
+
isSelected && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.05, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
42424
|
+
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
|
|
42425
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
|
|
42426
|
+
] }),
|
|
42427
|
+
unit.modelUrl ? (
|
|
42428
|
+
/* GLB unit model (box fallback while loading / on error) */
|
|
42429
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42430
|
+
ModelLoader,
|
|
42431
|
+
{
|
|
42432
|
+
url: unit.modelUrl,
|
|
42433
|
+
scale: 0.5,
|
|
42434
|
+
fallbackGeometry: "box",
|
|
42435
|
+
castShadow: true
|
|
42436
|
+
}
|
|
42437
|
+
)
|
|
42438
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
42439
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.3, 0], children: [
|
|
42440
|
+
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.3, 0.3, 0.1, 8] }),
|
|
42441
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
42442
|
+
] }),
|
|
42443
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.6, 0], children: [
|
|
42444
|
+
/* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [0.2, 0.4, 4, 8] }),
|
|
42445
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
42446
|
+
] }),
|
|
42447
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
42448
|
+
/* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [0.12, 8, 8] }),
|
|
42449
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
42450
|
+
] })
|
|
42451
|
+
] }),
|
|
42452
|
+
unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("group", { position: [0, 1.2, 0], children: [
|
|
42453
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [-0.25, 0, 0], children: [
|
|
42454
|
+
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.5, 0.05] }),
|
|
42455
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: 3355443 })
|
|
42456
|
+
] }),
|
|
42457
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
42458
|
+
"mesh",
|
|
42459
|
+
{
|
|
42460
|
+
position: [
|
|
42461
|
+
-0.25 + 0.5 * (unit.health / unit.maxHealth) / 2,
|
|
42462
|
+
0,
|
|
42463
|
+
0.01
|
|
42464
|
+
],
|
|
42465
|
+
children: [
|
|
42466
|
+
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.5 * (unit.health / unit.maxHealth), 0.05] }),
|
|
42467
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42468
|
+
"meshBasicMaterial",
|
|
42469
|
+
{
|
|
42470
|
+
color: unit.health / unit.maxHealth > 0.5 ? 4500036 : unit.health / unit.maxHealth > 0.25 ? 11184708 : 16729156
|
|
42471
|
+
}
|
|
42472
|
+
)
|
|
42473
|
+
]
|
|
42474
|
+
}
|
|
42475
|
+
)
|
|
42476
|
+
] })
|
|
42477
|
+
]
|
|
42478
|
+
}
|
|
42479
|
+
);
|
|
42480
|
+
},
|
|
42481
|
+
[selectedUnitId, handleUnitClick]
|
|
42482
|
+
);
|
|
42483
|
+
const DefaultFeatureRenderer = React83.useCallback(
|
|
42484
|
+
({
|
|
42485
|
+
feature,
|
|
42486
|
+
position
|
|
42487
|
+
}) => {
|
|
42488
|
+
if (feature.assetUrl) {
|
|
42489
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
42490
|
+
ModelLoader,
|
|
42491
|
+
{
|
|
42492
|
+
url: feature.assetUrl,
|
|
42493
|
+
position,
|
|
42494
|
+
scale: 0.5,
|
|
42495
|
+
rotation: [0, feature.rotation ?? 0, 0],
|
|
42496
|
+
onClick: () => handleFeatureClick(feature, null),
|
|
42497
|
+
fallbackGeometry: "box"
|
|
42498
|
+
},
|
|
42499
|
+
feature.id
|
|
42500
|
+
);
|
|
42501
|
+
}
|
|
42502
|
+
if (feature.type === "tree") {
|
|
42503
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
42504
|
+
"group",
|
|
42505
|
+
{
|
|
42506
|
+
position,
|
|
42507
|
+
onClick: (e) => handleFeatureClick(feature, e),
|
|
42508
|
+
userData: { type: "feature", featureId: feature.id },
|
|
42509
|
+
children: [
|
|
42510
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.4, 0], children: [
|
|
42511
|
+
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.1, 0.15, 0.8, 6] }),
|
|
42512
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 9127187 })
|
|
42513
|
+
] }),
|
|
42514
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
42515
|
+
/* @__PURE__ */ jsxRuntime.jsx("coneGeometry", { args: [0.5, 0.8, 8] }),
|
|
42516
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 2263842 })
|
|
42517
|
+
] })
|
|
42518
|
+
]
|
|
42519
|
+
}
|
|
42520
|
+
);
|
|
42521
|
+
}
|
|
42522
|
+
if (feature.type === "rock") {
|
|
42523
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
42524
|
+
"mesh",
|
|
42525
|
+
{
|
|
42526
|
+
position: [position[0], position[1] + 0.3, position[2]],
|
|
42527
|
+
onClick: (e) => handleFeatureClick(feature, e),
|
|
42528
|
+
userData: { type: "feature", featureId: feature.id },
|
|
42529
|
+
children: [
|
|
42530
|
+
/* @__PURE__ */ jsxRuntime.jsx("dodecahedronGeometry", { args: [0.3, 0] }),
|
|
42531
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 8421504 })
|
|
42532
|
+
]
|
|
42533
|
+
}
|
|
42534
|
+
);
|
|
42535
|
+
}
|
|
42536
|
+
return null;
|
|
42537
|
+
},
|
|
42538
|
+
[handleFeatureClick]
|
|
42539
|
+
);
|
|
42540
|
+
if (externalLoading || assetsLoading && preloadAssets.length > 0) {
|
|
42541
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
42542
|
+
Canvas3DLoadingState,
|
|
42543
|
+
{
|
|
42544
|
+
progress,
|
|
42545
|
+
loaded,
|
|
42546
|
+
total,
|
|
42547
|
+
message: loadingMessage,
|
|
42548
|
+
className
|
|
42549
|
+
}
|
|
42550
|
+
);
|
|
42551
|
+
}
|
|
42552
|
+
const displayError = externalError || internalError;
|
|
42553
|
+
if (displayError) {
|
|
42554
|
+
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: [
|
|
42555
|
+
"Error: ",
|
|
42556
|
+
displayError
|
|
42557
|
+
] }) }) });
|
|
42558
|
+
}
|
|
42559
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
42560
|
+
Canvas3DErrorBoundary,
|
|
42561
|
+
{
|
|
42562
|
+
onError: (err) => setInternalError(err.message),
|
|
42563
|
+
onReset: () => setInternalError(null),
|
|
42564
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
42565
|
+
"div",
|
|
42566
|
+
{
|
|
42567
|
+
ref: containerRef,
|
|
42568
|
+
className: cn("game-canvas-3d relative w-full h-full min-h-[85vh] overflow-hidden", className),
|
|
42569
|
+
"data-orientation": orientation,
|
|
42570
|
+
"data-camera-mode": cameraMode,
|
|
42571
|
+
"data-overlay": overlay,
|
|
42572
|
+
children: [
|
|
42573
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
42574
|
+
fiber.Canvas,
|
|
42575
|
+
{
|
|
42576
|
+
shadows,
|
|
42577
|
+
camera: {
|
|
42578
|
+
position: cameraConfig.position,
|
|
42579
|
+
fov: cameraConfig.fov,
|
|
42580
|
+
near: 0.1,
|
|
42581
|
+
far: 1e3
|
|
42582
|
+
},
|
|
42583
|
+
style: { background: backgroundColor },
|
|
42584
|
+
onClick: (e) => {
|
|
42585
|
+
if (e.target === e.currentTarget) {
|
|
42586
|
+
eventHandlers.handleCanvasClick(e);
|
|
42587
|
+
}
|
|
42588
|
+
},
|
|
42589
|
+
children: [
|
|
42590
|
+
/* @__PURE__ */ jsxRuntime.jsx(CameraController, { onCameraChange: eventHandlers.handleCameraChange }),
|
|
42591
|
+
/* @__PURE__ */ jsxRuntime.jsx("ambientLight", { intensity: 0.6 }),
|
|
42592
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42593
|
+
"directionalLight",
|
|
42594
|
+
{
|
|
42595
|
+
position: [10, 20, 10],
|
|
42596
|
+
intensity: 0.8,
|
|
42597
|
+
castShadow: shadows,
|
|
42598
|
+
"shadow-mapSize": [2048, 2048]
|
|
42599
|
+
}
|
|
42600
|
+
),
|
|
42601
|
+
/* @__PURE__ */ jsxRuntime.jsx("hemisphereLight", { intensity: 0.3, color: "#87ceeb", groundColor: "#362d1d" }),
|
|
42602
|
+
showGrid && /* @__PURE__ */ jsxRuntime.jsx(
|
|
42603
|
+
drei.Grid,
|
|
42604
|
+
{
|
|
42605
|
+
args: [
|
|
42606
|
+
Math.max(gridBounds.maxX - gridBounds.minX + 2, 10),
|
|
42607
|
+
Math.max(gridBounds.maxZ - gridBounds.minZ + 2, 10)
|
|
42608
|
+
],
|
|
42609
|
+
position: [
|
|
42610
|
+
(gridBounds.maxX - gridBounds.minX) / 2 - 0.5,
|
|
42611
|
+
0,
|
|
42612
|
+
(gridBounds.maxZ - gridBounds.minZ) / 2 - 0.5
|
|
42613
|
+
],
|
|
42614
|
+
cellSize: 1,
|
|
42615
|
+
cellThickness: 1,
|
|
42616
|
+
cellColor: "#444444",
|
|
42617
|
+
sectionSize: 5,
|
|
42618
|
+
sectionThickness: 1.5,
|
|
42619
|
+
sectionColor: "#666666",
|
|
42620
|
+
fadeDistance: 50,
|
|
42621
|
+
fadeStrength: 1
|
|
42622
|
+
}
|
|
42623
|
+
),
|
|
42624
|
+
tiles.map((tile, index) => {
|
|
42625
|
+
const position = gridToWorld(
|
|
42626
|
+
tile.x,
|
|
42627
|
+
tile.z ?? tile.y ?? 0,
|
|
42628
|
+
tile.elevation ?? 0
|
|
42629
|
+
);
|
|
42630
|
+
const Renderer = CustomTileRenderer || DefaultTileRenderer;
|
|
42631
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
|
|
42632
|
+
}),
|
|
42633
|
+
features.map((feature, index) => {
|
|
42634
|
+
const position = gridToWorld(
|
|
42635
|
+
feature.x,
|
|
42636
|
+
feature.z ?? feature.y ?? 0,
|
|
42637
|
+
(feature.elevation ?? 0) + 0.5
|
|
42638
|
+
);
|
|
42639
|
+
const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
|
|
42640
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
|
|
42641
|
+
}),
|
|
42642
|
+
units.map((unit) => {
|
|
42643
|
+
const position = gridToWorld(
|
|
42644
|
+
unit.x ?? 0,
|
|
42645
|
+
unit.z ?? unit.y ?? 0,
|
|
42646
|
+
(unit.elevation ?? 0) + 0.5
|
|
42647
|
+
);
|
|
42648
|
+
const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
|
|
42649
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { unit, position }, unit.id);
|
|
42650
|
+
}),
|
|
42651
|
+
children,
|
|
42652
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42653
|
+
drei.OrbitControls,
|
|
42654
|
+
{
|
|
42655
|
+
ref: controlsRef,
|
|
42656
|
+
target: cameraTarget,
|
|
42657
|
+
enableDamping: true,
|
|
42658
|
+
dampingFactor: 0.05,
|
|
42659
|
+
minDistance: 2,
|
|
42660
|
+
maxDistance: 100,
|
|
42661
|
+
maxPolarAngle: Math.PI / 2 - 0.1
|
|
42662
|
+
}
|
|
42663
|
+
)
|
|
42664
|
+
]
|
|
42665
|
+
}
|
|
42666
|
+
),
|
|
42667
|
+
showCoordinates && hoveredTile && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__coordinates", children: [
|
|
42668
|
+
"X: ",
|
|
42669
|
+
hoveredTile.x,
|
|
42670
|
+
", Z: ",
|
|
42671
|
+
hoveredTile.z ?? hoveredTile.y ?? 0
|
|
42672
|
+
] }),
|
|
42673
|
+
showTileInfo && hoveredTile && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__tile-info", children: [
|
|
42674
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "tile-info__type", children: hoveredTile.type }),
|
|
42675
|
+
hoveredTile.terrain && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tile-info__terrain", children: hoveredTile.terrain })
|
|
42676
|
+
] })
|
|
42677
|
+
]
|
|
42678
|
+
}
|
|
42679
|
+
)
|
|
42680
|
+
}
|
|
42681
|
+
);
|
|
42682
|
+
}
|
|
42683
|
+
);
|
|
42684
|
+
GameCanvas3D.displayName = "GameCanvas3D";
|
|
42685
|
+
}
|
|
42686
|
+
});
|
|
42687
|
+
function GameBoard3D({
|
|
42688
|
+
entity,
|
|
42689
|
+
tiles = [],
|
|
42690
|
+
features = [],
|
|
42691
|
+
cameraMode = "perspective",
|
|
42692
|
+
backgroundColor = "#2a1a1a",
|
|
42693
|
+
tileClickEvent,
|
|
42694
|
+
unitClickEvent,
|
|
42695
|
+
attackEvent,
|
|
42696
|
+
endTurnEvent,
|
|
42697
|
+
cancelEvent,
|
|
42698
|
+
playAgainEvent,
|
|
42699
|
+
gameEndEvent,
|
|
42700
|
+
className
|
|
42701
|
+
}) {
|
|
42702
|
+
const row = boardEntity(entity);
|
|
42703
|
+
const eventBus = useEventBus();
|
|
42704
|
+
const entityUnits = row ? rows(row.units) : [];
|
|
42705
|
+
const units = entityUnits;
|
|
42706
|
+
const selectedUnitId = row ? str(row.selectedUnitId) || null : null;
|
|
42707
|
+
const phase = row ? str(row.phase) : "observation";
|
|
42708
|
+
const result = row ? str(row.result) : "none";
|
|
42709
|
+
const validMoves = row && Array.isArray(row.validMoves) ? row.validMoves : [];
|
|
42710
|
+
const attackTargets = row && Array.isArray(row.attackTargets) ? row.attackTargets : [];
|
|
42711
|
+
const turn = row ? num(row.turn) : 0;
|
|
42712
|
+
const currentTeam = row ? str(row.currentTeam) : "player";
|
|
42713
|
+
const isGameOver = result !== "none";
|
|
42714
|
+
const gameEndEmittedRef = React83.useRef(false);
|
|
42715
|
+
React83.useEffect(() => {
|
|
42716
|
+
if ((result === "victory" || result === "defeat") && gameEndEvent) {
|
|
42717
|
+
if (!gameEndEmittedRef.current) {
|
|
42718
|
+
gameEndEmittedRef.current = true;
|
|
42719
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
42720
|
+
}
|
|
42721
|
+
} else {
|
|
42722
|
+
gameEndEmittedRef.current = false;
|
|
42723
|
+
}
|
|
42724
|
+
}, [result, gameEndEvent, eventBus]);
|
|
42725
|
+
const checkGameEnd = React83.useCallback(() => {
|
|
42726
|
+
const alivePlayer = entityUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
|
|
42727
|
+
const aliveEnemy = entityUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
|
|
42728
|
+
if (alivePlayer.length === 0 && gameEndEvent) {
|
|
42729
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result: "defeat" });
|
|
42730
|
+
} else if (aliveEnemy.length === 0 && gameEndEvent) {
|
|
42731
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result: "victory" });
|
|
42732
|
+
}
|
|
42733
|
+
}, [entityUnits, gameEndEvent, eventBus]);
|
|
42734
|
+
const handleUnitClickCallback = React83.useCallback((isoUnit) => {
|
|
42735
|
+
if (phase !== "action" || !selectedUnitId || !attackEvent) return;
|
|
42736
|
+
const attackerRow = entityUnits.find((u) => str(u.id) === selectedUnitId);
|
|
42737
|
+
const targetRow = entityUnits.find((u) => str(u.id) === isoUnit.id);
|
|
42738
|
+
if (!attackerRow || !targetRow) return;
|
|
42739
|
+
if (unitTeam(targetRow) !== "enemy" || unitHealth(targetRow) <= 0) return;
|
|
42740
|
+
const ap = unitPosition(attackerRow);
|
|
42741
|
+
const tp = unitPosition(targetRow);
|
|
42742
|
+
const dx = Math.abs(ap.x - tp.x);
|
|
42743
|
+
const dy = Math.abs(ap.y - tp.y);
|
|
42744
|
+
if (dx <= 1 && dy <= 1 && dx + dy > 0) {
|
|
42745
|
+
const damage = Math.max(1, num(attackerRow.attack) - num(targetRow.defense));
|
|
42746
|
+
eventBus.emit(`UI:${attackEvent}`, {
|
|
42747
|
+
attackerId: str(attackerRow.id),
|
|
42748
|
+
targetId: str(targetRow.id),
|
|
42749
|
+
damage
|
|
42750
|
+
});
|
|
42751
|
+
setTimeout(checkGameEnd, 100);
|
|
42752
|
+
}
|
|
42753
|
+
}, [phase, selectedUnitId, entityUnits, attackEvent, eventBus, checkGameEnd]);
|
|
42754
|
+
const handleEndTurn = React83.useCallback(() => {
|
|
42755
|
+
if (endTurnEvent) eventBus.emit(`UI:${endTurnEvent}`, {});
|
|
42756
|
+
}, [endTurnEvent, eventBus]);
|
|
42757
|
+
const handleCancel = React83.useCallback(() => {
|
|
42758
|
+
if (cancelEvent) eventBus.emit(`UI:${cancelEvent}`, {});
|
|
42759
|
+
}, [cancelEvent, eventBus]);
|
|
42760
|
+
const handlePlayAgain = React83.useCallback(() => {
|
|
42761
|
+
if (playAgainEvent) eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
42762
|
+
}, [playAgainEvent, eventBus]);
|
|
42763
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("game-board-3d block w-full min-h-[85vh] relative", className), children: [
|
|
42764
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-board-3d__status absolute top-3 left-3 z-10 flex gap-2 items-center", children: [
|
|
42765
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "status__phase capitalize", children: phase.replace("_", " ") }),
|
|
42766
|
+
currentTeam && !isGameOver && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", color: "muted", className: "status__team", children: [
|
|
42767
|
+
"\u2014 ",
|
|
42768
|
+
currentTeam === "player" ? "Your Turn" : "Enemy's Turn"
|
|
42769
|
+
] }),
|
|
42770
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", color: "muted", className: "status__turn", children: [
|
|
42771
|
+
"Turn ",
|
|
42772
|
+
turn
|
|
42773
|
+
] })
|
|
42774
|
+
] }),
|
|
42775
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42776
|
+
GameCanvas3D,
|
|
42777
|
+
{
|
|
42778
|
+
tiles,
|
|
42779
|
+
units,
|
|
42780
|
+
features,
|
|
42781
|
+
cameraMode,
|
|
42782
|
+
showGrid: true,
|
|
42783
|
+
showCoordinates: false,
|
|
42784
|
+
showTileInfo: false,
|
|
42785
|
+
shadows: true,
|
|
42786
|
+
backgroundColor,
|
|
42787
|
+
tileClickEvent,
|
|
42788
|
+
unitClickEvent,
|
|
42789
|
+
onUnitClick: handleUnitClickCallback,
|
|
42790
|
+
selectedUnitId,
|
|
42791
|
+
validMoves,
|
|
42792
|
+
attackTargets,
|
|
42793
|
+
className: "game-board-3d__canvas w-full min-h-[85vh]"
|
|
42794
|
+
}
|
|
42795
|
+
),
|
|
42796
|
+
!isGameOver && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "fixed bottom-6 right-6 z-50", gap: "sm", children: [
|
|
42797
|
+
(phase === "selection" || phase === "movement" || phase === "action") && /* @__PURE__ */ jsxRuntime.jsx(
|
|
42798
|
+
Button,
|
|
42799
|
+
{
|
|
42800
|
+
variant: "secondary",
|
|
42801
|
+
className: "shadow-xl",
|
|
42802
|
+
onClick: handleCancel,
|
|
42803
|
+
children: "Cancel"
|
|
42804
|
+
}
|
|
42805
|
+
),
|
|
42806
|
+
phase !== "enemy_turn" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
42807
|
+
Button,
|
|
42808
|
+
{
|
|
42809
|
+
variant: "primary",
|
|
42810
|
+
className: "shadow-xl",
|
|
42811
|
+
onClick: handleEndTurn,
|
|
42812
|
+
children: "End Turn"
|
|
42813
|
+
}
|
|
42814
|
+
)
|
|
42815
|
+
] }),
|
|
42816
|
+
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: [
|
|
42817
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42818
|
+
Typography,
|
|
42819
|
+
{
|
|
42820
|
+
variant: "h2",
|
|
42821
|
+
className: cn(
|
|
42822
|
+
"overlay__result",
|
|
42823
|
+
result === "victory" ? "text-yellow-400" : "text-red-500"
|
|
42824
|
+
),
|
|
42825
|
+
children: result === "victory" ? "Victory!" : "Defeat"
|
|
42826
|
+
}
|
|
42827
|
+
),
|
|
42828
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "body", color: "muted", className: "overlay__turn-count", children: [
|
|
42829
|
+
"Completed in ",
|
|
42830
|
+
turn,
|
|
42831
|
+
" turn",
|
|
42832
|
+
turn !== 1 ? "s" : ""
|
|
42833
|
+
] }),
|
|
42834
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42835
|
+
Button,
|
|
42836
|
+
{
|
|
42837
|
+
variant: "primary",
|
|
42838
|
+
className: "px-8 py-3 font-semibold",
|
|
42839
|
+
onClick: handlePlayAgain,
|
|
42840
|
+
children: "Play Again"
|
|
42841
|
+
}
|
|
42842
|
+
)
|
|
42843
|
+
] }) })
|
|
42844
|
+
] });
|
|
42845
|
+
}
|
|
42846
|
+
var init_GameBoard3D = __esm({
|
|
42847
|
+
"components/game/organisms/GameBoard3D.tsx"() {
|
|
42848
|
+
"use client";
|
|
42849
|
+
init_cn();
|
|
42850
|
+
init_Button();
|
|
42851
|
+
init_Typography();
|
|
42852
|
+
init_Stack();
|
|
42853
|
+
init_useEventBus();
|
|
42854
|
+
init_GameCanvas3D2();
|
|
42855
|
+
init_boardEntity();
|
|
42856
|
+
GameBoard3D.displayName = "GameBoard3D";
|
|
42857
|
+
}
|
|
42858
|
+
});
|
|
41155
42859
|
var GameShell;
|
|
41156
42860
|
var init_GameShell = __esm({
|
|
41157
42861
|
"components/game/templates/GameShell.tsx"() {
|
|
@@ -42520,21 +44224,6 @@ var init_ModalSlot = __esm({
|
|
|
42520
44224
|
ModalSlot.displayName = "ModalSlot";
|
|
42521
44225
|
}
|
|
42522
44226
|
});
|
|
42523
|
-
function getOpponentAction(strategy, actions, history) {
|
|
42524
|
-
const actionIds = actions.map((a) => a.id);
|
|
42525
|
-
switch (strategy) {
|
|
42526
|
-
case "always-cooperate":
|
|
42527
|
-
return actionIds[0];
|
|
42528
|
-
case "always-defect":
|
|
42529
|
-
return actionIds[actionIds.length - 1];
|
|
42530
|
-
case "tit-for-tat":
|
|
42531
|
-
if (history.length === 0) return actionIds[0];
|
|
42532
|
-
return history[history.length - 1].playerAction;
|
|
42533
|
-
case "random":
|
|
42534
|
-
default:
|
|
42535
|
-
return actionIds[Math.floor(Math.random() * actionIds.length)];
|
|
42536
|
-
}
|
|
42537
|
-
}
|
|
42538
44227
|
function NegotiatorBoard({
|
|
42539
44228
|
entity,
|
|
42540
44229
|
completeEvent = "PUZZLE_COMPLETE",
|
|
@@ -42556,28 +44245,35 @@ function NegotiatorBoard({
|
|
|
42556
44245
|
const playerTotal = num(resolved?.score);
|
|
42557
44246
|
const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
|
|
42558
44247
|
const won = result === "win";
|
|
42559
|
-
const
|
|
44248
|
+
const lastPlayerAction = str(resolved?.lastPlayerAction);
|
|
44249
|
+
const lastOpponentAction = str(resolved?.lastOpponentAction);
|
|
44250
|
+
const lastPayoff = num(resolved?.lastPayoff);
|
|
42560
44251
|
const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
|
|
42561
44252
|
const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
|
|
44253
|
+
const prevRoundRef = React83.useRef(currentRound);
|
|
44254
|
+
React83.useEffect(() => {
|
|
44255
|
+
if (currentRound > prevRoundRef.current && lastPlayerAction) {
|
|
44256
|
+
const opponentPayoffEntry = payoffMatrix.find(
|
|
44257
|
+
(p2) => p2.playerAction === lastPlayerAction && p2.opponentAction === lastOpponentAction
|
|
44258
|
+
);
|
|
44259
|
+
setHistory((prev) => [
|
|
44260
|
+
...prev,
|
|
44261
|
+
{
|
|
44262
|
+
round: currentRound,
|
|
44263
|
+
playerAction: lastPlayerAction,
|
|
44264
|
+
opponentAction: lastOpponentAction,
|
|
44265
|
+
playerPayoff: lastPayoff,
|
|
44266
|
+
opponentPayoff: opponentPayoffEntry?.opponentPayoff ?? 0
|
|
44267
|
+
}
|
|
44268
|
+
]);
|
|
44269
|
+
}
|
|
44270
|
+
prevRoundRef.current = currentRound;
|
|
44271
|
+
}, [currentRound, lastPlayerAction, lastOpponentAction, lastPayoff, payoffMatrix]);
|
|
44272
|
+
const opponentTotal = React83.useMemo(() => history.reduce((s, r) => s + r.opponentPayoff, 0), [history]);
|
|
42562
44273
|
const handleAction = React83.useCallback((actionId) => {
|
|
42563
44274
|
if (isComplete) return;
|
|
42564
|
-
const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
|
|
42565
|
-
const payoff = payoffMatrix.find(
|
|
42566
|
-
(p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
|
|
42567
|
-
);
|
|
42568
|
-
const playerPayoff = payoff?.playerPayoff ?? 0;
|
|
42569
|
-
setHistory((prev) => [
|
|
42570
|
-
...prev,
|
|
42571
|
-
{
|
|
42572
|
-
round: prev.length + 1,
|
|
42573
|
-
playerAction: actionId,
|
|
42574
|
-
opponentAction,
|
|
42575
|
-
playerPayoff,
|
|
42576
|
-
opponentPayoff: payoff?.opponentPayoff ?? 0
|
|
42577
|
-
}
|
|
42578
|
-
]);
|
|
42579
44275
|
if (playRoundEvent) {
|
|
42580
|
-
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff:
|
|
44276
|
+
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: 0 });
|
|
42581
44277
|
}
|
|
42582
44278
|
if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
|
|
42583
44279
|
if (finishEvent) {
|
|
@@ -42587,10 +44283,11 @@ function NegotiatorBoard({
|
|
|
42587
44283
|
setShowHint(true);
|
|
42588
44284
|
}
|
|
42589
44285
|
}
|
|
42590
|
-
}, [isComplete, resolved, totalRounds, currentRound,
|
|
44286
|
+
}, [isComplete, resolved, totalRounds, currentRound, playRoundEvent, finishEvent, emit]);
|
|
42591
44287
|
const handleReset = React83.useCallback(() => {
|
|
42592
44288
|
setHistory([]);
|
|
42593
44289
|
setShowHint(false);
|
|
44290
|
+
prevRoundRef.current = 0;
|
|
42594
44291
|
if (playAgainEvent) {
|
|
42595
44292
|
emit(`UI:${playAgainEvent}`, {});
|
|
42596
44293
|
}
|
|
@@ -42701,6 +44398,236 @@ var init_NegotiatorBoard = __esm({
|
|
|
42701
44398
|
NegotiatorBoard.displayName = "NegotiatorBoard";
|
|
42702
44399
|
}
|
|
42703
44400
|
});
|
|
44401
|
+
function resolveEntity(entity) {
|
|
44402
|
+
if (!entity) return {};
|
|
44403
|
+
if (Array.isArray(entity)) return entity[0] ?? {};
|
|
44404
|
+
return entity;
|
|
44405
|
+
}
|
|
44406
|
+
function numField(row, key, fallback) {
|
|
44407
|
+
const v = row[key];
|
|
44408
|
+
return typeof v === "number" ? v : fallback;
|
|
44409
|
+
}
|
|
44410
|
+
function strField(row, key, fallback) {
|
|
44411
|
+
const v = row[key];
|
|
44412
|
+
return typeof v === "string" ? v : fallback;
|
|
44413
|
+
}
|
|
44414
|
+
function PlatformerBoard({
|
|
44415
|
+
entity,
|
|
44416
|
+
player: propPlayer,
|
|
44417
|
+
platforms: propPlatforms,
|
|
44418
|
+
result: propResult,
|
|
44419
|
+
lives: propLives,
|
|
44420
|
+
score: propScore,
|
|
44421
|
+
level: propLevel,
|
|
44422
|
+
worldWidth = 800,
|
|
44423
|
+
worldHeight = 400,
|
|
44424
|
+
canvasWidth = 800,
|
|
44425
|
+
canvasHeight = 400,
|
|
44426
|
+
playerSprite,
|
|
44427
|
+
tileSprites,
|
|
44428
|
+
bgColor,
|
|
44429
|
+
leftEvent = "LEFT",
|
|
44430
|
+
rightEvent = "RIGHT",
|
|
44431
|
+
jumpEvent = "JUMP",
|
|
44432
|
+
stopEvent = "STOP",
|
|
44433
|
+
playAgainEvent = "PLAY_AGAIN",
|
|
44434
|
+
gameEndEvent = "GAME_END",
|
|
44435
|
+
className
|
|
44436
|
+
}) {
|
|
44437
|
+
const row = resolveEntity(entity);
|
|
44438
|
+
const eventBus = useEventBus();
|
|
44439
|
+
const { t } = hooks.useTranslate();
|
|
44440
|
+
const result = propResult ?? strField(row, "result", "none");
|
|
44441
|
+
propLives ?? numField(row, "lives", 3);
|
|
44442
|
+
const score = propScore ?? numField(row, "score", 0);
|
|
44443
|
+
const level = propLevel ?? numField(row, "level", 1);
|
|
44444
|
+
const entityPlayer = row["player"];
|
|
44445
|
+
const player = propPlayer ?? entityPlayer ?? {
|
|
44446
|
+
x: 80,
|
|
44447
|
+
y: 320,
|
|
44448
|
+
width: 32,
|
|
44449
|
+
height: 48,
|
|
44450
|
+
vx: 0,
|
|
44451
|
+
vy: 0,
|
|
44452
|
+
grounded: false,
|
|
44453
|
+
facingRight: true
|
|
44454
|
+
};
|
|
44455
|
+
const entityPlatforms = Array.isArray(row["platforms"]) ? row["platforms"] : void 0;
|
|
44456
|
+
const platforms = propPlatforms ?? entityPlatforms ?? [
|
|
44457
|
+
{ x: 0, y: 368, width: 800, height: 32, type: "ground" },
|
|
44458
|
+
{ x: 150, y: 280, width: 160, height: 16, type: "platform" },
|
|
44459
|
+
{ x: 420, y: 220, width: 160, height: 16, type: "platform" },
|
|
44460
|
+
{ x: 580, y: 300, width: 80, height: 16, type: "hazard" },
|
|
44461
|
+
{ x: 700, y: 340, width: 64, height: 28, type: "goal" }
|
|
44462
|
+
];
|
|
44463
|
+
const handleRestart = React83.useCallback(() => {
|
|
44464
|
+
if (playAgainEvent) {
|
|
44465
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
44466
|
+
}
|
|
44467
|
+
}, [playAgainEvent, eventBus]);
|
|
44468
|
+
const isGameOver = result === "won" || result === "lost";
|
|
44469
|
+
const gameEndEmittedRef = React83.useRef(false);
|
|
44470
|
+
React83.useEffect(() => {
|
|
44471
|
+
if ((result === "won" || result === "lost") && gameEndEvent) {
|
|
44472
|
+
if (!gameEndEmittedRef.current) {
|
|
44473
|
+
gameEndEmittedRef.current = true;
|
|
44474
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
44475
|
+
}
|
|
44476
|
+
} else {
|
|
44477
|
+
gameEndEmittedRef.current = false;
|
|
44478
|
+
}
|
|
44479
|
+
}, [result, gameEndEvent, eventBus]);
|
|
44480
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
44481
|
+
VStack,
|
|
44482
|
+
{
|
|
44483
|
+
className: cn("platformer-board relative bg-background", className),
|
|
44484
|
+
gap: "none",
|
|
44485
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative", children: [
|
|
44486
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44487
|
+
PlatformerCanvas,
|
|
44488
|
+
{
|
|
44489
|
+
player,
|
|
44490
|
+
platforms,
|
|
44491
|
+
worldWidth,
|
|
44492
|
+
worldHeight,
|
|
44493
|
+
canvasWidth,
|
|
44494
|
+
canvasHeight,
|
|
44495
|
+
followCamera: true,
|
|
44496
|
+
bgColor,
|
|
44497
|
+
playerSprite,
|
|
44498
|
+
tileSprites,
|
|
44499
|
+
leftEvent,
|
|
44500
|
+
rightEvent,
|
|
44501
|
+
jumpEvent,
|
|
44502
|
+
stopEvent
|
|
44503
|
+
}
|
|
44504
|
+
),
|
|
44505
|
+
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: [
|
|
44506
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44507
|
+
Typography,
|
|
44508
|
+
{
|
|
44509
|
+
variant: "h2",
|
|
44510
|
+
className: cn(
|
|
44511
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
44512
|
+
result === "won" ? "text-warning" : "text-error"
|
|
44513
|
+
),
|
|
44514
|
+
children: result === "won" ? t("platformer.won") : t("platformer.lost")
|
|
44515
|
+
}
|
|
44516
|
+
),
|
|
44517
|
+
result === "won" && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "body1", className: "text-muted-foreground", children: [
|
|
44518
|
+
t("platformer.level"),
|
|
44519
|
+
" ",
|
|
44520
|
+
level,
|
|
44521
|
+
" \u2014 ",
|
|
44522
|
+
t("platformer.score"),
|
|
44523
|
+
": ",
|
|
44524
|
+
score
|
|
44525
|
+
] }),
|
|
44526
|
+
result === "lost" && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: t("platformer.noLives") }),
|
|
44527
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44528
|
+
Button,
|
|
44529
|
+
{
|
|
44530
|
+
variant: "primary",
|
|
44531
|
+
className: "px-8 py-3 font-semibold",
|
|
44532
|
+
onClick: handleRestart,
|
|
44533
|
+
children: result === "won" ? t("platformer.nextLevel") : t("platformer.playAgain")
|
|
44534
|
+
}
|
|
44535
|
+
)
|
|
44536
|
+
] }) })
|
|
44537
|
+
] })
|
|
44538
|
+
}
|
|
44539
|
+
);
|
|
44540
|
+
}
|
|
44541
|
+
var init_PlatformerBoard = __esm({
|
|
44542
|
+
"components/game/organisms/PlatformerBoard.tsx"() {
|
|
44543
|
+
"use client";
|
|
44544
|
+
init_cn();
|
|
44545
|
+
init_useEventBus();
|
|
44546
|
+
init_Box();
|
|
44547
|
+
init_Button();
|
|
44548
|
+
init_Typography();
|
|
44549
|
+
init_Stack();
|
|
44550
|
+
init_PlatformerCanvas();
|
|
44551
|
+
PlatformerBoard.displayName = "PlatformerBoard";
|
|
44552
|
+
}
|
|
44553
|
+
});
|
|
44554
|
+
function PlatformerTemplate({
|
|
44555
|
+
entity,
|
|
44556
|
+
platforms = DEFAULT_PLATFORMS,
|
|
44557
|
+
worldWidth = 800,
|
|
44558
|
+
worldHeight = 400,
|
|
44559
|
+
canvasWidth = 800,
|
|
44560
|
+
canvasHeight = 400,
|
|
44561
|
+
playerSprite = `${CDN3}/characters/platformChar_idle.png`,
|
|
44562
|
+
tileSprites = DEFAULT_TILE_SPRITES,
|
|
44563
|
+
bgColor = "#5c94fc",
|
|
44564
|
+
gameEndEvent = "GAME_END",
|
|
44565
|
+
className
|
|
44566
|
+
}) {
|
|
44567
|
+
const row = entity && !Array.isArray(entity) ? entity : void 0;
|
|
44568
|
+
const lives = typeof row?.["lives"] === "number" ? row["lives"] : 3;
|
|
44569
|
+
const score = typeof row?.["score"] === "number" ? row["score"] : 0;
|
|
44570
|
+
const level = typeof row?.["level"] === "number" ? row["level"] : 1;
|
|
44571
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("platformer-template w-full", className), gap: "none", children: [
|
|
44572
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
44573
|
+
GameHud,
|
|
44574
|
+
{
|
|
44575
|
+
position: "top",
|
|
44576
|
+
stats: [
|
|
44577
|
+
{ label: "Lives", value: lives, format: "number", variant: "danger" },
|
|
44578
|
+
{ label: "Level", value: level, format: "number", variant: "default" }
|
|
44579
|
+
]
|
|
44580
|
+
}
|
|
44581
|
+
) }),
|
|
44582
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44583
|
+
PlatformerBoard,
|
|
44584
|
+
{
|
|
44585
|
+
entity,
|
|
44586
|
+
platforms,
|
|
44587
|
+
worldWidth,
|
|
44588
|
+
worldHeight,
|
|
44589
|
+
canvasWidth,
|
|
44590
|
+
canvasHeight,
|
|
44591
|
+
playerSprite,
|
|
44592
|
+
tileSprites,
|
|
44593
|
+
bgColor,
|
|
44594
|
+
leftEvent: "LEFT",
|
|
44595
|
+
rightEvent: "RIGHT",
|
|
44596
|
+
jumpEvent: "JUMP",
|
|
44597
|
+
stopEvent: "STOP",
|
|
44598
|
+
playAgainEvent: "PLAY_AGAIN",
|
|
44599
|
+
gameEndEvent
|
|
44600
|
+
}
|
|
44601
|
+
),
|
|
44602
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-4 py-2", children: /* @__PURE__ */ jsxRuntime.jsx(ScoreBoard, { score, level }) })
|
|
44603
|
+
] });
|
|
44604
|
+
}
|
|
44605
|
+
var CDN3, DEFAULT_TILE_SPRITES, DEFAULT_PLATFORMS;
|
|
44606
|
+
var init_PlatformerTemplate = __esm({
|
|
44607
|
+
"components/game/templates/PlatformerTemplate.tsx"() {
|
|
44608
|
+
init_cn();
|
|
44609
|
+
init_Box();
|
|
44610
|
+
init_Stack();
|
|
44611
|
+
init_PlatformerBoard();
|
|
44612
|
+
init_GameHud();
|
|
44613
|
+
init_ScoreBoard();
|
|
44614
|
+
CDN3 = "https://almadar-kflow-assets.web.app/shared/platformer";
|
|
44615
|
+
DEFAULT_TILE_SPRITES = {
|
|
44616
|
+
ground: `${CDN3}/tiles/platformPack_tile001.png`,
|
|
44617
|
+
platform: `${CDN3}/tiles/platformPack_tile004.png`,
|
|
44618
|
+
hazard: `${CDN3}/tiles/platformPack_tile017.png`,
|
|
44619
|
+
goal: `${CDN3}/tiles/platformPack_tile007.png`
|
|
44620
|
+
};
|
|
44621
|
+
DEFAULT_PLATFORMS = [
|
|
44622
|
+
{ x: 0, y: 368, width: 800, height: 32, type: "ground" },
|
|
44623
|
+
{ x: 150, y: 280, width: 160, height: 16, type: "platform" },
|
|
44624
|
+
{ x: 420, y: 220, width: 160, height: 16, type: "platform" },
|
|
44625
|
+
{ x: 580, y: 300, width: 80, height: 16, type: "hazard" },
|
|
44626
|
+
{ x: 700, y: 340, width: 64, height: 28, type: "goal" }
|
|
44627
|
+
];
|
|
44628
|
+
PlatformerTemplate.displayName = "PlatformerTemplate";
|
|
44629
|
+
}
|
|
44630
|
+
});
|
|
42704
44631
|
var PricingOrganism;
|
|
42705
44632
|
var init_PricingOrganism = __esm({
|
|
42706
44633
|
"components/marketing/organisms/PricingOrganism.tsx"() {
|
|
@@ -42823,6 +44750,356 @@ var init_PricingPageTemplate = __esm({
|
|
|
42823
44750
|
PricingPageTemplate.displayName = "PricingPageTemplate";
|
|
42824
44751
|
}
|
|
42825
44752
|
});
|
|
44753
|
+
function RoguelikeBoard({
|
|
44754
|
+
entity,
|
|
44755
|
+
tiles: propTiles,
|
|
44756
|
+
enemies: propEnemies,
|
|
44757
|
+
items: propItems,
|
|
44758
|
+
player: propPlayer,
|
|
44759
|
+
playerHp: propPlayerHp,
|
|
44760
|
+
playerMaxHp: propPlayerMaxHp,
|
|
44761
|
+
playerAttack: propPlayerAttack,
|
|
44762
|
+
depth: propDepth,
|
|
44763
|
+
result: propResult,
|
|
44764
|
+
phase: propPhase,
|
|
44765
|
+
stairsX: propStairsX,
|
|
44766
|
+
stairsY: propStairsY,
|
|
44767
|
+
assetManifest: propAssetManifest,
|
|
44768
|
+
scale = 0.45,
|
|
44769
|
+
unitScale = 1,
|
|
44770
|
+
moveEvent,
|
|
44771
|
+
playAgainEvent,
|
|
44772
|
+
gameEndEvent,
|
|
44773
|
+
className
|
|
44774
|
+
}) {
|
|
44775
|
+
const board = boardEntity(entity) ?? {};
|
|
44776
|
+
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : DEFAULT_TILES2);
|
|
44777
|
+
const enemies = propEnemies ?? (Array.isArray(board.enemies) ? board.enemies : DEFAULT_ENEMIES);
|
|
44778
|
+
const items = propItems ?? (Array.isArray(board.items) ? board.items : DEFAULT_ITEMS);
|
|
44779
|
+
const player = propPlayer ?? board.player ?? { x: 1, y: 1 };
|
|
44780
|
+
const playerHp = propPlayerHp ?? num(board.playerHp, 10);
|
|
44781
|
+
const playerMaxHp = propPlayerMaxHp ?? num(board.playerMaxHp, 10);
|
|
44782
|
+
const playerAttack = propPlayerAttack ?? num(board.playerAttack, 3);
|
|
44783
|
+
const depth = propDepth ?? num(board.depth, 1);
|
|
44784
|
+
const result = propResult ?? (str(board.result) || "none");
|
|
44785
|
+
const phase = propPhase ?? (str(board.phase) || "player_turn");
|
|
44786
|
+
propStairsX ?? num(board.stairsX, 3);
|
|
44787
|
+
propStairsY ?? num(board.stairsY, 3);
|
|
44788
|
+
const eventBus = useEventBus();
|
|
44789
|
+
const { t } = hooks.useTranslate();
|
|
44790
|
+
const [hoveredTile, setHoveredTile] = React83.useState(null);
|
|
44791
|
+
const emitMove = React83.useCallback((dx, dy) => {
|
|
44792
|
+
if (!moveEvent) return;
|
|
44793
|
+
eventBus.emit(`UI:${moveEvent}`, { dx, dy });
|
|
44794
|
+
}, [moveEvent, eventBus]);
|
|
44795
|
+
React83.useEffect(() => {
|
|
44796
|
+
const handler = (e) => {
|
|
44797
|
+
if (result !== "none" || phase !== "player_turn") return;
|
|
44798
|
+
if (e.key === "ArrowUp" || e.key === "w") emitMove(0, -1);
|
|
44799
|
+
if (e.key === "ArrowDown" || e.key === "s") emitMove(0, 1);
|
|
44800
|
+
if (e.key === "ArrowLeft" || e.key === "a") emitMove(-1, 0);
|
|
44801
|
+
if (e.key === "ArrowRight" || e.key === "d") emitMove(1, 0);
|
|
44802
|
+
};
|
|
44803
|
+
window.addEventListener("keydown", handler);
|
|
44804
|
+
return () => window.removeEventListener("keydown", handler);
|
|
44805
|
+
}, [result, phase, emitMove]);
|
|
44806
|
+
React83.useEffect(() => {
|
|
44807
|
+
if ((result === "won" || result === "lost") && gameEndEvent) {
|
|
44808
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
44809
|
+
}
|
|
44810
|
+
}, [result, gameEndEvent, eventBus]);
|
|
44811
|
+
const handleTileClick = React83.useCallback((x, y) => {
|
|
44812
|
+
if (result !== "none" || phase !== "player_turn") return;
|
|
44813
|
+
const dx = x - player.x;
|
|
44814
|
+
const dy = y - player.y;
|
|
44815
|
+
if (Math.abs(dx) + Math.abs(dy) === 1) {
|
|
44816
|
+
emitMove(dx, dy);
|
|
44817
|
+
}
|
|
44818
|
+
}, [result, phase, player, emitMove]);
|
|
44819
|
+
const handleReset = React83.useCallback(() => {
|
|
44820
|
+
if (!playAgainEvent) return;
|
|
44821
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
44822
|
+
}, [playAgainEvent, eventBus]);
|
|
44823
|
+
const isoUnits = React83.useMemo(() => {
|
|
44824
|
+
const liveEnemies = enemies.filter((e) => e.hp > 0);
|
|
44825
|
+
return [
|
|
44826
|
+
{
|
|
44827
|
+
id: "player",
|
|
44828
|
+
position: player,
|
|
44829
|
+
name: t("roguelike.player") || "Hero",
|
|
44830
|
+
team: "player",
|
|
44831
|
+
health: playerHp,
|
|
44832
|
+
maxHealth: playerMaxHp,
|
|
44833
|
+
unitType: "player",
|
|
44834
|
+
sprite: `${CDN4}/isometric-dungeon/Characters/Male/Male_0_Idle0.png`
|
|
44835
|
+
},
|
|
44836
|
+
...liveEnemies.map((e) => ({
|
|
44837
|
+
id: e.id,
|
|
44838
|
+
position: { x: e.x, y: e.y },
|
|
44839
|
+
name: t("roguelike.enemy") || "Enemy",
|
|
44840
|
+
team: "enemy",
|
|
44841
|
+
health: e.hp,
|
|
44842
|
+
maxHealth: 6,
|
|
44843
|
+
unitType: "enemy",
|
|
44844
|
+
sprite: `${CDN4}/sprite-sheets/shadow-legion-sprite-sheet-sw.png`
|
|
44845
|
+
}))
|
|
44846
|
+
];
|
|
44847
|
+
}, [player, enemies, playerHp, playerMaxHp, t]);
|
|
44848
|
+
const isoFeatures = React83.useMemo(
|
|
44849
|
+
() => items.map((it) => ({
|
|
44850
|
+
id: it.id,
|
|
44851
|
+
x: it.x,
|
|
44852
|
+
y: it.y,
|
|
44853
|
+
type: it.kind,
|
|
44854
|
+
sprite: FEATURE_SPRITE[it.kind] ?? FEATURE_SPRITE.health_potion
|
|
44855
|
+
})),
|
|
44856
|
+
[items]
|
|
44857
|
+
);
|
|
44858
|
+
const validMoves = React83.useMemo(() => {
|
|
44859
|
+
if (result !== "none" || phase !== "player_turn") return [];
|
|
44860
|
+
const dirs = [{ dx: 0, dy: -1 }, { dx: 0, dy: 1 }, { dx: -1, dy: 0 }, { dx: 1, dy: 0 }];
|
|
44861
|
+
return dirs.map(({ dx, dy }) => ({ x: player.x + dx, y: player.y + dy })).filter(({ x, y }) => {
|
|
44862
|
+
const tile = tiles.find((t2) => t2.x === x && t2.y === y);
|
|
44863
|
+
return tile?.passable ?? false;
|
|
44864
|
+
});
|
|
44865
|
+
}, [result, phase, player, tiles]);
|
|
44866
|
+
const isGameOver = result === "won" || result === "lost";
|
|
44867
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("roguelike-board relative min-h-[600px] bg-background", className), gap: "none", children: [
|
|
44868
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center justify-between px-4 py-2 border-b border-border", gap: "md", children: [
|
|
44869
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
44870
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.depth") || "Depth" }),
|
|
44871
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "font-bold tabular-nums", children: depth })
|
|
44872
|
+
] }),
|
|
44873
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
44874
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.hp") || "HP" }),
|
|
44875
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
44876
|
+
Typography,
|
|
44877
|
+
{
|
|
44878
|
+
variant: "body1",
|
|
44879
|
+
className: cn(
|
|
44880
|
+
"font-bold tabular-nums",
|
|
44881
|
+
playerHp <= Math.floor(playerMaxHp * 0.25) ? "text-error" : "text-success"
|
|
44882
|
+
),
|
|
44883
|
+
children: [
|
|
44884
|
+
playerHp,
|
|
44885
|
+
" / ",
|
|
44886
|
+
playerMaxHp
|
|
44887
|
+
]
|
|
44888
|
+
}
|
|
44889
|
+
)
|
|
44890
|
+
] }),
|
|
44891
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
44892
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.atk") || "ATK" }),
|
|
44893
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "font-bold tabular-nums text-foreground", children: playerAttack })
|
|
44894
|
+
] }),
|
|
44895
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
44896
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.phase") || "Phase" }),
|
|
44897
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "capitalize text-foreground", children: phase.replace("_", " ") })
|
|
44898
|
+
] })
|
|
44899
|
+
] }),
|
|
44900
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "relative flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
44901
|
+
IsometricCanvas_default,
|
|
44902
|
+
{
|
|
44903
|
+
tiles,
|
|
44904
|
+
units: isoUnits,
|
|
44905
|
+
features: isoFeatures,
|
|
44906
|
+
selectedUnitId: null,
|
|
44907
|
+
validMoves,
|
|
44908
|
+
attackTargets: [],
|
|
44909
|
+
hoveredTile,
|
|
44910
|
+
onTileClick: handleTileClick,
|
|
44911
|
+
onUnitClick: () => void 0,
|
|
44912
|
+
onTileHover: (x, y) => setHoveredTile({ x, y }),
|
|
44913
|
+
onTileLeave: () => setHoveredTile(null),
|
|
44914
|
+
scale,
|
|
44915
|
+
assetBaseUrl: propAssetManifest?.baseUrl ?? CDN4,
|
|
44916
|
+
assetManifest: propAssetManifest,
|
|
44917
|
+
unitScale
|
|
44918
|
+
}
|
|
44919
|
+
) }),
|
|
44920
|
+
!isGameOver && /* @__PURE__ */ jsxRuntime.jsx(HStack, { className: "justify-center gap-2 p-3", gap: "none", children: [
|
|
44921
|
+
{ label: "\u2191", dx: 0, dy: -1 },
|
|
44922
|
+
{ label: "\u2193", dx: 0, dy: 1 },
|
|
44923
|
+
{ label: "\u2190", dx: -1, dy: 0 },
|
|
44924
|
+
{ label: "\u2192", dx: 1, dy: 0 }
|
|
44925
|
+
].map(({ label, dx, dy }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
44926
|
+
Button,
|
|
44927
|
+
{
|
|
44928
|
+
variant: "secondary",
|
|
44929
|
+
className: "w-9 h-9 p-0 text-base font-mono",
|
|
44930
|
+
onClick: () => emitMove(dx, dy),
|
|
44931
|
+
disabled: phase !== "player_turn",
|
|
44932
|
+
children: label
|
|
44933
|
+
},
|
|
44934
|
+
label
|
|
44935
|
+
)) }),
|
|
44936
|
+
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: [
|
|
44937
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44938
|
+
Typography,
|
|
44939
|
+
{
|
|
44940
|
+
variant: "h2",
|
|
44941
|
+
className: cn(
|
|
44942
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
44943
|
+
result === "won" ? "text-warning" : "text-error"
|
|
44944
|
+
),
|
|
44945
|
+
children: result === "won" ? t("roguelike.victory") || "Victory!" : t("roguelike.defeat") || "Defeated!"
|
|
44946
|
+
}
|
|
44947
|
+
),
|
|
44948
|
+
result === "won" && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: t("roguelike.cleared") || `Dungeon cleared! Depth reached: ${depth}` }),
|
|
44949
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44950
|
+
Button,
|
|
44951
|
+
{
|
|
44952
|
+
variant: "primary",
|
|
44953
|
+
className: "px-8 py-3 font-semibold",
|
|
44954
|
+
onClick: handleReset,
|
|
44955
|
+
children: t("roguelike.playAgain") || "Play Again"
|
|
44956
|
+
}
|
|
44957
|
+
)
|
|
44958
|
+
] }) })
|
|
44959
|
+
] });
|
|
44960
|
+
}
|
|
44961
|
+
var CDN4, DEFAULT_TILES2, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
|
|
44962
|
+
var init_RoguelikeBoard = __esm({
|
|
44963
|
+
"components/game/organisms/RoguelikeBoard.tsx"() {
|
|
44964
|
+
"use client";
|
|
44965
|
+
init_cn();
|
|
44966
|
+
init_useEventBus();
|
|
44967
|
+
init_Box();
|
|
44968
|
+
init_Button();
|
|
44969
|
+
init_Typography();
|
|
44970
|
+
init_Stack();
|
|
44971
|
+
init_IsometricCanvas();
|
|
44972
|
+
init_boardEntity();
|
|
44973
|
+
CDN4 = "https://almadar-kflow-assets.web.app/shared";
|
|
44974
|
+
DEFAULT_TILES2 = [
|
|
44975
|
+
{ x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44976
|
+
{ x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44977
|
+
{ x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44978
|
+
{ x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44979
|
+
{ x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44980
|
+
{ x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44981
|
+
{ x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44982
|
+
{ x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44983
|
+
{ x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44984
|
+
{ x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44985
|
+
{ x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44986
|
+
{ x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
44987
|
+
{ x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
44988
|
+
{ x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44989
|
+
{ x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44990
|
+
{ x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44991
|
+
{ x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44992
|
+
{ x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
44993
|
+
{ x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` },
|
|
44994
|
+
{ x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44995
|
+
{ x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44996
|
+
{ x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44997
|
+
{ x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44998
|
+
{ x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44999
|
+
{ x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` }
|
|
45000
|
+
];
|
|
45001
|
+
DEFAULT_ENEMIES = [
|
|
45002
|
+
{ id: "e1", x: 3, y: 1, hp: 5, attack: 2 },
|
|
45003
|
+
{ id: "e2", x: 1, y: 3, hp: 4, attack: 1 }
|
|
45004
|
+
];
|
|
45005
|
+
DEFAULT_ITEMS = [
|
|
45006
|
+
{ id: "i1", x: 2, y: 2, kind: "health_potion" }
|
|
45007
|
+
];
|
|
45008
|
+
FEATURE_SPRITE = {
|
|
45009
|
+
health_potion: `${CDN4}/isometric-dungeon/Isometric/chestClosed_E.png`,
|
|
45010
|
+
sword: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`,
|
|
45011
|
+
shield: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`
|
|
45012
|
+
};
|
|
45013
|
+
RoguelikeBoard.displayName = "RoguelikeBoard";
|
|
45014
|
+
}
|
|
45015
|
+
});
|
|
45016
|
+
function RoguelikeTemplate({
|
|
45017
|
+
entity,
|
|
45018
|
+
scale = 0.45,
|
|
45019
|
+
unitScale = 1,
|
|
45020
|
+
tiles = DEFAULT_ROGUELIKE_TILES,
|
|
45021
|
+
enemies,
|
|
45022
|
+
items,
|
|
45023
|
+
stairsX,
|
|
45024
|
+
stairsY,
|
|
45025
|
+
assetManifest = DEFAULT_ROGUELIKE_MANIFEST,
|
|
45026
|
+
gameEndEvent,
|
|
45027
|
+
className
|
|
45028
|
+
}) {
|
|
45029
|
+
const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
|
|
45030
|
+
if (!resolved && !tiles && !assetManifest) return null;
|
|
45031
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
45032
|
+
RoguelikeBoard,
|
|
45033
|
+
{
|
|
45034
|
+
entity: resolved,
|
|
45035
|
+
tiles,
|
|
45036
|
+
enemies,
|
|
45037
|
+
items,
|
|
45038
|
+
stairsX,
|
|
45039
|
+
stairsY,
|
|
45040
|
+
assetManifest,
|
|
45041
|
+
scale,
|
|
45042
|
+
unitScale,
|
|
45043
|
+
moveEvent: "MOVE",
|
|
45044
|
+
playAgainEvent: "PLAY_AGAIN",
|
|
45045
|
+
gameEndEvent: gameEndEvent ?? "GAME_END",
|
|
45046
|
+
className
|
|
45047
|
+
}
|
|
45048
|
+
);
|
|
45049
|
+
}
|
|
45050
|
+
var CDN5, DEFAULT_ROGUELIKE_TILES, DEFAULT_ROGUELIKE_MANIFEST;
|
|
45051
|
+
var init_RoguelikeTemplate = __esm({
|
|
45052
|
+
"components/game/templates/RoguelikeTemplate.tsx"() {
|
|
45053
|
+
init_RoguelikeBoard();
|
|
45054
|
+
CDN5 = "https://almadar-kflow-assets.web.app/shared";
|
|
45055
|
+
DEFAULT_ROGUELIKE_TILES = [
|
|
45056
|
+
{ x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45057
|
+
{ x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45058
|
+
{ x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45059
|
+
{ x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45060
|
+
{ x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45061
|
+
{ x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45062
|
+
{ x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45063
|
+
{ x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45064
|
+
{ x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45065
|
+
{ x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45066
|
+
{ x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45067
|
+
{ x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
45068
|
+
{ x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
45069
|
+
{ x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45070
|
+
{ x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45071
|
+
{ x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45072
|
+
{ x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45073
|
+
{ x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
45074
|
+
{ x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stairs_E.png` },
|
|
45075
|
+
{ x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45076
|
+
{ x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45077
|
+
{ x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45078
|
+
{ x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45079
|
+
{ x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45080
|
+
{ x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` }
|
|
45081
|
+
];
|
|
45082
|
+
DEFAULT_ROGUELIKE_MANIFEST = {
|
|
45083
|
+
baseUrl: CDN5,
|
|
45084
|
+
terrains: {
|
|
45085
|
+
floor: "/isometric-dungeon/Isometric/dirt_E.png",
|
|
45086
|
+
wall: "/isometric-dungeon/Isometric/stoneInset_E.png",
|
|
45087
|
+
stairs: "/isometric-dungeon/Isometric/stairs_E.png",
|
|
45088
|
+
planks: "/isometric-dungeon/Isometric/planks_E.png"
|
|
45089
|
+
},
|
|
45090
|
+
units: {
|
|
45091
|
+
player: "/isometric-dungeon/Characters/Male/Male_0_Idle0.png",
|
|
45092
|
+
enemy: "/sprite-sheets/shadow-legion-sprite-sheet-sw.png"
|
|
45093
|
+
},
|
|
45094
|
+
features: {
|
|
45095
|
+
health_potion: "/isometric-dungeon/Isometric/chestClosed_E.png",
|
|
45096
|
+
sword: "/isometric-dungeon/Isometric/barrel_E.png",
|
|
45097
|
+
shield: "/isometric-dungeon/Isometric/barrel_E.png"
|
|
45098
|
+
}
|
|
45099
|
+
};
|
|
45100
|
+
RoguelikeTemplate.displayName = "RoguelikeTemplate";
|
|
45101
|
+
}
|
|
45102
|
+
});
|
|
42826
45103
|
|
|
42827
45104
|
// lib/traitRegistry.ts
|
|
42828
45105
|
function getAllTraits() {
|
|
@@ -44057,7 +46334,7 @@ function getAllEvents(traits2) {
|
|
|
44057
46334
|
function EventDispatcherTab({ traits: traits2, schema }) {
|
|
44058
46335
|
const eventBus = useEventBus();
|
|
44059
46336
|
const { t } = hooks.useTranslate();
|
|
44060
|
-
const [
|
|
46337
|
+
const [log14, setLog] = React83__namespace.useState([]);
|
|
44061
46338
|
const prevStatesRef = React83__namespace.useRef(/* @__PURE__ */ new Map());
|
|
44062
46339
|
React83__namespace.useEffect(() => {
|
|
44063
46340
|
for (const trait of traits2) {
|
|
@@ -44121,9 +46398,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
|
|
|
44121
46398
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.otherEvents") }),
|
|
44122
46399
|
/* @__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)) })
|
|
44123
46400
|
] }),
|
|
44124
|
-
|
|
46401
|
+
log14.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
44125
46402
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.recentTransitions") }),
|
|
44126
|
-
/* @__PURE__ */ jsxRuntime.jsx(Stack, { gap: "xs", children:
|
|
46403
|
+
/* @__PURE__ */ jsxRuntime.jsx(Stack, { gap: "xs", children: log14.map((entry, i) => /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
|
|
44127
46404
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-purple-400", children: entry.traitName }),
|
|
44128
46405
|
" ",
|
|
44129
46406
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-500", children: entry.from }),
|
|
@@ -44926,17 +47203,24 @@ function SequencerBoard({
|
|
|
44926
47203
|
const { emit } = useEventBus();
|
|
44927
47204
|
const { t } = hooks.useTranslate();
|
|
44928
47205
|
const resolved = boardEntity(entity);
|
|
44929
|
-
const maxSlots = num(resolved?.maxSlots);
|
|
47206
|
+
const maxSlots = num(resolved?.maxSlots) || 3;
|
|
44930
47207
|
const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
|
|
44931
47208
|
const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
|
|
44932
47209
|
const allowDuplicates = resolved?.allowDuplicates !== false;
|
|
47210
|
+
const entitySlots = Array.isArray(resolved?.slots) ? resolved.slots : [];
|
|
47211
|
+
const entityResult = str(resolved?.result);
|
|
47212
|
+
const entityAttempts = num(resolved?.attempts);
|
|
47213
|
+
const entityCurrentStep = typeof resolved?.currentStep === "number" ? resolved.currentStep : -1;
|
|
47214
|
+
const slots = Array.from({ length: maxSlots }, (_, i) => {
|
|
47215
|
+
const entitySlot = entitySlots.find((s) => s.index === i);
|
|
47216
|
+
if (!entitySlot?.placedActionId) return void 0;
|
|
47217
|
+
return availableActions.find((a) => a.id === entitySlot.placedActionId);
|
|
47218
|
+
});
|
|
47219
|
+
const isSuccess = entityResult === "win";
|
|
47220
|
+
const attempts = entityAttempts;
|
|
47221
|
+
const isPlayingBack = entityCurrentStep >= 0 && !isSuccess;
|
|
47222
|
+
const currentStep = entityCurrentStep;
|
|
44933
47223
|
const [headerError, setHeaderError] = React83.useState(false);
|
|
44934
|
-
const [slots, setSlots] = React83.useState(
|
|
44935
|
-
() => Array.from({ length: maxSlots }, () => void 0)
|
|
44936
|
-
);
|
|
44937
|
-
const [playState, setPlayState] = React83.useState("idle");
|
|
44938
|
-
const [currentStep, setCurrentStep] = React83.useState(-1);
|
|
44939
|
-
const [attempts, setAttempts] = React83.useState(0);
|
|
44940
47224
|
const [slotFeedback, setSlotFeedback] = React83.useState(
|
|
44941
47225
|
() => Array.from({ length: maxSlots }, () => null)
|
|
44942
47226
|
);
|
|
@@ -44945,11 +47229,6 @@ function SequencerBoard({
|
|
|
44945
47229
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
44946
47230
|
}, []);
|
|
44947
47231
|
const handleSlotDrop = React83.useCallback((index, item) => {
|
|
44948
|
-
setSlots((prev) => {
|
|
44949
|
-
const next = [...prev];
|
|
44950
|
-
next[index] = item;
|
|
44951
|
-
return next;
|
|
44952
|
-
});
|
|
44953
47232
|
setSlotFeedback((prev) => {
|
|
44954
47233
|
const next = [...prev];
|
|
44955
47234
|
next[index] = null;
|
|
@@ -44959,11 +47238,6 @@ function SequencerBoard({
|
|
|
44959
47238
|
if (placeEvent) emit(`UI:${placeEvent}`, { slotIndex: index, actionId: item.id });
|
|
44960
47239
|
}, [emit, placeEvent]);
|
|
44961
47240
|
const handleSlotRemove = React83.useCallback((index) => {
|
|
44962
|
-
setSlots((prev) => {
|
|
44963
|
-
const next = [...prev];
|
|
44964
|
-
next[index] = void 0;
|
|
44965
|
-
return next;
|
|
44966
|
-
});
|
|
44967
47241
|
setSlotFeedback((prev) => {
|
|
44968
47242
|
const next = [...prev];
|
|
44969
47243
|
next[index] = null;
|
|
@@ -44974,48 +47248,34 @@ function SequencerBoard({
|
|
|
44974
47248
|
}, [emit, removeEvent]);
|
|
44975
47249
|
const handleReset = React83.useCallback(() => {
|
|
44976
47250
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
44977
|
-
setSlots(Array.from({ length: maxSlots }, () => void 0));
|
|
44978
|
-
setPlayState("idle");
|
|
44979
|
-
setCurrentStep(-1);
|
|
44980
|
-
setAttempts(0);
|
|
44981
47251
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
44982
47252
|
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
44983
47253
|
}, [maxSlots, playAgainEvent, emit]);
|
|
44984
47254
|
const filledSlots = slots.filter((s) => !!s);
|
|
44985
|
-
const canPlay = filledSlots.length > 0 &&
|
|
47255
|
+
const canPlay = filledSlots.length > 0 && !isPlayingBack && !isSuccess;
|
|
44986
47256
|
const handlePlay = React83.useCallback(() => {
|
|
44987
47257
|
if (!canPlay) return;
|
|
44988
47258
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
44989
47259
|
emit("UI:PLAY_SOUND", { key: "confirm" });
|
|
44990
47260
|
const sequence = slots.map((s) => s?.id || "");
|
|
44991
|
-
|
|
44992
|
-
|
|
44993
|
-
}
|
|
44994
|
-
setPlayState("playing");
|
|
44995
|
-
setCurrentStep(0);
|
|
47261
|
+
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
47262
|
+
if (playEvent) emit(`UI:${playEvent}`, { sequence });
|
|
44996
47263
|
let step = 0;
|
|
44997
47264
|
const advance = () => {
|
|
44998
47265
|
step++;
|
|
47266
|
+
emit("UI:STEP", { step });
|
|
44999
47267
|
if (step >= maxSlots) {
|
|
47268
|
+
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
45000
47269
|
const playerSeq = slots.map((s) => s?.id);
|
|
45001
|
-
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
45002
47270
|
const success = solutions.some(
|
|
45003
47271
|
(sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
|
|
45004
47272
|
);
|
|
45005
|
-
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
45006
47273
|
if (success) {
|
|
45007
|
-
setPlayState("success");
|
|
45008
|
-
setCurrentStep(-1);
|
|
45009
47274
|
emit("UI:PLAY_SOUND", { key: "levelComplete" });
|
|
45010
|
-
if (completeEvent) {
|
|
45011
|
-
emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
45012
|
-
}
|
|
47275
|
+
if (completeEvent) emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
45013
47276
|
} else {
|
|
45014
|
-
setAttempts((prev) => prev + 1);
|
|
45015
47277
|
const feedback = computeSlotFeedback(playerSeq, solutions);
|
|
45016
47278
|
setSlotFeedback(feedback);
|
|
45017
|
-
setPlayState("idle");
|
|
45018
|
-
setCurrentStep(-1);
|
|
45019
47279
|
emit("UI:PLAY_SOUND", { key: "fail" });
|
|
45020
47280
|
const correctCount2 = feedback.filter((f3) => f3 === "correct").length;
|
|
45021
47281
|
for (let ci = 0; ci < correctCount2; ci++) {
|
|
@@ -45025,7 +47285,6 @@ function SequencerBoard({
|
|
|
45025
47285
|
}
|
|
45026
47286
|
}
|
|
45027
47287
|
} else {
|
|
45028
|
-
setCurrentStep(step);
|
|
45029
47288
|
timerRef.current = setTimeout(advance, stepDurationMs);
|
|
45030
47289
|
}
|
|
45031
47290
|
};
|
|
@@ -45075,7 +47334,7 @@ function SequencerBoard({
|
|
|
45075
47334
|
/* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
|
|
45076
47335
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center justify-between", children: [
|
|
45077
47336
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
|
|
45078
|
-
hasFeedback &&
|
|
47337
|
+
hasFeedback && !isPlayingBack && !isSuccess && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
|
|
45079
47338
|
`${correctCount}/${maxSlots} `,
|
|
45080
47339
|
"\u2705"
|
|
45081
47340
|
] })
|
|
@@ -45087,7 +47346,7 @@ function SequencerBoard({
|
|
|
45087
47346
|
maxSlots,
|
|
45088
47347
|
onSlotDrop: handleSlotDrop,
|
|
45089
47348
|
onSlotRemove: handleSlotRemove,
|
|
45090
|
-
playing:
|
|
47349
|
+
playing: isPlayingBack,
|
|
45091
47350
|
currentStep,
|
|
45092
47351
|
categoryColors,
|
|
45093
47352
|
slotFeedback,
|
|
@@ -45095,7 +47354,7 @@ function SequencerBoard({
|
|
|
45095
47354
|
}
|
|
45096
47355
|
)
|
|
45097
47356
|
] }),
|
|
45098
|
-
|
|
47357
|
+
!isPlayingBack && /* @__PURE__ */ jsxRuntime.jsx(
|
|
45099
47358
|
ActionPalette,
|
|
45100
47359
|
{
|
|
45101
47360
|
actions: availableActions,
|
|
@@ -45105,8 +47364,8 @@ function SequencerBoard({
|
|
|
45105
47364
|
label: t("sequencer.dragActions")
|
|
45106
47365
|
}
|
|
45107
47366
|
),
|
|
45108
|
-
hasFeedback &&
|
|
45109
|
-
|
|
47367
|
+
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) }) }),
|
|
47368
|
+
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") }) }),
|
|
45110
47369
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
|
|
45111
47370
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
45112
47371
|
Button,
|
|
@@ -46062,9 +48321,9 @@ function VariablePanel({
|
|
|
46062
48321
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
|
|
46063
48322
|
variables.map((v) => {
|
|
46064
48323
|
const name = v.name == null ? "" : String(v.name);
|
|
46065
|
-
const value =
|
|
46066
|
-
const max =
|
|
46067
|
-
const min =
|
|
48324
|
+
const value = numField2(v.value);
|
|
48325
|
+
const max = numField2(v.max, 100);
|
|
48326
|
+
const min = numField2(v.min, 0);
|
|
46068
48327
|
const unit = v.unit == null ? "" : String(v.unit);
|
|
46069
48328
|
const pct = Math.round((value - min) / (max - min) * 100);
|
|
46070
48329
|
const isHigh = pct > 80;
|
|
@@ -46094,12 +48353,12 @@ function VariablePanel({
|
|
|
46094
48353
|
})
|
|
46095
48354
|
] });
|
|
46096
48355
|
}
|
|
46097
|
-
var
|
|
48356
|
+
var numField2;
|
|
46098
48357
|
var init_VariablePanel = __esm({
|
|
46099
48358
|
"components/game/organisms/puzzles/state-architect/VariablePanel.tsx"() {
|
|
46100
48359
|
init_atoms2();
|
|
46101
48360
|
init_cn();
|
|
46102
|
-
|
|
48361
|
+
numField2 = (v, fallback = 0) => {
|
|
46103
48362
|
const n = Number(v);
|
|
46104
48363
|
return Number.isFinite(n) ? n : fallback;
|
|
46105
48364
|
};
|
|
@@ -46156,6 +48415,9 @@ function StateArchitectBoard({
|
|
|
46156
48415
|
stepDurationMs = 600,
|
|
46157
48416
|
testEvent,
|
|
46158
48417
|
completeEvent,
|
|
48418
|
+
addTransitionEvent,
|
|
48419
|
+
removeTransitionEvent,
|
|
48420
|
+
playAgainEvent,
|
|
46159
48421
|
className
|
|
46160
48422
|
}) {
|
|
46161
48423
|
const { emit } = useEventBus();
|
|
@@ -46168,14 +48430,16 @@ function StateArchitectBoard({
|
|
|
46168
48430
|
const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
|
|
46169
48431
|
const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
|
|
46170
48432
|
const entityVariables = rows(resolved?.variables);
|
|
46171
|
-
const
|
|
48433
|
+
const transitions = entityTransitions;
|
|
48434
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
48435
|
+
const entityResult = str(resolved?.result) || "none";
|
|
48436
|
+
const isSuccess = entityResult === "win";
|
|
48437
|
+
const [isTesting, setIsTesting] = React83.useState(false);
|
|
46172
48438
|
const [headerError, setHeaderError] = React83.useState(false);
|
|
46173
|
-
const [playState, setPlayState] = React83.useState("editing");
|
|
46174
48439
|
const [currentState, setCurrentState] = React83.useState(initialState);
|
|
46175
48440
|
const [selectedState, setSelectedState] = React83.useState(null);
|
|
46176
48441
|
const [testResults, setTestResults] = React83.useState([]);
|
|
46177
48442
|
const [variables, setVariables] = React83.useState(() => [...entityVariables]);
|
|
46178
|
-
const [attempts, setAttempts] = React83.useState(0);
|
|
46179
48443
|
const timerRef = React83.useRef(null);
|
|
46180
48444
|
const [addingFrom, setAddingFrom] = React83.useState(null);
|
|
46181
48445
|
React83.useEffect(() => () => {
|
|
@@ -46185,7 +48449,7 @@ function StateArchitectBoard({
|
|
|
46185
48449
|
const GRAPH_H = 400;
|
|
46186
48450
|
const positions = React83.useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
|
|
46187
48451
|
const handleStateClick = React83.useCallback((state) => {
|
|
46188
|
-
if (
|
|
48452
|
+
if (isTesting) return;
|
|
46189
48453
|
if (addingFrom) {
|
|
46190
48454
|
if (addingFrom !== state) {
|
|
46191
48455
|
const event = availableEvents[0] || "EVENT";
|
|
@@ -46195,20 +48459,20 @@ function StateArchitectBoard({
|
|
|
46195
48459
|
to: state,
|
|
46196
48460
|
event
|
|
46197
48461
|
};
|
|
46198
|
-
|
|
48462
|
+
if (addTransitionEvent) emit(`UI:${addTransitionEvent}`, { id: newTrans.id, from: newTrans.from, to: newTrans.to, event: newTrans.event });
|
|
46199
48463
|
}
|
|
46200
48464
|
setAddingFrom(null);
|
|
46201
48465
|
} else {
|
|
46202
48466
|
setSelectedState(state);
|
|
46203
48467
|
}
|
|
46204
|
-
}, [
|
|
48468
|
+
}, [isTesting, addingFrom, availableEvents, addTransitionEvent, emit]);
|
|
46205
48469
|
const handleStartAddTransition = React83.useCallback(() => {
|
|
46206
48470
|
if (!selectedState) return;
|
|
46207
48471
|
setAddingFrom(selectedState);
|
|
46208
48472
|
}, [selectedState]);
|
|
46209
48473
|
const handleRemoveTransition = React83.useCallback((transId) => {
|
|
46210
|
-
|
|
46211
|
-
}, []);
|
|
48474
|
+
if (removeTransitionEvent) emit(`UI:${removeTransitionEvent}`, { id: transId });
|
|
48475
|
+
}, [removeTransitionEvent, emit]);
|
|
46212
48476
|
const machine = React83.useMemo(() => ({
|
|
46213
48477
|
name: entityName,
|
|
46214
48478
|
description: str(resolved?.description),
|
|
@@ -46222,16 +48486,16 @@ function StateArchitectBoard({
|
|
|
46222
48486
|
}))
|
|
46223
48487
|
}), [entityName, resolved, entityStates, currentState, transitions]);
|
|
46224
48488
|
const handleTest = React83.useCallback(() => {
|
|
46225
|
-
if (
|
|
48489
|
+
if (isTesting) return;
|
|
46226
48490
|
if (testEvent) emit(`UI:${testEvent}`, {});
|
|
46227
|
-
|
|
48491
|
+
setIsTesting(true);
|
|
46228
48492
|
setTestResults([]);
|
|
46229
48493
|
const results = [];
|
|
46230
48494
|
let testIdx = 0;
|
|
46231
48495
|
const runNextTest = () => {
|
|
46232
48496
|
if (testIdx >= testCases.length) {
|
|
46233
48497
|
const allPassed = results.every((r) => r.passed);
|
|
46234
|
-
|
|
48498
|
+
setIsTesting(false);
|
|
46235
48499
|
setTestResults(results);
|
|
46236
48500
|
if (allPassed && completeEvent) {
|
|
46237
48501
|
emit(`UI:${completeEvent}`, {
|
|
@@ -46239,9 +48503,6 @@ function StateArchitectBoard({
|
|
|
46239
48503
|
passedTests: results.filter((r) => r.passed).length
|
|
46240
48504
|
});
|
|
46241
48505
|
}
|
|
46242
|
-
if (!allPassed) {
|
|
46243
|
-
setAttempts((prev) => prev + 1);
|
|
46244
|
-
}
|
|
46245
48506
|
return;
|
|
46246
48507
|
}
|
|
46247
48508
|
const testCase = testCases[testIdx];
|
|
@@ -46264,24 +48525,23 @@ function StateArchitectBoard({
|
|
|
46264
48525
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
46265
48526
|
};
|
|
46266
48527
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
46267
|
-
}, [
|
|
48528
|
+
}, [isTesting, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
|
|
46268
48529
|
const handleTryAgain = React83.useCallback(() => {
|
|
46269
48530
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
46270
|
-
|
|
48531
|
+
setIsTesting(false);
|
|
46271
48532
|
setCurrentState(initialState);
|
|
46272
48533
|
setTestResults([]);
|
|
46273
48534
|
}, [initialState]);
|
|
46274
48535
|
const handleReset = React83.useCallback(() => {
|
|
46275
48536
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
46276
|
-
|
|
46277
|
-
setPlayState("editing");
|
|
48537
|
+
setIsTesting(false);
|
|
46278
48538
|
setCurrentState(initialState);
|
|
46279
48539
|
setTestResults([]);
|
|
46280
48540
|
setVariables([...entityVariables]);
|
|
46281
48541
|
setSelectedState(null);
|
|
46282
48542
|
setAddingFrom(null);
|
|
46283
|
-
|
|
46284
|
-
}, [
|
|
48543
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
48544
|
+
}, [initialState, entityVariables, playAgainEvent, emit]);
|
|
46285
48545
|
const codeData = React83.useMemo(() => ({
|
|
46286
48546
|
name: entityName,
|
|
46287
48547
|
states: entityStates,
|
|
@@ -46377,7 +48637,7 @@ function StateArchitectBoard({
|
|
|
46377
48637
|
]
|
|
46378
48638
|
}
|
|
46379
48639
|
),
|
|
46380
|
-
|
|
48640
|
+
!isTesting && /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "sm", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
46381
48641
|
Button,
|
|
46382
48642
|
{
|
|
46383
48643
|
variant: "ghost",
|
|
@@ -46399,7 +48659,7 @@ function StateArchitectBoard({
|
|
|
46399
48659
|
t2.guardHint,
|
|
46400
48660
|
")"
|
|
46401
48661
|
] }),
|
|
46402
|
-
|
|
48662
|
+
!isTesting && /* @__PURE__ */ jsxRuntime.jsx(
|
|
46403
48663
|
Button,
|
|
46404
48664
|
{
|
|
46405
48665
|
variant: "ghost",
|
|
@@ -46431,21 +48691,21 @@ function StateArchitectBoard({
|
|
|
46431
48691
|
resolved.showCodeView !== false && /* @__PURE__ */ jsxRuntime.jsx(StateJsonView, { data: codeData, label: "View Code" })
|
|
46432
48692
|
] })
|
|
46433
48693
|
] }),
|
|
46434
|
-
|
|
46435
|
-
|
|
48694
|
+
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") }) }),
|
|
48695
|
+
!isTesting && !isSuccess && testResults.some((r) => !r.passed) && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
46436
48696
|
/* @__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]) }) }),
|
|
46437
|
-
attempts >=
|
|
48697
|
+
!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: [
|
|
46438
48698
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
46439
48699
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
|
|
46440
48700
|
] }) })
|
|
46441
48701
|
] }),
|
|
46442
48702
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
|
|
46443
|
-
|
|
48703
|
+
!isTesting && !isSuccess && testResults.some((r) => !r.passed) ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
46444
48704
|
Button,
|
|
46445
48705
|
{
|
|
46446
48706
|
variant: "primary",
|
|
46447
48707
|
onClick: handleTest,
|
|
46448
|
-
disabled:
|
|
48708
|
+
disabled: isTesting,
|
|
46449
48709
|
children: "\u25B6 " + t("game.runTests")
|
|
46450
48710
|
}
|
|
46451
48711
|
),
|
|
@@ -46951,6 +49211,289 @@ var init_ToastSlot = __esm({
|
|
|
46951
49211
|
ToastSlot.displayName = "ToastSlot";
|
|
46952
49212
|
}
|
|
46953
49213
|
});
|
|
49214
|
+
function tilesToIso(tiles) {
|
|
49215
|
+
return tiles.map((t) => ({
|
|
49216
|
+
x: t.x,
|
|
49217
|
+
y: t.y,
|
|
49218
|
+
terrain: t.terrain,
|
|
49219
|
+
terrainSprite: t.terrainSprite ?? TERRAIN_SPRITES[t.terrain ?? "ground"],
|
|
49220
|
+
passable: t.passable
|
|
49221
|
+
}));
|
|
49222
|
+
}
|
|
49223
|
+
function towersToUnits(towers) {
|
|
49224
|
+
return towers.map((t) => ({
|
|
49225
|
+
id: t.id,
|
|
49226
|
+
position: { x: t.x, y: t.y },
|
|
49227
|
+
name: "Tower",
|
|
49228
|
+
team: "player",
|
|
49229
|
+
sprite: TOWER_SPRITE,
|
|
49230
|
+
unitType: "guardian",
|
|
49231
|
+
health: 1,
|
|
49232
|
+
maxHealth: 1
|
|
49233
|
+
}));
|
|
49234
|
+
}
|
|
49235
|
+
function creepsToUnits(creeps) {
|
|
49236
|
+
return creeps.map((c) => ({
|
|
49237
|
+
id: c.id,
|
|
49238
|
+
position: { x: c.x, y: c.y },
|
|
49239
|
+
name: "Creep",
|
|
49240
|
+
team: "enemy",
|
|
49241
|
+
sprite: CREEP_SPRITE,
|
|
49242
|
+
unitType: "scrapper",
|
|
49243
|
+
health: c.hp,
|
|
49244
|
+
maxHealth: c.maxHp
|
|
49245
|
+
}));
|
|
49246
|
+
}
|
|
49247
|
+
function pathToFeatures(path) {
|
|
49248
|
+
return path.map((p2, i) => ({
|
|
49249
|
+
id: `path-${i}`,
|
|
49250
|
+
x: p2.x,
|
|
49251
|
+
y: p2.y,
|
|
49252
|
+
type: "path-marker",
|
|
49253
|
+
sprite: `${CDN6}world-map/road_straight.png`
|
|
49254
|
+
}));
|
|
49255
|
+
}
|
|
49256
|
+
function TowerDefenseBoard({
|
|
49257
|
+
entity,
|
|
49258
|
+
tiles: propTiles,
|
|
49259
|
+
path: propPath,
|
|
49260
|
+
towers: propTowers,
|
|
49261
|
+
creeps: propCreeps,
|
|
49262
|
+
gold: propGold,
|
|
49263
|
+
lives: propLives,
|
|
49264
|
+
wave: propWave,
|
|
49265
|
+
maxWaves: propMaxWaves,
|
|
49266
|
+
result: propResult,
|
|
49267
|
+
waveActive: propWaveActive,
|
|
49268
|
+
towerCost = 25,
|
|
49269
|
+
scale = 0.45,
|
|
49270
|
+
placeTowerEvent,
|
|
49271
|
+
startWaveEvent,
|
|
49272
|
+
playAgainEvent,
|
|
49273
|
+
gameEndEvent,
|
|
49274
|
+
className
|
|
49275
|
+
}) {
|
|
49276
|
+
const board = boardEntity(entity) ?? {};
|
|
49277
|
+
const eventBus = useEventBus();
|
|
49278
|
+
const { t } = hooks.useTranslate();
|
|
49279
|
+
const tiles = propTiles ?? rows(board.tiles);
|
|
49280
|
+
const path = propPath ?? rows(board.path);
|
|
49281
|
+
const towers = propTowers ?? rows(board.towers);
|
|
49282
|
+
const creeps = propCreeps ?? rows(board.creeps);
|
|
49283
|
+
const gold = propGold ?? num(board.gold, 100);
|
|
49284
|
+
const lives = propLives ?? num(board.lives, 20);
|
|
49285
|
+
const wave = propWave ?? num(board.wave, 1);
|
|
49286
|
+
const maxWaves = propMaxWaves ?? num(board.maxWaves, 5);
|
|
49287
|
+
const result = propResult ?? (str(board.result) || "none");
|
|
49288
|
+
const waveActive = propWaveActive ?? Boolean(board.waveActive);
|
|
49289
|
+
const [hoveredTile, setHoveredTile] = React83.useState(null);
|
|
49290
|
+
const emittedGameEnd = React83.useRef(false);
|
|
49291
|
+
if (result !== "none" && !emittedGameEnd.current) {
|
|
49292
|
+
emittedGameEnd.current = true;
|
|
49293
|
+
if (gameEndEvent) {
|
|
49294
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
49295
|
+
}
|
|
49296
|
+
}
|
|
49297
|
+
if (result === "none") {
|
|
49298
|
+
emittedGameEnd.current = false;
|
|
49299
|
+
}
|
|
49300
|
+
const towerPositions = React83.useMemo(() => new Set(towers.map((t2) => `${t2.x},${t2.y}`)), [towers]);
|
|
49301
|
+
const pathPositions = React83.useMemo(() => new Set(path.map((p2) => `${p2.x},${p2.y}`)), [path]);
|
|
49302
|
+
const validMoves = React83.useMemo(() => {
|
|
49303
|
+
if (result !== "none" || gold < towerCost) return [];
|
|
49304
|
+
return tiles.filter(
|
|
49305
|
+
(t2) => t2.passable !== false && !towerPositions.has(`${t2.x},${t2.y}`) && !pathPositions.has(`${t2.x},${t2.y}`)
|
|
49306
|
+
).map((t2) => ({ x: t2.x, y: t2.y }));
|
|
49307
|
+
}, [tiles, towerPositions, pathPositions, result, gold, towerCost]);
|
|
49308
|
+
const isoTiles = React83.useMemo(() => tilesToIso(tiles), [tiles]);
|
|
49309
|
+
const towerUnits = React83.useMemo(() => towersToUnits(towers), [towers]);
|
|
49310
|
+
const creepUnits = React83.useMemo(() => creepsToUnits(creeps), [creeps]);
|
|
49311
|
+
const isoUnits = React83.useMemo(() => [...towerUnits, ...creepUnits], [towerUnits, creepUnits]);
|
|
49312
|
+
const pathFeatures = React83.useMemo(() => pathToFeatures(path), [path]);
|
|
49313
|
+
const handleTileClick = React83.useCallback((x, y) => {
|
|
49314
|
+
if (result !== "none") return;
|
|
49315
|
+
if (pathPositions.has(`${x},${y}`)) return;
|
|
49316
|
+
if (towerPositions.has(`${x},${y}`)) return;
|
|
49317
|
+
if (gold < towerCost) return;
|
|
49318
|
+
if (placeTowerEvent) {
|
|
49319
|
+
eventBus.emit(`UI:${placeTowerEvent}`, { x, y });
|
|
49320
|
+
}
|
|
49321
|
+
}, [result, pathPositions, towerPositions, gold, towerCost, placeTowerEvent, eventBus]);
|
|
49322
|
+
const handleStartWave = React83.useCallback(() => {
|
|
49323
|
+
if (startWaveEvent) {
|
|
49324
|
+
eventBus.emit(`UI:${startWaveEvent}`, { wave });
|
|
49325
|
+
}
|
|
49326
|
+
}, [startWaveEvent, wave, eventBus]);
|
|
49327
|
+
const handlePlayAgain = React83.useCallback(() => {
|
|
49328
|
+
if (playAgainEvent) {
|
|
49329
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
49330
|
+
}
|
|
49331
|
+
}, [playAgainEvent, eventBus]);
|
|
49332
|
+
const canPlaceTower = gold >= towerCost && result === "none";
|
|
49333
|
+
const canStartWave = !waveActive && result === "none";
|
|
49334
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("tower-defense-board relative min-h-[600px] bg-background", className), gap: "none", children: [
|
|
49335
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
49336
|
+
HStack,
|
|
49337
|
+
{
|
|
49338
|
+
className: "px-4 py-2 border-b border-border bg-surface",
|
|
49339
|
+
gap: "lg",
|
|
49340
|
+
align: "center",
|
|
49341
|
+
children: [
|
|
49342
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
49343
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "muted", children: t("td.gold") ?? "Gold" }),
|
|
49344
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", weight: "bold", className: "text-warning", children: gold })
|
|
49345
|
+
] }),
|
|
49346
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
49347
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "muted", children: t("td.lives") ?? "Lives" }),
|
|
49348
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", weight: "bold", className: lives <= 5 ? "text-error" : "text-success", children: lives })
|
|
49349
|
+
] }),
|
|
49350
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
49351
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "muted", children: t("td.wave") ?? "Wave" }),
|
|
49352
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "body2", weight: "bold", children: [
|
|
49353
|
+
wave,
|
|
49354
|
+
" / ",
|
|
49355
|
+
maxWaves
|
|
49356
|
+
] })
|
|
49357
|
+
] }),
|
|
49358
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "ml-auto", children: [
|
|
49359
|
+
canStartWave && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", size: "sm", onClick: handleStartWave, children: t("td.startWave") ?? "Start Wave" }),
|
|
49360
|
+
waveActive && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "text-warning animate-pulse", children: t("td.waveInProgress") ?? "Wave in progress\u2026" })
|
|
49361
|
+
] })
|
|
49362
|
+
]
|
|
49363
|
+
}
|
|
49364
|
+
),
|
|
49365
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative flex-1", children: [
|
|
49366
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
49367
|
+
IsometricCanvas_default,
|
|
49368
|
+
{
|
|
49369
|
+
tiles: isoTiles,
|
|
49370
|
+
units: isoUnits,
|
|
49371
|
+
features: pathFeatures,
|
|
49372
|
+
validMoves,
|
|
49373
|
+
hoveredTile,
|
|
49374
|
+
onTileClick: handleTileClick,
|
|
49375
|
+
onTileHover: (x, y) => setHoveredTile({ x, y }),
|
|
49376
|
+
onTileLeave: () => setHoveredTile(null),
|
|
49377
|
+
scale
|
|
49378
|
+
}
|
|
49379
|
+
),
|
|
49380
|
+
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)` }) })
|
|
49381
|
+
] }),
|
|
49382
|
+
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: [
|
|
49383
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
49384
|
+
Typography,
|
|
49385
|
+
{
|
|
49386
|
+
variant: "h2",
|
|
49387
|
+
className: cn(
|
|
49388
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
49389
|
+
result === "won" ? "text-warning" : "text-error"
|
|
49390
|
+
),
|
|
49391
|
+
children: result === "won" ? t("td.victory") ?? "Victory!" : t("td.defeat") ?? "Defeat!"
|
|
49392
|
+
}
|
|
49393
|
+
),
|
|
49394
|
+
/* @__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." }),
|
|
49395
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
49396
|
+
Button,
|
|
49397
|
+
{
|
|
49398
|
+
variant: "primary",
|
|
49399
|
+
className: "px-8 py-3 font-semibold",
|
|
49400
|
+
onClick: handlePlayAgain,
|
|
49401
|
+
children: t("td.playAgain") ?? "Play Again"
|
|
49402
|
+
}
|
|
49403
|
+
)
|
|
49404
|
+
] }) })
|
|
49405
|
+
] });
|
|
49406
|
+
}
|
|
49407
|
+
var CDN6, TERRAIN_SPRITES, TOWER_SPRITE, CREEP_SPRITE;
|
|
49408
|
+
var init_TowerDefenseBoard = __esm({
|
|
49409
|
+
"components/game/organisms/TowerDefenseBoard.tsx"() {
|
|
49410
|
+
"use client";
|
|
49411
|
+
init_cn();
|
|
49412
|
+
init_useEventBus();
|
|
49413
|
+
init_Box();
|
|
49414
|
+
init_Button();
|
|
49415
|
+
init_Typography();
|
|
49416
|
+
init_Stack();
|
|
49417
|
+
init_IsometricCanvas();
|
|
49418
|
+
init_boardEntity();
|
|
49419
|
+
CDN6 = "https://almadar-kflow-assets.web.app/shared/";
|
|
49420
|
+
TERRAIN_SPRITES = {
|
|
49421
|
+
ground: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_01.png`,
|
|
49422
|
+
path: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_09.png`,
|
|
49423
|
+
wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`
|
|
49424
|
+
};
|
|
49425
|
+
TOWER_SPRITE = `${CDN6}units/guardian.png`;
|
|
49426
|
+
CREEP_SPRITE = `${CDN6}units/scrapper.png`;
|
|
49427
|
+
TowerDefenseBoard.displayName = "TowerDefenseBoard";
|
|
49428
|
+
}
|
|
49429
|
+
});
|
|
49430
|
+
function TowerDefenseTemplate({
|
|
49431
|
+
entity,
|
|
49432
|
+
title = "Tower Defense",
|
|
49433
|
+
tiles,
|
|
49434
|
+
path,
|
|
49435
|
+
towers,
|
|
49436
|
+
gold,
|
|
49437
|
+
lives,
|
|
49438
|
+
maxWaves,
|
|
49439
|
+
towerCost,
|
|
49440
|
+
scale,
|
|
49441
|
+
placeTowerEvent,
|
|
49442
|
+
startWaveEvent,
|
|
49443
|
+
playAgainEvent,
|
|
49444
|
+
gameEndEvent,
|
|
49445
|
+
className
|
|
49446
|
+
}) {
|
|
49447
|
+
const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
|
|
49448
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49449
|
+
Box,
|
|
49450
|
+
{
|
|
49451
|
+
display: "flex",
|
|
49452
|
+
fullHeight: true,
|
|
49453
|
+
className: cn("tower-defense-template flex-col", className),
|
|
49454
|
+
children: [
|
|
49455
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
49456
|
+
HStack,
|
|
49457
|
+
{
|
|
49458
|
+
gap: "sm",
|
|
49459
|
+
align: "center",
|
|
49460
|
+
className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
|
|
49461
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title })
|
|
49462
|
+
}
|
|
49463
|
+
),
|
|
49464
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
49465
|
+
TowerDefenseBoard,
|
|
49466
|
+
{
|
|
49467
|
+
entity: resolved,
|
|
49468
|
+
tiles,
|
|
49469
|
+
path,
|
|
49470
|
+
towers,
|
|
49471
|
+
gold,
|
|
49472
|
+
lives,
|
|
49473
|
+
maxWaves,
|
|
49474
|
+
towerCost,
|
|
49475
|
+
scale,
|
|
49476
|
+
placeTowerEvent,
|
|
49477
|
+
startWaveEvent,
|
|
49478
|
+
playAgainEvent,
|
|
49479
|
+
gameEndEvent,
|
|
49480
|
+
className: "h-full"
|
|
49481
|
+
}
|
|
49482
|
+
) })
|
|
49483
|
+
]
|
|
49484
|
+
}
|
|
49485
|
+
);
|
|
49486
|
+
}
|
|
49487
|
+
var init_TowerDefenseTemplate = __esm({
|
|
49488
|
+
"components/game/templates/TowerDefenseTemplate.tsx"() {
|
|
49489
|
+
init_cn();
|
|
49490
|
+
init_Box();
|
|
49491
|
+
init_Stack();
|
|
49492
|
+
init_Typography();
|
|
49493
|
+
init_TowerDefenseBoard();
|
|
49494
|
+
TowerDefenseTemplate.displayName = "TowerDefenseTemplate";
|
|
49495
|
+
}
|
|
49496
|
+
});
|
|
46954
49497
|
function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
|
|
46955
49498
|
const eventBus = useEventBus();
|
|
46956
49499
|
const {
|
|
@@ -47149,17 +49692,12 @@ var init_UncontrolledBattleBoard = __esm({
|
|
|
47149
49692
|
}
|
|
47150
49693
|
});
|
|
47151
49694
|
function heroPosition(h) {
|
|
49695
|
+
if ("position" in h && h.position != null) return h.position;
|
|
47152
49696
|
return vec2(h.position);
|
|
47153
49697
|
}
|
|
47154
|
-
function heroOwner(h) {
|
|
47155
|
-
return str(h.owner);
|
|
47156
|
-
}
|
|
47157
49698
|
function heroMovement(h) {
|
|
47158
49699
|
return num(h.movement);
|
|
47159
49700
|
}
|
|
47160
|
-
function hexPassable(h) {
|
|
47161
|
-
return h.passable !== false;
|
|
47162
|
-
}
|
|
47163
49701
|
function defaultIsInRange(from, to, range) {
|
|
47164
49702
|
return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
|
|
47165
49703
|
}
|
|
@@ -47195,38 +49733,41 @@ function WorldMapBoard({
|
|
|
47195
49733
|
}) {
|
|
47196
49734
|
const eventBus = useEventBus();
|
|
47197
49735
|
const resolved = boardEntity(entity);
|
|
47198
|
-
const
|
|
47199
|
-
const
|
|
49736
|
+
const entityUnits = rows(resolved?.units);
|
|
49737
|
+
const entityTiles = rows(resolved?.tiles);
|
|
47200
49738
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
47201
49739
|
const selectedHeroId = resolved?.selectedHeroId ?? null;
|
|
47202
49740
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
47203
49741
|
const backgroundImage = resolved?.backgroundImage;
|
|
47204
49742
|
const [hoveredTile, setHoveredTile] = React83.useState(null);
|
|
47205
|
-
const selectedHero = React83.useMemo(
|
|
47206
|
-
() => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
|
|
47207
|
-
[heroes, selectedHeroId]
|
|
47208
|
-
);
|
|
47209
49743
|
const derivedTiles = React83.useMemo(
|
|
47210
|
-
() =>
|
|
47211
|
-
x: num(
|
|
47212
|
-
y: num(
|
|
47213
|
-
terrain: str(
|
|
47214
|
-
terrainSprite:
|
|
49744
|
+
() => entityTiles.map((t) => ({
|
|
49745
|
+
x: num(t.x),
|
|
49746
|
+
y: num(t.y),
|
|
49747
|
+
terrain: str(t.terrain),
|
|
49748
|
+
terrainSprite: t.terrainSprite == null ? void 0 : str(t.terrainSprite),
|
|
49749
|
+
passable: t.passable !== false
|
|
47215
49750
|
})),
|
|
47216
|
-
[
|
|
49751
|
+
[entityTiles]
|
|
47217
49752
|
);
|
|
47218
49753
|
const tiles = propTiles ?? derivedTiles;
|
|
47219
49754
|
const baseUnits = React83.useMemo(
|
|
47220
|
-
() => propUnits ??
|
|
47221
|
-
id: str(
|
|
47222
|
-
position: heroPosition(
|
|
47223
|
-
name: str(
|
|
47224
|
-
|
|
47225
|
-
|
|
47226
|
-
|
|
47227
|
-
|
|
49755
|
+
() => propUnits ?? entityUnits.map((u) => ({
|
|
49756
|
+
id: str(u.id),
|
|
49757
|
+
position: heroPosition(u),
|
|
49758
|
+
name: str(u.name),
|
|
49759
|
+
// lolo uses `team` field (not `owner`)
|
|
49760
|
+
team: str(u.team) === "enemy" ? "enemy" : "player",
|
|
49761
|
+
health: num(u.health) || 100,
|
|
49762
|
+
maxHealth: num(u.maxHealth) || 100,
|
|
49763
|
+
sprite: u.sprite == null ? void 0 : str(u.sprite)
|
|
47228
49764
|
})),
|
|
47229
|
-
[
|
|
49765
|
+
[entityUnits, propUnits]
|
|
49766
|
+
);
|
|
49767
|
+
const gameUnits = baseUnits;
|
|
49768
|
+
const selectedHero = React83.useMemo(
|
|
49769
|
+
() => gameUnits.find((u) => u.id === selectedHeroId) ?? null,
|
|
49770
|
+
[gameUnits, selectedHeroId]
|
|
47230
49771
|
);
|
|
47231
49772
|
const MOVE_SPEED_MS_PER_TILE = 300;
|
|
47232
49773
|
const movementAnimRef = React83.useRef(null);
|
|
@@ -47272,51 +49813,50 @@ function WorldMapBoard({
|
|
|
47272
49813
|
const validMoves = React83.useMemo(() => {
|
|
47273
49814
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
47274
49815
|
const sp = heroPosition(selectedHero);
|
|
47275
|
-
const
|
|
49816
|
+
const sTeam = str(selectedHero.team);
|
|
47276
49817
|
const range = heroMovement(selectedHero);
|
|
47277
49818
|
const moves = [];
|
|
47278
|
-
|
|
47279
|
-
const
|
|
47280
|
-
const
|
|
47281
|
-
if (
|
|
47282
|
-
if (
|
|
47283
|
-
if (!isInRange(sp, { x:
|
|
47284
|
-
if (
|
|
47285
|
-
const
|
|
47286
|
-
return
|
|
49819
|
+
tiles.forEach((t) => {
|
|
49820
|
+
const tx = t.x;
|
|
49821
|
+
const ty = t.y;
|
|
49822
|
+
if (t.passable === false) return;
|
|
49823
|
+
if (tx === sp.x && ty === sp.y) return;
|
|
49824
|
+
if (!isInRange(sp, { x: tx, y: ty }, range)) return;
|
|
49825
|
+
if (gameUnits.some((u) => {
|
|
49826
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
49827
|
+
return up.x === tx && up.y === ty && str(u.team) === sTeam;
|
|
47287
49828
|
})) return;
|
|
47288
|
-
moves.push({ x:
|
|
49829
|
+
moves.push({ x: tx, y: ty });
|
|
47289
49830
|
});
|
|
47290
49831
|
return moves;
|
|
47291
|
-
}, [selectedHero,
|
|
49832
|
+
}, [selectedHero, tiles, gameUnits, isInRange]);
|
|
47292
49833
|
const attackTargets = React83.useMemo(() => {
|
|
47293
49834
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
47294
49835
|
const sp = heroPosition(selectedHero);
|
|
47295
|
-
const
|
|
49836
|
+
const sTeam = str(selectedHero.team);
|
|
47296
49837
|
const range = heroMovement(selectedHero);
|
|
47297
|
-
return
|
|
47298
|
-
}, [selectedHero,
|
|
47299
|
-
const maxY = Math.max(...
|
|
49838
|
+
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 });
|
|
49839
|
+
}, [selectedHero, gameUnits, isInRange]);
|
|
49840
|
+
const maxY = Math.max(...tiles.map((t) => t.y), 0);
|
|
47300
49841
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
47301
49842
|
const tileToScreen = React83.useCallback(
|
|
47302
49843
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
47303
49844
|
[scale, baseOffsetX]
|
|
47304
49845
|
);
|
|
47305
49846
|
const hoveredHex = React83.useMemo(
|
|
47306
|
-
() => hoveredTile ?
|
|
47307
|
-
[hoveredTile,
|
|
49847
|
+
() => hoveredTile ? tiles.find((t) => t.x === hoveredTile.x && t.y === hoveredTile.y) ?? null : null,
|
|
49848
|
+
[hoveredTile, tiles]
|
|
47308
49849
|
);
|
|
47309
49850
|
const hoveredHero = React83.useMemo(
|
|
47310
|
-
() => hoveredTile ?
|
|
47311
|
-
const
|
|
47312
|
-
return
|
|
49851
|
+
() => hoveredTile ? gameUnits.find((u) => {
|
|
49852
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
49853
|
+
return up.x === hoveredTile.x && up.y === hoveredTile.y;
|
|
47313
49854
|
}) ?? null : null,
|
|
47314
|
-
[hoveredTile,
|
|
49855
|
+
[hoveredTile, gameUnits]
|
|
47315
49856
|
);
|
|
47316
49857
|
const handleTileClick = React83.useCallback((x, y) => {
|
|
47317
49858
|
if (movementAnimRef.current) return;
|
|
47318
|
-
const
|
|
47319
|
-
if (!hex) return;
|
|
49859
|
+
const tile = tiles.find((t) => t.x === x && t.y === y);
|
|
47320
49860
|
if (tileClickEvent) {
|
|
47321
49861
|
eventBus.emit(`UI:${tileClickEvent}`, { x, y });
|
|
47322
49862
|
}
|
|
@@ -47327,38 +49867,39 @@ function WorldMapBoard({
|
|
|
47327
49867
|
if (heroMoveEvent) {
|
|
47328
49868
|
eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
|
|
47329
49869
|
}
|
|
47330
|
-
const feature = str(
|
|
49870
|
+
const feature = tile ? str(tile.feature) : "";
|
|
47331
49871
|
if (feature && feature !== "none") {
|
|
47332
|
-
|
|
49872
|
+
const tileRow = tile;
|
|
49873
|
+
onFeatureEnter?.(heroId, tileRow);
|
|
47333
49874
|
if (featureEnterEvent) {
|
|
47334
|
-
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
|
|
49875
|
+
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex: tileRow });
|
|
47335
49876
|
}
|
|
47336
49877
|
}
|
|
47337
49878
|
});
|
|
47338
49879
|
return;
|
|
47339
49880
|
}
|
|
47340
|
-
const enemy =
|
|
47341
|
-
const
|
|
47342
|
-
return
|
|
49881
|
+
const enemy = gameUnits.find((u) => {
|
|
49882
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
49883
|
+
return up.x === x && up.y === y && str(u.team) === "enemy";
|
|
47343
49884
|
});
|
|
47344
49885
|
if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
|
|
47345
49886
|
const attackerId = str(selectedHero.id);
|
|
47346
|
-
const defenderId =
|
|
49887
|
+
const defenderId = enemy.id;
|
|
47347
49888
|
onBattleEncounter?.(attackerId, defenderId);
|
|
47348
49889
|
if (battleEncounterEvent) {
|
|
47349
49890
|
eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
|
|
47350
49891
|
}
|
|
47351
49892
|
}
|
|
47352
|
-
}, [
|
|
49893
|
+
}, [tiles, gameUnits, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
|
|
47353
49894
|
const handleUnitClick = React83.useCallback((unitId) => {
|
|
47354
|
-
const
|
|
47355
|
-
if (
|
|
49895
|
+
const unit = gameUnits.find((u) => u.id === unitId);
|
|
49896
|
+
if (unit && (str(unit.team) === "player" || allowMoveAllHeroes)) {
|
|
47356
49897
|
onHeroSelect?.(unitId);
|
|
47357
49898
|
if (heroSelectEvent) {
|
|
47358
49899
|
eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
|
|
47359
49900
|
}
|
|
47360
49901
|
}
|
|
47361
|
-
}, [
|
|
49902
|
+
}, [gameUnits, onHeroSelect, allowMoveAllHeroes, eventBus, heroSelectEvent]);
|
|
47362
49903
|
const selectHero = React83.useCallback((id) => {
|
|
47363
49904
|
onHeroSelect?.(id);
|
|
47364
49905
|
if (heroSelectEvent) {
|
|
@@ -47462,48 +50003,48 @@ function WorldMapTemplate({
|
|
|
47462
50003
|
}
|
|
47463
50004
|
);
|
|
47464
50005
|
}
|
|
47465
|
-
var
|
|
50006
|
+
var CDN7, DEFAULT_WORLDMAP_TILES, DEFAULT_WORLDMAP_UNITS, DEFAULT_WORLDMAP_FEATURES, DEFAULT_WORLDMAP_MANIFEST;
|
|
47466
50007
|
var init_WorldMapTemplate = __esm({
|
|
47467
50008
|
"components/game/templates/WorldMapTemplate.tsx"() {
|
|
47468
50009
|
init_WorldMapBoard();
|
|
47469
|
-
|
|
50010
|
+
CDN7 = "https://almadar-kflow-assets.web.app/shared";
|
|
47470
50011
|
DEFAULT_WORLDMAP_TILES = [
|
|
47471
|
-
{ x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47472
|
-
{ x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47473
|
-
{ x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${
|
|
47474
|
-
{ x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47475
|
-
{ x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47476
|
-
{ x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47477
|
-
{ x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
47478
|
-
{ x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
47479
|
-
{ x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
47480
|
-
{ x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${
|
|
47481
|
-
{ x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${
|
|
47482
|
-
{ x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
47483
|
-
{ x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
47484
|
-
{ x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
47485
|
-
{ x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47486
|
-
{ x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47487
|
-
{ x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
47488
|
-
{ x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
47489
|
-
{ x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
47490
|
-
{ x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47491
|
-
{ x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47492
|
-
{ x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${
|
|
47493
|
-
{ x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${
|
|
47494
|
-
{ x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47495
|
-
{ x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
50012
|
+
{ x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50013
|
+
{ x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50014
|
+
{ x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
50015
|
+
{ x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50016
|
+
{ x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50017
|
+
{ x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50018
|
+
{ x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50019
|
+
{ x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50020
|
+
{ x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50021
|
+
{ x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
50022
|
+
{ x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
50023
|
+
{ x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50024
|
+
{ x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
|
|
50025
|
+
{ x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50026
|
+
{ x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50027
|
+
{ x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50028
|
+
{ x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50029
|
+
{ x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50030
|
+
{ x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
|
|
50031
|
+
{ x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50032
|
+
{ x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50033
|
+
{ x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
50034
|
+
{ x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50035
|
+
{ x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50036
|
+
{ x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` }
|
|
47496
50037
|
];
|
|
47497
50038
|
DEFAULT_WORLDMAP_UNITS = [
|
|
47498
|
-
{ id: "h1", position: { x: 1, y: 1 }, unitType: "hero", name: "Amir", team: "player", health: 10, maxHealth: 10, sprite: `${
|
|
47499
|
-
{ id: "h2", position: { x: 3, y: 3 }, unitType: "scout", name: "Archivist", team: "player", health: 10, maxHealth: 10, sprite: `${
|
|
50039
|
+
{ 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` },
|
|
50040
|
+
{ 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` }
|
|
47500
50041
|
];
|
|
47501
50042
|
DEFAULT_WORLDMAP_FEATURES = [
|
|
47502
|
-
{ id: "f1", x: 2, y: 2, type: "capital", sprite: `${
|
|
47503
|
-
{ id: "f2", x: 4, y: 2, type: "power_node", sprite: `${
|
|
50043
|
+
{ id: "f1", x: 2, y: 2, type: "capital", sprite: `${CDN7}/scenes/world/capital.png` },
|
|
50044
|
+
{ id: "f2", x: 4, y: 2, type: "power_node", sprite: `${CDN7}/world-map/power_node.png` }
|
|
47504
50045
|
];
|
|
47505
50046
|
DEFAULT_WORLDMAP_MANIFEST = {
|
|
47506
|
-
baseUrl:
|
|
50047
|
+
baseUrl: CDN7,
|
|
47507
50048
|
terrains: {
|
|
47508
50049
|
grass: "/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png",
|
|
47509
50050
|
water: "/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png",
|
|
@@ -47548,7 +50089,7 @@ function lazyThree(name, loader) {
|
|
|
47548
50089
|
ThreeWrapper.displayName = `Lazy(${name})`;
|
|
47549
50090
|
return ThreeWrapper;
|
|
47550
50091
|
}
|
|
47551
|
-
var ThreeBoundary, FeatureRenderer,
|
|
50092
|
+
var ThreeBoundary, FeatureRenderer, GameCanvas3D2, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
|
|
47552
50093
|
var init_component_registry_generated = __esm({
|
|
47553
50094
|
"components/core/organisms/component-registry.generated.ts"() {
|
|
47554
50095
|
init_AboutPageTemplate();
|
|
@@ -47658,6 +50199,7 @@ var init_component_registry_generated = __esm({
|
|
|
47658
50199
|
init_FormSectionHeader();
|
|
47659
50200
|
init_GameAudioProvider();
|
|
47660
50201
|
init_GameAudioToggle();
|
|
50202
|
+
init_GameBoard3D();
|
|
47661
50203
|
init_GameCanvas2D();
|
|
47662
50204
|
init_GameHud();
|
|
47663
50205
|
init_GameMenu();
|
|
@@ -47715,7 +50257,9 @@ var init_component_registry_generated = __esm({
|
|
|
47715
50257
|
init_PageHeader();
|
|
47716
50258
|
init_Pagination();
|
|
47717
50259
|
init_PatternTile();
|
|
50260
|
+
init_PlatformerBoard();
|
|
47718
50261
|
init_PlatformerCanvas();
|
|
50262
|
+
init_PlatformerTemplate();
|
|
47719
50263
|
init_Popover();
|
|
47720
50264
|
init_PositionedCanvas();
|
|
47721
50265
|
init_PowerupSlots();
|
|
@@ -47739,6 +50283,8 @@ var init_component_registry_generated = __esm({
|
|
|
47739
50283
|
init_ResourceBar();
|
|
47740
50284
|
init_ResourceCounter();
|
|
47741
50285
|
init_RichBlockEditor();
|
|
50286
|
+
init_RoguelikeBoard();
|
|
50287
|
+
init_RoguelikeTemplate();
|
|
47742
50288
|
init_RuntimeDebugger2();
|
|
47743
50289
|
init_ScaledDiagram();
|
|
47744
50290
|
init_ScoreBoard();
|
|
@@ -47811,6 +50357,8 @@ var init_component_registry_generated = __esm({
|
|
|
47811
50357
|
init_Toast();
|
|
47812
50358
|
init_ToastSlot();
|
|
47813
50359
|
init_Tooltip();
|
|
50360
|
+
init_TowerDefenseBoard();
|
|
50361
|
+
init_TowerDefenseTemplate();
|
|
47814
50362
|
init_TraitFrame();
|
|
47815
50363
|
init_TraitSlot();
|
|
47816
50364
|
init_TrendIndicator();
|
|
@@ -47855,7 +50403,7 @@ var init_component_registry_generated = __esm({
|
|
|
47855
50403
|
}
|
|
47856
50404
|
};
|
|
47857
50405
|
FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
|
|
47858
|
-
|
|
50406
|
+
GameCanvas3D2 = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
|
|
47859
50407
|
GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
47860
50408
|
GameCanvas3DCastleTemplate = lazyThree("GameCanvas3DCastleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
47861
50409
|
GameCanvas3DWorldMapTemplate = lazyThree("GameCanvas3DWorldMapTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
@@ -47974,8 +50522,9 @@ var init_component_registry_generated = __esm({
|
|
|
47974
50522
|
"FormSectionHeader": FormSectionHeader,
|
|
47975
50523
|
"GameAudioProvider": GameAudioProvider,
|
|
47976
50524
|
"GameAudioToggle": GameAudioToggle,
|
|
50525
|
+
"GameBoard3D": GameBoard3D,
|
|
47977
50526
|
"GameCanvas2D": GameCanvas2D,
|
|
47978
|
-
"GameCanvas3D":
|
|
50527
|
+
"GameCanvas3D": GameCanvas3D2,
|
|
47979
50528
|
"GameCanvas3DBattleTemplate": GameCanvas3DBattleTemplate,
|
|
47980
50529
|
"GameCanvas3DCastleTemplate": GameCanvas3DCastleTemplate,
|
|
47981
50530
|
"GameCanvas3DWorldMapTemplate": GameCanvas3DWorldMapTemplate,
|
|
@@ -48037,7 +50586,9 @@ var init_component_registry_generated = __esm({
|
|
|
48037
50586
|
"PageHeader": PageHeader,
|
|
48038
50587
|
"Pagination": Pagination,
|
|
48039
50588
|
"PatternTile": PatternTile,
|
|
50589
|
+
"PlatformerBoard": PlatformerBoard,
|
|
48040
50590
|
"PlatformerCanvas": PlatformerCanvas,
|
|
50591
|
+
"PlatformerTemplate": PlatformerTemplate,
|
|
48041
50592
|
"Popover": Popover,
|
|
48042
50593
|
"PositionedCanvas": PositionedCanvas,
|
|
48043
50594
|
"PowerupSlots": PowerupSlots,
|
|
@@ -48061,6 +50612,8 @@ var init_component_registry_generated = __esm({
|
|
|
48061
50612
|
"ResourceBar": ResourceBar,
|
|
48062
50613
|
"ResourceCounter": ResourceCounter,
|
|
48063
50614
|
"RichBlockEditor": RichBlockEditor,
|
|
50615
|
+
"RoguelikeBoard": RoguelikeBoard,
|
|
50616
|
+
"RoguelikeTemplate": RoguelikeTemplate,
|
|
48064
50617
|
"RuntimeDebugger": RuntimeDebugger,
|
|
48065
50618
|
"ScaledDiagram": ScaledDiagram,
|
|
48066
50619
|
"ScoreBoard": ScoreBoard,
|
|
@@ -48136,6 +50689,8 @@ var init_component_registry_generated = __esm({
|
|
|
48136
50689
|
"Toast": Toast,
|
|
48137
50690
|
"ToastSlot": ToastSlot,
|
|
48138
50691
|
"Tooltip": Tooltip,
|
|
50692
|
+
"TowerDefenseBoard": TowerDefenseBoard,
|
|
50693
|
+
"TowerDefenseTemplate": TowerDefenseTemplate,
|
|
48139
50694
|
"TraitFrame": TraitFrame,
|
|
48140
50695
|
"TraitSlot": TraitSlot,
|
|
48141
50696
|
"TrendIndicator": TrendIndicator,
|
|
@@ -49307,7 +51862,7 @@ init_UISlotRenderer();
|
|
|
49307
51862
|
// providers/VerificationProvider.tsx
|
|
49308
51863
|
init_useEventBus();
|
|
49309
51864
|
init_verificationRegistry();
|
|
49310
|
-
var
|
|
51865
|
+
var log13 = logger.createLogger("almadar:verify");
|
|
49311
51866
|
var DISPATCH_SUFFIX = ":DISPATCH";
|
|
49312
51867
|
var SUCCESS_SUFFIX = ":SUCCESS";
|
|
49313
51868
|
var ERROR_SUFFIX = ":ERROR";
|
|
@@ -49354,7 +51909,7 @@ function VerificationProvider({
|
|
|
49354
51909
|
const verificationProviderLifecycleListener = (evt) => {
|
|
49355
51910
|
const parsed = parseLifecycleEvent(evt.type);
|
|
49356
51911
|
if (!parsed) return;
|
|
49357
|
-
|
|
51912
|
+
log13.debug("lifecycle:event", { kind: parsed.kind, traitName: parsed.traitName, event: parsed.event, type: evt.type });
|
|
49358
51913
|
const payload = evt.payload ?? {};
|
|
49359
51914
|
if (parsed.kind === "dispatch") {
|
|
49360
51915
|
const key = `${parsed.traitName}:${String(payload["event"] ?? "")}`;
|
|
@@ -49409,7 +51964,7 @@ function VerificationProvider({
|
|
|
49409
51964
|
},
|
|
49410
51965
|
timestamp: Date.now()
|
|
49411
51966
|
});
|
|
49412
|
-
|
|
51967
|
+
log13.debug("transition:success", { trait: parsed.traitName, event: parsed.event, from: pending?.from, to: newState, effectCount: effects.length });
|
|
49413
51968
|
} else if (parsed.kind === "error" && parsed.event) {
|
|
49414
51969
|
const key = `${parsed.traitName}:${parsed.event}`;
|
|
49415
51970
|
const pending = pendingRef.current.get(key);
|
|
@@ -49439,7 +51994,7 @@ function VerificationProvider({
|
|
|
49439
51994
|
},
|
|
49440
51995
|
timestamp: Date.now()
|
|
49441
51996
|
});
|
|
49442
|
-
|
|
51997
|
+
log13.warn("transition:error", { trait: parsed.traitName, event: parsed.event, from: fromState, error: errorMsg });
|
|
49443
51998
|
}
|
|
49444
51999
|
};
|
|
49445
52000
|
Object.defineProperty(verificationProviderLifecycleListener, "name", {
|