@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/runtime/index.cjs
CHANGED
|
@@ -41,6 +41,12 @@ var core$1 = require('@dnd-kit/core');
|
|
|
41
41
|
var sortable = require('@dnd-kit/sortable');
|
|
42
42
|
var utilities = require('@dnd-kit/utilities');
|
|
43
43
|
var react = require('@xyflow/react');
|
|
44
|
+
var THREE = require('three');
|
|
45
|
+
var GLTFLoader_js = require('three/examples/jsm/loaders/GLTFLoader.js');
|
|
46
|
+
var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
|
|
47
|
+
var GLTFLoader = require('three/examples/jsm/loaders/GLTFLoader');
|
|
48
|
+
var fiber = require('@react-three/fiber');
|
|
49
|
+
var drei = require('@react-three/drei');
|
|
44
50
|
var patterns = require('@almadar/patterns');
|
|
45
51
|
var runtime = require('@almadar/runtime');
|
|
46
52
|
var OrbitalServerRuntime = require('@almadar/runtime/OrbitalServerRuntime');
|
|
@@ -90,6 +96,7 @@ var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
|
|
|
90
96
|
var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
|
|
91
97
|
var remarkMath__default = /*#__PURE__*/_interopDefault(remarkMath);
|
|
92
98
|
var rehypeKatex__default = /*#__PURE__*/_interopDefault(rehypeKatex);
|
|
99
|
+
var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
|
|
93
100
|
|
|
94
101
|
var __defProp = Object.defineProperty;
|
|
95
102
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -455,7 +462,7 @@ var init_Box = __esm({
|
|
|
455
462
|
position,
|
|
456
463
|
className,
|
|
457
464
|
children,
|
|
458
|
-
as:
|
|
465
|
+
as: Component2 = "div",
|
|
459
466
|
action,
|
|
460
467
|
actionPayload,
|
|
461
468
|
hoverEvent,
|
|
@@ -487,7 +494,7 @@ var init_Box = __esm({
|
|
|
487
494
|
}, [hoverEvent, eventBus, onMouseLeave]);
|
|
488
495
|
const isClickable = action || onClick;
|
|
489
496
|
return React82__namespace.default.createElement(
|
|
490
|
-
|
|
497
|
+
Component2,
|
|
491
498
|
{
|
|
492
499
|
ref,
|
|
493
500
|
className: cn(
|
|
@@ -625,9 +632,9 @@ function resolvePhosphor(name, weight, family) {
|
|
|
625
632
|
(lib) => {
|
|
626
633
|
const PhosphorComp = lib[target];
|
|
627
634
|
if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
|
|
628
|
-
const
|
|
635
|
+
const Component2 = PhosphorComp;
|
|
629
636
|
const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
630
|
-
|
|
637
|
+
Component2,
|
|
631
638
|
{
|
|
632
639
|
weight,
|
|
633
640
|
className: props.className,
|
|
@@ -651,9 +658,9 @@ function resolveTabler(name, family) {
|
|
|
651
658
|
(lib) => {
|
|
652
659
|
const TablerComp = lib[target];
|
|
653
660
|
if (!TablerComp || typeof TablerComp !== "object") return null;
|
|
654
|
-
const
|
|
661
|
+
const Component2 = TablerComp;
|
|
655
662
|
const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
656
|
-
|
|
663
|
+
Component2,
|
|
657
664
|
{
|
|
658
665
|
stroke: props.strokeWidth ?? 1.5,
|
|
659
666
|
className: props.className,
|
|
@@ -677,9 +684,9 @@ function resolveFa(name, family) {
|
|
|
677
684
|
(lib) => {
|
|
678
685
|
const FaComp = lib[target];
|
|
679
686
|
if (!FaComp || typeof FaComp !== "function") return null;
|
|
680
|
-
const
|
|
687
|
+
const Component2 = FaComp;
|
|
681
688
|
const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
682
|
-
|
|
689
|
+
Component2,
|
|
683
690
|
{
|
|
684
691
|
className: props.className,
|
|
685
692
|
style: props.style,
|
|
@@ -1634,8 +1641,8 @@ var init_Typography = __esm({
|
|
|
1634
1641
|
children
|
|
1635
1642
|
}) => {
|
|
1636
1643
|
const variant = variantProp ?? (level ? `h${level}` : "body1");
|
|
1637
|
-
const
|
|
1638
|
-
const Comp =
|
|
1644
|
+
const Component2 = as || defaultElements[variant];
|
|
1645
|
+
const Comp = Component2;
|
|
1639
1646
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1640
1647
|
Comp,
|
|
1641
1648
|
{
|
|
@@ -4190,10 +4197,10 @@ var init_Center = __esm({
|
|
|
4190
4197
|
className,
|
|
4191
4198
|
style,
|
|
4192
4199
|
children,
|
|
4193
|
-
as:
|
|
4200
|
+
as: Component2 = "div"
|
|
4194
4201
|
}) => {
|
|
4195
4202
|
const mergedStyle = minHeight ? { minHeight, ...style } : style;
|
|
4196
|
-
const Comp =
|
|
4203
|
+
const Comp = Component2;
|
|
4197
4204
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4198
4205
|
Comp,
|
|
4199
4206
|
{
|
|
@@ -4824,7 +4831,7 @@ var init_Stack = __esm({
|
|
|
4824
4831
|
className,
|
|
4825
4832
|
style,
|
|
4826
4833
|
children,
|
|
4827
|
-
as:
|
|
4834
|
+
as: Component2 = "div",
|
|
4828
4835
|
onClick,
|
|
4829
4836
|
onKeyDown,
|
|
4830
4837
|
role,
|
|
@@ -4842,7 +4849,7 @@ var init_Stack = __esm({
|
|
|
4842
4849
|
};
|
|
4843
4850
|
const isHorizontal = direction === "horizontal";
|
|
4844
4851
|
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";
|
|
4845
|
-
const Comp =
|
|
4852
|
+
const Comp = Component2;
|
|
4846
4853
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4847
4854
|
Comp,
|
|
4848
4855
|
{
|
|
@@ -7073,6 +7080,7 @@ var init_HealthBar = __esm({
|
|
|
7073
7080
|
}
|
|
7074
7081
|
});
|
|
7075
7082
|
function ScoreDisplay({
|
|
7083
|
+
assetUrl = DEFAULT_ASSET_URL,
|
|
7076
7084
|
value,
|
|
7077
7085
|
label,
|
|
7078
7086
|
icon,
|
|
@@ -7119,14 +7127,24 @@ function ScoreDisplay({
|
|
|
7119
7127
|
className
|
|
7120
7128
|
),
|
|
7121
7129
|
children: [
|
|
7122
|
-
|
|
7130
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7131
|
+
"img",
|
|
7132
|
+
{
|
|
7133
|
+
src: assetUrl,
|
|
7134
|
+
alt: "",
|
|
7135
|
+
width: 20,
|
|
7136
|
+
height: 20,
|
|
7137
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7138
|
+
className: "flex-shrink-0"
|
|
7139
|
+
}
|
|
7140
|
+
) : 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,
|
|
7123
7141
|
label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: label }),
|
|
7124
7142
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: formattedValue })
|
|
7125
7143
|
]
|
|
7126
7144
|
}
|
|
7127
7145
|
);
|
|
7128
7146
|
}
|
|
7129
|
-
var sizeMap2;
|
|
7147
|
+
var sizeMap2, DEFAULT_ASSET_URL;
|
|
7130
7148
|
var init_ScoreDisplay = __esm({
|
|
7131
7149
|
"components/game/atoms/ScoreDisplay.tsx"() {
|
|
7132
7150
|
"use client";
|
|
@@ -7138,10 +7156,12 @@ var init_ScoreDisplay = __esm({
|
|
|
7138
7156
|
lg: "text-2xl",
|
|
7139
7157
|
xl: "text-4xl"
|
|
7140
7158
|
};
|
|
7159
|
+
DEFAULT_ASSET_URL = "https://almadar-kflow-assets.web.app/shared/effects/particles/star_01.png";
|
|
7141
7160
|
ScoreDisplay.displayName = "ScoreDisplay";
|
|
7142
7161
|
}
|
|
7143
7162
|
});
|
|
7144
7163
|
function ControlButton({
|
|
7164
|
+
assetUrl = DEFAULT_ASSET_URL2,
|
|
7145
7165
|
label,
|
|
7146
7166
|
icon,
|
|
7147
7167
|
size = "md",
|
|
@@ -7188,7 +7208,7 @@ function ControlButton({
|
|
|
7188
7208
|
},
|
|
7189
7209
|
[isPressed, releaseEvent, eventBus, onRelease]
|
|
7190
7210
|
);
|
|
7191
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
7211
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7192
7212
|
"button",
|
|
7193
7213
|
{
|
|
7194
7214
|
type: "button",
|
|
@@ -7209,20 +7229,27 @@ function ControlButton({
|
|
|
7209
7229
|
disabled && "opacity-50 cursor-not-allowed",
|
|
7210
7230
|
className
|
|
7211
7231
|
),
|
|
7212
|
-
children:
|
|
7213
|
-
|
|
7214
|
-
|
|
7215
|
-
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7232
|
+
children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7233
|
+
"img",
|
|
7234
|
+
{
|
|
7235
|
+
src: assetUrl,
|
|
7236
|
+
alt: "",
|
|
7237
|
+
width: 24,
|
|
7238
|
+
height: 24,
|
|
7239
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7240
|
+
className: "flex-shrink-0"
|
|
7241
|
+
}
|
|
7242
|
+
) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-2xl", children: typeof icon === "string" ? /^[a-zA-Z0-9-]+$/.test(icon) ? (() => {
|
|
7243
|
+
const I = resolveIcon(icon);
|
|
7244
|
+
return I ? /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-6 h-6" }) : null;
|
|
7245
|
+
})() : icon : /* @__PURE__ */ (() => {
|
|
7246
|
+
const I = icon;
|
|
7247
|
+
return /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-6 h-6" });
|
|
7248
|
+
})() }) : label ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: label }) : null
|
|
7222
7249
|
}
|
|
7223
7250
|
);
|
|
7224
7251
|
}
|
|
7225
|
-
var sizeMap3, shapeMap, variantMap;
|
|
7252
|
+
var sizeMap3, shapeMap, variantMap, DEFAULT_ASSET_URL2;
|
|
7226
7253
|
var init_ControlButton = __esm({
|
|
7227
7254
|
"components/game/atoms/ControlButton.tsx"() {
|
|
7228
7255
|
"use client";
|
|
@@ -7245,6 +7272,7 @@ var init_ControlButton = __esm({
|
|
|
7245
7272
|
secondary: "bg-secondary text-secondary-foreground border-border hover:bg-secondary-hover",
|
|
7246
7273
|
ghost: "bg-transparent text-foreground border-border hover:bg-muted"
|
|
7247
7274
|
};
|
|
7275
|
+
DEFAULT_ASSET_URL2 = "https://almadar-kflow-assets.web.app/shared/effects/particles/circle_01.png";
|
|
7248
7276
|
ControlButton.displayName = "ControlButton";
|
|
7249
7277
|
}
|
|
7250
7278
|
});
|
|
@@ -7324,7 +7352,7 @@ var init_Sprite = __esm({
|
|
|
7324
7352
|
}
|
|
7325
7353
|
});
|
|
7326
7354
|
function StateIndicator({
|
|
7327
|
-
assetUrl =
|
|
7355
|
+
assetUrl = DEFAULT_ASSET_URL3,
|
|
7328
7356
|
state = "idle",
|
|
7329
7357
|
label,
|
|
7330
7358
|
size = "md",
|
|
@@ -7363,13 +7391,13 @@ function StateIndicator({
|
|
|
7363
7391
|
}
|
|
7364
7392
|
);
|
|
7365
7393
|
}
|
|
7366
|
-
var
|
|
7394
|
+
var DEFAULT_ASSET_URL3, DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
7367
7395
|
var init_StateIndicator = __esm({
|
|
7368
7396
|
"components/game/atoms/StateIndicator.tsx"() {
|
|
7369
7397
|
init_Box();
|
|
7370
7398
|
init_Icon();
|
|
7371
7399
|
init_cn();
|
|
7372
|
-
|
|
7400
|
+
DEFAULT_ASSET_URL3 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
7373
7401
|
DEFAULT_STATE_STYLES = {
|
|
7374
7402
|
idle: { icon: "\u23F8", bgClass: "bg-muted" },
|
|
7375
7403
|
active: { icon: "\u25B6", bgClass: "bg-success" },
|
|
@@ -7444,7 +7472,7 @@ var init_TimerDisplay = __esm({
|
|
|
7444
7472
|
}
|
|
7445
7473
|
});
|
|
7446
7474
|
function ResourceCounter({
|
|
7447
|
-
assetUrl =
|
|
7475
|
+
assetUrl = DEFAULT_ASSET_URL4,
|
|
7448
7476
|
icon,
|
|
7449
7477
|
label = "Gold",
|
|
7450
7478
|
value = 250,
|
|
@@ -7487,7 +7515,7 @@ function ResourceCounter({
|
|
|
7487
7515
|
}
|
|
7488
7516
|
);
|
|
7489
7517
|
}
|
|
7490
|
-
var colorTokenClasses2,
|
|
7518
|
+
var colorTokenClasses2, DEFAULT_ASSET_URL4, sizeMap5;
|
|
7491
7519
|
var init_ResourceCounter = __esm({
|
|
7492
7520
|
"components/game/atoms/ResourceCounter.tsx"() {
|
|
7493
7521
|
init_cn();
|
|
@@ -7500,7 +7528,7 @@ var init_ResourceCounter = __esm({
|
|
|
7500
7528
|
error: "text-error",
|
|
7501
7529
|
muted: "text-muted-foreground"
|
|
7502
7530
|
};
|
|
7503
|
-
|
|
7531
|
+
DEFAULT_ASSET_URL4 = "https://almadar-kflow-assets.web.app/shared/world-map/gold_mine.png";
|
|
7504
7532
|
sizeMap5 = {
|
|
7505
7533
|
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm", img: 16 },
|
|
7506
7534
|
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base", img: 20 },
|
|
@@ -7510,7 +7538,7 @@ var init_ResourceCounter = __esm({
|
|
|
7510
7538
|
}
|
|
7511
7539
|
});
|
|
7512
7540
|
function ItemSlot({
|
|
7513
|
-
assetUrl =
|
|
7541
|
+
assetUrl = DEFAULT_ASSET_URL5,
|
|
7514
7542
|
icon = "sword",
|
|
7515
7543
|
label = "Iron Sword",
|
|
7516
7544
|
quantity,
|
|
@@ -7569,7 +7597,7 @@ function ItemSlot({
|
|
|
7569
7597
|
}
|
|
7570
7598
|
);
|
|
7571
7599
|
}
|
|
7572
|
-
var sizeMap6, rarityBorderMap, rarityGlowMap,
|
|
7600
|
+
var sizeMap6, rarityBorderMap, rarityGlowMap, DEFAULT_ASSET_URL5, assetSizeMap;
|
|
7573
7601
|
var init_ItemSlot = __esm({
|
|
7574
7602
|
"components/game/atoms/ItemSlot.tsx"() {
|
|
7575
7603
|
"use client";
|
|
@@ -7594,7 +7622,7 @@ var init_ItemSlot = __esm({
|
|
|
7594
7622
|
epic: "shadow-lg",
|
|
7595
7623
|
legendary: "shadow-lg"
|
|
7596
7624
|
};
|
|
7597
|
-
|
|
7625
|
+
DEFAULT_ASSET_URL5 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
7598
7626
|
assetSizeMap = {
|
|
7599
7627
|
sm: 28,
|
|
7600
7628
|
md: 40,
|
|
@@ -7604,6 +7632,7 @@ var init_ItemSlot = __esm({
|
|
|
7604
7632
|
}
|
|
7605
7633
|
});
|
|
7606
7634
|
function TurnIndicator({
|
|
7635
|
+
assetUrl = DEFAULT_ASSET_URL6,
|
|
7607
7636
|
currentTurn = 1,
|
|
7608
7637
|
maxTurns,
|
|
7609
7638
|
activeTeam,
|
|
@@ -7636,14 +7665,24 @@ function TurnIndicator({
|
|
|
7636
7665
|
] }),
|
|
7637
7666
|
activeTeam && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7638
7667
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "|" }),
|
|
7639
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7668
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7669
|
+
"img",
|
|
7670
|
+
{
|
|
7671
|
+
src: assetUrl,
|
|
7672
|
+
alt: "",
|
|
7673
|
+
width: 12,
|
|
7674
|
+
height: 12,
|
|
7675
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7676
|
+
className: "flex-shrink-0"
|
|
7677
|
+
}
|
|
7678
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("rounded-full bg-success/20", sizes.dot) }),
|
|
7640
7679
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-success", children: activeTeam })
|
|
7641
7680
|
] })
|
|
7642
7681
|
]
|
|
7643
7682
|
}
|
|
7644
7683
|
);
|
|
7645
7684
|
}
|
|
7646
|
-
var sizeMap7;
|
|
7685
|
+
var sizeMap7, DEFAULT_ASSET_URL6;
|
|
7647
7686
|
var init_TurnIndicator = __esm({
|
|
7648
7687
|
"components/game/atoms/TurnIndicator.tsx"() {
|
|
7649
7688
|
init_cn();
|
|
@@ -7652,6 +7691,7 @@ var init_TurnIndicator = __esm({
|
|
|
7652
7691
|
md: { wrapper: "text-sm gap-2 px-3 py-1", dot: "w-2 h-2" },
|
|
7653
7692
|
lg: { wrapper: "text-base gap-2.5 px-4 py-1.5", dot: "w-2.5 h-2.5" }
|
|
7654
7693
|
};
|
|
7694
|
+
DEFAULT_ASSET_URL6 = "https://almadar-kflow-assets.web.app/shared/effects/particles/symbol_01.png";
|
|
7655
7695
|
TurnIndicator.displayName = "TurnIndicator";
|
|
7656
7696
|
}
|
|
7657
7697
|
});
|
|
@@ -7667,7 +7707,7 @@ function getComboScale(combo) {
|
|
|
7667
7707
|
return "";
|
|
7668
7708
|
}
|
|
7669
7709
|
function ComboCounter({
|
|
7670
|
-
assetUrl =
|
|
7710
|
+
assetUrl = DEFAULT_ASSET_URL7,
|
|
7671
7711
|
combo = 5,
|
|
7672
7712
|
multiplier,
|
|
7673
7713
|
streak,
|
|
@@ -7712,11 +7752,11 @@ function ComboCounter({
|
|
|
7712
7752
|
}
|
|
7713
7753
|
);
|
|
7714
7754
|
}
|
|
7715
|
-
var
|
|
7755
|
+
var DEFAULT_ASSET_URL7, sizeMap8;
|
|
7716
7756
|
var init_ComboCounter = __esm({
|
|
7717
7757
|
"components/game/atoms/ComboCounter.tsx"() {
|
|
7718
7758
|
init_cn();
|
|
7719
|
-
|
|
7759
|
+
DEFAULT_ASSET_URL7 = "https://almadar-kflow-assets.web.app/shared/effects/flash/flash00.png";
|
|
7720
7760
|
sizeMap8 = {
|
|
7721
7761
|
sm: { combo: "text-lg", label: "text-xs", multiplier: "text-xs" },
|
|
7722
7762
|
md: { combo: "text-2xl", label: "text-xs", multiplier: "text-sm" },
|
|
@@ -7810,7 +7850,7 @@ var init_XPBar = __esm({
|
|
|
7810
7850
|
}
|
|
7811
7851
|
});
|
|
7812
7852
|
function WaypointMarker({
|
|
7813
|
-
assetUrl =
|
|
7853
|
+
assetUrl = DEFAULT_ASSET_URL8,
|
|
7814
7854
|
label,
|
|
7815
7855
|
icon,
|
|
7816
7856
|
active = true,
|
|
@@ -7876,12 +7916,12 @@ function WaypointMarker({
|
|
|
7876
7916
|
)
|
|
7877
7917
|
] });
|
|
7878
7918
|
}
|
|
7879
|
-
var
|
|
7919
|
+
var DEFAULT_ASSET_URL8, sizeMap10, checkIcon;
|
|
7880
7920
|
var init_WaypointMarker = __esm({
|
|
7881
7921
|
"components/game/atoms/WaypointMarker.tsx"() {
|
|
7882
7922
|
init_cn();
|
|
7883
7923
|
init_Icon();
|
|
7884
|
-
|
|
7924
|
+
DEFAULT_ASSET_URL8 = "https://almadar-kflow-assets.web.app/shared/world-map/battle_marker.png";
|
|
7885
7925
|
sizeMap10 = {
|
|
7886
7926
|
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1", img: 12 },
|
|
7887
7927
|
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5", img: 18 },
|
|
@@ -7898,7 +7938,7 @@ function formatDuration(seconds) {
|
|
|
7898
7938
|
return `${Math.round(seconds)}s`;
|
|
7899
7939
|
}
|
|
7900
7940
|
function StatusEffect({
|
|
7901
|
-
assetUrl =
|
|
7941
|
+
assetUrl = DEFAULT_ASSET_URL9,
|
|
7902
7942
|
icon = "shield",
|
|
7903
7943
|
label = "Shield",
|
|
7904
7944
|
duration = 30,
|
|
@@ -7956,12 +7996,12 @@ function StatusEffect({
|
|
|
7956
7996
|
label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground mt-0.5 text-center whitespace-nowrap", children: label })
|
|
7957
7997
|
] });
|
|
7958
7998
|
}
|
|
7959
|
-
var
|
|
7999
|
+
var DEFAULT_ASSET_URL9, sizeMap11, variantStyles7;
|
|
7960
8000
|
var init_StatusEffect = __esm({
|
|
7961
8001
|
"components/game/atoms/StatusEffect.tsx"() {
|
|
7962
8002
|
init_cn();
|
|
7963
8003
|
init_Icon();
|
|
7964
|
-
|
|
8004
|
+
DEFAULT_ASSET_URL9 = "https://almadar-kflow-assets.web.app/shared/effects/particles/flame_01.png";
|
|
7965
8005
|
sizeMap11 = {
|
|
7966
8006
|
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 },
|
|
7967
8007
|
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 },
|
|
@@ -7976,6 +8016,7 @@ var init_StatusEffect = __esm({
|
|
|
7976
8016
|
}
|
|
7977
8017
|
});
|
|
7978
8018
|
function DamageNumber({
|
|
8019
|
+
assetUrl = DEFAULT_ASSET_URL10,
|
|
7979
8020
|
value = 42,
|
|
7980
8021
|
type = "damage",
|
|
7981
8022
|
size = "md",
|
|
@@ -7984,22 +8025,35 @@ function DamageNumber({
|
|
|
7984
8025
|
const displayText = type === "miss" ? "MISS" : type === "heal" ? `+${value}` : `${value}`;
|
|
7985
8026
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7986
8027
|
/* @__PURE__ */ jsxRuntime.jsx("style", { children: floatKeyframes }),
|
|
7987
|
-
/* @__PURE__ */ jsxRuntime.
|
|
8028
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
7988
8029
|
"span",
|
|
7989
8030
|
{
|
|
7990
8031
|
className: cn(
|
|
7991
|
-
"inline-
|
|
8032
|
+
"inline-flex items-center gap-0.5 font-mono select-none pointer-events-none",
|
|
7992
8033
|
sizeMap12[size],
|
|
7993
8034
|
typeStyles[type],
|
|
7994
8035
|
className
|
|
7995
8036
|
),
|
|
7996
8037
|
style: { animation: "damageFloat 1s ease-out forwards" },
|
|
7997
|
-
children:
|
|
8038
|
+
children: [
|
|
8039
|
+
assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8040
|
+
"img",
|
|
8041
|
+
{
|
|
8042
|
+
src: assetUrl,
|
|
8043
|
+
alt: "",
|
|
8044
|
+
width: 14,
|
|
8045
|
+
height: 14,
|
|
8046
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
8047
|
+
className: "flex-shrink-0"
|
|
8048
|
+
}
|
|
8049
|
+
),
|
|
8050
|
+
displayText
|
|
8051
|
+
]
|
|
7998
8052
|
}
|
|
7999
8053
|
)
|
|
8000
8054
|
] });
|
|
8001
8055
|
}
|
|
8002
|
-
var sizeMap12, typeStyles, floatKeyframes;
|
|
8056
|
+
var sizeMap12, typeStyles, floatKeyframes, DEFAULT_ASSET_URL10;
|
|
8003
8057
|
var init_DamageNumber = __esm({
|
|
8004
8058
|
"components/game/atoms/DamageNumber.tsx"() {
|
|
8005
8059
|
init_cn();
|
|
@@ -8021,6 +8075,7 @@ var init_DamageNumber = __esm({
|
|
|
8021
8075
|
100% { opacity: 0; transform: translateY(-32px) scale(0.8); }
|
|
8022
8076
|
}
|
|
8023
8077
|
`;
|
|
8078
|
+
DEFAULT_ASSET_URL10 = "https://almadar-kflow-assets.web.app/shared/effects/particles/spark_01.png";
|
|
8024
8079
|
DamageNumber.displayName = "DamageNumber";
|
|
8025
8080
|
}
|
|
8026
8081
|
});
|
|
@@ -8109,6 +8164,7 @@ var init_ChoiceButton = __esm({
|
|
|
8109
8164
|
}
|
|
8110
8165
|
});
|
|
8111
8166
|
function ActionButton({
|
|
8167
|
+
assetUrl = DEFAULT_ASSET_URL11,
|
|
8112
8168
|
label = "Attack",
|
|
8113
8169
|
icon,
|
|
8114
8170
|
cooldown = 0,
|
|
@@ -8148,13 +8204,23 @@ function ActionButton({
|
|
|
8148
8204
|
}
|
|
8149
8205
|
}
|
|
8150
8206
|
),
|
|
8151
|
-
|
|
8207
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
8208
|
+
"img",
|
|
8209
|
+
{
|
|
8210
|
+
src: assetUrl,
|
|
8211
|
+
alt: "",
|
|
8212
|
+
width: 16,
|
|
8213
|
+
height: 16,
|
|
8214
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
8215
|
+
className: cn("flex-shrink-0", sizes.icon)
|
|
8216
|
+
}
|
|
8217
|
+
) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? (() => {
|
|
8152
8218
|
const I = resolveIcon(icon);
|
|
8153
8219
|
return I ? /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-4 h-4" }) : null;
|
|
8154
8220
|
})() : /* @__PURE__ */ (() => {
|
|
8155
8221
|
const I = icon;
|
|
8156
8222
|
return /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-4 h-4" });
|
|
8157
|
-
})() }),
|
|
8223
|
+
})() }) : null,
|
|
8158
8224
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative z-10", children: label }),
|
|
8159
8225
|
hotkey && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8160
8226
|
"span",
|
|
@@ -8170,7 +8236,7 @@ function ActionButton({
|
|
|
8170
8236
|
}
|
|
8171
8237
|
);
|
|
8172
8238
|
}
|
|
8173
|
-
var sizeMap13, variantStyles8;
|
|
8239
|
+
var sizeMap13, variantStyles8, DEFAULT_ASSET_URL11;
|
|
8174
8240
|
var init_ActionButton = __esm({
|
|
8175
8241
|
"components/game/atoms/ActionButton.tsx"() {
|
|
8176
8242
|
init_cn();
|
|
@@ -8185,6 +8251,7 @@ var init_ActionButton = __esm({
|
|
|
8185
8251
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary-hover border-border",
|
|
8186
8252
|
danger: "bg-error text-error-foreground hover:bg-error/90 border-error"
|
|
8187
8253
|
};
|
|
8254
|
+
DEFAULT_ASSET_URL11 = "https://almadar-kflow-assets.web.app/shared/effects/particles/slash_01.png";
|
|
8188
8255
|
ActionButton.displayName = "ActionButton";
|
|
8189
8256
|
}
|
|
8190
8257
|
});
|
|
@@ -8697,10 +8764,10 @@ var init_Container = __esm({
|
|
|
8697
8764
|
center = true,
|
|
8698
8765
|
className,
|
|
8699
8766
|
children,
|
|
8700
|
-
as:
|
|
8767
|
+
as: Component2 = "div"
|
|
8701
8768
|
}) => {
|
|
8702
8769
|
const resolvedSize = maxWidth ?? size ?? "lg";
|
|
8703
|
-
const Comp =
|
|
8770
|
+
const Comp = Component2;
|
|
8704
8771
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8705
8772
|
Comp,
|
|
8706
8773
|
{
|
|
@@ -24706,7 +24773,7 @@ var init_Flex = __esm({
|
|
|
24706
24773
|
basis,
|
|
24707
24774
|
className,
|
|
24708
24775
|
children,
|
|
24709
|
-
as:
|
|
24776
|
+
as: Component2 = "div"
|
|
24710
24777
|
}) => {
|
|
24711
24778
|
const flexStyle = {};
|
|
24712
24779
|
if (grow !== void 0 || shrink !== void 0 || basis !== void 0) {
|
|
@@ -24718,7 +24785,7 @@ var init_Flex = __esm({
|
|
|
24718
24785
|
flexStyle.flexBasis = typeof basis === "number" ? `${basis}px` : basis;
|
|
24719
24786
|
}
|
|
24720
24787
|
}
|
|
24721
|
-
const Comp =
|
|
24788
|
+
const Comp = Component2;
|
|
24722
24789
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
24723
24790
|
Comp,
|
|
24724
24791
|
{
|
|
@@ -24838,11 +24905,11 @@ var init_Grid = __esm({
|
|
|
24838
24905
|
className,
|
|
24839
24906
|
style,
|
|
24840
24907
|
children,
|
|
24841
|
-
as:
|
|
24908
|
+
as: Component2 = "div"
|
|
24842
24909
|
}) => {
|
|
24843
24910
|
const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
|
|
24844
24911
|
return React82__namespace.default.createElement(
|
|
24845
|
-
|
|
24912
|
+
Component2,
|
|
24846
24913
|
{
|
|
24847
24914
|
className: cn(
|
|
24848
24915
|
"grid",
|
|
@@ -26668,6 +26735,7 @@ var init_ProgressDots = __esm({
|
|
|
26668
26735
|
}
|
|
26669
26736
|
});
|
|
26670
26737
|
function StatBadge({
|
|
26738
|
+
assetUrl = DEFAULT_ASSET_URL12,
|
|
26671
26739
|
label,
|
|
26672
26740
|
value = 0,
|
|
26673
26741
|
max,
|
|
@@ -26691,7 +26759,17 @@ function StatBadge({
|
|
|
26691
26759
|
className
|
|
26692
26760
|
),
|
|
26693
26761
|
children: [
|
|
26694
|
-
|
|
26762
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
26763
|
+
"img",
|
|
26764
|
+
{
|
|
26765
|
+
src: assetUrl,
|
|
26766
|
+
alt: "",
|
|
26767
|
+
width: 16,
|
|
26768
|
+
height: 16,
|
|
26769
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
26770
|
+
className: "flex-shrink-0"
|
|
26771
|
+
}
|
|
26772
|
+
) : 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,
|
|
26695
26773
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground font-medium", children: label }),
|
|
26696
26774
|
format === "hearts" && max && /* @__PURE__ */ jsxRuntime.jsx(
|
|
26697
26775
|
HealthBar,
|
|
@@ -26724,7 +26802,7 @@ function StatBadge({
|
|
|
26724
26802
|
}
|
|
26725
26803
|
);
|
|
26726
26804
|
}
|
|
26727
|
-
var sizeMap17, variantMap2;
|
|
26805
|
+
var sizeMap17, variantMap2, DEFAULT_ASSET_URL12;
|
|
26728
26806
|
var init_StatBadge = __esm({
|
|
26729
26807
|
"components/game/molecules/StatBadge.tsx"() {
|
|
26730
26808
|
init_cn();
|
|
@@ -26743,6 +26821,7 @@ var init_StatBadge = __esm({
|
|
|
26743
26821
|
warning: "bg-warning/15 border-warning/40 text-foreground",
|
|
26744
26822
|
danger: "bg-error/15 border-error/40 text-foreground"
|
|
26745
26823
|
};
|
|
26824
|
+
DEFAULT_ASSET_URL12 = "https://almadar-kflow-assets.web.app/shared/effects/particles/light_01.png";
|
|
26746
26825
|
StatBadge.displayName = "StatBadge";
|
|
26747
26826
|
}
|
|
26748
26827
|
});
|
|
@@ -27399,6 +27478,7 @@ var init_TurnPanel = __esm({
|
|
|
27399
27478
|
}
|
|
27400
27479
|
});
|
|
27401
27480
|
function EnemyPlate({
|
|
27481
|
+
assetUrl = DEFAULT_ASSET_URL13,
|
|
27402
27482
|
name = "Shadow Guard",
|
|
27403
27483
|
health = 80,
|
|
27404
27484
|
maxHealth = 100,
|
|
@@ -27416,15 +27496,28 @@ function EnemyPlate({
|
|
|
27416
27496
|
),
|
|
27417
27497
|
children: [
|
|
27418
27498
|
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center justify-between gap-2", children: [
|
|
27419
|
-
/* @__PURE__ */ jsxRuntime.
|
|
27420
|
-
|
|
27421
|
-
|
|
27422
|
-
|
|
27423
|
-
|
|
27424
|
-
|
|
27425
|
-
|
|
27426
|
-
|
|
27427
|
-
|
|
27499
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center gap-1.5 min-w-0", children: [
|
|
27500
|
+
assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
27501
|
+
"img",
|
|
27502
|
+
{
|
|
27503
|
+
src: assetUrl,
|
|
27504
|
+
alt: name,
|
|
27505
|
+
width: 24,
|
|
27506
|
+
height: 24,
|
|
27507
|
+
style: { objectFit: "cover", borderRadius: "50%" },
|
|
27508
|
+
className: "flex-shrink-0"
|
|
27509
|
+
}
|
|
27510
|
+
),
|
|
27511
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
27512
|
+
Typography,
|
|
27513
|
+
{
|
|
27514
|
+
variant: "caption",
|
|
27515
|
+
weight: "bold",
|
|
27516
|
+
className: "text-[var(--color-foreground)] truncate",
|
|
27517
|
+
children: name
|
|
27518
|
+
}
|
|
27519
|
+
)
|
|
27520
|
+
] }),
|
|
27428
27521
|
level != null && /* @__PURE__ */ jsxRuntime.jsxs(Badge, { variant: "neutral", size: "sm", children: [
|
|
27429
27522
|
"Lv.",
|
|
27430
27523
|
level
|
|
@@ -27469,7 +27562,7 @@ function EnemyPlate({
|
|
|
27469
27562
|
}
|
|
27470
27563
|
);
|
|
27471
27564
|
}
|
|
27472
|
-
var effectVariantMap2, DEFAULT_ENEMY_EFFECTS;
|
|
27565
|
+
var effectVariantMap2, DEFAULT_ENEMY_EFFECTS, DEFAULT_ASSET_URL13;
|
|
27473
27566
|
var init_EnemyPlate = __esm({
|
|
27474
27567
|
"components/game/molecules/EnemyPlate.tsx"() {
|
|
27475
27568
|
"use client";
|
|
@@ -27486,6 +27579,7 @@ var init_EnemyPlate = __esm({
|
|
|
27486
27579
|
DEFAULT_ENEMY_EFFECTS = [
|
|
27487
27580
|
{ icon: "flame", label: "Burn", variant: "debuff" }
|
|
27488
27581
|
];
|
|
27582
|
+
DEFAULT_ASSET_URL13 = "https://almadar-kflow-assets.web.app/shared/portraits/shadow-legion.png";
|
|
27489
27583
|
EnemyPlate.displayName = "EnemyPlate";
|
|
27490
27584
|
}
|
|
27491
27585
|
});
|
|
@@ -29017,13 +29111,13 @@ var init_MapView = __esm({
|
|
|
29017
29111
|
shadowSize: [41, 41]
|
|
29018
29112
|
});
|
|
29019
29113
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
29020
|
-
const { useEffect:
|
|
29114
|
+
const { useEffect: useEffect80, useRef: useRef74, useCallback: useCallback118, useState: useState112 } = React82__namespace.default;
|
|
29021
29115
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
29022
|
-
const { useEventBus:
|
|
29116
|
+
const { useEventBus: useEventBus4 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
29023
29117
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
29024
29118
|
const map = useMap();
|
|
29025
|
-
const prevRef =
|
|
29026
|
-
|
|
29119
|
+
const prevRef = useRef74({ centerLat, centerLng, zoom });
|
|
29120
|
+
useEffect80(() => {
|
|
29027
29121
|
const prev = prevRef.current;
|
|
29028
29122
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
29029
29123
|
map.setView([centerLat, centerLng], zoom);
|
|
@@ -29034,7 +29128,7 @@ var init_MapView = __esm({
|
|
|
29034
29128
|
}
|
|
29035
29129
|
function MapClickHandler({ onMapClick }) {
|
|
29036
29130
|
const map = useMap();
|
|
29037
|
-
|
|
29131
|
+
useEffect80(() => {
|
|
29038
29132
|
if (!onMapClick) return;
|
|
29039
29133
|
const handler = (e) => {
|
|
29040
29134
|
onMapClick(e.latlng.lat, e.latlng.lng);
|
|
@@ -29061,9 +29155,9 @@ var init_MapView = __esm({
|
|
|
29061
29155
|
className,
|
|
29062
29156
|
showAttribution = true
|
|
29063
29157
|
}) {
|
|
29064
|
-
const eventBus =
|
|
29065
|
-
const [clickedPosition, setClickedPosition] =
|
|
29066
|
-
const handleMapClick =
|
|
29158
|
+
const eventBus = useEventBus4();
|
|
29159
|
+
const [clickedPosition, setClickedPosition] = useState112(null);
|
|
29160
|
+
const handleMapClick = useCallback118((lat, lng) => {
|
|
29067
29161
|
if (showClickedPin) {
|
|
29068
29162
|
setClickedPosition({ lat, lng });
|
|
29069
29163
|
}
|
|
@@ -29072,7 +29166,7 @@ var init_MapView = __esm({
|
|
|
29072
29166
|
eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
|
|
29073
29167
|
}
|
|
29074
29168
|
}, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
|
|
29075
|
-
const handleMarkerClick =
|
|
29169
|
+
const handleMarkerClick = useCallback118((marker) => {
|
|
29076
29170
|
onMarkerClick?.(marker);
|
|
29077
29171
|
if (markerClickEvent) {
|
|
29078
29172
|
eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
|
|
@@ -36199,10 +36293,10 @@ var init_Section = __esm({
|
|
|
36199
36293
|
children,
|
|
36200
36294
|
headerClassName,
|
|
36201
36295
|
contentClassName,
|
|
36202
|
-
as:
|
|
36296
|
+
as: Component2 = "section"
|
|
36203
36297
|
}) => {
|
|
36204
36298
|
const hasHeader = title || description || action;
|
|
36205
|
-
const Comp =
|
|
36299
|
+
const Comp = Component2;
|
|
36206
36300
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
36207
36301
|
Comp,
|
|
36208
36302
|
{
|
|
@@ -40767,6 +40861,1568 @@ var init_GameAudioToggle = __esm({
|
|
|
40767
40861
|
GameAudioToggle.displayName = "GameAudioToggle";
|
|
40768
40862
|
}
|
|
40769
40863
|
});
|
|
40864
|
+
function detectAssetRoot(modelUrl) {
|
|
40865
|
+
const idx = modelUrl.indexOf("/3d/");
|
|
40866
|
+
if (idx !== -1) {
|
|
40867
|
+
return modelUrl.substring(0, idx + 4);
|
|
40868
|
+
}
|
|
40869
|
+
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
40870
|
+
}
|
|
40871
|
+
function createGLTFLoaderForUrl(url) {
|
|
40872
|
+
const loader = new GLTFLoader_js.GLTFLoader();
|
|
40873
|
+
loader.setResourcePath(detectAssetRoot(url));
|
|
40874
|
+
return loader;
|
|
40875
|
+
}
|
|
40876
|
+
var AssetLoader;
|
|
40877
|
+
var init_AssetLoader = __esm({
|
|
40878
|
+
"components/game/molecules/three/loaders/AssetLoader.ts"() {
|
|
40879
|
+
AssetLoader = class {
|
|
40880
|
+
constructor() {
|
|
40881
|
+
__publicField(this, "objLoader");
|
|
40882
|
+
__publicField(this, "textureLoader");
|
|
40883
|
+
__publicField(this, "modelCache");
|
|
40884
|
+
__publicField(this, "textureCache");
|
|
40885
|
+
__publicField(this, "loadingPromises");
|
|
40886
|
+
this.objLoader = new OBJLoader_js.OBJLoader();
|
|
40887
|
+
this.textureLoader = new THREE__namespace.TextureLoader();
|
|
40888
|
+
this.modelCache = /* @__PURE__ */ new Map();
|
|
40889
|
+
this.textureCache = /* @__PURE__ */ new Map();
|
|
40890
|
+
this.loadingPromises = /* @__PURE__ */ new Map();
|
|
40891
|
+
}
|
|
40892
|
+
/**
|
|
40893
|
+
* Load a GLB/GLTF model
|
|
40894
|
+
* @param url - URL to the .glb or .gltf file
|
|
40895
|
+
* @returns Promise with loaded model scene and animations
|
|
40896
|
+
*/
|
|
40897
|
+
async loadModel(url) {
|
|
40898
|
+
if (this.modelCache.has(url)) {
|
|
40899
|
+
return this.modelCache.get(url);
|
|
40900
|
+
}
|
|
40901
|
+
if (this.loadingPromises.has(url)) {
|
|
40902
|
+
return this.loadingPromises.get(url);
|
|
40903
|
+
}
|
|
40904
|
+
const loader = createGLTFLoaderForUrl(url);
|
|
40905
|
+
const loadPromise = loader.loadAsync(url).then((gltf) => {
|
|
40906
|
+
const result = {
|
|
40907
|
+
scene: gltf.scene,
|
|
40908
|
+
animations: gltf.animations || []
|
|
40909
|
+
};
|
|
40910
|
+
this.modelCache.set(url, result);
|
|
40911
|
+
this.loadingPromises.delete(url);
|
|
40912
|
+
return result;
|
|
40913
|
+
}).catch((error) => {
|
|
40914
|
+
this.loadingPromises.delete(url);
|
|
40915
|
+
throw new Error(`Failed to load model ${url}: ${error.message}`);
|
|
40916
|
+
});
|
|
40917
|
+
this.loadingPromises.set(url, loadPromise);
|
|
40918
|
+
return loadPromise;
|
|
40919
|
+
}
|
|
40920
|
+
/**
|
|
40921
|
+
* Load an OBJ model (fallback for non-GLB assets)
|
|
40922
|
+
* @param url - URL to the .obj file
|
|
40923
|
+
* @returns Promise with loaded object group
|
|
40924
|
+
*/
|
|
40925
|
+
async loadOBJ(url) {
|
|
40926
|
+
if (this.modelCache.has(url)) {
|
|
40927
|
+
return this.modelCache.get(url).scene;
|
|
40928
|
+
}
|
|
40929
|
+
if (this.loadingPromises.has(url)) {
|
|
40930
|
+
const result = await this.loadingPromises.get(url);
|
|
40931
|
+
return result.scene;
|
|
40932
|
+
}
|
|
40933
|
+
const loadPromise = this.objLoader.loadAsync(url).then((group) => {
|
|
40934
|
+
const result = {
|
|
40935
|
+
scene: group,
|
|
40936
|
+
animations: []
|
|
40937
|
+
};
|
|
40938
|
+
this.modelCache.set(url, result);
|
|
40939
|
+
this.loadingPromises.delete(url);
|
|
40940
|
+
return result;
|
|
40941
|
+
}).catch((error) => {
|
|
40942
|
+
this.loadingPromises.delete(url);
|
|
40943
|
+
throw new Error(`Failed to load OBJ ${url}: ${error.message}`);
|
|
40944
|
+
});
|
|
40945
|
+
this.loadingPromises.set(url, loadPromise);
|
|
40946
|
+
return (await loadPromise).scene;
|
|
40947
|
+
}
|
|
40948
|
+
/**
|
|
40949
|
+
* Load a texture
|
|
40950
|
+
* @param url - URL to the texture image
|
|
40951
|
+
* @returns Promise with loaded texture
|
|
40952
|
+
*/
|
|
40953
|
+
async loadTexture(url) {
|
|
40954
|
+
if (this.textureCache.has(url)) {
|
|
40955
|
+
return this.textureCache.get(url);
|
|
40956
|
+
}
|
|
40957
|
+
if (this.loadingPromises.has(`texture:${url}`)) {
|
|
40958
|
+
return this.loadingPromises.get(`texture:${url}`);
|
|
40959
|
+
}
|
|
40960
|
+
const loadPromise = this.textureLoader.loadAsync(url).then((texture) => {
|
|
40961
|
+
texture.colorSpace = THREE__namespace.SRGBColorSpace;
|
|
40962
|
+
this.textureCache.set(url, texture);
|
|
40963
|
+
this.loadingPromises.delete(`texture:${url}`);
|
|
40964
|
+
return texture;
|
|
40965
|
+
}).catch((error) => {
|
|
40966
|
+
this.loadingPromises.delete(`texture:${url}`);
|
|
40967
|
+
throw new Error(`Failed to load texture ${url}: ${error.message}`);
|
|
40968
|
+
});
|
|
40969
|
+
this.loadingPromises.set(`texture:${url}`, loadPromise);
|
|
40970
|
+
return loadPromise;
|
|
40971
|
+
}
|
|
40972
|
+
/**
|
|
40973
|
+
* Preload multiple assets
|
|
40974
|
+
* @param urls - Array of asset URLs to preload
|
|
40975
|
+
* @returns Promise that resolves when all assets are loaded
|
|
40976
|
+
*/
|
|
40977
|
+
async preload(urls) {
|
|
40978
|
+
const promises = urls.map((url) => {
|
|
40979
|
+
if (url.endsWith(".glb") || url.endsWith(".gltf")) {
|
|
40980
|
+
return this.loadModel(url).catch(() => null);
|
|
40981
|
+
} else if (url.endsWith(".obj")) {
|
|
40982
|
+
return this.loadOBJ(url).catch(() => null);
|
|
40983
|
+
} else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
|
|
40984
|
+
return this.loadTexture(url).catch(() => null);
|
|
40985
|
+
}
|
|
40986
|
+
return Promise.resolve(null);
|
|
40987
|
+
});
|
|
40988
|
+
await Promise.all(promises);
|
|
40989
|
+
}
|
|
40990
|
+
/**
|
|
40991
|
+
* Check if a model is cached
|
|
40992
|
+
* @param url - Model URL
|
|
40993
|
+
*/
|
|
40994
|
+
hasModel(url) {
|
|
40995
|
+
return this.modelCache.has(url);
|
|
40996
|
+
}
|
|
40997
|
+
/**
|
|
40998
|
+
* Check if a texture is cached
|
|
40999
|
+
* @param url - Texture URL
|
|
41000
|
+
*/
|
|
41001
|
+
hasTexture(url) {
|
|
41002
|
+
return this.textureCache.has(url);
|
|
41003
|
+
}
|
|
41004
|
+
/**
|
|
41005
|
+
* Get cached model (throws if not cached)
|
|
41006
|
+
* @param url - Model URL
|
|
41007
|
+
*/
|
|
41008
|
+
getModel(url) {
|
|
41009
|
+
const model = this.modelCache.get(url);
|
|
41010
|
+
if (!model) {
|
|
41011
|
+
throw new Error(`Model ${url} not in cache`);
|
|
41012
|
+
}
|
|
41013
|
+
return model;
|
|
41014
|
+
}
|
|
41015
|
+
/**
|
|
41016
|
+
* Get cached texture (throws if not cached)
|
|
41017
|
+
* @param url - Texture URL
|
|
41018
|
+
*/
|
|
41019
|
+
getTexture(url) {
|
|
41020
|
+
const texture = this.textureCache.get(url);
|
|
41021
|
+
if (!texture) {
|
|
41022
|
+
throw new Error(`Texture ${url} not in cache`);
|
|
41023
|
+
}
|
|
41024
|
+
return texture;
|
|
41025
|
+
}
|
|
41026
|
+
/**
|
|
41027
|
+
* Clear all caches
|
|
41028
|
+
*/
|
|
41029
|
+
clearCache() {
|
|
41030
|
+
this.textureCache.forEach((texture) => {
|
|
41031
|
+
texture.dispose();
|
|
41032
|
+
});
|
|
41033
|
+
this.modelCache.forEach((model) => {
|
|
41034
|
+
model.scene.traverse((child) => {
|
|
41035
|
+
if (child instanceof THREE__namespace.Mesh) {
|
|
41036
|
+
child.geometry.dispose();
|
|
41037
|
+
if (Array.isArray(child.material)) {
|
|
41038
|
+
child.material.forEach((m) => m.dispose());
|
|
41039
|
+
} else {
|
|
41040
|
+
child.material.dispose();
|
|
41041
|
+
}
|
|
41042
|
+
}
|
|
41043
|
+
});
|
|
41044
|
+
});
|
|
41045
|
+
this.modelCache.clear();
|
|
41046
|
+
this.textureCache.clear();
|
|
41047
|
+
this.loadingPromises.clear();
|
|
41048
|
+
}
|
|
41049
|
+
/**
|
|
41050
|
+
* Get cache statistics
|
|
41051
|
+
*/
|
|
41052
|
+
getStats() {
|
|
41053
|
+
return {
|
|
41054
|
+
models: this.modelCache.size,
|
|
41055
|
+
textures: this.textureCache.size,
|
|
41056
|
+
loading: this.loadingPromises.size
|
|
41057
|
+
};
|
|
41058
|
+
}
|
|
41059
|
+
};
|
|
41060
|
+
new AssetLoader();
|
|
41061
|
+
}
|
|
41062
|
+
});
|
|
41063
|
+
function useAssetLoader(options = {}) {
|
|
41064
|
+
const { preloadUrls = [], loader: customLoader } = options;
|
|
41065
|
+
const loaderRef = React82.useRef(customLoader || new AssetLoader());
|
|
41066
|
+
const [state, setState] = React82.useState({
|
|
41067
|
+
isLoading: false,
|
|
41068
|
+
progress: 0,
|
|
41069
|
+
loaded: 0,
|
|
41070
|
+
total: 0,
|
|
41071
|
+
errors: []
|
|
41072
|
+
});
|
|
41073
|
+
React82.useEffect(() => {
|
|
41074
|
+
if (preloadUrls.length > 0) {
|
|
41075
|
+
preload(preloadUrls);
|
|
41076
|
+
}
|
|
41077
|
+
}, []);
|
|
41078
|
+
const updateProgress = React82.useCallback((loaded, total) => {
|
|
41079
|
+
setState((prev) => ({
|
|
41080
|
+
...prev,
|
|
41081
|
+
loaded,
|
|
41082
|
+
total,
|
|
41083
|
+
progress: total > 0 ? Math.round(loaded / total * 100) : 0
|
|
41084
|
+
}));
|
|
41085
|
+
}, []);
|
|
41086
|
+
const loadModel = React82.useCallback(
|
|
41087
|
+
async (url) => {
|
|
41088
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
41089
|
+
try {
|
|
41090
|
+
const model = await loaderRef.current.loadModel(url);
|
|
41091
|
+
setState((prev) => ({
|
|
41092
|
+
...prev,
|
|
41093
|
+
isLoading: false,
|
|
41094
|
+
loaded: prev.loaded + 1
|
|
41095
|
+
}));
|
|
41096
|
+
return model;
|
|
41097
|
+
} catch (error) {
|
|
41098
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
41099
|
+
setState((prev) => ({
|
|
41100
|
+
...prev,
|
|
41101
|
+
isLoading: false,
|
|
41102
|
+
errors: [...prev.errors, errorMsg]
|
|
41103
|
+
}));
|
|
41104
|
+
throw error;
|
|
41105
|
+
}
|
|
41106
|
+
},
|
|
41107
|
+
[]
|
|
41108
|
+
);
|
|
41109
|
+
const loadOBJ = React82.useCallback(
|
|
41110
|
+
async (url) => {
|
|
41111
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
41112
|
+
try {
|
|
41113
|
+
const model = await loaderRef.current.loadOBJ(url);
|
|
41114
|
+
setState((prev) => ({
|
|
41115
|
+
...prev,
|
|
41116
|
+
isLoading: false,
|
|
41117
|
+
loaded: prev.loaded + 1
|
|
41118
|
+
}));
|
|
41119
|
+
return model;
|
|
41120
|
+
} catch (error) {
|
|
41121
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
41122
|
+
setState((prev) => ({
|
|
41123
|
+
...prev,
|
|
41124
|
+
isLoading: false,
|
|
41125
|
+
errors: [...prev.errors, errorMsg]
|
|
41126
|
+
}));
|
|
41127
|
+
throw error;
|
|
41128
|
+
}
|
|
41129
|
+
},
|
|
41130
|
+
[]
|
|
41131
|
+
);
|
|
41132
|
+
const loadTexture = React82.useCallback(
|
|
41133
|
+
async (url) => {
|
|
41134
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
41135
|
+
try {
|
|
41136
|
+
const texture = await loaderRef.current.loadTexture(url);
|
|
41137
|
+
setState((prev) => ({
|
|
41138
|
+
...prev,
|
|
41139
|
+
isLoading: false,
|
|
41140
|
+
loaded: prev.loaded + 1
|
|
41141
|
+
}));
|
|
41142
|
+
return texture;
|
|
41143
|
+
} catch (error) {
|
|
41144
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
41145
|
+
setState((prev) => ({
|
|
41146
|
+
...prev,
|
|
41147
|
+
isLoading: false,
|
|
41148
|
+
errors: [...prev.errors, errorMsg]
|
|
41149
|
+
}));
|
|
41150
|
+
throw error;
|
|
41151
|
+
}
|
|
41152
|
+
},
|
|
41153
|
+
[]
|
|
41154
|
+
);
|
|
41155
|
+
const preload = React82.useCallback(
|
|
41156
|
+
async (urls) => {
|
|
41157
|
+
setState((prev) => ({
|
|
41158
|
+
...prev,
|
|
41159
|
+
isLoading: true,
|
|
41160
|
+
total: urls.length,
|
|
41161
|
+
loaded: 0,
|
|
41162
|
+
errors: []
|
|
41163
|
+
}));
|
|
41164
|
+
let completed = 0;
|
|
41165
|
+
const errors = [];
|
|
41166
|
+
await Promise.all(
|
|
41167
|
+
urls.map(async (url) => {
|
|
41168
|
+
try {
|
|
41169
|
+
if (url.endsWith(".glb") || url.endsWith(".gltf")) {
|
|
41170
|
+
await loaderRef.current.loadModel(url);
|
|
41171
|
+
} else if (url.endsWith(".obj")) {
|
|
41172
|
+
await loaderRef.current.loadOBJ(url);
|
|
41173
|
+
} else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
|
|
41174
|
+
await loaderRef.current.loadTexture(url);
|
|
41175
|
+
}
|
|
41176
|
+
completed++;
|
|
41177
|
+
updateProgress(completed, urls.length);
|
|
41178
|
+
} catch (error) {
|
|
41179
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
41180
|
+
errors.push(`${url}: ${errorMsg}`);
|
|
41181
|
+
completed++;
|
|
41182
|
+
updateProgress(completed, urls.length);
|
|
41183
|
+
}
|
|
41184
|
+
})
|
|
41185
|
+
);
|
|
41186
|
+
setState((prev) => ({
|
|
41187
|
+
...prev,
|
|
41188
|
+
isLoading: false,
|
|
41189
|
+
errors
|
|
41190
|
+
}));
|
|
41191
|
+
},
|
|
41192
|
+
[updateProgress]
|
|
41193
|
+
);
|
|
41194
|
+
const hasModel = React82.useCallback((url) => {
|
|
41195
|
+
return loaderRef.current.hasModel(url);
|
|
41196
|
+
}, []);
|
|
41197
|
+
const hasTexture = React82.useCallback((url) => {
|
|
41198
|
+
return loaderRef.current.hasTexture(url);
|
|
41199
|
+
}, []);
|
|
41200
|
+
const getModel = React82.useCallback((url) => {
|
|
41201
|
+
try {
|
|
41202
|
+
return loaderRef.current.getModel(url);
|
|
41203
|
+
} catch {
|
|
41204
|
+
return void 0;
|
|
41205
|
+
}
|
|
41206
|
+
}, []);
|
|
41207
|
+
const getTexture = React82.useCallback((url) => {
|
|
41208
|
+
try {
|
|
41209
|
+
return loaderRef.current.getTexture(url);
|
|
41210
|
+
} catch {
|
|
41211
|
+
return void 0;
|
|
41212
|
+
}
|
|
41213
|
+
}, []);
|
|
41214
|
+
const clearCache = React82.useCallback(() => {
|
|
41215
|
+
loaderRef.current.clearCache();
|
|
41216
|
+
setState({
|
|
41217
|
+
isLoading: false,
|
|
41218
|
+
progress: 0,
|
|
41219
|
+
loaded: 0,
|
|
41220
|
+
total: 0,
|
|
41221
|
+
errors: []
|
|
41222
|
+
});
|
|
41223
|
+
}, []);
|
|
41224
|
+
return {
|
|
41225
|
+
...state,
|
|
41226
|
+
loadModel,
|
|
41227
|
+
loadOBJ,
|
|
41228
|
+
loadTexture,
|
|
41229
|
+
preload,
|
|
41230
|
+
hasModel,
|
|
41231
|
+
hasTexture,
|
|
41232
|
+
getModel,
|
|
41233
|
+
getTexture,
|
|
41234
|
+
clearCache
|
|
41235
|
+
};
|
|
41236
|
+
}
|
|
41237
|
+
var init_useAssetLoader = __esm({
|
|
41238
|
+
"components/game/molecules/three/hooks/useAssetLoader.ts"() {
|
|
41239
|
+
"use client";
|
|
41240
|
+
init_AssetLoader();
|
|
41241
|
+
}
|
|
41242
|
+
});
|
|
41243
|
+
function useGameCanvas3DEvents(options) {
|
|
41244
|
+
const {
|
|
41245
|
+
tileClickEvent,
|
|
41246
|
+
unitClickEvent,
|
|
41247
|
+
featureClickEvent,
|
|
41248
|
+
canvasClickEvent,
|
|
41249
|
+
tileHoverEvent,
|
|
41250
|
+
tileLeaveEvent,
|
|
41251
|
+
unitAnimationEvent,
|
|
41252
|
+
cameraChangeEvent,
|
|
41253
|
+
onTileClick,
|
|
41254
|
+
onUnitClick,
|
|
41255
|
+
onFeatureClick,
|
|
41256
|
+
onCanvasClick,
|
|
41257
|
+
onTileHover,
|
|
41258
|
+
onUnitAnimation
|
|
41259
|
+
} = options;
|
|
41260
|
+
const emit = useEmitEvent();
|
|
41261
|
+
const optionsRef = React82.useRef(options);
|
|
41262
|
+
optionsRef.current = options;
|
|
41263
|
+
const handleTileClick = React82.useCallback(
|
|
41264
|
+
(tile, event) => {
|
|
41265
|
+
if (tileClickEvent) {
|
|
41266
|
+
emit(tileClickEvent, {
|
|
41267
|
+
tileId: tile.id,
|
|
41268
|
+
x: tile.x,
|
|
41269
|
+
z: tile.z ?? tile.y ?? 0,
|
|
41270
|
+
type: tile.type,
|
|
41271
|
+
terrain: tile.terrain,
|
|
41272
|
+
elevation: tile.elevation
|
|
41273
|
+
});
|
|
41274
|
+
}
|
|
41275
|
+
optionsRef.current.onTileClick?.(tile, event);
|
|
41276
|
+
},
|
|
41277
|
+
[tileClickEvent, emit]
|
|
41278
|
+
);
|
|
41279
|
+
const handleUnitClick = React82.useCallback(
|
|
41280
|
+
(unit, event) => {
|
|
41281
|
+
if (unitClickEvent) {
|
|
41282
|
+
emit(unitClickEvent, {
|
|
41283
|
+
unitId: unit.id,
|
|
41284
|
+
x: unit.x,
|
|
41285
|
+
z: unit.z ?? unit.y ?? 0,
|
|
41286
|
+
unitType: unit.unitType,
|
|
41287
|
+
name: unit.name,
|
|
41288
|
+
team: unit.team,
|
|
41289
|
+
faction: unit.faction,
|
|
41290
|
+
health: unit.health,
|
|
41291
|
+
maxHealth: unit.maxHealth
|
|
41292
|
+
});
|
|
41293
|
+
}
|
|
41294
|
+
optionsRef.current.onUnitClick?.(unit, event);
|
|
41295
|
+
},
|
|
41296
|
+
[unitClickEvent, emit]
|
|
41297
|
+
);
|
|
41298
|
+
const handleFeatureClick = React82.useCallback(
|
|
41299
|
+
(feature, event) => {
|
|
41300
|
+
if (featureClickEvent) {
|
|
41301
|
+
emit(featureClickEvent, {
|
|
41302
|
+
featureId: feature.id,
|
|
41303
|
+
x: feature.x,
|
|
41304
|
+
z: feature.z ?? feature.y ?? 0,
|
|
41305
|
+
type: feature.type,
|
|
41306
|
+
elevation: feature.elevation
|
|
41307
|
+
});
|
|
41308
|
+
}
|
|
41309
|
+
optionsRef.current.onFeatureClick?.(feature, event);
|
|
41310
|
+
},
|
|
41311
|
+
[featureClickEvent, emit]
|
|
41312
|
+
);
|
|
41313
|
+
const handleCanvasClick = React82.useCallback(
|
|
41314
|
+
(event) => {
|
|
41315
|
+
if (canvasClickEvent) {
|
|
41316
|
+
emit(canvasClickEvent, {
|
|
41317
|
+
clientX: event.clientX,
|
|
41318
|
+
clientY: event.clientY,
|
|
41319
|
+
button: event.button
|
|
41320
|
+
});
|
|
41321
|
+
}
|
|
41322
|
+
optionsRef.current.onCanvasClick?.(event);
|
|
41323
|
+
},
|
|
41324
|
+
[canvasClickEvent, emit]
|
|
41325
|
+
);
|
|
41326
|
+
const handleTileHover = React82.useCallback(
|
|
41327
|
+
(tile, event) => {
|
|
41328
|
+
if (tile) {
|
|
41329
|
+
if (tileHoverEvent) {
|
|
41330
|
+
emit(tileHoverEvent, {
|
|
41331
|
+
tileId: tile.id,
|
|
41332
|
+
x: tile.x,
|
|
41333
|
+
z: tile.z ?? tile.y ?? 0,
|
|
41334
|
+
type: tile.type
|
|
41335
|
+
});
|
|
41336
|
+
}
|
|
41337
|
+
} else {
|
|
41338
|
+
if (tileLeaveEvent) {
|
|
41339
|
+
emit(tileLeaveEvent, {});
|
|
41340
|
+
}
|
|
41341
|
+
}
|
|
41342
|
+
optionsRef.current.onTileHover?.(tile, event);
|
|
41343
|
+
},
|
|
41344
|
+
[tileHoverEvent, tileLeaveEvent, emit]
|
|
41345
|
+
);
|
|
41346
|
+
const handleUnitAnimation = React82.useCallback(
|
|
41347
|
+
(unitId, state) => {
|
|
41348
|
+
if (unitAnimationEvent) {
|
|
41349
|
+
emit(unitAnimationEvent, {
|
|
41350
|
+
unitId,
|
|
41351
|
+
state,
|
|
41352
|
+
timestamp: Date.now()
|
|
41353
|
+
});
|
|
41354
|
+
}
|
|
41355
|
+
optionsRef.current.onUnitAnimation?.(unitId, state);
|
|
41356
|
+
},
|
|
41357
|
+
[unitAnimationEvent, emit]
|
|
41358
|
+
);
|
|
41359
|
+
const handleCameraChange = React82.useCallback(
|
|
41360
|
+
(position) => {
|
|
41361
|
+
if (cameraChangeEvent) {
|
|
41362
|
+
emit(cameraChangeEvent, {
|
|
41363
|
+
position,
|
|
41364
|
+
timestamp: Date.now()
|
|
41365
|
+
});
|
|
41366
|
+
}
|
|
41367
|
+
},
|
|
41368
|
+
[cameraChangeEvent, emit]
|
|
41369
|
+
);
|
|
41370
|
+
return {
|
|
41371
|
+
handleTileClick,
|
|
41372
|
+
handleUnitClick,
|
|
41373
|
+
handleFeatureClick,
|
|
41374
|
+
handleCanvasClick,
|
|
41375
|
+
handleTileHover,
|
|
41376
|
+
handleUnitAnimation,
|
|
41377
|
+
handleCameraChange
|
|
41378
|
+
};
|
|
41379
|
+
}
|
|
41380
|
+
var init_useGameCanvas3DEvents = __esm({
|
|
41381
|
+
"components/game/molecules/three/hooks/useGameCanvas3DEvents.ts"() {
|
|
41382
|
+
"use client";
|
|
41383
|
+
init_useEventBus();
|
|
41384
|
+
}
|
|
41385
|
+
});
|
|
41386
|
+
|
|
41387
|
+
// components/game/molecules/three/components/Canvas3DLoadingState.css
|
|
41388
|
+
var init_Canvas3DLoadingState = __esm({
|
|
41389
|
+
"components/game/molecules/three/components/Canvas3DLoadingState.css"() {
|
|
41390
|
+
}
|
|
41391
|
+
});
|
|
41392
|
+
function Canvas3DLoadingState({
|
|
41393
|
+
progress = 0,
|
|
41394
|
+
loaded = 0,
|
|
41395
|
+
total = 0,
|
|
41396
|
+
message = "Loading 3D Scene...",
|
|
41397
|
+
details,
|
|
41398
|
+
showSpinner = true,
|
|
41399
|
+
className
|
|
41400
|
+
}) {
|
|
41401
|
+
const clampedProgress = Math.max(0, Math.min(100, progress));
|
|
41402
|
+
const hasProgress = total > 0;
|
|
41403
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `canvas-3d-loading ${className || ""}`, children: [
|
|
41404
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__content", children: [
|
|
41405
|
+
showSpinner && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__spinner", children: [
|
|
41406
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "spinner__ring" }),
|
|
41407
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "spinner__ring spinner__ring--secondary" })
|
|
41408
|
+
] }),
|
|
41409
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__message", children: message }),
|
|
41410
|
+
details && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__details", children: details }),
|
|
41411
|
+
hasProgress && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__progress", children: [
|
|
41412
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "progress__bar", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
41413
|
+
"div",
|
|
41414
|
+
{
|
|
41415
|
+
className: "progress__fill",
|
|
41416
|
+
style: { width: `${clampedProgress}%` }
|
|
41417
|
+
}
|
|
41418
|
+
) }),
|
|
41419
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "progress__text", children: [
|
|
41420
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "progress__percentage", children: [
|
|
41421
|
+
clampedProgress,
|
|
41422
|
+
"%"
|
|
41423
|
+
] }),
|
|
41424
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "progress__count", children: [
|
|
41425
|
+
"(",
|
|
41426
|
+
loaded,
|
|
41427
|
+
"/",
|
|
41428
|
+
total,
|
|
41429
|
+
")"
|
|
41430
|
+
] })
|
|
41431
|
+
] })
|
|
41432
|
+
] })
|
|
41433
|
+
] }),
|
|
41434
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__background", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg__grid" }) })
|
|
41435
|
+
] });
|
|
41436
|
+
}
|
|
41437
|
+
var init_Canvas3DLoadingState2 = __esm({
|
|
41438
|
+
"components/game/molecules/three/components/Canvas3DLoadingState.tsx"() {
|
|
41439
|
+
init_Canvas3DLoadingState();
|
|
41440
|
+
}
|
|
41441
|
+
});
|
|
41442
|
+
|
|
41443
|
+
// components/game/molecules/three/components/Canvas3DErrorBoundary.css
|
|
41444
|
+
var init_Canvas3DErrorBoundary = __esm({
|
|
41445
|
+
"components/game/molecules/three/components/Canvas3DErrorBoundary.css"() {
|
|
41446
|
+
}
|
|
41447
|
+
});
|
|
41448
|
+
var log10, Canvas3DErrorBoundary;
|
|
41449
|
+
var init_Canvas3DErrorBoundary2 = __esm({
|
|
41450
|
+
"components/game/molecules/three/components/Canvas3DErrorBoundary.tsx"() {
|
|
41451
|
+
init_Canvas3DErrorBoundary();
|
|
41452
|
+
log10 = logger.createLogger("almadar:ui:game:canvas3d:error-boundary");
|
|
41453
|
+
Canvas3DErrorBoundary = class extends React82.Component {
|
|
41454
|
+
constructor(props) {
|
|
41455
|
+
super(props);
|
|
41456
|
+
__publicField(this, "handleReset", () => {
|
|
41457
|
+
this.setState({
|
|
41458
|
+
hasError: false,
|
|
41459
|
+
error: null,
|
|
41460
|
+
errorInfo: null
|
|
41461
|
+
});
|
|
41462
|
+
this.props.onReset?.();
|
|
41463
|
+
});
|
|
41464
|
+
this.state = {
|
|
41465
|
+
hasError: false,
|
|
41466
|
+
error: null,
|
|
41467
|
+
errorInfo: null
|
|
41468
|
+
};
|
|
41469
|
+
}
|
|
41470
|
+
static getDerivedStateFromError(error) {
|
|
41471
|
+
return {
|
|
41472
|
+
hasError: true,
|
|
41473
|
+
error,
|
|
41474
|
+
errorInfo: null
|
|
41475
|
+
};
|
|
41476
|
+
}
|
|
41477
|
+
componentDidCatch(error, errorInfo) {
|
|
41478
|
+
this.setState({ errorInfo });
|
|
41479
|
+
this.props.onError?.(error, errorInfo);
|
|
41480
|
+
log10.error("Error caught", { error });
|
|
41481
|
+
log10.error("Component stack", { componentStack: errorInfo.componentStack ?? "<none>" });
|
|
41482
|
+
}
|
|
41483
|
+
render() {
|
|
41484
|
+
if (this.state.hasError) {
|
|
41485
|
+
if (this.props.fallback) {
|
|
41486
|
+
return this.props.fallback;
|
|
41487
|
+
}
|
|
41488
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-error", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-error__content", children: [
|
|
41489
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-error__icon", children: "\u26A0\uFE0F" }),
|
|
41490
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "canvas-3d-error__title", children: "3D Scene Error" }),
|
|
41491
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "canvas-3d-error__message", children: "Something went wrong while rendering the 3D scene." }),
|
|
41492
|
+
this.state.error && /* @__PURE__ */ jsxRuntime.jsxs("details", { className: "canvas-3d-error__details", children: [
|
|
41493
|
+
/* @__PURE__ */ jsxRuntime.jsx("summary", { children: "Error Details" }),
|
|
41494
|
+
/* @__PURE__ */ jsxRuntime.jsxs("pre", { className: "error__stack", children: [
|
|
41495
|
+
this.state.error.message,
|
|
41496
|
+
"\n",
|
|
41497
|
+
this.state.error.stack
|
|
41498
|
+
] }),
|
|
41499
|
+
this.state.errorInfo && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "error__component-stack", children: this.state.errorInfo.componentStack })
|
|
41500
|
+
] }),
|
|
41501
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-error__actions", children: [
|
|
41502
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
41503
|
+
"button",
|
|
41504
|
+
{
|
|
41505
|
+
className: "error__button error__button--primary",
|
|
41506
|
+
onClick: this.handleReset,
|
|
41507
|
+
children: "Try Again"
|
|
41508
|
+
}
|
|
41509
|
+
),
|
|
41510
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
41511
|
+
"button",
|
|
41512
|
+
{
|
|
41513
|
+
className: "error__button error__button--secondary",
|
|
41514
|
+
onClick: () => window.location.reload(),
|
|
41515
|
+
children: "Reload Page"
|
|
41516
|
+
}
|
|
41517
|
+
)
|
|
41518
|
+
] })
|
|
41519
|
+
] }) });
|
|
41520
|
+
}
|
|
41521
|
+
return this.props.children;
|
|
41522
|
+
}
|
|
41523
|
+
};
|
|
41524
|
+
}
|
|
41525
|
+
});
|
|
41526
|
+
function detectAssetRoot2(modelUrl) {
|
|
41527
|
+
const idx = modelUrl.indexOf("/3d/");
|
|
41528
|
+
if (idx !== -1) {
|
|
41529
|
+
return modelUrl.substring(0, idx + 4);
|
|
41530
|
+
}
|
|
41531
|
+
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
41532
|
+
}
|
|
41533
|
+
function useGLTFModel(url, resourceBasePath) {
|
|
41534
|
+
const [state, setState] = React82.useState({
|
|
41535
|
+
model: null,
|
|
41536
|
+
isLoading: false,
|
|
41537
|
+
error: null
|
|
41538
|
+
});
|
|
41539
|
+
React82.useEffect(() => {
|
|
41540
|
+
if (!url) {
|
|
41541
|
+
setState({ model: null, isLoading: false, error: null });
|
|
41542
|
+
return;
|
|
41543
|
+
}
|
|
41544
|
+
log11.debug("Loading", { url });
|
|
41545
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
41546
|
+
const assetRoot = resourceBasePath || detectAssetRoot2(url);
|
|
41547
|
+
const loader = new GLTFLoader.GLTFLoader();
|
|
41548
|
+
loader.setResourcePath(assetRoot);
|
|
41549
|
+
loader.load(
|
|
41550
|
+
url,
|
|
41551
|
+
(gltf) => {
|
|
41552
|
+
log11.debug("Loaded", { url });
|
|
41553
|
+
setState({
|
|
41554
|
+
model: gltf.scene,
|
|
41555
|
+
isLoading: false,
|
|
41556
|
+
error: null
|
|
41557
|
+
});
|
|
41558
|
+
},
|
|
41559
|
+
void 0,
|
|
41560
|
+
(err) => {
|
|
41561
|
+
log11.warn("Failed", { url, error: err instanceof Error ? err : String(err) });
|
|
41562
|
+
setState({
|
|
41563
|
+
model: null,
|
|
41564
|
+
isLoading: false,
|
|
41565
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
41566
|
+
});
|
|
41567
|
+
}
|
|
41568
|
+
);
|
|
41569
|
+
}, [url, resourceBasePath]);
|
|
41570
|
+
return state;
|
|
41571
|
+
}
|
|
41572
|
+
function ModelLoader({
|
|
41573
|
+
url,
|
|
41574
|
+
position = [0, 0, 0],
|
|
41575
|
+
scale = 1,
|
|
41576
|
+
rotation = [0, 0, 0],
|
|
41577
|
+
isSelected = false,
|
|
41578
|
+
isHovered = false,
|
|
41579
|
+
onClick,
|
|
41580
|
+
onHover,
|
|
41581
|
+
fallbackGeometry = "box",
|
|
41582
|
+
castShadow = true,
|
|
41583
|
+
receiveShadow = true,
|
|
41584
|
+
resourceBasePath
|
|
41585
|
+
}) {
|
|
41586
|
+
const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
|
|
41587
|
+
const model = React82.useMemo(() => {
|
|
41588
|
+
if (!loadedModel) return null;
|
|
41589
|
+
const cloned = loadedModel.clone();
|
|
41590
|
+
cloned.traverse((child) => {
|
|
41591
|
+
if (child instanceof THREE__namespace.Mesh) {
|
|
41592
|
+
child.castShadow = castShadow;
|
|
41593
|
+
child.receiveShadow = receiveShadow;
|
|
41594
|
+
}
|
|
41595
|
+
});
|
|
41596
|
+
return cloned;
|
|
41597
|
+
}, [loadedModel, castShadow, receiveShadow]);
|
|
41598
|
+
const scaleArray = React82.useMemo(() => {
|
|
41599
|
+
if (typeof scale === "number") {
|
|
41600
|
+
return [scale, scale, scale];
|
|
41601
|
+
}
|
|
41602
|
+
return scale;
|
|
41603
|
+
}, [scale]);
|
|
41604
|
+
const rotationRad = React82.useMemo(() => {
|
|
41605
|
+
return [
|
|
41606
|
+
rotation[0] * Math.PI / 180,
|
|
41607
|
+
rotation[1] * Math.PI / 180,
|
|
41608
|
+
rotation[2] * Math.PI / 180
|
|
41609
|
+
];
|
|
41610
|
+
}, [rotation]);
|
|
41611
|
+
if (isLoading) {
|
|
41612
|
+
return /* @__PURE__ */ jsxRuntime.jsx("group", { position, children: /* @__PURE__ */ jsxRuntime.jsxs("mesh", { rotation: [Math.PI / 2, 0, 0], children: [
|
|
41613
|
+
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.3, 0.35, 16] }),
|
|
41614
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#4a90d9", transparent: true, opacity: 0.8 })
|
|
41615
|
+
] }) });
|
|
41616
|
+
}
|
|
41617
|
+
if (error || !model) {
|
|
41618
|
+
if (fallbackGeometry === "none") {
|
|
41619
|
+
return /* @__PURE__ */ jsxRuntime.jsx("group", { position });
|
|
41620
|
+
}
|
|
41621
|
+
const fallbackProps = {
|
|
41622
|
+
onClick,
|
|
41623
|
+
onPointerOver: () => onHover?.(true),
|
|
41624
|
+
onPointerOut: () => onHover?.(false)
|
|
41625
|
+
};
|
|
41626
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("group", { position, children: [
|
|
41627
|
+
(isSelected || isHovered) && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
41628
|
+
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
|
|
41629
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
41630
|
+
"meshBasicMaterial",
|
|
41631
|
+
{
|
|
41632
|
+
color: isSelected ? 16755200 : 16777215,
|
|
41633
|
+
transparent: true,
|
|
41634
|
+
opacity: 0.5
|
|
41635
|
+
}
|
|
41636
|
+
)
|
|
41637
|
+
] }),
|
|
41638
|
+
fallbackGeometry === "box" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
41639
|
+
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.8, 0.8, 0.8] }),
|
|
41640
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
41641
|
+
] }),
|
|
41642
|
+
fallbackGeometry === "sphere" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
41643
|
+
/* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [0.4, 16, 16] }),
|
|
41644
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
41645
|
+
] }),
|
|
41646
|
+
fallbackGeometry === "cylinder" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
41647
|
+
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.3, 0.3, 0.8, 16] }),
|
|
41648
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
41649
|
+
] })
|
|
41650
|
+
] });
|
|
41651
|
+
}
|
|
41652
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
41653
|
+
"group",
|
|
41654
|
+
{
|
|
41655
|
+
position,
|
|
41656
|
+
rotation: rotationRad,
|
|
41657
|
+
onClick,
|
|
41658
|
+
onPointerOver: () => onHover?.(true),
|
|
41659
|
+
onPointerOut: () => onHover?.(false),
|
|
41660
|
+
children: [
|
|
41661
|
+
(isSelected || isHovered) && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
41662
|
+
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
|
|
41663
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
41664
|
+
"meshBasicMaterial",
|
|
41665
|
+
{
|
|
41666
|
+
color: isSelected ? 16755200 : 16777215,
|
|
41667
|
+
transparent: true,
|
|
41668
|
+
opacity: 0.5
|
|
41669
|
+
}
|
|
41670
|
+
)
|
|
41671
|
+
] }),
|
|
41672
|
+
/* @__PURE__ */ jsxRuntime.jsx("primitive", { object: model, scale: scaleArray })
|
|
41673
|
+
]
|
|
41674
|
+
}
|
|
41675
|
+
);
|
|
41676
|
+
}
|
|
41677
|
+
var log11;
|
|
41678
|
+
var init_ModelLoader = __esm({
|
|
41679
|
+
"components/game/molecules/three/components/ModelLoader.tsx"() {
|
|
41680
|
+
"use client";
|
|
41681
|
+
log11 = logger.createLogger("almadar:ui:game:model-loader");
|
|
41682
|
+
}
|
|
41683
|
+
});
|
|
41684
|
+
|
|
41685
|
+
// components/game/molecules/GameCanvas3D.css
|
|
41686
|
+
var init_GameCanvas3D = __esm({
|
|
41687
|
+
"components/game/molecules/GameCanvas3D.css"() {
|
|
41688
|
+
}
|
|
41689
|
+
});
|
|
41690
|
+
function CameraController({
|
|
41691
|
+
onCameraChange
|
|
41692
|
+
}) {
|
|
41693
|
+
const { camera } = fiber.useThree();
|
|
41694
|
+
React82.useEffect(() => {
|
|
41695
|
+
if (onCameraChange) {
|
|
41696
|
+
onCameraChange({
|
|
41697
|
+
x: camera.position.x,
|
|
41698
|
+
y: camera.position.y,
|
|
41699
|
+
z: camera.position.z
|
|
41700
|
+
});
|
|
41701
|
+
}
|
|
41702
|
+
}, [camera.position, onCameraChange]);
|
|
41703
|
+
return null;
|
|
41704
|
+
}
|
|
41705
|
+
var DEFAULT_GRID_CONFIG, GameCanvas3D;
|
|
41706
|
+
var init_GameCanvas3D2 = __esm({
|
|
41707
|
+
"components/game/molecules/GameCanvas3D.tsx"() {
|
|
41708
|
+
"use client";
|
|
41709
|
+
init_AssetLoader();
|
|
41710
|
+
init_useAssetLoader();
|
|
41711
|
+
init_useGameCanvas3DEvents();
|
|
41712
|
+
init_Canvas3DLoadingState2();
|
|
41713
|
+
init_Canvas3DErrorBoundary2();
|
|
41714
|
+
init_ModelLoader();
|
|
41715
|
+
init_cn();
|
|
41716
|
+
init_GameCanvas3D();
|
|
41717
|
+
DEFAULT_GRID_CONFIG = {
|
|
41718
|
+
cellSize: 1,
|
|
41719
|
+
offsetX: 0,
|
|
41720
|
+
offsetZ: 0
|
|
41721
|
+
};
|
|
41722
|
+
GameCanvas3D = React82.forwardRef(
|
|
41723
|
+
({
|
|
41724
|
+
tiles = [],
|
|
41725
|
+
units = [],
|
|
41726
|
+
features = [],
|
|
41727
|
+
events: events2 = [],
|
|
41728
|
+
orientation = "standard",
|
|
41729
|
+
cameraMode = "isometric",
|
|
41730
|
+
showGrid = true,
|
|
41731
|
+
showCoordinates = false,
|
|
41732
|
+
showTileInfo = false,
|
|
41733
|
+
overlay = "default",
|
|
41734
|
+
shadows = true,
|
|
41735
|
+
backgroundColor = "#1a1a2e",
|
|
41736
|
+
onTileClick,
|
|
41737
|
+
onUnitClick,
|
|
41738
|
+
onFeatureClick,
|
|
41739
|
+
onCanvasClick,
|
|
41740
|
+
onTileHover,
|
|
41741
|
+
onUnitAnimation,
|
|
41742
|
+
assetLoader: customAssetLoader,
|
|
41743
|
+
tileRenderer: CustomTileRenderer,
|
|
41744
|
+
unitRenderer: CustomUnitRenderer,
|
|
41745
|
+
featureRenderer: CustomFeatureRenderer,
|
|
41746
|
+
className,
|
|
41747
|
+
isLoading: externalLoading,
|
|
41748
|
+
error: externalError,
|
|
41749
|
+
entity,
|
|
41750
|
+
preloadAssets = [],
|
|
41751
|
+
tileClickEvent,
|
|
41752
|
+
unitClickEvent,
|
|
41753
|
+
featureClickEvent,
|
|
41754
|
+
canvasClickEvent,
|
|
41755
|
+
tileHoverEvent,
|
|
41756
|
+
tileLeaveEvent,
|
|
41757
|
+
unitAnimationEvent,
|
|
41758
|
+
cameraChangeEvent,
|
|
41759
|
+
loadingMessage = "Loading 3D Scene...",
|
|
41760
|
+
useInstancing = true,
|
|
41761
|
+
validMoves = [],
|
|
41762
|
+
attackTargets = [],
|
|
41763
|
+
selectedTileIds = [],
|
|
41764
|
+
selectedUnitId = null,
|
|
41765
|
+
children
|
|
41766
|
+
}, ref) => {
|
|
41767
|
+
const containerRef = React82.useRef(null);
|
|
41768
|
+
const controlsRef = React82.useRef(null);
|
|
41769
|
+
const [hoveredTile, setHoveredTile] = React82.useState(null);
|
|
41770
|
+
const [internalError, setInternalError] = React82.useState(null);
|
|
41771
|
+
const { isLoading: assetsLoading, progress, loaded, total } = useAssetLoader({
|
|
41772
|
+
preloadUrls: preloadAssets,
|
|
41773
|
+
loader: customAssetLoader
|
|
41774
|
+
});
|
|
41775
|
+
const eventHandlers = useGameCanvas3DEvents({
|
|
41776
|
+
tileClickEvent,
|
|
41777
|
+
unitClickEvent,
|
|
41778
|
+
featureClickEvent,
|
|
41779
|
+
canvasClickEvent,
|
|
41780
|
+
tileHoverEvent,
|
|
41781
|
+
tileLeaveEvent,
|
|
41782
|
+
unitAnimationEvent,
|
|
41783
|
+
cameraChangeEvent,
|
|
41784
|
+
onTileClick,
|
|
41785
|
+
onUnitClick,
|
|
41786
|
+
onFeatureClick,
|
|
41787
|
+
onCanvasClick,
|
|
41788
|
+
onTileHover,
|
|
41789
|
+
onUnitAnimation
|
|
41790
|
+
});
|
|
41791
|
+
const gridBounds = React82.useMemo(() => {
|
|
41792
|
+
if (tiles.length === 0) {
|
|
41793
|
+
return { minX: 0, maxX: 10, minZ: 0, maxZ: 10 };
|
|
41794
|
+
}
|
|
41795
|
+
const xs = tiles.map((t) => t.x);
|
|
41796
|
+
const zs = tiles.map((t) => t.z || t.y || 0);
|
|
41797
|
+
return {
|
|
41798
|
+
minX: Math.min(...xs),
|
|
41799
|
+
maxX: Math.max(...xs),
|
|
41800
|
+
minZ: Math.min(...zs),
|
|
41801
|
+
maxZ: Math.max(...zs)
|
|
41802
|
+
};
|
|
41803
|
+
}, [tiles]);
|
|
41804
|
+
const cameraTarget = React82.useMemo(() => {
|
|
41805
|
+
return [
|
|
41806
|
+
(gridBounds.minX + gridBounds.maxX) / 2,
|
|
41807
|
+
0,
|
|
41808
|
+
(gridBounds.minZ + gridBounds.maxZ) / 2
|
|
41809
|
+
];
|
|
41810
|
+
}, [gridBounds]);
|
|
41811
|
+
const gridConfig = React82.useMemo(
|
|
41812
|
+
() => ({
|
|
41813
|
+
...DEFAULT_GRID_CONFIG,
|
|
41814
|
+
offsetX: -(gridBounds.maxX - gridBounds.minX) / 2,
|
|
41815
|
+
offsetZ: -(gridBounds.maxZ - gridBounds.minZ) / 2
|
|
41816
|
+
}),
|
|
41817
|
+
[gridBounds]
|
|
41818
|
+
);
|
|
41819
|
+
const gridToWorld = React82.useCallback(
|
|
41820
|
+
(x, z, y = 0) => {
|
|
41821
|
+
const worldX = (x - gridBounds.minX) * gridConfig.cellSize;
|
|
41822
|
+
const worldZ = (z - gridBounds.minZ) * gridConfig.cellSize;
|
|
41823
|
+
return [worldX, y * gridConfig.cellSize, worldZ];
|
|
41824
|
+
},
|
|
41825
|
+
[gridBounds, gridConfig]
|
|
41826
|
+
);
|
|
41827
|
+
React82.useImperativeHandle(ref, () => ({
|
|
41828
|
+
getCameraPosition: () => {
|
|
41829
|
+
if (controlsRef.current) {
|
|
41830
|
+
const pos = controlsRef.current.object.position;
|
|
41831
|
+
return new THREE__namespace.Vector3(pos.x, pos.y, pos.z);
|
|
41832
|
+
}
|
|
41833
|
+
return null;
|
|
41834
|
+
},
|
|
41835
|
+
setCameraPosition: (x, y, z) => {
|
|
41836
|
+
if (controlsRef.current) {
|
|
41837
|
+
controlsRef.current.object.position.set(x, y, z);
|
|
41838
|
+
controlsRef.current.update();
|
|
41839
|
+
}
|
|
41840
|
+
},
|
|
41841
|
+
lookAt: (x, y, z) => {
|
|
41842
|
+
if (controlsRef.current) {
|
|
41843
|
+
controlsRef.current.target.set(x, y, z);
|
|
41844
|
+
controlsRef.current.update();
|
|
41845
|
+
}
|
|
41846
|
+
},
|
|
41847
|
+
resetCamera: () => {
|
|
41848
|
+
if (controlsRef.current) {
|
|
41849
|
+
controlsRef.current.reset();
|
|
41850
|
+
}
|
|
41851
|
+
},
|
|
41852
|
+
screenshot: () => {
|
|
41853
|
+
const canvas = containerRef.current?.querySelector("canvas");
|
|
41854
|
+
if (canvas) {
|
|
41855
|
+
return canvas.toDataURL("image/png");
|
|
41856
|
+
}
|
|
41857
|
+
return null;
|
|
41858
|
+
},
|
|
41859
|
+
export: () => ({
|
|
41860
|
+
tiles,
|
|
41861
|
+
units,
|
|
41862
|
+
features
|
|
41863
|
+
})
|
|
41864
|
+
}));
|
|
41865
|
+
const handleTileClick = React82.useCallback(
|
|
41866
|
+
(tile, event) => {
|
|
41867
|
+
eventHandlers.handleTileClick(tile, event);
|
|
41868
|
+
},
|
|
41869
|
+
[eventHandlers]
|
|
41870
|
+
);
|
|
41871
|
+
const handleUnitClick = React82.useCallback(
|
|
41872
|
+
(unit, event) => {
|
|
41873
|
+
eventHandlers.handleUnitClick(unit, event);
|
|
41874
|
+
},
|
|
41875
|
+
[eventHandlers]
|
|
41876
|
+
);
|
|
41877
|
+
const handleFeatureClick = React82.useCallback(
|
|
41878
|
+
(feature, event) => {
|
|
41879
|
+
if (event) {
|
|
41880
|
+
eventHandlers.handleFeatureClick(feature, event);
|
|
41881
|
+
}
|
|
41882
|
+
},
|
|
41883
|
+
[eventHandlers]
|
|
41884
|
+
);
|
|
41885
|
+
const handleTileHover = React82.useCallback(
|
|
41886
|
+
(tile, event) => {
|
|
41887
|
+
setHoveredTile(tile);
|
|
41888
|
+
if (event) {
|
|
41889
|
+
eventHandlers.handleTileHover(tile, event);
|
|
41890
|
+
}
|
|
41891
|
+
},
|
|
41892
|
+
[eventHandlers]
|
|
41893
|
+
);
|
|
41894
|
+
const cameraConfig = React82.useMemo(() => {
|
|
41895
|
+
const size = Math.max(
|
|
41896
|
+
gridBounds.maxX - gridBounds.minX,
|
|
41897
|
+
gridBounds.maxZ - gridBounds.minZ
|
|
41898
|
+
);
|
|
41899
|
+
const distance = size * 1.5;
|
|
41900
|
+
switch (cameraMode) {
|
|
41901
|
+
case "isometric":
|
|
41902
|
+
return {
|
|
41903
|
+
position: [distance, distance * 0.8, distance],
|
|
41904
|
+
fov: 45
|
|
41905
|
+
};
|
|
41906
|
+
case "top-down":
|
|
41907
|
+
return {
|
|
41908
|
+
position: [0, distance * 2, 0],
|
|
41909
|
+
fov: 45
|
|
41910
|
+
};
|
|
41911
|
+
case "perspective":
|
|
41912
|
+
default:
|
|
41913
|
+
return {
|
|
41914
|
+
position: [distance, distance, distance],
|
|
41915
|
+
fov: 45
|
|
41916
|
+
};
|
|
41917
|
+
}
|
|
41918
|
+
}, [cameraMode, gridBounds]);
|
|
41919
|
+
const DefaultTileRenderer = React82.useCallback(
|
|
41920
|
+
({ tile, position }) => {
|
|
41921
|
+
const isSelected = tile.id ? selectedTileIds.includes(tile.id) : false;
|
|
41922
|
+
const isHovered = hoveredTile?.id === tile.id;
|
|
41923
|
+
const isValidMove = validMoves.some(
|
|
41924
|
+
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
41925
|
+
);
|
|
41926
|
+
const isAttackTarget = attackTargets.some(
|
|
41927
|
+
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
41928
|
+
);
|
|
41929
|
+
let color = 8421504;
|
|
41930
|
+
if (tile.type === "water") color = 4491468;
|
|
41931
|
+
else if (tile.type === "grass") color = 4500036;
|
|
41932
|
+
else if (tile.type === "sand") color = 14535816;
|
|
41933
|
+
else if (tile.type === "rock") color = 8947848;
|
|
41934
|
+
else if (tile.type === "snow") color = 15658734;
|
|
41935
|
+
let emissive = 0;
|
|
41936
|
+
if (isSelected) emissive = 4473924;
|
|
41937
|
+
else if (isAttackTarget) emissive = 4456448;
|
|
41938
|
+
else if (isValidMove) emissive = 17408;
|
|
41939
|
+
else if (isHovered) emissive = 2236962;
|
|
41940
|
+
if (tile.modelUrl) {
|
|
41941
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
41942
|
+
"group",
|
|
41943
|
+
{
|
|
41944
|
+
position,
|
|
41945
|
+
onClick: (e) => handleTileClick(tile, e),
|
|
41946
|
+
onPointerEnter: (e) => handleTileHover(tile, e),
|
|
41947
|
+
onPointerLeave: (e) => handleTileHover(null, e),
|
|
41948
|
+
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
41949
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
41950
|
+
ModelLoader,
|
|
41951
|
+
{
|
|
41952
|
+
url: tile.modelUrl,
|
|
41953
|
+
scale: 0.95,
|
|
41954
|
+
fallbackGeometry: "box",
|
|
41955
|
+
castShadow: true,
|
|
41956
|
+
receiveShadow: true
|
|
41957
|
+
}
|
|
41958
|
+
)
|
|
41959
|
+
}
|
|
41960
|
+
);
|
|
41961
|
+
}
|
|
41962
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
41963
|
+
"mesh",
|
|
41964
|
+
{
|
|
41965
|
+
position,
|
|
41966
|
+
onClick: (e) => handleTileClick(tile, e),
|
|
41967
|
+
onPointerEnter: (e) => handleTileHover(tile, e),
|
|
41968
|
+
onPointerLeave: (e) => handleTileHover(null, e),
|
|
41969
|
+
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
41970
|
+
children: [
|
|
41971
|
+
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.95, 0.2, 0.95] }),
|
|
41972
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color, emissive })
|
|
41973
|
+
]
|
|
41974
|
+
}
|
|
41975
|
+
);
|
|
41976
|
+
},
|
|
41977
|
+
[selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover]
|
|
41978
|
+
);
|
|
41979
|
+
const DefaultUnitRenderer = React82.useCallback(
|
|
41980
|
+
({ unit, position }) => {
|
|
41981
|
+
const isSelected = selectedUnitId === unit.id;
|
|
41982
|
+
const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
|
|
41983
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
41984
|
+
"group",
|
|
41985
|
+
{
|
|
41986
|
+
position,
|
|
41987
|
+
onClick: (e) => handleUnitClick(unit, e),
|
|
41988
|
+
userData: { type: "unit", unitId: unit.id },
|
|
41989
|
+
children: [
|
|
41990
|
+
isSelected && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.05, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
41991
|
+
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
|
|
41992
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
|
|
41993
|
+
] }),
|
|
41994
|
+
unit.modelUrl ? (
|
|
41995
|
+
/* GLB unit model (box fallback while loading / on error) */
|
|
41996
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
41997
|
+
ModelLoader,
|
|
41998
|
+
{
|
|
41999
|
+
url: unit.modelUrl,
|
|
42000
|
+
scale: 0.5,
|
|
42001
|
+
fallbackGeometry: "box",
|
|
42002
|
+
castShadow: true
|
|
42003
|
+
}
|
|
42004
|
+
)
|
|
42005
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
42006
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.3, 0], children: [
|
|
42007
|
+
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.3, 0.3, 0.1, 8] }),
|
|
42008
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
42009
|
+
] }),
|
|
42010
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.6, 0], children: [
|
|
42011
|
+
/* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [0.2, 0.4, 4, 8] }),
|
|
42012
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
42013
|
+
] }),
|
|
42014
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
42015
|
+
/* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [0.12, 8, 8] }),
|
|
42016
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
42017
|
+
] })
|
|
42018
|
+
] }),
|
|
42019
|
+
unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("group", { position: [0, 1.2, 0], children: [
|
|
42020
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [-0.25, 0, 0], children: [
|
|
42021
|
+
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.5, 0.05] }),
|
|
42022
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: 3355443 })
|
|
42023
|
+
] }),
|
|
42024
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
42025
|
+
"mesh",
|
|
42026
|
+
{
|
|
42027
|
+
position: [
|
|
42028
|
+
-0.25 + 0.5 * (unit.health / unit.maxHealth) / 2,
|
|
42029
|
+
0,
|
|
42030
|
+
0.01
|
|
42031
|
+
],
|
|
42032
|
+
children: [
|
|
42033
|
+
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.5 * (unit.health / unit.maxHealth), 0.05] }),
|
|
42034
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42035
|
+
"meshBasicMaterial",
|
|
42036
|
+
{
|
|
42037
|
+
color: unit.health / unit.maxHealth > 0.5 ? 4500036 : unit.health / unit.maxHealth > 0.25 ? 11184708 : 16729156
|
|
42038
|
+
}
|
|
42039
|
+
)
|
|
42040
|
+
]
|
|
42041
|
+
}
|
|
42042
|
+
)
|
|
42043
|
+
] })
|
|
42044
|
+
]
|
|
42045
|
+
}
|
|
42046
|
+
);
|
|
42047
|
+
},
|
|
42048
|
+
[selectedUnitId, handleUnitClick]
|
|
42049
|
+
);
|
|
42050
|
+
const DefaultFeatureRenderer = React82.useCallback(
|
|
42051
|
+
({
|
|
42052
|
+
feature,
|
|
42053
|
+
position
|
|
42054
|
+
}) => {
|
|
42055
|
+
if (feature.assetUrl) {
|
|
42056
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
42057
|
+
ModelLoader,
|
|
42058
|
+
{
|
|
42059
|
+
url: feature.assetUrl,
|
|
42060
|
+
position,
|
|
42061
|
+
scale: 0.5,
|
|
42062
|
+
rotation: [0, feature.rotation ?? 0, 0],
|
|
42063
|
+
onClick: () => handleFeatureClick(feature, null),
|
|
42064
|
+
fallbackGeometry: "box"
|
|
42065
|
+
},
|
|
42066
|
+
feature.id
|
|
42067
|
+
);
|
|
42068
|
+
}
|
|
42069
|
+
if (feature.type === "tree") {
|
|
42070
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
42071
|
+
"group",
|
|
42072
|
+
{
|
|
42073
|
+
position,
|
|
42074
|
+
onClick: (e) => handleFeatureClick(feature, e),
|
|
42075
|
+
userData: { type: "feature", featureId: feature.id },
|
|
42076
|
+
children: [
|
|
42077
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.4, 0], children: [
|
|
42078
|
+
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.1, 0.15, 0.8, 6] }),
|
|
42079
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 9127187 })
|
|
42080
|
+
] }),
|
|
42081
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
42082
|
+
/* @__PURE__ */ jsxRuntime.jsx("coneGeometry", { args: [0.5, 0.8, 8] }),
|
|
42083
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 2263842 })
|
|
42084
|
+
] })
|
|
42085
|
+
]
|
|
42086
|
+
}
|
|
42087
|
+
);
|
|
42088
|
+
}
|
|
42089
|
+
if (feature.type === "rock") {
|
|
42090
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
42091
|
+
"mesh",
|
|
42092
|
+
{
|
|
42093
|
+
position: [position[0], position[1] + 0.3, position[2]],
|
|
42094
|
+
onClick: (e) => handleFeatureClick(feature, e),
|
|
42095
|
+
userData: { type: "feature", featureId: feature.id },
|
|
42096
|
+
children: [
|
|
42097
|
+
/* @__PURE__ */ jsxRuntime.jsx("dodecahedronGeometry", { args: [0.3, 0] }),
|
|
42098
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 8421504 })
|
|
42099
|
+
]
|
|
42100
|
+
}
|
|
42101
|
+
);
|
|
42102
|
+
}
|
|
42103
|
+
return null;
|
|
42104
|
+
},
|
|
42105
|
+
[handleFeatureClick]
|
|
42106
|
+
);
|
|
42107
|
+
if (externalLoading || assetsLoading && preloadAssets.length > 0) {
|
|
42108
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
42109
|
+
Canvas3DLoadingState,
|
|
42110
|
+
{
|
|
42111
|
+
progress,
|
|
42112
|
+
loaded,
|
|
42113
|
+
total,
|
|
42114
|
+
message: loadingMessage,
|
|
42115
|
+
className
|
|
42116
|
+
}
|
|
42117
|
+
);
|
|
42118
|
+
}
|
|
42119
|
+
const displayError = externalError || internalError;
|
|
42120
|
+
if (displayError) {
|
|
42121
|
+
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: [
|
|
42122
|
+
"Error: ",
|
|
42123
|
+
displayError
|
|
42124
|
+
] }) }) });
|
|
42125
|
+
}
|
|
42126
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
42127
|
+
Canvas3DErrorBoundary,
|
|
42128
|
+
{
|
|
42129
|
+
onError: (err) => setInternalError(err.message),
|
|
42130
|
+
onReset: () => setInternalError(null),
|
|
42131
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
42132
|
+
"div",
|
|
42133
|
+
{
|
|
42134
|
+
ref: containerRef,
|
|
42135
|
+
className: cn("game-canvas-3d relative w-full h-full min-h-[85vh] overflow-hidden", className),
|
|
42136
|
+
"data-orientation": orientation,
|
|
42137
|
+
"data-camera-mode": cameraMode,
|
|
42138
|
+
"data-overlay": overlay,
|
|
42139
|
+
children: [
|
|
42140
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
42141
|
+
fiber.Canvas,
|
|
42142
|
+
{
|
|
42143
|
+
shadows,
|
|
42144
|
+
camera: {
|
|
42145
|
+
position: cameraConfig.position,
|
|
42146
|
+
fov: cameraConfig.fov,
|
|
42147
|
+
near: 0.1,
|
|
42148
|
+
far: 1e3
|
|
42149
|
+
},
|
|
42150
|
+
style: { background: backgroundColor },
|
|
42151
|
+
onClick: (e) => {
|
|
42152
|
+
if (e.target === e.currentTarget) {
|
|
42153
|
+
eventHandlers.handleCanvasClick(e);
|
|
42154
|
+
}
|
|
42155
|
+
},
|
|
42156
|
+
children: [
|
|
42157
|
+
/* @__PURE__ */ jsxRuntime.jsx(CameraController, { onCameraChange: eventHandlers.handleCameraChange }),
|
|
42158
|
+
/* @__PURE__ */ jsxRuntime.jsx("ambientLight", { intensity: 0.6 }),
|
|
42159
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42160
|
+
"directionalLight",
|
|
42161
|
+
{
|
|
42162
|
+
position: [10, 20, 10],
|
|
42163
|
+
intensity: 0.8,
|
|
42164
|
+
castShadow: shadows,
|
|
42165
|
+
"shadow-mapSize": [2048, 2048]
|
|
42166
|
+
}
|
|
42167
|
+
),
|
|
42168
|
+
/* @__PURE__ */ jsxRuntime.jsx("hemisphereLight", { intensity: 0.3, color: "#87ceeb", groundColor: "#362d1d" }),
|
|
42169
|
+
showGrid && /* @__PURE__ */ jsxRuntime.jsx(
|
|
42170
|
+
drei.Grid,
|
|
42171
|
+
{
|
|
42172
|
+
args: [
|
|
42173
|
+
Math.max(gridBounds.maxX - gridBounds.minX + 2, 10),
|
|
42174
|
+
Math.max(gridBounds.maxZ - gridBounds.minZ + 2, 10)
|
|
42175
|
+
],
|
|
42176
|
+
position: [
|
|
42177
|
+
(gridBounds.maxX - gridBounds.minX) / 2 - 0.5,
|
|
42178
|
+
0,
|
|
42179
|
+
(gridBounds.maxZ - gridBounds.minZ) / 2 - 0.5
|
|
42180
|
+
],
|
|
42181
|
+
cellSize: 1,
|
|
42182
|
+
cellThickness: 1,
|
|
42183
|
+
cellColor: "#444444",
|
|
42184
|
+
sectionSize: 5,
|
|
42185
|
+
sectionThickness: 1.5,
|
|
42186
|
+
sectionColor: "#666666",
|
|
42187
|
+
fadeDistance: 50,
|
|
42188
|
+
fadeStrength: 1
|
|
42189
|
+
}
|
|
42190
|
+
),
|
|
42191
|
+
tiles.map((tile, index) => {
|
|
42192
|
+
const position = gridToWorld(
|
|
42193
|
+
tile.x,
|
|
42194
|
+
tile.z ?? tile.y ?? 0,
|
|
42195
|
+
tile.elevation ?? 0
|
|
42196
|
+
);
|
|
42197
|
+
const Renderer = CustomTileRenderer || DefaultTileRenderer;
|
|
42198
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
|
|
42199
|
+
}),
|
|
42200
|
+
features.map((feature, index) => {
|
|
42201
|
+
const position = gridToWorld(
|
|
42202
|
+
feature.x,
|
|
42203
|
+
feature.z ?? feature.y ?? 0,
|
|
42204
|
+
(feature.elevation ?? 0) + 0.5
|
|
42205
|
+
);
|
|
42206
|
+
const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
|
|
42207
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
|
|
42208
|
+
}),
|
|
42209
|
+
units.map((unit) => {
|
|
42210
|
+
const position = gridToWorld(
|
|
42211
|
+
unit.x ?? 0,
|
|
42212
|
+
unit.z ?? unit.y ?? 0,
|
|
42213
|
+
(unit.elevation ?? 0) + 0.5
|
|
42214
|
+
);
|
|
42215
|
+
const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
|
|
42216
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { unit, position }, unit.id);
|
|
42217
|
+
}),
|
|
42218
|
+
children,
|
|
42219
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42220
|
+
drei.OrbitControls,
|
|
42221
|
+
{
|
|
42222
|
+
ref: controlsRef,
|
|
42223
|
+
target: cameraTarget,
|
|
42224
|
+
enableDamping: true,
|
|
42225
|
+
dampingFactor: 0.05,
|
|
42226
|
+
minDistance: 2,
|
|
42227
|
+
maxDistance: 100,
|
|
42228
|
+
maxPolarAngle: Math.PI / 2 - 0.1
|
|
42229
|
+
}
|
|
42230
|
+
)
|
|
42231
|
+
]
|
|
42232
|
+
}
|
|
42233
|
+
),
|
|
42234
|
+
showCoordinates && hoveredTile && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__coordinates", children: [
|
|
42235
|
+
"X: ",
|
|
42236
|
+
hoveredTile.x,
|
|
42237
|
+
", Z: ",
|
|
42238
|
+
hoveredTile.z ?? hoveredTile.y ?? 0
|
|
42239
|
+
] }),
|
|
42240
|
+
showTileInfo && hoveredTile && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__tile-info", children: [
|
|
42241
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "tile-info__type", children: hoveredTile.type }),
|
|
42242
|
+
hoveredTile.terrain && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tile-info__terrain", children: hoveredTile.terrain })
|
|
42243
|
+
] })
|
|
42244
|
+
]
|
|
42245
|
+
}
|
|
42246
|
+
)
|
|
42247
|
+
}
|
|
42248
|
+
);
|
|
42249
|
+
}
|
|
42250
|
+
);
|
|
42251
|
+
GameCanvas3D.displayName = "GameCanvas3D";
|
|
42252
|
+
}
|
|
42253
|
+
});
|
|
42254
|
+
function GameBoard3D({
|
|
42255
|
+
entity,
|
|
42256
|
+
tiles = [],
|
|
42257
|
+
features = [],
|
|
42258
|
+
cameraMode = "perspective",
|
|
42259
|
+
backgroundColor = "#2a1a1a",
|
|
42260
|
+
tileClickEvent,
|
|
42261
|
+
unitClickEvent,
|
|
42262
|
+
attackEvent,
|
|
42263
|
+
endTurnEvent,
|
|
42264
|
+
cancelEvent,
|
|
42265
|
+
playAgainEvent,
|
|
42266
|
+
gameEndEvent,
|
|
42267
|
+
className
|
|
42268
|
+
}) {
|
|
42269
|
+
const row = boardEntity(entity);
|
|
42270
|
+
const eventBus = useEventBus();
|
|
42271
|
+
const entityUnits = row ? rows(row.units) : [];
|
|
42272
|
+
const units = entityUnits;
|
|
42273
|
+
const selectedUnitId = row ? str(row.selectedUnitId) || null : null;
|
|
42274
|
+
const phase = row ? str(row.phase) : "observation";
|
|
42275
|
+
const result = row ? str(row.result) : "none";
|
|
42276
|
+
const validMoves = row && Array.isArray(row.validMoves) ? row.validMoves : [];
|
|
42277
|
+
const attackTargets = row && Array.isArray(row.attackTargets) ? row.attackTargets : [];
|
|
42278
|
+
const turn = row ? num(row.turn) : 0;
|
|
42279
|
+
const currentTeam = row ? str(row.currentTeam) : "player";
|
|
42280
|
+
const isGameOver = result !== "none";
|
|
42281
|
+
const gameEndEmittedRef = React82.useRef(false);
|
|
42282
|
+
React82.useEffect(() => {
|
|
42283
|
+
if ((result === "victory" || result === "defeat") && gameEndEvent) {
|
|
42284
|
+
if (!gameEndEmittedRef.current) {
|
|
42285
|
+
gameEndEmittedRef.current = true;
|
|
42286
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
42287
|
+
}
|
|
42288
|
+
} else {
|
|
42289
|
+
gameEndEmittedRef.current = false;
|
|
42290
|
+
}
|
|
42291
|
+
}, [result, gameEndEvent, eventBus]);
|
|
42292
|
+
const checkGameEnd = React82.useCallback(() => {
|
|
42293
|
+
const alivePlayer = entityUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
|
|
42294
|
+
const aliveEnemy = entityUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
|
|
42295
|
+
if (alivePlayer.length === 0 && gameEndEvent) {
|
|
42296
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result: "defeat" });
|
|
42297
|
+
} else if (aliveEnemy.length === 0 && gameEndEvent) {
|
|
42298
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result: "victory" });
|
|
42299
|
+
}
|
|
42300
|
+
}, [entityUnits, gameEndEvent, eventBus]);
|
|
42301
|
+
const handleUnitClickCallback = React82.useCallback((isoUnit) => {
|
|
42302
|
+
if (phase !== "action" || !selectedUnitId || !attackEvent) return;
|
|
42303
|
+
const attackerRow = entityUnits.find((u) => str(u.id) === selectedUnitId);
|
|
42304
|
+
const targetRow = entityUnits.find((u) => str(u.id) === isoUnit.id);
|
|
42305
|
+
if (!attackerRow || !targetRow) return;
|
|
42306
|
+
if (unitTeam(targetRow) !== "enemy" || unitHealth(targetRow) <= 0) return;
|
|
42307
|
+
const ap = unitPosition(attackerRow);
|
|
42308
|
+
const tp = unitPosition(targetRow);
|
|
42309
|
+
const dx = Math.abs(ap.x - tp.x);
|
|
42310
|
+
const dy = Math.abs(ap.y - tp.y);
|
|
42311
|
+
if (dx <= 1 && dy <= 1 && dx + dy > 0) {
|
|
42312
|
+
const damage = Math.max(1, num(attackerRow.attack) - num(targetRow.defense));
|
|
42313
|
+
eventBus.emit(`UI:${attackEvent}`, {
|
|
42314
|
+
attackerId: str(attackerRow.id),
|
|
42315
|
+
targetId: str(targetRow.id),
|
|
42316
|
+
damage
|
|
42317
|
+
});
|
|
42318
|
+
setTimeout(checkGameEnd, 100);
|
|
42319
|
+
}
|
|
42320
|
+
}, [phase, selectedUnitId, entityUnits, attackEvent, eventBus, checkGameEnd]);
|
|
42321
|
+
const handleEndTurn = React82.useCallback(() => {
|
|
42322
|
+
if (endTurnEvent) eventBus.emit(`UI:${endTurnEvent}`, {});
|
|
42323
|
+
}, [endTurnEvent, eventBus]);
|
|
42324
|
+
const handleCancel = React82.useCallback(() => {
|
|
42325
|
+
if (cancelEvent) eventBus.emit(`UI:${cancelEvent}`, {});
|
|
42326
|
+
}, [cancelEvent, eventBus]);
|
|
42327
|
+
const handlePlayAgain = React82.useCallback(() => {
|
|
42328
|
+
if (playAgainEvent) eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
42329
|
+
}, [playAgainEvent, eventBus]);
|
|
42330
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("game-board-3d block w-full min-h-[85vh] relative", className), children: [
|
|
42331
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-board-3d__status absolute top-3 left-3 z-10 flex gap-2 items-center", children: [
|
|
42332
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "status__phase capitalize", children: phase.replace("_", " ") }),
|
|
42333
|
+
currentTeam && !isGameOver && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", color: "muted", className: "status__team", children: [
|
|
42334
|
+
"\u2014 ",
|
|
42335
|
+
currentTeam === "player" ? "Your Turn" : "Enemy's Turn"
|
|
42336
|
+
] }),
|
|
42337
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", color: "muted", className: "status__turn", children: [
|
|
42338
|
+
"Turn ",
|
|
42339
|
+
turn
|
|
42340
|
+
] })
|
|
42341
|
+
] }),
|
|
42342
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42343
|
+
GameCanvas3D,
|
|
42344
|
+
{
|
|
42345
|
+
tiles,
|
|
42346
|
+
units,
|
|
42347
|
+
features,
|
|
42348
|
+
cameraMode,
|
|
42349
|
+
showGrid: true,
|
|
42350
|
+
showCoordinates: false,
|
|
42351
|
+
showTileInfo: false,
|
|
42352
|
+
shadows: true,
|
|
42353
|
+
backgroundColor,
|
|
42354
|
+
tileClickEvent,
|
|
42355
|
+
unitClickEvent,
|
|
42356
|
+
onUnitClick: handleUnitClickCallback,
|
|
42357
|
+
selectedUnitId,
|
|
42358
|
+
validMoves,
|
|
42359
|
+
attackTargets,
|
|
42360
|
+
className: "game-board-3d__canvas w-full min-h-[85vh]"
|
|
42361
|
+
}
|
|
42362
|
+
),
|
|
42363
|
+
!isGameOver && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "fixed bottom-6 right-6 z-50", gap: "sm", children: [
|
|
42364
|
+
(phase === "selection" || phase === "movement" || phase === "action") && /* @__PURE__ */ jsxRuntime.jsx(
|
|
42365
|
+
Button,
|
|
42366
|
+
{
|
|
42367
|
+
variant: "secondary",
|
|
42368
|
+
className: "shadow-xl",
|
|
42369
|
+
onClick: handleCancel,
|
|
42370
|
+
children: "Cancel"
|
|
42371
|
+
}
|
|
42372
|
+
),
|
|
42373
|
+
phase !== "enemy_turn" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
42374
|
+
Button,
|
|
42375
|
+
{
|
|
42376
|
+
variant: "primary",
|
|
42377
|
+
className: "shadow-xl",
|
|
42378
|
+
onClick: handleEndTurn,
|
|
42379
|
+
children: "End Turn"
|
|
42380
|
+
}
|
|
42381
|
+
)
|
|
42382
|
+
] }),
|
|
42383
|
+
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: [
|
|
42384
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42385
|
+
Typography,
|
|
42386
|
+
{
|
|
42387
|
+
variant: "h2",
|
|
42388
|
+
className: cn(
|
|
42389
|
+
"overlay__result",
|
|
42390
|
+
result === "victory" ? "text-yellow-400" : "text-red-500"
|
|
42391
|
+
),
|
|
42392
|
+
children: result === "victory" ? "Victory!" : "Defeat"
|
|
42393
|
+
}
|
|
42394
|
+
),
|
|
42395
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "body", color: "muted", className: "overlay__turn-count", children: [
|
|
42396
|
+
"Completed in ",
|
|
42397
|
+
turn,
|
|
42398
|
+
" turn",
|
|
42399
|
+
turn !== 1 ? "s" : ""
|
|
42400
|
+
] }),
|
|
42401
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42402
|
+
Button,
|
|
42403
|
+
{
|
|
42404
|
+
variant: "primary",
|
|
42405
|
+
className: "px-8 py-3 font-semibold",
|
|
42406
|
+
onClick: handlePlayAgain,
|
|
42407
|
+
children: "Play Again"
|
|
42408
|
+
}
|
|
42409
|
+
)
|
|
42410
|
+
] }) })
|
|
42411
|
+
] });
|
|
42412
|
+
}
|
|
42413
|
+
var init_GameBoard3D = __esm({
|
|
42414
|
+
"components/game/organisms/GameBoard3D.tsx"() {
|
|
42415
|
+
"use client";
|
|
42416
|
+
init_cn();
|
|
42417
|
+
init_Button();
|
|
42418
|
+
init_Typography();
|
|
42419
|
+
init_Stack();
|
|
42420
|
+
init_useEventBus();
|
|
42421
|
+
init_GameCanvas3D2();
|
|
42422
|
+
init_boardEntity();
|
|
42423
|
+
GameBoard3D.displayName = "GameBoard3D";
|
|
42424
|
+
}
|
|
42425
|
+
});
|
|
40770
42426
|
var GameShell;
|
|
40771
42427
|
var init_GameShell = __esm({
|
|
40772
42428
|
"components/game/templates/GameShell.tsx"() {
|
|
@@ -42309,6 +43965,236 @@ var init_NegotiatorBoard = __esm({
|
|
|
42309
43965
|
NegotiatorBoard.displayName = "NegotiatorBoard";
|
|
42310
43966
|
}
|
|
42311
43967
|
});
|
|
43968
|
+
function resolveEntity(entity) {
|
|
43969
|
+
if (!entity) return {};
|
|
43970
|
+
if (Array.isArray(entity)) return entity[0] ?? {};
|
|
43971
|
+
return entity;
|
|
43972
|
+
}
|
|
43973
|
+
function numField(row, key, fallback) {
|
|
43974
|
+
const v = row[key];
|
|
43975
|
+
return typeof v === "number" ? v : fallback;
|
|
43976
|
+
}
|
|
43977
|
+
function strField(row, key, fallback) {
|
|
43978
|
+
const v = row[key];
|
|
43979
|
+
return typeof v === "string" ? v : fallback;
|
|
43980
|
+
}
|
|
43981
|
+
function PlatformerBoard({
|
|
43982
|
+
entity,
|
|
43983
|
+
player: propPlayer,
|
|
43984
|
+
platforms: propPlatforms,
|
|
43985
|
+
result: propResult,
|
|
43986
|
+
lives: propLives,
|
|
43987
|
+
score: propScore,
|
|
43988
|
+
level: propLevel,
|
|
43989
|
+
worldWidth = 800,
|
|
43990
|
+
worldHeight = 400,
|
|
43991
|
+
canvasWidth = 800,
|
|
43992
|
+
canvasHeight = 400,
|
|
43993
|
+
playerSprite,
|
|
43994
|
+
tileSprites,
|
|
43995
|
+
bgColor,
|
|
43996
|
+
leftEvent = "LEFT",
|
|
43997
|
+
rightEvent = "RIGHT",
|
|
43998
|
+
jumpEvent = "JUMP",
|
|
43999
|
+
stopEvent = "STOP",
|
|
44000
|
+
playAgainEvent = "PLAY_AGAIN",
|
|
44001
|
+
gameEndEvent = "GAME_END",
|
|
44002
|
+
className
|
|
44003
|
+
}) {
|
|
44004
|
+
const row = resolveEntity(entity);
|
|
44005
|
+
const eventBus = useEventBus();
|
|
44006
|
+
const { t } = hooks.useTranslate();
|
|
44007
|
+
const result = propResult ?? strField(row, "result", "none");
|
|
44008
|
+
propLives ?? numField(row, "lives", 3);
|
|
44009
|
+
const score = propScore ?? numField(row, "score", 0);
|
|
44010
|
+
const level = propLevel ?? numField(row, "level", 1);
|
|
44011
|
+
const entityPlayer = row["player"];
|
|
44012
|
+
const player = propPlayer ?? entityPlayer ?? {
|
|
44013
|
+
x: 80,
|
|
44014
|
+
y: 320,
|
|
44015
|
+
width: 32,
|
|
44016
|
+
height: 48,
|
|
44017
|
+
vx: 0,
|
|
44018
|
+
vy: 0,
|
|
44019
|
+
grounded: false,
|
|
44020
|
+
facingRight: true
|
|
44021
|
+
};
|
|
44022
|
+
const entityPlatforms = Array.isArray(row["platforms"]) ? row["platforms"] : void 0;
|
|
44023
|
+
const platforms = propPlatforms ?? entityPlatforms ?? [
|
|
44024
|
+
{ x: 0, y: 368, width: 800, height: 32, type: "ground" },
|
|
44025
|
+
{ x: 150, y: 280, width: 160, height: 16, type: "platform" },
|
|
44026
|
+
{ x: 420, y: 220, width: 160, height: 16, type: "platform" },
|
|
44027
|
+
{ x: 580, y: 300, width: 80, height: 16, type: "hazard" },
|
|
44028
|
+
{ x: 700, y: 340, width: 64, height: 28, type: "goal" }
|
|
44029
|
+
];
|
|
44030
|
+
const handleRestart = React82.useCallback(() => {
|
|
44031
|
+
if (playAgainEvent) {
|
|
44032
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
44033
|
+
}
|
|
44034
|
+
}, [playAgainEvent, eventBus]);
|
|
44035
|
+
const isGameOver = result === "won" || result === "lost";
|
|
44036
|
+
const gameEndEmittedRef = React82.useRef(false);
|
|
44037
|
+
React82.useEffect(() => {
|
|
44038
|
+
if ((result === "won" || result === "lost") && gameEndEvent) {
|
|
44039
|
+
if (!gameEndEmittedRef.current) {
|
|
44040
|
+
gameEndEmittedRef.current = true;
|
|
44041
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
44042
|
+
}
|
|
44043
|
+
} else {
|
|
44044
|
+
gameEndEmittedRef.current = false;
|
|
44045
|
+
}
|
|
44046
|
+
}, [result, gameEndEvent, eventBus]);
|
|
44047
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
44048
|
+
VStack,
|
|
44049
|
+
{
|
|
44050
|
+
className: cn("platformer-board relative bg-background", className),
|
|
44051
|
+
gap: "none",
|
|
44052
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative", children: [
|
|
44053
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44054
|
+
PlatformerCanvas,
|
|
44055
|
+
{
|
|
44056
|
+
player,
|
|
44057
|
+
platforms,
|
|
44058
|
+
worldWidth,
|
|
44059
|
+
worldHeight,
|
|
44060
|
+
canvasWidth,
|
|
44061
|
+
canvasHeight,
|
|
44062
|
+
followCamera: true,
|
|
44063
|
+
bgColor,
|
|
44064
|
+
playerSprite,
|
|
44065
|
+
tileSprites,
|
|
44066
|
+
leftEvent,
|
|
44067
|
+
rightEvent,
|
|
44068
|
+
jumpEvent,
|
|
44069
|
+
stopEvent
|
|
44070
|
+
}
|
|
44071
|
+
),
|
|
44072
|
+
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: [
|
|
44073
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44074
|
+
Typography,
|
|
44075
|
+
{
|
|
44076
|
+
variant: "h2",
|
|
44077
|
+
className: cn(
|
|
44078
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
44079
|
+
result === "won" ? "text-warning" : "text-error"
|
|
44080
|
+
),
|
|
44081
|
+
children: result === "won" ? t("platformer.won") : t("platformer.lost")
|
|
44082
|
+
}
|
|
44083
|
+
),
|
|
44084
|
+
result === "won" && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "body1", className: "text-muted-foreground", children: [
|
|
44085
|
+
t("platformer.level"),
|
|
44086
|
+
" ",
|
|
44087
|
+
level,
|
|
44088
|
+
" \u2014 ",
|
|
44089
|
+
t("platformer.score"),
|
|
44090
|
+
": ",
|
|
44091
|
+
score
|
|
44092
|
+
] }),
|
|
44093
|
+
result === "lost" && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: t("platformer.noLives") }),
|
|
44094
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44095
|
+
Button,
|
|
44096
|
+
{
|
|
44097
|
+
variant: "primary",
|
|
44098
|
+
className: "px-8 py-3 font-semibold",
|
|
44099
|
+
onClick: handleRestart,
|
|
44100
|
+
children: result === "won" ? t("platformer.nextLevel") : t("platformer.playAgain")
|
|
44101
|
+
}
|
|
44102
|
+
)
|
|
44103
|
+
] }) })
|
|
44104
|
+
] })
|
|
44105
|
+
}
|
|
44106
|
+
);
|
|
44107
|
+
}
|
|
44108
|
+
var init_PlatformerBoard = __esm({
|
|
44109
|
+
"components/game/organisms/PlatformerBoard.tsx"() {
|
|
44110
|
+
"use client";
|
|
44111
|
+
init_cn();
|
|
44112
|
+
init_useEventBus();
|
|
44113
|
+
init_Box();
|
|
44114
|
+
init_Button();
|
|
44115
|
+
init_Typography();
|
|
44116
|
+
init_Stack();
|
|
44117
|
+
init_PlatformerCanvas();
|
|
44118
|
+
PlatformerBoard.displayName = "PlatformerBoard";
|
|
44119
|
+
}
|
|
44120
|
+
});
|
|
44121
|
+
function PlatformerTemplate({
|
|
44122
|
+
entity,
|
|
44123
|
+
platforms = DEFAULT_PLATFORMS,
|
|
44124
|
+
worldWidth = 800,
|
|
44125
|
+
worldHeight = 400,
|
|
44126
|
+
canvasWidth = 800,
|
|
44127
|
+
canvasHeight = 400,
|
|
44128
|
+
playerSprite = `${CDN3}/characters/platformChar_idle.png`,
|
|
44129
|
+
tileSprites = DEFAULT_TILE_SPRITES,
|
|
44130
|
+
bgColor = "#5c94fc",
|
|
44131
|
+
gameEndEvent = "GAME_END",
|
|
44132
|
+
className
|
|
44133
|
+
}) {
|
|
44134
|
+
const row = entity && !Array.isArray(entity) ? entity : void 0;
|
|
44135
|
+
const lives = typeof row?.["lives"] === "number" ? row["lives"] : 3;
|
|
44136
|
+
const score = typeof row?.["score"] === "number" ? row["score"] : 0;
|
|
44137
|
+
const level = typeof row?.["level"] === "number" ? row["level"] : 1;
|
|
44138
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("platformer-template w-full", className), gap: "none", children: [
|
|
44139
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
44140
|
+
GameHud,
|
|
44141
|
+
{
|
|
44142
|
+
position: "top",
|
|
44143
|
+
stats: [
|
|
44144
|
+
{ label: "Lives", value: lives, format: "number", variant: "danger" },
|
|
44145
|
+
{ label: "Level", value: level, format: "number", variant: "default" }
|
|
44146
|
+
]
|
|
44147
|
+
}
|
|
44148
|
+
) }),
|
|
44149
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44150
|
+
PlatformerBoard,
|
|
44151
|
+
{
|
|
44152
|
+
entity,
|
|
44153
|
+
platforms,
|
|
44154
|
+
worldWidth,
|
|
44155
|
+
worldHeight,
|
|
44156
|
+
canvasWidth,
|
|
44157
|
+
canvasHeight,
|
|
44158
|
+
playerSprite,
|
|
44159
|
+
tileSprites,
|
|
44160
|
+
bgColor,
|
|
44161
|
+
leftEvent: "LEFT",
|
|
44162
|
+
rightEvent: "RIGHT",
|
|
44163
|
+
jumpEvent: "JUMP",
|
|
44164
|
+
stopEvent: "STOP",
|
|
44165
|
+
playAgainEvent: "PLAY_AGAIN",
|
|
44166
|
+
gameEndEvent
|
|
44167
|
+
}
|
|
44168
|
+
),
|
|
44169
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "px-4 py-2", children: /* @__PURE__ */ jsxRuntime.jsx(ScoreBoard, { score, level }) })
|
|
44170
|
+
] });
|
|
44171
|
+
}
|
|
44172
|
+
var CDN3, DEFAULT_TILE_SPRITES, DEFAULT_PLATFORMS;
|
|
44173
|
+
var init_PlatformerTemplate = __esm({
|
|
44174
|
+
"components/game/templates/PlatformerTemplate.tsx"() {
|
|
44175
|
+
init_cn();
|
|
44176
|
+
init_Box();
|
|
44177
|
+
init_Stack();
|
|
44178
|
+
init_PlatformerBoard();
|
|
44179
|
+
init_GameHud();
|
|
44180
|
+
init_ScoreBoard();
|
|
44181
|
+
CDN3 = "https://almadar-kflow-assets.web.app/shared/platformer";
|
|
44182
|
+
DEFAULT_TILE_SPRITES = {
|
|
44183
|
+
ground: `${CDN3}/tiles/platformPack_tile001.png`,
|
|
44184
|
+
platform: `${CDN3}/tiles/platformPack_tile004.png`,
|
|
44185
|
+
hazard: `${CDN3}/tiles/platformPack_tile017.png`,
|
|
44186
|
+
goal: `${CDN3}/tiles/platformPack_tile007.png`
|
|
44187
|
+
};
|
|
44188
|
+
DEFAULT_PLATFORMS = [
|
|
44189
|
+
{ x: 0, y: 368, width: 800, height: 32, type: "ground" },
|
|
44190
|
+
{ x: 150, y: 280, width: 160, height: 16, type: "platform" },
|
|
44191
|
+
{ x: 420, y: 220, width: 160, height: 16, type: "platform" },
|
|
44192
|
+
{ x: 580, y: 300, width: 80, height: 16, type: "hazard" },
|
|
44193
|
+
{ x: 700, y: 340, width: 64, height: 28, type: "goal" }
|
|
44194
|
+
];
|
|
44195
|
+
PlatformerTemplate.displayName = "PlatformerTemplate";
|
|
44196
|
+
}
|
|
44197
|
+
});
|
|
42312
44198
|
var PricingOrganism;
|
|
42313
44199
|
var init_PricingOrganism = __esm({
|
|
42314
44200
|
"components/marketing/organisms/PricingOrganism.tsx"() {
|
|
@@ -42431,6 +44317,356 @@ var init_PricingPageTemplate = __esm({
|
|
|
42431
44317
|
PricingPageTemplate.displayName = "PricingPageTemplate";
|
|
42432
44318
|
}
|
|
42433
44319
|
});
|
|
44320
|
+
function RoguelikeBoard({
|
|
44321
|
+
entity,
|
|
44322
|
+
tiles: propTiles,
|
|
44323
|
+
enemies: propEnemies,
|
|
44324
|
+
items: propItems,
|
|
44325
|
+
player: propPlayer,
|
|
44326
|
+
playerHp: propPlayerHp,
|
|
44327
|
+
playerMaxHp: propPlayerMaxHp,
|
|
44328
|
+
playerAttack: propPlayerAttack,
|
|
44329
|
+
depth: propDepth,
|
|
44330
|
+
result: propResult,
|
|
44331
|
+
phase: propPhase,
|
|
44332
|
+
stairsX: propStairsX,
|
|
44333
|
+
stairsY: propStairsY,
|
|
44334
|
+
assetManifest: propAssetManifest,
|
|
44335
|
+
scale = 0.45,
|
|
44336
|
+
unitScale = 1,
|
|
44337
|
+
moveEvent,
|
|
44338
|
+
playAgainEvent,
|
|
44339
|
+
gameEndEvent,
|
|
44340
|
+
className
|
|
44341
|
+
}) {
|
|
44342
|
+
const board = boardEntity(entity) ?? {};
|
|
44343
|
+
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : DEFAULT_TILES2);
|
|
44344
|
+
const enemies = propEnemies ?? (Array.isArray(board.enemies) ? board.enemies : DEFAULT_ENEMIES);
|
|
44345
|
+
const items = propItems ?? (Array.isArray(board.items) ? board.items : DEFAULT_ITEMS);
|
|
44346
|
+
const player = propPlayer ?? board.player ?? { x: 1, y: 1 };
|
|
44347
|
+
const playerHp = propPlayerHp ?? num(board.playerHp, 10);
|
|
44348
|
+
const playerMaxHp = propPlayerMaxHp ?? num(board.playerMaxHp, 10);
|
|
44349
|
+
const playerAttack = propPlayerAttack ?? num(board.playerAttack, 3);
|
|
44350
|
+
const depth = propDepth ?? num(board.depth, 1);
|
|
44351
|
+
const result = propResult ?? (str(board.result) || "none");
|
|
44352
|
+
const phase = propPhase ?? (str(board.phase) || "player_turn");
|
|
44353
|
+
propStairsX ?? num(board.stairsX, 3);
|
|
44354
|
+
propStairsY ?? num(board.stairsY, 3);
|
|
44355
|
+
const eventBus = useEventBus();
|
|
44356
|
+
const { t } = hooks.useTranslate();
|
|
44357
|
+
const [hoveredTile, setHoveredTile] = React82.useState(null);
|
|
44358
|
+
const emitMove = React82.useCallback((dx, dy) => {
|
|
44359
|
+
if (!moveEvent) return;
|
|
44360
|
+
eventBus.emit(`UI:${moveEvent}`, { dx, dy });
|
|
44361
|
+
}, [moveEvent, eventBus]);
|
|
44362
|
+
React82.useEffect(() => {
|
|
44363
|
+
const handler = (e) => {
|
|
44364
|
+
if (result !== "none" || phase !== "player_turn") return;
|
|
44365
|
+
if (e.key === "ArrowUp" || e.key === "w") emitMove(0, -1);
|
|
44366
|
+
if (e.key === "ArrowDown" || e.key === "s") emitMove(0, 1);
|
|
44367
|
+
if (e.key === "ArrowLeft" || e.key === "a") emitMove(-1, 0);
|
|
44368
|
+
if (e.key === "ArrowRight" || e.key === "d") emitMove(1, 0);
|
|
44369
|
+
};
|
|
44370
|
+
window.addEventListener("keydown", handler);
|
|
44371
|
+
return () => window.removeEventListener("keydown", handler);
|
|
44372
|
+
}, [result, phase, emitMove]);
|
|
44373
|
+
React82.useEffect(() => {
|
|
44374
|
+
if ((result === "won" || result === "lost") && gameEndEvent) {
|
|
44375
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
44376
|
+
}
|
|
44377
|
+
}, [result, gameEndEvent, eventBus]);
|
|
44378
|
+
const handleTileClick = React82.useCallback((x, y) => {
|
|
44379
|
+
if (result !== "none" || phase !== "player_turn") return;
|
|
44380
|
+
const dx = x - player.x;
|
|
44381
|
+
const dy = y - player.y;
|
|
44382
|
+
if (Math.abs(dx) + Math.abs(dy) === 1) {
|
|
44383
|
+
emitMove(dx, dy);
|
|
44384
|
+
}
|
|
44385
|
+
}, [result, phase, player, emitMove]);
|
|
44386
|
+
const handleReset = React82.useCallback(() => {
|
|
44387
|
+
if (!playAgainEvent) return;
|
|
44388
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
44389
|
+
}, [playAgainEvent, eventBus]);
|
|
44390
|
+
const isoUnits = React82.useMemo(() => {
|
|
44391
|
+
const liveEnemies = enemies.filter((e) => e.hp > 0);
|
|
44392
|
+
return [
|
|
44393
|
+
{
|
|
44394
|
+
id: "player",
|
|
44395
|
+
position: player,
|
|
44396
|
+
name: t("roguelike.player") || "Hero",
|
|
44397
|
+
team: "player",
|
|
44398
|
+
health: playerHp,
|
|
44399
|
+
maxHealth: playerMaxHp,
|
|
44400
|
+
unitType: "player",
|
|
44401
|
+
sprite: `${CDN4}/isometric-dungeon/Characters/Male/Male_0_Idle0.png`
|
|
44402
|
+
},
|
|
44403
|
+
...liveEnemies.map((e) => ({
|
|
44404
|
+
id: e.id,
|
|
44405
|
+
position: { x: e.x, y: e.y },
|
|
44406
|
+
name: t("roguelike.enemy") || "Enemy",
|
|
44407
|
+
team: "enemy",
|
|
44408
|
+
health: e.hp,
|
|
44409
|
+
maxHealth: 6,
|
|
44410
|
+
unitType: "enemy",
|
|
44411
|
+
sprite: `${CDN4}/sprite-sheets/shadow-legion-sprite-sheet-sw.png`
|
|
44412
|
+
}))
|
|
44413
|
+
];
|
|
44414
|
+
}, [player, enemies, playerHp, playerMaxHp, t]);
|
|
44415
|
+
const isoFeatures = React82.useMemo(
|
|
44416
|
+
() => items.map((it) => ({
|
|
44417
|
+
id: it.id,
|
|
44418
|
+
x: it.x,
|
|
44419
|
+
y: it.y,
|
|
44420
|
+
type: it.kind,
|
|
44421
|
+
sprite: FEATURE_SPRITE[it.kind] ?? FEATURE_SPRITE.health_potion
|
|
44422
|
+
})),
|
|
44423
|
+
[items]
|
|
44424
|
+
);
|
|
44425
|
+
const validMoves = React82.useMemo(() => {
|
|
44426
|
+
if (result !== "none" || phase !== "player_turn") return [];
|
|
44427
|
+
const dirs = [{ dx: 0, dy: -1 }, { dx: 0, dy: 1 }, { dx: -1, dy: 0 }, { dx: 1, dy: 0 }];
|
|
44428
|
+
return dirs.map(({ dx, dy }) => ({ x: player.x + dx, y: player.y + dy })).filter(({ x, y }) => {
|
|
44429
|
+
const tile = tiles.find((t2) => t2.x === x && t2.y === y);
|
|
44430
|
+
return tile?.passable ?? false;
|
|
44431
|
+
});
|
|
44432
|
+
}, [result, phase, player, tiles]);
|
|
44433
|
+
const isGameOver = result === "won" || result === "lost";
|
|
44434
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("roguelike-board relative min-h-[600px] bg-background", className), gap: "none", children: [
|
|
44435
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center justify-between px-4 py-2 border-b border-border", gap: "md", children: [
|
|
44436
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
44437
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.depth") || "Depth" }),
|
|
44438
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "font-bold tabular-nums", children: depth })
|
|
44439
|
+
] }),
|
|
44440
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
44441
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.hp") || "HP" }),
|
|
44442
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
44443
|
+
Typography,
|
|
44444
|
+
{
|
|
44445
|
+
variant: "body1",
|
|
44446
|
+
className: cn(
|
|
44447
|
+
"font-bold tabular-nums",
|
|
44448
|
+
playerHp <= Math.floor(playerMaxHp * 0.25) ? "text-error" : "text-success"
|
|
44449
|
+
),
|
|
44450
|
+
children: [
|
|
44451
|
+
playerHp,
|
|
44452
|
+
" / ",
|
|
44453
|
+
playerMaxHp
|
|
44454
|
+
]
|
|
44455
|
+
}
|
|
44456
|
+
)
|
|
44457
|
+
] }),
|
|
44458
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
44459
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.atk") || "ATK" }),
|
|
44460
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "font-bold tabular-nums text-foreground", children: playerAttack })
|
|
44461
|
+
] }),
|
|
44462
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
44463
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.phase") || "Phase" }),
|
|
44464
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "capitalize text-foreground", children: phase.replace("_", " ") })
|
|
44465
|
+
] })
|
|
44466
|
+
] }),
|
|
44467
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "relative flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
44468
|
+
IsometricCanvas_default,
|
|
44469
|
+
{
|
|
44470
|
+
tiles,
|
|
44471
|
+
units: isoUnits,
|
|
44472
|
+
features: isoFeatures,
|
|
44473
|
+
selectedUnitId: null,
|
|
44474
|
+
validMoves,
|
|
44475
|
+
attackTargets: [],
|
|
44476
|
+
hoveredTile,
|
|
44477
|
+
onTileClick: handleTileClick,
|
|
44478
|
+
onUnitClick: () => void 0,
|
|
44479
|
+
onTileHover: (x, y) => setHoveredTile({ x, y }),
|
|
44480
|
+
onTileLeave: () => setHoveredTile(null),
|
|
44481
|
+
scale,
|
|
44482
|
+
assetBaseUrl: propAssetManifest?.baseUrl ?? CDN4,
|
|
44483
|
+
assetManifest: propAssetManifest,
|
|
44484
|
+
unitScale
|
|
44485
|
+
}
|
|
44486
|
+
) }),
|
|
44487
|
+
!isGameOver && /* @__PURE__ */ jsxRuntime.jsx(HStack, { className: "justify-center gap-2 p-3", gap: "none", children: [
|
|
44488
|
+
{ label: "\u2191", dx: 0, dy: -1 },
|
|
44489
|
+
{ label: "\u2193", dx: 0, dy: 1 },
|
|
44490
|
+
{ label: "\u2190", dx: -1, dy: 0 },
|
|
44491
|
+
{ label: "\u2192", dx: 1, dy: 0 }
|
|
44492
|
+
].map(({ label, dx, dy }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
44493
|
+
Button,
|
|
44494
|
+
{
|
|
44495
|
+
variant: "secondary",
|
|
44496
|
+
className: "w-9 h-9 p-0 text-base font-mono",
|
|
44497
|
+
onClick: () => emitMove(dx, dy),
|
|
44498
|
+
disabled: phase !== "player_turn",
|
|
44499
|
+
children: label
|
|
44500
|
+
},
|
|
44501
|
+
label
|
|
44502
|
+
)) }),
|
|
44503
|
+
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: [
|
|
44504
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44505
|
+
Typography,
|
|
44506
|
+
{
|
|
44507
|
+
variant: "h2",
|
|
44508
|
+
className: cn(
|
|
44509
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
44510
|
+
result === "won" ? "text-warning" : "text-error"
|
|
44511
|
+
),
|
|
44512
|
+
children: result === "won" ? t("roguelike.victory") || "Victory!" : t("roguelike.defeat") || "Defeated!"
|
|
44513
|
+
}
|
|
44514
|
+
),
|
|
44515
|
+
result === "won" && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: t("roguelike.cleared") || `Dungeon cleared! Depth reached: ${depth}` }),
|
|
44516
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44517
|
+
Button,
|
|
44518
|
+
{
|
|
44519
|
+
variant: "primary",
|
|
44520
|
+
className: "px-8 py-3 font-semibold",
|
|
44521
|
+
onClick: handleReset,
|
|
44522
|
+
children: t("roguelike.playAgain") || "Play Again"
|
|
44523
|
+
}
|
|
44524
|
+
)
|
|
44525
|
+
] }) })
|
|
44526
|
+
] });
|
|
44527
|
+
}
|
|
44528
|
+
var CDN4, DEFAULT_TILES2, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
|
|
44529
|
+
var init_RoguelikeBoard = __esm({
|
|
44530
|
+
"components/game/organisms/RoguelikeBoard.tsx"() {
|
|
44531
|
+
"use client";
|
|
44532
|
+
init_cn();
|
|
44533
|
+
init_useEventBus();
|
|
44534
|
+
init_Box();
|
|
44535
|
+
init_Button();
|
|
44536
|
+
init_Typography();
|
|
44537
|
+
init_Stack();
|
|
44538
|
+
init_IsometricCanvas();
|
|
44539
|
+
init_boardEntity();
|
|
44540
|
+
CDN4 = "https://almadar-kflow-assets.web.app/shared";
|
|
44541
|
+
DEFAULT_TILES2 = [
|
|
44542
|
+
{ x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44543
|
+
{ x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44544
|
+
{ x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44545
|
+
{ x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44546
|
+
{ x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44547
|
+
{ x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44548
|
+
{ x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44549
|
+
{ x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44550
|
+
{ x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44551
|
+
{ x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44552
|
+
{ x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44553
|
+
{ x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
44554
|
+
{ x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
44555
|
+
{ x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44556
|
+
{ x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44557
|
+
{ x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44558
|
+
{ x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44559
|
+
{ x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
44560
|
+
{ x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` },
|
|
44561
|
+
{ x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44562
|
+
{ x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44563
|
+
{ x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44564
|
+
{ x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44565
|
+
{ x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44566
|
+
{ x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` }
|
|
44567
|
+
];
|
|
44568
|
+
DEFAULT_ENEMIES = [
|
|
44569
|
+
{ id: "e1", x: 3, y: 1, hp: 5, attack: 2 },
|
|
44570
|
+
{ id: "e2", x: 1, y: 3, hp: 4, attack: 1 }
|
|
44571
|
+
];
|
|
44572
|
+
DEFAULT_ITEMS = [
|
|
44573
|
+
{ id: "i1", x: 2, y: 2, kind: "health_potion" }
|
|
44574
|
+
];
|
|
44575
|
+
FEATURE_SPRITE = {
|
|
44576
|
+
health_potion: `${CDN4}/isometric-dungeon/Isometric/chestClosed_E.png`,
|
|
44577
|
+
sword: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`,
|
|
44578
|
+
shield: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`
|
|
44579
|
+
};
|
|
44580
|
+
RoguelikeBoard.displayName = "RoguelikeBoard";
|
|
44581
|
+
}
|
|
44582
|
+
});
|
|
44583
|
+
function RoguelikeTemplate({
|
|
44584
|
+
entity,
|
|
44585
|
+
scale = 0.45,
|
|
44586
|
+
unitScale = 1,
|
|
44587
|
+
tiles = DEFAULT_ROGUELIKE_TILES,
|
|
44588
|
+
enemies,
|
|
44589
|
+
items,
|
|
44590
|
+
stairsX,
|
|
44591
|
+
stairsY,
|
|
44592
|
+
assetManifest = DEFAULT_ROGUELIKE_MANIFEST,
|
|
44593
|
+
gameEndEvent,
|
|
44594
|
+
className
|
|
44595
|
+
}) {
|
|
44596
|
+
const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
|
|
44597
|
+
if (!resolved && !tiles && !assetManifest) return null;
|
|
44598
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
44599
|
+
RoguelikeBoard,
|
|
44600
|
+
{
|
|
44601
|
+
entity: resolved,
|
|
44602
|
+
tiles,
|
|
44603
|
+
enemies,
|
|
44604
|
+
items,
|
|
44605
|
+
stairsX,
|
|
44606
|
+
stairsY,
|
|
44607
|
+
assetManifest,
|
|
44608
|
+
scale,
|
|
44609
|
+
unitScale,
|
|
44610
|
+
moveEvent: "MOVE",
|
|
44611
|
+
playAgainEvent: "PLAY_AGAIN",
|
|
44612
|
+
gameEndEvent: gameEndEvent ?? "GAME_END",
|
|
44613
|
+
className
|
|
44614
|
+
}
|
|
44615
|
+
);
|
|
44616
|
+
}
|
|
44617
|
+
var CDN5, DEFAULT_ROGUELIKE_TILES, DEFAULT_ROGUELIKE_MANIFEST;
|
|
44618
|
+
var init_RoguelikeTemplate = __esm({
|
|
44619
|
+
"components/game/templates/RoguelikeTemplate.tsx"() {
|
|
44620
|
+
init_RoguelikeBoard();
|
|
44621
|
+
CDN5 = "https://almadar-kflow-assets.web.app/shared";
|
|
44622
|
+
DEFAULT_ROGUELIKE_TILES = [
|
|
44623
|
+
{ x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44624
|
+
{ x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44625
|
+
{ x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44626
|
+
{ x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44627
|
+
{ x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44628
|
+
{ x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44629
|
+
{ x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44630
|
+
{ x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44631
|
+
{ x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44632
|
+
{ x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44633
|
+
{ x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44634
|
+
{ x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
44635
|
+
{ x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
44636
|
+
{ x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44637
|
+
{ x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44638
|
+
{ x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44639
|
+
{ x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44640
|
+
{ x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
44641
|
+
{ x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stairs_E.png` },
|
|
44642
|
+
{ x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44643
|
+
{ x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44644
|
+
{ x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44645
|
+
{ x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44646
|
+
{ x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44647
|
+
{ x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` }
|
|
44648
|
+
];
|
|
44649
|
+
DEFAULT_ROGUELIKE_MANIFEST = {
|
|
44650
|
+
baseUrl: CDN5,
|
|
44651
|
+
terrains: {
|
|
44652
|
+
floor: "/isometric-dungeon/Isometric/dirt_E.png",
|
|
44653
|
+
wall: "/isometric-dungeon/Isometric/stoneInset_E.png",
|
|
44654
|
+
stairs: "/isometric-dungeon/Isometric/stairs_E.png",
|
|
44655
|
+
planks: "/isometric-dungeon/Isometric/planks_E.png"
|
|
44656
|
+
},
|
|
44657
|
+
units: {
|
|
44658
|
+
player: "/isometric-dungeon/Characters/Male/Male_0_Idle0.png",
|
|
44659
|
+
enemy: "/sprite-sheets/shadow-legion-sprite-sheet-sw.png"
|
|
44660
|
+
},
|
|
44661
|
+
features: {
|
|
44662
|
+
health_potion: "/isometric-dungeon/Isometric/chestClosed_E.png",
|
|
44663
|
+
sword: "/isometric-dungeon/Isometric/barrel_E.png",
|
|
44664
|
+
shield: "/isometric-dungeon/Isometric/barrel_E.png"
|
|
44665
|
+
}
|
|
44666
|
+
};
|
|
44667
|
+
RoguelikeTemplate.displayName = "RoguelikeTemplate";
|
|
44668
|
+
}
|
|
44669
|
+
});
|
|
42434
44670
|
|
|
42435
44671
|
// lib/traitRegistry.ts
|
|
42436
44672
|
function notifyListeners2() {
|
|
@@ -43684,7 +45920,7 @@ function getAllEvents(traits2) {
|
|
|
43684
45920
|
function EventDispatcherTab({ traits: traits2, schema }) {
|
|
43685
45921
|
const eventBus = useEventBus();
|
|
43686
45922
|
const { t } = hooks.useTranslate();
|
|
43687
|
-
const [
|
|
45923
|
+
const [log14, setLog] = React82__namespace.useState([]);
|
|
43688
45924
|
const prevStatesRef = React82__namespace.useRef(/* @__PURE__ */ new Map());
|
|
43689
45925
|
React82__namespace.useEffect(() => {
|
|
43690
45926
|
for (const trait of traits2) {
|
|
@@ -43748,9 +45984,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
|
|
|
43748
45984
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.otherEvents") }),
|
|
43749
45985
|
/* @__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)) })
|
|
43750
45986
|
] }),
|
|
43751
|
-
|
|
45987
|
+
log14.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
43752
45988
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.recentTransitions") }),
|
|
43753
|
-
/* @__PURE__ */ jsxRuntime.jsx(Stack, { gap: "xs", children:
|
|
45989
|
+
/* @__PURE__ */ jsxRuntime.jsx(Stack, { gap: "xs", children: log14.map((entry, i) => /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
|
|
43754
45990
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-purple-400", children: entry.traitName }),
|
|
43755
45991
|
" ",
|
|
43756
45992
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-500", children: entry.from }),
|
|
@@ -45671,9 +47907,9 @@ function VariablePanel({
|
|
|
45671
47907
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
|
|
45672
47908
|
variables.map((v) => {
|
|
45673
47909
|
const name = v.name == null ? "" : String(v.name);
|
|
45674
|
-
const value =
|
|
45675
|
-
const max =
|
|
45676
|
-
const min =
|
|
47910
|
+
const value = numField2(v.value);
|
|
47911
|
+
const max = numField2(v.max, 100);
|
|
47912
|
+
const min = numField2(v.min, 0);
|
|
45677
47913
|
const unit = v.unit == null ? "" : String(v.unit);
|
|
45678
47914
|
const pct = Math.round((value - min) / (max - min) * 100);
|
|
45679
47915
|
const isHigh = pct > 80;
|
|
@@ -45703,12 +47939,12 @@ function VariablePanel({
|
|
|
45703
47939
|
})
|
|
45704
47940
|
] });
|
|
45705
47941
|
}
|
|
45706
|
-
var
|
|
47942
|
+
var numField2;
|
|
45707
47943
|
var init_VariablePanel = __esm({
|
|
45708
47944
|
"components/game/organisms/puzzles/state-architect/VariablePanel.tsx"() {
|
|
45709
47945
|
init_atoms2();
|
|
45710
47946
|
init_cn();
|
|
45711
|
-
|
|
47947
|
+
numField2 = (v, fallback = 0) => {
|
|
45712
47948
|
const n = Number(v);
|
|
45713
47949
|
return Number.isFinite(n) ? n : fallback;
|
|
45714
47950
|
};
|
|
@@ -46561,6 +48797,289 @@ var init_ToastSlot = __esm({
|
|
|
46561
48797
|
ToastSlot.displayName = "ToastSlot";
|
|
46562
48798
|
}
|
|
46563
48799
|
});
|
|
48800
|
+
function tilesToIso(tiles) {
|
|
48801
|
+
return tiles.map((t) => ({
|
|
48802
|
+
x: t.x,
|
|
48803
|
+
y: t.y,
|
|
48804
|
+
terrain: t.terrain,
|
|
48805
|
+
terrainSprite: t.terrainSprite ?? TERRAIN_SPRITES[t.terrain ?? "ground"],
|
|
48806
|
+
passable: t.passable
|
|
48807
|
+
}));
|
|
48808
|
+
}
|
|
48809
|
+
function towersToUnits(towers) {
|
|
48810
|
+
return towers.map((t) => ({
|
|
48811
|
+
id: t.id,
|
|
48812
|
+
position: { x: t.x, y: t.y },
|
|
48813
|
+
name: "Tower",
|
|
48814
|
+
team: "player",
|
|
48815
|
+
sprite: TOWER_SPRITE,
|
|
48816
|
+
unitType: "guardian",
|
|
48817
|
+
health: 1,
|
|
48818
|
+
maxHealth: 1
|
|
48819
|
+
}));
|
|
48820
|
+
}
|
|
48821
|
+
function creepsToUnits(creeps) {
|
|
48822
|
+
return creeps.map((c) => ({
|
|
48823
|
+
id: c.id,
|
|
48824
|
+
position: { x: c.x, y: c.y },
|
|
48825
|
+
name: "Creep",
|
|
48826
|
+
team: "enemy",
|
|
48827
|
+
sprite: CREEP_SPRITE,
|
|
48828
|
+
unitType: "scrapper",
|
|
48829
|
+
health: c.hp,
|
|
48830
|
+
maxHealth: c.maxHp
|
|
48831
|
+
}));
|
|
48832
|
+
}
|
|
48833
|
+
function pathToFeatures(path) {
|
|
48834
|
+
return path.map((p2, i) => ({
|
|
48835
|
+
id: `path-${i}`,
|
|
48836
|
+
x: p2.x,
|
|
48837
|
+
y: p2.y,
|
|
48838
|
+
type: "path-marker",
|
|
48839
|
+
sprite: `${CDN6}world-map/road_straight.png`
|
|
48840
|
+
}));
|
|
48841
|
+
}
|
|
48842
|
+
function TowerDefenseBoard({
|
|
48843
|
+
entity,
|
|
48844
|
+
tiles: propTiles,
|
|
48845
|
+
path: propPath,
|
|
48846
|
+
towers: propTowers,
|
|
48847
|
+
creeps: propCreeps,
|
|
48848
|
+
gold: propGold,
|
|
48849
|
+
lives: propLives,
|
|
48850
|
+
wave: propWave,
|
|
48851
|
+
maxWaves: propMaxWaves,
|
|
48852
|
+
result: propResult,
|
|
48853
|
+
waveActive: propWaveActive,
|
|
48854
|
+
towerCost = 25,
|
|
48855
|
+
scale = 0.45,
|
|
48856
|
+
placeTowerEvent,
|
|
48857
|
+
startWaveEvent,
|
|
48858
|
+
playAgainEvent,
|
|
48859
|
+
gameEndEvent,
|
|
48860
|
+
className
|
|
48861
|
+
}) {
|
|
48862
|
+
const board = boardEntity(entity) ?? {};
|
|
48863
|
+
const eventBus = useEventBus();
|
|
48864
|
+
const { t } = hooks.useTranslate();
|
|
48865
|
+
const tiles = propTiles ?? rows(board.tiles);
|
|
48866
|
+
const path = propPath ?? rows(board.path);
|
|
48867
|
+
const towers = propTowers ?? rows(board.towers);
|
|
48868
|
+
const creeps = propCreeps ?? rows(board.creeps);
|
|
48869
|
+
const gold = propGold ?? num(board.gold, 100);
|
|
48870
|
+
const lives = propLives ?? num(board.lives, 20);
|
|
48871
|
+
const wave = propWave ?? num(board.wave, 1);
|
|
48872
|
+
const maxWaves = propMaxWaves ?? num(board.maxWaves, 5);
|
|
48873
|
+
const result = propResult ?? (str(board.result) || "none");
|
|
48874
|
+
const waveActive = propWaveActive ?? Boolean(board.waveActive);
|
|
48875
|
+
const [hoveredTile, setHoveredTile] = React82.useState(null);
|
|
48876
|
+
const emittedGameEnd = React82.useRef(false);
|
|
48877
|
+
if (result !== "none" && !emittedGameEnd.current) {
|
|
48878
|
+
emittedGameEnd.current = true;
|
|
48879
|
+
if (gameEndEvent) {
|
|
48880
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
48881
|
+
}
|
|
48882
|
+
}
|
|
48883
|
+
if (result === "none") {
|
|
48884
|
+
emittedGameEnd.current = false;
|
|
48885
|
+
}
|
|
48886
|
+
const towerPositions = React82.useMemo(() => new Set(towers.map((t2) => `${t2.x},${t2.y}`)), [towers]);
|
|
48887
|
+
const pathPositions = React82.useMemo(() => new Set(path.map((p2) => `${p2.x},${p2.y}`)), [path]);
|
|
48888
|
+
const validMoves = React82.useMemo(() => {
|
|
48889
|
+
if (result !== "none" || gold < towerCost) return [];
|
|
48890
|
+
return tiles.filter(
|
|
48891
|
+
(t2) => t2.passable !== false && !towerPositions.has(`${t2.x},${t2.y}`) && !pathPositions.has(`${t2.x},${t2.y}`)
|
|
48892
|
+
).map((t2) => ({ x: t2.x, y: t2.y }));
|
|
48893
|
+
}, [tiles, towerPositions, pathPositions, result, gold, towerCost]);
|
|
48894
|
+
const isoTiles = React82.useMemo(() => tilesToIso(tiles), [tiles]);
|
|
48895
|
+
const towerUnits = React82.useMemo(() => towersToUnits(towers), [towers]);
|
|
48896
|
+
const creepUnits = React82.useMemo(() => creepsToUnits(creeps), [creeps]);
|
|
48897
|
+
const isoUnits = React82.useMemo(() => [...towerUnits, ...creepUnits], [towerUnits, creepUnits]);
|
|
48898
|
+
const pathFeatures = React82.useMemo(() => pathToFeatures(path), [path]);
|
|
48899
|
+
const handleTileClick = React82.useCallback((x, y) => {
|
|
48900
|
+
if (result !== "none") return;
|
|
48901
|
+
if (pathPositions.has(`${x},${y}`)) return;
|
|
48902
|
+
if (towerPositions.has(`${x},${y}`)) return;
|
|
48903
|
+
if (gold < towerCost) return;
|
|
48904
|
+
if (placeTowerEvent) {
|
|
48905
|
+
eventBus.emit(`UI:${placeTowerEvent}`, { x, y });
|
|
48906
|
+
}
|
|
48907
|
+
}, [result, pathPositions, towerPositions, gold, towerCost, placeTowerEvent, eventBus]);
|
|
48908
|
+
const handleStartWave = React82.useCallback(() => {
|
|
48909
|
+
if (startWaveEvent) {
|
|
48910
|
+
eventBus.emit(`UI:${startWaveEvent}`, { wave });
|
|
48911
|
+
}
|
|
48912
|
+
}, [startWaveEvent, wave, eventBus]);
|
|
48913
|
+
const handlePlayAgain = React82.useCallback(() => {
|
|
48914
|
+
if (playAgainEvent) {
|
|
48915
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
48916
|
+
}
|
|
48917
|
+
}, [playAgainEvent, eventBus]);
|
|
48918
|
+
const canPlaceTower = gold >= towerCost && result === "none";
|
|
48919
|
+
const canStartWave = !waveActive && result === "none";
|
|
48920
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("tower-defense-board relative min-h-[600px] bg-background", className), gap: "none", children: [
|
|
48921
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
48922
|
+
HStack,
|
|
48923
|
+
{
|
|
48924
|
+
className: "px-4 py-2 border-b border-border bg-surface",
|
|
48925
|
+
gap: "lg",
|
|
48926
|
+
align: "center",
|
|
48927
|
+
children: [
|
|
48928
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
48929
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "muted", children: t("td.gold") ?? "Gold" }),
|
|
48930
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", weight: "bold", className: "text-warning", children: gold })
|
|
48931
|
+
] }),
|
|
48932
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
48933
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "muted", children: t("td.lives") ?? "Lives" }),
|
|
48934
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", weight: "bold", className: lives <= 5 ? "text-error" : "text-success", children: lives })
|
|
48935
|
+
] }),
|
|
48936
|
+
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
48937
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "muted", children: t("td.wave") ?? "Wave" }),
|
|
48938
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "body2", weight: "bold", children: [
|
|
48939
|
+
wave,
|
|
48940
|
+
" / ",
|
|
48941
|
+
maxWaves
|
|
48942
|
+
] })
|
|
48943
|
+
] }),
|
|
48944
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "ml-auto", children: [
|
|
48945
|
+
canStartWave && /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", size: "sm", onClick: handleStartWave, children: t("td.startWave") ?? "Start Wave" }),
|
|
48946
|
+
waveActive && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "text-warning animate-pulse", children: t("td.waveInProgress") ?? "Wave in progress\u2026" })
|
|
48947
|
+
] })
|
|
48948
|
+
]
|
|
48949
|
+
}
|
|
48950
|
+
),
|
|
48951
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "relative flex-1", children: [
|
|
48952
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
48953
|
+
IsometricCanvas_default,
|
|
48954
|
+
{
|
|
48955
|
+
tiles: isoTiles,
|
|
48956
|
+
units: isoUnits,
|
|
48957
|
+
features: pathFeatures,
|
|
48958
|
+
validMoves,
|
|
48959
|
+
hoveredTile,
|
|
48960
|
+
onTileClick: handleTileClick,
|
|
48961
|
+
onTileHover: (x, y) => setHoveredTile({ x, y }),
|
|
48962
|
+
onTileLeave: () => setHoveredTile(null),
|
|
48963
|
+
scale
|
|
48964
|
+
}
|
|
48965
|
+
),
|
|
48966
|
+
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)` }) })
|
|
48967
|
+
] }),
|
|
48968
|
+
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: [
|
|
48969
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
48970
|
+
Typography,
|
|
48971
|
+
{
|
|
48972
|
+
variant: "h2",
|
|
48973
|
+
className: cn(
|
|
48974
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
48975
|
+
result === "won" ? "text-warning" : "text-error"
|
|
48976
|
+
),
|
|
48977
|
+
children: result === "won" ? t("td.victory") ?? "Victory!" : t("td.defeat") ?? "Defeat!"
|
|
48978
|
+
}
|
|
48979
|
+
),
|
|
48980
|
+
/* @__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." }),
|
|
48981
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
48982
|
+
Button,
|
|
48983
|
+
{
|
|
48984
|
+
variant: "primary",
|
|
48985
|
+
className: "px-8 py-3 font-semibold",
|
|
48986
|
+
onClick: handlePlayAgain,
|
|
48987
|
+
children: t("td.playAgain") ?? "Play Again"
|
|
48988
|
+
}
|
|
48989
|
+
)
|
|
48990
|
+
] }) })
|
|
48991
|
+
] });
|
|
48992
|
+
}
|
|
48993
|
+
var CDN6, TERRAIN_SPRITES, TOWER_SPRITE, CREEP_SPRITE;
|
|
48994
|
+
var init_TowerDefenseBoard = __esm({
|
|
48995
|
+
"components/game/organisms/TowerDefenseBoard.tsx"() {
|
|
48996
|
+
"use client";
|
|
48997
|
+
init_cn();
|
|
48998
|
+
init_useEventBus();
|
|
48999
|
+
init_Box();
|
|
49000
|
+
init_Button();
|
|
49001
|
+
init_Typography();
|
|
49002
|
+
init_Stack();
|
|
49003
|
+
init_IsometricCanvas();
|
|
49004
|
+
init_boardEntity();
|
|
49005
|
+
CDN6 = "https://almadar-kflow-assets.web.app/shared/";
|
|
49006
|
+
TERRAIN_SPRITES = {
|
|
49007
|
+
ground: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_01.png`,
|
|
49008
|
+
path: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_09.png`,
|
|
49009
|
+
wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`
|
|
49010
|
+
};
|
|
49011
|
+
TOWER_SPRITE = `${CDN6}units/guardian.png`;
|
|
49012
|
+
CREEP_SPRITE = `${CDN6}units/scrapper.png`;
|
|
49013
|
+
TowerDefenseBoard.displayName = "TowerDefenseBoard";
|
|
49014
|
+
}
|
|
49015
|
+
});
|
|
49016
|
+
function TowerDefenseTemplate({
|
|
49017
|
+
entity,
|
|
49018
|
+
title = "Tower Defense",
|
|
49019
|
+
tiles,
|
|
49020
|
+
path,
|
|
49021
|
+
towers,
|
|
49022
|
+
gold,
|
|
49023
|
+
lives,
|
|
49024
|
+
maxWaves,
|
|
49025
|
+
towerCost,
|
|
49026
|
+
scale,
|
|
49027
|
+
placeTowerEvent,
|
|
49028
|
+
startWaveEvent,
|
|
49029
|
+
playAgainEvent,
|
|
49030
|
+
gameEndEvent,
|
|
49031
|
+
className
|
|
49032
|
+
}) {
|
|
49033
|
+
const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
|
|
49034
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49035
|
+
Box,
|
|
49036
|
+
{
|
|
49037
|
+
display: "flex",
|
|
49038
|
+
fullHeight: true,
|
|
49039
|
+
className: cn("tower-defense-template flex-col", className),
|
|
49040
|
+
children: [
|
|
49041
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
49042
|
+
HStack,
|
|
49043
|
+
{
|
|
49044
|
+
gap: "sm",
|
|
49045
|
+
align: "center",
|
|
49046
|
+
className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
|
|
49047
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", children: title })
|
|
49048
|
+
}
|
|
49049
|
+
),
|
|
49050
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
49051
|
+
TowerDefenseBoard,
|
|
49052
|
+
{
|
|
49053
|
+
entity: resolved,
|
|
49054
|
+
tiles,
|
|
49055
|
+
path,
|
|
49056
|
+
towers,
|
|
49057
|
+
gold,
|
|
49058
|
+
lives,
|
|
49059
|
+
maxWaves,
|
|
49060
|
+
towerCost,
|
|
49061
|
+
scale,
|
|
49062
|
+
placeTowerEvent,
|
|
49063
|
+
startWaveEvent,
|
|
49064
|
+
playAgainEvent,
|
|
49065
|
+
gameEndEvent,
|
|
49066
|
+
className: "h-full"
|
|
49067
|
+
}
|
|
49068
|
+
) })
|
|
49069
|
+
]
|
|
49070
|
+
}
|
|
49071
|
+
);
|
|
49072
|
+
}
|
|
49073
|
+
var init_TowerDefenseTemplate = __esm({
|
|
49074
|
+
"components/game/templates/TowerDefenseTemplate.tsx"() {
|
|
49075
|
+
init_cn();
|
|
49076
|
+
init_Box();
|
|
49077
|
+
init_Stack();
|
|
49078
|
+
init_Typography();
|
|
49079
|
+
init_TowerDefenseBoard();
|
|
49080
|
+
TowerDefenseTemplate.displayName = "TowerDefenseTemplate";
|
|
49081
|
+
}
|
|
49082
|
+
});
|
|
46564
49083
|
function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
|
|
46565
49084
|
const eventBus = useEventBus();
|
|
46566
49085
|
const {
|
|
@@ -47070,48 +49589,48 @@ function WorldMapTemplate({
|
|
|
47070
49589
|
}
|
|
47071
49590
|
);
|
|
47072
49591
|
}
|
|
47073
|
-
var
|
|
49592
|
+
var CDN7, DEFAULT_WORLDMAP_TILES, DEFAULT_WORLDMAP_UNITS, DEFAULT_WORLDMAP_FEATURES, DEFAULT_WORLDMAP_MANIFEST;
|
|
47074
49593
|
var init_WorldMapTemplate = __esm({
|
|
47075
49594
|
"components/game/templates/WorldMapTemplate.tsx"() {
|
|
47076
49595
|
init_WorldMapBoard();
|
|
47077
|
-
|
|
49596
|
+
CDN7 = "https://almadar-kflow-assets.web.app/shared";
|
|
47078
49597
|
DEFAULT_WORLDMAP_TILES = [
|
|
47079
|
-
{ x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47080
|
-
{ x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47081
|
-
{ x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${
|
|
47082
|
-
{ x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47083
|
-
{ x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47084
|
-
{ x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47085
|
-
{ x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
47086
|
-
{ x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
47087
|
-
{ x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
47088
|
-
{ x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${
|
|
47089
|
-
{ x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${
|
|
47090
|
-
{ x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
47091
|
-
{ x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
47092
|
-
{ x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
47093
|
-
{ x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47094
|
-
{ x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47095
|
-
{ x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
47096
|
-
{ x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
47097
|
-
{ x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
47098
|
-
{ x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47099
|
-
{ x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47100
|
-
{ x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${
|
|
47101
|
-
{ x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${
|
|
47102
|
-
{ x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47103
|
-
{ x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49598
|
+
{ x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49599
|
+
{ x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49600
|
+
{ x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
49601
|
+
{ x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49602
|
+
{ x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49603
|
+
{ x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49604
|
+
{ x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49605
|
+
{ x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49606
|
+
{ x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49607
|
+
{ x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
49608
|
+
{ x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
49609
|
+
{ x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49610
|
+
{ x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
|
|
49611
|
+
{ x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49612
|
+
{ x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49613
|
+
{ x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49614
|
+
{ x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49615
|
+
{ x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49616
|
+
{ x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
|
|
49617
|
+
{ x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49618
|
+
{ x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49619
|
+
{ x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
49620
|
+
{ x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49621
|
+
{ x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49622
|
+
{ x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` }
|
|
47104
49623
|
];
|
|
47105
49624
|
DEFAULT_WORLDMAP_UNITS = [
|
|
47106
|
-
{ id: "h1", position: { x: 1, y: 1 }, unitType: "hero", name: "Amir", team: "player", health: 10, maxHealth: 10, sprite: `${
|
|
47107
|
-
{ id: "h2", position: { x: 3, y: 3 }, unitType: "scout", name: "Archivist", team: "player", health: 10, maxHealth: 10, sprite: `${
|
|
49625
|
+
{ 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` },
|
|
49626
|
+
{ 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` }
|
|
47108
49627
|
];
|
|
47109
49628
|
DEFAULT_WORLDMAP_FEATURES = [
|
|
47110
|
-
{ id: "f1", x: 2, y: 2, type: "capital", sprite: `${
|
|
47111
|
-
{ id: "f2", x: 4, y: 2, type: "power_node", sprite: `${
|
|
49629
|
+
{ id: "f1", x: 2, y: 2, type: "capital", sprite: `${CDN7}/scenes/world/capital.png` },
|
|
49630
|
+
{ id: "f2", x: 4, y: 2, type: "power_node", sprite: `${CDN7}/world-map/power_node.png` }
|
|
47112
49631
|
];
|
|
47113
49632
|
DEFAULT_WORLDMAP_MANIFEST = {
|
|
47114
|
-
baseUrl:
|
|
49633
|
+
baseUrl: CDN7,
|
|
47115
49634
|
terrains: {
|
|
47116
49635
|
grass: "/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png",
|
|
47117
49636
|
water: "/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png",
|
|
@@ -47156,7 +49675,7 @@ function lazyThree(name, loader) {
|
|
|
47156
49675
|
ThreeWrapper.displayName = `Lazy(${name})`;
|
|
47157
49676
|
return ThreeWrapper;
|
|
47158
49677
|
}
|
|
47159
|
-
var ThreeBoundary, FeatureRenderer,
|
|
49678
|
+
var ThreeBoundary, FeatureRenderer, GameCanvas3D2, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
|
|
47160
49679
|
var init_component_registry_generated = __esm({
|
|
47161
49680
|
"components/core/organisms/component-registry.generated.ts"() {
|
|
47162
49681
|
init_AboutPageTemplate();
|
|
@@ -47266,6 +49785,7 @@ var init_component_registry_generated = __esm({
|
|
|
47266
49785
|
init_FormSectionHeader();
|
|
47267
49786
|
init_GameAudioProvider();
|
|
47268
49787
|
init_GameAudioToggle();
|
|
49788
|
+
init_GameBoard3D();
|
|
47269
49789
|
init_GameCanvas2D();
|
|
47270
49790
|
init_GameHud();
|
|
47271
49791
|
init_GameMenu();
|
|
@@ -47323,7 +49843,9 @@ var init_component_registry_generated = __esm({
|
|
|
47323
49843
|
init_PageHeader();
|
|
47324
49844
|
init_Pagination();
|
|
47325
49845
|
init_PatternTile();
|
|
49846
|
+
init_PlatformerBoard();
|
|
47326
49847
|
init_PlatformerCanvas();
|
|
49848
|
+
init_PlatformerTemplate();
|
|
47327
49849
|
init_Popover();
|
|
47328
49850
|
init_PositionedCanvas();
|
|
47329
49851
|
init_PowerupSlots();
|
|
@@ -47347,6 +49869,8 @@ var init_component_registry_generated = __esm({
|
|
|
47347
49869
|
init_ResourceBar();
|
|
47348
49870
|
init_ResourceCounter();
|
|
47349
49871
|
init_RichBlockEditor();
|
|
49872
|
+
init_RoguelikeBoard();
|
|
49873
|
+
init_RoguelikeTemplate();
|
|
47350
49874
|
init_RuntimeDebugger2();
|
|
47351
49875
|
init_ScaledDiagram();
|
|
47352
49876
|
init_ScoreBoard();
|
|
@@ -47419,6 +49943,8 @@ var init_component_registry_generated = __esm({
|
|
|
47419
49943
|
init_Toast();
|
|
47420
49944
|
init_ToastSlot();
|
|
47421
49945
|
init_Tooltip();
|
|
49946
|
+
init_TowerDefenseBoard();
|
|
49947
|
+
init_TowerDefenseTemplate();
|
|
47422
49948
|
init_TraitFrame();
|
|
47423
49949
|
init_TraitSlot();
|
|
47424
49950
|
init_TrendIndicator();
|
|
@@ -47463,7 +49989,7 @@ var init_component_registry_generated = __esm({
|
|
|
47463
49989
|
}
|
|
47464
49990
|
};
|
|
47465
49991
|
FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
|
|
47466
|
-
|
|
49992
|
+
GameCanvas3D2 = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
|
|
47467
49993
|
GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
47468
49994
|
GameCanvas3DCastleTemplate = lazyThree("GameCanvas3DCastleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
47469
49995
|
GameCanvas3DWorldMapTemplate = lazyThree("GameCanvas3DWorldMapTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
@@ -47582,8 +50108,9 @@ var init_component_registry_generated = __esm({
|
|
|
47582
50108
|
"FormSectionHeader": FormSectionHeader,
|
|
47583
50109
|
"GameAudioProvider": GameAudioProvider,
|
|
47584
50110
|
"GameAudioToggle": GameAudioToggle,
|
|
50111
|
+
"GameBoard3D": GameBoard3D,
|
|
47585
50112
|
"GameCanvas2D": GameCanvas2D,
|
|
47586
|
-
"GameCanvas3D":
|
|
50113
|
+
"GameCanvas3D": GameCanvas3D2,
|
|
47587
50114
|
"GameCanvas3DBattleTemplate": GameCanvas3DBattleTemplate,
|
|
47588
50115
|
"GameCanvas3DCastleTemplate": GameCanvas3DCastleTemplate,
|
|
47589
50116
|
"GameCanvas3DWorldMapTemplate": GameCanvas3DWorldMapTemplate,
|
|
@@ -47645,7 +50172,9 @@ var init_component_registry_generated = __esm({
|
|
|
47645
50172
|
"PageHeader": PageHeader,
|
|
47646
50173
|
"Pagination": Pagination,
|
|
47647
50174
|
"PatternTile": PatternTile,
|
|
50175
|
+
"PlatformerBoard": PlatformerBoard,
|
|
47648
50176
|
"PlatformerCanvas": PlatformerCanvas,
|
|
50177
|
+
"PlatformerTemplate": PlatformerTemplate,
|
|
47649
50178
|
"Popover": Popover,
|
|
47650
50179
|
"PositionedCanvas": PositionedCanvas,
|
|
47651
50180
|
"PowerupSlots": PowerupSlots,
|
|
@@ -47669,6 +50198,8 @@ var init_component_registry_generated = __esm({
|
|
|
47669
50198
|
"ResourceBar": ResourceBar,
|
|
47670
50199
|
"ResourceCounter": ResourceCounter,
|
|
47671
50200
|
"RichBlockEditor": RichBlockEditor,
|
|
50201
|
+
"RoguelikeBoard": RoguelikeBoard,
|
|
50202
|
+
"RoguelikeTemplate": RoguelikeTemplate,
|
|
47672
50203
|
"RuntimeDebugger": RuntimeDebugger,
|
|
47673
50204
|
"ScaledDiagram": ScaledDiagram,
|
|
47674
50205
|
"ScoreBoard": ScoreBoard,
|
|
@@ -47744,6 +50275,8 @@ var init_component_registry_generated = __esm({
|
|
|
47744
50275
|
"Toast": Toast,
|
|
47745
50276
|
"ToastSlot": ToastSlot,
|
|
47746
50277
|
"Tooltip": Tooltip,
|
|
50278
|
+
"TowerDefenseBoard": TowerDefenseBoard,
|
|
50279
|
+
"TowerDefenseTemplate": TowerDefenseTemplate,
|
|
47747
50280
|
"TraitFrame": TraitFrame,
|
|
47748
50281
|
"TraitSlot": TraitSlot,
|
|
47749
50282
|
"TrendIndicator": TrendIndicator,
|
|
@@ -49640,7 +52173,7 @@ function clearSchemaCache() {
|
|
|
49640
52173
|
|
|
49641
52174
|
// runtime/index.ts
|
|
49642
52175
|
init_EntitySchemaContext();
|
|
49643
|
-
var
|
|
52176
|
+
var log12 = logger.createLogger("almadar:ui:trait-provider");
|
|
49644
52177
|
var TraitContext = React82.createContext(null);
|
|
49645
52178
|
function TraitProvider({
|
|
49646
52179
|
traits: traitBindings,
|
|
@@ -49657,7 +52190,7 @@ function TraitProvider({
|
|
|
49657
52190
|
currentState: stateName,
|
|
49658
52191
|
availableEvents: trait.transitions.filter((t) => t.from === stateName).map((t) => t.event),
|
|
49659
52192
|
dispatch: (eventKey, payload) => {
|
|
49660
|
-
|
|
52193
|
+
log12.debug("Dispatch", () => ({
|
|
49661
52194
|
trait: trait.name,
|
|
49662
52195
|
event: eventKey,
|
|
49663
52196
|
payloadKeys: payload ? Object.keys(payload) : []
|
|
@@ -50241,7 +52774,7 @@ OrbitalThemeProvider.displayName = "OrbitalThemeProvider";
|
|
|
50241
52774
|
init_navigation();
|
|
50242
52775
|
init_verificationRegistry();
|
|
50243
52776
|
var PERF_NAMESPACE = "almadar:perf:canvas";
|
|
50244
|
-
var
|
|
52777
|
+
var log13 = logger.createLogger(PERF_NAMESPACE);
|
|
50245
52778
|
var RING_SIZE = 50;
|
|
50246
52779
|
var ring = [];
|
|
50247
52780
|
var writeIdx = 0;
|
|
@@ -50296,7 +52829,7 @@ function perfEnd(name, startToken, detail) {
|
|
|
50296
52829
|
}
|
|
50297
52830
|
}
|
|
50298
52831
|
push({ name, durationMs, ts: endTs, detail });
|
|
50299
|
-
|
|
52832
|
+
log13.debug(name, () => ({ durationMs, ...detail ?? {} }));
|
|
50300
52833
|
}
|
|
50301
52834
|
function perfTime(name, fn, detail) {
|
|
50302
52835
|
const t = perfStart(name);
|
|
@@ -50314,7 +52847,7 @@ var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, com
|
|
|
50314
52847
|
ts: commitTime,
|
|
50315
52848
|
detail: { baseDuration }
|
|
50316
52849
|
});
|
|
50317
|
-
|
|
52850
|
+
log13.debug(`profiler:${id}:${phase}`, () => ({ actualDuration, baseDuration }));
|
|
50318
52851
|
};
|
|
50319
52852
|
function getPerfSnapshot() {
|
|
50320
52853
|
if (ring.length < RING_SIZE) return ring.slice();
|