mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-28 13:09:51 +01:00
skin switcher
This commit is contained in:
1 parent
fae0277b69
commit
bf7046c91b
8 files changed
+50
-34
No files matched your search
@@ -23,7 +23,6 @@ export function TankModel() {
|
|||||||
const turretContainer = useRef<Group>(null!);
|
const turretContainer = useRef<Group>(null!);
|
||||||
const gunContainer = useRef<Group>(null!);
|
const gunContainer = useRef<Group>(null!);
|
||||||
const tankModelDefinition = useDuelModel("protagonist");
|
const tankModelDefinition = useDuelModel("protagonist");
|
||||||
const trackModelDefinition = tankModelDefinition.tracks[track.id];
|
|
||||||
const turretModelDefinition = tankModelDefinition.turrets[protagonist.turret];
|
const turretModelDefinition = tankModelDefinition.turrets[protagonist.turret];
|
||||||
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun];
|
const gunModelDefinition = turretModelDefinition.guns[protagonist.gun];
|
||||||
const { gltf } = useModel(protagonist.tank);
|
const { gltf } = useModel(protagonist.tank);
|
||||||
|
|||||||
@@ -37,11 +37,12 @@ import { Tankopedia } from "../../../../../stores/tankopedia";
|
|||||||
import { TankopediaPersistent } from "../../../../../stores/tankopediaPersistent";
|
import { TankopediaPersistent } from "../../../../../stores/tankopediaPersistent";
|
||||||
import { TankopediaDisplay } from "../../../../../stores/tankopediaPersistent/constants";
|
import { TankopediaDisplay } from "../../../../../stores/tankopediaPersistent/constants";
|
||||||
import type { MaybeSkeletonComponentProps } from "../../../../../types/maybeSkeletonComponentProps";
|
import type { MaybeSkeletonComponentProps } from "../../../../../types/maybeSkeletonComponentProps";
|
||||||
import type { ThicknessRange } from "../../../../StaticArmor";
|
|
||||||
import { EqualIcon } from "../../../../EqualIcon";
|
import { EqualIcon } from "../../../../EqualIcon";
|
||||||
import { ModuleButton } from "../../../../ModuleButtons/ModuleButton";
|
import { ModuleButton } from "../../../../ModuleButtons/ModuleButton";
|
||||||
import { ScreenshotButton } from "../../../../ScreenshotButton";
|
import { ScreenshotButton } from "../../../../ScreenshotButton";
|
||||||
import { SmallTankIcon } from "../../../../SmallTankIcon";
|
import { SmallTankIcon } from "../../../../SmallTankIcon";
|
||||||
|
import type { ThicknessRange } from "../../../../StaticArmor";
|
||||||
|
import { SkinSwitcher } from "../../../../TankopediaSkinSwitcher";
|
||||||
import { TankSearch } from "../../../../TankSearch";
|
import { TankSearch } from "../../../../TankSearch";
|
||||||
import { CustomShellButton } from "./components/CustomShellButton";
|
import { CustomShellButton } from "./components/CustomShellButton";
|
||||||
import { DynamicArmorSwitcher } from "./components/DynamicArmorSwitcher";
|
import { DynamicArmorSwitcher } from "./components/DynamicArmorSwitcher";
|
||||||
@@ -50,7 +51,6 @@ import {
|
|||||||
SidebarIconButton,
|
SidebarIconButton,
|
||||||
SidebarIconGroup,
|
SidebarIconGroup,
|
||||||
} from "./components/SidebarIconButton";
|
} from "./components/SidebarIconButton";
|
||||||
import { SkinSwitcher } from "./components/SkinSwitcher";
|
|
||||||
import { SpallLinerSwitcher } from "./components/SpallLinerSwitcher";
|
import { SpallLinerSwitcher } from "./components/SpallLinerSwitcher";
|
||||||
import { Thicknesses } from "./components/Thicknesses";
|
import { Thicknesses } from "./components/Thicknesses";
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
height: calc(100dvh - 16rem);
|
height: calc(100dvh - 16rem);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
.sandbox {
|
.sandbox {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import { ShotDisplay } from "../ShotDisplay";
|
|||||||
import { SmartCanvas } from "../SmartCanvas";
|
import { SmartCanvas } from "../SmartCanvas";
|
||||||
import { StaticArmor, type ThicknessRange } from "../StaticArmor";
|
import { StaticArmor, type ThicknessRange } from "../StaticArmor";
|
||||||
import { TankModel } from "../TankModel";
|
import { TankModel } from "../TankModel";
|
||||||
|
import { TankopediaSkinSwitcher } from "../TankopediaSkinSwitcher";
|
||||||
import styles from "./index.module.css";
|
import styles from "./index.module.css";
|
||||||
|
|
||||||
type TankSandboxProps = MaybeSkeletonComponentProps & {
|
type TankSandboxProps = MaybeSkeletonComponentProps & {
|
||||||
@@ -158,6 +159,8 @@ export function TankopediaSandbox({
|
|||||||
>
|
>
|
||||||
{!skeleton && <Content thicknessRange={thicknessRange} />}
|
{!skeleton && <Content thicknessRange={thicknessRange} />}
|
||||||
</SmartCanvas>
|
</SmartCanvas>
|
||||||
|
|
||||||
|
<TankopediaSkinSwitcher />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
.skin-switcher {
|
||||||
|
pointer-events: auto;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
transition: var(--duration-brief);
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: var(--space-4);
|
||||||
|
|
||||||
|
transform: translate(calc(100% + var(--space-4)), -50%);
|
||||||
|
|
||||||
|
&[data-visible="true"] {
|
||||||
|
transform: translate(0, -50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
+21
-28
@@ -1,22 +1,26 @@
|
|||||||
import { alias } from "@blitzkit/core";
|
import { alias } from "@blitzkit/core";
|
||||||
import { Flex, Tooltip } from "@radix-ui/themes";
|
|
||||||
import { uniqBy } from "lodash-es";
|
import { uniqBy } from "lodash-es";
|
||||||
import { SKIN_NUMBER } from "../../../../../../constants/skinNumber";
|
import { api } from "../../api/dynamic";
|
||||||
import { api } from "../../../../../../core/blitzkit/api";
|
import { SKIN_NUMBER } from "../../constants/skinNumber";
|
||||||
import { useLocale } from "../../../../../../hooks/useLocale";
|
import { DuelModule, useDuel } from "../../hooks/useDuel";
|
||||||
import { Duel } from "../../../../../../stores/duel";
|
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";
|
||||||
import { SidebarIconButton } from "./SidebarIconButton";
|
import { SidebarIconButton } from "../Tankopedia/HeroSection/components/Options/components/SidebarIconButton";
|
||||||
|
import { Tooltip } from "../Tooltip";
|
||||||
|
import styles from "./index.module.css";
|
||||||
|
|
||||||
const camouflageDefinitions = await api.camouflageDefinitions();
|
const camouflageDefinitions = await api.camouflages();
|
||||||
|
|
||||||
|
export function TankopediaSkinSwitcher() {
|
||||||
|
const unwrap = useUnwrapper();
|
||||||
|
|
||||||
|
const tank = useDuel("protagonist", DuelModule.Tank);
|
||||||
|
|
||||||
export function SkinSwitcher() {
|
|
||||||
const tank = Duel.use((state) => state.protagonist.tank);
|
|
||||||
const skin = Tankopedia.use((state) => state.requestedSkin);
|
const skin = Tankopedia.use((state) => state.requestedSkin);
|
||||||
const requestedDisplay = Tankopedia.use((state) => state.requestedDisplay);
|
const requestedDisplay = Tankopedia.use((state) => state.requestedDisplay);
|
||||||
const disturbed = Tankopedia.use((state) => state.disturbed);
|
const disturbed = Tankopedia.use((state) => state.disturbed);
|
||||||
const { unwrap } = useLocale();
|
|
||||||
const skins = uniqBy(
|
const skins = uniqBy(
|
||||||
Object.values(camouflageDefinitions.camouflages).filter(
|
Object.values(camouflageDefinitions.camouflages).filter(
|
||||||
(camouflage) => camouflage.tank_id === tank.id,
|
(camouflage) => camouflage.tank_id === tank.id,
|
||||||
@@ -27,20 +31,9 @@ export function SkinSwitcher() {
|
|||||||
if (skins.length === 0) return null;
|
if (skins.length === 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<div
|
||||||
direction="column"
|
className={styles["skin-switcher"]}
|
||||||
top="50%"
|
data-visible={disturbed && requestedDisplay === TankopediaDisplay.Model}
|
||||||
right={
|
|
||||||
disturbed && requestedDisplay === TankopediaDisplay.Model
|
|
||||||
? "3"
|
|
||||||
: "-4rem"
|
|
||||||
}
|
|
||||||
style={{
|
|
||||||
position: "absolute",
|
|
||||||
transform: "translateY(-50%)",
|
|
||||||
transitionDuration: "200ms",
|
|
||||||
pointerEvents: "auto",
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{skins.map((camouflage, index) => {
|
{skins.map((camouflage, index) => {
|
||||||
const name = unwrap(camouflage.name!).replace(
|
const name = unwrap(camouflage.name!).replace(
|
||||||
@@ -51,7 +44,7 @@ export function SkinSwitcher() {
|
|||||||
const bottom = index === skins.length - 1 ? "var(--radius-full)" : "0";
|
const bottom = index === skins.length - 1 ? "var(--radius-full)" : "0";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip key={camouflage.id} content={name} side="left">
|
<Tooltip key={camouflage.id} tooltip={name} side="left">
|
||||||
<SidebarIconButton
|
<SidebarIconButton
|
||||||
selected={skin === camouflage.id}
|
selected={skin === camouflage.id}
|
||||||
alt={name}
|
alt={name}
|
||||||
@@ -78,6 +71,6 @@ export function SkinSwitcher() {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Flex>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@ import { Tankopedia } from "../stores/tankopedia";
|
|||||||
const equipment = await api.equipment();
|
const equipment = await api.equipment();
|
||||||
const tanks = await api.tanks();
|
const tanks = await api.tanks();
|
||||||
|
|
||||||
|
// TODO: use enum here instead
|
||||||
export const duelSides = ["protagonist", "antagonist"] as const;
|
export const duelSides = ["protagonist", "antagonist"] as const;
|
||||||
|
|
||||||
export type DuelSide = (typeof duelSides)[number];
|
export type DuelSide = (typeof duelSides)[number];
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import type { CamouflagesXml } from "@blitzkit/core";
|
import type { CamouflagesXml } from "@blitzkit/core";
|
||||||
import type { APIContext, GetStaticPathsItem } from "astro";
|
import type { APIContext, GetStaticPathsItem } from "astro";
|
||||||
import sharp from "sharp";
|
import sharp from "sharp";
|
||||||
|
import { api } from "../../../../api/dynamic";
|
||||||
|
import { mixStaticPaths } from "../../../../astro/mixStaticPaths";
|
||||||
import { extractPackedIcon } from "../../../../core/blitz/extractPackedIcon";
|
import { extractPackedIcon } from "../../../../core/blitz/extractPackedIcon";
|
||||||
import { parsePackedSpriteRect } from "../../../../core/blitz/parsePackedSpriteRect";
|
import { parsePackedSpriteRect } from "../../../../core/blitz/parsePackedSpriteRect";
|
||||||
import { api } from "../../../../core/blitzkit/api";
|
|
||||||
import { mixStaticPaths } from "../../../../core/blitzkit/mixStaticPaths";
|
|
||||||
import { vfs } from "../../../../core/blitzkit/vfs";
|
import { vfs } from "../../../../core/blitzkit/vfs";
|
||||||
import { getStaticPaths as _getStaticPaths } from "../../_index";
|
import { getStaticPaths as _getStaticPaths } from "../../_index";
|
||||||
|
|
||||||
export const getStaticPaths = mixStaticPaths(_getStaticPaths, async () => {
|
export const getStaticPaths = mixStaticPaths(_getStaticPaths, async () => {
|
||||||
const camouflageDefinitions = await api.camouflageDefinitions();
|
const camouflageDefinitions = await api.camouflages();
|
||||||
const camouflagesXml = await vfs.xml<{ root: CamouflagesXml }>(
|
const camouflagesXml = await vfs.xml<{ root: CamouflagesXml }>(
|
||||||
"Data/XML/item_defs/vehicles/common/camouflages.xml",
|
"Data/XML/item_defs/vehicles/common/camouflages.xml",
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in new issue
Block a user