@almadar/ui 5.104.0 → 5.107.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 +1911 -4127
- package/dist/avl/index.js +1006 -3222
- package/dist/components/core/atoms/index.d.ts +1 -1
- package/dist/components/core/molecules/index.d.ts +1 -1
- package/dist/components/core/organisms/index.d.ts +1 -1
- package/dist/components/core/templates/index.d.ts +2 -2
- package/dist/components/game/{2d/atoms → atoms}/ActionButton.d.ts +1 -1
- package/dist/components/game/{2d/atoms → atoms}/ChoiceButton.d.ts +1 -1
- package/dist/components/game/{2d/atoms → atoms}/ControlButton.d.ts +1 -1
- package/dist/components/game/atoms/DrawShape.d.ts +50 -0
- package/dist/components/game/atoms/DrawSprite.d.ts +47 -0
- package/dist/components/game/atoms/DrawText.d.ts +31 -0
- package/dist/components/game/{2d/atoms → atoms}/GameAudioToggle.d.ts +1 -1
- package/dist/components/game/{2d/atoms → atoms}/GameIcon.d.ts +1 -1
- package/dist/components/game/{2d/atoms → atoms}/ItemSlot.d.ts +1 -1
- package/dist/components/game/{2d/atoms → atoms}/MiniMap.d.ts +1 -1
- package/dist/components/game/{2d/atoms → atoms}/ResourceCounter.d.ts +2 -2
- package/dist/components/game/{2d/atoms → atoms}/ScoreDisplay.d.ts +1 -1
- package/dist/components/game/{2d/atoms → atoms}/Sprite.d.ts +1 -1
- package/dist/components/game/{2d/atoms → atoms}/StateIndicator.d.ts +1 -1
- package/dist/components/game/{2d/atoms → atoms}/StatusEffect.d.ts +1 -1
- package/dist/components/game/{2d/atoms → atoms}/WaypointMarker.d.ts +1 -1
- package/dist/components/game/{shared/hooks → hooks}/useCamera.d.ts +1 -1
- package/dist/components/game/{shared/hooks → hooks}/useUnitSpriteAtlas.d.ts +2 -2
- package/dist/components/game/{shared → lib}/index.d.ts +1 -2
- package/dist/components/game/{shared → lib}/isometric.d.ts +38 -0
- package/dist/components/game/{shared → lib}/isometricTypes.d.ts +4 -0
- package/dist/components/game/{shared/lib → lib}/puzzleObject.d.ts +1 -1
- package/dist/components/game/{2d/molecules → molecules}/ActionTile.d.ts +1 -1
- package/dist/components/game/molecules/Canvas.d.ts +96 -0
- package/dist/components/game/molecules/Canvas2D.d.ts +120 -0
- package/dist/components/game/{2d/molecules → molecules}/ControlGrid.d.ts +1 -1
- package/dist/components/game/molecules/DrawShapeLayer.d.ts +19 -0
- package/dist/components/game/molecules/DrawSpriteLayer.d.ts +21 -0
- package/dist/components/game/molecules/DrawTextLayer.d.ts +22 -0
- package/dist/components/game/{2d/molecules → molecules}/GameHud.d.ts +1 -1
- package/dist/components/game/{2d/molecules → molecules}/InventoryGrid.d.ts +1 -1
- package/dist/components/game/{2d/molecules → molecules}/ResourceBar.d.ts +1 -1
- package/dist/components/game/{2d/molecules → molecules}/StatBadge.d.ts +1 -1
- package/dist/components/game/{2d/molecules → molecules}/StateNode.d.ts +1 -1
- package/dist/components/game/{2d/molecules → molecules}/TraitSlot.d.ts +1 -1
- package/dist/components/game/{2d/molecules → molecules}/TraitStateViewer.d.ts +1 -1
- package/dist/components/game/{2d/molecules → molecules}/TransitionArrow.d.ts +1 -1
- package/dist/components/game/{2d/molecules → molecules}/index.d.ts +9 -16
- package/dist/components/game/patterns.d.ts +20 -0
- package/dist/components/game/{shared/providers → providers}/GameAudioProvider.d.ts +1 -1
- package/dist/components/game/{2d/templates → templates}/GameTemplate.d.ts +1 -1
- package/dist/components/game/three/atoms/SpriteBillboard3D.d.ts +18 -0
- package/dist/components/game/three/game3dTheme.d.ts +55 -0
- package/dist/components/game/{shared → three}/hooks/useGameCanvas3DEvents.d.ts +4 -3
- package/dist/components/game/{3d → three}/index.cjs +787 -2986
- package/dist/components/game/{3d → three}/index.css +3 -3
- package/dist/components/game/{3d → three}/index.d.ts +0 -1
- package/dist/components/game/{3d → three}/index.js +644 -2842
- package/dist/components/game/{shared → three}/lib/game3dAssetManifest.d.ts +1 -1
- package/dist/components/game/three/molecules/GameCamera3D.d.ts +35 -0
- package/dist/components/game/three/molecules/GameCanvas3D.d.ts +175 -0
- package/dist/components/game/{3d → three}/molecules/Lighting3D.d.ts +10 -2
- package/dist/components/game/{3d → three}/molecules/index.d.ts +9 -12
- package/dist/components/game/{3d → three}/patterns.d.ts +0 -4
- package/dist/components/index.cjs +2326 -4362
- package/dist/components/index.js +1420 -3449
- package/dist/lib/drawable/Drawable3D.d.ts +20 -0
- package/dist/lib/drawable/contract.d.ts +68 -0
- package/dist/lib/drawable/hitTest.d.ts +23 -0
- package/dist/lib/drawable/mesh3d.d.ts +28 -0
- package/dist/lib/drawable/paintDispatch.d.ts +22 -0
- package/dist/lib/drawable/projector.d.ts +14 -0
- package/dist/lib/drawable/projector3d.d.ts +20 -0
- package/dist/lib/imageCache.d.ts +9 -0
- package/dist/lib/painter2d.d.ts +81 -0
- package/dist/lib/webPainter2d.d.ts +7 -0
- package/dist/marketing/index.cjs +10 -0
- package/dist/marketing/index.js +10 -0
- package/dist/providers/index.cjs +1756 -3976
- package/dist/providers/index.js +964 -3184
- package/dist/runtime/index.cjs +1766 -3982
- package/dist/runtime/index.js +980 -3196
- package/package.json +13 -13
- package/dist/components/game/2d/molecules/Canvas2D.d.ts +0 -175
- package/dist/components/game/2d/organisms/BuilderBoard.d.ts +0 -60
- package/dist/components/game/2d/organisms/ClassifierBoard.d.ts +0 -66
- package/dist/components/game/2d/organisms/DebuggerBoard.d.ts +0 -61
- package/dist/components/game/2d/organisms/EventHandlerBoard.d.ts +0 -42
- package/dist/components/game/2d/organisms/NegotiatorBoard.d.ts +0 -56
- package/dist/components/game/2d/organisms/SequencerBoard.d.ts +0 -69
- package/dist/components/game/2d/organisms/SimulatorBoard.d.ts +0 -58
- package/dist/components/game/2d/organisms/StateArchitectBoard.d.ts +0 -61
- package/dist/components/game/3d/molecules/GameCanvas3D.d.ts +0 -259
- package/dist/components/game/3d/organisms/GameBoard3D.d.ts +0 -66
- package/dist/components/game/3d/templates/GameCanvas3DBattleTemplate.d.ts +0 -82
- package/dist/components/game/3d/templates/GameCanvas3DCastleTemplate.d.ts +0 -87
- package/dist/components/game/3d/templates/GameCanvas3DWorldMapTemplate.d.ts +0 -95
- package/dist/components/game/shared/effects.d.ts +0 -253
- /package/dist/components/game/{2d/atoms → atoms}/ComboCounter.d.ts +0 -0
- /package/dist/components/game/{2d/atoms → atoms}/DamageNumber.d.ts +0 -0
- /package/dist/components/game/{2d/atoms → atoms}/DialogueBubble.d.ts +0 -0
- /package/dist/components/game/{2d/atoms → atoms}/GameCard.d.ts +0 -0
- /package/dist/components/game/{2d/atoms → atoms}/HealthBar.d.ts +0 -0
- /package/dist/components/game/{2d/atoms → atoms}/TimerDisplay.d.ts +0 -0
- /package/dist/components/game/{2d/atoms → atoms}/TurnIndicator.d.ts +0 -0
- /package/dist/components/game/{shared/hooks → hooks}/useGameAudio.d.ts +0 -0
- /package/dist/components/game/{shared → hooks}/useImageCache.d.ts +0 -0
- /package/dist/components/game/{shared → lib}/boardEntity.d.ts +0 -0
- /package/dist/components/game/{shared/lib → lib}/editorUtils.d.ts +0 -0
- /package/dist/components/game/{shared → lib}/game.d.ts +0 -0
- /package/dist/components/game/{shared → lib}/makeAsset.d.ts +0 -0
- /package/dist/components/game/{shared/lib → lib}/mechanics.d.ts +0 -0
- /package/dist/components/game/{shared/lib → lib}/physicsPresets.d.ts +0 -0
- /package/dist/components/game/{shared/lib → lib}/physicsTypes.d.ts +0 -0
- /package/dist/components/game/{shared → lib}/spriteAnimation.d.ts +0 -0
- /package/dist/components/game/{shared → lib}/spriteAnimationTypes.d.ts +0 -0
- /package/dist/components/game/{shared → lib}/spriteSheetConstants.d.ts +0 -0
- /package/dist/components/game/{2d/molecules → molecules}/ActionPalette.d.ts +0 -0
- /package/dist/components/game/{2d/molecules → molecules}/EventLog.d.ts +0 -0
- /package/dist/components/game/{2d/molecules → molecules}/GameMenu.d.ts +0 -0
- /package/dist/components/game/{2d/molecules → molecules}/ObjectRulePanel.d.ts +0 -0
- /package/dist/components/game/{2d/molecules → molecules}/RuleEditor.d.ts +0 -0
- /package/dist/components/game/{2d/molecules → molecules}/SequenceBar.d.ts +0 -0
- /package/dist/components/game/{2d/molecules → molecules}/StateGraph.d.ts +0 -0
- /package/dist/components/game/{2d/molecules → molecules}/StateJsonView.d.ts +0 -0
- /package/dist/components/game/{2d/molecules → molecules}/VariablePanel.d.ts +0 -0
- /package/dist/components/game/{2d/templates → templates}/GameShell.d.ts +0 -0
- /package/dist/components/game/{shared → three}/hooks/useAssetLoader.d.ts +0 -0
- /package/dist/components/game/{shared → three}/hooks/useRaycaster.d.ts +0 -0
- /package/dist/components/game/{shared → three}/hooks/useSceneGraph.d.ts +0 -0
- /package/dist/components/game/{shared → three}/hooks/useThree.d.ts +0 -0
- /package/dist/components/game/{shared → three}/lib/AssetLoader.d.ts +0 -0
- /package/dist/components/game/{shared → three}/lib/culling.d.ts +0 -0
- /package/dist/components/game/{shared → three}/lib/grid3D.d.ts +0 -0
- /package/dist/components/game/{3d → three}/molecules/Camera3D.d.ts +0 -0
- /package/dist/components/game/{3d → three}/molecules/Canvas3DErrorBoundary.d.ts +0 -0
- /package/dist/components/game/{3d → three}/molecules/Canvas3DLoadingState.d.ts +0 -0
- /package/dist/components/game/{3d → three}/molecules/ModelLoader.d.ts +0 -0
- /package/dist/components/game/{3d → three}/molecules/Scene3D.d.ts +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var React7 = require('react');
|
|
4
4
|
var fiber = require('@react-three/fiber');
|
|
5
5
|
var THREE6 = require('three');
|
|
6
6
|
var jsxRuntime = require('react/jsx-runtime');
|
|
@@ -14,10 +14,11 @@ var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
|
|
|
14
14
|
var providers = require('@almadar/ui/providers');
|
|
15
15
|
var clsx = require('clsx');
|
|
16
16
|
var tailwindMerge = require('tailwind-merge');
|
|
17
|
-
var LucideIcons = require('lucide-react');
|
|
18
17
|
var postprocessing = require('@react-three/postprocessing');
|
|
19
18
|
var hooks = require('@almadar/ui/hooks');
|
|
20
19
|
|
|
20
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
21
|
+
|
|
21
22
|
function _interopNamespace(e) {
|
|
22
23
|
if (e && e.__esModule) return e;
|
|
23
24
|
var n = Object.create(null);
|
|
@@ -36,17 +37,16 @@ function _interopNamespace(e) {
|
|
|
36
37
|
return Object.freeze(n);
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
var
|
|
40
|
+
var React7__default = /*#__PURE__*/_interopDefault(React7);
|
|
40
41
|
var THREE6__namespace = /*#__PURE__*/_interopNamespace(THREE6);
|
|
41
|
-
var LucideIcons__namespace = /*#__PURE__*/_interopNamespace(LucideIcons);
|
|
42
42
|
|
|
43
43
|
var __defProp = Object.defineProperty;
|
|
44
44
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
45
45
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
46
46
|
function Scene3D({ background = "#1a1a2e", fog, children }) {
|
|
47
47
|
const { scene } = fiber.useThree();
|
|
48
|
-
const initializedRef =
|
|
49
|
-
|
|
48
|
+
const initializedRef = React7.useRef(false);
|
|
49
|
+
React7.useEffect(() => {
|
|
50
50
|
if (initializedRef.current) return;
|
|
51
51
|
initializedRef.current = true;
|
|
52
52
|
if (background.startsWith("#") || background.startsWith("rgb")) {
|
|
@@ -67,7 +67,7 @@ function Scene3D({ background = "#1a1a2e", fog, children }) {
|
|
|
67
67
|
}, [scene, background, fog]);
|
|
68
68
|
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
|
|
69
69
|
}
|
|
70
|
-
var Camera3D =
|
|
70
|
+
var Camera3D = React7.forwardRef(
|
|
71
71
|
({
|
|
72
72
|
mode = "isometric",
|
|
73
73
|
position = [10, 10, 10],
|
|
@@ -80,10 +80,10 @@ var Camera3D = React8.forwardRef(
|
|
|
80
80
|
onChange
|
|
81
81
|
}, ref) => {
|
|
82
82
|
const { camera, set, viewport } = fiber.useThree();
|
|
83
|
-
const controlsRef =
|
|
84
|
-
const initialPosition =
|
|
85
|
-
const initialTarget =
|
|
86
|
-
|
|
83
|
+
const controlsRef = React7.useRef(null);
|
|
84
|
+
const initialPosition = React7.useRef(new THREE6__namespace.Vector3(...position));
|
|
85
|
+
const initialTarget = React7.useRef(new THREE6__namespace.Vector3(...target));
|
|
86
|
+
React7.useEffect(() => {
|
|
87
87
|
let newCamera;
|
|
88
88
|
if (mode === "isometric") {
|
|
89
89
|
const aspect = viewport.aspect;
|
|
@@ -114,7 +114,7 @@ var Camera3D = React8.forwardRef(
|
|
|
114
114
|
onChange(camera);
|
|
115
115
|
}
|
|
116
116
|
});
|
|
117
|
-
|
|
117
|
+
React7.useImperativeHandle(ref, () => ({
|
|
118
118
|
getCamera: () => camera,
|
|
119
119
|
setPosition: (x, y, z) => {
|
|
120
120
|
camera.position.set(x, y, z);
|
|
@@ -170,6 +170,10 @@ function Lighting3D({
|
|
|
170
170
|
shadows = true,
|
|
171
171
|
shadowMapSize = 2048,
|
|
172
172
|
shadowCameraSize = 20,
|
|
173
|
+
shadowBias = -1e-3,
|
|
174
|
+
shadowNormalBias,
|
|
175
|
+
shadowCameraNear = 0.1,
|
|
176
|
+
shadowCameraFar = 100,
|
|
173
177
|
showHelpers = false
|
|
174
178
|
}) {
|
|
175
179
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
@@ -186,9 +190,10 @@ function Lighting3D({
|
|
|
186
190
|
"shadow-camera-right": shadowCameraSize,
|
|
187
191
|
"shadow-camera-top": shadowCameraSize,
|
|
188
192
|
"shadow-camera-bottom": -shadowCameraSize,
|
|
189
|
-
"shadow-camera-near":
|
|
190
|
-
"shadow-camera-far":
|
|
191
|
-
"shadow-bias":
|
|
193
|
+
"shadow-camera-near": shadowCameraNear,
|
|
194
|
+
"shadow-camera-far": shadowCameraFar,
|
|
195
|
+
"shadow-bias": shadowBias,
|
|
196
|
+
"shadow-normalBias": shadowNormalBias
|
|
192
197
|
}
|
|
193
198
|
),
|
|
194
199
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -256,7 +261,7 @@ function Canvas3DLoadingState({
|
|
|
256
261
|
] });
|
|
257
262
|
}
|
|
258
263
|
var log = logger.createLogger("almadar:ui:game:canvas3d:error-boundary");
|
|
259
|
-
var Canvas3DErrorBoundary = class extends
|
|
264
|
+
var Canvas3DErrorBoundary = class extends React7.Component {
|
|
260
265
|
constructor(props) {
|
|
261
266
|
super(props);
|
|
262
267
|
__publicField(this, "handleReset", () => {
|
|
@@ -371,10 +376,10 @@ function stateFromEntry(entry) {
|
|
|
371
376
|
return { model: null, clips: [], isLoading: !entry.error, error: entry.error ?? null };
|
|
372
377
|
}
|
|
373
378
|
function useGLTFModel(url, resourceBasePath) {
|
|
374
|
-
const [state, setState] =
|
|
379
|
+
const [state, setState] = React7.useState(
|
|
375
380
|
() => stateFromEntry(url ? loadGltfCached(url, resourceBasePath || detectAssetRoot(url)) : null)
|
|
376
381
|
);
|
|
377
|
-
|
|
382
|
+
React7.useEffect(() => {
|
|
378
383
|
if (!url) {
|
|
379
384
|
setState({ model: null, clips: [], isLoading: false, error: null });
|
|
380
385
|
return;
|
|
@@ -409,7 +414,7 @@ function ModelLoader({
|
|
|
409
414
|
tint
|
|
410
415
|
}) {
|
|
411
416
|
const { model: loadedModel, clips, isLoading, error } = useGLTFModel(url, resourceBasePath);
|
|
412
|
-
const model =
|
|
417
|
+
const model = React7.useMemo(() => {
|
|
413
418
|
if (!loadedModel) return null;
|
|
414
419
|
const cloned = SkeletonUtils.clone(loadedModel);
|
|
415
420
|
cloned.updateMatrixWorld(true);
|
|
@@ -427,8 +432,8 @@ function ModelLoader({
|
|
|
427
432
|
});
|
|
428
433
|
return cloned;
|
|
429
434
|
}, [loadedModel, castShadow, receiveShadow, tint]);
|
|
430
|
-
const mixer =
|
|
431
|
-
|
|
435
|
+
const mixer = React7.useMemo(() => model ? new THREE6__namespace.AnimationMixer(model) : null, [model]);
|
|
436
|
+
React7.useEffect(() => {
|
|
432
437
|
if (!mixer || !animation || clips.length === 0) return;
|
|
433
438
|
const wanted = animation.toLowerCase();
|
|
434
439
|
const clip = clips.find((c) => c.name === animation) ?? clips.find((c) => c.name.toLowerCase() === wanted);
|
|
@@ -442,7 +447,7 @@ function ModelLoader({
|
|
|
442
447
|
fiber.useFrame((_, delta) => {
|
|
443
448
|
mixer?.update(delta);
|
|
444
449
|
});
|
|
445
|
-
const normFactor =
|
|
450
|
+
const normFactor = React7.useMemo(() => {
|
|
446
451
|
if (!model) return 1;
|
|
447
452
|
const box = new THREE6__namespace.Box3().setFromObject(model);
|
|
448
453
|
const size = new THREE6__namespace.Vector3();
|
|
@@ -451,11 +456,11 @@ function ModelLoader({
|
|
|
451
456
|
if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
|
|
452
457
|
return 1 / maxDim;
|
|
453
458
|
}, [model]);
|
|
454
|
-
const scaleArray =
|
|
459
|
+
const scaleArray = React7.useMemo(() => {
|
|
455
460
|
const base = typeof scale === "number" ? [scale, scale, scale] : scale;
|
|
456
461
|
return [base[0] * normFactor, base[1] * normFactor, base[2] * normFactor];
|
|
457
462
|
}, [scale, normFactor]);
|
|
458
|
-
const rotationRad =
|
|
463
|
+
const rotationRad = React7.useMemo(() => {
|
|
459
464
|
return [
|
|
460
465
|
rotation[0] * Math.PI / 180,
|
|
461
466
|
rotation[1] * Math.PI / 180,
|
|
@@ -723,7 +728,7 @@ var AssetLoader = class {
|
|
|
723
728
|
};
|
|
724
729
|
var assetLoader = new AssetLoader();
|
|
725
730
|
|
|
726
|
-
// components/game/
|
|
731
|
+
// components/game/three/hooks/useThree.ts
|
|
727
732
|
var DEFAULT_OPTIONS = {
|
|
728
733
|
cameraMode: "isometric",
|
|
729
734
|
cameraPosition: [10, 10, 10],
|
|
@@ -735,21 +740,21 @@ var DEFAULT_OPTIONS = {
|
|
|
735
740
|
};
|
|
736
741
|
function useThree3(options = {}) {
|
|
737
742
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
738
|
-
const containerRef =
|
|
739
|
-
const canvasRef =
|
|
740
|
-
const rendererRef =
|
|
741
|
-
const sceneRef =
|
|
742
|
-
const cameraRef =
|
|
743
|
-
const controlsRef =
|
|
744
|
-
const gridHelperRef =
|
|
745
|
-
const rafRef =
|
|
746
|
-
const [isReady, setIsReady] =
|
|
747
|
-
const [dimensions, setDimensions] =
|
|
748
|
-
const initialCameraPosition =
|
|
743
|
+
const containerRef = React7.useRef(null);
|
|
744
|
+
const canvasRef = React7.useRef(null);
|
|
745
|
+
const rendererRef = React7.useRef(null);
|
|
746
|
+
const sceneRef = React7.useRef(null);
|
|
747
|
+
const cameraRef = React7.useRef(null);
|
|
748
|
+
const controlsRef = React7.useRef(null);
|
|
749
|
+
const gridHelperRef = React7.useRef(null);
|
|
750
|
+
const rafRef = React7.useRef(0);
|
|
751
|
+
const [isReady, setIsReady] = React7.useState(false);
|
|
752
|
+
const [dimensions, setDimensions] = React7.useState({ width: 0, height: 0 });
|
|
753
|
+
const initialCameraPosition = React7.useMemo(
|
|
749
754
|
() => new THREE6__namespace.Vector3(...opts.cameraPosition),
|
|
750
755
|
[]
|
|
751
756
|
);
|
|
752
|
-
|
|
757
|
+
React7.useEffect(() => {
|
|
753
758
|
if (!containerRef.current) return;
|
|
754
759
|
const container = containerRef.current;
|
|
755
760
|
const { clientWidth, clientHeight } = container;
|
|
@@ -842,7 +847,7 @@ function useThree3(options = {}) {
|
|
|
842
847
|
scene.clear();
|
|
843
848
|
};
|
|
844
849
|
}, []);
|
|
845
|
-
|
|
850
|
+
React7.useEffect(() => {
|
|
846
851
|
if (!cameraRef.current || !containerRef.current) return;
|
|
847
852
|
const container = containerRef.current;
|
|
848
853
|
const { clientWidth, clientHeight } = container;
|
|
@@ -877,20 +882,20 @@ function useThree3(options = {}) {
|
|
|
877
882
|
animate();
|
|
878
883
|
}
|
|
879
884
|
}, [opts.cameraMode]);
|
|
880
|
-
const setCameraPosition =
|
|
885
|
+
const setCameraPosition = React7.useCallback((x, y, z) => {
|
|
881
886
|
if (cameraRef.current) {
|
|
882
887
|
cameraRef.current.position.set(x, y, z);
|
|
883
888
|
controlsRef.current?.update();
|
|
884
889
|
}
|
|
885
890
|
}, []);
|
|
886
|
-
const lookAt =
|
|
891
|
+
const lookAt = React7.useCallback((x, y, z) => {
|
|
887
892
|
if (cameraRef.current) {
|
|
888
893
|
cameraRef.current.lookAt(x, y, z);
|
|
889
894
|
controlsRef.current?.target.set(x, y, z);
|
|
890
895
|
controlsRef.current?.update();
|
|
891
896
|
}
|
|
892
897
|
}, []);
|
|
893
|
-
const resetCamera =
|
|
898
|
+
const resetCamera = React7.useCallback(() => {
|
|
894
899
|
if (cameraRef.current) {
|
|
895
900
|
cameraRef.current.position.copy(initialCameraPosition);
|
|
896
901
|
cameraRef.current.lookAt(0, 0, 0);
|
|
@@ -900,7 +905,7 @@ function useThree3(options = {}) {
|
|
|
900
905
|
}
|
|
901
906
|
}
|
|
902
907
|
}, [initialCameraPosition]);
|
|
903
|
-
const fitView =
|
|
908
|
+
const fitView = React7.useCallback(
|
|
904
909
|
(bounds) => {
|
|
905
910
|
if (!cameraRef.current) return;
|
|
906
911
|
const centerX = (bounds.minX + bounds.maxX) / 2;
|
|
@@ -931,20 +936,20 @@ function useThree3(options = {}) {
|
|
|
931
936
|
}
|
|
932
937
|
function useAssetLoader(options = {}) {
|
|
933
938
|
const { preloadUrls = [], loader: customLoader } = options;
|
|
934
|
-
const loaderRef =
|
|
935
|
-
const [state, setState] =
|
|
939
|
+
const loaderRef = React7.useRef(customLoader || new AssetLoader());
|
|
940
|
+
const [state, setState] = React7.useState({
|
|
936
941
|
isLoading: false,
|
|
937
942
|
progress: 0,
|
|
938
943
|
loaded: 0,
|
|
939
944
|
total: 0,
|
|
940
945
|
errors: []
|
|
941
946
|
});
|
|
942
|
-
|
|
947
|
+
React7.useEffect(() => {
|
|
943
948
|
if (preloadUrls.length > 0) {
|
|
944
949
|
preload(preloadUrls);
|
|
945
950
|
}
|
|
946
951
|
}, []);
|
|
947
|
-
const updateProgress =
|
|
952
|
+
const updateProgress = React7.useCallback((loaded, total) => {
|
|
948
953
|
setState((prev) => ({
|
|
949
954
|
...prev,
|
|
950
955
|
loaded,
|
|
@@ -952,7 +957,7 @@ function useAssetLoader(options = {}) {
|
|
|
952
957
|
progress: total > 0 ? Math.round(loaded / total * 100) : 0
|
|
953
958
|
}));
|
|
954
959
|
}, []);
|
|
955
|
-
const loadModel =
|
|
960
|
+
const loadModel = React7.useCallback(
|
|
956
961
|
async (url) => {
|
|
957
962
|
setState((prev) => ({ ...prev, isLoading: true }));
|
|
958
963
|
try {
|
|
@@ -975,7 +980,7 @@ function useAssetLoader(options = {}) {
|
|
|
975
980
|
},
|
|
976
981
|
[]
|
|
977
982
|
);
|
|
978
|
-
const loadOBJ =
|
|
983
|
+
const loadOBJ = React7.useCallback(
|
|
979
984
|
async (url) => {
|
|
980
985
|
setState((prev) => ({ ...prev, isLoading: true }));
|
|
981
986
|
try {
|
|
@@ -998,7 +1003,7 @@ function useAssetLoader(options = {}) {
|
|
|
998
1003
|
},
|
|
999
1004
|
[]
|
|
1000
1005
|
);
|
|
1001
|
-
const loadTexture =
|
|
1006
|
+
const loadTexture = React7.useCallback(
|
|
1002
1007
|
async (url) => {
|
|
1003
1008
|
setState((prev) => ({ ...prev, isLoading: true }));
|
|
1004
1009
|
try {
|
|
@@ -1021,7 +1026,7 @@ function useAssetLoader(options = {}) {
|
|
|
1021
1026
|
},
|
|
1022
1027
|
[]
|
|
1023
1028
|
);
|
|
1024
|
-
const preload =
|
|
1029
|
+
const preload = React7.useCallback(
|
|
1025
1030
|
async (urls) => {
|
|
1026
1031
|
setState((prev) => ({
|
|
1027
1032
|
...prev,
|
|
@@ -1060,27 +1065,27 @@ function useAssetLoader(options = {}) {
|
|
|
1060
1065
|
},
|
|
1061
1066
|
[updateProgress]
|
|
1062
1067
|
);
|
|
1063
|
-
const hasModel =
|
|
1068
|
+
const hasModel = React7.useCallback((url) => {
|
|
1064
1069
|
return loaderRef.current.hasModel(url);
|
|
1065
1070
|
}, []);
|
|
1066
|
-
const hasTexture =
|
|
1071
|
+
const hasTexture = React7.useCallback((url) => {
|
|
1067
1072
|
return loaderRef.current.hasTexture(url);
|
|
1068
1073
|
}, []);
|
|
1069
|
-
const getModel =
|
|
1074
|
+
const getModel = React7.useCallback((url) => {
|
|
1070
1075
|
try {
|
|
1071
1076
|
return loaderRef.current.getModel(url);
|
|
1072
1077
|
} catch {
|
|
1073
1078
|
return void 0;
|
|
1074
1079
|
}
|
|
1075
1080
|
}, []);
|
|
1076
|
-
const getTexture =
|
|
1081
|
+
const getTexture = React7.useCallback((url) => {
|
|
1077
1082
|
try {
|
|
1078
1083
|
return loaderRef.current.getTexture(url);
|
|
1079
1084
|
} catch {
|
|
1080
1085
|
return void 0;
|
|
1081
1086
|
}
|
|
1082
1087
|
}, []);
|
|
1083
|
-
const clearCache =
|
|
1088
|
+
const clearCache = React7.useCallback(() => {
|
|
1084
1089
|
loaderRef.current.clearCache();
|
|
1085
1090
|
setState({
|
|
1086
1091
|
isLoading: false,
|
|
@@ -1104,25 +1109,25 @@ function useAssetLoader(options = {}) {
|
|
|
1104
1109
|
};
|
|
1105
1110
|
}
|
|
1106
1111
|
function useSceneGraph() {
|
|
1107
|
-
const nodesRef =
|
|
1108
|
-
const addNode =
|
|
1112
|
+
const nodesRef = React7.useRef(/* @__PURE__ */ new Map());
|
|
1113
|
+
const addNode = React7.useCallback((node) => {
|
|
1109
1114
|
const existing = nodesRef.current.get(node.id);
|
|
1110
1115
|
if (existing) {
|
|
1111
1116
|
existing.mesh.removeFromParent();
|
|
1112
1117
|
}
|
|
1113
1118
|
nodesRef.current.set(node.id, node);
|
|
1114
1119
|
}, []);
|
|
1115
|
-
const removeNode =
|
|
1120
|
+
const removeNode = React7.useCallback((id) => {
|
|
1116
1121
|
const node = nodesRef.current.get(id);
|
|
1117
1122
|
if (node) {
|
|
1118
1123
|
node.mesh.removeFromParent();
|
|
1119
1124
|
nodesRef.current.delete(id);
|
|
1120
1125
|
}
|
|
1121
1126
|
}, []);
|
|
1122
|
-
const getNode =
|
|
1127
|
+
const getNode = React7.useCallback((id) => {
|
|
1123
1128
|
return nodesRef.current.get(id);
|
|
1124
1129
|
}, []);
|
|
1125
|
-
const updateNodePosition =
|
|
1130
|
+
const updateNodePosition = React7.useCallback(
|
|
1126
1131
|
(id, x, y, z) => {
|
|
1127
1132
|
const node = nodesRef.current.get(id);
|
|
1128
1133
|
if (node) {
|
|
@@ -1132,7 +1137,7 @@ function useSceneGraph() {
|
|
|
1132
1137
|
},
|
|
1133
1138
|
[]
|
|
1134
1139
|
);
|
|
1135
|
-
const updateNodeGridPosition =
|
|
1140
|
+
const updateNodeGridPosition = React7.useCallback(
|
|
1136
1141
|
(id, gridX, gridZ) => {
|
|
1137
1142
|
const node = nodesRef.current.get(id);
|
|
1138
1143
|
if (node) {
|
|
@@ -1141,7 +1146,7 @@ function useSceneGraph() {
|
|
|
1141
1146
|
},
|
|
1142
1147
|
[]
|
|
1143
1148
|
);
|
|
1144
|
-
const getNodeAtGrid =
|
|
1149
|
+
const getNodeAtGrid = React7.useCallback(
|
|
1145
1150
|
(x, z, type) => {
|
|
1146
1151
|
return Array.from(nodesRef.current.values()).find((node) => {
|
|
1147
1152
|
const matchesGrid = node.gridPosition.x === x && node.gridPosition.z === z;
|
|
@@ -1150,10 +1155,10 @@ function useSceneGraph() {
|
|
|
1150
1155
|
},
|
|
1151
1156
|
[]
|
|
1152
1157
|
);
|
|
1153
|
-
const getNodesByType =
|
|
1158
|
+
const getNodesByType = React7.useCallback((type) => {
|
|
1154
1159
|
return Array.from(nodesRef.current.values()).filter((node) => node.type === type);
|
|
1155
1160
|
}, []);
|
|
1156
|
-
const getNodesInBounds =
|
|
1161
|
+
const getNodesInBounds = React7.useCallback(
|
|
1157
1162
|
(minX, maxX, minZ, maxZ) => {
|
|
1158
1163
|
return Array.from(nodesRef.current.values()).filter((node) => {
|
|
1159
1164
|
const { x, z } = node.gridPosition;
|
|
@@ -1162,13 +1167,13 @@ function useSceneGraph() {
|
|
|
1162
1167
|
},
|
|
1163
1168
|
[]
|
|
1164
1169
|
);
|
|
1165
|
-
const clearNodes =
|
|
1170
|
+
const clearNodes = React7.useCallback(() => {
|
|
1166
1171
|
nodesRef.current.forEach((node) => {
|
|
1167
1172
|
node.mesh.removeFromParent();
|
|
1168
1173
|
});
|
|
1169
1174
|
nodesRef.current.clear();
|
|
1170
1175
|
}, []);
|
|
1171
|
-
const countNodes =
|
|
1176
|
+
const countNodes = React7.useCallback((type) => {
|
|
1172
1177
|
if (!type) {
|
|
1173
1178
|
return nodesRef.current.size;
|
|
1174
1179
|
}
|
|
@@ -1190,9 +1195,9 @@ function useSceneGraph() {
|
|
|
1190
1195
|
}
|
|
1191
1196
|
function useRaycaster(options) {
|
|
1192
1197
|
const { camera, canvas, cellSize = 1, offsetX = 0, offsetZ = 0 } = options;
|
|
1193
|
-
const raycaster =
|
|
1194
|
-
const mouse =
|
|
1195
|
-
const clientToNDC =
|
|
1198
|
+
const raycaster = React7.useRef(new THREE6__namespace.Raycaster());
|
|
1199
|
+
const mouse = React7.useRef(new THREE6__namespace.Vector2());
|
|
1200
|
+
const clientToNDC = React7.useCallback(
|
|
1196
1201
|
(clientX, clientY) => {
|
|
1197
1202
|
if (!canvas) {
|
|
1198
1203
|
return { x: 0, y: 0 };
|
|
@@ -1205,7 +1210,7 @@ function useRaycaster(options) {
|
|
|
1205
1210
|
},
|
|
1206
1211
|
[canvas]
|
|
1207
1212
|
);
|
|
1208
|
-
const isWithinCanvas =
|
|
1213
|
+
const isWithinCanvas = React7.useCallback(
|
|
1209
1214
|
(clientX, clientY) => {
|
|
1210
1215
|
if (!canvas) return false;
|
|
1211
1216
|
const rect = canvas.getBoundingClientRect();
|
|
@@ -1213,7 +1218,7 @@ function useRaycaster(options) {
|
|
|
1213
1218
|
},
|
|
1214
1219
|
[canvas]
|
|
1215
1220
|
);
|
|
1216
|
-
const getIntersection =
|
|
1221
|
+
const getIntersection = React7.useCallback(
|
|
1217
1222
|
(clientX, clientY, objects) => {
|
|
1218
1223
|
if (!camera || !canvas) return null;
|
|
1219
1224
|
const ndc = clientToNDC(clientX, clientY);
|
|
@@ -1236,7 +1241,7 @@ function useRaycaster(options) {
|
|
|
1236
1241
|
},
|
|
1237
1242
|
[camera, canvas, clientToNDC]
|
|
1238
1243
|
);
|
|
1239
|
-
const getAllIntersections =
|
|
1244
|
+
const getAllIntersections = React7.useCallback(
|
|
1240
1245
|
(clientX, clientY, objects) => {
|
|
1241
1246
|
if (!camera || !canvas) return [];
|
|
1242
1247
|
const ndc = clientToNDC(clientX, clientY);
|
|
@@ -1255,7 +1260,7 @@ function useRaycaster(options) {
|
|
|
1255
1260
|
},
|
|
1256
1261
|
[camera, canvas, clientToNDC]
|
|
1257
1262
|
);
|
|
1258
|
-
const getGridCoordinates =
|
|
1263
|
+
const getGridCoordinates = React7.useCallback(
|
|
1259
1264
|
(clientX, clientY) => {
|
|
1260
1265
|
if (!camera || !canvas) return null;
|
|
1261
1266
|
const ndc = clientToNDC(clientX, clientY);
|
|
@@ -1273,7 +1278,7 @@ function useRaycaster(options) {
|
|
|
1273
1278
|
},
|
|
1274
1279
|
[camera, canvas, cellSize, offsetX, offsetZ, clientToNDC]
|
|
1275
1280
|
);
|
|
1276
|
-
const getTileAtPosition =
|
|
1281
|
+
const getTileAtPosition = React7.useCallback(
|
|
1277
1282
|
(clientX, clientY, scene) => {
|
|
1278
1283
|
if (!camera || !canvas) return null;
|
|
1279
1284
|
const tileMeshes = [];
|
|
@@ -1396,10 +1401,10 @@ var fallbackEventBus = {
|
|
|
1396
1401
|
}
|
|
1397
1402
|
};
|
|
1398
1403
|
function useEventBus() {
|
|
1399
|
-
const context =
|
|
1404
|
+
const context = React7.useContext(providers.EventBusContext);
|
|
1400
1405
|
const baseBus = context ?? getGlobalEventBus() ?? fallbackEventBus;
|
|
1401
1406
|
const scope = providers.useTraitScope();
|
|
1402
|
-
return
|
|
1407
|
+
return React7.useMemo(() => {
|
|
1403
1408
|
if (!scope) {
|
|
1404
1409
|
return {
|
|
1405
1410
|
...baseBus,
|
|
@@ -1435,7 +1440,7 @@ function useEventBus() {
|
|
|
1435
1440
|
}
|
|
1436
1441
|
function useEmitEvent() {
|
|
1437
1442
|
const eventBus = useEventBus();
|
|
1438
|
-
return
|
|
1443
|
+
return React7.useCallback(
|
|
1439
1444
|
(type, payload, source) => {
|
|
1440
1445
|
eventBus.emit(type, payload, source);
|
|
1441
1446
|
},
|
|
@@ -1443,7 +1448,7 @@ function useEmitEvent() {
|
|
|
1443
1448
|
);
|
|
1444
1449
|
}
|
|
1445
1450
|
|
|
1446
|
-
// components/game/
|
|
1451
|
+
// components/game/three/hooks/useGameCanvas3DEvents.ts
|
|
1447
1452
|
function useGameCanvas3DEvents(options) {
|
|
1448
1453
|
const {
|
|
1449
1454
|
tileClickEvent,
|
|
@@ -1462,9 +1467,9 @@ function useGameCanvas3DEvents(options) {
|
|
|
1462
1467
|
onUnitAnimation
|
|
1463
1468
|
} = options;
|
|
1464
1469
|
const emit = useEmitEvent();
|
|
1465
|
-
const optionsRef =
|
|
1470
|
+
const optionsRef = React7.useRef(options);
|
|
1466
1471
|
optionsRef.current = options;
|
|
1467
|
-
const handleTileClick =
|
|
1472
|
+
const handleTileClick = React7.useCallback(
|
|
1468
1473
|
(tile, event) => {
|
|
1469
1474
|
if (tileClickEvent) {
|
|
1470
1475
|
emit(tileClickEvent, {
|
|
@@ -1480,7 +1485,7 @@ function useGameCanvas3DEvents(options) {
|
|
|
1480
1485
|
},
|
|
1481
1486
|
[tileClickEvent, emit]
|
|
1482
1487
|
);
|
|
1483
|
-
const handleUnitClick =
|
|
1488
|
+
const handleUnitClick = React7.useCallback(
|
|
1484
1489
|
(unit, event) => {
|
|
1485
1490
|
if (unitClickEvent) {
|
|
1486
1491
|
emit(unitClickEvent, {
|
|
@@ -1499,7 +1504,7 @@ function useGameCanvas3DEvents(options) {
|
|
|
1499
1504
|
},
|
|
1500
1505
|
[unitClickEvent, emit]
|
|
1501
1506
|
);
|
|
1502
|
-
const handleFeatureClick =
|
|
1507
|
+
const handleFeatureClick = React7.useCallback(
|
|
1503
1508
|
(feature, event) => {
|
|
1504
1509
|
if (featureClickEvent) {
|
|
1505
1510
|
emit(featureClickEvent, {
|
|
@@ -1514,7 +1519,7 @@ function useGameCanvas3DEvents(options) {
|
|
|
1514
1519
|
},
|
|
1515
1520
|
[featureClickEvent, emit]
|
|
1516
1521
|
);
|
|
1517
|
-
const handleCanvasClick =
|
|
1522
|
+
const handleCanvasClick = React7.useCallback(
|
|
1518
1523
|
(event) => {
|
|
1519
1524
|
if (canvasClickEvent) {
|
|
1520
1525
|
emit(canvasClickEvent, {
|
|
@@ -1527,7 +1532,7 @@ function useGameCanvas3DEvents(options) {
|
|
|
1527
1532
|
},
|
|
1528
1533
|
[canvasClickEvent, emit]
|
|
1529
1534
|
);
|
|
1530
|
-
const handleTileHover =
|
|
1535
|
+
const handleTileHover = React7.useCallback(
|
|
1531
1536
|
(tile, event) => {
|
|
1532
1537
|
if (tile) {
|
|
1533
1538
|
if (tileHoverEvent) {
|
|
@@ -1547,7 +1552,7 @@ function useGameCanvas3DEvents(options) {
|
|
|
1547
1552
|
},
|
|
1548
1553
|
[tileHoverEvent, tileLeaveEvent, emit]
|
|
1549
1554
|
);
|
|
1550
|
-
const handleUnitAnimation =
|
|
1555
|
+
const handleUnitAnimation = React7.useCallback(
|
|
1551
1556
|
(unitId, state) => {
|
|
1552
1557
|
if (unitAnimationEvent) {
|
|
1553
1558
|
emit(unitAnimationEvent, {
|
|
@@ -1560,7 +1565,7 @@ function useGameCanvas3DEvents(options) {
|
|
|
1560
1565
|
},
|
|
1561
1566
|
[unitAnimationEvent, emit]
|
|
1562
1567
|
);
|
|
1563
|
-
const handleCameraChange =
|
|
1568
|
+
const handleCameraChange = React7.useCallback(
|
|
1564
1569
|
(position) => {
|
|
1565
1570
|
if (cameraChangeEvent) {
|
|
1566
1571
|
emit(cameraChangeEvent, {
|
|
@@ -1582,131 +1587,37 @@ function useGameCanvas3DEvents(options) {
|
|
|
1582
1587
|
};
|
|
1583
1588
|
}
|
|
1584
1589
|
|
|
1585
|
-
//
|
|
1586
|
-
function
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1590
|
+
// lib/drawable/hitTest.ts
|
|
1591
|
+
function collectDrawnItems(nodes) {
|
|
1592
|
+
const out = [];
|
|
1593
|
+
for (const n of nodes) {
|
|
1594
|
+
switch (n.type) {
|
|
1595
|
+
case "draw-sprite":
|
|
1596
|
+
case "draw-shape":
|
|
1597
|
+
case "draw-text":
|
|
1598
|
+
out.push({ pos: n.position, id: n.id });
|
|
1599
|
+
break;
|
|
1600
|
+
case "draw-sprite-layer":
|
|
1601
|
+
case "draw-shape-layer":
|
|
1602
|
+
case "draw-text-layer":
|
|
1603
|
+
for (const it of n.items) out.push({ pos: it.position, id: it.id });
|
|
1604
|
+
break;
|
|
1605
|
+
}
|
|
1596
1606
|
}
|
|
1607
|
+
return out;
|
|
1597
1608
|
}
|
|
1598
|
-
function
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
sw: frameWidth,
|
|
1603
|
-
sh: frameHeight
|
|
1604
|
-
};
|
|
1605
|
-
}
|
|
1606
|
-
|
|
1607
|
-
// components/game/shared/hooks/useUnitSpriteAtlas.ts
|
|
1608
|
-
function unitAtlasUrl(unit) {
|
|
1609
|
-
return unit.spriteSheet?.url ?? null;
|
|
1610
|
-
}
|
|
1611
|
-
function resolveSheetUrl(atlasUrl, relativeSheetPath) {
|
|
1612
|
-
try {
|
|
1613
|
-
return new URL(relativeSheetPath, atlasUrl).toString();
|
|
1614
|
-
} catch {
|
|
1615
|
-
const base = atlasUrl.slice(0, atlasUrl.lastIndexOf("/") + 1);
|
|
1616
|
-
return `${base}${relativeSheetPath.replace(/^\.\//, "")}`;
|
|
1609
|
+
function buildHitIndex(items) {
|
|
1610
|
+
const m = /* @__PURE__ */ new Map();
|
|
1611
|
+
for (const it of items) {
|
|
1612
|
+
if (it.id !== void 0) m.set(`${it.pos.x},${it.pos.y}`, it.id);
|
|
1617
1613
|
}
|
|
1614
|
+
return m;
|
|
1618
1615
|
}
|
|
1619
|
-
function
|
|
1620
|
-
const atlasCacheRef = React8.useRef(/* @__PURE__ */ new Map());
|
|
1621
|
-
const loadingRef = React8.useRef(/* @__PURE__ */ new Set());
|
|
1622
|
-
const [pendingCount, setPendingCount] = React8.useState(0);
|
|
1623
|
-
const [, forceTick] = React8.useState(0);
|
|
1624
|
-
const atlasUrls = React8.useMemo(() => {
|
|
1625
|
-
const set = /* @__PURE__ */ new Set();
|
|
1626
|
-
for (const unit of units) {
|
|
1627
|
-
const url = unitAtlasUrl(unit);
|
|
1628
|
-
if (url) set.add(url);
|
|
1629
|
-
}
|
|
1630
|
-
return [...set];
|
|
1631
|
-
}, [units]);
|
|
1632
|
-
React8.useEffect(() => {
|
|
1633
|
-
const cache = atlasCacheRef.current;
|
|
1634
|
-
const loading = loadingRef.current;
|
|
1635
|
-
const toLoad = atlasUrls.filter((url) => !cache.has(url) && !loading.has(url));
|
|
1636
|
-
if (toLoad.length === 0) return;
|
|
1637
|
-
let cancelled = false;
|
|
1638
|
-
setPendingCount((prev) => prev + toLoad.length);
|
|
1639
|
-
for (const url of toLoad) {
|
|
1640
|
-
loading.add(url);
|
|
1641
|
-
fetch(url).then((res) => res.ok ? res.json() : Promise.reject(new Error(String(res.status)))).then((atlas) => {
|
|
1642
|
-
if (cancelled) return;
|
|
1643
|
-
cache.set(url, atlas);
|
|
1644
|
-
}).catch(() => {
|
|
1645
|
-
}).finally(() => {
|
|
1646
|
-
if (cancelled) return;
|
|
1647
|
-
loading.delete(url);
|
|
1648
|
-
setPendingCount((prev) => Math.max(0, prev - 1));
|
|
1649
|
-
forceTick((n) => n + 1);
|
|
1650
|
-
});
|
|
1651
|
-
}
|
|
1652
|
-
return () => {
|
|
1653
|
-
cancelled = true;
|
|
1654
|
-
};
|
|
1655
|
-
}, [atlasUrls]);
|
|
1656
|
-
const sheetUrls = React8.useMemo(() => {
|
|
1657
|
-
const urls = /* @__PURE__ */ new Set();
|
|
1658
|
-
for (const unit of units) {
|
|
1659
|
-
const atlasUrl = unitAtlasUrl(unit);
|
|
1660
|
-
if (!atlasUrl) continue;
|
|
1661
|
-
const atlas = atlasCacheRef.current.get(atlasUrl);
|
|
1662
|
-
if (!atlas) continue;
|
|
1663
|
-
for (const rel of Object.values(atlas.sheets)) {
|
|
1664
|
-
if (rel) urls.add(resolveSheetUrl(atlasUrl, rel));
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
return [...urls];
|
|
1668
|
-
}, [units, pendingCount]);
|
|
1669
|
-
const resolveUnitFrame = React8.useCallback((unitId) => {
|
|
1670
|
-
const unit = units.find((u) => u.id === unitId);
|
|
1671
|
-
if (!unit) return null;
|
|
1672
|
-
const atlasUrl = unitAtlasUrl(unit);
|
|
1673
|
-
if (!atlasUrl) return null;
|
|
1674
|
-
const atlas = atlasCacheRef.current.get(atlasUrl);
|
|
1675
|
-
if (!atlas) return null;
|
|
1676
|
-
const animation = unit.animation ?? "idle";
|
|
1677
|
-
const frame = unit.frame ?? 0;
|
|
1678
|
-
const def = atlas.animations[animation] ?? atlas.animations.idle;
|
|
1679
|
-
if (!def) return null;
|
|
1680
|
-
const { sheetDir, flipX } = resolveSheetDirection("se");
|
|
1681
|
-
const rel = atlas.sheets[sheetDir] ?? atlas.sheets.se ?? atlas.sheets.sw;
|
|
1682
|
-
if (!rel) return null;
|
|
1683
|
-
const sheetUrl = resolveSheetUrl(atlasUrl, rel);
|
|
1684
|
-
const rect = frameRect(frame, def.row, atlas.columns, atlas.frameWidth, atlas.frameHeight);
|
|
1685
|
-
return {
|
|
1686
|
-
sheetUrl,
|
|
1687
|
-
sx: rect.sx,
|
|
1688
|
-
sy: rect.sy,
|
|
1689
|
-
sw: rect.sw,
|
|
1690
|
-
sh: rect.sh,
|
|
1691
|
-
flipX,
|
|
1692
|
-
applyBreathing: animation === "idle"
|
|
1693
|
-
};
|
|
1694
|
-
}, [units]);
|
|
1695
|
-
return { sheetUrls, resolveUnitFrame, pendingCount };
|
|
1696
|
-
}
|
|
1697
|
-
function cn(...inputs) {
|
|
1698
|
-
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
1699
|
-
}
|
|
1700
|
-
var DEFAULT_GRID_CONFIG = {
|
|
1701
|
-
cellSize: 1,
|
|
1702
|
-
offsetX: 0,
|
|
1703
|
-
offsetZ: 0
|
|
1704
|
-
};
|
|
1705
|
-
function CameraController({
|
|
1616
|
+
function CameraController3D({
|
|
1706
1617
|
onCameraChange
|
|
1707
1618
|
}) {
|
|
1708
1619
|
const { camera } = fiber.useThree();
|
|
1709
|
-
|
|
1620
|
+
React7.useEffect(() => {
|
|
1710
1621
|
if (onCameraChange) {
|
|
1711
1622
|
onCameraChange({
|
|
1712
1623
|
x: camera.position.x,
|
|
@@ -1717,54 +1628,13 @@ function CameraController({
|
|
|
1717
1628
|
}, [camera.position, onCameraChange]);
|
|
1718
1629
|
return null;
|
|
1719
1630
|
}
|
|
1720
|
-
function
|
|
1721
|
-
sheetUrl,
|
|
1722
|
-
resolveFrame,
|
|
1723
|
-
height = 1.2
|
|
1724
|
-
}) {
|
|
1725
|
-
const texture = fiber.useLoader(THREE6__namespace.TextureLoader, sheetUrl);
|
|
1726
|
-
const meshRef = React8.useRef(null);
|
|
1727
|
-
const matRef = React8.useRef(null);
|
|
1728
|
-
const [aspect, setAspect] = React8.useState(1);
|
|
1729
|
-
fiber.useFrame(() => {
|
|
1730
|
-
const frame = resolveFrame();
|
|
1731
|
-
if (!frame || !texture.image) return;
|
|
1732
|
-
const imgW = texture.image.width;
|
|
1733
|
-
const imgH = texture.image.height;
|
|
1734
|
-
if (!imgW || !imgH) return;
|
|
1735
|
-
texture.repeat.set((frame.flipX ? -1 : 1) * (frame.sw / imgW), frame.sh / imgH);
|
|
1736
|
-
texture.offset.set(
|
|
1737
|
-
frame.flipX ? (frame.sx + frame.sw) / imgW : frame.sx / imgW,
|
|
1738
|
-
1 - (frame.sy + frame.sh) / imgH
|
|
1739
|
-
);
|
|
1740
|
-
texture.magFilter = THREE6__namespace.NearestFilter;
|
|
1741
|
-
texture.minFilter = THREE6__namespace.NearestFilter;
|
|
1742
|
-
texture.needsUpdate = true;
|
|
1743
|
-
const nextAspect = frame.sw / frame.sh;
|
|
1744
|
-
if (Math.abs(nextAspect - aspect) > 1e-3) setAspect(nextAspect);
|
|
1745
|
-
if (matRef.current) matRef.current.needsUpdate = true;
|
|
1746
|
-
});
|
|
1747
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ref: meshRef, position: [0, height / 2, 0], children: [
|
|
1748
|
-
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [height * aspect, height] }),
|
|
1749
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1750
|
-
"meshBasicMaterial",
|
|
1751
|
-
{
|
|
1752
|
-
ref: matRef,
|
|
1753
|
-
map: texture,
|
|
1754
|
-
transparent: true,
|
|
1755
|
-
alphaTest: 0.1,
|
|
1756
|
-
side: THREE6__namespace.DoubleSide
|
|
1757
|
-
}
|
|
1758
|
-
)
|
|
1759
|
-
] });
|
|
1760
|
-
}
|
|
1761
|
-
function FollowCamera({
|
|
1631
|
+
function FollowCamera3D({
|
|
1762
1632
|
target,
|
|
1763
1633
|
offset
|
|
1764
1634
|
}) {
|
|
1765
1635
|
const { camera } = fiber.useThree();
|
|
1766
|
-
const look =
|
|
1767
|
-
const goal =
|
|
1636
|
+
const look = React7.useRef(new THREE6__namespace.Vector3(target[0], target[1], target[2]));
|
|
1637
|
+
const goal = React7.useRef(new THREE6__namespace.Vector3());
|
|
1768
1638
|
fiber.useFrame((_, delta) => {
|
|
1769
1639
|
const t = Math.min(1, delta * 5);
|
|
1770
1640
|
goal.current.set(target[0] + offset[0], target[1] + offset[1], target[2] + offset[2]);
|
|
@@ -1774,371 +1644,156 @@ function FollowCamera({
|
|
|
1774
1644
|
});
|
|
1775
1645
|
return null;
|
|
1776
1646
|
}
|
|
1777
|
-
function
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1792
|
-
}
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
platform: "#8b5a2b",
|
|
1796
|
-
hazard: "#cc4444",
|
|
1797
|
-
goal: "#4488cc"
|
|
1798
|
-
};
|
|
1799
|
-
function SideScene({
|
|
1800
|
-
player,
|
|
1801
|
-
platforms,
|
|
1802
|
-
worldHeight,
|
|
1803
|
-
ppu,
|
|
1804
|
-
playerSprite,
|
|
1805
|
-
tileSprites,
|
|
1806
|
-
interpolate,
|
|
1807
|
-
features = [],
|
|
1808
|
-
events = [],
|
|
1809
|
-
assetManifest
|
|
1810
|
-
}) {
|
|
1811
|
-
const pw = player.width ?? 32;
|
|
1812
|
-
const ph = player.height ?? 48;
|
|
1813
|
-
const playerPos = [
|
|
1814
|
-
(player.x + pw / 2) / ppu,
|
|
1815
|
-
(worldHeight - player.y - ph) / ppu,
|
|
1816
|
-
0
|
|
1817
|
-
];
|
|
1818
|
-
const playerH = Math.max(ph / ppu, 0.5);
|
|
1819
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("group", { children: [
|
|
1820
|
-
platforms.map((p, i) => {
|
|
1821
|
-
const platformType = p.type ?? "platform";
|
|
1822
|
-
const model = tileSprites?.[platformType]?.url;
|
|
1823
|
-
const topY = (worldHeight - p.y) / ppu;
|
|
1824
|
-
if (model) {
|
|
1825
|
-
const cells = Math.max(1, Math.round(p.width / ppu));
|
|
1826
|
-
const x0 = p.x / ppu;
|
|
1827
|
-
return /* @__PURE__ */ jsxRuntime.jsx("group", { children: Array.from({ length: cells }, (_, c) => /* @__PURE__ */ jsxRuntime.jsx("group", { position: [x0 + c + 0.5, topY - 0.475, 0], children: /* @__PURE__ */ jsxRuntime.jsx(ModelLoader, { url: model, scale: 0.95, fallbackGeometry: "box", castShadow: true, receiveShadow: true }) }, c)) }, `plat-${i}`);
|
|
1828
|
-
}
|
|
1829
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1830
|
-
"mesh",
|
|
1831
|
-
{
|
|
1832
|
-
position: [(p.x + p.width / 2) / ppu, topY - p.height / 2 / ppu, 0],
|
|
1833
|
-
children: [
|
|
1834
|
-
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [p.width / ppu, p.height / ppu, 1] }),
|
|
1835
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: SIDE_PLATFORM_COLORS[platformType] ?? "#888888" })
|
|
1836
|
-
]
|
|
1837
|
-
},
|
|
1838
|
-
`plat-${i}`
|
|
1839
|
-
);
|
|
1840
|
-
}),
|
|
1841
|
-
features.map((feature, i) => {
|
|
1842
|
-
const model = feature.assetUrl ?? assetManifest?.features?.[feature.type];
|
|
1843
|
-
const fx = feature.x / ppu;
|
|
1844
|
-
const fy = (worldHeight - feature.y) / ppu;
|
|
1845
|
-
if (!model?.url) return null;
|
|
1846
|
-
return /* @__PURE__ */ jsxRuntime.jsx("group", { position: [fx, fy, 0], children: /* @__PURE__ */ jsxRuntime.jsx(ModelLoader, { url: model.url, scale: 0.6, tint: feature.color, fallbackGeometry: "sphere", castShadow: true }) }, feature.id ?? `sfeat-${i}`);
|
|
1847
|
-
}),
|
|
1848
|
-
events.map((ev, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1849
|
-
EventMarker,
|
|
1647
|
+
function SpriteBillboard({ node, world }) {
|
|
1648
|
+
const texture = fiber.useLoader(THREE6__namespace.TextureLoader, node.asset.url);
|
|
1649
|
+
const img = texture.image;
|
|
1650
|
+
const imgW = img?.width || 1;
|
|
1651
|
+
const imgH = img?.height || 1;
|
|
1652
|
+
if (node.frame) {
|
|
1653
|
+
texture.repeat.set(node.frame.w / imgW, node.frame.h / imgH);
|
|
1654
|
+
texture.offset.set(node.frame.x / imgW, 1 - (node.frame.y + node.frame.h) / imgH);
|
|
1655
|
+
texture.magFilter = texture.minFilter = THREE6__namespace.NearestFilter;
|
|
1656
|
+
texture.needsUpdate = true;
|
|
1657
|
+
}
|
|
1658
|
+
const aspect = node.frame ? node.frame.w / node.frame.h : imgW / imgH;
|
|
1659
|
+
const height = node.height ?? 1;
|
|
1660
|
+
const width = node.width ?? height * aspect;
|
|
1661
|
+
return /* @__PURE__ */ jsxRuntime.jsx(drei.Billboard, { position: [world[0], world[1] + height / 2, world[2]], children: /* @__PURE__ */ jsxRuntime.jsxs("mesh", { children: [
|
|
1662
|
+
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [width, height] }),
|
|
1663
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1664
|
+
"meshBasicMaterial",
|
|
1850
1665
|
{
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1666
|
+
map: texture,
|
|
1667
|
+
transparent: true,
|
|
1668
|
+
alphaTest: 0.1,
|
|
1669
|
+
side: THREE6__namespace.DoubleSide,
|
|
1670
|
+
opacity: node.opacity ?? 1
|
|
1671
|
+
}
|
|
1672
|
+
)
|
|
1673
|
+
] }) });
|
|
1674
|
+
}
|
|
1675
|
+
function Sprite3D({ node, projector }) {
|
|
1676
|
+
if (node.asset.dimension === "3d" && node.asset.url) {
|
|
1677
|
+
return /* @__PURE__ */ jsxRuntime.jsx("group", { position: projector.toWorld(node.position), children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1857
1678
|
ModelLoader,
|
|
1858
1679
|
{
|
|
1859
|
-
url:
|
|
1860
|
-
scale:
|
|
1861
|
-
rotation: [0,
|
|
1862
|
-
animation: player.animation ?? "idle",
|
|
1680
|
+
url: node.asset.url,
|
|
1681
|
+
scale: node.width ?? projector.cellSize,
|
|
1682
|
+
rotation: [0, node.rotation ?? 0, 0],
|
|
1863
1683
|
fallbackGeometry: "box",
|
|
1864
|
-
castShadow: true
|
|
1684
|
+
castShadow: true,
|
|
1685
|
+
receiveShadow: true
|
|
1865
1686
|
}
|
|
1866
|
-
) })
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1687
|
+
) });
|
|
1688
|
+
}
|
|
1689
|
+
if (!node.asset.url) return null;
|
|
1690
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SpriteBillboard, { node, world: projector.toWorld(node.position) });
|
|
1691
|
+
}
|
|
1692
|
+
function Shape3D({ node, projector }) {
|
|
1693
|
+
const world = projector.toWorld(node.position);
|
|
1694
|
+
const color = node.fill ?? node.stroke ?? "#ffffff";
|
|
1695
|
+
let geometry;
|
|
1696
|
+
switch (node.shape) {
|
|
1697
|
+
case "cell":
|
|
1698
|
+
case "rect": {
|
|
1699
|
+
const w = node.shape === "cell" ? projector.cellSize * 0.95 : node.width ?? projector.cellSize;
|
|
1700
|
+
const h = node.shape === "cell" ? projector.cellSize * 0.95 : node.height ?? projector.cellSize;
|
|
1701
|
+
geometry = /* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [w, h] });
|
|
1702
|
+
break;
|
|
1703
|
+
}
|
|
1704
|
+
case "ellipse": {
|
|
1705
|
+
const radiusX = node.radiusX ?? 0.4;
|
|
1706
|
+
geometry = node.stroke && !node.fill ? /* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [Math.max(0, radiusX - 0.08), radiusX, 32] }) : /* @__PURE__ */ jsxRuntime.jsx("circleGeometry", { args: [radiusX, 32] });
|
|
1707
|
+
break;
|
|
1708
|
+
}
|
|
1709
|
+
case "poly": {
|
|
1710
|
+
if (!node.points || node.points.length === 0) return null;
|
|
1711
|
+
const s = new THREE6__namespace.Shape();
|
|
1712
|
+
node.points.forEach((p, i) => {
|
|
1713
|
+
if (i === 0) s.moveTo(p.x, p.y);
|
|
1714
|
+
else s.lineTo(p.x, p.y);
|
|
1715
|
+
});
|
|
1716
|
+
s.closePath();
|
|
1717
|
+
geometry = /* @__PURE__ */ jsxRuntime.jsx("shapeGeometry", { args: [s] });
|
|
1718
|
+
break;
|
|
1719
|
+
}
|
|
1720
|
+
default:
|
|
1721
|
+
return null;
|
|
1722
|
+
}
|
|
1723
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("mesh", { rotation: [-Math.PI / 2, 0, 0], position: [world[0], world[1] + 0.02, world[2]], children: [
|
|
1724
|
+
geometry,
|
|
1725
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color, transparent: true, opacity: node.opacity ?? 1, side: THREE6__namespace.DoubleSide })
|
|
1870
1726
|
] });
|
|
1871
1727
|
}
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
var TILE_TOP_OFFSET = 0.1;
|
|
1876
|
-
var EVENT_COLORS = {
|
|
1877
|
-
hit: "#ff5544",
|
|
1878
|
-
damage: "#ff5544",
|
|
1879
|
-
attack: "#ff8844",
|
|
1880
|
-
heal: "#44dd66",
|
|
1881
|
-
pickup: "#ffcc33",
|
|
1882
|
-
score: "#ffcc33",
|
|
1883
|
-
death: "#aa3333",
|
|
1884
|
-
win: "#66ff88",
|
|
1885
|
-
lose: "#ff6666"
|
|
1886
|
-
};
|
|
1887
|
-
function EventMarker({
|
|
1888
|
-
event,
|
|
1889
|
-
position
|
|
1890
|
-
}) {
|
|
1891
|
-
return /* @__PURE__ */ jsxRuntime.jsx(drei.Billboard, { position, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1728
|
+
function Text3D({ node, projector }) {
|
|
1729
|
+
const world = projector.toWorld(node.position);
|
|
1730
|
+
return /* @__PURE__ */ jsxRuntime.jsx(drei.Billboard, { position: [world[0], world[1] + 1.2, world[2]], children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1892
1731
|
drei.Text,
|
|
1893
1732
|
{
|
|
1894
1733
|
fontSize: 0.32,
|
|
1895
|
-
color:
|
|
1896
|
-
outlineWidth: 0.02,
|
|
1897
|
-
outlineColor: "#000000",
|
|
1734
|
+
color: node.color,
|
|
1898
1735
|
anchorX: "center",
|
|
1899
1736
|
anchorY: "middle",
|
|
1900
|
-
|
|
1737
|
+
outlineWidth: 0.02,
|
|
1738
|
+
outlineColor: "#000000",
|
|
1739
|
+
children: node.text
|
|
1901
1740
|
}
|
|
1902
1741
|
) });
|
|
1903
1742
|
}
|
|
1904
|
-
function
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
else if (tile.type === "sand") color = 14535816;
|
|
1919
|
-
else if (tile.type === "rock") color = 8947848;
|
|
1920
|
-
else if (tile.type === "snow") color = 15658734;
|
|
1921
|
-
let emissive = 0;
|
|
1922
|
-
if (isSelected) emissive = 4473924;
|
|
1923
|
-
else if (isAttackTarget) emissive = 4456448;
|
|
1924
|
-
else if (isValidMove) emissive = 17408;
|
|
1925
|
-
else if (isHovered) emissive = 2236962;
|
|
1926
|
-
if (model?.url) {
|
|
1927
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1928
|
-
"group",
|
|
1929
|
-
{
|
|
1930
|
-
position,
|
|
1931
|
-
onClick: (e) => onTileClick(tile, e),
|
|
1932
|
-
onPointerEnter: (e) => onTileHover(tile, e),
|
|
1933
|
-
onPointerLeave: (e) => onTileHover(null, e),
|
|
1934
|
-
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
1935
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1936
|
-
ModelLoader,
|
|
1937
|
-
{
|
|
1938
|
-
url: model.url,
|
|
1939
|
-
scale: 0.95,
|
|
1940
|
-
rotation: [0, tile.rotation ?? 0, 0],
|
|
1941
|
-
fallbackGeometry: "box",
|
|
1942
|
-
castShadow: true,
|
|
1943
|
-
receiveShadow: true
|
|
1944
|
-
}
|
|
1945
|
-
)
|
|
1946
|
-
}
|
|
1947
|
-
);
|
|
1743
|
+
function Drawable3D({ node, projector }) {
|
|
1744
|
+
switch (node.type) {
|
|
1745
|
+
case "draw-sprite":
|
|
1746
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Sprite3D, { node, projector });
|
|
1747
|
+
case "draw-shape":
|
|
1748
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Shape3D, { node, projector });
|
|
1749
|
+
case "draw-text":
|
|
1750
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Text3D, { node, projector });
|
|
1751
|
+
case "draw-sprite-layer":
|
|
1752
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(Sprite3D, { node: item, projector }, i)) });
|
|
1753
|
+
case "draw-shape-layer":
|
|
1754
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(Shape3D, { node: item, projector }, i)) });
|
|
1755
|
+
case "draw-text-layer":
|
|
1756
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: node.items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsx(Text3D, { node: item, projector }, i)) });
|
|
1948
1757
|
}
|
|
1949
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1950
|
-
"mesh",
|
|
1951
|
-
{
|
|
1952
|
-
position,
|
|
1953
|
-
onClick: (e) => onTileClick(tile, e),
|
|
1954
|
-
onPointerEnter: (e) => onTileHover(tile, e),
|
|
1955
|
-
onPointerLeave: (e) => onTileHover(null, e),
|
|
1956
|
-
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
1957
|
-
children: [
|
|
1958
|
-
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.95, 0.2, 0.95] }),
|
|
1959
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color, emissive })
|
|
1960
|
-
]
|
|
1961
|
-
}
|
|
1962
|
-
);
|
|
1963
1758
|
}
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
}
|
|
1974
|
-
const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
|
|
1975
|
-
const hasAtlas = unitAtlasUrl(unit) !== null;
|
|
1976
|
-
const initialFrame = hasAtlas ? resolveUnitFrame(unit.id) : null;
|
|
1977
|
-
const modelScale = UNIT_BASE_MODEL_SCALE * unitScale * cellSize;
|
|
1978
|
-
const billboardHeight = UNIT_BASE_BILLBOARD_HEIGHT * unitScale * cellSize;
|
|
1979
|
-
const primitiveRadius = UNIT_BASE_PRIMITIVE_RADIUS * unitScale * cellSize;
|
|
1980
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1981
|
-
"group",
|
|
1982
|
-
{
|
|
1983
|
-
position,
|
|
1984
|
-
onClick: (e) => onUnitClick(unit, e),
|
|
1985
|
-
userData: { type: "unit", unitId: unit.id },
|
|
1986
|
-
children: [
|
|
1987
|
-
isSelected && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.05, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
1988
|
-
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
|
|
1989
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: "#ffff00", transparent: true, opacity: 0.8 })
|
|
1990
|
-
] }),
|
|
1991
|
-
hasAtlas && initialFrame ? (
|
|
1992
|
-
/* Animated sprite-sheet billboard — single cropped frame, by state */
|
|
1993
|
-
/* @__PURE__ */ jsxRuntime.jsx(drei.Billboard, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1994
|
-
UnitSpriteBillboard,
|
|
1995
|
-
{
|
|
1996
|
-
sheetUrl: initialFrame.sheetUrl,
|
|
1997
|
-
resolveFrame: () => resolveUnitFrame(unit.id),
|
|
1998
|
-
height: billboardHeight
|
|
1999
|
-
}
|
|
2000
|
-
) })
|
|
2001
|
-
) : model?.url ? (
|
|
2002
|
-
/* GLB unit model — LOLO's `animation` field drives the named clip;
|
|
2003
|
-
`heading` (radians) faces the model along its travel direction (driving). */
|
|
2004
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2005
|
-
ModelLoader,
|
|
2006
|
-
{
|
|
2007
|
-
url: model.url,
|
|
2008
|
-
scale: modelScale,
|
|
2009
|
-
rotation: [0, unit.heading ?? 0, 0],
|
|
2010
|
-
animation: unit.animation ?? "idle",
|
|
2011
|
-
fallbackGeometry: "box",
|
|
2012
|
-
castShadow: true
|
|
2013
|
-
}
|
|
2014
|
-
)
|
|
2015
|
-
) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2016
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, primitiveRadius, 0], children: [
|
|
2017
|
-
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [primitiveRadius, primitiveRadius, primitiveRadius * 0.33, 8] }),
|
|
2018
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
2019
|
-
] }),
|
|
2020
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, primitiveRadius * 2, 0], children: [
|
|
2021
|
-
/* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [primitiveRadius * 0.67, primitiveRadius * 1.33, 4, 8] }),
|
|
2022
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
2023
|
-
] }),
|
|
2024
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, primitiveRadius * 3, 0], children: [
|
|
2025
|
-
/* @__PURE__ */ jsxRuntime.jsx("sphereGeometry", { args: [primitiveRadius * 0.4, 8, 8] }),
|
|
2026
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color })
|
|
2027
|
-
] })
|
|
2028
|
-
] }),
|
|
2029
|
-
unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("group", { position: [0, billboardHeight, 0], children: [
|
|
2030
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [-0.25, 0, 0], children: [
|
|
2031
|
-
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.5, 0.05] }),
|
|
2032
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: 3355443 })
|
|
2033
|
-
] }),
|
|
2034
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2035
|
-
"mesh",
|
|
2036
|
-
{
|
|
2037
|
-
position: [
|
|
2038
|
-
-0.25 + 0.5 * (unit.health / unit.maxHealth) / 2,
|
|
2039
|
-
0,
|
|
2040
|
-
0.01
|
|
2041
|
-
],
|
|
2042
|
-
children: [
|
|
2043
|
-
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [0.5 * (unit.health / unit.maxHealth), 0.05] }),
|
|
2044
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2045
|
-
"meshBasicMaterial",
|
|
2046
|
-
{
|
|
2047
|
-
color: unit.health / unit.maxHealth > 0.5 ? 4500036 : unit.health / unit.maxHealth > 0.25 ? 11184708 : 16729156
|
|
2048
|
-
}
|
|
2049
|
-
)
|
|
2050
|
-
]
|
|
2051
|
-
}
|
|
2052
|
-
)
|
|
2053
|
-
] })
|
|
2054
|
-
]
|
|
2055
|
-
}
|
|
2056
|
-
);
|
|
1759
|
+
|
|
1760
|
+
// lib/drawable/projector3d.ts
|
|
1761
|
+
function create3DProjector(opts = {}) {
|
|
1762
|
+
const cellSize = opts.cellSize ?? 1;
|
|
1763
|
+
const offsetX = opts.offsetX ?? 0;
|
|
1764
|
+
const offsetZ = opts.offsetZ ?? 0;
|
|
1765
|
+
return {
|
|
1766
|
+
cellSize,
|
|
1767
|
+
toWorld: (pos) => [pos.x * cellSize + offsetX, pos.z ?? 0, pos.y * cellSize + offsetZ]
|
|
1768
|
+
};
|
|
2057
1769
|
}
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
}
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
{
|
|
2068
|
-
url: model.url,
|
|
2069
|
-
position,
|
|
2070
|
-
scale: 0.5,
|
|
2071
|
-
rotation: [0, feature.rotation ?? 0, 0],
|
|
2072
|
-
tint: feature.color,
|
|
2073
|
-
onClick: () => onFeatureClick(feature, null),
|
|
2074
|
-
fallbackGeometry: "box"
|
|
2075
|
-
}
|
|
2076
|
-
);
|
|
2077
|
-
}
|
|
2078
|
-
if (feature.type === "tree") {
|
|
2079
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2080
|
-
"group",
|
|
2081
|
-
{
|
|
2082
|
-
position,
|
|
2083
|
-
onClick: (e) => onFeatureClick(feature, e),
|
|
2084
|
-
userData: { type: "feature", featureId: feature.id },
|
|
2085
|
-
children: [
|
|
2086
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.4, 0], children: [
|
|
2087
|
-
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.1, 0.15, 0.8, 6] }),
|
|
2088
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 9127187 })
|
|
2089
|
-
] }),
|
|
2090
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
2091
|
-
/* @__PURE__ */ jsxRuntime.jsx("coneGeometry", { args: [0.5, 0.8, 8] }),
|
|
2092
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 2263842 })
|
|
2093
|
-
] })
|
|
2094
|
-
]
|
|
2095
|
-
}
|
|
2096
|
-
);
|
|
2097
|
-
}
|
|
2098
|
-
if (feature.type === "rock") {
|
|
2099
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2100
|
-
"mesh",
|
|
2101
|
-
{
|
|
2102
|
-
position: [position[0], position[1] + 0.3, position[2]],
|
|
2103
|
-
onClick: (e) => onFeatureClick(feature, e),
|
|
2104
|
-
userData: { type: "feature", featureId: feature.id },
|
|
2105
|
-
children: [
|
|
2106
|
-
/* @__PURE__ */ jsxRuntime.jsx("dodecahedronGeometry", { args: [0.3, 0] }),
|
|
2107
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: 8421504 })
|
|
2108
|
-
]
|
|
2109
|
-
}
|
|
2110
|
-
);
|
|
2111
|
-
}
|
|
2112
|
-
return null;
|
|
1770
|
+
|
|
1771
|
+
// components/game/three/game3dTheme.ts
|
|
1772
|
+
var GRID_COLORS_3D = {
|
|
1773
|
+
cell: "#444444",
|
|
1774
|
+
section: "#666666"
|
|
1775
|
+
};
|
|
1776
|
+
var DEFAULT_BACKGROUND_3D = "#1a1a2e";
|
|
1777
|
+
function cn(...inputs) {
|
|
1778
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
2113
1779
|
}
|
|
2114
|
-
var
|
|
1780
|
+
var DEFAULT_GRID_CONFIG = {
|
|
1781
|
+
cellSize: 1,
|
|
1782
|
+
offsetX: 0,
|
|
1783
|
+
offsetZ: 0
|
|
1784
|
+
};
|
|
1785
|
+
var GameCanvas3D = React7.forwardRef(
|
|
2115
1786
|
({
|
|
2116
|
-
tiles = [],
|
|
2117
|
-
units = [],
|
|
2118
|
-
features = [],
|
|
2119
|
-
events = [],
|
|
2120
|
-
orientation = "standard",
|
|
2121
1787
|
cameraMode = "isometric",
|
|
1788
|
+
followTarget,
|
|
2122
1789
|
showGrid = true,
|
|
2123
|
-
|
|
2124
|
-
showTileInfo = false,
|
|
1790
|
+
orientation = "standard",
|
|
2125
1791
|
overlay = "default",
|
|
2126
1792
|
shadows = true,
|
|
2127
|
-
backgroundColor =
|
|
2128
|
-
onTileClick,
|
|
2129
|
-
onUnitClick,
|
|
2130
|
-
onFeatureClick,
|
|
2131
|
-
onCanvasClick,
|
|
2132
|
-
onTileHover,
|
|
2133
|
-
onUnitAnimation,
|
|
2134
|
-
assetLoader: customAssetLoader,
|
|
2135
|
-
tileRenderer: CustomTileRenderer,
|
|
2136
|
-
unitRenderer: CustomUnitRenderer,
|
|
2137
|
-
featureRenderer: CustomFeatureRenderer,
|
|
1793
|
+
backgroundColor = DEFAULT_BACKGROUND_3D,
|
|
2138
1794
|
className,
|
|
2139
1795
|
isLoading: externalLoading,
|
|
2140
1796
|
error: externalError,
|
|
2141
|
-
preloadAssets = [],
|
|
2142
1797
|
tileClickEvent,
|
|
2143
1798
|
unitClickEvent,
|
|
2144
1799
|
featureClickEvent,
|
|
@@ -2148,31 +1803,17 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2148
1803
|
unitAnimationEvent,
|
|
2149
1804
|
cameraChangeEvent,
|
|
2150
1805
|
loadingMessage = "Loading 3D Scene...",
|
|
2151
|
-
useInstancing = true,
|
|
2152
|
-
validMoves = [],
|
|
2153
|
-
attackTargets = [],
|
|
2154
|
-
selectedTileIds = [],
|
|
2155
|
-
selectedUnitId = null,
|
|
2156
|
-
unitScale = 1,
|
|
2157
1806
|
keyMap,
|
|
2158
1807
|
keyUpMap,
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
worldHeight = 400,
|
|
2162
|
-
pixelsPerUnit = 32,
|
|
2163
|
-
playerSprite,
|
|
2164
|
-
tileSprites,
|
|
2165
|
-
assetManifest,
|
|
2166
|
-
interpolateUnits = false,
|
|
2167
|
-
children
|
|
1808
|
+
children,
|
|
1809
|
+
drawables
|
|
2168
1810
|
}, ref) => {
|
|
2169
|
-
const containerRef =
|
|
2170
|
-
const controlsRef =
|
|
2171
|
-
const [
|
|
2172
|
-
const [internalError, setInternalError] = React8.useState(null);
|
|
1811
|
+
const containerRef = React7.useRef(null);
|
|
1812
|
+
const controlsRef = React7.useRef(null);
|
|
1813
|
+
const [internalError, setInternalError] = React7.useState(null);
|
|
2173
1814
|
const eventBus = useEventBus();
|
|
2174
|
-
const keysRef =
|
|
2175
|
-
|
|
1815
|
+
const keysRef = React7.useRef(/* @__PURE__ */ new Set());
|
|
1816
|
+
React7.useEffect(() => {
|
|
2176
1817
|
if (!keyMap && !keyUpMap) return;
|
|
2177
1818
|
const down = (e) => {
|
|
2178
1819
|
if (keysRef.current.has(e.code)) return;
|
|
@@ -2195,35 +1836,6 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2195
1836
|
window.removeEventListener("keyup", up);
|
|
2196
1837
|
};
|
|
2197
1838
|
}, [keyMap, keyUpMap, eventBus]);
|
|
2198
|
-
React8.useEffect(() => {
|
|
2199
|
-
const el = containerRef.current;
|
|
2200
|
-
if (!el) return;
|
|
2201
|
-
let node = el;
|
|
2202
|
-
let depth = 0;
|
|
2203
|
-
while (node && depth < 8) {
|
|
2204
|
-
const cs = window.getComputedStyle(node);
|
|
2205
|
-
const rect = node.getBoundingClientRect();
|
|
2206
|
-
console.log("[almadar:ui:game:3d-height]", {
|
|
2207
|
-
depth,
|
|
2208
|
-
tag: node.tagName,
|
|
2209
|
-
id: node.id || void 0,
|
|
2210
|
-
className: node.className?.slice?.(0, 60) || void 0,
|
|
2211
|
-
rectHeight: rect.height,
|
|
2212
|
-
computedHeight: cs.height,
|
|
2213
|
-
computedMinHeight: cs.minHeight,
|
|
2214
|
-
computedFlex: cs.flex,
|
|
2215
|
-
computedOverflow: cs.overflow
|
|
2216
|
-
});
|
|
2217
|
-
node = node.parentElement;
|
|
2218
|
-
depth++;
|
|
2219
|
-
}
|
|
2220
|
-
}, []);
|
|
2221
|
-
const { sheetUrls: atlasSheetUrls, resolveUnitFrame } = useUnitSpriteAtlas(units);
|
|
2222
|
-
const preloadUrls = React8.useMemo(() => [...preloadAssets, ...atlasSheetUrls], [preloadAssets, atlasSheetUrls]);
|
|
2223
|
-
const { isLoading: assetsLoading, progress, loaded, total } = useAssetLoader({
|
|
2224
|
-
preloadUrls,
|
|
2225
|
-
loader: customAssetLoader
|
|
2226
|
-
});
|
|
2227
1839
|
const eventHandlers = useGameCanvas3DEvents({
|
|
2228
1840
|
tileClickEvent,
|
|
2229
1841
|
unitClickEvent,
|
|
@@ -2232,35 +1844,36 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2232
1844
|
tileHoverEvent,
|
|
2233
1845
|
tileLeaveEvent,
|
|
2234
1846
|
unitAnimationEvent,
|
|
2235
|
-
cameraChangeEvent
|
|
2236
|
-
onTileClick,
|
|
2237
|
-
onUnitClick,
|
|
2238
|
-
onFeatureClick,
|
|
2239
|
-
onCanvasClick,
|
|
2240
|
-
onTileHover,
|
|
2241
|
-
onUnitAnimation
|
|
1847
|
+
cameraChangeEvent
|
|
2242
1848
|
});
|
|
2243
|
-
const
|
|
2244
|
-
|
|
1849
|
+
const drawnItems = React7.useMemo(() => collectDrawnItems(drawables ?? []), [drawables]);
|
|
1850
|
+
const scenePositions = React7.useMemo(() => drawnItems.map((i) => i.pos), [drawnItems]);
|
|
1851
|
+
const hitIndex = React7.useMemo(() => buildHitIndex(drawnItems), [drawnItems]);
|
|
1852
|
+
const gridBounds = React7.useMemo(() => {
|
|
1853
|
+
if (scenePositions.length === 0) {
|
|
2245
1854
|
return { minX: 0, maxX: 10, minZ: 0, maxZ: 10 };
|
|
2246
1855
|
}
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
return
|
|
1856
|
+
let minX = Infinity;
|
|
1857
|
+
let maxX = -Infinity;
|
|
1858
|
+
let minZ = Infinity;
|
|
1859
|
+
let maxZ = -Infinity;
|
|
1860
|
+
for (const p of scenePositions) {
|
|
1861
|
+
if (p.x < minX) minX = p.x;
|
|
1862
|
+
if (p.x > maxX) maxX = p.x;
|
|
1863
|
+
if (p.y < minZ) minZ = p.y;
|
|
1864
|
+
if (p.y > maxZ) maxZ = p.y;
|
|
1865
|
+
}
|
|
1866
|
+
return { minX, maxX, minZ, maxZ };
|
|
1867
|
+
}, [scenePositions]);
|
|
1868
|
+
const cameraTarget = React7.useMemo(
|
|
1869
|
+
() => [
|
|
2258
1870
|
(gridBounds.minX + gridBounds.maxX) / 2,
|
|
2259
1871
|
0,
|
|
2260
1872
|
(gridBounds.minZ + gridBounds.maxZ) / 2
|
|
2261
|
-
]
|
|
2262
|
-
|
|
2263
|
-
|
|
1873
|
+
],
|
|
1874
|
+
[gridBounds]
|
|
1875
|
+
);
|
|
1876
|
+
const gridConfig = React7.useMemo(
|
|
2264
1877
|
() => ({
|
|
2265
1878
|
...DEFAULT_GRID_CONFIG,
|
|
2266
1879
|
offsetX: -(gridBounds.maxX - gridBounds.minX) / 2,
|
|
@@ -2268,15 +1881,15 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2268
1881
|
}),
|
|
2269
1882
|
[gridBounds]
|
|
2270
1883
|
);
|
|
2271
|
-
const
|
|
2272
|
-
(
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
},
|
|
1884
|
+
const drawableProjector = React7.useMemo(
|
|
1885
|
+
() => create3DProjector({
|
|
1886
|
+
cellSize: gridConfig.cellSize,
|
|
1887
|
+
offsetX: -gridBounds.minX * gridConfig.cellSize,
|
|
1888
|
+
offsetZ: -gridBounds.minZ * gridConfig.cellSize
|
|
1889
|
+
}),
|
|
2277
1890
|
[gridBounds, gridConfig]
|
|
2278
1891
|
);
|
|
2279
|
-
|
|
1892
|
+
React7.useImperativeHandle(ref, () => ({
|
|
2280
1893
|
getCameraPosition: () => {
|
|
2281
1894
|
if (controlsRef.current) {
|
|
2282
1895
|
const pos = controlsRef.current.object.position;
|
|
@@ -2307,43 +1920,9 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2307
1920
|
return canvas.toDataURL("image/png");
|
|
2308
1921
|
}
|
|
2309
1922
|
return null;
|
|
2310
|
-
}
|
|
2311
|
-
export: () => ({
|
|
2312
|
-
tiles,
|
|
2313
|
-
units,
|
|
2314
|
-
features
|
|
2315
|
-
})
|
|
1923
|
+
}
|
|
2316
1924
|
}));
|
|
2317
|
-
const
|
|
2318
|
-
(tile, event) => {
|
|
2319
|
-
eventHandlers.handleTileClick(tile, event);
|
|
2320
|
-
},
|
|
2321
|
-
[eventHandlers]
|
|
2322
|
-
);
|
|
2323
|
-
const handleUnitClick = React8.useCallback(
|
|
2324
|
-
(unit, event) => {
|
|
2325
|
-
eventHandlers.handleUnitClick(unit, event);
|
|
2326
|
-
},
|
|
2327
|
-
[eventHandlers]
|
|
2328
|
-
);
|
|
2329
|
-
const handleFeatureClick = React8.useCallback(
|
|
2330
|
-
(feature, event) => {
|
|
2331
|
-
if (event) {
|
|
2332
|
-
eventHandlers.handleFeatureClick(feature, event);
|
|
2333
|
-
}
|
|
2334
|
-
},
|
|
2335
|
-
[eventHandlers]
|
|
2336
|
-
);
|
|
2337
|
-
const handleTileHover = React8.useCallback(
|
|
2338
|
-
(tile, event) => {
|
|
2339
|
-
setHoveredTile(tile);
|
|
2340
|
-
if (event) {
|
|
2341
|
-
eventHandlers.handleTileHover(tile, event);
|
|
2342
|
-
}
|
|
2343
|
-
},
|
|
2344
|
-
[eventHandlers]
|
|
2345
|
-
);
|
|
2346
|
-
const cameraConfig = React8.useMemo(() => {
|
|
1925
|
+
const cameraConfig = React7.useMemo(() => {
|
|
2347
1926
|
const size = Math.max(
|
|
2348
1927
|
gridBounds.maxX - gridBounds.minX,
|
|
2349
1928
|
gridBounds.maxZ - gridBounds.minZ,
|
|
@@ -2355,65 +1934,45 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2355
1934
|
const d = size * 1;
|
|
2356
1935
|
switch (cameraMode) {
|
|
2357
1936
|
case "isometric":
|
|
2358
|
-
return {
|
|
2359
|
-
position: [cx + d, d * 0.8, cz + d],
|
|
2360
|
-
fov: 45
|
|
2361
|
-
};
|
|
1937
|
+
return { position: [cx + d, d * 0.8, cz + d], fov: 45 };
|
|
2362
1938
|
case "top-down":
|
|
2363
|
-
return {
|
|
2364
|
-
position: [cx, d * 2, cz + d * 0.35],
|
|
2365
|
-
fov: 45
|
|
2366
|
-
};
|
|
1939
|
+
return { position: [cx, d * 2, cz + d * 0.35], fov: 45 };
|
|
2367
1940
|
case "follow":
|
|
2368
|
-
return {
|
|
2369
|
-
position: [cx, d * 0.5, cz + d],
|
|
2370
|
-
fov: 45
|
|
2371
|
-
};
|
|
1941
|
+
return { position: [cx, d * 0.5, cz + d], fov: 45 };
|
|
2372
1942
|
case "perspective":
|
|
2373
1943
|
default:
|
|
2374
|
-
return {
|
|
2375
|
-
position: [cx + d, d, cz + d],
|
|
2376
|
-
fov: 45
|
|
2377
|
-
};
|
|
1944
|
+
return { position: [cx + d, d, cz + d], fov: 45 };
|
|
2378
1945
|
}
|
|
2379
1946
|
}, [cameraMode, gridBounds]);
|
|
2380
|
-
const
|
|
2381
|
-
if (
|
|
2382
|
-
return [
|
|
2383
|
-
(player.x + (player.width ?? 32) / 2) / pixelsPerUnit,
|
|
2384
|
-
(worldHeight - player.y - (player.height ?? 48) / 2) / pixelsPerUnit,
|
|
2385
|
-
0
|
|
2386
|
-
];
|
|
2387
|
-
}
|
|
2388
|
-
const selected = units.find((u) => u.id === selectedUnitId);
|
|
2389
|
-
if (selected) {
|
|
2390
|
-
const sx = selected.x ?? selected.position?.x ?? 0;
|
|
2391
|
-
const sz = selected.z ?? selected.y ?? selected.position?.y ?? 0;
|
|
2392
|
-
return [
|
|
2393
|
-
(sx - gridBounds.minX) * gridConfig.cellSize,
|
|
2394
|
-
0,
|
|
2395
|
-
(sz - gridBounds.minZ) * gridConfig.cellSize
|
|
2396
|
-
];
|
|
2397
|
-
}
|
|
1947
|
+
const followWorld = React7.useMemo(() => {
|
|
1948
|
+
if (followTarget) return drawableProjector.toWorld(followTarget);
|
|
2398
1949
|
return cameraTarget;
|
|
2399
|
-
}, [
|
|
2400
|
-
const followOffset =
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
1950
|
+
}, [followTarget, drawableProjector, cameraTarget]);
|
|
1951
|
+
const followOffset = React7.useMemo(
|
|
1952
|
+
() => cameraMode === "chase" ? [0, 4, -7] : [5, 7, 5],
|
|
1953
|
+
[cameraMode]
|
|
1954
|
+
);
|
|
1955
|
+
const handleGroundClick = React7.useCallback((e) => {
|
|
1956
|
+
if (!tileClickEvent && !unitClickEvent) return;
|
|
1957
|
+
e.stopPropagation();
|
|
1958
|
+
const cell = {
|
|
1959
|
+
x: Math.round(e.point.x / gridConfig.cellSize + gridBounds.minX),
|
|
1960
|
+
y: Math.round(e.point.z / gridConfig.cellSize + gridBounds.minZ)
|
|
1961
|
+
};
|
|
1962
|
+
const hitId = hitIndex.get(`${cell.x},${cell.y}`);
|
|
1963
|
+
if (hitId !== void 0 && unitClickEvent) {
|
|
1964
|
+
eventBus.emit(`UI:${unitClickEvent}`, { unitId: hitId, x: cell.x, z: cell.y });
|
|
1965
|
+
} else if (tileClickEvent) {
|
|
1966
|
+
eventBus.emit(`UI:${tileClickEvent}`, { x: cell.x, z: cell.y });
|
|
2407
1967
|
}
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
if (externalLoading || assetsLoading && preloadAssets.length > 0) {
|
|
1968
|
+
}, [tileClickEvent, unitClickEvent, gridConfig, gridBounds, hitIndex, eventBus]);
|
|
1969
|
+
if (externalLoading) {
|
|
2411
1970
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2412
1971
|
Canvas3DLoadingState,
|
|
2413
1972
|
{
|
|
2414
|
-
progress,
|
|
2415
|
-
loaded,
|
|
2416
|
-
total,
|
|
1973
|
+
progress: 1,
|
|
1974
|
+
loaded: 1,
|
|
1975
|
+
total: 1,
|
|
2417
1976
|
message: loadingMessage,
|
|
2418
1977
|
className
|
|
2419
1978
|
}
|
|
@@ -2431,7 +1990,7 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2431
1990
|
{
|
|
2432
1991
|
onError: (err) => setInternalError(err.message),
|
|
2433
1992
|
onReset: () => setInternalError(null),
|
|
2434
|
-
children: /* @__PURE__ */ jsxRuntime.
|
|
1993
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2435
1994
|
"div",
|
|
2436
1995
|
{
|
|
2437
1996
|
ref: containerRef,
|
|
@@ -2440,195 +1999,103 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2440
1999
|
"data-orientation": orientation,
|
|
2441
2000
|
"data-camera-mode": cameraMode,
|
|
2442
2001
|
"data-overlay": overlay,
|
|
2443
|
-
children:
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2002
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2003
|
+
fiber.Canvas,
|
|
2004
|
+
{
|
|
2005
|
+
shadows,
|
|
2006
|
+
camera: {
|
|
2007
|
+
position: cameraConfig.position,
|
|
2008
|
+
fov: cameraConfig.fov,
|
|
2009
|
+
near: 0.1,
|
|
2010
|
+
far: 1e3
|
|
2011
|
+
},
|
|
2012
|
+
style: { background: backgroundColor, position: "absolute", inset: 0 },
|
|
2013
|
+
onClick: (e) => {
|
|
2014
|
+
if (e.target === e.currentTarget) {
|
|
2015
|
+
eventHandlers.handleCanvasClick(e);
|
|
2016
|
+
}
|
|
2017
|
+
},
|
|
2018
|
+
children: [
|
|
2019
|
+
/* @__PURE__ */ jsxRuntime.jsx(CameraController3D, { onCameraChange: eventHandlers.handleCameraChange }),
|
|
2020
|
+
(cameraMode === "follow" || cameraMode === "chase") && /* @__PURE__ */ jsxRuntime.jsx(FollowCamera3D, { target: followWorld, offset: followOffset }),
|
|
2021
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2022
|
+
Lighting3D,
|
|
2023
|
+
{
|
|
2024
|
+
shadows,
|
|
2025
|
+
shadowBias: -4e-4,
|
|
2026
|
+
shadowNormalBias: 0.04,
|
|
2027
|
+
shadowCameraSize: 5,
|
|
2028
|
+
shadowCameraNear: 0.5,
|
|
2029
|
+
shadowCameraFar: 500
|
|
2458
2030
|
}
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
cellColor: "#444444",
|
|
2497
|
-
sectionSize: 5,
|
|
2498
|
-
sectionThickness: 1.5,
|
|
2499
|
-
sectionColor: "#666666",
|
|
2500
|
-
fadeDistance: 50,
|
|
2501
|
-
fadeStrength: 1
|
|
2502
|
-
}
|
|
2503
|
-
),
|
|
2504
|
-
player ? (
|
|
2505
|
-
/* Side-scroller mode — LOLO-owned pixel space mapped to world units */
|
|
2506
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2507
|
-
SideScene,
|
|
2508
|
-
{
|
|
2509
|
-
player,
|
|
2510
|
-
platforms,
|
|
2511
|
-
worldHeight,
|
|
2512
|
-
ppu: pixelsPerUnit,
|
|
2513
|
-
playerSprite,
|
|
2514
|
-
tileSprites,
|
|
2515
|
-
interpolate: interpolateUnits,
|
|
2516
|
-
features,
|
|
2517
|
-
events,
|
|
2518
|
-
assetManifest
|
|
2519
|
-
}
|
|
2520
|
-
)
|
|
2521
|
-
) : /* @__PURE__ */ jsxRuntime.jsxs("group", { children: [
|
|
2522
|
-
tiles.map((tile, index) => {
|
|
2523
|
-
const position = gridToWorld2(
|
|
2524
|
-
tile.x,
|
|
2525
|
-
tile.z ?? tile.y ?? 0,
|
|
2526
|
-
tile.elevation ?? 0
|
|
2527
|
-
);
|
|
2528
|
-
const key = tile.id ?? `tile-${index}`;
|
|
2529
|
-
if (CustomTileRenderer) {
|
|
2530
|
-
return /* @__PURE__ */ jsxRuntime.jsx(CustomTileRenderer, { tile, position }, key);
|
|
2531
|
-
}
|
|
2532
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2533
|
-
DefaultTile,
|
|
2534
|
-
{
|
|
2535
|
-
tile,
|
|
2536
|
-
position,
|
|
2537
|
-
model: tile.modelUrl ?? assetManifest?.terrains?.[tile.terrain ?? tile.type ?? ""],
|
|
2538
|
-
isSelected: tile.id ? selectedTileIds.includes(tile.id) : false,
|
|
2539
|
-
isHovered: hoveredTile?.id === tile.id,
|
|
2540
|
-
isValidMove: validMoves.some((m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)),
|
|
2541
|
-
isAttackTarget: attackTargets.some((m) => m.x === tile.x && m.z === (tile.z ?? tile.y ?? 0)),
|
|
2542
|
-
onTileClick: handleTileClick,
|
|
2543
|
-
onTileHover: handleTileHover
|
|
2544
|
-
},
|
|
2545
|
-
key
|
|
2546
|
-
);
|
|
2547
|
-
}),
|
|
2548
|
-
features.map((feature, index) => {
|
|
2549
|
-
const position = gridToWorld2(
|
|
2550
|
-
feature.x,
|
|
2551
|
-
feature.z ?? feature.y ?? 0,
|
|
2552
|
-
(feature.elevation ?? 0) + TILE_TOP_OFFSET
|
|
2553
|
-
);
|
|
2554
|
-
const key = feature.id ?? `feature-${index}`;
|
|
2555
|
-
if (CustomFeatureRenderer) {
|
|
2556
|
-
return /* @__PURE__ */ jsxRuntime.jsx(CustomFeatureRenderer, { feature, position }, key);
|
|
2557
|
-
}
|
|
2558
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2559
|
-
DefaultFeature,
|
|
2560
|
-
{
|
|
2561
|
-
feature,
|
|
2562
|
-
position,
|
|
2563
|
-
model: feature.assetUrl ?? assetManifest?.features?.[feature.type],
|
|
2564
|
-
onFeatureClick: handleFeatureClick
|
|
2565
|
-
},
|
|
2566
|
-
key
|
|
2567
|
-
);
|
|
2568
|
-
}),
|
|
2569
|
-
units.map((unit) => {
|
|
2570
|
-
const position = gridToWorld2(
|
|
2571
|
-
unit.x ?? unit.position?.x ?? 0,
|
|
2572
|
-
unit.z ?? unit.y ?? unit.position?.y ?? 0,
|
|
2573
|
-
(unit.elevation ?? 0) + TILE_TOP_OFFSET
|
|
2574
|
-
);
|
|
2575
|
-
return /* @__PURE__ */ jsxRuntime.jsx(LerpedGroup, { target: position, enabled: interpolateUnits, children: CustomUnitRenderer ? /* @__PURE__ */ jsxRuntime.jsx(CustomUnitRenderer, { unit, position: [0, 0, 0] }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2576
|
-
DefaultUnit,
|
|
2031
|
+
),
|
|
2032
|
+
showGrid && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2033
|
+
drei.Grid,
|
|
2034
|
+
{
|
|
2035
|
+
args: [
|
|
2036
|
+
Math.max(gridBounds.maxX - gridBounds.minX + 2, 10),
|
|
2037
|
+
Math.max(gridBounds.maxZ - gridBounds.minZ + 2, 10)
|
|
2038
|
+
],
|
|
2039
|
+
position: [
|
|
2040
|
+
(gridBounds.maxX - gridBounds.minX) / 2 - 0.5,
|
|
2041
|
+
0,
|
|
2042
|
+
(gridBounds.maxZ - gridBounds.minZ) / 2 - 0.5
|
|
2043
|
+
],
|
|
2044
|
+
cellSize: 1,
|
|
2045
|
+
cellThickness: 1,
|
|
2046
|
+
cellColor: GRID_COLORS_3D.cell,
|
|
2047
|
+
sectionSize: 5,
|
|
2048
|
+
sectionThickness: 1.5,
|
|
2049
|
+
sectionColor: GRID_COLORS_3D.section,
|
|
2050
|
+
fadeDistance: 50,
|
|
2051
|
+
fadeStrength: 1
|
|
2052
|
+
}
|
|
2053
|
+
),
|
|
2054
|
+
drawables && drawables.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("group", { children: drawables.map((node, i) => /* @__PURE__ */ jsxRuntime.jsx(Drawable3D, { node, projector: drawableProjector }, i)) }),
|
|
2055
|
+
(tileClickEvent || unitClickEvent) && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2056
|
+
"mesh",
|
|
2057
|
+
{
|
|
2058
|
+
rotation: [-Math.PI / 2, 0, 0],
|
|
2059
|
+
position: [
|
|
2060
|
+
(gridBounds.maxX - gridBounds.minX) / 2 * gridConfig.cellSize,
|
|
2061
|
+
0,
|
|
2062
|
+
(gridBounds.maxZ - gridBounds.minZ) / 2 * gridConfig.cellSize
|
|
2063
|
+
],
|
|
2064
|
+
onClick: handleGroundClick,
|
|
2065
|
+
children: [
|
|
2066
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2067
|
+
"planeGeometry",
|
|
2577
2068
|
{
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
unitScale,
|
|
2583
|
-
cellSize: gridConfig.cellSize,
|
|
2584
|
-
resolveUnitFrame,
|
|
2585
|
-
onUnitClick: handleUnitClick
|
|
2069
|
+
args: [
|
|
2070
|
+
(gridBounds.maxX - gridBounds.minX + 4) * gridConfig.cellSize,
|
|
2071
|
+
(gridBounds.maxZ - gridBounds.minZ + 4) * gridConfig.cellSize
|
|
2072
|
+
]
|
|
2586
2073
|
}
|
|
2587
|
-
)
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
touches: { ONE: THREE6__namespace.TOUCH.ROTATE, TWO: THREE6__namespace.TOUCH.DOLLY_PAN },
|
|
2613
|
-
minDistance: 2,
|
|
2614
|
-
maxDistance: 100,
|
|
2615
|
-
maxPolarAngle: Math.PI / 2 - 0.1
|
|
2616
|
-
}
|
|
2617
|
-
)
|
|
2618
|
-
]
|
|
2619
|
-
}
|
|
2620
|
-
),
|
|
2621
|
-
showCoordinates && hoveredTile && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__coordinates", children: [
|
|
2622
|
-
"X: ",
|
|
2623
|
-
hoveredTile.x,
|
|
2624
|
-
", Z: ",
|
|
2625
|
-
hoveredTile.z ?? hoveredTile.y ?? 0
|
|
2626
|
-
] }),
|
|
2627
|
-
showTileInfo && hoveredTile && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-canvas-3d__tile-info", children: [
|
|
2628
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "tile-info__type", children: hoveredTile.type }),
|
|
2629
|
-
hoveredTile.terrain && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tile-info__terrain", children: hoveredTile.terrain })
|
|
2630
|
-
] })
|
|
2631
|
-
]
|
|
2074
|
+
),
|
|
2075
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { transparent: true, opacity: 0, depthWrite: false })
|
|
2076
|
+
]
|
|
2077
|
+
}
|
|
2078
|
+
),
|
|
2079
|
+
children,
|
|
2080
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2081
|
+
drei.OrbitControls,
|
|
2082
|
+
{
|
|
2083
|
+
ref: controlsRef,
|
|
2084
|
+
enabled: cameraMode !== "follow" && cameraMode !== "chase",
|
|
2085
|
+
target: cameraTarget,
|
|
2086
|
+
enableDamping: true,
|
|
2087
|
+
dampingFactor: 0.05,
|
|
2088
|
+
enableZoom: true,
|
|
2089
|
+
enablePan: true,
|
|
2090
|
+
touches: { ONE: THREE6__namespace.TOUCH.ROTATE, TWO: THREE6__namespace.TOUCH.DOLLY_PAN },
|
|
2091
|
+
minDistance: 2,
|
|
2092
|
+
maxDistance: 100,
|
|
2093
|
+
maxPolarAngle: Math.PI / 2 - 0.1
|
|
2094
|
+
}
|
|
2095
|
+
)
|
|
2096
|
+
]
|
|
2097
|
+
}
|
|
2098
|
+
)
|
|
2632
2099
|
}
|
|
2633
2100
|
)
|
|
2634
2101
|
}
|
|
@@ -2636,1086 +2103,357 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2636
2103
|
}
|
|
2637
2104
|
);
|
|
2638
2105
|
GameCanvas3D.displayName = "GameCanvas3D";
|
|
2639
|
-
var
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
"phosphor-duotone",
|
|
2645
|
-
"tabler",
|
|
2646
|
-
"fa-solid"
|
|
2647
|
-
];
|
|
2648
|
-
function getCurrentIconFamily() {
|
|
2649
|
-
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
2650
|
-
return DEFAULT_FAMILY;
|
|
2651
|
-
}
|
|
2652
|
-
const raw = getComputedStyle(document.documentElement).getPropertyValue("--icon-family").trim().replace(/^["']|["']$/g, "");
|
|
2653
|
-
return VALID_FAMILIES.includes(raw) ? raw : DEFAULT_FAMILY;
|
|
2654
|
-
}
|
|
2655
|
-
var cachedFamily = null;
|
|
2656
|
-
var listeners = /* @__PURE__ */ new Set();
|
|
2657
|
-
var observer = null;
|
|
2658
|
-
function ensureObserver() {
|
|
2659
|
-
if (typeof window === "undefined" || observer) return;
|
|
2660
|
-
observer = new MutationObserver(() => {
|
|
2661
|
-
const next = getCurrentIconFamily();
|
|
2662
|
-
if (next !== cachedFamily) {
|
|
2663
|
-
cachedFamily = next;
|
|
2664
|
-
listeners.forEach((fn) => fn());
|
|
2665
|
-
}
|
|
2666
|
-
});
|
|
2667
|
-
observer.observe(document.documentElement, {
|
|
2668
|
-
attributes: true,
|
|
2669
|
-
attributeFilter: ["data-theme", "style"]
|
|
2670
|
-
});
|
|
2671
|
-
cachedFamily = getCurrentIconFamily();
|
|
2672
|
-
}
|
|
2673
|
-
function subscribeIconFamily(notify) {
|
|
2674
|
-
ensureObserver();
|
|
2675
|
-
listeners.add(notify);
|
|
2676
|
-
return () => {
|
|
2677
|
-
listeners.delete(notify);
|
|
2678
|
-
};
|
|
2679
|
-
}
|
|
2680
|
-
function getIconFamilySnapshot() {
|
|
2681
|
-
if (cachedFamily !== null) return cachedFamily;
|
|
2682
|
-
cachedFamily = getCurrentIconFamily();
|
|
2683
|
-
return cachedFamily;
|
|
2684
|
-
}
|
|
2685
|
-
function getIconFamilyServerSnapshot() {
|
|
2686
|
-
return DEFAULT_FAMILY;
|
|
2687
|
-
}
|
|
2688
|
-
function useIconFamily() {
|
|
2689
|
-
return React8.useSyncExternalStore(
|
|
2690
|
-
subscribeIconFamily,
|
|
2691
|
-
getIconFamilySnapshot,
|
|
2692
|
-
getIconFamilyServerSnapshot
|
|
2693
|
-
);
|
|
2694
|
-
}
|
|
2695
|
-
function kebabToPascal(name) {
|
|
2696
|
-
return name.split("-").map((part) => {
|
|
2697
|
-
if (/^\d+$/.test(part)) return part;
|
|
2698
|
-
return part.charAt(0).toUpperCase() + part.slice(1);
|
|
2699
|
-
}).join("");
|
|
2700
|
-
}
|
|
2701
|
-
var libPromises = /* @__PURE__ */ new Map();
|
|
2702
|
-
function loadLib(key, importer) {
|
|
2703
|
-
let p = libPromises.get(key);
|
|
2704
|
-
if (!p) {
|
|
2705
|
-
p = importer();
|
|
2706
|
-
libPromises.set(key, p);
|
|
2707
|
-
}
|
|
2708
|
-
return p;
|
|
2709
|
-
}
|
|
2710
|
-
function lazyFamilyIcon(libKey, importer, pick, fallbackName, family) {
|
|
2711
|
-
const Lazy = React8__namespace.default.lazy(async () => {
|
|
2712
|
-
const lib = await loadLib(libKey, importer);
|
|
2713
|
-
const Comp = pick(lib);
|
|
2714
|
-
if (!Comp) {
|
|
2715
|
-
warnFallback(fallbackName, family);
|
|
2716
|
-
return { default: makeLucideAdapter(fallbackName, true) };
|
|
2717
|
-
}
|
|
2718
|
-
return { default: Comp };
|
|
2719
|
-
});
|
|
2720
|
-
const Wrapped = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2721
|
-
React8__namespace.default.Suspense,
|
|
2722
|
-
{
|
|
2723
|
-
fallback: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2724
|
-
"span",
|
|
2725
|
-
{
|
|
2726
|
-
"aria-hidden": true,
|
|
2727
|
-
className: props.className,
|
|
2728
|
-
style: { display: "inline-block", ...props.style }
|
|
2729
|
-
}
|
|
2730
|
-
),
|
|
2731
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(Lazy, { ...props })
|
|
2732
|
-
}
|
|
2733
|
-
);
|
|
2734
|
-
Wrapped.displayName = `Lazy.${libKey}.${fallbackName}`;
|
|
2735
|
-
return Wrapped;
|
|
2736
|
-
}
|
|
2737
|
-
var lucideAliases = {
|
|
2738
|
-
close: LucideIcons__namespace.X,
|
|
2739
|
-
trash: LucideIcons__namespace.Trash2,
|
|
2740
|
-
loader: LucideIcons__namespace.Loader2,
|
|
2741
|
-
stop: LucideIcons__namespace.Square,
|
|
2742
|
-
volume: LucideIcons__namespace.Volume2,
|
|
2743
|
-
"volume-off": LucideIcons__namespace.VolumeX,
|
|
2744
|
-
refresh: LucideIcons__namespace.RefreshCw,
|
|
2745
|
-
share: LucideIcons__namespace.Share2,
|
|
2746
|
-
"sort-asc": LucideIcons__namespace.ArrowUpNarrowWide,
|
|
2747
|
-
"sort-desc": LucideIcons__namespace.ArrowDownNarrowWide
|
|
2748
|
-
};
|
|
2749
|
-
function isComponentLike(v) {
|
|
2750
|
-
return v != null && (typeof v === "function" || typeof v === "object");
|
|
2751
|
-
}
|
|
2752
|
-
function resolveLucide(name) {
|
|
2753
|
-
if (lucideAliases[name]) return lucideAliases[name];
|
|
2754
|
-
const pascal = kebabToPascal(name);
|
|
2755
|
-
const lucideMap = LucideIcons__namespace;
|
|
2756
|
-
const direct = lucideMap[pascal];
|
|
2757
|
-
if (isComponentLike(direct)) return direct;
|
|
2758
|
-
const asIs = lucideMap[name];
|
|
2759
|
-
if (isComponentLike(asIs)) return asIs;
|
|
2760
|
-
return LucideIcons__namespace.HelpCircle;
|
|
2761
|
-
}
|
|
2762
|
-
var phosphorAliases = {
|
|
2763
|
-
// lucide name → phosphor PascalCase name
|
|
2764
|
-
// Actions
|
|
2765
|
-
plus: "Plus",
|
|
2766
|
-
minus: "Minus",
|
|
2767
|
-
x: "X",
|
|
2768
|
-
check: "Check",
|
|
2769
|
-
close: "X",
|
|
2770
|
-
edit: "PencilSimple",
|
|
2771
|
-
pencil: "PencilSimple",
|
|
2772
|
-
trash: "Trash",
|
|
2773
|
-
save: "FloppyDisk",
|
|
2774
|
-
copy: "Copy",
|
|
2775
|
-
share: "Share",
|
|
2776
|
-
send: "PaperPlaneRight",
|
|
2777
|
-
download: "DownloadSimple",
|
|
2778
|
-
upload: "UploadSimple",
|
|
2779
|
-
archive: "Archive",
|
|
2780
|
-
refresh: "ArrowsClockwise",
|
|
2781
|
-
loader: "CircleNotch",
|
|
2782
|
-
link: "Link",
|
|
2783
|
-
paperclip: "Paperclip",
|
|
2784
|
-
// Navigation
|
|
2785
|
-
"chevron-down": "CaretDown",
|
|
2786
|
-
"chevron-up": "CaretUp",
|
|
2787
|
-
"chevron-left": "CaretLeft",
|
|
2788
|
-
"chevron-right": "CaretRight",
|
|
2789
|
-
"arrow-up": "ArrowUp",
|
|
2790
|
-
"arrow-down": "ArrowDown",
|
|
2791
|
-
"arrow-left": "ArrowLeft",
|
|
2792
|
-
"arrow-right": "ArrowRight",
|
|
2793
|
-
menu: "List",
|
|
2794
|
-
more: "DotsThree",
|
|
2795
|
-
"more-vertical": "DotsThreeVertical",
|
|
2796
|
-
"more-horizontal": "DotsThree",
|
|
2797
|
-
external: "ArrowSquareOut",
|
|
2798
|
-
"external-link": "ArrowSquareOut",
|
|
2799
|
-
// Files
|
|
2800
|
-
file: "File",
|
|
2801
|
-
"file-text": "FileText",
|
|
2802
|
-
"file-plus": "FilePlus",
|
|
2803
|
-
"file-minus": "FileMinus",
|
|
2804
|
-
folder: "Folder",
|
|
2805
|
-
"folder-open": "FolderOpen",
|
|
2806
|
-
document: "FileText",
|
|
2807
|
-
// Charts
|
|
2808
|
-
"bar-chart": "ChartBar",
|
|
2809
|
-
"bar-chart-2": "ChartBar",
|
|
2810
|
-
"bar-chart-3": "ChartBar",
|
|
2811
|
-
"line-chart": "ChartLine",
|
|
2812
|
-
"pie-chart": "ChartPie",
|
|
2813
|
-
activity: "Pulse",
|
|
2814
|
-
"trending-up": "TrendUp",
|
|
2815
|
-
"trending-down": "TrendDown",
|
|
2816
|
-
// Messages
|
|
2817
|
-
message: "ChatCircle",
|
|
2818
|
-
"message-circle": "ChatCircle",
|
|
2819
|
-
"message-square": "ChatText",
|
|
2820
|
-
"messages-square": "ChatsCircle",
|
|
2821
|
-
comment: "ChatCircle",
|
|
2822
|
-
comments: "ChatsCircle",
|
|
2823
|
-
inbox: "Tray",
|
|
2824
|
-
mail: "Envelope",
|
|
2825
|
-
envelope: "Envelope",
|
|
2826
|
-
// Status
|
|
2827
|
-
"alert-circle": "WarningCircle",
|
|
2828
|
-
"alert-triangle": "Warning",
|
|
2829
|
-
"check-circle": "CheckCircle",
|
|
2830
|
-
"x-circle": "XCircle",
|
|
2831
|
-
info: "Info",
|
|
2832
|
-
"help-circle": "Question",
|
|
2833
|
-
"life-buoy": "Lifebuoy",
|
|
2834
|
-
lifebuoy: "Lifebuoy",
|
|
2835
|
-
warning: "Warning",
|
|
2836
|
-
error: "WarningCircle",
|
|
2837
|
-
// Media
|
|
2838
|
-
image: "Image",
|
|
2839
|
-
video: "VideoCamera",
|
|
2840
|
-
film: "FilmStrip",
|
|
2841
|
-
camera: "Camera",
|
|
2842
|
-
music: "MusicNote",
|
|
2843
|
-
play: "Play",
|
|
2844
|
-
pause: "Pause",
|
|
2845
|
-
stop: "Stop",
|
|
2846
|
-
"skip-forward": "SkipForward",
|
|
2847
|
-
"skip-back": "SkipBack",
|
|
2848
|
-
volume: "SpeakerHigh",
|
|
2849
|
-
"volume-2": "SpeakerHigh",
|
|
2850
|
-
"volume-x": "SpeakerX",
|
|
2851
|
-
mic: "Microphone",
|
|
2852
|
-
"mic-off": "MicrophoneSlash",
|
|
2853
|
-
// People
|
|
2854
|
-
user: "User",
|
|
2855
|
-
users: "Users",
|
|
2856
|
-
"user-plus": "UserPlus",
|
|
2857
|
-
"user-check": "UserCheck",
|
|
2858
|
-
// Time
|
|
2859
|
-
calendar: "Calendar",
|
|
2860
|
-
clock: "Clock",
|
|
2861
|
-
timer: "Timer",
|
|
2862
|
-
// Location
|
|
2863
|
-
map: "MapTrifold",
|
|
2864
|
-
"map-pin": "MapPin",
|
|
2865
|
-
navigation: "NavigationArrow",
|
|
2866
|
-
compass: "Compass",
|
|
2867
|
-
globe: "Globe",
|
|
2868
|
-
target: "Target",
|
|
2869
|
-
// Project / layout
|
|
2870
|
-
kanban: "Kanban",
|
|
2871
|
-
list: "List",
|
|
2872
|
-
table: "Table",
|
|
2873
|
-
grid: "GridFour",
|
|
2874
|
-
layout: "Layout",
|
|
2875
|
-
columns: "Columns",
|
|
2876
|
-
rows: "Rows",
|
|
2877
|
-
// Misc
|
|
2878
|
-
bell: "Bell",
|
|
2879
|
-
bookmark: "Bookmark",
|
|
2880
|
-
briefcase: "Briefcase",
|
|
2881
|
-
flag: "Flag",
|
|
2882
|
-
tag: "Tag",
|
|
2883
|
-
tags: "Tag",
|
|
2884
|
-
star: "Star",
|
|
2885
|
-
heart: "Heart",
|
|
2886
|
-
home: "House",
|
|
2887
|
-
settings: "Gear",
|
|
2888
|
-
eye: "Eye",
|
|
2889
|
-
"eye-off": "EyeSlash",
|
|
2890
|
-
lock: "Lock",
|
|
2891
|
-
unlock: "LockOpen",
|
|
2892
|
-
key: "Key",
|
|
2893
|
-
shield: "Shield",
|
|
2894
|
-
search: "MagnifyingGlass",
|
|
2895
|
-
filter: "Funnel",
|
|
2896
|
-
"sort-asc": "SortAscending",
|
|
2897
|
-
"sort-desc": "SortDescending",
|
|
2898
|
-
zap: "Lightning",
|
|
2899
|
-
sparkles: "Sparkle",
|
|
2900
|
-
// Code
|
|
2901
|
-
code: "Code",
|
|
2902
|
-
terminal: "Terminal",
|
|
2903
|
-
database: "Database",
|
|
2904
|
-
server: "HardDrives",
|
|
2905
|
-
cloud: "Cloud",
|
|
2906
|
-
wifi: "WifiHigh",
|
|
2907
|
-
package: "Package",
|
|
2908
|
-
box: "Package",
|
|
2909
|
-
// Theme
|
|
2910
|
-
sun: "Sun",
|
|
2911
|
-
moon: "Moon",
|
|
2912
|
-
// Phone
|
|
2913
|
-
phone: "Phone",
|
|
2914
|
-
printer: "Printer",
|
|
2915
|
-
// Hierarchy
|
|
2916
|
-
tree: "Tree",
|
|
2917
|
-
network: "Network"
|
|
2106
|
+
var DEFAULT_CONFIG = {
|
|
2107
|
+
cellSize: 1,
|
|
2108
|
+
offsetX: 0,
|
|
2109
|
+
offsetZ: 0,
|
|
2110
|
+
elevation: 0
|
|
2918
2111
|
};
|
|
2919
|
-
function
|
|
2920
|
-
const
|
|
2921
|
-
return
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
const PhosphorComp = lib[target];
|
|
2926
|
-
if (!PhosphorComp) return null;
|
|
2927
|
-
const Component2 = PhosphorComp;
|
|
2928
|
-
const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2929
|
-
Component2,
|
|
2930
|
-
{
|
|
2931
|
-
weight,
|
|
2932
|
-
className: props.className,
|
|
2933
|
-
style: props.style,
|
|
2934
|
-
size: props.size ?? "1em"
|
|
2935
|
-
}
|
|
2936
|
-
);
|
|
2937
|
-
Adapter.displayName = `Phosphor.${target}.${weight}`;
|
|
2938
|
-
return Adapter;
|
|
2939
|
-
},
|
|
2940
|
-
name,
|
|
2941
|
-
family
|
|
2112
|
+
function gridToWorld(gridX, gridZ, config = DEFAULT_CONFIG) {
|
|
2113
|
+
const opts = { ...DEFAULT_CONFIG, ...config };
|
|
2114
|
+
return new THREE6__namespace.Vector3(
|
|
2115
|
+
gridX * opts.cellSize + opts.offsetX,
|
|
2116
|
+
opts.elevation,
|
|
2117
|
+
gridZ * opts.cellSize + opts.offsetZ
|
|
2942
2118
|
);
|
|
2943
2119
|
}
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
check: "Check",
|
|
2951
|
-
close: "X",
|
|
2952
|
-
edit: "Pencil",
|
|
2953
|
-
pencil: "Pencil",
|
|
2954
|
-
trash: "Trash",
|
|
2955
|
-
save: "DeviceFloppy",
|
|
2956
|
-
copy: "Copy",
|
|
2957
|
-
share: "Share",
|
|
2958
|
-
send: "Send",
|
|
2959
|
-
download: "Download",
|
|
2960
|
-
upload: "Upload",
|
|
2961
|
-
archive: "Archive",
|
|
2962
|
-
refresh: "Refresh",
|
|
2963
|
-
loader: "Loader2",
|
|
2964
|
-
link: "Link",
|
|
2965
|
-
paperclip: "Paperclip",
|
|
2966
|
-
external: "ExternalLink",
|
|
2967
|
-
"external-link": "ExternalLink",
|
|
2968
|
-
// Navigation
|
|
2969
|
-
"chevron-down": "ChevronDown",
|
|
2970
|
-
"chevron-up": "ChevronUp",
|
|
2971
|
-
"chevron-left": "ChevronLeft",
|
|
2972
|
-
"chevron-right": "ChevronRight",
|
|
2973
|
-
"arrow-down": "ArrowDown",
|
|
2974
|
-
"arrow-up": "ArrowUp",
|
|
2975
|
-
"arrow-left": "ArrowLeft",
|
|
2976
|
-
"arrow-right": "ArrowRight",
|
|
2977
|
-
menu: "Menu2",
|
|
2978
|
-
more: "Dots",
|
|
2979
|
-
"more-vertical": "DotsVertical",
|
|
2980
|
-
// Files
|
|
2981
|
-
file: "File",
|
|
2982
|
-
"file-text": "FileText",
|
|
2983
|
-
"file-plus": "FilePlus",
|
|
2984
|
-
"file-check": "FileCheck",
|
|
2985
|
-
"file-minus": "FileMinus",
|
|
2986
|
-
folder: "Folder",
|
|
2987
|
-
"folder-open": "FolderOpen",
|
|
2988
|
-
document: "FileText",
|
|
2989
|
-
// Charts
|
|
2990
|
-
"bar-chart": "ChartBar",
|
|
2991
|
-
"bar-chart-2": "ChartBar",
|
|
2992
|
-
"bar-chart-3": "ChartBar",
|
|
2993
|
-
"line-chart": "ChartLine",
|
|
2994
|
-
"pie-chart": "ChartPie",
|
|
2995
|
-
activity: "Activity",
|
|
2996
|
-
"trending-up": "TrendingUp",
|
|
2997
|
-
"trending-down": "TrendingDown",
|
|
2998
|
-
// Messages
|
|
2999
|
-
message: "Message",
|
|
3000
|
-
"message-circle": "MessageCircle",
|
|
3001
|
-
"message-square": "Message2",
|
|
3002
|
-
"messages-square": "Messages",
|
|
3003
|
-
comment: "Message",
|
|
3004
|
-
comments: "Messages",
|
|
3005
|
-
inbox: "Inbox",
|
|
3006
|
-
mail: "Mail",
|
|
3007
|
-
envelope: "Mail",
|
|
3008
|
-
// Status
|
|
3009
|
-
"alert-circle": "AlertCircle",
|
|
3010
|
-
"alert-triangle": "AlertTriangle",
|
|
3011
|
-
"check-circle": "CircleCheck",
|
|
3012
|
-
"x-circle": "CircleX",
|
|
3013
|
-
info: "InfoCircle",
|
|
3014
|
-
"help-circle": "HelpCircle",
|
|
3015
|
-
"life-buoy": "Lifebuoy",
|
|
3016
|
-
warning: "AlertTriangle",
|
|
3017
|
-
error: "AlertOctagon",
|
|
3018
|
-
// Media
|
|
3019
|
-
image: "Photo",
|
|
3020
|
-
video: "Video",
|
|
3021
|
-
camera: "Camera",
|
|
3022
|
-
music: "Music",
|
|
3023
|
-
play: "PlayerPlay",
|
|
3024
|
-
pause: "PlayerPause",
|
|
3025
|
-
stop: "PlayerStop",
|
|
3026
|
-
"skip-forward": "PlayerSkipForward",
|
|
3027
|
-
"skip-back": "PlayerSkipBack",
|
|
3028
|
-
volume: "Volume",
|
|
3029
|
-
"volume-2": "Volume",
|
|
3030
|
-
"volume-x": "VolumeOff",
|
|
3031
|
-
mic: "Microphone",
|
|
3032
|
-
"mic-off": "MicrophoneOff",
|
|
3033
|
-
// People
|
|
3034
|
-
user: "User",
|
|
3035
|
-
users: "Users",
|
|
3036
|
-
"user-plus": "UserPlus",
|
|
3037
|
-
"user-check": "UserCheck",
|
|
3038
|
-
// Time
|
|
3039
|
-
calendar: "Calendar",
|
|
3040
|
-
clock: "Clock",
|
|
3041
|
-
timer: "Hourglass",
|
|
3042
|
-
// Location
|
|
3043
|
-
map: "Map",
|
|
3044
|
-
"map-pin": "MapPin",
|
|
3045
|
-
navigation: "Navigation",
|
|
3046
|
-
compass: "Compass",
|
|
3047
|
-
globe: "World",
|
|
3048
|
-
target: "Target",
|
|
3049
|
-
// Project / layout
|
|
3050
|
-
kanban: "LayoutKanban",
|
|
3051
|
-
list: "List",
|
|
3052
|
-
table: "Table",
|
|
3053
|
-
grid: "LayoutGrid",
|
|
3054
|
-
layout: "Layout",
|
|
3055
|
-
columns: "LayoutColumns",
|
|
3056
|
-
rows: "LayoutRows",
|
|
3057
|
-
// Misc
|
|
3058
|
-
bell: "Bell",
|
|
3059
|
-
bookmark: "Bookmark",
|
|
3060
|
-
briefcase: "Briefcase",
|
|
3061
|
-
flag: "Flag",
|
|
3062
|
-
tag: "Tag",
|
|
3063
|
-
tags: "Tags",
|
|
3064
|
-
star: "Star",
|
|
3065
|
-
heart: "Heart",
|
|
3066
|
-
home: "Home",
|
|
3067
|
-
settings: "Settings",
|
|
3068
|
-
eye: "Eye",
|
|
3069
|
-
"eye-off": "EyeOff",
|
|
3070
|
-
lock: "Lock",
|
|
3071
|
-
unlock: "LockOpen",
|
|
3072
|
-
key: "Key",
|
|
3073
|
-
shield: "Shield",
|
|
3074
|
-
search: "Search",
|
|
3075
|
-
filter: "Filter",
|
|
3076
|
-
"sort-asc": "SortAscending",
|
|
3077
|
-
"sort-desc": "SortDescending",
|
|
3078
|
-
zap: "Bolt",
|
|
3079
|
-
sparkles: "Sparkles",
|
|
3080
|
-
// Code / data
|
|
3081
|
-
code: "Code",
|
|
3082
|
-
terminal: "Terminal",
|
|
3083
|
-
database: "Database",
|
|
3084
|
-
server: "Server",
|
|
3085
|
-
cloud: "Cloud",
|
|
3086
|
-
wifi: "Wifi",
|
|
3087
|
-
package: "Package",
|
|
3088
|
-
box: "Box",
|
|
3089
|
-
// Theme
|
|
3090
|
-
sun: "Sun",
|
|
3091
|
-
moon: "Moon",
|
|
3092
|
-
// Phone
|
|
3093
|
-
phone: "Phone",
|
|
3094
|
-
printer: "Printer",
|
|
3095
|
-
// Hierarchy
|
|
3096
|
-
tree: "Hierarchy",
|
|
3097
|
-
network: "Network"
|
|
3098
|
-
};
|
|
3099
|
-
function resolveTabler(name, family) {
|
|
3100
|
-
const suffix = tablerAliases[name] ?? kebabToPascal(name);
|
|
3101
|
-
const target = `Icon${suffix}`;
|
|
3102
|
-
return lazyFamilyIcon(
|
|
3103
|
-
"tabler",
|
|
3104
|
-
() => import('@tabler/icons-react'),
|
|
3105
|
-
(lib) => {
|
|
3106
|
-
const TablerComp = lib[target];
|
|
3107
|
-
if (!TablerComp) return null;
|
|
3108
|
-
const Component2 = TablerComp;
|
|
3109
|
-
const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3110
|
-
Component2,
|
|
3111
|
-
{
|
|
3112
|
-
stroke: props.strokeWidth ?? 1.5,
|
|
3113
|
-
className: props.className,
|
|
3114
|
-
style: props.style,
|
|
3115
|
-
size: props.size ?? 24
|
|
3116
|
-
}
|
|
3117
|
-
);
|
|
3118
|
-
Adapter.displayName = `Tabler.${target}`;
|
|
3119
|
-
return Adapter;
|
|
3120
|
-
},
|
|
3121
|
-
name,
|
|
3122
|
-
family
|
|
3123
|
-
);
|
|
2120
|
+
function worldToGrid(worldX, worldZ, config = DEFAULT_CONFIG) {
|
|
2121
|
+
const opts = { ...DEFAULT_CONFIG, ...config };
|
|
2122
|
+
return {
|
|
2123
|
+
x: Math.round((worldX - opts.offsetX) / opts.cellSize),
|
|
2124
|
+
z: Math.round((worldZ - opts.offsetZ) / opts.cellSize)
|
|
2125
|
+
};
|
|
3124
2126
|
}
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
x: "Times",
|
|
3134
|
-
loader: "Spinner",
|
|
3135
|
-
refresh: "Sync",
|
|
3136
|
-
"sort-asc": "SortAmountUp",
|
|
3137
|
-
"sort-desc": "SortAmountDown",
|
|
3138
|
-
"chevron-down": "ChevronDown",
|
|
3139
|
-
"chevron-up": "ChevronUp",
|
|
3140
|
-
"chevron-left": "ChevronLeft",
|
|
3141
|
-
"chevron-right": "ChevronRight",
|
|
3142
|
-
"help-circle": "QuestionCircle",
|
|
3143
|
-
"alert-triangle": "ExclamationTriangle",
|
|
3144
|
-
"alert-circle": "ExclamationCircle",
|
|
3145
|
-
"check-circle": "CheckCircle",
|
|
3146
|
-
"x-circle": "TimesCircle",
|
|
3147
|
-
edit: "Edit",
|
|
3148
|
-
pencil: "PencilAlt",
|
|
3149
|
-
trash: "Trash",
|
|
3150
|
-
send: "PaperPlane",
|
|
3151
|
-
share: "ShareAlt",
|
|
3152
|
-
external: "ExternalLinkAlt",
|
|
3153
|
-
plus: "Plus",
|
|
3154
|
-
minus: "Minus",
|
|
3155
|
-
check: "Check",
|
|
3156
|
-
star: "Star",
|
|
3157
|
-
heart: "Heart",
|
|
3158
|
-
home: "Home",
|
|
3159
|
-
user: "User",
|
|
3160
|
-
users: "Users",
|
|
3161
|
-
"user-plus": "UserPlus",
|
|
3162
|
-
"user-check": "UserCheck",
|
|
3163
|
-
settings: "Cog",
|
|
3164
|
-
menu: "Bars",
|
|
3165
|
-
"arrow-up": "ArrowUp",
|
|
3166
|
-
"arrow-down": "ArrowDown",
|
|
3167
|
-
"arrow-left": "ArrowLeft",
|
|
3168
|
-
"arrow-right": "ArrowRight",
|
|
3169
|
-
copy: "Copy",
|
|
3170
|
-
download: "Download",
|
|
3171
|
-
upload: "Upload",
|
|
3172
|
-
filter: "Filter",
|
|
3173
|
-
calendar: "Calendar",
|
|
3174
|
-
clock: "Clock",
|
|
3175
|
-
bell: "Bell",
|
|
3176
|
-
mail: "Envelope",
|
|
3177
|
-
envelope: "Envelope",
|
|
3178
|
-
lock: "Lock",
|
|
3179
|
-
unlock: "LockOpen",
|
|
3180
|
-
eye: "Eye",
|
|
3181
|
-
"eye-off": "EyeSlash",
|
|
3182
|
-
more: "EllipsisH",
|
|
3183
|
-
"more-vertical": "EllipsisV",
|
|
3184
|
-
info: "InfoCircle",
|
|
3185
|
-
warning: "ExclamationTriangle",
|
|
3186
|
-
error: "ExclamationCircle",
|
|
3187
|
-
// Time
|
|
3188
|
-
timer: "Hourglass",
|
|
3189
|
-
// Files (FA renamed FileText → FileAlt)
|
|
3190
|
-
file: "File",
|
|
3191
|
-
"file-text": "FileAlt",
|
|
3192
|
-
"file-plus": "FileMedical",
|
|
3193
|
-
"file-minus": "FileExcel",
|
|
3194
|
-
"file-check": "FileSignature",
|
|
3195
|
-
document: "FileAlt",
|
|
3196
|
-
// Charts (lucide BarChart2 / BarChart3 → FA ChartBar)
|
|
3197
|
-
"bar-chart": "ChartBar",
|
|
3198
|
-
"bar-chart-2": "ChartBar",
|
|
3199
|
-
"bar-chart-3": "ChartBar",
|
|
3200
|
-
"line-chart": "ChartLine",
|
|
3201
|
-
"pie-chart": "ChartPie",
|
|
3202
|
-
activity: "ChartLine",
|
|
3203
|
-
"trending-up": "ChartLine",
|
|
3204
|
-
"trending-down": "ChartLine",
|
|
3205
|
-
// Messages (lucide MessageCircle/MessageSquare → FA CommentDots/CommentAlt)
|
|
3206
|
-
message: "Comment",
|
|
3207
|
-
"message-circle": "CommentDots",
|
|
3208
|
-
"message-square": "CommentAlt",
|
|
3209
|
-
"messages-square": "Comments",
|
|
3210
|
-
comment: "Comment",
|
|
3211
|
-
comments: "Comments",
|
|
3212
|
-
inbox: "Inbox",
|
|
3213
|
-
// Support / help
|
|
3214
|
-
"life-buoy": "LifeRing",
|
|
3215
|
-
lifebuoy: "LifeRing",
|
|
3216
|
-
// Project / kanban (FA has no kanban; closest semantic is Tasks/Columns)
|
|
3217
|
-
kanban: "Tasks",
|
|
3218
|
-
columns: "Columns",
|
|
3219
|
-
rows: "Bars",
|
|
3220
|
-
layout: "ThLarge",
|
|
3221
|
-
grid: "Th",
|
|
3222
|
-
list: "List",
|
|
3223
|
-
table: "Table",
|
|
3224
|
-
// Storage / folders
|
|
3225
|
-
folder: "Folder",
|
|
3226
|
-
"folder-open": "FolderOpen",
|
|
3227
|
-
archive: "Archive",
|
|
3228
|
-
bookmark: "Bookmark",
|
|
3229
|
-
briefcase: "Briefcase",
|
|
3230
|
-
package: "Box",
|
|
3231
|
-
box: "Box",
|
|
3232
|
-
// Map / location
|
|
3233
|
-
map: "Map",
|
|
3234
|
-
"map-pin": "MapMarkerAlt",
|
|
3235
|
-
navigation: "LocationArrow",
|
|
3236
|
-
compass: "Compass",
|
|
3237
|
-
globe: "Globe",
|
|
3238
|
-
target: "Bullseye",
|
|
3239
|
-
// Media
|
|
3240
|
-
image: "Image",
|
|
3241
|
-
video: "Video",
|
|
3242
|
-
film: "Film",
|
|
3243
|
-
camera: "Camera",
|
|
3244
|
-
music: "Music",
|
|
3245
|
-
play: "Play",
|
|
3246
|
-
pause: "Pause",
|
|
3247
|
-
stop: "Stop",
|
|
3248
|
-
"skip-forward": "Forward",
|
|
3249
|
-
"skip-back": "Backward",
|
|
3250
|
-
volume: "VolumeUp",
|
|
3251
|
-
"volume-2": "VolumeUp",
|
|
3252
|
-
"volume-x": "VolumeMute",
|
|
3253
|
-
mic: "Microphone",
|
|
3254
|
-
"mic-off": "MicrophoneSlash",
|
|
3255
|
-
phone: "Phone",
|
|
3256
|
-
// Code / data
|
|
3257
|
-
code: "Code",
|
|
3258
|
-
terminal: "Terminal",
|
|
3259
|
-
database: "Database",
|
|
3260
|
-
server: "Server",
|
|
3261
|
-
cloud: "Cloud",
|
|
3262
|
-
wifi: "Wifi",
|
|
3263
|
-
// Security
|
|
3264
|
-
shield: "ShieldAlt",
|
|
3265
|
-
key: "Key",
|
|
3266
|
-
// Misc actions
|
|
3267
|
-
printer: "Print",
|
|
3268
|
-
save: "Save",
|
|
3269
|
-
link: "Link",
|
|
3270
|
-
unlink: "Unlink",
|
|
3271
|
-
paperclip: "Paperclip",
|
|
3272
|
-
flag: "Flag",
|
|
3273
|
-
tag: "Tag",
|
|
3274
|
-
tags: "Tags",
|
|
3275
|
-
zap: "Bolt",
|
|
3276
|
-
sparkles: "Magic",
|
|
3277
|
-
// Theme
|
|
3278
|
-
sun: "Sun",
|
|
3279
|
-
moon: "Moon",
|
|
3280
|
-
// Hierarchy (FA has no Tree icon for hierarchies; Sitemap is the org-chart icon)
|
|
3281
|
-
tree: "Sitemap",
|
|
3282
|
-
network: "NetworkWired"
|
|
3283
|
-
};
|
|
3284
|
-
function resolveFa(name, family) {
|
|
3285
|
-
const suffix = faAliases[name] ?? kebabToPascal(name);
|
|
3286
|
-
const target = `Fa${suffix}`;
|
|
3287
|
-
return lazyFamilyIcon(
|
|
3288
|
-
"fa",
|
|
3289
|
-
() => import('react-icons/fa'),
|
|
3290
|
-
(lib) => {
|
|
3291
|
-
const FaComp = lib[target];
|
|
3292
|
-
if (!FaComp) return null;
|
|
3293
|
-
const Component2 = FaComp;
|
|
3294
|
-
const Adapter = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3295
|
-
Component2,
|
|
3296
|
-
{
|
|
3297
|
-
className: props.className,
|
|
3298
|
-
style: props.style,
|
|
3299
|
-
size: props.size ?? "1em"
|
|
3300
|
-
}
|
|
3301
|
-
);
|
|
3302
|
-
Adapter.displayName = `Fa.${target}`;
|
|
3303
|
-
return Adapter;
|
|
3304
|
-
},
|
|
3305
|
-
name,
|
|
3306
|
-
family
|
|
3307
|
-
);
|
|
2127
|
+
function raycastToPlane(camera, mouseX, mouseY, planeY = 0) {
|
|
2128
|
+
const raycaster = new THREE6__namespace.Raycaster();
|
|
2129
|
+
const mouse = new THREE6__namespace.Vector2(mouseX, mouseY);
|
|
2130
|
+
raycaster.setFromCamera(mouse, camera);
|
|
2131
|
+
const plane = new THREE6__namespace.Plane(new THREE6__namespace.Vector3(0, 1, 0), -planeY);
|
|
2132
|
+
const target = new THREE6__namespace.Vector3();
|
|
2133
|
+
const intersection = raycaster.ray.intersectPlane(plane, target);
|
|
2134
|
+
return intersection ? target : null;
|
|
3308
2135
|
}
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
const
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
console.warn(
|
|
3316
|
-
`[iconFamily] No '${name}' mapping in family '${family}'; falling back to lucide. Add an alias in lib/iconFamily.ts.`
|
|
3317
|
-
);
|
|
3318
|
-
}
|
|
2136
|
+
function raycastToObjects(camera, mouseX, mouseY, objects) {
|
|
2137
|
+
const raycaster = new THREE6__namespace.Raycaster();
|
|
2138
|
+
const mouse = new THREE6__namespace.Vector2(mouseX, mouseY);
|
|
2139
|
+
raycaster.setFromCamera(mouse, camera);
|
|
2140
|
+
const intersects = raycaster.intersectObjects(objects, true);
|
|
2141
|
+
return intersects.length > 0 ? intersects[0] : null;
|
|
3319
2142
|
}
|
|
3320
|
-
function
|
|
3321
|
-
const
|
|
3322
|
-
const
|
|
3323
|
-
|
|
3324
|
-
const style = isFallback ? { ...props.style ?? {}, strokeWidth: stroke ?? 2 } : props.style;
|
|
3325
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3326
|
-
LucideComp,
|
|
3327
|
-
{
|
|
3328
|
-
className: props.className,
|
|
3329
|
-
strokeWidth: stroke,
|
|
3330
|
-
style,
|
|
3331
|
-
size: props.size
|
|
3332
|
-
}
|
|
3333
|
-
);
|
|
3334
|
-
};
|
|
3335
|
-
Adapter.displayName = `Lucide.${name}${isFallback ? ".fallback" : ""}`;
|
|
3336
|
-
return Adapter;
|
|
2143
|
+
function gridDistance(a, b) {
|
|
2144
|
+
const dx = b.x - a.x;
|
|
2145
|
+
const dz = b.z - a.z;
|
|
2146
|
+
return Math.sqrt(dx * dx + dz * dz);
|
|
3337
2147
|
}
|
|
3338
|
-
function
|
|
3339
|
-
|
|
3340
|
-
case "lucide":
|
|
3341
|
-
return makeLucideAdapter(name, false);
|
|
3342
|
-
// Non-lucide families resolve to a lazy, Suspense-wrapped component that
|
|
3343
|
-
// dynamic-imports the library on first render and falls back to lucide
|
|
3344
|
-
// internally when the family lacks the icon (see lazyFamilyIcon).
|
|
3345
|
-
case "phosphor-outline":
|
|
3346
|
-
return resolvePhosphor(name, "regular", family);
|
|
3347
|
-
case "phosphor-fill":
|
|
3348
|
-
return resolvePhosphor(name, "fill", family);
|
|
3349
|
-
case "phosphor-duotone":
|
|
3350
|
-
return resolvePhosphor(name, "duotone", family);
|
|
3351
|
-
case "tabler":
|
|
3352
|
-
return resolveTabler(name, family);
|
|
3353
|
-
case "fa-solid":
|
|
3354
|
-
return resolveFa(name, family);
|
|
3355
|
-
}
|
|
2148
|
+
function gridManhattanDistance(a, b) {
|
|
2149
|
+
return Math.abs(b.x - a.x) + Math.abs(b.z - a.z);
|
|
3356
2150
|
}
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
xl: "w-8 h-8"
|
|
3371
|
-
};
|
|
3372
|
-
var animationClasses = {
|
|
3373
|
-
none: "",
|
|
3374
|
-
spin: "animate-spin",
|
|
3375
|
-
pulse: "animate-pulse"
|
|
3376
|
-
};
|
|
3377
|
-
var Icon = ({
|
|
3378
|
-
icon,
|
|
3379
|
-
name,
|
|
3380
|
-
size = "md",
|
|
3381
|
-
color,
|
|
3382
|
-
animation = "none",
|
|
3383
|
-
className,
|
|
3384
|
-
strokeWidth,
|
|
3385
|
-
style
|
|
3386
|
-
}) => {
|
|
3387
|
-
const directIcon = typeof icon === "string" ? void 0 : icon;
|
|
3388
|
-
const effectiveName = typeof icon === "string" ? icon : name;
|
|
3389
|
-
const family = useIconFamily();
|
|
3390
|
-
const RenderedComponent = React8__namespace.default.useMemo(() => {
|
|
3391
|
-
if (directIcon) return null;
|
|
3392
|
-
return effectiveName ? resolveIconForFamily(effectiveName, family) : null;
|
|
3393
|
-
}, [directIcon, effectiveName, family]);
|
|
3394
|
-
const effectiveStrokeWidth = strokeWidth ?? void 0;
|
|
3395
|
-
const inlineStyle = {
|
|
3396
|
-
...effectiveStrokeWidth === void 0 ? { strokeWidth: "var(--icon-stroke-width, 2)" } : {},
|
|
3397
|
-
...style
|
|
3398
|
-
};
|
|
3399
|
-
const resolvedColor = color ? color in colorTokenClasses ? colorTokenClasses[color] : color : "text-current";
|
|
3400
|
-
const composedClassName = cn(
|
|
3401
|
-
sizeClasses[size],
|
|
3402
|
-
animationClasses[animation],
|
|
3403
|
-
resolvedColor,
|
|
3404
|
-
className
|
|
3405
|
-
);
|
|
3406
|
-
if (directIcon) {
|
|
3407
|
-
const Direct = directIcon;
|
|
3408
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3409
|
-
Direct,
|
|
3410
|
-
{
|
|
3411
|
-
className: composedClassName,
|
|
3412
|
-
strokeWidth: effectiveStrokeWidth,
|
|
3413
|
-
style: inlineStyle
|
|
3414
|
-
}
|
|
3415
|
-
);
|
|
3416
|
-
}
|
|
3417
|
-
if (RenderedComponent) {
|
|
3418
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3419
|
-
RenderedComponent,
|
|
3420
|
-
{
|
|
3421
|
-
className: composedClassName,
|
|
3422
|
-
strokeWidth: effectiveStrokeWidth,
|
|
3423
|
-
style: inlineStyle
|
|
3424
|
-
}
|
|
2151
|
+
function getNeighbors(x, z, includeDiagonal = false) {
|
|
2152
|
+
const neighbors = [
|
|
2153
|
+
{ x: x + 1, z },
|
|
2154
|
+
{ x: x - 1, z },
|
|
2155
|
+
{ x, z: z + 1 },
|
|
2156
|
+
{ x, z: z - 1 }
|
|
2157
|
+
];
|
|
2158
|
+
if (includeDiagonal) {
|
|
2159
|
+
neighbors.push(
|
|
2160
|
+
{ x: x + 1, z: z + 1 },
|
|
2161
|
+
{ x: x - 1, z: z - 1 },
|
|
2162
|
+
{ x: x + 1, z: z - 1 },
|
|
2163
|
+
{ x: x - 1, z: z + 1 }
|
|
3425
2164
|
);
|
|
3426
2165
|
}
|
|
3427
|
-
|
|
3428
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3429
|
-
Fallback,
|
|
3430
|
-
{
|
|
3431
|
-
className: composedClassName,
|
|
3432
|
-
strokeWidth: effectiveStrokeWidth,
|
|
3433
|
-
style: inlineStyle
|
|
3434
|
-
}
|
|
3435
|
-
);
|
|
3436
|
-
};
|
|
3437
|
-
Icon.displayName = "Icon";
|
|
3438
|
-
|
|
3439
|
-
// lib/atlasSlice.ts
|
|
3440
|
-
var atlasCache = /* @__PURE__ */ new Map();
|
|
3441
|
-
function isTilesheet(a) {
|
|
3442
|
-
return typeof a.tileWidth === "number";
|
|
2166
|
+
return neighbors;
|
|
3443
2167
|
}
|
|
3444
|
-
function
|
|
3445
|
-
|
|
3446
|
-
atlasCache.set(url, void 0);
|
|
3447
|
-
fetch(url).then((r) => r.json()).then((json) => {
|
|
3448
|
-
atlasCache.set(url, json);
|
|
3449
|
-
onReady();
|
|
3450
|
-
}).catch(() => {
|
|
3451
|
-
atlasCache.set(url, null);
|
|
3452
|
-
});
|
|
3453
|
-
return void 0;
|
|
2168
|
+
function isInBounds(x, z, bounds) {
|
|
2169
|
+
return x >= bounds.minX && x <= bounds.maxX && z >= bounds.minZ && z <= bounds.maxZ;
|
|
3454
2170
|
}
|
|
3455
|
-
function
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
const
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
2171
|
+
function getCellsInRadius(centerX, centerZ, radius) {
|
|
2172
|
+
const cells = [];
|
|
2173
|
+
const radiusSquared = radius * radius;
|
|
2174
|
+
const minX = Math.floor(centerX - radius);
|
|
2175
|
+
const maxX = Math.ceil(centerX + radius);
|
|
2176
|
+
const minZ = Math.floor(centerZ - radius);
|
|
2177
|
+
const maxZ = Math.ceil(centerZ + radius);
|
|
2178
|
+
for (let x = minX; x <= maxX; x++) {
|
|
2179
|
+
for (let z = minZ; z <= maxZ; z++) {
|
|
2180
|
+
const dx = x - centerX;
|
|
2181
|
+
const dz = z - centerZ;
|
|
2182
|
+
if (dx * dx + dz * dz <= radiusSquared) {
|
|
2183
|
+
cells.push({ x, z });
|
|
2184
|
+
}
|
|
3468
2185
|
}
|
|
3469
|
-
if (!Number.isFinite(col) || !Number.isFinite(row) || col < 0 || row < 0 || col >= atlas.columns || row >= atlas.rows) return null;
|
|
3470
|
-
const margin = atlas.margin ?? 0;
|
|
3471
|
-
const spacing = atlas.spacing ?? 0;
|
|
3472
|
-
return {
|
|
3473
|
-
sx: margin + col * (atlas.tileWidth + spacing),
|
|
3474
|
-
sy: margin + row * (atlas.tileHeight + spacing),
|
|
3475
|
-
sw: atlas.tileWidth,
|
|
3476
|
-
sh: atlas.tileHeight
|
|
3477
|
-
};
|
|
3478
2186
|
}
|
|
3479
|
-
|
|
3480
|
-
if (!st) return null;
|
|
3481
|
-
return { sx: st.x, sy: st.y, sw: st.width, sh: st.height };
|
|
3482
|
-
}
|
|
3483
|
-
function isAtlasAsset(asset) {
|
|
3484
|
-
return !!asset && typeof asset.atlas === "string" && typeof asset.sprite === "string";
|
|
2187
|
+
return cells;
|
|
3485
2188
|
}
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
imageWaiters.get(url)?.forEach((fn) => fn());
|
|
3499
|
-
imageWaiters.delete(url);
|
|
3500
|
-
};
|
|
3501
|
-
img.src = url;
|
|
3502
|
-
}
|
|
3503
|
-
return null;
|
|
2189
|
+
function createGridHighlight(color = 16776960, opacity = 0.3) {
|
|
2190
|
+
const geometry = new THREE6__namespace.PlaneGeometry(0.95, 0.95);
|
|
2191
|
+
const material = new THREE6__namespace.MeshBasicMaterial({
|
|
2192
|
+
color,
|
|
2193
|
+
transparent: true,
|
|
2194
|
+
opacity,
|
|
2195
|
+
side: THREE6__namespace.DoubleSide
|
|
2196
|
+
});
|
|
2197
|
+
const mesh = new THREE6__namespace.Mesh(geometry, material);
|
|
2198
|
+
mesh.rotation.x = -Math.PI / 2;
|
|
2199
|
+
mesh.position.y = 0.01;
|
|
2200
|
+
return mesh;
|
|
3504
2201
|
}
|
|
3505
|
-
function
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
fill = false,
|
|
3511
|
-
fit = "contain",
|
|
3512
|
-
alt,
|
|
3513
|
-
className,
|
|
3514
|
-
style,
|
|
3515
|
-
"aria-hidden": ariaHidden
|
|
3516
|
-
}) {
|
|
3517
|
-
const [, bump] = React8__namespace.useReducer((x) => x + 1, 0);
|
|
3518
|
-
const canvasRef = React8__namespace.useRef(null);
|
|
3519
|
-
const sliced = isAtlasAsset(asset);
|
|
3520
|
-
const atlas = sliced ? getAtlas(asset.atlas, bump) : void 0;
|
|
3521
|
-
const img = sliced && asset?.url ? getSheetImage(asset.url, bump) : null;
|
|
3522
|
-
const rect = sliced && atlas ? subRectFor(atlas, asset.sprite) : null;
|
|
3523
|
-
React8__namespace.useEffect(() => {
|
|
3524
|
-
const canvas = canvasRef.current;
|
|
3525
|
-
if (!canvas || !img || !rect) return;
|
|
3526
|
-
canvas.width = rect.sw;
|
|
3527
|
-
canvas.height = rect.sh;
|
|
3528
|
-
const ctx = canvas.getContext("2d");
|
|
3529
|
-
if (!ctx) return;
|
|
3530
|
-
ctx.imageSmoothingEnabled = false;
|
|
3531
|
-
ctx.clearRect(0, 0, rect.sw, rect.sh);
|
|
3532
|
-
ctx.drawImage(img, rect.sx, rect.sy, rect.sw, rect.sh, 0, 0, rect.sw, rect.sh);
|
|
3533
|
-
}, [img, rect?.sx, rect?.sy, rect?.sw, rect?.sh]);
|
|
3534
|
-
const w = fill ? "100%" : width ?? size;
|
|
3535
|
-
const h = fill ? "100%" : height ?? size;
|
|
3536
|
-
const boxStyle = {
|
|
3537
|
-
...fill ? { position: "absolute", inset: 0 } : {},
|
|
3538
|
-
width: w,
|
|
3539
|
-
height: h,
|
|
3540
|
-
objectFit: fit,
|
|
3541
|
-
imageRendering: "pixelated",
|
|
3542
|
-
...style
|
|
2202
|
+
function normalizeMouseCoordinates(clientX, clientY, element) {
|
|
2203
|
+
const rect = element.getBoundingClientRect();
|
|
2204
|
+
return {
|
|
2205
|
+
x: (clientX - rect.left) / rect.width * 2 - 1,
|
|
2206
|
+
y: -((clientY - rect.top) / rect.height) * 2 + 1
|
|
3543
2207
|
};
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
)
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
"
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
}
|
|
2208
|
+
}
|
|
2209
|
+
function useTapReveal(options = {}) {
|
|
2210
|
+
const { onReveal, onDismiss, refs, enabled = true } = options;
|
|
2211
|
+
const [revealed, setRevealed] = React7.useState(false);
|
|
2212
|
+
const onRevealRef = React7.useRef(onReveal);
|
|
2213
|
+
const onDismissRef = React7.useRef(onDismiss);
|
|
2214
|
+
onRevealRef.current = onReveal;
|
|
2215
|
+
onDismissRef.current = onDismiss;
|
|
2216
|
+
const reveal = React7.useCallback(() => {
|
|
2217
|
+
setRevealed((wasRevealed) => {
|
|
2218
|
+
if (!wasRevealed) onRevealRef.current?.();
|
|
2219
|
+
return true;
|
|
2220
|
+
});
|
|
2221
|
+
}, []);
|
|
2222
|
+
const dismiss = React7.useCallback(() => {
|
|
2223
|
+
setRevealed((wasRevealed) => {
|
|
2224
|
+
if (wasRevealed) onDismissRef.current?.();
|
|
2225
|
+
return false;
|
|
2226
|
+
});
|
|
2227
|
+
}, []);
|
|
2228
|
+
const onPointerDown = React7.useCallback(
|
|
2229
|
+
(e) => {
|
|
2230
|
+
if (!enabled || e.pointerType === "mouse") return;
|
|
2231
|
+
if (revealed) dismiss();
|
|
2232
|
+
else reveal();
|
|
2233
|
+
},
|
|
2234
|
+
[enabled, revealed, reveal, dismiss]
|
|
3572
2235
|
);
|
|
2236
|
+
React7.useEffect(() => {
|
|
2237
|
+
if (!revealed || typeof document === "undefined") return;
|
|
2238
|
+
const onDocDown = (ev) => {
|
|
2239
|
+
const target = ev.target;
|
|
2240
|
+
if (!(target instanceof Node)) return;
|
|
2241
|
+
const inside = (refs ?? []).some((r) => r.current?.contains(target));
|
|
2242
|
+
if (!inside) dismiss();
|
|
2243
|
+
};
|
|
2244
|
+
const id = setTimeout(() => document.addEventListener("pointerdown", onDocDown), 0);
|
|
2245
|
+
return () => {
|
|
2246
|
+
clearTimeout(id);
|
|
2247
|
+
document.removeEventListener("pointerdown", onDocDown);
|
|
2248
|
+
};
|
|
2249
|
+
}, [revealed, refs, dismiss]);
|
|
2250
|
+
return { revealed, triggerProps: { onPointerDown }, reveal, dismiss };
|
|
3573
2251
|
}
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
"bg-transparent text-accent",
|
|
3585
|
-
"border border-accent",
|
|
3586
|
-
"hover:bg-accent hover:text-white hover:border-accent",
|
|
3587
|
-
"active:scale-[var(--active-scale)]"
|
|
3588
|
-
].join(" "),
|
|
3589
|
-
ghost: [
|
|
3590
|
-
"bg-transparent text-muted-foreground",
|
|
3591
|
-
"border border-transparent",
|
|
3592
|
-
"hover:text-primary-foreground hover:bg-primary hover:border-primary",
|
|
3593
|
-
"active:scale-[var(--active-scale)]"
|
|
3594
|
-
].join(" "),
|
|
3595
|
-
danger: [
|
|
3596
|
-
"bg-surface text-error",
|
|
3597
|
-
"border-[length:var(--border-width)] border-error",
|
|
3598
|
-
"shadow-sm",
|
|
3599
|
-
"hover:bg-error hover:text-error-foreground hover:shadow-lg",
|
|
3600
|
-
"active:scale-[var(--active-scale)] active:shadow-sm"
|
|
3601
|
-
].join(" "),
|
|
3602
|
-
success: [
|
|
3603
|
-
"bg-surface text-success",
|
|
3604
|
-
"border-[length:var(--border-width)] border-success",
|
|
3605
|
-
"shadow-sm",
|
|
3606
|
-
"hover:bg-success hover:text-success-foreground hover:shadow-lg",
|
|
3607
|
-
"active:scale-[var(--active-scale)] active:shadow-sm"
|
|
3608
|
-
].join(" "),
|
|
3609
|
-
warning: [
|
|
3610
|
-
"bg-surface text-warning",
|
|
3611
|
-
"border-[length:var(--border-width)] border-warning",
|
|
3612
|
-
"shadow-sm",
|
|
3613
|
-
"hover:bg-warning hover:text-warning-foreground hover:shadow-lg",
|
|
3614
|
-
"active:scale-[var(--active-scale)] active:shadow-sm"
|
|
3615
|
-
].join(" "),
|
|
3616
|
-
// "default" is an alias for secondary
|
|
3617
|
-
default: [
|
|
3618
|
-
"bg-secondary text-secondary-foreground",
|
|
3619
|
-
"border-[length:var(--border-width-thin)] border-border",
|
|
3620
|
-
"hover:bg-secondary-hover",
|
|
3621
|
-
"active:scale-[var(--active-scale)]"
|
|
3622
|
-
].join(" ")
|
|
2252
|
+
|
|
2253
|
+
// components/core/atoms/Box.tsx
|
|
2254
|
+
var paddingStyles = {
|
|
2255
|
+
none: "p-0",
|
|
2256
|
+
xs: "p-1",
|
|
2257
|
+
sm: "p-2",
|
|
2258
|
+
md: "p-4",
|
|
2259
|
+
lg: "p-6",
|
|
2260
|
+
xl: "p-8",
|
|
2261
|
+
"2xl": "p-12"
|
|
3623
2262
|
};
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
2263
|
+
var paddingXStyles = {
|
|
2264
|
+
none: "px-0",
|
|
2265
|
+
xs: "px-1",
|
|
2266
|
+
sm: "px-2",
|
|
2267
|
+
md: "px-4",
|
|
2268
|
+
lg: "px-6",
|
|
2269
|
+
xl: "px-8",
|
|
2270
|
+
"2xl": "px-12"
|
|
3629
2271
|
};
|
|
3630
|
-
var
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
2272
|
+
var paddingYStyles = {
|
|
2273
|
+
none: "py-0",
|
|
2274
|
+
xs: "py-1",
|
|
2275
|
+
sm: "py-2",
|
|
2276
|
+
md: "py-4",
|
|
2277
|
+
lg: "py-6",
|
|
2278
|
+
xl: "py-8",
|
|
2279
|
+
"2xl": "py-12"
|
|
3634
2280
|
};
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
2281
|
+
var marginStyles = {
|
|
2282
|
+
none: "m-0",
|
|
2283
|
+
xs: "m-1",
|
|
2284
|
+
sm: "m-2",
|
|
2285
|
+
md: "m-4",
|
|
2286
|
+
lg: "m-6",
|
|
2287
|
+
xl: "m-8",
|
|
2288
|
+
"2xl": "m-12",
|
|
2289
|
+
auto: "m-auto"
|
|
2290
|
+
};
|
|
2291
|
+
var marginXStyles = {
|
|
2292
|
+
none: "mx-0",
|
|
2293
|
+
xs: "mx-1",
|
|
2294
|
+
sm: "mx-2",
|
|
2295
|
+
md: "mx-4",
|
|
2296
|
+
lg: "mx-6",
|
|
2297
|
+
xl: "mx-8",
|
|
2298
|
+
"2xl": "mx-12",
|
|
2299
|
+
auto: "mx-auto"
|
|
2300
|
+
};
|
|
2301
|
+
var marginYStyles = {
|
|
2302
|
+
none: "my-0",
|
|
2303
|
+
xs: "my-1",
|
|
2304
|
+
sm: "my-2",
|
|
2305
|
+
md: "my-4",
|
|
2306
|
+
lg: "my-6",
|
|
2307
|
+
xl: "my-8",
|
|
2308
|
+
"2xl": "my-12",
|
|
2309
|
+
auto: "my-auto"
|
|
2310
|
+
};
|
|
2311
|
+
var bgStyles = {
|
|
2312
|
+
transparent: "bg-transparent",
|
|
2313
|
+
primary: "bg-primary text-primary-foreground",
|
|
2314
|
+
secondary: "bg-secondary text-secondary-foreground",
|
|
2315
|
+
muted: "bg-muted text-foreground",
|
|
2316
|
+
accent: "bg-accent text-accent-foreground",
|
|
2317
|
+
surface: "bg-card",
|
|
2318
|
+
overlay: "bg-card/80 backdrop-blur-sm"
|
|
2319
|
+
};
|
|
2320
|
+
var roundedStyles = {
|
|
2321
|
+
none: "rounded-none",
|
|
2322
|
+
sm: "rounded-sm",
|
|
2323
|
+
md: "rounded-md",
|
|
2324
|
+
lg: "rounded-lg",
|
|
2325
|
+
xl: "rounded-xl",
|
|
2326
|
+
"2xl": "rounded-xl",
|
|
2327
|
+
full: "rounded-full"
|
|
2328
|
+
};
|
|
2329
|
+
var shadowStyles = {
|
|
2330
|
+
none: "shadow-none",
|
|
2331
|
+
sm: "shadow-sm",
|
|
2332
|
+
md: "shadow",
|
|
2333
|
+
lg: "shadow-lg",
|
|
2334
|
+
xl: "shadow-lg"
|
|
2335
|
+
};
|
|
2336
|
+
var displayStyles = {
|
|
2337
|
+
block: "block",
|
|
2338
|
+
inline: "inline",
|
|
2339
|
+
"inline-block": "inline-block",
|
|
2340
|
+
flex: "flex",
|
|
2341
|
+
"inline-flex": "inline-flex",
|
|
2342
|
+
grid: "grid"
|
|
2343
|
+
};
|
|
2344
|
+
var overflowStyles = {
|
|
2345
|
+
auto: "overflow-auto",
|
|
2346
|
+
hidden: "overflow-hidden",
|
|
2347
|
+
visible: "overflow-visible",
|
|
2348
|
+
scroll: "overflow-scroll"
|
|
2349
|
+
};
|
|
2350
|
+
var positionStyles = {
|
|
2351
|
+
relative: "relative",
|
|
2352
|
+
absolute: "absolute",
|
|
2353
|
+
fixed: "fixed",
|
|
2354
|
+
sticky: "sticky"
|
|
2355
|
+
};
|
|
2356
|
+
var Box = React7__default.default.forwardRef(
|
|
2357
|
+
({
|
|
2358
|
+
padding,
|
|
2359
|
+
paddingX,
|
|
2360
|
+
paddingY,
|
|
2361
|
+
margin,
|
|
2362
|
+
marginX,
|
|
2363
|
+
marginY,
|
|
2364
|
+
bg = "transparent",
|
|
2365
|
+
border = false,
|
|
2366
|
+
rounded = "none",
|
|
2367
|
+
shadow = "none",
|
|
2368
|
+
display,
|
|
2369
|
+
fullWidth = false,
|
|
2370
|
+
fullHeight = false,
|
|
2371
|
+
overflow,
|
|
2372
|
+
position,
|
|
2373
|
+
className,
|
|
2374
|
+
children,
|
|
2375
|
+
as: Component2 = "div",
|
|
2376
|
+
action,
|
|
2377
|
+
actionPayload,
|
|
2378
|
+
hoverEvent,
|
|
2379
|
+
tapReveal = true,
|
|
2380
|
+
maxWidth,
|
|
2381
|
+
onClick,
|
|
2382
|
+
onMouseEnter,
|
|
2383
|
+
onMouseLeave,
|
|
2384
|
+
onPointerDown,
|
|
2385
|
+
...rest
|
|
2386
|
+
}, ref) => {
|
|
2387
|
+
const eventBus = useEventBus();
|
|
2388
|
+
const handleClick = React7.useCallback((e) => {
|
|
2389
|
+
if (action) {
|
|
2390
|
+
e.stopPropagation();
|
|
2391
|
+
eventBus.emit(`UI:${action}`, actionPayload ?? {});
|
|
2392
|
+
}
|
|
2393
|
+
onClick?.(e);
|
|
2394
|
+
}, [action, actionPayload, eventBus, onClick]);
|
|
2395
|
+
const handleMouseEnter = React7.useCallback((e) => {
|
|
2396
|
+
if (hoverEvent) {
|
|
2397
|
+
eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
|
|
2398
|
+
}
|
|
2399
|
+
onMouseEnter?.(e);
|
|
2400
|
+
}, [hoverEvent, eventBus, onMouseEnter]);
|
|
2401
|
+
const handleMouseLeave = React7.useCallback((e) => {
|
|
2402
|
+
if (hoverEvent) {
|
|
2403
|
+
eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
|
|
3713
2404
|
}
|
|
2405
|
+
onMouseLeave?.(e);
|
|
2406
|
+
}, [hoverEvent, eventBus, onMouseLeave]);
|
|
2407
|
+
const { triggerProps } = useTapReveal({
|
|
2408
|
+
enabled: tapReveal && !!hoverEvent,
|
|
2409
|
+
onReveal: React7.useCallback(() => {
|
|
2410
|
+
if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
|
|
2411
|
+
}, [hoverEvent, eventBus]),
|
|
2412
|
+
onDismiss: React7.useCallback(() => {
|
|
2413
|
+
if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
|
|
2414
|
+
}, [hoverEvent, eventBus])
|
|
2415
|
+
});
|
|
2416
|
+
const handlePointerDown = React7.useCallback((e) => {
|
|
2417
|
+
if (hoverEvent && tapReveal) triggerProps.onPointerDown(e);
|
|
2418
|
+
onPointerDown?.(e);
|
|
2419
|
+
}, [hoverEvent, tapReveal, triggerProps, onPointerDown]);
|
|
2420
|
+
const isClickable = action || onClick;
|
|
2421
|
+
return React7__default.default.createElement(
|
|
2422
|
+
Component2,
|
|
2423
|
+
{
|
|
2424
|
+
ref,
|
|
2425
|
+
className: cn(
|
|
2426
|
+
padding && paddingStyles[padding],
|
|
2427
|
+
paddingX && paddingXStyles[paddingX],
|
|
2428
|
+
paddingY && paddingYStyles[paddingY],
|
|
2429
|
+
margin && marginStyles[margin],
|
|
2430
|
+
marginX && marginXStyles[marginX],
|
|
2431
|
+
marginY && marginYStyles[marginY],
|
|
2432
|
+
bgStyles[bg],
|
|
2433
|
+
border && "border-[length:var(--border-width)] border-border",
|
|
2434
|
+
roundedStyles[rounded],
|
|
2435
|
+
shadowStyles[shadow],
|
|
2436
|
+
display && displayStyles[display],
|
|
2437
|
+
fullWidth && "w-full",
|
|
2438
|
+
fullHeight && "h-full",
|
|
2439
|
+
overflow && overflowStyles[overflow],
|
|
2440
|
+
position && positionStyles[position],
|
|
2441
|
+
isClickable && "cursor-pointer",
|
|
2442
|
+
className
|
|
2443
|
+
),
|
|
2444
|
+
onClick: isClickable ? handleClick : void 0,
|
|
2445
|
+
onMouseEnter: hoverEvent || onMouseEnter ? handleMouseEnter : void 0,
|
|
2446
|
+
onMouseLeave: hoverEvent || onMouseLeave ? handleMouseLeave : void 0,
|
|
2447
|
+
onPointerDown: hoverEvent && tapReveal || onPointerDown ? handlePointerDown : void 0,
|
|
2448
|
+
style: maxWidth ? { maxWidth, ...rest.style } : rest.style,
|
|
2449
|
+
...rest
|
|
2450
|
+
},
|
|
2451
|
+
children
|
|
3714
2452
|
);
|
|
3715
2453
|
}
|
|
3716
2454
|
);
|
|
3717
|
-
|
|
3718
|
-
var
|
|
2455
|
+
Box.displayName = "Box";
|
|
2456
|
+
var variantStyles = {
|
|
3719
2457
|
h1: "text-4xl font-bold tracking-tight text-foreground",
|
|
3720
2458
|
h2: "text-3xl font-bold tracking-tight text-foreground",
|
|
3721
2459
|
h3: "text-2xl font-bold text-foreground",
|
|
@@ -3776,7 +2514,7 @@ var typographySizeStyles = {
|
|
|
3776
2514
|
"2xl": "text-2xl",
|
|
3777
2515
|
"3xl": "text-3xl"
|
|
3778
2516
|
};
|
|
3779
|
-
var
|
|
2517
|
+
var overflowStyles2 = {
|
|
3780
2518
|
visible: "overflow-visible",
|
|
3781
2519
|
hidden: "overflow-hidden",
|
|
3782
2520
|
wrap: "break-words overflow-hidden",
|
|
@@ -3801,18 +2539,18 @@ var Typography = ({
|
|
|
3801
2539
|
}) => {
|
|
3802
2540
|
const variant = variantProp ?? (level ? `h${level}` : "body1");
|
|
3803
2541
|
const Component2 = as || defaultElements[variant];
|
|
3804
|
-
return
|
|
2542
|
+
return React7__default.default.createElement(
|
|
3805
2543
|
Component2,
|
|
3806
2544
|
{
|
|
3807
2545
|
id,
|
|
3808
2546
|
className: cn(
|
|
3809
|
-
|
|
2547
|
+
variantStyles[variant],
|
|
3810
2548
|
colorStyles[color],
|
|
3811
2549
|
weight && weightStyles[weight],
|
|
3812
2550
|
size && typographySizeStyles[size],
|
|
3813
2551
|
align && `text-${align}`,
|
|
3814
2552
|
truncate && "truncate overflow-hidden text-ellipsis",
|
|
3815
|
-
overflow &&
|
|
2553
|
+
overflow && overflowStyles2[overflow],
|
|
3816
2554
|
className
|
|
3817
2555
|
),
|
|
3818
2556
|
style
|
|
@@ -3874,7 +2612,7 @@ var Stack = ({
|
|
|
3874
2612
|
};
|
|
3875
2613
|
const isHorizontal = direction === "horizontal";
|
|
3876
2614
|
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";
|
|
3877
|
-
return
|
|
2615
|
+
return React7__default.default.createElement(
|
|
3878
2616
|
Component2,
|
|
3879
2617
|
{
|
|
3880
2618
|
className: cn(
|
|
@@ -3899,939 +2637,6 @@ var Stack = ({
|
|
|
3899
2637
|
var VStack = (props) => /* @__PURE__ */ jsxRuntime.jsx(Stack, { direction: "vertical", ...props });
|
|
3900
2638
|
var HStack = (props) => /* @__PURE__ */ jsxRuntime.jsx(Stack, { direction: "horizontal", ...props });
|
|
3901
2639
|
|
|
3902
|
-
// components/game/shared/boardEntity.ts
|
|
3903
|
-
function boardEntity(entity) {
|
|
3904
|
-
if (!entity) return void 0;
|
|
3905
|
-
return Array.isArray(entity) ? entity[0] : entity;
|
|
3906
|
-
}
|
|
3907
|
-
function str(v) {
|
|
3908
|
-
return v == null ? "" : String(v);
|
|
3909
|
-
}
|
|
3910
|
-
function num(v, fallback = 0) {
|
|
3911
|
-
const n = Number(v);
|
|
3912
|
-
return Number.isFinite(n) ? n : fallback;
|
|
3913
|
-
}
|
|
3914
|
-
function rows(v) {
|
|
3915
|
-
return Array.isArray(v) ? v : [];
|
|
3916
|
-
}
|
|
3917
|
-
function vec2(v) {
|
|
3918
|
-
const o = v ?? {};
|
|
3919
|
-
return { x: num(o.x), y: num(o.y) };
|
|
3920
|
-
}
|
|
3921
|
-
function unitPosition(u) {
|
|
3922
|
-
return vec2(u.position);
|
|
3923
|
-
}
|
|
3924
|
-
function unitTeam(u) {
|
|
3925
|
-
return str(u.team);
|
|
3926
|
-
}
|
|
3927
|
-
function unitHealth(u) {
|
|
3928
|
-
return num(u.health);
|
|
3929
|
-
}
|
|
3930
|
-
function GameBoard3D({
|
|
3931
|
-
entity,
|
|
3932
|
-
tiles = [],
|
|
3933
|
-
features = [],
|
|
3934
|
-
cameraMode = "perspective",
|
|
3935
|
-
backgroundColor = "#2a1a1a",
|
|
3936
|
-
unitScale = 1,
|
|
3937
|
-
scale = 0.45,
|
|
3938
|
-
tileClickEvent,
|
|
3939
|
-
unitClickEvent,
|
|
3940
|
-
attackEvent,
|
|
3941
|
-
endTurnEvent,
|
|
3942
|
-
cancelEvent,
|
|
3943
|
-
playAgainEvent,
|
|
3944
|
-
gameEndEvent,
|
|
3945
|
-
className
|
|
3946
|
-
}) {
|
|
3947
|
-
const row = boardEntity(entity);
|
|
3948
|
-
const eventBus = useEventBus();
|
|
3949
|
-
const entityUnits = row ? rows(row.units) : [];
|
|
3950
|
-
const units = entityUnits.map((r) => ({
|
|
3951
|
-
id: str(r.id),
|
|
3952
|
-
x: num(r.x),
|
|
3953
|
-
y: num(r.y),
|
|
3954
|
-
z: num(r.z),
|
|
3955
|
-
faction: str(r.faction) || "neutral",
|
|
3956
|
-
team: str(r.team) || str(r.faction) || "neutral",
|
|
3957
|
-
unitType: r.unitType == null ? void 0 : str(r.unitType),
|
|
3958
|
-
name: r.name == null ? void 0 : str(r.name),
|
|
3959
|
-
health: r.health == null ? void 0 : num(r.health),
|
|
3960
|
-
maxHealth: r.maxHealth == null ? void 0 : num(r.maxHealth)
|
|
3961
|
-
}));
|
|
3962
|
-
const selectedUnitId = row ? str(row.selectedUnitId) || null : null;
|
|
3963
|
-
const phase = row ? str(row.phase) : "observation";
|
|
3964
|
-
const result = row ? str(row.result) : "none";
|
|
3965
|
-
const validMoves = row && Array.isArray(row.validMoves) ? row.validMoves : [];
|
|
3966
|
-
const attackTargets = row && Array.isArray(row.attackTargets) ? row.attackTargets : [];
|
|
3967
|
-
const turn = row ? num(row.turn) : 0;
|
|
3968
|
-
const currentTeam = row ? str(row.currentTeam) : "player";
|
|
3969
|
-
const isGameOver = result !== "none";
|
|
3970
|
-
const gameEndEmittedRef = React8.useRef(false);
|
|
3971
|
-
React8.useEffect(() => {
|
|
3972
|
-
if ((result === "victory" || result === "defeat") && gameEndEvent) {
|
|
3973
|
-
if (!gameEndEmittedRef.current) {
|
|
3974
|
-
gameEndEmittedRef.current = true;
|
|
3975
|
-
eventBus.emit(`UI:${gameEndEvent}`, { result });
|
|
3976
|
-
}
|
|
3977
|
-
} else {
|
|
3978
|
-
gameEndEmittedRef.current = false;
|
|
3979
|
-
}
|
|
3980
|
-
}, [result, gameEndEvent, eventBus]);
|
|
3981
|
-
const checkGameEnd = React8.useCallback(() => {
|
|
3982
|
-
const alivePlayer = entityUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
|
|
3983
|
-
const aliveEnemy = entityUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
|
|
3984
|
-
if (alivePlayer.length === 0 && gameEndEvent) {
|
|
3985
|
-
eventBus.emit(`UI:${gameEndEvent}`, { result: "defeat" });
|
|
3986
|
-
} else if (aliveEnemy.length === 0 && gameEndEvent) {
|
|
3987
|
-
eventBus.emit(`UI:${gameEndEvent}`, { result: "victory" });
|
|
3988
|
-
}
|
|
3989
|
-
}, [entityUnits, gameEndEvent, eventBus]);
|
|
3990
|
-
const handleUnitClickCallback = React8.useCallback((isoUnit) => {
|
|
3991
|
-
if (phase !== "action" || !selectedUnitId || !attackEvent) return;
|
|
3992
|
-
const attackerRow = entityUnits.find((u) => str(u.id) === selectedUnitId);
|
|
3993
|
-
const targetRow = entityUnits.find((u) => str(u.id) === isoUnit.id);
|
|
3994
|
-
if (!attackerRow || !targetRow) return;
|
|
3995
|
-
if (unitTeam(targetRow) !== "enemy" || unitHealth(targetRow) <= 0) return;
|
|
3996
|
-
const ap = unitPosition(attackerRow);
|
|
3997
|
-
const tp = unitPosition(targetRow);
|
|
3998
|
-
const dx = Math.abs(ap.x - tp.x);
|
|
3999
|
-
const dy = Math.abs(ap.y - tp.y);
|
|
4000
|
-
if (dx <= 1 && dy <= 1 && dx + dy > 0) {
|
|
4001
|
-
const damage = Math.max(1, num(attackerRow.attack) - num(targetRow.defense));
|
|
4002
|
-
eventBus.emit(`UI:${attackEvent}`, {
|
|
4003
|
-
attackerId: str(attackerRow.id),
|
|
4004
|
-
targetId: str(targetRow.id),
|
|
4005
|
-
damage
|
|
4006
|
-
});
|
|
4007
|
-
setTimeout(checkGameEnd, 100);
|
|
4008
|
-
}
|
|
4009
|
-
}, [phase, selectedUnitId, entityUnits, attackEvent, eventBus, checkGameEnd]);
|
|
4010
|
-
const handleEndTurn = React8.useCallback(() => {
|
|
4011
|
-
if (endTurnEvent) eventBus.emit(`UI:${endTurnEvent}`, {});
|
|
4012
|
-
}, [endTurnEvent, eventBus]);
|
|
4013
|
-
const handleCancel = React8.useCallback(() => {
|
|
4014
|
-
if (cancelEvent) eventBus.emit(`UI:${cancelEvent}`, {});
|
|
4015
|
-
}, [cancelEvent, eventBus]);
|
|
4016
|
-
const handlePlayAgain = React8.useCallback(() => {
|
|
4017
|
-
if (playAgainEvent) eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
4018
|
-
}, [playAgainEvent, eventBus]);
|
|
4019
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("game-board-3d block w-full min-h-[85vh] relative", className), children: [
|
|
4020
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "game-board-3d__status absolute top-3 left-3 z-10 flex gap-2 items-center", children: [
|
|
4021
|
-
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "status__phase capitalize", children: phase.replace("_", " ") }),
|
|
4022
|
-
currentTeam && !isGameOver && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", color: "muted", className: "status__team", children: [
|
|
4023
|
-
"\u2014 ",
|
|
4024
|
-
currentTeam === "player" ? "Your Turn" : "Enemy's Turn"
|
|
4025
|
-
] }),
|
|
4026
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", color: "muted", className: "status__turn", children: [
|
|
4027
|
-
"Turn ",
|
|
4028
|
-
turn
|
|
4029
|
-
] })
|
|
4030
|
-
] }),
|
|
4031
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4032
|
-
GameCanvas3D,
|
|
4033
|
-
{
|
|
4034
|
-
tiles,
|
|
4035
|
-
units,
|
|
4036
|
-
features,
|
|
4037
|
-
cameraMode,
|
|
4038
|
-
showGrid: true,
|
|
4039
|
-
showCoordinates: false,
|
|
4040
|
-
showTileInfo: false,
|
|
4041
|
-
shadows: true,
|
|
4042
|
-
backgroundColor,
|
|
4043
|
-
tileClickEvent,
|
|
4044
|
-
unitClickEvent,
|
|
4045
|
-
onUnitClick: handleUnitClickCallback,
|
|
4046
|
-
selectedUnitId,
|
|
4047
|
-
validMoves,
|
|
4048
|
-
attackTargets,
|
|
4049
|
-
unitScale,
|
|
4050
|
-
scale,
|
|
4051
|
-
className: "game-board-3d__canvas w-full min-h-[85vh]"
|
|
4052
|
-
}
|
|
4053
|
-
),
|
|
4054
|
-
!isGameOver && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { className: "fixed bottom-6 right-6 z-50", gap: "sm", children: [
|
|
4055
|
-
(phase === "selection" || phase === "movement" || phase === "action") && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4056
|
-
Button,
|
|
4057
|
-
{
|
|
4058
|
-
variant: "secondary",
|
|
4059
|
-
className: "shadow-xl",
|
|
4060
|
-
onClick: handleCancel,
|
|
4061
|
-
children: "Cancel"
|
|
4062
|
-
}
|
|
4063
|
-
),
|
|
4064
|
-
phase !== "enemy_turn" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4065
|
-
Button,
|
|
4066
|
-
{
|
|
4067
|
-
variant: "primary",
|
|
4068
|
-
className: "shadow-xl",
|
|
4069
|
-
onClick: handleEndTurn,
|
|
4070
|
-
children: "End Turn"
|
|
4071
|
-
}
|
|
4072
|
-
)
|
|
4073
|
-
] }),
|
|
4074
|
-
isGameOver && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "game-board-3d__overlay absolute inset-0 z-20 flex items-center justify-center bg-black/60", children: /* @__PURE__ */ jsxRuntime.jsxs(VStack, { align: "center", gap: "md", className: "overlay__card p-8 rounded-xl bg-gray-900/90 shadow-2xl", children: [
|
|
4075
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4076
|
-
Typography,
|
|
4077
|
-
{
|
|
4078
|
-
variant: "h2",
|
|
4079
|
-
className: cn(
|
|
4080
|
-
"overlay__result",
|
|
4081
|
-
result === "victory" ? "text-yellow-400" : "text-red-500"
|
|
4082
|
-
),
|
|
4083
|
-
children: result === "victory" ? "Victory!" : "Defeat"
|
|
4084
|
-
}
|
|
4085
|
-
),
|
|
4086
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "body", color: "muted", className: "overlay__turn-count", children: [
|
|
4087
|
-
"Completed in ",
|
|
4088
|
-
turn,
|
|
4089
|
-
" turn",
|
|
4090
|
-
turn !== 1 ? "s" : ""
|
|
4091
|
-
] }),
|
|
4092
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4093
|
-
Button,
|
|
4094
|
-
{
|
|
4095
|
-
variant: "primary",
|
|
4096
|
-
className: "px-8 py-3 font-semibold",
|
|
4097
|
-
onClick: handlePlayAgain,
|
|
4098
|
-
children: "Play Again"
|
|
4099
|
-
}
|
|
4100
|
-
)
|
|
4101
|
-
] }) })
|
|
4102
|
-
] });
|
|
4103
|
-
}
|
|
4104
|
-
GameBoard3D.displayName = "GameBoard3D";
|
|
4105
|
-
function useTapReveal(options = {}) {
|
|
4106
|
-
const { onReveal, onDismiss, refs, enabled = true } = options;
|
|
4107
|
-
const [revealed, setRevealed] = React8.useState(false);
|
|
4108
|
-
const onRevealRef = React8.useRef(onReveal);
|
|
4109
|
-
const onDismissRef = React8.useRef(onDismiss);
|
|
4110
|
-
onRevealRef.current = onReveal;
|
|
4111
|
-
onDismissRef.current = onDismiss;
|
|
4112
|
-
const reveal = React8.useCallback(() => {
|
|
4113
|
-
setRevealed((wasRevealed) => {
|
|
4114
|
-
if (!wasRevealed) onRevealRef.current?.();
|
|
4115
|
-
return true;
|
|
4116
|
-
});
|
|
4117
|
-
}, []);
|
|
4118
|
-
const dismiss = React8.useCallback(() => {
|
|
4119
|
-
setRevealed((wasRevealed) => {
|
|
4120
|
-
if (wasRevealed) onDismissRef.current?.();
|
|
4121
|
-
return false;
|
|
4122
|
-
});
|
|
4123
|
-
}, []);
|
|
4124
|
-
const onPointerDown = React8.useCallback(
|
|
4125
|
-
(e) => {
|
|
4126
|
-
if (!enabled || e.pointerType === "mouse") return;
|
|
4127
|
-
if (revealed) dismiss();
|
|
4128
|
-
else reveal();
|
|
4129
|
-
},
|
|
4130
|
-
[enabled, revealed, reveal, dismiss]
|
|
4131
|
-
);
|
|
4132
|
-
React8.useEffect(() => {
|
|
4133
|
-
if (!revealed || typeof document === "undefined") return;
|
|
4134
|
-
const onDocDown = (ev) => {
|
|
4135
|
-
const target = ev.target;
|
|
4136
|
-
if (!(target instanceof Node)) return;
|
|
4137
|
-
const inside = (refs ?? []).some((r) => r.current?.contains(target));
|
|
4138
|
-
if (!inside) dismiss();
|
|
4139
|
-
};
|
|
4140
|
-
const id = setTimeout(() => document.addEventListener("pointerdown", onDocDown), 0);
|
|
4141
|
-
return () => {
|
|
4142
|
-
clearTimeout(id);
|
|
4143
|
-
document.removeEventListener("pointerdown", onDocDown);
|
|
4144
|
-
};
|
|
4145
|
-
}, [revealed, refs, dismiss]);
|
|
4146
|
-
return { revealed, triggerProps: { onPointerDown }, reveal, dismiss };
|
|
4147
|
-
}
|
|
4148
|
-
|
|
4149
|
-
// components/core/atoms/Box.tsx
|
|
4150
|
-
var paddingStyles = {
|
|
4151
|
-
none: "p-0",
|
|
4152
|
-
xs: "p-1",
|
|
4153
|
-
sm: "p-2",
|
|
4154
|
-
md: "p-4",
|
|
4155
|
-
lg: "p-6",
|
|
4156
|
-
xl: "p-8",
|
|
4157
|
-
"2xl": "p-12"
|
|
4158
|
-
};
|
|
4159
|
-
var paddingXStyles = {
|
|
4160
|
-
none: "px-0",
|
|
4161
|
-
xs: "px-1",
|
|
4162
|
-
sm: "px-2",
|
|
4163
|
-
md: "px-4",
|
|
4164
|
-
lg: "px-6",
|
|
4165
|
-
xl: "px-8",
|
|
4166
|
-
"2xl": "px-12"
|
|
4167
|
-
};
|
|
4168
|
-
var paddingYStyles = {
|
|
4169
|
-
none: "py-0",
|
|
4170
|
-
xs: "py-1",
|
|
4171
|
-
sm: "py-2",
|
|
4172
|
-
md: "py-4",
|
|
4173
|
-
lg: "py-6",
|
|
4174
|
-
xl: "py-8",
|
|
4175
|
-
"2xl": "py-12"
|
|
4176
|
-
};
|
|
4177
|
-
var marginStyles = {
|
|
4178
|
-
none: "m-0",
|
|
4179
|
-
xs: "m-1",
|
|
4180
|
-
sm: "m-2",
|
|
4181
|
-
md: "m-4",
|
|
4182
|
-
lg: "m-6",
|
|
4183
|
-
xl: "m-8",
|
|
4184
|
-
"2xl": "m-12",
|
|
4185
|
-
auto: "m-auto"
|
|
4186
|
-
};
|
|
4187
|
-
var marginXStyles = {
|
|
4188
|
-
none: "mx-0",
|
|
4189
|
-
xs: "mx-1",
|
|
4190
|
-
sm: "mx-2",
|
|
4191
|
-
md: "mx-4",
|
|
4192
|
-
lg: "mx-6",
|
|
4193
|
-
xl: "mx-8",
|
|
4194
|
-
"2xl": "mx-12",
|
|
4195
|
-
auto: "mx-auto"
|
|
4196
|
-
};
|
|
4197
|
-
var marginYStyles = {
|
|
4198
|
-
none: "my-0",
|
|
4199
|
-
xs: "my-1",
|
|
4200
|
-
sm: "my-2",
|
|
4201
|
-
md: "my-4",
|
|
4202
|
-
lg: "my-6",
|
|
4203
|
-
xl: "my-8",
|
|
4204
|
-
"2xl": "my-12",
|
|
4205
|
-
auto: "my-auto"
|
|
4206
|
-
};
|
|
4207
|
-
var bgStyles = {
|
|
4208
|
-
transparent: "bg-transparent",
|
|
4209
|
-
primary: "bg-primary text-primary-foreground",
|
|
4210
|
-
secondary: "bg-secondary text-secondary-foreground",
|
|
4211
|
-
muted: "bg-muted text-foreground",
|
|
4212
|
-
accent: "bg-accent text-accent-foreground",
|
|
4213
|
-
surface: "bg-card",
|
|
4214
|
-
overlay: "bg-card/80 backdrop-blur-sm"
|
|
4215
|
-
};
|
|
4216
|
-
var roundedStyles = {
|
|
4217
|
-
none: "rounded-none",
|
|
4218
|
-
sm: "rounded-sm",
|
|
4219
|
-
md: "rounded-md",
|
|
4220
|
-
lg: "rounded-lg",
|
|
4221
|
-
xl: "rounded-xl",
|
|
4222
|
-
"2xl": "rounded-xl",
|
|
4223
|
-
full: "rounded-full"
|
|
4224
|
-
};
|
|
4225
|
-
var shadowStyles = {
|
|
4226
|
-
none: "shadow-none",
|
|
4227
|
-
sm: "shadow-sm",
|
|
4228
|
-
md: "shadow",
|
|
4229
|
-
lg: "shadow-lg",
|
|
4230
|
-
xl: "shadow-lg"
|
|
4231
|
-
};
|
|
4232
|
-
var displayStyles = {
|
|
4233
|
-
block: "block",
|
|
4234
|
-
inline: "inline",
|
|
4235
|
-
"inline-block": "inline-block",
|
|
4236
|
-
flex: "flex",
|
|
4237
|
-
"inline-flex": "inline-flex",
|
|
4238
|
-
grid: "grid"
|
|
4239
|
-
};
|
|
4240
|
-
var overflowStyles2 = {
|
|
4241
|
-
auto: "overflow-auto",
|
|
4242
|
-
hidden: "overflow-hidden",
|
|
4243
|
-
visible: "overflow-visible",
|
|
4244
|
-
scroll: "overflow-scroll"
|
|
4245
|
-
};
|
|
4246
|
-
var positionStyles = {
|
|
4247
|
-
relative: "relative",
|
|
4248
|
-
absolute: "absolute",
|
|
4249
|
-
fixed: "fixed",
|
|
4250
|
-
sticky: "sticky"
|
|
4251
|
-
};
|
|
4252
|
-
var Box = React8__namespace.default.forwardRef(
|
|
4253
|
-
({
|
|
4254
|
-
padding,
|
|
4255
|
-
paddingX,
|
|
4256
|
-
paddingY,
|
|
4257
|
-
margin,
|
|
4258
|
-
marginX,
|
|
4259
|
-
marginY,
|
|
4260
|
-
bg = "transparent",
|
|
4261
|
-
border = false,
|
|
4262
|
-
rounded = "none",
|
|
4263
|
-
shadow = "none",
|
|
4264
|
-
display,
|
|
4265
|
-
fullWidth = false,
|
|
4266
|
-
fullHeight = false,
|
|
4267
|
-
overflow,
|
|
4268
|
-
position,
|
|
4269
|
-
className,
|
|
4270
|
-
children,
|
|
4271
|
-
as: Component2 = "div",
|
|
4272
|
-
action,
|
|
4273
|
-
actionPayload,
|
|
4274
|
-
hoverEvent,
|
|
4275
|
-
tapReveal = true,
|
|
4276
|
-
maxWidth,
|
|
4277
|
-
onClick,
|
|
4278
|
-
onMouseEnter,
|
|
4279
|
-
onMouseLeave,
|
|
4280
|
-
onPointerDown,
|
|
4281
|
-
...rest
|
|
4282
|
-
}, ref) => {
|
|
4283
|
-
const eventBus = useEventBus();
|
|
4284
|
-
const handleClick = React8.useCallback((e) => {
|
|
4285
|
-
if (action) {
|
|
4286
|
-
e.stopPropagation();
|
|
4287
|
-
eventBus.emit(`UI:${action}`, actionPayload ?? {});
|
|
4288
|
-
}
|
|
4289
|
-
onClick?.(e);
|
|
4290
|
-
}, [action, actionPayload, eventBus, onClick]);
|
|
4291
|
-
const handleMouseEnter = React8.useCallback((e) => {
|
|
4292
|
-
if (hoverEvent) {
|
|
4293
|
-
eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
|
|
4294
|
-
}
|
|
4295
|
-
onMouseEnter?.(e);
|
|
4296
|
-
}, [hoverEvent, eventBus, onMouseEnter]);
|
|
4297
|
-
const handleMouseLeave = React8.useCallback((e) => {
|
|
4298
|
-
if (hoverEvent) {
|
|
4299
|
-
eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
|
|
4300
|
-
}
|
|
4301
|
-
onMouseLeave?.(e);
|
|
4302
|
-
}, [hoverEvent, eventBus, onMouseLeave]);
|
|
4303
|
-
const { triggerProps } = useTapReveal({
|
|
4304
|
-
enabled: tapReveal && !!hoverEvent,
|
|
4305
|
-
onReveal: React8.useCallback(() => {
|
|
4306
|
-
if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
|
|
4307
|
-
}, [hoverEvent, eventBus]),
|
|
4308
|
-
onDismiss: React8.useCallback(() => {
|
|
4309
|
-
if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
|
|
4310
|
-
}, [hoverEvent, eventBus])
|
|
4311
|
-
});
|
|
4312
|
-
const handlePointerDown = React8.useCallback((e) => {
|
|
4313
|
-
if (hoverEvent && tapReveal) triggerProps.onPointerDown(e);
|
|
4314
|
-
onPointerDown?.(e);
|
|
4315
|
-
}, [hoverEvent, tapReveal, triggerProps, onPointerDown]);
|
|
4316
|
-
const isClickable = action || onClick;
|
|
4317
|
-
return React8__namespace.default.createElement(
|
|
4318
|
-
Component2,
|
|
4319
|
-
{
|
|
4320
|
-
ref,
|
|
4321
|
-
className: cn(
|
|
4322
|
-
padding && paddingStyles[padding],
|
|
4323
|
-
paddingX && paddingXStyles[paddingX],
|
|
4324
|
-
paddingY && paddingYStyles[paddingY],
|
|
4325
|
-
margin && marginStyles[margin],
|
|
4326
|
-
marginX && marginXStyles[marginX],
|
|
4327
|
-
marginY && marginYStyles[marginY],
|
|
4328
|
-
bgStyles[bg],
|
|
4329
|
-
border && "border-[length:var(--border-width)] border-border",
|
|
4330
|
-
roundedStyles[rounded],
|
|
4331
|
-
shadowStyles[shadow],
|
|
4332
|
-
display && displayStyles[display],
|
|
4333
|
-
fullWidth && "w-full",
|
|
4334
|
-
fullHeight && "h-full",
|
|
4335
|
-
overflow && overflowStyles2[overflow],
|
|
4336
|
-
position && positionStyles[position],
|
|
4337
|
-
isClickable && "cursor-pointer",
|
|
4338
|
-
className
|
|
4339
|
-
),
|
|
4340
|
-
onClick: isClickable ? handleClick : void 0,
|
|
4341
|
-
onMouseEnter: hoverEvent || onMouseEnter ? handleMouseEnter : void 0,
|
|
4342
|
-
onMouseLeave: hoverEvent || onMouseLeave ? handleMouseLeave : void 0,
|
|
4343
|
-
onPointerDown: hoverEvent && tapReveal || onPointerDown ? handlePointerDown : void 0,
|
|
4344
|
-
style: maxWidth ? { maxWidth, ...rest.style } : rest.style,
|
|
4345
|
-
...rest
|
|
4346
|
-
},
|
|
4347
|
-
children
|
|
4348
|
-
);
|
|
4349
|
-
}
|
|
4350
|
-
);
|
|
4351
|
-
Box.displayName = "Box";
|
|
4352
|
-
|
|
4353
|
-
// components/game/shared/makeAsset.ts
|
|
4354
|
-
var ROLE_DEFAULTS = {
|
|
4355
|
-
tile: { dimension: "2d", animations: ["static"], aspect: "1:1" },
|
|
4356
|
-
decoration: { dimension: "2d", animations: ["static"], aspect: "1:1" },
|
|
4357
|
-
effect: { dimension: "2d", animations: ["static"], aspect: "1:1" },
|
|
4358
|
-
item: { dimension: "2d", animations: ["static"], aspect: "5:7" },
|
|
4359
|
-
player: { dimension: "2d", animations: ["idle", "walk", "attack", "hit", "death"], aspect: "1:1" },
|
|
4360
|
-
enemy: { dimension: "2d", animations: ["idle", "walk", "attack", "hit", "death"], aspect: "1:1" },
|
|
4361
|
-
npc: { dimension: "2d", animations: ["idle", "walk", "attack", "hit", "death"], aspect: "1:1" },
|
|
4362
|
-
projectile: { dimension: "2d", animations: ["static"], aspect: "1:1" },
|
|
4363
|
-
vehicle: { dimension: "2d", animations: ["idle", "walk"], aspect: "1:1" },
|
|
4364
|
-
ui: { dimension: "2d", animations: ["static"], aspect: "1:1" }
|
|
4365
|
-
};
|
|
4366
|
-
function makeAsset(url, role, overrides = {}) {
|
|
4367
|
-
const defaults = ROLE_DEFAULTS[role] ?? { dimension: "2d", animations: ["static"], aspect: "1:1" };
|
|
4368
|
-
return {
|
|
4369
|
-
url,
|
|
4370
|
-
role,
|
|
4371
|
-
category: url.split("/").pop()?.replace(/\.[^.]+$/, "") ?? role,
|
|
4372
|
-
...defaults,
|
|
4373
|
-
...overrides
|
|
4374
|
-
};
|
|
4375
|
-
}
|
|
4376
|
-
|
|
4377
|
-
// components/game/shared/lib/game3dAssetManifest.ts
|
|
4378
|
-
function tileKind(type, passable) {
|
|
4379
|
-
if (passable === false) return "wall";
|
|
4380
|
-
if (type === "dirt" || type === "road") return "dirt";
|
|
4381
|
-
return "open";
|
|
4382
|
-
}
|
|
4383
|
-
function resolveTilesWithModels(layout, manifest) {
|
|
4384
|
-
return layout.map((t) => {
|
|
4385
|
-
const kind = t.kind || tileKind(t.type, t.passable);
|
|
4386
|
-
return {
|
|
4387
|
-
id: t.id,
|
|
4388
|
-
x: t.x,
|
|
4389
|
-
y: t.y,
|
|
4390
|
-
z: t.z,
|
|
4391
|
-
type: t.type,
|
|
4392
|
-
passable: t.passable,
|
|
4393
|
-
modelUrl: manifest?.models?.[kind]
|
|
4394
|
-
};
|
|
4395
|
-
});
|
|
4396
|
-
}
|
|
4397
|
-
function resolvePropTiles(tiles, manifest) {
|
|
4398
|
-
return tiles.map((t) => {
|
|
4399
|
-
const kind = tileKind(t.type ?? "", t.passable);
|
|
4400
|
-
return {
|
|
4401
|
-
...t,
|
|
4402
|
-
modelUrl: t.modelUrl ?? manifest?.models?.[kind]
|
|
4403
|
-
};
|
|
4404
|
-
});
|
|
4405
|
-
}
|
|
4406
|
-
function resolveEntityTiles(entity, manifest) {
|
|
4407
|
-
const row = boardEntity(entity);
|
|
4408
|
-
if (!row) return [];
|
|
4409
|
-
return rows(row.tiles).map((r) => {
|
|
4410
|
-
const type = str(r.type) || str(r.terrain);
|
|
4411
|
-
const passable = r.passable !== false;
|
|
4412
|
-
const kind = tileKind(type, passable);
|
|
4413
|
-
const explicitModelUrl = r.modelUrl == null ? void 0 : str(r.modelUrl);
|
|
4414
|
-
const explicitModel = explicitModelUrl ? makeAsset(explicitModelUrl, "decoration", { dimension: "3d" }) : void 0;
|
|
4415
|
-
return {
|
|
4416
|
-
id: r.id == null ? void 0 : str(r.id),
|
|
4417
|
-
x: num(r.x),
|
|
4418
|
-
y: num(r.y),
|
|
4419
|
-
z: r.z == null ? void 0 : num(r.z),
|
|
4420
|
-
type,
|
|
4421
|
-
passable,
|
|
4422
|
-
modelUrl: explicitModel ?? manifest?.models?.[kind]
|
|
4423
|
-
};
|
|
4424
|
-
});
|
|
4425
|
-
}
|
|
4426
|
-
function resolveFeaturesWithModels(features, manifest) {
|
|
4427
|
-
return features.map((f) => ({
|
|
4428
|
-
...f,
|
|
4429
|
-
assetUrl: f.assetUrl ?? manifest?.features?.[f.type]
|
|
4430
|
-
}));
|
|
4431
|
-
}
|
|
4432
|
-
function resolveEntityFeatures(entity, manifest) {
|
|
4433
|
-
const row = boardEntity(entity);
|
|
4434
|
-
if (!row) return [];
|
|
4435
|
-
return rows(row.features).map((r) => {
|
|
4436
|
-
const type = str(r.type);
|
|
4437
|
-
const explicitUrl = r.assetUrl == null ? void 0 : str(r.assetUrl);
|
|
4438
|
-
const explicit = explicitUrl ? makeAsset(explicitUrl, "decoration", { dimension: "3d" }) : void 0;
|
|
4439
|
-
return {
|
|
4440
|
-
id: r.id == null ? void 0 : str(r.id),
|
|
4441
|
-
x: num(r.x),
|
|
4442
|
-
y: num(r.y),
|
|
4443
|
-
z: r.z == null ? void 0 : num(r.z),
|
|
4444
|
-
type,
|
|
4445
|
-
color: r.color == null ? void 0 : str(r.color),
|
|
4446
|
-
assetUrl: explicit ?? manifest?.features?.[type]
|
|
4447
|
-
};
|
|
4448
|
-
});
|
|
4449
|
-
}
|
|
4450
|
-
var DEFAULT_3D_BATTLE_LAYOUT = [
|
|
4451
|
-
{ id: "t00", x: 0, y: 0, z: 0, type: "stone", passable: false, kind: "wall" },
|
|
4452
|
-
{ id: "t10", x: 1, y: 0, z: 0, type: "stone", passable: false, kind: "wall" },
|
|
4453
|
-
{ id: "t20", x: 2, y: 0, z: 0, type: "stone", passable: false, kind: "wall" },
|
|
4454
|
-
{ id: "t30", x: 3, y: 0, z: 0, type: "stone", passable: false, kind: "wall" },
|
|
4455
|
-
{ id: "t40", x: 4, y: 0, z: 0, type: "stone", passable: false, kind: "wall" },
|
|
4456
|
-
{ id: "t01", x: 0, y: 1, z: 1, type: "stone", passable: false, kind: "wall" },
|
|
4457
|
-
{ id: "t11", x: 1, y: 1, z: 1, type: "dirt", passable: true, kind: "dirt" },
|
|
4458
|
-
{ id: "t21", x: 2, y: 1, z: 1, type: "grass", passable: true, kind: "open" },
|
|
4459
|
-
{ id: "t31", x: 3, y: 1, z: 1, type: "grass", passable: true, kind: "open" },
|
|
4460
|
-
{ id: "t41", x: 4, y: 1, z: 1, type: "stone", passable: false, kind: "wall" },
|
|
4461
|
-
{ id: "t02", x: 0, y: 2, z: 2, type: "stone", passable: false, kind: "wall" },
|
|
4462
|
-
{ id: "t12", x: 1, y: 2, z: 2, type: "grass", passable: true, kind: "open" },
|
|
4463
|
-
{ id: "t22", x: 2, y: 2, z: 2, type: "dirt", passable: true, kind: "dirt" },
|
|
4464
|
-
{ id: "t32", x: 3, y: 2, z: 2, type: "grass", passable: true, kind: "open" },
|
|
4465
|
-
{ id: "t42", x: 4, y: 2, z: 2, type: "stone", passable: false, kind: "wall" },
|
|
4466
|
-
{ id: "t03", x: 0, y: 3, z: 3, type: "stone", passable: false, kind: "wall" },
|
|
4467
|
-
{ id: "t13", x: 1, y: 3, z: 3, type: "grass", passable: true, kind: "open" },
|
|
4468
|
-
{ id: "t23", x: 2, y: 3, z: 3, type: "grass", passable: true, kind: "open" },
|
|
4469
|
-
{ id: "t33", x: 3, y: 3, z: 3, type: "dirt", passable: true, kind: "dirt" },
|
|
4470
|
-
{ id: "t43", x: 4, y: 3, z: 3, type: "stone", passable: false, kind: "wall" },
|
|
4471
|
-
{ id: "t04", x: 0, y: 4, z: 4, type: "stone", passable: false, kind: "wall" },
|
|
4472
|
-
{ id: "t14", x: 1, y: 4, z: 4, type: "stone", passable: false, kind: "wall" },
|
|
4473
|
-
{ id: "t24", x: 2, y: 4, z: 4, type: "stone", passable: false, kind: "wall" },
|
|
4474
|
-
{ id: "t34", x: 3, y: 4, z: 4, type: "stone", passable: false, kind: "wall" },
|
|
4475
|
-
{ id: "t44", x: 4, y: 4, z: 4, type: "stone", passable: false, kind: "wall" }
|
|
4476
|
-
];
|
|
4477
|
-
var DEFAULT_3D_BATTLE_FEATURES = [
|
|
4478
|
-
{ id: "f1", x: 2, y: 2, z: 2, type: "gold_mine", color: "#f4c542" },
|
|
4479
|
-
{ id: "f2", x: 3, y: 1, z: 1, type: "portal", color: "#8b5cf6" }
|
|
4480
|
-
];
|
|
4481
|
-
function GameCanvas3DBattleTemplate({
|
|
4482
|
-
entity,
|
|
4483
|
-
tiles: propTiles,
|
|
4484
|
-
features: propFeatures = DEFAULT_3D_BATTLE_FEATURES,
|
|
4485
|
-
assetManifest,
|
|
4486
|
-
cameraMode = "perspective",
|
|
4487
|
-
backgroundColor = "#2a1a1a",
|
|
4488
|
-
tileClickEvent,
|
|
4489
|
-
unitClickEvent,
|
|
4490
|
-
endTurnEvent,
|
|
4491
|
-
cancelEvent,
|
|
4492
|
-
attackEvent,
|
|
4493
|
-
playAgainEvent,
|
|
4494
|
-
gameEndEvent,
|
|
4495
|
-
unitScale,
|
|
4496
|
-
scale,
|
|
4497
|
-
className
|
|
4498
|
-
}) {
|
|
4499
|
-
const row = boardEntity(entity);
|
|
4500
|
-
const hasEntityTiles = !!row && Array.isArray(row.tiles) && row.tiles.length > 0;
|
|
4501
|
-
const tiles = hasEntityTiles ? resolveEntityTiles(entity, assetManifest) : propTiles ? resolvePropTiles(propTiles, assetManifest) : resolveTilesWithModels(DEFAULT_3D_BATTLE_LAYOUT, assetManifest);
|
|
4502
|
-
const features = row && Array.isArray(row.features) && row.features.length > 0 ? resolveEntityFeatures(entity, assetManifest) : resolveFeaturesWithModels(propFeatures, assetManifest);
|
|
4503
|
-
const currentTurn = row?.currentTeam == null ? void 0 : String(row.currentTeam);
|
|
4504
|
-
const round = row?.turn == null ? void 0 : Number(row.turn);
|
|
4505
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4506
|
-
Box,
|
|
4507
|
-
{
|
|
4508
|
-
className: cn("game-canvas-3d-battle-template block relative w-full min-h-[85vh]", className),
|
|
4509
|
-
children: [
|
|
4510
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4511
|
-
GameBoard3D,
|
|
4512
|
-
{
|
|
4513
|
-
entity,
|
|
4514
|
-
tiles,
|
|
4515
|
-
features,
|
|
4516
|
-
cameraMode,
|
|
4517
|
-
backgroundColor,
|
|
4518
|
-
unitScale,
|
|
4519
|
-
scale,
|
|
4520
|
-
tileClickEvent,
|
|
4521
|
-
unitClickEvent,
|
|
4522
|
-
endTurnEvent,
|
|
4523
|
-
cancelEvent,
|
|
4524
|
-
attackEvent,
|
|
4525
|
-
playAgainEvent,
|
|
4526
|
-
gameEndEvent,
|
|
4527
|
-
className: "game-canvas-3d-battle-template__board"
|
|
4528
|
-
}
|
|
4529
|
-
),
|
|
4530
|
-
currentTurn && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4531
|
-
HStack,
|
|
4532
|
-
{
|
|
4533
|
-
gap: "sm",
|
|
4534
|
-
align: "center",
|
|
4535
|
-
className: cn("battle-template__turn-indicator absolute top-3 right-3 z-10", `battle-template__turn-indicator--${currentTurn}`),
|
|
4536
|
-
children: [
|
|
4537
|
-
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "body", className: "turn-indicator__label", children: currentTurn === "player" ? "Your Turn" : "Enemy's Turn" }),
|
|
4538
|
-
round != null && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", className: "turn-indicator__round", children: [
|
|
4539
|
-
"Round ",
|
|
4540
|
-
round
|
|
4541
|
-
] })
|
|
4542
|
-
]
|
|
4543
|
-
}
|
|
4544
|
-
)
|
|
4545
|
-
]
|
|
4546
|
-
}
|
|
4547
|
-
);
|
|
4548
|
-
}
|
|
4549
|
-
GameCanvas3DBattleTemplate.displayName = "GameCanvas3DBattleTemplate";
|
|
4550
|
-
var DEFAULT_3D_CASTLE_LAYOUT = [
|
|
4551
|
-
{ id: "t00", x: 0, y: 0, z: 0, type: "wall", passable: false, kind: "wall" },
|
|
4552
|
-
{ id: "t10", x: 1, y: 0, z: 0, type: "wall", passable: false, kind: "wall" },
|
|
4553
|
-
{ id: "t20", x: 2, y: 0, z: 0, type: "wall", passable: false, kind: "wall" },
|
|
4554
|
-
{ id: "t30", x: 3, y: 0, z: 0, type: "wall", passable: false, kind: "wall" },
|
|
4555
|
-
{ id: "t40", x: 4, y: 0, z: 0, type: "wall", passable: false, kind: "wall" },
|
|
4556
|
-
{ id: "t01", x: 0, y: 1, z: 1, type: "wall", passable: false, kind: "wall" },
|
|
4557
|
-
{ id: "t11", x: 1, y: 1, z: 1, type: "floor", passable: true, kind: "dirt" },
|
|
4558
|
-
{ id: "t21", x: 2, y: 1, z: 1, type: "floor", passable: true, kind: "open" },
|
|
4559
|
-
{ id: "t31", x: 3, y: 1, z: 1, type: "floor", passable: true, kind: "open" },
|
|
4560
|
-
{ id: "t41", x: 4, y: 1, z: 1, type: "wall", passable: false, kind: "wall" },
|
|
4561
|
-
{ id: "t02", x: 0, y: 2, z: 2, type: "wall", passable: false, kind: "wall" },
|
|
4562
|
-
{ id: "t12", x: 1, y: 2, z: 2, type: "floor", passable: true, kind: "open" },
|
|
4563
|
-
{ id: "t22", x: 2, y: 2, z: 2, type: "floor", passable: true, kind: "dirt" },
|
|
4564
|
-
{ id: "t32", x: 3, y: 2, z: 2, type: "floor", passable: true, kind: "open" },
|
|
4565
|
-
{ id: "t42", x: 4, y: 2, z: 2, type: "wall", passable: false, kind: "wall" },
|
|
4566
|
-
{ id: "t03", x: 0, y: 3, z: 3, type: "wall", passable: false, kind: "wall" },
|
|
4567
|
-
{ id: "t13", x: 1, y: 3, z: 3, type: "floor", passable: true, kind: "open" },
|
|
4568
|
-
{ id: "t23", x: 2, y: 3, z: 3, type: "floor", passable: true, kind: "open" },
|
|
4569
|
-
{ id: "t33", x: 3, y: 3, z: 3, type: "floor", passable: true, kind: "dirt" },
|
|
4570
|
-
{ id: "t43", x: 4, y: 3, z: 3, type: "wall", passable: false, kind: "wall" },
|
|
4571
|
-
{ id: "t04", x: 0, y: 4, z: 4, type: "wall", passable: false, kind: "wall" },
|
|
4572
|
-
{ id: "t14", x: 1, y: 4, z: 4, type: "wall", passable: false, kind: "wall" },
|
|
4573
|
-
{ id: "t24", x: 2, y: 4, z: 4, type: "wall", passable: false, kind: "wall" },
|
|
4574
|
-
{ id: "t34", x: 3, y: 4, z: 4, type: "wall", passable: false, kind: "wall" },
|
|
4575
|
-
{ id: "t44", x: 4, y: 4, z: 4, type: "wall", passable: false, kind: "wall" }
|
|
4576
|
-
];
|
|
4577
|
-
var DEFAULT_3D_CASTLE_FEATURES = [
|
|
4578
|
-
{ id: "f1", x: 2, y: 2, z: 2, type: "chest", color: "#f4c542" },
|
|
4579
|
-
{ id: "f2", x: 3, y: 1, z: 1, type: "crystal", color: "#8b5cf6" }
|
|
4580
|
-
];
|
|
4581
|
-
function GameCanvas3DCastleTemplate({
|
|
4582
|
-
entity,
|
|
4583
|
-
tiles: propTiles,
|
|
4584
|
-
features: propFeatures = DEFAULT_3D_CASTLE_FEATURES,
|
|
4585
|
-
assetManifest,
|
|
4586
|
-
cameraMode = "isometric",
|
|
4587
|
-
backgroundColor = "#1e1e2e",
|
|
4588
|
-
buildingClickEvent,
|
|
4589
|
-
unitClickEvent,
|
|
4590
|
-
endTurnEvent,
|
|
4591
|
-
cancelEvent,
|
|
4592
|
-
attackEvent,
|
|
4593
|
-
playAgainEvent,
|
|
4594
|
-
gameEndEvent,
|
|
4595
|
-
showHeader = true,
|
|
4596
|
-
unitScale,
|
|
4597
|
-
scale,
|
|
4598
|
-
className
|
|
4599
|
-
}) {
|
|
4600
|
-
const row = boardEntity(entity);
|
|
4601
|
-
const hasEntityTiles = !!row && Array.isArray(row.tiles) && row.tiles.length > 0;
|
|
4602
|
-
const tiles = hasEntityTiles ? resolveEntityTiles(entity, assetManifest) : propTiles ? resolvePropTiles(propTiles, assetManifest) : resolveTilesWithModels(DEFAULT_3D_CASTLE_LAYOUT, assetManifest);
|
|
4603
|
-
const features = row && Array.isArray(row.features) && row.features.length > 0 ? resolveEntityFeatures(entity, assetManifest) : resolveFeaturesWithModels(propFeatures, assetManifest);
|
|
4604
|
-
const name = row?.name == null ? void 0 : str(row.name);
|
|
4605
|
-
const level = row?.level == null ? void 0 : num(row.level);
|
|
4606
|
-
const owner = row?.owner == null ? void 0 : str(row.owner);
|
|
4607
|
-
const unitCount = row && Array.isArray(row.units) ? row.units.length : 0;
|
|
4608
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4609
|
-
VStack,
|
|
4610
|
-
{
|
|
4611
|
-
className: cn("game-canvas-3d-castle-template block w-full min-h-[85vh]", className),
|
|
4612
|
-
children: [
|
|
4613
|
-
showHeader && name && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "md", align: "center", className: "castle-template__header", children: [
|
|
4614
|
-
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "h2", className: "header__name", children: name }),
|
|
4615
|
-
level != null && /* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", className: "header__level", children: [
|
|
4616
|
-
"Level ",
|
|
4617
|
-
level
|
|
4618
|
-
] }),
|
|
4619
|
-
owner && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "muted", className: "header__owner", children: owner })
|
|
4620
|
-
] }),
|
|
4621
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4622
|
-
GameBoard3D,
|
|
4623
|
-
{
|
|
4624
|
-
entity,
|
|
4625
|
-
tiles,
|
|
4626
|
-
features,
|
|
4627
|
-
cameraMode,
|
|
4628
|
-
backgroundColor,
|
|
4629
|
-
unitScale,
|
|
4630
|
-
scale,
|
|
4631
|
-
tileClickEvent: buildingClickEvent,
|
|
4632
|
-
unitClickEvent,
|
|
4633
|
-
endTurnEvent,
|
|
4634
|
-
cancelEvent,
|
|
4635
|
-
attackEvent,
|
|
4636
|
-
playAgainEvent,
|
|
4637
|
-
gameEndEvent,
|
|
4638
|
-
className: "game-canvas-3d-castle-template__board"
|
|
4639
|
-
}
|
|
4640
|
-
),
|
|
4641
|
-
unitCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs(HStack, { gap: "sm", align: "center", className: "castle-template__garrison-info", children: [
|
|
4642
|
-
/* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", className: "garrison-info__label", children: "Garrison:" }),
|
|
4643
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Typography, { variant: "small", weight: "bold", className: "garrison-info__count", children: [
|
|
4644
|
-
unitCount,
|
|
4645
|
-
" units"
|
|
4646
|
-
] })
|
|
4647
|
-
] })
|
|
4648
|
-
]
|
|
4649
|
-
}
|
|
4650
|
-
);
|
|
4651
|
-
}
|
|
4652
|
-
GameCanvas3DCastleTemplate.displayName = "GameCanvas3DCastleTemplate";
|
|
4653
|
-
var DEFAULT_3D_WORLDMAP_LAYOUT = [
|
|
4654
|
-
{ id: "t00", x: 0, y: 0, z: 0, type: "mountain", passable: false, kind: "wall" },
|
|
4655
|
-
{ id: "t10", x: 1, y: 0, z: 0, type: "mountain", passable: false, kind: "wall" },
|
|
4656
|
-
{ id: "t20", x: 2, y: 0, z: 0, type: "water", passable: false, kind: "wall" },
|
|
4657
|
-
{ id: "t30", x: 3, y: 0, z: 0, type: "mountain", passable: false, kind: "wall" },
|
|
4658
|
-
{ id: "t40", x: 4, y: 0, z: 0, type: "mountain", passable: false, kind: "wall" },
|
|
4659
|
-
{ id: "t01", x: 0, y: 1, z: 1, type: "mountain", passable: false, kind: "wall" },
|
|
4660
|
-
{ id: "t11", x: 1, y: 1, z: 1, type: "grass", passable: true, kind: "open" },
|
|
4661
|
-
{ id: "t21", x: 2, y: 1, z: 1, type: "grass", passable: true, kind: "open" },
|
|
4662
|
-
{ id: "t31", x: 3, y: 1, z: 1, type: "grass", passable: true, kind: "open" },
|
|
4663
|
-
{ id: "t41", x: 4, y: 1, z: 1, type: "water", passable: false, kind: "wall" },
|
|
4664
|
-
{ id: "t02", x: 0, y: 2, z: 2, type: "water", passable: false, kind: "wall" },
|
|
4665
|
-
{ id: "t12", x: 1, y: 2, z: 2, type: "grass", passable: true, kind: "open" },
|
|
4666
|
-
{ id: "t22", x: 2, y: 2, z: 2, type: "road", passable: true, kind: "dirt" },
|
|
4667
|
-
{ id: "t32", x: 3, y: 2, z: 2, type: "grass", passable: true, kind: "open" },
|
|
4668
|
-
{ id: "t42", x: 4, y: 2, z: 2, type: "mountain", passable: false, kind: "wall" },
|
|
4669
|
-
{ id: "t03", x: 0, y: 3, z: 3, type: "mountain", passable: false, kind: "wall" },
|
|
4670
|
-
{ id: "t13", x: 1, y: 3, z: 3, type: "grass", passable: true, kind: "open" },
|
|
4671
|
-
{ id: "t23", x: 2, y: 3, z: 3, type: "grass", passable: true, kind: "open" },
|
|
4672
|
-
{ id: "t33", x: 3, y: 3, z: 3, type: "road", passable: true, kind: "dirt" },
|
|
4673
|
-
{ id: "t43", x: 4, y: 3, z: 3, type: "mountain", passable: false, kind: "wall" },
|
|
4674
|
-
{ id: "t04", x: 0, y: 4, z: 4, type: "mountain", passable: false, kind: "wall" },
|
|
4675
|
-
{ id: "t14", x: 1, y: 4, z: 4, type: "water", passable: false, kind: "wall" },
|
|
4676
|
-
{ id: "t24", x: 2, y: 4, z: 4, type: "grass", passable: true, kind: "open" },
|
|
4677
|
-
{ id: "t34", x: 3, y: 4, z: 4, type: "mountain", passable: false, kind: "wall" },
|
|
4678
|
-
{ id: "t44", x: 4, y: 4, z: 4, type: "mountain", passable: false, kind: "wall" }
|
|
4679
|
-
];
|
|
4680
|
-
var DEFAULT_3D_WORLDMAP_FEATURES = [
|
|
4681
|
-
{ id: "f1", x: 2, y: 2, z: 2, type: "capital", color: "#f4c542" },
|
|
4682
|
-
{ id: "f2", x: 4, y: 2, z: 2, type: "power_node", color: "#8b5cf6" }
|
|
4683
|
-
];
|
|
4684
|
-
function GameCanvas3DWorldMapTemplate({
|
|
4685
|
-
entity,
|
|
4686
|
-
tiles: propTiles,
|
|
4687
|
-
features: propFeatures = DEFAULT_3D_WORLDMAP_FEATURES,
|
|
4688
|
-
assetManifest,
|
|
4689
|
-
cameraMode = "isometric",
|
|
4690
|
-
backgroundColor = "#1a1a2e",
|
|
4691
|
-
tileClickEvent,
|
|
4692
|
-
unitClickEvent,
|
|
4693
|
-
endTurnEvent,
|
|
4694
|
-
cancelEvent,
|
|
4695
|
-
attackEvent,
|
|
4696
|
-
playAgainEvent,
|
|
4697
|
-
gameEndEvent,
|
|
4698
|
-
unitScale,
|
|
4699
|
-
scale,
|
|
4700
|
-
className
|
|
4701
|
-
}) {
|
|
4702
|
-
const row = boardEntity(entity);
|
|
4703
|
-
const hasEntityTiles = !!row && Array.isArray(row.tiles) && row.tiles.length > 0;
|
|
4704
|
-
const tiles = hasEntityTiles ? resolveEntityTiles(entity, assetManifest) : propTiles ? resolvePropTiles(propTiles, assetManifest) : resolveTilesWithModels(DEFAULT_3D_WORLDMAP_LAYOUT, assetManifest);
|
|
4705
|
-
const features = row && Array.isArray(row.features) && row.features.length > 0 ? resolveEntityFeatures(entity, assetManifest) : resolveFeaturesWithModels(propFeatures, assetManifest);
|
|
4706
|
-
return (
|
|
4707
|
-
/* GameBoard3D reads selectedUnitId/validMoves/attackTargets from entity */
|
|
4708
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4709
|
-
GameBoard3D,
|
|
4710
|
-
{
|
|
4711
|
-
entity,
|
|
4712
|
-
tiles,
|
|
4713
|
-
features,
|
|
4714
|
-
cameraMode,
|
|
4715
|
-
backgroundColor,
|
|
4716
|
-
unitScale,
|
|
4717
|
-
scale,
|
|
4718
|
-
tileClickEvent,
|
|
4719
|
-
unitClickEvent,
|
|
4720
|
-
endTurnEvent,
|
|
4721
|
-
cancelEvent,
|
|
4722
|
-
attackEvent,
|
|
4723
|
-
playAgainEvent,
|
|
4724
|
-
gameEndEvent,
|
|
4725
|
-
className
|
|
4726
|
-
}
|
|
4727
|
-
)
|
|
4728
|
-
);
|
|
4729
|
-
}
|
|
4730
|
-
GameCanvas3DWorldMapTemplate.displayName = "GameCanvas3DWorldMapTemplate";
|
|
4731
|
-
var DEFAULT_CONFIG = {
|
|
4732
|
-
cellSize: 1,
|
|
4733
|
-
offsetX: 0,
|
|
4734
|
-
offsetZ: 0,
|
|
4735
|
-
elevation: 0
|
|
4736
|
-
};
|
|
4737
|
-
function gridToWorld(gridX, gridZ, config = DEFAULT_CONFIG) {
|
|
4738
|
-
const opts = { ...DEFAULT_CONFIG, ...config };
|
|
4739
|
-
return new THREE6__namespace.Vector3(
|
|
4740
|
-
gridX * opts.cellSize + opts.offsetX,
|
|
4741
|
-
opts.elevation,
|
|
4742
|
-
gridZ * opts.cellSize + opts.offsetZ
|
|
4743
|
-
);
|
|
4744
|
-
}
|
|
4745
|
-
function worldToGrid(worldX, worldZ, config = DEFAULT_CONFIG) {
|
|
4746
|
-
const opts = { ...DEFAULT_CONFIG, ...config };
|
|
4747
|
-
return {
|
|
4748
|
-
x: Math.round((worldX - opts.offsetX) / opts.cellSize),
|
|
4749
|
-
z: Math.round((worldZ - opts.offsetZ) / opts.cellSize)
|
|
4750
|
-
};
|
|
4751
|
-
}
|
|
4752
|
-
function raycastToPlane(camera, mouseX, mouseY, planeY = 0) {
|
|
4753
|
-
const raycaster = new THREE6__namespace.Raycaster();
|
|
4754
|
-
const mouse = new THREE6__namespace.Vector2(mouseX, mouseY);
|
|
4755
|
-
raycaster.setFromCamera(mouse, camera);
|
|
4756
|
-
const plane = new THREE6__namespace.Plane(new THREE6__namespace.Vector3(0, 1, 0), -planeY);
|
|
4757
|
-
const target = new THREE6__namespace.Vector3();
|
|
4758
|
-
const intersection = raycaster.ray.intersectPlane(plane, target);
|
|
4759
|
-
return intersection ? target : null;
|
|
4760
|
-
}
|
|
4761
|
-
function raycastToObjects(camera, mouseX, mouseY, objects) {
|
|
4762
|
-
const raycaster = new THREE6__namespace.Raycaster();
|
|
4763
|
-
const mouse = new THREE6__namespace.Vector2(mouseX, mouseY);
|
|
4764
|
-
raycaster.setFromCamera(mouse, camera);
|
|
4765
|
-
const intersects = raycaster.intersectObjects(objects, true);
|
|
4766
|
-
return intersects.length > 0 ? intersects[0] : null;
|
|
4767
|
-
}
|
|
4768
|
-
function gridDistance(a, b) {
|
|
4769
|
-
const dx = b.x - a.x;
|
|
4770
|
-
const dz = b.z - a.z;
|
|
4771
|
-
return Math.sqrt(dx * dx + dz * dz);
|
|
4772
|
-
}
|
|
4773
|
-
function gridManhattanDistance(a, b) {
|
|
4774
|
-
return Math.abs(b.x - a.x) + Math.abs(b.z - a.z);
|
|
4775
|
-
}
|
|
4776
|
-
function getNeighbors(x, z, includeDiagonal = false) {
|
|
4777
|
-
const neighbors = [
|
|
4778
|
-
{ x: x + 1, z },
|
|
4779
|
-
{ x: x - 1, z },
|
|
4780
|
-
{ x, z: z + 1 },
|
|
4781
|
-
{ x, z: z - 1 }
|
|
4782
|
-
];
|
|
4783
|
-
if (includeDiagonal) {
|
|
4784
|
-
neighbors.push(
|
|
4785
|
-
{ x: x + 1, z: z + 1 },
|
|
4786
|
-
{ x: x - 1, z: z - 1 },
|
|
4787
|
-
{ x: x + 1, z: z - 1 },
|
|
4788
|
-
{ x: x - 1, z: z + 1 }
|
|
4789
|
-
);
|
|
4790
|
-
}
|
|
4791
|
-
return neighbors;
|
|
4792
|
-
}
|
|
4793
|
-
function isInBounds(x, z, bounds) {
|
|
4794
|
-
return x >= bounds.minX && x <= bounds.maxX && z >= bounds.minZ && z <= bounds.maxZ;
|
|
4795
|
-
}
|
|
4796
|
-
function getCellsInRadius(centerX, centerZ, radius) {
|
|
4797
|
-
const cells = [];
|
|
4798
|
-
const radiusSquared = radius * radius;
|
|
4799
|
-
const minX = Math.floor(centerX - radius);
|
|
4800
|
-
const maxX = Math.ceil(centerX + radius);
|
|
4801
|
-
const minZ = Math.floor(centerZ - radius);
|
|
4802
|
-
const maxZ = Math.ceil(centerZ + radius);
|
|
4803
|
-
for (let x = minX; x <= maxX; x++) {
|
|
4804
|
-
for (let z = minZ; z <= maxZ; z++) {
|
|
4805
|
-
const dx = x - centerX;
|
|
4806
|
-
const dz = z - centerZ;
|
|
4807
|
-
if (dx * dx + dz * dz <= radiusSquared) {
|
|
4808
|
-
cells.push({ x, z });
|
|
4809
|
-
}
|
|
4810
|
-
}
|
|
4811
|
-
}
|
|
4812
|
-
return cells;
|
|
4813
|
-
}
|
|
4814
|
-
function createGridHighlight(color = 16776960, opacity = 0.3) {
|
|
4815
|
-
const geometry = new THREE6__namespace.PlaneGeometry(0.95, 0.95);
|
|
4816
|
-
const material = new THREE6__namespace.MeshBasicMaterial({
|
|
4817
|
-
color,
|
|
4818
|
-
transparent: true,
|
|
4819
|
-
opacity,
|
|
4820
|
-
side: THREE6__namespace.DoubleSide
|
|
4821
|
-
});
|
|
4822
|
-
const mesh = new THREE6__namespace.Mesh(geometry, material);
|
|
4823
|
-
mesh.rotation.x = -Math.PI / 2;
|
|
4824
|
-
mesh.position.y = 0.01;
|
|
4825
|
-
return mesh;
|
|
4826
|
-
}
|
|
4827
|
-
function normalizeMouseCoordinates(clientX, clientY, element) {
|
|
4828
|
-
const rect = element.getBoundingClientRect();
|
|
4829
|
-
return {
|
|
4830
|
-
x: (clientX - rect.left) / rect.width * 2 - 1,
|
|
4831
|
-
y: -((clientY - rect.top) / rect.height) * 2 + 1
|
|
4832
|
-
};
|
|
4833
|
-
}
|
|
4834
|
-
|
|
4835
2640
|
// components/avl/lib/avl-schema-parser.ts
|
|
4836
2641
|
function getEntity(orbital) {
|
|
4837
2642
|
const entity = orbital.entity;
|
|
@@ -4926,10 +2731,10 @@ function parseApplicationLevel(schema) {
|
|
|
4926
2731
|
}
|
|
4927
2732
|
const count = schema.orbitals.length;
|
|
4928
2733
|
const cols = Math.ceil(Math.sqrt(count));
|
|
4929
|
-
const
|
|
2734
|
+
const rows = Math.ceil(count / cols);
|
|
4930
2735
|
const spacing = 200;
|
|
4931
2736
|
const gridW = cols * spacing;
|
|
4932
|
-
const gridH =
|
|
2737
|
+
const gridH = rows * spacing;
|
|
4933
2738
|
const originX = (600 - gridW) / 2 + spacing / 2;
|
|
4934
2739
|
const originY = (400 - gridH) / 2 + spacing / 2;
|
|
4935
2740
|
schema.orbitals.forEach((orbital, i) => {
|
|
@@ -5462,7 +3267,7 @@ Avl3DLabel.displayName = "Avl3DLabel";
|
|
|
5462
3267
|
var Avl3DTooltip = ({
|
|
5463
3268
|
position,
|
|
5464
3269
|
title,
|
|
5465
|
-
rows
|
|
3270
|
+
rows,
|
|
5466
3271
|
accentColor = "#5b9bd5"
|
|
5467
3272
|
}) => {
|
|
5468
3273
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -5500,7 +3305,7 @@ var Avl3DTooltip = ({
|
|
|
5500
3305
|
children: title
|
|
5501
3306
|
}
|
|
5502
3307
|
),
|
|
5503
|
-
|
|
3308
|
+
rows.map((row) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5504
3309
|
Box,
|
|
5505
3310
|
{
|
|
5506
3311
|
style: {
|
|
@@ -5554,10 +3359,10 @@ var Avl3DOrbitalNode = ({
|
|
|
5554
3359
|
position,
|
|
5555
3360
|
onClick
|
|
5556
3361
|
}) => {
|
|
5557
|
-
const groupRef =
|
|
5558
|
-
const [hovered, setHovered] =
|
|
5559
|
-
const scale =
|
|
5560
|
-
const currentScale =
|
|
3362
|
+
const groupRef = React7.useRef(null);
|
|
3363
|
+
const [hovered, setHovered] = React7.useState(false);
|
|
3364
|
+
const scale = React7.useMemo(() => 0.6 + Math.min(traitCount, 8) * 0.3, [traitCount]);
|
|
3365
|
+
const currentScale = React7.useRef(scale);
|
|
5561
3366
|
const targetScale = hovered ? scale * 1.06 : scale;
|
|
5562
3367
|
fiber.useFrame((_, delta) => {
|
|
5563
3368
|
if (!groupRef.current) return;
|
|
@@ -5699,7 +3504,7 @@ var Avl3DCrossWire = ({
|
|
|
5699
3504
|
eventName,
|
|
5700
3505
|
color = AVL_3D_COLORS.crossWire
|
|
5701
3506
|
}) => {
|
|
5702
|
-
const { tubeArgs, midpoint } =
|
|
3507
|
+
const { tubeArgs, midpoint } = React7.useMemo(() => {
|
|
5703
3508
|
const curve = arcCurve3D(from, to, 2);
|
|
5704
3509
|
const mid = curve.getPoint(0.5);
|
|
5705
3510
|
return {
|
|
@@ -5738,9 +3543,9 @@ var Avl3DApplicationScene = ({
|
|
|
5738
3543
|
data,
|
|
5739
3544
|
onOrbitalClick
|
|
5740
3545
|
}) => {
|
|
5741
|
-
const groupRef =
|
|
5742
|
-
const [, setHoverTrigger] =
|
|
5743
|
-
const orbitalPositions =
|
|
3546
|
+
const groupRef = React7.useRef(null);
|
|
3547
|
+
const [, setHoverTrigger] = React7.useState(0);
|
|
3548
|
+
const orbitalPositions = React7.useMemo(() => {
|
|
5744
3549
|
const baseRadius = Math.max(4, data.orbitals.length * 1.5);
|
|
5745
3550
|
if (data.crossLinks.length === 0) {
|
|
5746
3551
|
return goldenSpiralPositions(data.orbitals.length, baseRadius);
|
|
@@ -5753,7 +3558,7 @@ var Avl3DApplicationScene = ({
|
|
|
5753
3558
|
})).filter((e) => e.from >= 0 && e.to >= 0);
|
|
5754
3559
|
return forceDirectedPositions(data.orbitals.length, edges, baseRadius);
|
|
5755
3560
|
}, [data.orbitals, data.crossLinks]);
|
|
5756
|
-
const positionByName =
|
|
3561
|
+
const positionByName = React7.useMemo(() => {
|
|
5757
3562
|
const map = /* @__PURE__ */ new Map();
|
|
5758
3563
|
data.orbitals.forEach((orb, i) => {
|
|
5759
3564
|
const pos = orbitalPositions[i];
|
|
@@ -5836,7 +3641,7 @@ var Avl3DEntityCore = ({
|
|
|
5836
3641
|
position,
|
|
5837
3642
|
fields
|
|
5838
3643
|
}) => {
|
|
5839
|
-
const coreRef =
|
|
3644
|
+
const coreRef = React7.useRef(null);
|
|
5840
3645
|
fiber.useFrame((_, delta) => {
|
|
5841
3646
|
if (!coreRef.current) return;
|
|
5842
3647
|
coreRef.current.rotation.y += delta * 0.3;
|
|
@@ -5989,8 +3794,8 @@ function TraitOrbit({
|
|
|
5989
3794
|
onClick,
|
|
5990
3795
|
onHover
|
|
5991
3796
|
}) {
|
|
5992
|
-
const groupRef =
|
|
5993
|
-
const angleRef =
|
|
3797
|
+
const groupRef = React7.useRef(null);
|
|
3798
|
+
const angleRef = React7.useRef(Math.random() * Math.PI * 2);
|
|
5994
3799
|
fiber.useFrame((_, delta) => {
|
|
5995
3800
|
angleRef.current += delta * speed;
|
|
5996
3801
|
if (!groupRef.current) return;
|
|
@@ -6121,9 +3926,9 @@ var Avl3DOrbitalScene = ({
|
|
|
6121
3926
|
highlightedTrait,
|
|
6122
3927
|
onTraitHighlight
|
|
6123
3928
|
}) => {
|
|
6124
|
-
const [localHighlight, setLocalHighlight] =
|
|
3929
|
+
const [localHighlight, setLocalHighlight] = React7.useState(null);
|
|
6125
3930
|
const activeHighlight = highlightedTrait ?? localHighlight;
|
|
6126
|
-
const traitOrbits =
|
|
3931
|
+
const traitOrbits = React7.useMemo(() => {
|
|
6127
3932
|
return data.traits.map((trait, i) => ({
|
|
6128
3933
|
name: trait.name,
|
|
6129
3934
|
radius: 3 + i * 1.2,
|
|
@@ -6134,7 +3939,7 @@ var Avl3DOrbitalScene = ({
|
|
|
6134
3939
|
transitionCount: trait.transitionCount
|
|
6135
3940
|
}));
|
|
6136
3941
|
}, [data.traits]);
|
|
6137
|
-
const pagePositions =
|
|
3942
|
+
const pagePositions = React7.useMemo(() => {
|
|
6138
3943
|
const outerRadius = 3 + data.traits.length * 1.2 + 1.5;
|
|
6139
3944
|
return data.pages.map((_, i) => {
|
|
6140
3945
|
const angle = 2 * Math.PI * i / data.pages.length - Math.PI / 2;
|
|
@@ -6198,12 +4003,12 @@ var Avl3DStateNode = ({
|
|
|
6198
4003
|
outgoingCount = 0,
|
|
6199
4004
|
onClick
|
|
6200
4005
|
}) => {
|
|
6201
|
-
const [hovered, setHovered] =
|
|
4006
|
+
const [hovered, setHovered] = React7.useState(false);
|
|
6202
4007
|
const baseColor = active ? AVL_3D_COLORS.stateActive : AVL_3D_COLORS.stateIdle;
|
|
6203
4008
|
const emissiveColor = active ? AVL_3D_COLORS.stateActive : AVL_3D_COLORS.stateEdge;
|
|
6204
4009
|
const emissiveIntensity = active ? 2 : hovered ? 0.8 : 0.3;
|
|
6205
4010
|
const targetScale = hovered ? 1.08 : 1;
|
|
6206
|
-
const currentScale =
|
|
4011
|
+
const currentScale = React7.useRef(1);
|
|
6207
4012
|
fiber.useFrame((_, delta) => {
|
|
6208
4013
|
currentScale.current = THREE6.MathUtils.damp(currentScale.current, targetScale, 6, delta);
|
|
6209
4014
|
});
|
|
@@ -6377,8 +4182,8 @@ var Avl3DTransitionArc = ({
|
|
|
6377
4182
|
onClick,
|
|
6378
4183
|
color = AVL_3D_COLORS.transitionArc
|
|
6379
4184
|
}) => {
|
|
6380
|
-
const [hovered, setHovered] =
|
|
6381
|
-
const { tubeArgs, labelPos, guardPos, arrowPos, arrowQuat, effectPositions } =
|
|
4185
|
+
const [hovered, setHovered] = React7.useState(false);
|
|
4186
|
+
const { tubeArgs, labelPos, guardPos, arrowPos, arrowQuat, effectPositions } = React7.useMemo(() => {
|
|
6382
4187
|
const offset = 1.5 + index * 0.8;
|
|
6383
4188
|
const curve = isSelf ? selfLoopCurve3D(from, 1.2) : arcCurve3D(from, to, offset);
|
|
6384
4189
|
const mid = curve.getPoint(0.5);
|
|
@@ -6511,7 +4316,7 @@ var Avl3DTraitScene = ({
|
|
|
6511
4316
|
data,
|
|
6512
4317
|
onTransitionClick
|
|
6513
4318
|
}) => {
|
|
6514
|
-
const statePositions =
|
|
4319
|
+
const statePositions = React7.useMemo(() => {
|
|
6515
4320
|
const radius = Math.max(3, data.states.length * 0.8);
|
|
6516
4321
|
const positions = fibonacciSpherePositions(data.states.length, radius);
|
|
6517
4322
|
const posMap = /* @__PURE__ */ new Map();
|
|
@@ -6521,7 +4326,7 @@ var Avl3DTraitScene = ({
|
|
|
6521
4326
|
});
|
|
6522
4327
|
return posMap;
|
|
6523
4328
|
}, [data.states]);
|
|
6524
|
-
const transitionIndices =
|
|
4329
|
+
const transitionIndices = React7.useMemo(() => {
|
|
6525
4330
|
const pairCount = /* @__PURE__ */ new Map();
|
|
6526
4331
|
return data.transitions.map((t) => {
|
|
6527
4332
|
const key = `${t.from}->${t.to}`;
|
|
@@ -6530,7 +4335,7 @@ var Avl3DTraitScene = ({
|
|
|
6530
4335
|
return idx;
|
|
6531
4336
|
});
|
|
6532
4337
|
}, [data.transitions]);
|
|
6533
|
-
const transitionCounts =
|
|
4338
|
+
const transitionCounts = React7.useMemo(() => {
|
|
6534
4339
|
const incoming = /* @__PURE__ */ new Map();
|
|
6535
4340
|
const outgoing = /* @__PURE__ */ new Map();
|
|
6536
4341
|
for (const t of data.transitions) {
|
|
@@ -6640,11 +4445,11 @@ var Avl3DExprTree = ({
|
|
|
6640
4445
|
expression,
|
|
6641
4446
|
position
|
|
6642
4447
|
}) => {
|
|
6643
|
-
const layoutResults =
|
|
4448
|
+
const layoutResults = React7.useMemo(() => {
|
|
6644
4449
|
const origin = { x: position[0], y: position[1], z: position[2] };
|
|
6645
4450
|
return treeLayout3D(expression, origin, 2.5, 1.5);
|
|
6646
4451
|
}, [expression, position]);
|
|
6647
|
-
const edges =
|
|
4452
|
+
const edges = React7.useMemo(() => {
|
|
6648
4453
|
const result = [];
|
|
6649
4454
|
function collectEdges(node, parentPos) {
|
|
6650
4455
|
const entry = layoutResults.find((r) => r.node === node);
|
|
@@ -6924,7 +4729,7 @@ var Avl3DEffects = ({
|
|
|
6924
4729
|
const dof = DOF_CONFIG[level];
|
|
6925
4730
|
const sparkles = SPARKLE_CONFIGS[level];
|
|
6926
4731
|
const stars = STARS_CONFIG[level];
|
|
6927
|
-
const starsKey =
|
|
4732
|
+
const starsKey = React7.useMemo(() => `stars-${level}`, [level]);
|
|
6928
4733
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6929
4734
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6930
4735
|
drei.Stars,
|
|
@@ -6988,16 +4793,16 @@ var DEFAULT_CONFIG2 = {
|
|
|
6988
4793
|
modelOverrides: {},
|
|
6989
4794
|
effectsEnabled: true
|
|
6990
4795
|
};
|
|
6991
|
-
var Avl3DContext =
|
|
4796
|
+
var Avl3DContext = React7.createContext(DEFAULT_CONFIG2);
|
|
6992
4797
|
function useAvl3DConfig() {
|
|
6993
|
-
return
|
|
4798
|
+
return React7.useContext(Avl3DContext);
|
|
6994
4799
|
}
|
|
6995
|
-
function
|
|
4800
|
+
function CameraController({ targetPosition, targetLookAt, animated }) {
|
|
6996
4801
|
const { camera } = fiber.useThree();
|
|
6997
|
-
const targetPosVec =
|
|
6998
|
-
const targetLookVec =
|
|
6999
|
-
const isAnimating =
|
|
7000
|
-
|
|
4802
|
+
const targetPosVec = React7.useRef(new THREE6__namespace.Vector3(...targetPosition));
|
|
4803
|
+
const targetLookVec = React7.useRef(new THREE6__namespace.Vector3(...targetLookAt));
|
|
4804
|
+
const isAnimating = React7.useRef(false);
|
|
4805
|
+
React7.useEffect(() => {
|
|
7001
4806
|
const newTarget = new THREE6__namespace.Vector3(...targetPosition);
|
|
7002
4807
|
const newLookAt = new THREE6__namespace.Vector3(...targetLookAt);
|
|
7003
4808
|
if (!newTarget.equals(targetPosVec.current) || !newLookAt.equals(targetLookVec.current)) {
|
|
@@ -7026,8 +4831,8 @@ function CameraController2({ targetPosition, targetLookAt, animated }) {
|
|
|
7026
4831
|
return null;
|
|
7027
4832
|
}
|
|
7028
4833
|
function SceneFade({ animating, children }) {
|
|
7029
|
-
const groupRef =
|
|
7030
|
-
const opacityRef =
|
|
4834
|
+
const groupRef = React7.useRef(null);
|
|
4835
|
+
const opacityRef = React7.useRef(1);
|
|
7031
4836
|
fiber.useFrame((_, delta) => {
|
|
7032
4837
|
if (!groupRef.current) return;
|
|
7033
4838
|
const target = animating ? 0 : 1;
|
|
@@ -7051,7 +4856,7 @@ var Avl3DViewer = ({
|
|
|
7051
4856
|
modelOverrides = {}
|
|
7052
4857
|
}) => {
|
|
7053
4858
|
const { t } = hooks.useTranslate();
|
|
7054
|
-
const schema =
|
|
4859
|
+
const schema = React7.useMemo(() => {
|
|
7055
4860
|
if (typeof schemaProp === "string") {
|
|
7056
4861
|
try {
|
|
7057
4862
|
return JSON.parse(schemaProp);
|
|
@@ -7061,12 +4866,12 @@ var Avl3DViewer = ({
|
|
|
7061
4866
|
}
|
|
7062
4867
|
return schemaProp;
|
|
7063
4868
|
}, [schemaProp]);
|
|
7064
|
-
const configValue =
|
|
4869
|
+
const configValue = React7.useMemo(() => ({
|
|
7065
4870
|
modelOverrides,
|
|
7066
4871
|
effectsEnabled: effects
|
|
7067
4872
|
}), [modelOverrides, effects]);
|
|
7068
|
-
const [state, dispatch] =
|
|
7069
|
-
|
|
4873
|
+
const [state, dispatch] = React7.useReducer(zoomReducer, initialZoomState);
|
|
4874
|
+
React7.useEffect(() => {
|
|
7070
4875
|
if (initialOrbital) {
|
|
7071
4876
|
dispatch({ type: "ZOOM_INTO_ORBITAL", orbital: initialOrbital, targetPosition: { x: 0, y: 0 } });
|
|
7072
4877
|
setTimeout(() => dispatch({ type: "ANIMATION_COMPLETE" }), 0);
|
|
@@ -7078,35 +4883,35 @@ var Avl3DViewer = ({
|
|
|
7078
4883
|
}
|
|
7079
4884
|
}
|
|
7080
4885
|
}, [initialOrbital, initialTrait]);
|
|
7081
|
-
|
|
4886
|
+
React7.useEffect(() => {
|
|
7082
4887
|
onZoomChange?.(state.level, {
|
|
7083
4888
|
orbital: state.selectedOrbital ?? void 0,
|
|
7084
4889
|
trait: state.selectedTrait ?? void 0
|
|
7085
4890
|
});
|
|
7086
4891
|
}, [state.level, state.selectedOrbital, state.selectedTrait, onZoomChange]);
|
|
7087
|
-
|
|
4892
|
+
React7.useEffect(() => {
|
|
7088
4893
|
if (!state.animating) return;
|
|
7089
4894
|
const timer = setTimeout(() => {
|
|
7090
4895
|
dispatch({ type: "ANIMATION_COMPLETE" });
|
|
7091
4896
|
}, 800);
|
|
7092
4897
|
return () => clearTimeout(timer);
|
|
7093
4898
|
}, [state.animating]);
|
|
7094
|
-
const handleKeyDown =
|
|
4899
|
+
const handleKeyDown = React7.useCallback((e) => {
|
|
7095
4900
|
if (e.key === "Escape") {
|
|
7096
4901
|
dispatch({ type: "ZOOM_OUT" });
|
|
7097
4902
|
}
|
|
7098
4903
|
}, []);
|
|
7099
|
-
const handleOrbitalClick =
|
|
4904
|
+
const handleOrbitalClick = React7.useCallback((name) => {
|
|
7100
4905
|
dispatch({ type: "ZOOM_INTO_ORBITAL", orbital: name, targetPosition: { x: 0, y: 0 } });
|
|
7101
4906
|
}, []);
|
|
7102
|
-
const handleTraitClick =
|
|
4907
|
+
const handleTraitClick = React7.useCallback((name) => {
|
|
7103
4908
|
dispatch({ type: "ZOOM_INTO_TRAIT", trait: name, targetPosition: { x: 0, y: 0 } });
|
|
7104
4909
|
}, []);
|
|
7105
|
-
const [highlightedTrait, setHighlightedTrait] =
|
|
7106
|
-
const handleTransitionClick =
|
|
4910
|
+
const [highlightedTrait, setHighlightedTrait] = React7__default.default.useState(null);
|
|
4911
|
+
const handleTransitionClick = React7.useCallback((index) => {
|
|
7107
4912
|
dispatch({ type: "ZOOM_INTO_TRANSITION", transitionIndex: index, targetPosition: { x: 0, y: 0 } });
|
|
7108
4913
|
}, []);
|
|
7109
|
-
const handleBreadcrumbClick =
|
|
4914
|
+
const handleBreadcrumbClick = React7.useCallback((targetLevel) => {
|
|
7110
4915
|
const levelOrder = ["application", "orbital", "trait", "transition"];
|
|
7111
4916
|
const currentIdx = levelOrder.indexOf(state.level);
|
|
7112
4917
|
const targetIdx = levelOrder.indexOf(targetLevel);
|
|
@@ -7115,7 +4920,7 @@ var Avl3DViewer = ({
|
|
|
7115
4920
|
}
|
|
7116
4921
|
}, [state.level]);
|
|
7117
4922
|
const cameraConfig = CAMERA_POSITIONS[state.level];
|
|
7118
|
-
const sceneContent =
|
|
4923
|
+
const sceneContent = React7.useMemo(() => {
|
|
7119
4924
|
switch (state.level) {
|
|
7120
4925
|
case "application": {
|
|
7121
4926
|
const data = parseApplicationLevel(schema);
|
|
@@ -7189,7 +4994,7 @@ var Avl3DViewer = ({
|
|
|
7189
4994
|
gap: "xs",
|
|
7190
4995
|
align: "center",
|
|
7191
4996
|
className: "absolute top-2 left-2 z-10 bg-surface/80 backdrop-blur rounded-md px-3 py-1.5",
|
|
7192
|
-
children: breadcrumbs.map((crumb, i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4997
|
+
children: breadcrumbs.map((crumb, i) => /* @__PURE__ */ jsxRuntime.jsxs(React7__default.default.Fragment, { children: [
|
|
7193
4998
|
i > 0 && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "muted", className: "mx-1", children: "/" }),
|
|
7194
4999
|
i < breadcrumbs.length - 1 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7195
5000
|
Box,
|
|
@@ -7264,7 +5069,7 @@ var Avl3DViewer = ({
|
|
|
7264
5069
|
}
|
|
7265
5070
|
),
|
|
7266
5071
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7267
|
-
|
|
5072
|
+
CameraController,
|
|
7268
5073
|
{
|
|
7269
5074
|
targetPosition: cameraConfig.position,
|
|
7270
5075
|
targetLookAt: cameraConfig.target,
|
|
@@ -7478,11 +5283,7 @@ exports.CAMERA_POSITIONS = CAMERA_POSITIONS;
|
|
|
7478
5283
|
exports.Camera3D = Camera3D;
|
|
7479
5284
|
exports.Canvas3DErrorBoundary = Canvas3DErrorBoundary;
|
|
7480
5285
|
exports.Canvas3DLoadingState = Canvas3DLoadingState;
|
|
7481
|
-
exports.GameBoard3D = GameBoard3D;
|
|
7482
5286
|
exports.GameCanvas3D = GameCanvas3D;
|
|
7483
|
-
exports.GameCanvas3DBattleTemplate = GameCanvas3DBattleTemplate;
|
|
7484
|
-
exports.GameCanvas3DCastleTemplate = GameCanvas3DCastleTemplate;
|
|
7485
|
-
exports.GameCanvas3DWorldMapTemplate = GameCanvas3DWorldMapTemplate;
|
|
7486
5287
|
exports.Lighting3D = Lighting3D;
|
|
7487
5288
|
exports.ModelLoader = ModelLoader;
|
|
7488
5289
|
exports.Scene3D = Scene3D;
|