add consumable buttons

This commit is contained in:
tresabhi committed 2026-05-28 12:55:11 -05:00
1 parent f8a71feb46
commit 885c260605
9 files changed
+124 -32

No files matched your search

+3 -1
View File
@@ -4,6 +4,8 @@
"amogos", "amogos",
"Tankopedia", "Tankopedia",
"uasset", "uasset",
"oodle" "oodle",
"blitzkit",
"webp"
] ]
} }
+1 -5
View File
@@ -198,12 +198,8 @@
}, },
"tanks": { "tanks": {
"characteristics": {
"title": "Characteristics"
},
"loadout": { "loadout": {
"title": "Loadout", "consumables": "Consumables",
"modules": "Modules", "modules": "Modules",
"equipment": "Equipment", "equipment": "Equipment",
"alternative": "Alt" "alternative": "Alt"
@@ -13,6 +13,36 @@
} }
} }
.consumables {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: var(--space-3);
.consumable {
font-size: var(--space-5);
img {
width: 1em;
height: 1em;
}
.info {
display: flex;
flex-direction: column;
.entry {
display: flex;
gap: var(--space-2);
svg {
width: 1em;
height: 1em;
}
}
}
}
}
.equipment { .equipment {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -5,9 +5,11 @@ import { StandardPrice } from "@protos/blitz_static_standard_price";
import type { UpgradeLine } from "@protos/blitz_static_tank_upgrade_line"; import type { UpgradeLine } from "@protos/blitz_static_tank_upgrade_line";
import type { StageParameters } from "@protos/blitz_static_tank_upgrade_single_stage"; import type { StageParameters } from "@protos/blitz_static_tank_upgrade_single_stage";
import { import {
ClockIcon,
CornerBottomLeftIcon, CornerBottomLeftIcon,
DoubleArrowLeftIcon, DoubleArrowLeftIcon,
DoubleArrowRightIcon, DoubleArrowRightIcon,
ResetIcon,
TrashIcon, TrashIcon,
} from "@radix-ui/react-icons"; } from "@radix-ui/react-icons";
import { chunk } from "lodash-es"; import { chunk } from "lodash-es";
@@ -26,7 +28,7 @@ import { useProtagonist } from "../../hooks/useProtagonist";
import { useStrings } from "../../hooks/useStrings"; import { useStrings } from "../../hooks/useStrings";
import { useTierPrices } from "../../hooks/useTierPrices"; import { useTierPrices } from "../../hooks/useTierPrices";
import { useUpgradePreset } from "../../hooks/useUpgradePreset"; import { useUpgradePreset } from "../../hooks/useUpgradePreset";
import { Tankopedia } from "../../stores/tankopedia"; import { Tankopedia, TankopediaCompare } from "../../stores/tankopedia";
import { Button } from "../Button"; import { Button } from "../Button";
import { Heading } from "../Heading"; import { Heading } from "../Heading";
import { IconButton } from "../IconButton"; import { IconButton } from "../IconButton";
@@ -38,13 +40,15 @@ import type { ComputedCharacteristics } from "../../tankopedia/computeCharacteri
import { Tooltip } from "../Tooltip"; import { Tooltip } from "../Tooltip";
import { useGameStrings } from "../../hooks/useGameStrings"; import { useGameStrings } from "../../hooks/useGameStrings";
import { TankopediaCollapsible } from "../TankopediaCollapsible"; import { TankopediaCollapsible } from "../TankopediaCollapsible";
import type { ConsumableComponent } from "@protos/blitz_static_consumable_component";
import { Tank } from "@protos/tank";
import { select } from "three/src/nodes/math/ConditionalNode.js";
interface TankopediaLoadoutProps { interface TankopediaLoadoutProps {
characteristics: ComputedCharacteristics; characteristics: ComputedCharacteristics;
} }
export function TankopediaLoadout({ characteristics }: TankopediaLoadoutProps) { export function TankopediaLoadout({ characteristics }: TankopediaLoadoutProps) {
const strings = useStrings();
const tank = useProtagonist(); const tank = useProtagonist();
const showModules = tank.tank!.upgrade_lines.some( const showModules = tank.tank!.upgrade_lines.some(
@@ -53,15 +57,9 @@ export function TankopediaLoadout({ characteristics }: TankopediaLoadoutProps) {
return ( return (
<> <>
<TankopediaCollapsible title={strings.tanks.loadout.title}> {showModules && <Modules />}
{showModules && <Modules />} <Equipment />
<Equipment /> <Consumables characteristics={characteristics} />
<Consumables characteristics={characteristics} />
</TankopediaCollapsible>
<TankopediaCollapsible title={strings.tanks.characteristics.title}>
lmao
</TankopediaCollapsible>
</> </>
); );
} }
@@ -71,21 +69,88 @@ interface ConsumablesProps {
} }
function Consumables({ characteristics }: ConsumablesProps) { function Consumables({ characteristics }: ConsumablesProps) {
const strings = useStrings();
const consumables = useConsumables(); const consumables = useConsumables();
const tierPrices = useTierPrices();
const tank = useProtagonist(); const tank = useProtagonist();
const isCompatible = useCompatibility(tank, characteristics); const isCompatible = useCompatibility(tank, characteristics);
return Object.entries(consumables.consumables).map( return (
([id, { compatibility, consumable, purchase }]) => { <div className={styles.section}>
if (!isCompatible(compatibility!)) return null; <div className={styles.header}>
<Heading size="4">{strings.tanks.loadout.consumables}</Heading>
return ( <IconButton
<span> color="red"
{id} {JSON.stringify(consumable, null, 2)} onClick={() => {
</span> Tankopedia.mutate((draft) => {
); draft.protagonist.consumables = [];
}, });
}}
>
<TrashIcon />
</IconButton>
</div>
<div className={styles.consumables}>
{Object.entries(consumables.consumables).map(
([id, { compatibility, consumable, purchase }]) => {
if (!isCompatible(compatibility!)) return null;
return <Consumable id={id} consumable={consumable!} />;
},
)}
</div>
</div>
);
}
interface ConsumableProps {
id: string;
consumable: ConsumableComponent;
}
function Consumable({ id, consumable }: ConsumableProps) {
const gameStrings = useGameStrings("ConsumableEntity");
const isSelected = Tankopedia.use((state) =>
state.protagonist.consumables.includes(id),
);
return (
<Tooltip tooltip={gameStrings[consumable!.name_key]}>
<Button
radius="1"
variant={isSelected ? "surface" : "soft"}
color={isSelected ? undefined : "gray"}
className={styles.consumable}
onClick={() => {
Tankopedia.mutate((draft) => {
const index = draft.protagonist.consumables.indexOf(id);
if (index === -1) {
draft.protagonist.consumables.push(id);
} else {
draft.protagonist.consumables.splice(index, 1);
}
});
}}
>
<img src={`/media/consumables/${id}.webp`} />
<div className={styles.info}>
<Text lowContrast size="minor">
<div className={styles.entry}>
<ClockIcon />
10s
</div>
</Text>
<Text lowContrast size="minor">
<div className={styles.entry}>
<ResetIcon />
10s
</div>
</Text>
</div>
</Button>
</Tooltip>
); );
} }
@@ -20,9 +20,7 @@
white-space: nowrap; white-space: nowrap;
} }
&:hover, &:hover {
&:active,
&:focus-within {
.text { .text {
display: block; display: block;
} }
@@ -12,7 +12,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--space-6); gap: var(--space-5);
padding: var(--space-5); padding: var(--space-5);
overflow-y: auto; overflow-y: auto;
@@ -16,6 +16,5 @@ export enum TankopediaCompare {
export const Tankopedia = new Varuna<Tankopedia, Tank>((tank) => ({ export const Tankopedia = new Varuna<Tankopedia, Tank>((tank) => ({
compare: TankopediaCompare.TierAndClass, compare: TankopediaCompare.TierAndClass,
protagonist: createTankState(tank), protagonist: createTankState(tank),
})); }));
@@ -23,6 +23,7 @@ export function createTankState({ tank, id }: Tank) {
id, id,
equipment: {}, equipment: {},
consumables: [],
upgrades, upgrades,
alternates, alternates,
@@ -8,6 +8,7 @@ export interface TankState {
id: string; id: string;
equipment: Record<number, number>; equipment: Record<number, number>;
consumables: string[];
upgrades: Record<string, number>; upgrades: Record<string, number>;
alternates: Record<string, boolean>; alternates: Record<string, boolean>;