@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
|
@@ -40,6 +40,12 @@ var core = require('@dnd-kit/core');
|
|
|
40
40
|
var sortable = require('@dnd-kit/sortable');
|
|
41
41
|
var utilities = require('@dnd-kit/utilities');
|
|
42
42
|
var react = require('@xyflow/react');
|
|
43
|
+
var THREE = require('three');
|
|
44
|
+
var GLTFLoader_js = require('three/examples/jsm/loaders/GLTFLoader.js');
|
|
45
|
+
var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
|
|
46
|
+
var GLTFLoader = require('three/examples/jsm/loaders/GLTFLoader');
|
|
47
|
+
var fiber = require('@react-three/fiber');
|
|
48
|
+
var drei = require('@react-three/drei');
|
|
43
49
|
var context = require('@almadar/ui/context');
|
|
44
50
|
var patterns = require('@almadar/patterns');
|
|
45
51
|
|
|
@@ -88,6 +94,7 @@ var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
|
|
|
88
94
|
var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
|
|
89
95
|
var remarkMath__default = /*#__PURE__*/_interopDefault(remarkMath);
|
|
90
96
|
var rehypeKatex__default = /*#__PURE__*/_interopDefault(rehypeKatex);
|
|
97
|
+
var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
|
|
91
98
|
|
|
92
99
|
var __defProp = Object.defineProperty;
|
|
93
100
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -1321,9 +1328,9 @@ function resolvePhosphor(name, weight, family) {
|
|
|
1321
1328
|
(lib) => {
|
|
1322
1329
|
const PhosphorComp = lib[target];
|
|
1323
1330
|
if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
|
|
1324
|
-
const
|
|
1331
|
+
const Component2 = PhosphorComp;
|
|
1325
1332
|
const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1326
|
-
|
|
1333
|
+
Component2,
|
|
1327
1334
|
{
|
|
1328
1335
|
weight,
|
|
1329
1336
|
className: props.className,
|
|
@@ -1347,9 +1354,9 @@ function resolveTabler(name, family) {
|
|
|
1347
1354
|
(lib) => {
|
|
1348
1355
|
const TablerComp = lib[target];
|
|
1349
1356
|
if (!TablerComp || typeof TablerComp !== "object") return null;
|
|
1350
|
-
const
|
|
1357
|
+
const Component2 = TablerComp;
|
|
1351
1358
|
const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1352
|
-
|
|
1359
|
+
Component2,
|
|
1353
1360
|
{
|
|
1354
1361
|
stroke: props.strokeWidth ?? 1.5,
|
|
1355
1362
|
className: props.className,
|
|
@@ -1373,9 +1380,9 @@ function resolveFa(name, family) {
|
|
|
1373
1380
|
(lib) => {
|
|
1374
1381
|
const FaComp = lib[target];
|
|
1375
1382
|
if (!FaComp || typeof FaComp !== "function") return null;
|
|
1376
|
-
const
|
|
1383
|
+
const Component2 = FaComp;
|
|
1377
1384
|
const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1378
|
-
|
|
1385
|
+
Component2,
|
|
1379
1386
|
{
|
|
1380
1387
|
className: props.className,
|
|
1381
1388
|
style: props.style,
|
|
@@ -3368,7 +3375,7 @@ var init_Box = __esm({
|
|
|
3368
3375
|
position,
|
|
3369
3376
|
className,
|
|
3370
3377
|
children,
|
|
3371
|
-
as:
|
|
3378
|
+
as: Component2 = "div",
|
|
3372
3379
|
action,
|
|
3373
3380
|
actionPayload,
|
|
3374
3381
|
hoverEvent,
|
|
@@ -3400,7 +3407,7 @@ var init_Box = __esm({
|
|
|
3400
3407
|
}, [hoverEvent, eventBus, onMouseLeave]);
|
|
3401
3408
|
const isClickable = action || onClick;
|
|
3402
3409
|
return React77__namespace.default.createElement(
|
|
3403
|
-
|
|
3410
|
+
Component2,
|
|
3404
3411
|
{
|
|
3405
3412
|
ref,
|
|
3406
3413
|
className: cn(
|
|
@@ -3449,10 +3456,10 @@ var init_Center = __esm({
|
|
|
3449
3456
|
className,
|
|
3450
3457
|
style,
|
|
3451
3458
|
children,
|
|
3452
|
-
as:
|
|
3459
|
+
as: Component2 = "div"
|
|
3453
3460
|
}) => {
|
|
3454
3461
|
const mergedStyle = minHeight ? { minHeight, ...style } : style;
|
|
3455
|
-
const Comp =
|
|
3462
|
+
const Comp = Component2;
|
|
3456
3463
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3457
3464
|
Comp,
|
|
3458
3465
|
{
|
|
@@ -4083,7 +4090,7 @@ var init_Stack = __esm({
|
|
|
4083
4090
|
className,
|
|
4084
4091
|
style,
|
|
4085
4092
|
children,
|
|
4086
|
-
as:
|
|
4093
|
+
as: Component2 = "div",
|
|
4087
4094
|
onClick,
|
|
4088
4095
|
onKeyDown,
|
|
4089
4096
|
role,
|
|
@@ -4101,7 +4108,7 @@ var init_Stack = __esm({
|
|
|
4101
4108
|
};
|
|
4102
4109
|
const isHorizontal = direction === "horizontal";
|
|
4103
4110
|
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";
|
|
4104
|
-
const Comp =
|
|
4111
|
+
const Comp = Component2;
|
|
4105
4112
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4106
4113
|
Comp,
|
|
4107
4114
|
{
|
|
@@ -4292,8 +4299,8 @@ var init_Typography = __esm({
|
|
|
4292
4299
|
children
|
|
4293
4300
|
}) => {
|
|
4294
4301
|
const variant = variantProp ?? (level ? `h${level}` : "body1");
|
|
4295
|
-
const
|
|
4296
|
-
const Comp =
|
|
4302
|
+
const Component2 = as || defaultElements[variant];
|
|
4303
|
+
const Comp = Component2;
|
|
4297
4304
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4298
4305
|
Comp,
|
|
4299
4306
|
{
|
|
@@ -7506,10 +7513,10 @@ var init_Container = __esm({
|
|
|
7506
7513
|
center = true,
|
|
7507
7514
|
className,
|
|
7508
7515
|
children,
|
|
7509
|
-
as:
|
|
7516
|
+
as: Component2 = "div"
|
|
7510
7517
|
}) => {
|
|
7511
7518
|
const resolvedSize = maxWidth ?? size ?? "lg";
|
|
7512
|
-
const Comp =
|
|
7519
|
+
const Comp = Component2;
|
|
7513
7520
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7514
7521
|
Comp,
|
|
7515
7522
|
{
|
|
@@ -8224,6 +8231,7 @@ var init_Accordion = __esm({
|
|
|
8224
8231
|
}
|
|
8225
8232
|
});
|
|
8226
8233
|
function ActionButton({
|
|
8234
|
+
assetUrl = DEFAULT_ASSET_URL,
|
|
8227
8235
|
label = "Attack",
|
|
8228
8236
|
icon,
|
|
8229
8237
|
cooldown = 0,
|
|
@@ -8263,13 +8271,23 @@ function ActionButton({
|
|
|
8263
8271
|
}
|
|
8264
8272
|
}
|
|
8265
8273
|
),
|
|
8266
|
-
|
|
8274
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
8275
|
+
"img",
|
|
8276
|
+
{
|
|
8277
|
+
src: assetUrl,
|
|
8278
|
+
alt: "",
|
|
8279
|
+
width: 16,
|
|
8280
|
+
height: 16,
|
|
8281
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
8282
|
+
className: cn("flex-shrink-0", sizes.icon)
|
|
8283
|
+
}
|
|
8284
|
+
) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? (() => {
|
|
8267
8285
|
const I = resolveIcon(icon);
|
|
8268
8286
|
return I ? /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-4 h-4" }) : null;
|
|
8269
8287
|
})() : /* @__PURE__ */ (() => {
|
|
8270
8288
|
const I = icon;
|
|
8271
8289
|
return /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-4 h-4" });
|
|
8272
|
-
})() }),
|
|
8290
|
+
})() }) : null,
|
|
8273
8291
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative z-10", children: label }),
|
|
8274
8292
|
hotkey && /* @__PURE__ */ jsxRuntime.jsx(
|
|
8275
8293
|
"span",
|
|
@@ -8285,7 +8303,7 @@ function ActionButton({
|
|
|
8285
8303
|
}
|
|
8286
8304
|
);
|
|
8287
8305
|
}
|
|
8288
|
-
var sizeMap, variantStyles7;
|
|
8306
|
+
var sizeMap, variantStyles7, DEFAULT_ASSET_URL;
|
|
8289
8307
|
var init_ActionButton = __esm({
|
|
8290
8308
|
"components/game/atoms/ActionButton.tsx"() {
|
|
8291
8309
|
init_cn();
|
|
@@ -8300,10 +8318,12 @@ var init_ActionButton = __esm({
|
|
|
8300
8318
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary-hover border-border",
|
|
8301
8319
|
danger: "bg-error text-error-foreground hover:bg-error/90 border-error"
|
|
8302
8320
|
};
|
|
8321
|
+
DEFAULT_ASSET_URL = "https://almadar-kflow-assets.web.app/shared/effects/particles/slash_01.png";
|
|
8303
8322
|
ActionButton.displayName = "ActionButton";
|
|
8304
8323
|
}
|
|
8305
8324
|
});
|
|
8306
8325
|
function ControlButton({
|
|
8326
|
+
assetUrl = DEFAULT_ASSET_URL2,
|
|
8307
8327
|
label,
|
|
8308
8328
|
icon,
|
|
8309
8329
|
size = "md",
|
|
@@ -8350,7 +8370,7 @@ function ControlButton({
|
|
|
8350
8370
|
},
|
|
8351
8371
|
[isPressed, releaseEvent, eventBus, onRelease]
|
|
8352
8372
|
);
|
|
8353
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
8373
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
8354
8374
|
"button",
|
|
8355
8375
|
{
|
|
8356
8376
|
type: "button",
|
|
@@ -8371,20 +8391,27 @@ function ControlButton({
|
|
|
8371
8391
|
disabled && "opacity-50 cursor-not-allowed",
|
|
8372
8392
|
className
|
|
8373
8393
|
),
|
|
8374
|
-
children:
|
|
8375
|
-
|
|
8376
|
-
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
|
|
8394
|
+
children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
8395
|
+
"img",
|
|
8396
|
+
{
|
|
8397
|
+
src: assetUrl,
|
|
8398
|
+
alt: "",
|
|
8399
|
+
width: 24,
|
|
8400
|
+
height: 24,
|
|
8401
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
8402
|
+
className: "flex-shrink-0"
|
|
8403
|
+
}
|
|
8404
|
+
) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-2xl", children: typeof icon === "string" ? /^[a-zA-Z0-9-]+$/.test(icon) ? (() => {
|
|
8405
|
+
const I = resolveIcon(icon);
|
|
8406
|
+
return I ? /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-6 h-6" }) : null;
|
|
8407
|
+
})() : icon : /* @__PURE__ */ (() => {
|
|
8408
|
+
const I = icon;
|
|
8409
|
+
return /* @__PURE__ */ jsxRuntime.jsx(I, { className: "w-6 h-6" });
|
|
8410
|
+
})() }) : label ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: label }) : null
|
|
8384
8411
|
}
|
|
8385
8412
|
);
|
|
8386
8413
|
}
|
|
8387
|
-
var sizeMap2, shapeMap, variantMap;
|
|
8414
|
+
var sizeMap2, shapeMap, variantMap, DEFAULT_ASSET_URL2;
|
|
8388
8415
|
var init_ControlButton = __esm({
|
|
8389
8416
|
"components/game/atoms/ControlButton.tsx"() {
|
|
8390
8417
|
"use client";
|
|
@@ -8407,6 +8434,7 @@ var init_ControlButton = __esm({
|
|
|
8407
8434
|
secondary: "bg-secondary text-secondary-foreground border-border hover:bg-secondary-hover",
|
|
8408
8435
|
ghost: "bg-transparent text-foreground border-border hover:bg-muted"
|
|
8409
8436
|
};
|
|
8437
|
+
DEFAULT_ASSET_URL2 = "https://almadar-kflow-assets.web.app/shared/effects/particles/circle_01.png";
|
|
8410
8438
|
ControlButton.displayName = "ControlButton";
|
|
8411
8439
|
}
|
|
8412
8440
|
});
|
|
@@ -10429,15 +10457,15 @@ function BattleBoard({
|
|
|
10429
10457
|
const board = boardEntity(entity) ?? {};
|
|
10430
10458
|
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
|
|
10431
10459
|
const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
|
|
10432
|
-
const boardWidth = num(board.boardWidth, 8);
|
|
10433
|
-
const boardHeight = num(board.boardHeight, 6);
|
|
10460
|
+
const boardWidth = num(board.gridWidth ?? board.boardWidth, 8);
|
|
10461
|
+
const boardHeight = num(board.gridHeight ?? board.boardHeight, 6);
|
|
10434
10462
|
const assetManifest = propAssetManifest ?? board.assetManifest;
|
|
10435
10463
|
const backgroundImage = board.backgroundImage;
|
|
10436
10464
|
const units = rows(board.units);
|
|
10437
10465
|
const selectedUnitId = board.selectedUnitId ?? null;
|
|
10438
10466
|
const currentPhase = str(board.phase) || "observation";
|
|
10439
10467
|
const currentTurn = num(board.turn, 1);
|
|
10440
|
-
const gameResult = board.
|
|
10468
|
+
const gameResult = board.result ?? null;
|
|
10441
10469
|
const eventBus = useEventBus();
|
|
10442
10470
|
const { t } = hooks.useTranslate();
|
|
10443
10471
|
const [hoveredTile, setHoveredTile] = React77.useState(null);
|
|
@@ -10458,7 +10486,7 @@ function BattleBoard({
|
|
|
10458
10486
|
const validMoves = React77.useMemo(() => {
|
|
10459
10487
|
if (!selectedUnit || currentPhase !== "movement") return [];
|
|
10460
10488
|
const moves = [];
|
|
10461
|
-
const range = num(
|
|
10489
|
+
const range = num(board.movementRange, 2);
|
|
10462
10490
|
const origin = unitPosition(selectedUnit);
|
|
10463
10491
|
for (let dy = -range; dy <= range; dy++) {
|
|
10464
10492
|
for (let dx = -range; dx <= range; dx++) {
|
|
@@ -18743,15 +18771,24 @@ function CastleBoard({
|
|
|
18743
18771
|
featureClickEvent,
|
|
18744
18772
|
unitClickEvent,
|
|
18745
18773
|
tileClickEvent,
|
|
18774
|
+
playAgainEvent,
|
|
18746
18775
|
className
|
|
18747
18776
|
}) {
|
|
18748
18777
|
const eventBus = useEventBus();
|
|
18778
|
+
const { t } = hooks.useTranslate();
|
|
18749
18779
|
const resolved = boardEntity(entity);
|
|
18750
18780
|
const tiles = propTiles ?? (Array.isArray(resolved?.tiles) ? resolved.tiles : []);
|
|
18751
18781
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
18752
18782
|
const units = propUnits ?? (Array.isArray(resolved?.units) ? resolved.units : []);
|
|
18753
18783
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
18754
18784
|
const backgroundImage = resolved?.backgroundImage;
|
|
18785
|
+
const gold = num(resolved?.gold);
|
|
18786
|
+
const health = num(resolved?.health);
|
|
18787
|
+
const maxHealth = num(resolved?.maxHealth);
|
|
18788
|
+
const wave = num(resolved?.wave);
|
|
18789
|
+
const tickCount = num(resolved?.tickCount);
|
|
18790
|
+
const buildings = rows(resolved?.buildings);
|
|
18791
|
+
const result = str(resolved?.result) || "none";
|
|
18755
18792
|
const [hoveredTile, setHoveredTile] = React77.useState(null);
|
|
18756
18793
|
const [selectedFeature, setSelectedFeature] = React77.useState(null);
|
|
18757
18794
|
const hoveredFeature = React77.useMemo(() => {
|
|
@@ -18764,7 +18801,7 @@ function CastleBoard({
|
|
|
18764
18801
|
(u) => u.position?.x === hoveredTile.x && u.position?.y === hoveredTile.y
|
|
18765
18802
|
) ?? null;
|
|
18766
18803
|
}, [hoveredTile, units]);
|
|
18767
|
-
const maxY = Math.max(...tiles.map((
|
|
18804
|
+
const maxY = Math.max(...tiles.map((t2) => t2.y), 0);
|
|
18768
18805
|
const baseOffsetX = (maxY + 1) * (exports.TILE_WIDTH * scale / 2);
|
|
18769
18806
|
const tileToScreen = React77.useCallback(
|
|
18770
18807
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
@@ -18799,6 +18836,11 @@ function CastleBoard({
|
|
|
18799
18836
|
}
|
|
18800
18837
|
}, [units, onUnitClick, unitClickEvent, eventBus]);
|
|
18801
18838
|
const clearSelection = React77.useCallback(() => setSelectedFeature(null), []);
|
|
18839
|
+
const handlePlayAgain = React77.useCallback(() => {
|
|
18840
|
+
if (playAgainEvent) {
|
|
18841
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
18842
|
+
}
|
|
18843
|
+
}, [playAgainEvent, eventBus]);
|
|
18802
18844
|
const ctx = React77.useMemo(
|
|
18803
18845
|
() => ({
|
|
18804
18846
|
hoveredTile,
|
|
@@ -18807,11 +18849,18 @@ function CastleBoard({
|
|
|
18807
18849
|
selectedFeature,
|
|
18808
18850
|
clearSelection,
|
|
18809
18851
|
tileToScreen,
|
|
18810
|
-
scale
|
|
18852
|
+
scale,
|
|
18853
|
+
gold,
|
|
18854
|
+
health,
|
|
18855
|
+
maxHealth,
|
|
18856
|
+
wave,
|
|
18857
|
+
tickCount,
|
|
18858
|
+
buildings,
|
|
18859
|
+
result
|
|
18811
18860
|
}),
|
|
18812
|
-
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale]
|
|
18861
|
+
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale, gold, health, maxHealth, wave, tickCount, buildings, result]
|
|
18813
18862
|
);
|
|
18814
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("castle-board min-h-screen flex flex-col bg-background", className), children: [
|
|
18863
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("castle-board relative min-h-screen flex flex-col bg-background", className), children: [
|
|
18815
18864
|
header && header(ctx),
|
|
18816
18865
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
|
|
18817
18866
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 overflow-auto p-4 relative", children: [
|
|
@@ -18836,7 +18885,29 @@ function CastleBoard({
|
|
|
18836
18885
|
] }),
|
|
18837
18886
|
sidePanel && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-96 shrink-0 border-l border-border bg-surface overflow-y-auto", children: sidePanel(ctx) })
|
|
18838
18887
|
] }),
|
|
18839
|
-
footer && footer(ctx)
|
|
18888
|
+
footer && footer(ctx),
|
|
18889
|
+
result !== "none" && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
18890
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
18891
|
+
exports.Typography,
|
|
18892
|
+
{
|
|
18893
|
+
variant: "h2",
|
|
18894
|
+
className: cn(
|
|
18895
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
18896
|
+
result === "victory" ? "text-warning" : "text-error"
|
|
18897
|
+
),
|
|
18898
|
+
children: result === "victory" ? t("battle.victory") : t("battle.defeat")
|
|
18899
|
+
}
|
|
18900
|
+
),
|
|
18901
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
18902
|
+
exports.Button,
|
|
18903
|
+
{
|
|
18904
|
+
variant: "primary",
|
|
18905
|
+
className: "px-8 py-3 font-semibold",
|
|
18906
|
+
onClick: handlePlayAgain,
|
|
18907
|
+
children: t("battle.playAgain")
|
|
18908
|
+
}
|
|
18909
|
+
)
|
|
18910
|
+
] }) })
|
|
18840
18911
|
] });
|
|
18841
18912
|
}
|
|
18842
18913
|
var init_CastleBoard = __esm({
|
|
@@ -18844,6 +18915,10 @@ var init_CastleBoard = __esm({
|
|
|
18844
18915
|
"use client";
|
|
18845
18916
|
init_cn();
|
|
18846
18917
|
init_useEventBus();
|
|
18918
|
+
init_Box();
|
|
18919
|
+
init_Button();
|
|
18920
|
+
init_Typography();
|
|
18921
|
+
init_Stack();
|
|
18847
18922
|
init_IsometricCanvas();
|
|
18848
18923
|
init_boardEntity();
|
|
18849
18924
|
init_isometric();
|
|
@@ -19766,22 +19841,16 @@ function ClassifierBoard({
|
|
|
19766
19841
|
const { emit } = useEventBus();
|
|
19767
19842
|
const { t } = hooks.useTranslate();
|
|
19768
19843
|
const resolved = boardEntity(entity);
|
|
19769
|
-
const [localAssignments, setLocalAssignments] = React77.useState({});
|
|
19770
19844
|
const [headerError, setHeaderError] = React77.useState(false);
|
|
19771
|
-
const [localSubmitted, setLocalSubmitted] = React77.useState(false);
|
|
19772
|
-
const [localAttempts, setLocalAttempts] = React77.useState(0);
|
|
19773
|
-
const [showHint, setShowHint] = React77.useState(false);
|
|
19774
19845
|
const items = Array.isArray(resolved?.items) ? resolved.items : [];
|
|
19775
19846
|
const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
|
|
19776
|
-
const
|
|
19777
|
-
const
|
|
19778
|
-
const
|
|
19779
|
-
const assignments =
|
|
19780
|
-
if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
|
|
19847
|
+
const result = str(resolved?.result);
|
|
19848
|
+
const submitted = result === "win";
|
|
19849
|
+
const attempts = num(resolved?.attempts);
|
|
19850
|
+
const assignments = items.reduce((acc, item) => {
|
|
19851
|
+
if (item.assignedCategory != null && item.assignedCategory !== "") acc[item.id] = item.assignedCategory;
|
|
19781
19852
|
return acc;
|
|
19782
|
-
}, {})
|
|
19783
|
-
const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
|
|
19784
|
-
const submitted = entityDrivesResult || localSubmitted;
|
|
19853
|
+
}, {});
|
|
19785
19854
|
const unassignedItems = items.filter((item) => !assignments[item.id]);
|
|
19786
19855
|
const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
|
|
19787
19856
|
const results = submitted ? items.map((item) => ({
|
|
@@ -19789,54 +19858,25 @@ function ClassifierBoard({
|
|
|
19789
19858
|
assigned: assignments[item.id],
|
|
19790
19859
|
correct: assignments[item.id] === item.correctCategory
|
|
19791
19860
|
})) : [];
|
|
19792
|
-
const allCorrect =
|
|
19861
|
+
const allCorrect = result === "win";
|
|
19793
19862
|
const correctCount = results.filter((r) => r.correct).length;
|
|
19863
|
+
const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
|
|
19794
19864
|
const handleAssign = (itemId, categoryId) => {
|
|
19795
19865
|
if (submitted) return;
|
|
19796
|
-
if (assignEvent) {
|
|
19797
|
-
emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
19798
|
-
}
|
|
19799
|
-
if (!entityHasAssignments) {
|
|
19800
|
-
setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
|
|
19801
|
-
}
|
|
19866
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
19802
19867
|
};
|
|
19803
19868
|
const handleUnassign = (itemId) => {
|
|
19804
19869
|
if (submitted) return;
|
|
19805
|
-
if (
|
|
19806
|
-
setLocalAssignments((prev) => {
|
|
19807
|
-
const next = { ...prev };
|
|
19808
|
-
delete next[itemId];
|
|
19809
|
-
return next;
|
|
19810
|
-
});
|
|
19811
|
-
}
|
|
19870
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId: "" });
|
|
19812
19871
|
};
|
|
19813
|
-
const handleSubmit =
|
|
19814
|
-
if (checkEvent) {
|
|
19815
|
-
|
|
19816
|
-
|
|
19817
|
-
if (!entityDrivesResult) {
|
|
19818
|
-
setLocalSubmitted(true);
|
|
19819
|
-
setLocalAttempts((a) => a + 1);
|
|
19820
|
-
const correct = items.every((item) => assignments[item.id] === item.correctCategory);
|
|
19821
|
-
if (correct) {
|
|
19822
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
19823
|
-
}
|
|
19824
|
-
}
|
|
19825
|
-
}, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
|
|
19826
|
-
const handleReset = () => {
|
|
19827
|
-
if (!entityDrivesResult) setLocalSubmitted(false);
|
|
19828
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
19829
|
-
setShowHint(true);
|
|
19872
|
+
const handleSubmit = () => {
|
|
19873
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
19874
|
+
if (allAssigned && items.every((item) => assignments[item.id] === item.correctCategory)) {
|
|
19875
|
+
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
19830
19876
|
}
|
|
19831
19877
|
};
|
|
19832
19878
|
const handleFullReset = () => {
|
|
19833
|
-
if (playAgainEvent) {
|
|
19834
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
19835
|
-
}
|
|
19836
|
-
setLocalAssignments({});
|
|
19837
|
-
setLocalSubmitted(false);
|
|
19838
|
-
setLocalAttempts(0);
|
|
19839
|
-
setShowHint(false);
|
|
19879
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
19840
19880
|
};
|
|
19841
19881
|
if (!resolved) return null;
|
|
19842
19882
|
const theme = resolved.theme ?? void 0;
|
|
@@ -19875,17 +19915,17 @@ function ClassifierBoard({
|
|
|
19875
19915
|
/* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { size: "sm", children: catItems.length })
|
|
19876
19916
|
] }),
|
|
19877
19917
|
/* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "xs", className: "flex-wrap min-h-[2rem]", children: catItems.map((item) => {
|
|
19878
|
-
const
|
|
19918
|
+
const result2 = results.find((r) => r.item.id === item.id);
|
|
19879
19919
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
19880
19920
|
exports.Badge,
|
|
19881
19921
|
{
|
|
19882
19922
|
size: "sm",
|
|
19883
|
-
className: `cursor-pointer ${
|
|
19923
|
+
className: `cursor-pointer ${result2 ? result2.correct ? "border-success bg-success/10" : "border-error bg-error/10" : ""}`,
|
|
19884
19924
|
onClick: () => handleUnassign(item.id),
|
|
19885
19925
|
children: [
|
|
19886
19926
|
item.iconUrl && /* @__PURE__ */ jsxRuntime.jsx("img", { src: item.iconUrl, alt: "", className: "w-3 h-3 object-contain inline-block" }),
|
|
19887
19927
|
item.label,
|
|
19888
|
-
|
|
19928
|
+
result2 && /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: result2.correct ? LucideIcons2.CheckCircle : LucideIcons2.XCircle, size: "xs", className: result2.correct ? "text-success" : "text-error" })
|
|
19889
19929
|
]
|
|
19890
19930
|
},
|
|
19891
19931
|
item.id
|
|
@@ -19914,10 +19954,10 @@ function ClassifierBoard({
|
|
|
19914
19954
|
] }) }),
|
|
19915
19955
|
showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", children: hint }) }),
|
|
19916
19956
|
/* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", justify: "center", children: [
|
|
19917
|
-
!submitted
|
|
19957
|
+
!submitted && /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
|
|
19918
19958
|
/* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.Send, size: "sm" }),
|
|
19919
19959
|
t("classifier.check")
|
|
19920
|
-
] })
|
|
19960
|
+
] }),
|
|
19921
19961
|
/* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "secondary", onClick: handleFullReset, children: [
|
|
19922
19962
|
/* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
|
|
19923
19963
|
t("classifier.reset")
|
|
@@ -20204,6 +20244,7 @@ function getComboScale(combo) {
|
|
|
20204
20244
|
return "";
|
|
20205
20245
|
}
|
|
20206
20246
|
function ComboCounter({
|
|
20247
|
+
assetUrl = DEFAULT_ASSET_URL3,
|
|
20207
20248
|
combo = 5,
|
|
20208
20249
|
multiplier,
|
|
20209
20250
|
streak,
|
|
@@ -20223,6 +20264,17 @@ function ComboCounter({
|
|
|
20223
20264
|
className
|
|
20224
20265
|
),
|
|
20225
20266
|
children: [
|
|
20267
|
+
assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
20268
|
+
"img",
|
|
20269
|
+
{
|
|
20270
|
+
src: assetUrl,
|
|
20271
|
+
alt: "combo",
|
|
20272
|
+
width: 24,
|
|
20273
|
+
height: 24,
|
|
20274
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
20275
|
+
className: "flex-shrink-0 mb-0.5"
|
|
20276
|
+
}
|
|
20277
|
+
),
|
|
20226
20278
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-black tabular-nums leading-none", sizes.combo, getComboIntensity(combo)), children: combo }),
|
|
20227
20279
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-bold uppercase tracking-wider text-muted-foreground", sizes.label), children: "combo" }),
|
|
20228
20280
|
multiplier != null && multiplier > 1 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("font-bold text-warning tabular-nums", sizes.multiplier), children: [
|
|
@@ -20237,10 +20289,11 @@ function ComboCounter({
|
|
|
20237
20289
|
}
|
|
20238
20290
|
);
|
|
20239
20291
|
}
|
|
20240
|
-
var sizeMap4;
|
|
20292
|
+
var DEFAULT_ASSET_URL3, sizeMap4;
|
|
20241
20293
|
var init_ComboCounter = __esm({
|
|
20242
20294
|
"components/game/atoms/ComboCounter.tsx"() {
|
|
20243
20295
|
init_cn();
|
|
20296
|
+
DEFAULT_ASSET_URL3 = "https://almadar-kflow-assets.web.app/shared/effects/flash/flash00.png";
|
|
20244
20297
|
sizeMap4 = {
|
|
20245
20298
|
sm: { combo: "text-lg", label: "text-xs", multiplier: "text-xs" },
|
|
20246
20299
|
md: { combo: "text-2xl", label: "text-xs", multiplier: "text-sm" },
|
|
@@ -20649,7 +20702,7 @@ var init_CounterTemplate = __esm({
|
|
|
20649
20702
|
}
|
|
20650
20703
|
});
|
|
20651
20704
|
function ItemSlot({
|
|
20652
|
-
assetUrl =
|
|
20705
|
+
assetUrl = DEFAULT_ASSET_URL4,
|
|
20653
20706
|
icon = "sword",
|
|
20654
20707
|
label = "Iron Sword",
|
|
20655
20708
|
quantity,
|
|
@@ -20708,7 +20761,7 @@ function ItemSlot({
|
|
|
20708
20761
|
}
|
|
20709
20762
|
);
|
|
20710
20763
|
}
|
|
20711
|
-
var sizeMap5, rarityBorderMap, rarityGlowMap,
|
|
20764
|
+
var sizeMap5, rarityBorderMap, rarityGlowMap, DEFAULT_ASSET_URL4, assetSizeMap;
|
|
20712
20765
|
var init_ItemSlot = __esm({
|
|
20713
20766
|
"components/game/atoms/ItemSlot.tsx"() {
|
|
20714
20767
|
"use client";
|
|
@@ -20733,7 +20786,7 @@ var init_ItemSlot = __esm({
|
|
|
20733
20786
|
epic: "shadow-lg",
|
|
20734
20787
|
legendary: "shadow-lg"
|
|
20735
20788
|
};
|
|
20736
|
-
|
|
20789
|
+
DEFAULT_ASSET_URL4 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
20737
20790
|
assetSizeMap = {
|
|
20738
20791
|
sm: 28,
|
|
20739
20792
|
md: 40,
|
|
@@ -20912,6 +20965,7 @@ var init_DPad = __esm({
|
|
|
20912
20965
|
}
|
|
20913
20966
|
});
|
|
20914
20967
|
function DamageNumber({
|
|
20968
|
+
assetUrl = DEFAULT_ASSET_URL5,
|
|
20915
20969
|
value = 42,
|
|
20916
20970
|
type = "damage",
|
|
20917
20971
|
size = "md",
|
|
@@ -20920,22 +20974,35 @@ function DamageNumber({
|
|
|
20920
20974
|
const displayText = type === "miss" ? "MISS" : type === "heal" ? `+${value}` : `${value}`;
|
|
20921
20975
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
20922
20976
|
/* @__PURE__ */ jsxRuntime.jsx("style", { children: floatKeyframes }),
|
|
20923
|
-
/* @__PURE__ */ jsxRuntime.
|
|
20977
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
20924
20978
|
"span",
|
|
20925
20979
|
{
|
|
20926
20980
|
className: cn(
|
|
20927
|
-
"inline-
|
|
20981
|
+
"inline-flex items-center gap-0.5 font-mono select-none pointer-events-none",
|
|
20928
20982
|
sizeMap7[size],
|
|
20929
20983
|
typeStyles[type],
|
|
20930
20984
|
className
|
|
20931
20985
|
),
|
|
20932
20986
|
style: { animation: "damageFloat 1s ease-out forwards" },
|
|
20933
|
-
children:
|
|
20987
|
+
children: [
|
|
20988
|
+
assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
20989
|
+
"img",
|
|
20990
|
+
{
|
|
20991
|
+
src: assetUrl,
|
|
20992
|
+
alt: "",
|
|
20993
|
+
width: 14,
|
|
20994
|
+
height: 14,
|
|
20995
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
20996
|
+
className: "flex-shrink-0"
|
|
20997
|
+
}
|
|
20998
|
+
),
|
|
20999
|
+
displayText
|
|
21000
|
+
]
|
|
20934
21001
|
}
|
|
20935
21002
|
)
|
|
20936
21003
|
] });
|
|
20937
21004
|
}
|
|
20938
|
-
var sizeMap7, typeStyles, floatKeyframes;
|
|
21005
|
+
var sizeMap7, typeStyles, floatKeyframes, DEFAULT_ASSET_URL5;
|
|
20939
21006
|
var init_DamageNumber = __esm({
|
|
20940
21007
|
"components/game/atoms/DamageNumber.tsx"() {
|
|
20941
21008
|
init_cn();
|
|
@@ -20957,6 +21024,7 @@ var init_DamageNumber = __esm({
|
|
|
20957
21024
|
100% { opacity: 0; transform: translateY(-32px) scale(0.8); }
|
|
20958
21025
|
}
|
|
20959
21026
|
`;
|
|
21027
|
+
DEFAULT_ASSET_URL5 = "https://almadar-kflow-assets.web.app/shared/effects/particles/spark_01.png";
|
|
20960
21028
|
DamageNumber.displayName = "DamageNumber";
|
|
20961
21029
|
}
|
|
20962
21030
|
});
|
|
@@ -24777,7 +24845,7 @@ var init_Flex = __esm({
|
|
|
24777
24845
|
basis,
|
|
24778
24846
|
className,
|
|
24779
24847
|
children,
|
|
24780
|
-
as:
|
|
24848
|
+
as: Component2 = "div"
|
|
24781
24849
|
}) => {
|
|
24782
24850
|
const flexStyle = {};
|
|
24783
24851
|
if (grow !== void 0 || shrink !== void 0 || basis !== void 0) {
|
|
@@ -24789,7 +24857,7 @@ var init_Flex = __esm({
|
|
|
24789
24857
|
flexStyle.flexBasis = typeof basis === "number" ? `${basis}px` : basis;
|
|
24790
24858
|
}
|
|
24791
24859
|
}
|
|
24792
|
-
const Comp =
|
|
24860
|
+
const Comp = Component2;
|
|
24793
24861
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
24794
24862
|
Comp,
|
|
24795
24863
|
{
|
|
@@ -24909,11 +24977,11 @@ var init_Grid = __esm({
|
|
|
24909
24977
|
className,
|
|
24910
24978
|
style,
|
|
24911
24979
|
children,
|
|
24912
|
-
as:
|
|
24980
|
+
as: Component2 = "div"
|
|
24913
24981
|
}) => {
|
|
24914
24982
|
const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
|
|
24915
24983
|
return React77__namespace.default.createElement(
|
|
24916
|
-
|
|
24984
|
+
Component2,
|
|
24917
24985
|
{
|
|
24918
24986
|
className: cn(
|
|
24919
24987
|
"grid",
|
|
@@ -27062,6 +27130,7 @@ var init_HealthBar = __esm({
|
|
|
27062
27130
|
}
|
|
27063
27131
|
});
|
|
27064
27132
|
function ScoreDisplay({
|
|
27133
|
+
assetUrl = DEFAULT_ASSET_URL6,
|
|
27065
27134
|
value,
|
|
27066
27135
|
label,
|
|
27067
27136
|
icon,
|
|
@@ -27108,14 +27177,24 @@ function ScoreDisplay({
|
|
|
27108
27177
|
className
|
|
27109
27178
|
),
|
|
27110
27179
|
children: [
|
|
27111
|
-
|
|
27180
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
27181
|
+
"img",
|
|
27182
|
+
{
|
|
27183
|
+
src: assetUrl,
|
|
27184
|
+
alt: "",
|
|
27185
|
+
width: 20,
|
|
27186
|
+
height: 20,
|
|
27187
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
27188
|
+
className: "flex-shrink-0"
|
|
27189
|
+
}
|
|
27190
|
+
) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0", children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon }) }) : null,
|
|
27112
27191
|
label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: label }),
|
|
27113
27192
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "tabular-nums", children: formattedValue })
|
|
27114
27193
|
]
|
|
27115
27194
|
}
|
|
27116
27195
|
);
|
|
27117
27196
|
}
|
|
27118
|
-
var sizeMap10;
|
|
27197
|
+
var sizeMap10, DEFAULT_ASSET_URL6;
|
|
27119
27198
|
var init_ScoreDisplay = __esm({
|
|
27120
27199
|
"components/game/atoms/ScoreDisplay.tsx"() {
|
|
27121
27200
|
"use client";
|
|
@@ -27127,10 +27206,12 @@ var init_ScoreDisplay = __esm({
|
|
|
27127
27206
|
lg: "text-2xl",
|
|
27128
27207
|
xl: "text-4xl"
|
|
27129
27208
|
};
|
|
27209
|
+
DEFAULT_ASSET_URL6 = "https://almadar-kflow-assets.web.app/shared/effects/particles/star_01.png";
|
|
27130
27210
|
ScoreDisplay.displayName = "ScoreDisplay";
|
|
27131
27211
|
}
|
|
27132
27212
|
});
|
|
27133
27213
|
function StatBadge({
|
|
27214
|
+
assetUrl = DEFAULT_ASSET_URL7,
|
|
27134
27215
|
label,
|
|
27135
27216
|
value = 0,
|
|
27136
27217
|
max,
|
|
@@ -27154,7 +27235,17 @@ function StatBadge({
|
|
|
27154
27235
|
className
|
|
27155
27236
|
),
|
|
27156
27237
|
children: [
|
|
27157
|
-
|
|
27238
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
27239
|
+
"img",
|
|
27240
|
+
{
|
|
27241
|
+
src: assetUrl,
|
|
27242
|
+
alt: "",
|
|
27243
|
+
width: 16,
|
|
27244
|
+
height: 16,
|
|
27245
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
27246
|
+
className: "flex-shrink-0"
|
|
27247
|
+
}
|
|
27248
|
+
) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-shrink-0 text-lg", children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon, className: "w-4 h-4" }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon, className: "w-4 h-4" }) }) : null,
|
|
27158
27249
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground font-medium", children: label }),
|
|
27159
27250
|
format === "hearts" && max && /* @__PURE__ */ jsxRuntime.jsx(
|
|
27160
27251
|
HealthBar,
|
|
@@ -27187,7 +27278,7 @@ function StatBadge({
|
|
|
27187
27278
|
}
|
|
27188
27279
|
);
|
|
27189
27280
|
}
|
|
27190
|
-
var sizeMap11, variantMap2;
|
|
27281
|
+
var sizeMap11, variantMap2, DEFAULT_ASSET_URL7;
|
|
27191
27282
|
var init_StatBadge = __esm({
|
|
27192
27283
|
"components/game/molecules/StatBadge.tsx"() {
|
|
27193
27284
|
init_cn();
|
|
@@ -27206,6 +27297,7 @@ var init_StatBadge = __esm({
|
|
|
27206
27297
|
warning: "bg-warning/15 border-warning/40 text-foreground",
|
|
27207
27298
|
danger: "bg-error/15 border-error/40 text-foreground"
|
|
27208
27299
|
};
|
|
27300
|
+
DEFAULT_ASSET_URL7 = "https://almadar-kflow-assets.web.app/shared/effects/particles/light_01.png";
|
|
27209
27301
|
StatBadge.displayName = "StatBadge";
|
|
27210
27302
|
}
|
|
27211
27303
|
});
|
|
@@ -27289,6 +27381,7 @@ var init_InventoryGrid = __esm({
|
|
|
27289
27381
|
}
|
|
27290
27382
|
});
|
|
27291
27383
|
function WaypointMarker({
|
|
27384
|
+
assetUrl = DEFAULT_ASSET_URL8,
|
|
27292
27385
|
label,
|
|
27293
27386
|
icon,
|
|
27294
27387
|
active = true,
|
|
@@ -27327,7 +27420,17 @@ function WaypointMarker({
|
|
|
27327
27420
|
active && !completed && "bg-info text-foreground",
|
|
27328
27421
|
!active && !completed && "bg-muted"
|
|
27329
27422
|
),
|
|
27330
|
-
children: completed ? checkIcon :
|
|
27423
|
+
children: completed ? checkIcon : assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
27424
|
+
"img",
|
|
27425
|
+
{
|
|
27426
|
+
src: assetUrl,
|
|
27427
|
+
alt: label,
|
|
27428
|
+
width: sizes.img,
|
|
27429
|
+
height: sizes.img,
|
|
27430
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
27431
|
+
className: "flex-shrink-0"
|
|
27432
|
+
}
|
|
27433
|
+
) : icon ? typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon }) : null
|
|
27331
27434
|
}
|
|
27332
27435
|
)
|
|
27333
27436
|
] }),
|
|
@@ -27344,15 +27447,16 @@ function WaypointMarker({
|
|
|
27344
27447
|
)
|
|
27345
27448
|
] });
|
|
27346
27449
|
}
|
|
27347
|
-
var sizeMap12, checkIcon;
|
|
27450
|
+
var DEFAULT_ASSET_URL8, sizeMap12, checkIcon;
|
|
27348
27451
|
var init_WaypointMarker = __esm({
|
|
27349
27452
|
"components/game/atoms/WaypointMarker.tsx"() {
|
|
27350
27453
|
init_cn();
|
|
27351
27454
|
init_Icon();
|
|
27455
|
+
DEFAULT_ASSET_URL8 = "https://almadar-kflow-assets.web.app/shared/world-map/battle_marker.png";
|
|
27352
27456
|
sizeMap12 = {
|
|
27353
|
-
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1" },
|
|
27354
|
-
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5" },
|
|
27355
|
-
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2" }
|
|
27457
|
+
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1", img: 12 },
|
|
27458
|
+
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5", img: 18 },
|
|
27459
|
+
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2", img: 24 }
|
|
27356
27460
|
};
|
|
27357
27461
|
checkIcon = /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 3, className: "w-3/5 h-3/5", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 13l4 4L19 7", strokeLinecap: "round", strokeLinejoin: "round" }) });
|
|
27358
27462
|
WaypointMarker.displayName = "WaypointMarker";
|
|
@@ -27866,6 +27970,7 @@ var init_ResourceBar = __esm({
|
|
|
27866
27970
|
}
|
|
27867
27971
|
});
|
|
27868
27972
|
function TurnIndicator({
|
|
27973
|
+
assetUrl = DEFAULT_ASSET_URL9,
|
|
27869
27974
|
currentTurn = 1,
|
|
27870
27975
|
maxTurns,
|
|
27871
27976
|
activeTeam,
|
|
@@ -27898,14 +28003,24 @@ function TurnIndicator({
|
|
|
27898
28003
|
] }),
|
|
27899
28004
|
activeTeam && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
27900
28005
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "|" }),
|
|
27901
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28006
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
28007
|
+
"img",
|
|
28008
|
+
{
|
|
28009
|
+
src: assetUrl,
|
|
28010
|
+
alt: "",
|
|
28011
|
+
width: 12,
|
|
28012
|
+
height: 12,
|
|
28013
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
28014
|
+
className: "flex-shrink-0"
|
|
28015
|
+
}
|
|
28016
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("rounded-full bg-success/20", sizes.dot) }),
|
|
27902
28017
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-success", children: activeTeam })
|
|
27903
28018
|
] })
|
|
27904
28019
|
]
|
|
27905
28020
|
}
|
|
27906
28021
|
);
|
|
27907
28022
|
}
|
|
27908
|
-
var sizeMap14;
|
|
28023
|
+
var sizeMap14, DEFAULT_ASSET_URL9;
|
|
27909
28024
|
var init_TurnIndicator = __esm({
|
|
27910
28025
|
"components/game/atoms/TurnIndicator.tsx"() {
|
|
27911
28026
|
init_cn();
|
|
@@ -27914,6 +28029,7 @@ var init_TurnIndicator = __esm({
|
|
|
27914
28029
|
md: { wrapper: "text-sm gap-2 px-3 py-1", dot: "w-2 h-2" },
|
|
27915
28030
|
lg: { wrapper: "text-base gap-2.5 px-4 py-1.5", dot: "w-2.5 h-2.5" }
|
|
27916
28031
|
};
|
|
28032
|
+
DEFAULT_ASSET_URL9 = "https://almadar-kflow-assets.web.app/shared/effects/particles/symbol_01.png";
|
|
27917
28033
|
TurnIndicator.displayName = "TurnIndicator";
|
|
27918
28034
|
}
|
|
27919
28035
|
});
|
|
@@ -27984,6 +28100,7 @@ var init_TurnPanel = __esm({
|
|
|
27984
28100
|
}
|
|
27985
28101
|
});
|
|
27986
28102
|
function EnemyPlate({
|
|
28103
|
+
assetUrl = DEFAULT_ASSET_URL10,
|
|
27987
28104
|
name = "Shadow Guard",
|
|
27988
28105
|
health = 80,
|
|
27989
28106
|
maxHealth = 100,
|
|
@@ -28001,15 +28118,28 @@ function EnemyPlate({
|
|
|
28001
28118
|
),
|
|
28002
28119
|
children: [
|
|
28003
28120
|
/* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "flex items-center justify-between gap-2", children: [
|
|
28004
|
-
/* @__PURE__ */ jsxRuntime.
|
|
28005
|
-
|
|
28006
|
-
|
|
28007
|
-
|
|
28008
|
-
|
|
28009
|
-
|
|
28010
|
-
|
|
28011
|
-
|
|
28012
|
-
|
|
28121
|
+
/* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "flex items-center gap-1.5 min-w-0", children: [
|
|
28122
|
+
assetUrl && /* @__PURE__ */ jsxRuntime.jsx(
|
|
28123
|
+
"img",
|
|
28124
|
+
{
|
|
28125
|
+
src: assetUrl,
|
|
28126
|
+
alt: name,
|
|
28127
|
+
width: 24,
|
|
28128
|
+
height: 24,
|
|
28129
|
+
style: { objectFit: "cover", borderRadius: "50%" },
|
|
28130
|
+
className: "flex-shrink-0"
|
|
28131
|
+
}
|
|
28132
|
+
),
|
|
28133
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
28134
|
+
exports.Typography,
|
|
28135
|
+
{
|
|
28136
|
+
variant: "caption",
|
|
28137
|
+
weight: "bold",
|
|
28138
|
+
className: "text-[var(--color-foreground)] truncate",
|
|
28139
|
+
children: name
|
|
28140
|
+
}
|
|
28141
|
+
)
|
|
28142
|
+
] }),
|
|
28013
28143
|
level != null && /* @__PURE__ */ jsxRuntime.jsxs(exports.Badge, { variant: "neutral", size: "sm", children: [
|
|
28014
28144
|
"Lv.",
|
|
28015
28145
|
level
|
|
@@ -28054,7 +28184,7 @@ function EnemyPlate({
|
|
|
28054
28184
|
}
|
|
28055
28185
|
);
|
|
28056
28186
|
}
|
|
28057
|
-
var effectVariantMap2, DEFAULT_ENEMY_EFFECTS;
|
|
28187
|
+
var effectVariantMap2, DEFAULT_ENEMY_EFFECTS, DEFAULT_ASSET_URL10;
|
|
28058
28188
|
var init_EnemyPlate = __esm({
|
|
28059
28189
|
"components/game/molecules/EnemyPlate.tsx"() {
|
|
28060
28190
|
"use client";
|
|
@@ -28071,6 +28201,7 @@ var init_EnemyPlate = __esm({
|
|
|
28071
28201
|
DEFAULT_ENEMY_EFFECTS = [
|
|
28072
28202
|
{ icon: "flame", label: "Burn", variant: "debuff" }
|
|
28073
28203
|
];
|
|
28204
|
+
DEFAULT_ASSET_URL10 = "https://almadar-kflow-assets.web.app/shared/portraits/shadow-legion.png";
|
|
28074
28205
|
EnemyPlate.displayName = "EnemyPlate";
|
|
28075
28206
|
}
|
|
28076
28207
|
});
|
|
@@ -29624,13 +29755,13 @@ var init_MapView = __esm({
|
|
|
29624
29755
|
shadowSize: [41, 41]
|
|
29625
29756
|
});
|
|
29626
29757
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
29627
|
-
const { useEffect:
|
|
29758
|
+
const { useEffect: useEffect80, useRef: useRef77, useCallback: useCallback120, useState: useState110 } = React77__namespace.default;
|
|
29628
29759
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
29629
|
-
const { useEventBus:
|
|
29760
|
+
const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
29630
29761
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
29631
29762
|
const map = useMap();
|
|
29632
|
-
const prevRef =
|
|
29633
|
-
|
|
29763
|
+
const prevRef = useRef77({ centerLat, centerLng, zoom });
|
|
29764
|
+
useEffect80(() => {
|
|
29634
29765
|
const prev = prevRef.current;
|
|
29635
29766
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
29636
29767
|
map.setView([centerLat, centerLng], zoom);
|
|
@@ -29641,7 +29772,7 @@ var init_MapView = __esm({
|
|
|
29641
29772
|
}
|
|
29642
29773
|
function MapClickHandler({ onMapClick }) {
|
|
29643
29774
|
const map = useMap();
|
|
29644
|
-
|
|
29775
|
+
useEffect80(() => {
|
|
29645
29776
|
if (!onMapClick) return;
|
|
29646
29777
|
const handler = (e) => {
|
|
29647
29778
|
onMapClick(e.latlng.lat, e.latlng.lng);
|
|
@@ -29668,9 +29799,9 @@ var init_MapView = __esm({
|
|
|
29668
29799
|
className,
|
|
29669
29800
|
showAttribution = true
|
|
29670
29801
|
}) {
|
|
29671
|
-
const eventBus =
|
|
29672
|
-
const [clickedPosition, setClickedPosition] =
|
|
29673
|
-
const handleMapClick =
|
|
29802
|
+
const eventBus = useEventBus3();
|
|
29803
|
+
const [clickedPosition, setClickedPosition] = useState110(null);
|
|
29804
|
+
const handleMapClick = useCallback120((lat, lng) => {
|
|
29674
29805
|
if (showClickedPin) {
|
|
29675
29806
|
setClickedPosition({ lat, lng });
|
|
29676
29807
|
}
|
|
@@ -29679,7 +29810,7 @@ var init_MapView = __esm({
|
|
|
29679
29810
|
eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
|
|
29680
29811
|
}
|
|
29681
29812
|
}, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
|
|
29682
|
-
const handleMarkerClick =
|
|
29813
|
+
const handleMarkerClick = useCallback120((marker) => {
|
|
29683
29814
|
onMarkerClick?.(marker);
|
|
29684
29815
|
if (markerClickEvent) {
|
|
29685
29816
|
eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
|
|
@@ -36806,10 +36937,10 @@ var init_Section = __esm({
|
|
|
36806
36937
|
children,
|
|
36807
36938
|
headerClassName,
|
|
36808
36939
|
contentClassName,
|
|
36809
|
-
as:
|
|
36940
|
+
as: Component2 = "section"
|
|
36810
36941
|
}) => {
|
|
36811
36942
|
const hasHeader = title || description || action;
|
|
36812
|
-
const Comp =
|
|
36943
|
+
const Comp = Component2;
|
|
36813
36944
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
36814
36945
|
Comp,
|
|
36815
36946
|
{
|
|
@@ -39122,44 +39253,27 @@ function DebuggerBoard({
|
|
|
39122
39253
|
const { t } = hooks.useTranslate();
|
|
39123
39254
|
const resolved = boardEntity(entity);
|
|
39124
39255
|
const [headerError, setHeaderError] = React77.useState(false);
|
|
39125
|
-
const [showHint, setShowHint] = React77.useState(false);
|
|
39126
39256
|
const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
|
|
39127
|
-
const result = resolved?.result
|
|
39257
|
+
const result = str(resolved?.result) || "none";
|
|
39128
39258
|
const attempts = num(resolved?.attempts);
|
|
39129
|
-
const submitted = result
|
|
39259
|
+
const submitted = result === "win";
|
|
39260
|
+
const showHint = !submitted && attempts >= 2 && Boolean(str(resolved?.hint));
|
|
39130
39261
|
const bugLines = lines.filter((l) => l.isBug);
|
|
39131
39262
|
const flaggedLines = lines.filter((l) => l.isFlagged);
|
|
39132
39263
|
const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
|
|
39133
39264
|
const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
|
|
39134
|
-
const allCorrect =
|
|
39265
|
+
const allCorrect = submitted;
|
|
39135
39266
|
const toggleLine = (lineId) => {
|
|
39136
39267
|
if (submitted) return;
|
|
39137
|
-
if (toggleFlagEvent) {
|
|
39138
|
-
emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
39139
|
-
}
|
|
39268
|
+
if (toggleFlagEvent) emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
39140
39269
|
};
|
|
39141
39270
|
const handleSubmit = React77.useCallback(() => {
|
|
39142
|
-
if (checkEvent) {
|
|
39143
|
-
emit(`UI:${checkEvent}`, {});
|
|
39144
|
-
}
|
|
39271
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
39145
39272
|
const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
|
|
39146
|
-
if (correct) {
|
|
39147
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
39148
|
-
}
|
|
39273
|
+
if (correct) emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
39149
39274
|
}, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
|
|
39150
39275
|
const handleReset = () => {
|
|
39151
|
-
if (playAgainEvent) {
|
|
39152
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
39153
|
-
}
|
|
39154
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
39155
|
-
setShowHint(true);
|
|
39156
|
-
}
|
|
39157
|
-
};
|
|
39158
|
-
const handleFullReset = () => {
|
|
39159
|
-
if (playAgainEvent) {
|
|
39160
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
39161
|
-
}
|
|
39162
|
-
setShowHint(false);
|
|
39276
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
39163
39277
|
};
|
|
39164
39278
|
if (!resolved) return null;
|
|
39165
39279
|
const theme = resolved.theme ?? void 0;
|
|
@@ -39183,7 +39297,7 @@ function DebuggerBoard({
|
|
|
39183
39297
|
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
|
|
39184
39298
|
] }),
|
|
39185
39299
|
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", children: str(resolved.description) }),
|
|
39186
|
-
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(
|
|
39300
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(lines.filter((l) => l.isBug).length) }) })
|
|
39187
39301
|
] }) }),
|
|
39188
39302
|
/* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(exports.VStack, { gap: "none", children: lines.map((line, i) => {
|
|
39189
39303
|
const isFlagged = !!line.isFlagged;
|
|
@@ -39233,11 +39347,11 @@ function DebuggerBoard({
|
|
|
39233
39347
|
] }) }),
|
|
39234
39348
|
showHint && hint && /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body", children: hint }) }),
|
|
39235
39349
|
/* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", justify: "center", children: [
|
|
39236
|
-
!submitted
|
|
39350
|
+
!submitted && /* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
|
|
39237
39351
|
/* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.Send, size: "sm" }),
|
|
39238
39352
|
t("debugger.submit")
|
|
39239
|
-
] })
|
|
39240
|
-
/* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "secondary", onClick:
|
|
39353
|
+
] }),
|
|
39354
|
+
/* @__PURE__ */ jsxRuntime.jsxs(exports.Button, { variant: "secondary", onClick: handleReset, children: [
|
|
39241
39355
|
/* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: LucideIcons2.RotateCcw, size: "sm" }),
|
|
39242
39356
|
t("debugger.reset")
|
|
39243
39357
|
] })
|
|
@@ -39827,6 +39941,7 @@ var init_DrawerSlot = __esm({
|
|
|
39827
39941
|
}
|
|
39828
39942
|
});
|
|
39829
39943
|
function StateIndicator({
|
|
39944
|
+
assetUrl = DEFAULT_ASSET_URL11,
|
|
39830
39945
|
state = "idle",
|
|
39831
39946
|
label,
|
|
39832
39947
|
size = "md",
|
|
@@ -39849,18 +39964,29 @@ function StateIndicator({
|
|
|
39849
39964
|
className
|
|
39850
39965
|
),
|
|
39851
39966
|
children: [
|
|
39852
|
-
/* @__PURE__ */ jsxRuntime.jsx(exports.Box, { as: "span", children:
|
|
39967
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Box, { as: "span", children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
39968
|
+
"img",
|
|
39969
|
+
{
|
|
39970
|
+
src: assetUrl,
|
|
39971
|
+
alt: displayLabel,
|
|
39972
|
+
width: 16,
|
|
39973
|
+
height: 16,
|
|
39974
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
39975
|
+
className: "flex-shrink-0"
|
|
39976
|
+
}
|
|
39977
|
+
) : typeof config.icon === "string" ? /^[a-zA-Z0-9-]+$/.test(config.icon) ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: config.icon }) : config.icon : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon: config.icon }) }),
|
|
39853
39978
|
/* @__PURE__ */ jsxRuntime.jsx(exports.Box, { as: "span", children: displayLabel })
|
|
39854
39979
|
]
|
|
39855
39980
|
}
|
|
39856
39981
|
);
|
|
39857
39982
|
}
|
|
39858
|
-
var DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
39983
|
+
var DEFAULT_ASSET_URL11, DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
39859
39984
|
var init_StateIndicator = __esm({
|
|
39860
39985
|
"components/game/atoms/StateIndicator.tsx"() {
|
|
39861
39986
|
init_Box();
|
|
39862
39987
|
init_Icon();
|
|
39863
39988
|
init_cn();
|
|
39989
|
+
DEFAULT_ASSET_URL11 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
39864
39990
|
DEFAULT_STATE_STYLES = {
|
|
39865
39991
|
idle: { icon: "\u23F8", bgClass: "bg-muted" },
|
|
39866
39992
|
active: { icon: "\u25B6", bgClass: "bg-success" },
|
|
@@ -40334,39 +40460,40 @@ function EventHandlerBoard({
|
|
|
40334
40460
|
stepDurationMs = 800,
|
|
40335
40461
|
playEvent,
|
|
40336
40462
|
completeEvent,
|
|
40463
|
+
editRuleEvent,
|
|
40464
|
+
playAgainEvent,
|
|
40337
40465
|
className
|
|
40338
40466
|
}) {
|
|
40339
40467
|
const { emit } = useEventBus();
|
|
40340
40468
|
const { t } = hooks.useTranslate();
|
|
40341
40469
|
const resolved = boardEntity(entity);
|
|
40342
|
-
const
|
|
40343
|
-
const
|
|
40470
|
+
const objects = rows(resolved?.objects);
|
|
40471
|
+
const entityResult = str(resolved?.result) || "none";
|
|
40472
|
+
const isSuccess = entityResult === "win";
|
|
40473
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
40344
40474
|
const [selectedObjectId, setSelectedObjectId] = React77.useState(
|
|
40345
|
-
|
|
40475
|
+
objects[0] ? objId(objects[0]) : null
|
|
40346
40476
|
);
|
|
40347
40477
|
const [headerError, setHeaderError] = React77.useState(false);
|
|
40348
|
-
const [
|
|
40478
|
+
const [isPlaying, setIsPlaying] = React77.useState(false);
|
|
40349
40479
|
const [eventLog, setEventLog] = React77.useState([]);
|
|
40350
|
-
const [attempts, setAttempts] = React77.useState(0);
|
|
40351
40480
|
const timerRef = React77.useRef(null);
|
|
40352
40481
|
const logIdCounter = React77.useRef(0);
|
|
40353
40482
|
React77.useEffect(() => () => {
|
|
40354
40483
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
40355
40484
|
}, []);
|
|
40356
|
-
const selectedObject = objects.find((o) => objId(o) === selectedObjectId)
|
|
40485
|
+
const selectedObject = objects.find((o) => objId(o) === selectedObjectId) ?? null;
|
|
40357
40486
|
const handleRulesChange = React77.useCallback((objectId, rules) => {
|
|
40358
|
-
|
|
40359
|
-
|
|
40360
|
-
));
|
|
40361
|
-
}, []);
|
|
40487
|
+
if (editRuleEvent) emit(`UI:${editRuleEvent}`, { objectId, rules });
|
|
40488
|
+
}, [editRuleEvent, emit]);
|
|
40362
40489
|
const addLogEntry = React77.useCallback((icon, message, status = "done") => {
|
|
40363
40490
|
const id = `log-${logIdCounter.current++}`;
|
|
40364
40491
|
setEventLog((prev) => [...prev, { id, timestamp: Date.now(), icon, message, status }]);
|
|
40365
40492
|
}, []);
|
|
40366
40493
|
const handlePlay = React77.useCallback(() => {
|
|
40367
|
-
if (
|
|
40494
|
+
if (isPlaying || isSuccess) return;
|
|
40368
40495
|
if (playEvent) emit(`UI:${playEvent}`, {});
|
|
40369
|
-
|
|
40496
|
+
setIsPlaying(true);
|
|
40370
40497
|
setEventLog([]);
|
|
40371
40498
|
const allRules = [];
|
|
40372
40499
|
objects.forEach((obj) => {
|
|
@@ -40382,15 +40509,8 @@ function EventHandlerBoard({
|
|
|
40382
40509
|
let goalReached = false;
|
|
40383
40510
|
const processNext = () => {
|
|
40384
40511
|
if (eventQueue.length === 0 || stepIdx > 20) {
|
|
40385
|
-
|
|
40386
|
-
|
|
40387
|
-
if (completeEvent) {
|
|
40388
|
-
emit(`UI:${completeEvent}`, { success: true });
|
|
40389
|
-
}
|
|
40390
|
-
} else {
|
|
40391
|
-
setAttempts((prev) => prev + 1);
|
|
40392
|
-
setPlayState("fail");
|
|
40393
|
-
}
|
|
40512
|
+
setIsPlaying(false);
|
|
40513
|
+
if (goalReached && completeEvent) emit(`UI:${completeEvent}`, { success: true });
|
|
40394
40514
|
return;
|
|
40395
40515
|
}
|
|
40396
40516
|
const currentEvent = eventQueue.shift();
|
|
@@ -40421,21 +40541,19 @@ function EventHandlerBoard({
|
|
|
40421
40541
|
addLogEntry("\u{1F3AC}", t("eventHandler.simulationStarted", { events: triggers.join(", ") }), "active");
|
|
40422
40542
|
}
|
|
40423
40543
|
timerRef.current = setTimeout(processNext, stepDurationMs);
|
|
40424
|
-
}, [
|
|
40544
|
+
}, [isPlaying, isSuccess, objects, resolved, stepDurationMs, playEvent, completeEvent, emit, addLogEntry, t]);
|
|
40425
40545
|
const handleTryAgain = React77.useCallback(() => {
|
|
40426
40546
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
40427
|
-
|
|
40547
|
+
setIsPlaying(false);
|
|
40428
40548
|
setEventLog([]);
|
|
40429
40549
|
}, []);
|
|
40430
40550
|
const handleReset = React77.useCallback(() => {
|
|
40431
40551
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
40432
|
-
|
|
40433
|
-
setObjects([...resetObjects]);
|
|
40434
|
-
setPlayState("editing");
|
|
40552
|
+
setIsPlaying(false);
|
|
40435
40553
|
setEventLog([]);
|
|
40436
|
-
setSelectedObjectId(
|
|
40437
|
-
|
|
40438
|
-
}, [
|
|
40554
|
+
setSelectedObjectId(objects[0] ? objId(objects[0]) : null);
|
|
40555
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
40556
|
+
}, [objects, playAgainEvent, emit]);
|
|
40439
40557
|
if (!resolved) return null;
|
|
40440
40558
|
const objectViewers = objects.map((obj) => {
|
|
40441
40559
|
const states = objStates(obj);
|
|
@@ -40504,12 +40622,12 @@ function EventHandlerBoard({
|
|
|
40504
40622
|
{
|
|
40505
40623
|
object: selectedObject,
|
|
40506
40624
|
onRulesChange: handleRulesChange,
|
|
40507
|
-
disabled:
|
|
40625
|
+
disabled: isPlaying
|
|
40508
40626
|
}
|
|
40509
40627
|
),
|
|
40510
40628
|
eventLog.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(EventLog, { entries: eventLog }),
|
|
40511
|
-
|
|
40512
|
-
|
|
40629
|
+
isSuccess && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("eventHandler.chainComplete") }) }),
|
|
40630
|
+
!isPlaying && !isSuccess && attempts > 0 && /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", children: [
|
|
40513
40631
|
/* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body1", className: "text-foreground font-medium", children: t(encourageKey) }) }),
|
|
40514
40632
|
showHint && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { className: "items-start", gap: "xs", children: [
|
|
40515
40633
|
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
@@ -40517,12 +40635,12 @@ function EventHandlerBoard({
|
|
|
40517
40635
|
] }) })
|
|
40518
40636
|
] }),
|
|
40519
40637
|
/* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", children: [
|
|
40520
|
-
|
|
40638
|
+
!isPlaying && !isSuccess && attempts > 0 ? /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
40521
40639
|
exports.Button,
|
|
40522
40640
|
{
|
|
40523
40641
|
variant: "primary",
|
|
40524
40642
|
onClick: handlePlay,
|
|
40525
|
-
disabled:
|
|
40643
|
+
disabled: isPlaying || isSuccess,
|
|
40526
40644
|
children: "\u25B6 " + t("game.play")
|
|
40527
40645
|
}
|
|
40528
40646
|
),
|
|
@@ -41714,6 +41832,1568 @@ var init_GameAudioToggle = __esm({
|
|
|
41714
41832
|
GameAudioToggle.displayName = "GameAudioToggle";
|
|
41715
41833
|
}
|
|
41716
41834
|
});
|
|
41835
|
+
function detectAssetRoot(modelUrl) {
|
|
41836
|
+
const idx = modelUrl.indexOf("/3d/");
|
|
41837
|
+
if (idx !== -1) {
|
|
41838
|
+
return modelUrl.substring(0, idx + 4);
|
|
41839
|
+
}
|
|
41840
|
+
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
41841
|
+
}
|
|
41842
|
+
function createGLTFLoaderForUrl(url) {
|
|
41843
|
+
const loader = new GLTFLoader_js.GLTFLoader();
|
|
41844
|
+
loader.setResourcePath(detectAssetRoot(url));
|
|
41845
|
+
return loader;
|
|
41846
|
+
}
|
|
41847
|
+
var AssetLoader;
|
|
41848
|
+
var init_AssetLoader = __esm({
|
|
41849
|
+
"components/game/molecules/three/loaders/AssetLoader.ts"() {
|
|
41850
|
+
AssetLoader = class {
|
|
41851
|
+
constructor() {
|
|
41852
|
+
__publicField(this, "objLoader");
|
|
41853
|
+
__publicField(this, "textureLoader");
|
|
41854
|
+
__publicField(this, "modelCache");
|
|
41855
|
+
__publicField(this, "textureCache");
|
|
41856
|
+
__publicField(this, "loadingPromises");
|
|
41857
|
+
this.objLoader = new OBJLoader_js.OBJLoader();
|
|
41858
|
+
this.textureLoader = new THREE__namespace.TextureLoader();
|
|
41859
|
+
this.modelCache = /* @__PURE__ */ new Map();
|
|
41860
|
+
this.textureCache = /* @__PURE__ */ new Map();
|
|
41861
|
+
this.loadingPromises = /* @__PURE__ */ new Map();
|
|
41862
|
+
}
|
|
41863
|
+
/**
|
|
41864
|
+
* Load a GLB/GLTF model
|
|
41865
|
+
* @param url - URL to the .glb or .gltf file
|
|
41866
|
+
* @returns Promise with loaded model scene and animations
|
|
41867
|
+
*/
|
|
41868
|
+
async loadModel(url) {
|
|
41869
|
+
if (this.modelCache.has(url)) {
|
|
41870
|
+
return this.modelCache.get(url);
|
|
41871
|
+
}
|
|
41872
|
+
if (this.loadingPromises.has(url)) {
|
|
41873
|
+
return this.loadingPromises.get(url);
|
|
41874
|
+
}
|
|
41875
|
+
const loader = createGLTFLoaderForUrl(url);
|
|
41876
|
+
const loadPromise = loader.loadAsync(url).then((gltf) => {
|
|
41877
|
+
const result = {
|
|
41878
|
+
scene: gltf.scene,
|
|
41879
|
+
animations: gltf.animations || []
|
|
41880
|
+
};
|
|
41881
|
+
this.modelCache.set(url, result);
|
|
41882
|
+
this.loadingPromises.delete(url);
|
|
41883
|
+
return result;
|
|
41884
|
+
}).catch((error) => {
|
|
41885
|
+
this.loadingPromises.delete(url);
|
|
41886
|
+
throw new Error(`Failed to load model ${url}: ${error.message}`);
|
|
41887
|
+
});
|
|
41888
|
+
this.loadingPromises.set(url, loadPromise);
|
|
41889
|
+
return loadPromise;
|
|
41890
|
+
}
|
|
41891
|
+
/**
|
|
41892
|
+
* Load an OBJ model (fallback for non-GLB assets)
|
|
41893
|
+
* @param url - URL to the .obj file
|
|
41894
|
+
* @returns Promise with loaded object group
|
|
41895
|
+
*/
|
|
41896
|
+
async loadOBJ(url) {
|
|
41897
|
+
if (this.modelCache.has(url)) {
|
|
41898
|
+
return this.modelCache.get(url).scene;
|
|
41899
|
+
}
|
|
41900
|
+
if (this.loadingPromises.has(url)) {
|
|
41901
|
+
const result = await this.loadingPromises.get(url);
|
|
41902
|
+
return result.scene;
|
|
41903
|
+
}
|
|
41904
|
+
const loadPromise = this.objLoader.loadAsync(url).then((group) => {
|
|
41905
|
+
const result = {
|
|
41906
|
+
scene: group,
|
|
41907
|
+
animations: []
|
|
41908
|
+
};
|
|
41909
|
+
this.modelCache.set(url, result);
|
|
41910
|
+
this.loadingPromises.delete(url);
|
|
41911
|
+
return result;
|
|
41912
|
+
}).catch((error) => {
|
|
41913
|
+
this.loadingPromises.delete(url);
|
|
41914
|
+
throw new Error(`Failed to load OBJ ${url}: ${error.message}`);
|
|
41915
|
+
});
|
|
41916
|
+
this.loadingPromises.set(url, loadPromise);
|
|
41917
|
+
return (await loadPromise).scene;
|
|
41918
|
+
}
|
|
41919
|
+
/**
|
|
41920
|
+
* Load a texture
|
|
41921
|
+
* @param url - URL to the texture image
|
|
41922
|
+
* @returns Promise with loaded texture
|
|
41923
|
+
*/
|
|
41924
|
+
async loadTexture(url) {
|
|
41925
|
+
if (this.textureCache.has(url)) {
|
|
41926
|
+
return this.textureCache.get(url);
|
|
41927
|
+
}
|
|
41928
|
+
if (this.loadingPromises.has(`texture:${url}`)) {
|
|
41929
|
+
return this.loadingPromises.get(`texture:${url}`);
|
|
41930
|
+
}
|
|
41931
|
+
const loadPromise = this.textureLoader.loadAsync(url).then((texture) => {
|
|
41932
|
+
texture.colorSpace = THREE__namespace.SRGBColorSpace;
|
|
41933
|
+
this.textureCache.set(url, texture);
|
|
41934
|
+
this.loadingPromises.delete(`texture:${url}`);
|
|
41935
|
+
return texture;
|
|
41936
|
+
}).catch((error) => {
|
|
41937
|
+
this.loadingPromises.delete(`texture:${url}`);
|
|
41938
|
+
throw new Error(`Failed to load texture ${url}: ${error.message}`);
|
|
41939
|
+
});
|
|
41940
|
+
this.loadingPromises.set(`texture:${url}`, loadPromise);
|
|
41941
|
+
return loadPromise;
|
|
41942
|
+
}
|
|
41943
|
+
/**
|
|
41944
|
+
* Preload multiple assets
|
|
41945
|
+
* @param urls - Array of asset URLs to preload
|
|
41946
|
+
* @returns Promise that resolves when all assets are loaded
|
|
41947
|
+
*/
|
|
41948
|
+
async preload(urls) {
|
|
41949
|
+
const promises = urls.map((url) => {
|
|
41950
|
+
if (url.endsWith(".glb") || url.endsWith(".gltf")) {
|
|
41951
|
+
return this.loadModel(url).catch(() => null);
|
|
41952
|
+
} else if (url.endsWith(".obj")) {
|
|
41953
|
+
return this.loadOBJ(url).catch(() => null);
|
|
41954
|
+
} else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
|
|
41955
|
+
return this.loadTexture(url).catch(() => null);
|
|
41956
|
+
}
|
|
41957
|
+
return Promise.resolve(null);
|
|
41958
|
+
});
|
|
41959
|
+
await Promise.all(promises);
|
|
41960
|
+
}
|
|
41961
|
+
/**
|
|
41962
|
+
* Check if a model is cached
|
|
41963
|
+
* @param url - Model URL
|
|
41964
|
+
*/
|
|
41965
|
+
hasModel(url) {
|
|
41966
|
+
return this.modelCache.has(url);
|
|
41967
|
+
}
|
|
41968
|
+
/**
|
|
41969
|
+
* Check if a texture is cached
|
|
41970
|
+
* @param url - Texture URL
|
|
41971
|
+
*/
|
|
41972
|
+
hasTexture(url) {
|
|
41973
|
+
return this.textureCache.has(url);
|
|
41974
|
+
}
|
|
41975
|
+
/**
|
|
41976
|
+
* Get cached model (throws if not cached)
|
|
41977
|
+
* @param url - Model URL
|
|
41978
|
+
*/
|
|
41979
|
+
getModel(url) {
|
|
41980
|
+
const model = this.modelCache.get(url);
|
|
41981
|
+
if (!model) {
|
|
41982
|
+
throw new Error(`Model ${url} not in cache`);
|
|
41983
|
+
}
|
|
41984
|
+
return model;
|
|
41985
|
+
}
|
|
41986
|
+
/**
|
|
41987
|
+
* Get cached texture (throws if not cached)
|
|
41988
|
+
* @param url - Texture URL
|
|
41989
|
+
*/
|
|
41990
|
+
getTexture(url) {
|
|
41991
|
+
const texture = this.textureCache.get(url);
|
|
41992
|
+
if (!texture) {
|
|
41993
|
+
throw new Error(`Texture ${url} not in cache`);
|
|
41994
|
+
}
|
|
41995
|
+
return texture;
|
|
41996
|
+
}
|
|
41997
|
+
/**
|
|
41998
|
+
* Clear all caches
|
|
41999
|
+
*/
|
|
42000
|
+
clearCache() {
|
|
42001
|
+
this.textureCache.forEach((texture) => {
|
|
42002
|
+
texture.dispose();
|
|
42003
|
+
});
|
|
42004
|
+
this.modelCache.forEach((model) => {
|
|
42005
|
+
model.scene.traverse((child) => {
|
|
42006
|
+
if (child instanceof THREE__namespace.Mesh) {
|
|
42007
|
+
child.geometry.dispose();
|
|
42008
|
+
if (Array.isArray(child.material)) {
|
|
42009
|
+
child.material.forEach((m) => m.dispose());
|
|
42010
|
+
} else {
|
|
42011
|
+
child.material.dispose();
|
|
42012
|
+
}
|
|
42013
|
+
}
|
|
42014
|
+
});
|
|
42015
|
+
});
|
|
42016
|
+
this.modelCache.clear();
|
|
42017
|
+
this.textureCache.clear();
|
|
42018
|
+
this.loadingPromises.clear();
|
|
42019
|
+
}
|
|
42020
|
+
/**
|
|
42021
|
+
* Get cache statistics
|
|
42022
|
+
*/
|
|
42023
|
+
getStats() {
|
|
42024
|
+
return {
|
|
42025
|
+
models: this.modelCache.size,
|
|
42026
|
+
textures: this.textureCache.size,
|
|
42027
|
+
loading: this.loadingPromises.size
|
|
42028
|
+
};
|
|
42029
|
+
}
|
|
42030
|
+
};
|
|
42031
|
+
new AssetLoader();
|
|
42032
|
+
}
|
|
42033
|
+
});
|
|
42034
|
+
function useAssetLoader(options = {}) {
|
|
42035
|
+
const { preloadUrls = [], loader: customLoader } = options;
|
|
42036
|
+
const loaderRef = React77.useRef(customLoader || new AssetLoader());
|
|
42037
|
+
const [state, setState] = React77.useState({
|
|
42038
|
+
isLoading: false,
|
|
42039
|
+
progress: 0,
|
|
42040
|
+
loaded: 0,
|
|
42041
|
+
total: 0,
|
|
42042
|
+
errors: []
|
|
42043
|
+
});
|
|
42044
|
+
React77.useEffect(() => {
|
|
42045
|
+
if (preloadUrls.length > 0) {
|
|
42046
|
+
preload(preloadUrls);
|
|
42047
|
+
}
|
|
42048
|
+
}, []);
|
|
42049
|
+
const updateProgress = React77.useCallback((loaded, total) => {
|
|
42050
|
+
setState((prev) => ({
|
|
42051
|
+
...prev,
|
|
42052
|
+
loaded,
|
|
42053
|
+
total,
|
|
42054
|
+
progress: total > 0 ? Math.round(loaded / total * 100) : 0
|
|
42055
|
+
}));
|
|
42056
|
+
}, []);
|
|
42057
|
+
const loadModel = React77.useCallback(
|
|
42058
|
+
async (url) => {
|
|
42059
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
42060
|
+
try {
|
|
42061
|
+
const model = await loaderRef.current.loadModel(url);
|
|
42062
|
+
setState((prev) => ({
|
|
42063
|
+
...prev,
|
|
42064
|
+
isLoading: false,
|
|
42065
|
+
loaded: prev.loaded + 1
|
|
42066
|
+
}));
|
|
42067
|
+
return model;
|
|
42068
|
+
} catch (error) {
|
|
42069
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
42070
|
+
setState((prev) => ({
|
|
42071
|
+
...prev,
|
|
42072
|
+
isLoading: false,
|
|
42073
|
+
errors: [...prev.errors, errorMsg]
|
|
42074
|
+
}));
|
|
42075
|
+
throw error;
|
|
42076
|
+
}
|
|
42077
|
+
},
|
|
42078
|
+
[]
|
|
42079
|
+
);
|
|
42080
|
+
const loadOBJ = React77.useCallback(
|
|
42081
|
+
async (url) => {
|
|
42082
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
42083
|
+
try {
|
|
42084
|
+
const model = await loaderRef.current.loadOBJ(url);
|
|
42085
|
+
setState((prev) => ({
|
|
42086
|
+
...prev,
|
|
42087
|
+
isLoading: false,
|
|
42088
|
+
loaded: prev.loaded + 1
|
|
42089
|
+
}));
|
|
42090
|
+
return model;
|
|
42091
|
+
} catch (error) {
|
|
42092
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
42093
|
+
setState((prev) => ({
|
|
42094
|
+
...prev,
|
|
42095
|
+
isLoading: false,
|
|
42096
|
+
errors: [...prev.errors, errorMsg]
|
|
42097
|
+
}));
|
|
42098
|
+
throw error;
|
|
42099
|
+
}
|
|
42100
|
+
},
|
|
42101
|
+
[]
|
|
42102
|
+
);
|
|
42103
|
+
const loadTexture = React77.useCallback(
|
|
42104
|
+
async (url) => {
|
|
42105
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
42106
|
+
try {
|
|
42107
|
+
const texture = await loaderRef.current.loadTexture(url);
|
|
42108
|
+
setState((prev) => ({
|
|
42109
|
+
...prev,
|
|
42110
|
+
isLoading: false,
|
|
42111
|
+
loaded: prev.loaded + 1
|
|
42112
|
+
}));
|
|
42113
|
+
return texture;
|
|
42114
|
+
} catch (error) {
|
|
42115
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
42116
|
+
setState((prev) => ({
|
|
42117
|
+
...prev,
|
|
42118
|
+
isLoading: false,
|
|
42119
|
+
errors: [...prev.errors, errorMsg]
|
|
42120
|
+
}));
|
|
42121
|
+
throw error;
|
|
42122
|
+
}
|
|
42123
|
+
},
|
|
42124
|
+
[]
|
|
42125
|
+
);
|
|
42126
|
+
const preload = React77.useCallback(
|
|
42127
|
+
async (urls) => {
|
|
42128
|
+
setState((prev) => ({
|
|
42129
|
+
...prev,
|
|
42130
|
+
isLoading: true,
|
|
42131
|
+
total: urls.length,
|
|
42132
|
+
loaded: 0,
|
|
42133
|
+
errors: []
|
|
42134
|
+
}));
|
|
42135
|
+
let completed = 0;
|
|
42136
|
+
const errors = [];
|
|
42137
|
+
await Promise.all(
|
|
42138
|
+
urls.map(async (url) => {
|
|
42139
|
+
try {
|
|
42140
|
+
if (url.endsWith(".glb") || url.endsWith(".gltf")) {
|
|
42141
|
+
await loaderRef.current.loadModel(url);
|
|
42142
|
+
} else if (url.endsWith(".obj")) {
|
|
42143
|
+
await loaderRef.current.loadOBJ(url);
|
|
42144
|
+
} else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
|
|
42145
|
+
await loaderRef.current.loadTexture(url);
|
|
42146
|
+
}
|
|
42147
|
+
completed++;
|
|
42148
|
+
updateProgress(completed, urls.length);
|
|
42149
|
+
} catch (error) {
|
|
42150
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
42151
|
+
errors.push(`${url}: ${errorMsg}`);
|
|
42152
|
+
completed++;
|
|
42153
|
+
updateProgress(completed, urls.length);
|
|
42154
|
+
}
|
|
42155
|
+
})
|
|
42156
|
+
);
|
|
42157
|
+
setState((prev) => ({
|
|
42158
|
+
...prev,
|
|
42159
|
+
isLoading: false,
|
|
42160
|
+
errors
|
|
42161
|
+
}));
|
|
42162
|
+
},
|
|
42163
|
+
[updateProgress]
|
|
42164
|
+
);
|
|
42165
|
+
const hasModel = React77.useCallback((url) => {
|
|
42166
|
+
return loaderRef.current.hasModel(url);
|
|
42167
|
+
}, []);
|
|
42168
|
+
const hasTexture = React77.useCallback((url) => {
|
|
42169
|
+
return loaderRef.current.hasTexture(url);
|
|
42170
|
+
}, []);
|
|
42171
|
+
const getModel = React77.useCallback((url) => {
|
|
42172
|
+
try {
|
|
42173
|
+
return loaderRef.current.getModel(url);
|
|
42174
|
+
} catch {
|
|
42175
|
+
return void 0;
|
|
42176
|
+
}
|
|
42177
|
+
}, []);
|
|
42178
|
+
const getTexture = React77.useCallback((url) => {
|
|
42179
|
+
try {
|
|
42180
|
+
return loaderRef.current.getTexture(url);
|
|
42181
|
+
} catch {
|
|
42182
|
+
return void 0;
|
|
42183
|
+
}
|
|
42184
|
+
}, []);
|
|
42185
|
+
const clearCache = React77.useCallback(() => {
|
|
42186
|
+
loaderRef.current.clearCache();
|
|
42187
|
+
setState({
|
|
42188
|
+
isLoading: false,
|
|
42189
|
+
progress: 0,
|
|
42190
|
+
loaded: 0,
|
|
42191
|
+
total: 0,
|
|
42192
|
+
errors: []
|
|
42193
|
+
});
|
|
42194
|
+
}, []);
|
|
42195
|
+
return {
|
|
42196
|
+
...state,
|
|
42197
|
+
loadModel,
|
|
42198
|
+
loadOBJ,
|
|
42199
|
+
loadTexture,
|
|
42200
|
+
preload,
|
|
42201
|
+
hasModel,
|
|
42202
|
+
hasTexture,
|
|
42203
|
+
getModel,
|
|
42204
|
+
getTexture,
|
|
42205
|
+
clearCache
|
|
42206
|
+
};
|
|
42207
|
+
}
|
|
42208
|
+
var init_useAssetLoader = __esm({
|
|
42209
|
+
"components/game/molecules/three/hooks/useAssetLoader.ts"() {
|
|
42210
|
+
"use client";
|
|
42211
|
+
init_AssetLoader();
|
|
42212
|
+
}
|
|
42213
|
+
});
|
|
42214
|
+
function useGameCanvas3DEvents(options) {
|
|
42215
|
+
const {
|
|
42216
|
+
tileClickEvent,
|
|
42217
|
+
unitClickEvent,
|
|
42218
|
+
featureClickEvent,
|
|
42219
|
+
canvasClickEvent,
|
|
42220
|
+
tileHoverEvent,
|
|
42221
|
+
tileLeaveEvent,
|
|
42222
|
+
unitAnimationEvent,
|
|
42223
|
+
cameraChangeEvent,
|
|
42224
|
+
onTileClick,
|
|
42225
|
+
onUnitClick,
|
|
42226
|
+
onFeatureClick,
|
|
42227
|
+
onCanvasClick,
|
|
42228
|
+
onTileHover,
|
|
42229
|
+
onUnitAnimation
|
|
42230
|
+
} = options;
|
|
42231
|
+
const emit = useEmitEvent();
|
|
42232
|
+
const optionsRef = React77.useRef(options);
|
|
42233
|
+
optionsRef.current = options;
|
|
42234
|
+
const handleTileClick = React77.useCallback(
|
|
42235
|
+
(tile, event) => {
|
|
42236
|
+
if (tileClickEvent) {
|
|
42237
|
+
emit(tileClickEvent, {
|
|
42238
|
+
tileId: tile.id,
|
|
42239
|
+
x: tile.x,
|
|
42240
|
+
z: tile.z ?? tile.y ?? 0,
|
|
42241
|
+
type: tile.type,
|
|
42242
|
+
terrain: tile.terrain,
|
|
42243
|
+
elevation: tile.elevation
|
|
42244
|
+
});
|
|
42245
|
+
}
|
|
42246
|
+
optionsRef.current.onTileClick?.(tile, event);
|
|
42247
|
+
},
|
|
42248
|
+
[tileClickEvent, emit]
|
|
42249
|
+
);
|
|
42250
|
+
const handleUnitClick = React77.useCallback(
|
|
42251
|
+
(unit, event) => {
|
|
42252
|
+
if (unitClickEvent) {
|
|
42253
|
+
emit(unitClickEvent, {
|
|
42254
|
+
unitId: unit.id,
|
|
42255
|
+
x: unit.x,
|
|
42256
|
+
z: unit.z ?? unit.y ?? 0,
|
|
42257
|
+
unitType: unit.unitType,
|
|
42258
|
+
name: unit.name,
|
|
42259
|
+
team: unit.team,
|
|
42260
|
+
faction: unit.faction,
|
|
42261
|
+
health: unit.health,
|
|
42262
|
+
maxHealth: unit.maxHealth
|
|
42263
|
+
});
|
|
42264
|
+
}
|
|
42265
|
+
optionsRef.current.onUnitClick?.(unit, event);
|
|
42266
|
+
},
|
|
42267
|
+
[unitClickEvent, emit]
|
|
42268
|
+
);
|
|
42269
|
+
const handleFeatureClick = React77.useCallback(
|
|
42270
|
+
(feature, event) => {
|
|
42271
|
+
if (featureClickEvent) {
|
|
42272
|
+
emit(featureClickEvent, {
|
|
42273
|
+
featureId: feature.id,
|
|
42274
|
+
x: feature.x,
|
|
42275
|
+
z: feature.z ?? feature.y ?? 0,
|
|
42276
|
+
type: feature.type,
|
|
42277
|
+
elevation: feature.elevation
|
|
42278
|
+
});
|
|
42279
|
+
}
|
|
42280
|
+
optionsRef.current.onFeatureClick?.(feature, event);
|
|
42281
|
+
},
|
|
42282
|
+
[featureClickEvent, emit]
|
|
42283
|
+
);
|
|
42284
|
+
const handleCanvasClick = React77.useCallback(
|
|
42285
|
+
(event) => {
|
|
42286
|
+
if (canvasClickEvent) {
|
|
42287
|
+
emit(canvasClickEvent, {
|
|
42288
|
+
clientX: event.clientX,
|
|
42289
|
+
clientY: event.clientY,
|
|
42290
|
+
button: event.button
|
|
42291
|
+
});
|
|
42292
|
+
}
|
|
42293
|
+
optionsRef.current.onCanvasClick?.(event);
|
|
42294
|
+
},
|
|
42295
|
+
[canvasClickEvent, emit]
|
|
42296
|
+
);
|
|
42297
|
+
const handleTileHover = React77.useCallback(
|
|
42298
|
+
(tile, event) => {
|
|
42299
|
+
if (tile) {
|
|
42300
|
+
if (tileHoverEvent) {
|
|
42301
|
+
emit(tileHoverEvent, {
|
|
42302
|
+
tileId: tile.id,
|
|
42303
|
+
x: tile.x,
|
|
42304
|
+
z: tile.z ?? tile.y ?? 0,
|
|
42305
|
+
type: tile.type
|
|
42306
|
+
});
|
|
42307
|
+
}
|
|
42308
|
+
} else {
|
|
42309
|
+
if (tileLeaveEvent) {
|
|
42310
|
+
emit(tileLeaveEvent, {});
|
|
42311
|
+
}
|
|
42312
|
+
}
|
|
42313
|
+
optionsRef.current.onTileHover?.(tile, event);
|
|
42314
|
+
},
|
|
42315
|
+
[tileHoverEvent, tileLeaveEvent, emit]
|
|
42316
|
+
);
|
|
42317
|
+
const handleUnitAnimation = React77.useCallback(
|
|
42318
|
+
(unitId, state) => {
|
|
42319
|
+
if (unitAnimationEvent) {
|
|
42320
|
+
emit(unitAnimationEvent, {
|
|
42321
|
+
unitId,
|
|
42322
|
+
state,
|
|
42323
|
+
timestamp: Date.now()
|
|
42324
|
+
});
|
|
42325
|
+
}
|
|
42326
|
+
optionsRef.current.onUnitAnimation?.(unitId, state);
|
|
42327
|
+
},
|
|
42328
|
+
[unitAnimationEvent, emit]
|
|
42329
|
+
);
|
|
42330
|
+
const handleCameraChange = React77.useCallback(
|
|
42331
|
+
(position) => {
|
|
42332
|
+
if (cameraChangeEvent) {
|
|
42333
|
+
emit(cameraChangeEvent, {
|
|
42334
|
+
position,
|
|
42335
|
+
timestamp: Date.now()
|
|
42336
|
+
});
|
|
42337
|
+
}
|
|
42338
|
+
},
|
|
42339
|
+
[cameraChangeEvent, emit]
|
|
42340
|
+
);
|
|
42341
|
+
return {
|
|
42342
|
+
handleTileClick,
|
|
42343
|
+
handleUnitClick,
|
|
42344
|
+
handleFeatureClick,
|
|
42345
|
+
handleCanvasClick,
|
|
42346
|
+
handleTileHover,
|
|
42347
|
+
handleUnitAnimation,
|
|
42348
|
+
handleCameraChange
|
|
42349
|
+
};
|
|
42350
|
+
}
|
|
42351
|
+
var init_useGameCanvas3DEvents = __esm({
|
|
42352
|
+
"components/game/molecules/three/hooks/useGameCanvas3DEvents.ts"() {
|
|
42353
|
+
"use client";
|
|
42354
|
+
init_useEventBus();
|
|
42355
|
+
}
|
|
42356
|
+
});
|
|
42357
|
+
|
|
42358
|
+
// components/game/molecules/three/components/Canvas3DLoadingState.css
|
|
42359
|
+
var init_Canvas3DLoadingState = __esm({
|
|
42360
|
+
"components/game/molecules/three/components/Canvas3DLoadingState.css"() {
|
|
42361
|
+
}
|
|
42362
|
+
});
|
|
42363
|
+
function Canvas3DLoadingState({
|
|
42364
|
+
progress = 0,
|
|
42365
|
+
loaded = 0,
|
|
42366
|
+
total = 0,
|
|
42367
|
+
message = "Loading 3D Scene...",
|
|
42368
|
+
details,
|
|
42369
|
+
showSpinner = true,
|
|
42370
|
+
className
|
|
42371
|
+
}) {
|
|
42372
|
+
const clampedProgress = Math.max(0, Math.min(100, progress));
|
|
42373
|
+
const hasProgress = total > 0;
|
|
42374
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `canvas-3d-loading ${className || ""}`, children: [
|
|
42375
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__content", children: [
|
|
42376
|
+
showSpinner && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__spinner", children: [
|
|
42377
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "spinner__ring" }),
|
|
42378
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "spinner__ring spinner__ring--secondary" })
|
|
42379
|
+
] }),
|
|
42380
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__message", children: message }),
|
|
42381
|
+
details && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__details", children: details }),
|
|
42382
|
+
hasProgress && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-loading__progress", children: [
|
|
42383
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "progress__bar", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
42384
|
+
"div",
|
|
42385
|
+
{
|
|
42386
|
+
className: "progress__fill",
|
|
42387
|
+
style: { width: `${clampedProgress}%` }
|
|
42388
|
+
}
|
|
42389
|
+
) }),
|
|
42390
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "progress__text", children: [
|
|
42391
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "progress__percentage", children: [
|
|
42392
|
+
clampedProgress,
|
|
42393
|
+
"%"
|
|
42394
|
+
] }),
|
|
42395
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "progress__count", children: [
|
|
42396
|
+
"(",
|
|
42397
|
+
loaded,
|
|
42398
|
+
"/",
|
|
42399
|
+
total,
|
|
42400
|
+
")"
|
|
42401
|
+
] })
|
|
42402
|
+
] })
|
|
42403
|
+
] })
|
|
42404
|
+
] }),
|
|
42405
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-loading__background", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg__grid" }) })
|
|
42406
|
+
] });
|
|
42407
|
+
}
|
|
42408
|
+
var init_Canvas3DLoadingState2 = __esm({
|
|
42409
|
+
"components/game/molecules/three/components/Canvas3DLoadingState.tsx"() {
|
|
42410
|
+
init_Canvas3DLoadingState();
|
|
42411
|
+
}
|
|
42412
|
+
});
|
|
42413
|
+
|
|
42414
|
+
// components/game/molecules/three/components/Canvas3DErrorBoundary.css
|
|
42415
|
+
var init_Canvas3DErrorBoundary = __esm({
|
|
42416
|
+
"components/game/molecules/three/components/Canvas3DErrorBoundary.css"() {
|
|
42417
|
+
}
|
|
42418
|
+
});
|
|
42419
|
+
var log9, Canvas3DErrorBoundary;
|
|
42420
|
+
var init_Canvas3DErrorBoundary2 = __esm({
|
|
42421
|
+
"components/game/molecules/three/components/Canvas3DErrorBoundary.tsx"() {
|
|
42422
|
+
init_Canvas3DErrorBoundary();
|
|
42423
|
+
log9 = logger.createLogger("almadar:ui:game:canvas3d:error-boundary");
|
|
42424
|
+
Canvas3DErrorBoundary = class extends React77.Component {
|
|
42425
|
+
constructor(props) {
|
|
42426
|
+
super(props);
|
|
42427
|
+
__publicField(this, "handleReset", () => {
|
|
42428
|
+
this.setState({
|
|
42429
|
+
hasError: false,
|
|
42430
|
+
error: null,
|
|
42431
|
+
errorInfo: null
|
|
42432
|
+
});
|
|
42433
|
+
this.props.onReset?.();
|
|
42434
|
+
});
|
|
42435
|
+
this.state = {
|
|
42436
|
+
hasError: false,
|
|
42437
|
+
error: null,
|
|
42438
|
+
errorInfo: null
|
|
42439
|
+
};
|
|
42440
|
+
}
|
|
42441
|
+
static getDerivedStateFromError(error) {
|
|
42442
|
+
return {
|
|
42443
|
+
hasError: true,
|
|
42444
|
+
error,
|
|
42445
|
+
errorInfo: null
|
|
42446
|
+
};
|
|
42447
|
+
}
|
|
42448
|
+
componentDidCatch(error, errorInfo) {
|
|
42449
|
+
this.setState({ errorInfo });
|
|
42450
|
+
this.props.onError?.(error, errorInfo);
|
|
42451
|
+
log9.error("Error caught", { error });
|
|
42452
|
+
log9.error("Component stack", { componentStack: errorInfo.componentStack ?? "<none>" });
|
|
42453
|
+
}
|
|
42454
|
+
render() {
|
|
42455
|
+
if (this.state.hasError) {
|
|
42456
|
+
if (this.props.fallback) {
|
|
42457
|
+
return this.props.fallback;
|
|
42458
|
+
}
|
|
42459
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-error", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-error__content", children: [
|
|
42460
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "canvas-3d-error__icon", children: "\u26A0\uFE0F" }),
|
|
42461
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { className: "canvas-3d-error__title", children: "3D Scene Error" }),
|
|
42462
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "canvas-3d-error__message", children: "Something went wrong while rendering the 3D scene." }),
|
|
42463
|
+
this.state.error && /* @__PURE__ */ jsxRuntime.jsxs("details", { className: "canvas-3d-error__details", children: [
|
|
42464
|
+
/* @__PURE__ */ jsxRuntime.jsx("summary", { children: "Error Details" }),
|
|
42465
|
+
/* @__PURE__ */ jsxRuntime.jsxs("pre", { className: "error__stack", children: [
|
|
42466
|
+
this.state.error.message,
|
|
42467
|
+
"\n",
|
|
42468
|
+
this.state.error.stack
|
|
42469
|
+
] }),
|
|
42470
|
+
this.state.errorInfo && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "error__component-stack", children: this.state.errorInfo.componentStack })
|
|
42471
|
+
] }),
|
|
42472
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "canvas-3d-error__actions", children: [
|
|
42473
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42474
|
+
"button",
|
|
42475
|
+
{
|
|
42476
|
+
className: "error__button error__button--primary",
|
|
42477
|
+
onClick: this.handleReset,
|
|
42478
|
+
children: "Try Again"
|
|
42479
|
+
}
|
|
42480
|
+
),
|
|
42481
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42482
|
+
"button",
|
|
42483
|
+
{
|
|
42484
|
+
className: "error__button error__button--secondary",
|
|
42485
|
+
onClick: () => window.location.reload(),
|
|
42486
|
+
children: "Reload Page"
|
|
42487
|
+
}
|
|
42488
|
+
)
|
|
42489
|
+
] })
|
|
42490
|
+
] }) });
|
|
42491
|
+
}
|
|
42492
|
+
return this.props.children;
|
|
42493
|
+
}
|
|
42494
|
+
};
|
|
42495
|
+
}
|
|
42496
|
+
});
|
|
42497
|
+
function detectAssetRoot2(modelUrl) {
|
|
42498
|
+
const idx = modelUrl.indexOf("/3d/");
|
|
42499
|
+
if (idx !== -1) {
|
|
42500
|
+
return modelUrl.substring(0, idx + 4);
|
|
42501
|
+
}
|
|
42502
|
+
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
42503
|
+
}
|
|
42504
|
+
function useGLTFModel(url, resourceBasePath) {
|
|
42505
|
+
const [state, setState] = React77.useState({
|
|
42506
|
+
model: null,
|
|
42507
|
+
isLoading: false,
|
|
42508
|
+
error: null
|
|
42509
|
+
});
|
|
42510
|
+
React77.useEffect(() => {
|
|
42511
|
+
if (!url) {
|
|
42512
|
+
setState({ model: null, isLoading: false, error: null });
|
|
42513
|
+
return;
|
|
42514
|
+
}
|
|
42515
|
+
log10.debug("Loading", { url });
|
|
42516
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
42517
|
+
const assetRoot = resourceBasePath || detectAssetRoot2(url);
|
|
42518
|
+
const loader = new GLTFLoader.GLTFLoader();
|
|
42519
|
+
loader.setResourcePath(assetRoot);
|
|
42520
|
+
loader.load(
|
|
42521
|
+
url,
|
|
42522
|
+
(gltf) => {
|
|
42523
|
+
log10.debug("Loaded", { url });
|
|
42524
|
+
setState({
|
|
42525
|
+
model: gltf.scene,
|
|
42526
|
+
isLoading: false,
|
|
42527
|
+
error: null
|
|
42528
|
+
});
|
|
42529
|
+
},
|
|
42530
|
+
void 0,
|
|
42531
|
+
(err) => {
|
|
42532
|
+
log10.warn("Failed", { url, error: err instanceof Error ? err : String(err) });
|
|
42533
|
+
setState({
|
|
42534
|
+
model: null,
|
|
42535
|
+
isLoading: false,
|
|
42536
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
42537
|
+
});
|
|
42538
|
+
}
|
|
42539
|
+
);
|
|
42540
|
+
}, [url, resourceBasePath]);
|
|
42541
|
+
return state;
|
|
42542
|
+
}
|
|
42543
|
+
function ModelLoader({
|
|
42544
|
+
url,
|
|
42545
|
+
position = [0, 0, 0],
|
|
42546
|
+
scale = 1,
|
|
42547
|
+
rotation = [0, 0, 0],
|
|
42548
|
+
isSelected = false,
|
|
42549
|
+
isHovered = false,
|
|
42550
|
+
onClick,
|
|
42551
|
+
onHover,
|
|
42552
|
+
fallbackGeometry = "box",
|
|
42553
|
+
castShadow = true,
|
|
42554
|
+
receiveShadow = true,
|
|
42555
|
+
resourceBasePath
|
|
42556
|
+
}) {
|
|
42557
|
+
const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
|
|
42558
|
+
const model = React77.useMemo(() => {
|
|
42559
|
+
if (!loadedModel) return null;
|
|
42560
|
+
const cloned = loadedModel.clone();
|
|
42561
|
+
cloned.traverse((child) => {
|
|
42562
|
+
if (child instanceof THREE__namespace.Mesh) {
|
|
42563
|
+
child.castShadow = castShadow;
|
|
42564
|
+
child.receiveShadow = receiveShadow;
|
|
42565
|
+
}
|
|
42566
|
+
});
|
|
42567
|
+
return cloned;
|
|
42568
|
+
}, [loadedModel, castShadow, receiveShadow]);
|
|
42569
|
+
const scaleArray = React77.useMemo(() => {
|
|
42570
|
+
if (typeof scale === "number") {
|
|
42571
|
+
return [scale, scale, scale];
|
|
42572
|
+
}
|
|
42573
|
+
return scale;
|
|
42574
|
+
}, [scale]);
|
|
42575
|
+
const rotationRad = React77.useMemo(() => {
|
|
42576
|
+
return [
|
|
42577
|
+
rotation[0] * Math.PI / 180,
|
|
42578
|
+
rotation[1] * Math.PI / 180,
|
|
42579
|
+
rotation[2] * Math.PI / 180
|
|
42580
|
+
];
|
|
42581
|
+
}, [rotation]);
|
|
42582
|
+
if (isLoading) {
|
|
42583
|
+
return /* @__PURE__ */ jsxRuntime.jsx("group", { position, children: /* @__PURE__ */ jsxRuntime.jsxs("mesh", { rotation: [Math.PI / 2, 0, 0], children: [
|
|
42584
|
+
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.3, 0.35, 16] }),
|
|
42585
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#4a90d9", transparent: true, opacity: 0.8 })
|
|
42586
|
+
] }) });
|
|
42587
|
+
}
|
|
42588
|
+
if (error || !model) {
|
|
42589
|
+
if (fallbackGeometry === "none") {
|
|
42590
|
+
return /* @__PURE__ */ jsxRuntime.jsx("group", { position });
|
|
42591
|
+
}
|
|
42592
|
+
const fallbackProps = {
|
|
42593
|
+
onClick,
|
|
42594
|
+
onPointerOver: () => onHover?.(true),
|
|
42595
|
+
onPointerOut: () => onHover?.(false)
|
|
42596
|
+
};
|
|
42597
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("group", { position, children: [
|
|
42598
|
+
(isSelected || isHovered) && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
42599
|
+
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
|
|
42600
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42601
|
+
"meshBasicMaterial",
|
|
42602
|
+
{
|
|
42603
|
+
color: isSelected ? 16755200 : 16777215,
|
|
42604
|
+
transparent: true,
|
|
42605
|
+
opacity: 0.5
|
|
42606
|
+
}
|
|
42607
|
+
)
|
|
42608
|
+
] }),
|
|
42609
|
+
fallbackGeometry === "box" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
42610
|
+
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.8, 0.8, 0.8] }),
|
|
42611
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
42612
|
+
] }),
|
|
42613
|
+
fallbackGeometry === "sphere" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
42614
|
+
/* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [0.4, 16, 16] }),
|
|
42615
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
42616
|
+
] }),
|
|
42617
|
+
fallbackGeometry === "cylinder" && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
42618
|
+
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.3, 0.3, 0.8, 16] }),
|
|
42619
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
42620
|
+
] })
|
|
42621
|
+
] });
|
|
42622
|
+
}
|
|
42623
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
42624
|
+
"group",
|
|
42625
|
+
{
|
|
42626
|
+
position,
|
|
42627
|
+
rotation: rotationRad,
|
|
42628
|
+
onClick,
|
|
42629
|
+
onPointerOver: () => onHover?.(true),
|
|
42630
|
+
onPointerOut: () => onHover?.(false),
|
|
42631
|
+
children: [
|
|
42632
|
+
(isSelected || isHovered) && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
42633
|
+
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
|
|
42634
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42635
|
+
"meshBasicMaterial",
|
|
42636
|
+
{
|
|
42637
|
+
color: isSelected ? 16755200 : 16777215,
|
|
42638
|
+
transparent: true,
|
|
42639
|
+
opacity: 0.5
|
|
42640
|
+
}
|
|
42641
|
+
)
|
|
42642
|
+
] }),
|
|
42643
|
+
/* @__PURE__ */ jsxRuntime.jsx("primitive", { object: model, scale: scaleArray })
|
|
42644
|
+
]
|
|
42645
|
+
}
|
|
42646
|
+
);
|
|
42647
|
+
}
|
|
42648
|
+
var log10;
|
|
42649
|
+
var init_ModelLoader = __esm({
|
|
42650
|
+
"components/game/molecules/three/components/ModelLoader.tsx"() {
|
|
42651
|
+
"use client";
|
|
42652
|
+
log10 = logger.createLogger("almadar:ui:game:model-loader");
|
|
42653
|
+
}
|
|
42654
|
+
});
|
|
42655
|
+
|
|
42656
|
+
// components/game/molecules/GameCanvas3D.css
|
|
42657
|
+
var init_GameCanvas3D = __esm({
|
|
42658
|
+
"components/game/molecules/GameCanvas3D.css"() {
|
|
42659
|
+
}
|
|
42660
|
+
});
|
|
42661
|
+
function CameraController({
|
|
42662
|
+
onCameraChange
|
|
42663
|
+
}) {
|
|
42664
|
+
const { camera } = fiber.useThree();
|
|
42665
|
+
React77.useEffect(() => {
|
|
42666
|
+
if (onCameraChange) {
|
|
42667
|
+
onCameraChange({
|
|
42668
|
+
x: camera.position.x,
|
|
42669
|
+
y: camera.position.y,
|
|
42670
|
+
z: camera.position.z
|
|
42671
|
+
});
|
|
42672
|
+
}
|
|
42673
|
+
}, [camera.position, onCameraChange]);
|
|
42674
|
+
return null;
|
|
42675
|
+
}
|
|
42676
|
+
var DEFAULT_GRID_CONFIG, GameCanvas3D;
|
|
42677
|
+
var init_GameCanvas3D2 = __esm({
|
|
42678
|
+
"components/game/molecules/GameCanvas3D.tsx"() {
|
|
42679
|
+
"use client";
|
|
42680
|
+
init_AssetLoader();
|
|
42681
|
+
init_useAssetLoader();
|
|
42682
|
+
init_useGameCanvas3DEvents();
|
|
42683
|
+
init_Canvas3DLoadingState2();
|
|
42684
|
+
init_Canvas3DErrorBoundary2();
|
|
42685
|
+
init_ModelLoader();
|
|
42686
|
+
init_cn();
|
|
42687
|
+
init_GameCanvas3D();
|
|
42688
|
+
DEFAULT_GRID_CONFIG = {
|
|
42689
|
+
cellSize: 1,
|
|
42690
|
+
offsetX: 0,
|
|
42691
|
+
offsetZ: 0
|
|
42692
|
+
};
|
|
42693
|
+
GameCanvas3D = React77.forwardRef(
|
|
42694
|
+
({
|
|
42695
|
+
tiles = [],
|
|
42696
|
+
units = [],
|
|
42697
|
+
features = [],
|
|
42698
|
+
events: events2 = [],
|
|
42699
|
+
orientation = "standard",
|
|
42700
|
+
cameraMode = "isometric",
|
|
42701
|
+
showGrid = true,
|
|
42702
|
+
showCoordinates = false,
|
|
42703
|
+
showTileInfo = false,
|
|
42704
|
+
overlay = "default",
|
|
42705
|
+
shadows = true,
|
|
42706
|
+
backgroundColor = "#1a1a2e",
|
|
42707
|
+
onTileClick,
|
|
42708
|
+
onUnitClick,
|
|
42709
|
+
onFeatureClick,
|
|
42710
|
+
onCanvasClick,
|
|
42711
|
+
onTileHover,
|
|
42712
|
+
onUnitAnimation,
|
|
42713
|
+
assetLoader: customAssetLoader,
|
|
42714
|
+
tileRenderer: CustomTileRenderer,
|
|
42715
|
+
unitRenderer: CustomUnitRenderer,
|
|
42716
|
+
featureRenderer: CustomFeatureRenderer,
|
|
42717
|
+
className,
|
|
42718
|
+
isLoading: externalLoading,
|
|
42719
|
+
error: externalError,
|
|
42720
|
+
entity,
|
|
42721
|
+
preloadAssets = [],
|
|
42722
|
+
tileClickEvent,
|
|
42723
|
+
unitClickEvent,
|
|
42724
|
+
featureClickEvent,
|
|
42725
|
+
canvasClickEvent,
|
|
42726
|
+
tileHoverEvent,
|
|
42727
|
+
tileLeaveEvent,
|
|
42728
|
+
unitAnimationEvent,
|
|
42729
|
+
cameraChangeEvent,
|
|
42730
|
+
loadingMessage = "Loading 3D Scene...",
|
|
42731
|
+
useInstancing = true,
|
|
42732
|
+
validMoves = [],
|
|
42733
|
+
attackTargets = [],
|
|
42734
|
+
selectedTileIds = [],
|
|
42735
|
+
selectedUnitId = null,
|
|
42736
|
+
children
|
|
42737
|
+
}, ref) => {
|
|
42738
|
+
const containerRef = React77.useRef(null);
|
|
42739
|
+
const controlsRef = React77.useRef(null);
|
|
42740
|
+
const [hoveredTile, setHoveredTile] = React77.useState(null);
|
|
42741
|
+
const [internalError, setInternalError] = React77.useState(null);
|
|
42742
|
+
const { isLoading: assetsLoading, progress, loaded, total } = useAssetLoader({
|
|
42743
|
+
preloadUrls: preloadAssets,
|
|
42744
|
+
loader: customAssetLoader
|
|
42745
|
+
});
|
|
42746
|
+
const eventHandlers = useGameCanvas3DEvents({
|
|
42747
|
+
tileClickEvent,
|
|
42748
|
+
unitClickEvent,
|
|
42749
|
+
featureClickEvent,
|
|
42750
|
+
canvasClickEvent,
|
|
42751
|
+
tileHoverEvent,
|
|
42752
|
+
tileLeaveEvent,
|
|
42753
|
+
unitAnimationEvent,
|
|
42754
|
+
cameraChangeEvent,
|
|
42755
|
+
onTileClick,
|
|
42756
|
+
onUnitClick,
|
|
42757
|
+
onFeatureClick,
|
|
42758
|
+
onCanvasClick,
|
|
42759
|
+
onTileHover,
|
|
42760
|
+
onUnitAnimation
|
|
42761
|
+
});
|
|
42762
|
+
const gridBounds = React77.useMemo(() => {
|
|
42763
|
+
if (tiles.length === 0) {
|
|
42764
|
+
return { minX: 0, maxX: 10, minZ: 0, maxZ: 10 };
|
|
42765
|
+
}
|
|
42766
|
+
const xs = tiles.map((t) => t.x);
|
|
42767
|
+
const zs = tiles.map((t) => t.z || t.y || 0);
|
|
42768
|
+
return {
|
|
42769
|
+
minX: Math.min(...xs),
|
|
42770
|
+
maxX: Math.max(...xs),
|
|
42771
|
+
minZ: Math.min(...zs),
|
|
42772
|
+
maxZ: Math.max(...zs)
|
|
42773
|
+
};
|
|
42774
|
+
}, [tiles]);
|
|
42775
|
+
const cameraTarget = React77.useMemo(() => {
|
|
42776
|
+
return [
|
|
42777
|
+
(gridBounds.minX + gridBounds.maxX) / 2,
|
|
42778
|
+
0,
|
|
42779
|
+
(gridBounds.minZ + gridBounds.maxZ) / 2
|
|
42780
|
+
];
|
|
42781
|
+
}, [gridBounds]);
|
|
42782
|
+
const gridConfig = React77.useMemo(
|
|
42783
|
+
() => ({
|
|
42784
|
+
...DEFAULT_GRID_CONFIG,
|
|
42785
|
+
offsetX: -(gridBounds.maxX - gridBounds.minX) / 2,
|
|
42786
|
+
offsetZ: -(gridBounds.maxZ - gridBounds.minZ) / 2
|
|
42787
|
+
}),
|
|
42788
|
+
[gridBounds]
|
|
42789
|
+
);
|
|
42790
|
+
const gridToWorld = React77.useCallback(
|
|
42791
|
+
(x, z, y = 0) => {
|
|
42792
|
+
const worldX = (x - gridBounds.minX) * gridConfig.cellSize;
|
|
42793
|
+
const worldZ = (z - gridBounds.minZ) * gridConfig.cellSize;
|
|
42794
|
+
return [worldX, y * gridConfig.cellSize, worldZ];
|
|
42795
|
+
},
|
|
42796
|
+
[gridBounds, gridConfig]
|
|
42797
|
+
);
|
|
42798
|
+
React77.useImperativeHandle(ref, () => ({
|
|
42799
|
+
getCameraPosition: () => {
|
|
42800
|
+
if (controlsRef.current) {
|
|
42801
|
+
const pos = controlsRef.current.object.position;
|
|
42802
|
+
return new THREE__namespace.Vector3(pos.x, pos.y, pos.z);
|
|
42803
|
+
}
|
|
42804
|
+
return null;
|
|
42805
|
+
},
|
|
42806
|
+
setCameraPosition: (x, y, z) => {
|
|
42807
|
+
if (controlsRef.current) {
|
|
42808
|
+
controlsRef.current.object.position.set(x, y, z);
|
|
42809
|
+
controlsRef.current.update();
|
|
42810
|
+
}
|
|
42811
|
+
},
|
|
42812
|
+
lookAt: (x, y, z) => {
|
|
42813
|
+
if (controlsRef.current) {
|
|
42814
|
+
controlsRef.current.target.set(x, y, z);
|
|
42815
|
+
controlsRef.current.update();
|
|
42816
|
+
}
|
|
42817
|
+
},
|
|
42818
|
+
resetCamera: () => {
|
|
42819
|
+
if (controlsRef.current) {
|
|
42820
|
+
controlsRef.current.reset();
|
|
42821
|
+
}
|
|
42822
|
+
},
|
|
42823
|
+
screenshot: () => {
|
|
42824
|
+
const canvas = containerRef.current?.querySelector("canvas");
|
|
42825
|
+
if (canvas) {
|
|
42826
|
+
return canvas.toDataURL("image/png");
|
|
42827
|
+
}
|
|
42828
|
+
return null;
|
|
42829
|
+
},
|
|
42830
|
+
export: () => ({
|
|
42831
|
+
tiles,
|
|
42832
|
+
units,
|
|
42833
|
+
features
|
|
42834
|
+
})
|
|
42835
|
+
}));
|
|
42836
|
+
const handleTileClick = React77.useCallback(
|
|
42837
|
+
(tile, event) => {
|
|
42838
|
+
eventHandlers.handleTileClick(tile, event);
|
|
42839
|
+
},
|
|
42840
|
+
[eventHandlers]
|
|
42841
|
+
);
|
|
42842
|
+
const handleUnitClick = React77.useCallback(
|
|
42843
|
+
(unit, event) => {
|
|
42844
|
+
eventHandlers.handleUnitClick(unit, event);
|
|
42845
|
+
},
|
|
42846
|
+
[eventHandlers]
|
|
42847
|
+
);
|
|
42848
|
+
const handleFeatureClick = React77.useCallback(
|
|
42849
|
+
(feature, event) => {
|
|
42850
|
+
if (event) {
|
|
42851
|
+
eventHandlers.handleFeatureClick(feature, event);
|
|
42852
|
+
}
|
|
42853
|
+
},
|
|
42854
|
+
[eventHandlers]
|
|
42855
|
+
);
|
|
42856
|
+
const handleTileHover = React77.useCallback(
|
|
42857
|
+
(tile, event) => {
|
|
42858
|
+
setHoveredTile(tile);
|
|
42859
|
+
if (event) {
|
|
42860
|
+
eventHandlers.handleTileHover(tile, event);
|
|
42861
|
+
}
|
|
42862
|
+
},
|
|
42863
|
+
[eventHandlers]
|
|
42864
|
+
);
|
|
42865
|
+
const cameraConfig = React77.useMemo(() => {
|
|
42866
|
+
const size = Math.max(
|
|
42867
|
+
gridBounds.maxX - gridBounds.minX,
|
|
42868
|
+
gridBounds.maxZ - gridBounds.minZ
|
|
42869
|
+
);
|
|
42870
|
+
const distance = size * 1.5;
|
|
42871
|
+
switch (cameraMode) {
|
|
42872
|
+
case "isometric":
|
|
42873
|
+
return {
|
|
42874
|
+
position: [distance, distance * 0.8, distance],
|
|
42875
|
+
fov: 45
|
|
42876
|
+
};
|
|
42877
|
+
case "top-down":
|
|
42878
|
+
return {
|
|
42879
|
+
position: [0, distance * 2, 0],
|
|
42880
|
+
fov: 45
|
|
42881
|
+
};
|
|
42882
|
+
case "perspective":
|
|
42883
|
+
default:
|
|
42884
|
+
return {
|
|
42885
|
+
position: [distance, distance, distance],
|
|
42886
|
+
fov: 45
|
|
42887
|
+
};
|
|
42888
|
+
}
|
|
42889
|
+
}, [cameraMode, gridBounds]);
|
|
42890
|
+
const DefaultTileRenderer = React77.useCallback(
|
|
42891
|
+
({ tile, position }) => {
|
|
42892
|
+
const isSelected = tile.id ? selectedTileIds.includes(tile.id) : false;
|
|
42893
|
+
const isHovered = hoveredTile?.id === tile.id;
|
|
42894
|
+
const isValidMove = validMoves.some(
|
|
42895
|
+
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
42896
|
+
);
|
|
42897
|
+
const isAttackTarget = attackTargets.some(
|
|
42898
|
+
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
42899
|
+
);
|
|
42900
|
+
let color = 8421504;
|
|
42901
|
+
if (tile.type === "water") color = 4491468;
|
|
42902
|
+
else if (tile.type === "grass") color = 4500036;
|
|
42903
|
+
else if (tile.type === "sand") color = 14535816;
|
|
42904
|
+
else if (tile.type === "rock") color = 8947848;
|
|
42905
|
+
else if (tile.type === "snow") color = 15658734;
|
|
42906
|
+
let emissive = 0;
|
|
42907
|
+
if (isSelected) emissive = 4473924;
|
|
42908
|
+
else if (isAttackTarget) emissive = 4456448;
|
|
42909
|
+
else if (isValidMove) emissive = 17408;
|
|
42910
|
+
else if (isHovered) emissive = 2236962;
|
|
42911
|
+
if (tile.modelUrl) {
|
|
42912
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
42913
|
+
"group",
|
|
42914
|
+
{
|
|
42915
|
+
position,
|
|
42916
|
+
onClick: (e) => handleTileClick(tile, e),
|
|
42917
|
+
onPointerEnter: (e) => handleTileHover(tile, e),
|
|
42918
|
+
onPointerLeave: (e) => handleTileHover(null, e),
|
|
42919
|
+
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
42920
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
42921
|
+
ModelLoader,
|
|
42922
|
+
{
|
|
42923
|
+
url: tile.modelUrl,
|
|
42924
|
+
scale: 0.95,
|
|
42925
|
+
fallbackGeometry: "box",
|
|
42926
|
+
castShadow: true,
|
|
42927
|
+
receiveShadow: true
|
|
42928
|
+
}
|
|
42929
|
+
)
|
|
42930
|
+
}
|
|
42931
|
+
);
|
|
42932
|
+
}
|
|
42933
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
42934
|
+
"mesh",
|
|
42935
|
+
{
|
|
42936
|
+
position,
|
|
42937
|
+
onClick: (e) => handleTileClick(tile, e),
|
|
42938
|
+
onPointerEnter: (e) => handleTileHover(tile, e),
|
|
42939
|
+
onPointerLeave: (e) => handleTileHover(null, e),
|
|
42940
|
+
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
42941
|
+
children: [
|
|
42942
|
+
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.95, 0.2, 0.95] }),
|
|
42943
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color, emissive })
|
|
42944
|
+
]
|
|
42945
|
+
}
|
|
42946
|
+
);
|
|
42947
|
+
},
|
|
42948
|
+
[selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover]
|
|
42949
|
+
);
|
|
42950
|
+
const DefaultUnitRenderer = React77.useCallback(
|
|
42951
|
+
({ unit, position }) => {
|
|
42952
|
+
const isSelected = selectedUnitId === unit.id;
|
|
42953
|
+
const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
|
|
42954
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
42955
|
+
"group",
|
|
42956
|
+
{
|
|
42957
|
+
position,
|
|
42958
|
+
onClick: (e) => handleUnitClick(unit, e),
|
|
42959
|
+
userData: { type: "unit", unitId: unit.id },
|
|
42960
|
+
children: [
|
|
42961
|
+
isSelected && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.05, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
42962
|
+
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
|
|
42963
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
|
|
42964
|
+
] }),
|
|
42965
|
+
unit.modelUrl ? (
|
|
42966
|
+
/* GLB unit model (box fallback while loading / on error) */
|
|
42967
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
42968
|
+
ModelLoader,
|
|
42969
|
+
{
|
|
42970
|
+
url: unit.modelUrl,
|
|
42971
|
+
scale: 0.5,
|
|
42972
|
+
fallbackGeometry: "box",
|
|
42973
|
+
castShadow: true
|
|
42974
|
+
}
|
|
42975
|
+
)
|
|
42976
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
42977
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.3, 0], children: [
|
|
42978
|
+
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.3, 0.3, 0.1, 8] }),
|
|
42979
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
42980
|
+
] }),
|
|
42981
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.6, 0], children: [
|
|
42982
|
+
/* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [0.2, 0.4, 4, 8] }),
|
|
42983
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
42984
|
+
] }),
|
|
42985
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
42986
|
+
/* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [0.12, 8, 8] }),
|
|
42987
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
42988
|
+
] })
|
|
42989
|
+
] }),
|
|
42990
|
+
unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("group", { position: [0, 1.2, 0], children: [
|
|
42991
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [-0.25, 0, 0], children: [
|
|
42992
|
+
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.5, 0.05] }),
|
|
42993
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: 3355443 })
|
|
42994
|
+
] }),
|
|
42995
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
42996
|
+
"mesh",
|
|
42997
|
+
{
|
|
42998
|
+
position: [
|
|
42999
|
+
-0.25 + 0.5 * (unit.health / unit.maxHealth) / 2,
|
|
43000
|
+
0,
|
|
43001
|
+
0.01
|
|
43002
|
+
],
|
|
43003
|
+
children: [
|
|
43004
|
+
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.5 * (unit.health / unit.maxHealth), 0.05] }),
|
|
43005
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
43006
|
+
"meshBasicMaterial",
|
|
43007
|
+
{
|
|
43008
|
+
color: unit.health / unit.maxHealth > 0.5 ? 4500036 : unit.health / unit.maxHealth > 0.25 ? 11184708 : 16729156
|
|
43009
|
+
}
|
|
43010
|
+
)
|
|
43011
|
+
]
|
|
43012
|
+
}
|
|
43013
|
+
)
|
|
43014
|
+
] })
|
|
43015
|
+
]
|
|
43016
|
+
}
|
|
43017
|
+
);
|
|
43018
|
+
},
|
|
43019
|
+
[selectedUnitId, handleUnitClick]
|
|
43020
|
+
);
|
|
43021
|
+
const DefaultFeatureRenderer = React77.useCallback(
|
|
43022
|
+
({
|
|
43023
|
+
feature,
|
|
43024
|
+
position
|
|
43025
|
+
}) => {
|
|
43026
|
+
if (feature.assetUrl) {
|
|
43027
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
43028
|
+
ModelLoader,
|
|
43029
|
+
{
|
|
43030
|
+
url: feature.assetUrl,
|
|
43031
|
+
position,
|
|
43032
|
+
scale: 0.5,
|
|
43033
|
+
rotation: [0, feature.rotation ?? 0, 0],
|
|
43034
|
+
onClick: () => handleFeatureClick(feature, null),
|
|
43035
|
+
fallbackGeometry: "box"
|
|
43036
|
+
},
|
|
43037
|
+
feature.id
|
|
43038
|
+
);
|
|
43039
|
+
}
|
|
43040
|
+
if (feature.type === "tree") {
|
|
43041
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
43042
|
+
"group",
|
|
43043
|
+
{
|
|
43044
|
+
position,
|
|
43045
|
+
onClick: (e) => handleFeatureClick(feature, e),
|
|
43046
|
+
userData: { type: "feature", featureId: feature.id },
|
|
43047
|
+
children: [
|
|
43048
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.4, 0], children: [
|
|
43049
|
+
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.1, 0.15, 0.8, 6] }),
|
|
43050
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 9127187 })
|
|
43051
|
+
] }),
|
|
43052
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
43053
|
+
/* @__PURE__ */ jsxRuntime.jsx("coneGeometry", { args: [0.5, 0.8, 8] }),
|
|
43054
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 2263842 })
|
|
43055
|
+
] })
|
|
43056
|
+
]
|
|
43057
|
+
}
|
|
43058
|
+
);
|
|
43059
|
+
}
|
|
43060
|
+
if (feature.type === "rock") {
|
|
43061
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
43062
|
+
"mesh",
|
|
43063
|
+
{
|
|
43064
|
+
position: [position[0], position[1] + 0.3, position[2]],
|
|
43065
|
+
onClick: (e) => handleFeatureClick(feature, e),
|
|
43066
|
+
userData: { type: "feature", featureId: feature.id },
|
|
43067
|
+
children: [
|
|
43068
|
+
/* @__PURE__ */ jsxRuntime.jsx("dodecahedronGeometry", { args: [0.3, 0] }),
|
|
43069
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 8421504 })
|
|
43070
|
+
]
|
|
43071
|
+
}
|
|
43072
|
+
);
|
|
43073
|
+
}
|
|
43074
|
+
return null;
|
|
43075
|
+
},
|
|
43076
|
+
[handleFeatureClick]
|
|
43077
|
+
);
|
|
43078
|
+
if (externalLoading || assetsLoading && preloadAssets.length > 0) {
|
|
43079
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
43080
|
+
Canvas3DLoadingState,
|
|
43081
|
+
{
|
|
43082
|
+
progress,
|
|
43083
|
+
loaded,
|
|
43084
|
+
total,
|
|
43085
|
+
message: loadingMessage,
|
|
43086
|
+
className
|
|
43087
|
+
}
|
|
43088
|
+
);
|
|
43089
|
+
}
|
|
43090
|
+
const displayError = externalError || internalError;
|
|
43091
|
+
if (displayError) {
|
|
43092
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Canvas3DErrorBoundary, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "game-canvas-3d game-canvas-3d--error", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__error", children: [
|
|
43093
|
+
"Error: ",
|
|
43094
|
+
displayError
|
|
43095
|
+
] }) }) });
|
|
43096
|
+
}
|
|
43097
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
43098
|
+
Canvas3DErrorBoundary,
|
|
43099
|
+
{
|
|
43100
|
+
onError: (err) => setInternalError(err.message),
|
|
43101
|
+
onReset: () => setInternalError(null),
|
|
43102
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
43103
|
+
"div",
|
|
43104
|
+
{
|
|
43105
|
+
ref: containerRef,
|
|
43106
|
+
className: cn("game-canvas-3d relative w-full h-full min-h-[85vh] overflow-hidden", className),
|
|
43107
|
+
"data-orientation": orientation,
|
|
43108
|
+
"data-camera-mode": cameraMode,
|
|
43109
|
+
"data-overlay": overlay,
|
|
43110
|
+
children: [
|
|
43111
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
43112
|
+
fiber.Canvas,
|
|
43113
|
+
{
|
|
43114
|
+
shadows,
|
|
43115
|
+
camera: {
|
|
43116
|
+
position: cameraConfig.position,
|
|
43117
|
+
fov: cameraConfig.fov,
|
|
43118
|
+
near: 0.1,
|
|
43119
|
+
far: 1e3
|
|
43120
|
+
},
|
|
43121
|
+
style: { background: backgroundColor },
|
|
43122
|
+
onClick: (e) => {
|
|
43123
|
+
if (e.target === e.currentTarget) {
|
|
43124
|
+
eventHandlers.handleCanvasClick(e);
|
|
43125
|
+
}
|
|
43126
|
+
},
|
|
43127
|
+
children: [
|
|
43128
|
+
/* @__PURE__ */ jsxRuntime.jsx(CameraController, { onCameraChange: eventHandlers.handleCameraChange }),
|
|
43129
|
+
/* @__PURE__ */ jsxRuntime.jsx("ambientLight", { intensity: 0.6 }),
|
|
43130
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
43131
|
+
"directionalLight",
|
|
43132
|
+
{
|
|
43133
|
+
position: [10, 20, 10],
|
|
43134
|
+
intensity: 0.8,
|
|
43135
|
+
castShadow: shadows,
|
|
43136
|
+
"shadow-mapSize": [2048, 2048]
|
|
43137
|
+
}
|
|
43138
|
+
),
|
|
43139
|
+
/* @__PURE__ */ jsxRuntime.jsx("hemisphereLight", { intensity: 0.3, color: "#87ceeb", groundColor: "#362d1d" }),
|
|
43140
|
+
showGrid && /* @__PURE__ */ jsxRuntime.jsx(
|
|
43141
|
+
drei.Grid,
|
|
43142
|
+
{
|
|
43143
|
+
args: [
|
|
43144
|
+
Math.max(gridBounds.maxX - gridBounds.minX + 2, 10),
|
|
43145
|
+
Math.max(gridBounds.maxZ - gridBounds.minZ + 2, 10)
|
|
43146
|
+
],
|
|
43147
|
+
position: [
|
|
43148
|
+
(gridBounds.maxX - gridBounds.minX) / 2 - 0.5,
|
|
43149
|
+
0,
|
|
43150
|
+
(gridBounds.maxZ - gridBounds.minZ) / 2 - 0.5
|
|
43151
|
+
],
|
|
43152
|
+
cellSize: 1,
|
|
43153
|
+
cellThickness: 1,
|
|
43154
|
+
cellColor: "#444444",
|
|
43155
|
+
sectionSize: 5,
|
|
43156
|
+
sectionThickness: 1.5,
|
|
43157
|
+
sectionColor: "#666666",
|
|
43158
|
+
fadeDistance: 50,
|
|
43159
|
+
fadeStrength: 1
|
|
43160
|
+
}
|
|
43161
|
+
),
|
|
43162
|
+
tiles.map((tile, index) => {
|
|
43163
|
+
const position = gridToWorld(
|
|
43164
|
+
tile.x,
|
|
43165
|
+
tile.z ?? tile.y ?? 0,
|
|
43166
|
+
tile.elevation ?? 0
|
|
43167
|
+
);
|
|
43168
|
+
const Renderer = CustomTileRenderer || DefaultTileRenderer;
|
|
43169
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
|
|
43170
|
+
}),
|
|
43171
|
+
features.map((feature, index) => {
|
|
43172
|
+
const position = gridToWorld(
|
|
43173
|
+
feature.x,
|
|
43174
|
+
feature.z ?? feature.y ?? 0,
|
|
43175
|
+
(feature.elevation ?? 0) + 0.5
|
|
43176
|
+
);
|
|
43177
|
+
const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
|
|
43178
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
|
|
43179
|
+
}),
|
|
43180
|
+
units.map((unit) => {
|
|
43181
|
+
const position = gridToWorld(
|
|
43182
|
+
unit.x ?? 0,
|
|
43183
|
+
unit.z ?? unit.y ?? 0,
|
|
43184
|
+
(unit.elevation ?? 0) + 0.5
|
|
43185
|
+
);
|
|
43186
|
+
const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
|
|
43187
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { unit, position }, unit.id);
|
|
43188
|
+
}),
|
|
43189
|
+
children,
|
|
43190
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
43191
|
+
drei.OrbitControls,
|
|
43192
|
+
{
|
|
43193
|
+
ref: controlsRef,
|
|
43194
|
+
target: cameraTarget,
|
|
43195
|
+
enableDamping: true,
|
|
43196
|
+
dampingFactor: 0.05,
|
|
43197
|
+
minDistance: 2,
|
|
43198
|
+
maxDistance: 100,
|
|
43199
|
+
maxPolarAngle: Math.PI / 2 - 0.1
|
|
43200
|
+
}
|
|
43201
|
+
)
|
|
43202
|
+
]
|
|
43203
|
+
}
|
|
43204
|
+
),
|
|
43205
|
+
showCoordinates && hoveredTile && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__coordinates", children: [
|
|
43206
|
+
"X: ",
|
|
43207
|
+
hoveredTile.x,
|
|
43208
|
+
", Z: ",
|
|
43209
|
+
hoveredTile.z ?? hoveredTile.y ?? 0
|
|
43210
|
+
] }),
|
|
43211
|
+
showTileInfo && hoveredTile && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__tile-info", children: [
|
|
43212
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "tile-info__type", children: hoveredTile.type }),
|
|
43213
|
+
hoveredTile.terrain && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tile-info__terrain", children: hoveredTile.terrain })
|
|
43214
|
+
] })
|
|
43215
|
+
]
|
|
43216
|
+
}
|
|
43217
|
+
)
|
|
43218
|
+
}
|
|
43219
|
+
);
|
|
43220
|
+
}
|
|
43221
|
+
);
|
|
43222
|
+
GameCanvas3D.displayName = "GameCanvas3D";
|
|
43223
|
+
}
|
|
43224
|
+
});
|
|
43225
|
+
function GameBoard3D({
|
|
43226
|
+
entity,
|
|
43227
|
+
tiles = [],
|
|
43228
|
+
features = [],
|
|
43229
|
+
cameraMode = "perspective",
|
|
43230
|
+
backgroundColor = "#2a1a1a",
|
|
43231
|
+
tileClickEvent,
|
|
43232
|
+
unitClickEvent,
|
|
43233
|
+
attackEvent,
|
|
43234
|
+
endTurnEvent,
|
|
43235
|
+
cancelEvent,
|
|
43236
|
+
playAgainEvent,
|
|
43237
|
+
gameEndEvent,
|
|
43238
|
+
className
|
|
43239
|
+
}) {
|
|
43240
|
+
const row = boardEntity(entity);
|
|
43241
|
+
const eventBus = useEventBus();
|
|
43242
|
+
const entityUnits = row ? rows(row.units) : [];
|
|
43243
|
+
const units = entityUnits;
|
|
43244
|
+
const selectedUnitId = row ? str(row.selectedUnitId) || null : null;
|
|
43245
|
+
const phase = row ? str(row.phase) : "observation";
|
|
43246
|
+
const result = row ? str(row.result) : "none";
|
|
43247
|
+
const validMoves = row && Array.isArray(row.validMoves) ? row.validMoves : [];
|
|
43248
|
+
const attackTargets = row && Array.isArray(row.attackTargets) ? row.attackTargets : [];
|
|
43249
|
+
const turn = row ? num(row.turn) : 0;
|
|
43250
|
+
const currentTeam = row ? str(row.currentTeam) : "player";
|
|
43251
|
+
const isGameOver = result !== "none";
|
|
43252
|
+
const gameEndEmittedRef = React77.useRef(false);
|
|
43253
|
+
React77.useEffect(() => {
|
|
43254
|
+
if ((result === "victory" || result === "defeat") && gameEndEvent) {
|
|
43255
|
+
if (!gameEndEmittedRef.current) {
|
|
43256
|
+
gameEndEmittedRef.current = true;
|
|
43257
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
43258
|
+
}
|
|
43259
|
+
} else {
|
|
43260
|
+
gameEndEmittedRef.current = false;
|
|
43261
|
+
}
|
|
43262
|
+
}, [result, gameEndEvent, eventBus]);
|
|
43263
|
+
const checkGameEnd = React77.useCallback(() => {
|
|
43264
|
+
const alivePlayer = entityUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
|
|
43265
|
+
const aliveEnemy = entityUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
|
|
43266
|
+
if (alivePlayer.length === 0 && gameEndEvent) {
|
|
43267
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result: "defeat" });
|
|
43268
|
+
} else if (aliveEnemy.length === 0 && gameEndEvent) {
|
|
43269
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result: "victory" });
|
|
43270
|
+
}
|
|
43271
|
+
}, [entityUnits, gameEndEvent, eventBus]);
|
|
43272
|
+
const handleUnitClickCallback = React77.useCallback((isoUnit) => {
|
|
43273
|
+
if (phase !== "action" || !selectedUnitId || !attackEvent) return;
|
|
43274
|
+
const attackerRow = entityUnits.find((u) => str(u.id) === selectedUnitId);
|
|
43275
|
+
const targetRow = entityUnits.find((u) => str(u.id) === isoUnit.id);
|
|
43276
|
+
if (!attackerRow || !targetRow) return;
|
|
43277
|
+
if (unitTeam(targetRow) !== "enemy" || unitHealth(targetRow) <= 0) return;
|
|
43278
|
+
const ap = unitPosition(attackerRow);
|
|
43279
|
+
const tp = unitPosition(targetRow);
|
|
43280
|
+
const dx = Math.abs(ap.x - tp.x);
|
|
43281
|
+
const dy = Math.abs(ap.y - tp.y);
|
|
43282
|
+
if (dx <= 1 && dy <= 1 && dx + dy > 0) {
|
|
43283
|
+
const damage = Math.max(1, num(attackerRow.attack) - num(targetRow.defense));
|
|
43284
|
+
eventBus.emit(`UI:${attackEvent}`, {
|
|
43285
|
+
attackerId: str(attackerRow.id),
|
|
43286
|
+
targetId: str(targetRow.id),
|
|
43287
|
+
damage
|
|
43288
|
+
});
|
|
43289
|
+
setTimeout(checkGameEnd, 100);
|
|
43290
|
+
}
|
|
43291
|
+
}, [phase, selectedUnitId, entityUnits, attackEvent, eventBus, checkGameEnd]);
|
|
43292
|
+
const handleEndTurn = React77.useCallback(() => {
|
|
43293
|
+
if (endTurnEvent) eventBus.emit(`UI:${endTurnEvent}`, {});
|
|
43294
|
+
}, [endTurnEvent, eventBus]);
|
|
43295
|
+
const handleCancel = React77.useCallback(() => {
|
|
43296
|
+
if (cancelEvent) eventBus.emit(`UI:${cancelEvent}`, {});
|
|
43297
|
+
}, [cancelEvent, eventBus]);
|
|
43298
|
+
const handlePlayAgain = React77.useCallback(() => {
|
|
43299
|
+
if (playAgainEvent) eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
43300
|
+
}, [playAgainEvent, eventBus]);
|
|
43301
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { className: cn("game-board-3d block w-full min-h-[85vh] relative", className), children: [
|
|
43302
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-board-3d__status absolute top-3 left-3 z-10 flex gap-2 items-center", children: [
|
|
43303
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "status__phase capitalize", children: phase.replace("_", " ") }),
|
|
43304
|
+
currentTeam && !isGameOver && /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "small", color: "muted", className: "status__team", children: [
|
|
43305
|
+
"\u2014 ",
|
|
43306
|
+
currentTeam === "player" ? "Your Turn" : "Enemy's Turn"
|
|
43307
|
+
] }),
|
|
43308
|
+
/* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "small", color: "muted", className: "status__turn", children: [
|
|
43309
|
+
"Turn ",
|
|
43310
|
+
turn
|
|
43311
|
+
] })
|
|
43312
|
+
] }),
|
|
43313
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
43314
|
+
GameCanvas3D,
|
|
43315
|
+
{
|
|
43316
|
+
tiles,
|
|
43317
|
+
units,
|
|
43318
|
+
features,
|
|
43319
|
+
cameraMode,
|
|
43320
|
+
showGrid: true,
|
|
43321
|
+
showCoordinates: false,
|
|
43322
|
+
showTileInfo: false,
|
|
43323
|
+
shadows: true,
|
|
43324
|
+
backgroundColor,
|
|
43325
|
+
tileClickEvent,
|
|
43326
|
+
unitClickEvent,
|
|
43327
|
+
onUnitClick: handleUnitClickCallback,
|
|
43328
|
+
selectedUnitId,
|
|
43329
|
+
validMoves,
|
|
43330
|
+
attackTargets,
|
|
43331
|
+
className: "game-board-3d__canvas w-full min-h-[85vh]"
|
|
43332
|
+
}
|
|
43333
|
+
),
|
|
43334
|
+
!isGameOver && /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { className: "fixed bottom-6 right-6 z-50", gap: "sm", children: [
|
|
43335
|
+
(phase === "selection" || phase === "movement" || phase === "action") && /* @__PURE__ */ jsxRuntime.jsx(
|
|
43336
|
+
exports.Button,
|
|
43337
|
+
{
|
|
43338
|
+
variant: "secondary",
|
|
43339
|
+
className: "shadow-xl",
|
|
43340
|
+
onClick: handleCancel,
|
|
43341
|
+
children: "Cancel"
|
|
43342
|
+
}
|
|
43343
|
+
),
|
|
43344
|
+
phase !== "enemy_turn" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
43345
|
+
exports.Button,
|
|
43346
|
+
{
|
|
43347
|
+
variant: "primary",
|
|
43348
|
+
className: "shadow-xl",
|
|
43349
|
+
onClick: handleEndTurn,
|
|
43350
|
+
children: "End Turn"
|
|
43351
|
+
}
|
|
43352
|
+
)
|
|
43353
|
+
] }),
|
|
43354
|
+
isGameOver && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "game-board-3d__overlay absolute inset-0 z-20 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { align: "center", gap: "md", className: "overlay__card p-8 rounded-xl bg-gray-900/90 shadow-2xl", children: [
|
|
43355
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
43356
|
+
exports.Typography,
|
|
43357
|
+
{
|
|
43358
|
+
variant: "h2",
|
|
43359
|
+
className: cn(
|
|
43360
|
+
"overlay__result",
|
|
43361
|
+
result === "victory" ? "text-yellow-400" : "text-red-500"
|
|
43362
|
+
),
|
|
43363
|
+
children: result === "victory" ? "Victory!" : "Defeat"
|
|
43364
|
+
}
|
|
43365
|
+
),
|
|
43366
|
+
/* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "body", color: "muted", className: "overlay__turn-count", children: [
|
|
43367
|
+
"Completed in ",
|
|
43368
|
+
turn,
|
|
43369
|
+
" turn",
|
|
43370
|
+
turn !== 1 ? "s" : ""
|
|
43371
|
+
] }),
|
|
43372
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
43373
|
+
exports.Button,
|
|
43374
|
+
{
|
|
43375
|
+
variant: "primary",
|
|
43376
|
+
className: "px-8 py-3 font-semibold",
|
|
43377
|
+
onClick: handlePlayAgain,
|
|
43378
|
+
children: "Play Again"
|
|
43379
|
+
}
|
|
43380
|
+
)
|
|
43381
|
+
] }) })
|
|
43382
|
+
] });
|
|
43383
|
+
}
|
|
43384
|
+
var init_GameBoard3D = __esm({
|
|
43385
|
+
"components/game/organisms/GameBoard3D.tsx"() {
|
|
43386
|
+
"use client";
|
|
43387
|
+
init_cn();
|
|
43388
|
+
init_Button();
|
|
43389
|
+
init_Typography();
|
|
43390
|
+
init_Stack();
|
|
43391
|
+
init_useEventBus();
|
|
43392
|
+
init_GameCanvas3D2();
|
|
43393
|
+
init_boardEntity();
|
|
43394
|
+
GameBoard3D.displayName = "GameBoard3D";
|
|
43395
|
+
}
|
|
43396
|
+
});
|
|
41717
43397
|
exports.GameShell = void 0;
|
|
41718
43398
|
var init_GameShell = __esm({
|
|
41719
43399
|
"components/game/templates/GameShell.tsx"() {
|
|
@@ -43190,21 +44870,6 @@ var init_ModalSlot = __esm({
|
|
|
43190
44870
|
exports.ModalSlot.displayName = "ModalSlot";
|
|
43191
44871
|
}
|
|
43192
44872
|
});
|
|
43193
|
-
function getOpponentAction(strategy, actions, history) {
|
|
43194
|
-
const actionIds = actions.map((a) => a.id);
|
|
43195
|
-
switch (strategy) {
|
|
43196
|
-
case "always-cooperate":
|
|
43197
|
-
return actionIds[0];
|
|
43198
|
-
case "always-defect":
|
|
43199
|
-
return actionIds[actionIds.length - 1];
|
|
43200
|
-
case "tit-for-tat":
|
|
43201
|
-
if (history.length === 0) return actionIds[0];
|
|
43202
|
-
return history[history.length - 1].playerAction;
|
|
43203
|
-
case "random":
|
|
43204
|
-
default:
|
|
43205
|
-
return actionIds[Math.floor(Math.random() * actionIds.length)];
|
|
43206
|
-
}
|
|
43207
|
-
}
|
|
43208
44873
|
function NegotiatorBoard({
|
|
43209
44874
|
entity,
|
|
43210
44875
|
completeEvent = "PUZZLE_COMPLETE",
|
|
@@ -43226,28 +44891,35 @@ function NegotiatorBoard({
|
|
|
43226
44891
|
const playerTotal = num(resolved?.score);
|
|
43227
44892
|
const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
|
|
43228
44893
|
const won = result === "win";
|
|
43229
|
-
const
|
|
44894
|
+
const lastPlayerAction = str(resolved?.lastPlayerAction);
|
|
44895
|
+
const lastOpponentAction = str(resolved?.lastOpponentAction);
|
|
44896
|
+
const lastPayoff = num(resolved?.lastPayoff);
|
|
43230
44897
|
const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
|
|
43231
44898
|
const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
|
|
44899
|
+
const prevRoundRef = React77.useRef(currentRound);
|
|
44900
|
+
React77.useEffect(() => {
|
|
44901
|
+
if (currentRound > prevRoundRef.current && lastPlayerAction) {
|
|
44902
|
+
const opponentPayoffEntry = payoffMatrix.find(
|
|
44903
|
+
(p2) => p2.playerAction === lastPlayerAction && p2.opponentAction === lastOpponentAction
|
|
44904
|
+
);
|
|
44905
|
+
setHistory((prev) => [
|
|
44906
|
+
...prev,
|
|
44907
|
+
{
|
|
44908
|
+
round: currentRound,
|
|
44909
|
+
playerAction: lastPlayerAction,
|
|
44910
|
+
opponentAction: lastOpponentAction,
|
|
44911
|
+
playerPayoff: lastPayoff,
|
|
44912
|
+
opponentPayoff: opponentPayoffEntry?.opponentPayoff ?? 0
|
|
44913
|
+
}
|
|
44914
|
+
]);
|
|
44915
|
+
}
|
|
44916
|
+
prevRoundRef.current = currentRound;
|
|
44917
|
+
}, [currentRound, lastPlayerAction, lastOpponentAction, lastPayoff, payoffMatrix]);
|
|
44918
|
+
const opponentTotal = React77.useMemo(() => history.reduce((s, r) => s + r.opponentPayoff, 0), [history]);
|
|
43232
44919
|
const handleAction = React77.useCallback((actionId) => {
|
|
43233
44920
|
if (isComplete) return;
|
|
43234
|
-
const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
|
|
43235
|
-
const payoff = payoffMatrix.find(
|
|
43236
|
-
(p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
|
|
43237
|
-
);
|
|
43238
|
-
const playerPayoff = payoff?.playerPayoff ?? 0;
|
|
43239
|
-
setHistory((prev) => [
|
|
43240
|
-
...prev,
|
|
43241
|
-
{
|
|
43242
|
-
round: prev.length + 1,
|
|
43243
|
-
playerAction: actionId,
|
|
43244
|
-
opponentAction,
|
|
43245
|
-
playerPayoff,
|
|
43246
|
-
opponentPayoff: payoff?.opponentPayoff ?? 0
|
|
43247
|
-
}
|
|
43248
|
-
]);
|
|
43249
44921
|
if (playRoundEvent) {
|
|
43250
|
-
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff:
|
|
44922
|
+
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: 0 });
|
|
43251
44923
|
}
|
|
43252
44924
|
if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
|
|
43253
44925
|
if (finishEvent) {
|
|
@@ -43257,10 +44929,11 @@ function NegotiatorBoard({
|
|
|
43257
44929
|
setShowHint(true);
|
|
43258
44930
|
}
|
|
43259
44931
|
}
|
|
43260
|
-
}, [isComplete, resolved, totalRounds, currentRound,
|
|
44932
|
+
}, [isComplete, resolved, totalRounds, currentRound, playRoundEvent, finishEvent, emit]);
|
|
43261
44933
|
const handleReset = React77.useCallback(() => {
|
|
43262
44934
|
setHistory([]);
|
|
43263
44935
|
setShowHint(false);
|
|
44936
|
+
prevRoundRef.current = 0;
|
|
43264
44937
|
if (playAgainEvent) {
|
|
43265
44938
|
emit(`UI:${playAgainEvent}`, {});
|
|
43266
44939
|
}
|
|
@@ -43371,6 +45044,236 @@ var init_NegotiatorBoard = __esm({
|
|
|
43371
45044
|
NegotiatorBoard.displayName = "NegotiatorBoard";
|
|
43372
45045
|
}
|
|
43373
45046
|
});
|
|
45047
|
+
function resolveEntity(entity) {
|
|
45048
|
+
if (!entity) return {};
|
|
45049
|
+
if (Array.isArray(entity)) return entity[0] ?? {};
|
|
45050
|
+
return entity;
|
|
45051
|
+
}
|
|
45052
|
+
function numField(row, key, fallback) {
|
|
45053
|
+
const v = row[key];
|
|
45054
|
+
return typeof v === "number" ? v : fallback;
|
|
45055
|
+
}
|
|
45056
|
+
function strField(row, key, fallback) {
|
|
45057
|
+
const v = row[key];
|
|
45058
|
+
return typeof v === "string" ? v : fallback;
|
|
45059
|
+
}
|
|
45060
|
+
function PlatformerBoard({
|
|
45061
|
+
entity,
|
|
45062
|
+
player: propPlayer,
|
|
45063
|
+
platforms: propPlatforms,
|
|
45064
|
+
result: propResult,
|
|
45065
|
+
lives: propLives,
|
|
45066
|
+
score: propScore,
|
|
45067
|
+
level: propLevel,
|
|
45068
|
+
worldWidth = 800,
|
|
45069
|
+
worldHeight = 400,
|
|
45070
|
+
canvasWidth = 800,
|
|
45071
|
+
canvasHeight = 400,
|
|
45072
|
+
playerSprite,
|
|
45073
|
+
tileSprites,
|
|
45074
|
+
bgColor,
|
|
45075
|
+
leftEvent = "LEFT",
|
|
45076
|
+
rightEvent = "RIGHT",
|
|
45077
|
+
jumpEvent = "JUMP",
|
|
45078
|
+
stopEvent = "STOP",
|
|
45079
|
+
playAgainEvent = "PLAY_AGAIN",
|
|
45080
|
+
gameEndEvent = "GAME_END",
|
|
45081
|
+
className
|
|
45082
|
+
}) {
|
|
45083
|
+
const row = resolveEntity(entity);
|
|
45084
|
+
const eventBus = useEventBus();
|
|
45085
|
+
const { t } = hooks.useTranslate();
|
|
45086
|
+
const result = propResult ?? strField(row, "result", "none");
|
|
45087
|
+
propLives ?? numField(row, "lives", 3);
|
|
45088
|
+
const score = propScore ?? numField(row, "score", 0);
|
|
45089
|
+
const level = propLevel ?? numField(row, "level", 1);
|
|
45090
|
+
const entityPlayer = row["player"];
|
|
45091
|
+
const player = propPlayer ?? entityPlayer ?? {
|
|
45092
|
+
x: 80,
|
|
45093
|
+
y: 320,
|
|
45094
|
+
width: 32,
|
|
45095
|
+
height: 48,
|
|
45096
|
+
vx: 0,
|
|
45097
|
+
vy: 0,
|
|
45098
|
+
grounded: false,
|
|
45099
|
+
facingRight: true
|
|
45100
|
+
};
|
|
45101
|
+
const entityPlatforms = Array.isArray(row["platforms"]) ? row["platforms"] : void 0;
|
|
45102
|
+
const platforms = propPlatforms ?? entityPlatforms ?? [
|
|
45103
|
+
{ x: 0, y: 368, width: 800, height: 32, type: "ground" },
|
|
45104
|
+
{ x: 150, y: 280, width: 160, height: 16, type: "platform" },
|
|
45105
|
+
{ x: 420, y: 220, width: 160, height: 16, type: "platform" },
|
|
45106
|
+
{ x: 580, y: 300, width: 80, height: 16, type: "hazard" },
|
|
45107
|
+
{ x: 700, y: 340, width: 64, height: 28, type: "goal" }
|
|
45108
|
+
];
|
|
45109
|
+
const handleRestart = React77.useCallback(() => {
|
|
45110
|
+
if (playAgainEvent) {
|
|
45111
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
45112
|
+
}
|
|
45113
|
+
}, [playAgainEvent, eventBus]);
|
|
45114
|
+
const isGameOver = result === "won" || result === "lost";
|
|
45115
|
+
const gameEndEmittedRef = React77.useRef(false);
|
|
45116
|
+
React77.useEffect(() => {
|
|
45117
|
+
if ((result === "won" || result === "lost") && gameEndEvent) {
|
|
45118
|
+
if (!gameEndEmittedRef.current) {
|
|
45119
|
+
gameEndEmittedRef.current = true;
|
|
45120
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
45121
|
+
}
|
|
45122
|
+
} else {
|
|
45123
|
+
gameEndEmittedRef.current = false;
|
|
45124
|
+
}
|
|
45125
|
+
}, [result, gameEndEvent, eventBus]);
|
|
45126
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
45127
|
+
exports.VStack,
|
|
45128
|
+
{
|
|
45129
|
+
className: cn("platformer-board relative bg-background", className),
|
|
45130
|
+
gap: "none",
|
|
45131
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "relative", children: [
|
|
45132
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
45133
|
+
PlatformerCanvas,
|
|
45134
|
+
{
|
|
45135
|
+
player,
|
|
45136
|
+
platforms,
|
|
45137
|
+
worldWidth,
|
|
45138
|
+
worldHeight,
|
|
45139
|
+
canvasWidth,
|
|
45140
|
+
canvasHeight,
|
|
45141
|
+
followCamera: true,
|
|
45142
|
+
bgColor,
|
|
45143
|
+
playerSprite,
|
|
45144
|
+
tileSprites,
|
|
45145
|
+
leftEvent,
|
|
45146
|
+
rightEvent,
|
|
45147
|
+
jumpEvent,
|
|
45148
|
+
stopEvent
|
|
45149
|
+
}
|
|
45150
|
+
),
|
|
45151
|
+
isGameOver && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
45152
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
45153
|
+
exports.Typography,
|
|
45154
|
+
{
|
|
45155
|
+
variant: "h2",
|
|
45156
|
+
className: cn(
|
|
45157
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
45158
|
+
result === "won" ? "text-warning" : "text-error"
|
|
45159
|
+
),
|
|
45160
|
+
children: result === "won" ? t("platformer.won") : t("platformer.lost")
|
|
45161
|
+
}
|
|
45162
|
+
),
|
|
45163
|
+
result === "won" && /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "body1", className: "text-muted-foreground", children: [
|
|
45164
|
+
t("platformer.level"),
|
|
45165
|
+
" ",
|
|
45166
|
+
level,
|
|
45167
|
+
" \u2014 ",
|
|
45168
|
+
t("platformer.score"),
|
|
45169
|
+
": ",
|
|
45170
|
+
score
|
|
45171
|
+
] }),
|
|
45172
|
+
result === "lost" && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body1", className: "text-muted-foreground", children: t("platformer.noLives") }),
|
|
45173
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
45174
|
+
exports.Button,
|
|
45175
|
+
{
|
|
45176
|
+
variant: "primary",
|
|
45177
|
+
className: "px-8 py-3 font-semibold",
|
|
45178
|
+
onClick: handleRestart,
|
|
45179
|
+
children: result === "won" ? t("platformer.nextLevel") : t("platformer.playAgain")
|
|
45180
|
+
}
|
|
45181
|
+
)
|
|
45182
|
+
] }) })
|
|
45183
|
+
] })
|
|
45184
|
+
}
|
|
45185
|
+
);
|
|
45186
|
+
}
|
|
45187
|
+
var init_PlatformerBoard = __esm({
|
|
45188
|
+
"components/game/organisms/PlatformerBoard.tsx"() {
|
|
45189
|
+
"use client";
|
|
45190
|
+
init_cn();
|
|
45191
|
+
init_useEventBus();
|
|
45192
|
+
init_Box();
|
|
45193
|
+
init_Button();
|
|
45194
|
+
init_Typography();
|
|
45195
|
+
init_Stack();
|
|
45196
|
+
init_PlatformerCanvas();
|
|
45197
|
+
PlatformerBoard.displayName = "PlatformerBoard";
|
|
45198
|
+
}
|
|
45199
|
+
});
|
|
45200
|
+
function PlatformerTemplate({
|
|
45201
|
+
entity,
|
|
45202
|
+
platforms = DEFAULT_PLATFORMS,
|
|
45203
|
+
worldWidth = 800,
|
|
45204
|
+
worldHeight = 400,
|
|
45205
|
+
canvasWidth = 800,
|
|
45206
|
+
canvasHeight = 400,
|
|
45207
|
+
playerSprite = `${CDN3}/characters/platformChar_idle.png`,
|
|
45208
|
+
tileSprites = DEFAULT_TILE_SPRITES,
|
|
45209
|
+
bgColor = "#5c94fc",
|
|
45210
|
+
gameEndEvent = "GAME_END",
|
|
45211
|
+
className
|
|
45212
|
+
}) {
|
|
45213
|
+
const row = entity && !Array.isArray(entity) ? entity : void 0;
|
|
45214
|
+
const lives = typeof row?.["lives"] === "number" ? row["lives"] : 3;
|
|
45215
|
+
const score = typeof row?.["score"] === "number" ? row["score"] : 0;
|
|
45216
|
+
const level = typeof row?.["level"] === "number" ? row["level"] : 1;
|
|
45217
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { className: cn("platformer-template w-full", className), gap: "none", children: [
|
|
45218
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
45219
|
+
GameHud,
|
|
45220
|
+
{
|
|
45221
|
+
position: "top",
|
|
45222
|
+
stats: [
|
|
45223
|
+
{ label: "Lives", value: lives, format: "number", variant: "danger" },
|
|
45224
|
+
{ label: "Level", value: level, format: "number", variant: "default" }
|
|
45225
|
+
]
|
|
45226
|
+
}
|
|
45227
|
+
) }),
|
|
45228
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
45229
|
+
PlatformerBoard,
|
|
45230
|
+
{
|
|
45231
|
+
entity,
|
|
45232
|
+
platforms,
|
|
45233
|
+
worldWidth,
|
|
45234
|
+
worldHeight,
|
|
45235
|
+
canvasWidth,
|
|
45236
|
+
canvasHeight,
|
|
45237
|
+
playerSprite,
|
|
45238
|
+
tileSprites,
|
|
45239
|
+
bgColor,
|
|
45240
|
+
leftEvent: "LEFT",
|
|
45241
|
+
rightEvent: "RIGHT",
|
|
45242
|
+
jumpEvent: "JUMP",
|
|
45243
|
+
stopEvent: "STOP",
|
|
45244
|
+
playAgainEvent: "PLAY_AGAIN",
|
|
45245
|
+
gameEndEvent
|
|
45246
|
+
}
|
|
45247
|
+
),
|
|
45248
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "px-4 py-2", children: /* @__PURE__ */ jsxRuntime.jsx(ScoreBoard, { score, level }) })
|
|
45249
|
+
] });
|
|
45250
|
+
}
|
|
45251
|
+
var CDN3, DEFAULT_TILE_SPRITES, DEFAULT_PLATFORMS;
|
|
45252
|
+
var init_PlatformerTemplate = __esm({
|
|
45253
|
+
"components/game/templates/PlatformerTemplate.tsx"() {
|
|
45254
|
+
init_cn();
|
|
45255
|
+
init_Box();
|
|
45256
|
+
init_Stack();
|
|
45257
|
+
init_PlatformerBoard();
|
|
45258
|
+
init_GameHud();
|
|
45259
|
+
init_ScoreBoard();
|
|
45260
|
+
CDN3 = "https://almadar-kflow-assets.web.app/shared/platformer";
|
|
45261
|
+
DEFAULT_TILE_SPRITES = {
|
|
45262
|
+
ground: `${CDN3}/tiles/platformPack_tile001.png`,
|
|
45263
|
+
platform: `${CDN3}/tiles/platformPack_tile004.png`,
|
|
45264
|
+
hazard: `${CDN3}/tiles/platformPack_tile017.png`,
|
|
45265
|
+
goal: `${CDN3}/tiles/platformPack_tile007.png`
|
|
45266
|
+
};
|
|
45267
|
+
DEFAULT_PLATFORMS = [
|
|
45268
|
+
{ x: 0, y: 368, width: 800, height: 32, type: "ground" },
|
|
45269
|
+
{ x: 150, y: 280, width: 160, height: 16, type: "platform" },
|
|
45270
|
+
{ x: 420, y: 220, width: 160, height: 16, type: "platform" },
|
|
45271
|
+
{ x: 580, y: 300, width: 80, height: 16, type: "hazard" },
|
|
45272
|
+
{ x: 700, y: 340, width: 64, height: 28, type: "goal" }
|
|
45273
|
+
];
|
|
45274
|
+
PlatformerTemplate.displayName = "PlatformerTemplate";
|
|
45275
|
+
}
|
|
45276
|
+
});
|
|
43374
45277
|
exports.PricingOrganism = void 0;
|
|
43375
45278
|
var init_PricingOrganism = __esm({
|
|
43376
45279
|
"components/marketing/organisms/PricingOrganism.tsx"() {
|
|
@@ -43494,6 +45397,7 @@ var init_PricingPageTemplate = __esm({
|
|
|
43494
45397
|
}
|
|
43495
45398
|
});
|
|
43496
45399
|
function ResourceCounter({
|
|
45400
|
+
assetUrl = DEFAULT_ASSET_URL12,
|
|
43497
45401
|
icon,
|
|
43498
45402
|
label = "Gold",
|
|
43499
45403
|
value = 250,
|
|
@@ -43513,7 +45417,17 @@ function ResourceCounter({
|
|
|
43513
45417
|
className
|
|
43514
45418
|
),
|
|
43515
45419
|
children: [
|
|
43516
|
-
|
|
45420
|
+
assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
45421
|
+
"img",
|
|
45422
|
+
{
|
|
45423
|
+
src: assetUrl,
|
|
45424
|
+
alt: label,
|
|
45425
|
+
width: sizes.img,
|
|
45426
|
+
height: sizes.img,
|
|
45427
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
45428
|
+
className: "flex-shrink-0"
|
|
45429
|
+
}
|
|
45430
|
+
) : icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon }) }) : null,
|
|
43517
45431
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: label }),
|
|
43518
45432
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("font-bold tabular-nums", color && (color in colorTokenClasses2 ? colorTokenClasses2[color] : color)), children: [
|
|
43519
45433
|
value,
|
|
@@ -43526,7 +45440,7 @@ function ResourceCounter({
|
|
|
43526
45440
|
}
|
|
43527
45441
|
);
|
|
43528
45442
|
}
|
|
43529
|
-
var colorTokenClasses2, sizeMap15;
|
|
45443
|
+
var colorTokenClasses2, DEFAULT_ASSET_URL12, sizeMap15;
|
|
43530
45444
|
var init_ResourceCounter = __esm({
|
|
43531
45445
|
"components/game/atoms/ResourceCounter.tsx"() {
|
|
43532
45446
|
init_cn();
|
|
@@ -43539,14 +45453,365 @@ var init_ResourceCounter = __esm({
|
|
|
43539
45453
|
error: "text-error",
|
|
43540
45454
|
muted: "text-muted-foreground"
|
|
43541
45455
|
};
|
|
45456
|
+
DEFAULT_ASSET_URL12 = "https://almadar-kflow-assets.web.app/shared/world-map/gold_mine.png";
|
|
43542
45457
|
sizeMap15 = {
|
|
43543
|
-
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm" },
|
|
43544
|
-
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base" },
|
|
43545
|
-
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg" }
|
|
45458
|
+
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm", img: 16 },
|
|
45459
|
+
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base", img: 20 },
|
|
45460
|
+
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg", img: 28 }
|
|
43546
45461
|
};
|
|
43547
45462
|
ResourceCounter.displayName = "ResourceCounter";
|
|
43548
45463
|
}
|
|
43549
45464
|
});
|
|
45465
|
+
function RoguelikeBoard({
|
|
45466
|
+
entity,
|
|
45467
|
+
tiles: propTiles,
|
|
45468
|
+
enemies: propEnemies,
|
|
45469
|
+
items: propItems,
|
|
45470
|
+
player: propPlayer,
|
|
45471
|
+
playerHp: propPlayerHp,
|
|
45472
|
+
playerMaxHp: propPlayerMaxHp,
|
|
45473
|
+
playerAttack: propPlayerAttack,
|
|
45474
|
+
depth: propDepth,
|
|
45475
|
+
result: propResult,
|
|
45476
|
+
phase: propPhase,
|
|
45477
|
+
stairsX: propStairsX,
|
|
45478
|
+
stairsY: propStairsY,
|
|
45479
|
+
assetManifest: propAssetManifest,
|
|
45480
|
+
scale = 0.45,
|
|
45481
|
+
unitScale = 1,
|
|
45482
|
+
moveEvent,
|
|
45483
|
+
playAgainEvent,
|
|
45484
|
+
gameEndEvent,
|
|
45485
|
+
className
|
|
45486
|
+
}) {
|
|
45487
|
+
const board = boardEntity(entity) ?? {};
|
|
45488
|
+
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : DEFAULT_TILES2);
|
|
45489
|
+
const enemies = propEnemies ?? (Array.isArray(board.enemies) ? board.enemies : DEFAULT_ENEMIES);
|
|
45490
|
+
const items = propItems ?? (Array.isArray(board.items) ? board.items : DEFAULT_ITEMS);
|
|
45491
|
+
const player = propPlayer ?? board.player ?? { x: 1, y: 1 };
|
|
45492
|
+
const playerHp = propPlayerHp ?? num(board.playerHp, 10);
|
|
45493
|
+
const playerMaxHp = propPlayerMaxHp ?? num(board.playerMaxHp, 10);
|
|
45494
|
+
const playerAttack = propPlayerAttack ?? num(board.playerAttack, 3);
|
|
45495
|
+
const depth = propDepth ?? num(board.depth, 1);
|
|
45496
|
+
const result = propResult ?? (str(board.result) || "none");
|
|
45497
|
+
const phase = propPhase ?? (str(board.phase) || "player_turn");
|
|
45498
|
+
propStairsX ?? num(board.stairsX, 3);
|
|
45499
|
+
propStairsY ?? num(board.stairsY, 3);
|
|
45500
|
+
const eventBus = useEventBus();
|
|
45501
|
+
const { t } = hooks.useTranslate();
|
|
45502
|
+
const [hoveredTile, setHoveredTile] = React77.useState(null);
|
|
45503
|
+
const emitMove = React77.useCallback((dx, dy) => {
|
|
45504
|
+
if (!moveEvent) return;
|
|
45505
|
+
eventBus.emit(`UI:${moveEvent}`, { dx, dy });
|
|
45506
|
+
}, [moveEvent, eventBus]);
|
|
45507
|
+
React77.useEffect(() => {
|
|
45508
|
+
const handler = (e) => {
|
|
45509
|
+
if (result !== "none" || phase !== "player_turn") return;
|
|
45510
|
+
if (e.key === "ArrowUp" || e.key === "w") emitMove(0, -1);
|
|
45511
|
+
if (e.key === "ArrowDown" || e.key === "s") emitMove(0, 1);
|
|
45512
|
+
if (e.key === "ArrowLeft" || e.key === "a") emitMove(-1, 0);
|
|
45513
|
+
if (e.key === "ArrowRight" || e.key === "d") emitMove(1, 0);
|
|
45514
|
+
};
|
|
45515
|
+
window.addEventListener("keydown", handler);
|
|
45516
|
+
return () => window.removeEventListener("keydown", handler);
|
|
45517
|
+
}, [result, phase, emitMove]);
|
|
45518
|
+
React77.useEffect(() => {
|
|
45519
|
+
if ((result === "won" || result === "lost") && gameEndEvent) {
|
|
45520
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
45521
|
+
}
|
|
45522
|
+
}, [result, gameEndEvent, eventBus]);
|
|
45523
|
+
const handleTileClick = React77.useCallback((x, y) => {
|
|
45524
|
+
if (result !== "none" || phase !== "player_turn") return;
|
|
45525
|
+
const dx = x - player.x;
|
|
45526
|
+
const dy = y - player.y;
|
|
45527
|
+
if (Math.abs(dx) + Math.abs(dy) === 1) {
|
|
45528
|
+
emitMove(dx, dy);
|
|
45529
|
+
}
|
|
45530
|
+
}, [result, phase, player, emitMove]);
|
|
45531
|
+
const handleReset = React77.useCallback(() => {
|
|
45532
|
+
if (!playAgainEvent) return;
|
|
45533
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
45534
|
+
}, [playAgainEvent, eventBus]);
|
|
45535
|
+
const isoUnits = React77.useMemo(() => {
|
|
45536
|
+
const liveEnemies = enemies.filter((e) => e.hp > 0);
|
|
45537
|
+
return [
|
|
45538
|
+
{
|
|
45539
|
+
id: "player",
|
|
45540
|
+
position: player,
|
|
45541
|
+
name: t("roguelike.player") || "Hero",
|
|
45542
|
+
team: "player",
|
|
45543
|
+
health: playerHp,
|
|
45544
|
+
maxHealth: playerMaxHp,
|
|
45545
|
+
unitType: "player",
|
|
45546
|
+
sprite: `${CDN4}/isometric-dungeon/Characters/Male/Male_0_Idle0.png`
|
|
45547
|
+
},
|
|
45548
|
+
...liveEnemies.map((e) => ({
|
|
45549
|
+
id: e.id,
|
|
45550
|
+
position: { x: e.x, y: e.y },
|
|
45551
|
+
name: t("roguelike.enemy") || "Enemy",
|
|
45552
|
+
team: "enemy",
|
|
45553
|
+
health: e.hp,
|
|
45554
|
+
maxHealth: 6,
|
|
45555
|
+
unitType: "enemy",
|
|
45556
|
+
sprite: `${CDN4}/sprite-sheets/shadow-legion-sprite-sheet-sw.png`
|
|
45557
|
+
}))
|
|
45558
|
+
];
|
|
45559
|
+
}, [player, enemies, playerHp, playerMaxHp, t]);
|
|
45560
|
+
const isoFeatures = React77.useMemo(
|
|
45561
|
+
() => items.map((it) => ({
|
|
45562
|
+
id: it.id,
|
|
45563
|
+
x: it.x,
|
|
45564
|
+
y: it.y,
|
|
45565
|
+
type: it.kind,
|
|
45566
|
+
sprite: FEATURE_SPRITE[it.kind] ?? FEATURE_SPRITE.health_potion
|
|
45567
|
+
})),
|
|
45568
|
+
[items]
|
|
45569
|
+
);
|
|
45570
|
+
const validMoves = React77.useMemo(() => {
|
|
45571
|
+
if (result !== "none" || phase !== "player_turn") return [];
|
|
45572
|
+
const dirs = [{ dx: 0, dy: -1 }, { dx: 0, dy: 1 }, { dx: -1, dy: 0 }, { dx: 1, dy: 0 }];
|
|
45573
|
+
return dirs.map(({ dx, dy }) => ({ x: player.x + dx, y: player.y + dy })).filter(({ x, y }) => {
|
|
45574
|
+
const tile = tiles.find((t2) => t2.x === x && t2.y === y);
|
|
45575
|
+
return tile?.passable ?? false;
|
|
45576
|
+
});
|
|
45577
|
+
}, [result, phase, player, tiles]);
|
|
45578
|
+
const isGameOver = result === "won" || result === "lost";
|
|
45579
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { className: cn("roguelike-board relative min-h-[600px] bg-background", className), gap: "none", children: [
|
|
45580
|
+
/* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { className: "items-center justify-between px-4 py-2 border-b border-border", gap: "md", children: [
|
|
45581
|
+
/* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", className: "items-center", children: [
|
|
45582
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.depth") || "Depth" }),
|
|
45583
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body1", className: "font-bold tabular-nums", children: depth })
|
|
45584
|
+
] }),
|
|
45585
|
+
/* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", className: "items-center", children: [
|
|
45586
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.hp") || "HP" }),
|
|
45587
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
45588
|
+
exports.Typography,
|
|
45589
|
+
{
|
|
45590
|
+
variant: "body1",
|
|
45591
|
+
className: cn(
|
|
45592
|
+
"font-bold tabular-nums",
|
|
45593
|
+
playerHp <= Math.floor(playerMaxHp * 0.25) ? "text-error" : "text-success"
|
|
45594
|
+
),
|
|
45595
|
+
children: [
|
|
45596
|
+
playerHp,
|
|
45597
|
+
" / ",
|
|
45598
|
+
playerMaxHp
|
|
45599
|
+
]
|
|
45600
|
+
}
|
|
45601
|
+
)
|
|
45602
|
+
] }),
|
|
45603
|
+
/* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", className: "items-center", children: [
|
|
45604
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.atk") || "ATK" }),
|
|
45605
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body1", className: "font-bold tabular-nums text-foreground", children: playerAttack })
|
|
45606
|
+
] }),
|
|
45607
|
+
/* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", className: "items-center", children: [
|
|
45608
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.phase") || "Phase" }),
|
|
45609
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "capitalize text-foreground", children: phase.replace("_", " ") })
|
|
45610
|
+
] })
|
|
45611
|
+
] }),
|
|
45612
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "relative flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
45613
|
+
IsometricCanvas_default,
|
|
45614
|
+
{
|
|
45615
|
+
tiles,
|
|
45616
|
+
units: isoUnits,
|
|
45617
|
+
features: isoFeatures,
|
|
45618
|
+
selectedUnitId: null,
|
|
45619
|
+
validMoves,
|
|
45620
|
+
attackTargets: [],
|
|
45621
|
+
hoveredTile,
|
|
45622
|
+
onTileClick: handleTileClick,
|
|
45623
|
+
onUnitClick: () => void 0,
|
|
45624
|
+
onTileHover: (x, y) => setHoveredTile({ x, y }),
|
|
45625
|
+
onTileLeave: () => setHoveredTile(null),
|
|
45626
|
+
scale,
|
|
45627
|
+
assetBaseUrl: propAssetManifest?.baseUrl ?? CDN4,
|
|
45628
|
+
assetManifest: propAssetManifest,
|
|
45629
|
+
unitScale
|
|
45630
|
+
}
|
|
45631
|
+
) }),
|
|
45632
|
+
!isGameOver && /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { className: "justify-center gap-2 p-3", gap: "none", children: [
|
|
45633
|
+
{ label: "\u2191", dx: 0, dy: -1 },
|
|
45634
|
+
{ label: "\u2193", dx: 0, dy: 1 },
|
|
45635
|
+
{ label: "\u2190", dx: -1, dy: 0 },
|
|
45636
|
+
{ label: "\u2192", dx: 1, dy: 0 }
|
|
45637
|
+
].map(({ label, dx, dy }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
45638
|
+
exports.Button,
|
|
45639
|
+
{
|
|
45640
|
+
variant: "secondary",
|
|
45641
|
+
className: "w-9 h-9 p-0 text-base font-mono",
|
|
45642
|
+
onClick: () => emitMove(dx, dy),
|
|
45643
|
+
disabled: phase !== "player_turn",
|
|
45644
|
+
children: label
|
|
45645
|
+
},
|
|
45646
|
+
label
|
|
45647
|
+
)) }),
|
|
45648
|
+
isGameOver && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/75 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
45649
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
45650
|
+
exports.Typography,
|
|
45651
|
+
{
|
|
45652
|
+
variant: "h2",
|
|
45653
|
+
className: cn(
|
|
45654
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
45655
|
+
result === "won" ? "text-warning" : "text-error"
|
|
45656
|
+
),
|
|
45657
|
+
children: result === "won" ? t("roguelike.victory") || "Victory!" : t("roguelike.defeat") || "Defeated!"
|
|
45658
|
+
}
|
|
45659
|
+
),
|
|
45660
|
+
result === "won" && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body1", className: "text-muted-foreground", children: t("roguelike.cleared") || `Dungeon cleared! Depth reached: ${depth}` }),
|
|
45661
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
45662
|
+
exports.Button,
|
|
45663
|
+
{
|
|
45664
|
+
variant: "primary",
|
|
45665
|
+
className: "px-8 py-3 font-semibold",
|
|
45666
|
+
onClick: handleReset,
|
|
45667
|
+
children: t("roguelike.playAgain") || "Play Again"
|
|
45668
|
+
}
|
|
45669
|
+
)
|
|
45670
|
+
] }) })
|
|
45671
|
+
] });
|
|
45672
|
+
}
|
|
45673
|
+
var CDN4, DEFAULT_TILES2, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
|
|
45674
|
+
var init_RoguelikeBoard = __esm({
|
|
45675
|
+
"components/game/organisms/RoguelikeBoard.tsx"() {
|
|
45676
|
+
"use client";
|
|
45677
|
+
init_cn();
|
|
45678
|
+
init_useEventBus();
|
|
45679
|
+
init_Box();
|
|
45680
|
+
init_Button();
|
|
45681
|
+
init_Typography();
|
|
45682
|
+
init_Stack();
|
|
45683
|
+
init_IsometricCanvas();
|
|
45684
|
+
init_boardEntity();
|
|
45685
|
+
CDN4 = "https://almadar-kflow-assets.web.app/shared";
|
|
45686
|
+
DEFAULT_TILES2 = [
|
|
45687
|
+
{ x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45688
|
+
{ x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45689
|
+
{ x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45690
|
+
{ x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45691
|
+
{ x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45692
|
+
{ x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45693
|
+
{ x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45694
|
+
{ x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45695
|
+
{ x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45696
|
+
{ x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45697
|
+
{ x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45698
|
+
{ x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
45699
|
+
{ x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
45700
|
+
{ x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45701
|
+
{ x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45702
|
+
{ x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45703
|
+
{ x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45704
|
+
{ x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
45705
|
+
{ x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` },
|
|
45706
|
+
{ x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45707
|
+
{ x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45708
|
+
{ x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45709
|
+
{ x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45710
|
+
{ x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45711
|
+
{ x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` }
|
|
45712
|
+
];
|
|
45713
|
+
DEFAULT_ENEMIES = [
|
|
45714
|
+
{ id: "e1", x: 3, y: 1, hp: 5, attack: 2 },
|
|
45715
|
+
{ id: "e2", x: 1, y: 3, hp: 4, attack: 1 }
|
|
45716
|
+
];
|
|
45717
|
+
DEFAULT_ITEMS = [
|
|
45718
|
+
{ id: "i1", x: 2, y: 2, kind: "health_potion" }
|
|
45719
|
+
];
|
|
45720
|
+
FEATURE_SPRITE = {
|
|
45721
|
+
health_potion: `${CDN4}/isometric-dungeon/Isometric/chestClosed_E.png`,
|
|
45722
|
+
sword: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`,
|
|
45723
|
+
shield: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`
|
|
45724
|
+
};
|
|
45725
|
+
RoguelikeBoard.displayName = "RoguelikeBoard";
|
|
45726
|
+
}
|
|
45727
|
+
});
|
|
45728
|
+
function RoguelikeTemplate({
|
|
45729
|
+
entity,
|
|
45730
|
+
scale = 0.45,
|
|
45731
|
+
unitScale = 1,
|
|
45732
|
+
tiles = DEFAULT_ROGUELIKE_TILES,
|
|
45733
|
+
enemies,
|
|
45734
|
+
items,
|
|
45735
|
+
stairsX,
|
|
45736
|
+
stairsY,
|
|
45737
|
+
assetManifest = DEFAULT_ROGUELIKE_MANIFEST,
|
|
45738
|
+
gameEndEvent,
|
|
45739
|
+
className
|
|
45740
|
+
}) {
|
|
45741
|
+
const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
|
|
45742
|
+
if (!resolved && !tiles && !assetManifest) return null;
|
|
45743
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
45744
|
+
RoguelikeBoard,
|
|
45745
|
+
{
|
|
45746
|
+
entity: resolved,
|
|
45747
|
+
tiles,
|
|
45748
|
+
enemies,
|
|
45749
|
+
items,
|
|
45750
|
+
stairsX,
|
|
45751
|
+
stairsY,
|
|
45752
|
+
assetManifest,
|
|
45753
|
+
scale,
|
|
45754
|
+
unitScale,
|
|
45755
|
+
moveEvent: "MOVE",
|
|
45756
|
+
playAgainEvent: "PLAY_AGAIN",
|
|
45757
|
+
gameEndEvent: gameEndEvent ?? "GAME_END",
|
|
45758
|
+
className
|
|
45759
|
+
}
|
|
45760
|
+
);
|
|
45761
|
+
}
|
|
45762
|
+
var CDN5, DEFAULT_ROGUELIKE_TILES, DEFAULT_ROGUELIKE_MANIFEST;
|
|
45763
|
+
var init_RoguelikeTemplate = __esm({
|
|
45764
|
+
"components/game/templates/RoguelikeTemplate.tsx"() {
|
|
45765
|
+
init_RoguelikeBoard();
|
|
45766
|
+
CDN5 = "https://almadar-kflow-assets.web.app/shared";
|
|
45767
|
+
DEFAULT_ROGUELIKE_TILES = [
|
|
45768
|
+
{ x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45769
|
+
{ x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45770
|
+
{ x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45771
|
+
{ x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45772
|
+
{ x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45773
|
+
{ x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45774
|
+
{ x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45775
|
+
{ x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45776
|
+
{ x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45777
|
+
{ x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45778
|
+
{ x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45779
|
+
{ x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
45780
|
+
{ x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
45781
|
+
{ x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45782
|
+
{ x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45783
|
+
{ x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45784
|
+
{ x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45785
|
+
{ x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
45786
|
+
{ x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stairs_E.png` },
|
|
45787
|
+
{ x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45788
|
+
{ x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45789
|
+
{ x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45790
|
+
{ x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45791
|
+
{ x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45792
|
+
{ x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` }
|
|
45793
|
+
];
|
|
45794
|
+
DEFAULT_ROGUELIKE_MANIFEST = {
|
|
45795
|
+
baseUrl: CDN5,
|
|
45796
|
+
terrains: {
|
|
45797
|
+
floor: "/isometric-dungeon/Isometric/dirt_E.png",
|
|
45798
|
+
wall: "/isometric-dungeon/Isometric/stoneInset_E.png",
|
|
45799
|
+
stairs: "/isometric-dungeon/Isometric/stairs_E.png",
|
|
45800
|
+
planks: "/isometric-dungeon/Isometric/planks_E.png"
|
|
45801
|
+
},
|
|
45802
|
+
units: {
|
|
45803
|
+
player: "/isometric-dungeon/Characters/Male/Male_0_Idle0.png",
|
|
45804
|
+
enemy: "/sprite-sheets/shadow-legion-sprite-sheet-sw.png"
|
|
45805
|
+
},
|
|
45806
|
+
features: {
|
|
45807
|
+
health_potion: "/isometric-dungeon/Isometric/chestClosed_E.png",
|
|
45808
|
+
sword: "/isometric-dungeon/Isometric/barrel_E.png",
|
|
45809
|
+
shield: "/isometric-dungeon/Isometric/barrel_E.png"
|
|
45810
|
+
}
|
|
45811
|
+
};
|
|
45812
|
+
RoguelikeTemplate.displayName = "RoguelikeTemplate";
|
|
45813
|
+
}
|
|
45814
|
+
});
|
|
43550
45815
|
|
|
43551
45816
|
// lib/traitRegistry.ts
|
|
43552
45817
|
function getAllTraits() {
|
|
@@ -44781,7 +47046,7 @@ function getAllEvents(traits2) {
|
|
|
44781
47046
|
function EventDispatcherTab({ traits: traits2, schema }) {
|
|
44782
47047
|
const eventBus = useEventBus();
|
|
44783
47048
|
const { t } = hooks.useTranslate();
|
|
44784
|
-
const [
|
|
47049
|
+
const [log11, setLog] = React77__namespace.useState([]);
|
|
44785
47050
|
const prevStatesRef = React77__namespace.useRef(/* @__PURE__ */ new Map());
|
|
44786
47051
|
React77__namespace.useEffect(() => {
|
|
44787
47052
|
for (const trait of traits2) {
|
|
@@ -44845,9 +47110,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
|
|
|
44845
47110
|
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.otherEvents") }),
|
|
44846
47111
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1", children: unavailableEvents.map((event) => /* @__PURE__ */ jsxRuntime.jsx(exports.Badge, { variant: "default", size: "sm", className: "opacity-50", children: event }, event)) })
|
|
44847
47112
|
] }),
|
|
44848
|
-
|
|
47113
|
+
log11.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
44849
47114
|
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.recentTransitions") }),
|
|
44850
|
-
/* @__PURE__ */ jsxRuntime.jsx(exports.Stack, { gap: "xs", children:
|
|
47115
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Stack, { gap: "xs", children: log11.map((entry, i) => /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "small", className: "font-mono text-xs", children: [
|
|
44851
47116
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-purple-400", children: entry.traitName }),
|
|
44852
47117
|
" ",
|
|
44853
47118
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-500", children: entry.from }),
|
|
@@ -45650,17 +47915,24 @@ function SequencerBoard({
|
|
|
45650
47915
|
const { emit } = useEventBus();
|
|
45651
47916
|
const { t } = hooks.useTranslate();
|
|
45652
47917
|
const resolved = boardEntity(entity);
|
|
45653
|
-
const maxSlots = num(resolved?.maxSlots);
|
|
47918
|
+
const maxSlots = num(resolved?.maxSlots) || 3;
|
|
45654
47919
|
const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
|
|
45655
47920
|
const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
|
|
45656
47921
|
const allowDuplicates = resolved?.allowDuplicates !== false;
|
|
47922
|
+
const entitySlots = Array.isArray(resolved?.slots) ? resolved.slots : [];
|
|
47923
|
+
const entityResult = str(resolved?.result);
|
|
47924
|
+
const entityAttempts = num(resolved?.attempts);
|
|
47925
|
+
const entityCurrentStep = typeof resolved?.currentStep === "number" ? resolved.currentStep : -1;
|
|
47926
|
+
const slots = Array.from({ length: maxSlots }, (_, i) => {
|
|
47927
|
+
const entitySlot = entitySlots.find((s) => s.index === i);
|
|
47928
|
+
if (!entitySlot?.placedActionId) return void 0;
|
|
47929
|
+
return availableActions.find((a) => a.id === entitySlot.placedActionId);
|
|
47930
|
+
});
|
|
47931
|
+
const isSuccess = entityResult === "win";
|
|
47932
|
+
const attempts = entityAttempts;
|
|
47933
|
+
const isPlayingBack = entityCurrentStep >= 0 && !isSuccess;
|
|
47934
|
+
const currentStep = entityCurrentStep;
|
|
45657
47935
|
const [headerError, setHeaderError] = React77.useState(false);
|
|
45658
|
-
const [slots, setSlots] = React77.useState(
|
|
45659
|
-
() => Array.from({ length: maxSlots }, () => void 0)
|
|
45660
|
-
);
|
|
45661
|
-
const [playState, setPlayState] = React77.useState("idle");
|
|
45662
|
-
const [currentStep, setCurrentStep] = React77.useState(-1);
|
|
45663
|
-
const [attempts, setAttempts] = React77.useState(0);
|
|
45664
47936
|
const [slotFeedback, setSlotFeedback] = React77.useState(
|
|
45665
47937
|
() => Array.from({ length: maxSlots }, () => null)
|
|
45666
47938
|
);
|
|
@@ -45669,11 +47941,6 @@ function SequencerBoard({
|
|
|
45669
47941
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
45670
47942
|
}, []);
|
|
45671
47943
|
const handleSlotDrop = React77.useCallback((index, item) => {
|
|
45672
|
-
setSlots((prev) => {
|
|
45673
|
-
const next = [...prev];
|
|
45674
|
-
next[index] = item;
|
|
45675
|
-
return next;
|
|
45676
|
-
});
|
|
45677
47944
|
setSlotFeedback((prev) => {
|
|
45678
47945
|
const next = [...prev];
|
|
45679
47946
|
next[index] = null;
|
|
@@ -45683,11 +47950,6 @@ function SequencerBoard({
|
|
|
45683
47950
|
if (placeEvent) emit(`UI:${placeEvent}`, { slotIndex: index, actionId: item.id });
|
|
45684
47951
|
}, [emit, placeEvent]);
|
|
45685
47952
|
const handleSlotRemove = React77.useCallback((index) => {
|
|
45686
|
-
setSlots((prev) => {
|
|
45687
|
-
const next = [...prev];
|
|
45688
|
-
next[index] = void 0;
|
|
45689
|
-
return next;
|
|
45690
|
-
});
|
|
45691
47953
|
setSlotFeedback((prev) => {
|
|
45692
47954
|
const next = [...prev];
|
|
45693
47955
|
next[index] = null;
|
|
@@ -45698,48 +47960,34 @@ function SequencerBoard({
|
|
|
45698
47960
|
}, [emit, removeEvent]);
|
|
45699
47961
|
const handleReset = React77.useCallback(() => {
|
|
45700
47962
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
45701
|
-
setSlots(Array.from({ length: maxSlots }, () => void 0));
|
|
45702
|
-
setPlayState("idle");
|
|
45703
|
-
setCurrentStep(-1);
|
|
45704
|
-
setAttempts(0);
|
|
45705
47963
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
45706
47964
|
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
45707
47965
|
}, [maxSlots, playAgainEvent, emit]);
|
|
45708
47966
|
const filledSlots = slots.filter((s) => !!s);
|
|
45709
|
-
const canPlay = filledSlots.length > 0 &&
|
|
47967
|
+
const canPlay = filledSlots.length > 0 && !isPlayingBack && !isSuccess;
|
|
45710
47968
|
const handlePlay = React77.useCallback(() => {
|
|
45711
47969
|
if (!canPlay) return;
|
|
45712
47970
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
45713
47971
|
emit("UI:PLAY_SOUND", { key: "confirm" });
|
|
45714
47972
|
const sequence = slots.map((s) => s?.id || "");
|
|
45715
|
-
|
|
45716
|
-
|
|
45717
|
-
}
|
|
45718
|
-
setPlayState("playing");
|
|
45719
|
-
setCurrentStep(0);
|
|
47973
|
+
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
47974
|
+
if (playEvent) emit(`UI:${playEvent}`, { sequence });
|
|
45720
47975
|
let step = 0;
|
|
45721
47976
|
const advance = () => {
|
|
45722
47977
|
step++;
|
|
47978
|
+
emit("UI:STEP", { step });
|
|
45723
47979
|
if (step >= maxSlots) {
|
|
47980
|
+
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
45724
47981
|
const playerSeq = slots.map((s) => s?.id);
|
|
45725
|
-
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
45726
47982
|
const success = solutions.some(
|
|
45727
47983
|
(sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
|
|
45728
47984
|
);
|
|
45729
|
-
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
45730
47985
|
if (success) {
|
|
45731
|
-
setPlayState("success");
|
|
45732
|
-
setCurrentStep(-1);
|
|
45733
47986
|
emit("UI:PLAY_SOUND", { key: "levelComplete" });
|
|
45734
|
-
if (completeEvent) {
|
|
45735
|
-
emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
45736
|
-
}
|
|
47987
|
+
if (completeEvent) emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
45737
47988
|
} else {
|
|
45738
|
-
setAttempts((prev) => prev + 1);
|
|
45739
47989
|
const feedback = computeSlotFeedback(playerSeq, solutions);
|
|
45740
47990
|
setSlotFeedback(feedback);
|
|
45741
|
-
setPlayState("idle");
|
|
45742
|
-
setCurrentStep(-1);
|
|
45743
47991
|
emit("UI:PLAY_SOUND", { key: "fail" });
|
|
45744
47992
|
const correctCount2 = feedback.filter((f3) => f3 === "correct").length;
|
|
45745
47993
|
for (let ci = 0; ci < correctCount2; ci++) {
|
|
@@ -45749,7 +47997,6 @@ function SequencerBoard({
|
|
|
45749
47997
|
}
|
|
45750
47998
|
}
|
|
45751
47999
|
} else {
|
|
45752
|
-
setCurrentStep(step);
|
|
45753
48000
|
timerRef.current = setTimeout(advance, stepDurationMs);
|
|
45754
48001
|
}
|
|
45755
48002
|
};
|
|
@@ -45799,7 +48046,7 @@ function SequencerBoard({
|
|
|
45799
48046
|
/* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", children: [
|
|
45800
48047
|
/* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { className: "items-center justify-between", children: [
|
|
45801
48048
|
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
|
|
45802
|
-
hasFeedback &&
|
|
48049
|
+
hasFeedback && !isPlayingBack && !isSuccess && /* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "caption", className: "text-muted-foreground", children: [
|
|
45803
48050
|
`${correctCount}/${maxSlots} `,
|
|
45804
48051
|
"\u2705"
|
|
45805
48052
|
] })
|
|
@@ -45811,7 +48058,7 @@ function SequencerBoard({
|
|
|
45811
48058
|
maxSlots,
|
|
45812
48059
|
onSlotDrop: handleSlotDrop,
|
|
45813
48060
|
onSlotRemove: handleSlotRemove,
|
|
45814
|
-
playing:
|
|
48061
|
+
playing: isPlayingBack,
|
|
45815
48062
|
currentStep,
|
|
45816
48063
|
categoryColors,
|
|
45817
48064
|
slotFeedback,
|
|
@@ -45819,7 +48066,7 @@ function SequencerBoard({
|
|
|
45819
48066
|
}
|
|
45820
48067
|
)
|
|
45821
48068
|
] }),
|
|
45822
|
-
|
|
48069
|
+
!isPlayingBack && /* @__PURE__ */ jsxRuntime.jsx(
|
|
45823
48070
|
ActionPalette,
|
|
45824
48071
|
{
|
|
45825
48072
|
actions: availableActions,
|
|
@@ -45829,8 +48076,8 @@ function SequencerBoard({
|
|
|
45829
48076
|
label: t("sequencer.dragActions")
|
|
45830
48077
|
}
|
|
45831
48078
|
),
|
|
45832
|
-
hasFeedback &&
|
|
45833
|
-
|
|
48079
|
+
hasFeedback && !isPlayingBack && !isSuccess && attempts > 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-3 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-foreground", children: t(encourageKey) }) }),
|
|
48080
|
+
isSuccess && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("sequencer.levelComplete") }) }),
|
|
45834
48081
|
/* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", children: [
|
|
45835
48082
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
45836
48083
|
exports.Button,
|
|
@@ -46897,9 +49144,9 @@ function VariablePanel({
|
|
|
46897
49144
|
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
|
|
46898
49145
|
variables.map((v) => {
|
|
46899
49146
|
const name = v.name == null ? "" : String(v.name);
|
|
46900
|
-
const value =
|
|
46901
|
-
const max =
|
|
46902
|
-
const min =
|
|
49147
|
+
const value = numField2(v.value);
|
|
49148
|
+
const max = numField2(v.max, 100);
|
|
49149
|
+
const min = numField2(v.min, 0);
|
|
46903
49150
|
const unit = v.unit == null ? "" : String(v.unit);
|
|
46904
49151
|
const pct = Math.round((value - min) / (max - min) * 100);
|
|
46905
49152
|
const isHigh = pct > 80;
|
|
@@ -46929,12 +49176,12 @@ function VariablePanel({
|
|
|
46929
49176
|
})
|
|
46930
49177
|
] });
|
|
46931
49178
|
}
|
|
46932
|
-
var
|
|
49179
|
+
var numField2;
|
|
46933
49180
|
var init_VariablePanel = __esm({
|
|
46934
49181
|
"components/game/organisms/puzzles/state-architect/VariablePanel.tsx"() {
|
|
46935
49182
|
init_atoms2();
|
|
46936
49183
|
init_cn();
|
|
46937
|
-
|
|
49184
|
+
numField2 = (v, fallback = 0) => {
|
|
46938
49185
|
const n = Number(v);
|
|
46939
49186
|
return Number.isFinite(n) ? n : fallback;
|
|
46940
49187
|
};
|
|
@@ -46991,6 +49238,9 @@ function StateArchitectBoard({
|
|
|
46991
49238
|
stepDurationMs = 600,
|
|
46992
49239
|
testEvent,
|
|
46993
49240
|
completeEvent,
|
|
49241
|
+
addTransitionEvent,
|
|
49242
|
+
removeTransitionEvent,
|
|
49243
|
+
playAgainEvent,
|
|
46994
49244
|
className
|
|
46995
49245
|
}) {
|
|
46996
49246
|
const { emit } = useEventBus();
|
|
@@ -47003,14 +49253,16 @@ function StateArchitectBoard({
|
|
|
47003
49253
|
const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
|
|
47004
49254
|
const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
|
|
47005
49255
|
const entityVariables = rows(resolved?.variables);
|
|
47006
|
-
const
|
|
49256
|
+
const transitions = entityTransitions;
|
|
49257
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
49258
|
+
const entityResult = str(resolved?.result) || "none";
|
|
49259
|
+
const isSuccess = entityResult === "win";
|
|
49260
|
+
const [isTesting, setIsTesting] = React77.useState(false);
|
|
47007
49261
|
const [headerError, setHeaderError] = React77.useState(false);
|
|
47008
|
-
const [playState, setPlayState] = React77.useState("editing");
|
|
47009
49262
|
const [currentState, setCurrentState] = React77.useState(initialState);
|
|
47010
49263
|
const [selectedState, setSelectedState] = React77.useState(null);
|
|
47011
49264
|
const [testResults, setTestResults] = React77.useState([]);
|
|
47012
49265
|
const [variables, setVariables] = React77.useState(() => [...entityVariables]);
|
|
47013
|
-
const [attempts, setAttempts] = React77.useState(0);
|
|
47014
49266
|
const timerRef = React77.useRef(null);
|
|
47015
49267
|
const [addingFrom, setAddingFrom] = React77.useState(null);
|
|
47016
49268
|
React77.useEffect(() => () => {
|
|
@@ -47020,7 +49272,7 @@ function StateArchitectBoard({
|
|
|
47020
49272
|
const GRAPH_H = 400;
|
|
47021
49273
|
const positions = React77.useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
|
|
47022
49274
|
const handleStateClick = React77.useCallback((state) => {
|
|
47023
|
-
if (
|
|
49275
|
+
if (isTesting) return;
|
|
47024
49276
|
if (addingFrom) {
|
|
47025
49277
|
if (addingFrom !== state) {
|
|
47026
49278
|
const event = availableEvents[0] || "EVENT";
|
|
@@ -47030,20 +49282,20 @@ function StateArchitectBoard({
|
|
|
47030
49282
|
to: state,
|
|
47031
49283
|
event
|
|
47032
49284
|
};
|
|
47033
|
-
|
|
49285
|
+
if (addTransitionEvent) emit(`UI:${addTransitionEvent}`, { id: newTrans.id, from: newTrans.from, to: newTrans.to, event: newTrans.event });
|
|
47034
49286
|
}
|
|
47035
49287
|
setAddingFrom(null);
|
|
47036
49288
|
} else {
|
|
47037
49289
|
setSelectedState(state);
|
|
47038
49290
|
}
|
|
47039
|
-
}, [
|
|
49291
|
+
}, [isTesting, addingFrom, availableEvents, addTransitionEvent, emit]);
|
|
47040
49292
|
const handleStartAddTransition = React77.useCallback(() => {
|
|
47041
49293
|
if (!selectedState) return;
|
|
47042
49294
|
setAddingFrom(selectedState);
|
|
47043
49295
|
}, [selectedState]);
|
|
47044
49296
|
const handleRemoveTransition = React77.useCallback((transId) => {
|
|
47045
|
-
|
|
47046
|
-
}, []);
|
|
49297
|
+
if (removeTransitionEvent) emit(`UI:${removeTransitionEvent}`, { id: transId });
|
|
49298
|
+
}, [removeTransitionEvent, emit]);
|
|
47047
49299
|
const machine = React77.useMemo(() => ({
|
|
47048
49300
|
name: entityName,
|
|
47049
49301
|
description: str(resolved?.description),
|
|
@@ -47057,16 +49309,16 @@ function StateArchitectBoard({
|
|
|
47057
49309
|
}))
|
|
47058
49310
|
}), [entityName, resolved, entityStates, currentState, transitions]);
|
|
47059
49311
|
const handleTest = React77.useCallback(() => {
|
|
47060
|
-
if (
|
|
49312
|
+
if (isTesting) return;
|
|
47061
49313
|
if (testEvent) emit(`UI:${testEvent}`, {});
|
|
47062
|
-
|
|
49314
|
+
setIsTesting(true);
|
|
47063
49315
|
setTestResults([]);
|
|
47064
49316
|
const results = [];
|
|
47065
49317
|
let testIdx = 0;
|
|
47066
49318
|
const runNextTest = () => {
|
|
47067
49319
|
if (testIdx >= testCases.length) {
|
|
47068
49320
|
const allPassed = results.every((r) => r.passed);
|
|
47069
|
-
|
|
49321
|
+
setIsTesting(false);
|
|
47070
49322
|
setTestResults(results);
|
|
47071
49323
|
if (allPassed && completeEvent) {
|
|
47072
49324
|
emit(`UI:${completeEvent}`, {
|
|
@@ -47074,9 +49326,6 @@ function StateArchitectBoard({
|
|
|
47074
49326
|
passedTests: results.filter((r) => r.passed).length
|
|
47075
49327
|
});
|
|
47076
49328
|
}
|
|
47077
|
-
if (!allPassed) {
|
|
47078
|
-
setAttempts((prev) => prev + 1);
|
|
47079
|
-
}
|
|
47080
49329
|
return;
|
|
47081
49330
|
}
|
|
47082
49331
|
const testCase = testCases[testIdx];
|
|
@@ -47099,24 +49348,23 @@ function StateArchitectBoard({
|
|
|
47099
49348
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
47100
49349
|
};
|
|
47101
49350
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
47102
|
-
}, [
|
|
49351
|
+
}, [isTesting, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
|
|
47103
49352
|
const handleTryAgain = React77.useCallback(() => {
|
|
47104
49353
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
47105
|
-
|
|
49354
|
+
setIsTesting(false);
|
|
47106
49355
|
setCurrentState(initialState);
|
|
47107
49356
|
setTestResults([]);
|
|
47108
49357
|
}, [initialState]);
|
|
47109
49358
|
const handleReset = React77.useCallback(() => {
|
|
47110
49359
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
47111
|
-
|
|
47112
|
-
setPlayState("editing");
|
|
49360
|
+
setIsTesting(false);
|
|
47113
49361
|
setCurrentState(initialState);
|
|
47114
49362
|
setTestResults([]);
|
|
47115
49363
|
setVariables([...entityVariables]);
|
|
47116
49364
|
setSelectedState(null);
|
|
47117
49365
|
setAddingFrom(null);
|
|
47118
|
-
|
|
47119
|
-
}, [
|
|
49366
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
49367
|
+
}, [initialState, entityVariables, playAgainEvent, emit]);
|
|
47120
49368
|
const codeData = React77.useMemo(() => ({
|
|
47121
49369
|
name: entityName,
|
|
47122
49370
|
states: entityStates,
|
|
@@ -47212,7 +49460,7 @@ function StateArchitectBoard({
|
|
|
47212
49460
|
]
|
|
47213
49461
|
}
|
|
47214
49462
|
),
|
|
47215
|
-
|
|
49463
|
+
!isTesting && /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "sm", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
47216
49464
|
exports.Button,
|
|
47217
49465
|
{
|
|
47218
49466
|
variant: "ghost",
|
|
@@ -47234,7 +49482,7 @@ function StateArchitectBoard({
|
|
|
47234
49482
|
t2.guardHint,
|
|
47235
49483
|
")"
|
|
47236
49484
|
] }),
|
|
47237
|
-
|
|
49485
|
+
!isTesting && /* @__PURE__ */ jsxRuntime.jsx(
|
|
47238
49486
|
exports.Button,
|
|
47239
49487
|
{
|
|
47240
49488
|
variant: "ghost",
|
|
@@ -47266,21 +49514,21 @@ function StateArchitectBoard({
|
|
|
47266
49514
|
resolved.showCodeView !== false && /* @__PURE__ */ jsxRuntime.jsx(StateJsonView, { data: codeData, label: "View Code" })
|
|
47267
49515
|
] })
|
|
47268
49516
|
] }),
|
|
47269
|
-
|
|
47270
|
-
|
|
49517
|
+
isSuccess && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("stateArchitect.allPassed") }) }),
|
|
49518
|
+
!isTesting && !isSuccess && testResults.some((r) => !r.passed) && /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", children: [
|
|
47271
49519
|
/* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body1", className: "text-foreground font-medium", children: t(ENCOURAGEMENT_KEYS3[Math.min(attempts - 1, ENCOURAGEMENT_KEYS3.length - 1)] ?? ENCOURAGEMENT_KEYS3[0]) }) }),
|
|
47272
|
-
attempts >=
|
|
49520
|
+
!isSuccess && attempts >= 2 && Boolean(str(resolved?.hint)) && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { className: "items-start", gap: "xs", children: [
|
|
47273
49521
|
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
47274
49522
|
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", className: "text-foreground", children: hint })
|
|
47275
49523
|
] }) })
|
|
47276
49524
|
] }),
|
|
47277
49525
|
/* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", children: [
|
|
47278
|
-
|
|
49526
|
+
!isTesting && !isSuccess && testResults.some((r) => !r.passed) ? /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
47279
49527
|
exports.Button,
|
|
47280
49528
|
{
|
|
47281
49529
|
variant: "primary",
|
|
47282
49530
|
onClick: handleTest,
|
|
47283
|
-
disabled:
|
|
49531
|
+
disabled: isTesting,
|
|
47284
49532
|
children: "\u25B6 " + t("game.runTests")
|
|
47285
49533
|
}
|
|
47286
49534
|
),
|
|
@@ -47360,6 +49608,7 @@ function formatDuration(seconds) {
|
|
|
47360
49608
|
return `${Math.round(seconds)}s`;
|
|
47361
49609
|
}
|
|
47362
49610
|
function StatusEffect({
|
|
49611
|
+
assetUrl = DEFAULT_ASSET_URL13,
|
|
47363
49612
|
icon = "shield",
|
|
47364
49613
|
label = "Shield",
|
|
47365
49614
|
duration = 30,
|
|
@@ -47380,7 +49629,17 @@ function StatusEffect({
|
|
|
47380
49629
|
),
|
|
47381
49630
|
title: label,
|
|
47382
49631
|
children: [
|
|
47383
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children:
|
|
49632
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children: assetUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
49633
|
+
"img",
|
|
49634
|
+
{
|
|
49635
|
+
src: assetUrl,
|
|
49636
|
+
alt: label,
|
|
49637
|
+
width: sizes.img,
|
|
49638
|
+
height: sizes.img,
|
|
49639
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
49640
|
+
className: "flex-shrink-0"
|
|
49641
|
+
}
|
|
49642
|
+
) : typeof icon === "string" ? /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { name: icon, size: "sm" }) : /* @__PURE__ */ jsxRuntime.jsx(exports.Icon, { icon, size: "sm" }) }),
|
|
47384
49643
|
duration !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
47385
49644
|
"span",
|
|
47386
49645
|
{
|
|
@@ -47407,15 +49666,16 @@ function StatusEffect({
|
|
|
47407
49666
|
label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground mt-0.5 text-center whitespace-nowrap", children: label })
|
|
47408
49667
|
] });
|
|
47409
49668
|
}
|
|
47410
|
-
var sizeMap16, variantStyles9;
|
|
49669
|
+
var DEFAULT_ASSET_URL13, sizeMap16, variantStyles9;
|
|
47411
49670
|
var init_StatusEffect = __esm({
|
|
47412
49671
|
"components/game/atoms/StatusEffect.tsx"() {
|
|
47413
49672
|
init_cn();
|
|
47414
49673
|
init_Icon();
|
|
49674
|
+
DEFAULT_ASSET_URL13 = "https://almadar-kflow-assets.web.app/shared/effects/particles/flame_01.png";
|
|
47415
49675
|
sizeMap16 = {
|
|
47416
|
-
sm: { container: "w-8 h-8", icon: "text-sm", badge: "text-xs -top-1 -right-1 w-4 h-4", timer: "text-[9px]" },
|
|
47417
|
-
md: { container: "w-10 h-10", icon: "text-base", badge: "text-xs -top-1 -right-1 w-5 h-5", timer: "text-xs" },
|
|
47418
|
-
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" }
|
|
49676
|
+
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 },
|
|
49677
|
+
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 },
|
|
49678
|
+
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 }
|
|
47419
49679
|
};
|
|
47420
49680
|
variantStyles9 = {
|
|
47421
49681
|
buff: "border-success bg-success/20",
|
|
@@ -47908,6 +50168,289 @@ var init_ToastSlot = __esm({
|
|
|
47908
50168
|
exports.ToastSlot.displayName = "ToastSlot";
|
|
47909
50169
|
}
|
|
47910
50170
|
});
|
|
50171
|
+
function tilesToIso(tiles) {
|
|
50172
|
+
return tiles.map((t) => ({
|
|
50173
|
+
x: t.x,
|
|
50174
|
+
y: t.y,
|
|
50175
|
+
terrain: t.terrain,
|
|
50176
|
+
terrainSprite: t.terrainSprite ?? TERRAIN_SPRITES[t.terrain ?? "ground"],
|
|
50177
|
+
passable: t.passable
|
|
50178
|
+
}));
|
|
50179
|
+
}
|
|
50180
|
+
function towersToUnits(towers) {
|
|
50181
|
+
return towers.map((t) => ({
|
|
50182
|
+
id: t.id,
|
|
50183
|
+
position: { x: t.x, y: t.y },
|
|
50184
|
+
name: "Tower",
|
|
50185
|
+
team: "player",
|
|
50186
|
+
sprite: TOWER_SPRITE,
|
|
50187
|
+
unitType: "guardian",
|
|
50188
|
+
health: 1,
|
|
50189
|
+
maxHealth: 1
|
|
50190
|
+
}));
|
|
50191
|
+
}
|
|
50192
|
+
function creepsToUnits(creeps) {
|
|
50193
|
+
return creeps.map((c) => ({
|
|
50194
|
+
id: c.id,
|
|
50195
|
+
position: { x: c.x, y: c.y },
|
|
50196
|
+
name: "Creep",
|
|
50197
|
+
team: "enemy",
|
|
50198
|
+
sprite: CREEP_SPRITE,
|
|
50199
|
+
unitType: "scrapper",
|
|
50200
|
+
health: c.hp,
|
|
50201
|
+
maxHealth: c.maxHp
|
|
50202
|
+
}));
|
|
50203
|
+
}
|
|
50204
|
+
function pathToFeatures(path) {
|
|
50205
|
+
return path.map((p2, i) => ({
|
|
50206
|
+
id: `path-${i}`,
|
|
50207
|
+
x: p2.x,
|
|
50208
|
+
y: p2.y,
|
|
50209
|
+
type: "path-marker",
|
|
50210
|
+
sprite: `${CDN6}world-map/road_straight.png`
|
|
50211
|
+
}));
|
|
50212
|
+
}
|
|
50213
|
+
function TowerDefenseBoard({
|
|
50214
|
+
entity,
|
|
50215
|
+
tiles: propTiles,
|
|
50216
|
+
path: propPath,
|
|
50217
|
+
towers: propTowers,
|
|
50218
|
+
creeps: propCreeps,
|
|
50219
|
+
gold: propGold,
|
|
50220
|
+
lives: propLives,
|
|
50221
|
+
wave: propWave,
|
|
50222
|
+
maxWaves: propMaxWaves,
|
|
50223
|
+
result: propResult,
|
|
50224
|
+
waveActive: propWaveActive,
|
|
50225
|
+
towerCost = 25,
|
|
50226
|
+
scale = 0.45,
|
|
50227
|
+
placeTowerEvent,
|
|
50228
|
+
startWaveEvent,
|
|
50229
|
+
playAgainEvent,
|
|
50230
|
+
gameEndEvent,
|
|
50231
|
+
className
|
|
50232
|
+
}) {
|
|
50233
|
+
const board = boardEntity(entity) ?? {};
|
|
50234
|
+
const eventBus = useEventBus();
|
|
50235
|
+
const { t } = hooks.useTranslate();
|
|
50236
|
+
const tiles = propTiles ?? rows(board.tiles);
|
|
50237
|
+
const path = propPath ?? rows(board.path);
|
|
50238
|
+
const towers = propTowers ?? rows(board.towers);
|
|
50239
|
+
const creeps = propCreeps ?? rows(board.creeps);
|
|
50240
|
+
const gold = propGold ?? num(board.gold, 100);
|
|
50241
|
+
const lives = propLives ?? num(board.lives, 20);
|
|
50242
|
+
const wave = propWave ?? num(board.wave, 1);
|
|
50243
|
+
const maxWaves = propMaxWaves ?? num(board.maxWaves, 5);
|
|
50244
|
+
const result = propResult ?? (str(board.result) || "none");
|
|
50245
|
+
const waveActive = propWaveActive ?? Boolean(board.waveActive);
|
|
50246
|
+
const [hoveredTile, setHoveredTile] = React77.useState(null);
|
|
50247
|
+
const emittedGameEnd = React77.useRef(false);
|
|
50248
|
+
if (result !== "none" && !emittedGameEnd.current) {
|
|
50249
|
+
emittedGameEnd.current = true;
|
|
50250
|
+
if (gameEndEvent) {
|
|
50251
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
50252
|
+
}
|
|
50253
|
+
}
|
|
50254
|
+
if (result === "none") {
|
|
50255
|
+
emittedGameEnd.current = false;
|
|
50256
|
+
}
|
|
50257
|
+
const towerPositions = React77.useMemo(() => new Set(towers.map((t2) => `${t2.x},${t2.y}`)), [towers]);
|
|
50258
|
+
const pathPositions = React77.useMemo(() => new Set(path.map((p2) => `${p2.x},${p2.y}`)), [path]);
|
|
50259
|
+
const validMoves = React77.useMemo(() => {
|
|
50260
|
+
if (result !== "none" || gold < towerCost) return [];
|
|
50261
|
+
return tiles.filter(
|
|
50262
|
+
(t2) => t2.passable !== false && !towerPositions.has(`${t2.x},${t2.y}`) && !pathPositions.has(`${t2.x},${t2.y}`)
|
|
50263
|
+
).map((t2) => ({ x: t2.x, y: t2.y }));
|
|
50264
|
+
}, [tiles, towerPositions, pathPositions, result, gold, towerCost]);
|
|
50265
|
+
const isoTiles = React77.useMemo(() => tilesToIso(tiles), [tiles]);
|
|
50266
|
+
const towerUnits = React77.useMemo(() => towersToUnits(towers), [towers]);
|
|
50267
|
+
const creepUnits = React77.useMemo(() => creepsToUnits(creeps), [creeps]);
|
|
50268
|
+
const isoUnits = React77.useMemo(() => [...towerUnits, ...creepUnits], [towerUnits, creepUnits]);
|
|
50269
|
+
const pathFeatures = React77.useMemo(() => pathToFeatures(path), [path]);
|
|
50270
|
+
const handleTileClick = React77.useCallback((x, y) => {
|
|
50271
|
+
if (result !== "none") return;
|
|
50272
|
+
if (pathPositions.has(`${x},${y}`)) return;
|
|
50273
|
+
if (towerPositions.has(`${x},${y}`)) return;
|
|
50274
|
+
if (gold < towerCost) return;
|
|
50275
|
+
if (placeTowerEvent) {
|
|
50276
|
+
eventBus.emit(`UI:${placeTowerEvent}`, { x, y });
|
|
50277
|
+
}
|
|
50278
|
+
}, [result, pathPositions, towerPositions, gold, towerCost, placeTowerEvent, eventBus]);
|
|
50279
|
+
const handleStartWave = React77.useCallback(() => {
|
|
50280
|
+
if (startWaveEvent) {
|
|
50281
|
+
eventBus.emit(`UI:${startWaveEvent}`, { wave });
|
|
50282
|
+
}
|
|
50283
|
+
}, [startWaveEvent, wave, eventBus]);
|
|
50284
|
+
const handlePlayAgain = React77.useCallback(() => {
|
|
50285
|
+
if (playAgainEvent) {
|
|
50286
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
50287
|
+
}
|
|
50288
|
+
}, [playAgainEvent, eventBus]);
|
|
50289
|
+
const canPlaceTower = gold >= towerCost && result === "none";
|
|
50290
|
+
const canStartWave = !waveActive && result === "none";
|
|
50291
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { className: cn("tower-defense-board relative min-h-[600px] bg-background", className), gap: "none", children: [
|
|
50292
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
50293
|
+
exports.HStack,
|
|
50294
|
+
{
|
|
50295
|
+
className: "px-4 py-2 border-b border-border bg-surface",
|
|
50296
|
+
gap: "lg",
|
|
50297
|
+
align: "center",
|
|
50298
|
+
children: [
|
|
50299
|
+
/* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", children: [
|
|
50300
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "muted", children: t("td.gold") ?? "Gold" }),
|
|
50301
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", weight: "bold", className: "text-warning", children: gold })
|
|
50302
|
+
] }),
|
|
50303
|
+
/* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", children: [
|
|
50304
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "muted", children: t("td.lives") ?? "Lives" }),
|
|
50305
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body2", weight: "bold", className: lives <= 5 ? "text-error" : "text-success", children: lives })
|
|
50306
|
+
] }),
|
|
50307
|
+
/* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", children: [
|
|
50308
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "muted", children: t("td.wave") ?? "Wave" }),
|
|
50309
|
+
/* @__PURE__ */ jsxRuntime.jsxs(exports.Typography, { variant: "body2", weight: "bold", children: [
|
|
50310
|
+
wave,
|
|
50311
|
+
" / ",
|
|
50312
|
+
maxWaves
|
|
50313
|
+
] })
|
|
50314
|
+
] }),
|
|
50315
|
+
/* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "ml-auto", children: [
|
|
50316
|
+
canStartWave && /* @__PURE__ */ jsxRuntime.jsx(exports.Button, { variant: "primary", size: "sm", onClick: handleStartWave, children: t("td.startWave") ?? "Start Wave" }),
|
|
50317
|
+
waveActive && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", className: "text-warning animate-pulse", children: t("td.waveInProgress") ?? "Wave in progress\u2026" })
|
|
50318
|
+
] })
|
|
50319
|
+
]
|
|
50320
|
+
}
|
|
50321
|
+
),
|
|
50322
|
+
/* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "relative flex-1", children: [
|
|
50323
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
50324
|
+
IsometricCanvas_default,
|
|
50325
|
+
{
|
|
50326
|
+
tiles: isoTiles,
|
|
50327
|
+
units: isoUnits,
|
|
50328
|
+
features: pathFeatures,
|
|
50329
|
+
validMoves,
|
|
50330
|
+
hoveredTile,
|
|
50331
|
+
onTileClick: handleTileClick,
|
|
50332
|
+
onTileHover: (x, y) => setHoveredTile({ x, y }),
|
|
50333
|
+
onTileLeave: () => setHoveredTile(null),
|
|
50334
|
+
scale
|
|
50335
|
+
}
|
|
50336
|
+
),
|
|
50337
|
+
hoveredTile && canPlaceTower && validMoves.some((m) => m.x === hoveredTile.x && m.y === hoveredTile.y) && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "absolute bottom-4 left-1/2 -translate-x-1/2 z-10 bg-background/80 rounded px-3 py-1 backdrop-blur-sm pointer-events-none", children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", children: t("td.placeTower") ?? `Place tower (${towerCost} gold)` }) })
|
|
50338
|
+
] }),
|
|
50339
|
+
result !== "none" && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm", children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
50340
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
50341
|
+
exports.Typography,
|
|
50342
|
+
{
|
|
50343
|
+
variant: "h2",
|
|
50344
|
+
className: cn(
|
|
50345
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
50346
|
+
result === "won" ? "text-warning" : "text-error"
|
|
50347
|
+
),
|
|
50348
|
+
children: result === "won" ? t("td.victory") ?? "Victory!" : t("td.defeat") ?? "Defeat!"
|
|
50349
|
+
}
|
|
50350
|
+
),
|
|
50351
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "body1", className: "text-muted-foreground", children: result === "won" ? t("td.survivedAllWaves") ?? `Survived all ${maxWaves} waves!` : t("td.livesReachedZero") ?? "Your base was overrun." }),
|
|
50352
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
50353
|
+
exports.Button,
|
|
50354
|
+
{
|
|
50355
|
+
variant: "primary",
|
|
50356
|
+
className: "px-8 py-3 font-semibold",
|
|
50357
|
+
onClick: handlePlayAgain,
|
|
50358
|
+
children: t("td.playAgain") ?? "Play Again"
|
|
50359
|
+
}
|
|
50360
|
+
)
|
|
50361
|
+
] }) })
|
|
50362
|
+
] });
|
|
50363
|
+
}
|
|
50364
|
+
var CDN6, TERRAIN_SPRITES, TOWER_SPRITE, CREEP_SPRITE;
|
|
50365
|
+
var init_TowerDefenseBoard = __esm({
|
|
50366
|
+
"components/game/organisms/TowerDefenseBoard.tsx"() {
|
|
50367
|
+
"use client";
|
|
50368
|
+
init_cn();
|
|
50369
|
+
init_useEventBus();
|
|
50370
|
+
init_Box();
|
|
50371
|
+
init_Button();
|
|
50372
|
+
init_Typography();
|
|
50373
|
+
init_Stack();
|
|
50374
|
+
init_IsometricCanvas();
|
|
50375
|
+
init_boardEntity();
|
|
50376
|
+
CDN6 = "https://almadar-kflow-assets.web.app/shared/";
|
|
50377
|
+
TERRAIN_SPRITES = {
|
|
50378
|
+
ground: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_01.png`,
|
|
50379
|
+
path: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_09.png`,
|
|
50380
|
+
wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`
|
|
50381
|
+
};
|
|
50382
|
+
TOWER_SPRITE = `${CDN6}units/guardian.png`;
|
|
50383
|
+
CREEP_SPRITE = `${CDN6}units/scrapper.png`;
|
|
50384
|
+
TowerDefenseBoard.displayName = "TowerDefenseBoard";
|
|
50385
|
+
}
|
|
50386
|
+
});
|
|
50387
|
+
function TowerDefenseTemplate({
|
|
50388
|
+
entity,
|
|
50389
|
+
title = "Tower Defense",
|
|
50390
|
+
tiles,
|
|
50391
|
+
path,
|
|
50392
|
+
towers,
|
|
50393
|
+
gold,
|
|
50394
|
+
lives,
|
|
50395
|
+
maxWaves,
|
|
50396
|
+
towerCost,
|
|
50397
|
+
scale,
|
|
50398
|
+
placeTowerEvent,
|
|
50399
|
+
startWaveEvent,
|
|
50400
|
+
playAgainEvent,
|
|
50401
|
+
gameEndEvent,
|
|
50402
|
+
className
|
|
50403
|
+
}) {
|
|
50404
|
+
const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
|
|
50405
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
50406
|
+
exports.Box,
|
|
50407
|
+
{
|
|
50408
|
+
display: "flex",
|
|
50409
|
+
fullHeight: true,
|
|
50410
|
+
className: cn("tower-defense-template flex-col", className),
|
|
50411
|
+
children: [
|
|
50412
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
50413
|
+
exports.HStack,
|
|
50414
|
+
{
|
|
50415
|
+
gap: "sm",
|
|
50416
|
+
align: "center",
|
|
50417
|
+
className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
|
|
50418
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h4", children: title })
|
|
50419
|
+
}
|
|
50420
|
+
),
|
|
50421
|
+
/* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
50422
|
+
TowerDefenseBoard,
|
|
50423
|
+
{
|
|
50424
|
+
entity: resolved,
|
|
50425
|
+
tiles,
|
|
50426
|
+
path,
|
|
50427
|
+
towers,
|
|
50428
|
+
gold,
|
|
50429
|
+
lives,
|
|
50430
|
+
maxWaves,
|
|
50431
|
+
towerCost,
|
|
50432
|
+
scale,
|
|
50433
|
+
placeTowerEvent,
|
|
50434
|
+
startWaveEvent,
|
|
50435
|
+
playAgainEvent,
|
|
50436
|
+
gameEndEvent,
|
|
50437
|
+
className: "h-full"
|
|
50438
|
+
}
|
|
50439
|
+
) })
|
|
50440
|
+
]
|
|
50441
|
+
}
|
|
50442
|
+
);
|
|
50443
|
+
}
|
|
50444
|
+
var init_TowerDefenseTemplate = __esm({
|
|
50445
|
+
"components/game/templates/TowerDefenseTemplate.tsx"() {
|
|
50446
|
+
init_cn();
|
|
50447
|
+
init_Box();
|
|
50448
|
+
init_Stack();
|
|
50449
|
+
init_Typography();
|
|
50450
|
+
init_TowerDefenseBoard();
|
|
50451
|
+
TowerDefenseTemplate.displayName = "TowerDefenseTemplate";
|
|
50452
|
+
}
|
|
50453
|
+
});
|
|
47911
50454
|
function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
|
|
47912
50455
|
const eventBus = useEventBus();
|
|
47913
50456
|
const {
|
|
@@ -48106,17 +50649,12 @@ var init_UncontrolledBattleBoard = __esm({
|
|
|
48106
50649
|
}
|
|
48107
50650
|
});
|
|
48108
50651
|
function heroPosition(h) {
|
|
50652
|
+
if ("position" in h && h.position != null) return h.position;
|
|
48109
50653
|
return vec2(h.position);
|
|
48110
50654
|
}
|
|
48111
|
-
function heroOwner(h) {
|
|
48112
|
-
return str(h.owner);
|
|
48113
|
-
}
|
|
48114
50655
|
function heroMovement(h) {
|
|
48115
50656
|
return num(h.movement);
|
|
48116
50657
|
}
|
|
48117
|
-
function hexPassable(h) {
|
|
48118
|
-
return h.passable !== false;
|
|
48119
|
-
}
|
|
48120
50658
|
function defaultIsInRange(from, to, range) {
|
|
48121
50659
|
return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
|
|
48122
50660
|
}
|
|
@@ -48152,38 +50690,41 @@ function WorldMapBoard({
|
|
|
48152
50690
|
}) {
|
|
48153
50691
|
const eventBus = useEventBus();
|
|
48154
50692
|
const resolved = boardEntity(entity);
|
|
48155
|
-
const
|
|
48156
|
-
const
|
|
50693
|
+
const entityUnits = rows(resolved?.units);
|
|
50694
|
+
const entityTiles = rows(resolved?.tiles);
|
|
48157
50695
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
48158
50696
|
const selectedHeroId = resolved?.selectedHeroId ?? null;
|
|
48159
50697
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
48160
50698
|
const backgroundImage = resolved?.backgroundImage;
|
|
48161
50699
|
const [hoveredTile, setHoveredTile] = React77.useState(null);
|
|
48162
|
-
const selectedHero = React77.useMemo(
|
|
48163
|
-
() => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
|
|
48164
|
-
[heroes, selectedHeroId]
|
|
48165
|
-
);
|
|
48166
50700
|
const derivedTiles = React77.useMemo(
|
|
48167
|
-
() =>
|
|
48168
|
-
x: num(
|
|
48169
|
-
y: num(
|
|
48170
|
-
terrain: str(
|
|
48171
|
-
terrainSprite:
|
|
50701
|
+
() => entityTiles.map((t) => ({
|
|
50702
|
+
x: num(t.x),
|
|
50703
|
+
y: num(t.y),
|
|
50704
|
+
terrain: str(t.terrain),
|
|
50705
|
+
terrainSprite: t.terrainSprite == null ? void 0 : str(t.terrainSprite),
|
|
50706
|
+
passable: t.passable !== false
|
|
48172
50707
|
})),
|
|
48173
|
-
[
|
|
50708
|
+
[entityTiles]
|
|
48174
50709
|
);
|
|
48175
50710
|
const tiles = propTiles ?? derivedTiles;
|
|
48176
50711
|
const baseUnits = React77.useMemo(
|
|
48177
|
-
() => propUnits ??
|
|
48178
|
-
id: str(
|
|
48179
|
-
position: heroPosition(
|
|
48180
|
-
name: str(
|
|
48181
|
-
|
|
48182
|
-
|
|
48183
|
-
|
|
48184
|
-
|
|
50712
|
+
() => propUnits ?? entityUnits.map((u) => ({
|
|
50713
|
+
id: str(u.id),
|
|
50714
|
+
position: heroPosition(u),
|
|
50715
|
+
name: str(u.name),
|
|
50716
|
+
// lolo uses `team` field (not `owner`)
|
|
50717
|
+
team: str(u.team) === "enemy" ? "enemy" : "player",
|
|
50718
|
+
health: num(u.health) || 100,
|
|
50719
|
+
maxHealth: num(u.maxHealth) || 100,
|
|
50720
|
+
sprite: u.sprite == null ? void 0 : str(u.sprite)
|
|
48185
50721
|
})),
|
|
48186
|
-
[
|
|
50722
|
+
[entityUnits, propUnits]
|
|
50723
|
+
);
|
|
50724
|
+
const gameUnits = baseUnits;
|
|
50725
|
+
const selectedHero = React77.useMemo(
|
|
50726
|
+
() => gameUnits.find((u) => u.id === selectedHeroId) ?? null,
|
|
50727
|
+
[gameUnits, selectedHeroId]
|
|
48187
50728
|
);
|
|
48188
50729
|
const MOVE_SPEED_MS_PER_TILE = 300;
|
|
48189
50730
|
const movementAnimRef = React77.useRef(null);
|
|
@@ -48229,51 +50770,50 @@ function WorldMapBoard({
|
|
|
48229
50770
|
const validMoves = React77.useMemo(() => {
|
|
48230
50771
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
48231
50772
|
const sp = heroPosition(selectedHero);
|
|
48232
|
-
const
|
|
50773
|
+
const sTeam = str(selectedHero.team);
|
|
48233
50774
|
const range = heroMovement(selectedHero);
|
|
48234
50775
|
const moves = [];
|
|
48235
|
-
|
|
48236
|
-
const
|
|
48237
|
-
const
|
|
48238
|
-
if (
|
|
48239
|
-
if (
|
|
48240
|
-
if (!isInRange(sp, { x:
|
|
48241
|
-
if (
|
|
48242
|
-
const
|
|
48243
|
-
return
|
|
50776
|
+
tiles.forEach((t) => {
|
|
50777
|
+
const tx = t.x;
|
|
50778
|
+
const ty = t.y;
|
|
50779
|
+
if (t.passable === false) return;
|
|
50780
|
+
if (tx === sp.x && ty === sp.y) return;
|
|
50781
|
+
if (!isInRange(sp, { x: tx, y: ty }, range)) return;
|
|
50782
|
+
if (gameUnits.some((u) => {
|
|
50783
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
50784
|
+
return up.x === tx && up.y === ty && str(u.team) === sTeam;
|
|
48244
50785
|
})) return;
|
|
48245
|
-
moves.push({ x:
|
|
50786
|
+
moves.push({ x: tx, y: ty });
|
|
48246
50787
|
});
|
|
48247
50788
|
return moves;
|
|
48248
|
-
}, [selectedHero,
|
|
50789
|
+
}, [selectedHero, tiles, gameUnits, isInRange]);
|
|
48249
50790
|
const attackTargets = React77.useMemo(() => {
|
|
48250
50791
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
48251
50792
|
const sp = heroPosition(selectedHero);
|
|
48252
|
-
const
|
|
50793
|
+
const sTeam = str(selectedHero.team);
|
|
48253
50794
|
const range = heroMovement(selectedHero);
|
|
48254
|
-
return
|
|
48255
|
-
}, [selectedHero,
|
|
48256
|
-
const maxY = Math.max(...
|
|
50795
|
+
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 });
|
|
50796
|
+
}, [selectedHero, gameUnits, isInRange]);
|
|
50797
|
+
const maxY = Math.max(...tiles.map((t) => t.y), 0);
|
|
48257
50798
|
const baseOffsetX = (maxY + 1) * (exports.TILE_WIDTH * scale / 2);
|
|
48258
50799
|
const tileToScreen = React77.useCallback(
|
|
48259
50800
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
48260
50801
|
[scale, baseOffsetX]
|
|
48261
50802
|
);
|
|
48262
50803
|
const hoveredHex = React77.useMemo(
|
|
48263
|
-
() => hoveredTile ?
|
|
48264
|
-
[hoveredTile,
|
|
50804
|
+
() => hoveredTile ? tiles.find((t) => t.x === hoveredTile.x && t.y === hoveredTile.y) ?? null : null,
|
|
50805
|
+
[hoveredTile, tiles]
|
|
48265
50806
|
);
|
|
48266
50807
|
const hoveredHero = React77.useMemo(
|
|
48267
|
-
() => hoveredTile ?
|
|
48268
|
-
const
|
|
48269
|
-
return
|
|
50808
|
+
() => hoveredTile ? gameUnits.find((u) => {
|
|
50809
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
50810
|
+
return up.x === hoveredTile.x && up.y === hoveredTile.y;
|
|
48270
50811
|
}) ?? null : null,
|
|
48271
|
-
[hoveredTile,
|
|
50812
|
+
[hoveredTile, gameUnits]
|
|
48272
50813
|
);
|
|
48273
50814
|
const handleTileClick = React77.useCallback((x, y) => {
|
|
48274
50815
|
if (movementAnimRef.current) return;
|
|
48275
|
-
const
|
|
48276
|
-
if (!hex) return;
|
|
50816
|
+
const tile = tiles.find((t) => t.x === x && t.y === y);
|
|
48277
50817
|
if (tileClickEvent) {
|
|
48278
50818
|
eventBus.emit(`UI:${tileClickEvent}`, { x, y });
|
|
48279
50819
|
}
|
|
@@ -48284,38 +50824,39 @@ function WorldMapBoard({
|
|
|
48284
50824
|
if (heroMoveEvent) {
|
|
48285
50825
|
eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
|
|
48286
50826
|
}
|
|
48287
|
-
const feature = str(
|
|
50827
|
+
const feature = tile ? str(tile.feature) : "";
|
|
48288
50828
|
if (feature && feature !== "none") {
|
|
48289
|
-
|
|
50829
|
+
const tileRow = tile;
|
|
50830
|
+
onFeatureEnter?.(heroId, tileRow);
|
|
48290
50831
|
if (featureEnterEvent) {
|
|
48291
|
-
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
|
|
50832
|
+
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex: tileRow });
|
|
48292
50833
|
}
|
|
48293
50834
|
}
|
|
48294
50835
|
});
|
|
48295
50836
|
return;
|
|
48296
50837
|
}
|
|
48297
|
-
const enemy =
|
|
48298
|
-
const
|
|
48299
|
-
return
|
|
50838
|
+
const enemy = gameUnits.find((u) => {
|
|
50839
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
50840
|
+
return up.x === x && up.y === y && str(u.team) === "enemy";
|
|
48300
50841
|
});
|
|
48301
50842
|
if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
|
|
48302
50843
|
const attackerId = str(selectedHero.id);
|
|
48303
|
-
const defenderId =
|
|
50844
|
+
const defenderId = enemy.id;
|
|
48304
50845
|
onBattleEncounter?.(attackerId, defenderId);
|
|
48305
50846
|
if (battleEncounterEvent) {
|
|
48306
50847
|
eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
|
|
48307
50848
|
}
|
|
48308
50849
|
}
|
|
48309
|
-
}, [
|
|
50850
|
+
}, [tiles, gameUnits, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
|
|
48310
50851
|
const handleUnitClick = React77.useCallback((unitId) => {
|
|
48311
|
-
const
|
|
48312
|
-
if (
|
|
50852
|
+
const unit = gameUnits.find((u) => u.id === unitId);
|
|
50853
|
+
if (unit && (str(unit.team) === "player" || allowMoveAllHeroes)) {
|
|
48313
50854
|
onHeroSelect?.(unitId);
|
|
48314
50855
|
if (heroSelectEvent) {
|
|
48315
50856
|
eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
|
|
48316
50857
|
}
|
|
48317
50858
|
}
|
|
48318
|
-
}, [
|
|
50859
|
+
}, [gameUnits, onHeroSelect, allowMoveAllHeroes, eventBus, heroSelectEvent]);
|
|
48319
50860
|
const selectHero = React77.useCallback((id) => {
|
|
48320
50861
|
onHeroSelect?.(id);
|
|
48321
50862
|
if (heroSelectEvent) {
|
|
@@ -48419,48 +50960,48 @@ function WorldMapTemplate({
|
|
|
48419
50960
|
}
|
|
48420
50961
|
);
|
|
48421
50962
|
}
|
|
48422
|
-
var
|
|
50963
|
+
var CDN7, DEFAULT_WORLDMAP_TILES, DEFAULT_WORLDMAP_UNITS, DEFAULT_WORLDMAP_FEATURES, DEFAULT_WORLDMAP_MANIFEST;
|
|
48423
50964
|
var init_WorldMapTemplate = __esm({
|
|
48424
50965
|
"components/game/templates/WorldMapTemplate.tsx"() {
|
|
48425
50966
|
init_WorldMapBoard();
|
|
48426
|
-
|
|
50967
|
+
CDN7 = "https://almadar-kflow-assets.web.app/shared";
|
|
48427
50968
|
DEFAULT_WORLDMAP_TILES = [
|
|
48428
|
-
{ x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
48429
|
-
{ x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
48430
|
-
{ x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${
|
|
48431
|
-
{ x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
48432
|
-
{ x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
48433
|
-
{ x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${
|
|
48434
|
-
{ x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
48435
|
-
{ x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
48436
|
-
{ x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
48437
|
-
{ x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${
|
|
48438
|
-
{ x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${
|
|
48439
|
-
{ x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
48440
|
-
{ x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
48441
|
-
{ x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
48442
|
-
{ x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${
|
|
48443
|
-
{ x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${
|
|
48444
|
-
{ x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
48445
|
-
{ x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
48446
|
-
{ x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
48447
|
-
{ x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${
|
|
48448
|
-
{ x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
48449
|
-
{ x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${
|
|
48450
|
-
{ x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${
|
|
48451
|
-
{ x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
48452
|
-
{ x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
50969
|
+
{ x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50970
|
+
{ x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50971
|
+
{ x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
50972
|
+
{ x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50973
|
+
{ x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50974
|
+
{ x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50975
|
+
{ x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50976
|
+
{ x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50977
|
+
{ x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50978
|
+
{ x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
50979
|
+
{ x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
50980
|
+
{ x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50981
|
+
{ x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
|
|
50982
|
+
{ x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50983
|
+
{ x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50984
|
+
{ x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50985
|
+
{ x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50986
|
+
{ x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50987
|
+
{ x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
|
|
50988
|
+
{ x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50989
|
+
{ x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50990
|
+
{ x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
50991
|
+
{ x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50992
|
+
{ x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50993
|
+
{ x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` }
|
|
48453
50994
|
];
|
|
48454
50995
|
DEFAULT_WORLDMAP_UNITS = [
|
|
48455
|
-
{ id: "h1", position: { x: 1, y: 1 }, unitType: "hero", name: "Amir", team: "player", health: 10, maxHealth: 10, sprite: `${
|
|
48456
|
-
{ id: "h2", position: { x: 3, y: 3 }, unitType: "scout", name: "Archivist", team: "player", health: 10, maxHealth: 10, sprite: `${
|
|
50996
|
+
{ 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` },
|
|
50997
|
+
{ 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` }
|
|
48457
50998
|
];
|
|
48458
50999
|
DEFAULT_WORLDMAP_FEATURES = [
|
|
48459
|
-
{ id: "f1", x: 2, y: 2, type: "capital", sprite: `${
|
|
48460
|
-
{ id: "f2", x: 4, y: 2, type: "power_node", sprite: `${
|
|
51000
|
+
{ id: "f1", x: 2, y: 2, type: "capital", sprite: `${CDN7}/scenes/world/capital.png` },
|
|
51001
|
+
{ id: "f2", x: 4, y: 2, type: "power_node", sprite: `${CDN7}/world-map/power_node.png` }
|
|
48461
51002
|
];
|
|
48462
51003
|
DEFAULT_WORLDMAP_MANIFEST = {
|
|
48463
|
-
baseUrl:
|
|
51004
|
+
baseUrl: CDN7,
|
|
48464
51005
|
terrains: {
|
|
48465
51006
|
grass: "/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png",
|
|
48466
51007
|
water: "/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png",
|
|
@@ -48589,7 +51130,7 @@ function lazyThree(name, loader) {
|
|
|
48589
51130
|
ThreeWrapper.displayName = `Lazy(${name})`;
|
|
48590
51131
|
return ThreeWrapper;
|
|
48591
51132
|
}
|
|
48592
|
-
var ThreeBoundary, FeatureRenderer,
|
|
51133
|
+
var ThreeBoundary, FeatureRenderer, GameCanvas3D2, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
|
|
48593
51134
|
var init_component_registry_generated = __esm({
|
|
48594
51135
|
"components/core/organisms/component-registry.generated.ts"() {
|
|
48595
51136
|
init_AboutPageTemplate();
|
|
@@ -48699,6 +51240,7 @@ var init_component_registry_generated = __esm({
|
|
|
48699
51240
|
init_FormSectionHeader();
|
|
48700
51241
|
init_GameAudioProvider();
|
|
48701
51242
|
init_GameAudioToggle();
|
|
51243
|
+
init_GameBoard3D();
|
|
48702
51244
|
init_GameCanvas2D();
|
|
48703
51245
|
init_GameHud();
|
|
48704
51246
|
init_GameMenu();
|
|
@@ -48756,7 +51298,9 @@ var init_component_registry_generated = __esm({
|
|
|
48756
51298
|
init_PageHeader();
|
|
48757
51299
|
init_Pagination();
|
|
48758
51300
|
init_PatternTile();
|
|
51301
|
+
init_PlatformerBoard();
|
|
48759
51302
|
init_PlatformerCanvas();
|
|
51303
|
+
init_PlatformerTemplate();
|
|
48760
51304
|
init_Popover();
|
|
48761
51305
|
init_PositionedCanvas();
|
|
48762
51306
|
init_PowerupSlots();
|
|
@@ -48780,6 +51324,8 @@ var init_component_registry_generated = __esm({
|
|
|
48780
51324
|
init_ResourceBar();
|
|
48781
51325
|
init_ResourceCounter();
|
|
48782
51326
|
init_RichBlockEditor();
|
|
51327
|
+
init_RoguelikeBoard();
|
|
51328
|
+
init_RoguelikeTemplate();
|
|
48783
51329
|
init_RuntimeDebugger2();
|
|
48784
51330
|
init_ScaledDiagram();
|
|
48785
51331
|
init_ScoreBoard();
|
|
@@ -48852,6 +51398,8 @@ var init_component_registry_generated = __esm({
|
|
|
48852
51398
|
init_Toast();
|
|
48853
51399
|
init_ToastSlot();
|
|
48854
51400
|
init_Tooltip();
|
|
51401
|
+
init_TowerDefenseBoard();
|
|
51402
|
+
init_TowerDefenseTemplate();
|
|
48855
51403
|
init_TraitFrame();
|
|
48856
51404
|
init_TraitSlot();
|
|
48857
51405
|
init_TrendIndicator();
|
|
@@ -48896,7 +51444,7 @@ var init_component_registry_generated = __esm({
|
|
|
48896
51444
|
}
|
|
48897
51445
|
};
|
|
48898
51446
|
FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
|
|
48899
|
-
|
|
51447
|
+
GameCanvas3D2 = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
|
|
48900
51448
|
GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
48901
51449
|
GameCanvas3DCastleTemplate = lazyThree("GameCanvas3DCastleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
48902
51450
|
GameCanvas3DWorldMapTemplate = lazyThree("GameCanvas3DWorldMapTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
@@ -49015,8 +51563,9 @@ var init_component_registry_generated = __esm({
|
|
|
49015
51563
|
"FormSectionHeader": exports.FormSectionHeader,
|
|
49016
51564
|
"GameAudioProvider": GameAudioProvider,
|
|
49017
51565
|
"GameAudioToggle": GameAudioToggle,
|
|
51566
|
+
"GameBoard3D": GameBoard3D,
|
|
49018
51567
|
"GameCanvas2D": GameCanvas2D,
|
|
49019
|
-
"GameCanvas3D":
|
|
51568
|
+
"GameCanvas3D": GameCanvas3D2,
|
|
49020
51569
|
"GameCanvas3DBattleTemplate": GameCanvas3DBattleTemplate,
|
|
49021
51570
|
"GameCanvas3DCastleTemplate": GameCanvas3DCastleTemplate,
|
|
49022
51571
|
"GameCanvas3DWorldMapTemplate": GameCanvas3DWorldMapTemplate,
|
|
@@ -49078,7 +51627,9 @@ var init_component_registry_generated = __esm({
|
|
|
49078
51627
|
"PageHeader": exports.PageHeader,
|
|
49079
51628
|
"Pagination": exports.Pagination,
|
|
49080
51629
|
"PatternTile": exports.PatternTile,
|
|
51630
|
+
"PlatformerBoard": PlatformerBoard,
|
|
49081
51631
|
"PlatformerCanvas": PlatformerCanvas,
|
|
51632
|
+
"PlatformerTemplate": PlatformerTemplate,
|
|
49082
51633
|
"Popover": exports.Popover,
|
|
49083
51634
|
"PositionedCanvas": exports.PositionedCanvas,
|
|
49084
51635
|
"PowerupSlots": PowerupSlots,
|
|
@@ -49102,6 +51653,8 @@ var init_component_registry_generated = __esm({
|
|
|
49102
51653
|
"ResourceBar": ResourceBar,
|
|
49103
51654
|
"ResourceCounter": ResourceCounter,
|
|
49104
51655
|
"RichBlockEditor": exports.RichBlockEditor,
|
|
51656
|
+
"RoguelikeBoard": RoguelikeBoard,
|
|
51657
|
+
"RoguelikeTemplate": RoguelikeTemplate,
|
|
49105
51658
|
"RuntimeDebugger": RuntimeDebugger,
|
|
49106
51659
|
"ScaledDiagram": exports.ScaledDiagram,
|
|
49107
51660
|
"ScoreBoard": ScoreBoard,
|
|
@@ -49177,6 +51730,8 @@ var init_component_registry_generated = __esm({
|
|
|
49177
51730
|
"Toast": exports.Toast,
|
|
49178
51731
|
"ToastSlot": exports.ToastSlot,
|
|
49179
51732
|
"Tooltip": exports.Tooltip,
|
|
51733
|
+
"TowerDefenseBoard": TowerDefenseBoard,
|
|
51734
|
+
"TowerDefenseTemplate": TowerDefenseTemplate,
|
|
49180
51735
|
"TraitFrame": TraitFrame,
|
|
49181
51736
|
"TraitSlot": TraitSlot,
|
|
49182
51737
|
"TrendIndicator": exports.TrendIndicator,
|
|
@@ -50739,6 +53294,10 @@ init_UncontrolledBattleBoard();
|
|
|
50739
53294
|
init_useBattleState();
|
|
50740
53295
|
init_WorldMapBoard();
|
|
50741
53296
|
init_CastleBoard();
|
|
53297
|
+
init_PlatformerBoard();
|
|
53298
|
+
init_TowerDefenseBoard();
|
|
53299
|
+
init_RoguelikeBoard();
|
|
53300
|
+
init_GameBoard3D();
|
|
50742
53301
|
init_TraitStateViewer();
|
|
50743
53302
|
init_TraitSlot();
|
|
50744
53303
|
|
|
@@ -51311,6 +53870,9 @@ init_GameShell();
|
|
|
51311
53870
|
init_BattleTemplate();
|
|
51312
53871
|
init_CastleTemplate();
|
|
51313
53872
|
init_WorldMapTemplate();
|
|
53873
|
+
init_PlatformerTemplate();
|
|
53874
|
+
init_TowerDefenseTemplate();
|
|
53875
|
+
init_RoguelikeTemplate();
|
|
51314
53876
|
init_LandingPageTemplate();
|
|
51315
53877
|
init_PricingPageTemplate();
|
|
51316
53878
|
init_FeatureDetailPageTemplate();
|
|
@@ -51361,6 +53923,7 @@ exports.EventLog = EventLog;
|
|
|
51361
53923
|
exports.FEATURE_TYPES = FEATURE_TYPES;
|
|
51362
53924
|
exports.GameAudioProvider = GameAudioProvider;
|
|
51363
53925
|
exports.GameAudioToggle = GameAudioToggle;
|
|
53926
|
+
exports.GameBoard3D = GameBoard3D;
|
|
51364
53927
|
exports.GameCanvas2D = GameCanvas2D;
|
|
51365
53928
|
exports.GameHud = GameHud;
|
|
51366
53929
|
exports.GameMenu = GameMenu;
|
|
@@ -51379,11 +53942,15 @@ exports.NotifyListener = NotifyListener;
|
|
|
51379
53942
|
exports.ObjectRulePanel = ObjectRulePanel;
|
|
51380
53943
|
exports.OrbitalStateMachineView = exports.StateMachineView;
|
|
51381
53944
|
exports.PhysicsManager = PhysicsManager;
|
|
53945
|
+
exports.PlatformerBoard = PlatformerBoard;
|
|
51382
53946
|
exports.PlatformerCanvas = PlatformerCanvas;
|
|
53947
|
+
exports.PlatformerTemplate = PlatformerTemplate;
|
|
51383
53948
|
exports.PowerupSlots = PowerupSlots;
|
|
51384
53949
|
exports.QuestTracker = QuestTracker;
|
|
51385
53950
|
exports.ResourceBar = ResourceBar;
|
|
51386
53951
|
exports.ResourceCounter = ResourceCounter;
|
|
53952
|
+
exports.RoguelikeBoard = RoguelikeBoard;
|
|
53953
|
+
exports.RoguelikeTemplate = RoguelikeTemplate;
|
|
51387
53954
|
exports.RuleEditor = RuleEditor;
|
|
51388
53955
|
exports.RuntimeDebugger = RuntimeDebugger;
|
|
51389
53956
|
exports.SHEET_COLUMNS = SHEET_COLUMNS;
|
|
@@ -51411,6 +53978,8 @@ exports.TableView = TableView;
|
|
|
51411
53978
|
exports.TerrainPalette = TerrainPalette;
|
|
51412
53979
|
exports.TimeSlotCell = TimeSlotCell;
|
|
51413
53980
|
exports.TimerDisplay = TimerDisplay;
|
|
53981
|
+
exports.TowerDefenseBoard = TowerDefenseBoard;
|
|
53982
|
+
exports.TowerDefenseTemplate = TowerDefenseTemplate;
|
|
51414
53983
|
exports.TraitFrame = TraitFrame;
|
|
51415
53984
|
exports.TraitSlot = TraitSlot;
|
|
51416
53985
|
exports.TraitStateViewer = TraitStateViewer;
|