@almadar/ui 5.103.0 → 5.105.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 +168 -88
- package/dist/avl/index.css +3 -3
- package/dist/avl/index.js +168 -88
- package/dist/components/core/atoms/AtlasImage.d.ts +2 -2
- package/dist/components/core/atoms/FlipContainer.d.ts +1 -1
- package/dist/components/core/molecules/Card.d.ts +1 -1
- package/dist/components/core/molecules/Chart.d.ts +3 -3
- package/dist/components/core/molecules/DataGrid.d.ts +1 -1
- package/dist/components/core/molecules/DataList.d.ts +1 -1
- package/dist/components/core/molecules/DocPagination.d.ts +8 -1
- package/dist/components/core/molecules/DocSearch.d.ts +8 -1
- package/dist/components/core/molecules/FlipCard.d.ts +1 -1
- package/dist/components/core/molecules/LineChart.d.ts +3 -1
- package/dist/components/core/molecules/MapView.d.ts +18 -1
- package/dist/components/core/molecules/SortableList.d.ts +1 -1
- package/dist/components/core/molecules/TableView.d.ts +1 -1
- package/dist/components/core/organisms/DataTable.d.ts +7 -3
- package/dist/components/core/organisms/debug/RuntimeDebugger.d.ts +10 -1
- package/dist/components/core/organisms/debug/tabs/EntitiesTab.d.ts +2 -1
- package/dist/components/core/organisms/debug/tabs/EventDispatcherTab.d.ts +2 -1
- package/dist/components/core/organisms/debug/tabs/EventFlowTab.d.ts +6 -1
- package/dist/components/core/organisms/debug/tabs/GuardsPanel.d.ts +6 -1
- package/dist/components/core/organisms/debug/tabs/ServerBridgeTab.d.ts +12 -1
- package/dist/components/core/organisms/debug/tabs/TicksTab.d.ts +2 -1
- package/dist/components/core/organisms/debug/tabs/TraitsTab.d.ts +2 -1
- package/dist/components/core/organisms/debug/tabs/TransitionTimeline.d.ts +11 -1
- package/dist/components/core/organisms/debug/tabs/VerificationTab.d.ts +8 -1
- package/dist/components/game/2d/atoms/ActionButton.d.ts +2 -1
- package/dist/components/game/2d/atoms/ChoiceButton.d.ts +2 -1
- package/dist/components/game/2d/atoms/ComboCounter.d.ts +2 -1
- package/dist/components/game/2d/atoms/ControlButton.d.ts +2 -1
- package/dist/components/game/2d/atoms/DamageNumber.d.ts +2 -1
- package/dist/components/game/2d/atoms/DialogueBubble.d.ts +2 -1
- package/dist/components/game/2d/atoms/GameIcon.d.ts +2 -1
- package/dist/components/game/2d/atoms/HealthBar.d.ts +2 -1
- package/dist/components/game/2d/atoms/ItemSlot.d.ts +2 -1
- package/dist/components/game/2d/atoms/MiniMap.d.ts +2 -1
- package/dist/components/game/2d/atoms/ResourceCounter.d.ts +2 -1
- package/dist/components/game/2d/atoms/ScoreDisplay.d.ts +2 -1
- package/dist/components/game/2d/atoms/StatusEffect.d.ts +2 -1
- package/dist/components/game/2d/atoms/TimerDisplay.d.ts +2 -1
- package/dist/components/game/2d/atoms/TurnIndicator.d.ts +2 -1
- package/dist/components/game/2d/atoms/WaypointMarker.d.ts +2 -1
- package/dist/components/game/2d/molecules/Canvas2D.d.ts +6 -5
- package/dist/components/game/2d/molecules/GameHud.d.ts +2 -1
- package/dist/components/game/2d/molecules/GameMenu.d.ts +2 -1
- package/dist/components/game/2d/molecules/InventoryGrid.d.ts +2 -1
- package/dist/components/game/2d/molecules/ResourceBar.d.ts +2 -1
- package/dist/components/game/2d/molecules/SideCanvas2D.d.ts +36 -0
- package/dist/components/game/2d/molecules/StatBadge.d.ts +2 -1
- package/dist/components/game/3d/atoms/EventMarker3D.d.ts +17 -0
- package/dist/components/game/3d/atoms/SpriteBillboard3D.d.ts +18 -0
- package/dist/components/game/3d/index.cjs +539 -496
- package/dist/components/game/3d/index.js +367 -324
- package/dist/components/game/3d/molecules/FeatureMesh3D.d.ts +21 -0
- package/dist/components/game/3d/molecules/GameCamera3D.d.ts +35 -0
- package/dist/components/game/3d/molecules/Lighting3D.d.ts +10 -2
- package/dist/components/game/3d/molecules/SideScene3D.d.ts +31 -0
- package/dist/components/game/3d/molecules/TileMesh3D.d.ts +28 -0
- package/dist/components/game/3d/molecules/UnitMesh3D.d.ts +29 -0
- package/dist/components/game/shared/game3dTheme.d.ts +55 -0
- package/dist/components/game/shared/isometric.d.ts +38 -0
- package/dist/components/game/shared/isometricTypes.d.ts +4 -0
- package/dist/components/game/shared/lib/editorUtils.d.ts +8 -8
- package/dist/components/index.cjs +156 -83
- package/dist/components/index.css +3 -3
- package/dist/components/index.js +160 -87
- package/dist/hooks/useGitHub.d.ts +7 -7
- package/dist/marketing/index.cjs +2 -2
- package/dist/marketing/index.js +2 -2
- package/dist/providers/EventBusProvider.d.ts +1 -1
- package/dist/providers/SelectionProvider.d.ts +1 -1
- package/dist/providers/ServerBridge.d.ts +1 -1
- package/dist/providers/index.cjs +164 -84
- package/dist/providers/index.css +3 -3
- package/dist/providers/index.js +164 -84
- package/dist/runtime/index.cjs +164 -84
- package/dist/runtime/index.css +3 -3
- package/dist/runtime/index.js +164 -84
- package/package.json +10 -15
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var React10 = require('react');
|
|
4
4
|
var fiber = require('@react-three/fiber');
|
|
5
5
|
var THREE6 = require('three');
|
|
6
6
|
var jsxRuntime = require('react/jsx-runtime');
|
|
@@ -36,7 +36,7 @@ function _interopNamespace(e) {
|
|
|
36
36
|
return Object.freeze(n);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
var
|
|
39
|
+
var React10__namespace = /*#__PURE__*/_interopNamespace(React10);
|
|
40
40
|
var THREE6__namespace = /*#__PURE__*/_interopNamespace(THREE6);
|
|
41
41
|
var LucideIcons__namespace = /*#__PURE__*/_interopNamespace(LucideIcons);
|
|
42
42
|
|
|
@@ -45,8 +45,8 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
|
|
|
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 = React10.useRef(false);
|
|
49
|
+
React10.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 = React10.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 = React10.useRef(null);
|
|
84
|
+
const initialPosition = React10.useRef(new THREE6__namespace.Vector3(...position));
|
|
85
|
+
const initialTarget = React10.useRef(new THREE6__namespace.Vector3(...target));
|
|
86
|
+
React10.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
|
+
React10.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 React10.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] = React10.useState(
|
|
375
380
|
() => stateFromEntry(url ? loadGltfCached(url, resourceBasePath || detectAssetRoot(url)) : null)
|
|
376
381
|
);
|
|
377
|
-
|
|
382
|
+
React10.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 = React10.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 = React10.useMemo(() => model ? new THREE6__namespace.AnimationMixer(model) : null, [model]);
|
|
436
|
+
React10.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 = React10.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 = React10.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 = React10.useMemo(() => {
|
|
459
464
|
return [
|
|
460
465
|
rotation[0] * Math.PI / 180,
|
|
461
466
|
rotation[1] * Math.PI / 180,
|
|
@@ -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 = React10.useRef(null);
|
|
744
|
+
const canvasRef = React10.useRef(null);
|
|
745
|
+
const rendererRef = React10.useRef(null);
|
|
746
|
+
const sceneRef = React10.useRef(null);
|
|
747
|
+
const cameraRef = React10.useRef(null);
|
|
748
|
+
const controlsRef = React10.useRef(null);
|
|
749
|
+
const gridHelperRef = React10.useRef(null);
|
|
750
|
+
const rafRef = React10.useRef(0);
|
|
751
|
+
const [isReady, setIsReady] = React10.useState(false);
|
|
752
|
+
const [dimensions, setDimensions] = React10.useState({ width: 0, height: 0 });
|
|
753
|
+
const initialCameraPosition = React10.useMemo(
|
|
749
754
|
() => new THREE6__namespace.Vector3(...opts.cameraPosition),
|
|
750
755
|
[]
|
|
751
756
|
);
|
|
752
|
-
|
|
757
|
+
React10.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
|
+
React10.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 = React10.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 = React10.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 = React10.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 = React10.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 = React10.useRef(customLoader || new AssetLoader());
|
|
940
|
+
const [state, setState] = React10.useState({
|
|
936
941
|
isLoading: false,
|
|
937
942
|
progress: 0,
|
|
938
943
|
loaded: 0,
|
|
939
944
|
total: 0,
|
|
940
945
|
errors: []
|
|
941
946
|
});
|
|
942
|
-
|
|
947
|
+
React10.useEffect(() => {
|
|
943
948
|
if (preloadUrls.length > 0) {
|
|
944
949
|
preload(preloadUrls);
|
|
945
950
|
}
|
|
946
951
|
}, []);
|
|
947
|
-
const updateProgress =
|
|
952
|
+
const updateProgress = React10.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 = React10.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 = React10.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 = React10.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 = React10.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 = React10.useCallback((url) => {
|
|
1064
1069
|
return loaderRef.current.hasModel(url);
|
|
1065
1070
|
}, []);
|
|
1066
|
-
const hasTexture =
|
|
1071
|
+
const hasTexture = React10.useCallback((url) => {
|
|
1067
1072
|
return loaderRef.current.hasTexture(url);
|
|
1068
1073
|
}, []);
|
|
1069
|
-
const getModel =
|
|
1074
|
+
const getModel = React10.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 = React10.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 = React10.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 = React10.useRef(/* @__PURE__ */ new Map());
|
|
1113
|
+
const addNode = React10.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 = React10.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 = React10.useCallback((id) => {
|
|
1123
1128
|
return nodesRef.current.get(id);
|
|
1124
1129
|
}, []);
|
|
1125
|
-
const updateNodePosition =
|
|
1130
|
+
const updateNodePosition = React10.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 = React10.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 = React10.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 = React10.useCallback((type) => {
|
|
1154
1159
|
return Array.from(nodesRef.current.values()).filter((node) => node.type === type);
|
|
1155
1160
|
}, []);
|
|
1156
|
-
const getNodesInBounds =
|
|
1161
|
+
const getNodesInBounds = React10.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 = React10.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 = React10.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 = React10.useRef(new THREE6__namespace.Raycaster());
|
|
1199
|
+
const mouse = React10.useRef(new THREE6__namespace.Vector2());
|
|
1200
|
+
const clientToNDC = React10.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 = React10.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 = React10.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 = React10.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 = React10.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 = React10.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 = React10.useContext(providers.EventBusContext);
|
|
1400
1405
|
const baseBus = context ?? getGlobalEventBus() ?? fallbackEventBus;
|
|
1401
1406
|
const scope = providers.useTraitScope();
|
|
1402
|
-
return
|
|
1407
|
+
return React10.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 React10.useCallback(
|
|
1439
1444
|
(type, payload, source) => {
|
|
1440
1445
|
eventBus.emit(type, payload, source);
|
|
1441
1446
|
},
|
|
@@ -1462,9 +1467,9 @@ function useGameCanvas3DEvents(options) {
|
|
|
1462
1467
|
onUnitAnimation
|
|
1463
1468
|
} = options;
|
|
1464
1469
|
const emit = useEmitEvent();
|
|
1465
|
-
const optionsRef =
|
|
1470
|
+
const optionsRef = React10.useRef(options);
|
|
1466
1471
|
optionsRef.current = options;
|
|
1467
|
-
const handleTileClick =
|
|
1472
|
+
const handleTileClick = React10.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 = React10.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 = React10.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 = React10.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 = React10.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 = React10.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 = React10.useCallback(
|
|
1564
1569
|
(position) => {
|
|
1565
1570
|
if (cameraChangeEvent) {
|
|
1566
1571
|
emit(cameraChangeEvent, {
|
|
@@ -1582,6 +1587,171 @@ function useGameCanvas3DEvents(options) {
|
|
|
1582
1587
|
};
|
|
1583
1588
|
}
|
|
1584
1589
|
|
|
1590
|
+
// components/game/shared/game3dTheme.ts
|
|
1591
|
+
var TERRAIN_COLORS_3D = {
|
|
1592
|
+
base: 8421504,
|
|
1593
|
+
water: 4491468,
|
|
1594
|
+
grass: 4500036,
|
|
1595
|
+
sand: 14535816,
|
|
1596
|
+
rock: 8947848,
|
|
1597
|
+
snow: 15658734
|
|
1598
|
+
};
|
|
1599
|
+
var HIGHLIGHT_EMISSIVE_3D = {
|
|
1600
|
+
selected: 4473924,
|
|
1601
|
+
attackTarget: 4456448,
|
|
1602
|
+
validMove: 17408,
|
|
1603
|
+
hovered: 2236962,
|
|
1604
|
+
none: 0
|
|
1605
|
+
};
|
|
1606
|
+
var FACTION_COLORS_3D = {
|
|
1607
|
+
player: 4491519,
|
|
1608
|
+
enemy: 16729156,
|
|
1609
|
+
default: 16777028
|
|
1610
|
+
};
|
|
1611
|
+
var SELECTION_RING_COLOR_3D = "#ffff00";
|
|
1612
|
+
var HEALTHBAR_3D = {
|
|
1613
|
+
bg: 3355443,
|
|
1614
|
+
/** fill color thresholds: fraction > key → color */
|
|
1615
|
+
high: 4500036,
|
|
1616
|
+
// fraction > 0.5
|
|
1617
|
+
mid: 11184708,
|
|
1618
|
+
// fraction > 0.25
|
|
1619
|
+
low: 16729156,
|
|
1620
|
+
// else
|
|
1621
|
+
width: 0.5
|
|
1622
|
+
};
|
|
1623
|
+
function healthbarFill3D(fraction) {
|
|
1624
|
+
if (fraction > 0.5) return HEALTHBAR_3D.high;
|
|
1625
|
+
if (fraction > 0.25) return HEALTHBAR_3D.mid;
|
|
1626
|
+
return HEALTHBAR_3D.low;
|
|
1627
|
+
}
|
|
1628
|
+
var TREE_COLORS_3D = {
|
|
1629
|
+
trunk: 9127187,
|
|
1630
|
+
foliage: 2263842
|
|
1631
|
+
};
|
|
1632
|
+
var ROCK_COLOR_3D = 8421504;
|
|
1633
|
+
var EVENT_COLORS_3D = {
|
|
1634
|
+
hit: 16733508,
|
|
1635
|
+
damage: 16733508,
|
|
1636
|
+
attack: 16746564,
|
|
1637
|
+
heal: 4513126,
|
|
1638
|
+
pickup: 16763955,
|
|
1639
|
+
score: 16763955,
|
|
1640
|
+
death: 11154227,
|
|
1641
|
+
win: 6750088,
|
|
1642
|
+
lose: 16737894,
|
|
1643
|
+
default: 16777215
|
|
1644
|
+
};
|
|
1645
|
+
var SIDE_PLATFORM_COLORS_3D = {
|
|
1646
|
+
ground: 5999678,
|
|
1647
|
+
platform: 9132587,
|
|
1648
|
+
hazard: 13386820,
|
|
1649
|
+
goal: 4491468,
|
|
1650
|
+
default: 8947848
|
|
1651
|
+
};
|
|
1652
|
+
var SIDE_PLAYER_COLOR_3D = "#4488ff";
|
|
1653
|
+
var GRID_COLORS_3D = {
|
|
1654
|
+
cell: "#444444",
|
|
1655
|
+
section: "#666666"
|
|
1656
|
+
};
|
|
1657
|
+
var DEFAULT_BACKGROUND_3D = "#1a1a2e";
|
|
1658
|
+
function TileMesh3D({
|
|
1659
|
+
tile,
|
|
1660
|
+
position,
|
|
1661
|
+
model,
|
|
1662
|
+
isSelected,
|
|
1663
|
+
isHovered,
|
|
1664
|
+
isValidMove,
|
|
1665
|
+
isAttackTarget,
|
|
1666
|
+
onTileClick,
|
|
1667
|
+
onTileHover
|
|
1668
|
+
}) {
|
|
1669
|
+
const color = tile.color ?? TERRAIN_COLORS_3D[tile.type ?? ""] ?? TERRAIN_COLORS_3D.base;
|
|
1670
|
+
let emissive = HIGHLIGHT_EMISSIVE_3D.none;
|
|
1671
|
+
if (isSelected) emissive = HIGHLIGHT_EMISSIVE_3D.selected;
|
|
1672
|
+
else if (isAttackTarget) emissive = HIGHLIGHT_EMISSIVE_3D.attackTarget;
|
|
1673
|
+
else if (isValidMove) emissive = HIGHLIGHT_EMISSIVE_3D.validMove;
|
|
1674
|
+
else if (isHovered) emissive = HIGHLIGHT_EMISSIVE_3D.hovered;
|
|
1675
|
+
if (model?.url) {
|
|
1676
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1677
|
+
"group",
|
|
1678
|
+
{
|
|
1679
|
+
position,
|
|
1680
|
+
onClick: (e) => onTileClick(tile, e),
|
|
1681
|
+
onPointerEnter: (e) => onTileHover(tile, e),
|
|
1682
|
+
onPointerLeave: (e) => onTileHover(null, e),
|
|
1683
|
+
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
1684
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1685
|
+
ModelLoader,
|
|
1686
|
+
{
|
|
1687
|
+
url: model.url,
|
|
1688
|
+
scale: 0.95,
|
|
1689
|
+
rotation: [0, tile.rotation ?? 0, 0],
|
|
1690
|
+
fallbackGeometry: "box",
|
|
1691
|
+
castShadow: true,
|
|
1692
|
+
receiveShadow: true
|
|
1693
|
+
}
|
|
1694
|
+
)
|
|
1695
|
+
}
|
|
1696
|
+
);
|
|
1697
|
+
}
|
|
1698
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1699
|
+
"mesh",
|
|
1700
|
+
{
|
|
1701
|
+
position,
|
|
1702
|
+
onClick: (e) => onTileClick(tile, e),
|
|
1703
|
+
onPointerEnter: (e) => onTileHover(tile, e),
|
|
1704
|
+
onPointerLeave: (e) => onTileHover(null, e),
|
|
1705
|
+
userData: { type: "tile", tileId: tile.id, gridX: tile.x, gridZ: tile.z ?? tile.y },
|
|
1706
|
+
children: [
|
|
1707
|
+
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [0.95, 0.2, 0.95] }),
|
|
1708
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color, emissive })
|
|
1709
|
+
]
|
|
1710
|
+
}
|
|
1711
|
+
);
|
|
1712
|
+
}
|
|
1713
|
+
function SpriteBillboard3D({
|
|
1714
|
+
sheetUrl,
|
|
1715
|
+
resolveFrame,
|
|
1716
|
+
height = 1.2
|
|
1717
|
+
}) {
|
|
1718
|
+
const texture = fiber.useLoader(THREE6__namespace.TextureLoader, sheetUrl);
|
|
1719
|
+
const meshRef = React10.useRef(null);
|
|
1720
|
+
const matRef = React10.useRef(null);
|
|
1721
|
+
const [aspect, setAspect] = React10.useState(1);
|
|
1722
|
+
fiber.useFrame(() => {
|
|
1723
|
+
const frame = resolveFrame();
|
|
1724
|
+
if (!frame || !texture.image) return;
|
|
1725
|
+
const imgW = texture.image.width;
|
|
1726
|
+
const imgH = texture.image.height;
|
|
1727
|
+
if (!imgW || !imgH) return;
|
|
1728
|
+
texture.repeat.set((frame.flipX ? -1 : 1) * (frame.sw / imgW), frame.sh / imgH);
|
|
1729
|
+
texture.offset.set(
|
|
1730
|
+
frame.flipX ? (frame.sx + frame.sw) / imgW : frame.sx / imgW,
|
|
1731
|
+
1 - (frame.sy + frame.sh) / imgH
|
|
1732
|
+
);
|
|
1733
|
+
texture.magFilter = THREE6__namespace.NearestFilter;
|
|
1734
|
+
texture.minFilter = THREE6__namespace.NearestFilter;
|
|
1735
|
+
texture.needsUpdate = true;
|
|
1736
|
+
const nextAspect = frame.sw / frame.sh;
|
|
1737
|
+
if (Math.abs(nextAspect - aspect) > 1e-3) setAspect(nextAspect);
|
|
1738
|
+
if (matRef.current) matRef.current.needsUpdate = true;
|
|
1739
|
+
});
|
|
1740
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("mesh", { ref: meshRef, position: [0, height / 2, 0], children: [
|
|
1741
|
+
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [height * aspect, height] }),
|
|
1742
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1743
|
+
"meshBasicMaterial",
|
|
1744
|
+
{
|
|
1745
|
+
ref: matRef,
|
|
1746
|
+
map: texture,
|
|
1747
|
+
transparent: true,
|
|
1748
|
+
alphaTest: 0.1,
|
|
1749
|
+
side: THREE6__namespace.DoubleSide
|
|
1750
|
+
}
|
|
1751
|
+
)
|
|
1752
|
+
] });
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1585
1755
|
// components/game/shared/spriteAnimation.ts
|
|
1586
1756
|
function resolveSheetDirection(facing) {
|
|
1587
1757
|
switch (facing) {
|
|
@@ -1617,11 +1787,11 @@ function resolveSheetUrl(atlasUrl, relativeSheetPath) {
|
|
|
1617
1787
|
}
|
|
1618
1788
|
}
|
|
1619
1789
|
function useUnitSpriteAtlas(units) {
|
|
1620
|
-
const atlasCacheRef =
|
|
1621
|
-
const loadingRef =
|
|
1622
|
-
const [pendingCount, setPendingCount] =
|
|
1623
|
-
const [, forceTick] =
|
|
1624
|
-
const atlasUrls =
|
|
1790
|
+
const atlasCacheRef = React10.useRef(/* @__PURE__ */ new Map());
|
|
1791
|
+
const loadingRef = React10.useRef(/* @__PURE__ */ new Set());
|
|
1792
|
+
const [pendingCount, setPendingCount] = React10.useState(0);
|
|
1793
|
+
const [, forceTick] = React10.useState(0);
|
|
1794
|
+
const atlasUrls = React10.useMemo(() => {
|
|
1625
1795
|
const set = /* @__PURE__ */ new Set();
|
|
1626
1796
|
for (const unit of units) {
|
|
1627
1797
|
const url = unitAtlasUrl(unit);
|
|
@@ -1629,7 +1799,7 @@ function useUnitSpriteAtlas(units) {
|
|
|
1629
1799
|
}
|
|
1630
1800
|
return [...set];
|
|
1631
1801
|
}, [units]);
|
|
1632
|
-
|
|
1802
|
+
React10.useEffect(() => {
|
|
1633
1803
|
const cache = atlasCacheRef.current;
|
|
1634
1804
|
const loading = loadingRef.current;
|
|
1635
1805
|
const toLoad = atlasUrls.filter((url) => !cache.has(url) && !loading.has(url));
|
|
@@ -1653,7 +1823,7 @@ function useUnitSpriteAtlas(units) {
|
|
|
1653
1823
|
cancelled = true;
|
|
1654
1824
|
};
|
|
1655
1825
|
}, [atlasUrls]);
|
|
1656
|
-
const sheetUrls =
|
|
1826
|
+
const sheetUrls = React10.useMemo(() => {
|
|
1657
1827
|
const urls = /* @__PURE__ */ new Set();
|
|
1658
1828
|
for (const unit of units) {
|
|
1659
1829
|
const atlasUrl = unitAtlasUrl(unit);
|
|
@@ -1666,7 +1836,7 @@ function useUnitSpriteAtlas(units) {
|
|
|
1666
1836
|
}
|
|
1667
1837
|
return [...urls];
|
|
1668
1838
|
}, [units, pendingCount]);
|
|
1669
|
-
const resolveUnitFrame =
|
|
1839
|
+
const resolveUnitFrame = React10.useCallback((unitId) => {
|
|
1670
1840
|
const unit = units.find((u) => u.id === unitId);
|
|
1671
1841
|
if (!unit) return null;
|
|
1672
1842
|
const atlasUrl = unitAtlasUrl(unit);
|
|
@@ -1694,274 +1864,11 @@ function useUnitSpriteAtlas(units) {
|
|
|
1694
1864
|
}, [units]);
|
|
1695
1865
|
return { sheetUrls, resolveUnitFrame, pendingCount };
|
|
1696
1866
|
}
|
|
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({
|
|
1706
|
-
onCameraChange
|
|
1707
|
-
}) {
|
|
1708
|
-
const { camera } = fiber.useThree();
|
|
1709
|
-
React8.useEffect(() => {
|
|
1710
|
-
if (onCameraChange) {
|
|
1711
|
-
onCameraChange({
|
|
1712
|
-
x: camera.position.x,
|
|
1713
|
-
y: camera.position.y,
|
|
1714
|
-
z: camera.position.z
|
|
1715
|
-
});
|
|
1716
|
-
}
|
|
1717
|
-
}, [camera.position, onCameraChange]);
|
|
1718
|
-
return null;
|
|
1719
|
-
}
|
|
1720
|
-
function UnitSpriteBillboard({
|
|
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({
|
|
1762
|
-
target,
|
|
1763
|
-
offset
|
|
1764
|
-
}) {
|
|
1765
|
-
const { camera } = fiber.useThree();
|
|
1766
|
-
const look = React8.useRef(new THREE6__namespace.Vector3(target[0], target[1], target[2]));
|
|
1767
|
-
const goal = React8.useRef(new THREE6__namespace.Vector3());
|
|
1768
|
-
fiber.useFrame((_, delta) => {
|
|
1769
|
-
const t = Math.min(1, delta * 5);
|
|
1770
|
-
goal.current.set(target[0] + offset[0], target[1] + offset[1], target[2] + offset[2]);
|
|
1771
|
-
camera.position.lerp(goal.current, t);
|
|
1772
|
-
look.current.lerp(goal.current.set(target[0], target[1], target[2]), t);
|
|
1773
|
-
camera.lookAt(look.current);
|
|
1774
|
-
});
|
|
1775
|
-
return null;
|
|
1776
|
-
}
|
|
1777
|
-
function LerpedGroup({
|
|
1778
|
-
target,
|
|
1779
|
-
enabled,
|
|
1780
|
-
children
|
|
1781
|
-
}) {
|
|
1782
|
-
const ref = React8.useRef(null);
|
|
1783
|
-
const goal = React8.useRef(new THREE6__namespace.Vector3());
|
|
1784
|
-
fiber.useFrame((_, delta) => {
|
|
1785
|
-
const g = ref.current;
|
|
1786
|
-
if (!g) return;
|
|
1787
|
-
goal.current.set(target[0], target[1], target[2]);
|
|
1788
|
-
if (enabled) g.position.lerp(goal.current, Math.min(1, delta * 10));
|
|
1789
|
-
else g.position.copy(goal.current);
|
|
1790
|
-
});
|
|
1791
|
-
return /* @__PURE__ */ jsxRuntime.jsx("group", { ref, position: target, children });
|
|
1792
|
-
}
|
|
1793
|
-
var SIDE_PLATFORM_COLORS = {
|
|
1794
|
-
ground: "#5b8c3e",
|
|
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,
|
|
1850
|
-
{
|
|
1851
|
-
event: ev,
|
|
1852
|
-
position: [ev.x / ppu, (worldHeight - (ev.y ?? 0)) / ppu + 0.8, 0.2]
|
|
1853
|
-
},
|
|
1854
|
-
ev.id ?? `sev-${i}`
|
|
1855
|
-
)),
|
|
1856
|
-
/* @__PURE__ */ jsxRuntime.jsx(LerpedGroup, { target: playerPos, enabled: interpolate, children: playerSprite?.url ? /* @__PURE__ */ jsxRuntime.jsx("group", { position: [0, playerH / 2, 0], children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1857
|
-
ModelLoader,
|
|
1858
|
-
{
|
|
1859
|
-
url: playerSprite.url,
|
|
1860
|
-
scale: playerH,
|
|
1861
|
-
rotation: [0, player.facingRight ? 90 : -90, 0],
|
|
1862
|
-
animation: player.animation ?? "idle",
|
|
1863
|
-
fallbackGeometry: "box",
|
|
1864
|
-
castShadow: true
|
|
1865
|
-
}
|
|
1866
|
-
) }) : /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, playerH / 2, 0], children: [
|
|
1867
|
-
/* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [playerH * 0.25, playerH * 0.5, 4, 8] }),
|
|
1868
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: "#4488ff" })
|
|
1869
|
-
] }) })
|
|
1870
|
-
] });
|
|
1871
|
-
}
|
|
1872
1867
|
var UNIT_BASE_MODEL_SCALE = 0.5;
|
|
1873
1868
|
var UNIT_BASE_BILLBOARD_HEIGHT = 1.2;
|
|
1874
1869
|
var UNIT_BASE_PRIMITIVE_RADIUS = 0.3;
|
|
1875
|
-
var
|
|
1876
|
-
|
|
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(
|
|
1892
|
-
drei.Text,
|
|
1893
|
-
{
|
|
1894
|
-
fontSize: 0.32,
|
|
1895
|
-
color: EVENT_COLORS[event.type] ?? "#ffffff",
|
|
1896
|
-
outlineWidth: 0.02,
|
|
1897
|
-
outlineColor: "#000000",
|
|
1898
|
-
anchorX: "center",
|
|
1899
|
-
anchorY: "middle",
|
|
1900
|
-
children: event.message ?? event.type
|
|
1901
|
-
}
|
|
1902
|
-
) });
|
|
1903
|
-
}
|
|
1904
|
-
function DefaultTile({
|
|
1905
|
-
tile,
|
|
1906
|
-
position,
|
|
1907
|
-
model,
|
|
1908
|
-
isSelected,
|
|
1909
|
-
isHovered,
|
|
1910
|
-
isValidMove,
|
|
1911
|
-
isAttackTarget,
|
|
1912
|
-
onTileClick,
|
|
1913
|
-
onTileHover
|
|
1914
|
-
}) {
|
|
1915
|
-
let color = 8421504;
|
|
1916
|
-
if (tile.type === "water") color = 4491468;
|
|
1917
|
-
else if (tile.type === "grass") color = 4500036;
|
|
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
|
-
);
|
|
1948
|
-
}
|
|
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
|
-
}
|
|
1964
|
-
function DefaultUnit({
|
|
1870
|
+
var HEALTHBAR_HEIGHT = 0.05;
|
|
1871
|
+
function UnitMesh3D({
|
|
1965
1872
|
unit,
|
|
1966
1873
|
position,
|
|
1967
1874
|
model,
|
|
@@ -1971,7 +1878,7 @@ function DefaultUnit({
|
|
|
1971
1878
|
resolveUnitFrame,
|
|
1972
1879
|
onUnitClick
|
|
1973
1880
|
}) {
|
|
1974
|
-
const color = unit.
|
|
1881
|
+
const color = unit.tint ?? FACTION_COLORS_3D[unit.faction ?? ""] ?? FACTION_COLORS_3D.default;
|
|
1975
1882
|
const hasAtlas = unitAtlasUrl(unit) !== null;
|
|
1976
1883
|
const initialFrame = hasAtlas ? resolveUnitFrame(unit.id) : null;
|
|
1977
1884
|
const modelScale = UNIT_BASE_MODEL_SCALE * unitScale * cellSize;
|
|
@@ -1986,12 +1893,12 @@ function DefaultUnit({
|
|
|
1986
1893
|
children: [
|
|
1987
1894
|
isSelected && /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.05, 0], rotation: [-Math.PI / 2, 0, 0], children: [
|
|
1988
1895
|
/* @__PURE__ */ jsxRuntime.jsx("ringGeometry", { args: [0.4, 0.5, 32] }),
|
|
1989
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color:
|
|
1896
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: SELECTION_RING_COLOR_3D, transparent: true, opacity: 0.8 })
|
|
1990
1897
|
] }),
|
|
1991
1898
|
hasAtlas && initialFrame ? (
|
|
1992
1899
|
/* Animated sprite-sheet billboard — single cropped frame, by state */
|
|
1993
1900
|
/* @__PURE__ */ jsxRuntime.jsx(drei.Billboard, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1994
|
-
|
|
1901
|
+
SpriteBillboard3D,
|
|
1995
1902
|
{
|
|
1996
1903
|
sheetUrl: initialFrame.sheetUrl,
|
|
1997
1904
|
resolveFrame: () => resolveUnitFrame(unit.id),
|
|
@@ -2027,26 +1934,21 @@ function DefaultUnit({
|
|
|
2027
1934
|
] })
|
|
2028
1935
|
] }),
|
|
2029
1936
|
unit.health !== void 0 && unit.maxHealth !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("group", { position: [0, billboardHeight, 0], children: [
|
|
2030
|
-
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [-0.
|
|
2031
|
-
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [
|
|
2032
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color:
|
|
1937
|
+
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [-0.5 / 2, 0, 0], children: [
|
|
1938
|
+
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [HEALTHBAR_3D.width, HEALTHBAR_HEIGHT] }),
|
|
1939
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: HEALTHBAR_3D.bg })
|
|
2033
1940
|
] }),
|
|
2034
1941
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2035
1942
|
"mesh",
|
|
2036
1943
|
{
|
|
2037
1944
|
position: [
|
|
2038
|
-
-0.
|
|
1945
|
+
-0.5 / 2 + HEALTHBAR_3D.width * (unit.health / unit.maxHealth) / 2,
|
|
2039
1946
|
0,
|
|
2040
1947
|
0.01
|
|
2041
1948
|
],
|
|
2042
1949
|
children: [
|
|
2043
|
-
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [
|
|
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
|
-
)
|
|
1950
|
+
/* @__PURE__ */ jsxRuntime.jsx("planeGeometry", { args: [HEALTHBAR_3D.width * (unit.health / unit.maxHealth), HEALTHBAR_HEIGHT] }),
|
|
1951
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshBasicMaterial", { color: healthbarFill3D(unit.health / unit.maxHealth) })
|
|
2050
1952
|
]
|
|
2051
1953
|
}
|
|
2052
1954
|
)
|
|
@@ -2055,7 +1957,7 @@ function DefaultUnit({
|
|
|
2055
1957
|
}
|
|
2056
1958
|
);
|
|
2057
1959
|
}
|
|
2058
|
-
function
|
|
1960
|
+
function FeatureMesh3D({
|
|
2059
1961
|
feature,
|
|
2060
1962
|
position,
|
|
2061
1963
|
model,
|
|
@@ -2085,11 +1987,11 @@ function DefaultFeature({
|
|
|
2085
1987
|
children: [
|
|
2086
1988
|
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.4, 0], children: [
|
|
2087
1989
|
/* @__PURE__ */ jsxRuntime.jsx("cylinderGeometry", { args: [0.1, 0.15, 0.8, 6] }),
|
|
2088
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color:
|
|
1990
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: TREE_COLORS_3D.trunk })
|
|
2089
1991
|
] }),
|
|
2090
1992
|
/* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, 0.9, 0], children: [
|
|
2091
1993
|
/* @__PURE__ */ jsxRuntime.jsx("coneGeometry", { args: [0.5, 0.8, 8] }),
|
|
2092
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color:
|
|
1994
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: TREE_COLORS_3D.foliage })
|
|
2093
1995
|
] })
|
|
2094
1996
|
]
|
|
2095
1997
|
}
|
|
@@ -2104,14 +2006,157 @@ function DefaultFeature({
|
|
|
2104
2006
|
userData: { type: "feature", featureId: feature.id },
|
|
2105
2007
|
children: [
|
|
2106
2008
|
/* @__PURE__ */ jsxRuntime.jsx("dodecahedronGeometry", { args: [0.3, 0] }),
|
|
2107
|
-
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color:
|
|
2009
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: ROCK_COLOR_3D })
|
|
2108
2010
|
]
|
|
2109
2011
|
}
|
|
2110
2012
|
);
|
|
2111
2013
|
}
|
|
2112
2014
|
return null;
|
|
2113
2015
|
}
|
|
2114
|
-
|
|
2016
|
+
function CameraController3D({
|
|
2017
|
+
onCameraChange
|
|
2018
|
+
}) {
|
|
2019
|
+
const { camera } = fiber.useThree();
|
|
2020
|
+
React10.useEffect(() => {
|
|
2021
|
+
if (onCameraChange) {
|
|
2022
|
+
onCameraChange({
|
|
2023
|
+
x: camera.position.x,
|
|
2024
|
+
y: camera.position.y,
|
|
2025
|
+
z: camera.position.z
|
|
2026
|
+
});
|
|
2027
|
+
}
|
|
2028
|
+
}, [camera.position, onCameraChange]);
|
|
2029
|
+
return null;
|
|
2030
|
+
}
|
|
2031
|
+
function FollowCamera3D({
|
|
2032
|
+
target,
|
|
2033
|
+
offset
|
|
2034
|
+
}) {
|
|
2035
|
+
const { camera } = fiber.useThree();
|
|
2036
|
+
const look = React10.useRef(new THREE6__namespace.Vector3(target[0], target[1], target[2]));
|
|
2037
|
+
const goal = React10.useRef(new THREE6__namespace.Vector3());
|
|
2038
|
+
fiber.useFrame((_, delta) => {
|
|
2039
|
+
const t = Math.min(1, delta * 5);
|
|
2040
|
+
goal.current.set(target[0] + offset[0], target[1] + offset[1], target[2] + offset[2]);
|
|
2041
|
+
camera.position.lerp(goal.current, t);
|
|
2042
|
+
look.current.lerp(goal.current.set(target[0], target[1], target[2]), t);
|
|
2043
|
+
camera.lookAt(look.current);
|
|
2044
|
+
});
|
|
2045
|
+
return null;
|
|
2046
|
+
}
|
|
2047
|
+
function LerpedGroup3D({
|
|
2048
|
+
target,
|
|
2049
|
+
enabled,
|
|
2050
|
+
children
|
|
2051
|
+
}) {
|
|
2052
|
+
const ref = React10.useRef(null);
|
|
2053
|
+
const goal = React10.useRef(new THREE6__namespace.Vector3());
|
|
2054
|
+
fiber.useFrame((_, delta) => {
|
|
2055
|
+
const g = ref.current;
|
|
2056
|
+
if (!g) return;
|
|
2057
|
+
goal.current.set(target[0], target[1], target[2]);
|
|
2058
|
+
if (enabled) g.position.lerp(goal.current, Math.min(1, delta * 10));
|
|
2059
|
+
else g.position.copy(goal.current);
|
|
2060
|
+
});
|
|
2061
|
+
return /* @__PURE__ */ jsxRuntime.jsx("group", { ref, position: target, children });
|
|
2062
|
+
}
|
|
2063
|
+
function EventMarker3D({ event, position }) {
|
|
2064
|
+
return /* @__PURE__ */ jsxRuntime.jsx(drei.Billboard, { position, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2065
|
+
drei.Text,
|
|
2066
|
+
{
|
|
2067
|
+
fontSize: 0.32,
|
|
2068
|
+
color: EVENT_COLORS_3D[event.type] ?? EVENT_COLORS_3D.default,
|
|
2069
|
+
outlineWidth: 0.02,
|
|
2070
|
+
outlineColor: "#000000",
|
|
2071
|
+
anchorX: "center",
|
|
2072
|
+
anchorY: "middle",
|
|
2073
|
+
children: event.message ?? event.type
|
|
2074
|
+
}
|
|
2075
|
+
) });
|
|
2076
|
+
}
|
|
2077
|
+
function SideScene3D({
|
|
2078
|
+
player,
|
|
2079
|
+
platforms,
|
|
2080
|
+
worldHeight,
|
|
2081
|
+
ppu,
|
|
2082
|
+
playerSprite,
|
|
2083
|
+
tileSprites,
|
|
2084
|
+
interpolate,
|
|
2085
|
+
features = [],
|
|
2086
|
+
events = [],
|
|
2087
|
+
assetManifest
|
|
2088
|
+
}) {
|
|
2089
|
+
const pw = player.width ?? 32;
|
|
2090
|
+
const ph = player.height ?? 48;
|
|
2091
|
+
const playerPos = [
|
|
2092
|
+
(player.x + pw / 2) / ppu,
|
|
2093
|
+
(worldHeight - player.y - ph) / ppu,
|
|
2094
|
+
0
|
|
2095
|
+
];
|
|
2096
|
+
const playerH = Math.max(ph / ppu, 0.5);
|
|
2097
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("group", { children: [
|
|
2098
|
+
platforms.map((p, i) => {
|
|
2099
|
+
const platformType = p.type ?? "platform";
|
|
2100
|
+
const model = tileSprites?.[platformType]?.url;
|
|
2101
|
+
const topY = (worldHeight - p.y) / ppu;
|
|
2102
|
+
if (model) {
|
|
2103
|
+
const cells = Math.max(1, Math.round(p.width / ppu));
|
|
2104
|
+
const x0 = p.x / ppu;
|
|
2105
|
+
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}`);
|
|
2106
|
+
}
|
|
2107
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2108
|
+
"mesh",
|
|
2109
|
+
{
|
|
2110
|
+
position: [(p.x + p.width / 2) / ppu, topY - p.height / 2 / ppu, 0],
|
|
2111
|
+
children: [
|
|
2112
|
+
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [p.width / ppu, p.height / ppu, 1] }),
|
|
2113
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: SIDE_PLATFORM_COLORS_3D[platformType] ?? SIDE_PLATFORM_COLORS_3D.default })
|
|
2114
|
+
]
|
|
2115
|
+
},
|
|
2116
|
+
`plat-${i}`
|
|
2117
|
+
);
|
|
2118
|
+
}),
|
|
2119
|
+
features.map((feature, i) => {
|
|
2120
|
+
const model = feature.assetUrl ?? assetManifest?.features?.[feature.type];
|
|
2121
|
+
const fx = feature.x / ppu;
|
|
2122
|
+
const fy = (worldHeight - feature.y) / ppu;
|
|
2123
|
+
if (!model?.url) return null;
|
|
2124
|
+
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}`);
|
|
2125
|
+
}),
|
|
2126
|
+
events.map((ev, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2127
|
+
EventMarker3D,
|
|
2128
|
+
{
|
|
2129
|
+
event: ev,
|
|
2130
|
+
position: [ev.x / ppu, (worldHeight - (ev.y ?? 0)) / ppu + 0.8, 0.2]
|
|
2131
|
+
},
|
|
2132
|
+
ev.id ?? `sev-${i}`
|
|
2133
|
+
)),
|
|
2134
|
+
/* @__PURE__ */ jsxRuntime.jsx(LerpedGroup3D, { target: playerPos, enabled: interpolate, children: playerSprite?.url ? /* @__PURE__ */ jsxRuntime.jsx("group", { position: [0, playerH / 2, 0], children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2135
|
+
ModelLoader,
|
|
2136
|
+
{
|
|
2137
|
+
url: playerSprite.url,
|
|
2138
|
+
scale: playerH,
|
|
2139
|
+
rotation: [0, player.facingRight ? 90 : -90, 0],
|
|
2140
|
+
animation: player.animation ?? "idle",
|
|
2141
|
+
fallbackGeometry: "box",
|
|
2142
|
+
castShadow: true
|
|
2143
|
+
}
|
|
2144
|
+
) }) : /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, playerH / 2, 0], children: [
|
|
2145
|
+
/* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [playerH * 0.25, playerH * 0.5, 4, 8] }),
|
|
2146
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: SIDE_PLAYER_COLOR_3D })
|
|
2147
|
+
] }) })
|
|
2148
|
+
] });
|
|
2149
|
+
}
|
|
2150
|
+
function cn(...inputs) {
|
|
2151
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
2152
|
+
}
|
|
2153
|
+
var DEFAULT_GRID_CONFIG = {
|
|
2154
|
+
cellSize: 1,
|
|
2155
|
+
offsetX: 0,
|
|
2156
|
+
offsetZ: 0
|
|
2157
|
+
};
|
|
2158
|
+
var TILE_TOP_OFFSET = 0.1;
|
|
2159
|
+
var GameCanvas3D = React10.forwardRef(
|
|
2115
2160
|
({
|
|
2116
2161
|
tiles = [],
|
|
2117
2162
|
units = [],
|
|
@@ -2124,7 +2169,7 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2124
2169
|
showTileInfo = false,
|
|
2125
2170
|
overlay = "default",
|
|
2126
2171
|
shadows = true,
|
|
2127
|
-
backgroundColor =
|
|
2172
|
+
backgroundColor = DEFAULT_BACKGROUND_3D,
|
|
2128
2173
|
onTileClick,
|
|
2129
2174
|
onUnitClick,
|
|
2130
2175
|
onFeatureClick,
|
|
@@ -2166,13 +2211,13 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2166
2211
|
interpolateUnits = false,
|
|
2167
2212
|
children
|
|
2168
2213
|
}, ref) => {
|
|
2169
|
-
const containerRef =
|
|
2170
|
-
const controlsRef =
|
|
2171
|
-
const [hoveredTile, setHoveredTile] =
|
|
2172
|
-
const [internalError, setInternalError] =
|
|
2214
|
+
const containerRef = React10.useRef(null);
|
|
2215
|
+
const controlsRef = React10.useRef(null);
|
|
2216
|
+
const [hoveredTile, setHoveredTile] = React10.useState(null);
|
|
2217
|
+
const [internalError, setInternalError] = React10.useState(null);
|
|
2173
2218
|
const eventBus = useEventBus();
|
|
2174
|
-
const keysRef =
|
|
2175
|
-
|
|
2219
|
+
const keysRef = React10.useRef(/* @__PURE__ */ new Set());
|
|
2220
|
+
React10.useEffect(() => {
|
|
2176
2221
|
if (!keyMap && !keyUpMap) return;
|
|
2177
2222
|
const down = (e) => {
|
|
2178
2223
|
if (keysRef.current.has(e.code)) return;
|
|
@@ -2195,7 +2240,7 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2195
2240
|
window.removeEventListener("keyup", up);
|
|
2196
2241
|
};
|
|
2197
2242
|
}, [keyMap, keyUpMap, eventBus]);
|
|
2198
|
-
|
|
2243
|
+
React10.useEffect(() => {
|
|
2199
2244
|
const el = containerRef.current;
|
|
2200
2245
|
if (!el) return;
|
|
2201
2246
|
let node = el;
|
|
@@ -2219,7 +2264,7 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2219
2264
|
}
|
|
2220
2265
|
}, []);
|
|
2221
2266
|
const { sheetUrls: atlasSheetUrls, resolveUnitFrame } = useUnitSpriteAtlas(units);
|
|
2222
|
-
const preloadUrls =
|
|
2267
|
+
const preloadUrls = React10.useMemo(() => [...preloadAssets, ...atlasSheetUrls], [preloadAssets, atlasSheetUrls]);
|
|
2223
2268
|
const { isLoading: assetsLoading, progress, loaded, total } = useAssetLoader({
|
|
2224
2269
|
preloadUrls,
|
|
2225
2270
|
loader: customAssetLoader
|
|
@@ -2240,7 +2285,7 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2240
2285
|
onTileHover,
|
|
2241
2286
|
onUnitAnimation
|
|
2242
2287
|
});
|
|
2243
|
-
const gridBounds =
|
|
2288
|
+
const gridBounds = React10.useMemo(() => {
|
|
2244
2289
|
if (tiles.length === 0) {
|
|
2245
2290
|
return { minX: 0, maxX: 10, minZ: 0, maxZ: 10 };
|
|
2246
2291
|
}
|
|
@@ -2253,14 +2298,14 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2253
2298
|
maxZ: Math.max(...zs)
|
|
2254
2299
|
};
|
|
2255
2300
|
}, [tiles]);
|
|
2256
|
-
const cameraTarget =
|
|
2301
|
+
const cameraTarget = React10.useMemo(() => {
|
|
2257
2302
|
return [
|
|
2258
2303
|
(gridBounds.minX + gridBounds.maxX) / 2,
|
|
2259
2304
|
0,
|
|
2260
2305
|
(gridBounds.minZ + gridBounds.maxZ) / 2
|
|
2261
2306
|
];
|
|
2262
2307
|
}, [gridBounds]);
|
|
2263
|
-
const gridConfig =
|
|
2308
|
+
const gridConfig = React10.useMemo(
|
|
2264
2309
|
() => ({
|
|
2265
2310
|
...DEFAULT_GRID_CONFIG,
|
|
2266
2311
|
offsetX: -(gridBounds.maxX - gridBounds.minX) / 2,
|
|
@@ -2268,7 +2313,7 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2268
2313
|
}),
|
|
2269
2314
|
[gridBounds]
|
|
2270
2315
|
);
|
|
2271
|
-
const gridToWorld2 =
|
|
2316
|
+
const gridToWorld2 = React10.useCallback(
|
|
2272
2317
|
(x, z, y = 0) => {
|
|
2273
2318
|
const worldX = (x - gridBounds.minX) * gridConfig.cellSize;
|
|
2274
2319
|
const worldZ = (z - gridBounds.minZ) * gridConfig.cellSize;
|
|
@@ -2276,7 +2321,7 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2276
2321
|
},
|
|
2277
2322
|
[gridBounds, gridConfig]
|
|
2278
2323
|
);
|
|
2279
|
-
|
|
2324
|
+
React10.useImperativeHandle(ref, () => ({
|
|
2280
2325
|
getCameraPosition: () => {
|
|
2281
2326
|
if (controlsRef.current) {
|
|
2282
2327
|
const pos = controlsRef.current.object.position;
|
|
@@ -2314,19 +2359,19 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2314
2359
|
features
|
|
2315
2360
|
})
|
|
2316
2361
|
}));
|
|
2317
|
-
const handleTileClick =
|
|
2362
|
+
const handleTileClick = React10.useCallback(
|
|
2318
2363
|
(tile, event) => {
|
|
2319
2364
|
eventHandlers.handleTileClick(tile, event);
|
|
2320
2365
|
},
|
|
2321
2366
|
[eventHandlers]
|
|
2322
2367
|
);
|
|
2323
|
-
const handleUnitClick =
|
|
2368
|
+
const handleUnitClick = React10.useCallback(
|
|
2324
2369
|
(unit, event) => {
|
|
2325
2370
|
eventHandlers.handleUnitClick(unit, event);
|
|
2326
2371
|
},
|
|
2327
2372
|
[eventHandlers]
|
|
2328
2373
|
);
|
|
2329
|
-
const handleFeatureClick =
|
|
2374
|
+
const handleFeatureClick = React10.useCallback(
|
|
2330
2375
|
(feature, event) => {
|
|
2331
2376
|
if (event) {
|
|
2332
2377
|
eventHandlers.handleFeatureClick(feature, event);
|
|
@@ -2334,7 +2379,7 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2334
2379
|
},
|
|
2335
2380
|
[eventHandlers]
|
|
2336
2381
|
);
|
|
2337
|
-
const handleTileHover =
|
|
2382
|
+
const handleTileHover = React10.useCallback(
|
|
2338
2383
|
(tile, event) => {
|
|
2339
2384
|
setHoveredTile(tile);
|
|
2340
2385
|
if (event) {
|
|
@@ -2343,7 +2388,7 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2343
2388
|
},
|
|
2344
2389
|
[eventHandlers]
|
|
2345
2390
|
);
|
|
2346
|
-
const cameraConfig =
|
|
2391
|
+
const cameraConfig = React10.useMemo(() => {
|
|
2347
2392
|
const size = Math.max(
|
|
2348
2393
|
gridBounds.maxX - gridBounds.minX,
|
|
2349
2394
|
gridBounds.maxZ - gridBounds.minZ,
|
|
@@ -2377,7 +2422,7 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2377
2422
|
};
|
|
2378
2423
|
}
|
|
2379
2424
|
}, [cameraMode, gridBounds]);
|
|
2380
|
-
const followTarget =
|
|
2425
|
+
const followTarget = React10.useMemo(() => {
|
|
2381
2426
|
if (player) {
|
|
2382
2427
|
return [
|
|
2383
2428
|
(player.x + (player.width ?? 32) / 2) / pixelsPerUnit,
|
|
@@ -2397,7 +2442,7 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2397
2442
|
}
|
|
2398
2443
|
return cameraTarget;
|
|
2399
2444
|
}, [player, pixelsPerUnit, worldHeight, units, selectedUnitId, gridBounds, gridConfig, cameraTarget]);
|
|
2400
|
-
const followOffset =
|
|
2445
|
+
const followOffset = React10.useMemo(() => {
|
|
2401
2446
|
if (cameraMode === "chase") {
|
|
2402
2447
|
const selected = units.find((u) => u.id === selectedUnitId);
|
|
2403
2448
|
const h = selected?.heading ?? 0;
|
|
@@ -2458,27 +2503,25 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2458
2503
|
}
|
|
2459
2504
|
},
|
|
2460
2505
|
children: [
|
|
2461
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2506
|
+
/* @__PURE__ */ jsxRuntime.jsx(CameraController3D, { onCameraChange: eventHandlers.handleCameraChange }),
|
|
2462
2507
|
(cameraMode === "follow" || cameraMode === "chase") && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2463
|
-
|
|
2508
|
+
FollowCamera3D,
|
|
2464
2509
|
{
|
|
2465
2510
|
target: followTarget,
|
|
2466
2511
|
offset: followOffset
|
|
2467
2512
|
}
|
|
2468
2513
|
),
|
|
2469
|
-
/* @__PURE__ */ jsxRuntime.jsx("ambientLight", { intensity: 0.6 }),
|
|
2470
2514
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2471
|
-
|
|
2515
|
+
Lighting3D,
|
|
2472
2516
|
{
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2517
|
+
shadows,
|
|
2518
|
+
shadowBias: -4e-4,
|
|
2519
|
+
shadowNormalBias: 0.04,
|
|
2520
|
+
shadowCameraSize: 5,
|
|
2521
|
+
shadowCameraNear: 0.5,
|
|
2522
|
+
shadowCameraFar: 500
|
|
2479
2523
|
}
|
|
2480
2524
|
),
|
|
2481
|
-
/* @__PURE__ */ jsxRuntime.jsx("hemisphereLight", { intensity: 0.3, color: "#87ceeb", groundColor: "#362d1d" }),
|
|
2482
2525
|
showGrid && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2483
2526
|
drei.Grid,
|
|
2484
2527
|
{
|
|
@@ -2493,10 +2536,10 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2493
2536
|
],
|
|
2494
2537
|
cellSize: 1,
|
|
2495
2538
|
cellThickness: 1,
|
|
2496
|
-
cellColor:
|
|
2539
|
+
cellColor: GRID_COLORS_3D.cell,
|
|
2497
2540
|
sectionSize: 5,
|
|
2498
2541
|
sectionThickness: 1.5,
|
|
2499
|
-
sectionColor:
|
|
2542
|
+
sectionColor: GRID_COLORS_3D.section,
|
|
2500
2543
|
fadeDistance: 50,
|
|
2501
2544
|
fadeStrength: 1
|
|
2502
2545
|
}
|
|
@@ -2504,7 +2547,7 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2504
2547
|
player ? (
|
|
2505
2548
|
/* Side-scroller mode — LOLO-owned pixel space mapped to world units */
|
|
2506
2549
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2507
|
-
|
|
2550
|
+
SideScene3D,
|
|
2508
2551
|
{
|
|
2509
2552
|
player,
|
|
2510
2553
|
platforms,
|
|
@@ -2530,7 +2573,7 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2530
2573
|
return /* @__PURE__ */ jsxRuntime.jsx(CustomTileRenderer, { tile, position }, key);
|
|
2531
2574
|
}
|
|
2532
2575
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2533
|
-
|
|
2576
|
+
TileMesh3D,
|
|
2534
2577
|
{
|
|
2535
2578
|
tile,
|
|
2536
2579
|
position,
|
|
@@ -2556,7 +2599,7 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2556
2599
|
return /* @__PURE__ */ jsxRuntime.jsx(CustomFeatureRenderer, { feature, position }, key);
|
|
2557
2600
|
}
|
|
2558
2601
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2559
|
-
|
|
2602
|
+
FeatureMesh3D,
|
|
2560
2603
|
{
|
|
2561
2604
|
feature,
|
|
2562
2605
|
position,
|
|
@@ -2572,8 +2615,8 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2572
2615
|
unit.z ?? unit.y ?? unit.position?.y ?? 0,
|
|
2573
2616
|
(unit.elevation ?? 0) + TILE_TOP_OFFSET
|
|
2574
2617
|
);
|
|
2575
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2576
|
-
|
|
2618
|
+
return /* @__PURE__ */ jsxRuntime.jsx(LerpedGroup3D, { target: position, enabled: interpolateUnits, children: CustomUnitRenderer ? /* @__PURE__ */ jsxRuntime.jsx(CustomUnitRenderer, { unit, position: [0, 0, 0] }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2619
|
+
UnitMesh3D,
|
|
2577
2620
|
{
|
|
2578
2621
|
unit,
|
|
2579
2622
|
position: [0, 0, 0],
|
|
@@ -2589,7 +2632,7 @@ var GameCanvas3D = React8.forwardRef(
|
|
|
2589
2632
|
events.map((ev, i) => {
|
|
2590
2633
|
const position = gridToWorld2(ev.x, ev.z ?? ev.y ?? 0, 0);
|
|
2591
2634
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2592
|
-
|
|
2635
|
+
EventMarker3D,
|
|
2593
2636
|
{
|
|
2594
2637
|
event: ev,
|
|
2595
2638
|
position: [position[0], position[1] + 1.4, position[2]]
|
|
@@ -2686,7 +2729,7 @@ function getIconFamilyServerSnapshot() {
|
|
|
2686
2729
|
return DEFAULT_FAMILY;
|
|
2687
2730
|
}
|
|
2688
2731
|
function useIconFamily() {
|
|
2689
|
-
return
|
|
2732
|
+
return React10.useSyncExternalStore(
|
|
2690
2733
|
subscribeIconFamily,
|
|
2691
2734
|
getIconFamilySnapshot,
|
|
2692
2735
|
getIconFamilyServerSnapshot
|
|
@@ -2708,7 +2751,7 @@ function loadLib(key, importer) {
|
|
|
2708
2751
|
return p;
|
|
2709
2752
|
}
|
|
2710
2753
|
function lazyFamilyIcon(libKey, importer, pick, fallbackName, family) {
|
|
2711
|
-
const Lazy =
|
|
2754
|
+
const Lazy = React10__namespace.default.lazy(async () => {
|
|
2712
2755
|
const lib = await loadLib(libKey, importer);
|
|
2713
2756
|
const Comp = pick(lib);
|
|
2714
2757
|
if (!Comp) {
|
|
@@ -2718,7 +2761,7 @@ function lazyFamilyIcon(libKey, importer, pick, fallbackName, family) {
|
|
|
2718
2761
|
return { default: Comp };
|
|
2719
2762
|
});
|
|
2720
2763
|
const Wrapped = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2721
|
-
|
|
2764
|
+
React10__namespace.default.Suspense,
|
|
2722
2765
|
{
|
|
2723
2766
|
fallback: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2724
2767
|
"span",
|
|
@@ -3387,7 +3430,7 @@ var Icon = ({
|
|
|
3387
3430
|
const directIcon = typeof icon === "string" ? void 0 : icon;
|
|
3388
3431
|
const effectiveName = typeof icon === "string" ? icon : name;
|
|
3389
3432
|
const family = useIconFamily();
|
|
3390
|
-
const RenderedComponent =
|
|
3433
|
+
const RenderedComponent = React10__namespace.default.useMemo(() => {
|
|
3391
3434
|
if (directIcon) return null;
|
|
3392
3435
|
return effectiveName ? resolveIconForFamily(effectiveName, family) : null;
|
|
3393
3436
|
}, [directIcon, effectiveName, family]);
|
|
@@ -3514,13 +3557,13 @@ function AtlasImage({
|
|
|
3514
3557
|
style,
|
|
3515
3558
|
"aria-hidden": ariaHidden
|
|
3516
3559
|
}) {
|
|
3517
|
-
const [, bump] =
|
|
3518
|
-
const canvasRef =
|
|
3560
|
+
const [, bump] = React10__namespace.useReducer((x) => x + 1, 0);
|
|
3561
|
+
const canvasRef = React10__namespace.useRef(null);
|
|
3519
3562
|
const sliced = isAtlasAsset(asset);
|
|
3520
3563
|
const atlas = sliced ? getAtlas(asset.atlas, bump) : void 0;
|
|
3521
3564
|
const img = sliced && asset?.url ? getSheetImage(asset.url, bump) : null;
|
|
3522
3565
|
const rect = sliced && atlas ? subRectFor(atlas, asset.sprite) : null;
|
|
3523
|
-
|
|
3566
|
+
React10__namespace.useEffect(() => {
|
|
3524
3567
|
const canvas = canvasRef.current;
|
|
3525
3568
|
if (!canvas || !img || !rect) return;
|
|
3526
3569
|
canvas.width = rect.sw;
|
|
@@ -3644,7 +3687,7 @@ function resolveIconProp(value, sizeClass) {
|
|
|
3644
3687
|
const IconComp = value;
|
|
3645
3688
|
return /* @__PURE__ */ jsxRuntime.jsx(IconComp, { className: sizeClass });
|
|
3646
3689
|
}
|
|
3647
|
-
if (
|
|
3690
|
+
if (React10__namespace.default.isValidElement(value)) {
|
|
3648
3691
|
return value;
|
|
3649
3692
|
}
|
|
3650
3693
|
if (typeof value === "object" && value !== null && isIconLike(value)) {
|
|
@@ -3653,7 +3696,7 @@ function resolveIconProp(value, sizeClass) {
|
|
|
3653
3696
|
}
|
|
3654
3697
|
return value;
|
|
3655
3698
|
}
|
|
3656
|
-
var Button =
|
|
3699
|
+
var Button = React10__namespace.default.forwardRef(
|
|
3657
3700
|
({
|
|
3658
3701
|
className,
|
|
3659
3702
|
variant = "primary",
|
|
@@ -3801,7 +3844,7 @@ var Typography = ({
|
|
|
3801
3844
|
}) => {
|
|
3802
3845
|
const variant = variantProp ?? (level ? `h${level}` : "body1");
|
|
3803
3846
|
const Component2 = as || defaultElements[variant];
|
|
3804
|
-
return
|
|
3847
|
+
return React10__namespace.default.createElement(
|
|
3805
3848
|
Component2,
|
|
3806
3849
|
{
|
|
3807
3850
|
id,
|
|
@@ -3874,7 +3917,7 @@ var Stack = ({
|
|
|
3874
3917
|
};
|
|
3875
3918
|
const isHorizontal = direction === "horizontal";
|
|
3876
3919
|
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
|
|
3920
|
+
return React10__namespace.default.createElement(
|
|
3878
3921
|
Component2,
|
|
3879
3922
|
{
|
|
3880
3923
|
className: cn(
|
|
@@ -3967,8 +4010,8 @@ function GameBoard3D({
|
|
|
3967
4010
|
const turn = row ? num(row.turn) : 0;
|
|
3968
4011
|
const currentTeam = row ? str(row.currentTeam) : "player";
|
|
3969
4012
|
const isGameOver = result !== "none";
|
|
3970
|
-
const gameEndEmittedRef =
|
|
3971
|
-
|
|
4013
|
+
const gameEndEmittedRef = React10.useRef(false);
|
|
4014
|
+
React10.useEffect(() => {
|
|
3972
4015
|
if ((result === "victory" || result === "defeat") && gameEndEvent) {
|
|
3973
4016
|
if (!gameEndEmittedRef.current) {
|
|
3974
4017
|
gameEndEmittedRef.current = true;
|
|
@@ -3978,7 +4021,7 @@ function GameBoard3D({
|
|
|
3978
4021
|
gameEndEmittedRef.current = false;
|
|
3979
4022
|
}
|
|
3980
4023
|
}, [result, gameEndEvent, eventBus]);
|
|
3981
|
-
const checkGameEnd =
|
|
4024
|
+
const checkGameEnd = React10.useCallback(() => {
|
|
3982
4025
|
const alivePlayer = entityUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
|
|
3983
4026
|
const aliveEnemy = entityUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
|
|
3984
4027
|
if (alivePlayer.length === 0 && gameEndEvent) {
|
|
@@ -3987,7 +4030,7 @@ function GameBoard3D({
|
|
|
3987
4030
|
eventBus.emit(`UI:${gameEndEvent}`, { result: "victory" });
|
|
3988
4031
|
}
|
|
3989
4032
|
}, [entityUnits, gameEndEvent, eventBus]);
|
|
3990
|
-
const handleUnitClickCallback =
|
|
4033
|
+
const handleUnitClickCallback = React10.useCallback((isoUnit) => {
|
|
3991
4034
|
if (phase !== "action" || !selectedUnitId || !attackEvent) return;
|
|
3992
4035
|
const attackerRow = entityUnits.find((u) => str(u.id) === selectedUnitId);
|
|
3993
4036
|
const targetRow = entityUnits.find((u) => str(u.id) === isoUnit.id);
|
|
@@ -4007,13 +4050,13 @@ function GameBoard3D({
|
|
|
4007
4050
|
setTimeout(checkGameEnd, 100);
|
|
4008
4051
|
}
|
|
4009
4052
|
}, [phase, selectedUnitId, entityUnits, attackEvent, eventBus, checkGameEnd]);
|
|
4010
|
-
const handleEndTurn =
|
|
4053
|
+
const handleEndTurn = React10.useCallback(() => {
|
|
4011
4054
|
if (endTurnEvent) eventBus.emit(`UI:${endTurnEvent}`, {});
|
|
4012
4055
|
}, [endTurnEvent, eventBus]);
|
|
4013
|
-
const handleCancel =
|
|
4056
|
+
const handleCancel = React10.useCallback(() => {
|
|
4014
4057
|
if (cancelEvent) eventBus.emit(`UI:${cancelEvent}`, {});
|
|
4015
4058
|
}, [cancelEvent, eventBus]);
|
|
4016
|
-
const handlePlayAgain =
|
|
4059
|
+
const handlePlayAgain = React10.useCallback(() => {
|
|
4017
4060
|
if (playAgainEvent) eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
4018
4061
|
}, [playAgainEvent, eventBus]);
|
|
4019
4062
|
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("game-board-3d block w-full min-h-[85vh] relative", className), children: [
|
|
@@ -4104,24 +4147,24 @@ function GameBoard3D({
|
|
|
4104
4147
|
GameBoard3D.displayName = "GameBoard3D";
|
|
4105
4148
|
function useTapReveal(options = {}) {
|
|
4106
4149
|
const { onReveal, onDismiss, refs, enabled = true } = options;
|
|
4107
|
-
const [revealed, setRevealed] =
|
|
4108
|
-
const onRevealRef =
|
|
4109
|
-
const onDismissRef =
|
|
4150
|
+
const [revealed, setRevealed] = React10.useState(false);
|
|
4151
|
+
const onRevealRef = React10.useRef(onReveal);
|
|
4152
|
+
const onDismissRef = React10.useRef(onDismiss);
|
|
4110
4153
|
onRevealRef.current = onReveal;
|
|
4111
4154
|
onDismissRef.current = onDismiss;
|
|
4112
|
-
const reveal =
|
|
4155
|
+
const reveal = React10.useCallback(() => {
|
|
4113
4156
|
setRevealed((wasRevealed) => {
|
|
4114
4157
|
if (!wasRevealed) onRevealRef.current?.();
|
|
4115
4158
|
return true;
|
|
4116
4159
|
});
|
|
4117
4160
|
}, []);
|
|
4118
|
-
const dismiss =
|
|
4161
|
+
const dismiss = React10.useCallback(() => {
|
|
4119
4162
|
setRevealed((wasRevealed) => {
|
|
4120
4163
|
if (wasRevealed) onDismissRef.current?.();
|
|
4121
4164
|
return false;
|
|
4122
4165
|
});
|
|
4123
4166
|
}, []);
|
|
4124
|
-
const onPointerDown =
|
|
4167
|
+
const onPointerDown = React10.useCallback(
|
|
4125
4168
|
(e) => {
|
|
4126
4169
|
if (!enabled || e.pointerType === "mouse") return;
|
|
4127
4170
|
if (revealed) dismiss();
|
|
@@ -4129,7 +4172,7 @@ function useTapReveal(options = {}) {
|
|
|
4129
4172
|
},
|
|
4130
4173
|
[enabled, revealed, reveal, dismiss]
|
|
4131
4174
|
);
|
|
4132
|
-
|
|
4175
|
+
React10.useEffect(() => {
|
|
4133
4176
|
if (!revealed || typeof document === "undefined") return;
|
|
4134
4177
|
const onDocDown = (ev) => {
|
|
4135
4178
|
const target = ev.target;
|
|
@@ -4249,7 +4292,7 @@ var positionStyles = {
|
|
|
4249
4292
|
fixed: "fixed",
|
|
4250
4293
|
sticky: "sticky"
|
|
4251
4294
|
};
|
|
4252
|
-
var Box =
|
|
4295
|
+
var Box = React10__namespace.default.forwardRef(
|
|
4253
4296
|
({
|
|
4254
4297
|
padding,
|
|
4255
4298
|
paddingX,
|
|
@@ -4281,20 +4324,20 @@ var Box = React8__namespace.default.forwardRef(
|
|
|
4281
4324
|
...rest
|
|
4282
4325
|
}, ref) => {
|
|
4283
4326
|
const eventBus = useEventBus();
|
|
4284
|
-
const handleClick =
|
|
4327
|
+
const handleClick = React10.useCallback((e) => {
|
|
4285
4328
|
if (action) {
|
|
4286
4329
|
e.stopPropagation();
|
|
4287
4330
|
eventBus.emit(`UI:${action}`, actionPayload ?? {});
|
|
4288
4331
|
}
|
|
4289
4332
|
onClick?.(e);
|
|
4290
4333
|
}, [action, actionPayload, eventBus, onClick]);
|
|
4291
|
-
const handleMouseEnter =
|
|
4334
|
+
const handleMouseEnter = React10.useCallback((e) => {
|
|
4292
4335
|
if (hoverEvent) {
|
|
4293
4336
|
eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
|
|
4294
4337
|
}
|
|
4295
4338
|
onMouseEnter?.(e);
|
|
4296
4339
|
}, [hoverEvent, eventBus, onMouseEnter]);
|
|
4297
|
-
const handleMouseLeave =
|
|
4340
|
+
const handleMouseLeave = React10.useCallback((e) => {
|
|
4298
4341
|
if (hoverEvent) {
|
|
4299
4342
|
eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
|
|
4300
4343
|
}
|
|
@@ -4302,19 +4345,19 @@ var Box = React8__namespace.default.forwardRef(
|
|
|
4302
4345
|
}, [hoverEvent, eventBus, onMouseLeave]);
|
|
4303
4346
|
const { triggerProps } = useTapReveal({
|
|
4304
4347
|
enabled: tapReveal && !!hoverEvent,
|
|
4305
|
-
onReveal:
|
|
4348
|
+
onReveal: React10.useCallback(() => {
|
|
4306
4349
|
if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
|
|
4307
4350
|
}, [hoverEvent, eventBus]),
|
|
4308
|
-
onDismiss:
|
|
4351
|
+
onDismiss: React10.useCallback(() => {
|
|
4309
4352
|
if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
|
|
4310
4353
|
}, [hoverEvent, eventBus])
|
|
4311
4354
|
});
|
|
4312
|
-
const handlePointerDown =
|
|
4355
|
+
const handlePointerDown = React10.useCallback((e) => {
|
|
4313
4356
|
if (hoverEvent && tapReveal) triggerProps.onPointerDown(e);
|
|
4314
4357
|
onPointerDown?.(e);
|
|
4315
4358
|
}, [hoverEvent, tapReveal, triggerProps, onPointerDown]);
|
|
4316
4359
|
const isClickable = action || onClick;
|
|
4317
|
-
return
|
|
4360
|
+
return React10__namespace.default.createElement(
|
|
4318
4361
|
Component2,
|
|
4319
4362
|
{
|
|
4320
4363
|
ref,
|
|
@@ -5554,10 +5597,10 @@ var Avl3DOrbitalNode = ({
|
|
|
5554
5597
|
position,
|
|
5555
5598
|
onClick
|
|
5556
5599
|
}) => {
|
|
5557
|
-
const groupRef =
|
|
5558
|
-
const [hovered, setHovered] =
|
|
5559
|
-
const scale =
|
|
5560
|
-
const currentScale =
|
|
5600
|
+
const groupRef = React10.useRef(null);
|
|
5601
|
+
const [hovered, setHovered] = React10.useState(false);
|
|
5602
|
+
const scale = React10.useMemo(() => 0.6 + Math.min(traitCount, 8) * 0.3, [traitCount]);
|
|
5603
|
+
const currentScale = React10.useRef(scale);
|
|
5561
5604
|
const targetScale = hovered ? scale * 1.06 : scale;
|
|
5562
5605
|
fiber.useFrame((_, delta) => {
|
|
5563
5606
|
if (!groupRef.current) return;
|
|
@@ -5699,7 +5742,7 @@ var Avl3DCrossWire = ({
|
|
|
5699
5742
|
eventName,
|
|
5700
5743
|
color = AVL_3D_COLORS.crossWire
|
|
5701
5744
|
}) => {
|
|
5702
|
-
const { tubeArgs, midpoint } =
|
|
5745
|
+
const { tubeArgs, midpoint } = React10.useMemo(() => {
|
|
5703
5746
|
const curve = arcCurve3D(from, to, 2);
|
|
5704
5747
|
const mid = curve.getPoint(0.5);
|
|
5705
5748
|
return {
|
|
@@ -5738,9 +5781,9 @@ var Avl3DApplicationScene = ({
|
|
|
5738
5781
|
data,
|
|
5739
5782
|
onOrbitalClick
|
|
5740
5783
|
}) => {
|
|
5741
|
-
const groupRef =
|
|
5742
|
-
const [, setHoverTrigger] =
|
|
5743
|
-
const orbitalPositions =
|
|
5784
|
+
const groupRef = React10.useRef(null);
|
|
5785
|
+
const [, setHoverTrigger] = React10.useState(0);
|
|
5786
|
+
const orbitalPositions = React10.useMemo(() => {
|
|
5744
5787
|
const baseRadius = Math.max(4, data.orbitals.length * 1.5);
|
|
5745
5788
|
if (data.crossLinks.length === 0) {
|
|
5746
5789
|
return goldenSpiralPositions(data.orbitals.length, baseRadius);
|
|
@@ -5753,7 +5796,7 @@ var Avl3DApplicationScene = ({
|
|
|
5753
5796
|
})).filter((e) => e.from >= 0 && e.to >= 0);
|
|
5754
5797
|
return forceDirectedPositions(data.orbitals.length, edges, baseRadius);
|
|
5755
5798
|
}, [data.orbitals, data.crossLinks]);
|
|
5756
|
-
const positionByName =
|
|
5799
|
+
const positionByName = React10.useMemo(() => {
|
|
5757
5800
|
const map = /* @__PURE__ */ new Map();
|
|
5758
5801
|
data.orbitals.forEach((orb, i) => {
|
|
5759
5802
|
const pos = orbitalPositions[i];
|
|
@@ -5836,7 +5879,7 @@ var Avl3DEntityCore = ({
|
|
|
5836
5879
|
position,
|
|
5837
5880
|
fields
|
|
5838
5881
|
}) => {
|
|
5839
|
-
const coreRef =
|
|
5882
|
+
const coreRef = React10.useRef(null);
|
|
5840
5883
|
fiber.useFrame((_, delta) => {
|
|
5841
5884
|
if (!coreRef.current) return;
|
|
5842
5885
|
coreRef.current.rotation.y += delta * 0.3;
|
|
@@ -5989,8 +6032,8 @@ function TraitOrbit({
|
|
|
5989
6032
|
onClick,
|
|
5990
6033
|
onHover
|
|
5991
6034
|
}) {
|
|
5992
|
-
const groupRef =
|
|
5993
|
-
const angleRef =
|
|
6035
|
+
const groupRef = React10.useRef(null);
|
|
6036
|
+
const angleRef = React10.useRef(Math.random() * Math.PI * 2);
|
|
5994
6037
|
fiber.useFrame((_, delta) => {
|
|
5995
6038
|
angleRef.current += delta * speed;
|
|
5996
6039
|
if (!groupRef.current) return;
|
|
@@ -6121,9 +6164,9 @@ var Avl3DOrbitalScene = ({
|
|
|
6121
6164
|
highlightedTrait,
|
|
6122
6165
|
onTraitHighlight
|
|
6123
6166
|
}) => {
|
|
6124
|
-
const [localHighlight, setLocalHighlight] =
|
|
6167
|
+
const [localHighlight, setLocalHighlight] = React10.useState(null);
|
|
6125
6168
|
const activeHighlight = highlightedTrait ?? localHighlight;
|
|
6126
|
-
const traitOrbits =
|
|
6169
|
+
const traitOrbits = React10.useMemo(() => {
|
|
6127
6170
|
return data.traits.map((trait, i) => ({
|
|
6128
6171
|
name: trait.name,
|
|
6129
6172
|
radius: 3 + i * 1.2,
|
|
@@ -6134,7 +6177,7 @@ var Avl3DOrbitalScene = ({
|
|
|
6134
6177
|
transitionCount: trait.transitionCount
|
|
6135
6178
|
}));
|
|
6136
6179
|
}, [data.traits]);
|
|
6137
|
-
const pagePositions =
|
|
6180
|
+
const pagePositions = React10.useMemo(() => {
|
|
6138
6181
|
const outerRadius = 3 + data.traits.length * 1.2 + 1.5;
|
|
6139
6182
|
return data.pages.map((_, i) => {
|
|
6140
6183
|
const angle = 2 * Math.PI * i / data.pages.length - Math.PI / 2;
|
|
@@ -6198,12 +6241,12 @@ var Avl3DStateNode = ({
|
|
|
6198
6241
|
outgoingCount = 0,
|
|
6199
6242
|
onClick
|
|
6200
6243
|
}) => {
|
|
6201
|
-
const [hovered, setHovered] =
|
|
6244
|
+
const [hovered, setHovered] = React10.useState(false);
|
|
6202
6245
|
const baseColor = active ? AVL_3D_COLORS.stateActive : AVL_3D_COLORS.stateIdle;
|
|
6203
6246
|
const emissiveColor = active ? AVL_3D_COLORS.stateActive : AVL_3D_COLORS.stateEdge;
|
|
6204
6247
|
const emissiveIntensity = active ? 2 : hovered ? 0.8 : 0.3;
|
|
6205
6248
|
const targetScale = hovered ? 1.08 : 1;
|
|
6206
|
-
const currentScale =
|
|
6249
|
+
const currentScale = React10.useRef(1);
|
|
6207
6250
|
fiber.useFrame((_, delta) => {
|
|
6208
6251
|
currentScale.current = THREE6.MathUtils.damp(currentScale.current, targetScale, 6, delta);
|
|
6209
6252
|
});
|
|
@@ -6377,8 +6420,8 @@ var Avl3DTransitionArc = ({
|
|
|
6377
6420
|
onClick,
|
|
6378
6421
|
color = AVL_3D_COLORS.transitionArc
|
|
6379
6422
|
}) => {
|
|
6380
|
-
const [hovered, setHovered] =
|
|
6381
|
-
const { tubeArgs, labelPos, guardPos, arrowPos, arrowQuat, effectPositions } =
|
|
6423
|
+
const [hovered, setHovered] = React10.useState(false);
|
|
6424
|
+
const { tubeArgs, labelPos, guardPos, arrowPos, arrowQuat, effectPositions } = React10.useMemo(() => {
|
|
6382
6425
|
const offset = 1.5 + index * 0.8;
|
|
6383
6426
|
const curve = isSelf ? selfLoopCurve3D(from, 1.2) : arcCurve3D(from, to, offset);
|
|
6384
6427
|
const mid = curve.getPoint(0.5);
|
|
@@ -6511,7 +6554,7 @@ var Avl3DTraitScene = ({
|
|
|
6511
6554
|
data,
|
|
6512
6555
|
onTransitionClick
|
|
6513
6556
|
}) => {
|
|
6514
|
-
const statePositions =
|
|
6557
|
+
const statePositions = React10.useMemo(() => {
|
|
6515
6558
|
const radius = Math.max(3, data.states.length * 0.8);
|
|
6516
6559
|
const positions = fibonacciSpherePositions(data.states.length, radius);
|
|
6517
6560
|
const posMap = /* @__PURE__ */ new Map();
|
|
@@ -6521,7 +6564,7 @@ var Avl3DTraitScene = ({
|
|
|
6521
6564
|
});
|
|
6522
6565
|
return posMap;
|
|
6523
6566
|
}, [data.states]);
|
|
6524
|
-
const transitionIndices =
|
|
6567
|
+
const transitionIndices = React10.useMemo(() => {
|
|
6525
6568
|
const pairCount = /* @__PURE__ */ new Map();
|
|
6526
6569
|
return data.transitions.map((t) => {
|
|
6527
6570
|
const key = `${t.from}->${t.to}`;
|
|
@@ -6530,7 +6573,7 @@ var Avl3DTraitScene = ({
|
|
|
6530
6573
|
return idx;
|
|
6531
6574
|
});
|
|
6532
6575
|
}, [data.transitions]);
|
|
6533
|
-
const transitionCounts =
|
|
6576
|
+
const transitionCounts = React10.useMemo(() => {
|
|
6534
6577
|
const incoming = /* @__PURE__ */ new Map();
|
|
6535
6578
|
const outgoing = /* @__PURE__ */ new Map();
|
|
6536
6579
|
for (const t of data.transitions) {
|
|
@@ -6640,11 +6683,11 @@ var Avl3DExprTree = ({
|
|
|
6640
6683
|
expression,
|
|
6641
6684
|
position
|
|
6642
6685
|
}) => {
|
|
6643
|
-
const layoutResults =
|
|
6686
|
+
const layoutResults = React10.useMemo(() => {
|
|
6644
6687
|
const origin = { x: position[0], y: position[1], z: position[2] };
|
|
6645
6688
|
return treeLayout3D(expression, origin, 2.5, 1.5);
|
|
6646
6689
|
}, [expression, position]);
|
|
6647
|
-
const edges =
|
|
6690
|
+
const edges = React10.useMemo(() => {
|
|
6648
6691
|
const result = [];
|
|
6649
6692
|
function collectEdges(node, parentPos) {
|
|
6650
6693
|
const entry = layoutResults.find((r) => r.node === node);
|
|
@@ -6924,7 +6967,7 @@ var Avl3DEffects = ({
|
|
|
6924
6967
|
const dof = DOF_CONFIG[level];
|
|
6925
6968
|
const sparkles = SPARKLE_CONFIGS[level];
|
|
6926
6969
|
const stars = STARS_CONFIG[level];
|
|
6927
|
-
const starsKey =
|
|
6970
|
+
const starsKey = React10.useMemo(() => `stars-${level}`, [level]);
|
|
6928
6971
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6929
6972
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6930
6973
|
drei.Stars,
|
|
@@ -6988,16 +7031,16 @@ var DEFAULT_CONFIG2 = {
|
|
|
6988
7031
|
modelOverrides: {},
|
|
6989
7032
|
effectsEnabled: true
|
|
6990
7033
|
};
|
|
6991
|
-
var Avl3DContext =
|
|
7034
|
+
var Avl3DContext = React10.createContext(DEFAULT_CONFIG2);
|
|
6992
7035
|
function useAvl3DConfig() {
|
|
6993
|
-
return
|
|
7036
|
+
return React10.useContext(Avl3DContext);
|
|
6994
7037
|
}
|
|
6995
|
-
function
|
|
7038
|
+
function CameraController({ targetPosition, targetLookAt, animated }) {
|
|
6996
7039
|
const { camera } = fiber.useThree();
|
|
6997
|
-
const targetPosVec =
|
|
6998
|
-
const targetLookVec =
|
|
6999
|
-
const isAnimating =
|
|
7000
|
-
|
|
7040
|
+
const targetPosVec = React10.useRef(new THREE6__namespace.Vector3(...targetPosition));
|
|
7041
|
+
const targetLookVec = React10.useRef(new THREE6__namespace.Vector3(...targetLookAt));
|
|
7042
|
+
const isAnimating = React10.useRef(false);
|
|
7043
|
+
React10.useEffect(() => {
|
|
7001
7044
|
const newTarget = new THREE6__namespace.Vector3(...targetPosition);
|
|
7002
7045
|
const newLookAt = new THREE6__namespace.Vector3(...targetLookAt);
|
|
7003
7046
|
if (!newTarget.equals(targetPosVec.current) || !newLookAt.equals(targetLookVec.current)) {
|
|
@@ -7026,8 +7069,8 @@ function CameraController2({ targetPosition, targetLookAt, animated }) {
|
|
|
7026
7069
|
return null;
|
|
7027
7070
|
}
|
|
7028
7071
|
function SceneFade({ animating, children }) {
|
|
7029
|
-
const groupRef =
|
|
7030
|
-
const opacityRef =
|
|
7072
|
+
const groupRef = React10.useRef(null);
|
|
7073
|
+
const opacityRef = React10.useRef(1);
|
|
7031
7074
|
fiber.useFrame((_, delta) => {
|
|
7032
7075
|
if (!groupRef.current) return;
|
|
7033
7076
|
const target = animating ? 0 : 1;
|
|
@@ -7051,7 +7094,7 @@ var Avl3DViewer = ({
|
|
|
7051
7094
|
modelOverrides = {}
|
|
7052
7095
|
}) => {
|
|
7053
7096
|
const { t } = hooks.useTranslate();
|
|
7054
|
-
const schema =
|
|
7097
|
+
const schema = React10.useMemo(() => {
|
|
7055
7098
|
if (typeof schemaProp === "string") {
|
|
7056
7099
|
try {
|
|
7057
7100
|
return JSON.parse(schemaProp);
|
|
@@ -7061,12 +7104,12 @@ var Avl3DViewer = ({
|
|
|
7061
7104
|
}
|
|
7062
7105
|
return schemaProp;
|
|
7063
7106
|
}, [schemaProp]);
|
|
7064
|
-
const configValue =
|
|
7107
|
+
const configValue = React10.useMemo(() => ({
|
|
7065
7108
|
modelOverrides,
|
|
7066
7109
|
effectsEnabled: effects
|
|
7067
7110
|
}), [modelOverrides, effects]);
|
|
7068
|
-
const [state, dispatch] =
|
|
7069
|
-
|
|
7111
|
+
const [state, dispatch] = React10.useReducer(zoomReducer, initialZoomState);
|
|
7112
|
+
React10.useEffect(() => {
|
|
7070
7113
|
if (initialOrbital) {
|
|
7071
7114
|
dispatch({ type: "ZOOM_INTO_ORBITAL", orbital: initialOrbital, targetPosition: { x: 0, y: 0 } });
|
|
7072
7115
|
setTimeout(() => dispatch({ type: "ANIMATION_COMPLETE" }), 0);
|
|
@@ -7078,35 +7121,35 @@ var Avl3DViewer = ({
|
|
|
7078
7121
|
}
|
|
7079
7122
|
}
|
|
7080
7123
|
}, [initialOrbital, initialTrait]);
|
|
7081
|
-
|
|
7124
|
+
React10.useEffect(() => {
|
|
7082
7125
|
onZoomChange?.(state.level, {
|
|
7083
7126
|
orbital: state.selectedOrbital ?? void 0,
|
|
7084
7127
|
trait: state.selectedTrait ?? void 0
|
|
7085
7128
|
});
|
|
7086
7129
|
}, [state.level, state.selectedOrbital, state.selectedTrait, onZoomChange]);
|
|
7087
|
-
|
|
7130
|
+
React10.useEffect(() => {
|
|
7088
7131
|
if (!state.animating) return;
|
|
7089
7132
|
const timer = setTimeout(() => {
|
|
7090
7133
|
dispatch({ type: "ANIMATION_COMPLETE" });
|
|
7091
7134
|
}, 800);
|
|
7092
7135
|
return () => clearTimeout(timer);
|
|
7093
7136
|
}, [state.animating]);
|
|
7094
|
-
const handleKeyDown =
|
|
7137
|
+
const handleKeyDown = React10.useCallback((e) => {
|
|
7095
7138
|
if (e.key === "Escape") {
|
|
7096
7139
|
dispatch({ type: "ZOOM_OUT" });
|
|
7097
7140
|
}
|
|
7098
7141
|
}, []);
|
|
7099
|
-
const handleOrbitalClick =
|
|
7142
|
+
const handleOrbitalClick = React10.useCallback((name) => {
|
|
7100
7143
|
dispatch({ type: "ZOOM_INTO_ORBITAL", orbital: name, targetPosition: { x: 0, y: 0 } });
|
|
7101
7144
|
}, []);
|
|
7102
|
-
const handleTraitClick =
|
|
7145
|
+
const handleTraitClick = React10.useCallback((name) => {
|
|
7103
7146
|
dispatch({ type: "ZOOM_INTO_TRAIT", trait: name, targetPosition: { x: 0, y: 0 } });
|
|
7104
7147
|
}, []);
|
|
7105
|
-
const [highlightedTrait, setHighlightedTrait] =
|
|
7106
|
-
const handleTransitionClick =
|
|
7148
|
+
const [highlightedTrait, setHighlightedTrait] = React10__namespace.default.useState(null);
|
|
7149
|
+
const handleTransitionClick = React10.useCallback((index) => {
|
|
7107
7150
|
dispatch({ type: "ZOOM_INTO_TRANSITION", transitionIndex: index, targetPosition: { x: 0, y: 0 } });
|
|
7108
7151
|
}, []);
|
|
7109
|
-
const handleBreadcrumbClick =
|
|
7152
|
+
const handleBreadcrumbClick = React10.useCallback((targetLevel) => {
|
|
7110
7153
|
const levelOrder = ["application", "orbital", "trait", "transition"];
|
|
7111
7154
|
const currentIdx = levelOrder.indexOf(state.level);
|
|
7112
7155
|
const targetIdx = levelOrder.indexOf(targetLevel);
|
|
@@ -7115,7 +7158,7 @@ var Avl3DViewer = ({
|
|
|
7115
7158
|
}
|
|
7116
7159
|
}, [state.level]);
|
|
7117
7160
|
const cameraConfig = CAMERA_POSITIONS[state.level];
|
|
7118
|
-
const sceneContent =
|
|
7161
|
+
const sceneContent = React10.useMemo(() => {
|
|
7119
7162
|
switch (state.level) {
|
|
7120
7163
|
case "application": {
|
|
7121
7164
|
const data = parseApplicationLevel(schema);
|
|
@@ -7189,7 +7232,7 @@ var Avl3DViewer = ({
|
|
|
7189
7232
|
gap: "xs",
|
|
7190
7233
|
align: "center",
|
|
7191
7234
|
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(
|
|
7235
|
+
children: breadcrumbs.map((crumb, i) => /* @__PURE__ */ jsxRuntime.jsxs(React10__namespace.default.Fragment, { children: [
|
|
7193
7236
|
i > 0 && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "muted", className: "mx-1", children: "/" }),
|
|
7194
7237
|
i < breadcrumbs.length - 1 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
7195
7238
|
Box,
|
|
@@ -7264,7 +7307,7 @@ var Avl3DViewer = ({
|
|
|
7264
7307
|
}
|
|
7265
7308
|
),
|
|
7266
7309
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7267
|
-
|
|
7310
|
+
CameraController,
|
|
7268
7311
|
{
|
|
7269
7312
|
targetPosition: cameraConfig.position,
|
|
7270
7313
|
targetLookAt: cameraConfig.target,
|