cleaner equipment and modules ui

This commit is contained in:
tresabhi committed 2026-05-17 13:46:53 -05:00
1 parent 26cc0cc723
commit c100a1e655
4 files changed
+150 -122

No files matched your search

@@ -1,11 +1,11 @@
.price {
display: flex;
align-items: center;
gap: var(--space-3);
.entry {
display: block;
line-height: 1;
.wrapper {
display: flex;
@@ -20,25 +20,32 @@
.slots {
display: flex;
flex-direction: column;
gap: var(--space-2);
align-items: center;
.slot {
aspect-ratio: 1 / 1;
padding: var(--space-3);
.options {
display: flex;
font-size: var(--space-5);
.slot {
aspect-ratio: 1 / 1;
padding: var(--space-3);
&:first-child {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
font-size: var(--space-5);
&:last-child {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
&:first-child {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
&[data-selected="true"] {
z-index: 1;
&:last-child {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
&[data-selected="true"] {
z-index: 1;
}
}
}
}
@@ -53,37 +60,41 @@
.line {
display: flex;
align-items: center;
gap: var(--space-3);
.stage {
position: relative;
.wrapper {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--space-2);
padding: var(--space-3);
.stage {
position: relative;
font-size: 2em;
padding: var(--space-3);
.icon {
width: 1em;
height: 1em;
font-size: 2em;
padding: var(--space-3) 0;
.icon {
width: 1em;
height: 1em;
transform: translate(var(--space-1), calc(-1 * var(--space-1)));
}
transform: translate(var(--space-1), var(--space-1));
}
.tier {
position: absolute;
left: var(--space-2);
top: var(--space-2);
}
.tier {
position: absolute;
left: var(--space-2);
top: var(--space-2);
}
.price {
position: absolute;
left: 50%;
bottom: var(--space-2);
transform: translateX(-50%);
.price {
position: absolute;
left: 50%;
bottom: var(--space-2);
transform: translateX(-50%);
}
}
}
}
@@ -39,10 +39,7 @@ export function TankopediaLoadout() {
function Equipment() {
const strings = useStrings();
const tank = useProtagonist();
const equipment = useEquipment(
tank.tank!.equipment_preset_catalog_id,
tank.tank!.equipment_price_preset_catalog_id,
);
const equipment = useEquipment(tank.tank!);
return (
<div className={styles.section}>
@@ -86,16 +83,25 @@ interface EquipmentSlotProps {
}
function EquipmentSlot({ slot, rowIndex, columnIndex }: EquipmentSlotProps) {
const equipmentIndex = rowIndex * equipmentColumns + columnIndex;
const tank = useProtagonist();
const equipment = useEquipment(tank.tank!);
const { price } = equipment.price.unlock_slot_prices[equipmentIndex];
return (
<div className={styles.slots}>
{slot.options_catalog_i_ds.map((id, index) => (
<EquipmentOption
key={id}
optionIndex={index}
rowIndex={rowIndex}
columnIndex={columnIndex}
/>
))}
<div className={styles.options}>
{slot.options_catalog_i_ds.map((id, index) => (
<EquipmentOption
key={id}
optionIndex={index}
rowIndex={rowIndex}
columnIndex={columnIndex}
/>
))}
</div>
<Price price={price!} />
</div>
);
}
@@ -112,11 +118,17 @@ function EquipmentOption({
columnIndex,
}: EquipmentOptionProps) {
const equipmentIndex = rowIndex * equipmentColumns + columnIndex;
const tank = useProtagonist();
const equipment = useEquipment(tank.tank!);
const isSelected = Tankopedia.use(
(state) =>
equipmentIndex in state.protagonist.equipment &&
state.protagonist.equipment[equipmentIndex] === optionIndex,
);
const name =
equipment.preset.slots[equipmentIndex].options_catalog_i_ds[optionIndex];
const equipmentData = equipment.equipments[name];
return (
<Button
@@ -292,87 +304,91 @@ function LineElement({ index, lineName, stage }: LineElementProps) {
}
return (
<Button
color={isSelected ? undefined : "gray"}
variant={isSelected ? "surface" : "soft"}
radius="1"
className={styles.stage}
onClick={() => {
Tankopedia.mutate((draft) => {
const tankData = tank.tank!;
<div className={styles.wrapper}>
<Button
className={styles.stage}
color={isSelected ? undefined : "gray"}
variant={isSelected ? "surface" : "soft"}
radius="1"
onClick={() => {
Tankopedia.mutate((draft) => {
const tankData = tank.tank!;
if (isAlternativeLine(lineName)) {
const originalLine = originalLineName(lineName)!;
if (isAlternativeLine(lineName)) {
const originalLine = originalLineName(lineName)!;
draft.protagonist.alternates[originalLine] = true;
draft.protagonist.upgrades[lineName] = index;
} else {
draft.protagonist.alternates[lineName] = false;
draft.protagonist.upgrades[lineName] = index;
}
draft.protagonist.alternates[originalLine] = true;
draft.protagonist.upgrades[lineName] = index;
} else {
draft.protagonist.alternates[lineName] = false;
draft.protagonist.upgrades[lineName] = index;
}
for (const required of stage.required_upgrades) {
for (const line of tankData.upgrade_lines) {
let i = 0;
for (const required of stage.required_upgrades) {
for (const line of tankData.upgrade_lines) {
let i = 0;
for (const candidateStage of line.stages) {
if (candidateStage.tech_name === required) {
draft.protagonist.upgrades[line.name] = Math.max(
draft.protagonist.upgrades[line.name],
i,
);
for (const candidateStage of line.stages) {
if (candidateStage.tech_name === required) {
draft.protagonist.upgrades[line.name] = Math.max(
draft.protagonist.upgrades[line.name],
i,
);
}
i++;
}
i++;
}
}
}
for (const line of tankData.upgrade_lines) {
if (draft.protagonist.alternates[line.name]) {
continue;
for (const line of tankData.upgrade_lines) {
if (draft.protagonist.alternates[line.name]) {
continue;
}
let i = draft.protagonist.upgrades[line.name];
while (i > 0) {
const currentStage = line.stages[i];
const valid = currentStage.required_upgrades.every(
(required) => {
for (const otherLine of tankData.upgrade_lines) {
const idx = draft.protagonist.upgrades[otherLine.name];
if (otherLine.stages[idx]?.tech_name === required) {
return true;
}
}
return false;
},
);
if (valid) break;
i--;
}
draft.protagonist.upgrades[line.name] = i;
}
});
}}
>
<img
className={styles.icon}
src={`/api/tanks/modules/${stage.stage_type}.webp`}
/>
{/* {stage.display_name} */}
let i = draft.protagonist.upgrades[line.name];
while (i > 0) {
const currentStage = line.stages[i];
const valid = currentStage.required_upgrades.every((required) => {
for (const otherLine of tankData.upgrade_lines) {
const idx = draft.protagonist.upgrades[otherLine.name];
if (otherLine.stages[idx]?.tech_name === required) {
return true;
}
}
return false;
});
if (valid) break;
i--;
}
draft.protagonist.upgrades[line.name] = i;
}
});
}}
>
<img
className={styles.icon}
src={`/api/tanks/modules/${stage.stage_type}.webp`}
/>
{/* {stage.display_name} */}
<Text weight="light" lowContrast size="minor" className={styles.tier}>
{isAlternativeLine(lineName)
? strings.tanks.loadout.alternative
: romanize(stage.number)}
</Text>
<Text weight="light" lowContrast size="minor" className={styles.tier}>
{isAlternativeLine(lineName)
? strings.tanks.loadout.alternative
: romanize(stage.number)}
</Text>
</Button>
{price && <Price className={styles.price} price={price} />}
</Button>
</div>
);
}
@@ -1,10 +1,11 @@
import type { TankCatalogComponent } from "@protos/blitz_static_tank_component";
import { api } from "../api/dynamic";
import { useAwait } from "./useAwait";
export function useEquipment(presetId: string, priceId: string) {
export function useEquipment(tank: TankCatalogComponent) {
const equipment = useAwait(() => api.equipment(), "equipment");
const preset = equipment.presets[presetId];
const price = equipment.prices[priceId];
const preset = equipment.presets[tank.equipment_preset_catalog_id];
const price = equipment.prices[tank.equipment_price_preset_catalog_id];
return { preset, price, equipment };
return { preset, price, equipments: equipment.equipments };
}