Add new skins section

This commit is contained in:
Brian Beck 2024-10-26 18:29:03 -07:00
parent 3fe12cb2b6
commit c2fd1e9395
10 changed files with 106 additions and 59 deletions

View file

@ -1,4 +1,3 @@
import path from "path";
import { globby } from "globby";
import orderBy from "lodash.orderby";
@ -42,19 +41,9 @@ const vehicleModels = [
const T2_SKINS_PATH = process.env.T2_SKINS_PATH || "../t2-skins";
export async function getSkinConfig() {
const [defaultSkins, customSkins, customWeaponSkins] = await Promise.all([
Promise.all(
models.map((name) => globby(`./public/textures/*.${name}.png`))
),
Promise.all(
models.map((name) => globby(`${T2_SKINS_PATH}/docs/skins/*.${name}.png`))
),
Promise.all(
weaponModels.map((name) =>
globby(`${T2_SKINS_PATH}/docs/skins/*/weapon_${name}.png`)
)
),
]);
const defaultSkins = await Promise.all(
models.map((name) => globby(`./public/textures/*.${name}.png`))
);
return {
defaultSkins: models.reduce((skins, name, i) => {
@ -67,29 +56,6 @@ export async function getSkinConfig() {
);
return skins;
}, {}),
customSkins: {
...models.reduce((skins, name, i) => {
skins[name] = orderBy(
customSkins[i].map((name) =>
name.replace(/(^.*\/|\.[lmh](male|female|bioderm)\.png$)/g, "")
),
[(name) => name.toLowerCase()],
["asc"]
);
return skins;
}, {}),
...weaponModels.reduce((skins, name, i) => {
skins[name] = orderBy(
customWeaponSkins[i].map((name) => {
const match = name.match(/\/([^/]+)\/weapon_\w+\.png$/);
return match[1];
}),
[(name) => name.toLowerCase()],
["asc"]
);
return skins;
}, {}),
},
modelDefaults: {
// Players
lmale: "Blood Eagle",

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
self.__BUILD_MANIFEST={__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/":["static/chunks/78e521c3-3739cc27b3254d35.js","static/chunks/95b64a6e-a0ff77d56afeed48.js","static/chunks/31664189-69d752d1129a4958.js","static/chunks/545f34e4-3e66c340444ca8b2.js","static/chunks/1bfc9850-b4ceccea4b74407c.js","static/chunks/d7eeaac4-d223ea230e13423c.js","static/chunks/f580fadb-2911e2fbf64aae5a.js","static/chunks/470-094a8f589946fc6b.js","static/chunks/pages/index-6d3dd4f1be9e3279.js"],"/_error":["static/chunks/pages/_error-54b9fcf45cb5bc62.js"],sortedPages:["/","/_app","/_error"]},self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
self.__BUILD_MANIFEST={__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},"/":["static/chunks/78e521c3-3739cc27b3254d35.js","static/chunks/95b64a6e-a0ff77d56afeed48.js","static/chunks/31664189-69d752d1129a4958.js","static/chunks/545f34e4-3e66c340444ca8b2.js","static/chunks/1bfc9850-b4ceccea4b74407c.js","static/chunks/d7eeaac4-d223ea230e13423c.js","static/chunks/f580fadb-2911e2fbf64aae5a.js","static/chunks/470-094a8f589946fc6b.js","static/chunks/pages/index-f57aecfcfef566e7.js"],"/_error":["static/chunks/pages/_error-54b9fcf45cb5bc62.js"],sortedPages:["/","/_app","/_error"]},self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,13 +1,15 @@
import getConfig from "next/config";
import useWarrior from "./useWarrior";
import { AiTwotoneFolderOpen } from "react-icons/ai";
import { useRef } from "react";
import { useEffect, useRef, useState } from "react";
import useTools from "./useTools";
import { detectFileType } from "./importUtils";
const { publicRuntimeConfig } = getConfig();
const { defaultSkins, customSkins, modelDefaults, materials } =
publicRuntimeConfig;
const { defaultSkins, modelDefaults, materials } = publicRuntimeConfig;
const baseManifestPath = `https://exogen.github.io/t2-skins`;
const defaultCustomSkins = {};
export default function WarriorSelector() {
const {
@ -26,8 +28,49 @@ export default function WarriorSelector() {
const { selectedMaterialIndex, setSelectedMaterialIndex } = useTools();
const materialDefs = materials[actualModel];
const materialDef = materialDefs[selectedMaterialIndex];
const [customSkins, setCustomSkins] =
useState<Record<string, string[]>>(defaultCustomSkins);
const [newSkins, setNewSkins] =
useState<Record<string, string[]>>(defaultCustomSkins);
const [selectedSkinSection, setSelectedSkinSection] = useState<string | null>(
null
);
const fileInputRef = useRef<HTMLInputElement | null>(null);
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
let ignore = false;
const loadCustomSkins = async () => {
let res;
try {
res = await fetch(`${baseManifestPath}/skins.json`, { signal });
} catch (err) {
return;
}
if (!ignore) {
const json = await res.json();
if (!ignore) {
setCustomSkins(json.customSkins ?? {});
setNewSkins(json.newSkins ?? {});
}
}
};
loadCustomSkins();
return () => {
ignore = true;
controller.abort();
};
}, []);
let skinSelectValue = selectedSkin ?? "";
if (selectedSkin && selectedSkinSection) {
skinSelectValue = `${selectedSkinSection}/${selectedSkin}`;
}
return (
<div className="Toolbar">
<div className="Field">
@ -46,10 +89,21 @@ export default function WarriorSelector() {
throw new Error("No data-model-type found");
}
const newModelHasSkin =
defaultSkins[newActualModel]?.includes(selectedSkin) ||
customSkins[newActualModel]?.includes(selectedSkin) ||
(selectedSkin &&
(defaultSkins[newActualModel]?.includes(selectedSkin) ||
customSkins[newActualModel]?.includes(selectedSkin))) ||
false;
// startTransition(() => {
let newModelHasSection = false;
if (
selectedSkin &&
selectedSkinSection === "new" &&
newModelHasSkin
) {
newModelHasSection =
newSkins[newActualModel]?.includes(selectedSkin);
}
setSelectedAnimation(null);
setAnimationPaused(false);
setSelectedModelType(modelType);
@ -59,7 +113,9 @@ export default function WarriorSelector() {
setSelectedSkin(modelDefaults[newActualModel] ?? null);
setSelectedSkinType("default");
}
// });
if (!newModelHasSection) {
setSelectedSkinSection(null);
}
}}
>
<optgroup label="Players" data-model-type="player">
@ -104,15 +160,22 @@ export default function WarriorSelector() {
<div className="Buttons">
<select
id="SkinSelect"
value={selectedSkin ?? ""}
value={skinSelectValue}
onChange={(event) => {
const parentNode = event.target.selectedOptions[0]
.parentNode as HTMLElement;
const skinType = event.target.value
? parentNode.dataset.skinType ?? null
: null;
setSelectedSkin(event.target.value || null);
const skinParts = event.target.value.split("/");
const selectedSkin = skinParts.slice(-1)[0] ?? null;
setSelectedSkin(selectedSkin);
setSelectedSkinType(skinType);
if (skinParts.length > 1) {
setSelectedSkinSection(skinParts[0]);
} else {
setSelectedSkinSection(null);
}
}}
>
<option value="">Select a skin</option>
@ -127,14 +190,31 @@ export default function WarriorSelector() {
);
})}
</optgroup>
{newSkins[actualModel]?.length ? (
<optgroup label="New Skins ✨" data-skin-type="custom">
{newSkins[actualModel]?.map((name: string) => {
return (
<option key={`new/${name}`} value={`new/${name}`}>
{name}
</option>
);
})}
</optgroup>
) : null}
<optgroup label="Custom Skins" data-skin-type="custom">
{customSkins[actualModel]?.map((name: string) => {
return (
<option key={name} value={name}>
{name}
</option>
);
})}
{customSkins === defaultCustomSkins ? (
<option key="loading" value="">
Loading
</option>
) : (
customSkins[actualModel]?.map((name: string) => {
return (
<option key={name} value={name}>
{name}
</option>
);
})
)}
</optgroup>
</>
) : null}
@ -199,6 +279,7 @@ export default function WarriorSelector() {
}
});
setSelectedSkin(null);
setSelectedSkinSection(null);
setSkinImageUrls({
[materialDef.file ?? materialDef.name]: [imageUrl],
});