@almadar/ui 5.95.0 → 5.96.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 +1665 -1416
- package/dist/avl/index.js +660 -411
- package/dist/components/core/atoms/AtlasImage.d.ts +58 -0
- package/dist/components/core/atoms/index.d.ts +1 -0
- package/dist/components/game/2d/molecules/Canvas2D.d.ts +4 -0
- package/dist/components/game/2d/templates/GameShell.d.ts +19 -23
- package/dist/components/game/3d/index.cjs +543 -212
- package/dist/components/game/3d/index.js +371 -36
- package/dist/components/game/3d/molecules/GameCanvas3D.d.ts +34 -3
- package/dist/components/game/3d/molecules/ModelLoader.d.ts +7 -1
- package/dist/components/game/3d/molecules/index.d.ts +0 -1
- package/dist/components/index.cjs +1645 -1392
- package/dist/components/index.js +637 -387
- package/dist/marketing/index.cjs +208 -73
- package/dist/marketing/index.js +166 -28
- package/dist/providers/index.cjs +1533 -1284
- package/dist/providers/index.js +636 -387
- package/dist/runtime/index.cjs +1526 -1277
- package/dist/runtime/index.js +640 -391
- package/package.json +1 -1
- package/dist/components/game/3d/molecules/Canvas3D.d.ts +0 -45
- /package/dist/{components/game/shared → lib}/atlasSlice.d.ts +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var React8 = require('react');
|
|
4
4
|
var fiber = require('@react-three/fiber');
|
|
5
5
|
var THREE6 = require('three');
|
|
6
6
|
var jsxRuntime = require('react/jsx-runtime');
|
|
@@ -18,8 +18,6 @@ var LucideIcons = require('lucide-react');
|
|
|
18
18
|
var postprocessing = require('@react-three/postprocessing');
|
|
19
19
|
var hooks = require('@almadar/ui/hooks');
|
|
20
20
|
|
|
21
|
-
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
22
|
-
|
|
23
21
|
function _interopNamespace(e) {
|
|
24
22
|
if (e && e.__esModule) return e;
|
|
25
23
|
var n = Object.create(null);
|
|
@@ -38,7 +36,7 @@ function _interopNamespace(e) {
|
|
|
38
36
|
return Object.freeze(n);
|
|
39
37
|
}
|
|
40
38
|
|
|
41
|
-
var
|
|
39
|
+
var React8__namespace = /*#__PURE__*/_interopNamespace(React8);
|
|
42
40
|
var THREE6__namespace = /*#__PURE__*/_interopNamespace(THREE6);
|
|
43
41
|
var LucideIcons__namespace = /*#__PURE__*/_interopNamespace(LucideIcons);
|
|
44
42
|
|
|
@@ -47,8 +45,8 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
|
|
|
47
45
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
48
46
|
function Scene3D({ background = "#1a1a2e", fog, children }) {
|
|
49
47
|
const { scene } = fiber.useThree();
|
|
50
|
-
const initializedRef =
|
|
51
|
-
|
|
48
|
+
const initializedRef = React8.useRef(false);
|
|
49
|
+
React8.useEffect(() => {
|
|
52
50
|
if (initializedRef.current) return;
|
|
53
51
|
initializedRef.current = true;
|
|
54
52
|
if (background.startsWith("#") || background.startsWith("rgb")) {
|
|
@@ -69,7 +67,7 @@ function Scene3D({ background = "#1a1a2e", fog, children }) {
|
|
|
69
67
|
}, [scene, background, fog]);
|
|
70
68
|
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
|
|
71
69
|
}
|
|
72
|
-
var Camera3D =
|
|
70
|
+
var Camera3D = React8.forwardRef(
|
|
73
71
|
({
|
|
74
72
|
mode = "isometric",
|
|
75
73
|
position = [10, 10, 10],
|
|
@@ -82,10 +80,10 @@ var Camera3D = React6.forwardRef(
|
|
|
82
80
|
onChange
|
|
83
81
|
}, ref) => {
|
|
84
82
|
const { camera, set, viewport } = fiber.useThree();
|
|
85
|
-
const controlsRef =
|
|
86
|
-
const initialPosition =
|
|
87
|
-
const initialTarget =
|
|
88
|
-
|
|
83
|
+
const controlsRef = React8.useRef(null);
|
|
84
|
+
const initialPosition = React8.useRef(new THREE6__namespace.Vector3(...position));
|
|
85
|
+
const initialTarget = React8.useRef(new THREE6__namespace.Vector3(...target));
|
|
86
|
+
React8.useEffect(() => {
|
|
89
87
|
let newCamera;
|
|
90
88
|
if (mode === "isometric") {
|
|
91
89
|
const aspect = viewport.aspect;
|
|
@@ -116,7 +114,7 @@ var Camera3D = React6.forwardRef(
|
|
|
116
114
|
onChange(camera);
|
|
117
115
|
}
|
|
118
116
|
});
|
|
119
|
-
|
|
117
|
+
React8.useImperativeHandle(ref, () => ({
|
|
120
118
|
getCamera: () => camera,
|
|
121
119
|
setPosition: (x, y, z) => {
|
|
122
120
|
camera.position.set(x, y, z);
|
|
@@ -258,7 +256,7 @@ function Canvas3DLoadingState({
|
|
|
258
256
|
] });
|
|
259
257
|
}
|
|
260
258
|
var log = logger.createLogger("almadar:ui:game:canvas3d:error-boundary");
|
|
261
|
-
var Canvas3DErrorBoundary = class extends
|
|
259
|
+
var Canvas3DErrorBoundary = class extends React8.Component {
|
|
262
260
|
constructor(props) {
|
|
263
261
|
super(props);
|
|
264
262
|
__publicField(this, "handleReset", () => {
|
|
@@ -338,14 +336,15 @@ function detectAssetRoot(modelUrl) {
|
|
|
338
336
|
return modelUrl.substring(0, modelUrl.lastIndexOf("/") + 1);
|
|
339
337
|
}
|
|
340
338
|
function useGLTFModel(url, resourceBasePath) {
|
|
341
|
-
const [state, setState] =
|
|
339
|
+
const [state, setState] = React8.useState({
|
|
342
340
|
model: null,
|
|
341
|
+
clips: [],
|
|
343
342
|
isLoading: false,
|
|
344
343
|
error: null
|
|
345
344
|
});
|
|
346
|
-
|
|
345
|
+
React8.useEffect(() => {
|
|
347
346
|
if (!url) {
|
|
348
|
-
setState({ model: null, isLoading: false, error: null });
|
|
347
|
+
setState({ model: null, clips: [], isLoading: false, error: null });
|
|
349
348
|
return;
|
|
350
349
|
}
|
|
351
350
|
log2.debug("Loading", { url });
|
|
@@ -356,9 +355,10 @@ function useGLTFModel(url, resourceBasePath) {
|
|
|
356
355
|
loader.load(
|
|
357
356
|
url,
|
|
358
357
|
(gltf) => {
|
|
359
|
-
log2.debug("Loaded", { url });
|
|
358
|
+
log2.debug("Loaded", { url, clips: gltf.animations.length });
|
|
360
359
|
setState({
|
|
361
360
|
model: gltf.scene,
|
|
361
|
+
clips: gltf.animations,
|
|
362
362
|
isLoading: false,
|
|
363
363
|
error: null
|
|
364
364
|
});
|
|
@@ -368,6 +368,7 @@ function useGLTFModel(url, resourceBasePath) {
|
|
|
368
368
|
log2.warn("Failed", { url, error: err instanceof Error ? err : String(err) });
|
|
369
369
|
setState({
|
|
370
370
|
model: null,
|
|
371
|
+
clips: [],
|
|
371
372
|
isLoading: false,
|
|
372
373
|
error: err instanceof Error ? err : new Error(String(err))
|
|
373
374
|
});
|
|
@@ -388,10 +389,11 @@ function ModelLoader({
|
|
|
388
389
|
fallbackGeometry = "box",
|
|
389
390
|
castShadow = true,
|
|
390
391
|
receiveShadow = true,
|
|
391
|
-
resourceBasePath
|
|
392
|
+
resourceBasePath,
|
|
393
|
+
animation
|
|
392
394
|
}) {
|
|
393
|
-
const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
|
|
394
|
-
const model =
|
|
395
|
+
const { model: loadedModel, clips, isLoading, error } = useGLTFModel(url, resourceBasePath);
|
|
396
|
+
const model = React8.useMemo(() => {
|
|
395
397
|
if (!loadedModel) return null;
|
|
396
398
|
const cloned = SkeletonUtils.clone(loadedModel);
|
|
397
399
|
cloned.updateMatrixWorld(true);
|
|
@@ -403,7 +405,22 @@ function ModelLoader({
|
|
|
403
405
|
});
|
|
404
406
|
return cloned;
|
|
405
407
|
}, [loadedModel, castShadow, receiveShadow]);
|
|
406
|
-
const
|
|
408
|
+
const mixer = React8.useMemo(() => model ? new THREE6__namespace.AnimationMixer(model) : null, [model]);
|
|
409
|
+
React8.useEffect(() => {
|
|
410
|
+
if (!mixer || !animation || clips.length === 0) return;
|
|
411
|
+
const wanted = animation.toLowerCase();
|
|
412
|
+
const clip = clips.find((c) => c.name === animation) ?? clips.find((c) => c.name.toLowerCase() === wanted);
|
|
413
|
+
if (!clip) return;
|
|
414
|
+
const action = mixer.clipAction(clip);
|
|
415
|
+
action.reset().fadeIn(0.15).play();
|
|
416
|
+
return () => {
|
|
417
|
+
action.fadeOut(0.15);
|
|
418
|
+
};
|
|
419
|
+
}, [mixer, clips, animation]);
|
|
420
|
+
fiber.useFrame((_, delta) => {
|
|
421
|
+
mixer?.update(delta);
|
|
422
|
+
});
|
|
423
|
+
const normFactor = React8.useMemo(() => {
|
|
407
424
|
if (!model) return 1;
|
|
408
425
|
const box = new THREE6__namespace.Box3().setFromObject(model);
|
|
409
426
|
const size = new THREE6__namespace.Vector3();
|
|
@@ -412,11 +429,11 @@ function ModelLoader({
|
|
|
412
429
|
if (!Number.isFinite(maxDim) || maxDim < 0.05) return 1;
|
|
413
430
|
return 1 / maxDim;
|
|
414
431
|
}, [model]);
|
|
415
|
-
const scaleArray =
|
|
432
|
+
const scaleArray = React8.useMemo(() => {
|
|
416
433
|
const base = typeof scale === "number" ? [scale, scale, scale] : scale;
|
|
417
434
|
return [base[0] * normFactor, base[1] * normFactor, base[2] * normFactor];
|
|
418
435
|
}, [scale, normFactor]);
|
|
419
|
-
const rotationRad =
|
|
436
|
+
const rotationRad = React8.useMemo(() => {
|
|
420
437
|
return [
|
|
421
438
|
rotation[0] * Math.PI / 180,
|
|
422
439
|
rotation[1] * Math.PI / 180,
|
|
@@ -696,21 +713,21 @@ var DEFAULT_OPTIONS = {
|
|
|
696
713
|
};
|
|
697
714
|
function useThree3(options = {}) {
|
|
698
715
|
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
699
|
-
const containerRef =
|
|
700
|
-
const canvasRef =
|
|
701
|
-
const rendererRef =
|
|
702
|
-
const sceneRef =
|
|
703
|
-
const cameraRef =
|
|
704
|
-
const controlsRef =
|
|
705
|
-
const gridHelperRef =
|
|
706
|
-
const rafRef =
|
|
707
|
-
const [isReady, setIsReady] =
|
|
708
|
-
const [dimensions, setDimensions] =
|
|
709
|
-
const initialCameraPosition =
|
|
716
|
+
const containerRef = React8.useRef(null);
|
|
717
|
+
const canvasRef = React8.useRef(null);
|
|
718
|
+
const rendererRef = React8.useRef(null);
|
|
719
|
+
const sceneRef = React8.useRef(null);
|
|
720
|
+
const cameraRef = React8.useRef(null);
|
|
721
|
+
const controlsRef = React8.useRef(null);
|
|
722
|
+
const gridHelperRef = React8.useRef(null);
|
|
723
|
+
const rafRef = React8.useRef(0);
|
|
724
|
+
const [isReady, setIsReady] = React8.useState(false);
|
|
725
|
+
const [dimensions, setDimensions] = React8.useState({ width: 0, height: 0 });
|
|
726
|
+
const initialCameraPosition = React8.useMemo(
|
|
710
727
|
() => new THREE6__namespace.Vector3(...opts.cameraPosition),
|
|
711
728
|
[]
|
|
712
729
|
);
|
|
713
|
-
|
|
730
|
+
React8.useEffect(() => {
|
|
714
731
|
if (!containerRef.current) return;
|
|
715
732
|
const container = containerRef.current;
|
|
716
733
|
const { clientWidth, clientHeight } = container;
|
|
@@ -803,7 +820,7 @@ function useThree3(options = {}) {
|
|
|
803
820
|
scene.clear();
|
|
804
821
|
};
|
|
805
822
|
}, []);
|
|
806
|
-
|
|
823
|
+
React8.useEffect(() => {
|
|
807
824
|
if (!cameraRef.current || !containerRef.current) return;
|
|
808
825
|
const container = containerRef.current;
|
|
809
826
|
const { clientWidth, clientHeight } = container;
|
|
@@ -838,20 +855,20 @@ function useThree3(options = {}) {
|
|
|
838
855
|
animate();
|
|
839
856
|
}
|
|
840
857
|
}, [opts.cameraMode]);
|
|
841
|
-
const setCameraPosition =
|
|
858
|
+
const setCameraPosition = React8.useCallback((x, y, z) => {
|
|
842
859
|
if (cameraRef.current) {
|
|
843
860
|
cameraRef.current.position.set(x, y, z);
|
|
844
861
|
controlsRef.current?.update();
|
|
845
862
|
}
|
|
846
863
|
}, []);
|
|
847
|
-
const lookAt =
|
|
864
|
+
const lookAt = React8.useCallback((x, y, z) => {
|
|
848
865
|
if (cameraRef.current) {
|
|
849
866
|
cameraRef.current.lookAt(x, y, z);
|
|
850
867
|
controlsRef.current?.target.set(x, y, z);
|
|
851
868
|
controlsRef.current?.update();
|
|
852
869
|
}
|
|
853
870
|
}, []);
|
|
854
|
-
const resetCamera =
|
|
871
|
+
const resetCamera = React8.useCallback(() => {
|
|
855
872
|
if (cameraRef.current) {
|
|
856
873
|
cameraRef.current.position.copy(initialCameraPosition);
|
|
857
874
|
cameraRef.current.lookAt(0, 0, 0);
|
|
@@ -861,7 +878,7 @@ function useThree3(options = {}) {
|
|
|
861
878
|
}
|
|
862
879
|
}
|
|
863
880
|
}, [initialCameraPosition]);
|
|
864
|
-
const fitView =
|
|
881
|
+
const fitView = React8.useCallback(
|
|
865
882
|
(bounds) => {
|
|
866
883
|
if (!cameraRef.current) return;
|
|
867
884
|
const centerX = (bounds.minX + bounds.maxX) / 2;
|
|
@@ -892,20 +909,20 @@ function useThree3(options = {}) {
|
|
|
892
909
|
}
|
|
893
910
|
function useAssetLoader(options = {}) {
|
|
894
911
|
const { preloadUrls = [], loader: customLoader } = options;
|
|
895
|
-
const loaderRef =
|
|
896
|
-
const [state, setState] =
|
|
912
|
+
const loaderRef = React8.useRef(customLoader || new AssetLoader());
|
|
913
|
+
const [state, setState] = React8.useState({
|
|
897
914
|
isLoading: false,
|
|
898
915
|
progress: 0,
|
|
899
916
|
loaded: 0,
|
|
900
917
|
total: 0,
|
|
901
918
|
errors: []
|
|
902
919
|
});
|
|
903
|
-
|
|
920
|
+
React8.useEffect(() => {
|
|
904
921
|
if (preloadUrls.length > 0) {
|
|
905
922
|
preload(preloadUrls);
|
|
906
923
|
}
|
|
907
924
|
}, []);
|
|
908
|
-
const updateProgress =
|
|
925
|
+
const updateProgress = React8.useCallback((loaded, total) => {
|
|
909
926
|
setState((prev) => ({
|
|
910
927
|
...prev,
|
|
911
928
|
loaded,
|
|
@@ -913,7 +930,7 @@ function useAssetLoader(options = {}) {
|
|
|
913
930
|
progress: total > 0 ? Math.round(loaded / total * 100) : 0
|
|
914
931
|
}));
|
|
915
932
|
}, []);
|
|
916
|
-
const loadModel =
|
|
933
|
+
const loadModel = React8.useCallback(
|
|
917
934
|
async (url) => {
|
|
918
935
|
setState((prev) => ({ ...prev, isLoading: true }));
|
|
919
936
|
try {
|
|
@@ -936,7 +953,7 @@ function useAssetLoader(options = {}) {
|
|
|
936
953
|
},
|
|
937
954
|
[]
|
|
938
955
|
);
|
|
939
|
-
const loadOBJ =
|
|
956
|
+
const loadOBJ = React8.useCallback(
|
|
940
957
|
async (url) => {
|
|
941
958
|
setState((prev) => ({ ...prev, isLoading: true }));
|
|
942
959
|
try {
|
|
@@ -959,7 +976,7 @@ function useAssetLoader(options = {}) {
|
|
|
959
976
|
},
|
|
960
977
|
[]
|
|
961
978
|
);
|
|
962
|
-
const loadTexture =
|
|
979
|
+
const loadTexture = React8.useCallback(
|
|
963
980
|
async (url) => {
|
|
964
981
|
setState((prev) => ({ ...prev, isLoading: true }));
|
|
965
982
|
try {
|
|
@@ -982,7 +999,7 @@ function useAssetLoader(options = {}) {
|
|
|
982
999
|
},
|
|
983
1000
|
[]
|
|
984
1001
|
);
|
|
985
|
-
const preload =
|
|
1002
|
+
const preload = React8.useCallback(
|
|
986
1003
|
async (urls) => {
|
|
987
1004
|
setState((prev) => ({
|
|
988
1005
|
...prev,
|
|
@@ -1021,27 +1038,27 @@ function useAssetLoader(options = {}) {
|
|
|
1021
1038
|
},
|
|
1022
1039
|
[updateProgress]
|
|
1023
1040
|
);
|
|
1024
|
-
const hasModel =
|
|
1041
|
+
const hasModel = React8.useCallback((url) => {
|
|
1025
1042
|
return loaderRef.current.hasModel(url);
|
|
1026
1043
|
}, []);
|
|
1027
|
-
const hasTexture =
|
|
1044
|
+
const hasTexture = React8.useCallback((url) => {
|
|
1028
1045
|
return loaderRef.current.hasTexture(url);
|
|
1029
1046
|
}, []);
|
|
1030
|
-
const getModel =
|
|
1047
|
+
const getModel = React8.useCallback((url) => {
|
|
1031
1048
|
try {
|
|
1032
1049
|
return loaderRef.current.getModel(url);
|
|
1033
1050
|
} catch {
|
|
1034
1051
|
return void 0;
|
|
1035
1052
|
}
|
|
1036
1053
|
}, []);
|
|
1037
|
-
const getTexture =
|
|
1054
|
+
const getTexture = React8.useCallback((url) => {
|
|
1038
1055
|
try {
|
|
1039
1056
|
return loaderRef.current.getTexture(url);
|
|
1040
1057
|
} catch {
|
|
1041
1058
|
return void 0;
|
|
1042
1059
|
}
|
|
1043
1060
|
}, []);
|
|
1044
|
-
const clearCache =
|
|
1061
|
+
const clearCache = React8.useCallback(() => {
|
|
1045
1062
|
loaderRef.current.clearCache();
|
|
1046
1063
|
setState({
|
|
1047
1064
|
isLoading: false,
|
|
@@ -1065,25 +1082,25 @@ function useAssetLoader(options = {}) {
|
|
|
1065
1082
|
};
|
|
1066
1083
|
}
|
|
1067
1084
|
function useSceneGraph() {
|
|
1068
|
-
const nodesRef =
|
|
1069
|
-
const addNode =
|
|
1085
|
+
const nodesRef = React8.useRef(/* @__PURE__ */ new Map());
|
|
1086
|
+
const addNode = React8.useCallback((node) => {
|
|
1070
1087
|
const existing = nodesRef.current.get(node.id);
|
|
1071
1088
|
if (existing) {
|
|
1072
1089
|
existing.mesh.removeFromParent();
|
|
1073
1090
|
}
|
|
1074
1091
|
nodesRef.current.set(node.id, node);
|
|
1075
1092
|
}, []);
|
|
1076
|
-
const removeNode =
|
|
1093
|
+
const removeNode = React8.useCallback((id) => {
|
|
1077
1094
|
const node = nodesRef.current.get(id);
|
|
1078
1095
|
if (node) {
|
|
1079
1096
|
node.mesh.removeFromParent();
|
|
1080
1097
|
nodesRef.current.delete(id);
|
|
1081
1098
|
}
|
|
1082
1099
|
}, []);
|
|
1083
|
-
const getNode =
|
|
1100
|
+
const getNode = React8.useCallback((id) => {
|
|
1084
1101
|
return nodesRef.current.get(id);
|
|
1085
1102
|
}, []);
|
|
1086
|
-
const updateNodePosition =
|
|
1103
|
+
const updateNodePosition = React8.useCallback(
|
|
1087
1104
|
(id, x, y, z) => {
|
|
1088
1105
|
const node = nodesRef.current.get(id);
|
|
1089
1106
|
if (node) {
|
|
@@ -1093,7 +1110,7 @@ function useSceneGraph() {
|
|
|
1093
1110
|
},
|
|
1094
1111
|
[]
|
|
1095
1112
|
);
|
|
1096
|
-
const updateNodeGridPosition =
|
|
1113
|
+
const updateNodeGridPosition = React8.useCallback(
|
|
1097
1114
|
(id, gridX, gridZ) => {
|
|
1098
1115
|
const node = nodesRef.current.get(id);
|
|
1099
1116
|
if (node) {
|
|
@@ -1102,7 +1119,7 @@ function useSceneGraph() {
|
|
|
1102
1119
|
},
|
|
1103
1120
|
[]
|
|
1104
1121
|
);
|
|
1105
|
-
const getNodeAtGrid =
|
|
1122
|
+
const getNodeAtGrid = React8.useCallback(
|
|
1106
1123
|
(x, z, type) => {
|
|
1107
1124
|
return Array.from(nodesRef.current.values()).find((node) => {
|
|
1108
1125
|
const matchesGrid = node.gridPosition.x === x && node.gridPosition.z === z;
|
|
@@ -1111,10 +1128,10 @@ function useSceneGraph() {
|
|
|
1111
1128
|
},
|
|
1112
1129
|
[]
|
|
1113
1130
|
);
|
|
1114
|
-
const getNodesByType =
|
|
1131
|
+
const getNodesByType = React8.useCallback((type) => {
|
|
1115
1132
|
return Array.from(nodesRef.current.values()).filter((node) => node.type === type);
|
|
1116
1133
|
}, []);
|
|
1117
|
-
const getNodesInBounds =
|
|
1134
|
+
const getNodesInBounds = React8.useCallback(
|
|
1118
1135
|
(minX, maxX, minZ, maxZ) => {
|
|
1119
1136
|
return Array.from(nodesRef.current.values()).filter((node) => {
|
|
1120
1137
|
const { x, z } = node.gridPosition;
|
|
@@ -1123,13 +1140,13 @@ function useSceneGraph() {
|
|
|
1123
1140
|
},
|
|
1124
1141
|
[]
|
|
1125
1142
|
);
|
|
1126
|
-
const clearNodes =
|
|
1143
|
+
const clearNodes = React8.useCallback(() => {
|
|
1127
1144
|
nodesRef.current.forEach((node) => {
|
|
1128
1145
|
node.mesh.removeFromParent();
|
|
1129
1146
|
});
|
|
1130
1147
|
nodesRef.current.clear();
|
|
1131
1148
|
}, []);
|
|
1132
|
-
const countNodes =
|
|
1149
|
+
const countNodes = React8.useCallback((type) => {
|
|
1133
1150
|
if (!type) {
|
|
1134
1151
|
return nodesRef.current.size;
|
|
1135
1152
|
}
|
|
@@ -1151,9 +1168,9 @@ function useSceneGraph() {
|
|
|
1151
1168
|
}
|
|
1152
1169
|
function useRaycaster(options) {
|
|
1153
1170
|
const { camera, canvas, cellSize = 1, offsetX = 0, offsetZ = 0 } = options;
|
|
1154
|
-
const raycaster =
|
|
1155
|
-
const mouse =
|
|
1156
|
-
const clientToNDC =
|
|
1171
|
+
const raycaster = React8.useRef(new THREE6__namespace.Raycaster());
|
|
1172
|
+
const mouse = React8.useRef(new THREE6__namespace.Vector2());
|
|
1173
|
+
const clientToNDC = React8.useCallback(
|
|
1157
1174
|
(clientX, clientY) => {
|
|
1158
1175
|
if (!canvas) {
|
|
1159
1176
|
return { x: 0, y: 0 };
|
|
@@ -1166,7 +1183,7 @@ function useRaycaster(options) {
|
|
|
1166
1183
|
},
|
|
1167
1184
|
[canvas]
|
|
1168
1185
|
);
|
|
1169
|
-
const isWithinCanvas =
|
|
1186
|
+
const isWithinCanvas = React8.useCallback(
|
|
1170
1187
|
(clientX, clientY) => {
|
|
1171
1188
|
if (!canvas) return false;
|
|
1172
1189
|
const rect = canvas.getBoundingClientRect();
|
|
@@ -1174,7 +1191,7 @@ function useRaycaster(options) {
|
|
|
1174
1191
|
},
|
|
1175
1192
|
[canvas]
|
|
1176
1193
|
);
|
|
1177
|
-
const getIntersection =
|
|
1194
|
+
const getIntersection = React8.useCallback(
|
|
1178
1195
|
(clientX, clientY, objects) => {
|
|
1179
1196
|
if (!camera || !canvas) return null;
|
|
1180
1197
|
const ndc = clientToNDC(clientX, clientY);
|
|
@@ -1197,7 +1214,7 @@ function useRaycaster(options) {
|
|
|
1197
1214
|
},
|
|
1198
1215
|
[camera, canvas, clientToNDC]
|
|
1199
1216
|
);
|
|
1200
|
-
const getAllIntersections =
|
|
1217
|
+
const getAllIntersections = React8.useCallback(
|
|
1201
1218
|
(clientX, clientY, objects) => {
|
|
1202
1219
|
if (!camera || !canvas) return [];
|
|
1203
1220
|
const ndc = clientToNDC(clientX, clientY);
|
|
@@ -1216,7 +1233,7 @@ function useRaycaster(options) {
|
|
|
1216
1233
|
},
|
|
1217
1234
|
[camera, canvas, clientToNDC]
|
|
1218
1235
|
);
|
|
1219
|
-
const getGridCoordinates =
|
|
1236
|
+
const getGridCoordinates = React8.useCallback(
|
|
1220
1237
|
(clientX, clientY) => {
|
|
1221
1238
|
if (!camera || !canvas) return null;
|
|
1222
1239
|
const ndc = clientToNDC(clientX, clientY);
|
|
@@ -1234,7 +1251,7 @@ function useRaycaster(options) {
|
|
|
1234
1251
|
},
|
|
1235
1252
|
[camera, canvas, cellSize, offsetX, offsetZ, clientToNDC]
|
|
1236
1253
|
);
|
|
1237
|
-
const getTileAtPosition =
|
|
1254
|
+
const getTileAtPosition = React8.useCallback(
|
|
1238
1255
|
(clientX, clientY, scene) => {
|
|
1239
1256
|
if (!camera || !canvas) return null;
|
|
1240
1257
|
const tileMeshes = [];
|
|
@@ -1357,10 +1374,10 @@ var fallbackEventBus = {
|
|
|
1357
1374
|
}
|
|
1358
1375
|
};
|
|
1359
1376
|
function useEventBus() {
|
|
1360
|
-
const context =
|
|
1377
|
+
const context = React8.useContext(providers.EventBusContext);
|
|
1361
1378
|
const baseBus = context ?? getGlobalEventBus() ?? fallbackEventBus;
|
|
1362
1379
|
const scope = providers.useTraitScope();
|
|
1363
|
-
return
|
|
1380
|
+
return React8.useMemo(() => {
|
|
1364
1381
|
if (!scope) {
|
|
1365
1382
|
return {
|
|
1366
1383
|
...baseBus,
|
|
@@ -1396,7 +1413,7 @@ function useEventBus() {
|
|
|
1396
1413
|
}
|
|
1397
1414
|
function useEmitEvent() {
|
|
1398
1415
|
const eventBus = useEventBus();
|
|
1399
|
-
return
|
|
1416
|
+
return React8.useCallback(
|
|
1400
1417
|
(type, payload, source) => {
|
|
1401
1418
|
eventBus.emit(type, payload, source);
|
|
1402
1419
|
},
|
|
@@ -1423,9 +1440,9 @@ function useGameCanvas3DEvents(options) {
|
|
|
1423
1440
|
onUnitAnimation
|
|
1424
1441
|
} = options;
|
|
1425
1442
|
const emit = useEmitEvent();
|
|
1426
|
-
const optionsRef =
|
|
1443
|
+
const optionsRef = React8.useRef(options);
|
|
1427
1444
|
optionsRef.current = options;
|
|
1428
|
-
const handleTileClick =
|
|
1445
|
+
const handleTileClick = React8.useCallback(
|
|
1429
1446
|
(tile, event) => {
|
|
1430
1447
|
if (tileClickEvent) {
|
|
1431
1448
|
emit(tileClickEvent, {
|
|
@@ -1441,7 +1458,7 @@ function useGameCanvas3DEvents(options) {
|
|
|
1441
1458
|
},
|
|
1442
1459
|
[tileClickEvent, emit]
|
|
1443
1460
|
);
|
|
1444
|
-
const handleUnitClick =
|
|
1461
|
+
const handleUnitClick = React8.useCallback(
|
|
1445
1462
|
(unit, event) => {
|
|
1446
1463
|
if (unitClickEvent) {
|
|
1447
1464
|
emit(unitClickEvent, {
|
|
@@ -1460,7 +1477,7 @@ function useGameCanvas3DEvents(options) {
|
|
|
1460
1477
|
},
|
|
1461
1478
|
[unitClickEvent, emit]
|
|
1462
1479
|
);
|
|
1463
|
-
const handleFeatureClick =
|
|
1480
|
+
const handleFeatureClick = React8.useCallback(
|
|
1464
1481
|
(feature, event) => {
|
|
1465
1482
|
if (featureClickEvent) {
|
|
1466
1483
|
emit(featureClickEvent, {
|
|
@@ -1475,7 +1492,7 @@ function useGameCanvas3DEvents(options) {
|
|
|
1475
1492
|
},
|
|
1476
1493
|
[featureClickEvent, emit]
|
|
1477
1494
|
);
|
|
1478
|
-
const handleCanvasClick =
|
|
1495
|
+
const handleCanvasClick = React8.useCallback(
|
|
1479
1496
|
(event) => {
|
|
1480
1497
|
if (canvasClickEvent) {
|
|
1481
1498
|
emit(canvasClickEvent, {
|
|
@@ -1488,7 +1505,7 @@ function useGameCanvas3DEvents(options) {
|
|
|
1488
1505
|
},
|
|
1489
1506
|
[canvasClickEvent, emit]
|
|
1490
1507
|
);
|
|
1491
|
-
const handleTileHover =
|
|
1508
|
+
const handleTileHover = React8.useCallback(
|
|
1492
1509
|
(tile, event) => {
|
|
1493
1510
|
if (tile) {
|
|
1494
1511
|
if (tileHoverEvent) {
|
|
@@ -1508,7 +1525,7 @@ function useGameCanvas3DEvents(options) {
|
|
|
1508
1525
|
},
|
|
1509
1526
|
[tileHoverEvent, tileLeaveEvent, emit]
|
|
1510
1527
|
);
|
|
1511
|
-
const handleUnitAnimation =
|
|
1528
|
+
const handleUnitAnimation = React8.useCallback(
|
|
1512
1529
|
(unitId, state) => {
|
|
1513
1530
|
if (unitAnimationEvent) {
|
|
1514
1531
|
emit(unitAnimationEvent, {
|
|
@@ -1521,7 +1538,7 @@ function useGameCanvas3DEvents(options) {
|
|
|
1521
1538
|
},
|
|
1522
1539
|
[unitAnimationEvent, emit]
|
|
1523
1540
|
);
|
|
1524
|
-
const handleCameraChange =
|
|
1541
|
+
const handleCameraChange = React8.useCallback(
|
|
1525
1542
|
(position) => {
|
|
1526
1543
|
if (cameraChangeEvent) {
|
|
1527
1544
|
emit(cameraChangeEvent, {
|
|
@@ -1578,11 +1595,11 @@ function resolveSheetUrl(atlasUrl, relativeSheetPath) {
|
|
|
1578
1595
|
}
|
|
1579
1596
|
}
|
|
1580
1597
|
function useUnitSpriteAtlas(units) {
|
|
1581
|
-
const atlasCacheRef =
|
|
1582
|
-
const loadingRef =
|
|
1583
|
-
const [pendingCount, setPendingCount] =
|
|
1584
|
-
const [, forceTick] =
|
|
1585
|
-
const atlasUrls =
|
|
1598
|
+
const atlasCacheRef = React8.useRef(/* @__PURE__ */ new Map());
|
|
1599
|
+
const loadingRef = React8.useRef(/* @__PURE__ */ new Set());
|
|
1600
|
+
const [pendingCount, setPendingCount] = React8.useState(0);
|
|
1601
|
+
const [, forceTick] = React8.useState(0);
|
|
1602
|
+
const atlasUrls = React8.useMemo(() => {
|
|
1586
1603
|
const set = /* @__PURE__ */ new Set();
|
|
1587
1604
|
for (const unit of units) {
|
|
1588
1605
|
const url = unitAtlasUrl(unit);
|
|
@@ -1590,7 +1607,7 @@ function useUnitSpriteAtlas(units) {
|
|
|
1590
1607
|
}
|
|
1591
1608
|
return [...set];
|
|
1592
1609
|
}, [units]);
|
|
1593
|
-
|
|
1610
|
+
React8.useEffect(() => {
|
|
1594
1611
|
const cache = atlasCacheRef.current;
|
|
1595
1612
|
const loading = loadingRef.current;
|
|
1596
1613
|
const toLoad = atlasUrls.filter((url) => !cache.has(url) && !loading.has(url));
|
|
@@ -1614,7 +1631,7 @@ function useUnitSpriteAtlas(units) {
|
|
|
1614
1631
|
cancelled = true;
|
|
1615
1632
|
};
|
|
1616
1633
|
}, [atlasUrls]);
|
|
1617
|
-
const sheetUrls =
|
|
1634
|
+
const sheetUrls = React8.useMemo(() => {
|
|
1618
1635
|
const urls = /* @__PURE__ */ new Set();
|
|
1619
1636
|
for (const unit of units) {
|
|
1620
1637
|
const atlasUrl = unitAtlasUrl(unit);
|
|
@@ -1627,7 +1644,7 @@ function useUnitSpriteAtlas(units) {
|
|
|
1627
1644
|
}
|
|
1628
1645
|
return [...urls];
|
|
1629
1646
|
}, [units, pendingCount]);
|
|
1630
|
-
const resolveUnitFrame =
|
|
1647
|
+
const resolveUnitFrame = React8.useCallback((unitId) => {
|
|
1631
1648
|
const unit = units.find((u) => u.id === unitId);
|
|
1632
1649
|
if (!unit) return null;
|
|
1633
1650
|
const atlasUrl = unitAtlasUrl(unit);
|
|
@@ -1667,7 +1684,7 @@ function CameraController({
|
|
|
1667
1684
|
onCameraChange
|
|
1668
1685
|
}) {
|
|
1669
1686
|
const { camera } = fiber.useThree();
|
|
1670
|
-
|
|
1687
|
+
React8.useEffect(() => {
|
|
1671
1688
|
if (onCameraChange) {
|
|
1672
1689
|
onCameraChange({
|
|
1673
1690
|
x: camera.position.x,
|
|
@@ -1684,9 +1701,9 @@ function UnitSpriteBillboard({
|
|
|
1684
1701
|
height = 1.2
|
|
1685
1702
|
}) {
|
|
1686
1703
|
const texture = fiber.useLoader(THREE6__namespace.TextureLoader, sheetUrl);
|
|
1687
|
-
const meshRef =
|
|
1688
|
-
const matRef =
|
|
1689
|
-
const [aspect, setAspect] =
|
|
1704
|
+
const meshRef = React8.useRef(null);
|
|
1705
|
+
const matRef = React8.useRef(null);
|
|
1706
|
+
const [aspect, setAspect] = React8.useState(1);
|
|
1690
1707
|
fiber.useFrame(() => {
|
|
1691
1708
|
const frame = resolveFrame();
|
|
1692
1709
|
if (!frame || !texture.image) return;
|
|
@@ -1719,7 +1736,100 @@ function UnitSpriteBillboard({
|
|
|
1719
1736
|
)
|
|
1720
1737
|
] });
|
|
1721
1738
|
}
|
|
1722
|
-
|
|
1739
|
+
function FollowCamera({
|
|
1740
|
+
target,
|
|
1741
|
+
offset
|
|
1742
|
+
}) {
|
|
1743
|
+
const { camera } = fiber.useThree();
|
|
1744
|
+
const look = React8.useRef(new THREE6__namespace.Vector3(target[0], target[1], target[2]));
|
|
1745
|
+
const goal = React8.useRef(new THREE6__namespace.Vector3());
|
|
1746
|
+
fiber.useFrame((_, delta) => {
|
|
1747
|
+
const t = Math.min(1, delta * 5);
|
|
1748
|
+
goal.current.set(target[0] + offset[0], target[1] + offset[1], target[2] + offset[2]);
|
|
1749
|
+
camera.position.lerp(goal.current, t);
|
|
1750
|
+
look.current.lerp(goal.current.set(target[0], target[1], target[2]), t);
|
|
1751
|
+
camera.lookAt(look.current);
|
|
1752
|
+
});
|
|
1753
|
+
return null;
|
|
1754
|
+
}
|
|
1755
|
+
function LerpedGroup({
|
|
1756
|
+
target,
|
|
1757
|
+
enabled,
|
|
1758
|
+
children
|
|
1759
|
+
}) {
|
|
1760
|
+
const ref = React8.useRef(null);
|
|
1761
|
+
const goal = React8.useRef(new THREE6__namespace.Vector3());
|
|
1762
|
+
fiber.useFrame((_, delta) => {
|
|
1763
|
+
const g = ref.current;
|
|
1764
|
+
if (!g) return;
|
|
1765
|
+
goal.current.set(target[0], target[1], target[2]);
|
|
1766
|
+
if (enabled) g.position.lerp(goal.current, Math.min(1, delta * 10));
|
|
1767
|
+
else g.position.copy(goal.current);
|
|
1768
|
+
});
|
|
1769
|
+
return /* @__PURE__ */ jsxRuntime.jsx("group", { ref, position: target, children });
|
|
1770
|
+
}
|
|
1771
|
+
var SIDE_PLATFORM_COLORS = {
|
|
1772
|
+
ground: "#5b8c3e",
|
|
1773
|
+
platform: "#8b5a2b",
|
|
1774
|
+
hazard: "#cc4444",
|
|
1775
|
+
goal: "#4488cc"
|
|
1776
|
+
};
|
|
1777
|
+
function SideScene({
|
|
1778
|
+
player,
|
|
1779
|
+
platforms,
|
|
1780
|
+
worldHeight,
|
|
1781
|
+
ppu,
|
|
1782
|
+
playerSprite,
|
|
1783
|
+
tileSprites,
|
|
1784
|
+
interpolate
|
|
1785
|
+
}) {
|
|
1786
|
+
const pw = player.width ?? 32;
|
|
1787
|
+
const ph = player.height ?? 48;
|
|
1788
|
+
const playerPos = [
|
|
1789
|
+
(player.x + pw / 2) / ppu,
|
|
1790
|
+
(worldHeight - player.y - ph) / ppu,
|
|
1791
|
+
0
|
|
1792
|
+
];
|
|
1793
|
+
const playerH = Math.max(ph / ppu, 0.5);
|
|
1794
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("group", { children: [
|
|
1795
|
+
platforms.map((p, i) => {
|
|
1796
|
+
const platformType = p.type ?? "platform";
|
|
1797
|
+
const model = tileSprites?.[platformType]?.url;
|
|
1798
|
+
const topY = (worldHeight - p.y) / ppu;
|
|
1799
|
+
if (model) {
|
|
1800
|
+
const cells = Math.max(1, Math.round(p.width / ppu));
|
|
1801
|
+
const x0 = p.x / ppu;
|
|
1802
|
+
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}`);
|
|
1803
|
+
}
|
|
1804
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1805
|
+
"mesh",
|
|
1806
|
+
{
|
|
1807
|
+
position: [(p.x + p.width / 2) / ppu, topY - p.height / 2 / ppu, 0],
|
|
1808
|
+
children: [
|
|
1809
|
+
/* @__PURE__ */ jsxRuntime.jsx("boxGeometry", { args: [p.width / ppu, p.height / ppu, 1] }),
|
|
1810
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: SIDE_PLATFORM_COLORS[platformType] ?? "#888888" })
|
|
1811
|
+
]
|
|
1812
|
+
},
|
|
1813
|
+
`plat-${i}`
|
|
1814
|
+
);
|
|
1815
|
+
}),
|
|
1816
|
+
/* @__PURE__ */ jsxRuntime.jsx(LerpedGroup, { target: playerPos, enabled: interpolate, children: playerSprite?.url ? /* @__PURE__ */ jsxRuntime.jsx("group", { position: [0, playerH / 2, 0], children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1817
|
+
ModelLoader,
|
|
1818
|
+
{
|
|
1819
|
+
url: playerSprite.url,
|
|
1820
|
+
scale: playerH,
|
|
1821
|
+
rotation: [0, player.facingRight ? 90 : -90, 0],
|
|
1822
|
+
animation: player.animation ?? "idle",
|
|
1823
|
+
fallbackGeometry: "box",
|
|
1824
|
+
castShadow: true
|
|
1825
|
+
}
|
|
1826
|
+
) }) : /* @__PURE__ */ jsxRuntime.jsxs("mesh", { position: [0, playerH / 2, 0], children: [
|
|
1827
|
+
/* @__PURE__ */ jsxRuntime.jsx("capsuleGeometry", { args: [playerH * 0.25, playerH * 0.5, 4, 8] }),
|
|
1828
|
+
/* @__PURE__ */ jsxRuntime.jsx("meshStandardMaterial", { color: "#4488ff" })
|
|
1829
|
+
] }) })
|
|
1830
|
+
] });
|
|
1831
|
+
}
|
|
1832
|
+
var GameCanvas3D = React8.forwardRef(
|
|
1723
1833
|
({
|
|
1724
1834
|
tiles = [],
|
|
1725
1835
|
units = [],
|
|
@@ -1762,13 +1872,48 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
1762
1872
|
selectedTileIds = [],
|
|
1763
1873
|
selectedUnitId = null,
|
|
1764
1874
|
unitScale = 1,
|
|
1875
|
+
keyMap,
|
|
1876
|
+
keyUpMap,
|
|
1877
|
+
player,
|
|
1878
|
+
platforms = [],
|
|
1879
|
+
worldHeight = 400,
|
|
1880
|
+
pixelsPerUnit = 32,
|
|
1881
|
+
playerSprite,
|
|
1882
|
+
tileSprites,
|
|
1883
|
+
assetManifest,
|
|
1884
|
+
interpolateUnits = false,
|
|
1765
1885
|
children
|
|
1766
1886
|
}, ref) => {
|
|
1767
|
-
const containerRef =
|
|
1768
|
-
const controlsRef =
|
|
1769
|
-
const [hoveredTile, setHoveredTile] =
|
|
1770
|
-
const [internalError, setInternalError] =
|
|
1771
|
-
|
|
1887
|
+
const containerRef = React8.useRef(null);
|
|
1888
|
+
const controlsRef = React8.useRef(null);
|
|
1889
|
+
const [hoveredTile, setHoveredTile] = React8.useState(null);
|
|
1890
|
+
const [internalError, setInternalError] = React8.useState(null);
|
|
1891
|
+
const eventBus = useEventBus();
|
|
1892
|
+
const keysRef = React8.useRef(/* @__PURE__ */ new Set());
|
|
1893
|
+
React8.useEffect(() => {
|
|
1894
|
+
if (!keyMap && !keyUpMap) return;
|
|
1895
|
+
const down = (e) => {
|
|
1896
|
+
if (keysRef.current.has(e.code)) return;
|
|
1897
|
+
keysRef.current.add(e.code);
|
|
1898
|
+
const ev = keyMap?.[e.code];
|
|
1899
|
+
if (ev) {
|
|
1900
|
+
eventBus.emit(`UI:${ev}`, {});
|
|
1901
|
+
e.preventDefault();
|
|
1902
|
+
}
|
|
1903
|
+
};
|
|
1904
|
+
const up = (e) => {
|
|
1905
|
+
keysRef.current.delete(e.code);
|
|
1906
|
+
const ev = keyUpMap?.[e.code];
|
|
1907
|
+
if (ev) eventBus.emit(`UI:${ev}`, {});
|
|
1908
|
+
};
|
|
1909
|
+
window.addEventListener("keydown", down);
|
|
1910
|
+
window.addEventListener("keyup", up);
|
|
1911
|
+
return () => {
|
|
1912
|
+
window.removeEventListener("keydown", down);
|
|
1913
|
+
window.removeEventListener("keyup", up);
|
|
1914
|
+
};
|
|
1915
|
+
}, [keyMap, keyUpMap, eventBus]);
|
|
1916
|
+
React8.useEffect(() => {
|
|
1772
1917
|
const el = containerRef.current;
|
|
1773
1918
|
if (!el) return;
|
|
1774
1919
|
let node = el;
|
|
@@ -1792,7 +1937,7 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
1792
1937
|
}
|
|
1793
1938
|
}, []);
|
|
1794
1939
|
const { sheetUrls: atlasSheetUrls, resolveUnitFrame } = useUnitSpriteAtlas(units);
|
|
1795
|
-
const preloadUrls =
|
|
1940
|
+
const preloadUrls = React8.useMemo(() => [...preloadAssets, ...atlasSheetUrls], [preloadAssets, atlasSheetUrls]);
|
|
1796
1941
|
const { isLoading: assetsLoading, progress, loaded, total } = useAssetLoader({
|
|
1797
1942
|
preloadUrls,
|
|
1798
1943
|
loader: customAssetLoader
|
|
@@ -1813,7 +1958,7 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
1813
1958
|
onTileHover,
|
|
1814
1959
|
onUnitAnimation
|
|
1815
1960
|
});
|
|
1816
|
-
const gridBounds =
|
|
1961
|
+
const gridBounds = React8.useMemo(() => {
|
|
1817
1962
|
if (tiles.length === 0) {
|
|
1818
1963
|
return { minX: 0, maxX: 10, minZ: 0, maxZ: 10 };
|
|
1819
1964
|
}
|
|
@@ -1826,14 +1971,14 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
1826
1971
|
maxZ: Math.max(...zs)
|
|
1827
1972
|
};
|
|
1828
1973
|
}, [tiles]);
|
|
1829
|
-
const cameraTarget =
|
|
1974
|
+
const cameraTarget = React8.useMemo(() => {
|
|
1830
1975
|
return [
|
|
1831
1976
|
(gridBounds.minX + gridBounds.maxX) / 2,
|
|
1832
1977
|
0,
|
|
1833
1978
|
(gridBounds.minZ + gridBounds.maxZ) / 2
|
|
1834
1979
|
];
|
|
1835
1980
|
}, [gridBounds]);
|
|
1836
|
-
const gridConfig =
|
|
1981
|
+
const gridConfig = React8.useMemo(
|
|
1837
1982
|
() => ({
|
|
1838
1983
|
...DEFAULT_GRID_CONFIG,
|
|
1839
1984
|
offsetX: -(gridBounds.maxX - gridBounds.minX) / 2,
|
|
@@ -1841,7 +1986,7 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
1841
1986
|
}),
|
|
1842
1987
|
[gridBounds]
|
|
1843
1988
|
);
|
|
1844
|
-
const gridToWorld2 =
|
|
1989
|
+
const gridToWorld2 = React8.useCallback(
|
|
1845
1990
|
(x, z, y = 0) => {
|
|
1846
1991
|
const worldX = (x - gridBounds.minX) * gridConfig.cellSize;
|
|
1847
1992
|
const worldZ = (z - gridBounds.minZ) * gridConfig.cellSize;
|
|
@@ -1849,7 +1994,7 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
1849
1994
|
},
|
|
1850
1995
|
[gridBounds, gridConfig]
|
|
1851
1996
|
);
|
|
1852
|
-
|
|
1997
|
+
React8.useImperativeHandle(ref, () => ({
|
|
1853
1998
|
getCameraPosition: () => {
|
|
1854
1999
|
if (controlsRef.current) {
|
|
1855
2000
|
const pos = controlsRef.current.object.position;
|
|
@@ -1887,19 +2032,19 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
1887
2032
|
features
|
|
1888
2033
|
})
|
|
1889
2034
|
}));
|
|
1890
|
-
const handleTileClick =
|
|
2035
|
+
const handleTileClick = React8.useCallback(
|
|
1891
2036
|
(tile, event) => {
|
|
1892
2037
|
eventHandlers.handleTileClick(tile, event);
|
|
1893
2038
|
},
|
|
1894
2039
|
[eventHandlers]
|
|
1895
2040
|
);
|
|
1896
|
-
const handleUnitClick =
|
|
2041
|
+
const handleUnitClick = React8.useCallback(
|
|
1897
2042
|
(unit, event) => {
|
|
1898
2043
|
eventHandlers.handleUnitClick(unit, event);
|
|
1899
2044
|
},
|
|
1900
2045
|
[eventHandlers]
|
|
1901
2046
|
);
|
|
1902
|
-
const handleFeatureClick =
|
|
2047
|
+
const handleFeatureClick = React8.useCallback(
|
|
1903
2048
|
(feature, event) => {
|
|
1904
2049
|
if (event) {
|
|
1905
2050
|
eventHandlers.handleFeatureClick(feature, event);
|
|
@@ -1907,7 +2052,7 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
1907
2052
|
},
|
|
1908
2053
|
[eventHandlers]
|
|
1909
2054
|
);
|
|
1910
|
-
const handleTileHover =
|
|
2055
|
+
const handleTileHover = React8.useCallback(
|
|
1911
2056
|
(tile, event) => {
|
|
1912
2057
|
setHoveredTile(tile);
|
|
1913
2058
|
if (event) {
|
|
@@ -1916,7 +2061,7 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
1916
2061
|
},
|
|
1917
2062
|
[eventHandlers]
|
|
1918
2063
|
);
|
|
1919
|
-
const cameraConfig =
|
|
2064
|
+
const cameraConfig = React8.useMemo(() => {
|
|
1920
2065
|
const size = Math.max(
|
|
1921
2066
|
gridBounds.maxX - gridBounds.minX,
|
|
1922
2067
|
gridBounds.maxZ - gridBounds.minZ,
|
|
@@ -1937,6 +2082,11 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
1937
2082
|
position: [cx, d * 2, cz],
|
|
1938
2083
|
fov: 45
|
|
1939
2084
|
};
|
|
2085
|
+
case "follow":
|
|
2086
|
+
return {
|
|
2087
|
+
position: [cx, d * 0.5, cz + d],
|
|
2088
|
+
fov: 45
|
|
2089
|
+
};
|
|
1940
2090
|
case "perspective":
|
|
1941
2091
|
default:
|
|
1942
2092
|
return {
|
|
@@ -1945,7 +2095,27 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
1945
2095
|
};
|
|
1946
2096
|
}
|
|
1947
2097
|
}, [cameraMode, gridBounds]);
|
|
1948
|
-
const
|
|
2098
|
+
const followTarget = React8.useMemo(() => {
|
|
2099
|
+
if (player) {
|
|
2100
|
+
return [
|
|
2101
|
+
(player.x + (player.width ?? 32) / 2) / pixelsPerUnit,
|
|
2102
|
+
(worldHeight - player.y - (player.height ?? 48) / 2) / pixelsPerUnit,
|
|
2103
|
+
0
|
|
2104
|
+
];
|
|
2105
|
+
}
|
|
2106
|
+
const selected = units.find((u) => u.id === selectedUnitId);
|
|
2107
|
+
if (selected) {
|
|
2108
|
+
const sx = selected.x ?? selected.position?.x ?? 0;
|
|
2109
|
+
const sz = selected.z ?? selected.y ?? selected.position?.y ?? 0;
|
|
2110
|
+
return [
|
|
2111
|
+
(sx - gridBounds.minX) * gridConfig.cellSize,
|
|
2112
|
+
0,
|
|
2113
|
+
(sz - gridBounds.minZ) * gridConfig.cellSize
|
|
2114
|
+
];
|
|
2115
|
+
}
|
|
2116
|
+
return cameraTarget;
|
|
2117
|
+
}, [player, pixelsPerUnit, worldHeight, units, selectedUnitId, gridBounds, gridConfig, cameraTarget]);
|
|
2118
|
+
const DefaultTileRenderer = React8.useCallback(
|
|
1949
2119
|
({ tile, position }) => {
|
|
1950
2120
|
const isSelected = tile.id ? selectedTileIds.includes(tile.id) : false;
|
|
1951
2121
|
const isHovered = hoveredTile?.id === tile.id;
|
|
@@ -1966,7 +2136,8 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
1966
2136
|
else if (isAttackTarget) emissive = 4456448;
|
|
1967
2137
|
else if (isValidMove) emissive = 17408;
|
|
1968
2138
|
else if (isHovered) emissive = 2236962;
|
|
1969
|
-
|
|
2139
|
+
const tileModel = tile.modelUrl ?? assetManifest?.terrains?.[tile.terrain ?? tile.type ?? ""];
|
|
2140
|
+
if (tileModel?.url) {
|
|
1970
2141
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1971
2142
|
"group",
|
|
1972
2143
|
{
|
|
@@ -1978,7 +2149,7 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
1978
2149
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1979
2150
|
ModelLoader,
|
|
1980
2151
|
{
|
|
1981
|
-
url:
|
|
2152
|
+
url: tileModel.url,
|
|
1982
2153
|
scale: 0.95,
|
|
1983
2154
|
fallbackGeometry: "box",
|
|
1984
2155
|
castShadow: true,
|
|
@@ -2003,17 +2174,18 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
2003
2174
|
}
|
|
2004
2175
|
);
|
|
2005
2176
|
},
|
|
2006
|
-
[selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover]
|
|
2177
|
+
[selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover, assetManifest]
|
|
2007
2178
|
);
|
|
2008
2179
|
const UNIT_BASE_MODEL_SCALE = 0.5;
|
|
2009
2180
|
const UNIT_BASE_BILLBOARD_HEIGHT = 1.2;
|
|
2010
2181
|
const UNIT_BASE_PRIMITIVE_RADIUS = 0.3;
|
|
2011
|
-
const DefaultUnitRenderer =
|
|
2182
|
+
const DefaultUnitRenderer = React8.useCallback(
|
|
2012
2183
|
({ unit, position }) => {
|
|
2013
2184
|
const isSelected = selectedUnitId === unit.id;
|
|
2014
2185
|
const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
|
|
2015
2186
|
const hasAtlas = unitAtlasUrl(unit) !== null;
|
|
2016
2187
|
const initialFrame = hasAtlas ? resolveUnitFrame(unit.id) : null;
|
|
2188
|
+
const unitModel = unit.modelUrl ?? assetManifest?.units?.[unit.unitType ?? ""];
|
|
2017
2189
|
const modelScale = UNIT_BASE_MODEL_SCALE * unitScale * gridConfig.cellSize;
|
|
2018
2190
|
const billboardHeight = UNIT_BASE_BILLBOARD_HEIGHT * unitScale * gridConfig.cellSize;
|
|
2019
2191
|
const primitiveRadius = UNIT_BASE_PRIMITIVE_RADIUS * unitScale * gridConfig.cellSize;
|
|
@@ -2038,13 +2210,14 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
2038
2210
|
height: billboardHeight
|
|
2039
2211
|
}
|
|
2040
2212
|
) })
|
|
2041
|
-
) :
|
|
2042
|
-
/* GLB unit model
|
|
2213
|
+
) : unitModel?.url ? (
|
|
2214
|
+
/* GLB unit model — LOLO's `animation` field drives the named clip */
|
|
2043
2215
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2044
2216
|
ModelLoader,
|
|
2045
2217
|
{
|
|
2046
|
-
url:
|
|
2218
|
+
url: unitModel.url,
|
|
2047
2219
|
scale: modelScale,
|
|
2220
|
+
animation: unit.animation ?? "idle",
|
|
2048
2221
|
fallbackGeometry: "box",
|
|
2049
2222
|
castShadow: true
|
|
2050
2223
|
}
|
|
@@ -2092,18 +2265,19 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
2092
2265
|
}
|
|
2093
2266
|
);
|
|
2094
2267
|
},
|
|
2095
|
-
[selectedUnitId, handleUnitClick, resolveUnitFrame, unitScale, gridConfig]
|
|
2268
|
+
[selectedUnitId, handleUnitClick, resolveUnitFrame, unitScale, gridConfig, assetManifest]
|
|
2096
2269
|
);
|
|
2097
|
-
const DefaultFeatureRenderer =
|
|
2270
|
+
const DefaultFeatureRenderer = React8.useCallback(
|
|
2098
2271
|
({
|
|
2099
2272
|
feature,
|
|
2100
2273
|
position
|
|
2101
2274
|
}) => {
|
|
2102
|
-
|
|
2275
|
+
const featureModel = feature.assetUrl ?? assetManifest?.features?.[feature.type];
|
|
2276
|
+
if (featureModel?.url) {
|
|
2103
2277
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2104
2278
|
ModelLoader,
|
|
2105
2279
|
{
|
|
2106
|
-
url:
|
|
2280
|
+
url: featureModel.url,
|
|
2107
2281
|
position,
|
|
2108
2282
|
scale: 0.5,
|
|
2109
2283
|
rotation: [0, feature.rotation ?? 0, 0],
|
|
@@ -2149,7 +2323,7 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
2149
2323
|
}
|
|
2150
2324
|
return null;
|
|
2151
2325
|
},
|
|
2152
|
-
[handleFeatureClick]
|
|
2326
|
+
[handleFeatureClick, assetManifest]
|
|
2153
2327
|
);
|
|
2154
2328
|
if (externalLoading || assetsLoading && preloadAssets.length > 0) {
|
|
2155
2329
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2203,6 +2377,13 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
2203
2377
|
},
|
|
2204
2378
|
children: [
|
|
2205
2379
|
/* @__PURE__ */ jsxRuntime.jsx(CameraController, { onCameraChange: eventHandlers.handleCameraChange }),
|
|
2380
|
+
cameraMode === "follow" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2381
|
+
FollowCamera,
|
|
2382
|
+
{
|
|
2383
|
+
target: followTarget,
|
|
2384
|
+
offset: player ? [0, 2, 9] : [5, 7, 5]
|
|
2385
|
+
}
|
|
2386
|
+
),
|
|
2206
2387
|
/* @__PURE__ */ jsxRuntime.jsx("ambientLight", { intensity: 0.6 }),
|
|
2207
2388
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2208
2389
|
"directionalLight",
|
|
@@ -2210,7 +2391,9 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
2210
2391
|
position: [10, 20, 10],
|
|
2211
2392
|
intensity: 0.8,
|
|
2212
2393
|
castShadow: shadows,
|
|
2213
|
-
"shadow-mapSize": [2048, 2048]
|
|
2394
|
+
"shadow-mapSize": [2048, 2048],
|
|
2395
|
+
"shadow-bias": -4e-4,
|
|
2396
|
+
"shadow-normalBias": 0.04
|
|
2214
2397
|
}
|
|
2215
2398
|
),
|
|
2216
2399
|
/* @__PURE__ */ jsxRuntime.jsx("hemisphereLight", { intensity: 0.3, color: "#87ceeb", groundColor: "#362d1d" }),
|
|
@@ -2236,7 +2419,21 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
2236
2419
|
fadeStrength: 1
|
|
2237
2420
|
}
|
|
2238
2421
|
),
|
|
2239
|
-
|
|
2422
|
+
player ? (
|
|
2423
|
+
/* Side-scroller mode — LOLO-owned pixel space mapped to world units */
|
|
2424
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2425
|
+
SideScene,
|
|
2426
|
+
{
|
|
2427
|
+
player,
|
|
2428
|
+
platforms,
|
|
2429
|
+
worldHeight,
|
|
2430
|
+
ppu: pixelsPerUnit,
|
|
2431
|
+
playerSprite,
|
|
2432
|
+
tileSprites,
|
|
2433
|
+
interpolate: interpolateUnits
|
|
2434
|
+
}
|
|
2435
|
+
)
|
|
2436
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs("group", { children: [
|
|
2240
2437
|
tiles.map((tile, index) => {
|
|
2241
2438
|
const position = gridToWorld2(
|
|
2242
2439
|
tile.x,
|
|
@@ -2257,12 +2454,12 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
2257
2454
|
}),
|
|
2258
2455
|
units.map((unit) => {
|
|
2259
2456
|
const position = gridToWorld2(
|
|
2260
|
-
unit.x ?? 0,
|
|
2261
|
-
unit.z ?? unit.y ?? 0,
|
|
2457
|
+
unit.x ?? unit.position?.x ?? 0,
|
|
2458
|
+
unit.z ?? unit.y ?? unit.position?.y ?? 0,
|
|
2262
2459
|
(unit.elevation ?? 0) + 0.5
|
|
2263
2460
|
);
|
|
2264
2461
|
const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
|
|
2265
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Renderer, { unit, position }, unit.id);
|
|
2462
|
+
return /* @__PURE__ */ jsxRuntime.jsx(LerpedGroup, { target: position, enabled: interpolateUnits, children: /* @__PURE__ */ jsxRuntime.jsx(Renderer, { unit, position: [0, 0, 0] }) }, unit.id);
|
|
2266
2463
|
})
|
|
2267
2464
|
] }),
|
|
2268
2465
|
children,
|
|
@@ -2270,6 +2467,7 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
2270
2467
|
drei.OrbitControls,
|
|
2271
2468
|
{
|
|
2272
2469
|
ref: controlsRef,
|
|
2470
|
+
enabled: cameraMode !== "follow",
|
|
2273
2471
|
target: cameraTarget,
|
|
2274
2472
|
enableDamping: true,
|
|
2275
2473
|
dampingFactor: 0.05,
|
|
@@ -2302,9 +2500,6 @@ var GameCanvas3D = React6.forwardRef(
|
|
|
2302
2500
|
}
|
|
2303
2501
|
);
|
|
2304
2502
|
GameCanvas3D.displayName = "GameCanvas3D";
|
|
2305
|
-
function Canvas3D(props) {
|
|
2306
|
-
return /* @__PURE__ */ jsxRuntime.jsx(GameCanvas3D, { ...props });
|
|
2307
|
-
}
|
|
2308
2503
|
var DEFAULT_FAMILY = "lucide";
|
|
2309
2504
|
var VALID_FAMILIES = [
|
|
2310
2505
|
"lucide",
|
|
@@ -2355,7 +2550,7 @@ function getIconFamilyServerSnapshot() {
|
|
|
2355
2550
|
return DEFAULT_FAMILY;
|
|
2356
2551
|
}
|
|
2357
2552
|
function useIconFamily() {
|
|
2358
|
-
return
|
|
2553
|
+
return React8.useSyncExternalStore(
|
|
2359
2554
|
subscribeIconFamily,
|
|
2360
2555
|
getIconFamilySnapshot,
|
|
2361
2556
|
getIconFamilyServerSnapshot
|
|
@@ -2377,7 +2572,7 @@ function loadLib(key, importer) {
|
|
|
2377
2572
|
return p;
|
|
2378
2573
|
}
|
|
2379
2574
|
function lazyFamilyIcon(libKey, importer, pick, fallbackName, family) {
|
|
2380
|
-
const Lazy =
|
|
2575
|
+
const Lazy = React8__namespace.default.lazy(async () => {
|
|
2381
2576
|
const lib = await loadLib(libKey, importer);
|
|
2382
2577
|
const Comp = pick(lib);
|
|
2383
2578
|
if (!Comp) {
|
|
@@ -2387,7 +2582,7 @@ function lazyFamilyIcon(libKey, importer, pick, fallbackName, family) {
|
|
|
2387
2582
|
return { default: Comp };
|
|
2388
2583
|
});
|
|
2389
2584
|
const Wrapped = (props) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2390
|
-
|
|
2585
|
+
React8__namespace.default.Suspense,
|
|
2391
2586
|
{
|
|
2392
2587
|
fallback: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2393
2588
|
"span",
|
|
@@ -3056,7 +3251,7 @@ var Icon = ({
|
|
|
3056
3251
|
const directIcon = typeof icon === "string" ? void 0 : icon;
|
|
3057
3252
|
const effectiveName = typeof icon === "string" ? icon : name;
|
|
3058
3253
|
const family = useIconFamily();
|
|
3059
|
-
const RenderedComponent =
|
|
3254
|
+
const RenderedComponent = React8__namespace.default.useMemo(() => {
|
|
3060
3255
|
if (directIcon) return null;
|
|
3061
3256
|
return effectiveName ? resolveIconForFamily(effectiveName, family) : null;
|
|
3062
3257
|
}, [directIcon, effectiveName, family]);
|
|
@@ -3104,6 +3299,143 @@ var Icon = ({
|
|
|
3104
3299
|
);
|
|
3105
3300
|
};
|
|
3106
3301
|
Icon.displayName = "Icon";
|
|
3302
|
+
|
|
3303
|
+
// lib/atlasSlice.ts
|
|
3304
|
+
var atlasCache = /* @__PURE__ */ new Map();
|
|
3305
|
+
function isTilesheet(a) {
|
|
3306
|
+
return typeof a.tileWidth === "number";
|
|
3307
|
+
}
|
|
3308
|
+
function getAtlas(url, onReady) {
|
|
3309
|
+
if (atlasCache.has(url)) return atlasCache.get(url) ?? void 0;
|
|
3310
|
+
atlasCache.set(url, void 0);
|
|
3311
|
+
fetch(url).then((r) => r.json()).then((json) => {
|
|
3312
|
+
atlasCache.set(url, json);
|
|
3313
|
+
onReady();
|
|
3314
|
+
}).catch(() => {
|
|
3315
|
+
atlasCache.set(url, null);
|
|
3316
|
+
});
|
|
3317
|
+
return void 0;
|
|
3318
|
+
}
|
|
3319
|
+
function subRectFor(atlas, sprite) {
|
|
3320
|
+
if (isTilesheet(atlas)) {
|
|
3321
|
+
let col;
|
|
3322
|
+
let row;
|
|
3323
|
+
if (sprite.includes(",")) {
|
|
3324
|
+
const [c, r] = sprite.split(",").map((n) => Number(n.trim()));
|
|
3325
|
+
col = c;
|
|
3326
|
+
row = r;
|
|
3327
|
+
} else {
|
|
3328
|
+
const i = Number(sprite);
|
|
3329
|
+
if (!Number.isFinite(i)) return null;
|
|
3330
|
+
col = i % atlas.columns;
|
|
3331
|
+
row = Math.floor(i / atlas.columns);
|
|
3332
|
+
}
|
|
3333
|
+
if (!Number.isFinite(col) || !Number.isFinite(row) || col < 0 || row < 0 || col >= atlas.columns || row >= atlas.rows) return null;
|
|
3334
|
+
const margin = atlas.margin ?? 0;
|
|
3335
|
+
const spacing = atlas.spacing ?? 0;
|
|
3336
|
+
return {
|
|
3337
|
+
sx: margin + col * (atlas.tileWidth + spacing),
|
|
3338
|
+
sy: margin + row * (atlas.tileHeight + spacing),
|
|
3339
|
+
sw: atlas.tileWidth,
|
|
3340
|
+
sh: atlas.tileHeight
|
|
3341
|
+
};
|
|
3342
|
+
}
|
|
3343
|
+
const st = atlas.subTextures[sprite];
|
|
3344
|
+
if (!st) return null;
|
|
3345
|
+
return { sx: st.x, sy: st.y, sw: st.width, sh: st.height };
|
|
3346
|
+
}
|
|
3347
|
+
function isAtlasAsset(asset) {
|
|
3348
|
+
return !!asset && typeof asset.atlas === "string" && typeof asset.sprite === "string";
|
|
3349
|
+
}
|
|
3350
|
+
var imageCache = /* @__PURE__ */ new Map();
|
|
3351
|
+
var imageWaiters = /* @__PURE__ */ new Map();
|
|
3352
|
+
function getSheetImage(url, onReady) {
|
|
3353
|
+
const cached = imageCache.get(url);
|
|
3354
|
+
if (cached) return cached;
|
|
3355
|
+
(imageWaiters.get(url) ?? imageWaiters.set(url, /* @__PURE__ */ new Set()).get(url)).add(onReady);
|
|
3356
|
+
if (!imageCache.has(url)) {
|
|
3357
|
+
imageCache.set(url, null);
|
|
3358
|
+
const img = new Image();
|
|
3359
|
+
img.crossOrigin = "anonymous";
|
|
3360
|
+
img.onload = () => {
|
|
3361
|
+
imageCache.set(url, img);
|
|
3362
|
+
imageWaiters.get(url)?.forEach((fn) => fn());
|
|
3363
|
+
imageWaiters.delete(url);
|
|
3364
|
+
};
|
|
3365
|
+
img.src = url;
|
|
3366
|
+
}
|
|
3367
|
+
return null;
|
|
3368
|
+
}
|
|
3369
|
+
function AtlasImage({
|
|
3370
|
+
asset,
|
|
3371
|
+
size,
|
|
3372
|
+
width,
|
|
3373
|
+
height,
|
|
3374
|
+
fill = false,
|
|
3375
|
+
fit = "contain",
|
|
3376
|
+
alt,
|
|
3377
|
+
className,
|
|
3378
|
+
style,
|
|
3379
|
+
"aria-hidden": ariaHidden
|
|
3380
|
+
}) {
|
|
3381
|
+
const [, bump] = React8__namespace.useReducer((x) => x + 1, 0);
|
|
3382
|
+
const canvasRef = React8__namespace.useRef(null);
|
|
3383
|
+
const sliced = isAtlasAsset(asset);
|
|
3384
|
+
const atlas = sliced ? getAtlas(asset.atlas, bump) : void 0;
|
|
3385
|
+
const img = sliced && asset?.url ? getSheetImage(asset.url, bump) : null;
|
|
3386
|
+
const rect = sliced && atlas ? subRectFor(atlas, asset.sprite) : null;
|
|
3387
|
+
React8__namespace.useEffect(() => {
|
|
3388
|
+
const canvas = canvasRef.current;
|
|
3389
|
+
if (!canvas || !img || !rect) return;
|
|
3390
|
+
canvas.width = rect.sw;
|
|
3391
|
+
canvas.height = rect.sh;
|
|
3392
|
+
const ctx = canvas.getContext("2d");
|
|
3393
|
+
if (!ctx) return;
|
|
3394
|
+
ctx.imageSmoothingEnabled = false;
|
|
3395
|
+
ctx.clearRect(0, 0, rect.sw, rect.sh);
|
|
3396
|
+
ctx.drawImage(img, rect.sx, rect.sy, rect.sw, rect.sh, 0, 0, rect.sw, rect.sh);
|
|
3397
|
+
}, [img, rect?.sx, rect?.sy, rect?.sw, rect?.sh]);
|
|
3398
|
+
const w = fill ? "100%" : width ?? size;
|
|
3399
|
+
const h = fill ? "100%" : height ?? size;
|
|
3400
|
+
const boxStyle = {
|
|
3401
|
+
...fill ? { position: "absolute", inset: 0 } : {},
|
|
3402
|
+
width: w,
|
|
3403
|
+
height: h,
|
|
3404
|
+
objectFit: fit,
|
|
3405
|
+
imageRendering: "pixelated",
|
|
3406
|
+
...style
|
|
3407
|
+
};
|
|
3408
|
+
if (!asset?.url) return null;
|
|
3409
|
+
if (sliced) {
|
|
3410
|
+
if (!rect || !img) {
|
|
3411
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: cn("inline-block flex-shrink-0", className), style: { ...boxStyle, objectFit: void 0 } });
|
|
3412
|
+
}
|
|
3413
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3414
|
+
"canvas",
|
|
3415
|
+
{
|
|
3416
|
+
ref: canvasRef,
|
|
3417
|
+
role: ariaHidden ? void 0 : "img",
|
|
3418
|
+
"aria-hidden": ariaHidden,
|
|
3419
|
+
"aria-label": ariaHidden ? void 0 : alt ?? asset.name ?? asset.category ?? "",
|
|
3420
|
+
className: cn("flex-shrink-0", className),
|
|
3421
|
+
style: boxStyle
|
|
3422
|
+
}
|
|
3423
|
+
);
|
|
3424
|
+
}
|
|
3425
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3426
|
+
"img",
|
|
3427
|
+
{
|
|
3428
|
+
src: asset.url,
|
|
3429
|
+
alt: alt ?? asset.name ?? asset.category ?? "",
|
|
3430
|
+
"aria-hidden": ariaHidden,
|
|
3431
|
+
...typeof w === "number" ? { width: w } : {},
|
|
3432
|
+
...typeof h === "number" ? { height: h } : {},
|
|
3433
|
+
className: cn("flex-shrink-0", className),
|
|
3434
|
+
style: boxStyle
|
|
3435
|
+
}
|
|
3436
|
+
);
|
|
3437
|
+
}
|
|
3438
|
+
AtlasImage.displayName = "AtlasImage";
|
|
3107
3439
|
var variantStyles = {
|
|
3108
3440
|
primary: [
|
|
3109
3441
|
"bg-primary text-primary-foreground",
|
|
@@ -3176,7 +3508,7 @@ function resolveIconProp(value, sizeClass) {
|
|
|
3176
3508
|
const IconComp = value;
|
|
3177
3509
|
return /* @__PURE__ */ jsxRuntime.jsx(IconComp, { className: sizeClass });
|
|
3178
3510
|
}
|
|
3179
|
-
if (
|
|
3511
|
+
if (React8__namespace.default.isValidElement(value)) {
|
|
3180
3512
|
return value;
|
|
3181
3513
|
}
|
|
3182
3514
|
if (typeof value === "object" && value !== null && isIconLike(value)) {
|
|
@@ -3185,7 +3517,7 @@ function resolveIconProp(value, sizeClass) {
|
|
|
3185
3517
|
}
|
|
3186
3518
|
return value;
|
|
3187
3519
|
}
|
|
3188
|
-
var Button =
|
|
3520
|
+
var Button = React8__namespace.default.forwardRef(
|
|
3189
3521
|
({
|
|
3190
3522
|
className,
|
|
3191
3523
|
variant = "primary",
|
|
@@ -3209,7 +3541,7 @@ var Button = React6__default.default.forwardRef(
|
|
|
3209
3541
|
const leftIconValue = leftIcon || iconProp;
|
|
3210
3542
|
const rightIconValue = rightIcon || iconRightProp;
|
|
3211
3543
|
const px = size === "sm" ? 16 : size === "lg" ? 20 : 16;
|
|
3212
|
-
const resolvedLeftIcon = iconAsset?.url ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3544
|
+
const resolvedLeftIcon = iconAsset?.url ? /* @__PURE__ */ jsxRuntime.jsx(AtlasImage, { asset: iconAsset, size: px, alt: iconAsset.name ?? iconAsset.category ?? "", className: "flex-shrink-0" }) : resolveIconProp(leftIconValue, iconSizeStyles[size]);
|
|
3213
3545
|
const resolvedRightIcon = resolveIconProp(rightIconValue, iconSizeStyles[size]);
|
|
3214
3546
|
const handleClick = (e) => {
|
|
3215
3547
|
if (action) {
|
|
@@ -3333,7 +3665,7 @@ var Typography = ({
|
|
|
3333
3665
|
}) => {
|
|
3334
3666
|
const variant = variantProp ?? (level ? `h${level}` : "body1");
|
|
3335
3667
|
const Component2 = as || defaultElements[variant];
|
|
3336
|
-
return
|
|
3668
|
+
return React8__namespace.default.createElement(
|
|
3337
3669
|
Component2,
|
|
3338
3670
|
{
|
|
3339
3671
|
id,
|
|
@@ -3406,7 +3738,7 @@ var Stack = ({
|
|
|
3406
3738
|
};
|
|
3407
3739
|
const isHorizontal = direction === "horizontal";
|
|
3408
3740
|
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";
|
|
3409
|
-
return
|
|
3741
|
+
return React8__namespace.default.createElement(
|
|
3410
3742
|
Component2,
|
|
3411
3743
|
{
|
|
3412
3744
|
className: cn(
|
|
@@ -3499,8 +3831,8 @@ function GameBoard3D({
|
|
|
3499
3831
|
const turn = row ? num(row.turn) : 0;
|
|
3500
3832
|
const currentTeam = row ? str(row.currentTeam) : "player";
|
|
3501
3833
|
const isGameOver = result !== "none";
|
|
3502
|
-
const gameEndEmittedRef =
|
|
3503
|
-
|
|
3834
|
+
const gameEndEmittedRef = React8.useRef(false);
|
|
3835
|
+
React8.useEffect(() => {
|
|
3504
3836
|
if ((result === "victory" || result === "defeat") && gameEndEvent) {
|
|
3505
3837
|
if (!gameEndEmittedRef.current) {
|
|
3506
3838
|
gameEndEmittedRef.current = true;
|
|
@@ -3510,7 +3842,7 @@ function GameBoard3D({
|
|
|
3510
3842
|
gameEndEmittedRef.current = false;
|
|
3511
3843
|
}
|
|
3512
3844
|
}, [result, gameEndEvent, eventBus]);
|
|
3513
|
-
const checkGameEnd =
|
|
3845
|
+
const checkGameEnd = React8.useCallback(() => {
|
|
3514
3846
|
const alivePlayer = entityUnits.filter((u) => unitTeam(u) === "player" && unitHealth(u) > 0);
|
|
3515
3847
|
const aliveEnemy = entityUnits.filter((u) => unitTeam(u) === "enemy" && unitHealth(u) > 0);
|
|
3516
3848
|
if (alivePlayer.length === 0 && gameEndEvent) {
|
|
@@ -3519,7 +3851,7 @@ function GameBoard3D({
|
|
|
3519
3851
|
eventBus.emit(`UI:${gameEndEvent}`, { result: "victory" });
|
|
3520
3852
|
}
|
|
3521
3853
|
}, [entityUnits, gameEndEvent, eventBus]);
|
|
3522
|
-
const handleUnitClickCallback =
|
|
3854
|
+
const handleUnitClickCallback = React8.useCallback((isoUnit) => {
|
|
3523
3855
|
if (phase !== "action" || !selectedUnitId || !attackEvent) return;
|
|
3524
3856
|
const attackerRow = entityUnits.find((u) => str(u.id) === selectedUnitId);
|
|
3525
3857
|
const targetRow = entityUnits.find((u) => str(u.id) === isoUnit.id);
|
|
@@ -3539,13 +3871,13 @@ function GameBoard3D({
|
|
|
3539
3871
|
setTimeout(checkGameEnd, 100);
|
|
3540
3872
|
}
|
|
3541
3873
|
}, [phase, selectedUnitId, entityUnits, attackEvent, eventBus, checkGameEnd]);
|
|
3542
|
-
const handleEndTurn =
|
|
3874
|
+
const handleEndTurn = React8.useCallback(() => {
|
|
3543
3875
|
if (endTurnEvent) eventBus.emit(`UI:${endTurnEvent}`, {});
|
|
3544
3876
|
}, [endTurnEvent, eventBus]);
|
|
3545
|
-
const handleCancel =
|
|
3877
|
+
const handleCancel = React8.useCallback(() => {
|
|
3546
3878
|
if (cancelEvent) eventBus.emit(`UI:${cancelEvent}`, {});
|
|
3547
3879
|
}, [cancelEvent, eventBus]);
|
|
3548
|
-
const handlePlayAgain =
|
|
3880
|
+
const handlePlayAgain = React8.useCallback(() => {
|
|
3549
3881
|
if (playAgainEvent) eventBus.emit(`UI:${playAgainEvent}`, {});
|
|
3550
3882
|
}, [playAgainEvent, eventBus]);
|
|
3551
3883
|
return /* @__PURE__ */ jsxRuntime.jsxs(VStack, { className: cn("game-board-3d block w-full min-h-[85vh] relative", className), children: [
|
|
@@ -3636,24 +3968,24 @@ function GameBoard3D({
|
|
|
3636
3968
|
GameBoard3D.displayName = "GameBoard3D";
|
|
3637
3969
|
function useTapReveal(options = {}) {
|
|
3638
3970
|
const { onReveal, onDismiss, refs, enabled = true } = options;
|
|
3639
|
-
const [revealed, setRevealed] =
|
|
3640
|
-
const onRevealRef =
|
|
3641
|
-
const onDismissRef =
|
|
3971
|
+
const [revealed, setRevealed] = React8.useState(false);
|
|
3972
|
+
const onRevealRef = React8.useRef(onReveal);
|
|
3973
|
+
const onDismissRef = React8.useRef(onDismiss);
|
|
3642
3974
|
onRevealRef.current = onReveal;
|
|
3643
3975
|
onDismissRef.current = onDismiss;
|
|
3644
|
-
const reveal =
|
|
3976
|
+
const reveal = React8.useCallback(() => {
|
|
3645
3977
|
setRevealed((wasRevealed) => {
|
|
3646
3978
|
if (!wasRevealed) onRevealRef.current?.();
|
|
3647
3979
|
return true;
|
|
3648
3980
|
});
|
|
3649
3981
|
}, []);
|
|
3650
|
-
const dismiss =
|
|
3982
|
+
const dismiss = React8.useCallback(() => {
|
|
3651
3983
|
setRevealed((wasRevealed) => {
|
|
3652
3984
|
if (wasRevealed) onDismissRef.current?.();
|
|
3653
3985
|
return false;
|
|
3654
3986
|
});
|
|
3655
3987
|
}, []);
|
|
3656
|
-
const onPointerDown =
|
|
3988
|
+
const onPointerDown = React8.useCallback(
|
|
3657
3989
|
(e) => {
|
|
3658
3990
|
if (!enabled || e.pointerType === "mouse") return;
|
|
3659
3991
|
if (revealed) dismiss();
|
|
@@ -3661,7 +3993,7 @@ function useTapReveal(options = {}) {
|
|
|
3661
3993
|
},
|
|
3662
3994
|
[enabled, revealed, reveal, dismiss]
|
|
3663
3995
|
);
|
|
3664
|
-
|
|
3996
|
+
React8.useEffect(() => {
|
|
3665
3997
|
if (!revealed || typeof document === "undefined") return;
|
|
3666
3998
|
const onDocDown = (ev) => {
|
|
3667
3999
|
const target = ev.target;
|
|
@@ -3781,7 +4113,7 @@ var positionStyles = {
|
|
|
3781
4113
|
fixed: "fixed",
|
|
3782
4114
|
sticky: "sticky"
|
|
3783
4115
|
};
|
|
3784
|
-
var Box =
|
|
4116
|
+
var Box = React8__namespace.default.forwardRef(
|
|
3785
4117
|
({
|
|
3786
4118
|
padding,
|
|
3787
4119
|
paddingX,
|
|
@@ -3813,20 +4145,20 @@ var Box = React6__default.default.forwardRef(
|
|
|
3813
4145
|
...rest
|
|
3814
4146
|
}, ref) => {
|
|
3815
4147
|
const eventBus = useEventBus();
|
|
3816
|
-
const handleClick =
|
|
4148
|
+
const handleClick = React8.useCallback((e) => {
|
|
3817
4149
|
if (action) {
|
|
3818
4150
|
e.stopPropagation();
|
|
3819
4151
|
eventBus.emit(`UI:${action}`, actionPayload ?? {});
|
|
3820
4152
|
}
|
|
3821
4153
|
onClick?.(e);
|
|
3822
4154
|
}, [action, actionPayload, eventBus, onClick]);
|
|
3823
|
-
const handleMouseEnter =
|
|
4155
|
+
const handleMouseEnter = React8.useCallback((e) => {
|
|
3824
4156
|
if (hoverEvent) {
|
|
3825
4157
|
eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
|
|
3826
4158
|
}
|
|
3827
4159
|
onMouseEnter?.(e);
|
|
3828
4160
|
}, [hoverEvent, eventBus, onMouseEnter]);
|
|
3829
|
-
const handleMouseLeave =
|
|
4161
|
+
const handleMouseLeave = React8.useCallback((e) => {
|
|
3830
4162
|
if (hoverEvent) {
|
|
3831
4163
|
eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
|
|
3832
4164
|
}
|
|
@@ -3834,19 +4166,19 @@ var Box = React6__default.default.forwardRef(
|
|
|
3834
4166
|
}, [hoverEvent, eventBus, onMouseLeave]);
|
|
3835
4167
|
const { triggerProps } = useTapReveal({
|
|
3836
4168
|
enabled: tapReveal && !!hoverEvent,
|
|
3837
|
-
onReveal:
|
|
4169
|
+
onReveal: React8.useCallback(() => {
|
|
3838
4170
|
if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: true });
|
|
3839
4171
|
}, [hoverEvent, eventBus]),
|
|
3840
|
-
onDismiss:
|
|
4172
|
+
onDismiss: React8.useCallback(() => {
|
|
3841
4173
|
if (hoverEvent) eventBus.emit(`UI:${hoverEvent}`, { hovered: false });
|
|
3842
4174
|
}, [hoverEvent, eventBus])
|
|
3843
4175
|
});
|
|
3844
|
-
const handlePointerDown =
|
|
4176
|
+
const handlePointerDown = React8.useCallback((e) => {
|
|
3845
4177
|
if (hoverEvent && tapReveal) triggerProps.onPointerDown(e);
|
|
3846
4178
|
onPointerDown?.(e);
|
|
3847
4179
|
}, [hoverEvent, tapReveal, triggerProps, onPointerDown]);
|
|
3848
4180
|
const isClickable = action || onClick;
|
|
3849
|
-
return
|
|
4181
|
+
return React8__namespace.default.createElement(
|
|
3850
4182
|
Component2,
|
|
3851
4183
|
{
|
|
3852
4184
|
ref,
|
|
@@ -5086,10 +5418,10 @@ var Avl3DOrbitalNode = ({
|
|
|
5086
5418
|
position,
|
|
5087
5419
|
onClick
|
|
5088
5420
|
}) => {
|
|
5089
|
-
const groupRef =
|
|
5090
|
-
const [hovered, setHovered] =
|
|
5091
|
-
const scale =
|
|
5092
|
-
const currentScale =
|
|
5421
|
+
const groupRef = React8.useRef(null);
|
|
5422
|
+
const [hovered, setHovered] = React8.useState(false);
|
|
5423
|
+
const scale = React8.useMemo(() => 0.6 + Math.min(traitCount, 8) * 0.3, [traitCount]);
|
|
5424
|
+
const currentScale = React8.useRef(scale);
|
|
5093
5425
|
const targetScale = hovered ? scale * 1.06 : scale;
|
|
5094
5426
|
fiber.useFrame((_, delta) => {
|
|
5095
5427
|
if (!groupRef.current) return;
|
|
@@ -5231,7 +5563,7 @@ var Avl3DCrossWire = ({
|
|
|
5231
5563
|
eventName,
|
|
5232
5564
|
color = AVL_3D_COLORS.crossWire
|
|
5233
5565
|
}) => {
|
|
5234
|
-
const { tubeArgs, midpoint } =
|
|
5566
|
+
const { tubeArgs, midpoint } = React8.useMemo(() => {
|
|
5235
5567
|
const curve = arcCurve3D(from, to, 2);
|
|
5236
5568
|
const mid = curve.getPoint(0.5);
|
|
5237
5569
|
return {
|
|
@@ -5270,9 +5602,9 @@ var Avl3DApplicationScene = ({
|
|
|
5270
5602
|
data,
|
|
5271
5603
|
onOrbitalClick
|
|
5272
5604
|
}) => {
|
|
5273
|
-
const groupRef =
|
|
5274
|
-
const [, setHoverTrigger] =
|
|
5275
|
-
const orbitalPositions =
|
|
5605
|
+
const groupRef = React8.useRef(null);
|
|
5606
|
+
const [, setHoverTrigger] = React8.useState(0);
|
|
5607
|
+
const orbitalPositions = React8.useMemo(() => {
|
|
5276
5608
|
const baseRadius = Math.max(4, data.orbitals.length * 1.5);
|
|
5277
5609
|
if (data.crossLinks.length === 0) {
|
|
5278
5610
|
return goldenSpiralPositions(data.orbitals.length, baseRadius);
|
|
@@ -5285,7 +5617,7 @@ var Avl3DApplicationScene = ({
|
|
|
5285
5617
|
})).filter((e) => e.from >= 0 && e.to >= 0);
|
|
5286
5618
|
return forceDirectedPositions(data.orbitals.length, edges, baseRadius);
|
|
5287
5619
|
}, [data.orbitals, data.crossLinks]);
|
|
5288
|
-
const positionByName =
|
|
5620
|
+
const positionByName = React8.useMemo(() => {
|
|
5289
5621
|
const map = /* @__PURE__ */ new Map();
|
|
5290
5622
|
data.orbitals.forEach((orb, i) => {
|
|
5291
5623
|
const pos = orbitalPositions[i];
|
|
@@ -5368,7 +5700,7 @@ var Avl3DEntityCore = ({
|
|
|
5368
5700
|
position,
|
|
5369
5701
|
fields
|
|
5370
5702
|
}) => {
|
|
5371
|
-
const coreRef =
|
|
5703
|
+
const coreRef = React8.useRef(null);
|
|
5372
5704
|
fiber.useFrame((_, delta) => {
|
|
5373
5705
|
if (!coreRef.current) return;
|
|
5374
5706
|
coreRef.current.rotation.y += delta * 0.3;
|
|
@@ -5521,8 +5853,8 @@ function TraitOrbit({
|
|
|
5521
5853
|
onClick,
|
|
5522
5854
|
onHover
|
|
5523
5855
|
}) {
|
|
5524
|
-
const groupRef =
|
|
5525
|
-
const angleRef =
|
|
5856
|
+
const groupRef = React8.useRef(null);
|
|
5857
|
+
const angleRef = React8.useRef(Math.random() * Math.PI * 2);
|
|
5526
5858
|
fiber.useFrame((_, delta) => {
|
|
5527
5859
|
angleRef.current += delta * speed;
|
|
5528
5860
|
if (!groupRef.current) return;
|
|
@@ -5653,9 +5985,9 @@ var Avl3DOrbitalScene = ({
|
|
|
5653
5985
|
highlightedTrait,
|
|
5654
5986
|
onTraitHighlight
|
|
5655
5987
|
}) => {
|
|
5656
|
-
const [localHighlight, setLocalHighlight] =
|
|
5988
|
+
const [localHighlight, setLocalHighlight] = React8.useState(null);
|
|
5657
5989
|
const activeHighlight = highlightedTrait ?? localHighlight;
|
|
5658
|
-
const traitOrbits =
|
|
5990
|
+
const traitOrbits = React8.useMemo(() => {
|
|
5659
5991
|
return data.traits.map((trait, i) => ({
|
|
5660
5992
|
name: trait.name,
|
|
5661
5993
|
radius: 3 + i * 1.2,
|
|
@@ -5666,7 +5998,7 @@ var Avl3DOrbitalScene = ({
|
|
|
5666
5998
|
transitionCount: trait.transitionCount
|
|
5667
5999
|
}));
|
|
5668
6000
|
}, [data.traits]);
|
|
5669
|
-
const pagePositions =
|
|
6001
|
+
const pagePositions = React8.useMemo(() => {
|
|
5670
6002
|
const outerRadius = 3 + data.traits.length * 1.2 + 1.5;
|
|
5671
6003
|
return data.pages.map((_, i) => {
|
|
5672
6004
|
const angle = 2 * Math.PI * i / data.pages.length - Math.PI / 2;
|
|
@@ -5730,12 +6062,12 @@ var Avl3DStateNode = ({
|
|
|
5730
6062
|
outgoingCount = 0,
|
|
5731
6063
|
onClick
|
|
5732
6064
|
}) => {
|
|
5733
|
-
const [hovered, setHovered] =
|
|
6065
|
+
const [hovered, setHovered] = React8.useState(false);
|
|
5734
6066
|
const baseColor = active ? AVL_3D_COLORS.stateActive : AVL_3D_COLORS.stateIdle;
|
|
5735
6067
|
const emissiveColor = active ? AVL_3D_COLORS.stateActive : AVL_3D_COLORS.stateEdge;
|
|
5736
6068
|
const emissiveIntensity = active ? 2 : hovered ? 0.8 : 0.3;
|
|
5737
6069
|
const targetScale = hovered ? 1.08 : 1;
|
|
5738
|
-
const currentScale =
|
|
6070
|
+
const currentScale = React8.useRef(1);
|
|
5739
6071
|
fiber.useFrame((_, delta) => {
|
|
5740
6072
|
currentScale.current = THREE6.MathUtils.damp(currentScale.current, targetScale, 6, delta);
|
|
5741
6073
|
});
|
|
@@ -5909,8 +6241,8 @@ var Avl3DTransitionArc = ({
|
|
|
5909
6241
|
onClick,
|
|
5910
6242
|
color = AVL_3D_COLORS.transitionArc
|
|
5911
6243
|
}) => {
|
|
5912
|
-
const [hovered, setHovered] =
|
|
5913
|
-
const { tubeArgs, labelPos, guardPos, arrowPos, arrowQuat, effectPositions } =
|
|
6244
|
+
const [hovered, setHovered] = React8.useState(false);
|
|
6245
|
+
const { tubeArgs, labelPos, guardPos, arrowPos, arrowQuat, effectPositions } = React8.useMemo(() => {
|
|
5914
6246
|
const offset = 1.5 + index * 0.8;
|
|
5915
6247
|
const curve = isSelf ? selfLoopCurve3D(from, 1.2) : arcCurve3D(from, to, offset);
|
|
5916
6248
|
const mid = curve.getPoint(0.5);
|
|
@@ -6043,7 +6375,7 @@ var Avl3DTraitScene = ({
|
|
|
6043
6375
|
data,
|
|
6044
6376
|
onTransitionClick
|
|
6045
6377
|
}) => {
|
|
6046
|
-
const statePositions =
|
|
6378
|
+
const statePositions = React8.useMemo(() => {
|
|
6047
6379
|
const radius = Math.max(3, data.states.length * 0.8);
|
|
6048
6380
|
const positions = fibonacciSpherePositions(data.states.length, radius);
|
|
6049
6381
|
const posMap = /* @__PURE__ */ new Map();
|
|
@@ -6053,7 +6385,7 @@ var Avl3DTraitScene = ({
|
|
|
6053
6385
|
});
|
|
6054
6386
|
return posMap;
|
|
6055
6387
|
}, [data.states]);
|
|
6056
|
-
const transitionIndices =
|
|
6388
|
+
const transitionIndices = React8.useMemo(() => {
|
|
6057
6389
|
const pairCount = /* @__PURE__ */ new Map();
|
|
6058
6390
|
return data.transitions.map((t) => {
|
|
6059
6391
|
const key = `${t.from}->${t.to}`;
|
|
@@ -6062,7 +6394,7 @@ var Avl3DTraitScene = ({
|
|
|
6062
6394
|
return idx;
|
|
6063
6395
|
});
|
|
6064
6396
|
}, [data.transitions]);
|
|
6065
|
-
const transitionCounts =
|
|
6397
|
+
const transitionCounts = React8.useMemo(() => {
|
|
6066
6398
|
const incoming = /* @__PURE__ */ new Map();
|
|
6067
6399
|
const outgoing = /* @__PURE__ */ new Map();
|
|
6068
6400
|
for (const t of data.transitions) {
|
|
@@ -6172,11 +6504,11 @@ var Avl3DExprTree = ({
|
|
|
6172
6504
|
expression,
|
|
6173
6505
|
position
|
|
6174
6506
|
}) => {
|
|
6175
|
-
const layoutResults =
|
|
6507
|
+
const layoutResults = React8.useMemo(() => {
|
|
6176
6508
|
const origin = { x: position[0], y: position[1], z: position[2] };
|
|
6177
6509
|
return treeLayout3D(expression, origin, 2.5, 1.5);
|
|
6178
6510
|
}, [expression, position]);
|
|
6179
|
-
const edges =
|
|
6511
|
+
const edges = React8.useMemo(() => {
|
|
6180
6512
|
const result = [];
|
|
6181
6513
|
function collectEdges(node, parentPos) {
|
|
6182
6514
|
const entry = layoutResults.find((r) => r.node === node);
|
|
@@ -6456,7 +6788,7 @@ var Avl3DEffects = ({
|
|
|
6456
6788
|
const dof = DOF_CONFIG[level];
|
|
6457
6789
|
const sparkles = SPARKLE_CONFIGS[level];
|
|
6458
6790
|
const stars = STARS_CONFIG[level];
|
|
6459
|
-
const starsKey =
|
|
6791
|
+
const starsKey = React8.useMemo(() => `stars-${level}`, [level]);
|
|
6460
6792
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6461
6793
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6462
6794
|
drei.Stars,
|
|
@@ -6520,16 +6852,16 @@ var DEFAULT_CONFIG2 = {
|
|
|
6520
6852
|
modelOverrides: {},
|
|
6521
6853
|
effectsEnabled: true
|
|
6522
6854
|
};
|
|
6523
|
-
var Avl3DContext =
|
|
6855
|
+
var Avl3DContext = React8.createContext(DEFAULT_CONFIG2);
|
|
6524
6856
|
function useAvl3DConfig() {
|
|
6525
|
-
return
|
|
6857
|
+
return React8.useContext(Avl3DContext);
|
|
6526
6858
|
}
|
|
6527
6859
|
function CameraController2({ targetPosition, targetLookAt, animated }) {
|
|
6528
6860
|
const { camera } = fiber.useThree();
|
|
6529
|
-
const targetPosVec =
|
|
6530
|
-
const targetLookVec =
|
|
6531
|
-
const isAnimating =
|
|
6532
|
-
|
|
6861
|
+
const targetPosVec = React8.useRef(new THREE6__namespace.Vector3(...targetPosition));
|
|
6862
|
+
const targetLookVec = React8.useRef(new THREE6__namespace.Vector3(...targetLookAt));
|
|
6863
|
+
const isAnimating = React8.useRef(false);
|
|
6864
|
+
React8.useEffect(() => {
|
|
6533
6865
|
const newTarget = new THREE6__namespace.Vector3(...targetPosition);
|
|
6534
6866
|
const newLookAt = new THREE6__namespace.Vector3(...targetLookAt);
|
|
6535
6867
|
if (!newTarget.equals(targetPosVec.current) || !newLookAt.equals(targetLookVec.current)) {
|
|
@@ -6558,8 +6890,8 @@ function CameraController2({ targetPosition, targetLookAt, animated }) {
|
|
|
6558
6890
|
return null;
|
|
6559
6891
|
}
|
|
6560
6892
|
function SceneFade({ animating, children }) {
|
|
6561
|
-
const groupRef =
|
|
6562
|
-
const opacityRef =
|
|
6893
|
+
const groupRef = React8.useRef(null);
|
|
6894
|
+
const opacityRef = React8.useRef(1);
|
|
6563
6895
|
fiber.useFrame((_, delta) => {
|
|
6564
6896
|
if (!groupRef.current) return;
|
|
6565
6897
|
const target = animating ? 0 : 1;
|
|
@@ -6583,7 +6915,7 @@ var Avl3DViewer = ({
|
|
|
6583
6915
|
modelOverrides = {}
|
|
6584
6916
|
}) => {
|
|
6585
6917
|
const { t } = hooks.useTranslate();
|
|
6586
|
-
const schema =
|
|
6918
|
+
const schema = React8.useMemo(() => {
|
|
6587
6919
|
if (typeof schemaProp === "string") {
|
|
6588
6920
|
try {
|
|
6589
6921
|
return JSON.parse(schemaProp);
|
|
@@ -6593,12 +6925,12 @@ var Avl3DViewer = ({
|
|
|
6593
6925
|
}
|
|
6594
6926
|
return schemaProp;
|
|
6595
6927
|
}, [schemaProp]);
|
|
6596
|
-
const configValue =
|
|
6928
|
+
const configValue = React8.useMemo(() => ({
|
|
6597
6929
|
modelOverrides,
|
|
6598
6930
|
effectsEnabled: effects
|
|
6599
6931
|
}), [modelOverrides, effects]);
|
|
6600
|
-
const [state, dispatch] =
|
|
6601
|
-
|
|
6932
|
+
const [state, dispatch] = React8.useReducer(zoomReducer, initialZoomState);
|
|
6933
|
+
React8.useEffect(() => {
|
|
6602
6934
|
if (initialOrbital) {
|
|
6603
6935
|
dispatch({ type: "ZOOM_INTO_ORBITAL", orbital: initialOrbital, targetPosition: { x: 0, y: 0 } });
|
|
6604
6936
|
setTimeout(() => dispatch({ type: "ANIMATION_COMPLETE" }), 0);
|
|
@@ -6610,35 +6942,35 @@ var Avl3DViewer = ({
|
|
|
6610
6942
|
}
|
|
6611
6943
|
}
|
|
6612
6944
|
}, [initialOrbital, initialTrait]);
|
|
6613
|
-
|
|
6945
|
+
React8.useEffect(() => {
|
|
6614
6946
|
onZoomChange?.(state.level, {
|
|
6615
6947
|
orbital: state.selectedOrbital ?? void 0,
|
|
6616
6948
|
trait: state.selectedTrait ?? void 0
|
|
6617
6949
|
});
|
|
6618
6950
|
}, [state.level, state.selectedOrbital, state.selectedTrait, onZoomChange]);
|
|
6619
|
-
|
|
6951
|
+
React8.useEffect(() => {
|
|
6620
6952
|
if (!state.animating) return;
|
|
6621
6953
|
const timer = setTimeout(() => {
|
|
6622
6954
|
dispatch({ type: "ANIMATION_COMPLETE" });
|
|
6623
6955
|
}, 800);
|
|
6624
6956
|
return () => clearTimeout(timer);
|
|
6625
6957
|
}, [state.animating]);
|
|
6626
|
-
const handleKeyDown =
|
|
6958
|
+
const handleKeyDown = React8.useCallback((e) => {
|
|
6627
6959
|
if (e.key === "Escape") {
|
|
6628
6960
|
dispatch({ type: "ZOOM_OUT" });
|
|
6629
6961
|
}
|
|
6630
6962
|
}, []);
|
|
6631
|
-
const handleOrbitalClick =
|
|
6963
|
+
const handleOrbitalClick = React8.useCallback((name) => {
|
|
6632
6964
|
dispatch({ type: "ZOOM_INTO_ORBITAL", orbital: name, targetPosition: { x: 0, y: 0 } });
|
|
6633
6965
|
}, []);
|
|
6634
|
-
const handleTraitClick =
|
|
6966
|
+
const handleTraitClick = React8.useCallback((name) => {
|
|
6635
6967
|
dispatch({ type: "ZOOM_INTO_TRAIT", trait: name, targetPosition: { x: 0, y: 0 } });
|
|
6636
6968
|
}, []);
|
|
6637
|
-
const [highlightedTrait, setHighlightedTrait] =
|
|
6638
|
-
const handleTransitionClick =
|
|
6969
|
+
const [highlightedTrait, setHighlightedTrait] = React8__namespace.default.useState(null);
|
|
6970
|
+
const handleTransitionClick = React8.useCallback((index) => {
|
|
6639
6971
|
dispatch({ type: "ZOOM_INTO_TRANSITION", transitionIndex: index, targetPosition: { x: 0, y: 0 } });
|
|
6640
6972
|
}, []);
|
|
6641
|
-
const handleBreadcrumbClick =
|
|
6973
|
+
const handleBreadcrumbClick = React8.useCallback((targetLevel) => {
|
|
6642
6974
|
const levelOrder = ["application", "orbital", "trait", "transition"];
|
|
6643
6975
|
const currentIdx = levelOrder.indexOf(state.level);
|
|
6644
6976
|
const targetIdx = levelOrder.indexOf(targetLevel);
|
|
@@ -6647,7 +6979,7 @@ var Avl3DViewer = ({
|
|
|
6647
6979
|
}
|
|
6648
6980
|
}, [state.level]);
|
|
6649
6981
|
const cameraConfig = CAMERA_POSITIONS[state.level];
|
|
6650
|
-
const sceneContent =
|
|
6982
|
+
const sceneContent = React8.useMemo(() => {
|
|
6651
6983
|
switch (state.level) {
|
|
6652
6984
|
case "application": {
|
|
6653
6985
|
const data = parseApplicationLevel(schema);
|
|
@@ -6721,7 +7053,7 @@ var Avl3DViewer = ({
|
|
|
6721
7053
|
gap: "xs",
|
|
6722
7054
|
align: "center",
|
|
6723
7055
|
className: "absolute top-2 left-2 z-10 bg-surface/80 backdrop-blur rounded-md px-3 py-1.5",
|
|
6724
|
-
children: breadcrumbs.map((crumb, i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7056
|
+
children: breadcrumbs.map((crumb, i) => /* @__PURE__ */ jsxRuntime.jsxs(React8__namespace.default.Fragment, { children: [
|
|
6725
7057
|
i > 0 && /* @__PURE__ */ jsxRuntime.jsx(Typography, { variant: "small", color: "muted", className: "mx-1", children: "/" }),
|
|
6726
7058
|
i < breadcrumbs.length - 1 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
6727
7059
|
Box,
|
|
@@ -7008,7 +7340,6 @@ exports.Avl3DTransitionScene = Avl3DTransitionScene;
|
|
|
7008
7340
|
exports.Avl3DViewer = Avl3DViewer;
|
|
7009
7341
|
exports.CAMERA_POSITIONS = CAMERA_POSITIONS;
|
|
7010
7342
|
exports.Camera3D = Camera3D;
|
|
7011
|
-
exports.Canvas3D = Canvas3D;
|
|
7012
7343
|
exports.Canvas3DErrorBoundary = Canvas3DErrorBoundary;
|
|
7013
7344
|
exports.Canvas3DLoadingState = Canvas3DLoadingState;
|
|
7014
7345
|
exports.GameBoard3D = GameBoard3D;
|