Files
blitzkit/packages/bot/src/commands/research.tsx
T

626 lines
21 KiB
TypeScript
Raw Normal View History

2024-09-07 21:45:00 -05:00
import {
asset,
2024-09-08 10:42:34 -05:00
emblemURL,
2024-09-07 21:45:00 -05:00
equipmentPriceMatrix,
2024-09-08 10:17:40 -05:00
getAccountInfo,
getClanAccountInfo,
getTankStats,
2024-09-07 21:45:00 -05:00
TANK_ICONS,
2024-10-05 23:20:04 -05:00
TankType,
2024-09-07 21:45:00 -05:00
TIER_ROMAN_NUMERALS,
2025-10-02 10:15:38 -05:00
} from "@blitzkit/core";
import { literals } from "@blitzkit/i18n";
import { escapeMarkdown, Locale } from "discord.js";
import { CommandWrapper } from "../components/CommandWrapper";
import { TitleBar } from "../components/TitleBar";
import { resolveTankId } from "../core/blitz/resolveTankId";
import { buildTechTreeLine } from "../core/blitzkit/buildTechTreeLine";
import { iconPng } from "../core/blitzkit/iconPng";
import { tankDefinitions } from "../core/blitzkit/nonBlockingPromises";
import { resolveAncestry } from "../core/blitzkit/resolveAncestry";
import { tankIconPng } from "../core/blitzkit/tankIconPng";
import { addUsernameChoices } from "../core/discord/addUsernameChoices";
import { autocompleteTanks } from "../core/discord/autocompleteTanks";
import { createLocalizedCommand } from "../core/discord/createLocalizedCommand";
import { localizationObject } from "../core/discord/localizationObject";
import { resolvePlayerFromCommand } from "../core/discord/resolvePlayerFromCommand";
import { translator } from "../core/localization/translator";
import { CommandRegistry } from "../events/interactionCreate";
import { theme } from "../stitches.config";
2024-03-14 16:43:28 -05:00
export const researchCommand = new Promise<CommandRegistry>((resolve) => {
2025-01-16 13:48:13 -06:00
const { strings } = translator(Locale.EnglishUS);
2024-03-15 21:50:19 -05:00
2024-03-14 16:43:28 -05:00
resolve({
2025-10-02 10:15:38 -05:00
command: createLocalizedCommand("research")
2024-03-15 21:50:19 -05:00
.addStringOption((option) =>
option
2025-01-16 14:22:30 -06:00
.setName(strings.bot.commands.research.options.target_tank.name)
2024-03-15 21:50:19 -05:00
.setNameLocalizations(
2024-12-19 11:04:48 -06:00
localizationObject(
2025-01-16 14:22:30 -06:00
(strings) =>
strings.bot.commands.research.options.target_tank.name,
2024-12-19 11:04:48 -06:00
undefined,
2025-10-02 10:15:38 -05:00
true
)
2024-03-15 21:50:19 -05:00
)
.setDescription(
2025-10-02 10:15:38 -05:00
strings.bot.commands.research.options.target_tank.description
2024-03-15 21:50:19 -05:00
)
.setDescriptionLocalizations(
localizationObject(
2025-01-16 13:48:13 -06:00
(strings) =>
2025-10-02 10:15:38 -05:00
strings.bot.commands.research.options.target_tank.description
)
2024-03-15 21:50:19 -05:00
)
.setAutocomplete(true)
2025-10-02 10:15:38 -05:00
.setRequired(true)
2024-03-15 21:50:19 -05:00
)
.addStringOption((option) =>
option
2025-01-16 14:22:30 -06:00
.setName(strings.bot.commands.research.options.starting_tank.name)
2024-03-15 21:50:19 -05:00
.setNameLocalizations(
2024-12-19 11:04:48 -06:00
localizationObject(
2025-01-16 13:48:13 -06:00
(strings) =>
2025-01-16 14:22:30 -06:00
strings.bot.commands.research.options.starting_tank.name,
2024-12-19 11:04:48 -06:00
undefined,
2025-10-02 10:15:38 -05:00
true
)
2024-03-15 21:50:19 -05:00
)
.setDescription(
2025-10-02 10:15:38 -05:00
strings.bot.commands.research.options.starting_tank.description
2024-03-15 21:50:19 -05:00
)
.setDescriptionLocalizations(
localizationObject(
2025-01-16 13:48:13 -06:00
(strings) =>
2025-10-02 10:15:38 -05:00
strings.bot.commands.research.options.starting_tank.description
)
2024-03-15 21:50:19 -05:00
)
.setAutocomplete(true)
2025-10-02 10:15:38 -05:00
.setRequired(false)
2024-03-18 16:47:53 -05:00
)
.addStringOption(addUsernameChoices),
2024-03-14 16:43:28 -05:00
async handler(interaction) {
2025-01-16 13:48:13 -06:00
const { strings, unwrap } = translator(interaction.locale);
2024-03-14 16:43:28 -05:00
const awaitedTankDefinitions = await tankDefinitions;
2024-03-15 21:50:19 -05:00
const targetTankId = await resolveTankId(
2025-10-02 10:15:38 -05:00
interaction.options.getString("target-tank", true),
2024-03-14 16:43:28 -05:00
interaction.locale,
2025-10-02 10:15:38 -05:00
true
2024-03-14 16:43:28 -05:00
);
2025-10-02 10:15:38 -05:00
const startingTankRaw = interaction.options.getString("starting-tank");
2024-10-05 23:20:04 -05:00
const targetTank = awaitedTankDefinitions.tanks[targetTankId];
2024-03-14 16:43:28 -05:00
const { id, region } = await resolvePlayerFromCommand(interaction);
2024-03-15 21:50:19 -05:00
let startingTankId: number;
const targetTankAncestry = await resolveAncestry(targetTankId);
2024-03-14 16:43:28 -05:00
2024-03-15 21:50:19 -05:00
if (startingTankRaw) {
startingTankId = await resolveTankId(
startingTankRaw,
interaction.locale,
2025-10-02 10:15:38 -05:00
true
2024-03-15 21:50:19 -05:00
);
if (targetTankId === startingTankId) {
2025-01-16 13:48:13 -06:00
return literals(
strings.bot.commands.research.errors.start_end_equal,
2025-10-02 10:15:38 -05:00
{ tank: escapeMarkdown(unwrap(targetTank.name)) }
2025-01-16 13:48:13 -06:00
);
2024-03-15 21:50:19 -05:00
}
2024-10-05 23:20:04 -05:00
const startingTank = awaitedTankDefinitions.tanks[startingTankId];
2024-03-15 21:50:19 -05:00
if (startingTank.tier > targetTank.tier) {
2025-10-02 10:15:38 -05:00
return literals(strings.bot.commands.research.errors.unordered, {
tank_1: escapeMarkdown(unwrap(startingTank.name)),
tank_2: escapeMarkdown(unwrap(targetTank.name)),
});
2024-03-15 21:50:19 -05:00
}
if (!targetTankAncestry.includes(startingTankId)) {
2025-01-16 13:48:13 -06:00
return literals(
strings.bot.commands.research.errors.tanks_not_on_line,
2025-10-02 10:15:38 -05:00
{
tank_1: escapeMarkdown(unwrap(targetTank.name)),
tank_2: escapeMarkdown(unwrap(startingTank.name)),
}
2025-01-16 13:48:13 -06:00
);
2024-03-15 21:50:19 -05:00
}
} else {
2024-10-05 23:20:04 -05:00
if (targetTank.type !== TankType.RESEARCHABLE) {
2025-10-02 10:15:38 -05:00
return literals(strings.bot.commands.research.errors.non_tech_tree, {
tank: escapeMarkdown(unwrap(targetTank.name)),
});
2024-03-15 21:50:19 -05:00
}
if (targetTank.tier === 1) {
2025-10-02 10:15:38 -05:00
return literals(strings.bot.commands.research.errors.tier_1, {
tank: escapeMarkdown(unwrap(targetTank.name)),
});
2024-03-15 21:50:19 -05:00
}
2024-04-15 13:25:35 -05:00
const tankStats = await getTankStats(region, id);
if (tankStats === null) {
2025-01-16 13:48:13 -06:00
return strings.bot.common.errors.no_tank_stats;
2024-04-15 13:25:35 -05:00
}
2024-03-15 21:50:19 -05:00
if (tankStats.some(({ tank_id }) => tank_id === targetTankId)) {
2025-01-16 13:48:13 -06:00
return literals(
strings.bot.commands.research.errors.already_researched,
2025-10-02 10:15:38 -05:00
{ tank: escapeMarkdown(unwrap(targetTank.name)) }
2025-01-16 13:48:13 -06:00
);
2024-03-15 21:50:19 -05:00
}
const foundAncestor = targetTankAncestry.find(
(id) =>
2024-10-05 23:20:04 -05:00
awaitedTankDefinitions.tanks[id].tier === 1 ||
2025-10-02 10:15:38 -05:00
tankStats.some(({ tank_id }) => tank_id === id)
2024-03-15 21:50:19 -05:00
);
if (foundAncestor === undefined) {
2025-10-02 10:15:38 -05:00
throw new Error("No first owned tech tree found.");
2024-03-15 21:50:19 -05:00
}
startingTankId = foundAncestor;
2024-03-14 16:43:28 -05:00
}
2024-03-15 21:50:19 -05:00
const techTreeLine = await buildTechTreeLine(
startingTankId,
2025-10-02 10:15:38 -05:00
targetTankId
2024-03-15 21:50:19 -05:00
);
const line = [...techTreeLine, startingTankId].reverse();
2024-03-14 16:43:28 -05:00
const { nickname } = await getAccountInfo(region, id);
2025-10-02 10:15:38 -05:00
const clan = (await getClanAccountInfo(region, id, ["clan"]))?.clan;
2024-03-24 22:06:48 -05:00
const clanImage = clan ? emblemURL(clan.emblem_set_id) : undefined;
2024-03-15 21:50:19 -05:00
const costs = await Promise.all(
line.map(async (id) => {
2024-10-05 23:20:04 -05:00
const tank = awaitedTankDefinitions.tanks[id];
2024-03-15 21:50:19 -05:00
const turretXps = new Map<number, number>();
const gunXps = new Map<number, number>();
const engineXps = new Map<number, number>();
const trackXps = new Map<number, number>();
2024-03-14 16:43:28 -05:00
2024-03-15 21:50:19 -05:00
tank.turrets.forEach((turret) => {
2024-11-18 10:56:24 -06:00
if (
turret.research_cost !== undefined &&
2025-10-02 10:15:38 -05:00
typeof turret.research_cost.research_cost_type!.value === "number"
2024-11-18 10:56:24 -06:00
) {
turretXps.set(
turret.id,
2025-10-02 10:15:38 -05:00
turret.research_cost.research_cost_type!.value
2024-11-18 10:56:24 -06:00
);
2024-03-15 21:50:19 -05:00
}
turret.guns.forEach((gun) => {
2024-11-18 10:56:24 -06:00
if (
2025-08-18 12:06:14 -05:00
gun.research_cost !== undefined &&
2025-10-02 10:15:38 -05:00
typeof gun.research_cost.research_cost_type!.value === "number"
2024-11-18 10:56:24 -06:00
) {
2025-08-18 12:06:14 -05:00
gunXps.set(gun.id, gun.research_cost.research_cost_type!.value);
2024-03-15 21:50:19 -05:00
}
});
});
tank.engines.forEach((engine) => {
2024-11-18 10:56:24 -06:00
if (
engine.research_cost !== undefined &&
2025-10-02 10:15:38 -05:00
typeof engine.research_cost.research_cost_type!.value === "number"
2024-11-18 10:56:24 -06:00
) {
engineXps.set(
engine.id,
2025-10-02 10:15:38 -05:00
engine.research_cost.research_cost_type!.value
2024-11-18 10:56:24 -06:00
);
2024-03-15 21:50:19 -05:00
}
});
tank.tracks.forEach((track) => {
2024-11-18 10:56:24 -06:00
if (
track.research_cost !== undefined &&
2025-10-02 10:15:38 -05:00
typeof track.research_cost.research_cost_type!.value === "number"
2024-11-18 10:56:24 -06:00
) {
trackXps.set(
track.id,
2025-10-02 10:15:38 -05:00
track.research_cost.research_cost_type!.value
2024-11-18 10:56:24 -06:00
);
2024-03-15 21:50:19 -05:00
}
});
let moduleXp = 0;
turretXps.forEach((value) => {
moduleXp += value;
});
gunXps.forEach((value) => {
moduleXp += value;
});
engineXps.forEach((value) => {
moduleXp += value;
});
trackXps.forEach((value) => {
moduleXp += value;
});
return {
2024-11-18 10:56:24 -06:00
research: tank.research_cost!.research_cost_type!.value as number,
2024-03-15 21:50:19 -05:00
upgrades: moduleXp,
purchase: tank.price.value,
equipment: equipmentPriceMatrix[tank.tier].reduce(
(a, b) => a + 3 * b,
2025-10-02 10:15:38 -05:00
0
2024-03-15 21:50:19 -05:00
),
};
2025-10-02 10:15:38 -05:00
})
2024-03-15 21:50:19 -05:00
);
const image = (
2024-03-14 16:43:28 -05:00
<CommandWrapper>
<TitleBar
title={nickname}
image={clanImage}
2025-01-16 13:48:13 -06:00
description={`${strings.bot.commands.research.body.subtitle} • ${
2024-09-07 21:45:00 -05:00
targetTank.name
}`}
2024-03-14 16:43:28 -05:00
/>
2024-03-14 17:31:11 -05:00
<div
style={{
2025-10-02 10:15:38 -05:00
display: "flex",
flexDirection: "column",
2024-03-14 17:31:11 -05:00
gap: 8,
}}
>
2024-03-15 21:50:19 -05:00
<div
style={{
2025-10-02 10:15:38 -05:00
display: "flex",
2024-03-15 21:50:19 -05:00
borderRadius: 4,
backgroundColor: theme.colors.appBackground2,
border: theme.borderStyles.subtle,
}}
>
<div style={{ width: 48 }}></div>
<div
style={{
flex: 1,
2025-10-02 10:15:38 -05:00
display: "flex",
alignItems: "center",
justifyContent: "center",
position: "relative",
2024-03-15 21:50:19 -05:00
right: 24,
}}
>
<span
style={{
color: theme.colors.textHighContrast,
}}
>
2025-01-16 13:48:13 -06:00
{strings.bot.commands.research.body.total}
2024-03-15 21:50:19 -05:00
</span>
</div>
<div
style={{
flex: 1,
2025-10-02 10:15:38 -05:00
display: "flex",
flexDirection: "column",
justifyContent: "center",
2024-03-15 21:50:19 -05:00
gap: 8,
2025-10-02 10:15:38 -05:00
padding: "16px 0",
2024-03-15 21:50:19 -05:00
}}
>
<div
style={{
2025-10-02 10:15:38 -05:00
display: "flex",
2024-03-15 21:50:19 -05:00
gap: 4,
2025-10-02 10:15:38 -05:00
alignItems: "center",
2024-03-15 21:50:19 -05:00
}}
>
<img
2024-05-04 16:28:12 -05:00
alt="XP icon"
2025-10-02 10:15:38 -05:00
src={await iconPng(asset("icons/currencies/xp.webp"))}
2024-03-15 21:50:19 -05:00
width={16}
height={16}
/>
<span
style={{
fontSize: 16,
color: theme.colors.textHighContrast,
}}
>
2025-10-02 10:15:38 -05:00
Experience:{" "}
2024-03-15 21:50:19 -05:00
{costs
2024-03-17 21:40:21 -05:00
.reduce((a, b) => a + (b.research ?? 0) + b.upgrades, 0)
2024-03-15 21:50:19 -05:00
.toLocaleString(interaction.locale)}
</span>
</div>
<div
style={{
2025-10-02 10:15:38 -05:00
display: "flex",
2024-03-15 21:50:19 -05:00
gap: 4,
2025-10-02 10:15:38 -05:00
alignItems: "center",
2024-03-15 21:50:19 -05:00
}}
>
<img
2024-05-04 16:28:12 -05:00
alt="Silver icon"
2025-10-02 10:15:38 -05:00
src={await iconPng(asset("icons/currencies/silver.webp"))}
2024-03-15 21:50:19 -05:00
width={16}
height={16}
/>
<span
style={{
fontSize: 16,
color: theme.colors.textHighContrast,
}}
>
2025-10-02 10:15:38 -05:00
Purchase:{" "}
2024-03-15 21:50:19 -05:00
{costs
2024-03-17 21:40:21 -05:00
.reduce((a, b) => a + b.purchase, 0)
.toLocaleString(interaction.locale)}
</span>
</div>
<div
style={{
2025-10-02 10:15:38 -05:00
display: "flex",
2024-03-17 21:40:21 -05:00
gap: 4,
2025-10-02 10:15:38 -05:00
alignItems: "center",
2024-03-17 21:40:21 -05:00
}}
>
<img
2024-05-04 16:28:12 -05:00
alt="Silver icon"
2025-10-02 10:15:38 -05:00
src={await iconPng(asset("icons/currencies/silver.webp"))}
2024-03-17 21:40:21 -05:00
width={16}
height={16}
/>
<span
style={{
fontSize: 16,
color: theme.colors.textHighContrast,
}}
>
2025-10-02 10:15:38 -05:00
Equipment:{" "}
2024-03-17 21:40:21 -05:00
{costs
.reduce((a, b) => a + b.equipment, 0)
2024-03-15 21:50:19 -05:00
.toLocaleString(interaction.locale)}
</span>
</div>
</div>
</div>
2024-03-14 17:31:11 -05:00
{await Promise.all(
2024-03-15 21:50:19 -05:00
line.map(async (id, index) => {
2024-10-05 23:20:04 -05:00
const tank = awaitedTankDefinitions.tanks[id];
2024-03-14 17:31:11 -05:00
return (
<div
key={id}
style={{
2025-10-02 10:15:38 -05:00
display: "flex",
2024-03-14 17:31:11 -05:00
borderRadius: 4,
backgroundColor: theme.colors.appBackground2,
2024-03-15 21:50:19 -05:00
border: theme.borderStyles.subtle,
2024-03-14 17:31:11 -05:00
}}
>
<div
style={{
width: 48,
2025-10-02 10:15:38 -05:00
display: "flex",
alignItems: "center",
justifyContent: "center",
2024-03-14 17:31:11 -05:00
}}
>
<span
style={{
color: theme.colors.textLowContrast,
fontSize: 16,
}}
>
{TIER_ROMAN_NUMERALS[tank.tier]}
</span>
</div>
<div
style={{
flex: 1,
2025-10-02 10:15:38 -05:00
display: "flex",
position: "relative",
right: "5%",
2024-03-14 17:31:11 -05:00
}}
>
2024-04-12 09:17:05 -05:00
<img
2024-05-04 16:28:12 -05:00
alt="Tank icon"
2024-04-12 09:17:05 -05:00
src={await tankIconPng(id)}
2024-03-14 17:31:11 -05:00
style={{
2024-04-12 09:17:05 -05:00
height: 100,
width: 100,
2025-10-02 10:15:38 -05:00
objectFit: "contain",
position: "absolute",
left: "25%",
2024-04-12 09:17:05 -05:00
top: 0,
2024-03-14 17:31:11 -05:00
}}
2024-04-12 09:17:05 -05:00
/>
2024-03-14 17:31:11 -05:00
<div
style={{
2025-10-02 10:15:38 -05:00
display: "flex",
2024-03-14 17:31:11 -05:00
gap: 4,
2025-10-02 10:15:38 -05:00
alignItems: "center",
justifyContent: "center",
position: "absolute",
2024-03-14 17:31:11 -05:00
bottom: 8,
2025-10-02 10:15:38 -05:00
left: "50%",
transform: "translateX(-50%)",
2024-03-14 17:31:11 -05:00
}}
>
<img
2025-01-14 19:22:41 -06:00
alt={unwrap(tank.name)}
2024-03-14 17:31:11 -05:00
src={TANK_ICONS[tank.class]}
width={16}
height={16}
/>
<span
style={{
color: theme.colors.textHighContrast,
}}
>
2025-01-14 19:22:41 -06:00
{unwrap(tank.name)}
2024-03-14 17:31:11 -05:00
</span>
</div>
</div>
<div
style={{
flex: 1,
2025-10-02 10:15:38 -05:00
display: "flex",
flexDirection: "column",
justifyContent: "center",
2024-03-14 17:31:11 -05:00
gap: 8,
2025-10-02 10:15:38 -05:00
padding: "16px 0",
2024-03-14 17:31:11 -05:00
}}
>
2024-03-15 12:28:29 -05:00
<div
style={{
2025-10-02 10:15:38 -05:00
display: "flex",
2024-03-15 12:28:29 -05:00
gap: 4,
2025-10-02 10:15:38 -05:00
alignItems: "center",
2024-03-15 12:28:29 -05:00
}}
>
<img
2024-05-04 16:28:12 -05:00
alt="XP icon"
2025-10-02 10:15:38 -05:00
src={await iconPng(asset("icons/currencies/xp.webp"))}
2024-03-15 12:28:29 -05:00
width={16}
height={16}
/>
<span
style={{
fontSize: 16,
color: theme.colors.textHighContrast,
}}
>
2025-01-16 13:48:13 -06:00
{literals(
strings.bot.commands.research.body.research,
2025-10-02 10:15:38 -05:00
{
cost: costs[index].research?.toLocaleString(
interaction.locale
),
}
2025-01-16 13:48:13 -06:00
)}
2024-03-15 12:28:29 -05:00
</span>
</div>
2024-03-15 21:50:19 -05:00
{costs[index].upgrades > 0 && (
2024-03-15 12:28:29 -05:00
<div
style={{
2025-10-02 10:15:38 -05:00
display: "flex",
2024-03-15 12:28:29 -05:00
gap: 4,
2025-10-02 10:15:38 -05:00
alignItems: "center",
2024-03-15 12:28:29 -05:00
}}
>
<img
2024-05-04 16:28:12 -05:00
alt="Free XP icon"
2024-04-24 16:22:48 -05:00
src={await iconPng(
2025-10-02 10:15:38 -05:00
asset("icons/currencies/free-xp.webp")
2024-03-15 12:28:29 -05:00
)}
width={16}
height={16}
/>
<span
style={{
fontSize: 16,
color: theme.colors.textHighContrast,
}}
>
2025-01-16 13:48:13 -06:00
{literals(
strings.bot.commands.research.body.upgrades,
2025-10-02 10:15:38 -05:00
{ cost: costs[index].upgrades.toLocaleString() }
2025-01-16 13:48:13 -06:00
)}
2024-03-15 12:28:29 -05:00
</span>
</div>
)}
<div
style={{
2025-10-02 10:15:38 -05:00
display: "flex",
2024-03-15 12:28:29 -05:00
gap: 4,
2025-10-02 10:15:38 -05:00
alignItems: "center",
2024-03-15 12:28:29 -05:00
}}
>
<img
2024-05-04 16:28:12 -05:00
alt="Silver icon"
2024-04-24 16:22:48 -05:00
src={await iconPng(
2025-10-02 10:15:38 -05:00
asset("icons/currencies/silver.webp")
2024-03-15 12:28:29 -05:00
)}
width={16}
height={16}
/>
<span
style={{
fontSize: 16,
color: theme.colors.textHighContrast,
}}
>
2025-01-16 13:48:13 -06:00
{literals(
strings.bot.commands.research.body.purchase,
2025-10-02 10:15:38 -05:00
{
cost: costs[index].purchase.toLocaleString(
interaction.locale
2025-01-16 13:48:13 -06:00
),
2025-10-02 10:15:38 -05:00
}
2025-01-16 13:48:13 -06:00
)}
2024-03-15 12:28:29 -05:00
</span>
</div>
<div
style={{
2025-10-02 10:15:38 -05:00
display: "flex",
2024-03-15 12:28:29 -05:00
gap: 4,
2025-10-02 10:15:38 -05:00
alignItems: "center",
2024-03-15 12:28:29 -05:00
}}
>
<img
2024-05-04 16:28:12 -05:00
alt="Silver icon"
2024-04-24 16:22:48 -05:00
src={await iconPng(
2025-10-02 10:15:38 -05:00
asset("icons/currencies/silver.webp")
2024-03-15 12:28:29 -05:00
)}
width={16}
height={16}
/>
<span
style={{
fontSize: 16,
color: theme.colors.textHighContrast,
}}
>
2025-01-16 13:48:13 -06:00
{literals(
strings.bot.commands.research.body.equipment,
2025-10-02 10:15:38 -05:00
{
cost: costs[index].equipment.toLocaleString(
interaction.locale
2025-01-16 13:48:13 -06:00
),
2025-10-02 10:15:38 -05:00
}
2025-01-16 13:48:13 -06:00
)}
2024-03-15 12:28:29 -05:00
</span>
</div>
2024-03-14 17:31:11 -05:00
</div>
</div>
);
2025-10-02 10:15:38 -05:00
})
2024-03-14 17:31:11 -05:00
)}
</div>
2024-03-14 16:43:28 -05:00
</CommandWrapper>
);
2024-03-15 21:50:19 -05:00
return startingTankRaw
? image
2025-01-16 13:48:13 -06:00
: [strings.bot.commands.research.body.estimation, image];
2024-03-14 16:43:28 -05:00
},
autocomplete(interaction) {
2025-10-02 10:15:38 -05:00
autocompleteTanks(interaction, true, ["target-tank", "starting-tank"]);
2024-03-14 16:43:28 -05:00
},
});
});