();
function getCachedAudioBuffer(
audioUrl: string,
audioLoader: any,
- onLoad: (buffer: AudioBuffer) => void
+ onLoad: (buffer: AudioBuffer) => void,
) {
if (audioBufferCache.has(audioUrl)) {
onLoad(audioBufferCache.get(audioUrl)!);
@@ -27,7 +27,7 @@ function getCachedAudioBuffer(
undefined,
(err: any) => {
console.error("AudioEmitter: Audio load error", audioUrl, err);
- }
+ },
);
}
}
@@ -41,16 +41,16 @@ export const AudioEmitter = memo(function AudioEmitter({
const fileName = getProperty(object, "fileName")?.value ?? "";
const volume = parseFloat(getProperty(object, "volume")?.value ?? "1");
const minDistance = parseFloat(
- getProperty(object, "minDistance")?.value ?? "1"
+ getProperty(object, "minDistance")?.value ?? "1",
);
const maxDistance = parseFloat(
- getProperty(object, "maxDistance")?.value ?? "1"
+ getProperty(object, "maxDistance")?.value ?? "1",
);
const minLoopGap = parseFloat(
- getProperty(object, "minLoopGap")?.value ?? "0"
+ getProperty(object, "minLoopGap")?.value ?? "0",
);
const maxLoopGap = parseFloat(
- getProperty(object, "maxLoopGap")?.value ?? "0"
+ getProperty(object, "maxLoopGap")?.value ?? "0",
);
const is3D = parseInt(getProperty(object, "is3D")?.value ?? "0");
diff --git a/src/components/CamerasProvider.tsx b/src/components/CamerasProvider.tsx
index 49de1708..0f07b78a 100644
--- a/src/components/CamerasProvider.tsx
+++ b/src/components/CamerasProvider.tsx
@@ -71,7 +71,7 @@ export function CamerasProvider({ children }: { children: ReactNode }) {
setCameraIndex(index);
}
},
- [cameraCount]
+ [cameraCount],
);
useEffect(() => {
@@ -83,7 +83,7 @@ export function CamerasProvider({ children }: { children: ReactNode }) {
// Apply coordinate system correction for Torque3D to Three.js
const correction = new Quaternion().setFromAxisAngle(
new Vector3(0, 1, 0),
- -Math.PI / 2
+ -Math.PI / 2,
);
camera.quaternion.copy(cameraInfo.rotation).multiply(correction);
}
@@ -97,7 +97,7 @@ export function CamerasProvider({ children }: { children: ReactNode }) {
setCameraIndex: setCamera,
cameraCount,
}),
- [registerCamera, unregisterCamera, nextCamera, setCamera, cameraCount]
+ [registerCamera, unregisterCamera, nextCamera, setCamera, cameraCount],
);
return (
diff --git a/src/components/GenericShape.tsx b/src/components/GenericShape.tsx
index bcb412e0..4dd3b07c 100644
--- a/src/components/GenericShape.tsx
+++ b/src/components/GenericShape.tsx
@@ -81,7 +81,7 @@ export const ShapeModel = memo(function ShapeModel() {
const hullBoneIndices = useMemo(() => {
const skeletonsFound = Object.values(nodes).filter(
- (node: any) => node.skeleton
+ (node: any) => node.skeleton,
);
if (skeletonsFound.length > 0) {
@@ -97,12 +97,12 @@ export const ShapeModel = memo(function ShapeModel() {
([name, node]: [string, any]) =>
node.material &&
node.material.name !== "Unassigned" &&
- !node.name.match(/^Hulk/i)
+ !node.name.match(/^Hulk/i),
)
.map(([name, node]: [string, any]) => {
const geometry = filterGeometryByVertexGroups(
node.geometry,
- hullBoneIndices
+ hullBoneIndices,
);
return { node, geometry };
});
diff --git a/src/components/InspectorControls.tsx b/src/components/InspectorControls.tsx
index 5e5a15a2..234e976d 100644
--- a/src/components/InspectorControls.tsx
+++ b/src/components/InspectorControls.tsx
@@ -47,7 +47,7 @@ const groupedMissions = getMissionList().reduce(
missionName: string;
displayName: string;
}>
- >()
+ >(),
);
groupedMissions.forEach((groupMissions, groupName) => {
@@ -59,8 +59,8 @@ groupedMissions.forEach((groupMissions, groupName) => {
(missionInfo) =>
(missionInfo.displayName || missionInfo.missionName).toLowerCase(),
],
- ["asc"]
- )
+ ["asc"],
+ ),
);
});
@@ -90,7 +90,7 @@ export function InspectorControls({
groupName === "Official" ? 0 : groupName == null ? 2 : 1,
([groupName]) => (groupName ? groupName.toLowerCase() : ""),
],
- ["asc", "asc"]
+ ["asc", "asc"],
);
return groups;
}, []);
@@ -125,7 +125,7 @@ export function InspectorControls({
))}
- )
+ ),
)}
diff --git a/src/components/InteriorInstance.tsx b/src/components/InteriorInstance.tsx
index 325c832d..e69fbdd8 100644
--- a/src/components/InteriorInstance.tsx
+++ b/src/components/InteriorInstance.tsx
@@ -65,7 +65,7 @@ export const InteriorModel = memo(
{Object.entries(nodes)
.filter(
([name, node]: [string, any]) =>
- !node.material || !node.material.name.match(/\.\d+$/)
+ !node.material || !node.material.name.match(/\.\d+$/),
)
.map(([name, node]: [string, any]) => (
@@ -73,7 +73,7 @@ export const InteriorModel = memo(
{debugMode ? {interiorFile} : null}
);
- }
+ },
);
function InteriorPlaceholder({ color }: { color: string }) {
diff --git a/src/components/Item.tsx b/src/components/Item.tsx
index 94e0d057..612224f4 100644
--- a/src/components/Item.tsx
+++ b/src/components/Item.tsx
@@ -50,7 +50,7 @@ function getDataBlockShape(dataBlock: string) {
_caseInsensitiveLookup = Object.fromEntries(
Object.entries(dataBlockToShapeName).map(([key, value]) => {
return [key.toLowerCase(), value];
- })
+ }),
);
}
return _caseInsensitiveLookup[dataBlock.toLowerCase()];
diff --git a/src/components/ObserverControls.tsx b/src/components/ObserverControls.tsx
index 2b9a045b..73aba793 100644
--- a/src/components/ObserverControls.tsx
+++ b/src/components/ObserverControls.tsx
@@ -98,7 +98,7 @@ function CameraMovement() {
// updates while mouse wheels have fewer updates but large deltas.
Math.max(
MIN_SPEED_ADJUSTMENT,
- Math.min(MAX_SPEED_ADJUSTMENT, Math.abs(e.deltaY * 0.01))
+ Math.min(MAX_SPEED_ADJUSTMENT, Math.abs(e.deltaY * 0.01)),
) * direction;
setSpeedMultiplier((prev) => {
diff --git a/src/components/SettingsProvider.tsx b/src/components/SettingsProvider.tsx
index c2ad4e6b..6cea6e60 100644
--- a/src/components/SettingsProvider.tsx
+++ b/src/components/SettingsProvider.tsx
@@ -48,17 +48,17 @@ export function SettingsProvider({ children }: { children: ReactNode }) {
audioEnabled,
setAudioEnabled,
}),
- [fogEnabled, speedMultiplier, fov, audioEnabled]
+ [fogEnabled, speedMultiplier, fov, audioEnabled],
);
const debugContext = useMemo(
() => ({ debugMode, setDebugMode }),
- [debugMode, setDebugMode]
+ [debugMode, setDebugMode],
);
const controlsContext = useMemo(
() => ({ speedMultiplier, setSpeedMultiplier }),
- [speedMultiplier, setSpeedMultiplier]
+ [speedMultiplier, setSpeedMultiplier],
);
// Read persisted settings from localStoarge.
diff --git a/src/components/Sky.tsx b/src/components/Sky.tsx
index e474e4fa..a3175e19 100644
--- a/src/components/Sky.tsx
+++ b/src/components/Sky.tsx
@@ -49,7 +49,7 @@ export function SkyBox({
FALLBACK_URL,
FALLBACK_URL,
],
- [detailMapList]
+ [detailMapList],
);
const skyBox = useCubeTexture(skyBoxFiles, { path: "" });
diff --git a/src/components/StaticShape.tsx b/src/components/StaticShape.tsx
index efcd6425..6d932e37 100644
--- a/src/components/StaticShape.tsx
+++ b/src/components/StaticShape.tsx
@@ -38,7 +38,7 @@ function getDataBlockShape(dataBlock: string) {
_caseInsensitiveLookup = Object.fromEntries(
Object.entries(dataBlockToShapeName).map(([key, value]) => {
return [key.toLowerCase(), value];
- })
+ }),
);
}
return _caseInsensitiveLookup[dataBlock.toLowerCase()];
diff --git a/src/components/TerrainBlock.tsx b/src/components/TerrainBlock.tsx
index 6005cc4e..f3481cba 100644
--- a/src/components/TerrainBlock.tsx
+++ b/src/components/TerrainBlock.tsx
@@ -58,12 +58,12 @@ function BlendedTerrainTextures({
textureNames.map((name) => terrainTextureToUrl(name)),
(textures) => {
textures.forEach((tex) => setupColor(tex));
- }
+ },
);
const alphaTextures = useMemo(
() => alphaMaps.map((data) => setupMask(data)),
- [alphaMaps]
+ [alphaMaps],
);
const tiling = useMemo(
@@ -75,7 +75,7 @@ function BlendedTerrainTextures({
4: 32,
5: 32,
}),
- []
+ [],
);
const onBeforeCompile = useCallback(
@@ -89,7 +89,7 @@ function BlendedTerrainTextures({
debugMode,
});
},
- [baseTextures, alphaTextures, visibilityMask, tiling, debugMode]
+ [baseTextures, alphaTextures, visibilityMask, tiling, debugMode],
);
return (
@@ -125,7 +125,7 @@ function TerrainMaterial({
256,
256,
RedFormat,
- FloatType
+ FloatType,
);
displacementMap.colorSpace = NoColorSpace;
displacementMap.generateMipmaps = false;
@@ -168,7 +168,7 @@ function TerrainMaterial({
terrainSize,
terrainSize,
RedFormat,
- UnsignedByteType
+ UnsignedByteType,
);
visibilityMask.colorSpace = NoColorSpace;
visibilityMask.wrapS = visibilityMask.wrapT = ClampToEdgeWrapping;
@@ -211,7 +211,7 @@ export const TerrainBlock = memo(function TerrainBlock({
const squareSize = useMemo(() => {
const squareSizeString: string | undefined = getProperty(
object,
- "squareSize"
+ "squareSize",
)?.value;
return squareSizeString
? parseInt(squareSizeString, 10)
@@ -221,7 +221,7 @@ export const TerrainBlock = memo(function TerrainBlock({
const emptySquares: number[] = useMemo(() => {
const emptySquaresString: string | undefined = getProperty(
object,
- "emptySquares"
+ "emptySquares",
)?.value;
return emptySquaresString
diff --git a/src/components/Turret.tsx b/src/components/Turret.tsx
index 43df5f74..92dcce9e 100644
--- a/src/components/Turret.tsx
+++ b/src/components/Turret.tsx
@@ -28,7 +28,7 @@ function getDataBlockShape(dataBlock: string) {
_caseInsensitiveLookup = Object.fromEntries(
Object.entries(dataBlockToShapeName).map(([key, value]) => {
return [key.toLowerCase(), value];
- })
+ }),
);
}
return _caseInsensitiveLookup[dataBlock.toLowerCase()];
@@ -54,7 +54,7 @@ export function Turret({ object }: { object: ConsoleObject }) {
}
if (!barrelShapeName) {
console.error(
- ` missing shape for initialBarrel dataBlock: ${initialBarrel}`
+ ` missing shape for initialBarrel dataBlock: ${initialBarrel}`,
);
}
diff --git a/src/components/useDistanceFromCamera.ts b/src/components/useDistanceFromCamera.ts
index 5936a7c7..676b4ceb 100644
--- a/src/components/useDistanceFromCamera.ts
+++ b/src/components/useDistanceFromCamera.ts
@@ -4,7 +4,7 @@ import { Object3D } from "three";
import { useWorldPosition } from "./useWorldPosition";
export function useDistanceFromCamera(
- ref: RefObject
+ ref: RefObject,
): RefObject {
const { camera } = useThree();
const distanceRef = useRef(null);
diff --git a/src/components/useWorldPosition.ts b/src/components/useWorldPosition.ts
index 2a34582a..2af9bec7 100644
--- a/src/components/useWorldPosition.ts
+++ b/src/components/useWorldPosition.ts
@@ -3,7 +3,7 @@ import { useRef, RefObject } from "react";
import { Object3D, Vector3 } from "three";
export function useWorldPosition(
- ref: RefObject
+ ref: RefObject,
): RefObject {
const worldPositionRef = useRef(null);
diff --git a/src/manifest.ts b/src/manifest.ts
index eb27f5fe..4d49478e 100644
--- a/src/manifest.ts
+++ b/src/manifest.ts
@@ -41,7 +41,7 @@ export function getActualResourcePathUncached(resourcePath: string) {
// First, try exact case-insensitive match
const foundLowerCase = resourcePaths.find(
- (s) => s.toLowerCase() === lowerCased
+ (s) => s.toLowerCase() === lowerCased,
);
if (foundLowerCase) {
return foundLowerCase;
@@ -55,7 +55,7 @@ export function getActualResourcePathUncached(resourcePath: string) {
if (pathWithoutNumber !== resourcePath) {
// If we stripped a number, try to find the version without it
const foundWithoutNumber = resourcePaths.find(
- (s) => s.toLowerCase() === lowerCasedWithoutNumber
+ (s) => s.toLowerCase() === lowerCasedWithoutNumber,
);
if (foundWithoutNumber) {
return foundWithoutNumber;
@@ -69,9 +69,9 @@ export function getActualResourcePathUncached(resourcePath: string) {
s
.replace(
/^(textures\/)((lush|desert|badlands|lava|ice|jaggedclaw|terrainTiles)\/)/,
- "$1"
+ "$1",
)
- .toLowerCase() === lowerCased
+ .toLowerCase() === lowerCased,
);
if (foundNested) {
return foundNested;
diff --git a/src/meshUtils.ts b/src/meshUtils.ts
index ea4109ab..6ef51c87 100644
--- a/src/meshUtils.ts
+++ b/src/meshUtils.ts
@@ -23,7 +23,7 @@ export function getHullBoneIndices(skeleton: any): Set {
*/
export function filterGeometryByVertexGroups(
geometry: any,
- hullBoneIndices: Set
+ hullBoneIndices: Set,
): any {
// If no hull bones or no skinning data, return original geometry
if (hullBoneIndices.size === 0 || !geometry.attributes.skinIndex) {
diff --git a/src/mission.ts b/src/mission.ts
index 1c87e01f..2ab6da5f 100644
--- a/src/mission.ts
+++ b/src/mission.ts
@@ -55,7 +55,7 @@ function parseInstance(instance) {
default:
throw new Error(
- `Unhandled value type: ${def.target.name} = ${def.value.type}`
+ `Unhandled value type: ${def.target.name} = ${def.value.type}`,
);
}
}),
@@ -70,7 +70,7 @@ export function parseMissionScript(script) {
// - Remove code-like parts of the script so it's easier to parse.
script = script.replace(
/(\/\/--- OBJECT WRITE END ---\s+)(?:.|[\r\n])*$/,
- "$1"
+ "$1",
);
let objectWriteBegin = /(\/\/--- OBJECT WRITE BEGIN ---\s+)/.exec(script);
@@ -173,7 +173,7 @@ export function parseMissionScript(script) {
globals: mission.sections
.filter((section) => !section.name)
.flatMap((section) =>
- section.definitions.filter((def) => def.type === "definition")
+ section.definitions.filter((def) => def.type === "definition"),
),
};
}
@@ -202,7 +202,7 @@ export function getTerrainBlock(mission: Mission): ConsoleObject {
export function getTerrainFile(mission: Mission) {
const terrainBlock = getTerrainBlock(mission);
return terrainBlock.properties.find(
- (prop) => prop.target.name === "terrainFile"
+ (prop) => prop.target.name === "terrainFile",
).value;
}
diff --git a/src/shaderMaterials.ts b/src/shaderMaterials.ts
index 552caf93..a3460603 100644
--- a/src/shaderMaterials.ts
+++ b/src/shaderMaterials.ts
@@ -19,7 +19,7 @@ const alphaAsRoughnessShaderModifier = (shader: any) => {
#ifdef USE_MAP
roughnessFactor = texture2D(map, vMapUv).a * 1;
#endif
- `
+ `,
);
};
diff --git a/src/textureUtils.ts b/src/textureUtils.ts
index 8e1bc50c..ae553941 100644
--- a/src/textureUtils.ts
+++ b/src/textureUtils.ts
@@ -30,7 +30,7 @@ export function setupMask(data) {
256,
256,
RedFormat, // 1 channel
- UnsignedByteType // 8-bit
+ UnsignedByteType, // 8-bit
);
// Masks should stay linear
@@ -132,7 +132,7 @@ float getWireframe(vec2 uv, float gridSize, float lineWidth) {
if (visibility < 0.5) {
discard;
}
- `
+ `,
);
}
@@ -206,6 +206,6 @@ float getWireframe(vec2 uv, float gridSize, float lineWidth) {
} else {
diffuseColor.rgb = textureColor;
}
-`
+`,
);
}
diff --git a/vitest.config.ts b/vitest.config.ts
new file mode 100644
index 00000000..48ea18d5
--- /dev/null
+++ b/vitest.config.ts
@@ -0,0 +1,13 @@
+import { defineConfig } from "vitest/config";
+import { resolve } from "node:path";
+
+export default defineConfig({
+ test: {
+ include: ["**/*.spec.ts"],
+ },
+ resolve: {
+ alias: {
+ "@": resolve(__dirname, "."),
+ },
+ },
+});