@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/avl/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import * as React91 from 'react';
|
|
3
|
-
import React91__default, { createContext, useState, useMemo, useRef, useEffect, useContext, useCallback, Suspense, useLayoutEffect, Profiler, useReducer, useSyncExternalStore, lazy, useId } from 'react';
|
|
3
|
+
import React91__default, { createContext, useState, useMemo, useRef, useEffect, useContext, useCallback, Suspense, useLayoutEffect, Profiler, useReducer, useSyncExternalStore, lazy, useId, forwardRef, useImperativeHandle, Component } from 'react';
|
|
4
4
|
import { OrbitalProvider, EventBusContext, useTraitScope, VerificationProvider, TraitScopeProvider } from '@almadar/ui/providers';
|
|
5
5
|
import { createLogger, isLogLevelEnabled } from '@almadar/logger';
|
|
6
6
|
import ELK from 'elkjs/lib/elk.bundled.js';
|
|
@@ -39,6 +39,12 @@ import { FieldTypeSchema, isInlineTrait, isPageReference, isEntityCall, schemaTo
|
|
|
39
39
|
import { useDroppable, useDraggable, DndContext, DragOverlay, useSensors, useSensor, PointerSensor, KeyboardSensor, pointerWithin, rectIntersection, closestCorners } from '@dnd-kit/core';
|
|
40
40
|
import { sortableKeyboardCoordinates, useSortable, arrayMove, SortableContext, rectSortingStrategy, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
|
41
41
|
import { CSS } from '@dnd-kit/utilities';
|
|
42
|
+
import * as THREE from 'three';
|
|
43
|
+
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
|
44
|
+
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
|
|
45
|
+
import { GLTFLoader as GLTFLoader$1 } from 'three/examples/jsm/loaders/GLTFLoader';
|
|
46
|
+
import { Canvas, useThree } from '@react-three/fiber';
|
|
47
|
+
import { Grid as Grid$1, OrbitControls } from '@react-three/drei';
|
|
42
48
|
import { getPatternDefinition, getComponentForPattern as getComponentForPattern$1, isEntityAwarePattern } from '@almadar/patterns';
|
|
43
49
|
import { OrbitalServerRuntime } from '@almadar/runtime/OrbitalServerRuntime';
|
|
44
50
|
import { InMemoryPersistence, StateMachineManager, createContextFromBindings, interpolateValue, collectDeclaredConfigDefaults, createServerEffectHandlers, EffectExecutor } from '@almadar/runtime';
|
|
@@ -3173,8 +3179,8 @@ var init_Typography = __esm({
|
|
|
3173
3179
|
children
|
|
3174
3180
|
}) => {
|
|
3175
3181
|
const variant = variantProp ?? (level ? `h${level}` : "body1");
|
|
3176
|
-
const
|
|
3177
|
-
const Comp =
|
|
3182
|
+
const Component2 = as || defaultElements[variant];
|
|
3183
|
+
const Comp = Component2;
|
|
3178
3184
|
return /* @__PURE__ */ jsx(
|
|
3179
3185
|
Comp,
|
|
3180
3186
|
{
|
|
@@ -3483,7 +3489,7 @@ var init_Box = __esm({
|
|
|
3483
3489
|
position,
|
|
3484
3490
|
className,
|
|
3485
3491
|
children,
|
|
3486
|
-
as:
|
|
3492
|
+
as: Component2 = "div",
|
|
3487
3493
|
action,
|
|
3488
3494
|
actionPayload,
|
|
3489
3495
|
hoverEvent,
|
|
@@ -3515,7 +3521,7 @@ var init_Box = __esm({
|
|
|
3515
3521
|
}, [hoverEvent, eventBus, onMouseLeave]);
|
|
3516
3522
|
const isClickable = action || onClick;
|
|
3517
3523
|
return React91__default.createElement(
|
|
3518
|
-
|
|
3524
|
+
Component2,
|
|
3519
3525
|
{
|
|
3520
3526
|
ref,
|
|
3521
3527
|
className: cn(
|
|
@@ -3591,7 +3597,7 @@ var init_Stack = __esm({
|
|
|
3591
3597
|
className,
|
|
3592
3598
|
style,
|
|
3593
3599
|
children,
|
|
3594
|
-
as:
|
|
3600
|
+
as: Component2 = "div",
|
|
3595
3601
|
onClick,
|
|
3596
3602
|
onKeyDown,
|
|
3597
3603
|
role,
|
|
@@ -3609,7 +3615,7 @@ var init_Stack = __esm({
|
|
|
3609
3615
|
};
|
|
3610
3616
|
const isHorizontal = direction === "horizontal";
|
|
3611
3617
|
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";
|
|
3612
|
-
const Comp =
|
|
3618
|
+
const Comp = Component2;
|
|
3613
3619
|
return /* @__PURE__ */ jsx(
|
|
3614
3620
|
Comp,
|
|
3615
3621
|
{
|
|
@@ -4523,9 +4529,9 @@ function resolvePhosphor(name, weight, family) {
|
|
|
4523
4529
|
(lib) => {
|
|
4524
4530
|
const PhosphorComp = lib[target];
|
|
4525
4531
|
if (!PhosphorComp || typeof PhosphorComp !== "object") return null;
|
|
4526
|
-
const
|
|
4532
|
+
const Component2 = PhosphorComp;
|
|
4527
4533
|
const Adapter = (props) => /* @__PURE__ */ jsx(
|
|
4528
|
-
|
|
4534
|
+
Component2,
|
|
4529
4535
|
{
|
|
4530
4536
|
weight,
|
|
4531
4537
|
className: props.className,
|
|
@@ -4549,9 +4555,9 @@ function resolveTabler(name, family) {
|
|
|
4549
4555
|
(lib) => {
|
|
4550
4556
|
const TablerComp = lib[target];
|
|
4551
4557
|
if (!TablerComp || typeof TablerComp !== "object") return null;
|
|
4552
|
-
const
|
|
4558
|
+
const Component2 = TablerComp;
|
|
4553
4559
|
const Adapter = (props) => /* @__PURE__ */ jsx(
|
|
4554
|
-
|
|
4560
|
+
Component2,
|
|
4555
4561
|
{
|
|
4556
4562
|
stroke: props.strokeWidth ?? 1.5,
|
|
4557
4563
|
className: props.className,
|
|
@@ -4575,9 +4581,9 @@ function resolveFa(name, family) {
|
|
|
4575
4581
|
(lib) => {
|
|
4576
4582
|
const FaComp = lib[target];
|
|
4577
4583
|
if (!FaComp || typeof FaComp !== "function") return null;
|
|
4578
|
-
const
|
|
4584
|
+
const Component2 = FaComp;
|
|
4579
4585
|
const Adapter = (props) => /* @__PURE__ */ jsx(
|
|
4580
|
-
|
|
4586
|
+
Component2,
|
|
4581
4587
|
{
|
|
4582
4588
|
className: props.className,
|
|
4583
4589
|
style: props.style,
|
|
@@ -7969,10 +7975,10 @@ var init_Center = __esm({
|
|
|
7969
7975
|
className,
|
|
7970
7976
|
style,
|
|
7971
7977
|
children,
|
|
7972
|
-
as:
|
|
7978
|
+
as: Component2 = "div"
|
|
7973
7979
|
}) => {
|
|
7974
7980
|
const mergedStyle = minHeight ? { minHeight, ...style } : style;
|
|
7975
|
-
const Comp =
|
|
7981
|
+
const Comp = Component2;
|
|
7976
7982
|
return /* @__PURE__ */ jsx(
|
|
7977
7983
|
Comp,
|
|
7978
7984
|
{
|
|
@@ -10766,6 +10772,7 @@ var init_HealthBar = __esm({
|
|
|
10766
10772
|
}
|
|
10767
10773
|
});
|
|
10768
10774
|
function ScoreDisplay({
|
|
10775
|
+
assetUrl = DEFAULT_ASSET_URL,
|
|
10769
10776
|
value,
|
|
10770
10777
|
label,
|
|
10771
10778
|
icon,
|
|
@@ -10812,14 +10819,24 @@ function ScoreDisplay({
|
|
|
10812
10819
|
className
|
|
10813
10820
|
),
|
|
10814
10821
|
children: [
|
|
10815
|
-
|
|
10822
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
10823
|
+
"img",
|
|
10824
|
+
{
|
|
10825
|
+
src: assetUrl,
|
|
10826
|
+
alt: "",
|
|
10827
|
+
width: 20,
|
|
10828
|
+
height: 20,
|
|
10829
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
10830
|
+
className: "flex-shrink-0"
|
|
10831
|
+
}
|
|
10832
|
+
) : icon ? /* @__PURE__ */ jsx("span", { className: "flex-shrink-0", children: typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon }) : /* @__PURE__ */ jsx(Icon, { icon }) }) : null,
|
|
10816
10833
|
label && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label }),
|
|
10817
10834
|
/* @__PURE__ */ jsx("span", { className: "tabular-nums", children: formattedValue })
|
|
10818
10835
|
]
|
|
10819
10836
|
}
|
|
10820
10837
|
);
|
|
10821
10838
|
}
|
|
10822
|
-
var sizeMap2;
|
|
10839
|
+
var sizeMap2, DEFAULT_ASSET_URL;
|
|
10823
10840
|
var init_ScoreDisplay = __esm({
|
|
10824
10841
|
"components/game/atoms/ScoreDisplay.tsx"() {
|
|
10825
10842
|
"use client";
|
|
@@ -10831,10 +10848,12 @@ var init_ScoreDisplay = __esm({
|
|
|
10831
10848
|
lg: "text-2xl",
|
|
10832
10849
|
xl: "text-4xl"
|
|
10833
10850
|
};
|
|
10851
|
+
DEFAULT_ASSET_URL = "https://almadar-kflow-assets.web.app/shared/effects/particles/star_01.png";
|
|
10834
10852
|
ScoreDisplay.displayName = "ScoreDisplay";
|
|
10835
10853
|
}
|
|
10836
10854
|
});
|
|
10837
10855
|
function ControlButton({
|
|
10856
|
+
assetUrl = DEFAULT_ASSET_URL2,
|
|
10838
10857
|
label,
|
|
10839
10858
|
icon,
|
|
10840
10859
|
size = "md",
|
|
@@ -10881,7 +10900,7 @@ function ControlButton({
|
|
|
10881
10900
|
},
|
|
10882
10901
|
[isPressed, releaseEvent, eventBus, onRelease]
|
|
10883
10902
|
);
|
|
10884
|
-
return /* @__PURE__ */
|
|
10903
|
+
return /* @__PURE__ */ jsx(
|
|
10885
10904
|
"button",
|
|
10886
10905
|
{
|
|
10887
10906
|
type: "button",
|
|
@@ -10902,20 +10921,27 @@ function ControlButton({
|
|
|
10902
10921
|
disabled && "opacity-50 cursor-not-allowed",
|
|
10903
10922
|
className
|
|
10904
10923
|
),
|
|
10905
|
-
children:
|
|
10906
|
-
|
|
10907
|
-
|
|
10908
|
-
|
|
10909
|
-
|
|
10910
|
-
|
|
10911
|
-
|
|
10912
|
-
|
|
10913
|
-
|
|
10914
|
-
|
|
10924
|
+
children: assetUrl ? /* @__PURE__ */ jsx(
|
|
10925
|
+
"img",
|
|
10926
|
+
{
|
|
10927
|
+
src: assetUrl,
|
|
10928
|
+
alt: "",
|
|
10929
|
+
width: 24,
|
|
10930
|
+
height: 24,
|
|
10931
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
10932
|
+
className: "flex-shrink-0"
|
|
10933
|
+
}
|
|
10934
|
+
) : icon ? /* @__PURE__ */ jsx("span", { className: "text-2xl", children: typeof icon === "string" ? /^[a-zA-Z0-9-]+$/.test(icon) ? (() => {
|
|
10935
|
+
const I = resolveIcon(icon);
|
|
10936
|
+
return I ? /* @__PURE__ */ jsx(I, { className: "w-6 h-6" }) : null;
|
|
10937
|
+
})() : icon : /* @__PURE__ */ (() => {
|
|
10938
|
+
const I = icon;
|
|
10939
|
+
return /* @__PURE__ */ jsx(I, { className: "w-6 h-6" });
|
|
10940
|
+
})() }) : label ? /* @__PURE__ */ jsx("span", { children: label }) : null
|
|
10915
10941
|
}
|
|
10916
10942
|
);
|
|
10917
10943
|
}
|
|
10918
|
-
var sizeMap3, shapeMap, variantMap;
|
|
10944
|
+
var sizeMap3, shapeMap, variantMap, DEFAULT_ASSET_URL2;
|
|
10919
10945
|
var init_ControlButton = __esm({
|
|
10920
10946
|
"components/game/atoms/ControlButton.tsx"() {
|
|
10921
10947
|
"use client";
|
|
@@ -10938,6 +10964,7 @@ var init_ControlButton = __esm({
|
|
|
10938
10964
|
secondary: "bg-secondary text-secondary-foreground border-border hover:bg-secondary-hover",
|
|
10939
10965
|
ghost: "bg-transparent text-foreground border-border hover:bg-muted"
|
|
10940
10966
|
};
|
|
10967
|
+
DEFAULT_ASSET_URL2 = "https://almadar-kflow-assets.web.app/shared/effects/particles/circle_01.png";
|
|
10941
10968
|
ControlButton.displayName = "ControlButton";
|
|
10942
10969
|
}
|
|
10943
10970
|
});
|
|
@@ -11017,6 +11044,7 @@ var init_Sprite = __esm({
|
|
|
11017
11044
|
}
|
|
11018
11045
|
});
|
|
11019
11046
|
function StateIndicator({
|
|
11047
|
+
assetUrl = DEFAULT_ASSET_URL3,
|
|
11020
11048
|
state = "idle",
|
|
11021
11049
|
label,
|
|
11022
11050
|
size = "md",
|
|
@@ -11039,18 +11067,29 @@ function StateIndicator({
|
|
|
11039
11067
|
className
|
|
11040
11068
|
),
|
|
11041
11069
|
children: [
|
|
11042
|
-
/* @__PURE__ */ jsx(Box, { as: "span", children:
|
|
11070
|
+
/* @__PURE__ */ jsx(Box, { as: "span", children: assetUrl ? /* @__PURE__ */ jsx(
|
|
11071
|
+
"img",
|
|
11072
|
+
{
|
|
11073
|
+
src: assetUrl,
|
|
11074
|
+
alt: displayLabel,
|
|
11075
|
+
width: 16,
|
|
11076
|
+
height: 16,
|
|
11077
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11078
|
+
className: "flex-shrink-0"
|
|
11079
|
+
}
|
|
11080
|
+
) : 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 }) }),
|
|
11043
11081
|
/* @__PURE__ */ jsx(Box, { as: "span", children: displayLabel })
|
|
11044
11082
|
]
|
|
11045
11083
|
}
|
|
11046
11084
|
);
|
|
11047
11085
|
}
|
|
11048
|
-
var DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
11086
|
+
var DEFAULT_ASSET_URL3, DEFAULT_STATE_STYLES, DEFAULT_STYLE, SIZE_CLASSES;
|
|
11049
11087
|
var init_StateIndicator = __esm({
|
|
11050
11088
|
"components/game/atoms/StateIndicator.tsx"() {
|
|
11051
11089
|
init_Box();
|
|
11052
11090
|
init_Icon();
|
|
11053
11091
|
init_cn();
|
|
11092
|
+
DEFAULT_ASSET_URL3 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
11054
11093
|
DEFAULT_STATE_STYLES = {
|
|
11055
11094
|
idle: { icon: "\u23F8", bgClass: "bg-muted" },
|
|
11056
11095
|
active: { icon: "\u25B6", bgClass: "bg-success" },
|
|
@@ -11125,6 +11164,7 @@ var init_TimerDisplay = __esm({
|
|
|
11125
11164
|
}
|
|
11126
11165
|
});
|
|
11127
11166
|
function ResourceCounter({
|
|
11167
|
+
assetUrl = DEFAULT_ASSET_URL4,
|
|
11128
11168
|
icon,
|
|
11129
11169
|
label = "Gold",
|
|
11130
11170
|
value = 250,
|
|
@@ -11144,7 +11184,17 @@ function ResourceCounter({
|
|
|
11144
11184
|
className
|
|
11145
11185
|
),
|
|
11146
11186
|
children: [
|
|
11147
|
-
|
|
11187
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
11188
|
+
"img",
|
|
11189
|
+
{
|
|
11190
|
+
src: assetUrl,
|
|
11191
|
+
alt: label,
|
|
11192
|
+
width: sizes.img,
|
|
11193
|
+
height: sizes.img,
|
|
11194
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11195
|
+
className: "flex-shrink-0"
|
|
11196
|
+
}
|
|
11197
|
+
) : 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,
|
|
11148
11198
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: label }),
|
|
11149
11199
|
/* @__PURE__ */ jsxs("span", { className: cn("font-bold tabular-nums", color && (color in colorTokenClasses2 ? colorTokenClasses2[color] : color)), children: [
|
|
11150
11200
|
value,
|
|
@@ -11157,7 +11207,7 @@ function ResourceCounter({
|
|
|
11157
11207
|
}
|
|
11158
11208
|
);
|
|
11159
11209
|
}
|
|
11160
|
-
var colorTokenClasses2, sizeMap5;
|
|
11210
|
+
var colorTokenClasses2, DEFAULT_ASSET_URL4, sizeMap5;
|
|
11161
11211
|
var init_ResourceCounter = __esm({
|
|
11162
11212
|
"components/game/atoms/ResourceCounter.tsx"() {
|
|
11163
11213
|
init_cn();
|
|
@@ -11170,16 +11220,17 @@ var init_ResourceCounter = __esm({
|
|
|
11170
11220
|
error: "text-error",
|
|
11171
11221
|
muted: "text-muted-foreground"
|
|
11172
11222
|
};
|
|
11223
|
+
DEFAULT_ASSET_URL4 = "https://almadar-kflow-assets.web.app/shared/world-map/gold_mine.png";
|
|
11173
11224
|
sizeMap5 = {
|
|
11174
|
-
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm" },
|
|
11175
|
-
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base" },
|
|
11176
|
-
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg" }
|
|
11225
|
+
sm: { wrapper: "text-xs gap-1 px-1.5 py-0.5", icon: "text-sm", img: 16 },
|
|
11226
|
+
md: { wrapper: "text-sm gap-1.5 px-2 py-1", icon: "text-base", img: 20 },
|
|
11227
|
+
lg: { wrapper: "text-base gap-2 px-3 py-1.5", icon: "text-lg", img: 28 }
|
|
11177
11228
|
};
|
|
11178
11229
|
ResourceCounter.displayName = "ResourceCounter";
|
|
11179
11230
|
}
|
|
11180
11231
|
});
|
|
11181
11232
|
function ItemSlot({
|
|
11182
|
-
assetUrl =
|
|
11233
|
+
assetUrl = DEFAULT_ASSET_URL5,
|
|
11183
11234
|
icon = "sword",
|
|
11184
11235
|
label = "Iron Sword",
|
|
11185
11236
|
quantity,
|
|
@@ -11238,7 +11289,7 @@ function ItemSlot({
|
|
|
11238
11289
|
}
|
|
11239
11290
|
);
|
|
11240
11291
|
}
|
|
11241
|
-
var sizeMap6, rarityBorderMap, rarityGlowMap,
|
|
11292
|
+
var sizeMap6, rarityBorderMap, rarityGlowMap, DEFAULT_ASSET_URL5, assetSizeMap;
|
|
11242
11293
|
var init_ItemSlot = __esm({
|
|
11243
11294
|
"components/game/atoms/ItemSlot.tsx"() {
|
|
11244
11295
|
"use client";
|
|
@@ -11263,7 +11314,7 @@ var init_ItemSlot = __esm({
|
|
|
11263
11314
|
epic: "shadow-lg",
|
|
11264
11315
|
legendary: "shadow-lg"
|
|
11265
11316
|
};
|
|
11266
|
-
|
|
11317
|
+
DEFAULT_ASSET_URL5 = "https://almadar-kflow-assets.web.app/shared/isometric-dungeon/Isometric/chestClosed_E.png";
|
|
11267
11318
|
assetSizeMap = {
|
|
11268
11319
|
sm: 28,
|
|
11269
11320
|
md: 40,
|
|
@@ -11273,6 +11324,7 @@ var init_ItemSlot = __esm({
|
|
|
11273
11324
|
}
|
|
11274
11325
|
});
|
|
11275
11326
|
function TurnIndicator({
|
|
11327
|
+
assetUrl = DEFAULT_ASSET_URL6,
|
|
11276
11328
|
currentTurn = 1,
|
|
11277
11329
|
maxTurns,
|
|
11278
11330
|
activeTeam,
|
|
@@ -11305,14 +11357,24 @@ function TurnIndicator({
|
|
|
11305
11357
|
] }),
|
|
11306
11358
|
activeTeam && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
11307
11359
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "|" }),
|
|
11308
|
-
/* @__PURE__ */ jsx(
|
|
11360
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
11361
|
+
"img",
|
|
11362
|
+
{
|
|
11363
|
+
src: assetUrl,
|
|
11364
|
+
alt: "",
|
|
11365
|
+
width: 12,
|
|
11366
|
+
height: 12,
|
|
11367
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11368
|
+
className: "flex-shrink-0"
|
|
11369
|
+
}
|
|
11370
|
+
) : /* @__PURE__ */ jsx("span", { className: cn("rounded-full bg-success/20", sizes.dot) }),
|
|
11309
11371
|
/* @__PURE__ */ jsx("span", { className: "text-success", children: activeTeam })
|
|
11310
11372
|
] })
|
|
11311
11373
|
]
|
|
11312
11374
|
}
|
|
11313
11375
|
);
|
|
11314
11376
|
}
|
|
11315
|
-
var sizeMap7;
|
|
11377
|
+
var sizeMap7, DEFAULT_ASSET_URL6;
|
|
11316
11378
|
var init_TurnIndicator = __esm({
|
|
11317
11379
|
"components/game/atoms/TurnIndicator.tsx"() {
|
|
11318
11380
|
init_cn();
|
|
@@ -11321,6 +11383,7 @@ var init_TurnIndicator = __esm({
|
|
|
11321
11383
|
md: { wrapper: "text-sm gap-2 px-3 py-1", dot: "w-2 h-2" },
|
|
11322
11384
|
lg: { wrapper: "text-base gap-2.5 px-4 py-1.5", dot: "w-2.5 h-2.5" }
|
|
11323
11385
|
};
|
|
11386
|
+
DEFAULT_ASSET_URL6 = "https://almadar-kflow-assets.web.app/shared/effects/particles/symbol_01.png";
|
|
11324
11387
|
TurnIndicator.displayName = "TurnIndicator";
|
|
11325
11388
|
}
|
|
11326
11389
|
});
|
|
@@ -11336,6 +11399,7 @@ function getComboScale(combo) {
|
|
|
11336
11399
|
return "";
|
|
11337
11400
|
}
|
|
11338
11401
|
function ComboCounter({
|
|
11402
|
+
assetUrl = DEFAULT_ASSET_URL7,
|
|
11339
11403
|
combo = 5,
|
|
11340
11404
|
multiplier,
|
|
11341
11405
|
streak,
|
|
@@ -11355,6 +11419,17 @@ function ComboCounter({
|
|
|
11355
11419
|
className
|
|
11356
11420
|
),
|
|
11357
11421
|
children: [
|
|
11422
|
+
assetUrl && /* @__PURE__ */ jsx(
|
|
11423
|
+
"img",
|
|
11424
|
+
{
|
|
11425
|
+
src: assetUrl,
|
|
11426
|
+
alt: "combo",
|
|
11427
|
+
width: 24,
|
|
11428
|
+
height: 24,
|
|
11429
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11430
|
+
className: "flex-shrink-0 mb-0.5"
|
|
11431
|
+
}
|
|
11432
|
+
),
|
|
11358
11433
|
/* @__PURE__ */ jsx("span", { className: cn("font-black tabular-nums leading-none", sizes.combo, getComboIntensity(combo)), children: combo }),
|
|
11359
11434
|
/* @__PURE__ */ jsx("span", { className: cn("font-bold uppercase tracking-wider text-muted-foreground", sizes.label), children: "combo" }),
|
|
11360
11435
|
multiplier != null && multiplier > 1 && /* @__PURE__ */ jsxs("span", { className: cn("font-bold text-warning tabular-nums", sizes.multiplier), children: [
|
|
@@ -11369,10 +11444,11 @@ function ComboCounter({
|
|
|
11369
11444
|
}
|
|
11370
11445
|
);
|
|
11371
11446
|
}
|
|
11372
|
-
var sizeMap8;
|
|
11447
|
+
var DEFAULT_ASSET_URL7, sizeMap8;
|
|
11373
11448
|
var init_ComboCounter = __esm({
|
|
11374
11449
|
"components/game/atoms/ComboCounter.tsx"() {
|
|
11375
11450
|
init_cn();
|
|
11451
|
+
DEFAULT_ASSET_URL7 = "https://almadar-kflow-assets.web.app/shared/effects/flash/flash00.png";
|
|
11376
11452
|
sizeMap8 = {
|
|
11377
11453
|
sm: { combo: "text-lg", label: "text-xs", multiplier: "text-xs" },
|
|
11378
11454
|
md: { combo: "text-2xl", label: "text-xs", multiplier: "text-sm" },
|
|
@@ -11466,6 +11542,7 @@ var init_XPBar = __esm({
|
|
|
11466
11542
|
}
|
|
11467
11543
|
});
|
|
11468
11544
|
function WaypointMarker({
|
|
11545
|
+
assetUrl = DEFAULT_ASSET_URL8,
|
|
11469
11546
|
label,
|
|
11470
11547
|
icon,
|
|
11471
11548
|
active = true,
|
|
@@ -11504,7 +11581,17 @@ function WaypointMarker({
|
|
|
11504
11581
|
active && !completed && "bg-info text-foreground",
|
|
11505
11582
|
!active && !completed && "bg-muted"
|
|
11506
11583
|
),
|
|
11507
|
-
children: completed ? checkIcon :
|
|
11584
|
+
children: completed ? checkIcon : assetUrl ? /* @__PURE__ */ jsx(
|
|
11585
|
+
"img",
|
|
11586
|
+
{
|
|
11587
|
+
src: assetUrl,
|
|
11588
|
+
alt: label,
|
|
11589
|
+
width: sizes.img,
|
|
11590
|
+
height: sizes.img,
|
|
11591
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11592
|
+
className: "flex-shrink-0"
|
|
11593
|
+
}
|
|
11594
|
+
) : icon ? typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon }) : /* @__PURE__ */ jsx(Icon, { icon }) : null
|
|
11508
11595
|
}
|
|
11509
11596
|
)
|
|
11510
11597
|
] }),
|
|
@@ -11521,15 +11608,16 @@ function WaypointMarker({
|
|
|
11521
11608
|
)
|
|
11522
11609
|
] });
|
|
11523
11610
|
}
|
|
11524
|
-
var sizeMap10, checkIcon;
|
|
11611
|
+
var DEFAULT_ASSET_URL8, sizeMap10, checkIcon;
|
|
11525
11612
|
var init_WaypointMarker = __esm({
|
|
11526
11613
|
"components/game/atoms/WaypointMarker.tsx"() {
|
|
11527
11614
|
init_cn();
|
|
11528
11615
|
init_Icon();
|
|
11616
|
+
DEFAULT_ASSET_URL8 = "https://almadar-kflow-assets.web.app/shared/world-map/battle_marker.png";
|
|
11529
11617
|
sizeMap10 = {
|
|
11530
|
-
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1" },
|
|
11531
|
-
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5" },
|
|
11532
|
-
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2" }
|
|
11618
|
+
sm: { dot: "w-4 h-4", ring: "w-6 h-6", label: "text-xs mt-1", img: 12 },
|
|
11619
|
+
md: { dot: "w-6 h-6", ring: "w-8 h-8", label: "text-sm mt-1.5", img: 18 },
|
|
11620
|
+
lg: { dot: "w-8 h-8", ring: "w-10 h-10", label: "text-base mt-2", img: 24 }
|
|
11533
11621
|
};
|
|
11534
11622
|
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" }) });
|
|
11535
11623
|
WaypointMarker.displayName = "WaypointMarker";
|
|
@@ -11542,6 +11630,7 @@ function formatDuration(seconds) {
|
|
|
11542
11630
|
return `${Math.round(seconds)}s`;
|
|
11543
11631
|
}
|
|
11544
11632
|
function StatusEffect({
|
|
11633
|
+
assetUrl = DEFAULT_ASSET_URL9,
|
|
11545
11634
|
icon = "shield",
|
|
11546
11635
|
label = "Shield",
|
|
11547
11636
|
duration = 30,
|
|
@@ -11562,7 +11651,17 @@ function StatusEffect({
|
|
|
11562
11651
|
),
|
|
11563
11652
|
title: label,
|
|
11564
11653
|
children: [
|
|
11565
|
-
/* @__PURE__ */ jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children:
|
|
11654
|
+
/* @__PURE__ */ jsx("span", { className: cn("flex items-center justify-center", sizes.icon), children: assetUrl ? /* @__PURE__ */ jsx(
|
|
11655
|
+
"img",
|
|
11656
|
+
{
|
|
11657
|
+
src: assetUrl,
|
|
11658
|
+
alt: label,
|
|
11659
|
+
width: sizes.img,
|
|
11660
|
+
height: sizes.img,
|
|
11661
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11662
|
+
className: "flex-shrink-0"
|
|
11663
|
+
}
|
|
11664
|
+
) : typeof icon === "string" ? /* @__PURE__ */ jsx(Icon, { name: icon, size: "sm" }) : /* @__PURE__ */ jsx(Icon, { icon, size: "sm" }) }),
|
|
11566
11665
|
duration !== void 0 && /* @__PURE__ */ jsx(
|
|
11567
11666
|
"span",
|
|
11568
11667
|
{
|
|
@@ -11589,15 +11688,16 @@ function StatusEffect({
|
|
|
11589
11688
|
label && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground mt-0.5 text-center whitespace-nowrap", children: label })
|
|
11590
11689
|
] });
|
|
11591
11690
|
}
|
|
11592
|
-
var sizeMap11, variantStyles7;
|
|
11691
|
+
var DEFAULT_ASSET_URL9, sizeMap11, variantStyles7;
|
|
11593
11692
|
var init_StatusEffect = __esm({
|
|
11594
11693
|
"components/game/atoms/StatusEffect.tsx"() {
|
|
11595
11694
|
init_cn();
|
|
11596
11695
|
init_Icon();
|
|
11696
|
+
DEFAULT_ASSET_URL9 = "https://almadar-kflow-assets.web.app/shared/effects/particles/flame_01.png";
|
|
11597
11697
|
sizeMap11 = {
|
|
11598
|
-
sm: { container: "w-8 h-8", icon: "text-sm", badge: "text-xs -top-1 -right-1 w-4 h-4", timer: "text-[9px]" },
|
|
11599
|
-
md: { container: "w-10 h-10", icon: "text-base", badge: "text-xs -top-1 -right-1 w-5 h-5", timer: "text-xs" },
|
|
11600
|
-
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" }
|
|
11698
|
+
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 },
|
|
11699
|
+
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 },
|
|
11700
|
+
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 }
|
|
11601
11701
|
};
|
|
11602
11702
|
variantStyles7 = {
|
|
11603
11703
|
buff: "border-success bg-success/20",
|
|
@@ -11608,6 +11708,7 @@ var init_StatusEffect = __esm({
|
|
|
11608
11708
|
}
|
|
11609
11709
|
});
|
|
11610
11710
|
function DamageNumber({
|
|
11711
|
+
assetUrl = DEFAULT_ASSET_URL10,
|
|
11611
11712
|
value = 42,
|
|
11612
11713
|
type = "damage",
|
|
11613
11714
|
size = "md",
|
|
@@ -11616,22 +11717,35 @@ function DamageNumber({
|
|
|
11616
11717
|
const displayText = type === "miss" ? "MISS" : type === "heal" ? `+${value}` : `${value}`;
|
|
11617
11718
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
11618
11719
|
/* @__PURE__ */ jsx("style", { children: floatKeyframes }),
|
|
11619
|
-
/* @__PURE__ */
|
|
11720
|
+
/* @__PURE__ */ jsxs(
|
|
11620
11721
|
"span",
|
|
11621
11722
|
{
|
|
11622
11723
|
className: cn(
|
|
11623
|
-
"inline-
|
|
11724
|
+
"inline-flex items-center gap-0.5 font-mono select-none pointer-events-none",
|
|
11624
11725
|
sizeMap12[size],
|
|
11625
11726
|
typeStyles[type],
|
|
11626
11727
|
className
|
|
11627
11728
|
),
|
|
11628
11729
|
style: { animation: "damageFloat 1s ease-out forwards" },
|
|
11629
|
-
children:
|
|
11730
|
+
children: [
|
|
11731
|
+
assetUrl && /* @__PURE__ */ jsx(
|
|
11732
|
+
"img",
|
|
11733
|
+
{
|
|
11734
|
+
src: assetUrl,
|
|
11735
|
+
alt: "",
|
|
11736
|
+
width: 14,
|
|
11737
|
+
height: 14,
|
|
11738
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11739
|
+
className: "flex-shrink-0"
|
|
11740
|
+
}
|
|
11741
|
+
),
|
|
11742
|
+
displayText
|
|
11743
|
+
]
|
|
11630
11744
|
}
|
|
11631
11745
|
)
|
|
11632
11746
|
] });
|
|
11633
11747
|
}
|
|
11634
|
-
var sizeMap12, typeStyles, floatKeyframes;
|
|
11748
|
+
var sizeMap12, typeStyles, floatKeyframes, DEFAULT_ASSET_URL10;
|
|
11635
11749
|
var init_DamageNumber = __esm({
|
|
11636
11750
|
"components/game/atoms/DamageNumber.tsx"() {
|
|
11637
11751
|
init_cn();
|
|
@@ -11653,6 +11767,7 @@ var init_DamageNumber = __esm({
|
|
|
11653
11767
|
100% { opacity: 0; transform: translateY(-32px) scale(0.8); }
|
|
11654
11768
|
}
|
|
11655
11769
|
`;
|
|
11770
|
+
DEFAULT_ASSET_URL10 = "https://almadar-kflow-assets.web.app/shared/effects/particles/spark_01.png";
|
|
11656
11771
|
DamageNumber.displayName = "DamageNumber";
|
|
11657
11772
|
}
|
|
11658
11773
|
});
|
|
@@ -11741,6 +11856,7 @@ var init_ChoiceButton = __esm({
|
|
|
11741
11856
|
}
|
|
11742
11857
|
});
|
|
11743
11858
|
function ActionButton({
|
|
11859
|
+
assetUrl = DEFAULT_ASSET_URL11,
|
|
11744
11860
|
label = "Attack",
|
|
11745
11861
|
icon,
|
|
11746
11862
|
cooldown = 0,
|
|
@@ -11780,13 +11896,23 @@ function ActionButton({
|
|
|
11780
11896
|
}
|
|
11781
11897
|
}
|
|
11782
11898
|
),
|
|
11783
|
-
|
|
11899
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
11900
|
+
"img",
|
|
11901
|
+
{
|
|
11902
|
+
src: assetUrl,
|
|
11903
|
+
alt: "",
|
|
11904
|
+
width: 16,
|
|
11905
|
+
height: 16,
|
|
11906
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
11907
|
+
className: cn("flex-shrink-0", sizes.icon)
|
|
11908
|
+
}
|
|
11909
|
+
) : icon ? /* @__PURE__ */ jsx("span", { className: cn("flex-shrink-0", sizes.icon), children: typeof icon === "string" ? (() => {
|
|
11784
11910
|
const I = resolveIcon(icon);
|
|
11785
11911
|
return I ? /* @__PURE__ */ jsx(I, { className: "w-4 h-4" }) : null;
|
|
11786
11912
|
})() : /* @__PURE__ */ (() => {
|
|
11787
11913
|
const I = icon;
|
|
11788
11914
|
return /* @__PURE__ */ jsx(I, { className: "w-4 h-4" });
|
|
11789
|
-
})() }),
|
|
11915
|
+
})() }) : null,
|
|
11790
11916
|
/* @__PURE__ */ jsx("span", { className: "relative z-10", children: label }),
|
|
11791
11917
|
hotkey && /* @__PURE__ */ jsx(
|
|
11792
11918
|
"span",
|
|
@@ -11802,7 +11928,7 @@ function ActionButton({
|
|
|
11802
11928
|
}
|
|
11803
11929
|
);
|
|
11804
11930
|
}
|
|
11805
|
-
var sizeMap13, variantStyles8;
|
|
11931
|
+
var sizeMap13, variantStyles8, DEFAULT_ASSET_URL11;
|
|
11806
11932
|
var init_ActionButton = __esm({
|
|
11807
11933
|
"components/game/atoms/ActionButton.tsx"() {
|
|
11808
11934
|
init_cn();
|
|
@@ -11817,6 +11943,7 @@ var init_ActionButton = __esm({
|
|
|
11817
11943
|
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary-hover border-border",
|
|
11818
11944
|
danger: "bg-error text-error-foreground hover:bg-error/90 border-error"
|
|
11819
11945
|
};
|
|
11946
|
+
DEFAULT_ASSET_URL11 = "https://almadar-kflow-assets.web.app/shared/effects/particles/slash_01.png";
|
|
11820
11947
|
ActionButton.displayName = "ActionButton";
|
|
11821
11948
|
}
|
|
11822
11949
|
});
|
|
@@ -12329,10 +12456,10 @@ var init_Container = __esm({
|
|
|
12329
12456
|
center = true,
|
|
12330
12457
|
className,
|
|
12331
12458
|
children,
|
|
12332
|
-
as:
|
|
12459
|
+
as: Component2 = "div"
|
|
12333
12460
|
}) => {
|
|
12334
12461
|
const resolvedSize = maxWidth ?? size ?? "lg";
|
|
12335
|
-
const Comp =
|
|
12462
|
+
const Comp = Component2;
|
|
12336
12463
|
return /* @__PURE__ */ jsx(
|
|
12337
12464
|
Comp,
|
|
12338
12465
|
{
|
|
@@ -15166,15 +15293,15 @@ function BattleBoard({
|
|
|
15166
15293
|
const board = boardEntity(entity) ?? {};
|
|
15167
15294
|
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : []);
|
|
15168
15295
|
const features = propFeatures ?? (Array.isArray(board.features) ? board.features : []);
|
|
15169
|
-
const boardWidth = num(board.boardWidth, 8);
|
|
15170
|
-
const boardHeight = num(board.boardHeight, 6);
|
|
15296
|
+
const boardWidth = num(board.gridWidth ?? board.boardWidth, 8);
|
|
15297
|
+
const boardHeight = num(board.gridHeight ?? board.boardHeight, 6);
|
|
15171
15298
|
const assetManifest = propAssetManifest ?? board.assetManifest;
|
|
15172
15299
|
const backgroundImage = board.backgroundImage;
|
|
15173
15300
|
const units = rows(board.units);
|
|
15174
15301
|
const selectedUnitId = board.selectedUnitId ?? null;
|
|
15175
15302
|
const currentPhase = str(board.phase) || "observation";
|
|
15176
15303
|
const currentTurn = num(board.turn, 1);
|
|
15177
|
-
const gameResult = board.
|
|
15304
|
+
const gameResult = board.result ?? null;
|
|
15178
15305
|
const eventBus = useEventBus();
|
|
15179
15306
|
const { t } = useTranslate();
|
|
15180
15307
|
const [hoveredTile, setHoveredTile] = useState(null);
|
|
@@ -15195,7 +15322,7 @@ function BattleBoard({
|
|
|
15195
15322
|
const validMoves = useMemo(() => {
|
|
15196
15323
|
if (!selectedUnit || currentPhase !== "movement") return [];
|
|
15197
15324
|
const moves = [];
|
|
15198
|
-
const range = num(
|
|
15325
|
+
const range = num(board.movementRange, 2);
|
|
15199
15326
|
const origin = unitPosition(selectedUnit);
|
|
15200
15327
|
for (let dy = -range; dy <= range; dy++) {
|
|
15201
15328
|
for (let dx = -range; dx <= range; dx++) {
|
|
@@ -22651,15 +22778,24 @@ function CastleBoard({
|
|
|
22651
22778
|
featureClickEvent,
|
|
22652
22779
|
unitClickEvent,
|
|
22653
22780
|
tileClickEvent,
|
|
22781
|
+
playAgainEvent,
|
|
22654
22782
|
className
|
|
22655
22783
|
}) {
|
|
22656
22784
|
const eventBus = useEventBus();
|
|
22785
|
+
const { t } = useTranslate();
|
|
22657
22786
|
const resolved = boardEntity(entity);
|
|
22658
22787
|
const tiles = propTiles ?? (Array.isArray(resolved?.tiles) ? resolved.tiles : []);
|
|
22659
22788
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
22660
22789
|
const units = propUnits ?? (Array.isArray(resolved?.units) ? resolved.units : []);
|
|
22661
22790
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
22662
22791
|
const backgroundImage = resolved?.backgroundImage;
|
|
22792
|
+
const gold = num(resolved?.gold);
|
|
22793
|
+
const health = num(resolved?.health);
|
|
22794
|
+
const maxHealth = num(resolved?.maxHealth);
|
|
22795
|
+
const wave = num(resolved?.wave);
|
|
22796
|
+
const tickCount = num(resolved?.tickCount);
|
|
22797
|
+
const buildings = rows(resolved?.buildings);
|
|
22798
|
+
const result = str(resolved?.result) || "none";
|
|
22663
22799
|
const [hoveredTile, setHoveredTile] = useState(null);
|
|
22664
22800
|
const [selectedFeature, setSelectedFeature] = useState(null);
|
|
22665
22801
|
const hoveredFeature = useMemo(() => {
|
|
@@ -22672,7 +22808,7 @@ function CastleBoard({
|
|
|
22672
22808
|
(u) => u.position?.x === hoveredTile.x && u.position?.y === hoveredTile.y
|
|
22673
22809
|
) ?? null;
|
|
22674
22810
|
}, [hoveredTile, units]);
|
|
22675
|
-
const maxY = Math.max(...tiles.map((
|
|
22811
|
+
const maxY = Math.max(...tiles.map((t2) => t2.y), 0);
|
|
22676
22812
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
22677
22813
|
const tileToScreen = useCallback(
|
|
22678
22814
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
@@ -22707,6 +22843,11 @@ function CastleBoard({
|
|
|
22707
22843
|
}
|
|
22708
22844
|
}, [units, onUnitClick, unitClickEvent, eventBus]);
|
|
22709
22845
|
const clearSelection = useCallback(() => setSelectedFeature(null), []);
|
|
22846
|
+
const handlePlayAgain = useCallback(() => {
|
|
22847
|
+
if (playAgainEvent) {
|
|
22848
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
22849
|
+
}
|
|
22850
|
+
}, [playAgainEvent, eventBus]);
|
|
22710
22851
|
const ctx = useMemo(
|
|
22711
22852
|
() => ({
|
|
22712
22853
|
hoveredTile,
|
|
@@ -22715,11 +22856,18 @@ function CastleBoard({
|
|
|
22715
22856
|
selectedFeature,
|
|
22716
22857
|
clearSelection,
|
|
22717
22858
|
tileToScreen,
|
|
22718
|
-
scale
|
|
22859
|
+
scale,
|
|
22860
|
+
gold,
|
|
22861
|
+
health,
|
|
22862
|
+
maxHealth,
|
|
22863
|
+
wave,
|
|
22864
|
+
tickCount,
|
|
22865
|
+
buildings,
|
|
22866
|
+
result
|
|
22719
22867
|
}),
|
|
22720
|
-
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale]
|
|
22868
|
+
[hoveredTile, hoveredFeature, hoveredUnit, selectedFeature, clearSelection, tileToScreen, scale, gold, health, maxHealth, wave, tickCount, buildings, result]
|
|
22721
22869
|
);
|
|
22722
|
-
return /* @__PURE__ */ jsxs("div", { className: cn("castle-board min-h-screen flex flex-col bg-background", className), children: [
|
|
22870
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("castle-board relative min-h-screen flex flex-col bg-background", className), children: [
|
|
22723
22871
|
header && header(ctx),
|
|
22724
22872
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
|
|
22725
22873
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-auto p-4 relative", children: [
|
|
@@ -22744,7 +22892,29 @@ function CastleBoard({
|
|
|
22744
22892
|
] }),
|
|
22745
22893
|
sidePanel && /* @__PURE__ */ jsx("div", { className: "w-96 shrink-0 border-l border-border bg-surface overflow-y-auto", children: sidePanel(ctx) })
|
|
22746
22894
|
] }),
|
|
22747
|
-
footer && footer(ctx)
|
|
22895
|
+
footer && footer(ctx),
|
|
22896
|
+
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: [
|
|
22897
|
+
/* @__PURE__ */ jsx(
|
|
22898
|
+
Typography,
|
|
22899
|
+
{
|
|
22900
|
+
variant: "h2",
|
|
22901
|
+
className: cn(
|
|
22902
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
22903
|
+
result === "victory" ? "text-warning" : "text-error"
|
|
22904
|
+
),
|
|
22905
|
+
children: result === "victory" ? t("battle.victory") : t("battle.defeat")
|
|
22906
|
+
}
|
|
22907
|
+
),
|
|
22908
|
+
/* @__PURE__ */ jsx(
|
|
22909
|
+
Button,
|
|
22910
|
+
{
|
|
22911
|
+
variant: "primary",
|
|
22912
|
+
className: "px-8 py-3 font-semibold",
|
|
22913
|
+
onClick: handlePlayAgain,
|
|
22914
|
+
children: t("battle.playAgain")
|
|
22915
|
+
}
|
|
22916
|
+
)
|
|
22917
|
+
] }) })
|
|
22748
22918
|
] });
|
|
22749
22919
|
}
|
|
22750
22920
|
var init_CastleBoard = __esm({
|
|
@@ -22752,6 +22922,10 @@ var init_CastleBoard = __esm({
|
|
|
22752
22922
|
"use client";
|
|
22753
22923
|
init_cn();
|
|
22754
22924
|
init_useEventBus();
|
|
22925
|
+
init_Box();
|
|
22926
|
+
init_Button();
|
|
22927
|
+
init_Typography();
|
|
22928
|
+
init_Stack();
|
|
22755
22929
|
init_IsometricCanvas();
|
|
22756
22930
|
init_boardEntity();
|
|
22757
22931
|
init_isometric();
|
|
@@ -23628,22 +23802,16 @@ function ClassifierBoard({
|
|
|
23628
23802
|
const { emit } = useEventBus();
|
|
23629
23803
|
const { t } = useTranslate();
|
|
23630
23804
|
const resolved = boardEntity(entity);
|
|
23631
|
-
const [localAssignments, setLocalAssignments] = useState({});
|
|
23632
23805
|
const [headerError, setHeaderError] = useState(false);
|
|
23633
|
-
const [localSubmitted, setLocalSubmitted] = useState(false);
|
|
23634
|
-
const [localAttempts, setLocalAttempts] = useState(0);
|
|
23635
|
-
const [showHint, setShowHint] = useState(false);
|
|
23636
23806
|
const items = Array.isArray(resolved?.items) ? resolved.items : [];
|
|
23637
23807
|
const categories = Array.isArray(resolved?.categories) ? resolved.categories : [];
|
|
23638
|
-
const
|
|
23639
|
-
const
|
|
23640
|
-
const
|
|
23641
|
-
const assignments =
|
|
23642
|
-
if (item.assignedCategory != null) acc[item.id] = item.assignedCategory;
|
|
23808
|
+
const result = str(resolved?.result);
|
|
23809
|
+
const submitted = result === "win";
|
|
23810
|
+
const attempts = num(resolved?.attempts);
|
|
23811
|
+
const assignments = items.reduce((acc, item) => {
|
|
23812
|
+
if (item.assignedCategory != null && item.assignedCategory !== "") acc[item.id] = item.assignedCategory;
|
|
23643
23813
|
return acc;
|
|
23644
|
-
}, {})
|
|
23645
|
-
const attempts = entityDrivesResult ? num(resolved?.attempts) : localAttempts;
|
|
23646
|
-
const submitted = entityDrivesResult || localSubmitted;
|
|
23814
|
+
}, {});
|
|
23647
23815
|
const unassignedItems = items.filter((item) => !assignments[item.id]);
|
|
23648
23816
|
const allAssigned = items.length > 0 && Object.keys(assignments).length === items.length;
|
|
23649
23817
|
const results = submitted ? items.map((item) => ({
|
|
@@ -23651,54 +23819,25 @@ function ClassifierBoard({
|
|
|
23651
23819
|
assigned: assignments[item.id],
|
|
23652
23820
|
correct: assignments[item.id] === item.correctCategory
|
|
23653
23821
|
})) : [];
|
|
23654
|
-
const allCorrect =
|
|
23822
|
+
const allCorrect = result === "win";
|
|
23655
23823
|
const correctCount = results.filter((r2) => r2.correct).length;
|
|
23824
|
+
const showHint = attempts >= 2 && Boolean(str(resolved?.hint));
|
|
23656
23825
|
const handleAssign = (itemId, categoryId) => {
|
|
23657
23826
|
if (submitted) return;
|
|
23658
|
-
if (assignEvent) {
|
|
23659
|
-
emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
23660
|
-
}
|
|
23661
|
-
if (!entityHasAssignments) {
|
|
23662
|
-
setLocalAssignments((prev) => ({ ...prev, [itemId]: categoryId }));
|
|
23663
|
-
}
|
|
23827
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId });
|
|
23664
23828
|
};
|
|
23665
23829
|
const handleUnassign = (itemId) => {
|
|
23666
23830
|
if (submitted) return;
|
|
23667
|
-
if (
|
|
23668
|
-
setLocalAssignments((prev) => {
|
|
23669
|
-
const next = { ...prev };
|
|
23670
|
-
delete next[itemId];
|
|
23671
|
-
return next;
|
|
23672
|
-
});
|
|
23673
|
-
}
|
|
23831
|
+
if (assignEvent) emit(`UI:${assignEvent}`, { itemId, categoryId: "" });
|
|
23674
23832
|
};
|
|
23675
|
-
const handleSubmit =
|
|
23676
|
-
if (checkEvent) {
|
|
23677
|
-
|
|
23678
|
-
|
|
23679
|
-
if (!entityDrivesResult) {
|
|
23680
|
-
setLocalSubmitted(true);
|
|
23681
|
-
setLocalAttempts((a) => a + 1);
|
|
23682
|
-
const correct = items.every((item) => assignments[item.id] === item.correctCategory);
|
|
23683
|
-
if (correct) {
|
|
23684
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
23685
|
-
}
|
|
23686
|
-
}
|
|
23687
|
-
}, [checkEvent, entityDrivesResult, items, assignments, attempts, completeEvent, emit]);
|
|
23688
|
-
const handleReset = () => {
|
|
23689
|
-
if (!entityDrivesResult) setLocalSubmitted(false);
|
|
23690
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
23691
|
-
setShowHint(true);
|
|
23833
|
+
const handleSubmit = () => {
|
|
23834
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
23835
|
+
if (allAssigned && items.every((item) => assignments[item.id] === item.correctCategory)) {
|
|
23836
|
+
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
23692
23837
|
}
|
|
23693
23838
|
};
|
|
23694
23839
|
const handleFullReset = () => {
|
|
23695
|
-
if (playAgainEvent) {
|
|
23696
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
23697
|
-
}
|
|
23698
|
-
setLocalAssignments({});
|
|
23699
|
-
setLocalSubmitted(false);
|
|
23700
|
-
setLocalAttempts(0);
|
|
23701
|
-
setShowHint(false);
|
|
23840
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
23702
23841
|
};
|
|
23703
23842
|
if (!resolved) return null;
|
|
23704
23843
|
const theme = resolved.theme ?? void 0;
|
|
@@ -23737,17 +23876,17 @@ function ClassifierBoard({
|
|
|
23737
23876
|
/* @__PURE__ */ jsx(Badge, { size: "sm", children: catItems.length })
|
|
23738
23877
|
] }),
|
|
23739
23878
|
/* @__PURE__ */ jsx(HStack, { gap: "xs", className: "flex-wrap min-h-[2rem]", children: catItems.map((item) => {
|
|
23740
|
-
const
|
|
23879
|
+
const result2 = results.find((r2) => r2.item.id === item.id);
|
|
23741
23880
|
return /* @__PURE__ */ jsxs(
|
|
23742
23881
|
Badge,
|
|
23743
23882
|
{
|
|
23744
23883
|
size: "sm",
|
|
23745
|
-
className: `cursor-pointer ${
|
|
23884
|
+
className: `cursor-pointer ${result2 ? result2.correct ? "border-success bg-success/10" : "border-error bg-error/10" : ""}`,
|
|
23746
23885
|
onClick: () => handleUnassign(item.id),
|
|
23747
23886
|
children: [
|
|
23748
23887
|
item.iconUrl && /* @__PURE__ */ jsx("img", { src: item.iconUrl, alt: "", className: "w-3 h-3 object-contain inline-block" }),
|
|
23749
23888
|
item.label,
|
|
23750
|
-
|
|
23889
|
+
result2 && /* @__PURE__ */ jsx(Icon, { icon: result2.correct ? CheckCircle : XCircle, size: "xs", className: result2.correct ? "text-success" : "text-error" })
|
|
23751
23890
|
]
|
|
23752
23891
|
},
|
|
23753
23892
|
item.id
|
|
@@ -23776,10 +23915,10 @@ function ClassifierBoard({
|
|
|
23776
23915
|
] }) }),
|
|
23777
23916
|
showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
|
|
23778
23917
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
23779
|
-
!submitted
|
|
23918
|
+
!submitted && /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: !allAssigned, children: [
|
|
23780
23919
|
/* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
|
|
23781
23920
|
t("classifier.check")
|
|
23782
|
-
] })
|
|
23921
|
+
] }),
|
|
23783
23922
|
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleFullReset, children: [
|
|
23784
23923
|
/* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
|
|
23785
23924
|
t("classifier.reset")
|
|
@@ -27497,7 +27636,7 @@ var init_Flex = __esm({
|
|
|
27497
27636
|
basis,
|
|
27498
27637
|
className,
|
|
27499
27638
|
children,
|
|
27500
|
-
as:
|
|
27639
|
+
as: Component2 = "div"
|
|
27501
27640
|
}) => {
|
|
27502
27641
|
const flexStyle = {};
|
|
27503
27642
|
if (grow !== void 0 || shrink !== void 0 || basis !== void 0) {
|
|
@@ -27509,7 +27648,7 @@ var init_Flex = __esm({
|
|
|
27509
27648
|
flexStyle.flexBasis = typeof basis === "number" ? `${basis}px` : basis;
|
|
27510
27649
|
}
|
|
27511
27650
|
}
|
|
27512
|
-
const Comp =
|
|
27651
|
+
const Comp = Component2;
|
|
27513
27652
|
return /* @__PURE__ */ jsx(
|
|
27514
27653
|
Comp,
|
|
27515
27654
|
{
|
|
@@ -27629,11 +27768,11 @@ var init_Grid = __esm({
|
|
|
27629
27768
|
className,
|
|
27630
27769
|
style,
|
|
27631
27770
|
children,
|
|
27632
|
-
as:
|
|
27771
|
+
as: Component2 = "div"
|
|
27633
27772
|
}) => {
|
|
27634
27773
|
const mergedStyle = rows2 ? { gridTemplateRows: `repeat(${rows2}, minmax(0, 1fr))`, ...style } : style;
|
|
27635
27774
|
return React91__default.createElement(
|
|
27636
|
-
|
|
27775
|
+
Component2,
|
|
27637
27776
|
{
|
|
27638
27777
|
className: cn(
|
|
27639
27778
|
"grid",
|
|
@@ -29459,6 +29598,7 @@ var init_ProgressDots = __esm({
|
|
|
29459
29598
|
}
|
|
29460
29599
|
});
|
|
29461
29600
|
function StatBadge({
|
|
29601
|
+
assetUrl = DEFAULT_ASSET_URL12,
|
|
29462
29602
|
label,
|
|
29463
29603
|
value = 0,
|
|
29464
29604
|
max,
|
|
@@ -29482,7 +29622,17 @@ function StatBadge({
|
|
|
29482
29622
|
className
|
|
29483
29623
|
),
|
|
29484
29624
|
children: [
|
|
29485
|
-
|
|
29625
|
+
assetUrl ? /* @__PURE__ */ jsx(
|
|
29626
|
+
"img",
|
|
29627
|
+
{
|
|
29628
|
+
src: assetUrl,
|
|
29629
|
+
alt: "",
|
|
29630
|
+
width: 16,
|
|
29631
|
+
height: 16,
|
|
29632
|
+
style: { imageRendering: "pixelated", objectFit: "contain" },
|
|
29633
|
+
className: "flex-shrink-0"
|
|
29634
|
+
}
|
|
29635
|
+
) : 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,
|
|
29486
29636
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground font-medium", children: label }),
|
|
29487
29637
|
format === "hearts" && max && /* @__PURE__ */ jsx(
|
|
29488
29638
|
HealthBar,
|
|
@@ -29515,7 +29665,7 @@ function StatBadge({
|
|
|
29515
29665
|
}
|
|
29516
29666
|
);
|
|
29517
29667
|
}
|
|
29518
|
-
var sizeMap17, variantMap2;
|
|
29668
|
+
var sizeMap17, variantMap2, DEFAULT_ASSET_URL12;
|
|
29519
29669
|
var init_StatBadge = __esm({
|
|
29520
29670
|
"components/game/molecules/StatBadge.tsx"() {
|
|
29521
29671
|
init_cn();
|
|
@@ -29534,6 +29684,7 @@ var init_StatBadge = __esm({
|
|
|
29534
29684
|
warning: "bg-warning/15 border-warning/40 text-foreground",
|
|
29535
29685
|
danger: "bg-error/15 border-error/40 text-foreground"
|
|
29536
29686
|
};
|
|
29687
|
+
DEFAULT_ASSET_URL12 = "https://almadar-kflow-assets.web.app/shared/effects/particles/light_01.png";
|
|
29537
29688
|
StatBadge.displayName = "StatBadge";
|
|
29538
29689
|
}
|
|
29539
29690
|
});
|
|
@@ -30190,6 +30341,7 @@ var init_TurnPanel = __esm({
|
|
|
30190
30341
|
}
|
|
30191
30342
|
});
|
|
30192
30343
|
function EnemyPlate({
|
|
30344
|
+
assetUrl = DEFAULT_ASSET_URL13,
|
|
30193
30345
|
name = "Shadow Guard",
|
|
30194
30346
|
health = 80,
|
|
30195
30347
|
maxHealth = 100,
|
|
@@ -30207,15 +30359,28 @@ function EnemyPlate({
|
|
|
30207
30359
|
),
|
|
30208
30360
|
children: [
|
|
30209
30361
|
/* @__PURE__ */ jsxs(Box, { className: "flex items-center justify-between gap-2", children: [
|
|
30210
|
-
/* @__PURE__ */
|
|
30211
|
-
|
|
30212
|
-
|
|
30213
|
-
|
|
30214
|
-
|
|
30215
|
-
|
|
30216
|
-
|
|
30217
|
-
|
|
30218
|
-
|
|
30362
|
+
/* @__PURE__ */ jsxs(Box, { className: "flex items-center gap-1.5 min-w-0", children: [
|
|
30363
|
+
assetUrl && /* @__PURE__ */ jsx(
|
|
30364
|
+
"img",
|
|
30365
|
+
{
|
|
30366
|
+
src: assetUrl,
|
|
30367
|
+
alt: name,
|
|
30368
|
+
width: 24,
|
|
30369
|
+
height: 24,
|
|
30370
|
+
style: { objectFit: "cover", borderRadius: "50%" },
|
|
30371
|
+
className: "flex-shrink-0"
|
|
30372
|
+
}
|
|
30373
|
+
),
|
|
30374
|
+
/* @__PURE__ */ jsx(
|
|
30375
|
+
Typography,
|
|
30376
|
+
{
|
|
30377
|
+
variant: "caption",
|
|
30378
|
+
weight: "bold",
|
|
30379
|
+
className: "text-[var(--color-foreground)] truncate",
|
|
30380
|
+
children: name
|
|
30381
|
+
}
|
|
30382
|
+
)
|
|
30383
|
+
] }),
|
|
30219
30384
|
level != null && /* @__PURE__ */ jsxs(Badge, { variant: "neutral", size: "sm", children: [
|
|
30220
30385
|
"Lv.",
|
|
30221
30386
|
level
|
|
@@ -30260,7 +30425,7 @@ function EnemyPlate({
|
|
|
30260
30425
|
}
|
|
30261
30426
|
);
|
|
30262
30427
|
}
|
|
30263
|
-
var effectVariantMap2, DEFAULT_ENEMY_EFFECTS;
|
|
30428
|
+
var effectVariantMap2, DEFAULT_ENEMY_EFFECTS, DEFAULT_ASSET_URL13;
|
|
30264
30429
|
var init_EnemyPlate = __esm({
|
|
30265
30430
|
"components/game/molecules/EnemyPlate.tsx"() {
|
|
30266
30431
|
"use client";
|
|
@@ -30277,6 +30442,7 @@ var init_EnemyPlate = __esm({
|
|
|
30277
30442
|
DEFAULT_ENEMY_EFFECTS = [
|
|
30278
30443
|
{ icon: "flame", label: "Burn", variant: "debuff" }
|
|
30279
30444
|
];
|
|
30445
|
+
DEFAULT_ASSET_URL13 = "https://almadar-kflow-assets.web.app/shared/portraits/shadow-legion.png";
|
|
30280
30446
|
EnemyPlate.displayName = "EnemyPlate";
|
|
30281
30447
|
}
|
|
30282
30448
|
});
|
|
@@ -31808,13 +31974,13 @@ var init_MapView = __esm({
|
|
|
31808
31974
|
shadowSize: [41, 41]
|
|
31809
31975
|
});
|
|
31810
31976
|
L.Marker.prototype.options.icon = defaultIcon;
|
|
31811
|
-
const { useEffect:
|
|
31977
|
+
const { useEffect: useEffect84, useRef: useRef76, useCallback: useCallback124, useState: useState120 } = React91__default;
|
|
31812
31978
|
const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
|
|
31813
|
-
const { useEventBus:
|
|
31979
|
+
const { useEventBus: useEventBus4 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
|
|
31814
31980
|
function MapUpdater({ centerLat, centerLng, zoom }) {
|
|
31815
31981
|
const map = useMap();
|
|
31816
|
-
const prevRef =
|
|
31817
|
-
|
|
31982
|
+
const prevRef = useRef76({ centerLat, centerLng, zoom });
|
|
31983
|
+
useEffect84(() => {
|
|
31818
31984
|
const prev = prevRef.current;
|
|
31819
31985
|
if (prev.centerLat !== centerLat || prev.centerLng !== centerLng || prev.zoom !== zoom) {
|
|
31820
31986
|
map.setView([centerLat, centerLng], zoom);
|
|
@@ -31825,7 +31991,7 @@ var init_MapView = __esm({
|
|
|
31825
31991
|
}
|
|
31826
31992
|
function MapClickHandler({ onMapClick }) {
|
|
31827
31993
|
const map = useMap();
|
|
31828
|
-
|
|
31994
|
+
useEffect84(() => {
|
|
31829
31995
|
if (!onMapClick) return;
|
|
31830
31996
|
const handler = (e) => {
|
|
31831
31997
|
onMapClick(e.latlng.lat, e.latlng.lng);
|
|
@@ -31852,9 +32018,9 @@ var init_MapView = __esm({
|
|
|
31852
32018
|
className,
|
|
31853
32019
|
showAttribution = true
|
|
31854
32020
|
}) {
|
|
31855
|
-
const eventBus =
|
|
31856
|
-
const [clickedPosition, setClickedPosition] =
|
|
31857
|
-
const handleMapClick =
|
|
32021
|
+
const eventBus = useEventBus4();
|
|
32022
|
+
const [clickedPosition, setClickedPosition] = useState120(null);
|
|
32023
|
+
const handleMapClick = useCallback124((lat, lng) => {
|
|
31858
32024
|
if (showClickedPin) {
|
|
31859
32025
|
setClickedPosition({ lat, lng });
|
|
31860
32026
|
}
|
|
@@ -31863,7 +32029,7 @@ var init_MapView = __esm({
|
|
|
31863
32029
|
eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
|
|
31864
32030
|
}
|
|
31865
32031
|
}, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
|
|
31866
|
-
const handleMarkerClick =
|
|
32032
|
+
const handleMarkerClick = useCallback124((marker) => {
|
|
31867
32033
|
onMarkerClick?.(marker);
|
|
31868
32034
|
if (markerClickEvent) {
|
|
31869
32035
|
eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
|
|
@@ -38581,10 +38747,10 @@ var init_Section = __esm({
|
|
|
38581
38747
|
children,
|
|
38582
38748
|
headerClassName,
|
|
38583
38749
|
contentClassName,
|
|
38584
|
-
as:
|
|
38750
|
+
as: Component2 = "section"
|
|
38585
38751
|
}) => {
|
|
38586
38752
|
const hasHeader = title || description || action;
|
|
38587
|
-
const Comp =
|
|
38753
|
+
const Comp = Component2;
|
|
38588
38754
|
return /* @__PURE__ */ jsxs(
|
|
38589
38755
|
Comp,
|
|
38590
38756
|
{
|
|
@@ -40685,44 +40851,27 @@ function DebuggerBoard({
|
|
|
40685
40851
|
const { t } = useTranslate();
|
|
40686
40852
|
const resolved = boardEntity(entity);
|
|
40687
40853
|
const [headerError, setHeaderError] = useState(false);
|
|
40688
|
-
const [showHint, setShowHint] = useState(false);
|
|
40689
40854
|
const lines = Array.isArray(resolved?.lines) ? resolved.lines : [];
|
|
40690
|
-
const result = resolved?.result
|
|
40855
|
+
const result = str(resolved?.result) || "none";
|
|
40691
40856
|
const attempts = num(resolved?.attempts);
|
|
40692
|
-
const submitted = result
|
|
40857
|
+
const submitted = result === "win";
|
|
40858
|
+
const showHint = !submitted && attempts >= 2 && Boolean(str(resolved?.hint));
|
|
40693
40859
|
const bugLines = lines.filter((l) => l.isBug);
|
|
40694
40860
|
const flaggedLines = lines.filter((l) => l.isFlagged);
|
|
40695
40861
|
const correctFlags = lines.filter((l) => l.isBug && l.isFlagged);
|
|
40696
40862
|
const falseFlags = lines.filter((l) => !l.isBug && l.isFlagged);
|
|
40697
|
-
const allCorrect =
|
|
40863
|
+
const allCorrect = submitted;
|
|
40698
40864
|
const toggleLine = (lineId) => {
|
|
40699
40865
|
if (submitted) return;
|
|
40700
|
-
if (toggleFlagEvent) {
|
|
40701
|
-
emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
40702
|
-
}
|
|
40866
|
+
if (toggleFlagEvent) emit(`UI:${toggleFlagEvent}`, { lineId });
|
|
40703
40867
|
};
|
|
40704
40868
|
const handleSubmit = useCallback(() => {
|
|
40705
|
-
if (checkEvent) {
|
|
40706
|
-
emit(`UI:${checkEvent}`, {});
|
|
40707
|
-
}
|
|
40869
|
+
if (checkEvent) emit(`UI:${checkEvent}`, {});
|
|
40708
40870
|
const correct = correctFlags.length === bugLines.length && falseFlags.length === 0;
|
|
40709
|
-
if (correct) {
|
|
40710
|
-
emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
40711
|
-
}
|
|
40871
|
+
if (correct) emit(`UI:${completeEvent}`, { success: true, attempts: attempts + 1 });
|
|
40712
40872
|
}, [checkEvent, correctFlags.length, bugLines.length, falseFlags.length, attempts, completeEvent, emit]);
|
|
40713
40873
|
const handleReset = () => {
|
|
40714
|
-
if (playAgainEvent) {
|
|
40715
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
40716
|
-
}
|
|
40717
|
-
if (attempts >= 2 && str(resolved?.hint)) {
|
|
40718
|
-
setShowHint(true);
|
|
40719
|
-
}
|
|
40720
|
-
};
|
|
40721
|
-
const handleFullReset = () => {
|
|
40722
|
-
if (playAgainEvent) {
|
|
40723
|
-
emit(`UI:${playAgainEvent}`, {});
|
|
40724
|
-
}
|
|
40725
|
-
setShowHint(false);
|
|
40874
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
40726
40875
|
};
|
|
40727
40876
|
if (!resolved) return null;
|
|
40728
40877
|
const theme = resolved.theme ?? void 0;
|
|
@@ -40746,7 +40895,7 @@ function DebuggerBoard({
|
|
|
40746
40895
|
/* @__PURE__ */ jsx(Typography, { variant: "h4", weight: "bold", children: str(resolved.title) })
|
|
40747
40896
|
] }),
|
|
40748
40897
|
/* @__PURE__ */ jsx(Typography, { variant: "body", children: str(resolved.description) }),
|
|
40749
|
-
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(
|
|
40898
|
+
/* @__PURE__ */ jsx(Typography, { variant: "caption", className: "text-muted-foreground", children: t("debugger.findBugs", { count: String(lines.filter((l) => l.isBug).length) }) })
|
|
40750
40899
|
] }) }),
|
|
40751
40900
|
/* @__PURE__ */ jsx(Card, { className: "p-0 overflow-hidden", children: /* @__PURE__ */ jsx(VStack, { gap: "none", children: lines.map((line, i) => {
|
|
40752
40901
|
const isFlagged = !!line.isFlagged;
|
|
@@ -40796,11 +40945,11 @@ function DebuggerBoard({
|
|
|
40796
40945
|
] }) }),
|
|
40797
40946
|
showHint && hint && /* @__PURE__ */ jsx(Card, { className: "p-4 border-l-4 border-l-warning", children: /* @__PURE__ */ jsx(Typography, { variant: "body", children: hint }) }),
|
|
40798
40947
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", justify: "center", children: [
|
|
40799
|
-
!submitted
|
|
40948
|
+
!submitted && /* @__PURE__ */ jsxs(Button, { variant: "primary", onClick: handleSubmit, disabled: flaggedLines.length === 0, children: [
|
|
40800
40949
|
/* @__PURE__ */ jsx(Icon, { icon: Send, size: "sm" }),
|
|
40801
40950
|
t("debugger.submit")
|
|
40802
|
-
] })
|
|
40803
|
-
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick:
|
|
40951
|
+
] }),
|
|
40952
|
+
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: handleReset, children: [
|
|
40804
40953
|
/* @__PURE__ */ jsx(Icon, { icon: RotateCcw, size: "sm" }),
|
|
40805
40954
|
t("debugger.reset")
|
|
40806
40955
|
] })
|
|
@@ -41801,39 +41950,40 @@ function EventHandlerBoard({
|
|
|
41801
41950
|
stepDurationMs = 800,
|
|
41802
41951
|
playEvent,
|
|
41803
41952
|
completeEvent,
|
|
41953
|
+
editRuleEvent,
|
|
41954
|
+
playAgainEvent,
|
|
41804
41955
|
className
|
|
41805
41956
|
}) {
|
|
41806
41957
|
const { emit } = useEventBus();
|
|
41807
41958
|
const { t } = useTranslate();
|
|
41808
41959
|
const resolved = boardEntity(entity);
|
|
41809
|
-
const
|
|
41810
|
-
const
|
|
41960
|
+
const objects = rows(resolved?.objects);
|
|
41961
|
+
const entityResult = str(resolved?.result) || "none";
|
|
41962
|
+
const isSuccess = entityResult === "win";
|
|
41963
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
41811
41964
|
const [selectedObjectId, setSelectedObjectId] = useState(
|
|
41812
|
-
|
|
41965
|
+
objects[0] ? objId(objects[0]) : null
|
|
41813
41966
|
);
|
|
41814
41967
|
const [headerError, setHeaderError] = useState(false);
|
|
41815
|
-
const [
|
|
41968
|
+
const [isPlaying, setIsPlaying] = useState(false);
|
|
41816
41969
|
const [eventLog, setEventLog] = useState([]);
|
|
41817
|
-
const [attempts, setAttempts] = useState(0);
|
|
41818
41970
|
const timerRef = useRef(null);
|
|
41819
41971
|
const logIdCounter = useRef(0);
|
|
41820
41972
|
useEffect(() => () => {
|
|
41821
41973
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
41822
41974
|
}, []);
|
|
41823
|
-
const selectedObject = objects.find((o) => objId(o) === selectedObjectId)
|
|
41975
|
+
const selectedObject = objects.find((o) => objId(o) === selectedObjectId) ?? null;
|
|
41824
41976
|
const handleRulesChange = useCallback((objectId, rules) => {
|
|
41825
|
-
|
|
41826
|
-
|
|
41827
|
-
));
|
|
41828
|
-
}, []);
|
|
41977
|
+
if (editRuleEvent) emit(`UI:${editRuleEvent}`, { objectId, rules });
|
|
41978
|
+
}, [editRuleEvent, emit]);
|
|
41829
41979
|
const addLogEntry = useCallback((icon, message, status = "done") => {
|
|
41830
41980
|
const id = `log-${logIdCounter.current++}`;
|
|
41831
41981
|
setEventLog((prev) => [...prev, { id, timestamp: Date.now(), icon, message, status }]);
|
|
41832
41982
|
}, []);
|
|
41833
41983
|
const handlePlay = useCallback(() => {
|
|
41834
|
-
if (
|
|
41984
|
+
if (isPlaying || isSuccess) return;
|
|
41835
41985
|
if (playEvent) emit(`UI:${playEvent}`, {});
|
|
41836
|
-
|
|
41986
|
+
setIsPlaying(true);
|
|
41837
41987
|
setEventLog([]);
|
|
41838
41988
|
const allRules = [];
|
|
41839
41989
|
objects.forEach((obj) => {
|
|
@@ -41849,15 +41999,8 @@ function EventHandlerBoard({
|
|
|
41849
41999
|
let goalReached = false;
|
|
41850
42000
|
const processNext = () => {
|
|
41851
42001
|
if (eventQueue.length === 0 || stepIdx > 20) {
|
|
41852
|
-
|
|
41853
|
-
|
|
41854
|
-
if (completeEvent) {
|
|
41855
|
-
emit(`UI:${completeEvent}`, { success: true });
|
|
41856
|
-
}
|
|
41857
|
-
} else {
|
|
41858
|
-
setAttempts((prev) => prev + 1);
|
|
41859
|
-
setPlayState("fail");
|
|
41860
|
-
}
|
|
42002
|
+
setIsPlaying(false);
|
|
42003
|
+
if (goalReached && completeEvent) emit(`UI:${completeEvent}`, { success: true });
|
|
41861
42004
|
return;
|
|
41862
42005
|
}
|
|
41863
42006
|
const currentEvent = eventQueue.shift();
|
|
@@ -41888,21 +42031,19 @@ function EventHandlerBoard({
|
|
|
41888
42031
|
addLogEntry("\u{1F3AC}", t("eventHandler.simulationStarted", { events: triggers.join(", ") }), "active");
|
|
41889
42032
|
}
|
|
41890
42033
|
timerRef.current = setTimeout(processNext, stepDurationMs);
|
|
41891
|
-
}, [
|
|
42034
|
+
}, [isPlaying, isSuccess, objects, resolved, stepDurationMs, playEvent, completeEvent, emit, addLogEntry, t]);
|
|
41892
42035
|
const handleTryAgain = useCallback(() => {
|
|
41893
42036
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
41894
|
-
|
|
42037
|
+
setIsPlaying(false);
|
|
41895
42038
|
setEventLog([]);
|
|
41896
42039
|
}, []);
|
|
41897
42040
|
const handleReset = useCallback(() => {
|
|
41898
42041
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
41899
|
-
|
|
41900
|
-
setObjects([...resetObjects]);
|
|
41901
|
-
setPlayState("editing");
|
|
42042
|
+
setIsPlaying(false);
|
|
41902
42043
|
setEventLog([]);
|
|
41903
|
-
setSelectedObjectId(
|
|
41904
|
-
|
|
41905
|
-
}, [
|
|
42044
|
+
setSelectedObjectId(objects[0] ? objId(objects[0]) : null);
|
|
42045
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
42046
|
+
}, [objects, playAgainEvent, emit]);
|
|
41906
42047
|
if (!resolved) return null;
|
|
41907
42048
|
const objectViewers = objects.map((obj) => {
|
|
41908
42049
|
const states = objStates(obj);
|
|
@@ -41971,12 +42112,12 @@ function EventHandlerBoard({
|
|
|
41971
42112
|
{
|
|
41972
42113
|
object: selectedObject,
|
|
41973
42114
|
onRulesChange: handleRulesChange,
|
|
41974
|
-
disabled:
|
|
42115
|
+
disabled: isPlaying
|
|
41975
42116
|
}
|
|
41976
42117
|
),
|
|
41977
42118
|
eventLog.length > 0 && /* @__PURE__ */ jsx(EventLog, { entries: eventLog }),
|
|
41978
|
-
|
|
41979
|
-
|
|
42119
|
+
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") }) }),
|
|
42120
|
+
!isPlaying && !isSuccess && attempts > 0 && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
41980
42121
|
/* @__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) }) }),
|
|
41981
42122
|
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: [
|
|
41982
42123
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
@@ -41984,12 +42125,12 @@ function EventHandlerBoard({
|
|
|
41984
42125
|
] }) })
|
|
41985
42126
|
] }),
|
|
41986
42127
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
|
|
41987
|
-
|
|
42128
|
+
!isPlaying && !isSuccess && attempts > 0 ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsx(
|
|
41988
42129
|
Button,
|
|
41989
42130
|
{
|
|
41990
42131
|
variant: "primary",
|
|
41991
42132
|
onClick: handlePlay,
|
|
41992
|
-
disabled:
|
|
42133
|
+
disabled: isPlaying || isSuccess,
|
|
41993
42134
|
children: "\u25B6 " + t("game.play")
|
|
41994
42135
|
}
|
|
41995
42136
|
),
|
|
@@ -43174,6 +43315,1568 @@ var init_GameAudioToggle = __esm({
|
|
|
43174
43315
|
GameAudioToggle.displayName = "GameAudioToggle";
|
|
43175
43316
|
}
|
|
43176
43317
|
});
|
|
43318
|
+
function detectAssetRoot(modelUrl) {
|
|
43319
|
+
const idx = modelUrl.indexOf("/3d/");
|
|
43320
|
+
if (idx !== -1) {
|
|
43321
|
+
return modelUrl.substring(0, idx + 4);
|
|
43322
|
+
}
|
|
43323
|
+
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
43324
|
+
}
|
|
43325
|
+
function createGLTFLoaderForUrl(url) {
|
|
43326
|
+
const loader = new GLTFLoader();
|
|
43327
|
+
loader.setResourcePath(detectAssetRoot(url));
|
|
43328
|
+
return loader;
|
|
43329
|
+
}
|
|
43330
|
+
var AssetLoader;
|
|
43331
|
+
var init_AssetLoader = __esm({
|
|
43332
|
+
"components/game/molecules/three/loaders/AssetLoader.ts"() {
|
|
43333
|
+
AssetLoader = class {
|
|
43334
|
+
constructor() {
|
|
43335
|
+
__publicField(this, "objLoader");
|
|
43336
|
+
__publicField(this, "textureLoader");
|
|
43337
|
+
__publicField(this, "modelCache");
|
|
43338
|
+
__publicField(this, "textureCache");
|
|
43339
|
+
__publicField(this, "loadingPromises");
|
|
43340
|
+
this.objLoader = new OBJLoader();
|
|
43341
|
+
this.textureLoader = new THREE.TextureLoader();
|
|
43342
|
+
this.modelCache = /* @__PURE__ */ new Map();
|
|
43343
|
+
this.textureCache = /* @__PURE__ */ new Map();
|
|
43344
|
+
this.loadingPromises = /* @__PURE__ */ new Map();
|
|
43345
|
+
}
|
|
43346
|
+
/**
|
|
43347
|
+
* Load a GLB/GLTF model
|
|
43348
|
+
* @param url - URL to the .glb or .gltf file
|
|
43349
|
+
* @returns Promise with loaded model scene and animations
|
|
43350
|
+
*/
|
|
43351
|
+
async loadModel(url) {
|
|
43352
|
+
if (this.modelCache.has(url)) {
|
|
43353
|
+
return this.modelCache.get(url);
|
|
43354
|
+
}
|
|
43355
|
+
if (this.loadingPromises.has(url)) {
|
|
43356
|
+
return this.loadingPromises.get(url);
|
|
43357
|
+
}
|
|
43358
|
+
const loader = createGLTFLoaderForUrl(url);
|
|
43359
|
+
const loadPromise = loader.loadAsync(url).then((gltf) => {
|
|
43360
|
+
const result = {
|
|
43361
|
+
scene: gltf.scene,
|
|
43362
|
+
animations: gltf.animations || []
|
|
43363
|
+
};
|
|
43364
|
+
this.modelCache.set(url, result);
|
|
43365
|
+
this.loadingPromises.delete(url);
|
|
43366
|
+
return result;
|
|
43367
|
+
}).catch((error) => {
|
|
43368
|
+
this.loadingPromises.delete(url);
|
|
43369
|
+
throw new Error(`Failed to load model ${url}: ${error.message}`);
|
|
43370
|
+
});
|
|
43371
|
+
this.loadingPromises.set(url, loadPromise);
|
|
43372
|
+
return loadPromise;
|
|
43373
|
+
}
|
|
43374
|
+
/**
|
|
43375
|
+
* Load an OBJ model (fallback for non-GLB assets)
|
|
43376
|
+
* @param url - URL to the .obj file
|
|
43377
|
+
* @returns Promise with loaded object group
|
|
43378
|
+
*/
|
|
43379
|
+
async loadOBJ(url) {
|
|
43380
|
+
if (this.modelCache.has(url)) {
|
|
43381
|
+
return this.modelCache.get(url).scene;
|
|
43382
|
+
}
|
|
43383
|
+
if (this.loadingPromises.has(url)) {
|
|
43384
|
+
const result = await this.loadingPromises.get(url);
|
|
43385
|
+
return result.scene;
|
|
43386
|
+
}
|
|
43387
|
+
const loadPromise = this.objLoader.loadAsync(url).then((group) => {
|
|
43388
|
+
const result = {
|
|
43389
|
+
scene: group,
|
|
43390
|
+
animations: []
|
|
43391
|
+
};
|
|
43392
|
+
this.modelCache.set(url, result);
|
|
43393
|
+
this.loadingPromises.delete(url);
|
|
43394
|
+
return result;
|
|
43395
|
+
}).catch((error) => {
|
|
43396
|
+
this.loadingPromises.delete(url);
|
|
43397
|
+
throw new Error(`Failed to load OBJ ${url}: ${error.message}`);
|
|
43398
|
+
});
|
|
43399
|
+
this.loadingPromises.set(url, loadPromise);
|
|
43400
|
+
return (await loadPromise).scene;
|
|
43401
|
+
}
|
|
43402
|
+
/**
|
|
43403
|
+
* Load a texture
|
|
43404
|
+
* @param url - URL to the texture image
|
|
43405
|
+
* @returns Promise with loaded texture
|
|
43406
|
+
*/
|
|
43407
|
+
async loadTexture(url) {
|
|
43408
|
+
if (this.textureCache.has(url)) {
|
|
43409
|
+
return this.textureCache.get(url);
|
|
43410
|
+
}
|
|
43411
|
+
if (this.loadingPromises.has(`texture:${url}`)) {
|
|
43412
|
+
return this.loadingPromises.get(`texture:${url}`);
|
|
43413
|
+
}
|
|
43414
|
+
const loadPromise = this.textureLoader.loadAsync(url).then((texture) => {
|
|
43415
|
+
texture.colorSpace = THREE.SRGBColorSpace;
|
|
43416
|
+
this.textureCache.set(url, texture);
|
|
43417
|
+
this.loadingPromises.delete(`texture:${url}`);
|
|
43418
|
+
return texture;
|
|
43419
|
+
}).catch((error) => {
|
|
43420
|
+
this.loadingPromises.delete(`texture:${url}`);
|
|
43421
|
+
throw new Error(`Failed to load texture ${url}: ${error.message}`);
|
|
43422
|
+
});
|
|
43423
|
+
this.loadingPromises.set(`texture:${url}`, loadPromise);
|
|
43424
|
+
return loadPromise;
|
|
43425
|
+
}
|
|
43426
|
+
/**
|
|
43427
|
+
* Preload multiple assets
|
|
43428
|
+
* @param urls - Array of asset URLs to preload
|
|
43429
|
+
* @returns Promise that resolves when all assets are loaded
|
|
43430
|
+
*/
|
|
43431
|
+
async preload(urls) {
|
|
43432
|
+
const promises = urls.map((url) => {
|
|
43433
|
+
if (url.endsWith(".glb") || url.endsWith(".gltf")) {
|
|
43434
|
+
return this.loadModel(url).catch(() => null);
|
|
43435
|
+
} else if (url.endsWith(".obj")) {
|
|
43436
|
+
return this.loadOBJ(url).catch(() => null);
|
|
43437
|
+
} else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
|
|
43438
|
+
return this.loadTexture(url).catch(() => null);
|
|
43439
|
+
}
|
|
43440
|
+
return Promise.resolve(null);
|
|
43441
|
+
});
|
|
43442
|
+
await Promise.all(promises);
|
|
43443
|
+
}
|
|
43444
|
+
/**
|
|
43445
|
+
* Check if a model is cached
|
|
43446
|
+
* @param url - Model URL
|
|
43447
|
+
*/
|
|
43448
|
+
hasModel(url) {
|
|
43449
|
+
return this.modelCache.has(url);
|
|
43450
|
+
}
|
|
43451
|
+
/**
|
|
43452
|
+
* Check if a texture is cached
|
|
43453
|
+
* @param url - Texture URL
|
|
43454
|
+
*/
|
|
43455
|
+
hasTexture(url) {
|
|
43456
|
+
return this.textureCache.has(url);
|
|
43457
|
+
}
|
|
43458
|
+
/**
|
|
43459
|
+
* Get cached model (throws if not cached)
|
|
43460
|
+
* @param url - Model URL
|
|
43461
|
+
*/
|
|
43462
|
+
getModel(url) {
|
|
43463
|
+
const model = this.modelCache.get(url);
|
|
43464
|
+
if (!model) {
|
|
43465
|
+
throw new Error(`Model ${url} not in cache`);
|
|
43466
|
+
}
|
|
43467
|
+
return model;
|
|
43468
|
+
}
|
|
43469
|
+
/**
|
|
43470
|
+
* Get cached texture (throws if not cached)
|
|
43471
|
+
* @param url - Texture URL
|
|
43472
|
+
*/
|
|
43473
|
+
getTexture(url) {
|
|
43474
|
+
const texture = this.textureCache.get(url);
|
|
43475
|
+
if (!texture) {
|
|
43476
|
+
throw new Error(`Texture ${url} not in cache`);
|
|
43477
|
+
}
|
|
43478
|
+
return texture;
|
|
43479
|
+
}
|
|
43480
|
+
/**
|
|
43481
|
+
* Clear all caches
|
|
43482
|
+
*/
|
|
43483
|
+
clearCache() {
|
|
43484
|
+
this.textureCache.forEach((texture) => {
|
|
43485
|
+
texture.dispose();
|
|
43486
|
+
});
|
|
43487
|
+
this.modelCache.forEach((model) => {
|
|
43488
|
+
model.scene.traverse((child) => {
|
|
43489
|
+
if (child instanceof THREE.Mesh) {
|
|
43490
|
+
child.geometry.dispose();
|
|
43491
|
+
if (Array.isArray(child.material)) {
|
|
43492
|
+
child.material.forEach((m) => m.dispose());
|
|
43493
|
+
} else {
|
|
43494
|
+
child.material.dispose();
|
|
43495
|
+
}
|
|
43496
|
+
}
|
|
43497
|
+
});
|
|
43498
|
+
});
|
|
43499
|
+
this.modelCache.clear();
|
|
43500
|
+
this.textureCache.clear();
|
|
43501
|
+
this.loadingPromises.clear();
|
|
43502
|
+
}
|
|
43503
|
+
/**
|
|
43504
|
+
* Get cache statistics
|
|
43505
|
+
*/
|
|
43506
|
+
getStats() {
|
|
43507
|
+
return {
|
|
43508
|
+
models: this.modelCache.size,
|
|
43509
|
+
textures: this.textureCache.size,
|
|
43510
|
+
loading: this.loadingPromises.size
|
|
43511
|
+
};
|
|
43512
|
+
}
|
|
43513
|
+
};
|
|
43514
|
+
new AssetLoader();
|
|
43515
|
+
}
|
|
43516
|
+
});
|
|
43517
|
+
function useAssetLoader(options = {}) {
|
|
43518
|
+
const { preloadUrls = [], loader: customLoader } = options;
|
|
43519
|
+
const loaderRef = useRef(customLoader || new AssetLoader());
|
|
43520
|
+
const [state, setState] = useState({
|
|
43521
|
+
isLoading: false,
|
|
43522
|
+
progress: 0,
|
|
43523
|
+
loaded: 0,
|
|
43524
|
+
total: 0,
|
|
43525
|
+
errors: []
|
|
43526
|
+
});
|
|
43527
|
+
useEffect(() => {
|
|
43528
|
+
if (preloadUrls.length > 0) {
|
|
43529
|
+
preload(preloadUrls);
|
|
43530
|
+
}
|
|
43531
|
+
}, []);
|
|
43532
|
+
const updateProgress = useCallback((loaded, total) => {
|
|
43533
|
+
setState((prev) => ({
|
|
43534
|
+
...prev,
|
|
43535
|
+
loaded,
|
|
43536
|
+
total,
|
|
43537
|
+
progress: total > 0 ? Math.round(loaded / total * 100) : 0
|
|
43538
|
+
}));
|
|
43539
|
+
}, []);
|
|
43540
|
+
const loadModel = useCallback(
|
|
43541
|
+
async (url) => {
|
|
43542
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
43543
|
+
try {
|
|
43544
|
+
const model = await loaderRef.current.loadModel(url);
|
|
43545
|
+
setState((prev) => ({
|
|
43546
|
+
...prev,
|
|
43547
|
+
isLoading: false,
|
|
43548
|
+
loaded: prev.loaded + 1
|
|
43549
|
+
}));
|
|
43550
|
+
return model;
|
|
43551
|
+
} catch (error) {
|
|
43552
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
43553
|
+
setState((prev) => ({
|
|
43554
|
+
...prev,
|
|
43555
|
+
isLoading: false,
|
|
43556
|
+
errors: [...prev.errors, errorMsg]
|
|
43557
|
+
}));
|
|
43558
|
+
throw error;
|
|
43559
|
+
}
|
|
43560
|
+
},
|
|
43561
|
+
[]
|
|
43562
|
+
);
|
|
43563
|
+
const loadOBJ = useCallback(
|
|
43564
|
+
async (url) => {
|
|
43565
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
43566
|
+
try {
|
|
43567
|
+
const model = await loaderRef.current.loadOBJ(url);
|
|
43568
|
+
setState((prev) => ({
|
|
43569
|
+
...prev,
|
|
43570
|
+
isLoading: false,
|
|
43571
|
+
loaded: prev.loaded + 1
|
|
43572
|
+
}));
|
|
43573
|
+
return model;
|
|
43574
|
+
} catch (error) {
|
|
43575
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
43576
|
+
setState((prev) => ({
|
|
43577
|
+
...prev,
|
|
43578
|
+
isLoading: false,
|
|
43579
|
+
errors: [...prev.errors, errorMsg]
|
|
43580
|
+
}));
|
|
43581
|
+
throw error;
|
|
43582
|
+
}
|
|
43583
|
+
},
|
|
43584
|
+
[]
|
|
43585
|
+
);
|
|
43586
|
+
const loadTexture = useCallback(
|
|
43587
|
+
async (url) => {
|
|
43588
|
+
setState((prev) => ({ ...prev, isLoading: true }));
|
|
43589
|
+
try {
|
|
43590
|
+
const texture = await loaderRef.current.loadTexture(url);
|
|
43591
|
+
setState((prev) => ({
|
|
43592
|
+
...prev,
|
|
43593
|
+
isLoading: false,
|
|
43594
|
+
loaded: prev.loaded + 1
|
|
43595
|
+
}));
|
|
43596
|
+
return texture;
|
|
43597
|
+
} catch (error) {
|
|
43598
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
43599
|
+
setState((prev) => ({
|
|
43600
|
+
...prev,
|
|
43601
|
+
isLoading: false,
|
|
43602
|
+
errors: [...prev.errors, errorMsg]
|
|
43603
|
+
}));
|
|
43604
|
+
throw error;
|
|
43605
|
+
}
|
|
43606
|
+
},
|
|
43607
|
+
[]
|
|
43608
|
+
);
|
|
43609
|
+
const preload = useCallback(
|
|
43610
|
+
async (urls) => {
|
|
43611
|
+
setState((prev) => ({
|
|
43612
|
+
...prev,
|
|
43613
|
+
isLoading: true,
|
|
43614
|
+
total: urls.length,
|
|
43615
|
+
loaded: 0,
|
|
43616
|
+
errors: []
|
|
43617
|
+
}));
|
|
43618
|
+
let completed = 0;
|
|
43619
|
+
const errors = [];
|
|
43620
|
+
await Promise.all(
|
|
43621
|
+
urls.map(async (url) => {
|
|
43622
|
+
try {
|
|
43623
|
+
if (url.endsWith(".glb") || url.endsWith(".gltf")) {
|
|
43624
|
+
await loaderRef.current.loadModel(url);
|
|
43625
|
+
} else if (url.endsWith(".obj")) {
|
|
43626
|
+
await loaderRef.current.loadOBJ(url);
|
|
43627
|
+
} else if (/\.(png|jpg|jpeg|webp)$/i.test(url)) {
|
|
43628
|
+
await loaderRef.current.loadTexture(url);
|
|
43629
|
+
}
|
|
43630
|
+
completed++;
|
|
43631
|
+
updateProgress(completed, urls.length);
|
|
43632
|
+
} catch (error) {
|
|
43633
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
43634
|
+
errors.push(`${url}: ${errorMsg}`);
|
|
43635
|
+
completed++;
|
|
43636
|
+
updateProgress(completed, urls.length);
|
|
43637
|
+
}
|
|
43638
|
+
})
|
|
43639
|
+
);
|
|
43640
|
+
setState((prev) => ({
|
|
43641
|
+
...prev,
|
|
43642
|
+
isLoading: false,
|
|
43643
|
+
errors
|
|
43644
|
+
}));
|
|
43645
|
+
},
|
|
43646
|
+
[updateProgress]
|
|
43647
|
+
);
|
|
43648
|
+
const hasModel = useCallback((url) => {
|
|
43649
|
+
return loaderRef.current.hasModel(url);
|
|
43650
|
+
}, []);
|
|
43651
|
+
const hasTexture = useCallback((url) => {
|
|
43652
|
+
return loaderRef.current.hasTexture(url);
|
|
43653
|
+
}, []);
|
|
43654
|
+
const getModel = useCallback((url) => {
|
|
43655
|
+
try {
|
|
43656
|
+
return loaderRef.current.getModel(url);
|
|
43657
|
+
} catch {
|
|
43658
|
+
return void 0;
|
|
43659
|
+
}
|
|
43660
|
+
}, []);
|
|
43661
|
+
const getTexture = useCallback((url) => {
|
|
43662
|
+
try {
|
|
43663
|
+
return loaderRef.current.getTexture(url);
|
|
43664
|
+
} catch {
|
|
43665
|
+
return void 0;
|
|
43666
|
+
}
|
|
43667
|
+
}, []);
|
|
43668
|
+
const clearCache = useCallback(() => {
|
|
43669
|
+
loaderRef.current.clearCache();
|
|
43670
|
+
setState({
|
|
43671
|
+
isLoading: false,
|
|
43672
|
+
progress: 0,
|
|
43673
|
+
loaded: 0,
|
|
43674
|
+
total: 0,
|
|
43675
|
+
errors: []
|
|
43676
|
+
});
|
|
43677
|
+
}, []);
|
|
43678
|
+
return {
|
|
43679
|
+
...state,
|
|
43680
|
+
loadModel,
|
|
43681
|
+
loadOBJ,
|
|
43682
|
+
loadTexture,
|
|
43683
|
+
preload,
|
|
43684
|
+
hasModel,
|
|
43685
|
+
hasTexture,
|
|
43686
|
+
getModel,
|
|
43687
|
+
getTexture,
|
|
43688
|
+
clearCache
|
|
43689
|
+
};
|
|
43690
|
+
}
|
|
43691
|
+
var init_useAssetLoader = __esm({
|
|
43692
|
+
"components/game/molecules/three/hooks/useAssetLoader.ts"() {
|
|
43693
|
+
"use client";
|
|
43694
|
+
init_AssetLoader();
|
|
43695
|
+
}
|
|
43696
|
+
});
|
|
43697
|
+
function useGameCanvas3DEvents(options) {
|
|
43698
|
+
const {
|
|
43699
|
+
tileClickEvent,
|
|
43700
|
+
unitClickEvent,
|
|
43701
|
+
featureClickEvent,
|
|
43702
|
+
canvasClickEvent,
|
|
43703
|
+
tileHoverEvent,
|
|
43704
|
+
tileLeaveEvent,
|
|
43705
|
+
unitAnimationEvent,
|
|
43706
|
+
cameraChangeEvent,
|
|
43707
|
+
onTileClick,
|
|
43708
|
+
onUnitClick,
|
|
43709
|
+
onFeatureClick,
|
|
43710
|
+
onCanvasClick,
|
|
43711
|
+
onTileHover,
|
|
43712
|
+
onUnitAnimation
|
|
43713
|
+
} = options;
|
|
43714
|
+
const emit = useEmitEvent();
|
|
43715
|
+
const optionsRef = useRef(options);
|
|
43716
|
+
optionsRef.current = options;
|
|
43717
|
+
const handleTileClick = useCallback(
|
|
43718
|
+
(tile, event) => {
|
|
43719
|
+
if (tileClickEvent) {
|
|
43720
|
+
emit(tileClickEvent, {
|
|
43721
|
+
tileId: tile.id,
|
|
43722
|
+
x: tile.x,
|
|
43723
|
+
z: tile.z ?? tile.y ?? 0,
|
|
43724
|
+
type: tile.type,
|
|
43725
|
+
terrain: tile.terrain,
|
|
43726
|
+
elevation: tile.elevation
|
|
43727
|
+
});
|
|
43728
|
+
}
|
|
43729
|
+
optionsRef.current.onTileClick?.(tile, event);
|
|
43730
|
+
},
|
|
43731
|
+
[tileClickEvent, emit]
|
|
43732
|
+
);
|
|
43733
|
+
const handleUnitClick = useCallback(
|
|
43734
|
+
(unit, event) => {
|
|
43735
|
+
if (unitClickEvent) {
|
|
43736
|
+
emit(unitClickEvent, {
|
|
43737
|
+
unitId: unit.id,
|
|
43738
|
+
x: unit.x,
|
|
43739
|
+
z: unit.z ?? unit.y ?? 0,
|
|
43740
|
+
unitType: unit.unitType,
|
|
43741
|
+
name: unit.name,
|
|
43742
|
+
team: unit.team,
|
|
43743
|
+
faction: unit.faction,
|
|
43744
|
+
health: unit.health,
|
|
43745
|
+
maxHealth: unit.maxHealth
|
|
43746
|
+
});
|
|
43747
|
+
}
|
|
43748
|
+
optionsRef.current.onUnitClick?.(unit, event);
|
|
43749
|
+
},
|
|
43750
|
+
[unitClickEvent, emit]
|
|
43751
|
+
);
|
|
43752
|
+
const handleFeatureClick = useCallback(
|
|
43753
|
+
(feature, event) => {
|
|
43754
|
+
if (featureClickEvent) {
|
|
43755
|
+
emit(featureClickEvent, {
|
|
43756
|
+
featureId: feature.id,
|
|
43757
|
+
x: feature.x,
|
|
43758
|
+
z: feature.z ?? feature.y ?? 0,
|
|
43759
|
+
type: feature.type,
|
|
43760
|
+
elevation: feature.elevation
|
|
43761
|
+
});
|
|
43762
|
+
}
|
|
43763
|
+
optionsRef.current.onFeatureClick?.(feature, event);
|
|
43764
|
+
},
|
|
43765
|
+
[featureClickEvent, emit]
|
|
43766
|
+
);
|
|
43767
|
+
const handleCanvasClick = useCallback(
|
|
43768
|
+
(event) => {
|
|
43769
|
+
if (canvasClickEvent) {
|
|
43770
|
+
emit(canvasClickEvent, {
|
|
43771
|
+
clientX: event.clientX,
|
|
43772
|
+
clientY: event.clientY,
|
|
43773
|
+
button: event.button
|
|
43774
|
+
});
|
|
43775
|
+
}
|
|
43776
|
+
optionsRef.current.onCanvasClick?.(event);
|
|
43777
|
+
},
|
|
43778
|
+
[canvasClickEvent, emit]
|
|
43779
|
+
);
|
|
43780
|
+
const handleTileHover = useCallback(
|
|
43781
|
+
(tile, event) => {
|
|
43782
|
+
if (tile) {
|
|
43783
|
+
if (tileHoverEvent) {
|
|
43784
|
+
emit(tileHoverEvent, {
|
|
43785
|
+
tileId: tile.id,
|
|
43786
|
+
x: tile.x,
|
|
43787
|
+
z: tile.z ?? tile.y ?? 0,
|
|
43788
|
+
type: tile.type
|
|
43789
|
+
});
|
|
43790
|
+
}
|
|
43791
|
+
} else {
|
|
43792
|
+
if (tileLeaveEvent) {
|
|
43793
|
+
emit(tileLeaveEvent, {});
|
|
43794
|
+
}
|
|
43795
|
+
}
|
|
43796
|
+
optionsRef.current.onTileHover?.(tile, event);
|
|
43797
|
+
},
|
|
43798
|
+
[tileHoverEvent, tileLeaveEvent, emit]
|
|
43799
|
+
);
|
|
43800
|
+
const handleUnitAnimation = useCallback(
|
|
43801
|
+
(unitId, state) => {
|
|
43802
|
+
if (unitAnimationEvent) {
|
|
43803
|
+
emit(unitAnimationEvent, {
|
|
43804
|
+
unitId,
|
|
43805
|
+
state,
|
|
43806
|
+
timestamp: Date.now()
|
|
43807
|
+
});
|
|
43808
|
+
}
|
|
43809
|
+
optionsRef.current.onUnitAnimation?.(unitId, state);
|
|
43810
|
+
},
|
|
43811
|
+
[unitAnimationEvent, emit]
|
|
43812
|
+
);
|
|
43813
|
+
const handleCameraChange = useCallback(
|
|
43814
|
+
(position) => {
|
|
43815
|
+
if (cameraChangeEvent) {
|
|
43816
|
+
emit(cameraChangeEvent, {
|
|
43817
|
+
position,
|
|
43818
|
+
timestamp: Date.now()
|
|
43819
|
+
});
|
|
43820
|
+
}
|
|
43821
|
+
},
|
|
43822
|
+
[cameraChangeEvent, emit]
|
|
43823
|
+
);
|
|
43824
|
+
return {
|
|
43825
|
+
handleTileClick,
|
|
43826
|
+
handleUnitClick,
|
|
43827
|
+
handleFeatureClick,
|
|
43828
|
+
handleCanvasClick,
|
|
43829
|
+
handleTileHover,
|
|
43830
|
+
handleUnitAnimation,
|
|
43831
|
+
handleCameraChange
|
|
43832
|
+
};
|
|
43833
|
+
}
|
|
43834
|
+
var init_useGameCanvas3DEvents = __esm({
|
|
43835
|
+
"components/game/molecules/three/hooks/useGameCanvas3DEvents.ts"() {
|
|
43836
|
+
"use client";
|
|
43837
|
+
init_useEventBus();
|
|
43838
|
+
}
|
|
43839
|
+
});
|
|
43840
|
+
|
|
43841
|
+
// components/game/molecules/three/components/Canvas3DLoadingState.css
|
|
43842
|
+
var init_Canvas3DLoadingState = __esm({
|
|
43843
|
+
"components/game/molecules/three/components/Canvas3DLoadingState.css"() {
|
|
43844
|
+
}
|
|
43845
|
+
});
|
|
43846
|
+
function Canvas3DLoadingState({
|
|
43847
|
+
progress = 0,
|
|
43848
|
+
loaded = 0,
|
|
43849
|
+
total = 0,
|
|
43850
|
+
message = "Loading 3D Scene...",
|
|
43851
|
+
details,
|
|
43852
|
+
showSpinner = true,
|
|
43853
|
+
className
|
|
43854
|
+
}) {
|
|
43855
|
+
const clampedProgress = Math.max(0, Math.min(100, progress));
|
|
43856
|
+
const hasProgress = total > 0;
|
|
43857
|
+
return /* @__PURE__ */ jsxs("div", { className: `canvas-3d-loading ${className || ""}`, children: [
|
|
43858
|
+
/* @__PURE__ */ jsxs("div", { className: "canvas-3d-loading__content", children: [
|
|
43859
|
+
showSpinner && /* @__PURE__ */ jsxs("div", { className: "canvas-3d-loading__spinner", children: [
|
|
43860
|
+
/* @__PURE__ */ jsx("div", { className: "spinner__ring" }),
|
|
43861
|
+
/* @__PURE__ */ jsx("div", { className: "spinner__ring spinner__ring--secondary" })
|
|
43862
|
+
] }),
|
|
43863
|
+
/* @__PURE__ */ jsx("div", { className: "canvas-3d-loading__message", children: message }),
|
|
43864
|
+
details && /* @__PURE__ */ jsx("div", { className: "canvas-3d-loading__details", children: details }),
|
|
43865
|
+
hasProgress && /* @__PURE__ */ jsxs("div", { className: "canvas-3d-loading__progress", children: [
|
|
43866
|
+
/* @__PURE__ */ jsx("div", { className: "progress__bar", children: /* @__PURE__ */ jsx(
|
|
43867
|
+
"div",
|
|
43868
|
+
{
|
|
43869
|
+
className: "progress__fill",
|
|
43870
|
+
style: { width: `${clampedProgress}%` }
|
|
43871
|
+
}
|
|
43872
|
+
) }),
|
|
43873
|
+
/* @__PURE__ */ jsxs("div", { className: "progress__text", children: [
|
|
43874
|
+
/* @__PURE__ */ jsxs("span", { className: "progress__percentage", children: [
|
|
43875
|
+
clampedProgress,
|
|
43876
|
+
"%"
|
|
43877
|
+
] }),
|
|
43878
|
+
/* @__PURE__ */ jsxs("span", { className: "progress__count", children: [
|
|
43879
|
+
"(",
|
|
43880
|
+
loaded,
|
|
43881
|
+
"/",
|
|
43882
|
+
total,
|
|
43883
|
+
")"
|
|
43884
|
+
] })
|
|
43885
|
+
] })
|
|
43886
|
+
] })
|
|
43887
|
+
] }),
|
|
43888
|
+
/* @__PURE__ */ jsx("div", { className: "canvas-3d-loading__background", children: /* @__PURE__ */ jsx("div", { className: "bg__grid" }) })
|
|
43889
|
+
] });
|
|
43890
|
+
}
|
|
43891
|
+
var init_Canvas3DLoadingState2 = __esm({
|
|
43892
|
+
"components/game/molecules/three/components/Canvas3DLoadingState.tsx"() {
|
|
43893
|
+
init_Canvas3DLoadingState();
|
|
43894
|
+
}
|
|
43895
|
+
});
|
|
43896
|
+
|
|
43897
|
+
// components/game/molecules/three/components/Canvas3DErrorBoundary.css
|
|
43898
|
+
var init_Canvas3DErrorBoundary = __esm({
|
|
43899
|
+
"components/game/molecules/three/components/Canvas3DErrorBoundary.css"() {
|
|
43900
|
+
}
|
|
43901
|
+
});
|
|
43902
|
+
var log9, Canvas3DErrorBoundary;
|
|
43903
|
+
var init_Canvas3DErrorBoundary2 = __esm({
|
|
43904
|
+
"components/game/molecules/three/components/Canvas3DErrorBoundary.tsx"() {
|
|
43905
|
+
init_Canvas3DErrorBoundary();
|
|
43906
|
+
log9 = createLogger("almadar:ui:game:canvas3d:error-boundary");
|
|
43907
|
+
Canvas3DErrorBoundary = class extends Component {
|
|
43908
|
+
constructor(props) {
|
|
43909
|
+
super(props);
|
|
43910
|
+
__publicField(this, "handleReset", () => {
|
|
43911
|
+
this.setState({
|
|
43912
|
+
hasError: false,
|
|
43913
|
+
error: null,
|
|
43914
|
+
errorInfo: null
|
|
43915
|
+
});
|
|
43916
|
+
this.props.onReset?.();
|
|
43917
|
+
});
|
|
43918
|
+
this.state = {
|
|
43919
|
+
hasError: false,
|
|
43920
|
+
error: null,
|
|
43921
|
+
errorInfo: null
|
|
43922
|
+
};
|
|
43923
|
+
}
|
|
43924
|
+
static getDerivedStateFromError(error) {
|
|
43925
|
+
return {
|
|
43926
|
+
hasError: true,
|
|
43927
|
+
error,
|
|
43928
|
+
errorInfo: null
|
|
43929
|
+
};
|
|
43930
|
+
}
|
|
43931
|
+
componentDidCatch(error, errorInfo) {
|
|
43932
|
+
this.setState({ errorInfo });
|
|
43933
|
+
this.props.onError?.(error, errorInfo);
|
|
43934
|
+
log9.error("Error caught", { error });
|
|
43935
|
+
log9.error("Component stack", { componentStack: errorInfo.componentStack ?? "<none>" });
|
|
43936
|
+
}
|
|
43937
|
+
render() {
|
|
43938
|
+
if (this.state.hasError) {
|
|
43939
|
+
if (this.props.fallback) {
|
|
43940
|
+
return this.props.fallback;
|
|
43941
|
+
}
|
|
43942
|
+
return /* @__PURE__ */ jsx("div", { className: "canvas-3d-error", children: /* @__PURE__ */ jsxs("div", { className: "canvas-3d-error__content", children: [
|
|
43943
|
+
/* @__PURE__ */ jsx("div", { className: "canvas-3d-error__icon", children: "\u26A0\uFE0F" }),
|
|
43944
|
+
/* @__PURE__ */ jsx("h2", { className: "canvas-3d-error__title", children: "3D Scene Error" }),
|
|
43945
|
+
/* @__PURE__ */ jsx("p", { className: "canvas-3d-error__message", children: "Something went wrong while rendering the 3D scene." }),
|
|
43946
|
+
this.state.error && /* @__PURE__ */ jsxs("details", { className: "canvas-3d-error__details", children: [
|
|
43947
|
+
/* @__PURE__ */ jsx("summary", { children: "Error Details" }),
|
|
43948
|
+
/* @__PURE__ */ jsxs("pre", { className: "error__stack", children: [
|
|
43949
|
+
this.state.error.message,
|
|
43950
|
+
"\n",
|
|
43951
|
+
this.state.error.stack
|
|
43952
|
+
] }),
|
|
43953
|
+
this.state.errorInfo && /* @__PURE__ */ jsx("pre", { className: "error__component-stack", children: this.state.errorInfo.componentStack })
|
|
43954
|
+
] }),
|
|
43955
|
+
/* @__PURE__ */ jsxs("div", { className: "canvas-3d-error__actions", children: [
|
|
43956
|
+
/* @__PURE__ */ jsx(
|
|
43957
|
+
"button",
|
|
43958
|
+
{
|
|
43959
|
+
className: "error__button error__button--primary",
|
|
43960
|
+
onClick: this.handleReset,
|
|
43961
|
+
children: "Try Again"
|
|
43962
|
+
}
|
|
43963
|
+
),
|
|
43964
|
+
/* @__PURE__ */ jsx(
|
|
43965
|
+
"button",
|
|
43966
|
+
{
|
|
43967
|
+
className: "error__button error__button--secondary",
|
|
43968
|
+
onClick: () => window.location.reload(),
|
|
43969
|
+
children: "Reload Page"
|
|
43970
|
+
}
|
|
43971
|
+
)
|
|
43972
|
+
] })
|
|
43973
|
+
] }) });
|
|
43974
|
+
}
|
|
43975
|
+
return this.props.children;
|
|
43976
|
+
}
|
|
43977
|
+
};
|
|
43978
|
+
}
|
|
43979
|
+
});
|
|
43980
|
+
function detectAssetRoot2(modelUrl) {
|
|
43981
|
+
const idx = modelUrl.indexOf("/3d/");
|
|
43982
|
+
if (idx !== -1) {
|
|
43983
|
+
return modelUrl.substring(0, idx + 4);
|
|
43984
|
+
}
|
|
43985
|
+
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
43986
|
+
}
|
|
43987
|
+
function useGLTFModel(url, resourceBasePath) {
|
|
43988
|
+
const [state, setState] = useState({
|
|
43989
|
+
model: null,
|
|
43990
|
+
isLoading: false,
|
|
43991
|
+
error: null
|
|
43992
|
+
});
|
|
43993
|
+
useEffect(() => {
|
|
43994
|
+
if (!url) {
|
|
43995
|
+
setState({ model: null, isLoading: false, error: null });
|
|
43996
|
+
return;
|
|
43997
|
+
}
|
|
43998
|
+
log10.debug("Loading", { url });
|
|
43999
|
+
setState((prev) => ({ ...prev, isLoading: true, error: null }));
|
|
44000
|
+
const assetRoot = resourceBasePath || detectAssetRoot2(url);
|
|
44001
|
+
const loader = new GLTFLoader$1();
|
|
44002
|
+
loader.setResourcePath(assetRoot);
|
|
44003
|
+
loader.load(
|
|
44004
|
+
url,
|
|
44005
|
+
(gltf) => {
|
|
44006
|
+
log10.debug("Loaded", { url });
|
|
44007
|
+
setState({
|
|
44008
|
+
model: gltf.scene,
|
|
44009
|
+
isLoading: false,
|
|
44010
|
+
error: null
|
|
44011
|
+
});
|
|
44012
|
+
},
|
|
44013
|
+
void 0,
|
|
44014
|
+
(err) => {
|
|
44015
|
+
log10.warn("Failed", { url, error: err instanceof Error ? err : String(err) });
|
|
44016
|
+
setState({
|
|
44017
|
+
model: null,
|
|
44018
|
+
isLoading: false,
|
|
44019
|
+
error: err instanceof Error ? err : new Error(String(err))
|
|
44020
|
+
});
|
|
44021
|
+
}
|
|
44022
|
+
);
|
|
44023
|
+
}, [url, resourceBasePath]);
|
|
44024
|
+
return state;
|
|
44025
|
+
}
|
|
44026
|
+
function ModelLoader({
|
|
44027
|
+
url,
|
|
44028
|
+
position = [0, 0, 0],
|
|
44029
|
+
scale = 1,
|
|
44030
|
+
rotation = [0, 0, 0],
|
|
44031
|
+
isSelected = false,
|
|
44032
|
+
isHovered = false,
|
|
44033
|
+
onClick,
|
|
44034
|
+
onHover,
|
|
44035
|
+
fallbackGeometry = "box",
|
|
44036
|
+
castShadow = true,
|
|
44037
|
+
receiveShadow = true,
|
|
44038
|
+
resourceBasePath
|
|
44039
|
+
}) {
|
|
44040
|
+
const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
|
|
44041
|
+
const model = useMemo(() => {
|
|
44042
|
+
if (!loadedModel) return null;
|
|
44043
|
+
const cloned = loadedModel.clone();
|
|
44044
|
+
cloned.traverse((child) => {
|
|
44045
|
+
if (child instanceof THREE.Mesh) {
|
|
44046
|
+
child.castShadow = castShadow;
|
|
44047
|
+
child.receiveShadow = receiveShadow;
|
|
44048
|
+
}
|
|
44049
|
+
});
|
|
44050
|
+
return cloned;
|
|
44051
|
+
}, [loadedModel, castShadow, receiveShadow]);
|
|
44052
|
+
const scaleArray = useMemo(() => {
|
|
44053
|
+
if (typeof scale === "number") {
|
|
44054
|
+
return [scale, scale, scale];
|
|
44055
|
+
}
|
|
44056
|
+
return scale;
|
|
44057
|
+
}, [scale]);
|
|
44058
|
+
const rotationRad = useMemo(() => {
|
|
44059
|
+
return [
|
|
44060
|
+
rotation[0] * Math.PI / 180,
|
|
44061
|
+
rotation[1] * Math.PI / 180,
|
|
44062
|
+
rotation[2] * Math.PI / 180
|
|
44063
|
+
];
|
|
44064
|
+
}, [rotation]);
|
|
44065
|
+
if (isLoading) {
|
|
44066
|
+
return /* @__PURE__ */ jsx("group", { position, children: /* @__PURE__ */ jsxs("mesh", { rotation: [Math.PI / 2, 0, 0], children: [
|
|
44067
|
+
/* @__PURE__ */ jsx("ringGeometry", { args: [0.3, 0.35, 16] }),
|
|
44068
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { color: "#4a90d9", transparent: true, opacity: 0.8 })
|
|
44069
|
+
] }) });
|
|
44070
|
+
}
|
|
44071
|
+
if (error || !model) {
|
|
44072
|
+
if (fallbackGeometry === "none") {
|
|
44073
|
+
return /* @__PURE__ */ jsx("group", { position });
|
|
44074
|
+
}
|
|
44075
|
+
const fallbackProps = {
|
|
44076
|
+
onClick,
|
|
44077
|
+
onPointerOver: () => onHover?.(true),
|
|
44078
|
+
onPointerOut: () => onHover?.(false)
|
|
44079
|
+
};
|
|
44080
|
+
return /* @__PURE__ */ jsxs("group", { position, children: [
|
|
44081
|
+
(isSelected || isHovered) && /* @__PURE__ */ jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
44082
|
+
/* @__PURE__ */ jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
|
|
44083
|
+
/* @__PURE__ */ jsx(
|
|
44084
|
+
"meshBasicMaterial",
|
|
44085
|
+
{
|
|
44086
|
+
color: isSelected ? 16755200 : 16777215,
|
|
44087
|
+
transparent: true,
|
|
44088
|
+
opacity: 0.5
|
|
44089
|
+
}
|
|
44090
|
+
)
|
|
44091
|
+
] }),
|
|
44092
|
+
fallbackGeometry === "box" && /* @__PURE__ */ jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
44093
|
+
/* @__PURE__ */ jsx("boxGeometry", { args: [0.8, 0.8, 0.8] }),
|
|
44094
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
44095
|
+
] }),
|
|
44096
|
+
fallbackGeometry === "sphere" && /* @__PURE__ */ jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
44097
|
+
/* @__PURE__ */ jsx("sphereGeometry", { args: [0.4, 16, 16] }),
|
|
44098
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
44099
|
+
] }),
|
|
44100
|
+
fallbackGeometry === "cylinder" && /* @__PURE__ */ jsxs("mesh", { ...fallbackProps, position: [0, 0.5, 0], children: [
|
|
44101
|
+
/* @__PURE__ */ jsx("cylinderGeometry", { args: [0.3, 0.3, 0.8, 16] }),
|
|
44102
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: error ? 16729156 : 8947848 })
|
|
44103
|
+
] })
|
|
44104
|
+
] });
|
|
44105
|
+
}
|
|
44106
|
+
return /* @__PURE__ */ jsxs(
|
|
44107
|
+
"group",
|
|
44108
|
+
{
|
|
44109
|
+
position,
|
|
44110
|
+
rotation: rotationRad,
|
|
44111
|
+
onClick,
|
|
44112
|
+
onPointerOver: () => onHover?.(true),
|
|
44113
|
+
onPointerOut: () => onHover?.(false),
|
|
44114
|
+
children: [
|
|
44115
|
+
(isSelected || isHovered) && /* @__PURE__ */ jsxs("mesh", { position: [0, 0.02, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
44116
|
+
/* @__PURE__ */ jsx("ringGeometry", { args: [0.6, 0.7, 32] }),
|
|
44117
|
+
/* @__PURE__ */ jsx(
|
|
44118
|
+
"meshBasicMaterial",
|
|
44119
|
+
{
|
|
44120
|
+
color: isSelected ? 16755200 : 16777215,
|
|
44121
|
+
transparent: true,
|
|
44122
|
+
opacity: 0.5
|
|
44123
|
+
}
|
|
44124
|
+
)
|
|
44125
|
+
] }),
|
|
44126
|
+
/* @__PURE__ */ jsx("primitive", { object: model, scale: scaleArray })
|
|
44127
|
+
]
|
|
44128
|
+
}
|
|
44129
|
+
);
|
|
44130
|
+
}
|
|
44131
|
+
var log10;
|
|
44132
|
+
var init_ModelLoader = __esm({
|
|
44133
|
+
"components/game/molecules/three/components/ModelLoader.tsx"() {
|
|
44134
|
+
"use client";
|
|
44135
|
+
log10 = createLogger("almadar:ui:game:model-loader");
|
|
44136
|
+
}
|
|
44137
|
+
});
|
|
44138
|
+
|
|
44139
|
+
// components/game/molecules/GameCanvas3D.css
|
|
44140
|
+
var init_GameCanvas3D = __esm({
|
|
44141
|
+
"components/game/molecules/GameCanvas3D.css"() {
|
|
44142
|
+
}
|
|
44143
|
+
});
|
|
44144
|
+
function CameraController({
|
|
44145
|
+
onCameraChange
|
|
44146
|
+
}) {
|
|
44147
|
+
const { camera } = useThree();
|
|
44148
|
+
useEffect(() => {
|
|
44149
|
+
if (onCameraChange) {
|
|
44150
|
+
onCameraChange({
|
|
44151
|
+
x: camera.position.x,
|
|
44152
|
+
y: camera.position.y,
|
|
44153
|
+
z: camera.position.z
|
|
44154
|
+
});
|
|
44155
|
+
}
|
|
44156
|
+
}, [camera.position, onCameraChange]);
|
|
44157
|
+
return null;
|
|
44158
|
+
}
|
|
44159
|
+
var DEFAULT_GRID_CONFIG, GameCanvas3D;
|
|
44160
|
+
var init_GameCanvas3D2 = __esm({
|
|
44161
|
+
"components/game/molecules/GameCanvas3D.tsx"() {
|
|
44162
|
+
"use client";
|
|
44163
|
+
init_AssetLoader();
|
|
44164
|
+
init_useAssetLoader();
|
|
44165
|
+
init_useGameCanvas3DEvents();
|
|
44166
|
+
init_Canvas3DLoadingState2();
|
|
44167
|
+
init_Canvas3DErrorBoundary2();
|
|
44168
|
+
init_ModelLoader();
|
|
44169
|
+
init_cn();
|
|
44170
|
+
init_GameCanvas3D();
|
|
44171
|
+
DEFAULT_GRID_CONFIG = {
|
|
44172
|
+
cellSize: 1,
|
|
44173
|
+
offsetX: 0,
|
|
44174
|
+
offsetZ: 0
|
|
44175
|
+
};
|
|
44176
|
+
GameCanvas3D = forwardRef(
|
|
44177
|
+
({
|
|
44178
|
+
tiles = [],
|
|
44179
|
+
units = [],
|
|
44180
|
+
features = [],
|
|
44181
|
+
events: events2 = [],
|
|
44182
|
+
orientation = "standard",
|
|
44183
|
+
cameraMode = "isometric",
|
|
44184
|
+
showGrid = true,
|
|
44185
|
+
showCoordinates = false,
|
|
44186
|
+
showTileInfo = false,
|
|
44187
|
+
overlay = "default",
|
|
44188
|
+
shadows = true,
|
|
44189
|
+
backgroundColor = "#1a1a2e",
|
|
44190
|
+
onTileClick,
|
|
44191
|
+
onUnitClick,
|
|
44192
|
+
onFeatureClick,
|
|
44193
|
+
onCanvasClick,
|
|
44194
|
+
onTileHover,
|
|
44195
|
+
onUnitAnimation,
|
|
44196
|
+
assetLoader: customAssetLoader,
|
|
44197
|
+
tileRenderer: CustomTileRenderer,
|
|
44198
|
+
unitRenderer: CustomUnitRenderer,
|
|
44199
|
+
featureRenderer: CustomFeatureRenderer,
|
|
44200
|
+
className,
|
|
44201
|
+
isLoading: externalLoading,
|
|
44202
|
+
error: externalError,
|
|
44203
|
+
entity,
|
|
44204
|
+
preloadAssets = [],
|
|
44205
|
+
tileClickEvent,
|
|
44206
|
+
unitClickEvent,
|
|
44207
|
+
featureClickEvent,
|
|
44208
|
+
canvasClickEvent,
|
|
44209
|
+
tileHoverEvent,
|
|
44210
|
+
tileLeaveEvent,
|
|
44211
|
+
unitAnimationEvent,
|
|
44212
|
+
cameraChangeEvent,
|
|
44213
|
+
loadingMessage = "Loading 3D Scene...",
|
|
44214
|
+
useInstancing = true,
|
|
44215
|
+
validMoves = [],
|
|
44216
|
+
attackTargets = [],
|
|
44217
|
+
selectedTileIds = [],
|
|
44218
|
+
selectedUnitId = null,
|
|
44219
|
+
children
|
|
44220
|
+
}, ref) => {
|
|
44221
|
+
const containerRef = useRef(null);
|
|
44222
|
+
const controlsRef = useRef(null);
|
|
44223
|
+
const [hoveredTile, setHoveredTile] = useState(null);
|
|
44224
|
+
const [internalError, setInternalError] = useState(null);
|
|
44225
|
+
const { isLoading: assetsLoading, progress, loaded, total } = useAssetLoader({
|
|
44226
|
+
preloadUrls: preloadAssets,
|
|
44227
|
+
loader: customAssetLoader
|
|
44228
|
+
});
|
|
44229
|
+
const eventHandlers = useGameCanvas3DEvents({
|
|
44230
|
+
tileClickEvent,
|
|
44231
|
+
unitClickEvent,
|
|
44232
|
+
featureClickEvent,
|
|
44233
|
+
canvasClickEvent,
|
|
44234
|
+
tileHoverEvent,
|
|
44235
|
+
tileLeaveEvent,
|
|
44236
|
+
unitAnimationEvent,
|
|
44237
|
+
cameraChangeEvent,
|
|
44238
|
+
onTileClick,
|
|
44239
|
+
onUnitClick,
|
|
44240
|
+
onFeatureClick,
|
|
44241
|
+
onCanvasClick,
|
|
44242
|
+
onTileHover,
|
|
44243
|
+
onUnitAnimation
|
|
44244
|
+
});
|
|
44245
|
+
const gridBounds = useMemo(() => {
|
|
44246
|
+
if (tiles.length === 0) {
|
|
44247
|
+
return { minX: 0, maxX: 10, minZ: 0, maxZ: 10 };
|
|
44248
|
+
}
|
|
44249
|
+
const xs = tiles.map((t) => t.x);
|
|
44250
|
+
const zs = tiles.map((t) => t.z || t.y || 0);
|
|
44251
|
+
return {
|
|
44252
|
+
minX: Math.min(...xs),
|
|
44253
|
+
maxX: Math.max(...xs),
|
|
44254
|
+
minZ: Math.min(...zs),
|
|
44255
|
+
maxZ: Math.max(...zs)
|
|
44256
|
+
};
|
|
44257
|
+
}, [tiles]);
|
|
44258
|
+
const cameraTarget = useMemo(() => {
|
|
44259
|
+
return [
|
|
44260
|
+
(gridBounds.minX + gridBounds.maxX) / 2,
|
|
44261
|
+
0,
|
|
44262
|
+
(gridBounds.minZ + gridBounds.maxZ) / 2
|
|
44263
|
+
];
|
|
44264
|
+
}, [gridBounds]);
|
|
44265
|
+
const gridConfig = useMemo(
|
|
44266
|
+
() => ({
|
|
44267
|
+
...DEFAULT_GRID_CONFIG,
|
|
44268
|
+
offsetX: -(gridBounds.maxX - gridBounds.minX) / 2,
|
|
44269
|
+
offsetZ: -(gridBounds.maxZ - gridBounds.minZ) / 2
|
|
44270
|
+
}),
|
|
44271
|
+
[gridBounds]
|
|
44272
|
+
);
|
|
44273
|
+
const gridToWorld = useCallback(
|
|
44274
|
+
(x, z, y = 0) => {
|
|
44275
|
+
const worldX = (x - gridBounds.minX) * gridConfig.cellSize;
|
|
44276
|
+
const worldZ = (z - gridBounds.minZ) * gridConfig.cellSize;
|
|
44277
|
+
return [worldX, y * gridConfig.cellSize, worldZ];
|
|
44278
|
+
},
|
|
44279
|
+
[gridBounds, gridConfig]
|
|
44280
|
+
);
|
|
44281
|
+
useImperativeHandle(ref, () => ({
|
|
44282
|
+
getCameraPosition: () => {
|
|
44283
|
+
if (controlsRef.current) {
|
|
44284
|
+
const pos = controlsRef.current.object.position;
|
|
44285
|
+
return new THREE.Vector3(pos.x, pos.y, pos.z);
|
|
44286
|
+
}
|
|
44287
|
+
return null;
|
|
44288
|
+
},
|
|
44289
|
+
setCameraPosition: (x, y, z) => {
|
|
44290
|
+
if (controlsRef.current) {
|
|
44291
|
+
controlsRef.current.object.position.set(x, y, z);
|
|
44292
|
+
controlsRef.current.update();
|
|
44293
|
+
}
|
|
44294
|
+
},
|
|
44295
|
+
lookAt: (x, y, z) => {
|
|
44296
|
+
if (controlsRef.current) {
|
|
44297
|
+
controlsRef.current.target.set(x, y, z);
|
|
44298
|
+
controlsRef.current.update();
|
|
44299
|
+
}
|
|
44300
|
+
},
|
|
44301
|
+
resetCamera: () => {
|
|
44302
|
+
if (controlsRef.current) {
|
|
44303
|
+
controlsRef.current.reset();
|
|
44304
|
+
}
|
|
44305
|
+
},
|
|
44306
|
+
screenshot: () => {
|
|
44307
|
+
const canvas = containerRef.current?.querySelector("canvas");
|
|
44308
|
+
if (canvas) {
|
|
44309
|
+
return canvas.toDataURL("image/png");
|
|
44310
|
+
}
|
|
44311
|
+
return null;
|
|
44312
|
+
},
|
|
44313
|
+
export: () => ({
|
|
44314
|
+
tiles,
|
|
44315
|
+
units,
|
|
44316
|
+
features
|
|
44317
|
+
})
|
|
44318
|
+
}));
|
|
44319
|
+
const handleTileClick = useCallback(
|
|
44320
|
+
(tile, event) => {
|
|
44321
|
+
eventHandlers.handleTileClick(tile, event);
|
|
44322
|
+
},
|
|
44323
|
+
[eventHandlers]
|
|
44324
|
+
);
|
|
44325
|
+
const handleUnitClick = useCallback(
|
|
44326
|
+
(unit, event) => {
|
|
44327
|
+
eventHandlers.handleUnitClick(unit, event);
|
|
44328
|
+
},
|
|
44329
|
+
[eventHandlers]
|
|
44330
|
+
);
|
|
44331
|
+
const handleFeatureClick = useCallback(
|
|
44332
|
+
(feature, event) => {
|
|
44333
|
+
if (event) {
|
|
44334
|
+
eventHandlers.handleFeatureClick(feature, event);
|
|
44335
|
+
}
|
|
44336
|
+
},
|
|
44337
|
+
[eventHandlers]
|
|
44338
|
+
);
|
|
44339
|
+
const handleTileHover = useCallback(
|
|
44340
|
+
(tile, event) => {
|
|
44341
|
+
setHoveredTile(tile);
|
|
44342
|
+
if (event) {
|
|
44343
|
+
eventHandlers.handleTileHover(tile, event);
|
|
44344
|
+
}
|
|
44345
|
+
},
|
|
44346
|
+
[eventHandlers]
|
|
44347
|
+
);
|
|
44348
|
+
const cameraConfig = useMemo(() => {
|
|
44349
|
+
const size = Math.max(
|
|
44350
|
+
gridBounds.maxX - gridBounds.minX,
|
|
44351
|
+
gridBounds.maxZ - gridBounds.minZ
|
|
44352
|
+
);
|
|
44353
|
+
const distance = size * 1.5;
|
|
44354
|
+
switch (cameraMode) {
|
|
44355
|
+
case "isometric":
|
|
44356
|
+
return {
|
|
44357
|
+
position: [distance, distance * 0.8, distance],
|
|
44358
|
+
fov: 45
|
|
44359
|
+
};
|
|
44360
|
+
case "top-down":
|
|
44361
|
+
return {
|
|
44362
|
+
position: [0, distance * 2, 0],
|
|
44363
|
+
fov: 45
|
|
44364
|
+
};
|
|
44365
|
+
case "perspective":
|
|
44366
|
+
default:
|
|
44367
|
+
return {
|
|
44368
|
+
position: [distance, distance, distance],
|
|
44369
|
+
fov: 45
|
|
44370
|
+
};
|
|
44371
|
+
}
|
|
44372
|
+
}, [cameraMode, gridBounds]);
|
|
44373
|
+
const DefaultTileRenderer = useCallback(
|
|
44374
|
+
({ tile, position }) => {
|
|
44375
|
+
const isSelected = tile.id ? selectedTileIds.includes(tile.id) : false;
|
|
44376
|
+
const isHovered = hoveredTile?.id === tile.id;
|
|
44377
|
+
const isValidMove = validMoves.some(
|
|
44378
|
+
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
44379
|
+
);
|
|
44380
|
+
const isAttackTarget = attackTargets.some(
|
|
44381
|
+
(m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)
|
|
44382
|
+
);
|
|
44383
|
+
let color = 8421504;
|
|
44384
|
+
if (tile.type === "water") color = 4491468;
|
|
44385
|
+
else if (tile.type === "grass") color = 4500036;
|
|
44386
|
+
else if (tile.type === "sand") color = 14535816;
|
|
44387
|
+
else if (tile.type === "rock") color = 8947848;
|
|
44388
|
+
else if (tile.type === "snow") color = 15658734;
|
|
44389
|
+
let emissive = 0;
|
|
44390
|
+
if (isSelected) emissive = 4473924;
|
|
44391
|
+
else if (isAttackTarget) emissive = 4456448;
|
|
44392
|
+
else if (isValidMove) emissive = 17408;
|
|
44393
|
+
else if (isHovered) emissive = 2236962;
|
|
44394
|
+
if (tile.modelUrl) {
|
|
44395
|
+
return /* @__PURE__ */ jsx(
|
|
44396
|
+
"group",
|
|
44397
|
+
{
|
|
44398
|
+
position,
|
|
44399
|
+
onClick: (e) => handleTileClick(tile, e),
|
|
44400
|
+
onPointerEnter: (e) => handleTileHover(tile, e),
|
|
44401
|
+
onPointerLeave: (e) => handleTileHover(null, e),
|
|
44402
|
+
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
44403
|
+
children: /* @__PURE__ */ jsx(
|
|
44404
|
+
ModelLoader,
|
|
44405
|
+
{
|
|
44406
|
+
url: tile.modelUrl,
|
|
44407
|
+
scale: 0.95,
|
|
44408
|
+
fallbackGeometry: "box",
|
|
44409
|
+
castShadow: true,
|
|
44410
|
+
receiveShadow: true
|
|
44411
|
+
}
|
|
44412
|
+
)
|
|
44413
|
+
}
|
|
44414
|
+
);
|
|
44415
|
+
}
|
|
44416
|
+
return /* @__PURE__ */ jsxs(
|
|
44417
|
+
"mesh",
|
|
44418
|
+
{
|
|
44419
|
+
position,
|
|
44420
|
+
onClick: (e) => handleTileClick(tile, e),
|
|
44421
|
+
onPointerEnter: (e) => handleTileHover(tile, e),
|
|
44422
|
+
onPointerLeave: (e) => handleTileHover(null, e),
|
|
44423
|
+
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
44424
|
+
children: [
|
|
44425
|
+
/* @__PURE__ */ jsx("boxGeometry", { args: [0.95, 0.2, 0.95] }),
|
|
44426
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color, emissive })
|
|
44427
|
+
]
|
|
44428
|
+
}
|
|
44429
|
+
);
|
|
44430
|
+
},
|
|
44431
|
+
[selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover]
|
|
44432
|
+
);
|
|
44433
|
+
const DefaultUnitRenderer = useCallback(
|
|
44434
|
+
({ unit, position }) => {
|
|
44435
|
+
const isSelected = selectedUnitId === unit.id;
|
|
44436
|
+
const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
|
|
44437
|
+
return /* @__PURE__ */ jsxs(
|
|
44438
|
+
"group",
|
|
44439
|
+
{
|
|
44440
|
+
position,
|
|
44441
|
+
onClick: (e) => handleUnitClick(unit, e),
|
|
44442
|
+
userData: { type: "unit", unitId: unit.id },
|
|
44443
|
+
children: [
|
|
44444
|
+
isSelected && /* @__PURE__ */ jsxs("mesh", { position: [0, 0.05, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
44445
|
+
/* @__PURE__ */ jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
|
|
44446
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
|
|
44447
|
+
] }),
|
|
44448
|
+
unit.modelUrl ? (
|
|
44449
|
+
/* GLB unit model (box fallback while loading / on error) */
|
|
44450
|
+
/* @__PURE__ */ jsx(
|
|
44451
|
+
ModelLoader,
|
|
44452
|
+
{
|
|
44453
|
+
url: unit.modelUrl,
|
|
44454
|
+
scale: 0.5,
|
|
44455
|
+
fallbackGeometry: "box",
|
|
44456
|
+
castShadow: true
|
|
44457
|
+
}
|
|
44458
|
+
)
|
|
44459
|
+
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
44460
|
+
/* @__PURE__ */ jsxs("mesh", { position: [0, 0.3, 0], children: [
|
|
44461
|
+
/* @__PURE__ */ jsx("cylinderGeometry", { args: [0.3, 0.3, 0.1, 8] }),
|
|
44462
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color })
|
|
44463
|
+
] }),
|
|
44464
|
+
/* @__PURE__ */ jsxs("mesh", { position: [0, 0.6, 0], children: [
|
|
44465
|
+
/* @__PURE__ */ jsx("capsuleGeometry", { args: [0.2, 0.4, 4, 8] }),
|
|
44466
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color })
|
|
44467
|
+
] }),
|
|
44468
|
+
/* @__PURE__ */ jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
44469
|
+
/* @__PURE__ */ jsx("sphereGeometry", { args: [0.12, 8, 8] }),
|
|
44470
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color })
|
|
44471
|
+
] })
|
|
44472
|
+
] }),
|
|
44473
|
+
unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxs("group", { position: [0, 1.2, 0], children: [
|
|
44474
|
+
/* @__PURE__ */ jsxs("mesh", { position: [-0.25, 0, 0], children: [
|
|
44475
|
+
/* @__PURE__ */ jsx("planeGeometry", { args: [0.5, 0.05] }),
|
|
44476
|
+
/* @__PURE__ */ jsx("meshBasicMaterial", { color: 3355443 })
|
|
44477
|
+
] }),
|
|
44478
|
+
/* @__PURE__ */ jsxs(
|
|
44479
|
+
"mesh",
|
|
44480
|
+
{
|
|
44481
|
+
position: [
|
|
44482
|
+
-0.25 + 0.5 * (unit.health / unit.maxHealth) / 2,
|
|
44483
|
+
0,
|
|
44484
|
+
0.01
|
|
44485
|
+
],
|
|
44486
|
+
children: [
|
|
44487
|
+
/* @__PURE__ */ jsx("planeGeometry", { args: [0.5 * (unit.health / unit.maxHealth), 0.05] }),
|
|
44488
|
+
/* @__PURE__ */ jsx(
|
|
44489
|
+
"meshBasicMaterial",
|
|
44490
|
+
{
|
|
44491
|
+
color: unit.health / unit.maxHealth > 0.5 ? 4500036 : unit.health / unit.maxHealth > 0.25 ? 11184708 : 16729156
|
|
44492
|
+
}
|
|
44493
|
+
)
|
|
44494
|
+
]
|
|
44495
|
+
}
|
|
44496
|
+
)
|
|
44497
|
+
] })
|
|
44498
|
+
]
|
|
44499
|
+
}
|
|
44500
|
+
);
|
|
44501
|
+
},
|
|
44502
|
+
[selectedUnitId, handleUnitClick]
|
|
44503
|
+
);
|
|
44504
|
+
const DefaultFeatureRenderer = useCallback(
|
|
44505
|
+
({
|
|
44506
|
+
feature,
|
|
44507
|
+
position
|
|
44508
|
+
}) => {
|
|
44509
|
+
if (feature.assetUrl) {
|
|
44510
|
+
return /* @__PURE__ */ jsx(
|
|
44511
|
+
ModelLoader,
|
|
44512
|
+
{
|
|
44513
|
+
url: feature.assetUrl,
|
|
44514
|
+
position,
|
|
44515
|
+
scale: 0.5,
|
|
44516
|
+
rotation: [0, feature.rotation ?? 0, 0],
|
|
44517
|
+
onClick: () => handleFeatureClick(feature, null),
|
|
44518
|
+
fallbackGeometry: "box"
|
|
44519
|
+
},
|
|
44520
|
+
feature.id
|
|
44521
|
+
);
|
|
44522
|
+
}
|
|
44523
|
+
if (feature.type === "tree") {
|
|
44524
|
+
return /* @__PURE__ */ jsxs(
|
|
44525
|
+
"group",
|
|
44526
|
+
{
|
|
44527
|
+
position,
|
|
44528
|
+
onClick: (e) => handleFeatureClick(feature, e),
|
|
44529
|
+
userData: { type: "feature", featureId: feature.id },
|
|
44530
|
+
children: [
|
|
44531
|
+
/* @__PURE__ */ jsxs("mesh", { position: [0, 0.4, 0], children: [
|
|
44532
|
+
/* @__PURE__ */ jsx("cylinderGeometry", { args: [0.1, 0.15, 0.8, 6] }),
|
|
44533
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: 9127187 })
|
|
44534
|
+
] }),
|
|
44535
|
+
/* @__PURE__ */ jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
44536
|
+
/* @__PURE__ */ jsx("coneGeometry", { args: [0.5, 0.8, 8] }),
|
|
44537
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: 2263842 })
|
|
44538
|
+
] })
|
|
44539
|
+
]
|
|
44540
|
+
}
|
|
44541
|
+
);
|
|
44542
|
+
}
|
|
44543
|
+
if (feature.type === "rock") {
|
|
44544
|
+
return /* @__PURE__ */ jsxs(
|
|
44545
|
+
"mesh",
|
|
44546
|
+
{
|
|
44547
|
+
position: [position[0], position[1] + 0.3, position[2]],
|
|
44548
|
+
onClick: (e) => handleFeatureClick(feature, e),
|
|
44549
|
+
userData: { type: "feature", featureId: feature.id },
|
|
44550
|
+
children: [
|
|
44551
|
+
/* @__PURE__ */ jsx("dodecahedronGeometry", { args: [0.3, 0] }),
|
|
44552
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: 8421504 })
|
|
44553
|
+
]
|
|
44554
|
+
}
|
|
44555
|
+
);
|
|
44556
|
+
}
|
|
44557
|
+
return null;
|
|
44558
|
+
},
|
|
44559
|
+
[handleFeatureClick]
|
|
44560
|
+
);
|
|
44561
|
+
if (externalLoading || assetsLoading && preloadAssets.length > 0) {
|
|
44562
|
+
return /* @__PURE__ */ jsx(
|
|
44563
|
+
Canvas3DLoadingState,
|
|
44564
|
+
{
|
|
44565
|
+
progress,
|
|
44566
|
+
loaded,
|
|
44567
|
+
total,
|
|
44568
|
+
message: loadingMessage,
|
|
44569
|
+
className
|
|
44570
|
+
}
|
|
44571
|
+
);
|
|
44572
|
+
}
|
|
44573
|
+
const displayError = externalError || internalError;
|
|
44574
|
+
if (displayError) {
|
|
44575
|
+
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: [
|
|
44576
|
+
"Error: ",
|
|
44577
|
+
displayError
|
|
44578
|
+
] }) }) });
|
|
44579
|
+
}
|
|
44580
|
+
return /* @__PURE__ */ jsx(
|
|
44581
|
+
Canvas3DErrorBoundary,
|
|
44582
|
+
{
|
|
44583
|
+
onError: (err) => setInternalError(err.message),
|
|
44584
|
+
onReset: () => setInternalError(null),
|
|
44585
|
+
children: /* @__PURE__ */ jsxs(
|
|
44586
|
+
"div",
|
|
44587
|
+
{
|
|
44588
|
+
ref: containerRef,
|
|
44589
|
+
className: cn("game-canvas-3d relative w-full h-full min-h-[85vh] overflow-hidden", className),
|
|
44590
|
+
"data-orientation": orientation,
|
|
44591
|
+
"data-camera-mode": cameraMode,
|
|
44592
|
+
"data-overlay": overlay,
|
|
44593
|
+
children: [
|
|
44594
|
+
/* @__PURE__ */ jsxs(
|
|
44595
|
+
Canvas,
|
|
44596
|
+
{
|
|
44597
|
+
shadows,
|
|
44598
|
+
camera: {
|
|
44599
|
+
position: cameraConfig.position,
|
|
44600
|
+
fov: cameraConfig.fov,
|
|
44601
|
+
near: 0.1,
|
|
44602
|
+
far: 1e3
|
|
44603
|
+
},
|
|
44604
|
+
style: { background: backgroundColor },
|
|
44605
|
+
onClick: (e) => {
|
|
44606
|
+
if (e.target === e.currentTarget) {
|
|
44607
|
+
eventHandlers.handleCanvasClick(e);
|
|
44608
|
+
}
|
|
44609
|
+
},
|
|
44610
|
+
children: [
|
|
44611
|
+
/* @__PURE__ */ jsx(CameraController, { onCameraChange: eventHandlers.handleCameraChange }),
|
|
44612
|
+
/* @__PURE__ */ jsx("ambientLight", { intensity: 0.6 }),
|
|
44613
|
+
/* @__PURE__ */ jsx(
|
|
44614
|
+
"directionalLight",
|
|
44615
|
+
{
|
|
44616
|
+
position: [10, 20, 10],
|
|
44617
|
+
intensity: 0.8,
|
|
44618
|
+
castShadow: shadows,
|
|
44619
|
+
"shadow-mapSize": [2048, 2048]
|
|
44620
|
+
}
|
|
44621
|
+
),
|
|
44622
|
+
/* @__PURE__ */ jsx("hemisphereLight", { intensity: 0.3, color: "#87ceeb", groundColor: "#362d1d" }),
|
|
44623
|
+
showGrid && /* @__PURE__ */ jsx(
|
|
44624
|
+
Grid$1,
|
|
44625
|
+
{
|
|
44626
|
+
args: [
|
|
44627
|
+
Math.max(gridBounds.maxX - gridBounds.minX + 2, 10),
|
|
44628
|
+
Math.max(gridBounds.maxZ - gridBounds.minZ + 2, 10)
|
|
44629
|
+
],
|
|
44630
|
+
position: [
|
|
44631
|
+
(gridBounds.maxX - gridBounds.minX) / 2 - 0.5,
|
|
44632
|
+
0,
|
|
44633
|
+
(gridBounds.maxZ - gridBounds.minZ) / 2 - 0.5
|
|
44634
|
+
],
|
|
44635
|
+
cellSize: 1,
|
|
44636
|
+
cellThickness: 1,
|
|
44637
|
+
cellColor: "#444444",
|
|
44638
|
+
sectionSize: 5,
|
|
44639
|
+
sectionThickness: 1.5,
|
|
44640
|
+
sectionColor: "#666666",
|
|
44641
|
+
fadeDistance: 50,
|
|
44642
|
+
fadeStrength: 1
|
|
44643
|
+
}
|
|
44644
|
+
),
|
|
44645
|
+
tiles.map((tile, index) => {
|
|
44646
|
+
const position = gridToWorld(
|
|
44647
|
+
tile.x,
|
|
44648
|
+
tile.z ?? tile.y ?? 0,
|
|
44649
|
+
tile.elevation ?? 0
|
|
44650
|
+
);
|
|
44651
|
+
const Renderer = CustomTileRenderer || DefaultTileRenderer;
|
|
44652
|
+
return /* @__PURE__ */ jsx(Renderer, { tile, position }, tile.id ?? `tile-${index}`);
|
|
44653
|
+
}),
|
|
44654
|
+
features.map((feature, index) => {
|
|
44655
|
+
const position = gridToWorld(
|
|
44656
|
+
feature.x,
|
|
44657
|
+
feature.z ?? feature.y ?? 0,
|
|
44658
|
+
(feature.elevation ?? 0) + 0.5
|
|
44659
|
+
);
|
|
44660
|
+
const Renderer = CustomFeatureRenderer || DefaultFeatureRenderer;
|
|
44661
|
+
return /* @__PURE__ */ jsx(Renderer, { feature, position }, feature.id ?? `feature-${index}`);
|
|
44662
|
+
}),
|
|
44663
|
+
units.map((unit) => {
|
|
44664
|
+
const position = gridToWorld(
|
|
44665
|
+
unit.x ?? 0,
|
|
44666
|
+
unit.z ?? unit.y ?? 0,
|
|
44667
|
+
(unit.elevation ?? 0) + 0.5
|
|
44668
|
+
);
|
|
44669
|
+
const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
|
|
44670
|
+
return /* @__PURE__ */ jsx(Renderer, { unit, position }, unit.id);
|
|
44671
|
+
}),
|
|
44672
|
+
children,
|
|
44673
|
+
/* @__PURE__ */ jsx(
|
|
44674
|
+
OrbitControls,
|
|
44675
|
+
{
|
|
44676
|
+
ref: controlsRef,
|
|
44677
|
+
target: cameraTarget,
|
|
44678
|
+
enableDamping: true,
|
|
44679
|
+
dampingFactor: 0.05,
|
|
44680
|
+
minDistance: 2,
|
|
44681
|
+
maxDistance: 100,
|
|
44682
|
+
maxPolarAngle: Math.PI / 2 - 0.1
|
|
44683
|
+
}
|
|
44684
|
+
)
|
|
44685
|
+
]
|
|
44686
|
+
}
|
|
44687
|
+
),
|
|
44688
|
+
showCoordinates && hoveredTile && /* @__PURE__ */ jsxs("div", { className: "game-canvas-3d__coordinates", children: [
|
|
44689
|
+
"X: ",
|
|
44690
|
+
hoveredTile.x,
|
|
44691
|
+
", Z: ",
|
|
44692
|
+
hoveredTile.z ?? hoveredTile.y ?? 0
|
|
44693
|
+
] }),
|
|
44694
|
+
showTileInfo && hoveredTile && /* @__PURE__ */ jsxs("div", { className: "game-canvas-3d__tile-info", children: [
|
|
44695
|
+
/* @__PURE__ */ jsx("div", { className: "tile-info__type", children: hoveredTile.type }),
|
|
44696
|
+
hoveredTile.terrain && /* @__PURE__ */ jsx("div", { className: "tile-info__terrain", children: hoveredTile.terrain })
|
|
44697
|
+
] })
|
|
44698
|
+
]
|
|
44699
|
+
}
|
|
44700
|
+
)
|
|
44701
|
+
}
|
|
44702
|
+
);
|
|
44703
|
+
}
|
|
44704
|
+
);
|
|
44705
|
+
GameCanvas3D.displayName = "GameCanvas3D";
|
|
44706
|
+
}
|
|
44707
|
+
});
|
|
44708
|
+
function GameBoard3D({
|
|
44709
|
+
entity,
|
|
44710
|
+
tiles = [],
|
|
44711
|
+
features = [],
|
|
44712
|
+
cameraMode = "perspective",
|
|
44713
|
+
backgroundColor = "#2a1a1a",
|
|
44714
|
+
tileClickEvent,
|
|
44715
|
+
unitClickEvent,
|
|
44716
|
+
attackEvent,
|
|
44717
|
+
endTurnEvent,
|
|
44718
|
+
cancelEvent,
|
|
44719
|
+
playAgainEvent,
|
|
44720
|
+
gameEndEvent,
|
|
44721
|
+
className
|
|
44722
|
+
}) {
|
|
44723
|
+
const row = boardEntity(entity);
|
|
44724
|
+
const eventBus = useEventBus();
|
|
44725
|
+
const entityUnits = row ? rows(row.units) : [];
|
|
44726
|
+
const units = entityUnits;
|
|
44727
|
+
const selectedUnitId = row ? str(row.selectedUnitId) || null : null;
|
|
44728
|
+
const phase = row ? str(row.phase) : "observation";
|
|
44729
|
+
const result = row ? str(row.result) : "none";
|
|
44730
|
+
const validMoves = row && Array.isArray(row.validMoves) ? row.validMoves : [];
|
|
44731
|
+
const attackTargets = row && Array.isArray(row.attackTargets) ? row.attackTargets : [];
|
|
44732
|
+
const turn = row ? num(row.turn) : 0;
|
|
44733
|
+
const currentTeam = row ? str(row.currentTeam) : "player";
|
|
44734
|
+
const isGameOver = result !== "none";
|
|
44735
|
+
const gameEndEmittedRef = useRef(false);
|
|
44736
|
+
useEffect(() => {
|
|
44737
|
+
if ((result === "victory" || result === "defeat") && gameEndEvent) {
|
|
44738
|
+
if (!gameEndEmittedRef.current) {
|
|
44739
|
+
gameEndEmittedRef.current = true;
|
|
44740
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
44741
|
+
}
|
|
44742
|
+
} else {
|
|
44743
|
+
gameEndEmittedRef.current = false;
|
|
44744
|
+
}
|
|
44745
|
+
}, [result, gameEndEvent, eventBus]);
|
|
44746
|
+
const checkGameEnd = useCallback(() => {
|
|
44747
|
+
const alivePlayer = entityUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
|
|
44748
|
+
const aliveEnemy = entityUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
|
|
44749
|
+
if (alivePlayer.length === 0 && gameEndEvent) {
|
|
44750
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result: "defeat" });
|
|
44751
|
+
} else if (aliveEnemy.length === 0 && gameEndEvent) {
|
|
44752
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result: "victory" });
|
|
44753
|
+
}
|
|
44754
|
+
}, [entityUnits, gameEndEvent, eventBus]);
|
|
44755
|
+
const handleUnitClickCallback = useCallback((isoUnit) => {
|
|
44756
|
+
if (phase !== "action" || !selectedUnitId || !attackEvent) return;
|
|
44757
|
+
const attackerRow = entityUnits.find((u) => str(u.id) === selectedUnitId);
|
|
44758
|
+
const targetRow = entityUnits.find((u) => str(u.id) === isoUnit.id);
|
|
44759
|
+
if (!attackerRow || !targetRow) return;
|
|
44760
|
+
if (unitTeam(targetRow) !== "enemy" || unitHealth(targetRow) <= 0) return;
|
|
44761
|
+
const ap = unitPosition(attackerRow);
|
|
44762
|
+
const tp = unitPosition(targetRow);
|
|
44763
|
+
const dx = Math.abs(ap.x - tp.x);
|
|
44764
|
+
const dy = Math.abs(ap.y - tp.y);
|
|
44765
|
+
if (dx <= 1 && dy <= 1 && dx + dy > 0) {
|
|
44766
|
+
const damage = Math.max(1, num(attackerRow.attack) - num(targetRow.defense));
|
|
44767
|
+
eventBus.emit(`UI:${attackEvent}`, {
|
|
44768
|
+
attackerId: str(attackerRow.id),
|
|
44769
|
+
targetId: str(targetRow.id),
|
|
44770
|
+
damage
|
|
44771
|
+
});
|
|
44772
|
+
setTimeout(checkGameEnd, 100);
|
|
44773
|
+
}
|
|
44774
|
+
}, [phase, selectedUnitId, entityUnits, attackEvent, eventBus, checkGameEnd]);
|
|
44775
|
+
const handleEndTurn = useCallback(() => {
|
|
44776
|
+
if (endTurnEvent) eventBus.emit(`UI:${endTurnEvent}`, {});
|
|
44777
|
+
}, [endTurnEvent, eventBus]);
|
|
44778
|
+
const handleCancel = useCallback(() => {
|
|
44779
|
+
if (cancelEvent) eventBus.emit(`UI:${cancelEvent}`, {});
|
|
44780
|
+
}, [cancelEvent, eventBus]);
|
|
44781
|
+
const handlePlayAgain = useCallback(() => {
|
|
44782
|
+
if (playAgainEvent) eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
44783
|
+
}, [playAgainEvent, eventBus]);
|
|
44784
|
+
return /* @__PURE__ */ jsxs(VStack, { className: cn("game-board-3d block w-full min-h-[85vh] relative", className), children: [
|
|
44785
|
+
/* @__PURE__ */ jsxs("div", { className: "game-board-3d__status absolute top-3 left-3 z-10 flex gap-2 items-center", children: [
|
|
44786
|
+
/* @__PURE__ */ jsx(Typography, { variant: "small", className: "status__phase capitalize", children: phase.replace("_", " ") }),
|
|
44787
|
+
currentTeam && !isGameOver && /* @__PURE__ */ jsxs(Typography, { variant: "small", color: "muted", className: "status__team", children: [
|
|
44788
|
+
"\u2014 ",
|
|
44789
|
+
currentTeam === "player" ? "Your Turn" : "Enemy's Turn"
|
|
44790
|
+
] }),
|
|
44791
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "small", color: "muted", className: "status__turn", children: [
|
|
44792
|
+
"Turn ",
|
|
44793
|
+
turn
|
|
44794
|
+
] })
|
|
44795
|
+
] }),
|
|
44796
|
+
/* @__PURE__ */ jsx(
|
|
44797
|
+
GameCanvas3D,
|
|
44798
|
+
{
|
|
44799
|
+
tiles,
|
|
44800
|
+
units,
|
|
44801
|
+
features,
|
|
44802
|
+
cameraMode,
|
|
44803
|
+
showGrid: true,
|
|
44804
|
+
showCoordinates: false,
|
|
44805
|
+
showTileInfo: false,
|
|
44806
|
+
shadows: true,
|
|
44807
|
+
backgroundColor,
|
|
44808
|
+
tileClickEvent,
|
|
44809
|
+
unitClickEvent,
|
|
44810
|
+
onUnitClick: handleUnitClickCallback,
|
|
44811
|
+
selectedUnitId,
|
|
44812
|
+
validMoves,
|
|
44813
|
+
attackTargets,
|
|
44814
|
+
className: "game-board-3d__canvas w-full min-h-[85vh]"
|
|
44815
|
+
}
|
|
44816
|
+
),
|
|
44817
|
+
!isGameOver && /* @__PURE__ */ jsxs(HStack, { className: "fixed bottom-6 right-6 z-50", gap: "sm", children: [
|
|
44818
|
+
(phase === "selection" || phase === "movement" || phase === "action") && /* @__PURE__ */ jsx(
|
|
44819
|
+
Button,
|
|
44820
|
+
{
|
|
44821
|
+
variant: "secondary",
|
|
44822
|
+
className: "shadow-xl",
|
|
44823
|
+
onClick: handleCancel,
|
|
44824
|
+
children: "Cancel"
|
|
44825
|
+
}
|
|
44826
|
+
),
|
|
44827
|
+
phase !== "enemy_turn" && /* @__PURE__ */ jsx(
|
|
44828
|
+
Button,
|
|
44829
|
+
{
|
|
44830
|
+
variant: "primary",
|
|
44831
|
+
className: "shadow-xl",
|
|
44832
|
+
onClick: handleEndTurn,
|
|
44833
|
+
children: "End Turn"
|
|
44834
|
+
}
|
|
44835
|
+
)
|
|
44836
|
+
] }),
|
|
44837
|
+
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: [
|
|
44838
|
+
/* @__PURE__ */ jsx(
|
|
44839
|
+
Typography,
|
|
44840
|
+
{
|
|
44841
|
+
variant: "h2",
|
|
44842
|
+
className: cn(
|
|
44843
|
+
"overlay__result",
|
|
44844
|
+
result === "victory" ? "text-yellow-400" : "text-red-500"
|
|
44845
|
+
),
|
|
44846
|
+
children: result === "victory" ? "Victory!" : "Defeat"
|
|
44847
|
+
}
|
|
44848
|
+
),
|
|
44849
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "body", color: "muted", className: "overlay__turn-count", children: [
|
|
44850
|
+
"Completed in ",
|
|
44851
|
+
turn,
|
|
44852
|
+
" turn",
|
|
44853
|
+
turn !== 1 ? "s" : ""
|
|
44854
|
+
] }),
|
|
44855
|
+
/* @__PURE__ */ jsx(
|
|
44856
|
+
Button,
|
|
44857
|
+
{
|
|
44858
|
+
variant: "primary",
|
|
44859
|
+
className: "px-8 py-3 font-semibold",
|
|
44860
|
+
onClick: handlePlayAgain,
|
|
44861
|
+
children: "Play Again"
|
|
44862
|
+
}
|
|
44863
|
+
)
|
|
44864
|
+
] }) })
|
|
44865
|
+
] });
|
|
44866
|
+
}
|
|
44867
|
+
var init_GameBoard3D = __esm({
|
|
44868
|
+
"components/game/organisms/GameBoard3D.tsx"() {
|
|
44869
|
+
"use client";
|
|
44870
|
+
init_cn();
|
|
44871
|
+
init_Button();
|
|
44872
|
+
init_Typography();
|
|
44873
|
+
init_Stack();
|
|
44874
|
+
init_useEventBus();
|
|
44875
|
+
init_GameCanvas3D2();
|
|
44876
|
+
init_boardEntity();
|
|
44877
|
+
GameBoard3D.displayName = "GameBoard3D";
|
|
44878
|
+
}
|
|
44879
|
+
});
|
|
43177
44880
|
var GameShell;
|
|
43178
44881
|
var init_GameShell = __esm({
|
|
43179
44882
|
"components/game/templates/GameShell.tsx"() {
|
|
@@ -44542,21 +46245,6 @@ var init_ModalSlot = __esm({
|
|
|
44542
46245
|
ModalSlot.displayName = "ModalSlot";
|
|
44543
46246
|
}
|
|
44544
46247
|
});
|
|
44545
|
-
function getOpponentAction(strategy, actions, history) {
|
|
44546
|
-
const actionIds = actions.map((a) => a.id);
|
|
44547
|
-
switch (strategy) {
|
|
44548
|
-
case "always-cooperate":
|
|
44549
|
-
return actionIds[0];
|
|
44550
|
-
case "always-defect":
|
|
44551
|
-
return actionIds[actionIds.length - 1];
|
|
44552
|
-
case "tit-for-tat":
|
|
44553
|
-
if (history.length === 0) return actionIds[0];
|
|
44554
|
-
return history[history.length - 1].playerAction;
|
|
44555
|
-
case "random":
|
|
44556
|
-
default:
|
|
44557
|
-
return actionIds[Math.floor(Math.random() * actionIds.length)];
|
|
44558
|
-
}
|
|
44559
|
-
}
|
|
44560
46248
|
function NegotiatorBoard({
|
|
44561
46249
|
entity,
|
|
44562
46250
|
completeEvent = "PUZZLE_COMPLETE",
|
|
@@ -44578,28 +46266,35 @@ function NegotiatorBoard({
|
|
|
44578
46266
|
const playerTotal = num(resolved?.score);
|
|
44579
46267
|
const isComplete = result !== "none" || totalRounds > 0 && currentRound >= totalRounds;
|
|
44580
46268
|
const won = result === "win";
|
|
44581
|
-
const
|
|
46269
|
+
const lastPlayerAction = str(resolved?.lastPlayerAction);
|
|
46270
|
+
const lastOpponentAction = str(resolved?.lastOpponentAction);
|
|
46271
|
+
const lastPayoff = num(resolved?.lastPayoff);
|
|
44582
46272
|
const actions = Array.isArray(resolved?.actions) ? resolved.actions : [];
|
|
44583
46273
|
const payoffMatrix = Array.isArray(resolved?.payoffMatrix) ? resolved.payoffMatrix : [];
|
|
46274
|
+
const prevRoundRef = useRef(currentRound);
|
|
46275
|
+
useEffect(() => {
|
|
46276
|
+
if (currentRound > prevRoundRef.current && lastPlayerAction) {
|
|
46277
|
+
const opponentPayoffEntry = payoffMatrix.find(
|
|
46278
|
+
(p2) => p2.playerAction === lastPlayerAction && p2.opponentAction === lastOpponentAction
|
|
46279
|
+
);
|
|
46280
|
+
setHistory((prev) => [
|
|
46281
|
+
...prev,
|
|
46282
|
+
{
|
|
46283
|
+
round: currentRound,
|
|
46284
|
+
playerAction: lastPlayerAction,
|
|
46285
|
+
opponentAction: lastOpponentAction,
|
|
46286
|
+
playerPayoff: lastPayoff,
|
|
46287
|
+
opponentPayoff: opponentPayoffEntry?.opponentPayoff ?? 0
|
|
46288
|
+
}
|
|
46289
|
+
]);
|
|
46290
|
+
}
|
|
46291
|
+
prevRoundRef.current = currentRound;
|
|
46292
|
+
}, [currentRound, lastPlayerAction, lastOpponentAction, lastPayoff, payoffMatrix]);
|
|
46293
|
+
const opponentTotal = useMemo(() => history.reduce((s, r2) => s + r2.opponentPayoff, 0), [history]);
|
|
44584
46294
|
const handleAction = useCallback((actionId) => {
|
|
44585
46295
|
if (isComplete) return;
|
|
44586
|
-
const opponentAction = getOpponentAction(str(resolved?.opponentStrategy) || "random", actions, history);
|
|
44587
|
-
const payoff = payoffMatrix.find(
|
|
44588
|
-
(p2) => p2.playerAction === actionId && p2.opponentAction === opponentAction
|
|
44589
|
-
);
|
|
44590
|
-
const playerPayoff = payoff?.playerPayoff ?? 0;
|
|
44591
|
-
setHistory((prev) => [
|
|
44592
|
-
...prev,
|
|
44593
|
-
{
|
|
44594
|
-
round: prev.length + 1,
|
|
44595
|
-
playerAction: actionId,
|
|
44596
|
-
opponentAction,
|
|
44597
|
-
playerPayoff,
|
|
44598
|
-
opponentPayoff: payoff?.opponentPayoff ?? 0
|
|
44599
|
-
}
|
|
44600
|
-
]);
|
|
44601
46296
|
if (playRoundEvent) {
|
|
44602
|
-
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff:
|
|
46297
|
+
emit(`UI:${playRoundEvent}`, { playerAction: actionId, payoff: 0 });
|
|
44603
46298
|
}
|
|
44604
46299
|
if (totalRounds > 0 && currentRound + 1 >= totalRounds) {
|
|
44605
46300
|
if (finishEvent) {
|
|
@@ -44609,10 +46304,11 @@ function NegotiatorBoard({
|
|
|
44609
46304
|
setShowHint(true);
|
|
44610
46305
|
}
|
|
44611
46306
|
}
|
|
44612
|
-
}, [isComplete, resolved, totalRounds, currentRound,
|
|
46307
|
+
}, [isComplete, resolved, totalRounds, currentRound, playRoundEvent, finishEvent, emit]);
|
|
44613
46308
|
const handleReset = useCallback(() => {
|
|
44614
46309
|
setHistory([]);
|
|
44615
46310
|
setShowHint(false);
|
|
46311
|
+
prevRoundRef.current = 0;
|
|
44616
46312
|
if (playAgainEvent) {
|
|
44617
46313
|
emit(`UI:${playAgainEvent}`, {});
|
|
44618
46314
|
}
|
|
@@ -44723,6 +46419,236 @@ var init_NegotiatorBoard = __esm({
|
|
|
44723
46419
|
NegotiatorBoard.displayName = "NegotiatorBoard";
|
|
44724
46420
|
}
|
|
44725
46421
|
});
|
|
46422
|
+
function resolveEntity(entity) {
|
|
46423
|
+
if (!entity) return {};
|
|
46424
|
+
if (Array.isArray(entity)) return entity[0] ?? {};
|
|
46425
|
+
return entity;
|
|
46426
|
+
}
|
|
46427
|
+
function numField(row, key, fallback) {
|
|
46428
|
+
const v = row[key];
|
|
46429
|
+
return typeof v === "number" ? v : fallback;
|
|
46430
|
+
}
|
|
46431
|
+
function strField(row, key, fallback) {
|
|
46432
|
+
const v = row[key];
|
|
46433
|
+
return typeof v === "string" ? v : fallback;
|
|
46434
|
+
}
|
|
46435
|
+
function PlatformerBoard({
|
|
46436
|
+
entity,
|
|
46437
|
+
player: propPlayer,
|
|
46438
|
+
platforms: propPlatforms,
|
|
46439
|
+
result: propResult,
|
|
46440
|
+
lives: propLives,
|
|
46441
|
+
score: propScore,
|
|
46442
|
+
level: propLevel,
|
|
46443
|
+
worldWidth = 800,
|
|
46444
|
+
worldHeight = 400,
|
|
46445
|
+
canvasWidth = 800,
|
|
46446
|
+
canvasHeight = 400,
|
|
46447
|
+
playerSprite,
|
|
46448
|
+
tileSprites,
|
|
46449
|
+
bgColor,
|
|
46450
|
+
leftEvent = "LEFT",
|
|
46451
|
+
rightEvent = "RIGHT",
|
|
46452
|
+
jumpEvent = "JUMP",
|
|
46453
|
+
stopEvent = "STOP",
|
|
46454
|
+
playAgainEvent = "PLAY_AGAIN",
|
|
46455
|
+
gameEndEvent = "GAME_END",
|
|
46456
|
+
className
|
|
46457
|
+
}) {
|
|
46458
|
+
const row = resolveEntity(entity);
|
|
46459
|
+
const eventBus = useEventBus();
|
|
46460
|
+
const { t } = useTranslate();
|
|
46461
|
+
const result = propResult ?? strField(row, "result", "none");
|
|
46462
|
+
propLives ?? numField(row, "lives", 3);
|
|
46463
|
+
const score = propScore ?? numField(row, "score", 0);
|
|
46464
|
+
const level = propLevel ?? numField(row, "level", 1);
|
|
46465
|
+
const entityPlayer = row["player"];
|
|
46466
|
+
const player = propPlayer ?? entityPlayer ?? {
|
|
46467
|
+
x: 80,
|
|
46468
|
+
y: 320,
|
|
46469
|
+
width: 32,
|
|
46470
|
+
height: 48,
|
|
46471
|
+
vx: 0,
|
|
46472
|
+
vy: 0,
|
|
46473
|
+
grounded: false,
|
|
46474
|
+
facingRight: true
|
|
46475
|
+
};
|
|
46476
|
+
const entityPlatforms = Array.isArray(row["platforms"]) ? row["platforms"] : void 0;
|
|
46477
|
+
const platforms = propPlatforms ?? entityPlatforms ?? [
|
|
46478
|
+
{ x: 0, y: 368, width: 800, height: 32, type: "ground" },
|
|
46479
|
+
{ x: 150, y: 280, width: 160, height: 16, type: "platform" },
|
|
46480
|
+
{ x: 420, y: 220, width: 160, height: 16, type: "platform" },
|
|
46481
|
+
{ x: 580, y: 300, width: 80, height: 16, type: "hazard" },
|
|
46482
|
+
{ x: 700, y: 340, width: 64, height: 28, type: "goal" }
|
|
46483
|
+
];
|
|
46484
|
+
const handleRestart = useCallback(() => {
|
|
46485
|
+
if (playAgainEvent) {
|
|
46486
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
46487
|
+
}
|
|
46488
|
+
}, [playAgainEvent, eventBus]);
|
|
46489
|
+
const isGameOver = result === "won" || result === "lost";
|
|
46490
|
+
const gameEndEmittedRef = useRef(false);
|
|
46491
|
+
useEffect(() => {
|
|
46492
|
+
if ((result === "won" || result === "lost") && gameEndEvent) {
|
|
46493
|
+
if (!gameEndEmittedRef.current) {
|
|
46494
|
+
gameEndEmittedRef.current = true;
|
|
46495
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
46496
|
+
}
|
|
46497
|
+
} else {
|
|
46498
|
+
gameEndEmittedRef.current = false;
|
|
46499
|
+
}
|
|
46500
|
+
}, [result, gameEndEvent, eventBus]);
|
|
46501
|
+
return /* @__PURE__ */ jsx(
|
|
46502
|
+
VStack,
|
|
46503
|
+
{
|
|
46504
|
+
className: cn("platformer-board relative bg-background", className),
|
|
46505
|
+
gap: "none",
|
|
46506
|
+
children: /* @__PURE__ */ jsxs(Box, { className: "relative", children: [
|
|
46507
|
+
/* @__PURE__ */ jsx(
|
|
46508
|
+
PlatformerCanvas,
|
|
46509
|
+
{
|
|
46510
|
+
player,
|
|
46511
|
+
platforms,
|
|
46512
|
+
worldWidth,
|
|
46513
|
+
worldHeight,
|
|
46514
|
+
canvasWidth,
|
|
46515
|
+
canvasHeight,
|
|
46516
|
+
followCamera: true,
|
|
46517
|
+
bgColor,
|
|
46518
|
+
playerSprite,
|
|
46519
|
+
tileSprites,
|
|
46520
|
+
leftEvent,
|
|
46521
|
+
rightEvent,
|
|
46522
|
+
jumpEvent,
|
|
46523
|
+
stopEvent
|
|
46524
|
+
}
|
|
46525
|
+
),
|
|
46526
|
+
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: [
|
|
46527
|
+
/* @__PURE__ */ jsx(
|
|
46528
|
+
Typography,
|
|
46529
|
+
{
|
|
46530
|
+
variant: "h2",
|
|
46531
|
+
className: cn(
|
|
46532
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
46533
|
+
result === "won" ? "text-warning" : "text-error"
|
|
46534
|
+
),
|
|
46535
|
+
children: result === "won" ? t("platformer.won") : t("platformer.lost")
|
|
46536
|
+
}
|
|
46537
|
+
),
|
|
46538
|
+
result === "won" && /* @__PURE__ */ jsxs(Typography, { variant: "body1", className: "text-muted-foreground", children: [
|
|
46539
|
+
t("platformer.level"),
|
|
46540
|
+
" ",
|
|
46541
|
+
level,
|
|
46542
|
+
" \u2014 ",
|
|
46543
|
+
t("platformer.score"),
|
|
46544
|
+
": ",
|
|
46545
|
+
score
|
|
46546
|
+
] }),
|
|
46547
|
+
result === "lost" && /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: t("platformer.noLives") }),
|
|
46548
|
+
/* @__PURE__ */ jsx(
|
|
46549
|
+
Button,
|
|
46550
|
+
{
|
|
46551
|
+
variant: "primary",
|
|
46552
|
+
className: "px-8 py-3 font-semibold",
|
|
46553
|
+
onClick: handleRestart,
|
|
46554
|
+
children: result === "won" ? t("platformer.nextLevel") : t("platformer.playAgain")
|
|
46555
|
+
}
|
|
46556
|
+
)
|
|
46557
|
+
] }) })
|
|
46558
|
+
] })
|
|
46559
|
+
}
|
|
46560
|
+
);
|
|
46561
|
+
}
|
|
46562
|
+
var init_PlatformerBoard = __esm({
|
|
46563
|
+
"components/game/organisms/PlatformerBoard.tsx"() {
|
|
46564
|
+
"use client";
|
|
46565
|
+
init_cn();
|
|
46566
|
+
init_useEventBus();
|
|
46567
|
+
init_Box();
|
|
46568
|
+
init_Button();
|
|
46569
|
+
init_Typography();
|
|
46570
|
+
init_Stack();
|
|
46571
|
+
init_PlatformerCanvas();
|
|
46572
|
+
PlatformerBoard.displayName = "PlatformerBoard";
|
|
46573
|
+
}
|
|
46574
|
+
});
|
|
46575
|
+
function PlatformerTemplate({
|
|
46576
|
+
entity,
|
|
46577
|
+
platforms = DEFAULT_PLATFORMS,
|
|
46578
|
+
worldWidth = 800,
|
|
46579
|
+
worldHeight = 400,
|
|
46580
|
+
canvasWidth = 800,
|
|
46581
|
+
canvasHeight = 400,
|
|
46582
|
+
playerSprite = `${CDN3}/characters/platformChar_idle.png`,
|
|
46583
|
+
tileSprites = DEFAULT_TILE_SPRITES,
|
|
46584
|
+
bgColor = "#5c94fc",
|
|
46585
|
+
gameEndEvent = "GAME_END",
|
|
46586
|
+
className
|
|
46587
|
+
}) {
|
|
46588
|
+
const row = entity && !Array.isArray(entity) ? entity : void 0;
|
|
46589
|
+
const lives = typeof row?.["lives"] === "number" ? row["lives"] : 3;
|
|
46590
|
+
const score = typeof row?.["score"] === "number" ? row["score"] : 0;
|
|
46591
|
+
const level = typeof row?.["level"] === "number" ? row["level"] : 1;
|
|
46592
|
+
return /* @__PURE__ */ jsxs(VStack, { className: cn("platformer-template w-full", className), gap: "none", children: [
|
|
46593
|
+
/* @__PURE__ */ jsx(Box, { className: "relative", children: /* @__PURE__ */ jsx(
|
|
46594
|
+
GameHud,
|
|
46595
|
+
{
|
|
46596
|
+
position: "top",
|
|
46597
|
+
stats: [
|
|
46598
|
+
{ label: "Lives", value: lives, format: "number", variant: "danger" },
|
|
46599
|
+
{ label: "Level", value: level, format: "number", variant: "default" }
|
|
46600
|
+
]
|
|
46601
|
+
}
|
|
46602
|
+
) }),
|
|
46603
|
+
/* @__PURE__ */ jsx(
|
|
46604
|
+
PlatformerBoard,
|
|
46605
|
+
{
|
|
46606
|
+
entity,
|
|
46607
|
+
platforms,
|
|
46608
|
+
worldWidth,
|
|
46609
|
+
worldHeight,
|
|
46610
|
+
canvasWidth,
|
|
46611
|
+
canvasHeight,
|
|
46612
|
+
playerSprite,
|
|
46613
|
+
tileSprites,
|
|
46614
|
+
bgColor,
|
|
46615
|
+
leftEvent: "LEFT",
|
|
46616
|
+
rightEvent: "RIGHT",
|
|
46617
|
+
jumpEvent: "JUMP",
|
|
46618
|
+
stopEvent: "STOP",
|
|
46619
|
+
playAgainEvent: "PLAY_AGAIN",
|
|
46620
|
+
gameEndEvent
|
|
46621
|
+
}
|
|
46622
|
+
),
|
|
46623
|
+
/* @__PURE__ */ jsx(Box, { className: "px-4 py-2", children: /* @__PURE__ */ jsx(ScoreBoard, { score, level }) })
|
|
46624
|
+
] });
|
|
46625
|
+
}
|
|
46626
|
+
var CDN3, DEFAULT_TILE_SPRITES, DEFAULT_PLATFORMS;
|
|
46627
|
+
var init_PlatformerTemplate = __esm({
|
|
46628
|
+
"components/game/templates/PlatformerTemplate.tsx"() {
|
|
46629
|
+
init_cn();
|
|
46630
|
+
init_Box();
|
|
46631
|
+
init_Stack();
|
|
46632
|
+
init_PlatformerBoard();
|
|
46633
|
+
init_GameHud();
|
|
46634
|
+
init_ScoreBoard();
|
|
46635
|
+
CDN3 = "https://almadar-kflow-assets.web.app/shared/platformer";
|
|
46636
|
+
DEFAULT_TILE_SPRITES = {
|
|
46637
|
+
ground: `${CDN3}/tiles/platformPack_tile001.png`,
|
|
46638
|
+
platform: `${CDN3}/tiles/platformPack_tile004.png`,
|
|
46639
|
+
hazard: `${CDN3}/tiles/platformPack_tile017.png`,
|
|
46640
|
+
goal: `${CDN3}/tiles/platformPack_tile007.png`
|
|
46641
|
+
};
|
|
46642
|
+
DEFAULT_PLATFORMS = [
|
|
46643
|
+
{ x: 0, y: 368, width: 800, height: 32, type: "ground" },
|
|
46644
|
+
{ x: 150, y: 280, width: 160, height: 16, type: "platform" },
|
|
46645
|
+
{ x: 420, y: 220, width: 160, height: 16, type: "platform" },
|
|
46646
|
+
{ x: 580, y: 300, width: 80, height: 16, type: "hazard" },
|
|
46647
|
+
{ x: 700, y: 340, width: 64, height: 28, type: "goal" }
|
|
46648
|
+
];
|
|
46649
|
+
PlatformerTemplate.displayName = "PlatformerTemplate";
|
|
46650
|
+
}
|
|
46651
|
+
});
|
|
44726
46652
|
var PricingOrganism;
|
|
44727
46653
|
var init_PricingOrganism = __esm({
|
|
44728
46654
|
"components/marketing/organisms/PricingOrganism.tsx"() {
|
|
@@ -44845,6 +46771,356 @@ var init_PricingPageTemplate = __esm({
|
|
|
44845
46771
|
PricingPageTemplate.displayName = "PricingPageTemplate";
|
|
44846
46772
|
}
|
|
44847
46773
|
});
|
|
46774
|
+
function RoguelikeBoard({
|
|
46775
|
+
entity,
|
|
46776
|
+
tiles: propTiles,
|
|
46777
|
+
enemies: propEnemies,
|
|
46778
|
+
items: propItems,
|
|
46779
|
+
player: propPlayer,
|
|
46780
|
+
playerHp: propPlayerHp,
|
|
46781
|
+
playerMaxHp: propPlayerMaxHp,
|
|
46782
|
+
playerAttack: propPlayerAttack,
|
|
46783
|
+
depth: propDepth,
|
|
46784
|
+
result: propResult,
|
|
46785
|
+
phase: propPhase,
|
|
46786
|
+
stairsX: propStairsX,
|
|
46787
|
+
stairsY: propStairsY,
|
|
46788
|
+
assetManifest: propAssetManifest,
|
|
46789
|
+
scale = 0.45,
|
|
46790
|
+
unitScale = 1,
|
|
46791
|
+
moveEvent,
|
|
46792
|
+
playAgainEvent,
|
|
46793
|
+
gameEndEvent,
|
|
46794
|
+
className
|
|
46795
|
+
}) {
|
|
46796
|
+
const board = boardEntity(entity) ?? {};
|
|
46797
|
+
const tiles = propTiles ?? (Array.isArray(board.tiles) ? board.tiles : DEFAULT_TILES2);
|
|
46798
|
+
const enemies = propEnemies ?? (Array.isArray(board.enemies) ? board.enemies : DEFAULT_ENEMIES);
|
|
46799
|
+
const items = propItems ?? (Array.isArray(board.items) ? board.items : DEFAULT_ITEMS);
|
|
46800
|
+
const player = propPlayer ?? board.player ?? { x: 1, y: 1 };
|
|
46801
|
+
const playerHp = propPlayerHp ?? num(board.playerHp, 10);
|
|
46802
|
+
const playerMaxHp = propPlayerMaxHp ?? num(board.playerMaxHp, 10);
|
|
46803
|
+
const playerAttack = propPlayerAttack ?? num(board.playerAttack, 3);
|
|
46804
|
+
const depth = propDepth ?? num(board.depth, 1);
|
|
46805
|
+
const result = propResult ?? (str(board.result) || "none");
|
|
46806
|
+
const phase = propPhase ?? (str(board.phase) || "player_turn");
|
|
46807
|
+
propStairsX ?? num(board.stairsX, 3);
|
|
46808
|
+
propStairsY ?? num(board.stairsY, 3);
|
|
46809
|
+
const eventBus = useEventBus();
|
|
46810
|
+
const { t } = useTranslate();
|
|
46811
|
+
const [hoveredTile, setHoveredTile] = useState(null);
|
|
46812
|
+
const emitMove = useCallback((dx, dy) => {
|
|
46813
|
+
if (!moveEvent) return;
|
|
46814
|
+
eventBus.emit(`UI:${moveEvent}`, { dx, dy });
|
|
46815
|
+
}, [moveEvent, eventBus]);
|
|
46816
|
+
useEffect(() => {
|
|
46817
|
+
const handler = (e) => {
|
|
46818
|
+
if (result !== "none" || phase !== "player_turn") return;
|
|
46819
|
+
if (e.key === "ArrowUp" || e.key === "w") emitMove(0, -1);
|
|
46820
|
+
if (e.key === "ArrowDown" || e.key === "s") emitMove(0, 1);
|
|
46821
|
+
if (e.key === "ArrowLeft" || e.key === "a") emitMove(-1, 0);
|
|
46822
|
+
if (e.key === "ArrowRight" || e.key === "d") emitMove(1, 0);
|
|
46823
|
+
};
|
|
46824
|
+
window.addEventListener("keydown", handler);
|
|
46825
|
+
return () => window.removeEventListener("keydown", handler);
|
|
46826
|
+
}, [result, phase, emitMove]);
|
|
46827
|
+
useEffect(() => {
|
|
46828
|
+
if ((result === "won" || result === "lost") && gameEndEvent) {
|
|
46829
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
46830
|
+
}
|
|
46831
|
+
}, [result, gameEndEvent, eventBus]);
|
|
46832
|
+
const handleTileClick = useCallback((x, y) => {
|
|
46833
|
+
if (result !== "none" || phase !== "player_turn") return;
|
|
46834
|
+
const dx = x - player.x;
|
|
46835
|
+
const dy = y - player.y;
|
|
46836
|
+
if (Math.abs(dx) + Math.abs(dy) === 1) {
|
|
46837
|
+
emitMove(dx, dy);
|
|
46838
|
+
}
|
|
46839
|
+
}, [result, phase, player, emitMove]);
|
|
46840
|
+
const handleReset = useCallback(() => {
|
|
46841
|
+
if (!playAgainEvent) return;
|
|
46842
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
46843
|
+
}, [playAgainEvent, eventBus]);
|
|
46844
|
+
const isoUnits = useMemo(() => {
|
|
46845
|
+
const liveEnemies = enemies.filter((e) => e.hp > 0);
|
|
46846
|
+
return [
|
|
46847
|
+
{
|
|
46848
|
+
id: "player",
|
|
46849
|
+
position: player,
|
|
46850
|
+
name: t("roguelike.player") || "Hero",
|
|
46851
|
+
team: "player",
|
|
46852
|
+
health: playerHp,
|
|
46853
|
+
maxHealth: playerMaxHp,
|
|
46854
|
+
unitType: "player",
|
|
46855
|
+
sprite: `${CDN4}/isometric-dungeon/Characters/Male/Male_0_Idle0.png`
|
|
46856
|
+
},
|
|
46857
|
+
...liveEnemies.map((e) => ({
|
|
46858
|
+
id: e.id,
|
|
46859
|
+
position: { x: e.x, y: e.y },
|
|
46860
|
+
name: t("roguelike.enemy") || "Enemy",
|
|
46861
|
+
team: "enemy",
|
|
46862
|
+
health: e.hp,
|
|
46863
|
+
maxHealth: 6,
|
|
46864
|
+
unitType: "enemy",
|
|
46865
|
+
sprite: `${CDN4}/sprite-sheets/shadow-legion-sprite-sheet-sw.png`
|
|
46866
|
+
}))
|
|
46867
|
+
];
|
|
46868
|
+
}, [player, enemies, playerHp, playerMaxHp, t]);
|
|
46869
|
+
const isoFeatures = useMemo(
|
|
46870
|
+
() => items.map((it) => ({
|
|
46871
|
+
id: it.id,
|
|
46872
|
+
x: it.x,
|
|
46873
|
+
y: it.y,
|
|
46874
|
+
type: it.kind,
|
|
46875
|
+
sprite: FEATURE_SPRITE[it.kind] ?? FEATURE_SPRITE.health_potion
|
|
46876
|
+
})),
|
|
46877
|
+
[items]
|
|
46878
|
+
);
|
|
46879
|
+
const validMoves = useMemo(() => {
|
|
46880
|
+
if (result !== "none" || phase !== "player_turn") return [];
|
|
46881
|
+
const dirs = [{ dx: 0, dy: -1 }, { dx: 0, dy: 1 }, { dx: -1, dy: 0 }, { dx: 1, dy: 0 }];
|
|
46882
|
+
return dirs.map(({ dx, dy }) => ({ x: player.x + dx, y: player.y + dy })).filter(({ x, y }) => {
|
|
46883
|
+
const tile = tiles.find((t2) => t2.x === x && t2.y === y);
|
|
46884
|
+
return tile?.passable ?? false;
|
|
46885
|
+
});
|
|
46886
|
+
}, [result, phase, player, tiles]);
|
|
46887
|
+
const isGameOver = result === "won" || result === "lost";
|
|
46888
|
+
return /* @__PURE__ */ jsxs(VStack, { className: cn("roguelike-board relative min-h-[600px] bg-background", className), gap: "none", children: [
|
|
46889
|
+
/* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between px-4 py-2 border-b border-border", gap: "md", children: [
|
|
46890
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
46891
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.depth") || "Depth" }),
|
|
46892
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body1", className: "font-bold tabular-nums", children: depth })
|
|
46893
|
+
] }),
|
|
46894
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
46895
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.hp") || "HP" }),
|
|
46896
|
+
/* @__PURE__ */ jsxs(
|
|
46897
|
+
Typography,
|
|
46898
|
+
{
|
|
46899
|
+
variant: "body1",
|
|
46900
|
+
className: cn(
|
|
46901
|
+
"font-bold tabular-nums",
|
|
46902
|
+
playerHp <= Math.floor(playerMaxHp * 0.25) ? "text-error" : "text-success"
|
|
46903
|
+
),
|
|
46904
|
+
children: [
|
|
46905
|
+
playerHp,
|
|
46906
|
+
" / ",
|
|
46907
|
+
playerMaxHp
|
|
46908
|
+
]
|
|
46909
|
+
}
|
|
46910
|
+
)
|
|
46911
|
+
] }),
|
|
46912
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
46913
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.atk") || "ATK" }),
|
|
46914
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body1", className: "font-bold tabular-nums text-foreground", children: playerAttack })
|
|
46915
|
+
] }),
|
|
46916
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "sm", className: "items-center", children: [
|
|
46917
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground", children: t("roguelike.phase") || "Phase" }),
|
|
46918
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "capitalize text-foreground", children: phase.replace("_", " ") })
|
|
46919
|
+
] })
|
|
46920
|
+
] }),
|
|
46921
|
+
/* @__PURE__ */ jsx(Box, { className: "relative flex-1", children: /* @__PURE__ */ jsx(
|
|
46922
|
+
IsometricCanvas_default,
|
|
46923
|
+
{
|
|
46924
|
+
tiles,
|
|
46925
|
+
units: isoUnits,
|
|
46926
|
+
features: isoFeatures,
|
|
46927
|
+
selectedUnitId: null,
|
|
46928
|
+
validMoves,
|
|
46929
|
+
attackTargets: [],
|
|
46930
|
+
hoveredTile,
|
|
46931
|
+
onTileClick: handleTileClick,
|
|
46932
|
+
onUnitClick: () => void 0,
|
|
46933
|
+
onTileHover: (x, y) => setHoveredTile({ x, y }),
|
|
46934
|
+
onTileLeave: () => setHoveredTile(null),
|
|
46935
|
+
scale,
|
|
46936
|
+
assetBaseUrl: propAssetManifest?.baseUrl ?? CDN4,
|
|
46937
|
+
assetManifest: propAssetManifest,
|
|
46938
|
+
unitScale
|
|
46939
|
+
}
|
|
46940
|
+
) }),
|
|
46941
|
+
!isGameOver && /* @__PURE__ */ jsx(HStack, { className: "justify-center gap-2 p-3", gap: "none", children: [
|
|
46942
|
+
{ label: "\u2191", dx: 0, dy: -1 },
|
|
46943
|
+
{ label: "\u2193", dx: 0, dy: 1 },
|
|
46944
|
+
{ label: "\u2190", dx: -1, dy: 0 },
|
|
46945
|
+
{ label: "\u2192", dx: 1, dy: 0 }
|
|
46946
|
+
].map(({ label, dx, dy }) => /* @__PURE__ */ jsx(
|
|
46947
|
+
Button,
|
|
46948
|
+
{
|
|
46949
|
+
variant: "secondary",
|
|
46950
|
+
className: "w-9 h-9 p-0 text-base font-mono",
|
|
46951
|
+
onClick: () => emitMove(dx, dy),
|
|
46952
|
+
disabled: phase !== "player_turn",
|
|
46953
|
+
children: label
|
|
46954
|
+
},
|
|
46955
|
+
label
|
|
46956
|
+
)) }),
|
|
46957
|
+
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: [
|
|
46958
|
+
/* @__PURE__ */ jsx(
|
|
46959
|
+
Typography,
|
|
46960
|
+
{
|
|
46961
|
+
variant: "h2",
|
|
46962
|
+
className: cn(
|
|
46963
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
46964
|
+
result === "won" ? "text-warning" : "text-error"
|
|
46965
|
+
),
|
|
46966
|
+
children: result === "won" ? t("roguelike.victory") || "Victory!" : t("roguelike.defeat") || "Defeated!"
|
|
46967
|
+
}
|
|
46968
|
+
),
|
|
46969
|
+
result === "won" && /* @__PURE__ */ jsx(Typography, { variant: "body1", className: "text-muted-foreground", children: t("roguelike.cleared") || `Dungeon cleared! Depth reached: ${depth}` }),
|
|
46970
|
+
/* @__PURE__ */ jsx(
|
|
46971
|
+
Button,
|
|
46972
|
+
{
|
|
46973
|
+
variant: "primary",
|
|
46974
|
+
className: "px-8 py-3 font-semibold",
|
|
46975
|
+
onClick: handleReset,
|
|
46976
|
+
children: t("roguelike.playAgain") || "Play Again"
|
|
46977
|
+
}
|
|
46978
|
+
)
|
|
46979
|
+
] }) })
|
|
46980
|
+
] });
|
|
46981
|
+
}
|
|
46982
|
+
var CDN4, DEFAULT_TILES2, DEFAULT_ENEMIES, DEFAULT_ITEMS, FEATURE_SPRITE;
|
|
46983
|
+
var init_RoguelikeBoard = __esm({
|
|
46984
|
+
"components/game/organisms/RoguelikeBoard.tsx"() {
|
|
46985
|
+
"use client";
|
|
46986
|
+
init_cn();
|
|
46987
|
+
init_useEventBus();
|
|
46988
|
+
init_Box();
|
|
46989
|
+
init_Button();
|
|
46990
|
+
init_Typography();
|
|
46991
|
+
init_Stack();
|
|
46992
|
+
init_IsometricCanvas();
|
|
46993
|
+
init_boardEntity();
|
|
46994
|
+
CDN4 = "https://almadar-kflow-assets.web.app/shared";
|
|
46995
|
+
DEFAULT_TILES2 = [
|
|
46996
|
+
{ x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
46997
|
+
{ x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
46998
|
+
{ x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
46999
|
+
{ x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47000
|
+
{ x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47001
|
+
{ x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47002
|
+
{ x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47003
|
+
{ x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47004
|
+
{ x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47005
|
+
{ x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47006
|
+
{ x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47007
|
+
{ x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
47008
|
+
{ x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
47009
|
+
{ x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47010
|
+
{ x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47011
|
+
{ x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47012
|
+
{ x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47013
|
+
{ x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
47014
|
+
{ x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stairs_E.png` },
|
|
47015
|
+
{ x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47016
|
+
{ x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47017
|
+
{ x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47018
|
+
{ x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47019
|
+
{ x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47020
|
+
{ x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN4}/isometric-dungeon/Isometric/stoneInset_E.png` }
|
|
47021
|
+
];
|
|
47022
|
+
DEFAULT_ENEMIES = [
|
|
47023
|
+
{ id: "e1", x: 3, y: 1, hp: 5, attack: 2 },
|
|
47024
|
+
{ id: "e2", x: 1, y: 3, hp: 4, attack: 1 }
|
|
47025
|
+
];
|
|
47026
|
+
DEFAULT_ITEMS = [
|
|
47027
|
+
{ id: "i1", x: 2, y: 2, kind: "health_potion" }
|
|
47028
|
+
];
|
|
47029
|
+
FEATURE_SPRITE = {
|
|
47030
|
+
health_potion: `${CDN4}/isometric-dungeon/Isometric/chestClosed_E.png`,
|
|
47031
|
+
sword: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`,
|
|
47032
|
+
shield: `${CDN4}/isometric-dungeon/Isometric/barrel_E.png`
|
|
47033
|
+
};
|
|
47034
|
+
RoguelikeBoard.displayName = "RoguelikeBoard";
|
|
47035
|
+
}
|
|
47036
|
+
});
|
|
47037
|
+
function RoguelikeTemplate({
|
|
47038
|
+
entity,
|
|
47039
|
+
scale = 0.45,
|
|
47040
|
+
unitScale = 1,
|
|
47041
|
+
tiles = DEFAULT_ROGUELIKE_TILES,
|
|
47042
|
+
enemies,
|
|
47043
|
+
items,
|
|
47044
|
+
stairsX,
|
|
47045
|
+
stairsY,
|
|
47046
|
+
assetManifest = DEFAULT_ROGUELIKE_MANIFEST,
|
|
47047
|
+
gameEndEvent,
|
|
47048
|
+
className
|
|
47049
|
+
}) {
|
|
47050
|
+
const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
|
|
47051
|
+
if (!resolved && !tiles && !assetManifest) return null;
|
|
47052
|
+
return /* @__PURE__ */ jsx(
|
|
47053
|
+
RoguelikeBoard,
|
|
47054
|
+
{
|
|
47055
|
+
entity: resolved,
|
|
47056
|
+
tiles,
|
|
47057
|
+
enemies,
|
|
47058
|
+
items,
|
|
47059
|
+
stairsX,
|
|
47060
|
+
stairsY,
|
|
47061
|
+
assetManifest,
|
|
47062
|
+
scale,
|
|
47063
|
+
unitScale,
|
|
47064
|
+
moveEvent: "MOVE",
|
|
47065
|
+
playAgainEvent: "PLAY_AGAIN",
|
|
47066
|
+
gameEndEvent: gameEndEvent ?? "GAME_END",
|
|
47067
|
+
className
|
|
47068
|
+
}
|
|
47069
|
+
);
|
|
47070
|
+
}
|
|
47071
|
+
var CDN5, DEFAULT_ROGUELIKE_TILES, DEFAULT_ROGUELIKE_MANIFEST;
|
|
47072
|
+
var init_RoguelikeTemplate = __esm({
|
|
47073
|
+
"components/game/templates/RoguelikeTemplate.tsx"() {
|
|
47074
|
+
init_RoguelikeBoard();
|
|
47075
|
+
CDN5 = "https://almadar-kflow-assets.web.app/shared";
|
|
47076
|
+
DEFAULT_ROGUELIKE_TILES = [
|
|
47077
|
+
{ x: 0, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47078
|
+
{ x: 1, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47079
|
+
{ x: 2, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47080
|
+
{ x: 3, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47081
|
+
{ x: 4, y: 0, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47082
|
+
{ x: 0, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47083
|
+
{ x: 1, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47084
|
+
{ x: 2, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47085
|
+
{ x: 3, y: 1, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47086
|
+
{ x: 4, y: 1, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47087
|
+
{ x: 0, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47088
|
+
{ x: 1, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
47089
|
+
{ x: 2, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
47090
|
+
{ x: 3, y: 2, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47091
|
+
{ x: 4, y: 2, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47092
|
+
{ x: 0, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47093
|
+
{ x: 1, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirt_E.png` },
|
|
47094
|
+
{ x: 2, y: 3, terrain: "floor", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/dirtTiles_E.png` },
|
|
47095
|
+
{ x: 3, y: 3, terrain: "stairs", passable: true, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stairs_E.png` },
|
|
47096
|
+
{ x: 4, y: 3, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47097
|
+
{ x: 0, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47098
|
+
{ x: 1, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47099
|
+
{ x: 2, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47100
|
+
{ x: 3, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` },
|
|
47101
|
+
{ x: 4, y: 4, terrain: "wall", passable: false, terrainSprite: `${CDN5}/isometric-dungeon/Isometric/stoneInset_E.png` }
|
|
47102
|
+
];
|
|
47103
|
+
DEFAULT_ROGUELIKE_MANIFEST = {
|
|
47104
|
+
baseUrl: CDN5,
|
|
47105
|
+
terrains: {
|
|
47106
|
+
floor: "/isometric-dungeon/Isometric/dirt_E.png",
|
|
47107
|
+
wall: "/isometric-dungeon/Isometric/stoneInset_E.png",
|
|
47108
|
+
stairs: "/isometric-dungeon/Isometric/stairs_E.png",
|
|
47109
|
+
planks: "/isometric-dungeon/Isometric/planks_E.png"
|
|
47110
|
+
},
|
|
47111
|
+
units: {
|
|
47112
|
+
player: "/isometric-dungeon/Characters/Male/Male_0_Idle0.png",
|
|
47113
|
+
enemy: "/sprite-sheets/shadow-legion-sprite-sheet-sw.png"
|
|
47114
|
+
},
|
|
47115
|
+
features: {
|
|
47116
|
+
health_potion: "/isometric-dungeon/Isometric/chestClosed_E.png",
|
|
47117
|
+
sword: "/isometric-dungeon/Isometric/barrel_E.png",
|
|
47118
|
+
shield: "/isometric-dungeon/Isometric/barrel_E.png"
|
|
47119
|
+
}
|
|
47120
|
+
};
|
|
47121
|
+
RoguelikeTemplate.displayName = "RoguelikeTemplate";
|
|
47122
|
+
}
|
|
47123
|
+
});
|
|
44848
47124
|
|
|
44849
47125
|
// lib/traitRegistry.ts
|
|
44850
47126
|
function notifyListeners2() {
|
|
@@ -46098,7 +48374,7 @@ function getAllEvents(traits2) {
|
|
|
46098
48374
|
function EventDispatcherTab({ traits: traits2, schema }) {
|
|
46099
48375
|
const eventBus = useEventBus();
|
|
46100
48376
|
const { t } = useTranslate();
|
|
46101
|
-
const [
|
|
48377
|
+
const [log15, setLog] = React91.useState([]);
|
|
46102
48378
|
const prevStatesRef = React91.useRef(/* @__PURE__ */ new Map());
|
|
46103
48379
|
React91.useEffect(() => {
|
|
46104
48380
|
for (const trait of traits2) {
|
|
@@ -46162,9 +48438,9 @@ function EventDispatcherTab({ traits: traits2, schema }) {
|
|
|
46162
48438
|
/* @__PURE__ */ jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.otherEvents") }),
|
|
46163
48439
|
/* @__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)) })
|
|
46164
48440
|
] }),
|
|
46165
|
-
|
|
48441
|
+
log15.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
46166
48442
|
/* @__PURE__ */ jsx(Typography, { variant: "small", weight: "medium", className: "text-gray-500 mb-1", children: t("debug.recentTransitions") }),
|
|
46167
|
-
/* @__PURE__ */ jsx(Stack, { gap: "xs", children:
|
|
48443
|
+
/* @__PURE__ */ jsx(Stack, { gap: "xs", children: log15.map((entry, i) => /* @__PURE__ */ jsxs(Typography, { variant: "small", className: "font-mono text-xs", children: [
|
|
46168
48444
|
/* @__PURE__ */ jsx("span", { className: "text-purple-400", children: entry.traitName }),
|
|
46169
48445
|
" ",
|
|
46170
48446
|
/* @__PURE__ */ jsx("span", { className: "text-gray-500", children: entry.from }),
|
|
@@ -46967,17 +49243,24 @@ function SequencerBoard({
|
|
|
46967
49243
|
const { emit } = useEventBus();
|
|
46968
49244
|
const { t } = useTranslate();
|
|
46969
49245
|
const resolved = boardEntity(entity);
|
|
46970
|
-
const maxSlots = num(resolved?.maxSlots);
|
|
49246
|
+
const maxSlots = num(resolved?.maxSlots) || 3;
|
|
46971
49247
|
const solutions = Array.isArray(resolved?.solutions) ? resolved.solutions : [];
|
|
46972
49248
|
const availableActions = Array.isArray(resolved?.availableActions) ? resolved.availableActions : [];
|
|
46973
49249
|
const allowDuplicates = resolved?.allowDuplicates !== false;
|
|
49250
|
+
const entitySlots = Array.isArray(resolved?.slots) ? resolved.slots : [];
|
|
49251
|
+
const entityResult = str(resolved?.result);
|
|
49252
|
+
const entityAttempts = num(resolved?.attempts);
|
|
49253
|
+
const entityCurrentStep = typeof resolved?.currentStep === "number" ? resolved.currentStep : -1;
|
|
49254
|
+
const slots = Array.from({ length: maxSlots }, (_, i) => {
|
|
49255
|
+
const entitySlot = entitySlots.find((s) => s.index === i);
|
|
49256
|
+
if (!entitySlot?.placedActionId) return void 0;
|
|
49257
|
+
return availableActions.find((a) => a.id === entitySlot.placedActionId);
|
|
49258
|
+
});
|
|
49259
|
+
const isSuccess = entityResult === "win";
|
|
49260
|
+
const attempts = entityAttempts;
|
|
49261
|
+
const isPlayingBack = entityCurrentStep >= 0 && !isSuccess;
|
|
49262
|
+
const currentStep = entityCurrentStep;
|
|
46974
49263
|
const [headerError, setHeaderError] = useState(false);
|
|
46975
|
-
const [slots, setSlots] = useState(
|
|
46976
|
-
() => Array.from({ length: maxSlots }, () => void 0)
|
|
46977
|
-
);
|
|
46978
|
-
const [playState, setPlayState] = useState("idle");
|
|
46979
|
-
const [currentStep, setCurrentStep] = useState(-1);
|
|
46980
|
-
const [attempts, setAttempts] = useState(0);
|
|
46981
49264
|
const [slotFeedback, setSlotFeedback] = useState(
|
|
46982
49265
|
() => Array.from({ length: maxSlots }, () => null)
|
|
46983
49266
|
);
|
|
@@ -46986,11 +49269,6 @@ function SequencerBoard({
|
|
|
46986
49269
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
46987
49270
|
}, []);
|
|
46988
49271
|
const handleSlotDrop = useCallback((index, item) => {
|
|
46989
|
-
setSlots((prev) => {
|
|
46990
|
-
const next = [...prev];
|
|
46991
|
-
next[index] = item;
|
|
46992
|
-
return next;
|
|
46993
|
-
});
|
|
46994
49272
|
setSlotFeedback((prev) => {
|
|
46995
49273
|
const next = [...prev];
|
|
46996
49274
|
next[index] = null;
|
|
@@ -47000,11 +49278,6 @@ function SequencerBoard({
|
|
|
47000
49278
|
if (placeEvent) emit(`UI:${placeEvent}`, { slotIndex: index, actionId: item.id });
|
|
47001
49279
|
}, [emit, placeEvent]);
|
|
47002
49280
|
const handleSlotRemove = useCallback((index) => {
|
|
47003
|
-
setSlots((prev) => {
|
|
47004
|
-
const next = [...prev];
|
|
47005
|
-
next[index] = void 0;
|
|
47006
|
-
return next;
|
|
47007
|
-
});
|
|
47008
49281
|
setSlotFeedback((prev) => {
|
|
47009
49282
|
const next = [...prev];
|
|
47010
49283
|
next[index] = null;
|
|
@@ -47015,48 +49288,34 @@ function SequencerBoard({
|
|
|
47015
49288
|
}, [emit, removeEvent]);
|
|
47016
49289
|
const handleReset = useCallback(() => {
|
|
47017
49290
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
47018
|
-
setSlots(Array.from({ length: maxSlots }, () => void 0));
|
|
47019
|
-
setPlayState("idle");
|
|
47020
|
-
setCurrentStep(-1);
|
|
47021
|
-
setAttempts(0);
|
|
47022
49291
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
47023
49292
|
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
47024
49293
|
}, [maxSlots, playAgainEvent, emit]);
|
|
47025
49294
|
const filledSlots = slots.filter((s) => !!s);
|
|
47026
|
-
const canPlay = filledSlots.length > 0 &&
|
|
49295
|
+
const canPlay = filledSlots.length > 0 && !isPlayingBack && !isSuccess;
|
|
47027
49296
|
const handlePlay = useCallback(() => {
|
|
47028
49297
|
if (!canPlay) return;
|
|
47029
49298
|
setSlotFeedback(Array.from({ length: maxSlots }, () => null));
|
|
47030
49299
|
emit("UI:PLAY_SOUND", { key: "confirm" });
|
|
47031
49300
|
const sequence = slots.map((s) => s?.id || "");
|
|
47032
|
-
|
|
47033
|
-
|
|
47034
|
-
}
|
|
47035
|
-
setPlayState("playing");
|
|
47036
|
-
setCurrentStep(0);
|
|
49301
|
+
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
49302
|
+
if (playEvent) emit(`UI:${playEvent}`, { sequence });
|
|
47037
49303
|
let step = 0;
|
|
47038
49304
|
const advance = () => {
|
|
47039
49305
|
step++;
|
|
49306
|
+
emit("UI:STEP", { step });
|
|
47040
49307
|
if (step >= maxSlots) {
|
|
49308
|
+
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
47041
49309
|
const playerSeq = slots.map((s) => s?.id);
|
|
47042
|
-
const playerIds = slots.filter(Boolean).map((s) => s?.id || "");
|
|
47043
49310
|
const success = solutions.some(
|
|
47044
49311
|
(sol) => sol.length === playerIds.length && sol.every((id, i) => id === playerIds[i])
|
|
47045
49312
|
);
|
|
47046
|
-
if (checkEvent) emit(`UI:${checkEvent}`, { sequence: playerIds });
|
|
47047
49313
|
if (success) {
|
|
47048
|
-
setPlayState("success");
|
|
47049
|
-
setCurrentStep(-1);
|
|
47050
49314
|
emit("UI:PLAY_SOUND", { key: "levelComplete" });
|
|
47051
|
-
if (completeEvent) {
|
|
47052
|
-
emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
47053
|
-
}
|
|
49315
|
+
if (completeEvent) emit(`UI:${completeEvent}`, { success: true, sequence: playerIds });
|
|
47054
49316
|
} else {
|
|
47055
|
-
setAttempts((prev) => prev + 1);
|
|
47056
49317
|
const feedback = computeSlotFeedback(playerSeq, solutions);
|
|
47057
49318
|
setSlotFeedback(feedback);
|
|
47058
|
-
setPlayState("idle");
|
|
47059
|
-
setCurrentStep(-1);
|
|
47060
49319
|
emit("UI:PLAY_SOUND", { key: "fail" });
|
|
47061
49320
|
const correctCount2 = feedback.filter((f3) => f3 === "correct").length;
|
|
47062
49321
|
for (let ci = 0; ci < correctCount2; ci++) {
|
|
@@ -47066,7 +49325,6 @@ function SequencerBoard({
|
|
|
47066
49325
|
}
|
|
47067
49326
|
}
|
|
47068
49327
|
} else {
|
|
47069
|
-
setCurrentStep(step);
|
|
47070
49328
|
timerRef.current = setTimeout(advance, stepDurationMs);
|
|
47071
49329
|
}
|
|
47072
49330
|
};
|
|
@@ -47116,7 +49374,7 @@ function SequencerBoard({
|
|
|
47116
49374
|
/* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
|
|
47117
49375
|
/* @__PURE__ */ jsxs(HStack, { className: "items-center justify-between", children: [
|
|
47118
49376
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("sequencer.yourSequence") + ":" }),
|
|
47119
|
-
hasFeedback &&
|
|
49377
|
+
hasFeedback && !isPlayingBack && !isSuccess && /* @__PURE__ */ jsxs(Typography, { variant: "caption", className: "text-muted-foreground", children: [
|
|
47120
49378
|
`${correctCount}/${maxSlots} `,
|
|
47121
49379
|
"\u2705"
|
|
47122
49380
|
] })
|
|
@@ -47128,7 +49386,7 @@ function SequencerBoard({
|
|
|
47128
49386
|
maxSlots,
|
|
47129
49387
|
onSlotDrop: handleSlotDrop,
|
|
47130
49388
|
onSlotRemove: handleSlotRemove,
|
|
47131
|
-
playing:
|
|
49389
|
+
playing: isPlayingBack,
|
|
47132
49390
|
currentStep,
|
|
47133
49391
|
categoryColors,
|
|
47134
49392
|
slotFeedback,
|
|
@@ -47136,7 +49394,7 @@ function SequencerBoard({
|
|
|
47136
49394
|
}
|
|
47137
49395
|
)
|
|
47138
49396
|
] }),
|
|
47139
|
-
|
|
49397
|
+
!isPlayingBack && /* @__PURE__ */ jsx(
|
|
47140
49398
|
ActionPalette,
|
|
47141
49399
|
{
|
|
47142
49400
|
actions: availableActions,
|
|
@@ -47146,8 +49404,8 @@ function SequencerBoard({
|
|
|
47146
49404
|
label: t("sequencer.dragActions")
|
|
47147
49405
|
}
|
|
47148
49406
|
),
|
|
47149
|
-
hasFeedback &&
|
|
47150
|
-
|
|
49407
|
+
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) }) }),
|
|
49408
|
+
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") }) }),
|
|
47151
49409
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
|
|
47152
49410
|
/* @__PURE__ */ jsx(
|
|
47153
49411
|
Button,
|
|
@@ -48103,9 +50361,9 @@ function VariablePanel({
|
|
|
48103
50361
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-muted-foreground font-medium", children: t("stateArchitect.variables", { name: entityName }) }),
|
|
48104
50362
|
variables.map((v) => {
|
|
48105
50363
|
const name = v.name == null ? "" : String(v.name);
|
|
48106
|
-
const value =
|
|
48107
|
-
const max =
|
|
48108
|
-
const min =
|
|
50364
|
+
const value = numField2(v.value);
|
|
50365
|
+
const max = numField2(v.max, 100);
|
|
50366
|
+
const min = numField2(v.min, 0);
|
|
48109
50367
|
const unit = v.unit == null ? "" : String(v.unit);
|
|
48110
50368
|
const pct = Math.round((value - min) / (max - min) * 100);
|
|
48111
50369
|
const isHigh = pct > 80;
|
|
@@ -48135,12 +50393,12 @@ function VariablePanel({
|
|
|
48135
50393
|
})
|
|
48136
50394
|
] });
|
|
48137
50395
|
}
|
|
48138
|
-
var
|
|
50396
|
+
var numField2;
|
|
48139
50397
|
var init_VariablePanel = __esm({
|
|
48140
50398
|
"components/game/organisms/puzzles/state-architect/VariablePanel.tsx"() {
|
|
48141
50399
|
init_atoms2();
|
|
48142
50400
|
init_cn();
|
|
48143
|
-
|
|
50401
|
+
numField2 = (v, fallback = 0) => {
|
|
48144
50402
|
const n = Number(v);
|
|
48145
50403
|
return Number.isFinite(n) ? n : fallback;
|
|
48146
50404
|
};
|
|
@@ -48197,6 +50455,9 @@ function StateArchitectBoard({
|
|
|
48197
50455
|
stepDurationMs = 600,
|
|
48198
50456
|
testEvent,
|
|
48199
50457
|
completeEvent,
|
|
50458
|
+
addTransitionEvent,
|
|
50459
|
+
removeTransitionEvent,
|
|
50460
|
+
playAgainEvent,
|
|
48200
50461
|
className
|
|
48201
50462
|
}) {
|
|
48202
50463
|
const { emit } = useEventBus();
|
|
@@ -48209,14 +50470,16 @@ function StateArchitectBoard({
|
|
|
48209
50470
|
const testCases = Array.isArray(resolved?.testCases) ? resolved.testCases : [];
|
|
48210
50471
|
const entityTransitions = Array.isArray(resolved?.transitions) ? resolved.transitions : [];
|
|
48211
50472
|
const entityVariables = rows(resolved?.variables);
|
|
48212
|
-
const
|
|
50473
|
+
const transitions = entityTransitions;
|
|
50474
|
+
const attempts = typeof resolved?.attempts === "number" ? resolved.attempts : 0;
|
|
50475
|
+
const entityResult = str(resolved?.result) || "none";
|
|
50476
|
+
const isSuccess = entityResult === "win";
|
|
50477
|
+
const [isTesting, setIsTesting] = useState(false);
|
|
48213
50478
|
const [headerError, setHeaderError] = useState(false);
|
|
48214
|
-
const [playState, setPlayState] = useState("editing");
|
|
48215
50479
|
const [currentState, setCurrentState] = useState(initialState);
|
|
48216
50480
|
const [selectedState, setSelectedState] = useState(null);
|
|
48217
50481
|
const [testResults, setTestResults] = useState([]);
|
|
48218
50482
|
const [variables, setVariables] = useState(() => [...entityVariables]);
|
|
48219
|
-
const [attempts, setAttempts] = useState(0);
|
|
48220
50483
|
const timerRef = useRef(null);
|
|
48221
50484
|
const [addingFrom, setAddingFrom] = useState(null);
|
|
48222
50485
|
useEffect(() => () => {
|
|
@@ -48226,7 +50489,7 @@ function StateArchitectBoard({
|
|
|
48226
50489
|
const GRAPH_H = 400;
|
|
48227
50490
|
const positions = useMemo(() => layoutStates(entityStates, GRAPH_W, GRAPH_H), [entityStates]);
|
|
48228
50491
|
const handleStateClick = useCallback((state) => {
|
|
48229
|
-
if (
|
|
50492
|
+
if (isTesting) return;
|
|
48230
50493
|
if (addingFrom) {
|
|
48231
50494
|
if (addingFrom !== state) {
|
|
48232
50495
|
const event = availableEvents[0] || "EVENT";
|
|
@@ -48236,20 +50499,20 @@ function StateArchitectBoard({
|
|
|
48236
50499
|
to: state,
|
|
48237
50500
|
event
|
|
48238
50501
|
};
|
|
48239
|
-
|
|
50502
|
+
if (addTransitionEvent) emit(`UI:${addTransitionEvent}`, { id: newTrans.id, from: newTrans.from, to: newTrans.to, event: newTrans.event });
|
|
48240
50503
|
}
|
|
48241
50504
|
setAddingFrom(null);
|
|
48242
50505
|
} else {
|
|
48243
50506
|
setSelectedState(state);
|
|
48244
50507
|
}
|
|
48245
|
-
}, [
|
|
50508
|
+
}, [isTesting, addingFrom, availableEvents, addTransitionEvent, emit]);
|
|
48246
50509
|
const handleStartAddTransition = useCallback(() => {
|
|
48247
50510
|
if (!selectedState) return;
|
|
48248
50511
|
setAddingFrom(selectedState);
|
|
48249
50512
|
}, [selectedState]);
|
|
48250
50513
|
const handleRemoveTransition = useCallback((transId) => {
|
|
48251
|
-
|
|
48252
|
-
}, []);
|
|
50514
|
+
if (removeTransitionEvent) emit(`UI:${removeTransitionEvent}`, { id: transId });
|
|
50515
|
+
}, [removeTransitionEvent, emit]);
|
|
48253
50516
|
const machine = useMemo(() => ({
|
|
48254
50517
|
name: entityName,
|
|
48255
50518
|
description: str(resolved?.description),
|
|
@@ -48263,16 +50526,16 @@ function StateArchitectBoard({
|
|
|
48263
50526
|
}))
|
|
48264
50527
|
}), [entityName, resolved, entityStates, currentState, transitions]);
|
|
48265
50528
|
const handleTest = useCallback(() => {
|
|
48266
|
-
if (
|
|
50529
|
+
if (isTesting) return;
|
|
48267
50530
|
if (testEvent) emit(`UI:${testEvent}`, {});
|
|
48268
|
-
|
|
50531
|
+
setIsTesting(true);
|
|
48269
50532
|
setTestResults([]);
|
|
48270
50533
|
const results = [];
|
|
48271
50534
|
let testIdx = 0;
|
|
48272
50535
|
const runNextTest = () => {
|
|
48273
50536
|
if (testIdx >= testCases.length) {
|
|
48274
50537
|
const allPassed = results.every((r2) => r2.passed);
|
|
48275
|
-
|
|
50538
|
+
setIsTesting(false);
|
|
48276
50539
|
setTestResults(results);
|
|
48277
50540
|
if (allPassed && completeEvent) {
|
|
48278
50541
|
emit(`UI:${completeEvent}`, {
|
|
@@ -48280,9 +50543,6 @@ function StateArchitectBoard({
|
|
|
48280
50543
|
passedTests: results.filter((r2) => r2.passed).length
|
|
48281
50544
|
});
|
|
48282
50545
|
}
|
|
48283
|
-
if (!allPassed) {
|
|
48284
|
-
setAttempts((prev) => prev + 1);
|
|
48285
|
-
}
|
|
48286
50546
|
return;
|
|
48287
50547
|
}
|
|
48288
50548
|
const testCase = testCases[testIdx];
|
|
@@ -48305,24 +50565,23 @@ function StateArchitectBoard({
|
|
|
48305
50565
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
48306
50566
|
};
|
|
48307
50567
|
timerRef.current = setTimeout(runNextTest, stepDurationMs);
|
|
48308
|
-
}, [
|
|
50568
|
+
}, [isTesting, transitions, testCases, initialState, stepDurationMs, testEvent, completeEvent, emit]);
|
|
48309
50569
|
const handleTryAgain = useCallback(() => {
|
|
48310
50570
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
48311
|
-
|
|
50571
|
+
setIsTesting(false);
|
|
48312
50572
|
setCurrentState(initialState);
|
|
48313
50573
|
setTestResults([]);
|
|
48314
50574
|
}, [initialState]);
|
|
48315
50575
|
const handleReset = useCallback(() => {
|
|
48316
50576
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
48317
|
-
|
|
48318
|
-
setPlayState("editing");
|
|
50577
|
+
setIsTesting(false);
|
|
48319
50578
|
setCurrentState(initialState);
|
|
48320
50579
|
setTestResults([]);
|
|
48321
50580
|
setVariables([...entityVariables]);
|
|
48322
50581
|
setSelectedState(null);
|
|
48323
50582
|
setAddingFrom(null);
|
|
48324
|
-
|
|
48325
|
-
}, [
|
|
50583
|
+
if (playAgainEvent) emit(`UI:${playAgainEvent}`, {});
|
|
50584
|
+
}, [initialState, entityVariables, playAgainEvent, emit]);
|
|
48326
50585
|
const codeData = useMemo(() => ({
|
|
48327
50586
|
name: entityName,
|
|
48328
50587
|
states: entityStates,
|
|
@@ -48418,7 +50677,7 @@ function StateArchitectBoard({
|
|
|
48418
50677
|
]
|
|
48419
50678
|
}
|
|
48420
50679
|
),
|
|
48421
|
-
|
|
50680
|
+
!isTesting && /* @__PURE__ */ jsx(HStack, { gap: "sm", children: /* @__PURE__ */ jsx(
|
|
48422
50681
|
Button,
|
|
48423
50682
|
{
|
|
48424
50683
|
variant: "ghost",
|
|
@@ -48440,7 +50699,7 @@ function StateArchitectBoard({
|
|
|
48440
50699
|
t2.guardHint,
|
|
48441
50700
|
")"
|
|
48442
50701
|
] }),
|
|
48443
|
-
|
|
50702
|
+
!isTesting && /* @__PURE__ */ jsx(
|
|
48444
50703
|
Button,
|
|
48445
50704
|
{
|
|
48446
50705
|
variant: "ghost",
|
|
@@ -48472,21 +50731,21 @@ function StateArchitectBoard({
|
|
|
48472
50731
|
resolved.showCodeView !== false && /* @__PURE__ */ jsx(StateJsonView, { data: codeData, label: "View Code" })
|
|
48473
50732
|
] })
|
|
48474
50733
|
] }),
|
|
48475
|
-
|
|
48476
|
-
|
|
50734
|
+
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") }) }),
|
|
50735
|
+
!isTesting && !isSuccess && testResults.some((r2) => !r2.passed) && /* @__PURE__ */ jsxs(VStack, { gap: "sm", children: [
|
|
48477
50736
|
/* @__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]) }) }),
|
|
48478
|
-
attempts >=
|
|
50737
|
+
!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: [
|
|
48479
50738
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-accent font-bold shrink-0", children: "\u{1F4A1} " + t("game.hint") + ":" }),
|
|
48480
50739
|
/* @__PURE__ */ jsx(Typography, { variant: "body2", className: "text-foreground", children: hint })
|
|
48481
50740
|
] }) })
|
|
48482
50741
|
] }),
|
|
48483
50742
|
/* @__PURE__ */ jsxs(HStack, { gap: "sm", children: [
|
|
48484
|
-
|
|
50743
|
+
!isTesting && !isSuccess && testResults.some((r2) => !r2.passed) ? /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: handleTryAgain, children: "\u{1F504} " + t("puzzle.tryAgainButton") }) : /* @__PURE__ */ jsx(
|
|
48485
50744
|
Button,
|
|
48486
50745
|
{
|
|
48487
50746
|
variant: "primary",
|
|
48488
50747
|
onClick: handleTest,
|
|
48489
|
-
disabled:
|
|
50748
|
+
disabled: isTesting,
|
|
48490
50749
|
children: "\u25B6 " + t("game.runTests")
|
|
48491
50750
|
}
|
|
48492
50751
|
),
|
|
@@ -48992,6 +51251,289 @@ var init_ToastSlot = __esm({
|
|
|
48992
51251
|
ToastSlot.displayName = "ToastSlot";
|
|
48993
51252
|
}
|
|
48994
51253
|
});
|
|
51254
|
+
function tilesToIso(tiles) {
|
|
51255
|
+
return tiles.map((t) => ({
|
|
51256
|
+
x: t.x,
|
|
51257
|
+
y: t.y,
|
|
51258
|
+
terrain: t.terrain,
|
|
51259
|
+
terrainSprite: t.terrainSprite ?? TERRAIN_SPRITES[t.terrain ?? "ground"],
|
|
51260
|
+
passable: t.passable
|
|
51261
|
+
}));
|
|
51262
|
+
}
|
|
51263
|
+
function towersToUnits(towers) {
|
|
51264
|
+
return towers.map((t) => ({
|
|
51265
|
+
id: t.id,
|
|
51266
|
+
position: { x: t.x, y: t.y },
|
|
51267
|
+
name: "Tower",
|
|
51268
|
+
team: "player",
|
|
51269
|
+
sprite: TOWER_SPRITE,
|
|
51270
|
+
unitType: "guardian",
|
|
51271
|
+
health: 1,
|
|
51272
|
+
maxHealth: 1
|
|
51273
|
+
}));
|
|
51274
|
+
}
|
|
51275
|
+
function creepsToUnits(creeps) {
|
|
51276
|
+
return creeps.map((c) => ({
|
|
51277
|
+
id: c.id,
|
|
51278
|
+
position: { x: c.x, y: c.y },
|
|
51279
|
+
name: "Creep",
|
|
51280
|
+
team: "enemy",
|
|
51281
|
+
sprite: CREEP_SPRITE,
|
|
51282
|
+
unitType: "scrapper",
|
|
51283
|
+
health: c.hp,
|
|
51284
|
+
maxHealth: c.maxHp
|
|
51285
|
+
}));
|
|
51286
|
+
}
|
|
51287
|
+
function pathToFeatures(path) {
|
|
51288
|
+
return path.map((p2, i) => ({
|
|
51289
|
+
id: `path-${i}`,
|
|
51290
|
+
x: p2.x,
|
|
51291
|
+
y: p2.y,
|
|
51292
|
+
type: "path-marker",
|
|
51293
|
+
sprite: `${CDN6}world-map/road_straight.png`
|
|
51294
|
+
}));
|
|
51295
|
+
}
|
|
51296
|
+
function TowerDefenseBoard({
|
|
51297
|
+
entity,
|
|
51298
|
+
tiles: propTiles,
|
|
51299
|
+
path: propPath,
|
|
51300
|
+
towers: propTowers,
|
|
51301
|
+
creeps: propCreeps,
|
|
51302
|
+
gold: propGold,
|
|
51303
|
+
lives: propLives,
|
|
51304
|
+
wave: propWave,
|
|
51305
|
+
maxWaves: propMaxWaves,
|
|
51306
|
+
result: propResult,
|
|
51307
|
+
waveActive: propWaveActive,
|
|
51308
|
+
towerCost = 25,
|
|
51309
|
+
scale = 0.45,
|
|
51310
|
+
placeTowerEvent,
|
|
51311
|
+
startWaveEvent,
|
|
51312
|
+
playAgainEvent,
|
|
51313
|
+
gameEndEvent,
|
|
51314
|
+
className
|
|
51315
|
+
}) {
|
|
51316
|
+
const board = boardEntity(entity) ?? {};
|
|
51317
|
+
const eventBus = useEventBus();
|
|
51318
|
+
const { t } = useTranslate();
|
|
51319
|
+
const tiles = propTiles ?? rows(board.tiles);
|
|
51320
|
+
const path = propPath ?? rows(board.path);
|
|
51321
|
+
const towers = propTowers ?? rows(board.towers);
|
|
51322
|
+
const creeps = propCreeps ?? rows(board.creeps);
|
|
51323
|
+
const gold = propGold ?? num(board.gold, 100);
|
|
51324
|
+
const lives = propLives ?? num(board.lives, 20);
|
|
51325
|
+
const wave = propWave ?? num(board.wave, 1);
|
|
51326
|
+
const maxWaves = propMaxWaves ?? num(board.maxWaves, 5);
|
|
51327
|
+
const result = propResult ?? (str(board.result) || "none");
|
|
51328
|
+
const waveActive = propWaveActive ?? Boolean(board.waveActive);
|
|
51329
|
+
const [hoveredTile, setHoveredTile] = useState(null);
|
|
51330
|
+
const emittedGameEnd = useRef(false);
|
|
51331
|
+
if (result !== "none" && !emittedGameEnd.current) {
|
|
51332
|
+
emittedGameEnd.current = true;
|
|
51333
|
+
if (gameEndEvent) {
|
|
51334
|
+
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
51335
|
+
}
|
|
51336
|
+
}
|
|
51337
|
+
if (result === "none") {
|
|
51338
|
+
emittedGameEnd.current = false;
|
|
51339
|
+
}
|
|
51340
|
+
const towerPositions = useMemo(() => new Set(towers.map((t2) => `${t2.x},${t2.y}`)), [towers]);
|
|
51341
|
+
const pathPositions = useMemo(() => new Set(path.map((p2) => `${p2.x},${p2.y}`)), [path]);
|
|
51342
|
+
const validMoves = useMemo(() => {
|
|
51343
|
+
if (result !== "none" || gold < towerCost) return [];
|
|
51344
|
+
return tiles.filter(
|
|
51345
|
+
(t2) => t2.passable !== false && !towerPositions.has(`${t2.x},${t2.y}`) && !pathPositions.has(`${t2.x},${t2.y}`)
|
|
51346
|
+
).map((t2) => ({ x: t2.x, y: t2.y }));
|
|
51347
|
+
}, [tiles, towerPositions, pathPositions, result, gold, towerCost]);
|
|
51348
|
+
const isoTiles = useMemo(() => tilesToIso(tiles), [tiles]);
|
|
51349
|
+
const towerUnits = useMemo(() => towersToUnits(towers), [towers]);
|
|
51350
|
+
const creepUnits = useMemo(() => creepsToUnits(creeps), [creeps]);
|
|
51351
|
+
const isoUnits = useMemo(() => [...towerUnits, ...creepUnits], [towerUnits, creepUnits]);
|
|
51352
|
+
const pathFeatures = useMemo(() => pathToFeatures(path), [path]);
|
|
51353
|
+
const handleTileClick = useCallback((x, y) => {
|
|
51354
|
+
if (result !== "none") return;
|
|
51355
|
+
if (pathPositions.has(`${x},${y}`)) return;
|
|
51356
|
+
if (towerPositions.has(`${x},${y}`)) return;
|
|
51357
|
+
if (gold < towerCost) return;
|
|
51358
|
+
if (placeTowerEvent) {
|
|
51359
|
+
eventBus.emit(`UI:${placeTowerEvent}`, { x, y });
|
|
51360
|
+
}
|
|
51361
|
+
}, [result, pathPositions, towerPositions, gold, towerCost, placeTowerEvent, eventBus]);
|
|
51362
|
+
const handleStartWave = useCallback(() => {
|
|
51363
|
+
if (startWaveEvent) {
|
|
51364
|
+
eventBus.emit(`UI:${startWaveEvent}`, { wave });
|
|
51365
|
+
}
|
|
51366
|
+
}, [startWaveEvent, wave, eventBus]);
|
|
51367
|
+
const handlePlayAgain = useCallback(() => {
|
|
51368
|
+
if (playAgainEvent) {
|
|
51369
|
+
eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
51370
|
+
}
|
|
51371
|
+
}, [playAgainEvent, eventBus]);
|
|
51372
|
+
const canPlaceTower = gold >= towerCost && result === "none";
|
|
51373
|
+
const canStartWave = !waveActive && result === "none";
|
|
51374
|
+
return /* @__PURE__ */ jsxs(VStack, { className: cn("tower-defense-board relative min-h-[600px] bg-background", className), gap: "none", children: [
|
|
51375
|
+
/* @__PURE__ */ jsxs(
|
|
51376
|
+
HStack,
|
|
51377
|
+
{
|
|
51378
|
+
className: "px-4 py-2 border-b border-border bg-surface",
|
|
51379
|
+
gap: "lg",
|
|
51380
|
+
align: "center",
|
|
51381
|
+
children: [
|
|
51382
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
51383
|
+
/* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("td.gold") ?? "Gold" }),
|
|
51384
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", className: "text-warning", children: gold })
|
|
51385
|
+
] }),
|
|
51386
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
51387
|
+
/* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("td.lives") ?? "Lives" }),
|
|
51388
|
+
/* @__PURE__ */ jsx(Typography, { variant: "body2", weight: "bold", className: lives <= 5 ? "text-error" : "text-success", children: lives })
|
|
51389
|
+
] }),
|
|
51390
|
+
/* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
|
|
51391
|
+
/* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", children: t("td.wave") ?? "Wave" }),
|
|
51392
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "body2", weight: "bold", children: [
|
|
51393
|
+
wave,
|
|
51394
|
+
" / ",
|
|
51395
|
+
maxWaves
|
|
51396
|
+
] })
|
|
51397
|
+
] }),
|
|
51398
|
+
/* @__PURE__ */ jsxs(Box, { className: "ml-auto", children: [
|
|
51399
|
+
canStartWave && /* @__PURE__ */ jsx(Button, { variant: "primary", size: "sm", onClick: handleStartWave, children: t("td.startWave") ?? "Start Wave" }),
|
|
51400
|
+
waveActive && /* @__PURE__ */ jsx(Typography, { variant: "small", className: "text-warning animate-pulse", children: t("td.waveInProgress") ?? "Wave in progress\u2026" })
|
|
51401
|
+
] })
|
|
51402
|
+
]
|
|
51403
|
+
}
|
|
51404
|
+
),
|
|
51405
|
+
/* @__PURE__ */ jsxs(Box, { className: "relative flex-1", children: [
|
|
51406
|
+
/* @__PURE__ */ jsx(
|
|
51407
|
+
IsometricCanvas_default,
|
|
51408
|
+
{
|
|
51409
|
+
tiles: isoTiles,
|
|
51410
|
+
units: isoUnits,
|
|
51411
|
+
features: pathFeatures,
|
|
51412
|
+
validMoves,
|
|
51413
|
+
hoveredTile,
|
|
51414
|
+
onTileClick: handleTileClick,
|
|
51415
|
+
onTileHover: (x, y) => setHoveredTile({ x, y }),
|
|
51416
|
+
onTileLeave: () => setHoveredTile(null),
|
|
51417
|
+
scale
|
|
51418
|
+
}
|
|
51419
|
+
),
|
|
51420
|
+
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)` }) })
|
|
51421
|
+
] }),
|
|
51422
|
+
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: [
|
|
51423
|
+
/* @__PURE__ */ jsx(
|
|
51424
|
+
Typography,
|
|
51425
|
+
{
|
|
51426
|
+
variant: "h2",
|
|
51427
|
+
className: cn(
|
|
51428
|
+
"text-4xl font-black tracking-widest uppercase",
|
|
51429
|
+
result === "won" ? "text-warning" : "text-error"
|
|
51430
|
+
),
|
|
51431
|
+
children: result === "won" ? t("td.victory") ?? "Victory!" : t("td.defeat") ?? "Defeat!"
|
|
51432
|
+
}
|
|
51433
|
+
),
|
|
51434
|
+
/* @__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." }),
|
|
51435
|
+
/* @__PURE__ */ jsx(
|
|
51436
|
+
Button,
|
|
51437
|
+
{
|
|
51438
|
+
variant: "primary",
|
|
51439
|
+
className: "px-8 py-3 font-semibold",
|
|
51440
|
+
onClick: handlePlayAgain,
|
|
51441
|
+
children: t("td.playAgain") ?? "Play Again"
|
|
51442
|
+
}
|
|
51443
|
+
)
|
|
51444
|
+
] }) })
|
|
51445
|
+
] });
|
|
51446
|
+
}
|
|
51447
|
+
var CDN6, TERRAIN_SPRITES, TOWER_SPRITE, CREEP_SPRITE;
|
|
51448
|
+
var init_TowerDefenseBoard = __esm({
|
|
51449
|
+
"components/game/organisms/TowerDefenseBoard.tsx"() {
|
|
51450
|
+
"use client";
|
|
51451
|
+
init_cn();
|
|
51452
|
+
init_useEventBus();
|
|
51453
|
+
init_Box();
|
|
51454
|
+
init_Button();
|
|
51455
|
+
init_Typography();
|
|
51456
|
+
init_Stack();
|
|
51457
|
+
init_IsometricCanvas();
|
|
51458
|
+
init_boardEntity();
|
|
51459
|
+
CDN6 = "https://almadar-kflow-assets.web.app/shared/";
|
|
51460
|
+
TERRAIN_SPRITES = {
|
|
51461
|
+
ground: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_01.png`,
|
|
51462
|
+
path: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_09.png`,
|
|
51463
|
+
wall: `${CDN6}isometric-blocks/PNG/Platformer tiles/platformerTile_05.png`
|
|
51464
|
+
};
|
|
51465
|
+
TOWER_SPRITE = `${CDN6}units/guardian.png`;
|
|
51466
|
+
CREEP_SPRITE = `${CDN6}units/scrapper.png`;
|
|
51467
|
+
TowerDefenseBoard.displayName = "TowerDefenseBoard";
|
|
51468
|
+
}
|
|
51469
|
+
});
|
|
51470
|
+
function TowerDefenseTemplate({
|
|
51471
|
+
entity,
|
|
51472
|
+
title = "Tower Defense",
|
|
51473
|
+
tiles,
|
|
51474
|
+
path,
|
|
51475
|
+
towers,
|
|
51476
|
+
gold,
|
|
51477
|
+
lives,
|
|
51478
|
+
maxWaves,
|
|
51479
|
+
towerCost,
|
|
51480
|
+
scale,
|
|
51481
|
+
placeTowerEvent,
|
|
51482
|
+
startWaveEvent,
|
|
51483
|
+
playAgainEvent,
|
|
51484
|
+
gameEndEvent,
|
|
51485
|
+
className
|
|
51486
|
+
}) {
|
|
51487
|
+
const resolved = entity && typeof entity === "object" && !Array.isArray(entity) ? entity : void 0;
|
|
51488
|
+
return /* @__PURE__ */ jsxs(
|
|
51489
|
+
Box,
|
|
51490
|
+
{
|
|
51491
|
+
display: "flex",
|
|
51492
|
+
fullHeight: true,
|
|
51493
|
+
className: cn("tower-defense-template flex-col", className),
|
|
51494
|
+
children: [
|
|
51495
|
+
/* @__PURE__ */ jsx(
|
|
51496
|
+
HStack,
|
|
51497
|
+
{
|
|
51498
|
+
gap: "sm",
|
|
51499
|
+
align: "center",
|
|
51500
|
+
className: "px-4 py-3 border-b-2 border-border bg-surface shrink-0",
|
|
51501
|
+
children: /* @__PURE__ */ jsx(Typography, { variant: "h4", children: title })
|
|
51502
|
+
}
|
|
51503
|
+
),
|
|
51504
|
+
/* @__PURE__ */ jsx(Box, { className: "flex-1 relative overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
51505
|
+
TowerDefenseBoard,
|
|
51506
|
+
{
|
|
51507
|
+
entity: resolved,
|
|
51508
|
+
tiles,
|
|
51509
|
+
path,
|
|
51510
|
+
towers,
|
|
51511
|
+
gold,
|
|
51512
|
+
lives,
|
|
51513
|
+
maxWaves,
|
|
51514
|
+
towerCost,
|
|
51515
|
+
scale,
|
|
51516
|
+
placeTowerEvent,
|
|
51517
|
+
startWaveEvent,
|
|
51518
|
+
playAgainEvent,
|
|
51519
|
+
gameEndEvent,
|
|
51520
|
+
className: "h-full"
|
|
51521
|
+
}
|
|
51522
|
+
) })
|
|
51523
|
+
]
|
|
51524
|
+
}
|
|
51525
|
+
);
|
|
51526
|
+
}
|
|
51527
|
+
var init_TowerDefenseTemplate = __esm({
|
|
51528
|
+
"components/game/templates/TowerDefenseTemplate.tsx"() {
|
|
51529
|
+
init_cn();
|
|
51530
|
+
init_Box();
|
|
51531
|
+
init_Stack();
|
|
51532
|
+
init_Typography();
|
|
51533
|
+
init_TowerDefenseBoard();
|
|
51534
|
+
TowerDefenseTemplate.displayName = "TowerDefenseTemplate";
|
|
51535
|
+
}
|
|
51536
|
+
});
|
|
48995
51537
|
function useBattleState(initialUnits, eventConfig = {}, callbacks = {}) {
|
|
48996
51538
|
const eventBus = useEventBus();
|
|
48997
51539
|
const {
|
|
@@ -49190,17 +51732,12 @@ var init_UncontrolledBattleBoard = __esm({
|
|
|
49190
51732
|
}
|
|
49191
51733
|
});
|
|
49192
51734
|
function heroPosition(h) {
|
|
51735
|
+
if ("position" in h && h.position != null) return h.position;
|
|
49193
51736
|
return vec2(h.position);
|
|
49194
51737
|
}
|
|
49195
|
-
function heroOwner(h) {
|
|
49196
|
-
return str(h.owner);
|
|
49197
|
-
}
|
|
49198
51738
|
function heroMovement(h) {
|
|
49199
51739
|
return num(h.movement);
|
|
49200
51740
|
}
|
|
49201
|
-
function hexPassable(h) {
|
|
49202
|
-
return h.passable !== false;
|
|
49203
|
-
}
|
|
49204
51741
|
function defaultIsInRange(from, to, range) {
|
|
49205
51742
|
return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) <= range;
|
|
49206
51743
|
}
|
|
@@ -49236,38 +51773,41 @@ function WorldMapBoard({
|
|
|
49236
51773
|
}) {
|
|
49237
51774
|
const eventBus = useEventBus();
|
|
49238
51775
|
const resolved = boardEntity(entity);
|
|
49239
|
-
const
|
|
49240
|
-
const
|
|
51776
|
+
const entityUnits = rows(resolved?.units);
|
|
51777
|
+
const entityTiles = rows(resolved?.tiles);
|
|
49241
51778
|
const features = propFeatures ?? (Array.isArray(resolved?.features) ? resolved.features : []);
|
|
49242
51779
|
const selectedHeroId = resolved?.selectedHeroId ?? null;
|
|
49243
51780
|
const assetManifest = propAssetManifest ?? resolved?.assetManifest;
|
|
49244
51781
|
const backgroundImage = resolved?.backgroundImage;
|
|
49245
51782
|
const [hoveredTile, setHoveredTile] = useState(null);
|
|
49246
|
-
const selectedHero = useMemo(
|
|
49247
|
-
() => heroes.find((h) => str(h.id) === selectedHeroId) ?? null,
|
|
49248
|
-
[heroes, selectedHeroId]
|
|
49249
|
-
);
|
|
49250
51783
|
const derivedTiles = useMemo(
|
|
49251
|
-
() =>
|
|
49252
|
-
x: num(
|
|
49253
|
-
y: num(
|
|
49254
|
-
terrain: str(
|
|
49255
|
-
terrainSprite:
|
|
51784
|
+
() => entityTiles.map((t) => ({
|
|
51785
|
+
x: num(t.x),
|
|
51786
|
+
y: num(t.y),
|
|
51787
|
+
terrain: str(t.terrain),
|
|
51788
|
+
terrainSprite: t.terrainSprite == null ? void 0 : str(t.terrainSprite),
|
|
51789
|
+
passable: t.passable !== false
|
|
49256
51790
|
})),
|
|
49257
|
-
[
|
|
51791
|
+
[entityTiles]
|
|
49258
51792
|
);
|
|
49259
51793
|
const tiles = propTiles ?? derivedTiles;
|
|
49260
51794
|
const baseUnits = useMemo(
|
|
49261
|
-
() => propUnits ??
|
|
49262
|
-
id: str(
|
|
49263
|
-
position: heroPosition(
|
|
49264
|
-
name: str(
|
|
49265
|
-
|
|
49266
|
-
|
|
49267
|
-
|
|
49268
|
-
|
|
51795
|
+
() => propUnits ?? entityUnits.map((u) => ({
|
|
51796
|
+
id: str(u.id),
|
|
51797
|
+
position: heroPosition(u),
|
|
51798
|
+
name: str(u.name),
|
|
51799
|
+
// lolo uses `team` field (not `owner`)
|
|
51800
|
+
team: str(u.team) === "enemy" ? "enemy" : "player",
|
|
51801
|
+
health: num(u.health) || 100,
|
|
51802
|
+
maxHealth: num(u.maxHealth) || 100,
|
|
51803
|
+
sprite: u.sprite == null ? void 0 : str(u.sprite)
|
|
49269
51804
|
})),
|
|
49270
|
-
[
|
|
51805
|
+
[entityUnits, propUnits]
|
|
51806
|
+
);
|
|
51807
|
+
const gameUnits = baseUnits;
|
|
51808
|
+
const selectedHero = useMemo(
|
|
51809
|
+
() => gameUnits.find((u) => u.id === selectedHeroId) ?? null,
|
|
51810
|
+
[gameUnits, selectedHeroId]
|
|
49271
51811
|
);
|
|
49272
51812
|
const MOVE_SPEED_MS_PER_TILE = 300;
|
|
49273
51813
|
const movementAnimRef = useRef(null);
|
|
@@ -49313,51 +51853,50 @@ function WorldMapBoard({
|
|
|
49313
51853
|
const validMoves = useMemo(() => {
|
|
49314
51854
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
49315
51855
|
const sp = heroPosition(selectedHero);
|
|
49316
|
-
const
|
|
51856
|
+
const sTeam = str(selectedHero.team);
|
|
49317
51857
|
const range = heroMovement(selectedHero);
|
|
49318
51858
|
const moves = [];
|
|
49319
|
-
|
|
49320
|
-
const
|
|
49321
|
-
const
|
|
49322
|
-
if (
|
|
49323
|
-
if (
|
|
49324
|
-
if (!isInRange(sp, { x:
|
|
49325
|
-
if (
|
|
49326
|
-
const
|
|
49327
|
-
return
|
|
51859
|
+
tiles.forEach((t) => {
|
|
51860
|
+
const tx = t.x;
|
|
51861
|
+
const ty = t.y;
|
|
51862
|
+
if (t.passable === false) return;
|
|
51863
|
+
if (tx === sp.x && ty === sp.y) return;
|
|
51864
|
+
if (!isInRange(sp, { x: tx, y: ty }, range)) return;
|
|
51865
|
+
if (gameUnits.some((u) => {
|
|
51866
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
51867
|
+
return up.x === tx && up.y === ty && str(u.team) === sTeam;
|
|
49328
51868
|
})) return;
|
|
49329
|
-
moves.push({ x:
|
|
51869
|
+
moves.push({ x: tx, y: ty });
|
|
49330
51870
|
});
|
|
49331
51871
|
return moves;
|
|
49332
|
-
}, [selectedHero,
|
|
51872
|
+
}, [selectedHero, tiles, gameUnits, isInRange]);
|
|
49333
51873
|
const attackTargets = useMemo(() => {
|
|
49334
51874
|
if (!selectedHero || heroMovement(selectedHero) <= 0) return [];
|
|
49335
51875
|
const sp = heroPosition(selectedHero);
|
|
49336
|
-
const
|
|
51876
|
+
const sTeam = str(selectedHero.team);
|
|
49337
51877
|
const range = heroMovement(selectedHero);
|
|
49338
|
-
return
|
|
49339
|
-
}, [selectedHero,
|
|
49340
|
-
const maxY = Math.max(...
|
|
51878
|
+
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 });
|
|
51879
|
+
}, [selectedHero, gameUnits, isInRange]);
|
|
51880
|
+
const maxY = Math.max(...tiles.map((t) => t.y), 0);
|
|
49341
51881
|
const baseOffsetX = (maxY + 1) * (TILE_WIDTH * scale / 2);
|
|
49342
51882
|
const tileToScreen = useCallback(
|
|
49343
51883
|
(tx, ty) => isoToScreen(tx, ty, scale, baseOffsetX),
|
|
49344
51884
|
[scale, baseOffsetX]
|
|
49345
51885
|
);
|
|
49346
51886
|
const hoveredHex = useMemo(
|
|
49347
|
-
() => hoveredTile ?
|
|
49348
|
-
[hoveredTile,
|
|
51887
|
+
() => hoveredTile ? tiles.find((t) => t.x === hoveredTile.x && t.y === hoveredTile.y) ?? null : null,
|
|
51888
|
+
[hoveredTile, tiles]
|
|
49349
51889
|
);
|
|
49350
51890
|
const hoveredHero = useMemo(
|
|
49351
|
-
() => hoveredTile ?
|
|
49352
|
-
const
|
|
49353
|
-
return
|
|
51891
|
+
() => hoveredTile ? gameUnits.find((u) => {
|
|
51892
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
51893
|
+
return up.x === hoveredTile.x && up.y === hoveredTile.y;
|
|
49354
51894
|
}) ?? null : null,
|
|
49355
|
-
[hoveredTile,
|
|
51895
|
+
[hoveredTile, gameUnits]
|
|
49356
51896
|
);
|
|
49357
51897
|
const handleTileClick = useCallback((x, y) => {
|
|
49358
51898
|
if (movementAnimRef.current) return;
|
|
49359
|
-
const
|
|
49360
|
-
if (!hex) return;
|
|
51899
|
+
const tile = tiles.find((t) => t.x === x && t.y === y);
|
|
49361
51900
|
if (tileClickEvent) {
|
|
49362
51901
|
eventBus.emit(`UI:${tileClickEvent}`, { x, y });
|
|
49363
51902
|
}
|
|
@@ -49368,38 +51907,39 @@ function WorldMapBoard({
|
|
|
49368
51907
|
if (heroMoveEvent) {
|
|
49369
51908
|
eventBus.emit(`UI:${heroMoveEvent}`, { heroId, toX: x, toY: y });
|
|
49370
51909
|
}
|
|
49371
|
-
const feature = str(
|
|
51910
|
+
const feature = tile ? str(tile.feature) : "";
|
|
49372
51911
|
if (feature && feature !== "none") {
|
|
49373
|
-
|
|
51912
|
+
const tileRow = tile;
|
|
51913
|
+
onFeatureEnter?.(heroId, tileRow);
|
|
49374
51914
|
if (featureEnterEvent) {
|
|
49375
|
-
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex });
|
|
51915
|
+
eventBus.emit(`UI:${featureEnterEvent}`, { heroId, feature, hex: tileRow });
|
|
49376
51916
|
}
|
|
49377
51917
|
}
|
|
49378
51918
|
});
|
|
49379
51919
|
return;
|
|
49380
51920
|
}
|
|
49381
|
-
const enemy =
|
|
49382
|
-
const
|
|
49383
|
-
return
|
|
51921
|
+
const enemy = gameUnits.find((u) => {
|
|
51922
|
+
const up = u.position ?? { x: u.x ?? -1, y: u.y ?? -1 };
|
|
51923
|
+
return up.x === x && up.y === y && str(u.team) === "enemy";
|
|
49384
51924
|
});
|
|
49385
51925
|
if (selectedHero && enemy && attackTargets.some((t) => t.x === x && t.y === y)) {
|
|
49386
51926
|
const attackerId = str(selectedHero.id);
|
|
49387
|
-
const defenderId =
|
|
51927
|
+
const defenderId = enemy.id;
|
|
49388
51928
|
onBattleEncounter?.(attackerId, defenderId);
|
|
49389
51929
|
if (battleEncounterEvent) {
|
|
49390
51930
|
eventBus.emit(`UI:${battleEncounterEvent}`, { attackerId, defenderId });
|
|
49391
51931
|
}
|
|
49392
51932
|
}
|
|
49393
|
-
}, [
|
|
51933
|
+
}, [tiles, gameUnits, selectedHero, validMoves, attackTargets, startMoveAnimation, onHeroMove, onFeatureEnter, onBattleEncounter, eventBus, tileClickEvent, heroMoveEvent, featureEnterEvent, battleEncounterEvent]);
|
|
49394
51934
|
const handleUnitClick = useCallback((unitId) => {
|
|
49395
|
-
const
|
|
49396
|
-
if (
|
|
51935
|
+
const unit = gameUnits.find((u) => u.id === unitId);
|
|
51936
|
+
if (unit && (str(unit.team) === "player" || allowMoveAllHeroes)) {
|
|
49397
51937
|
onHeroSelect?.(unitId);
|
|
49398
51938
|
if (heroSelectEvent) {
|
|
49399
51939
|
eventBus.emit(`UI:${heroSelectEvent}`, { heroId: unitId });
|
|
49400
51940
|
}
|
|
49401
51941
|
}
|
|
49402
|
-
}, [
|
|
51942
|
+
}, [gameUnits, onHeroSelect, allowMoveAllHeroes, eventBus, heroSelectEvent]);
|
|
49403
51943
|
const selectHero = useCallback((id) => {
|
|
49404
51944
|
onHeroSelect?.(id);
|
|
49405
51945
|
if (heroSelectEvent) {
|
|
@@ -49503,48 +52043,48 @@ function WorldMapTemplate({
|
|
|
49503
52043
|
}
|
|
49504
52044
|
);
|
|
49505
52045
|
}
|
|
49506
|
-
var
|
|
52046
|
+
var CDN7, DEFAULT_WORLDMAP_TILES, DEFAULT_WORLDMAP_UNITS, DEFAULT_WORLDMAP_FEATURES, DEFAULT_WORLDMAP_MANIFEST;
|
|
49507
52047
|
var init_WorldMapTemplate = __esm({
|
|
49508
52048
|
"components/game/templates/WorldMapTemplate.tsx"() {
|
|
49509
52049
|
init_WorldMapBoard();
|
|
49510
|
-
|
|
52050
|
+
CDN7 = "https://almadar-kflow-assets.web.app/shared";
|
|
49511
52051
|
DEFAULT_WORLDMAP_TILES = [
|
|
49512
|
-
{ x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49513
|
-
{ x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49514
|
-
{ x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${
|
|
49515
|
-
{ x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49516
|
-
{ x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49517
|
-
{ x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49518
|
-
{ x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
49519
|
-
{ x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
49520
|
-
{ x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${
|
|
49521
|
-
{ x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${
|
|
49522
|
-
{ x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${
|
|
49523
|
-
{ x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
49524
|
-
{ x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
49525
|
-
{ x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${
|
|
49526
|
-
{ x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49527
|
-
{ x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49528
|
-
{ x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
49529
|
-
{ x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
49530
|
-
{ x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${
|
|
49531
|
-
{ x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49532
|
-
{ x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49533
|
-
{ x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${
|
|
49534
|
-
{ x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${
|
|
49535
|
-
{ x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
49536
|
-
{ x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${
|
|
52052
|
+
{ x: 0, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52053
|
+
{ x: 1, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52054
|
+
{ x: 2, y: 0, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
52055
|
+
{ x: 3, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52056
|
+
{ x: 4, y: 0, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52057
|
+
{ x: 0, y: 1, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52058
|
+
{ x: 1, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52059
|
+
{ x: 2, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52060
|
+
{ x: 3, y: 1, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52061
|
+
{ x: 4, y: 1, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
52062
|
+
{ x: 0, y: 2, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
52063
|
+
{ x: 1, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52064
|
+
{ x: 2, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
|
|
52065
|
+
{ x: 3, y: 2, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52066
|
+
{ x: 4, y: 2, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52067
|
+
{ x: 0, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52068
|
+
{ x: 1, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52069
|
+
{ x: 2, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52070
|
+
{ x: 3, y: 3, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_03.png` },
|
|
52071
|
+
{ x: 4, y: 3, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52072
|
+
{ x: 0, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52073
|
+
{ x: 1, y: 4, terrain: "water", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png` },
|
|
52074
|
+
{ x: 2, y: 4, terrain: "grass", passable: true, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png` },
|
|
52075
|
+
{ x: 3, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` },
|
|
52076
|
+
{ x: 4, y: 4, terrain: "mountain", passable: false, terrainSprite: `${CDN7}/isometric-blocks/PNG/Platformer tiles/platformerTile_13.png` }
|
|
49537
52077
|
];
|
|
49538
52078
|
DEFAULT_WORLDMAP_UNITS = [
|
|
49539
|
-
{ id: "h1", position: { x: 1, y: 1 }, unitType: "hero", name: "Amir", team: "player", health: 10, maxHealth: 10, sprite: `${
|
|
49540
|
-
{ id: "h2", position: { x: 3, y: 3 }, unitType: "scout", name: "Archivist", team: "player", health: 10, maxHealth: 10, sprite: `${
|
|
52079
|
+
{ 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` },
|
|
52080
|
+
{ 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` }
|
|
49541
52081
|
];
|
|
49542
52082
|
DEFAULT_WORLDMAP_FEATURES = [
|
|
49543
|
-
{ id: "f1", x: 2, y: 2, type: "capital", sprite: `${
|
|
49544
|
-
{ id: "f2", x: 4, y: 2, type: "power_node", sprite: `${
|
|
52083
|
+
{ id: "f1", x: 2, y: 2, type: "capital", sprite: `${CDN7}/scenes/world/capital.png` },
|
|
52084
|
+
{ id: "f2", x: 4, y: 2, type: "power_node", sprite: `${CDN7}/world-map/power_node.png` }
|
|
49545
52085
|
];
|
|
49546
52086
|
DEFAULT_WORLDMAP_MANIFEST = {
|
|
49547
|
-
baseUrl:
|
|
52087
|
+
baseUrl: CDN7,
|
|
49548
52088
|
terrains: {
|
|
49549
52089
|
grass: "/isometric-blocks/PNG/Platformer tiles/platformerTile_01.png",
|
|
49550
52090
|
water: "/isometric-blocks/PNG/Platformer tiles/platformerTile_11.png",
|
|
@@ -49589,7 +52129,7 @@ function lazyThree(name, loader) {
|
|
|
49589
52129
|
ThreeWrapper.displayName = `Lazy(${name})`;
|
|
49590
52130
|
return ThreeWrapper;
|
|
49591
52131
|
}
|
|
49592
|
-
var ThreeBoundary, FeatureRenderer,
|
|
52132
|
+
var ThreeBoundary, FeatureRenderer, GameCanvas3D2, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
|
|
49593
52133
|
var init_component_registry_generated = __esm({
|
|
49594
52134
|
"components/core/organisms/component-registry.generated.ts"() {
|
|
49595
52135
|
init_AboutPageTemplate();
|
|
@@ -49699,6 +52239,7 @@ var init_component_registry_generated = __esm({
|
|
|
49699
52239
|
init_FormSectionHeader();
|
|
49700
52240
|
init_GameAudioProvider();
|
|
49701
52241
|
init_GameAudioToggle();
|
|
52242
|
+
init_GameBoard3D();
|
|
49702
52243
|
init_GameCanvas2D();
|
|
49703
52244
|
init_GameHud();
|
|
49704
52245
|
init_GameMenu();
|
|
@@ -49756,7 +52297,9 @@ var init_component_registry_generated = __esm({
|
|
|
49756
52297
|
init_PageHeader();
|
|
49757
52298
|
init_Pagination();
|
|
49758
52299
|
init_PatternTile();
|
|
52300
|
+
init_PlatformerBoard();
|
|
49759
52301
|
init_PlatformerCanvas();
|
|
52302
|
+
init_PlatformerTemplate();
|
|
49760
52303
|
init_Popover();
|
|
49761
52304
|
init_PositionedCanvas();
|
|
49762
52305
|
init_PowerupSlots();
|
|
@@ -49780,6 +52323,8 @@ var init_component_registry_generated = __esm({
|
|
|
49780
52323
|
init_ResourceBar();
|
|
49781
52324
|
init_ResourceCounter();
|
|
49782
52325
|
init_RichBlockEditor();
|
|
52326
|
+
init_RoguelikeBoard();
|
|
52327
|
+
init_RoguelikeTemplate();
|
|
49783
52328
|
init_RuntimeDebugger2();
|
|
49784
52329
|
init_ScaledDiagram();
|
|
49785
52330
|
init_ScoreBoard();
|
|
@@ -49852,6 +52397,8 @@ var init_component_registry_generated = __esm({
|
|
|
49852
52397
|
init_Toast();
|
|
49853
52398
|
init_ToastSlot();
|
|
49854
52399
|
init_Tooltip();
|
|
52400
|
+
init_TowerDefenseBoard();
|
|
52401
|
+
init_TowerDefenseTemplate();
|
|
49855
52402
|
init_TraitFrame();
|
|
49856
52403
|
init_TraitSlot();
|
|
49857
52404
|
init_TrendIndicator();
|
|
@@ -49896,7 +52443,7 @@ var init_component_registry_generated = __esm({
|
|
|
49896
52443
|
}
|
|
49897
52444
|
};
|
|
49898
52445
|
FeatureRenderer = lazyThree("FeatureRenderer", () => import('@almadar/ui/components/molecules/game/three'));
|
|
49899
|
-
|
|
52446
|
+
GameCanvas3D2 = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
|
|
49900
52447
|
GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
49901
52448
|
GameCanvas3DCastleTemplate = lazyThree("GameCanvas3DCastleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
49902
52449
|
GameCanvas3DWorldMapTemplate = lazyThree("GameCanvas3DWorldMapTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
@@ -50015,8 +52562,9 @@ var init_component_registry_generated = __esm({
|
|
|
50015
52562
|
"FormSectionHeader": FormSectionHeader,
|
|
50016
52563
|
"GameAudioProvider": GameAudioProvider,
|
|
50017
52564
|
"GameAudioToggle": GameAudioToggle,
|
|
52565
|
+
"GameBoard3D": GameBoard3D,
|
|
50018
52566
|
"GameCanvas2D": GameCanvas2D,
|
|
50019
|
-
"GameCanvas3D":
|
|
52567
|
+
"GameCanvas3D": GameCanvas3D2,
|
|
50020
52568
|
"GameCanvas3DBattleTemplate": GameCanvas3DBattleTemplate,
|
|
50021
52569
|
"GameCanvas3DCastleTemplate": GameCanvas3DCastleTemplate,
|
|
50022
52570
|
"GameCanvas3DWorldMapTemplate": GameCanvas3DWorldMapTemplate,
|
|
@@ -50078,7 +52626,9 @@ var init_component_registry_generated = __esm({
|
|
|
50078
52626
|
"PageHeader": PageHeader,
|
|
50079
52627
|
"Pagination": Pagination,
|
|
50080
52628
|
"PatternTile": PatternTile,
|
|
52629
|
+
"PlatformerBoard": PlatformerBoard,
|
|
50081
52630
|
"PlatformerCanvas": PlatformerCanvas,
|
|
52631
|
+
"PlatformerTemplate": PlatformerTemplate,
|
|
50082
52632
|
"Popover": Popover,
|
|
50083
52633
|
"PositionedCanvas": PositionedCanvas,
|
|
50084
52634
|
"PowerupSlots": PowerupSlots,
|
|
@@ -50102,6 +52652,8 @@ var init_component_registry_generated = __esm({
|
|
|
50102
52652
|
"ResourceBar": ResourceBar,
|
|
50103
52653
|
"ResourceCounter": ResourceCounter,
|
|
50104
52654
|
"RichBlockEditor": RichBlockEditor,
|
|
52655
|
+
"RoguelikeBoard": RoguelikeBoard,
|
|
52656
|
+
"RoguelikeTemplate": RoguelikeTemplate,
|
|
50105
52657
|
"RuntimeDebugger": RuntimeDebugger,
|
|
50106
52658
|
"ScaledDiagram": ScaledDiagram,
|
|
50107
52659
|
"ScoreBoard": ScoreBoard,
|
|
@@ -50177,6 +52729,8 @@ var init_component_registry_generated = __esm({
|
|
|
50177
52729
|
"Toast": Toast,
|
|
50178
52730
|
"ToastSlot": ToastSlot,
|
|
50179
52731
|
"Tooltip": Tooltip,
|
|
52732
|
+
"TowerDefenseBoard": TowerDefenseBoard,
|
|
52733
|
+
"TowerDefenseTemplate": TowerDefenseTemplate,
|
|
50180
52734
|
"TraitFrame": TraitFrame,
|
|
50181
52735
|
"TraitSlot": TraitSlot,
|
|
50182
52736
|
"TrendIndicator": TrendIndicator,
|
|
@@ -54663,7 +57217,7 @@ function convertObjectProps(props) {
|
|
|
54663
57217
|
function convertFnFormLambdasInProps(props) {
|
|
54664
57218
|
return convertObjectProps(props);
|
|
54665
57219
|
}
|
|
54666
|
-
var
|
|
57220
|
+
var log11 = createLogger("almadar:ui:effects:client-handlers");
|
|
54667
57221
|
function createClientEffectHandlers(options) {
|
|
54668
57222
|
const { eventBus, slotSetter, navigate, notify, callService } = options;
|
|
54669
57223
|
return {
|
|
@@ -54672,10 +57226,10 @@ function createClientEffectHandlers(options) {
|
|
|
54672
57226
|
eventBus.emit(prefixedEvent, payload);
|
|
54673
57227
|
},
|
|
54674
57228
|
persist: async () => {
|
|
54675
|
-
|
|
57229
|
+
log11.warn("persist is server-side only, ignored on client");
|
|
54676
57230
|
},
|
|
54677
57231
|
set: () => {
|
|
54678
|
-
|
|
57232
|
+
log11.warn("set is server-side only, ignored on client");
|
|
54679
57233
|
},
|
|
54680
57234
|
callService: async (service, action, params) => {
|
|
54681
57235
|
if (callService) return callService(service, action, params);
|
|
@@ -54704,10 +57258,10 @@ function createClientEffectHandlers(options) {
|
|
|
54704
57258
|
slotSetter.addPattern(slot, pattern, props);
|
|
54705
57259
|
},
|
|
54706
57260
|
navigate: navigate ?? ((path) => {
|
|
54707
|
-
|
|
57261
|
+
log11.warn("No navigate handler, ignoring", { path });
|
|
54708
57262
|
}),
|
|
54709
57263
|
notify: notify ?? ((msg, type) => {
|
|
54710
|
-
|
|
57264
|
+
log11.debug("notify", { type, message: msg });
|
|
54711
57265
|
})
|
|
54712
57266
|
};
|
|
54713
57267
|
}
|
|
@@ -55872,7 +58426,7 @@ OrbitalThemeProvider.displayName = "OrbitalThemeProvider";
|
|
|
55872
58426
|
init_navigation();
|
|
55873
58427
|
init_verificationRegistry();
|
|
55874
58428
|
var PERF_NAMESPACE = "almadar:perf:canvas";
|
|
55875
|
-
var
|
|
58429
|
+
var log12 = createLogger(PERF_NAMESPACE);
|
|
55876
58430
|
var RING_SIZE = 50;
|
|
55877
58431
|
var ring = [];
|
|
55878
58432
|
var writeIdx = 0;
|
|
@@ -55923,7 +58477,7 @@ function perfEnd(name, startToken, detail) {
|
|
|
55923
58477
|
}
|
|
55924
58478
|
}
|
|
55925
58479
|
push({ name, durationMs, ts: endTs, detail });
|
|
55926
|
-
|
|
58480
|
+
log12.debug(name, () => ({ durationMs, ...detail ?? {} }));
|
|
55927
58481
|
}
|
|
55928
58482
|
var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, commitTime) => {
|
|
55929
58483
|
if (!isEnabled()) return;
|
|
@@ -55933,7 +58487,7 @@ var profilerOnRender = (id, phase, actualDuration, baseDuration, _startTime, com
|
|
|
55933
58487
|
ts: commitTime,
|
|
55934
58488
|
detail: { baseDuration }
|
|
55935
58489
|
});
|
|
55936
|
-
|
|
58490
|
+
log12.debug(`profiler:${id}:${phase}`, () => ({ actualDuration, baseDuration }));
|
|
55937
58491
|
};
|
|
55938
58492
|
|
|
55939
58493
|
// runtime/prepareSchemaForPreview.ts
|
|
@@ -56576,7 +59130,7 @@ init_useEventBus();
|
|
|
56576
59130
|
// components/avl/molecules/useCanvasDnd.tsx
|
|
56577
59131
|
init_useEventBus();
|
|
56578
59132
|
init_useAlmadarDndCollision();
|
|
56579
|
-
var
|
|
59133
|
+
var log13 = createLogger("almadar:ui:canvas-dnd");
|
|
56580
59134
|
function useCanvasDraggable({
|
|
56581
59135
|
id,
|
|
56582
59136
|
payload,
|
|
@@ -56615,7 +59169,7 @@ function defaultEmit(eventBus, drop) {
|
|
|
56615
59169
|
if (payload.kind === "pattern") {
|
|
56616
59170
|
const patternType = payload.data.type;
|
|
56617
59171
|
if (typeof patternType !== "string") {
|
|
56618
|
-
|
|
59172
|
+
log13.warn("default-emit:pattern:missing-type");
|
|
56619
59173
|
return;
|
|
56620
59174
|
}
|
|
56621
59175
|
const out = { patternType, containerNode: target.containerNode };
|
|
@@ -56624,23 +59178,23 @@ function defaultEmit(eventBus, drop) {
|
|
|
56624
59178
|
out.index = resolved.index;
|
|
56625
59179
|
}
|
|
56626
59180
|
eventBus.emit("UI:PATTERN_DROP", out);
|
|
56627
|
-
|
|
59181
|
+
log13.info("default-emit:pattern", { patternType, level: target.level });
|
|
56628
59182
|
return;
|
|
56629
59183
|
}
|
|
56630
59184
|
if (payload.kind === "behavior") {
|
|
56631
59185
|
const behaviorName = payload.data.name;
|
|
56632
59186
|
if (typeof behaviorName !== "string") {
|
|
56633
|
-
|
|
59187
|
+
log13.warn("default-emit:behavior:missing-name");
|
|
56634
59188
|
return;
|
|
56635
59189
|
}
|
|
56636
59190
|
eventBus.emit("UI:BEHAVIOR_DROP", {
|
|
56637
59191
|
behaviorName,
|
|
56638
59192
|
containerNode: target.containerNode
|
|
56639
59193
|
});
|
|
56640
|
-
|
|
59194
|
+
log13.info("default-emit:behavior", { behaviorName, level: target.level });
|
|
56641
59195
|
return;
|
|
56642
59196
|
}
|
|
56643
|
-
|
|
59197
|
+
log13.debug("default-emit:unhandled-kind", { kind: payload.kind });
|
|
56644
59198
|
}
|
|
56645
59199
|
function CanvasDndProvider({
|
|
56646
59200
|
children,
|
|
@@ -56656,9 +59210,9 @@ function CanvasDndProvider({
|
|
|
56656
59210
|
if (payload) {
|
|
56657
59211
|
setActivePayload(payload);
|
|
56658
59212
|
eventBus.emit("UI:DRAG_START", { kind: payload.kind, data: payload.data });
|
|
56659
|
-
|
|
59213
|
+
log13.info("dragStart", { id: e.active.id, kind: payload.kind });
|
|
56660
59214
|
} else {
|
|
56661
|
-
|
|
59215
|
+
log13.warn("dragStart:missing-payload", { id: e.active.id });
|
|
56662
59216
|
}
|
|
56663
59217
|
}, [eventBus]);
|
|
56664
59218
|
const handleDragEnd = React91__default.useCallback((e) => {
|
|
@@ -56668,7 +59222,7 @@ function CanvasDndProvider({
|
|
|
56668
59222
|
const overData = e.over?.data.current;
|
|
56669
59223
|
const target = overData?.target;
|
|
56670
59224
|
const accepts = overData?.accepts;
|
|
56671
|
-
|
|
59225
|
+
log13.info("dragEnd", {
|
|
56672
59226
|
activeId: e.active.id,
|
|
56673
59227
|
overId: e.over?.id,
|
|
56674
59228
|
hasPayload: !!payload,
|
|
@@ -56680,7 +59234,7 @@ function CanvasDndProvider({
|
|
|
56680
59234
|
}
|
|
56681
59235
|
if (!payload || !target) return;
|
|
56682
59236
|
if (accepts && !accepts.includes(payload.kind)) {
|
|
56683
|
-
|
|
59237
|
+
log13.debug("dragEnd:rejected:kind", { kind: payload.kind, accepts: [...accepts] });
|
|
56684
59238
|
return;
|
|
56685
59239
|
}
|
|
56686
59240
|
const activator = e.activatorEvent;
|
|
@@ -56692,7 +59246,7 @@ function CanvasDndProvider({
|
|
|
56692
59246
|
}, [eventBus, onDrop]);
|
|
56693
59247
|
const handleDragCancel = React91__default.useCallback(() => {
|
|
56694
59248
|
setActivePayload(null);
|
|
56695
|
-
|
|
59249
|
+
log13.info("dragCancel");
|
|
56696
59250
|
}, []);
|
|
56697
59251
|
return /* @__PURE__ */ jsxs(
|
|
56698
59252
|
DndContext,
|
|
@@ -58507,7 +61061,7 @@ init_AvlTransitionLane();
|
|
|
58507
61061
|
init_AvlSwimLane();
|
|
58508
61062
|
init_types();
|
|
58509
61063
|
init_avl_elk_layout();
|
|
58510
|
-
var
|
|
61064
|
+
var log14 = createLogger("almadar:ui:avl:trait-scene");
|
|
58511
61065
|
var SWIM_GUTTER2 = 120;
|
|
58512
61066
|
var CENTER_W2 = 360;
|
|
58513
61067
|
var AvlTraitScene = ({
|
|
@@ -58520,7 +61074,7 @@ var AvlTraitScene = ({
|
|
|
58520
61074
|
const dataKey = useMemo(() => JSON.stringify(data), [data]);
|
|
58521
61075
|
useEffect(() => {
|
|
58522
61076
|
computeTraitLayout(data).then(setLayout).catch((error) => {
|
|
58523
|
-
|
|
61077
|
+
log14.error("computeTraitLayout failed", { error: error instanceof Error ? error : String(error) });
|
|
58524
61078
|
});
|
|
58525
61079
|
}, [dataKey]);
|
|
58526
61080
|
if (!layout) {
|