animation pause button

This commit is contained in:
tresabhi committed 2026-11-09 22:07:56 -06:00
1 parent c883f79025
commit 7ff386cecc
4 files changed
+47

No files matched your search

@@ -1,8 +1,10 @@
import { alias } from "@blitzkit/core"; import { alias } from "@blitzkit/core";
import { PauseIcon, PlayIcon } from "@radix-ui/react-icons";
import { uniqBy } from "lodash-es"; import { uniqBy } from "lodash-es";
import { api } from "../../api/dynamic"; import { api } from "../../api/dynamic";
import { SKIN_NUMBER } from "../../constants/skinNumber"; import { SKIN_NUMBER } from "../../constants/skinNumber";
import { DuelModule, useDuel } from "../../hooks/useDuel"; import { DuelModule, useDuel } from "../../hooks/useDuel";
import { hasSkinAnimations } from "../../hooks/useSkin";
import { useUnwrapper } from "../../hooks/useUnwrapper"; import { useUnwrapper } from "../../hooks/useUnwrapper";
import { Tankopedia } from "../../stores/tankopedia"; import { Tankopedia } from "../../stores/tankopedia";
import { TankopediaDisplay } from "../../stores/tankopediaPersistent/constants"; import { TankopediaDisplay } from "../../stores/tankopediaPersistent/constants";
@@ -20,6 +22,11 @@ export function TankopediaSkinSwitcher() {
const skin = Tankopedia.use((state) => state.loaders.skin.requested); const skin = Tankopedia.use((state) => state.loaders.skin.requested);
const showControls = Tankopedia.use((state) => state.showControls); const showControls = Tankopedia.use((state) => state.showControls);
const display = Tankopedia.use((state) => state.loaders.display.loaded); 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( const skins = uniqBy(
Object.values(camouflageDefinitions.camouflages).filter( Object.values(camouflageDefinitions.camouflages).filter(
@@ -36,6 +43,11 @@ export function TankopediaSkinSwitcher() {
].filter((group) => group.length > 0); ].filter((group) => group.length > 0);
const isVisible = showControls && display === TankopediaDisplay.Model; const isVisible = showControls && display === TankopediaDisplay.Model;
const hasAnimations =
hasSkinAnimations(tank.id, loadedSkin) ||
hasSkinAnimations(tank.id, loadedAttachment);
console.log("hasAnimations", hasAnimations);
return ( return (
<div className={styles["skin-switcher"]} data-visible={isVisible}> <div className={styles["skin-switcher"]} data-visible={isVisible}>
@@ -80,6 +92,26 @@ export function TankopediaSkinSwitcher() {
})} })}
</div> </div>
))} ))}
{hasAnimations && (
<Tooltip
tooltip={playing ? "Pause animations" : "Play animations"}
side="left"
>
<IconButton
color="gray"
variant="soft"
size="major"
onClick={() => {
Tankopedia.mutate((draft) => {
draft.animate = !draft.animate;
});
}}
>
{playing ? <PauseIcon /> : <PlayIcon />}
</IconButton>
</Tooltip>
)}
</div> </div>
); );
} }
+8
View File
@@ -79,6 +79,14 @@ function skinPath(id: number, skin: number) {
return alias("models_legendary", `/${id}/skins/${skin}.glb`); 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 // attachments load the same way as camouflages and can be shown with one
export function useSkin(id: number, loader: "skin" | "attachment" = "skin") { export function useSkin(id: number, loader: "skin" | "attachment" = "skin") {
const requested = Tankopedia.use((state) => state.loaders[loader].requested); const requested = Tankopedia.use((state) => state.loaders[loader].requested);
@@ -1,8 +1,13 @@
import { invalidate, useFrame } from "@react-three/fiber"; import { invalidate, useFrame } from "@react-three/fiber";
import { Tankopedia } from "../stores/tankopedia";
import type { Skin } from "./useSkin"; import type { Skin } from "./useSkin";
export function useSkinAnimation(skins: Skin[]) { export function useSkinAnimation(skins: Skin[]) {
const animate = Tankopedia.use((state) => state.animate);
useFrame((_, delta) => { useFrame((_, delta) => {
if (animate) return;
for (const { mixer } of skins) { for (const { mixer } of skins) {
if (!mixer) continue; if (!mixer) continue;
@@ -100,6 +100,7 @@ interface Tankopedia {
disturbed: boolean; disturbed: boolean;
revealed: boolean; revealed: boolean;
showControls: boolean; showControls: boolean;
animate: boolean;
protagonist: TankState; protagonist: TankState;
antagonist: TankState; antagonist: TankState;
@@ -131,6 +132,7 @@ export const Tankopedia = new Soapstone<Tankopedia, [TankDefinition]>(
disturbed: false, disturbed: false,
revealed: false, revealed: false,
showControls: false, showControls: false,
animate: true,
protagonist: createTankState(tank), protagonist: createTankState(tank),
antagonist: createTankState(tank), antagonist: createTankState(tank),