@almadar/ui 5.94.1 → 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 +1872 -1379
- package/dist/avl/index.js +873 -380
- package/dist/components/core/atoms/AtlasImage.d.ts +58 -0
- package/dist/components/core/atoms/index.d.ts +1 -0
- package/dist/components/core/molecules/index.d.ts +1 -0
- package/dist/components/game/2d/molecules/Canvas2D.d.ts +10 -6
- 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 +2153 -1655
- package/dist/components/index.js +1160 -665
- package/dist/components/learning/molecules/AlgorithmCanvas.d.ts +58 -0
- package/dist/lib/atlasSlice.d.ts +58 -0
- package/dist/marketing/index.cjs +208 -73
- package/dist/marketing/index.js +166 -28
- package/dist/providers/index.cjs +1740 -1247
- package/dist/providers/index.js +849 -356
- package/dist/runtime/index.cjs +1733 -1240
- package/dist/runtime/index.js +853 -360
- package/package.json +2 -2
- package/dist/components/game/3d/molecules/Canvas3D.d.ts +0 -45
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as React8 from 'react';
|
|
2
|
+
import React8__default, { forwardRef, useRef, useEffect, useImperativeHandle, useState, useMemo, useCallback, createContext, useContext, Component, useSyncExternalStore, useReducer } from 'react';
|
|
2
3
|
import { useThree, useFrame, Canvas, useLoader } from '@react-three/fiber';
|
|
3
4
|
import * as THREE6 from 'three';
|
|
4
5
|
import { Vector3, QuadraticBezierCurve3, MathUtils, Quaternion } from 'three';
|
|
@@ -316,12 +317,13 @@ function detectAssetRoot(modelUrl) {
|
|
|
316
317
|
function useGLTFModel(url, resourceBasePath) {
|
|
317
318
|
const [state, setState] = useState({
|
|
318
319
|
model: null,
|
|
320
|
+
clips: [],
|
|
319
321
|
isLoading: false,
|
|
320
322
|
error: null
|
|
321
323
|
});
|
|
322
324
|
useEffect(() => {
|
|
323
325
|
if (!url) {
|
|
324
|
-
setState({ model: null, isLoading: false, error: null });
|
|
326
|
+
setState({ model: null, clips: [], isLoading: false, error: null });
|
|
325
327
|
return;
|
|
326
328
|
}
|
|
327
329
|
log2.debug("Loading", { url });
|
|
@@ -332,9 +334,10 @@ function useGLTFModel(url, resourceBasePath) {
|
|
|
332
334
|
loader.load(
|
|
333
335
|
url,
|
|
334
336
|
(gltf) => {
|
|
335
|
-
log2.debug("Loaded", { url });
|
|
337
|
+
log2.debug("Loaded", { url, clips: gltf.animations.length });
|
|
336
338
|
setState({
|
|
337
339
|
model: gltf.scene,
|
|
340
|
+
clips: gltf.animations,
|
|
338
341
|
isLoading: false,
|
|
339
342
|
error: null
|
|
340
343
|
});
|
|
@@ -344,6 +347,7 @@ function useGLTFModel(url, resourceBasePath) {
|
|
|
344
347
|
log2.warn("Failed", { url, error: err instanceof Error ? err : String(err) });
|
|
345
348
|
setState({
|
|
346
349
|
model: null,
|
|
350
|
+
clips: [],
|
|
347
351
|
isLoading: false,
|
|
348
352
|
error: err instanceof Error ? err : new Error(String(err))
|
|
349
353
|
});
|
|
@@ -364,9 +368,10 @@ function ModelLoader({
|
|
|
364
368
|
fallbackGeometry = "box",
|
|
365
369
|
castShadow = true,
|
|
366
370
|
receiveShadow = true,
|
|
367
|
-
resourceBasePath
|
|
371
|
+
resourceBasePath,
|
|
372
|
+
animation
|
|
368
373
|
}) {
|
|
369
|
-
const { model: loadedModel, isLoading, error } = useGLTFModel(url, resourceBasePath);
|
|
374
|
+
const { model: loadedModel, clips, isLoading, error } = useGLTFModel(url, resourceBasePath);
|
|
370
375
|
const model = useMemo(() => {
|
|
371
376
|
if (!loadedModel) return null;
|
|
372
377
|
const cloned = clone(loadedModel);
|
|
@@ -379,6 +384,21 @@ function ModelLoader({
|
|
|
379
384
|
});
|
|
380
385
|
return cloned;
|
|
381
386
|
}, [loadedModel, castShadow, receiveShadow]);
|
|
387
|
+
const mixer = useMemo(() => model ? new THREE6.AnimationMixer(model) : null, [model]);
|
|
388
|
+
useEffect(() => {
|
|
389
|
+
if (!mixer || !animation || clips.length === 0) return;
|
|
390
|
+
const wanted = animation.toLowerCase();
|
|
391
|
+
const clip = clips.find((c) => c.name === animation) ?? clips.find((c) => c.name.toLowerCase() === wanted);
|
|
392
|
+
if (!clip) return;
|
|
393
|
+
const action = mixer.clipAction(clip);
|
|
394
|
+
action.reset().fadeIn(0.15).play();
|
|
395
|
+
return () => {
|
|
396
|
+
action.fadeOut(0.15);
|
|
397
|
+
};
|
|
398
|
+
}, [mixer, clips, animation]);
|
|
399
|
+
useFrame((_, delta) => {
|
|
400
|
+
mixer?.update(delta);
|
|
401
|
+
});
|
|
382
402
|
const normFactor = useMemo(() => {
|
|
383
403
|
if (!model) return 1;
|
|
384
404
|
const box = new THREE6.Box3().setFromObject(model);
|
|
@@ -1695,6 +1715,99 @@ function UnitSpriteBillboard({
|
|
|
1695
1715
|
)
|
|
1696
1716
|
] });
|
|
1697
1717
|
}
|
|
1718
|
+
function FollowCamera({
|
|
1719
|
+
target,
|
|
1720
|
+
offset
|
|
1721
|
+
}) {
|
|
1722
|
+
const { camera } = useThree();
|
|
1723
|
+
const look = useRef(new THREE6.Vector3(target[0], target[1], target[2]));
|
|
1724
|
+
const goal = useRef(new THREE6.Vector3());
|
|
1725
|
+
useFrame((_, delta) => {
|
|
1726
|
+
const t = Math.min(1, delta * 5);
|
|
1727
|
+
goal.current.set(target[0] + offset[0], target[1] + offset[1], target[2] + offset[2]);
|
|
1728
|
+
camera.position.lerp(goal.current, t);
|
|
1729
|
+
look.current.lerp(goal.current.set(target[0], target[1], target[2]), t);
|
|
1730
|
+
camera.lookAt(look.current);
|
|
1731
|
+
});
|
|
1732
|
+
return null;
|
|
1733
|
+
}
|
|
1734
|
+
function LerpedGroup({
|
|
1735
|
+
target,
|
|
1736
|
+
enabled,
|
|
1737
|
+
children
|
|
1738
|
+
}) {
|
|
1739
|
+
const ref = useRef(null);
|
|
1740
|
+
const goal = useRef(new THREE6.Vector3());
|
|
1741
|
+
useFrame((_, delta) => {
|
|
1742
|
+
const g = ref.current;
|
|
1743
|
+
if (!g) return;
|
|
1744
|
+
goal.current.set(target[0], target[1], target[2]);
|
|
1745
|
+
if (enabled) g.position.lerp(goal.current, Math.min(1, delta * 10));
|
|
1746
|
+
else g.position.copy(goal.current);
|
|
1747
|
+
});
|
|
1748
|
+
return /* @__PURE__ */ jsx("group", { ref, position: target, children });
|
|
1749
|
+
}
|
|
1750
|
+
var SIDE_PLATFORM_COLORS = {
|
|
1751
|
+
ground: "#5b8c3e",
|
|
1752
|
+
platform: "#8b5a2b",
|
|
1753
|
+
hazard: "#cc4444",
|
|
1754
|
+
goal: "#4488cc"
|
|
1755
|
+
};
|
|
1756
|
+
function SideScene({
|
|
1757
|
+
player,
|
|
1758
|
+
platforms,
|
|
1759
|
+
worldHeight,
|
|
1760
|
+
ppu,
|
|
1761
|
+
playerSprite,
|
|
1762
|
+
tileSprites,
|
|
1763
|
+
interpolate
|
|
1764
|
+
}) {
|
|
1765
|
+
const pw = player.width ?? 32;
|
|
1766
|
+
const ph = player.height ?? 48;
|
|
1767
|
+
const playerPos = [
|
|
1768
|
+
(player.x + pw / 2) / ppu,
|
|
1769
|
+
(worldHeight - player.y - ph) / ppu,
|
|
1770
|
+
0
|
|
1771
|
+
];
|
|
1772
|
+
const playerH = Math.max(ph / ppu, 0.5);
|
|
1773
|
+
return /* @__PURE__ */ jsxs("group", { children: [
|
|
1774
|
+
platforms.map((p, i) => {
|
|
1775
|
+
const platformType = p.type ?? "platform";
|
|
1776
|
+
const model = tileSprites?.[platformType]?.url;
|
|
1777
|
+
const topY = (worldHeight - p.y) / ppu;
|
|
1778
|
+
if (model) {
|
|
1779
|
+
const cells = Math.max(1, Math.round(p.width / ppu));
|
|
1780
|
+
const x0 = p.x / ppu;
|
|
1781
|
+
return /* @__PURE__ */ jsx("group", { children: Array.from({ length: cells }, (_, c) => /* @__PURE__ */ jsx("group", { position: [x0 + c + 0.5, topY - 0.475, 0], children: /* @__PURE__ */ jsx(ModelLoader, { url: model, scale: 0.95, fallbackGeometry: "box", castShadow: true, receiveShadow: true }) }, c)) }, `plat-${i}`);
|
|
1782
|
+
}
|
|
1783
|
+
return /* @__PURE__ */ jsxs(
|
|
1784
|
+
"mesh",
|
|
1785
|
+
{
|
|
1786
|
+
position: [(p.x + p.width / 2) / ppu, topY - p.height / 2 / ppu, 0],
|
|
1787
|
+
children: [
|
|
1788
|
+
/* @__PURE__ */ jsx("boxGeometry", { args: [p.width / ppu, p.height / ppu, 1] }),
|
|
1789
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: SIDE_PLATFORM_COLORS[platformType] ?? "#888888" })
|
|
1790
|
+
]
|
|
1791
|
+
},
|
|
1792
|
+
`plat-${i}`
|
|
1793
|
+
);
|
|
1794
|
+
}),
|
|
1795
|
+
/* @__PURE__ */ jsx(LerpedGroup, { target: playerPos, enabled: interpolate, children: playerSprite?.url ? /* @__PURE__ */ jsx("group", { position: [0, playerH / 2, 0], children: /* @__PURE__ */ jsx(
|
|
1796
|
+
ModelLoader,
|
|
1797
|
+
{
|
|
1798
|
+
url: playerSprite.url,
|
|
1799
|
+
scale: playerH,
|
|
1800
|
+
rotation: [0, player.facingRight ? 90 : -90, 0],
|
|
1801
|
+
animation: player.animation ?? "idle",
|
|
1802
|
+
fallbackGeometry: "box",
|
|
1803
|
+
castShadow: true
|
|
1804
|
+
}
|
|
1805
|
+
) }) : /* @__PURE__ */ jsxs("mesh", { position: [0, playerH / 2, 0], children: [
|
|
1806
|
+
/* @__PURE__ */ jsx("capsuleGeometry", { args: [playerH * 0.25, playerH * 0.5, 4, 8] }),
|
|
1807
|
+
/* @__PURE__ */ jsx("meshStandardMaterial", { color: "#4488ff" })
|
|
1808
|
+
] }) })
|
|
1809
|
+
] });
|
|
1810
|
+
}
|
|
1698
1811
|
var GameCanvas3D = forwardRef(
|
|
1699
1812
|
({
|
|
1700
1813
|
tiles = [],
|
|
@@ -1738,12 +1851,47 @@ var GameCanvas3D = forwardRef(
|
|
|
1738
1851
|
selectedTileIds = [],
|
|
1739
1852
|
selectedUnitId = null,
|
|
1740
1853
|
unitScale = 1,
|
|
1854
|
+
keyMap,
|
|
1855
|
+
keyUpMap,
|
|
1856
|
+
player,
|
|
1857
|
+
platforms = [],
|
|
1858
|
+
worldHeight = 400,
|
|
1859
|
+
pixelsPerUnit = 32,
|
|
1860
|
+
playerSprite,
|
|
1861
|
+
tileSprites,
|
|
1862
|
+
assetManifest,
|
|
1863
|
+
interpolateUnits = false,
|
|
1741
1864
|
children
|
|
1742
1865
|
}, ref) => {
|
|
1743
1866
|
const containerRef = useRef(null);
|
|
1744
1867
|
const controlsRef = useRef(null);
|
|
1745
1868
|
const [hoveredTile, setHoveredTile] = useState(null);
|
|
1746
1869
|
const [internalError, setInternalError] = useState(null);
|
|
1870
|
+
const eventBus = useEventBus();
|
|
1871
|
+
const keysRef = useRef(/* @__PURE__ */ new Set());
|
|
1872
|
+
useEffect(() => {
|
|
1873
|
+
if (!keyMap && !keyUpMap) return;
|
|
1874
|
+
const down = (e) => {
|
|
1875
|
+
if (keysRef.current.has(e.code)) return;
|
|
1876
|
+
keysRef.current.add(e.code);
|
|
1877
|
+
const ev = keyMap?.[e.code];
|
|
1878
|
+
if (ev) {
|
|
1879
|
+
eventBus.emit(`UI:${ev}`, {});
|
|
1880
|
+
e.preventDefault();
|
|
1881
|
+
}
|
|
1882
|
+
};
|
|
1883
|
+
const up = (e) => {
|
|
1884
|
+
keysRef.current.delete(e.code);
|
|
1885
|
+
const ev = keyUpMap?.[e.code];
|
|
1886
|
+
if (ev) eventBus.emit(`UI:${ev}`, {});
|
|
1887
|
+
};
|
|
1888
|
+
window.addEventListener("keydown", down);
|
|
1889
|
+
window.addEventListener("keyup", up);
|
|
1890
|
+
return () => {
|
|
1891
|
+
window.removeEventListener("keydown", down);
|
|
1892
|
+
window.removeEventListener("keyup", up);
|
|
1893
|
+
};
|
|
1894
|
+
}, [keyMap, keyUpMap, eventBus]);
|
|
1747
1895
|
useEffect(() => {
|
|
1748
1896
|
const el = containerRef.current;
|
|
1749
1897
|
if (!el) return;
|
|
@@ -1913,6 +2061,11 @@ var GameCanvas3D = forwardRef(
|
|
|
1913
2061
|
position: [cx, d * 2, cz],
|
|
1914
2062
|
fov: 45
|
|
1915
2063
|
};
|
|
2064
|
+
case "follow":
|
|
2065
|
+
return {
|
|
2066
|
+
position: [cx, d * 0.5, cz + d],
|
|
2067
|
+
fov: 45
|
|
2068
|
+
};
|
|
1916
2069
|
case "perspective":
|
|
1917
2070
|
default:
|
|
1918
2071
|
return {
|
|
@@ -1921,6 +2074,26 @@ var GameCanvas3D = forwardRef(
|
|
|
1921
2074
|
};
|
|
1922
2075
|
}
|
|
1923
2076
|
}, [cameraMode, gridBounds]);
|
|
2077
|
+
const followTarget = useMemo(() => {
|
|
2078
|
+
if (player) {
|
|
2079
|
+
return [
|
|
2080
|
+
(player.x + (player.width ?? 32) / 2) / pixelsPerUnit,
|
|
2081
|
+
(worldHeight - player.y - (player.height ?? 48) / 2) / pixelsPerUnit,
|
|
2082
|
+
0
|
|
2083
|
+
];
|
|
2084
|
+
}
|
|
2085
|
+
const selected = units.find((u) => u.id === selectedUnitId);
|
|
2086
|
+
if (selected) {
|
|
2087
|
+
const sx = selected.x ?? selected.position?.x ?? 0;
|
|
2088
|
+
const sz = selected.z ?? selected.y ?? selected.position?.y ?? 0;
|
|
2089
|
+
return [
|
|
2090
|
+
(sx - gridBounds.minX) * gridConfig.cellSize,
|
|
2091
|
+
0,
|
|
2092
|
+
(sz - gridBounds.minZ) * gridConfig.cellSize
|
|
2093
|
+
];
|
|
2094
|
+
}
|
|
2095
|
+
return cameraTarget;
|
|
2096
|
+
}, [player, pixelsPerUnit, worldHeight, units, selectedUnitId, gridBounds, gridConfig, cameraTarget]);
|
|
1924
2097
|
const DefaultTileRenderer = useCallback(
|
|
1925
2098
|
({ tile, position }) => {
|
|
1926
2099
|
const isSelected = tile.id ? selectedTileIds.includes(tile.id) : false;
|
|
@@ -1942,7 +2115,8 @@ var GameCanvas3D = forwardRef(
|
|
|
1942
2115
|
else if (isAttackTarget) emissive = 4456448;
|
|
1943
2116
|
else if (isValidMove) emissive = 17408;
|
|
1944
2117
|
else if (isHovered) emissive = 2236962;
|
|
1945
|
-
|
|
2118
|
+
const tileModel = tile.modelUrl ?? assetManifest?.terrains?.[tile.terrain ?? tile.type ?? ""];
|
|
2119
|
+
if (tileModel?.url) {
|
|
1946
2120
|
return /* @__PURE__ */ jsx(
|
|
1947
2121
|
"group",
|
|
1948
2122
|
{
|
|
@@ -1954,7 +2128,7 @@ var GameCanvas3D = forwardRef(
|
|
|
1954
2128
|
children: /* @__PURE__ */ jsx(
|
|
1955
2129
|
ModelLoader,
|
|
1956
2130
|
{
|
|
1957
|
-
url:
|
|
2131
|
+
url: tileModel.url,
|
|
1958
2132
|
scale: 0.95,
|
|
1959
2133
|
fallbackGeometry: "box",
|
|
1960
2134
|
castShadow: true,
|
|
@@ -1979,7 +2153,7 @@ var GameCanvas3D = forwardRef(
|
|
|
1979
2153
|
}
|
|
1980
2154
|
);
|
|
1981
2155
|
},
|
|
1982
|
-
[selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover]
|
|
2156
|
+
[selectedTileIds, hoveredTile, validMoves, attackTargets, handleTileClick, handleTileHover, assetManifest]
|
|
1983
2157
|
);
|
|
1984
2158
|
const UNIT_BASE_MODEL_SCALE = 0.5;
|
|
1985
2159
|
const UNIT_BASE_BILLBOARD_HEIGHT = 1.2;
|
|
@@ -1990,6 +2164,7 @@ var GameCanvas3D = forwardRef(
|
|
|
1990
2164
|
const color = unit.faction === "player" ? 4491519 : unit.faction === "enemy" ? 16729156 : 16777028;
|
|
1991
2165
|
const hasAtlas = unitAtlasUrl(unit) !== null;
|
|
1992
2166
|
const initialFrame = hasAtlas ? resolveUnitFrame(unit.id) : null;
|
|
2167
|
+
const unitModel = unit.modelUrl ?? assetManifest?.units?.[unit.unitType ?? ""];
|
|
1993
2168
|
const modelScale = UNIT_BASE_MODEL_SCALE * unitScale * gridConfig.cellSize;
|
|
1994
2169
|
const billboardHeight = UNIT_BASE_BILLBOARD_HEIGHT * unitScale * gridConfig.cellSize;
|
|
1995
2170
|
const primitiveRadius = UNIT_BASE_PRIMITIVE_RADIUS * unitScale * gridConfig.cellSize;
|
|
@@ -2014,13 +2189,14 @@ var GameCanvas3D = forwardRef(
|
|
|
2014
2189
|
height: billboardHeight
|
|
2015
2190
|
}
|
|
2016
2191
|
) })
|
|
2017
|
-
) :
|
|
2018
|
-
/* GLB unit model
|
|
2192
|
+
) : unitModel?.url ? (
|
|
2193
|
+
/* GLB unit model — LOLO's `animation` field drives the named clip */
|
|
2019
2194
|
/* @__PURE__ */ jsx(
|
|
2020
2195
|
ModelLoader,
|
|
2021
2196
|
{
|
|
2022
|
-
url:
|
|
2197
|
+
url: unitModel.url,
|
|
2023
2198
|
scale: modelScale,
|
|
2199
|
+
animation: unit.animation ?? "idle",
|
|
2024
2200
|
fallbackGeometry: "box",
|
|
2025
2201
|
castShadow: true
|
|
2026
2202
|
}
|
|
@@ -2068,18 +2244,19 @@ var GameCanvas3D = forwardRef(
|
|
|
2068
2244
|
}
|
|
2069
2245
|
);
|
|
2070
2246
|
},
|
|
2071
|
-
[selectedUnitId, handleUnitClick, resolveUnitFrame, unitScale, gridConfig]
|
|
2247
|
+
[selectedUnitId, handleUnitClick, resolveUnitFrame, unitScale, gridConfig, assetManifest]
|
|
2072
2248
|
);
|
|
2073
2249
|
const DefaultFeatureRenderer = useCallback(
|
|
2074
2250
|
({
|
|
2075
2251
|
feature,
|
|
2076
2252
|
position
|
|
2077
2253
|
}) => {
|
|
2078
|
-
|
|
2254
|
+
const featureModel = feature.assetUrl ?? assetManifest?.features?.[feature.type];
|
|
2255
|
+
if (featureModel?.url) {
|
|
2079
2256
|
return /* @__PURE__ */ jsx(
|
|
2080
2257
|
ModelLoader,
|
|
2081
2258
|
{
|
|
2082
|
-
url:
|
|
2259
|
+
url: featureModel.url,
|
|
2083
2260
|
position,
|
|
2084
2261
|
scale: 0.5,
|
|
2085
2262
|
rotation: [0, feature.rotation ?? 0, 0],
|
|
@@ -2125,7 +2302,7 @@ var GameCanvas3D = forwardRef(
|
|
|
2125
2302
|
}
|
|
2126
2303
|
return null;
|
|
2127
2304
|
},
|
|
2128
|
-
[handleFeatureClick]
|
|
2305
|
+
[handleFeatureClick, assetManifest]
|
|
2129
2306
|
);
|
|
2130
2307
|
if (externalLoading || assetsLoading && preloadAssets.length > 0) {
|
|
2131
2308
|
return /* @__PURE__ */ jsx(
|
|
@@ -2179,6 +2356,13 @@ var GameCanvas3D = forwardRef(
|
|
|
2179
2356
|
},
|
|
2180
2357
|
children: [
|
|
2181
2358
|
/* @__PURE__ */ jsx(CameraController, { onCameraChange: eventHandlers.handleCameraChange }),
|
|
2359
|
+
cameraMode === "follow" && /* @__PURE__ */ jsx(
|
|
2360
|
+
FollowCamera,
|
|
2361
|
+
{
|
|
2362
|
+
target: followTarget,
|
|
2363
|
+
offset: player ? [0, 2, 9] : [5, 7, 5]
|
|
2364
|
+
}
|
|
2365
|
+
),
|
|
2182
2366
|
/* @__PURE__ */ jsx("ambientLight", { intensity: 0.6 }),
|
|
2183
2367
|
/* @__PURE__ */ jsx(
|
|
2184
2368
|
"directionalLight",
|
|
@@ -2186,7 +2370,9 @@ var GameCanvas3D = forwardRef(
|
|
|
2186
2370
|
position: [10, 20, 10],
|
|
2187
2371
|
intensity: 0.8,
|
|
2188
2372
|
castShadow: shadows,
|
|
2189
|
-
"shadow-mapSize": [2048, 2048]
|
|
2373
|
+
"shadow-mapSize": [2048, 2048],
|
|
2374
|
+
"shadow-bias": -4e-4,
|
|
2375
|
+
"shadow-normalBias": 0.04
|
|
2190
2376
|
}
|
|
2191
2377
|
),
|
|
2192
2378
|
/* @__PURE__ */ jsx("hemisphereLight", { intensity: 0.3, color: "#87ceeb", groundColor: "#362d1d" }),
|
|
@@ -2212,7 +2398,21 @@ var GameCanvas3D = forwardRef(
|
|
|
2212
2398
|
fadeStrength: 1
|
|
2213
2399
|
}
|
|
2214
2400
|
),
|
|
2215
|
-
|
|
2401
|
+
player ? (
|
|
2402
|
+
/* Side-scroller mode — LOLO-owned pixel space mapped to world units */
|
|
2403
|
+
/* @__PURE__ */ jsx(
|
|
2404
|
+
SideScene,
|
|
2405
|
+
{
|
|
2406
|
+
player,
|
|
2407
|
+
platforms,
|
|
2408
|
+
worldHeight,
|
|
2409
|
+
ppu: pixelsPerUnit,
|
|
2410
|
+
playerSprite,
|
|
2411
|
+
tileSprites,
|
|
2412
|
+
interpolate: interpolateUnits
|
|
2413
|
+
}
|
|
2414
|
+
)
|
|
2415
|
+
) : /* @__PURE__ */ jsxs("group", { children: [
|
|
2216
2416
|
tiles.map((tile, index) => {
|
|
2217
2417
|
const position = gridToWorld2(
|
|
2218
2418
|
tile.x,
|
|
@@ -2233,12 +2433,12 @@ var GameCanvas3D = forwardRef(
|
|
|
2233
2433
|
}),
|
|
2234
2434
|
units.map((unit) => {
|
|
2235
2435
|
const position = gridToWorld2(
|
|
2236
|
-
unit.x ?? 0,
|
|
2237
|
-
unit.z ?? unit.y ?? 0,
|
|
2436
|
+
unit.x ?? unit.position?.x ?? 0,
|
|
2437
|
+
unit.z ?? unit.y ?? unit.position?.y ?? 0,
|
|
2238
2438
|
(unit.elevation ?? 0) + 0.5
|
|
2239
2439
|
);
|
|
2240
2440
|
const Renderer = CustomUnitRenderer || DefaultUnitRenderer;
|
|
2241
|
-
return /* @__PURE__ */ jsx(Renderer, { unit, position }, unit.id);
|
|
2441
|
+
return /* @__PURE__ */ jsx(LerpedGroup, { target: position, enabled: interpolateUnits, children: /* @__PURE__ */ jsx(Renderer, { unit, position: [0, 0, 0] }) }, unit.id);
|
|
2242
2442
|
})
|
|
2243
2443
|
] }),
|
|
2244
2444
|
children,
|
|
@@ -2246,6 +2446,7 @@ var GameCanvas3D = forwardRef(
|
|
|
2246
2446
|
OrbitControls,
|
|
2247
2447
|
{
|
|
2248
2448
|
ref: controlsRef,
|
|
2449
|
+
enabled: cameraMode !== "follow",
|
|
2249
2450
|
target: cameraTarget,
|
|
2250
2451
|
enableDamping: true,
|
|
2251
2452
|
dampingFactor: 0.05,
|
|
@@ -2278,9 +2479,6 @@ var GameCanvas3D = forwardRef(
|
|
|
2278
2479
|
}
|
|
2279
2480
|
);
|
|
2280
2481
|
GameCanvas3D.displayName = "GameCanvas3D";
|
|
2281
|
-
function Canvas3D(props) {
|
|
2282
|
-
return /* @__PURE__ */ jsx(GameCanvas3D, { ...props });
|
|
2283
|
-
}
|
|
2284
2482
|
var DEFAULT_FAMILY = "lucide";
|
|
2285
2483
|
var VALID_FAMILIES = [
|
|
2286
2484
|
"lucide",
|
|
@@ -2353,7 +2551,7 @@ function loadLib(key, importer) {
|
|
|
2353
2551
|
return p;
|
|
2354
2552
|
}
|
|
2355
2553
|
function lazyFamilyIcon(libKey, importer, pick, fallbackName, family) {
|
|
2356
|
-
const Lazy =
|
|
2554
|
+
const Lazy = React8__default.lazy(async () => {
|
|
2357
2555
|
const lib = await loadLib(libKey, importer);
|
|
2358
2556
|
const Comp = pick(lib);
|
|
2359
2557
|
if (!Comp) {
|
|
@@ -2363,7 +2561,7 @@ function lazyFamilyIcon(libKey, importer, pick, fallbackName, family) {
|
|
|
2363
2561
|
return { default: Comp };
|
|
2364
2562
|
});
|
|
2365
2563
|
const Wrapped = (props) => /* @__PURE__ */ jsx(
|
|
2366
|
-
|
|
2564
|
+
React8__default.Suspense,
|
|
2367
2565
|
{
|
|
2368
2566
|
fallback: /* @__PURE__ */ jsx(
|
|
2369
2567
|
"span",
|
|
@@ -3032,7 +3230,7 @@ var Icon = ({
|
|
|
3032
3230
|
const directIcon = typeof icon === "string" ? void 0 : icon;
|
|
3033
3231
|
const effectiveName = typeof icon === "string" ? icon : name;
|
|
3034
3232
|
const family = useIconFamily();
|
|
3035
|
-
const RenderedComponent =
|
|
3233
|
+
const RenderedComponent = React8__default.useMemo(() => {
|
|
3036
3234
|
if (directIcon) return null;
|
|
3037
3235
|
return effectiveName ? resolveIconForFamily(effectiveName, family) : null;
|
|
3038
3236
|
}, [directIcon, effectiveName, family]);
|
|
@@ -3080,6 +3278,143 @@ var Icon = ({
|
|
|
3080
3278
|
);
|
|
3081
3279
|
};
|
|
3082
3280
|
Icon.displayName = "Icon";
|
|
3281
|
+
|
|
3282
|
+
// lib/atlasSlice.ts
|
|
3283
|
+
var atlasCache = /* @__PURE__ */ new Map();
|
|
3284
|
+
function isTilesheet(a) {
|
|
3285
|
+
return typeof a.tileWidth === "number";
|
|
3286
|
+
}
|
|
3287
|
+
function getAtlas(url, onReady) {
|
|
3288
|
+
if (atlasCache.has(url)) return atlasCache.get(url) ?? void 0;
|
|
3289
|
+
atlasCache.set(url, void 0);
|
|
3290
|
+
fetch(url).then((r) => r.json()).then((json) => {
|
|
3291
|
+
atlasCache.set(url, json);
|
|
3292
|
+
onReady();
|
|
3293
|
+
}).catch(() => {
|
|
3294
|
+
atlasCache.set(url, null);
|
|
3295
|
+
});
|
|
3296
|
+
return void 0;
|
|
3297
|
+
}
|
|
3298
|
+
function subRectFor(atlas, sprite) {
|
|
3299
|
+
if (isTilesheet(atlas)) {
|
|
3300
|
+
let col;
|
|
3301
|
+
let row;
|
|
3302
|
+
if (sprite.includes(",")) {
|
|
3303
|
+
const [c, r] = sprite.split(",").map((n) => Number(n.trim()));
|
|
3304
|
+
col = c;
|
|
3305
|
+
row = r;
|
|
3306
|
+
} else {
|
|
3307
|
+
const i = Number(sprite);
|
|
3308
|
+
if (!Number.isFinite(i)) return null;
|
|
3309
|
+
col = i % atlas.columns;
|
|
3310
|
+
row = Math.floor(i / atlas.columns);
|
|
3311
|
+
}
|
|
3312
|
+
if (!Number.isFinite(col) || !Number.isFinite(row) || col < 0 || row < 0 || col >= atlas.columns || row >= atlas.rows) return null;
|
|
3313
|
+
const margin = atlas.margin ?? 0;
|
|
3314
|
+
const spacing = atlas.spacing ?? 0;
|
|
3315
|
+
return {
|
|
3316
|
+
sx: margin + col * (atlas.tileWidth + spacing),
|
|
3317
|
+
sy: margin + row * (atlas.tileHeight + spacing),
|
|
3318
|
+
sw: atlas.tileWidth,
|
|
3319
|
+
sh: atlas.tileHeight
|
|
3320
|
+
};
|
|
3321
|
+
}
|
|
3322
|
+
const st = atlas.subTextures[sprite];
|
|
3323
|
+
if (!st) return null;
|
|
3324
|
+
return { sx: st.x, sy: st.y, sw: st.width, sh: st.height };
|
|
3325
|
+
}
|
|
3326
|
+
function isAtlasAsset(asset) {
|
|
3327
|
+
return !!asset && typeof asset.atlas === "string" && typeof asset.sprite === "string";
|
|
3328
|
+
}
|
|
3329
|
+
var imageCache = /* @__PURE__ */ new Map();
|
|
3330
|
+
var imageWaiters = /* @__PURE__ */ new Map();
|
|
3331
|
+
function getSheetImage(url, onReady) {
|
|
3332
|
+
const cached = imageCache.get(url);
|
|
3333
|
+
if (cached) return cached;
|
|
3334
|
+
(imageWaiters.get(url) ?? imageWaiters.set(url, /* @__PURE__ */ new Set()).get(url)).add(onReady);
|
|
3335
|
+
if (!imageCache.has(url)) {
|
|
3336
|
+
imageCache.set(url, null);
|
|
3337
|
+
const img = new Image();
|
|
3338
|
+
img.crossOrigin = "anonymous";
|
|
3339
|
+
img.onload = () => {
|
|
3340
|
+
imageCache.set(url, img);
|
|
3341
|
+
imageWaiters.get(url)?.forEach((fn) => fn());
|
|
3342
|
+
imageWaiters.delete(url);
|
|
3343
|
+
};
|
|
3344
|
+
img.src = url;
|
|
3345
|
+
}
|
|
3346
|
+
return null;
|
|
3347
|
+
}
|
|
3348
|
+
function AtlasImage({
|
|
3349
|
+
asset,
|
|
3350
|
+
size,
|
|
3351
|
+
width,
|
|
3352
|
+
height,
|
|
3353
|
+
fill = false,
|
|
3354
|
+
fit = "contain",
|
|
3355
|
+
alt,
|
|
3356
|
+
className,
|
|
3357
|
+
style,
|
|
3358
|
+
"aria-hidden": ariaHidden
|
|
3359
|
+
}) {
|
|
3360
|
+
const [, bump] = React8.useReducer((x) => x + 1, 0);
|
|
3361
|
+
const canvasRef = React8.useRef(null);
|
|
3362
|
+
const sliced = isAtlasAsset(asset);
|
|
3363
|
+
const atlas = sliced ? getAtlas(asset.atlas, bump) : void 0;
|
|
3364
|
+
const img = sliced && asset?.url ? getSheetImage(asset.url, bump) : null;
|
|
3365
|
+
const rect = sliced && atlas ? subRectFor(atlas, asset.sprite) : null;
|
|
3366
|
+
React8.useEffect(() => {
|
|
3367
|
+
const canvas = canvasRef.current;
|
|
3368
|
+
if (!canvas || !img || !rect) return;
|
|
3369
|
+
canvas.width = rect.sw;
|
|
3370
|
+
canvas.height = rect.sh;
|
|
3371
|
+
const ctx = canvas.getContext("2d");
|
|
3372
|
+
if (!ctx) return;
|
|
3373
|
+
ctx.imageSmoothingEnabled = false;
|
|
3374
|
+
ctx.clearRect(0, 0, rect.sw, rect.sh);
|
|
3375
|
+
ctx.drawImage(img, rect.sx, rect.sy, rect.sw, rect.sh, 0, 0, rect.sw, rect.sh);
|
|
3376
|
+
}, [img, rect?.sx, rect?.sy, rect?.sw, rect?.sh]);
|
|
3377
|
+
const w = fill ? "100%" : width ?? size;
|
|
3378
|
+
const h = fill ? "100%" : height ?? size;
|
|
3379
|
+
const boxStyle = {
|
|
3380
|
+
...fill ? { position: "absolute", inset: 0 } : {},
|
|
3381
|
+
width: w,
|
|
3382
|
+
height: h,
|
|
3383
|
+
objectFit: fit,
|
|
3384
|
+
imageRendering: "pixelated",
|
|
3385
|
+
...style
|
|
3386
|
+
};
|
|
3387
|
+
if (!asset?.url) return null;
|
|
3388
|
+
if (sliced) {
|
|
3389
|
+
if (!rect || !img) {
|
|
3390
|
+
return /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: cn("inline-block flex-shrink-0", className), style: { ...boxStyle, objectFit: void 0 } });
|
|
3391
|
+
}
|
|
3392
|
+
return /* @__PURE__ */ jsx(
|
|
3393
|
+
"canvas",
|
|
3394
|
+
{
|
|
3395
|
+
ref: canvasRef,
|
|
3396
|
+
role: ariaHidden ? void 0 : "img",
|
|
3397
|
+
"aria-hidden": ariaHidden,
|
|
3398
|
+
"aria-label": ariaHidden ? void 0 : alt ?? asset.name ?? asset.category ?? "",
|
|
3399
|
+
className: cn("flex-shrink-0", className),
|
|
3400
|
+
style: boxStyle
|
|
3401
|
+
}
|
|
3402
|
+
);
|
|
3403
|
+
}
|
|
3404
|
+
return /* @__PURE__ */ jsx(
|
|
3405
|
+
"img",
|
|
3406
|
+
{
|
|
3407
|
+
src: asset.url,
|
|
3408
|
+
alt: alt ?? asset.name ?? asset.category ?? "",
|
|
3409
|
+
"aria-hidden": ariaHidden,
|
|
3410
|
+
...typeof w === "number" ? { width: w } : {},
|
|
3411
|
+
...typeof h === "number" ? { height: h } : {},
|
|
3412
|
+
className: cn("flex-shrink-0", className),
|
|
3413
|
+
style: boxStyle
|
|
3414
|
+
}
|
|
3415
|
+
);
|
|
3416
|
+
}
|
|
3417
|
+
AtlasImage.displayName = "AtlasImage";
|
|
3083
3418
|
var variantStyles = {
|
|
3084
3419
|
primary: [
|
|
3085
3420
|
"bg-primary text-primary-foreground",
|
|
@@ -3152,7 +3487,7 @@ function resolveIconProp(value, sizeClass) {
|
|
|
3152
3487
|
const IconComp = value;
|
|
3153
3488
|
return /* @__PURE__ */ jsx(IconComp, { className: sizeClass });
|
|
3154
3489
|
}
|
|
3155
|
-
if (
|
|
3490
|
+
if (React8__default.isValidElement(value)) {
|
|
3156
3491
|
return value;
|
|
3157
3492
|
}
|
|
3158
3493
|
if (typeof value === "object" && value !== null && isIconLike(value)) {
|
|
@@ -3161,7 +3496,7 @@ function resolveIconProp(value, sizeClass) {
|
|
|
3161
3496
|
}
|
|
3162
3497
|
return value;
|
|
3163
3498
|
}
|
|
3164
|
-
var Button =
|
|
3499
|
+
var Button = React8__default.forwardRef(
|
|
3165
3500
|
({
|
|
3166
3501
|
className,
|
|
3167
3502
|
variant = "primary",
|
|
@@ -3185,7 +3520,7 @@ var Button = React6.forwardRef(
|
|
|
3185
3520
|
const leftIconValue = leftIcon || iconProp;
|
|
3186
3521
|
const rightIconValue = rightIcon || iconRightProp;
|
|
3187
3522
|
const px = size === "sm" ? 16 : size === "lg" ? 20 : 16;
|
|
3188
|
-
const resolvedLeftIcon = iconAsset?.url ? /* @__PURE__ */ jsx(
|
|
3523
|
+
const resolvedLeftIcon = iconAsset?.url ? /* @__PURE__ */ jsx(AtlasImage, { asset: iconAsset, size: px, alt: iconAsset.name ?? iconAsset.category ?? "", className: "flex-shrink-0" }) : resolveIconProp(leftIconValue, iconSizeStyles[size]);
|
|
3189
3524
|
const resolvedRightIcon = resolveIconProp(rightIconValue, iconSizeStyles[size]);
|
|
3190
3525
|
const handleClick = (e) => {
|
|
3191
3526
|
if (action) {
|
|
@@ -3309,7 +3644,7 @@ var Typography = ({
|
|
|
3309
3644
|
}) => {
|
|
3310
3645
|
const variant = variantProp ?? (level ? `h${level}` : "body1");
|
|
3311
3646
|
const Component2 = as || defaultElements[variant];
|
|
3312
|
-
return
|
|
3647
|
+
return React8__default.createElement(
|
|
3313
3648
|
Component2,
|
|
3314
3649
|
{
|
|
3315
3650
|
id,
|
|
@@ -3382,7 +3717,7 @@ var Stack = ({
|
|
|
3382
3717
|
};
|
|
3383
3718
|
const isHorizontal = direction === "horizontal";
|
|
3384
3719
|
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";
|
|
3385
|
-
return
|
|
3720
|
+
return React8__default.createElement(
|
|
3386
3721
|
Component2,
|
|
3387
3722
|
{
|
|
3388
3723
|
className: cn(
|
|
@@ -3757,7 +4092,7 @@ var positionStyles = {
|
|
|
3757
4092
|
fixed: "fixed",
|
|
3758
4093
|
sticky: "sticky"
|
|
3759
4094
|
};
|
|
3760
|
-
var Box =
|
|
4095
|
+
var Box = React8__default.forwardRef(
|
|
3761
4096
|
({
|
|
3762
4097
|
padding,
|
|
3763
4098
|
paddingX,
|
|
@@ -3822,7 +4157,7 @@ var Box = React6.forwardRef(
|
|
|
3822
4157
|
onPointerDown?.(e);
|
|
3823
4158
|
}, [hoverEvent, tapReveal, triggerProps, onPointerDown]);
|
|
3824
4159
|
const isClickable = action || onClick;
|
|
3825
|
-
return
|
|
4160
|
+
return React8__default.createElement(
|
|
3826
4161
|
Component2,
|
|
3827
4162
|
{
|
|
3828
4163
|
ref,
|
|
@@ -6610,7 +6945,7 @@ var Avl3DViewer = ({
|
|
|
6610
6945
|
const handleTraitClick = useCallback((name) => {
|
|
6611
6946
|
dispatch({ type: "ZOOM_INTO_TRAIT", trait: name, targetPosition: { x: 0, y: 0 } });
|
|
6612
6947
|
}, []);
|
|
6613
|
-
const [highlightedTrait, setHighlightedTrait] =
|
|
6948
|
+
const [highlightedTrait, setHighlightedTrait] = React8__default.useState(null);
|
|
6614
6949
|
const handleTransitionClick = useCallback((index) => {
|
|
6615
6950
|
dispatch({ type: "ZOOM_INTO_TRANSITION", transitionIndex: index, targetPosition: { x: 0, y: 0 } });
|
|
6616
6951
|
}, []);
|
|
@@ -6697,7 +7032,7 @@ var Avl3DViewer = ({
|
|
|
6697
7032
|
gap: "xs",
|
|
6698
7033
|
align: "center",
|
|
6699
7034
|
className: "absolute top-2 left-2 z-10 bg-surface/80 backdrop-blur rounded-md px-3 py-1.5",
|
|
6700
|
-
children: breadcrumbs.map((crumb, i) => /* @__PURE__ */ jsxs(
|
|
7035
|
+
children: breadcrumbs.map((crumb, i) => /* @__PURE__ */ jsxs(React8__default.Fragment, { children: [
|
|
6701
7036
|
i > 0 && /* @__PURE__ */ jsx(Typography, { variant: "small", color: "muted", className: "mx-1", children: "/" }),
|
|
6702
7037
|
i < breadcrumbs.length - 1 ? /* @__PURE__ */ jsx(
|
|
6703
7038
|
Box,
|
|
@@ -6973,4 +7308,4 @@ var SpatialHashGrid = class {
|
|
|
6973
7308
|
}
|
|
6974
7309
|
};
|
|
6975
7310
|
|
|
6976
|
-
export { AVL_3D_COLORS, AssetLoader, Avl3DApplicationScene, Avl3DContext, Avl3DEffects, Avl3DOrbitalScene, Avl3DTraitScene, Avl3DTransitionScene, Avl3DViewer, CAMERA_POSITIONS, Camera3D,
|
|
7311
|
+
export { AVL_3D_COLORS, AssetLoader, Avl3DApplicationScene, Avl3DContext, Avl3DEffects, Avl3DOrbitalScene, Avl3DTraitScene, Avl3DTransitionScene, Avl3DViewer, CAMERA_POSITIONS, Camera3D, Canvas3DErrorBoundary, Canvas3DLoadingState, GameBoard3D, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, Lighting3D, ModelLoader, Scene3D, SpatialHashGrid, arcCurve3D, assetLoader, calculateLODLevel, createGridHighlight, cullInstancedMesh, fibonacciSpherePositions, filterByFrustum, getCellsInRadius, getNeighbors, getVisibleIndices, goldenSpiralPositions, gridDistance, gridManhattanDistance, gridToWorld, isInBounds, isInFrustum, normalizeMouseCoordinates, orbitRingPositions, raycastToObjects, raycastToPlane, selfLoopCurve3D, treeLayout3D, updateInstanceLOD, useAssetLoader, useAvl3DConfig, useGameCanvas3DEvents, useRaycaster, useSceneGraph, useThree3 as useThree, worldToGrid };
|