@almadar/ui 5.48.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 +2681 -148
- package/dist/avl/index.css +504 -0
- package/dist/avl/index.js +2681 -149
- 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/ControlButton.d.ts +4 -2
- package/dist/components/game/atoms/DamageNumber.d.ts +4 -1
- package/dist/components/game/atoms/ScoreDisplay.d.ts +4 -1
- package/dist/components/game/atoms/TurnIndicator.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 +1371 -283
- package/dist/components/game/molecules/three/index.js +1372 -284
- 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/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 +2674 -127
- package/dist/components/index.css +504 -0
- package/dist/components/index.js +2668 -129
- package/dist/docs/index.css +504 -0
- package/dist/providers/index.cjs +2664 -131
- package/dist/providers/index.css +504 -0
- package/dist/providers/index.js +2664 -132
- package/dist/runtime/index.cjs +2665 -132
- package/dist/runtime/index.css +504 -0
- package/dist/runtime/index.js +2665 -133
- package/package.json +1 -1
package/dist/avl/index.cjs
CHANGED
|
@@ -39,6 +39,12 @@ var core = require('@almadar/core');
|
|
|
39
39
|
var core$1 = require('@dnd-kit/core');
|
|
40
40
|
var sortable = require('@dnd-kit/sortable');
|
|
41
41
|
var utilities = require('@dnd-kit/utilities');
|
|
42
|
+
var THREE = require('three');
|
|
43
|
+
var GLTFLoader_js = require('three/examples/jsm/loaders/GLTFLoader.js');
|
|
44
|
+
var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
|
|
45
|
+
var GLTFLoader = require('three/examples/jsm/loaders/GLTFLoader');
|
|
46
|
+
var fiber = require('@react-three/fiber');
|
|
47
|
+
var drei = require('@react-three/drei');
|
|
42
48
|
var patterns = require('@almadar/patterns');
|
|
43
49
|
var OrbitalServerRuntime = require('@almadar/runtime/OrbitalServerRuntime');
|
|
44
50
|
var runtime = require('@almadar/runtime');
|
|
@@ -88,6 +94,7 @@ var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
|
|
|
88
94
|
var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
|
|
89
95
|
var remarkMath__default = /*#__PURE__*/_interopDefault(remarkMath);
|
|
90
96
|
var rehypeKatex__default = /*#__PURE__*/_interopDefault(rehypeKatex);
|
|
97
|
+
var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
|
|
91
98
|
|
|
92
99
|
var __defProp = Object.defineProperty;
|
|
93
100
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -3219,8 +3226,8 @@ var init_Typography = __esm({
|
|
|
3219
3226
|
children
|
|
3220
3227
|
}) => {
|
|
3221
3228
|
const variant = variantProp ?? (level ? `h${level}` : "body1");
|
|
3222
|
-
const
|
|
3223
|
-
const Comp =
|
|
3229
|
+
const Component2 = as || defaultElements[variant];
|
|
3230
|
+
const Comp = Component2;
|
|
3224
3231
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3225
3232
|
Comp,
|
|
3226
3233
|
{
|
|
@@ -3529,7 +3536,7 @@ var init_Box = __esm({
|
|
|
3529
3536
|
position,
|
|
3530
3537
|
className,
|
|
3531
3538
|
children,
|
|
3532
|
-
as:
|
|
3539
|
+
as: Component2 = "div",
|
|
3533
3540
|
action,
|
|
3534
3541
|
actionPayload,
|
|
3535
3542
|
hoverEvent,
|
|
@@ -3561,7 +3568,7 @@ var init_Box = __esm({
|
|
|
3561
3568
|
}, [hoverEvent, eventBus, onMouseLeave]);
|
|
3562
3569
|
const isClickable = action || onClick;
|
|
3563
3570
|
return React91__namespace.default.createElement(
|
|
3564
|
-
|
|
3571
|
+
Component2,
|
|
3565
3572
|
{
|
|
3566
3573
|
ref,
|
|
3567
3574
|
className: cn(
|
|
@@ -3637,7 +3644,7 @@ var init_Stack = __esm({
|
|
|
3637
3644
|
className,
|
|
3638
3645
|
style,
|
|
3639
3646
|
children,
|
|
3640
|
-
as:
|
|
3647
|
+
as: Component2 = "div",
|
|
3641
3648
|
onClick,
|
|
3642
3649
|
onKeyDown,
|
|
3643
3650
|
role,
|
|
@@ -3655,7 +3662,7 @@ var init_Stack = __esm({
|
|
|
3655
3662
|
};
|
|
3656
3663
|
const isHorizontal = direction === "horizontal";
|
|
3657
3664
|
const directionClass = responsive && isHorizontal ? reverse ? "flex-col-reverse md:flex-row-reverse" : "flex-col md:flex-row" : isHorizontal ? reverse ? "flex-row-reverse" : "flex-row" : reverse ? "flex-col-reverse" : "flex-col";
|
|
3658
|
-
const Comp =
|
|
3665
|
+
const Comp = Component2;
|
|
3659
3666
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3660
3667
|
Comp,
|
|
3661
3668
|
{
|
|
@@ -4569,9 +4576,9 @@ function resolvePhosphor(name, weight, family) {
|
|
|
4569
4576
|
(lib) => {
|
|
4570
4577
|
const PhosphorComp = lib[target];
|
|
4571
4578
|
if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
|
|
4572
|
-
const
|
|
4579
|
+
const Component2 = PhosphorComp;
|
|
4573
4580
|
const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4574
|
-
|
|
4581
|
+
Component2,
|
|
4575
4582
|
{
|
|
4576
4583
|
weight,
|
|
4577
4584
|
className: props.className,
|
|
@@ -4595,9 +4602,9 @@ function resolveTabler(name, family) {
|
|
|
4595
4602
|
(lib) => {
|
|
4596
4603
|
const TablerComp = lib[target];
|
|
4597
4604
|
if (!TablerComp || typeof TablerComp !== "object") return null;
|
|
4598
|
-
const
|
|
4605
|
+
const Component2 = TablerComp;
|
|
4599
4606
|
const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4600
|
-
|
|
4607
|
+
Component2,
|
|
4601
4608
|
{
|
|
4602
4609
|
stroke: props.strokeWidth ?? 1.5,
|
|
4603
4610
|
className: props.className,
|
|
@@ -4621,9 +4628,9 @@ function resolveFa(name, family) {
|
|
|
4621
4628
|
(lib) => {
|
|
4622
4629
|
const FaComp = lib[target];
|
|
4623
4630
|
if (!FaComp || typeof FaComp !== "function") return null;
|
|
4624
|
-
const
|
|
4631
|
+
const Component2 = FaComp;
|
|
4625
4632
|
const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4626
|
-
|
|
4633
|
+
Component2,
|
|
4627
4634
|
{
|
|
4628
4635
|
className: props.className,
|
|
4629
4636
|
style: props.style,
|
|
@@ -8015,10 +8022,10 @@ var init_Center = __esm({
|
|
|
8015
8022
|
className,
|
|
8016
8023
|
style,
|
|
8017
8024
|
children,
|
|
8018
|
-
as:
|
|
8025
|
+
as: Component2 = "div"
|
|
8019
8026
|
}) => {
|
|
8020
8027
|
const mergedStyle = minHeight ? { minHeight, ...style } : style;
|
|
8021
|
-
const Comp =
|
|
8028
|
+
const Comp = Component2;
|
|
8022
8029
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8023
8030
|
Comp,
|
|
8024
8031
|
{
|
|
@@ -10812,6 +10819,7 @@ var init_HealthBar = __esm({
|
|
|
10812
10819
|
}
|
|
10813
10820
|
});
|
|
10814
10821
|
function ScoreDisplay({
|
|
10822
|
+
assetUrl = DEFAULT_ASSET_URL,
|
|
10815
10823
|
value,
|
|
10816
10824
|
label,
|
|
10817
10825
|
icon,
|
|
@@ -10858,14 +10866,24 @@ function ScoreDisplay({
|
|
|
10858
10866
|
className
|
|
10859
10867
|
),
|
|
10860
10868
|
children: [
|
|
10861
|
-
|
|
10869
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
10870
|
+
"img",
|
|
10871
|
+
{
|
|
10872
|
+
src: assetUrl,
|
|
10873
|
+
alt: "",
|
|
10874
|
+
width: 20,
|
|
10875
|
+
height: 20,
|
|
10876
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
10877
|
+
className: "flex-shrink-0"
|
|
10878
|
+
}
|
|
10879
|
+
) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon }) }) : null,
|
|
10862
10880
|
label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: label }),
|
|
10863
10881
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: formattedValue })
|
|
10864
10882
|
]
|
|
10865
10883
|
}
|
|
10866
10884
|
);
|
|
10867
10885
|
}
|
|
10868
|
-
var sizeMap2;
|
|
10886
|
+
var sizeMap2, DEFAULT_ASSET_URL;
|
|
10869
10887
|
var init_ScoreDisplay = __esm({
|
|
10870
10888
|
"components/game/atoms/ScoreDisplay.tsx"() {
|
|
10871
10889
|
"use client";
|
|
@@ -10877,10 +10895,12 @@ var init_ScoreDisplay = __esm({
|
|
|
10877
10895
|
lg: "text-2xl",
|
|
10878
10896
|
xl: "text-4xl"
|
|
10879
10897
|
};
|
|
10898
|
+
DEFAULT_ASSET_URL = "https://almadar-kflow-assets.web.app/shared/effects/particles/star_01.png";
|
|
10880
10899
|
ScoreDisplay.displayName = "ScoreDisplay";
|
|
10881
10900
|
}
|
|
10882
10901
|
});
|
|
10883
10902
|
function ControlButton({
|
|
10903
|
+
assetUrl = DEFAULT_ASSET_URL2,
|
|
10884
10904
|
label,
|
|
10885
10905
|
icon,
|
|
10886
10906
|
size = "md",
|
|
@@ -10927,7 +10947,7 @@ function ControlButton({
|
|
|
10927
10947
|
},
|
|
10928
10948
|
[isPressed, releaseEvent, eventBus, onRelease]
|
|
10929
10949
|
);
|
|
10930
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
10950
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10931
10951
|
"button",
|
|
10932
10952
|
{
|
|
10933
10953
|
type: "button",
|
|
@@ -10948,20 +10968,27 @@ function ControlButton({
|
|
|
10948
10968
|
disabled && "opacity-50 cursor-not-allowed",
|
|
10949
10969
|
className
|
|
10950
10970
|
),
|
|
10951
|
-
children:
|
|
10952
|
-
|
|
10953
|
-
|
|
10954
|
-
|
|
10955
|
-
|
|
10956
|
-
|
|
10957
|
-
|
|
10958
|
-
|
|
10959
|
-
|
|
10960
|
-
|
|
10971
|
+
children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
10972
|
+
"img",
|
|
10973
|
+
{
|
|
10974
|
+
src: assetUrl,
|
|
10975
|
+
alt: "",
|
|
10976
|
+
width: 24,
|
|
10977
|
+
height: 24,
|
|
10978
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
10979
|
+
className: "flex-shrink-0"
|
|
10980
|
+
}
|
|
10981
|
+
) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-2xl", children: typeof icon === "string" ? /^[a-zA-Z0-9-]+$/.test(icon) ? (() => {
|
|
10982
|
+
const I = resolveIcon(icon);
|
|
10983
|
+
return I ? /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-6 h-6" }) : null;
|
|
10984
|
+
})() : icon : /* @__PURE__ */ (() => {
|
|
10985
|
+
const I = icon;
|
|
10986
|
+
return /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-6 h-6" });
|
|
10987
|
+
})() }) : label ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: label }) : null
|
|
10961
10988
|
}
|
|
10962
10989
|
);
|
|
10963
10990
|
}
|
|
10964
|
-
var sizeMap3, shapeMap, variantMap;
|
|
10991
|
+
var sizeMap3, shapeMap, variantMap, DEFAULT_ASSET_URL2;
|
|
10965
10992
|
var init_ControlButton = __esm({
|
|
10966
10993
|
"components/game/atoms/ControlButton.tsx"() {
|
|
10967
10994
|
"use client";
|
|
@@ -10984,6 +11011,7 @@ var init_ControlButton = __esm({
|
|
|
10984
11011
|
secondary: "bg-secondary text-secondary-foreground border-border hover:bg-secondary-hover",
|
|
10985
11012
|
ghost: "bg-transparent text-foreground border-border hover:bg-muted"
|
|
10986
11013
|
};
|
|
11014
|
+
DEFAULT_ASSET_URL2 = "https://almadar-kflow-assets.web.app/shared/effects/particles/circle_01.png";
|
|
10987
11015
|
ControlButton.displayName = "ControlButton";
|
|
10988
11016
|
}
|
|
10989
11017
|
});
|
|
@@ -11063,7 +11091,7 @@ var init_Sprite = __esm({
|
|
|
11063
11091
|
}
|
|
11064
11092
|
});
|
|
11065
11093
|
function StateIndicator({
|
|
11066
|
-
assetUrl =
|
|
11094
|
+
assetUrl = DEFAULT_ASSET_URL3,
|
|
11067
11095
|
state = "idle",
|
|
11068
11096
|
label,
|
|
11069
11097
|
size = "md",
|
|
@@ -11102,13 +11130,13 @@ function StateIndicator({
|
|
|
11102
11130
|
}
|
|
11103
11131
|
);
|
|
11104
11132
|
}
|
|
11105
|
-
var
|
|
11133
|
+
var DEFAULT_ASSET_URL3, DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
11106
11134
|
var init_StateIndicator = __esm({
|
|
11107
11135
|
"components/game/atoms/StateIndicator.tsx"() {
|
|
11108
11136
|
init_Box();
|
|
11109
11137
|
init_Icon();
|
|
11110
11138
|
init_cn();
|
|
11111
|
-
|
|
11139
|
+
DEFAULT_ASSET_URL3 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
11112
11140
|
DEFAULT_STATE_STYLES = {
|
|
11113
11141
|
idle: { icon: "\u23F8", bgClass: "bg-muted" },
|
|
11114
11142
|
active: { icon: "\u25B6", bgClass: "bg-success" },
|
|
@@ -11183,7 +11211,7 @@ var init_TimerDisplay = __esm({
|
|
|
11183
11211
|
}
|
|
11184
11212
|
});
|
|
11185
11213
|
function ResourceCounter({
|
|
11186
|
-
assetUrl =
|
|
11214
|
+
assetUrl = DEFAULT_ASSET_URL4,
|
|
11187
11215
|
icon,
|
|
11188
11216
|
label = "Gold",
|
|
11189
11217
|
value = 250,
|
|
@@ -11226,7 +11254,7 @@ function ResourceCounter({
|
|
|
11226
11254
|
}
|
|
11227
11255
|
);
|
|
11228
11256
|
}
|
|
11229
|
-
var colorTokenClasses2,
|
|
11257
|
+
var colorTokenClasses2, DEFAULT_ASSET_URL4, sizeMap5;
|
|
11230
11258
|
var init_ResourceCounter = __esm({
|
|
11231
11259
|
"components/game/atoms/ResourceCounter.tsx"() {
|
|
11232
11260
|
init_cn();
|
|
@@ -11239,7 +11267,7 @@ var init_ResourceCounter = __esm({
|
|
|
11239
11267
|
error: "text-error",
|
|
11240
11268
|
muted: "text-muted-foreground"
|
|
11241
11269
|
};
|
|
11242
|
-
|
|
11270
|
+
DEFAULT_ASSET_URL4 = "https://almadar-kflow-assets.web.app/shared/world-map/gold_mine.png";
|
|
11243
11271
|
sizeMap5 = {
|
|
11244
11272
|
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm", img: 16 },
|
|
11245
11273
|
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base", img: 20 },
|
|
@@ -11249,7 +11277,7 @@ var init_ResourceCounter = __esm({
|
|
|
11249
11277
|
}
|
|
11250
11278
|
});
|
|
11251
11279
|
function ItemSlot({
|
|
11252
|
-
assetUrl =
|
|
11280
|
+
assetUrl = DEFAULT_ASSET_URL5,
|
|
11253
11281
|
icon = "sword",
|
|
11254
11282
|
label = "Iron Sword",
|
|
11255
11283
|
quantity,
|
|
@@ -11308,7 +11336,7 @@ function ItemSlot({
|
|
|
11308
11336
|
}
|
|
11309
11337
|
);
|
|
11310
11338
|
}
|
|
11311
|
-
var sizeMap6, rarityBorderMap, rarityGlowMap,
|
|
11339
|
+
var sizeMap6, rarityBorderMap, rarityGlowMap, DEFAULT_ASSET_URL5, assetSizeMap;
|
|
11312
11340
|
var init_ItemSlot = __esm({
|
|
11313
11341
|
"components/game/atoms/ItemSlot.tsx"() {
|
|
11314
11342
|
"use client";
|
|
@@ -11333,7 +11361,7 @@ var init_ItemSlot = __esm({
|
|
|
11333
11361
|
epic: "shadow-lg",
|
|
11334
11362
|
legendary: "shadow-lg"
|
|
11335
11363
|
};
|
|
11336
|
-
|
|
11364
|
+
DEFAULT_ASSET_URL5 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
11337
11365
|
assetSizeMap = {
|
|
11338
11366
|
sm: 28,
|
|
11339
11367
|
md: 40,
|
|
@@ -11343,6 +11371,7 @@ var init_ItemSlot = __esm({
|
|
|
11343
11371
|
}
|
|
11344
11372
|
});
|
|
11345
11373
|
function TurnIndicator({
|
|
11374
|
+
assetUrl = DEFAULT_ASSET_URL6,
|
|
11346
11375
|
currentTurn = 1,
|
|
11347
11376
|
maxTurns,
|
|
11348
11377
|
activeTeam,
|
|
@@ -11375,14 +11404,24 @@ function TurnIndicator({
|
|
|
11375
11404
|
] }),
|
|
11376
11405
|
activeTeam && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
11377
11406
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "|" }),
|
|
11378
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11407
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
11408
|
+
"img",
|
|
11409
|
+
{
|
|
11410
|
+
src: assetUrl,
|
|
11411
|
+
alt: "",
|
|
11412
|
+
width: 12,
|
|
11413
|
+
height: 12,
|
|
11414
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11415
|
+
className: "flex-shrink-0"
|
|
11416
|
+
}
|
|
11417
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("rounded-full bg-success/20", sizes.dot) }),
|
|
11379
11418
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-success", children: activeTeam })
|
|
11380
11419
|
] })
|
|
11381
11420
|
]
|
|
11382
11421
|
}
|
|
11383
11422
|
);
|
|
11384
11423
|
}
|
|
11385
|
-
var sizeMap7;
|
|
11424
|
+
var sizeMap7, DEFAULT_ASSET_URL6;
|
|
11386
11425
|
var init_TurnIndicator = __esm({
|
|
11387
11426
|
"components/game/atoms/TurnIndicator.tsx"() {
|
|
11388
11427
|
init_cn();
|
|
@@ -11391,6 +11430,7 @@ var init_TurnIndicator = __esm({
|
|
|
11391
11430
|
md: { wrapper: "text-sm gap-2 px-3 py-1", dot: "w-2 h-2" },
|
|
11392
11431
|
lg: { wrapper: "text-base gap-2.5 px-4 py-1.5", dot: "w-2.5 h-2.5" }
|
|
11393
11432
|
};
|
|
11433
|
+
DEFAULT_ASSET_URL6 = "https://almadar-kflow-assets.web.app/shared/effects/particles/symbol_01.png";
|
|
11394
11434
|
TurnIndicator.displayName = "TurnIndicator";
|
|
11395
11435
|
}
|
|
11396
11436
|
});
|
|
@@ -11406,7 +11446,7 @@ function getComboScale(combo) {
|
|
|
11406
11446
|
return "";
|
|
11407
11447
|
}
|
|
11408
11448
|
function ComboCounter({
|
|
11409
|
-
assetUrl =
|
|
11449
|
+
assetUrl = DEFAULT_ASSET_URL7,
|
|
11410
11450
|
combo = 5,
|
|
11411
11451
|
multiplier,
|
|
11412
11452
|
streak,
|
|
@@ -11451,11 +11491,11 @@ function ComboCounter({
|
|
|
11451
11491
|
}
|
|
11452
11492
|
);
|
|
11453
11493
|
}
|
|
11454
|
-
var
|
|
11494
|
+
var DEFAULT_ASSET_URL7, sizeMap8;
|
|
11455
11495
|
var init_ComboCounter = __esm({
|
|
11456
11496
|
"components/game/atoms/ComboCounter.tsx"() {
|
|
11457
11497
|
init_cn();
|
|
11458
|
-
|
|
11498
|
+
DEFAULT_ASSET_URL7 = "https://almadar-kflow-assets.web.app/shared/effects/flash/flash00.png";
|
|
11459
11499
|
sizeMap8 = {
|
|
11460
11500
|
sm: { combo: "text-lg", label: "text-xs", multiplier: "text-xs" },
|
|
11461
11501
|
md: { combo: "text-2xl", label: "text-xs", multiplier: "text-sm" },
|
|
@@ -11549,7 +11589,7 @@ var init_XPBar = __esm({
|
|
|
11549
11589
|
}
|
|
11550
11590
|
});
|
|
11551
11591
|
function WaypointMarker({
|
|
11552
|
-
assetUrl =
|
|
11592
|
+
assetUrl = DEFAULT_ASSET_URL8,
|
|
11553
11593
|
label,
|
|
11554
11594
|
icon,
|
|
11555
11595
|
active = true,
|
|
@@ -11615,12 +11655,12 @@ function WaypointMarker({
|
|
|
11615
11655
|
)
|
|
11616
11656
|
] });
|
|
11617
11657
|
}
|
|
11618
|
-
var
|
|
11658
|
+
var DEFAULT_ASSET_URL8, sizeMap10, checkIcon;
|
|
11619
11659
|
var init_WaypointMarker = __esm({
|
|
11620
11660
|
"components/game/atoms/WaypointMarker.tsx"() {
|
|
11621
11661
|
init_cn();
|
|
11622
11662
|
init_Icon();
|
|
11623
|
-
|
|
11663
|
+
DEFAULT_ASSET_URL8 = "https://almadar-kflow-assets.web.app/shared/world-map/battle_marker.png";
|
|
11624
11664
|
sizeMap10 = {
|
|
11625
11665
|
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1", img: 12 },
|
|
11626
11666
|
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5", img: 18 },
|
|
@@ -11637,7 +11677,7 @@ function formatDuration(seconds) {
|
|
|
11637
11677
|
return `${Math.round(seconds)}s`;
|
|
11638
11678
|
}
|
|
11639
11679
|
function StatusEffect({
|
|
11640
|
-
assetUrl =
|
|
11680
|
+
assetUrl = DEFAULT_ASSET_URL9,
|
|
11641
11681
|
icon = "shield",
|
|
11642
11682
|
label = "Shield",
|
|
11643
11683
|
duration = 30,
|
|
@@ -11695,12 +11735,12 @@ function StatusEffect({
|
|
|
11695
11735
|
label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground mt-0.5 text-center whitespace-nowrap", children: label })
|
|
11696
11736
|
] });
|
|
11697
11737
|
}
|
|
11698
|
-
var
|
|
11738
|
+
var DEFAULT_ASSET_URL9, sizeMap11, variantStyles7;
|
|
11699
11739
|
var init_StatusEffect = __esm({
|
|
11700
11740
|
"components/game/atoms/StatusEffect.tsx"() {
|
|
11701
11741
|
init_cn();
|
|
11702
11742
|
init_Icon();
|
|
11703
|
-
|
|
11743
|
+
DEFAULT_ASSET_URL9 = "https://almadar-kflow-assets.web.app/shared/effects/particles/flame_01.png";
|
|
11704
11744
|
sizeMap11 = {
|
|
11705
11745
|
sm: { container: "w-8 h-8", icon: "text-sm", badge: "text-xs -top-1 -right-1 w-4 h-4", timer: "text-[9px]", img: 20 },
|
|
11706
11746
|
md: { container: "w-10 h-10", icon: "text-base", badge: "text-xs -top-1 -right-1 w-5 h-5", timer: "text-xs", img: 28 },
|
|
@@ -11715,6 +11755,7 @@ var init_StatusEffect = __esm({
|
|
|
11715
11755
|
}
|
|
11716
11756
|
});
|
|
11717
11757
|
function DamageNumber({
|
|
11758
|
+
assetUrl = DEFAULT_ASSET_URL10,
|
|
11718
11759
|
value = 42,
|
|
11719
11760
|
type = "damage",
|
|
11720
11761
|
size = "md",
|
|
@@ -11723,22 +11764,35 @@ function DamageNumber({
|
|
|
11723
11764
|
const displayText = type === "miss" ? "MISS" : type === "heal" ? `+${value}` : `${value}`;
|
|
11724
11765
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
11725
11766
|
/* @__PURE__ */ jsxRuntime.jsx("style", { children: floatKeyframes }),
|
|
11726
|
-
/* @__PURE__ */ jsxRuntime.
|
|
11767
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
11727
11768
|
"span",
|
|
11728
11769
|
{
|
|
11729
11770
|
className: cn(
|
|
11730
|
-
"inline-
|
|
11771
|
+
"inline-flex items-center gap-0.5 font-mono select-none pointer-events-none",
|
|
11731
11772
|
sizeMap12[size],
|
|
11732
11773
|
typeStyles[type],
|
|
11733
11774
|
className
|
|
11734
11775
|
),
|
|
11735
11776
|
style: { animation: "damageFloat 1s ease-out forwards" },
|
|
11736
|
-
children:
|
|
11777
|
+
children: [
|
|
11778
|
+
assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11779
|
+
"img",
|
|
11780
|
+
{
|
|
11781
|
+
src: assetUrl,
|
|
11782
|
+
alt: "",
|
|
11783
|
+
width: 14,
|
|
11784
|
+
height: 14,
|
|
11785
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11786
|
+
className: "flex-shrink-0"
|
|
11787
|
+
}
|
|
11788
|
+
),
|
|
11789
|
+
displayText
|
|
11790
|
+
]
|
|
11737
11791
|
}
|
|
11738
11792
|
)
|
|
11739
11793
|
] });
|
|
11740
11794
|
}
|
|
11741
|
-
var sizeMap12, typeStyles, floatKeyframes;
|
|
11795
|
+
var sizeMap12, typeStyles, floatKeyframes, DEFAULT_ASSET_URL10;
|
|
11742
11796
|
var init_DamageNumber = __esm({
|
|
11743
11797
|
"components/game/atoms/DamageNumber.tsx"() {
|
|
11744
11798
|
init_cn();
|
|
@@ -11760,6 +11814,7 @@ var init_DamageNumber = __esm({
|
|
|
11760
11814
|
100% { opacity: 0; transform: translateY(-32px) scale(0.8); }
|
|
11761
11815
|
}
|
|
11762
11816
|
`;
|
|
11817
|
+
DEFAULT_ASSET_URL10 = "https://almadar-kflow-assets.web.app/shared/effects/particles/spark_01.png";
|
|
11763
11818
|
DamageNumber.displayName = "DamageNumber";
|
|
11764
11819
|
}
|
|
11765
11820
|
});
|
|
@@ -11848,6 +11903,7 @@ var init_ChoiceButton = __esm({
|
|
|
11848
11903
|
}
|
|
11849
11904
|
});
|
|
11850
11905
|
function ActionButton({
|
|
11906
|
+
assetUrl = DEFAULT_ASSET_URL11,
|
|
11851
11907
|
label = "Attack",
|
|
11852
11908
|
icon,
|
|
11853
11909
|
cooldown = 0,
|
|
@@ -11887,13 +11943,23 @@ function ActionButton({
|
|
|
11887
11943
|
}
|
|
11888
11944
|
}
|
|
11889
11945
|
),
|
|
11890
|
-
|
|
11946
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
11947
|
+
"img",
|
|
11948
|
+
{
|
|
11949
|
+
src: assetUrl,
|
|
11950
|
+
alt: "",
|
|
11951
|
+
width: 16,
|
|
11952
|
+
height: 16,
|
|
11953
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11954
|
+
className: cn("flex-shrink-0", sizes.icon)
|
|
11955
|
+
}
|
|
11956
|
+
) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? (() => {
|
|
11891
11957
|
const I = resolveIcon(icon);
|
|
11892
11958
|
return I ? /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-4 h-4" }) : null;
|
|
11893
11959
|
})() : /* @__PURE__ */ (() => {
|
|
11894
11960
|
const I = icon;
|
|
11895
11961
|
return /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-4 h-4" });
|
|
11896
|
-
})() }),
|
|
11962
|
+
})() }) : null,
|
|
11897
11963
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative z-10", children: label }),
|
|
11898
11964
|
hotkey && /* @__PURE__ */ jsxRuntime.jsx(
|
|
11899
11965
|
"span",
|
|
@@ -11909,7 +11975,7 @@ function ActionButton({
|
|
|
11909
11975
|
}
|
|
11910
11976
|
);
|
|
11911
11977
|
}
|
|
11912
|
-
var sizeMap13, variantStyles8;
|
|
11978
|
+
var sizeMap13, variantStyles8, DEFAULT_ASSET_URL11;
|
|
11913
11979
|
var init_ActionButton = __esm({
|
|
11914
11980
|
"components/game/atoms/ActionButton.tsx"() {
|
|
11915
11981
|
init_cn();
|
|
@@ -11924,6 +11990,7 @@ var init_ActionButton = __esm({
|
|
|
11924
11990
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary-hover border-border",
|
|
11925
11991
|
danger: "bg-error text-error-foreground hover:bg-error/90 border-error"
|
|
11926
11992
|
};
|
|
11993
|
+
DEFAULT_ASSET_URL11 = "https://almadar-kflow-assets.web.app/shared/effects/particles/slash_01.png";
|
|
11927
11994
|
ActionButton.displayName = "ActionButton";
|
|
11928
11995
|
}
|
|
11929
11996
|
});
|
|
@@ -12436,10 +12503,10 @@ var init_Container = __esm({
|
|
|
12436
12503
|
center = true,
|
|
12437
12504
|
className,
|
|
12438
12505
|
children,
|
|
12439
|
-
as:
|
|
12506
|
+
as: Component2 = "div"
|
|
12440
12507
|
}) => {
|
|
12441
12508
|
const resolvedSize = maxWidth ?? size ?? "lg";
|
|
12442
|
-
const Comp =
|
|
12509
|
+
const Comp = Component2;
|
|
12443
12510
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
12444
12511
|
Comp,
|
|
12445
12512
|
{
|
|
@@ -27616,7 +27683,7 @@ var init_Flex = __esm({
|
|
|
27616
27683
|
basis,
|
|
27617
27684
|
className,
|
|
27618
27685
|
children,
|
|
27619
|
-
as:
|
|
27686
|
+
as: Component2 = "div"
|
|
27620
27687
|
}) => {
|
|
27621
27688
|
const flexStyle = {};
|
|
27622
27689
|
if (grow !== void 0 || shrink !== void 0 || basis !== void 0) {
|
|
@@ -27628,7 +27695,7 @@ var init_Flex = __esm({
|
|
|
27628
27695
|
flexStyle.flexBasis = typeof basis === "number" ? `${basis}px` : basis;
|
|
27629
27696
|
}
|
|
27630
27697
|
}
|
|
27631
|
-
const Comp =
|
|
27698
|
+
const Comp = Component2;
|
|
27632
27699
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
27633
27700
|
Comp,
|
|
27634
27701
|
{
|
|
@@ -27748,11 +27815,11 @@ var init_Grid = __esm({
|
|
|
27748
27815
|
className,
|
|
27749
27816
|
style,
|
|
27750
27817
|
children,
|
|
27751
|
-
as:
|
|
27818
|
+
as: Component2 = "div"
|
|
27752
27819
|
}) => {
|
|
27753
27820
|
const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
|
|
27754
27821
|
return React91__namespace.default.createElement(
|
|
27755
|
-
|
|
27822
|
+
Component2,
|
|
27756
27823
|
{
|
|
27757
27824
|
className: cn(
|
|
27758
27825
|
"grid",
|
|
@@ -29578,6 +29645,7 @@ var init_ProgressDots = __esm({
|
|
|
29578
29645
|
}
|
|
29579
29646
|
});
|
|
29580
29647
|
function StatBadge({
|
|
29648
|
+
assetUrl = DEFAULT_ASSET_URL12,
|
|
29581
29649
|
label,
|
|
29582
29650
|
value = 0,
|
|
29583
29651
|
max,
|
|
@@ -29601,7 +29669,17 @@ function StatBadge({
|
|
|
29601
29669
|
className
|
|
29602
29670
|
),
|
|
29603
29671
|
children: [
|
|
29604
|
-
|
|
29672
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
29673
|
+
"img",
|
|
29674
|
+
{
|
|
29675
|
+
src: assetUrl,
|
|
29676
|
+
alt: "",
|
|
29677
|
+
width: 16,
|
|
29678
|
+
height: 16,
|
|
29679
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
29680
|
+
className: "flex-shrink-0"
|
|
29681
|
+
}
|
|
29682
|
+
) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0 text-lg", children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, className: "w-4 h-4" }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, className: "w-4 h-4" }) }) : null,
|
|
29605
29683
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground font-medium", children: label }),
|
|
29606
29684
|
format === "hearts" && max && /* @__PURE__ */ jsxRuntime.jsx(
|
|
29607
29685
|
HealthBar,
|
|
@@ -29634,7 +29712,7 @@ function StatBadge({
|
|
|
29634
29712
|
}
|
|
29635
29713
|
);
|
|
29636
29714
|
}
|
|
29637
|
-
var sizeMap17, variantMap2;
|
|
29715
|
+
var sizeMap17, variantMap2, DEFAULT_ASSET_URL12;
|
|
29638
29716
|
var init_StatBadge = __esm({
|
|
29639
29717
|
"components/game/molecules/StatBadge.tsx"() {
|
|
29640
29718
|
init_cn();
|
|
@@ -29653,6 +29731,7 @@ var init_StatBadge = __esm({
|
|
|
29653
29731
|
warning: "bg-warning/15 border-warning/40 text-foreground",
|
|
29654
29732
|
danger: "bg-error/15 border-error/40 text-foreground"
|
|
29655
29733
|
};
|
|
29734
|
+
DEFAULT_ASSET_URL12 = "https://almadar-kflow-assets.web.app/shared/effects/particles/light_01.png";
|
|
29656
29735
|
StatBadge.displayName = "StatBadge";
|
|
29657
29736
|
}
|
|
29658
29737
|
});
|
|
@@ -30309,6 +30388,7 @@ var init_TurnPanel = __esm({
|
|
|
30309
30388
|
}
|
|
30310
30389
|
});
|
|
30311
30390
|
function EnemyPlate({
|
|
30391
|
+
assetUrl = DEFAULT_ASSET_URL13,
|
|
30312
30392
|
name = "Shadow Guard",
|
|
30313
30393
|
health = 80,
|
|
30314
30394
|
maxHealth = 100,
|
|
@@ -30326,15 +30406,28 @@ function EnemyPlate({
|
|
|
30326
30406
|
),
|
|
30327
30407
|
children: [
|
|
30328
30408
|
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center justify-between gap-2", children: [
|
|
30329
|
-
/* @__PURE__ */ jsxRuntime.
|
|
30330
|
-
|
|
30331
|
-
|
|
30332
|
-
|
|
30333
|
-
|
|
30334
|
-
|
|
30335
|
-
|
|
30336
|
-
|
|
30337
|
-
|
|
30409
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-1.5 min-w-0", children: [
|
|
30410
|
+
assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
30411
|
+
"img",
|
|
30412
|
+
{
|
|
30413
|
+
src: assetUrl,
|
|
30414
|
+
alt: name,
|
|
30415
|
+
width: 24,
|
|
30416
|
+
height: 24,
|
|
30417
|
+
style: { objectFit: "cover", borderRadius: "50%" },
|
|
30418
|
+
className: "flex-shrink-0"
|
|
30419
|
+
}
|
|
30420
|
+
),
|
|
30421
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
30422
|
+
Typography,
|
|
30423
|
+
{
|
|
30424
|
+
variant: "caption",
|
|
30425
|
+
weight: "bold",
|
|
30426
|
+
className: "text-[var(--color-foreground)] truncate",
|
|
30427
|
+
children: name
|
|
30428
|
+
}
|
|
30429
|
+
)
|
|
30430
|
+
] }),
|
|
30338
30431
|
level != null && /* @__PURE__ */ jsxRuntime.jsxs(Badge, { variant: "neutral", size: "sm", children: [
|
|
30339
30432
|
"Lv.",
|
|
30340
30433
|
level
|
|
@@ -30379,7 +30472,7 @@ function EnemyPlate({
|
|
|
30379
30472
|
}
|
|
30380
30473
|
);
|
|
30381
30474
|
}
|
|
30382
|
-
var effectVariantMap2, DEFAULT_ENEMY_EFFECTS;
|
|
30475
|
+
var effectVariantMap2, DEFAULT_ENEMY_EFFECTS, DEFAULT_ASSET_URL13;
|
|
30383
30476
|
var init_EnemyPlate = __esm({
|
|
30384
30477
|
"components/game/molecules/EnemyPlate.tsx"() {
|
|
30385
30478
|
"use client";
|
|
@@ -30396,6 +30489,7 @@ var init_EnemyPlate = __esm({
|
|
|
30396
30489
|
DEFAULT_ENEMY_EFFECTS = [
|
|
30397
30490
|
{ icon: "flame", label: "Burn", variant: "debuff" }
|
|
30398
30491
|
];
|
|
30492
|
+
DEFAULT_ASSET_URL13 = "https://almadar-kflow-assets.web.app/shared/portraits/shadow-legion.png";
|
|
30399
30493
|
EnemyPlate.displayName = "EnemyPlate";
|
|
30400
30494
|
}
|
|
30401
30495
|
});
|
|
@@ -31927,13 +32021,13 @@ var init_MapView = __esm({
|
|
|
31927
32021
|
shadowSize: [41, 41]
|
|
31928
32022
|
});
|
|
31929
32023
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
31930
|
-
const { useEffect:
|
|
32024
|
+
const { useEffect: useEffect84, useRef: useRef76, useCallback: useCallback124, useState: useState120 } = React91__namespace.default;
|
|
31931
32025
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
31932
|
-
const { useEventBus:
|
|
32026
|
+
const { useEventBus: useEventBus4 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
31933
32027
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
31934
32028
|
const map = useMap();
|
|
31935
|
-
const prevRef =
|
|
31936
|
-
|
|
32029
|
+
const prevRef = useRef76({ centerLat, centerLng, zoom });
|
|
32030
|
+
useEffect84(() => {
|
|
31937
32031
|
const prev = prevRef.current;
|
|
31938
32032
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
31939
32033
|
map.setView([centerLat, centerLng], zoom);
|
|
@@ -31944,7 +32038,7 @@ var init_MapView = __esm({
|
|
|
31944
32038
|
}
|
|
31945
32039
|
function MapClickHandler({ onMapClick }) {
|
|
31946
32040
|
const map = useMap();
|
|
31947
|
-
|
|
32041
|
+
useEffect84(() => {
|
|
31948
32042
|
if (!onMapClick) return;
|
|
31949
32043
|
const handler = (e) => {
|
|
31950
32044
|
onMapClick(e.latlng.lat, e.latlng.lng);
|
|
@@ -31971,9 +32065,9 @@ var init_MapView = __esm({
|
|
|
31971
32065
|
className,
|
|
31972
32066
|
showAttribution = true
|
|
31973
32067
|
}) {
|
|
31974
|
-
const eventBus =
|
|
31975
|
-
const [clickedPosition, setClickedPosition] =
|
|
31976
|
-
const handleMapClick =
|
|
32068
|
+
const eventBus = useEventBus4();
|
|
32069
|
+
const [clickedPosition, setClickedPosition] = useState120(null);
|
|
32070
|
+
const handleMapClick = useCallback124((lat, lng) => {
|
|
31977
32071
|
if (showClickedPin) {
|
|
31978
32072
|
setClickedPosition({ lat, lng });
|
|
31979
32073
|
}
|
|
@@ -31982,7 +32076,7 @@ var init_MapView = __esm({
|
|
|
31982
32076
|
eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
|
|
31983
32077
|
}
|
|
31984
32078
|
}, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
|
|
31985
|
-
const handleMarkerClick =
|
|
32079
|
+
const handleMarkerClick = useCallback124((marker) => {
|
|
31986
32080
|
onMarkerClick?.(marker);
|
|
31987
32081
|
if (markerClickEvent) {
|
|
31988
32082
|
eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
|
|
@@ -38700,10 +38794,10 @@ var init_Section = __esm({
|
|
|
38700
38794
|
children,
|
|
38701
38795
|
headerClassName,
|
|
38702
38796
|
contentClassName,
|
|
38703
|
-
as:
|
|
38797
|
+
as: Component2 = "section"
|
|
38704
38798
|
}) => {
|
|
38705
38799
|
const hasHeader = title || description || action;
|
|
38706
|
-
const Comp =
|
|
38800
|
+
const Comp = Component2;
|
|
38707
38801
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
38708
38802
|
Comp,
|
|
38709
38803
|
{
|
|
@@ -43268,6 +43362,1568 @@ var init_GameAudioToggle = __esm({
|
|
|
43268
43362
|
GameAudioToggle.displayName = "GameAudioToggle";
|
|
43269
43363
|
}
|
|
43270
43364
|
});
|
|
43365
|
+
function detectAssetRoot(modelUrl) {
|
|
43366
|
+
const idx = modelUrl.indexOf("/3d/");
|
|
43367
|
+
if (idx !== -1) {
|
|
43368
|
+
return modelUrl.substring(0, idx + 4);
|
|
43369
|
+
}
|
|
43370
|
+
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
43371
|
+
}
|
|
43372
|
+
function createGLTFLoaderForUrl(url) {
|
|
43373
|
+
const loader = new GLTFLoader_js.GLTFLoader();
|
|
43374
|
+
loader.setResourcePath(detectAssetRoot(url));
|
|
43375
|
+
return loader;
|
|
43376
|
+
}
|
|
43377
|
+
var AssetLoader;
|
|
43378
|
+
var init_AssetLoader = __esm({
|
|
43379
|
+
"components/game/molecules/three/loaders/AssetLoader.ts"() {
|
|
43380
|
+
AssetLoader = class {
|
|
43381
|
+
constructor() {
|
|
43382
|
+
__publicField(this, "objLoader");
|
|
43383
|
+
__publicField(this, "textureLoader");
|
|
43384
|
+
__publicField(this, "modelCache");
|
|
43385
|
+
__publicField(this, "textureCache");
|
|
43386
|
+
__publicField(this, "loadingPromises");
|
|
43387
|
+
this.objLoader = new OBJLoader_js.OBJLoader();
|
|
43388
|
+
this.textureLoader = new THREE__namespace.TextureLoader();
|
|
43389
|
+
this.modelCache = /* @__PURE__ */ new Map();
|
|
43390
|
+
this.textureCache = /* @__PURE__ */ new Map();
|
|
43391
|
+
this.loadingPromises = /* @__PURE__ */ new Map();
|
|
43392
|
+
}
|
|
43393
|
+
/**
|
|
43394
|
+
* Load a GLB/GLTF model
|
|
43395
|
+
* @param url - URL to the .glb or .gltf file
|
|
43396
|
+
* @returns Promise with loaded model scene and animations
|
|
43397
|
+
*/
|
|
43398
|
+
async loadModel(url) {
|
|
43399
|
+
if (this.modelCache.has(url)) {
|
|
43400
|
+
return this.modelCache.get(url);
|
|
43401
|
+
}
|
|
43402
|
+
if (this.loadingPromises.has(url)) {
|
|
43403
|
+
return this.loadingPromises.get(url);
|
|
43404
|
+
}
|
|
43405
|
+
const loader = createGLTFLoaderForUrl(url);
|
|
43406
|
+
const loadPromise = loader.loadAsync(url).then((gltf) => {
|
|
43407
|
+
const result = {
|
|
43408
|
+
scene: gltf.scene,
|
|
43409
|
+
animations: gltf.animations || []
|
|
43410
|
+
};
|
|
43411
|
+
this.modelCache.set(url, result);
|
|
43412
|
+
this.loadingPromises.delete(url);
|
|
43413
|
+
return result;
|
|
43414
|
+
}).catch((error) => {
|
|
43415
|
+
this.loadingPromises.delete(url);
|
|
43416
|
+
throw new Error(`Failed to load model ${url}: ${error.message}`);
|
|
43417
|
+
});
|
|
43418
|
+
this.loadingPromises.set(url, loadPromise);
|
|
43419
|
+
return loadPromise;
|
|
43420
|
+
}
|
|
43421
|
+
/**
|
|
43422
|
+
* Load an OBJ model (fallback for non-GLB assets)
|
|
43423
|
+
* @param url - URL to the .obj file
|
|
43424
|
+
* @returns Promise with loaded object group
|
|
43425
|
+
*/
|
|
43426
|
+
async loadOBJ(url) {
|
|
43427
|
+
if (this.modelCache.has(url)) {
|
|
43428
|
+
return this.modelCache.get(url).scene;
|
|
43429
|
+
}
|
|
43430
|
+
if (this.loadingPromises.has(url)) {
|
|
43431
|
+
const result = await this.loadingPromises.get(url);
|
|
43432
|
+
return result.scene;
|
|
43433
|
+
}
|
|
43434
|
+
const loadPromise = this.objLoader.loadAsync(url).then((group) => {
|
|
43435
|
+
const result = {
|
|
43436
|
+
scene: group,
|
|
43437
|
+
animations: []
|
|
43438
|
+
};
|
|
43439
|
+
this.modelCache.set(url, result);
|
|
43440
|
+
this.loadingPromises.delete(url);
|
|
43441
|
+
return result;
|
|
43442
|
+
}).catch((error) => {
|
|
43443
|
+
this.loadingPromises.delete(url);
|
|
43444
|
+
throw new Error(`Failed to load OBJ ${url}: ${error.message}`);
|
|
43445
|
+
});
|
|
43446
|
+
this.loadingPromises.set(url, loadPromise);
|
|
43447
|
+
return (await loadPromise).scene;
|
|
43448
|
+
}
|
|
43449
|
+
/**
|
|
43450
|
+
* Load a texture
|
|
43451
|
+
* @param url - URL to the texture image
|
|
43452
|
+
* @returns Promise with loaded texture
|
|
43453
|
+
*/
|
|
43454
|
+
async loadTexture(url) {
|
|
43455
|
+
if (this.textureCache.has(url)) {
|
|
43456
|
+
return this.textureCache.get(url);
|
|
43457
|
+
}
|
|
43458
|
+
if (this.loadingPromises.has(`texture:${url}`)) {
|
|
43459
|
+
return this.loadingPromises.get(`texture:${url}`);
|
|
43460
|
+
}
|
|
43461
|
+
const loadPromise = this.textureLoader.loadAsync(url).then((texture) => {
|
|
43462
|
+
texture.colorSpace = THREE__namespace.SRGBColorSpace;
|
|
43463
|
+
this.textureCache.set(url, texture);
|
|
43464
|
+
this.loadingPromises.delete(`texture:${url}`);
|
|
43465
|
+
return texture;
|
|
43466
|
+
}).catch((error) => {
|
|
43467
|
+
this.loadingPromises.delete(`texture:${url}`);
|
|
43468
|
+
throw new Error(`Failed to load texture ${url}: ${error.message}`);
|
|
43469
|
+
});
|
|
43470
|
+
this.loadingPromises.set(`texture:${url}`, loadPromise);
|
|
43471
|
+
return loadPromise;
|
|
43472
|
+
}
|
|
43473
|
+
/**
|
|
43474
|
+
* Preload multiple assets
|
|
43475
|
+
* @param urls - Array of asset URLs to preload
|
|
43476
|
+
* @returns Promise that resolves when all assets are loaded
|
|
43477
|
+
*/
|
|
43478
|
+
async preload(urls) {
|
|
43479
|
+
const promises = urls.map((url) => {
|
|
43480
|
+
if (url.endsWith(".glb") || url.endsWith(".gltf")) {
|
|
43481
|
+
return this.loadModel(url).catch(() => null);
|
|
43482
|
+
} else if (url.endsWith(".obj")) {
|
|
43483
|
+
return this.loadOBJ(url).catch(() => null);
|
|
43484
|
+
} else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
|
|
43485
|
+
return this.loadTexture(url).catch(() => null);
|
|
43486
|
+
}
|
|
43487
|
+
return Promise.resolve(null);
|
|
43488
|
+
});
|
|
43489
|
+
await Promise.all(promises);
|
|
43490
|
+
}
|
|
43491
|
+
/**
|
|
43492
|
+
* Check if a model is cached
|
|
43493
|
+
* @param url - Model URL
|
|
43494
|
+
*/
|
|
43495
|
+
hasModel(url) {
|
|
43496
|
+
return this.modelCache.has(url);
|
|
43497
|
+
}
|
|
43498
|
+
/**
|
|
43499
|
+
* Check if a texture is cached
|
|
43500
|
+
* @param url - Texture URL
|
|
43501
|
+
*/
|
|
43502
|
+
hasTexture(url) {
|
|
43503
|
+
return this.textureCache.has(url);
|
|
43504
|
+
}
|
|
43505
|
+
/**
|
|
43506
|
+
* Get cached model (throws if not cached)
|
|
43507
|
+
* @param url - Model URL
|
|
43508
|
+
*/
|
|
43509
|
+
getModel(url) {
|
|
43510
|
+
const model = this.modelCache.get(url);
|
|
43511
|
+
if (!model) {
|
|
43512
|
+
throw new Error(`Model ${url} not in cache`);
|
|
43513
|
+
}
|
|
43514
|
+
return model;
|
|
43515
|
+
}
|
|
43516
|
+
/**
|
|
43517
|
+
* Get cached texture (throws if not cached)
|
|
43518
|
+
* @param url - Texture URL
|
|
43519
|
+
*/
|
|
43520
|
+
getTexture(url) {
|
|
43521
|
+
const texture = this.textureCache.get(url);
|
|
43522
|
+
if (!texture) {
|
|
43523
|
+
throw new Error(`Texture ${url} not in cache`);
|
|
43524
|
+
}
|
|
43525
|
+
return texture;
|
|
43526
|
+
}
|
|
43527
|
+
/**
|
|
43528
|
+
* Clear all caches
|
|
43529
|
+
*/
|
|
43530
|
+
clearCache() {
|
|
43531
|
+
this.textureCache.forEach((texture) => {
|
|
43532
|
+
texture.dispose();
|
|
43533
|
+
});
|
|
43534
|
+
this.modelCache.forEach((model) => {
|
|
43535
|
+
model.scene.traverse((child) => {
|
|
43536
|
+
if (child instanceof THREE__namespace.Mesh) {
|
|
43537
|
+
child.geometry.dispose();
|
|
43538
|
+
if (Array.isArray(child.material)) {
|
|
43539
|
+
child.material.forEach((m) => m.dispose());
|
|
43540
|
+
} else {
|
|
43541
|
+
child.material.dispose();
|
|
43542
|
+
}
|
|
43543
|
+
}
|
|
43544
|
+
});
|
|
43545
|
+
});
|
|
43546
|
+
this.modelCache.clear();
|
|
43547
|
+
this.textureCache.clear();
|
|
43548
|
+
this.loadingPromises.clear();
|
|
43549
|
+
}
|
|
43550
|
+
/**
|
|
43551
|
+
* Get cache statistics
|
|
43552
|
+
*/
|
|
43553
|
+
getStats() {
|
|
43554
|
+
return {
|
|
43555
|
+
models: this.modelCache.size,
|
|
43556
|
+
textures: this.textureCache.size,
|
|
43557
|
+
loading: this.loadingPromises.size
|
|
43558
|
+
};
|
|
43559
|
+
}
|
|
43560
|
+
};
|
|
43561
|
+
new AssetLoader();
|
|
43562
|
+
}
|
|
43563
|
+
});
|
|
43564
|
+
function useAssetLoader(options = {}) {
|
|
43565
|
+
const { preloadUrls = [], loader: customLoader } = options;
|
|
43566
|
+
const loaderRef = React91.useRef(customLoader || new AssetLoader());
|
|
43567
|
+
const [state, setState] = React91.useState({
|
|
43568
|
+
isLoading: false,
|
|
43569
|
+
progress: 0,
|
|
43570
|
+
loaded: 0,
|
|
43571
|
+
total: 0,
|
|
43572
|
+
errors: []
|
|
43573
|
+
});
|
|
43574
|
+
React91.useEffect(() => {
|
|
43575
|
+
if (preloadUrls.length > 0) {
|
|
43576
|
+
preload(preloadUrls);
|
|
43577
|
+
}
|
|
43578
|
+
}, []);
|
|
43579
|
+
const updateProgress = React91.useCallback((loaded, total) => {
|
|
43580
|
+
setState((prev) => ({
|
|
43581
|
+
...prev,
|
|
43582
|
+
loaded,
|
|
43583
|
+
total,
|
|
43584
|
+
progress: total > 0 ? Math.round(loaded / total * 100) : 0
|
|
43585
|
+
}));
|
|
43586
|
+
}, []);
|
|
43587
|
+
const loadModel = React91.useCallback(
|
|
43588
|
+
async (url) => {
|
|
43589
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
43590
|
+
try {
|
|
43591
|
+
const model = await loaderRef.current.loadModel(url);
|
|
43592
|
+
setState((prev) => ({
|
|
43593
|
+
...prev,
|
|
43594
|
+
isLoading: false,
|
|
43595
|
+
loaded: prev.loaded + 1
|
|
43596
|
+
}));
|
|
43597
|
+
return model;
|
|
43598
|
+
} catch (error) {
|
|
43599
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
43600
|
+
setState((prev) => ({
|
|
43601
|
+
...prev,
|
|
43602
|
+
isLoading: false,
|
|
43603
|
+
errors: [...prev.errors, errorMsg]
|
|
43604
|
+
}));
|
|
43605
|
+
throw error;
|
|
43606
|
+
}
|
|
43607
|
+
},
|
|
43608
|
+
[]
|
|
43609
|
+
);
|
|
43610
|
+
const loadOBJ = React91.useCallback(
|
|
43611
|
+
async (url) => {
|
|
43612
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
43613
|
+
try {
|
|
43614
|
+
const model = await loaderRef.current.loadOBJ(url);
|
|
43615
|
+
setState((prev) => ({
|
|
43616
|
+
...prev,
|
|
43617
|
+
isLoading: false,
|
|
43618
|
+
loaded: prev.loaded + 1
|
|
43619
|
+
}));
|
|
43620
|
+
return model;
|
|
43621
|
+
} catch (error) {
|
|
43622
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
43623
|
+
setState((prev) => ({
|
|
43624
|
+
...prev,
|
|
43625
|
+
isLoading: false,
|
|
43626
|
+
errors: [...prev.errors, errorMsg]
|
|
43627
|
+
}));
|
|
43628
|
+
throw error;
|
|
43629
|
+
}
|
|
43630
|
+
},
|
|
43631
|
+
[]
|
|
43632
|
+
);
|
|
43633
|
+
const loadTexture = React91.useCallback(
|
|
43634
|
+
async (url) => {
|
|
43635
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
43636
|
+
try {
|
|
43637
|
+
const texture = await loaderRef.current.loadTexture(url);
|
|
43638
|
+
setState((prev) => ({
|
|
43639
|
+
...prev,
|
|
43640
|
+
isLoading: false,
|
|
43641
|
+
loaded: prev.loaded + 1
|
|
43642
|
+
}));
|
|
43643
|
+
return texture;
|
|
43644
|
+
} catch (error) {
|
|
43645
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
43646
|
+
setState((prev) => ({
|
|
43647
|
+
...prev,
|
|
43648
|
+
isLoading: false,
|
|
43649
|
+
errors: [...prev.errors, errorMsg]
|
|
43650
|
+
}));
|
|
43651
|
+
throw error;
|
|
43652
|
+
}
|
|
43653
|
+
},
|
|
43654
|
+
[]
|
|
43655
|
+
);
|
|
43656
|
+
const preload = React91.useCallback(
|
|
43657
|
+
async (urls) => {
|
|
43658
|
+
setState((prev) => ({
|
|
43659
|
+
...prev,
|
|
43660
|
+
isLoading: true,
|
|
43661
|
+
total: urls.length,
|
|
43662
|
+
loaded: 0,
|
|
43663
|
+
errors: []
|
|
43664
|
+
}));
|
|
43665
|
+
let completed = 0;
|
|
43666
|
+
const errors = [];
|
|
43667
|
+
await Promise.all(
|
|
43668
|
+
urls.map(async (url) => {
|
|
43669
|
+
try {
|
|
43670
|
+
if (url.endsWith(".glb") || url.endsWith(".gltf")) {
|
|
43671
|
+
await loaderRef.current.loadModel(url);
|
|
43672
|
+
} else if (url.endsWith(".obj")) {
|
|
43673
|
+
await loaderRef.current.loadOBJ(url);
|
|
43674
|
+
} else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
|
|
43675
|
+
await loaderRef.current.loadTexture(url);
|
|
43676
|
+
}
|
|
43677
|
+
completed++;
|
|
43678
|
+
updateProgress(completed, urls.length);
|
|
43679
|
+
} catch (error) {
|
|
43680
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
43681
|
+
errors.push(`${url}: ${errorMsg}`);
|
|
43682
|
+
completed++;
|
|
43683
|
+
updateProgress(completed, urls.length);
|
|
43684
|
+
}
|
|
43685
|
+
})
|
|
43686
|
+
);
|
|
43687
|
+
setState((prev) => ({
|
|
43688
|
+
...prev,
|
|
43689
|
+
isLoading: false,
|
|
43690
|
+
errors
|
|
43691
|
+
}));
|
|
43692
|
+
},
|
|
43693
|
+
[updateProgress]
|
|
43694
|
+
);
|
|
43695
|
+
const hasModel = React91.useCallback((url) => {
|
|
43696
|
+
return loaderRef.current.hasModel(url);
|
|
43697
|
+
}, []);
|
|
43698
|
+
const hasTexture = React91.useCallback((url) => {
|
|
43699
|
+
return loaderRef.current.hasTexture(url);
|
|
43700
|
+
}, []);
|
|
43701
|
+
const getModel = React91.useCallback((url) => {
|
|
43702
|
+
try {
|
|
43703
|
+
return loaderRef.current.getModel(url);
|
|
43704
|
+
} catch {
|
|
43705
|
+
return void 0;
|
|
43706
|
+
}
|
|
43707
|
+
}, []);
|
|
43708
|
+
const getTexture = React91.useCallback((url) => {
|
|
43709
|
+
try {
|
|
43710
|
+
return loaderRef.current.getTexture(url);
|
|
43711
|
+
} catch {
|
|
43712
|
+
return void 0;
|
|
43713
|
+
}
|
|
43714
|
+
}, []);
|
|
43715
|
+
const clearCache = React91.useCallback(() => {
|
|
43716
|
+
loaderRef.current.clearCache();
|
|
43717
|
+
setState({
|
|
43718
|
+
isLoading: false,
|
|
43719
|
+
progress: 0,
|
|
43720
|
+
loaded: 0,
|
|
43721
|
+
total: 0,
|
|
43722
|
+
errors: []
|
|
43723
|
+
});
|
|
43724
|
+
}, []);
|
|
43725
|
+
return {
|
|
43726
|
+
...state,
|
|
43727
|
+
loadModel,
|
|
43728
|
+
loadOBJ,
|
|
43729
|
+
loadTexture,
|
|
43730
|
+
preload,
|
|
43731
|
+
hasModel,
|
|
43732
|
+
hasTexture,
|
|
43733
|
+
getModel,
|
|
43734
|
+
getTexture,
|
|
43735
|
+
clearCache
|
|
43736
|
+
};
|
|
43737
|
+
}
|
|
43738
|
+
var init_useAssetLoader = __esm({
|
|
43739
|
+
"components/game/molecules/three/hooks/useAssetLoader.ts"() {
|
|
43740
|
+
"use client";
|
|
43741
|
+
init_AssetLoader();
|
|
43742
|
+
}
|
|
43743
|
+
});
|
|
43744
|
+
function useGameCanvas3DEvents(options) {
|
|
43745
|
+
const {
|
|
43746
|
+
tileClickEvent,
|
|
43747
|
+
unitClickEvent,
|
|
43748
|
+
featureClickEvent,
|
|
43749
|
+
canvasClickEvent,
|
|
43750
|
+
tileHoverEvent,
|
|
43751
|
+
tileLeaveEvent,
|
|
43752
|
+
unitAnimationEvent,
|
|
43753
|
+
cameraChangeEvent,
|
|
43754
|
+
onTileClick,
|
|
43755
|
+
onUnitClick,
|
|
43756
|
+
onFeatureClick,
|
|
43757
|
+
onCanvasClick,
|
|
43758
|
+
onTileHover,
|
|
43759
|
+
onUnitAnimation
|
|
43760
|
+
} = options;
|
|
43761
|
+
const emit = useEmitEvent();
|
|
43762
|
+
const optionsRef = React91.useRef(options);
|
|
43763
|
+
optionsRef.current = options;
|
|
43764
|
+
const handleTileClick = React91.useCallback(
|
|
43765
|
+
(tile, event) => {
|
|
43766
|
+
if (tileClickEvent) {
|
|
43767
|
+
emit(tileClickEvent, {
|
|
43768
|
+
tileId: tile.id,
|
|
43769
|
+
x: tile.x,
|
|
43770
|
+
z: tile.z ?? tile.y ?? 0,
|
|
43771
|
+
type: tile.type,
|
|
43772
|
+
terrain: tile.terrain,
|
|
43773
|
+
elevation: tile.elevation
|
|
43774
|
+
});
|
|
43775
|
+
}
|
|
43776
|
+
optionsRef.current.onTileClick?.(tile, event);
|
|
43777
|
+
},
|
|
43778
|
+
[tileClickEvent, emit]
|
|
43779
|
+
);
|
|
43780
|
+
const handleUnitClick = React91.useCallback(
|
|
43781
|
+
(unit, event) => {
|
|
43782
|
+
if (unitClickEvent) {
|
|
43783
|
+
emit(unitClickEvent, {
|
|
43784
|
+
unitId: unit.id,
|
|
43785
|
+
x: unit.x,
|
|
43786
|
+
z: unit.z ?? unit.y ?? 0,
|
|
43787
|
+
unitType: unit.unitType,
|
|
43788
|
+
name: unit.name,
|
|
43789
|
+
team: unit.team,
|
|
43790
|
+
faction: unit.faction,
|
|
43791
|
+
health: unit.health,
|
|
43792
|
+
maxHealth: unit.maxHealth
|
|
43793
|
+
});
|
|
43794
|
+
}
|
|
43795
|
+
optionsRef.current.onUnitClick?.(unit, event);
|
|
43796
|
+
},
|
|
43797
|
+
[unitClickEvent, emit]
|
|
43798
|
+
);
|
|
43799
|
+
const handleFeatureClick = React91.useCallback(
|
|
43800
|
+
(feature, event) => {
|
|
43801
|
+
if (featureClickEvent) {
|
|
43802
|
+
emit(featureClickEvent, {
|
|
43803
|
+
featureId: feature.id,
|
|
43804
|
+
x: feature.x,
|
|
43805
|
+
z: feature.z ?? feature.y ?? 0,
|
|
43806
|
+
type: feature.type,
|
|
43807
|
+
elevation: feature.elevation
|
|
43808
|
+
});
|
|
43809
|
+
}
|
|
43810
|
+
optionsRef.current.onFeatureClick?.(feature, event);
|
|
43811
|
+
},
|
|
43812
|
+
[featureClickEvent, emit]
|
|
43813
|
+
);
|
|
43814
|
+
const handleCanvasClick = React91.useCallback(
|
|
43815
|
+
(event) => {
|
|
43816
|
+
if (canvasClickEvent) {
|
|
43817
|
+
emit(canvasClickEvent, {
|
|
43818
|
+
clientX: event.clientX,
|
|
43819
|
+
clientY: event.clientY,
|
|
43820
|
+
button: event.button
|
|
43821
|
+
});
|
|
43822
|
+
}
|
|
43823
|
+
optionsRef.current.onCanvasClick?.(event);
|
|
43824
|
+
},
|
|
43825
|
+
[canvasClickEvent, emit]
|
|
43826
|
+
);
|
|
43827
|
+
const handleTileHover = React91.useCallback(
|
|
43828
|
+
(tile, event) => {
|
|
43829
|
+
if (tile) {
|
|
43830
|
+
if (tileHoverEvent) {
|
|
43831
|
+
emit(tileHoverEvent, {
|
|
43832
|
+
tileId: tile.id,
|
|
43833
|
+
x: tile.x,
|
|
43834
|
+
z: tile.z ?? tile.y ?? 0,
|
|
43835
|
+
type: tile.type
|
|
43836
|
+
});
|
|
43837
|
+
}
|
|
43838
|
+
} else {
|
|
43839
|
+
if (tileLeaveEvent) {
|
|
43840
|
+
emit(tileLeaveEvent, {});
|
|
43841
|
+
}
|
|
43842
|
+
}
|
|
43843
|
+
optionsRef.current.onTileHover?.(tile, event);
|
|
43844
|
+
},
|
|
43845
|
+
[tileHoverEvent, tileLeaveEvent, emit]
|
|
43846
|
+
);
|
|
43847
|
+
const handleUnitAnimation = React91.useCallback(
|
|
43848
|
+
(unitId, state) => {
|
|
43849
|
+
if (unitAnimationEvent) {
|
|
43850
|
+
emit(unitAnimationEvent, {
|
|
43851
|
+
unitId,
|
|
43852
|
+
state,
|
|
43853
|
+
timestamp: Date.now()
|
|
43854
|
+
});
|
|
43855
|
+
}
|
|
43856
|
+
optionsRef.current.onUnitAnimation?.(unitId, state);
|
|
43857
|
+
},
|
|
43858
|
+
[unitAnimationEvent, emit]
|
|
43859
|
+
);
|
|
43860
|
+
const handleCameraChange = React91.useCallback(
|
|
43861
|
+
(position) => {
|
|
43862
|
+
if (cameraChangeEvent) {
|
|
43863
|
+
emit(cameraChangeEvent, {
|
|
43864
|
+
position,
|
|
43865
|
+
timestamp: Date.now()
|
|
43866
|
+
});
|
|
43867
|
+
}
|
|
43868
|
+
},
|
|
43869
|
+
[cameraChangeEvent, emit]
|
|
43870
|
+
);
|
|
43871
|
+
return {
|
|
43872
|
+
handleTileClick,
|
|
43873
|
+
handleUnitClick,
|
|
43874
|
+
handleFeatureClick,
|
|
43875
|
+
handleCanvasClick,
|
|
43876
|
+
handleTileHover,
|
|
43877
|
+
handleUnitAnimation,
|
|
43878
|
+
handleCameraChange
|
|
43879
|
+
};
|
|
43880
|
+
}
|
|
43881
|
+
var init_useGameCanvas3DEvents = __esm({
|
|
43882
|
+
"components/game/molecules/three/hooks/useGameCanvas3DEvents.ts"() {
|
|
43883
|
+
"use client";
|
|
43884
|
+
init_useEventBus();
|
|
43885
|
+
}
|
|
43886
|
+
});
|
|
43887
|
+
|
|
43888
|
+
// components/game/molecules/three/components/Canvas3DLoadingState.css
|
|
43889
|
+
var init_Canvas3DLoadingState = __esm({
|
|
43890
|
+
"components/game/molecules/three/components/Canvas3DLoadingState.css"() {
|
|
43891
|
+
}
|
|
43892
|
+
});
|
|
43893
|
+
function Canvas3DLoadingState({
|
|
43894
|
+
progress = 0,
|
|
43895
|
+
loaded = 0,
|
|
43896
|
+
total = 0,
|
|
43897
|
+
message = "Loading 3D Scene...",
|
|
43898
|
+
details,
|
|
43899
|
+
showSpinner = true,
|
|
43900
|
+
className
|
|
43901
|
+
}) {
|
|
43902
|
+
const clampedProgress = Math.max(0, Math.min(100, progress));
|
|
43903
|
+
const hasProgress = total > 0;
|
|
43904
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `canvas-3d-loading ${className || ""}`, children: [
|
|
43905
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__content", children: [
|
|
43906
|
+
showSpinner && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__spinner", children: [
|
|
43907
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "spinner__ring" }),
|
|
43908
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "spinner__ring spinner__ring--secondary" })
|
|
43909
|
+
] }),
|
|
43910
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__message", children: message }),
|
|
43911
|
+
details && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__details", children: details }),
|
|
43912
|
+
hasProgress && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__progress", children: [
|
|
43913
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "progress__bar", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
43914
|
+
"div",
|
|
43915
|
+
{
|
|
43916
|
+
className: "progress__fill",
|
|
43917
|
+
style: { width: `${clampedProgress}%` }
|
|
43918
|
+
}
|
|
43919
|
+
) }),
|
|
43920
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "progress__text", children: [
|
|
43921
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "progress__percentage", children: [
|
|
43922
|
+
clampedProgress,
|
|
43923
|
+
"%"
|
|
43924
|
+
] }),
|
|
43925
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "progress__count", children: [
|
|
43926
|
+
"(",
|
|
43927
|
+
loaded,
|
|
43928
|
+
"/",
|
|
43929
|
+
total,
|
|
43930
|
+
")"
|
|
43931
|
+
] })
|
|
43932
|
+
] })
|
|
43933
|
+
] })
|
|
43934
|
+
] }),
|
|
43935
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__background", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg__grid" }) })
|
|
43936
|
+
] });
|
|
43937
|
+
}
|
|
43938
|
+
var init_Canvas3DLoadingState2 = __esm({
|
|
43939
|
+
"components/game/molecules/three/components/Canvas3DLoadingState.tsx"() {
|
|
43940
|
+
init_Canvas3DLoadingState();
|
|
43941
|
+
}
|
|
43942
|
+
});
|
|
43943
|
+
|
|
43944
|
+
// components/game/molecules/three/components/Canvas3DErrorBoundary.css
|
|
43945
|
+
var init_Canvas3DErrorBoundary = __esm({
|
|
43946
|
+
"components/game/molecules/three/components/Canvas3DErrorBoundary.css"() {
|
|
43947
|
+
}
|
|
43948
|
+
});
|
|
43949
|
+
var log9, Canvas3DErrorBoundary;
|
|
43950
|
+
var init_Canvas3DErrorBoundary2 = __esm({
|
|
43951
|
+
"components/game/molecules/three/components/Canvas3DErrorBoundary.tsx"() {
|
|
43952
|
+
init_Canvas3DErrorBoundary();
|
|
43953
|
+
log9 = logger.createLogger("almadar:ui:game:canvas3d:error-boundary");
|
|
43954
|
+
Canvas3DErrorBoundary = class extends React91.Component {
|
|
43955
|
+
constructor(props) {
|
|
43956
|
+
super(props);
|
|
43957
|
+
__publicField(this, "handleReset", () => {
|
|
43958
|
+
this.setState({
|
|
43959
|
+
hasError: false,
|
|
43960
|
+
error: null,
|
|
43961
|
+
errorInfo: null
|
|
43962
|
+
});
|
|
43963
|
+
this.props.onReset?.();
|
|
43964
|
+
});
|
|
43965
|
+
this.state = {
|
|
43966
|
+
hasError: false,
|
|
43967
|
+
error: null,
|
|
43968
|
+
errorInfo: null
|
|
43969
|
+
};
|
|
43970
|
+
}
|
|
43971
|
+
static getDerivedStateFromError(error) {
|
|
43972
|
+
return {
|
|
43973
|
+
hasError: true,
|
|
43974
|
+
error,
|
|
43975
|
+
errorInfo: null
|
|
43976
|
+
};
|
|
43977
|
+
}
|
|
43978
|
+
componentDidCatch(error, errorInfo) {
|
|
43979
|
+
this.setState({ errorInfo });
|
|
43980
|
+
this.props.onError?.(error, errorInfo);
|
|
43981
|
+
log9.error("Error caught", { error });
|
|
43982
|
+
log9.error("Component stack", { componentStack: errorInfo.componentStack ?? "<none>" });
|
|
43983
|
+
}
|
|
43984
|
+
render() {
|
|
43985
|
+
if (this.state.hasError) {
|
|
43986
|
+
if (this.props.fallback) {
|
|
43987
|
+
return this.props.fallback;
|
|
43988
|
+
}
|
|
43989
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-error", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-error__content", children: [
|
|
43990
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-error__icon", children: "\u26A0\uFE0F" }),
|
|
43991
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "canvas-3d-error__title", children: "3D Scene Error" }),
|
|
43992
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "canvas-3d-error__message", children: "Something went wrong while rendering the 3D scene." }),
|
|
43993
|
+
this.state.error && /* @__PURE__ */ jsxRuntime.jsxs("details", { className: "canvas-3d-error__details", children: [
|
|
43994
|
+
/* @__PURE__ */ jsxRuntime.jsx("summary", { children: "Error Details" }),
|
|
43995
|
+
/* @__PURE__ */ jsxRuntime.jsxs("pre", { className: "error__stack", children: [
|
|
43996
|
+
this.state.error.message,
|
|
43997
|
+
"\n",
|
|
43998
|
+
this.state.error.stack
|
|
43999
|
+
] }),
|
|
44000
|
+
this.state.errorInfo && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "error__component-stack", children: this.state.errorInfo.componentStack })
|
|
44001
|
+
] }),
|
|
44002
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-error__actions", children: [
|
|
44003
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44004
|
+
"button",
|
|
44005
|
+
{
|
|
44006
|
+
className: "error__button error__button--primary",
|
|
44007
|
+
onClick: this.handleReset,
|
|
44008
|
+
children: "Try Again"
|
|
44009
|
+
}
|
|
44010
|
+
),
|
|
44011
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44012
|
+
"button",
|
|
44013
|
+
{
|
|
44014
|
+
className: "error__button error__button--secondary",
|
|
44015
|
+
onClick: () => window.location.reload(),
|
|
44016
|
+
children: "Reload Page"
|
|
44017
|
+
}
|
|
44018
|
+
)
|
|
44019
|
+
] })
|
|
44020
|
+
] }) });
|
|
44021
|
+
}
|
|
44022
|
+
return this.props.children;
|
|
44023
|
+
}
|
|
44024
|
+
};
|
|
44025
|
+
}
|
|
44026
|
+
});
|
|
44027
|
+
function detectAssetRoot2(modelUrl) {
|
|
44028
|
+
const idx = modelUrl.indexOf("/3d/");
|
|
44029
|
+
if (idx !== -1) {
|
|
44030
|
+
return modelUrl.substring(0, idx + 4);
|
|
44031
|
+
}
|
|
44032
|
+
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
44033
|
+
}
|
|
44034
|
+
function useGLTFModel(url, resourceBasePath) {
|
|
44035
|
+
const [state, setState] = React91.useState({
|
|
44036
|
+
model: null,
|
|
44037
|
+
isLoading: false,
|
|
44038
|
+
error: null
|
|
44039
|
+
});
|
|
44040
|
+
React91.useEffect(() => {
|
|
44041
|
+
if (!url) {
|
|
44042
|
+
setState({ model: null, isLoading: false, error: null });
|
|
44043
|
+
return;
|
|
44044
|
+
}
|
|
44045
|
+
log10.debug("Loading", { url });
|
|
44046
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
44047
|
+
const assetRoot = resourceBasePath || detectAssetRoot2(url);
|
|
44048
|
+
const loader = new GLTFLoader.GLTFLoader();
|
|
44049
|
+
loader.setResourcePath(assetRoot);
|
|
44050
|
+
loader.load(
|
|
44051
|
+
url,
|
|
44052
|
+
(gltf) => {
|
|
44053
|
+
log10.debug("Loaded", { url });
|
|
44054
|
+
setState({
|
|
44055
|
+
model: gltf.scene,
|
|
44056
|
+
isLoading: false,
|
|
44057
|
+
error: null
|
|
44058
|
+
});
|
|
44059
|
+
},
|
|
44060
|
+
void 0,
|
|
44061
|
+
(err) => {
|
|
44062
|
+
log10.warn("Failed", { url, error: err instanceof Error ? err : String(err) });
|
|
44063
|
+
setState({
|
|
44064
|
+
model: null,
|
|
44065
|
+
isLoading: false,
|
|
44066
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
44067
|
+
});
|
|
44068
|
+
}
|
|
44069
|
+
);
|
|
44070
|
+
}, [url, resourceBasePath]);
|
|
44071
|
+
return state;
|
|
44072
|
+
}
|
|
44073
|
+
function ModelLoader({
|
|
44074
|
+
url,
|
|
44075
|
+
position = [0, 0, 0],
|
|
44076
|
+
scale = 1,
|
|
44077
|
+
rotation = [0, 0, 0],
|
|
44078
|
+
isSelected = false,
|
|
44079
|
+
isHovered = false,
|
|
44080
|
+
onClick,
|
|
44081
|
+
onHover,
|
|
44082
|
+
fallbackGeometry = "box",
|
|
44083
|
+
castShadow = true,
|
|
44084
|
+
receiveShadow = true,
|
|
44085
|
+
resourceBasePath
|
|
44086
|
+
}) {
|
|
44087
|
+
const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
|
|
44088
|
+
const model = React91.useMemo(() => {
|
|
44089
|
+
if (!loadedModel) return null;
|
|
44090
|
+
const cloned = loadedModel.clone();
|
|
44091
|
+
cloned.traverse((child) => {
|
|
44092
|
+
if (child instanceof THREE__namespace.Mesh) {
|
|
44093
|
+
child.castShadow = castShadow;
|
|
44094
|
+
child.receiveShadow = receiveShadow;
|
|
44095
|
+
}
|
|
44096
|
+
});
|
|
44097
|
+
return cloned;
|
|
44098
|
+
}, [loadedModel, castShadow, receiveShadow]);
|
|
44099
|
+
const scaleArray = React91.useMemo(() => {
|
|
44100
|
+
if (typeof scale === "number") {
|
|
44101
|
+
return [scale, scale, scale];
|
|
44102
|
+
}
|
|
44103
|
+
return scale;
|
|
44104
|
+
}, [scale]);
|
|
44105
|
+
const rotationRad = React91.useMemo(() => {
|
|
44106
|
+
return [
|
|
44107
|
+
rotation[0] * Math.PI / 180,
|
|
44108
|
+
rotation[1] * Math.PI / 180,
|
|
44109
|
+
rotation[2] * Math.PI / 180
|
|
44110
|
+
];
|
|
44111
|
+
}, [rotation]);
|
|
44112
|
+
if (isLoading) {
|
|
44113
|
+
return /* @__PURE__ */ jsxRuntime.jsx("group", { position, children: /* @__PURE__ */ jsxRuntime.jsxs("mesh", { rotation: [Math.PI / 2, 0, 0], children: [
|
|
44114
|
+
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.3, 0.35, 16] }),
|
|
44115
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#4a90d9", transparent: true, opacity: 0.8 })
|
|
44116
|
+
] }) });
|
|
44117
|
+
}
|
|
44118
|
+
if (error || !model) {
|
|
44119
|
+
if (fallbackGeometry === "none") {
|
|
44120
|
+
return /* @__PURE__ */ jsxRuntime.jsx("group", { position });
|
|
44121
|
+
}
|
|
44122
|
+
const fallbackProps = {
|
|
44123
|
+
onClick,
|
|
44124
|
+
onPointerOver: () => onHover?.(true),
|
|
44125
|
+
onPointerOut: () => onHover?.(false)
|
|
44126
|
+
};
|
|
44127
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("group", { position, children: [
|
|
44128
|
+
(isSelected || isHovered) && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
44129
|
+
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
|
|
44130
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44131
|
+
"meshBasicMaterial",
|
|
44132
|
+
{
|
|
44133
|
+
color: isSelected ? 16755200 : 16777215,
|
|
44134
|
+
transparent: true,
|
|
44135
|
+
opacity: 0.5
|
|
44136
|
+
}
|
|
44137
|
+
)
|
|
44138
|
+
] }),
|
|
44139
|
+
fallbackGeometry === "box" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
44140
|
+
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.8, 0.8, 0.8] }),
|
|
44141
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
44142
|
+
] }),
|
|
44143
|
+
fallbackGeometry === "sphere" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
44144
|
+
/* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [0.4, 16, 16] }),
|
|
44145
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
44146
|
+
] }),
|
|
44147
|
+
fallbackGeometry === "cylinder" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
44148
|
+
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.3, 0.3, 0.8, 16] }),
|
|
44149
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
44150
|
+
] })
|
|
44151
|
+
] });
|
|
44152
|
+
}
|
|
44153
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
44154
|
+
"group",
|
|
44155
|
+
{
|
|
44156
|
+
position,
|
|
44157
|
+
rotation: rotationRad,
|
|
44158
|
+
onClick,
|
|
44159
|
+
onPointerOver: () => onHover?.(true),
|
|
44160
|
+
onPointerOut: () => onHover?.(false),
|
|
44161
|
+
children: [
|
|
44162
|
+
(isSelected || isHovered) && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
44163
|
+
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
|
|
44164
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44165
|
+
"meshBasicMaterial",
|
|
44166
|
+
{
|
|
44167
|
+
color: isSelected ? 16755200 : 16777215,
|
|
44168
|
+
transparent: true,
|
|
44169
|
+
opacity: 0.5
|
|
44170
|
+
}
|
|
44171
|
+
)
|
|
44172
|
+
] }),
|
|
44173
|
+
/* @__PURE__ */ jsxRuntime.jsx("primitive", { object: model, scale: scaleArray })
|
|
44174
|
+
]
|
|
44175
|
+
}
|
|
44176
|
+
);
|
|
44177
|
+
}
|
|
44178
|
+
var log10;
|
|
44179
|
+
var init_ModelLoader = __esm({
|
|
44180
|
+
"components/game/molecules/three/components/ModelLoader.tsx"() {
|
|
44181
|
+
"use client";
|
|
44182
|
+
log10 = logger.createLogger("almadar:ui:game:model-loader");
|
|
44183
|
+
}
|
|
44184
|
+
});
|
|
44185
|
+
|
|
44186
|
+
// components/game/molecules/GameCanvas3D.css
|
|
44187
|
+
var init_GameCanvas3D = __esm({
|
|
44188
|
+
"components/game/molecules/GameCanvas3D.css"() {
|
|
44189
|
+
}
|
|
44190
|
+
});
|
|
44191
|
+
function CameraController({
|
|
44192
|
+
onCameraChange
|
|
44193
|
+
}) {
|
|
44194
|
+
const { camera } = fiber.useThree();
|
|
44195
|
+
React91.useEffect(() => {
|
|
44196
|
+
if (onCameraChange) {
|
|
44197
|
+
onCameraChange({
|
|
44198
|
+
x: camera.position.x,
|
|
44199
|
+
y: camera.position.y,
|
|
44200
|
+
z: camera.position.z
|
|
44201
|
+
});
|
|
44202
|
+
}
|
|
44203
|
+
}, [camera.position, onCameraChange]);
|
|
44204
|
+
return null;
|
|
44205
|
+
}
|
|
44206
|
+
var DEFAULT_GRID_CONFIG, GameCanvas3D;
|
|
44207
|
+
var init_GameCanvas3D2 = __esm({
|
|
44208
|
+
"components/game/molecules/GameCanvas3D.tsx"() {
|
|
44209
|
+
"use client";
|
|
44210
|
+
init_AssetLoader();
|
|
44211
|
+
init_useAssetLoader();
|
|
44212
|
+
init_useGameCanvas3DEvents();
|
|
44213
|
+
init_Canvas3DLoadingState2();
|
|
44214
|
+
init_Canvas3DErrorBoundary2();
|
|
44215
|
+
init_ModelLoader();
|
|
44216
|
+
init_cn();
|
|
44217
|
+
init_GameCanvas3D();
|
|
44218
|
+
DEFAULT_GRID_CONFIG = {
|
|
44219
|
+
cellSize: 1,
|
|
44220
|
+
offsetX: 0,
|
|
44221
|
+
offsetZ: 0
|
|
44222
|
+
};
|
|
44223
|
+
GameCanvas3D = React91.forwardRef(
|
|
44224
|
+
({
|
|
44225
|
+
tiles = [],
|
|
44226
|
+
units = [],
|
|
44227
|
+
features = [],
|
|
44228
|
+
events: events2 = [],
|
|
44229
|
+
orientation = "standard",
|
|
44230
|
+
cameraMode = "isometric",
|
|
44231
|
+
showGrid = true,
|
|
44232
|
+
showCoordinates = false,
|
|
44233
|
+
showTileInfo = false,
|
|
44234
|
+
overlay = "default",
|
|
44235
|
+
shadows = true,
|
|
44236
|
+
backgroundColor = "#1a1a2e",
|
|
44237
|
+
onTileClick,
|
|
44238
|
+
onUnitClick,
|
|
44239
|
+
onFeatureClick,
|
|
44240
|
+
onCanvasClick,
|
|
44241
|
+
onTileHover,
|
|
44242
|
+
onUnitAnimation,
|
|
44243
|
+
assetLoader: customAssetLoader,
|
|
44244
|
+
tileRenderer: CustomTileRenderer,
|
|
44245
|
+
unitRenderer: CustomUnitRenderer,
|
|
44246
|
+
featureRenderer: CustomFeatureRenderer,
|
|
44247
|
+
className,
|
|
44248
|
+
isLoading: externalLoading,
|
|
44249
|
+
error: externalError,
|
|
44250
|
+
entity,
|
|
44251
|
+
preloadAssets = [],
|
|
44252
|
+
tileClickEvent,
|
|
44253
|
+
unitClickEvent,
|
|
44254
|
+
featureClickEvent,
|
|
44255
|
+
canvasClickEvent,
|
|
44256
|
+
tileHoverEvent,
|
|
44257
|
+
tileLeaveEvent,
|
|
44258
|
+
unitAnimationEvent,
|
|
44259
|
+
cameraChangeEvent,
|
|
44260
|
+
loadingMessage = "Loading 3D Scene...",
|
|
44261
|
+
useInstancing = true,
|
|
44262
|
+
validMoves = [],
|
|
44263
|
+
attackTargets = [],
|
|
44264
|
+
selectedTileIds = [],
|
|
44265
|
+
selectedUnitId = null,
|
|
44266
|
+
children
|
|
44267
|
+
}, ref) => {
|
|
44268
|
+
const containerRef = React91.useRef(null);
|
|
44269
|
+
const controlsRef = React91.useRef(null);
|
|
44270
|
+
const [hoveredTile, setHoveredTile] = React91.useState(null);
|
|
44271
|
+
const [internalError, setInternalError] = React91.useState(null);
|
|
44272
|
+
const { isLoading: assetsLoading, progress, loaded, total } = useAssetLoader({
|
|
44273
|
+
preloadUrls: preloadAssets,
|
|
44274
|
+
loader: customAssetLoader
|
|
44275
|
+
});
|
|
44276
|
+
const eventHandlers = useGameCanvas3DEvents({
|
|
44277
|
+
tileClickEvent,
|
|
44278
|
+
unitClickEvent,
|
|
44279
|
+
featureClickEvent,
|
|
44280
|
+
canvasClickEvent,
|
|
44281
|
+
tileHoverEvent,
|
|
44282
|
+
tileLeaveEvent,
|
|
44283
|
+
unitAnimationEvent,
|
|
44284
|
+
cameraChangeEvent,
|
|
44285
|
+
onTileClick,
|
|
44286
|
+
onUnitClick,
|
|
44287
|
+
onFeatureClick,
|
|
44288
|
+
onCanvasClick,
|
|
44289
|
+
onTileHover,
|
|
44290
|
+
onUnitAnimation
|
|
44291
|
+
});
|
|
44292
|
+
const gridBounds = React91.useMemo(() => {
|
|
44293
|
+
if (tiles.length === 0) {
|
|
44294
|
+
return { minX: 0, maxX: 10, minZ: 0, maxZ: 10 };
|
|
44295
|
+
}
|
|
44296
|
+
const xs = tiles.map((t) => t.x);
|
|
44297
|
+
const zs = tiles.map((t) => t.z || t.y || 0);
|
|
44298
|
+
return {
|
|
44299
|
+
minX: Math.min(...xs),
|
|
44300
|
+
maxX: Math.max(...xs),
|
|
44301
|
+
minZ: Math.min(...zs),
|
|
44302
|
+
maxZ: Math.max(...zs)
|
|
44303
|
+
};
|
|
44304
|
+
}, [tiles]);
|
|
44305
|
+
const cameraTarget = React91.useMemo(() => {
|
|
44306
|
+
return [
|
|
44307
|
+
(gridBounds.minX + gridBounds.maxX) / 2,
|
|
44308
|
+
0,
|
|
44309
|
+
(gridBounds.minZ + gridBounds.maxZ) / 2
|
|
44310
|
+
];
|
|
44311
|
+
}, [gridBounds]);
|
|
44312
|
+
const gridConfig = React91.useMemo(
|
|
44313
|
+
() => ({
|
|
44314
|
+
...DEFAULT_GRID_CONFIG,
|
|
44315
|
+
offsetX: -(gridBounds.maxX - gridBounds.minX) / 2,
|
|
44316
|
+
offsetZ: -(gridBounds.maxZ - gridBounds.minZ) / 2
|
|
44317
|
+
}),
|
|
44318
|
+
[gridBounds]
|
|
44319
|
+
);
|
|
44320
|
+
const gridToWorld = React91.useCallback(
|
|
44321
|
+
(x, z, y = 0) => {
|
|
44322
|
+
const worldX = (x - gridBounds.minX) * gridConfig.cellSize;
|
|
44323
|
+
const worldZ = (z - gridBounds.minZ) * gridConfig.cellSize;
|
|
44324
|
+
return [worldX, y * gridConfig.cellSize, worldZ];
|
|
44325
|
+
},
|
|
44326
|
+
[gridBounds, gridConfig]
|
|
44327
|
+
);
|
|
44328
|
+
React91.useImperativeHandle(ref, () => ({
|
|
44329
|
+
getCameraPosition: () => {
|
|
44330
|
+
if (controlsRef.current) {
|
|
44331
|
+
const pos = controlsRef.current.object.position;
|
|
44332
|
+
return new THREE__namespace.Vector3(pos.x, pos.y, pos.z);
|
|
44333
|
+
}
|
|
44334
|
+
return null;
|
|
44335
|
+
},
|
|
44336
|
+
setCameraPosition: (x, y, z) => {
|
|
44337
|
+
if (controlsRef.current) {
|
|
44338
|
+
controlsRef.current.object.position.set(x, y, z);
|
|
44339
|
+
controlsRef.current.update();
|
|
44340
|
+
}
|
|
44341
|
+
},
|
|
44342
|
+
lookAt: (x, y, z) => {
|
|
44343
|
+
if (controlsRef.current) {
|
|
44344
|
+
controlsRef.current.target.set(x, y, z);
|
|
44345
|
+
controlsRef.current.update();
|
|
44346
|
+
}
|
|
44347
|
+
},
|
|
44348
|
+
resetCamera: () => {
|
|
44349
|
+
if (controlsRef.current) {
|
|
44350
|
+
controlsRef.current.reset();
|
|
44351
|
+
}
|
|
44352
|
+
},
|
|
44353
|
+
screenshot: () => {
|
|
44354
|
+
const canvas = containerRef.current?.querySelector("canvas");
|
|
44355
|
+
if (canvas) {
|
|
44356
|
+
return canvas.toDataURL("image/png");
|
|
44357
|
+
}
|
|
44358
|
+
return null;
|
|
44359
|
+
},
|
|
44360
|
+
export: () => ({
|
|
44361
|
+
tiles,
|
|
44362
|
+
units,
|
|
44363
|
+
features
|
|
44364
|
+
})
|
|
44365
|
+
}));
|
|
44366
|
+
const handleTileClick = React91.useCallback(
|
|
44367
|
+
(tile, event) => {
|
|
44368
|
+
eventHandlers.handleTileClick(tile, event);
|
|
44369
|
+
},
|
|
44370
|
+
[eventHandlers]
|
|
44371
|
+
);
|
|
44372
|
+
const handleUnitClick = React91.useCallback(
|
|
44373
|
+
(unit, event) => {
|
|
44374
|
+
eventHandlers.handleUnitClick(unit, event);
|
|
44375
|
+
},
|
|
44376
|
+
[eventHandlers]
|
|
44377
|
+
);
|
|
44378
|
+
const handleFeatureClick = React91.useCallback(
|
|
44379
|
+
(feature, event) => {
|
|
44380
|
+
if (event) {
|
|
44381
|
+
eventHandlers.handleFeatureClick(feature, event);
|
|
44382
|
+
}
|
|
44383
|
+
},
|
|
44384
|
+
[eventHandlers]
|
|
44385
|
+
);
|
|
44386
|
+
const handleTileHover = React91.useCallback(
|
|
44387
|
+
(tile, event) => {
|
|
44388
|
+
setHoveredTile(tile);
|
|
44389
|
+
if (event) {
|
|
44390
|
+
eventHandlers.handleTileHover(tile, event);
|
|
44391
|
+
}
|
|
44392
|
+
},
|
|
44393
|
+
[eventHandlers]
|
|
44394
|
+
);
|
|
44395
|
+
const cameraConfig = React91.useMemo(() => {
|
|
44396
|
+
const size = Math.max(
|
|
44397
|
+
gridBounds.maxX - gridBounds.minX,
|
|
44398
|
+
gridBounds.maxZ - gridBounds.minZ
|
|
44399
|
+
);
|
|
44400
|
+
const distance = size * 1.5;
|
|
44401
|
+
switch (cameraMode) {
|
|
44402
|
+
case "isometric":
|
|
44403
|
+
return {
|
|
44404
|
+
position: [distance, distance * 0.8, distance],
|
|
44405
|
+
fov: 45
|
|
44406
|
+
};
|
|
44407
|
+
case "top-down":
|
|
44408
|
+
return {
|
|
44409
|
+
position: [0, distance * 2, 0],
|
|
44410
|
+
fov: 45
|
|
44411
|
+
};
|
|
44412
|
+
case "perspective":
|
|
44413
|
+
default:
|
|
44414
|
+
return {
|
|
44415
|
+
position: [distance, distance, distance],
|
|
44416
|
+
fov: 45
|
|
44417
|
+
};
|
|
44418
|
+
}
|
|
44419
|
+
}, [cameraMode, gridBounds]);
|
|
44420
|
+
const DefaultTileRenderer = React91.useCallback(
|
|
44421
|
+
({ tile, position }) => {
|
|
44422
|
+
const isSelected = tile.id ? selectedTileIds.includes(tile.id) : false;
|
|
44423
|
+
const isHovered = hoveredTile?.id === tile.id;
|
|
44424
|
+
const isValidMove = validMoves.some(
|
|
44425
|
+
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
44426
|
+
);
|
|
44427
|
+
const isAttackTarget = attackTargets.some(
|
|
44428
|
+
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
44429
|
+
);
|
|
44430
|
+
let color = 8421504;
|
|
44431
|
+
if (tile.type === "water") color = 4491468;
|
|
44432
|
+
else if (tile.type === "grass") color = 4500036;
|
|
44433
|
+
else if (tile.type === "sand") color = 14535816;
|
|
44434
|
+
else if (tile.type === "rock") color = 8947848;
|
|
44435
|
+
else if (tile.type === "snow") color = 15658734;
|
|
44436
|
+
let emissive = 0;
|
|
44437
|
+
if (isSelected) emissive = 4473924;
|
|
44438
|
+
else if (isAttackTarget) emissive = 4456448;
|
|
44439
|
+
else if (isValidMove) emissive = 17408;
|
|
44440
|
+
else if (isHovered) emissive = 2236962;
|
|
44441
|
+
if (tile.modelUrl) {
|
|
44442
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
44443
|
+
"group",
|
|
44444
|
+
{
|
|
44445
|
+
position,
|
|
44446
|
+
onClick: (e) => handleTileClick(tile, e),
|
|
44447
|
+
onPointerEnter: (e) => handleTileHover(tile, e),
|
|
44448
|
+
onPointerLeave: (e) => handleTileHover(null, e),
|
|
44449
|
+
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
44450
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
44451
|
+
ModelLoader,
|
|
44452
|
+
{
|
|
44453
|
+
url: tile.modelUrl,
|
|
44454
|
+
scale: 0.95,
|
|
44455
|
+
fallbackGeometry: "box",
|
|
44456
|
+
castShadow: true,
|
|
44457
|
+
receiveShadow: true
|
|
44458
|
+
}
|
|
44459
|
+
)
|
|
44460
|
+
}
|
|
44461
|
+
);
|
|
44462
|
+
}
|
|
44463
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
44464
|
+
"mesh",
|
|
44465
|
+
{
|
|
44466
|
+
position,
|
|
44467
|
+
onClick: (e) => handleTileClick(tile, e),
|
|
44468
|
+
onPointerEnter: (e) => handleTileHover(tile, e),
|
|
44469
|
+
onPointerLeave: (e) => handleTileHover(null, e),
|
|
44470
|
+
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
44471
|
+
children: [
|
|
44472
|
+
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.95, 0.2, 0.95] }),
|
|
44473
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color, emissive })
|
|
44474
|
+
]
|
|
44475
|
+
}
|
|
44476
|
+
);
|
|
44477
|
+
},
|
|
44478
|
+
[selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover]
|
|
44479
|
+
);
|
|
44480
|
+
const DefaultUnitRenderer = React91.useCallback(
|
|
44481
|
+
({ unit, position }) => {
|
|
44482
|
+
const isSelected = selectedUnitId === unit.id;
|
|
44483
|
+
const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
|
|
44484
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
44485
|
+
"group",
|
|
44486
|
+
{
|
|
44487
|
+
position,
|
|
44488
|
+
onClick: (e) => handleUnitClick(unit, e),
|
|
44489
|
+
userData: { type: "unit", unitId: unit.id },
|
|
44490
|
+
children: [
|
|
44491
|
+
isSelected && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.05, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
44492
|
+
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
|
|
44493
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
|
|
44494
|
+
] }),
|
|
44495
|
+
unit.modelUrl ? (
|
|
44496
|
+
/* GLB unit model (box fallback while loading / on error) */
|
|
44497
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44498
|
+
ModelLoader,
|
|
44499
|
+
{
|
|
44500
|
+
url: unit.modelUrl,
|
|
44501
|
+
scale: 0.5,
|
|
44502
|
+
fallbackGeometry: "box",
|
|
44503
|
+
castShadow: true
|
|
44504
|
+
}
|
|
44505
|
+
)
|
|
44506
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
44507
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.3, 0], children: [
|
|
44508
|
+
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.3, 0.3, 0.1, 8] }),
|
|
44509
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
44510
|
+
] }),
|
|
44511
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.6, 0], children: [
|
|
44512
|
+
/* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [0.2, 0.4, 4, 8] }),
|
|
44513
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
44514
|
+
] }),
|
|
44515
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
44516
|
+
/* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [0.12, 8, 8] }),
|
|
44517
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
44518
|
+
] })
|
|
44519
|
+
] }),
|
|
44520
|
+
unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("group", { position: [0, 1.2, 0], children: [
|
|
44521
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [-0.25, 0, 0], children: [
|
|
44522
|
+
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.5, 0.05] }),
|
|
44523
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: 3355443 })
|
|
44524
|
+
] }),
|
|
44525
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
44526
|
+
"mesh",
|
|
44527
|
+
{
|
|
44528
|
+
position: [
|
|
44529
|
+
-0.25 + 0.5 * (unit.health / unit.maxHealth) / 2,
|
|
44530
|
+
0,
|
|
44531
|
+
0.01
|
|
44532
|
+
],
|
|
44533
|
+
children: [
|
|
44534
|
+
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.5 * (unit.health / unit.maxHealth), 0.05] }),
|
|
44535
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44536
|
+
"meshBasicMaterial",
|
|
44537
|
+
{
|
|
44538
|
+
color: unit.health / unit.maxHealth > 0.5 ? 4500036 : unit.health / unit.maxHealth > 0.25 ? 11184708 : 16729156
|
|
44539
|
+
}
|
|
44540
|
+
)
|
|
44541
|
+
]
|
|
44542
|
+
}
|
|
44543
|
+
)
|
|
44544
|
+
] })
|
|
44545
|
+
]
|
|
44546
|
+
}
|
|
44547
|
+
);
|
|
44548
|
+
},
|
|
44549
|
+
[selectedUnitId, handleUnitClick]
|
|
44550
|
+
);
|
|
44551
|
+
const DefaultFeatureRenderer = React91.useCallback(
|
|
44552
|
+
({
|
|
44553
|
+
feature,
|
|
44554
|
+
position
|
|
44555
|
+
}) => {
|
|
44556
|
+
if (feature.assetUrl) {
|
|
44557
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
44558
|
+
ModelLoader,
|
|
44559
|
+
{
|
|
44560
|
+
url: feature.assetUrl,
|
|
44561
|
+
position,
|
|
44562
|
+
scale: 0.5,
|
|
44563
|
+
rotation: [0, feature.rotation ?? 0, 0],
|
|
44564
|
+
onClick: () => handleFeatureClick(feature, null),
|
|
44565
|
+
fallbackGeometry: "box"
|
|
44566
|
+
},
|
|
44567
|
+
feature.id
|
|
44568
|
+
);
|
|
44569
|
+
}
|
|
44570
|
+
if (feature.type === "tree") {
|
|
44571
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
44572
|
+
"group",
|
|
44573
|
+
{
|
|
44574
|
+
position,
|
|
44575
|
+
onClick: (e) => handleFeatureClick(feature, e),
|
|
44576
|
+
userData: { type: "feature", featureId: feature.id },
|
|
44577
|
+
children: [
|
|
44578
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.4, 0], children: [
|
|
44579
|
+
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.1, 0.15, 0.8, 6] }),
|
|
44580
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 9127187 })
|
|
44581
|
+
] }),
|
|
44582
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
44583
|
+
/* @__PURE__ */ jsxRuntime.jsx("coneGeometry", { args: [0.5, 0.8, 8] }),
|
|
44584
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 2263842 })
|
|
44585
|
+
] })
|
|
44586
|
+
]
|
|
44587
|
+
}
|
|
44588
|
+
);
|
|
44589
|
+
}
|
|
44590
|
+
if (feature.type === "rock") {
|
|
44591
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
44592
|
+
"mesh",
|
|
44593
|
+
{
|
|
44594
|
+
position: [position[0], position[1] + 0.3, position[2]],
|
|
44595
|
+
onClick: (e) => handleFeatureClick(feature, e),
|
|
44596
|
+
userData: { type: "feature", featureId: feature.id },
|
|
44597
|
+
children: [
|
|
44598
|
+
/* @__PURE__ */ jsxRuntime.jsx("dodecahedronGeometry", { args: [0.3, 0] }),
|
|
44599
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 8421504 })
|
|
44600
|
+
]
|
|
44601
|
+
}
|
|
44602
|
+
);
|
|
44603
|
+
}
|
|
44604
|
+
return null;
|
|
44605
|
+
},
|
|
44606
|
+
[handleFeatureClick]
|
|
44607
|
+
);
|
|
44608
|
+
if (externalLoading || assetsLoading && preloadAssets.length > 0) {
|
|
44609
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
44610
|
+
Canvas3DLoadingState,
|
|
44611
|
+
{
|
|
44612
|
+
progress,
|
|
44613
|
+
loaded,
|
|
44614
|
+
total,
|
|
44615
|
+
message: loadingMessage,
|
|
44616
|
+
className
|
|
44617
|
+
}
|
|
44618
|
+
);
|
|
44619
|
+
}
|
|
44620
|
+
const displayError = externalError || internalError;
|
|
44621
|
+
if (displayError) {
|
|
44622
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Canvas3DErrorBoundary, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "game-canvas-3d game-canvas-3d--error", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__error", children: [
|
|
44623
|
+
"Error: ",
|
|
44624
|
+
displayError
|
|
44625
|
+
] }) }) });
|
|
44626
|
+
}
|
|
44627
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
44628
|
+
Canvas3DErrorBoundary,
|
|
44629
|
+
{
|
|
44630
|
+
onError: (err) => setInternalError(err.message),
|
|
44631
|
+
onReset: () => setInternalError(null),
|
|
44632
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
44633
|
+
"div",
|
|
44634
|
+
{
|
|
44635
|
+
ref: containerRef,
|
|
44636
|
+
className: cn("game-canvas-3d relative w-full h-full min-h-[85vh] overflow-hidden", className),
|
|
44637
|
+
"data-orientation": orientation,
|
|
44638
|
+
"data-camera-mode": cameraMode,
|
|
44639
|
+
"data-overlay": overlay,
|
|
44640
|
+
children: [
|
|
44641
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
44642
|
+
fiber.Canvas,
|
|
44643
|
+
{
|
|
44644
|
+
shadows,
|
|
44645
|
+
camera: {
|
|
44646
|
+
position: cameraConfig.position,
|
|
44647
|
+
fov: cameraConfig.fov,
|
|
44648
|
+
near: 0.1,
|
|
44649
|
+
far: 1e3
|
|
44650
|
+
},
|
|
44651
|
+
style: { background: backgroundColor },
|
|
44652
|
+
onClick: (e) => {
|
|
44653
|
+
if (e.target === e.currentTarget) {
|
|
44654
|
+
eventHandlers.handleCanvasClick(e);
|
|
44655
|
+
}
|
|
44656
|
+
},
|
|
44657
|
+
children: [
|
|
44658
|
+
/* @__PURE__ */ jsxRuntime.jsx(CameraController, { onCameraChange: eventHandlers.handleCameraChange }),
|
|
44659
|
+
/* @__PURE__ */ jsxRuntime.jsx("ambientLight", { intensity: 0.6 }),
|
|
44660
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44661
|
+
"directionalLight",
|
|
44662
|
+
{
|
|
44663
|
+
position: [10, 20, 10],
|
|
44664
|
+
intensity: 0.8,
|
|
44665
|
+
castShadow: shadows,
|
|
44666
|
+
"shadow-mapSize": [2048, 2048]
|
|
44667
|
+
}
|
|
44668
|
+
),
|
|
44669
|
+
/* @__PURE__ */ jsxRuntime.jsx("hemisphereLight", { intensity: 0.3, color: "#87ceeb", groundColor: "#362d1d" }),
|
|
44670
|
+
showGrid && /* @__PURE__ */ jsxRuntime.jsx(
|
|
44671
|
+
drei.Grid,
|
|
44672
|
+
{
|
|
44673
|
+
args: [
|
|
44674
|
+
Math.max(gridBounds.maxX - gridBounds.minX + 2, 10),
|
|
44675
|
+
Math.max(gridBounds.maxZ - gridBounds.minZ + 2, 10)
|
|
44676
|
+
],
|
|
44677
|
+
position: [
|
|
44678
|
+
(gridBounds.maxX - gridBounds.minX) / 2 - 0.5,
|
|
44679
|
+
0,
|
|
44680
|
+
(gridBounds.maxZ - gridBounds.minZ) / 2 - 0.5
|
|
44681
|
+
],
|
|
44682
|
+
cellSize: 1,
|
|
44683
|
+
cellThickness: 1,
|
|
44684
|
+
cellColor: "#444444",
|
|
44685
|
+
sectionSize: 5,
|
|
44686
|
+
sectionThickness: 1.5,
|
|
44687
|
+
sectionColor: "#666666",
|
|
44688
|
+
fadeDistance: 50,
|
|
44689
|
+
fadeStrength: 1
|
|
44690
|
+
}
|
|
44691
|
+
),
|
|
44692
|
+
tiles.map((tile, index) => {
|
|
44693
|
+
const position = gridToWorld(
|
|
44694
|
+
tile.x,
|
|
44695
|
+
tile.z ?? tile.y ?? 0,
|
|
44696
|
+
tile.elevation ?? 0
|
|
44697
|
+
);
|
|
44698
|
+
const Renderer = CustomTileRenderer || DefaultTileRenderer;
|
|
44699
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
|
|
44700
|
+
}),
|
|
44701
|
+
features.map((feature, index) => {
|
|
44702
|
+
const position = gridToWorld(
|
|
44703
|
+
feature.x,
|
|
44704
|
+
feature.z ?? feature.y ?? 0,
|
|
44705
|
+
(feature.elevation ?? 0) + 0.5
|
|
44706
|
+
);
|
|
44707
|
+
const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
|
|
44708
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
|
|
44709
|
+
}),
|
|
44710
|
+
units.map((unit) => {
|
|
44711
|
+
const position = gridToWorld(
|
|
44712
|
+
unit.x ?? 0,
|
|
44713
|
+
unit.z ?? unit.y ?? 0,
|
|
44714
|
+
(unit.elevation ?? 0) + 0.5
|
|
44715
|
+
);
|
|
44716
|
+
const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
|
|
44717
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { unit, position }, unit.id);
|
|
44718
|
+
}),
|
|
44719
|
+
children,
|
|
44720
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44721
|
+
drei.OrbitControls,
|
|
44722
|
+
{
|
|
44723
|
+
ref: controlsRef,
|
|
44724
|
+
target: cameraTarget,
|
|
44725
|
+
enableDamping: true,
|
|
44726
|
+
dampingFactor: 0.05,
|
|
44727
|
+
minDistance: 2,
|
|
44728
|
+
maxDistance: 100,
|
|
44729
|
+
maxPolarAngle: Math.PI / 2 - 0.1
|
|
44730
|
+
}
|
|
44731
|
+
)
|
|
44732
|
+
]
|
|
44733
|
+
}
|
|
44734
|
+
),
|
|
44735
|
+
showCoordinates && hoveredTile && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__coordinates", children: [
|
|
44736
|
+
"X: ",
|
|
44737
|
+
hoveredTile.x,
|
|
44738
|
+
", Z: ",
|
|
44739
|
+
hoveredTile.z ?? hoveredTile.y ?? 0
|
|
44740
|
+
] }),
|
|
44741
|
+
showTileInfo && hoveredTile && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__tile-info", children: [
|
|
44742
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "tile-info__type", children: hoveredTile.type }),
|
|
44743
|
+
hoveredTile.terrain && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tile-info__terrain", children: hoveredTile.terrain })
|
|
44744
|
+
] })
|
|
44745
|
+
]
|
|
44746
|
+
}
|
|
44747
|
+
)
|
|
44748
|
+
}
|
|
44749
|
+
);
|
|
44750
|
+
}
|
|
44751
|
+
);
|
|
44752
|
+
GameCanvas3D.displayName = "GameCanvas3D";
|
|
44753
|
+
}
|
|
44754
|
+
});
|
|
44755
|
+
function GameBoard3D({
|
|
44756
|
+
entity,
|
|
44757
|
+
tiles = [],
|
|
44758
|
+
features = [],
|
|
44759
|
+
cameraMode = "perspective",
|
|
44760
|
+
backgroundColor = "#2a1a1a",
|
|
44761
|
+
tileClickEvent,
|
|
44762
|
+
unitClickEvent,
|
|
44763
|
+
attackEvent,
|
|
44764
|
+
endTurnEvent,
|
|
44765
|
+
cancelEvent,
|
|
44766
|
+
playAgainEvent,
|
|
44767
|
+
gameEndEvent,
|
|
44768
|
+
className
|
|
44769
|
+
}) {
|
|
44770
|
+
const row = boardEntity(entity);
|
|
44771
|
+
const eventBus = useEventBus();
|
|
44772
|
+
const entityUnits = row ? rows(row.units) : [];
|
|
44773
|
+
const units = entityUnits;
|
|
44774
|
+
const selectedUnitId = row ? str(row.selectedUnitId) || null : null;
|
|
44775
|
+
const phase = row ? str(row.phase) : "observation";
|
|
44776
|
+
const result = row ? str(row.result) : "none";
|
|
44777
|
+
const validMoves = row && Array.isArray(row.validMoves) ? row.validMoves : [];
|
|
44778
|
+
const attackTargets = row && Array.isArray(row.attackTargets) ? row.attackTargets : [];
|
|
44779
|
+
const turn = row ? num(row.turn) : 0;
|
|
44780
|
+
const currentTeam = row ? str(row.currentTeam) : "player";
|
|
44781
|
+
const isGameOver = result !== "none";
|
|
44782
|
+
const gameEndEmittedRef = React91.useRef(false);
|
|
44783
|
+
React91.useEffect(() => {
|
|
44784
|
+
if ((result === "victory" || result === "defeat") && gameEndEvent) {
|
|
44785
|
+
if (!gameEndEmittedRef.current) {
|
|
44786
|
+
gameEndEmittedRef.current = true;
|
|
44787
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
44788
|
+
}
|
|
44789
|
+
} else {
|
|
44790
|
+
gameEndEmittedRef.current = false;
|
|
44791
|
+
}
|
|
44792
|
+
}, [result, gameEndEvent, eventBus]);
|
|
44793
|
+
const checkGameEnd = React91.useCallback(() => {
|
|
44794
|
+
const alivePlayer = entityUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
|
|
44795
|
+
const aliveEnemy = entityUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
|
|
44796
|
+
if (alivePlayer.length === 0 && gameEndEvent) {
|
|
44797
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result: "defeat" });
|
|
44798
|
+
} else if (aliveEnemy.length === 0 && gameEndEvent) {
|
|
44799
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result: "victory" });
|
|
44800
|
+
}
|
|
44801
|
+
}, [entityUnits, gameEndEvent, eventBus]);
|
|
44802
|
+
const handleUnitClickCallback = React91.useCallback((isoUnit) => {
|
|
44803
|
+
if (phase !== "action" || !selectedUnitId || !attackEvent) return;
|
|
44804
|
+
const attackerRow = entityUnits.find((u) => str(u.id) === selectedUnitId);
|
|
44805
|
+
const targetRow = entityUnits.find((u) => str(u.id) === isoUnit.id);
|
|
44806
|
+
if (!attackerRow || !targetRow) return;
|
|
44807
|
+
if (unitTeam(targetRow) !== "enemy" || unitHealth(targetRow) <= 0) return;
|
|
44808
|
+
const ap = unitPosition(attackerRow);
|
|
44809
|
+
const tp = unitPosition(targetRow);
|
|
44810
|
+
const dx = Math.abs(ap.x - tp.x);
|
|
44811
|
+
const dy = Math.abs(ap.y - tp.y);
|
|
44812
|
+
if (dx <= 1 && dy <= 1 && dx + dy > 0) {
|
|
44813
|
+
const damage = Math.max(1, num(attackerRow.attack) - num(targetRow.defense));
|
|
44814
|
+
eventBus.emit(`UI:${attackEvent}`, {
|
|
44815
|
+
attackerId: str(attackerRow.id),
|
|
44816
|
+
targetId: str(targetRow.id),
|
|
44817
|
+
damage
|
|
44818
|
+
});
|
|
44819
|
+
setTimeout(checkGameEnd, 100);
|
|
44820
|
+
}
|
|
44821
|
+
}, [phase, selectedUnitId, entityUnits, attackEvent, eventBus, checkGameEnd]);
|
|
44822
|
+
const handleEndTurn = React91.useCallback(() => {
|
|
44823
|
+
if (endTurnEvent) eventBus.emit(`UI:${endTurnEvent}`, {});
|
|
44824
|
+
}, [endTurnEvent, eventBus]);
|
|
44825
|
+
const handleCancel = React91.useCallback(() => {
|
|
44826
|
+
if (cancelEvent) eventBus.emit(`UI:${cancelEvent}`, {});
|
|
44827
|
+
}, [cancelEvent, eventBus]);
|
|
44828
|
+
const handlePlayAgain = React91.useCallback(() => {
|
|
44829
|
+
if (playAgainEvent) eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
44830
|
+
}, [playAgainEvent, eventBus]);
|
|
44831
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("game-board-3d block w-full min-h-[85vh] relative", className), children: [
|
|
44832
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-board-3d__status absolute top-3 left-3 z-10 flex gap-2 items-center", children: [
|
|
44833
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "status__phase capitalize", children: phase.replace("_", " ") }),
|
|
44834
|
+
currentTeam && !isGameOver && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", color: "muted", className: "status__team", children: [
|
|
44835
|
+
"\u2014 ",
|
|
44836
|
+
currentTeam === "player" ? "Your Turn" : "Enemy's Turn"
|
|
44837
|
+
] }),
|
|
44838
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", color: "muted", className: "status__turn", children: [
|
|
44839
|
+
"Turn ",
|
|
44840
|
+
turn
|
|
44841
|
+
] })
|
|
44842
|
+
] }),
|
|
44843
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44844
|
+
GameCanvas3D,
|
|
44845
|
+
{
|
|
44846
|
+
tiles,
|
|
44847
|
+
units,
|
|
44848
|
+
features,
|
|
44849
|
+
cameraMode,
|
|
44850
|
+
showGrid: true,
|
|
44851
|
+
showCoordinates: false,
|
|
44852
|
+
showTileInfo: false,
|
|
44853
|
+
shadows: true,
|
|
44854
|
+
backgroundColor,
|
|
44855
|
+
tileClickEvent,
|
|
44856
|
+
unitClickEvent,
|
|
44857
|
+
onUnitClick: handleUnitClickCallback,
|
|
44858
|
+
selectedUnitId,
|
|
44859
|
+
validMoves,
|
|
44860
|
+
attackTargets,
|
|
44861
|
+
className: "game-board-3d__canvas w-full min-h-[85vh]"
|
|
44862
|
+
}
|
|
44863
|
+
),
|
|
44864
|
+
!isGameOver && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "fixed bottom-6 right-6 z-50", gap: "sm", children: [
|
|
44865
|
+
(phase === "selection" || phase === "movement" || phase === "action") && /* @__PURE__ */ jsxRuntime.jsx(
|
|
44866
|
+
Button,
|
|
44867
|
+
{
|
|
44868
|
+
variant: "secondary",
|
|
44869
|
+
className: "shadow-xl",
|
|
44870
|
+
onClick: handleCancel,
|
|
44871
|
+
children: "Cancel"
|
|
44872
|
+
}
|
|
44873
|
+
),
|
|
44874
|
+
phase !== "enemy_turn" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
44875
|
+
Button,
|
|
44876
|
+
{
|
|
44877
|
+
variant: "primary",
|
|
44878
|
+
className: "shadow-xl",
|
|
44879
|
+
onClick: handleEndTurn,
|
|
44880
|
+
children: "End Turn"
|
|
44881
|
+
}
|
|
44882
|
+
)
|
|
44883
|
+
] }),
|
|
44884
|
+
isGameOver && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "game-board-3d__overlay absolute inset-0 z-20 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { align: "center", gap: "md", className: "overlay__card p-8 rounded-xl bg-gray-900/90 shadow-2xl", children: [
|
|
44885
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44886
|
+
Typography,
|
|
44887
|
+
{
|
|
44888
|
+
variant: "h2",
|
|
44889
|
+
className: cn(
|
|
44890
|
+
"overlay__result",
|
|
44891
|
+
result === "victory" ? "text-yellow-400" : "text-red-500"
|
|
44892
|
+
),
|
|
44893
|
+
children: result === "victory" ? "Victory!" : "Defeat"
|
|
44894
|
+
}
|
|
44895
|
+
),
|
|
44896
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "body", color: "muted", className: "overlay__turn-count", children: [
|
|
44897
|
+
"Completed in ",
|
|
44898
|
+
turn,
|
|
44899
|
+
" turn",
|
|
44900
|
+
turn !== 1 ? "s" : ""
|
|
44901
|
+
] }),
|
|
44902
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44903
|
+
Button,
|
|
44904
|
+
{
|
|
44905
|
+
variant: "primary",
|
|
44906
|
+
className: "px-8 py-3 font-semibold",
|
|
44907
|
+
onClick: handlePlayAgain,
|
|
44908
|
+
children: "Play Again"
|
|
44909
|
+
}
|
|
44910
|
+
)
|
|
44911
|
+
] }) })
|
|
44912
|
+
] });
|
|
44913
|
+
}
|
|
44914
|
+
var init_GameBoard3D = __esm({
|
|
44915
|
+
"components/game/organisms/GameBoard3D.tsx"() {
|
|
44916
|
+
"use client";
|
|
44917
|
+
init_cn();
|
|
44918
|
+
init_Button();
|
|
44919
|
+
init_Typography();
|
|
44920
|
+
init_Stack();
|
|
44921
|
+
init_useEventBus();
|
|
44922
|
+
init_GameCanvas3D2();
|
|
44923
|
+
init_boardEntity();
|
|
44924
|
+
GameBoard3D.displayName = "GameBoard3D";
|
|
44925
|
+
}
|
|
44926
|
+
});
|
|
43271
44927
|
var GameShell;
|
|
43272
44928
|
var init_GameShell = __esm({
|
|
43273
44929
|
"components/game/templates/GameShell.tsx"() {
|
|
@@ -44810,6 +46466,236 @@ var init_NegotiatorBoard = __esm({
|
|
|
44810
46466
|
NegotiatorBoard.displayName = "NegotiatorBoard";
|
|
44811
46467
|
}
|
|
44812
46468
|
});
|
|
46469
|
+
function resolveEntity(entity) {
|
|
46470
|
+
if (!entity) return {};
|
|
46471
|
+
if (Array.isArray(entity)) return entity[0] ?? {};
|
|
46472
|
+
return entity;
|
|
46473
|
+
}
|
|
46474
|
+
function numField(row, key, fallback) {
|
|
46475
|
+
const v = row[key];
|
|
46476
|
+
return typeof v === "number" ? v : fallback;
|
|
46477
|
+
}
|
|
46478
|
+
function strField(row, key, fallback) {
|
|
46479
|
+
const v = row[key];
|
|
46480
|
+
return typeof v === "string" ? v : fallback;
|
|
46481
|
+
}
|
|
46482
|
+
function PlatformerBoard({
|
|
46483
|
+
entity,
|
|
46484
|
+
player: propPlayer,
|
|
46485
|
+
platforms: propPlatforms,
|
|
46486
|
+
result: propResult,
|
|
46487
|
+
lives: propLives,
|
|
46488
|
+
score: propScore,
|
|
46489
|
+
level: propLevel,
|
|
46490
|
+
worldWidth = 800,
|
|
46491
|
+
worldHeight = 400,
|
|
46492
|
+
canvasWidth = 800,
|
|
46493
|
+
canvasHeight = 400,
|
|
46494
|
+
playerSprite,
|
|
46495
|
+
tileSprites,
|
|
46496
|
+
bgColor,
|
|
46497
|
+
leftEvent = "LEFT",
|
|
46498
|
+
rightEvent = "RIGHT",
|
|
46499
|
+
jumpEvent = "JUMP",
|
|
46500
|
+
stopEvent = "STOP",
|
|
46501
|
+
playAgainEvent = "PLAY_AGAIN",
|
|
46502
|
+
gameEndEvent = "GAME_END",
|
|
46503
|
+
className
|
|
46504
|
+
}) {
|
|
46505
|
+
const row = resolveEntity(entity);
|
|
46506
|
+
const eventBus = useEventBus();
|
|
46507
|
+
const { t } = hooks.useTranslate();
|
|
46508
|
+
const result = propResult ?? strField(row, "result", "none");
|
|
46509
|
+
propLives ?? numField(row, "lives", 3);
|
|
46510
|
+
const score = propScore ?? numField(row, "score", 0);
|
|
46511
|
+
const level = propLevel ?? numField(row, "level", 1);
|
|
46512
|
+
const entityPlayer = row["player"];
|
|
46513
|
+
const player = propPlayer ?? entityPlayer ?? {
|
|
46514
|
+
x: 80,
|
|
46515
|
+
y: 320,
|
|
46516
|
+
width: 32,
|
|
46517
|
+
height: 48,
|
|
46518
|
+
vx: 0,
|
|
46519
|
+
vy: 0,
|
|
46520
|
+
grounded: false,
|
|
46521
|
+
facingRight: true
|
|
46522
|
+
};
|
|
46523
|
+
const entityPlatforms = Array.isArray(row["platforms"]) ? row["platforms"] : void 0;
|
|
46524
|
+
const platforms = propPlatforms ?? entityPlatforms ?? [
|
|
46525
|
+
{ x: 0, y: 368, width: 800, height: 32, type: "ground" },
|
|
46526
|
+
{ x: 150, y: 280, width: 160, height: 16, type: "platform" },
|
|
46527
|
+
{ x: 420, y: 220, width: 160, height: 16, type: "platform" },
|
|
46528
|
+
{ x: 580, y: 300, width: 80, height: 16, type: "hazard" },
|
|
46529
|
+
{ x: 700, y: 340, width: 64, height: 28, type: "goal" }
|
|
46530
|
+
];
|
|
46531
|
+
const handleRestart = React91.useCallback(() => {
|
|
46532
|
+
if (playAgainEvent) {
|
|
46533
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
46534
|
+
}
|
|
46535
|
+
}, [playAgainEvent, eventBus]);
|
|
46536
|
+
const isGameOver = result === "won" || result === "lost";
|
|
46537
|
+
const gameEndEmittedRef = React91.useRef(false);
|
|
46538
|
+
React91.useEffect(() => {
|
|
46539
|
+
if ((result === "won" || result === "lost") && gameEndEvent) {
|
|
46540
|
+
if (!gameEndEmittedRef.current) {
|
|
46541
|
+
gameEndEmittedRef.current = true;
|
|
46542
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
46543
|
+
}
|
|
46544
|
+
} else {
|
|
46545
|
+
gameEndEmittedRef.current = false;
|
|
46546
|
+
}
|
|
46547
|
+
}, [result, gameEndEvent, eventBus]);
|
|
46548
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
46549
|
+
VStack,
|
|
46550
|
+
{
|
|
46551
|
+
className: cn("platformer-board relative bg-background", className),
|
|
46552
|
+
gap: "none",
|
|
46553
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative", children: [
|
|
46554
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
46555
|
+
PlatformerCanvas,
|
|
46556
|
+
{
|
|
46557
|
+
player,
|
|
46558
|
+
platforms,
|
|
46559
|
+
worldWidth,
|
|
46560
|
+
worldHeight,
|
|
46561
|
+
canvasWidth,
|
|
46562
|
+
canvasHeight,
|
|
46563
|
+
followCamera: true,
|
|
46564
|
+
bgColor,
|
|
46565
|
+
playerSprite,
|
|
46566
|
+
tileSprites,
|
|
46567
|
+
leftEvent,
|
|
46568
|
+
rightEvent,
|
|
46569
|
+
jumpEvent,
|
|
46570
|
+
stopEvent
|
|
46571
|
+
}
|
|
46572
|
+
),
|
|
46573
|
+
isGameOver && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
46574
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
46575
|
+
Typography,
|
|
46576
|
+
{
|
|
46577
|
+
variant: "h2",
|
|
46578
|
+
className: cn(
|
|
46579
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
46580
|
+
result === "won" ? "text-warning" : "text-error"
|
|
46581
|
+
),
|
|
46582
|
+
children: result === "won" ? t("platformer.won") : t("platformer.lost")
|
|
46583
|
+
}
|
|
46584
|
+
),
|
|
46585
|
+
result === "won" && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "body1", className: "text-muted-foreground", children: [
|
|
46586
|
+
t("platformer.level"),
|
|
46587
|
+
" ",
|
|
46588
|
+
level,
|
|
46589
|
+
" \u2014 ",
|
|
46590
|
+
t("platformer.score"),
|
|
46591
|
+
": ",
|
|
46592
|
+
score
|
|
46593
|
+
] }),
|
|
46594
|
+
result === "lost" && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: t("platformer.noLives") }),
|
|
46595
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
46596
|
+
Button,
|
|
46597
|
+
{
|
|
46598
|
+
variant: "primary",
|
|
46599
|
+
className: "px-8 py-3 font-semibold",
|
|
46600
|
+
onClick: handleRestart,
|
|
46601
|
+
children: result === "won" ? t("platformer.nextLevel") : t("platformer.playAgain")
|
|
46602
|
+
}
|
|
46603
|
+
)
|
|
46604
|
+
] }) })
|
|
46605
|
+
] })
|
|
46606
|
+
}
|
|
46607
|
+
);
|
|
46608
|
+
}
|
|
46609
|
+
var init_PlatformerBoard = __esm({
|
|
46610
|
+
"components/game/organisms/PlatformerBoard.tsx"() {
|
|
46611
|
+
"use client";
|
|
46612
|
+
init_cn();
|
|
46613
|
+
init_useEventBus();
|
|
46614
|
+
init_Box();
|
|
46615
|
+
init_Button();
|
|
46616
|
+
init_Typography();
|
|
46617
|
+
init_Stack();
|
|
46618
|
+
init_PlatformerCanvas();
|
|
46619
|
+
PlatformerBoard.displayName = "PlatformerBoard";
|
|
46620
|
+
}
|
|
46621
|
+
});
|
|
46622
|
+
function PlatformerTemplate({
|
|
46623
|
+
entity,
|
|
46624
|
+
platforms = DEFAULT_PLATFORMS,
|
|
46625
|
+
worldWidth = 800,
|
|
46626
|
+
worldHeight = 400,
|
|
46627
|
+
canvasWidth = 800,
|
|
46628
|
+
canvasHeight = 400,
|
|
46629
|
+
playerSprite = `${CDN3}/characters/platformChar_idle.png`,
|
|
46630
|
+
tileSprites = DEFAULT_TILE_SPRITES,
|
|
46631
|
+
bgColor = "#5c94fc",
|
|
46632
|
+
gameEndEvent = "GAME_END",
|
|
46633
|
+
className
|
|
46634
|
+
}) {
|
|
46635
|
+
const row = entity && !Array.isArray(entity) ? entity : void 0;
|
|
46636
|
+
const lives = typeof row?.["lives"] === "number" ? row["lives"] : 3;
|
|
46637
|
+
const score = typeof row?.["score"] === "number" ? row["score"] : 0;
|
|
46638
|
+
const level = typeof row?.["level"] === "number" ? row["level"] : 1;
|
|
46639
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("platformer-template w-full", className), gap: "none", children: [
|
|
46640
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
46641
|
+
GameHud,
|
|
46642
|
+
{
|
|
46643
|
+
position: "top",
|
|
46644
|
+
stats: [
|
|
46645
|
+
{ label: "Lives", value: lives, format: "number", variant: "danger" },
|
|
46646
|
+
{ label: "Level", value: level, format: "number", variant: "default" }
|
|
46647
|
+
]
|
|
46648
|
+
}
|
|
46649
|
+
) }),
|
|
46650
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
46651
|
+
PlatformerBoard,
|
|
46652
|
+
{
|
|
46653
|
+
entity,
|
|
46654
|
+
platforms,
|
|
46655
|
+
worldWidth,
|
|
46656
|
+
worldHeight,
|
|
46657
|
+
canvasWidth,
|
|
46658
|
+
canvasHeight,
|
|
46659
|
+
playerSprite,
|
|
46660
|
+
tileSprites,
|
|
46661
|
+
bgColor,
|
|
46662
|
+
leftEvent: "LEFT",
|
|
46663
|
+
rightEvent: "RIGHT",
|
|
46664
|
+
jumpEvent: "JUMP",
|
|
46665
|
+
stopEvent: "STOP",
|
|
46666
|
+
playAgainEvent: "PLAY_AGAIN",
|
|
46667
|
+
gameEndEvent
|
|
46668
|
+
}
|
|
46669
|
+
),
|
|
46670
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-4 py-2", children: /* @__PURE__ */ jsxRuntime.jsx(ScoreBoard, { score, level }) })
|
|
46671
|
+
] });
|
|
46672
|
+
}
|
|
46673
|
+
var CDN3, DEFAULT_TILE_SPRITES, DEFAULT_PLATFORMS;
|
|
46674
|
+
var init_PlatformerTemplate = __esm({
|
|
46675
|
+
"components/game/templates/PlatformerTemplate.tsx"() {
|
|
46676
|
+
init_cn();
|
|
46677
|
+
init_Box();
|
|
46678
|
+
init_Stack();
|
|
46679
|
+
init_PlatformerBoard();
|
|
46680
|
+
init_GameHud();
|
|
46681
|
+
init_ScoreBoard();
|
|
46682
|
+
CDN3 = "https://almadar-kflow-assets.web.app/shared/platformer";
|
|
46683
|
+
DEFAULT_TILE_SPRITES = {
|
|
46684
|
+
ground: `${CDN3}/tiles/platformPack_tile001.png`,
|
|
46685
|
+
platform: `${CDN3}/tiles/platformPack_tile004.png`,
|
|
46686
|
+
hazard: `${CDN3}/tiles/platformPack_tile017.png`,
|
|
46687
|
+
goal: `${CDN3}/tiles/platformPack_tile007.png`
|
|
46688
|
+
};
|
|
46689
|
+
DEFAULT_PLATFORMS = [
|
|
46690
|
+
{ x: 0, y: 368, width: 800, height: 32, type: "ground" },
|
|
46691
|
+
{ x: 150, y: 280, width: 160, height: 16, type: "platform" },
|
|
46692
|
+
{ x: 420, y: 220, width: 160, height: 16, type: "platform" },
|
|
46693
|
+
{ x: 580, y: 300, width: 80, height: 16, type: "hazard" },
|
|
46694
|
+
{ x: 700, y: 340, width: 64, height: 28, type: "goal" }
|
|
46695
|
+
];
|
|
46696
|
+
PlatformerTemplate.displayName = "PlatformerTemplate";
|
|
46697
|
+
}
|
|
46698
|
+
});
|
|
44813
46699
|
var PricingOrganism;
|
|
44814
46700
|
var init_PricingOrganism = __esm({
|
|
44815
46701
|
"components/marketing/organisms/PricingOrganism.tsx"() {
|
|
@@ -44932,6 +46818,356 @@ var init_PricingPageTemplate = __esm({
|
|
|
44932
46818
|
PricingPageTemplate.displayName = "PricingPageTemplate";
|
|
44933
46819
|
}
|
|
44934
46820
|
});
|
|
46821
|
+
function RoguelikeBoard({
|
|
46822
|
+
entity,
|
|
46823
|
+
tiles: propTiles,
|
|
46824
|
+
enemies: propEnemies,
|
|
46825
|
+
items: propItems,
|
|
46826
|
+
player: propPlayer,
|
|
46827
|
+
playerHp: propPlayerHp,
|
|
46828
|
+
playerMaxHp: propPlayerMaxHp,
|
|
46829
|
+
playerAttack: propPlayerAttack,
|
|
46830
|
+
depth: propDepth,
|
|
46831
|
+
result: propResult,
|
|
46832
|
+
phase: propPhase,
|
|
46833
|
+
stairsX: propStairsX,
|
|
46834
|
+
stairsY: propStairsY,
|
|
46835
|
+
assetManifest: propAssetManifest,
|
|
46836
|
+
scale = 0.45,
|
|
46837
|
+
unitScale = 1,
|
|
46838
|
+
moveEvent,
|
|
46839
|
+
playAgainEvent,
|
|
46840
|
+
gameEndEvent,
|
|
46841
|
+
className
|
|
46842
|
+
}) {
|
|
46843
|
+
const board = boardEntity(entity) ?? {};
|
|
46844
|
+
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : DEFAULT_TILES2);
|
|
46845
|
+
const enemies = propEnemies ?? (Array.isArray(board.enemies) ? board.enemies : DEFAULT_ENEMIES);
|
|
46846
|
+
const items = propItems ?? (Array.isArray(board.items) ? board.items : DEFAULT_ITEMS);
|
|
46847
|
+
const player = propPlayer ?? board.player ?? { x: 1, y: 1 };
|
|
46848
|
+
const playerHp = propPlayerHp ?? num(board.playerHp, 10);
|
|
46849
|
+
const playerMaxHp = propPlayerMaxHp ?? num(board.playerMaxHp, 10);
|
|
46850
|
+
const playerAttack = propPlayerAttack ?? num(board.playerAttack, 3);
|
|
46851
|
+
const depth = propDepth ?? num(board.depth, 1);
|
|
46852
|
+
const result = propResult ?? (str(board.result) || "none");
|
|
46853
|
+
const phase = propPhase ?? (str(board.phase) || "player_turn");
|
|
46854
|
+
propStairsX ?? num(board.stairsX, 3);
|
|
46855
|
+
propStairsY ?? num(board.stairsY, 3);
|
|
46856
|
+
const eventBus = useEventBus();
|
|
46857
|
+
const { t } = hooks.useTranslate();
|
|
46858
|
+
const [hoveredTile, setHoveredTile] = React91.useState(null);
|
|
46859
|
+
const emitMove = React91.useCallback((dx, dy) => {
|
|
46860
|
+
if (!moveEvent) return;
|
|
46861
|
+
eventBus.emit(`UI:${moveEvent}`, { dx, dy });
|
|
46862
|
+
}, [moveEvent, eventBus]);
|
|
46863
|
+
React91.useEffect(() => {
|
|
46864
|
+
const handler = (e) => {
|
|
46865
|
+
if (result !== "none" || phase !== "player_turn") return;
|
|
46866
|
+
if (e.key === "ArrowUp" || e.key === "w") emitMove(0, -1);
|
|
46867
|
+
if (e.key === "ArrowDown" || e.key === "s") emitMove(0, 1);
|
|
46868
|
+
if (e.key === "ArrowLeft" || e.key === "a") emitMove(-1, 0);
|
|
46869
|
+
if (e.key === "ArrowRight" || e.key === "d") emitMove(1, 0);
|
|
46870
|
+
};
|
|
46871
|
+
window.addEventListener("keydown", handler);
|
|
46872
|
+
return () => window.removeEventListener("keydown", handler);
|
|
46873
|
+
}, [result, phase, emitMove]);
|
|
46874
|
+
React91.useEffect(() => {
|
|
46875
|
+
if ((result === "won" || result === "lost") && gameEndEvent) {
|
|
46876
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
46877
|
+
}
|
|
46878
|
+
}, [result, gameEndEvent, eventBus]);
|
|
46879
|
+
const handleTileClick = React91.useCallback((x, y) => {
|
|
46880
|
+
if (result !== "none" || phase !== "player_turn") return;
|
|
46881
|
+
const dx = x - player.x;
|
|
46882
|
+
const dy = y - player.y;
|
|
46883
|
+
if (Math.abs(dx) + Math.abs(dy) === 1) {
|
|
46884
|
+
emitMove(dx, dy);
|
|
46885
|
+
}
|
|
46886
|
+
}, [result, phase, player, emitMove]);
|
|
46887
|
+
const handleReset = React91.useCallback(() => {
|
|
46888
|
+
if (!playAgainEvent) return;
|
|
46889
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
46890
|
+
}, [playAgainEvent, eventBus]);
|
|
46891
|
+
const isoUnits = React91.useMemo(() => {
|
|
46892
|
+
const liveEnemies = enemies.filter((e) => e.hp > 0);
|
|
46893
|
+
return [
|
|
46894
|
+
{
|
|
46895
|
+
id: "player",
|
|
46896
|
+
position: player,
|
|
46897
|
+
name: t("roguelike.player") || "Hero",
|
|
46898
|
+
team: "player",
|
|
46899
|
+
health: playerHp,
|
|
46900
|
+
maxHealth: playerMaxHp,
|
|
46901
|
+
unitType: "player",
|
|
46902
|
+
sprite: `${CDN4}/isometric-dungeon/Characters/Male/Male_0_Idle0.png`
|
|
46903
|
+
},
|
|
46904
|
+
...liveEnemies.map((e) => ({
|
|
46905
|
+
id: e.id,
|
|
46906
|
+
position: { x: e.x, y: e.y },
|
|
46907
|
+
name: t("roguelike.enemy") || "Enemy",
|
|
46908
|
+
team: "enemy",
|
|
46909
|
+
health: e.hp,
|
|
46910
|
+
maxHealth: 6,
|
|
46911
|
+
unitType: "enemy",
|
|
46912
|
+
sprite: `${CDN4}/sprite-sheets/shadow-legion-sprite-sheet-sw.png`
|
|
46913
|
+
}))
|
|
46914
|
+
];
|
|
46915
|
+
}, [player, enemies, playerHp, playerMaxHp, t]);
|
|
46916
|
+
const isoFeatures = React91.useMemo(
|
|
46917
|
+
() => items.map((it) => ({
|
|
46918
|
+
id: it.id,
|
|
46919
|
+
x: it.x,
|
|
46920
|
+
y: it.y,
|
|
46921
|
+
type: it.kind,
|
|
46922
|
+
sprite: FEATURE_SPRITE[it.kind] ?? FEATURE_SPRITE.health_potion
|
|
46923
|
+
})),
|
|
46924
|
+
[items]
|
|
46925
|
+
);
|
|
46926
|
+
const validMoves = React91.useMemo(() => {
|
|
46927
|
+
if (result !== "none" || phase !== "player_turn") return [];
|
|
46928
|
+
const dirs = [{ dx: 0, dy: -1 }, { dx: 0, dy: 1 }, { dx: -1, dy: 0 }, { dx: 1, dy: 0 }];
|
|
46929
|
+
return dirs.map(({ dx, dy }) => ({ x: player.x + dx, y: player.y + dy })).filter(({ x, y }) => {
|
|
46930
|
+
const tile = tiles.find((t2) => t2.x === x && t2.y === y);
|
|
46931
|
+
return tile?.passable ?? false;
|
|
46932
|
+
});
|
|
46933
|
+
}, [result, phase, player, tiles]);
|
|
46934
|
+
const isGameOver = result === "won" || result === "lost";
|
|
46935
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("roguelike-board relative min-h-[600px] bg-background", className), gap: "none", children: [
|
|
46936
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center justify-between px-4 py-2 border-b border-border", gap: "md", children: [
|
|
46937
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
46938
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.depth") || "Depth" }),
|
|
46939
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "font-bold tabular-nums", children: depth })
|
|
46940
|
+
] }),
|
|
46941
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
46942
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.hp") || "HP" }),
|
|
46943
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
46944
|
+
Typography,
|
|
46945
|
+
{
|
|
46946
|
+
variant: "body1",
|
|
46947
|
+
className: cn(
|
|
46948
|
+
"font-bold tabular-nums",
|
|
46949
|
+
playerHp <= Math.floor(playerMaxHp * 0.25) ? "text-error" : "text-success"
|
|
46950
|
+
),
|
|
46951
|
+
children: [
|
|
46952
|
+
playerHp,
|
|
46953
|
+
" / ",
|
|
46954
|
+
playerMaxHp
|
|
46955
|
+
]
|
|
46956
|
+
}
|
|
46957
|
+
)
|
|
46958
|
+
] }),
|
|
46959
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
46960
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.atk") || "ATK" }),
|
|
46961
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "font-bold tabular-nums text-foreground", children: playerAttack })
|
|
46962
|
+
] }),
|
|
46963
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
46964
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.phase") || "Phase" }),
|
|
46965
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "capitalize text-foreground", children: phase.replace("_", " ") })
|
|
46966
|
+
] })
|
|
46967
|
+
] }),
|
|
46968
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "relative flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
46969
|
+
IsometricCanvas_default,
|
|
46970
|
+
{
|
|
46971
|
+
tiles,
|
|
46972
|
+
units: isoUnits,
|
|
46973
|
+
features: isoFeatures,
|
|
46974
|
+
selectedUnitId: null,
|
|
46975
|
+
validMoves,
|
|
46976
|
+
attackTargets: [],
|
|
46977
|
+
hoveredTile,
|
|
46978
|
+
onTileClick: handleTileClick,
|
|
46979
|
+
onUnitClick: () => void 0,
|
|
46980
|
+
onTileHover: (x, y) => setHoveredTile({ x, y }),
|
|
46981
|
+
onTileLeave: () => setHoveredTile(null),
|
|
46982
|
+
scale,
|
|
46983
|
+
assetBaseUrl: propAssetManifest?.baseUrl ?? CDN4,
|
|
46984
|
+
assetManifest: propAssetManifest,
|
|
46985
|
+
unitScale
|
|
46986
|
+
}
|
|
46987
|
+
) }),
|
|
46988
|
+
!isGameOver && /* @__PURE__ */ jsxRuntime.jsx(HStack, { className: "justify-center gap-2 p-3", gap: "none", children: [
|
|
46989
|
+
{ label: "\u2191", dx: 0, dy: -1 },
|
|
46990
|
+
{ label: "\u2193", dx: 0, dy: 1 },
|
|
46991
|
+
{ label: "\u2190", dx: -1, dy: 0 },
|
|
46992
|
+
{ label: "\u2192", dx: 1, dy: 0 }
|
|
46993
|
+
].map(({ label, dx, dy }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
46994
|
+
Button,
|
|
46995
|
+
{
|
|
46996
|
+
variant: "secondary",
|
|
46997
|
+
className: "w-9 h-9 p-0 text-base font-mono",
|
|
46998
|
+
onClick: () => emitMove(dx, dy),
|
|
46999
|
+
disabled: phase !== "player_turn",
|
|
47000
|
+
children: label
|
|
47001
|
+
},
|
|
47002
|
+
label
|
|
47003
|
+
)) }),
|
|
47004
|
+
isGameOver && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/75 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
47005
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
47006
|
+
Typography,
|
|
47007
|
+
{
|
|
47008
|
+
variant: "h2",
|
|
47009
|
+
className: cn(
|
|
47010
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
47011
|
+
result === "won" ? "text-warning" : "text-error"
|
|
47012
|
+
),
|
|
47013
|
+
children: result === "won" ? t("roguelike.victory") || "Victory!" : t("roguelike.defeat") || "Defeated!"
|
|
47014
|
+
}
|
|
47015
|
+
),
|
|
47016
|
+
result === "won" && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: t("roguelike.cleared") || `Dungeon cleared! Depth reached: ${depth}` }),
|
|
47017
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
47018
|
+
Button,
|
|
47019
|
+
{
|
|
47020
|
+
variant: "primary",
|
|
47021
|
+
className: "px-8 py-3 font-semibold",
|
|
47022
|
+
onClick: handleReset,
|
|
47023
|
+
children: t("roguelike.playAgain") || "Play Again"
|
|
47024
|
+
}
|
|
47025
|
+
)
|
|
47026
|
+
] }) })
|
|
47027
|
+
] });
|
|
47028
|
+
}
|
|
47029
|
+
var CDN4, DEFAULT_TILES2, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
|
|
47030
|
+
var init_RoguelikeBoard = __esm({
|
|
47031
|
+
"components/game/organisms/RoguelikeBoard.tsx"() {
|
|
47032
|
+
"use client";
|
|
47033
|
+
init_cn();
|
|
47034
|
+
init_useEventBus();
|
|
47035
|
+
init_Box();
|
|
47036
|
+
init_Button();
|
|
47037
|
+
init_Typography();
|
|
47038
|
+
init_Stack();
|
|
47039
|
+
init_IsometricCanvas();
|
|
47040
|
+
init_boardEntity();
|
|
47041
|
+
CDN4 = "https://almadar-kflow-assets.web.app/shared";
|
|
47042
|
+
DEFAULT_TILES2 = [
|
|
47043
|
+
{ x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47044
|
+
{ x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47045
|
+
{ x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47046
|
+
{ x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47047
|
+
{ x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47048
|
+
{ x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47049
|
+
{ x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47050
|
+
{ x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47051
|
+
{ x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47052
|
+
{ x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47053
|
+
{ x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47054
|
+
{ x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
47055
|
+
{ x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
47056
|
+
{ x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47057
|
+
{ x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47058
|
+
{ x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47059
|
+
{ x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47060
|
+
{ x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
47061
|
+
{ x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` },
|
|
47062
|
+
{ x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47063
|
+
{ x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47064
|
+
{ x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47065
|
+
{ x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47066
|
+
{ x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47067
|
+
{ x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` }
|
|
47068
|
+
];
|
|
47069
|
+
DEFAULT_ENEMIES = [
|
|
47070
|
+
{ id: "e1", x: 3, y: 1, hp: 5, attack: 2 },
|
|
47071
|
+
{ id: "e2", x: 1, y: 3, hp: 4, attack: 1 }
|
|
47072
|
+
];
|
|
47073
|
+
DEFAULT_ITEMS = [
|
|
47074
|
+
{ id: "i1", x: 2, y: 2, kind: "health_potion" }
|
|
47075
|
+
];
|
|
47076
|
+
FEATURE_SPRITE = {
|
|
47077
|
+
health_potion: `${CDN4}/isometric-dungeon/Isometric/chestClosed_E.png`,
|
|
47078
|
+
sword: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`,
|
|
47079
|
+
shield: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`
|
|
47080
|
+
};
|
|
47081
|
+
RoguelikeBoard.displayName = "RoguelikeBoard";
|
|
47082
|
+
}
|
|
47083
|
+
});
|
|
47084
|
+
function RoguelikeTemplate({
|
|
47085
|
+
entity,
|
|
47086
|
+
scale = 0.45,
|
|
47087
|
+
unitScale = 1,
|
|
47088
|
+
tiles = DEFAULT_ROGUELIKE_TILES,
|
|
47089
|
+
enemies,
|
|
47090
|
+
items,
|
|
47091
|
+
stairsX,
|
|
47092
|
+
stairsY,
|
|
47093
|
+
assetManifest = DEFAULT_ROGUELIKE_MANIFEST,
|
|
47094
|
+
gameEndEvent,
|
|
47095
|
+
className
|
|
47096
|
+
}) {
|
|
47097
|
+
const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
|
|
47098
|
+
if (!resolved && !tiles && !assetManifest) return null;
|
|
47099
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
47100
|
+
RoguelikeBoard,
|
|
47101
|
+
{
|
|
47102
|
+
entity: resolved,
|
|
47103
|
+
tiles,
|
|
47104
|
+
enemies,
|
|
47105
|
+
items,
|
|
47106
|
+
stairsX,
|
|
47107
|
+
stairsY,
|
|
47108
|
+
assetManifest,
|
|
47109
|
+
scale,
|
|
47110
|
+
unitScale,
|
|
47111
|
+
moveEvent: "MOVE",
|
|
47112
|
+
playAgainEvent: "PLAY_AGAIN",
|
|
47113
|
+
gameEndEvent: gameEndEvent ?? "GAME_END",
|
|
47114
|
+
className
|
|
47115
|
+
}
|
|
47116
|
+
);
|
|
47117
|
+
}
|
|
47118
|
+
var CDN5, DEFAULT_ROGUELIKE_TILES, DEFAULT_ROGUELIKE_MANIFEST;
|
|
47119
|
+
var init_RoguelikeTemplate = __esm({
|
|
47120
|
+
"components/game/templates/RoguelikeTemplate.tsx"() {
|
|
47121
|
+
init_RoguelikeBoard();
|
|
47122
|
+
CDN5 = "https://almadar-kflow-assets.web.app/shared";
|
|
47123
|
+
DEFAULT_ROGUELIKE_TILES = [
|
|
47124
|
+
{ x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47125
|
+
{ x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47126
|
+
{ x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47127
|
+
{ x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47128
|
+
{ x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47129
|
+
{ x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47130
|
+
{ x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47131
|
+
{ x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47132
|
+
{ x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47133
|
+
{ x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47134
|
+
{ x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47135
|
+
{ x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
47136
|
+
{ x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
47137
|
+
{ x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47138
|
+
{ x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47139
|
+
{ x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47140
|
+
{ x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47141
|
+
{ x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
47142
|
+
{ x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stairs_E.png` },
|
|
47143
|
+
{ x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47144
|
+
{ x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47145
|
+
{ x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47146
|
+
{ x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47147
|
+
{ x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47148
|
+
{ x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` }
|
|
47149
|
+
];
|
|
47150
|
+
DEFAULT_ROGUELIKE_MANIFEST = {
|
|
47151
|
+
baseUrl: CDN5,
|
|
47152
|
+
terrains: {
|
|
47153
|
+
floor: "/isometric-dungeon/Isometric/dirt_E.png",
|
|
47154
|
+
wall: "/isometric-dungeon/Isometric/stoneInset_E.png",
|
|
47155
|
+
stairs: "/isometric-dungeon/Isometric/stairs_E.png",
|
|
47156
|
+
planks: "/isometric-dungeon/Isometric/planks_E.png"
|
|
47157
|
+
},
|
|
47158
|
+
units: {
|
|
47159
|
+
player: "/isometric-dungeon/Characters/Male/Male_0_Idle0.png",
|
|
47160
|
+
enemy: "/sprite-sheets/shadow-legion-sprite-sheet-sw.png"
|
|
47161
|
+
},
|
|
47162
|
+
features: {
|
|
47163
|
+
health_potion: "/isometric-dungeon/Isometric/chestClosed_E.png",
|
|
47164
|
+
sword: "/isometric-dungeon/Isometric/barrel_E.png",
|
|
47165
|
+
shield: "/isometric-dungeon/Isometric/barrel_E.png"
|
|
47166
|
+
}
|
|
47167
|
+
};
|
|
47168
|
+
RoguelikeTemplate.displayName = "RoguelikeTemplate";
|
|
47169
|
+
}
|
|
47170
|
+
});
|
|
44935
47171
|
|
|
44936
47172
|
// lib/traitRegistry.ts
|
|
44937
47173
|
function notifyListeners2() {
|
|
@@ -46185,7 +48421,7 @@ function getAllEvents(traits2) {
|
|
|
46185
48421
|
function EventDispatcherTab({ traits: traits2, schema }) {
|
|
46186
48422
|
const eventBus = useEventBus();
|
|
46187
48423
|
const { t } = hooks.useTranslate();
|
|
46188
|
-
const [
|
|
48424
|
+
const [log15, setLog] = React91__namespace.useState([]);
|
|
46189
48425
|
const prevStatesRef = React91__namespace.useRef(/* @__PURE__ */ new Map());
|
|
46190
48426
|
React91__namespace.useEffect(() => {
|
|
46191
48427
|
for (const trait of traits2) {
|
|
@@ -46249,9 +48485,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
|
|
|
46249
48485
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.otherEvents") }),
|
|
46250
48486
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1", children: unavailableEvents.map((event) => /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", size: "sm", className: "opacity-50", children: event }, event)) })
|
|
46251
48487
|
] }),
|
|
46252
|
-
|
|
48488
|
+
log15.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
46253
48489
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.recentTransitions") }),
|
|
46254
|
-
/* @__PURE__ */ jsxRuntime.jsx(Stack, { gap: "xs", children:
|
|
48490
|
+
/* @__PURE__ */ jsxRuntime.jsx(Stack, { gap: "xs", children: log15.map((entry, i) => /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
|
|
46255
48491
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-purple-400", children: entry.traitName }),
|
|
46256
48492
|
" ",
|
|
46257
48493
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-500", children: entry.from }),
|
|
@@ -48172,9 +50408,9 @@ function VariablePanel({
|
|
|
48172
50408
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
|
|
48173
50409
|
variables.map((v) => {
|
|
48174
50410
|
const name = v.name == null ? "" : String(v.name);
|
|
48175
|
-
const value =
|
|
48176
|
-
const max =
|
|
48177
|
-
const min =
|
|
50411
|
+
const value = numField2(v.value);
|
|
50412
|
+
const max = numField2(v.max, 100);
|
|
50413
|
+
const min = numField2(v.min, 0);
|
|
48178
50414
|
const unit = v.unit == null ? "" : String(v.unit);
|
|
48179
50415
|
const pct = Math.round((value - min) / (max - min) * 100);
|
|
48180
50416
|
const isHigh = pct > 80;
|
|
@@ -48204,12 +50440,12 @@ function VariablePanel({
|
|
|
48204
50440
|
})
|
|
48205
50441
|
] });
|
|
48206
50442
|
}
|
|
48207
|
-
var
|
|
50443
|
+
var numField2;
|
|
48208
50444
|
var init_VariablePanel = __esm({
|
|
48209
50445
|
"components/game/organisms/puzzles/state-architect/VariablePanel.tsx"() {
|
|
48210
50446
|
init_atoms2();
|
|
48211
50447
|
init_cn();
|
|
48212
|
-
|
|
50448
|
+
numField2 = (v, fallback = 0) => {
|
|
48213
50449
|
const n = Number(v);
|
|
48214
50450
|
return Number.isFinite(n) ? n : fallback;
|
|
48215
50451
|
};
|
|
@@ -49062,6 +51298,289 @@ var init_ToastSlot = __esm({
|
|
|
49062
51298
|
ToastSlot.displayName = "ToastSlot";
|
|
49063
51299
|
}
|
|
49064
51300
|
});
|
|
51301
|
+
function tilesToIso(tiles) {
|
|
51302
|
+
return tiles.map((t) => ({
|
|
51303
|
+
x: t.x,
|
|
51304
|
+
y: t.y,
|
|
51305
|
+
terrain: t.terrain,
|
|
51306
|
+
terrainSprite: t.terrainSprite ?? TERRAIN_SPRITES[t.terrain ?? "ground"],
|
|
51307
|
+
passable: t.passable
|
|
51308
|
+
}));
|
|
51309
|
+
}
|
|
51310
|
+
function towersToUnits(towers) {
|
|
51311
|
+
return towers.map((t) => ({
|
|
51312
|
+
id: t.id,
|
|
51313
|
+
position: { x: t.x, y: t.y },
|
|
51314
|
+
name: "Tower",
|
|
51315
|
+
team: "player",
|
|
51316
|
+
sprite: TOWER_SPRITE,
|
|
51317
|
+
unitType: "guardian",
|
|
51318
|
+
health: 1,
|
|
51319
|
+
maxHealth: 1
|
|
51320
|
+
}));
|
|
51321
|
+
}
|
|
51322
|
+
function creepsToUnits(creeps) {
|
|
51323
|
+
return creeps.map((c) => ({
|
|
51324
|
+
id: c.id,
|
|
51325
|
+
position: { x: c.x, y: c.y },
|
|
51326
|
+
name: "Creep",
|
|
51327
|
+
team: "enemy",
|
|
51328
|
+
sprite: CREEP_SPRITE,
|
|
51329
|
+
unitType: "scrapper",
|
|
51330
|
+
health: c.hp,
|
|
51331
|
+
maxHealth: c.maxHp
|
|
51332
|
+
}));
|
|
51333
|
+
}
|
|
51334
|
+
function pathToFeatures(path) {
|
|
51335
|
+
return path.map((p2, i) => ({
|
|
51336
|
+
id: `path-${i}`,
|
|
51337
|
+
x: p2.x,
|
|
51338
|
+
y: p2.y,
|
|
51339
|
+
type: "path-marker",
|
|
51340
|
+
sprite: `${CDN6}world-map/road_straight.png`
|
|
51341
|
+
}));
|
|
51342
|
+
}
|
|
51343
|
+
function TowerDefenseBoard({
|
|
51344
|
+
entity,
|
|
51345
|
+
tiles: propTiles,
|
|
51346
|
+
path: propPath,
|
|
51347
|
+
towers: propTowers,
|
|
51348
|
+
creeps: propCreeps,
|
|
51349
|
+
gold: propGold,
|
|
51350
|
+
lives: propLives,
|
|
51351
|
+
wave: propWave,
|
|
51352
|
+
maxWaves: propMaxWaves,
|
|
51353
|
+
result: propResult,
|
|
51354
|
+
waveActive: propWaveActive,
|
|
51355
|
+
towerCost = 25,
|
|
51356
|
+
scale = 0.45,
|
|
51357
|
+
placeTowerEvent,
|
|
51358
|
+
startWaveEvent,
|
|
51359
|
+
playAgainEvent,
|
|
51360
|
+
gameEndEvent,
|
|
51361
|
+
className
|
|
51362
|
+
}) {
|
|
51363
|
+
const board = boardEntity(entity) ?? {};
|
|
51364
|
+
const eventBus = useEventBus();
|
|
51365
|
+
const { t } = hooks.useTranslate();
|
|
51366
|
+
const tiles = propTiles ?? rows(board.tiles);
|
|
51367
|
+
const path = propPath ?? rows(board.path);
|
|
51368
|
+
const towers = propTowers ?? rows(board.towers);
|
|
51369
|
+
const creeps = propCreeps ?? rows(board.creeps);
|
|
51370
|
+
const gold = propGold ?? num(board.gold, 100);
|
|
51371
|
+
const lives = propLives ?? num(board.lives, 20);
|
|
51372
|
+
const wave = propWave ?? num(board.wave, 1);
|
|
51373
|
+
const maxWaves = propMaxWaves ?? num(board.maxWaves, 5);
|
|
51374
|
+
const result = propResult ?? (str(board.result) || "none");
|
|
51375
|
+
const waveActive = propWaveActive ?? Boolean(board.waveActive);
|
|
51376
|
+
const [hoveredTile, setHoveredTile] = React91.useState(null);
|
|
51377
|
+
const emittedGameEnd = React91.useRef(false);
|
|
51378
|
+
if (result !== "none" && !emittedGameEnd.current) {
|
|
51379
|
+
emittedGameEnd.current = true;
|
|
51380
|
+
if (gameEndEvent) {
|
|
51381
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
51382
|
+
}
|
|
51383
|
+
}
|
|
51384
|
+
if (result === "none") {
|
|
51385
|
+
emittedGameEnd.current = false;
|
|
51386
|
+
}
|
|
51387
|
+
const towerPositions = React91.useMemo(() => new Set(towers.map((t2) => `${t2.x},${t2.y}`)), [towers]);
|
|
51388
|
+
const pathPositions = React91.useMemo(() => new Set(path.map((p2) => `${p2.x},${p2.y}`)), [path]);
|
|
51389
|
+
const validMoves = React91.useMemo(() => {
|
|
51390
|
+
if (result !== "none" || gold < towerCost) return [];
|
|
51391
|
+
return tiles.filter(
|
|
51392
|
+
(t2) => t2.passable !== false && !towerPositions.has(`${t2.x},${t2.y}`) && !pathPositions.has(`${t2.x},${t2.y}`)
|
|
51393
|
+
).map((t2) => ({ x: t2.x, y: t2.y }));
|
|
51394
|
+
}, [tiles, towerPositions, pathPositions, result, gold, towerCost]);
|
|
51395
|
+
const isoTiles = React91.useMemo(() => tilesToIso(tiles), [tiles]);
|
|
51396
|
+
const towerUnits = React91.useMemo(() => towersToUnits(towers), [towers]);
|
|
51397
|
+
const creepUnits = React91.useMemo(() => creepsToUnits(creeps), [creeps]);
|
|
51398
|
+
const isoUnits = React91.useMemo(() => [...towerUnits, ...creepUnits], [towerUnits, creepUnits]);
|
|
51399
|
+
const pathFeatures = React91.useMemo(() => pathToFeatures(path), [path]);
|
|
51400
|
+
const handleTileClick = React91.useCallback((x, y) => {
|
|
51401
|
+
if (result !== "none") return;
|
|
51402
|
+
if (pathPositions.has(`${x},${y}`)) return;
|
|
51403
|
+
if (towerPositions.has(`${x},${y}`)) return;
|
|
51404
|
+
if (gold < towerCost) return;
|
|
51405
|
+
if (placeTowerEvent) {
|
|
51406
|
+
eventBus.emit(`UI:${placeTowerEvent}`, { x, y });
|
|
51407
|
+
}
|
|
51408
|
+
}, [result, pathPositions, towerPositions, gold, towerCost, placeTowerEvent, eventBus]);
|
|
51409
|
+
const handleStartWave = React91.useCallback(() => {
|
|
51410
|
+
if (startWaveEvent) {
|
|
51411
|
+
eventBus.emit(`UI:${startWaveEvent}`, { wave });
|
|
51412
|
+
}
|
|
51413
|
+
}, [startWaveEvent, wave, eventBus]);
|
|
51414
|
+
const handlePlayAgain = React91.useCallback(() => {
|
|
51415
|
+
if (playAgainEvent) {
|
|
51416
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
51417
|
+
}
|
|
51418
|
+
}, [playAgainEvent, eventBus]);
|
|
51419
|
+
const canPlaceTower = gold >= towerCost && result === "none";
|
|
51420
|
+
const canStartWave = !waveActive && result === "none";
|
|
51421
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("tower-defense-board relative min-h-[600px] bg-background", className), gap: "none", children: [
|
|
51422
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
51423
|
+
HStack,
|
|
51424
|
+
{
|
|
51425
|
+
className: "px-4 py-2 border-b border-border bg-surface",
|
|
51426
|
+
gap: "lg",
|
|
51427
|
+
align: "center",
|
|
51428
|
+
children: [
|
|
51429
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
51430
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "muted", children: t("td.gold") ?? "Gold" }),
|
|
51431
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", weight: "bold", className: "text-warning", children: gold })
|
|
51432
|
+
] }),
|
|
51433
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
51434
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "muted", children: t("td.lives") ?? "Lives" }),
|
|
51435
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", weight: "bold", className: lives <= 5 ? "text-error" : "text-success", children: lives })
|
|
51436
|
+
] }),
|
|
51437
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
51438
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "muted", children: t("td.wave") ?? "Wave" }),
|
|
51439
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "body2", weight: "bold", children: [
|
|
51440
|
+
wave,
|
|
51441
|
+
" / ",
|
|
51442
|
+
maxWaves
|
|
51443
|
+
] })
|
|
51444
|
+
] }),
|
|
51445
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "ml-auto", children: [
|
|
51446
|
+
canStartWave && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", size: "sm", onClick: handleStartWave, children: t("td.startWave") ?? "Start Wave" }),
|
|
51447
|
+
waveActive && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "text-warning animate-pulse", children: t("td.waveInProgress") ?? "Wave in progress\u2026" })
|
|
51448
|
+
] })
|
|
51449
|
+
]
|
|
51450
|
+
}
|
|
51451
|
+
),
|
|
51452
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative flex-1", children: [
|
|
51453
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51454
|
+
IsometricCanvas_default,
|
|
51455
|
+
{
|
|
51456
|
+
tiles: isoTiles,
|
|
51457
|
+
units: isoUnits,
|
|
51458
|
+
features: pathFeatures,
|
|
51459
|
+
validMoves,
|
|
51460
|
+
hoveredTile,
|
|
51461
|
+
onTileClick: handleTileClick,
|
|
51462
|
+
onTileHover: (x, y) => setHoveredTile({ x, y }),
|
|
51463
|
+
onTileLeave: () => setHoveredTile(null),
|
|
51464
|
+
scale
|
|
51465
|
+
}
|
|
51466
|
+
),
|
|
51467
|
+
hoveredTile && canPlaceTower && validMoves.some((m) => m.x === hoveredTile.x && m.y === hoveredTile.y) && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "absolute bottom-4 left-1/2 -translate-x-1/2 z-10 bg-background/80 rounded px-3 py-1 backdrop-blur-sm pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", children: t("td.placeTower") ?? `Place tower (${towerCost} gold)` }) })
|
|
51468
|
+
] }),
|
|
51469
|
+
result !== "none" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
51470
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51471
|
+
Typography,
|
|
51472
|
+
{
|
|
51473
|
+
variant: "h2",
|
|
51474
|
+
className: cn(
|
|
51475
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
51476
|
+
result === "won" ? "text-warning" : "text-error"
|
|
51477
|
+
),
|
|
51478
|
+
children: result === "won" ? t("td.victory") ?? "Victory!" : t("td.defeat") ?? "Defeat!"
|
|
51479
|
+
}
|
|
51480
|
+
),
|
|
51481
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: result === "won" ? t("td.survivedAllWaves") ?? `Survived all ${maxWaves} waves!` : t("td.livesReachedZero") ?? "Your base was overrun." }),
|
|
51482
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51483
|
+
Button,
|
|
51484
|
+
{
|
|
51485
|
+
variant: "primary",
|
|
51486
|
+
className: "px-8 py-3 font-semibold",
|
|
51487
|
+
onClick: handlePlayAgain,
|
|
51488
|
+
children: t("td.playAgain") ?? "Play Again"
|
|
51489
|
+
}
|
|
51490
|
+
)
|
|
51491
|
+
] }) })
|
|
51492
|
+
] });
|
|
51493
|
+
}
|
|
51494
|
+
var CDN6, TERRAIN_SPRITES, TOWER_SPRITE, CREEP_SPRITE;
|
|
51495
|
+
var init_TowerDefenseBoard = __esm({
|
|
51496
|
+
"components/game/organisms/TowerDefenseBoard.tsx"() {
|
|
51497
|
+
"use client";
|
|
51498
|
+
init_cn();
|
|
51499
|
+
init_useEventBus();
|
|
51500
|
+
init_Box();
|
|
51501
|
+
init_Button();
|
|
51502
|
+
init_Typography();
|
|
51503
|
+
init_Stack();
|
|
51504
|
+
init_IsometricCanvas();
|
|
51505
|
+
init_boardEntity();
|
|
51506
|
+
CDN6 = "https://almadar-kflow-assets.web.app/shared/";
|
|
51507
|
+
TERRAIN_SPRITES = {
|
|
51508
|
+
ground: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_01.png`,
|
|
51509
|
+
path: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_09.png`,
|
|
51510
|
+
wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`
|
|
51511
|
+
};
|
|
51512
|
+
TOWER_SPRITE = `${CDN6}units/guardian.png`;
|
|
51513
|
+
CREEP_SPRITE = `${CDN6}units/scrapper.png`;
|
|
51514
|
+
TowerDefenseBoard.displayName = "TowerDefenseBoard";
|
|
51515
|
+
}
|
|
51516
|
+
});
|
|
51517
|
+
function TowerDefenseTemplate({
|
|
51518
|
+
entity,
|
|
51519
|
+
title = "Tower Defense",
|
|
51520
|
+
tiles,
|
|
51521
|
+
path,
|
|
51522
|
+
towers,
|
|
51523
|
+
gold,
|
|
51524
|
+
lives,
|
|
51525
|
+
maxWaves,
|
|
51526
|
+
towerCost,
|
|
51527
|
+
scale,
|
|
51528
|
+
placeTowerEvent,
|
|
51529
|
+
startWaveEvent,
|
|
51530
|
+
playAgainEvent,
|
|
51531
|
+
gameEndEvent,
|
|
51532
|
+
className
|
|
51533
|
+
}) {
|
|
51534
|
+
const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
|
|
51535
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
51536
|
+
Box,
|
|
51537
|
+
{
|
|
51538
|
+
display: "flex",
|
|
51539
|
+
fullHeight: true,
|
|
51540
|
+
className: cn("tower-defense-template flex-col", className),
|
|
51541
|
+
children: [
|
|
51542
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
51543
|
+
HStack,
|
|
51544
|
+
{
|
|
51545
|
+
gap: "sm",
|
|
51546
|
+
align: "center",
|
|
51547
|
+
className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
|
|
51548
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title })
|
|
51549
|
+
}
|
|
51550
|
+
),
|
|
51551
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
51552
|
+
TowerDefenseBoard,
|
|
51553
|
+
{
|
|
51554
|
+
entity: resolved,
|
|
51555
|
+
tiles,
|
|
51556
|
+
path,
|
|
51557
|
+
towers,
|
|
51558
|
+
gold,
|
|
51559
|
+
lives,
|
|
51560
|
+
maxWaves,
|
|
51561
|
+
towerCost,
|
|
51562
|
+
scale,
|
|
51563
|
+
placeTowerEvent,
|
|
51564
|
+
startWaveEvent,
|
|
51565
|
+
playAgainEvent,
|
|
51566
|
+
gameEndEvent,
|
|
51567
|
+
className: "h-full"
|
|
51568
|
+
}
|
|
51569
|
+
) })
|
|
51570
|
+
]
|
|
51571
|
+
}
|
|
51572
|
+
);
|
|
51573
|
+
}
|
|
51574
|
+
var init_TowerDefenseTemplate = __esm({
|
|
51575
|
+
"components/game/templates/TowerDefenseTemplate.tsx"() {
|
|
51576
|
+
init_cn();
|
|
51577
|
+
init_Box();
|
|
51578
|
+
init_Stack();
|
|
51579
|
+
init_Typography();
|
|
51580
|
+
init_TowerDefenseBoard();
|
|
51581
|
+
TowerDefenseTemplate.displayName = "TowerDefenseTemplate";
|
|
51582
|
+
}
|
|
51583
|
+
});
|
|
49065
51584
|
function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
|
|
49066
51585
|
const eventBus = useEventBus();
|
|
49067
51586
|
const {
|
|
@@ -49571,48 +52090,48 @@ function WorldMapTemplate({
|
|
|
49571
52090
|
}
|
|
49572
52091
|
);
|
|
49573
52092
|
}
|
|
49574
|
-
var
|
|
52093
|
+
var CDN7, DEFAULT_WORLDMAP_TILES, DEFAULT_WORLDMAP_UNITS, DEFAULT_WORLDMAP_FEATURES, DEFAULT_WORLDMAP_MANIFEST;
|
|
49575
52094
|
var init_WorldMapTemplate = __esm({
|
|
49576
52095
|
"components/game/templates/WorldMapTemplate.tsx"() {
|
|
49577
52096
|
init_WorldMapBoard();
|
|
49578
|
-
|
|
52097
|
+
CDN7 = "https://almadar-kflow-assets.web.app/shared";
|
|
49579
52098
|
DEFAULT_WORLDMAP_TILES = [
|
|
49580
|
-
{ x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49581
|
-
{ x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49582
|
-
{ x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${
|
|
49583
|
-
{ x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49584
|
-
{ x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49585
|
-
{ x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49586
|
-
{ x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
49587
|
-
{ x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
49588
|
-
{ x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
49589
|
-
{ x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${
|
|
49590
|
-
{ x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${
|
|
49591
|
-
{ x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
49592
|
-
{ x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
49593
|
-
{ x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
49594
|
-
{ x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49595
|
-
{ x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49596
|
-
{ x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
49597
|
-
{ x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
49598
|
-
{ x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
49599
|
-
{ x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49600
|
-
{ x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49601
|
-
{ x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${
|
|
49602
|
-
{ x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${
|
|
49603
|
-
{ x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49604
|
-
{ x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
52099
|
+
{ x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52100
|
+
{ x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52101
|
+
{ x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
52102
|
+
{ x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52103
|
+
{ x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52104
|
+
{ x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52105
|
+
{ x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52106
|
+
{ x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52107
|
+
{ x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52108
|
+
{ x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
52109
|
+
{ x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
52110
|
+
{ x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52111
|
+
{ x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
|
|
52112
|
+
{ x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52113
|
+
{ x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52114
|
+
{ x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52115
|
+
{ x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52116
|
+
{ x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52117
|
+
{ x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
|
|
52118
|
+
{ x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52119
|
+
{ x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52120
|
+
{ x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
52121
|
+
{ x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52122
|
+
{ x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52123
|
+
{ x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` }
|
|
49605
52124
|
];
|
|
49606
52125
|
DEFAULT_WORLDMAP_UNITS = [
|
|
49607
|
-
{ id: "h1", position: { x: 1, y: 1 }, unitType: "hero", name: "Amir", team: "player", health: 10, maxHealth: 10, sprite: `${
|
|
49608
|
-
{ id: "h2", position: { x: 3, y: 3 }, unitType: "scout", name: "Archivist", team: "player", health: 10, maxHealth: 10, sprite: `${
|
|
52126
|
+
{ id: "h1", position: { x: 1, y: 1 }, unitType: "hero", name: "Amir", team: "player", health: 10, maxHealth: 10, sprite: `${CDN7}/sprite-sheets/amir-sprite-sheet-se.png` },
|
|
52127
|
+
{ id: "h2", position: { x: 3, y: 3 }, unitType: "scout", name: "Archivist", team: "player", health: 10, maxHealth: 10, sprite: `${CDN7}/sprite-sheets/archivist-sprite-sheet-se.png` }
|
|
49609
52128
|
];
|
|
49610
52129
|
DEFAULT_WORLDMAP_FEATURES = [
|
|
49611
|
-
{ id: "f1", x: 2, y: 2, type: "capital", sprite: `${
|
|
49612
|
-
{ id: "f2", x: 4, y: 2, type: "power_node", sprite: `${
|
|
52130
|
+
{ id: "f1", x: 2, y: 2, type: "capital", sprite: `${CDN7}/scenes/world/capital.png` },
|
|
52131
|
+
{ id: "f2", x: 4, y: 2, type: "power_node", sprite: `${CDN7}/world-map/power_node.png` }
|
|
49613
52132
|
];
|
|
49614
52133
|
DEFAULT_WORLDMAP_MANIFEST = {
|
|
49615
|
-
baseUrl:
|
|
52134
|
+
baseUrl: CDN7,
|
|
49616
52135
|
terrains: {
|
|
49617
52136
|
grass: "/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png",
|
|
49618
52137
|
water: "/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png",
|
|
@@ -49657,7 +52176,7 @@ function lazyThree(name, loader) {
|
|
|
49657
52176
|
ThreeWrapper.displayName = `Lazy(${name})`;
|
|
49658
52177
|
return ThreeWrapper;
|
|
49659
52178
|
}
|
|
49660
|
-
var ThreeBoundary, FeatureRenderer,
|
|
52179
|
+
var ThreeBoundary, FeatureRenderer, GameCanvas3D2, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
|
|
49661
52180
|
var init_component_registry_generated = __esm({
|
|
49662
52181
|
"components/core/organisms/component-registry.generated.ts"() {
|
|
49663
52182
|
init_AboutPageTemplate();
|
|
@@ -49767,6 +52286,7 @@ var init_component_registry_generated = __esm({
|
|
|
49767
52286
|
init_FormSectionHeader();
|
|
49768
52287
|
init_GameAudioProvider();
|
|
49769
52288
|
init_GameAudioToggle();
|
|
52289
|
+
init_GameBoard3D();
|
|
49770
52290
|
init_GameCanvas2D();
|
|
49771
52291
|
init_GameHud();
|
|
49772
52292
|
init_GameMenu();
|
|
@@ -49824,7 +52344,9 @@ var init_component_registry_generated = __esm({
|
|
|
49824
52344
|
init_PageHeader();
|
|
49825
52345
|
init_Pagination();
|
|
49826
52346
|
init_PatternTile();
|
|
52347
|
+
init_PlatformerBoard();
|
|
49827
52348
|
init_PlatformerCanvas();
|
|
52349
|
+
init_PlatformerTemplate();
|
|
49828
52350
|
init_Popover();
|
|
49829
52351
|
init_PositionedCanvas();
|
|
49830
52352
|
init_PowerupSlots();
|
|
@@ -49848,6 +52370,8 @@ var init_component_registry_generated = __esm({
|
|
|
49848
52370
|
init_ResourceBar();
|
|
49849
52371
|
init_ResourceCounter();
|
|
49850
52372
|
init_RichBlockEditor();
|
|
52373
|
+
init_RoguelikeBoard();
|
|
52374
|
+
init_RoguelikeTemplate();
|
|
49851
52375
|
init_RuntimeDebugger2();
|
|
49852
52376
|
init_ScaledDiagram();
|
|
49853
52377
|
init_ScoreBoard();
|
|
@@ -49920,6 +52444,8 @@ var init_component_registry_generated = __esm({
|
|
|
49920
52444
|
init_Toast();
|
|
49921
52445
|
init_ToastSlot();
|
|
49922
52446
|
init_Tooltip();
|
|
52447
|
+
init_TowerDefenseBoard();
|
|
52448
|
+
init_TowerDefenseTemplate();
|
|
49923
52449
|
init_TraitFrame();
|
|
49924
52450
|
init_TraitSlot();
|
|
49925
52451
|
init_TrendIndicator();
|
|
@@ -49964,7 +52490,7 @@ var init_component_registry_generated = __esm({
|
|
|
49964
52490
|
}
|
|
49965
52491
|
};
|
|
49966
52492
|
FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
|
|
49967
|
-
|
|
52493
|
+
GameCanvas3D2 = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
|
|
49968
52494
|
GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
49969
52495
|
GameCanvas3DCastleTemplate = lazyThree("GameCanvas3DCastleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
49970
52496
|
GameCanvas3DWorldMapTemplate = lazyThree("GameCanvas3DWorldMapTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
@@ -50083,8 +52609,9 @@ var init_component_registry_generated = __esm({
|
|
|
50083
52609
|
"FormSectionHeader": FormSectionHeader,
|
|
50084
52610
|
"GameAudioProvider": GameAudioProvider,
|
|
50085
52611
|
"GameAudioToggle": GameAudioToggle,
|
|
52612
|
+
"GameBoard3D": GameBoard3D,
|
|
50086
52613
|
"GameCanvas2D": GameCanvas2D,
|
|
50087
|
-
"GameCanvas3D":
|
|
52614
|
+
"GameCanvas3D": GameCanvas3D2,
|
|
50088
52615
|
"GameCanvas3DBattleTemplate": GameCanvas3DBattleTemplate,
|
|
50089
52616
|
"GameCanvas3DCastleTemplate": GameCanvas3DCastleTemplate,
|
|
50090
52617
|
"GameCanvas3DWorldMapTemplate": GameCanvas3DWorldMapTemplate,
|
|
@@ -50146,7 +52673,9 @@ var init_component_registry_generated = __esm({
|
|
|
50146
52673
|
"PageHeader": PageHeader,
|
|
50147
52674
|
"Pagination": Pagination,
|
|
50148
52675
|
"PatternTile": PatternTile,
|
|
52676
|
+
"PlatformerBoard": PlatformerBoard,
|
|
50149
52677
|
"PlatformerCanvas": PlatformerCanvas,
|
|
52678
|
+
"PlatformerTemplate": PlatformerTemplate,
|
|
50150
52679
|
"Popover": Popover,
|
|
50151
52680
|
"PositionedCanvas": PositionedCanvas,
|
|
50152
52681
|
"PowerupSlots": PowerupSlots,
|
|
@@ -50170,6 +52699,8 @@ var init_component_registry_generated = __esm({
|
|
|
50170
52699
|
"ResourceBar": ResourceBar,
|
|
50171
52700
|
"ResourceCounter": ResourceCounter,
|
|
50172
52701
|
"RichBlockEditor": RichBlockEditor,
|
|
52702
|
+
"RoguelikeBoard": RoguelikeBoard,
|
|
52703
|
+
"RoguelikeTemplate": RoguelikeTemplate,
|
|
50173
52704
|
"RuntimeDebugger": RuntimeDebugger,
|
|
50174
52705
|
"ScaledDiagram": ScaledDiagram,
|
|
50175
52706
|
"ScoreBoard": ScoreBoard,
|
|
@@ -50245,6 +52776,8 @@ var init_component_registry_generated = __esm({
|
|
|
50245
52776
|
"Toast": Toast,
|
|
50246
52777
|
"ToastSlot": ToastSlot,
|
|
50247
52778
|
"Tooltip": Tooltip,
|
|
52779
|
+
"TowerDefenseBoard": TowerDefenseBoard,
|
|
52780
|
+
"TowerDefenseTemplate": TowerDefenseTemplate,
|
|
50248
52781
|
"TraitFrame": TraitFrame,
|
|
50249
52782
|
"TraitSlot": TraitSlot,
|
|
50250
52783
|
"TrendIndicator": TrendIndicator,
|
|
@@ -54731,7 +57264,7 @@ function convertObjectProps(props) {
|
|
|
54731
57264
|
function convertFnFormLambdasInProps(props) {
|
|
54732
57265
|
return convertObjectProps(props);
|
|
54733
57266
|
}
|
|
54734
|
-
var
|
|
57267
|
+
var log11 = logger.createLogger("almadar:ui:effects:client-handlers");
|
|
54735
57268
|
function createClientEffectHandlers(options) {
|
|
54736
57269
|
const { eventBus, slotSetter, navigate, notify, callService } = options;
|
|
54737
57270
|
return {
|
|
@@ -54740,10 +57273,10 @@ function createClientEffectHandlers(options) {
|
|
|
54740
57273
|
eventBus.emit(prefixedEvent, payload);
|
|
54741
57274
|
},
|
|
54742
57275
|
persist: async () => {
|
|
54743
|
-
|
|
57276
|
+
log11.warn("persist is server-side only, ignored on client");
|
|
54744
57277
|
},
|
|
54745
57278
|
set: () => {
|
|
54746
|
-
|
|
57279
|
+
log11.warn("set is server-side only, ignored on client");
|
|
54747
57280
|
},
|
|
54748
57281
|
callService: async (service, action, params) => {
|
|
54749
57282
|
if (callService) return callService(service, action, params);
|
|
@@ -54772,10 +57305,10 @@ function createClientEffectHandlers(options) {
|
|
|
54772
57305
|
slotSetter.addPattern(slot, pattern, props);
|
|
54773
57306
|
},
|
|
54774
57307
|
navigate: navigate ?? ((path) => {
|
|
54775
|
-
|
|
57308
|
+
log11.warn("No navigate handler, ignoring", { path });
|
|
54776
57309
|
}),
|
|
54777
57310
|
notify: notify ?? ((msg, type) => {
|
|
54778
|
-
|
|
57311
|
+
log11.debug("notify", { type, message: msg });
|
|
54779
57312
|
})
|
|
54780
57313
|
};
|
|
54781
57314
|
}
|
|
@@ -55940,7 +58473,7 @@ OrbitalThemeProvider.displayName = "OrbitalThemeProvider";
|
|
|
55940
58473
|
init_navigation();
|
|
55941
58474
|
init_verificationRegistry();
|
|
55942
58475
|
var PERF_NAMESPACE = "almadar:perf:canvas";
|
|
55943
|
-
var
|
|
58476
|
+
var log12 = logger.createLogger(PERF_NAMESPACE);
|
|
55944
58477
|
var RING_SIZE = 50;
|
|
55945
58478
|
var ring = [];
|
|
55946
58479
|
var writeIdx = 0;
|
|
@@ -55991,7 +58524,7 @@ function perfEnd(name, startToken, detail) {
|
|
|
55991
58524
|
}
|
|
55992
58525
|
}
|
|
55993
58526
|
push({ name, durationMs, ts: endTs, detail });
|
|
55994
|
-
|
|
58527
|
+
log12.debug(name, () => ({ durationMs, ...detail ?? {} }));
|
|
55995
58528
|
}
|
|
55996
58529
|
var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, commitTime) => {
|
|
55997
58530
|
if (!isEnabled()) return;
|
|
@@ -56001,7 +58534,7 @@ var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, com
|
|
|
56001
58534
|
ts: commitTime,
|
|
56002
58535
|
detail: { baseDuration }
|
|
56003
58536
|
});
|
|
56004
|
-
|
|
58537
|
+
log12.debug(`profiler:${id}:${phase}`, () => ({ actualDuration, baseDuration }));
|
|
56005
58538
|
};
|
|
56006
58539
|
|
|
56007
58540
|
// runtime/prepareSchemaForPreview.ts
|
|
@@ -56644,7 +59177,7 @@ init_useEventBus();
|
|
|
56644
59177
|
// components/avl/molecules/useCanvasDnd.tsx
|
|
56645
59178
|
init_useEventBus();
|
|
56646
59179
|
init_useAlmadarDndCollision();
|
|
56647
|
-
var
|
|
59180
|
+
var log13 = logger.createLogger("almadar:ui:canvas-dnd");
|
|
56648
59181
|
function useCanvasDraggable({
|
|
56649
59182
|
id,
|
|
56650
59183
|
payload,
|
|
@@ -56683,7 +59216,7 @@ function defaultEmit(eventBus, drop) {
|
|
|
56683
59216
|
if (payload.kind === "pattern") {
|
|
56684
59217
|
const patternType = payload.data.type;
|
|
56685
59218
|
if (typeof patternType !== "string") {
|
|
56686
|
-
|
|
59219
|
+
log13.warn("default-emit:pattern:missing-type");
|
|
56687
59220
|
return;
|
|
56688
59221
|
}
|
|
56689
59222
|
const out = { patternType, containerNode: target.containerNode };
|
|
@@ -56692,23 +59225,23 @@ function defaultEmit(eventBus, drop) {
|
|
|
56692
59225
|
out.index = resolved.index;
|
|
56693
59226
|
}
|
|
56694
59227
|
eventBus.emit("UI:PATTERN_DROP", out);
|
|
56695
|
-
|
|
59228
|
+
log13.info("default-emit:pattern", { patternType, level: target.level });
|
|
56696
59229
|
return;
|
|
56697
59230
|
}
|
|
56698
59231
|
if (payload.kind === "behavior") {
|
|
56699
59232
|
const behaviorName = payload.data.name;
|
|
56700
59233
|
if (typeof behaviorName !== "string") {
|
|
56701
|
-
|
|
59234
|
+
log13.warn("default-emit:behavior:missing-name");
|
|
56702
59235
|
return;
|
|
56703
59236
|
}
|
|
56704
59237
|
eventBus.emit("UI:BEHAVIOR_DROP", {
|
|
56705
59238
|
behaviorName,
|
|
56706
59239
|
containerNode: target.containerNode
|
|
56707
59240
|
});
|
|
56708
|
-
|
|
59241
|
+
log13.info("default-emit:behavior", { behaviorName, level: target.level });
|
|
56709
59242
|
return;
|
|
56710
59243
|
}
|
|
56711
|
-
|
|
59244
|
+
log13.debug("default-emit:unhandled-kind", { kind: payload.kind });
|
|
56712
59245
|
}
|
|
56713
59246
|
function CanvasDndProvider({
|
|
56714
59247
|
children,
|
|
@@ -56724,9 +59257,9 @@ function CanvasDndProvider({
|
|
|
56724
59257
|
if (payload) {
|
|
56725
59258
|
setActivePayload(payload);
|
|
56726
59259
|
eventBus.emit("UI:DRAG_START", { kind: payload.kind, data: payload.data });
|
|
56727
|
-
|
|
59260
|
+
log13.info("dragStart", { id: e.active.id, kind: payload.kind });
|
|
56728
59261
|
} else {
|
|
56729
|
-
|
|
59262
|
+
log13.warn("dragStart:missing-payload", { id: e.active.id });
|
|
56730
59263
|
}
|
|
56731
59264
|
}, [eventBus]);
|
|
56732
59265
|
const handleDragEnd = React91__namespace.default.useCallback((e) => {
|
|
@@ -56736,7 +59269,7 @@ function CanvasDndProvider({
|
|
|
56736
59269
|
const overData = e.over?.data.current;
|
|
56737
59270
|
const target = overData?.target;
|
|
56738
59271
|
const accepts = overData?.accepts;
|
|
56739
|
-
|
|
59272
|
+
log13.info("dragEnd", {
|
|
56740
59273
|
activeId: e.active.id,
|
|
56741
59274
|
overId: e.over?.id,
|
|
56742
59275
|
hasPayload: !!payload,
|
|
@@ -56748,7 +59281,7 @@ function CanvasDndProvider({
|
|
|
56748
59281
|
}
|
|
56749
59282
|
if (!payload || !target) return;
|
|
56750
59283
|
if (accepts && !accepts.includes(payload.kind)) {
|
|
56751
|
-
|
|
59284
|
+
log13.debug("dragEnd:rejected:kind", { kind: payload.kind, accepts: [...accepts] });
|
|
56752
59285
|
return;
|
|
56753
59286
|
}
|
|
56754
59287
|
const activator = e.activatorEvent;
|
|
@@ -56760,7 +59293,7 @@ function CanvasDndProvider({
|
|
|
56760
59293
|
}, [eventBus, onDrop]);
|
|
56761
59294
|
const handleDragCancel = React91__namespace.default.useCallback(() => {
|
|
56762
59295
|
setActivePayload(null);
|
|
56763
|
-
|
|
59296
|
+
log13.info("dragCancel");
|
|
56764
59297
|
}, []);
|
|
56765
59298
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
56766
59299
|
core$1.DndContext,
|
|
@@ -58575,7 +61108,7 @@ init_AvlTransitionLane();
|
|
|
58575
61108
|
init_AvlSwimLane();
|
|
58576
61109
|
init_types();
|
|
58577
61110
|
init_avl_elk_layout();
|
|
58578
|
-
var
|
|
61111
|
+
var log14 = logger.createLogger("almadar:ui:avl:trait-scene");
|
|
58579
61112
|
var SWIM_GUTTER2 = 120;
|
|
58580
61113
|
var CENTER_W2 = 360;
|
|
58581
61114
|
var AvlTraitScene = ({
|
|
@@ -58588,7 +61121,7 @@ var AvlTraitScene = ({
|
|
|
58588
61121
|
const dataKey = React91.useMemo(() => JSON.stringify(data), [data]);
|
|
58589
61122
|
React91.useEffect(() => {
|
|
58590
61123
|
computeTraitLayout(data).then(setLayout).catch((error) => {
|
|
58591
|
-
|
|
61124
|
+
log14.error("computeTraitLayout failed", { error: error instanceof Error ? error : String(error) });
|
|
58592
61125
|
});
|
|
58593
61126
|
}, [dataKey]);
|
|
58594
61127
|
if (!layout) {
|