@almadar/ui 5.47.0 → 5.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/avl/index.cjs +2998 -443
- package/dist/avl/index.css +504 -0
- package/dist/avl/index.js +2998 -444
- package/dist/components/core/templates/index.d.ts +3 -0
- package/dist/components/game/atoms/ActionButton.d.ts +4 -1
- package/dist/components/game/atoms/ComboCounter.d.ts +4 -1
- package/dist/components/game/atoms/ControlButton.d.ts +4 -2
- package/dist/components/game/atoms/DamageNumber.d.ts +4 -1
- package/dist/components/game/atoms/ResourceCounter.d.ts +4 -1
- package/dist/components/game/atoms/ScoreDisplay.d.ts +4 -1
- package/dist/components/game/atoms/StateIndicator.d.ts +4 -7
- package/dist/components/game/atoms/StatusEffect.d.ts +4 -1
- package/dist/components/game/atoms/TurnIndicator.d.ts +4 -1
- package/dist/components/game/atoms/WaypointMarker.d.ts +4 -1
- package/dist/components/game/molecules/EnemyPlate.d.ts +4 -1
- package/dist/components/game/molecules/StatBadge.d.ts +4 -1
- package/dist/components/game/molecules/three/index.cjs +1428 -346
- package/dist/components/game/molecules/three/index.css +0 -5
- package/dist/components/game/molecules/three/index.js +1429 -347
- package/dist/components/game/organisms/CastleBoard.d.ts +10 -1
- package/dist/components/game/organisms/GameBoard3D.d.ts +62 -0
- package/dist/components/game/organisms/PlatformerBoard.d.ts +51 -0
- package/dist/components/game/organisms/RoguelikeBoard.d.ts +59 -0
- package/dist/components/game/organisms/TowerDefenseBoard.d.ts +64 -0
- package/dist/components/game/organisms/index.d.ts +4 -0
- package/dist/components/game/organisms/puzzles/event-handler/EventHandlerBoard.d.ts +9 -1
- package/dist/components/game/organisms/puzzles/state-architect/StateArchitectBoard.d.ts +14 -1
- package/dist/components/game/templates/GameCanvas3DBattleTemplate.d.ts +22 -35
- package/dist/components/game/templates/GameCanvas3DCastleTemplate.d.ts +23 -23
- package/dist/components/game/templates/GameCanvas3DWorldMapTemplate.d.ts +27 -29
- package/dist/components/game/templates/PlatformerTemplate.d.ts +26 -0
- package/dist/components/game/templates/RoguelikeTemplate.d.ts +23 -0
- package/dist/components/game/templates/TowerDefenseTemplate.d.ts +43 -0
- package/dist/components/index.cjs +2991 -422
- package/dist/components/index.css +504 -0
- package/dist/components/index.js +2985 -424
- package/dist/docs/index.css +504 -0
- package/dist/providers/index.cjs +2981 -426
- package/dist/providers/index.css +504 -0
- package/dist/providers/index.js +2981 -427
- package/dist/runtime/index.cjs +2982 -427
- package/dist/runtime/index.css +504 -0
- package/dist/runtime/index.js +2982 -428
- package/package.json +1 -1
- package/tailwind-preset.cjs +1 -0
package/dist/components/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import * as React77 from 'react';
|
|
3
|
-
import React77__default, { useContext, useMemo, useRef, useEffect, useCallback, useState, Suspense, lazy, createContext, useLayoutEffect, useId, useSyncExternalStore } from 'react';
|
|
3
|
+
import React77__default, { useContext, useMemo, useRef, useEffect, useCallback, useState, Suspense, lazy, createContext, useLayoutEffect, useId, forwardRef, useImperativeHandle, useSyncExternalStore, Component } from 'react';
|
|
4
4
|
import { clsx } from 'clsx';
|
|
5
5
|
import { twMerge } from 'tailwind-merge';
|
|
6
6
|
import { EventBusContext, useTraitScope, TraitScopeProvider } from '@almadar/ui/providers';
|
|
@@ -41,6 +41,12 @@ import { DndContext, useSensors, useSensor, PointerSensor, KeyboardSensor, useDr
|
|
|
41
41
|
import { useSortable, arrayMove, sortableKeyboardCoordinates, SortableContext, rectSortingStrategy, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
|
42
42
|
import { CSS } from '@dnd-kit/utilities';
|
|
43
43
|
import { useNodeId, ReactFlowProvider, Handle, Position } from '@xyflow/react';
|
|
44
|
+
import * as THREE from 'three';
|
|
45
|
+
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
|
46
|
+
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
|
|
47
|
+
import { GLTFLoader as GLTFLoader$1 } from 'three/examples/jsm/loaders/GLTFLoader';
|
|
48
|
+
import { Canvas, useThree } from '@react-three/fiber';
|
|
49
|
+
import { Grid as Grid$1, OrbitControls } from '@react-three/drei';
|
|
44
50
|
import { useUISlots } from '@almadar/ui/context';
|
|
45
51
|
import { getPatternDefinition as getPatternDefinition$1, getComponentForPattern as getComponentForPattern$1 } from '@almadar/patterns';
|
|
46
52
|
|
|
@@ -1276,9 +1282,9 @@ function resolvePhosphor(name, weight, family) {
|
|
|
1276
1282
|
(lib) => {
|
|
1277
1283
|
const PhosphorComp = lib[target];
|
|
1278
1284
|
if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
|
|
1279
|
-
const
|
|
1285
|
+
const Component2 = PhosphorComp;
|
|
1280
1286
|
const Adapter = (props) => /* @__PURE__ */ jsx(
|
|
1281
|
-
|
|
1287
|
+
Component2,
|
|
1282
1288
|
{
|
|
1283
1289
|
weight,
|
|
1284
1290
|
className: props.className,
|
|
@@ -1302,9 +1308,9 @@ function resolveTabler(name, family) {
|
|
|
1302
1308
|
(lib) => {
|
|
1303
1309
|
const TablerComp = lib[target];
|
|
1304
1310
|
if (!TablerComp || typeof TablerComp !== "object") return null;
|
|
1305
|
-
const
|
|
1311
|
+
const Component2 = TablerComp;
|
|
1306
1312
|
const Adapter = (props) => /* @__PURE__ */ jsx(
|
|
1307
|
-
|
|
1313
|
+
Component2,
|
|
1308
1314
|
{
|
|
1309
1315
|
stroke: props.strokeWidth ?? 1.5,
|
|
1310
1316
|
className: props.className,
|
|
@@ -1328,9 +1334,9 @@ function resolveFa(name, family) {
|
|
|
1328
1334
|
(lib) => {
|
|
1329
1335
|
const FaComp = lib[target];
|
|
1330
1336
|
if (!FaComp || typeof FaComp !== "function") return null;
|
|
1331
|
-
const
|
|
1337
|
+
const Component2 = FaComp;
|
|
1332
1338
|
const Adapter = (props) => /* @__PURE__ */ jsx(
|
|
1333
|
-
|
|
1339
|
+
Component2,
|
|
1334
1340
|
{
|
|
1335
1341
|
className: props.className,
|
|
1336
1342
|
style: props.style,
|
|
@@ -3323,7 +3329,7 @@ var init_Box = __esm({
|
|
|
3323
3329
|
position,
|
|
3324
3330
|
className,
|
|
3325
3331
|
children,
|
|
3326
|
-
as:
|
|
3332
|
+
as: Component2 = "div",
|
|
3327
3333
|
action,
|
|
3328
3334
|
actionPayload,
|
|
3329
3335
|
hoverEvent,
|
|
@@ -3355,7 +3361,7 @@ var init_Box = __esm({
|
|
|
3355
3361
|
}, [hoverEvent, eventBus, onMouseLeave]);
|
|
3356
3362
|
const isClickable = action || onClick;
|
|
3357
3363
|
return React77__default.createElement(
|
|
3358
|
-
|
|
3364
|
+
Component2,
|
|
3359
3365
|
{
|
|
3360
3366
|
ref,
|
|
3361
3367
|
className: cn(
|
|
@@ -3404,10 +3410,10 @@ var init_Center = __esm({
|
|
|
3404
3410
|
className,
|
|
3405
3411
|
style,
|
|
3406
3412
|
children,
|
|
3407
|
-
as:
|
|
3413
|
+
as: Component2 = "div"
|
|
3408
3414
|
}) => {
|
|
3409
3415
|
const mergedStyle = minHeight ? { minHeight, ...style } : style;
|
|
3410
|
-
const Comp =
|
|
3416
|
+
const Comp = Component2;
|
|
3411
3417
|
return /* @__PURE__ */ jsx(
|
|
3412
3418
|
Comp,
|
|
3413
3419
|
{
|
|
@@ -4038,7 +4044,7 @@ var init_Stack = __esm({
|
|
|
4038
4044
|
className,
|
|
4039
4045
|
style,
|
|
4040
4046
|
children,
|
|
4041
|
-
as:
|
|
4047
|
+
as: Component2 = "div",
|
|
4042
4048
|
onClick,
|
|
4043
4049
|
onKeyDown,
|
|
4044
4050
|
role,
|
|
@@ -4056,7 +4062,7 @@ var init_Stack = __esm({
|
|
|
4056
4062
|
};
|
|
4057
4063
|
const isHorizontal = direction === "horizontal";
|
|
4058
4064
|
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";
|
|
4059
|
-
const Comp =
|
|
4065
|
+
const Comp = Component2;
|
|
4060
4066
|
return /* @__PURE__ */ jsx(
|
|
4061
4067
|
Comp,
|
|
4062
4068
|
{
|
|
@@ -4247,8 +4253,8 @@ var init_Typography = __esm({
|
|
|
4247
4253
|
children
|
|
4248
4254
|
}) => {
|
|
4249
4255
|
const variant = variantProp ?? (level ? `h${level}` : "body1");
|
|
4250
|
-
const
|
|
4251
|
-
const Comp =
|
|
4256
|
+
const Component2 = as || defaultElements[variant];
|
|
4257
|
+
const Comp = Component2;
|
|
4252
4258
|
return /* @__PURE__ */ jsx(
|
|
4253
4259
|
Comp,
|
|
4254
4260
|
{
|
|
@@ -7461,10 +7467,10 @@ var init_Container = __esm({
|
|
|
7461
7467
|
center = true,
|
|
7462
7468
|
className,
|
|
7463
7469
|
children,
|
|
7464
|
-
as:
|
|
7470
|
+
as: Component2 = "div"
|
|
7465
7471
|
}) => {
|
|
7466
7472
|
const resolvedSize = maxWidth ?? size ?? "lg";
|
|
7467
|
-
const Comp =
|
|
7473
|
+
const Comp = Component2;
|
|
7468
7474
|
return /* @__PURE__ */ jsx(
|
|
7469
7475
|
Comp,
|
|
7470
7476
|
{
|
|
@@ -8179,6 +8185,7 @@ var init_Accordion = __esm({
|
|
|
8179
8185
|
}
|
|
8180
8186
|
});
|
|
8181
8187
|
function ActionButton({
|
|
8188
|
+
assetUrl = DEFAULT_ASSET_URL,
|
|
8182
8189
|
label = "Attack",
|
|
8183
8190
|
icon,
|
|
8184
8191
|
cooldown = 0,
|
|
@@ -8218,13 +8225,23 @@ function ActionButton({
|
|
|
8218
8225
|
}
|
|
8219
8226
|
}
|
|
8220
8227
|
),
|
|
8221
|
-
|
|
8228
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
8229
|
+
"img",
|
|
8230
|
+
{
|
|
8231
|
+
src: assetUrl,
|
|
8232
|
+
alt: "",
|
|
8233
|
+
width: 16,
|
|
8234
|
+
height: 16,
|
|
8235
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
8236
|
+
className: cn("flex-shrink-0", sizes.icon)
|
|
8237
|
+
}
|
|
8238
|
+
) : icon ? /* @__PURE__ */ jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? (() => {
|
|
8222
8239
|
const I = resolveIcon(icon);
|
|
8223
8240
|
return I ? /* @__PURE__ */ jsx(I, { className: "w-4 h-4" }) : null;
|
|
8224
8241
|
})() : /* @__PURE__ */ (() => {
|
|
8225
8242
|
const I = icon;
|
|
8226
8243
|
return /* @__PURE__ */ jsx(I, { className: "w-4 h-4" });
|
|
8227
|
-
})() }),
|
|
8244
|
+
})() }) : null,
|
|
8228
8245
|
/* @__PURE__ */ jsx("span", { className: "relative z-10", children: label }),
|
|
8229
8246
|
hotkey && /* @__PURE__ */ jsx(
|
|
8230
8247
|
"span",
|
|
@@ -8240,7 +8257,7 @@ function ActionButton({
|
|
|
8240
8257
|
}
|
|
8241
8258
|
);
|
|
8242
8259
|
}
|
|
8243
|
-
var sizeMap, variantStyles7;
|
|
8260
|
+
var sizeMap, variantStyles7, DEFAULT_ASSET_URL;
|
|
8244
8261
|
var init_ActionButton = __esm({
|
|
8245
8262
|
"components/game/atoms/ActionButton.tsx"() {
|
|
8246
8263
|
init_cn();
|
|
@@ -8255,10 +8272,12 @@ var init_ActionButton = __esm({
|
|
|
8255
8272
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary-hover border-border",
|
|
8256
8273
|
danger: "bg-error text-error-foreground hover:bg-error/90 border-error"
|
|
8257
8274
|
};
|
|
8275
|
+
DEFAULT_ASSET_URL = "https://almadar-kflow-assets.web.app/shared/effects/particles/slash_01.png";
|
|
8258
8276
|
ActionButton.displayName = "ActionButton";
|
|
8259
8277
|
}
|
|
8260
8278
|
});
|
|
8261
8279
|
function ControlButton({
|
|
8280
|
+
assetUrl = DEFAULT_ASSET_URL2,
|
|
8262
8281
|
label,
|
|
8263
8282
|
icon,
|
|
8264
8283
|
size = "md",
|
|
@@ -8305,7 +8324,7 @@ function ControlButton({
|
|
|
8305
8324
|
},
|
|
8306
8325
|
[isPressed, releaseEvent, eventBus, onRelease]
|
|
8307
8326
|
);
|
|
8308
|
-
return /* @__PURE__ */
|
|
8327
|
+
return /* @__PURE__ */ jsx(
|
|
8309
8328
|
"button",
|
|
8310
8329
|
{
|
|
8311
8330
|
type: "button",
|
|
@@ -8326,20 +8345,27 @@ function ControlButton({
|
|
|
8326
8345
|
disabled && "opacity-50 cursor-not-allowed",
|
|
8327
8346
|
className
|
|
8328
8347
|
),
|
|
8329
|
-
children:
|
|
8330
|
-
|
|
8331
|
-
|
|
8332
|
-
|
|
8333
|
-
|
|
8334
|
-
|
|
8335
|
-
|
|
8336
|
-
|
|
8337
|
-
|
|
8338
|
-
|
|
8348
|
+
children: assetUrl ? /* @__PURE__ */ jsx(
|
|
8349
|
+
"img",
|
|
8350
|
+
{
|
|
8351
|
+
src: assetUrl,
|
|
8352
|
+
alt: "",
|
|
8353
|
+
width: 24,
|
|
8354
|
+
height: 24,
|
|
8355
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
8356
|
+
className: "flex-shrink-0"
|
|
8357
|
+
}
|
|
8358
|
+
) : icon ? /* @__PURE__ */ jsx("span", { className: "text-2xl", children: typeof icon === "string" ? /^[a-zA-Z0-9-]+$/.test(icon) ? (() => {
|
|
8359
|
+
const I = resolveIcon(icon);
|
|
8360
|
+
return I ? /* @__PURE__ */ jsx(I, { className: "w-6 h-6" }) : null;
|
|
8361
|
+
})() : icon : /* @__PURE__ */ (() => {
|
|
8362
|
+
const I = icon;
|
|
8363
|
+
return /* @__PURE__ */ jsx(I, { className: "w-6 h-6" });
|
|
8364
|
+
})() }) : label ? /* @__PURE__ */ jsx("span", { children: label }) : null
|
|
8339
8365
|
}
|
|
8340
8366
|
);
|
|
8341
8367
|
}
|
|
8342
|
-
var sizeMap2, shapeMap, variantMap;
|
|
8368
|
+
var sizeMap2, shapeMap, variantMap, DEFAULT_ASSET_URL2;
|
|
8343
8369
|
var init_ControlButton = __esm({
|
|
8344
8370
|
"components/game/atoms/ControlButton.tsx"() {
|
|
8345
8371
|
"use client";
|
|
@@ -8362,6 +8388,7 @@ var init_ControlButton = __esm({
|
|
|
8362
8388
|
secondary: "bg-secondary text-secondary-foreground border-border hover:bg-secondary-hover",
|
|
8363
8389
|
ghost: "bg-transparent text-foreground border-border hover:bg-muted"
|
|
8364
8390
|
};
|
|
8391
|
+
DEFAULT_ASSET_URL2 = "https://almadar-kflow-assets.web.app/shared/effects/particles/circle_01.png";
|
|
8365
8392
|
ControlButton.displayName = "ControlButton";
|
|
8366
8393
|
}
|
|
8367
8394
|
});
|
|
@@ -10384,15 +10411,15 @@ function BattleBoard({
|
|
|
10384
10411
|
const board = boardEntity(entity) ?? {};
|
|
10385
10412
|
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
|
|
10386
10413
|
const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
|
|
10387
|
-
const boardWidth = num(board.boardWidth, 8);
|
|
10388
|
-
const boardHeight = num(board.boardHeight, 6);
|
|
10414
|
+
const boardWidth = num(board.gridWidth ?? board.boardWidth, 8);
|
|
10415
|
+
const boardHeight = num(board.gridHeight ?? board.boardHeight, 6);
|
|
10389
10416
|
const assetManifest = propAssetManifest ?? board.assetManifest;
|
|
10390
10417
|
const backgroundImage = board.backgroundImage;
|
|
10391
10418
|
const units = rows(board.units);
|
|
10392
10419
|
const selectedUnitId = board.selectedUnitId ?? null;
|
|
10393
10420
|
const currentPhase = str(board.phase) || "observation";
|
|
10394
10421
|
const currentTurn = num(board.turn, 1);
|
|
10395
|
-
const gameResult = board.
|
|
10422
|
+
const gameResult = board.result ?? null;
|
|
10396
10423
|
const eventBus = useEventBus();
|
|
10397
10424
|
const { t } = useTranslate();
|
|
10398
10425
|
const [hoveredTile, setHoveredTile] = useState(null);
|
|
@@ -10413,7 +10440,7 @@ function BattleBoard({
|
|
|
10413
10440
|
const validMoves = useMemo(() => {
|
|
10414
10441
|
if (!selectedUnit || currentPhase !== "movement") return [];
|
|
10415
10442
|
const moves = [];
|
|
10416
|
-
const range = num(
|
|
10443
|
+
const range = num(board.movementRange, 2);
|
|
10417
10444
|
const origin = unitPosition(selectedUnit);
|
|
10418
10445
|
for (let dy = -range; dy <= range; dy++) {
|
|
10419
10446
|
for (let dx = -range; dx <= range; dx++) {
|
|
@@ -18698,15 +18725,24 @@ function CastleBoard({
|
|
|
18698
18725
|
featureClickEvent,
|
|
18699
18726
|
unitClickEvent,
|
|
18700
18727
|
tileClickEvent,
|
|
18728
|
+
playAgainEvent,
|
|
18701
18729
|
className
|
|
18702
18730
|
}) {
|
|
18703
18731
|
const eventBus = useEventBus();
|
|
18732
|
+
const { t } = useTranslate();
|
|
18704
18733
|
const resolved = boardEntity(entity);
|
|
18705
18734
|
const tiles = propTiles ?? (Array.isArray(resolved?.tiles) ? resolved.tiles : []);
|
|
18706
18735
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
18707
18736
|
const units = propUnits ?? (Array.isArray(resolved?.units) ? resolved.units : []);
|
|
18708
18737
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
18709
18738
|
const backgroundImage = resolved?.backgroundImage;
|
|
18739
|
+
const gold = num(resolved?.gold);
|
|
18740
|
+
const health = num(resolved?.health);
|
|
18741
|
+
const maxHealth = num(resolved?.maxHealth);
|
|
18742
|
+
const wave = num(resolved?.wave);
|
|
18743
|
+
const tickCount = num(resolved?.tickCount);
|
|
18744
|
+
const buildings = rows(resolved?.buildings);
|
|
18745
|
+
const result = str(resolved?.result) || "none";
|
|
18710
18746
|
const [hoveredTile, setHoveredTile] = useState(null);
|
|
18711
18747
|
const [selectedFeature, setSelectedFeature] = useState(null);
|
|
18712
18748
|
const hoveredFeature = useMemo(() => {
|
|
@@ -18719,7 +18755,7 @@ function CastleBoard({
|
|
|
18719
18755
|
(u) => u.position?.x === hoveredTile.x && u.position?.y === hoveredTile.y
|
|
18720
18756
|
) ?? null;
|
|
18721
18757
|
}, [hoveredTile, units]);
|
|
18722
|
-
const maxY = Math.max(...tiles.map((
|
|
18758
|
+
const maxY = Math.max(...tiles.map((t2) => t2.y), 0);
|
|
18723
18759
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
18724
18760
|
const tileToScreen = useCallback(
|
|
18725
18761
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
@@ -18754,6 +18790,11 @@ function CastleBoard({
|
|
|
18754
18790
|
}
|
|
18755
18791
|
}, [units, onUnitClick, unitClickEvent, eventBus]);
|
|
18756
18792
|
const clearSelection = useCallback(() => setSelectedFeature(null), []);
|
|
18793
|
+
const handlePlayAgain = useCallback(() => {
|
|
18794
|
+
if (playAgainEvent) {
|
|
18795
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
18796
|
+
}
|
|
18797
|
+
}, [playAgainEvent, eventBus]);
|
|
18757
18798
|
const ctx = useMemo(
|
|
18758
18799
|
() => ({
|
|
18759
18800
|
hoveredTile,
|
|
@@ -18762,11 +18803,18 @@ function CastleBoard({
|
|
|
18762
18803
|
selectedFeature,
|
|
18763
18804
|
clearSelection,
|
|
18764
18805
|
tileToScreen,
|
|
18765
|
-
scale
|
|
18806
|
+
scale,
|
|
18807
|
+
gold,
|
|
18808
|
+
health,
|
|
18809
|
+
maxHealth,
|
|
18810
|
+
wave,
|
|
18811
|
+
tickCount,
|
|
18812
|
+
buildings,
|
|
18813
|
+
result
|
|
18766
18814
|
}),
|
|
18767
|
-
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale]
|
|
18815
|
+
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale, gold, health, maxHealth, wave, tickCount, buildings, result]
|
|
18768
18816
|
);
|
|
18769
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("castle-board min-h-screen flex flex-col bg-background", className), children: [
|
|
18817
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("castle-board relative min-h-screen flex flex-col bg-background", className), children: [
|
|
18770
18818
|
header && header(ctx),
|
|
18771
18819
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
|
|
18772
18820
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-auto p-4 relative", children: [
|
|
@@ -18791,7 +18839,29 @@ function CastleBoard({
|
|
|
18791
18839
|
] }),
|
|
18792
18840
|
sidePanel && /* @__PURE__ */ jsx("div", { className: "w-96 shrink-0 border-l border-border bg-surface overflow-y-auto", children: sidePanel(ctx) })
|
|
18793
18841
|
] }),
|
|
18794
|
-
footer && footer(ctx)
|
|
18842
|
+
footer && footer(ctx),
|
|
18843
|
+
result !== "none" && /* @__PURE__ */ jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
18844
|
+
/* @__PURE__ */ jsx(
|
|
18845
|
+
Typography,
|
|
18846
|
+
{
|
|
18847
|
+
variant: "h2",
|
|
18848
|
+
className: cn(
|
|
18849
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
18850
|
+
result === "victory" ? "text-warning" : "text-error"
|
|
18851
|
+
),
|
|
18852
|
+
children: result === "victory" ? t("battle.victory") : t("battle.defeat")
|
|
18853
|
+
}
|
|
18854
|
+
),
|
|
18855
|
+
/* @__PURE__ */ jsx(
|
|
18856
|
+
Button,
|
|
18857
|
+
{
|
|
18858
|
+
variant: "primary",
|
|
18859
|
+
className: "px-8 py-3 font-semibold",
|
|
18860
|
+
onClick: handlePlayAgain,
|
|
18861
|
+
children: t("battle.playAgain")
|
|
18862
|
+
}
|
|
18863
|
+
)
|
|
18864
|
+
] }) })
|
|
18795
18865
|
] });
|
|
18796
18866
|
}
|
|
18797
18867
|
var init_CastleBoard = __esm({
|
|
@@ -18799,6 +18869,10 @@ var init_CastleBoard = __esm({
|
|
|
18799
18869
|
"use client";
|
|
18800
18870
|
init_cn();
|
|
18801
18871
|
init_useEventBus();
|
|
18872
|
+
init_Box();
|
|
18873
|
+
init_Button();
|
|
18874
|
+
init_Typography();
|
|
18875
|
+
init_Stack();
|
|
18802
18876
|
init_IsometricCanvas();
|
|
18803
18877
|
init_boardEntity();
|
|
18804
18878
|
init_isometric();
|
|
@@ -19721,22 +19795,16 @@ function ClassifierBoard({
|
|
|
19721
19795
|
const { emit } = useEventBus();
|
|
19722
19796
|
const { t } = useTranslate();
|
|
19723
19797
|
const resolved = boardEntity(entity);
|
|
19724
|
-
const [localAssignments, setLocalAssignments] = useState({});
|
|
19725
19798
|
const [headerError, setHeaderError] = useState(false);
|
|
19726
|
-
const [localSubmitted, setLocalSubmitted] = useState(false);
|
|
19727
|
-
const [localAttempts, setLocalAttempts] = useState(0);
|
|
19728
|
-
const [showHint, setShowHint] = useState(false);
|
|
19729
19799
|
const items = Array.isArray(resolved?.items) ? resolved.items : [];
|
|
19730
19800
|
const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
|
|
19731
|
-
const
|
|
19732
|
-
const
|
|
19733
|
-
const
|
|
19734
|
-
const assignments =
|
|
19735
|
-
if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
|
|
19801
|
+
const result = str(resolved?.result);
|
|
19802
|
+
const submitted = result === "win";
|
|
19803
|
+
const attempts = num(resolved?.attempts);
|
|
19804
|
+
const assignments = items.reduce((acc, item) => {
|
|
19805
|
+
if (item.assignedCategory != null && item.assignedCategory !== "") acc[item.id] = item.assignedCategory;
|
|
19736
19806
|
return acc;
|
|
19737
|
-
}, {})
|
|
19738
|
-
const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
|
|
19739
|
-
const submitted = entityDrivesResult || localSubmitted;
|
|
19807
|
+
}, {});
|
|
19740
19808
|
const unassignedItems = items.filter((item) => !assignments[item.id]);
|
|
19741
19809
|
const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
|
|
19742
19810
|
const results = submitted ? items.map((item) => ({
|
|
@@ -19744,54 +19812,25 @@ function ClassifierBoard({
|
|
|
19744
19812
|
assigned: assignments[item.id],
|
|
19745
19813
|
correct: assignments[item.id] === item.correctCategory
|
|
19746
19814
|
})) : [];
|
|
19747
|
-
const allCorrect =
|
|
19815
|
+
const allCorrect = result === "win";
|
|
19748
19816
|
const correctCount = results.filter((r) => r.correct).length;
|
|
19817
|
+
const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
|
|
19749
19818
|
const handleAssign = (itemId, categoryId) => {
|
|
19750
19819
|
if (submitted) return;
|
|
19751
|
-
if (assignEvent) {
|
|
19752
|
-
emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
19753
|
-
}
|
|
19754
|
-
if (!entityHasAssignments) {
|
|
19755
|
-
setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
|
|
19756
|
-
}
|
|
19820
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
19757
19821
|
};
|
|
19758
19822
|
const handleUnassign = (itemId) => {
|
|
19759
19823
|
if (submitted) return;
|
|
19760
|
-
if (
|
|
19761
|
-
setLocalAssignments((prev) => {
|
|
19762
|
-
const next = { ...prev };
|
|
19763
|
-
delete next[itemId];
|
|
19764
|
-
return next;
|
|
19765
|
-
});
|
|
19766
|
-
}
|
|
19824
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId: "" });
|
|
19767
19825
|
};
|
|
19768
|
-
const handleSubmit =
|
|
19769
|
-
if (checkEvent) {
|
|
19770
|
-
|
|
19771
|
-
|
|
19772
|
-
if (!entityDrivesResult) {
|
|
19773
|
-
setLocalSubmitted(true);
|
|
19774
|
-
setLocalAttempts((a) => a + 1);
|
|
19775
|
-
const correct = items.every((item) => assignments[item.id] === item.correctCategory);
|
|
19776
|
-
if (correct) {
|
|
19777
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
19778
|
-
}
|
|
19779
|
-
}
|
|
19780
|
-
}, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
|
|
19781
|
-
const handleReset = () => {
|
|
19782
|
-
if (!entityDrivesResult) setLocalSubmitted(false);
|
|
19783
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
19784
|
-
setShowHint(true);
|
|
19826
|
+
const handleSubmit = () => {
|
|
19827
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
19828
|
+
if (allAssigned && items.every((item) => assignments[item.id] === item.correctCategory)) {
|
|
19829
|
+
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
19785
19830
|
}
|
|
19786
19831
|
};
|
|
19787
19832
|
const handleFullReset = () => {
|
|
19788
|
-
if (playAgainEvent) {
|
|
19789
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
19790
|
-
}
|
|
19791
|
-
setLocalAssignments({});
|
|
19792
|
-
setLocalSubmitted(false);
|
|
19793
|
-
setLocalAttempts(0);
|
|
19794
|
-
setShowHint(false);
|
|
19833
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
19795
19834
|
};
|
|
19796
19835
|
if (!resolved) return null;
|
|
19797
19836
|
const theme = resolved.theme ?? void 0;
|
|
@@ -19830,17 +19869,17 @@ function ClassifierBoard({
|
|
|
19830
19869
|
/* @__PURE__ */ jsx(Badge, { size: "sm", children: catItems.length })
|
|
19831
19870
|
] }),
|
|
19832
19871
|
/* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-wrap min-h-[2rem]", children: catItems.map((item) => {
|
|
19833
|
-
const
|
|
19872
|
+
const result2 = results.find((r) => r.item.id === item.id);
|
|
19834
19873
|
return /* @__PURE__ */ jsxs(
|
|
19835
19874
|
Badge,
|
|
19836
19875
|
{
|
|
19837
19876
|
size: "sm",
|
|
19838
|
-
className: `cursor-pointer ${
|
|
19877
|
+
className: `cursor-pointer ${result2 ? result2.correct ? "border-success bg-success/10" : "border-error bg-error/10" : ""}`,
|
|
19839
19878
|
onClick: () => handleUnassign(item.id),
|
|
19840
19879
|
children: [
|
|
19841
19880
|
item.iconUrl && /* @__PURE__ */ jsx("img", { src: item.iconUrl, alt: "", className: "w-3 h-3 object-contain inline-block" }),
|
|
19842
19881
|
item.label,
|
|
19843
|
-
|
|
19882
|
+
result2 && /* @__PURE__ */ jsx(Icon, { icon: result2.correct ? CheckCircle : XCircle, size: "xs", className: result2.correct ? "text-success" : "text-error" })
|
|
19844
19883
|
]
|
|
19845
19884
|
},
|
|
19846
19885
|
item.id
|
|
@@ -19869,10 +19908,10 @@ function ClassifierBoard({
|
|
|
19869
19908
|
] }) }),
|
|
19870
19909
|
showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
|
|
19871
19910
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
19872
|
-
!submitted
|
|
19911
|
+
!submitted && /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
|
|
19873
19912
|
/* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
|
|
19874
19913
|
t("classifier.check")
|
|
19875
|
-
] })
|
|
19914
|
+
] }),
|
|
19876
19915
|
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
|
|
19877
19916
|
/* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
|
|
19878
19917
|
t("classifier.reset")
|
|
@@ -20159,6 +20198,7 @@ function getComboScale(combo) {
|
|
|
20159
20198
|
return "";
|
|
20160
20199
|
}
|
|
20161
20200
|
function ComboCounter({
|
|
20201
|
+
assetUrl = DEFAULT_ASSET_URL3,
|
|
20162
20202
|
combo = 5,
|
|
20163
20203
|
multiplier,
|
|
20164
20204
|
streak,
|
|
@@ -20178,6 +20218,17 @@ function ComboCounter({
|
|
|
20178
20218
|
className
|
|
20179
20219
|
),
|
|
20180
20220
|
children: [
|
|
20221
|
+
assetUrl && /* @__PURE__ */ jsx(
|
|
20222
|
+
"img",
|
|
20223
|
+
{
|
|
20224
|
+
src: assetUrl,
|
|
20225
|
+
alt: "combo",
|
|
20226
|
+
width: 24,
|
|
20227
|
+
height: 24,
|
|
20228
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
20229
|
+
className: "flex-shrink-0 mb-0.5"
|
|
20230
|
+
}
|
|
20231
|
+
),
|
|
20181
20232
|
/* @__PURE__ */ jsx("span", { className: cn("font-black tabular-nums leading-none", sizes.combo, getComboIntensity(combo)), children: combo }),
|
|
20182
20233
|
/* @__PURE__ */ jsx("span", { className: cn("font-bold uppercase tracking-wider text-muted-foreground", sizes.label), children: "combo" }),
|
|
20183
20234
|
multiplier != null && multiplier > 1 && /* @__PURE__ */ jsxs("span", { className: cn("font-bold text-warning tabular-nums", sizes.multiplier), children: [
|
|
@@ -20192,10 +20243,11 @@ function ComboCounter({
|
|
|
20192
20243
|
}
|
|
20193
20244
|
);
|
|
20194
20245
|
}
|
|
20195
|
-
var sizeMap4;
|
|
20246
|
+
var DEFAULT_ASSET_URL3, sizeMap4;
|
|
20196
20247
|
var init_ComboCounter = __esm({
|
|
20197
20248
|
"components/game/atoms/ComboCounter.tsx"() {
|
|
20198
20249
|
init_cn();
|
|
20250
|
+
DEFAULT_ASSET_URL3 = "https://almadar-kflow-assets.web.app/shared/effects/flash/flash00.png";
|
|
20199
20251
|
sizeMap4 = {
|
|
20200
20252
|
sm: { combo: "text-lg", label: "text-xs", multiplier: "text-xs" },
|
|
20201
20253
|
md: { combo: "text-2xl", label: "text-xs", multiplier: "text-sm" },
|
|
@@ -20604,7 +20656,7 @@ var init_CounterTemplate = __esm({
|
|
|
20604
20656
|
}
|
|
20605
20657
|
});
|
|
20606
20658
|
function ItemSlot({
|
|
20607
|
-
assetUrl =
|
|
20659
|
+
assetUrl = DEFAULT_ASSET_URL4,
|
|
20608
20660
|
icon = "sword",
|
|
20609
20661
|
label = "Iron Sword",
|
|
20610
20662
|
quantity,
|
|
@@ -20663,7 +20715,7 @@ function ItemSlot({
|
|
|
20663
20715
|
}
|
|
20664
20716
|
);
|
|
20665
20717
|
}
|
|
20666
|
-
var sizeMap5, rarityBorderMap, rarityGlowMap,
|
|
20718
|
+
var sizeMap5, rarityBorderMap, rarityGlowMap, DEFAULT_ASSET_URL4, assetSizeMap;
|
|
20667
20719
|
var init_ItemSlot = __esm({
|
|
20668
20720
|
"components/game/atoms/ItemSlot.tsx"() {
|
|
20669
20721
|
"use client";
|
|
@@ -20688,7 +20740,7 @@ var init_ItemSlot = __esm({
|
|
|
20688
20740
|
epic: "shadow-lg",
|
|
20689
20741
|
legendary: "shadow-lg"
|
|
20690
20742
|
};
|
|
20691
|
-
|
|
20743
|
+
DEFAULT_ASSET_URL4 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
20692
20744
|
assetSizeMap = {
|
|
20693
20745
|
sm: 28,
|
|
20694
20746
|
md: 40,
|
|
@@ -20867,6 +20919,7 @@ var init_DPad = __esm({
|
|
|
20867
20919
|
}
|
|
20868
20920
|
});
|
|
20869
20921
|
function DamageNumber({
|
|
20922
|
+
assetUrl = DEFAULT_ASSET_URL5,
|
|
20870
20923
|
value = 42,
|
|
20871
20924
|
type = "damage",
|
|
20872
20925
|
size = "md",
|
|
@@ -20875,22 +20928,35 @@ function DamageNumber({
|
|
|
20875
20928
|
const displayText = type === "miss" ? "MISS" : type === "heal" ? `+${value}` : `${value}`;
|
|
20876
20929
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
20877
20930
|
/* @__PURE__ */ jsx("style", { children: floatKeyframes }),
|
|
20878
|
-
/* @__PURE__ */
|
|
20931
|
+
/* @__PURE__ */ jsxs(
|
|
20879
20932
|
"span",
|
|
20880
20933
|
{
|
|
20881
20934
|
className: cn(
|
|
20882
|
-
"inline-
|
|
20935
|
+
"inline-flex items-center gap-0.5 font-mono select-none pointer-events-none",
|
|
20883
20936
|
sizeMap7[size],
|
|
20884
20937
|
typeStyles[type],
|
|
20885
20938
|
className
|
|
20886
20939
|
),
|
|
20887
20940
|
style: { animation: "damageFloat 1s ease-out forwards" },
|
|
20888
|
-
children:
|
|
20941
|
+
children: [
|
|
20942
|
+
assetUrl && /* @__PURE__ */ jsx(
|
|
20943
|
+
"img",
|
|
20944
|
+
{
|
|
20945
|
+
src: assetUrl,
|
|
20946
|
+
alt: "",
|
|
20947
|
+
width: 14,
|
|
20948
|
+
height: 14,
|
|
20949
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
20950
|
+
className: "flex-shrink-0"
|
|
20951
|
+
}
|
|
20952
|
+
),
|
|
20953
|
+
displayText
|
|
20954
|
+
]
|
|
20889
20955
|
}
|
|
20890
20956
|
)
|
|
20891
20957
|
] });
|
|
20892
20958
|
}
|
|
20893
|
-
var sizeMap7, typeStyles, floatKeyframes;
|
|
20959
|
+
var sizeMap7, typeStyles, floatKeyframes, DEFAULT_ASSET_URL5;
|
|
20894
20960
|
var init_DamageNumber = __esm({
|
|
20895
20961
|
"components/game/atoms/DamageNumber.tsx"() {
|
|
20896
20962
|
init_cn();
|
|
@@ -20912,6 +20978,7 @@ var init_DamageNumber = __esm({
|
|
|
20912
20978
|
100% { opacity: 0; transform: translateY(-32px) scale(0.8); }
|
|
20913
20979
|
}
|
|
20914
20980
|
`;
|
|
20981
|
+
DEFAULT_ASSET_URL5 = "https://almadar-kflow-assets.web.app/shared/effects/particles/spark_01.png";
|
|
20915
20982
|
DamageNumber.displayName = "DamageNumber";
|
|
20916
20983
|
}
|
|
20917
20984
|
});
|
|
@@ -24732,7 +24799,7 @@ var init_Flex = __esm({
|
|
|
24732
24799
|
basis,
|
|
24733
24800
|
className,
|
|
24734
24801
|
children,
|
|
24735
|
-
as:
|
|
24802
|
+
as: Component2 = "div"
|
|
24736
24803
|
}) => {
|
|
24737
24804
|
const flexStyle = {};
|
|
24738
24805
|
if (grow !== void 0 || shrink !== void 0 || basis !== void 0) {
|
|
@@ -24744,7 +24811,7 @@ var init_Flex = __esm({
|
|
|
24744
24811
|
flexStyle.flexBasis = typeof basis === "number" ? `${basis}px` : basis;
|
|
24745
24812
|
}
|
|
24746
24813
|
}
|
|
24747
|
-
const Comp =
|
|
24814
|
+
const Comp = Component2;
|
|
24748
24815
|
return /* @__PURE__ */ jsx(
|
|
24749
24816
|
Comp,
|
|
24750
24817
|
{
|
|
@@ -24864,11 +24931,11 @@ var init_Grid = __esm({
|
|
|
24864
24931
|
className,
|
|
24865
24932
|
style,
|
|
24866
24933
|
children,
|
|
24867
|
-
as:
|
|
24934
|
+
as: Component2 = "div"
|
|
24868
24935
|
}) => {
|
|
24869
24936
|
const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
|
|
24870
24937
|
return React77__default.createElement(
|
|
24871
|
-
|
|
24938
|
+
Component2,
|
|
24872
24939
|
{
|
|
24873
24940
|
className: cn(
|
|
24874
24941
|
"grid",
|
|
@@ -27017,6 +27084,7 @@ var init_HealthBar = __esm({
|
|
|
27017
27084
|
}
|
|
27018
27085
|
});
|
|
27019
27086
|
function ScoreDisplay({
|
|
27087
|
+
assetUrl = DEFAULT_ASSET_URL6,
|
|
27020
27088
|
value,
|
|
27021
27089
|
label,
|
|
27022
27090
|
icon,
|
|
@@ -27063,14 +27131,24 @@ function ScoreDisplay({
|
|
|
27063
27131
|
className
|
|
27064
27132
|
),
|
|
27065
27133
|
children: [
|
|
27066
|
-
|
|
27134
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
27135
|
+
"img",
|
|
27136
|
+
{
|
|
27137
|
+
src: assetUrl,
|
|
27138
|
+
alt: "",
|
|
27139
|
+
width: 20,
|
|
27140
|
+
height: 20,
|
|
27141
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
27142
|
+
className: "flex-shrink-0"
|
|
27143
|
+
}
|
|
27144
|
+
) : icon ? /* @__PURE__ */ jsx("span", { className: "flex-shrink-0", children: typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon }) : /* @__PURE__ */ jsx(Icon, { icon }) }) : null,
|
|
27067
27145
|
label && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label }),
|
|
27068
27146
|
/* @__PURE__ */ jsx("span", { className: "tabular-nums", children: formattedValue })
|
|
27069
27147
|
]
|
|
27070
27148
|
}
|
|
27071
27149
|
);
|
|
27072
27150
|
}
|
|
27073
|
-
var sizeMap10;
|
|
27151
|
+
var sizeMap10, DEFAULT_ASSET_URL6;
|
|
27074
27152
|
var init_ScoreDisplay = __esm({
|
|
27075
27153
|
"components/game/atoms/ScoreDisplay.tsx"() {
|
|
27076
27154
|
"use client";
|
|
@@ -27082,10 +27160,12 @@ var init_ScoreDisplay = __esm({
|
|
|
27082
27160
|
lg: "text-2xl",
|
|
27083
27161
|
xl: "text-4xl"
|
|
27084
27162
|
};
|
|
27163
|
+
DEFAULT_ASSET_URL6 = "https://almadar-kflow-assets.web.app/shared/effects/particles/star_01.png";
|
|
27085
27164
|
ScoreDisplay.displayName = "ScoreDisplay";
|
|
27086
27165
|
}
|
|
27087
27166
|
});
|
|
27088
27167
|
function StatBadge({
|
|
27168
|
+
assetUrl = DEFAULT_ASSET_URL7,
|
|
27089
27169
|
label,
|
|
27090
27170
|
value = 0,
|
|
27091
27171
|
max,
|
|
@@ -27109,7 +27189,17 @@ function StatBadge({
|
|
|
27109
27189
|
className
|
|
27110
27190
|
),
|
|
27111
27191
|
children: [
|
|
27112
|
-
|
|
27192
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
27193
|
+
"img",
|
|
27194
|
+
{
|
|
27195
|
+
src: assetUrl,
|
|
27196
|
+
alt: "",
|
|
27197
|
+
width: 16,
|
|
27198
|
+
height: 16,
|
|
27199
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
27200
|
+
className: "flex-shrink-0"
|
|
27201
|
+
}
|
|
27202
|
+
) : icon ? /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 text-lg", children: typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, className: "w-4 h-4" }) : /* @__PURE__ */ jsx(Icon, { icon, className: "w-4 h-4" }) }) : null,
|
|
27113
27203
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground font-medium", children: label }),
|
|
27114
27204
|
format === "hearts" && max && /* @__PURE__ */ jsx(
|
|
27115
27205
|
HealthBar,
|
|
@@ -27142,7 +27232,7 @@ function StatBadge({
|
|
|
27142
27232
|
}
|
|
27143
27233
|
);
|
|
27144
27234
|
}
|
|
27145
|
-
var sizeMap11, variantMap2;
|
|
27235
|
+
var sizeMap11, variantMap2, DEFAULT_ASSET_URL7;
|
|
27146
27236
|
var init_StatBadge = __esm({
|
|
27147
27237
|
"components/game/molecules/StatBadge.tsx"() {
|
|
27148
27238
|
init_cn();
|
|
@@ -27161,6 +27251,7 @@ var init_StatBadge = __esm({
|
|
|
27161
27251
|
warning: "bg-warning/15 border-warning/40 text-foreground",
|
|
27162
27252
|
danger: "bg-error/15 border-error/40 text-foreground"
|
|
27163
27253
|
};
|
|
27254
|
+
DEFAULT_ASSET_URL7 = "https://almadar-kflow-assets.web.app/shared/effects/particles/light_01.png";
|
|
27164
27255
|
StatBadge.displayName = "StatBadge";
|
|
27165
27256
|
}
|
|
27166
27257
|
});
|
|
@@ -27244,6 +27335,7 @@ var init_InventoryGrid = __esm({
|
|
|
27244
27335
|
}
|
|
27245
27336
|
});
|
|
27246
27337
|
function WaypointMarker({
|
|
27338
|
+
assetUrl = DEFAULT_ASSET_URL8,
|
|
27247
27339
|
label,
|
|
27248
27340
|
icon,
|
|
27249
27341
|
active = true,
|
|
@@ -27282,7 +27374,17 @@ function WaypointMarker({
|
|
|
27282
27374
|
active && !completed && "bg-info text-foreground",
|
|
27283
27375
|
!active && !completed && "bg-muted"
|
|
27284
27376
|
),
|
|
27285
|
-
children: completed ? checkIcon :
|
|
27377
|
+
children: completed ? checkIcon : assetUrl ? /* @__PURE__ */ jsx(
|
|
27378
|
+
"img",
|
|
27379
|
+
{
|
|
27380
|
+
src: assetUrl,
|
|
27381
|
+
alt: label,
|
|
27382
|
+
width: sizes.img,
|
|
27383
|
+
height: sizes.img,
|
|
27384
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
27385
|
+
className: "flex-shrink-0"
|
|
27386
|
+
}
|
|
27387
|
+
) : icon ? typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon }) : /* @__PURE__ */ jsx(Icon, { icon }) : null
|
|
27286
27388
|
}
|
|
27287
27389
|
)
|
|
27288
27390
|
] }),
|
|
@@ -27299,15 +27401,16 @@ function WaypointMarker({
|
|
|
27299
27401
|
)
|
|
27300
27402
|
] });
|
|
27301
27403
|
}
|
|
27302
|
-
var sizeMap12, checkIcon;
|
|
27404
|
+
var DEFAULT_ASSET_URL8, sizeMap12, checkIcon;
|
|
27303
27405
|
var init_WaypointMarker = __esm({
|
|
27304
27406
|
"components/game/atoms/WaypointMarker.tsx"() {
|
|
27305
27407
|
init_cn();
|
|
27306
27408
|
init_Icon();
|
|
27409
|
+
DEFAULT_ASSET_URL8 = "https://almadar-kflow-assets.web.app/shared/world-map/battle_marker.png";
|
|
27307
27410
|
sizeMap12 = {
|
|
27308
|
-
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1" },
|
|
27309
|
-
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5" },
|
|
27310
|
-
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2" }
|
|
27411
|
+
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1", img: 12 },
|
|
27412
|
+
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5", img: 18 },
|
|
27413
|
+
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2", img: 24 }
|
|
27311
27414
|
};
|
|
27312
27415
|
checkIcon = /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 3, className: "w-3/5 h-3/5", children: /* @__PURE__ */ jsx("path", { d: "M5 13l4 4L19 7", strokeLinecap: "round", strokeLinejoin: "round" }) });
|
|
27313
27416
|
WaypointMarker.displayName = "WaypointMarker";
|
|
@@ -27821,6 +27924,7 @@ var init_ResourceBar = __esm({
|
|
|
27821
27924
|
}
|
|
27822
27925
|
});
|
|
27823
27926
|
function TurnIndicator({
|
|
27927
|
+
assetUrl = DEFAULT_ASSET_URL9,
|
|
27824
27928
|
currentTurn = 1,
|
|
27825
27929
|
maxTurns,
|
|
27826
27930
|
activeTeam,
|
|
@@ -27853,14 +27957,24 @@ function TurnIndicator({
|
|
|
27853
27957
|
] }),
|
|
27854
27958
|
activeTeam && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
27855
27959
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "|" }),
|
|
27856
|
-
/* @__PURE__ */ jsx(
|
|
27960
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
27961
|
+
"img",
|
|
27962
|
+
{
|
|
27963
|
+
src: assetUrl,
|
|
27964
|
+
alt: "",
|
|
27965
|
+
width: 12,
|
|
27966
|
+
height: 12,
|
|
27967
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
27968
|
+
className: "flex-shrink-0"
|
|
27969
|
+
}
|
|
27970
|
+
) : /* @__PURE__ */ jsx("span", { className: cn("rounded-full bg-success/20", sizes.dot) }),
|
|
27857
27971
|
/* @__PURE__ */ jsx("span", { className: "text-success", children: activeTeam })
|
|
27858
27972
|
] })
|
|
27859
27973
|
]
|
|
27860
27974
|
}
|
|
27861
27975
|
);
|
|
27862
27976
|
}
|
|
27863
|
-
var sizeMap14;
|
|
27977
|
+
var sizeMap14, DEFAULT_ASSET_URL9;
|
|
27864
27978
|
var init_TurnIndicator = __esm({
|
|
27865
27979
|
"components/game/atoms/TurnIndicator.tsx"() {
|
|
27866
27980
|
init_cn();
|
|
@@ -27869,6 +27983,7 @@ var init_TurnIndicator = __esm({
|
|
|
27869
27983
|
md: { wrapper: "text-sm gap-2 px-3 py-1", dot: "w-2 h-2" },
|
|
27870
27984
|
lg: { wrapper: "text-base gap-2.5 px-4 py-1.5", dot: "w-2.5 h-2.5" }
|
|
27871
27985
|
};
|
|
27986
|
+
DEFAULT_ASSET_URL9 = "https://almadar-kflow-assets.web.app/shared/effects/particles/symbol_01.png";
|
|
27872
27987
|
TurnIndicator.displayName = "TurnIndicator";
|
|
27873
27988
|
}
|
|
27874
27989
|
});
|
|
@@ -27939,6 +28054,7 @@ var init_TurnPanel = __esm({
|
|
|
27939
28054
|
}
|
|
27940
28055
|
});
|
|
27941
28056
|
function EnemyPlate({
|
|
28057
|
+
assetUrl = DEFAULT_ASSET_URL10,
|
|
27942
28058
|
name = "Shadow Guard",
|
|
27943
28059
|
health = 80,
|
|
27944
28060
|
maxHealth = 100,
|
|
@@ -27956,15 +28072,28 @@ function EnemyPlate({
|
|
|
27956
28072
|
),
|
|
27957
28073
|
children: [
|
|
27958
28074
|
/* @__PURE__ */ jsxs(Box, { className: "flex items-center justify-between gap-2", children: [
|
|
27959
|
-
/* @__PURE__ */
|
|
27960
|
-
|
|
27961
|
-
|
|
27962
|
-
|
|
27963
|
-
|
|
27964
|
-
|
|
27965
|
-
|
|
27966
|
-
|
|
27967
|
-
|
|
28075
|
+
/* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-1.5 min-w-0", children: [
|
|
28076
|
+
assetUrl && /* @__PURE__ */ jsx(
|
|
28077
|
+
"img",
|
|
28078
|
+
{
|
|
28079
|
+
src: assetUrl,
|
|
28080
|
+
alt: name,
|
|
28081
|
+
width: 24,
|
|
28082
|
+
height: 24,
|
|
28083
|
+
style: { objectFit: "cover", borderRadius: "50%" },
|
|
28084
|
+
className: "flex-shrink-0"
|
|
28085
|
+
}
|
|
28086
|
+
),
|
|
28087
|
+
/* @__PURE__ */ jsx(
|
|
28088
|
+
Typography,
|
|
28089
|
+
{
|
|
28090
|
+
variant: "caption",
|
|
28091
|
+
weight: "bold",
|
|
28092
|
+
className: "text-[var(--color-foreground)] truncate",
|
|
28093
|
+
children: name
|
|
28094
|
+
}
|
|
28095
|
+
)
|
|
28096
|
+
] }),
|
|
27968
28097
|
level != null && /* @__PURE__ */ jsxs(Badge, { variant: "neutral", size: "sm", children: [
|
|
27969
28098
|
"Lv.",
|
|
27970
28099
|
level
|
|
@@ -28009,7 +28138,7 @@ function EnemyPlate({
|
|
|
28009
28138
|
}
|
|
28010
28139
|
);
|
|
28011
28140
|
}
|
|
28012
|
-
var effectVariantMap2, DEFAULT_ENEMY_EFFECTS;
|
|
28141
|
+
var effectVariantMap2, DEFAULT_ENEMY_EFFECTS, DEFAULT_ASSET_URL10;
|
|
28013
28142
|
var init_EnemyPlate = __esm({
|
|
28014
28143
|
"components/game/molecules/EnemyPlate.tsx"() {
|
|
28015
28144
|
"use client";
|
|
@@ -28026,6 +28155,7 @@ var init_EnemyPlate = __esm({
|
|
|
28026
28155
|
DEFAULT_ENEMY_EFFECTS = [
|
|
28027
28156
|
{ icon: "flame", label: "Burn", variant: "debuff" }
|
|
28028
28157
|
];
|
|
28158
|
+
DEFAULT_ASSET_URL10 = "https://almadar-kflow-assets.web.app/shared/portraits/shadow-legion.png";
|
|
28029
28159
|
EnemyPlate.displayName = "EnemyPlate";
|
|
28030
28160
|
}
|
|
28031
28161
|
});
|
|
@@ -29579,13 +29709,13 @@ var init_MapView = __esm({
|
|
|
29579
29709
|
shadowSize: [41, 41]
|
|
29580
29710
|
});
|
|
29581
29711
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
29582
|
-
const { useEffect:
|
|
29712
|
+
const { useEffect: useEffect80, useRef: useRef77, useCallback: useCallback120, useState: useState110 } = React77__default;
|
|
29583
29713
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
29584
|
-
const { useEventBus:
|
|
29714
|
+
const { useEventBus: useEventBus3 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
29585
29715
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
29586
29716
|
const map = useMap();
|
|
29587
|
-
const prevRef =
|
|
29588
|
-
|
|
29717
|
+
const prevRef = useRef77({ centerLat, centerLng, zoom });
|
|
29718
|
+
useEffect80(() => {
|
|
29589
29719
|
const prev = prevRef.current;
|
|
29590
29720
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
29591
29721
|
map.setView([centerLat, centerLng], zoom);
|
|
@@ -29596,7 +29726,7 @@ var init_MapView = __esm({
|
|
|
29596
29726
|
}
|
|
29597
29727
|
function MapClickHandler({ onMapClick }) {
|
|
29598
29728
|
const map = useMap();
|
|
29599
|
-
|
|
29729
|
+
useEffect80(() => {
|
|
29600
29730
|
if (!onMapClick) return;
|
|
29601
29731
|
const handler = (e) => {
|
|
29602
29732
|
onMapClick(e.latlng.lat, e.latlng.lng);
|
|
@@ -29623,9 +29753,9 @@ var init_MapView = __esm({
|
|
|
29623
29753
|
className,
|
|
29624
29754
|
showAttribution = true
|
|
29625
29755
|
}) {
|
|
29626
|
-
const eventBus =
|
|
29627
|
-
const [clickedPosition, setClickedPosition] =
|
|
29628
|
-
const handleMapClick =
|
|
29756
|
+
const eventBus = useEventBus3();
|
|
29757
|
+
const [clickedPosition, setClickedPosition] = useState110(null);
|
|
29758
|
+
const handleMapClick = useCallback120((lat, lng) => {
|
|
29629
29759
|
if (showClickedPin) {
|
|
29630
29760
|
setClickedPosition({ lat, lng });
|
|
29631
29761
|
}
|
|
@@ -29634,7 +29764,7 @@ var init_MapView = __esm({
|
|
|
29634
29764
|
eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
|
|
29635
29765
|
}
|
|
29636
29766
|
}, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
|
|
29637
|
-
const handleMarkerClick =
|
|
29767
|
+
const handleMarkerClick = useCallback120((marker) => {
|
|
29638
29768
|
onMarkerClick?.(marker);
|
|
29639
29769
|
if (markerClickEvent) {
|
|
29640
29770
|
eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
|
|
@@ -36761,10 +36891,10 @@ var init_Section = __esm({
|
|
|
36761
36891
|
children,
|
|
36762
36892
|
headerClassName,
|
|
36763
36893
|
contentClassName,
|
|
36764
|
-
as:
|
|
36894
|
+
as: Component2 = "section"
|
|
36765
36895
|
}) => {
|
|
36766
36896
|
const hasHeader = title || description || action;
|
|
36767
|
-
const Comp =
|
|
36897
|
+
const Comp = Component2;
|
|
36768
36898
|
return /* @__PURE__ */ jsxs(
|
|
36769
36899
|
Comp,
|
|
36770
36900
|
{
|
|
@@ -39077,44 +39207,27 @@ function DebuggerBoard({
|
|
|
39077
39207
|
const { t } = useTranslate();
|
|
39078
39208
|
const resolved = boardEntity(entity);
|
|
39079
39209
|
const [headerError, setHeaderError] = useState(false);
|
|
39080
|
-
const [showHint, setShowHint] = useState(false);
|
|
39081
39210
|
const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
|
|
39082
|
-
const result = resolved?.result
|
|
39211
|
+
const result = str(resolved?.result) || "none";
|
|
39083
39212
|
const attempts = num(resolved?.attempts);
|
|
39084
|
-
const submitted = result
|
|
39213
|
+
const submitted = result === "win";
|
|
39214
|
+
const showHint = !submitted && attempts >= 2 && Boolean(str(resolved?.hint));
|
|
39085
39215
|
const bugLines = lines.filter((l) => l.isBug);
|
|
39086
39216
|
const flaggedLines = lines.filter((l) => l.isFlagged);
|
|
39087
39217
|
const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
|
|
39088
39218
|
const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
|
|
39089
|
-
const allCorrect =
|
|
39219
|
+
const allCorrect = submitted;
|
|
39090
39220
|
const toggleLine = (lineId) => {
|
|
39091
39221
|
if (submitted) return;
|
|
39092
|
-
if (toggleFlagEvent) {
|
|
39093
|
-
emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
39094
|
-
}
|
|
39222
|
+
if (toggleFlagEvent) emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
39095
39223
|
};
|
|
39096
39224
|
const handleSubmit = useCallback(() => {
|
|
39097
|
-
if (checkEvent) {
|
|
39098
|
-
emit(`UI:${checkEvent}`, {});
|
|
39099
|
-
}
|
|
39225
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
39100
39226
|
const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
|
|
39101
|
-
if (correct) {
|
|
39102
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
39103
|
-
}
|
|
39227
|
+
if (correct) emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
39104
39228
|
}, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
|
|
39105
39229
|
const handleReset = () => {
|
|
39106
|
-
if (playAgainEvent) {
|
|
39107
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
39108
|
-
}
|
|
39109
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
39110
|
-
setShowHint(true);
|
|
39111
|
-
}
|
|
39112
|
-
};
|
|
39113
|
-
const handleFullReset = () => {
|
|
39114
|
-
if (playAgainEvent) {
|
|
39115
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
39116
|
-
}
|
|
39117
|
-
setShowHint(false);
|
|
39230
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
39118
39231
|
};
|
|
39119
39232
|
if (!resolved) return null;
|
|
39120
39233
|
const theme = resolved.theme ?? void 0;
|
|
@@ -39138,7 +39251,7 @@ function DebuggerBoard({
|
|
|
39138
39251
|
/* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
|
|
39139
39252
|
] }),
|
|
39140
39253
|
/* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) }),
|
|
39141
|
-
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(
|
|
39254
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(lines.filter((l) => l.isBug).length) }) })
|
|
39142
39255
|
] }) }),
|
|
39143
39256
|
/* @__PURE__ */ jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsx(VStack, { gap: "none", children: lines.map((line, i) => {
|
|
39144
39257
|
const isFlagged = !!line.isFlagged;
|
|
@@ -39188,11 +39301,11 @@ function DebuggerBoard({
|
|
|
39188
39301
|
] }) }),
|
|
39189
39302
|
showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
|
|
39190
39303
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
39191
|
-
!submitted
|
|
39304
|
+
!submitted && /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
|
|
39192
39305
|
/* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
|
|
39193
39306
|
t("debugger.submit")
|
|
39194
|
-
] })
|
|
39195
|
-
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick:
|
|
39307
|
+
] }),
|
|
39308
|
+
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleReset, children: [
|
|
39196
39309
|
/* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
|
|
39197
39310
|
t("debugger.reset")
|
|
39198
39311
|
] })
|
|
@@ -39782,6 +39895,7 @@ var init_DrawerSlot = __esm({
|
|
|
39782
39895
|
}
|
|
39783
39896
|
});
|
|
39784
39897
|
function StateIndicator({
|
|
39898
|
+
assetUrl = DEFAULT_ASSET_URL11,
|
|
39785
39899
|
state = "idle",
|
|
39786
39900
|
label,
|
|
39787
39901
|
size = "md",
|
|
@@ -39804,18 +39918,29 @@ function StateIndicator({
|
|
|
39804
39918
|
className
|
|
39805
39919
|
),
|
|
39806
39920
|
children: [
|
|
39807
|
-
/* @__PURE__ */ jsx(Box, { as: "span", children:
|
|
39921
|
+
/* @__PURE__ */ jsx(Box, { as: "span", children: assetUrl ? /* @__PURE__ */ jsx(
|
|
39922
|
+
"img",
|
|
39923
|
+
{
|
|
39924
|
+
src: assetUrl,
|
|
39925
|
+
alt: displayLabel,
|
|
39926
|
+
width: 16,
|
|
39927
|
+
height: 16,
|
|
39928
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
39929
|
+
className: "flex-shrink-0"
|
|
39930
|
+
}
|
|
39931
|
+
) : typeof config.icon === "string" ? /^[a-zA-Z0-9-]+$/.test(config.icon) ? /* @__PURE__ */ jsx(Icon, { name: config.icon }) : config.icon : /* @__PURE__ */ jsx(Icon, { icon: config.icon }) }),
|
|
39808
39932
|
/* @__PURE__ */ jsx(Box, { as: "span", children: displayLabel })
|
|
39809
39933
|
]
|
|
39810
39934
|
}
|
|
39811
39935
|
);
|
|
39812
39936
|
}
|
|
39813
|
-
var DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
39937
|
+
var DEFAULT_ASSET_URL11, DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
39814
39938
|
var init_StateIndicator = __esm({
|
|
39815
39939
|
"components/game/atoms/StateIndicator.tsx"() {
|
|
39816
39940
|
init_Box();
|
|
39817
39941
|
init_Icon();
|
|
39818
39942
|
init_cn();
|
|
39943
|
+
DEFAULT_ASSET_URL11 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
39819
39944
|
DEFAULT_STATE_STYLES = {
|
|
39820
39945
|
idle: { icon: "\u23F8", bgClass: "bg-muted" },
|
|
39821
39946
|
active: { icon: "\u25B6", bgClass: "bg-success" },
|
|
@@ -40289,39 +40414,40 @@ function EventHandlerBoard({
|
|
|
40289
40414
|
stepDurationMs = 800,
|
|
40290
40415
|
playEvent,
|
|
40291
40416
|
completeEvent,
|
|
40417
|
+
editRuleEvent,
|
|
40418
|
+
playAgainEvent,
|
|
40292
40419
|
className
|
|
40293
40420
|
}) {
|
|
40294
40421
|
const { emit } = useEventBus();
|
|
40295
40422
|
const { t } = useTranslate();
|
|
40296
40423
|
const resolved = boardEntity(entity);
|
|
40297
|
-
const
|
|
40298
|
-
const
|
|
40424
|
+
const objects = rows(resolved?.objects);
|
|
40425
|
+
const entityResult = str(resolved?.result) || "none";
|
|
40426
|
+
const isSuccess = entityResult === "win";
|
|
40427
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
40299
40428
|
const [selectedObjectId, setSelectedObjectId] = useState(
|
|
40300
|
-
|
|
40429
|
+
objects[0] ? objId(objects[0]) : null
|
|
40301
40430
|
);
|
|
40302
40431
|
const [headerError, setHeaderError] = useState(false);
|
|
40303
|
-
const [
|
|
40432
|
+
const [isPlaying, setIsPlaying] = useState(false);
|
|
40304
40433
|
const [eventLog, setEventLog] = useState([]);
|
|
40305
|
-
const [attempts, setAttempts] = useState(0);
|
|
40306
40434
|
const timerRef = useRef(null);
|
|
40307
40435
|
const logIdCounter = useRef(0);
|
|
40308
40436
|
useEffect(() => () => {
|
|
40309
40437
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
40310
40438
|
}, []);
|
|
40311
|
-
const selectedObject = objects.find((o) => objId(o) === selectedObjectId)
|
|
40439
|
+
const selectedObject = objects.find((o) => objId(o) === selectedObjectId) ?? null;
|
|
40312
40440
|
const handleRulesChange = useCallback((objectId, rules) => {
|
|
40313
|
-
|
|
40314
|
-
|
|
40315
|
-
));
|
|
40316
|
-
}, []);
|
|
40441
|
+
if (editRuleEvent) emit(`UI:${editRuleEvent}`, { objectId, rules });
|
|
40442
|
+
}, [editRuleEvent, emit]);
|
|
40317
40443
|
const addLogEntry = useCallback((icon, message, status = "done") => {
|
|
40318
40444
|
const id = `log-${logIdCounter.current++}`;
|
|
40319
40445
|
setEventLog((prev) => [...prev, { id, timestamp: Date.now(), icon, message, status }]);
|
|
40320
40446
|
}, []);
|
|
40321
40447
|
const handlePlay = useCallback(() => {
|
|
40322
|
-
if (
|
|
40448
|
+
if (isPlaying || isSuccess) return;
|
|
40323
40449
|
if (playEvent) emit(`UI:${playEvent}`, {});
|
|
40324
|
-
|
|
40450
|
+
setIsPlaying(true);
|
|
40325
40451
|
setEventLog([]);
|
|
40326
40452
|
const allRules = [];
|
|
40327
40453
|
objects.forEach((obj) => {
|
|
@@ -40337,15 +40463,8 @@ function EventHandlerBoard({
|
|
|
40337
40463
|
let goalReached = false;
|
|
40338
40464
|
const processNext = () => {
|
|
40339
40465
|
if (eventQueue.length === 0 || stepIdx > 20) {
|
|
40340
|
-
|
|
40341
|
-
|
|
40342
|
-
if (completeEvent) {
|
|
40343
|
-
emit(`UI:${completeEvent}`, { success: true });
|
|
40344
|
-
}
|
|
40345
|
-
} else {
|
|
40346
|
-
setAttempts((prev) => prev + 1);
|
|
40347
|
-
setPlayState("fail");
|
|
40348
|
-
}
|
|
40466
|
+
setIsPlaying(false);
|
|
40467
|
+
if (goalReached && completeEvent) emit(`UI:${completeEvent}`, { success: true });
|
|
40349
40468
|
return;
|
|
40350
40469
|
}
|
|
40351
40470
|
const currentEvent = eventQueue.shift();
|
|
@@ -40376,21 +40495,19 @@ function EventHandlerBoard({
|
|
|
40376
40495
|
addLogEntry("\u{1F3AC}", t("eventHandler.simulationStarted", { events: triggers.join(", ") }), "active");
|
|
40377
40496
|
}
|
|
40378
40497
|
timerRef.current = setTimeout(processNext, stepDurationMs);
|
|
40379
|
-
}, [
|
|
40498
|
+
}, [isPlaying, isSuccess, objects, resolved, stepDurationMs, playEvent, completeEvent, emit, addLogEntry, t]);
|
|
40380
40499
|
const handleTryAgain = useCallback(() => {
|
|
40381
40500
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
40382
|
-
|
|
40501
|
+
setIsPlaying(false);
|
|
40383
40502
|
setEventLog([]);
|
|
40384
40503
|
}, []);
|
|
40385
40504
|
const handleReset = useCallback(() => {
|
|
40386
40505
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
40387
|
-
|
|
40388
|
-
setObjects([...resetObjects]);
|
|
40389
|
-
setPlayState("editing");
|
|
40506
|
+
setIsPlaying(false);
|
|
40390
40507
|
setEventLog([]);
|
|
40391
|
-
setSelectedObjectId(
|
|
40392
|
-
|
|
40393
|
-
}, [
|
|
40508
|
+
setSelectedObjectId(objects[0] ? objId(objects[0]) : null);
|
|
40509
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
40510
|
+
}, [objects, playAgainEvent, emit]);
|
|
40394
40511
|
if (!resolved) return null;
|
|
40395
40512
|
const objectViewers = objects.map((obj) => {
|
|
40396
40513
|
const states = objStates(obj);
|
|
@@ -40459,12 +40576,12 @@ function EventHandlerBoard({
|
|
|
40459
40576
|
{
|
|
40460
40577
|
object: selectedObject,
|
|
40461
40578
|
onRulesChange: handleRulesChange,
|
|
40462
|
-
disabled:
|
|
40579
|
+
disabled: isPlaying
|
|
40463
40580
|
}
|
|
40464
40581
|
),
|
|
40465
40582
|
eventLog.length > 0 && /* @__PURE__ */ jsx(EventLog, { entries: eventLog }),
|
|
40466
|
-
|
|
40467
|
-
|
|
40583
|
+
isSuccess && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("eventHandler.chainComplete") }) }),
|
|
40584
|
+
!isPlaying && !isSuccess && attempts > 0 && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
40468
40585
|
/* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(encourageKey) }) }),
|
|
40469
40586
|
showHint && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
|
|
40470
40587
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
@@ -40472,12 +40589,12 @@ function EventHandlerBoard({
|
|
|
40472
40589
|
] }) })
|
|
40473
40590
|
] }),
|
|
40474
40591
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
|
|
40475
|
-
|
|
40592
|
+
!isPlaying && !isSuccess && attempts > 0 ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsx(
|
|
40476
40593
|
Button,
|
|
40477
40594
|
{
|
|
40478
40595
|
variant: "primary",
|
|
40479
40596
|
onClick: handlePlay,
|
|
40480
|
-
disabled:
|
|
40597
|
+
disabled: isPlaying || isSuccess,
|
|
40481
40598
|
children: "\u25B6 " + t("game.play")
|
|
40482
40599
|
}
|
|
40483
40600
|
),
|
|
@@ -41669,6 +41786,1568 @@ var init_GameAudioToggle = __esm({
|
|
|
41669
41786
|
GameAudioToggle.displayName = "GameAudioToggle";
|
|
41670
41787
|
}
|
|
41671
41788
|
});
|
|
41789
|
+
function detectAssetRoot(modelUrl) {
|
|
41790
|
+
const idx = modelUrl.indexOf("/3d/");
|
|
41791
|
+
if (idx !== -1) {
|
|
41792
|
+
return modelUrl.substring(0, idx + 4);
|
|
41793
|
+
}
|
|
41794
|
+
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
41795
|
+
}
|
|
41796
|
+
function createGLTFLoaderForUrl(url) {
|
|
41797
|
+
const loader = new GLTFLoader();
|
|
41798
|
+
loader.setResourcePath(detectAssetRoot(url));
|
|
41799
|
+
return loader;
|
|
41800
|
+
}
|
|
41801
|
+
var AssetLoader;
|
|
41802
|
+
var init_AssetLoader = __esm({
|
|
41803
|
+
"components/game/molecules/three/loaders/AssetLoader.ts"() {
|
|
41804
|
+
AssetLoader = class {
|
|
41805
|
+
constructor() {
|
|
41806
|
+
__publicField(this, "objLoader");
|
|
41807
|
+
__publicField(this, "textureLoader");
|
|
41808
|
+
__publicField(this, "modelCache");
|
|
41809
|
+
__publicField(this, "textureCache");
|
|
41810
|
+
__publicField(this, "loadingPromises");
|
|
41811
|
+
this.objLoader = new OBJLoader();
|
|
41812
|
+
this.textureLoader = new THREE.TextureLoader();
|
|
41813
|
+
this.modelCache = /* @__PURE__ */ new Map();
|
|
41814
|
+
this.textureCache = /* @__PURE__ */ new Map();
|
|
41815
|
+
this.loadingPromises = /* @__PURE__ */ new Map();
|
|
41816
|
+
}
|
|
41817
|
+
/**
|
|
41818
|
+
* Load a GLB/GLTF model
|
|
41819
|
+
* @param url - URL to the .glb or .gltf file
|
|
41820
|
+
* @returns Promise with loaded model scene and animations
|
|
41821
|
+
*/
|
|
41822
|
+
async loadModel(url) {
|
|
41823
|
+
if (this.modelCache.has(url)) {
|
|
41824
|
+
return this.modelCache.get(url);
|
|
41825
|
+
}
|
|
41826
|
+
if (this.loadingPromises.has(url)) {
|
|
41827
|
+
return this.loadingPromises.get(url);
|
|
41828
|
+
}
|
|
41829
|
+
const loader = createGLTFLoaderForUrl(url);
|
|
41830
|
+
const loadPromise = loader.loadAsync(url).then((gltf) => {
|
|
41831
|
+
const result = {
|
|
41832
|
+
scene: gltf.scene,
|
|
41833
|
+
animations: gltf.animations || []
|
|
41834
|
+
};
|
|
41835
|
+
this.modelCache.set(url, result);
|
|
41836
|
+
this.loadingPromises.delete(url);
|
|
41837
|
+
return result;
|
|
41838
|
+
}).catch((error) => {
|
|
41839
|
+
this.loadingPromises.delete(url);
|
|
41840
|
+
throw new Error(`Failed to load model ${url}: ${error.message}`);
|
|
41841
|
+
});
|
|
41842
|
+
this.loadingPromises.set(url, loadPromise);
|
|
41843
|
+
return loadPromise;
|
|
41844
|
+
}
|
|
41845
|
+
/**
|
|
41846
|
+
* Load an OBJ model (fallback for non-GLB assets)
|
|
41847
|
+
* @param url - URL to the .obj file
|
|
41848
|
+
* @returns Promise with loaded object group
|
|
41849
|
+
*/
|
|
41850
|
+
async loadOBJ(url) {
|
|
41851
|
+
if (this.modelCache.has(url)) {
|
|
41852
|
+
return this.modelCache.get(url).scene;
|
|
41853
|
+
}
|
|
41854
|
+
if (this.loadingPromises.has(url)) {
|
|
41855
|
+
const result = await this.loadingPromises.get(url);
|
|
41856
|
+
return result.scene;
|
|
41857
|
+
}
|
|
41858
|
+
const loadPromise = this.objLoader.loadAsync(url).then((group) => {
|
|
41859
|
+
const result = {
|
|
41860
|
+
scene: group,
|
|
41861
|
+
animations: []
|
|
41862
|
+
};
|
|
41863
|
+
this.modelCache.set(url, result);
|
|
41864
|
+
this.loadingPromises.delete(url);
|
|
41865
|
+
return result;
|
|
41866
|
+
}).catch((error) => {
|
|
41867
|
+
this.loadingPromises.delete(url);
|
|
41868
|
+
throw new Error(`Failed to load OBJ ${url}: ${error.message}`);
|
|
41869
|
+
});
|
|
41870
|
+
this.loadingPromises.set(url, loadPromise);
|
|
41871
|
+
return (await loadPromise).scene;
|
|
41872
|
+
}
|
|
41873
|
+
/**
|
|
41874
|
+
* Load a texture
|
|
41875
|
+
* @param url - URL to the texture image
|
|
41876
|
+
* @returns Promise with loaded texture
|
|
41877
|
+
*/
|
|
41878
|
+
async loadTexture(url) {
|
|
41879
|
+
if (this.textureCache.has(url)) {
|
|
41880
|
+
return this.textureCache.get(url);
|
|
41881
|
+
}
|
|
41882
|
+
if (this.loadingPromises.has(`texture:${url}`)) {
|
|
41883
|
+
return this.loadingPromises.get(`texture:${url}`);
|
|
41884
|
+
}
|
|
41885
|
+
const loadPromise = this.textureLoader.loadAsync(url).then((texture) => {
|
|
41886
|
+
texture.colorSpace = THREE.SRGBColorSpace;
|
|
41887
|
+
this.textureCache.set(url, texture);
|
|
41888
|
+
this.loadingPromises.delete(`texture:${url}`);
|
|
41889
|
+
return texture;
|
|
41890
|
+
}).catch((error) => {
|
|
41891
|
+
this.loadingPromises.delete(`texture:${url}`);
|
|
41892
|
+
throw new Error(`Failed to load texture ${url}: ${error.message}`);
|
|
41893
|
+
});
|
|
41894
|
+
this.loadingPromises.set(`texture:${url}`, loadPromise);
|
|
41895
|
+
return loadPromise;
|
|
41896
|
+
}
|
|
41897
|
+
/**
|
|
41898
|
+
* Preload multiple assets
|
|
41899
|
+
* @param urls - Array of asset URLs to preload
|
|
41900
|
+
* @returns Promise that resolves when all assets are loaded
|
|
41901
|
+
*/
|
|
41902
|
+
async preload(urls) {
|
|
41903
|
+
const promises = urls.map((url) => {
|
|
41904
|
+
if (url.endsWith(".glb") || url.endsWith(".gltf")) {
|
|
41905
|
+
return this.loadModel(url).catch(() => null);
|
|
41906
|
+
} else if (url.endsWith(".obj")) {
|
|
41907
|
+
return this.loadOBJ(url).catch(() => null);
|
|
41908
|
+
} else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
|
|
41909
|
+
return this.loadTexture(url).catch(() => null);
|
|
41910
|
+
}
|
|
41911
|
+
return Promise.resolve(null);
|
|
41912
|
+
});
|
|
41913
|
+
await Promise.all(promises);
|
|
41914
|
+
}
|
|
41915
|
+
/**
|
|
41916
|
+
* Check if a model is cached
|
|
41917
|
+
* @param url - Model URL
|
|
41918
|
+
*/
|
|
41919
|
+
hasModel(url) {
|
|
41920
|
+
return this.modelCache.has(url);
|
|
41921
|
+
}
|
|
41922
|
+
/**
|
|
41923
|
+
* Check if a texture is cached
|
|
41924
|
+
* @param url - Texture URL
|
|
41925
|
+
*/
|
|
41926
|
+
hasTexture(url) {
|
|
41927
|
+
return this.textureCache.has(url);
|
|
41928
|
+
}
|
|
41929
|
+
/**
|
|
41930
|
+
* Get cached model (throws if not cached)
|
|
41931
|
+
* @param url - Model URL
|
|
41932
|
+
*/
|
|
41933
|
+
getModel(url) {
|
|
41934
|
+
const model = this.modelCache.get(url);
|
|
41935
|
+
if (!model) {
|
|
41936
|
+
throw new Error(`Model ${url} not in cache`);
|
|
41937
|
+
}
|
|
41938
|
+
return model;
|
|
41939
|
+
}
|
|
41940
|
+
/**
|
|
41941
|
+
* Get cached texture (throws if not cached)
|
|
41942
|
+
* @param url - Texture URL
|
|
41943
|
+
*/
|
|
41944
|
+
getTexture(url) {
|
|
41945
|
+
const texture = this.textureCache.get(url);
|
|
41946
|
+
if (!texture) {
|
|
41947
|
+
throw new Error(`Texture ${url} not in cache`);
|
|
41948
|
+
}
|
|
41949
|
+
return texture;
|
|
41950
|
+
}
|
|
41951
|
+
/**
|
|
41952
|
+
* Clear all caches
|
|
41953
|
+
*/
|
|
41954
|
+
clearCache() {
|
|
41955
|
+
this.textureCache.forEach((texture) => {
|
|
41956
|
+
texture.dispose();
|
|
41957
|
+
});
|
|
41958
|
+
this.modelCache.forEach((model) => {
|
|
41959
|
+
model.scene.traverse((child) => {
|
|
41960
|
+
if (child instanceof THREE.Mesh) {
|
|
41961
|
+
child.geometry.dispose();
|
|
41962
|
+
if (Array.isArray(child.material)) {
|
|
41963
|
+
child.material.forEach((m) => m.dispose());
|
|
41964
|
+
} else {
|
|
41965
|
+
child.material.dispose();
|
|
41966
|
+
}
|
|
41967
|
+
}
|
|
41968
|
+
});
|
|
41969
|
+
});
|
|
41970
|
+
this.modelCache.clear();
|
|
41971
|
+
this.textureCache.clear();
|
|
41972
|
+
this.loadingPromises.clear();
|
|
41973
|
+
}
|
|
41974
|
+
/**
|
|
41975
|
+
* Get cache statistics
|
|
41976
|
+
*/
|
|
41977
|
+
getStats() {
|
|
41978
|
+
return {
|
|
41979
|
+
models: this.modelCache.size,
|
|
41980
|
+
textures: this.textureCache.size,
|
|
41981
|
+
loading: this.loadingPromises.size
|
|
41982
|
+
};
|
|
41983
|
+
}
|
|
41984
|
+
};
|
|
41985
|
+
new AssetLoader();
|
|
41986
|
+
}
|
|
41987
|
+
});
|
|
41988
|
+
function useAssetLoader(options = {}) {
|
|
41989
|
+
const { preloadUrls = [], loader: customLoader } = options;
|
|
41990
|
+
const loaderRef = useRef(customLoader || new AssetLoader());
|
|
41991
|
+
const [state, setState] = useState({
|
|
41992
|
+
isLoading: false,
|
|
41993
|
+
progress: 0,
|
|
41994
|
+
loaded: 0,
|
|
41995
|
+
total: 0,
|
|
41996
|
+
errors: []
|
|
41997
|
+
});
|
|
41998
|
+
useEffect(() => {
|
|
41999
|
+
if (preloadUrls.length > 0) {
|
|
42000
|
+
preload(preloadUrls);
|
|
42001
|
+
}
|
|
42002
|
+
}, []);
|
|
42003
|
+
const updateProgress = useCallback((loaded, total) => {
|
|
42004
|
+
setState((prev) => ({
|
|
42005
|
+
...prev,
|
|
42006
|
+
loaded,
|
|
42007
|
+
total,
|
|
42008
|
+
progress: total > 0 ? Math.round(loaded / total * 100) : 0
|
|
42009
|
+
}));
|
|
42010
|
+
}, []);
|
|
42011
|
+
const loadModel = useCallback(
|
|
42012
|
+
async (url) => {
|
|
42013
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
42014
|
+
try {
|
|
42015
|
+
const model = await loaderRef.current.loadModel(url);
|
|
42016
|
+
setState((prev) => ({
|
|
42017
|
+
...prev,
|
|
42018
|
+
isLoading: false,
|
|
42019
|
+
loaded: prev.loaded + 1
|
|
42020
|
+
}));
|
|
42021
|
+
return model;
|
|
42022
|
+
} catch (error) {
|
|
42023
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
42024
|
+
setState((prev) => ({
|
|
42025
|
+
...prev,
|
|
42026
|
+
isLoading: false,
|
|
42027
|
+
errors: [...prev.errors, errorMsg]
|
|
42028
|
+
}));
|
|
42029
|
+
throw error;
|
|
42030
|
+
}
|
|
42031
|
+
},
|
|
42032
|
+
[]
|
|
42033
|
+
);
|
|
42034
|
+
const loadOBJ = useCallback(
|
|
42035
|
+
async (url) => {
|
|
42036
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
42037
|
+
try {
|
|
42038
|
+
const model = await loaderRef.current.loadOBJ(url);
|
|
42039
|
+
setState((prev) => ({
|
|
42040
|
+
...prev,
|
|
42041
|
+
isLoading: false,
|
|
42042
|
+
loaded: prev.loaded + 1
|
|
42043
|
+
}));
|
|
42044
|
+
return model;
|
|
42045
|
+
} catch (error) {
|
|
42046
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
42047
|
+
setState((prev) => ({
|
|
42048
|
+
...prev,
|
|
42049
|
+
isLoading: false,
|
|
42050
|
+
errors: [...prev.errors, errorMsg]
|
|
42051
|
+
}));
|
|
42052
|
+
throw error;
|
|
42053
|
+
}
|
|
42054
|
+
},
|
|
42055
|
+
[]
|
|
42056
|
+
);
|
|
42057
|
+
const loadTexture = useCallback(
|
|
42058
|
+
async (url) => {
|
|
42059
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
42060
|
+
try {
|
|
42061
|
+
const texture = await loaderRef.current.loadTexture(url);
|
|
42062
|
+
setState((prev) => ({
|
|
42063
|
+
...prev,
|
|
42064
|
+
isLoading: false,
|
|
42065
|
+
loaded: prev.loaded + 1
|
|
42066
|
+
}));
|
|
42067
|
+
return texture;
|
|
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 preload = useCallback(
|
|
42081
|
+
async (urls) => {
|
|
42082
|
+
setState((prev) => ({
|
|
42083
|
+
...prev,
|
|
42084
|
+
isLoading: true,
|
|
42085
|
+
total: urls.length,
|
|
42086
|
+
loaded: 0,
|
|
42087
|
+
errors: []
|
|
42088
|
+
}));
|
|
42089
|
+
let completed = 0;
|
|
42090
|
+
const errors = [];
|
|
42091
|
+
await Promise.all(
|
|
42092
|
+
urls.map(async (url) => {
|
|
42093
|
+
try {
|
|
42094
|
+
if (url.endsWith(".glb") || url.endsWith(".gltf")) {
|
|
42095
|
+
await loaderRef.current.loadModel(url);
|
|
42096
|
+
} else if (url.endsWith(".obj")) {
|
|
42097
|
+
await loaderRef.current.loadOBJ(url);
|
|
42098
|
+
} else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
|
|
42099
|
+
await loaderRef.current.loadTexture(url);
|
|
42100
|
+
}
|
|
42101
|
+
completed++;
|
|
42102
|
+
updateProgress(completed, urls.length);
|
|
42103
|
+
} catch (error) {
|
|
42104
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
42105
|
+
errors.push(`${url}: ${errorMsg}`);
|
|
42106
|
+
completed++;
|
|
42107
|
+
updateProgress(completed, urls.length);
|
|
42108
|
+
}
|
|
42109
|
+
})
|
|
42110
|
+
);
|
|
42111
|
+
setState((prev) => ({
|
|
42112
|
+
...prev,
|
|
42113
|
+
isLoading: false,
|
|
42114
|
+
errors
|
|
42115
|
+
}));
|
|
42116
|
+
},
|
|
42117
|
+
[updateProgress]
|
|
42118
|
+
);
|
|
42119
|
+
const hasModel = useCallback((url) => {
|
|
42120
|
+
return loaderRef.current.hasModel(url);
|
|
42121
|
+
}, []);
|
|
42122
|
+
const hasTexture = useCallback((url) => {
|
|
42123
|
+
return loaderRef.current.hasTexture(url);
|
|
42124
|
+
}, []);
|
|
42125
|
+
const getModel = useCallback((url) => {
|
|
42126
|
+
try {
|
|
42127
|
+
return loaderRef.current.getModel(url);
|
|
42128
|
+
} catch {
|
|
42129
|
+
return void 0;
|
|
42130
|
+
}
|
|
42131
|
+
}, []);
|
|
42132
|
+
const getTexture = useCallback((url) => {
|
|
42133
|
+
try {
|
|
42134
|
+
return loaderRef.current.getTexture(url);
|
|
42135
|
+
} catch {
|
|
42136
|
+
return void 0;
|
|
42137
|
+
}
|
|
42138
|
+
}, []);
|
|
42139
|
+
const clearCache = useCallback(() => {
|
|
42140
|
+
loaderRef.current.clearCache();
|
|
42141
|
+
setState({
|
|
42142
|
+
isLoading: false,
|
|
42143
|
+
progress: 0,
|
|
42144
|
+
loaded: 0,
|
|
42145
|
+
total: 0,
|
|
42146
|
+
errors: []
|
|
42147
|
+
});
|
|
42148
|
+
}, []);
|
|
42149
|
+
return {
|
|
42150
|
+
...state,
|
|
42151
|
+
loadModel,
|
|
42152
|
+
loadOBJ,
|
|
42153
|
+
loadTexture,
|
|
42154
|
+
preload,
|
|
42155
|
+
hasModel,
|
|
42156
|
+
hasTexture,
|
|
42157
|
+
getModel,
|
|
42158
|
+
getTexture,
|
|
42159
|
+
clearCache
|
|
42160
|
+
};
|
|
42161
|
+
}
|
|
42162
|
+
var init_useAssetLoader = __esm({
|
|
42163
|
+
"components/game/molecules/three/hooks/useAssetLoader.ts"() {
|
|
42164
|
+
"use client";
|
|
42165
|
+
init_AssetLoader();
|
|
42166
|
+
}
|
|
42167
|
+
});
|
|
42168
|
+
function useGameCanvas3DEvents(options) {
|
|
42169
|
+
const {
|
|
42170
|
+
tileClickEvent,
|
|
42171
|
+
unitClickEvent,
|
|
42172
|
+
featureClickEvent,
|
|
42173
|
+
canvasClickEvent,
|
|
42174
|
+
tileHoverEvent,
|
|
42175
|
+
tileLeaveEvent,
|
|
42176
|
+
unitAnimationEvent,
|
|
42177
|
+
cameraChangeEvent,
|
|
42178
|
+
onTileClick,
|
|
42179
|
+
onUnitClick,
|
|
42180
|
+
onFeatureClick,
|
|
42181
|
+
onCanvasClick,
|
|
42182
|
+
onTileHover,
|
|
42183
|
+
onUnitAnimation
|
|
42184
|
+
} = options;
|
|
42185
|
+
const emit = useEmitEvent();
|
|
42186
|
+
const optionsRef = useRef(options);
|
|
42187
|
+
optionsRef.current = options;
|
|
42188
|
+
const handleTileClick = useCallback(
|
|
42189
|
+
(tile, event) => {
|
|
42190
|
+
if (tileClickEvent) {
|
|
42191
|
+
emit(tileClickEvent, {
|
|
42192
|
+
tileId: tile.id,
|
|
42193
|
+
x: tile.x,
|
|
42194
|
+
z: tile.z ?? tile.y ?? 0,
|
|
42195
|
+
type: tile.type,
|
|
42196
|
+
terrain: tile.terrain,
|
|
42197
|
+
elevation: tile.elevation
|
|
42198
|
+
});
|
|
42199
|
+
}
|
|
42200
|
+
optionsRef.current.onTileClick?.(tile, event);
|
|
42201
|
+
},
|
|
42202
|
+
[tileClickEvent, emit]
|
|
42203
|
+
);
|
|
42204
|
+
const handleUnitClick = useCallback(
|
|
42205
|
+
(unit, event) => {
|
|
42206
|
+
if (unitClickEvent) {
|
|
42207
|
+
emit(unitClickEvent, {
|
|
42208
|
+
unitId: unit.id,
|
|
42209
|
+
x: unit.x,
|
|
42210
|
+
z: unit.z ?? unit.y ?? 0,
|
|
42211
|
+
unitType: unit.unitType,
|
|
42212
|
+
name: unit.name,
|
|
42213
|
+
team: unit.team,
|
|
42214
|
+
faction: unit.faction,
|
|
42215
|
+
health: unit.health,
|
|
42216
|
+
maxHealth: unit.maxHealth
|
|
42217
|
+
});
|
|
42218
|
+
}
|
|
42219
|
+
optionsRef.current.onUnitClick?.(unit, event);
|
|
42220
|
+
},
|
|
42221
|
+
[unitClickEvent, emit]
|
|
42222
|
+
);
|
|
42223
|
+
const handleFeatureClick = useCallback(
|
|
42224
|
+
(feature, event) => {
|
|
42225
|
+
if (featureClickEvent) {
|
|
42226
|
+
emit(featureClickEvent, {
|
|
42227
|
+
featureId: feature.id,
|
|
42228
|
+
x: feature.x,
|
|
42229
|
+
z: feature.z ?? feature.y ?? 0,
|
|
42230
|
+
type: feature.type,
|
|
42231
|
+
elevation: feature.elevation
|
|
42232
|
+
});
|
|
42233
|
+
}
|
|
42234
|
+
optionsRef.current.onFeatureClick?.(feature, event);
|
|
42235
|
+
},
|
|
42236
|
+
[featureClickEvent, emit]
|
|
42237
|
+
);
|
|
42238
|
+
const handleCanvasClick = useCallback(
|
|
42239
|
+
(event) => {
|
|
42240
|
+
if (canvasClickEvent) {
|
|
42241
|
+
emit(canvasClickEvent, {
|
|
42242
|
+
clientX: event.clientX,
|
|
42243
|
+
clientY: event.clientY,
|
|
42244
|
+
button: event.button
|
|
42245
|
+
});
|
|
42246
|
+
}
|
|
42247
|
+
optionsRef.current.onCanvasClick?.(event);
|
|
42248
|
+
},
|
|
42249
|
+
[canvasClickEvent, emit]
|
|
42250
|
+
);
|
|
42251
|
+
const handleTileHover = useCallback(
|
|
42252
|
+
(tile, event) => {
|
|
42253
|
+
if (tile) {
|
|
42254
|
+
if (tileHoverEvent) {
|
|
42255
|
+
emit(tileHoverEvent, {
|
|
42256
|
+
tileId: tile.id,
|
|
42257
|
+
x: tile.x,
|
|
42258
|
+
z: tile.z ?? tile.y ?? 0,
|
|
42259
|
+
type: tile.type
|
|
42260
|
+
});
|
|
42261
|
+
}
|
|
42262
|
+
} else {
|
|
42263
|
+
if (tileLeaveEvent) {
|
|
42264
|
+
emit(tileLeaveEvent, {});
|
|
42265
|
+
}
|
|
42266
|
+
}
|
|
42267
|
+
optionsRef.current.onTileHover?.(tile, event);
|
|
42268
|
+
},
|
|
42269
|
+
[tileHoverEvent, tileLeaveEvent, emit]
|
|
42270
|
+
);
|
|
42271
|
+
const handleUnitAnimation = useCallback(
|
|
42272
|
+
(unitId, state) => {
|
|
42273
|
+
if (unitAnimationEvent) {
|
|
42274
|
+
emit(unitAnimationEvent, {
|
|
42275
|
+
unitId,
|
|
42276
|
+
state,
|
|
42277
|
+
timestamp: Date.now()
|
|
42278
|
+
});
|
|
42279
|
+
}
|
|
42280
|
+
optionsRef.current.onUnitAnimation?.(unitId, state);
|
|
42281
|
+
},
|
|
42282
|
+
[unitAnimationEvent, emit]
|
|
42283
|
+
);
|
|
42284
|
+
const handleCameraChange = useCallback(
|
|
42285
|
+
(position) => {
|
|
42286
|
+
if (cameraChangeEvent) {
|
|
42287
|
+
emit(cameraChangeEvent, {
|
|
42288
|
+
position,
|
|
42289
|
+
timestamp: Date.now()
|
|
42290
|
+
});
|
|
42291
|
+
}
|
|
42292
|
+
},
|
|
42293
|
+
[cameraChangeEvent, emit]
|
|
42294
|
+
);
|
|
42295
|
+
return {
|
|
42296
|
+
handleTileClick,
|
|
42297
|
+
handleUnitClick,
|
|
42298
|
+
handleFeatureClick,
|
|
42299
|
+
handleCanvasClick,
|
|
42300
|
+
handleTileHover,
|
|
42301
|
+
handleUnitAnimation,
|
|
42302
|
+
handleCameraChange
|
|
42303
|
+
};
|
|
42304
|
+
}
|
|
42305
|
+
var init_useGameCanvas3DEvents = __esm({
|
|
42306
|
+
"components/game/molecules/three/hooks/useGameCanvas3DEvents.ts"() {
|
|
42307
|
+
"use client";
|
|
42308
|
+
init_useEventBus();
|
|
42309
|
+
}
|
|
42310
|
+
});
|
|
42311
|
+
|
|
42312
|
+
// components/game/molecules/three/components/Canvas3DLoadingState.css
|
|
42313
|
+
var init_Canvas3DLoadingState = __esm({
|
|
42314
|
+
"components/game/molecules/three/components/Canvas3DLoadingState.css"() {
|
|
42315
|
+
}
|
|
42316
|
+
});
|
|
42317
|
+
function Canvas3DLoadingState({
|
|
42318
|
+
progress = 0,
|
|
42319
|
+
loaded = 0,
|
|
42320
|
+
total = 0,
|
|
42321
|
+
message = "Loading 3D Scene...",
|
|
42322
|
+
details,
|
|
42323
|
+
showSpinner = true,
|
|
42324
|
+
className
|
|
42325
|
+
}) {
|
|
42326
|
+
const clampedProgress = Math.max(0, Math.min(100, progress));
|
|
42327
|
+
const hasProgress = total > 0;
|
|
42328
|
+
return /* @__PURE__ */ jsxs("div", { className: `canvas-3d-loading ${className || ""}`, children: [
|
|
42329
|
+
/* @__PURE__ */ jsxs("div", { className: "canvas-3d-loading__content", children: [
|
|
42330
|
+
showSpinner && /* @__PURE__ */ jsxs("div", { className: "canvas-3d-loading__spinner", children: [
|
|
42331
|
+
/* @__PURE__ */ jsx("div", { className: "spinner__ring" }),
|
|
42332
|
+
/* @__PURE__ */ jsx("div", { className: "spinner__ring spinner__ring--secondary" })
|
|
42333
|
+
] }),
|
|
42334
|
+
/* @__PURE__ */ jsx("div", { className: "canvas-3d-loading__message", children: message }),
|
|
42335
|
+
details && /* @__PURE__ */ jsx("div", { className: "canvas-3d-loading__details", children: details }),
|
|
42336
|
+
hasProgress && /* @__PURE__ */ jsxs("div", { className: "canvas-3d-loading__progress", children: [
|
|
42337
|
+
/* @__PURE__ */ jsx("div", { className: "progress__bar", children: /* @__PURE__ */ jsx(
|
|
42338
|
+
"div",
|
|
42339
|
+
{
|
|
42340
|
+
className: "progress__fill",
|
|
42341
|
+
style: { width: `${clampedProgress}%` }
|
|
42342
|
+
}
|
|
42343
|
+
) }),
|
|
42344
|
+
/* @__PURE__ */ jsxs("div", { className: "progress__text", children: [
|
|
42345
|
+
/* @__PURE__ */ jsxs("span", { className: "progress__percentage", children: [
|
|
42346
|
+
clampedProgress,
|
|
42347
|
+
"%"
|
|
42348
|
+
] }),
|
|
42349
|
+
/* @__PURE__ */ jsxs("span", { className: "progress__count", children: [
|
|
42350
|
+
"(",
|
|
42351
|
+
loaded,
|
|
42352
|
+
"/",
|
|
42353
|
+
total,
|
|
42354
|
+
")"
|
|
42355
|
+
] })
|
|
42356
|
+
] })
|
|
42357
|
+
] })
|
|
42358
|
+
] }),
|
|
42359
|
+
/* @__PURE__ */ jsx("div", { className: "canvas-3d-loading__background", children: /* @__PURE__ */ jsx("div", { className: "bg__grid" }) })
|
|
42360
|
+
] });
|
|
42361
|
+
}
|
|
42362
|
+
var init_Canvas3DLoadingState2 = __esm({
|
|
42363
|
+
"components/game/molecules/three/components/Canvas3DLoadingState.tsx"() {
|
|
42364
|
+
init_Canvas3DLoadingState();
|
|
42365
|
+
}
|
|
42366
|
+
});
|
|
42367
|
+
|
|
42368
|
+
// components/game/molecules/three/components/Canvas3DErrorBoundary.css
|
|
42369
|
+
var init_Canvas3DErrorBoundary = __esm({
|
|
42370
|
+
"components/game/molecules/three/components/Canvas3DErrorBoundary.css"() {
|
|
42371
|
+
}
|
|
42372
|
+
});
|
|
42373
|
+
var log9, Canvas3DErrorBoundary;
|
|
42374
|
+
var init_Canvas3DErrorBoundary2 = __esm({
|
|
42375
|
+
"components/game/molecules/three/components/Canvas3DErrorBoundary.tsx"() {
|
|
42376
|
+
init_Canvas3DErrorBoundary();
|
|
42377
|
+
log9 = createLogger("almadar:ui:game:canvas3d:error-boundary");
|
|
42378
|
+
Canvas3DErrorBoundary = class extends Component {
|
|
42379
|
+
constructor(props) {
|
|
42380
|
+
super(props);
|
|
42381
|
+
__publicField(this, "handleReset", () => {
|
|
42382
|
+
this.setState({
|
|
42383
|
+
hasError: false,
|
|
42384
|
+
error: null,
|
|
42385
|
+
errorInfo: null
|
|
42386
|
+
});
|
|
42387
|
+
this.props.onReset?.();
|
|
42388
|
+
});
|
|
42389
|
+
this.state = {
|
|
42390
|
+
hasError: false,
|
|
42391
|
+
error: null,
|
|
42392
|
+
errorInfo: null
|
|
42393
|
+
};
|
|
42394
|
+
}
|
|
42395
|
+
static getDerivedStateFromError(error) {
|
|
42396
|
+
return {
|
|
42397
|
+
hasError: true,
|
|
42398
|
+
error,
|
|
42399
|
+
errorInfo: null
|
|
42400
|
+
};
|
|
42401
|
+
}
|
|
42402
|
+
componentDidCatch(error, errorInfo) {
|
|
42403
|
+
this.setState({ errorInfo });
|
|
42404
|
+
this.props.onError?.(error, errorInfo);
|
|
42405
|
+
log9.error("Error caught", { error });
|
|
42406
|
+
log9.error("Component stack", { componentStack: errorInfo.componentStack ?? "<none>" });
|
|
42407
|
+
}
|
|
42408
|
+
render() {
|
|
42409
|
+
if (this.state.hasError) {
|
|
42410
|
+
if (this.props.fallback) {
|
|
42411
|
+
return this.props.fallback;
|
|
42412
|
+
}
|
|
42413
|
+
return /* @__PURE__ */ jsx("div", { className: "canvas-3d-error", children: /* @__PURE__ */ jsxs("div", { className: "canvas-3d-error__content", children: [
|
|
42414
|
+
/* @__PURE__ */ jsx("div", { className: "canvas-3d-error__icon", children: "\u26A0\uFE0F" }),
|
|
42415
|
+
/* @__PURE__ */ jsx("h2", { className: "canvas-3d-error__title", children: "3D Scene Error" }),
|
|
42416
|
+
/* @__PURE__ */ jsx("p", { className: "canvas-3d-error__message", children: "Something went wrong while rendering the 3D scene." }),
|
|
42417
|
+
this.state.error && /* @__PURE__ */ jsxs("details", { className: "canvas-3d-error__details", children: [
|
|
42418
|
+
/* @__PURE__ */ jsx("summary", { children: "Error Details" }),
|
|
42419
|
+
/* @__PURE__ */ jsxs("pre", { className: "error__stack", children: [
|
|
42420
|
+
this.state.error.message,
|
|
42421
|
+
"\n",
|
|
42422
|
+
this.state.error.stack
|
|
42423
|
+
] }),
|
|
42424
|
+
this.state.errorInfo && /* @__PURE__ */ jsx("pre", { className: "error__component-stack", children: this.state.errorInfo.componentStack })
|
|
42425
|
+
] }),
|
|
42426
|
+
/* @__PURE__ */ jsxs("div", { className: "canvas-3d-error__actions", children: [
|
|
42427
|
+
/* @__PURE__ */ jsx(
|
|
42428
|
+
"button",
|
|
42429
|
+
{
|
|
42430
|
+
className: "error__button error__button--primary",
|
|
42431
|
+
onClick: this.handleReset,
|
|
42432
|
+
children: "Try Again"
|
|
42433
|
+
}
|
|
42434
|
+
),
|
|
42435
|
+
/* @__PURE__ */ jsx(
|
|
42436
|
+
"button",
|
|
42437
|
+
{
|
|
42438
|
+
className: "error__button error__button--secondary",
|
|
42439
|
+
onClick: () => window.location.reload(),
|
|
42440
|
+
children: "Reload Page"
|
|
42441
|
+
}
|
|
42442
|
+
)
|
|
42443
|
+
] })
|
|
42444
|
+
] }) });
|
|
42445
|
+
}
|
|
42446
|
+
return this.props.children;
|
|
42447
|
+
}
|
|
42448
|
+
};
|
|
42449
|
+
}
|
|
42450
|
+
});
|
|
42451
|
+
function detectAssetRoot2(modelUrl) {
|
|
42452
|
+
const idx = modelUrl.indexOf("/3d/");
|
|
42453
|
+
if (idx !== -1) {
|
|
42454
|
+
return modelUrl.substring(0, idx + 4);
|
|
42455
|
+
}
|
|
42456
|
+
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
42457
|
+
}
|
|
42458
|
+
function useGLTFModel(url, resourceBasePath) {
|
|
42459
|
+
const [state, setState] = useState({
|
|
42460
|
+
model: null,
|
|
42461
|
+
isLoading: false,
|
|
42462
|
+
error: null
|
|
42463
|
+
});
|
|
42464
|
+
useEffect(() => {
|
|
42465
|
+
if (!url) {
|
|
42466
|
+
setState({ model: null, isLoading: false, error: null });
|
|
42467
|
+
return;
|
|
42468
|
+
}
|
|
42469
|
+
log10.debug("Loading", { url });
|
|
42470
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
42471
|
+
const assetRoot = resourceBasePath || detectAssetRoot2(url);
|
|
42472
|
+
const loader = new GLTFLoader$1();
|
|
42473
|
+
loader.setResourcePath(assetRoot);
|
|
42474
|
+
loader.load(
|
|
42475
|
+
url,
|
|
42476
|
+
(gltf) => {
|
|
42477
|
+
log10.debug("Loaded", { url });
|
|
42478
|
+
setState({
|
|
42479
|
+
model: gltf.scene,
|
|
42480
|
+
isLoading: false,
|
|
42481
|
+
error: null
|
|
42482
|
+
});
|
|
42483
|
+
},
|
|
42484
|
+
void 0,
|
|
42485
|
+
(err) => {
|
|
42486
|
+
log10.warn("Failed", { url, error: err instanceof Error ? err : String(err) });
|
|
42487
|
+
setState({
|
|
42488
|
+
model: null,
|
|
42489
|
+
isLoading: false,
|
|
42490
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
42491
|
+
});
|
|
42492
|
+
}
|
|
42493
|
+
);
|
|
42494
|
+
}, [url, resourceBasePath]);
|
|
42495
|
+
return state;
|
|
42496
|
+
}
|
|
42497
|
+
function ModelLoader({
|
|
42498
|
+
url,
|
|
42499
|
+
position = [0, 0, 0],
|
|
42500
|
+
scale = 1,
|
|
42501
|
+
rotation = [0, 0, 0],
|
|
42502
|
+
isSelected = false,
|
|
42503
|
+
isHovered = false,
|
|
42504
|
+
onClick,
|
|
42505
|
+
onHover,
|
|
42506
|
+
fallbackGeometry = "box",
|
|
42507
|
+
castShadow = true,
|
|
42508
|
+
receiveShadow = true,
|
|
42509
|
+
resourceBasePath
|
|
42510
|
+
}) {
|
|
42511
|
+
const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
|
|
42512
|
+
const model = useMemo(() => {
|
|
42513
|
+
if (!loadedModel) return null;
|
|
42514
|
+
const cloned = loadedModel.clone();
|
|
42515
|
+
cloned.traverse((child) => {
|
|
42516
|
+
if (child instanceof THREE.Mesh) {
|
|
42517
|
+
child.castShadow = castShadow;
|
|
42518
|
+
child.receiveShadow = receiveShadow;
|
|
42519
|
+
}
|
|
42520
|
+
});
|
|
42521
|
+
return cloned;
|
|
42522
|
+
}, [loadedModel, castShadow, receiveShadow]);
|
|
42523
|
+
const scaleArray = useMemo(() => {
|
|
42524
|
+
if (typeof scale === "number") {
|
|
42525
|
+
return [scale, scale, scale];
|
|
42526
|
+
}
|
|
42527
|
+
return scale;
|
|
42528
|
+
}, [scale]);
|
|
42529
|
+
const rotationRad = useMemo(() => {
|
|
42530
|
+
return [
|
|
42531
|
+
rotation[0] * Math.PI / 180,
|
|
42532
|
+
rotation[1] * Math.PI / 180,
|
|
42533
|
+
rotation[2] * Math.PI / 180
|
|
42534
|
+
];
|
|
42535
|
+
}, [rotation]);
|
|
42536
|
+
if (isLoading) {
|
|
42537
|
+
return /* @__PURE__ */ jsx("group", { position, children: /* @__PURE__ */ jsxs("mesh", { rotation: [Math.PI / 2, 0, 0], children: [
|
|
42538
|
+
/* @__PURE__ */ jsx("ringGeometry", { args: [0.3, 0.35, 16] }),
|
|
42539
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { color: "#4a90d9", transparent: true, opacity: 0.8 })
|
|
42540
|
+
] }) });
|
|
42541
|
+
}
|
|
42542
|
+
if (error || !model) {
|
|
42543
|
+
if (fallbackGeometry === "none") {
|
|
42544
|
+
return /* @__PURE__ */ jsx("group", { position });
|
|
42545
|
+
}
|
|
42546
|
+
const fallbackProps = {
|
|
42547
|
+
onClick,
|
|
42548
|
+
onPointerOver: () => onHover?.(true),
|
|
42549
|
+
onPointerOut: () => onHover?.(false)
|
|
42550
|
+
};
|
|
42551
|
+
return /* @__PURE__ */ jsxs("group", { position, children: [
|
|
42552
|
+
(isSelected || isHovered) && /* @__PURE__ */ jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
42553
|
+
/* @__PURE__ */ jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
|
|
42554
|
+
/* @__PURE__ */ jsx(
|
|
42555
|
+
"meshBasicMaterial",
|
|
42556
|
+
{
|
|
42557
|
+
color: isSelected ? 16755200 : 16777215,
|
|
42558
|
+
transparent: true,
|
|
42559
|
+
opacity: 0.5
|
|
42560
|
+
}
|
|
42561
|
+
)
|
|
42562
|
+
] }),
|
|
42563
|
+
fallbackGeometry === "box" && /* @__PURE__ */ jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
42564
|
+
/* @__PURE__ */ jsx("boxGeometry", { args: [0.8, 0.8, 0.8] }),
|
|
42565
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
42566
|
+
] }),
|
|
42567
|
+
fallbackGeometry === "sphere" && /* @__PURE__ */ jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
42568
|
+
/* @__PURE__ */ jsx("sphereGeometry", { args: [0.4, 16, 16] }),
|
|
42569
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
42570
|
+
] }),
|
|
42571
|
+
fallbackGeometry === "cylinder" && /* @__PURE__ */ jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
42572
|
+
/* @__PURE__ */ jsx("cylinderGeometry", { args: [0.3, 0.3, 0.8, 16] }),
|
|
42573
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
42574
|
+
] })
|
|
42575
|
+
] });
|
|
42576
|
+
}
|
|
42577
|
+
return /* @__PURE__ */ jsxs(
|
|
42578
|
+
"group",
|
|
42579
|
+
{
|
|
42580
|
+
position,
|
|
42581
|
+
rotation: rotationRad,
|
|
42582
|
+
onClick,
|
|
42583
|
+
onPointerOver: () => onHover?.(true),
|
|
42584
|
+
onPointerOut: () => onHover?.(false),
|
|
42585
|
+
children: [
|
|
42586
|
+
(isSelected || isHovered) && /* @__PURE__ */ jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
42587
|
+
/* @__PURE__ */ jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
|
|
42588
|
+
/* @__PURE__ */ jsx(
|
|
42589
|
+
"meshBasicMaterial",
|
|
42590
|
+
{
|
|
42591
|
+
color: isSelected ? 16755200 : 16777215,
|
|
42592
|
+
transparent: true,
|
|
42593
|
+
opacity: 0.5
|
|
42594
|
+
}
|
|
42595
|
+
)
|
|
42596
|
+
] }),
|
|
42597
|
+
/* @__PURE__ */ jsx("primitive", { object: model, scale: scaleArray })
|
|
42598
|
+
]
|
|
42599
|
+
}
|
|
42600
|
+
);
|
|
42601
|
+
}
|
|
42602
|
+
var log10;
|
|
42603
|
+
var init_ModelLoader = __esm({
|
|
42604
|
+
"components/game/molecules/three/components/ModelLoader.tsx"() {
|
|
42605
|
+
"use client";
|
|
42606
|
+
log10 = createLogger("almadar:ui:game:model-loader");
|
|
42607
|
+
}
|
|
42608
|
+
});
|
|
42609
|
+
|
|
42610
|
+
// components/game/molecules/GameCanvas3D.css
|
|
42611
|
+
var init_GameCanvas3D = __esm({
|
|
42612
|
+
"components/game/molecules/GameCanvas3D.css"() {
|
|
42613
|
+
}
|
|
42614
|
+
});
|
|
42615
|
+
function CameraController({
|
|
42616
|
+
onCameraChange
|
|
42617
|
+
}) {
|
|
42618
|
+
const { camera } = useThree();
|
|
42619
|
+
useEffect(() => {
|
|
42620
|
+
if (onCameraChange) {
|
|
42621
|
+
onCameraChange({
|
|
42622
|
+
x: camera.position.x,
|
|
42623
|
+
y: camera.position.y,
|
|
42624
|
+
z: camera.position.z
|
|
42625
|
+
});
|
|
42626
|
+
}
|
|
42627
|
+
}, [camera.position, onCameraChange]);
|
|
42628
|
+
return null;
|
|
42629
|
+
}
|
|
42630
|
+
var DEFAULT_GRID_CONFIG, GameCanvas3D;
|
|
42631
|
+
var init_GameCanvas3D2 = __esm({
|
|
42632
|
+
"components/game/molecules/GameCanvas3D.tsx"() {
|
|
42633
|
+
"use client";
|
|
42634
|
+
init_AssetLoader();
|
|
42635
|
+
init_useAssetLoader();
|
|
42636
|
+
init_useGameCanvas3DEvents();
|
|
42637
|
+
init_Canvas3DLoadingState2();
|
|
42638
|
+
init_Canvas3DErrorBoundary2();
|
|
42639
|
+
init_ModelLoader();
|
|
42640
|
+
init_cn();
|
|
42641
|
+
init_GameCanvas3D();
|
|
42642
|
+
DEFAULT_GRID_CONFIG = {
|
|
42643
|
+
cellSize: 1,
|
|
42644
|
+
offsetX: 0,
|
|
42645
|
+
offsetZ: 0
|
|
42646
|
+
};
|
|
42647
|
+
GameCanvas3D = forwardRef(
|
|
42648
|
+
({
|
|
42649
|
+
tiles = [],
|
|
42650
|
+
units = [],
|
|
42651
|
+
features = [],
|
|
42652
|
+
events: events2 = [],
|
|
42653
|
+
orientation = "standard",
|
|
42654
|
+
cameraMode = "isometric",
|
|
42655
|
+
showGrid = true,
|
|
42656
|
+
showCoordinates = false,
|
|
42657
|
+
showTileInfo = false,
|
|
42658
|
+
overlay = "default",
|
|
42659
|
+
shadows = true,
|
|
42660
|
+
backgroundColor = "#1a1a2e",
|
|
42661
|
+
onTileClick,
|
|
42662
|
+
onUnitClick,
|
|
42663
|
+
onFeatureClick,
|
|
42664
|
+
onCanvasClick,
|
|
42665
|
+
onTileHover,
|
|
42666
|
+
onUnitAnimation,
|
|
42667
|
+
assetLoader: customAssetLoader,
|
|
42668
|
+
tileRenderer: CustomTileRenderer,
|
|
42669
|
+
unitRenderer: CustomUnitRenderer,
|
|
42670
|
+
featureRenderer: CustomFeatureRenderer,
|
|
42671
|
+
className,
|
|
42672
|
+
isLoading: externalLoading,
|
|
42673
|
+
error: externalError,
|
|
42674
|
+
entity,
|
|
42675
|
+
preloadAssets = [],
|
|
42676
|
+
tileClickEvent,
|
|
42677
|
+
unitClickEvent,
|
|
42678
|
+
featureClickEvent,
|
|
42679
|
+
canvasClickEvent,
|
|
42680
|
+
tileHoverEvent,
|
|
42681
|
+
tileLeaveEvent,
|
|
42682
|
+
unitAnimationEvent,
|
|
42683
|
+
cameraChangeEvent,
|
|
42684
|
+
loadingMessage = "Loading 3D Scene...",
|
|
42685
|
+
useInstancing = true,
|
|
42686
|
+
validMoves = [],
|
|
42687
|
+
attackTargets = [],
|
|
42688
|
+
selectedTileIds = [],
|
|
42689
|
+
selectedUnitId = null,
|
|
42690
|
+
children
|
|
42691
|
+
}, ref) => {
|
|
42692
|
+
const containerRef = useRef(null);
|
|
42693
|
+
const controlsRef = useRef(null);
|
|
42694
|
+
const [hoveredTile, setHoveredTile] = useState(null);
|
|
42695
|
+
const [internalError, setInternalError] = useState(null);
|
|
42696
|
+
const { isLoading: assetsLoading, progress, loaded, total } = useAssetLoader({
|
|
42697
|
+
preloadUrls: preloadAssets,
|
|
42698
|
+
loader: customAssetLoader
|
|
42699
|
+
});
|
|
42700
|
+
const eventHandlers = useGameCanvas3DEvents({
|
|
42701
|
+
tileClickEvent,
|
|
42702
|
+
unitClickEvent,
|
|
42703
|
+
featureClickEvent,
|
|
42704
|
+
canvasClickEvent,
|
|
42705
|
+
tileHoverEvent,
|
|
42706
|
+
tileLeaveEvent,
|
|
42707
|
+
unitAnimationEvent,
|
|
42708
|
+
cameraChangeEvent,
|
|
42709
|
+
onTileClick,
|
|
42710
|
+
onUnitClick,
|
|
42711
|
+
onFeatureClick,
|
|
42712
|
+
onCanvasClick,
|
|
42713
|
+
onTileHover,
|
|
42714
|
+
onUnitAnimation
|
|
42715
|
+
});
|
|
42716
|
+
const gridBounds = useMemo(() => {
|
|
42717
|
+
if (tiles.length === 0) {
|
|
42718
|
+
return { minX: 0, maxX: 10, minZ: 0, maxZ: 10 };
|
|
42719
|
+
}
|
|
42720
|
+
const xs = tiles.map((t) => t.x);
|
|
42721
|
+
const zs = tiles.map((t) => t.z || t.y || 0);
|
|
42722
|
+
return {
|
|
42723
|
+
minX: Math.min(...xs),
|
|
42724
|
+
maxX: Math.max(...xs),
|
|
42725
|
+
minZ: Math.min(...zs),
|
|
42726
|
+
maxZ: Math.max(...zs)
|
|
42727
|
+
};
|
|
42728
|
+
}, [tiles]);
|
|
42729
|
+
const cameraTarget = useMemo(() => {
|
|
42730
|
+
return [
|
|
42731
|
+
(gridBounds.minX + gridBounds.maxX) / 2,
|
|
42732
|
+
0,
|
|
42733
|
+
(gridBounds.minZ + gridBounds.maxZ) / 2
|
|
42734
|
+
];
|
|
42735
|
+
}, [gridBounds]);
|
|
42736
|
+
const gridConfig = useMemo(
|
|
42737
|
+
() => ({
|
|
42738
|
+
...DEFAULT_GRID_CONFIG,
|
|
42739
|
+
offsetX: -(gridBounds.maxX - gridBounds.minX) / 2,
|
|
42740
|
+
offsetZ: -(gridBounds.maxZ - gridBounds.minZ) / 2
|
|
42741
|
+
}),
|
|
42742
|
+
[gridBounds]
|
|
42743
|
+
);
|
|
42744
|
+
const gridToWorld = useCallback(
|
|
42745
|
+
(x, z, y = 0) => {
|
|
42746
|
+
const worldX = (x - gridBounds.minX) * gridConfig.cellSize;
|
|
42747
|
+
const worldZ = (z - gridBounds.minZ) * gridConfig.cellSize;
|
|
42748
|
+
return [worldX, y * gridConfig.cellSize, worldZ];
|
|
42749
|
+
},
|
|
42750
|
+
[gridBounds, gridConfig]
|
|
42751
|
+
);
|
|
42752
|
+
useImperativeHandle(ref, () => ({
|
|
42753
|
+
getCameraPosition: () => {
|
|
42754
|
+
if (controlsRef.current) {
|
|
42755
|
+
const pos = controlsRef.current.object.position;
|
|
42756
|
+
return new THREE.Vector3(pos.x, pos.y, pos.z);
|
|
42757
|
+
}
|
|
42758
|
+
return null;
|
|
42759
|
+
},
|
|
42760
|
+
setCameraPosition: (x, y, z) => {
|
|
42761
|
+
if (controlsRef.current) {
|
|
42762
|
+
controlsRef.current.object.position.set(x, y, z);
|
|
42763
|
+
controlsRef.current.update();
|
|
42764
|
+
}
|
|
42765
|
+
},
|
|
42766
|
+
lookAt: (x, y, z) => {
|
|
42767
|
+
if (controlsRef.current) {
|
|
42768
|
+
controlsRef.current.target.set(x, y, z);
|
|
42769
|
+
controlsRef.current.update();
|
|
42770
|
+
}
|
|
42771
|
+
},
|
|
42772
|
+
resetCamera: () => {
|
|
42773
|
+
if (controlsRef.current) {
|
|
42774
|
+
controlsRef.current.reset();
|
|
42775
|
+
}
|
|
42776
|
+
},
|
|
42777
|
+
screenshot: () => {
|
|
42778
|
+
const canvas = containerRef.current?.querySelector("canvas");
|
|
42779
|
+
if (canvas) {
|
|
42780
|
+
return canvas.toDataURL("image/png");
|
|
42781
|
+
}
|
|
42782
|
+
return null;
|
|
42783
|
+
},
|
|
42784
|
+
export: () => ({
|
|
42785
|
+
tiles,
|
|
42786
|
+
units,
|
|
42787
|
+
features
|
|
42788
|
+
})
|
|
42789
|
+
}));
|
|
42790
|
+
const handleTileClick = useCallback(
|
|
42791
|
+
(tile, event) => {
|
|
42792
|
+
eventHandlers.handleTileClick(tile, event);
|
|
42793
|
+
},
|
|
42794
|
+
[eventHandlers]
|
|
42795
|
+
);
|
|
42796
|
+
const handleUnitClick = useCallback(
|
|
42797
|
+
(unit, event) => {
|
|
42798
|
+
eventHandlers.handleUnitClick(unit, event);
|
|
42799
|
+
},
|
|
42800
|
+
[eventHandlers]
|
|
42801
|
+
);
|
|
42802
|
+
const handleFeatureClick = useCallback(
|
|
42803
|
+
(feature, event) => {
|
|
42804
|
+
if (event) {
|
|
42805
|
+
eventHandlers.handleFeatureClick(feature, event);
|
|
42806
|
+
}
|
|
42807
|
+
},
|
|
42808
|
+
[eventHandlers]
|
|
42809
|
+
);
|
|
42810
|
+
const handleTileHover = useCallback(
|
|
42811
|
+
(tile, event) => {
|
|
42812
|
+
setHoveredTile(tile);
|
|
42813
|
+
if (event) {
|
|
42814
|
+
eventHandlers.handleTileHover(tile, event);
|
|
42815
|
+
}
|
|
42816
|
+
},
|
|
42817
|
+
[eventHandlers]
|
|
42818
|
+
);
|
|
42819
|
+
const cameraConfig = useMemo(() => {
|
|
42820
|
+
const size = Math.max(
|
|
42821
|
+
gridBounds.maxX - gridBounds.minX,
|
|
42822
|
+
gridBounds.maxZ - gridBounds.minZ
|
|
42823
|
+
);
|
|
42824
|
+
const distance = size * 1.5;
|
|
42825
|
+
switch (cameraMode) {
|
|
42826
|
+
case "isometric":
|
|
42827
|
+
return {
|
|
42828
|
+
position: [distance, distance * 0.8, distance],
|
|
42829
|
+
fov: 45
|
|
42830
|
+
};
|
|
42831
|
+
case "top-down":
|
|
42832
|
+
return {
|
|
42833
|
+
position: [0, distance * 2, 0],
|
|
42834
|
+
fov: 45
|
|
42835
|
+
};
|
|
42836
|
+
case "perspective":
|
|
42837
|
+
default:
|
|
42838
|
+
return {
|
|
42839
|
+
position: [distance, distance, distance],
|
|
42840
|
+
fov: 45
|
|
42841
|
+
};
|
|
42842
|
+
}
|
|
42843
|
+
}, [cameraMode, gridBounds]);
|
|
42844
|
+
const DefaultTileRenderer = useCallback(
|
|
42845
|
+
({ tile, position }) => {
|
|
42846
|
+
const isSelected = tile.id ? selectedTileIds.includes(tile.id) : false;
|
|
42847
|
+
const isHovered = hoveredTile?.id === tile.id;
|
|
42848
|
+
const isValidMove = validMoves.some(
|
|
42849
|
+
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
42850
|
+
);
|
|
42851
|
+
const isAttackTarget = attackTargets.some(
|
|
42852
|
+
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
42853
|
+
);
|
|
42854
|
+
let color = 8421504;
|
|
42855
|
+
if (tile.type === "water") color = 4491468;
|
|
42856
|
+
else if (tile.type === "grass") color = 4500036;
|
|
42857
|
+
else if (tile.type === "sand") color = 14535816;
|
|
42858
|
+
else if (tile.type === "rock") color = 8947848;
|
|
42859
|
+
else if (tile.type === "snow") color = 15658734;
|
|
42860
|
+
let emissive = 0;
|
|
42861
|
+
if (isSelected) emissive = 4473924;
|
|
42862
|
+
else if (isAttackTarget) emissive = 4456448;
|
|
42863
|
+
else if (isValidMove) emissive = 17408;
|
|
42864
|
+
else if (isHovered) emissive = 2236962;
|
|
42865
|
+
if (tile.modelUrl) {
|
|
42866
|
+
return /* @__PURE__ */ jsx(
|
|
42867
|
+
"group",
|
|
42868
|
+
{
|
|
42869
|
+
position,
|
|
42870
|
+
onClick: (e) => handleTileClick(tile, e),
|
|
42871
|
+
onPointerEnter: (e) => handleTileHover(tile, e),
|
|
42872
|
+
onPointerLeave: (e) => handleTileHover(null, e),
|
|
42873
|
+
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
42874
|
+
children: /* @__PURE__ */ jsx(
|
|
42875
|
+
ModelLoader,
|
|
42876
|
+
{
|
|
42877
|
+
url: tile.modelUrl,
|
|
42878
|
+
scale: 0.95,
|
|
42879
|
+
fallbackGeometry: "box",
|
|
42880
|
+
castShadow: true,
|
|
42881
|
+
receiveShadow: true
|
|
42882
|
+
}
|
|
42883
|
+
)
|
|
42884
|
+
}
|
|
42885
|
+
);
|
|
42886
|
+
}
|
|
42887
|
+
return /* @__PURE__ */ jsxs(
|
|
42888
|
+
"mesh",
|
|
42889
|
+
{
|
|
42890
|
+
position,
|
|
42891
|
+
onClick: (e) => handleTileClick(tile, e),
|
|
42892
|
+
onPointerEnter: (e) => handleTileHover(tile, e),
|
|
42893
|
+
onPointerLeave: (e) => handleTileHover(null, e),
|
|
42894
|
+
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
42895
|
+
children: [
|
|
42896
|
+
/* @__PURE__ */ jsx("boxGeometry", { args: [0.95, 0.2, 0.95] }),
|
|
42897
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color, emissive })
|
|
42898
|
+
]
|
|
42899
|
+
}
|
|
42900
|
+
);
|
|
42901
|
+
},
|
|
42902
|
+
[selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover]
|
|
42903
|
+
);
|
|
42904
|
+
const DefaultUnitRenderer = useCallback(
|
|
42905
|
+
({ unit, position }) => {
|
|
42906
|
+
const isSelected = selectedUnitId === unit.id;
|
|
42907
|
+
const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
|
|
42908
|
+
return /* @__PURE__ */ jsxs(
|
|
42909
|
+
"group",
|
|
42910
|
+
{
|
|
42911
|
+
position,
|
|
42912
|
+
onClick: (e) => handleUnitClick(unit, e),
|
|
42913
|
+
userData: { type: "unit", unitId: unit.id },
|
|
42914
|
+
children: [
|
|
42915
|
+
isSelected && /* @__PURE__ */ jsxs("mesh", { position: [0, 0.05, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
42916
|
+
/* @__PURE__ */ jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
|
|
42917
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
|
|
42918
|
+
] }),
|
|
42919
|
+
unit.modelUrl ? (
|
|
42920
|
+
/* GLB unit model (box fallback while loading / on error) */
|
|
42921
|
+
/* @__PURE__ */ jsx(
|
|
42922
|
+
ModelLoader,
|
|
42923
|
+
{
|
|
42924
|
+
url: unit.modelUrl,
|
|
42925
|
+
scale: 0.5,
|
|
42926
|
+
fallbackGeometry: "box",
|
|
42927
|
+
castShadow: true
|
|
42928
|
+
}
|
|
42929
|
+
)
|
|
42930
|
+
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
42931
|
+
/* @__PURE__ */ jsxs("mesh", { position: [0, 0.3, 0], children: [
|
|
42932
|
+
/* @__PURE__ */ jsx("cylinderGeometry", { args: [0.3, 0.3, 0.1, 8] }),
|
|
42933
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color })
|
|
42934
|
+
] }),
|
|
42935
|
+
/* @__PURE__ */ jsxs("mesh", { position: [0, 0.6, 0], children: [
|
|
42936
|
+
/* @__PURE__ */ jsx("capsuleGeometry", { args: [0.2, 0.4, 4, 8] }),
|
|
42937
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color })
|
|
42938
|
+
] }),
|
|
42939
|
+
/* @__PURE__ */ jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
42940
|
+
/* @__PURE__ */ jsx("sphereGeometry", { args: [0.12, 8, 8] }),
|
|
42941
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color })
|
|
42942
|
+
] })
|
|
42943
|
+
] }),
|
|
42944
|
+
unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxs("group", { position: [0, 1.2, 0], children: [
|
|
42945
|
+
/* @__PURE__ */ jsxs("mesh", { position: [-0.25, 0, 0], children: [
|
|
42946
|
+
/* @__PURE__ */ jsx("planeGeometry", { args: [0.5, 0.05] }),
|
|
42947
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { color: 3355443 })
|
|
42948
|
+
] }),
|
|
42949
|
+
/* @__PURE__ */ jsxs(
|
|
42950
|
+
"mesh",
|
|
42951
|
+
{
|
|
42952
|
+
position: [
|
|
42953
|
+
-0.25 + 0.5 * (unit.health / unit.maxHealth) / 2,
|
|
42954
|
+
0,
|
|
42955
|
+
0.01
|
|
42956
|
+
],
|
|
42957
|
+
children: [
|
|
42958
|
+
/* @__PURE__ */ jsx("planeGeometry", { args: [0.5 * (unit.health / unit.maxHealth), 0.05] }),
|
|
42959
|
+
/* @__PURE__ */ jsx(
|
|
42960
|
+
"meshBasicMaterial",
|
|
42961
|
+
{
|
|
42962
|
+
color: unit.health / unit.maxHealth > 0.5 ? 4500036 : unit.health / unit.maxHealth > 0.25 ? 11184708 : 16729156
|
|
42963
|
+
}
|
|
42964
|
+
)
|
|
42965
|
+
]
|
|
42966
|
+
}
|
|
42967
|
+
)
|
|
42968
|
+
] })
|
|
42969
|
+
]
|
|
42970
|
+
}
|
|
42971
|
+
);
|
|
42972
|
+
},
|
|
42973
|
+
[selectedUnitId, handleUnitClick]
|
|
42974
|
+
);
|
|
42975
|
+
const DefaultFeatureRenderer = useCallback(
|
|
42976
|
+
({
|
|
42977
|
+
feature,
|
|
42978
|
+
position
|
|
42979
|
+
}) => {
|
|
42980
|
+
if (feature.assetUrl) {
|
|
42981
|
+
return /* @__PURE__ */ jsx(
|
|
42982
|
+
ModelLoader,
|
|
42983
|
+
{
|
|
42984
|
+
url: feature.assetUrl,
|
|
42985
|
+
position,
|
|
42986
|
+
scale: 0.5,
|
|
42987
|
+
rotation: [0, feature.rotation ?? 0, 0],
|
|
42988
|
+
onClick: () => handleFeatureClick(feature, null),
|
|
42989
|
+
fallbackGeometry: "box"
|
|
42990
|
+
},
|
|
42991
|
+
feature.id
|
|
42992
|
+
);
|
|
42993
|
+
}
|
|
42994
|
+
if (feature.type === "tree") {
|
|
42995
|
+
return /* @__PURE__ */ jsxs(
|
|
42996
|
+
"group",
|
|
42997
|
+
{
|
|
42998
|
+
position,
|
|
42999
|
+
onClick: (e) => handleFeatureClick(feature, e),
|
|
43000
|
+
userData: { type: "feature", featureId: feature.id },
|
|
43001
|
+
children: [
|
|
43002
|
+
/* @__PURE__ */ jsxs("mesh", { position: [0, 0.4, 0], children: [
|
|
43003
|
+
/* @__PURE__ */ jsx("cylinderGeometry", { args: [0.1, 0.15, 0.8, 6] }),
|
|
43004
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: 9127187 })
|
|
43005
|
+
] }),
|
|
43006
|
+
/* @__PURE__ */ jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
43007
|
+
/* @__PURE__ */ jsx("coneGeometry", { args: [0.5, 0.8, 8] }),
|
|
43008
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: 2263842 })
|
|
43009
|
+
] })
|
|
43010
|
+
]
|
|
43011
|
+
}
|
|
43012
|
+
);
|
|
43013
|
+
}
|
|
43014
|
+
if (feature.type === "rock") {
|
|
43015
|
+
return /* @__PURE__ */ jsxs(
|
|
43016
|
+
"mesh",
|
|
43017
|
+
{
|
|
43018
|
+
position: [position[0], position[1] + 0.3, position[2]],
|
|
43019
|
+
onClick: (e) => handleFeatureClick(feature, e),
|
|
43020
|
+
userData: { type: "feature", featureId: feature.id },
|
|
43021
|
+
children: [
|
|
43022
|
+
/* @__PURE__ */ jsx("dodecahedronGeometry", { args: [0.3, 0] }),
|
|
43023
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: 8421504 })
|
|
43024
|
+
]
|
|
43025
|
+
}
|
|
43026
|
+
);
|
|
43027
|
+
}
|
|
43028
|
+
return null;
|
|
43029
|
+
},
|
|
43030
|
+
[handleFeatureClick]
|
|
43031
|
+
);
|
|
43032
|
+
if (externalLoading || assetsLoading && preloadAssets.length > 0) {
|
|
43033
|
+
return /* @__PURE__ */ jsx(
|
|
43034
|
+
Canvas3DLoadingState,
|
|
43035
|
+
{
|
|
43036
|
+
progress,
|
|
43037
|
+
loaded,
|
|
43038
|
+
total,
|
|
43039
|
+
message: loadingMessage,
|
|
43040
|
+
className
|
|
43041
|
+
}
|
|
43042
|
+
);
|
|
43043
|
+
}
|
|
43044
|
+
const displayError = externalError || internalError;
|
|
43045
|
+
if (displayError) {
|
|
43046
|
+
return /* @__PURE__ */ jsx(Canvas3DErrorBoundary, { children: /* @__PURE__ */ jsx("div", { className: "game-canvas-3d game-canvas-3d--error", children: /* @__PURE__ */ jsxs("div", { className: "game-canvas-3d__error", children: [
|
|
43047
|
+
"Error: ",
|
|
43048
|
+
displayError
|
|
43049
|
+
] }) }) });
|
|
43050
|
+
}
|
|
43051
|
+
return /* @__PURE__ */ jsx(
|
|
43052
|
+
Canvas3DErrorBoundary,
|
|
43053
|
+
{
|
|
43054
|
+
onError: (err) => setInternalError(err.message),
|
|
43055
|
+
onReset: () => setInternalError(null),
|
|
43056
|
+
children: /* @__PURE__ */ jsxs(
|
|
43057
|
+
"div",
|
|
43058
|
+
{
|
|
43059
|
+
ref: containerRef,
|
|
43060
|
+
className: cn("game-canvas-3d relative w-full h-full min-h-[85vh] overflow-hidden", className),
|
|
43061
|
+
"data-orientation": orientation,
|
|
43062
|
+
"data-camera-mode": cameraMode,
|
|
43063
|
+
"data-overlay": overlay,
|
|
43064
|
+
children: [
|
|
43065
|
+
/* @__PURE__ */ jsxs(
|
|
43066
|
+
Canvas,
|
|
43067
|
+
{
|
|
43068
|
+
shadows,
|
|
43069
|
+
camera: {
|
|
43070
|
+
position: cameraConfig.position,
|
|
43071
|
+
fov: cameraConfig.fov,
|
|
43072
|
+
near: 0.1,
|
|
43073
|
+
far: 1e3
|
|
43074
|
+
},
|
|
43075
|
+
style: { background: backgroundColor },
|
|
43076
|
+
onClick: (e) => {
|
|
43077
|
+
if (e.target === e.currentTarget) {
|
|
43078
|
+
eventHandlers.handleCanvasClick(e);
|
|
43079
|
+
}
|
|
43080
|
+
},
|
|
43081
|
+
children: [
|
|
43082
|
+
/* @__PURE__ */ jsx(CameraController, { onCameraChange: eventHandlers.handleCameraChange }),
|
|
43083
|
+
/* @__PURE__ */ jsx("ambientLight", { intensity: 0.6 }),
|
|
43084
|
+
/* @__PURE__ */ jsx(
|
|
43085
|
+
"directionalLight",
|
|
43086
|
+
{
|
|
43087
|
+
position: [10, 20, 10],
|
|
43088
|
+
intensity: 0.8,
|
|
43089
|
+
castShadow: shadows,
|
|
43090
|
+
"shadow-mapSize": [2048, 2048]
|
|
43091
|
+
}
|
|
43092
|
+
),
|
|
43093
|
+
/* @__PURE__ */ jsx("hemisphereLight", { intensity: 0.3, color: "#87ceeb", groundColor: "#362d1d" }),
|
|
43094
|
+
showGrid && /* @__PURE__ */ jsx(
|
|
43095
|
+
Grid$1,
|
|
43096
|
+
{
|
|
43097
|
+
args: [
|
|
43098
|
+
Math.max(gridBounds.maxX - gridBounds.minX + 2, 10),
|
|
43099
|
+
Math.max(gridBounds.maxZ - gridBounds.minZ + 2, 10)
|
|
43100
|
+
],
|
|
43101
|
+
position: [
|
|
43102
|
+
(gridBounds.maxX - gridBounds.minX) / 2 - 0.5,
|
|
43103
|
+
0,
|
|
43104
|
+
(gridBounds.maxZ - gridBounds.minZ) / 2 - 0.5
|
|
43105
|
+
],
|
|
43106
|
+
cellSize: 1,
|
|
43107
|
+
cellThickness: 1,
|
|
43108
|
+
cellColor: "#444444",
|
|
43109
|
+
sectionSize: 5,
|
|
43110
|
+
sectionThickness: 1.5,
|
|
43111
|
+
sectionColor: "#666666",
|
|
43112
|
+
fadeDistance: 50,
|
|
43113
|
+
fadeStrength: 1
|
|
43114
|
+
}
|
|
43115
|
+
),
|
|
43116
|
+
tiles.map((tile, index) => {
|
|
43117
|
+
const position = gridToWorld(
|
|
43118
|
+
tile.x,
|
|
43119
|
+
tile.z ?? tile.y ?? 0,
|
|
43120
|
+
tile.elevation ?? 0
|
|
43121
|
+
);
|
|
43122
|
+
const Renderer = CustomTileRenderer || DefaultTileRenderer;
|
|
43123
|
+
return /* @__PURE__ */ jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
|
|
43124
|
+
}),
|
|
43125
|
+
features.map((feature, index) => {
|
|
43126
|
+
const position = gridToWorld(
|
|
43127
|
+
feature.x,
|
|
43128
|
+
feature.z ?? feature.y ?? 0,
|
|
43129
|
+
(feature.elevation ?? 0) + 0.5
|
|
43130
|
+
);
|
|
43131
|
+
const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
|
|
43132
|
+
return /* @__PURE__ */ jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
|
|
43133
|
+
}),
|
|
43134
|
+
units.map((unit) => {
|
|
43135
|
+
const position = gridToWorld(
|
|
43136
|
+
unit.x ?? 0,
|
|
43137
|
+
unit.z ?? unit.y ?? 0,
|
|
43138
|
+
(unit.elevation ?? 0) + 0.5
|
|
43139
|
+
);
|
|
43140
|
+
const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
|
|
43141
|
+
return /* @__PURE__ */ jsx(Renderer, { unit, position }, unit.id);
|
|
43142
|
+
}),
|
|
43143
|
+
children,
|
|
43144
|
+
/* @__PURE__ */ jsx(
|
|
43145
|
+
OrbitControls,
|
|
43146
|
+
{
|
|
43147
|
+
ref: controlsRef,
|
|
43148
|
+
target: cameraTarget,
|
|
43149
|
+
enableDamping: true,
|
|
43150
|
+
dampingFactor: 0.05,
|
|
43151
|
+
minDistance: 2,
|
|
43152
|
+
maxDistance: 100,
|
|
43153
|
+
maxPolarAngle: Math.PI / 2 - 0.1
|
|
43154
|
+
}
|
|
43155
|
+
)
|
|
43156
|
+
]
|
|
43157
|
+
}
|
|
43158
|
+
),
|
|
43159
|
+
showCoordinates && hoveredTile && /* @__PURE__ */ jsxs("div", { className: "game-canvas-3d__coordinates", children: [
|
|
43160
|
+
"X: ",
|
|
43161
|
+
hoveredTile.x,
|
|
43162
|
+
", Z: ",
|
|
43163
|
+
hoveredTile.z ?? hoveredTile.y ?? 0
|
|
43164
|
+
] }),
|
|
43165
|
+
showTileInfo && hoveredTile && /* @__PURE__ */ jsxs("div", { className: "game-canvas-3d__tile-info", children: [
|
|
43166
|
+
/* @__PURE__ */ jsx("div", { className: "tile-info__type", children: hoveredTile.type }),
|
|
43167
|
+
hoveredTile.terrain && /* @__PURE__ */ jsx("div", { className: "tile-info__terrain", children: hoveredTile.terrain })
|
|
43168
|
+
] })
|
|
43169
|
+
]
|
|
43170
|
+
}
|
|
43171
|
+
)
|
|
43172
|
+
}
|
|
43173
|
+
);
|
|
43174
|
+
}
|
|
43175
|
+
);
|
|
43176
|
+
GameCanvas3D.displayName = "GameCanvas3D";
|
|
43177
|
+
}
|
|
43178
|
+
});
|
|
43179
|
+
function GameBoard3D({
|
|
43180
|
+
entity,
|
|
43181
|
+
tiles = [],
|
|
43182
|
+
features = [],
|
|
43183
|
+
cameraMode = "perspective",
|
|
43184
|
+
backgroundColor = "#2a1a1a",
|
|
43185
|
+
tileClickEvent,
|
|
43186
|
+
unitClickEvent,
|
|
43187
|
+
attackEvent,
|
|
43188
|
+
endTurnEvent,
|
|
43189
|
+
cancelEvent,
|
|
43190
|
+
playAgainEvent,
|
|
43191
|
+
gameEndEvent,
|
|
43192
|
+
className
|
|
43193
|
+
}) {
|
|
43194
|
+
const row = boardEntity(entity);
|
|
43195
|
+
const eventBus = useEventBus();
|
|
43196
|
+
const entityUnits = row ? rows(row.units) : [];
|
|
43197
|
+
const units = entityUnits;
|
|
43198
|
+
const selectedUnitId = row ? str(row.selectedUnitId) || null : null;
|
|
43199
|
+
const phase = row ? str(row.phase) : "observation";
|
|
43200
|
+
const result = row ? str(row.result) : "none";
|
|
43201
|
+
const validMoves = row && Array.isArray(row.validMoves) ? row.validMoves : [];
|
|
43202
|
+
const attackTargets = row && Array.isArray(row.attackTargets) ? row.attackTargets : [];
|
|
43203
|
+
const turn = row ? num(row.turn) : 0;
|
|
43204
|
+
const currentTeam = row ? str(row.currentTeam) : "player";
|
|
43205
|
+
const isGameOver = result !== "none";
|
|
43206
|
+
const gameEndEmittedRef = useRef(false);
|
|
43207
|
+
useEffect(() => {
|
|
43208
|
+
if ((result === "victory" || result === "defeat") && gameEndEvent) {
|
|
43209
|
+
if (!gameEndEmittedRef.current) {
|
|
43210
|
+
gameEndEmittedRef.current = true;
|
|
43211
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
43212
|
+
}
|
|
43213
|
+
} else {
|
|
43214
|
+
gameEndEmittedRef.current = false;
|
|
43215
|
+
}
|
|
43216
|
+
}, [result, gameEndEvent, eventBus]);
|
|
43217
|
+
const checkGameEnd = useCallback(() => {
|
|
43218
|
+
const alivePlayer = entityUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
|
|
43219
|
+
const aliveEnemy = entityUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
|
|
43220
|
+
if (alivePlayer.length === 0 && gameEndEvent) {
|
|
43221
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result: "defeat" });
|
|
43222
|
+
} else if (aliveEnemy.length === 0 && gameEndEvent) {
|
|
43223
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result: "victory" });
|
|
43224
|
+
}
|
|
43225
|
+
}, [entityUnits, gameEndEvent, eventBus]);
|
|
43226
|
+
const handleUnitClickCallback = useCallback((isoUnit) => {
|
|
43227
|
+
if (phase !== "action" || !selectedUnitId || !attackEvent) return;
|
|
43228
|
+
const attackerRow = entityUnits.find((u) => str(u.id) === selectedUnitId);
|
|
43229
|
+
const targetRow = entityUnits.find((u) => str(u.id) === isoUnit.id);
|
|
43230
|
+
if (!attackerRow || !targetRow) return;
|
|
43231
|
+
if (unitTeam(targetRow) !== "enemy" || unitHealth(targetRow) <= 0) return;
|
|
43232
|
+
const ap = unitPosition(attackerRow);
|
|
43233
|
+
const tp = unitPosition(targetRow);
|
|
43234
|
+
const dx = Math.abs(ap.x - tp.x);
|
|
43235
|
+
const dy = Math.abs(ap.y - tp.y);
|
|
43236
|
+
if (dx <= 1 && dy <= 1 && dx + dy > 0) {
|
|
43237
|
+
const damage = Math.max(1, num(attackerRow.attack) - num(targetRow.defense));
|
|
43238
|
+
eventBus.emit(`UI:${attackEvent}`, {
|
|
43239
|
+
attackerId: str(attackerRow.id),
|
|
43240
|
+
targetId: str(targetRow.id),
|
|
43241
|
+
damage
|
|
43242
|
+
});
|
|
43243
|
+
setTimeout(checkGameEnd, 100);
|
|
43244
|
+
}
|
|
43245
|
+
}, [phase, selectedUnitId, entityUnits, attackEvent, eventBus, checkGameEnd]);
|
|
43246
|
+
const handleEndTurn = useCallback(() => {
|
|
43247
|
+
if (endTurnEvent) eventBus.emit(`UI:${endTurnEvent}`, {});
|
|
43248
|
+
}, [endTurnEvent, eventBus]);
|
|
43249
|
+
const handleCancel = useCallback(() => {
|
|
43250
|
+
if (cancelEvent) eventBus.emit(`UI:${cancelEvent}`, {});
|
|
43251
|
+
}, [cancelEvent, eventBus]);
|
|
43252
|
+
const handlePlayAgain = useCallback(() => {
|
|
43253
|
+
if (playAgainEvent) eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
43254
|
+
}, [playAgainEvent, eventBus]);
|
|
43255
|
+
return /* @__PURE__ */ jsxs(VStack, { className: cn("game-board-3d block w-full min-h-[85vh] relative", className), children: [
|
|
43256
|
+
/* @__PURE__ */ jsxs("div", { className: "game-board-3d__status absolute top-3 left-3 z-10 flex gap-2 items-center", children: [
|
|
43257
|
+
/* @__PURE__ */ jsx(Typography, { variant: "small", className: "status__phase capitalize", children: phase.replace("_", " ") }),
|
|
43258
|
+
currentTeam && !isGameOver && /* @__PURE__ */ jsxs(Typography, { variant: "small", color: "muted", className: "status__team", children: [
|
|
43259
|
+
"\u2014 ",
|
|
43260
|
+
currentTeam === "player" ? "Your Turn" : "Enemy's Turn"
|
|
43261
|
+
] }),
|
|
43262
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "small", color: "muted", className: "status__turn", children: [
|
|
43263
|
+
"Turn ",
|
|
43264
|
+
turn
|
|
43265
|
+
] })
|
|
43266
|
+
] }),
|
|
43267
|
+
/* @__PURE__ */ jsx(
|
|
43268
|
+
GameCanvas3D,
|
|
43269
|
+
{
|
|
43270
|
+
tiles,
|
|
43271
|
+
units,
|
|
43272
|
+
features,
|
|
43273
|
+
cameraMode,
|
|
43274
|
+
showGrid: true,
|
|
43275
|
+
showCoordinates: false,
|
|
43276
|
+
showTileInfo: false,
|
|
43277
|
+
shadows: true,
|
|
43278
|
+
backgroundColor,
|
|
43279
|
+
tileClickEvent,
|
|
43280
|
+
unitClickEvent,
|
|
43281
|
+
onUnitClick: handleUnitClickCallback,
|
|
43282
|
+
selectedUnitId,
|
|
43283
|
+
validMoves,
|
|
43284
|
+
attackTargets,
|
|
43285
|
+
className: "game-board-3d__canvas w-full min-h-[85vh]"
|
|
43286
|
+
}
|
|
43287
|
+
),
|
|
43288
|
+
!isGameOver && /* @__PURE__ */ jsxs(HStack, { className: "fixed bottom-6 right-6 z-50", gap: "sm", children: [
|
|
43289
|
+
(phase === "selection" || phase === "movement" || phase === "action") && /* @__PURE__ */ jsx(
|
|
43290
|
+
Button,
|
|
43291
|
+
{
|
|
43292
|
+
variant: "secondary",
|
|
43293
|
+
className: "shadow-xl",
|
|
43294
|
+
onClick: handleCancel,
|
|
43295
|
+
children: "Cancel"
|
|
43296
|
+
}
|
|
43297
|
+
),
|
|
43298
|
+
phase !== "enemy_turn" && /* @__PURE__ */ jsx(
|
|
43299
|
+
Button,
|
|
43300
|
+
{
|
|
43301
|
+
variant: "primary",
|
|
43302
|
+
className: "shadow-xl",
|
|
43303
|
+
onClick: handleEndTurn,
|
|
43304
|
+
children: "End Turn"
|
|
43305
|
+
}
|
|
43306
|
+
)
|
|
43307
|
+
] }),
|
|
43308
|
+
isGameOver && /* @__PURE__ */ jsx("div", { className: "game-board-3d__overlay absolute inset-0 z-20 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxs(VStack, { align: "center", gap: "md", className: "overlay__card p-8 rounded-xl bg-gray-900/90 shadow-2xl", children: [
|
|
43309
|
+
/* @__PURE__ */ jsx(
|
|
43310
|
+
Typography,
|
|
43311
|
+
{
|
|
43312
|
+
variant: "h2",
|
|
43313
|
+
className: cn(
|
|
43314
|
+
"overlay__result",
|
|
43315
|
+
result === "victory" ? "text-yellow-400" : "text-red-500"
|
|
43316
|
+
),
|
|
43317
|
+
children: result === "victory" ? "Victory!" : "Defeat"
|
|
43318
|
+
}
|
|
43319
|
+
),
|
|
43320
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "body", color: "muted", className: "overlay__turn-count", children: [
|
|
43321
|
+
"Completed in ",
|
|
43322
|
+
turn,
|
|
43323
|
+
" turn",
|
|
43324
|
+
turn !== 1 ? "s" : ""
|
|
43325
|
+
] }),
|
|
43326
|
+
/* @__PURE__ */ jsx(
|
|
43327
|
+
Button,
|
|
43328
|
+
{
|
|
43329
|
+
variant: "primary",
|
|
43330
|
+
className: "px-8 py-3 font-semibold",
|
|
43331
|
+
onClick: handlePlayAgain,
|
|
43332
|
+
children: "Play Again"
|
|
43333
|
+
}
|
|
43334
|
+
)
|
|
43335
|
+
] }) })
|
|
43336
|
+
] });
|
|
43337
|
+
}
|
|
43338
|
+
var init_GameBoard3D = __esm({
|
|
43339
|
+
"components/game/organisms/GameBoard3D.tsx"() {
|
|
43340
|
+
"use client";
|
|
43341
|
+
init_cn();
|
|
43342
|
+
init_Button();
|
|
43343
|
+
init_Typography();
|
|
43344
|
+
init_Stack();
|
|
43345
|
+
init_useEventBus();
|
|
43346
|
+
init_GameCanvas3D2();
|
|
43347
|
+
init_boardEntity();
|
|
43348
|
+
GameBoard3D.displayName = "GameBoard3D";
|
|
43349
|
+
}
|
|
43350
|
+
});
|
|
41672
43351
|
var GameShell;
|
|
41673
43352
|
var init_GameShell = __esm({
|
|
41674
43353
|
"components/game/templates/GameShell.tsx"() {
|
|
@@ -43145,21 +44824,6 @@ var init_ModalSlot = __esm({
|
|
|
43145
44824
|
ModalSlot.displayName = "ModalSlot";
|
|
43146
44825
|
}
|
|
43147
44826
|
});
|
|
43148
|
-
function getOpponentAction(strategy, actions, history) {
|
|
43149
|
-
const actionIds = actions.map((a) => a.id);
|
|
43150
|
-
switch (strategy) {
|
|
43151
|
-
case "always-cooperate":
|
|
43152
|
-
return actionIds[0];
|
|
43153
|
-
case "always-defect":
|
|
43154
|
-
return actionIds[actionIds.length - 1];
|
|
43155
|
-
case "tit-for-tat":
|
|
43156
|
-
if (history.length === 0) return actionIds[0];
|
|
43157
|
-
return history[history.length - 1].playerAction;
|
|
43158
|
-
case "random":
|
|
43159
|
-
default:
|
|
43160
|
-
return actionIds[Math.floor(Math.random() * actionIds.length)];
|
|
43161
|
-
}
|
|
43162
|
-
}
|
|
43163
44827
|
function NegotiatorBoard({
|
|
43164
44828
|
entity,
|
|
43165
44829
|
completeEvent = "PUZZLE_COMPLETE",
|
|
@@ -43181,28 +44845,35 @@ function NegotiatorBoard({
|
|
|
43181
44845
|
const playerTotal = num(resolved?.score);
|
|
43182
44846
|
const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
|
|
43183
44847
|
const won = result === "win";
|
|
43184
|
-
const
|
|
44848
|
+
const lastPlayerAction = str(resolved?.lastPlayerAction);
|
|
44849
|
+
const lastOpponentAction = str(resolved?.lastOpponentAction);
|
|
44850
|
+
const lastPayoff = num(resolved?.lastPayoff);
|
|
43185
44851
|
const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
|
|
43186
44852
|
const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
|
|
44853
|
+
const prevRoundRef = useRef(currentRound);
|
|
44854
|
+
useEffect(() => {
|
|
44855
|
+
if (currentRound > prevRoundRef.current && lastPlayerAction) {
|
|
44856
|
+
const opponentPayoffEntry = payoffMatrix.find(
|
|
44857
|
+
(p2) => p2.playerAction === lastPlayerAction && p2.opponentAction === lastOpponentAction
|
|
44858
|
+
);
|
|
44859
|
+
setHistory((prev) => [
|
|
44860
|
+
...prev,
|
|
44861
|
+
{
|
|
44862
|
+
round: currentRound,
|
|
44863
|
+
playerAction: lastPlayerAction,
|
|
44864
|
+
opponentAction: lastOpponentAction,
|
|
44865
|
+
playerPayoff: lastPayoff,
|
|
44866
|
+
opponentPayoff: opponentPayoffEntry?.opponentPayoff ?? 0
|
|
44867
|
+
}
|
|
44868
|
+
]);
|
|
44869
|
+
}
|
|
44870
|
+
prevRoundRef.current = currentRound;
|
|
44871
|
+
}, [currentRound, lastPlayerAction, lastOpponentAction, lastPayoff, payoffMatrix]);
|
|
44872
|
+
const opponentTotal = useMemo(() => history.reduce((s, r) => s + r.opponentPayoff, 0), [history]);
|
|
43187
44873
|
const handleAction = useCallback((actionId) => {
|
|
43188
44874
|
if (isComplete) return;
|
|
43189
|
-
const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
|
|
43190
|
-
const payoff = payoffMatrix.find(
|
|
43191
|
-
(p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
|
|
43192
|
-
);
|
|
43193
|
-
const playerPayoff = payoff?.playerPayoff ?? 0;
|
|
43194
|
-
setHistory((prev) => [
|
|
43195
|
-
...prev,
|
|
43196
|
-
{
|
|
43197
|
-
round: prev.length + 1,
|
|
43198
|
-
playerAction: actionId,
|
|
43199
|
-
opponentAction,
|
|
43200
|
-
playerPayoff,
|
|
43201
|
-
opponentPayoff: payoff?.opponentPayoff ?? 0
|
|
43202
|
-
}
|
|
43203
|
-
]);
|
|
43204
44875
|
if (playRoundEvent) {
|
|
43205
|
-
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff:
|
|
44876
|
+
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: 0 });
|
|
43206
44877
|
}
|
|
43207
44878
|
if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
|
|
43208
44879
|
if (finishEvent) {
|
|
@@ -43212,10 +44883,11 @@ function NegotiatorBoard({
|
|
|
43212
44883
|
setShowHint(true);
|
|
43213
44884
|
}
|
|
43214
44885
|
}
|
|
43215
|
-
}, [isComplete, resolved, totalRounds, currentRound,
|
|
44886
|
+
}, [isComplete, resolved, totalRounds, currentRound, playRoundEvent, finishEvent, emit]);
|
|
43216
44887
|
const handleReset = useCallback(() => {
|
|
43217
44888
|
setHistory([]);
|
|
43218
44889
|
setShowHint(false);
|
|
44890
|
+
prevRoundRef.current = 0;
|
|
43219
44891
|
if (playAgainEvent) {
|
|
43220
44892
|
emit(`UI:${playAgainEvent}`, {});
|
|
43221
44893
|
}
|
|
@@ -43326,6 +44998,236 @@ var init_NegotiatorBoard = __esm({
|
|
|
43326
44998
|
NegotiatorBoard.displayName = "NegotiatorBoard";
|
|
43327
44999
|
}
|
|
43328
45000
|
});
|
|
45001
|
+
function resolveEntity(entity) {
|
|
45002
|
+
if (!entity) return {};
|
|
45003
|
+
if (Array.isArray(entity)) return entity[0] ?? {};
|
|
45004
|
+
return entity;
|
|
45005
|
+
}
|
|
45006
|
+
function numField(row, key, fallback) {
|
|
45007
|
+
const v = row[key];
|
|
45008
|
+
return typeof v === "number" ? v : fallback;
|
|
45009
|
+
}
|
|
45010
|
+
function strField(row, key, fallback) {
|
|
45011
|
+
const v = row[key];
|
|
45012
|
+
return typeof v === "string" ? v : fallback;
|
|
45013
|
+
}
|
|
45014
|
+
function PlatformerBoard({
|
|
45015
|
+
entity,
|
|
45016
|
+
player: propPlayer,
|
|
45017
|
+
platforms: propPlatforms,
|
|
45018
|
+
result: propResult,
|
|
45019
|
+
lives: propLives,
|
|
45020
|
+
score: propScore,
|
|
45021
|
+
level: propLevel,
|
|
45022
|
+
worldWidth = 800,
|
|
45023
|
+
worldHeight = 400,
|
|
45024
|
+
canvasWidth = 800,
|
|
45025
|
+
canvasHeight = 400,
|
|
45026
|
+
playerSprite,
|
|
45027
|
+
tileSprites,
|
|
45028
|
+
bgColor,
|
|
45029
|
+
leftEvent = "LEFT",
|
|
45030
|
+
rightEvent = "RIGHT",
|
|
45031
|
+
jumpEvent = "JUMP",
|
|
45032
|
+
stopEvent = "STOP",
|
|
45033
|
+
playAgainEvent = "PLAY_AGAIN",
|
|
45034
|
+
gameEndEvent = "GAME_END",
|
|
45035
|
+
className
|
|
45036
|
+
}) {
|
|
45037
|
+
const row = resolveEntity(entity);
|
|
45038
|
+
const eventBus = useEventBus();
|
|
45039
|
+
const { t } = useTranslate();
|
|
45040
|
+
const result = propResult ?? strField(row, "result", "none");
|
|
45041
|
+
propLives ?? numField(row, "lives", 3);
|
|
45042
|
+
const score = propScore ?? numField(row, "score", 0);
|
|
45043
|
+
const level = propLevel ?? numField(row, "level", 1);
|
|
45044
|
+
const entityPlayer = row["player"];
|
|
45045
|
+
const player = propPlayer ?? entityPlayer ?? {
|
|
45046
|
+
x: 80,
|
|
45047
|
+
y: 320,
|
|
45048
|
+
width: 32,
|
|
45049
|
+
height: 48,
|
|
45050
|
+
vx: 0,
|
|
45051
|
+
vy: 0,
|
|
45052
|
+
grounded: false,
|
|
45053
|
+
facingRight: true
|
|
45054
|
+
};
|
|
45055
|
+
const entityPlatforms = Array.isArray(row["platforms"]) ? row["platforms"] : void 0;
|
|
45056
|
+
const platforms = propPlatforms ?? entityPlatforms ?? [
|
|
45057
|
+
{ x: 0, y: 368, width: 800, height: 32, type: "ground" },
|
|
45058
|
+
{ x: 150, y: 280, width: 160, height: 16, type: "platform" },
|
|
45059
|
+
{ x: 420, y: 220, width: 160, height: 16, type: "platform" },
|
|
45060
|
+
{ x: 580, y: 300, width: 80, height: 16, type: "hazard" },
|
|
45061
|
+
{ x: 700, y: 340, width: 64, height: 28, type: "goal" }
|
|
45062
|
+
];
|
|
45063
|
+
const handleRestart = useCallback(() => {
|
|
45064
|
+
if (playAgainEvent) {
|
|
45065
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
45066
|
+
}
|
|
45067
|
+
}, [playAgainEvent, eventBus]);
|
|
45068
|
+
const isGameOver = result === "won" || result === "lost";
|
|
45069
|
+
const gameEndEmittedRef = useRef(false);
|
|
45070
|
+
useEffect(() => {
|
|
45071
|
+
if ((result === "won" || result === "lost") && gameEndEvent) {
|
|
45072
|
+
if (!gameEndEmittedRef.current) {
|
|
45073
|
+
gameEndEmittedRef.current = true;
|
|
45074
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
45075
|
+
}
|
|
45076
|
+
} else {
|
|
45077
|
+
gameEndEmittedRef.current = false;
|
|
45078
|
+
}
|
|
45079
|
+
}, [result, gameEndEvent, eventBus]);
|
|
45080
|
+
return /* @__PURE__ */ jsx(
|
|
45081
|
+
VStack,
|
|
45082
|
+
{
|
|
45083
|
+
className: cn("platformer-board relative bg-background", className),
|
|
45084
|
+
gap: "none",
|
|
45085
|
+
children: /* @__PURE__ */ jsxs(Box, { className: "relative", children: [
|
|
45086
|
+
/* @__PURE__ */ jsx(
|
|
45087
|
+
PlatformerCanvas,
|
|
45088
|
+
{
|
|
45089
|
+
player,
|
|
45090
|
+
platforms,
|
|
45091
|
+
worldWidth,
|
|
45092
|
+
worldHeight,
|
|
45093
|
+
canvasWidth,
|
|
45094
|
+
canvasHeight,
|
|
45095
|
+
followCamera: true,
|
|
45096
|
+
bgColor,
|
|
45097
|
+
playerSprite,
|
|
45098
|
+
tileSprites,
|
|
45099
|
+
leftEvent,
|
|
45100
|
+
rightEvent,
|
|
45101
|
+
jumpEvent,
|
|
45102
|
+
stopEvent
|
|
45103
|
+
}
|
|
45104
|
+
),
|
|
45105
|
+
isGameOver && /* @__PURE__ */ jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
45106
|
+
/* @__PURE__ */ jsx(
|
|
45107
|
+
Typography,
|
|
45108
|
+
{
|
|
45109
|
+
variant: "h2",
|
|
45110
|
+
className: cn(
|
|
45111
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
45112
|
+
result === "won" ? "text-warning" : "text-error"
|
|
45113
|
+
),
|
|
45114
|
+
children: result === "won" ? t("platformer.won") : t("platformer.lost")
|
|
45115
|
+
}
|
|
45116
|
+
),
|
|
45117
|
+
result === "won" && /* @__PURE__ */ jsxs(Typography, { variant: "body1", className: "text-muted-foreground", children: [
|
|
45118
|
+
t("platformer.level"),
|
|
45119
|
+
" ",
|
|
45120
|
+
level,
|
|
45121
|
+
" \u2014 ",
|
|
45122
|
+
t("platformer.score"),
|
|
45123
|
+
": ",
|
|
45124
|
+
score
|
|
45125
|
+
] }),
|
|
45126
|
+
result === "lost" && /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: t("platformer.noLives") }),
|
|
45127
|
+
/* @__PURE__ */ jsx(
|
|
45128
|
+
Button,
|
|
45129
|
+
{
|
|
45130
|
+
variant: "primary",
|
|
45131
|
+
className: "px-8 py-3 font-semibold",
|
|
45132
|
+
onClick: handleRestart,
|
|
45133
|
+
children: result === "won" ? t("platformer.nextLevel") : t("platformer.playAgain")
|
|
45134
|
+
}
|
|
45135
|
+
)
|
|
45136
|
+
] }) })
|
|
45137
|
+
] })
|
|
45138
|
+
}
|
|
45139
|
+
);
|
|
45140
|
+
}
|
|
45141
|
+
var init_PlatformerBoard = __esm({
|
|
45142
|
+
"components/game/organisms/PlatformerBoard.tsx"() {
|
|
45143
|
+
"use client";
|
|
45144
|
+
init_cn();
|
|
45145
|
+
init_useEventBus();
|
|
45146
|
+
init_Box();
|
|
45147
|
+
init_Button();
|
|
45148
|
+
init_Typography();
|
|
45149
|
+
init_Stack();
|
|
45150
|
+
init_PlatformerCanvas();
|
|
45151
|
+
PlatformerBoard.displayName = "PlatformerBoard";
|
|
45152
|
+
}
|
|
45153
|
+
});
|
|
45154
|
+
function PlatformerTemplate({
|
|
45155
|
+
entity,
|
|
45156
|
+
platforms = DEFAULT_PLATFORMS,
|
|
45157
|
+
worldWidth = 800,
|
|
45158
|
+
worldHeight = 400,
|
|
45159
|
+
canvasWidth = 800,
|
|
45160
|
+
canvasHeight = 400,
|
|
45161
|
+
playerSprite = `${CDN3}/characters/platformChar_idle.png`,
|
|
45162
|
+
tileSprites = DEFAULT_TILE_SPRITES,
|
|
45163
|
+
bgColor = "#5c94fc",
|
|
45164
|
+
gameEndEvent = "GAME_END",
|
|
45165
|
+
className
|
|
45166
|
+
}) {
|
|
45167
|
+
const row = entity && !Array.isArray(entity) ? entity : void 0;
|
|
45168
|
+
const lives = typeof row?.["lives"] === "number" ? row["lives"] : 3;
|
|
45169
|
+
const score = typeof row?.["score"] === "number" ? row["score"] : 0;
|
|
45170
|
+
const level = typeof row?.["level"] === "number" ? row["level"] : 1;
|
|
45171
|
+
return /* @__PURE__ */ jsxs(VStack, { className: cn("platformer-template w-full", className), gap: "none", children: [
|
|
45172
|
+
/* @__PURE__ */ jsx(Box, { className: "relative", children: /* @__PURE__ */ jsx(
|
|
45173
|
+
GameHud,
|
|
45174
|
+
{
|
|
45175
|
+
position: "top",
|
|
45176
|
+
stats: [
|
|
45177
|
+
{ label: "Lives", value: lives, format: "number", variant: "danger" },
|
|
45178
|
+
{ label: "Level", value: level, format: "number", variant: "default" }
|
|
45179
|
+
]
|
|
45180
|
+
}
|
|
45181
|
+
) }),
|
|
45182
|
+
/* @__PURE__ */ jsx(
|
|
45183
|
+
PlatformerBoard,
|
|
45184
|
+
{
|
|
45185
|
+
entity,
|
|
45186
|
+
platforms,
|
|
45187
|
+
worldWidth,
|
|
45188
|
+
worldHeight,
|
|
45189
|
+
canvasWidth,
|
|
45190
|
+
canvasHeight,
|
|
45191
|
+
playerSprite,
|
|
45192
|
+
tileSprites,
|
|
45193
|
+
bgColor,
|
|
45194
|
+
leftEvent: "LEFT",
|
|
45195
|
+
rightEvent: "RIGHT",
|
|
45196
|
+
jumpEvent: "JUMP",
|
|
45197
|
+
stopEvent: "STOP",
|
|
45198
|
+
playAgainEvent: "PLAY_AGAIN",
|
|
45199
|
+
gameEndEvent
|
|
45200
|
+
}
|
|
45201
|
+
),
|
|
45202
|
+
/* @__PURE__ */ jsx(Box, { className: "px-4 py-2", children: /* @__PURE__ */ jsx(ScoreBoard, { score, level }) })
|
|
45203
|
+
] });
|
|
45204
|
+
}
|
|
45205
|
+
var CDN3, DEFAULT_TILE_SPRITES, DEFAULT_PLATFORMS;
|
|
45206
|
+
var init_PlatformerTemplate = __esm({
|
|
45207
|
+
"components/game/templates/PlatformerTemplate.tsx"() {
|
|
45208
|
+
init_cn();
|
|
45209
|
+
init_Box();
|
|
45210
|
+
init_Stack();
|
|
45211
|
+
init_PlatformerBoard();
|
|
45212
|
+
init_GameHud();
|
|
45213
|
+
init_ScoreBoard();
|
|
45214
|
+
CDN3 = "https://almadar-kflow-assets.web.app/shared/platformer";
|
|
45215
|
+
DEFAULT_TILE_SPRITES = {
|
|
45216
|
+
ground: `${CDN3}/tiles/platformPack_tile001.png`,
|
|
45217
|
+
platform: `${CDN3}/tiles/platformPack_tile004.png`,
|
|
45218
|
+
hazard: `${CDN3}/tiles/platformPack_tile017.png`,
|
|
45219
|
+
goal: `${CDN3}/tiles/platformPack_tile007.png`
|
|
45220
|
+
};
|
|
45221
|
+
DEFAULT_PLATFORMS = [
|
|
45222
|
+
{ x: 0, y: 368, width: 800, height: 32, type: "ground" },
|
|
45223
|
+
{ x: 150, y: 280, width: 160, height: 16, type: "platform" },
|
|
45224
|
+
{ x: 420, y: 220, width: 160, height: 16, type: "platform" },
|
|
45225
|
+
{ x: 580, y: 300, width: 80, height: 16, type: "hazard" },
|
|
45226
|
+
{ x: 700, y: 340, width: 64, height: 28, type: "goal" }
|
|
45227
|
+
];
|
|
45228
|
+
PlatformerTemplate.displayName = "PlatformerTemplate";
|
|
45229
|
+
}
|
|
45230
|
+
});
|
|
43329
45231
|
var PricingOrganism;
|
|
43330
45232
|
var init_PricingOrganism = __esm({
|
|
43331
45233
|
"components/marketing/organisms/PricingOrganism.tsx"() {
|
|
@@ -43449,6 +45351,7 @@ var init_PricingPageTemplate = __esm({
|
|
|
43449
45351
|
}
|
|
43450
45352
|
});
|
|
43451
45353
|
function ResourceCounter({
|
|
45354
|
+
assetUrl = DEFAULT_ASSET_URL12,
|
|
43452
45355
|
icon,
|
|
43453
45356
|
label = "Gold",
|
|
43454
45357
|
value = 250,
|
|
@@ -43468,7 +45371,17 @@ function ResourceCounter({
|
|
|
43468
45371
|
className
|
|
43469
45372
|
),
|
|
43470
45373
|
children: [
|
|
43471
|
-
|
|
45374
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
45375
|
+
"img",
|
|
45376
|
+
{
|
|
45377
|
+
src: assetUrl,
|
|
45378
|
+
alt: label,
|
|
45379
|
+
width: sizes.img,
|
|
45380
|
+
height: sizes.img,
|
|
45381
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
45382
|
+
className: "flex-shrink-0"
|
|
45383
|
+
}
|
|
45384
|
+
) : icon ? /* @__PURE__ */ jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon }) : /* @__PURE__ */ jsx(Icon, { icon }) }) : null,
|
|
43472
45385
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label }),
|
|
43473
45386
|
/* @__PURE__ */ jsxs("span", { className: cn("font-bold tabular-nums", color && (color in colorTokenClasses2 ? colorTokenClasses2[color] : color)), children: [
|
|
43474
45387
|
value,
|
|
@@ -43481,7 +45394,7 @@ function ResourceCounter({
|
|
|
43481
45394
|
}
|
|
43482
45395
|
);
|
|
43483
45396
|
}
|
|
43484
|
-
var colorTokenClasses2, sizeMap15;
|
|
45397
|
+
var colorTokenClasses2, DEFAULT_ASSET_URL12, sizeMap15;
|
|
43485
45398
|
var init_ResourceCounter = __esm({
|
|
43486
45399
|
"components/game/atoms/ResourceCounter.tsx"() {
|
|
43487
45400
|
init_cn();
|
|
@@ -43494,14 +45407,365 @@ var init_ResourceCounter = __esm({
|
|
|
43494
45407
|
error: "text-error",
|
|
43495
45408
|
muted: "text-muted-foreground"
|
|
43496
45409
|
};
|
|
45410
|
+
DEFAULT_ASSET_URL12 = "https://almadar-kflow-assets.web.app/shared/world-map/gold_mine.png";
|
|
43497
45411
|
sizeMap15 = {
|
|
43498
|
-
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm" },
|
|
43499
|
-
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base" },
|
|
43500
|
-
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg" }
|
|
45412
|
+
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm", img: 16 },
|
|
45413
|
+
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base", img: 20 },
|
|
45414
|
+
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg", img: 28 }
|
|
43501
45415
|
};
|
|
43502
45416
|
ResourceCounter.displayName = "ResourceCounter";
|
|
43503
45417
|
}
|
|
43504
45418
|
});
|
|
45419
|
+
function RoguelikeBoard({
|
|
45420
|
+
entity,
|
|
45421
|
+
tiles: propTiles,
|
|
45422
|
+
enemies: propEnemies,
|
|
45423
|
+
items: propItems,
|
|
45424
|
+
player: propPlayer,
|
|
45425
|
+
playerHp: propPlayerHp,
|
|
45426
|
+
playerMaxHp: propPlayerMaxHp,
|
|
45427
|
+
playerAttack: propPlayerAttack,
|
|
45428
|
+
depth: propDepth,
|
|
45429
|
+
result: propResult,
|
|
45430
|
+
phase: propPhase,
|
|
45431
|
+
stairsX: propStairsX,
|
|
45432
|
+
stairsY: propStairsY,
|
|
45433
|
+
assetManifest: propAssetManifest,
|
|
45434
|
+
scale = 0.45,
|
|
45435
|
+
unitScale = 1,
|
|
45436
|
+
moveEvent,
|
|
45437
|
+
playAgainEvent,
|
|
45438
|
+
gameEndEvent,
|
|
45439
|
+
className
|
|
45440
|
+
}) {
|
|
45441
|
+
const board = boardEntity(entity) ?? {};
|
|
45442
|
+
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : DEFAULT_TILES2);
|
|
45443
|
+
const enemies = propEnemies ?? (Array.isArray(board.enemies) ? board.enemies : DEFAULT_ENEMIES);
|
|
45444
|
+
const items = propItems ?? (Array.isArray(board.items) ? board.items : DEFAULT_ITEMS);
|
|
45445
|
+
const player = propPlayer ?? board.player ?? { x: 1, y: 1 };
|
|
45446
|
+
const playerHp = propPlayerHp ?? num(board.playerHp, 10);
|
|
45447
|
+
const playerMaxHp = propPlayerMaxHp ?? num(board.playerMaxHp, 10);
|
|
45448
|
+
const playerAttack = propPlayerAttack ?? num(board.playerAttack, 3);
|
|
45449
|
+
const depth = propDepth ?? num(board.depth, 1);
|
|
45450
|
+
const result = propResult ?? (str(board.result) || "none");
|
|
45451
|
+
const phase = propPhase ?? (str(board.phase) || "player_turn");
|
|
45452
|
+
propStairsX ?? num(board.stairsX, 3);
|
|
45453
|
+
propStairsY ?? num(board.stairsY, 3);
|
|
45454
|
+
const eventBus = useEventBus();
|
|
45455
|
+
const { t } = useTranslate();
|
|
45456
|
+
const [hoveredTile, setHoveredTile] = useState(null);
|
|
45457
|
+
const emitMove = useCallback((dx, dy) => {
|
|
45458
|
+
if (!moveEvent) return;
|
|
45459
|
+
eventBus.emit(`UI:${moveEvent}`, { dx, dy });
|
|
45460
|
+
}, [moveEvent, eventBus]);
|
|
45461
|
+
useEffect(() => {
|
|
45462
|
+
const handler = (e) => {
|
|
45463
|
+
if (result !== "none" || phase !== "player_turn") return;
|
|
45464
|
+
if (e.key === "ArrowUp" || e.key === "w") emitMove(0, -1);
|
|
45465
|
+
if (e.key === "ArrowDown" || e.key === "s") emitMove(0, 1);
|
|
45466
|
+
if (e.key === "ArrowLeft" || e.key === "a") emitMove(-1, 0);
|
|
45467
|
+
if (e.key === "ArrowRight" || e.key === "d") emitMove(1, 0);
|
|
45468
|
+
};
|
|
45469
|
+
window.addEventListener("keydown", handler);
|
|
45470
|
+
return () => window.removeEventListener("keydown", handler);
|
|
45471
|
+
}, [result, phase, emitMove]);
|
|
45472
|
+
useEffect(() => {
|
|
45473
|
+
if ((result === "won" || result === "lost") && gameEndEvent) {
|
|
45474
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
45475
|
+
}
|
|
45476
|
+
}, [result, gameEndEvent, eventBus]);
|
|
45477
|
+
const handleTileClick = useCallback((x, y) => {
|
|
45478
|
+
if (result !== "none" || phase !== "player_turn") return;
|
|
45479
|
+
const dx = x - player.x;
|
|
45480
|
+
const dy = y - player.y;
|
|
45481
|
+
if (Math.abs(dx) + Math.abs(dy) === 1) {
|
|
45482
|
+
emitMove(dx, dy);
|
|
45483
|
+
}
|
|
45484
|
+
}, [result, phase, player, emitMove]);
|
|
45485
|
+
const handleReset = useCallback(() => {
|
|
45486
|
+
if (!playAgainEvent) return;
|
|
45487
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
45488
|
+
}, [playAgainEvent, eventBus]);
|
|
45489
|
+
const isoUnits = useMemo(() => {
|
|
45490
|
+
const liveEnemies = enemies.filter((e) => e.hp > 0);
|
|
45491
|
+
return [
|
|
45492
|
+
{
|
|
45493
|
+
id: "player",
|
|
45494
|
+
position: player,
|
|
45495
|
+
name: t("roguelike.player") || "Hero",
|
|
45496
|
+
team: "player",
|
|
45497
|
+
health: playerHp,
|
|
45498
|
+
maxHealth: playerMaxHp,
|
|
45499
|
+
unitType: "player",
|
|
45500
|
+
sprite: `${CDN4}/isometric-dungeon/Characters/Male/Male_0_Idle0.png`
|
|
45501
|
+
},
|
|
45502
|
+
...liveEnemies.map((e) => ({
|
|
45503
|
+
id: e.id,
|
|
45504
|
+
position: { x: e.x, y: e.y },
|
|
45505
|
+
name: t("roguelike.enemy") || "Enemy",
|
|
45506
|
+
team: "enemy",
|
|
45507
|
+
health: e.hp,
|
|
45508
|
+
maxHealth: 6,
|
|
45509
|
+
unitType: "enemy",
|
|
45510
|
+
sprite: `${CDN4}/sprite-sheets/shadow-legion-sprite-sheet-sw.png`
|
|
45511
|
+
}))
|
|
45512
|
+
];
|
|
45513
|
+
}, [player, enemies, playerHp, playerMaxHp, t]);
|
|
45514
|
+
const isoFeatures = useMemo(
|
|
45515
|
+
() => items.map((it) => ({
|
|
45516
|
+
id: it.id,
|
|
45517
|
+
x: it.x,
|
|
45518
|
+
y: it.y,
|
|
45519
|
+
type: it.kind,
|
|
45520
|
+
sprite: FEATURE_SPRITE[it.kind] ?? FEATURE_SPRITE.health_potion
|
|
45521
|
+
})),
|
|
45522
|
+
[items]
|
|
45523
|
+
);
|
|
45524
|
+
const validMoves = useMemo(() => {
|
|
45525
|
+
if (result !== "none" || phase !== "player_turn") return [];
|
|
45526
|
+
const dirs = [{ dx: 0, dy: -1 }, { dx: 0, dy: 1 }, { dx: -1, dy: 0 }, { dx: 1, dy: 0 }];
|
|
45527
|
+
return dirs.map(({ dx, dy }) => ({ x: player.x + dx, y: player.y + dy })).filter(({ x, y }) => {
|
|
45528
|
+
const tile = tiles.find((t2) => t2.x === x && t2.y === y);
|
|
45529
|
+
return tile?.passable ?? false;
|
|
45530
|
+
});
|
|
45531
|
+
}, [result, phase, player, tiles]);
|
|
45532
|
+
const isGameOver = result === "won" || result === "lost";
|
|
45533
|
+
return /* @__PURE__ */ jsxs(VStack, { className: cn("roguelike-board relative min-h-[600px] bg-background", className), gap: "none", children: [
|
|
45534
|
+
/* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between px-4 py-2 border-b border-border", gap: "md", children: [
|
|
45535
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
45536
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.depth") || "Depth" }),
|
|
45537
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body1", className: "font-bold tabular-nums", children: depth })
|
|
45538
|
+
] }),
|
|
45539
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
45540
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.hp") || "HP" }),
|
|
45541
|
+
/* @__PURE__ */ jsxs(
|
|
45542
|
+
Typography,
|
|
45543
|
+
{
|
|
45544
|
+
variant: "body1",
|
|
45545
|
+
className: cn(
|
|
45546
|
+
"font-bold tabular-nums",
|
|
45547
|
+
playerHp <= Math.floor(playerMaxHp * 0.25) ? "text-error" : "text-success"
|
|
45548
|
+
),
|
|
45549
|
+
children: [
|
|
45550
|
+
playerHp,
|
|
45551
|
+
" / ",
|
|
45552
|
+
playerMaxHp
|
|
45553
|
+
]
|
|
45554
|
+
}
|
|
45555
|
+
)
|
|
45556
|
+
] }),
|
|
45557
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
45558
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.atk") || "ATK" }),
|
|
45559
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body1", className: "font-bold tabular-nums text-foreground", children: playerAttack })
|
|
45560
|
+
] }),
|
|
45561
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
45562
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.phase") || "Phase" }),
|
|
45563
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "capitalize text-foreground", children: phase.replace("_", " ") })
|
|
45564
|
+
] })
|
|
45565
|
+
] }),
|
|
45566
|
+
/* @__PURE__ */ jsx(Box, { className: "relative flex-1", children: /* @__PURE__ */ jsx(
|
|
45567
|
+
IsometricCanvas_default,
|
|
45568
|
+
{
|
|
45569
|
+
tiles,
|
|
45570
|
+
units: isoUnits,
|
|
45571
|
+
features: isoFeatures,
|
|
45572
|
+
selectedUnitId: null,
|
|
45573
|
+
validMoves,
|
|
45574
|
+
attackTargets: [],
|
|
45575
|
+
hoveredTile,
|
|
45576
|
+
onTileClick: handleTileClick,
|
|
45577
|
+
onUnitClick: () => void 0,
|
|
45578
|
+
onTileHover: (x, y) => setHoveredTile({ x, y }),
|
|
45579
|
+
onTileLeave: () => setHoveredTile(null),
|
|
45580
|
+
scale,
|
|
45581
|
+
assetBaseUrl: propAssetManifest?.baseUrl ?? CDN4,
|
|
45582
|
+
assetManifest: propAssetManifest,
|
|
45583
|
+
unitScale
|
|
45584
|
+
}
|
|
45585
|
+
) }),
|
|
45586
|
+
!isGameOver && /* @__PURE__ */ jsx(HStack, { className: "justify-center gap-2 p-3", gap: "none", children: [
|
|
45587
|
+
{ label: "\u2191", dx: 0, dy: -1 },
|
|
45588
|
+
{ label: "\u2193", dx: 0, dy: 1 },
|
|
45589
|
+
{ label: "\u2190", dx: -1, dy: 0 },
|
|
45590
|
+
{ label: "\u2192", dx: 1, dy: 0 }
|
|
45591
|
+
].map(({ label, dx, dy }) => /* @__PURE__ */ jsx(
|
|
45592
|
+
Button,
|
|
45593
|
+
{
|
|
45594
|
+
variant: "secondary",
|
|
45595
|
+
className: "w-9 h-9 p-0 text-base font-mono",
|
|
45596
|
+
onClick: () => emitMove(dx, dy),
|
|
45597
|
+
disabled: phase !== "player_turn",
|
|
45598
|
+
children: label
|
|
45599
|
+
},
|
|
45600
|
+
label
|
|
45601
|
+
)) }),
|
|
45602
|
+
isGameOver && /* @__PURE__ */ jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/75 backdrop-blur-sm rounded-container", children: /* @__PURE__ */ jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
45603
|
+
/* @__PURE__ */ jsx(
|
|
45604
|
+
Typography,
|
|
45605
|
+
{
|
|
45606
|
+
variant: "h2",
|
|
45607
|
+
className: cn(
|
|
45608
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
45609
|
+
result === "won" ? "text-warning" : "text-error"
|
|
45610
|
+
),
|
|
45611
|
+
children: result === "won" ? t("roguelike.victory") || "Victory!" : t("roguelike.defeat") || "Defeated!"
|
|
45612
|
+
}
|
|
45613
|
+
),
|
|
45614
|
+
result === "won" && /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: t("roguelike.cleared") || `Dungeon cleared! Depth reached: ${depth}` }),
|
|
45615
|
+
/* @__PURE__ */ jsx(
|
|
45616
|
+
Button,
|
|
45617
|
+
{
|
|
45618
|
+
variant: "primary",
|
|
45619
|
+
className: "px-8 py-3 font-semibold",
|
|
45620
|
+
onClick: handleReset,
|
|
45621
|
+
children: t("roguelike.playAgain") || "Play Again"
|
|
45622
|
+
}
|
|
45623
|
+
)
|
|
45624
|
+
] }) })
|
|
45625
|
+
] });
|
|
45626
|
+
}
|
|
45627
|
+
var CDN4, DEFAULT_TILES2, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
|
|
45628
|
+
var init_RoguelikeBoard = __esm({
|
|
45629
|
+
"components/game/organisms/RoguelikeBoard.tsx"() {
|
|
45630
|
+
"use client";
|
|
45631
|
+
init_cn();
|
|
45632
|
+
init_useEventBus();
|
|
45633
|
+
init_Box();
|
|
45634
|
+
init_Button();
|
|
45635
|
+
init_Typography();
|
|
45636
|
+
init_Stack();
|
|
45637
|
+
init_IsometricCanvas();
|
|
45638
|
+
init_boardEntity();
|
|
45639
|
+
CDN4 = "https://almadar-kflow-assets.web.app/shared";
|
|
45640
|
+
DEFAULT_TILES2 = [
|
|
45641
|
+
{ x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45642
|
+
{ x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45643
|
+
{ x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45644
|
+
{ x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45645
|
+
{ x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45646
|
+
{ x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45647
|
+
{ x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45648
|
+
{ x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45649
|
+
{ x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45650
|
+
{ x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45651
|
+
{ x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45652
|
+
{ x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
45653
|
+
{ x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
45654
|
+
{ x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45655
|
+
{ x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45656
|
+
{ x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45657
|
+
{ x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45658
|
+
{ x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
45659
|
+
{ x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` },
|
|
45660
|
+
{ x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45661
|
+
{ x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45662
|
+
{ x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45663
|
+
{ x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45664
|
+
{ x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45665
|
+
{ x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` }
|
|
45666
|
+
];
|
|
45667
|
+
DEFAULT_ENEMIES = [
|
|
45668
|
+
{ id: "e1", x: 3, y: 1, hp: 5, attack: 2 },
|
|
45669
|
+
{ id: "e2", x: 1, y: 3, hp: 4, attack: 1 }
|
|
45670
|
+
];
|
|
45671
|
+
DEFAULT_ITEMS = [
|
|
45672
|
+
{ id: "i1", x: 2, y: 2, kind: "health_potion" }
|
|
45673
|
+
];
|
|
45674
|
+
FEATURE_SPRITE = {
|
|
45675
|
+
health_potion: `${CDN4}/isometric-dungeon/Isometric/chestClosed_E.png`,
|
|
45676
|
+
sword: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`,
|
|
45677
|
+
shield: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`
|
|
45678
|
+
};
|
|
45679
|
+
RoguelikeBoard.displayName = "RoguelikeBoard";
|
|
45680
|
+
}
|
|
45681
|
+
});
|
|
45682
|
+
function RoguelikeTemplate({
|
|
45683
|
+
entity,
|
|
45684
|
+
scale = 0.45,
|
|
45685
|
+
unitScale = 1,
|
|
45686
|
+
tiles = DEFAULT_ROGUELIKE_TILES,
|
|
45687
|
+
enemies,
|
|
45688
|
+
items,
|
|
45689
|
+
stairsX,
|
|
45690
|
+
stairsY,
|
|
45691
|
+
assetManifest = DEFAULT_ROGUELIKE_MANIFEST,
|
|
45692
|
+
gameEndEvent,
|
|
45693
|
+
className
|
|
45694
|
+
}) {
|
|
45695
|
+
const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
|
|
45696
|
+
if (!resolved && !tiles && !assetManifest) return null;
|
|
45697
|
+
return /* @__PURE__ */ jsx(
|
|
45698
|
+
RoguelikeBoard,
|
|
45699
|
+
{
|
|
45700
|
+
entity: resolved,
|
|
45701
|
+
tiles,
|
|
45702
|
+
enemies,
|
|
45703
|
+
items,
|
|
45704
|
+
stairsX,
|
|
45705
|
+
stairsY,
|
|
45706
|
+
assetManifest,
|
|
45707
|
+
scale,
|
|
45708
|
+
unitScale,
|
|
45709
|
+
moveEvent: "MOVE",
|
|
45710
|
+
playAgainEvent: "PLAY_AGAIN",
|
|
45711
|
+
gameEndEvent: gameEndEvent ?? "GAME_END",
|
|
45712
|
+
className
|
|
45713
|
+
}
|
|
45714
|
+
);
|
|
45715
|
+
}
|
|
45716
|
+
var CDN5, DEFAULT_ROGUELIKE_TILES, DEFAULT_ROGUELIKE_MANIFEST;
|
|
45717
|
+
var init_RoguelikeTemplate = __esm({
|
|
45718
|
+
"components/game/templates/RoguelikeTemplate.tsx"() {
|
|
45719
|
+
init_RoguelikeBoard();
|
|
45720
|
+
CDN5 = "https://almadar-kflow-assets.web.app/shared";
|
|
45721
|
+
DEFAULT_ROGUELIKE_TILES = [
|
|
45722
|
+
{ x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45723
|
+
{ x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45724
|
+
{ x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45725
|
+
{ x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45726
|
+
{ x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45727
|
+
{ x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45728
|
+
{ x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45729
|
+
{ x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45730
|
+
{ x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45731
|
+
{ x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45732
|
+
{ x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45733
|
+
{ x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
45734
|
+
{ x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
45735
|
+
{ x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45736
|
+
{ x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45737
|
+
{ x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45738
|
+
{ x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
45739
|
+
{ x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
45740
|
+
{ x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stairs_E.png` },
|
|
45741
|
+
{ x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45742
|
+
{ x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45743
|
+
{ x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45744
|
+
{ x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45745
|
+
{ x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
45746
|
+
{ x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` }
|
|
45747
|
+
];
|
|
45748
|
+
DEFAULT_ROGUELIKE_MANIFEST = {
|
|
45749
|
+
baseUrl: CDN5,
|
|
45750
|
+
terrains: {
|
|
45751
|
+
floor: "/isometric-dungeon/Isometric/dirt_E.png",
|
|
45752
|
+
wall: "/isometric-dungeon/Isometric/stoneInset_E.png",
|
|
45753
|
+
stairs: "/isometric-dungeon/Isometric/stairs_E.png",
|
|
45754
|
+
planks: "/isometric-dungeon/Isometric/planks_E.png"
|
|
45755
|
+
},
|
|
45756
|
+
units: {
|
|
45757
|
+
player: "/isometric-dungeon/Characters/Male/Male_0_Idle0.png",
|
|
45758
|
+
enemy: "/sprite-sheets/shadow-legion-sprite-sheet-sw.png"
|
|
45759
|
+
},
|
|
45760
|
+
features: {
|
|
45761
|
+
health_potion: "/isometric-dungeon/Isometric/chestClosed_E.png",
|
|
45762
|
+
sword: "/isometric-dungeon/Isometric/barrel_E.png",
|
|
45763
|
+
shield: "/isometric-dungeon/Isometric/barrel_E.png"
|
|
45764
|
+
}
|
|
45765
|
+
};
|
|
45766
|
+
RoguelikeTemplate.displayName = "RoguelikeTemplate";
|
|
45767
|
+
}
|
|
45768
|
+
});
|
|
43505
45769
|
|
|
43506
45770
|
// lib/traitRegistry.ts
|
|
43507
45771
|
function getAllTraits() {
|
|
@@ -44736,7 +47000,7 @@ function getAllEvents(traits2) {
|
|
|
44736
47000
|
function EventDispatcherTab({ traits: traits2, schema }) {
|
|
44737
47001
|
const eventBus = useEventBus();
|
|
44738
47002
|
const { t } = useTranslate();
|
|
44739
|
-
const [
|
|
47003
|
+
const [log11, setLog] = React77.useState([]);
|
|
44740
47004
|
const prevStatesRef = React77.useRef(/* @__PURE__ */ new Map());
|
|
44741
47005
|
React77.useEffect(() => {
|
|
44742
47006
|
for (const trait of traits2) {
|
|
@@ -44800,9 +47064,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
|
|
|
44800
47064
|
/* @__PURE__ */ jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.otherEvents") }),
|
|
44801
47065
|
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: unavailableEvents.map((event) => /* @__PURE__ */ jsx(Badge, { variant: "default", size: "sm", className: "opacity-50", children: event }, event)) })
|
|
44802
47066
|
] }),
|
|
44803
|
-
|
|
47067
|
+
log11.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
44804
47068
|
/* @__PURE__ */ jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.recentTransitions") }),
|
|
44805
|
-
/* @__PURE__ */ jsx(Stack, { gap: "xs", children:
|
|
47069
|
+
/* @__PURE__ */ jsx(Stack, { gap: "xs", children: log11.map((entry, i) => /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
|
|
44806
47070
|
/* @__PURE__ */ jsx("span", { className: "text-purple-400", children: entry.traitName }),
|
|
44807
47071
|
" ",
|
|
44808
47072
|
/* @__PURE__ */ jsx("span", { className: "text-gray-500", children: entry.from }),
|
|
@@ -45605,17 +47869,24 @@ function SequencerBoard({
|
|
|
45605
47869
|
const { emit } = useEventBus();
|
|
45606
47870
|
const { t } = useTranslate();
|
|
45607
47871
|
const resolved = boardEntity(entity);
|
|
45608
|
-
const maxSlots = num(resolved?.maxSlots);
|
|
47872
|
+
const maxSlots = num(resolved?.maxSlots) || 3;
|
|
45609
47873
|
const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
|
|
45610
47874
|
const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
|
|
45611
47875
|
const allowDuplicates = resolved?.allowDuplicates !== false;
|
|
47876
|
+
const entitySlots = Array.isArray(resolved?.slots) ? resolved.slots : [];
|
|
47877
|
+
const entityResult = str(resolved?.result);
|
|
47878
|
+
const entityAttempts = num(resolved?.attempts);
|
|
47879
|
+
const entityCurrentStep = typeof resolved?.currentStep === "number" ? resolved.currentStep : -1;
|
|
47880
|
+
const slots = Array.from({ length: maxSlots }, (_, i) => {
|
|
47881
|
+
const entitySlot = entitySlots.find((s) => s.index === i);
|
|
47882
|
+
if (!entitySlot?.placedActionId) return void 0;
|
|
47883
|
+
return availableActions.find((a) => a.id === entitySlot.placedActionId);
|
|
47884
|
+
});
|
|
47885
|
+
const isSuccess = entityResult === "win";
|
|
47886
|
+
const attempts = entityAttempts;
|
|
47887
|
+
const isPlayingBack = entityCurrentStep >= 0 && !isSuccess;
|
|
47888
|
+
const currentStep = entityCurrentStep;
|
|
45612
47889
|
const [headerError, setHeaderError] = useState(false);
|
|
45613
|
-
const [slots, setSlots] = useState(
|
|
45614
|
-
() => Array.from({ length: maxSlots }, () => void 0)
|
|
45615
|
-
);
|
|
45616
|
-
const [playState, setPlayState] = useState("idle");
|
|
45617
|
-
const [currentStep, setCurrentStep] = useState(-1);
|
|
45618
|
-
const [attempts, setAttempts] = useState(0);
|
|
45619
47890
|
const [slotFeedback, setSlotFeedback] = useState(
|
|
45620
47891
|
() => Array.from({ length: maxSlots }, () => null)
|
|
45621
47892
|
);
|
|
@@ -45624,11 +47895,6 @@ function SequencerBoard({
|
|
|
45624
47895
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
45625
47896
|
}, []);
|
|
45626
47897
|
const handleSlotDrop = useCallback((index, item) => {
|
|
45627
|
-
setSlots((prev) => {
|
|
45628
|
-
const next = [...prev];
|
|
45629
|
-
next[index] = item;
|
|
45630
|
-
return next;
|
|
45631
|
-
});
|
|
45632
47898
|
setSlotFeedback((prev) => {
|
|
45633
47899
|
const next = [...prev];
|
|
45634
47900
|
next[index] = null;
|
|
@@ -45638,11 +47904,6 @@ function SequencerBoard({
|
|
|
45638
47904
|
if (placeEvent) emit(`UI:${placeEvent}`, { slotIndex: index, actionId: item.id });
|
|
45639
47905
|
}, [emit, placeEvent]);
|
|
45640
47906
|
const handleSlotRemove = useCallback((index) => {
|
|
45641
|
-
setSlots((prev) => {
|
|
45642
|
-
const next = [...prev];
|
|
45643
|
-
next[index] = void 0;
|
|
45644
|
-
return next;
|
|
45645
|
-
});
|
|
45646
47907
|
setSlotFeedback((prev) => {
|
|
45647
47908
|
const next = [...prev];
|
|
45648
47909
|
next[index] = null;
|
|
@@ -45653,48 +47914,34 @@ function SequencerBoard({
|
|
|
45653
47914
|
}, [emit, removeEvent]);
|
|
45654
47915
|
const handleReset = useCallback(() => {
|
|
45655
47916
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
45656
|
-
setSlots(Array.from({ length: maxSlots }, () => void 0));
|
|
45657
|
-
setPlayState("idle");
|
|
45658
|
-
setCurrentStep(-1);
|
|
45659
|
-
setAttempts(0);
|
|
45660
47917
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
45661
47918
|
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
45662
47919
|
}, [maxSlots, playAgainEvent, emit]);
|
|
45663
47920
|
const filledSlots = slots.filter((s) => !!s);
|
|
45664
|
-
const canPlay = filledSlots.length > 0 &&
|
|
47921
|
+
const canPlay = filledSlots.length > 0 && !isPlayingBack && !isSuccess;
|
|
45665
47922
|
const handlePlay = useCallback(() => {
|
|
45666
47923
|
if (!canPlay) return;
|
|
45667
47924
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
45668
47925
|
emit("UI:PLAY_SOUND", { key: "confirm" });
|
|
45669
47926
|
const sequence = slots.map((s) => s?.id || "");
|
|
45670
|
-
|
|
45671
|
-
|
|
45672
|
-
}
|
|
45673
|
-
setPlayState("playing");
|
|
45674
|
-
setCurrentStep(0);
|
|
47927
|
+
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
47928
|
+
if (playEvent) emit(`UI:${playEvent}`, { sequence });
|
|
45675
47929
|
let step = 0;
|
|
45676
47930
|
const advance = () => {
|
|
45677
47931
|
step++;
|
|
47932
|
+
emit("UI:STEP", { step });
|
|
45678
47933
|
if (step >= maxSlots) {
|
|
47934
|
+
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
45679
47935
|
const playerSeq = slots.map((s) => s?.id);
|
|
45680
|
-
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
45681
47936
|
const success = solutions.some(
|
|
45682
47937
|
(sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
|
|
45683
47938
|
);
|
|
45684
|
-
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
45685
47939
|
if (success) {
|
|
45686
|
-
setPlayState("success");
|
|
45687
|
-
setCurrentStep(-1);
|
|
45688
47940
|
emit("UI:PLAY_SOUND", { key: "levelComplete" });
|
|
45689
|
-
if (completeEvent) {
|
|
45690
|
-
emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
45691
|
-
}
|
|
47941
|
+
if (completeEvent) emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
45692
47942
|
} else {
|
|
45693
|
-
setAttempts((prev) => prev + 1);
|
|
45694
47943
|
const feedback = computeSlotFeedback(playerSeq, solutions);
|
|
45695
47944
|
setSlotFeedback(feedback);
|
|
45696
|
-
setPlayState("idle");
|
|
45697
|
-
setCurrentStep(-1);
|
|
45698
47945
|
emit("UI:PLAY_SOUND", { key: "fail" });
|
|
45699
47946
|
const correctCount2 = feedback.filter((f3) => f3 === "correct").length;
|
|
45700
47947
|
for (let ci = 0; ci < correctCount2; ci++) {
|
|
@@ -45704,7 +47951,6 @@ function SequencerBoard({
|
|
|
45704
47951
|
}
|
|
45705
47952
|
}
|
|
45706
47953
|
} else {
|
|
45707
|
-
setCurrentStep(step);
|
|
45708
47954
|
timerRef.current = setTimeout(advance, stepDurationMs);
|
|
45709
47955
|
}
|
|
45710
47956
|
};
|
|
@@ -45754,7 +48000,7 @@ function SequencerBoard({
|
|
|
45754
48000
|
/* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
|
|
45755
48001
|
/* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between", children: [
|
|
45756
48002
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
|
|
45757
|
-
hasFeedback &&
|
|
48003
|
+
hasFeedback && !isPlayingBack && !isSuccess && /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
|
|
45758
48004
|
`${correctCount}/${maxSlots} `,
|
|
45759
48005
|
"\u2705"
|
|
45760
48006
|
] })
|
|
@@ -45766,7 +48012,7 @@ function SequencerBoard({
|
|
|
45766
48012
|
maxSlots,
|
|
45767
48013
|
onSlotDrop: handleSlotDrop,
|
|
45768
48014
|
onSlotRemove: handleSlotRemove,
|
|
45769
|
-
playing:
|
|
48015
|
+
playing: isPlayingBack,
|
|
45770
48016
|
currentStep,
|
|
45771
48017
|
categoryColors,
|
|
45772
48018
|
slotFeedback,
|
|
@@ -45774,7 +48020,7 @@ function SequencerBoard({
|
|
|
45774
48020
|
}
|
|
45775
48021
|
)
|
|
45776
48022
|
] }),
|
|
45777
|
-
|
|
48023
|
+
!isPlayingBack && /* @__PURE__ */ jsx(
|
|
45778
48024
|
ActionPalette,
|
|
45779
48025
|
{
|
|
45780
48026
|
actions: availableActions,
|
|
@@ -45784,8 +48030,8 @@ function SequencerBoard({
|
|
|
45784
48030
|
label: t("sequencer.dragActions")
|
|
45785
48031
|
}
|
|
45786
48032
|
),
|
|
45787
|
-
hasFeedback &&
|
|
45788
|
-
|
|
48033
|
+
hasFeedback && !isPlayingBack && !isSuccess && attempts > 0 && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: t(encourageKey) }) }),
|
|
48034
|
+
isSuccess && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("sequencer.levelComplete") }) }),
|
|
45789
48035
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
|
|
45790
48036
|
/* @__PURE__ */ jsx(
|
|
45791
48037
|
Button,
|
|
@@ -46852,9 +49098,9 @@ function VariablePanel({
|
|
|
46852
49098
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
|
|
46853
49099
|
variables.map((v) => {
|
|
46854
49100
|
const name = v.name == null ? "" : String(v.name);
|
|
46855
|
-
const value =
|
|
46856
|
-
const max =
|
|
46857
|
-
const min =
|
|
49101
|
+
const value = numField2(v.value);
|
|
49102
|
+
const max = numField2(v.max, 100);
|
|
49103
|
+
const min = numField2(v.min, 0);
|
|
46858
49104
|
const unit = v.unit == null ? "" : String(v.unit);
|
|
46859
49105
|
const pct = Math.round((value - min) / (max - min) * 100);
|
|
46860
49106
|
const isHigh = pct > 80;
|
|
@@ -46884,12 +49130,12 @@ function VariablePanel({
|
|
|
46884
49130
|
})
|
|
46885
49131
|
] });
|
|
46886
49132
|
}
|
|
46887
|
-
var
|
|
49133
|
+
var numField2;
|
|
46888
49134
|
var init_VariablePanel = __esm({
|
|
46889
49135
|
"components/game/organisms/puzzles/state-architect/VariablePanel.tsx"() {
|
|
46890
49136
|
init_atoms2();
|
|
46891
49137
|
init_cn();
|
|
46892
|
-
|
|
49138
|
+
numField2 = (v, fallback = 0) => {
|
|
46893
49139
|
const n = Number(v);
|
|
46894
49140
|
return Number.isFinite(n) ? n : fallback;
|
|
46895
49141
|
};
|
|
@@ -46946,6 +49192,9 @@ function StateArchitectBoard({
|
|
|
46946
49192
|
stepDurationMs = 600,
|
|
46947
49193
|
testEvent,
|
|
46948
49194
|
completeEvent,
|
|
49195
|
+
addTransitionEvent,
|
|
49196
|
+
removeTransitionEvent,
|
|
49197
|
+
playAgainEvent,
|
|
46949
49198
|
className
|
|
46950
49199
|
}) {
|
|
46951
49200
|
const { emit } = useEventBus();
|
|
@@ -46958,14 +49207,16 @@ function StateArchitectBoard({
|
|
|
46958
49207
|
const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
|
|
46959
49208
|
const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
|
|
46960
49209
|
const entityVariables = rows(resolved?.variables);
|
|
46961
|
-
const
|
|
49210
|
+
const transitions = entityTransitions;
|
|
49211
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
49212
|
+
const entityResult = str(resolved?.result) || "none";
|
|
49213
|
+
const isSuccess = entityResult === "win";
|
|
49214
|
+
const [isTesting, setIsTesting] = useState(false);
|
|
46962
49215
|
const [headerError, setHeaderError] = useState(false);
|
|
46963
|
-
const [playState, setPlayState] = useState("editing");
|
|
46964
49216
|
const [currentState, setCurrentState] = useState(initialState);
|
|
46965
49217
|
const [selectedState, setSelectedState] = useState(null);
|
|
46966
49218
|
const [testResults, setTestResults] = useState([]);
|
|
46967
49219
|
const [variables, setVariables] = useState(() => [...entityVariables]);
|
|
46968
|
-
const [attempts, setAttempts] = useState(0);
|
|
46969
49220
|
const timerRef = useRef(null);
|
|
46970
49221
|
const [addingFrom, setAddingFrom] = useState(null);
|
|
46971
49222
|
useEffect(() => () => {
|
|
@@ -46975,7 +49226,7 @@ function StateArchitectBoard({
|
|
|
46975
49226
|
const GRAPH_H = 400;
|
|
46976
49227
|
const positions = useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
|
|
46977
49228
|
const handleStateClick = useCallback((state) => {
|
|
46978
|
-
if (
|
|
49229
|
+
if (isTesting) return;
|
|
46979
49230
|
if (addingFrom) {
|
|
46980
49231
|
if (addingFrom !== state) {
|
|
46981
49232
|
const event = availableEvents[0] || "EVENT";
|
|
@@ -46985,20 +49236,20 @@ function StateArchitectBoard({
|
|
|
46985
49236
|
to: state,
|
|
46986
49237
|
event
|
|
46987
49238
|
};
|
|
46988
|
-
|
|
49239
|
+
if (addTransitionEvent) emit(`UI:${addTransitionEvent}`, { id: newTrans.id, from: newTrans.from, to: newTrans.to, event: newTrans.event });
|
|
46989
49240
|
}
|
|
46990
49241
|
setAddingFrom(null);
|
|
46991
49242
|
} else {
|
|
46992
49243
|
setSelectedState(state);
|
|
46993
49244
|
}
|
|
46994
|
-
}, [
|
|
49245
|
+
}, [isTesting, addingFrom, availableEvents, addTransitionEvent, emit]);
|
|
46995
49246
|
const handleStartAddTransition = useCallback(() => {
|
|
46996
49247
|
if (!selectedState) return;
|
|
46997
49248
|
setAddingFrom(selectedState);
|
|
46998
49249
|
}, [selectedState]);
|
|
46999
49250
|
const handleRemoveTransition = useCallback((transId) => {
|
|
47000
|
-
|
|
47001
|
-
}, []);
|
|
49251
|
+
if (removeTransitionEvent) emit(`UI:${removeTransitionEvent}`, { id: transId });
|
|
49252
|
+
}, [removeTransitionEvent, emit]);
|
|
47002
49253
|
const machine = useMemo(() => ({
|
|
47003
49254
|
name: entityName,
|
|
47004
49255
|
description: str(resolved?.description),
|
|
@@ -47012,16 +49263,16 @@ function StateArchitectBoard({
|
|
|
47012
49263
|
}))
|
|
47013
49264
|
}), [entityName, resolved, entityStates, currentState, transitions]);
|
|
47014
49265
|
const handleTest = useCallback(() => {
|
|
47015
|
-
if (
|
|
49266
|
+
if (isTesting) return;
|
|
47016
49267
|
if (testEvent) emit(`UI:${testEvent}`, {});
|
|
47017
|
-
|
|
49268
|
+
setIsTesting(true);
|
|
47018
49269
|
setTestResults([]);
|
|
47019
49270
|
const results = [];
|
|
47020
49271
|
let testIdx = 0;
|
|
47021
49272
|
const runNextTest = () => {
|
|
47022
49273
|
if (testIdx >= testCases.length) {
|
|
47023
49274
|
const allPassed = results.every((r) => r.passed);
|
|
47024
|
-
|
|
49275
|
+
setIsTesting(false);
|
|
47025
49276
|
setTestResults(results);
|
|
47026
49277
|
if (allPassed && completeEvent) {
|
|
47027
49278
|
emit(`UI:${completeEvent}`, {
|
|
@@ -47029,9 +49280,6 @@ function StateArchitectBoard({
|
|
|
47029
49280
|
passedTests: results.filter((r) => r.passed).length
|
|
47030
49281
|
});
|
|
47031
49282
|
}
|
|
47032
|
-
if (!allPassed) {
|
|
47033
|
-
setAttempts((prev) => prev + 1);
|
|
47034
|
-
}
|
|
47035
49283
|
return;
|
|
47036
49284
|
}
|
|
47037
49285
|
const testCase = testCases[testIdx];
|
|
@@ -47054,24 +49302,23 @@ function StateArchitectBoard({
|
|
|
47054
49302
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
47055
49303
|
};
|
|
47056
49304
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
47057
|
-
}, [
|
|
49305
|
+
}, [isTesting, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
|
|
47058
49306
|
const handleTryAgain = useCallback(() => {
|
|
47059
49307
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
47060
|
-
|
|
49308
|
+
setIsTesting(false);
|
|
47061
49309
|
setCurrentState(initialState);
|
|
47062
49310
|
setTestResults([]);
|
|
47063
49311
|
}, [initialState]);
|
|
47064
49312
|
const handleReset = useCallback(() => {
|
|
47065
49313
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
47066
|
-
|
|
47067
|
-
setPlayState("editing");
|
|
49314
|
+
setIsTesting(false);
|
|
47068
49315
|
setCurrentState(initialState);
|
|
47069
49316
|
setTestResults([]);
|
|
47070
49317
|
setVariables([...entityVariables]);
|
|
47071
49318
|
setSelectedState(null);
|
|
47072
49319
|
setAddingFrom(null);
|
|
47073
|
-
|
|
47074
|
-
}, [
|
|
49320
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
49321
|
+
}, [initialState, entityVariables, playAgainEvent, emit]);
|
|
47075
49322
|
const codeData = useMemo(() => ({
|
|
47076
49323
|
name: entityName,
|
|
47077
49324
|
states: entityStates,
|
|
@@ -47167,7 +49414,7 @@ function StateArchitectBoard({
|
|
|
47167
49414
|
]
|
|
47168
49415
|
}
|
|
47169
49416
|
),
|
|
47170
|
-
|
|
49417
|
+
!isTesting && /* @__PURE__ */ jsx(HStack, { gap: "sm", children: /* @__PURE__ */ jsx(
|
|
47171
49418
|
Button,
|
|
47172
49419
|
{
|
|
47173
49420
|
variant: "ghost",
|
|
@@ -47189,7 +49436,7 @@ function StateArchitectBoard({
|
|
|
47189
49436
|
t2.guardHint,
|
|
47190
49437
|
")"
|
|
47191
49438
|
] }),
|
|
47192
|
-
|
|
49439
|
+
!isTesting && /* @__PURE__ */ jsx(
|
|
47193
49440
|
Button,
|
|
47194
49441
|
{
|
|
47195
49442
|
variant: "ghost",
|
|
@@ -47221,21 +49468,21 @@ function StateArchitectBoard({
|
|
|
47221
49468
|
resolved.showCodeView !== false && /* @__PURE__ */ jsx(StateJsonView, { data: codeData, label: "View Code" })
|
|
47222
49469
|
] })
|
|
47223
49470
|
] }),
|
|
47224
|
-
|
|
47225
|
-
|
|
49471
|
+
isSuccess && /* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-success/20 border border-success text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "h5", className: "text-success", children: str(resolved.successMessage) || t("stateArchitect.allPassed") }) }),
|
|
49472
|
+
!isTesting && !isSuccess && testResults.some((r) => !r.passed) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
47226
49473
|
/* @__PURE__ */ jsx(Box, { className: "p-4 rounded-container bg-warning/10 border border-warning/30 text-center", children: /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-foreground font-medium", children: t(ENCOURAGEMENT_KEYS3[Math.min(attempts - 1, ENCOURAGEMENT_KEYS3.length - 1)] ?? ENCOURAGEMENT_KEYS3[0]) }) }),
|
|
47227
|
-
attempts >=
|
|
49474
|
+
!isSuccess && attempts >= 2 && Boolean(str(resolved?.hint)) && /* @__PURE__ */ jsx(Box, { className: "p-3 rounded-container bg-accent/10 border border-accent/30", children: /* @__PURE__ */ jsxs(HStack, { className: "items-start", gap: "xs", children: [
|
|
47228
49475
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
47229
49476
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
|
|
47230
49477
|
] }) })
|
|
47231
49478
|
] }),
|
|
47232
49479
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
|
|
47233
|
-
|
|
49480
|
+
!isTesting && !isSuccess && testResults.some((r) => !r.passed) ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsx(
|
|
47234
49481
|
Button,
|
|
47235
49482
|
{
|
|
47236
49483
|
variant: "primary",
|
|
47237
49484
|
onClick: handleTest,
|
|
47238
|
-
disabled:
|
|
49485
|
+
disabled: isTesting,
|
|
47239
49486
|
children: "\u25B6 " + t("game.runTests")
|
|
47240
49487
|
}
|
|
47241
49488
|
),
|
|
@@ -47315,6 +49562,7 @@ function formatDuration(seconds) {
|
|
|
47315
49562
|
return `${Math.round(seconds)}s`;
|
|
47316
49563
|
}
|
|
47317
49564
|
function StatusEffect({
|
|
49565
|
+
assetUrl = DEFAULT_ASSET_URL13,
|
|
47318
49566
|
icon = "shield",
|
|
47319
49567
|
label = "Shield",
|
|
47320
49568
|
duration = 30,
|
|
@@ -47335,7 +49583,17 @@ function StatusEffect({
|
|
|
47335
49583
|
),
|
|
47336
49584
|
title: label,
|
|
47337
49585
|
children: [
|
|
47338
|
-
/* @__PURE__ */ jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children:
|
|
49586
|
+
/* @__PURE__ */ jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children: assetUrl ? /* @__PURE__ */ jsx(
|
|
49587
|
+
"img",
|
|
49588
|
+
{
|
|
49589
|
+
src: assetUrl,
|
|
49590
|
+
alt: label,
|
|
49591
|
+
width: sizes.img,
|
|
49592
|
+
height: sizes.img,
|
|
49593
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
49594
|
+
className: "flex-shrink-0"
|
|
49595
|
+
}
|
|
49596
|
+
) : typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, size: "sm" }) : /* @__PURE__ */ jsx(Icon, { icon, size: "sm" }) }),
|
|
47339
49597
|
duration !== void 0 && /* @__PURE__ */ jsx(
|
|
47340
49598
|
"span",
|
|
47341
49599
|
{
|
|
@@ -47362,15 +49620,16 @@ function StatusEffect({
|
|
|
47362
49620
|
label && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground mt-0.5 text-center whitespace-nowrap", children: label })
|
|
47363
49621
|
] });
|
|
47364
49622
|
}
|
|
47365
|
-
var sizeMap16, variantStyles9;
|
|
49623
|
+
var DEFAULT_ASSET_URL13, sizeMap16, variantStyles9;
|
|
47366
49624
|
var init_StatusEffect = __esm({
|
|
47367
49625
|
"components/game/atoms/StatusEffect.tsx"() {
|
|
47368
49626
|
init_cn();
|
|
47369
49627
|
init_Icon();
|
|
49628
|
+
DEFAULT_ASSET_URL13 = "https://almadar-kflow-assets.web.app/shared/effects/particles/flame_01.png";
|
|
47370
49629
|
sizeMap16 = {
|
|
47371
|
-
sm: { container: "w-8 h-8", icon: "text-sm", badge: "text-xs -top-1 -right-1 w-4 h-4", timer: "text-[9px]" },
|
|
47372
|
-
md: { container: "w-10 h-10", icon: "text-base", badge: "text-xs -top-1 -right-1 w-5 h-5", timer: "text-xs" },
|
|
47373
|
-
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" }
|
|
49630
|
+
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 },
|
|
49631
|
+
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 },
|
|
49632
|
+
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 }
|
|
47374
49633
|
};
|
|
47375
49634
|
variantStyles9 = {
|
|
47376
49635
|
buff: "border-success bg-success/20",
|
|
@@ -47863,6 +50122,289 @@ var init_ToastSlot = __esm({
|
|
|
47863
50122
|
ToastSlot.displayName = "ToastSlot";
|
|
47864
50123
|
}
|
|
47865
50124
|
});
|
|
50125
|
+
function tilesToIso(tiles) {
|
|
50126
|
+
return tiles.map((t) => ({
|
|
50127
|
+
x: t.x,
|
|
50128
|
+
y: t.y,
|
|
50129
|
+
terrain: t.terrain,
|
|
50130
|
+
terrainSprite: t.terrainSprite ?? TERRAIN_SPRITES[t.terrain ?? "ground"],
|
|
50131
|
+
passable: t.passable
|
|
50132
|
+
}));
|
|
50133
|
+
}
|
|
50134
|
+
function towersToUnits(towers) {
|
|
50135
|
+
return towers.map((t) => ({
|
|
50136
|
+
id: t.id,
|
|
50137
|
+
position: { x: t.x, y: t.y },
|
|
50138
|
+
name: "Tower",
|
|
50139
|
+
team: "player",
|
|
50140
|
+
sprite: TOWER_SPRITE,
|
|
50141
|
+
unitType: "guardian",
|
|
50142
|
+
health: 1,
|
|
50143
|
+
maxHealth: 1
|
|
50144
|
+
}));
|
|
50145
|
+
}
|
|
50146
|
+
function creepsToUnits(creeps) {
|
|
50147
|
+
return creeps.map((c) => ({
|
|
50148
|
+
id: c.id,
|
|
50149
|
+
position: { x: c.x, y: c.y },
|
|
50150
|
+
name: "Creep",
|
|
50151
|
+
team: "enemy",
|
|
50152
|
+
sprite: CREEP_SPRITE,
|
|
50153
|
+
unitType: "scrapper",
|
|
50154
|
+
health: c.hp,
|
|
50155
|
+
maxHealth: c.maxHp
|
|
50156
|
+
}));
|
|
50157
|
+
}
|
|
50158
|
+
function pathToFeatures(path) {
|
|
50159
|
+
return path.map((p2, i) => ({
|
|
50160
|
+
id: `path-${i}`,
|
|
50161
|
+
x: p2.x,
|
|
50162
|
+
y: p2.y,
|
|
50163
|
+
type: "path-marker",
|
|
50164
|
+
sprite: `${CDN6}world-map/road_straight.png`
|
|
50165
|
+
}));
|
|
50166
|
+
}
|
|
50167
|
+
function TowerDefenseBoard({
|
|
50168
|
+
entity,
|
|
50169
|
+
tiles: propTiles,
|
|
50170
|
+
path: propPath,
|
|
50171
|
+
towers: propTowers,
|
|
50172
|
+
creeps: propCreeps,
|
|
50173
|
+
gold: propGold,
|
|
50174
|
+
lives: propLives,
|
|
50175
|
+
wave: propWave,
|
|
50176
|
+
maxWaves: propMaxWaves,
|
|
50177
|
+
result: propResult,
|
|
50178
|
+
waveActive: propWaveActive,
|
|
50179
|
+
towerCost = 25,
|
|
50180
|
+
scale = 0.45,
|
|
50181
|
+
placeTowerEvent,
|
|
50182
|
+
startWaveEvent,
|
|
50183
|
+
playAgainEvent,
|
|
50184
|
+
gameEndEvent,
|
|
50185
|
+
className
|
|
50186
|
+
}) {
|
|
50187
|
+
const board = boardEntity(entity) ?? {};
|
|
50188
|
+
const eventBus = useEventBus();
|
|
50189
|
+
const { t } = useTranslate();
|
|
50190
|
+
const tiles = propTiles ?? rows(board.tiles);
|
|
50191
|
+
const path = propPath ?? rows(board.path);
|
|
50192
|
+
const towers = propTowers ?? rows(board.towers);
|
|
50193
|
+
const creeps = propCreeps ?? rows(board.creeps);
|
|
50194
|
+
const gold = propGold ?? num(board.gold, 100);
|
|
50195
|
+
const lives = propLives ?? num(board.lives, 20);
|
|
50196
|
+
const wave = propWave ?? num(board.wave, 1);
|
|
50197
|
+
const maxWaves = propMaxWaves ?? num(board.maxWaves, 5);
|
|
50198
|
+
const result = propResult ?? (str(board.result) || "none");
|
|
50199
|
+
const waveActive = propWaveActive ?? Boolean(board.waveActive);
|
|
50200
|
+
const [hoveredTile, setHoveredTile] = useState(null);
|
|
50201
|
+
const emittedGameEnd = useRef(false);
|
|
50202
|
+
if (result !== "none" && !emittedGameEnd.current) {
|
|
50203
|
+
emittedGameEnd.current = true;
|
|
50204
|
+
if (gameEndEvent) {
|
|
50205
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
50206
|
+
}
|
|
50207
|
+
}
|
|
50208
|
+
if (result === "none") {
|
|
50209
|
+
emittedGameEnd.current = false;
|
|
50210
|
+
}
|
|
50211
|
+
const towerPositions = useMemo(() => new Set(towers.map((t2) => `${t2.x},${t2.y}`)), [towers]);
|
|
50212
|
+
const pathPositions = useMemo(() => new Set(path.map((p2) => `${p2.x},${p2.y}`)), [path]);
|
|
50213
|
+
const validMoves = useMemo(() => {
|
|
50214
|
+
if (result !== "none" || gold < towerCost) return [];
|
|
50215
|
+
return tiles.filter(
|
|
50216
|
+
(t2) => t2.passable !== false && !towerPositions.has(`${t2.x},${t2.y}`) && !pathPositions.has(`${t2.x},${t2.y}`)
|
|
50217
|
+
).map((t2) => ({ x: t2.x, y: t2.y }));
|
|
50218
|
+
}, [tiles, towerPositions, pathPositions, result, gold, towerCost]);
|
|
50219
|
+
const isoTiles = useMemo(() => tilesToIso(tiles), [tiles]);
|
|
50220
|
+
const towerUnits = useMemo(() => towersToUnits(towers), [towers]);
|
|
50221
|
+
const creepUnits = useMemo(() => creepsToUnits(creeps), [creeps]);
|
|
50222
|
+
const isoUnits = useMemo(() => [...towerUnits, ...creepUnits], [towerUnits, creepUnits]);
|
|
50223
|
+
const pathFeatures = useMemo(() => pathToFeatures(path), [path]);
|
|
50224
|
+
const handleTileClick = useCallback((x, y) => {
|
|
50225
|
+
if (result !== "none") return;
|
|
50226
|
+
if (pathPositions.has(`${x},${y}`)) return;
|
|
50227
|
+
if (towerPositions.has(`${x},${y}`)) return;
|
|
50228
|
+
if (gold < towerCost) return;
|
|
50229
|
+
if (placeTowerEvent) {
|
|
50230
|
+
eventBus.emit(`UI:${placeTowerEvent}`, { x, y });
|
|
50231
|
+
}
|
|
50232
|
+
}, [result, pathPositions, towerPositions, gold, towerCost, placeTowerEvent, eventBus]);
|
|
50233
|
+
const handleStartWave = useCallback(() => {
|
|
50234
|
+
if (startWaveEvent) {
|
|
50235
|
+
eventBus.emit(`UI:${startWaveEvent}`, { wave });
|
|
50236
|
+
}
|
|
50237
|
+
}, [startWaveEvent, wave, eventBus]);
|
|
50238
|
+
const handlePlayAgain = useCallback(() => {
|
|
50239
|
+
if (playAgainEvent) {
|
|
50240
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
50241
|
+
}
|
|
50242
|
+
}, [playAgainEvent, eventBus]);
|
|
50243
|
+
const canPlaceTower = gold >= towerCost && result === "none";
|
|
50244
|
+
const canStartWave = !waveActive && result === "none";
|
|
50245
|
+
return /* @__PURE__ */ jsxs(VStack, { className: cn("tower-defense-board relative min-h-[600px] bg-background", className), gap: "none", children: [
|
|
50246
|
+
/* @__PURE__ */ jsxs(
|
|
50247
|
+
HStack,
|
|
50248
|
+
{
|
|
50249
|
+
className: "px-4 py-2 border-b border-border bg-surface",
|
|
50250
|
+
gap: "lg",
|
|
50251
|
+
align: "center",
|
|
50252
|
+
children: [
|
|
50253
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
50254
|
+
/* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("td.gold") ?? "Gold" }),
|
|
50255
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", className: "text-warning", children: gold })
|
|
50256
|
+
] }),
|
|
50257
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
50258
|
+
/* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("td.lives") ?? "Lives" }),
|
|
50259
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", className: lives <= 5 ? "text-error" : "text-success", children: lives })
|
|
50260
|
+
] }),
|
|
50261
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
50262
|
+
/* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("td.wave") ?? "Wave" }),
|
|
50263
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "body2", weight: "bold", children: [
|
|
50264
|
+
wave,
|
|
50265
|
+
" / ",
|
|
50266
|
+
maxWaves
|
|
50267
|
+
] })
|
|
50268
|
+
] }),
|
|
50269
|
+
/* @__PURE__ */ jsxs(Box, { className: "ml-auto", children: [
|
|
50270
|
+
canStartWave && /* @__PURE__ */ jsx(Button, { variant: "primary", size: "sm", onClick: handleStartWave, children: t("td.startWave") ?? "Start Wave" }),
|
|
50271
|
+
waveActive && /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-warning animate-pulse", children: t("td.waveInProgress") ?? "Wave in progress\u2026" })
|
|
50272
|
+
] })
|
|
50273
|
+
]
|
|
50274
|
+
}
|
|
50275
|
+
),
|
|
50276
|
+
/* @__PURE__ */ jsxs(Box, { className: "relative flex-1", children: [
|
|
50277
|
+
/* @__PURE__ */ jsx(
|
|
50278
|
+
IsometricCanvas_default,
|
|
50279
|
+
{
|
|
50280
|
+
tiles: isoTiles,
|
|
50281
|
+
units: isoUnits,
|
|
50282
|
+
features: pathFeatures,
|
|
50283
|
+
validMoves,
|
|
50284
|
+
hoveredTile,
|
|
50285
|
+
onTileClick: handleTileClick,
|
|
50286
|
+
onTileHover: (x, y) => setHoveredTile({ x, y }),
|
|
50287
|
+
onTileLeave: () => setHoveredTile(null),
|
|
50288
|
+
scale
|
|
50289
|
+
}
|
|
50290
|
+
),
|
|
50291
|
+
hoveredTile && canPlaceTower && validMoves.some((m) => m.x === hoveredTile.x && m.y === hoveredTile.y) && /* @__PURE__ */ jsx(Box, { className: "absolute bottom-4 left-1/2 -translate-x-1/2 z-10 bg-background/80 rounded px-3 py-1 backdrop-blur-sm pointer-events-none", children: /* @__PURE__ */ jsx(Typography, { variant: "small", children: t("td.placeTower") ?? `Place tower (${towerCost} gold)` }) })
|
|
50292
|
+
] }),
|
|
50293
|
+
result !== "none" && /* @__PURE__ */ jsx(Box, { className: "absolute inset-0 z-50 flex items-center justify-center bg-background/70 backdrop-blur-sm", children: /* @__PURE__ */ jsxs(VStack, { className: "text-center p-8", gap: "lg", children: [
|
|
50294
|
+
/* @__PURE__ */ jsx(
|
|
50295
|
+
Typography,
|
|
50296
|
+
{
|
|
50297
|
+
variant: "h2",
|
|
50298
|
+
className: cn(
|
|
50299
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
50300
|
+
result === "won" ? "text-warning" : "text-error"
|
|
50301
|
+
),
|
|
50302
|
+
children: result === "won" ? t("td.victory") ?? "Victory!" : t("td.defeat") ?? "Defeat!"
|
|
50303
|
+
}
|
|
50304
|
+
),
|
|
50305
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: result === "won" ? t("td.survivedAllWaves") ?? `Survived all ${maxWaves} waves!` : t("td.livesReachedZero") ?? "Your base was overrun." }),
|
|
50306
|
+
/* @__PURE__ */ jsx(
|
|
50307
|
+
Button,
|
|
50308
|
+
{
|
|
50309
|
+
variant: "primary",
|
|
50310
|
+
className: "px-8 py-3 font-semibold",
|
|
50311
|
+
onClick: handlePlayAgain,
|
|
50312
|
+
children: t("td.playAgain") ?? "Play Again"
|
|
50313
|
+
}
|
|
50314
|
+
)
|
|
50315
|
+
] }) })
|
|
50316
|
+
] });
|
|
50317
|
+
}
|
|
50318
|
+
var CDN6, TERRAIN_SPRITES, TOWER_SPRITE, CREEP_SPRITE;
|
|
50319
|
+
var init_TowerDefenseBoard = __esm({
|
|
50320
|
+
"components/game/organisms/TowerDefenseBoard.tsx"() {
|
|
50321
|
+
"use client";
|
|
50322
|
+
init_cn();
|
|
50323
|
+
init_useEventBus();
|
|
50324
|
+
init_Box();
|
|
50325
|
+
init_Button();
|
|
50326
|
+
init_Typography();
|
|
50327
|
+
init_Stack();
|
|
50328
|
+
init_IsometricCanvas();
|
|
50329
|
+
init_boardEntity();
|
|
50330
|
+
CDN6 = "https://almadar-kflow-assets.web.app/shared/";
|
|
50331
|
+
TERRAIN_SPRITES = {
|
|
50332
|
+
ground: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_01.png`,
|
|
50333
|
+
path: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_09.png`,
|
|
50334
|
+
wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`
|
|
50335
|
+
};
|
|
50336
|
+
TOWER_SPRITE = `${CDN6}units/guardian.png`;
|
|
50337
|
+
CREEP_SPRITE = `${CDN6}units/scrapper.png`;
|
|
50338
|
+
TowerDefenseBoard.displayName = "TowerDefenseBoard";
|
|
50339
|
+
}
|
|
50340
|
+
});
|
|
50341
|
+
function TowerDefenseTemplate({
|
|
50342
|
+
entity,
|
|
50343
|
+
title = "Tower Defense",
|
|
50344
|
+
tiles,
|
|
50345
|
+
path,
|
|
50346
|
+
towers,
|
|
50347
|
+
gold,
|
|
50348
|
+
lives,
|
|
50349
|
+
maxWaves,
|
|
50350
|
+
towerCost,
|
|
50351
|
+
scale,
|
|
50352
|
+
placeTowerEvent,
|
|
50353
|
+
startWaveEvent,
|
|
50354
|
+
playAgainEvent,
|
|
50355
|
+
gameEndEvent,
|
|
50356
|
+
className
|
|
50357
|
+
}) {
|
|
50358
|
+
const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
|
|
50359
|
+
return /* @__PURE__ */ jsxs(
|
|
50360
|
+
Box,
|
|
50361
|
+
{
|
|
50362
|
+
display: "flex",
|
|
50363
|
+
fullHeight: true,
|
|
50364
|
+
className: cn("tower-defense-template flex-col", className),
|
|
50365
|
+
children: [
|
|
50366
|
+
/* @__PURE__ */ jsx(
|
|
50367
|
+
HStack,
|
|
50368
|
+
{
|
|
50369
|
+
gap: "sm",
|
|
50370
|
+
align: "center",
|
|
50371
|
+
className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
|
|
50372
|
+
children: /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title })
|
|
50373
|
+
}
|
|
50374
|
+
),
|
|
50375
|
+
/* @__PURE__ */ jsx(Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
50376
|
+
TowerDefenseBoard,
|
|
50377
|
+
{
|
|
50378
|
+
entity: resolved,
|
|
50379
|
+
tiles,
|
|
50380
|
+
path,
|
|
50381
|
+
towers,
|
|
50382
|
+
gold,
|
|
50383
|
+
lives,
|
|
50384
|
+
maxWaves,
|
|
50385
|
+
towerCost,
|
|
50386
|
+
scale,
|
|
50387
|
+
placeTowerEvent,
|
|
50388
|
+
startWaveEvent,
|
|
50389
|
+
playAgainEvent,
|
|
50390
|
+
gameEndEvent,
|
|
50391
|
+
className: "h-full"
|
|
50392
|
+
}
|
|
50393
|
+
) })
|
|
50394
|
+
]
|
|
50395
|
+
}
|
|
50396
|
+
);
|
|
50397
|
+
}
|
|
50398
|
+
var init_TowerDefenseTemplate = __esm({
|
|
50399
|
+
"components/game/templates/TowerDefenseTemplate.tsx"() {
|
|
50400
|
+
init_cn();
|
|
50401
|
+
init_Box();
|
|
50402
|
+
init_Stack();
|
|
50403
|
+
init_Typography();
|
|
50404
|
+
init_TowerDefenseBoard();
|
|
50405
|
+
TowerDefenseTemplate.displayName = "TowerDefenseTemplate";
|
|
50406
|
+
}
|
|
50407
|
+
});
|
|
47866
50408
|
function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
|
|
47867
50409
|
const eventBus = useEventBus();
|
|
47868
50410
|
const {
|
|
@@ -48061,17 +50603,12 @@ var init_UncontrolledBattleBoard = __esm({
|
|
|
48061
50603
|
}
|
|
48062
50604
|
});
|
|
48063
50605
|
function heroPosition(h) {
|
|
50606
|
+
if ("position" in h && h.position != null) return h.position;
|
|
48064
50607
|
return vec2(h.position);
|
|
48065
50608
|
}
|
|
48066
|
-
function heroOwner(h) {
|
|
48067
|
-
return str(h.owner);
|
|
48068
|
-
}
|
|
48069
50609
|
function heroMovement(h) {
|
|
48070
50610
|
return num(h.movement);
|
|
48071
50611
|
}
|
|
48072
|
-
function hexPassable(h) {
|
|
48073
|
-
return h.passable !== false;
|
|
48074
|
-
}
|
|
48075
50612
|
function defaultIsInRange(from, to, range) {
|
|
48076
50613
|
return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
|
|
48077
50614
|
}
|
|
@@ -48107,38 +50644,41 @@ function WorldMapBoard({
|
|
|
48107
50644
|
}) {
|
|
48108
50645
|
const eventBus = useEventBus();
|
|
48109
50646
|
const resolved = boardEntity(entity);
|
|
48110
|
-
const
|
|
48111
|
-
const
|
|
50647
|
+
const entityUnits = rows(resolved?.units);
|
|
50648
|
+
const entityTiles = rows(resolved?.tiles);
|
|
48112
50649
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
48113
50650
|
const selectedHeroId = resolved?.selectedHeroId ?? null;
|
|
48114
50651
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
48115
50652
|
const backgroundImage = resolved?.backgroundImage;
|
|
48116
50653
|
const [hoveredTile, setHoveredTile] = useState(null);
|
|
48117
|
-
const selectedHero = useMemo(
|
|
48118
|
-
() => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
|
|
48119
|
-
[heroes, selectedHeroId]
|
|
48120
|
-
);
|
|
48121
50654
|
const derivedTiles = useMemo(
|
|
48122
|
-
() =>
|
|
48123
|
-
x: num(
|
|
48124
|
-
y: num(
|
|
48125
|
-
terrain: str(
|
|
48126
|
-
terrainSprite:
|
|
50655
|
+
() => entityTiles.map((t) => ({
|
|
50656
|
+
x: num(t.x),
|
|
50657
|
+
y: num(t.y),
|
|
50658
|
+
terrain: str(t.terrain),
|
|
50659
|
+
terrainSprite: t.terrainSprite == null ? void 0 : str(t.terrainSprite),
|
|
50660
|
+
passable: t.passable !== false
|
|
48127
50661
|
})),
|
|
48128
|
-
[
|
|
50662
|
+
[entityTiles]
|
|
48129
50663
|
);
|
|
48130
50664
|
const tiles = propTiles ?? derivedTiles;
|
|
48131
50665
|
const baseUnits = useMemo(
|
|
48132
|
-
() => propUnits ??
|
|
48133
|
-
id: str(
|
|
48134
|
-
position: heroPosition(
|
|
48135
|
-
name: str(
|
|
48136
|
-
|
|
48137
|
-
|
|
48138
|
-
|
|
48139
|
-
|
|
50666
|
+
() => propUnits ?? entityUnits.map((u) => ({
|
|
50667
|
+
id: str(u.id),
|
|
50668
|
+
position: heroPosition(u),
|
|
50669
|
+
name: str(u.name),
|
|
50670
|
+
// lolo uses `team` field (not `owner`)
|
|
50671
|
+
team: str(u.team) === "enemy" ? "enemy" : "player",
|
|
50672
|
+
health: num(u.health) || 100,
|
|
50673
|
+
maxHealth: num(u.maxHealth) || 100,
|
|
50674
|
+
sprite: u.sprite == null ? void 0 : str(u.sprite)
|
|
48140
50675
|
})),
|
|
48141
|
-
[
|
|
50676
|
+
[entityUnits, propUnits]
|
|
50677
|
+
);
|
|
50678
|
+
const gameUnits = baseUnits;
|
|
50679
|
+
const selectedHero = useMemo(
|
|
50680
|
+
() => gameUnits.find((u) => u.id === selectedHeroId) ?? null,
|
|
50681
|
+
[gameUnits, selectedHeroId]
|
|
48142
50682
|
);
|
|
48143
50683
|
const MOVE_SPEED_MS_PER_TILE = 300;
|
|
48144
50684
|
const movementAnimRef = useRef(null);
|
|
@@ -48184,51 +50724,50 @@ function WorldMapBoard({
|
|
|
48184
50724
|
const validMoves = useMemo(() => {
|
|
48185
50725
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
48186
50726
|
const sp = heroPosition(selectedHero);
|
|
48187
|
-
const
|
|
50727
|
+
const sTeam = str(selectedHero.team);
|
|
48188
50728
|
const range = heroMovement(selectedHero);
|
|
48189
50729
|
const moves = [];
|
|
48190
|
-
|
|
48191
|
-
const
|
|
48192
|
-
const
|
|
48193
|
-
if (
|
|
48194
|
-
if (
|
|
48195
|
-
if (!isInRange(sp, { x:
|
|
48196
|
-
if (
|
|
48197
|
-
const
|
|
48198
|
-
return
|
|
50730
|
+
tiles.forEach((t) => {
|
|
50731
|
+
const tx = t.x;
|
|
50732
|
+
const ty = t.y;
|
|
50733
|
+
if (t.passable === false) return;
|
|
50734
|
+
if (tx === sp.x && ty === sp.y) return;
|
|
50735
|
+
if (!isInRange(sp, { x: tx, y: ty }, range)) return;
|
|
50736
|
+
if (gameUnits.some((u) => {
|
|
50737
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
50738
|
+
return up.x === tx && up.y === ty && str(u.team) === sTeam;
|
|
48199
50739
|
})) return;
|
|
48200
|
-
moves.push({ x:
|
|
50740
|
+
moves.push({ x: tx, y: ty });
|
|
48201
50741
|
});
|
|
48202
50742
|
return moves;
|
|
48203
|
-
}, [selectedHero,
|
|
50743
|
+
}, [selectedHero, tiles, gameUnits, isInRange]);
|
|
48204
50744
|
const attackTargets = useMemo(() => {
|
|
48205
50745
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
48206
50746
|
const sp = heroPosition(selectedHero);
|
|
48207
|
-
const
|
|
50747
|
+
const sTeam = str(selectedHero.team);
|
|
48208
50748
|
const range = heroMovement(selectedHero);
|
|
48209
|
-
return
|
|
48210
|
-
}, [selectedHero,
|
|
48211
|
-
const maxY = Math.max(...
|
|
50749
|
+
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 });
|
|
50750
|
+
}, [selectedHero, gameUnits, isInRange]);
|
|
50751
|
+
const maxY = Math.max(...tiles.map((t) => t.y), 0);
|
|
48212
50752
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
48213
50753
|
const tileToScreen = useCallback(
|
|
48214
50754
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
48215
50755
|
[scale, baseOffsetX]
|
|
48216
50756
|
);
|
|
48217
50757
|
const hoveredHex = useMemo(
|
|
48218
|
-
() => hoveredTile ?
|
|
48219
|
-
[hoveredTile,
|
|
50758
|
+
() => hoveredTile ? tiles.find((t) => t.x === hoveredTile.x && t.y === hoveredTile.y) ?? null : null,
|
|
50759
|
+
[hoveredTile, tiles]
|
|
48220
50760
|
);
|
|
48221
50761
|
const hoveredHero = useMemo(
|
|
48222
|
-
() => hoveredTile ?
|
|
48223
|
-
const
|
|
48224
|
-
return
|
|
50762
|
+
() => hoveredTile ? gameUnits.find((u) => {
|
|
50763
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
50764
|
+
return up.x === hoveredTile.x && up.y === hoveredTile.y;
|
|
48225
50765
|
}) ?? null : null,
|
|
48226
|
-
[hoveredTile,
|
|
50766
|
+
[hoveredTile, gameUnits]
|
|
48227
50767
|
);
|
|
48228
50768
|
const handleTileClick = useCallback((x, y) => {
|
|
48229
50769
|
if (movementAnimRef.current) return;
|
|
48230
|
-
const
|
|
48231
|
-
if (!hex) return;
|
|
50770
|
+
const tile = tiles.find((t) => t.x === x && t.y === y);
|
|
48232
50771
|
if (tileClickEvent) {
|
|
48233
50772
|
eventBus.emit(`UI:${tileClickEvent}`, { x, y });
|
|
48234
50773
|
}
|
|
@@ -48239,38 +50778,39 @@ function WorldMapBoard({
|
|
|
48239
50778
|
if (heroMoveEvent) {
|
|
48240
50779
|
eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
|
|
48241
50780
|
}
|
|
48242
|
-
const feature = str(
|
|
50781
|
+
const feature = tile ? str(tile.feature) : "";
|
|
48243
50782
|
if (feature && feature !== "none") {
|
|
48244
|
-
|
|
50783
|
+
const tileRow = tile;
|
|
50784
|
+
onFeatureEnter?.(heroId, tileRow);
|
|
48245
50785
|
if (featureEnterEvent) {
|
|
48246
|
-
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
|
|
50786
|
+
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex: tileRow });
|
|
48247
50787
|
}
|
|
48248
50788
|
}
|
|
48249
50789
|
});
|
|
48250
50790
|
return;
|
|
48251
50791
|
}
|
|
48252
|
-
const enemy =
|
|
48253
|
-
const
|
|
48254
|
-
return
|
|
50792
|
+
const enemy = gameUnits.find((u) => {
|
|
50793
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
50794
|
+
return up.x === x && up.y === y && str(u.team) === "enemy";
|
|
48255
50795
|
});
|
|
48256
50796
|
if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
|
|
48257
50797
|
const attackerId = str(selectedHero.id);
|
|
48258
|
-
const defenderId =
|
|
50798
|
+
const defenderId = enemy.id;
|
|
48259
50799
|
onBattleEncounter?.(attackerId, defenderId);
|
|
48260
50800
|
if (battleEncounterEvent) {
|
|
48261
50801
|
eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
|
|
48262
50802
|
}
|
|
48263
50803
|
}
|
|
48264
|
-
}, [
|
|
50804
|
+
}, [tiles, gameUnits, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
|
|
48265
50805
|
const handleUnitClick = useCallback((unitId) => {
|
|
48266
|
-
const
|
|
48267
|
-
if (
|
|
50806
|
+
const unit = gameUnits.find((u) => u.id === unitId);
|
|
50807
|
+
if (unit && (str(unit.team) === "player" || allowMoveAllHeroes)) {
|
|
48268
50808
|
onHeroSelect?.(unitId);
|
|
48269
50809
|
if (heroSelectEvent) {
|
|
48270
50810
|
eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
|
|
48271
50811
|
}
|
|
48272
50812
|
}
|
|
48273
|
-
}, [
|
|
50813
|
+
}, [gameUnits, onHeroSelect, allowMoveAllHeroes, eventBus, heroSelectEvent]);
|
|
48274
50814
|
const selectHero = useCallback((id) => {
|
|
48275
50815
|
onHeroSelect?.(id);
|
|
48276
50816
|
if (heroSelectEvent) {
|
|
@@ -48374,48 +50914,48 @@ function WorldMapTemplate({
|
|
|
48374
50914
|
}
|
|
48375
50915
|
);
|
|
48376
50916
|
}
|
|
48377
|
-
var
|
|
50917
|
+
var CDN7, DEFAULT_WORLDMAP_TILES, DEFAULT_WORLDMAP_UNITS, DEFAULT_WORLDMAP_FEATURES, DEFAULT_WORLDMAP_MANIFEST;
|
|
48378
50918
|
var init_WorldMapTemplate = __esm({
|
|
48379
50919
|
"components/game/templates/WorldMapTemplate.tsx"() {
|
|
48380
50920
|
init_WorldMapBoard();
|
|
48381
|
-
|
|
50921
|
+
CDN7 = "https://almadar-kflow-assets.web.app/shared";
|
|
48382
50922
|
DEFAULT_WORLDMAP_TILES = [
|
|
48383
|
-
{ x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
48384
|
-
{ x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
48385
|
-
{ x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${
|
|
48386
|
-
{ x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
48387
|
-
{ x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
48388
|
-
{ x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${
|
|
48389
|
-
{ x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
48390
|
-
{ x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
48391
|
-
{ x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
48392
|
-
{ x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${
|
|
48393
|
-
{ x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${
|
|
48394
|
-
{ x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
48395
|
-
{ x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
48396
|
-
{ x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
48397
|
-
{ x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${
|
|
48398
|
-
{ x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${
|
|
48399
|
-
{ x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
48400
|
-
{ x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
48401
|
-
{ x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
48402
|
-
{ x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${
|
|
48403
|
-
{ x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
48404
|
-
{ x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${
|
|
48405
|
-
{ x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${
|
|
48406
|
-
{ x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
48407
|
-
{ x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
50923
|
+
{ x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50924
|
+
{ x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50925
|
+
{ x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
50926
|
+
{ x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50927
|
+
{ x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50928
|
+
{ x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50929
|
+
{ x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50930
|
+
{ x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50931
|
+
{ x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50932
|
+
{ x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
50933
|
+
{ x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
50934
|
+
{ x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50935
|
+
{ x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
|
|
50936
|
+
{ x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50937
|
+
{ x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50938
|
+
{ x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50939
|
+
{ x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50940
|
+
{ x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50941
|
+
{ x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
|
|
50942
|
+
{ x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50943
|
+
{ x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50944
|
+
{ x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
50945
|
+
{ x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
50946
|
+
{ x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
50947
|
+
{ x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` }
|
|
48408
50948
|
];
|
|
48409
50949
|
DEFAULT_WORLDMAP_UNITS = [
|
|
48410
|
-
{ id: "h1", position: { x: 1, y: 1 }, unitType: "hero", name: "Amir", team: "player", health: 10, maxHealth: 10, sprite: `${
|
|
48411
|
-
{ id: "h2", position: { x: 3, y: 3 }, unitType: "scout", name: "Archivist", team: "player", health: 10, maxHealth: 10, sprite: `${
|
|
50950
|
+
{ 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` },
|
|
50951
|
+
{ 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` }
|
|
48412
50952
|
];
|
|
48413
50953
|
DEFAULT_WORLDMAP_FEATURES = [
|
|
48414
|
-
{ id: "f1", x: 2, y: 2, type: "capital", sprite: `${
|
|
48415
|
-
{ id: "f2", x: 4, y: 2, type: "power_node", sprite: `${
|
|
50954
|
+
{ id: "f1", x: 2, y: 2, type: "capital", sprite: `${CDN7}/scenes/world/capital.png` },
|
|
50955
|
+
{ id: "f2", x: 4, y: 2, type: "power_node", sprite: `${CDN7}/world-map/power_node.png` }
|
|
48416
50956
|
];
|
|
48417
50957
|
DEFAULT_WORLDMAP_MANIFEST = {
|
|
48418
|
-
baseUrl:
|
|
50958
|
+
baseUrl: CDN7,
|
|
48419
50959
|
terrains: {
|
|
48420
50960
|
grass: "/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png",
|
|
48421
50961
|
water: "/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png",
|
|
@@ -48544,7 +51084,7 @@ function lazyThree(name, loader) {
|
|
|
48544
51084
|
ThreeWrapper.displayName = `Lazy(${name})`;
|
|
48545
51085
|
return ThreeWrapper;
|
|
48546
51086
|
}
|
|
48547
|
-
var ThreeBoundary, FeatureRenderer,
|
|
51087
|
+
var ThreeBoundary, FeatureRenderer, GameCanvas3D2, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
|
|
48548
51088
|
var init_component_registry_generated = __esm({
|
|
48549
51089
|
"components/core/organisms/component-registry.generated.ts"() {
|
|
48550
51090
|
init_AboutPageTemplate();
|
|
@@ -48654,6 +51194,7 @@ var init_component_registry_generated = __esm({
|
|
|
48654
51194
|
init_FormSectionHeader();
|
|
48655
51195
|
init_GameAudioProvider();
|
|
48656
51196
|
init_GameAudioToggle();
|
|
51197
|
+
init_GameBoard3D();
|
|
48657
51198
|
init_GameCanvas2D();
|
|
48658
51199
|
init_GameHud();
|
|
48659
51200
|
init_GameMenu();
|
|
@@ -48711,7 +51252,9 @@ var init_component_registry_generated = __esm({
|
|
|
48711
51252
|
init_PageHeader();
|
|
48712
51253
|
init_Pagination();
|
|
48713
51254
|
init_PatternTile();
|
|
51255
|
+
init_PlatformerBoard();
|
|
48714
51256
|
init_PlatformerCanvas();
|
|
51257
|
+
init_PlatformerTemplate();
|
|
48715
51258
|
init_Popover();
|
|
48716
51259
|
init_PositionedCanvas();
|
|
48717
51260
|
init_PowerupSlots();
|
|
@@ -48735,6 +51278,8 @@ var init_component_registry_generated = __esm({
|
|
|
48735
51278
|
init_ResourceBar();
|
|
48736
51279
|
init_ResourceCounter();
|
|
48737
51280
|
init_RichBlockEditor();
|
|
51281
|
+
init_RoguelikeBoard();
|
|
51282
|
+
init_RoguelikeTemplate();
|
|
48738
51283
|
init_RuntimeDebugger2();
|
|
48739
51284
|
init_ScaledDiagram();
|
|
48740
51285
|
init_ScoreBoard();
|
|
@@ -48807,6 +51352,8 @@ var init_component_registry_generated = __esm({
|
|
|
48807
51352
|
init_Toast();
|
|
48808
51353
|
init_ToastSlot();
|
|
48809
51354
|
init_Tooltip();
|
|
51355
|
+
init_TowerDefenseBoard();
|
|
51356
|
+
init_TowerDefenseTemplate();
|
|
48810
51357
|
init_TraitFrame();
|
|
48811
51358
|
init_TraitSlot();
|
|
48812
51359
|
init_TrendIndicator();
|
|
@@ -48851,7 +51398,7 @@ var init_component_registry_generated = __esm({
|
|
|
48851
51398
|
}
|
|
48852
51399
|
};
|
|
48853
51400
|
FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
|
|
48854
|
-
|
|
51401
|
+
GameCanvas3D2 = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
|
|
48855
51402
|
GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
48856
51403
|
GameCanvas3DCastleTemplate = lazyThree("GameCanvas3DCastleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
48857
51404
|
GameCanvas3DWorldMapTemplate = lazyThree("GameCanvas3DWorldMapTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
@@ -48970,8 +51517,9 @@ var init_component_registry_generated = __esm({
|
|
|
48970
51517
|
"FormSectionHeader": FormSectionHeader,
|
|
48971
51518
|
"GameAudioProvider": GameAudioProvider,
|
|
48972
51519
|
"GameAudioToggle": GameAudioToggle,
|
|
51520
|
+
"GameBoard3D": GameBoard3D,
|
|
48973
51521
|
"GameCanvas2D": GameCanvas2D,
|
|
48974
|
-
"GameCanvas3D":
|
|
51522
|
+
"GameCanvas3D": GameCanvas3D2,
|
|
48975
51523
|
"GameCanvas3DBattleTemplate": GameCanvas3DBattleTemplate,
|
|
48976
51524
|
"GameCanvas3DCastleTemplate": GameCanvas3DCastleTemplate,
|
|
48977
51525
|
"GameCanvas3DWorldMapTemplate": GameCanvas3DWorldMapTemplate,
|
|
@@ -49033,7 +51581,9 @@ var init_component_registry_generated = __esm({
|
|
|
49033
51581
|
"PageHeader": PageHeader,
|
|
49034
51582
|
"Pagination": Pagination,
|
|
49035
51583
|
"PatternTile": PatternTile,
|
|
51584
|
+
"PlatformerBoard": PlatformerBoard,
|
|
49036
51585
|
"PlatformerCanvas": PlatformerCanvas,
|
|
51586
|
+
"PlatformerTemplate": PlatformerTemplate,
|
|
49037
51587
|
"Popover": Popover,
|
|
49038
51588
|
"PositionedCanvas": PositionedCanvas,
|
|
49039
51589
|
"PowerupSlots": PowerupSlots,
|
|
@@ -49057,6 +51607,8 @@ var init_component_registry_generated = __esm({
|
|
|
49057
51607
|
"ResourceBar": ResourceBar,
|
|
49058
51608
|
"ResourceCounter": ResourceCounter,
|
|
49059
51609
|
"RichBlockEditor": RichBlockEditor,
|
|
51610
|
+
"RoguelikeBoard": RoguelikeBoard,
|
|
51611
|
+
"RoguelikeTemplate": RoguelikeTemplate,
|
|
49060
51612
|
"RuntimeDebugger": RuntimeDebugger,
|
|
49061
51613
|
"ScaledDiagram": ScaledDiagram,
|
|
49062
51614
|
"ScoreBoard": ScoreBoard,
|
|
@@ -49132,6 +51684,8 @@ var init_component_registry_generated = __esm({
|
|
|
49132
51684
|
"Toast": Toast,
|
|
49133
51685
|
"ToastSlot": ToastSlot,
|
|
49134
51686
|
"Tooltip": Tooltip,
|
|
51687
|
+
"TowerDefenseBoard": TowerDefenseBoard,
|
|
51688
|
+
"TowerDefenseTemplate": TowerDefenseTemplate,
|
|
49135
51689
|
"TraitFrame": TraitFrame,
|
|
49136
51690
|
"TraitSlot": TraitSlot,
|
|
49137
51691
|
"TrendIndicator": TrendIndicator,
|
|
@@ -50694,6 +53248,10 @@ init_UncontrolledBattleBoard();
|
|
|
50694
53248
|
init_useBattleState();
|
|
50695
53249
|
init_WorldMapBoard();
|
|
50696
53250
|
init_CastleBoard();
|
|
53251
|
+
init_PlatformerBoard();
|
|
53252
|
+
init_TowerDefenseBoard();
|
|
53253
|
+
init_RoguelikeBoard();
|
|
53254
|
+
init_GameBoard3D();
|
|
50697
53255
|
init_TraitStateViewer();
|
|
50698
53256
|
init_TraitSlot();
|
|
50699
53257
|
|
|
@@ -51266,6 +53824,9 @@ init_GameShell();
|
|
|
51266
53824
|
init_BattleTemplate();
|
|
51267
53825
|
init_CastleTemplate();
|
|
51268
53826
|
init_WorldMapTemplate();
|
|
53827
|
+
init_PlatformerTemplate();
|
|
53828
|
+
init_TowerDefenseTemplate();
|
|
53829
|
+
init_RoguelikeTemplate();
|
|
51269
53830
|
init_LandingPageTemplate();
|
|
51270
53831
|
init_PricingPageTemplate();
|
|
51271
53832
|
init_FeatureDetailPageTemplate();
|
|
@@ -51274,4 +53835,4 @@ init_AboutPageTemplate();
|
|
|
51274
53835
|
// components/index.ts
|
|
51275
53836
|
init_cn();
|
|
51276
53837
|
|
|
51277
|
-
export { ALL_PRESETS, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, ActionButton, ActionButtons, Card2 as ActionCard, ActionPalette, ActionTile, ActivationBlock, Alert, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, Aside, AssetPicker, AuthLayout, Avatar, Badge, BattleBoard, BattleTemplate, BehaviorView, BloomQuizBlock, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, BranchingLogicBuilder, Breadcrumb, BuilderBoard, Button, ButtonGroup, CTABanner, CalendarGrid, CanvasEffect, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, CastleBoard, CastleTemplate, Center, Chart, ChartLegend, Checkbox, ChoiceButton, ClassifierBoard, Coachmark, CodeBlock, CodeRunnerPanel, CollapsibleSection, CombatLog, ComboCounter, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, ConnectionBlock, Container, ContentRenderer, ContentSection, ControlButton, CounterTemplate, CraftingRecipe, DEFAULT_LIKERT_OPTIONS, DEFAULT_MATRIX_COLUMNS, DIAMOND_TOP_Y, DPad, DamageNumber, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangePicker, DateRangeSelector, DayCell, DebuggerBoard, DetailPanel, Dialog, DialogueBox, DialogueBubble, Divider, DocBreadcrumb, DocPagination, DocSearch, DocSidebar, DocTOC, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmptyState, EnemyPlate, EntityDisplayEvents, ErrorBoundary, ErrorState, EventHandlerBoard, EventLog, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FileTree, FilterGroup, FilterPill, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, GameAudioContext, GameAudioProvider, GameAudioToggle, GameCanvas2D, GameHud, GameMenu, GameOverScreen, GameShell, GameTemplate, GenericAppTemplate, GeometricPattern, GradientDivider, GraphCanvas, GraphView, Grid, GridPicker, HStack, Header, HealthBar, HealthPanel, HeroOrganism, HeroSection, IDENTITY_BOOK_FIELDS, Icon, IconPicker, InfiniteScrollSentinel, Input, InputGroup, InstallBox, InventoryGrid, InventoryPanel, IsometricCanvas, ItemSlot, JazariStateMachine, JsonTreeEditor, Label, LandingPageTemplate, LawReferenceTooltip, Lightbox, LikertScale, LineChart2 as LineChart, List3 as List, LoadingState, MapView, MarkdownContent, MarketingFooter, MarketingStatCard, MasterDetail, MasterDetailLayout, MatrixQuestion, MediaGallery, Menu, Meter, MiniMap, Modal, ModalSlot, ModuleCard, Navigation, NegotiatorBoard, NodeSlotEditor, NotifyListener, NumberStepper, ObjectRulePanel, OnboardingSpotlight, OptionConstraintGroup, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, Pagination, PatternTile, PhysicsManager, PlatformerCanvas, Popover, PositionedCanvas, PowerupSlots, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PropertyInspector, PullQuote, PullToRefresh, QrScanner, QuestTracker, QuizBlock, Radio, RangeSlider, ReflectionBlock, RelationSelect, RepeatableFormSection, ReplyTree, ResourceBar, ResourceCounter, RichBlockEditor, RuleEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreBoard, ScoreDisplay, SearchInput, Section, SectionHeader, SegmentRenderer, Select, SequenceBar, SequencerBoard, ServiceCatalog, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, SimulationCanvas, SimulationControls, SimulationGraph, SimulatorBoard, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Sparkline, Spinner, Split, SplitPane, SplitSection, Sprite, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateArchitectBoard, StateIndicator, StateJsonView, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StatusEffect, StepFlow, StepFlowOrganism, SvgBranch, SvgConnection, SvgFlow, SvgGrid, SvgLobe, SvgMesh, SvgMorph, SvgNode, SvgPulse, SvgRing, SvgShield, SvgStack, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, TableView, Tabs, TagCloud, TagInput, TeamCard, TeamOrganism, TerrainPalette, TextHighlight, Textarea, ThemeSelector, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TurnIndicator, TurnPanel, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UncontrolledBattleBoard, UnitCommandBar, UploadDropZone, VStack, VariablePanel, VersionDiff, ViolationAlert, VoteStack, WaypointMarker, WizardContainer, WizardNavigation, WizardProgress, WorldMapBoard, WorldMapTemplate, XPBar, applyTemporaryEffect, calculateAttackTargets, calculateDamage, calculateValidMoves, cn, combatAnimations, combatClasses, combatEffects, createInitialGameState, createUnitAnimationState, drawSprite, generateCombatMessage, getCurrentFrame, getTileDimensions, inferDirection, isoToScreen, mapBookData, parseLessonSegments, parseMarkdownWithCodeBlocks, pendulum, projectileMotion, resolveFieldMap, resolveFrame, resolveSheetDirection, screenToIso, springOscillator, tickAnimationState, toCodeLanguage, transitionAnimation, useAnchorRect, useBattleState, useCamera, useGameAudio, useGameAudioContext, useImageCache, usePhysics2D, useSpriteAnimations };
|
|
53838
|
+
export { ALL_PRESETS, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, ActionButton, ActionButtons, Card2 as ActionCard, ActionPalette, ActionTile, ActivationBlock, Alert, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, Aside, AssetPicker, AuthLayout, Avatar, Badge, BattleBoard, BattleTemplate, BehaviorView, BloomQuizBlock, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, BranchingLogicBuilder, Breadcrumb, BuilderBoard, Button, ButtonGroup, CTABanner, CalendarGrid, CanvasEffect, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, CastleBoard, CastleTemplate, Center, Chart, ChartLegend, Checkbox, ChoiceButton, ClassifierBoard, Coachmark, CodeBlock, CodeRunnerPanel, CollapsibleSection, CombatLog, ComboCounter, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, ConnectionBlock, Container, ContentRenderer, ContentSection, ControlButton, CounterTemplate, CraftingRecipe, DEFAULT_LIKERT_OPTIONS, DEFAULT_MATRIX_COLUMNS, DIAMOND_TOP_Y, DPad, DamageNumber, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangePicker, DateRangeSelector, DayCell, DebuggerBoard, DetailPanel, Dialog, DialogueBox, DialogueBubble, Divider, DocBreadcrumb, DocPagination, DocSearch, DocSidebar, DocTOC, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmptyState, EnemyPlate, EntityDisplayEvents, ErrorBoundary, ErrorState, EventHandlerBoard, EventLog, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FileTree, FilterGroup, FilterPill, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, GameAudioContext, GameAudioProvider, GameAudioToggle, GameBoard3D, GameCanvas2D, GameHud, GameMenu, GameOverScreen, GameShell, GameTemplate, GenericAppTemplate, GeometricPattern, GradientDivider, GraphCanvas, GraphView, Grid, GridPicker, HStack, Header, HealthBar, HealthPanel, HeroOrganism, HeroSection, IDENTITY_BOOK_FIELDS, Icon, IconPicker, InfiniteScrollSentinel, Input, InputGroup, InstallBox, InventoryGrid, InventoryPanel, IsometricCanvas, ItemSlot, JazariStateMachine, JsonTreeEditor, Label, LandingPageTemplate, LawReferenceTooltip, Lightbox, LikertScale, LineChart2 as LineChart, List3 as List, LoadingState, MapView, MarkdownContent, MarketingFooter, MarketingStatCard, MasterDetail, MasterDetailLayout, MatrixQuestion, MediaGallery, Menu, Meter, MiniMap, Modal, ModalSlot, ModuleCard, Navigation, NegotiatorBoard, NodeSlotEditor, NotifyListener, NumberStepper, ObjectRulePanel, OnboardingSpotlight, OptionConstraintGroup, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, Pagination, PatternTile, PhysicsManager, PlatformerBoard, PlatformerCanvas, PlatformerTemplate, Popover, PositionedCanvas, PowerupSlots, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PropertyInspector, PullQuote, PullToRefresh, QrScanner, QuestTracker, QuizBlock, Radio, RangeSlider, ReflectionBlock, RelationSelect, RepeatableFormSection, ReplyTree, ResourceBar, ResourceCounter, RichBlockEditor, RoguelikeBoard, RoguelikeTemplate, RuleEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreBoard, ScoreDisplay, SearchInput, Section, SectionHeader, SegmentRenderer, Select, SequenceBar, SequencerBoard, ServiceCatalog, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, SimulationCanvas, SimulationControls, SimulationGraph, SimulatorBoard, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Sparkline, Spinner, Split, SplitPane, SplitSection, Sprite, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateArchitectBoard, StateIndicator, StateJsonView, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StatusEffect, StepFlow, StepFlowOrganism, SvgBranch, SvgConnection, SvgFlow, SvgGrid, SvgLobe, SvgMesh, SvgMorph, SvgNode, SvgPulse, SvgRing, SvgShield, SvgStack, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, TableView, Tabs, TagCloud, TagInput, TeamCard, TeamOrganism, TerrainPalette, TextHighlight, Textarea, ThemeSelector, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TowerDefenseBoard, TowerDefenseTemplate, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TurnIndicator, TurnPanel, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UncontrolledBattleBoard, UnitCommandBar, UploadDropZone, VStack, VariablePanel, VersionDiff, ViolationAlert, VoteStack, WaypointMarker, WizardContainer, WizardNavigation, WizardProgress, WorldMapBoard, WorldMapTemplate, XPBar, applyTemporaryEffect, calculateAttackTargets, calculateDamage, calculateValidMoves, cn, combatAnimations, combatClasses, combatEffects, createInitialGameState, createUnitAnimationState, drawSprite, generateCombatMessage, getCurrentFrame, getTileDimensions, inferDirection, isoToScreen, mapBookData, parseLessonSegments, parseMarkdownWithCodeBlocks, pendulum, projectileMotion, resolveFieldMap, resolveFrame, resolveSheetDirection, screenToIso, springOscillator, tickAnimationState, toCodeLanguage, transitionAnimation, useAnchorRect, useBattleState, useCamera, useGameAudio, useGameAudioContext, useImageCache, usePhysics2D, useSpriteAnimations };
|