@almadar/ui 5.47.0 → 5.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/avl/index.cjs +2998 -443
- package/dist/avl/index.css +504 -0
- package/dist/avl/index.js +2998 -444
- package/dist/components/core/templates/index.d.ts +3 -0
- package/dist/components/game/atoms/ActionButton.d.ts +4 -1
- package/dist/components/game/atoms/ComboCounter.d.ts +4 -1
- package/dist/components/game/atoms/ControlButton.d.ts +4 -2
- package/dist/components/game/atoms/DamageNumber.d.ts +4 -1
- package/dist/components/game/atoms/ResourceCounter.d.ts +4 -1
- package/dist/components/game/atoms/ScoreDisplay.d.ts +4 -1
- package/dist/components/game/atoms/StateIndicator.d.ts +4 -7
- package/dist/components/game/atoms/StatusEffect.d.ts +4 -1
- package/dist/components/game/atoms/TurnIndicator.d.ts +4 -1
- package/dist/components/game/atoms/WaypointMarker.d.ts +4 -1
- package/dist/components/game/molecules/EnemyPlate.d.ts +4 -1
- package/dist/components/game/molecules/StatBadge.d.ts +4 -1
- package/dist/components/game/molecules/three/index.cjs +1428 -346
- package/dist/components/game/molecules/three/index.css +0 -5
- package/dist/components/game/molecules/three/index.js +1429 -347
- package/dist/components/game/organisms/CastleBoard.d.ts +10 -1
- package/dist/components/game/organisms/GameBoard3D.d.ts +62 -0
- package/dist/components/game/organisms/PlatformerBoard.d.ts +51 -0
- package/dist/components/game/organisms/RoguelikeBoard.d.ts +59 -0
- package/dist/components/game/organisms/TowerDefenseBoard.d.ts +64 -0
- package/dist/components/game/organisms/index.d.ts +4 -0
- package/dist/components/game/organisms/puzzles/event-handler/EventHandlerBoard.d.ts +9 -1
- package/dist/components/game/organisms/puzzles/state-architect/StateArchitectBoard.d.ts +14 -1
- package/dist/components/game/templates/GameCanvas3DBattleTemplate.d.ts +22 -35
- package/dist/components/game/templates/GameCanvas3DCastleTemplate.d.ts +23 -23
- package/dist/components/game/templates/GameCanvas3DWorldMapTemplate.d.ts +27 -29
- package/dist/components/game/templates/PlatformerTemplate.d.ts +26 -0
- package/dist/components/game/templates/RoguelikeTemplate.d.ts +23 -0
- package/dist/components/game/templates/TowerDefenseTemplate.d.ts +43 -0
- package/dist/components/index.cjs +2991 -422
- package/dist/components/index.css +504 -0
- package/dist/components/index.js +2985 -424
- package/dist/docs/index.css +504 -0
- package/dist/providers/index.cjs +2981 -426
- package/dist/providers/index.css +504 -0
- package/dist/providers/index.js +2981 -427
- package/dist/runtime/index.cjs +2982 -427
- package/dist/runtime/index.css +504 -0
- package/dist/runtime/index.js +2982 -428
- package/package.json +1 -1
- package/tailwind-preset.cjs +1 -0
package/dist/providers/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React83 from 'react';
|
|
2
|
-
import React83__default, { createContext, useContext, useMemo, useRef, useEffect, useCallback, Suspense, useState, useLayoutEffect, lazy, useId, useSyncExternalStore } from 'react';
|
|
2
|
+
import React83__default, { createContext, useContext, useMemo, useRef, useEffect, useCallback, Suspense, useState, useLayoutEffect, lazy, useId, useSyncExternalStore, forwardRef, useImperativeHandle, Component } from 'react';
|
|
3
3
|
import { createLogger, isLogLevelEnabled } from '@almadar/logger';
|
|
4
4
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
5
5
|
import { EventBusContext, useTraitScope, TraitScopeProvider } from '@almadar/ui/providers';
|
|
@@ -41,6 +41,12 @@ import { DndContext, pointerWithin, rectIntersection, closestCorners, useSensors
|
|
|
41
41
|
import { useSortable, arrayMove, sortableKeyboardCoordinates, SortableContext, rectSortingStrategy, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
|
42
42
|
import { CSS } from '@dnd-kit/utilities';
|
|
43
43
|
import { useNodeId, ReactFlowProvider, Handle, Position } from '@xyflow/react';
|
|
44
|
+
import * as THREE from 'three';
|
|
45
|
+
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
|
46
|
+
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
|
|
47
|
+
import { GLTFLoader as GLTFLoader$1 } from 'three/examples/jsm/loaders/GLTFLoader';
|
|
48
|
+
import { Canvas, useThree } from '@react-three/fiber';
|
|
49
|
+
import { Grid as Grid$1, OrbitControls } from '@react-three/drei';
|
|
44
50
|
import { getPatternDefinition, getComponentForPattern as getComponentForPattern$1 } from '@almadar/patterns';
|
|
45
51
|
|
|
46
52
|
var __defProp = Object.defineProperty;
|
|
@@ -673,7 +679,7 @@ var init_Box = __esm({
|
|
|
673
679
|
position,
|
|
674
680
|
className,
|
|
675
681
|
children,
|
|
676
|
-
as:
|
|
682
|
+
as: Component2 = "div",
|
|
677
683
|
action,
|
|
678
684
|
actionPayload,
|
|
679
685
|
hoverEvent,
|
|
@@ -705,7 +711,7 @@ var init_Box = __esm({
|
|
|
705
711
|
}, [hoverEvent, eventBus, onMouseLeave]);
|
|
706
712
|
const isClickable = action || onClick;
|
|
707
713
|
return React83__default.createElement(
|
|
708
|
-
|
|
714
|
+
Component2,
|
|
709
715
|
{
|
|
710
716
|
ref,
|
|
711
717
|
className: cn(
|
|
@@ -843,9 +849,9 @@ function resolvePhosphor(name, weight, family) {
|
|
|
843
849
|
(lib) => {
|
|
844
850
|
const PhosphorComp = lib[target];
|
|
845
851
|
if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
|
|
846
|
-
const
|
|
852
|
+
const Component2 = PhosphorComp;
|
|
847
853
|
const Adapter = (props) => /* @__PURE__ */ jsx(
|
|
848
|
-
|
|
854
|
+
Component2,
|
|
849
855
|
{
|
|
850
856
|
weight,
|
|
851
857
|
className: props.className,
|
|
@@ -869,9 +875,9 @@ function resolveTabler(name, family) {
|
|
|
869
875
|
(lib) => {
|
|
870
876
|
const TablerComp = lib[target];
|
|
871
877
|
if (!TablerComp || typeof TablerComp !== "object") return null;
|
|
872
|
-
const
|
|
878
|
+
const Component2 = TablerComp;
|
|
873
879
|
const Adapter = (props) => /* @__PURE__ */ jsx(
|
|
874
|
-
|
|
880
|
+
Component2,
|
|
875
881
|
{
|
|
876
882
|
stroke: props.strokeWidth ?? 1.5,
|
|
877
883
|
className: props.className,
|
|
@@ -895,9 +901,9 @@ function resolveFa(name, family) {
|
|
|
895
901
|
(lib) => {
|
|
896
902
|
const FaComp = lib[target];
|
|
897
903
|
if (!FaComp || typeof FaComp !== "function") return null;
|
|
898
|
-
const
|
|
904
|
+
const Component2 = FaComp;
|
|
899
905
|
const Adapter = (props) => /* @__PURE__ */ jsx(
|
|
900
|
-
|
|
906
|
+
Component2,
|
|
901
907
|
{
|
|
902
908
|
className: props.className,
|
|
903
909
|
style: props.style,
|
|
@@ -1852,8 +1858,8 @@ var init_Typography = __esm({
|
|
|
1852
1858
|
children
|
|
1853
1859
|
}) => {
|
|
1854
1860
|
const variant = variantProp ?? (level ? `h${level}` : "body1");
|
|
1855
|
-
const
|
|
1856
|
-
const Comp =
|
|
1861
|
+
const Component2 = as || defaultElements[variant];
|
|
1862
|
+
const Comp = Component2;
|
|
1857
1863
|
return /* @__PURE__ */ jsx(
|
|
1858
1864
|
Comp,
|
|
1859
1865
|
{
|
|
@@ -4408,10 +4414,10 @@ var init_Center = __esm({
|
|
|
4408
4414
|
className,
|
|
4409
4415
|
style,
|
|
4410
4416
|
children,
|
|
4411
|
-
as:
|
|
4417
|
+
as: Component2 = "div"
|
|
4412
4418
|
}) => {
|
|
4413
4419
|
const mergedStyle = minHeight ? { minHeight, ...style } : style;
|
|
4414
|
-
const Comp =
|
|
4420
|
+
const Comp = Component2;
|
|
4415
4421
|
return /* @__PURE__ */ jsx(
|
|
4416
4422
|
Comp,
|
|
4417
4423
|
{
|
|
@@ -5042,7 +5048,7 @@ var init_Stack = __esm({
|
|
|
5042
5048
|
className,
|
|
5043
5049
|
style,
|
|
5044
5050
|
children,
|
|
5045
|
-
as:
|
|
5051
|
+
as: Component2 = "div",
|
|
5046
5052
|
onClick,
|
|
5047
5053
|
onKeyDown,
|
|
5048
5054
|
role,
|
|
@@ -5060,7 +5066,7 @@ var init_Stack = __esm({
|
|
|
5060
5066
|
};
|
|
5061
5067
|
const isHorizontal = direction === "horizontal";
|
|
5062
5068
|
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";
|
|
5063
|
-
const Comp =
|
|
5069
|
+
const Comp = Component2;
|
|
5064
5070
|
return /* @__PURE__ */ jsx(
|
|
5065
5071
|
Comp,
|
|
5066
5072
|
{
|
|
@@ -7116,6 +7122,7 @@ var init_HealthBar = __esm({
|
|
|
7116
7122
|
}
|
|
7117
7123
|
});
|
|
7118
7124
|
function ScoreDisplay({
|
|
7125
|
+
assetUrl = DEFAULT_ASSET_URL,
|
|
7119
7126
|
value,
|
|
7120
7127
|
label,
|
|
7121
7128
|
icon,
|
|
@@ -7162,14 +7169,24 @@ function ScoreDisplay({
|
|
|
7162
7169
|
className
|
|
7163
7170
|
),
|
|
7164
7171
|
children: [
|
|
7165
|
-
|
|
7172
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
7173
|
+
"img",
|
|
7174
|
+
{
|
|
7175
|
+
src: assetUrl,
|
|
7176
|
+
alt: "",
|
|
7177
|
+
width: 20,
|
|
7178
|
+
height: 20,
|
|
7179
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7180
|
+
className: "flex-shrink-0"
|
|
7181
|
+
}
|
|
7182
|
+
) : icon ? /* @__PURE__ */ jsx("span", { className: "flex-shrink-0", children: typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon }) : /* @__PURE__ */ jsx(Icon, { icon }) }) : null,
|
|
7166
7183
|
label && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label }),
|
|
7167
7184
|
/* @__PURE__ */ jsx("span", { className: "tabular-nums", children: formattedValue })
|
|
7168
7185
|
]
|
|
7169
7186
|
}
|
|
7170
7187
|
);
|
|
7171
7188
|
}
|
|
7172
|
-
var sizeMap2;
|
|
7189
|
+
var sizeMap2, DEFAULT_ASSET_URL;
|
|
7173
7190
|
var init_ScoreDisplay = __esm({
|
|
7174
7191
|
"components/game/atoms/ScoreDisplay.tsx"() {
|
|
7175
7192
|
"use client";
|
|
@@ -7181,10 +7198,12 @@ var init_ScoreDisplay = __esm({
|
|
|
7181
7198
|
lg: "text-2xl",
|
|
7182
7199
|
xl: "text-4xl"
|
|
7183
7200
|
};
|
|
7201
|
+
DEFAULT_ASSET_URL = "https://almadar-kflow-assets.web.app/shared/effects/particles/star_01.png";
|
|
7184
7202
|
ScoreDisplay.displayName = "ScoreDisplay";
|
|
7185
7203
|
}
|
|
7186
7204
|
});
|
|
7187
7205
|
function ControlButton({
|
|
7206
|
+
assetUrl = DEFAULT_ASSET_URL2,
|
|
7188
7207
|
label,
|
|
7189
7208
|
icon,
|
|
7190
7209
|
size = "md",
|
|
@@ -7231,7 +7250,7 @@ function ControlButton({
|
|
|
7231
7250
|
},
|
|
7232
7251
|
[isPressed, releaseEvent, eventBus, onRelease]
|
|
7233
7252
|
);
|
|
7234
|
-
return /* @__PURE__ */
|
|
7253
|
+
return /* @__PURE__ */ jsx(
|
|
7235
7254
|
"button",
|
|
7236
7255
|
{
|
|
7237
7256
|
type: "button",
|
|
@@ -7252,20 +7271,27 @@ function ControlButton({
|
|
|
7252
7271
|
disabled && "opacity-50 cursor-not-allowed",
|
|
7253
7272
|
className
|
|
7254
7273
|
),
|
|
7255
|
-
children:
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7274
|
+
children: assetUrl ? /* @__PURE__ */ jsx(
|
|
7275
|
+
"img",
|
|
7276
|
+
{
|
|
7277
|
+
src: assetUrl,
|
|
7278
|
+
alt: "",
|
|
7279
|
+
width: 24,
|
|
7280
|
+
height: 24,
|
|
7281
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7282
|
+
className: "flex-shrink-0"
|
|
7283
|
+
}
|
|
7284
|
+
) : icon ? /* @__PURE__ */ jsx("span", { className: "text-2xl", children: typeof icon === "string" ? /^[a-zA-Z0-9-]+$/.test(icon) ? (() => {
|
|
7285
|
+
const I = resolveIcon(icon);
|
|
7286
|
+
return I ? /* @__PURE__ */ jsx(I, { className: "w-6 h-6" }) : null;
|
|
7287
|
+
})() : icon : /* @__PURE__ */ (() => {
|
|
7288
|
+
const I = icon;
|
|
7289
|
+
return /* @__PURE__ */ jsx(I, { className: "w-6 h-6" });
|
|
7290
|
+
})() }) : label ? /* @__PURE__ */ jsx("span", { children: label }) : null
|
|
7265
7291
|
}
|
|
7266
7292
|
);
|
|
7267
7293
|
}
|
|
7268
|
-
var sizeMap3, shapeMap, variantMap;
|
|
7294
|
+
var sizeMap3, shapeMap, variantMap, DEFAULT_ASSET_URL2;
|
|
7269
7295
|
var init_ControlButton = __esm({
|
|
7270
7296
|
"components/game/atoms/ControlButton.tsx"() {
|
|
7271
7297
|
"use client";
|
|
@@ -7288,6 +7314,7 @@ var init_ControlButton = __esm({
|
|
|
7288
7314
|
secondary: "bg-secondary text-secondary-foreground border-border hover:bg-secondary-hover",
|
|
7289
7315
|
ghost: "bg-transparent text-foreground border-border hover:bg-muted"
|
|
7290
7316
|
};
|
|
7317
|
+
DEFAULT_ASSET_URL2 = "https://almadar-kflow-assets.web.app/shared/effects/particles/circle_01.png";
|
|
7291
7318
|
ControlButton.displayName = "ControlButton";
|
|
7292
7319
|
}
|
|
7293
7320
|
});
|
|
@@ -7367,6 +7394,7 @@ var init_Sprite = __esm({
|
|
|
7367
7394
|
}
|
|
7368
7395
|
});
|
|
7369
7396
|
function StateIndicator({
|
|
7397
|
+
assetUrl = DEFAULT_ASSET_URL3,
|
|
7370
7398
|
state = "idle",
|
|
7371
7399
|
label,
|
|
7372
7400
|
size = "md",
|
|
@@ -7389,18 +7417,29 @@ function StateIndicator({
|
|
|
7389
7417
|
className
|
|
7390
7418
|
),
|
|
7391
7419
|
children: [
|
|
7392
|
-
/* @__PURE__ */ jsx(Box, { as: "span", children:
|
|
7420
|
+
/* @__PURE__ */ jsx(Box, { as: "span", children: assetUrl ? /* @__PURE__ */ jsx(
|
|
7421
|
+
"img",
|
|
7422
|
+
{
|
|
7423
|
+
src: assetUrl,
|
|
7424
|
+
alt: displayLabel,
|
|
7425
|
+
width: 16,
|
|
7426
|
+
height: 16,
|
|
7427
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7428
|
+
className: "flex-shrink-0"
|
|
7429
|
+
}
|
|
7430
|
+
) : typeof config.icon === "string" ? /^[a-zA-Z0-9-]+$/.test(config.icon) ? /* @__PURE__ */ jsx(Icon, { name: config.icon }) : config.icon : /* @__PURE__ */ jsx(Icon, { icon: config.icon }) }),
|
|
7393
7431
|
/* @__PURE__ */ jsx(Box, { as: "span", children: displayLabel })
|
|
7394
7432
|
]
|
|
7395
7433
|
}
|
|
7396
7434
|
);
|
|
7397
7435
|
}
|
|
7398
|
-
var DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
7436
|
+
var DEFAULT_ASSET_URL3, DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
7399
7437
|
var init_StateIndicator = __esm({
|
|
7400
7438
|
"components/game/atoms/StateIndicator.tsx"() {
|
|
7401
7439
|
init_Box();
|
|
7402
7440
|
init_Icon();
|
|
7403
7441
|
init_cn();
|
|
7442
|
+
DEFAULT_ASSET_URL3 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
7404
7443
|
DEFAULT_STATE_STYLES = {
|
|
7405
7444
|
idle: { icon: "\u23F8", bgClass: "bg-muted" },
|
|
7406
7445
|
active: { icon: "\u25B6", bgClass: "bg-success" },
|
|
@@ -7475,6 +7514,7 @@ var init_TimerDisplay = __esm({
|
|
|
7475
7514
|
}
|
|
7476
7515
|
});
|
|
7477
7516
|
function ResourceCounter({
|
|
7517
|
+
assetUrl = DEFAULT_ASSET_URL4,
|
|
7478
7518
|
icon,
|
|
7479
7519
|
label = "Gold",
|
|
7480
7520
|
value = 250,
|
|
@@ -7494,7 +7534,17 @@ function ResourceCounter({
|
|
|
7494
7534
|
className
|
|
7495
7535
|
),
|
|
7496
7536
|
children: [
|
|
7497
|
-
|
|
7537
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
7538
|
+
"img",
|
|
7539
|
+
{
|
|
7540
|
+
src: assetUrl,
|
|
7541
|
+
alt: label,
|
|
7542
|
+
width: sizes.img,
|
|
7543
|
+
height: sizes.img,
|
|
7544
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7545
|
+
className: "flex-shrink-0"
|
|
7546
|
+
}
|
|
7547
|
+
) : icon ? /* @__PURE__ */ jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon }) : /* @__PURE__ */ jsx(Icon, { icon }) }) : null,
|
|
7498
7548
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label }),
|
|
7499
7549
|
/* @__PURE__ */ jsxs("span", { className: cn("font-bold tabular-nums", color && (color in colorTokenClasses2 ? colorTokenClasses2[color] : color)), children: [
|
|
7500
7550
|
value,
|
|
@@ -7507,7 +7557,7 @@ function ResourceCounter({
|
|
|
7507
7557
|
}
|
|
7508
7558
|
);
|
|
7509
7559
|
}
|
|
7510
|
-
var colorTokenClasses2, sizeMap5;
|
|
7560
|
+
var colorTokenClasses2, DEFAULT_ASSET_URL4, sizeMap5;
|
|
7511
7561
|
var init_ResourceCounter = __esm({
|
|
7512
7562
|
"components/game/atoms/ResourceCounter.tsx"() {
|
|
7513
7563
|
init_cn();
|
|
@@ -7520,16 +7570,17 @@ var init_ResourceCounter = __esm({
|
|
|
7520
7570
|
error: "text-error",
|
|
7521
7571
|
muted: "text-muted-foreground"
|
|
7522
7572
|
};
|
|
7573
|
+
DEFAULT_ASSET_URL4 = "https://almadar-kflow-assets.web.app/shared/world-map/gold_mine.png";
|
|
7523
7574
|
sizeMap5 = {
|
|
7524
|
-
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm" },
|
|
7525
|
-
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base" },
|
|
7526
|
-
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg" }
|
|
7575
|
+
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm", img: 16 },
|
|
7576
|
+
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base", img: 20 },
|
|
7577
|
+
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg", img: 28 }
|
|
7527
7578
|
};
|
|
7528
7579
|
ResourceCounter.displayName = "ResourceCounter";
|
|
7529
7580
|
}
|
|
7530
7581
|
});
|
|
7531
7582
|
function ItemSlot({
|
|
7532
|
-
assetUrl =
|
|
7583
|
+
assetUrl = DEFAULT_ASSET_URL5,
|
|
7533
7584
|
icon = "sword",
|
|
7534
7585
|
label = "Iron Sword",
|
|
7535
7586
|
quantity,
|
|
@@ -7588,7 +7639,7 @@ function ItemSlot({
|
|
|
7588
7639
|
}
|
|
7589
7640
|
);
|
|
7590
7641
|
}
|
|
7591
|
-
var sizeMap6, rarityBorderMap, rarityGlowMap,
|
|
7642
|
+
var sizeMap6, rarityBorderMap, rarityGlowMap, DEFAULT_ASSET_URL5, assetSizeMap;
|
|
7592
7643
|
var init_ItemSlot = __esm({
|
|
7593
7644
|
"components/game/atoms/ItemSlot.tsx"() {
|
|
7594
7645
|
"use client";
|
|
@@ -7613,7 +7664,7 @@ var init_ItemSlot = __esm({
|
|
|
7613
7664
|
epic: "shadow-lg",
|
|
7614
7665
|
legendary: "shadow-lg"
|
|
7615
7666
|
};
|
|
7616
|
-
|
|
7667
|
+
DEFAULT_ASSET_URL5 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
7617
7668
|
assetSizeMap = {
|
|
7618
7669
|
sm: 28,
|
|
7619
7670
|
md: 40,
|
|
@@ -7623,6 +7674,7 @@ var init_ItemSlot = __esm({
|
|
|
7623
7674
|
}
|
|
7624
7675
|
});
|
|
7625
7676
|
function TurnIndicator({
|
|
7677
|
+
assetUrl = DEFAULT_ASSET_URL6,
|
|
7626
7678
|
currentTurn = 1,
|
|
7627
7679
|
maxTurns,
|
|
7628
7680
|
activeTeam,
|
|
@@ -7655,14 +7707,24 @@ function TurnIndicator({
|
|
|
7655
7707
|
] }),
|
|
7656
7708
|
activeTeam && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7657
7709
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "|" }),
|
|
7658
|
-
/* @__PURE__ */ jsx(
|
|
7710
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
7711
|
+
"img",
|
|
7712
|
+
{
|
|
7713
|
+
src: assetUrl,
|
|
7714
|
+
alt: "",
|
|
7715
|
+
width: 12,
|
|
7716
|
+
height: 12,
|
|
7717
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7718
|
+
className: "flex-shrink-0"
|
|
7719
|
+
}
|
|
7720
|
+
) : /* @__PURE__ */ jsx("span", { className: cn("rounded-full bg-success/20", sizes.dot) }),
|
|
7659
7721
|
/* @__PURE__ */ jsx("span", { className: "text-success", children: activeTeam })
|
|
7660
7722
|
] })
|
|
7661
7723
|
]
|
|
7662
7724
|
}
|
|
7663
7725
|
);
|
|
7664
7726
|
}
|
|
7665
|
-
var sizeMap7;
|
|
7727
|
+
var sizeMap7, DEFAULT_ASSET_URL6;
|
|
7666
7728
|
var init_TurnIndicator = __esm({
|
|
7667
7729
|
"components/game/atoms/TurnIndicator.tsx"() {
|
|
7668
7730
|
init_cn();
|
|
@@ -7671,6 +7733,7 @@ var init_TurnIndicator = __esm({
|
|
|
7671
7733
|
md: { wrapper: "text-sm gap-2 px-3 py-1", dot: "w-2 h-2" },
|
|
7672
7734
|
lg: { wrapper: "text-base gap-2.5 px-4 py-1.5", dot: "w-2.5 h-2.5" }
|
|
7673
7735
|
};
|
|
7736
|
+
DEFAULT_ASSET_URL6 = "https://almadar-kflow-assets.web.app/shared/effects/particles/symbol_01.png";
|
|
7674
7737
|
TurnIndicator.displayName = "TurnIndicator";
|
|
7675
7738
|
}
|
|
7676
7739
|
});
|
|
@@ -7686,6 +7749,7 @@ function getComboScale(combo) {
|
|
|
7686
7749
|
return "";
|
|
7687
7750
|
}
|
|
7688
7751
|
function ComboCounter({
|
|
7752
|
+
assetUrl = DEFAULT_ASSET_URL7,
|
|
7689
7753
|
combo = 5,
|
|
7690
7754
|
multiplier,
|
|
7691
7755
|
streak,
|
|
@@ -7705,6 +7769,17 @@ function ComboCounter({
|
|
|
7705
7769
|
className
|
|
7706
7770
|
),
|
|
7707
7771
|
children: [
|
|
7772
|
+
assetUrl && /* @__PURE__ */ jsx(
|
|
7773
|
+
"img",
|
|
7774
|
+
{
|
|
7775
|
+
src: assetUrl,
|
|
7776
|
+
alt: "combo",
|
|
7777
|
+
width: 24,
|
|
7778
|
+
height: 24,
|
|
7779
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7780
|
+
className: "flex-shrink-0 mb-0.5"
|
|
7781
|
+
}
|
|
7782
|
+
),
|
|
7708
7783
|
/* @__PURE__ */ jsx("span", { className: cn("font-black tabular-nums leading-none", sizes.combo, getComboIntensity(combo)), children: combo }),
|
|
7709
7784
|
/* @__PURE__ */ jsx("span", { className: cn("font-bold uppercase tracking-wider text-muted-foreground", sizes.label), children: "combo" }),
|
|
7710
7785
|
multiplier != null && multiplier > 1 && /* @__PURE__ */ jsxs("span", { className: cn("font-bold text-warning tabular-nums", sizes.multiplier), children: [
|
|
@@ -7719,10 +7794,11 @@ function ComboCounter({
|
|
|
7719
7794
|
}
|
|
7720
7795
|
);
|
|
7721
7796
|
}
|
|
7722
|
-
var sizeMap8;
|
|
7797
|
+
var DEFAULT_ASSET_URL7, sizeMap8;
|
|
7723
7798
|
var init_ComboCounter = __esm({
|
|
7724
7799
|
"components/game/atoms/ComboCounter.tsx"() {
|
|
7725
7800
|
init_cn();
|
|
7801
|
+
DEFAULT_ASSET_URL7 = "https://almadar-kflow-assets.web.app/shared/effects/flash/flash00.png";
|
|
7726
7802
|
sizeMap8 = {
|
|
7727
7803
|
sm: { combo: "text-lg", label: "text-xs", multiplier: "text-xs" },
|
|
7728
7804
|
md: { combo: "text-2xl", label: "text-xs", multiplier: "text-sm" },
|
|
@@ -7816,6 +7892,7 @@ var init_XPBar = __esm({
|
|
|
7816
7892
|
}
|
|
7817
7893
|
});
|
|
7818
7894
|
function WaypointMarker({
|
|
7895
|
+
assetUrl = DEFAULT_ASSET_URL8,
|
|
7819
7896
|
label,
|
|
7820
7897
|
icon,
|
|
7821
7898
|
active = true,
|
|
@@ -7854,7 +7931,17 @@ function WaypointMarker({
|
|
|
7854
7931
|
active && !completed && "bg-info text-foreground",
|
|
7855
7932
|
!active && !completed && "bg-muted"
|
|
7856
7933
|
),
|
|
7857
|
-
children: completed ? checkIcon :
|
|
7934
|
+
children: completed ? checkIcon : assetUrl ? /* @__PURE__ */ jsx(
|
|
7935
|
+
"img",
|
|
7936
|
+
{
|
|
7937
|
+
src: assetUrl,
|
|
7938
|
+
alt: label,
|
|
7939
|
+
width: sizes.img,
|
|
7940
|
+
height: sizes.img,
|
|
7941
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7942
|
+
className: "flex-shrink-0"
|
|
7943
|
+
}
|
|
7944
|
+
) : icon ? typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon }) : /* @__PURE__ */ jsx(Icon, { icon }) : null
|
|
7858
7945
|
}
|
|
7859
7946
|
)
|
|
7860
7947
|
] }),
|
|
@@ -7871,15 +7958,16 @@ function WaypointMarker({
|
|
|
7871
7958
|
)
|
|
7872
7959
|
] });
|
|
7873
7960
|
}
|
|
7874
|
-
var sizeMap10, checkIcon;
|
|
7961
|
+
var DEFAULT_ASSET_URL8, sizeMap10, checkIcon;
|
|
7875
7962
|
var init_WaypointMarker = __esm({
|
|
7876
7963
|
"components/game/atoms/WaypointMarker.tsx"() {
|
|
7877
7964
|
init_cn();
|
|
7878
7965
|
init_Icon();
|
|
7966
|
+
DEFAULT_ASSET_URL8 = "https://almadar-kflow-assets.web.app/shared/world-map/battle_marker.png";
|
|
7879
7967
|
sizeMap10 = {
|
|
7880
|
-
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1" },
|
|
7881
|
-
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5" },
|
|
7882
|
-
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2" }
|
|
7968
|
+
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1", img: 12 },
|
|
7969
|
+
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5", img: 18 },
|
|
7970
|
+
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2", img: 24 }
|
|
7883
7971
|
};
|
|
7884
7972
|
checkIcon = /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 3, className: "w-3/5 h-3/5", children: /* @__PURE__ */ jsx("path", { d: "M5 13l4 4L19 7", strokeLinecap: "round", strokeLinejoin: "round" }) });
|
|
7885
7973
|
WaypointMarker.displayName = "WaypointMarker";
|
|
@@ -7892,6 +7980,7 @@ function formatDuration(seconds) {
|
|
|
7892
7980
|
return `${Math.round(seconds)}s`;
|
|
7893
7981
|
}
|
|
7894
7982
|
function StatusEffect({
|
|
7983
|
+
assetUrl = DEFAULT_ASSET_URL9,
|
|
7895
7984
|
icon = "shield",
|
|
7896
7985
|
label = "Shield",
|
|
7897
7986
|
duration = 30,
|
|
@@ -7912,7 +8001,17 @@ function StatusEffect({
|
|
|
7912
8001
|
),
|
|
7913
8002
|
title: label,
|
|
7914
8003
|
children: [
|
|
7915
|
-
/* @__PURE__ */ jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children:
|
|
8004
|
+
/* @__PURE__ */ jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children: assetUrl ? /* @__PURE__ */ jsx(
|
|
8005
|
+
"img",
|
|
8006
|
+
{
|
|
8007
|
+
src: assetUrl,
|
|
8008
|
+
alt: label,
|
|
8009
|
+
width: sizes.img,
|
|
8010
|
+
height: sizes.img,
|
|
8011
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
8012
|
+
className: "flex-shrink-0"
|
|
8013
|
+
}
|
|
8014
|
+
) : typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, size: "sm" }) : /* @__PURE__ */ jsx(Icon, { icon, size: "sm" }) }),
|
|
7916
8015
|
duration !== void 0 && /* @__PURE__ */ jsx(
|
|
7917
8016
|
"span",
|
|
7918
8017
|
{
|
|
@@ -7939,15 +8038,16 @@ function StatusEffect({
|
|
|
7939
8038
|
label && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground mt-0.5 text-center whitespace-nowrap", children: label })
|
|
7940
8039
|
] });
|
|
7941
8040
|
}
|
|
7942
|
-
var sizeMap11, variantStyles7;
|
|
8041
|
+
var DEFAULT_ASSET_URL9, sizeMap11, variantStyles7;
|
|
7943
8042
|
var init_StatusEffect = __esm({
|
|
7944
8043
|
"components/game/atoms/StatusEffect.tsx"() {
|
|
7945
8044
|
init_cn();
|
|
7946
8045
|
init_Icon();
|
|
8046
|
+
DEFAULT_ASSET_URL9 = "https://almadar-kflow-assets.web.app/shared/effects/particles/flame_01.png";
|
|
7947
8047
|
sizeMap11 = {
|
|
7948
|
-
sm: { container: "w-8 h-8", icon: "text-sm", badge: "text-xs -top-1 -right-1 w-4 h-4", timer: "text-[9px]" },
|
|
7949
|
-
md: { container: "w-10 h-10", icon: "text-base", badge: "text-xs -top-1 -right-1 w-5 h-5", timer: "text-xs" },
|
|
7950
|
-
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" }
|
|
8048
|
+
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 },
|
|
8049
|
+
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 },
|
|
8050
|
+
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 }
|
|
7951
8051
|
};
|
|
7952
8052
|
variantStyles7 = {
|
|
7953
8053
|
buff: "border-success bg-success/20",
|
|
@@ -7958,6 +8058,7 @@ var init_StatusEffect = __esm({
|
|
|
7958
8058
|
}
|
|
7959
8059
|
});
|
|
7960
8060
|
function DamageNumber({
|
|
8061
|
+
assetUrl = DEFAULT_ASSET_URL10,
|
|
7961
8062
|
value = 42,
|
|
7962
8063
|
type = "damage",
|
|
7963
8064
|
size = "md",
|
|
@@ -7966,22 +8067,35 @@ function DamageNumber({
|
|
|
7966
8067
|
const displayText = type === "miss" ? "MISS" : type === "heal" ? `+${value}` : `${value}`;
|
|
7967
8068
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7968
8069
|
/* @__PURE__ */ jsx("style", { children: floatKeyframes }),
|
|
7969
|
-
/* @__PURE__ */
|
|
8070
|
+
/* @__PURE__ */ jsxs(
|
|
7970
8071
|
"span",
|
|
7971
8072
|
{
|
|
7972
8073
|
className: cn(
|
|
7973
|
-
"inline-
|
|
8074
|
+
"inline-flex items-center gap-0.5 font-mono select-none pointer-events-none",
|
|
7974
8075
|
sizeMap12[size],
|
|
7975
8076
|
typeStyles[type],
|
|
7976
8077
|
className
|
|
7977
8078
|
),
|
|
7978
8079
|
style: { animation: "damageFloat 1s ease-out forwards" },
|
|
7979
|
-
children:
|
|
8080
|
+
children: [
|
|
8081
|
+
assetUrl && /* @__PURE__ */ jsx(
|
|
8082
|
+
"img",
|
|
8083
|
+
{
|
|
8084
|
+
src: assetUrl,
|
|
8085
|
+
alt: "",
|
|
8086
|
+
width: 14,
|
|
8087
|
+
height: 14,
|
|
8088
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
8089
|
+
className: "flex-shrink-0"
|
|
8090
|
+
}
|
|
8091
|
+
),
|
|
8092
|
+
displayText
|
|
8093
|
+
]
|
|
7980
8094
|
}
|
|
7981
8095
|
)
|
|
7982
8096
|
] });
|
|
7983
8097
|
}
|
|
7984
|
-
var sizeMap12, typeStyles, floatKeyframes;
|
|
8098
|
+
var sizeMap12, typeStyles, floatKeyframes, DEFAULT_ASSET_URL10;
|
|
7985
8099
|
var init_DamageNumber = __esm({
|
|
7986
8100
|
"components/game/atoms/DamageNumber.tsx"() {
|
|
7987
8101
|
init_cn();
|
|
@@ -8003,6 +8117,7 @@ var init_DamageNumber = __esm({
|
|
|
8003
8117
|
100% { opacity: 0; transform: translateY(-32px) scale(0.8); }
|
|
8004
8118
|
}
|
|
8005
8119
|
`;
|
|
8120
|
+
DEFAULT_ASSET_URL10 = "https://almadar-kflow-assets.web.app/shared/effects/particles/spark_01.png";
|
|
8006
8121
|
DamageNumber.displayName = "DamageNumber";
|
|
8007
8122
|
}
|
|
8008
8123
|
});
|
|
@@ -8091,6 +8206,7 @@ var init_ChoiceButton = __esm({
|
|
|
8091
8206
|
}
|
|
8092
8207
|
});
|
|
8093
8208
|
function ActionButton({
|
|
8209
|
+
assetUrl = DEFAULT_ASSET_URL11,
|
|
8094
8210
|
label = "Attack",
|
|
8095
8211
|
icon,
|
|
8096
8212
|
cooldown = 0,
|
|
@@ -8130,13 +8246,23 @@ function ActionButton({
|
|
|
8130
8246
|
}
|
|
8131
8247
|
}
|
|
8132
8248
|
),
|
|
8133
|
-
|
|
8249
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
8250
|
+
"img",
|
|
8251
|
+
{
|
|
8252
|
+
src: assetUrl,
|
|
8253
|
+
alt: "",
|
|
8254
|
+
width: 16,
|
|
8255
|
+
height: 16,
|
|
8256
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
8257
|
+
className: cn("flex-shrink-0", sizes.icon)
|
|
8258
|
+
}
|
|
8259
|
+
) : icon ? /* @__PURE__ */ jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? (() => {
|
|
8134
8260
|
const I = resolveIcon(icon);
|
|
8135
8261
|
return I ? /* @__PURE__ */ jsx(I, { className: "w-4 h-4" }) : null;
|
|
8136
8262
|
})() : /* @__PURE__ */ (() => {
|
|
8137
8263
|
const I = icon;
|
|
8138
8264
|
return /* @__PURE__ */ jsx(I, { className: "w-4 h-4" });
|
|
8139
|
-
})() }),
|
|
8265
|
+
})() }) : null,
|
|
8140
8266
|
/* @__PURE__ */ jsx("span", { className: "relative z-10", children: label }),
|
|
8141
8267
|
hotkey && /* @__PURE__ */ jsx(
|
|
8142
8268
|
"span",
|
|
@@ -8152,7 +8278,7 @@ function ActionButton({
|
|
|
8152
8278
|
}
|
|
8153
8279
|
);
|
|
8154
8280
|
}
|
|
8155
|
-
var sizeMap13, variantStyles8;
|
|
8281
|
+
var sizeMap13, variantStyles8, DEFAULT_ASSET_URL11;
|
|
8156
8282
|
var init_ActionButton = __esm({
|
|
8157
8283
|
"components/game/atoms/ActionButton.tsx"() {
|
|
8158
8284
|
init_cn();
|
|
@@ -8167,6 +8293,7 @@ var init_ActionButton = __esm({
|
|
|
8167
8293
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary-hover border-border",
|
|
8168
8294
|
danger: "bg-error text-error-foreground hover:bg-error/90 border-error"
|
|
8169
8295
|
};
|
|
8296
|
+
DEFAULT_ASSET_URL11 = "https://almadar-kflow-assets.web.app/shared/effects/particles/slash_01.png";
|
|
8170
8297
|
ActionButton.displayName = "ActionButton";
|
|
8171
8298
|
}
|
|
8172
8299
|
});
|
|
@@ -9035,10 +9162,10 @@ var init_Container = __esm({
|
|
|
9035
9162
|
center = true,
|
|
9036
9163
|
className,
|
|
9037
9164
|
children,
|
|
9038
|
-
as:
|
|
9165
|
+
as: Component2 = "div"
|
|
9039
9166
|
}) => {
|
|
9040
9167
|
const resolvedSize = maxWidth ?? size ?? "lg";
|
|
9041
|
-
const Comp =
|
|
9168
|
+
const Comp = Component2;
|
|
9042
9169
|
return /* @__PURE__ */ jsx(
|
|
9043
9170
|
Comp,
|
|
9044
9171
|
{
|
|
@@ -11860,15 +11987,15 @@ function BattleBoard({
|
|
|
11860
11987
|
const board = boardEntity(entity) ?? {};
|
|
11861
11988
|
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
|
|
11862
11989
|
const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
|
|
11863
|
-
const boardWidth = num(board.boardWidth, 8);
|
|
11864
|
-
const boardHeight = num(board.boardHeight, 6);
|
|
11990
|
+
const boardWidth = num(board.gridWidth ?? board.boardWidth, 8);
|
|
11991
|
+
const boardHeight = num(board.gridHeight ?? board.boardHeight, 6);
|
|
11865
11992
|
const assetManifest = propAssetManifest ?? board.assetManifest;
|
|
11866
11993
|
const backgroundImage = board.backgroundImage;
|
|
11867
11994
|
const units = rows(board.units);
|
|
11868
11995
|
const selectedUnitId = board.selectedUnitId ?? null;
|
|
11869
11996
|
const currentPhase = str(board.phase) || "observation";
|
|
11870
11997
|
const currentTurn = num(board.turn, 1);
|
|
11871
|
-
const gameResult = board.
|
|
11998
|
+
const gameResult = board.result ?? null;
|
|
11872
11999
|
const eventBus = useEventBus();
|
|
11873
12000
|
const { t } = useTranslate();
|
|
11874
12001
|
const [hoveredTile, setHoveredTile] = useState(null);
|
|
@@ -11889,7 +12016,7 @@ function BattleBoard({
|
|
|
11889
12016
|
const validMoves = useMemo(() => {
|
|
11890
12017
|
if (!selectedUnit || currentPhase !== "movement") return [];
|
|
11891
12018
|
const moves = [];
|
|
11892
|
-
const range = num(
|
|
12019
|
+
const range = num(board.movementRange, 2);
|
|
11893
12020
|
const origin = unitPosition(selectedUnit);
|
|
11894
12021
|
for (let dy = -range; dy <= range; dy++) {
|
|
11895
12022
|
for (let dx = -range; dx <= range; dx++) {
|
|
@@ -20174,15 +20301,24 @@ function CastleBoard({
|
|
|
20174
20301
|
featureClickEvent,
|
|
20175
20302
|
unitClickEvent,
|
|
20176
20303
|
tileClickEvent,
|
|
20304
|
+
playAgainEvent,
|
|
20177
20305
|
className
|
|
20178
20306
|
}) {
|
|
20179
20307
|
const eventBus = useEventBus();
|
|
20308
|
+
const { t } = useTranslate();
|
|
20180
20309
|
const resolved = boardEntity(entity);
|
|
20181
20310
|
const tiles = propTiles ?? (Array.isArray(resolved?.tiles) ? resolved.tiles : []);
|
|
20182
20311
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
20183
20312
|
const units = propUnits ?? (Array.isArray(resolved?.units) ? resolved.units : []);
|
|
20184
20313
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
20185
20314
|
const backgroundImage = resolved?.backgroundImage;
|
|
20315
|
+
const gold = num(resolved?.gold);
|
|
20316
|
+
const health = num(resolved?.health);
|
|
20317
|
+
const maxHealth = num(resolved?.maxHealth);
|
|
20318
|
+
const wave = num(resolved?.wave);
|
|
20319
|
+
const tickCount = num(resolved?.tickCount);
|
|
20320
|
+
const buildings = rows(resolved?.buildings);
|
|
20321
|
+
const result = str(resolved?.result) || "none";
|
|
20186
20322
|
const [hoveredTile, setHoveredTile] = useState(null);
|
|
20187
20323
|
const [selectedFeature, setSelectedFeature] = useState(null);
|
|
20188
20324
|
const hoveredFeature = useMemo(() => {
|
|
@@ -20195,7 +20331,7 @@ function CastleBoard({
|
|
|
20195
20331
|
(u) => u.position?.x === hoveredTile.x && u.position?.y === hoveredTile.y
|
|
20196
20332
|
) ?? null;
|
|
20197
20333
|
}, [hoveredTile, units]);
|
|
20198
|
-
const maxY = Math.max(...tiles.map((
|
|
20334
|
+
const maxY = Math.max(...tiles.map((t2) => t2.y), 0);
|
|
20199
20335
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
20200
20336
|
const tileToScreen = useCallback(
|
|
20201
20337
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
@@ -20230,6 +20366,11 @@ function CastleBoard({
|
|
|
20230
20366
|
}
|
|
20231
20367
|
}, [units, onUnitClick, unitClickEvent, eventBus]);
|
|
20232
20368
|
const clearSelection = useCallback(() => setSelectedFeature(null), []);
|
|
20369
|
+
const handlePlayAgain = useCallback(() => {
|
|
20370
|
+
if (playAgainEvent) {
|
|
20371
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
20372
|
+
}
|
|
20373
|
+
}, [playAgainEvent, eventBus]);
|
|
20233
20374
|
const ctx = useMemo(
|
|
20234
20375
|
() => ({
|
|
20235
20376
|
hoveredTile,
|
|
@@ -20238,11 +20379,18 @@ function CastleBoard({
|
|
|
20238
20379
|
selectedFeature,
|
|
20239
20380
|
clearSelection,
|
|
20240
20381
|
tileToScreen,
|
|
20241
|
-
scale
|
|
20382
|
+
scale,
|
|
20383
|
+
gold,
|
|
20384
|
+
health,
|
|
20385
|
+
maxHealth,
|
|
20386
|
+
wave,
|
|
20387
|
+
tickCount,
|
|
20388
|
+
buildings,
|
|
20389
|
+
result
|
|
20242
20390
|
}),
|
|
20243
|
-
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale]
|
|
20391
|
+
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale, gold, health, maxHealth, wave, tickCount, buildings, result]
|
|
20244
20392
|
);
|
|
20245
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("castle-board min-h-screen flex flex-col bg-background", className), children: [
|
|
20393
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("castle-board relative min-h-screen flex flex-col bg-background", className), children: [
|
|
20246
20394
|
header && header(ctx),
|
|
20247
20395
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
|
|
20248
20396
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-auto p-4 relative", children: [
|
|
@@ -20267,7 +20415,29 @@ function CastleBoard({
|
|
|
20267
20415
|
] }),
|
|
20268
20416
|
sidePanel && /* @__PURE__ */ jsx("div", { className: "w-96 shrink-0 border-l border-border bg-surface overflow-y-auto", children: sidePanel(ctx) })
|
|
20269
20417
|
] }),
|
|
20270
|
-
footer && footer(ctx)
|
|
20418
|
+
footer && footer(ctx),
|
|
20419
|
+
result !== "none" && /* @__PURE__ */ jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
20420
|
+
/* @__PURE__ */ jsx(
|
|
20421
|
+
Typography,
|
|
20422
|
+
{
|
|
20423
|
+
variant: "h2",
|
|
20424
|
+
className: cn(
|
|
20425
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
20426
|
+
result === "victory" ? "text-warning" : "text-error"
|
|
20427
|
+
),
|
|
20428
|
+
children: result === "victory" ? t("battle.victory") : t("battle.defeat")
|
|
20429
|
+
}
|
|
20430
|
+
),
|
|
20431
|
+
/* @__PURE__ */ jsx(
|
|
20432
|
+
Button,
|
|
20433
|
+
{
|
|
20434
|
+
variant: "primary",
|
|
20435
|
+
className: "px-8 py-3 font-semibold",
|
|
20436
|
+
onClick: handlePlayAgain,
|
|
20437
|
+
children: t("battle.playAgain")
|
|
20438
|
+
}
|
|
20439
|
+
)
|
|
20440
|
+
] }) })
|
|
20271
20441
|
] });
|
|
20272
20442
|
}
|
|
20273
20443
|
var init_CastleBoard = __esm({
|
|
@@ -20275,6 +20445,10 @@ var init_CastleBoard = __esm({
|
|
|
20275
20445
|
"use client";
|
|
20276
20446
|
init_cn();
|
|
20277
20447
|
init_useEventBus();
|
|
20448
|
+
init_Box();
|
|
20449
|
+
init_Button();
|
|
20450
|
+
init_Typography();
|
|
20451
|
+
init_Stack();
|
|
20278
20452
|
init_IsometricCanvas();
|
|
20279
20453
|
init_boardEntity();
|
|
20280
20454
|
init_isometric();
|
|
@@ -21151,22 +21325,16 @@ function ClassifierBoard({
|
|
|
21151
21325
|
const { emit } = useEventBus();
|
|
21152
21326
|
const { t } = useTranslate();
|
|
21153
21327
|
const resolved = boardEntity(entity);
|
|
21154
|
-
const [localAssignments, setLocalAssignments] = useState({});
|
|
21155
21328
|
const [headerError, setHeaderError] = useState(false);
|
|
21156
|
-
const [localSubmitted, setLocalSubmitted] = useState(false);
|
|
21157
|
-
const [localAttempts, setLocalAttempts] = useState(0);
|
|
21158
|
-
const [showHint, setShowHint] = useState(false);
|
|
21159
21329
|
const items = Array.isArray(resolved?.items) ? resolved.items : [];
|
|
21160
21330
|
const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
|
|
21161
|
-
const
|
|
21162
|
-
const
|
|
21163
|
-
const
|
|
21164
|
-
const assignments =
|
|
21165
|
-
if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
|
|
21331
|
+
const result = str(resolved?.result);
|
|
21332
|
+
const submitted = result === "win";
|
|
21333
|
+
const attempts = num(resolved?.attempts);
|
|
21334
|
+
const assignments = items.reduce((acc, item) => {
|
|
21335
|
+
if (item.assignedCategory != null && item.assignedCategory !== "") acc[item.id] = item.assignedCategory;
|
|
21166
21336
|
return acc;
|
|
21167
|
-
}, {})
|
|
21168
|
-
const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
|
|
21169
|
-
const submitted = entityDrivesResult || localSubmitted;
|
|
21337
|
+
}, {});
|
|
21170
21338
|
const unassignedItems = items.filter((item) => !assignments[item.id]);
|
|
21171
21339
|
const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
|
|
21172
21340
|
const results = submitted ? items.map((item) => ({
|
|
@@ -21174,54 +21342,25 @@ function ClassifierBoard({
|
|
|
21174
21342
|
assigned: assignments[item.id],
|
|
21175
21343
|
correct: assignments[item.id] === item.correctCategory
|
|
21176
21344
|
})) : [];
|
|
21177
|
-
const allCorrect =
|
|
21345
|
+
const allCorrect = result === "win";
|
|
21178
21346
|
const correctCount = results.filter((r) => r.correct).length;
|
|
21347
|
+
const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
|
|
21179
21348
|
const handleAssign = (itemId, categoryId) => {
|
|
21180
21349
|
if (submitted) return;
|
|
21181
|
-
if (assignEvent) {
|
|
21182
|
-
emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
21183
|
-
}
|
|
21184
|
-
if (!entityHasAssignments) {
|
|
21185
|
-
setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
|
|
21186
|
-
}
|
|
21350
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
21187
21351
|
};
|
|
21188
21352
|
const handleUnassign = (itemId) => {
|
|
21189
21353
|
if (submitted) return;
|
|
21190
|
-
if (
|
|
21191
|
-
setLocalAssignments((prev) => {
|
|
21192
|
-
const next = { ...prev };
|
|
21193
|
-
delete next[itemId];
|
|
21194
|
-
return next;
|
|
21195
|
-
});
|
|
21196
|
-
}
|
|
21354
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId: "" });
|
|
21197
21355
|
};
|
|
21198
|
-
const handleSubmit =
|
|
21199
|
-
if (checkEvent) {
|
|
21200
|
-
|
|
21201
|
-
|
|
21202
|
-
if (!entityDrivesResult) {
|
|
21203
|
-
setLocalSubmitted(true);
|
|
21204
|
-
setLocalAttempts((a) => a + 1);
|
|
21205
|
-
const correct = items.every((item) => assignments[item.id] === item.correctCategory);
|
|
21206
|
-
if (correct) {
|
|
21207
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
21208
|
-
}
|
|
21209
|
-
}
|
|
21210
|
-
}, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
|
|
21211
|
-
const handleReset = () => {
|
|
21212
|
-
if (!entityDrivesResult) setLocalSubmitted(false);
|
|
21213
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
21214
|
-
setShowHint(true);
|
|
21356
|
+
const handleSubmit = () => {
|
|
21357
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
21358
|
+
if (allAssigned && items.every((item) => assignments[item.id] === item.correctCategory)) {
|
|
21359
|
+
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
21215
21360
|
}
|
|
21216
21361
|
};
|
|
21217
21362
|
const handleFullReset = () => {
|
|
21218
|
-
if (playAgainEvent) {
|
|
21219
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
21220
|
-
}
|
|
21221
|
-
setLocalAssignments({});
|
|
21222
|
-
setLocalSubmitted(false);
|
|
21223
|
-
setLocalAttempts(0);
|
|
21224
|
-
setShowHint(false);
|
|
21363
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
21225
21364
|
};
|
|
21226
21365
|
if (!resolved) return null;
|
|
21227
21366
|
const theme = resolved.theme ?? void 0;
|
|
@@ -21260,17 +21399,17 @@ function ClassifierBoard({
|
|
|
21260
21399
|
/* @__PURE__ */ jsx(Badge, { size: "sm", children: catItems.length })
|
|
21261
21400
|
] }),
|
|
21262
21401
|
/* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-wrap min-h-[2rem]", children: catItems.map((item) => {
|
|
21263
|
-
const
|
|
21402
|
+
const result2 = results.find((r) => r.item.id === item.id);
|
|
21264
21403
|
return /* @__PURE__ */ jsxs(
|
|
21265
21404
|
Badge,
|
|
21266
21405
|
{
|
|
21267
21406
|
size: "sm",
|
|
21268
|
-
className: `cursor-pointer ${
|
|
21407
|
+
className: `cursor-pointer ${result2 ? result2.correct ? "border-success bg-success/10" : "border-error bg-error/10" : ""}`,
|
|
21269
21408
|
onClick: () => handleUnassign(item.id),
|
|
21270
21409
|
children: [
|
|
21271
21410
|
item.iconUrl && /* @__PURE__ */ jsx("img", { src: item.iconUrl, alt: "", className: "w-3 h-3 object-contain inline-block" }),
|
|
21272
21411
|
item.label,
|
|
21273
|
-
|
|
21412
|
+
result2 && /* @__PURE__ */ jsx(Icon, { icon: result2.correct ? CheckCircle : XCircle, size: "xs", className: result2.correct ? "text-success" : "text-error" })
|
|
21274
21413
|
]
|
|
21275
21414
|
},
|
|
21276
21415
|
item.id
|
|
@@ -21299,10 +21438,10 @@ function ClassifierBoard({
|
|
|
21299
21438
|
] }) }),
|
|
21300
21439
|
showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
|
|
21301
21440
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
21302
|
-
!submitted
|
|
21441
|
+
!submitted && /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
|
|
21303
21442
|
/* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
|
|
21304
21443
|
t("classifier.check")
|
|
21305
|
-
] })
|
|
21444
|
+
] }),
|
|
21306
21445
|
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
|
|
21307
21446
|
/* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
|
|
21308
21447
|
t("classifier.reset")
|
|
@@ -25020,7 +25159,7 @@ var init_Flex = __esm({
|
|
|
25020
25159
|
basis,
|
|
25021
25160
|
className,
|
|
25022
25161
|
children,
|
|
25023
|
-
as:
|
|
25162
|
+
as: Component2 = "div"
|
|
25024
25163
|
}) => {
|
|
25025
25164
|
const flexStyle = {};
|
|
25026
25165
|
if (grow !== void 0 || shrink !== void 0 || basis !== void 0) {
|
|
@@ -25032,7 +25171,7 @@ var init_Flex = __esm({
|
|
|
25032
25171
|
flexStyle.flexBasis = typeof basis === "number" ? `${basis}px` : basis;
|
|
25033
25172
|
}
|
|
25034
25173
|
}
|
|
25035
|
-
const Comp =
|
|
25174
|
+
const Comp = Component2;
|
|
25036
25175
|
return /* @__PURE__ */ jsx(
|
|
25037
25176
|
Comp,
|
|
25038
25177
|
{
|
|
@@ -25152,11 +25291,11 @@ var init_Grid = __esm({
|
|
|
25152
25291
|
className,
|
|
25153
25292
|
style,
|
|
25154
25293
|
children,
|
|
25155
|
-
as:
|
|
25294
|
+
as: Component2 = "div"
|
|
25156
25295
|
}) => {
|
|
25157
25296
|
const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
|
|
25158
25297
|
return React83__default.createElement(
|
|
25159
|
-
|
|
25298
|
+
Component2,
|
|
25160
25299
|
{
|
|
25161
25300
|
className: cn(
|
|
25162
25301
|
"grid",
|
|
@@ -26982,6 +27121,7 @@ var init_ProgressDots = __esm({
|
|
|
26982
27121
|
}
|
|
26983
27122
|
});
|
|
26984
27123
|
function StatBadge({
|
|
27124
|
+
assetUrl = DEFAULT_ASSET_URL12,
|
|
26985
27125
|
label,
|
|
26986
27126
|
value = 0,
|
|
26987
27127
|
max,
|
|
@@ -27005,7 +27145,17 @@ function StatBadge({
|
|
|
27005
27145
|
className
|
|
27006
27146
|
),
|
|
27007
27147
|
children: [
|
|
27008
|
-
|
|
27148
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
27149
|
+
"img",
|
|
27150
|
+
{
|
|
27151
|
+
src: assetUrl,
|
|
27152
|
+
alt: "",
|
|
27153
|
+
width: 16,
|
|
27154
|
+
height: 16,
|
|
27155
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
27156
|
+
className: "flex-shrink-0"
|
|
27157
|
+
}
|
|
27158
|
+
) : icon ? /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 text-lg", children: typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, className: "w-4 h-4" }) : /* @__PURE__ */ jsx(Icon, { icon, className: "w-4 h-4" }) }) : null,
|
|
27009
27159
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground font-medium", children: label }),
|
|
27010
27160
|
format === "hearts" && max && /* @__PURE__ */ jsx(
|
|
27011
27161
|
HealthBar,
|
|
@@ -27038,7 +27188,7 @@ function StatBadge({
|
|
|
27038
27188
|
}
|
|
27039
27189
|
);
|
|
27040
27190
|
}
|
|
27041
|
-
var sizeMap17, variantMap2;
|
|
27191
|
+
var sizeMap17, variantMap2, DEFAULT_ASSET_URL12;
|
|
27042
27192
|
var init_StatBadge = __esm({
|
|
27043
27193
|
"components/game/molecules/StatBadge.tsx"() {
|
|
27044
27194
|
init_cn();
|
|
@@ -27057,6 +27207,7 @@ var init_StatBadge = __esm({
|
|
|
27057
27207
|
warning: "bg-warning/15 border-warning/40 text-foreground",
|
|
27058
27208
|
danger: "bg-error/15 border-error/40 text-foreground"
|
|
27059
27209
|
};
|
|
27210
|
+
DEFAULT_ASSET_URL12 = "https://almadar-kflow-assets.web.app/shared/effects/particles/light_01.png";
|
|
27060
27211
|
StatBadge.displayName = "StatBadge";
|
|
27061
27212
|
}
|
|
27062
27213
|
});
|
|
@@ -27713,6 +27864,7 @@ var init_TurnPanel = __esm({
|
|
|
27713
27864
|
}
|
|
27714
27865
|
});
|
|
27715
27866
|
function EnemyPlate({
|
|
27867
|
+
assetUrl = DEFAULT_ASSET_URL13,
|
|
27716
27868
|
name = "Shadow Guard",
|
|
27717
27869
|
health = 80,
|
|
27718
27870
|
maxHealth = 100,
|
|
@@ -27730,15 +27882,28 @@ function EnemyPlate({
|
|
|
27730
27882
|
),
|
|
27731
27883
|
children: [
|
|
27732
27884
|
/* @__PURE__ */ jsxs(Box, { className: "flex items-center justify-between gap-2", children: [
|
|
27733
|
-
/* @__PURE__ */
|
|
27734
|
-
|
|
27735
|
-
|
|
27736
|
-
|
|
27737
|
-
|
|
27738
|
-
|
|
27739
|
-
|
|
27740
|
-
|
|
27741
|
-
|
|
27885
|
+
/* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-1.5 min-w-0", children: [
|
|
27886
|
+
assetUrl && /* @__PURE__ */ jsx(
|
|
27887
|
+
"img",
|
|
27888
|
+
{
|
|
27889
|
+
src: assetUrl,
|
|
27890
|
+
alt: name,
|
|
27891
|
+
width: 24,
|
|
27892
|
+
height: 24,
|
|
27893
|
+
style: { objectFit: "cover", borderRadius: "50%" },
|
|
27894
|
+
className: "flex-shrink-0"
|
|
27895
|
+
}
|
|
27896
|
+
),
|
|
27897
|
+
/* @__PURE__ */ jsx(
|
|
27898
|
+
Typography,
|
|
27899
|
+
{
|
|
27900
|
+
variant: "caption",
|
|
27901
|
+
weight: "bold",
|
|
27902
|
+
className: "text-[var(--color-foreground)] truncate",
|
|
27903
|
+
children: name
|
|
27904
|
+
}
|
|
27905
|
+
)
|
|
27906
|
+
] }),
|
|
27742
27907
|
level != null && /* @__PURE__ */ jsxs(Badge, { variant: "neutral", size: "sm", children: [
|
|
27743
27908
|
"Lv.",
|
|
27744
27909
|
level
|
|
@@ -27783,7 +27948,7 @@ function EnemyPlate({
|
|
|
27783
27948
|
}
|
|
27784
27949
|
);
|
|
27785
27950
|
}
|
|
27786
|
-
var effectVariantMap2, DEFAULT_ENEMY_EFFECTS;
|
|
27951
|
+
var effectVariantMap2, DEFAULT_ENEMY_EFFECTS, DEFAULT_ASSET_URL13;
|
|
27787
27952
|
var init_EnemyPlate = __esm({
|
|
27788
27953
|
"components/game/molecules/EnemyPlate.tsx"() {
|
|
27789
27954
|
"use client";
|
|
@@ -27800,6 +27965,7 @@ var init_EnemyPlate = __esm({
|
|
|
27800
27965
|
DEFAULT_ENEMY_EFFECTS = [
|
|
27801
27966
|
{ icon: "flame", label: "Burn", variant: "debuff" }
|
|
27802
27967
|
];
|
|
27968
|
+
DEFAULT_ASSET_URL13 = "https://almadar-kflow-assets.web.app/shared/portraits/shadow-legion.png";
|
|
27803
27969
|
EnemyPlate.displayName = "EnemyPlate";
|
|
27804
27970
|
}
|
|
27805
27971
|
});
|
|
@@ -29331,13 +29497,13 @@ var init_MapView = __esm({
|
|
|
29331
29497
|
shadowSize: [41, 41]
|
|
29332
29498
|
});
|
|
29333
29499
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
29334
|
-
const { useEffect:
|
|
29500
|
+
const { useEffect: useEffect79, useRef: useRef74, useCallback: useCallback118, useState: useState109 } = React83__default;
|
|
29335
29501
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
29336
|
-
const { useEventBus:
|
|
29502
|
+
const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
29337
29503
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
29338
29504
|
const map = useMap();
|
|
29339
|
-
const prevRef =
|
|
29340
|
-
|
|
29505
|
+
const prevRef = useRef74({ centerLat, centerLng, zoom });
|
|
29506
|
+
useEffect79(() => {
|
|
29341
29507
|
const prev = prevRef.current;
|
|
29342
29508
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
29343
29509
|
map.setView([centerLat, centerLng], zoom);
|
|
@@ -29348,7 +29514,7 @@ var init_MapView = __esm({
|
|
|
29348
29514
|
}
|
|
29349
29515
|
function MapClickHandler({ onMapClick }) {
|
|
29350
29516
|
const map = useMap();
|
|
29351
|
-
|
|
29517
|
+
useEffect79(() => {
|
|
29352
29518
|
if (!onMapClick) return;
|
|
29353
29519
|
const handler = (e) => {
|
|
29354
29520
|
onMapClick(e.latlng.lat, e.latlng.lng);
|
|
@@ -29375,9 +29541,9 @@ var init_MapView = __esm({
|
|
|
29375
29541
|
className,
|
|
29376
29542
|
showAttribution = true
|
|
29377
29543
|
}) {
|
|
29378
|
-
const eventBus =
|
|
29379
|
-
const [clickedPosition, setClickedPosition] =
|
|
29380
|
-
const handleMapClick =
|
|
29544
|
+
const eventBus = useEventBus3();
|
|
29545
|
+
const [clickedPosition, setClickedPosition] = useState109(null);
|
|
29546
|
+
const handleMapClick = useCallback118((lat, lng) => {
|
|
29381
29547
|
if (showClickedPin) {
|
|
29382
29548
|
setClickedPosition({ lat, lng });
|
|
29383
29549
|
}
|
|
@@ -29386,7 +29552,7 @@ var init_MapView = __esm({
|
|
|
29386
29552
|
eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
|
|
29387
29553
|
}
|
|
29388
29554
|
}, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
|
|
29389
|
-
const handleMarkerClick =
|
|
29555
|
+
const handleMarkerClick = useCallback118((marker) => {
|
|
29390
29556
|
onMarkerClick?.(marker);
|
|
29391
29557
|
if (markerClickEvent) {
|
|
29392
29558
|
eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
|
|
@@ -36513,10 +36679,10 @@ var init_Section = __esm({
|
|
|
36513
36679
|
children,
|
|
36514
36680
|
headerClassName,
|
|
36515
36681
|
contentClassName,
|
|
36516
|
-
as:
|
|
36682
|
+
as: Component2 = "section"
|
|
36517
36683
|
}) => {
|
|
36518
36684
|
const hasHeader = title || description || action;
|
|
36519
|
-
const Comp =
|
|
36685
|
+
const Comp = Component2;
|
|
36520
36686
|
return /* @__PURE__ */ jsxs(
|
|
36521
36687
|
Comp,
|
|
36522
36688
|
{
|
|
@@ -38617,44 +38783,27 @@ function DebuggerBoard({
|
|
|
38617
38783
|
const { t } = useTranslate();
|
|
38618
38784
|
const resolved = boardEntity(entity);
|
|
38619
38785
|
const [headerError, setHeaderError] = useState(false);
|
|
38620
|
-
const [showHint, setShowHint] = useState(false);
|
|
38621
38786
|
const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
|
|
38622
|
-
const result = resolved?.result
|
|
38787
|
+
const result = str(resolved?.result) || "none";
|
|
38623
38788
|
const attempts = num(resolved?.attempts);
|
|
38624
|
-
const submitted = result
|
|
38789
|
+
const submitted = result === "win";
|
|
38790
|
+
const showHint = !submitted && attempts >= 2 && Boolean(str(resolved?.hint));
|
|
38625
38791
|
const bugLines = lines.filter((l) => l.isBug);
|
|
38626
38792
|
const flaggedLines = lines.filter((l) => l.isFlagged);
|
|
38627
38793
|
const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
|
|
38628
38794
|
const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
|
|
38629
|
-
const allCorrect =
|
|
38795
|
+
const allCorrect = submitted;
|
|
38630
38796
|
const toggleLine = (lineId) => {
|
|
38631
38797
|
if (submitted) return;
|
|
38632
|
-
if (toggleFlagEvent) {
|
|
38633
|
-
emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
38634
|
-
}
|
|
38798
|
+
if (toggleFlagEvent) emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
38635
38799
|
};
|
|
38636
38800
|
const handleSubmit = useCallback(() => {
|
|
38637
|
-
if (checkEvent) {
|
|
38638
|
-
emit(`UI:${checkEvent}`, {});
|
|
38639
|
-
}
|
|
38801
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
38640
38802
|
const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
|
|
38641
|
-
if (correct) {
|
|
38642
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
38643
|
-
}
|
|
38803
|
+
if (correct) emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
38644
38804
|
}, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
|
|
38645
38805
|
const handleReset = () => {
|
|
38646
|
-
if (playAgainEvent) {
|
|
38647
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
38648
|
-
}
|
|
38649
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
38650
|
-
setShowHint(true);
|
|
38651
|
-
}
|
|
38652
|
-
};
|
|
38653
|
-
const handleFullReset = () => {
|
|
38654
|
-
if (playAgainEvent) {
|
|
38655
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
38656
|
-
}
|
|
38657
|
-
setShowHint(false);
|
|
38806
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
38658
38807
|
};
|
|
38659
38808
|
if (!resolved) return null;
|
|
38660
38809
|
const theme = resolved.theme ?? void 0;
|
|
@@ -38678,7 +38827,7 @@ function DebuggerBoard({
|
|
|
38678
38827
|
/* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
|
|
38679
38828
|
] }),
|
|
38680
38829
|
/* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) }),
|
|
38681
|
-
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(
|
|
38830
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(lines.filter((l) => l.isBug).length) }) })
|
|
38682
38831
|
] }) }),
|
|
38683
38832
|
/* @__PURE__ */ jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsx(VStack, { gap: "none", children: lines.map((line, i) => {
|
|
38684
38833
|
const isFlagged = !!line.isFlagged;
|
|
@@ -38728,11 +38877,11 @@ function DebuggerBoard({
|
|
|
38728
38877
|
] }) }),
|
|
38729
38878
|
showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
|
|
38730
38879
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
38731
|
-
!submitted
|
|
38880
|
+
!submitted && /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
|
|
38732
38881
|
/* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
|
|
38733
38882
|
t("debugger.submit")
|
|
38734
|
-
] })
|
|
38735
|
-
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick:
|
|
38883
|
+
] }),
|
|
38884
|
+
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleReset, children: [
|
|
38736
38885
|
/* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
|
|
38737
38886
|
t("debugger.reset")
|
|
38738
38887
|
] })
|
|
@@ -39733,39 +39882,40 @@ function EventHandlerBoard({
|
|
|
39733
39882
|
stepDurationMs = 800,
|
|
39734
39883
|
playEvent,
|
|
39735
39884
|
completeEvent,
|
|
39885
|
+
editRuleEvent,
|
|
39886
|
+
playAgainEvent,
|
|
39736
39887
|
className
|
|
39737
39888
|
}) {
|
|
39738
39889
|
const { emit } = useEventBus();
|
|
39739
39890
|
const { t } = useTranslate();
|
|
39740
39891
|
const resolved = boardEntity(entity);
|
|
39741
|
-
const
|
|
39742
|
-
const
|
|
39892
|
+
const objects = rows(resolved?.objects);
|
|
39893
|
+
const entityResult = str(resolved?.result) || "none";
|
|
39894
|
+
const isSuccess = entityResult === "win";
|
|
39895
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
39743
39896
|
const [selectedObjectId, setSelectedObjectId] = useState(
|
|
39744
|
-
|
|
39897
|
+
objects[0] ? objId(objects[0]) : null
|
|
39745
39898
|
);
|
|
39746
39899
|
const [headerError, setHeaderError] = useState(false);
|
|
39747
|
-
const [
|
|
39900
|
+
const [isPlaying, setIsPlaying] = useState(false);
|
|
39748
39901
|
const [eventLog, setEventLog] = useState([]);
|
|
39749
|
-
const [attempts, setAttempts] = useState(0);
|
|
39750
39902
|
const timerRef = useRef(null);
|
|
39751
39903
|
const logIdCounter = useRef(0);
|
|
39752
39904
|
useEffect(() => () => {
|
|
39753
39905
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39754
39906
|
}, []);
|
|
39755
|
-
const selectedObject = objects.find((o) => objId(o) === selectedObjectId)
|
|
39907
|
+
const selectedObject = objects.find((o) => objId(o) === selectedObjectId) ?? null;
|
|
39756
39908
|
const handleRulesChange = useCallback((objectId, rules) => {
|
|
39757
|
-
|
|
39758
|
-
|
|
39759
|
-
));
|
|
39760
|
-
}, []);
|
|
39909
|
+
if (editRuleEvent) emit(`UI:${editRuleEvent}`, { objectId, rules });
|
|
39910
|
+
}, [editRuleEvent, emit]);
|
|
39761
39911
|
const addLogEntry = useCallback((icon, message, status = "done") => {
|
|
39762
39912
|
const id = `log-${logIdCounter.current++}`;
|
|
39763
39913
|
setEventLog((prev) => [...prev, { id, timestamp: Date.now(), icon, message, status }]);
|
|
39764
39914
|
}, []);
|
|
39765
39915
|
const handlePlay = useCallback(() => {
|
|
39766
|
-
if (
|
|
39916
|
+
if (isPlaying || isSuccess) return;
|
|
39767
39917
|
if (playEvent) emit(`UI:${playEvent}`, {});
|
|
39768
|
-
|
|
39918
|
+
setIsPlaying(true);
|
|
39769
39919
|
setEventLog([]);
|
|
39770
39920
|
const allRules = [];
|
|
39771
39921
|
objects.forEach((obj) => {
|
|
@@ -39781,15 +39931,8 @@ function EventHandlerBoard({
|
|
|
39781
39931
|
let goalReached = false;
|
|
39782
39932
|
const processNext = () => {
|
|
39783
39933
|
if (eventQueue.length === 0 || stepIdx > 20) {
|
|
39784
|
-
|
|
39785
|
-
|
|
39786
|
-
if (completeEvent) {
|
|
39787
|
-
emit(`UI:${completeEvent}`, { success: true });
|
|
39788
|
-
}
|
|
39789
|
-
} else {
|
|
39790
|
-
setAttempts((prev) => prev + 1);
|
|
39791
|
-
setPlayState("fail");
|
|
39792
|
-
}
|
|
39934
|
+
setIsPlaying(false);
|
|
39935
|
+
if (goalReached && completeEvent) emit(`UI:${completeEvent}`, { success: true });
|
|
39793
39936
|
return;
|
|
39794
39937
|
}
|
|
39795
39938
|
const currentEvent = eventQueue.shift();
|
|
@@ -39820,21 +39963,19 @@ function EventHandlerBoard({
|
|
|
39820
39963
|
addLogEntry("\u{1F3AC}", t("eventHandler.simulationStarted", { events: triggers.join(", ") }), "active");
|
|
39821
39964
|
}
|
|
39822
39965
|
timerRef.current = setTimeout(processNext, stepDurationMs);
|
|
39823
|
-
}, [
|
|
39966
|
+
}, [isPlaying, isSuccess, objects, resolved, stepDurationMs, playEvent, completeEvent, emit, addLogEntry, t]);
|
|
39824
39967
|
const handleTryAgain = useCallback(() => {
|
|
39825
39968
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39826
|
-
|
|
39969
|
+
setIsPlaying(false);
|
|
39827
39970
|
setEventLog([]);
|
|
39828
39971
|
}, []);
|
|
39829
39972
|
const handleReset = useCallback(() => {
|
|
39830
39973
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39831
|
-
|
|
39832
|
-
setObjects([...resetObjects]);
|
|
39833
|
-
setPlayState("editing");
|
|
39974
|
+
setIsPlaying(false);
|
|
39834
39975
|
setEventLog([]);
|
|
39835
|
-
setSelectedObjectId(
|
|
39836
|
-
|
|
39837
|
-
}, [
|
|
39976
|
+
setSelectedObjectId(objects[0] ? objId(objects[0]) : null);
|
|
39977
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
39978
|
+
}, [objects, playAgainEvent, emit]);
|
|
39838
39979
|
if (!resolved) return null;
|
|
39839
39980
|
const objectViewers = objects.map((obj) => {
|
|
39840
39981
|
const states = objStates(obj);
|
|
@@ -39903,12 +40044,12 @@ function EventHandlerBoard({
|
|
|
39903
40044
|
{
|
|
39904
40045
|
object: selectedObject,
|
|
39905
40046
|
onRulesChange: handleRulesChange,
|
|
39906
|
-
disabled:
|
|
40047
|
+
disabled: isPlaying
|
|
39907
40048
|
}
|
|
39908
40049
|
),
|
|
39909
40050
|
eventLog.length > 0 && /* @__PURE__ */ jsx(EventLog, { entries: eventLog }),
|
|
39910
|
-
|
|
39911
|
-
|
|
40051
|
+
isSuccess && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("eventHandler.chainComplete") }) }),
|
|
40052
|
+
!isPlaying && !isSuccess && attempts > 0 && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
39912
40053
|
/* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(encourageKey) }) }),
|
|
39913
40054
|
showHint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
|
|
39914
40055
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
@@ -39916,12 +40057,12 @@ function EventHandlerBoard({
|
|
|
39916
40057
|
] }) })
|
|
39917
40058
|
] }),
|
|
39918
40059
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
|
|
39919
|
-
|
|
40060
|
+
!isPlaying && !isSuccess && attempts > 0 ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsx(
|
|
39920
40061
|
Button,
|
|
39921
40062
|
{
|
|
39922
40063
|
variant: "primary",
|
|
39923
40064
|
onClick: handlePlay,
|
|
39924
|
-
disabled:
|
|
40065
|
+
disabled: isPlaying || isSuccess,
|
|
39925
40066
|
children: "\u25B6 " + t("game.play")
|
|
39926
40067
|
}
|
|
39927
40068
|
),
|
|
@@ -41106,6 +41247,1568 @@ var init_GameAudioToggle = __esm({
|
|
|
41106
41247
|
GameAudioToggle.displayName = "GameAudioToggle";
|
|
41107
41248
|
}
|
|
41108
41249
|
});
|
|
41250
|
+
function detectAssetRoot(modelUrl) {
|
|
41251
|
+
const idx = modelUrl.indexOf("/3d/");
|
|
41252
|
+
if (idx !== -1) {
|
|
41253
|
+
return modelUrl.substring(0, idx + 4);
|
|
41254
|
+
}
|
|
41255
|
+
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
41256
|
+
}
|
|
41257
|
+
function createGLTFLoaderForUrl(url) {
|
|
41258
|
+
const loader = new GLTFLoader();
|
|
41259
|
+
loader.setResourcePath(detectAssetRoot(url));
|
|
41260
|
+
return loader;
|
|
41261
|
+
}
|
|
41262
|
+
var AssetLoader;
|
|
41263
|
+
var init_AssetLoader = __esm({
|
|
41264
|
+
"components/game/molecules/three/loaders/AssetLoader.ts"() {
|
|
41265
|
+
AssetLoader = class {
|
|
41266
|
+
constructor() {
|
|
41267
|
+
__publicField(this, "objLoader");
|
|
41268
|
+
__publicField(this, "textureLoader");
|
|
41269
|
+
__publicField(this, "modelCache");
|
|
41270
|
+
__publicField(this, "textureCache");
|
|
41271
|
+
__publicField(this, "loadingPromises");
|
|
41272
|
+
this.objLoader = new OBJLoader();
|
|
41273
|
+
this.textureLoader = new THREE.TextureLoader();
|
|
41274
|
+
this.modelCache = /* @__PURE__ */ new Map();
|
|
41275
|
+
this.textureCache = /* @__PURE__ */ new Map();
|
|
41276
|
+
this.loadingPromises = /* @__PURE__ */ new Map();
|
|
41277
|
+
}
|
|
41278
|
+
/**
|
|
41279
|
+
* Load a GLB/GLTF model
|
|
41280
|
+
* @param url - URL to the .glb or .gltf file
|
|
41281
|
+
* @returns Promise with loaded model scene and animations
|
|
41282
|
+
*/
|
|
41283
|
+
async loadModel(url) {
|
|
41284
|
+
if (this.modelCache.has(url)) {
|
|
41285
|
+
return this.modelCache.get(url);
|
|
41286
|
+
}
|
|
41287
|
+
if (this.loadingPromises.has(url)) {
|
|
41288
|
+
return this.loadingPromises.get(url);
|
|
41289
|
+
}
|
|
41290
|
+
const loader = createGLTFLoaderForUrl(url);
|
|
41291
|
+
const loadPromise = loader.loadAsync(url).then((gltf) => {
|
|
41292
|
+
const result = {
|
|
41293
|
+
scene: gltf.scene,
|
|
41294
|
+
animations: gltf.animations || []
|
|
41295
|
+
};
|
|
41296
|
+
this.modelCache.set(url, result);
|
|
41297
|
+
this.loadingPromises.delete(url);
|
|
41298
|
+
return result;
|
|
41299
|
+
}).catch((error) => {
|
|
41300
|
+
this.loadingPromises.delete(url);
|
|
41301
|
+
throw new Error(`Failed to load model ${url}: ${error.message}`);
|
|
41302
|
+
});
|
|
41303
|
+
this.loadingPromises.set(url, loadPromise);
|
|
41304
|
+
return loadPromise;
|
|
41305
|
+
}
|
|
41306
|
+
/**
|
|
41307
|
+
* Load an OBJ model (fallback for non-GLB assets)
|
|
41308
|
+
* @param url - URL to the .obj file
|
|
41309
|
+
* @returns Promise with loaded object group
|
|
41310
|
+
*/
|
|
41311
|
+
async loadOBJ(url) {
|
|
41312
|
+
if (this.modelCache.has(url)) {
|
|
41313
|
+
return this.modelCache.get(url).scene;
|
|
41314
|
+
}
|
|
41315
|
+
if (this.loadingPromises.has(url)) {
|
|
41316
|
+
const result = await this.loadingPromises.get(url);
|
|
41317
|
+
return result.scene;
|
|
41318
|
+
}
|
|
41319
|
+
const loadPromise = this.objLoader.loadAsync(url).then((group) => {
|
|
41320
|
+
const result = {
|
|
41321
|
+
scene: group,
|
|
41322
|
+
animations: []
|
|
41323
|
+
};
|
|
41324
|
+
this.modelCache.set(url, result);
|
|
41325
|
+
this.loadingPromises.delete(url);
|
|
41326
|
+
return result;
|
|
41327
|
+
}).catch((error) => {
|
|
41328
|
+
this.loadingPromises.delete(url);
|
|
41329
|
+
throw new Error(`Failed to load OBJ ${url}: ${error.message}`);
|
|
41330
|
+
});
|
|
41331
|
+
this.loadingPromises.set(url, loadPromise);
|
|
41332
|
+
return (await loadPromise).scene;
|
|
41333
|
+
}
|
|
41334
|
+
/**
|
|
41335
|
+
* Load a texture
|
|
41336
|
+
* @param url - URL to the texture image
|
|
41337
|
+
* @returns Promise with loaded texture
|
|
41338
|
+
*/
|
|
41339
|
+
async loadTexture(url) {
|
|
41340
|
+
if (this.textureCache.has(url)) {
|
|
41341
|
+
return this.textureCache.get(url);
|
|
41342
|
+
}
|
|
41343
|
+
if (this.loadingPromises.has(`texture:${url}`)) {
|
|
41344
|
+
return this.loadingPromises.get(`texture:${url}`);
|
|
41345
|
+
}
|
|
41346
|
+
const loadPromise = this.textureLoader.loadAsync(url).then((texture) => {
|
|
41347
|
+
texture.colorSpace = THREE.SRGBColorSpace;
|
|
41348
|
+
this.textureCache.set(url, texture);
|
|
41349
|
+
this.loadingPromises.delete(`texture:${url}`);
|
|
41350
|
+
return texture;
|
|
41351
|
+
}).catch((error) => {
|
|
41352
|
+
this.loadingPromises.delete(`texture:${url}`);
|
|
41353
|
+
throw new Error(`Failed to load texture ${url}: ${error.message}`);
|
|
41354
|
+
});
|
|
41355
|
+
this.loadingPromises.set(`texture:${url}`, loadPromise);
|
|
41356
|
+
return loadPromise;
|
|
41357
|
+
}
|
|
41358
|
+
/**
|
|
41359
|
+
* Preload multiple assets
|
|
41360
|
+
* @param urls - Array of asset URLs to preload
|
|
41361
|
+
* @returns Promise that resolves when all assets are loaded
|
|
41362
|
+
*/
|
|
41363
|
+
async preload(urls) {
|
|
41364
|
+
const promises = urls.map((url) => {
|
|
41365
|
+
if (url.endsWith(".glb") || url.endsWith(".gltf")) {
|
|
41366
|
+
return this.loadModel(url).catch(() => null);
|
|
41367
|
+
} else if (url.endsWith(".obj")) {
|
|
41368
|
+
return this.loadOBJ(url).catch(() => null);
|
|
41369
|
+
} else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
|
|
41370
|
+
return this.loadTexture(url).catch(() => null);
|
|
41371
|
+
}
|
|
41372
|
+
return Promise.resolve(null);
|
|
41373
|
+
});
|
|
41374
|
+
await Promise.all(promises);
|
|
41375
|
+
}
|
|
41376
|
+
/**
|
|
41377
|
+
* Check if a model is cached
|
|
41378
|
+
* @param url - Model URL
|
|
41379
|
+
*/
|
|
41380
|
+
hasModel(url) {
|
|
41381
|
+
return this.modelCache.has(url);
|
|
41382
|
+
}
|
|
41383
|
+
/**
|
|
41384
|
+
* Check if a texture is cached
|
|
41385
|
+
* @param url - Texture URL
|
|
41386
|
+
*/
|
|
41387
|
+
hasTexture(url) {
|
|
41388
|
+
return this.textureCache.has(url);
|
|
41389
|
+
}
|
|
41390
|
+
/**
|
|
41391
|
+
* Get cached model (throws if not cached)
|
|
41392
|
+
* @param url - Model URL
|
|
41393
|
+
*/
|
|
41394
|
+
getModel(url) {
|
|
41395
|
+
const model = this.modelCache.get(url);
|
|
41396
|
+
if (!model) {
|
|
41397
|
+
throw new Error(`Model ${url} not in cache`);
|
|
41398
|
+
}
|
|
41399
|
+
return model;
|
|
41400
|
+
}
|
|
41401
|
+
/**
|
|
41402
|
+
* Get cached texture (throws if not cached)
|
|
41403
|
+
* @param url - Texture URL
|
|
41404
|
+
*/
|
|
41405
|
+
getTexture(url) {
|
|
41406
|
+
const texture = this.textureCache.get(url);
|
|
41407
|
+
if (!texture) {
|
|
41408
|
+
throw new Error(`Texture ${url} not in cache`);
|
|
41409
|
+
}
|
|
41410
|
+
return texture;
|
|
41411
|
+
}
|
|
41412
|
+
/**
|
|
41413
|
+
* Clear all caches
|
|
41414
|
+
*/
|
|
41415
|
+
clearCache() {
|
|
41416
|
+
this.textureCache.forEach((texture) => {
|
|
41417
|
+
texture.dispose();
|
|
41418
|
+
});
|
|
41419
|
+
this.modelCache.forEach((model) => {
|
|
41420
|
+
model.scene.traverse((child) => {
|
|
41421
|
+
if (child instanceof THREE.Mesh) {
|
|
41422
|
+
child.geometry.dispose();
|
|
41423
|
+
if (Array.isArray(child.material)) {
|
|
41424
|
+
child.material.forEach((m) => m.dispose());
|
|
41425
|
+
} else {
|
|
41426
|
+
child.material.dispose();
|
|
41427
|
+
}
|
|
41428
|
+
}
|
|
41429
|
+
});
|
|
41430
|
+
});
|
|
41431
|
+
this.modelCache.clear();
|
|
41432
|
+
this.textureCache.clear();
|
|
41433
|
+
this.loadingPromises.clear();
|
|
41434
|
+
}
|
|
41435
|
+
/**
|
|
41436
|
+
* Get cache statistics
|
|
41437
|
+
*/
|
|
41438
|
+
getStats() {
|
|
41439
|
+
return {
|
|
41440
|
+
models: this.modelCache.size,
|
|
41441
|
+
textures: this.textureCache.size,
|
|
41442
|
+
loading: this.loadingPromises.size
|
|
41443
|
+
};
|
|
41444
|
+
}
|
|
41445
|
+
};
|
|
41446
|
+
new AssetLoader();
|
|
41447
|
+
}
|
|
41448
|
+
});
|
|
41449
|
+
function useAssetLoader(options = {}) {
|
|
41450
|
+
const { preloadUrls = [], loader: customLoader } = options;
|
|
41451
|
+
const loaderRef = useRef(customLoader || new AssetLoader());
|
|
41452
|
+
const [state, setState] = useState({
|
|
41453
|
+
isLoading: false,
|
|
41454
|
+
progress: 0,
|
|
41455
|
+
loaded: 0,
|
|
41456
|
+
total: 0,
|
|
41457
|
+
errors: []
|
|
41458
|
+
});
|
|
41459
|
+
useEffect(() => {
|
|
41460
|
+
if (preloadUrls.length > 0) {
|
|
41461
|
+
preload(preloadUrls);
|
|
41462
|
+
}
|
|
41463
|
+
}, []);
|
|
41464
|
+
const updateProgress = useCallback((loaded, total) => {
|
|
41465
|
+
setState((prev) => ({
|
|
41466
|
+
...prev,
|
|
41467
|
+
loaded,
|
|
41468
|
+
total,
|
|
41469
|
+
progress: total > 0 ? Math.round(loaded / total * 100) : 0
|
|
41470
|
+
}));
|
|
41471
|
+
}, []);
|
|
41472
|
+
const loadModel = useCallback(
|
|
41473
|
+
async (url) => {
|
|
41474
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
41475
|
+
try {
|
|
41476
|
+
const model = await loaderRef.current.loadModel(url);
|
|
41477
|
+
setState((prev) => ({
|
|
41478
|
+
...prev,
|
|
41479
|
+
isLoading: false,
|
|
41480
|
+
loaded: prev.loaded + 1
|
|
41481
|
+
}));
|
|
41482
|
+
return model;
|
|
41483
|
+
} catch (error) {
|
|
41484
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
41485
|
+
setState((prev) => ({
|
|
41486
|
+
...prev,
|
|
41487
|
+
isLoading: false,
|
|
41488
|
+
errors: [...prev.errors, errorMsg]
|
|
41489
|
+
}));
|
|
41490
|
+
throw error;
|
|
41491
|
+
}
|
|
41492
|
+
},
|
|
41493
|
+
[]
|
|
41494
|
+
);
|
|
41495
|
+
const loadOBJ = useCallback(
|
|
41496
|
+
async (url) => {
|
|
41497
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
41498
|
+
try {
|
|
41499
|
+
const model = await loaderRef.current.loadOBJ(url);
|
|
41500
|
+
setState((prev) => ({
|
|
41501
|
+
...prev,
|
|
41502
|
+
isLoading: false,
|
|
41503
|
+
loaded: prev.loaded + 1
|
|
41504
|
+
}));
|
|
41505
|
+
return model;
|
|
41506
|
+
} catch (error) {
|
|
41507
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
41508
|
+
setState((prev) => ({
|
|
41509
|
+
...prev,
|
|
41510
|
+
isLoading: false,
|
|
41511
|
+
errors: [...prev.errors, errorMsg]
|
|
41512
|
+
}));
|
|
41513
|
+
throw error;
|
|
41514
|
+
}
|
|
41515
|
+
},
|
|
41516
|
+
[]
|
|
41517
|
+
);
|
|
41518
|
+
const loadTexture = useCallback(
|
|
41519
|
+
async (url) => {
|
|
41520
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
41521
|
+
try {
|
|
41522
|
+
const texture = await loaderRef.current.loadTexture(url);
|
|
41523
|
+
setState((prev) => ({
|
|
41524
|
+
...prev,
|
|
41525
|
+
isLoading: false,
|
|
41526
|
+
loaded: prev.loaded + 1
|
|
41527
|
+
}));
|
|
41528
|
+
return texture;
|
|
41529
|
+
} catch (error) {
|
|
41530
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
41531
|
+
setState((prev) => ({
|
|
41532
|
+
...prev,
|
|
41533
|
+
isLoading: false,
|
|
41534
|
+
errors: [...prev.errors, errorMsg]
|
|
41535
|
+
}));
|
|
41536
|
+
throw error;
|
|
41537
|
+
}
|
|
41538
|
+
},
|
|
41539
|
+
[]
|
|
41540
|
+
);
|
|
41541
|
+
const preload = useCallback(
|
|
41542
|
+
async (urls) => {
|
|
41543
|
+
setState((prev) => ({
|
|
41544
|
+
...prev,
|
|
41545
|
+
isLoading: true,
|
|
41546
|
+
total: urls.length,
|
|
41547
|
+
loaded: 0,
|
|
41548
|
+
errors: []
|
|
41549
|
+
}));
|
|
41550
|
+
let completed = 0;
|
|
41551
|
+
const errors = [];
|
|
41552
|
+
await Promise.all(
|
|
41553
|
+
urls.map(async (url) => {
|
|
41554
|
+
try {
|
|
41555
|
+
if (url.endsWith(".glb") || url.endsWith(".gltf")) {
|
|
41556
|
+
await loaderRef.current.loadModel(url);
|
|
41557
|
+
} else if (url.endsWith(".obj")) {
|
|
41558
|
+
await loaderRef.current.loadOBJ(url);
|
|
41559
|
+
} else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
|
|
41560
|
+
await loaderRef.current.loadTexture(url);
|
|
41561
|
+
}
|
|
41562
|
+
completed++;
|
|
41563
|
+
updateProgress(completed, urls.length);
|
|
41564
|
+
} catch (error) {
|
|
41565
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
41566
|
+
errors.push(`${url}: ${errorMsg}`);
|
|
41567
|
+
completed++;
|
|
41568
|
+
updateProgress(completed, urls.length);
|
|
41569
|
+
}
|
|
41570
|
+
})
|
|
41571
|
+
);
|
|
41572
|
+
setState((prev) => ({
|
|
41573
|
+
...prev,
|
|
41574
|
+
isLoading: false,
|
|
41575
|
+
errors
|
|
41576
|
+
}));
|
|
41577
|
+
},
|
|
41578
|
+
[updateProgress]
|
|
41579
|
+
);
|
|
41580
|
+
const hasModel = useCallback((url) => {
|
|
41581
|
+
return loaderRef.current.hasModel(url);
|
|
41582
|
+
}, []);
|
|
41583
|
+
const hasTexture = useCallback((url) => {
|
|
41584
|
+
return loaderRef.current.hasTexture(url);
|
|
41585
|
+
}, []);
|
|
41586
|
+
const getModel = useCallback((url) => {
|
|
41587
|
+
try {
|
|
41588
|
+
return loaderRef.current.getModel(url);
|
|
41589
|
+
} catch {
|
|
41590
|
+
return void 0;
|
|
41591
|
+
}
|
|
41592
|
+
}, []);
|
|
41593
|
+
const getTexture = useCallback((url) => {
|
|
41594
|
+
try {
|
|
41595
|
+
return loaderRef.current.getTexture(url);
|
|
41596
|
+
} catch {
|
|
41597
|
+
return void 0;
|
|
41598
|
+
}
|
|
41599
|
+
}, []);
|
|
41600
|
+
const clearCache = useCallback(() => {
|
|
41601
|
+
loaderRef.current.clearCache();
|
|
41602
|
+
setState({
|
|
41603
|
+
isLoading: false,
|
|
41604
|
+
progress: 0,
|
|
41605
|
+
loaded: 0,
|
|
41606
|
+
total: 0,
|
|
41607
|
+
errors: []
|
|
41608
|
+
});
|
|
41609
|
+
}, []);
|
|
41610
|
+
return {
|
|
41611
|
+
...state,
|
|
41612
|
+
loadModel,
|
|
41613
|
+
loadOBJ,
|
|
41614
|
+
loadTexture,
|
|
41615
|
+
preload,
|
|
41616
|
+
hasModel,
|
|
41617
|
+
hasTexture,
|
|
41618
|
+
getModel,
|
|
41619
|
+
getTexture,
|
|
41620
|
+
clearCache
|
|
41621
|
+
};
|
|
41622
|
+
}
|
|
41623
|
+
var init_useAssetLoader = __esm({
|
|
41624
|
+
"components/game/molecules/three/hooks/useAssetLoader.ts"() {
|
|
41625
|
+
"use client";
|
|
41626
|
+
init_AssetLoader();
|
|
41627
|
+
}
|
|
41628
|
+
});
|
|
41629
|
+
function useGameCanvas3DEvents(options) {
|
|
41630
|
+
const {
|
|
41631
|
+
tileClickEvent,
|
|
41632
|
+
unitClickEvent,
|
|
41633
|
+
featureClickEvent,
|
|
41634
|
+
canvasClickEvent,
|
|
41635
|
+
tileHoverEvent,
|
|
41636
|
+
tileLeaveEvent,
|
|
41637
|
+
unitAnimationEvent,
|
|
41638
|
+
cameraChangeEvent,
|
|
41639
|
+
onTileClick,
|
|
41640
|
+
onUnitClick,
|
|
41641
|
+
onFeatureClick,
|
|
41642
|
+
onCanvasClick,
|
|
41643
|
+
onTileHover,
|
|
41644
|
+
onUnitAnimation
|
|
41645
|
+
} = options;
|
|
41646
|
+
const emit = useEmitEvent();
|
|
41647
|
+
const optionsRef = useRef(options);
|
|
41648
|
+
optionsRef.current = options;
|
|
41649
|
+
const handleTileClick = useCallback(
|
|
41650
|
+
(tile, event) => {
|
|
41651
|
+
if (tileClickEvent) {
|
|
41652
|
+
emit(tileClickEvent, {
|
|
41653
|
+
tileId: tile.id,
|
|
41654
|
+
x: tile.x,
|
|
41655
|
+
z: tile.z ?? tile.y ?? 0,
|
|
41656
|
+
type: tile.type,
|
|
41657
|
+
terrain: tile.terrain,
|
|
41658
|
+
elevation: tile.elevation
|
|
41659
|
+
});
|
|
41660
|
+
}
|
|
41661
|
+
optionsRef.current.onTileClick?.(tile, event);
|
|
41662
|
+
},
|
|
41663
|
+
[tileClickEvent, emit]
|
|
41664
|
+
);
|
|
41665
|
+
const handleUnitClick = useCallback(
|
|
41666
|
+
(unit, event) => {
|
|
41667
|
+
if (unitClickEvent) {
|
|
41668
|
+
emit(unitClickEvent, {
|
|
41669
|
+
unitId: unit.id,
|
|
41670
|
+
x: unit.x,
|
|
41671
|
+
z: unit.z ?? unit.y ?? 0,
|
|
41672
|
+
unitType: unit.unitType,
|
|
41673
|
+
name: unit.name,
|
|
41674
|
+
team: unit.team,
|
|
41675
|
+
faction: unit.faction,
|
|
41676
|
+
health: unit.health,
|
|
41677
|
+
maxHealth: unit.maxHealth
|
|
41678
|
+
});
|
|
41679
|
+
}
|
|
41680
|
+
optionsRef.current.onUnitClick?.(unit, event);
|
|
41681
|
+
},
|
|
41682
|
+
[unitClickEvent, emit]
|
|
41683
|
+
);
|
|
41684
|
+
const handleFeatureClick = useCallback(
|
|
41685
|
+
(feature, event) => {
|
|
41686
|
+
if (featureClickEvent) {
|
|
41687
|
+
emit(featureClickEvent, {
|
|
41688
|
+
featureId: feature.id,
|
|
41689
|
+
x: feature.x,
|
|
41690
|
+
z: feature.z ?? feature.y ?? 0,
|
|
41691
|
+
type: feature.type,
|
|
41692
|
+
elevation: feature.elevation
|
|
41693
|
+
});
|
|
41694
|
+
}
|
|
41695
|
+
optionsRef.current.onFeatureClick?.(feature, event);
|
|
41696
|
+
},
|
|
41697
|
+
[featureClickEvent, emit]
|
|
41698
|
+
);
|
|
41699
|
+
const handleCanvasClick = useCallback(
|
|
41700
|
+
(event) => {
|
|
41701
|
+
if (canvasClickEvent) {
|
|
41702
|
+
emit(canvasClickEvent, {
|
|
41703
|
+
clientX: event.clientX,
|
|
41704
|
+
clientY: event.clientY,
|
|
41705
|
+
button: event.button
|
|
41706
|
+
});
|
|
41707
|
+
}
|
|
41708
|
+
optionsRef.current.onCanvasClick?.(event);
|
|
41709
|
+
},
|
|
41710
|
+
[canvasClickEvent, emit]
|
|
41711
|
+
);
|
|
41712
|
+
const handleTileHover = useCallback(
|
|
41713
|
+
(tile, event) => {
|
|
41714
|
+
if (tile) {
|
|
41715
|
+
if (tileHoverEvent) {
|
|
41716
|
+
emit(tileHoverEvent, {
|
|
41717
|
+
tileId: tile.id,
|
|
41718
|
+
x: tile.x,
|
|
41719
|
+
z: tile.z ?? tile.y ?? 0,
|
|
41720
|
+
type: tile.type
|
|
41721
|
+
});
|
|
41722
|
+
}
|
|
41723
|
+
} else {
|
|
41724
|
+
if (tileLeaveEvent) {
|
|
41725
|
+
emit(tileLeaveEvent, {});
|
|
41726
|
+
}
|
|
41727
|
+
}
|
|
41728
|
+
optionsRef.current.onTileHover?.(tile, event);
|
|
41729
|
+
},
|
|
41730
|
+
[tileHoverEvent, tileLeaveEvent, emit]
|
|
41731
|
+
);
|
|
41732
|
+
const handleUnitAnimation = useCallback(
|
|
41733
|
+
(unitId, state) => {
|
|
41734
|
+
if (unitAnimationEvent) {
|
|
41735
|
+
emit(unitAnimationEvent, {
|
|
41736
|
+
unitId,
|
|
41737
|
+
state,
|
|
41738
|
+
timestamp: Date.now()
|
|
41739
|
+
});
|
|
41740
|
+
}
|
|
41741
|
+
optionsRef.current.onUnitAnimation?.(unitId, state);
|
|
41742
|
+
},
|
|
41743
|
+
[unitAnimationEvent, emit]
|
|
41744
|
+
);
|
|
41745
|
+
const handleCameraChange = useCallback(
|
|
41746
|
+
(position) => {
|
|
41747
|
+
if (cameraChangeEvent) {
|
|
41748
|
+
emit(cameraChangeEvent, {
|
|
41749
|
+
position,
|
|
41750
|
+
timestamp: Date.now()
|
|
41751
|
+
});
|
|
41752
|
+
}
|
|
41753
|
+
},
|
|
41754
|
+
[cameraChangeEvent, emit]
|
|
41755
|
+
);
|
|
41756
|
+
return {
|
|
41757
|
+
handleTileClick,
|
|
41758
|
+
handleUnitClick,
|
|
41759
|
+
handleFeatureClick,
|
|
41760
|
+
handleCanvasClick,
|
|
41761
|
+
handleTileHover,
|
|
41762
|
+
handleUnitAnimation,
|
|
41763
|
+
handleCameraChange
|
|
41764
|
+
};
|
|
41765
|
+
}
|
|
41766
|
+
var init_useGameCanvas3DEvents = __esm({
|
|
41767
|
+
"components/game/molecules/three/hooks/useGameCanvas3DEvents.ts"() {
|
|
41768
|
+
"use client";
|
|
41769
|
+
init_useEventBus();
|
|
41770
|
+
}
|
|
41771
|
+
});
|
|
41772
|
+
|
|
41773
|
+
// components/game/molecules/three/components/Canvas3DLoadingState.css
|
|
41774
|
+
var init_Canvas3DLoadingState = __esm({
|
|
41775
|
+
"components/game/molecules/three/components/Canvas3DLoadingState.css"() {
|
|
41776
|
+
}
|
|
41777
|
+
});
|
|
41778
|
+
function Canvas3DLoadingState({
|
|
41779
|
+
progress = 0,
|
|
41780
|
+
loaded = 0,
|
|
41781
|
+
total = 0,
|
|
41782
|
+
message = "Loading 3D Scene...",
|
|
41783
|
+
details,
|
|
41784
|
+
showSpinner = true,
|
|
41785
|
+
className
|
|
41786
|
+
}) {
|
|
41787
|
+
const clampedProgress = Math.max(0, Math.min(100, progress));
|
|
41788
|
+
const hasProgress = total > 0;
|
|
41789
|
+
return /* @__PURE__ */ jsxs("div", { className: `canvas-3d-loading ${className || ""}`, children: [
|
|
41790
|
+
/* @__PURE__ */ jsxs("div", { className: "canvas-3d-loading__content", children: [
|
|
41791
|
+
showSpinner && /* @__PURE__ */ jsxs("div", { className: "canvas-3d-loading__spinner", children: [
|
|
41792
|
+
/* @__PURE__ */ jsx("div", { className: "spinner__ring" }),
|
|
41793
|
+
/* @__PURE__ */ jsx("div", { className: "spinner__ring spinner__ring--secondary" })
|
|
41794
|
+
] }),
|
|
41795
|
+
/* @__PURE__ */ jsx("div", { className: "canvas-3d-loading__message", children: message }),
|
|
41796
|
+
details && /* @__PURE__ */ jsx("div", { className: "canvas-3d-loading__details", children: details }),
|
|
41797
|
+
hasProgress && /* @__PURE__ */ jsxs("div", { className: "canvas-3d-loading__progress", children: [
|
|
41798
|
+
/* @__PURE__ */ jsx("div", { className: "progress__bar", children: /* @__PURE__ */ jsx(
|
|
41799
|
+
"div",
|
|
41800
|
+
{
|
|
41801
|
+
className: "progress__fill",
|
|
41802
|
+
style: { width: `${clampedProgress}%` }
|
|
41803
|
+
}
|
|
41804
|
+
) }),
|
|
41805
|
+
/* @__PURE__ */ jsxs("div", { className: "progress__text", children: [
|
|
41806
|
+
/* @__PURE__ */ jsxs("span", { className: "progress__percentage", children: [
|
|
41807
|
+
clampedProgress,
|
|
41808
|
+
"%"
|
|
41809
|
+
] }),
|
|
41810
|
+
/* @__PURE__ */ jsxs("span", { className: "progress__count", children: [
|
|
41811
|
+
"(",
|
|
41812
|
+
loaded,
|
|
41813
|
+
"/",
|
|
41814
|
+
total,
|
|
41815
|
+
")"
|
|
41816
|
+
] })
|
|
41817
|
+
] })
|
|
41818
|
+
] })
|
|
41819
|
+
] }),
|
|
41820
|
+
/* @__PURE__ */ jsx("div", { className: "canvas-3d-loading__background", children: /* @__PURE__ */ jsx("div", { className: "bg__grid" }) })
|
|
41821
|
+
] });
|
|
41822
|
+
}
|
|
41823
|
+
var init_Canvas3DLoadingState2 = __esm({
|
|
41824
|
+
"components/game/molecules/three/components/Canvas3DLoadingState.tsx"() {
|
|
41825
|
+
init_Canvas3DLoadingState();
|
|
41826
|
+
}
|
|
41827
|
+
});
|
|
41828
|
+
|
|
41829
|
+
// components/game/molecules/three/components/Canvas3DErrorBoundary.css
|
|
41830
|
+
var init_Canvas3DErrorBoundary = __esm({
|
|
41831
|
+
"components/game/molecules/three/components/Canvas3DErrorBoundary.css"() {
|
|
41832
|
+
}
|
|
41833
|
+
});
|
|
41834
|
+
var log11, Canvas3DErrorBoundary;
|
|
41835
|
+
var init_Canvas3DErrorBoundary2 = __esm({
|
|
41836
|
+
"components/game/molecules/three/components/Canvas3DErrorBoundary.tsx"() {
|
|
41837
|
+
init_Canvas3DErrorBoundary();
|
|
41838
|
+
log11 = createLogger("almadar:ui:game:canvas3d:error-boundary");
|
|
41839
|
+
Canvas3DErrorBoundary = class extends Component {
|
|
41840
|
+
constructor(props) {
|
|
41841
|
+
super(props);
|
|
41842
|
+
__publicField(this, "handleReset", () => {
|
|
41843
|
+
this.setState({
|
|
41844
|
+
hasError: false,
|
|
41845
|
+
error: null,
|
|
41846
|
+
errorInfo: null
|
|
41847
|
+
});
|
|
41848
|
+
this.props.onReset?.();
|
|
41849
|
+
});
|
|
41850
|
+
this.state = {
|
|
41851
|
+
hasError: false,
|
|
41852
|
+
error: null,
|
|
41853
|
+
errorInfo: null
|
|
41854
|
+
};
|
|
41855
|
+
}
|
|
41856
|
+
static getDerivedStateFromError(error) {
|
|
41857
|
+
return {
|
|
41858
|
+
hasError: true,
|
|
41859
|
+
error,
|
|
41860
|
+
errorInfo: null
|
|
41861
|
+
};
|
|
41862
|
+
}
|
|
41863
|
+
componentDidCatch(error, errorInfo) {
|
|
41864
|
+
this.setState({ errorInfo });
|
|
41865
|
+
this.props.onError?.(error, errorInfo);
|
|
41866
|
+
log11.error("Error caught", { error });
|
|
41867
|
+
log11.error("Component stack", { componentStack: errorInfo.componentStack ?? "<none>" });
|
|
41868
|
+
}
|
|
41869
|
+
render() {
|
|
41870
|
+
if (this.state.hasError) {
|
|
41871
|
+
if (this.props.fallback) {
|
|
41872
|
+
return this.props.fallback;
|
|
41873
|
+
}
|
|
41874
|
+
return /* @__PURE__ */ jsx("div", { className: "canvas-3d-error", children: /* @__PURE__ */ jsxs("div", { className: "canvas-3d-error__content", children: [
|
|
41875
|
+
/* @__PURE__ */ jsx("div", { className: "canvas-3d-error__icon", children: "\u26A0\uFE0F" }),
|
|
41876
|
+
/* @__PURE__ */ jsx("h2", { className: "canvas-3d-error__title", children: "3D Scene Error" }),
|
|
41877
|
+
/* @__PURE__ */ jsx("p", { className: "canvas-3d-error__message", children: "Something went wrong while rendering the 3D scene." }),
|
|
41878
|
+
this.state.error && /* @__PURE__ */ jsxs("details", { className: "canvas-3d-error__details", children: [
|
|
41879
|
+
/* @__PURE__ */ jsx("summary", { children: "Error Details" }),
|
|
41880
|
+
/* @__PURE__ */ jsxs("pre", { className: "error__stack", children: [
|
|
41881
|
+
this.state.error.message,
|
|
41882
|
+
"\n",
|
|
41883
|
+
this.state.error.stack
|
|
41884
|
+
] }),
|
|
41885
|
+
this.state.errorInfo && /* @__PURE__ */ jsx("pre", { className: "error__component-stack", children: this.state.errorInfo.componentStack })
|
|
41886
|
+
] }),
|
|
41887
|
+
/* @__PURE__ */ jsxs("div", { className: "canvas-3d-error__actions", children: [
|
|
41888
|
+
/* @__PURE__ */ jsx(
|
|
41889
|
+
"button",
|
|
41890
|
+
{
|
|
41891
|
+
className: "error__button error__button--primary",
|
|
41892
|
+
onClick: this.handleReset,
|
|
41893
|
+
children: "Try Again"
|
|
41894
|
+
}
|
|
41895
|
+
),
|
|
41896
|
+
/* @__PURE__ */ jsx(
|
|
41897
|
+
"button",
|
|
41898
|
+
{
|
|
41899
|
+
className: "error__button error__button--secondary",
|
|
41900
|
+
onClick: () => window.location.reload(),
|
|
41901
|
+
children: "Reload Page"
|
|
41902
|
+
}
|
|
41903
|
+
)
|
|
41904
|
+
] })
|
|
41905
|
+
] }) });
|
|
41906
|
+
}
|
|
41907
|
+
return this.props.children;
|
|
41908
|
+
}
|
|
41909
|
+
};
|
|
41910
|
+
}
|
|
41911
|
+
});
|
|
41912
|
+
function detectAssetRoot2(modelUrl) {
|
|
41913
|
+
const idx = modelUrl.indexOf("/3d/");
|
|
41914
|
+
if (idx !== -1) {
|
|
41915
|
+
return modelUrl.substring(0, idx + 4);
|
|
41916
|
+
}
|
|
41917
|
+
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
41918
|
+
}
|
|
41919
|
+
function useGLTFModel(url, resourceBasePath) {
|
|
41920
|
+
const [state, setState] = useState({
|
|
41921
|
+
model: null,
|
|
41922
|
+
isLoading: false,
|
|
41923
|
+
error: null
|
|
41924
|
+
});
|
|
41925
|
+
useEffect(() => {
|
|
41926
|
+
if (!url) {
|
|
41927
|
+
setState({ model: null, isLoading: false, error: null });
|
|
41928
|
+
return;
|
|
41929
|
+
}
|
|
41930
|
+
log12.debug("Loading", { url });
|
|
41931
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
41932
|
+
const assetRoot = resourceBasePath || detectAssetRoot2(url);
|
|
41933
|
+
const loader = new GLTFLoader$1();
|
|
41934
|
+
loader.setResourcePath(assetRoot);
|
|
41935
|
+
loader.load(
|
|
41936
|
+
url,
|
|
41937
|
+
(gltf) => {
|
|
41938
|
+
log12.debug("Loaded", { url });
|
|
41939
|
+
setState({
|
|
41940
|
+
model: gltf.scene,
|
|
41941
|
+
isLoading: false,
|
|
41942
|
+
error: null
|
|
41943
|
+
});
|
|
41944
|
+
},
|
|
41945
|
+
void 0,
|
|
41946
|
+
(err) => {
|
|
41947
|
+
log12.warn("Failed", { url, error: err instanceof Error ? err : String(err) });
|
|
41948
|
+
setState({
|
|
41949
|
+
model: null,
|
|
41950
|
+
isLoading: false,
|
|
41951
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
41952
|
+
});
|
|
41953
|
+
}
|
|
41954
|
+
);
|
|
41955
|
+
}, [url, resourceBasePath]);
|
|
41956
|
+
return state;
|
|
41957
|
+
}
|
|
41958
|
+
function ModelLoader({
|
|
41959
|
+
url,
|
|
41960
|
+
position = [0, 0, 0],
|
|
41961
|
+
scale = 1,
|
|
41962
|
+
rotation = [0, 0, 0],
|
|
41963
|
+
isSelected = false,
|
|
41964
|
+
isHovered = false,
|
|
41965
|
+
onClick,
|
|
41966
|
+
onHover,
|
|
41967
|
+
fallbackGeometry = "box",
|
|
41968
|
+
castShadow = true,
|
|
41969
|
+
receiveShadow = true,
|
|
41970
|
+
resourceBasePath
|
|
41971
|
+
}) {
|
|
41972
|
+
const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
|
|
41973
|
+
const model = useMemo(() => {
|
|
41974
|
+
if (!loadedModel) return null;
|
|
41975
|
+
const cloned = loadedModel.clone();
|
|
41976
|
+
cloned.traverse((child) => {
|
|
41977
|
+
if (child instanceof THREE.Mesh) {
|
|
41978
|
+
child.castShadow = castShadow;
|
|
41979
|
+
child.receiveShadow = receiveShadow;
|
|
41980
|
+
}
|
|
41981
|
+
});
|
|
41982
|
+
return cloned;
|
|
41983
|
+
}, [loadedModel, castShadow, receiveShadow]);
|
|
41984
|
+
const scaleArray = useMemo(() => {
|
|
41985
|
+
if (typeof scale === "number") {
|
|
41986
|
+
return [scale, scale, scale];
|
|
41987
|
+
}
|
|
41988
|
+
return scale;
|
|
41989
|
+
}, [scale]);
|
|
41990
|
+
const rotationRad = useMemo(() => {
|
|
41991
|
+
return [
|
|
41992
|
+
rotation[0] * Math.PI / 180,
|
|
41993
|
+
rotation[1] * Math.PI / 180,
|
|
41994
|
+
rotation[2] * Math.PI / 180
|
|
41995
|
+
];
|
|
41996
|
+
}, [rotation]);
|
|
41997
|
+
if (isLoading) {
|
|
41998
|
+
return /* @__PURE__ */ jsx("group", { position, children: /* @__PURE__ */ jsxs("mesh", { rotation: [Math.PI / 2, 0, 0], children: [
|
|
41999
|
+
/* @__PURE__ */ jsx("ringGeometry", { args: [0.3, 0.35, 16] }),
|
|
42000
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { color: "#4a90d9", transparent: true, opacity: 0.8 })
|
|
42001
|
+
] }) });
|
|
42002
|
+
}
|
|
42003
|
+
if (error || !model) {
|
|
42004
|
+
if (fallbackGeometry === "none") {
|
|
42005
|
+
return /* @__PURE__ */ jsx("group", { position });
|
|
42006
|
+
}
|
|
42007
|
+
const fallbackProps = {
|
|
42008
|
+
onClick,
|
|
42009
|
+
onPointerOver: () => onHover?.(true),
|
|
42010
|
+
onPointerOut: () => onHover?.(false)
|
|
42011
|
+
};
|
|
42012
|
+
return /* @__PURE__ */ jsxs("group", { position, children: [
|
|
42013
|
+
(isSelected || isHovered) && /* @__PURE__ */ jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
42014
|
+
/* @__PURE__ */ jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
|
|
42015
|
+
/* @__PURE__ */ jsx(
|
|
42016
|
+
"meshBasicMaterial",
|
|
42017
|
+
{
|
|
42018
|
+
color: isSelected ? 16755200 : 16777215,
|
|
42019
|
+
transparent: true,
|
|
42020
|
+
opacity: 0.5
|
|
42021
|
+
}
|
|
42022
|
+
)
|
|
42023
|
+
] }),
|
|
42024
|
+
fallbackGeometry === "box" && /* @__PURE__ */ jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
42025
|
+
/* @__PURE__ */ jsx("boxGeometry", { args: [0.8, 0.8, 0.8] }),
|
|
42026
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
42027
|
+
] }),
|
|
42028
|
+
fallbackGeometry === "sphere" && /* @__PURE__ */ jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
42029
|
+
/* @__PURE__ */ jsx("sphereGeometry", { args: [0.4, 16, 16] }),
|
|
42030
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
42031
|
+
] }),
|
|
42032
|
+
fallbackGeometry === "cylinder" && /* @__PURE__ */ jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
42033
|
+
/* @__PURE__ */ jsx("cylinderGeometry", { args: [0.3, 0.3, 0.8, 16] }),
|
|
42034
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
42035
|
+
] })
|
|
42036
|
+
] });
|
|
42037
|
+
}
|
|
42038
|
+
return /* @__PURE__ */ jsxs(
|
|
42039
|
+
"group",
|
|
42040
|
+
{
|
|
42041
|
+
position,
|
|
42042
|
+
rotation: rotationRad,
|
|
42043
|
+
onClick,
|
|
42044
|
+
onPointerOver: () => onHover?.(true),
|
|
42045
|
+
onPointerOut: () => onHover?.(false),
|
|
42046
|
+
children: [
|
|
42047
|
+
(isSelected || isHovered) && /* @__PURE__ */ jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
42048
|
+
/* @__PURE__ */ jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
|
|
42049
|
+
/* @__PURE__ */ jsx(
|
|
42050
|
+
"meshBasicMaterial",
|
|
42051
|
+
{
|
|
42052
|
+
color: isSelected ? 16755200 : 16777215,
|
|
42053
|
+
transparent: true,
|
|
42054
|
+
opacity: 0.5
|
|
42055
|
+
}
|
|
42056
|
+
)
|
|
42057
|
+
] }),
|
|
42058
|
+
/* @__PURE__ */ jsx("primitive", { object: model, scale: scaleArray })
|
|
42059
|
+
]
|
|
42060
|
+
}
|
|
42061
|
+
);
|
|
42062
|
+
}
|
|
42063
|
+
var log12;
|
|
42064
|
+
var init_ModelLoader = __esm({
|
|
42065
|
+
"components/game/molecules/three/components/ModelLoader.tsx"() {
|
|
42066
|
+
"use client";
|
|
42067
|
+
log12 = createLogger("almadar:ui:game:model-loader");
|
|
42068
|
+
}
|
|
42069
|
+
});
|
|
42070
|
+
|
|
42071
|
+
// components/game/molecules/GameCanvas3D.css
|
|
42072
|
+
var init_GameCanvas3D = __esm({
|
|
42073
|
+
"components/game/molecules/GameCanvas3D.css"() {
|
|
42074
|
+
}
|
|
42075
|
+
});
|
|
42076
|
+
function CameraController({
|
|
42077
|
+
onCameraChange
|
|
42078
|
+
}) {
|
|
42079
|
+
const { camera } = useThree();
|
|
42080
|
+
useEffect(() => {
|
|
42081
|
+
if (onCameraChange) {
|
|
42082
|
+
onCameraChange({
|
|
42083
|
+
x: camera.position.x,
|
|
42084
|
+
y: camera.position.y,
|
|
42085
|
+
z: camera.position.z
|
|
42086
|
+
});
|
|
42087
|
+
}
|
|
42088
|
+
}, [camera.position, onCameraChange]);
|
|
42089
|
+
return null;
|
|
42090
|
+
}
|
|
42091
|
+
var DEFAULT_GRID_CONFIG, GameCanvas3D;
|
|
42092
|
+
var init_GameCanvas3D2 = __esm({
|
|
42093
|
+
"components/game/molecules/GameCanvas3D.tsx"() {
|
|
42094
|
+
"use client";
|
|
42095
|
+
init_AssetLoader();
|
|
42096
|
+
init_useAssetLoader();
|
|
42097
|
+
init_useGameCanvas3DEvents();
|
|
42098
|
+
init_Canvas3DLoadingState2();
|
|
42099
|
+
init_Canvas3DErrorBoundary2();
|
|
42100
|
+
init_ModelLoader();
|
|
42101
|
+
init_cn();
|
|
42102
|
+
init_GameCanvas3D();
|
|
42103
|
+
DEFAULT_GRID_CONFIG = {
|
|
42104
|
+
cellSize: 1,
|
|
42105
|
+
offsetX: 0,
|
|
42106
|
+
offsetZ: 0
|
|
42107
|
+
};
|
|
42108
|
+
GameCanvas3D = forwardRef(
|
|
42109
|
+
({
|
|
42110
|
+
tiles = [],
|
|
42111
|
+
units = [],
|
|
42112
|
+
features = [],
|
|
42113
|
+
events: events2 = [],
|
|
42114
|
+
orientation = "standard",
|
|
42115
|
+
cameraMode = "isometric",
|
|
42116
|
+
showGrid = true,
|
|
42117
|
+
showCoordinates = false,
|
|
42118
|
+
showTileInfo = false,
|
|
42119
|
+
overlay = "default",
|
|
42120
|
+
shadows = true,
|
|
42121
|
+
backgroundColor = "#1a1a2e",
|
|
42122
|
+
onTileClick,
|
|
42123
|
+
onUnitClick,
|
|
42124
|
+
onFeatureClick,
|
|
42125
|
+
onCanvasClick,
|
|
42126
|
+
onTileHover,
|
|
42127
|
+
onUnitAnimation,
|
|
42128
|
+
assetLoader: customAssetLoader,
|
|
42129
|
+
tileRenderer: CustomTileRenderer,
|
|
42130
|
+
unitRenderer: CustomUnitRenderer,
|
|
42131
|
+
featureRenderer: CustomFeatureRenderer,
|
|
42132
|
+
className,
|
|
42133
|
+
isLoading: externalLoading,
|
|
42134
|
+
error: externalError,
|
|
42135
|
+
entity,
|
|
42136
|
+
preloadAssets = [],
|
|
42137
|
+
tileClickEvent,
|
|
42138
|
+
unitClickEvent,
|
|
42139
|
+
featureClickEvent,
|
|
42140
|
+
canvasClickEvent,
|
|
42141
|
+
tileHoverEvent,
|
|
42142
|
+
tileLeaveEvent,
|
|
42143
|
+
unitAnimationEvent,
|
|
42144
|
+
cameraChangeEvent,
|
|
42145
|
+
loadingMessage = "Loading 3D Scene...",
|
|
42146
|
+
useInstancing = true,
|
|
42147
|
+
validMoves = [],
|
|
42148
|
+
attackTargets = [],
|
|
42149
|
+
selectedTileIds = [],
|
|
42150
|
+
selectedUnitId = null,
|
|
42151
|
+
children
|
|
42152
|
+
}, ref) => {
|
|
42153
|
+
const containerRef = useRef(null);
|
|
42154
|
+
const controlsRef = useRef(null);
|
|
42155
|
+
const [hoveredTile, setHoveredTile] = useState(null);
|
|
42156
|
+
const [internalError, setInternalError] = useState(null);
|
|
42157
|
+
const { isLoading: assetsLoading, progress, loaded, total } = useAssetLoader({
|
|
42158
|
+
preloadUrls: preloadAssets,
|
|
42159
|
+
loader: customAssetLoader
|
|
42160
|
+
});
|
|
42161
|
+
const eventHandlers = useGameCanvas3DEvents({
|
|
42162
|
+
tileClickEvent,
|
|
42163
|
+
unitClickEvent,
|
|
42164
|
+
featureClickEvent,
|
|
42165
|
+
canvasClickEvent,
|
|
42166
|
+
tileHoverEvent,
|
|
42167
|
+
tileLeaveEvent,
|
|
42168
|
+
unitAnimationEvent,
|
|
42169
|
+
cameraChangeEvent,
|
|
42170
|
+
onTileClick,
|
|
42171
|
+
onUnitClick,
|
|
42172
|
+
onFeatureClick,
|
|
42173
|
+
onCanvasClick,
|
|
42174
|
+
onTileHover,
|
|
42175
|
+
onUnitAnimation
|
|
42176
|
+
});
|
|
42177
|
+
const gridBounds = useMemo(() => {
|
|
42178
|
+
if (tiles.length === 0) {
|
|
42179
|
+
return { minX: 0, maxX: 10, minZ: 0, maxZ: 10 };
|
|
42180
|
+
}
|
|
42181
|
+
const xs = tiles.map((t) => t.x);
|
|
42182
|
+
const zs = tiles.map((t) => t.z || t.y || 0);
|
|
42183
|
+
return {
|
|
42184
|
+
minX: Math.min(...xs),
|
|
42185
|
+
maxX: Math.max(...xs),
|
|
42186
|
+
minZ: Math.min(...zs),
|
|
42187
|
+
maxZ: Math.max(...zs)
|
|
42188
|
+
};
|
|
42189
|
+
}, [tiles]);
|
|
42190
|
+
const cameraTarget = useMemo(() => {
|
|
42191
|
+
return [
|
|
42192
|
+
(gridBounds.minX + gridBounds.maxX) / 2,
|
|
42193
|
+
0,
|
|
42194
|
+
(gridBounds.minZ + gridBounds.maxZ) / 2
|
|
42195
|
+
];
|
|
42196
|
+
}, [gridBounds]);
|
|
42197
|
+
const gridConfig = useMemo(
|
|
42198
|
+
() => ({
|
|
42199
|
+
...DEFAULT_GRID_CONFIG,
|
|
42200
|
+
offsetX: -(gridBounds.maxX - gridBounds.minX) / 2,
|
|
42201
|
+
offsetZ: -(gridBounds.maxZ - gridBounds.minZ) / 2
|
|
42202
|
+
}),
|
|
42203
|
+
[gridBounds]
|
|
42204
|
+
);
|
|
42205
|
+
const gridToWorld = useCallback(
|
|
42206
|
+
(x, z, y = 0) => {
|
|
42207
|
+
const worldX = (x - gridBounds.minX) * gridConfig.cellSize;
|
|
42208
|
+
const worldZ = (z - gridBounds.minZ) * gridConfig.cellSize;
|
|
42209
|
+
return [worldX, y * gridConfig.cellSize, worldZ];
|
|
42210
|
+
},
|
|
42211
|
+
[gridBounds, gridConfig]
|
|
42212
|
+
);
|
|
42213
|
+
useImperativeHandle(ref, () => ({
|
|
42214
|
+
getCameraPosition: () => {
|
|
42215
|
+
if (controlsRef.current) {
|
|
42216
|
+
const pos = controlsRef.current.object.position;
|
|
42217
|
+
return new THREE.Vector3(pos.x, pos.y, pos.z);
|
|
42218
|
+
}
|
|
42219
|
+
return null;
|
|
42220
|
+
},
|
|
42221
|
+
setCameraPosition: (x, y, z) => {
|
|
42222
|
+
if (controlsRef.current) {
|
|
42223
|
+
controlsRef.current.object.position.set(x, y, z);
|
|
42224
|
+
controlsRef.current.update();
|
|
42225
|
+
}
|
|
42226
|
+
},
|
|
42227
|
+
lookAt: (x, y, z) => {
|
|
42228
|
+
if (controlsRef.current) {
|
|
42229
|
+
controlsRef.current.target.set(x, y, z);
|
|
42230
|
+
controlsRef.current.update();
|
|
42231
|
+
}
|
|
42232
|
+
},
|
|
42233
|
+
resetCamera: () => {
|
|
42234
|
+
if (controlsRef.current) {
|
|
42235
|
+
controlsRef.current.reset();
|
|
42236
|
+
}
|
|
42237
|
+
},
|
|
42238
|
+
screenshot: () => {
|
|
42239
|
+
const canvas = containerRef.current?.querySelector("canvas");
|
|
42240
|
+
if (canvas) {
|
|
42241
|
+
return canvas.toDataURL("image/png");
|
|
42242
|
+
}
|
|
42243
|
+
return null;
|
|
42244
|
+
},
|
|
42245
|
+
export: () => ({
|
|
42246
|
+
tiles,
|
|
42247
|
+
units,
|
|
42248
|
+
features
|
|
42249
|
+
})
|
|
42250
|
+
}));
|
|
42251
|
+
const handleTileClick = useCallback(
|
|
42252
|
+
(tile, event) => {
|
|
42253
|
+
eventHandlers.handleTileClick(tile, event);
|
|
42254
|
+
},
|
|
42255
|
+
[eventHandlers]
|
|
42256
|
+
);
|
|
42257
|
+
const handleUnitClick = useCallback(
|
|
42258
|
+
(unit, event) => {
|
|
42259
|
+
eventHandlers.handleUnitClick(unit, event);
|
|
42260
|
+
},
|
|
42261
|
+
[eventHandlers]
|
|
42262
|
+
);
|
|
42263
|
+
const handleFeatureClick = useCallback(
|
|
42264
|
+
(feature, event) => {
|
|
42265
|
+
if (event) {
|
|
42266
|
+
eventHandlers.handleFeatureClick(feature, event);
|
|
42267
|
+
}
|
|
42268
|
+
},
|
|
42269
|
+
[eventHandlers]
|
|
42270
|
+
);
|
|
42271
|
+
const handleTileHover = useCallback(
|
|
42272
|
+
(tile, event) => {
|
|
42273
|
+
setHoveredTile(tile);
|
|
42274
|
+
if (event) {
|
|
42275
|
+
eventHandlers.handleTileHover(tile, event);
|
|
42276
|
+
}
|
|
42277
|
+
},
|
|
42278
|
+
[eventHandlers]
|
|
42279
|
+
);
|
|
42280
|
+
const cameraConfig = useMemo(() => {
|
|
42281
|
+
const size = Math.max(
|
|
42282
|
+
gridBounds.maxX - gridBounds.minX,
|
|
42283
|
+
gridBounds.maxZ - gridBounds.minZ
|
|
42284
|
+
);
|
|
42285
|
+
const distance = size * 1.5;
|
|
42286
|
+
switch (cameraMode) {
|
|
42287
|
+
case "isometric":
|
|
42288
|
+
return {
|
|
42289
|
+
position: [distance, distance * 0.8, distance],
|
|
42290
|
+
fov: 45
|
|
42291
|
+
};
|
|
42292
|
+
case "top-down":
|
|
42293
|
+
return {
|
|
42294
|
+
position: [0, distance * 2, 0],
|
|
42295
|
+
fov: 45
|
|
42296
|
+
};
|
|
42297
|
+
case "perspective":
|
|
42298
|
+
default:
|
|
42299
|
+
return {
|
|
42300
|
+
position: [distance, distance, distance],
|
|
42301
|
+
fov: 45
|
|
42302
|
+
};
|
|
42303
|
+
}
|
|
42304
|
+
}, [cameraMode, gridBounds]);
|
|
42305
|
+
const DefaultTileRenderer = useCallback(
|
|
42306
|
+
({ tile, position }) => {
|
|
42307
|
+
const isSelected = tile.id ? selectedTileIds.includes(tile.id) : false;
|
|
42308
|
+
const isHovered = hoveredTile?.id === tile.id;
|
|
42309
|
+
const isValidMove = validMoves.some(
|
|
42310
|
+
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
42311
|
+
);
|
|
42312
|
+
const isAttackTarget = attackTargets.some(
|
|
42313
|
+
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
42314
|
+
);
|
|
42315
|
+
let color = 8421504;
|
|
42316
|
+
if (tile.type === "water") color = 4491468;
|
|
42317
|
+
else if (tile.type === "grass") color = 4500036;
|
|
42318
|
+
else if (tile.type === "sand") color = 14535816;
|
|
42319
|
+
else if (tile.type === "rock") color = 8947848;
|
|
42320
|
+
else if (tile.type === "snow") color = 15658734;
|
|
42321
|
+
let emissive = 0;
|
|
42322
|
+
if (isSelected) emissive = 4473924;
|
|
42323
|
+
else if (isAttackTarget) emissive = 4456448;
|
|
42324
|
+
else if (isValidMove) emissive = 17408;
|
|
42325
|
+
else if (isHovered) emissive = 2236962;
|
|
42326
|
+
if (tile.modelUrl) {
|
|
42327
|
+
return /* @__PURE__ */ jsx(
|
|
42328
|
+
"group",
|
|
42329
|
+
{
|
|
42330
|
+
position,
|
|
42331
|
+
onClick: (e) => handleTileClick(tile, e),
|
|
42332
|
+
onPointerEnter: (e) => handleTileHover(tile, e),
|
|
42333
|
+
onPointerLeave: (e) => handleTileHover(null, e),
|
|
42334
|
+
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
42335
|
+
children: /* @__PURE__ */ jsx(
|
|
42336
|
+
ModelLoader,
|
|
42337
|
+
{
|
|
42338
|
+
url: tile.modelUrl,
|
|
42339
|
+
scale: 0.95,
|
|
42340
|
+
fallbackGeometry: "box",
|
|
42341
|
+
castShadow: true,
|
|
42342
|
+
receiveShadow: true
|
|
42343
|
+
}
|
|
42344
|
+
)
|
|
42345
|
+
}
|
|
42346
|
+
);
|
|
42347
|
+
}
|
|
42348
|
+
return /* @__PURE__ */ jsxs(
|
|
42349
|
+
"mesh",
|
|
42350
|
+
{
|
|
42351
|
+
position,
|
|
42352
|
+
onClick: (e) => handleTileClick(tile, e),
|
|
42353
|
+
onPointerEnter: (e) => handleTileHover(tile, e),
|
|
42354
|
+
onPointerLeave: (e) => handleTileHover(null, e),
|
|
42355
|
+
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
42356
|
+
children: [
|
|
42357
|
+
/* @__PURE__ */ jsx("boxGeometry", { args: [0.95, 0.2, 0.95] }),
|
|
42358
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color, emissive })
|
|
42359
|
+
]
|
|
42360
|
+
}
|
|
42361
|
+
);
|
|
42362
|
+
},
|
|
42363
|
+
[selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover]
|
|
42364
|
+
);
|
|
42365
|
+
const DefaultUnitRenderer = useCallback(
|
|
42366
|
+
({ unit, position }) => {
|
|
42367
|
+
const isSelected = selectedUnitId === unit.id;
|
|
42368
|
+
const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
|
|
42369
|
+
return /* @__PURE__ */ jsxs(
|
|
42370
|
+
"group",
|
|
42371
|
+
{
|
|
42372
|
+
position,
|
|
42373
|
+
onClick: (e) => handleUnitClick(unit, e),
|
|
42374
|
+
userData: { type: "unit", unitId: unit.id },
|
|
42375
|
+
children: [
|
|
42376
|
+
isSelected && /* @__PURE__ */ jsxs("mesh", { position: [0, 0.05, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
42377
|
+
/* @__PURE__ */ jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
|
|
42378
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
|
|
42379
|
+
] }),
|
|
42380
|
+
unit.modelUrl ? (
|
|
42381
|
+
/* GLB unit model (box fallback while loading / on error) */
|
|
42382
|
+
/* @__PURE__ */ jsx(
|
|
42383
|
+
ModelLoader,
|
|
42384
|
+
{
|
|
42385
|
+
url: unit.modelUrl,
|
|
42386
|
+
scale: 0.5,
|
|
42387
|
+
fallbackGeometry: "box",
|
|
42388
|
+
castShadow: true
|
|
42389
|
+
}
|
|
42390
|
+
)
|
|
42391
|
+
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
42392
|
+
/* @__PURE__ */ jsxs("mesh", { position: [0, 0.3, 0], children: [
|
|
42393
|
+
/* @__PURE__ */ jsx("cylinderGeometry", { args: [0.3, 0.3, 0.1, 8] }),
|
|
42394
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color })
|
|
42395
|
+
] }),
|
|
42396
|
+
/* @__PURE__ */ jsxs("mesh", { position: [0, 0.6, 0], children: [
|
|
42397
|
+
/* @__PURE__ */ jsx("capsuleGeometry", { args: [0.2, 0.4, 4, 8] }),
|
|
42398
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color })
|
|
42399
|
+
] }),
|
|
42400
|
+
/* @__PURE__ */ jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
42401
|
+
/* @__PURE__ */ jsx("sphereGeometry", { args: [0.12, 8, 8] }),
|
|
42402
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color })
|
|
42403
|
+
] })
|
|
42404
|
+
] }),
|
|
42405
|
+
unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxs("group", { position: [0, 1.2, 0], children: [
|
|
42406
|
+
/* @__PURE__ */ jsxs("mesh", { position: [-0.25, 0, 0], children: [
|
|
42407
|
+
/* @__PURE__ */ jsx("planeGeometry", { args: [0.5, 0.05] }),
|
|
42408
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { color: 3355443 })
|
|
42409
|
+
] }),
|
|
42410
|
+
/* @__PURE__ */ jsxs(
|
|
42411
|
+
"mesh",
|
|
42412
|
+
{
|
|
42413
|
+
position: [
|
|
42414
|
+
-0.25 + 0.5 * (unit.health / unit.maxHealth) / 2,
|
|
42415
|
+
0,
|
|
42416
|
+
0.01
|
|
42417
|
+
],
|
|
42418
|
+
children: [
|
|
42419
|
+
/* @__PURE__ */ jsx("planeGeometry", { args: [0.5 * (unit.health / unit.maxHealth), 0.05] }),
|
|
42420
|
+
/* @__PURE__ */ jsx(
|
|
42421
|
+
"meshBasicMaterial",
|
|
42422
|
+
{
|
|
42423
|
+
color: unit.health / unit.maxHealth > 0.5 ? 4500036 : unit.health / unit.maxHealth > 0.25 ? 11184708 : 16729156
|
|
42424
|
+
}
|
|
42425
|
+
)
|
|
42426
|
+
]
|
|
42427
|
+
}
|
|
42428
|
+
)
|
|
42429
|
+
] })
|
|
42430
|
+
]
|
|
42431
|
+
}
|
|
42432
|
+
);
|
|
42433
|
+
},
|
|
42434
|
+
[selectedUnitId, handleUnitClick]
|
|
42435
|
+
);
|
|
42436
|
+
const DefaultFeatureRenderer = useCallback(
|
|
42437
|
+
({
|
|
42438
|
+
feature,
|
|
42439
|
+
position
|
|
42440
|
+
}) => {
|
|
42441
|
+
if (feature.assetUrl) {
|
|
42442
|
+
return /* @__PURE__ */ jsx(
|
|
42443
|
+
ModelLoader,
|
|
42444
|
+
{
|
|
42445
|
+
url: feature.assetUrl,
|
|
42446
|
+
position,
|
|
42447
|
+
scale: 0.5,
|
|
42448
|
+
rotation: [0, feature.rotation ?? 0, 0],
|
|
42449
|
+
onClick: () => handleFeatureClick(feature, null),
|
|
42450
|
+
fallbackGeometry: "box"
|
|
42451
|
+
},
|
|
42452
|
+
feature.id
|
|
42453
|
+
);
|
|
42454
|
+
}
|
|
42455
|
+
if (feature.type === "tree") {
|
|
42456
|
+
return /* @__PURE__ */ jsxs(
|
|
42457
|
+
"group",
|
|
42458
|
+
{
|
|
42459
|
+
position,
|
|
42460
|
+
onClick: (e) => handleFeatureClick(feature, e),
|
|
42461
|
+
userData: { type: "feature", featureId: feature.id },
|
|
42462
|
+
children: [
|
|
42463
|
+
/* @__PURE__ */ jsxs("mesh", { position: [0, 0.4, 0], children: [
|
|
42464
|
+
/* @__PURE__ */ jsx("cylinderGeometry", { args: [0.1, 0.15, 0.8, 6] }),
|
|
42465
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: 9127187 })
|
|
42466
|
+
] }),
|
|
42467
|
+
/* @__PURE__ */ jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
42468
|
+
/* @__PURE__ */ jsx("coneGeometry", { args: [0.5, 0.8, 8] }),
|
|
42469
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: 2263842 })
|
|
42470
|
+
] })
|
|
42471
|
+
]
|
|
42472
|
+
}
|
|
42473
|
+
);
|
|
42474
|
+
}
|
|
42475
|
+
if (feature.type === "rock") {
|
|
42476
|
+
return /* @__PURE__ */ jsxs(
|
|
42477
|
+
"mesh",
|
|
42478
|
+
{
|
|
42479
|
+
position: [position[0], position[1] + 0.3, position[2]],
|
|
42480
|
+
onClick: (e) => handleFeatureClick(feature, e),
|
|
42481
|
+
userData: { type: "feature", featureId: feature.id },
|
|
42482
|
+
children: [
|
|
42483
|
+
/* @__PURE__ */ jsx("dodecahedronGeometry", { args: [0.3, 0] }),
|
|
42484
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: 8421504 })
|
|
42485
|
+
]
|
|
42486
|
+
}
|
|
42487
|
+
);
|
|
42488
|
+
}
|
|
42489
|
+
return null;
|
|
42490
|
+
},
|
|
42491
|
+
[handleFeatureClick]
|
|
42492
|
+
);
|
|
42493
|
+
if (externalLoading || assetsLoading && preloadAssets.length > 0) {
|
|
42494
|
+
return /* @__PURE__ */ jsx(
|
|
42495
|
+
Canvas3DLoadingState,
|
|
42496
|
+
{
|
|
42497
|
+
progress,
|
|
42498
|
+
loaded,
|
|
42499
|
+
total,
|
|
42500
|
+
message: loadingMessage,
|
|
42501
|
+
className
|
|
42502
|
+
}
|
|
42503
|
+
);
|
|
42504
|
+
}
|
|
42505
|
+
const displayError = externalError || internalError;
|
|
42506
|
+
if (displayError) {
|
|
42507
|
+
return /* @__PURE__ */ jsx(Canvas3DErrorBoundary, { children: /* @__PURE__ */ jsx("div", { className: "game-canvas-3d game-canvas-3d--error", children: /* @__PURE__ */ jsxs("div", { className: "game-canvas-3d__error", children: [
|
|
42508
|
+
"Error: ",
|
|
42509
|
+
displayError
|
|
42510
|
+
] }) }) });
|
|
42511
|
+
}
|
|
42512
|
+
return /* @__PURE__ */ jsx(
|
|
42513
|
+
Canvas3DErrorBoundary,
|
|
42514
|
+
{
|
|
42515
|
+
onError: (err) => setInternalError(err.message),
|
|
42516
|
+
onReset: () => setInternalError(null),
|
|
42517
|
+
children: /* @__PURE__ */ jsxs(
|
|
42518
|
+
"div",
|
|
42519
|
+
{
|
|
42520
|
+
ref: containerRef,
|
|
42521
|
+
className: cn("game-canvas-3d relative w-full h-full min-h-[85vh] overflow-hidden", className),
|
|
42522
|
+
"data-orientation": orientation,
|
|
42523
|
+
"data-camera-mode": cameraMode,
|
|
42524
|
+
"data-overlay": overlay,
|
|
42525
|
+
children: [
|
|
42526
|
+
/* @__PURE__ */ jsxs(
|
|
42527
|
+
Canvas,
|
|
42528
|
+
{
|
|
42529
|
+
shadows,
|
|
42530
|
+
camera: {
|
|
42531
|
+
position: cameraConfig.position,
|
|
42532
|
+
fov: cameraConfig.fov,
|
|
42533
|
+
near: 0.1,
|
|
42534
|
+
far: 1e3
|
|
42535
|
+
},
|
|
42536
|
+
style: { background: backgroundColor },
|
|
42537
|
+
onClick: (e) => {
|
|
42538
|
+
if (e.target === e.currentTarget) {
|
|
42539
|
+
eventHandlers.handleCanvasClick(e);
|
|
42540
|
+
}
|
|
42541
|
+
},
|
|
42542
|
+
children: [
|
|
42543
|
+
/* @__PURE__ */ jsx(CameraController, { onCameraChange: eventHandlers.handleCameraChange }),
|
|
42544
|
+
/* @__PURE__ */ jsx("ambientLight", { intensity: 0.6 }),
|
|
42545
|
+
/* @__PURE__ */ jsx(
|
|
42546
|
+
"directionalLight",
|
|
42547
|
+
{
|
|
42548
|
+
position: [10, 20, 10],
|
|
42549
|
+
intensity: 0.8,
|
|
42550
|
+
castShadow: shadows,
|
|
42551
|
+
"shadow-mapSize": [2048, 2048]
|
|
42552
|
+
}
|
|
42553
|
+
),
|
|
42554
|
+
/* @__PURE__ */ jsx("hemisphereLight", { intensity: 0.3, color: "#87ceeb", groundColor: "#362d1d" }),
|
|
42555
|
+
showGrid && /* @__PURE__ */ jsx(
|
|
42556
|
+
Grid$1,
|
|
42557
|
+
{
|
|
42558
|
+
args: [
|
|
42559
|
+
Math.max(gridBounds.maxX - gridBounds.minX + 2, 10),
|
|
42560
|
+
Math.max(gridBounds.maxZ - gridBounds.minZ + 2, 10)
|
|
42561
|
+
],
|
|
42562
|
+
position: [
|
|
42563
|
+
(gridBounds.maxX - gridBounds.minX) / 2 - 0.5,
|
|
42564
|
+
0,
|
|
42565
|
+
(gridBounds.maxZ - gridBounds.minZ) / 2 - 0.5
|
|
42566
|
+
],
|
|
42567
|
+
cellSize: 1,
|
|
42568
|
+
cellThickness: 1,
|
|
42569
|
+
cellColor: "#444444",
|
|
42570
|
+
sectionSize: 5,
|
|
42571
|
+
sectionThickness: 1.5,
|
|
42572
|
+
sectionColor: "#666666",
|
|
42573
|
+
fadeDistance: 50,
|
|
42574
|
+
fadeStrength: 1
|
|
42575
|
+
}
|
|
42576
|
+
),
|
|
42577
|
+
tiles.map((tile, index) => {
|
|
42578
|
+
const position = gridToWorld(
|
|
42579
|
+
tile.x,
|
|
42580
|
+
tile.z ?? tile.y ?? 0,
|
|
42581
|
+
tile.elevation ?? 0
|
|
42582
|
+
);
|
|
42583
|
+
const Renderer = CustomTileRenderer || DefaultTileRenderer;
|
|
42584
|
+
return /* @__PURE__ */ jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
|
|
42585
|
+
}),
|
|
42586
|
+
features.map((feature, index) => {
|
|
42587
|
+
const position = gridToWorld(
|
|
42588
|
+
feature.x,
|
|
42589
|
+
feature.z ?? feature.y ?? 0,
|
|
42590
|
+
(feature.elevation ?? 0) + 0.5
|
|
42591
|
+
);
|
|
42592
|
+
const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
|
|
42593
|
+
return /* @__PURE__ */ jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
|
|
42594
|
+
}),
|
|
42595
|
+
units.map((unit) => {
|
|
42596
|
+
const position = gridToWorld(
|
|
42597
|
+
unit.x ?? 0,
|
|
42598
|
+
unit.z ?? unit.y ?? 0,
|
|
42599
|
+
(unit.elevation ?? 0) + 0.5
|
|
42600
|
+
);
|
|
42601
|
+
const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
|
|
42602
|
+
return /* @__PURE__ */ jsx(Renderer, { unit, position }, unit.id);
|
|
42603
|
+
}),
|
|
42604
|
+
children,
|
|
42605
|
+
/* @__PURE__ */ jsx(
|
|
42606
|
+
OrbitControls,
|
|
42607
|
+
{
|
|
42608
|
+
ref: controlsRef,
|
|
42609
|
+
target: cameraTarget,
|
|
42610
|
+
enableDamping: true,
|
|
42611
|
+
dampingFactor: 0.05,
|
|
42612
|
+
minDistance: 2,
|
|
42613
|
+
maxDistance: 100,
|
|
42614
|
+
maxPolarAngle: Math.PI / 2 - 0.1
|
|
42615
|
+
}
|
|
42616
|
+
)
|
|
42617
|
+
]
|
|
42618
|
+
}
|
|
42619
|
+
),
|
|
42620
|
+
showCoordinates && hoveredTile && /* @__PURE__ */ jsxs("div", { className: "game-canvas-3d__coordinates", children: [
|
|
42621
|
+
"X: ",
|
|
42622
|
+
hoveredTile.x,
|
|
42623
|
+
", Z: ",
|
|
42624
|
+
hoveredTile.z ?? hoveredTile.y ?? 0
|
|
42625
|
+
] }),
|
|
42626
|
+
showTileInfo && hoveredTile && /* @__PURE__ */ jsxs("div", { className: "game-canvas-3d__tile-info", children: [
|
|
42627
|
+
/* @__PURE__ */ jsx("div", { className: "tile-info__type", children: hoveredTile.type }),
|
|
42628
|
+
hoveredTile.terrain && /* @__PURE__ */ jsx("div", { className: "tile-info__terrain", children: hoveredTile.terrain })
|
|
42629
|
+
] })
|
|
42630
|
+
]
|
|
42631
|
+
}
|
|
42632
|
+
)
|
|
42633
|
+
}
|
|
42634
|
+
);
|
|
42635
|
+
}
|
|
42636
|
+
);
|
|
42637
|
+
GameCanvas3D.displayName = "GameCanvas3D";
|
|
42638
|
+
}
|
|
42639
|
+
});
|
|
42640
|
+
function GameBoard3D({
|
|
42641
|
+
entity,
|
|
42642
|
+
tiles = [],
|
|
42643
|
+
features = [],
|
|
42644
|
+
cameraMode = "perspective",
|
|
42645
|
+
backgroundColor = "#2a1a1a",
|
|
42646
|
+
tileClickEvent,
|
|
42647
|
+
unitClickEvent,
|
|
42648
|
+
attackEvent,
|
|
42649
|
+
endTurnEvent,
|
|
42650
|
+
cancelEvent,
|
|
42651
|
+
playAgainEvent,
|
|
42652
|
+
gameEndEvent,
|
|
42653
|
+
className
|
|
42654
|
+
}) {
|
|
42655
|
+
const row = boardEntity(entity);
|
|
42656
|
+
const eventBus = useEventBus();
|
|
42657
|
+
const entityUnits = row ? rows(row.units) : [];
|
|
42658
|
+
const units = entityUnits;
|
|
42659
|
+
const selectedUnitId = row ? str(row.selectedUnitId) || null : null;
|
|
42660
|
+
const phase = row ? str(row.phase) : "observation";
|
|
42661
|
+
const result = row ? str(row.result) : "none";
|
|
42662
|
+
const validMoves = row && Array.isArray(row.validMoves) ? row.validMoves : [];
|
|
42663
|
+
const attackTargets = row && Array.isArray(row.attackTargets) ? row.attackTargets : [];
|
|
42664
|
+
const turn = row ? num(row.turn) : 0;
|
|
42665
|
+
const currentTeam = row ? str(row.currentTeam) : "player";
|
|
42666
|
+
const isGameOver = result !== "none";
|
|
42667
|
+
const gameEndEmittedRef = useRef(false);
|
|
42668
|
+
useEffect(() => {
|
|
42669
|
+
if ((result === "victory" || result === "defeat") && gameEndEvent) {
|
|
42670
|
+
if (!gameEndEmittedRef.current) {
|
|
42671
|
+
gameEndEmittedRef.current = true;
|
|
42672
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
42673
|
+
}
|
|
42674
|
+
} else {
|
|
42675
|
+
gameEndEmittedRef.current = false;
|
|
42676
|
+
}
|
|
42677
|
+
}, [result, gameEndEvent, eventBus]);
|
|
42678
|
+
const checkGameEnd = useCallback(() => {
|
|
42679
|
+
const alivePlayer = entityUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
|
|
42680
|
+
const aliveEnemy = entityUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
|
|
42681
|
+
if (alivePlayer.length === 0 && gameEndEvent) {
|
|
42682
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result: "defeat" });
|
|
42683
|
+
} else if (aliveEnemy.length === 0 && gameEndEvent) {
|
|
42684
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result: "victory" });
|
|
42685
|
+
}
|
|
42686
|
+
}, [entityUnits, gameEndEvent, eventBus]);
|
|
42687
|
+
const handleUnitClickCallback = useCallback((isoUnit) => {
|
|
42688
|
+
if (phase !== "action" || !selectedUnitId || !attackEvent) return;
|
|
42689
|
+
const attackerRow = entityUnits.find((u) => str(u.id) === selectedUnitId);
|
|
42690
|
+
const targetRow = entityUnits.find((u) => str(u.id) === isoUnit.id);
|
|
42691
|
+
if (!attackerRow || !targetRow) return;
|
|
42692
|
+
if (unitTeam(targetRow) !== "enemy" || unitHealth(targetRow) <= 0) return;
|
|
42693
|
+
const ap = unitPosition(attackerRow);
|
|
42694
|
+
const tp = unitPosition(targetRow);
|
|
42695
|
+
const dx = Math.abs(ap.x - tp.x);
|
|
42696
|
+
const dy = Math.abs(ap.y - tp.y);
|
|
42697
|
+
if (dx <= 1 && dy <= 1 && dx + dy > 0) {
|
|
42698
|
+
const damage = Math.max(1, num(attackerRow.attack) - num(targetRow.defense));
|
|
42699
|
+
eventBus.emit(`UI:${attackEvent}`, {
|
|
42700
|
+
attackerId: str(attackerRow.id),
|
|
42701
|
+
targetId: str(targetRow.id),
|
|
42702
|
+
damage
|
|
42703
|
+
});
|
|
42704
|
+
setTimeout(checkGameEnd, 100);
|
|
42705
|
+
}
|
|
42706
|
+
}, [phase, selectedUnitId, entityUnits, attackEvent, eventBus, checkGameEnd]);
|
|
42707
|
+
const handleEndTurn = useCallback(() => {
|
|
42708
|
+
if (endTurnEvent) eventBus.emit(`UI:${endTurnEvent}`, {});
|
|
42709
|
+
}, [endTurnEvent, eventBus]);
|
|
42710
|
+
const handleCancel = useCallback(() => {
|
|
42711
|
+
if (cancelEvent) eventBus.emit(`UI:${cancelEvent}`, {});
|
|
42712
|
+
}, [cancelEvent, eventBus]);
|
|
42713
|
+
const handlePlayAgain = useCallback(() => {
|
|
42714
|
+
if (playAgainEvent) eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
42715
|
+
}, [playAgainEvent, eventBus]);
|
|
42716
|
+
return /* @__PURE__ */ jsxs(VStack, { className: cn("game-board-3d block w-full min-h-[85vh] relative", className), children: [
|
|
42717
|
+
/* @__PURE__ */ jsxs("div", { className: "game-board-3d__status absolute top-3 left-3 z-10 flex gap-2 items-center", children: [
|
|
42718
|
+
/* @__PURE__ */ jsx(Typography, { variant: "small", className: "status__phase capitalize", children: phase.replace("_", " ") }),
|
|
42719
|
+
currentTeam && !isGameOver && /* @__PURE__ */ jsxs(Typography, { variant: "small", color: "muted", className: "status__team", children: [
|
|
42720
|
+
"\u2014 ",
|
|
42721
|
+
currentTeam === "player" ? "Your Turn" : "Enemy's Turn"
|
|
42722
|
+
] }),
|
|
42723
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "small", color: "muted", className: "status__turn", children: [
|
|
42724
|
+
"Turn ",
|
|
42725
|
+
turn
|
|
42726
|
+
] })
|
|
42727
|
+
] }),
|
|
42728
|
+
/* @__PURE__ */ jsx(
|
|
42729
|
+
GameCanvas3D,
|
|
42730
|
+
{
|
|
42731
|
+
tiles,
|
|
42732
|
+
units,
|
|
42733
|
+
features,
|
|
42734
|
+
cameraMode,
|
|
42735
|
+
showGrid: true,
|
|
42736
|
+
showCoordinates: false,
|
|
42737
|
+
showTileInfo: false,
|
|
42738
|
+
shadows: true,
|
|
42739
|
+
backgroundColor,
|
|
42740
|
+
tileClickEvent,
|
|
42741
|
+
unitClickEvent,
|
|
42742
|
+
onUnitClick: handleUnitClickCallback,
|
|
42743
|
+
selectedUnitId,
|
|
42744
|
+
validMoves,
|
|
42745
|
+
attackTargets,
|
|
42746
|
+
className: "game-board-3d__canvas w-full min-h-[85vh]"
|
|
42747
|
+
}
|
|
42748
|
+
),
|
|
42749
|
+
!isGameOver && /* @__PURE__ */ jsxs(HStack, { className: "fixed bottom-6 right-6 z-50", gap: "sm", children: [
|
|
42750
|
+
(phase === "selection" || phase === "movement" || phase === "action") && /* @__PURE__ */ jsx(
|
|
42751
|
+
Button,
|
|
42752
|
+
{
|
|
42753
|
+
variant: "secondary",
|
|
42754
|
+
className: "shadow-xl",
|
|
42755
|
+
onClick: handleCancel,
|
|
42756
|
+
children: "Cancel"
|
|
42757
|
+
}
|
|
42758
|
+
),
|
|
42759
|
+
phase !== "enemy_turn" && /* @__PURE__ */ jsx(
|
|
42760
|
+
Button,
|
|
42761
|
+
{
|
|
42762
|
+
variant: "primary",
|
|
42763
|
+
className: "shadow-xl",
|
|
42764
|
+
onClick: handleEndTurn,
|
|
42765
|
+
children: "End Turn"
|
|
42766
|
+
}
|
|
42767
|
+
)
|
|
42768
|
+
] }),
|
|
42769
|
+
isGameOver && /* @__PURE__ */ jsx("div", { className: "game-board-3d__overlay absolute inset-0 z-20 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxs(VStack, { align: "center", gap: "md", className: "overlay__card p-8 rounded-xl bg-gray-900/90 shadow-2xl", children: [
|
|
42770
|
+
/* @__PURE__ */ jsx(
|
|
42771
|
+
Typography,
|
|
42772
|
+
{
|
|
42773
|
+
variant: "h2",
|
|
42774
|
+
className: cn(
|
|
42775
|
+
"overlay__result",
|
|
42776
|
+
result === "victory" ? "text-yellow-400" : "text-red-500"
|
|
42777
|
+
),
|
|
42778
|
+
children: result === "victory" ? "Victory!" : "Defeat"
|
|
42779
|
+
}
|
|
42780
|
+
),
|
|
42781
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "body", color: "muted", className: "overlay__turn-count", children: [
|
|
42782
|
+
"Completed in ",
|
|
42783
|
+
turn,
|
|
42784
|
+
" turn",
|
|
42785
|
+
turn !== 1 ? "s" : ""
|
|
42786
|
+
] }),
|
|
42787
|
+
/* @__PURE__ */ jsx(
|
|
42788
|
+
Button,
|
|
42789
|
+
{
|
|
42790
|
+
variant: "primary",
|
|
42791
|
+
className: "px-8 py-3 font-semibold",
|
|
42792
|
+
onClick: handlePlayAgain,
|
|
42793
|
+
children: "Play Again"
|
|
42794
|
+
}
|
|
42795
|
+
)
|
|
42796
|
+
] }) })
|
|
42797
|
+
] });
|
|
42798
|
+
}
|
|
42799
|
+
var init_GameBoard3D = __esm({
|
|
42800
|
+
"components/game/organisms/GameBoard3D.tsx"() {
|
|
42801
|
+
"use client";
|
|
42802
|
+
init_cn();
|
|
42803
|
+
init_Button();
|
|
42804
|
+
init_Typography();
|
|
42805
|
+
init_Stack();
|
|
42806
|
+
init_useEventBus();
|
|
42807
|
+
init_GameCanvas3D2();
|
|
42808
|
+
init_boardEntity();
|
|
42809
|
+
GameBoard3D.displayName = "GameBoard3D";
|
|
42810
|
+
}
|
|
42811
|
+
});
|
|
41109
42812
|
var GameShell;
|
|
41110
42813
|
var init_GameShell = __esm({
|
|
41111
42814
|
"components/game/templates/GameShell.tsx"() {
|
|
@@ -42474,21 +44177,6 @@ var init_ModalSlot = __esm({
|
|
|
42474
44177
|
ModalSlot.displayName = "ModalSlot";
|
|
42475
44178
|
}
|
|
42476
44179
|
});
|
|
42477
|
-
function getOpponentAction(strategy, actions, history) {
|
|
42478
|
-
const actionIds = actions.map((a) => a.id);
|
|
42479
|
-
switch (strategy) {
|
|
42480
|
-
case "always-cooperate":
|
|
42481
|
-
return actionIds[0];
|
|
42482
|
-
case "always-defect":
|
|
42483
|
-
return actionIds[actionIds.length - 1];
|
|
42484
|
-
case "tit-for-tat":
|
|
42485
|
-
if (history.length === 0) return actionIds[0];
|
|
42486
|
-
return history[history.length - 1].playerAction;
|
|
42487
|
-
case "random":
|
|
42488
|
-
default:
|
|
42489
|
-
return actionIds[Math.floor(Math.random() * actionIds.length)];
|
|
42490
|
-
}
|
|
42491
|
-
}
|
|
42492
44180
|
function NegotiatorBoard({
|
|
42493
44181
|
entity,
|
|
42494
44182
|
completeEvent = "PUZZLE_COMPLETE",
|
|
@@ -42510,28 +44198,35 @@ function NegotiatorBoard({
|
|
|
42510
44198
|
const playerTotal = num(resolved?.score);
|
|
42511
44199
|
const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
|
|
42512
44200
|
const won = result === "win";
|
|
42513
|
-
const
|
|
44201
|
+
const lastPlayerAction = str(resolved?.lastPlayerAction);
|
|
44202
|
+
const lastOpponentAction = str(resolved?.lastOpponentAction);
|
|
44203
|
+
const lastPayoff = num(resolved?.lastPayoff);
|
|
42514
44204
|
const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
|
|
42515
44205
|
const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
|
|
44206
|
+
const prevRoundRef = useRef(currentRound);
|
|
44207
|
+
useEffect(() => {
|
|
44208
|
+
if (currentRound > prevRoundRef.current && lastPlayerAction) {
|
|
44209
|
+
const opponentPayoffEntry = payoffMatrix.find(
|
|
44210
|
+
(p2) => p2.playerAction === lastPlayerAction && p2.opponentAction === lastOpponentAction
|
|
44211
|
+
);
|
|
44212
|
+
setHistory((prev) => [
|
|
44213
|
+
...prev,
|
|
44214
|
+
{
|
|
44215
|
+
round: currentRound,
|
|
44216
|
+
playerAction: lastPlayerAction,
|
|
44217
|
+
opponentAction: lastOpponentAction,
|
|
44218
|
+
playerPayoff: lastPayoff,
|
|
44219
|
+
opponentPayoff: opponentPayoffEntry?.opponentPayoff ?? 0
|
|
44220
|
+
}
|
|
44221
|
+
]);
|
|
44222
|
+
}
|
|
44223
|
+
prevRoundRef.current = currentRound;
|
|
44224
|
+
}, [currentRound, lastPlayerAction, lastOpponentAction, lastPayoff, payoffMatrix]);
|
|
44225
|
+
const opponentTotal = useMemo(() => history.reduce((s, r) => s + r.opponentPayoff, 0), [history]);
|
|
42516
44226
|
const handleAction = useCallback((actionId) => {
|
|
42517
44227
|
if (isComplete) return;
|
|
42518
|
-
const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
|
|
42519
|
-
const payoff = payoffMatrix.find(
|
|
42520
|
-
(p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
|
|
42521
|
-
);
|
|
42522
|
-
const playerPayoff = payoff?.playerPayoff ?? 0;
|
|
42523
|
-
setHistory((prev) => [
|
|
42524
|
-
...prev,
|
|
42525
|
-
{
|
|
42526
|
-
round: prev.length + 1,
|
|
42527
|
-
playerAction: actionId,
|
|
42528
|
-
opponentAction,
|
|
42529
|
-
playerPayoff,
|
|
42530
|
-
opponentPayoff: payoff?.opponentPayoff ?? 0
|
|
42531
|
-
}
|
|
42532
|
-
]);
|
|
42533
44228
|
if (playRoundEvent) {
|
|
42534
|
-
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff:
|
|
44229
|
+
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: 0 });
|
|
42535
44230
|
}
|
|
42536
44231
|
if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
|
|
42537
44232
|
if (finishEvent) {
|
|
@@ -42541,10 +44236,11 @@ function NegotiatorBoard({
|
|
|
42541
44236
|
setShowHint(true);
|
|
42542
44237
|
}
|
|
42543
44238
|
}
|
|
42544
|
-
}, [isComplete, resolved, totalRounds, currentRound,
|
|
44239
|
+
}, [isComplete, resolved, totalRounds, currentRound, playRoundEvent, finishEvent, emit]);
|
|
42545
44240
|
const handleReset = useCallback(() => {
|
|
42546
44241
|
setHistory([]);
|
|
42547
44242
|
setShowHint(false);
|
|
44243
|
+
prevRoundRef.current = 0;
|
|
42548
44244
|
if (playAgainEvent) {
|
|
42549
44245
|
emit(`UI:${playAgainEvent}`, {});
|
|
42550
44246
|
}
|
|
@@ -42655,6 +44351,236 @@ var init_NegotiatorBoard = __esm({
|
|
|
42655
44351
|
NegotiatorBoard.displayName = "NegotiatorBoard";
|
|
42656
44352
|
}
|
|
42657
44353
|
});
|
|
44354
|
+
function resolveEntity(entity) {
|
|
44355
|
+
if (!entity) return {};
|
|
44356
|
+
if (Array.isArray(entity)) return entity[0] ?? {};
|
|
44357
|
+
return entity;
|
|
44358
|
+
}
|
|
44359
|
+
function numField(row, key, fallback) {
|
|
44360
|
+
const v = row[key];
|
|
44361
|
+
return typeof v === "number" ? v : fallback;
|
|
44362
|
+
}
|
|
44363
|
+
function strField(row, key, fallback) {
|
|
44364
|
+
const v = row[key];
|
|
44365
|
+
return typeof v === "string" ? v : fallback;
|
|
44366
|
+
}
|
|
44367
|
+
function PlatformerBoard({
|
|
44368
|
+
entity,
|
|
44369
|
+
player: propPlayer,
|
|
44370
|
+
platforms: propPlatforms,
|
|
44371
|
+
result: propResult,
|
|
44372
|
+
lives: propLives,
|
|
44373
|
+
score: propScore,
|
|
44374
|
+
level: propLevel,
|
|
44375
|
+
worldWidth = 800,
|
|
44376
|
+
worldHeight = 400,
|
|
44377
|
+
canvasWidth = 800,
|
|
44378
|
+
canvasHeight = 400,
|
|
44379
|
+
playerSprite,
|
|
44380
|
+
tileSprites,
|
|
44381
|
+
bgColor,
|
|
44382
|
+
leftEvent = "LEFT",
|
|
44383
|
+
rightEvent = "RIGHT",
|
|
44384
|
+
jumpEvent = "JUMP",
|
|
44385
|
+
stopEvent = "STOP",
|
|
44386
|
+
playAgainEvent = "PLAY_AGAIN",
|
|
44387
|
+
gameEndEvent = "GAME_END",
|
|
44388
|
+
className
|
|
44389
|
+
}) {
|
|
44390
|
+
const row = resolveEntity(entity);
|
|
44391
|
+
const eventBus = useEventBus();
|
|
44392
|
+
const { t } = useTranslate();
|
|
44393
|
+
const result = propResult ?? strField(row, "result", "none");
|
|
44394
|
+
propLives ?? numField(row, "lives", 3);
|
|
44395
|
+
const score = propScore ?? numField(row, "score", 0);
|
|
44396
|
+
const level = propLevel ?? numField(row, "level", 1);
|
|
44397
|
+
const entityPlayer = row["player"];
|
|
44398
|
+
const player = propPlayer ?? entityPlayer ?? {
|
|
44399
|
+
x: 80,
|
|
44400
|
+
y: 320,
|
|
44401
|
+
width: 32,
|
|
44402
|
+
height: 48,
|
|
44403
|
+
vx: 0,
|
|
44404
|
+
vy: 0,
|
|
44405
|
+
grounded: false,
|
|
44406
|
+
facingRight: true
|
|
44407
|
+
};
|
|
44408
|
+
const entityPlatforms = Array.isArray(row["platforms"]) ? row["platforms"] : void 0;
|
|
44409
|
+
const platforms = propPlatforms ?? entityPlatforms ?? [
|
|
44410
|
+
{ x: 0, y: 368, width: 800, height: 32, type: "ground" },
|
|
44411
|
+
{ x: 150, y: 280, width: 160, height: 16, type: "platform" },
|
|
44412
|
+
{ x: 420, y: 220, width: 160, height: 16, type: "platform" },
|
|
44413
|
+
{ x: 580, y: 300, width: 80, height: 16, type: "hazard" },
|
|
44414
|
+
{ x: 700, y: 340, width: 64, height: 28, type: "goal" }
|
|
44415
|
+
];
|
|
44416
|
+
const handleRestart = useCallback(() => {
|
|
44417
|
+
if (playAgainEvent) {
|
|
44418
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
44419
|
+
}
|
|
44420
|
+
}, [playAgainEvent, eventBus]);
|
|
44421
|
+
const isGameOver = result === "won" || result === "lost";
|
|
44422
|
+
const gameEndEmittedRef = useRef(false);
|
|
44423
|
+
useEffect(() => {
|
|
44424
|
+
if ((result === "won" || result === "lost") && gameEndEvent) {
|
|
44425
|
+
if (!gameEndEmittedRef.current) {
|
|
44426
|
+
gameEndEmittedRef.current = true;
|
|
44427
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
44428
|
+
}
|
|
44429
|
+
} else {
|
|
44430
|
+
gameEndEmittedRef.current = false;
|
|
44431
|
+
}
|
|
44432
|
+
}, [result, gameEndEvent, eventBus]);
|
|
44433
|
+
return /* @__PURE__ */ jsx(
|
|
44434
|
+
VStack,
|
|
44435
|
+
{
|
|
44436
|
+
className: cn("platformer-board relative bg-background", className),
|
|
44437
|
+
gap: "none",
|
|
44438
|
+
children: /* @__PURE__ */ jsxs(Box, { className: "relative", children: [
|
|
44439
|
+
/* @__PURE__ */ jsx(
|
|
44440
|
+
PlatformerCanvas,
|
|
44441
|
+
{
|
|
44442
|
+
player,
|
|
44443
|
+
platforms,
|
|
44444
|
+
worldWidth,
|
|
44445
|
+
worldHeight,
|
|
44446
|
+
canvasWidth,
|
|
44447
|
+
canvasHeight,
|
|
44448
|
+
followCamera: true,
|
|
44449
|
+
bgColor,
|
|
44450
|
+
playerSprite,
|
|
44451
|
+
tileSprites,
|
|
44452
|
+
leftEvent,
|
|
44453
|
+
rightEvent,
|
|
44454
|
+
jumpEvent,
|
|
44455
|
+
stopEvent
|
|
44456
|
+
}
|
|
44457
|
+
),
|
|
44458
|
+
isGameOver && /* @__PURE__ */ jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
44459
|
+
/* @__PURE__ */ jsx(
|
|
44460
|
+
Typography,
|
|
44461
|
+
{
|
|
44462
|
+
variant: "h2",
|
|
44463
|
+
className: cn(
|
|
44464
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
44465
|
+
result === "won" ? "text-warning" : "text-error"
|
|
44466
|
+
),
|
|
44467
|
+
children: result === "won" ? t("platformer.won") : t("platformer.lost")
|
|
44468
|
+
}
|
|
44469
|
+
),
|
|
44470
|
+
result === "won" && /* @__PURE__ */ jsxs(Typography, { variant: "body1", className: "text-muted-foreground", children: [
|
|
44471
|
+
t("platformer.level"),
|
|
44472
|
+
" ",
|
|
44473
|
+
level,
|
|
44474
|
+
" \u2014 ",
|
|
44475
|
+
t("platformer.score"),
|
|
44476
|
+
": ",
|
|
44477
|
+
score
|
|
44478
|
+
] }),
|
|
44479
|
+
result === "lost" && /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: t("platformer.noLives") }),
|
|
44480
|
+
/* @__PURE__ */ jsx(
|
|
44481
|
+
Button,
|
|
44482
|
+
{
|
|
44483
|
+
variant: "primary",
|
|
44484
|
+
className: "px-8 py-3 font-semibold",
|
|
44485
|
+
onClick: handleRestart,
|
|
44486
|
+
children: result === "won" ? t("platformer.nextLevel") : t("platformer.playAgain")
|
|
44487
|
+
}
|
|
44488
|
+
)
|
|
44489
|
+
] }) })
|
|
44490
|
+
] })
|
|
44491
|
+
}
|
|
44492
|
+
);
|
|
44493
|
+
}
|
|
44494
|
+
var init_PlatformerBoard = __esm({
|
|
44495
|
+
"components/game/organisms/PlatformerBoard.tsx"() {
|
|
44496
|
+
"use client";
|
|
44497
|
+
init_cn();
|
|
44498
|
+
init_useEventBus();
|
|
44499
|
+
init_Box();
|
|
44500
|
+
init_Button();
|
|
44501
|
+
init_Typography();
|
|
44502
|
+
init_Stack();
|
|
44503
|
+
init_PlatformerCanvas();
|
|
44504
|
+
PlatformerBoard.displayName = "PlatformerBoard";
|
|
44505
|
+
}
|
|
44506
|
+
});
|
|
44507
|
+
function PlatformerTemplate({
|
|
44508
|
+
entity,
|
|
44509
|
+
platforms = DEFAULT_PLATFORMS,
|
|
44510
|
+
worldWidth = 800,
|
|
44511
|
+
worldHeight = 400,
|
|
44512
|
+
canvasWidth = 800,
|
|
44513
|
+
canvasHeight = 400,
|
|
44514
|
+
playerSprite = `${CDN3}/characters/platformChar_idle.png`,
|
|
44515
|
+
tileSprites = DEFAULT_TILE_SPRITES,
|
|
44516
|
+
bgColor = "#5c94fc",
|
|
44517
|
+
gameEndEvent = "GAME_END",
|
|
44518
|
+
className
|
|
44519
|
+
}) {
|
|
44520
|
+
const row = entity && !Array.isArray(entity) ? entity : void 0;
|
|
44521
|
+
const lives = typeof row?.["lives"] === "number" ? row["lives"] : 3;
|
|
44522
|
+
const score = typeof row?.["score"] === "number" ? row["score"] : 0;
|
|
44523
|
+
const level = typeof row?.["level"] === "number" ? row["level"] : 1;
|
|
44524
|
+
return /* @__PURE__ */ jsxs(VStack, { className: cn("platformer-template w-full", className), gap: "none", children: [
|
|
44525
|
+
/* @__PURE__ */ jsx(Box, { className: "relative", children: /* @__PURE__ */ jsx(
|
|
44526
|
+
GameHud,
|
|
44527
|
+
{
|
|
44528
|
+
position: "top",
|
|
44529
|
+
stats: [
|
|
44530
|
+
{ label: "Lives", value: lives, format: "number", variant: "danger" },
|
|
44531
|
+
{ label: "Level", value: level, format: "number", variant: "default" }
|
|
44532
|
+
]
|
|
44533
|
+
}
|
|
44534
|
+
) }),
|
|
44535
|
+
/* @__PURE__ */ jsx(
|
|
44536
|
+
PlatformerBoard,
|
|
44537
|
+
{
|
|
44538
|
+
entity,
|
|
44539
|
+
platforms,
|
|
44540
|
+
worldWidth,
|
|
44541
|
+
worldHeight,
|
|
44542
|
+
canvasWidth,
|
|
44543
|
+
canvasHeight,
|
|
44544
|
+
playerSprite,
|
|
44545
|
+
tileSprites,
|
|
44546
|
+
bgColor,
|
|
44547
|
+
leftEvent: "LEFT",
|
|
44548
|
+
rightEvent: "RIGHT",
|
|
44549
|
+
jumpEvent: "JUMP",
|
|
44550
|
+
stopEvent: "STOP",
|
|
44551
|
+
playAgainEvent: "PLAY_AGAIN",
|
|
44552
|
+
gameEndEvent
|
|
44553
|
+
}
|
|
44554
|
+
),
|
|
44555
|
+
/* @__PURE__ */ jsx(Box, { className: "px-4 py-2", children: /* @__PURE__ */ jsx(ScoreBoard, { score, level }) })
|
|
44556
|
+
] });
|
|
44557
|
+
}
|
|
44558
|
+
var CDN3, DEFAULT_TILE_SPRITES, DEFAULT_PLATFORMS;
|
|
44559
|
+
var init_PlatformerTemplate = __esm({
|
|
44560
|
+
"components/game/templates/PlatformerTemplate.tsx"() {
|
|
44561
|
+
init_cn();
|
|
44562
|
+
init_Box();
|
|
44563
|
+
init_Stack();
|
|
44564
|
+
init_PlatformerBoard();
|
|
44565
|
+
init_GameHud();
|
|
44566
|
+
init_ScoreBoard();
|
|
44567
|
+
CDN3 = "https://almadar-kflow-assets.web.app/shared/platformer";
|
|
44568
|
+
DEFAULT_TILE_SPRITES = {
|
|
44569
|
+
ground: `${CDN3}/tiles/platformPack_tile001.png`,
|
|
44570
|
+
platform: `${CDN3}/tiles/platformPack_tile004.png`,
|
|
44571
|
+
hazard: `${CDN3}/tiles/platformPack_tile017.png`,
|
|
44572
|
+
goal: `${CDN3}/tiles/platformPack_tile007.png`
|
|
44573
|
+
};
|
|
44574
|
+
DEFAULT_PLATFORMS = [
|
|
44575
|
+
{ x: 0, y: 368, width: 800, height: 32, type: "ground" },
|
|
44576
|
+
{ x: 150, y: 280, width: 160, height: 16, type: "platform" },
|
|
44577
|
+
{ x: 420, y: 220, width: 160, height: 16, type: "platform" },
|
|
44578
|
+
{ x: 580, y: 300, width: 80, height: 16, type: "hazard" },
|
|
44579
|
+
{ x: 700, y: 340, width: 64, height: 28, type: "goal" }
|
|
44580
|
+
];
|
|
44581
|
+
PlatformerTemplate.displayName = "PlatformerTemplate";
|
|
44582
|
+
}
|
|
44583
|
+
});
|
|
42658
44584
|
var PricingOrganism;
|
|
42659
44585
|
var init_PricingOrganism = __esm({
|
|
42660
44586
|
"components/marketing/organisms/PricingOrganism.tsx"() {
|
|
@@ -42777,6 +44703,356 @@ var init_PricingPageTemplate = __esm({
|
|
|
42777
44703
|
PricingPageTemplate.displayName = "PricingPageTemplate";
|
|
42778
44704
|
}
|
|
42779
44705
|
});
|
|
44706
|
+
function RoguelikeBoard({
|
|
44707
|
+
entity,
|
|
44708
|
+
tiles: propTiles,
|
|
44709
|
+
enemies: propEnemies,
|
|
44710
|
+
items: propItems,
|
|
44711
|
+
player: propPlayer,
|
|
44712
|
+
playerHp: propPlayerHp,
|
|
44713
|
+
playerMaxHp: propPlayerMaxHp,
|
|
44714
|
+
playerAttack: propPlayerAttack,
|
|
44715
|
+
depth: propDepth,
|
|
44716
|
+
result: propResult,
|
|
44717
|
+
phase: propPhase,
|
|
44718
|
+
stairsX: propStairsX,
|
|
44719
|
+
stairsY: propStairsY,
|
|
44720
|
+
assetManifest: propAssetManifest,
|
|
44721
|
+
scale = 0.45,
|
|
44722
|
+
unitScale = 1,
|
|
44723
|
+
moveEvent,
|
|
44724
|
+
playAgainEvent,
|
|
44725
|
+
gameEndEvent,
|
|
44726
|
+
className
|
|
44727
|
+
}) {
|
|
44728
|
+
const board = boardEntity(entity) ?? {};
|
|
44729
|
+
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : DEFAULT_TILES2);
|
|
44730
|
+
const enemies = propEnemies ?? (Array.isArray(board.enemies) ? board.enemies : DEFAULT_ENEMIES);
|
|
44731
|
+
const items = propItems ?? (Array.isArray(board.items) ? board.items : DEFAULT_ITEMS);
|
|
44732
|
+
const player = propPlayer ?? board.player ?? { x: 1, y: 1 };
|
|
44733
|
+
const playerHp = propPlayerHp ?? num(board.playerHp, 10);
|
|
44734
|
+
const playerMaxHp = propPlayerMaxHp ?? num(board.playerMaxHp, 10);
|
|
44735
|
+
const playerAttack = propPlayerAttack ?? num(board.playerAttack, 3);
|
|
44736
|
+
const depth = propDepth ?? num(board.depth, 1);
|
|
44737
|
+
const result = propResult ?? (str(board.result) || "none");
|
|
44738
|
+
const phase = propPhase ?? (str(board.phase) || "player_turn");
|
|
44739
|
+
propStairsX ?? num(board.stairsX, 3);
|
|
44740
|
+
propStairsY ?? num(board.stairsY, 3);
|
|
44741
|
+
const eventBus = useEventBus();
|
|
44742
|
+
const { t } = useTranslate();
|
|
44743
|
+
const [hoveredTile, setHoveredTile] = useState(null);
|
|
44744
|
+
const emitMove = useCallback((dx, dy) => {
|
|
44745
|
+
if (!moveEvent) return;
|
|
44746
|
+
eventBus.emit(`UI:${moveEvent}`, { dx, dy });
|
|
44747
|
+
}, [moveEvent, eventBus]);
|
|
44748
|
+
useEffect(() => {
|
|
44749
|
+
const handler = (e) => {
|
|
44750
|
+
if (result !== "none" || phase !== "player_turn") return;
|
|
44751
|
+
if (e.key === "ArrowUp" || e.key === "w") emitMove(0, -1);
|
|
44752
|
+
if (e.key === "ArrowDown" || e.key === "s") emitMove(0, 1);
|
|
44753
|
+
if (e.key === "ArrowLeft" || e.key === "a") emitMove(-1, 0);
|
|
44754
|
+
if (e.key === "ArrowRight" || e.key === "d") emitMove(1, 0);
|
|
44755
|
+
};
|
|
44756
|
+
window.addEventListener("keydown", handler);
|
|
44757
|
+
return () => window.removeEventListener("keydown", handler);
|
|
44758
|
+
}, [result, phase, emitMove]);
|
|
44759
|
+
useEffect(() => {
|
|
44760
|
+
if ((result === "won" || result === "lost") && gameEndEvent) {
|
|
44761
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
44762
|
+
}
|
|
44763
|
+
}, [result, gameEndEvent, eventBus]);
|
|
44764
|
+
const handleTileClick = useCallback((x, y) => {
|
|
44765
|
+
if (result !== "none" || phase !== "player_turn") return;
|
|
44766
|
+
const dx = x - player.x;
|
|
44767
|
+
const dy = y - player.y;
|
|
44768
|
+
if (Math.abs(dx) + Math.abs(dy) === 1) {
|
|
44769
|
+
emitMove(dx, dy);
|
|
44770
|
+
}
|
|
44771
|
+
}, [result, phase, player, emitMove]);
|
|
44772
|
+
const handleReset = useCallback(() => {
|
|
44773
|
+
if (!playAgainEvent) return;
|
|
44774
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
44775
|
+
}, [playAgainEvent, eventBus]);
|
|
44776
|
+
const isoUnits = useMemo(() => {
|
|
44777
|
+
const liveEnemies = enemies.filter((e) => e.hp > 0);
|
|
44778
|
+
return [
|
|
44779
|
+
{
|
|
44780
|
+
id: "player",
|
|
44781
|
+
position: player,
|
|
44782
|
+
name: t("roguelike.player") || "Hero",
|
|
44783
|
+
team: "player",
|
|
44784
|
+
health: playerHp,
|
|
44785
|
+
maxHealth: playerMaxHp,
|
|
44786
|
+
unitType: "player",
|
|
44787
|
+
sprite: `${CDN4}/isometric-dungeon/Characters/Male/Male_0_Idle0.png`
|
|
44788
|
+
},
|
|
44789
|
+
...liveEnemies.map((e) => ({
|
|
44790
|
+
id: e.id,
|
|
44791
|
+
position: { x: e.x, y: e.y },
|
|
44792
|
+
name: t("roguelike.enemy") || "Enemy",
|
|
44793
|
+
team: "enemy",
|
|
44794
|
+
health: e.hp,
|
|
44795
|
+
maxHealth: 6,
|
|
44796
|
+
unitType: "enemy",
|
|
44797
|
+
sprite: `${CDN4}/sprite-sheets/shadow-legion-sprite-sheet-sw.png`
|
|
44798
|
+
}))
|
|
44799
|
+
];
|
|
44800
|
+
}, [player, enemies, playerHp, playerMaxHp, t]);
|
|
44801
|
+
const isoFeatures = useMemo(
|
|
44802
|
+
() => items.map((it) => ({
|
|
44803
|
+
id: it.id,
|
|
44804
|
+
x: it.x,
|
|
44805
|
+
y: it.y,
|
|
44806
|
+
type: it.kind,
|
|
44807
|
+
sprite: FEATURE_SPRITE[it.kind] ?? FEATURE_SPRITE.health_potion
|
|
44808
|
+
})),
|
|
44809
|
+
[items]
|
|
44810
|
+
);
|
|
44811
|
+
const validMoves = useMemo(() => {
|
|
44812
|
+
if (result !== "none" || phase !== "player_turn") return [];
|
|
44813
|
+
const dirs = [{ dx: 0, dy: -1 }, { dx: 0, dy: 1 }, { dx: -1, dy: 0 }, { dx: 1, dy: 0 }];
|
|
44814
|
+
return dirs.map(({ dx, dy }) => ({ x: player.x + dx, y: player.y + dy })).filter(({ x, y }) => {
|
|
44815
|
+
const tile = tiles.find((t2) => t2.x === x && t2.y === y);
|
|
44816
|
+
return tile?.passable ?? false;
|
|
44817
|
+
});
|
|
44818
|
+
}, [result, phase, player, tiles]);
|
|
44819
|
+
const isGameOver = result === "won" || result === "lost";
|
|
44820
|
+
return /* @__PURE__ */ jsxs(VStack, { className: cn("roguelike-board relative min-h-[600px] bg-background", className), gap: "none", children: [
|
|
44821
|
+
/* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between px-4 py-2 border-b border-border", gap: "md", children: [
|
|
44822
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
44823
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.depth") || "Depth" }),
|
|
44824
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body1", className: "font-bold tabular-nums", children: depth })
|
|
44825
|
+
] }),
|
|
44826
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
44827
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.hp") || "HP" }),
|
|
44828
|
+
/* @__PURE__ */ jsxs(
|
|
44829
|
+
Typography,
|
|
44830
|
+
{
|
|
44831
|
+
variant: "body1",
|
|
44832
|
+
className: cn(
|
|
44833
|
+
"font-bold tabular-nums",
|
|
44834
|
+
playerHp <= Math.floor(playerMaxHp * 0.25) ? "text-error" : "text-success"
|
|
44835
|
+
),
|
|
44836
|
+
children: [
|
|
44837
|
+
playerHp,
|
|
44838
|
+
" / ",
|
|
44839
|
+
playerMaxHp
|
|
44840
|
+
]
|
|
44841
|
+
}
|
|
44842
|
+
)
|
|
44843
|
+
] }),
|
|
44844
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
44845
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.atk") || "ATK" }),
|
|
44846
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body1", className: "font-bold tabular-nums text-foreground", children: playerAttack })
|
|
44847
|
+
] }),
|
|
44848
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
44849
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.phase") || "Phase" }),
|
|
44850
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "capitalize text-foreground", children: phase.replace("_", " ") })
|
|
44851
|
+
] })
|
|
44852
|
+
] }),
|
|
44853
|
+
/* @__PURE__ */ jsx(Box, { className: "relative flex-1", children: /* @__PURE__ */ jsx(
|
|
44854
|
+
IsometricCanvas_default,
|
|
44855
|
+
{
|
|
44856
|
+
tiles,
|
|
44857
|
+
units: isoUnits,
|
|
44858
|
+
features: isoFeatures,
|
|
44859
|
+
selectedUnitId: null,
|
|
44860
|
+
validMoves,
|
|
44861
|
+
attackTargets: [],
|
|
44862
|
+
hoveredTile,
|
|
44863
|
+
onTileClick: handleTileClick,
|
|
44864
|
+
onUnitClick: () => void 0,
|
|
44865
|
+
onTileHover: (x, y) => setHoveredTile({ x, y }),
|
|
44866
|
+
onTileLeave: () => setHoveredTile(null),
|
|
44867
|
+
scale,
|
|
44868
|
+
assetBaseUrl: propAssetManifest?.baseUrl ?? CDN4,
|
|
44869
|
+
assetManifest: propAssetManifest,
|
|
44870
|
+
unitScale
|
|
44871
|
+
}
|
|
44872
|
+
) }),
|
|
44873
|
+
!isGameOver && /* @__PURE__ */ jsx(HStack, { className: "justify-center gap-2 p-3", gap: "none", children: [
|
|
44874
|
+
{ label: "\u2191", dx: 0, dy: -1 },
|
|
44875
|
+
{ label: "\u2193", dx: 0, dy: 1 },
|
|
44876
|
+
{ label: "\u2190", dx: -1, dy: 0 },
|
|
44877
|
+
{ label: "\u2192", dx: 1, dy: 0 }
|
|
44878
|
+
].map(({ label, dx, dy }) => /* @__PURE__ */ jsx(
|
|
44879
|
+
Button,
|
|
44880
|
+
{
|
|
44881
|
+
variant: "secondary",
|
|
44882
|
+
className: "w-9 h-9 p-0 text-base font-mono",
|
|
44883
|
+
onClick: () => emitMove(dx, dy),
|
|
44884
|
+
disabled: phase !== "player_turn",
|
|
44885
|
+
children: label
|
|
44886
|
+
},
|
|
44887
|
+
label
|
|
44888
|
+
)) }),
|
|
44889
|
+
isGameOver && /* @__PURE__ */ jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/75 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
44890
|
+
/* @__PURE__ */ jsx(
|
|
44891
|
+
Typography,
|
|
44892
|
+
{
|
|
44893
|
+
variant: "h2",
|
|
44894
|
+
className: cn(
|
|
44895
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
44896
|
+
result === "won" ? "text-warning" : "text-error"
|
|
44897
|
+
),
|
|
44898
|
+
children: result === "won" ? t("roguelike.victory") || "Victory!" : t("roguelike.defeat") || "Defeated!"
|
|
44899
|
+
}
|
|
44900
|
+
),
|
|
44901
|
+
result === "won" && /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: t("roguelike.cleared") || `Dungeon cleared! Depth reached: ${depth}` }),
|
|
44902
|
+
/* @__PURE__ */ jsx(
|
|
44903
|
+
Button,
|
|
44904
|
+
{
|
|
44905
|
+
variant: "primary",
|
|
44906
|
+
className: "px-8 py-3 font-semibold",
|
|
44907
|
+
onClick: handleReset,
|
|
44908
|
+
children: t("roguelike.playAgain") || "Play Again"
|
|
44909
|
+
}
|
|
44910
|
+
)
|
|
44911
|
+
] }) })
|
|
44912
|
+
] });
|
|
44913
|
+
}
|
|
44914
|
+
var CDN4, DEFAULT_TILES2, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
|
|
44915
|
+
var init_RoguelikeBoard = __esm({
|
|
44916
|
+
"components/game/organisms/RoguelikeBoard.tsx"() {
|
|
44917
|
+
"use client";
|
|
44918
|
+
init_cn();
|
|
44919
|
+
init_useEventBus();
|
|
44920
|
+
init_Box();
|
|
44921
|
+
init_Button();
|
|
44922
|
+
init_Typography();
|
|
44923
|
+
init_Stack();
|
|
44924
|
+
init_IsometricCanvas();
|
|
44925
|
+
init_boardEntity();
|
|
44926
|
+
CDN4 = "https://almadar-kflow-assets.web.app/shared";
|
|
44927
|
+
DEFAULT_TILES2 = [
|
|
44928
|
+
{ x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44929
|
+
{ x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44930
|
+
{ x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44931
|
+
{ x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44932
|
+
{ x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44933
|
+
{ x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44934
|
+
{ x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44935
|
+
{ x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44936
|
+
{ x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44937
|
+
{ x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44938
|
+
{ x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44939
|
+
{ x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
44940
|
+
{ x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
44941
|
+
{ x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44942
|
+
{ x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44943
|
+
{ x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44944
|
+
{ x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44945
|
+
{ x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
44946
|
+
{ x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` },
|
|
44947
|
+
{ x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44948
|
+
{ x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44949
|
+
{ x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44950
|
+
{ x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44951
|
+
{ x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44952
|
+
{ x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` }
|
|
44953
|
+
];
|
|
44954
|
+
DEFAULT_ENEMIES = [
|
|
44955
|
+
{ id: "e1", x: 3, y: 1, hp: 5, attack: 2 },
|
|
44956
|
+
{ id: "e2", x: 1, y: 3, hp: 4, attack: 1 }
|
|
44957
|
+
];
|
|
44958
|
+
DEFAULT_ITEMS = [
|
|
44959
|
+
{ id: "i1", x: 2, y: 2, kind: "health_potion" }
|
|
44960
|
+
];
|
|
44961
|
+
FEATURE_SPRITE = {
|
|
44962
|
+
health_potion: `${CDN4}/isometric-dungeon/Isometric/chestClosed_E.png`,
|
|
44963
|
+
sword: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`,
|
|
44964
|
+
shield: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`
|
|
44965
|
+
};
|
|
44966
|
+
RoguelikeBoard.displayName = "RoguelikeBoard";
|
|
44967
|
+
}
|
|
44968
|
+
});
|
|
44969
|
+
function RoguelikeTemplate({
|
|
44970
|
+
entity,
|
|
44971
|
+
scale = 0.45,
|
|
44972
|
+
unitScale = 1,
|
|
44973
|
+
tiles = DEFAULT_ROGUELIKE_TILES,
|
|
44974
|
+
enemies,
|
|
44975
|
+
items,
|
|
44976
|
+
stairsX,
|
|
44977
|
+
stairsY,
|
|
44978
|
+
assetManifest = DEFAULT_ROGUELIKE_MANIFEST,
|
|
44979
|
+
gameEndEvent,
|
|
44980
|
+
className
|
|
44981
|
+
}) {
|
|
44982
|
+
const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
|
|
44983
|
+
if (!resolved && !tiles && !assetManifest) return null;
|
|
44984
|
+
return /* @__PURE__ */ jsx(
|
|
44985
|
+
RoguelikeBoard,
|
|
44986
|
+
{
|
|
44987
|
+
entity: resolved,
|
|
44988
|
+
tiles,
|
|
44989
|
+
enemies,
|
|
44990
|
+
items,
|
|
44991
|
+
stairsX,
|
|
44992
|
+
stairsY,
|
|
44993
|
+
assetManifest,
|
|
44994
|
+
scale,
|
|
44995
|
+
unitScale,
|
|
44996
|
+
moveEvent: "MOVE",
|
|
44997
|
+
playAgainEvent: "PLAY_AGAIN",
|
|
44998
|
+
gameEndEvent: gameEndEvent ?? "GAME_END",
|
|
44999
|
+
className
|
|
45000
|
+
}
|
|
45001
|
+
);
|
|
45002
|
+
}
|
|
45003
|
+
var CDN5, DEFAULT_ROGUELIKE_TILES, DEFAULT_ROGUELIKE_MANIFEST;
|
|
45004
|
+
var init_RoguelikeTemplate = __esm({
|
|
45005
|
+
"components/game/templates/RoguelikeTemplate.tsx"() {
|
|
45006
|
+
init_RoguelikeBoard();
|
|
45007
|
+
CDN5 = "https://almadar-kflow-assets.web.app/shared";
|
|
45008
|
+
DEFAULT_ROGUELIKE_TILES = [
|
|
45009
|
+
{ x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45010
|
+
{ x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45011
|
+
{ x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45012
|
+
{ x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45013
|
+
{ x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45014
|
+
{ x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45015
|
+
{ x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45016
|
+
{ x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45017
|
+
{ x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45018
|
+
{ x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45019
|
+
{ x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45020
|
+
{ x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
45021
|
+
{ x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
45022
|
+
{ x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45023
|
+
{ x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45024
|
+
{ x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45025
|
+
{ x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45026
|
+
{ x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
45027
|
+
{ x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stairs_E.png` },
|
|
45028
|
+
{ x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45029
|
+
{ x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45030
|
+
{ x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45031
|
+
{ x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45032
|
+
{ x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45033
|
+
{ x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` }
|
|
45034
|
+
];
|
|
45035
|
+
DEFAULT_ROGUELIKE_MANIFEST = {
|
|
45036
|
+
baseUrl: CDN5,
|
|
45037
|
+
terrains: {
|
|
45038
|
+
floor: "/isometric-dungeon/Isometric/dirt_E.png",
|
|
45039
|
+
wall: "/isometric-dungeon/Isometric/stoneInset_E.png",
|
|
45040
|
+
stairs: "/isometric-dungeon/Isometric/stairs_E.png",
|
|
45041
|
+
planks: "/isometric-dungeon/Isometric/planks_E.png"
|
|
45042
|
+
},
|
|
45043
|
+
units: {
|
|
45044
|
+
player: "/isometric-dungeon/Characters/Male/Male_0_Idle0.png",
|
|
45045
|
+
enemy: "/sprite-sheets/shadow-legion-sprite-sheet-sw.png"
|
|
45046
|
+
},
|
|
45047
|
+
features: {
|
|
45048
|
+
health_potion: "/isometric-dungeon/Isometric/chestClosed_E.png",
|
|
45049
|
+
sword: "/isometric-dungeon/Isometric/barrel_E.png",
|
|
45050
|
+
shield: "/isometric-dungeon/Isometric/barrel_E.png"
|
|
45051
|
+
}
|
|
45052
|
+
};
|
|
45053
|
+
RoguelikeTemplate.displayName = "RoguelikeTemplate";
|
|
45054
|
+
}
|
|
45055
|
+
});
|
|
42780
45056
|
|
|
42781
45057
|
// lib/traitRegistry.ts
|
|
42782
45058
|
function getAllTraits() {
|
|
@@ -44011,7 +46287,7 @@ function getAllEvents(traits2) {
|
|
|
44011
46287
|
function EventDispatcherTab({ traits: traits2, schema }) {
|
|
44012
46288
|
const eventBus = useEventBus();
|
|
44013
46289
|
const { t } = useTranslate();
|
|
44014
|
-
const [
|
|
46290
|
+
const [log14, setLog] = React83.useState([]);
|
|
44015
46291
|
const prevStatesRef = React83.useRef(/* @__PURE__ */ new Map());
|
|
44016
46292
|
React83.useEffect(() => {
|
|
44017
46293
|
for (const trait of traits2) {
|
|
@@ -44075,9 +46351,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
|
|
|
44075
46351
|
/* @__PURE__ */ jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.otherEvents") }),
|
|
44076
46352
|
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: unavailableEvents.map((event) => /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", className: "opacity-50", children: event }, event)) })
|
|
44077
46353
|
] }),
|
|
44078
|
-
|
|
46354
|
+
log14.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
44079
46355
|
/* @__PURE__ */ jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.recentTransitions") }),
|
|
44080
|
-
/* @__PURE__ */ jsx(Stack, { gap: "xs", children:
|
|
46356
|
+
/* @__PURE__ */ jsx(Stack, { gap: "xs", children: log14.map((entry, i) => /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
|
|
44081
46357
|
/* @__PURE__ */ jsx("span", { className: "text-purple-400", children: entry.traitName }),
|
|
44082
46358
|
" ",
|
|
44083
46359
|
/* @__PURE__ */ jsx("span", { className: "text-gray-500", children: entry.from }),
|
|
@@ -44880,17 +47156,24 @@ function SequencerBoard({
|
|
|
44880
47156
|
const { emit } = useEventBus();
|
|
44881
47157
|
const { t } = useTranslate();
|
|
44882
47158
|
const resolved = boardEntity(entity);
|
|
44883
|
-
const maxSlots = num(resolved?.maxSlots);
|
|
47159
|
+
const maxSlots = num(resolved?.maxSlots) || 3;
|
|
44884
47160
|
const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
|
|
44885
47161
|
const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
|
|
44886
47162
|
const allowDuplicates = resolved?.allowDuplicates !== false;
|
|
47163
|
+
const entitySlots = Array.isArray(resolved?.slots) ? resolved.slots : [];
|
|
47164
|
+
const entityResult = str(resolved?.result);
|
|
47165
|
+
const entityAttempts = num(resolved?.attempts);
|
|
47166
|
+
const entityCurrentStep = typeof resolved?.currentStep === "number" ? resolved.currentStep : -1;
|
|
47167
|
+
const slots = Array.from({ length: maxSlots }, (_, i) => {
|
|
47168
|
+
const entitySlot = entitySlots.find((s) => s.index === i);
|
|
47169
|
+
if (!entitySlot?.placedActionId) return void 0;
|
|
47170
|
+
return availableActions.find((a) => a.id === entitySlot.placedActionId);
|
|
47171
|
+
});
|
|
47172
|
+
const isSuccess = entityResult === "win";
|
|
47173
|
+
const attempts = entityAttempts;
|
|
47174
|
+
const isPlayingBack = entityCurrentStep >= 0 && !isSuccess;
|
|
47175
|
+
const currentStep = entityCurrentStep;
|
|
44887
47176
|
const [headerError, setHeaderError] = useState(false);
|
|
44888
|
-
const [slots, setSlots] = useState(
|
|
44889
|
-
() => Array.from({ length: maxSlots }, () => void 0)
|
|
44890
|
-
);
|
|
44891
|
-
const [playState, setPlayState] = useState("idle");
|
|
44892
|
-
const [currentStep, setCurrentStep] = useState(-1);
|
|
44893
|
-
const [attempts, setAttempts] = useState(0);
|
|
44894
47177
|
const [slotFeedback, setSlotFeedback] = useState(
|
|
44895
47178
|
() => Array.from({ length: maxSlots }, () => null)
|
|
44896
47179
|
);
|
|
@@ -44899,11 +47182,6 @@ function SequencerBoard({
|
|
|
44899
47182
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
44900
47183
|
}, []);
|
|
44901
47184
|
const handleSlotDrop = useCallback((index, item) => {
|
|
44902
|
-
setSlots((prev) => {
|
|
44903
|
-
const next = [...prev];
|
|
44904
|
-
next[index] = item;
|
|
44905
|
-
return next;
|
|
44906
|
-
});
|
|
44907
47185
|
setSlotFeedback((prev) => {
|
|
44908
47186
|
const next = [...prev];
|
|
44909
47187
|
next[index] = null;
|
|
@@ -44913,11 +47191,6 @@ function SequencerBoard({
|
|
|
44913
47191
|
if (placeEvent) emit(`UI:${placeEvent}`, { slotIndex: index, actionId: item.id });
|
|
44914
47192
|
}, [emit, placeEvent]);
|
|
44915
47193
|
const handleSlotRemove = useCallback((index) => {
|
|
44916
|
-
setSlots((prev) => {
|
|
44917
|
-
const next = [...prev];
|
|
44918
|
-
next[index] = void 0;
|
|
44919
|
-
return next;
|
|
44920
|
-
});
|
|
44921
47194
|
setSlotFeedback((prev) => {
|
|
44922
47195
|
const next = [...prev];
|
|
44923
47196
|
next[index] = null;
|
|
@@ -44928,48 +47201,34 @@ function SequencerBoard({
|
|
|
44928
47201
|
}, [emit, removeEvent]);
|
|
44929
47202
|
const handleReset = useCallback(() => {
|
|
44930
47203
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
44931
|
-
setSlots(Array.from({ length: maxSlots }, () => void 0));
|
|
44932
|
-
setPlayState("idle");
|
|
44933
|
-
setCurrentStep(-1);
|
|
44934
|
-
setAttempts(0);
|
|
44935
47204
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
44936
47205
|
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
44937
47206
|
}, [maxSlots, playAgainEvent, emit]);
|
|
44938
47207
|
const filledSlots = slots.filter((s) => !!s);
|
|
44939
|
-
const canPlay = filledSlots.length > 0 &&
|
|
47208
|
+
const canPlay = filledSlots.length > 0 && !isPlayingBack && !isSuccess;
|
|
44940
47209
|
const handlePlay = useCallback(() => {
|
|
44941
47210
|
if (!canPlay) return;
|
|
44942
47211
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
44943
47212
|
emit("UI:PLAY_SOUND", { key: "confirm" });
|
|
44944
47213
|
const sequence = slots.map((s) => s?.id || "");
|
|
44945
|
-
|
|
44946
|
-
|
|
44947
|
-
}
|
|
44948
|
-
setPlayState("playing");
|
|
44949
|
-
setCurrentStep(0);
|
|
47214
|
+
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
47215
|
+
if (playEvent) emit(`UI:${playEvent}`, { sequence });
|
|
44950
47216
|
let step = 0;
|
|
44951
47217
|
const advance = () => {
|
|
44952
47218
|
step++;
|
|
47219
|
+
emit("UI:STEP", { step });
|
|
44953
47220
|
if (step >= maxSlots) {
|
|
47221
|
+
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
44954
47222
|
const playerSeq = slots.map((s) => s?.id);
|
|
44955
|
-
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
44956
47223
|
const success = solutions.some(
|
|
44957
47224
|
(sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
|
|
44958
47225
|
);
|
|
44959
|
-
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
44960
47226
|
if (success) {
|
|
44961
|
-
setPlayState("success");
|
|
44962
|
-
setCurrentStep(-1);
|
|
44963
47227
|
emit("UI:PLAY_SOUND", { key: "levelComplete" });
|
|
44964
|
-
if (completeEvent) {
|
|
44965
|
-
emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
44966
|
-
}
|
|
47228
|
+
if (completeEvent) emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
44967
47229
|
} else {
|
|
44968
|
-
setAttempts((prev) => prev + 1);
|
|
44969
47230
|
const feedback = computeSlotFeedback(playerSeq, solutions);
|
|
44970
47231
|
setSlotFeedback(feedback);
|
|
44971
|
-
setPlayState("idle");
|
|
44972
|
-
setCurrentStep(-1);
|
|
44973
47232
|
emit("UI:PLAY_SOUND", { key: "fail" });
|
|
44974
47233
|
const correctCount2 = feedback.filter((f3) => f3 === "correct").length;
|
|
44975
47234
|
for (let ci = 0; ci < correctCount2; ci++) {
|
|
@@ -44979,7 +47238,6 @@ function SequencerBoard({
|
|
|
44979
47238
|
}
|
|
44980
47239
|
}
|
|
44981
47240
|
} else {
|
|
44982
|
-
setCurrentStep(step);
|
|
44983
47241
|
timerRef.current = setTimeout(advance, stepDurationMs);
|
|
44984
47242
|
}
|
|
44985
47243
|
};
|
|
@@ -45029,7 +47287,7 @@ function SequencerBoard({
|
|
|
45029
47287
|
/* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
|
|
45030
47288
|
/* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between", children: [
|
|
45031
47289
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
|
|
45032
|
-
hasFeedback &&
|
|
47290
|
+
hasFeedback && !isPlayingBack && !isSuccess && /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
|
|
45033
47291
|
`${correctCount}/${maxSlots} `,
|
|
45034
47292
|
"\u2705"
|
|
45035
47293
|
] })
|
|
@@ -45041,7 +47299,7 @@ function SequencerBoard({
|
|
|
45041
47299
|
maxSlots,
|
|
45042
47300
|
onSlotDrop: handleSlotDrop,
|
|
45043
47301
|
onSlotRemove: handleSlotRemove,
|
|
45044
|
-
playing:
|
|
47302
|
+
playing: isPlayingBack,
|
|
45045
47303
|
currentStep,
|
|
45046
47304
|
categoryColors,
|
|
45047
47305
|
slotFeedback,
|
|
@@ -45049,7 +47307,7 @@ function SequencerBoard({
|
|
|
45049
47307
|
}
|
|
45050
47308
|
)
|
|
45051
47309
|
] }),
|
|
45052
|
-
|
|
47310
|
+
!isPlayingBack && /* @__PURE__ */ jsx(
|
|
45053
47311
|
ActionPalette,
|
|
45054
47312
|
{
|
|
45055
47313
|
actions: availableActions,
|
|
@@ -45059,8 +47317,8 @@ function SequencerBoard({
|
|
|
45059
47317
|
label: t("sequencer.dragActions")
|
|
45060
47318
|
}
|
|
45061
47319
|
),
|
|
45062
|
-
hasFeedback &&
|
|
45063
|
-
|
|
47320
|
+
hasFeedback && !isPlayingBack && !isSuccess && attempts > 0 && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: t(encourageKey) }) }),
|
|
47321
|
+
isSuccess && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("sequencer.levelComplete") }) }),
|
|
45064
47322
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
|
|
45065
47323
|
/* @__PURE__ */ jsx(
|
|
45066
47324
|
Button,
|
|
@@ -46016,9 +48274,9 @@ function VariablePanel({
|
|
|
46016
48274
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
|
|
46017
48275
|
variables.map((v) => {
|
|
46018
48276
|
const name = v.name == null ? "" : String(v.name);
|
|
46019
|
-
const value =
|
|
46020
|
-
const max =
|
|
46021
|
-
const min =
|
|
48277
|
+
const value = numField2(v.value);
|
|
48278
|
+
const max = numField2(v.max, 100);
|
|
48279
|
+
const min = numField2(v.min, 0);
|
|
46022
48280
|
const unit = v.unit == null ? "" : String(v.unit);
|
|
46023
48281
|
const pct = Math.round((value - min) / (max - min) * 100);
|
|
46024
48282
|
const isHigh = pct > 80;
|
|
@@ -46048,12 +48306,12 @@ function VariablePanel({
|
|
|
46048
48306
|
})
|
|
46049
48307
|
] });
|
|
46050
48308
|
}
|
|
46051
|
-
var
|
|
48309
|
+
var numField2;
|
|
46052
48310
|
var init_VariablePanel = __esm({
|
|
46053
48311
|
"components/game/organisms/puzzles/state-architect/VariablePanel.tsx"() {
|
|
46054
48312
|
init_atoms2();
|
|
46055
48313
|
init_cn();
|
|
46056
|
-
|
|
48314
|
+
numField2 = (v, fallback = 0) => {
|
|
46057
48315
|
const n = Number(v);
|
|
46058
48316
|
return Number.isFinite(n) ? n : fallback;
|
|
46059
48317
|
};
|
|
@@ -46110,6 +48368,9 @@ function StateArchitectBoard({
|
|
|
46110
48368
|
stepDurationMs = 600,
|
|
46111
48369
|
testEvent,
|
|
46112
48370
|
completeEvent,
|
|
48371
|
+
addTransitionEvent,
|
|
48372
|
+
removeTransitionEvent,
|
|
48373
|
+
playAgainEvent,
|
|
46113
48374
|
className
|
|
46114
48375
|
}) {
|
|
46115
48376
|
const { emit } = useEventBus();
|
|
@@ -46122,14 +48383,16 @@ function StateArchitectBoard({
|
|
|
46122
48383
|
const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
|
|
46123
48384
|
const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
|
|
46124
48385
|
const entityVariables = rows(resolved?.variables);
|
|
46125
|
-
const
|
|
48386
|
+
const transitions = entityTransitions;
|
|
48387
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
48388
|
+
const entityResult = str(resolved?.result) || "none";
|
|
48389
|
+
const isSuccess = entityResult === "win";
|
|
48390
|
+
const [isTesting, setIsTesting] = useState(false);
|
|
46126
48391
|
const [headerError, setHeaderError] = useState(false);
|
|
46127
|
-
const [playState, setPlayState] = useState("editing");
|
|
46128
48392
|
const [currentState, setCurrentState] = useState(initialState);
|
|
46129
48393
|
const [selectedState, setSelectedState] = useState(null);
|
|
46130
48394
|
const [testResults, setTestResults] = useState([]);
|
|
46131
48395
|
const [variables, setVariables] = useState(() => [...entityVariables]);
|
|
46132
|
-
const [attempts, setAttempts] = useState(0);
|
|
46133
48396
|
const timerRef = useRef(null);
|
|
46134
48397
|
const [addingFrom, setAddingFrom] = useState(null);
|
|
46135
48398
|
useEffect(() => () => {
|
|
@@ -46139,7 +48402,7 @@ function StateArchitectBoard({
|
|
|
46139
48402
|
const GRAPH_H = 400;
|
|
46140
48403
|
const positions = useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
|
|
46141
48404
|
const handleStateClick = useCallback((state) => {
|
|
46142
|
-
if (
|
|
48405
|
+
if (isTesting) return;
|
|
46143
48406
|
if (addingFrom) {
|
|
46144
48407
|
if (addingFrom !== state) {
|
|
46145
48408
|
const event = availableEvents[0] || "EVENT";
|
|
@@ -46149,20 +48412,20 @@ function StateArchitectBoard({
|
|
|
46149
48412
|
to: state,
|
|
46150
48413
|
event
|
|
46151
48414
|
};
|
|
46152
|
-
|
|
48415
|
+
if (addTransitionEvent) emit(`UI:${addTransitionEvent}`, { id: newTrans.id, from: newTrans.from, to: newTrans.to, event: newTrans.event });
|
|
46153
48416
|
}
|
|
46154
48417
|
setAddingFrom(null);
|
|
46155
48418
|
} else {
|
|
46156
48419
|
setSelectedState(state);
|
|
46157
48420
|
}
|
|
46158
|
-
}, [
|
|
48421
|
+
}, [isTesting, addingFrom, availableEvents, addTransitionEvent, emit]);
|
|
46159
48422
|
const handleStartAddTransition = useCallback(() => {
|
|
46160
48423
|
if (!selectedState) return;
|
|
46161
48424
|
setAddingFrom(selectedState);
|
|
46162
48425
|
}, [selectedState]);
|
|
46163
48426
|
const handleRemoveTransition = useCallback((transId) => {
|
|
46164
|
-
|
|
46165
|
-
}, []);
|
|
48427
|
+
if (removeTransitionEvent) emit(`UI:${removeTransitionEvent}`, { id: transId });
|
|
48428
|
+
}, [removeTransitionEvent, emit]);
|
|
46166
48429
|
const machine = useMemo(() => ({
|
|
46167
48430
|
name: entityName,
|
|
46168
48431
|
description: str(resolved?.description),
|
|
@@ -46176,16 +48439,16 @@ function StateArchitectBoard({
|
|
|
46176
48439
|
}))
|
|
46177
48440
|
}), [entityName, resolved, entityStates, currentState, transitions]);
|
|
46178
48441
|
const handleTest = useCallback(() => {
|
|
46179
|
-
if (
|
|
48442
|
+
if (isTesting) return;
|
|
46180
48443
|
if (testEvent) emit(`UI:${testEvent}`, {});
|
|
46181
|
-
|
|
48444
|
+
setIsTesting(true);
|
|
46182
48445
|
setTestResults([]);
|
|
46183
48446
|
const results = [];
|
|
46184
48447
|
let testIdx = 0;
|
|
46185
48448
|
const runNextTest = () => {
|
|
46186
48449
|
if (testIdx >= testCases.length) {
|
|
46187
48450
|
const allPassed = results.every((r) => r.passed);
|
|
46188
|
-
|
|
48451
|
+
setIsTesting(false);
|
|
46189
48452
|
setTestResults(results);
|
|
46190
48453
|
if (allPassed && completeEvent) {
|
|
46191
48454
|
emit(`UI:${completeEvent}`, {
|
|
@@ -46193,9 +48456,6 @@ function StateArchitectBoard({
|
|
|
46193
48456
|
passedTests: results.filter((r) => r.passed).length
|
|
46194
48457
|
});
|
|
46195
48458
|
}
|
|
46196
|
-
if (!allPassed) {
|
|
46197
|
-
setAttempts((prev) => prev + 1);
|
|
46198
|
-
}
|
|
46199
48459
|
return;
|
|
46200
48460
|
}
|
|
46201
48461
|
const testCase = testCases[testIdx];
|
|
@@ -46218,24 +48478,23 @@ function StateArchitectBoard({
|
|
|
46218
48478
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
46219
48479
|
};
|
|
46220
48480
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
46221
|
-
}, [
|
|
48481
|
+
}, [isTesting, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
|
|
46222
48482
|
const handleTryAgain = useCallback(() => {
|
|
46223
48483
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
46224
|
-
|
|
48484
|
+
setIsTesting(false);
|
|
46225
48485
|
setCurrentState(initialState);
|
|
46226
48486
|
setTestResults([]);
|
|
46227
48487
|
}, [initialState]);
|
|
46228
48488
|
const handleReset = useCallback(() => {
|
|
46229
48489
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
46230
|
-
|
|
46231
|
-
setPlayState("editing");
|
|
48490
|
+
setIsTesting(false);
|
|
46232
48491
|
setCurrentState(initialState);
|
|
46233
48492
|
setTestResults([]);
|
|
46234
48493
|
setVariables([...entityVariables]);
|
|
46235
48494
|
setSelectedState(null);
|
|
46236
48495
|
setAddingFrom(null);
|
|
46237
|
-
|
|
46238
|
-
}, [
|
|
48496
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
48497
|
+
}, [initialState, entityVariables, playAgainEvent, emit]);
|
|
46239
48498
|
const codeData = useMemo(() => ({
|
|
46240
48499
|
name: entityName,
|
|
46241
48500
|
states: entityStates,
|
|
@@ -46331,7 +48590,7 @@ function StateArchitectBoard({
|
|
|
46331
48590
|
]
|
|
46332
48591
|
}
|
|
46333
48592
|
),
|
|
46334
|
-
|
|
48593
|
+
!isTesting && /* @__PURE__ */ jsx(HStack, { gap: "sm", children: /* @__PURE__ */ jsx(
|
|
46335
48594
|
Button,
|
|
46336
48595
|
{
|
|
46337
48596
|
variant: "ghost",
|
|
@@ -46353,7 +48612,7 @@ function StateArchitectBoard({
|
|
|
46353
48612
|
t2.guardHint,
|
|
46354
48613
|
")"
|
|
46355
48614
|
] }),
|
|
46356
|
-
|
|
48615
|
+
!isTesting && /* @__PURE__ */ jsx(
|
|
46357
48616
|
Button,
|
|
46358
48617
|
{
|
|
46359
48618
|
variant: "ghost",
|
|
@@ -46385,21 +48644,21 @@ function StateArchitectBoard({
|
|
|
46385
48644
|
resolved.showCodeView !== false && /* @__PURE__ */ jsx(StateJsonView, { data: codeData, label: "View Code" })
|
|
46386
48645
|
] })
|
|
46387
48646
|
] }),
|
|
46388
|
-
|
|
46389
|
-
|
|
48647
|
+
isSuccess && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("stateArchitect.allPassed") }) }),
|
|
48648
|
+
!isTesting && !isSuccess && testResults.some((r) => !r.passed) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
46390
48649
|
/* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(ENCOURAGEMENT_KEYS3[Math.min(attempts - 1, ENCOURAGEMENT_KEYS3.length - 1)] ?? ENCOURAGEMENT_KEYS3[0]) }) }),
|
|
46391
|
-
attempts >=
|
|
48650
|
+
!isSuccess && attempts >= 2 && Boolean(str(resolved?.hint)) && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
|
|
46392
48651
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
46393
48652
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
|
|
46394
48653
|
] }) })
|
|
46395
48654
|
] }),
|
|
46396
48655
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
|
|
46397
|
-
|
|
48656
|
+
!isTesting && !isSuccess && testResults.some((r) => !r.passed) ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsx(
|
|
46398
48657
|
Button,
|
|
46399
48658
|
{
|
|
46400
48659
|
variant: "primary",
|
|
46401
48660
|
onClick: handleTest,
|
|
46402
|
-
disabled:
|
|
48661
|
+
disabled: isTesting,
|
|
46403
48662
|
children: "\u25B6 " + t("game.runTests")
|
|
46404
48663
|
}
|
|
46405
48664
|
),
|
|
@@ -46905,6 +49164,289 @@ var init_ToastSlot = __esm({
|
|
|
46905
49164
|
ToastSlot.displayName = "ToastSlot";
|
|
46906
49165
|
}
|
|
46907
49166
|
});
|
|
49167
|
+
function tilesToIso(tiles) {
|
|
49168
|
+
return tiles.map((t) => ({
|
|
49169
|
+
x: t.x,
|
|
49170
|
+
y: t.y,
|
|
49171
|
+
terrain: t.terrain,
|
|
49172
|
+
terrainSprite: t.terrainSprite ?? TERRAIN_SPRITES[t.terrain ?? "ground"],
|
|
49173
|
+
passable: t.passable
|
|
49174
|
+
}));
|
|
49175
|
+
}
|
|
49176
|
+
function towersToUnits(towers) {
|
|
49177
|
+
return towers.map((t) => ({
|
|
49178
|
+
id: t.id,
|
|
49179
|
+
position: { x: t.x, y: t.y },
|
|
49180
|
+
name: "Tower",
|
|
49181
|
+
team: "player",
|
|
49182
|
+
sprite: TOWER_SPRITE,
|
|
49183
|
+
unitType: "guardian",
|
|
49184
|
+
health: 1,
|
|
49185
|
+
maxHealth: 1
|
|
49186
|
+
}));
|
|
49187
|
+
}
|
|
49188
|
+
function creepsToUnits(creeps) {
|
|
49189
|
+
return creeps.map((c) => ({
|
|
49190
|
+
id: c.id,
|
|
49191
|
+
position: { x: c.x, y: c.y },
|
|
49192
|
+
name: "Creep",
|
|
49193
|
+
team: "enemy",
|
|
49194
|
+
sprite: CREEP_SPRITE,
|
|
49195
|
+
unitType: "scrapper",
|
|
49196
|
+
health: c.hp,
|
|
49197
|
+
maxHealth: c.maxHp
|
|
49198
|
+
}));
|
|
49199
|
+
}
|
|
49200
|
+
function pathToFeatures(path) {
|
|
49201
|
+
return path.map((p2, i) => ({
|
|
49202
|
+
id: `path-${i}`,
|
|
49203
|
+
x: p2.x,
|
|
49204
|
+
y: p2.y,
|
|
49205
|
+
type: "path-marker",
|
|
49206
|
+
sprite: `${CDN6}world-map/road_straight.png`
|
|
49207
|
+
}));
|
|
49208
|
+
}
|
|
49209
|
+
function TowerDefenseBoard({
|
|
49210
|
+
entity,
|
|
49211
|
+
tiles: propTiles,
|
|
49212
|
+
path: propPath,
|
|
49213
|
+
towers: propTowers,
|
|
49214
|
+
creeps: propCreeps,
|
|
49215
|
+
gold: propGold,
|
|
49216
|
+
lives: propLives,
|
|
49217
|
+
wave: propWave,
|
|
49218
|
+
maxWaves: propMaxWaves,
|
|
49219
|
+
result: propResult,
|
|
49220
|
+
waveActive: propWaveActive,
|
|
49221
|
+
towerCost = 25,
|
|
49222
|
+
scale = 0.45,
|
|
49223
|
+
placeTowerEvent,
|
|
49224
|
+
startWaveEvent,
|
|
49225
|
+
playAgainEvent,
|
|
49226
|
+
gameEndEvent,
|
|
49227
|
+
className
|
|
49228
|
+
}) {
|
|
49229
|
+
const board = boardEntity(entity) ?? {};
|
|
49230
|
+
const eventBus = useEventBus();
|
|
49231
|
+
const { t } = useTranslate();
|
|
49232
|
+
const tiles = propTiles ?? rows(board.tiles);
|
|
49233
|
+
const path = propPath ?? rows(board.path);
|
|
49234
|
+
const towers = propTowers ?? rows(board.towers);
|
|
49235
|
+
const creeps = propCreeps ?? rows(board.creeps);
|
|
49236
|
+
const gold = propGold ?? num(board.gold, 100);
|
|
49237
|
+
const lives = propLives ?? num(board.lives, 20);
|
|
49238
|
+
const wave = propWave ?? num(board.wave, 1);
|
|
49239
|
+
const maxWaves = propMaxWaves ?? num(board.maxWaves, 5);
|
|
49240
|
+
const result = propResult ?? (str(board.result) || "none");
|
|
49241
|
+
const waveActive = propWaveActive ?? Boolean(board.waveActive);
|
|
49242
|
+
const [hoveredTile, setHoveredTile] = useState(null);
|
|
49243
|
+
const emittedGameEnd = useRef(false);
|
|
49244
|
+
if (result !== "none" && !emittedGameEnd.current) {
|
|
49245
|
+
emittedGameEnd.current = true;
|
|
49246
|
+
if (gameEndEvent) {
|
|
49247
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
49248
|
+
}
|
|
49249
|
+
}
|
|
49250
|
+
if (result === "none") {
|
|
49251
|
+
emittedGameEnd.current = false;
|
|
49252
|
+
}
|
|
49253
|
+
const towerPositions = useMemo(() => new Set(towers.map((t2) => `${t2.x},${t2.y}`)), [towers]);
|
|
49254
|
+
const pathPositions = useMemo(() => new Set(path.map((p2) => `${p2.x},${p2.y}`)), [path]);
|
|
49255
|
+
const validMoves = useMemo(() => {
|
|
49256
|
+
if (result !== "none" || gold < towerCost) return [];
|
|
49257
|
+
return tiles.filter(
|
|
49258
|
+
(t2) => t2.passable !== false && !towerPositions.has(`${t2.x},${t2.y}`) && !pathPositions.has(`${t2.x},${t2.y}`)
|
|
49259
|
+
).map((t2) => ({ x: t2.x, y: t2.y }));
|
|
49260
|
+
}, [tiles, towerPositions, pathPositions, result, gold, towerCost]);
|
|
49261
|
+
const isoTiles = useMemo(() => tilesToIso(tiles), [tiles]);
|
|
49262
|
+
const towerUnits = useMemo(() => towersToUnits(towers), [towers]);
|
|
49263
|
+
const creepUnits = useMemo(() => creepsToUnits(creeps), [creeps]);
|
|
49264
|
+
const isoUnits = useMemo(() => [...towerUnits, ...creepUnits], [towerUnits, creepUnits]);
|
|
49265
|
+
const pathFeatures = useMemo(() => pathToFeatures(path), [path]);
|
|
49266
|
+
const handleTileClick = useCallback((x, y) => {
|
|
49267
|
+
if (result !== "none") return;
|
|
49268
|
+
if (pathPositions.has(`${x},${y}`)) return;
|
|
49269
|
+
if (towerPositions.has(`${x},${y}`)) return;
|
|
49270
|
+
if (gold < towerCost) return;
|
|
49271
|
+
if (placeTowerEvent) {
|
|
49272
|
+
eventBus.emit(`UI:${placeTowerEvent}`, { x, y });
|
|
49273
|
+
}
|
|
49274
|
+
}, [result, pathPositions, towerPositions, gold, towerCost, placeTowerEvent, eventBus]);
|
|
49275
|
+
const handleStartWave = useCallback(() => {
|
|
49276
|
+
if (startWaveEvent) {
|
|
49277
|
+
eventBus.emit(`UI:${startWaveEvent}`, { wave });
|
|
49278
|
+
}
|
|
49279
|
+
}, [startWaveEvent, wave, eventBus]);
|
|
49280
|
+
const handlePlayAgain = useCallback(() => {
|
|
49281
|
+
if (playAgainEvent) {
|
|
49282
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
49283
|
+
}
|
|
49284
|
+
}, [playAgainEvent, eventBus]);
|
|
49285
|
+
const canPlaceTower = gold >= towerCost && result === "none";
|
|
49286
|
+
const canStartWave = !waveActive && result === "none";
|
|
49287
|
+
return /* @__PURE__ */ jsxs(VStack, { className: cn("tower-defense-board relative min-h-[600px] bg-background", className), gap: "none", children: [
|
|
49288
|
+
/* @__PURE__ */ jsxs(
|
|
49289
|
+
HStack,
|
|
49290
|
+
{
|
|
49291
|
+
className: "px-4 py-2 border-b border-border bg-surface",
|
|
49292
|
+
gap: "lg",
|
|
49293
|
+
align: "center",
|
|
49294
|
+
children: [
|
|
49295
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
49296
|
+
/* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("td.gold") ?? "Gold" }),
|
|
49297
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", className: "text-warning", children: gold })
|
|
49298
|
+
] }),
|
|
49299
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
49300
|
+
/* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("td.lives") ?? "Lives" }),
|
|
49301
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", className: lives <= 5 ? "text-error" : "text-success", children: lives })
|
|
49302
|
+
] }),
|
|
49303
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
49304
|
+
/* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("td.wave") ?? "Wave" }),
|
|
49305
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "body2", weight: "bold", children: [
|
|
49306
|
+
wave,
|
|
49307
|
+
" / ",
|
|
49308
|
+
maxWaves
|
|
49309
|
+
] })
|
|
49310
|
+
] }),
|
|
49311
|
+
/* @__PURE__ */ jsxs(Box, { className: "ml-auto", children: [
|
|
49312
|
+
canStartWave && /* @__PURE__ */ jsx(Button, { variant: "primary", size: "sm", onClick: handleStartWave, children: t("td.startWave") ?? "Start Wave" }),
|
|
49313
|
+
waveActive && /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-warning animate-pulse", children: t("td.waveInProgress") ?? "Wave in progress\u2026" })
|
|
49314
|
+
] })
|
|
49315
|
+
]
|
|
49316
|
+
}
|
|
49317
|
+
),
|
|
49318
|
+
/* @__PURE__ */ jsxs(Box, { className: "relative flex-1", children: [
|
|
49319
|
+
/* @__PURE__ */ jsx(
|
|
49320
|
+
IsometricCanvas_default,
|
|
49321
|
+
{
|
|
49322
|
+
tiles: isoTiles,
|
|
49323
|
+
units: isoUnits,
|
|
49324
|
+
features: pathFeatures,
|
|
49325
|
+
validMoves,
|
|
49326
|
+
hoveredTile,
|
|
49327
|
+
onTileClick: handleTileClick,
|
|
49328
|
+
onTileHover: (x, y) => setHoveredTile({ x, y }),
|
|
49329
|
+
onTileLeave: () => setHoveredTile(null),
|
|
49330
|
+
scale
|
|
49331
|
+
}
|
|
49332
|
+
),
|
|
49333
|
+
hoveredTile && canPlaceTower && validMoves.some((m) => m.x === hoveredTile.x && m.y === hoveredTile.y) && /* @__PURE__ */ 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__ */ jsx(Typography, { variant: "small", children: t("td.placeTower") ?? `Place tower (${towerCost} gold)` }) })
|
|
49334
|
+
] }),
|
|
49335
|
+
result !== "none" && /* @__PURE__ */ jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm", children: /* @__PURE__ */ jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
49336
|
+
/* @__PURE__ */ jsx(
|
|
49337
|
+
Typography,
|
|
49338
|
+
{
|
|
49339
|
+
variant: "h2",
|
|
49340
|
+
className: cn(
|
|
49341
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
49342
|
+
result === "won" ? "text-warning" : "text-error"
|
|
49343
|
+
),
|
|
49344
|
+
children: result === "won" ? t("td.victory") ?? "Victory!" : t("td.defeat") ?? "Defeat!"
|
|
49345
|
+
}
|
|
49346
|
+
),
|
|
49347
|
+
/* @__PURE__ */ 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." }),
|
|
49348
|
+
/* @__PURE__ */ jsx(
|
|
49349
|
+
Button,
|
|
49350
|
+
{
|
|
49351
|
+
variant: "primary",
|
|
49352
|
+
className: "px-8 py-3 font-semibold",
|
|
49353
|
+
onClick: handlePlayAgain,
|
|
49354
|
+
children: t("td.playAgain") ?? "Play Again"
|
|
49355
|
+
}
|
|
49356
|
+
)
|
|
49357
|
+
] }) })
|
|
49358
|
+
] });
|
|
49359
|
+
}
|
|
49360
|
+
var CDN6, TERRAIN_SPRITES, TOWER_SPRITE, CREEP_SPRITE;
|
|
49361
|
+
var init_TowerDefenseBoard = __esm({
|
|
49362
|
+
"components/game/organisms/TowerDefenseBoard.tsx"() {
|
|
49363
|
+
"use client";
|
|
49364
|
+
init_cn();
|
|
49365
|
+
init_useEventBus();
|
|
49366
|
+
init_Box();
|
|
49367
|
+
init_Button();
|
|
49368
|
+
init_Typography();
|
|
49369
|
+
init_Stack();
|
|
49370
|
+
init_IsometricCanvas();
|
|
49371
|
+
init_boardEntity();
|
|
49372
|
+
CDN6 = "https://almadar-kflow-assets.web.app/shared/";
|
|
49373
|
+
TERRAIN_SPRITES = {
|
|
49374
|
+
ground: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_01.png`,
|
|
49375
|
+
path: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_09.png`,
|
|
49376
|
+
wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`
|
|
49377
|
+
};
|
|
49378
|
+
TOWER_SPRITE = `${CDN6}units/guardian.png`;
|
|
49379
|
+
CREEP_SPRITE = `${CDN6}units/scrapper.png`;
|
|
49380
|
+
TowerDefenseBoard.displayName = "TowerDefenseBoard";
|
|
49381
|
+
}
|
|
49382
|
+
});
|
|
49383
|
+
function TowerDefenseTemplate({
|
|
49384
|
+
entity,
|
|
49385
|
+
title = "Tower Defense",
|
|
49386
|
+
tiles,
|
|
49387
|
+
path,
|
|
49388
|
+
towers,
|
|
49389
|
+
gold,
|
|
49390
|
+
lives,
|
|
49391
|
+
maxWaves,
|
|
49392
|
+
towerCost,
|
|
49393
|
+
scale,
|
|
49394
|
+
placeTowerEvent,
|
|
49395
|
+
startWaveEvent,
|
|
49396
|
+
playAgainEvent,
|
|
49397
|
+
gameEndEvent,
|
|
49398
|
+
className
|
|
49399
|
+
}) {
|
|
49400
|
+
const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
|
|
49401
|
+
return /* @__PURE__ */ jsxs(
|
|
49402
|
+
Box,
|
|
49403
|
+
{
|
|
49404
|
+
display: "flex",
|
|
49405
|
+
fullHeight: true,
|
|
49406
|
+
className: cn("tower-defense-template flex-col", className),
|
|
49407
|
+
children: [
|
|
49408
|
+
/* @__PURE__ */ jsx(
|
|
49409
|
+
HStack,
|
|
49410
|
+
{
|
|
49411
|
+
gap: "sm",
|
|
49412
|
+
align: "center",
|
|
49413
|
+
className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
|
|
49414
|
+
children: /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title })
|
|
49415
|
+
}
|
|
49416
|
+
),
|
|
49417
|
+
/* @__PURE__ */ jsx(Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
49418
|
+
TowerDefenseBoard,
|
|
49419
|
+
{
|
|
49420
|
+
entity: resolved,
|
|
49421
|
+
tiles,
|
|
49422
|
+
path,
|
|
49423
|
+
towers,
|
|
49424
|
+
gold,
|
|
49425
|
+
lives,
|
|
49426
|
+
maxWaves,
|
|
49427
|
+
towerCost,
|
|
49428
|
+
scale,
|
|
49429
|
+
placeTowerEvent,
|
|
49430
|
+
startWaveEvent,
|
|
49431
|
+
playAgainEvent,
|
|
49432
|
+
gameEndEvent,
|
|
49433
|
+
className: "h-full"
|
|
49434
|
+
}
|
|
49435
|
+
) })
|
|
49436
|
+
]
|
|
49437
|
+
}
|
|
49438
|
+
);
|
|
49439
|
+
}
|
|
49440
|
+
var init_TowerDefenseTemplate = __esm({
|
|
49441
|
+
"components/game/templates/TowerDefenseTemplate.tsx"() {
|
|
49442
|
+
init_cn();
|
|
49443
|
+
init_Box();
|
|
49444
|
+
init_Stack();
|
|
49445
|
+
init_Typography();
|
|
49446
|
+
init_TowerDefenseBoard();
|
|
49447
|
+
TowerDefenseTemplate.displayName = "TowerDefenseTemplate";
|
|
49448
|
+
}
|
|
49449
|
+
});
|
|
46908
49450
|
function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
|
|
46909
49451
|
const eventBus = useEventBus();
|
|
46910
49452
|
const {
|
|
@@ -47103,17 +49645,12 @@ var init_UncontrolledBattleBoard = __esm({
|
|
|
47103
49645
|
}
|
|
47104
49646
|
});
|
|
47105
49647
|
function heroPosition(h) {
|
|
49648
|
+
if ("position" in h && h.position != null) return h.position;
|
|
47106
49649
|
return vec2(h.position);
|
|
47107
49650
|
}
|
|
47108
|
-
function heroOwner(h) {
|
|
47109
|
-
return str(h.owner);
|
|
47110
|
-
}
|
|
47111
49651
|
function heroMovement(h) {
|
|
47112
49652
|
return num(h.movement);
|
|
47113
49653
|
}
|
|
47114
|
-
function hexPassable(h) {
|
|
47115
|
-
return h.passable !== false;
|
|
47116
|
-
}
|
|
47117
49654
|
function defaultIsInRange(from, to, range) {
|
|
47118
49655
|
return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
|
|
47119
49656
|
}
|
|
@@ -47149,38 +49686,41 @@ function WorldMapBoard({
|
|
|
47149
49686
|
}) {
|
|
47150
49687
|
const eventBus = useEventBus();
|
|
47151
49688
|
const resolved = boardEntity(entity);
|
|
47152
|
-
const
|
|
47153
|
-
const
|
|
49689
|
+
const entityUnits = rows(resolved?.units);
|
|
49690
|
+
const entityTiles = rows(resolved?.tiles);
|
|
47154
49691
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
47155
49692
|
const selectedHeroId = resolved?.selectedHeroId ?? null;
|
|
47156
49693
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
47157
49694
|
const backgroundImage = resolved?.backgroundImage;
|
|
47158
49695
|
const [hoveredTile, setHoveredTile] = useState(null);
|
|
47159
|
-
const selectedHero = useMemo(
|
|
47160
|
-
() => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
|
|
47161
|
-
[heroes, selectedHeroId]
|
|
47162
|
-
);
|
|
47163
49696
|
const derivedTiles = useMemo(
|
|
47164
|
-
() =>
|
|
47165
|
-
x: num(
|
|
47166
|
-
y: num(
|
|
47167
|
-
terrain: str(
|
|
47168
|
-
terrainSprite:
|
|
49697
|
+
() => entityTiles.map((t) => ({
|
|
49698
|
+
x: num(t.x),
|
|
49699
|
+
y: num(t.y),
|
|
49700
|
+
terrain: str(t.terrain),
|
|
49701
|
+
terrainSprite: t.terrainSprite == null ? void 0 : str(t.terrainSprite),
|
|
49702
|
+
passable: t.passable !== false
|
|
47169
49703
|
})),
|
|
47170
|
-
[
|
|
49704
|
+
[entityTiles]
|
|
47171
49705
|
);
|
|
47172
49706
|
const tiles = propTiles ?? derivedTiles;
|
|
47173
49707
|
const baseUnits = useMemo(
|
|
47174
|
-
() => propUnits ??
|
|
47175
|
-
id: str(
|
|
47176
|
-
position: heroPosition(
|
|
47177
|
-
name: str(
|
|
47178
|
-
|
|
47179
|
-
|
|
47180
|
-
|
|
47181
|
-
|
|
49708
|
+
() => propUnits ?? entityUnits.map((u) => ({
|
|
49709
|
+
id: str(u.id),
|
|
49710
|
+
position: heroPosition(u),
|
|
49711
|
+
name: str(u.name),
|
|
49712
|
+
// lolo uses `team` field (not `owner`)
|
|
49713
|
+
team: str(u.team) === "enemy" ? "enemy" : "player",
|
|
49714
|
+
health: num(u.health) || 100,
|
|
49715
|
+
maxHealth: num(u.maxHealth) || 100,
|
|
49716
|
+
sprite: u.sprite == null ? void 0 : str(u.sprite)
|
|
47182
49717
|
})),
|
|
47183
|
-
[
|
|
49718
|
+
[entityUnits, propUnits]
|
|
49719
|
+
);
|
|
49720
|
+
const gameUnits = baseUnits;
|
|
49721
|
+
const selectedHero = useMemo(
|
|
49722
|
+
() => gameUnits.find((u) => u.id === selectedHeroId) ?? null,
|
|
49723
|
+
[gameUnits, selectedHeroId]
|
|
47184
49724
|
);
|
|
47185
49725
|
const MOVE_SPEED_MS_PER_TILE = 300;
|
|
47186
49726
|
const movementAnimRef = useRef(null);
|
|
@@ -47226,51 +49766,50 @@ function WorldMapBoard({
|
|
|
47226
49766
|
const validMoves = useMemo(() => {
|
|
47227
49767
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
47228
49768
|
const sp = heroPosition(selectedHero);
|
|
47229
|
-
const
|
|
49769
|
+
const sTeam = str(selectedHero.team);
|
|
47230
49770
|
const range = heroMovement(selectedHero);
|
|
47231
49771
|
const moves = [];
|
|
47232
|
-
|
|
47233
|
-
const
|
|
47234
|
-
const
|
|
47235
|
-
if (
|
|
47236
|
-
if (
|
|
47237
|
-
if (!isInRange(sp, { x:
|
|
47238
|
-
if (
|
|
47239
|
-
const
|
|
47240
|
-
return
|
|
49772
|
+
tiles.forEach((t) => {
|
|
49773
|
+
const tx = t.x;
|
|
49774
|
+
const ty = t.y;
|
|
49775
|
+
if (t.passable === false) return;
|
|
49776
|
+
if (tx === sp.x && ty === sp.y) return;
|
|
49777
|
+
if (!isInRange(sp, { x: tx, y: ty }, range)) return;
|
|
49778
|
+
if (gameUnits.some((u) => {
|
|
49779
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
49780
|
+
return up.x === tx && up.y === ty && str(u.team) === sTeam;
|
|
47241
49781
|
})) return;
|
|
47242
|
-
moves.push({ x:
|
|
49782
|
+
moves.push({ x: tx, y: ty });
|
|
47243
49783
|
});
|
|
47244
49784
|
return moves;
|
|
47245
|
-
}, [selectedHero,
|
|
49785
|
+
}, [selectedHero, tiles, gameUnits, isInRange]);
|
|
47246
49786
|
const attackTargets = useMemo(() => {
|
|
47247
49787
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
47248
49788
|
const sp = heroPosition(selectedHero);
|
|
47249
|
-
const
|
|
49789
|
+
const sTeam = str(selectedHero.team);
|
|
47250
49790
|
const range = heroMovement(selectedHero);
|
|
47251
|
-
return
|
|
47252
|
-
}, [selectedHero,
|
|
47253
|
-
const maxY = Math.max(...
|
|
49791
|
+
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 });
|
|
49792
|
+
}, [selectedHero, gameUnits, isInRange]);
|
|
49793
|
+
const maxY = Math.max(...tiles.map((t) => t.y), 0);
|
|
47254
49794
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
47255
49795
|
const tileToScreen = useCallback(
|
|
47256
49796
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
47257
49797
|
[scale, baseOffsetX]
|
|
47258
49798
|
);
|
|
47259
49799
|
const hoveredHex = useMemo(
|
|
47260
|
-
() => hoveredTile ?
|
|
47261
|
-
[hoveredTile,
|
|
49800
|
+
() => hoveredTile ? tiles.find((t) => t.x === hoveredTile.x && t.y === hoveredTile.y) ?? null : null,
|
|
49801
|
+
[hoveredTile, tiles]
|
|
47262
49802
|
);
|
|
47263
49803
|
const hoveredHero = useMemo(
|
|
47264
|
-
() => hoveredTile ?
|
|
47265
|
-
const
|
|
47266
|
-
return
|
|
49804
|
+
() => hoveredTile ? gameUnits.find((u) => {
|
|
49805
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
49806
|
+
return up.x === hoveredTile.x && up.y === hoveredTile.y;
|
|
47267
49807
|
}) ?? null : null,
|
|
47268
|
-
[hoveredTile,
|
|
49808
|
+
[hoveredTile, gameUnits]
|
|
47269
49809
|
);
|
|
47270
49810
|
const handleTileClick = useCallback((x, y) => {
|
|
47271
49811
|
if (movementAnimRef.current) return;
|
|
47272
|
-
const
|
|
47273
|
-
if (!hex) return;
|
|
49812
|
+
const tile = tiles.find((t) => t.x === x && t.y === y);
|
|
47274
49813
|
if (tileClickEvent) {
|
|
47275
49814
|
eventBus.emit(`UI:${tileClickEvent}`, { x, y });
|
|
47276
49815
|
}
|
|
@@ -47281,38 +49820,39 @@ function WorldMapBoard({
|
|
|
47281
49820
|
if (heroMoveEvent) {
|
|
47282
49821
|
eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
|
|
47283
49822
|
}
|
|
47284
|
-
const feature = str(
|
|
49823
|
+
const feature = tile ? str(tile.feature) : "";
|
|
47285
49824
|
if (feature && feature !== "none") {
|
|
47286
|
-
|
|
49825
|
+
const tileRow = tile;
|
|
49826
|
+
onFeatureEnter?.(heroId, tileRow);
|
|
47287
49827
|
if (featureEnterEvent) {
|
|
47288
|
-
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
|
|
49828
|
+
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex: tileRow });
|
|
47289
49829
|
}
|
|
47290
49830
|
}
|
|
47291
49831
|
});
|
|
47292
49832
|
return;
|
|
47293
49833
|
}
|
|
47294
|
-
const enemy =
|
|
47295
|
-
const
|
|
47296
|
-
return
|
|
49834
|
+
const enemy = gameUnits.find((u) => {
|
|
49835
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
49836
|
+
return up.x === x && up.y === y && str(u.team) === "enemy";
|
|
47297
49837
|
});
|
|
47298
49838
|
if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
|
|
47299
49839
|
const attackerId = str(selectedHero.id);
|
|
47300
|
-
const defenderId =
|
|
49840
|
+
const defenderId = enemy.id;
|
|
47301
49841
|
onBattleEncounter?.(attackerId, defenderId);
|
|
47302
49842
|
if (battleEncounterEvent) {
|
|
47303
49843
|
eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
|
|
47304
49844
|
}
|
|
47305
49845
|
}
|
|
47306
|
-
}, [
|
|
49846
|
+
}, [tiles, gameUnits, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
|
|
47307
49847
|
const handleUnitClick = useCallback((unitId) => {
|
|
47308
|
-
const
|
|
47309
|
-
if (
|
|
49848
|
+
const unit = gameUnits.find((u) => u.id === unitId);
|
|
49849
|
+
if (unit && (str(unit.team) === "player" || allowMoveAllHeroes)) {
|
|
47310
49850
|
onHeroSelect?.(unitId);
|
|
47311
49851
|
if (heroSelectEvent) {
|
|
47312
49852
|
eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
|
|
47313
49853
|
}
|
|
47314
49854
|
}
|
|
47315
|
-
}, [
|
|
49855
|
+
}, [gameUnits, onHeroSelect, allowMoveAllHeroes, eventBus, heroSelectEvent]);
|
|
47316
49856
|
const selectHero = useCallback((id) => {
|
|
47317
49857
|
onHeroSelect?.(id);
|
|
47318
49858
|
if (heroSelectEvent) {
|
|
@@ -47416,48 +49956,48 @@ function WorldMapTemplate({
|
|
|
47416
49956
|
}
|
|
47417
49957
|
);
|
|
47418
49958
|
}
|
|
47419
|
-
var
|
|
49959
|
+
var CDN7, DEFAULT_WORLDMAP_TILES, DEFAULT_WORLDMAP_UNITS, DEFAULT_WORLDMAP_FEATURES, DEFAULT_WORLDMAP_MANIFEST;
|
|
47420
49960
|
var init_WorldMapTemplate = __esm({
|
|
47421
49961
|
"components/game/templates/WorldMapTemplate.tsx"() {
|
|
47422
49962
|
init_WorldMapBoard();
|
|
47423
|
-
|
|
49963
|
+
CDN7 = "https://almadar-kflow-assets.web.app/shared";
|
|
47424
49964
|
DEFAULT_WORLDMAP_TILES = [
|
|
47425
|
-
{ x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47426
|
-
{ x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47427
|
-
{ x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${
|
|
47428
|
-
{ x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47429
|
-
{ x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47430
|
-
{ x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47431
|
-
{ x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
47432
|
-
{ x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
47433
|
-
{ x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
47434
|
-
{ x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${
|
|
47435
|
-
{ x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${
|
|
47436
|
-
{ x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
47437
|
-
{ x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
47438
|
-
{ x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
47439
|
-
{ x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47440
|
-
{ x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47441
|
-
{ x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
47442
|
-
{ x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
47443
|
-
{ x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
47444
|
-
{ x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47445
|
-
{ x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47446
|
-
{ x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${
|
|
47447
|
-
{ x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${
|
|
47448
|
-
{ x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47449
|
-
{ x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49965
|
+
{ x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49966
|
+
{ x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49967
|
+
{ x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
49968
|
+
{ x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49969
|
+
{ x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49970
|
+
{ x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49971
|
+
{ x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49972
|
+
{ x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49973
|
+
{ x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49974
|
+
{ x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
49975
|
+
{ x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
49976
|
+
{ x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49977
|
+
{ x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
|
|
49978
|
+
{ x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49979
|
+
{ x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49980
|
+
{ x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49981
|
+
{ x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49982
|
+
{ x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49983
|
+
{ x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
|
|
49984
|
+
{ x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49985
|
+
{ x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49986
|
+
{ x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
49987
|
+
{ x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49988
|
+
{ x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49989
|
+
{ x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` }
|
|
47450
49990
|
];
|
|
47451
49991
|
DEFAULT_WORLDMAP_UNITS = [
|
|
47452
|
-
{ id: "h1", position: { x: 1, y: 1 }, unitType: "hero", name: "Amir", team: "player", health: 10, maxHealth: 10, sprite: `${
|
|
47453
|
-
{ id: "h2", position: { x: 3, y: 3 }, unitType: "scout", name: "Archivist", team: "player", health: 10, maxHealth: 10, sprite: `${
|
|
49992
|
+
{ 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` },
|
|
49993
|
+
{ 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` }
|
|
47454
49994
|
];
|
|
47455
49995
|
DEFAULT_WORLDMAP_FEATURES = [
|
|
47456
|
-
{ id: "f1", x: 2, y: 2, type: "capital", sprite: `${
|
|
47457
|
-
{ id: "f2", x: 4, y: 2, type: "power_node", sprite: `${
|
|
49996
|
+
{ id: "f1", x: 2, y: 2, type: "capital", sprite: `${CDN7}/scenes/world/capital.png` },
|
|
49997
|
+
{ id: "f2", x: 4, y: 2, type: "power_node", sprite: `${CDN7}/world-map/power_node.png` }
|
|
47458
49998
|
];
|
|
47459
49999
|
DEFAULT_WORLDMAP_MANIFEST = {
|
|
47460
|
-
baseUrl:
|
|
50000
|
+
baseUrl: CDN7,
|
|
47461
50001
|
terrains: {
|
|
47462
50002
|
grass: "/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png",
|
|
47463
50003
|
water: "/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png",
|
|
@@ -47502,7 +50042,7 @@ function lazyThree(name, loader) {
|
|
|
47502
50042
|
ThreeWrapper.displayName = `Lazy(${name})`;
|
|
47503
50043
|
return ThreeWrapper;
|
|
47504
50044
|
}
|
|
47505
|
-
var ThreeBoundary, FeatureRenderer,
|
|
50045
|
+
var ThreeBoundary, FeatureRenderer, GameCanvas3D2, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
|
|
47506
50046
|
var init_component_registry_generated = __esm({
|
|
47507
50047
|
"components/core/organisms/component-registry.generated.ts"() {
|
|
47508
50048
|
init_AboutPageTemplate();
|
|
@@ -47612,6 +50152,7 @@ var init_component_registry_generated = __esm({
|
|
|
47612
50152
|
init_FormSectionHeader();
|
|
47613
50153
|
init_GameAudioProvider();
|
|
47614
50154
|
init_GameAudioToggle();
|
|
50155
|
+
init_GameBoard3D();
|
|
47615
50156
|
init_GameCanvas2D();
|
|
47616
50157
|
init_GameHud();
|
|
47617
50158
|
init_GameMenu();
|
|
@@ -47669,7 +50210,9 @@ var init_component_registry_generated = __esm({
|
|
|
47669
50210
|
init_PageHeader();
|
|
47670
50211
|
init_Pagination();
|
|
47671
50212
|
init_PatternTile();
|
|
50213
|
+
init_PlatformerBoard();
|
|
47672
50214
|
init_PlatformerCanvas();
|
|
50215
|
+
init_PlatformerTemplate();
|
|
47673
50216
|
init_Popover();
|
|
47674
50217
|
init_PositionedCanvas();
|
|
47675
50218
|
init_PowerupSlots();
|
|
@@ -47693,6 +50236,8 @@ var init_component_registry_generated = __esm({
|
|
|
47693
50236
|
init_ResourceBar();
|
|
47694
50237
|
init_ResourceCounter();
|
|
47695
50238
|
init_RichBlockEditor();
|
|
50239
|
+
init_RoguelikeBoard();
|
|
50240
|
+
init_RoguelikeTemplate();
|
|
47696
50241
|
init_RuntimeDebugger2();
|
|
47697
50242
|
init_ScaledDiagram();
|
|
47698
50243
|
init_ScoreBoard();
|
|
@@ -47765,6 +50310,8 @@ var init_component_registry_generated = __esm({
|
|
|
47765
50310
|
init_Toast();
|
|
47766
50311
|
init_ToastSlot();
|
|
47767
50312
|
init_Tooltip();
|
|
50313
|
+
init_TowerDefenseBoard();
|
|
50314
|
+
init_TowerDefenseTemplate();
|
|
47768
50315
|
init_TraitFrame();
|
|
47769
50316
|
init_TraitSlot();
|
|
47770
50317
|
init_TrendIndicator();
|
|
@@ -47809,7 +50356,7 @@ var init_component_registry_generated = __esm({
|
|
|
47809
50356
|
}
|
|
47810
50357
|
};
|
|
47811
50358
|
FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
|
|
47812
|
-
|
|
50359
|
+
GameCanvas3D2 = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
|
|
47813
50360
|
GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
47814
50361
|
GameCanvas3DCastleTemplate = lazyThree("GameCanvas3DCastleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
47815
50362
|
GameCanvas3DWorldMapTemplate = lazyThree("GameCanvas3DWorldMapTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
@@ -47928,8 +50475,9 @@ var init_component_registry_generated = __esm({
|
|
|
47928
50475
|
"FormSectionHeader": FormSectionHeader,
|
|
47929
50476
|
"GameAudioProvider": GameAudioProvider,
|
|
47930
50477
|
"GameAudioToggle": GameAudioToggle,
|
|
50478
|
+
"GameBoard3D": GameBoard3D,
|
|
47931
50479
|
"GameCanvas2D": GameCanvas2D,
|
|
47932
|
-
"GameCanvas3D":
|
|
50480
|
+
"GameCanvas3D": GameCanvas3D2,
|
|
47933
50481
|
"GameCanvas3DBattleTemplate": GameCanvas3DBattleTemplate,
|
|
47934
50482
|
"GameCanvas3DCastleTemplate": GameCanvas3DCastleTemplate,
|
|
47935
50483
|
"GameCanvas3DWorldMapTemplate": GameCanvas3DWorldMapTemplate,
|
|
@@ -47991,7 +50539,9 @@ var init_component_registry_generated = __esm({
|
|
|
47991
50539
|
"PageHeader": PageHeader,
|
|
47992
50540
|
"Pagination": Pagination,
|
|
47993
50541
|
"PatternTile": PatternTile,
|
|
50542
|
+
"PlatformerBoard": PlatformerBoard,
|
|
47994
50543
|
"PlatformerCanvas": PlatformerCanvas,
|
|
50544
|
+
"PlatformerTemplate": PlatformerTemplate,
|
|
47995
50545
|
"Popover": Popover,
|
|
47996
50546
|
"PositionedCanvas": PositionedCanvas,
|
|
47997
50547
|
"PowerupSlots": PowerupSlots,
|
|
@@ -48015,6 +50565,8 @@ var init_component_registry_generated = __esm({
|
|
|
48015
50565
|
"ResourceBar": ResourceBar,
|
|
48016
50566
|
"ResourceCounter": ResourceCounter,
|
|
48017
50567
|
"RichBlockEditor": RichBlockEditor,
|
|
50568
|
+
"RoguelikeBoard": RoguelikeBoard,
|
|
50569
|
+
"RoguelikeTemplate": RoguelikeTemplate,
|
|
48018
50570
|
"RuntimeDebugger": RuntimeDebugger,
|
|
48019
50571
|
"ScaledDiagram": ScaledDiagram,
|
|
48020
50572
|
"ScoreBoard": ScoreBoard,
|
|
@@ -48090,6 +50642,8 @@ var init_component_registry_generated = __esm({
|
|
|
48090
50642
|
"Toast": Toast,
|
|
48091
50643
|
"ToastSlot": ToastSlot,
|
|
48092
50644
|
"Tooltip": Tooltip,
|
|
50645
|
+
"TowerDefenseBoard": TowerDefenseBoard,
|
|
50646
|
+
"TowerDefenseTemplate": TowerDefenseTemplate,
|
|
48093
50647
|
"TraitFrame": TraitFrame,
|
|
48094
50648
|
"TraitSlot": TraitSlot,
|
|
48095
50649
|
"TrendIndicator": TrendIndicator,
|
|
@@ -49261,7 +51815,7 @@ init_UISlotRenderer();
|
|
|
49261
51815
|
// providers/VerificationProvider.tsx
|
|
49262
51816
|
init_useEventBus();
|
|
49263
51817
|
init_verificationRegistry();
|
|
49264
|
-
var
|
|
51818
|
+
var log13 = createLogger("almadar:verify");
|
|
49265
51819
|
var DISPATCH_SUFFIX = ":DISPATCH";
|
|
49266
51820
|
var SUCCESS_SUFFIX = ":SUCCESS";
|
|
49267
51821
|
var ERROR_SUFFIX = ":ERROR";
|
|
@@ -49308,7 +51862,7 @@ function VerificationProvider({
|
|
|
49308
51862
|
const verificationProviderLifecycleListener = (evt) => {
|
|
49309
51863
|
const parsed = parseLifecycleEvent(evt.type);
|
|
49310
51864
|
if (!parsed) return;
|
|
49311
|
-
|
|
51865
|
+
log13.debug("lifecycle:event", { kind: parsed.kind, traitName: parsed.traitName, event: parsed.event, type: evt.type });
|
|
49312
51866
|
const payload = evt.payload ?? {};
|
|
49313
51867
|
if (parsed.kind === "dispatch") {
|
|
49314
51868
|
const key = `${parsed.traitName}:${String(payload["event"] ?? "")}`;
|
|
@@ -49363,7 +51917,7 @@ function VerificationProvider({
|
|
|
49363
51917
|
},
|
|
49364
51918
|
timestamp: Date.now()
|
|
49365
51919
|
});
|
|
49366
|
-
|
|
51920
|
+
log13.debug("transition:success", { trait: parsed.traitName, event: parsed.event, from: pending?.from, to: newState, effectCount: effects.length });
|
|
49367
51921
|
} else if (parsed.kind === "error" && parsed.event) {
|
|
49368
51922
|
const key = `${parsed.traitName}:${parsed.event}`;
|
|
49369
51923
|
const pending = pendingRef.current.get(key);
|
|
@@ -49393,7 +51947,7 @@ function VerificationProvider({
|
|
|
49393
51947
|
},
|
|
49394
51948
|
timestamp: Date.now()
|
|
49395
51949
|
});
|
|
49396
|
-
|
|
51950
|
+
log13.warn("transition:error", { trait: parsed.traitName, event: parsed.event, from: fromState, error: errorMsg });
|
|
49397
51951
|
}
|
|
49398
51952
|
};
|
|
49399
51953
|
Object.defineProperty(verificationProviderLifecycleListener, "name", {
|