@almadar/ui 5.47.0 → 5.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/avl/index.cjs +2998 -443
- package/dist/avl/index.css +504 -0
- package/dist/avl/index.js +2998 -444
- package/dist/components/core/templates/index.d.ts +3 -0
- package/dist/components/game/atoms/ActionButton.d.ts +4 -1
- package/dist/components/game/atoms/ComboCounter.d.ts +4 -1
- package/dist/components/game/atoms/ControlButton.d.ts +4 -2
- package/dist/components/game/atoms/DamageNumber.d.ts +4 -1
- package/dist/components/game/atoms/ResourceCounter.d.ts +4 -1
- package/dist/components/game/atoms/ScoreDisplay.d.ts +4 -1
- package/dist/components/game/atoms/StateIndicator.d.ts +4 -7
- package/dist/components/game/atoms/StatusEffect.d.ts +4 -1
- package/dist/components/game/atoms/TurnIndicator.d.ts +4 -1
- package/dist/components/game/atoms/WaypointMarker.d.ts +4 -1
- package/dist/components/game/molecules/EnemyPlate.d.ts +4 -1
- package/dist/components/game/molecules/StatBadge.d.ts +4 -1
- package/dist/components/game/molecules/three/index.cjs +1428 -346
- package/dist/components/game/molecules/three/index.css +0 -5
- package/dist/components/game/molecules/three/index.js +1429 -347
- package/dist/components/game/organisms/CastleBoard.d.ts +10 -1
- package/dist/components/game/organisms/GameBoard3D.d.ts +62 -0
- package/dist/components/game/organisms/PlatformerBoard.d.ts +51 -0
- package/dist/components/game/organisms/RoguelikeBoard.d.ts +59 -0
- package/dist/components/game/organisms/TowerDefenseBoard.d.ts +64 -0
- package/dist/components/game/organisms/index.d.ts +4 -0
- package/dist/components/game/organisms/puzzles/event-handler/EventHandlerBoard.d.ts +9 -1
- package/dist/components/game/organisms/puzzles/state-architect/StateArchitectBoard.d.ts +14 -1
- package/dist/components/game/templates/GameCanvas3DBattleTemplate.d.ts +22 -35
- package/dist/components/game/templates/GameCanvas3DCastleTemplate.d.ts +23 -23
- package/dist/components/game/templates/GameCanvas3DWorldMapTemplate.d.ts +27 -29
- package/dist/components/game/templates/PlatformerTemplate.d.ts +26 -0
- package/dist/components/game/templates/RoguelikeTemplate.d.ts +23 -0
- package/dist/components/game/templates/TowerDefenseTemplate.d.ts +43 -0
- package/dist/components/index.cjs +2991 -422
- package/dist/components/index.css +504 -0
- package/dist/components/index.js +2985 -424
- package/dist/docs/index.css +504 -0
- package/dist/providers/index.cjs +2981 -426
- package/dist/providers/index.css +504 -0
- package/dist/providers/index.js +2981 -427
- package/dist/runtime/index.cjs +2982 -427
- package/dist/runtime/index.css +504 -0
- package/dist/runtime/index.js +2982 -428
- package/package.json +1 -1
- package/tailwind-preset.cjs +1 -0
package/dist/runtime/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React82 from 'react';
|
|
2
|
-
import React82__default, { createContext, useMemo, useContext, useRef, useEffect, useCallback, Suspense, useState, useSyncExternalStore, useLayoutEffect, lazy, useId } from 'react';
|
|
2
|
+
import React82__default, { createContext, useMemo, useContext, useRef, useEffect, useCallback, Suspense, useState, useSyncExternalStore, useLayoutEffect, lazy, useId, forwardRef, useImperativeHandle, Component } from 'react';
|
|
3
3
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
import { clsx } from 'clsx';
|
|
5
5
|
import { twMerge } from 'tailwind-merge';
|
|
@@ -41,6 +41,12 @@ import { DndContext, pointerWithin, rectIntersection, closestCorners, useSensors
|
|
|
41
41
|
import { useSortable, arrayMove, sortableKeyboardCoordinates, SortableContext, rectSortingStrategy, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
|
42
42
|
import { CSS } from '@dnd-kit/utilities';
|
|
43
43
|
import { useNodeId, ReactFlowProvider, Handle, Position } from '@xyflow/react';
|
|
44
|
+
import * as THREE from 'three';
|
|
45
|
+
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
|
46
|
+
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
|
|
47
|
+
import { GLTFLoader as GLTFLoader$1 } from 'three/examples/jsm/loaders/GLTFLoader';
|
|
48
|
+
import { Canvas, useThree } from '@react-three/fiber';
|
|
49
|
+
import { Grid as Grid$1, OrbitControls } from '@react-three/drei';
|
|
44
50
|
import { getPatternDefinition, getComponentForPattern as getComponentForPattern$1 } from '@almadar/patterns';
|
|
45
51
|
import { StateMachineManager, createContextFromBindings, interpolateValue, collectDeclaredConfigDefaults, createServerEffectHandlers, EffectExecutor, InMemoryPersistence } from '@almadar/runtime';
|
|
46
52
|
import { OrbitalServerRuntime } from '@almadar/runtime/OrbitalServerRuntime';
|
|
@@ -409,7 +415,7 @@ var init_Box = __esm({
|
|
|
409
415
|
position,
|
|
410
416
|
className,
|
|
411
417
|
children,
|
|
412
|
-
as:
|
|
418
|
+
as: Component2 = "div",
|
|
413
419
|
action,
|
|
414
420
|
actionPayload,
|
|
415
421
|
hoverEvent,
|
|
@@ -441,7 +447,7 @@ var init_Box = __esm({
|
|
|
441
447
|
}, [hoverEvent, eventBus, onMouseLeave]);
|
|
442
448
|
const isClickable = action || onClick;
|
|
443
449
|
return React82__default.createElement(
|
|
444
|
-
|
|
450
|
+
Component2,
|
|
445
451
|
{
|
|
446
452
|
ref,
|
|
447
453
|
className: cn(
|
|
@@ -579,9 +585,9 @@ function resolvePhosphor(name, weight, family) {
|
|
|
579
585
|
(lib) => {
|
|
580
586
|
const PhosphorComp = lib[target];
|
|
581
587
|
if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
|
|
582
|
-
const
|
|
588
|
+
const Component2 = PhosphorComp;
|
|
583
589
|
const Adapter = (props) => /* @__PURE__ */ jsx(
|
|
584
|
-
|
|
590
|
+
Component2,
|
|
585
591
|
{
|
|
586
592
|
weight,
|
|
587
593
|
className: props.className,
|
|
@@ -605,9 +611,9 @@ function resolveTabler(name, family) {
|
|
|
605
611
|
(lib) => {
|
|
606
612
|
const TablerComp = lib[target];
|
|
607
613
|
if (!TablerComp || typeof TablerComp !== "object") return null;
|
|
608
|
-
const
|
|
614
|
+
const Component2 = TablerComp;
|
|
609
615
|
const Adapter = (props) => /* @__PURE__ */ jsx(
|
|
610
|
-
|
|
616
|
+
Component2,
|
|
611
617
|
{
|
|
612
618
|
stroke: props.strokeWidth ?? 1.5,
|
|
613
619
|
className: props.className,
|
|
@@ -631,9 +637,9 @@ function resolveFa(name, family) {
|
|
|
631
637
|
(lib) => {
|
|
632
638
|
const FaComp = lib[target];
|
|
633
639
|
if (!FaComp || typeof FaComp !== "function") return null;
|
|
634
|
-
const
|
|
640
|
+
const Component2 = FaComp;
|
|
635
641
|
const Adapter = (props) => /* @__PURE__ */ jsx(
|
|
636
|
-
|
|
642
|
+
Component2,
|
|
637
643
|
{
|
|
638
644
|
className: props.className,
|
|
639
645
|
style: props.style,
|
|
@@ -1588,8 +1594,8 @@ var init_Typography = __esm({
|
|
|
1588
1594
|
children
|
|
1589
1595
|
}) => {
|
|
1590
1596
|
const variant = variantProp ?? (level ? `h${level}` : "body1");
|
|
1591
|
-
const
|
|
1592
|
-
const Comp =
|
|
1597
|
+
const Component2 = as || defaultElements[variant];
|
|
1598
|
+
const Comp = Component2;
|
|
1593
1599
|
return /* @__PURE__ */ jsx(
|
|
1594
1600
|
Comp,
|
|
1595
1601
|
{
|
|
@@ -4144,10 +4150,10 @@ var init_Center = __esm({
|
|
|
4144
4150
|
className,
|
|
4145
4151
|
style,
|
|
4146
4152
|
children,
|
|
4147
|
-
as:
|
|
4153
|
+
as: Component2 = "div"
|
|
4148
4154
|
}) => {
|
|
4149
4155
|
const mergedStyle = minHeight ? { minHeight, ...style } : style;
|
|
4150
|
-
const Comp =
|
|
4156
|
+
const Comp = Component2;
|
|
4151
4157
|
return /* @__PURE__ */ jsx(
|
|
4152
4158
|
Comp,
|
|
4153
4159
|
{
|
|
@@ -4778,7 +4784,7 @@ var init_Stack = __esm({
|
|
|
4778
4784
|
className,
|
|
4779
4785
|
style,
|
|
4780
4786
|
children,
|
|
4781
|
-
as:
|
|
4787
|
+
as: Component2 = "div",
|
|
4782
4788
|
onClick,
|
|
4783
4789
|
onKeyDown,
|
|
4784
4790
|
role,
|
|
@@ -4796,7 +4802,7 @@ var init_Stack = __esm({
|
|
|
4796
4802
|
};
|
|
4797
4803
|
const isHorizontal = direction === "horizontal";
|
|
4798
4804
|
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";
|
|
4799
|
-
const Comp =
|
|
4805
|
+
const Comp = Component2;
|
|
4800
4806
|
return /* @__PURE__ */ jsx(
|
|
4801
4807
|
Comp,
|
|
4802
4808
|
{
|
|
@@ -7027,6 +7033,7 @@ var init_HealthBar = __esm({
|
|
|
7027
7033
|
}
|
|
7028
7034
|
});
|
|
7029
7035
|
function ScoreDisplay({
|
|
7036
|
+
assetUrl = DEFAULT_ASSET_URL,
|
|
7030
7037
|
value,
|
|
7031
7038
|
label,
|
|
7032
7039
|
icon,
|
|
@@ -7073,14 +7080,24 @@ function ScoreDisplay({
|
|
|
7073
7080
|
className
|
|
7074
7081
|
),
|
|
7075
7082
|
children: [
|
|
7076
|
-
|
|
7083
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
7084
|
+
"img",
|
|
7085
|
+
{
|
|
7086
|
+
src: assetUrl,
|
|
7087
|
+
alt: "",
|
|
7088
|
+
width: 20,
|
|
7089
|
+
height: 20,
|
|
7090
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7091
|
+
className: "flex-shrink-0"
|
|
7092
|
+
}
|
|
7093
|
+
) : icon ? /* @__PURE__ */ jsx("span", { className: "flex-shrink-0", children: typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon }) : /* @__PURE__ */ jsx(Icon, { icon }) }) : null,
|
|
7077
7094
|
label && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label }),
|
|
7078
7095
|
/* @__PURE__ */ jsx("span", { className: "tabular-nums", children: formattedValue })
|
|
7079
7096
|
]
|
|
7080
7097
|
}
|
|
7081
7098
|
);
|
|
7082
7099
|
}
|
|
7083
|
-
var sizeMap2;
|
|
7100
|
+
var sizeMap2, DEFAULT_ASSET_URL;
|
|
7084
7101
|
var init_ScoreDisplay = __esm({
|
|
7085
7102
|
"components/game/atoms/ScoreDisplay.tsx"() {
|
|
7086
7103
|
"use client";
|
|
@@ -7092,10 +7109,12 @@ var init_ScoreDisplay = __esm({
|
|
|
7092
7109
|
lg: "text-2xl",
|
|
7093
7110
|
xl: "text-4xl"
|
|
7094
7111
|
};
|
|
7112
|
+
DEFAULT_ASSET_URL = "https://almadar-kflow-assets.web.app/shared/effects/particles/star_01.png";
|
|
7095
7113
|
ScoreDisplay.displayName = "ScoreDisplay";
|
|
7096
7114
|
}
|
|
7097
7115
|
});
|
|
7098
7116
|
function ControlButton({
|
|
7117
|
+
assetUrl = DEFAULT_ASSET_URL2,
|
|
7099
7118
|
label,
|
|
7100
7119
|
icon,
|
|
7101
7120
|
size = "md",
|
|
@@ -7142,7 +7161,7 @@ function ControlButton({
|
|
|
7142
7161
|
},
|
|
7143
7162
|
[isPressed, releaseEvent, eventBus, onRelease]
|
|
7144
7163
|
);
|
|
7145
|
-
return /* @__PURE__ */
|
|
7164
|
+
return /* @__PURE__ */ jsx(
|
|
7146
7165
|
"button",
|
|
7147
7166
|
{
|
|
7148
7167
|
type: "button",
|
|
@@ -7163,20 +7182,27 @@ function ControlButton({
|
|
|
7163
7182
|
disabled && "opacity-50 cursor-not-allowed",
|
|
7164
7183
|
className
|
|
7165
7184
|
),
|
|
7166
|
-
children:
|
|
7167
|
-
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7185
|
+
children: assetUrl ? /* @__PURE__ */ jsx(
|
|
7186
|
+
"img",
|
|
7187
|
+
{
|
|
7188
|
+
src: assetUrl,
|
|
7189
|
+
alt: "",
|
|
7190
|
+
width: 24,
|
|
7191
|
+
height: 24,
|
|
7192
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7193
|
+
className: "flex-shrink-0"
|
|
7194
|
+
}
|
|
7195
|
+
) : icon ? /* @__PURE__ */ jsx("span", { className: "text-2xl", children: typeof icon === "string" ? /^[a-zA-Z0-9-]+$/.test(icon) ? (() => {
|
|
7196
|
+
const I = resolveIcon(icon);
|
|
7197
|
+
return I ? /* @__PURE__ */ jsx(I, { className: "w-6 h-6" }) : null;
|
|
7198
|
+
})() : icon : /* @__PURE__ */ (() => {
|
|
7199
|
+
const I = icon;
|
|
7200
|
+
return /* @__PURE__ */ jsx(I, { className: "w-6 h-6" });
|
|
7201
|
+
})() }) : label ? /* @__PURE__ */ jsx("span", { children: label }) : null
|
|
7176
7202
|
}
|
|
7177
7203
|
);
|
|
7178
7204
|
}
|
|
7179
|
-
var sizeMap3, shapeMap, variantMap;
|
|
7205
|
+
var sizeMap3, shapeMap, variantMap, DEFAULT_ASSET_URL2;
|
|
7180
7206
|
var init_ControlButton = __esm({
|
|
7181
7207
|
"components/game/atoms/ControlButton.tsx"() {
|
|
7182
7208
|
"use client";
|
|
@@ -7199,6 +7225,7 @@ var init_ControlButton = __esm({
|
|
|
7199
7225
|
secondary: "bg-secondary text-secondary-foreground border-border hover:bg-secondary-hover",
|
|
7200
7226
|
ghost: "bg-transparent text-foreground border-border hover:bg-muted"
|
|
7201
7227
|
};
|
|
7228
|
+
DEFAULT_ASSET_URL2 = "https://almadar-kflow-assets.web.app/shared/effects/particles/circle_01.png";
|
|
7202
7229
|
ControlButton.displayName = "ControlButton";
|
|
7203
7230
|
}
|
|
7204
7231
|
});
|
|
@@ -7278,6 +7305,7 @@ var init_Sprite = __esm({
|
|
|
7278
7305
|
}
|
|
7279
7306
|
});
|
|
7280
7307
|
function StateIndicator({
|
|
7308
|
+
assetUrl = DEFAULT_ASSET_URL3,
|
|
7281
7309
|
state = "idle",
|
|
7282
7310
|
label,
|
|
7283
7311
|
size = "md",
|
|
@@ -7300,18 +7328,29 @@ function StateIndicator({
|
|
|
7300
7328
|
className
|
|
7301
7329
|
),
|
|
7302
7330
|
children: [
|
|
7303
|
-
/* @__PURE__ */ jsx(Box, { as: "span", children:
|
|
7331
|
+
/* @__PURE__ */ jsx(Box, { as: "span", children: assetUrl ? /* @__PURE__ */ jsx(
|
|
7332
|
+
"img",
|
|
7333
|
+
{
|
|
7334
|
+
src: assetUrl,
|
|
7335
|
+
alt: displayLabel,
|
|
7336
|
+
width: 16,
|
|
7337
|
+
height: 16,
|
|
7338
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7339
|
+
className: "flex-shrink-0"
|
|
7340
|
+
}
|
|
7341
|
+
) : 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 }) }),
|
|
7304
7342
|
/* @__PURE__ */ jsx(Box, { as: "span", children: displayLabel })
|
|
7305
7343
|
]
|
|
7306
7344
|
}
|
|
7307
7345
|
);
|
|
7308
7346
|
}
|
|
7309
|
-
var DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
7347
|
+
var DEFAULT_ASSET_URL3, DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
7310
7348
|
var init_StateIndicator = __esm({
|
|
7311
7349
|
"components/game/atoms/StateIndicator.tsx"() {
|
|
7312
7350
|
init_Box();
|
|
7313
7351
|
init_Icon();
|
|
7314
7352
|
init_cn();
|
|
7353
|
+
DEFAULT_ASSET_URL3 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
7315
7354
|
DEFAULT_STATE_STYLES = {
|
|
7316
7355
|
idle: { icon: "\u23F8", bgClass: "bg-muted" },
|
|
7317
7356
|
active: { icon: "\u25B6", bgClass: "bg-success" },
|
|
@@ -7386,6 +7425,7 @@ var init_TimerDisplay = __esm({
|
|
|
7386
7425
|
}
|
|
7387
7426
|
});
|
|
7388
7427
|
function ResourceCounter({
|
|
7428
|
+
assetUrl = DEFAULT_ASSET_URL4,
|
|
7389
7429
|
icon,
|
|
7390
7430
|
label = "Gold",
|
|
7391
7431
|
value = 250,
|
|
@@ -7405,7 +7445,17 @@ function ResourceCounter({
|
|
|
7405
7445
|
className
|
|
7406
7446
|
),
|
|
7407
7447
|
children: [
|
|
7408
|
-
|
|
7448
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
7449
|
+
"img",
|
|
7450
|
+
{
|
|
7451
|
+
src: assetUrl,
|
|
7452
|
+
alt: label,
|
|
7453
|
+
width: sizes.img,
|
|
7454
|
+
height: sizes.img,
|
|
7455
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7456
|
+
className: "flex-shrink-0"
|
|
7457
|
+
}
|
|
7458
|
+
) : 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,
|
|
7409
7459
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label }),
|
|
7410
7460
|
/* @__PURE__ */ jsxs("span", { className: cn("font-bold tabular-nums", color && (color in colorTokenClasses2 ? colorTokenClasses2[color] : color)), children: [
|
|
7411
7461
|
value,
|
|
@@ -7418,7 +7468,7 @@ function ResourceCounter({
|
|
|
7418
7468
|
}
|
|
7419
7469
|
);
|
|
7420
7470
|
}
|
|
7421
|
-
var colorTokenClasses2, sizeMap5;
|
|
7471
|
+
var colorTokenClasses2, DEFAULT_ASSET_URL4, sizeMap5;
|
|
7422
7472
|
var init_ResourceCounter = __esm({
|
|
7423
7473
|
"components/game/atoms/ResourceCounter.tsx"() {
|
|
7424
7474
|
init_cn();
|
|
@@ -7431,16 +7481,17 @@ var init_ResourceCounter = __esm({
|
|
|
7431
7481
|
error: "text-error",
|
|
7432
7482
|
muted: "text-muted-foreground"
|
|
7433
7483
|
};
|
|
7484
|
+
DEFAULT_ASSET_URL4 = "https://almadar-kflow-assets.web.app/shared/world-map/gold_mine.png";
|
|
7434
7485
|
sizeMap5 = {
|
|
7435
|
-
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm" },
|
|
7436
|
-
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base" },
|
|
7437
|
-
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg" }
|
|
7486
|
+
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm", img: 16 },
|
|
7487
|
+
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base", img: 20 },
|
|
7488
|
+
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg", img: 28 }
|
|
7438
7489
|
};
|
|
7439
7490
|
ResourceCounter.displayName = "ResourceCounter";
|
|
7440
7491
|
}
|
|
7441
7492
|
});
|
|
7442
7493
|
function ItemSlot({
|
|
7443
|
-
assetUrl =
|
|
7494
|
+
assetUrl = DEFAULT_ASSET_URL5,
|
|
7444
7495
|
icon = "sword",
|
|
7445
7496
|
label = "Iron Sword",
|
|
7446
7497
|
quantity,
|
|
@@ -7499,7 +7550,7 @@ function ItemSlot({
|
|
|
7499
7550
|
}
|
|
7500
7551
|
);
|
|
7501
7552
|
}
|
|
7502
|
-
var sizeMap6, rarityBorderMap, rarityGlowMap,
|
|
7553
|
+
var sizeMap6, rarityBorderMap, rarityGlowMap, DEFAULT_ASSET_URL5, assetSizeMap;
|
|
7503
7554
|
var init_ItemSlot = __esm({
|
|
7504
7555
|
"components/game/atoms/ItemSlot.tsx"() {
|
|
7505
7556
|
"use client";
|
|
@@ -7524,7 +7575,7 @@ var init_ItemSlot = __esm({
|
|
|
7524
7575
|
epic: "shadow-lg",
|
|
7525
7576
|
legendary: "shadow-lg"
|
|
7526
7577
|
};
|
|
7527
|
-
|
|
7578
|
+
DEFAULT_ASSET_URL5 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
7528
7579
|
assetSizeMap = {
|
|
7529
7580
|
sm: 28,
|
|
7530
7581
|
md: 40,
|
|
@@ -7534,6 +7585,7 @@ var init_ItemSlot = __esm({
|
|
|
7534
7585
|
}
|
|
7535
7586
|
});
|
|
7536
7587
|
function TurnIndicator({
|
|
7588
|
+
assetUrl = DEFAULT_ASSET_URL6,
|
|
7537
7589
|
currentTurn = 1,
|
|
7538
7590
|
maxTurns,
|
|
7539
7591
|
activeTeam,
|
|
@@ -7566,14 +7618,24 @@ function TurnIndicator({
|
|
|
7566
7618
|
] }),
|
|
7567
7619
|
activeTeam && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7568
7620
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "|" }),
|
|
7569
|
-
/* @__PURE__ */ jsx(
|
|
7621
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
7622
|
+
"img",
|
|
7623
|
+
{
|
|
7624
|
+
src: assetUrl,
|
|
7625
|
+
alt: "",
|
|
7626
|
+
width: 12,
|
|
7627
|
+
height: 12,
|
|
7628
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7629
|
+
className: "flex-shrink-0"
|
|
7630
|
+
}
|
|
7631
|
+
) : /* @__PURE__ */ jsx("span", { className: cn("rounded-full bg-success/20", sizes.dot) }),
|
|
7570
7632
|
/* @__PURE__ */ jsx("span", { className: "text-success", children: activeTeam })
|
|
7571
7633
|
] })
|
|
7572
7634
|
]
|
|
7573
7635
|
}
|
|
7574
7636
|
);
|
|
7575
7637
|
}
|
|
7576
|
-
var sizeMap7;
|
|
7638
|
+
var sizeMap7, DEFAULT_ASSET_URL6;
|
|
7577
7639
|
var init_TurnIndicator = __esm({
|
|
7578
7640
|
"components/game/atoms/TurnIndicator.tsx"() {
|
|
7579
7641
|
init_cn();
|
|
@@ -7582,6 +7644,7 @@ var init_TurnIndicator = __esm({
|
|
|
7582
7644
|
md: { wrapper: "text-sm gap-2 px-3 py-1", dot: "w-2 h-2" },
|
|
7583
7645
|
lg: { wrapper: "text-base gap-2.5 px-4 py-1.5", dot: "w-2.5 h-2.5" }
|
|
7584
7646
|
};
|
|
7647
|
+
DEFAULT_ASSET_URL6 = "https://almadar-kflow-assets.web.app/shared/effects/particles/symbol_01.png";
|
|
7585
7648
|
TurnIndicator.displayName = "TurnIndicator";
|
|
7586
7649
|
}
|
|
7587
7650
|
});
|
|
@@ -7597,6 +7660,7 @@ function getComboScale(combo) {
|
|
|
7597
7660
|
return "";
|
|
7598
7661
|
}
|
|
7599
7662
|
function ComboCounter({
|
|
7663
|
+
assetUrl = DEFAULT_ASSET_URL7,
|
|
7600
7664
|
combo = 5,
|
|
7601
7665
|
multiplier,
|
|
7602
7666
|
streak,
|
|
@@ -7616,6 +7680,17 @@ function ComboCounter({
|
|
|
7616
7680
|
className
|
|
7617
7681
|
),
|
|
7618
7682
|
children: [
|
|
7683
|
+
assetUrl && /* @__PURE__ */ jsx(
|
|
7684
|
+
"img",
|
|
7685
|
+
{
|
|
7686
|
+
src: assetUrl,
|
|
7687
|
+
alt: "combo",
|
|
7688
|
+
width: 24,
|
|
7689
|
+
height: 24,
|
|
7690
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7691
|
+
className: "flex-shrink-0 mb-0.5"
|
|
7692
|
+
}
|
|
7693
|
+
),
|
|
7619
7694
|
/* @__PURE__ */ jsx("span", { className: cn("font-black tabular-nums leading-none", sizes.combo, getComboIntensity(combo)), children: combo }),
|
|
7620
7695
|
/* @__PURE__ */ jsx("span", { className: cn("font-bold uppercase tracking-wider text-muted-foreground", sizes.label), children: "combo" }),
|
|
7621
7696
|
multiplier != null && multiplier > 1 && /* @__PURE__ */ jsxs("span", { className: cn("font-bold text-warning tabular-nums", sizes.multiplier), children: [
|
|
@@ -7630,10 +7705,11 @@ function ComboCounter({
|
|
|
7630
7705
|
}
|
|
7631
7706
|
);
|
|
7632
7707
|
}
|
|
7633
|
-
var sizeMap8;
|
|
7708
|
+
var DEFAULT_ASSET_URL7, sizeMap8;
|
|
7634
7709
|
var init_ComboCounter = __esm({
|
|
7635
7710
|
"components/game/atoms/ComboCounter.tsx"() {
|
|
7636
7711
|
init_cn();
|
|
7712
|
+
DEFAULT_ASSET_URL7 = "https://almadar-kflow-assets.web.app/shared/effects/flash/flash00.png";
|
|
7637
7713
|
sizeMap8 = {
|
|
7638
7714
|
sm: { combo: "text-lg", label: "text-xs", multiplier: "text-xs" },
|
|
7639
7715
|
md: { combo: "text-2xl", label: "text-xs", multiplier: "text-sm" },
|
|
@@ -7727,6 +7803,7 @@ var init_XPBar = __esm({
|
|
|
7727
7803
|
}
|
|
7728
7804
|
});
|
|
7729
7805
|
function WaypointMarker({
|
|
7806
|
+
assetUrl = DEFAULT_ASSET_URL8,
|
|
7730
7807
|
label,
|
|
7731
7808
|
icon,
|
|
7732
7809
|
active = true,
|
|
@@ -7765,7 +7842,17 @@ function WaypointMarker({
|
|
|
7765
7842
|
active && !completed && "bg-info text-foreground",
|
|
7766
7843
|
!active && !completed && "bg-muted"
|
|
7767
7844
|
),
|
|
7768
|
-
children: completed ? checkIcon :
|
|
7845
|
+
children: completed ? checkIcon : assetUrl ? /* @__PURE__ */ jsx(
|
|
7846
|
+
"img",
|
|
7847
|
+
{
|
|
7848
|
+
src: assetUrl,
|
|
7849
|
+
alt: label,
|
|
7850
|
+
width: sizes.img,
|
|
7851
|
+
height: sizes.img,
|
|
7852
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7853
|
+
className: "flex-shrink-0"
|
|
7854
|
+
}
|
|
7855
|
+
) : icon ? typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon }) : /* @__PURE__ */ jsx(Icon, { icon }) : null
|
|
7769
7856
|
}
|
|
7770
7857
|
)
|
|
7771
7858
|
] }),
|
|
@@ -7782,15 +7869,16 @@ function WaypointMarker({
|
|
|
7782
7869
|
)
|
|
7783
7870
|
] });
|
|
7784
7871
|
}
|
|
7785
|
-
var sizeMap10, checkIcon;
|
|
7872
|
+
var DEFAULT_ASSET_URL8, sizeMap10, checkIcon;
|
|
7786
7873
|
var init_WaypointMarker = __esm({
|
|
7787
7874
|
"components/game/atoms/WaypointMarker.tsx"() {
|
|
7788
7875
|
init_cn();
|
|
7789
7876
|
init_Icon();
|
|
7877
|
+
DEFAULT_ASSET_URL8 = "https://almadar-kflow-assets.web.app/shared/world-map/battle_marker.png";
|
|
7790
7878
|
sizeMap10 = {
|
|
7791
|
-
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1" },
|
|
7792
|
-
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5" },
|
|
7793
|
-
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2" }
|
|
7879
|
+
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1", img: 12 },
|
|
7880
|
+
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5", img: 18 },
|
|
7881
|
+
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2", img: 24 }
|
|
7794
7882
|
};
|
|
7795
7883
|
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" }) });
|
|
7796
7884
|
WaypointMarker.displayName = "WaypointMarker";
|
|
@@ -7803,6 +7891,7 @@ function formatDuration(seconds) {
|
|
|
7803
7891
|
return `${Math.round(seconds)}s`;
|
|
7804
7892
|
}
|
|
7805
7893
|
function StatusEffect({
|
|
7894
|
+
assetUrl = DEFAULT_ASSET_URL9,
|
|
7806
7895
|
icon = "shield",
|
|
7807
7896
|
label = "Shield",
|
|
7808
7897
|
duration = 30,
|
|
@@ -7823,7 +7912,17 @@ function StatusEffect({
|
|
|
7823
7912
|
),
|
|
7824
7913
|
title: label,
|
|
7825
7914
|
children: [
|
|
7826
|
-
/* @__PURE__ */ jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children:
|
|
7915
|
+
/* @__PURE__ */ jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children: assetUrl ? /* @__PURE__ */ jsx(
|
|
7916
|
+
"img",
|
|
7917
|
+
{
|
|
7918
|
+
src: assetUrl,
|
|
7919
|
+
alt: label,
|
|
7920
|
+
width: sizes.img,
|
|
7921
|
+
height: sizes.img,
|
|
7922
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
7923
|
+
className: "flex-shrink-0"
|
|
7924
|
+
}
|
|
7925
|
+
) : typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, size: "sm" }) : /* @__PURE__ */ jsx(Icon, { icon, size: "sm" }) }),
|
|
7827
7926
|
duration !== void 0 && /* @__PURE__ */ jsx(
|
|
7828
7927
|
"span",
|
|
7829
7928
|
{
|
|
@@ -7850,15 +7949,16 @@ function StatusEffect({
|
|
|
7850
7949
|
label && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground mt-0.5 text-center whitespace-nowrap", children: label })
|
|
7851
7950
|
] });
|
|
7852
7951
|
}
|
|
7853
|
-
var sizeMap11, variantStyles7;
|
|
7952
|
+
var DEFAULT_ASSET_URL9, sizeMap11, variantStyles7;
|
|
7854
7953
|
var init_StatusEffect = __esm({
|
|
7855
7954
|
"components/game/atoms/StatusEffect.tsx"() {
|
|
7856
7955
|
init_cn();
|
|
7857
7956
|
init_Icon();
|
|
7957
|
+
DEFAULT_ASSET_URL9 = "https://almadar-kflow-assets.web.app/shared/effects/particles/flame_01.png";
|
|
7858
7958
|
sizeMap11 = {
|
|
7859
|
-
sm: { container: "w-8 h-8", icon: "text-sm", badge: "text-xs -top-1 -right-1 w-4 h-4", timer: "text-[9px]" },
|
|
7860
|
-
md: { container: "w-10 h-10", icon: "text-base", badge: "text-xs -top-1 -right-1 w-5 h-5", timer: "text-xs" },
|
|
7861
|
-
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" }
|
|
7959
|
+
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 },
|
|
7960
|
+
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 },
|
|
7961
|
+
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 }
|
|
7862
7962
|
};
|
|
7863
7963
|
variantStyles7 = {
|
|
7864
7964
|
buff: "border-success bg-success/20",
|
|
@@ -7869,6 +7969,7 @@ var init_StatusEffect = __esm({
|
|
|
7869
7969
|
}
|
|
7870
7970
|
});
|
|
7871
7971
|
function DamageNumber({
|
|
7972
|
+
assetUrl = DEFAULT_ASSET_URL10,
|
|
7872
7973
|
value = 42,
|
|
7873
7974
|
type = "damage",
|
|
7874
7975
|
size = "md",
|
|
@@ -7877,22 +7978,35 @@ function DamageNumber({
|
|
|
7877
7978
|
const displayText = type === "miss" ? "MISS" : type === "heal" ? `+${value}` : `${value}`;
|
|
7878
7979
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7879
7980
|
/* @__PURE__ */ jsx("style", { children: floatKeyframes }),
|
|
7880
|
-
/* @__PURE__ */
|
|
7981
|
+
/* @__PURE__ */ jsxs(
|
|
7881
7982
|
"span",
|
|
7882
7983
|
{
|
|
7883
7984
|
className: cn(
|
|
7884
|
-
"inline-
|
|
7985
|
+
"inline-flex items-center gap-0.5 font-mono select-none pointer-events-none",
|
|
7885
7986
|
sizeMap12[size],
|
|
7886
7987
|
typeStyles[type],
|
|
7887
7988
|
className
|
|
7888
7989
|
),
|
|
7889
7990
|
style: { animation: "damageFloat 1s ease-out forwards" },
|
|
7890
|
-
children:
|
|
7991
|
+
children: [
|
|
7992
|
+
assetUrl && /* @__PURE__ */ jsx(
|
|
7993
|
+
"img",
|
|
7994
|
+
{
|
|
7995
|
+
src: assetUrl,
|
|
7996
|
+
alt: "",
|
|
7997
|
+
width: 14,
|
|
7998
|
+
height: 14,
|
|
7999
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
8000
|
+
className: "flex-shrink-0"
|
|
8001
|
+
}
|
|
8002
|
+
),
|
|
8003
|
+
displayText
|
|
8004
|
+
]
|
|
7891
8005
|
}
|
|
7892
8006
|
)
|
|
7893
8007
|
] });
|
|
7894
8008
|
}
|
|
7895
|
-
var sizeMap12, typeStyles, floatKeyframes;
|
|
8009
|
+
var sizeMap12, typeStyles, floatKeyframes, DEFAULT_ASSET_URL10;
|
|
7896
8010
|
var init_DamageNumber = __esm({
|
|
7897
8011
|
"components/game/atoms/DamageNumber.tsx"() {
|
|
7898
8012
|
init_cn();
|
|
@@ -7914,6 +8028,7 @@ var init_DamageNumber = __esm({
|
|
|
7914
8028
|
100% { opacity: 0; transform: translateY(-32px) scale(0.8); }
|
|
7915
8029
|
}
|
|
7916
8030
|
`;
|
|
8031
|
+
DEFAULT_ASSET_URL10 = "https://almadar-kflow-assets.web.app/shared/effects/particles/spark_01.png";
|
|
7917
8032
|
DamageNumber.displayName = "DamageNumber";
|
|
7918
8033
|
}
|
|
7919
8034
|
});
|
|
@@ -8002,6 +8117,7 @@ var init_ChoiceButton = __esm({
|
|
|
8002
8117
|
}
|
|
8003
8118
|
});
|
|
8004
8119
|
function ActionButton({
|
|
8120
|
+
assetUrl = DEFAULT_ASSET_URL11,
|
|
8005
8121
|
label = "Attack",
|
|
8006
8122
|
icon,
|
|
8007
8123
|
cooldown = 0,
|
|
@@ -8041,13 +8157,23 @@ function ActionButton({
|
|
|
8041
8157
|
}
|
|
8042
8158
|
}
|
|
8043
8159
|
),
|
|
8044
|
-
|
|
8160
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
8161
|
+
"img",
|
|
8162
|
+
{
|
|
8163
|
+
src: assetUrl,
|
|
8164
|
+
alt: "",
|
|
8165
|
+
width: 16,
|
|
8166
|
+
height: 16,
|
|
8167
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
8168
|
+
className: cn("flex-shrink-0", sizes.icon)
|
|
8169
|
+
}
|
|
8170
|
+
) : icon ? /* @__PURE__ */ jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? (() => {
|
|
8045
8171
|
const I = resolveIcon(icon);
|
|
8046
8172
|
return I ? /* @__PURE__ */ jsx(I, { className: "w-4 h-4" }) : null;
|
|
8047
8173
|
})() : /* @__PURE__ */ (() => {
|
|
8048
8174
|
const I = icon;
|
|
8049
8175
|
return /* @__PURE__ */ jsx(I, { className: "w-4 h-4" });
|
|
8050
|
-
})() }),
|
|
8176
|
+
})() }) : null,
|
|
8051
8177
|
/* @__PURE__ */ jsx("span", { className: "relative z-10", children: label }),
|
|
8052
8178
|
hotkey && /* @__PURE__ */ jsx(
|
|
8053
8179
|
"span",
|
|
@@ -8063,7 +8189,7 @@ function ActionButton({
|
|
|
8063
8189
|
}
|
|
8064
8190
|
);
|
|
8065
8191
|
}
|
|
8066
|
-
var sizeMap13, variantStyles8;
|
|
8192
|
+
var sizeMap13, variantStyles8, DEFAULT_ASSET_URL11;
|
|
8067
8193
|
var init_ActionButton = __esm({
|
|
8068
8194
|
"components/game/atoms/ActionButton.tsx"() {
|
|
8069
8195
|
init_cn();
|
|
@@ -8078,6 +8204,7 @@ var init_ActionButton = __esm({
|
|
|
8078
8204
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary-hover border-border",
|
|
8079
8205
|
danger: "bg-error text-error-foreground hover:bg-error/90 border-error"
|
|
8080
8206
|
};
|
|
8207
|
+
DEFAULT_ASSET_URL11 = "https://almadar-kflow-assets.web.app/shared/effects/particles/slash_01.png";
|
|
8081
8208
|
ActionButton.displayName = "ActionButton";
|
|
8082
8209
|
}
|
|
8083
8210
|
});
|
|
@@ -8590,10 +8717,10 @@ var init_Container = __esm({
|
|
|
8590
8717
|
center = true,
|
|
8591
8718
|
className,
|
|
8592
8719
|
children,
|
|
8593
|
-
as:
|
|
8720
|
+
as: Component2 = "div"
|
|
8594
8721
|
}) => {
|
|
8595
8722
|
const resolvedSize = maxWidth ?? size ?? "lg";
|
|
8596
|
-
const Comp =
|
|
8723
|
+
const Comp = Component2;
|
|
8597
8724
|
return /* @__PURE__ */ jsx(
|
|
8598
8725
|
Comp,
|
|
8599
8726
|
{
|
|
@@ -11427,15 +11554,15 @@ function BattleBoard({
|
|
|
11427
11554
|
const board = boardEntity(entity) ?? {};
|
|
11428
11555
|
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
|
|
11429
11556
|
const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
|
|
11430
|
-
const boardWidth = num(board.boardWidth, 8);
|
|
11431
|
-
const boardHeight = num(board.boardHeight, 6);
|
|
11557
|
+
const boardWidth = num(board.gridWidth ?? board.boardWidth, 8);
|
|
11558
|
+
const boardHeight = num(board.gridHeight ?? board.boardHeight, 6);
|
|
11432
11559
|
const assetManifest = propAssetManifest ?? board.assetManifest;
|
|
11433
11560
|
const backgroundImage = board.backgroundImage;
|
|
11434
11561
|
const units = rows(board.units);
|
|
11435
11562
|
const selectedUnitId = board.selectedUnitId ?? null;
|
|
11436
11563
|
const currentPhase = str(board.phase) || "observation";
|
|
11437
11564
|
const currentTurn = num(board.turn, 1);
|
|
11438
|
-
const gameResult = board.
|
|
11565
|
+
const gameResult = board.result ?? null;
|
|
11439
11566
|
const eventBus = useEventBus();
|
|
11440
11567
|
const { t } = useTranslate();
|
|
11441
11568
|
const [hoveredTile, setHoveredTile] = useState(null);
|
|
@@ -11456,7 +11583,7 @@ function BattleBoard({
|
|
|
11456
11583
|
const validMoves = useMemo(() => {
|
|
11457
11584
|
if (!selectedUnit || currentPhase !== "movement") return [];
|
|
11458
11585
|
const moves = [];
|
|
11459
|
-
const range = num(
|
|
11586
|
+
const range = num(board.movementRange, 2);
|
|
11460
11587
|
const origin = unitPosition(selectedUnit);
|
|
11461
11588
|
for (let dy = -range; dy <= range; dy++) {
|
|
11462
11589
|
for (let dx = -range; dx <= range; dx++) {
|
|
@@ -19741,15 +19868,24 @@ function CastleBoard({
|
|
|
19741
19868
|
featureClickEvent,
|
|
19742
19869
|
unitClickEvent,
|
|
19743
19870
|
tileClickEvent,
|
|
19871
|
+
playAgainEvent,
|
|
19744
19872
|
className
|
|
19745
19873
|
}) {
|
|
19746
19874
|
const eventBus = useEventBus();
|
|
19875
|
+
const { t } = useTranslate();
|
|
19747
19876
|
const resolved = boardEntity(entity);
|
|
19748
19877
|
const tiles = propTiles ?? (Array.isArray(resolved?.tiles) ? resolved.tiles : []);
|
|
19749
19878
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
19750
19879
|
const units = propUnits ?? (Array.isArray(resolved?.units) ? resolved.units : []);
|
|
19751
19880
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
19752
19881
|
const backgroundImage = resolved?.backgroundImage;
|
|
19882
|
+
const gold = num(resolved?.gold);
|
|
19883
|
+
const health = num(resolved?.health);
|
|
19884
|
+
const maxHealth = num(resolved?.maxHealth);
|
|
19885
|
+
const wave = num(resolved?.wave);
|
|
19886
|
+
const tickCount = num(resolved?.tickCount);
|
|
19887
|
+
const buildings = rows(resolved?.buildings);
|
|
19888
|
+
const result = str(resolved?.result) || "none";
|
|
19753
19889
|
const [hoveredTile, setHoveredTile] = useState(null);
|
|
19754
19890
|
const [selectedFeature, setSelectedFeature] = useState(null);
|
|
19755
19891
|
const hoveredFeature = useMemo(() => {
|
|
@@ -19762,7 +19898,7 @@ function CastleBoard({
|
|
|
19762
19898
|
(u) => u.position?.x === hoveredTile.x && u.position?.y === hoveredTile.y
|
|
19763
19899
|
) ?? null;
|
|
19764
19900
|
}, [hoveredTile, units]);
|
|
19765
|
-
const maxY = Math.max(...tiles.map((
|
|
19901
|
+
const maxY = Math.max(...tiles.map((t2) => t2.y), 0);
|
|
19766
19902
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
19767
19903
|
const tileToScreen = useCallback(
|
|
19768
19904
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
@@ -19797,6 +19933,11 @@ function CastleBoard({
|
|
|
19797
19933
|
}
|
|
19798
19934
|
}, [units, onUnitClick, unitClickEvent, eventBus]);
|
|
19799
19935
|
const clearSelection = useCallback(() => setSelectedFeature(null), []);
|
|
19936
|
+
const handlePlayAgain = useCallback(() => {
|
|
19937
|
+
if (playAgainEvent) {
|
|
19938
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
19939
|
+
}
|
|
19940
|
+
}, [playAgainEvent, eventBus]);
|
|
19800
19941
|
const ctx = useMemo(
|
|
19801
19942
|
() => ({
|
|
19802
19943
|
hoveredTile,
|
|
@@ -19805,11 +19946,18 @@ function CastleBoard({
|
|
|
19805
19946
|
selectedFeature,
|
|
19806
19947
|
clearSelection,
|
|
19807
19948
|
tileToScreen,
|
|
19808
|
-
scale
|
|
19949
|
+
scale,
|
|
19950
|
+
gold,
|
|
19951
|
+
health,
|
|
19952
|
+
maxHealth,
|
|
19953
|
+
wave,
|
|
19954
|
+
tickCount,
|
|
19955
|
+
buildings,
|
|
19956
|
+
result
|
|
19809
19957
|
}),
|
|
19810
|
-
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale]
|
|
19958
|
+
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale, gold, health, maxHealth, wave, tickCount, buildings, result]
|
|
19811
19959
|
);
|
|
19812
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("castle-board min-h-screen flex flex-col bg-background", className), children: [
|
|
19960
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("castle-board relative min-h-screen flex flex-col bg-background", className), children: [
|
|
19813
19961
|
header && header(ctx),
|
|
19814
19962
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
|
|
19815
19963
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-auto p-4 relative", children: [
|
|
@@ -19834,7 +19982,29 @@ function CastleBoard({
|
|
|
19834
19982
|
] }),
|
|
19835
19983
|
sidePanel && /* @__PURE__ */ jsx("div", { className: "w-96 shrink-0 border-l border-border bg-surface overflow-y-auto", children: sidePanel(ctx) })
|
|
19836
19984
|
] }),
|
|
19837
|
-
footer && footer(ctx)
|
|
19985
|
+
footer && footer(ctx),
|
|
19986
|
+
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: [
|
|
19987
|
+
/* @__PURE__ */ jsx(
|
|
19988
|
+
Typography,
|
|
19989
|
+
{
|
|
19990
|
+
variant: "h2",
|
|
19991
|
+
className: cn(
|
|
19992
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
19993
|
+
result === "victory" ? "text-warning" : "text-error"
|
|
19994
|
+
),
|
|
19995
|
+
children: result === "victory" ? t("battle.victory") : t("battle.defeat")
|
|
19996
|
+
}
|
|
19997
|
+
),
|
|
19998
|
+
/* @__PURE__ */ jsx(
|
|
19999
|
+
Button,
|
|
20000
|
+
{
|
|
20001
|
+
variant: "primary",
|
|
20002
|
+
className: "px-8 py-3 font-semibold",
|
|
20003
|
+
onClick: handlePlayAgain,
|
|
20004
|
+
children: t("battle.playAgain")
|
|
20005
|
+
}
|
|
20006
|
+
)
|
|
20007
|
+
] }) })
|
|
19838
20008
|
] });
|
|
19839
20009
|
}
|
|
19840
20010
|
var init_CastleBoard = __esm({
|
|
@@ -19842,6 +20012,10 @@ var init_CastleBoard = __esm({
|
|
|
19842
20012
|
"use client";
|
|
19843
20013
|
init_cn();
|
|
19844
20014
|
init_useEventBus();
|
|
20015
|
+
init_Box();
|
|
20016
|
+
init_Button();
|
|
20017
|
+
init_Typography();
|
|
20018
|
+
init_Stack();
|
|
19845
20019
|
init_IsometricCanvas();
|
|
19846
20020
|
init_boardEntity();
|
|
19847
20021
|
init_isometric();
|
|
@@ -20718,22 +20892,16 @@ function ClassifierBoard({
|
|
|
20718
20892
|
const { emit } = useEventBus();
|
|
20719
20893
|
const { t } = useTranslate();
|
|
20720
20894
|
const resolved = boardEntity(entity);
|
|
20721
|
-
const [localAssignments, setLocalAssignments] = useState({});
|
|
20722
20895
|
const [headerError, setHeaderError] = useState(false);
|
|
20723
|
-
const [localSubmitted, setLocalSubmitted] = useState(false);
|
|
20724
|
-
const [localAttempts, setLocalAttempts] = useState(0);
|
|
20725
|
-
const [showHint, setShowHint] = useState(false);
|
|
20726
20896
|
const items = Array.isArray(resolved?.items) ? resolved.items : [];
|
|
20727
20897
|
const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
|
|
20728
|
-
const
|
|
20729
|
-
const
|
|
20730
|
-
const
|
|
20731
|
-
const assignments =
|
|
20732
|
-
if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
|
|
20898
|
+
const result = str(resolved?.result);
|
|
20899
|
+
const submitted = result === "win";
|
|
20900
|
+
const attempts = num(resolved?.attempts);
|
|
20901
|
+
const assignments = items.reduce((acc, item) => {
|
|
20902
|
+
if (item.assignedCategory != null && item.assignedCategory !== "") acc[item.id] = item.assignedCategory;
|
|
20733
20903
|
return acc;
|
|
20734
|
-
}, {})
|
|
20735
|
-
const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
|
|
20736
|
-
const submitted = entityDrivesResult || localSubmitted;
|
|
20904
|
+
}, {});
|
|
20737
20905
|
const unassignedItems = items.filter((item) => !assignments[item.id]);
|
|
20738
20906
|
const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
|
|
20739
20907
|
const results = submitted ? items.map((item) => ({
|
|
@@ -20741,54 +20909,25 @@ function ClassifierBoard({
|
|
|
20741
20909
|
assigned: assignments[item.id],
|
|
20742
20910
|
correct: assignments[item.id] === item.correctCategory
|
|
20743
20911
|
})) : [];
|
|
20744
|
-
const allCorrect =
|
|
20912
|
+
const allCorrect = result === "win";
|
|
20745
20913
|
const correctCount = results.filter((r) => r.correct).length;
|
|
20914
|
+
const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
|
|
20746
20915
|
const handleAssign = (itemId, categoryId) => {
|
|
20747
20916
|
if (submitted) return;
|
|
20748
|
-
if (assignEvent) {
|
|
20749
|
-
emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
20750
|
-
}
|
|
20751
|
-
if (!entityHasAssignments) {
|
|
20752
|
-
setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
|
|
20753
|
-
}
|
|
20917
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
20754
20918
|
};
|
|
20755
20919
|
const handleUnassign = (itemId) => {
|
|
20756
20920
|
if (submitted) return;
|
|
20757
|
-
if (
|
|
20758
|
-
setLocalAssignments((prev) => {
|
|
20759
|
-
const next = { ...prev };
|
|
20760
|
-
delete next[itemId];
|
|
20761
|
-
return next;
|
|
20762
|
-
});
|
|
20763
|
-
}
|
|
20921
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId: "" });
|
|
20764
20922
|
};
|
|
20765
|
-
const handleSubmit =
|
|
20766
|
-
if (checkEvent) {
|
|
20767
|
-
|
|
20768
|
-
|
|
20769
|
-
if (!entityDrivesResult) {
|
|
20770
|
-
setLocalSubmitted(true);
|
|
20771
|
-
setLocalAttempts((a) => a + 1);
|
|
20772
|
-
const correct = items.every((item) => assignments[item.id] === item.correctCategory);
|
|
20773
|
-
if (correct) {
|
|
20774
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
20775
|
-
}
|
|
20776
|
-
}
|
|
20777
|
-
}, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
|
|
20778
|
-
const handleReset = () => {
|
|
20779
|
-
if (!entityDrivesResult) setLocalSubmitted(false);
|
|
20780
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
20781
|
-
setShowHint(true);
|
|
20923
|
+
const handleSubmit = () => {
|
|
20924
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
20925
|
+
if (allAssigned && items.every((item) => assignments[item.id] === item.correctCategory)) {
|
|
20926
|
+
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
20782
20927
|
}
|
|
20783
20928
|
};
|
|
20784
20929
|
const handleFullReset = () => {
|
|
20785
|
-
if (playAgainEvent) {
|
|
20786
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
20787
|
-
}
|
|
20788
|
-
setLocalAssignments({});
|
|
20789
|
-
setLocalSubmitted(false);
|
|
20790
|
-
setLocalAttempts(0);
|
|
20791
|
-
setShowHint(false);
|
|
20930
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
20792
20931
|
};
|
|
20793
20932
|
if (!resolved) return null;
|
|
20794
20933
|
const theme = resolved.theme ?? void 0;
|
|
@@ -20827,17 +20966,17 @@ function ClassifierBoard({
|
|
|
20827
20966
|
/* @__PURE__ */ jsx(Badge, { size: "sm", children: catItems.length })
|
|
20828
20967
|
] }),
|
|
20829
20968
|
/* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-wrap min-h-[2rem]", children: catItems.map((item) => {
|
|
20830
|
-
const
|
|
20969
|
+
const result2 = results.find((r) => r.item.id === item.id);
|
|
20831
20970
|
return /* @__PURE__ */ jsxs(
|
|
20832
20971
|
Badge,
|
|
20833
20972
|
{
|
|
20834
20973
|
size: "sm",
|
|
20835
|
-
className: `cursor-pointer ${
|
|
20974
|
+
className: `cursor-pointer ${result2 ? result2.correct ? "border-success bg-success/10" : "border-error bg-error/10" : ""}`,
|
|
20836
20975
|
onClick: () => handleUnassign(item.id),
|
|
20837
20976
|
children: [
|
|
20838
20977
|
item.iconUrl && /* @__PURE__ */ jsx("img", { src: item.iconUrl, alt: "", className: "w-3 h-3 object-contain inline-block" }),
|
|
20839
20978
|
item.label,
|
|
20840
|
-
|
|
20979
|
+
result2 && /* @__PURE__ */ jsx(Icon, { icon: result2.correct ? CheckCircle : XCircle, size: "xs", className: result2.correct ? "text-success" : "text-error" })
|
|
20841
20980
|
]
|
|
20842
20981
|
},
|
|
20843
20982
|
item.id
|
|
@@ -20866,10 +21005,10 @@ function ClassifierBoard({
|
|
|
20866
21005
|
] }) }),
|
|
20867
21006
|
showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
|
|
20868
21007
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
20869
|
-
!submitted
|
|
21008
|
+
!submitted && /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
|
|
20870
21009
|
/* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
|
|
20871
21010
|
t("classifier.check")
|
|
20872
|
-
] })
|
|
21011
|
+
] }),
|
|
20873
21012
|
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
|
|
20874
21013
|
/* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
|
|
20875
21014
|
t("classifier.reset")
|
|
@@ -24587,7 +24726,7 @@ var init_Flex = __esm({
|
|
|
24587
24726
|
basis,
|
|
24588
24727
|
className,
|
|
24589
24728
|
children,
|
|
24590
|
-
as:
|
|
24729
|
+
as: Component2 = "div"
|
|
24591
24730
|
}) => {
|
|
24592
24731
|
const flexStyle = {};
|
|
24593
24732
|
if (grow !== void 0 || shrink !== void 0 || basis !== void 0) {
|
|
@@ -24599,7 +24738,7 @@ var init_Flex = __esm({
|
|
|
24599
24738
|
flexStyle.flexBasis = typeof basis === "number" ? `${basis}px` : basis;
|
|
24600
24739
|
}
|
|
24601
24740
|
}
|
|
24602
|
-
const Comp =
|
|
24741
|
+
const Comp = Component2;
|
|
24603
24742
|
return /* @__PURE__ */ jsx(
|
|
24604
24743
|
Comp,
|
|
24605
24744
|
{
|
|
@@ -24719,11 +24858,11 @@ var init_Grid = __esm({
|
|
|
24719
24858
|
className,
|
|
24720
24859
|
style,
|
|
24721
24860
|
children,
|
|
24722
|
-
as:
|
|
24861
|
+
as: Component2 = "div"
|
|
24723
24862
|
}) => {
|
|
24724
24863
|
const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
|
|
24725
24864
|
return React82__default.createElement(
|
|
24726
|
-
|
|
24865
|
+
Component2,
|
|
24727
24866
|
{
|
|
24728
24867
|
className: cn(
|
|
24729
24868
|
"grid",
|
|
@@ -26549,6 +26688,7 @@ var init_ProgressDots = __esm({
|
|
|
26549
26688
|
}
|
|
26550
26689
|
});
|
|
26551
26690
|
function StatBadge({
|
|
26691
|
+
assetUrl = DEFAULT_ASSET_URL12,
|
|
26552
26692
|
label,
|
|
26553
26693
|
value = 0,
|
|
26554
26694
|
max,
|
|
@@ -26572,7 +26712,17 @@ function StatBadge({
|
|
|
26572
26712
|
className
|
|
26573
26713
|
),
|
|
26574
26714
|
children: [
|
|
26575
|
-
|
|
26715
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
26716
|
+
"img",
|
|
26717
|
+
{
|
|
26718
|
+
src: assetUrl,
|
|
26719
|
+
alt: "",
|
|
26720
|
+
width: 16,
|
|
26721
|
+
height: 16,
|
|
26722
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
26723
|
+
className: "flex-shrink-0"
|
|
26724
|
+
}
|
|
26725
|
+
) : 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,
|
|
26576
26726
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground font-medium", children: label }),
|
|
26577
26727
|
format === "hearts" && max && /* @__PURE__ */ jsx(
|
|
26578
26728
|
HealthBar,
|
|
@@ -26605,7 +26755,7 @@ function StatBadge({
|
|
|
26605
26755
|
}
|
|
26606
26756
|
);
|
|
26607
26757
|
}
|
|
26608
|
-
var sizeMap17, variantMap2;
|
|
26758
|
+
var sizeMap17, variantMap2, DEFAULT_ASSET_URL12;
|
|
26609
26759
|
var init_StatBadge = __esm({
|
|
26610
26760
|
"components/game/molecules/StatBadge.tsx"() {
|
|
26611
26761
|
init_cn();
|
|
@@ -26624,6 +26774,7 @@ var init_StatBadge = __esm({
|
|
|
26624
26774
|
warning: "bg-warning/15 border-warning/40 text-foreground",
|
|
26625
26775
|
danger: "bg-error/15 border-error/40 text-foreground"
|
|
26626
26776
|
};
|
|
26777
|
+
DEFAULT_ASSET_URL12 = "https://almadar-kflow-assets.web.app/shared/effects/particles/light_01.png";
|
|
26627
26778
|
StatBadge.displayName = "StatBadge";
|
|
26628
26779
|
}
|
|
26629
26780
|
});
|
|
@@ -27280,6 +27431,7 @@ var init_TurnPanel = __esm({
|
|
|
27280
27431
|
}
|
|
27281
27432
|
});
|
|
27282
27433
|
function EnemyPlate({
|
|
27434
|
+
assetUrl = DEFAULT_ASSET_URL13,
|
|
27283
27435
|
name = "Shadow Guard",
|
|
27284
27436
|
health = 80,
|
|
27285
27437
|
maxHealth = 100,
|
|
@@ -27297,15 +27449,28 @@ function EnemyPlate({
|
|
|
27297
27449
|
),
|
|
27298
27450
|
children: [
|
|
27299
27451
|
/* @__PURE__ */ jsxs(Box, { className: "flex items-center justify-between gap-2", children: [
|
|
27300
|
-
/* @__PURE__ */
|
|
27301
|
-
|
|
27302
|
-
|
|
27303
|
-
|
|
27304
|
-
|
|
27305
|
-
|
|
27306
|
-
|
|
27307
|
-
|
|
27308
|
-
|
|
27452
|
+
/* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-1.5 min-w-0", children: [
|
|
27453
|
+
assetUrl && /* @__PURE__ */ jsx(
|
|
27454
|
+
"img",
|
|
27455
|
+
{
|
|
27456
|
+
src: assetUrl,
|
|
27457
|
+
alt: name,
|
|
27458
|
+
width: 24,
|
|
27459
|
+
height: 24,
|
|
27460
|
+
style: { objectFit: "cover", borderRadius: "50%" },
|
|
27461
|
+
className: "flex-shrink-0"
|
|
27462
|
+
}
|
|
27463
|
+
),
|
|
27464
|
+
/* @__PURE__ */ jsx(
|
|
27465
|
+
Typography,
|
|
27466
|
+
{
|
|
27467
|
+
variant: "caption",
|
|
27468
|
+
weight: "bold",
|
|
27469
|
+
className: "text-[var(--color-foreground)] truncate",
|
|
27470
|
+
children: name
|
|
27471
|
+
}
|
|
27472
|
+
)
|
|
27473
|
+
] }),
|
|
27309
27474
|
level != null && /* @__PURE__ */ jsxs(Badge, { variant: "neutral", size: "sm", children: [
|
|
27310
27475
|
"Lv.",
|
|
27311
27476
|
level
|
|
@@ -27350,7 +27515,7 @@ function EnemyPlate({
|
|
|
27350
27515
|
}
|
|
27351
27516
|
);
|
|
27352
27517
|
}
|
|
27353
|
-
var effectVariantMap2, DEFAULT_ENEMY_EFFECTS;
|
|
27518
|
+
var effectVariantMap2, DEFAULT_ENEMY_EFFECTS, DEFAULT_ASSET_URL13;
|
|
27354
27519
|
var init_EnemyPlate = __esm({
|
|
27355
27520
|
"components/game/molecules/EnemyPlate.tsx"() {
|
|
27356
27521
|
"use client";
|
|
@@ -27367,6 +27532,7 @@ var init_EnemyPlate = __esm({
|
|
|
27367
27532
|
DEFAULT_ENEMY_EFFECTS = [
|
|
27368
27533
|
{ icon: "flame", label: "Burn", variant: "debuff" }
|
|
27369
27534
|
];
|
|
27535
|
+
DEFAULT_ASSET_URL13 = "https://almadar-kflow-assets.web.app/shared/portraits/shadow-legion.png";
|
|
27370
27536
|
EnemyPlate.displayName = "EnemyPlate";
|
|
27371
27537
|
}
|
|
27372
27538
|
});
|
|
@@ -28898,13 +29064,13 @@ var init_MapView = __esm({
|
|
|
28898
29064
|
shadowSize: [41, 41]
|
|
28899
29065
|
});
|
|
28900
29066
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
28901
|
-
const { useEffect:
|
|
29067
|
+
const { useEffect: useEffect80, useRef: useRef74, useCallback: useCallback118, useState: useState112 } = React82__default;
|
|
28902
29068
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
28903
|
-
const { useEventBus:
|
|
29069
|
+
const { useEventBus: useEventBus4 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
28904
29070
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
28905
29071
|
const map = useMap();
|
|
28906
|
-
const prevRef =
|
|
28907
|
-
|
|
29072
|
+
const prevRef = useRef74({ centerLat, centerLng, zoom });
|
|
29073
|
+
useEffect80(() => {
|
|
28908
29074
|
const prev = prevRef.current;
|
|
28909
29075
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
28910
29076
|
map.setView([centerLat, centerLng], zoom);
|
|
@@ -28915,7 +29081,7 @@ var init_MapView = __esm({
|
|
|
28915
29081
|
}
|
|
28916
29082
|
function MapClickHandler({ onMapClick }) {
|
|
28917
29083
|
const map = useMap();
|
|
28918
|
-
|
|
29084
|
+
useEffect80(() => {
|
|
28919
29085
|
if (!onMapClick) return;
|
|
28920
29086
|
const handler = (e) => {
|
|
28921
29087
|
onMapClick(e.latlng.lat, e.latlng.lng);
|
|
@@ -28942,9 +29108,9 @@ var init_MapView = __esm({
|
|
|
28942
29108
|
className,
|
|
28943
29109
|
showAttribution = true
|
|
28944
29110
|
}) {
|
|
28945
|
-
const eventBus =
|
|
28946
|
-
const [clickedPosition, setClickedPosition] =
|
|
28947
|
-
const handleMapClick =
|
|
29111
|
+
const eventBus = useEventBus4();
|
|
29112
|
+
const [clickedPosition, setClickedPosition] = useState112(null);
|
|
29113
|
+
const handleMapClick = useCallback118((lat, lng) => {
|
|
28948
29114
|
if (showClickedPin) {
|
|
28949
29115
|
setClickedPosition({ lat, lng });
|
|
28950
29116
|
}
|
|
@@ -28953,7 +29119,7 @@ var init_MapView = __esm({
|
|
|
28953
29119
|
eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
|
|
28954
29120
|
}
|
|
28955
29121
|
}, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
|
|
28956
|
-
const handleMarkerClick =
|
|
29122
|
+
const handleMarkerClick = useCallback118((marker) => {
|
|
28957
29123
|
onMarkerClick?.(marker);
|
|
28958
29124
|
if (markerClickEvent) {
|
|
28959
29125
|
eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
|
|
@@ -36080,10 +36246,10 @@ var init_Section = __esm({
|
|
|
36080
36246
|
children,
|
|
36081
36247
|
headerClassName,
|
|
36082
36248
|
contentClassName,
|
|
36083
|
-
as:
|
|
36249
|
+
as: Component2 = "section"
|
|
36084
36250
|
}) => {
|
|
36085
36251
|
const hasHeader = title || description || action;
|
|
36086
|
-
const Comp =
|
|
36252
|
+
const Comp = Component2;
|
|
36087
36253
|
return /* @__PURE__ */ jsxs(
|
|
36088
36254
|
Comp,
|
|
36089
36255
|
{
|
|
@@ -38184,44 +38350,27 @@ function DebuggerBoard({
|
|
|
38184
38350
|
const { t } = useTranslate();
|
|
38185
38351
|
const resolved = boardEntity(entity);
|
|
38186
38352
|
const [headerError, setHeaderError] = useState(false);
|
|
38187
|
-
const [showHint, setShowHint] = useState(false);
|
|
38188
38353
|
const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
|
|
38189
|
-
const result = resolved?.result
|
|
38354
|
+
const result = str(resolved?.result) || "none";
|
|
38190
38355
|
const attempts = num(resolved?.attempts);
|
|
38191
|
-
const submitted = result
|
|
38356
|
+
const submitted = result === "win";
|
|
38357
|
+
const showHint = !submitted && attempts >= 2 && Boolean(str(resolved?.hint));
|
|
38192
38358
|
const bugLines = lines.filter((l) => l.isBug);
|
|
38193
38359
|
const flaggedLines = lines.filter((l) => l.isFlagged);
|
|
38194
38360
|
const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
|
|
38195
38361
|
const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
|
|
38196
|
-
const allCorrect =
|
|
38362
|
+
const allCorrect = submitted;
|
|
38197
38363
|
const toggleLine = (lineId) => {
|
|
38198
38364
|
if (submitted) return;
|
|
38199
|
-
if (toggleFlagEvent) {
|
|
38200
|
-
emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
38201
|
-
}
|
|
38365
|
+
if (toggleFlagEvent) emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
38202
38366
|
};
|
|
38203
38367
|
const handleSubmit = useCallback(() => {
|
|
38204
|
-
if (checkEvent) {
|
|
38205
|
-
emit(`UI:${checkEvent}`, {});
|
|
38206
|
-
}
|
|
38368
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
38207
38369
|
const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
|
|
38208
|
-
if (correct) {
|
|
38209
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
38210
|
-
}
|
|
38370
|
+
if (correct) emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
38211
38371
|
}, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
|
|
38212
38372
|
const handleReset = () => {
|
|
38213
|
-
if (playAgainEvent) {
|
|
38214
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
38215
|
-
}
|
|
38216
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
38217
|
-
setShowHint(true);
|
|
38218
|
-
}
|
|
38219
|
-
};
|
|
38220
|
-
const handleFullReset = () => {
|
|
38221
|
-
if (playAgainEvent) {
|
|
38222
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
38223
|
-
}
|
|
38224
|
-
setShowHint(false);
|
|
38373
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
38225
38374
|
};
|
|
38226
38375
|
if (!resolved) return null;
|
|
38227
38376
|
const theme = resolved.theme ?? void 0;
|
|
@@ -38245,7 +38394,7 @@ function DebuggerBoard({
|
|
|
38245
38394
|
/* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
|
|
38246
38395
|
] }),
|
|
38247
38396
|
/* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) }),
|
|
38248
|
-
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(
|
|
38397
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(lines.filter((l) => l.isBug).length) }) })
|
|
38249
38398
|
] }) }),
|
|
38250
38399
|
/* @__PURE__ */ jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsx(VStack, { gap: "none", children: lines.map((line, i) => {
|
|
38251
38400
|
const isFlagged = !!line.isFlagged;
|
|
@@ -38295,11 +38444,11 @@ function DebuggerBoard({
|
|
|
38295
38444
|
] }) }),
|
|
38296
38445
|
showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
|
|
38297
38446
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
38298
|
-
!submitted
|
|
38447
|
+
!submitted && /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
|
|
38299
38448
|
/* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
|
|
38300
38449
|
t("debugger.submit")
|
|
38301
|
-
] })
|
|
38302
|
-
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick:
|
|
38450
|
+
] }),
|
|
38451
|
+
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleReset, children: [
|
|
38303
38452
|
/* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
|
|
38304
38453
|
t("debugger.reset")
|
|
38305
38454
|
] })
|
|
@@ -39300,39 +39449,40 @@ function EventHandlerBoard({
|
|
|
39300
39449
|
stepDurationMs = 800,
|
|
39301
39450
|
playEvent,
|
|
39302
39451
|
completeEvent,
|
|
39452
|
+
editRuleEvent,
|
|
39453
|
+
playAgainEvent,
|
|
39303
39454
|
className
|
|
39304
39455
|
}) {
|
|
39305
39456
|
const { emit } = useEventBus();
|
|
39306
39457
|
const { t } = useTranslate();
|
|
39307
39458
|
const resolved = boardEntity(entity);
|
|
39308
|
-
const
|
|
39309
|
-
const
|
|
39459
|
+
const objects = rows(resolved?.objects);
|
|
39460
|
+
const entityResult = str(resolved?.result) || "none";
|
|
39461
|
+
const isSuccess = entityResult === "win";
|
|
39462
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
39310
39463
|
const [selectedObjectId, setSelectedObjectId] = useState(
|
|
39311
|
-
|
|
39464
|
+
objects[0] ? objId(objects[0]) : null
|
|
39312
39465
|
);
|
|
39313
39466
|
const [headerError, setHeaderError] = useState(false);
|
|
39314
|
-
const [
|
|
39467
|
+
const [isPlaying, setIsPlaying] = useState(false);
|
|
39315
39468
|
const [eventLog, setEventLog] = useState([]);
|
|
39316
|
-
const [attempts, setAttempts] = useState(0);
|
|
39317
39469
|
const timerRef = useRef(null);
|
|
39318
39470
|
const logIdCounter = useRef(0);
|
|
39319
39471
|
useEffect(() => () => {
|
|
39320
39472
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39321
39473
|
}, []);
|
|
39322
|
-
const selectedObject = objects.find((o) => objId(o) === selectedObjectId)
|
|
39474
|
+
const selectedObject = objects.find((o) => objId(o) === selectedObjectId) ?? null;
|
|
39323
39475
|
const handleRulesChange = useCallback((objectId, rules) => {
|
|
39324
|
-
|
|
39325
|
-
|
|
39326
|
-
));
|
|
39327
|
-
}, []);
|
|
39476
|
+
if (editRuleEvent) emit(`UI:${editRuleEvent}`, { objectId, rules });
|
|
39477
|
+
}, [editRuleEvent, emit]);
|
|
39328
39478
|
const addLogEntry = useCallback((icon, message, status = "done") => {
|
|
39329
39479
|
const id = `log-${logIdCounter.current++}`;
|
|
39330
39480
|
setEventLog((prev) => [...prev, { id, timestamp: Date.now(), icon, message, status }]);
|
|
39331
39481
|
}, []);
|
|
39332
39482
|
const handlePlay = useCallback(() => {
|
|
39333
|
-
if (
|
|
39483
|
+
if (isPlaying || isSuccess) return;
|
|
39334
39484
|
if (playEvent) emit(`UI:${playEvent}`, {});
|
|
39335
|
-
|
|
39485
|
+
setIsPlaying(true);
|
|
39336
39486
|
setEventLog([]);
|
|
39337
39487
|
const allRules = [];
|
|
39338
39488
|
objects.forEach((obj) => {
|
|
@@ -39348,15 +39498,8 @@ function EventHandlerBoard({
|
|
|
39348
39498
|
let goalReached = false;
|
|
39349
39499
|
const processNext = () => {
|
|
39350
39500
|
if (eventQueue.length === 0 || stepIdx > 20) {
|
|
39351
|
-
|
|
39352
|
-
|
|
39353
|
-
if (completeEvent) {
|
|
39354
|
-
emit(`UI:${completeEvent}`, { success: true });
|
|
39355
|
-
}
|
|
39356
|
-
} else {
|
|
39357
|
-
setAttempts((prev) => prev + 1);
|
|
39358
|
-
setPlayState("fail");
|
|
39359
|
-
}
|
|
39501
|
+
setIsPlaying(false);
|
|
39502
|
+
if (goalReached && completeEvent) emit(`UI:${completeEvent}`, { success: true });
|
|
39360
39503
|
return;
|
|
39361
39504
|
}
|
|
39362
39505
|
const currentEvent = eventQueue.shift();
|
|
@@ -39387,21 +39530,19 @@ function EventHandlerBoard({
|
|
|
39387
39530
|
addLogEntry("\u{1F3AC}", t("eventHandler.simulationStarted", { events: triggers.join(", ") }), "active");
|
|
39388
39531
|
}
|
|
39389
39532
|
timerRef.current = setTimeout(processNext, stepDurationMs);
|
|
39390
|
-
}, [
|
|
39533
|
+
}, [isPlaying, isSuccess, objects, resolved, stepDurationMs, playEvent, completeEvent, emit, addLogEntry, t]);
|
|
39391
39534
|
const handleTryAgain = useCallback(() => {
|
|
39392
39535
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39393
|
-
|
|
39536
|
+
setIsPlaying(false);
|
|
39394
39537
|
setEventLog([]);
|
|
39395
39538
|
}, []);
|
|
39396
39539
|
const handleReset = useCallback(() => {
|
|
39397
39540
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
39398
|
-
|
|
39399
|
-
setObjects([...resetObjects]);
|
|
39400
|
-
setPlayState("editing");
|
|
39541
|
+
setIsPlaying(false);
|
|
39401
39542
|
setEventLog([]);
|
|
39402
|
-
setSelectedObjectId(
|
|
39403
|
-
|
|
39404
|
-
}, [
|
|
39543
|
+
setSelectedObjectId(objects[0] ? objId(objects[0]) : null);
|
|
39544
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
39545
|
+
}, [objects, playAgainEvent, emit]);
|
|
39405
39546
|
if (!resolved) return null;
|
|
39406
39547
|
const objectViewers = objects.map((obj) => {
|
|
39407
39548
|
const states = objStates(obj);
|
|
@@ -39470,12 +39611,12 @@ function EventHandlerBoard({
|
|
|
39470
39611
|
{
|
|
39471
39612
|
object: selectedObject,
|
|
39472
39613
|
onRulesChange: handleRulesChange,
|
|
39473
|
-
disabled:
|
|
39614
|
+
disabled: isPlaying
|
|
39474
39615
|
}
|
|
39475
39616
|
),
|
|
39476
39617
|
eventLog.length > 0 && /* @__PURE__ */ jsx(EventLog, { entries: eventLog }),
|
|
39477
|
-
|
|
39478
|
-
|
|
39618
|
+
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") }) }),
|
|
39619
|
+
!isPlaying && !isSuccess && attempts > 0 && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
39479
39620
|
/* @__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) }) }),
|
|
39480
39621
|
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: [
|
|
39481
39622
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
@@ -39483,12 +39624,12 @@ function EventHandlerBoard({
|
|
|
39483
39624
|
] }) })
|
|
39484
39625
|
] }),
|
|
39485
39626
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
|
|
39486
|
-
|
|
39627
|
+
!isPlaying && !isSuccess && attempts > 0 ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsx(
|
|
39487
39628
|
Button,
|
|
39488
39629
|
{
|
|
39489
39630
|
variant: "primary",
|
|
39490
39631
|
onClick: handlePlay,
|
|
39491
|
-
disabled:
|
|
39632
|
+
disabled: isPlaying || isSuccess,
|
|
39492
39633
|
children: "\u25B6 " + t("game.play")
|
|
39493
39634
|
}
|
|
39494
39635
|
),
|
|
@@ -40673,6 +40814,1568 @@ var init_GameAudioToggle = __esm({
|
|
|
40673
40814
|
GameAudioToggle.displayName = "GameAudioToggle";
|
|
40674
40815
|
}
|
|
40675
40816
|
});
|
|
40817
|
+
function detectAssetRoot(modelUrl) {
|
|
40818
|
+
const idx = modelUrl.indexOf("/3d/");
|
|
40819
|
+
if (idx !== -1) {
|
|
40820
|
+
return modelUrl.substring(0, idx + 4);
|
|
40821
|
+
}
|
|
40822
|
+
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
40823
|
+
}
|
|
40824
|
+
function createGLTFLoaderForUrl(url) {
|
|
40825
|
+
const loader = new GLTFLoader();
|
|
40826
|
+
loader.setResourcePath(detectAssetRoot(url));
|
|
40827
|
+
return loader;
|
|
40828
|
+
}
|
|
40829
|
+
var AssetLoader;
|
|
40830
|
+
var init_AssetLoader = __esm({
|
|
40831
|
+
"components/game/molecules/three/loaders/AssetLoader.ts"() {
|
|
40832
|
+
AssetLoader = class {
|
|
40833
|
+
constructor() {
|
|
40834
|
+
__publicField(this, "objLoader");
|
|
40835
|
+
__publicField(this, "textureLoader");
|
|
40836
|
+
__publicField(this, "modelCache");
|
|
40837
|
+
__publicField(this, "textureCache");
|
|
40838
|
+
__publicField(this, "loadingPromises");
|
|
40839
|
+
this.objLoader = new OBJLoader();
|
|
40840
|
+
this.textureLoader = new THREE.TextureLoader();
|
|
40841
|
+
this.modelCache = /* @__PURE__ */ new Map();
|
|
40842
|
+
this.textureCache = /* @__PURE__ */ new Map();
|
|
40843
|
+
this.loadingPromises = /* @__PURE__ */ new Map();
|
|
40844
|
+
}
|
|
40845
|
+
/**
|
|
40846
|
+
* Load a GLB/GLTF model
|
|
40847
|
+
* @param url - URL to the .glb or .gltf file
|
|
40848
|
+
* @returns Promise with loaded model scene and animations
|
|
40849
|
+
*/
|
|
40850
|
+
async loadModel(url) {
|
|
40851
|
+
if (this.modelCache.has(url)) {
|
|
40852
|
+
return this.modelCache.get(url);
|
|
40853
|
+
}
|
|
40854
|
+
if (this.loadingPromises.has(url)) {
|
|
40855
|
+
return this.loadingPromises.get(url);
|
|
40856
|
+
}
|
|
40857
|
+
const loader = createGLTFLoaderForUrl(url);
|
|
40858
|
+
const loadPromise = loader.loadAsync(url).then((gltf) => {
|
|
40859
|
+
const result = {
|
|
40860
|
+
scene: gltf.scene,
|
|
40861
|
+
animations: gltf.animations || []
|
|
40862
|
+
};
|
|
40863
|
+
this.modelCache.set(url, result);
|
|
40864
|
+
this.loadingPromises.delete(url);
|
|
40865
|
+
return result;
|
|
40866
|
+
}).catch((error) => {
|
|
40867
|
+
this.loadingPromises.delete(url);
|
|
40868
|
+
throw new Error(`Failed to load model ${url}: ${error.message}`);
|
|
40869
|
+
});
|
|
40870
|
+
this.loadingPromises.set(url, loadPromise);
|
|
40871
|
+
return loadPromise;
|
|
40872
|
+
}
|
|
40873
|
+
/**
|
|
40874
|
+
* Load an OBJ model (fallback for non-GLB assets)
|
|
40875
|
+
* @param url - URL to the .obj file
|
|
40876
|
+
* @returns Promise with loaded object group
|
|
40877
|
+
*/
|
|
40878
|
+
async loadOBJ(url) {
|
|
40879
|
+
if (this.modelCache.has(url)) {
|
|
40880
|
+
return this.modelCache.get(url).scene;
|
|
40881
|
+
}
|
|
40882
|
+
if (this.loadingPromises.has(url)) {
|
|
40883
|
+
const result = await this.loadingPromises.get(url);
|
|
40884
|
+
return result.scene;
|
|
40885
|
+
}
|
|
40886
|
+
const loadPromise = this.objLoader.loadAsync(url).then((group) => {
|
|
40887
|
+
const result = {
|
|
40888
|
+
scene: group,
|
|
40889
|
+
animations: []
|
|
40890
|
+
};
|
|
40891
|
+
this.modelCache.set(url, result);
|
|
40892
|
+
this.loadingPromises.delete(url);
|
|
40893
|
+
return result;
|
|
40894
|
+
}).catch((error) => {
|
|
40895
|
+
this.loadingPromises.delete(url);
|
|
40896
|
+
throw new Error(`Failed to load OBJ ${url}: ${error.message}`);
|
|
40897
|
+
});
|
|
40898
|
+
this.loadingPromises.set(url, loadPromise);
|
|
40899
|
+
return (await loadPromise).scene;
|
|
40900
|
+
}
|
|
40901
|
+
/**
|
|
40902
|
+
* Load a texture
|
|
40903
|
+
* @param url - URL to the texture image
|
|
40904
|
+
* @returns Promise with loaded texture
|
|
40905
|
+
*/
|
|
40906
|
+
async loadTexture(url) {
|
|
40907
|
+
if (this.textureCache.has(url)) {
|
|
40908
|
+
return this.textureCache.get(url);
|
|
40909
|
+
}
|
|
40910
|
+
if (this.loadingPromises.has(`texture:${url}`)) {
|
|
40911
|
+
return this.loadingPromises.get(`texture:${url}`);
|
|
40912
|
+
}
|
|
40913
|
+
const loadPromise = this.textureLoader.loadAsync(url).then((texture) => {
|
|
40914
|
+
texture.colorSpace = THREE.SRGBColorSpace;
|
|
40915
|
+
this.textureCache.set(url, texture);
|
|
40916
|
+
this.loadingPromises.delete(`texture:${url}`);
|
|
40917
|
+
return texture;
|
|
40918
|
+
}).catch((error) => {
|
|
40919
|
+
this.loadingPromises.delete(`texture:${url}`);
|
|
40920
|
+
throw new Error(`Failed to load texture ${url}: ${error.message}`);
|
|
40921
|
+
});
|
|
40922
|
+
this.loadingPromises.set(`texture:${url}`, loadPromise);
|
|
40923
|
+
return loadPromise;
|
|
40924
|
+
}
|
|
40925
|
+
/**
|
|
40926
|
+
* Preload multiple assets
|
|
40927
|
+
* @param urls - Array of asset URLs to preload
|
|
40928
|
+
* @returns Promise that resolves when all assets are loaded
|
|
40929
|
+
*/
|
|
40930
|
+
async preload(urls) {
|
|
40931
|
+
const promises = urls.map((url) => {
|
|
40932
|
+
if (url.endsWith(".glb") || url.endsWith(".gltf")) {
|
|
40933
|
+
return this.loadModel(url).catch(() => null);
|
|
40934
|
+
} else if (url.endsWith(".obj")) {
|
|
40935
|
+
return this.loadOBJ(url).catch(() => null);
|
|
40936
|
+
} else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
|
|
40937
|
+
return this.loadTexture(url).catch(() => null);
|
|
40938
|
+
}
|
|
40939
|
+
return Promise.resolve(null);
|
|
40940
|
+
});
|
|
40941
|
+
await Promise.all(promises);
|
|
40942
|
+
}
|
|
40943
|
+
/**
|
|
40944
|
+
* Check if a model is cached
|
|
40945
|
+
* @param url - Model URL
|
|
40946
|
+
*/
|
|
40947
|
+
hasModel(url) {
|
|
40948
|
+
return this.modelCache.has(url);
|
|
40949
|
+
}
|
|
40950
|
+
/**
|
|
40951
|
+
* Check if a texture is cached
|
|
40952
|
+
* @param url - Texture URL
|
|
40953
|
+
*/
|
|
40954
|
+
hasTexture(url) {
|
|
40955
|
+
return this.textureCache.has(url);
|
|
40956
|
+
}
|
|
40957
|
+
/**
|
|
40958
|
+
* Get cached model (throws if not cached)
|
|
40959
|
+
* @param url - Model URL
|
|
40960
|
+
*/
|
|
40961
|
+
getModel(url) {
|
|
40962
|
+
const model = this.modelCache.get(url);
|
|
40963
|
+
if (!model) {
|
|
40964
|
+
throw new Error(`Model ${url} not in cache`);
|
|
40965
|
+
}
|
|
40966
|
+
return model;
|
|
40967
|
+
}
|
|
40968
|
+
/**
|
|
40969
|
+
* Get cached texture (throws if not cached)
|
|
40970
|
+
* @param url - Texture URL
|
|
40971
|
+
*/
|
|
40972
|
+
getTexture(url) {
|
|
40973
|
+
const texture = this.textureCache.get(url);
|
|
40974
|
+
if (!texture) {
|
|
40975
|
+
throw new Error(`Texture ${url} not in cache`);
|
|
40976
|
+
}
|
|
40977
|
+
return texture;
|
|
40978
|
+
}
|
|
40979
|
+
/**
|
|
40980
|
+
* Clear all caches
|
|
40981
|
+
*/
|
|
40982
|
+
clearCache() {
|
|
40983
|
+
this.textureCache.forEach((texture) => {
|
|
40984
|
+
texture.dispose();
|
|
40985
|
+
});
|
|
40986
|
+
this.modelCache.forEach((model) => {
|
|
40987
|
+
model.scene.traverse((child) => {
|
|
40988
|
+
if (child instanceof THREE.Mesh) {
|
|
40989
|
+
child.geometry.dispose();
|
|
40990
|
+
if (Array.isArray(child.material)) {
|
|
40991
|
+
child.material.forEach((m) => m.dispose());
|
|
40992
|
+
} else {
|
|
40993
|
+
child.material.dispose();
|
|
40994
|
+
}
|
|
40995
|
+
}
|
|
40996
|
+
});
|
|
40997
|
+
});
|
|
40998
|
+
this.modelCache.clear();
|
|
40999
|
+
this.textureCache.clear();
|
|
41000
|
+
this.loadingPromises.clear();
|
|
41001
|
+
}
|
|
41002
|
+
/**
|
|
41003
|
+
* Get cache statistics
|
|
41004
|
+
*/
|
|
41005
|
+
getStats() {
|
|
41006
|
+
return {
|
|
41007
|
+
models: this.modelCache.size,
|
|
41008
|
+
textures: this.textureCache.size,
|
|
41009
|
+
loading: this.loadingPromises.size
|
|
41010
|
+
};
|
|
41011
|
+
}
|
|
41012
|
+
};
|
|
41013
|
+
new AssetLoader();
|
|
41014
|
+
}
|
|
41015
|
+
});
|
|
41016
|
+
function useAssetLoader(options = {}) {
|
|
41017
|
+
const { preloadUrls = [], loader: customLoader } = options;
|
|
41018
|
+
const loaderRef = useRef(customLoader || new AssetLoader());
|
|
41019
|
+
const [state, setState] = useState({
|
|
41020
|
+
isLoading: false,
|
|
41021
|
+
progress: 0,
|
|
41022
|
+
loaded: 0,
|
|
41023
|
+
total: 0,
|
|
41024
|
+
errors: []
|
|
41025
|
+
});
|
|
41026
|
+
useEffect(() => {
|
|
41027
|
+
if (preloadUrls.length > 0) {
|
|
41028
|
+
preload(preloadUrls);
|
|
41029
|
+
}
|
|
41030
|
+
}, []);
|
|
41031
|
+
const updateProgress = useCallback((loaded, total) => {
|
|
41032
|
+
setState((prev) => ({
|
|
41033
|
+
...prev,
|
|
41034
|
+
loaded,
|
|
41035
|
+
total,
|
|
41036
|
+
progress: total > 0 ? Math.round(loaded / total * 100) : 0
|
|
41037
|
+
}));
|
|
41038
|
+
}, []);
|
|
41039
|
+
const loadModel = useCallback(
|
|
41040
|
+
async (url) => {
|
|
41041
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
41042
|
+
try {
|
|
41043
|
+
const model = await loaderRef.current.loadModel(url);
|
|
41044
|
+
setState((prev) => ({
|
|
41045
|
+
...prev,
|
|
41046
|
+
isLoading: false,
|
|
41047
|
+
loaded: prev.loaded + 1
|
|
41048
|
+
}));
|
|
41049
|
+
return model;
|
|
41050
|
+
} catch (error) {
|
|
41051
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
41052
|
+
setState((prev) => ({
|
|
41053
|
+
...prev,
|
|
41054
|
+
isLoading: false,
|
|
41055
|
+
errors: [...prev.errors, errorMsg]
|
|
41056
|
+
}));
|
|
41057
|
+
throw error;
|
|
41058
|
+
}
|
|
41059
|
+
},
|
|
41060
|
+
[]
|
|
41061
|
+
);
|
|
41062
|
+
const loadOBJ = useCallback(
|
|
41063
|
+
async (url) => {
|
|
41064
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
41065
|
+
try {
|
|
41066
|
+
const model = await loaderRef.current.loadOBJ(url);
|
|
41067
|
+
setState((prev) => ({
|
|
41068
|
+
...prev,
|
|
41069
|
+
isLoading: false,
|
|
41070
|
+
loaded: prev.loaded + 1
|
|
41071
|
+
}));
|
|
41072
|
+
return model;
|
|
41073
|
+
} catch (error) {
|
|
41074
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
41075
|
+
setState((prev) => ({
|
|
41076
|
+
...prev,
|
|
41077
|
+
isLoading: false,
|
|
41078
|
+
errors: [...prev.errors, errorMsg]
|
|
41079
|
+
}));
|
|
41080
|
+
throw error;
|
|
41081
|
+
}
|
|
41082
|
+
},
|
|
41083
|
+
[]
|
|
41084
|
+
);
|
|
41085
|
+
const loadTexture = useCallback(
|
|
41086
|
+
async (url) => {
|
|
41087
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
41088
|
+
try {
|
|
41089
|
+
const texture = await loaderRef.current.loadTexture(url);
|
|
41090
|
+
setState((prev) => ({
|
|
41091
|
+
...prev,
|
|
41092
|
+
isLoading: false,
|
|
41093
|
+
loaded: prev.loaded + 1
|
|
41094
|
+
}));
|
|
41095
|
+
return texture;
|
|
41096
|
+
} catch (error) {
|
|
41097
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
41098
|
+
setState((prev) => ({
|
|
41099
|
+
...prev,
|
|
41100
|
+
isLoading: false,
|
|
41101
|
+
errors: [...prev.errors, errorMsg]
|
|
41102
|
+
}));
|
|
41103
|
+
throw error;
|
|
41104
|
+
}
|
|
41105
|
+
},
|
|
41106
|
+
[]
|
|
41107
|
+
);
|
|
41108
|
+
const preload = useCallback(
|
|
41109
|
+
async (urls) => {
|
|
41110
|
+
setState((prev) => ({
|
|
41111
|
+
...prev,
|
|
41112
|
+
isLoading: true,
|
|
41113
|
+
total: urls.length,
|
|
41114
|
+
loaded: 0,
|
|
41115
|
+
errors: []
|
|
41116
|
+
}));
|
|
41117
|
+
let completed = 0;
|
|
41118
|
+
const errors = [];
|
|
41119
|
+
await Promise.all(
|
|
41120
|
+
urls.map(async (url) => {
|
|
41121
|
+
try {
|
|
41122
|
+
if (url.endsWith(".glb") || url.endsWith(".gltf")) {
|
|
41123
|
+
await loaderRef.current.loadModel(url);
|
|
41124
|
+
} else if (url.endsWith(".obj")) {
|
|
41125
|
+
await loaderRef.current.loadOBJ(url);
|
|
41126
|
+
} else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
|
|
41127
|
+
await loaderRef.current.loadTexture(url);
|
|
41128
|
+
}
|
|
41129
|
+
completed++;
|
|
41130
|
+
updateProgress(completed, urls.length);
|
|
41131
|
+
} catch (error) {
|
|
41132
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
41133
|
+
errors.push(`${url}: ${errorMsg}`);
|
|
41134
|
+
completed++;
|
|
41135
|
+
updateProgress(completed, urls.length);
|
|
41136
|
+
}
|
|
41137
|
+
})
|
|
41138
|
+
);
|
|
41139
|
+
setState((prev) => ({
|
|
41140
|
+
...prev,
|
|
41141
|
+
isLoading: false,
|
|
41142
|
+
errors
|
|
41143
|
+
}));
|
|
41144
|
+
},
|
|
41145
|
+
[updateProgress]
|
|
41146
|
+
);
|
|
41147
|
+
const hasModel = useCallback((url) => {
|
|
41148
|
+
return loaderRef.current.hasModel(url);
|
|
41149
|
+
}, []);
|
|
41150
|
+
const hasTexture = useCallback((url) => {
|
|
41151
|
+
return loaderRef.current.hasTexture(url);
|
|
41152
|
+
}, []);
|
|
41153
|
+
const getModel = useCallback((url) => {
|
|
41154
|
+
try {
|
|
41155
|
+
return loaderRef.current.getModel(url);
|
|
41156
|
+
} catch {
|
|
41157
|
+
return void 0;
|
|
41158
|
+
}
|
|
41159
|
+
}, []);
|
|
41160
|
+
const getTexture = useCallback((url) => {
|
|
41161
|
+
try {
|
|
41162
|
+
return loaderRef.current.getTexture(url);
|
|
41163
|
+
} catch {
|
|
41164
|
+
return void 0;
|
|
41165
|
+
}
|
|
41166
|
+
}, []);
|
|
41167
|
+
const clearCache = useCallback(() => {
|
|
41168
|
+
loaderRef.current.clearCache();
|
|
41169
|
+
setState({
|
|
41170
|
+
isLoading: false,
|
|
41171
|
+
progress: 0,
|
|
41172
|
+
loaded: 0,
|
|
41173
|
+
total: 0,
|
|
41174
|
+
errors: []
|
|
41175
|
+
});
|
|
41176
|
+
}, []);
|
|
41177
|
+
return {
|
|
41178
|
+
...state,
|
|
41179
|
+
loadModel,
|
|
41180
|
+
loadOBJ,
|
|
41181
|
+
loadTexture,
|
|
41182
|
+
preload,
|
|
41183
|
+
hasModel,
|
|
41184
|
+
hasTexture,
|
|
41185
|
+
getModel,
|
|
41186
|
+
getTexture,
|
|
41187
|
+
clearCache
|
|
41188
|
+
};
|
|
41189
|
+
}
|
|
41190
|
+
var init_useAssetLoader = __esm({
|
|
41191
|
+
"components/game/molecules/three/hooks/useAssetLoader.ts"() {
|
|
41192
|
+
"use client";
|
|
41193
|
+
init_AssetLoader();
|
|
41194
|
+
}
|
|
41195
|
+
});
|
|
41196
|
+
function useGameCanvas3DEvents(options) {
|
|
41197
|
+
const {
|
|
41198
|
+
tileClickEvent,
|
|
41199
|
+
unitClickEvent,
|
|
41200
|
+
featureClickEvent,
|
|
41201
|
+
canvasClickEvent,
|
|
41202
|
+
tileHoverEvent,
|
|
41203
|
+
tileLeaveEvent,
|
|
41204
|
+
unitAnimationEvent,
|
|
41205
|
+
cameraChangeEvent,
|
|
41206
|
+
onTileClick,
|
|
41207
|
+
onUnitClick,
|
|
41208
|
+
onFeatureClick,
|
|
41209
|
+
onCanvasClick,
|
|
41210
|
+
onTileHover,
|
|
41211
|
+
onUnitAnimation
|
|
41212
|
+
} = options;
|
|
41213
|
+
const emit = useEmitEvent();
|
|
41214
|
+
const optionsRef = useRef(options);
|
|
41215
|
+
optionsRef.current = options;
|
|
41216
|
+
const handleTileClick = useCallback(
|
|
41217
|
+
(tile, event) => {
|
|
41218
|
+
if (tileClickEvent) {
|
|
41219
|
+
emit(tileClickEvent, {
|
|
41220
|
+
tileId: tile.id,
|
|
41221
|
+
x: tile.x,
|
|
41222
|
+
z: tile.z ?? tile.y ?? 0,
|
|
41223
|
+
type: tile.type,
|
|
41224
|
+
terrain: tile.terrain,
|
|
41225
|
+
elevation: tile.elevation
|
|
41226
|
+
});
|
|
41227
|
+
}
|
|
41228
|
+
optionsRef.current.onTileClick?.(tile, event);
|
|
41229
|
+
},
|
|
41230
|
+
[tileClickEvent, emit]
|
|
41231
|
+
);
|
|
41232
|
+
const handleUnitClick = useCallback(
|
|
41233
|
+
(unit, event) => {
|
|
41234
|
+
if (unitClickEvent) {
|
|
41235
|
+
emit(unitClickEvent, {
|
|
41236
|
+
unitId: unit.id,
|
|
41237
|
+
x: unit.x,
|
|
41238
|
+
z: unit.z ?? unit.y ?? 0,
|
|
41239
|
+
unitType: unit.unitType,
|
|
41240
|
+
name: unit.name,
|
|
41241
|
+
team: unit.team,
|
|
41242
|
+
faction: unit.faction,
|
|
41243
|
+
health: unit.health,
|
|
41244
|
+
maxHealth: unit.maxHealth
|
|
41245
|
+
});
|
|
41246
|
+
}
|
|
41247
|
+
optionsRef.current.onUnitClick?.(unit, event);
|
|
41248
|
+
},
|
|
41249
|
+
[unitClickEvent, emit]
|
|
41250
|
+
);
|
|
41251
|
+
const handleFeatureClick = useCallback(
|
|
41252
|
+
(feature, event) => {
|
|
41253
|
+
if (featureClickEvent) {
|
|
41254
|
+
emit(featureClickEvent, {
|
|
41255
|
+
featureId: feature.id,
|
|
41256
|
+
x: feature.x,
|
|
41257
|
+
z: feature.z ?? feature.y ?? 0,
|
|
41258
|
+
type: feature.type,
|
|
41259
|
+
elevation: feature.elevation
|
|
41260
|
+
});
|
|
41261
|
+
}
|
|
41262
|
+
optionsRef.current.onFeatureClick?.(feature, event);
|
|
41263
|
+
},
|
|
41264
|
+
[featureClickEvent, emit]
|
|
41265
|
+
);
|
|
41266
|
+
const handleCanvasClick = useCallback(
|
|
41267
|
+
(event) => {
|
|
41268
|
+
if (canvasClickEvent) {
|
|
41269
|
+
emit(canvasClickEvent, {
|
|
41270
|
+
clientX: event.clientX,
|
|
41271
|
+
clientY: event.clientY,
|
|
41272
|
+
button: event.button
|
|
41273
|
+
});
|
|
41274
|
+
}
|
|
41275
|
+
optionsRef.current.onCanvasClick?.(event);
|
|
41276
|
+
},
|
|
41277
|
+
[canvasClickEvent, emit]
|
|
41278
|
+
);
|
|
41279
|
+
const handleTileHover = useCallback(
|
|
41280
|
+
(tile, event) => {
|
|
41281
|
+
if (tile) {
|
|
41282
|
+
if (tileHoverEvent) {
|
|
41283
|
+
emit(tileHoverEvent, {
|
|
41284
|
+
tileId: tile.id,
|
|
41285
|
+
x: tile.x,
|
|
41286
|
+
z: tile.z ?? tile.y ?? 0,
|
|
41287
|
+
type: tile.type
|
|
41288
|
+
});
|
|
41289
|
+
}
|
|
41290
|
+
} else {
|
|
41291
|
+
if (tileLeaveEvent) {
|
|
41292
|
+
emit(tileLeaveEvent, {});
|
|
41293
|
+
}
|
|
41294
|
+
}
|
|
41295
|
+
optionsRef.current.onTileHover?.(tile, event);
|
|
41296
|
+
},
|
|
41297
|
+
[tileHoverEvent, tileLeaveEvent, emit]
|
|
41298
|
+
);
|
|
41299
|
+
const handleUnitAnimation = useCallback(
|
|
41300
|
+
(unitId, state) => {
|
|
41301
|
+
if (unitAnimationEvent) {
|
|
41302
|
+
emit(unitAnimationEvent, {
|
|
41303
|
+
unitId,
|
|
41304
|
+
state,
|
|
41305
|
+
timestamp: Date.now()
|
|
41306
|
+
});
|
|
41307
|
+
}
|
|
41308
|
+
optionsRef.current.onUnitAnimation?.(unitId, state);
|
|
41309
|
+
},
|
|
41310
|
+
[unitAnimationEvent, emit]
|
|
41311
|
+
);
|
|
41312
|
+
const handleCameraChange = useCallback(
|
|
41313
|
+
(position) => {
|
|
41314
|
+
if (cameraChangeEvent) {
|
|
41315
|
+
emit(cameraChangeEvent, {
|
|
41316
|
+
position,
|
|
41317
|
+
timestamp: Date.now()
|
|
41318
|
+
});
|
|
41319
|
+
}
|
|
41320
|
+
},
|
|
41321
|
+
[cameraChangeEvent, emit]
|
|
41322
|
+
);
|
|
41323
|
+
return {
|
|
41324
|
+
handleTileClick,
|
|
41325
|
+
handleUnitClick,
|
|
41326
|
+
handleFeatureClick,
|
|
41327
|
+
handleCanvasClick,
|
|
41328
|
+
handleTileHover,
|
|
41329
|
+
handleUnitAnimation,
|
|
41330
|
+
handleCameraChange
|
|
41331
|
+
};
|
|
41332
|
+
}
|
|
41333
|
+
var init_useGameCanvas3DEvents = __esm({
|
|
41334
|
+
"components/game/molecules/three/hooks/useGameCanvas3DEvents.ts"() {
|
|
41335
|
+
"use client";
|
|
41336
|
+
init_useEventBus();
|
|
41337
|
+
}
|
|
41338
|
+
});
|
|
41339
|
+
|
|
41340
|
+
// components/game/molecules/three/components/Canvas3DLoadingState.css
|
|
41341
|
+
var init_Canvas3DLoadingState = __esm({
|
|
41342
|
+
"components/game/molecules/three/components/Canvas3DLoadingState.css"() {
|
|
41343
|
+
}
|
|
41344
|
+
});
|
|
41345
|
+
function Canvas3DLoadingState({
|
|
41346
|
+
progress = 0,
|
|
41347
|
+
loaded = 0,
|
|
41348
|
+
total = 0,
|
|
41349
|
+
message = "Loading 3D Scene...",
|
|
41350
|
+
details,
|
|
41351
|
+
showSpinner = true,
|
|
41352
|
+
className
|
|
41353
|
+
}) {
|
|
41354
|
+
const clampedProgress = Math.max(0, Math.min(100, progress));
|
|
41355
|
+
const hasProgress = total > 0;
|
|
41356
|
+
return /* @__PURE__ */ jsxs("div", { className: `canvas-3d-loading ${className || ""}`, children: [
|
|
41357
|
+
/* @__PURE__ */ jsxs("div", { className: "canvas-3d-loading__content", children: [
|
|
41358
|
+
showSpinner && /* @__PURE__ */ jsxs("div", { className: "canvas-3d-loading__spinner", children: [
|
|
41359
|
+
/* @__PURE__ */ jsx("div", { className: "spinner__ring" }),
|
|
41360
|
+
/* @__PURE__ */ jsx("div", { className: "spinner__ring spinner__ring--secondary" })
|
|
41361
|
+
] }),
|
|
41362
|
+
/* @__PURE__ */ jsx("div", { className: "canvas-3d-loading__message", children: message }),
|
|
41363
|
+
details && /* @__PURE__ */ jsx("div", { className: "canvas-3d-loading__details", children: details }),
|
|
41364
|
+
hasProgress && /* @__PURE__ */ jsxs("div", { className: "canvas-3d-loading__progress", children: [
|
|
41365
|
+
/* @__PURE__ */ jsx("div", { className: "progress__bar", children: /* @__PURE__ */ jsx(
|
|
41366
|
+
"div",
|
|
41367
|
+
{
|
|
41368
|
+
className: "progress__fill",
|
|
41369
|
+
style: { width: `${clampedProgress}%` }
|
|
41370
|
+
}
|
|
41371
|
+
) }),
|
|
41372
|
+
/* @__PURE__ */ jsxs("div", { className: "progress__text", children: [
|
|
41373
|
+
/* @__PURE__ */ jsxs("span", { className: "progress__percentage", children: [
|
|
41374
|
+
clampedProgress,
|
|
41375
|
+
"%"
|
|
41376
|
+
] }),
|
|
41377
|
+
/* @__PURE__ */ jsxs("span", { className: "progress__count", children: [
|
|
41378
|
+
"(",
|
|
41379
|
+
loaded,
|
|
41380
|
+
"/",
|
|
41381
|
+
total,
|
|
41382
|
+
")"
|
|
41383
|
+
] })
|
|
41384
|
+
] })
|
|
41385
|
+
] })
|
|
41386
|
+
] }),
|
|
41387
|
+
/* @__PURE__ */ jsx("div", { className: "canvas-3d-loading__background", children: /* @__PURE__ */ jsx("div", { className: "bg__grid" }) })
|
|
41388
|
+
] });
|
|
41389
|
+
}
|
|
41390
|
+
var init_Canvas3DLoadingState2 = __esm({
|
|
41391
|
+
"components/game/molecules/three/components/Canvas3DLoadingState.tsx"() {
|
|
41392
|
+
init_Canvas3DLoadingState();
|
|
41393
|
+
}
|
|
41394
|
+
});
|
|
41395
|
+
|
|
41396
|
+
// components/game/molecules/three/components/Canvas3DErrorBoundary.css
|
|
41397
|
+
var init_Canvas3DErrorBoundary = __esm({
|
|
41398
|
+
"components/game/molecules/three/components/Canvas3DErrorBoundary.css"() {
|
|
41399
|
+
}
|
|
41400
|
+
});
|
|
41401
|
+
var log10, Canvas3DErrorBoundary;
|
|
41402
|
+
var init_Canvas3DErrorBoundary2 = __esm({
|
|
41403
|
+
"components/game/molecules/three/components/Canvas3DErrorBoundary.tsx"() {
|
|
41404
|
+
init_Canvas3DErrorBoundary();
|
|
41405
|
+
log10 = createLogger("almadar:ui:game:canvas3d:error-boundary");
|
|
41406
|
+
Canvas3DErrorBoundary = class extends Component {
|
|
41407
|
+
constructor(props) {
|
|
41408
|
+
super(props);
|
|
41409
|
+
__publicField(this, "handleReset", () => {
|
|
41410
|
+
this.setState({
|
|
41411
|
+
hasError: false,
|
|
41412
|
+
error: null,
|
|
41413
|
+
errorInfo: null
|
|
41414
|
+
});
|
|
41415
|
+
this.props.onReset?.();
|
|
41416
|
+
});
|
|
41417
|
+
this.state = {
|
|
41418
|
+
hasError: false,
|
|
41419
|
+
error: null,
|
|
41420
|
+
errorInfo: null
|
|
41421
|
+
};
|
|
41422
|
+
}
|
|
41423
|
+
static getDerivedStateFromError(error) {
|
|
41424
|
+
return {
|
|
41425
|
+
hasError: true,
|
|
41426
|
+
error,
|
|
41427
|
+
errorInfo: null
|
|
41428
|
+
};
|
|
41429
|
+
}
|
|
41430
|
+
componentDidCatch(error, errorInfo) {
|
|
41431
|
+
this.setState({ errorInfo });
|
|
41432
|
+
this.props.onError?.(error, errorInfo);
|
|
41433
|
+
log10.error("Error caught", { error });
|
|
41434
|
+
log10.error("Component stack", { componentStack: errorInfo.componentStack ?? "<none>" });
|
|
41435
|
+
}
|
|
41436
|
+
render() {
|
|
41437
|
+
if (this.state.hasError) {
|
|
41438
|
+
if (this.props.fallback) {
|
|
41439
|
+
return this.props.fallback;
|
|
41440
|
+
}
|
|
41441
|
+
return /* @__PURE__ */ jsx("div", { className: "canvas-3d-error", children: /* @__PURE__ */ jsxs("div", { className: "canvas-3d-error__content", children: [
|
|
41442
|
+
/* @__PURE__ */ jsx("div", { className: "canvas-3d-error__icon", children: "\u26A0\uFE0F" }),
|
|
41443
|
+
/* @__PURE__ */ jsx("h2", { className: "canvas-3d-error__title", children: "3D Scene Error" }),
|
|
41444
|
+
/* @__PURE__ */ jsx("p", { className: "canvas-3d-error__message", children: "Something went wrong while rendering the 3D scene." }),
|
|
41445
|
+
this.state.error && /* @__PURE__ */ jsxs("details", { className: "canvas-3d-error__details", children: [
|
|
41446
|
+
/* @__PURE__ */ jsx("summary", { children: "Error Details" }),
|
|
41447
|
+
/* @__PURE__ */ jsxs("pre", { className: "error__stack", children: [
|
|
41448
|
+
this.state.error.message,
|
|
41449
|
+
"\n",
|
|
41450
|
+
this.state.error.stack
|
|
41451
|
+
] }),
|
|
41452
|
+
this.state.errorInfo && /* @__PURE__ */ jsx("pre", { className: "error__component-stack", children: this.state.errorInfo.componentStack })
|
|
41453
|
+
] }),
|
|
41454
|
+
/* @__PURE__ */ jsxs("div", { className: "canvas-3d-error__actions", children: [
|
|
41455
|
+
/* @__PURE__ */ jsx(
|
|
41456
|
+
"button",
|
|
41457
|
+
{
|
|
41458
|
+
className: "error__button error__button--primary",
|
|
41459
|
+
onClick: this.handleReset,
|
|
41460
|
+
children: "Try Again"
|
|
41461
|
+
}
|
|
41462
|
+
),
|
|
41463
|
+
/* @__PURE__ */ jsx(
|
|
41464
|
+
"button",
|
|
41465
|
+
{
|
|
41466
|
+
className: "error__button error__button--secondary",
|
|
41467
|
+
onClick: () => window.location.reload(),
|
|
41468
|
+
children: "Reload Page"
|
|
41469
|
+
}
|
|
41470
|
+
)
|
|
41471
|
+
] })
|
|
41472
|
+
] }) });
|
|
41473
|
+
}
|
|
41474
|
+
return this.props.children;
|
|
41475
|
+
}
|
|
41476
|
+
};
|
|
41477
|
+
}
|
|
41478
|
+
});
|
|
41479
|
+
function detectAssetRoot2(modelUrl) {
|
|
41480
|
+
const idx = modelUrl.indexOf("/3d/");
|
|
41481
|
+
if (idx !== -1) {
|
|
41482
|
+
return modelUrl.substring(0, idx + 4);
|
|
41483
|
+
}
|
|
41484
|
+
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
41485
|
+
}
|
|
41486
|
+
function useGLTFModel(url, resourceBasePath) {
|
|
41487
|
+
const [state, setState] = useState({
|
|
41488
|
+
model: null,
|
|
41489
|
+
isLoading: false,
|
|
41490
|
+
error: null
|
|
41491
|
+
});
|
|
41492
|
+
useEffect(() => {
|
|
41493
|
+
if (!url) {
|
|
41494
|
+
setState({ model: null, isLoading: false, error: null });
|
|
41495
|
+
return;
|
|
41496
|
+
}
|
|
41497
|
+
log11.debug("Loading", { url });
|
|
41498
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
41499
|
+
const assetRoot = resourceBasePath || detectAssetRoot2(url);
|
|
41500
|
+
const loader = new GLTFLoader$1();
|
|
41501
|
+
loader.setResourcePath(assetRoot);
|
|
41502
|
+
loader.load(
|
|
41503
|
+
url,
|
|
41504
|
+
(gltf) => {
|
|
41505
|
+
log11.debug("Loaded", { url });
|
|
41506
|
+
setState({
|
|
41507
|
+
model: gltf.scene,
|
|
41508
|
+
isLoading: false,
|
|
41509
|
+
error: null
|
|
41510
|
+
});
|
|
41511
|
+
},
|
|
41512
|
+
void 0,
|
|
41513
|
+
(err) => {
|
|
41514
|
+
log11.warn("Failed", { url, error: err instanceof Error ? err : String(err) });
|
|
41515
|
+
setState({
|
|
41516
|
+
model: null,
|
|
41517
|
+
isLoading: false,
|
|
41518
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
41519
|
+
});
|
|
41520
|
+
}
|
|
41521
|
+
);
|
|
41522
|
+
}, [url, resourceBasePath]);
|
|
41523
|
+
return state;
|
|
41524
|
+
}
|
|
41525
|
+
function ModelLoader({
|
|
41526
|
+
url,
|
|
41527
|
+
position = [0, 0, 0],
|
|
41528
|
+
scale = 1,
|
|
41529
|
+
rotation = [0, 0, 0],
|
|
41530
|
+
isSelected = false,
|
|
41531
|
+
isHovered = false,
|
|
41532
|
+
onClick,
|
|
41533
|
+
onHover,
|
|
41534
|
+
fallbackGeometry = "box",
|
|
41535
|
+
castShadow = true,
|
|
41536
|
+
receiveShadow = true,
|
|
41537
|
+
resourceBasePath
|
|
41538
|
+
}) {
|
|
41539
|
+
const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
|
|
41540
|
+
const model = useMemo(() => {
|
|
41541
|
+
if (!loadedModel) return null;
|
|
41542
|
+
const cloned = loadedModel.clone();
|
|
41543
|
+
cloned.traverse((child) => {
|
|
41544
|
+
if (child instanceof THREE.Mesh) {
|
|
41545
|
+
child.castShadow = castShadow;
|
|
41546
|
+
child.receiveShadow = receiveShadow;
|
|
41547
|
+
}
|
|
41548
|
+
});
|
|
41549
|
+
return cloned;
|
|
41550
|
+
}, [loadedModel, castShadow, receiveShadow]);
|
|
41551
|
+
const scaleArray = useMemo(() => {
|
|
41552
|
+
if (typeof scale === "number") {
|
|
41553
|
+
return [scale, scale, scale];
|
|
41554
|
+
}
|
|
41555
|
+
return scale;
|
|
41556
|
+
}, [scale]);
|
|
41557
|
+
const rotationRad = useMemo(() => {
|
|
41558
|
+
return [
|
|
41559
|
+
rotation[0] * Math.PI / 180,
|
|
41560
|
+
rotation[1] * Math.PI / 180,
|
|
41561
|
+
rotation[2] * Math.PI / 180
|
|
41562
|
+
];
|
|
41563
|
+
}, [rotation]);
|
|
41564
|
+
if (isLoading) {
|
|
41565
|
+
return /* @__PURE__ */ jsx("group", { position, children: /* @__PURE__ */ jsxs("mesh", { rotation: [Math.PI / 2, 0, 0], children: [
|
|
41566
|
+
/* @__PURE__ */ jsx("ringGeometry", { args: [0.3, 0.35, 16] }),
|
|
41567
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { color: "#4a90d9", transparent: true, opacity: 0.8 })
|
|
41568
|
+
] }) });
|
|
41569
|
+
}
|
|
41570
|
+
if (error || !model) {
|
|
41571
|
+
if (fallbackGeometry === "none") {
|
|
41572
|
+
return /* @__PURE__ */ jsx("group", { position });
|
|
41573
|
+
}
|
|
41574
|
+
const fallbackProps = {
|
|
41575
|
+
onClick,
|
|
41576
|
+
onPointerOver: () => onHover?.(true),
|
|
41577
|
+
onPointerOut: () => onHover?.(false)
|
|
41578
|
+
};
|
|
41579
|
+
return /* @__PURE__ */ jsxs("group", { position, children: [
|
|
41580
|
+
(isSelected || isHovered) && /* @__PURE__ */ jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
41581
|
+
/* @__PURE__ */ jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
|
|
41582
|
+
/* @__PURE__ */ jsx(
|
|
41583
|
+
"meshBasicMaterial",
|
|
41584
|
+
{
|
|
41585
|
+
color: isSelected ? 16755200 : 16777215,
|
|
41586
|
+
transparent: true,
|
|
41587
|
+
opacity: 0.5
|
|
41588
|
+
}
|
|
41589
|
+
)
|
|
41590
|
+
] }),
|
|
41591
|
+
fallbackGeometry === "box" && /* @__PURE__ */ jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
41592
|
+
/* @__PURE__ */ jsx("boxGeometry", { args: [0.8, 0.8, 0.8] }),
|
|
41593
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
41594
|
+
] }),
|
|
41595
|
+
fallbackGeometry === "sphere" && /* @__PURE__ */ jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
41596
|
+
/* @__PURE__ */ jsx("sphereGeometry", { args: [0.4, 16, 16] }),
|
|
41597
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
41598
|
+
] }),
|
|
41599
|
+
fallbackGeometry === "cylinder" && /* @__PURE__ */ jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
41600
|
+
/* @__PURE__ */ jsx("cylinderGeometry", { args: [0.3, 0.3, 0.8, 16] }),
|
|
41601
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
41602
|
+
] })
|
|
41603
|
+
] });
|
|
41604
|
+
}
|
|
41605
|
+
return /* @__PURE__ */ jsxs(
|
|
41606
|
+
"group",
|
|
41607
|
+
{
|
|
41608
|
+
position,
|
|
41609
|
+
rotation: rotationRad,
|
|
41610
|
+
onClick,
|
|
41611
|
+
onPointerOver: () => onHover?.(true),
|
|
41612
|
+
onPointerOut: () => onHover?.(false),
|
|
41613
|
+
children: [
|
|
41614
|
+
(isSelected || isHovered) && /* @__PURE__ */ jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
41615
|
+
/* @__PURE__ */ jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
|
|
41616
|
+
/* @__PURE__ */ jsx(
|
|
41617
|
+
"meshBasicMaterial",
|
|
41618
|
+
{
|
|
41619
|
+
color: isSelected ? 16755200 : 16777215,
|
|
41620
|
+
transparent: true,
|
|
41621
|
+
opacity: 0.5
|
|
41622
|
+
}
|
|
41623
|
+
)
|
|
41624
|
+
] }),
|
|
41625
|
+
/* @__PURE__ */ jsx("primitive", { object: model, scale: scaleArray })
|
|
41626
|
+
]
|
|
41627
|
+
}
|
|
41628
|
+
);
|
|
41629
|
+
}
|
|
41630
|
+
var log11;
|
|
41631
|
+
var init_ModelLoader = __esm({
|
|
41632
|
+
"components/game/molecules/three/components/ModelLoader.tsx"() {
|
|
41633
|
+
"use client";
|
|
41634
|
+
log11 = createLogger("almadar:ui:game:model-loader");
|
|
41635
|
+
}
|
|
41636
|
+
});
|
|
41637
|
+
|
|
41638
|
+
// components/game/molecules/GameCanvas3D.css
|
|
41639
|
+
var init_GameCanvas3D = __esm({
|
|
41640
|
+
"components/game/molecules/GameCanvas3D.css"() {
|
|
41641
|
+
}
|
|
41642
|
+
});
|
|
41643
|
+
function CameraController({
|
|
41644
|
+
onCameraChange
|
|
41645
|
+
}) {
|
|
41646
|
+
const { camera } = useThree();
|
|
41647
|
+
useEffect(() => {
|
|
41648
|
+
if (onCameraChange) {
|
|
41649
|
+
onCameraChange({
|
|
41650
|
+
x: camera.position.x,
|
|
41651
|
+
y: camera.position.y,
|
|
41652
|
+
z: camera.position.z
|
|
41653
|
+
});
|
|
41654
|
+
}
|
|
41655
|
+
}, [camera.position, onCameraChange]);
|
|
41656
|
+
return null;
|
|
41657
|
+
}
|
|
41658
|
+
var DEFAULT_GRID_CONFIG, GameCanvas3D;
|
|
41659
|
+
var init_GameCanvas3D2 = __esm({
|
|
41660
|
+
"components/game/molecules/GameCanvas3D.tsx"() {
|
|
41661
|
+
"use client";
|
|
41662
|
+
init_AssetLoader();
|
|
41663
|
+
init_useAssetLoader();
|
|
41664
|
+
init_useGameCanvas3DEvents();
|
|
41665
|
+
init_Canvas3DLoadingState2();
|
|
41666
|
+
init_Canvas3DErrorBoundary2();
|
|
41667
|
+
init_ModelLoader();
|
|
41668
|
+
init_cn();
|
|
41669
|
+
init_GameCanvas3D();
|
|
41670
|
+
DEFAULT_GRID_CONFIG = {
|
|
41671
|
+
cellSize: 1,
|
|
41672
|
+
offsetX: 0,
|
|
41673
|
+
offsetZ: 0
|
|
41674
|
+
};
|
|
41675
|
+
GameCanvas3D = forwardRef(
|
|
41676
|
+
({
|
|
41677
|
+
tiles = [],
|
|
41678
|
+
units = [],
|
|
41679
|
+
features = [],
|
|
41680
|
+
events: events2 = [],
|
|
41681
|
+
orientation = "standard",
|
|
41682
|
+
cameraMode = "isometric",
|
|
41683
|
+
showGrid = true,
|
|
41684
|
+
showCoordinates = false,
|
|
41685
|
+
showTileInfo = false,
|
|
41686
|
+
overlay = "default",
|
|
41687
|
+
shadows = true,
|
|
41688
|
+
backgroundColor = "#1a1a2e",
|
|
41689
|
+
onTileClick,
|
|
41690
|
+
onUnitClick,
|
|
41691
|
+
onFeatureClick,
|
|
41692
|
+
onCanvasClick,
|
|
41693
|
+
onTileHover,
|
|
41694
|
+
onUnitAnimation,
|
|
41695
|
+
assetLoader: customAssetLoader,
|
|
41696
|
+
tileRenderer: CustomTileRenderer,
|
|
41697
|
+
unitRenderer: CustomUnitRenderer,
|
|
41698
|
+
featureRenderer: CustomFeatureRenderer,
|
|
41699
|
+
className,
|
|
41700
|
+
isLoading: externalLoading,
|
|
41701
|
+
error: externalError,
|
|
41702
|
+
entity,
|
|
41703
|
+
preloadAssets = [],
|
|
41704
|
+
tileClickEvent,
|
|
41705
|
+
unitClickEvent,
|
|
41706
|
+
featureClickEvent,
|
|
41707
|
+
canvasClickEvent,
|
|
41708
|
+
tileHoverEvent,
|
|
41709
|
+
tileLeaveEvent,
|
|
41710
|
+
unitAnimationEvent,
|
|
41711
|
+
cameraChangeEvent,
|
|
41712
|
+
loadingMessage = "Loading 3D Scene...",
|
|
41713
|
+
useInstancing = true,
|
|
41714
|
+
validMoves = [],
|
|
41715
|
+
attackTargets = [],
|
|
41716
|
+
selectedTileIds = [],
|
|
41717
|
+
selectedUnitId = null,
|
|
41718
|
+
children
|
|
41719
|
+
}, ref) => {
|
|
41720
|
+
const containerRef = useRef(null);
|
|
41721
|
+
const controlsRef = useRef(null);
|
|
41722
|
+
const [hoveredTile, setHoveredTile] = useState(null);
|
|
41723
|
+
const [internalError, setInternalError] = useState(null);
|
|
41724
|
+
const { isLoading: assetsLoading, progress, loaded, total } = useAssetLoader({
|
|
41725
|
+
preloadUrls: preloadAssets,
|
|
41726
|
+
loader: customAssetLoader
|
|
41727
|
+
});
|
|
41728
|
+
const eventHandlers = useGameCanvas3DEvents({
|
|
41729
|
+
tileClickEvent,
|
|
41730
|
+
unitClickEvent,
|
|
41731
|
+
featureClickEvent,
|
|
41732
|
+
canvasClickEvent,
|
|
41733
|
+
tileHoverEvent,
|
|
41734
|
+
tileLeaveEvent,
|
|
41735
|
+
unitAnimationEvent,
|
|
41736
|
+
cameraChangeEvent,
|
|
41737
|
+
onTileClick,
|
|
41738
|
+
onUnitClick,
|
|
41739
|
+
onFeatureClick,
|
|
41740
|
+
onCanvasClick,
|
|
41741
|
+
onTileHover,
|
|
41742
|
+
onUnitAnimation
|
|
41743
|
+
});
|
|
41744
|
+
const gridBounds = useMemo(() => {
|
|
41745
|
+
if (tiles.length === 0) {
|
|
41746
|
+
return { minX: 0, maxX: 10, minZ: 0, maxZ: 10 };
|
|
41747
|
+
}
|
|
41748
|
+
const xs = tiles.map((t) => t.x);
|
|
41749
|
+
const zs = tiles.map((t) => t.z || t.y || 0);
|
|
41750
|
+
return {
|
|
41751
|
+
minX: Math.min(...xs),
|
|
41752
|
+
maxX: Math.max(...xs),
|
|
41753
|
+
minZ: Math.min(...zs),
|
|
41754
|
+
maxZ: Math.max(...zs)
|
|
41755
|
+
};
|
|
41756
|
+
}, [tiles]);
|
|
41757
|
+
const cameraTarget = useMemo(() => {
|
|
41758
|
+
return [
|
|
41759
|
+
(gridBounds.minX + gridBounds.maxX) / 2,
|
|
41760
|
+
0,
|
|
41761
|
+
(gridBounds.minZ + gridBounds.maxZ) / 2
|
|
41762
|
+
];
|
|
41763
|
+
}, [gridBounds]);
|
|
41764
|
+
const gridConfig = useMemo(
|
|
41765
|
+
() => ({
|
|
41766
|
+
...DEFAULT_GRID_CONFIG,
|
|
41767
|
+
offsetX: -(gridBounds.maxX - gridBounds.minX) / 2,
|
|
41768
|
+
offsetZ: -(gridBounds.maxZ - gridBounds.minZ) / 2
|
|
41769
|
+
}),
|
|
41770
|
+
[gridBounds]
|
|
41771
|
+
);
|
|
41772
|
+
const gridToWorld = useCallback(
|
|
41773
|
+
(x, z, y = 0) => {
|
|
41774
|
+
const worldX = (x - gridBounds.minX) * gridConfig.cellSize;
|
|
41775
|
+
const worldZ = (z - gridBounds.minZ) * gridConfig.cellSize;
|
|
41776
|
+
return [worldX, y * gridConfig.cellSize, worldZ];
|
|
41777
|
+
},
|
|
41778
|
+
[gridBounds, gridConfig]
|
|
41779
|
+
);
|
|
41780
|
+
useImperativeHandle(ref, () => ({
|
|
41781
|
+
getCameraPosition: () => {
|
|
41782
|
+
if (controlsRef.current) {
|
|
41783
|
+
const pos = controlsRef.current.object.position;
|
|
41784
|
+
return new THREE.Vector3(pos.x, pos.y, pos.z);
|
|
41785
|
+
}
|
|
41786
|
+
return null;
|
|
41787
|
+
},
|
|
41788
|
+
setCameraPosition: (x, y, z) => {
|
|
41789
|
+
if (controlsRef.current) {
|
|
41790
|
+
controlsRef.current.object.position.set(x, y, z);
|
|
41791
|
+
controlsRef.current.update();
|
|
41792
|
+
}
|
|
41793
|
+
},
|
|
41794
|
+
lookAt: (x, y, z) => {
|
|
41795
|
+
if (controlsRef.current) {
|
|
41796
|
+
controlsRef.current.target.set(x, y, z);
|
|
41797
|
+
controlsRef.current.update();
|
|
41798
|
+
}
|
|
41799
|
+
},
|
|
41800
|
+
resetCamera: () => {
|
|
41801
|
+
if (controlsRef.current) {
|
|
41802
|
+
controlsRef.current.reset();
|
|
41803
|
+
}
|
|
41804
|
+
},
|
|
41805
|
+
screenshot: () => {
|
|
41806
|
+
const canvas = containerRef.current?.querySelector("canvas");
|
|
41807
|
+
if (canvas) {
|
|
41808
|
+
return canvas.toDataURL("image/png");
|
|
41809
|
+
}
|
|
41810
|
+
return null;
|
|
41811
|
+
},
|
|
41812
|
+
export: () => ({
|
|
41813
|
+
tiles,
|
|
41814
|
+
units,
|
|
41815
|
+
features
|
|
41816
|
+
})
|
|
41817
|
+
}));
|
|
41818
|
+
const handleTileClick = useCallback(
|
|
41819
|
+
(tile, event) => {
|
|
41820
|
+
eventHandlers.handleTileClick(tile, event);
|
|
41821
|
+
},
|
|
41822
|
+
[eventHandlers]
|
|
41823
|
+
);
|
|
41824
|
+
const handleUnitClick = useCallback(
|
|
41825
|
+
(unit, event) => {
|
|
41826
|
+
eventHandlers.handleUnitClick(unit, event);
|
|
41827
|
+
},
|
|
41828
|
+
[eventHandlers]
|
|
41829
|
+
);
|
|
41830
|
+
const handleFeatureClick = useCallback(
|
|
41831
|
+
(feature, event) => {
|
|
41832
|
+
if (event) {
|
|
41833
|
+
eventHandlers.handleFeatureClick(feature, event);
|
|
41834
|
+
}
|
|
41835
|
+
},
|
|
41836
|
+
[eventHandlers]
|
|
41837
|
+
);
|
|
41838
|
+
const handleTileHover = useCallback(
|
|
41839
|
+
(tile, event) => {
|
|
41840
|
+
setHoveredTile(tile);
|
|
41841
|
+
if (event) {
|
|
41842
|
+
eventHandlers.handleTileHover(tile, event);
|
|
41843
|
+
}
|
|
41844
|
+
},
|
|
41845
|
+
[eventHandlers]
|
|
41846
|
+
);
|
|
41847
|
+
const cameraConfig = useMemo(() => {
|
|
41848
|
+
const size = Math.max(
|
|
41849
|
+
gridBounds.maxX - gridBounds.minX,
|
|
41850
|
+
gridBounds.maxZ - gridBounds.minZ
|
|
41851
|
+
);
|
|
41852
|
+
const distance = size * 1.5;
|
|
41853
|
+
switch (cameraMode) {
|
|
41854
|
+
case "isometric":
|
|
41855
|
+
return {
|
|
41856
|
+
position: [distance, distance * 0.8, distance],
|
|
41857
|
+
fov: 45
|
|
41858
|
+
};
|
|
41859
|
+
case "top-down":
|
|
41860
|
+
return {
|
|
41861
|
+
position: [0, distance * 2, 0],
|
|
41862
|
+
fov: 45
|
|
41863
|
+
};
|
|
41864
|
+
case "perspective":
|
|
41865
|
+
default:
|
|
41866
|
+
return {
|
|
41867
|
+
position: [distance, distance, distance],
|
|
41868
|
+
fov: 45
|
|
41869
|
+
};
|
|
41870
|
+
}
|
|
41871
|
+
}, [cameraMode, gridBounds]);
|
|
41872
|
+
const DefaultTileRenderer = useCallback(
|
|
41873
|
+
({ tile, position }) => {
|
|
41874
|
+
const isSelected = tile.id ? selectedTileIds.includes(tile.id) : false;
|
|
41875
|
+
const isHovered = hoveredTile?.id === tile.id;
|
|
41876
|
+
const isValidMove = validMoves.some(
|
|
41877
|
+
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
41878
|
+
);
|
|
41879
|
+
const isAttackTarget = attackTargets.some(
|
|
41880
|
+
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
41881
|
+
);
|
|
41882
|
+
let color = 8421504;
|
|
41883
|
+
if (tile.type === "water") color = 4491468;
|
|
41884
|
+
else if (tile.type === "grass") color = 4500036;
|
|
41885
|
+
else if (tile.type === "sand") color = 14535816;
|
|
41886
|
+
else if (tile.type === "rock") color = 8947848;
|
|
41887
|
+
else if (tile.type === "snow") color = 15658734;
|
|
41888
|
+
let emissive = 0;
|
|
41889
|
+
if (isSelected) emissive = 4473924;
|
|
41890
|
+
else if (isAttackTarget) emissive = 4456448;
|
|
41891
|
+
else if (isValidMove) emissive = 17408;
|
|
41892
|
+
else if (isHovered) emissive = 2236962;
|
|
41893
|
+
if (tile.modelUrl) {
|
|
41894
|
+
return /* @__PURE__ */ jsx(
|
|
41895
|
+
"group",
|
|
41896
|
+
{
|
|
41897
|
+
position,
|
|
41898
|
+
onClick: (e) => handleTileClick(tile, e),
|
|
41899
|
+
onPointerEnter: (e) => handleTileHover(tile, e),
|
|
41900
|
+
onPointerLeave: (e) => handleTileHover(null, e),
|
|
41901
|
+
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
41902
|
+
children: /* @__PURE__ */ jsx(
|
|
41903
|
+
ModelLoader,
|
|
41904
|
+
{
|
|
41905
|
+
url: tile.modelUrl,
|
|
41906
|
+
scale: 0.95,
|
|
41907
|
+
fallbackGeometry: "box",
|
|
41908
|
+
castShadow: true,
|
|
41909
|
+
receiveShadow: true
|
|
41910
|
+
}
|
|
41911
|
+
)
|
|
41912
|
+
}
|
|
41913
|
+
);
|
|
41914
|
+
}
|
|
41915
|
+
return /* @__PURE__ */ jsxs(
|
|
41916
|
+
"mesh",
|
|
41917
|
+
{
|
|
41918
|
+
position,
|
|
41919
|
+
onClick: (e) => handleTileClick(tile, e),
|
|
41920
|
+
onPointerEnter: (e) => handleTileHover(tile, e),
|
|
41921
|
+
onPointerLeave: (e) => handleTileHover(null, e),
|
|
41922
|
+
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
41923
|
+
children: [
|
|
41924
|
+
/* @__PURE__ */ jsx("boxGeometry", { args: [0.95, 0.2, 0.95] }),
|
|
41925
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color, emissive })
|
|
41926
|
+
]
|
|
41927
|
+
}
|
|
41928
|
+
);
|
|
41929
|
+
},
|
|
41930
|
+
[selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover]
|
|
41931
|
+
);
|
|
41932
|
+
const DefaultUnitRenderer = useCallback(
|
|
41933
|
+
({ unit, position }) => {
|
|
41934
|
+
const isSelected = selectedUnitId === unit.id;
|
|
41935
|
+
const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
|
|
41936
|
+
return /* @__PURE__ */ jsxs(
|
|
41937
|
+
"group",
|
|
41938
|
+
{
|
|
41939
|
+
position,
|
|
41940
|
+
onClick: (e) => handleUnitClick(unit, e),
|
|
41941
|
+
userData: { type: "unit", unitId: unit.id },
|
|
41942
|
+
children: [
|
|
41943
|
+
isSelected && /* @__PURE__ */ jsxs("mesh", { position: [0, 0.05, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
41944
|
+
/* @__PURE__ */ jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
|
|
41945
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
|
|
41946
|
+
] }),
|
|
41947
|
+
unit.modelUrl ? (
|
|
41948
|
+
/* GLB unit model (box fallback while loading / on error) */
|
|
41949
|
+
/* @__PURE__ */ jsx(
|
|
41950
|
+
ModelLoader,
|
|
41951
|
+
{
|
|
41952
|
+
url: unit.modelUrl,
|
|
41953
|
+
scale: 0.5,
|
|
41954
|
+
fallbackGeometry: "box",
|
|
41955
|
+
castShadow: true
|
|
41956
|
+
}
|
|
41957
|
+
)
|
|
41958
|
+
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
41959
|
+
/* @__PURE__ */ jsxs("mesh", { position: [0, 0.3, 0], children: [
|
|
41960
|
+
/* @__PURE__ */ jsx("cylinderGeometry", { args: [0.3, 0.3, 0.1, 8] }),
|
|
41961
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color })
|
|
41962
|
+
] }),
|
|
41963
|
+
/* @__PURE__ */ jsxs("mesh", { position: [0, 0.6, 0], children: [
|
|
41964
|
+
/* @__PURE__ */ jsx("capsuleGeometry", { args: [0.2, 0.4, 4, 8] }),
|
|
41965
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color })
|
|
41966
|
+
] }),
|
|
41967
|
+
/* @__PURE__ */ jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
41968
|
+
/* @__PURE__ */ jsx("sphereGeometry", { args: [0.12, 8, 8] }),
|
|
41969
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color })
|
|
41970
|
+
] })
|
|
41971
|
+
] }),
|
|
41972
|
+
unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxs("group", { position: [0, 1.2, 0], children: [
|
|
41973
|
+
/* @__PURE__ */ jsxs("mesh", { position: [-0.25, 0, 0], children: [
|
|
41974
|
+
/* @__PURE__ */ jsx("planeGeometry", { args: [0.5, 0.05] }),
|
|
41975
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { color: 3355443 })
|
|
41976
|
+
] }),
|
|
41977
|
+
/* @__PURE__ */ jsxs(
|
|
41978
|
+
"mesh",
|
|
41979
|
+
{
|
|
41980
|
+
position: [
|
|
41981
|
+
-0.25 + 0.5 * (unit.health / unit.maxHealth) / 2,
|
|
41982
|
+
0,
|
|
41983
|
+
0.01
|
|
41984
|
+
],
|
|
41985
|
+
children: [
|
|
41986
|
+
/* @__PURE__ */ jsx("planeGeometry", { args: [0.5 * (unit.health / unit.maxHealth), 0.05] }),
|
|
41987
|
+
/* @__PURE__ */ jsx(
|
|
41988
|
+
"meshBasicMaterial",
|
|
41989
|
+
{
|
|
41990
|
+
color: unit.health / unit.maxHealth > 0.5 ? 4500036 : unit.health / unit.maxHealth > 0.25 ? 11184708 : 16729156
|
|
41991
|
+
}
|
|
41992
|
+
)
|
|
41993
|
+
]
|
|
41994
|
+
}
|
|
41995
|
+
)
|
|
41996
|
+
] })
|
|
41997
|
+
]
|
|
41998
|
+
}
|
|
41999
|
+
);
|
|
42000
|
+
},
|
|
42001
|
+
[selectedUnitId, handleUnitClick]
|
|
42002
|
+
);
|
|
42003
|
+
const DefaultFeatureRenderer = useCallback(
|
|
42004
|
+
({
|
|
42005
|
+
feature,
|
|
42006
|
+
position
|
|
42007
|
+
}) => {
|
|
42008
|
+
if (feature.assetUrl) {
|
|
42009
|
+
return /* @__PURE__ */ jsx(
|
|
42010
|
+
ModelLoader,
|
|
42011
|
+
{
|
|
42012
|
+
url: feature.assetUrl,
|
|
42013
|
+
position,
|
|
42014
|
+
scale: 0.5,
|
|
42015
|
+
rotation: [0, feature.rotation ?? 0, 0],
|
|
42016
|
+
onClick: () => handleFeatureClick(feature, null),
|
|
42017
|
+
fallbackGeometry: "box"
|
|
42018
|
+
},
|
|
42019
|
+
feature.id
|
|
42020
|
+
);
|
|
42021
|
+
}
|
|
42022
|
+
if (feature.type === "tree") {
|
|
42023
|
+
return /* @__PURE__ */ jsxs(
|
|
42024
|
+
"group",
|
|
42025
|
+
{
|
|
42026
|
+
position,
|
|
42027
|
+
onClick: (e) => handleFeatureClick(feature, e),
|
|
42028
|
+
userData: { type: "feature", featureId: feature.id },
|
|
42029
|
+
children: [
|
|
42030
|
+
/* @__PURE__ */ jsxs("mesh", { position: [0, 0.4, 0], children: [
|
|
42031
|
+
/* @__PURE__ */ jsx("cylinderGeometry", { args: [0.1, 0.15, 0.8, 6] }),
|
|
42032
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: 9127187 })
|
|
42033
|
+
] }),
|
|
42034
|
+
/* @__PURE__ */ jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
42035
|
+
/* @__PURE__ */ jsx("coneGeometry", { args: [0.5, 0.8, 8] }),
|
|
42036
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: 2263842 })
|
|
42037
|
+
] })
|
|
42038
|
+
]
|
|
42039
|
+
}
|
|
42040
|
+
);
|
|
42041
|
+
}
|
|
42042
|
+
if (feature.type === "rock") {
|
|
42043
|
+
return /* @__PURE__ */ jsxs(
|
|
42044
|
+
"mesh",
|
|
42045
|
+
{
|
|
42046
|
+
position: [position[0], position[1] + 0.3, position[2]],
|
|
42047
|
+
onClick: (e) => handleFeatureClick(feature, e),
|
|
42048
|
+
userData: { type: "feature", featureId: feature.id },
|
|
42049
|
+
children: [
|
|
42050
|
+
/* @__PURE__ */ jsx("dodecahedronGeometry", { args: [0.3, 0] }),
|
|
42051
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: 8421504 })
|
|
42052
|
+
]
|
|
42053
|
+
}
|
|
42054
|
+
);
|
|
42055
|
+
}
|
|
42056
|
+
return null;
|
|
42057
|
+
},
|
|
42058
|
+
[handleFeatureClick]
|
|
42059
|
+
);
|
|
42060
|
+
if (externalLoading || assetsLoading && preloadAssets.length > 0) {
|
|
42061
|
+
return /* @__PURE__ */ jsx(
|
|
42062
|
+
Canvas3DLoadingState,
|
|
42063
|
+
{
|
|
42064
|
+
progress,
|
|
42065
|
+
loaded,
|
|
42066
|
+
total,
|
|
42067
|
+
message: loadingMessage,
|
|
42068
|
+
className
|
|
42069
|
+
}
|
|
42070
|
+
);
|
|
42071
|
+
}
|
|
42072
|
+
const displayError = externalError || internalError;
|
|
42073
|
+
if (displayError) {
|
|
42074
|
+
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: [
|
|
42075
|
+
"Error: ",
|
|
42076
|
+
displayError
|
|
42077
|
+
] }) }) });
|
|
42078
|
+
}
|
|
42079
|
+
return /* @__PURE__ */ jsx(
|
|
42080
|
+
Canvas3DErrorBoundary,
|
|
42081
|
+
{
|
|
42082
|
+
onError: (err) => setInternalError(err.message),
|
|
42083
|
+
onReset: () => setInternalError(null),
|
|
42084
|
+
children: /* @__PURE__ */ jsxs(
|
|
42085
|
+
"div",
|
|
42086
|
+
{
|
|
42087
|
+
ref: containerRef,
|
|
42088
|
+
className: cn("game-canvas-3d relative w-full h-full min-h-[85vh] overflow-hidden", className),
|
|
42089
|
+
"data-orientation": orientation,
|
|
42090
|
+
"data-camera-mode": cameraMode,
|
|
42091
|
+
"data-overlay": overlay,
|
|
42092
|
+
children: [
|
|
42093
|
+
/* @__PURE__ */ jsxs(
|
|
42094
|
+
Canvas,
|
|
42095
|
+
{
|
|
42096
|
+
shadows,
|
|
42097
|
+
camera: {
|
|
42098
|
+
position: cameraConfig.position,
|
|
42099
|
+
fov: cameraConfig.fov,
|
|
42100
|
+
near: 0.1,
|
|
42101
|
+
far: 1e3
|
|
42102
|
+
},
|
|
42103
|
+
style: { background: backgroundColor },
|
|
42104
|
+
onClick: (e) => {
|
|
42105
|
+
if (e.target === e.currentTarget) {
|
|
42106
|
+
eventHandlers.handleCanvasClick(e);
|
|
42107
|
+
}
|
|
42108
|
+
},
|
|
42109
|
+
children: [
|
|
42110
|
+
/* @__PURE__ */ jsx(CameraController, { onCameraChange: eventHandlers.handleCameraChange }),
|
|
42111
|
+
/* @__PURE__ */ jsx("ambientLight", { intensity: 0.6 }),
|
|
42112
|
+
/* @__PURE__ */ jsx(
|
|
42113
|
+
"directionalLight",
|
|
42114
|
+
{
|
|
42115
|
+
position: [10, 20, 10],
|
|
42116
|
+
intensity: 0.8,
|
|
42117
|
+
castShadow: shadows,
|
|
42118
|
+
"shadow-mapSize": [2048, 2048]
|
|
42119
|
+
}
|
|
42120
|
+
),
|
|
42121
|
+
/* @__PURE__ */ jsx("hemisphereLight", { intensity: 0.3, color: "#87ceeb", groundColor: "#362d1d" }),
|
|
42122
|
+
showGrid && /* @__PURE__ */ jsx(
|
|
42123
|
+
Grid$1,
|
|
42124
|
+
{
|
|
42125
|
+
args: [
|
|
42126
|
+
Math.max(gridBounds.maxX - gridBounds.minX + 2, 10),
|
|
42127
|
+
Math.max(gridBounds.maxZ - gridBounds.minZ + 2, 10)
|
|
42128
|
+
],
|
|
42129
|
+
position: [
|
|
42130
|
+
(gridBounds.maxX - gridBounds.minX) / 2 - 0.5,
|
|
42131
|
+
0,
|
|
42132
|
+
(gridBounds.maxZ - gridBounds.minZ) / 2 - 0.5
|
|
42133
|
+
],
|
|
42134
|
+
cellSize: 1,
|
|
42135
|
+
cellThickness: 1,
|
|
42136
|
+
cellColor: "#444444",
|
|
42137
|
+
sectionSize: 5,
|
|
42138
|
+
sectionThickness: 1.5,
|
|
42139
|
+
sectionColor: "#666666",
|
|
42140
|
+
fadeDistance: 50,
|
|
42141
|
+
fadeStrength: 1
|
|
42142
|
+
}
|
|
42143
|
+
),
|
|
42144
|
+
tiles.map((tile, index) => {
|
|
42145
|
+
const position = gridToWorld(
|
|
42146
|
+
tile.x,
|
|
42147
|
+
tile.z ?? tile.y ?? 0,
|
|
42148
|
+
tile.elevation ?? 0
|
|
42149
|
+
);
|
|
42150
|
+
const Renderer = CustomTileRenderer || DefaultTileRenderer;
|
|
42151
|
+
return /* @__PURE__ */ jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
|
|
42152
|
+
}),
|
|
42153
|
+
features.map((feature, index) => {
|
|
42154
|
+
const position = gridToWorld(
|
|
42155
|
+
feature.x,
|
|
42156
|
+
feature.z ?? feature.y ?? 0,
|
|
42157
|
+
(feature.elevation ?? 0) + 0.5
|
|
42158
|
+
);
|
|
42159
|
+
const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
|
|
42160
|
+
return /* @__PURE__ */ jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
|
|
42161
|
+
}),
|
|
42162
|
+
units.map((unit) => {
|
|
42163
|
+
const position = gridToWorld(
|
|
42164
|
+
unit.x ?? 0,
|
|
42165
|
+
unit.z ?? unit.y ?? 0,
|
|
42166
|
+
(unit.elevation ?? 0) + 0.5
|
|
42167
|
+
);
|
|
42168
|
+
const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
|
|
42169
|
+
return /* @__PURE__ */ jsx(Renderer, { unit, position }, unit.id);
|
|
42170
|
+
}),
|
|
42171
|
+
children,
|
|
42172
|
+
/* @__PURE__ */ jsx(
|
|
42173
|
+
OrbitControls,
|
|
42174
|
+
{
|
|
42175
|
+
ref: controlsRef,
|
|
42176
|
+
target: cameraTarget,
|
|
42177
|
+
enableDamping: true,
|
|
42178
|
+
dampingFactor: 0.05,
|
|
42179
|
+
minDistance: 2,
|
|
42180
|
+
maxDistance: 100,
|
|
42181
|
+
maxPolarAngle: Math.PI / 2 - 0.1
|
|
42182
|
+
}
|
|
42183
|
+
)
|
|
42184
|
+
]
|
|
42185
|
+
}
|
|
42186
|
+
),
|
|
42187
|
+
showCoordinates && hoveredTile && /* @__PURE__ */ jsxs("div", { className: "game-canvas-3d__coordinates", children: [
|
|
42188
|
+
"X: ",
|
|
42189
|
+
hoveredTile.x,
|
|
42190
|
+
", Z: ",
|
|
42191
|
+
hoveredTile.z ?? hoveredTile.y ?? 0
|
|
42192
|
+
] }),
|
|
42193
|
+
showTileInfo && hoveredTile && /* @__PURE__ */ jsxs("div", { className: "game-canvas-3d__tile-info", children: [
|
|
42194
|
+
/* @__PURE__ */ jsx("div", { className: "tile-info__type", children: hoveredTile.type }),
|
|
42195
|
+
hoveredTile.terrain && /* @__PURE__ */ jsx("div", { className: "tile-info__terrain", children: hoveredTile.terrain })
|
|
42196
|
+
] })
|
|
42197
|
+
]
|
|
42198
|
+
}
|
|
42199
|
+
)
|
|
42200
|
+
}
|
|
42201
|
+
);
|
|
42202
|
+
}
|
|
42203
|
+
);
|
|
42204
|
+
GameCanvas3D.displayName = "GameCanvas3D";
|
|
42205
|
+
}
|
|
42206
|
+
});
|
|
42207
|
+
function GameBoard3D({
|
|
42208
|
+
entity,
|
|
42209
|
+
tiles = [],
|
|
42210
|
+
features = [],
|
|
42211
|
+
cameraMode = "perspective",
|
|
42212
|
+
backgroundColor = "#2a1a1a",
|
|
42213
|
+
tileClickEvent,
|
|
42214
|
+
unitClickEvent,
|
|
42215
|
+
attackEvent,
|
|
42216
|
+
endTurnEvent,
|
|
42217
|
+
cancelEvent,
|
|
42218
|
+
playAgainEvent,
|
|
42219
|
+
gameEndEvent,
|
|
42220
|
+
className
|
|
42221
|
+
}) {
|
|
42222
|
+
const row = boardEntity(entity);
|
|
42223
|
+
const eventBus = useEventBus();
|
|
42224
|
+
const entityUnits = row ? rows(row.units) : [];
|
|
42225
|
+
const units = entityUnits;
|
|
42226
|
+
const selectedUnitId = row ? str(row.selectedUnitId) || null : null;
|
|
42227
|
+
const phase = row ? str(row.phase) : "observation";
|
|
42228
|
+
const result = row ? str(row.result) : "none";
|
|
42229
|
+
const validMoves = row && Array.isArray(row.validMoves) ? row.validMoves : [];
|
|
42230
|
+
const attackTargets = row && Array.isArray(row.attackTargets) ? row.attackTargets : [];
|
|
42231
|
+
const turn = row ? num(row.turn) : 0;
|
|
42232
|
+
const currentTeam = row ? str(row.currentTeam) : "player";
|
|
42233
|
+
const isGameOver = result !== "none";
|
|
42234
|
+
const gameEndEmittedRef = useRef(false);
|
|
42235
|
+
useEffect(() => {
|
|
42236
|
+
if ((result === "victory" || result === "defeat") && gameEndEvent) {
|
|
42237
|
+
if (!gameEndEmittedRef.current) {
|
|
42238
|
+
gameEndEmittedRef.current = true;
|
|
42239
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
42240
|
+
}
|
|
42241
|
+
} else {
|
|
42242
|
+
gameEndEmittedRef.current = false;
|
|
42243
|
+
}
|
|
42244
|
+
}, [result, gameEndEvent, eventBus]);
|
|
42245
|
+
const checkGameEnd = useCallback(() => {
|
|
42246
|
+
const alivePlayer = entityUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
|
|
42247
|
+
const aliveEnemy = entityUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
|
|
42248
|
+
if (alivePlayer.length === 0 && gameEndEvent) {
|
|
42249
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result: "defeat" });
|
|
42250
|
+
} else if (aliveEnemy.length === 0 && gameEndEvent) {
|
|
42251
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result: "victory" });
|
|
42252
|
+
}
|
|
42253
|
+
}, [entityUnits, gameEndEvent, eventBus]);
|
|
42254
|
+
const handleUnitClickCallback = useCallback((isoUnit) => {
|
|
42255
|
+
if (phase !== "action" || !selectedUnitId || !attackEvent) return;
|
|
42256
|
+
const attackerRow = entityUnits.find((u) => str(u.id) === selectedUnitId);
|
|
42257
|
+
const targetRow = entityUnits.find((u) => str(u.id) === isoUnit.id);
|
|
42258
|
+
if (!attackerRow || !targetRow) return;
|
|
42259
|
+
if (unitTeam(targetRow) !== "enemy" || unitHealth(targetRow) <= 0) return;
|
|
42260
|
+
const ap = unitPosition(attackerRow);
|
|
42261
|
+
const tp = unitPosition(targetRow);
|
|
42262
|
+
const dx = Math.abs(ap.x - tp.x);
|
|
42263
|
+
const dy = Math.abs(ap.y - tp.y);
|
|
42264
|
+
if (dx <= 1 && dy <= 1 && dx + dy > 0) {
|
|
42265
|
+
const damage = Math.max(1, num(attackerRow.attack) - num(targetRow.defense));
|
|
42266
|
+
eventBus.emit(`UI:${attackEvent}`, {
|
|
42267
|
+
attackerId: str(attackerRow.id),
|
|
42268
|
+
targetId: str(targetRow.id),
|
|
42269
|
+
damage
|
|
42270
|
+
});
|
|
42271
|
+
setTimeout(checkGameEnd, 100);
|
|
42272
|
+
}
|
|
42273
|
+
}, [phase, selectedUnitId, entityUnits, attackEvent, eventBus, checkGameEnd]);
|
|
42274
|
+
const handleEndTurn = useCallback(() => {
|
|
42275
|
+
if (endTurnEvent) eventBus.emit(`UI:${endTurnEvent}`, {});
|
|
42276
|
+
}, [endTurnEvent, eventBus]);
|
|
42277
|
+
const handleCancel = useCallback(() => {
|
|
42278
|
+
if (cancelEvent) eventBus.emit(`UI:${cancelEvent}`, {});
|
|
42279
|
+
}, [cancelEvent, eventBus]);
|
|
42280
|
+
const handlePlayAgain = useCallback(() => {
|
|
42281
|
+
if (playAgainEvent) eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
42282
|
+
}, [playAgainEvent, eventBus]);
|
|
42283
|
+
return /* @__PURE__ */ jsxs(VStack, { className: cn("game-board-3d block w-full min-h-[85vh] relative", className), children: [
|
|
42284
|
+
/* @__PURE__ */ jsxs("div", { className: "game-board-3d__status absolute top-3 left-3 z-10 flex gap-2 items-center", children: [
|
|
42285
|
+
/* @__PURE__ */ jsx(Typography, { variant: "small", className: "status__phase capitalize", children: phase.replace("_", " ") }),
|
|
42286
|
+
currentTeam && !isGameOver && /* @__PURE__ */ jsxs(Typography, { variant: "small", color: "muted", className: "status__team", children: [
|
|
42287
|
+
"\u2014 ",
|
|
42288
|
+
currentTeam === "player" ? "Your Turn" : "Enemy's Turn"
|
|
42289
|
+
] }),
|
|
42290
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "small", color: "muted", className: "status__turn", children: [
|
|
42291
|
+
"Turn ",
|
|
42292
|
+
turn
|
|
42293
|
+
] })
|
|
42294
|
+
] }),
|
|
42295
|
+
/* @__PURE__ */ jsx(
|
|
42296
|
+
GameCanvas3D,
|
|
42297
|
+
{
|
|
42298
|
+
tiles,
|
|
42299
|
+
units,
|
|
42300
|
+
features,
|
|
42301
|
+
cameraMode,
|
|
42302
|
+
showGrid: true,
|
|
42303
|
+
showCoordinates: false,
|
|
42304
|
+
showTileInfo: false,
|
|
42305
|
+
shadows: true,
|
|
42306
|
+
backgroundColor,
|
|
42307
|
+
tileClickEvent,
|
|
42308
|
+
unitClickEvent,
|
|
42309
|
+
onUnitClick: handleUnitClickCallback,
|
|
42310
|
+
selectedUnitId,
|
|
42311
|
+
validMoves,
|
|
42312
|
+
attackTargets,
|
|
42313
|
+
className: "game-board-3d__canvas w-full min-h-[85vh]"
|
|
42314
|
+
}
|
|
42315
|
+
),
|
|
42316
|
+
!isGameOver && /* @__PURE__ */ jsxs(HStack, { className: "fixed bottom-6 right-6 z-50", gap: "sm", children: [
|
|
42317
|
+
(phase === "selection" || phase === "movement" || phase === "action") && /* @__PURE__ */ jsx(
|
|
42318
|
+
Button,
|
|
42319
|
+
{
|
|
42320
|
+
variant: "secondary",
|
|
42321
|
+
className: "shadow-xl",
|
|
42322
|
+
onClick: handleCancel,
|
|
42323
|
+
children: "Cancel"
|
|
42324
|
+
}
|
|
42325
|
+
),
|
|
42326
|
+
phase !== "enemy_turn" && /* @__PURE__ */ jsx(
|
|
42327
|
+
Button,
|
|
42328
|
+
{
|
|
42329
|
+
variant: "primary",
|
|
42330
|
+
className: "shadow-xl",
|
|
42331
|
+
onClick: handleEndTurn,
|
|
42332
|
+
children: "End Turn"
|
|
42333
|
+
}
|
|
42334
|
+
)
|
|
42335
|
+
] }),
|
|
42336
|
+
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: [
|
|
42337
|
+
/* @__PURE__ */ jsx(
|
|
42338
|
+
Typography,
|
|
42339
|
+
{
|
|
42340
|
+
variant: "h2",
|
|
42341
|
+
className: cn(
|
|
42342
|
+
"overlay__result",
|
|
42343
|
+
result === "victory" ? "text-yellow-400" : "text-red-500"
|
|
42344
|
+
),
|
|
42345
|
+
children: result === "victory" ? "Victory!" : "Defeat"
|
|
42346
|
+
}
|
|
42347
|
+
),
|
|
42348
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "body", color: "muted", className: "overlay__turn-count", children: [
|
|
42349
|
+
"Completed in ",
|
|
42350
|
+
turn,
|
|
42351
|
+
" turn",
|
|
42352
|
+
turn !== 1 ? "s" : ""
|
|
42353
|
+
] }),
|
|
42354
|
+
/* @__PURE__ */ jsx(
|
|
42355
|
+
Button,
|
|
42356
|
+
{
|
|
42357
|
+
variant: "primary",
|
|
42358
|
+
className: "px-8 py-3 font-semibold",
|
|
42359
|
+
onClick: handlePlayAgain,
|
|
42360
|
+
children: "Play Again"
|
|
42361
|
+
}
|
|
42362
|
+
)
|
|
42363
|
+
] }) })
|
|
42364
|
+
] });
|
|
42365
|
+
}
|
|
42366
|
+
var init_GameBoard3D = __esm({
|
|
42367
|
+
"components/game/organisms/GameBoard3D.tsx"() {
|
|
42368
|
+
"use client";
|
|
42369
|
+
init_cn();
|
|
42370
|
+
init_Button();
|
|
42371
|
+
init_Typography();
|
|
42372
|
+
init_Stack();
|
|
42373
|
+
init_useEventBus();
|
|
42374
|
+
init_GameCanvas3D2();
|
|
42375
|
+
init_boardEntity();
|
|
42376
|
+
GameBoard3D.displayName = "GameBoard3D";
|
|
42377
|
+
}
|
|
42378
|
+
});
|
|
40676
42379
|
var GameShell;
|
|
40677
42380
|
var init_GameShell = __esm({
|
|
40678
42381
|
"components/game/templates/GameShell.tsx"() {
|
|
@@ -42041,21 +43744,6 @@ var init_ModalSlot = __esm({
|
|
|
42041
43744
|
ModalSlot.displayName = "ModalSlot";
|
|
42042
43745
|
}
|
|
42043
43746
|
});
|
|
42044
|
-
function getOpponentAction(strategy, actions, history) {
|
|
42045
|
-
const actionIds = actions.map((a) => a.id);
|
|
42046
|
-
switch (strategy) {
|
|
42047
|
-
case "always-cooperate":
|
|
42048
|
-
return actionIds[0];
|
|
42049
|
-
case "always-defect":
|
|
42050
|
-
return actionIds[actionIds.length - 1];
|
|
42051
|
-
case "tit-for-tat":
|
|
42052
|
-
if (history.length === 0) return actionIds[0];
|
|
42053
|
-
return history[history.length - 1].playerAction;
|
|
42054
|
-
case "random":
|
|
42055
|
-
default:
|
|
42056
|
-
return actionIds[Math.floor(Math.random() * actionIds.length)];
|
|
42057
|
-
}
|
|
42058
|
-
}
|
|
42059
43747
|
function NegotiatorBoard({
|
|
42060
43748
|
entity,
|
|
42061
43749
|
completeEvent = "PUZZLE_COMPLETE",
|
|
@@ -42077,28 +43765,35 @@ function NegotiatorBoard({
|
|
|
42077
43765
|
const playerTotal = num(resolved?.score);
|
|
42078
43766
|
const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
|
|
42079
43767
|
const won = result === "win";
|
|
42080
|
-
const
|
|
43768
|
+
const lastPlayerAction = str(resolved?.lastPlayerAction);
|
|
43769
|
+
const lastOpponentAction = str(resolved?.lastOpponentAction);
|
|
43770
|
+
const lastPayoff = num(resolved?.lastPayoff);
|
|
42081
43771
|
const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
|
|
42082
43772
|
const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
|
|
43773
|
+
const prevRoundRef = useRef(currentRound);
|
|
43774
|
+
useEffect(() => {
|
|
43775
|
+
if (currentRound > prevRoundRef.current && lastPlayerAction) {
|
|
43776
|
+
const opponentPayoffEntry = payoffMatrix.find(
|
|
43777
|
+
(p2) => p2.playerAction === lastPlayerAction && p2.opponentAction === lastOpponentAction
|
|
43778
|
+
);
|
|
43779
|
+
setHistory((prev) => [
|
|
43780
|
+
...prev,
|
|
43781
|
+
{
|
|
43782
|
+
round: currentRound,
|
|
43783
|
+
playerAction: lastPlayerAction,
|
|
43784
|
+
opponentAction: lastOpponentAction,
|
|
43785
|
+
playerPayoff: lastPayoff,
|
|
43786
|
+
opponentPayoff: opponentPayoffEntry?.opponentPayoff ?? 0
|
|
43787
|
+
}
|
|
43788
|
+
]);
|
|
43789
|
+
}
|
|
43790
|
+
prevRoundRef.current = currentRound;
|
|
43791
|
+
}, [currentRound, lastPlayerAction, lastOpponentAction, lastPayoff, payoffMatrix]);
|
|
43792
|
+
const opponentTotal = useMemo(() => history.reduce((s, r) => s + r.opponentPayoff, 0), [history]);
|
|
42083
43793
|
const handleAction = useCallback((actionId) => {
|
|
42084
43794
|
if (isComplete) return;
|
|
42085
|
-
const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
|
|
42086
|
-
const payoff = payoffMatrix.find(
|
|
42087
|
-
(p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
|
|
42088
|
-
);
|
|
42089
|
-
const playerPayoff = payoff?.playerPayoff ?? 0;
|
|
42090
|
-
setHistory((prev) => [
|
|
42091
|
-
...prev,
|
|
42092
|
-
{
|
|
42093
|
-
round: prev.length + 1,
|
|
42094
|
-
playerAction: actionId,
|
|
42095
|
-
opponentAction,
|
|
42096
|
-
playerPayoff,
|
|
42097
|
-
opponentPayoff: payoff?.opponentPayoff ?? 0
|
|
42098
|
-
}
|
|
42099
|
-
]);
|
|
42100
43795
|
if (playRoundEvent) {
|
|
42101
|
-
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff:
|
|
43796
|
+
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: 0 });
|
|
42102
43797
|
}
|
|
42103
43798
|
if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
|
|
42104
43799
|
if (finishEvent) {
|
|
@@ -42108,10 +43803,11 @@ function NegotiatorBoard({
|
|
|
42108
43803
|
setShowHint(true);
|
|
42109
43804
|
}
|
|
42110
43805
|
}
|
|
42111
|
-
}, [isComplete, resolved, totalRounds, currentRound,
|
|
43806
|
+
}, [isComplete, resolved, totalRounds, currentRound, playRoundEvent, finishEvent, emit]);
|
|
42112
43807
|
const handleReset = useCallback(() => {
|
|
42113
43808
|
setHistory([]);
|
|
42114
43809
|
setShowHint(false);
|
|
43810
|
+
prevRoundRef.current = 0;
|
|
42115
43811
|
if (playAgainEvent) {
|
|
42116
43812
|
emit(`UI:${playAgainEvent}`, {});
|
|
42117
43813
|
}
|
|
@@ -42222,6 +43918,236 @@ var init_NegotiatorBoard = __esm({
|
|
|
42222
43918
|
NegotiatorBoard.displayName = "NegotiatorBoard";
|
|
42223
43919
|
}
|
|
42224
43920
|
});
|
|
43921
|
+
function resolveEntity(entity) {
|
|
43922
|
+
if (!entity) return {};
|
|
43923
|
+
if (Array.isArray(entity)) return entity[0] ?? {};
|
|
43924
|
+
return entity;
|
|
43925
|
+
}
|
|
43926
|
+
function numField(row, key, fallback) {
|
|
43927
|
+
const v = row[key];
|
|
43928
|
+
return typeof v === "number" ? v : fallback;
|
|
43929
|
+
}
|
|
43930
|
+
function strField(row, key, fallback) {
|
|
43931
|
+
const v = row[key];
|
|
43932
|
+
return typeof v === "string" ? v : fallback;
|
|
43933
|
+
}
|
|
43934
|
+
function PlatformerBoard({
|
|
43935
|
+
entity,
|
|
43936
|
+
player: propPlayer,
|
|
43937
|
+
platforms: propPlatforms,
|
|
43938
|
+
result: propResult,
|
|
43939
|
+
lives: propLives,
|
|
43940
|
+
score: propScore,
|
|
43941
|
+
level: propLevel,
|
|
43942
|
+
worldWidth = 800,
|
|
43943
|
+
worldHeight = 400,
|
|
43944
|
+
canvasWidth = 800,
|
|
43945
|
+
canvasHeight = 400,
|
|
43946
|
+
playerSprite,
|
|
43947
|
+
tileSprites,
|
|
43948
|
+
bgColor,
|
|
43949
|
+
leftEvent = "LEFT",
|
|
43950
|
+
rightEvent = "RIGHT",
|
|
43951
|
+
jumpEvent = "JUMP",
|
|
43952
|
+
stopEvent = "STOP",
|
|
43953
|
+
playAgainEvent = "PLAY_AGAIN",
|
|
43954
|
+
gameEndEvent = "GAME_END",
|
|
43955
|
+
className
|
|
43956
|
+
}) {
|
|
43957
|
+
const row = resolveEntity(entity);
|
|
43958
|
+
const eventBus = useEventBus();
|
|
43959
|
+
const { t } = useTranslate();
|
|
43960
|
+
const result = propResult ?? strField(row, "result", "none");
|
|
43961
|
+
propLives ?? numField(row, "lives", 3);
|
|
43962
|
+
const score = propScore ?? numField(row, "score", 0);
|
|
43963
|
+
const level = propLevel ?? numField(row, "level", 1);
|
|
43964
|
+
const entityPlayer = row["player"];
|
|
43965
|
+
const player = propPlayer ?? entityPlayer ?? {
|
|
43966
|
+
x: 80,
|
|
43967
|
+
y: 320,
|
|
43968
|
+
width: 32,
|
|
43969
|
+
height: 48,
|
|
43970
|
+
vx: 0,
|
|
43971
|
+
vy: 0,
|
|
43972
|
+
grounded: false,
|
|
43973
|
+
facingRight: true
|
|
43974
|
+
};
|
|
43975
|
+
const entityPlatforms = Array.isArray(row["platforms"]) ? row["platforms"] : void 0;
|
|
43976
|
+
const platforms = propPlatforms ?? entityPlatforms ?? [
|
|
43977
|
+
{ x: 0, y: 368, width: 800, height: 32, type: "ground" },
|
|
43978
|
+
{ x: 150, y: 280, width: 160, height: 16, type: "platform" },
|
|
43979
|
+
{ x: 420, y: 220, width: 160, height: 16, type: "platform" },
|
|
43980
|
+
{ x: 580, y: 300, width: 80, height: 16, type: "hazard" },
|
|
43981
|
+
{ x: 700, y: 340, width: 64, height: 28, type: "goal" }
|
|
43982
|
+
];
|
|
43983
|
+
const handleRestart = useCallback(() => {
|
|
43984
|
+
if (playAgainEvent) {
|
|
43985
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
43986
|
+
}
|
|
43987
|
+
}, [playAgainEvent, eventBus]);
|
|
43988
|
+
const isGameOver = result === "won" || result === "lost";
|
|
43989
|
+
const gameEndEmittedRef = useRef(false);
|
|
43990
|
+
useEffect(() => {
|
|
43991
|
+
if ((result === "won" || result === "lost") && gameEndEvent) {
|
|
43992
|
+
if (!gameEndEmittedRef.current) {
|
|
43993
|
+
gameEndEmittedRef.current = true;
|
|
43994
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
43995
|
+
}
|
|
43996
|
+
} else {
|
|
43997
|
+
gameEndEmittedRef.current = false;
|
|
43998
|
+
}
|
|
43999
|
+
}, [result, gameEndEvent, eventBus]);
|
|
44000
|
+
return /* @__PURE__ */ jsx(
|
|
44001
|
+
VStack,
|
|
44002
|
+
{
|
|
44003
|
+
className: cn("platformer-board relative bg-background", className),
|
|
44004
|
+
gap: "none",
|
|
44005
|
+
children: /* @__PURE__ */ jsxs(Box, { className: "relative", children: [
|
|
44006
|
+
/* @__PURE__ */ jsx(
|
|
44007
|
+
PlatformerCanvas,
|
|
44008
|
+
{
|
|
44009
|
+
player,
|
|
44010
|
+
platforms,
|
|
44011
|
+
worldWidth,
|
|
44012
|
+
worldHeight,
|
|
44013
|
+
canvasWidth,
|
|
44014
|
+
canvasHeight,
|
|
44015
|
+
followCamera: true,
|
|
44016
|
+
bgColor,
|
|
44017
|
+
playerSprite,
|
|
44018
|
+
tileSprites,
|
|
44019
|
+
leftEvent,
|
|
44020
|
+
rightEvent,
|
|
44021
|
+
jumpEvent,
|
|
44022
|
+
stopEvent
|
|
44023
|
+
}
|
|
44024
|
+
),
|
|
44025
|
+
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: [
|
|
44026
|
+
/* @__PURE__ */ jsx(
|
|
44027
|
+
Typography,
|
|
44028
|
+
{
|
|
44029
|
+
variant: "h2",
|
|
44030
|
+
className: cn(
|
|
44031
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
44032
|
+
result === "won" ? "text-warning" : "text-error"
|
|
44033
|
+
),
|
|
44034
|
+
children: result === "won" ? t("platformer.won") : t("platformer.lost")
|
|
44035
|
+
}
|
|
44036
|
+
),
|
|
44037
|
+
result === "won" && /* @__PURE__ */ jsxs(Typography, { variant: "body1", className: "text-muted-foreground", children: [
|
|
44038
|
+
t("platformer.level"),
|
|
44039
|
+
" ",
|
|
44040
|
+
level,
|
|
44041
|
+
" \u2014 ",
|
|
44042
|
+
t("platformer.score"),
|
|
44043
|
+
": ",
|
|
44044
|
+
score
|
|
44045
|
+
] }),
|
|
44046
|
+
result === "lost" && /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: t("platformer.noLives") }),
|
|
44047
|
+
/* @__PURE__ */ jsx(
|
|
44048
|
+
Button,
|
|
44049
|
+
{
|
|
44050
|
+
variant: "primary",
|
|
44051
|
+
className: "px-8 py-3 font-semibold",
|
|
44052
|
+
onClick: handleRestart,
|
|
44053
|
+
children: result === "won" ? t("platformer.nextLevel") : t("platformer.playAgain")
|
|
44054
|
+
}
|
|
44055
|
+
)
|
|
44056
|
+
] }) })
|
|
44057
|
+
] })
|
|
44058
|
+
}
|
|
44059
|
+
);
|
|
44060
|
+
}
|
|
44061
|
+
var init_PlatformerBoard = __esm({
|
|
44062
|
+
"components/game/organisms/PlatformerBoard.tsx"() {
|
|
44063
|
+
"use client";
|
|
44064
|
+
init_cn();
|
|
44065
|
+
init_useEventBus();
|
|
44066
|
+
init_Box();
|
|
44067
|
+
init_Button();
|
|
44068
|
+
init_Typography();
|
|
44069
|
+
init_Stack();
|
|
44070
|
+
init_PlatformerCanvas();
|
|
44071
|
+
PlatformerBoard.displayName = "PlatformerBoard";
|
|
44072
|
+
}
|
|
44073
|
+
});
|
|
44074
|
+
function PlatformerTemplate({
|
|
44075
|
+
entity,
|
|
44076
|
+
platforms = DEFAULT_PLATFORMS,
|
|
44077
|
+
worldWidth = 800,
|
|
44078
|
+
worldHeight = 400,
|
|
44079
|
+
canvasWidth = 800,
|
|
44080
|
+
canvasHeight = 400,
|
|
44081
|
+
playerSprite = `${CDN3}/characters/platformChar_idle.png`,
|
|
44082
|
+
tileSprites = DEFAULT_TILE_SPRITES,
|
|
44083
|
+
bgColor = "#5c94fc",
|
|
44084
|
+
gameEndEvent = "GAME_END",
|
|
44085
|
+
className
|
|
44086
|
+
}) {
|
|
44087
|
+
const row = entity && !Array.isArray(entity) ? entity : void 0;
|
|
44088
|
+
const lives = typeof row?.["lives"] === "number" ? row["lives"] : 3;
|
|
44089
|
+
const score = typeof row?.["score"] === "number" ? row["score"] : 0;
|
|
44090
|
+
const level = typeof row?.["level"] === "number" ? row["level"] : 1;
|
|
44091
|
+
return /* @__PURE__ */ jsxs(VStack, { className: cn("platformer-template w-full", className), gap: "none", children: [
|
|
44092
|
+
/* @__PURE__ */ jsx(Box, { className: "relative", children: /* @__PURE__ */ jsx(
|
|
44093
|
+
GameHud,
|
|
44094
|
+
{
|
|
44095
|
+
position: "top",
|
|
44096
|
+
stats: [
|
|
44097
|
+
{ label: "Lives", value: lives, format: "number", variant: "danger" },
|
|
44098
|
+
{ label: "Level", value: level, format: "number", variant: "default" }
|
|
44099
|
+
]
|
|
44100
|
+
}
|
|
44101
|
+
) }),
|
|
44102
|
+
/* @__PURE__ */ jsx(
|
|
44103
|
+
PlatformerBoard,
|
|
44104
|
+
{
|
|
44105
|
+
entity,
|
|
44106
|
+
platforms,
|
|
44107
|
+
worldWidth,
|
|
44108
|
+
worldHeight,
|
|
44109
|
+
canvasWidth,
|
|
44110
|
+
canvasHeight,
|
|
44111
|
+
playerSprite,
|
|
44112
|
+
tileSprites,
|
|
44113
|
+
bgColor,
|
|
44114
|
+
leftEvent: "LEFT",
|
|
44115
|
+
rightEvent: "RIGHT",
|
|
44116
|
+
jumpEvent: "JUMP",
|
|
44117
|
+
stopEvent: "STOP",
|
|
44118
|
+
playAgainEvent: "PLAY_AGAIN",
|
|
44119
|
+
gameEndEvent
|
|
44120
|
+
}
|
|
44121
|
+
),
|
|
44122
|
+
/* @__PURE__ */ jsx(Box, { className: "px-4 py-2", children: /* @__PURE__ */ jsx(ScoreBoard, { score, level }) })
|
|
44123
|
+
] });
|
|
44124
|
+
}
|
|
44125
|
+
var CDN3, DEFAULT_TILE_SPRITES, DEFAULT_PLATFORMS;
|
|
44126
|
+
var init_PlatformerTemplate = __esm({
|
|
44127
|
+
"components/game/templates/PlatformerTemplate.tsx"() {
|
|
44128
|
+
init_cn();
|
|
44129
|
+
init_Box();
|
|
44130
|
+
init_Stack();
|
|
44131
|
+
init_PlatformerBoard();
|
|
44132
|
+
init_GameHud();
|
|
44133
|
+
init_ScoreBoard();
|
|
44134
|
+
CDN3 = "https://almadar-kflow-assets.web.app/shared/platformer";
|
|
44135
|
+
DEFAULT_TILE_SPRITES = {
|
|
44136
|
+
ground: `${CDN3}/tiles/platformPack_tile001.png`,
|
|
44137
|
+
platform: `${CDN3}/tiles/platformPack_tile004.png`,
|
|
44138
|
+
hazard: `${CDN3}/tiles/platformPack_tile017.png`,
|
|
44139
|
+
goal: `${CDN3}/tiles/platformPack_tile007.png`
|
|
44140
|
+
};
|
|
44141
|
+
DEFAULT_PLATFORMS = [
|
|
44142
|
+
{ x: 0, y: 368, width: 800, height: 32, type: "ground" },
|
|
44143
|
+
{ x: 150, y: 280, width: 160, height: 16, type: "platform" },
|
|
44144
|
+
{ x: 420, y: 220, width: 160, height: 16, type: "platform" },
|
|
44145
|
+
{ x: 580, y: 300, width: 80, height: 16, type: "hazard" },
|
|
44146
|
+
{ x: 700, y: 340, width: 64, height: 28, type: "goal" }
|
|
44147
|
+
];
|
|
44148
|
+
PlatformerTemplate.displayName = "PlatformerTemplate";
|
|
44149
|
+
}
|
|
44150
|
+
});
|
|
42225
44151
|
var PricingOrganism;
|
|
42226
44152
|
var init_PricingOrganism = __esm({
|
|
42227
44153
|
"components/marketing/organisms/PricingOrganism.tsx"() {
|
|
@@ -42344,6 +44270,356 @@ var init_PricingPageTemplate = __esm({
|
|
|
42344
44270
|
PricingPageTemplate.displayName = "PricingPageTemplate";
|
|
42345
44271
|
}
|
|
42346
44272
|
});
|
|
44273
|
+
function RoguelikeBoard({
|
|
44274
|
+
entity,
|
|
44275
|
+
tiles: propTiles,
|
|
44276
|
+
enemies: propEnemies,
|
|
44277
|
+
items: propItems,
|
|
44278
|
+
player: propPlayer,
|
|
44279
|
+
playerHp: propPlayerHp,
|
|
44280
|
+
playerMaxHp: propPlayerMaxHp,
|
|
44281
|
+
playerAttack: propPlayerAttack,
|
|
44282
|
+
depth: propDepth,
|
|
44283
|
+
result: propResult,
|
|
44284
|
+
phase: propPhase,
|
|
44285
|
+
stairsX: propStairsX,
|
|
44286
|
+
stairsY: propStairsY,
|
|
44287
|
+
assetManifest: propAssetManifest,
|
|
44288
|
+
scale = 0.45,
|
|
44289
|
+
unitScale = 1,
|
|
44290
|
+
moveEvent,
|
|
44291
|
+
playAgainEvent,
|
|
44292
|
+
gameEndEvent,
|
|
44293
|
+
className
|
|
44294
|
+
}) {
|
|
44295
|
+
const board = boardEntity(entity) ?? {};
|
|
44296
|
+
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : DEFAULT_TILES2);
|
|
44297
|
+
const enemies = propEnemies ?? (Array.isArray(board.enemies) ? board.enemies : DEFAULT_ENEMIES);
|
|
44298
|
+
const items = propItems ?? (Array.isArray(board.items) ? board.items : DEFAULT_ITEMS);
|
|
44299
|
+
const player = propPlayer ?? board.player ?? { x: 1, y: 1 };
|
|
44300
|
+
const playerHp = propPlayerHp ?? num(board.playerHp, 10);
|
|
44301
|
+
const playerMaxHp = propPlayerMaxHp ?? num(board.playerMaxHp, 10);
|
|
44302
|
+
const playerAttack = propPlayerAttack ?? num(board.playerAttack, 3);
|
|
44303
|
+
const depth = propDepth ?? num(board.depth, 1);
|
|
44304
|
+
const result = propResult ?? (str(board.result) || "none");
|
|
44305
|
+
const phase = propPhase ?? (str(board.phase) || "player_turn");
|
|
44306
|
+
propStairsX ?? num(board.stairsX, 3);
|
|
44307
|
+
propStairsY ?? num(board.stairsY, 3);
|
|
44308
|
+
const eventBus = useEventBus();
|
|
44309
|
+
const { t } = useTranslate();
|
|
44310
|
+
const [hoveredTile, setHoveredTile] = useState(null);
|
|
44311
|
+
const emitMove = useCallback((dx, dy) => {
|
|
44312
|
+
if (!moveEvent) return;
|
|
44313
|
+
eventBus.emit(`UI:${moveEvent}`, { dx, dy });
|
|
44314
|
+
}, [moveEvent, eventBus]);
|
|
44315
|
+
useEffect(() => {
|
|
44316
|
+
const handler = (e) => {
|
|
44317
|
+
if (result !== "none" || phase !== "player_turn") return;
|
|
44318
|
+
if (e.key === "ArrowUp" || e.key === "w") emitMove(0, -1);
|
|
44319
|
+
if (e.key === "ArrowDown" || e.key === "s") emitMove(0, 1);
|
|
44320
|
+
if (e.key === "ArrowLeft" || e.key === "a") emitMove(-1, 0);
|
|
44321
|
+
if (e.key === "ArrowRight" || e.key === "d") emitMove(1, 0);
|
|
44322
|
+
};
|
|
44323
|
+
window.addEventListener("keydown", handler);
|
|
44324
|
+
return () => window.removeEventListener("keydown", handler);
|
|
44325
|
+
}, [result, phase, emitMove]);
|
|
44326
|
+
useEffect(() => {
|
|
44327
|
+
if ((result === "won" || result === "lost") && gameEndEvent) {
|
|
44328
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
44329
|
+
}
|
|
44330
|
+
}, [result, gameEndEvent, eventBus]);
|
|
44331
|
+
const handleTileClick = useCallback((x, y) => {
|
|
44332
|
+
if (result !== "none" || phase !== "player_turn") return;
|
|
44333
|
+
const dx = x - player.x;
|
|
44334
|
+
const dy = y - player.y;
|
|
44335
|
+
if (Math.abs(dx) + Math.abs(dy) === 1) {
|
|
44336
|
+
emitMove(dx, dy);
|
|
44337
|
+
}
|
|
44338
|
+
}, [result, phase, player, emitMove]);
|
|
44339
|
+
const handleReset = useCallback(() => {
|
|
44340
|
+
if (!playAgainEvent) return;
|
|
44341
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
44342
|
+
}, [playAgainEvent, eventBus]);
|
|
44343
|
+
const isoUnits = useMemo(() => {
|
|
44344
|
+
const liveEnemies = enemies.filter((e) => e.hp > 0);
|
|
44345
|
+
return [
|
|
44346
|
+
{
|
|
44347
|
+
id: "player",
|
|
44348
|
+
position: player,
|
|
44349
|
+
name: t("roguelike.player") || "Hero",
|
|
44350
|
+
team: "player",
|
|
44351
|
+
health: playerHp,
|
|
44352
|
+
maxHealth: playerMaxHp,
|
|
44353
|
+
unitType: "player",
|
|
44354
|
+
sprite: `${CDN4}/isometric-dungeon/Characters/Male/Male_0_Idle0.png`
|
|
44355
|
+
},
|
|
44356
|
+
...liveEnemies.map((e) => ({
|
|
44357
|
+
id: e.id,
|
|
44358
|
+
position: { x: e.x, y: e.y },
|
|
44359
|
+
name: t("roguelike.enemy") || "Enemy",
|
|
44360
|
+
team: "enemy",
|
|
44361
|
+
health: e.hp,
|
|
44362
|
+
maxHealth: 6,
|
|
44363
|
+
unitType: "enemy",
|
|
44364
|
+
sprite: `${CDN4}/sprite-sheets/shadow-legion-sprite-sheet-sw.png`
|
|
44365
|
+
}))
|
|
44366
|
+
];
|
|
44367
|
+
}, [player, enemies, playerHp, playerMaxHp, t]);
|
|
44368
|
+
const isoFeatures = useMemo(
|
|
44369
|
+
() => items.map((it) => ({
|
|
44370
|
+
id: it.id,
|
|
44371
|
+
x: it.x,
|
|
44372
|
+
y: it.y,
|
|
44373
|
+
type: it.kind,
|
|
44374
|
+
sprite: FEATURE_SPRITE[it.kind] ?? FEATURE_SPRITE.health_potion
|
|
44375
|
+
})),
|
|
44376
|
+
[items]
|
|
44377
|
+
);
|
|
44378
|
+
const validMoves = useMemo(() => {
|
|
44379
|
+
if (result !== "none" || phase !== "player_turn") return [];
|
|
44380
|
+
const dirs = [{ dx: 0, dy: -1 }, { dx: 0, dy: 1 }, { dx: -1, dy: 0 }, { dx: 1, dy: 0 }];
|
|
44381
|
+
return dirs.map(({ dx, dy }) => ({ x: player.x + dx, y: player.y + dy })).filter(({ x, y }) => {
|
|
44382
|
+
const tile = tiles.find((t2) => t2.x === x && t2.y === y);
|
|
44383
|
+
return tile?.passable ?? false;
|
|
44384
|
+
});
|
|
44385
|
+
}, [result, phase, player, tiles]);
|
|
44386
|
+
const isGameOver = result === "won" || result === "lost";
|
|
44387
|
+
return /* @__PURE__ */ jsxs(VStack, { className: cn("roguelike-board relative min-h-[600px] bg-background", className), gap: "none", children: [
|
|
44388
|
+
/* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between px-4 py-2 border-b border-border", gap: "md", children: [
|
|
44389
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
44390
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.depth") || "Depth" }),
|
|
44391
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body1", className: "font-bold tabular-nums", children: depth })
|
|
44392
|
+
] }),
|
|
44393
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
44394
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.hp") || "HP" }),
|
|
44395
|
+
/* @__PURE__ */ jsxs(
|
|
44396
|
+
Typography,
|
|
44397
|
+
{
|
|
44398
|
+
variant: "body1",
|
|
44399
|
+
className: cn(
|
|
44400
|
+
"font-bold tabular-nums",
|
|
44401
|
+
playerHp <= Math.floor(playerMaxHp * 0.25) ? "text-error" : "text-success"
|
|
44402
|
+
),
|
|
44403
|
+
children: [
|
|
44404
|
+
playerHp,
|
|
44405
|
+
" / ",
|
|
44406
|
+
playerMaxHp
|
|
44407
|
+
]
|
|
44408
|
+
}
|
|
44409
|
+
)
|
|
44410
|
+
] }),
|
|
44411
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
44412
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.atk") || "ATK" }),
|
|
44413
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body1", className: "font-bold tabular-nums text-foreground", children: playerAttack })
|
|
44414
|
+
] }),
|
|
44415
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
44416
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.phase") || "Phase" }),
|
|
44417
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "capitalize text-foreground", children: phase.replace("_", " ") })
|
|
44418
|
+
] })
|
|
44419
|
+
] }),
|
|
44420
|
+
/* @__PURE__ */ jsx(Box, { className: "relative flex-1", children: /* @__PURE__ */ jsx(
|
|
44421
|
+
IsometricCanvas_default,
|
|
44422
|
+
{
|
|
44423
|
+
tiles,
|
|
44424
|
+
units: isoUnits,
|
|
44425
|
+
features: isoFeatures,
|
|
44426
|
+
selectedUnitId: null,
|
|
44427
|
+
validMoves,
|
|
44428
|
+
attackTargets: [],
|
|
44429
|
+
hoveredTile,
|
|
44430
|
+
onTileClick: handleTileClick,
|
|
44431
|
+
onUnitClick: () => void 0,
|
|
44432
|
+
onTileHover: (x, y) => setHoveredTile({ x, y }),
|
|
44433
|
+
onTileLeave: () => setHoveredTile(null),
|
|
44434
|
+
scale,
|
|
44435
|
+
assetBaseUrl: propAssetManifest?.baseUrl ?? CDN4,
|
|
44436
|
+
assetManifest: propAssetManifest,
|
|
44437
|
+
unitScale
|
|
44438
|
+
}
|
|
44439
|
+
) }),
|
|
44440
|
+
!isGameOver && /* @__PURE__ */ jsx(HStack, { className: "justify-center gap-2 p-3", gap: "none", children: [
|
|
44441
|
+
{ label: "\u2191", dx: 0, dy: -1 },
|
|
44442
|
+
{ label: "\u2193", dx: 0, dy: 1 },
|
|
44443
|
+
{ label: "\u2190", dx: -1, dy: 0 },
|
|
44444
|
+
{ label: "\u2192", dx: 1, dy: 0 }
|
|
44445
|
+
].map(({ label, dx, dy }) => /* @__PURE__ */ jsx(
|
|
44446
|
+
Button,
|
|
44447
|
+
{
|
|
44448
|
+
variant: "secondary",
|
|
44449
|
+
className: "w-9 h-9 p-0 text-base font-mono",
|
|
44450
|
+
onClick: () => emitMove(dx, dy),
|
|
44451
|
+
disabled: phase !== "player_turn",
|
|
44452
|
+
children: label
|
|
44453
|
+
},
|
|
44454
|
+
label
|
|
44455
|
+
)) }),
|
|
44456
|
+
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: [
|
|
44457
|
+
/* @__PURE__ */ jsx(
|
|
44458
|
+
Typography,
|
|
44459
|
+
{
|
|
44460
|
+
variant: "h2",
|
|
44461
|
+
className: cn(
|
|
44462
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
44463
|
+
result === "won" ? "text-warning" : "text-error"
|
|
44464
|
+
),
|
|
44465
|
+
children: result === "won" ? t("roguelike.victory") || "Victory!" : t("roguelike.defeat") || "Defeated!"
|
|
44466
|
+
}
|
|
44467
|
+
),
|
|
44468
|
+
result === "won" && /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: t("roguelike.cleared") || `Dungeon cleared! Depth reached: ${depth}` }),
|
|
44469
|
+
/* @__PURE__ */ jsx(
|
|
44470
|
+
Button,
|
|
44471
|
+
{
|
|
44472
|
+
variant: "primary",
|
|
44473
|
+
className: "px-8 py-3 font-semibold",
|
|
44474
|
+
onClick: handleReset,
|
|
44475
|
+
children: t("roguelike.playAgain") || "Play Again"
|
|
44476
|
+
}
|
|
44477
|
+
)
|
|
44478
|
+
] }) })
|
|
44479
|
+
] });
|
|
44480
|
+
}
|
|
44481
|
+
var CDN4, DEFAULT_TILES2, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
|
|
44482
|
+
var init_RoguelikeBoard = __esm({
|
|
44483
|
+
"components/game/organisms/RoguelikeBoard.tsx"() {
|
|
44484
|
+
"use client";
|
|
44485
|
+
init_cn();
|
|
44486
|
+
init_useEventBus();
|
|
44487
|
+
init_Box();
|
|
44488
|
+
init_Button();
|
|
44489
|
+
init_Typography();
|
|
44490
|
+
init_Stack();
|
|
44491
|
+
init_IsometricCanvas();
|
|
44492
|
+
init_boardEntity();
|
|
44493
|
+
CDN4 = "https://almadar-kflow-assets.web.app/shared";
|
|
44494
|
+
DEFAULT_TILES2 = [
|
|
44495
|
+
{ x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44496
|
+
{ x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44497
|
+
{ x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44498
|
+
{ x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44499
|
+
{ x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44500
|
+
{ x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44501
|
+
{ x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44502
|
+
{ x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44503
|
+
{ x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44504
|
+
{ x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44505
|
+
{ x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44506
|
+
{ x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
44507
|
+
{ x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
44508
|
+
{ x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44509
|
+
{ x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44510
|
+
{ x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44511
|
+
{ x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44512
|
+
{ x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
44513
|
+
{ x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` },
|
|
44514
|
+
{ x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44515
|
+
{ x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44516
|
+
{ x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44517
|
+
{ x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44518
|
+
{ x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44519
|
+
{ x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` }
|
|
44520
|
+
];
|
|
44521
|
+
DEFAULT_ENEMIES = [
|
|
44522
|
+
{ id: "e1", x: 3, y: 1, hp: 5, attack: 2 },
|
|
44523
|
+
{ id: "e2", x: 1, y: 3, hp: 4, attack: 1 }
|
|
44524
|
+
];
|
|
44525
|
+
DEFAULT_ITEMS = [
|
|
44526
|
+
{ id: "i1", x: 2, y: 2, kind: "health_potion" }
|
|
44527
|
+
];
|
|
44528
|
+
FEATURE_SPRITE = {
|
|
44529
|
+
health_potion: `${CDN4}/isometric-dungeon/Isometric/chestClosed_E.png`,
|
|
44530
|
+
sword: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`,
|
|
44531
|
+
shield: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`
|
|
44532
|
+
};
|
|
44533
|
+
RoguelikeBoard.displayName = "RoguelikeBoard";
|
|
44534
|
+
}
|
|
44535
|
+
});
|
|
44536
|
+
function RoguelikeTemplate({
|
|
44537
|
+
entity,
|
|
44538
|
+
scale = 0.45,
|
|
44539
|
+
unitScale = 1,
|
|
44540
|
+
tiles = DEFAULT_ROGUELIKE_TILES,
|
|
44541
|
+
enemies,
|
|
44542
|
+
items,
|
|
44543
|
+
stairsX,
|
|
44544
|
+
stairsY,
|
|
44545
|
+
assetManifest = DEFAULT_ROGUELIKE_MANIFEST,
|
|
44546
|
+
gameEndEvent,
|
|
44547
|
+
className
|
|
44548
|
+
}) {
|
|
44549
|
+
const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
|
|
44550
|
+
if (!resolved && !tiles && !assetManifest) return null;
|
|
44551
|
+
return /* @__PURE__ */ jsx(
|
|
44552
|
+
RoguelikeBoard,
|
|
44553
|
+
{
|
|
44554
|
+
entity: resolved,
|
|
44555
|
+
tiles,
|
|
44556
|
+
enemies,
|
|
44557
|
+
items,
|
|
44558
|
+
stairsX,
|
|
44559
|
+
stairsY,
|
|
44560
|
+
assetManifest,
|
|
44561
|
+
scale,
|
|
44562
|
+
unitScale,
|
|
44563
|
+
moveEvent: "MOVE",
|
|
44564
|
+
playAgainEvent: "PLAY_AGAIN",
|
|
44565
|
+
gameEndEvent: gameEndEvent ?? "GAME_END",
|
|
44566
|
+
className
|
|
44567
|
+
}
|
|
44568
|
+
);
|
|
44569
|
+
}
|
|
44570
|
+
var CDN5, DEFAULT_ROGUELIKE_TILES, DEFAULT_ROGUELIKE_MANIFEST;
|
|
44571
|
+
var init_RoguelikeTemplate = __esm({
|
|
44572
|
+
"components/game/templates/RoguelikeTemplate.tsx"() {
|
|
44573
|
+
init_RoguelikeBoard();
|
|
44574
|
+
CDN5 = "https://almadar-kflow-assets.web.app/shared";
|
|
44575
|
+
DEFAULT_ROGUELIKE_TILES = [
|
|
44576
|
+
{ x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44577
|
+
{ x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44578
|
+
{ x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44579
|
+
{ x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44580
|
+
{ x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44581
|
+
{ x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44582
|
+
{ x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44583
|
+
{ x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44584
|
+
{ x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44585
|
+
{ x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44586
|
+
{ x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44587
|
+
{ x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
44588
|
+
{ x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
44589
|
+
{ x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44590
|
+
{ x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44591
|
+
{ x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44592
|
+
{ x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
44593
|
+
{ x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
44594
|
+
{ x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stairs_E.png` },
|
|
44595
|
+
{ x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44596
|
+
{ x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44597
|
+
{ x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44598
|
+
{ x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44599
|
+
{ x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
44600
|
+
{ x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` }
|
|
44601
|
+
];
|
|
44602
|
+
DEFAULT_ROGUELIKE_MANIFEST = {
|
|
44603
|
+
baseUrl: CDN5,
|
|
44604
|
+
terrains: {
|
|
44605
|
+
floor: "/isometric-dungeon/Isometric/dirt_E.png",
|
|
44606
|
+
wall: "/isometric-dungeon/Isometric/stoneInset_E.png",
|
|
44607
|
+
stairs: "/isometric-dungeon/Isometric/stairs_E.png",
|
|
44608
|
+
planks: "/isometric-dungeon/Isometric/planks_E.png"
|
|
44609
|
+
},
|
|
44610
|
+
units: {
|
|
44611
|
+
player: "/isometric-dungeon/Characters/Male/Male_0_Idle0.png",
|
|
44612
|
+
enemy: "/sprite-sheets/shadow-legion-sprite-sheet-sw.png"
|
|
44613
|
+
},
|
|
44614
|
+
features: {
|
|
44615
|
+
health_potion: "/isometric-dungeon/Isometric/chestClosed_E.png",
|
|
44616
|
+
sword: "/isometric-dungeon/Isometric/barrel_E.png",
|
|
44617
|
+
shield: "/isometric-dungeon/Isometric/barrel_E.png"
|
|
44618
|
+
}
|
|
44619
|
+
};
|
|
44620
|
+
RoguelikeTemplate.displayName = "RoguelikeTemplate";
|
|
44621
|
+
}
|
|
44622
|
+
});
|
|
42347
44623
|
|
|
42348
44624
|
// lib/traitRegistry.ts
|
|
42349
44625
|
function notifyListeners2() {
|
|
@@ -43597,7 +45873,7 @@ function getAllEvents(traits2) {
|
|
|
43597
45873
|
function EventDispatcherTab({ traits: traits2, schema }) {
|
|
43598
45874
|
const eventBus = useEventBus();
|
|
43599
45875
|
const { t } = useTranslate();
|
|
43600
|
-
const [
|
|
45876
|
+
const [log14, setLog] = React82.useState([]);
|
|
43601
45877
|
const prevStatesRef = React82.useRef(/* @__PURE__ */ new Map());
|
|
43602
45878
|
React82.useEffect(() => {
|
|
43603
45879
|
for (const trait of traits2) {
|
|
@@ -43661,9 +45937,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
|
|
|
43661
45937
|
/* @__PURE__ */ jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.otherEvents") }),
|
|
43662
45938
|
/* @__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)) })
|
|
43663
45939
|
] }),
|
|
43664
|
-
|
|
45940
|
+
log14.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
43665
45941
|
/* @__PURE__ */ jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.recentTransitions") }),
|
|
43666
|
-
/* @__PURE__ */ jsx(Stack, { gap: "xs", children:
|
|
45942
|
+
/* @__PURE__ */ jsx(Stack, { gap: "xs", children: log14.map((entry, i) => /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
|
|
43667
45943
|
/* @__PURE__ */ jsx("span", { className: "text-purple-400", children: entry.traitName }),
|
|
43668
45944
|
" ",
|
|
43669
45945
|
/* @__PURE__ */ jsx("span", { className: "text-gray-500", children: entry.from }),
|
|
@@ -44466,17 +46742,24 @@ function SequencerBoard({
|
|
|
44466
46742
|
const { emit } = useEventBus();
|
|
44467
46743
|
const { t } = useTranslate();
|
|
44468
46744
|
const resolved = boardEntity(entity);
|
|
44469
|
-
const maxSlots = num(resolved?.maxSlots);
|
|
46745
|
+
const maxSlots = num(resolved?.maxSlots) || 3;
|
|
44470
46746
|
const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
|
|
44471
46747
|
const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
|
|
44472
46748
|
const allowDuplicates = resolved?.allowDuplicates !== false;
|
|
46749
|
+
const entitySlots = Array.isArray(resolved?.slots) ? resolved.slots : [];
|
|
46750
|
+
const entityResult = str(resolved?.result);
|
|
46751
|
+
const entityAttempts = num(resolved?.attempts);
|
|
46752
|
+
const entityCurrentStep = typeof resolved?.currentStep === "number" ? resolved.currentStep : -1;
|
|
46753
|
+
const slots = Array.from({ length: maxSlots }, (_, i) => {
|
|
46754
|
+
const entitySlot = entitySlots.find((s) => s.index === i);
|
|
46755
|
+
if (!entitySlot?.placedActionId) return void 0;
|
|
46756
|
+
return availableActions.find((a) => a.id === entitySlot.placedActionId);
|
|
46757
|
+
});
|
|
46758
|
+
const isSuccess = entityResult === "win";
|
|
46759
|
+
const attempts = entityAttempts;
|
|
46760
|
+
const isPlayingBack = entityCurrentStep >= 0 && !isSuccess;
|
|
46761
|
+
const currentStep = entityCurrentStep;
|
|
44473
46762
|
const [headerError, setHeaderError] = useState(false);
|
|
44474
|
-
const [slots, setSlots] = useState(
|
|
44475
|
-
() => Array.from({ length: maxSlots }, () => void 0)
|
|
44476
|
-
);
|
|
44477
|
-
const [playState, setPlayState] = useState("idle");
|
|
44478
|
-
const [currentStep, setCurrentStep] = useState(-1);
|
|
44479
|
-
const [attempts, setAttempts] = useState(0);
|
|
44480
46763
|
const [slotFeedback, setSlotFeedback] = useState(
|
|
44481
46764
|
() => Array.from({ length: maxSlots }, () => null)
|
|
44482
46765
|
);
|
|
@@ -44485,11 +46768,6 @@ function SequencerBoard({
|
|
|
44485
46768
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
44486
46769
|
}, []);
|
|
44487
46770
|
const handleSlotDrop = useCallback((index, item) => {
|
|
44488
|
-
setSlots((prev) => {
|
|
44489
|
-
const next = [...prev];
|
|
44490
|
-
next[index] = item;
|
|
44491
|
-
return next;
|
|
44492
|
-
});
|
|
44493
46771
|
setSlotFeedback((prev) => {
|
|
44494
46772
|
const next = [...prev];
|
|
44495
46773
|
next[index] = null;
|
|
@@ -44499,11 +46777,6 @@ function SequencerBoard({
|
|
|
44499
46777
|
if (placeEvent) emit(`UI:${placeEvent}`, { slotIndex: index, actionId: item.id });
|
|
44500
46778
|
}, [emit, placeEvent]);
|
|
44501
46779
|
const handleSlotRemove = useCallback((index) => {
|
|
44502
|
-
setSlots((prev) => {
|
|
44503
|
-
const next = [...prev];
|
|
44504
|
-
next[index] = void 0;
|
|
44505
|
-
return next;
|
|
44506
|
-
});
|
|
44507
46780
|
setSlotFeedback((prev) => {
|
|
44508
46781
|
const next = [...prev];
|
|
44509
46782
|
next[index] = null;
|
|
@@ -44514,48 +46787,34 @@ function SequencerBoard({
|
|
|
44514
46787
|
}, [emit, removeEvent]);
|
|
44515
46788
|
const handleReset = useCallback(() => {
|
|
44516
46789
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
44517
|
-
setSlots(Array.from({ length: maxSlots }, () => void 0));
|
|
44518
|
-
setPlayState("idle");
|
|
44519
|
-
setCurrentStep(-1);
|
|
44520
|
-
setAttempts(0);
|
|
44521
46790
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
44522
46791
|
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
44523
46792
|
}, [maxSlots, playAgainEvent, emit]);
|
|
44524
46793
|
const filledSlots = slots.filter((s) => !!s);
|
|
44525
|
-
const canPlay = filledSlots.length > 0 &&
|
|
46794
|
+
const canPlay = filledSlots.length > 0 && !isPlayingBack && !isSuccess;
|
|
44526
46795
|
const handlePlay = useCallback(() => {
|
|
44527
46796
|
if (!canPlay) return;
|
|
44528
46797
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
44529
46798
|
emit("UI:PLAY_SOUND", { key: "confirm" });
|
|
44530
46799
|
const sequence = slots.map((s) => s?.id || "");
|
|
44531
|
-
|
|
44532
|
-
|
|
44533
|
-
}
|
|
44534
|
-
setPlayState("playing");
|
|
44535
|
-
setCurrentStep(0);
|
|
46800
|
+
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
46801
|
+
if (playEvent) emit(`UI:${playEvent}`, { sequence });
|
|
44536
46802
|
let step = 0;
|
|
44537
46803
|
const advance = () => {
|
|
44538
46804
|
step++;
|
|
46805
|
+
emit("UI:STEP", { step });
|
|
44539
46806
|
if (step >= maxSlots) {
|
|
46807
|
+
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
44540
46808
|
const playerSeq = slots.map((s) => s?.id);
|
|
44541
|
-
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
44542
46809
|
const success = solutions.some(
|
|
44543
46810
|
(sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
|
|
44544
46811
|
);
|
|
44545
|
-
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
44546
46812
|
if (success) {
|
|
44547
|
-
setPlayState("success");
|
|
44548
|
-
setCurrentStep(-1);
|
|
44549
46813
|
emit("UI:PLAY_SOUND", { key: "levelComplete" });
|
|
44550
|
-
if (completeEvent) {
|
|
44551
|
-
emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
44552
|
-
}
|
|
46814
|
+
if (completeEvent) emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
44553
46815
|
} else {
|
|
44554
|
-
setAttempts((prev) => prev + 1);
|
|
44555
46816
|
const feedback = computeSlotFeedback(playerSeq, solutions);
|
|
44556
46817
|
setSlotFeedback(feedback);
|
|
44557
|
-
setPlayState("idle");
|
|
44558
|
-
setCurrentStep(-1);
|
|
44559
46818
|
emit("UI:PLAY_SOUND", { key: "fail" });
|
|
44560
46819
|
const correctCount2 = feedback.filter((f3) => f3 === "correct").length;
|
|
44561
46820
|
for (let ci = 0; ci < correctCount2; ci++) {
|
|
@@ -44565,7 +46824,6 @@ function SequencerBoard({
|
|
|
44565
46824
|
}
|
|
44566
46825
|
}
|
|
44567
46826
|
} else {
|
|
44568
|
-
setCurrentStep(step);
|
|
44569
46827
|
timerRef.current = setTimeout(advance, stepDurationMs);
|
|
44570
46828
|
}
|
|
44571
46829
|
};
|
|
@@ -44615,7 +46873,7 @@ function SequencerBoard({
|
|
|
44615
46873
|
/* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
|
|
44616
46874
|
/* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between", children: [
|
|
44617
46875
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
|
|
44618
|
-
hasFeedback &&
|
|
46876
|
+
hasFeedback && !isPlayingBack && !isSuccess && /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
|
|
44619
46877
|
`${correctCount}/${maxSlots} `,
|
|
44620
46878
|
"\u2705"
|
|
44621
46879
|
] })
|
|
@@ -44627,7 +46885,7 @@ function SequencerBoard({
|
|
|
44627
46885
|
maxSlots,
|
|
44628
46886
|
onSlotDrop: handleSlotDrop,
|
|
44629
46887
|
onSlotRemove: handleSlotRemove,
|
|
44630
|
-
playing:
|
|
46888
|
+
playing: isPlayingBack,
|
|
44631
46889
|
currentStep,
|
|
44632
46890
|
categoryColors,
|
|
44633
46891
|
slotFeedback,
|
|
@@ -44635,7 +46893,7 @@ function SequencerBoard({
|
|
|
44635
46893
|
}
|
|
44636
46894
|
)
|
|
44637
46895
|
] }),
|
|
44638
|
-
|
|
46896
|
+
!isPlayingBack && /* @__PURE__ */ jsx(
|
|
44639
46897
|
ActionPalette,
|
|
44640
46898
|
{
|
|
44641
46899
|
actions: availableActions,
|
|
@@ -44645,8 +46903,8 @@ function SequencerBoard({
|
|
|
44645
46903
|
label: t("sequencer.dragActions")
|
|
44646
46904
|
}
|
|
44647
46905
|
),
|
|
44648
|
-
hasFeedback &&
|
|
44649
|
-
|
|
46906
|
+
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) }) }),
|
|
46907
|
+
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") }) }),
|
|
44650
46908
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
|
|
44651
46909
|
/* @__PURE__ */ jsx(
|
|
44652
46910
|
Button,
|
|
@@ -45602,9 +47860,9 @@ function VariablePanel({
|
|
|
45602
47860
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
|
|
45603
47861
|
variables.map((v) => {
|
|
45604
47862
|
const name = v.name == null ? "" : String(v.name);
|
|
45605
|
-
const value =
|
|
45606
|
-
const max =
|
|
45607
|
-
const min =
|
|
47863
|
+
const value = numField2(v.value);
|
|
47864
|
+
const max = numField2(v.max, 100);
|
|
47865
|
+
const min = numField2(v.min, 0);
|
|
45608
47866
|
const unit = v.unit == null ? "" : String(v.unit);
|
|
45609
47867
|
const pct = Math.round((value - min) / (max - min) * 100);
|
|
45610
47868
|
const isHigh = pct > 80;
|
|
@@ -45634,12 +47892,12 @@ function VariablePanel({
|
|
|
45634
47892
|
})
|
|
45635
47893
|
] });
|
|
45636
47894
|
}
|
|
45637
|
-
var
|
|
47895
|
+
var numField2;
|
|
45638
47896
|
var init_VariablePanel = __esm({
|
|
45639
47897
|
"components/game/organisms/puzzles/state-architect/VariablePanel.tsx"() {
|
|
45640
47898
|
init_atoms2();
|
|
45641
47899
|
init_cn();
|
|
45642
|
-
|
|
47900
|
+
numField2 = (v, fallback = 0) => {
|
|
45643
47901
|
const n = Number(v);
|
|
45644
47902
|
return Number.isFinite(n) ? n : fallback;
|
|
45645
47903
|
};
|
|
@@ -45696,6 +47954,9 @@ function StateArchitectBoard({
|
|
|
45696
47954
|
stepDurationMs = 600,
|
|
45697
47955
|
testEvent,
|
|
45698
47956
|
completeEvent,
|
|
47957
|
+
addTransitionEvent,
|
|
47958
|
+
removeTransitionEvent,
|
|
47959
|
+
playAgainEvent,
|
|
45699
47960
|
className
|
|
45700
47961
|
}) {
|
|
45701
47962
|
const { emit } = useEventBus();
|
|
@@ -45708,14 +47969,16 @@ function StateArchitectBoard({
|
|
|
45708
47969
|
const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
|
|
45709
47970
|
const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
|
|
45710
47971
|
const entityVariables = rows(resolved?.variables);
|
|
45711
|
-
const
|
|
47972
|
+
const transitions = entityTransitions;
|
|
47973
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
47974
|
+
const entityResult = str(resolved?.result) || "none";
|
|
47975
|
+
const isSuccess = entityResult === "win";
|
|
47976
|
+
const [isTesting, setIsTesting] = useState(false);
|
|
45712
47977
|
const [headerError, setHeaderError] = useState(false);
|
|
45713
|
-
const [playState, setPlayState] = useState("editing");
|
|
45714
47978
|
const [currentState, setCurrentState] = useState(initialState);
|
|
45715
47979
|
const [selectedState, setSelectedState] = useState(null);
|
|
45716
47980
|
const [testResults, setTestResults] = useState([]);
|
|
45717
47981
|
const [variables, setVariables] = useState(() => [...entityVariables]);
|
|
45718
|
-
const [attempts, setAttempts] = useState(0);
|
|
45719
47982
|
const timerRef = useRef(null);
|
|
45720
47983
|
const [addingFrom, setAddingFrom] = useState(null);
|
|
45721
47984
|
useEffect(() => () => {
|
|
@@ -45725,7 +47988,7 @@ function StateArchitectBoard({
|
|
|
45725
47988
|
const GRAPH_H = 400;
|
|
45726
47989
|
const positions = useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
|
|
45727
47990
|
const handleStateClick = useCallback((state) => {
|
|
45728
|
-
if (
|
|
47991
|
+
if (isTesting) return;
|
|
45729
47992
|
if (addingFrom) {
|
|
45730
47993
|
if (addingFrom !== state) {
|
|
45731
47994
|
const event = availableEvents[0] || "EVENT";
|
|
@@ -45735,20 +47998,20 @@ function StateArchitectBoard({
|
|
|
45735
47998
|
to: state,
|
|
45736
47999
|
event
|
|
45737
48000
|
};
|
|
45738
|
-
|
|
48001
|
+
if (addTransitionEvent) emit(`UI:${addTransitionEvent}`, { id: newTrans.id, from: newTrans.from, to: newTrans.to, event: newTrans.event });
|
|
45739
48002
|
}
|
|
45740
48003
|
setAddingFrom(null);
|
|
45741
48004
|
} else {
|
|
45742
48005
|
setSelectedState(state);
|
|
45743
48006
|
}
|
|
45744
|
-
}, [
|
|
48007
|
+
}, [isTesting, addingFrom, availableEvents, addTransitionEvent, emit]);
|
|
45745
48008
|
const handleStartAddTransition = useCallback(() => {
|
|
45746
48009
|
if (!selectedState) return;
|
|
45747
48010
|
setAddingFrom(selectedState);
|
|
45748
48011
|
}, [selectedState]);
|
|
45749
48012
|
const handleRemoveTransition = useCallback((transId) => {
|
|
45750
|
-
|
|
45751
|
-
}, []);
|
|
48013
|
+
if (removeTransitionEvent) emit(`UI:${removeTransitionEvent}`, { id: transId });
|
|
48014
|
+
}, [removeTransitionEvent, emit]);
|
|
45752
48015
|
const machine = useMemo(() => ({
|
|
45753
48016
|
name: entityName,
|
|
45754
48017
|
description: str(resolved?.description),
|
|
@@ -45762,16 +48025,16 @@ function StateArchitectBoard({
|
|
|
45762
48025
|
}))
|
|
45763
48026
|
}), [entityName, resolved, entityStates, currentState, transitions]);
|
|
45764
48027
|
const handleTest = useCallback(() => {
|
|
45765
|
-
if (
|
|
48028
|
+
if (isTesting) return;
|
|
45766
48029
|
if (testEvent) emit(`UI:${testEvent}`, {});
|
|
45767
|
-
|
|
48030
|
+
setIsTesting(true);
|
|
45768
48031
|
setTestResults([]);
|
|
45769
48032
|
const results = [];
|
|
45770
48033
|
let testIdx = 0;
|
|
45771
48034
|
const runNextTest = () => {
|
|
45772
48035
|
if (testIdx >= testCases.length) {
|
|
45773
48036
|
const allPassed = results.every((r) => r.passed);
|
|
45774
|
-
|
|
48037
|
+
setIsTesting(false);
|
|
45775
48038
|
setTestResults(results);
|
|
45776
48039
|
if (allPassed && completeEvent) {
|
|
45777
48040
|
emit(`UI:${completeEvent}`, {
|
|
@@ -45779,9 +48042,6 @@ function StateArchitectBoard({
|
|
|
45779
48042
|
passedTests: results.filter((r) => r.passed).length
|
|
45780
48043
|
});
|
|
45781
48044
|
}
|
|
45782
|
-
if (!allPassed) {
|
|
45783
|
-
setAttempts((prev) => prev + 1);
|
|
45784
|
-
}
|
|
45785
48045
|
return;
|
|
45786
48046
|
}
|
|
45787
48047
|
const testCase = testCases[testIdx];
|
|
@@ -45804,24 +48064,23 @@ function StateArchitectBoard({
|
|
|
45804
48064
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
45805
48065
|
};
|
|
45806
48066
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
45807
|
-
}, [
|
|
48067
|
+
}, [isTesting, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
|
|
45808
48068
|
const handleTryAgain = useCallback(() => {
|
|
45809
48069
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
45810
|
-
|
|
48070
|
+
setIsTesting(false);
|
|
45811
48071
|
setCurrentState(initialState);
|
|
45812
48072
|
setTestResults([]);
|
|
45813
48073
|
}, [initialState]);
|
|
45814
48074
|
const handleReset = useCallback(() => {
|
|
45815
48075
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
45816
|
-
|
|
45817
|
-
setPlayState("editing");
|
|
48076
|
+
setIsTesting(false);
|
|
45818
48077
|
setCurrentState(initialState);
|
|
45819
48078
|
setTestResults([]);
|
|
45820
48079
|
setVariables([...entityVariables]);
|
|
45821
48080
|
setSelectedState(null);
|
|
45822
48081
|
setAddingFrom(null);
|
|
45823
|
-
|
|
45824
|
-
}, [
|
|
48082
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
48083
|
+
}, [initialState, entityVariables, playAgainEvent, emit]);
|
|
45825
48084
|
const codeData = useMemo(() => ({
|
|
45826
48085
|
name: entityName,
|
|
45827
48086
|
states: entityStates,
|
|
@@ -45917,7 +48176,7 @@ function StateArchitectBoard({
|
|
|
45917
48176
|
]
|
|
45918
48177
|
}
|
|
45919
48178
|
),
|
|
45920
|
-
|
|
48179
|
+
!isTesting && /* @__PURE__ */ jsx(HStack, { gap: "sm", children: /* @__PURE__ */ jsx(
|
|
45921
48180
|
Button,
|
|
45922
48181
|
{
|
|
45923
48182
|
variant: "ghost",
|
|
@@ -45939,7 +48198,7 @@ function StateArchitectBoard({
|
|
|
45939
48198
|
t2.guardHint,
|
|
45940
48199
|
")"
|
|
45941
48200
|
] }),
|
|
45942
|
-
|
|
48201
|
+
!isTesting && /* @__PURE__ */ jsx(
|
|
45943
48202
|
Button,
|
|
45944
48203
|
{
|
|
45945
48204
|
variant: "ghost",
|
|
@@ -45971,21 +48230,21 @@ function StateArchitectBoard({
|
|
|
45971
48230
|
resolved.showCodeView !== false && /* @__PURE__ */ jsx(StateJsonView, { data: codeData, label: "View Code" })
|
|
45972
48231
|
] })
|
|
45973
48232
|
] }),
|
|
45974
|
-
|
|
45975
|
-
|
|
48233
|
+
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") }) }),
|
|
48234
|
+
!isTesting && !isSuccess && testResults.some((r) => !r.passed) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
45976
48235
|
/* @__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]) }) }),
|
|
45977
|
-
attempts >=
|
|
48236
|
+
!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: [
|
|
45978
48237
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
45979
48238
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
|
|
45980
48239
|
] }) })
|
|
45981
48240
|
] }),
|
|
45982
48241
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
|
|
45983
|
-
|
|
48242
|
+
!isTesting && !isSuccess && testResults.some((r) => !r.passed) ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsx(
|
|
45984
48243
|
Button,
|
|
45985
48244
|
{
|
|
45986
48245
|
variant: "primary",
|
|
45987
48246
|
onClick: handleTest,
|
|
45988
|
-
disabled:
|
|
48247
|
+
disabled: isTesting,
|
|
45989
48248
|
children: "\u25B6 " + t("game.runTests")
|
|
45990
48249
|
}
|
|
45991
48250
|
),
|
|
@@ -46491,6 +48750,289 @@ var init_ToastSlot = __esm({
|
|
|
46491
48750
|
ToastSlot.displayName = "ToastSlot";
|
|
46492
48751
|
}
|
|
46493
48752
|
});
|
|
48753
|
+
function tilesToIso(tiles) {
|
|
48754
|
+
return tiles.map((t) => ({
|
|
48755
|
+
x: t.x,
|
|
48756
|
+
y: t.y,
|
|
48757
|
+
terrain: t.terrain,
|
|
48758
|
+
terrainSprite: t.terrainSprite ?? TERRAIN_SPRITES[t.terrain ?? "ground"],
|
|
48759
|
+
passable: t.passable
|
|
48760
|
+
}));
|
|
48761
|
+
}
|
|
48762
|
+
function towersToUnits(towers) {
|
|
48763
|
+
return towers.map((t) => ({
|
|
48764
|
+
id: t.id,
|
|
48765
|
+
position: { x: t.x, y: t.y },
|
|
48766
|
+
name: "Tower",
|
|
48767
|
+
team: "player",
|
|
48768
|
+
sprite: TOWER_SPRITE,
|
|
48769
|
+
unitType: "guardian",
|
|
48770
|
+
health: 1,
|
|
48771
|
+
maxHealth: 1
|
|
48772
|
+
}));
|
|
48773
|
+
}
|
|
48774
|
+
function creepsToUnits(creeps) {
|
|
48775
|
+
return creeps.map((c) => ({
|
|
48776
|
+
id: c.id,
|
|
48777
|
+
position: { x: c.x, y: c.y },
|
|
48778
|
+
name: "Creep",
|
|
48779
|
+
team: "enemy",
|
|
48780
|
+
sprite: CREEP_SPRITE,
|
|
48781
|
+
unitType: "scrapper",
|
|
48782
|
+
health: c.hp,
|
|
48783
|
+
maxHealth: c.maxHp
|
|
48784
|
+
}));
|
|
48785
|
+
}
|
|
48786
|
+
function pathToFeatures(path) {
|
|
48787
|
+
return path.map((p2, i) => ({
|
|
48788
|
+
id: `path-${i}`,
|
|
48789
|
+
x: p2.x,
|
|
48790
|
+
y: p2.y,
|
|
48791
|
+
type: "path-marker",
|
|
48792
|
+
sprite: `${CDN6}world-map/road_straight.png`
|
|
48793
|
+
}));
|
|
48794
|
+
}
|
|
48795
|
+
function TowerDefenseBoard({
|
|
48796
|
+
entity,
|
|
48797
|
+
tiles: propTiles,
|
|
48798
|
+
path: propPath,
|
|
48799
|
+
towers: propTowers,
|
|
48800
|
+
creeps: propCreeps,
|
|
48801
|
+
gold: propGold,
|
|
48802
|
+
lives: propLives,
|
|
48803
|
+
wave: propWave,
|
|
48804
|
+
maxWaves: propMaxWaves,
|
|
48805
|
+
result: propResult,
|
|
48806
|
+
waveActive: propWaveActive,
|
|
48807
|
+
towerCost = 25,
|
|
48808
|
+
scale = 0.45,
|
|
48809
|
+
placeTowerEvent,
|
|
48810
|
+
startWaveEvent,
|
|
48811
|
+
playAgainEvent,
|
|
48812
|
+
gameEndEvent,
|
|
48813
|
+
className
|
|
48814
|
+
}) {
|
|
48815
|
+
const board = boardEntity(entity) ?? {};
|
|
48816
|
+
const eventBus = useEventBus();
|
|
48817
|
+
const { t } = useTranslate();
|
|
48818
|
+
const tiles = propTiles ?? rows(board.tiles);
|
|
48819
|
+
const path = propPath ?? rows(board.path);
|
|
48820
|
+
const towers = propTowers ?? rows(board.towers);
|
|
48821
|
+
const creeps = propCreeps ?? rows(board.creeps);
|
|
48822
|
+
const gold = propGold ?? num(board.gold, 100);
|
|
48823
|
+
const lives = propLives ?? num(board.lives, 20);
|
|
48824
|
+
const wave = propWave ?? num(board.wave, 1);
|
|
48825
|
+
const maxWaves = propMaxWaves ?? num(board.maxWaves, 5);
|
|
48826
|
+
const result = propResult ?? (str(board.result) || "none");
|
|
48827
|
+
const waveActive = propWaveActive ?? Boolean(board.waveActive);
|
|
48828
|
+
const [hoveredTile, setHoveredTile] = useState(null);
|
|
48829
|
+
const emittedGameEnd = useRef(false);
|
|
48830
|
+
if (result !== "none" && !emittedGameEnd.current) {
|
|
48831
|
+
emittedGameEnd.current = true;
|
|
48832
|
+
if (gameEndEvent) {
|
|
48833
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
48834
|
+
}
|
|
48835
|
+
}
|
|
48836
|
+
if (result === "none") {
|
|
48837
|
+
emittedGameEnd.current = false;
|
|
48838
|
+
}
|
|
48839
|
+
const towerPositions = useMemo(() => new Set(towers.map((t2) => `${t2.x},${t2.y}`)), [towers]);
|
|
48840
|
+
const pathPositions = useMemo(() => new Set(path.map((p2) => `${p2.x},${p2.y}`)), [path]);
|
|
48841
|
+
const validMoves = useMemo(() => {
|
|
48842
|
+
if (result !== "none" || gold < towerCost) return [];
|
|
48843
|
+
return tiles.filter(
|
|
48844
|
+
(t2) => t2.passable !== false && !towerPositions.has(`${t2.x},${t2.y}`) && !pathPositions.has(`${t2.x},${t2.y}`)
|
|
48845
|
+
).map((t2) => ({ x: t2.x, y: t2.y }));
|
|
48846
|
+
}, [tiles, towerPositions, pathPositions, result, gold, towerCost]);
|
|
48847
|
+
const isoTiles = useMemo(() => tilesToIso(tiles), [tiles]);
|
|
48848
|
+
const towerUnits = useMemo(() => towersToUnits(towers), [towers]);
|
|
48849
|
+
const creepUnits = useMemo(() => creepsToUnits(creeps), [creeps]);
|
|
48850
|
+
const isoUnits = useMemo(() => [...towerUnits, ...creepUnits], [towerUnits, creepUnits]);
|
|
48851
|
+
const pathFeatures = useMemo(() => pathToFeatures(path), [path]);
|
|
48852
|
+
const handleTileClick = useCallback((x, y) => {
|
|
48853
|
+
if (result !== "none") return;
|
|
48854
|
+
if (pathPositions.has(`${x},${y}`)) return;
|
|
48855
|
+
if (towerPositions.has(`${x},${y}`)) return;
|
|
48856
|
+
if (gold < towerCost) return;
|
|
48857
|
+
if (placeTowerEvent) {
|
|
48858
|
+
eventBus.emit(`UI:${placeTowerEvent}`, { x, y });
|
|
48859
|
+
}
|
|
48860
|
+
}, [result, pathPositions, towerPositions, gold, towerCost, placeTowerEvent, eventBus]);
|
|
48861
|
+
const handleStartWave = useCallback(() => {
|
|
48862
|
+
if (startWaveEvent) {
|
|
48863
|
+
eventBus.emit(`UI:${startWaveEvent}`, { wave });
|
|
48864
|
+
}
|
|
48865
|
+
}, [startWaveEvent, wave, eventBus]);
|
|
48866
|
+
const handlePlayAgain = useCallback(() => {
|
|
48867
|
+
if (playAgainEvent) {
|
|
48868
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
48869
|
+
}
|
|
48870
|
+
}, [playAgainEvent, eventBus]);
|
|
48871
|
+
const canPlaceTower = gold >= towerCost && result === "none";
|
|
48872
|
+
const canStartWave = !waveActive && result === "none";
|
|
48873
|
+
return /* @__PURE__ */ jsxs(VStack, { className: cn("tower-defense-board relative min-h-[600px] bg-background", className), gap: "none", children: [
|
|
48874
|
+
/* @__PURE__ */ jsxs(
|
|
48875
|
+
HStack,
|
|
48876
|
+
{
|
|
48877
|
+
className: "px-4 py-2 border-b border-border bg-surface",
|
|
48878
|
+
gap: "lg",
|
|
48879
|
+
align: "center",
|
|
48880
|
+
children: [
|
|
48881
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
48882
|
+
/* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("td.gold") ?? "Gold" }),
|
|
48883
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", className: "text-warning", children: gold })
|
|
48884
|
+
] }),
|
|
48885
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
48886
|
+
/* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("td.lives") ?? "Lives" }),
|
|
48887
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", className: lives <= 5 ? "text-error" : "text-success", children: lives })
|
|
48888
|
+
] }),
|
|
48889
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
48890
|
+
/* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("td.wave") ?? "Wave" }),
|
|
48891
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "body2", weight: "bold", children: [
|
|
48892
|
+
wave,
|
|
48893
|
+
" / ",
|
|
48894
|
+
maxWaves
|
|
48895
|
+
] })
|
|
48896
|
+
] }),
|
|
48897
|
+
/* @__PURE__ */ jsxs(Box, { className: "ml-auto", children: [
|
|
48898
|
+
canStartWave && /* @__PURE__ */ jsx(Button, { variant: "primary", size: "sm", onClick: handleStartWave, children: t("td.startWave") ?? "Start Wave" }),
|
|
48899
|
+
waveActive && /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-warning animate-pulse", children: t("td.waveInProgress") ?? "Wave in progress\u2026" })
|
|
48900
|
+
] })
|
|
48901
|
+
]
|
|
48902
|
+
}
|
|
48903
|
+
),
|
|
48904
|
+
/* @__PURE__ */ jsxs(Box, { className: "relative flex-1", children: [
|
|
48905
|
+
/* @__PURE__ */ jsx(
|
|
48906
|
+
IsometricCanvas_default,
|
|
48907
|
+
{
|
|
48908
|
+
tiles: isoTiles,
|
|
48909
|
+
units: isoUnits,
|
|
48910
|
+
features: pathFeatures,
|
|
48911
|
+
validMoves,
|
|
48912
|
+
hoveredTile,
|
|
48913
|
+
onTileClick: handleTileClick,
|
|
48914
|
+
onTileHover: (x, y) => setHoveredTile({ x, y }),
|
|
48915
|
+
onTileLeave: () => setHoveredTile(null),
|
|
48916
|
+
scale
|
|
48917
|
+
}
|
|
48918
|
+
),
|
|
48919
|
+
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)` }) })
|
|
48920
|
+
] }),
|
|
48921
|
+
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: [
|
|
48922
|
+
/* @__PURE__ */ jsx(
|
|
48923
|
+
Typography,
|
|
48924
|
+
{
|
|
48925
|
+
variant: "h2",
|
|
48926
|
+
className: cn(
|
|
48927
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
48928
|
+
result === "won" ? "text-warning" : "text-error"
|
|
48929
|
+
),
|
|
48930
|
+
children: result === "won" ? t("td.victory") ?? "Victory!" : t("td.defeat") ?? "Defeat!"
|
|
48931
|
+
}
|
|
48932
|
+
),
|
|
48933
|
+
/* @__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." }),
|
|
48934
|
+
/* @__PURE__ */ jsx(
|
|
48935
|
+
Button,
|
|
48936
|
+
{
|
|
48937
|
+
variant: "primary",
|
|
48938
|
+
className: "px-8 py-3 font-semibold",
|
|
48939
|
+
onClick: handlePlayAgain,
|
|
48940
|
+
children: t("td.playAgain") ?? "Play Again"
|
|
48941
|
+
}
|
|
48942
|
+
)
|
|
48943
|
+
] }) })
|
|
48944
|
+
] });
|
|
48945
|
+
}
|
|
48946
|
+
var CDN6, TERRAIN_SPRITES, TOWER_SPRITE, CREEP_SPRITE;
|
|
48947
|
+
var init_TowerDefenseBoard = __esm({
|
|
48948
|
+
"components/game/organisms/TowerDefenseBoard.tsx"() {
|
|
48949
|
+
"use client";
|
|
48950
|
+
init_cn();
|
|
48951
|
+
init_useEventBus();
|
|
48952
|
+
init_Box();
|
|
48953
|
+
init_Button();
|
|
48954
|
+
init_Typography();
|
|
48955
|
+
init_Stack();
|
|
48956
|
+
init_IsometricCanvas();
|
|
48957
|
+
init_boardEntity();
|
|
48958
|
+
CDN6 = "https://almadar-kflow-assets.web.app/shared/";
|
|
48959
|
+
TERRAIN_SPRITES = {
|
|
48960
|
+
ground: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_01.png`,
|
|
48961
|
+
path: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_09.png`,
|
|
48962
|
+
wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`
|
|
48963
|
+
};
|
|
48964
|
+
TOWER_SPRITE = `${CDN6}units/guardian.png`;
|
|
48965
|
+
CREEP_SPRITE = `${CDN6}units/scrapper.png`;
|
|
48966
|
+
TowerDefenseBoard.displayName = "TowerDefenseBoard";
|
|
48967
|
+
}
|
|
48968
|
+
});
|
|
48969
|
+
function TowerDefenseTemplate({
|
|
48970
|
+
entity,
|
|
48971
|
+
title = "Tower Defense",
|
|
48972
|
+
tiles,
|
|
48973
|
+
path,
|
|
48974
|
+
towers,
|
|
48975
|
+
gold,
|
|
48976
|
+
lives,
|
|
48977
|
+
maxWaves,
|
|
48978
|
+
towerCost,
|
|
48979
|
+
scale,
|
|
48980
|
+
placeTowerEvent,
|
|
48981
|
+
startWaveEvent,
|
|
48982
|
+
playAgainEvent,
|
|
48983
|
+
gameEndEvent,
|
|
48984
|
+
className
|
|
48985
|
+
}) {
|
|
48986
|
+
const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
|
|
48987
|
+
return /* @__PURE__ */ jsxs(
|
|
48988
|
+
Box,
|
|
48989
|
+
{
|
|
48990
|
+
display: "flex",
|
|
48991
|
+
fullHeight: true,
|
|
48992
|
+
className: cn("tower-defense-template flex-col", className),
|
|
48993
|
+
children: [
|
|
48994
|
+
/* @__PURE__ */ jsx(
|
|
48995
|
+
HStack,
|
|
48996
|
+
{
|
|
48997
|
+
gap: "sm",
|
|
48998
|
+
align: "center",
|
|
48999
|
+
className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
|
|
49000
|
+
children: /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title })
|
|
49001
|
+
}
|
|
49002
|
+
),
|
|
49003
|
+
/* @__PURE__ */ jsx(Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
49004
|
+
TowerDefenseBoard,
|
|
49005
|
+
{
|
|
49006
|
+
entity: resolved,
|
|
49007
|
+
tiles,
|
|
49008
|
+
path,
|
|
49009
|
+
towers,
|
|
49010
|
+
gold,
|
|
49011
|
+
lives,
|
|
49012
|
+
maxWaves,
|
|
49013
|
+
towerCost,
|
|
49014
|
+
scale,
|
|
49015
|
+
placeTowerEvent,
|
|
49016
|
+
startWaveEvent,
|
|
49017
|
+
playAgainEvent,
|
|
49018
|
+
gameEndEvent,
|
|
49019
|
+
className: "h-full"
|
|
49020
|
+
}
|
|
49021
|
+
) })
|
|
49022
|
+
]
|
|
49023
|
+
}
|
|
49024
|
+
);
|
|
49025
|
+
}
|
|
49026
|
+
var init_TowerDefenseTemplate = __esm({
|
|
49027
|
+
"components/game/templates/TowerDefenseTemplate.tsx"() {
|
|
49028
|
+
init_cn();
|
|
49029
|
+
init_Box();
|
|
49030
|
+
init_Stack();
|
|
49031
|
+
init_Typography();
|
|
49032
|
+
init_TowerDefenseBoard();
|
|
49033
|
+
TowerDefenseTemplate.displayName = "TowerDefenseTemplate";
|
|
49034
|
+
}
|
|
49035
|
+
});
|
|
46494
49036
|
function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
|
|
46495
49037
|
const eventBus = useEventBus();
|
|
46496
49038
|
const {
|
|
@@ -46689,17 +49231,12 @@ var init_UncontrolledBattleBoard = __esm({
|
|
|
46689
49231
|
}
|
|
46690
49232
|
});
|
|
46691
49233
|
function heroPosition(h) {
|
|
49234
|
+
if ("position" in h && h.position != null) return h.position;
|
|
46692
49235
|
return vec2(h.position);
|
|
46693
49236
|
}
|
|
46694
|
-
function heroOwner(h) {
|
|
46695
|
-
return str(h.owner);
|
|
46696
|
-
}
|
|
46697
49237
|
function heroMovement(h) {
|
|
46698
49238
|
return num(h.movement);
|
|
46699
49239
|
}
|
|
46700
|
-
function hexPassable(h) {
|
|
46701
|
-
return h.passable !== false;
|
|
46702
|
-
}
|
|
46703
49240
|
function defaultIsInRange(from, to, range) {
|
|
46704
49241
|
return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
|
|
46705
49242
|
}
|
|
@@ -46735,38 +49272,41 @@ function WorldMapBoard({
|
|
|
46735
49272
|
}) {
|
|
46736
49273
|
const eventBus = useEventBus();
|
|
46737
49274
|
const resolved = boardEntity(entity);
|
|
46738
|
-
const
|
|
46739
|
-
const
|
|
49275
|
+
const entityUnits = rows(resolved?.units);
|
|
49276
|
+
const entityTiles = rows(resolved?.tiles);
|
|
46740
49277
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
46741
49278
|
const selectedHeroId = resolved?.selectedHeroId ?? null;
|
|
46742
49279
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
46743
49280
|
const backgroundImage = resolved?.backgroundImage;
|
|
46744
49281
|
const [hoveredTile, setHoveredTile] = useState(null);
|
|
46745
|
-
const selectedHero = useMemo(
|
|
46746
|
-
() => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
|
|
46747
|
-
[heroes, selectedHeroId]
|
|
46748
|
-
);
|
|
46749
49282
|
const derivedTiles = useMemo(
|
|
46750
|
-
() =>
|
|
46751
|
-
x: num(
|
|
46752
|
-
y: num(
|
|
46753
|
-
terrain: str(
|
|
46754
|
-
terrainSprite:
|
|
49283
|
+
() => entityTiles.map((t) => ({
|
|
49284
|
+
x: num(t.x),
|
|
49285
|
+
y: num(t.y),
|
|
49286
|
+
terrain: str(t.terrain),
|
|
49287
|
+
terrainSprite: t.terrainSprite == null ? void 0 : str(t.terrainSprite),
|
|
49288
|
+
passable: t.passable !== false
|
|
46755
49289
|
})),
|
|
46756
|
-
[
|
|
49290
|
+
[entityTiles]
|
|
46757
49291
|
);
|
|
46758
49292
|
const tiles = propTiles ?? derivedTiles;
|
|
46759
49293
|
const baseUnits = useMemo(
|
|
46760
|
-
() => propUnits ??
|
|
46761
|
-
id: str(
|
|
46762
|
-
position: heroPosition(
|
|
46763
|
-
name: str(
|
|
46764
|
-
|
|
46765
|
-
|
|
46766
|
-
|
|
46767
|
-
|
|
49294
|
+
() => propUnits ?? entityUnits.map((u) => ({
|
|
49295
|
+
id: str(u.id),
|
|
49296
|
+
position: heroPosition(u),
|
|
49297
|
+
name: str(u.name),
|
|
49298
|
+
// lolo uses `team` field (not `owner`)
|
|
49299
|
+
team: str(u.team) === "enemy" ? "enemy" : "player",
|
|
49300
|
+
health: num(u.health) || 100,
|
|
49301
|
+
maxHealth: num(u.maxHealth) || 100,
|
|
49302
|
+
sprite: u.sprite == null ? void 0 : str(u.sprite)
|
|
46768
49303
|
})),
|
|
46769
|
-
[
|
|
49304
|
+
[entityUnits, propUnits]
|
|
49305
|
+
);
|
|
49306
|
+
const gameUnits = baseUnits;
|
|
49307
|
+
const selectedHero = useMemo(
|
|
49308
|
+
() => gameUnits.find((u) => u.id === selectedHeroId) ?? null,
|
|
49309
|
+
[gameUnits, selectedHeroId]
|
|
46770
49310
|
);
|
|
46771
49311
|
const MOVE_SPEED_MS_PER_TILE = 300;
|
|
46772
49312
|
const movementAnimRef = useRef(null);
|
|
@@ -46812,51 +49352,50 @@ function WorldMapBoard({
|
|
|
46812
49352
|
const validMoves = useMemo(() => {
|
|
46813
49353
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
46814
49354
|
const sp = heroPosition(selectedHero);
|
|
46815
|
-
const
|
|
49355
|
+
const sTeam = str(selectedHero.team);
|
|
46816
49356
|
const range = heroMovement(selectedHero);
|
|
46817
49357
|
const moves = [];
|
|
46818
|
-
|
|
46819
|
-
const
|
|
46820
|
-
const
|
|
46821
|
-
if (
|
|
46822
|
-
if (
|
|
46823
|
-
if (!isInRange(sp, { x:
|
|
46824
|
-
if (
|
|
46825
|
-
const
|
|
46826
|
-
return
|
|
49358
|
+
tiles.forEach((t) => {
|
|
49359
|
+
const tx = t.x;
|
|
49360
|
+
const ty = t.y;
|
|
49361
|
+
if (t.passable === false) return;
|
|
49362
|
+
if (tx === sp.x && ty === sp.y) return;
|
|
49363
|
+
if (!isInRange(sp, { x: tx, y: ty }, range)) return;
|
|
49364
|
+
if (gameUnits.some((u) => {
|
|
49365
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
49366
|
+
return up.x === tx && up.y === ty && str(u.team) === sTeam;
|
|
46827
49367
|
})) return;
|
|
46828
|
-
moves.push({ x:
|
|
49368
|
+
moves.push({ x: tx, y: ty });
|
|
46829
49369
|
});
|
|
46830
49370
|
return moves;
|
|
46831
|
-
}, [selectedHero,
|
|
49371
|
+
}, [selectedHero, tiles, gameUnits, isInRange]);
|
|
46832
49372
|
const attackTargets = useMemo(() => {
|
|
46833
49373
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
46834
49374
|
const sp = heroPosition(selectedHero);
|
|
46835
|
-
const
|
|
49375
|
+
const sTeam = str(selectedHero.team);
|
|
46836
49376
|
const range = heroMovement(selectedHero);
|
|
46837
|
-
return
|
|
46838
|
-
}, [selectedHero,
|
|
46839
|
-
const maxY = Math.max(...
|
|
49377
|
+
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 });
|
|
49378
|
+
}, [selectedHero, gameUnits, isInRange]);
|
|
49379
|
+
const maxY = Math.max(...tiles.map((t) => t.y), 0);
|
|
46840
49380
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
46841
49381
|
const tileToScreen = useCallback(
|
|
46842
49382
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
46843
49383
|
[scale, baseOffsetX]
|
|
46844
49384
|
);
|
|
46845
49385
|
const hoveredHex = useMemo(
|
|
46846
|
-
() => hoveredTile ?
|
|
46847
|
-
[hoveredTile,
|
|
49386
|
+
() => hoveredTile ? tiles.find((t) => t.x === hoveredTile.x && t.y === hoveredTile.y) ?? null : null,
|
|
49387
|
+
[hoveredTile, tiles]
|
|
46848
49388
|
);
|
|
46849
49389
|
const hoveredHero = useMemo(
|
|
46850
|
-
() => hoveredTile ?
|
|
46851
|
-
const
|
|
46852
|
-
return
|
|
49390
|
+
() => hoveredTile ? gameUnits.find((u) => {
|
|
49391
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
49392
|
+
return up.x === hoveredTile.x && up.y === hoveredTile.y;
|
|
46853
49393
|
}) ?? null : null,
|
|
46854
|
-
[hoveredTile,
|
|
49394
|
+
[hoveredTile, gameUnits]
|
|
46855
49395
|
);
|
|
46856
49396
|
const handleTileClick = useCallback((x, y) => {
|
|
46857
49397
|
if (movementAnimRef.current) return;
|
|
46858
|
-
const
|
|
46859
|
-
if (!hex) return;
|
|
49398
|
+
const tile = tiles.find((t) => t.x === x && t.y === y);
|
|
46860
49399
|
if (tileClickEvent) {
|
|
46861
49400
|
eventBus.emit(`UI:${tileClickEvent}`, { x, y });
|
|
46862
49401
|
}
|
|
@@ -46867,38 +49406,39 @@ function WorldMapBoard({
|
|
|
46867
49406
|
if (heroMoveEvent) {
|
|
46868
49407
|
eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
|
|
46869
49408
|
}
|
|
46870
|
-
const feature = str(
|
|
49409
|
+
const feature = tile ? str(tile.feature) : "";
|
|
46871
49410
|
if (feature && feature !== "none") {
|
|
46872
|
-
|
|
49411
|
+
const tileRow = tile;
|
|
49412
|
+
onFeatureEnter?.(heroId, tileRow);
|
|
46873
49413
|
if (featureEnterEvent) {
|
|
46874
|
-
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
|
|
49414
|
+
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex: tileRow });
|
|
46875
49415
|
}
|
|
46876
49416
|
}
|
|
46877
49417
|
});
|
|
46878
49418
|
return;
|
|
46879
49419
|
}
|
|
46880
|
-
const enemy =
|
|
46881
|
-
const
|
|
46882
|
-
return
|
|
49420
|
+
const enemy = gameUnits.find((u) => {
|
|
49421
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
49422
|
+
return up.x === x && up.y === y && str(u.team) === "enemy";
|
|
46883
49423
|
});
|
|
46884
49424
|
if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
|
|
46885
49425
|
const attackerId = str(selectedHero.id);
|
|
46886
|
-
const defenderId =
|
|
49426
|
+
const defenderId = enemy.id;
|
|
46887
49427
|
onBattleEncounter?.(attackerId, defenderId);
|
|
46888
49428
|
if (battleEncounterEvent) {
|
|
46889
49429
|
eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
|
|
46890
49430
|
}
|
|
46891
49431
|
}
|
|
46892
|
-
}, [
|
|
49432
|
+
}, [tiles, gameUnits, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
|
|
46893
49433
|
const handleUnitClick = useCallback((unitId) => {
|
|
46894
|
-
const
|
|
46895
|
-
if (
|
|
49434
|
+
const unit = gameUnits.find((u) => u.id === unitId);
|
|
49435
|
+
if (unit && (str(unit.team) === "player" || allowMoveAllHeroes)) {
|
|
46896
49436
|
onHeroSelect?.(unitId);
|
|
46897
49437
|
if (heroSelectEvent) {
|
|
46898
49438
|
eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
|
|
46899
49439
|
}
|
|
46900
49440
|
}
|
|
46901
|
-
}, [
|
|
49441
|
+
}, [gameUnits, onHeroSelect, allowMoveAllHeroes, eventBus, heroSelectEvent]);
|
|
46902
49442
|
const selectHero = useCallback((id) => {
|
|
46903
49443
|
onHeroSelect?.(id);
|
|
46904
49444
|
if (heroSelectEvent) {
|
|
@@ -47002,48 +49542,48 @@ function WorldMapTemplate({
|
|
|
47002
49542
|
}
|
|
47003
49543
|
);
|
|
47004
49544
|
}
|
|
47005
|
-
var
|
|
49545
|
+
var CDN7, DEFAULT_WORLDMAP_TILES, DEFAULT_WORLDMAP_UNITS, DEFAULT_WORLDMAP_FEATURES, DEFAULT_WORLDMAP_MANIFEST;
|
|
47006
49546
|
var init_WorldMapTemplate = __esm({
|
|
47007
49547
|
"components/game/templates/WorldMapTemplate.tsx"() {
|
|
47008
49548
|
init_WorldMapBoard();
|
|
47009
|
-
|
|
49549
|
+
CDN7 = "https://almadar-kflow-assets.web.app/shared";
|
|
47010
49550
|
DEFAULT_WORLDMAP_TILES = [
|
|
47011
|
-
{ x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47012
|
-
{ x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47013
|
-
{ x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${
|
|
47014
|
-
{ x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47015
|
-
{ x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47016
|
-
{ x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47017
|
-
{ x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
47018
|
-
{ x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
47019
|
-
{ x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
47020
|
-
{ x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${
|
|
47021
|
-
{ x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${
|
|
47022
|
-
{ x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
47023
|
-
{ x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
47024
|
-
{ x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
47025
|
-
{ x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47026
|
-
{ x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47027
|
-
{ x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
47028
|
-
{ x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
47029
|
-
{ x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
47030
|
-
{ x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47031
|
-
{ x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47032
|
-
{ x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${
|
|
47033
|
-
{ x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${
|
|
47034
|
-
{ x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
47035
|
-
{ x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49551
|
+
{ x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49552
|
+
{ x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49553
|
+
{ x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
49554
|
+
{ x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49555
|
+
{ x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49556
|
+
{ x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49557
|
+
{ x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49558
|
+
{ x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49559
|
+
{ x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49560
|
+
{ x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
49561
|
+
{ x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
49562
|
+
{ x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49563
|
+
{ x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
|
|
49564
|
+
{ x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49565
|
+
{ x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49566
|
+
{ x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49567
|
+
{ x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49568
|
+
{ x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49569
|
+
{ x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
|
|
49570
|
+
{ x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49571
|
+
{ x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49572
|
+
{ x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
49573
|
+
{ x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
49574
|
+
{ x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
49575
|
+
{ x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` }
|
|
47036
49576
|
];
|
|
47037
49577
|
DEFAULT_WORLDMAP_UNITS = [
|
|
47038
|
-
{ id: "h1", position: { x: 1, y: 1 }, unitType: "hero", name: "Amir", team: "player", health: 10, maxHealth: 10, sprite: `${
|
|
47039
|
-
{ id: "h2", position: { x: 3, y: 3 }, unitType: "scout", name: "Archivist", team: "player", health: 10, maxHealth: 10, sprite: `${
|
|
49578
|
+
{ 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` },
|
|
49579
|
+
{ 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` }
|
|
47040
49580
|
];
|
|
47041
49581
|
DEFAULT_WORLDMAP_FEATURES = [
|
|
47042
|
-
{ id: "f1", x: 2, y: 2, type: "capital", sprite: `${
|
|
47043
|
-
{ id: "f2", x: 4, y: 2, type: "power_node", sprite: `${
|
|
49582
|
+
{ id: "f1", x: 2, y: 2, type: "capital", sprite: `${CDN7}/scenes/world/capital.png` },
|
|
49583
|
+
{ id: "f2", x: 4, y: 2, type: "power_node", sprite: `${CDN7}/world-map/power_node.png` }
|
|
47044
49584
|
];
|
|
47045
49585
|
DEFAULT_WORLDMAP_MANIFEST = {
|
|
47046
|
-
baseUrl:
|
|
49586
|
+
baseUrl: CDN7,
|
|
47047
49587
|
terrains: {
|
|
47048
49588
|
grass: "/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png",
|
|
47049
49589
|
water: "/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png",
|
|
@@ -47088,7 +49628,7 @@ function lazyThree(name, loader) {
|
|
|
47088
49628
|
ThreeWrapper.displayName = `Lazy(${name})`;
|
|
47089
49629
|
return ThreeWrapper;
|
|
47090
49630
|
}
|
|
47091
|
-
var ThreeBoundary, FeatureRenderer,
|
|
49631
|
+
var ThreeBoundary, FeatureRenderer, GameCanvas3D2, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
|
|
47092
49632
|
var init_component_registry_generated = __esm({
|
|
47093
49633
|
"components/core/organisms/component-registry.generated.ts"() {
|
|
47094
49634
|
init_AboutPageTemplate();
|
|
@@ -47198,6 +49738,7 @@ var init_component_registry_generated = __esm({
|
|
|
47198
49738
|
init_FormSectionHeader();
|
|
47199
49739
|
init_GameAudioProvider();
|
|
47200
49740
|
init_GameAudioToggle();
|
|
49741
|
+
init_GameBoard3D();
|
|
47201
49742
|
init_GameCanvas2D();
|
|
47202
49743
|
init_GameHud();
|
|
47203
49744
|
init_GameMenu();
|
|
@@ -47255,7 +49796,9 @@ var init_component_registry_generated = __esm({
|
|
|
47255
49796
|
init_PageHeader();
|
|
47256
49797
|
init_Pagination();
|
|
47257
49798
|
init_PatternTile();
|
|
49799
|
+
init_PlatformerBoard();
|
|
47258
49800
|
init_PlatformerCanvas();
|
|
49801
|
+
init_PlatformerTemplate();
|
|
47259
49802
|
init_Popover();
|
|
47260
49803
|
init_PositionedCanvas();
|
|
47261
49804
|
init_PowerupSlots();
|
|
@@ -47279,6 +49822,8 @@ var init_component_registry_generated = __esm({
|
|
|
47279
49822
|
init_ResourceBar();
|
|
47280
49823
|
init_ResourceCounter();
|
|
47281
49824
|
init_RichBlockEditor();
|
|
49825
|
+
init_RoguelikeBoard();
|
|
49826
|
+
init_RoguelikeTemplate();
|
|
47282
49827
|
init_RuntimeDebugger2();
|
|
47283
49828
|
init_ScaledDiagram();
|
|
47284
49829
|
init_ScoreBoard();
|
|
@@ -47351,6 +49896,8 @@ var init_component_registry_generated = __esm({
|
|
|
47351
49896
|
init_Toast();
|
|
47352
49897
|
init_ToastSlot();
|
|
47353
49898
|
init_Tooltip();
|
|
49899
|
+
init_TowerDefenseBoard();
|
|
49900
|
+
init_TowerDefenseTemplate();
|
|
47354
49901
|
init_TraitFrame();
|
|
47355
49902
|
init_TraitSlot();
|
|
47356
49903
|
init_TrendIndicator();
|
|
@@ -47395,7 +49942,7 @@ var init_component_registry_generated = __esm({
|
|
|
47395
49942
|
}
|
|
47396
49943
|
};
|
|
47397
49944
|
FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
|
|
47398
|
-
|
|
49945
|
+
GameCanvas3D2 = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
|
|
47399
49946
|
GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
47400
49947
|
GameCanvas3DCastleTemplate = lazyThree("GameCanvas3DCastleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
47401
49948
|
GameCanvas3DWorldMapTemplate = lazyThree("GameCanvas3DWorldMapTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
@@ -47514,8 +50061,9 @@ var init_component_registry_generated = __esm({
|
|
|
47514
50061
|
"FormSectionHeader": FormSectionHeader,
|
|
47515
50062
|
"GameAudioProvider": GameAudioProvider,
|
|
47516
50063
|
"GameAudioToggle": GameAudioToggle,
|
|
50064
|
+
"GameBoard3D": GameBoard3D,
|
|
47517
50065
|
"GameCanvas2D": GameCanvas2D,
|
|
47518
|
-
"GameCanvas3D":
|
|
50066
|
+
"GameCanvas3D": GameCanvas3D2,
|
|
47519
50067
|
"GameCanvas3DBattleTemplate": GameCanvas3DBattleTemplate,
|
|
47520
50068
|
"GameCanvas3DCastleTemplate": GameCanvas3DCastleTemplate,
|
|
47521
50069
|
"GameCanvas3DWorldMapTemplate": GameCanvas3DWorldMapTemplate,
|
|
@@ -47577,7 +50125,9 @@ var init_component_registry_generated = __esm({
|
|
|
47577
50125
|
"PageHeader": PageHeader,
|
|
47578
50126
|
"Pagination": Pagination,
|
|
47579
50127
|
"PatternTile": PatternTile,
|
|
50128
|
+
"PlatformerBoard": PlatformerBoard,
|
|
47580
50129
|
"PlatformerCanvas": PlatformerCanvas,
|
|
50130
|
+
"PlatformerTemplate": PlatformerTemplate,
|
|
47581
50131
|
"Popover": Popover,
|
|
47582
50132
|
"PositionedCanvas": PositionedCanvas,
|
|
47583
50133
|
"PowerupSlots": PowerupSlots,
|
|
@@ -47601,6 +50151,8 @@ var init_component_registry_generated = __esm({
|
|
|
47601
50151
|
"ResourceBar": ResourceBar,
|
|
47602
50152
|
"ResourceCounter": ResourceCounter,
|
|
47603
50153
|
"RichBlockEditor": RichBlockEditor,
|
|
50154
|
+
"RoguelikeBoard": RoguelikeBoard,
|
|
50155
|
+
"RoguelikeTemplate": RoguelikeTemplate,
|
|
47604
50156
|
"RuntimeDebugger": RuntimeDebugger,
|
|
47605
50157
|
"ScaledDiagram": ScaledDiagram,
|
|
47606
50158
|
"ScoreBoard": ScoreBoard,
|
|
@@ -47676,6 +50228,8 @@ var init_component_registry_generated = __esm({
|
|
|
47676
50228
|
"Toast": Toast,
|
|
47677
50229
|
"ToastSlot": ToastSlot,
|
|
47678
50230
|
"Tooltip": Tooltip,
|
|
50231
|
+
"TowerDefenseBoard": TowerDefenseBoard,
|
|
50232
|
+
"TowerDefenseTemplate": TowerDefenseTemplate,
|
|
47679
50233
|
"TraitFrame": TraitFrame,
|
|
47680
50234
|
"TraitSlot": TraitSlot,
|
|
47681
50235
|
"TrendIndicator": TrendIndicator,
|
|
@@ -49572,7 +52126,7 @@ function clearSchemaCache() {
|
|
|
49572
52126
|
|
|
49573
52127
|
// runtime/index.ts
|
|
49574
52128
|
init_EntitySchemaContext();
|
|
49575
|
-
var
|
|
52129
|
+
var log12 = createLogger("almadar:ui:trait-provider");
|
|
49576
52130
|
var TraitContext = createContext(null);
|
|
49577
52131
|
function TraitProvider({
|
|
49578
52132
|
traits: traitBindings,
|
|
@@ -49589,7 +52143,7 @@ function TraitProvider({
|
|
|
49589
52143
|
currentState: stateName,
|
|
49590
52144
|
availableEvents: trait.transitions.filter((t) => t.from === stateName).map((t) => t.event),
|
|
49591
52145
|
dispatch: (eventKey, payload) => {
|
|
49592
|
-
|
|
52146
|
+
log12.debug("Dispatch", () => ({
|
|
49593
52147
|
trait: trait.name,
|
|
49594
52148
|
event: eventKey,
|
|
49595
52149
|
payloadKeys: payload ? Object.keys(payload) : []
|
|
@@ -50173,7 +52727,7 @@ OrbitalThemeProvider.displayName = "OrbitalThemeProvider";
|
|
|
50173
52727
|
init_navigation();
|
|
50174
52728
|
init_verificationRegistry();
|
|
50175
52729
|
var PERF_NAMESPACE = "almadar:perf:canvas";
|
|
50176
|
-
var
|
|
52730
|
+
var log13 = createLogger(PERF_NAMESPACE);
|
|
50177
52731
|
var RING_SIZE = 50;
|
|
50178
52732
|
var ring = [];
|
|
50179
52733
|
var writeIdx = 0;
|
|
@@ -50228,7 +52782,7 @@ function perfEnd(name, startToken, detail) {
|
|
|
50228
52782
|
}
|
|
50229
52783
|
}
|
|
50230
52784
|
push({ name, durationMs, ts: endTs, detail });
|
|
50231
|
-
|
|
52785
|
+
log13.debug(name, () => ({ durationMs, ...detail ?? {} }));
|
|
50232
52786
|
}
|
|
50233
52787
|
function perfTime(name, fn, detail) {
|
|
50234
52788
|
const t = perfStart(name);
|
|
@@ -50246,7 +52800,7 @@ var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, com
|
|
|
50246
52800
|
ts: commitTime,
|
|
50247
52801
|
detail: { baseDuration }
|
|
50248
52802
|
});
|
|
50249
|
-
|
|
52803
|
+
log13.debug(`profiler:${id}:${phase}`, () => ({ actualDuration, baseDuration }));
|
|
50250
52804
|
};
|
|
50251
52805
|
function getPerfSnapshot() {
|
|
50252
52806
|
if (ring.length < RING_SIZE) return ring.slice();
|