@almadar/ui 5.47.0 → 5.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/avl/index.cjs +2998 -443
- package/dist/avl/index.css +504 -0
- package/dist/avl/index.js +2998 -444
- package/dist/components/core/templates/index.d.ts +3 -0
- package/dist/components/game/atoms/ActionButton.d.ts +4 -1
- package/dist/components/game/atoms/ComboCounter.d.ts +4 -1
- package/dist/components/game/atoms/ControlButton.d.ts +4 -2
- package/dist/components/game/atoms/DamageNumber.d.ts +4 -1
- package/dist/components/game/atoms/ResourceCounter.d.ts +4 -1
- package/dist/components/game/atoms/ScoreDisplay.d.ts +4 -1
- package/dist/components/game/atoms/StateIndicator.d.ts +4 -7
- package/dist/components/game/atoms/StatusEffect.d.ts +4 -1
- package/dist/components/game/atoms/TurnIndicator.d.ts +4 -1
- package/dist/components/game/atoms/WaypointMarker.d.ts +4 -1
- package/dist/components/game/molecules/EnemyPlate.d.ts +4 -1
- package/dist/components/game/molecules/StatBadge.d.ts +4 -1
- package/dist/components/game/molecules/three/index.cjs +1428 -346
- package/dist/components/game/molecules/three/index.css +0 -5
- package/dist/components/game/molecules/three/index.js +1429 -347
- package/dist/components/game/organisms/CastleBoard.d.ts +10 -1
- package/dist/components/game/organisms/GameBoard3D.d.ts +62 -0
- package/dist/components/game/organisms/PlatformerBoard.d.ts +51 -0
- package/dist/components/game/organisms/RoguelikeBoard.d.ts +59 -0
- package/dist/components/game/organisms/TowerDefenseBoard.d.ts +64 -0
- package/dist/components/game/organisms/index.d.ts +4 -0
- package/dist/components/game/organisms/puzzles/event-handler/EventHandlerBoard.d.ts +9 -1
- package/dist/components/game/organisms/puzzles/state-architect/StateArchitectBoard.d.ts +14 -1
- package/dist/components/game/templates/GameCanvas3DBattleTemplate.d.ts +22 -35
- package/dist/components/game/templates/GameCanvas3DCastleTemplate.d.ts +23 -23
- package/dist/components/game/templates/GameCanvas3DWorldMapTemplate.d.ts +27 -29
- package/dist/components/game/templates/PlatformerTemplate.d.ts +26 -0
- package/dist/components/game/templates/RoguelikeTemplate.d.ts +23 -0
- package/dist/components/game/templates/TowerDefenseTemplate.d.ts +43 -0
- package/dist/components/index.cjs +2991 -422
- package/dist/components/index.css +504 -0
- package/dist/components/index.js +2985 -424
- package/dist/docs/index.css +504 -0
- package/dist/providers/index.cjs +2981 -426
- package/dist/providers/index.css +504 -0
- package/dist/providers/index.js +2981 -427
- package/dist/runtime/index.cjs +2982 -427
- package/dist/runtime/index.css +504 -0
- package/dist/runtime/index.js +2982 -428
- package/package.json +1 -1
- package/tailwind-preset.cjs +1 -0
package/dist/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,6 +7352,7 @@ var init_Sprite = __esm({
|
|
|
7324
7352
|
}
|
|
7325
7353
|
});
|
|
7326
7354
|
function StateIndicator({
|
|
7355
|
+
assetUrl = DEFAULT_ASSET_URL3,
|
|
7327
7356
|
state = "idle",
|
|
7328
7357
|
label,
|
|
7329
7358
|
size = "md",
|
|
@@ -7346,18 +7375,29 @@ function StateIndicator({
|
|
|
7346
7375
|
className
|
|
7347
7376
|
),
|
|
7348
7377
|
children: [
|
|
7349
|
-
/* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", children:
|
|
7378
|
+
/* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7379
|
+
"img",
|
|
7380
|
+
{
|
|
7381
|
+
src: assetUrl,
|
|
7382
|
+
alt: displayLabel,
|
|
7383
|
+
width: 16,
|
|
7384
|
+
height: 16,
|
|
7385
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7386
|
+
className: "flex-shrink-0"
|
|
7387
|
+
}
|
|
7388
|
+
) : typeof config.icon === "string" ? /^[a-zA-Z0-9-]+$/.test(config.icon) ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: config.icon }) : config.icon : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: config.icon }) }),
|
|
7350
7389
|
/* @__PURE__ */ jsxRuntime.jsx(Box, { as: "span", children: displayLabel })
|
|
7351
7390
|
]
|
|
7352
7391
|
}
|
|
7353
7392
|
);
|
|
7354
7393
|
}
|
|
7355
|
-
var DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
7394
|
+
var DEFAULT_ASSET_URL3, DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
7356
7395
|
var init_StateIndicator = __esm({
|
|
7357
7396
|
"components/game/atoms/StateIndicator.tsx"() {
|
|
7358
7397
|
init_Box();
|
|
7359
7398
|
init_Icon();
|
|
7360
7399
|
init_cn();
|
|
7400
|
+
DEFAULT_ASSET_URL3 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
7361
7401
|
DEFAULT_STATE_STYLES = {
|
|
7362
7402
|
idle: { icon: "\u23F8", bgClass: "bg-muted" },
|
|
7363
7403
|
active: { icon: "\u25B6", bgClass: "bg-success" },
|
|
@@ -7432,6 +7472,7 @@ var init_TimerDisplay = __esm({
|
|
|
7432
7472
|
}
|
|
7433
7473
|
});
|
|
7434
7474
|
function ResourceCounter({
|
|
7475
|
+
assetUrl = DEFAULT_ASSET_URL4,
|
|
7435
7476
|
icon,
|
|
7436
7477
|
label = "Gold",
|
|
7437
7478
|
value = 250,
|
|
@@ -7451,7 +7492,17 @@ function ResourceCounter({
|
|
|
7451
7492
|
className
|
|
7452
7493
|
),
|
|
7453
7494
|
children: [
|
|
7454
|
-
|
|
7495
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7496
|
+
"img",
|
|
7497
|
+
{
|
|
7498
|
+
src: assetUrl,
|
|
7499
|
+
alt: label,
|
|
7500
|
+
width: sizes.img,
|
|
7501
|
+
height: sizes.img,
|
|
7502
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7503
|
+
className: "flex-shrink-0"
|
|
7504
|
+
}
|
|
7505
|
+
) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon }) }) : null,
|
|
7455
7506
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: label }),
|
|
7456
7507
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("font-bold tabular-nums", color && (color in colorTokenClasses2 ? colorTokenClasses2[color] : color)), children: [
|
|
7457
7508
|
value,
|
|
@@ -7464,7 +7515,7 @@ function ResourceCounter({
|
|
|
7464
7515
|
}
|
|
7465
7516
|
);
|
|
7466
7517
|
}
|
|
7467
|
-
var colorTokenClasses2, sizeMap5;
|
|
7518
|
+
var colorTokenClasses2, DEFAULT_ASSET_URL4, sizeMap5;
|
|
7468
7519
|
var init_ResourceCounter = __esm({
|
|
7469
7520
|
"components/game/atoms/ResourceCounter.tsx"() {
|
|
7470
7521
|
init_cn();
|
|
@@ -7477,16 +7528,17 @@ var init_ResourceCounter = __esm({
|
|
|
7477
7528
|
error: "text-error",
|
|
7478
7529
|
muted: "text-muted-foreground"
|
|
7479
7530
|
};
|
|
7531
|
+
DEFAULT_ASSET_URL4 = "https://almadar-kflow-assets.web.app/shared/world-map/gold_mine.png";
|
|
7480
7532
|
sizeMap5 = {
|
|
7481
|
-
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm" },
|
|
7482
|
-
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base" },
|
|
7483
|
-
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg" }
|
|
7533
|
+
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm", img: 16 },
|
|
7534
|
+
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base", img: 20 },
|
|
7535
|
+
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg", img: 28 }
|
|
7484
7536
|
};
|
|
7485
7537
|
ResourceCounter.displayName = "ResourceCounter";
|
|
7486
7538
|
}
|
|
7487
7539
|
});
|
|
7488
7540
|
function ItemSlot({
|
|
7489
|
-
assetUrl =
|
|
7541
|
+
assetUrl = DEFAULT_ASSET_URL5,
|
|
7490
7542
|
icon = "sword",
|
|
7491
7543
|
label = "Iron Sword",
|
|
7492
7544
|
quantity,
|
|
@@ -7545,7 +7597,7 @@ function ItemSlot({
|
|
|
7545
7597
|
}
|
|
7546
7598
|
);
|
|
7547
7599
|
}
|
|
7548
|
-
var sizeMap6, rarityBorderMap, rarityGlowMap,
|
|
7600
|
+
var sizeMap6, rarityBorderMap, rarityGlowMap, DEFAULT_ASSET_URL5, assetSizeMap;
|
|
7549
7601
|
var init_ItemSlot = __esm({
|
|
7550
7602
|
"components/game/atoms/ItemSlot.tsx"() {
|
|
7551
7603
|
"use client";
|
|
@@ -7570,7 +7622,7 @@ var init_ItemSlot = __esm({
|
|
|
7570
7622
|
epic: "shadow-lg",
|
|
7571
7623
|
legendary: "shadow-lg"
|
|
7572
7624
|
};
|
|
7573
|
-
|
|
7625
|
+
DEFAULT_ASSET_URL5 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
7574
7626
|
assetSizeMap = {
|
|
7575
7627
|
sm: 28,
|
|
7576
7628
|
md: 40,
|
|
@@ -7580,6 +7632,7 @@ var init_ItemSlot = __esm({
|
|
|
7580
7632
|
}
|
|
7581
7633
|
});
|
|
7582
7634
|
function TurnIndicator({
|
|
7635
|
+
assetUrl = DEFAULT_ASSET_URL6,
|
|
7583
7636
|
currentTurn = 1,
|
|
7584
7637
|
maxTurns,
|
|
7585
7638
|
activeTeam,
|
|
@@ -7612,14 +7665,24 @@ function TurnIndicator({
|
|
|
7612
7665
|
] }),
|
|
7613
7666
|
activeTeam && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7614
7667
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "|" }),
|
|
7615
|
-
/* @__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) }),
|
|
7616
7679
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-success", children: activeTeam })
|
|
7617
7680
|
] })
|
|
7618
7681
|
]
|
|
7619
7682
|
}
|
|
7620
7683
|
);
|
|
7621
7684
|
}
|
|
7622
|
-
var sizeMap7;
|
|
7685
|
+
var sizeMap7, DEFAULT_ASSET_URL6;
|
|
7623
7686
|
var init_TurnIndicator = __esm({
|
|
7624
7687
|
"components/game/atoms/TurnIndicator.tsx"() {
|
|
7625
7688
|
init_cn();
|
|
@@ -7628,6 +7691,7 @@ var init_TurnIndicator = __esm({
|
|
|
7628
7691
|
md: { wrapper: "text-sm gap-2 px-3 py-1", dot: "w-2 h-2" },
|
|
7629
7692
|
lg: { wrapper: "text-base gap-2.5 px-4 py-1.5", dot: "w-2.5 h-2.5" }
|
|
7630
7693
|
};
|
|
7694
|
+
DEFAULT_ASSET_URL6 = "https://almadar-kflow-assets.web.app/shared/effects/particles/symbol_01.png";
|
|
7631
7695
|
TurnIndicator.displayName = "TurnIndicator";
|
|
7632
7696
|
}
|
|
7633
7697
|
});
|
|
@@ -7643,6 +7707,7 @@ function getComboScale(combo) {
|
|
|
7643
7707
|
return "";
|
|
7644
7708
|
}
|
|
7645
7709
|
function ComboCounter({
|
|
7710
|
+
assetUrl = DEFAULT_ASSET_URL7,
|
|
7646
7711
|
combo = 5,
|
|
7647
7712
|
multiplier,
|
|
7648
7713
|
streak,
|
|
@@ -7662,6 +7727,17 @@ function ComboCounter({
|
|
|
7662
7727
|
className
|
|
7663
7728
|
),
|
|
7664
7729
|
children: [
|
|
7730
|
+
assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7731
|
+
"img",
|
|
7732
|
+
{
|
|
7733
|
+
src: assetUrl,
|
|
7734
|
+
alt: "combo",
|
|
7735
|
+
width: 24,
|
|
7736
|
+
height: 24,
|
|
7737
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7738
|
+
className: "flex-shrink-0 mb-0.5"
|
|
7739
|
+
}
|
|
7740
|
+
),
|
|
7665
7741
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-black tabular-nums leading-none", sizes.combo, getComboIntensity(combo)), children: combo }),
|
|
7666
7742
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-bold uppercase tracking-wider text-muted-foreground", sizes.label), children: "combo" }),
|
|
7667
7743
|
multiplier != null && multiplier > 1 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("font-bold text-warning tabular-nums", sizes.multiplier), children: [
|
|
@@ -7676,10 +7752,11 @@ function ComboCounter({
|
|
|
7676
7752
|
}
|
|
7677
7753
|
);
|
|
7678
7754
|
}
|
|
7679
|
-
var sizeMap8;
|
|
7755
|
+
var DEFAULT_ASSET_URL7, sizeMap8;
|
|
7680
7756
|
var init_ComboCounter = __esm({
|
|
7681
7757
|
"components/game/atoms/ComboCounter.tsx"() {
|
|
7682
7758
|
init_cn();
|
|
7759
|
+
DEFAULT_ASSET_URL7 = "https://almadar-kflow-assets.web.app/shared/effects/flash/flash00.png";
|
|
7683
7760
|
sizeMap8 = {
|
|
7684
7761
|
sm: { combo: "text-lg", label: "text-xs", multiplier: "text-xs" },
|
|
7685
7762
|
md: { combo: "text-2xl", label: "text-xs", multiplier: "text-sm" },
|
|
@@ -7773,6 +7850,7 @@ var init_XPBar = __esm({
|
|
|
7773
7850
|
}
|
|
7774
7851
|
});
|
|
7775
7852
|
function WaypointMarker({
|
|
7853
|
+
assetUrl = DEFAULT_ASSET_URL8,
|
|
7776
7854
|
label,
|
|
7777
7855
|
icon,
|
|
7778
7856
|
active = true,
|
|
@@ -7811,7 +7889,17 @@ function WaypointMarker({
|
|
|
7811
7889
|
active && !completed && "bg-info text-foreground",
|
|
7812
7890
|
!active && !completed && "bg-muted"
|
|
7813
7891
|
),
|
|
7814
|
-
children: completed ? checkIcon :
|
|
7892
|
+
children: completed ? checkIcon : assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7893
|
+
"img",
|
|
7894
|
+
{
|
|
7895
|
+
src: assetUrl,
|
|
7896
|
+
alt: label,
|
|
7897
|
+
width: sizes.img,
|
|
7898
|
+
height: sizes.img,
|
|
7899
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7900
|
+
className: "flex-shrink-0"
|
|
7901
|
+
}
|
|
7902
|
+
) : icon ? typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon }) : null
|
|
7815
7903
|
}
|
|
7816
7904
|
)
|
|
7817
7905
|
] }),
|
|
@@ -7828,15 +7916,16 @@ function WaypointMarker({
|
|
|
7828
7916
|
)
|
|
7829
7917
|
] });
|
|
7830
7918
|
}
|
|
7831
|
-
var sizeMap10, checkIcon;
|
|
7919
|
+
var DEFAULT_ASSET_URL8, sizeMap10, checkIcon;
|
|
7832
7920
|
var init_WaypointMarker = __esm({
|
|
7833
7921
|
"components/game/atoms/WaypointMarker.tsx"() {
|
|
7834
7922
|
init_cn();
|
|
7835
7923
|
init_Icon();
|
|
7924
|
+
DEFAULT_ASSET_URL8 = "https://almadar-kflow-assets.web.app/shared/world-map/battle_marker.png";
|
|
7836
7925
|
sizeMap10 = {
|
|
7837
|
-
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1" },
|
|
7838
|
-
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5" },
|
|
7839
|
-
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2" }
|
|
7926
|
+
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1", img: 12 },
|
|
7927
|
+
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5", img: 18 },
|
|
7928
|
+
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2", img: 24 }
|
|
7840
7929
|
};
|
|
7841
7930
|
checkIcon = /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 3, className: "w-3/5 h-3/5", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 13l4 4L19 7", strokeLinecap: "round", strokeLinejoin: "round" }) });
|
|
7842
7931
|
WaypointMarker.displayName = "WaypointMarker";
|
|
@@ -7849,6 +7938,7 @@ function formatDuration(seconds) {
|
|
|
7849
7938
|
return `${Math.round(seconds)}s`;
|
|
7850
7939
|
}
|
|
7851
7940
|
function StatusEffect({
|
|
7941
|
+
assetUrl = DEFAULT_ASSET_URL9,
|
|
7852
7942
|
icon = "shield",
|
|
7853
7943
|
label = "Shield",
|
|
7854
7944
|
duration = 30,
|
|
@@ -7869,7 +7959,17 @@ function StatusEffect({
|
|
|
7869
7959
|
),
|
|
7870
7960
|
title: label,
|
|
7871
7961
|
children: [
|
|
7872
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children:
|
|
7962
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7963
|
+
"img",
|
|
7964
|
+
{
|
|
7965
|
+
src: assetUrl,
|
|
7966
|
+
alt: label,
|
|
7967
|
+
width: sizes.img,
|
|
7968
|
+
height: sizes.img,
|
|
7969
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7970
|
+
className: "flex-shrink-0"
|
|
7971
|
+
}
|
|
7972
|
+
) : typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, size: "sm" }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon, size: "sm" }) }),
|
|
7873
7973
|
duration !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7874
7974
|
"span",
|
|
7875
7975
|
{
|
|
@@ -7896,15 +7996,16 @@ function StatusEffect({
|
|
|
7896
7996
|
label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground mt-0.5 text-center whitespace-nowrap", children: label })
|
|
7897
7997
|
] });
|
|
7898
7998
|
}
|
|
7899
|
-
var sizeMap11, variantStyles7;
|
|
7999
|
+
var DEFAULT_ASSET_URL9, sizeMap11, variantStyles7;
|
|
7900
8000
|
var init_StatusEffect = __esm({
|
|
7901
8001
|
"components/game/atoms/StatusEffect.tsx"() {
|
|
7902
8002
|
init_cn();
|
|
7903
8003
|
init_Icon();
|
|
8004
|
+
DEFAULT_ASSET_URL9 = "https://almadar-kflow-assets.web.app/shared/effects/particles/flame_01.png";
|
|
7904
8005
|
sizeMap11 = {
|
|
7905
|
-
sm: { container: "w-8 h-8", icon: "text-sm", badge: "text-xs -top-1 -right-1 w-4 h-4", timer: "text-[9px]" },
|
|
7906
|
-
md: { container: "w-10 h-10", icon: "text-base", badge: "text-xs -top-1 -right-1 w-5 h-5", timer: "text-xs" },
|
|
7907
|
-
lg: { container: "w-12 h-12", icon: "text-lg", badge: "text-sm -top-1.5 -right-1.5 w-6 h-6", timer: "text-xs" }
|
|
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 },
|
|
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 },
|
|
8008
|
+
lg: { container: "w-12 h-12", icon: "text-lg", badge: "text-sm -top-1.5 -right-1.5 w-6 h-6", timer: "text-xs", img: 36 }
|
|
7908
8009
|
};
|
|
7909
8010
|
variantStyles7 = {
|
|
7910
8011
|
buff: "border-success bg-success/20",
|
|
@@ -7915,6 +8016,7 @@ var init_StatusEffect = __esm({
|
|
|
7915
8016
|
}
|
|
7916
8017
|
});
|
|
7917
8018
|
function DamageNumber({
|
|
8019
|
+
assetUrl = DEFAULT_ASSET_URL10,
|
|
7918
8020
|
value = 42,
|
|
7919
8021
|
type = "damage",
|
|
7920
8022
|
size = "md",
|
|
@@ -7923,22 +8025,35 @@ function DamageNumber({
|
|
|
7923
8025
|
const displayText = type === "miss" ? "MISS" : type === "heal" ? `+${value}` : `${value}`;
|
|
7924
8026
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7925
8027
|
/* @__PURE__ */ jsxRuntime.jsx("style", { children: floatKeyframes }),
|
|
7926
|
-
/* @__PURE__ */ jsxRuntime.
|
|
8028
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
7927
8029
|
"span",
|
|
7928
8030
|
{
|
|
7929
8031
|
className: cn(
|
|
7930
|
-
"inline-
|
|
8032
|
+
"inline-flex items-center gap-0.5 font-mono select-none pointer-events-none",
|
|
7931
8033
|
sizeMap12[size],
|
|
7932
8034
|
typeStyles[type],
|
|
7933
8035
|
className
|
|
7934
8036
|
),
|
|
7935
8037
|
style: { animation: "damageFloat 1s ease-out forwards" },
|
|
7936
|
-
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
|
+
]
|
|
7937
8052
|
}
|
|
7938
8053
|
)
|
|
7939
8054
|
] });
|
|
7940
8055
|
}
|
|
7941
|
-
var sizeMap12, typeStyles, floatKeyframes;
|
|
8056
|
+
var sizeMap12, typeStyles, floatKeyframes, DEFAULT_ASSET_URL10;
|
|
7942
8057
|
var init_DamageNumber = __esm({
|
|
7943
8058
|
"components/game/atoms/DamageNumber.tsx"() {
|
|
7944
8059
|
init_cn();
|
|
@@ -7960,6 +8075,7 @@ var init_DamageNumber = __esm({
|
|
|
7960
8075
|
100% { opacity: 0; transform: translateY(-32px) scale(0.8); }
|
|
7961
8076
|
}
|
|
7962
8077
|
`;
|
|
8078
|
+
DEFAULT_ASSET_URL10 = "https://almadar-kflow-assets.web.app/shared/effects/particles/spark_01.png";
|
|
7963
8079
|
DamageNumber.displayName = "DamageNumber";
|
|
7964
8080
|
}
|
|
7965
8081
|
});
|
|
@@ -8048,6 +8164,7 @@ var init_ChoiceButton = __esm({
|
|
|
8048
8164
|
}
|
|
8049
8165
|
});
|
|
8050
8166
|
function ActionButton({
|
|
8167
|
+
assetUrl = DEFAULT_ASSET_URL11,
|
|
8051
8168
|
label = "Attack",
|
|
8052
8169
|
icon,
|
|
8053
8170
|
cooldown = 0,
|
|
@@ -8087,13 +8204,23 @@ function ActionButton({
|
|
|
8087
8204
|
}
|
|
8088
8205
|
}
|
|
8089
8206
|
),
|
|
8090
|
-
|
|
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" ? (() => {
|
|
8091
8218
|
const I = resolveIcon(icon);
|
|
8092
8219
|
return I ? /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-4 h-4" }) : null;
|
|
8093
8220
|
})() : /* @__PURE__ */ (() => {
|
|
8094
8221
|
const I = icon;
|
|
8095
8222
|
return /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-4 h-4" });
|
|
8096
|
-
})() }),
|
|
8223
|
+
})() }) : null,
|
|
8097
8224
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative z-10", children: label }),
|
|
8098
8225
|
hotkey && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8099
8226
|
"span",
|
|
@@ -8109,7 +8236,7 @@ function ActionButton({
|
|
|
8109
8236
|
}
|
|
8110
8237
|
);
|
|
8111
8238
|
}
|
|
8112
|
-
var sizeMap13, variantStyles8;
|
|
8239
|
+
var sizeMap13, variantStyles8, DEFAULT_ASSET_URL11;
|
|
8113
8240
|
var init_ActionButton = __esm({
|
|
8114
8241
|
"components/game/atoms/ActionButton.tsx"() {
|
|
8115
8242
|
init_cn();
|
|
@@ -8124,6 +8251,7 @@ var init_ActionButton = __esm({
|
|
|
8124
8251
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary-hover border-border",
|
|
8125
8252
|
danger: "bg-error text-error-foreground hover:bg-error/90 border-error"
|
|
8126
8253
|
};
|
|
8254
|
+
DEFAULT_ASSET_URL11 = "https://almadar-kflow-assets.web.app/shared/effects/particles/slash_01.png";
|
|
8127
8255
|
ActionButton.displayName = "ActionButton";
|
|
8128
8256
|
}
|
|
8129
8257
|
});
|
|
@@ -8636,10 +8764,10 @@ var init_Container = __esm({
|
|
|
8636
8764
|
center = true,
|
|
8637
8765
|
className,
|
|
8638
8766
|
children,
|
|
8639
|
-
as:
|
|
8767
|
+
as: Component2 = "div"
|
|
8640
8768
|
}) => {
|
|
8641
8769
|
const resolvedSize = maxWidth ?? size ?? "lg";
|
|
8642
|
-
const Comp =
|
|
8770
|
+
const Comp = Component2;
|
|
8643
8771
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8644
8772
|
Comp,
|
|
8645
8773
|
{
|
|
@@ -11473,15 +11601,15 @@ function BattleBoard({
|
|
|
11473
11601
|
const board = boardEntity(entity) ?? {};
|
|
11474
11602
|
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
|
|
11475
11603
|
const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
|
|
11476
|
-
const boardWidth = num(board.boardWidth, 8);
|
|
11477
|
-
const boardHeight = num(board.boardHeight, 6);
|
|
11604
|
+
const boardWidth = num(board.gridWidth ?? board.boardWidth, 8);
|
|
11605
|
+
const boardHeight = num(board.gridHeight ?? board.boardHeight, 6);
|
|
11478
11606
|
const assetManifest = propAssetManifest ?? board.assetManifest;
|
|
11479
11607
|
const backgroundImage = board.backgroundImage;
|
|
11480
11608
|
const units = rows(board.units);
|
|
11481
11609
|
const selectedUnitId = board.selectedUnitId ?? null;
|
|
11482
11610
|
const currentPhase = str(board.phase) || "observation";
|
|
11483
11611
|
const currentTurn = num(board.turn, 1);
|
|
11484
|
-
const gameResult = board.
|
|
11612
|
+
const gameResult = board.result ?? null;
|
|
11485
11613
|
const eventBus = useEventBus();
|
|
11486
11614
|
const { t } = hooks.useTranslate();
|
|
11487
11615
|
const [hoveredTile, setHoveredTile] = React82.useState(null);
|
|
@@ -11502,7 +11630,7 @@ function BattleBoard({
|
|
|
11502
11630
|
const validMoves = React82.useMemo(() => {
|
|
11503
11631
|
if (!selectedUnit || currentPhase !== "movement") return [];
|
|
11504
11632
|
const moves = [];
|
|
11505
|
-
const range = num(
|
|
11633
|
+
const range = num(board.movementRange, 2);
|
|
11506
11634
|
const origin = unitPosition(selectedUnit);
|
|
11507
11635
|
for (let dy = -range; dy <= range; dy++) {
|
|
11508
11636
|
for (let dx = -range; dx <= range; dx++) {
|
|
@@ -19787,15 +19915,24 @@ function CastleBoard({
|
|
|
19787
19915
|
featureClickEvent,
|
|
19788
19916
|
unitClickEvent,
|
|
19789
19917
|
tileClickEvent,
|
|
19918
|
+
playAgainEvent,
|
|
19790
19919
|
className
|
|
19791
19920
|
}) {
|
|
19792
19921
|
const eventBus = useEventBus();
|
|
19922
|
+
const { t } = hooks.useTranslate();
|
|
19793
19923
|
const resolved = boardEntity(entity);
|
|
19794
19924
|
const tiles = propTiles ?? (Array.isArray(resolved?.tiles) ? resolved.tiles : []);
|
|
19795
19925
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
19796
19926
|
const units = propUnits ?? (Array.isArray(resolved?.units) ? resolved.units : []);
|
|
19797
19927
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
19798
19928
|
const backgroundImage = resolved?.backgroundImage;
|
|
19929
|
+
const gold = num(resolved?.gold);
|
|
19930
|
+
const health = num(resolved?.health);
|
|
19931
|
+
const maxHealth = num(resolved?.maxHealth);
|
|
19932
|
+
const wave = num(resolved?.wave);
|
|
19933
|
+
const tickCount = num(resolved?.tickCount);
|
|
19934
|
+
const buildings = rows(resolved?.buildings);
|
|
19935
|
+
const result = str(resolved?.result) || "none";
|
|
19799
19936
|
const [hoveredTile, setHoveredTile] = React82.useState(null);
|
|
19800
19937
|
const [selectedFeature, setSelectedFeature] = React82.useState(null);
|
|
19801
19938
|
const hoveredFeature = React82.useMemo(() => {
|
|
@@ -19808,7 +19945,7 @@ function CastleBoard({
|
|
|
19808
19945
|
(u) => u.position?.x === hoveredTile.x && u.position?.y === hoveredTile.y
|
|
19809
19946
|
) ?? null;
|
|
19810
19947
|
}, [hoveredTile, units]);
|
|
19811
|
-
const maxY = Math.max(...tiles.map((
|
|
19948
|
+
const maxY = Math.max(...tiles.map((t2) => t2.y), 0);
|
|
19812
19949
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
19813
19950
|
const tileToScreen = React82.useCallback(
|
|
19814
19951
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
@@ -19843,6 +19980,11 @@ function CastleBoard({
|
|
|
19843
19980
|
}
|
|
19844
19981
|
}, [units, onUnitClick, unitClickEvent, eventBus]);
|
|
19845
19982
|
const clearSelection = React82.useCallback(() => setSelectedFeature(null), []);
|
|
19983
|
+
const handlePlayAgain = React82.useCallback(() => {
|
|
19984
|
+
if (playAgainEvent) {
|
|
19985
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
19986
|
+
}
|
|
19987
|
+
}, [playAgainEvent, eventBus]);
|
|
19846
19988
|
const ctx = React82.useMemo(
|
|
19847
19989
|
() => ({
|
|
19848
19990
|
hoveredTile,
|
|
@@ -19851,11 +19993,18 @@ function CastleBoard({
|
|
|
19851
19993
|
selectedFeature,
|
|
19852
19994
|
clearSelection,
|
|
19853
19995
|
tileToScreen,
|
|
19854
|
-
scale
|
|
19996
|
+
scale,
|
|
19997
|
+
gold,
|
|
19998
|
+
health,
|
|
19999
|
+
maxHealth,
|
|
20000
|
+
wave,
|
|
20001
|
+
tickCount,
|
|
20002
|
+
buildings,
|
|
20003
|
+
result
|
|
19855
20004
|
}),
|
|
19856
|
-
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale]
|
|
20005
|
+
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale, gold, health, maxHealth, wave, tickCount, buildings, result]
|
|
19857
20006
|
);
|
|
19858
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("castle-board min-h-screen flex flex-col bg-background", className), children: [
|
|
20007
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("castle-board relative min-h-screen flex flex-col bg-background", className), children: [
|
|
19859
20008
|
header && header(ctx),
|
|
19860
20009
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
|
|
19861
20010
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-auto p-4 relative", children: [
|
|
@@ -19880,7 +20029,29 @@ function CastleBoard({
|
|
|
19880
20029
|
] }),
|
|
19881
20030
|
sidePanel && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-96 shrink-0 border-l border-border bg-surface overflow-y-auto", children: sidePanel(ctx) })
|
|
19882
20031
|
] }),
|
|
19883
|
-
footer && footer(ctx)
|
|
20032
|
+
footer && footer(ctx),
|
|
20033
|
+
result !== "none" && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
20034
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
20035
|
+
Typography,
|
|
20036
|
+
{
|
|
20037
|
+
variant: "h2",
|
|
20038
|
+
className: cn(
|
|
20039
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
20040
|
+
result === "victory" ? "text-warning" : "text-error"
|
|
20041
|
+
),
|
|
20042
|
+
children: result === "victory" ? t("battle.victory") : t("battle.defeat")
|
|
20043
|
+
}
|
|
20044
|
+
),
|
|
20045
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
20046
|
+
Button,
|
|
20047
|
+
{
|
|
20048
|
+
variant: "primary",
|
|
20049
|
+
className: "px-8 py-3 font-semibold",
|
|
20050
|
+
onClick: handlePlayAgain,
|
|
20051
|
+
children: t("battle.playAgain")
|
|
20052
|
+
}
|
|
20053
|
+
)
|
|
20054
|
+
] }) })
|
|
19884
20055
|
] });
|
|
19885
20056
|
}
|
|
19886
20057
|
var init_CastleBoard = __esm({
|
|
@@ -19888,6 +20059,10 @@ var init_CastleBoard = __esm({
|
|
|
19888
20059
|
"use client";
|
|
19889
20060
|
init_cn();
|
|
19890
20061
|
init_useEventBus();
|
|
20062
|
+
init_Box();
|
|
20063
|
+
init_Button();
|
|
20064
|
+
init_Typography();
|
|
20065
|
+
init_Stack();
|
|
19891
20066
|
init_IsometricCanvas();
|
|
19892
20067
|
init_boardEntity();
|
|
19893
20068
|
init_isometric();
|
|
@@ -20764,22 +20939,16 @@ function ClassifierBoard({
|
|
|
20764
20939
|
const { emit } = useEventBus();
|
|
20765
20940
|
const { t } = hooks.useTranslate();
|
|
20766
20941
|
const resolved = boardEntity(entity);
|
|
20767
|
-
const [localAssignments, setLocalAssignments] = React82.useState({});
|
|
20768
20942
|
const [headerError, setHeaderError] = React82.useState(false);
|
|
20769
|
-
const [localSubmitted, setLocalSubmitted] = React82.useState(false);
|
|
20770
|
-
const [localAttempts, setLocalAttempts] = React82.useState(0);
|
|
20771
|
-
const [showHint, setShowHint] = React82.useState(false);
|
|
20772
20943
|
const items = Array.isArray(resolved?.items) ? resolved.items : [];
|
|
20773
20944
|
const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
|
|
20774
|
-
const
|
|
20775
|
-
const
|
|
20776
|
-
const
|
|
20777
|
-
const assignments =
|
|
20778
|
-
if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
|
|
20945
|
+
const result = str(resolved?.result);
|
|
20946
|
+
const submitted = result === "win";
|
|
20947
|
+
const attempts = num(resolved?.attempts);
|
|
20948
|
+
const assignments = items.reduce((acc, item) => {
|
|
20949
|
+
if (item.assignedCategory != null && item.assignedCategory !== "") acc[item.id] = item.assignedCategory;
|
|
20779
20950
|
return acc;
|
|
20780
|
-
}, {})
|
|
20781
|
-
const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
|
|
20782
|
-
const submitted = entityDrivesResult || localSubmitted;
|
|
20951
|
+
}, {});
|
|
20783
20952
|
const unassignedItems = items.filter((item) => !assignments[item.id]);
|
|
20784
20953
|
const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
|
|
20785
20954
|
const results = submitted ? items.map((item) => ({
|
|
@@ -20787,54 +20956,25 @@ function ClassifierBoard({
|
|
|
20787
20956
|
assigned: assignments[item.id],
|
|
20788
20957
|
correct: assignments[item.id] === item.correctCategory
|
|
20789
20958
|
})) : [];
|
|
20790
|
-
const allCorrect =
|
|
20959
|
+
const allCorrect = result === "win";
|
|
20791
20960
|
const correctCount = results.filter((r) => r.correct).length;
|
|
20961
|
+
const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
|
|
20792
20962
|
const handleAssign = (itemId, categoryId) => {
|
|
20793
20963
|
if (submitted) return;
|
|
20794
|
-
if (assignEvent) {
|
|
20795
|
-
emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
20796
|
-
}
|
|
20797
|
-
if (!entityHasAssignments) {
|
|
20798
|
-
setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
|
|
20799
|
-
}
|
|
20964
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
20800
20965
|
};
|
|
20801
20966
|
const handleUnassign = (itemId) => {
|
|
20802
20967
|
if (submitted) return;
|
|
20803
|
-
if (
|
|
20804
|
-
setLocalAssignments((prev) => {
|
|
20805
|
-
const next = { ...prev };
|
|
20806
|
-
delete next[itemId];
|
|
20807
|
-
return next;
|
|
20808
|
-
});
|
|
20809
|
-
}
|
|
20968
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId: "" });
|
|
20810
20969
|
};
|
|
20811
|
-
const handleSubmit =
|
|
20812
|
-
if (checkEvent) {
|
|
20813
|
-
|
|
20814
|
-
|
|
20815
|
-
if (!entityDrivesResult) {
|
|
20816
|
-
setLocalSubmitted(true);
|
|
20817
|
-
setLocalAttempts((a) => a + 1);
|
|
20818
|
-
const correct = items.every((item) => assignments[item.id] === item.correctCategory);
|
|
20819
|
-
if (correct) {
|
|
20820
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
20821
|
-
}
|
|
20822
|
-
}
|
|
20823
|
-
}, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
|
|
20824
|
-
const handleReset = () => {
|
|
20825
|
-
if (!entityDrivesResult) setLocalSubmitted(false);
|
|
20826
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
20827
|
-
setShowHint(true);
|
|
20970
|
+
const handleSubmit = () => {
|
|
20971
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
20972
|
+
if (allAssigned && items.every((item) => assignments[item.id] === item.correctCategory)) {
|
|
20973
|
+
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
20828
20974
|
}
|
|
20829
20975
|
};
|
|
20830
20976
|
const handleFullReset = () => {
|
|
20831
|
-
if (playAgainEvent) {
|
|
20832
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
20833
|
-
}
|
|
20834
|
-
setLocalAssignments({});
|
|
20835
|
-
setLocalSubmitted(false);
|
|
20836
|
-
setLocalAttempts(0);
|
|
20837
|
-
setShowHint(false);
|
|
20977
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
20838
20978
|
};
|
|
20839
20979
|
if (!resolved) return null;
|
|
20840
20980
|
const theme = resolved.theme ?? void 0;
|
|
@@ -20873,17 +21013,17 @@ function ClassifierBoard({
|
|
|
20873
21013
|
/* @__PURE__ */ jsxRuntime.jsx(Badge, { size: "sm", children: catItems.length })
|
|
20874
21014
|
] }),
|
|
20875
21015
|
/* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "xs", className: "flex-wrap min-h-[2rem]", children: catItems.map((item) => {
|
|
20876
|
-
const
|
|
21016
|
+
const result2 = results.find((r) => r.item.id === item.id);
|
|
20877
21017
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
20878
21018
|
Badge,
|
|
20879
21019
|
{
|
|
20880
21020
|
size: "sm",
|
|
20881
|
-
className: `cursor-pointer ${
|
|
21021
|
+
className: `cursor-pointer ${result2 ? result2.correct ? "border-success bg-success/10" : "border-error bg-error/10" : ""}`,
|
|
20882
21022
|
onClick: () => handleUnassign(item.id),
|
|
20883
21023
|
children: [
|
|
20884
21024
|
item.iconUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: item.iconUrl, alt: "", className: "w-3 h-3 object-contain inline-block" }),
|
|
20885
21025
|
item.label,
|
|
20886
|
-
|
|
21026
|
+
result2 && /* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: result2.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "xs", className: result2.correct ? "text-success" : "text-error" })
|
|
20887
21027
|
]
|
|
20888
21028
|
},
|
|
20889
21029
|
item.id
|
|
@@ -20912,10 +21052,10 @@ function ClassifierBoard({
|
|
|
20912
21052
|
] }) }),
|
|
20913
21053
|
showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
|
|
20914
21054
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
20915
|
-
!submitted
|
|
21055
|
+
!submitted && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
|
|
20916
21056
|
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
|
|
20917
21057
|
t("classifier.check")
|
|
20918
|
-
] })
|
|
21058
|
+
] }),
|
|
20919
21059
|
/* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
|
|
20920
21060
|
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
|
|
20921
21061
|
t("classifier.reset")
|
|
@@ -24633,7 +24773,7 @@ var init_Flex = __esm({
|
|
|
24633
24773
|
basis,
|
|
24634
24774
|
className,
|
|
24635
24775
|
children,
|
|
24636
|
-
as:
|
|
24776
|
+
as: Component2 = "div"
|
|
24637
24777
|
}) => {
|
|
24638
24778
|
const flexStyle = {};
|
|
24639
24779
|
if (grow !== void 0 || shrink !== void 0 || basis !== void 0) {
|
|
@@ -24645,7 +24785,7 @@ var init_Flex = __esm({
|
|
|
24645
24785
|
flexStyle.flexBasis = typeof basis === "number" ? `${basis}px` : basis;
|
|
24646
24786
|
}
|
|
24647
24787
|
}
|
|
24648
|
-
const Comp =
|
|
24788
|
+
const Comp = Component2;
|
|
24649
24789
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
24650
24790
|
Comp,
|
|
24651
24791
|
{
|
|
@@ -24765,11 +24905,11 @@ var init_Grid = __esm({
|
|
|
24765
24905
|
className,
|
|
24766
24906
|
style,
|
|
24767
24907
|
children,
|
|
24768
|
-
as:
|
|
24908
|
+
as: Component2 = "div"
|
|
24769
24909
|
}) => {
|
|
24770
24910
|
const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
|
|
24771
24911
|
return React82__namespace.default.createElement(
|
|
24772
|
-
|
|
24912
|
+
Component2,
|
|
24773
24913
|
{
|
|
24774
24914
|
className: cn(
|
|
24775
24915
|
"grid",
|
|
@@ -26595,6 +26735,7 @@ var init_ProgressDots = __esm({
|
|
|
26595
26735
|
}
|
|
26596
26736
|
});
|
|
26597
26737
|
function StatBadge({
|
|
26738
|
+
assetUrl = DEFAULT_ASSET_URL12,
|
|
26598
26739
|
label,
|
|
26599
26740
|
value = 0,
|
|
26600
26741
|
max,
|
|
@@ -26618,7 +26759,17 @@ function StatBadge({
|
|
|
26618
26759
|
className
|
|
26619
26760
|
),
|
|
26620
26761
|
children: [
|
|
26621
|
-
|
|
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,
|
|
26622
26773
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground font-medium", children: label }),
|
|
26623
26774
|
format === "hearts" && max && /* @__PURE__ */ jsxRuntime.jsx(
|
|
26624
26775
|
HealthBar,
|
|
@@ -26651,7 +26802,7 @@ function StatBadge({
|
|
|
26651
26802
|
}
|
|
26652
26803
|
);
|
|
26653
26804
|
}
|
|
26654
|
-
var sizeMap17, variantMap2;
|
|
26805
|
+
var sizeMap17, variantMap2, DEFAULT_ASSET_URL12;
|
|
26655
26806
|
var init_StatBadge = __esm({
|
|
26656
26807
|
"components/game/molecules/StatBadge.tsx"() {
|
|
26657
26808
|
init_cn();
|
|
@@ -26670,6 +26821,7 @@ var init_StatBadge = __esm({
|
|
|
26670
26821
|
warning: "bg-warning/15 border-warning/40 text-foreground",
|
|
26671
26822
|
danger: "bg-error/15 border-error/40 text-foreground"
|
|
26672
26823
|
};
|
|
26824
|
+
DEFAULT_ASSET_URL12 = "https://almadar-kflow-assets.web.app/shared/effects/particles/light_01.png";
|
|
26673
26825
|
StatBadge.displayName = "StatBadge";
|
|
26674
26826
|
}
|
|
26675
26827
|
});
|
|
@@ -27326,6 +27478,7 @@ var init_TurnPanel = __esm({
|
|
|
27326
27478
|
}
|
|
27327
27479
|
});
|
|
27328
27480
|
function EnemyPlate({
|
|
27481
|
+
assetUrl = DEFAULT_ASSET_URL13,
|
|
27329
27482
|
name = "Shadow Guard",
|
|
27330
27483
|
health = 80,
|
|
27331
27484
|
maxHealth = 100,
|
|
@@ -27343,15 +27496,28 @@ function EnemyPlate({
|
|
|
27343
27496
|
),
|
|
27344
27497
|
children: [
|
|
27345
27498
|
/* @__PURE__ */ jsxRuntime.jsxs(Box, { className: "flex items-center justify-between gap-2", children: [
|
|
27346
|
-
/* @__PURE__ */ jsxRuntime.
|
|
27347
|
-
|
|
27348
|
-
|
|
27349
|
-
|
|
27350
|
-
|
|
27351
|
-
|
|
27352
|
-
|
|
27353
|
-
|
|
27354
|
-
|
|
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
|
+
] }),
|
|
27355
27521
|
level != null && /* @__PURE__ */ jsxRuntime.jsxs(Badge, { variant: "neutral", size: "sm", children: [
|
|
27356
27522
|
"Lv.",
|
|
27357
27523
|
level
|
|
@@ -27396,7 +27562,7 @@ function EnemyPlate({
|
|
|
27396
27562
|
}
|
|
27397
27563
|
);
|
|
27398
27564
|
}
|
|
27399
|
-
var effectVariantMap2, DEFAULT_ENEMY_EFFECTS;
|
|
27565
|
+
var effectVariantMap2, DEFAULT_ENEMY_EFFECTS, DEFAULT_ASSET_URL13;
|
|
27400
27566
|
var init_EnemyPlate = __esm({
|
|
27401
27567
|
"components/game/molecules/EnemyPlate.tsx"() {
|
|
27402
27568
|
"use client";
|
|
@@ -27413,6 +27579,7 @@ var init_EnemyPlate = __esm({
|
|
|
27413
27579
|
DEFAULT_ENEMY_EFFECTS = [
|
|
27414
27580
|
{ icon: "flame", label: "Burn", variant: "debuff" }
|
|
27415
27581
|
];
|
|
27582
|
+
DEFAULT_ASSET_URL13 = "https://almadar-kflow-assets.web.app/shared/portraits/shadow-legion.png";
|
|
27416
27583
|
EnemyPlate.displayName = "EnemyPlate";
|
|
27417
27584
|
}
|
|
27418
27585
|
});
|
|
@@ -28944,13 +29111,13 @@ var init_MapView = __esm({
|
|
|
28944
29111
|
shadowSize: [41, 41]
|
|
28945
29112
|
});
|
|
28946
29113
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
28947
|
-
const { useEffect:
|
|
29114
|
+
const { useEffect: useEffect80, useRef: useRef74, useCallback: useCallback118, useState: useState112 } = React82__namespace.default;
|
|
28948
29115
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
28949
|
-
const { useEventBus:
|
|
29116
|
+
const { useEventBus: useEventBus4 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
28950
29117
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
28951
29118
|
const map = useMap();
|
|
28952
|
-
const prevRef =
|
|
28953
|
-
|
|
29119
|
+
const prevRef = useRef74({ centerLat, centerLng, zoom });
|
|
29120
|
+
useEffect80(() => {
|
|
28954
29121
|
const prev = prevRef.current;
|
|
28955
29122
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
28956
29123
|
map.setView([centerLat, centerLng], zoom);
|
|
@@ -28961,7 +29128,7 @@ var init_MapView = __esm({
|
|
|
28961
29128
|
}
|
|
28962
29129
|
function MapClickHandler({ onMapClick }) {
|
|
28963
29130
|
const map = useMap();
|
|
28964
|
-
|
|
29131
|
+
useEffect80(() => {
|
|
28965
29132
|
if (!onMapClick) return;
|
|
28966
29133
|
const handler = (e) => {
|
|
28967
29134
|
onMapClick(e.latlng.lat, e.latlng.lng);
|
|
@@ -28988,9 +29155,9 @@ var init_MapView = __esm({
|
|
|
28988
29155
|
className,
|
|
28989
29156
|
showAttribution = true
|
|
28990
29157
|
}) {
|
|
28991
|
-
const eventBus =
|
|
28992
|
-
const [clickedPosition, setClickedPosition] =
|
|
28993
|
-
const handleMapClick =
|
|
29158
|
+
const eventBus = useEventBus4();
|
|
29159
|
+
const [clickedPosition, setClickedPosition] = useState112(null);
|
|
29160
|
+
const handleMapClick = useCallback118((lat, lng) => {
|
|
28994
29161
|
if (showClickedPin) {
|
|
28995
29162
|
setClickedPosition({ lat, lng });
|
|
28996
29163
|
}
|
|
@@ -28999,7 +29166,7 @@ var init_MapView = __esm({
|
|
|
28999
29166
|
eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
|
|
29000
29167
|
}
|
|
29001
29168
|
}, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
|
|
29002
|
-
const handleMarkerClick =
|
|
29169
|
+
const handleMarkerClick = useCallback118((marker) => {
|
|
29003
29170
|
onMarkerClick?.(marker);
|
|
29004
29171
|
if (markerClickEvent) {
|
|
29005
29172
|
eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
|
|
@@ -36126,10 +36293,10 @@ var init_Section = __esm({
|
|
|
36126
36293
|
children,
|
|
36127
36294
|
headerClassName,
|
|
36128
36295
|
contentClassName,
|
|
36129
|
-
as:
|
|
36296
|
+
as: Component2 = "section"
|
|
36130
36297
|
}) => {
|
|
36131
36298
|
const hasHeader = title || description || action;
|
|
36132
|
-
const Comp =
|
|
36299
|
+
const Comp = Component2;
|
|
36133
36300
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
36134
36301
|
Comp,
|
|
36135
36302
|
{
|
|
@@ -38230,44 +38397,27 @@ function DebuggerBoard({
|
|
|
38230
38397
|
const { t } = hooks.useTranslate();
|
|
38231
38398
|
const resolved = boardEntity(entity);
|
|
38232
38399
|
const [headerError, setHeaderError] = React82.useState(false);
|
|
38233
|
-
const [showHint, setShowHint] = React82.useState(false);
|
|
38234
38400
|
const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
|
|
38235
|
-
const result = resolved?.result
|
|
38401
|
+
const result = str(resolved?.result) || "none";
|
|
38236
38402
|
const attempts = num(resolved?.attempts);
|
|
38237
|
-
const submitted = result
|
|
38403
|
+
const submitted = result === "win";
|
|
38404
|
+
const showHint = !submitted && attempts >= 2 && Boolean(str(resolved?.hint));
|
|
38238
38405
|
const bugLines = lines.filter((l) => l.isBug);
|
|
38239
38406
|
const flaggedLines = lines.filter((l) => l.isFlagged);
|
|
38240
38407
|
const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
|
|
38241
38408
|
const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
|
|
38242
|
-
const allCorrect =
|
|
38409
|
+
const allCorrect = submitted;
|
|
38243
38410
|
const toggleLine = (lineId) => {
|
|
38244
38411
|
if (submitted) return;
|
|
38245
|
-
if (toggleFlagEvent) {
|
|
38246
|
-
emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
38247
|
-
}
|
|
38412
|
+
if (toggleFlagEvent) emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
38248
38413
|
};
|
|
38249
38414
|
const handleSubmit = React82.useCallback(() => {
|
|
38250
|
-
if (checkEvent) {
|
|
38251
|
-
emit(`UI:${checkEvent}`, {});
|
|
38252
|
-
}
|
|
38415
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
38253
38416
|
const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
|
|
38254
|
-
if (correct) {
|
|
38255
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
38256
|
-
}
|
|
38417
|
+
if (correct) emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
38257
38418
|
}, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
|
|
38258
38419
|
const handleReset = () => {
|
|
38259
|
-
if (playAgainEvent) {
|
|
38260
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
38261
|
-
}
|
|
38262
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
38263
|
-
setShowHint(true);
|
|
38264
|
-
}
|
|
38265
|
-
};
|
|
38266
|
-
const handleFullReset = () => {
|
|
38267
|
-
if (playAgainEvent) {
|
|
38268
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
38269
|
-
}
|
|
38270
|
-
setShowHint(false);
|
|
38420
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
38271
38421
|
};
|
|
38272
38422
|
if (!resolved) return null;
|
|
38273
38423
|
const theme = resolved.theme ?? void 0;
|
|
@@ -38291,7 +38441,7 @@ function DebuggerBoard({
|
|
|
38291
38441
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
|
|
38292
38442
|
] }),
|
|
38293
38443
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: str(resolved.description) }),
|
|
38294
|
-
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(
|
|
38444
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(lines.filter((l) => l.isBug).length) }) })
|
|
38295
38445
|
] }) }),
|
|
38296
38446
|
/* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(VStack, { gap: "none", children: lines.map((line, i) => {
|
|
38297
38447
|
const isFlagged = !!line.isFlagged;
|
|
@@ -38341,11 +38491,11 @@ function DebuggerBoard({
|
|
|
38341
38491
|
] }) }),
|
|
38342
38492
|
showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", children: hint }) }),
|
|
38343
38493
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
38344
|
-
!submitted
|
|
38494
|
+
!submitted && /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
|
|
38345
38495
|
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.Send, size: "sm" }),
|
|
38346
38496
|
t("debugger.submit")
|
|
38347
|
-
] })
|
|
38348
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick:
|
|
38497
|
+
] }),
|
|
38498
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "secondary", onClick: handleReset, children: [
|
|
38349
38499
|
/* @__PURE__ */ jsxRuntime.jsx(Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
|
|
38350
38500
|
t("debugger.reset")
|
|
38351
38501
|
] })
|
|
@@ -39346,39 +39496,40 @@ function EventHandlerBoard({
|
|
|
39346
39496
|
stepDurationMs = 800,
|
|
39347
39497
|
playEvent,
|
|
39348
39498
|
completeEvent,
|
|
39499
|
+
editRuleEvent,
|
|
39500
|
+
playAgainEvent,
|
|
39349
39501
|
className
|
|
39350
39502
|
}) {
|
|
39351
39503
|
const { emit } = useEventBus();
|
|
39352
39504
|
const { t } = hooks.useTranslate();
|
|
39353
39505
|
const resolved = boardEntity(entity);
|
|
39354
|
-
const
|
|
39355
|
-
const
|
|
39506
|
+
const objects = rows(resolved?.objects);
|
|
39507
|
+
const entityResult = str(resolved?.result) || "none";
|
|
39508
|
+
const isSuccess = entityResult === "win";
|
|
39509
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
39356
39510
|
const [selectedObjectId, setSelectedObjectId] = React82.useState(
|
|
39357
|
-
|
|
39511
|
+
objects[0] ? objId(objects[0]) : null
|
|
39358
39512
|
);
|
|
39359
39513
|
const [headerError, setHeaderError] = React82.useState(false);
|
|
39360
|
-
const [
|
|
39514
|
+
const [isPlaying, setIsPlaying] = React82.useState(false);
|
|
39361
39515
|
const [eventLog, setEventLog] = React82.useState([]);
|
|
39362
|
-
const [attempts, setAttempts] = React82.useState(0);
|
|
39363
39516
|
const timerRef = React82.useRef(null);
|
|
39364
39517
|
const logIdCounter = React82.useRef(0);
|
|
39365
39518
|
React82.useEffect(() => () => {
|
|
39366
39519
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39367
39520
|
}, []);
|
|
39368
|
-
const selectedObject = objects.find((o) => objId(o) === selectedObjectId)
|
|
39521
|
+
const selectedObject = objects.find((o) => objId(o) === selectedObjectId) ?? null;
|
|
39369
39522
|
const handleRulesChange = React82.useCallback((objectId, rules) => {
|
|
39370
|
-
|
|
39371
|
-
|
|
39372
|
-
));
|
|
39373
|
-
}, []);
|
|
39523
|
+
if (editRuleEvent) emit(`UI:${editRuleEvent}`, { objectId, rules });
|
|
39524
|
+
}, [editRuleEvent, emit]);
|
|
39374
39525
|
const addLogEntry = React82.useCallback((icon, message, status = "done") => {
|
|
39375
39526
|
const id = `log-${logIdCounter.current++}`;
|
|
39376
39527
|
setEventLog((prev) => [...prev, { id, timestamp: Date.now(), icon, message, status }]);
|
|
39377
39528
|
}, []);
|
|
39378
39529
|
const handlePlay = React82.useCallback(() => {
|
|
39379
|
-
if (
|
|
39530
|
+
if (isPlaying || isSuccess) return;
|
|
39380
39531
|
if (playEvent) emit(`UI:${playEvent}`, {});
|
|
39381
|
-
|
|
39532
|
+
setIsPlaying(true);
|
|
39382
39533
|
setEventLog([]);
|
|
39383
39534
|
const allRules = [];
|
|
39384
39535
|
objects.forEach((obj) => {
|
|
@@ -39394,15 +39545,8 @@ function EventHandlerBoard({
|
|
|
39394
39545
|
let goalReached = false;
|
|
39395
39546
|
const processNext = () => {
|
|
39396
39547
|
if (eventQueue.length === 0 || stepIdx > 20) {
|
|
39397
|
-
|
|
39398
|
-
|
|
39399
|
-
if (completeEvent) {
|
|
39400
|
-
emit(`UI:${completeEvent}`, { success: true });
|
|
39401
|
-
}
|
|
39402
|
-
} else {
|
|
39403
|
-
setAttempts((prev) => prev + 1);
|
|
39404
|
-
setPlayState("fail");
|
|
39405
|
-
}
|
|
39548
|
+
setIsPlaying(false);
|
|
39549
|
+
if (goalReached && completeEvent) emit(`UI:${completeEvent}`, { success: true });
|
|
39406
39550
|
return;
|
|
39407
39551
|
}
|
|
39408
39552
|
const currentEvent = eventQueue.shift();
|
|
@@ -39433,21 +39577,19 @@ function EventHandlerBoard({
|
|
|
39433
39577
|
addLogEntry("\u{1F3AC}", t("eventHandler.simulationStarted", { events: triggers.join(", ") }), "active");
|
|
39434
39578
|
}
|
|
39435
39579
|
timerRef.current = setTimeout(processNext, stepDurationMs);
|
|
39436
|
-
}, [
|
|
39580
|
+
}, [isPlaying, isSuccess, objects, resolved, stepDurationMs, playEvent, completeEvent, emit, addLogEntry, t]);
|
|
39437
39581
|
const handleTryAgain = React82.useCallback(() => {
|
|
39438
39582
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39439
|
-
|
|
39583
|
+
setIsPlaying(false);
|
|
39440
39584
|
setEventLog([]);
|
|
39441
39585
|
}, []);
|
|
39442
39586
|
const handleReset = React82.useCallback(() => {
|
|
39443
39587
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39444
|
-
|
|
39445
|
-
setObjects([...resetObjects]);
|
|
39446
|
-
setPlayState("editing");
|
|
39588
|
+
setIsPlaying(false);
|
|
39447
39589
|
setEventLog([]);
|
|
39448
|
-
setSelectedObjectId(
|
|
39449
|
-
|
|
39450
|
-
}, [
|
|
39590
|
+
setSelectedObjectId(objects[0] ? objId(objects[0]) : null);
|
|
39591
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
39592
|
+
}, [objects, playAgainEvent, emit]);
|
|
39451
39593
|
if (!resolved) return null;
|
|
39452
39594
|
const objectViewers = objects.map((obj) => {
|
|
39453
39595
|
const states = objStates(obj);
|
|
@@ -39516,12 +39658,12 @@ function EventHandlerBoard({
|
|
|
39516
39658
|
{
|
|
39517
39659
|
object: selectedObject,
|
|
39518
39660
|
onRulesChange: handleRulesChange,
|
|
39519
|
-
disabled:
|
|
39661
|
+
disabled: isPlaying
|
|
39520
39662
|
}
|
|
39521
39663
|
),
|
|
39522
39664
|
eventLog.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(EventLog, { entries: eventLog }),
|
|
39523
|
-
|
|
39524
|
-
|
|
39665
|
+
isSuccess && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("eventHandler.chainComplete") }) }),
|
|
39666
|
+
!isPlaying && !isSuccess && attempts > 0 && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
39525
39667
|
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(encourageKey) }) }),
|
|
39526
39668
|
showHint && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-start", gap: "xs", children: [
|
|
39527
39669
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
@@ -39529,12 +39671,12 @@ function EventHandlerBoard({
|
|
|
39529
39671
|
] }) })
|
|
39530
39672
|
] }),
|
|
39531
39673
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
|
|
39532
|
-
|
|
39674
|
+
!isPlaying && !isSuccess && attempts > 0 ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
39533
39675
|
Button,
|
|
39534
39676
|
{
|
|
39535
39677
|
variant: "primary",
|
|
39536
39678
|
onClick: handlePlay,
|
|
39537
|
-
disabled:
|
|
39679
|
+
disabled: isPlaying || isSuccess,
|
|
39538
39680
|
children: "\u25B6 " + t("game.play")
|
|
39539
39681
|
}
|
|
39540
39682
|
),
|
|
@@ -40719,6 +40861,1568 @@ var init_GameAudioToggle = __esm({
|
|
|
40719
40861
|
GameAudioToggle.displayName = "GameAudioToggle";
|
|
40720
40862
|
}
|
|
40721
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
|
+
});
|
|
40722
42426
|
var GameShell;
|
|
40723
42427
|
var init_GameShell = __esm({
|
|
40724
42428
|
"components/game/templates/GameShell.tsx"() {
|
|
@@ -42087,21 +43791,6 @@ var init_ModalSlot = __esm({
|
|
|
42087
43791
|
ModalSlot.displayName = "ModalSlot";
|
|
42088
43792
|
}
|
|
42089
43793
|
});
|
|
42090
|
-
function getOpponentAction(strategy, actions, history) {
|
|
42091
|
-
const actionIds = actions.map((a) => a.id);
|
|
42092
|
-
switch (strategy) {
|
|
42093
|
-
case "always-cooperate":
|
|
42094
|
-
return actionIds[0];
|
|
42095
|
-
case "always-defect":
|
|
42096
|
-
return actionIds[actionIds.length - 1];
|
|
42097
|
-
case "tit-for-tat":
|
|
42098
|
-
if (history.length === 0) return actionIds[0];
|
|
42099
|
-
return history[history.length - 1].playerAction;
|
|
42100
|
-
case "random":
|
|
42101
|
-
default:
|
|
42102
|
-
return actionIds[Math.floor(Math.random() * actionIds.length)];
|
|
42103
|
-
}
|
|
42104
|
-
}
|
|
42105
43794
|
function NegotiatorBoard({
|
|
42106
43795
|
entity,
|
|
42107
43796
|
completeEvent = "PUZZLE_COMPLETE",
|
|
@@ -42123,28 +43812,35 @@ function NegotiatorBoard({
|
|
|
42123
43812
|
const playerTotal = num(resolved?.score);
|
|
42124
43813
|
const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
|
|
42125
43814
|
const won = result === "win";
|
|
42126
|
-
const
|
|
43815
|
+
const lastPlayerAction = str(resolved?.lastPlayerAction);
|
|
43816
|
+
const lastOpponentAction = str(resolved?.lastOpponentAction);
|
|
43817
|
+
const lastPayoff = num(resolved?.lastPayoff);
|
|
42127
43818
|
const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
|
|
42128
43819
|
const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
|
|
43820
|
+
const prevRoundRef = React82.useRef(currentRound);
|
|
43821
|
+
React82.useEffect(() => {
|
|
43822
|
+
if (currentRound > prevRoundRef.current && lastPlayerAction) {
|
|
43823
|
+
const opponentPayoffEntry = payoffMatrix.find(
|
|
43824
|
+
(p2) => p2.playerAction === lastPlayerAction && p2.opponentAction === lastOpponentAction
|
|
43825
|
+
);
|
|
43826
|
+
setHistory((prev) => [
|
|
43827
|
+
...prev,
|
|
43828
|
+
{
|
|
43829
|
+
round: currentRound,
|
|
43830
|
+
playerAction: lastPlayerAction,
|
|
43831
|
+
opponentAction: lastOpponentAction,
|
|
43832
|
+
playerPayoff: lastPayoff,
|
|
43833
|
+
opponentPayoff: opponentPayoffEntry?.opponentPayoff ?? 0
|
|
43834
|
+
}
|
|
43835
|
+
]);
|
|
43836
|
+
}
|
|
43837
|
+
prevRoundRef.current = currentRound;
|
|
43838
|
+
}, [currentRound, lastPlayerAction, lastOpponentAction, lastPayoff, payoffMatrix]);
|
|
43839
|
+
const opponentTotal = React82.useMemo(() => history.reduce((s, r) => s + r.opponentPayoff, 0), [history]);
|
|
42129
43840
|
const handleAction = React82.useCallback((actionId) => {
|
|
42130
43841
|
if (isComplete) return;
|
|
42131
|
-
const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
|
|
42132
|
-
const payoff = payoffMatrix.find(
|
|
42133
|
-
(p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
|
|
42134
|
-
);
|
|
42135
|
-
const playerPayoff = payoff?.playerPayoff ?? 0;
|
|
42136
|
-
setHistory((prev) => [
|
|
42137
|
-
...prev,
|
|
42138
|
-
{
|
|
42139
|
-
round: prev.length + 1,
|
|
42140
|
-
playerAction: actionId,
|
|
42141
|
-
opponentAction,
|
|
42142
|
-
playerPayoff,
|
|
42143
|
-
opponentPayoff: payoff?.opponentPayoff ?? 0
|
|
42144
|
-
}
|
|
42145
|
-
]);
|
|
42146
43842
|
if (playRoundEvent) {
|
|
42147
|
-
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff:
|
|
43843
|
+
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: 0 });
|
|
42148
43844
|
}
|
|
42149
43845
|
if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
|
|
42150
43846
|
if (finishEvent) {
|
|
@@ -42154,10 +43850,11 @@ function NegotiatorBoard({
|
|
|
42154
43850
|
setShowHint(true);
|
|
42155
43851
|
}
|
|
42156
43852
|
}
|
|
42157
|
-
}, [isComplete, resolved, totalRounds, currentRound,
|
|
43853
|
+
}, [isComplete, resolved, totalRounds, currentRound, playRoundEvent, finishEvent, emit]);
|
|
42158
43854
|
const handleReset = React82.useCallback(() => {
|
|
42159
43855
|
setHistory([]);
|
|
42160
43856
|
setShowHint(false);
|
|
43857
|
+
prevRoundRef.current = 0;
|
|
42161
43858
|
if (playAgainEvent) {
|
|
42162
43859
|
emit(`UI:${playAgainEvent}`, {});
|
|
42163
43860
|
}
|
|
@@ -42268,6 +43965,236 @@ var init_NegotiatorBoard = __esm({
|
|
|
42268
43965
|
NegotiatorBoard.displayName = "NegotiatorBoard";
|
|
42269
43966
|
}
|
|
42270
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
|
+
});
|
|
42271
44198
|
var PricingOrganism;
|
|
42272
44199
|
var init_PricingOrganism = __esm({
|
|
42273
44200
|
"components/marketing/organisms/PricingOrganism.tsx"() {
|
|
@@ -42390,6 +44317,356 @@ var init_PricingPageTemplate = __esm({
|
|
|
42390
44317
|
PricingPageTemplate.displayName = "PricingPageTemplate";
|
|
42391
44318
|
}
|
|
42392
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
|
+
});
|
|
42393
44670
|
|
|
42394
44671
|
// lib/traitRegistry.ts
|
|
42395
44672
|
function notifyListeners2() {
|
|
@@ -43643,7 +45920,7 @@ function getAllEvents(traits2) {
|
|
|
43643
45920
|
function EventDispatcherTab({ traits: traits2, schema }) {
|
|
43644
45921
|
const eventBus = useEventBus();
|
|
43645
45922
|
const { t } = hooks.useTranslate();
|
|
43646
|
-
const [
|
|
45923
|
+
const [log14, setLog] = React82__namespace.useState([]);
|
|
43647
45924
|
const prevStatesRef = React82__namespace.useRef(/* @__PURE__ */ new Map());
|
|
43648
45925
|
React82__namespace.useEffect(() => {
|
|
43649
45926
|
for (const trait of traits2) {
|
|
@@ -43707,9 +45984,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
|
|
|
43707
45984
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.otherEvents") }),
|
|
43708
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)) })
|
|
43709
45986
|
] }),
|
|
43710
|
-
|
|
45987
|
+
log14.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
43711
45988
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.recentTransitions") }),
|
|
43712
|
-
/* @__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: [
|
|
43713
45990
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-purple-400", children: entry.traitName }),
|
|
43714
45991
|
" ",
|
|
43715
45992
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-500", children: entry.from }),
|
|
@@ -44512,17 +46789,24 @@ function SequencerBoard({
|
|
|
44512
46789
|
const { emit } = useEventBus();
|
|
44513
46790
|
const { t } = hooks.useTranslate();
|
|
44514
46791
|
const resolved = boardEntity(entity);
|
|
44515
|
-
const maxSlots = num(resolved?.maxSlots);
|
|
46792
|
+
const maxSlots = num(resolved?.maxSlots) || 3;
|
|
44516
46793
|
const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
|
|
44517
46794
|
const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
|
|
44518
46795
|
const allowDuplicates = resolved?.allowDuplicates !== false;
|
|
46796
|
+
const entitySlots = Array.isArray(resolved?.slots) ? resolved.slots : [];
|
|
46797
|
+
const entityResult = str(resolved?.result);
|
|
46798
|
+
const entityAttempts = num(resolved?.attempts);
|
|
46799
|
+
const entityCurrentStep = typeof resolved?.currentStep === "number" ? resolved.currentStep : -1;
|
|
46800
|
+
const slots = Array.from({ length: maxSlots }, (_, i) => {
|
|
46801
|
+
const entitySlot = entitySlots.find((s) => s.index === i);
|
|
46802
|
+
if (!entitySlot?.placedActionId) return void 0;
|
|
46803
|
+
return availableActions.find((a) => a.id === entitySlot.placedActionId);
|
|
46804
|
+
});
|
|
46805
|
+
const isSuccess = entityResult === "win";
|
|
46806
|
+
const attempts = entityAttempts;
|
|
46807
|
+
const isPlayingBack = entityCurrentStep >= 0 && !isSuccess;
|
|
46808
|
+
const currentStep = entityCurrentStep;
|
|
44519
46809
|
const [headerError, setHeaderError] = React82.useState(false);
|
|
44520
|
-
const [slots, setSlots] = React82.useState(
|
|
44521
|
-
() => Array.from({ length: maxSlots }, () => void 0)
|
|
44522
|
-
);
|
|
44523
|
-
const [playState, setPlayState] = React82.useState("idle");
|
|
44524
|
-
const [currentStep, setCurrentStep] = React82.useState(-1);
|
|
44525
|
-
const [attempts, setAttempts] = React82.useState(0);
|
|
44526
46810
|
const [slotFeedback, setSlotFeedback] = React82.useState(
|
|
44527
46811
|
() => Array.from({ length: maxSlots }, () => null)
|
|
44528
46812
|
);
|
|
@@ -44531,11 +46815,6 @@ function SequencerBoard({
|
|
|
44531
46815
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
44532
46816
|
}, []);
|
|
44533
46817
|
const handleSlotDrop = React82.useCallback((index, item) => {
|
|
44534
|
-
setSlots((prev) => {
|
|
44535
|
-
const next = [...prev];
|
|
44536
|
-
next[index] = item;
|
|
44537
|
-
return next;
|
|
44538
|
-
});
|
|
44539
46818
|
setSlotFeedback((prev) => {
|
|
44540
46819
|
const next = [...prev];
|
|
44541
46820
|
next[index] = null;
|
|
@@ -44545,11 +46824,6 @@ function SequencerBoard({
|
|
|
44545
46824
|
if (placeEvent) emit(`UI:${placeEvent}`, { slotIndex: index, actionId: item.id });
|
|
44546
46825
|
}, [emit, placeEvent]);
|
|
44547
46826
|
const handleSlotRemove = React82.useCallback((index) => {
|
|
44548
|
-
setSlots((prev) => {
|
|
44549
|
-
const next = [...prev];
|
|
44550
|
-
next[index] = void 0;
|
|
44551
|
-
return next;
|
|
44552
|
-
});
|
|
44553
46827
|
setSlotFeedback((prev) => {
|
|
44554
46828
|
const next = [...prev];
|
|
44555
46829
|
next[index] = null;
|
|
@@ -44560,48 +46834,34 @@ function SequencerBoard({
|
|
|
44560
46834
|
}, [emit, removeEvent]);
|
|
44561
46835
|
const handleReset = React82.useCallback(() => {
|
|
44562
46836
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
44563
|
-
setSlots(Array.from({ length: maxSlots }, () => void 0));
|
|
44564
|
-
setPlayState("idle");
|
|
44565
|
-
setCurrentStep(-1);
|
|
44566
|
-
setAttempts(0);
|
|
44567
46837
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
44568
46838
|
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
44569
46839
|
}, [maxSlots, playAgainEvent, emit]);
|
|
44570
46840
|
const filledSlots = slots.filter((s) => !!s);
|
|
44571
|
-
const canPlay = filledSlots.length > 0 &&
|
|
46841
|
+
const canPlay = filledSlots.length > 0 && !isPlayingBack && !isSuccess;
|
|
44572
46842
|
const handlePlay = React82.useCallback(() => {
|
|
44573
46843
|
if (!canPlay) return;
|
|
44574
46844
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
44575
46845
|
emit("UI:PLAY_SOUND", { key: "confirm" });
|
|
44576
46846
|
const sequence = slots.map((s) => s?.id || "");
|
|
44577
|
-
|
|
44578
|
-
|
|
44579
|
-
}
|
|
44580
|
-
setPlayState("playing");
|
|
44581
|
-
setCurrentStep(0);
|
|
46847
|
+
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
46848
|
+
if (playEvent) emit(`UI:${playEvent}`, { sequence });
|
|
44582
46849
|
let step = 0;
|
|
44583
46850
|
const advance = () => {
|
|
44584
46851
|
step++;
|
|
46852
|
+
emit("UI:STEP", { step });
|
|
44585
46853
|
if (step >= maxSlots) {
|
|
46854
|
+
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
44586
46855
|
const playerSeq = slots.map((s) => s?.id);
|
|
44587
|
-
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
44588
46856
|
const success = solutions.some(
|
|
44589
46857
|
(sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
|
|
44590
46858
|
);
|
|
44591
|
-
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
44592
46859
|
if (success) {
|
|
44593
|
-
setPlayState("success");
|
|
44594
|
-
setCurrentStep(-1);
|
|
44595
46860
|
emit("UI:PLAY_SOUND", { key: "levelComplete" });
|
|
44596
|
-
if (completeEvent) {
|
|
44597
|
-
emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
44598
|
-
}
|
|
46861
|
+
if (completeEvent) emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
44599
46862
|
} else {
|
|
44600
|
-
setAttempts((prev) => prev + 1);
|
|
44601
46863
|
const feedback = computeSlotFeedback(playerSeq, solutions);
|
|
44602
46864
|
setSlotFeedback(feedback);
|
|
44603
|
-
setPlayState("idle");
|
|
44604
|
-
setCurrentStep(-1);
|
|
44605
46865
|
emit("UI:PLAY_SOUND", { key: "fail" });
|
|
44606
46866
|
const correctCount2 = feedback.filter((f3) => f3 === "correct").length;
|
|
44607
46867
|
for (let ci = 0; ci < correctCount2; ci++) {
|
|
@@ -44611,7 +46871,6 @@ function SequencerBoard({
|
|
|
44611
46871
|
}
|
|
44612
46872
|
}
|
|
44613
46873
|
} else {
|
|
44614
|
-
setCurrentStep(step);
|
|
44615
46874
|
timerRef.current = setTimeout(advance, stepDurationMs);
|
|
44616
46875
|
}
|
|
44617
46876
|
};
|
|
@@ -44661,7 +46920,7 @@ function SequencerBoard({
|
|
|
44661
46920
|
/* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "xs", children: [
|
|
44662
46921
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-center justify-between", children: [
|
|
44663
46922
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
|
|
44664
|
-
hasFeedback &&
|
|
46923
|
+
hasFeedback && !isPlayingBack && !isSuccess && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
|
|
44665
46924
|
`${correctCount}/${maxSlots} `,
|
|
44666
46925
|
"\u2705"
|
|
44667
46926
|
] })
|
|
@@ -44673,7 +46932,7 @@ function SequencerBoard({
|
|
|
44673
46932
|
maxSlots,
|
|
44674
46933
|
onSlotDrop: handleSlotDrop,
|
|
44675
46934
|
onSlotRemove: handleSlotRemove,
|
|
44676
|
-
playing:
|
|
46935
|
+
playing: isPlayingBack,
|
|
44677
46936
|
currentStep,
|
|
44678
46937
|
categoryColors,
|
|
44679
46938
|
slotFeedback,
|
|
@@ -44681,7 +46940,7 @@ function SequencerBoard({
|
|
|
44681
46940
|
}
|
|
44682
46941
|
)
|
|
44683
46942
|
] }),
|
|
44684
|
-
|
|
46943
|
+
!isPlayingBack && /* @__PURE__ */ jsxRuntime.jsx(
|
|
44685
46944
|
ActionPalette,
|
|
44686
46945
|
{
|
|
44687
46946
|
actions: availableActions,
|
|
@@ -44691,8 +46950,8 @@ function SequencerBoard({
|
|
|
44691
46950
|
label: t("sequencer.dragActions")
|
|
44692
46951
|
}
|
|
44693
46952
|
),
|
|
44694
|
-
hasFeedback &&
|
|
44695
|
-
|
|
46953
|
+
hasFeedback && !isPlayingBack && !isSuccess && attempts > 0 && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: t(encourageKey) }) }),
|
|
46954
|
+
isSuccess && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("sequencer.levelComplete") }) }),
|
|
44696
46955
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
|
|
44697
46956
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44698
46957
|
Button,
|
|
@@ -45648,9 +47907,9 @@ function VariablePanel({
|
|
|
45648
47907
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
|
|
45649
47908
|
variables.map((v) => {
|
|
45650
47909
|
const name = v.name == null ? "" : String(v.name);
|
|
45651
|
-
const value =
|
|
45652
|
-
const max =
|
|
45653
|
-
const min =
|
|
47910
|
+
const value = numField2(v.value);
|
|
47911
|
+
const max = numField2(v.max, 100);
|
|
47912
|
+
const min = numField2(v.min, 0);
|
|
45654
47913
|
const unit = v.unit == null ? "" : String(v.unit);
|
|
45655
47914
|
const pct = Math.round((value - min) / (max - min) * 100);
|
|
45656
47915
|
const isHigh = pct > 80;
|
|
@@ -45680,12 +47939,12 @@ function VariablePanel({
|
|
|
45680
47939
|
})
|
|
45681
47940
|
] });
|
|
45682
47941
|
}
|
|
45683
|
-
var
|
|
47942
|
+
var numField2;
|
|
45684
47943
|
var init_VariablePanel = __esm({
|
|
45685
47944
|
"components/game/organisms/puzzles/state-architect/VariablePanel.tsx"() {
|
|
45686
47945
|
init_atoms2();
|
|
45687
47946
|
init_cn();
|
|
45688
|
-
|
|
47947
|
+
numField2 = (v, fallback = 0) => {
|
|
45689
47948
|
const n = Number(v);
|
|
45690
47949
|
return Number.isFinite(n) ? n : fallback;
|
|
45691
47950
|
};
|
|
@@ -45742,6 +48001,9 @@ function StateArchitectBoard({
|
|
|
45742
48001
|
stepDurationMs = 600,
|
|
45743
48002
|
testEvent,
|
|
45744
48003
|
completeEvent,
|
|
48004
|
+
addTransitionEvent,
|
|
48005
|
+
removeTransitionEvent,
|
|
48006
|
+
playAgainEvent,
|
|
45745
48007
|
className
|
|
45746
48008
|
}) {
|
|
45747
48009
|
const { emit } = useEventBus();
|
|
@@ -45754,14 +48016,16 @@ function StateArchitectBoard({
|
|
|
45754
48016
|
const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
|
|
45755
48017
|
const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
|
|
45756
48018
|
const entityVariables = rows(resolved?.variables);
|
|
45757
|
-
const
|
|
48019
|
+
const transitions = entityTransitions;
|
|
48020
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
48021
|
+
const entityResult = str(resolved?.result) || "none";
|
|
48022
|
+
const isSuccess = entityResult === "win";
|
|
48023
|
+
const [isTesting, setIsTesting] = React82.useState(false);
|
|
45758
48024
|
const [headerError, setHeaderError] = React82.useState(false);
|
|
45759
|
-
const [playState, setPlayState] = React82.useState("editing");
|
|
45760
48025
|
const [currentState, setCurrentState] = React82.useState(initialState);
|
|
45761
48026
|
const [selectedState, setSelectedState] = React82.useState(null);
|
|
45762
48027
|
const [testResults, setTestResults] = React82.useState([]);
|
|
45763
48028
|
const [variables, setVariables] = React82.useState(() => [...entityVariables]);
|
|
45764
|
-
const [attempts, setAttempts] = React82.useState(0);
|
|
45765
48029
|
const timerRef = React82.useRef(null);
|
|
45766
48030
|
const [addingFrom, setAddingFrom] = React82.useState(null);
|
|
45767
48031
|
React82.useEffect(() => () => {
|
|
@@ -45771,7 +48035,7 @@ function StateArchitectBoard({
|
|
|
45771
48035
|
const GRAPH_H = 400;
|
|
45772
48036
|
const positions = React82.useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
|
|
45773
48037
|
const handleStateClick = React82.useCallback((state) => {
|
|
45774
|
-
if (
|
|
48038
|
+
if (isTesting) return;
|
|
45775
48039
|
if (addingFrom) {
|
|
45776
48040
|
if (addingFrom !== state) {
|
|
45777
48041
|
const event = availableEvents[0] || "EVENT";
|
|
@@ -45781,20 +48045,20 @@ function StateArchitectBoard({
|
|
|
45781
48045
|
to: state,
|
|
45782
48046
|
event
|
|
45783
48047
|
};
|
|
45784
|
-
|
|
48048
|
+
if (addTransitionEvent) emit(`UI:${addTransitionEvent}`, { id: newTrans.id, from: newTrans.from, to: newTrans.to, event: newTrans.event });
|
|
45785
48049
|
}
|
|
45786
48050
|
setAddingFrom(null);
|
|
45787
48051
|
} else {
|
|
45788
48052
|
setSelectedState(state);
|
|
45789
48053
|
}
|
|
45790
|
-
}, [
|
|
48054
|
+
}, [isTesting, addingFrom, availableEvents, addTransitionEvent, emit]);
|
|
45791
48055
|
const handleStartAddTransition = React82.useCallback(() => {
|
|
45792
48056
|
if (!selectedState) return;
|
|
45793
48057
|
setAddingFrom(selectedState);
|
|
45794
48058
|
}, [selectedState]);
|
|
45795
48059
|
const handleRemoveTransition = React82.useCallback((transId) => {
|
|
45796
|
-
|
|
45797
|
-
}, []);
|
|
48060
|
+
if (removeTransitionEvent) emit(`UI:${removeTransitionEvent}`, { id: transId });
|
|
48061
|
+
}, [removeTransitionEvent, emit]);
|
|
45798
48062
|
const machine = React82.useMemo(() => ({
|
|
45799
48063
|
name: entityName,
|
|
45800
48064
|
description: str(resolved?.description),
|
|
@@ -45808,16 +48072,16 @@ function StateArchitectBoard({
|
|
|
45808
48072
|
}))
|
|
45809
48073
|
}), [entityName, resolved, entityStates, currentState, transitions]);
|
|
45810
48074
|
const handleTest = React82.useCallback(() => {
|
|
45811
|
-
if (
|
|
48075
|
+
if (isTesting) return;
|
|
45812
48076
|
if (testEvent) emit(`UI:${testEvent}`, {});
|
|
45813
|
-
|
|
48077
|
+
setIsTesting(true);
|
|
45814
48078
|
setTestResults([]);
|
|
45815
48079
|
const results = [];
|
|
45816
48080
|
let testIdx = 0;
|
|
45817
48081
|
const runNextTest = () => {
|
|
45818
48082
|
if (testIdx >= testCases.length) {
|
|
45819
48083
|
const allPassed = results.every((r) => r.passed);
|
|
45820
|
-
|
|
48084
|
+
setIsTesting(false);
|
|
45821
48085
|
setTestResults(results);
|
|
45822
48086
|
if (allPassed && completeEvent) {
|
|
45823
48087
|
emit(`UI:${completeEvent}`, {
|
|
@@ -45825,9 +48089,6 @@ function StateArchitectBoard({
|
|
|
45825
48089
|
passedTests: results.filter((r) => r.passed).length
|
|
45826
48090
|
});
|
|
45827
48091
|
}
|
|
45828
|
-
if (!allPassed) {
|
|
45829
|
-
setAttempts((prev) => prev + 1);
|
|
45830
|
-
}
|
|
45831
48092
|
return;
|
|
45832
48093
|
}
|
|
45833
48094
|
const testCase = testCases[testIdx];
|
|
@@ -45850,24 +48111,23 @@ function StateArchitectBoard({
|
|
|
45850
48111
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
45851
48112
|
};
|
|
45852
48113
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
45853
|
-
}, [
|
|
48114
|
+
}, [isTesting, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
|
|
45854
48115
|
const handleTryAgain = React82.useCallback(() => {
|
|
45855
48116
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
45856
|
-
|
|
48117
|
+
setIsTesting(false);
|
|
45857
48118
|
setCurrentState(initialState);
|
|
45858
48119
|
setTestResults([]);
|
|
45859
48120
|
}, [initialState]);
|
|
45860
48121
|
const handleReset = React82.useCallback(() => {
|
|
45861
48122
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
45862
|
-
|
|
45863
|
-
setPlayState("editing");
|
|
48123
|
+
setIsTesting(false);
|
|
45864
48124
|
setCurrentState(initialState);
|
|
45865
48125
|
setTestResults([]);
|
|
45866
48126
|
setVariables([...entityVariables]);
|
|
45867
48127
|
setSelectedState(null);
|
|
45868
48128
|
setAddingFrom(null);
|
|
45869
|
-
|
|
45870
|
-
}, [
|
|
48129
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
48130
|
+
}, [initialState, entityVariables, playAgainEvent, emit]);
|
|
45871
48131
|
const codeData = React82.useMemo(() => ({
|
|
45872
48132
|
name: entityName,
|
|
45873
48133
|
states: entityStates,
|
|
@@ -45963,7 +48223,7 @@ function StateArchitectBoard({
|
|
|
45963
48223
|
]
|
|
45964
48224
|
}
|
|
45965
48225
|
),
|
|
45966
|
-
|
|
48226
|
+
!isTesting && /* @__PURE__ */ jsxRuntime.jsx(HStack, { gap: "sm", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
45967
48227
|
Button,
|
|
45968
48228
|
{
|
|
45969
48229
|
variant: "ghost",
|
|
@@ -45985,7 +48245,7 @@ function StateArchitectBoard({
|
|
|
45985
48245
|
t2.guardHint,
|
|
45986
48246
|
")"
|
|
45987
48247
|
] }),
|
|
45988
|
-
|
|
48248
|
+
!isTesting && /* @__PURE__ */ jsxRuntime.jsx(
|
|
45989
48249
|
Button,
|
|
45990
48250
|
{
|
|
45991
48251
|
variant: "ghost",
|
|
@@ -46017,21 +48277,21 @@ function StateArchitectBoard({
|
|
|
46017
48277
|
resolved.showCodeView !== false && /* @__PURE__ */ jsxRuntime.jsx(StateJsonView, { data: codeData, label: "View Code" })
|
|
46018
48278
|
] })
|
|
46019
48279
|
] }),
|
|
46020
|
-
|
|
46021
|
-
|
|
48280
|
+
isSuccess && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("stateArchitect.allPassed") }) }),
|
|
48281
|
+
!isTesting && !isSuccess && testResults.some((r) => !r.passed) && /* @__PURE__ */ jsxRuntime.jsxs(VStack, { gap: "sm", children: [
|
|
46022
48282
|
/* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(ENCOURAGEMENT_KEYS3[Math.min(attempts - 1, ENCOURAGEMENT_KEYS3.length - 1)] ?? ENCOURAGEMENT_KEYS3[0]) }) }),
|
|
46023
|
-
attempts >=
|
|
48283
|
+
!isSuccess && attempts >= 2 && Boolean(str(resolved?.hint)) && /* @__PURE__ */ jsxRuntime.jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "items-start", gap: "xs", children: [
|
|
46024
48284
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
46025
48285
|
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
|
|
46026
48286
|
] }) })
|
|
46027
48287
|
] }),
|
|
46028
48288
|
/* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", children: [
|
|
46029
|
-
|
|
48289
|
+
!isTesting && !isSuccess && testResults.some((r) => !r.passed) ? /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
46030
48290
|
Button,
|
|
46031
48291
|
{
|
|
46032
48292
|
variant: "primary",
|
|
46033
48293
|
onClick: handleTest,
|
|
46034
|
-
disabled:
|
|
48294
|
+
disabled: isTesting,
|
|
46035
48295
|
children: "\u25B6 " + t("game.runTests")
|
|
46036
48296
|
}
|
|
46037
48297
|
),
|
|
@@ -46537,6 +48797,289 @@ var init_ToastSlot = __esm({
|
|
|
46537
48797
|
ToastSlot.displayName = "ToastSlot";
|
|
46538
48798
|
}
|
|
46539
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
|
+
});
|
|
46540
49083
|
function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
|
|
46541
49084
|
const eventBus = useEventBus();
|
|
46542
49085
|
const {
|
|
@@ -46735,17 +49278,12 @@ var init_UncontrolledBattleBoard = __esm({
|
|
|
46735
49278
|
}
|
|
46736
49279
|
});
|
|
46737
49280
|
function heroPosition(h) {
|
|
49281
|
+
if ("position" in h && h.position != null) return h.position;
|
|
46738
49282
|
return vec2(h.position);
|
|
46739
49283
|
}
|
|
46740
|
-
function heroOwner(h) {
|
|
46741
|
-
return str(h.owner);
|
|
46742
|
-
}
|
|
46743
49284
|
function heroMovement(h) {
|
|
46744
49285
|
return num(h.movement);
|
|
46745
49286
|
}
|
|
46746
|
-
function hexPassable(h) {
|
|
46747
|
-
return h.passable !== false;
|
|
46748
|
-
}
|
|
46749
49287
|
function defaultIsInRange(from, to, range) {
|
|
46750
49288
|
return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
|
|
46751
49289
|
}
|
|
@@ -46781,38 +49319,41 @@ function WorldMapBoard({
|
|
|
46781
49319
|
}) {
|
|
46782
49320
|
const eventBus = useEventBus();
|
|
46783
49321
|
const resolved = boardEntity(entity);
|
|
46784
|
-
const
|
|
46785
|
-
const
|
|
49322
|
+
const entityUnits = rows(resolved?.units);
|
|
49323
|
+
const entityTiles = rows(resolved?.tiles);
|
|
46786
49324
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
46787
49325
|
const selectedHeroId = resolved?.selectedHeroId ?? null;
|
|
46788
49326
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
46789
49327
|
const backgroundImage = resolved?.backgroundImage;
|
|
46790
49328
|
const [hoveredTile, setHoveredTile] = React82.useState(null);
|
|
46791
|
-
const selectedHero = React82.useMemo(
|
|
46792
|
-
() => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
|
|
46793
|
-
[heroes, selectedHeroId]
|
|
46794
|
-
);
|
|
46795
49329
|
const derivedTiles = React82.useMemo(
|
|
46796
|
-
() =>
|
|
46797
|
-
x: num(
|
|
46798
|
-
y: num(
|
|
46799
|
-
terrain: str(
|
|
46800
|
-
terrainSprite:
|
|
49330
|
+
() => entityTiles.map((t) => ({
|
|
49331
|
+
x: num(t.x),
|
|
49332
|
+
y: num(t.y),
|
|
49333
|
+
terrain: str(t.terrain),
|
|
49334
|
+
terrainSprite: t.terrainSprite == null ? void 0 : str(t.terrainSprite),
|
|
49335
|
+
passable: t.passable !== false
|
|
46801
49336
|
})),
|
|
46802
|
-
[
|
|
49337
|
+
[entityTiles]
|
|
46803
49338
|
);
|
|
46804
49339
|
const tiles = propTiles ?? derivedTiles;
|
|
46805
49340
|
const baseUnits = React82.useMemo(
|
|
46806
|
-
() => propUnits ??
|
|
46807
|
-
id: str(
|
|
46808
|
-
position: heroPosition(
|
|
46809
|
-
name: str(
|
|
46810
|
-
|
|
46811
|
-
|
|
46812
|
-
|
|
46813
|
-
|
|
49341
|
+
() => propUnits ?? entityUnits.map((u) => ({
|
|
49342
|
+
id: str(u.id),
|
|
49343
|
+
position: heroPosition(u),
|
|
49344
|
+
name: str(u.name),
|
|
49345
|
+
// lolo uses `team` field (not `owner`)
|
|
49346
|
+
team: str(u.team) === "enemy" ? "enemy" : "player",
|
|
49347
|
+
health: num(u.health) || 100,
|
|
49348
|
+
maxHealth: num(u.maxHealth) || 100,
|
|
49349
|
+
sprite: u.sprite == null ? void 0 : str(u.sprite)
|
|
46814
49350
|
})),
|
|
46815
|
-
[
|
|
49351
|
+
[entityUnits, propUnits]
|
|
49352
|
+
);
|
|
49353
|
+
const gameUnits = baseUnits;
|
|
49354
|
+
const selectedHero = React82.useMemo(
|
|
49355
|
+
() => gameUnits.find((u) => u.id === selectedHeroId) ?? null,
|
|
49356
|
+
[gameUnits, selectedHeroId]
|
|
46816
49357
|
);
|
|
46817
49358
|
const MOVE_SPEED_MS_PER_TILE = 300;
|
|
46818
49359
|
const movementAnimRef = React82.useRef(null);
|
|
@@ -46858,51 +49399,50 @@ function WorldMapBoard({
|
|
|
46858
49399
|
const validMoves = React82.useMemo(() => {
|
|
46859
49400
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
46860
49401
|
const sp = heroPosition(selectedHero);
|
|
46861
|
-
const
|
|
49402
|
+
const sTeam = str(selectedHero.team);
|
|
46862
49403
|
const range = heroMovement(selectedHero);
|
|
46863
49404
|
const moves = [];
|
|
46864
|
-
|
|
46865
|
-
const
|
|
46866
|
-
const
|
|
46867
|
-
if (
|
|
46868
|
-
if (
|
|
46869
|
-
if (!isInRange(sp, { x:
|
|
46870
|
-
if (
|
|
46871
|
-
const
|
|
46872
|
-
return
|
|
49405
|
+
tiles.forEach((t) => {
|
|
49406
|
+
const tx = t.x;
|
|
49407
|
+
const ty = t.y;
|
|
49408
|
+
if (t.passable === false) return;
|
|
49409
|
+
if (tx === sp.x && ty === sp.y) return;
|
|
49410
|
+
if (!isInRange(sp, { x: tx, y: ty }, range)) return;
|
|
49411
|
+
if (gameUnits.some((u) => {
|
|
49412
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
49413
|
+
return up.x === tx && up.y === ty && str(u.team) === sTeam;
|
|
46873
49414
|
})) return;
|
|
46874
|
-
moves.push({ x:
|
|
49415
|
+
moves.push({ x: tx, y: ty });
|
|
46875
49416
|
});
|
|
46876
49417
|
return moves;
|
|
46877
|
-
}, [selectedHero,
|
|
49418
|
+
}, [selectedHero, tiles, gameUnits, isInRange]);
|
|
46878
49419
|
const attackTargets = React82.useMemo(() => {
|
|
46879
49420
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
46880
49421
|
const sp = heroPosition(selectedHero);
|
|
46881
|
-
const
|
|
49422
|
+
const sTeam = str(selectedHero.team);
|
|
46882
49423
|
const range = heroMovement(selectedHero);
|
|
46883
|
-
return
|
|
46884
|
-
}, [selectedHero,
|
|
46885
|
-
const maxY = Math.max(...
|
|
49424
|
+
return gameUnits.filter((u) => str(u.team) !== sTeam).filter((u) => isInRange(sp, u.position ?? { x: u.x ?? -1, y: u.y ?? -1 }, range)).map((u) => u.position ?? { x: u.x ?? -1, y: u.y ?? -1 });
|
|
49425
|
+
}, [selectedHero, gameUnits, isInRange]);
|
|
49426
|
+
const maxY = Math.max(...tiles.map((t) => t.y), 0);
|
|
46886
49427
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
46887
49428
|
const tileToScreen = React82.useCallback(
|
|
46888
49429
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
46889
49430
|
[scale, baseOffsetX]
|
|
46890
49431
|
);
|
|
46891
49432
|
const hoveredHex = React82.useMemo(
|
|
46892
|
-
() => hoveredTile ?
|
|
46893
|
-
[hoveredTile,
|
|
49433
|
+
() => hoveredTile ? tiles.find((t) => t.x === hoveredTile.x && t.y === hoveredTile.y) ?? null : null,
|
|
49434
|
+
[hoveredTile, tiles]
|
|
46894
49435
|
);
|
|
46895
49436
|
const hoveredHero = React82.useMemo(
|
|
46896
|
-
() => hoveredTile ?
|
|
46897
|
-
const
|
|
46898
|
-
return
|
|
49437
|
+
() => hoveredTile ? gameUnits.find((u) => {
|
|
49438
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
49439
|
+
return up.x === hoveredTile.x && up.y === hoveredTile.y;
|
|
46899
49440
|
}) ?? null : null,
|
|
46900
|
-
[hoveredTile,
|
|
49441
|
+
[hoveredTile, gameUnits]
|
|
46901
49442
|
);
|
|
46902
49443
|
const handleTileClick = React82.useCallback((x, y) => {
|
|
46903
49444
|
if (movementAnimRef.current) return;
|
|
46904
|
-
const
|
|
46905
|
-
if (!hex) return;
|
|
49445
|
+
const tile = tiles.find((t) => t.x === x && t.y === y);
|
|
46906
49446
|
if (tileClickEvent) {
|
|
46907
49447
|
eventBus.emit(`UI:${tileClickEvent}`, { x, y });
|
|
46908
49448
|
}
|
|
@@ -46913,38 +49453,39 @@ function WorldMapBoard({
|
|
|
46913
49453
|
if (heroMoveEvent) {
|
|
46914
49454
|
eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
|
|
46915
49455
|
}
|
|
46916
|
-
const feature = str(
|
|
49456
|
+
const feature = tile ? str(tile.feature) : "";
|
|
46917
49457
|
if (feature && feature !== "none") {
|
|
46918
|
-
|
|
49458
|
+
const tileRow = tile;
|
|
49459
|
+
onFeatureEnter?.(heroId, tileRow);
|
|
46919
49460
|
if (featureEnterEvent) {
|
|
46920
|
-
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
|
|
49461
|
+
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex: tileRow });
|
|
46921
49462
|
}
|
|
46922
49463
|
}
|
|
46923
49464
|
});
|
|
46924
49465
|
return;
|
|
46925
49466
|
}
|
|
46926
|
-
const enemy =
|
|
46927
|
-
const
|
|
46928
|
-
return
|
|
49467
|
+
const enemy = gameUnits.find((u) => {
|
|
49468
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
49469
|
+
return up.x === x && up.y === y && str(u.team) === "enemy";
|
|
46929
49470
|
});
|
|
46930
49471
|
if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
|
|
46931
49472
|
const attackerId = str(selectedHero.id);
|
|
46932
|
-
const defenderId =
|
|
49473
|
+
const defenderId = enemy.id;
|
|
46933
49474
|
onBattleEncounter?.(attackerId, defenderId);
|
|
46934
49475
|
if (battleEncounterEvent) {
|
|
46935
49476
|
eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
|
|
46936
49477
|
}
|
|
46937
49478
|
}
|
|
46938
|
-
}, [
|
|
49479
|
+
}, [tiles, gameUnits, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
|
|
46939
49480
|
const handleUnitClick = React82.useCallback((unitId) => {
|
|
46940
|
-
const
|
|
46941
|
-
if (
|
|
49481
|
+
const unit = gameUnits.find((u) => u.id === unitId);
|
|
49482
|
+
if (unit && (str(unit.team) === "player" || allowMoveAllHeroes)) {
|
|
46942
49483
|
onHeroSelect?.(unitId);
|
|
46943
49484
|
if (heroSelectEvent) {
|
|
46944
49485
|
eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
|
|
46945
49486
|
}
|
|
46946
49487
|
}
|
|
46947
|
-
}, [
|
|
49488
|
+
}, [gameUnits, onHeroSelect, allowMoveAllHeroes, eventBus, heroSelectEvent]);
|
|
46948
49489
|
const selectHero = React82.useCallback((id) => {
|
|
46949
49490
|
onHeroSelect?.(id);
|
|
46950
49491
|
if (heroSelectEvent) {
|
|
@@ -47048,48 +49589,48 @@ function WorldMapTemplate({
|
|
|
47048
49589
|
}
|
|
47049
49590
|
);
|
|
47050
49591
|
}
|
|
47051
|
-
var
|
|
49592
|
+
var CDN7, DEFAULT_WORLDMAP_TILES, DEFAULT_WORLDMAP_UNITS, DEFAULT_WORLDMAP_FEATURES, DEFAULT_WORLDMAP_MANIFEST;
|
|
47052
49593
|
var init_WorldMapTemplate = __esm({
|
|
47053
49594
|
"components/game/templates/WorldMapTemplate.tsx"() {
|
|
47054
49595
|
init_WorldMapBoard();
|
|
47055
|
-
|
|
49596
|
+
CDN7 = "https://almadar-kflow-assets.web.app/shared";
|
|
47056
49597
|
DEFAULT_WORLDMAP_TILES = [
|
|
47057
|
-
{ x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47058
|
-
{ x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47059
|
-
{ x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${
|
|
47060
|
-
{ x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47061
|
-
{ x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47062
|
-
{ x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47063
|
-
{ x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
47064
|
-
{ x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
47065
|
-
{ x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
47066
|
-
{ x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${
|
|
47067
|
-
{ x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${
|
|
47068
|
-
{ x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
47069
|
-
{ x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
47070
|
-
{ x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
47071
|
-
{ x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47072
|
-
{ x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47073
|
-
{ x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
47074
|
-
{ x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
47075
|
-
{ x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
47076
|
-
{ x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47077
|
-
{ x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47078
|
-
{ x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${
|
|
47079
|
-
{ x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${
|
|
47080
|
-
{ x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47081
|
-
{ 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` }
|
|
47082
49623
|
];
|
|
47083
49624
|
DEFAULT_WORLDMAP_UNITS = [
|
|
47084
|
-
{ id: "h1", position: { x: 1, y: 1 }, unitType: "hero", name: "Amir", team: "player", health: 10, maxHealth: 10, sprite: `${
|
|
47085
|
-
{ 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` }
|
|
47086
49627
|
];
|
|
47087
49628
|
DEFAULT_WORLDMAP_FEATURES = [
|
|
47088
|
-
{ id: "f1", x: 2, y: 2, type: "capital", sprite: `${
|
|
47089
|
-
{ 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` }
|
|
47090
49631
|
];
|
|
47091
49632
|
DEFAULT_WORLDMAP_MANIFEST = {
|
|
47092
|
-
baseUrl:
|
|
49633
|
+
baseUrl: CDN7,
|
|
47093
49634
|
terrains: {
|
|
47094
49635
|
grass: "/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png",
|
|
47095
49636
|
water: "/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png",
|
|
@@ -47134,7 +49675,7 @@ function lazyThree(name, loader) {
|
|
|
47134
49675
|
ThreeWrapper.displayName = `Lazy(${name})`;
|
|
47135
49676
|
return ThreeWrapper;
|
|
47136
49677
|
}
|
|
47137
|
-
var ThreeBoundary, FeatureRenderer,
|
|
49678
|
+
var ThreeBoundary, FeatureRenderer, GameCanvas3D2, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
|
|
47138
49679
|
var init_component_registry_generated = __esm({
|
|
47139
49680
|
"components/core/organisms/component-registry.generated.ts"() {
|
|
47140
49681
|
init_AboutPageTemplate();
|
|
@@ -47244,6 +49785,7 @@ var init_component_registry_generated = __esm({
|
|
|
47244
49785
|
init_FormSectionHeader();
|
|
47245
49786
|
init_GameAudioProvider();
|
|
47246
49787
|
init_GameAudioToggle();
|
|
49788
|
+
init_GameBoard3D();
|
|
47247
49789
|
init_GameCanvas2D();
|
|
47248
49790
|
init_GameHud();
|
|
47249
49791
|
init_GameMenu();
|
|
@@ -47301,7 +49843,9 @@ var init_component_registry_generated = __esm({
|
|
|
47301
49843
|
init_PageHeader();
|
|
47302
49844
|
init_Pagination();
|
|
47303
49845
|
init_PatternTile();
|
|
49846
|
+
init_PlatformerBoard();
|
|
47304
49847
|
init_PlatformerCanvas();
|
|
49848
|
+
init_PlatformerTemplate();
|
|
47305
49849
|
init_Popover();
|
|
47306
49850
|
init_PositionedCanvas();
|
|
47307
49851
|
init_PowerupSlots();
|
|
@@ -47325,6 +49869,8 @@ var init_component_registry_generated = __esm({
|
|
|
47325
49869
|
init_ResourceBar();
|
|
47326
49870
|
init_ResourceCounter();
|
|
47327
49871
|
init_RichBlockEditor();
|
|
49872
|
+
init_RoguelikeBoard();
|
|
49873
|
+
init_RoguelikeTemplate();
|
|
47328
49874
|
init_RuntimeDebugger2();
|
|
47329
49875
|
init_ScaledDiagram();
|
|
47330
49876
|
init_ScoreBoard();
|
|
@@ -47397,6 +49943,8 @@ var init_component_registry_generated = __esm({
|
|
|
47397
49943
|
init_Toast();
|
|
47398
49944
|
init_ToastSlot();
|
|
47399
49945
|
init_Tooltip();
|
|
49946
|
+
init_TowerDefenseBoard();
|
|
49947
|
+
init_TowerDefenseTemplate();
|
|
47400
49948
|
init_TraitFrame();
|
|
47401
49949
|
init_TraitSlot();
|
|
47402
49950
|
init_TrendIndicator();
|
|
@@ -47441,7 +49989,7 @@ var init_component_registry_generated = __esm({
|
|
|
47441
49989
|
}
|
|
47442
49990
|
};
|
|
47443
49991
|
FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
|
|
47444
|
-
|
|
49992
|
+
GameCanvas3D2 = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
|
|
47445
49993
|
GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
47446
49994
|
GameCanvas3DCastleTemplate = lazyThree("GameCanvas3DCastleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
47447
49995
|
GameCanvas3DWorldMapTemplate = lazyThree("GameCanvas3DWorldMapTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
@@ -47560,8 +50108,9 @@ var init_component_registry_generated = __esm({
|
|
|
47560
50108
|
"FormSectionHeader": FormSectionHeader,
|
|
47561
50109
|
"GameAudioProvider": GameAudioProvider,
|
|
47562
50110
|
"GameAudioToggle": GameAudioToggle,
|
|
50111
|
+
"GameBoard3D": GameBoard3D,
|
|
47563
50112
|
"GameCanvas2D": GameCanvas2D,
|
|
47564
|
-
"GameCanvas3D":
|
|
50113
|
+
"GameCanvas3D": GameCanvas3D2,
|
|
47565
50114
|
"GameCanvas3DBattleTemplate": GameCanvas3DBattleTemplate,
|
|
47566
50115
|
"GameCanvas3DCastleTemplate": GameCanvas3DCastleTemplate,
|
|
47567
50116
|
"GameCanvas3DWorldMapTemplate": GameCanvas3DWorldMapTemplate,
|
|
@@ -47623,7 +50172,9 @@ var init_component_registry_generated = __esm({
|
|
|
47623
50172
|
"PageHeader": PageHeader,
|
|
47624
50173
|
"Pagination": Pagination,
|
|
47625
50174
|
"PatternTile": PatternTile,
|
|
50175
|
+
"PlatformerBoard": PlatformerBoard,
|
|
47626
50176
|
"PlatformerCanvas": PlatformerCanvas,
|
|
50177
|
+
"PlatformerTemplate": PlatformerTemplate,
|
|
47627
50178
|
"Popover": Popover,
|
|
47628
50179
|
"PositionedCanvas": PositionedCanvas,
|
|
47629
50180
|
"PowerupSlots": PowerupSlots,
|
|
@@ -47647,6 +50198,8 @@ var init_component_registry_generated = __esm({
|
|
|
47647
50198
|
"ResourceBar": ResourceBar,
|
|
47648
50199
|
"ResourceCounter": ResourceCounter,
|
|
47649
50200
|
"RichBlockEditor": RichBlockEditor,
|
|
50201
|
+
"RoguelikeBoard": RoguelikeBoard,
|
|
50202
|
+
"RoguelikeTemplate": RoguelikeTemplate,
|
|
47650
50203
|
"RuntimeDebugger": RuntimeDebugger,
|
|
47651
50204
|
"ScaledDiagram": ScaledDiagram,
|
|
47652
50205
|
"ScoreBoard": ScoreBoard,
|
|
@@ -47722,6 +50275,8 @@ var init_component_registry_generated = __esm({
|
|
|
47722
50275
|
"Toast": Toast,
|
|
47723
50276
|
"ToastSlot": ToastSlot,
|
|
47724
50277
|
"Tooltip": Tooltip,
|
|
50278
|
+
"TowerDefenseBoard": TowerDefenseBoard,
|
|
50279
|
+
"TowerDefenseTemplate": TowerDefenseTemplate,
|
|
47725
50280
|
"TraitFrame": TraitFrame,
|
|
47726
50281
|
"TraitSlot": TraitSlot,
|
|
47727
50282
|
"TrendIndicator": TrendIndicator,
|
|
@@ -49618,7 +52173,7 @@ function clearSchemaCache() {
|
|
|
49618
52173
|
|
|
49619
52174
|
// runtime/index.ts
|
|
49620
52175
|
init_EntitySchemaContext();
|
|
49621
|
-
var
|
|
52176
|
+
var log12 = logger.createLogger("almadar:ui:trait-provider");
|
|
49622
52177
|
var TraitContext = React82.createContext(null);
|
|
49623
52178
|
function TraitProvider({
|
|
49624
52179
|
traits: traitBindings,
|
|
@@ -49635,7 +52190,7 @@ function TraitProvider({
|
|
|
49635
52190
|
currentState: stateName,
|
|
49636
52191
|
availableEvents: trait.transitions.filter((t) => t.from === stateName).map((t) => t.event),
|
|
49637
52192
|
dispatch: (eventKey, payload) => {
|
|
49638
|
-
|
|
52193
|
+
log12.debug("Dispatch", () => ({
|
|
49639
52194
|
trait: trait.name,
|
|
49640
52195
|
event: eventKey,
|
|
49641
52196
|
payloadKeys: payload ? Object.keys(payload) : []
|
|
@@ -50219,7 +52774,7 @@ OrbitalThemeProvider.displayName = "OrbitalThemeProvider";
|
|
|
50219
52774
|
init_navigation();
|
|
50220
52775
|
init_verificationRegistry();
|
|
50221
52776
|
var PERF_NAMESPACE = "almadar:perf:canvas";
|
|
50222
|
-
var
|
|
52777
|
+
var log13 = logger.createLogger(PERF_NAMESPACE);
|
|
50223
52778
|
var RING_SIZE = 50;
|
|
50224
52779
|
var ring = [];
|
|
50225
52780
|
var writeIdx = 0;
|
|
@@ -50274,7 +52829,7 @@ function perfEnd(name, startToken, detail) {
|
|
|
50274
52829
|
}
|
|
50275
52830
|
}
|
|
50276
52831
|
push({ name, durationMs, ts: endTs, detail });
|
|
50277
|
-
|
|
52832
|
+
log13.debug(name, () => ({ durationMs, ...detail ?? {} }));
|
|
50278
52833
|
}
|
|
50279
52834
|
function perfTime(name, fn, detail) {
|
|
50280
52835
|
const t = perfStart(name);
|
|
@@ -50292,7 +52847,7 @@ var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, com
|
|
|
50292
52847
|
ts: commitTime,
|
|
50293
52848
|
detail: { baseDuration }
|
|
50294
52849
|
});
|
|
50295
|
-
|
|
52850
|
+
log13.debug(`profiler:${id}:${phase}`, () => ({ actualDuration, baseDuration }));
|
|
50296
52851
|
};
|
|
50297
52852
|
function getPerfSnapshot() {
|
|
50298
52853
|
if (ring.length < RING_SIZE) return ring.slice();
|