Progress on implementation of skin selector
This commit is contained in:
@@ -1,10 +1,11 @@
|
|||||||
import { Surface } from "@heroui/react";
|
import { Surface } from "@heroui/react";
|
||||||
|
import type { AgentInfo } from "~/types/skin";
|
||||||
|
|
||||||
export function Agent({ agent_id }: { agent_id: string }) {
|
export function Agent({ agent }: { agent: AgentInfo }) {
|
||||||
return (
|
return (
|
||||||
<Surface className="bg-[#1f2937] flex justify-center flex-1 min-h-0 p-4">
|
<Surface className="bg-[#1f2937] flex justify-center flex-1 min-h-0 p-4">
|
||||||
<img
|
<img
|
||||||
src={`/skins/${agent_id}_big.png`}
|
src={`/skins/${agent.name}_big.png`}
|
||||||
className="pointer-events-none object-contain object-bottom h-full w-full"
|
className="pointer-events-none object-contain object-bottom h-full w-full"
|
||||||
alt="Agent image"
|
alt="Agent image"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,31 +1,60 @@
|
|||||||
import { Slider, Label } from "@heroui/react";
|
import { Slider, Label, Typography, Input } from "@heroui/react";
|
||||||
import { useEffect, useState } from "react";
|
import {
|
||||||
|
useEffect,
|
||||||
|
type ChangeEvent,
|
||||||
|
type Dispatch,
|
||||||
|
type SetStateAction,
|
||||||
|
} from "react";
|
||||||
|
import { AgentInfo, WeaponInfo } from "~/types/skin";
|
||||||
|
|
||||||
export function Float({
|
export function Float({
|
||||||
float,
|
skin,
|
||||||
minFloat = 0,
|
setSkin,
|
||||||
maxFloat = 1,
|
|
||||||
acceptInput = false,
|
acceptInput = false,
|
||||||
|
hasLabel = false,
|
||||||
}: {
|
}: {
|
||||||
float: number;
|
skin: WeaponInfo;
|
||||||
|
setSkin: Dispatch<SetStateAction<WeaponInfo | AgentInfo>>;
|
||||||
minFloat?: number;
|
minFloat?: number;
|
||||||
maxFloat?: number;
|
maxFloat?: number;
|
||||||
acceptInput?: boolean;
|
acceptInput?: boolean;
|
||||||
|
hasLabel?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const [value, setValue] = useState(float);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setValue(Math.max(minFloat, Math.min(maxFloat, float)));
|
|
||||||
}, [float, minFloat, maxFloat]);
|
|
||||||
|
|
||||||
const handleChange = (newValue: number | number[]) => {
|
const handleChange = (newValue: number | number[]) => {
|
||||||
const val = Array.isArray(newValue) ? newValue[0]: newValue;
|
const val = Array.isArray(newValue) ? newValue[0] : newValue;
|
||||||
setValue(Math.max(minFloat, Math.min(maxFloat, val)));
|
const newSkin = new WeaponInfo(skin);
|
||||||
|
newSkin.float = Math.max(skin.min_float, Math.min(skin.max_float, val));
|
||||||
|
setSkin(newSkin);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const val = event.currentTarget.valueAsNumber;
|
||||||
|
const newSkin = new WeaponInfo(skin);
|
||||||
|
newSkin.float = Math.max(skin.min_float, Math.min(skin.max_float, val));
|
||||||
|
setSkin(newSkin);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Slider value={value} maxValue={1} step={0.0001} onChange={handleChange}>
|
{hasLabel && (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Typography.Paragraph>Float</Typography.Paragraph>
|
||||||
|
<Input
|
||||||
|
className="flex-1"
|
||||||
|
id="float"
|
||||||
|
type="number"
|
||||||
|
step={0.0001}
|
||||||
|
value={skin.float}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<Slider
|
||||||
|
value={skin.float}
|
||||||
|
maxValue={1}
|
||||||
|
step={0.0001}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
<Slider.Track
|
<Slider.Track
|
||||||
className="border-0 h-2 rounded-xl"
|
className="border-0 h-2 rounded-xl"
|
||||||
style={{
|
style={{
|
||||||
@@ -36,7 +65,7 @@ export function Float({
|
|||||||
<div
|
<div
|
||||||
className="h-full rounded-xl"
|
className="h-full rounded-xl"
|
||||||
style={{
|
style={{
|
||||||
background: `linear-gradient(90deg, rgba(0, 0, 0, 0.8) ${minFloat*100}%, transparent ${minFloat*100}%, transparent ${maxFloat*100}%, rgba(0, 0, 0, 0.8) ${maxFloat*100}%)`,
|
background: `linear-gradient(90deg, rgba(0, 0, 0, 0.8) ${skin.min_float * 100}%, transparent ${skin.min_float * 100}%, transparent ${skin.max_float * 100}%, rgba(0, 0, 0, 0.8) ${skin.max_float * 100}%)`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Slider.Thumb className="w-1" isDisabled={!acceptInput} />
|
<Slider.Thumb className="w-1" isDisabled={!acceptInput} />
|
||||||
|
|||||||
@@ -137,23 +137,23 @@ export function Inventory({ side }: { side: "T" | "CT" }) {
|
|||||||
className="flex flex-col rounded-xl w-1/3 p-4 gap-2 min-h-0"
|
className="flex flex-col rounded-xl w-1/3 p-4 gap-2 min-h-0"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
>
|
>
|
||||||
{agent && <Agent agent_id={agent["name"]}></Agent>}
|
{agent && <Agent agent={agent}></Agent>}
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
{knife && (
|
{knife && (
|
||||||
<SkinTile
|
<SkinTile
|
||||||
skin={knife}
|
skinInfo={knife}
|
||||||
side={side}
|
side={side}
|
||||||
></SkinTile>
|
></SkinTile>
|
||||||
)}
|
)}
|
||||||
{gloves && (
|
{gloves && (
|
||||||
<SkinTile
|
<SkinTile
|
||||||
skin={gloves}
|
skinInfo={gloves}
|
||||||
side={side}
|
side={side}
|
||||||
></SkinTile>
|
></SkinTile>
|
||||||
)}
|
)}
|
||||||
{agent && (
|
{agent && (
|
||||||
<SkinTile
|
<SkinTile
|
||||||
skin={agent}
|
skinInfo={agent}
|
||||||
side={side}
|
side={side}
|
||||||
></SkinTile>
|
></SkinTile>
|
||||||
)}
|
)}
|
||||||
@@ -167,7 +167,7 @@ export function Inventory({ side }: { side: "T" | "CT" }) {
|
|||||||
{weapons.map((v, i) => (
|
{weapons.map((v, i) => (
|
||||||
<SkinTile
|
<SkinTile
|
||||||
key={i}
|
key={i}
|
||||||
skin={v}
|
skinInfo={v}
|
||||||
side={side}
|
side={side}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,34 +1,77 @@
|
|||||||
import { Button, Modal, Surface, Typography } from "@heroui/react";
|
import { Button, Input, Modal, Surface, Typography } from "@heroui/react";
|
||||||
import type { Dispatch, SetStateAction } from "react";
|
import {
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
type ChangeEvent,
|
||||||
|
type Dispatch,
|
||||||
|
type SetStateAction,
|
||||||
|
} from "react";
|
||||||
import { AgentInfo, WeaponInfo } from "~/types/skin";
|
import { AgentInfo, WeaponInfo } from "~/types/skin";
|
||||||
import { Float } from "./float";
|
import { Float } from "./float";
|
||||||
|
import { SkinSelector } from "./skin_selector";
|
||||||
|
|
||||||
export function SkinDialog({
|
export function SkinDialog({
|
||||||
isOpen,
|
isOpen,
|
||||||
setIsOpen,
|
setIsOpen,
|
||||||
skin,
|
skin,
|
||||||
side
|
setSkin,
|
||||||
|
side,
|
||||||
}: {
|
}: {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
||||||
skin: WeaponInfo | AgentInfo;
|
skin: WeaponInfo | AgentInfo;
|
||||||
side: "T" | "CT"
|
setSkin: Dispatch<SetStateAction<WeaponInfo | AgentInfo>>;
|
||||||
|
side: "T" | "CT";
|
||||||
}) {
|
}) {
|
||||||
const acolor = `${skin.color}64`;
|
const acolor = `${skin.color}64`;
|
||||||
|
const [selectorOpen, setSelectorOpen] = useState(false);
|
||||||
|
const [canClose, setCanClose] = useState(true);
|
||||||
|
const [localSkin, setLocalSkin] = useState(skin);
|
||||||
|
|
||||||
|
const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
if (localSkin instanceof AgentInfo) return;
|
||||||
|
const newSkin = new WeaponInfo(localSkin);
|
||||||
|
newSkin.pattern = event.currentTarget.valueAsNumber;
|
||||||
|
setLocalSkin(newSkin);
|
||||||
|
};
|
||||||
|
|
||||||
|
const applySkin = (side: "SIDE" | "BOTH") => {
|
||||||
|
setSkin(localSkin);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!selectorOpen) {
|
||||||
|
setTimeout(() => {
|
||||||
|
setCanClose(true);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}, [selectorOpen]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOpen) {
|
||||||
|
setLocalSkin(skin);
|
||||||
|
}
|
||||||
|
}, [isOpen, skin]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<Modal isOpen={isOpen}>
|
<Modal isOpen={isOpen}>
|
||||||
<Modal.Backdrop
|
<Modal.Backdrop
|
||||||
variant="blur"
|
variant="blur"
|
||||||
shouldCloseOnInteractOutside={(_element) => {
|
shouldCloseOnInteractOutside={(_element) => {
|
||||||
|
if (canClose) {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
return true;
|
}
|
||||||
|
return canClose;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Modal.Container size="lg">
|
<Modal.Container size="lg">
|
||||||
<Modal.Dialog>
|
<Modal.Dialog>
|
||||||
<Modal.CloseTrigger
|
<Modal.CloseTrigger
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
|
if (!selectorOpen) {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Modal.Body>
|
<Modal.Body>
|
||||||
@@ -37,9 +80,15 @@ export function SkinDialog({
|
|||||||
level={4}
|
level={4}
|
||||||
className="py-1 text-wrap"
|
className="py-1 text-wrap"
|
||||||
>
|
>
|
||||||
{skin.display}
|
{localSkin.display}
|
||||||
</Typography.Heading>
|
</Typography.Heading>
|
||||||
<Button className="bg-mist-700 flex flex-col h-full p-0 rounded-none gap-0 hover:bg-indigo-800 active:bg-indigo-900">
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setCanClose(false);
|
||||||
|
setSelectorOpen(true);
|
||||||
|
}}
|
||||||
|
className="bg-mist-700 flex flex-col h-full p-0 rounded-none gap-0 hover:bg-indigo-800 active:bg-indigo-900"
|
||||||
|
>
|
||||||
<div className="flex flex-col w-full h-full">
|
<div className="flex flex-col w-full h-full">
|
||||||
<div
|
<div
|
||||||
className="bg-linear-to-b pointer-events-none w-full h-full relative"
|
className="bg-linear-to-b pointer-events-none w-full h-full relative"
|
||||||
@@ -48,11 +97,14 @@ export function SkinDialog({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={`/skins/${skin.name}_${skin instanceof WeaponInfo ? skin.paint_index : 0}.png`}
|
src={`/skins/${localSkin.name}_${localSkin instanceof WeaponInfo ? localSkin.paint_index : 0}.png`}
|
||||||
className="pointer-events-none object-contain object-bottom h-full"
|
className="pointer-events-none object-contain object-bottom h-full"
|
||||||
alt="Weapon image"
|
alt="Weapon image"
|
||||||
/>
|
/>
|
||||||
<Surface className="rounded-full w-fit h-fit p-2 absolute bottom-2 right-2" variant="secondary">
|
<Surface
|
||||||
|
className="rounded-full w-fit h-fit p-2 absolute bottom-2 right-2"
|
||||||
|
variant="secondary"
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
fill="none"
|
||||||
@@ -72,30 +124,49 @@ export function SkinDialog({
|
|||||||
<div
|
<div
|
||||||
className={`h-1 w-full`}
|
className={`h-1 w-full`}
|
||||||
style={{
|
style={{
|
||||||
background: `${skin.color}`,
|
background: `${localSkin.color}`,
|
||||||
}}
|
}}
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
{skin instanceof WeaponInfo && (
|
{localSkin instanceof WeaponInfo && (
|
||||||
<>
|
<Float
|
||||||
<Typography.Paragraph>
|
skin={localSkin}
|
||||||
Float: {skin.float}
|
setSkin={setLocalSkin}
|
||||||
</Typography.Paragraph>
|
acceptInput
|
||||||
<Float float={skin.float} minFloat={skin.min_float} maxFloat={skin.max_float} acceptInput/>
|
hasLabel
|
||||||
</>
|
/>
|
||||||
)}
|
)}
|
||||||
{skin instanceof WeaponInfo && (
|
{localSkin instanceof WeaponInfo && (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
<Typography.Paragraph>
|
<Typography.Paragraph>
|
||||||
Pattern: {skin.pattern}
|
Pattern:{" "}
|
||||||
</Typography.Paragraph>
|
</Typography.Paragraph>
|
||||||
|
<Input
|
||||||
|
className="flex-1"
|
||||||
|
id="pattern"
|
||||||
|
type="number"
|
||||||
|
step={1}
|
||||||
|
value={localSkin.pattern}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
<Typography.Code>
|
<Typography.Code>
|
||||||
Transaction id: {skin.id}
|
Transaction id: {localSkin.id}
|
||||||
</Typography.Code>
|
</Typography.Code>
|
||||||
<div className="flex gap-2 justify-end">
|
<div className="flex gap-2 justify-end">
|
||||||
{skin instanceof WeaponInfo && <Button variant="secondary">Apply to Both</Button>}
|
{localSkin instanceof WeaponInfo && (
|
||||||
<Button>Apply{skin instanceof WeaponInfo ? ` to ${side}` : ""}</Button>
|
<Button onClick={() => {applySkin("BOTH")}} variant="secondary">
|
||||||
|
Apply to Both
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button onClick={() => {applySkin("SIDE")}}>
|
||||||
|
Apply
|
||||||
|
{localSkin instanceof WeaponInfo
|
||||||
|
? ` to ${side}`
|
||||||
|
: ""}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal.Body>
|
</Modal.Body>
|
||||||
@@ -103,5 +174,13 @@ export function SkinDialog({
|
|||||||
</Modal.Container>
|
</Modal.Container>
|
||||||
</Modal.Backdrop>
|
</Modal.Backdrop>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
<SkinSelector
|
||||||
|
isOpen={selectorOpen}
|
||||||
|
setIsOpen={setSelectorOpen}
|
||||||
|
skin={localSkin}
|
||||||
|
setSkin={setLocalSkin}
|
||||||
|
side={side}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
115
app/components/skin_selector.tsx
Normal file
115
app/components/skin_selector.tsx
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
import { Button, Modal, Typography } from "@heroui/react";
|
||||||
|
import { useEffect, useState, type Dispatch, type SetStateAction } from "react";
|
||||||
|
import { useNotification } from "~/hooks/notification";
|
||||||
|
import { AgentInfo, WeaponInfo } from "~/types/skin";
|
||||||
|
|
||||||
|
interface Info {
|
||||||
|
name: string;
|
||||||
|
paint_index: number;
|
||||||
|
display: string;
|
||||||
|
color: string;
|
||||||
|
min_float: number;
|
||||||
|
max_float: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SkinSelector({
|
||||||
|
isOpen,
|
||||||
|
setIsOpen,
|
||||||
|
skin,
|
||||||
|
setSkin,
|
||||||
|
side
|
||||||
|
}: {
|
||||||
|
isOpen: boolean;
|
||||||
|
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
||||||
|
skin: WeaponInfo | AgentInfo;
|
||||||
|
setSkin: Dispatch<SetStateAction<WeaponInfo | AgentInfo>>;
|
||||||
|
side: "T" | "CT" | "BOTH";
|
||||||
|
}) {
|
||||||
|
const [skins, setSkins] = useState<Array<Info>>([]);
|
||||||
|
const { notify } = useNotification();
|
||||||
|
|
||||||
|
const setPaint = (v: Info) => {
|
||||||
|
if (skin instanceof WeaponInfo) {
|
||||||
|
const newSkin = new WeaponInfo(skin);
|
||||||
|
newSkin.paint_index = v.paint_index;
|
||||||
|
newSkin.display = newSkin.display.split("|")[0] + " | " + v.display;
|
||||||
|
newSkin.min_float = v.min_float;
|
||||||
|
newSkin.max_float = v.max_float;
|
||||||
|
setSkin(newSkin);
|
||||||
|
} else {
|
||||||
|
const newSkin = new AgentInfo(skin);
|
||||||
|
newSkin.name = v.name;
|
||||||
|
setSkin(newSkin);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchSkins = async () => {
|
||||||
|
let response = await fetch(
|
||||||
|
`${import.meta.env.VITE_BASE_API_URL}/index?weapon=${skin.weapon_id}&team=${side}${skin instanceof AgentInfo ? "&agent=true" : ""}`,
|
||||||
|
{ credentials: "include" },
|
||||||
|
)
|
||||||
|
.then((r) => r.json())
|
||||||
|
.catch((e) => {
|
||||||
|
notify(
|
||||||
|
{
|
||||||
|
type: "danger",
|
||||||
|
loading: false,
|
||||||
|
title: "An error occured.",
|
||||||
|
message: `An error occured: ${e}`,
|
||||||
|
},
|
||||||
|
10000,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
setSkins(response);
|
||||||
|
};
|
||||||
|
if (skins.length === 0 && isOpen) {
|
||||||
|
fetchSkins();
|
||||||
|
}
|
||||||
|
}, [isOpen]);
|
||||||
|
return (
|
||||||
|
<Modal isOpen={isOpen}>
|
||||||
|
<Modal.Backdrop
|
||||||
|
variant="blur"
|
||||||
|
shouldCloseOnInteractOutside={(_element) => {
|
||||||
|
setIsOpen(false);
|
||||||
|
return true;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Modal.Container size="lg">
|
||||||
|
<Modal.Dialog>
|
||||||
|
<Modal.CloseTrigger
|
||||||
|
onPress={() => {
|
||||||
|
setIsOpen(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Modal.Body className="mt-6">
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
{skins &&
|
||||||
|
skins.map((v, i) => (
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setPaint(v);
|
||||||
|
}}
|
||||||
|
className="flex flex-col w-full h-fit items-center justify-start"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
key={i}
|
||||||
|
src={`/skins/${v.name}_${skin instanceof WeaponInfo ? v.paint_index : 0}.png`}
|
||||||
|
className="pointer-events-none object-contain object-bottom"
|
||||||
|
alt="Weapon image"
|
||||||
|
/>
|
||||||
|
<Typography.Heading level={3}>
|
||||||
|
{v.display}
|
||||||
|
</Typography.Heading>
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Modal.Body>
|
||||||
|
</Modal.Dialog>
|
||||||
|
</Modal.Container>
|
||||||
|
</Modal.Backdrop>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -5,20 +5,22 @@ import { SkinDialog } from "./skin_dialog";
|
|||||||
import { WeaponInfo, AgentInfo } from "~/types/skin";
|
import { WeaponInfo, AgentInfo } from "~/types/skin";
|
||||||
|
|
||||||
export function SkinTile({
|
export function SkinTile({
|
||||||
skin,
|
skinInfo,
|
||||||
side,
|
side,
|
||||||
}: {
|
}: {
|
||||||
skin: WeaponInfo | AgentInfo;
|
skinInfo: WeaponInfo | AgentInfo;
|
||||||
side: "T" | "CT";
|
side: "T" | "CT";
|
||||||
}) {
|
}) {
|
||||||
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
||||||
const acolor = `${skin.color}64`;
|
const acolor = `${skinInfo.color}64`;
|
||||||
|
const [skin, setSkin] = useState(skinInfo);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SkinDialog
|
<SkinDialog
|
||||||
isOpen={isDialogOpen}
|
isOpen={isDialogOpen}
|
||||||
setIsOpen={setIsDialogOpen}
|
setIsOpen={setIsDialogOpen}
|
||||||
skin={skin}
|
skin={skin}
|
||||||
|
setSkin={setSkin}
|
||||||
side={side}
|
side={side}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
@@ -50,7 +52,7 @@ export function SkinTile({
|
|||||||
{skin.display}
|
{skin.display}
|
||||||
</Typography.Heading>
|
</Typography.Heading>
|
||||||
{skin instanceof WeaponInfo && (
|
{skin instanceof WeaponInfo && (
|
||||||
<Float float={skin.float} minFloat={skin.min_float} maxFloat={skin.max_float}/>
|
<Float skin={skin} setSkin={setSkin} />
|
||||||
)}
|
)}
|
||||||
{skin instanceof WeaponInfo && (
|
{skin instanceof WeaponInfo && (
|
||||||
<Typography.Paragraph>
|
<Typography.Paragraph>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export class WeaponInfo {
|
export class WeaponInfo {
|
||||||
id!: string;
|
id!: string;
|
||||||
|
weapon_id!: string;
|
||||||
name!: string;
|
name!: string;
|
||||||
color!: string;
|
color!: string;
|
||||||
display!: string;
|
display!: string;
|
||||||
@@ -16,6 +17,7 @@ export class WeaponInfo {
|
|||||||
|
|
||||||
export class AgentInfo {
|
export class AgentInfo {
|
||||||
id!: string;
|
id!: string;
|
||||||
|
weapon_id!: string;
|
||||||
name!: string;
|
name!: string;
|
||||||
color!: string;
|
color!: string;
|
||||||
display!: string;
|
display!: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user