2026-11-14 01:17:38 -06:00
|
|
|
import { MAX_SKILL_LEVEL } from "@blitzkit/core";
|
|
|
|
|
import { useDuelModules } from "../../hooks/useDuelModules";
|
2026-09-22 16:38:30 -05:00
|
|
|
import { Tankopedia } from "../../stores/tankopedia";
|
2026-11-14 01:17:38 -06:00
|
|
|
import { selectModule } from "../../tankopedia/selectModule";
|
|
|
|
|
import { LoadoutConsumables } from "../LoadoutConsumables";
|
|
|
|
|
import { LoadoutEquipment } from "../LoadoutEquipment";
|
|
|
|
|
import { LoadoutModules } from "../LoadoutModules";
|
|
|
|
|
import { LoadoutProvisions } from "../LoadoutProvisions";
|
|
|
|
|
import { LoadoutShells } from "../LoadoutShells";
|
|
|
|
|
import { LoadoutSkills } from "../LoadoutSkills";
|
|
|
|
|
import { LoadoutStatus } from "../LoadoutStatus";
|
2026-09-22 16:17:55 -05:00
|
|
|
|
2026-11-14 01:17:38 -06:00
|
|
|
export function TankopediaLoadout() {
|
2026-09-22 16:17:55 -05:00
|
|
|
return (
|
|
|
|
|
<>
|
2026-11-11 22:43:47 -06:00
|
|
|
<Modules />
|
2026-10-11 16:01:02 -05:00
|
|
|
<Shells />
|
2026-09-22 16:17:55 -05:00
|
|
|
<Equipment />
|
2026-10-04 17:53:57 -05:00
|
|
|
<Provisions />
|
2026-11-14 01:17:38 -06:00
|
|
|
<Consumables />
|
2026-10-13 20:37:53 -05:00
|
|
|
<CrewSkills />
|
2026-09-22 16:17:55 -05:00
|
|
|
<Status />
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-11 22:43:47 -06:00
|
|
|
function Modules() {
|
2026-11-14 01:17:38 -06:00
|
|
|
const protagonist = useDuelModules("protagonist");
|
|
|
|
|
const selected = Tankopedia.use((state) => state.protagonist);
|
|
|
|
|
|
|
|
|
|
function setAll(position: "first" | "last") {
|
|
|
|
|
Tankopedia.mutate((draft) => {
|
|
|
|
|
const pick = <Item,>(list: Item[]) =>
|
|
|
|
|
position === "first" ? list[0] : list.at(-1)!;
|
|
|
|
|
const turret = pick(protagonist.tank.turrets);
|
|
|
|
|
const gun = pick(turret.guns);
|
|
|
|
|
|
|
|
|
|
draft.protagonist.turret = turret.id;
|
|
|
|
|
draft.protagonist.gun = gun.id;
|
|
|
|
|
draft.protagonist.shell = gun.shells[0].id;
|
|
|
|
|
draft.protagonist.engine = pick(protagonist.tank.engines).id;
|
|
|
|
|
draft.protagonist.track = pick(protagonist.tank.tracks).id;
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-11-11 22:43:47 -06:00
|
|
|
|
|
|
|
|
return (
|
2026-11-14 01:17:38 -06:00
|
|
|
<LoadoutModules
|
|
|
|
|
tank={protagonist.tank}
|
|
|
|
|
selected={selected}
|
|
|
|
|
onSelect={(unlock) => {
|
2026-11-11 22:43:47 -06:00
|
|
|
Tankopedia.mutate((draft) => {
|
2026-11-14 01:17:38 -06:00
|
|
|
selectModule(draft.protagonist, protagonist.tank, unlock);
|
2026-11-11 22:43:47 -06:00
|
|
|
});
|
|
|
|
|
}}
|
2026-11-14 01:17:38 -06:00
|
|
|
onStock={() => setAll("first")}
|
|
|
|
|
onMaximize={() => setAll("last")}
|
|
|
|
|
/>
|
2026-09-22 16:17:55 -05:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-11 16:01:02 -05:00
|
|
|
function Shells() {
|
2026-11-14 01:17:38 -06:00
|
|
|
const protagonist = useDuelModules("protagonist");
|
2026-10-11 16:01:02 -05:00
|
|
|
|
|
|
|
|
return (
|
2026-11-14 01:17:38 -06:00
|
|
|
<LoadoutShells
|
|
|
|
|
gun={protagonist.gun}
|
|
|
|
|
selected={protagonist.shell.id}
|
|
|
|
|
onSelect={(id) => {
|
|
|
|
|
Tankopedia.mutate((draft) => {
|
|
|
|
|
draft.protagonist.shell = id;
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
/>
|
2026-09-22 16:17:55 -05:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Equipment() {
|
2026-11-14 01:17:38 -06:00
|
|
|
const protagonist = useDuelModules("protagonist");
|
|
|
|
|
const selected = Tankopedia.use((state) => state.protagonist.equipment);
|
2026-09-22 16:17:55 -05:00
|
|
|
|
|
|
|
|
return (
|
2026-11-14 01:17:38 -06:00
|
|
|
<LoadoutEquipment
|
|
|
|
|
tank={protagonist.tank}
|
|
|
|
|
selected={selected}
|
|
|
|
|
onToggle={(slotIndex, optionIndex) => {
|
|
|
|
|
Tankopedia.mutate((draft) => {
|
|
|
|
|
const current = draft.protagonist.equipment;
|
2026-09-22 16:17:55 -05:00
|
|
|
|
2026-11-14 01:17:38 -06:00
|
|
|
if (current[slotIndex] === optionIndex) delete current[slotIndex];
|
|
|
|
|
else current[slotIndex] = optionIndex;
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
onClear={() => {
|
|
|
|
|
Tankopedia.mutate((draft) => {
|
|
|
|
|
draft.protagonist.equipment = {};
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
/>
|
2026-09-22 16:17:55 -05:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-14 01:17:38 -06:00
|
|
|
function Provisions() {
|
|
|
|
|
const protagonist = useDuelModules("protagonist");
|
|
|
|
|
const selected = Tankopedia.use((state) => state.protagonist.provisions);
|
2026-09-22 16:17:55 -05:00
|
|
|
|
|
|
|
|
return (
|
2026-11-14 01:17:38 -06:00
|
|
|
<LoadoutProvisions
|
|
|
|
|
tank={protagonist.tank}
|
|
|
|
|
gun={protagonist.gun}
|
|
|
|
|
selected={selected}
|
|
|
|
|
onToggle={(id) => {
|
|
|
|
|
Tankopedia.mutate((draft) => {
|
|
|
|
|
const list = draft.protagonist.provisions;
|
|
|
|
|
const index = list.indexOf(id);
|
2026-09-22 16:17:55 -05:00
|
|
|
|
2026-11-14 01:17:38 -06:00
|
|
|
if (index === -1) {
|
|
|
|
|
if (list.length >= protagonist.tank.max_provisions) return;
|
|
|
|
|
|
|
|
|
|
list.push(id);
|
|
|
|
|
} else {
|
|
|
|
|
list.splice(index, 1);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
onClear={() => {
|
|
|
|
|
Tankopedia.mutate((draft) => {
|
|
|
|
|
draft.protagonist.provisions = [];
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
/>
|
2026-09-22 16:17:55 -05:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-14 01:17:38 -06:00
|
|
|
function Consumables() {
|
|
|
|
|
const protagonist = useDuelModules("protagonist");
|
|
|
|
|
const selected = Tankopedia.use((state) => state.protagonist.consumables);
|
2026-09-22 16:17:55 -05:00
|
|
|
|
|
|
|
|
return (
|
2026-11-14 01:17:38 -06:00
|
|
|
<LoadoutConsumables
|
|
|
|
|
tank={protagonist.tank}
|
|
|
|
|
gun={protagonist.gun}
|
|
|
|
|
selected={selected}
|
|
|
|
|
onToggle={(id) => {
|
|
|
|
|
Tankopedia.mutate((draft) => {
|
|
|
|
|
const list = draft.protagonist.consumables;
|
|
|
|
|
const index = list.indexOf(id);
|
|
|
|
|
|
|
|
|
|
if (index === -1) {
|
|
|
|
|
if (list.length >= protagonist.tank.max_consumables) return;
|
|
|
|
|
|
|
|
|
|
list.push(id);
|
|
|
|
|
} else {
|
|
|
|
|
list.splice(index, 1);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
onClear={() => {
|
|
|
|
|
Tankopedia.mutate((draft) => {
|
|
|
|
|
draft.protagonist.consumables = [];
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function CrewSkills() {
|
|
|
|
|
const selected = Tankopedia.use((state) => state.environment.skills);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<LoadoutSkills
|
|
|
|
|
selected={selected}
|
|
|
|
|
onChange={(skill, level) => {
|
|
|
|
|
Tankopedia.mutate((draft) => {
|
|
|
|
|
draft.environment.skills[skill] = level;
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
onClear={() => {
|
|
|
|
|
Tankopedia.mutate((draft) => {
|
|
|
|
|
for (const skill in draft.environment.skills) {
|
|
|
|
|
draft.environment.skills[skill] = 0;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
onMaximize={() => {
|
|
|
|
|
Tankopedia.mutate((draft) => {
|
|
|
|
|
for (const skill in draft.environment.skills) {
|
|
|
|
|
draft.environment.skills[skill] = MAX_SKILL_LEVEL;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Status() {
|
|
|
|
|
const status = Tankopedia.use((state) => state.protagonist.status);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<LoadoutStatus
|
|
|
|
|
status={status}
|
|
|
|
|
onToggle={(key) => {
|
|
|
|
|
Tankopedia.mutate((draft) => {
|
|
|
|
|
draft.protagonist.status[key] = !draft.protagonist.status[key];
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
onClear={() => {
|
|
|
|
|
Tankopedia.mutate((draft) => {
|
|
|
|
|
for (const key in draft.protagonist.status) {
|
|
|
|
|
draft.protagonist.status[key as keyof typeof status] = false;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
/>
|
2026-09-22 16:17:55 -05:00
|
|
|
);
|
|
|
|
|
}
|