diff --git a/packages/website/src/components/Guesser.tsx b/packages/website/src/components/Guesser.tsx index 62ff27553..27c7915f0 100644 --- a/packages/website/src/components/Guesser.tsx +++ b/packages/website/src/components/Guesser.tsx @@ -58,12 +58,14 @@ export function Guesser() { const [results, setResults] = useState(null); const [selected, setSelected] = useState(null); const tiers = Guess.use((state) => state.tiers); + const status = useRef(null); const requestSearch = useCallback(() => { setSelected(null); setSearching(true); search(); }, []); + const search = useCallback( debounce(() => { if (!input.current) return; @@ -92,6 +94,20 @@ export function Guesser() { if (guessState !== GuessState.NotGuessed) setSelected(null); }, [guessState]); + useEffect(() => { + const interval = setInterval(() => { + if (!status.current) return; + + status.current.innerText = literals(strings.website.tools.guess.stats, { + correct: correctGuesses, + total: totalGuesses, + streak: streak, + }); + }, 1000 / 200); + + return () => clearInterval(interval); + }, [correctGuesses, totalGuesses, streak]); + return ( - - {literals(strings.website.tools.guess.stats, { - correct: correctGuesses, - total: totalGuesses, - streak: streak, - })} - + @@ -312,7 +322,7 @@ export function Guesser() { ? GuessState.Correct : GuessState.Incorrect; draft.totalGuesses++; - draft.correctGuesses += correct ? 1 : 0; + draft.correctGuesses += correct && draft.tiers.length === 10 ? 1 : 0; draft.streak = correct ? draft.streak + 1 : 0; }); } else { diff --git a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Equalizer.tsx b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Equalizer.tsx index f49200157..1ec26568a 100644 --- a/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Equalizer.tsx +++ b/packages/website/src/components/Tankopedia/CharacteristicsSection/components/Equalizer.tsx @@ -70,37 +70,6 @@ export function Equalizer() { - - - - - - Brought to you by{" "} - {" "} - WoTB News - - - - - - - This feature was brought to you early, ahead of the release of the - game mode, thanks to{" "} - - WoTB News - - . - - - ); } diff --git a/packages/website/src/hooks/useArmor.ts b/packages/website/src/hooks/useArmor.ts index e1a5f05a6..d697bd0ba 100644 --- a/packages/website/src/hooks/useArmor.ts +++ b/packages/website/src/hooks/useArmor.ts @@ -1,23 +1,19 @@ -import { asset } from '@blitzkit/core'; -import { type ObjectMap, useLoader } from '@react-three/fiber'; -import { type GLTF, GLTFLoader } from 'three-stdlib'; - -const cache: Record< - number, - { gltf: GLTF & ObjectMap; hasDynamicArmor: boolean } -> = {}; +import { asset } from "@blitzkit/core"; +import { useLoader } from "@react-three/fiber"; +import { GLTFLoader } from "three-stdlib"; +import { useDispose } from "./useDispose"; export function useArmor(id: number) { - const gltf = useLoader(GLTFLoader, asset(`3d/tanks/armor/${id}.glb`)); + const path = asset(`3d/tanks/armor/${id}.glb`); + const gltf = useLoader(GLTFLoader, path); - if (!cache[id]) { - cache[id] = { - gltf, - hasDynamicArmor: Object.values(gltf.nodes).some( - (node) => - node.name.includes('state_00') || node.name.includes('state_01'), - ), - }; - } - return cache[id]; + useDispose(gltf, path); + + return { + gltf, + hasDynamicArmor: Object.values(gltf.nodes).some( + (node) => + node.name.includes("state_00") || node.name.includes("state_01"), + ), + }; } diff --git a/packages/website/src/hooks/useDispose.ts b/packages/website/src/hooks/useDispose.ts new file mode 100644 index 000000000..2ff4e3685 --- /dev/null +++ b/packages/website/src/hooks/useDispose.ts @@ -0,0 +1,50 @@ +import { useLoader, type ObjectMap } from "@react-three/fiber"; +import { useEffect } from "react"; +import { Cache, Group, Mesh, MeshStandardMaterial, Object3D } from "three"; +import { GLTFLoader, type GLTF } from "three-stdlib"; + +export function useDispose(gltf: GLTF & ObjectMap, path: string) { + useEffect(() => { + return () => { + gltf.scene.traverse((object) => { + if (object instanceof Mesh) { + if (object.material instanceof MeshStandardMaterial) { + const maps = [ + object.material.map, + object.material.aoMap, + object.material.envMap, + object.material.bumpMap, + object.material.alphaMap, + object.material.lightMap, + object.material.normalMap, + object.material.emissiveMap, + object.material.roughnessMap, + object.material.metalnessMap, + object.material.displacementMap, + ]; + + for (const map of maps) { + if (!map) continue; + + map.source.data.close(); + map.dispose(); + } + + object.material.dispose(); + } else { + throw new TypeError( + `Unexpected material type: ${object.material.type}`, + ); + } + } else if (object instanceof Group || object instanceof Object3D) { + // traverse will get children + } else { + throw new TypeError(`Unexpected object type: ${object}`); + } + }); + + useLoader.clear(GLTFLoader, path); + Cache.remove(path); + }; + }, [gltf, path]); +} diff --git a/packages/website/src/hooks/useModel.ts b/packages/website/src/hooks/useModel.ts index 8f6d8a411..c8d532bd5 100644 --- a/packages/website/src/hooks/useModel.ts +++ b/packages/website/src/hooks/useModel.ts @@ -1,30 +1,22 @@ import { asset } from "@blitzkit/core"; -import { type ObjectMap, useLoader } from "@react-three/fiber"; +import { useLoader } from "@react-three/fiber"; import { Mesh, MeshStandardMaterial } from "three"; -import { type GLTF, GLTFLoader } from "three-stdlib"; - -const cache: Record< - number, - { - gltf: GLTF & ObjectMap; - hasPbr: boolean; - } -> = {}; +import { GLTFLoader } from "three-stdlib"; +import { useDispose } from "./useDispose"; export function useModel(id: number) { - const gltf = useLoader(GLTFLoader, asset(`3d/tanks/models/${id}.glb`)); + const path = asset(`3d/tanks/models/${id}.glb`); + const gltf = useLoader(GLTFLoader, path); - if (!cache[id]) { - cache[id] = { - gltf, - hasPbr: Object.values(gltf.nodes).some( - (node) => - node instanceof Mesh && - node.material instanceof MeshStandardMaterial && - node.material.roughnessMap !== null - ), - }; - } + useDispose(gltf, path); - return cache[id]; + return { + gltf, + hasPbr: Object.values(gltf.nodes).some( + (node) => + node instanceof Mesh && + node.material instanceof MeshStandardMaterial && + node.material.roughnessMap !== null, + ), + }; }