From 7ff386cecca8e6e7b3d603973d225569bb5e35b6 Mon Sep 17 00:00:00 2001 From: tresabhi <43380238+tresabhi@users.noreply.github.com> Date: Mon, 9 Nov 2026 22:07:56 -0600 Subject: [PATCH] animation pause button --- .../TankopediaSkinSwitcher/index.tsx | 32 +++++++++++++++++++ packages/website/src/hooks/useSkin.ts | 8 +++++ .../website/src/hooks/useSkinAnimation.ts | 5 +++ packages/website/src/stores/tankopedia.ts | 2 ++ 4 files changed, 47 insertions(+) diff --git a/packages/website/src/components/TankopediaSkinSwitcher/index.tsx b/packages/website/src/components/TankopediaSkinSwitcher/index.tsx index 6f60d4e1e..6cf61a20c 100644 --- a/packages/website/src/components/TankopediaSkinSwitcher/index.tsx +++ b/packages/website/src/components/TankopediaSkinSwitcher/index.tsx @@ -1,8 +1,10 @@ import { alias } from "@blitzkit/core"; +import { PauseIcon, PlayIcon } from "@radix-ui/react-icons"; import { uniqBy } from "lodash-es"; import { api } from "../../api/dynamic"; import { SKIN_NUMBER } from "../../constants/skinNumber"; import { DuelModule, useDuel } from "../../hooks/useDuel"; +import { hasSkinAnimations } from "../../hooks/useSkin"; import { useUnwrapper } from "../../hooks/useUnwrapper"; import { Tankopedia } from "../../stores/tankopedia"; import { TankopediaDisplay } from "../../stores/tankopediaPersistent/constants"; @@ -20,6 +22,11 @@ export function TankopediaSkinSwitcher() { const skin = Tankopedia.use((state) => state.loaders.skin.requested); const showControls = Tankopedia.use((state) => state.showControls); const display = Tankopedia.use((state) => state.loaders.display.loaded); + const loadedSkin = Tankopedia.use((state) => state.loaders.skin.loaded); + const loadedAttachment = Tankopedia.use( + (state) => state.loaders.attachment.loaded, + ); + const playing = Tankopedia.use((state) => state.animate); const skins = uniqBy( Object.values(camouflageDefinitions.camouflages).filter( @@ -36,6 +43,11 @@ export function TankopediaSkinSwitcher() { ].filter((group) => group.length > 0); const isVisible = showControls && display === TankopediaDisplay.Model; + const hasAnimations = + hasSkinAnimations(tank.id, loadedSkin) || + hasSkinAnimations(tank.id, loadedAttachment); + + console.log("hasAnimations", hasAnimations); return (
@@ -80,6 +92,26 @@ export function TankopediaSkinSwitcher() { })}
))} + + {hasAnimations && ( + + { + Tankopedia.mutate((draft) => { + draft.animate = !draft.animate; + }); + }} + > + {playing ? : } + + + )} ); } diff --git a/packages/website/src/hooks/useSkin.ts b/packages/website/src/hooks/useSkin.ts index 402a05de3..fe8516d46 100644 --- a/packages/website/src/hooks/useSkin.ts +++ b/packages/website/src/hooks/useSkin.ts @@ -79,6 +79,14 @@ function skinPath(id: number, skin: number) { return alias("models_legendary", `/${id}/skins/${skin}.glb`); } +/** whether an already loaded skin of this tank has animations */ +export function hasSkinAnimations(id: number, skin: number | undefined) { + return ( + skin !== undefined && + loadedSkins.get(skinPath(id, skin))?.mixer !== undefined + ); +} + // attachments load the same way as camouflages and can be shown with one export function useSkin(id: number, loader: "skin" | "attachment" = "skin") { const requested = Tankopedia.use((state) => state.loaders[loader].requested); diff --git a/packages/website/src/hooks/useSkinAnimation.ts b/packages/website/src/hooks/useSkinAnimation.ts index 1c686bd28..94d381296 100644 --- a/packages/website/src/hooks/useSkinAnimation.ts +++ b/packages/website/src/hooks/useSkinAnimation.ts @@ -1,8 +1,13 @@ import { invalidate, useFrame } from "@react-three/fiber"; +import { Tankopedia } from "../stores/tankopedia"; import type { Skin } from "./useSkin"; export function useSkinAnimation(skins: Skin[]) { + const animate = Tankopedia.use((state) => state.animate); + useFrame((_, delta) => { + if (animate) return; + for (const { mixer } of skins) { if (!mixer) continue; diff --git a/packages/website/src/stores/tankopedia.ts b/packages/website/src/stores/tankopedia.ts index f24b2341a..0454d954a 100644 --- a/packages/website/src/stores/tankopedia.ts +++ b/packages/website/src/stores/tankopedia.ts @@ -100,6 +100,7 @@ interface Tankopedia { disturbed: boolean; revealed: boolean; showControls: boolean; + animate: boolean; protagonist: TankState; antagonist: TankState; @@ -131,6 +132,7 @@ export const Tankopedia = new Soapstone( disturbed: false, revealed: false, showControls: false, + animate: true, protagonist: createTankState(tank), antagonist: createTankState(tank),