mirror of
https://github.com/exogen/t2-mapper.git
synced 2026-01-19 20:25:01 +00:00
format with Prettier
This commit is contained in:
parent
c5c43d92d9
commit
c8391a1056
5
.prettierignore
Normal file
5
.prettierignore
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
.claude/
|
||||||
|
docs/
|
||||||
|
generated/
|
||||||
|
public/manifest.json
|
||||||
|
package-lock.json
|
||||||
1
.prettierrc
Normal file
1
.prettierrc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{}
|
||||||
25
README.md
25
README.md
|
|
@ -12,19 +12,18 @@
|
||||||
|
|
||||||
Click inside the map preview area to capture the mouse.
|
Click inside the map preview area to capture the mouse.
|
||||||
|
|
||||||
| Key | Action |
|
| Key | Action |
|
||||||
|-----|--------|
|
| ---------------------------------------- | -------------------- |
|
||||||
| <kbd>W</kbd> | Forward |
|
| <kbd>W</kbd> | Forward |
|
||||||
| <kbd>A</kbd> | Left |
|
| <kbd>A</kbd> | Left |
|
||||||
| <kbd>S</kbd> | Backward |
|
| <kbd>S</kbd> | Backward |
|
||||||
| <kbd>D</kbd> | Right |
|
| <kbd>D</kbd> | Right |
|
||||||
| <kbd>Space</kbd> | Up |
|
| <kbd>Space</kbd> | Up |
|
||||||
| <kbd>Shift</kbd> | Down |
|
| <kbd>Shift</kbd> | Down |
|
||||||
| <kbd>Esc</kbd> | Release mouse |
|
| <kbd>Esc</kbd> | Release mouse |
|
||||||
| <small>Left click</small> | Next observer camera |
|
| <small>Left click</small> | Next observer camera |
|
||||||
| △ <small>Scroll/mouse wheel up</small> | Increase speed |
|
| △ <small>Scroll/mouse wheel up</small> | Increase speed |
|
||||||
| ▽ <small>Scroll/mouse wheel down</small> | Decrease speed |
|
| ▽ <small>Scroll/mouse wheel down</small> | Decrease speed |
|
||||||
|
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ function MapInspector() {
|
||||||
|
|
||||||
// Initialize state from query params
|
// Initialize state from query params
|
||||||
const [missionName, setMissionName] = useState(
|
const [missionName, setMissionName] = useState(
|
||||||
searchParams.get("mission") || "TWL2_WoodyMyrk"
|
searchParams.get("mission") || "TWL2_WoodyMyrk",
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update query params when state changes
|
// Update query params when state changes
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,18 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
font-family:
|
||||||
Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
system-ui,
|
||||||
|
-apple-system,
|
||||||
|
BlinkMacSystemFont,
|
||||||
|
"Segoe UI",
|
||||||
|
Roboto,
|
||||||
|
Oxygen,
|
||||||
|
Ubuntu,
|
||||||
|
Cantarell,
|
||||||
|
"Open Sans",
|
||||||
|
"Helvetica Neue",
|
||||||
|
sans-serif;
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
890
package-lock.json
generated
890
package-lock.json
generated
File diff suppressed because it is too large
Load diff
10
package.json
10
package.json
|
|
@ -11,9 +11,13 @@
|
||||||
"build": "next build && touch docs/.nojekyll",
|
"build": "next build && touch docs/.nojekyll",
|
||||||
"clean": "rimraf .next",
|
"clean": "rimraf .next",
|
||||||
"deploy": "npm run build && git add -f docs && git commit -m \"Deploy\" && git push",
|
"deploy": "npm run build && git add -f docs && git commit -m \"Deploy\" && git push",
|
||||||
|
"format": "prettier --write .",
|
||||||
"postbuild": "git checkout -- public/base",
|
"postbuild": "git checkout -- public/base",
|
||||||
"prebuild": "npm run clean && git checkout -- docs && rimraf public/base && mv docs/base public/",
|
"prebuild": "npm run clean && git checkout -- docs && rimraf public/base && mv docs/base public/",
|
||||||
"start": "next dev --turbopack"
|
"start": "next dev --turbopack",
|
||||||
|
"test": "vitest run",
|
||||||
|
"test:watch": "vitest",
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-three/drei": "^10.7.6",
|
"@react-three/drei": "^10.7.6",
|
||||||
|
|
@ -35,8 +39,10 @@
|
||||||
"@types/three": "^0.180.0",
|
"@types/three": "^0.180.0",
|
||||||
"@types/unzipper": "^0.10.11",
|
"@types/unzipper": "^0.10.11",
|
||||||
"peggy": "^5.0.6",
|
"peggy": "^5.0.6",
|
||||||
|
"prettier": "^3.7.1",
|
||||||
"rimraf": "^6.0.1",
|
"rimraf": "^6.0.1",
|
||||||
"tsx": "^4.20.5",
|
"tsx": "^4.20.5",
|
||||||
"typescript": "5.9.2"
|
"typescript": "5.9.2",
|
||||||
|
"vitest": "^4.0.14"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ async function run({ onlyNew }: { onlyNew: boolean }) {
|
||||||
"--", // args after here go to the script
|
"--", // args after here go to the script
|
||||||
...inputFiles,
|
...inputFiles,
|
||||||
],
|
],
|
||||||
{ stdio: "inherit" }
|
{ stdio: "inherit" },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ async function run() {
|
||||||
// "--no-anims",
|
// "--no-anims",
|
||||||
// "--only-visible",
|
// "--only-visible",
|
||||||
],
|
],
|
||||||
{ stdio: "inherit" }
|
{ stdio: "inherit" },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ async function buildExtractedGameDataFolder() {
|
||||||
archives.set(source, archive);
|
archives.set(source, archive);
|
||||||
}
|
}
|
||||||
const entry = archive.files.find(
|
const entry = archive.files.find(
|
||||||
(entry) => normalizePath(entry.path) === filePath
|
(entry) => normalizePath(entry.path) === filePath,
|
||||||
);
|
);
|
||||||
const inFile = `${inputBaseDir}/${source}:${filePath}`;
|
const inFile = `${inputBaseDir}/${source}:${filePath}`;
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ async function walkDirectory(
|
||||||
entry: Dirent<string>;
|
entry: Dirent<string>;
|
||||||
fullPath: string;
|
fullPath: string;
|
||||||
}) => boolean | Promise<boolean>;
|
}) => boolean | Promise<boolean>;
|
||||||
}
|
},
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const entries = await fs.readdir(dir, { withFileTypes: true });
|
const entries = await fs.readdir(dir, { withFileTypes: true });
|
||||||
|
|
||||||
|
|
@ -102,12 +102,12 @@ async function buildManifest() {
|
||||||
archiveDirs = orderBy(
|
archiveDirs = orderBy(
|
||||||
archiveDirs,
|
archiveDirs,
|
||||||
[(fullPath) => path.basename(fullPath).toLowerCase()],
|
[(fullPath) => path.basename(fullPath).toLowerCase()],
|
||||||
["asc"]
|
["asc"],
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const archivePath of archiveDirs) {
|
for (const archivePath of archiveDirs) {
|
||||||
const relativeArchivePath = normalizePath(
|
const relativeArchivePath = normalizePath(
|
||||||
path.relative(`${baseDir}/@vl2`, archivePath)
|
path.relative(`${baseDir}/@vl2`, archivePath),
|
||||||
);
|
);
|
||||||
await walkDirectory(archivePath, {
|
await walkDirectory(archivePath, {
|
||||||
onFile: ({ dir, entry, fullPath }) => {
|
onFile: ({ dir, entry, fullPath }) => {
|
||||||
|
|
@ -140,7 +140,7 @@ async function buildManifest() {
|
||||||
.map((source) => ` ❗️ ${source}`)
|
.map((source) => ` ❗️ ${source}`)
|
||||||
.join("")
|
.join("")
|
||||||
: ""
|
: ""
|
||||||
}`
|
}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const resolvedPath = lastSource
|
const resolvedPath = lastSource
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ async function run() {
|
||||||
.map((f) => f.match(/^textures\/skins\/(.+)\.ifl$/))
|
.map((f) => f.match(/^textures\/skins\/(.+)\.ifl$/))
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.map((match) => match[1])
|
.map((match) => match[1])
|
||||||
.join("\n")
|
.join("\n"),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
} else if (
|
} else if (
|
||||||
|
|
@ -38,7 +38,7 @@ async function run() {
|
||||||
(!values.name && !positionals[0])
|
(!values.name && !positionals[0])
|
||||||
) {
|
) {
|
||||||
console.error(
|
console.error(
|
||||||
"Must specify exactly one of --name (-n) or a positional filename."
|
"Must specify exactly one of --name (-n) or a positional filename.",
|
||||||
);
|
);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +53,7 @@ async function run() {
|
||||||
inspect(parseImageFrameList(missionScript), {
|
inspect(parseImageFrameList(missionScript), {
|
||||||
colors: true,
|
colors: true,
|
||||||
depth: Infinity,
|
depth: Infinity,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,12 @@ const interiorFile = process.argv[2];
|
||||||
const interiorBuffer = fs.readFileSync(interiorFile);
|
const interiorBuffer = fs.readFileSync(interiorFile);
|
||||||
const interiorArrayBuffer = interiorBuffer.buffer.slice(
|
const interiorArrayBuffer = interiorBuffer.buffer.slice(
|
||||||
interiorBuffer.byteOffset,
|
interiorBuffer.byteOffset,
|
||||||
interiorBuffer.byteOffset + interiorBuffer.byteLength
|
interiorBuffer.byteOffset + interiorBuffer.byteLength,
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
inspect(parseInteriorBuffer(interiorArrayBuffer), {
|
inspect(parseInteriorBuffer(interiorArrayBuffer), {
|
||||||
colors: true,
|
colors: true,
|
||||||
depth: Infinity,
|
depth: Infinity,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ async function run() {
|
||||||
.map((f) => f.match(/^missions\/(.+)\.mis$/))
|
.map((f) => f.match(/^missions\/(.+)\.mis$/))
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.map((match) => match[1])
|
.map((match) => match[1])
|
||||||
.join("\n")
|
.join("\n"),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
} else if (
|
} else if (
|
||||||
|
|
@ -38,7 +38,7 @@ async function run() {
|
||||||
(!values.name && !positionals[0])
|
(!values.name && !positionals[0])
|
||||||
) {
|
) {
|
||||||
console.error(
|
console.error(
|
||||||
"Must specify exactly one of --name (-n) or a positional filename."
|
"Must specify exactly one of --name (-n) or a positional filename.",
|
||||||
);
|
);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +53,7 @@ async function run() {
|
||||||
inspect(parseMissionScript(missionScript), {
|
inspect(parseMissionScript(missionScript), {
|
||||||
colors: true,
|
colors: true,
|
||||||
depth: Infinity,
|
depth: Infinity,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ async function run() {
|
||||||
.map((f) => f.match(/^terrains\/(.+)\.ter$/))
|
.map((f) => f.match(/^terrains\/(.+)\.ter$/))
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.map((match) => match[1])
|
.map((match) => match[1])
|
||||||
.join("\n")
|
.join("\n"),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
} else if (
|
} else if (
|
||||||
|
|
@ -38,7 +38,7 @@ async function run() {
|
||||||
(!values.name && !positionals[0])
|
(!values.name && !positionals[0])
|
||||||
) {
|
) {
|
||||||
console.error(
|
console.error(
|
||||||
"Must specify exactly one of --name (-n) or a positional filename."
|
"Must specify exactly one of --name (-n) or a positional filename.",
|
||||||
);
|
);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -51,14 +51,14 @@ async function run() {
|
||||||
const terrainBuffer = fs.readFileSync(terrainFile);
|
const terrainBuffer = fs.readFileSync(terrainFile);
|
||||||
const terrainArrayBuffer = terrainBuffer.buffer.slice(
|
const terrainArrayBuffer = terrainBuffer.buffer.slice(
|
||||||
terrainBuffer.byteOffset,
|
terrainBuffer.byteOffset,
|
||||||
terrainBuffer.byteOffset + terrainBuffer.byteLength
|
terrainBuffer.byteOffset + terrainBuffer.byteLength,
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
inspect(parseTerrainBuffer(terrainArrayBuffer), {
|
inspect(parseTerrainBuffer(terrainArrayBuffer), {
|
||||||
colors: true,
|
colors: true,
|
||||||
depth: Infinity,
|
depth: Infinity,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ async function run({
|
||||||
console.log(property.value);
|
console.log(property.value);
|
||||||
} else {
|
} else {
|
||||||
console.log(
|
console.log(
|
||||||
`${baseName} > ${consoleObject.className} > ${property.target.name} = ${property.value}`
|
`${baseName} > ${consoleObject.className} > ${property.target.name} = ${property.value}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ export function AudioProvider({ children }: { children: ReactNode }) {
|
||||||
|
|
||||||
// Create listener if not already present
|
// Create listener if not already present
|
||||||
let listener = camera.children.find(
|
let listener = camera.children.find(
|
||||||
(child) => child instanceof AudioListener
|
(child) => child instanceof AudioListener,
|
||||||
) as AudioListener | undefined;
|
) as AudioListener | undefined;
|
||||||
|
|
||||||
if (!listener) {
|
if (!listener) {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ const audioBufferCache = new Map<string, AudioBuffer>();
|
||||||
function getCachedAudioBuffer(
|
function getCachedAudioBuffer(
|
||||||
audioUrl: string,
|
audioUrl: string,
|
||||||
audioLoader: any,
|
audioLoader: any,
|
||||||
onLoad: (buffer: AudioBuffer) => void
|
onLoad: (buffer: AudioBuffer) => void,
|
||||||
) {
|
) {
|
||||||
if (audioBufferCache.has(audioUrl)) {
|
if (audioBufferCache.has(audioUrl)) {
|
||||||
onLoad(audioBufferCache.get(audioUrl)!);
|
onLoad(audioBufferCache.get(audioUrl)!);
|
||||||
|
|
@ -27,7 +27,7 @@ function getCachedAudioBuffer(
|
||||||
undefined,
|
undefined,
|
||||||
(err: any) => {
|
(err: any) => {
|
||||||
console.error("AudioEmitter: Audio load error", audioUrl, err);
|
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 fileName = getProperty(object, "fileName")?.value ?? "";
|
||||||
const volume = parseFloat(getProperty(object, "volume")?.value ?? "1");
|
const volume = parseFloat(getProperty(object, "volume")?.value ?? "1");
|
||||||
const minDistance = parseFloat(
|
const minDistance = parseFloat(
|
||||||
getProperty(object, "minDistance")?.value ?? "1"
|
getProperty(object, "minDistance")?.value ?? "1",
|
||||||
);
|
);
|
||||||
const maxDistance = parseFloat(
|
const maxDistance = parseFloat(
|
||||||
getProperty(object, "maxDistance")?.value ?? "1"
|
getProperty(object, "maxDistance")?.value ?? "1",
|
||||||
);
|
);
|
||||||
const minLoopGap = parseFloat(
|
const minLoopGap = parseFloat(
|
||||||
getProperty(object, "minLoopGap")?.value ?? "0"
|
getProperty(object, "minLoopGap")?.value ?? "0",
|
||||||
);
|
);
|
||||||
const maxLoopGap = parseFloat(
|
const maxLoopGap = parseFloat(
|
||||||
getProperty(object, "maxLoopGap")?.value ?? "0"
|
getProperty(object, "maxLoopGap")?.value ?? "0",
|
||||||
);
|
);
|
||||||
const is3D = parseInt(getProperty(object, "is3D")?.value ?? "0");
|
const is3D = parseInt(getProperty(object, "is3D")?.value ?? "0");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ export function CamerasProvider({ children }: { children: ReactNode }) {
|
||||||
setCameraIndex(index);
|
setCameraIndex(index);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[cameraCount]
|
[cameraCount],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -83,7 +83,7 @@ export function CamerasProvider({ children }: { children: ReactNode }) {
|
||||||
// Apply coordinate system correction for Torque3D to Three.js
|
// Apply coordinate system correction for Torque3D to Three.js
|
||||||
const correction = new Quaternion().setFromAxisAngle(
|
const correction = new Quaternion().setFromAxisAngle(
|
||||||
new Vector3(0, 1, 0),
|
new Vector3(0, 1, 0),
|
||||||
-Math.PI / 2
|
-Math.PI / 2,
|
||||||
);
|
);
|
||||||
camera.quaternion.copy(cameraInfo.rotation).multiply(correction);
|
camera.quaternion.copy(cameraInfo.rotation).multiply(correction);
|
||||||
}
|
}
|
||||||
|
|
@ -97,7 +97,7 @@ export function CamerasProvider({ children }: { children: ReactNode }) {
|
||||||
setCameraIndex: setCamera,
|
setCameraIndex: setCamera,
|
||||||
cameraCount,
|
cameraCount,
|
||||||
}),
|
}),
|
||||||
[registerCamera, unregisterCamera, nextCamera, setCamera, cameraCount]
|
[registerCamera, unregisterCamera, nextCamera, setCamera, cameraCount],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ export const ShapeModel = memo(function ShapeModel() {
|
||||||
|
|
||||||
const hullBoneIndices = useMemo(() => {
|
const hullBoneIndices = useMemo(() => {
|
||||||
const skeletonsFound = Object.values(nodes).filter(
|
const skeletonsFound = Object.values(nodes).filter(
|
||||||
(node: any) => node.skeleton
|
(node: any) => node.skeleton,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (skeletonsFound.length > 0) {
|
if (skeletonsFound.length > 0) {
|
||||||
|
|
@ -97,12 +97,12 @@ export const ShapeModel = memo(function ShapeModel() {
|
||||||
([name, node]: [string, any]) =>
|
([name, node]: [string, any]) =>
|
||||||
node.material &&
|
node.material &&
|
||||||
node.material.name !== "Unassigned" &&
|
node.material.name !== "Unassigned" &&
|
||||||
!node.name.match(/^Hulk/i)
|
!node.name.match(/^Hulk/i),
|
||||||
)
|
)
|
||||||
.map(([name, node]: [string, any]) => {
|
.map(([name, node]: [string, any]) => {
|
||||||
const geometry = filterGeometryByVertexGroups(
|
const geometry = filterGeometryByVertexGroups(
|
||||||
node.geometry,
|
node.geometry,
|
||||||
hullBoneIndices
|
hullBoneIndices,
|
||||||
);
|
);
|
||||||
return { node, geometry };
|
return { node, geometry };
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ const groupedMissions = getMissionList().reduce(
|
||||||
missionName: string;
|
missionName: string;
|
||||||
displayName: string;
|
displayName: string;
|
||||||
}>
|
}>
|
||||||
>()
|
>(),
|
||||||
);
|
);
|
||||||
|
|
||||||
groupedMissions.forEach((groupMissions, groupName) => {
|
groupedMissions.forEach((groupMissions, groupName) => {
|
||||||
|
|
@ -59,8 +59,8 @@ groupedMissions.forEach((groupMissions, groupName) => {
|
||||||
(missionInfo) =>
|
(missionInfo) =>
|
||||||
(missionInfo.displayName || missionInfo.missionName).toLowerCase(),
|
(missionInfo.displayName || missionInfo.missionName).toLowerCase(),
|
||||||
],
|
],
|
||||||
["asc"]
|
["asc"],
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -90,7 +90,7 @@ export function InspectorControls({
|
||||||
groupName === "Official" ? 0 : groupName == null ? 2 : 1,
|
groupName === "Official" ? 0 : groupName == null ? 2 : 1,
|
||||||
([groupName]) => (groupName ? groupName.toLowerCase() : ""),
|
([groupName]) => (groupName ? groupName.toLowerCase() : ""),
|
||||||
],
|
],
|
||||||
["asc", "asc"]
|
["asc", "asc"],
|
||||||
);
|
);
|
||||||
return groups;
|
return groups;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
@ -125,7 +125,7 @@ export function InspectorControls({
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
),
|
||||||
)}
|
)}
|
||||||
</select>
|
</select>
|
||||||
<div className="CheckboxField">
|
<div className="CheckboxField">
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ export const InteriorModel = memo(
|
||||||
{Object.entries(nodes)
|
{Object.entries(nodes)
|
||||||
.filter(
|
.filter(
|
||||||
([name, node]: [string, any]) =>
|
([name, node]: [string, any]) =>
|
||||||
!node.material || !node.material.name.match(/\.\d+$/)
|
!node.material || !node.material.name.match(/\.\d+$/),
|
||||||
)
|
)
|
||||||
.map(([name, node]: [string, any]) => (
|
.map(([name, node]: [string, any]) => (
|
||||||
<InteriorMesh key={name} node={node} />
|
<InteriorMesh key={name} node={node} />
|
||||||
|
|
@ -73,7 +73,7 @@ export const InteriorModel = memo(
|
||||||
{debugMode ? <FloatingLabel>{interiorFile}</FloatingLabel> : null}
|
{debugMode ? <FloatingLabel>{interiorFile}</FloatingLabel> : null}
|
||||||
</group>
|
</group>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
function InteriorPlaceholder({ color }: { color: string }) {
|
function InteriorPlaceholder({ color }: { color: string }) {
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ function getDataBlockShape(dataBlock: string) {
|
||||||
_caseInsensitiveLookup = Object.fromEntries(
|
_caseInsensitiveLookup = Object.fromEntries(
|
||||||
Object.entries(dataBlockToShapeName).map(([key, value]) => {
|
Object.entries(dataBlockToShapeName).map(([key, value]) => {
|
||||||
return [key.toLowerCase(), value];
|
return [key.toLowerCase(), value];
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return _caseInsensitiveLookup[dataBlock.toLowerCase()];
|
return _caseInsensitiveLookup[dataBlock.toLowerCase()];
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ function CameraMovement() {
|
||||||
// updates while mouse wheels have fewer updates but large deltas.
|
// updates while mouse wheels have fewer updates but large deltas.
|
||||||
Math.max(
|
Math.max(
|
||||||
MIN_SPEED_ADJUSTMENT,
|
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;
|
) * direction;
|
||||||
|
|
||||||
setSpeedMultiplier((prev) => {
|
setSpeedMultiplier((prev) => {
|
||||||
|
|
|
||||||
|
|
@ -48,17 +48,17 @@ export function SettingsProvider({ children }: { children: ReactNode }) {
|
||||||
audioEnabled,
|
audioEnabled,
|
||||||
setAudioEnabled,
|
setAudioEnabled,
|
||||||
}),
|
}),
|
||||||
[fogEnabled, speedMultiplier, fov, audioEnabled]
|
[fogEnabled, speedMultiplier, fov, audioEnabled],
|
||||||
);
|
);
|
||||||
|
|
||||||
const debugContext = useMemo(
|
const debugContext = useMemo(
|
||||||
() => ({ debugMode, setDebugMode }),
|
() => ({ debugMode, setDebugMode }),
|
||||||
[debugMode, setDebugMode]
|
[debugMode, setDebugMode],
|
||||||
);
|
);
|
||||||
|
|
||||||
const controlsContext = useMemo(
|
const controlsContext = useMemo(
|
||||||
() => ({ speedMultiplier, setSpeedMultiplier }),
|
() => ({ speedMultiplier, setSpeedMultiplier }),
|
||||||
[speedMultiplier, setSpeedMultiplier]
|
[speedMultiplier, setSpeedMultiplier],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Read persisted settings from localStoarge.
|
// Read persisted settings from localStoarge.
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ export function SkyBox({
|
||||||
FALLBACK_URL,
|
FALLBACK_URL,
|
||||||
FALLBACK_URL,
|
FALLBACK_URL,
|
||||||
],
|
],
|
||||||
[detailMapList]
|
[detailMapList],
|
||||||
);
|
);
|
||||||
|
|
||||||
const skyBox = useCubeTexture(skyBoxFiles, { path: "" });
|
const skyBox = useCubeTexture(skyBoxFiles, { path: "" });
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ function getDataBlockShape(dataBlock: string) {
|
||||||
_caseInsensitiveLookup = Object.fromEntries(
|
_caseInsensitiveLookup = Object.fromEntries(
|
||||||
Object.entries(dataBlockToShapeName).map(([key, value]) => {
|
Object.entries(dataBlockToShapeName).map(([key, value]) => {
|
||||||
return [key.toLowerCase(), value];
|
return [key.toLowerCase(), value];
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return _caseInsensitiveLookup[dataBlock.toLowerCase()];
|
return _caseInsensitiveLookup[dataBlock.toLowerCase()];
|
||||||
|
|
|
||||||
|
|
@ -58,12 +58,12 @@ function BlendedTerrainTextures({
|
||||||
textureNames.map((name) => terrainTextureToUrl(name)),
|
textureNames.map((name) => terrainTextureToUrl(name)),
|
||||||
(textures) => {
|
(textures) => {
|
||||||
textures.forEach((tex) => setupColor(tex));
|
textures.forEach((tex) => setupColor(tex));
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const alphaTextures = useMemo(
|
const alphaTextures = useMemo(
|
||||||
() => alphaMaps.map((data) => setupMask(data)),
|
() => alphaMaps.map((data) => setupMask(data)),
|
||||||
[alphaMaps]
|
[alphaMaps],
|
||||||
);
|
);
|
||||||
|
|
||||||
const tiling = useMemo(
|
const tiling = useMemo(
|
||||||
|
|
@ -75,7 +75,7 @@ function BlendedTerrainTextures({
|
||||||
4: 32,
|
4: 32,
|
||||||
5: 32,
|
5: 32,
|
||||||
}),
|
}),
|
||||||
[]
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onBeforeCompile = useCallback(
|
const onBeforeCompile = useCallback(
|
||||||
|
|
@ -89,7 +89,7 @@ function BlendedTerrainTextures({
|
||||||
debugMode,
|
debugMode,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[baseTextures, alphaTextures, visibilityMask, tiling, debugMode]
|
[baseTextures, alphaTextures, visibilityMask, tiling, debugMode],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -125,7 +125,7 @@ function TerrainMaterial({
|
||||||
256,
|
256,
|
||||||
256,
|
256,
|
||||||
RedFormat,
|
RedFormat,
|
||||||
FloatType
|
FloatType,
|
||||||
);
|
);
|
||||||
displacementMap.colorSpace = NoColorSpace;
|
displacementMap.colorSpace = NoColorSpace;
|
||||||
displacementMap.generateMipmaps = false;
|
displacementMap.generateMipmaps = false;
|
||||||
|
|
@ -168,7 +168,7 @@ function TerrainMaterial({
|
||||||
terrainSize,
|
terrainSize,
|
||||||
terrainSize,
|
terrainSize,
|
||||||
RedFormat,
|
RedFormat,
|
||||||
UnsignedByteType
|
UnsignedByteType,
|
||||||
);
|
);
|
||||||
visibilityMask.colorSpace = NoColorSpace;
|
visibilityMask.colorSpace = NoColorSpace;
|
||||||
visibilityMask.wrapS = visibilityMask.wrapT = ClampToEdgeWrapping;
|
visibilityMask.wrapS = visibilityMask.wrapT = ClampToEdgeWrapping;
|
||||||
|
|
@ -211,7 +211,7 @@ export const TerrainBlock = memo(function TerrainBlock({
|
||||||
const squareSize = useMemo(() => {
|
const squareSize = useMemo(() => {
|
||||||
const squareSizeString: string | undefined = getProperty(
|
const squareSizeString: string | undefined = getProperty(
|
||||||
object,
|
object,
|
||||||
"squareSize"
|
"squareSize",
|
||||||
)?.value;
|
)?.value;
|
||||||
return squareSizeString
|
return squareSizeString
|
||||||
? parseInt(squareSizeString, 10)
|
? parseInt(squareSizeString, 10)
|
||||||
|
|
@ -221,7 +221,7 @@ export const TerrainBlock = memo(function TerrainBlock({
|
||||||
const emptySquares: number[] = useMemo(() => {
|
const emptySquares: number[] = useMemo(() => {
|
||||||
const emptySquaresString: string | undefined = getProperty(
|
const emptySquaresString: string | undefined = getProperty(
|
||||||
object,
|
object,
|
||||||
"emptySquares"
|
"emptySquares",
|
||||||
)?.value;
|
)?.value;
|
||||||
|
|
||||||
return emptySquaresString
|
return emptySquaresString
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ function getDataBlockShape(dataBlock: string) {
|
||||||
_caseInsensitiveLookup = Object.fromEntries(
|
_caseInsensitiveLookup = Object.fromEntries(
|
||||||
Object.entries(dataBlockToShapeName).map(([key, value]) => {
|
Object.entries(dataBlockToShapeName).map(([key, value]) => {
|
||||||
return [key.toLowerCase(), value];
|
return [key.toLowerCase(), value];
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return _caseInsensitiveLookup[dataBlock.toLowerCase()];
|
return _caseInsensitiveLookup[dataBlock.toLowerCase()];
|
||||||
|
|
@ -54,7 +54,7 @@ export function Turret({ object }: { object: ConsoleObject }) {
|
||||||
}
|
}
|
||||||
if (!barrelShapeName) {
|
if (!barrelShapeName) {
|
||||||
console.error(
|
console.error(
|
||||||
`<Turret> missing shape for initialBarrel dataBlock: ${initialBarrel}`
|
`<Turret> missing shape for initialBarrel dataBlock: ${initialBarrel}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { Object3D } from "three";
|
||||||
import { useWorldPosition } from "./useWorldPosition";
|
import { useWorldPosition } from "./useWorldPosition";
|
||||||
|
|
||||||
export function useDistanceFromCamera<T extends Object3D>(
|
export function useDistanceFromCamera<T extends Object3D>(
|
||||||
ref: RefObject<T>
|
ref: RefObject<T>,
|
||||||
): RefObject<number> {
|
): RefObject<number> {
|
||||||
const { camera } = useThree();
|
const { camera } = useThree();
|
||||||
const distanceRef = useRef<number>(null);
|
const distanceRef = useRef<number>(null);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { useRef, RefObject } from "react";
|
||||||
import { Object3D, Vector3 } from "three";
|
import { Object3D, Vector3 } from "three";
|
||||||
|
|
||||||
export function useWorldPosition<T extends Object3D>(
|
export function useWorldPosition<T extends Object3D>(
|
||||||
ref: RefObject<T>
|
ref: RefObject<T>,
|
||||||
): RefObject<Vector3 | null> {
|
): RefObject<Vector3 | null> {
|
||||||
const worldPositionRef = useRef<Vector3 | null>(null);
|
const worldPositionRef = useRef<Vector3 | null>(null);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ export function getActualResourcePathUncached(resourcePath: string) {
|
||||||
|
|
||||||
// First, try exact case-insensitive match
|
// First, try exact case-insensitive match
|
||||||
const foundLowerCase = resourcePaths.find(
|
const foundLowerCase = resourcePaths.find(
|
||||||
(s) => s.toLowerCase() === lowerCased
|
(s) => s.toLowerCase() === lowerCased,
|
||||||
);
|
);
|
||||||
if (foundLowerCase) {
|
if (foundLowerCase) {
|
||||||
return foundLowerCase;
|
return foundLowerCase;
|
||||||
|
|
@ -55,7 +55,7 @@ export function getActualResourcePathUncached(resourcePath: string) {
|
||||||
if (pathWithoutNumber !== resourcePath) {
|
if (pathWithoutNumber !== resourcePath) {
|
||||||
// If we stripped a number, try to find the version without it
|
// If we stripped a number, try to find the version without it
|
||||||
const foundWithoutNumber = resourcePaths.find(
|
const foundWithoutNumber = resourcePaths.find(
|
||||||
(s) => s.toLowerCase() === lowerCasedWithoutNumber
|
(s) => s.toLowerCase() === lowerCasedWithoutNumber,
|
||||||
);
|
);
|
||||||
if (foundWithoutNumber) {
|
if (foundWithoutNumber) {
|
||||||
return foundWithoutNumber;
|
return foundWithoutNumber;
|
||||||
|
|
@ -69,9 +69,9 @@ export function getActualResourcePathUncached(resourcePath: string) {
|
||||||
s
|
s
|
||||||
.replace(
|
.replace(
|
||||||
/^(textures\/)((lush|desert|badlands|lava|ice|jaggedclaw|terrainTiles)\/)/,
|
/^(textures\/)((lush|desert|badlands|lava|ice|jaggedclaw|terrainTiles)\/)/,
|
||||||
"$1"
|
"$1",
|
||||||
)
|
)
|
||||||
.toLowerCase() === lowerCased
|
.toLowerCase() === lowerCased,
|
||||||
);
|
);
|
||||||
if (foundNested) {
|
if (foundNested) {
|
||||||
return foundNested;
|
return foundNested;
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ export function getHullBoneIndices(skeleton: any): Set<number> {
|
||||||
*/
|
*/
|
||||||
export function filterGeometryByVertexGroups(
|
export function filterGeometryByVertexGroups(
|
||||||
geometry: any,
|
geometry: any,
|
||||||
hullBoneIndices: Set<number>
|
hullBoneIndices: Set<number>,
|
||||||
): any {
|
): any {
|
||||||
// If no hull bones or no skinning data, return original geometry
|
// If no hull bones or no skinning data, return original geometry
|
||||||
if (hullBoneIndices.size === 0 || !geometry.attributes.skinIndex) {
|
if (hullBoneIndices.size === 0 || !geometry.attributes.skinIndex) {
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ function parseInstance(instance) {
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(
|
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.
|
// - Remove code-like parts of the script so it's easier to parse.
|
||||||
script = script.replace(
|
script = script.replace(
|
||||||
/(\/\/--- OBJECT WRITE END ---\s+)(?:.|[\r\n])*$/,
|
/(\/\/--- OBJECT WRITE END ---\s+)(?:.|[\r\n])*$/,
|
||||||
"$1"
|
"$1",
|
||||||
);
|
);
|
||||||
|
|
||||||
let objectWriteBegin = /(\/\/--- OBJECT WRITE BEGIN ---\s+)/.exec(script);
|
let objectWriteBegin = /(\/\/--- OBJECT WRITE BEGIN ---\s+)/.exec(script);
|
||||||
|
|
@ -173,7 +173,7 @@ export function parseMissionScript(script) {
|
||||||
globals: mission.sections
|
globals: mission.sections
|
||||||
.filter((section) => !section.name)
|
.filter((section) => !section.name)
|
||||||
.flatMap((section) =>
|
.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) {
|
export function getTerrainFile(mission: Mission) {
|
||||||
const terrainBlock = getTerrainBlock(mission);
|
const terrainBlock = getTerrainBlock(mission);
|
||||||
return terrainBlock.properties.find(
|
return terrainBlock.properties.find(
|
||||||
(prop) => prop.target.name === "terrainFile"
|
(prop) => prop.target.name === "terrainFile",
|
||||||
).value;
|
).value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ const alphaAsRoughnessShaderModifier = (shader: any) => {
|
||||||
#ifdef USE_MAP
|
#ifdef USE_MAP
|
||||||
roughnessFactor = texture2D(map, vMapUv).a * 1;
|
roughnessFactor = texture2D(map, vMapUv).a * 1;
|
||||||
#endif
|
#endif
|
||||||
`
|
`,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ export function setupMask(data) {
|
||||||
256,
|
256,
|
||||||
256,
|
256,
|
||||||
RedFormat, // 1 channel
|
RedFormat, // 1 channel
|
||||||
UnsignedByteType // 8-bit
|
UnsignedByteType, // 8-bit
|
||||||
);
|
);
|
||||||
|
|
||||||
// Masks should stay linear
|
// Masks should stay linear
|
||||||
|
|
@ -132,7 +132,7 @@ float getWireframe(vec2 uv, float gridSize, float lineWidth) {
|
||||||
if (visibility < 0.5) {
|
if (visibility < 0.5) {
|
||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,6 +206,6 @@ float getWireframe(vec2 uv, float gridSize, float lineWidth) {
|
||||||
} else {
|
} else {
|
||||||
diffuseColor.rgb = textureColor;
|
diffuseColor.rgb = textureColor;
|
||||||
}
|
}
|
||||||
`
|
`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
vitest.config.ts
Normal file
13
vitest.config.ts
Normal file
|
|
@ -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, "."),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue