Merge branch 'main' into ue

This commit is contained in:
TrèsAbhi committed 2026-04-04 20:25:51 -05:00
commit c0376182f8
164 files changed
+5145 -4079

No files matched your search

+18
View File
@@ -0,0 +1,18 @@
# BlitzKit 1.29.16
Thank you to [GonnaHetzMe](https://github.com/karelpak1) for your contributions this update.
## Fixes
- [GonnaHetzMe] The turret for Centurion I is no longer offset from the body
- Please allow up to 24 hours for the model to update
- Vertical Stabilizers now give the correct 12% buff instead of 15%
- Re-ordering tanks in Compare now works once again
- Fixed review videos from being about the Object 452K across all tanks
- Please allow up to 2 weeks for all tanks to refresh their videos
- Tanks in Mixer with oscillating turrets are not handled properly
## Features
- Headings in the Compare tool are now even more compact for mobile devices
- Enabled all tiers for the randomizer in Mixer
+23
View File
@@ -0,0 +1,23 @@
# BlitzKit 1.30.0
Thank you to [GonnaHetzMe](https://github.com/karelpak1) for your contributions this update.
## Features
- Added an equalizer mode for Tankopedia and Compare to preview what tank stats will look like in the upcoming Equalizer game mode
- This feature is in Beta and the stats are subject to change
- Thanks to WoTB News for sourcing data well ahead of the release of the gamemode
- Added a toggle in Tankopedia, right beneath Provisions
- Added button under the shell selector in the Tank Sandbox
- Added another toggle in the Compare table to enable equalizer for all tanks
## Technical Features
- [GonnaHetzMe] Enabling dev mode now displays the tank's non-numerical id
- [GonnaHetzMe] The dev id and the new dev name are now copyable on click
## Fixes
- [GonnaHetzMe] Tech trees that start at tiers higher than tier 1 like the Blitz Nation ones now properly render their trees in Tankopedia
- [GonnaHetzMe] The tank pitch/yaw inputs in the Tank Sandbox now initialize with the correct values instead of 0
- [GonnaHetzMe] Large Repair Kits once again use the correct modern icon
+8
View File
@@ -0,0 +1,8 @@
# BlitzKit 1.30.1
Thank you to [GonnaHetzMe](https://github.com/karelpak1) for your contributions this update.
## Fixes
- [GonnaHetzMe] Tank hit points in Equalizer mode is now accurate to the game
- Armor highlighting now reflects the actual thicknesses of tanks in Equalizer mode
+4 -4
View File
@@ -43,7 +43,7 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
tankStats.map(async (tankData) => ({ tankStats.map(async (tankData) => ({
tankDefinitions: (await tankDefinitions).tanks[tankData.tank_id]!, tankDefinitions: (await tankDefinitions).tanks[tankData.tank_id]!,
id: tankData.tank_id, id: tankData.tank_id,
})) })),
) )
).filter((tank) => tank.tankDefinitions?.tier === tier); ).filter((tank) => tank.tankDefinitions?.tier === tier);
const groupedTanks: Record<string, TankDefinition[]> = {}; const groupedTanks: Record<string, TankDefinition[]> = {};
@@ -69,16 +69,16 @@ export const ownedTanksCommand = new Promise<CommandRegistry>((resolve) => {
.filter((tank) => tank.tier === tier) .filter((tank) => tank.tier === tier)
.sort( .sort(
(a, b) => (a, b) =>
TANK_CLASSES.indexOf(a.class) - TANK_CLASSES.indexOf(b.class) TANK_CLASSES.indexOf(a.class) - TANK_CLASSES.indexOf(b.class),
) )
.sort( .sort(
(a, b) => (a, b) =>
awaitedGameDefinitions.nations.indexOf(a.nation) - awaitedGameDefinitions.nations.indexOf(a.nation) -
awaitedGameDefinitions.nations.indexOf(b.nation) awaitedGameDefinitions.nations.indexOf(b.nation),
) )
.map( .map(
(tank) => (tank) =>
`[${flags[tank.nation]} ${markdownEscape(unwrap(tank.name))}](<https://blitzkit.app/tanks/${tank.slug}>)` `[${flags[tank.nation]} ${markdownEscape(unwrap(tank.name!))}](<https://blitzkit.app/tanks/${tank.slug}>)`,
); );
const lines = [title, ...tankList]; const lines = [title, ...tankList];
+2 -2
View File
@@ -280,7 +280,7 @@ async function playerListing(
}} }}
> >
<img <img
alt={unwrap(tank.name)} alt={unwrap(tank.name!)}
src={await iconPng(tankIcon(tank.id))} src={await iconPng(tankIcon(tank.id))}
style={{ style={{
width: 32, width: 32,
@@ -317,7 +317,7 @@ async function playerListing(
fontSize: 16, fontSize: 16,
}} }}
> >
{unwrap(tank.name)} {unwrap(tank.name!)}
</span> </span>
</div> </div>
+47 -47
View File
@@ -35,49 +35,49 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
command: createLocalizedCommand("research") command: createLocalizedCommand("research")
.addStringOption((option) => .addStringOption((option) =>
option option
.setName(strings.bot.commands.research.options.target_tank.name) .setName(strings.bot.commands.research.options.target_tank.name!)
.setNameLocalizations( .setNameLocalizations(
localizationObject( localizationObject(
(strings) => (strings) =>
strings.bot.commands.research.options.target_tank.name, strings.bot.commands.research.options.target_tank.name,
undefined, undefined,
true true,
) ),
) )
.setDescription( .setDescription(
strings.bot.commands.research.options.target_tank.description strings.bot.commands.research.options.target_tank.description,
) )
.setDescriptionLocalizations( .setDescriptionLocalizations(
localizationObject( localizationObject(
(strings) => (strings) =>
strings.bot.commands.research.options.target_tank.description strings.bot.commands.research.options.target_tank.description,
) ),
) )
.setAutocomplete(true) .setAutocomplete(true)
.setRequired(true) .setRequired(true),
) )
.addStringOption((option) => .addStringOption((option) =>
option option
.setName(strings.bot.commands.research.options.starting_tank.name) .setName(strings.bot.commands.research.options.starting_tank.name!)
.setNameLocalizations( .setNameLocalizations(
localizationObject( localizationObject(
(strings) => (strings) =>
strings.bot.commands.research.options.starting_tank.name, strings.bot.commands.research.options.starting_tank.name,
undefined, undefined,
true true,
) ),
) )
.setDescription( .setDescription(
strings.bot.commands.research.options.starting_tank.description strings.bot.commands.research.options.starting_tank.description,
) )
.setDescriptionLocalizations( .setDescriptionLocalizations(
localizationObject( localizationObject(
(strings) => (strings) =>
strings.bot.commands.research.options.starting_tank.description strings.bot.commands.research.options.starting_tank.description,
) ),
) )
.setAutocomplete(true) .setAutocomplete(true)
.setRequired(false) .setRequired(false),
) )
.addStringOption(addUsernameChoices), .addStringOption(addUsernameChoices),
@@ -87,7 +87,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
const targetTankId = await resolveTankId( const targetTankId = await resolveTankId(
interaction.options.getString("target-tank", true), interaction.options.getString("target-tank", true),
interaction.locale, interaction.locale,
true true,
); );
const startingTankRaw = interaction.options.getString("starting-tank"); const startingTankRaw = interaction.options.getString("starting-tank");
const targetTank = awaitedTankDefinitions.tanks[targetTankId]; const targetTank = awaitedTankDefinitions.tanks[targetTankId];
@@ -99,13 +99,13 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
startingTankId = await resolveTankId( startingTankId = await resolveTankId(
startingTankRaw, startingTankRaw,
interaction.locale, interaction.locale,
true true,
); );
if (targetTankId === startingTankId) { if (targetTankId === startingTankId) {
return literals( return literals(
strings.bot.commands.research.errors.start_end_equal, strings.bot.commands.research.errors.start_end_equal,
{ tank: escapeMarkdown(unwrap(targetTank.name)) } { tank: escapeMarkdown(unwrap(targettank.name!)) },
); );
} }
@@ -113,8 +113,8 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
if (startingTank.tier > targetTank.tier) { if (startingTank.tier > targetTank.tier) {
return literals(strings.bot.commands.research.errors.unordered, { return literals(strings.bot.commands.research.errors.unordered, {
tank_1: escapeMarkdown(unwrap(startingTank.name)), tank_1: escapeMarkdown(unwrap(startingtank.name!)),
tank_2: escapeMarkdown(unwrap(targetTank.name)), tank_2: escapeMarkdown(unwrap(targettank.name!)),
}); });
} }
@@ -122,21 +122,21 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
return literals( return literals(
strings.bot.commands.research.errors.tanks_not_on_line, strings.bot.commands.research.errors.tanks_not_on_line,
{ {
tank_1: escapeMarkdown(unwrap(targetTank.name)), tank_1: escapeMarkdown(unwrap(targettank.name!)),
tank_2: escapeMarkdown(unwrap(startingTank.name)), tank_2: escapeMarkdown(unwrap(startingtank.name!)),
} },
); );
} }
} else { } else {
if (targetTank.type !== TankType.RESEARCHABLE) { if (targetTank.type !== TankType.TANK_TYPE_RESEARCHABLE) {
return literals(strings.bot.commands.research.errors.non_tech_tree, { return literals(strings.bot.commands.research.errors.non_tech_tree, {
tank: escapeMarkdown(unwrap(targetTank.name)), tank: escapeMarkdown(unwrap(targettank.name!)),
}); });
} }
if (targetTank.tier === 1) { if (targetTank.tier === 1) {
return literals(strings.bot.commands.research.errors.tier_1, { return literals(strings.bot.commands.research.errors.tier_1, {
tank: escapeMarkdown(unwrap(targetTank.name)), tank: escapeMarkdown(unwrap(targettank.name!)),
}); });
} }
@@ -149,14 +149,14 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
if (tankStats.some(({ tank_id }) => tank_id === targetTankId)) { if (tankStats.some(({ tank_id }) => tank_id === targetTankId)) {
return literals( return literals(
strings.bot.commands.research.errors.already_researched, strings.bot.commands.research.errors.already_researched,
{ tank: escapeMarkdown(unwrap(targetTank.name)) } { tank: escapeMarkdown(unwrap(targettank.name!)) },
); );
} }
const foundAncestor = targetTankAncestry.find( const foundAncestor = targetTankAncestry.find(
(id) => (id) =>
awaitedTankDefinitions.tanks[id].tier === 1 || awaitedTankDefinitions.tanks[id].tier === 1 ||
tankStats.some(({ tank_id }) => tank_id === id) tankStats.some(({ tank_id }) => tank_id === id),
); );
if (foundAncestor === undefined) { if (foundAncestor === undefined) {
@@ -168,7 +168,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
const techTreeLine = await buildTechTreeLine( const techTreeLine = await buildTechTreeLine(
startingTankId, startingTankId,
targetTankId targetTankId,
); );
const line = [...techTreeLine, startingTankId].reverse(); const line = [...techTreeLine, startingTankId].reverse();
@@ -190,7 +190,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
) { ) {
turretXps.set( turretXps.set(
turret.id, turret.id,
turret.research_cost.research_cost_type!.value turret.research_cost.research_cost_type!.value,
); );
} }
@@ -211,7 +211,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
) { ) {
engineXps.set( engineXps.set(
engine.id, engine.id,
engine.research_cost.research_cost_type!.value engine.research_cost.research_cost_type!.value,
); );
} }
}); });
@@ -223,7 +223,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
) { ) {
trackXps.set( trackXps.set(
track.id, track.id,
track.research_cost.research_cost_type!.value track.research_cost.research_cost_type!.value,
); );
} }
}); });
@@ -245,13 +245,13 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
return { return {
research: tank.research_cost!.research_cost_type!.value as number, research: tank.research_cost!.research_cost_type!.value as number,
upgrades: moduleXp, upgrades: moduleXp,
purchase: tank.price.value, purchase: tank.price!.value,
equipment: equipmentPriceMatrix[tank.tier].reduce( equipment: equipmentPriceMatrix[tank.tier].reduce(
(a, b) => a + 3 * b, (a, b) => a + 3 * b,
0 0,
), ),
}; };
}) }),
); );
const image = ( const image = (
@@ -456,7 +456,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
}} }}
> >
<img <img
alt={unwrap(tank.name)} alt={unwrap(tank.name!)}
src={TANK_ICONS[tank.class]} src={TANK_ICONS[tank.class]}
width={16} width={16}
height={16} height={16}
@@ -467,7 +467,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
color: theme.colors.textHighContrast, color: theme.colors.textHighContrast,
}} }}
> >
{unwrap(tank.name)} {unwrap(tank.name!)}
</span> </span>
</div> </div>
</div> </div>
@@ -505,9 +505,9 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
strings.bot.commands.research.body.research, strings.bot.commands.research.body.research,
{ {
cost: costs[index].research?.toLocaleString( cost: costs[index].research?.toLocaleString(
interaction.locale interaction.locale,
), ),
} },
)} )}
</span> </span>
</div> </div>
@@ -523,7 +523,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<img <img
alt="Free XP icon" alt="Free XP icon"
src={await iconPng( src={await iconPng(
asset("icons/currencies/free-xp.webp") asset("icons/currencies/free-xp.webp"),
)} )}
width={16} width={16}
height={16} height={16}
@@ -536,7 +536,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
> >
{literals( {literals(
strings.bot.commands.research.body.upgrades, strings.bot.commands.research.body.upgrades,
{ cost: costs[index].upgrades.toLocaleString() } { cost: costs[index].upgrades.toLocaleString() },
)} )}
</span> </span>
</div> </div>
@@ -552,7 +552,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<img <img
alt="Silver icon" alt="Silver icon"
src={await iconPng( src={await iconPng(
asset("icons/currencies/silver.webp") asset("icons/currencies/silver.webp"),
)} )}
width={16} width={16}
height={16} height={16}
@@ -567,9 +567,9 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
strings.bot.commands.research.body.purchase, strings.bot.commands.research.body.purchase,
{ {
cost: costs[index].purchase.toLocaleString( cost: costs[index].purchase.toLocaleString(
interaction.locale interaction.locale,
), ),
} },
)} )}
</span> </span>
</div> </div>
@@ -584,7 +584,7 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
<img <img
alt="Silver icon" alt="Silver icon"
src={await iconPng( src={await iconPng(
asset("icons/currencies/silver.webp") asset("icons/currencies/silver.webp"),
)} )}
width={16} width={16}
height={16} height={16}
@@ -599,16 +599,16 @@ export const researchCommand = new Promise<CommandRegistry>((resolve) => {
strings.bot.commands.research.body.equipment, strings.bot.commands.research.body.equipment,
{ {
cost: costs[index].equipment.toLocaleString( cost: costs[index].equipment.toLocaleString(
interaction.locale interaction.locale,
), ),
} },
)} )}
</span> </span>
</div> </div>
</div> </div>
</div> </div>
); );
}) }),
)} )}
</div> </div>
</CommandWrapper> </CommandWrapper>
@@ -3,14 +3,14 @@ import {
TankClass, TankClass,
TankType, TankType,
TREE_TYPE_ICONS, TREE_TYPE_ICONS,
} from '@blitzkit/core'; } from "@blitzkit/core";
import { createColors } from 'bepaint'; import { createColors } from "bepaint";
import { import {
type AccentColor, type AccentColor,
type GrayColor, type GrayColor,
PALETTES, PALETTES,
} from '../../../core/radix/radixColors'; } from "../../../core/radix/radixColors";
import { RowStat } from './RowStat'; import { RowStat } from "./RowStat";
export interface RowStatItem { export interface RowStatItem {
title: string; title: string;
@@ -21,7 +21,7 @@ export interface RowStatItem {
} }
interface RowProps { interface RowProps {
displayType?: 'tank' | 'summary'; displayType?: "tank" | "summary";
naked?: boolean; naked?: boolean;
title: string; title: string;
minimized?: boolean; minimized?: boolean;
@@ -32,7 +32,7 @@ interface RowProps {
} }
export function Row({ export function Row({
color = 'slate', color = "slate",
stats, stats,
title, title,
minimized, minimized,
@@ -49,20 +49,20 @@ export function Row({
className="session-tracker-row" className="session-tracker-row"
style={{ style={{
maxWidth: naked ? 600 : Infinity, maxWidth: naked ? 600 : Infinity,
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
borderRadius: 4, borderRadius: 4,
backgroundColor: naked backgroundColor: naked
? `${theme.appBackground2}c0` ? `${theme.appBackground2}c0`
: theme.appBackground2, : theme.appBackground2,
overflow: 'hidden', overflow: "hidden",
}} }}
> >
<div <div
style={{ style={{
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
justifyContent: 'center', justifyContent: "center",
padding: 8, padding: 8,
gap: 4, gap: 4,
backgroundColor: naked backgroundColor: naked
@@ -70,10 +70,14 @@ export function Row({
: theme.componentInteractive, : theme.componentInteractive,
}} }}
> >
{displayType === 'tank' && tankClass !== undefined && ( {displayType === "tank" && tankClass !== undefined && (
<img <img
alt={TankClass[tankClass]} alt={TankClass[tankClass]}
src={TREE_TYPE_ICONS[type ?? TankType.RESEARCHABLE][tankClass]} src={
TREE_TYPE_ICONS[type ?? TankType.TANK_TYPE_RESEARCHABLE][
tankClass
]
}
style={{ width: 16, height: 16 }} style={{ width: 16, height: 16 }}
/> />
)} )}
@@ -81,10 +85,10 @@ export function Row({
<span <span
style={{ style={{
color: color:
displayType === 'tank' displayType === "tank"
? type === TankType.COLLECTOR ? type === TankType.TANK_TYPE_COLLECTOR
? theme.textLowContrast_blue ? theme.textLowContrast_blue
: type === TankType.PREMIUM : type === TankType.TANK_TYPE_PREMIUM
? theme.textLowContrast_amber ? theme.textLowContrast_amber
: theme.textHighContrast : theme.textHighContrast
: theme.textLowContrast, : theme.textLowContrast,
@@ -98,7 +102,7 @@ export function Row({
<div <div
style={{ style={{
display: 'flex', display: "flex",
paddingTop: 8, paddingTop: 8,
paddingBottom: 8, paddingBottom: 8,
}} }}
@@ -109,8 +113,8 @@ export function Row({
<RowStat <RowStat
minimized={minimized} minimized={minimized}
key={index} key={index}
name={`${row.title} • ${row.career ?? '--'}`} name={`${row.title} • ${row.career ?? "--"}`}
value={row.current ?? '--'} value={row.current ?? "--"}
delta={row.delta} delta={row.delta}
percentile={row.percentile} percentile={row.percentile}
/> />
@@ -1,5 +1,5 @@
import { TankClass, TankType, TREE_TYPE_ICONS } from '@blitzkit/core'; import { TankClass, TankType, TREE_TYPE_ICONS } from "@blitzkit/core";
import { theme } from '../../../../stitches.config'; import { theme } from "../../../../stitches.config";
export interface ItemProps { export interface ItemProps {
tankClass?: TankClass; tankClass?: TankClass;
@@ -8,9 +8,9 @@ export interface ItemProps {
} }
const TREE_TYPE_COLOR = { const TREE_TYPE_COLOR = {
[TankType.RESEARCHABLE]: '', [TankType.TANK_TYPE_RESEARCHABLE]: "",
[TankType.PREMIUM]: '_amber', [TankType.TANK_TYPE_PREMIUM]: "_amber",
[TankType.COLLECTOR]: '_blue', [TankType.TANK_TYPE_COLLECTOR]: "_blue",
} as const; } as const;
export function Item({ export function Item({
@@ -21,12 +21,12 @@ export function Item({
return ( return (
<div <div
style={{ style={{
display: 'flex', display: "flex",
height: 32, height: 32,
paddingLeft: 8, paddingLeft: 8,
paddingRight: 8, paddingRight: 8,
overflow: 'hidden', overflow: "hidden",
alignItems: 'center', alignItems: "center",
borderRadius: 4, borderRadius: 4,
backgroundColor: backgroundColor:
theme.colors[`componentInteractive${TREE_TYPE_COLOR[treeType]}`], theme.colors[`componentInteractive${TREE_TYPE_COLOR[treeType]}`],
@@ -34,12 +34,12 @@ export function Item({
> >
<div <div
style={{ style={{
display: 'flex', display: "flex",
gap: 4, gap: 4,
flex: 1, flex: 1,
alignItems: 'center', alignItems: "center",
justifyContent: 'flex-start', justifyContent: "flex-start",
overflow: 'hidden', overflow: "hidden",
}} }}
> >
{tankType && ( {tankType && (
@@ -52,7 +52,7 @@ export function Item({
<span <span
style={{ style={{
fontSize: 16, fontSize: 16,
whiteSpace: 'nowrap', whiteSpace: "nowrap",
color: theme.colors[`textLowContrast${TREE_TYPE_COLOR[treeType]}`], color: theme.colors[`textLowContrast${TREE_TYPE_COLOR[treeType]}`],
}} }}
> >
@@ -6,21 +6,21 @@ import { translator } from "../localization/translator";
export async function filtersToDescription( export async function filtersToDescription(
{ nation, tier, type, class: tankClass, tank }: StatFilters, { nation, tier, type, class: tankClass, tank }: StatFilters,
locale: Locale locale: Locale,
) { ) {
const { strings, bkLocale } = translator(locale); const { strings, bkLocale } = translator(locale);
const awaitedTankDefinitions = await fetchTankDefinitions(); const awaitedTankDefinitions = await fetchTankDefinitions();
const info: string[] = []; const info: string[] = [];
// TODO: somehow localize tank names? // TODO: somehow localize tank names?
if (tank) return awaitedTankDefinitions.tanks[tank].name.locales[bkLocale]; if (tank) return awaitedTankDefinitions.tanks[tank].name!.locales[bkLocale];
if (nation) if (nation)
info.push((strings.common.nations as Record<string, string>)[nation]); info.push((strings.common.nations as Record<string, string>)[nation]);
if (tier) { if (tier) {
info.push( info.push(
literals(strings.bot.common.filters.tier, { literals(strings.bot.common.filters.tier, {
tier: TIER_ROMAN_NUMERALS[tier], tier: TIER_ROMAN_NUMERALS[tier],
}) }),
); );
} }
if (tankClass) info.push(strings.common.tank_class_short[tankClass]); if (tankClass) info.push(strings.common.tank_class_short[tankClass]);
@@ -34,12 +34,12 @@ export async function filterStats(
(filters.tier === undefined || entry.tier === filters.tier) && (filters.tier === undefined || entry.tier === filters.tier) &&
(filters.class === undefined || entry.class === filters.class) && (filters.class === undefined || entry.class === filters.class) &&
(filters.type === undefined || (filters.type === undefined ||
(filters.type === TankType.COLLECTOR && (filters.type === TankType.TANK_TYPE_COLLECTOR &&
entry.type === TankType.COLLECTOR) || entry.type === TankType.TANK_TYPE_COLLECTOR) ||
(filters.type === TankType.PREMIUM && (filters.type === TankType.TANK_TYPE_PREMIUM &&
entry.type === TankType.PREMIUM) || entry.type === TankType.TANK_TYPE_PREMIUM) ||
(filters.type === TankType.RESEARCHABLE && (filters.type === TankType.TANK_TYPE_RESEARCHABLE &&
entry.type === TankType.RESEARCHABLE)) && entry.type === TankType.TANK_TYPE_RESEARCHABLE)) &&
(filters.tank === undefined || entry.id === filters.tank) (filters.tank === undefined || entry.id === filters.tank)
); );
}); });
@@ -1,12 +1,12 @@
import { Locale, SlashCommandStringOption } from 'discord.js'; import { Locale, SlashCommandStringOption } from "discord.js";
import { translator } from '../localization/translator'; import { translator } from "../localization/translator";
import { localizationObject } from './localizationObject'; import { localizationObject } from "./localizationObject";
export function addTankChoices(option: SlashCommandStringOption) { export function addTankChoices(option: SlashCommandStringOption) {
const { strings } = translator(Locale.EnglishUS); const { strings } = translator(Locale.EnglishUS);
return option return option
.setName(strings.bot.common.options.tank.name) .setName(strings.bot.common.options.tank.name!)
.setNameLocalizations( .setNameLocalizations(
localizationObject( localizationObject(
(strings) => strings.bot.common.options.tank.name, (strings) => strings.bot.common.options.tank.name,
@@ -13,13 +13,13 @@ import {
} from "./autocompleteClan/constants"; } from "./autocompleteClan/constants";
export const tankNamesTechTreeOnly = fetchTankNames().then((names) => export const tankNamesTechTreeOnly = fetchTankNames().then((names) =>
names.filter((tank) => tank.treeType === TankType.RESEARCHABLE) names.filter((tank) => tank.treeType === TankType.TANK_TYPE_RESEARCHABLE),
); );
export async function autocompleteTanks( export async function autocompleteTanks(
interaction: AutocompleteInteraction<CacheType>, interaction: AutocompleteInteraction<CacheType>,
techTreeOnly = false, techTreeOnly = false,
fields = ["tank"] fields = ["tank"],
) { ) {
const { unwrap } = translator(interaction.locale); const { unwrap } = translator(interaction.locale);
const focusedOption = interaction.options.getFocused(true); const focusedOption = interaction.options.getFocused(true);
@@ -31,7 +31,7 @@ export async function autocompleteTanks(
go( go(
focusedOption.value, focusedOption.value,
await (techTreeOnly ? tankNamesTechTreeOnly : tankNames), await (techTreeOnly ? tankNamesTechTreeOnly : tankNames),
{ keys: SEARCH_KEYS, limit: 10 } { keys: SEARCH_KEYS, limit: 10 },
).map(async (item) => { ).map(async (item) => {
let name = unwrap(item.obj.searchableName); let name = unwrap(item.obj.searchableName);
@@ -42,7 +42,7 @@ export async function autocompleteTanks(
0, 0,
unwrap(item.obj.searchableName).length - unwrap(item.obj.searchableName).length -
overSize - overSize -
OVERFLOW_SUFFIX.length OVERFLOW_SUFFIX.length,
)}${OVERFLOW_SUFFIX}`; )}${OVERFLOW_SUFFIX}`;
} }
@@ -50,8 +50,8 @@ export async function autocompleteTanks(
name, name,
value: `${item.obj.id}`, value: `${item.obj.id}`,
} satisfies ApplicationCommandOptionChoiceData<string>; } satisfies ApplicationCommandOptionChoiceData<string>;
}) }),
) )
: [] : [],
); );
} }
+4 -2
View File
@@ -1,5 +1,7 @@
import { ShellDefinition, ShellType } from "../protos"; import { ShellDefinition, ShellType } from "../protos";
export function canSplash(type: ShellDefinition["type"]): type is ShellType.HE { export function canSplash(
return type === ShellType.HE; type: ShellDefinition["type"],
): type is ShellType.SHELL_TYPE_HE {
return type === ShellType.SHELL_TYPE_HE;
} }
+10 -10
View File
@@ -2,22 +2,22 @@ import { TankClass, TankType } from "../protos";
import type { BlitzStats } from "../statistics"; import type { BlitzStats } from "../statistics";
export const TANK_TYPES = [ export const TANK_TYPES = [
TankType.RESEARCHABLE, TankType.TANK_TYPE_RESEARCHABLE,
TankType.PREMIUM, TankType.TANK_TYPE_PREMIUM,
TankType.COLLECTOR, TankType.TANK_TYPE_COLLECTOR,
]; ];
export const TANK_TYPE_COMMAND_NAMES = { export const TANK_TYPE_COMMAND_NAMES = {
[TankType.RESEARCHABLE]: "researchable", [TankType.TANK_TYPE_RESEARCHABLE]: "researchable",
[TankType.PREMIUM]: "premium", [TankType.TANK_TYPE_PREMIUM]: "premium",
[TankType.COLLECTOR]: "collector", [TankType.TANK_TYPE_COLLECTOR]: "collector",
}; };
export const TANK_CLASSES = [ export const TANK_CLASSES = [
TankClass.LIGHT, TankClass.TANK_CLASS_LIGHT,
TankClass.MEDIUM, TankClass.TANK_CLASS_MEDIUM,
TankClass.HEAVY, TankClass.TANK_CLASS_HEAVY,
TankClass.TANK_DESTROYER, TankClass.TANK_CLASS_TANK_DESTROYER,
]; ];
export const emptyAllStats: BlitzStats = { export const emptyAllStats: BlitzStats = {
@@ -9,7 +9,7 @@ import {
export function checkConsumableProvisionInclusivity( export function checkConsumableProvisionInclusivity(
consumableProvision: Consumable | Provision, consumableProvision: Consumable | Provision,
tank: TankDefinition, tank: TankDefinition,
gun: GunDefinition gun: GunDefinition,
) { ) {
const included = consumableProvision.include?.every((rule) => { const included = consumableProvision.include?.every((rule) => {
switch (rule.filter_type!.$case) { switch (rule.filter_type!.$case) {
@@ -46,7 +46,7 @@ export function checkConsumableProvisionInclusivity(
case "categories": case "categories":
return rule.filter_type!.value.categories.some((category) => { return rule.filter_type!.value.categories.some((category) => {
switch (category) { switch (category) {
case ConsumableTankCategoryFilterCategory.CLIP: case ConsumableTankCategoryFilterCategory.CONSUMABLE_TANK_CATEGORY_FILTER_CATEGORY_CLIP:
return gun.gun_type!.$case !== "regular"; return gun.gun_type!.$case !== "regular";
} }
}); });
+3 -3
View File
@@ -1,7 +1,7 @@
import { ShellDefinition, ShellType } from "../protos"; import { ShellDefinition, ShellType } from "../protos";
export function isExplosive( export function isExplosive(
type: ShellDefinition["type"] type: ShellDefinition["type"],
): type is ShellType.HEAT | ShellType.HE { ): type is ShellType.SHELL_TYPE_HEAT | ShellType.SHELL_TYPE_HE {
return type === ShellType.HEAT || type === ShellType.HE; return type === ShellType.SHELL_TYPE_HEAT || type === ShellType.SHELL_TYPE_HE;
} }
+7 -7
View File
@@ -2,27 +2,27 @@ import { TANK_CLASSES } from "../blitz/constants";
import { GameDefinitions, TankDefinition, TankType } from "../protos"; import { GameDefinitions, TankDefinition, TankType } from "../protos";
const treeTypeOrder = [ const treeTypeOrder = [
TankType.RESEARCHABLE, TankType.TANK_TYPE_RESEARCHABLE,
TankType.PREMIUM, TankType.TANK_TYPE_PREMIUM,
TankType.COLLECTOR, TankType.TANK_TYPE_COLLECTOR,
]; ];
export function metaSortTank( export function metaSortTank(
tanks: TankDefinition[], tanks: TankDefinition[],
gameDefinitions: GameDefinitions gameDefinitions: GameDefinitions,
) { ) {
return tanks return tanks
.sort((a, b) => b.tier - a.tier) .sort((a, b) => b.tier - a.tier)
.sort( .sort(
(a, b) => treeTypeOrder.indexOf(b.type) - treeTypeOrder.indexOf(a.type) (a, b) => treeTypeOrder.indexOf(b.type) - treeTypeOrder.indexOf(a.type),
) )
.sort( .sort(
(a, b) => TANK_CLASSES.indexOf(b.class) - TANK_CLASSES.indexOf(a.class) (a, b) => TANK_CLASSES.indexOf(b.class) - TANK_CLASSES.indexOf(a.class),
) )
.sort( .sort(
(a, b) => (a, b) =>
gameDefinitions.nations.indexOf(b.nation) - gameDefinitions.nations.indexOf(b.nation) -
gameDefinitions.nations.indexOf(a.nation) gameDefinitions.nations.indexOf(a.nation),
) )
.sort((a, b) => a.tier - b.tier); .sort((a, b) => a.tier - b.tier);
} }
@@ -2,8 +2,8 @@ import { BoundingBox, Vector3 } from "../protos";
export function normalizeBoundingBox(boundingBox: BoundingBox) { export function normalizeBoundingBox(boundingBox: BoundingBox) {
return { return {
x: boundingBox.max.x - boundingBox.min.x, x: boundingBox.max!.x - boundingBox.min!.x,
y: boundingBox.max.y - boundingBox.min.y, y: boundingBox.max!.y - boundingBox.min!.y,
z: boundingBox.max.z - boundingBox.min.z, z: boundingBox.max!.z - boundingBox.min!.z,
} satisfies Vector3; } satisfies Vector3;
} }
@@ -1,16 +1,21 @@
import { ShellType } from '../protos'; import { Equalizer, ShellType } from "../protos";
export function resolvePenetrationCoefficient( export function resolvePenetrationCoefficient(
calibratedShells: boolean, calibratedShells: boolean,
equalize: boolean,
type: ShellType, type: ShellType,
equalizerObj?: Equalizer,
) { ) {
if (!calibratedShells) return 1; const calibrated = calibratedShells
? type === ShellType.SHELL_TYPE_AP
? 1.08
: type === ShellType.SHELL_TYPE_APCR
? 1.05
: type === ShellType.SHELL_TYPE_HEAT
? 1.13
: 1.08
: 1;
const equalizer = (equalize ? equalizerObj?.penetration : undefined) ?? 1;
return type === ShellType.AP return calibrated * equalizer;
? 1.08
: type === ShellType.APCR
? 1.05
: type === ShellType.HEAT
? 1.13
: 1.08;
} }
@@ -1,74 +1,74 @@
import { times } from 'lodash-es'; import { times } from "lodash-es";
import { CrewType, ShellType, TankClass, TankType } from '../../protos'; import { CrewType, ShellType, TankClass, TankType } from "../../protos";
export const SHELL_NAMES: Record<ShellType, string> = { export const SHELL_NAMES: Record<ShellType, string> = {
[ShellType.AP]: 'AP', [ShellType.SHELL_TYPE_AP]: "AP",
[ShellType.APCR]: 'APCR', [ShellType.SHELL_TYPE_APCR]: "APCR",
[ShellType.HEAT]: 'HEAT', [ShellType.SHELL_TYPE_HEAT]: "HEAT",
[ShellType.HE]: 'HE', [ShellType.SHELL_TYPE_HE]: "HE",
}; };
export const CREW_MEMBER_NAMES = { export const CREW_MEMBER_NAMES = {
[CrewType.COMMANDER]: 'commander', [CrewType.CREW_TYPE_COMMANDER]: "commander",
[CrewType.DRIVER]: 'driver', [CrewType.CREW_TYPE_DRIVER]: "driver",
[CrewType.GUNNER]: 'gunner', [CrewType.CREW_TYPE_GUNNER]: "gunner",
[CrewType.LOADER]: 'loader', [CrewType.CREW_TYPE_LOADER]: "loader",
[CrewType.RADIOMAN]: 'radioman', [CrewType.CREW_TYPE_RADIOMAN]: "radioman",
} as const; } as const;
/** /**
* @deprecated use svg * @deprecated use svg
*/ */
export const TANK_ICONS: Record<TankClass, string> = { export const TANK_ICONS: Record<TankClass, string> = {
[TankClass.TANK_DESTROYER]: 'https://i.imgur.com/BIHSEH0.png', [TankClass.TANK_CLASS_TANK_DESTROYER]: "https://i.imgur.com/BIHSEH0.png",
[TankClass.LIGHT]: 'https://i.imgur.com/CSNha5V.png', [TankClass.TANK_CLASS_LIGHT]: "https://i.imgur.com/CSNha5V.png",
[TankClass.MEDIUM]: 'https://i.imgur.com/wvf3ltm.png', [TankClass.TANK_CLASS_MEDIUM]: "https://i.imgur.com/wvf3ltm.png",
[TankClass.HEAVY]: 'https://i.imgur.com/ECeqlZa.png', [TankClass.TANK_CLASS_HEAVY]: "https://i.imgur.com/ECeqlZa.png",
}; };
/** /**
* @deprecated use svg * @deprecated use svg
*/ */
export const TANK_ICONS_PREMIUM: Record<TankClass, string> = { export const TANK_ICONS_PREMIUM: Record<TankClass, string> = {
[TankClass.TANK_DESTROYER]: 'https://i.imgur.com/TCu3EdR.png', [TankClass.TANK_CLASS_TANK_DESTROYER]: "https://i.imgur.com/TCu3EdR.png",
[TankClass.LIGHT]: 'https://i.imgur.com/zdkpTRb.png', [TankClass.TANK_CLASS_LIGHT]: "https://i.imgur.com/zdkpTRb.png",
[TankClass.MEDIUM]: 'https://i.imgur.com/3z7eHX6.png', [TankClass.TANK_CLASS_MEDIUM]: "https://i.imgur.com/3z7eHX6.png",
[TankClass.HEAVY]: 'https://i.imgur.com/P3vbmyA.png', [TankClass.TANK_CLASS_HEAVY]: "https://i.imgur.com/P3vbmyA.png",
}; };
/** /**
* @deprecated use svg * @deprecated use svg
*/ */
export const TANK_ICONS_COLLECTOR: Record<TankClass, string> = { export const TANK_ICONS_COLLECTOR: Record<TankClass, string> = {
[TankClass.TANK_DESTROYER]: 'https://i.imgur.com/WTjeirB.png', [TankClass.TANK_CLASS_TANK_DESTROYER]: "https://i.imgur.com/WTjeirB.png",
[TankClass.LIGHT]: 'https://i.imgur.com/EwhtKkU.png', [TankClass.TANK_CLASS_LIGHT]: "https://i.imgur.com/EwhtKkU.png",
[TankClass.MEDIUM]: 'https://i.imgur.com/u8YDMBh.png', [TankClass.TANK_CLASS_MEDIUM]: "https://i.imgur.com/u8YDMBh.png",
[TankClass.HEAVY]: 'https://i.imgur.com/8xRf3nc.png', [TankClass.TANK_CLASS_HEAVY]: "https://i.imgur.com/8xRf3nc.png",
}; };
export const TIERS = times(10, (index) => index + 1); export const TIERS = times(10, (index) => index + 1);
export const TIER_ROMAN_NUMERALS: Record<number, string> = { export const TIER_ROMAN_NUMERALS: Record<number, string> = {
1: 'I', 1: "I",
2: 'II', 2: "II",
3: 'III', 3: "III",
4: 'IV', 4: "IV",
5: 'V', 5: "V",
6: 'VI', 6: "VI",
7: 'VII', 7: "VII",
8: 'VIII', 8: "VIII",
9: 'IX', 9: "IX",
10: 'X', 10: "X",
}; };
export const flags: Record<string, string> = { export const flags: Record<string, string> = {
ussr: '<:ussr:1218421042033197197>', ussr: "<:ussr:1218421042033197197>",
germany: '🇩🇪', germany: "🇩🇪",
usa: '🇺🇸', usa: "🇺🇸",
china: '🇨🇳', china: "🇨🇳",
uk: '🇬🇧', uk: "🇬🇧",
france: '🇫🇷', france: "🇫🇷",
japan: '🇯🇵', japan: "🇯🇵",
european: '🇪🇺', european: "🇪🇺",
other: '<:other:1218421572243558482>', other: "<:other:1218421572243558482>",
}; };
export const TREE_TYPE_ICONS: Record<TankType, Record<TankClass, string>> = { export const TREE_TYPE_ICONS: Record<TankType, Record<TankClass, string>> = {
[TankType.RESEARCHABLE]: TANK_ICONS, [TankType.TANK_TYPE_RESEARCHABLE]: TANK_ICONS,
[TankType.PREMIUM]: TANK_ICONS_PREMIUM, [TankType.TANK_TYPE_PREMIUM]: TANK_ICONS_PREMIUM,
[TankType.COLLECTOR]: TANK_ICONS_COLLECTOR, [TankType.TANK_TYPE_COLLECTOR]: TANK_ICONS_COLLECTOR,
}; };
+4 -4
View File
@@ -15,7 +15,7 @@ export async function fetchTankNames() {
tankDefinitionsArray.map(async (tank) => { tankDefinitionsArray.map(async (tank) => {
const searchableNameDeburr: I18nString = { locales: {} }; const searchableNameDeburr: I18nString = { locales: {} };
Object.entries(tank.name).forEach(([key, value]) => { Object.entries(tank.name!).forEach(([key, value]) => {
searchableNameDeburr.locales[key] = deburr(value); searchableNameDeburr.locales[key] = deburr(value);
}); });
@@ -28,8 +28,8 @@ export async function fetchTankNames() {
?.map((id) => ?.map((id) =>
locales.supported.map( locales.supported.map(
({ locale }) => ({ locale }) =>
camouflageDefinitions.camouflages[id]?.name.locales[locale] camouflageDefinitions.camouflages[id]?.name!.locales[locale],
) ),
) )
.flat() .flat()
.filter(Boolean) .filter(Boolean)
@@ -37,7 +37,7 @@ export async function fetchTankNames() {
.join(" "), .join(" "),
treeType: tank.type, treeType: tank.type,
}; };
}) }),
); );
} }
@@ -3,14 +3,14 @@ import { BoundingBox } from "../protos";
export function unionBoundingBox(a: BoundingBox, b: BoundingBox) { export function unionBoundingBox(a: BoundingBox, b: BoundingBox) {
return { return {
min: { min: {
x: Math.min(a.min.x, b.min.x), x: Math.min(a.min!.x, b.min!.x),
y: Math.min(a.min.y, b.min.y), y: Math.min(a.min!.y, b.min!.y),
z: Math.min(a.min.z, b.min.z), z: Math.min(a.min!.z, b.min!.z),
}, },
max: { max: {
x: Math.max(a.max.x, b.max.x), x: Math.max(a.max!.x, b.max!.x),
y: Math.max(a.max.y, b.max.y), y: Math.max(a.max!.y, b.max!.y),
z: Math.max(a.max.z, b.max.z), z: Math.max(a.max!.z, b.max!.z),
}, },
} satisfies BoundingBox; } satisfies BoundingBox;
} }
@@ -4,44 +4,44 @@ package blitzkit;
message AverageDefinitions { message AverageDefinitions {
map<uint32, AverageDefinitionsEntry> averages = 1; map<uint32, AverageDefinitionsEntry> averages = 1;
required Samples samples = 2; required Samples samples = 2;
required uint64 time = 3; required uint64 time = 3;
} }
message AverageDefinitionsEntry { message AverageDefinitionsEntry {
required AverageDefinitionsAllStats mu = 1; required AverageDefinitionsAllStats mu = 1;
required AverageDefinitionsAllStats sigma = 2; required AverageDefinitionsAllStats sigma = 2;
required AverageDefinitionsAllStats r = 3; required AverageDefinitionsAllStats r = 3;
required Samples samples = 4; required Samples samples = 4;
} }
message AverageDefinitionsAllStats { message AverageDefinitionsAllStats {
required float battles = 1; required float battles = 1;
required float capture_points = 2; required float capture_points = 2;
required float damage_dealt = 3; required float damage_dealt = 3;
required float damage_received = 4; required float damage_received = 4;
required float dropped_capture_points = 5; required float dropped_capture_points = 5;
required float frags = 6; required float frags = 6;
required float frags8p = 7; required float frags8p = 7;
required float hits = 8; required float hits = 8;
required float losses = 9; required float losses = 9;
required float max_frags = 10; required float max_frags = 10;
required float max_xp = 11; required float max_xp = 11;
required float shots = 12; required float shots = 12;
required float spotted = 13; required float spotted = 13;
required float survived_battles = 14; required float survived_battles = 14;
required float win_and_survived = 15; required float win_and_survived = 15;
required float wins = 16; required float wins = 16;
required float xp = 17; required float xp = 17;
required float battle_life_time = 18; required float battle_life_time = 18;
} }
message Samples { message Samples {
required uint32 d_1 = 1; required uint32 d_1 = 1;
required uint32 d_7 = 2; required uint32 d_7 = 2;
required uint32 d_30 = 3; required uint32 d_30 = 3;
required uint32 d_60 = 4; required uint32 d_60 = 4;
required uint32 d_90 = 5; required uint32 d_90 = 5;
required uint32 d_120 = 6; required uint32 d_120 = 6;
required uint32 total = 7; required uint32 total = 7;
} }
+127 -107
View File
@@ -1,8 +1,8 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT. // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions: // versions:
// protoc-gen-ts_proto v2.2.2 // protoc-gen-ts_proto v2.11.2
// protoc v5.28.2 // protoc v5.28.2
// source: packages/core/src/protos/average_definitions.proto // source: average_definitions.proto
/* eslint-disable */ /* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
@@ -11,7 +11,7 @@ export const protobufPackage = "blitzkit";
export interface AverageDefinitions { export interface AverageDefinitions {
averages: { [key: number]: AverageDefinitionsEntry }; averages: { [key: number]: AverageDefinitionsEntry };
samples: Samples; samples: Samples | undefined;
time: number; time: number;
} }
@@ -21,10 +21,10 @@ export interface AverageDefinitions_AveragesEntry {
} }
export interface AverageDefinitionsEntry { export interface AverageDefinitionsEntry {
mu: AverageDefinitionsAllStats; mu: AverageDefinitionsAllStats | undefined;
sigma: AverageDefinitionsAllStats; sigma: AverageDefinitionsAllStats | undefined;
r: AverageDefinitionsAllStats; r: AverageDefinitionsAllStats | undefined;
samples: Samples; samples: Samples | undefined;
} }
export interface AverageDefinitionsAllStats { export interface AverageDefinitionsAllStats {
@@ -58,13 +58,13 @@ export interface Samples {
total: number; total: number;
} }
export function createBaseAverageDefinitions(): AverageDefinitions { function createBaseAverageDefinitions(): AverageDefinitions {
return { averages: {}, samples: createBaseSamples(), time: 0 }; return { averages: {}, samples: undefined, time: 0 };
} }
export const AverageDefinitions: MessageFns<AverageDefinitions> = { export const AverageDefinitions: MessageFns<AverageDefinitions> = {
encode(message: AverageDefinitions, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: AverageDefinitions, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
Object.entries(message.averages).forEach(([key, value]) => { globalThis.Object.entries(message.averages).forEach(([key, value]: [string, AverageDefinitionsEntry]) => {
AverageDefinitions_AveragesEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join(); AverageDefinitions_AveragesEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join();
}); });
if (message.samples !== undefined) { if (message.samples !== undefined) {
@@ -78,7 +78,7 @@ export const AverageDefinitions: MessageFns<AverageDefinitions> = {
decode(input: BinaryReader | Uint8Array, length?: number): AverageDefinitions { decode(input: BinaryReader | Uint8Array, length?: number): AverageDefinitions {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseAverageDefinitions(); const message = createBaseAverageDefinitions();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -122,20 +122,23 @@ export const AverageDefinitions: MessageFns<AverageDefinitions> = {
fromJSON(object: any): AverageDefinitions { fromJSON(object: any): AverageDefinitions {
return { return {
averages: isObject(object.averages) averages: isObject(object.averages)
? Object.entries(object.averages).reduce<{ [key: number]: AverageDefinitionsEntry }>((acc, [key, value]) => { ? (globalThis.Object.entries(object.averages) as [string, any][]).reduce(
acc[globalThis.Number(key)] = AverageDefinitionsEntry.fromJSON(value); (acc: { [key: number]: AverageDefinitionsEntry }, [key, value]: [string, any]) => {
return acc; acc[globalThis.Number(key)] = AverageDefinitionsEntry.fromJSON(value);
}, {}) return acc;
},
{},
)
: {}, : {},
samples: Samples.fromJSON(assertSet("AverageDefinitions.samples", object.samples)), samples: isSet(object.samples) ? Samples.fromJSON(object.samples) : undefined,
time: globalThis.Number(assertSet("AverageDefinitions.time", object.time)), time: isSet(object.time) ? globalThis.Number(object.time) : 0,
}; };
}, },
toJSON(message: AverageDefinitions): unknown { toJSON(message: AverageDefinitions): unknown {
const obj: any = {}; const obj: any = {};
if (message.averages) { if (message.averages) {
const entries = Object.entries(message.averages); const entries = globalThis.Object.entries(message.averages) as [string, AverageDefinitionsEntry][];
if (entries.length > 0) { if (entries.length > 0) {
obj.averages = {}; obj.averages = {};
entries.forEach(([k, v]) => { entries.forEach(([k, v]) => {
@@ -157,8 +160,8 @@ export const AverageDefinitions: MessageFns<AverageDefinitions> = {
}, },
fromPartial<I extends Exact<DeepPartial<AverageDefinitions>, I>>(object: I): AverageDefinitions { fromPartial<I extends Exact<DeepPartial<AverageDefinitions>, I>>(object: I): AverageDefinitions {
const message = createBaseAverageDefinitions(); const message = createBaseAverageDefinitions();
message.averages = Object.entries(object.averages ?? {}).reduce<{ [key: number]: AverageDefinitionsEntry }>( message.averages = (globalThis.Object.entries(object.averages ?? {}) as [string, AverageDefinitionsEntry][]).reduce(
(acc, [key, value]) => { (acc: { [key: number]: AverageDefinitionsEntry }, [key, value]: [string, AverageDefinitionsEntry]) => {
if (value !== undefined) { if (value !== undefined) {
acc[globalThis.Number(key)] = AverageDefinitionsEntry.fromPartial(value); acc[globalThis.Number(key)] = AverageDefinitionsEntry.fromPartial(value);
} }
@@ -168,19 +171,19 @@ export const AverageDefinitions: MessageFns<AverageDefinitions> = {
); );
message.samples = (object.samples !== undefined && object.samples !== null) message.samples = (object.samples !== undefined && object.samples !== null)
? Samples.fromPartial(object.samples) ? Samples.fromPartial(object.samples)
: createBaseSamples(); : undefined;
message.time = object.time ?? 0; message.time = object.time ?? 0;
return message; return message;
}, },
}; };
export function createBaseAverageDefinitions_AveragesEntry(): AverageDefinitions_AveragesEntry { function createBaseAverageDefinitions_AveragesEntry(): AverageDefinitions_AveragesEntry {
return { key: 0, value: createBaseAverageDefinitionsEntry() }; return { key: 0, value: undefined };
} }
export const AverageDefinitions_AveragesEntry: MessageFns<AverageDefinitions_AveragesEntry> = { export const AverageDefinitions_AveragesEntry: MessageFns<AverageDefinitions_AveragesEntry> = {
encode(message: AverageDefinitions_AveragesEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: AverageDefinitions_AveragesEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.key !== undefined) { if (message.key !== 0) {
writer.uint32(8).uint32(message.key); writer.uint32(8).uint32(message.key);
} }
if (message.value !== undefined) { if (message.value !== undefined) {
@@ -191,7 +194,7 @@ export const AverageDefinitions_AveragesEntry: MessageFns<AverageDefinitions_Ave
decode(input: BinaryReader | Uint8Array, length?: number): AverageDefinitions_AveragesEntry { decode(input: BinaryReader | Uint8Array, length?: number): AverageDefinitions_AveragesEntry {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseAverageDefinitions_AveragesEntry(); const message = createBaseAverageDefinitions_AveragesEntry();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -223,14 +226,14 @@ export const AverageDefinitions_AveragesEntry: MessageFns<AverageDefinitions_Ave
fromJSON(object: any): AverageDefinitions_AveragesEntry { fromJSON(object: any): AverageDefinitions_AveragesEntry {
return { return {
key: globalThis.Number(assertSet("AverageDefinitions_AveragesEntry.key", object.key)), key: isSet(object.key) ? globalThis.Number(object.key) : 0,
value: AverageDefinitionsEntry.fromJSON(assertSet("AverageDefinitions_AveragesEntry.value", object.value)), value: isSet(object.value) ? AverageDefinitionsEntry.fromJSON(object.value) : undefined,
}; };
}, },
toJSON(message: AverageDefinitions_AveragesEntry): unknown { toJSON(message: AverageDefinitions_AveragesEntry): unknown {
const obj: any = {}; const obj: any = {};
if (message.key !== undefined) { if (message.key !== 0) {
obj.key = Math.round(message.key); obj.key = Math.round(message.key);
} }
if (message.value !== undefined) { if (message.value !== undefined) {
@@ -251,18 +254,13 @@ export const AverageDefinitions_AveragesEntry: MessageFns<AverageDefinitions_Ave
message.key = object.key ?? 0; message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null) message.value = (object.value !== undefined && object.value !== null)
? AverageDefinitionsEntry.fromPartial(object.value) ? AverageDefinitionsEntry.fromPartial(object.value)
: createBaseAverageDefinitionsEntry(); : undefined;
return message; return message;
}, },
}; };
export function createBaseAverageDefinitionsEntry(): AverageDefinitionsEntry { function createBaseAverageDefinitionsEntry(): AverageDefinitionsEntry {
return { return { mu: undefined, sigma: undefined, r: undefined, samples: undefined };
mu: createBaseAverageDefinitionsAllStats(),
sigma: createBaseAverageDefinitionsAllStats(),
r: createBaseAverageDefinitionsAllStats(),
samples: createBaseSamples(),
};
} }
export const AverageDefinitionsEntry: MessageFns<AverageDefinitionsEntry> = { export const AverageDefinitionsEntry: MessageFns<AverageDefinitionsEntry> = {
@@ -284,7 +282,7 @@ export const AverageDefinitionsEntry: MessageFns<AverageDefinitionsEntry> = {
decode(input: BinaryReader | Uint8Array, length?: number): AverageDefinitionsEntry { decode(input: BinaryReader | Uint8Array, length?: number): AverageDefinitionsEntry {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseAverageDefinitionsEntry(); const message = createBaseAverageDefinitionsEntry();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -332,10 +330,10 @@ export const AverageDefinitionsEntry: MessageFns<AverageDefinitionsEntry> = {
fromJSON(object: any): AverageDefinitionsEntry { fromJSON(object: any): AverageDefinitionsEntry {
return { return {
mu: AverageDefinitionsAllStats.fromJSON(assertSet("AverageDefinitionsEntry.mu", object.mu)), mu: isSet(object.mu) ? AverageDefinitionsAllStats.fromJSON(object.mu) : undefined,
sigma: AverageDefinitionsAllStats.fromJSON(assertSet("AverageDefinitionsEntry.sigma", object.sigma)), sigma: isSet(object.sigma) ? AverageDefinitionsAllStats.fromJSON(object.sigma) : undefined,
r: AverageDefinitionsAllStats.fromJSON(assertSet("AverageDefinitionsEntry.r", object.r)), r: isSet(object.r) ? AverageDefinitionsAllStats.fromJSON(object.r) : undefined,
samples: Samples.fromJSON(assertSet("AverageDefinitionsEntry.samples", object.samples)), samples: isSet(object.samples) ? Samples.fromJSON(object.samples) : undefined,
}; };
}, },
@@ -363,21 +361,21 @@ export const AverageDefinitionsEntry: MessageFns<AverageDefinitionsEntry> = {
const message = createBaseAverageDefinitionsEntry(); const message = createBaseAverageDefinitionsEntry();
message.mu = (object.mu !== undefined && object.mu !== null) message.mu = (object.mu !== undefined && object.mu !== null)
? AverageDefinitionsAllStats.fromPartial(object.mu) ? AverageDefinitionsAllStats.fromPartial(object.mu)
: createBaseAverageDefinitionsAllStats(); : undefined;
message.sigma = (object.sigma !== undefined && object.sigma !== null) message.sigma = (object.sigma !== undefined && object.sigma !== null)
? AverageDefinitionsAllStats.fromPartial(object.sigma) ? AverageDefinitionsAllStats.fromPartial(object.sigma)
: createBaseAverageDefinitionsAllStats(); : undefined;
message.r = (object.r !== undefined && object.r !== null) message.r = (object.r !== undefined && object.r !== null)
? AverageDefinitionsAllStats.fromPartial(object.r) ? AverageDefinitionsAllStats.fromPartial(object.r)
: createBaseAverageDefinitionsAllStats(); : undefined;
message.samples = (object.samples !== undefined && object.samples !== null) message.samples = (object.samples !== undefined && object.samples !== null)
? Samples.fromPartial(object.samples) ? Samples.fromPartial(object.samples)
: createBaseSamples(); : undefined;
return message; return message;
}, },
}; };
export function createBaseAverageDefinitionsAllStats(): AverageDefinitionsAllStats { function createBaseAverageDefinitionsAllStats(): AverageDefinitionsAllStats {
return { return {
battles: 0, battles: 0,
capture_points: 0, capture_points: 0,
@@ -461,7 +459,7 @@ export const AverageDefinitionsAllStats: MessageFns<AverageDefinitionsAllStats>
decode(input: BinaryReader | Uint8Array, length?: number): AverageDefinitionsAllStats { decode(input: BinaryReader | Uint8Array, length?: number): AverageDefinitionsAllStats {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseAverageDefinitionsAllStats(); const message = createBaseAverageDefinitionsAllStats();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -621,34 +619,60 @@ export const AverageDefinitionsAllStats: MessageFns<AverageDefinitionsAllStats>
fromJSON(object: any): AverageDefinitionsAllStats { fromJSON(object: any): AverageDefinitionsAllStats {
return { return {
battles: globalThis.Number(assertSet("AverageDefinitionsAllStats.battles", object.battles)), battles: isSet(object.battles) ? globalThis.Number(object.battles) : 0,
capture_points: globalThis.Number(assertSet("AverageDefinitionsAllStats.capture_points", object.capture_points)), capture_points: isSet(object.capturePoints)
damage_dealt: globalThis.Number(assertSet("AverageDefinitionsAllStats.damage_dealt", object.damage_dealt)), ? globalThis.Number(object.capturePoints)
damage_received: globalThis.Number( : isSet(object.capture_points)
assertSet("AverageDefinitionsAllStats.damage_received", object.damage_received), ? globalThis.Number(object.capture_points)
), : 0,
dropped_capture_points: globalThis.Number( damage_dealt: isSet(object.damageDealt)
assertSet("AverageDefinitionsAllStats.dropped_capture_points", object.dropped_capture_points), ? globalThis.Number(object.damageDealt)
), : isSet(object.damage_dealt)
frags: globalThis.Number(assertSet("AverageDefinitionsAllStats.frags", object.frags)), ? globalThis.Number(object.damage_dealt)
frags8p: globalThis.Number(assertSet("AverageDefinitionsAllStats.frags8p", object.frags8p)), : 0,
hits: globalThis.Number(assertSet("AverageDefinitionsAllStats.hits", object.hits)), damage_received: isSet(object.damageReceived)
losses: globalThis.Number(assertSet("AverageDefinitionsAllStats.losses", object.losses)), ? globalThis.Number(object.damageReceived)
max_frags: globalThis.Number(assertSet("AverageDefinitionsAllStats.max_frags", object.max_frags)), : isSet(object.damage_received)
max_xp: globalThis.Number(assertSet("AverageDefinitionsAllStats.max_xp", object.max_xp)), ? globalThis.Number(object.damage_received)
shots: globalThis.Number(assertSet("AverageDefinitionsAllStats.shots", object.shots)), : 0,
spotted: globalThis.Number(assertSet("AverageDefinitionsAllStats.spotted", object.spotted)), dropped_capture_points: isSet(object.droppedCapturePoints)
survived_battles: globalThis.Number( ? globalThis.Number(object.droppedCapturePoints)
assertSet("AverageDefinitionsAllStats.survived_battles", object.survived_battles), : isSet(object.dropped_capture_points)
), ? globalThis.Number(object.dropped_capture_points)
win_and_survived: globalThis.Number( : 0,
assertSet("AverageDefinitionsAllStats.win_and_survived", object.win_and_survived), frags: isSet(object.frags) ? globalThis.Number(object.frags) : 0,
), frags8p: isSet(object.frags8p) ? globalThis.Number(object.frags8p) : 0,
wins: globalThis.Number(assertSet("AverageDefinitionsAllStats.wins", object.wins)), hits: isSet(object.hits) ? globalThis.Number(object.hits) : 0,
xp: globalThis.Number(assertSet("AverageDefinitionsAllStats.xp", object.xp)), losses: isSet(object.losses) ? globalThis.Number(object.losses) : 0,
battle_life_time: globalThis.Number( max_frags: isSet(object.maxFrags)
assertSet("AverageDefinitionsAllStats.battle_life_time", object.battle_life_time), ? globalThis.Number(object.maxFrags)
), : isSet(object.max_frags)
? globalThis.Number(object.max_frags)
: 0,
max_xp: isSet(object.maxXp)
? globalThis.Number(object.maxXp)
: isSet(object.max_xp)
? globalThis.Number(object.max_xp)
: 0,
shots: isSet(object.shots) ? globalThis.Number(object.shots) : 0,
spotted: isSet(object.spotted) ? globalThis.Number(object.spotted) : 0,
survived_battles: isSet(object.survivedBattles)
? globalThis.Number(object.survivedBattles)
: isSet(object.survived_battles)
? globalThis.Number(object.survived_battles)
: 0,
win_and_survived: isSet(object.winAndSurvived)
? globalThis.Number(object.winAndSurvived)
: isSet(object.win_and_survived)
? globalThis.Number(object.win_and_survived)
: 0,
wins: isSet(object.wins) ? globalThis.Number(object.wins) : 0,
xp: isSet(object.xp) ? globalThis.Number(object.xp) : 0,
battle_life_time: isSet(object.battleLifeTime)
? globalThis.Number(object.battleLifeTime)
: isSet(object.battle_life_time)
? globalThis.Number(object.battle_life_time)
: 0,
}; };
}, },
@@ -658,16 +682,16 @@ export const AverageDefinitionsAllStats: MessageFns<AverageDefinitionsAllStats>
obj.battles = message.battles; obj.battles = message.battles;
} }
if (message.capture_points !== 0) { if (message.capture_points !== 0) {
obj.capture_points = message.capture_points; obj.capturePoints = message.capture_points;
} }
if (message.damage_dealt !== 0) { if (message.damage_dealt !== 0) {
obj.damage_dealt = message.damage_dealt; obj.damageDealt = message.damage_dealt;
} }
if (message.damage_received !== 0) { if (message.damage_received !== 0) {
obj.damage_received = message.damage_received; obj.damageReceived = message.damage_received;
} }
if (message.dropped_capture_points !== 0) { if (message.dropped_capture_points !== 0) {
obj.dropped_capture_points = message.dropped_capture_points; obj.droppedCapturePoints = message.dropped_capture_points;
} }
if (message.frags !== 0) { if (message.frags !== 0) {
obj.frags = message.frags; obj.frags = message.frags;
@@ -682,10 +706,10 @@ export const AverageDefinitionsAllStats: MessageFns<AverageDefinitionsAllStats>
obj.losses = message.losses; obj.losses = message.losses;
} }
if (message.max_frags !== 0) { if (message.max_frags !== 0) {
obj.max_frags = message.max_frags; obj.maxFrags = message.max_frags;
} }
if (message.max_xp !== 0) { if (message.max_xp !== 0) {
obj.max_xp = message.max_xp; obj.maxXp = message.max_xp;
} }
if (message.shots !== 0) { if (message.shots !== 0) {
obj.shots = message.shots; obj.shots = message.shots;
@@ -694,10 +718,10 @@ export const AverageDefinitionsAllStats: MessageFns<AverageDefinitionsAllStats>
obj.spotted = message.spotted; obj.spotted = message.spotted;
} }
if (message.survived_battles !== 0) { if (message.survived_battles !== 0) {
obj.survived_battles = message.survived_battles; obj.survivedBattles = message.survived_battles;
} }
if (message.win_and_survived !== 0) { if (message.win_and_survived !== 0) {
obj.win_and_survived = message.win_and_survived; obj.winAndSurvived = message.win_and_survived;
} }
if (message.wins !== 0) { if (message.wins !== 0) {
obj.wins = message.wins; obj.wins = message.wins;
@@ -706,7 +730,7 @@ export const AverageDefinitionsAllStats: MessageFns<AverageDefinitionsAllStats>
obj.xp = message.xp; obj.xp = message.xp;
} }
if (message.battle_life_time !== 0) { if (message.battle_life_time !== 0) {
obj.battle_life_time = message.battle_life_time; obj.battleLifeTime = message.battle_life_time;
} }
return obj; return obj;
}, },
@@ -738,7 +762,7 @@ export const AverageDefinitionsAllStats: MessageFns<AverageDefinitionsAllStats>
}, },
}; };
export function createBaseSamples(): Samples { function createBaseSamples(): Samples {
return { d_1: 0, d_7: 0, d_30: 0, d_60: 0, d_90: 0, d_120: 0, total: 0 }; return { d_1: 0, d_7: 0, d_30: 0, d_60: 0, d_90: 0, d_120: 0, total: 0 };
} }
@@ -770,7 +794,7 @@ export const Samples: MessageFns<Samples> = {
decode(input: BinaryReader | Uint8Array, length?: number): Samples { decode(input: BinaryReader | Uint8Array, length?: number): Samples {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseSamples(); const message = createBaseSamples();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -842,35 +866,39 @@ export const Samples: MessageFns<Samples> = {
fromJSON(object: any): Samples { fromJSON(object: any): Samples {
return { return {
d_1: globalThis.Number(assertSet("Samples.d_1", object.d_1)), d_1: isSet(object.d1) ? globalThis.Number(object.d1) : isSet(object.d_1) ? globalThis.Number(object.d_1) : 0,
d_7: globalThis.Number(assertSet("Samples.d_7", object.d_7)), d_7: isSet(object.d7) ? globalThis.Number(object.d7) : isSet(object.d_7) ? globalThis.Number(object.d_7) : 0,
d_30: globalThis.Number(assertSet("Samples.d_30", object.d_30)), d_30: isSet(object.d30) ? globalThis.Number(object.d30) : isSet(object.d_30) ? globalThis.Number(object.d_30) : 0,
d_60: globalThis.Number(assertSet("Samples.d_60", object.d_60)), d_60: isSet(object.d60) ? globalThis.Number(object.d60) : isSet(object.d_60) ? globalThis.Number(object.d_60) : 0,
d_90: globalThis.Number(assertSet("Samples.d_90", object.d_90)), d_90: isSet(object.d90) ? globalThis.Number(object.d90) : isSet(object.d_90) ? globalThis.Number(object.d_90) : 0,
d_120: globalThis.Number(assertSet("Samples.d_120", object.d_120)), d_120: isSet(object.d120)
total: globalThis.Number(assertSet("Samples.total", object.total)), ? globalThis.Number(object.d120)
: isSet(object.d_120)
? globalThis.Number(object.d_120)
: 0,
total: isSet(object.total) ? globalThis.Number(object.total) : 0,
}; };
}, },
toJSON(message: Samples): unknown { toJSON(message: Samples): unknown {
const obj: any = {}; const obj: any = {};
if (message.d_1 !== 0) { if (message.d_1 !== 0) {
obj.d_1 = Math.round(message.d_1); obj.d1 = Math.round(message.d_1);
} }
if (message.d_7 !== 0) { if (message.d_7 !== 0) {
obj.d_7 = Math.round(message.d_7); obj.d7 = Math.round(message.d_7);
} }
if (message.d_30 !== 0) { if (message.d_30 !== 0) {
obj.d_30 = Math.round(message.d_30); obj.d30 = Math.round(message.d_30);
} }
if (message.d_60 !== 0) { if (message.d_60 !== 0) {
obj.d_60 = Math.round(message.d_60); obj.d60 = Math.round(message.d_60);
} }
if (message.d_90 !== 0) { if (message.d_90 !== 0) {
obj.d_90 = Math.round(message.d_90); obj.d90 = Math.round(message.d_90);
} }
if (message.d_120 !== 0) { if (message.d_120 !== 0) {
obj.d_120 = Math.round(message.d_120); obj.d120 = Math.round(message.d_120);
} }
if (message.total !== 0) { if (message.total !== 0) {
obj.total = Math.round(message.total); obj.total = Math.round(message.total);
@@ -926,14 +954,6 @@ function isSet(value: any): boolean {
return value !== null && value !== undefined; return value !== null && value !== undefined;
} }
function assertSet<T>(field: string, value: T | undefined): T {
if (!isSet(value)) {
throw new TypeError(`Required field ${field} is not set`);
}
return value as T;
}
export interface MessageFns<T> { export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter; encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T; decode(input: BinaryReader | Uint8Array, length?: number): T;
@@ -2,15 +2,15 @@ syntax = "proto2";
package blitzkit; package blitzkit;
import "packages/core/src/protos/i18n.proto"; import "i18n.proto";
message CamouflageDefinitions { message CamouflageDefinitions {
map<uint32, Camouflage> camouflages = 1; map<uint32, Camouflage> camouflages = 1;
} }
message Camouflage { message Camouflage {
required uint32 id = 1; required uint32 id = 1;
required I18nString name = 2; required I18nString name = 2;
optional I18nString tank_name = 3; optional I18nString tank_name = 3;
optional I18nString tank_name_full = 4; optional I18nString tank_name_full = 4;
} }
+140 -209
View File
@@ -1,14 +1,14 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT. // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions: // versions:
// protoc-gen-ts_proto v2.2.2 // protoc-gen-ts_proto v2.11.2
// protoc v5.28.2 // protoc v5.28.2
// source: packages/core/src/protos/camouflage_definitions.proto // source: camouflage_definitions.proto
/* eslint-disable */ /* eslint-disable */
import { BinaryReader, BinaryWriter } from '@bufbuild/protobuf/wire'; import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { createBaseI18nString, I18nString } from './i18n'; import { I18nString } from "./i18n";
export const protobufPackage = 'blitzkit'; export const protobufPackage = "blitzkit";
export interface CamouflageDefinitions { export interface CamouflageDefinitions {
camouflages: { [key: number]: Camouflage }; camouflages: { [key: number]: Camouflage };
@@ -21,36 +21,26 @@ export interface CamouflageDefinitions_CamouflagesEntry {
export interface Camouflage { export interface Camouflage {
id: number; id: number;
name: I18nString; name: I18nString | undefined;
tank_name?: I18nString; tank_name?: I18nString | undefined;
tank_name_full?: I18nString; tank_name_full?: I18nString | undefined;
} }
export function createBaseCamouflageDefinitions(): CamouflageDefinitions { function createBaseCamouflageDefinitions(): CamouflageDefinitions {
return { camouflages: {} }; return { camouflages: {} };
} }
export const CamouflageDefinitions: MessageFns<CamouflageDefinitions> = { export const CamouflageDefinitions: MessageFns<CamouflageDefinitions> = {
encode( encode(message: CamouflageDefinitions, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
message: CamouflageDefinitions, globalThis.Object.entries(message.camouflages).forEach(([key, value]: [string, Camouflage]) => {
writer: BinaryWriter = new BinaryWriter(), CamouflageDefinitions_CamouflagesEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join();
): BinaryWriter {
Object.entries(message.camouflages).forEach(([key, value]) => {
CamouflageDefinitions_CamouflagesEntry.encode(
{ key: key as any, value },
writer.uint32(10).fork(),
).join();
}); });
return writer; return writer;
}, },
decode( decode(input: BinaryReader | Uint8Array, length?: number): CamouflageDefinitions {
input: BinaryReader | Uint8Array, const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
length?: number, const end = length === undefined ? reader.len : reader.pos + length;
): CamouflageDefinitions {
const reader =
input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseCamouflageDefinitions(); const message = createBaseCamouflageDefinitions();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -60,10 +50,7 @@ export const CamouflageDefinitions: MessageFns<CamouflageDefinitions> = {
break; break;
} }
const entry1 = CamouflageDefinitions_CamouflagesEntry.decode( const entry1 = CamouflageDefinitions_CamouflagesEntry.decode(reader, reader.uint32());
reader,
reader.uint32(),
);
if (entry1.value !== undefined) { if (entry1.value !== undefined) {
message.camouflages[entry1.key] = entry1.value; message.camouflages[entry1.key] = entry1.value;
} }
@@ -81,12 +68,13 @@ export const CamouflageDefinitions: MessageFns<CamouflageDefinitions> = {
fromJSON(object: any): CamouflageDefinitions { fromJSON(object: any): CamouflageDefinitions {
return { return {
camouflages: isObject(object.camouflages) camouflages: isObject(object.camouflages)
? Object.entries(object.camouflages).reduce<{ ? (globalThis.Object.entries(object.camouflages) as [string, any][]).reduce(
[key: number]: Camouflage; (acc: { [key: number]: Camouflage }, [key, value]: [string, any]) => {
}>((acc, [key, value]) => {
acc[globalThis.Number(key)] = Camouflage.fromJSON(value); acc[globalThis.Number(key)] = Camouflage.fromJSON(value);
return acc; return acc;
}, {}) },
{},
)
: {}, : {},
}; };
}, },
@@ -94,7 +82,7 @@ export const CamouflageDefinitions: MessageFns<CamouflageDefinitions> = {
toJSON(message: CamouflageDefinitions): unknown { toJSON(message: CamouflageDefinitions): unknown {
const obj: any = {}; const obj: any = {};
if (message.camouflages) { if (message.camouflages) {
const entries = Object.entries(message.camouflages); const entries = globalThis.Object.entries(message.camouflages) as [string, Camouflage][];
if (entries.length > 0) { if (entries.length > 0) {
obj.camouflages = {}; obj.camouflages = {};
entries.forEach(([k, v]) => { entries.forEach(([k, v]) => {
@@ -105,141 +93,112 @@ export const CamouflageDefinitions: MessageFns<CamouflageDefinitions> = {
return obj; return obj;
}, },
create<I extends Exact<DeepPartial<CamouflageDefinitions>, I>>( create<I extends Exact<DeepPartial<CamouflageDefinitions>, I>>(base?: I): CamouflageDefinitions {
base?: I,
): CamouflageDefinitions {
return CamouflageDefinitions.fromPartial(base ?? ({} as any)); return CamouflageDefinitions.fromPartial(base ?? ({} as any));
}, },
fromPartial<I extends Exact<DeepPartial<CamouflageDefinitions>, I>>( fromPartial<I extends Exact<DeepPartial<CamouflageDefinitions>, I>>(object: I): CamouflageDefinitions {
object: I,
): CamouflageDefinitions {
const message = createBaseCamouflageDefinitions(); const message = createBaseCamouflageDefinitions();
message.camouflages = Object.entries(object.camouflages ?? {}).reduce<{ message.camouflages = (globalThis.Object.entries(object.camouflages ?? {}) as [string, Camouflage][]).reduce(
[key: number]: Camouflage; (acc: { [key: number]: Camouflage }, [key, value]: [string, Camouflage]) => {
}>((acc, [key, value]) => { if (value !== undefined) {
if (value !== undefined) { acc[globalThis.Number(key)] = Camouflage.fromPartial(value);
acc[globalThis.Number(key)] = Camouflage.fromPartial(value); }
} return acc;
return acc; },
}, {}); {},
);
return message; return message;
}, },
}; };
export function createBaseCamouflageDefinitions_CamouflagesEntry(): CamouflageDefinitions_CamouflagesEntry { function createBaseCamouflageDefinitions_CamouflagesEntry(): CamouflageDefinitions_CamouflagesEntry {
return { key: 0, value: createBaseCamouflage() }; return { key: 0, value: undefined };
} }
export const CamouflageDefinitions_CamouflagesEntry: MessageFns<CamouflageDefinitions_CamouflagesEntry> = export const CamouflageDefinitions_CamouflagesEntry: MessageFns<CamouflageDefinitions_CamouflagesEntry> = {
{ encode(message: CamouflageDefinitions_CamouflagesEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
encode( if (message.key !== 0) {
message: CamouflageDefinitions_CamouflagesEntry, writer.uint32(8).uint32(message.key);
writer: BinaryWriter = new BinaryWriter(), }
): BinaryWriter { if (message.value !== undefined) {
if (message.key !== undefined) { Camouflage.encode(message.value, writer.uint32(18).fork()).join();
writer.uint32(8).uint32(message.key); }
} return writer;
if (message.value !== undefined) { },
Camouflage.encode(message.value, writer.uint32(18).fork()).join();
}
return writer;
},
decode( decode(input: BinaryReader | Uint8Array, length?: number): CamouflageDefinitions_CamouflagesEntry {
input: BinaryReader | Uint8Array, const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
length?: number, const end = length === undefined ? reader.len : reader.pos + length;
): CamouflageDefinitions_CamouflagesEntry { const message = createBaseCamouflageDefinitions_CamouflagesEntry();
const reader = while (reader.pos < end) {
input instanceof BinaryReader ? input : new BinaryReader(input); const tag = reader.uint32();
let end = length === undefined ? reader.len : reader.pos + length; switch (tag >>> 3) {
const message = createBaseCamouflageDefinitions_CamouflagesEntry(); case 1: {
while (reader.pos < end) { if (tag !== 8) {
const tag = reader.uint32(); break;
switch (tag >>> 3) {
case 1: {
if (tag !== 8) {
break;
}
message.key = reader.uint32();
continue;
} }
case 2: {
if (tag !== 18) {
break;
}
message.value = Camouflage.decode(reader, reader.uint32()); message.key = reader.uint32();
continue; continue;
}
case 2: {
if (tag !== 18) {
break;
} }
message.value = Camouflage.decode(reader, reader.uint32());
continue;
} }
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
} }
return message; if ((tag & 7) === 4 || tag === 0) {
}, break;
fromJSON(object: any): CamouflageDefinitions_CamouflagesEntry {
return {
key: globalThis.Number(
assertSet('CamouflageDefinitions_CamouflagesEntry.key', object.key),
),
value: Camouflage.fromJSON(
assertSet(
'CamouflageDefinitions_CamouflagesEntry.value',
object.value,
),
),
};
},
toJSON(message: CamouflageDefinitions_CamouflagesEntry): unknown {
const obj: any = {};
if (message.key !== undefined) {
obj.key = Math.round(message.key);
} }
if (message.value !== undefined) { reader.skip(tag & 7);
obj.value = Camouflage.toJSON(message.value); }
} return message;
return obj; },
},
create< fromJSON(object: any): CamouflageDefinitions_CamouflagesEntry {
I extends Exact<DeepPartial<CamouflageDefinitions_CamouflagesEntry>, I>, return {
>(base?: I): CamouflageDefinitions_CamouflagesEntry { key: isSet(object.key) ? globalThis.Number(object.key) : 0,
return CamouflageDefinitions_CamouflagesEntry.fromPartial( value: isSet(object.value) ? Camouflage.fromJSON(object.value) : undefined,
base ?? ({} as any), };
); },
},
fromPartial<
I extends Exact<DeepPartial<CamouflageDefinitions_CamouflagesEntry>, I>,
>(object: I): CamouflageDefinitions_CamouflagesEntry {
const message = createBaseCamouflageDefinitions_CamouflagesEntry();
message.key = object.key ?? 0;
message.value =
object.value !== undefined && object.value !== null
? Camouflage.fromPartial(object.value)
: createBaseCamouflage();
return message;
},
};
export function createBaseCamouflage(): Camouflage { toJSON(message: CamouflageDefinitions_CamouflagesEntry): unknown {
return { const obj: any = {};
id: 0, if (message.key !== 0) {
name: createBaseI18nString(), obj.key = Math.round(message.key);
tank_name: undefined, }
tank_name_full: undefined, if (message.value !== undefined) {
}; obj.value = Camouflage.toJSON(message.value);
}
return obj;
},
create<I extends Exact<DeepPartial<CamouflageDefinitions_CamouflagesEntry>, I>>(
base?: I,
): CamouflageDefinitions_CamouflagesEntry {
return CamouflageDefinitions_CamouflagesEntry.fromPartial(base ?? ({} as any));
},
fromPartial<I extends Exact<DeepPartial<CamouflageDefinitions_CamouflagesEntry>, I>>(
object: I,
): CamouflageDefinitions_CamouflagesEntry {
const message = createBaseCamouflageDefinitions_CamouflagesEntry();
message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null)
? Camouflage.fromPartial(object.value)
: undefined;
return message;
},
};
function createBaseCamouflage(): Camouflage {
return { id: 0, name: undefined, tank_name: undefined, tank_name_full: undefined };
} }
export const Camouflage: MessageFns<Camouflage> = { export const Camouflage: MessageFns<Camouflage> = {
encode( encode(message: Camouflage, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
message: Camouflage,
writer: BinaryWriter = new BinaryWriter(),
): BinaryWriter {
if (message.id !== 0) { if (message.id !== 0) {
writer.uint32(8).uint32(message.id); writer.uint32(8).uint32(message.id);
} }
@@ -250,18 +209,14 @@ export const Camouflage: MessageFns<Camouflage> = {
I18nString.encode(message.tank_name, writer.uint32(26).fork()).join(); I18nString.encode(message.tank_name, writer.uint32(26).fork()).join();
} }
if (message.tank_name_full !== undefined) { if (message.tank_name_full !== undefined) {
I18nString.encode( I18nString.encode(message.tank_name_full, writer.uint32(34).fork()).join();
message.tank_name_full,
writer.uint32(34).fork(),
).join();
} }
return writer; return writer;
}, },
decode(input: BinaryReader | Uint8Array, length?: number): Camouflage { decode(input: BinaryReader | Uint8Array, length?: number): Camouflage {
const reader = const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
input instanceof BinaryReader ? input : new BinaryReader(input); const end = length === undefined ? reader.len : reader.pos + length;
let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseCamouflage(); const message = createBaseCamouflage();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -309,12 +264,16 @@ export const Camouflage: MessageFns<Camouflage> = {
fromJSON(object: any): Camouflage { fromJSON(object: any): Camouflage {
return { return {
id: globalThis.Number(assertSet('Camouflage.id', object.id)), id: isSet(object.id) ? globalThis.Number(object.id) : 0,
name: I18nString.fromJSON(assertSet('Camouflage.name', object.name)), name: isSet(object.name) ? I18nString.fromJSON(object.name) : undefined,
tank_name: isSet(object.tank_name) tank_name: isSet(object.tankName)
? I18nString.fromJSON(object.tankName)
: isSet(object.tank_name)
? I18nString.fromJSON(object.tank_name) ? I18nString.fromJSON(object.tank_name)
: undefined, : undefined,
tank_name_full: isSet(object.tank_name_full) tank_name_full: isSet(object.tankNameFull)
? I18nString.fromJSON(object.tankNameFull)
: isSet(object.tank_name_full)
? I18nString.fromJSON(object.tank_name_full) ? I18nString.fromJSON(object.tank_name_full)
: undefined, : undefined,
}; };
@@ -329,10 +288,10 @@ export const Camouflage: MessageFns<Camouflage> = {
obj.name = I18nString.toJSON(message.name); obj.name = I18nString.toJSON(message.name);
} }
if (message.tank_name !== undefined) { if (message.tank_name !== undefined) {
obj.tank_name = I18nString.toJSON(message.tank_name); obj.tankName = I18nString.toJSON(message.tank_name);
} }
if (message.tank_name_full !== undefined) { if (message.tank_name_full !== undefined) {
obj.tank_name_full = I18nString.toJSON(message.tank_name_full); obj.tankNameFull = I18nString.toJSON(message.tank_name_full);
} }
return obj; return obj;
}, },
@@ -340,71 +299,43 @@ export const Camouflage: MessageFns<Camouflage> = {
create<I extends Exact<DeepPartial<Camouflage>, I>>(base?: I): Camouflage { create<I extends Exact<DeepPartial<Camouflage>, I>>(base?: I): Camouflage {
return Camouflage.fromPartial(base ?? ({} as any)); return Camouflage.fromPartial(base ?? ({} as any));
}, },
fromPartial<I extends Exact<DeepPartial<Camouflage>, I>>( fromPartial<I extends Exact<DeepPartial<Camouflage>, I>>(object: I): Camouflage {
object: I,
): Camouflage {
const message = createBaseCamouflage(); const message = createBaseCamouflage();
message.id = object.id ?? 0; message.id = object.id ?? 0;
message.name = message.name = (object.name !== undefined && object.name !== null)
object.name !== undefined && object.name !== null ? I18nString.fromPartial(object.name)
? I18nString.fromPartial(object.name) : undefined;
: createBaseI18nString(); message.tank_name = (object.tank_name !== undefined && object.tank_name !== null)
message.tank_name = ? I18nString.fromPartial(object.tank_name)
object.tank_name !== undefined && object.tank_name !== null : undefined;
? I18nString.fromPartial(object.tank_name) message.tank_name_full = (object.tank_name_full !== undefined && object.tank_name_full !== null)
: undefined; ? I18nString.fromPartial(object.tank_name_full)
message.tank_name_full = : undefined;
object.tank_name_full !== undefined && object.tank_name_full !== null
? I18nString.fromPartial(object.tank_name_full)
: undefined;
return message; return message;
}, },
}; };
type Builtin = type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
| Date
| Function
| Uint8Array
| string
| number
| boolean
| undefined;
export type DeepPartial<T> = T extends Builtin export type DeepPartial<T> = T extends Builtin ? T
? T : T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>>
: T extends globalThis.Array<infer U> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>>
? globalThis.Array<DeepPartial<U>> : T extends { $case: string; value: unknown } ? { $case: T["$case"]; value?: DeepPartial<T["value"]> }
: T extends ReadonlyArray<infer U> : T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
? ReadonlyArray<DeepPartial<U>> : Partial<T>;
: T extends { $case: string; value: unknown }
? { $case: T['$case']; value?: DeepPartial<T['value']> }
: T extends {}
? { [K in keyof T]?: DeepPartial<T[K]> }
: Partial<T>;
type KeysOfUnion<T> = T extends T ? keyof T : never; type KeysOfUnion<T> = T extends T ? keyof T : never;
export type Exact<P, I extends P> = P extends Builtin export type Exact<P, I extends P> = P extends Builtin ? P
? P : P & { [K in keyof P]: Exact<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion<P>>]: never };
: P & { [K in keyof P]: Exact<P[K], I[K]> } & {
[K in Exclude<keyof I, KeysOfUnion<P>>]: never;
};
function isObject(value: any): boolean { function isObject(value: any): boolean {
return typeof value === 'object' && value !== null; return typeof value === "object" && value !== null;
} }
function isSet(value: any): boolean { function isSet(value: any): boolean {
return value !== null && value !== undefined; return value !== null && value !== undefined;
} }
function assertSet<T>(field: string, value: T | undefined): T {
if (!isSet(value)) {
throw new TypeError(`Required field ${field} is not set`);
}
return value as T;
}
export interface MessageFns<T> { export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter; encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T; decode(input: BinaryReader | Uint8Array, length?: number): T;
@@ -2,28 +2,28 @@ syntax = "proto2";
package blitzkit; package blitzkit;
import "packages/core/src/protos/i18n.proto"; import "i18n.proto";
message ConsumableDefinitions { message ConsumableDefinitions {
map<uint32, Consumable> consumables = 1; map<uint32, Consumable> consumables = 1;
} }
message Consumable { message Consumable {
required uint32 id = 1; required uint32 id = 1;
required I18nString name = 2; required I18nString name = 2;
required uint32 cooldown = 3; required uint32 cooldown = 3;
optional float duration = 4; optional float duration = 4;
required bool game_mode_exclusive = 5; required bool game_mode_exclusive = 5;
repeated ConsumableTankFilter include = 6; repeated ConsumableTankFilter include = 6;
repeated ConsumableTankFilter exclude = 7; repeated ConsumableTankFilter exclude = 7;
} }
message ConsumableTankFilter { message ConsumableTankFilter {
oneof filter_type { oneof filter_type {
ConsumableTankTierFilter tiers = 1; ConsumableTankTierFilter tiers = 1;
ConsumableTankIdsFilter ids = 2; ConsumableTankIdsFilter ids = 2;
ConsumableTankCategoryFilter categories = 3; ConsumableTankCategoryFilter categories = 3;
ConsumableTankNationFilter nations = 4; ConsumableTankNationFilter nations = 4;
} }
} }
@@ -1,24 +1,24 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT. // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions: // versions:
// protoc-gen-ts_proto v2.2.2 // protoc-gen-ts_proto v2.11.2
// protoc v5.28.2 // protoc v5.28.2
// source: packages/core/src/protos/consumable_definitions.proto // source: consumable_definitions.proto
/* eslint-disable */ /* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { createBaseI18nString, I18nString } from "./i18n"; import { I18nString } from "./i18n";
export const protobufPackage = "blitzkit"; export const protobufPackage = "blitzkit";
export enum ConsumableTankCategoryFilterCategory { export enum ConsumableTankCategoryFilterCategory {
CLIP = 0, CONSUMABLE_TANK_CATEGORY_FILTER_CATEGORY_CLIP = 0,
} }
export function consumableTankCategoryFilterCategoryFromJSON(object: any): ConsumableTankCategoryFilterCategory { export function consumableTankCategoryFilterCategoryFromJSON(object: any): ConsumableTankCategoryFilterCategory {
switch (object) { switch (object) {
case 0: case 0:
case "CONSUMABLE_TANK_CATEGORY_FILTER_CATEGORY_CLIP": case "CONSUMABLE_TANK_CATEGORY_FILTER_CATEGORY_CLIP":
return ConsumableTankCategoryFilterCategory.CLIP; return ConsumableTankCategoryFilterCategory.CONSUMABLE_TANK_CATEGORY_FILTER_CATEGORY_CLIP;
default: default:
throw new globalThis.Error( throw new globalThis.Error(
"Unrecognized enum value " + object + " for enum ConsumableTankCategoryFilterCategory", "Unrecognized enum value " + object + " for enum ConsumableTankCategoryFilterCategory",
@@ -28,7 +28,7 @@ export function consumableTankCategoryFilterCategoryFromJSON(object: any): Consu
export function consumableTankCategoryFilterCategoryToJSON(object: ConsumableTankCategoryFilterCategory): string { export function consumableTankCategoryFilterCategoryToJSON(object: ConsumableTankCategoryFilterCategory): string {
switch (object) { switch (object) {
case ConsumableTankCategoryFilterCategory.CLIP: case ConsumableTankCategoryFilterCategory.CONSUMABLE_TANK_CATEGORY_FILTER_CATEGORY_CLIP:
return "CONSUMABLE_TANK_CATEGORY_FILTER_CATEGORY_CLIP"; return "CONSUMABLE_TANK_CATEGORY_FILTER_CATEGORY_CLIP";
default: default:
throw new globalThis.Error( throw new globalThis.Error(
@@ -48,7 +48,7 @@ export interface ConsumableDefinitions_ConsumablesEntry {
export interface Consumable { export interface Consumable {
id: number; id: number;
name: I18nString; name: I18nString | undefined;
cooldown: number; cooldown: number;
duration?: number | undefined; duration?: number | undefined;
game_mode_exclusive: boolean; game_mode_exclusive: boolean;
@@ -82,13 +82,13 @@ export interface ConsumableTankNationFilter {
nations: string[]; nations: string[];
} }
export function createBaseConsumableDefinitions(): ConsumableDefinitions { function createBaseConsumableDefinitions(): ConsumableDefinitions {
return { consumables: {} }; return { consumables: {} };
} }
export const ConsumableDefinitions: MessageFns<ConsumableDefinitions> = { export const ConsumableDefinitions: MessageFns<ConsumableDefinitions> = {
encode(message: ConsumableDefinitions, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: ConsumableDefinitions, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
Object.entries(message.consumables).forEach(([key, value]) => { globalThis.Object.entries(message.consumables).forEach(([key, value]: [string, Consumable]) => {
ConsumableDefinitions_ConsumablesEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join(); ConsumableDefinitions_ConsumablesEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join();
}); });
return writer; return writer;
@@ -96,7 +96,7 @@ export const ConsumableDefinitions: MessageFns<ConsumableDefinitions> = {
decode(input: BinaryReader | Uint8Array, length?: number): ConsumableDefinitions { decode(input: BinaryReader | Uint8Array, length?: number): ConsumableDefinitions {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseConsumableDefinitions(); const message = createBaseConsumableDefinitions();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -124,10 +124,13 @@ export const ConsumableDefinitions: MessageFns<ConsumableDefinitions> = {
fromJSON(object: any): ConsumableDefinitions { fromJSON(object: any): ConsumableDefinitions {
return { return {
consumables: isObject(object.consumables) consumables: isObject(object.consumables)
? Object.entries(object.consumables).reduce<{ [key: number]: Consumable }>((acc, [key, value]) => { ? (globalThis.Object.entries(object.consumables) as [string, any][]).reduce(
acc[globalThis.Number(key)] = Consumable.fromJSON(value); (acc: { [key: number]: Consumable }, [key, value]: [string, any]) => {
return acc; acc[globalThis.Number(key)] = Consumable.fromJSON(value);
}, {}) return acc;
},
{},
)
: {}, : {},
}; };
}, },
@@ -135,7 +138,7 @@ export const ConsumableDefinitions: MessageFns<ConsumableDefinitions> = {
toJSON(message: ConsumableDefinitions): unknown { toJSON(message: ConsumableDefinitions): unknown {
const obj: any = {}; const obj: any = {};
if (message.consumables) { if (message.consumables) {
const entries = Object.entries(message.consumables); const entries = globalThis.Object.entries(message.consumables) as [string, Consumable][];
if (entries.length > 0) { if (entries.length > 0) {
obj.consumables = {}; obj.consumables = {};
entries.forEach(([k, v]) => { entries.forEach(([k, v]) => {
@@ -151,8 +154,8 @@ export const ConsumableDefinitions: MessageFns<ConsumableDefinitions> = {
}, },
fromPartial<I extends Exact<DeepPartial<ConsumableDefinitions>, I>>(object: I): ConsumableDefinitions { fromPartial<I extends Exact<DeepPartial<ConsumableDefinitions>, I>>(object: I): ConsumableDefinitions {
const message = createBaseConsumableDefinitions(); const message = createBaseConsumableDefinitions();
message.consumables = Object.entries(object.consumables ?? {}).reduce<{ [key: number]: Consumable }>( message.consumables = (globalThis.Object.entries(object.consumables ?? {}) as [string, Consumable][]).reduce(
(acc, [key, value]) => { (acc: { [key: number]: Consumable }, [key, value]: [string, Consumable]) => {
if (value !== undefined) { if (value !== undefined) {
acc[globalThis.Number(key)] = Consumable.fromPartial(value); acc[globalThis.Number(key)] = Consumable.fromPartial(value);
} }
@@ -164,13 +167,13 @@ export const ConsumableDefinitions: MessageFns<ConsumableDefinitions> = {
}, },
}; };
export function createBaseConsumableDefinitions_ConsumablesEntry(): ConsumableDefinitions_ConsumablesEntry { function createBaseConsumableDefinitions_ConsumablesEntry(): ConsumableDefinitions_ConsumablesEntry {
return { key: 0, value: createBaseConsumable() }; return { key: 0, value: undefined };
} }
export const ConsumableDefinitions_ConsumablesEntry: MessageFns<ConsumableDefinitions_ConsumablesEntry> = { export const ConsumableDefinitions_ConsumablesEntry: MessageFns<ConsumableDefinitions_ConsumablesEntry> = {
encode(message: ConsumableDefinitions_ConsumablesEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: ConsumableDefinitions_ConsumablesEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.key !== undefined) { if (message.key !== 0) {
writer.uint32(8).uint32(message.key); writer.uint32(8).uint32(message.key);
} }
if (message.value !== undefined) { if (message.value !== undefined) {
@@ -181,7 +184,7 @@ export const ConsumableDefinitions_ConsumablesEntry: MessageFns<ConsumableDefini
decode(input: BinaryReader | Uint8Array, length?: number): ConsumableDefinitions_ConsumablesEntry { decode(input: BinaryReader | Uint8Array, length?: number): ConsumableDefinitions_ConsumablesEntry {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseConsumableDefinitions_ConsumablesEntry(); const message = createBaseConsumableDefinitions_ConsumablesEntry();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -213,14 +216,14 @@ export const ConsumableDefinitions_ConsumablesEntry: MessageFns<ConsumableDefini
fromJSON(object: any): ConsumableDefinitions_ConsumablesEntry { fromJSON(object: any): ConsumableDefinitions_ConsumablesEntry {
return { return {
key: globalThis.Number(assertSet("ConsumableDefinitions_ConsumablesEntry.key", object.key)), key: isSet(object.key) ? globalThis.Number(object.key) : 0,
value: Consumable.fromJSON(assertSet("ConsumableDefinitions_ConsumablesEntry.value", object.value)), value: isSet(object.value) ? Consumable.fromJSON(object.value) : undefined,
}; };
}, },
toJSON(message: ConsumableDefinitions_ConsumablesEntry): unknown { toJSON(message: ConsumableDefinitions_ConsumablesEntry): unknown {
const obj: any = {}; const obj: any = {};
if (message.key !== undefined) { if (message.key !== 0) {
obj.key = Math.round(message.key); obj.key = Math.round(message.key);
} }
if (message.value !== undefined) { if (message.value !== undefined) {
@@ -241,21 +244,13 @@ export const ConsumableDefinitions_ConsumablesEntry: MessageFns<ConsumableDefini
message.key = object.key ?? 0; message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null) message.value = (object.value !== undefined && object.value !== null)
? Consumable.fromPartial(object.value) ? Consumable.fromPartial(object.value)
: createBaseConsumable(); : undefined;
return message; return message;
}, },
}; };
export function createBaseConsumable(): Consumable { function createBaseConsumable(): Consumable {
return { return { id: 0, name: undefined, cooldown: 0, duration: 0, game_mode_exclusive: false, include: [], exclude: [] };
id: 0,
name: createBaseI18nString(),
cooldown: 0,
duration: undefined,
game_mode_exclusive: false,
include: [],
exclude: [],
};
} }
export const Consumable: MessageFns<Consumable> = { export const Consumable: MessageFns<Consumable> = {
@@ -269,7 +264,7 @@ export const Consumable: MessageFns<Consumable> = {
if (message.cooldown !== 0) { if (message.cooldown !== 0) {
writer.uint32(24).uint32(message.cooldown); writer.uint32(24).uint32(message.cooldown);
} }
if (message.duration !== undefined && message.duration !== undefined) { if (message.duration !== undefined && message.duration !== 0) {
writer.uint32(37).float(message.duration); writer.uint32(37).float(message.duration);
} }
if (message.game_mode_exclusive !== false) { if (message.game_mode_exclusive !== false) {
@@ -286,7 +281,7 @@ export const Consumable: MessageFns<Consumable> = {
decode(input: BinaryReader | Uint8Array, length?: number): Consumable { decode(input: BinaryReader | Uint8Array, length?: number): Consumable {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseConsumable(); const message = createBaseConsumable();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -358,11 +353,15 @@ export const Consumable: MessageFns<Consumable> = {
fromJSON(object: any): Consumable { fromJSON(object: any): Consumable {
return { return {
id: globalThis.Number(assertSet("Consumable.id", object.id)), id: isSet(object.id) ? globalThis.Number(object.id) : 0,
name: I18nString.fromJSON(assertSet("Consumable.name", object.name)), name: isSet(object.name) ? I18nString.fromJSON(object.name) : undefined,
cooldown: globalThis.Number(assertSet("Consumable.cooldown", object.cooldown)), cooldown: isSet(object.cooldown) ? globalThis.Number(object.cooldown) : 0,
duration: isSet(object.duration) ? globalThis.Number(object.duration) : undefined, duration: isSet(object.duration) ? globalThis.Number(object.duration) : 0,
game_mode_exclusive: globalThis.Boolean(assertSet("Consumable.game_mode_exclusive", object.game_mode_exclusive)), game_mode_exclusive: isSet(object.gameModeExclusive)
? globalThis.Boolean(object.gameModeExclusive)
: isSet(object.game_mode_exclusive)
? globalThis.Boolean(object.game_mode_exclusive)
: false,
include: globalThis.Array.isArray(object?.include) include: globalThis.Array.isArray(object?.include)
? object.include.map((e: any) => ConsumableTankFilter.fromJSON(e)) ? object.include.map((e: any) => ConsumableTankFilter.fromJSON(e))
: [], : [],
@@ -383,11 +382,11 @@ export const Consumable: MessageFns<Consumable> = {
if (message.cooldown !== 0) { if (message.cooldown !== 0) {
obj.cooldown = Math.round(message.cooldown); obj.cooldown = Math.round(message.cooldown);
} }
if (message.duration !== undefined && message.duration !== undefined) { if (message.duration !== undefined && message.duration !== 0) {
obj.duration = message.duration; obj.duration = message.duration;
} }
if (message.game_mode_exclusive !== false) { if (message.game_mode_exclusive !== false) {
obj.game_mode_exclusive = message.game_mode_exclusive; obj.gameModeExclusive = message.game_mode_exclusive;
} }
if (message.include?.length) { if (message.include?.length) {
obj.include = message.include.map((e) => ConsumableTankFilter.toJSON(e)); obj.include = message.include.map((e) => ConsumableTankFilter.toJSON(e));
@@ -406,9 +405,9 @@ export const Consumable: MessageFns<Consumable> = {
message.id = object.id ?? 0; message.id = object.id ?? 0;
message.name = (object.name !== undefined && object.name !== null) message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name) ? I18nString.fromPartial(object.name)
: createBaseI18nString(); : undefined;
message.cooldown = object.cooldown ?? 0; message.cooldown = object.cooldown ?? 0;
message.duration = object.duration ?? undefined; message.duration = object.duration ?? 0;
message.game_mode_exclusive = object.game_mode_exclusive ?? false; message.game_mode_exclusive = object.game_mode_exclusive ?? false;
message.include = object.include?.map((e) => ConsumableTankFilter.fromPartial(e)) || []; message.include = object.include?.map((e) => ConsumableTankFilter.fromPartial(e)) || [];
message.exclude = object.exclude?.map((e) => ConsumableTankFilter.fromPartial(e)) || []; message.exclude = object.exclude?.map((e) => ConsumableTankFilter.fromPartial(e)) || [];
@@ -416,7 +415,7 @@ export const Consumable: MessageFns<Consumable> = {
}, },
}; };
export function createBaseConsumableTankFilter(): ConsumableTankFilter { function createBaseConsumableTankFilter(): ConsumableTankFilter {
return { filter_type: undefined }; return { filter_type: undefined };
} }
@@ -441,7 +440,7 @@ export const ConsumableTankFilter: MessageFns<ConsumableTankFilter> = {
decode(input: BinaryReader | Uint8Array, length?: number): ConsumableTankFilter { decode(input: BinaryReader | Uint8Array, length?: number): ConsumableTankFilter {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseConsumableTankFilter(); const message = createBaseConsumableTankFilter();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -508,14 +507,11 @@ export const ConsumableTankFilter: MessageFns<ConsumableTankFilter> = {
const obj: any = {}; const obj: any = {};
if (message.filter_type?.$case === "tiers") { if (message.filter_type?.$case === "tiers") {
obj.tiers = ConsumableTankTierFilter.toJSON(message.filter_type.value); obj.tiers = ConsumableTankTierFilter.toJSON(message.filter_type.value);
} } else if (message.filter_type?.$case === "ids") {
if (message.filter_type?.$case === "ids") {
obj.ids = ConsumableTankIdsFilter.toJSON(message.filter_type.value); obj.ids = ConsumableTankIdsFilter.toJSON(message.filter_type.value);
} } else if (message.filter_type?.$case === "categories") {
if (message.filter_type?.$case === "categories") {
obj.categories = ConsumableTankCategoryFilter.toJSON(message.filter_type.value); obj.categories = ConsumableTankCategoryFilter.toJSON(message.filter_type.value);
} } else if (message.filter_type?.$case === "nations") {
if (message.filter_type?.$case === "nations") {
obj.nations = ConsumableTankNationFilter.toJSON(message.filter_type.value); obj.nations = ConsumableTankNationFilter.toJSON(message.filter_type.value);
} }
return obj; return obj;
@@ -526,45 +522,46 @@ export const ConsumableTankFilter: MessageFns<ConsumableTankFilter> = {
}, },
fromPartial<I extends Exact<DeepPartial<ConsumableTankFilter>, I>>(object: I): ConsumableTankFilter { fromPartial<I extends Exact<DeepPartial<ConsumableTankFilter>, I>>(object: I): ConsumableTankFilter {
const message = createBaseConsumableTankFilter(); const message = createBaseConsumableTankFilter();
if ( switch (object.filter_type?.$case) {
object.filter_type?.$case === "tiers" && case "tiers": {
object.filter_type?.value !== undefined && if (object.filter_type?.value !== undefined && object.filter_type?.value !== null) {
object.filter_type?.value !== null message.filter_type = {
) { $case: "tiers",
message.filter_type = { $case: "tiers", value: ConsumableTankTierFilter.fromPartial(object.filter_type.value) }; value: ConsumableTankTierFilter.fromPartial(object.filter_type.value),
} };
if ( }
object.filter_type?.$case === "ids" && break;
object.filter_type?.value !== undefined && }
object.filter_type?.value !== null case "ids": {
) { if (object.filter_type?.value !== undefined && object.filter_type?.value !== null) {
message.filter_type = { $case: "ids", value: ConsumableTankIdsFilter.fromPartial(object.filter_type.value) }; message.filter_type = { $case: "ids", value: ConsumableTankIdsFilter.fromPartial(object.filter_type.value) };
} }
if ( break;
object.filter_type?.$case === "categories" && }
object.filter_type?.value !== undefined && case "categories": {
object.filter_type?.value !== null if (object.filter_type?.value !== undefined && object.filter_type?.value !== null) {
) { message.filter_type = {
message.filter_type = { $case: "categories",
$case: "categories", value: ConsumableTankCategoryFilter.fromPartial(object.filter_type.value),
value: ConsumableTankCategoryFilter.fromPartial(object.filter_type.value), };
}; }
} break;
if ( }
object.filter_type?.$case === "nations" && case "nations": {
object.filter_type?.value !== undefined && if (object.filter_type?.value !== undefined && object.filter_type?.value !== null) {
object.filter_type?.value !== null message.filter_type = {
) { $case: "nations",
message.filter_type = { value: ConsumableTankNationFilter.fromPartial(object.filter_type.value),
$case: "nations", };
value: ConsumableTankNationFilter.fromPartial(object.filter_type.value), }
}; break;
}
} }
return message; return message;
}, },
}; };
export function createBaseConsumableTankTierFilter(): ConsumableTankTierFilter { function createBaseConsumableTankTierFilter(): ConsumableTankTierFilter {
return { min: 0, max: 0 }; return { min: 0, max: 0 };
} }
@@ -581,7 +578,7 @@ export const ConsumableTankTierFilter: MessageFns<ConsumableTankTierFilter> = {
decode(input: BinaryReader | Uint8Array, length?: number): ConsumableTankTierFilter { decode(input: BinaryReader | Uint8Array, length?: number): ConsumableTankTierFilter {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseConsumableTankTierFilter(); const message = createBaseConsumableTankTierFilter();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -613,8 +610,8 @@ export const ConsumableTankTierFilter: MessageFns<ConsumableTankTierFilter> = {
fromJSON(object: any): ConsumableTankTierFilter { fromJSON(object: any): ConsumableTankTierFilter {
return { return {
min: globalThis.Number(assertSet("ConsumableTankTierFilter.min", object.min)), min: isSet(object.min) ? globalThis.Number(object.min) : 0,
max: globalThis.Number(assertSet("ConsumableTankTierFilter.max", object.max)), max: isSet(object.max) ? globalThis.Number(object.max) : 0,
}; };
}, },
@@ -640,23 +637,21 @@ export const ConsumableTankTierFilter: MessageFns<ConsumableTankTierFilter> = {
}, },
}; };
export function createBaseConsumableTankIdsFilter(): ConsumableTankIdsFilter { function createBaseConsumableTankIdsFilter(): ConsumableTankIdsFilter {
return { ids: [] }; return { ids: [] };
} }
export const ConsumableTankIdsFilter: MessageFns<ConsumableTankIdsFilter> = { export const ConsumableTankIdsFilter: MessageFns<ConsumableTankIdsFilter> = {
encode(message: ConsumableTankIdsFilter, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: ConsumableTankIdsFilter, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
writer.uint32(10).fork();
for (const v of message.ids) { for (const v of message.ids) {
writer.uint32(v); writer.uint32(8).uint32(v!);
} }
writer.join();
return writer; return writer;
}, },
decode(input: BinaryReader | Uint8Array, length?: number): ConsumableTankIdsFilter { decode(input: BinaryReader | Uint8Array, length?: number): ConsumableTankIdsFilter {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseConsumableTankIdsFilter(); const message = createBaseConsumableTankIdsFilter();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -710,23 +705,21 @@ export const ConsumableTankIdsFilter: MessageFns<ConsumableTankIdsFilter> = {
}, },
}; };
export function createBaseConsumableTankCategoryFilter(): ConsumableTankCategoryFilter { function createBaseConsumableTankCategoryFilter(): ConsumableTankCategoryFilter {
return { categories: [] }; return { categories: [] };
} }
export const ConsumableTankCategoryFilter: MessageFns<ConsumableTankCategoryFilter> = { export const ConsumableTankCategoryFilter: MessageFns<ConsumableTankCategoryFilter> = {
encode(message: ConsumableTankCategoryFilter, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: ConsumableTankCategoryFilter, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
writer.uint32(10).fork();
for (const v of message.categories) { for (const v of message.categories) {
writer.int32(v); writer.uint32(8).int32(v!);
} }
writer.join();
return writer; return writer;
}, },
decode(input: BinaryReader | Uint8Array, length?: number): ConsumableTankCategoryFilter { decode(input: BinaryReader | Uint8Array, length?: number): ConsumableTankCategoryFilter {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseConsumableTankCategoryFilter(); const message = createBaseConsumableTankCategoryFilter();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -784,7 +777,7 @@ export const ConsumableTankCategoryFilter: MessageFns<ConsumableTankCategoryFilt
}, },
}; };
export function createBaseConsumableTankNationFilter(): ConsumableTankNationFilter { function createBaseConsumableTankNationFilter(): ConsumableTankNationFilter {
return { nations: [] }; return { nations: [] };
} }
@@ -798,7 +791,7 @@ export const ConsumableTankNationFilter: MessageFns<ConsumableTankNationFilter>
decode(input: BinaryReader | Uint8Array, length?: number): ConsumableTankNationFilter { decode(input: BinaryReader | Uint8Array, length?: number): ConsumableTankNationFilter {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseConsumableTankNationFilter(); const message = createBaseConsumableTankNationFilter();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -865,14 +858,6 @@ function isSet(value: any): boolean {
return value !== null && value !== undefined; return value !== null && value !== undefined;
} }
function assertSet<T>(field: string, value: T | undefined): T {
if (!isSet(value)) {
throw new TypeError(`Required field ${field} is not set`);
}
return value as T;
}
export interface MessageFns<T> { export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter; encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T; decode(input: BinaryReader | Uint8Array, length?: number): T;
@@ -2,15 +2,15 @@ syntax = "proto2";
package blitzkit; package blitzkit;
import "packages/core/src/protos/i18n.proto"; import "i18n.proto";
message EquipmentDefinitions { message EquipmentDefinitions {
map<string, EquipmentPreset> presets = 1; map<string, EquipmentPreset> presets = 1;
map<uint32, Equipment> equipments = 2; map<uint32, Equipment> equipments = 2;
} }
message Equipment { message Equipment {
required I18nString name = 1; required I18nString name = 1;
required I18nString description = 2; required I18nString description = 2;
} }
@@ -19,6 +19,6 @@ message EquipmentPreset {
} }
message EquipmentSlot { message EquipmentSlot {
required uint32 left = 1; required uint32 left = 1;
required uint32 right = 2; required uint32 right = 2;
} }
@@ -1,12 +1,12 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT. // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions: // versions:
// protoc-gen-ts_proto v2.2.2 // protoc-gen-ts_proto v2.11.2
// protoc v5.28.2 // protoc v5.28.2
// source: packages/core/src/protos/equipment_definitions.proto // source: equipment_definitions.proto
/* eslint-disable */ /* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { createBaseI18nString, I18nString } from "./i18n"; import { I18nString } from "./i18n";
export const protobufPackage = "blitzkit"; export const protobufPackage = "blitzkit";
@@ -26,8 +26,8 @@ export interface EquipmentDefinitions_EquipmentsEntry {
} }
export interface Equipment { export interface Equipment {
name: I18nString; name: I18nString | undefined;
description: I18nString; description: I18nString | undefined;
} }
export interface EquipmentPreset { export interface EquipmentPreset {
@@ -39,16 +39,16 @@ export interface EquipmentSlot {
right: number; right: number;
} }
export function createBaseEquipmentDefinitions(): EquipmentDefinitions { function createBaseEquipmentDefinitions(): EquipmentDefinitions {
return { presets: {}, equipments: {} }; return { presets: {}, equipments: {} };
} }
export const EquipmentDefinitions: MessageFns<EquipmentDefinitions> = { export const EquipmentDefinitions: MessageFns<EquipmentDefinitions> = {
encode(message: EquipmentDefinitions, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: EquipmentDefinitions, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
Object.entries(message.presets).forEach(([key, value]) => { globalThis.Object.entries(message.presets).forEach(([key, value]: [string, EquipmentPreset]) => {
EquipmentDefinitions_PresetsEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join(); EquipmentDefinitions_PresetsEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join();
}); });
Object.entries(message.equipments).forEach(([key, value]) => { globalThis.Object.entries(message.equipments).forEach(([key, value]: [string, Equipment]) => {
EquipmentDefinitions_EquipmentsEntry.encode({ key: key as any, value }, writer.uint32(18).fork()).join(); EquipmentDefinitions_EquipmentsEntry.encode({ key: key as any, value }, writer.uint32(18).fork()).join();
}); });
return writer; return writer;
@@ -56,7 +56,7 @@ export const EquipmentDefinitions: MessageFns<EquipmentDefinitions> = {
decode(input: BinaryReader | Uint8Array, length?: number): EquipmentDefinitions { decode(input: BinaryReader | Uint8Array, length?: number): EquipmentDefinitions {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseEquipmentDefinitions(); const message = createBaseEquipmentDefinitions();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -95,16 +95,22 @@ export const EquipmentDefinitions: MessageFns<EquipmentDefinitions> = {
fromJSON(object: any): EquipmentDefinitions { fromJSON(object: any): EquipmentDefinitions {
return { return {
presets: isObject(object.presets) presets: isObject(object.presets)
? Object.entries(object.presets).reduce<{ [key: string]: EquipmentPreset }>((acc, [key, value]) => { ? (globalThis.Object.entries(object.presets) as [string, any][]).reduce(
acc[key] = EquipmentPreset.fromJSON(value); (acc: { [key: string]: EquipmentPreset }, [key, value]: [string, any]) => {
return acc; acc[key] = EquipmentPreset.fromJSON(value);
}, {}) return acc;
},
{},
)
: {}, : {},
equipments: isObject(object.equipments) equipments: isObject(object.equipments)
? Object.entries(object.equipments).reduce<{ [key: number]: Equipment }>((acc, [key, value]) => { ? (globalThis.Object.entries(object.equipments) as [string, any][]).reduce(
acc[globalThis.Number(key)] = Equipment.fromJSON(value); (acc: { [key: number]: Equipment }, [key, value]: [string, any]) => {
return acc; acc[globalThis.Number(key)] = Equipment.fromJSON(value);
}, {}) return acc;
},
{},
)
: {}, : {},
}; };
}, },
@@ -112,7 +118,7 @@ export const EquipmentDefinitions: MessageFns<EquipmentDefinitions> = {
toJSON(message: EquipmentDefinitions): unknown { toJSON(message: EquipmentDefinitions): unknown {
const obj: any = {}; const obj: any = {};
if (message.presets) { if (message.presets) {
const entries = Object.entries(message.presets); const entries = globalThis.Object.entries(message.presets) as [string, EquipmentPreset][];
if (entries.length > 0) { if (entries.length > 0) {
obj.presets = {}; obj.presets = {};
entries.forEach(([k, v]) => { entries.forEach(([k, v]) => {
@@ -121,7 +127,7 @@ export const EquipmentDefinitions: MessageFns<EquipmentDefinitions> = {
} }
} }
if (message.equipments) { if (message.equipments) {
const entries = Object.entries(message.equipments); const entries = globalThis.Object.entries(message.equipments) as [string, Equipment][];
if (entries.length > 0) { if (entries.length > 0) {
obj.equipments = {}; obj.equipments = {};
entries.forEach(([k, v]) => { entries.forEach(([k, v]) => {
@@ -137,8 +143,8 @@ export const EquipmentDefinitions: MessageFns<EquipmentDefinitions> = {
}, },
fromPartial<I extends Exact<DeepPartial<EquipmentDefinitions>, I>>(object: I): EquipmentDefinitions { fromPartial<I extends Exact<DeepPartial<EquipmentDefinitions>, I>>(object: I): EquipmentDefinitions {
const message = createBaseEquipmentDefinitions(); const message = createBaseEquipmentDefinitions();
message.presets = Object.entries(object.presets ?? {}).reduce<{ [key: string]: EquipmentPreset }>( message.presets = (globalThis.Object.entries(object.presets ?? {}) as [string, EquipmentPreset][]).reduce(
(acc, [key, value]) => { (acc: { [key: string]: EquipmentPreset }, [key, value]: [string, EquipmentPreset]) => {
if (value !== undefined) { if (value !== undefined) {
acc[key] = EquipmentPreset.fromPartial(value); acc[key] = EquipmentPreset.fromPartial(value);
} }
@@ -146,8 +152,8 @@ export const EquipmentDefinitions: MessageFns<EquipmentDefinitions> = {
}, },
{}, {},
); );
message.equipments = Object.entries(object.equipments ?? {}).reduce<{ [key: number]: Equipment }>( message.equipments = (globalThis.Object.entries(object.equipments ?? {}) as [string, Equipment][]).reduce(
(acc, [key, value]) => { (acc: { [key: number]: Equipment }, [key, value]: [string, Equipment]) => {
if (value !== undefined) { if (value !== undefined) {
acc[globalThis.Number(key)] = Equipment.fromPartial(value); acc[globalThis.Number(key)] = Equipment.fromPartial(value);
} }
@@ -159,13 +165,13 @@ export const EquipmentDefinitions: MessageFns<EquipmentDefinitions> = {
}, },
}; };
export function createBaseEquipmentDefinitions_PresetsEntry(): EquipmentDefinitions_PresetsEntry { function createBaseEquipmentDefinitions_PresetsEntry(): EquipmentDefinitions_PresetsEntry {
return { key: "", value: createBaseEquipmentPreset() }; return { key: "", value: undefined };
} }
export const EquipmentDefinitions_PresetsEntry: MessageFns<EquipmentDefinitions_PresetsEntry> = { export const EquipmentDefinitions_PresetsEntry: MessageFns<EquipmentDefinitions_PresetsEntry> = {
encode(message: EquipmentDefinitions_PresetsEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: EquipmentDefinitions_PresetsEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.key !== undefined) { if (message.key !== "") {
writer.uint32(10).string(message.key); writer.uint32(10).string(message.key);
} }
if (message.value !== undefined) { if (message.value !== undefined) {
@@ -176,7 +182,7 @@ export const EquipmentDefinitions_PresetsEntry: MessageFns<EquipmentDefinitions_
decode(input: BinaryReader | Uint8Array, length?: number): EquipmentDefinitions_PresetsEntry { decode(input: BinaryReader | Uint8Array, length?: number): EquipmentDefinitions_PresetsEntry {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseEquipmentDefinitions_PresetsEntry(); const message = createBaseEquipmentDefinitions_PresetsEntry();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -208,14 +214,14 @@ export const EquipmentDefinitions_PresetsEntry: MessageFns<EquipmentDefinitions_
fromJSON(object: any): EquipmentDefinitions_PresetsEntry { fromJSON(object: any): EquipmentDefinitions_PresetsEntry {
return { return {
key: globalThis.String(assertSet("EquipmentDefinitions_PresetsEntry.key", object.key)), key: isSet(object.key) ? globalThis.String(object.key) : "",
value: EquipmentPreset.fromJSON(assertSet("EquipmentDefinitions_PresetsEntry.value", object.value)), value: isSet(object.value) ? EquipmentPreset.fromJSON(object.value) : undefined,
}; };
}, },
toJSON(message: EquipmentDefinitions_PresetsEntry): unknown { toJSON(message: EquipmentDefinitions_PresetsEntry): unknown {
const obj: any = {}; const obj: any = {};
if (message.key !== undefined) { if (message.key !== "") {
obj.key = message.key; obj.key = message.key;
} }
if (message.value !== undefined) { if (message.value !== undefined) {
@@ -236,18 +242,18 @@ export const EquipmentDefinitions_PresetsEntry: MessageFns<EquipmentDefinitions_
message.key = object.key ?? ""; message.key = object.key ?? "";
message.value = (object.value !== undefined && object.value !== null) message.value = (object.value !== undefined && object.value !== null)
? EquipmentPreset.fromPartial(object.value) ? EquipmentPreset.fromPartial(object.value)
: createBaseEquipmentPreset(); : undefined;
return message; return message;
}, },
}; };
export function createBaseEquipmentDefinitions_EquipmentsEntry(): EquipmentDefinitions_EquipmentsEntry { function createBaseEquipmentDefinitions_EquipmentsEntry(): EquipmentDefinitions_EquipmentsEntry {
return { key: 0, value: createBaseEquipment() }; return { key: 0, value: undefined };
} }
export const EquipmentDefinitions_EquipmentsEntry: MessageFns<EquipmentDefinitions_EquipmentsEntry> = { export const EquipmentDefinitions_EquipmentsEntry: MessageFns<EquipmentDefinitions_EquipmentsEntry> = {
encode(message: EquipmentDefinitions_EquipmentsEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: EquipmentDefinitions_EquipmentsEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.key !== undefined) { if (message.key !== 0) {
writer.uint32(8).uint32(message.key); writer.uint32(8).uint32(message.key);
} }
if (message.value !== undefined) { if (message.value !== undefined) {
@@ -258,7 +264,7 @@ export const EquipmentDefinitions_EquipmentsEntry: MessageFns<EquipmentDefinitio
decode(input: BinaryReader | Uint8Array, length?: number): EquipmentDefinitions_EquipmentsEntry { decode(input: BinaryReader | Uint8Array, length?: number): EquipmentDefinitions_EquipmentsEntry {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseEquipmentDefinitions_EquipmentsEntry(); const message = createBaseEquipmentDefinitions_EquipmentsEntry();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -290,14 +296,14 @@ export const EquipmentDefinitions_EquipmentsEntry: MessageFns<EquipmentDefinitio
fromJSON(object: any): EquipmentDefinitions_EquipmentsEntry { fromJSON(object: any): EquipmentDefinitions_EquipmentsEntry {
return { return {
key: globalThis.Number(assertSet("EquipmentDefinitions_EquipmentsEntry.key", object.key)), key: isSet(object.key) ? globalThis.Number(object.key) : 0,
value: Equipment.fromJSON(assertSet("EquipmentDefinitions_EquipmentsEntry.value", object.value)), value: isSet(object.value) ? Equipment.fromJSON(object.value) : undefined,
}; };
}, },
toJSON(message: EquipmentDefinitions_EquipmentsEntry): unknown { toJSON(message: EquipmentDefinitions_EquipmentsEntry): unknown {
const obj: any = {}; const obj: any = {};
if (message.key !== undefined) { if (message.key !== 0) {
obj.key = Math.round(message.key); obj.key = Math.round(message.key);
} }
if (message.value !== undefined) { if (message.value !== undefined) {
@@ -318,13 +324,13 @@ export const EquipmentDefinitions_EquipmentsEntry: MessageFns<EquipmentDefinitio
message.key = object.key ?? 0; message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null) message.value = (object.value !== undefined && object.value !== null)
? Equipment.fromPartial(object.value) ? Equipment.fromPartial(object.value)
: createBaseEquipment(); : undefined;
return message; return message;
}, },
}; };
export function createBaseEquipment(): Equipment { function createBaseEquipment(): Equipment {
return { name: createBaseI18nString(), description: createBaseI18nString() }; return { name: undefined, description: undefined };
} }
export const Equipment: MessageFns<Equipment> = { export const Equipment: MessageFns<Equipment> = {
@@ -340,7 +346,7 @@ export const Equipment: MessageFns<Equipment> = {
decode(input: BinaryReader | Uint8Array, length?: number): Equipment { decode(input: BinaryReader | Uint8Array, length?: number): Equipment {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseEquipment(); const message = createBaseEquipment();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -372,8 +378,8 @@ export const Equipment: MessageFns<Equipment> = {
fromJSON(object: any): Equipment { fromJSON(object: any): Equipment {
return { return {
name: I18nString.fromJSON(assertSet("Equipment.name", object.name)), name: isSet(object.name) ? I18nString.fromJSON(object.name) : undefined,
description: I18nString.fromJSON(assertSet("Equipment.description", object.description)), description: isSet(object.description) ? I18nString.fromJSON(object.description) : undefined,
}; };
}, },
@@ -395,15 +401,15 @@ export const Equipment: MessageFns<Equipment> = {
const message = createBaseEquipment(); const message = createBaseEquipment();
message.name = (object.name !== undefined && object.name !== null) message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name) ? I18nString.fromPartial(object.name)
: createBaseI18nString(); : undefined;
message.description = (object.description !== undefined && object.description !== null) message.description = (object.description !== undefined && object.description !== null)
? I18nString.fromPartial(object.description) ? I18nString.fromPartial(object.description)
: createBaseI18nString(); : undefined;
return message; return message;
}, },
}; };
export function createBaseEquipmentPreset(): EquipmentPreset { function createBaseEquipmentPreset(): EquipmentPreset {
return { slots: [] }; return { slots: [] };
} }
@@ -417,7 +423,7 @@ export const EquipmentPreset: MessageFns<EquipmentPreset> = {
decode(input: BinaryReader | Uint8Array, length?: number): EquipmentPreset { decode(input: BinaryReader | Uint8Array, length?: number): EquipmentPreset {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseEquipmentPreset(); const message = createBaseEquipmentPreset();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -463,7 +469,7 @@ export const EquipmentPreset: MessageFns<EquipmentPreset> = {
}, },
}; };
export function createBaseEquipmentSlot(): EquipmentSlot { function createBaseEquipmentSlot(): EquipmentSlot {
return { left: 0, right: 0 }; return { left: 0, right: 0 };
} }
@@ -480,7 +486,7 @@ export const EquipmentSlot: MessageFns<EquipmentSlot> = {
decode(input: BinaryReader | Uint8Array, length?: number): EquipmentSlot { decode(input: BinaryReader | Uint8Array, length?: number): EquipmentSlot {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseEquipmentSlot(); const message = createBaseEquipmentSlot();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -512,8 +518,8 @@ export const EquipmentSlot: MessageFns<EquipmentSlot> = {
fromJSON(object: any): EquipmentSlot { fromJSON(object: any): EquipmentSlot {
return { return {
left: globalThis.Number(assertSet("EquipmentSlot.left", object.left)), left: isSet(object.left) ? globalThis.Number(object.left) : 0,
right: globalThis.Number(assertSet("EquipmentSlot.right", object.right)), right: isSet(object.right) ? globalThis.Number(object.right) : 0,
}; };
}, },
@@ -560,14 +566,6 @@ function isSet(value: any): boolean {
return value !== null && value !== undefined; return value !== null && value !== undefined;
} }
function assertSet<T>(field: string, value: T | undefined): T {
if (!isSet(value)) {
throw new TypeError(`Required field ${field} is not set`);
}
return value as T;
}
export interface MessageFns<T> { export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter; encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T; decode(input: BinaryReader | Uint8Array, length?: number): T;
+4 -4
View File
@@ -2,14 +2,14 @@ syntax = "proto2";
package blitzkit; package blitzkit;
import "packages/core/src/protos/i18n.proto"; import "i18n.proto";
message Gallery { message Gallery {
repeated Avatar avatars = 1; repeated Avatar avatars = 1;
} }
message Avatar { message Avatar {
required string id = 1; required string id = 1;
required I18nString name = 2; required I18nString name = 2;
required string extension = 3; required string extension = 3;
} }
+13 -21
View File
@@ -1,12 +1,12 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT. // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions: // versions:
// protoc-gen-ts_proto v2.2.2 // protoc-gen-ts_proto v2.11.2
// protoc v5.28.2 // protoc v5.28.2
// source: packages/core/src/protos/gallery.proto // source: gallery.proto
/* eslint-disable */ /* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { createBaseI18nString, I18nString } from "./i18n"; import { I18nString } from "./i18n";
export const protobufPackage = "blitzkit"; export const protobufPackage = "blitzkit";
@@ -16,11 +16,11 @@ export interface Gallery {
export interface Avatar { export interface Avatar {
id: string; id: string;
name: I18nString; name: I18nString | undefined;
extension: string; extension: string;
} }
export function createBaseGallery(): Gallery { function createBaseGallery(): Gallery {
return { avatars: [] }; return { avatars: [] };
} }
@@ -34,7 +34,7 @@ export const Gallery: MessageFns<Gallery> = {
decode(input: BinaryReader | Uint8Array, length?: number): Gallery { decode(input: BinaryReader | Uint8Array, length?: number): Gallery {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseGallery(); const message = createBaseGallery();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -80,8 +80,8 @@ export const Gallery: MessageFns<Gallery> = {
}, },
}; };
export function createBaseAvatar(): Avatar { function createBaseAvatar(): Avatar {
return { id: "", name: createBaseI18nString(), extension: "" }; return { id: "", name: undefined, extension: "" };
} }
export const Avatar: MessageFns<Avatar> = { export const Avatar: MessageFns<Avatar> = {
@@ -100,7 +100,7 @@ export const Avatar: MessageFns<Avatar> = {
decode(input: BinaryReader | Uint8Array, length?: number): Avatar { decode(input: BinaryReader | Uint8Array, length?: number): Avatar {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseAvatar(); const message = createBaseAvatar();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -140,9 +140,9 @@ export const Avatar: MessageFns<Avatar> = {
fromJSON(object: any): Avatar { fromJSON(object: any): Avatar {
return { return {
id: globalThis.String(assertSet("Avatar.id", object.id)), id: isSet(object.id) ? globalThis.String(object.id) : "",
name: I18nString.fromJSON(assertSet("Avatar.name", object.name)), name: isSet(object.name) ? I18nString.fromJSON(object.name) : undefined,
extension: globalThis.String(assertSet("Avatar.extension", object.extension)), extension: isSet(object.extension) ? globalThis.String(object.extension) : "",
}; };
}, },
@@ -168,7 +168,7 @@ export const Avatar: MessageFns<Avatar> = {
message.id = object.id ?? ""; message.id = object.id ?? "";
message.name = (object.name !== undefined && object.name !== null) message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name) ? I18nString.fromPartial(object.name)
: createBaseI18nString(); : undefined;
message.extension = object.extension ?? ""; message.extension = object.extension ?? "";
return message; return message;
}, },
@@ -191,14 +191,6 @@ function isSet(value: any): boolean {
return value !== null && value !== undefined; return value !== null && value !== undefined;
} }
function assertSet<T>(field: string, value: T | undefined): T {
if (!isSet(value)) {
throw new TypeError(`Required field ${field} is not set`);
}
return value as T;
}
export interface MessageFns<T> { export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter; encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T; decode(input: BinaryReader | Uint8Array, length?: number): T;
@@ -2,13 +2,13 @@ syntax = "proto2";
package blitzkit; package blitzkit;
import "packages/core/src/protos/i18n.proto"; import "i18n.proto";
message GameDefinitions { message GameDefinitions {
required string version = 1; required string version = 1;
repeated string nations = 2; repeated string nations = 2;
map<uint32, GameMode> gameModes = 3; map<uint32, GameMode> gameModes = 3;
map<uint32, GameModeRole> roles = 4; map<uint32, GameModeRole> roles = 4;
} }
message GameMode { message GameMode {
@@ -16,6 +16,6 @@ message GameMode {
} }
message GameModeRole { message GameModeRole {
repeated uint32 provisions = 1; repeated uint32 provisions = 1;
repeated uint32 consumables = 2; repeated uint32 consumables = 2;
} }
+61 -64
View File
@@ -1,12 +1,12 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT. // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions: // versions:
// protoc-gen-ts_proto v2.2.2 // protoc-gen-ts_proto v2.11.2
// protoc v5.28.2 // protoc v5.28.2
// source: packages/core/src/protos/game_definitions.proto // source: game_definitions.proto
/* eslint-disable */ /* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { createBaseI18nString, I18nString } from "./i18n"; import { I18nString } from "./i18n";
export const protobufPackage = "blitzkit"; export const protobufPackage = "blitzkit";
@@ -28,7 +28,7 @@ export interface GameDefinitions_RolesEntry {
} }
export interface GameMode { export interface GameMode {
name: I18nString; name: I18nString | undefined;
} }
export interface GameModeRole { export interface GameModeRole {
@@ -36,7 +36,7 @@ export interface GameModeRole {
consumables: number[]; consumables: number[];
} }
export function createBaseGameDefinitions(): GameDefinitions { function createBaseGameDefinitions(): GameDefinitions {
return { version: "", nations: [], gameModes: {}, roles: {} }; return { version: "", nations: [], gameModes: {}, roles: {} };
} }
@@ -48,10 +48,10 @@ export const GameDefinitions: MessageFns<GameDefinitions> = {
for (const v of message.nations) { for (const v of message.nations) {
writer.uint32(18).string(v!); writer.uint32(18).string(v!);
} }
Object.entries(message.gameModes).forEach(([key, value]) => { globalThis.Object.entries(message.gameModes).forEach(([key, value]: [string, GameMode]) => {
GameDefinitions_GameModesEntry.encode({ key: key as any, value }, writer.uint32(26).fork()).join(); GameDefinitions_GameModesEntry.encode({ key: key as any, value }, writer.uint32(26).fork()).join();
}); });
Object.entries(message.roles).forEach(([key, value]) => { globalThis.Object.entries(message.roles).forEach(([key, value]: [string, GameModeRole]) => {
GameDefinitions_RolesEntry.encode({ key: key as any, value }, writer.uint32(34).fork()).join(); GameDefinitions_RolesEntry.encode({ key: key as any, value }, writer.uint32(34).fork()).join();
}); });
return writer; return writer;
@@ -59,7 +59,7 @@ export const GameDefinitions: MessageFns<GameDefinitions> = {
decode(input: BinaryReader | Uint8Array, length?: number): GameDefinitions { decode(input: BinaryReader | Uint8Array, length?: number): GameDefinitions {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseGameDefinitions(); const message = createBaseGameDefinitions();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -113,19 +113,25 @@ export const GameDefinitions: MessageFns<GameDefinitions> = {
fromJSON(object: any): GameDefinitions { fromJSON(object: any): GameDefinitions {
return { return {
version: globalThis.String(assertSet("GameDefinitions.version", object.version)), version: isSet(object.version) ? globalThis.String(object.version) : "",
nations: globalThis.Array.isArray(object?.nations) ? object.nations.map((e: any) => globalThis.String(e)) : [], nations: globalThis.Array.isArray(object?.nations) ? object.nations.map((e: any) => globalThis.String(e)) : [],
gameModes: isObject(object.gameModes) gameModes: isObject(object.gameModes)
? Object.entries(object.gameModes).reduce<{ [key: number]: GameMode }>((acc, [key, value]) => { ? (globalThis.Object.entries(object.gameModes) as [string, any][]).reduce(
acc[globalThis.Number(key)] = GameMode.fromJSON(value); (acc: { [key: number]: GameMode }, [key, value]: [string, any]) => {
return acc; acc[globalThis.Number(key)] = GameMode.fromJSON(value);
}, {}) return acc;
},
{},
)
: {}, : {},
roles: isObject(object.roles) roles: isObject(object.roles)
? Object.entries(object.roles).reduce<{ [key: number]: GameModeRole }>((acc, [key, value]) => { ? (globalThis.Object.entries(object.roles) as [string, any][]).reduce(
acc[globalThis.Number(key)] = GameModeRole.fromJSON(value); (acc: { [key: number]: GameModeRole }, [key, value]: [string, any]) => {
return acc; acc[globalThis.Number(key)] = GameModeRole.fromJSON(value);
}, {}) return acc;
},
{},
)
: {}, : {},
}; };
}, },
@@ -139,7 +145,7 @@ export const GameDefinitions: MessageFns<GameDefinitions> = {
obj.nations = message.nations; obj.nations = message.nations;
} }
if (message.gameModes) { if (message.gameModes) {
const entries = Object.entries(message.gameModes); const entries = globalThis.Object.entries(message.gameModes) as [string, GameMode][];
if (entries.length > 0) { if (entries.length > 0) {
obj.gameModes = {}; obj.gameModes = {};
entries.forEach(([k, v]) => { entries.forEach(([k, v]) => {
@@ -148,7 +154,7 @@ export const GameDefinitions: MessageFns<GameDefinitions> = {
} }
} }
if (message.roles) { if (message.roles) {
const entries = Object.entries(message.roles); const entries = globalThis.Object.entries(message.roles) as [string, GameModeRole][];
if (entries.length > 0) { if (entries.length > 0) {
obj.roles = {}; obj.roles = {};
entries.forEach(([k, v]) => { entries.forEach(([k, v]) => {
@@ -166,8 +172,8 @@ export const GameDefinitions: MessageFns<GameDefinitions> = {
const message = createBaseGameDefinitions(); const message = createBaseGameDefinitions();
message.version = object.version ?? ""; message.version = object.version ?? "";
message.nations = object.nations?.map((e) => e) || []; message.nations = object.nations?.map((e) => e) || [];
message.gameModes = Object.entries(object.gameModes ?? {}).reduce<{ [key: number]: GameMode }>( message.gameModes = (globalThis.Object.entries(object.gameModes ?? {}) as [string, GameMode][]).reduce(
(acc, [key, value]) => { (acc: { [key: number]: GameMode }, [key, value]: [string, GameMode]) => {
if (value !== undefined) { if (value !== undefined) {
acc[globalThis.Number(key)] = GameMode.fromPartial(value); acc[globalThis.Number(key)] = GameMode.fromPartial(value);
} }
@@ -175,23 +181,26 @@ export const GameDefinitions: MessageFns<GameDefinitions> = {
}, },
{}, {},
); );
message.roles = Object.entries(object.roles ?? {}).reduce<{ [key: number]: GameModeRole }>((acc, [key, value]) => { message.roles = (globalThis.Object.entries(object.roles ?? {}) as [string, GameModeRole][]).reduce(
if (value !== undefined) { (acc: { [key: number]: GameModeRole }, [key, value]: [string, GameModeRole]) => {
acc[globalThis.Number(key)] = GameModeRole.fromPartial(value); if (value !== undefined) {
} acc[globalThis.Number(key)] = GameModeRole.fromPartial(value);
return acc; }
}, {}); return acc;
},
{},
);
return message; return message;
}, },
}; };
export function createBaseGameDefinitions_GameModesEntry(): GameDefinitions_GameModesEntry { function createBaseGameDefinitions_GameModesEntry(): GameDefinitions_GameModesEntry {
return { key: 0, value: createBaseGameMode() }; return { key: 0, value: undefined };
} }
export const GameDefinitions_GameModesEntry: MessageFns<GameDefinitions_GameModesEntry> = { export const GameDefinitions_GameModesEntry: MessageFns<GameDefinitions_GameModesEntry> = {
encode(message: GameDefinitions_GameModesEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: GameDefinitions_GameModesEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.key !== undefined) { if (message.key !== 0) {
writer.uint32(8).uint32(message.key); writer.uint32(8).uint32(message.key);
} }
if (message.value !== undefined) { if (message.value !== undefined) {
@@ -202,7 +211,7 @@ export const GameDefinitions_GameModesEntry: MessageFns<GameDefinitions_GameMode
decode(input: BinaryReader | Uint8Array, length?: number): GameDefinitions_GameModesEntry { decode(input: BinaryReader | Uint8Array, length?: number): GameDefinitions_GameModesEntry {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseGameDefinitions_GameModesEntry(); const message = createBaseGameDefinitions_GameModesEntry();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -234,14 +243,14 @@ export const GameDefinitions_GameModesEntry: MessageFns<GameDefinitions_GameMode
fromJSON(object: any): GameDefinitions_GameModesEntry { fromJSON(object: any): GameDefinitions_GameModesEntry {
return { return {
key: globalThis.Number(assertSet("GameDefinitions_GameModesEntry.key", object.key)), key: isSet(object.key) ? globalThis.Number(object.key) : 0,
value: GameMode.fromJSON(assertSet("GameDefinitions_GameModesEntry.value", object.value)), value: isSet(object.value) ? GameMode.fromJSON(object.value) : undefined,
}; };
}, },
toJSON(message: GameDefinitions_GameModesEntry): unknown { toJSON(message: GameDefinitions_GameModesEntry): unknown {
const obj: any = {}; const obj: any = {};
if (message.key !== undefined) { if (message.key !== 0) {
obj.key = Math.round(message.key); obj.key = Math.round(message.key);
} }
if (message.value !== undefined) { if (message.value !== undefined) {
@@ -260,18 +269,18 @@ export const GameDefinitions_GameModesEntry: MessageFns<GameDefinitions_GameMode
message.key = object.key ?? 0; message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null) message.value = (object.value !== undefined && object.value !== null)
? GameMode.fromPartial(object.value) ? GameMode.fromPartial(object.value)
: createBaseGameMode(); : undefined;
return message; return message;
}, },
}; };
export function createBaseGameDefinitions_RolesEntry(): GameDefinitions_RolesEntry { function createBaseGameDefinitions_RolesEntry(): GameDefinitions_RolesEntry {
return { key: 0, value: createBaseGameModeRole() }; return { key: 0, value: undefined };
} }
export const GameDefinitions_RolesEntry: MessageFns<GameDefinitions_RolesEntry> = { export const GameDefinitions_RolesEntry: MessageFns<GameDefinitions_RolesEntry> = {
encode(message: GameDefinitions_RolesEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: GameDefinitions_RolesEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.key !== undefined) { if (message.key !== 0) {
writer.uint32(8).uint32(message.key); writer.uint32(8).uint32(message.key);
} }
if (message.value !== undefined) { if (message.value !== undefined) {
@@ -282,7 +291,7 @@ export const GameDefinitions_RolesEntry: MessageFns<GameDefinitions_RolesEntry>
decode(input: BinaryReader | Uint8Array, length?: number): GameDefinitions_RolesEntry { decode(input: BinaryReader | Uint8Array, length?: number): GameDefinitions_RolesEntry {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseGameDefinitions_RolesEntry(); const message = createBaseGameDefinitions_RolesEntry();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -314,14 +323,14 @@ export const GameDefinitions_RolesEntry: MessageFns<GameDefinitions_RolesEntry>
fromJSON(object: any): GameDefinitions_RolesEntry { fromJSON(object: any): GameDefinitions_RolesEntry {
return { return {
key: globalThis.Number(assertSet("GameDefinitions_RolesEntry.key", object.key)), key: isSet(object.key) ? globalThis.Number(object.key) : 0,
value: GameModeRole.fromJSON(assertSet("GameDefinitions_RolesEntry.value", object.value)), value: isSet(object.value) ? GameModeRole.fromJSON(object.value) : undefined,
}; };
}, },
toJSON(message: GameDefinitions_RolesEntry): unknown { toJSON(message: GameDefinitions_RolesEntry): unknown {
const obj: any = {}; const obj: any = {};
if (message.key !== undefined) { if (message.key !== 0) {
obj.key = Math.round(message.key); obj.key = Math.round(message.key);
} }
if (message.value !== undefined) { if (message.value !== undefined) {
@@ -338,13 +347,13 @@ export const GameDefinitions_RolesEntry: MessageFns<GameDefinitions_RolesEntry>
message.key = object.key ?? 0; message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null) message.value = (object.value !== undefined && object.value !== null)
? GameModeRole.fromPartial(object.value) ? GameModeRole.fromPartial(object.value)
: createBaseGameModeRole(); : undefined;
return message; return message;
}, },
}; };
export function createBaseGameMode(): GameMode { function createBaseGameMode(): GameMode {
return { name: createBaseI18nString() }; return { name: undefined };
} }
export const GameMode: MessageFns<GameMode> = { export const GameMode: MessageFns<GameMode> = {
@@ -357,7 +366,7 @@ export const GameMode: MessageFns<GameMode> = {
decode(input: BinaryReader | Uint8Array, length?: number): GameMode { decode(input: BinaryReader | Uint8Array, length?: number): GameMode {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseGameMode(); const message = createBaseGameMode();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -380,7 +389,7 @@ export const GameMode: MessageFns<GameMode> = {
}, },
fromJSON(object: any): GameMode { fromJSON(object: any): GameMode {
return { name: I18nString.fromJSON(assertSet("GameMode.name", object.name)) }; return { name: isSet(object.name) ? I18nString.fromJSON(object.name) : undefined };
}, },
toJSON(message: GameMode): unknown { toJSON(message: GameMode): unknown {
@@ -398,33 +407,29 @@ export const GameMode: MessageFns<GameMode> = {
const message = createBaseGameMode(); const message = createBaseGameMode();
message.name = (object.name !== undefined && object.name !== null) message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name) ? I18nString.fromPartial(object.name)
: createBaseI18nString(); : undefined;
return message; return message;
}, },
}; };
export function createBaseGameModeRole(): GameModeRole { function createBaseGameModeRole(): GameModeRole {
return { provisions: [], consumables: [] }; return { provisions: [], consumables: [] };
} }
export const GameModeRole: MessageFns<GameModeRole> = { export const GameModeRole: MessageFns<GameModeRole> = {
encode(message: GameModeRole, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: GameModeRole, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
writer.uint32(10).fork();
for (const v of message.provisions) { for (const v of message.provisions) {
writer.uint32(v); writer.uint32(8).uint32(v!);
} }
writer.join();
writer.uint32(18).fork();
for (const v of message.consumables) { for (const v of message.consumables) {
writer.uint32(v); writer.uint32(16).uint32(v!);
} }
writer.join();
return writer; return writer;
}, },
decode(input: BinaryReader | Uint8Array, length?: number): GameModeRole { decode(input: BinaryReader | Uint8Array, length?: number): GameModeRole {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseGameModeRole(); const message = createBaseGameModeRole();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -528,14 +533,6 @@ function isSet(value: any): boolean {
return value !== null && value !== undefined; return value !== null && value !== undefined;
} }
function assertSet<T>(field: string, value: T | undefined): T {
if (!isSet(value)) {
throw new TypeError(`Required field ${field} is not set`);
}
return value as T;
}
export interface MessageFns<T> { export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter; encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T; decode(input: BinaryReader | Uint8Array, length?: number): T;
+30 -32
View File
@@ -1,8 +1,8 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT. // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions: // versions:
// protoc-gen-ts_proto v2.2.2 // protoc-gen-ts_proto v2.11.2
// protoc v5.28.2 // protoc v5.28.2
// source: packages/core/src/protos/i18n.proto // source: i18n.proto
/* eslint-disable */ /* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
@@ -18,13 +18,13 @@ export interface I18nString_LocalesEntry {
value: string; value: string;
} }
export function createBaseI18nString(): I18nString { function createBaseI18nString(): I18nString {
return { locales: {} }; return { locales: {} };
} }
export const I18nString: MessageFns<I18nString> = { export const I18nString: MessageFns<I18nString> = {
encode(message: I18nString, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: I18nString, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
Object.entries(message.locales).forEach(([key, value]) => { globalThis.Object.entries(message.locales).forEach(([key, value]: [string, string]) => {
I18nString_LocalesEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join(); I18nString_LocalesEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join();
}); });
return writer; return writer;
@@ -32,7 +32,7 @@ export const I18nString: MessageFns<I18nString> = {
decode(input: BinaryReader | Uint8Array, length?: number): I18nString { decode(input: BinaryReader | Uint8Array, length?: number): I18nString {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseI18nString(); const message = createBaseI18nString();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -60,10 +60,13 @@ export const I18nString: MessageFns<I18nString> = {
fromJSON(object: any): I18nString { fromJSON(object: any): I18nString {
return { return {
locales: isObject(object.locales) locales: isObject(object.locales)
? Object.entries(object.locales).reduce<{ [key: string]: string }>((acc, [key, value]) => { ? (globalThis.Object.entries(object.locales) as [string, any][]).reduce(
acc[key] = String(value); (acc: { [key: string]: string }, [key, value]: [string, any]) => {
return acc; acc[key] = globalThis.String(value);
}, {}) return acc;
},
{},
)
: {}, : {},
}; };
}, },
@@ -71,7 +74,7 @@ export const I18nString: MessageFns<I18nString> = {
toJSON(message: I18nString): unknown { toJSON(message: I18nString): unknown {
const obj: any = {}; const obj: any = {};
if (message.locales) { if (message.locales) {
const entries = Object.entries(message.locales); const entries = globalThis.Object.entries(message.locales) as [string, string][];
if (entries.length > 0) { if (entries.length > 0) {
obj.locales = {}; obj.locales = {};
entries.forEach(([k, v]) => { entries.forEach(([k, v]) => {
@@ -87,26 +90,29 @@ export const I18nString: MessageFns<I18nString> = {
}, },
fromPartial<I extends Exact<DeepPartial<I18nString>, I>>(object: I): I18nString { fromPartial<I extends Exact<DeepPartial<I18nString>, I>>(object: I): I18nString {
const message = createBaseI18nString(); const message = createBaseI18nString();
message.locales = Object.entries(object.locales ?? {}).reduce<{ [key: string]: string }>((acc, [key, value]) => { message.locales = (globalThis.Object.entries(object.locales ?? {}) as [string, string][]).reduce(
if (value !== undefined) { (acc: { [key: string]: string }, [key, value]: [string, string]) => {
acc[key] = globalThis.String(value); if (value !== undefined) {
} acc[key] = globalThis.String(value);
return acc; }
}, {}); return acc;
},
{},
);
return message; return message;
}, },
}; };
export function createBaseI18nString_LocalesEntry(): I18nString_LocalesEntry { function createBaseI18nString_LocalesEntry(): I18nString_LocalesEntry {
return { key: "", value: "" }; return { key: "", value: "" };
} }
export const I18nString_LocalesEntry: MessageFns<I18nString_LocalesEntry> = { export const I18nString_LocalesEntry: MessageFns<I18nString_LocalesEntry> = {
encode(message: I18nString_LocalesEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: I18nString_LocalesEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.key !== undefined) { if (message.key !== "") {
writer.uint32(10).string(message.key); writer.uint32(10).string(message.key);
} }
if (message.value !== undefined) { if (message.value !== "") {
writer.uint32(18).string(message.value); writer.uint32(18).string(message.value);
} }
return writer; return writer;
@@ -114,7 +120,7 @@ export const I18nString_LocalesEntry: MessageFns<I18nString_LocalesEntry> = {
decode(input: BinaryReader | Uint8Array, length?: number): I18nString_LocalesEntry { decode(input: BinaryReader | Uint8Array, length?: number): I18nString_LocalesEntry {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseI18nString_LocalesEntry(); const message = createBaseI18nString_LocalesEntry();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -146,17 +152,17 @@ export const I18nString_LocalesEntry: MessageFns<I18nString_LocalesEntry> = {
fromJSON(object: any): I18nString_LocalesEntry { fromJSON(object: any): I18nString_LocalesEntry {
return { return {
key: globalThis.String(assertSet("I18nString_LocalesEntry.key", object.key)), key: isSet(object.key) ? globalThis.String(object.key) : "",
value: globalThis.String(assertSet("I18nString_LocalesEntry.value", object.value)), value: isSet(object.value) ? globalThis.String(object.value) : "",
}; };
}, },
toJSON(message: I18nString_LocalesEntry): unknown { toJSON(message: I18nString_LocalesEntry): unknown {
const obj: any = {}; const obj: any = {};
if (message.key !== undefined) { if (message.key !== "") {
obj.key = message.key; obj.key = message.key;
} }
if (message.value !== undefined) { if (message.value !== "") {
obj.value = message.value; obj.value = message.value;
} }
return obj; return obj;
@@ -194,14 +200,6 @@ function isSet(value: any): boolean {
return value !== null && value !== undefined; return value !== null && value !== undefined;
} }
function assertSet<T>(field: string, value: T | undefined): T {
if (!isSet(value)) {
throw new TypeError(`Required field ${field} is not set`);
}
return value as T;
}
export interface MessageFns<T> { export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter; encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T; decode(input: BinaryReader | Uint8Array, length?: number): T;
+15 -15
View File
@@ -1,16 +1,16 @@
// @ts-nocheck // @ts-nocheck
export * from './average_definitions.ts'; export * from "./average_definitions.ts";
export * from './camouflage_definitions.ts'; export * from "./camouflage_definitions.ts";
export * from './consumable_definitions.ts'; export * from "./consumable_definitions.ts";
export * from './equipment_definitions.ts'; export * from "./equipment_definitions.ts";
export * from './gallery.ts'; export * from "./gallery.ts";
export * from './game_definitions.ts'; export * from "./game_definitions.ts";
export * from './i18n.ts'; export * from "./i18n.ts";
export * from './map_definitions.ts'; export * from "./map_definitions.ts";
export * from './model_definitions.ts'; export * from "./model_definitions.ts";
export * from './popular_tanks.ts'; export * from "./popular_tanks.ts";
export * from './provision_definitions.ts'; export * from "./provision_definitions.ts";
export * from './rating_leaderboard.ts'; export * from "./rating_leaderboard.ts";
export * from './reviews.ts'; export * from "./reviews.ts";
export * from './skill_definitions.ts'; export * from "./skill_definitions.ts";
export * from './tank_definitions.ts'; export * from "./tank_definitions.ts";
@@ -2,13 +2,13 @@ syntax = "proto2";
package blitzkit; package blitzkit;
import "packages/core/src/protos/i18n.proto"; import "i18n.proto";
message MapDefinitions { message MapDefinitions {
map<int32, MapDefinition> maps = 1; map<int32, MapDefinition> maps = 1;
} }
message MapDefinition { message MapDefinition {
required uint32 id = 1; required uint32 id = 1;
required I18nString name = 2; required I18nString name = 2;
} }
+38 -40
View File
@@ -1,12 +1,12 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT. // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions: // versions:
// protoc-gen-ts_proto v2.2.2 // protoc-gen-ts_proto v2.11.2
// protoc v5.28.2 // protoc v5.28.2
// source: packages/core/src/protos/map_definitions.proto // source: map_definitions.proto
/* eslint-disable */ /* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { createBaseI18nString, I18nString } from "./i18n"; import { I18nString } from "./i18n";
export const protobufPackage = "blitzkit"; export const protobufPackage = "blitzkit";
@@ -21,16 +21,16 @@ export interface MapDefinitions_MapsEntry {
export interface MapDefinition { export interface MapDefinition {
id: number; id: number;
name: I18nString; name: I18nString | undefined;
} }
export function createBaseMapDefinitions(): MapDefinitions { function createBaseMapDefinitions(): MapDefinitions {
return { maps: {} }; return { maps: {} };
} }
export const MapDefinitions: MessageFns<MapDefinitions> = { export const MapDefinitions: MessageFns<MapDefinitions> = {
encode(message: MapDefinitions, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: MapDefinitions, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
Object.entries(message.maps).forEach(([key, value]) => { globalThis.Object.entries(message.maps).forEach(([key, value]: [string, MapDefinition]) => {
MapDefinitions_MapsEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join(); MapDefinitions_MapsEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join();
}); });
return writer; return writer;
@@ -38,7 +38,7 @@ export const MapDefinitions: MessageFns<MapDefinitions> = {
decode(input: BinaryReader | Uint8Array, length?: number): MapDefinitions { decode(input: BinaryReader | Uint8Array, length?: number): MapDefinitions {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseMapDefinitions(); const message = createBaseMapDefinitions();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -66,10 +66,13 @@ export const MapDefinitions: MessageFns<MapDefinitions> = {
fromJSON(object: any): MapDefinitions { fromJSON(object: any): MapDefinitions {
return { return {
maps: isObject(object.maps) maps: isObject(object.maps)
? Object.entries(object.maps).reduce<{ [key: number]: MapDefinition }>((acc, [key, value]) => { ? (globalThis.Object.entries(object.maps) as [string, any][]).reduce(
acc[globalThis.Number(key)] = MapDefinition.fromJSON(value); (acc: { [key: number]: MapDefinition }, [key, value]: [string, any]) => {
return acc; acc[globalThis.Number(key)] = MapDefinition.fromJSON(value);
}, {}) return acc;
},
{},
)
: {}, : {},
}; };
}, },
@@ -77,7 +80,7 @@ export const MapDefinitions: MessageFns<MapDefinitions> = {
toJSON(message: MapDefinitions): unknown { toJSON(message: MapDefinitions): unknown {
const obj: any = {}; const obj: any = {};
if (message.maps) { if (message.maps) {
const entries = Object.entries(message.maps); const entries = globalThis.Object.entries(message.maps) as [string, MapDefinition][];
if (entries.length > 0) { if (entries.length > 0) {
obj.maps = {}; obj.maps = {};
entries.forEach(([k, v]) => { entries.forEach(([k, v]) => {
@@ -93,23 +96,26 @@ export const MapDefinitions: MessageFns<MapDefinitions> = {
}, },
fromPartial<I extends Exact<DeepPartial<MapDefinitions>, I>>(object: I): MapDefinitions { fromPartial<I extends Exact<DeepPartial<MapDefinitions>, I>>(object: I): MapDefinitions {
const message = createBaseMapDefinitions(); const message = createBaseMapDefinitions();
message.maps = Object.entries(object.maps ?? {}).reduce<{ [key: number]: MapDefinition }>((acc, [key, value]) => { message.maps = (globalThis.Object.entries(object.maps ?? {}) as [string, MapDefinition][]).reduce(
if (value !== undefined) { (acc: { [key: number]: MapDefinition }, [key, value]: [string, MapDefinition]) => {
acc[globalThis.Number(key)] = MapDefinition.fromPartial(value); if (value !== undefined) {
} acc[globalThis.Number(key)] = MapDefinition.fromPartial(value);
return acc; }
}, {}); return acc;
},
{},
);
return message; return message;
}, },
}; };
export function createBaseMapDefinitions_MapsEntry(): MapDefinitions_MapsEntry { function createBaseMapDefinitions_MapsEntry(): MapDefinitions_MapsEntry {
return { key: 0, value: createBaseMapDefinition() }; return { key: 0, value: undefined };
} }
export const MapDefinitions_MapsEntry: MessageFns<MapDefinitions_MapsEntry> = { export const MapDefinitions_MapsEntry: MessageFns<MapDefinitions_MapsEntry> = {
encode(message: MapDefinitions_MapsEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: MapDefinitions_MapsEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.key !== undefined) { if (message.key !== 0) {
writer.uint32(8).int32(message.key); writer.uint32(8).int32(message.key);
} }
if (message.value !== undefined) { if (message.value !== undefined) {
@@ -120,7 +126,7 @@ export const MapDefinitions_MapsEntry: MessageFns<MapDefinitions_MapsEntry> = {
decode(input: BinaryReader | Uint8Array, length?: number): MapDefinitions_MapsEntry { decode(input: BinaryReader | Uint8Array, length?: number): MapDefinitions_MapsEntry {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseMapDefinitions_MapsEntry(); const message = createBaseMapDefinitions_MapsEntry();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -152,14 +158,14 @@ export const MapDefinitions_MapsEntry: MessageFns<MapDefinitions_MapsEntry> = {
fromJSON(object: any): MapDefinitions_MapsEntry { fromJSON(object: any): MapDefinitions_MapsEntry {
return { return {
key: globalThis.Number(assertSet("MapDefinitions_MapsEntry.key", object.key)), key: isSet(object.key) ? globalThis.Number(object.key) : 0,
value: MapDefinition.fromJSON(assertSet("MapDefinitions_MapsEntry.value", object.value)), value: isSet(object.value) ? MapDefinition.fromJSON(object.value) : undefined,
}; };
}, },
toJSON(message: MapDefinitions_MapsEntry): unknown { toJSON(message: MapDefinitions_MapsEntry): unknown {
const obj: any = {}; const obj: any = {};
if (message.key !== undefined) { if (message.key !== 0) {
obj.key = Math.round(message.key); obj.key = Math.round(message.key);
} }
if (message.value !== undefined) { if (message.value !== undefined) {
@@ -176,13 +182,13 @@ export const MapDefinitions_MapsEntry: MessageFns<MapDefinitions_MapsEntry> = {
message.key = object.key ?? 0; message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null) message.value = (object.value !== undefined && object.value !== null)
? MapDefinition.fromPartial(object.value) ? MapDefinition.fromPartial(object.value)
: createBaseMapDefinition(); : undefined;
return message; return message;
}, },
}; };
export function createBaseMapDefinition(): MapDefinition { function createBaseMapDefinition(): MapDefinition {
return { id: 0, name: createBaseI18nString() }; return { id: 0, name: undefined };
} }
export const MapDefinition: MessageFns<MapDefinition> = { export const MapDefinition: MessageFns<MapDefinition> = {
@@ -198,7 +204,7 @@ export const MapDefinition: MessageFns<MapDefinition> = {
decode(input: BinaryReader | Uint8Array, length?: number): MapDefinition { decode(input: BinaryReader | Uint8Array, length?: number): MapDefinition {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseMapDefinition(); const message = createBaseMapDefinition();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -230,8 +236,8 @@ export const MapDefinition: MessageFns<MapDefinition> = {
fromJSON(object: any): MapDefinition { fromJSON(object: any): MapDefinition {
return { return {
id: globalThis.Number(assertSet("MapDefinition.id", object.id)), id: isSet(object.id) ? globalThis.Number(object.id) : 0,
name: I18nString.fromJSON(assertSet("MapDefinition.name", object.name)), name: isSet(object.name) ? I18nString.fromJSON(object.name) : undefined,
}; };
}, },
@@ -254,7 +260,7 @@ export const MapDefinition: MessageFns<MapDefinition> = {
message.id = object.id ?? 0; message.id = object.id ?? 0;
message.name = (object.name !== undefined && object.name !== null) message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name) ? I18nString.fromPartial(object.name)
: createBaseI18nString(); : undefined;
return message; return message;
}, },
}; };
@@ -280,14 +286,6 @@ function isSet(value: any): boolean {
return value !== null && value !== undefined; return value !== null && value !== undefined;
} }
function assertSet<T>(field: string, value: T | undefined): T {
if (!isSet(value)) {
throw new TypeError(`Required field ${field} is not set`);
}
return value as T;
}
export interface MessageFns<T> { export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter; encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T; decode(input: BinaryReader | Uint8Array, length?: number): T;
@@ -7,17 +7,17 @@ message ModelDefinitions {
} }
message ModelDefinition { message ModelDefinition {
required Armor armor = 1; required Armor armor = 1;
required Vector3 turret_origin = 2; required Vector3 turret_origin = 2;
optional InitialTurretRotation initial_turret_rotation = 3; optional InitialTurretRotation initial_turret_rotation = 3;
map<uint32, TurretModelDefinition> turrets = 4; map<uint32, TurretModelDefinition> turrets = 4;
map<uint32, TrackModelDefinition> tracks = 5; map<uint32, TrackModelDefinition> tracks = 5;
required BoundingBox bounding_box = 6; required BoundingBox bounding_box = 6;
} }
message Armor { message Armor {
map<uint32, float> thickness = 1; map<uint32, float> thickness = 1;
repeated uint32 spaced = 2; repeated uint32 spaced = 2;
} }
message Vector3 { message Vector3 {
@@ -27,18 +27,18 @@ message Vector3 {
} }
message InitialTurretRotation { message InitialTurretRotation {
required float yaw = 1; required float yaw = 1;
required float pitch = 2; required float pitch = 2;
required float roll = 3; required float roll = 3;
} }
message TurretModelDefinition { message TurretModelDefinition {
required BoundingBox bounding_box = 1; required BoundingBox bounding_box = 1;
required Armor armor = 2; required Armor armor = 2;
required uint32 model_id = 3; required uint32 model_id = 3;
required Vector3 gun_origin = 4; required Vector3 gun_origin = 4;
map<uint32, GunModelDefinition> guns = 5; map<uint32, GunModelDefinition> guns = 5;
optional YawLimits yaw = 6; optional YawLimits yaw = 6;
} }
message YawLimits { message YawLimits {
@@ -52,28 +52,28 @@ message BoundingBox {
} }
message TrackModelDefinition { message TrackModelDefinition {
required float thickness = 1; required float thickness = 1;
required Vector3 origin = 2; required Vector3 origin = 2;
} }
message GunModelDefinition { message GunModelDefinition {
required Armor armor = 1; required Armor armor = 1;
required float thickness = 2; required float thickness = 2;
required uint32 model_id = 3; required uint32 model_id = 3;
required PitchLimits pitch = 4; required PitchLimits pitch = 4;
optional float mask = 5; optional float mask = 5;
} }
message PitchLimits { message PitchLimits {
required float min = 1; required float min = 1;
required float max = 2; required float max = 2;
optional PitchLimitsExtrema front = 3; optional PitchLimitsExtrema front = 3;
optional PitchLimitsExtrema back = 4; optional PitchLimitsExtrema back = 4;
optional float transition = 5; optional float transition = 5;
} }
message PitchLimitsExtrema { message PitchLimitsExtrema {
required float min = 1; required float min = 1;
required float max = 2; required float max = 2;
required float range = 3; required float range = 3;
} }
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -7,6 +7,6 @@ message PopularTanks {
} }
message PopularTank { message PopularTank {
required uint32 id = 1; required uint32 id = 1;
required uint32 views = 2; // views in the last 7 days required uint32 views = 2; // views in the last 7 days
} }
+8 -16
View File
@@ -1,8 +1,8 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT. // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions: // versions:
// protoc-gen-ts_proto v2.2.2 // protoc-gen-ts_proto v2.11.2
// protoc v5.28.2 // protoc v5.28.2
// source: packages/core/src/protos/popular_tanks.proto // source: popular_tanks.proto
/* eslint-disable */ /* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
@@ -19,7 +19,7 @@ export interface PopularTank {
views: number; views: number;
} }
export function createBasePopularTanks(): PopularTanks { function createBasePopularTanks(): PopularTanks {
return { tanks: [] }; return { tanks: [] };
} }
@@ -33,7 +33,7 @@ export const PopularTanks: MessageFns<PopularTanks> = {
decode(input: BinaryReader | Uint8Array, length?: number): PopularTanks { decode(input: BinaryReader | Uint8Array, length?: number): PopularTanks {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBasePopularTanks(); const message = createBasePopularTanks();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -79,7 +79,7 @@ export const PopularTanks: MessageFns<PopularTanks> = {
}, },
}; };
export function createBasePopularTank(): PopularTank { function createBasePopularTank(): PopularTank {
return { id: 0, views: 0 }; return { id: 0, views: 0 };
} }
@@ -96,7 +96,7 @@ export const PopularTank: MessageFns<PopularTank> = {
decode(input: BinaryReader | Uint8Array, length?: number): PopularTank { decode(input: BinaryReader | Uint8Array, length?: number): PopularTank {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBasePopularTank(); const message = createBasePopularTank();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -128,8 +128,8 @@ export const PopularTank: MessageFns<PopularTank> = {
fromJSON(object: any): PopularTank { fromJSON(object: any): PopularTank {
return { return {
id: globalThis.Number(assertSet("PopularTank.id", object.id)), id: isSet(object.id) ? globalThis.Number(object.id) : 0,
views: globalThis.Number(assertSet("PopularTank.views", object.views)), views: isSet(object.views) ? globalThis.Number(object.views) : 0,
}; };
}, },
@@ -172,14 +172,6 @@ function isSet(value: any): boolean {
return value !== null && value !== undefined; return value !== null && value !== undefined;
} }
function assertSet<T>(field: string, value: T | undefined): T {
if (!isSet(value)) {
throw new TypeError(`Required field ${field} is not set`);
}
return value as T;
}
export interface MessageFns<T> { export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter; encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T; decode(input: BinaryReader | Uint8Array, length?: number): T;
@@ -2,18 +2,18 @@ syntax = "proto2";
package blitzkit; package blitzkit;
import "packages/core/src/protos/consumable_definitions.proto"; import "consumable_definitions.proto";
import "packages/core/src/protos/i18n.proto"; import "i18n.proto";
message ProvisionDefinitions { message ProvisionDefinitions {
map<uint32, Provision> provisions = 1; map<uint32, Provision> provisions = 1;
} }
message Provision { message Provision {
required uint32 id = 1; required uint32 id = 1;
required I18nString name = 2; required I18nString name = 2;
optional float crew = 3; optional float crew = 3;
required bool game_mode_exclusive = 5; required bool game_mode_exclusive = 5;
repeated ConsumableTankFilter include = 6; repeated ConsumableTankFilter include = 6;
repeated ConsumableTankFilter exclude = 7; repeated ConsumableTankFilter exclude = 7;
} }
@@ -1,13 +1,13 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT. // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions: // versions:
// protoc-gen-ts_proto v2.2.2 // protoc-gen-ts_proto v2.11.2
// protoc v5.28.2 // protoc v5.28.2
// source: packages/core/src/protos/provision_definitions.proto // source: provision_definitions.proto
/* eslint-disable */ /* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
import { ConsumableTankFilter } from "./consumable_definitions"; import { ConsumableTankFilter } from "./consumable_definitions";
import { createBaseI18nString, I18nString } from "./i18n"; import { I18nString } from "./i18n";
export const protobufPackage = "blitzkit"; export const protobufPackage = "blitzkit";
@@ -22,20 +22,20 @@ export interface ProvisionDefinitions_ProvisionsEntry {
export interface Provision { export interface Provision {
id: number; id: number;
name: I18nString; name: I18nString | undefined;
crew?: number | undefined; crew?: number | undefined;
game_mode_exclusive: boolean; game_mode_exclusive: boolean;
include: ConsumableTankFilter[]; include: ConsumableTankFilter[];
exclude: ConsumableTankFilter[]; exclude: ConsumableTankFilter[];
} }
export function createBaseProvisionDefinitions(): ProvisionDefinitions { function createBaseProvisionDefinitions(): ProvisionDefinitions {
return { provisions: {} }; return { provisions: {} };
} }
export const ProvisionDefinitions: MessageFns<ProvisionDefinitions> = { export const ProvisionDefinitions: MessageFns<ProvisionDefinitions> = {
encode(message: ProvisionDefinitions, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: ProvisionDefinitions, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
Object.entries(message.provisions).forEach(([key, value]) => { globalThis.Object.entries(message.provisions).forEach(([key, value]: [string, Provision]) => {
ProvisionDefinitions_ProvisionsEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join(); ProvisionDefinitions_ProvisionsEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join();
}); });
return writer; return writer;
@@ -43,7 +43,7 @@ export const ProvisionDefinitions: MessageFns<ProvisionDefinitions> = {
decode(input: BinaryReader | Uint8Array, length?: number): ProvisionDefinitions { decode(input: BinaryReader | Uint8Array, length?: number): ProvisionDefinitions {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseProvisionDefinitions(); const message = createBaseProvisionDefinitions();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -71,10 +71,13 @@ export const ProvisionDefinitions: MessageFns<ProvisionDefinitions> = {
fromJSON(object: any): ProvisionDefinitions { fromJSON(object: any): ProvisionDefinitions {
return { return {
provisions: isObject(object.provisions) provisions: isObject(object.provisions)
? Object.entries(object.provisions).reduce<{ [key: number]: Provision }>((acc, [key, value]) => { ? (globalThis.Object.entries(object.provisions) as [string, any][]).reduce(
acc[globalThis.Number(key)] = Provision.fromJSON(value); (acc: { [key: number]: Provision }, [key, value]: [string, any]) => {
return acc; acc[globalThis.Number(key)] = Provision.fromJSON(value);
}, {}) return acc;
},
{},
)
: {}, : {},
}; };
}, },
@@ -82,7 +85,7 @@ export const ProvisionDefinitions: MessageFns<ProvisionDefinitions> = {
toJSON(message: ProvisionDefinitions): unknown { toJSON(message: ProvisionDefinitions): unknown {
const obj: any = {}; const obj: any = {};
if (message.provisions) { if (message.provisions) {
const entries = Object.entries(message.provisions); const entries = globalThis.Object.entries(message.provisions) as [string, Provision][];
if (entries.length > 0) { if (entries.length > 0) {
obj.provisions = {}; obj.provisions = {};
entries.forEach(([k, v]) => { entries.forEach(([k, v]) => {
@@ -98,8 +101,8 @@ export const ProvisionDefinitions: MessageFns<ProvisionDefinitions> = {
}, },
fromPartial<I extends Exact<DeepPartial<ProvisionDefinitions>, I>>(object: I): ProvisionDefinitions { fromPartial<I extends Exact<DeepPartial<ProvisionDefinitions>, I>>(object: I): ProvisionDefinitions {
const message = createBaseProvisionDefinitions(); const message = createBaseProvisionDefinitions();
message.provisions = Object.entries(object.provisions ?? {}).reduce<{ [key: number]: Provision }>( message.provisions = (globalThis.Object.entries(object.provisions ?? {}) as [string, Provision][]).reduce(
(acc, [key, value]) => { (acc: { [key: number]: Provision }, [key, value]: [string, Provision]) => {
if (value !== undefined) { if (value !== undefined) {
acc[globalThis.Number(key)] = Provision.fromPartial(value); acc[globalThis.Number(key)] = Provision.fromPartial(value);
} }
@@ -111,13 +114,13 @@ export const ProvisionDefinitions: MessageFns<ProvisionDefinitions> = {
}, },
}; };
export function createBaseProvisionDefinitions_ProvisionsEntry(): ProvisionDefinitions_ProvisionsEntry { function createBaseProvisionDefinitions_ProvisionsEntry(): ProvisionDefinitions_ProvisionsEntry {
return { key: 0, value: createBaseProvision() }; return { key: 0, value: undefined };
} }
export const ProvisionDefinitions_ProvisionsEntry: MessageFns<ProvisionDefinitions_ProvisionsEntry> = { export const ProvisionDefinitions_ProvisionsEntry: MessageFns<ProvisionDefinitions_ProvisionsEntry> = {
encode(message: ProvisionDefinitions_ProvisionsEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: ProvisionDefinitions_ProvisionsEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.key !== undefined) { if (message.key !== 0) {
writer.uint32(8).uint32(message.key); writer.uint32(8).uint32(message.key);
} }
if (message.value !== undefined) { if (message.value !== undefined) {
@@ -128,7 +131,7 @@ export const ProvisionDefinitions_ProvisionsEntry: MessageFns<ProvisionDefinitio
decode(input: BinaryReader | Uint8Array, length?: number): ProvisionDefinitions_ProvisionsEntry { decode(input: BinaryReader | Uint8Array, length?: number): ProvisionDefinitions_ProvisionsEntry {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseProvisionDefinitions_ProvisionsEntry(); const message = createBaseProvisionDefinitions_ProvisionsEntry();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -160,14 +163,14 @@ export const ProvisionDefinitions_ProvisionsEntry: MessageFns<ProvisionDefinitio
fromJSON(object: any): ProvisionDefinitions_ProvisionsEntry { fromJSON(object: any): ProvisionDefinitions_ProvisionsEntry {
return { return {
key: globalThis.Number(assertSet("ProvisionDefinitions_ProvisionsEntry.key", object.key)), key: isSet(object.key) ? globalThis.Number(object.key) : 0,
value: Provision.fromJSON(assertSet("ProvisionDefinitions_ProvisionsEntry.value", object.value)), value: isSet(object.value) ? Provision.fromJSON(object.value) : undefined,
}; };
}, },
toJSON(message: ProvisionDefinitions_ProvisionsEntry): unknown { toJSON(message: ProvisionDefinitions_ProvisionsEntry): unknown {
const obj: any = {}; const obj: any = {};
if (message.key !== undefined) { if (message.key !== 0) {
obj.key = Math.round(message.key); obj.key = Math.round(message.key);
} }
if (message.value !== undefined) { if (message.value !== undefined) {
@@ -188,13 +191,13 @@ export const ProvisionDefinitions_ProvisionsEntry: MessageFns<ProvisionDefinitio
message.key = object.key ?? 0; message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null) message.value = (object.value !== undefined && object.value !== null)
? Provision.fromPartial(object.value) ? Provision.fromPartial(object.value)
: createBaseProvision(); : undefined;
return message; return message;
}, },
}; };
export function createBaseProvision(): Provision { function createBaseProvision(): Provision {
return { id: 0, name: createBaseI18nString(), crew: undefined, game_mode_exclusive: false, include: [], exclude: [] }; return { id: 0, name: undefined, crew: 0, game_mode_exclusive: false, include: [], exclude: [] };
} }
export const Provision: MessageFns<Provision> = { export const Provision: MessageFns<Provision> = {
@@ -205,7 +208,7 @@ export const Provision: MessageFns<Provision> = {
if (message.name !== undefined) { if (message.name !== undefined) {
I18nString.encode(message.name, writer.uint32(18).fork()).join(); I18nString.encode(message.name, writer.uint32(18).fork()).join();
} }
if (message.crew !== undefined && message.crew !== undefined) { if (message.crew !== undefined && message.crew !== 0) {
writer.uint32(29).float(message.crew); writer.uint32(29).float(message.crew);
} }
if (message.game_mode_exclusive !== false) { if (message.game_mode_exclusive !== false) {
@@ -222,7 +225,7 @@ export const Provision: MessageFns<Provision> = {
decode(input: BinaryReader | Uint8Array, length?: number): Provision { decode(input: BinaryReader | Uint8Array, length?: number): Provision {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseProvision(); const message = createBaseProvision();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -286,10 +289,14 @@ export const Provision: MessageFns<Provision> = {
fromJSON(object: any): Provision { fromJSON(object: any): Provision {
return { return {
id: globalThis.Number(assertSet("Provision.id", object.id)), id: isSet(object.id) ? globalThis.Number(object.id) : 0,
name: I18nString.fromJSON(assertSet("Provision.name", object.name)), name: isSet(object.name) ? I18nString.fromJSON(object.name) : undefined,
crew: isSet(object.crew) ? globalThis.Number(object.crew) : undefined, crew: isSet(object.crew) ? globalThis.Number(object.crew) : 0,
game_mode_exclusive: globalThis.Boolean(assertSet("Provision.game_mode_exclusive", object.game_mode_exclusive)), game_mode_exclusive: isSet(object.gameModeExclusive)
? globalThis.Boolean(object.gameModeExclusive)
: isSet(object.game_mode_exclusive)
? globalThis.Boolean(object.game_mode_exclusive)
: false,
include: globalThis.Array.isArray(object?.include) include: globalThis.Array.isArray(object?.include)
? object.include.map((e: any) => ConsumableTankFilter.fromJSON(e)) ? object.include.map((e: any) => ConsumableTankFilter.fromJSON(e))
: [], : [],
@@ -307,11 +314,11 @@ export const Provision: MessageFns<Provision> = {
if (message.name !== undefined) { if (message.name !== undefined) {
obj.name = I18nString.toJSON(message.name); obj.name = I18nString.toJSON(message.name);
} }
if (message.crew !== undefined && message.crew !== undefined) { if (message.crew !== undefined && message.crew !== 0) {
obj.crew = message.crew; obj.crew = message.crew;
} }
if (message.game_mode_exclusive !== false) { if (message.game_mode_exclusive !== false) {
obj.game_mode_exclusive = message.game_mode_exclusive; obj.gameModeExclusive = message.game_mode_exclusive;
} }
if (message.include?.length) { if (message.include?.length) {
obj.include = message.include.map((e) => ConsumableTankFilter.toJSON(e)); obj.include = message.include.map((e) => ConsumableTankFilter.toJSON(e));
@@ -330,8 +337,8 @@ export const Provision: MessageFns<Provision> = {
message.id = object.id ?? 0; message.id = object.id ?? 0;
message.name = (object.name !== undefined && object.name !== null) message.name = (object.name !== undefined && object.name !== null)
? I18nString.fromPartial(object.name) ? I18nString.fromPartial(object.name)
: createBaseI18nString(); : undefined;
message.crew = object.crew ?? undefined; message.crew = object.crew ?? 0;
message.game_mode_exclusive = object.game_mode_exclusive ?? false; message.game_mode_exclusive = object.game_mode_exclusive ?? false;
message.include = object.include?.map((e) => ConsumableTankFilter.fromPartial(e)) || []; message.include = object.include?.map((e) => ConsumableTankFilter.fromPartial(e)) || [];
message.exclude = object.exclude?.map((e) => ConsumableTankFilter.fromPartial(e)) || []; message.exclude = object.exclude?.map((e) => ConsumableTankFilter.fromPartial(e)) || [];
@@ -360,14 +367,6 @@ function isSet(value: any): boolean {
return value !== null && value !== undefined; return value !== null && value !== undefined;
} }
function assertSet<T>(field: string, value: T | undefined): T {
if (!isSet(value)) {
throw new TypeError(`Required field ${field} is not set`);
}
return value as T;
}
export interface MessageFns<T> { export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter; encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T; decode(input: BinaryReader | Uint8Array, length?: number): T;
@@ -14,7 +14,7 @@ message RatingLeaderboardV1 {
} }
message RatingLeaderboardEntryV1 { message RatingLeaderboardEntryV1 {
required uint32 id = 1; required uint32 id = 1;
required uint32 score = 2; required uint32 score = 2;
} }
@@ -23,11 +23,11 @@ message RatingLeaderboardV2 {
} }
message RatingLeaderboardEntryV2 { message RatingLeaderboardEntryV2 {
required uint32 id = 1; required uint32 id = 1;
required uint32 score = 2; required uint32 score = 2;
required uint32 battles = 3; required uint32 battles = 3;
required uint32 wins = 4; required uint32 wins = 4;
required uint32 survived = 5; required uint32 survived = 5;
required uint32 damage = 6; required uint32 damage = 6;
required uint32 kills = 7; required uint32 kills = 7;
} }
+35 -36
View File
@@ -1,8 +1,8 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT. // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions: // versions:
// protoc-gen-ts_proto v2.2.2 // protoc-gen-ts_proto v2.11.2
// protoc v5.28.2 // protoc v5.28.2
// source: packages/core/src/protos/rating_leaderboard.proto // source: rating_leaderboard.proto
/* eslint-disable */ /* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
@@ -36,7 +36,7 @@ export interface RatingLeaderboardEntryV2 {
kills: number; kills: number;
} }
export function createBaseRatingLeaderboard(): RatingLeaderboard { function createBaseRatingLeaderboard(): RatingLeaderboard {
return { version: undefined }; return { version: undefined };
} }
@@ -55,7 +55,7 @@ export const RatingLeaderboard: MessageFns<RatingLeaderboard> = {
decode(input: BinaryReader | Uint8Array, length?: number): RatingLeaderboard { decode(input: BinaryReader | Uint8Array, length?: number): RatingLeaderboard {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseRatingLeaderboard(); const message = createBaseRatingLeaderboard();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -99,8 +99,7 @@ export const RatingLeaderboard: MessageFns<RatingLeaderboard> = {
const obj: any = {}; const obj: any = {};
if (message.version?.$case === "v1") { if (message.version?.$case === "v1") {
obj.v1 = RatingLeaderboardV1.toJSON(message.version.value); obj.v1 = RatingLeaderboardV1.toJSON(message.version.value);
} } else if (message.version?.$case === "v2") {
if (message.version?.$case === "v2") {
obj.v2 = RatingLeaderboardV2.toJSON(message.version.value); obj.v2 = RatingLeaderboardV2.toJSON(message.version.value);
} }
return obj; return obj;
@@ -111,17 +110,25 @@ export const RatingLeaderboard: MessageFns<RatingLeaderboard> = {
}, },
fromPartial<I extends Exact<DeepPartial<RatingLeaderboard>, I>>(object: I): RatingLeaderboard { fromPartial<I extends Exact<DeepPartial<RatingLeaderboard>, I>>(object: I): RatingLeaderboard {
const message = createBaseRatingLeaderboard(); const message = createBaseRatingLeaderboard();
if (object.version?.$case === "v1" && object.version?.value !== undefined && object.version?.value !== null) { switch (object.version?.$case) {
message.version = { $case: "v1", value: RatingLeaderboardV1.fromPartial(object.version.value) }; case "v1": {
} if (object.version?.value !== undefined && object.version?.value !== null) {
if (object.version?.$case === "v2" && object.version?.value !== undefined && object.version?.value !== null) { message.version = { $case: "v1", value: RatingLeaderboardV1.fromPartial(object.version.value) };
message.version = { $case: "v2", value: RatingLeaderboardV2.fromPartial(object.version.value) }; }
break;
}
case "v2": {
if (object.version?.value !== undefined && object.version?.value !== null) {
message.version = { $case: "v2", value: RatingLeaderboardV2.fromPartial(object.version.value) };
}
break;
}
} }
return message; return message;
}, },
}; };
export function createBaseRatingLeaderboardV1(): RatingLeaderboardV1 { function createBaseRatingLeaderboardV1(): RatingLeaderboardV1 {
return { entries: [] }; return { entries: [] };
} }
@@ -135,7 +142,7 @@ export const RatingLeaderboardV1: MessageFns<RatingLeaderboardV1> = {
decode(input: BinaryReader | Uint8Array, length?: number): RatingLeaderboardV1 { decode(input: BinaryReader | Uint8Array, length?: number): RatingLeaderboardV1 {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseRatingLeaderboardV1(); const message = createBaseRatingLeaderboardV1();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -183,7 +190,7 @@ export const RatingLeaderboardV1: MessageFns<RatingLeaderboardV1> = {
}, },
}; };
export function createBaseRatingLeaderboardEntryV1(): RatingLeaderboardEntryV1 { function createBaseRatingLeaderboardEntryV1(): RatingLeaderboardEntryV1 {
return { id: 0, score: 0 }; return { id: 0, score: 0 };
} }
@@ -200,7 +207,7 @@ export const RatingLeaderboardEntryV1: MessageFns<RatingLeaderboardEntryV1> = {
decode(input: BinaryReader | Uint8Array, length?: number): RatingLeaderboardEntryV1 { decode(input: BinaryReader | Uint8Array, length?: number): RatingLeaderboardEntryV1 {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseRatingLeaderboardEntryV1(); const message = createBaseRatingLeaderboardEntryV1();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -232,8 +239,8 @@ export const RatingLeaderboardEntryV1: MessageFns<RatingLeaderboardEntryV1> = {
fromJSON(object: any): RatingLeaderboardEntryV1 { fromJSON(object: any): RatingLeaderboardEntryV1 {
return { return {
id: globalThis.Number(assertSet("RatingLeaderboardEntryV1.id", object.id)), id: isSet(object.id) ? globalThis.Number(object.id) : 0,
score: globalThis.Number(assertSet("RatingLeaderboardEntryV1.score", object.score)), score: isSet(object.score) ? globalThis.Number(object.score) : 0,
}; };
}, },
@@ -259,7 +266,7 @@ export const RatingLeaderboardEntryV1: MessageFns<RatingLeaderboardEntryV1> = {
}, },
}; };
export function createBaseRatingLeaderboardV2(): RatingLeaderboardV2 { function createBaseRatingLeaderboardV2(): RatingLeaderboardV2 {
return { entries: [] }; return { entries: [] };
} }
@@ -273,7 +280,7 @@ export const RatingLeaderboardV2: MessageFns<RatingLeaderboardV2> = {
decode(input: BinaryReader | Uint8Array, length?: number): RatingLeaderboardV2 { decode(input: BinaryReader | Uint8Array, length?: number): RatingLeaderboardV2 {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseRatingLeaderboardV2(); const message = createBaseRatingLeaderboardV2();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -321,7 +328,7 @@ export const RatingLeaderboardV2: MessageFns<RatingLeaderboardV2> = {
}, },
}; };
export function createBaseRatingLeaderboardEntryV2(): RatingLeaderboardEntryV2 { function createBaseRatingLeaderboardEntryV2(): RatingLeaderboardEntryV2 {
return { id: 0, score: 0, battles: 0, wins: 0, survived: 0, damage: 0, kills: 0 }; return { id: 0, score: 0, battles: 0, wins: 0, survived: 0, damage: 0, kills: 0 };
} }
@@ -353,7 +360,7 @@ export const RatingLeaderboardEntryV2: MessageFns<RatingLeaderboardEntryV2> = {
decode(input: BinaryReader | Uint8Array, length?: number): RatingLeaderboardEntryV2 { decode(input: BinaryReader | Uint8Array, length?: number): RatingLeaderboardEntryV2 {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseRatingLeaderboardEntryV2(); const message = createBaseRatingLeaderboardEntryV2();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -425,13 +432,13 @@ export const RatingLeaderboardEntryV2: MessageFns<RatingLeaderboardEntryV2> = {
fromJSON(object: any): RatingLeaderboardEntryV2 { fromJSON(object: any): RatingLeaderboardEntryV2 {
return { return {
id: globalThis.Number(assertSet("RatingLeaderboardEntryV2.id", object.id)), id: isSet(object.id) ? globalThis.Number(object.id) : 0,
score: globalThis.Number(assertSet("RatingLeaderboardEntryV2.score", object.score)), score: isSet(object.score) ? globalThis.Number(object.score) : 0,
battles: globalThis.Number(assertSet("RatingLeaderboardEntryV2.battles", object.battles)), battles: isSet(object.battles) ? globalThis.Number(object.battles) : 0,
wins: globalThis.Number(assertSet("RatingLeaderboardEntryV2.wins", object.wins)), wins: isSet(object.wins) ? globalThis.Number(object.wins) : 0,
survived: globalThis.Number(assertSet("RatingLeaderboardEntryV2.survived", object.survived)), survived: isSet(object.survived) ? globalThis.Number(object.survived) : 0,
damage: globalThis.Number(assertSet("RatingLeaderboardEntryV2.damage", object.damage)), damage: isSet(object.damage) ? globalThis.Number(object.damage) : 0,
kills: globalThis.Number(assertSet("RatingLeaderboardEntryV2.kills", object.kills)), kills: isSet(object.kills) ? globalThis.Number(object.kills) : 0,
}; };
}, },
@@ -494,14 +501,6 @@ function isSet(value: any): boolean {
return value !== null && value !== undefined; return value !== null && value !== undefined;
} }
function assertSet<T>(field: string, value: T | undefined): T {
if (!isSet(value)) {
throw new TypeError(`Required field ${field} is not set`);
}
return value as T;
}
export interface MessageFns<T> { export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter; encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T; decode(input: BinaryReader | Uint8Array, length?: number): T;
+2 -2
View File
@@ -8,10 +8,10 @@ message Reviews {
message Review { message Review {
required float last_updated = 1; required float last_updated = 1;
repeated Video videos = 2; repeated Video videos = 2;
} }
message Video { message Video {
required string id = 1; required string id = 1;
required string author = 2; required string author = 2;
} }
+42 -40
View File
@@ -1,8 +1,8 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT. // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions: // versions:
// protoc-gen-ts_proto v2.2.2 // protoc-gen-ts_proto v2.11.2
// protoc v5.28.2 // protoc v5.28.2
// source: packages/core/src/protos/reviews.proto // source: reviews.proto
/* eslint-disable */ /* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
@@ -28,13 +28,13 @@ export interface Video {
author: string; author: string;
} }
export function createBaseReviews(): Reviews { function createBaseReviews(): Reviews {
return { reviews: {} }; return { reviews: {} };
} }
export const Reviews: MessageFns<Reviews> = { export const Reviews: MessageFns<Reviews> = {
encode(message: Reviews, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: Reviews, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
Object.entries(message.reviews).forEach(([key, value]) => { globalThis.Object.entries(message.reviews).forEach(([key, value]: [string, Review]) => {
Reviews_ReviewsEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join(); Reviews_ReviewsEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join();
}); });
return writer; return writer;
@@ -42,7 +42,7 @@ export const Reviews: MessageFns<Reviews> = {
decode(input: BinaryReader | Uint8Array, length?: number): Reviews { decode(input: BinaryReader | Uint8Array, length?: number): Reviews {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseReviews(); const message = createBaseReviews();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -70,10 +70,13 @@ export const Reviews: MessageFns<Reviews> = {
fromJSON(object: any): Reviews { fromJSON(object: any): Reviews {
return { return {
reviews: isObject(object.reviews) reviews: isObject(object.reviews)
? Object.entries(object.reviews).reduce<{ [key: number]: Review }>((acc, [key, value]) => { ? (globalThis.Object.entries(object.reviews) as [string, any][]).reduce(
acc[globalThis.Number(key)] = Review.fromJSON(value); (acc: { [key: number]: Review }, [key, value]: [string, any]) => {
return acc; acc[globalThis.Number(key)] = Review.fromJSON(value);
}, {}) return acc;
},
{},
)
: {}, : {},
}; };
}, },
@@ -81,7 +84,7 @@ export const Reviews: MessageFns<Reviews> = {
toJSON(message: Reviews): unknown { toJSON(message: Reviews): unknown {
const obj: any = {}; const obj: any = {};
if (message.reviews) { if (message.reviews) {
const entries = Object.entries(message.reviews); const entries = globalThis.Object.entries(message.reviews) as [string, Review][];
if (entries.length > 0) { if (entries.length > 0) {
obj.reviews = {}; obj.reviews = {};
entries.forEach(([k, v]) => { entries.forEach(([k, v]) => {
@@ -97,23 +100,26 @@ export const Reviews: MessageFns<Reviews> = {
}, },
fromPartial<I extends Exact<DeepPartial<Reviews>, I>>(object: I): Reviews { fromPartial<I extends Exact<DeepPartial<Reviews>, I>>(object: I): Reviews {
const message = createBaseReviews(); const message = createBaseReviews();
message.reviews = Object.entries(object.reviews ?? {}).reduce<{ [key: number]: Review }>((acc, [key, value]) => { message.reviews = (globalThis.Object.entries(object.reviews ?? {}) as [string, Review][]).reduce(
if (value !== undefined) { (acc: { [key: number]: Review }, [key, value]: [string, Review]) => {
acc[globalThis.Number(key)] = Review.fromPartial(value); if (value !== undefined) {
} acc[globalThis.Number(key)] = Review.fromPartial(value);
return acc; }
}, {}); return acc;
},
{},
);
return message; return message;
}, },
}; };
export function createBaseReviews_ReviewsEntry(): Reviews_ReviewsEntry { function createBaseReviews_ReviewsEntry(): Reviews_ReviewsEntry {
return { key: 0, value: createBaseReview() }; return { key: 0, value: undefined };
} }
export const Reviews_ReviewsEntry: MessageFns<Reviews_ReviewsEntry> = { export const Reviews_ReviewsEntry: MessageFns<Reviews_ReviewsEntry> = {
encode(message: Reviews_ReviewsEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: Reviews_ReviewsEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.key !== undefined) { if (message.key !== 0) {
writer.uint32(8).uint32(message.key); writer.uint32(8).uint32(message.key);
} }
if (message.value !== undefined) { if (message.value !== undefined) {
@@ -124,7 +130,7 @@ export const Reviews_ReviewsEntry: MessageFns<Reviews_ReviewsEntry> = {
decode(input: BinaryReader | Uint8Array, length?: number): Reviews_ReviewsEntry { decode(input: BinaryReader | Uint8Array, length?: number): Reviews_ReviewsEntry {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseReviews_ReviewsEntry(); const message = createBaseReviews_ReviewsEntry();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -156,14 +162,14 @@ export const Reviews_ReviewsEntry: MessageFns<Reviews_ReviewsEntry> = {
fromJSON(object: any): Reviews_ReviewsEntry { fromJSON(object: any): Reviews_ReviewsEntry {
return { return {
key: globalThis.Number(assertSet("Reviews_ReviewsEntry.key", object.key)), key: isSet(object.key) ? globalThis.Number(object.key) : 0,
value: Review.fromJSON(assertSet("Reviews_ReviewsEntry.value", object.value)), value: isSet(object.value) ? Review.fromJSON(object.value) : undefined,
}; };
}, },
toJSON(message: Reviews_ReviewsEntry): unknown { toJSON(message: Reviews_ReviewsEntry): unknown {
const obj: any = {}; const obj: any = {};
if (message.key !== undefined) { if (message.key !== 0) {
obj.key = Math.round(message.key); obj.key = Math.round(message.key);
} }
if (message.value !== undefined) { if (message.value !== undefined) {
@@ -180,12 +186,12 @@ export const Reviews_ReviewsEntry: MessageFns<Reviews_ReviewsEntry> = {
message.key = object.key ?? 0; message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null) message.value = (object.value !== undefined && object.value !== null)
? Review.fromPartial(object.value) ? Review.fromPartial(object.value)
: createBaseReview(); : undefined;
return message; return message;
}, },
}; };
export function createBaseReview(): Review { function createBaseReview(): Review {
return { last_updated: 0, videos: [] }; return { last_updated: 0, videos: [] };
} }
@@ -202,7 +208,7 @@ export const Review: MessageFns<Review> = {
decode(input: BinaryReader | Uint8Array, length?: number): Review { decode(input: BinaryReader | Uint8Array, length?: number): Review {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseReview(); const message = createBaseReview();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -234,7 +240,11 @@ export const Review: MessageFns<Review> = {
fromJSON(object: any): Review { fromJSON(object: any): Review {
return { return {
last_updated: globalThis.Number(assertSet("Review.last_updated", object.last_updated)), last_updated: isSet(object.lastUpdated)
? globalThis.Number(object.lastUpdated)
: isSet(object.last_updated)
? globalThis.Number(object.last_updated)
: 0,
videos: globalThis.Array.isArray(object?.videos) ? object.videos.map((e: any) => Video.fromJSON(e)) : [], videos: globalThis.Array.isArray(object?.videos) ? object.videos.map((e: any) => Video.fromJSON(e)) : [],
}; };
}, },
@@ -242,7 +252,7 @@ export const Review: MessageFns<Review> = {
toJSON(message: Review): unknown { toJSON(message: Review): unknown {
const obj: any = {}; const obj: any = {};
if (message.last_updated !== 0) { if (message.last_updated !== 0) {
obj.last_updated = message.last_updated; obj.lastUpdated = message.last_updated;
} }
if (message.videos?.length) { if (message.videos?.length) {
obj.videos = message.videos.map((e) => Video.toJSON(e)); obj.videos = message.videos.map((e) => Video.toJSON(e));
@@ -261,7 +271,7 @@ export const Review: MessageFns<Review> = {
}, },
}; };
export function createBaseVideo(): Video { function createBaseVideo(): Video {
return { id: "", author: "" }; return { id: "", author: "" };
} }
@@ -278,7 +288,7 @@ export const Video: MessageFns<Video> = {
decode(input: BinaryReader | Uint8Array, length?: number): Video { decode(input: BinaryReader | Uint8Array, length?: number): Video {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseVideo(); const message = createBaseVideo();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -310,8 +320,8 @@ export const Video: MessageFns<Video> = {
fromJSON(object: any): Video { fromJSON(object: any): Video {
return { return {
id: globalThis.String(assertSet("Video.id", object.id)), id: isSet(object.id) ? globalThis.String(object.id) : "",
author: globalThis.String(assertSet("Video.author", object.author)), author: isSet(object.author) ? globalThis.String(object.author) : "",
}; };
}, },
@@ -358,14 +368,6 @@ function isSet(value: any): boolean {
return value !== null && value !== undefined; return value !== null && value !== undefined;
} }
function assertSet<T>(field: string, value: T | undefined): T {
if (!isSet(value)) {
throw new TypeError(`Required field ${field} is not set`);
}
return value as T;
}
export interface MessageFns<T> { export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter; encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T; decode(input: BinaryReader | Uint8Array, length?: number): T;
+32 -36
View File
@@ -1,8 +1,8 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT. // Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions: // versions:
// protoc-gen-ts_proto v2.2.2 // protoc-gen-ts_proto v2.11.2
// protoc v5.28.2 // protoc v5.28.2
// source: packages/core/src/protos/skill_definitions.proto // source: skill_definitions.proto
/* eslint-disable */ /* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
@@ -23,13 +23,13 @@ export interface Skill {
skills: string[]; skills: string[];
} }
export function createBaseSkillDefinitions(): SkillDefinitions { function createBaseSkillDefinitions(): SkillDefinitions {
return { classes: {} }; return { classes: {} };
} }
export const SkillDefinitions: MessageFns<SkillDefinitions> = { export const SkillDefinitions: MessageFns<SkillDefinitions> = {
encode(message: SkillDefinitions, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: SkillDefinitions, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
Object.entries(message.classes).forEach(([key, value]) => { globalThis.Object.entries(message.classes).forEach(([key, value]: [string, Skill]) => {
SkillDefinitions_ClassesEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join(); SkillDefinitions_ClassesEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).join();
}); });
return writer; return writer;
@@ -37,7 +37,7 @@ export const SkillDefinitions: MessageFns<SkillDefinitions> = {
decode(input: BinaryReader | Uint8Array, length?: number): SkillDefinitions { decode(input: BinaryReader | Uint8Array, length?: number): SkillDefinitions {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseSkillDefinitions(); const message = createBaseSkillDefinitions();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -65,10 +65,13 @@ export const SkillDefinitions: MessageFns<SkillDefinitions> = {
fromJSON(object: any): SkillDefinitions { fromJSON(object: any): SkillDefinitions {
return { return {
classes: isObject(object.classes) classes: isObject(object.classes)
? Object.entries(object.classes).reduce<{ [key: number]: Skill }>((acc, [key, value]) => { ? (globalThis.Object.entries(object.classes) as [string, any][]).reduce(
acc[globalThis.Number(key)] = Skill.fromJSON(value); (acc: { [key: number]: Skill }, [key, value]: [string, any]) => {
return acc; acc[globalThis.Number(key)] = Skill.fromJSON(value);
}, {}) return acc;
},
{},
)
: {}, : {},
}; };
}, },
@@ -76,7 +79,7 @@ export const SkillDefinitions: MessageFns<SkillDefinitions> = {
toJSON(message: SkillDefinitions): unknown { toJSON(message: SkillDefinitions): unknown {
const obj: any = {}; const obj: any = {};
if (message.classes) { if (message.classes) {
const entries = Object.entries(message.classes); const entries = globalThis.Object.entries(message.classes) as [string, Skill][];
if (entries.length > 0) { if (entries.length > 0) {
obj.classes = {}; obj.classes = {};
entries.forEach(([k, v]) => { entries.forEach(([k, v]) => {
@@ -92,23 +95,26 @@ export const SkillDefinitions: MessageFns<SkillDefinitions> = {
}, },
fromPartial<I extends Exact<DeepPartial<SkillDefinitions>, I>>(object: I): SkillDefinitions { fromPartial<I extends Exact<DeepPartial<SkillDefinitions>, I>>(object: I): SkillDefinitions {
const message = createBaseSkillDefinitions(); const message = createBaseSkillDefinitions();
message.classes = Object.entries(object.classes ?? {}).reduce<{ [key: number]: Skill }>((acc, [key, value]) => { message.classes = (globalThis.Object.entries(object.classes ?? {}) as [string, Skill][]).reduce(
if (value !== undefined) { (acc: { [key: number]: Skill }, [key, value]: [string, Skill]) => {
acc[globalThis.Number(key)] = Skill.fromPartial(value); if (value !== undefined) {
} acc[globalThis.Number(key)] = Skill.fromPartial(value);
return acc; }
}, {}); return acc;
},
{},
);
return message; return message;
}, },
}; };
export function createBaseSkillDefinitions_ClassesEntry(): SkillDefinitions_ClassesEntry { function createBaseSkillDefinitions_ClassesEntry(): SkillDefinitions_ClassesEntry {
return { key: 0, value: createBaseSkill() }; return { key: 0, value: undefined };
} }
export const SkillDefinitions_ClassesEntry: MessageFns<SkillDefinitions_ClassesEntry> = { export const SkillDefinitions_ClassesEntry: MessageFns<SkillDefinitions_ClassesEntry> = {
encode(message: SkillDefinitions_ClassesEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { encode(message: SkillDefinitions_ClassesEntry, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.key !== undefined) { if (message.key !== 0) {
writer.uint32(8).uint32(message.key); writer.uint32(8).uint32(message.key);
} }
if (message.value !== undefined) { if (message.value !== undefined) {
@@ -119,7 +125,7 @@ export const SkillDefinitions_ClassesEntry: MessageFns<SkillDefinitions_ClassesE
decode(input: BinaryReader | Uint8Array, length?: number): SkillDefinitions_ClassesEntry { decode(input: BinaryReader | Uint8Array, length?: number): SkillDefinitions_ClassesEntry {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseSkillDefinitions_ClassesEntry(); const message = createBaseSkillDefinitions_ClassesEntry();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -151,14 +157,14 @@ export const SkillDefinitions_ClassesEntry: MessageFns<SkillDefinitions_ClassesE
fromJSON(object: any): SkillDefinitions_ClassesEntry { fromJSON(object: any): SkillDefinitions_ClassesEntry {
return { return {
key: globalThis.Number(assertSet("SkillDefinitions_ClassesEntry.key", object.key)), key: isSet(object.key) ? globalThis.Number(object.key) : 0,
value: Skill.fromJSON(assertSet("SkillDefinitions_ClassesEntry.value", object.value)), value: isSet(object.value) ? Skill.fromJSON(object.value) : undefined,
}; };
}, },
toJSON(message: SkillDefinitions_ClassesEntry): unknown { toJSON(message: SkillDefinitions_ClassesEntry): unknown {
const obj: any = {}; const obj: any = {};
if (message.key !== undefined) { if (message.key !== 0) {
obj.key = Math.round(message.key); obj.key = Math.round(message.key);
} }
if (message.value !== undefined) { if (message.value !== undefined) {
@@ -175,14 +181,12 @@ export const SkillDefinitions_ClassesEntry: MessageFns<SkillDefinitions_ClassesE
): SkillDefinitions_ClassesEntry { ): SkillDefinitions_ClassesEntry {
const message = createBaseSkillDefinitions_ClassesEntry(); const message = createBaseSkillDefinitions_ClassesEntry();
message.key = object.key ?? 0; message.key = object.key ?? 0;
message.value = (object.value !== undefined && object.value !== null) message.value = (object.value !== undefined && object.value !== null) ? Skill.fromPartial(object.value) : undefined;
? Skill.fromPartial(object.value)
: createBaseSkill();
return message; return message;
}, },
}; };
export function createBaseSkill(): Skill { function createBaseSkill(): Skill {
return { skills: [] }; return { skills: [] };
} }
@@ -196,7 +200,7 @@ export const Skill: MessageFns<Skill> = {
decode(input: BinaryReader | Uint8Array, length?: number): Skill { decode(input: BinaryReader | Uint8Array, length?: number): Skill {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input); const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
let end = length === undefined ? reader.len : reader.pos + length; const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseSkill(); const message = createBaseSkill();
while (reader.pos < end) { while (reader.pos < end) {
const tag = reader.uint32(); const tag = reader.uint32();
@@ -263,14 +267,6 @@ function isSet(value: any): boolean {
return value !== null && value !== undefined; return value !== null && value !== undefined;
} }
function assertSet<T>(field: string, value: T | undefined): T {
if (!isSet(value)) {
throw new TypeError(`Required field ${field} is not set`);
}
return value as T;
}
export interface MessageFns<T> { export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter; encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T; decode(input: BinaryReader | Uint8Array, length?: number): T;
+134 -124
View File
@@ -2,48 +2,58 @@ syntax = "proto2";
package blitzkit; package blitzkit;
import "packages/core/src/protos/i18n.proto"; import "i18n.proto";
message TankDefinitions { message TankDefinitions {
map<uint32, TankDefinition> tanks = 1; map<uint32, TankDefinition> tanks = 1;
} }
message TankDefinition { message TankDefinition {
required uint32 id = 1; required uint32 id = 1;
required string slug = 2; required string slug = 2;
map<uint32, uint32> roles = 3; map<uint32, uint32> roles = 3;
required bool fixed_camouflage = 5; required bool fixed_camouflage = 5;
repeated int32 camouflages = 6; repeated int32 camouflages = 6;
repeated int32 ancestors = 7; repeated int32 ancestors = 7;
repeated int32 successors = 8; repeated int32 successors = 8;
repeated Crew crew = 9; repeated Crew crew = 9;
required uint32 health = 10; required uint32 health = 10;
required string nation = 11; required string nation = 11;
required I18nString name = 12; required I18nString name = 12;
required TankType type = 13; required TankType type = 13;
required uint32 max_consumables = 14; required uint32 max_consumables = 14;
required uint32 max_provisions = 15; required uint32 max_provisions = 15;
required uint32 tier = 16; required uint32 tier = 16;
required TankClass class = 17; required TankClass class = 17;
required bool testing = 18; required bool testing = 18;
required bool deprecated = 19; required bool deprecated = 19;
repeated TurretDefinition turrets = 20; repeated TurretDefinition turrets = 20;
repeated EngineDefinition engines = 21; repeated EngineDefinition engines = 21;
repeated TrackDefinition tracks = 22; repeated TrackDefinition tracks = 22;
required TankPrice price = 23; required TankPrice price = 23;
optional ResearchCost research_cost = 24; optional ResearchCost research_cost = 24;
required float speed_forwards = 25; required float speed_forwards = 25;
required float speed_backwards = 26; required float speed_backwards = 26;
required float camouflage_still = 27; required float camouflage_still = 27;
required float camouflage_moving = 28; required float camouflage_moving = 28;
required float camouflage_onFire = 29; required float camouflage_on_fire = 29;
required string equipment_preset = 30; required string equipment_preset = 30;
required uint32 weight = 31; required uint32 weight = 31;
required string dev_name = 32;
optional Equalizer equalizer = 33;
}
message Equalizer {
required float health = 1;
required float penetration = 2;
required float module_health = 3;
required float damage = 4;
required float armor = 5;
} }
message ResearchCost { message ResearchCost {
oneof research_cost_type { oneof research_cost_type {
uint32 xp = 1; uint32 xp = 1;
ResearchCostSeasonalTokens seasonal_tokens = 2; ResearchCostSeasonalTokens seasonal_tokens = 2;
} }
} }
@@ -55,77 +65,77 @@ message ResearchCostSeasonalTokens {
enum TankPriceType { enum TankPriceType {
TANK_PRICE_TYPE_CREDITS = 0; TANK_PRICE_TYPE_CREDITS = 0;
TANK_PRICE_TYPE_GOLD = 1; TANK_PRICE_TYPE_GOLD = 1;
} }
message TankPrice { message TankPrice {
required TankPriceType type = 1; required TankPriceType type = 1;
required uint32 value = 2; required uint32 value = 2;
} }
message TrackDefinition { message TrackDefinition {
required uint32 id = 1; required uint32 id = 1;
required uint32 tier = 2; required uint32 tier = 2;
required I18nString name = 3; required I18nString name = 3;
required uint32 weight = 4; required uint32 weight = 4;
required float traverse_speed = 5; required float traverse_speed = 5;
optional ResearchCost research_cost = 6; optional ResearchCost research_cost = 6;
required float dispersion_move = 7; required float dispersion_move = 7;
required float dispersion_traverse = 8; required float dispersion_traverse = 8;
required float resistance_hard = 9; required float resistance_hard = 9;
required float resistance_medium = 10; required float resistance_medium = 10;
required float resistance_soft = 11; required float resistance_soft = 11;
repeated Unlock unlocks = 12; repeated Unlock unlocks = 12;
} }
message EngineDefinition { message EngineDefinition {
required uint32 id = 1; required uint32 id = 1;
required I18nString name = 2; required I18nString name = 2;
optional ResearchCost research_cost = 3; optional ResearchCost research_cost = 3;
required uint32 tier = 4; required uint32 tier = 4;
required float fire_chance = 5; required float fire_chance = 5;
required uint32 power = 6; required uint32 power = 6;
required uint32 weight = 7; required uint32 weight = 7;
repeated Unlock unlocks = 8; repeated Unlock unlocks = 8;
} }
message TurretDefinition { message TurretDefinition {
required uint32 id = 1; required uint32 id = 1;
required uint32 health = 2; required uint32 health = 2;
required uint32 view_range = 3; required uint32 view_range = 3;
required float traverse_speed = 4; required float traverse_speed = 4;
optional ResearchCost research_cost = 5; optional ResearchCost research_cost = 5;
required I18nString name = 6; required I18nString name = 6;
required uint32 tier = 7; required uint32 tier = 7;
required uint32 weight = 8; required uint32 weight = 8;
repeated GunDefinition guns = 9; repeated GunDefinition guns = 9;
repeated Unlock unlocks = 10; repeated Unlock unlocks = 10;
} }
message GunDefinition { message GunDefinition {
oneof gun_type { oneof gun_type {
GunDefinitionRegular regular = 1; GunDefinitionRegular regular = 1;
GunDefinitionAutoLoader auto_loader = 2; GunDefinitionAutoLoader auto_loader = 2;
GunDefinitionAutoReloader auto_reloader = 3; GunDefinitionAutoReloader auto_reloader = 3;
} }
required uint32 id = 4; required uint32 id = 4;
required float rotation_speed = 5; required float rotation_speed = 5;
optional ResearchCost research_cost = 6; optional ResearchCost research_cost = 6;
required uint32 weight = 7; required uint32 weight = 7;
required I18nString name = 8; required I18nString name = 8;
required uint32 tier = 9; required uint32 tier = 9;
repeated ShellDefinition shells = 10; repeated ShellDefinition shells = 10;
required float camouflage_loss = 11; required float camouflage_loss = 11;
required float aim_time = 12; required float aim_time = 12;
required float dispersion_base = 13; required float dispersion_base = 13;
required float dispersion_traverse = 14; required float dispersion_traverse = 14;
required float dispersion_shot = 15; required float dispersion_shot = 15;
required float dispersion_damaged = 16; required float dispersion_damaged = 16;
repeated Unlock unlocks = 17; repeated Unlock unlocks = 17;
required uint32 shell_capacity = 18; required uint32 shell_capacity = 18;
optional AssaultRanges assault_ranges = 19; optional AssaultRanges assault_ranges = 19;
optional Burst burst = 20; optional Burst burst = 20;
} }
message GunDefinitionRegular { message GunDefinitionRegular {
@@ -134,101 +144,101 @@ message GunDefinitionRegular {
message GunDefinitionAutoLoader { message GunDefinitionAutoLoader {
required float clip_reload = 1; required float clip_reload = 1;
required float intra_clip = 2; required float intra_clip = 2;
required float shell_count = 3; required float shell_count = 3;
} }
message Burst { message Burst {
required uint32 count = 1; required uint32 count = 1;
required float interval = 2; required float interval = 2;
} }
message GunDefinitionAutoReloader { message GunDefinitionAutoReloader {
repeated float shell_reloads = 1; repeated float shell_reloads = 1;
required float intra_clip = 2; required float intra_clip = 2;
required float shell_count = 3; required float shell_count = 3;
} }
message AssaultRanges { message AssaultRanges {
repeated AssaultRange ranges = 1; repeated AssaultRange ranges = 1;
repeated ShellType types = 2; repeated ShellType types = 2;
} }
message AssaultRange { message AssaultRange {
required float distance = 1; required float distance = 1;
required float factor = 2; required float factor = 2;
} }
message ShellDefinition { message ShellDefinition {
required uint32 id = 1; required uint32 id = 1;
required I18nString name = 2; required I18nString name = 2;
required uint32 velocity = 3; required uint32 velocity = 3;
required uint32 armor_damage = 4; required uint32 armor_damage = 4;
required uint32 module_damage = 5; required uint32 module_damage = 5;
required float caliber = 6; required float caliber = 6;
required string icon = 7; required string icon = 7;
required ShellPenetration penetration = 8; required ShellPenetration penetration = 8;
required ShellType type = 9; required ShellType type = 9;
optional float normalization = 10; optional float normalization = 10;
optional float ricochet = 11; optional float ricochet = 11;
optional float explosion_radius = 12; optional float explosion_radius = 12;
required uint32 range = 13; required uint32 range = 13;
} }
enum ShellType { enum ShellType {
SHELL_TYPE_AP = 0; SHELL_TYPE_AP = 0;
SHELL_TYPE_APCR = 1; SHELL_TYPE_APCR = 1;
SHELL_TYPE_HEAT = 2; SHELL_TYPE_HEAT = 2;
SHELL_TYPE_HE = 3; SHELL_TYPE_HE = 3;
} }
message ShellPenetration { message ShellPenetration {
required float near = 1; required float near = 1;
required float far = 2; required float far = 2;
} }
message Unlock { message Unlock {
required int32 id = 1; required int32 id = 1;
required ModuleType type = 2; required ModuleType type = 2;
required UnlockCost cost = 3; required UnlockCost cost = 3;
} }
message UnlockCost { message UnlockCost {
required string type = 1; required string type = 1;
required int32 value = 2; required int32 value = 2;
} }
enum ModuleType { enum ModuleType {
MODULE_TYPE_VEHICLE = 0; MODULE_TYPE_VEHICLE = 0;
MODULE_TYPE_ENGINE = 1; MODULE_TYPE_ENGINE = 1;
MODULE_TYPE_TRACKS = 2; MODULE_TYPE_TRACKS = 2;
MODULE_TYPE_TURRET = 3; MODULE_TYPE_TURRET = 3;
MODULE_TYPE_GUN = 4; MODULE_TYPE_GUN = 4;
} }
enum TankClass { enum TankClass {
TANK_CLASS_LIGHT = 0; TANK_CLASS_LIGHT = 0;
TANK_CLASS_MEDIUM = 1; TANK_CLASS_MEDIUM = 1;
TANK_CLASS_HEAVY = 2; TANK_CLASS_HEAVY = 2;
TANK_CLASS_TANK_DESTROYER = 3; TANK_CLASS_TANK_DESTROYER = 3;
} }
enum TankType { enum TankType {
TANK_TYPE_RESEARCHABLE = 0; TANK_TYPE_RESEARCHABLE = 0;
TANK_TYPE_PREMIUM = 1; TANK_TYPE_PREMIUM = 1;
TANK_TYPE_COLLECTOR = 2; TANK_TYPE_COLLECTOR = 2;
} }
message Crew { message Crew {
required CrewType type = 1; required CrewType type = 1;
required uint32 count = 2; required uint32 count = 2;
repeated CrewType substitute = 3; repeated CrewType substitute = 3;
} }
enum CrewType { enum CrewType {
CREW_TYPE_COMMANDER = 0; CREW_TYPE_COMMANDER = 0;
CREW_TYPE_RADIOMAN = 1; CREW_TYPE_RADIOMAN = 1;
CREW_TYPE_GUNNER = 2; CREW_TYPE_GUNNER = 2;
CREW_TYPE_DRIVER = 3; CREW_TYPE_DRIVER = 3;
CREW_TYPE_LOADER = 4; CREW_TYPE_LOADER = 4;
} }
File diff suppressed because it is too large. Load diff
+36 -36
View File
@@ -1,7 +1,7 @@
{ {
"test": { "test": {
"default": "", "default": "test",
"long": ".", "long": "This is a long test string. You MUST NEVER see this in the UI. My favorite fruit is {fruit} because it has the color of {color}. I like it so much that I will say its name 3 times! {fruit}! {fruit}! {fruit}! Once again, it's {color}, not {not_color}.",
"fox": "Szybki brązowy lis przeskoczył nad leniwym psem." "fox": "Szybki brązowy lis przeskoczył nad leniwym psem."
}, },
"common": { "common": {
@@ -17,9 +17,9 @@
"plus": "+{value}" "plus": "+{value}"
}, },
"gun_types": { "gun_types": {
"auto_loader": "Auto loader", "auto_loader": "Samo ładowacz",
"auto_reloader": "Automatyczne przeładowanie", "auto_reloader": "Automatyczne przeładowanie",
"regular": "" "regular": "Regular"
}, },
"shells": { "shells": {
"ap": "AP", "ap": "AP",
@@ -28,18 +28,18 @@
"he": "OB" "he": "OB"
}, },
"player_count_period": { "player_count_period": {
"total": "Total", "total": "Łącznie",
"d_120": "Past 120 days", "d_120": "Powyżej 120 dni",
"d_90": "Past 90 days", "d_90": "Powyżej 90 dni",
"d_60": "Past 60 days", "d_60": "Powyżej 60 dni",
"d_30": "Past month", "d_30": "W ciągu miesiąca",
"d_7": "W minionym tygodniu", "d_7": "W minionym tygodniu",
"d_1": "Yesterday" "d_1": "Wczoraj"
}, },
"branches": { "branches": {
"local": "dev", "local": "dev",
"dev": "beta", "dev": "beta",
"opentest": "opentest", "opentest": "otwarty test",
"preview": "podgląd" "preview": "podgląd"
}, },
"locales": { "locales": {
@@ -80,45 +80,45 @@
}, },
"nations": { "nations": {
"ussr": "ZSRR", "ussr": "ZSRR",
"germany": "Germany", "germany": "Niemcy",
"usa": "U.S.A.", "usa": "Stany Zjednoczone.",
"china": "Chiny", "china": "Chiny",
"uk": "Wielka Brytania", "uk": "Wielka Brytania",
"france": "France", "france": "Francja",
"japan": "Japan", "japan": "Japonia",
"european": "Nacja Europejska", "european": "Nacja Europejska",
"other": "Nacja Hybrydowa" "other": "Nacja Hybrydowa"
}, },
"nations_adjectives": { "nations_adjectives": {
"ussr": "ZSRR", "ussr": "ZSRR",
"germany": "German", "germany": "Niemcy",
"usa": "Amerykański", "usa": "Amerykański",
"china": "Chiński", "china": "Chiński",
"uk": "Wielkiej Brytanii", "uk": "Wielkiej Brytanii",
"france": "Francuski", "france": "Francuski",
"japan": "Japoński", "japan": "Japoński",
"european": "European", "european": "Europa",
"other": "Hybrid" "other": "Hybryda"
}, },
"tank_class_short": { "tank_class_short": {
"-1": "unrecognized", "-1": "unrecognized",
"0": "Light", "0": "Lekki",
"1": "Medium", "1": "Średni",
"2": "Heavy", "2": "Ciężki",
"3": "TD" "3": "TD"
}, },
"tank_class_short_plural_lowercase": { "tank_class_short_plural_lowercase": {
"0": "lights", "0": "lekkie",
"1": "mediums", "1": "średnie",
"2": "heavies", "2": "ciężkie",
"3": "TDs" "3": "TDs"
}, },
"tank_class_medium": { "tank_class_medium": {
"-1": "unrecognized", "-1": "unrecognized",
"0": "Lekki", "0": "Lekki",
"1": "Medium", "1": "Średni",
"2": "Heavy", "2": "Ciężki",
"3": "Tank destroyer" "3": "Niszczyciel czołgów"
}, },
"tree_type": { "tree_type": {
"-1": "unrecognized", "-1": "unrecognized",
@@ -210,7 +210,7 @@
"good": "Dobrze", "good": "Dobrze",
"very_good": "Bardzo dobrze", "very_good": "Bardzo dobrze",
"great": "Świetne", "great": "Świetne",
"unicum": "Unikalny ", "unicum": "Unikalny",
"super_unicum": "Bardzo unikalny" "super_unicum": "Bardzo unikalny"
} }
}, },
@@ -236,7 +236,7 @@
"description": "Kariera" "description": "Kariera"
}, },
"custom": { "custom": {
"name": "Niestandardowy", "name": "custom",
"description": "Zakres niestandardowy", "description": "Zakres niestandardowy",
"options": { "options": {
"start": { "start": {
@@ -856,7 +856,7 @@
"hull": "Hull", "hull": "Hull",
"turret": "Turret", "turret": "Turret",
"hard": "Hard", "hard": "Hard",
"medium": "Medium", "medium": "Średni",
"soft": "Soft", "soft": "Soft",
"effective": "Effective" "effective": "Effective"
}, },
@@ -1066,19 +1066,19 @@
"speedBackwards": "Backwards", "speedBackwards": "Backwards",
"hardTerrainCoefficient": "Hard", "hardTerrainCoefficient": "Hard",
"hardTerrainCoefficientRaw": "Hard", "hardTerrainCoefficientRaw": "Hard",
"mediumTerrainCoefficient": "Medium", "mediumTerrainCoefficient": "Średni",
"mediumTerrainCoefficientRaw": "Medium", "mediumTerrainCoefficientRaw": "Średni",
"softTerrainCoefficient": "Soft", "softTerrainCoefficient": "Soft",
"softTerrainCoefficientRaw": "Soft", "softTerrainCoefficientRaw": "Soft",
"enginePower": "Engine power", "enginePower": "Engine power",
"powerToWeightRatioHardTerrain": "Hard", "powerToWeightRatioHardTerrain": "Hard",
"powerToWeightRatioMediumTerrain": "Medium", "powerToWeightRatioMediumTerrain": "Średni",
"powerToWeightRatioSoftTerrain": "Soft", "powerToWeightRatioSoftTerrain": "Soft",
"weight": "Weight", "weight": "Weight",
"turretTraverseSpeed": "Turret traverse speed", "turretTraverseSpeed": "Turret traverse speed",
"gunTraverseSpeed": "Gun traverse speed", "gunTraverseSpeed": "Gun traverse speed",
"hullTraverseHardTerrain": "Hard", "hullTraverseHardTerrain": "Hard",
"hullTraverseMediumTerrain": "Medium", "hullTraverseMediumTerrain": "Średni",
"hullTraverseSoftTerrain": "Soft", "hullTraverseSoftTerrain": "Soft",
"health": "Health", "health": "Health",
"fireChance": "Fire chance", "fireChance": "Fire chance",
@@ -1181,7 +1181,7 @@
"table": { "table": {
"tanks": { "tanks": {
"header": "Tank", "header": "Tank",
"total": "Total" "total": "Łącznie"
}, },
"stats": { "stats": {
"winrate": "Śr. wygranych", "winrate": "Śr. wygranych",
@@ -1239,7 +1239,7 @@
}, },
"table": { "table": {
"tank": "Tank", "tank": "Tank",
"total": "Total", "total": "Łącznie",
"remove": "Remove" "remove": "Remove"
} }
}, },
+43 -43
View File
@@ -86,8 +86,8 @@
"uk": "英国", "uk": "英国",
"france": "法国", "france": "法国",
"japan": "日本", "japan": "日本",
"european": "欧洲国家", "european": "欧盟 (E系)",
"other": "混合型国家" "other": "X系"
}, },
"nations_adjectives": { "nations_adjectives": {
"ussr": "苏联", "ussr": "苏联",
@@ -97,34 +97,34 @@
"uk": "英国", "uk": "英国",
"france": "法国", "france": "法国",
"japan": "日本", "japan": "日本",
"european": "欧洲国家", "european": "欧盟 (E系)",
"other": "混合型国家" "other": "X系"
}, },
"tank_class_short": { "tank_class_short": {
"-1": "未识别", "-1": "未识别",
"0": "轻型坦克", "0": "轻型坦克",
"1": "中型坦克", "1": "中型坦克",
"2": "重型战车", "2": "重型战车",
"3": "驱逐战车" "3": "坦克歼击车"
}, },
"tank_class_short_plural_lowercase": { "tank_class_short_plural_lowercase": {
"0": "轻型战车", "0": "轻型坦克",
"1": "中型战车", "1": "中型坦克",
"2": "重型战车", "2": "重型坦克",
"3": "驱逐战车" "3": "坦克歼击车"
}, },
"tank_class_medium": { "tank_class_medium": {
"-1": "未识别", "-1": "未识别",
"0": "轻型战车", "0": "轻型坦克",
"1": "中型战车", "1": "中型坦克",
"2": "重型战车", "2": "重型坦克",
"3": "驱逐战车" "3": "坦克歼击车"
}, },
"tree_type": { "tree_type": {
"-1": "未识别", "-1": "未识别",
"0": "科技树", "0": "科技树",
"1": "加值战车", "1": "金币车",
"2": "收藏家战车" "2": "收藏车"
}, },
"regions": { "regions": {
"normal": { "normal": {
@@ -140,8 +140,8 @@
}, },
"leagues": { "leagues": {
"diamond": "钻石", "diamond": "钻石",
"platinum": "白金", "platinum": "铂金",
"gold": "黃金", "gold": "黄金",
"silver": "白银", "silver": "白银",
"bronze": "青铜" "bronze": "青铜"
}, },
@@ -210,7 +210,7 @@
"good": "好", "good": "好",
"very_good": "非常好", "very_good": "非常好",
"great": "很好", "great": "很好",
"unicum": "極品", "unicum": "极品",
"super_unicum": "超强势玩家" "super_unicum": "超强势玩家"
} }
}, },
@@ -256,7 +256,7 @@
"description": "公会名称或标签" "description": "公会名称或标签"
}, },
"tank": { "tank": {
"name": "战车", "name": "坦克",
"description": "要获取战绩的坦克" "description": "要获取战绩的坦克"
}, },
"username": { "username": {
@@ -269,8 +269,8 @@
"description": "国别" "description": "国别"
}, },
"class": { "class": {
"name": "战车车型", "name": "坦克车型",
"description": "战车车型" "description": "坦克车型"
}, },
"type": { "type": {
"name": "战车类型", "name": "战车类型",
@@ -320,7 +320,7 @@
} }
}, },
"commands": { "commands": {
"dead": "# Stat Commands Are Dead\n\nThe BlitzKit Discord bot used to use BlitzStars to fetch periodic statistics. BlitzStars has enabled aggressive Cloudflare protections; hence, disabling BlitzKit from using BlitzStar's API. This has impacted the following commands:\n\n- `/stats`\n- `/today`\n- `/breakdown`\n- `/evolution`\n- `/full-stats`\n\nPlease use [Aftermath](<https://amth.one/invite>) as an alternative which is completely independent of BlitzStars. BlitzKit-hosted stat commands will return in the near future.", "dead": "# 统计指令现已停用\nBlitzKit Discord 机器人此前依赖 BlitzStars 获取周期性的统计数据。由于 BlitzStars 已启用严格的 Cloudflare 防护,导致 BlitzKit 无法继续使用其 API。这已导致以下指令失效:\n/统计\n/今日\n/损坏\n/演变\n/完整统计\n请使用 [Aftermath](<https://amth.one/invite>) \n作为完全独立于 BlitzStars 的替代方案。由 BlitzKit 托管的统计指令将在近期内恢复。",
"replay": { "replay": {
"name": "回放", "name": "回放",
"description": "显示重播结果", "description": "显示重播结果",
@@ -427,8 +427,8 @@
"description": "玩家数据统计" "description": "玩家数据统计"
}, },
"tank": { "tank": {
"name": "战车", "name": "坦克",
"description": "战车数据统计" "description": "坦克数据统计"
} }
} }
}, },
@@ -564,15 +564,15 @@
}, },
"today": { "today": {
"name": "今日", "name": "今日",
"description": "今天玩过的战车和统计资料" "description": "今天玩过的坦克和统计数据"
}, },
"stats": { "stats": {
"name": "统计资料", "name": "统计资料",
"description": "一段时间内的快速统计" "description": "一段时间内的快速统计"
}, },
"owned_tanks": { "owned_tanks": {
"name": "已拥有的战车", "name": "已拥有的坦克",
"description": "显示该阶级中所有您已拥有的战车", "description": "在一个等级中显示所有已拥有的坦克",
"body": { "body": {
"title": "# {name} 所拥有的 {tier} 阶坦克\n\n" "title": "# {name} 所拥有的 {tier} 阶坦克\n\n"
} }
@@ -585,7 +585,7 @@
"view_channels": "查看频道:需要找到重新整理按钮使用的讯息", "view_channels": "查看频道:需要找到重新整理按钮使用的讯息",
"read_message_history": "读取消息历史: 需要找到消息以刷新", "read_message_history": "读取消息历史: 需要找到消息以刷新",
"attach_files": "附加文件:需要将图像添加到基于图像的命令", "attach_files": "附加文件:需要将图像添加到基于图像的命令",
"external_emojis": "外部表情符号:需要添加国家旗帜和战车类别" "external_emojis": "外部表情:需要添加国家旗帜和坦克等级表情"
} }
}, },
"ping": { "ping": {
@@ -602,7 +602,7 @@
}, },
"wotb": { "wotb": {
"name": "战车世界闪击战", "name": "战车世界闪击战",
"description": "测试战车世界闪击战的ping值(延迟值)" "description": "测试坦克世界闪击战的ping值(延迟值)"
} }
} }
}, },
@@ -673,7 +673,7 @@
"search_bar_hint": "搜寻战车……", "search_bar_hint": "搜寻战车……",
"recent": "最近查看的", "recent": "最近查看的",
"select_all": "全选", "select_all": "全选",
"count_singular": "1辆战车", "count_singular": "1 辆坦克",
"count_plural": "{count} 辆坦克", "count_plural": "{count} 辆坦克",
"sorting_by": "按 {name} 排序", "sorting_by": "按 {name} 排序",
"clear": "清除", "clear": "清除",
@@ -681,7 +681,7 @@
"passive": "{name} (被动)", "passive": "{name} (被动)",
"active": "{name} (主动)", "active": "{name} (主动)",
"no_tanks_found": { "no_tanks_found": {
"body": "找不到战车。", "body": "未找到坦克。",
"search_again": "请尝试重新搜索。", "search_again": "请尝试重新搜索。",
"clear_filters": "请尝试清除筛选。" "clear_filters": "请尝试清除筛选。"
}, },
@@ -695,11 +695,11 @@
"survivability.volume": "体积", "survivability.volume": "体积",
"survivability.length": "长度", "survivability.length": "长度",
"fire.dpm": "标准炮弹 每分钟伤害血量", "fire.dpm": "标准炮弹 每分钟伤害血量",
"fire.dpmPremium": "加值炮弹 每分钟伤害血量", "fire.dpmPremium": "金币炮弹 每分钟伤害值",
"fire.reload": "装填时间", "fire.reload": "装填时间",
"fire.caliber": "口径", "fire.caliber": "口径",
"fire.standardPenetration": "标准炮弹穿透力", "fire.standardPenetration": "标准炮弹穿透力",
"fire.premiumPenetration": "加值炮弹穿透力", "fire.premiumPenetration": "金币炮弹穿透力",
"fire.tertiaryPenetration": "第三种炮弹穿透力", "fire.tertiaryPenetration": "第三种炮弹穿透力",
"fire.damage": "炮弹伤害值", "fire.damage": "炮弹伤害值",
"fire.moduleDamage": "模组伤害值", "fire.moduleDamage": "模组伤害值",
@@ -750,8 +750,8 @@
"dismiss": "取消" "dismiss": "取消"
}, },
"discord": { "discord": {
"join_short": "加入超級酷的人群!", "join_short": "加入超级酷的人群!",
"join_long": "加入我們的Discord社群,和超酷的人一起聊天!", "join_long": "加入我们的Discord社群,和超酷的人一起聊天!",
"action": "Discord" "action": "Discord"
}, },
"hero": { "hero": {
@@ -765,7 +765,7 @@
"title": "其他酷炫的项目" "title": "其他酷炫的项目"
}, },
"help_translate": "请帮忙将 {name} 翻译成 {language}!", "help_translate": "请帮忙将 {name} 翻译成 {language}!",
"seo_description": "战车世界闪击战 全方位工具" "seo_description": "坦克世界闪击战 全方位工具"
}, },
"docs": { "docs": {
"patches": { "patches": {
@@ -800,7 +800,7 @@
"proceed": "继续操作" "proceed": "继续操作"
}, },
"generate": "创建游玩列表", "generate": "创建游玩列表",
"name": "遊玩列表", "name": "游玩列表",
"description": "坦克游玩列表生成器", "description": "坦克游玩列表生成器",
"seo_description": "想不到在坦克世界闪击战玩什么? 使用BlitzKit 游玩列表来获得建议!", "seo_description": "想不到在坦克世界闪击战玩什么? 使用BlitzKit 游玩列表来获得建议!",
"button": "生成", "button": "生成",
@@ -961,7 +961,7 @@
"type": "战车类型", "type": "战车类型",
"dev_id": "开发参数:ID", "dev_id": "开发参数:ID",
"purchase": "购买价格", "purchase": "购买价格",
"restoration": "恢复战车价格", "restoration": "恢复坦克价格",
"sale": "出售价格", "sale": "出售价格",
"research": "研发所需经验" "research": "研发所需经验"
}, },
@@ -1056,10 +1056,10 @@
"dispersionGunDamaged": "主炮损坏时", "dispersionGunDamaged": "主炮损坏时",
"gunDepression": "俯角", "gunDepression": "俯角",
"gunElevation": "仰角", "gunElevation": "仰角",
"gunFrontalDepression": "车辆前部俯角", "gunFrontalDepression": "车体前部俯角",
"gunFrontalElevation": "车辆前部仰角", "gunFrontalElevation": "车体前部仰角",
"gunRearDepression": "车辆後部俯角", "gunRearDepression": "车体后部俯角",
"gunRearElevation": "车辆後部仰角", "gunRearElevation": "车体后部仰角",
"azimuthLeft": "左射界", "azimuthLeft": "左射界",
"azimuthRight": "右射界", "azimuthRight": "右射界",
"speedForwards": "最高前进速度", "speedForwards": "最高前进速度",
@@ -1259,7 +1259,7 @@
"description": "用于串流的嵌入", "description": "用于串流的嵌入",
"seo_description": "创建具有极高自订化的《坦克世界:闪击战》串流嵌入功能", "seo_description": "创建具有极高自订化的《坦克世界:闪击战》串流嵌入功能",
"coming_soon": "更多嵌入功能即将推出。", "coming_soon": "更多嵌入功能即将推出。",
"suggest": "建议与想法", "suggest": "建议与想法。",
"preview": { "preview": {
"title": "示例预览", "title": "示例预览",
"upload": "上传测试背景" "upload": "上传测试背景"
+23 -20
View File
@@ -1,5 +1,6 @@
import { import {
AverageDefinitions, AverageDefinitions,
AverageDefinitionsAllStats,
AverageDefinitionsEntry, AverageDefinitionsEntry,
type AverageDefinitionsManifest, type AverageDefinitionsManifest,
type IndividualTankStats, type IndividualTankStats,
@@ -30,9 +31,6 @@ type OptionalSecondLevel<T> = {
}; };
}; };
type AverageDefinitionsEntrySubPartial =
OptionalSecondLevel<AverageDefinitionsEntry>;
const MINUTE = 60 * 1000; const MINUTE = 60 * 1000;
const HOUR = 60 * MINUTE; const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR; const DAY = 24 * HOUR;
@@ -47,15 +45,15 @@ const preDiscoveredIds = await fetchPreDiscoveredIds();
const playerIds: Record<Region, number[][]> = { const playerIds: Record<Region, number[][]> = {
asia: chunk( asia: chunk(
preDiscoveredIds.filter((id) => idToRegion(id) === "asia"), preDiscoveredIds.filter((id) => idToRegion(id) === "asia"),
PLAYER_IDS_PER_CALL PLAYER_IDS_PER_CALL,
), ),
com: chunk( com: chunk(
preDiscoveredIds.filter((id) => idToRegion(id) === "com"), preDiscoveredIds.filter((id) => idToRegion(id) === "com"),
PLAYER_IDS_PER_CALL PLAYER_IDS_PER_CALL,
), ),
eu: chunk( eu: chunk(
preDiscoveredIds.filter((id) => idToRegion(id) === "eu"), preDiscoveredIds.filter((id) => idToRegion(id) === "eu"),
PLAYER_IDS_PER_CALL PLAYER_IDS_PER_CALL,
), ),
}; };
let regionIndex = 0; let regionIndex = 0;
@@ -71,7 +69,7 @@ times(THREADS, async () => {
const regionIds = playerIds[region]; const regionIds = playerIds[region];
const [ids] = regionIds.splice( const [ids] = regionIds.splice(
Math.floor(Math.random() * regionIds.length), Math.floor(Math.random() * regionIds.length),
1 1,
); );
const accountInfo = await getAccountInfo(region, ids, undefined, { const accountInfo = await getAccountInfo(region, ids, undefined, {
fields: "last_battle_time,statistics.all.battles", fields: "last_battle_time,statistics.all.battles",
@@ -104,8 +102,8 @@ times(THREADS, async () => {
filteredIds.map((id) => filteredIds.map((id) =>
getTankStats(region, id, { getTankStats(region, id, {
fields: "last_battle_time,battle_life_time,all,tank_id", fields: "last_battle_time,battle_life_time,all,tank_id",
}) }),
) ),
); );
players.forEach((tanks) => { players.forEach((tanks) => {
@@ -114,7 +112,7 @@ times(THREADS, async () => {
if (!tankIds.includes(tank.tank_id)) { if (!tankIds.includes(tank.tank_id)) {
console.log( console.log(
`Found new tank: ${tank.tank_id} (${tankIds.length + 1})` `Found new tank: ${tank.tank_id} (${tankIds.length + 1})`,
); );
tankIds.push(tank.tank_id); tankIds.push(tank.tank_id);
tanksSorted[tank.tank_id] = []; tanksSorted[tank.tank_id] = [];
@@ -140,7 +138,7 @@ async function postWork() {
console.log( console.log(
`Generating statistics based on ${samples.d_120.toLocaleString()} players (${samples.total.toLocaleString()} checked in total) and ${ `Generating statistics based on ${samples.d_120.toLocaleString()} players (${samples.total.toLocaleString()} checked in total) and ${
tankIds.length tankIds.length
} tanks...` } tanks...`,
); );
using uploader = new AssetUploader("averages"); using uploader = new AssetUploader("averages");
@@ -152,7 +150,12 @@ async function postWork() {
// (tank) => Date.now() - tank.last_battle_time * 1000 <= MAX_ACTIVITY_TIME, // (tank) => Date.now() - tank.last_battle_time * 1000 <= MAX_ACTIVITY_TIME,
// ); // );
const tanks = tanksSortedEntry; const tanks = tanksSortedEntry;
const entry: AverageDefinitionsEntrySubPartial = { const entry: {
mu: Partial<Record<keyof AverageDefinitionsAllStats, number>>;
sigma: Partial<Record<keyof AverageDefinitionsAllStats, number>>;
r: Partial<Record<keyof AverageDefinitionsAllStats, number>>;
samples: Samples;
} = {
mu: {}, mu: {},
sigma: {}, sigma: {},
r: {}, r: {},
@@ -162,13 +165,13 @@ async function postWork() {
tanks.forEach((tank) => { tanks.forEach((tank) => {
const timeSinceLastActivity = Date.now() - tank.last_battle_time * 1000; const timeSinceLastActivity = Date.now() - tank.last_battle_time * 1000;
entry.samples.total++; entry.samples!.total++;
if (timeSinceLastActivity <= 120 * DAY) entry.samples.d_120++; if (timeSinceLastActivity <= 120 * DAY) entry.samples!.d_120++;
if (timeSinceLastActivity <= 90 * DAY) entry.samples.d_90++; if (timeSinceLastActivity <= 90 * DAY) entry.samples!.d_90++;
if (timeSinceLastActivity <= 60 * DAY) entry.samples.d_60++; if (timeSinceLastActivity <= 60 * DAY) entry.samples!.d_60++;
if (timeSinceLastActivity <= 30 * DAY) entry.samples.d_30++; if (timeSinceLastActivity <= 30 * DAY) entry.samples!.d_30++;
if (timeSinceLastActivity <= 7 * DAY) entry.samples.d_7++; if (timeSinceLastActivity <= 7 * DAY) entry.samples!.d_7++;
if (timeSinceLastActivity <= 1 * DAY) entry.samples.d_1++; if (timeSinceLastActivity <= 1 * DAY) entry.samples!.d_1++;
}); });
const dataWY = tanks.map((tank) => ({ const dataWY = tanks.map((tank) => ({
@@ -191,7 +194,7 @@ async function postWork() {
function sum(slicer: (data: DataPoint) => number) { function sum(slicer: (data: DataPoint) => number) {
return data.reduce( return data.reduce(
(accumulator, data) => accumulator + slicer(data), (accumulator, data) => accumulator + slicer(data),
0 0,
); );
} }
@@ -6,13 +6,15 @@ export async function boosterIcons() {
console.log("Building booster icons..."); console.log("Building booster icons...");
const uploader = new AssetUploader("booster icons"); const uploader = new AssetUploader("booster icons");
const boosterFiles = vfs const boosterFiles = await vfs
.dir("Data/Gfx/Shared/boosters") .dir("Data/Gfx/Shared/boosters")
.filter( .then((files) =>
(file) => !file.includes("@2x.txt") && !file.startsWith("texture0") files.filter(
(file) => !file.includes("@2x.txt") && !file.startsWith("texture0"),
),
); );
const image = sharp( const image = sharp(
await vfs.file(`Data/Gfx/Shared/boosters/texture0.packed.webp`) await vfs.file(`Data/Gfx/Shared/boosters/texture0.packed.webp`),
); );
for (const file of boosterFiles) { for (const file of boosterFiles) {
@@ -6,7 +6,7 @@ export async function camouflageIcons() {
using uploader = new AssetUploader("camo icons"); using uploader = new AssetUploader("camo icons");
const content = await vfs.file( const content = await vfs.file(
`Data/Gfx/UI/Hangar/IconCamouflage.packed.webp` `Data/Gfx/UI/Hangar/IconCamouflage.packed.webp`,
); );
await uploader.add({ content, path: "icons/camo.webp" }); await uploader.add({ content, path: "icons/camo.webp" });
@@ -1,10 +1,12 @@
import { assertSecret } from "@blitzkit/core"; import { assertSecret } from "@blitzkit/core";
import { AbstractVFS } from "../core/vfs/abstract"; import { AbstractVFS } from "../core/vfs/abstract";
import { LocalVFS } from "../core/vfs/local";
import { SteamVFS } from "../core/vfs/steam"; import { SteamVFS } from "../core/vfs/steam";
import { ZipVFS } from "../core/vfs/zip"; import { ZipVFS } from "../core/vfs/zip";
const steamPattern = /steam:(\d+)\/(\d+)/; const steamPattern = /steam:(\d+)\/(\d+)/;
const zipPattern = /zip:(.+)/; const zipPattern = /zip:(.+)/;
const localePattern = /local:"?(.+)"?/;
const source = Bun.argv const source = Bun.argv
.find((arg) => arg.startsWith("--source=")) .find((arg) => arg.startsWith("--source="))
@@ -21,11 +23,14 @@ if (!source) {
assertSecret(import.meta.env.STEAM_USERNAME), assertSecret(import.meta.env.STEAM_USERNAME),
assertSecret(import.meta.env.STEAM_PASSWORD), assertSecret(import.meta.env.STEAM_PASSWORD),
app, app,
depot depot,
).init(); ).init();
} else if (zipPattern.test(source)) { } else if (zipPattern.test(source)) {
const [, url] = source.match(zipPattern)!; const [, url] = source.match(zipPattern)!;
resolved = await new ZipVFS(url).init(); resolved = await new ZipVFS(url).init();
} else if (localePattern.test(source)) {
const [, path] = source.match(localePattern)!;
resolved = await new LocalVFS(path).init();
} else { } else {
throw new Error("No valid source specified"); throw new Error("No valid source specified");
} }
@@ -18,26 +18,20 @@ export async function consumableProvisionIcons() {
using uploader = new AssetUploader("consumable and provision icons"); using uploader = new AssetUploader("consumable and provision icons");
const styles = [ const styles = [
"UI/Styles/Lobby/Inventory/InventoryNormalStyles.yaml",
"UI/Styles/Lobby/Inventory/Event/InventoryBigStyles.yaml", "UI/Styles/Lobby/Inventory/Event/InventoryBigStyles.yaml",
"UI/Styles/Lobby/Inventory/Event/InventoryNormalStyles.yaml", "UI/Styles/Lobby/Inventory/Event/InventoryNormalStyles.yaml",
"UI/Styles/Lobby/Inventory/InventoryNormalStyles.yaml",
"UI/Screens3/Lobby/Inventory/Equipment/EquipmentItemImage.style.yaml", "UI/Screens3/Lobby/Inventory/Equipment/EquipmentItemImage.style.yaml",
"UI/Screens/Battle/Styles/BattleEquipmentStyles.yaml", "UI/Screens/Battle/Styles/BattleEquipmentStyles.yaml",
]; ];
const styleSheets = await Promise.all( const styleSheets = await Promise.all(
styles.map(async (path) => styles.map(async (path) =>
Object.values( Object.values((await vfs.yaml<Mappings>(`Data/${path}`)).StyleSheets),
( ),
await vfs.yaml<Mappings>(`Data/${path}`)
).StyleSheets
)
)
).then((array) => array.flat()); ).then((array) => array.flat());
for (const match of ( for (const match of (
await vfs.text( await vfs.text(`Data/XML/item_defs/vehicles/common/consumables/list.xml`)
`Data/XML/item_defs/vehicles/common/consumables/list.xml`
)
).matchAll(listItemsPattern)) { ).matchAll(listItemsPattern)) {
const consumablesCommon = await vfs.xml<{ const consumablesCommon = await vfs.xml<{
root: ConsumablesCommon; root: ConsumablesCommon;
@@ -46,13 +40,13 @@ export async function consumableProvisionIcons() {
for (const consumable of Object.values(consumablesCommon.root)) { for (const consumable of Object.values(consumablesCommon.root)) {
const styleSheet = styleSheets.find((styleSheet) => const styleSheet = styleSheets.find((styleSheet) =>
styleSheet.selectors.some((selector) => styleSheet.selectors.some((selector) =>
selector.includes(`${consumable.icon} `) selector.includes(`${consumable.icon} `),
) ),
); );
if (!styleSheet) { if (!styleSheet) {
console.warn( console.warn(
`No style sheet found for consumable ${consumable.icon}; skipping...` `No style sheet found for consumable ${consumable.icon}; skipping...`,
); );
continue; continue;
} }
@@ -62,10 +56,8 @@ export async function consumableProvisionIcons() {
.replace(".psd", "") .replace(".psd", "")
.replace(".txt", ""); .replace(".txt", "");
if (vfs.resolve(`Data/${configPath}.packed.webp`)) { if (await vfs.resolve(`Data/${configPath}.packed.webp`)) {
const image = sharp( const image = sharp(await vfs.file(`Data/${configPath}.packed.webp`));
await vfs.file(`Data/${configPath}.packed.webp`)
);
const content = await image.trim({ threshold: 100 }).toBuffer(); const content = await image.trim({ threshold: 100 }).toBuffer();
await uploader.add({ await uploader.add({
@@ -78,8 +70,8 @@ export async function consumableProvisionIcons() {
`${`Data/${configPath}` `${`Data/${configPath}`
.split("/") .split("/")
.slice(0, -1) .slice(0, -1)
.join("/")}/texture0.packed.webp` .join("/")}/texture0.packed.webp`,
) ),
); );
const sizes = (await vfs.text(`Data/${configPath}.txt`)) const sizes = (await vfs.text(`Data/${configPath}.txt`))
.split("\n")[4] .split("\n")[4]
@@ -104,9 +96,7 @@ export async function consumableProvisionIcons() {
} }
for (const match of ( for (const match of (
await vfs.text( await vfs.text(`Data/XML/item_defs/vehicles/common/provisions/list.xml`)
`Data/XML/item_defs/vehicles/common/provisions/list.xml`
)
).matchAll(listItemsPattern)) { ).matchAll(listItemsPattern)) {
const provisionsCommon = await vfs.xml<{ const provisionsCommon = await vfs.xml<{
root: ProvisionsCommon; root: ProvisionsCommon;
@@ -115,13 +105,13 @@ export async function consumableProvisionIcons() {
for (const provision of Object.values(provisionsCommon.root)) { for (const provision of Object.values(provisionsCommon.root)) {
const styleSheet = styleSheets.find((styleSheet) => const styleSheet = styleSheets.find((styleSheet) =>
styleSheet.selectors.some((selector) => styleSheet.selectors.some((selector) =>
selector.includes(`${provision.icon} `) selector.includes(`${provision.icon} `),
) ),
); );
if (!styleSheet) { if (!styleSheet) {
console.warn( console.warn(
`No style sheet found for provision ${provision.icon}; skipping...` `No style sheet found for provision ${provision.icon}; skipping...`,
); );
continue; continue;
} }
@@ -131,10 +121,8 @@ export async function consumableProvisionIcons() {
.replace(".psd", "") .replace(".psd", "")
.replace(".txt", ""); .replace(".txt", "");
if (vfs.resolve(`Data/${configPath}.packed.webp`)) { if (await vfs.resolve(`Data/${configPath}.packed.webp`)) {
const image = sharp( const image = sharp(await vfs.file(`Data/${configPath}.packed.webp`));
await vfs.file(`Data/${configPath}.packed.webp`)
);
const content = await image.trim({ threshold: 100 }).toBuffer(); const content = await image.trim({ threshold: 100 }).toBuffer();
await uploader.add({ await uploader.add({
@@ -147,8 +135,8 @@ export async function consumableProvisionIcons() {
`${`Data/${configPath}` `${`Data/${configPath}`
.split("/") .split("/")
.slice(0, -1) .slice(0, -1)
.join("/")}/texture0.packed.webp` .join("/")}/texture0.packed.webp`,
) ),
); );
const sizes = (await vfs.text(`Data/${configPath}.txt`)) const sizes = (await vfs.text(`Data/${configPath}.txt`))
.split("\n")[4] .split("\n")[4]
@@ -20,7 +20,7 @@ export async function currencies() {
for (const [file, name] of ICONS) { for (const [file, name] of ICONS) {
const content = await sharp( const content = await sharp(
await vfs.file(`Data/Gfx/Lobby/currency/${file}`) await vfs.file(`Data/Gfx/Lobby/currency/${file}`),
) )
.trim() .trim()
.toBuffer(); .toBuffer();
@@ -36,14 +36,14 @@ export async function currencies() {
.then((glossary) => .then((glossary) =>
Object.values(glossary) Object.values(glossary)
.entries() .entries()
.filter(([key]) => /^prx_season_\d+$/.test(`${key}`)) .filter(([key]) => /^prx_season_\d+$/.test(`${key}`)),
); );
for (const [key, item] of glossary) { for (const [key, item] of glossary) {
if (item.image_url === null) throw new Error(`No image_url for ${key}`); if (item.image_url === null) throw new Error(`No image_url for ${key}`);
const imageRaw = await fetch(item.image_url).then((response) => const imageRaw = await fetch(item.image_url).then((response) =>
response.arrayBuffer() response.arrayBuffer(),
); );
const content = await sharp(imageRaw) const content = await sharp(imageRaw)
.trim({ .trim({
+155 -122
View File
@@ -8,6 +8,7 @@ import {
ConsumableTankCategoryFilterCategory, ConsumableTankCategoryFilterCategory,
Crew, Crew,
CrewType, CrewType,
Equalizer,
EquipmentDefinitions, EquipmentDefinitions,
EquipmentSlot, EquipmentSlot,
GameDefinitions, GameDefinitions,
@@ -32,6 +33,7 @@ import {
} from "@blitzkit/core"; } from "@blitzkit/core";
import { SUPPORTED_LOCALE_BLITZ_MAP } from "@blitzkit/i18n"; import { SUPPORTED_LOCALE_BLITZ_MAP } from "@blitzkit/i18n";
import locales from "@blitzkit/i18n/locales.json"; import locales from "@blitzkit/i18n/locales.json";
import { readFile } from "fs/promises";
import { deburr } from "lodash-es"; import { deburr } from "lodash-es";
import { parse as parsePath } from "path"; import { parse as parsePath } from "path";
import type { Vector3Tuple } from "three"; import type { Vector3Tuple } from "three";
@@ -81,16 +83,16 @@ const blitzTankFilterDefinitionCategoryToBlitzkit: Record<
BlitzTankFilterDefinitionCategory, BlitzTankFilterDefinitionCategory,
ConsumableTankCategoryFilterCategory ConsumableTankCategoryFilterCategory
> = { > = {
clip: ConsumableTankCategoryFilterCategory.CLIP, clip: ConsumableTankCategoryFilterCategory.CONSUMABLE_TANK_CATEGORY_FILTER_CATEGORY_CLIP,
}; };
function vector3TupleToBlitzkit(tuple: Vector3Tuple) { function vector3TupleToBlitzkit(tuple: Vector3Tuple) {
return { x: tuple[0], y: tuple[1], z: tuple[2] } satisfies Vector3; return { x: tuple[0], y: tuple[1], z: tuple[2] } satisfies Vector3;
} }
const blitzTankClassToBlitzkit: Record<BlitzTankClass, TankClass> = { const blitzTankClassToBlitzkit: Record<BlitzTankClass, TankClass> = {
lightTank: TankClass.LIGHT, lightTank: TankClass.TANK_CLASS_LIGHT,
"AT-SPG": TankClass.TANK_DESTROYER, "AT-SPG": TankClass.TANK_CLASS_TANK_DESTROYER,
heavyTank: TankClass.HEAVY, heavyTank: TankClass.TANK_CLASS_HEAVY,
mediumTank: TankClass.MEDIUM, mediumTank: TankClass.TANK_CLASS_MEDIUM,
}; };
export interface BlitzStrings { export interface BlitzStrings {
[key: string]: string; [key: string]: string;
@@ -135,7 +137,7 @@ export interface ChassisDefinitionsList {
} }
type VehicleDefinitionArmor = Record< type VehicleDefinitionArmor = Record<
string, string,
number | { vehicleDamageFactor: 0; "#text": number } number | { vehicleDamageFactor: 0; "#text": number } | number[]
>; >;
interface UnlocksInner { interface UnlocksInner {
cost: number | string; cost: number | string;
@@ -149,18 +151,18 @@ type BlitzModuleType = {
type UnlocksListing = BlitzModuleType | undefined; type UnlocksListing = BlitzModuleType | undefined;
type BlitzCrewType = "commander" | "radioman" | "gunner" | "driver" | "loader"; type BlitzCrewType = "commander" | "radioman" | "gunner" | "driver" | "loader";
const blitzCrewTypeToBlitzkit: Record<BlitzCrewType, CrewType> = { const blitzCrewTypeToBlitzkit: Record<BlitzCrewType, CrewType> = {
commander: CrewType.COMMANDER, commander: CrewType.CREW_TYPE_COMMANDER,
driver: CrewType.DRIVER, driver: CrewType.CREW_TYPE_DRIVER,
gunner: CrewType.GUNNER, gunner: CrewType.CREW_TYPE_GUNNER,
loader: CrewType.LOADER, loader: CrewType.CREW_TYPE_LOADER,
radioman: CrewType.RADIOMAN, radioman: CrewType.CREW_TYPE_RADIOMAN,
}; };
const blitzkitCrewTypeToBlitz: Record<CrewType, BlitzCrewType> = { const blitzkitCrewTypeToBlitz: Record<CrewType, BlitzCrewType> = {
[CrewType.COMMANDER]: "commander", [CrewType.CREW_TYPE_COMMANDER]: "commander",
[CrewType.DRIVER]: "driver", [CrewType.CREW_TYPE_DRIVER]: "driver",
[CrewType.GUNNER]: "gunner", [CrewType.CREW_TYPE_GUNNER]: "gunner",
[CrewType.LOADER]: "loader", [CrewType.CREW_TYPE_LOADER]: "loader",
[CrewType.RADIOMAN]: "radioman", [CrewType.CREW_TYPE_RADIOMAN]: "radioman",
}; };
interface VehicleDefinitions { interface VehicleDefinitions {
invisibility: { invisibility: {
@@ -298,7 +300,7 @@ interface VehicleDefinitions {
type: "default"; type: "default";
enabled: false; enabled: false;
factorsModifiers: ""; factorsModifiers: "";
} },
]; ];
}; };
}; };
@@ -517,20 +519,36 @@ type CombatRolesYaml = Record<
>; >;
const blitzShellKindToBlitzkit: Record<ShellKind, ShellType> = { const blitzShellKindToBlitzkit: Record<ShellKind, ShellType> = {
ARMOR_PIERCING: ShellType.AP, ARMOR_PIERCING: ShellType.SHELL_TYPE_AP,
ARMOR_PIERCING_CR: ShellType.APCR, ARMOR_PIERCING_CR: ShellType.SHELL_TYPE_APCR,
HIGH_EXPLOSIVE: ShellType.HE, HIGH_EXPLOSIVE: ShellType.SHELL_TYPE_HE,
HOLLOW_CHARGE: ShellType.HEAT, HOLLOW_CHARGE: ShellType.SHELL_TYPE_HEAT,
}; };
export const botPattern = /^.+((tutorial_bot(\d+)?)|(TU))$/; export const botPattern = /^.+((tutorial_bot(\d+)?)|(TU))$/;
const blitzModuleTypeToBlitzkit: Record<keyof BlitzModuleType, ModuleType> = { const blitzModuleTypeToBlitzkit: Record<keyof BlitzModuleType, ModuleType> = {
chassis: ModuleType.TRACKS, chassis: ModuleType.MODULE_TYPE_TRACKS,
engine: ModuleType.ENGINE, engine: ModuleType.MODULE_TYPE_ENGINE,
gun: ModuleType.GUN, gun: ModuleType.MODULE_TYPE_GUN,
turret: ModuleType.TURRET, turret: ModuleType.MODULE_TYPE_TURRET,
vehicle: ModuleType.VEHICLE, vehicle: ModuleType.MODULE_TYPE_VEHICLE,
}; };
function assignArmor(
raw: VehicleDefinitionArmor[string],
id: number,
armor: Armor,
) {
if (typeof raw === "number") {
armor.thickness[id] = raw;
} else if (Array.isArray(raw)) {
armor.thickness[id] = raw.at(-1)!;
} else {
if (!armor.spaced) armor.spaced = [];
armor.thickness[id] = raw["#text"];
armor.spaced.push(id);
}
}
export async function definitions() { export async function definitions() {
console.log("Building definitions..."); console.log("Building definitions...");
@@ -555,18 +573,18 @@ export async function definitions() {
const provisionDefinitions: ProvisionDefinitions = { provisions: {} }; const provisionDefinitions: ProvisionDefinitions = { provisions: {} };
const skillDefinitions: SkillDefinitions = { const skillDefinitions: SkillDefinitions = {
classes: { classes: {
[TankClass.LIGHT]: { skills: [] }, [TankClass.TANK_CLASS_LIGHT]: { skills: [] },
[TankClass.MEDIUM]: { skills: [] }, [TankClass.TANK_CLASS_MEDIUM]: { skills: [] },
[TankClass.HEAVY]: { skills: [] }, [TankClass.TANK_CLASS_HEAVY]: { skills: [] },
[TankClass.TANK_DESTROYER]: { skills: [] }, [TankClass.TANK_CLASS_TANK_DESTROYER]: { skills: [] },
}, },
}; };
const nations = vfs const nations = await vfs
.dir("Data/XML/item_defs/vehicles") .dir("Data/XML/item_defs/vehicles")
.filter((nation) => nation !== "common"); .then((files) => files.filter((nation) => nation !== "common"));
const tankStringIdMap: Record<string, number> = {}; const tankStringIdMap: Record<string, number> = {};
const optionalDevices = await vfs.xml<{ root: OptionalDevices }>( const optionalDevices = await vfs.xml<{ root: OptionalDevices }>(
"Data/XML/item_defs/vehicles/common/optional_devices.xml" "Data/XML/item_defs/vehicles/common/optional_devices.xml",
); );
const stringsI18n: Record<string, Record<string, string>> = {}; const stringsI18n: Record<string, Record<string, string>> = {};
@@ -574,12 +592,12 @@ export async function definitions() {
locales.supported.map(async ({ locale }) => { locales.supported.map(async ({ locale }) => {
const blitzLocale = SUPPORTED_LOCALE_BLITZ_MAP[locale]; const blitzLocale = SUPPORTED_LOCALE_BLITZ_MAP[locale];
const cache = await fetch( const cache = await fetch(
`https://stufficons.wgcdn.co/localizations/${blitzLocale}.yaml` `https://stufficons.wgcdn.co/localizations/${blitzLocale}.yaml`,
) )
.then((response) => response.text()) .then((response) => response.text())
.then((string) => parseYaml(string) as BlitzStrings); .then((string) => parseYaml(string) as BlitzStrings);
const preInstalled = await vfs.yaml<BlitzStrings>( const preInstalled = await vfs.yaml<BlitzStrings>(
`Data/Strings/${blitzLocale}.yaml` `Data/Strings/${blitzLocale}.yaml`,
); );
stringsI18n[locale] = { stringsI18n[locale] = {
@@ -588,7 +606,7 @@ export async function definitions() {
}; };
console.log(`Loaded strings for ${locale}`); console.log(`Loaded strings for ${locale}`);
}) }),
); );
function getString(name: string) { function getString(name: string) {
@@ -627,9 +645,9 @@ export async function definitions() {
consumables, consumables,
( (
await vfs.xml<{ root: ConsumablesCommon }>( await vfs.xml<{ root: ConsumablesCommon }>(
`Data/XML/item_defs/vehicles/common/consumables/${match[1]}.xml` `Data/XML/item_defs/vehicles/common/consumables/${match[1]}.xml`,
) )
).root ).root,
); );
} }
@@ -642,39 +660,57 @@ export async function definitions() {
provisions, provisions,
( (
await vfs.xml<{ root: ConsumablesCommon }>( await vfs.xml<{ root: ConsumablesCommon }>(
`Data/XML/item_defs/vehicles/common/provisions/${match[1]}.xml` `Data/XML/item_defs/vehicles/common/provisions/${match[1]}.xml`,
) )
).root ).root,
); );
} }
const avatar = await vfs.xml<{ root: Avatar }>( const avatar = await vfs.xml<{ root: Avatar }>(
`Data/XML/item_defs/tankmen/avatar.xml` `Data/XML/item_defs/tankmen/avatar.xml`,
); );
const maps = await vfs.yaml<Maps>(`Data/maps.yaml`); const maps = await vfs.yaml<Maps>(`Data/maps.yaml`);
const tankXps = new Map<number, ResearchCost>(); const tankXps = new Map<number, ResearchCost>();
const camouflagesXml = await vfs.xml<{ root: CamouflagesXML }>( const camouflagesXml = await vfs.xml<{ root: CamouflagesXML }>(
`Data/XML/item_defs/vehicles/common/camouflages.xml` `Data/XML/item_defs/vehicles/common/camouflages.xml`,
); );
const camouflagesYaml = await vfs.yaml<CamouflagesYaml>( const camouflagesYaml = await vfs.yaml<CamouflagesYaml>(
`Data/camouflages.yaml` `Data/camouflages.yaml`,
); );
const camouflagesXmlEntries = Object.entries(camouflagesXml.root.camouflages); const camouflagesXmlEntries = Object.entries(camouflagesXml.root.camouflages);
const squadBattleTypeStyles = await vfs.yaml<SquadBattleTypeStyles>( const squadBattleTypeStyles = await vfs.yaml<SquadBattleTypeStyles>(
`Data/UI/Screens3/Lobby/Hangar/Squad/SquadBattleType.yaml` `Data/UI/Screens3/Lobby/Hangar/Squad/SquadBattleType.yaml`,
); );
const gameTypeSelectorStyles = await vfs.yaml<SquadBattleTypeStyles>( const gameTypeSelectorStyles = await vfs.yaml<SquadBattleTypeStyles>(
`Data/UI/Screens/Lobby/Hangar/GameTypeSelector.yaml` `Data/UI/Screens/Lobby/Hangar/GameTypeSelector.yaml`,
); );
const gameModeNativeNames: Record<string, number> = {}; const gameModeNativeNames: Record<string, number> = {};
const combatRoles = await vfs.yaml<CombatRolesYaml>( const combatRoles = await vfs.yaml<CombatRolesYaml>(
`Data/XML/item_defs/vehicles/common/combat_roles.yaml` `Data/XML/item_defs/vehicles/common/combat_roles.yaml`,
); );
const consumableNativeNames: Record<string, number> = {}; const consumableNativeNames: Record<string, number> = {};
const provisionNativeNames: Record<string, number> = {}; const provisionNativeNames: Record<string, number> = {};
let equalizerDefinitions: string[][];
if (
await vfs.resolve("Data/XML/item_defs/vehicles/common/tier_equializer.csv")
) {
equalizerDefinitions = await vfs.csv(
`Data/XML/item_defs/vehicles/common/tier_equializer.csv`,
{ delimiter: ";" },
);
} else {
const stubEqualizerDefinitions = await readFile("stub/equalizer.csv").then(
(buffer) => buffer.toString(),
);
equalizerDefinitions = await vfs._csv(stubEqualizerDefinitions, {
delimiter: ";",
});
}
for (const match of squadBattleTypeStyles.Prototypes[0].components.UIDataLocalBindingsComponent.data[1][2].matchAll( for (const match of squadBattleTypeStyles.Prototypes[0].components.UIDataLocalBindingsComponent.data[1][2].matchAll(
/"(\d+)" -> "(battleType\/([a-zA-Z]+))"/g /"(\d+)" -> "(battleType\/([a-zA-Z]+))"/g,
)) { )) {
const id = Number(match[1]); const id = Number(match[1]);
const name = getString(match[2]); const name = getString(match[2]);
@@ -686,7 +722,7 @@ export async function definitions() {
} }
for (const match of gameTypeSelectorStyles.Prototypes[0].components.UIDataLocalBindingsComponent.data[1][2].matchAll( for (const match of gameTypeSelectorStyles.Prototypes[0].components.UIDataLocalBindingsComponent.data[1][2].matchAll(
/eGameMode\.([a-zA-Z]+) -> "~res:\/Gfx\/UI\/Hangar\/GameTypes\/battle-type_([^"]+)"/g /eGameMode\.([a-zA-Z]+) -> "~res:\/Gfx\/UI\/Hangar\/GameTypes\/battle-type_([^"]+)"/g,
)) { )) {
Object.entries(gameModeNativeNames).forEach(([key, value]) => { Object.entries(gameModeNativeNames).forEach(([key, value]) => {
if (key.toLowerCase() === match[2].toLowerCase()) { if (key.toLowerCase() === match[2].toLowerCase()) {
@@ -720,7 +756,7 @@ export async function definitions() {
for (const nation of nations) { for (const nation of nations) {
const tankList = await vfs.xml<{ root: VehicleDefinitionList }>( const tankList = await vfs.xml<{ root: VehicleDefinitionList }>(
`Data/XML/item_defs/vehicles/${nation}/list.xml` `Data/XML/item_defs/vehicles/${nation}/list.xml`,
); );
for (const tankKey in tankList.root) { for (const tankKey in tankList.root) {
@@ -761,7 +797,7 @@ export async function definitions() {
console.warn( console.warn(
`Multiple tanks share slug ${slug}: ${requesters `Multiple tanks share slug ${slug}: ${requesters
.map(({ key }) => key) .map(({ key }) => key)
.join(", ")}` .join(", ")}`,
); );
if (requesters.length !== 2) { if (requesters.length !== 2) {
@@ -791,7 +827,7 @@ export async function definitions() {
if (canonical === undefined) { if (canonical === undefined) {
throw new Error( throw new Error(
"Two tutorial bots share the same slug? The world is truly broken." "Two tutorial bots share the same slug? The world is truly broken.",
); );
} }
@@ -804,7 +840,7 @@ export async function definitions() {
for (const nation of nations) { for (const nation of nations) {
const tankList = await vfs.xml<{ root: VehicleDefinitionList }>( const tankList = await vfs.xml<{ root: VehicleDefinitionList }>(
`Data/XML/item_defs/vehicles/${nation}/list.xml` `Data/XML/item_defs/vehicles/${nation}/list.xml`,
); );
const turretList = await vfs.xml<{ const turretList = await vfs.xml<{
root: TurretDefinitionsList; root: TurretDefinitionsList;
@@ -857,7 +893,7 @@ export async function definitions() {
: Number(item.cost.split(":")[1]), : Number(item.cost.split(":")[1]),
}, },
} satisfies Unlock; } satisfies Unlock;
}) }),
) )
.flat(); .flat();
} }
@@ -872,10 +908,10 @@ export async function definitions() {
const tank = tankList.root[tankKey]; const tank = tankList.root[tankKey];
let tankPrice: TankPrice; let tankPrice: TankPrice;
const tankDefinition = await vfs.xml<{ root: VehicleDefinitions }>( const tankDefinition = await vfs.xml<{ root: VehicleDefinitions }>(
`Data/XML/item_defs/vehicles/${nation}/${tankKey}.xml` `Data/XML/item_defs/vehicles/${nation}/${tankKey}.xml`,
); );
const tankParameters = await vfs.yaml<TankParameters>( const tankParameters = await vfs.yaml<TankParameters>(
`Data/3d/Tanks/Parameters/${nation}/${tankKey}.yaml` `Data/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`,
); );
const turretOrigin = tankDefinition.root.hull.turretPositions.turret const turretOrigin = tankDefinition.root.hull.turretPositions.turret
.split(" ") .split(" ")
@@ -890,23 +926,23 @@ export async function definitions() {
if (slug === undefined) { if (slug === undefined) {
throw new Error( throw new Error(
`Could not find slug for ${nation}/${tankKey} (${tankId})` `Could not find slug for ${nation}/${tankKey} (${tankId})`,
); );
} }
if (tank.sellPrice) { if (tank.sellPrice) {
tankPrice = { tankPrice = {
type: TankPriceType.GOLD, type: TankPriceType.TANK_PRICE_TYPE_GOLD,
value: tank.sellPrice["#text"] * 2, value: tank.sellPrice["#text"] * 2,
}; };
} else if (typeof tank.price === "number") { } else if (typeof tank.price === "number") {
tankPrice = { tankPrice = {
type: TankPriceType.CREDITS, type: TankPriceType.TANK_PRICE_TYPE_CREDITS,
value: tank.price, value: tank.price,
}; };
} else { } else {
tankPrice = { tankPrice = {
type: TankPriceType.CREDITS, type: TankPriceType.TANK_PRICE_TYPE_CREDITS,
value: tank.price["#text"] * 400, value: tank.price["#text"] * 400,
}; };
} }
@@ -923,13 +959,7 @@ export async function definitions() {
const armorId = parseInt(armorIdString); const armorId = parseInt(armorIdString);
const armorRaw = tankDefinition.root.hull.armor[name]; const armorRaw = tankDefinition.root.hull.armor[name];
if (typeof armorRaw === "number") { assignArmor(armorRaw, armorId, hullArmor);
hullArmor.thickness[armorId] = armorRaw;
} else {
if (!hullArmor.spaced) hullArmor.spaced = [];
hullArmor.thickness[armorId] = armorRaw["#text"];
hullArmor.spaced.push(armorId);
}
}); });
const crew: Crew[] = []; const crew: Crew[] = [];
const fixedCamouflage = tankTags.includes("eventCamouflage_user"); const fixedCamouflage = tankTags.includes("eventCamouflage_user");
@@ -938,7 +968,7 @@ export async function definitions() {
Object.entries(tankDefinition.root.crew).forEach(([crewKey, value]) => { Object.entries(tankDefinition.root.crew).forEach(([crewKey, value]) => {
let entry: Crew; let entry: Crew;
const index = crew.findIndex( const index = crew.findIndex(
({ type }) => blitzkitCrewTypeToBlitz[type] === crewKey ({ type }) => blitzkitCrewTypeToBlitz[type] === crewKey,
); );
if (index === -1) { if (index === -1) {
if (crewKey === "#text") return; if (crewKey === "#text") return;
@@ -988,10 +1018,23 @@ export async function definitions() {
}) })
.map(([, camo]) => camo.id); .map(([, camo]) => camo.id);
let equalizerEntry = equalizerDefinitions.find(
(line) => line[0] === `${nation}:${tankKey}`,
);
let equalizer: Equalizer | undefined;
if (equalizerEntry) {
const [health, penetration, module_health, damage, armor] =
equalizerEntry?.slice(1).map(Number);
equalizer = { health, penetration, module_health, damage, armor };
}
tankDefinitions.tanks[tankId] = { tankDefinitions.tanks[tankId] = {
ancestors: [], ancestors: [],
successors: [], successors: [],
id: tankId, id: tankId,
dev_name: tankKey,
roles: {}, roles: {},
camouflages: camouflages, camouflages: camouflages,
fixed_camouflage: fixedCamouflage, fixed_camouflage: fixedCamouflage,
@@ -1011,10 +1054,10 @@ export async function definitions() {
slug, slug,
nation, nation,
type: tankTags.includes("collectible") type: tankTags.includes("collectible")
? TankType.COLLECTOR ? TankType.TANK_TYPE_COLLECTOR
: (typeof tank.price === "number" ? false : "gold" in tank.price) : (typeof tank.price === "number" ? false : "gold" in tank.price)
? TankType.PREMIUM ? TankType.TANK_TYPE_PREMIUM
: TankType.RESEARCHABLE, : TankType.TANK_TYPE_RESEARCHABLE,
tier: tank.level, tier: tank.level,
class: blitzTankClassToBlitzkit[tankTags[0] as BlitzTankClass], class: blitzTankClassToBlitzkit[tankTags[0] as BlitzTankClass],
testing: tankTags.includes("testTank"), testing: tankTags.includes("testTank"),
@@ -1022,21 +1065,22 @@ export async function definitions() {
price: tankPrice, price: tankPrice,
camouflage_still: tankDefinition.root.invisibility.still, camouflage_still: tankDefinition.root.invisibility.still,
camouflage_moving: tankDefinition.root.invisibility.moving, camouflage_moving: tankDefinition.root.invisibility.moving,
camouflage_onFire: tankDefinition.root.invisibility.firePenalty, camouflage_on_fire: tankDefinition.root.invisibility.firePenalty,
turrets: [], turrets: [],
engines: [], engines: [],
tracks: [], tracks: [],
equalizer,
}; };
if (tank.combatRole) { if (tank.combatRole) {
Object.entries(tank.combatRole).forEach(([gameMode, role]) => { Object.entries(tank.combatRole).forEach(([gameMode, role]) => {
const id = Object.entries(gameModeNativeNames).find( const id = Object.entries(gameModeNativeNames).find(
([key]) => key.toLowerCase() === gameMode.toLowerCase() ([key]) => key.toLowerCase() === gameMode.toLowerCase(),
)?.[1]; )?.[1];
if (id === undefined) { if (id === undefined) {
throw new Error( throw new Error(
`Unknown game mode in tank ${tankKey}: ${gameMode}` `Unknown game mode in tank ${tankKey}: ${gameMode}`,
); );
} }
@@ -1118,7 +1162,7 @@ export async function definitions() {
(turretKey, turretIndex) => { (turretKey, turretIndex) => {
const turret = tankDefinition.root.turrets0[turretKey]; const turret = tankDefinition.root.turrets0[turretKey];
const turretModel = Number( const turretModel = Number(
parsePath(turret.models.undamaged).name.split("_")[1] parsePath(turret.models.undamaged).name.split("_")[1],
); );
const turretId = toUniqueId(nation, turretList.root.ids[turretKey]); const turretId = toUniqueId(nation, turretList.root.ids[turretKey]);
const turretYaw = ( const turretYaw = (
@@ -1150,13 +1194,7 @@ export async function definitions() {
const armorId = parseInt(armorIdString); const armorId = parseInt(armorIdString);
const armorRaw = turret.armor[name]; const armorRaw = turret.armor[name];
if (typeof armorRaw === "number") { assignArmor(armorRaw, armorId, turretArmor);
turretArmor.thickness[armorId] = armorRaw;
} else {
if (!turretArmor.spaced) turretArmor.spaced = [];
turretArmor.thickness[armorId] = armorRaw["#text"];
turretArmor.spaced.push(armorId);
}
}); });
turret.userString; turret.userString;
@@ -1186,12 +1224,12 @@ export async function definitions() {
min: vector3TupleToBlitzkit( min: vector3TupleToBlitzkit(
tankParameters.collision[ tankParameters.collision[
parsePath(turret.hitTester.collisionModel).name.toLowerCase() parsePath(turret.hitTester.collisionModel).name.toLowerCase()
].bbox.min ].bbox.min,
), ),
max: vector3TupleToBlitzkit( max: vector3TupleToBlitzkit(
tankParameters.collision[ tankParameters.collision[
parsePath(turret.hitTester.collisionModel).name.toLowerCase() parsePath(turret.hitTester.collisionModel).name.toLowerCase()
].bbox.max ].bbox.max,
), ),
}, },
}; };
@@ -1209,7 +1247,7 @@ export async function definitions() {
.split(" ") .split(" ")
.map(Number) as [number, number]; .map(Number) as [number, number];
const gunModel = Number( const gunModel = Number(
parsePath(gun.models.undamaged).name.split("_")[1] parsePath(gun.models.undamaged).name.split("_")[1],
); );
const gunName = getString(gunListEntry.userString); const gunName = getString(gunListEntry.userString);
const gunType = const gunType =
@@ -1287,13 +1325,8 @@ export async function definitions() {
} }
const armorId = parseInt(armorIdString); const armorId = parseInt(armorIdString);
const armorRaw = gun.armor[name]; const armorRaw = gun.armor[name];
if (typeof armorRaw === "number") {
gunArmor.thickness[armorId] = armorRaw; assignArmor(armorRaw, armorId, gunArmor);
} else {
if (!gunArmor.spaced) gunArmor.spaced = [];
gunArmor.thickness[armorId] = armorRaw["#text"];
gunArmor.spaced.push(armorId);
}
}); });
tankDefinitions.tanks[tankId].turrets[turretIndex].guns.push({ tankDefinitions.tanks[tankId].turrets[turretIndex].guns.push({
@@ -1332,24 +1365,24 @@ export async function definitions() {
}, },
} }
: gunType === "autoReloader" : gunType === "autoReloader"
? { ? {
$case: "auto_reloader", $case: "auto_reloader",
value: { value: {
intra_clip: 60 / gun.clip!.rate, intra_clip: 60 / gun.clip!.rate,
shell_count: gunClipCount, shell_count: gunClipCount,
shell_reloads: gun shell_reloads: gun
.pumpGunReloadTimes!.split(" ") .pumpGunReloadTimes!.split(" ")
.map(Number), .map(Number),
},
}
: {
$case: "auto_loader",
value: {
intra_clip: 60 / gun.clip!.rate,
clip_reload: gun.reloadTime,
shell_count: gunClipCount,
},
}, },
}
: {
$case: "auto_loader",
value: {
intra_clip: 60 / gun.clip!.rate,
clip_reload: gun.reloadTime,
shell_count: gunClipCount,
},
},
} satisfies GunDefinition); } satisfies GunDefinition);
modelDefinitions.models[tankId].turrets[turretId].guns[gunId] = { modelDefinitions.models[tankId].turrets[turretId].guns[gunId] = {
@@ -1360,8 +1393,8 @@ export async function definitions() {
gun.armor.gun === undefined gun.armor.gun === undefined
? 0 ? 0
: typeof gun.armor.gun === "number" : typeof gun.armor.gun === "number"
? gun.armor.gun ? gun.armor.gun
: gun.armor.gun["#text"], : gun.armor.gun["#text"],
pitch: { pitch: {
min: gunPitch[0], min: gunPitch[0],
max: gunPitch[1], max: gunPitch[1],
@@ -1408,7 +1441,7 @@ export async function definitions() {
type: blitzShellKindToBlitzkit[shell.kind], type: blitzShellKindToBlitzkit[shell.kind],
explosion_radius: explosion_radius:
shell.kind === "HIGH_EXPLOSIVE" shell.kind === "HIGH_EXPLOSIVE"
? shell.explosionRadius ?? 0 ? (shell.explosionRadius ?? 0)
: undefined, : undefined,
icon: shell.icon, icon: shell.icon,
penetration: { penetration: {
@@ -1419,7 +1452,7 @@ export async function definitions() {
}); });
}); });
}); });
} },
); );
totalUnlocks.forEach((unlocks) => { totalUnlocks.forEach((unlocks) => {
@@ -1447,7 +1480,7 @@ export async function definitions() {
case "gun": { case "gun": {
gunXps.set( gunXps.set(
toUniqueId(nation, gunList.root.ids[vehicle["#text"]]), toUniqueId(nation, gunList.root.ids[vehicle["#text"]]),
parseResearchCost(vehicle.cost) parseResearchCost(vehicle.cost),
); );
break; break;
} }
@@ -1455,7 +1488,7 @@ export async function definitions() {
case "turret": { case "turret": {
turretXps.set( turretXps.set(
toUniqueId(nation, turretList.root.ids[vehicle["#text"]]), toUniqueId(nation, turretList.root.ids[vehicle["#text"]]),
parseResearchCost(vehicle.cost) parseResearchCost(vehicle.cost),
); );
break; break;
} }
@@ -1463,7 +1496,7 @@ export async function definitions() {
case "engine": { case "engine": {
engineXps.set( engineXps.set(
toUniqueId(nation, enginesList.root.ids[vehicle["#text"]]), toUniqueId(nation, enginesList.root.ids[vehicle["#text"]]),
parseResearchCost(vehicle.cost) parseResearchCost(vehicle.cost),
); );
break; break;
} }
@@ -1471,7 +1504,7 @@ export async function definitions() {
case "chassis": { case "chassis": {
trackXps.set( trackXps.set(
toUniqueId(nation, chassisList.root.ids[vehicle["#text"]]), toUniqueId(nation, chassisList.root.ids[vehicle["#text"]]),
parseResearchCost(vehicle.cost) parseResearchCost(vehicle.cost),
); );
break; break;
} }
@@ -1518,7 +1551,7 @@ export async function definitions() {
name: getString(optionalDeviceEntry.userString), name: getString(optionalDeviceEntry.userString),
description: getString(optionalDeviceEntry.description), description: getString(optionalDeviceEntry.description),
}; };
} },
); );
Object.entries(optionalDeviceSlots.root.presets).forEach( Object.entries(optionalDeviceSlots.root.presets).forEach(
@@ -1537,7 +1570,7 @@ export async function definitions() {
}) })
.flat(), .flat(),
}; };
} },
); );
Object.entries(consumables).forEach(([key, consumable]) => { Object.entries(consumables).forEach(([key, consumable]) => {
@@ -1620,7 +1653,7 @@ export async function definitions() {
(item) => (item) =>
blitzTankFilterDefinitionCategoryToBlitzkit[ blitzTankFilterDefinitionCategoryToBlitzkit[
item as BlitzTankFilterDefinitionCategory item as BlitzTankFilterDefinitionCategory
] ],
), ),
}, },
}, },
@@ -1718,7 +1751,7 @@ export async function definitions() {
(item) => (item) =>
blitzTankFilterDefinitionCategoryToBlitzkit[ blitzTankFilterDefinitionCategoryToBlitzkit[
item as BlitzTankFilterDefinitionCategory item as BlitzTankFilterDefinitionCategory
] ],
), ),
}, },
}, },
@@ -1749,11 +1782,11 @@ export async function definitions() {
value.default_abilities.forEach((ability) => { value.default_abilities.forEach((ability) => {
if (ability in consumableNativeNames) { if (ability in consumableNativeNames) {
gameDefinitions.roles[value.id].consumables.push( gameDefinitions.roles[value.id].consumables.push(
consumableNativeNames[ability] consumableNativeNames[ability],
); );
} else if (ability in provisionNativeNames) { } else if (ability in provisionNativeNames) {
gameDefinitions.roles[value.id].provisions.push( gameDefinitions.roles[value.id].provisions.push(
provisionNativeNames[ability] provisionNativeNames[ability],
); );
} else throw new Error(`Unknown ability ${ability}`); } else throw new Error(`Unknown ability ${ability}`);
}); });
@@ -17,15 +17,15 @@ export async function equipmentIcons() {
using uploader = new AssetUploader("equipment icons"); using uploader = new AssetUploader("equipment icons");
const optionalDevices = await vfs.xml<{ root: OptionalDevices }>( const optionalDevices = await vfs.xml<{ root: OptionalDevices }>(
`Data/XML/item_defs/vehicles/common/optional_devices.xml` `Data/XML/item_defs/vehicles/common/optional_devices.xml`,
); );
const mappings = await vfs.yaml<Mappings>( const mappings = await vfs.yaml<Mappings>(
`Data/UI/Screens3/Lobby/Inventory/OptionalDevices/OptionalDevicesItemImage.style.yaml` `Data/UI/Screens3/Lobby/Inventory/OptionalDevices/OptionalDevicesItemImage.style.yaml`,
); );
const image = sharp( const image = sharp(
await vfs.file( await vfs.file(
`Data/Gfx/UI/InventoryIcons/Big/OptionalDevices/texture0.packed.webp` `Data/Gfx/UI/InventoryIcons/Big/OptionalDevices/texture0.packed.webp`,
) ),
); );
for (const optionalDeviceKey in optionalDevices.root) { for (const optionalDeviceKey in optionalDevices.root) {
@@ -35,8 +35,8 @@ export async function equipmentIcons() {
const mapping = mappings.StyleSheets.find((mapping) => const mapping = mappings.StyleSheets.find((mapping) =>
mapping.selectors.includes( mapping.selectors.includes(
`.optional_device_item.${optionalDevice.icon} #Img` `.optional_device_item.${optionalDevice.icon} #Img`,
) ),
); );
if (!mapping) { if (!mapping) {
@@ -49,9 +49,7 @@ export async function equipmentIcons() {
if (configPath.startsWith("Gfx/Lobby")) { if (configPath.startsWith("Gfx/Lobby")) {
const configPathWebp = configPath.replace(".txt", ""); const configPathWebp = configPath.replace(".txt", "");
const image = sharp( const image = sharp(await vfs.file(`Data/${configPathWebp}.packed.webp`));
await vfs.file(`Data/${configPathWebp}.packed.webp`)
);
const content = await image.trim().toBuffer(); const content = await image.trim().toBuffer();
await uploader.add({ await uploader.add({
+24 -14
View File
@@ -6,20 +6,32 @@ export async function flags() {
console.log("Building flags..."); console.log("Building flags...");
using uploader = new AssetUploader("flags"); using uploader = new AssetUploader("flags");
const circleFiles = vfs.dir(`Data/Gfx/Lobby/flags`).filter( const circleFiles = await vfs
(flag) => .dir(`Data/Gfx/Lobby/flags`)
flag.startsWith("flag_profile-stat_") && .then((files) =>
!flag.endsWith("@2x.packed.webp") files.filter(
(flag) =>
flag.startsWith("flag_profile-stat_") &&
!flag.endsWith("@2x.packed.webp"),
),
); );
const scratchedFiles = vfs.dir(`Data/Gfx/Lobby/flags`).filter( const scratchedFiles = await vfs
.dir(`Data/Gfx/Lobby/flags`)
.then((files) =>
files.filter(
(flag) => (flag) =>
flag.startsWith("flag_tutor-tank_") && flag.startsWith("flag_tutor-tank_") &&
!flag.endsWith("@2x.packed.webp") !flag.endsWith("@2x.packed.webp"),
) ),
const fadedFiles = vfs.dir(`Data/Gfx/Lobby/flags`).filter( );
(flag) => const fadedFiles = await vfs
flag.startsWith("flag_filter_") && flag.endsWith("@2x.packed.webp") .dir(`Data/Gfx/Lobby/flags`)
) .then((files) =>
files.filter(
(flag) =>
flag.startsWith("flag_filter_") && flag.endsWith("@2x.packed.webp"),
),
);
for (const flag of circleFiles) { for (const flag of circleFiles) {
const image = sharp(await vfs.file(`Data/Gfx/Lobby/flags/${flag}`)); const image = sharp(await vfs.file(`Data/Gfx/Lobby/flags/${flag}`));
@@ -43,9 +55,7 @@ export async function flags() {
} }
for (const flag of fadedFiles) { for (const flag of fadedFiles) {
const content = await sharp( const content = await sharp(await vfs.file(`Data/Gfx/Lobby/flags/${flag}`))
await vfs.file(`Data/Gfx/Lobby/flags/${flag}`)
)
.trim({ .trim({
threshold: 100, threshold: 100,
background: { r: 0, g: 0, b: 0, alpha: 0 }, background: { r: 0, g: 0, b: 0, alpha: 0 },
@@ -8,35 +8,33 @@ export async function gameModeBanners() {
using uploader = new AssetUploader("game mode banners"); using uploader = new AssetUploader("game mode banners");
const gameTypeSelectorStyles = await vfs.yaml<SquadBattleTypeStyles>( const gameTypeSelectorStyles = await vfs.yaml<SquadBattleTypeStyles>(
`Data/UI/Screens/Lobby/Hangar/GameTypeSelector.yaml` `Data/UI/Screens/Lobby/Hangar/GameTypeSelector.yaml`,
); );
const squadBattleTypeStyles = await vfs.yaml<SquadBattleTypeStyles>( const squadBattleTypeStyles = await vfs.yaml<SquadBattleTypeStyles>(
`Data/UI/Screens3/Lobby/Hangar/Squad/SquadBattleType.yaml` `Data/UI/Screens3/Lobby/Hangar/Squad/SquadBattleType.yaml`,
); );
const bannerMatches: { name: string; path: string }[] = []; const bannerMatches: { name: string; path: string }[] = [];
for (const match of gameTypeSelectorStyles.Prototypes[0].components.UIDataLocalBindingsComponent.data[1][2].matchAll( for (const match of gameTypeSelectorStyles.Prototypes[0].components.UIDataLocalBindingsComponent.data[1][2].matchAll(
/eGameMode\.([a-zA-Z]+) -> "~res:([^"]+)"/g /eGameMode\.([a-zA-Z]+) -> "~res:([^"]+)"/g,
)) { )) {
bannerMatches.push({ name: match[1], path: match[2] }); bannerMatches.push({ name: match[1], path: match[2] });
} }
for (const match of squadBattleTypeStyles.Prototypes[0].components.UIDataLocalBindingsComponent.data[1][2].matchAll( for (const match of squadBattleTypeStyles.Prototypes[0].components.UIDataLocalBindingsComponent.data[1][2].matchAll(
/"(\d+)" -> "battleType\/([a-zA-Z]+)"/g /"(\d+)" -> "battleType\/([a-zA-Z]+)"/g,
)) { )) {
const id = Number(match[1]); const id = Number(match[1]);
const name = match[2]; const name = match[2];
let path = bannerMatches.find( let path = bannerMatches.find(
(bannerMatch) => bannerMatch.name.toLowerCase() === name.toLowerCase() (bannerMatch) => bannerMatch.name.toLowerCase() === name.toLowerCase(),
)?.path; )?.path;
if (path === undefined) { if (path === undefined) {
path = `/Gfx/UI/Hangar/GameTypes/battle-type_${name.toLowerCase()}`; path = `/Gfx/UI/Hangar/GameTypes/battle-type_${name.toLowerCase()}`;
} }
const content = await sharp( const content = await sharp(await vfs.file(`Data${path}.packed.webp`))
await vfs.file(`Data${path}.packed.webp`)
)
.trim({ background: { r: 0, g: 0, b: 0, alpha: 0 } }) .trim({ background: { r: 0, g: 0, b: 0, alpha: 0 } })
.toBuffer(); .toBuffer();
+6 -6
View File
@@ -15,7 +15,7 @@ export async function glossary() {
const glossary = await fetchGlossary(supported.blitz ?? supported.locale); const glossary = await fetchGlossary(supported.blitz ?? supported.locale);
console.log( console.log(
`Found ${Object.keys(glossary).length} things for ${supported.locale}` `Found ${Object.keys(glossary).length} things for ${supported.locale}`,
); );
for (const key in glossary) { for (const key in glossary) {
@@ -30,7 +30,7 @@ export async function glossary() {
} }
if (key in avatars) { if (key in avatars) {
avatars[key].avatar.name.locales[supported.locale] = avatars[key].avatar.name!.locales[supported.locale] =
glossaryEntry.title; glossaryEntry.title;
} else { } else {
const extension = extname(glossaryEntry.image_url); const extension = extname(glossaryEntry.image_url);
@@ -45,7 +45,7 @@ export async function glossary() {
}; };
} }
} }
}) }),
); );
const gallery: Gallery = { avatars: [] }; const gallery: Gallery = { avatars: [] };
@@ -53,12 +53,12 @@ export async function glossary() {
const bar = new ProgressBar( const bar = new ProgressBar(
`Fetching avatars of ${totalCount} things :bar`, `Fetching avatars of ${totalCount} things :bar`,
totalCount totalCount,
); );
await Promise.all( await Promise.all(
Object.entries(avatars).map(async ([key, { url, avatar }]) => { Object.entries(avatars).map(async ([key, { url, avatar }]) => {
if (!(locales.default in avatars[key].avatar.name.locales)) { if (!(locales.default in avatars[key].avatar.name!.locales)) {
console.warn(`Avatar ${key} has no ${locales.default} name`); console.warn(`Avatar ${key} has no ${locales.default} name`);
bar.tick(); bar.tick();
@@ -77,7 +77,7 @@ export async function glossary() {
}); });
bar.tick(); bar.tick();
}) }),
); );
await uploader.add({ await uploader.add({
@@ -8,11 +8,13 @@ export async function migration() {
using uploader = new AssetUploader("migration map"); using uploader = new AssetUploader("migration map");
const map: Record<string, number> = {}; const map: Record<string, number> = {};
const nations = vfs.dir(`Data/XML/item_defs/vehicles`).filter((nation) => nation !== "common"); const nations = await vfs
.dir(`Data/XML/item_defs/vehicles`)
.then((files) => files.filter((nation) => nation !== "common"));
for (const nation of nations) { for (const nation of nations) {
const tankList = await vfs.xml<{ root: VehicleDefinitionList }>( const tankList = await vfs.xml<{ root: VehicleDefinitionList }>(
`Data/XML/item_defs/vehicles/${nation}/list.xml` `Data/XML/item_defs/vehicles/${nation}/list.xml`,
); );
for (const tankKey in tankList.root) { for (const tankKey in tankList.root) {
@@ -6,15 +6,20 @@ export async function moduleIcons() {
console.log("Building module icons..."); console.log("Building module icons...");
using uploader = new AssetUploader("module icons"); using uploader = new AssetUploader("module icons");
const files = vfs.dir(`Data/Gfx/UI/ModulesTechTree`).filter( const files = await vfs
(file) => !file.endsWith("@2x.packed.webp") && file.startsWith("vehicle") .dir(`Data/Gfx/UI/ModulesTechTree`)
) .then((files) =>
files.filter(
(file) =>
!file.endsWith("@2x.packed.webp") && file.startsWith("vehicle"),
),
);
for (const file of files) { for (const file of files) {
const nameRaw = file.match(/vehicle(.+)\.packed\.webp/)![1]; const nameRaw = file.match(/vehicle(.+)\.packed\.webp/)![1];
const name = nameRaw[0].toLowerCase() + nameRaw.slice(1); const name = nameRaw[0].toLowerCase() + nameRaw.slice(1);
const content = await sharp( const content = await sharp(
await vfs.file(`Data/Gfx/UI/ModulesTechTree/${file}`) await vfs.file(`Data/Gfx/UI/ModulesTechTree/${file}`),
) )
.trim() .trim()
.toBuffer(); .toBuffer();
@@ -17,7 +17,7 @@ export async function popularTanks() {
const analytics = google.analyticsdata({ version: "v1beta", auth }); const analytics = google.analyticsdata({ version: "v1beta", auth });
const report = await analytics.properties.runReport({ const report = await analytics.properties.runReport({
property: `properties/${assertSecret( property: `properties/${assertSecret(
import.meta.env.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID import.meta.env.PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID,
)}`, )}`,
requestBody: { requestBody: {
dateRanges: [{ startDate: "7daysAgo", endDate: "today" }], dateRanges: [{ startDate: "7daysAgo", endDate: "today" }],
@@ -47,13 +47,13 @@ export async function popularTanks() {
row.dimensionValues[0].value && row.dimensionValues[0].value &&
row.dimensionValues[0].value !== "/tanks/" && row.dimensionValues[0].value !== "/tanks/" &&
row.metricValues && row.metricValues &&
row.metricValues[0].value row.metricValues[0].value,
) )
.map((row) => ({ .map((row) => ({
id: Number( id: Number(
row.dimensionValues![0].value!.match( row.dimensionValues![0].value!.match(
/\/tools\/tankopedia\/(\d+)\/?/ /\/tools\/tankopedia\/(\d+)\/?/,
)?.[1] )?.[1],
), ),
views: Number(row.metricValues![0].value!), views: Number(row.metricValues![0].value!),
})) }))
@@ -8,19 +8,17 @@ export async function shellIcons() {
using uploader = new AssetUploader("shell icons"); using uploader = new AssetUploader("shell icons");
const image = sharp( const image = sharp(
await vfs.file( await vfs.file(
`Data/Gfx/Shared/tank-supply/ammunition/big/texture0.packed.webp` `Data/Gfx/Shared/tank-supply/ammunition/big/texture0.packed.webp`,
) ),
); );
const files = vfs.dir( const files = await vfs
`Data/Gfx/Shared/tank-supply/ammunition/big` .dir(`Data/Gfx/Shared/tank-supply/ammunition/big`)
).filter((file) => file.endsWith("_l.txt")) .then((files) => files.filter((file) => file.endsWith("_l.txt")));
for (const file of files) { for (const file of files) {
const name = file.match(/(.+)_l\.txt/)![1]; const name = file.match(/(.+)_l\.txt/)![1];
const sizes = ( const sizes = (
await vfs.text( await vfs.text(`Data/Gfx/Shared/tank-supply/ammunition/big/${file}`)
`Data/Gfx/Shared/tank-supply/ammunition/big/${file}`
)
) )
.split("\n")[4] .split("\n")[4]
.split(" ") .split(" ")
@@ -26,7 +26,7 @@ export async function skillIcons() {
console.log("Building skill icons..."); console.log("Building skill icons...");
const avatar = await vfs.xml<{ root: Avatar }>( const avatar = await vfs.xml<{ root: Avatar }>(
`Data/XML/item_defs/tankmen/avatar.xml` `Data/XML/item_defs/tankmen/avatar.xml`,
); );
const uploader = new AssetUploader("skill icons"); const uploader = new AssetUploader("skill icons");
@@ -10,11 +10,13 @@ export async function tankArmors() {
using uploader = new AssetUploader("tank armors"); using uploader = new AssetUploader("tank armors");
const nodeIO = new NodeIO(); const nodeIO = new NodeIO();
const nations = vfs.dir(`Data/XML/item_defs/vehicles`).filter((nation) => nation !== "common"); const nations = await vfs
.dir(`Data/XML/item_defs/vehicles`)
.then((files) => files.filter((nation) => nation !== "common"));
for (const nation of nations) { for (const nation of nations) {
const tanks = await vfs.xml<{ root: VehicleDefinitionList }>( const tanks = await vfs.xml<{ root: VehicleDefinitionList }>(
`Data/XML/item_defs/vehicles/${nation}/list.xml` `Data/XML/item_defs/vehicles/${nation}/list.xml`,
); );
for (const tankKey in tanks.root) { for (const tankKey in tanks.root) {
@@ -41,11 +41,13 @@ export async function tankIcons() {
console.log("Building tank icons..."); console.log("Building tank icons...");
using uploader = new AssetUploader("tank icons"); using uploader = new AssetUploader("tank icons");
const nations = vfs.dir(`Data/XML/item_defs/vehicles`).filter((nation) => nation !== "common") const nations = await vfs
.dir(`Data/XML/item_defs/vehicles`)
.then((files) => files.filter((nation) => nation !== "common"));
for (const nation of nations) { for (const nation of nations) {
const tanks = await vfs.xml<{ root: VehicleDefinitionList }>( const tanks = await vfs.xml<{ root: VehicleDefinitionList }>(
`Data/XML/item_defs/vehicles/${nation}/list.xml` `Data/XML/item_defs/vehicles/${nation}/list.xml`,
); );
for (const tankKey in tanks.root) { for (const tankKey in tanks.root) {
@@ -57,7 +59,7 @@ export async function tankIcons() {
const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1; const id = (nationVehicleId << 8) + (NATION_IDS[nation] << 4) + 1;
const parameters = await vfs.yaml<TankParameters>( const parameters = await vfs.yaml<TankParameters>(
`Data/3d/Tanks/Parameters/${nation}/${tankKey}.yaml` `Data/3d/Tanks/Parameters/${nation}/${tankKey}.yaml`,
); );
const smallPath = `Data/${parameters.resourcesPath.smallIconPath const smallPath = `Data/${parameters.resourcesPath.smallIconPath
.replace(/~res:\//, "") .replace(/~res:\//, "")
@@ -12,9 +12,9 @@ export async function tankModels() {
using uploader = new AssetUploader("tank models"); using uploader = new AssetUploader("tank models");
const nodeIO = new NodeIO().registerExtensions(ALL_EXTENSIONS); const nodeIO = new NodeIO().registerExtensions(ALL_EXTENSIONS);
const nations = vfs const nations = await vfs
.dir(`Data/XML/item_defs/vehicles`) .dir(`Data/XML/item_defs/vehicles`)
.filter((nation) => nation !== "common"); .then((files) => files.filter((nation) => nation !== "common"));
for (const nationIndex in nations) { for (const nationIndex in nations) {
const nation = nations[nationIndex]; const nation = nations[nationIndex];
@@ -41,4 +41,4 @@ export async function tankModels() {
} }
await uploader.flush(); await uploader.flush();
} }
@@ -170,6 +170,7 @@ export async function extractModel(vfs: AbstractVFS, path: string) {
break; break;
case "TransformComponent": { case "TransformComponent": {
component["tc.localTranslation"] = [0, 0, 0]; // Game when is loading model resets position to [0, 0, 0], so any set value is ignored.
node.setTranslation(component["tc.localTranslation"]); node.setTranslation(component["tc.localTranslation"]);
node.setRotation(component["tc.localRotation"]); node.setRotation(component["tc.localRotation"]);
node.setScale(component["tc.localScale"]); node.setScale(component["tc.localScale"]);
@@ -3,7 +3,7 @@ import { vfs } from "../../../buildAssets/constants";
export async function readTexture(path: string) { export async function readTexture(path: string) {
const ddsTexturePath = path.replace(".tex", ".dx11.dds"); const ddsTexturePath = path.replace(".tex", ".dx11.dds");
const isDds = vfs.resolve(ddsTexturePath); const isDds = await vfs.resolve(ddsTexturePath);
const resolvedTexturePath = isDds const resolvedTexturePath = isDds
? ddsTexturePath ? ddsTexturePath
: ddsTexturePath.replace(".dds", ".pvr"); : ddsTexturePath.replace(".dds", ".pvr");
+4 -4
View File
@@ -1,5 +1,5 @@
import { crc32 } from 'crc'; import { crc32 } from "crc";
import { compressBlock } from 'lz4js'; import { compressBlock } from "lz4js";
/** /**
* Thanks Maddoxkkm! Modernized heavily. * Thanks Maddoxkkm! Modernized heavily.
@@ -14,7 +14,7 @@ export function writeDVPL(buffer: Buffer) {
destination, destination,
0, 0,
source.length, source.length,
0, [0],
); );
let output = Buffer.from(destination); let output = Buffer.from(destination);
@@ -58,7 +58,7 @@ function toDVPLFooter(
result.writeUInt32LE(compressedSize, 4); result.writeUInt32LE(compressedSize, 4);
result.writeUInt32LE(crc32, 8); result.writeUInt32LE(crc32, 8);
result.writeUInt32LE(type, 12); result.writeUInt32LE(type, 12);
result.write('DVPL', 16, 4, 'utf8'); result.write("DVPL", 16, 4, "utf8");
return result; return result;
} }
@@ -43,7 +43,7 @@ export class AssetUploader {
constructor(public message: string) { constructor(public message: string) {
[this.owner, this.repo] = assertSecret( [this.owner, this.repo] = assertSecret(
import.meta.env.PUBLIC_ASSET_REPO import.meta.env.PUBLIC_ASSET_REPO,
).split("/"); ).split("/");
this.branch = assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH); this.branch = assertSecret(import.meta.env.PUBLIC_ASSET_BRANCH);
} }
@@ -52,7 +52,7 @@ export class AssetUploader {
const blobPath = `${this.owner}/${this.repo}/${this.branch}/${change.path}`; const blobPath = `${this.owner}/${this.repo}/${this.branch}/${change.path}`;
const response = await fetch( const response = await fetch(
`https://raw.githubusercontent.com/${blobPath}`, `https://raw.githubusercontent.com/${blobPath}`,
{ headers: { "Accept-Encoding": "identity" } } { headers: { "Accept-Encoding": "identity" } },
); );
let diff: { change: number; status: DiffStatus }; let diff: { change: number; status: DiffStatus };
@@ -71,7 +71,7 @@ export class AssetUploader {
} else if (response.status === 200) { } else if (response.status === 200) {
if ( if (
this.heuristicFormats.some((format) => this.heuristicFormats.some((format) =>
change.path.endsWith(`.${format}`) change.path.endsWith(`.${format}`),
) )
) { ) {
/** /**
@@ -94,7 +94,7 @@ export class AssetUploader {
} }
} else { } else {
throw new Error( throw new Error(
`Unexpected status code ${response.status} for ${change.path}` `Unexpected status code ${response.status} for ${change.path}`,
); );
} }
@@ -104,7 +104,7 @@ export class AssetUploader {
console.log( console.log(
`${diff.status === DiffStatus.New ? "🟢" : "🟡"} (${ `${diff.status === DiffStatus.New ? "🟢" : "🟡"} (${
diff.change > 0 ? "+" : "" diff.change > 0 ? "+" : ""
}${diff.change.toLocaleString()}B) ${change.path}` }${diff.change.toLocaleString()}B) ${change.path}`,
); );
// flush BEFORE going over limit // flush BEFORE going over limit
@@ -160,8 +160,8 @@ export class AssetUploader {
await new Promise((resolve) => await new Promise((resolve) =>
setTimeout( setTimeout(
resolve, resolve,
Math.max(0, this.time_between_blobs + (start - end)) Math.max(0, this.time_between_blobs + (start - end)),
) ),
); );
blobs.push({ sha, path: change.path, mode: "100644", type: "blob" }); blobs.push({ sha, path: change.path, mode: "100644", type: "blob" });
@@ -170,11 +170,11 @@ export class AssetUploader {
break; break;
} catch { } catch {
console.warn( console.warn(
` failed ${change.path}; retrying in ${this.time_per_blob}ms...` ` failed ${change.path}; retrying in ${this.time_per_blob}ms...`,
); );
await new Promise((resolve) => await new Promise((resolve) =>
setTimeout(resolve, this.time_per_blob) setTimeout(resolve, this.time_per_blob),
); );
} }
} }
@@ -209,7 +209,7 @@ export class AssetUploader {
[Symbol.dispose]() { [Symbol.dispose]() {
if (this.changes.length > 0) { if (this.changes.length > 0) {
throw new Error( throw new Error(
`${this.changes.length} uncommitted changes. Did you forget to flush?` `${this.changes.length} uncommitted changes. Did you forget to flush?`,
); );
} }
} }
+21 -26
View File
@@ -1,7 +1,8 @@
import { readDVPL } from "@blitzkit/core"; import { readDVPL } from "@blitzkit/core";
import { Options, parse as parseCSV } from "csv-parse/sync";
import { XMLParser } from "fast-xml-parser"; import { XMLParser } from "fast-xml-parser";
import { normalize } from "path/posix"; import { normalize } from "path/posix";
import { parse } from "yaml"; import { parse as parseYaml } from "yaml";
export abstract class AbstractVFS { export abstract class AbstractVFS {
textDecoder = new TextDecoder(); textDecoder = new TextDecoder();
@@ -11,24 +12,22 @@ export abstract class AbstractVFS {
abstract dispose(): void; abstract dispose(): void;
abstract has(path: string): boolean; abstract has(path: string): Promise<boolean>;
abstract raw(path: string): Promise<Uint8Array>; abstract raw(path: string): Promise<Uint8Array>;
abstract paths(): string[]; async resolve(path: string) {
resolve(path: string) {
const normalized = normalize(path); const normalized = normalize(path);
if (this.has(normalized)) return normalized; if (await this.has(normalized)) return normalized;
const dvplPath = `${normalized}.dvpl`; const dvplPath = `${normalized}.dvpl`;
if (this.has(dvplPath)) return dvplPath; if (await this.has(dvplPath)) return dvplPath;
return null; return null;
} }
assert(path: string) { async assert(path: string) {
const resolved = this.resolve(path); const resolved = await this.resolve(path);
if (resolved === null) throw new Error(`File not found: ${path}`); if (resolved === null) throw new Error(`File not found: ${path}`);
@@ -36,7 +35,7 @@ export abstract class AbstractVFS {
} }
async file(file: string) { async file(file: string) {
const resolved = this.assert(file); const resolved = await this.assert(file);
const raw = await this.raw(resolved); const raw = await this.raw(resolved);
let buffer = raw; let buffer = raw;
@@ -47,21 +46,7 @@ export abstract class AbstractVFS {
return buffer; return buffer;
} }
dir(path: string) { abstract dir(path: string): Promise<string[]>;
const parentSegments = path.split("/").length;
const children = new Set<string>();
for (const child of this.paths()) {
if (!child.startsWith(path) || child === path) continue;
const childSegments = child.split("/");
const nextSegment = childSegments[parentSegments];
children.add(nextSegment);
}
return Array.from(children);
}
async text(path: string) { async text(path: string) {
const file = await this.file(path); const file = await this.file(path);
@@ -70,7 +55,7 @@ export abstract class AbstractVFS {
async yaml<Type>(path: string) { async yaml<Type>(path: string) {
const file = await this.text(path); const file = await this.text(path);
return parse(file) as Type; return parseYaml(file) as Type;
} }
async xml<Type>(path: string) { async xml<Type>(path: string) {
@@ -78,6 +63,16 @@ export abstract class AbstractVFS {
return this.xmlParser.parse(file) as Type; return this.xmlParser.parse(file) as Type;
} }
async _csv(text: string, options: Options = {}) {
const parsed = parseCSV(text, options);
return parsed;
}
async csv(path: string, options: Options = {}) {
const text = await this.text(path);
return await this._csv(text, options);
}
[Symbol.dispose]() { [Symbol.dispose]() {
this.dispose(); this.dispose();
} }
+31
View File
@@ -0,0 +1,31 @@
import { readdir } from "node:fs/promises";
import { AbstractVFS } from "./abstract";
export class LocalVFS extends AbstractVFS {
constructor(private base: string) {
super();
}
async init() {
return this;
}
async has(path: string) {
const file = Bun.file(`${this.base}/${path}`);
return await file.exists();
}
async raw(path: string) {
const file = Bun.file(`${this.base}/${path}`);
const buffer = await file.arrayBuffer();
const array = new Uint8Array(buffer);
return array;
}
async dir(path: string) {
return await readdir(`${this.base}/${path}`);
}
dispose() {}
}
+18 -5
View File
@@ -38,7 +38,7 @@ export class SteamVFS extends AbstractVFS {
private username: string, private username: string,
private password: string, private password: string,
private app: number, private app: number,
private depot: number private depot: number,
) { ) {
super(); super();
} }
@@ -57,13 +57,14 @@ export class SteamVFS extends AbstractVFS {
this.steam.getManifest( this.steam.getManifest(
this.app, this.app,
this.depot, this.depot,
// @ts-expect-error
productInfo.apps[this.app].appinfo.depots[this.depot].manifests.public productInfo.apps[this.app].appinfo.depots[this.depot].manifests.public
.gid, .gid,
"public", "public",
// @ts-expect-error // @ts-expect-error
(_, response) => { (_, response) => {
resolve(response); resolve(response);
} },
); );
}); });
@@ -75,7 +76,7 @@ export class SteamVFS extends AbstractVFS {
return this; return this;
} }
has(path: string) { async has(path: string) {
return this.manifest.has(path); return this.manifest.has(path);
} }
@@ -88,8 +89,20 @@ export class SteamVFS extends AbstractVFS {
return new Uint8Array(downloaded.file); return new Uint8Array(downloaded.file);
} }
paths() { async dir(path: string) {
return Array.from(this.manifest.keys()); const parentSegments = path.split("/").length;
const children = new Set<string>();
for (const child of this.manifest.keys()) {
if (!child.startsWith(path) || child === path) continue;
const childSegments = child.split("/");
const nextSegment = childSegments[parentSegments];
children.add(nextSegment);
}
return Array.from(children);
} }
dispose() { dispose() {
+19 -4
View File
@@ -5,7 +5,10 @@ import { AbstractVFS } from "./abstract";
export class ZipVFS extends AbstractVFS { export class ZipVFS extends AbstractVFS {
entries = new Map<string, ZipEntry>(); entries = new Map<string, ZipEntry>();
constructor(private url: string, private root = "app/") { constructor(
private url: string,
private root = "app/",
) {
super(); super();
} }
@@ -21,7 +24,7 @@ export class ZipVFS extends AbstractVFS {
return this; return this;
} }
has(path: string) { async has(path: string) {
return this.entries.has(path); return this.entries.has(path);
} }
@@ -30,8 +33,20 @@ export class ZipVFS extends AbstractVFS {
return new Uint8Array(arrayBuffer); return new Uint8Array(arrayBuffer);
} }
paths() { async dir(path: string) {
return Array.from(this.entries.keys()); const parentSegments = path.split("/").length;
const children = new Set<string>();
for (const child of this.entries.keys()) {
if (!child.startsWith(path) || child === path) continue;
const childSegments = child.split("/");
const nextSegment = childSegments[parentSegments];
children.add(nextSegment);
}
return Array.from(children);
} }
dispose() {} dispose() {}
+35 -30
View File
@@ -53,7 +53,7 @@ if (newTanks.length > 0) {
notes += "## New Tanks\n\n"; notes += "## New Tanks\n\n";
for (const tank of newTanks) { for (const tank of newTanks) {
notes += `### [${tank.name.locales.en}](${domain}/tanks/${tank.slug})\n`; notes += `### [${tank.name!.locales.en}](${domain}/tanks/${tank.slug})\n`;
notes += `![](${tankIcon(tank.id)})\n\n`; notes += `![](${tankIcon(tank.id)})\n\n`;
} }
@@ -96,49 +96,49 @@ if (changedTanks.length > 0) {
const previewTankModel = previewModelDefinitions.models[preview.id]; const previewTankModel = previewModelDefinitions.models[preview.id];
const currentConsumables = Object.values( const currentConsumables = Object.values(
currentConsumableDefinitions.consumables currentConsumableDefinitions.consumables,
).filter((consumable) => ).filter((consumable) =>
checkConsumableProvisionInclusivity(consumable, current, currentGun) checkConsumableProvisionInclusivity(consumable, current, currentGun),
); );
const previewConsumables = Object.values( const previewConsumables = Object.values(
previewConsumableDefinitions.consumables previewConsumableDefinitions.consumables,
).filter((consumable) => ).filter((consumable) =>
checkConsumableProvisionInclusivity(consumable, preview, previewGun) checkConsumableProvisionInclusivity(consumable, preview, previewGun),
); );
const addedConsumables = previewConsumables.filter( const addedConsumables = previewConsumables.filter(
(previewConsumable) => (previewConsumable) =>
!currentConsumables.some( !currentConsumables.some(
(currentConsumable) => currentConsumable.id === previewConsumable.id (currentConsumable) => currentConsumable.id === previewConsumable.id,
) ),
); );
const removedConsumables = currentConsumables.filter( const removedConsumables = currentConsumables.filter(
(currentConsumable) => (currentConsumable) =>
!previewConsumables.some( !previewConsumables.some(
(previewConsumable) => previewConsumable.id === currentConsumable.id (previewConsumable) => previewConsumable.id === currentConsumable.id,
) ),
); );
const currentProvisions = availableProvisions( const currentProvisions = availableProvisions(
current, current,
currentGun, currentGun,
currentProvisionDefinitions currentProvisionDefinitions,
); );
const previewProvisions = availableProvisions( const previewProvisions = availableProvisions(
preview, preview,
previewGun, previewGun,
previewProvisionDefinitions previewProvisionDefinitions,
); );
const addedProvisions = previewProvisions.filter( const addedProvisions = previewProvisions.filter(
(previewProvision) => (previewProvision) =>
!currentProvisions.some( !currentProvisions.some(
(currentProvision) => currentProvision.id === previewProvision.id (currentProvision) => currentProvision.id === previewProvision.id,
) ),
); );
const removedProvisions = currentProvisions.filter( const removedProvisions = currentProvisions.filter(
(currentProvision) => (currentProvision) =>
!previewProvisions.some( !previewProvisions.some(
(previewProvision) => previewProvision.id === currentProvision.id (previewProvision) => previewProvision.id === currentProvision.id,
) ),
); );
const currentEquipment = currentEquipmentDefinitions.presets[ const currentEquipment = currentEquipmentDefinitions.presets[
@@ -155,19 +155,20 @@ if (changedTanks.length > 0) {
.filter( .filter(
(previewEquipment) => (previewEquipment) =>
!currentEquipment.some( !currentEquipment.some(
(currentEquipment) => currentEquipment === previewEquipment (currentEquipment) => currentEquipment === previewEquipment,
) ),
) )
.map((id) => previewEquipmentDefinitions.equipments[id]); .map((id) => previewEquipmentDefinitions.equipments[id]);
const removedEquipment = currentEquipment const removedEquipment = currentEquipment
.filter( .filter(
(currentEquipment) => (currentEquipment) =>
!previewEquipment.some( !previewEquipment.some(
(previewEquipment) => previewEquipment === currentEquipment (previewEquipment) => previewEquipment === currentEquipment,
) ),
) )
.map((id) => currentEquipmentDefinitions.equipments[id]); .map((id) => currentEquipmentDefinitions.equipments[id]);
const equalize = false;
const currentCharacteristics = tankCharacteristics( const currentCharacteristics = tankCharacteristics(
{ {
tank: current, tank: current,
@@ -184,7 +185,7 @@ if (changedTanks.length > 0) {
provisions: createDefaultProvisions( provisions: createDefaultProvisions(
current, current,
currentGun, currentGun,
currentProvisionDefinitions currentProvisionDefinitions,
), ),
equipmentMatrix: genericDefaultEquipmentMatrix, equipmentMatrix: genericDefaultEquipmentMatrix,
stockEngine: current.engines[0], stockEngine: current.engines[0],
@@ -193,12 +194,13 @@ if (changedTanks.length > 0) {
stockTurret: current.turrets[0], stockTurret: current.turrets[0],
track: currentTracks, track: currentTracks,
turret: currentTurret, turret: currentTurret,
equalize,
}, },
{ {
equipmentDefinitions: currentEquipmentDefinitions, equipmentDefinitions: currentEquipmentDefinitions,
provisionDefinitions: currentProvisionDefinitions, provisionDefinitions: currentProvisionDefinitions,
tankModelDefinition: currentTankModel, tankModelDefinition: currentTankModel,
} },
); );
const previewCharacteristics = tankCharacteristics( const previewCharacteristics = tankCharacteristics(
{ {
@@ -216,7 +218,7 @@ if (changedTanks.length > 0) {
provisions: createDefaultProvisions( provisions: createDefaultProvisions(
preview, preview,
previewGun, previewGun,
previewProvisionDefinitions previewProvisionDefinitions,
), ),
equipmentMatrix: genericDefaultEquipmentMatrix, equipmentMatrix: genericDefaultEquipmentMatrix,
stockEngine: preview.engines[0], stockEngine: preview.engines[0],
@@ -225,12 +227,13 @@ if (changedTanks.length > 0) {
stockTurret: preview.turrets[0], stockTurret: preview.turrets[0],
track: previewTracks, track: previewTracks,
turret: previewTurret, turret: previewTurret,
equalize,
}, },
{ {
equipmentDefinitions: previewEquipmentDefinitions, equipmentDefinitions: previewEquipmentDefinitions,
provisionDefinitions: previewProvisionDefinitions, provisionDefinitions: previewProvisionDefinitions,
tankModelDefinition: previewTankModel, tankModelDefinition: previewTankModel,
} },
); );
const changedCharacteristics: string[] = []; const changedCharacteristics: string[] = [];
@@ -244,17 +247,17 @@ if (changedTanks.length > 0) {
} }
} }
notes += `### [${preview.name.locales.en}](${domain}/tanks/${preview.slug})\n`; notes += `### [${preview.name!.locales.en}](${domain}/tanks/${preview.slug})\n`;
notes += `![](${tankIcon(preview.id)})\n\n`; notes += `![](${tankIcon(preview.id)})\n\n`;
if (addedConsumables.length + removedConsumables.length > 0) { if (addedConsumables.length + removedConsumables.length > 0) {
notes += `Consumables: `; notes += `Consumables: `;
notes += [ notes += [
...addedConsumables.map( ...addedConsumables.map(
(consumable) => `🟢 ${consumable.name.locales.en}` (consumable) => `🟢 ${consumable.name!.locales.en}`,
), ),
...removedConsumables.map( ...removedConsumables.map(
(consumable) => `🔴 ${consumable.name.locales.en}` (consumable) => `🔴 ${consumable.name!.locales.en}`,
), ),
].join(", "); ].join(", ");
notes += `\n`; notes += `\n`;
@@ -264,10 +267,10 @@ if (changedTanks.length > 0) {
notes += `Provisions: `; notes += `Provisions: `;
notes += [ notes += [
...addedProvisions.map( ...addedProvisions.map(
(provision) => `🟢 ${provision.name.locales.en}` (provision) => `🟢 ${provision.name!.locales.en}`,
), ),
...removedProvisions.map( ...removedProvisions.map(
(provision) => `🔴 ${provision.name.locales.en}` (provision) => `🔴 ${provision.name!.locales.en}`,
), ),
].join(", "); ].join(", ");
notes += `\n`; notes += `\n`;
@@ -276,9 +279,11 @@ if (changedTanks.length > 0) {
if (addedEquipment.length + removedEquipment.length > 0) { if (addedEquipment.length + removedEquipment.length > 0) {
notes += `-Equipment: `; notes += `-Equipment: `;
notes += [ notes += [
...addedEquipment.map((equipment) => `🟢 ${equipment.name.locales.en}`), ...addedEquipment.map(
(equipment) => `🟢 ${equipment.name!.locales.en}`,
),
...removedEquipment.map( ...removedEquipment.map(
(equipment) => `🔴 ${equipment.name.locales.en}` (equipment) => `🔴 ${equipment.name!.locales.en}`,
), ),
].join(", "); ].join(", ");
notes += `\n`; notes += `\n`;
+2 -2
View File
@@ -26,8 +26,8 @@ for (const location of TS_PROTO_EXECUTABLE_LOCATIONS) {
if (!tsProtoExecutableLocation) { if (!tsProtoExecutableLocation) {
throw new Error( throw new Error(
`Could not find ts-proto executable. Checked locations: ${TS_PROTO_EXECUTABLE_LOCATIONS.join( `Could not find ts-proto executable. Checked locations: ${TS_PROTO_EXECUTABLE_LOCATIONS.join(
", " ", ",
)}` )}`,
); );
} }
+10 -5
View File
@@ -5,6 +5,7 @@ import {
Video, Video,
youtubers, youtubers,
} from "@blitzkit/core"; } from "@blitzkit/core";
import locales from "@blitzkit/i18n/locales.json";
import { google } from "googleapis"; import { google } from "googleapis";
import { cloneDeep, uniqBy } from "lodash-es"; import { cloneDeep, uniqBy } from "lodash-es";
import { AssetUploader } from "./core/github/assetUploader"; import { AssetUploader } from "./core/github/assetUploader";
@@ -42,9 +43,11 @@ for (const tank of tanksSanitized) {
} }
try { try {
const q = `World of Tanks Blitz ${tank.name!.locales[locales.default]}`;
const results = await youtube.search.list({ const results = await youtube.search.list({
part: ["snippet"], part: ["snippet"],
q: `World of Tanks Blitz ${tank.name}`, q,
}); });
if (!results.data.items || results.data.items.length === 0) continue; if (!results.data.items || results.data.items.length === 0) continue;
@@ -54,9 +57,9 @@ for (const tank of tanksSanitized) {
(item) => (item) =>
item.snippet?.channelId && item.snippet?.channelId &&
item.id?.videoId && item.id?.videoId &&
youtubers.some(({ id }) => id === item.snippet?.channelId) youtubers.some(({ id }) => id === item.snippet?.channelId),
), ),
(item) => item.snippet!.channelId (item) => item.snippet!.channelId,
); );
reviews.reviews[tank.id] = { reviews.reviews[tank.id] = {
@@ -66,11 +69,13 @@ for (const tank of tanksSanitized) {
({ ({
id: item.id!.videoId!, id: item.id!.videoId!,
author: item.snippet!.channelId!, author: item.snippet!.channelId!,
} satisfies Video) }) satisfies Video,
), ),
}; };
console.log(`${++done} done; found ${items.length} for ${tank.name.locales.en}`); console.log(
`${++done} done; found ${items.length} for ${tank.name!.locales.en}`,
);
await new Promise((resolve) => setTimeout(resolve, 1000 / 5)); await new Promise((resolve) => setTimeout(resolve, 1000 / 5));
} catch (error) { } catch (error) {
+619
View File
@@ -0,0 +1,619 @@
name;maxHealthFactor;piercingPowerFactor;moduleHealthFactor;damageFactor;armorFactor
china:Ch01_Type59;1.3;1.33;1.5;1.29;1.35
china:Ch02_Type62;1.5;1.6;1.5;1.27;1.61
china:Ch03_WZ-111;1.4;1.17;1.4;1.3;1.35
china:Ch04_T34_1;1.5;1.54;1.4;1.3;1.61
china:Ch05_T34_2;1.3;1.33;1.5;1.29;1.34
china:Ch07_Vickers_MkE_Type_BT26;5.5;5.07;3.3;7.05;8.8
china:Ch08_Type97_Chi_Ha;3.7;3.71;2.2;3.13;5.56
china:Ch09_M5;2.9;2.91;1.9;2.54;3.47
china:Ch10_IS2;1.6;1.44;1.4;1.33;1.61
china:Ch11_110;1.4;1.17;1.4;1.29;1.35
china:Ch12_111_1_2_3;1.2;1.02;1.4;1.17;1.14
china:Ch14_T34_3;1.3;1.37;1.5;1.13;1.17
china:Ch15_59_16;1.9;1.77;1.6;1.57;2.24
china:Ch15_59_16_BP;1.9;1.7;1.6;1.56;2.24
china:Ch16_WZ_131;1.6;1.52;1.5;1.28;1.61
china:Ch17_WZ131_1_WZ132;1.4;1.22;1.5;1.29;1.34
china:Ch18_WZ-120;1.1;1.09;1.4;1.11;1.17
china:Ch20_Type58;1.9;1.88;1.5;1.57;1.86
china:Ch21_T34;2.7;2.1;1.7;1.97;2.12
china:Ch23_112;1.4;1.17;1.4;1.31;1.35
china:Ch24_Type64;1.9;1.86;1.6;1.57;2.35
china:Ch26_59_Patton;1.3;1.33;1.5;1.1;1.16
china:Ch28_WZ_132A;1.1;1.04;1.5;1.11;1.24
china:Ch34_WZ-131G_FT;2.1;1.57;1.4;1.51;1.73
china:Ch34_WZ-131G_FT_BP;2.1;1.57;1.4;1.51;2.02
china:Ch35_T-34-2G_FT;1.8;1.48;1.4;1.25;1.61
china:Ch36_WZ-111-1G_FT;1.6;1.16;1.4;1.25;1.35
china:Ch37_WZ-111G_FT;1.2;1.05;1.4;1.15;1.13
china:Ch39_WZ120_1G_FT;1.6;1.16;1.4;1.24;1.35
china:Ch40_WZ120G_FT;1.1;1.05;1.4;1.08;1.13
china:Ch42_M41D;1.7;1.49;1.5;1.24;1.61
china:Ch43_WZ-122_2;1.3;1.21;1.5;1.31;1.35
china:Ch45_WZ_114;1.2;1.02;1.4;1.19;1.13
china:Ch47_BZ_176;1.2;1.03;1.4;1.17;1.13
china:Ch49_BZ_68;1.2;1.02;1.4;1.2;1.16
china:Ch50_BZ_166;1.4;1.17;1.4;1.3;1.35
china:Ch51_BZ_58;1.6;1.46;1.4;1.29;1.62
china:Ch56_BZ_74_1;1.2;1.02;1.4;1.16;1.14
china:Ch59_BZ58_2;1.2;1.02;1.4;1.19;1.13
china:Ch60_Type59_spc;1.3;1.37;1.5;1.32;1.35
china:Ch_LT_vz38;4.3;4.29;2.7;3.99;4.88
china:Ch_WZ-112v2;1.4;1.17;1.4;1.31;1.35
china:Type_58BP;1.9;1.88;1.5;1.57;2.04
european:Cz02_T50;1.1;1.03;1.4;1.12;1.19
european:Cz05_T34-100;1.5;1.45;1.4;1.29;1.61
european:Cz07_TVP_46;1.3;1.12;1.5;1.29;1.35
european:Cz08_T-25;1.9;1.83;1.5;1.56;2.02
european:Cz08_T-25BP;1.9;1.83;1.5;1.55;2.02
european:Cz08_T-25BPS;1.9;1.83;1.5;1.57;2.02
european:Cz13_T-27;1.1;1.07;1.4;1.12;1.19
european:Cz14_Skoda_T-56;1.4;1.17;1.4;1.33;1.35
european:Cz15_Skoda_T-45;1.7;1.44;1.4;1.27;1.61
european:Cz16_TNH_T_vz51;1.2;1.02;1.4;1.17;1.15
european:Cz18_TNH_105_1000;1.4;1.16;1.4;1.28;1.35
european:Cz19_Vz_44_1;1.7;1.43;1.4;1.3;1.61
european:Cz20_ShPTK_TVP_100;1.5;1.16;1.4;1.24;1.34
european:Cz29_Skoda_P-JS;2.2;1.67;1.4;1.56;1.78
european:Cz29_Skoda_P-JS_BP;2.2;1.67;1.4;1.57;1.79
european:F01_Vickers_MkF;5.5;5.07;3.3;6.16;8.8
european:It09_P43_ter;1.5;1.54;1.4;1.3;1.61
european:It10_P43_BD;1.9;1.74;1.5;1.57;1.82
european:It10_P43_bis;1.9;1.74;1.5;1.57;1.82
european:It10_P43_bis_BP;1.9;1.7;1.5;1.58;1.82
european:It116_Carro_d_assalto_P88;1.6;1.33;1.4;1.28;1.61
european:It117_ProgettoCC55mod54;1.3;1.13;1.4;1.29;1.35
european:It119_ProgettoC50mod66;1.1;1.02;1.4;1.18;1.13
european:It12_Prototipo_Standard_B;1.1;1.04;1.4;1.14;1.17
european:It13_Progetto_M35_mod_46;1.3;1.33;1.5;1.27;1.35
european:It14_P44_Pantera;1.3;1.21;1.5;1.26;1.35
european:It15_Semovente_M41;3.2;1.98;1.5;1.97;2.13
european:It16_Bassoto;2;1.59;1.4;1.53;1.8
european:It17_Semovente_mod_56;1.7;1.4;1.4;1.28;1.62
european:It18_Semovente_mod_64;1.4;1.2;1.4;1.24;1.35
european:It19_Controcarro_1MK2_Ciclope;1.1;1.05;1.4;1.19;1.13
european:It22_Progetto_C45_mod_71;1.3;1.14;1.4;1.23;1.35
european:It30_CC_mod_64_Prem;1.4;1.17;1.4;1.25;1.35
european:Pl03_PzV_Pudel;1.9;1.57;1.5;1.56;1.92
european:Pl05_50TP_Tyszkiewicza;1.2;1.02;1.4;1.18;1.13
european:Pl06_10TP;4.3;4.29;2.7;4.28;7.66
european:Pl07_14TP;3.6;3.57;2.1;3.12;5.56
european:Pl08_50TP_prot;1.2;1.02;1.4;1.17;1.14
european:Pl10_40TP_Habicha;2.1;1.64;1.4;1.57;1.83
european:Pl119_CS_52_LIS;1.5;1.56;1.4;1.31;1.61
european:Pl11_45TP_Habicha;1.6;1.44;1.4;1.3;1.61
european:Pl13_53TP_Markowskiego;1.4;1.14;1.4;1.32;1.35
european:Pl17_DS_PZlnz;2.6;2.28;1.7;1.95;2.12
european:Pl18_BUGI;1.9;1.57;1.5;1.58;1.94
european:Pl20_CS_44;1.5;1.54;1.4;1.3;1.61
european:Pl22_CS_59;1.1;1.04;1.4;1.1;1.21
european:Pl23_CS_53;1.3;1.31;1.5;1.29;1.35
european:Pl24_56TP;1.4;1.15;1.4;1.29;1.35
european:Pl27_SDP_Wz_66T;1.5;1.17;1.4;1.27;1.35
european:Pl28_SDP_Wz_60;1.1;1.05;1.4;1.11;1.12
european:Pl30_SDP_Wz58T;1.4;1.16;1.4;1.26;1.35
european:Pl31_SDP_Wz_57D;1.7;1.43;1.4;1.28;1.62
european:Pl32_SDP_Wz_44;2;1.57;1.4;1.52;1.76
european:Pl33_SDP_Wz_40;3.2;2.13;1.5;1.96;2.13
european:S02_StrvM42;2.7;2.22;1.7;1.95;2.11
european:S04_Lago-I;3.1;2.69;1.9;2.54;2.32
european:S07_Strv74BP;1.9;1.67;1.5;1.55;1.9
european:S07_Strv_74;1.9;1.67;1.5;1.55;1.9
european:S13_Leo;1.5;1.58;1.4;1.3;1.61
european:S13_Leo_NY;1.5;1.58;1.4;1.3;1.61
european:S17_EMIL_1952E2;1.2;1.02;1.4;1.16;1.12
european:S18_EMIL_1951E1;1.3;1.16;1.4;1.29;1.35
european:S23_Strv_81;1.3;1.14;1.5;1.26;1.35
european:S25_EMIL_51;1.3;1.12;1.4;1.29;1.35
european:S26_Lancen_C;1.3;1.22;1.5;1.31;1.35
european:S32_Bofors_Tornvagn;1.1;1.02;1.4;1.17;1.12
france:AMX38;4.4;4.29;2.7;4.15;5.95
france:AMX40;3;2.88;1.9;2.52;3.53
france:AMX50_Foch;1.2;1.05;1.4;1.11;1.14
france:AMX_12t;1.9;1.64;1.6;1.6;2.02
france:AMX_13_75;1.7;1.61;1.5;1.25;1.61
france:AMX_13_90;1.4;1.37;1.5;1.29;1.34
france:AMX_50_100;1.4;1.12;1.4;1.26;1.35
france:AMX_50_120;1.2;1.02;1.4;1.12;1.17
france:AMX_AC_Mle1946;1.7;1.45;1.4;1.24;1.61
france:AMX_AC_Mle1948;1.5;1.16;1.4;1.24;1.35
france:AMX_M4_1945;1.6;1.47;1.4;1.28;1.61
france:ARL_44;2.1;1.57;1.4;1.53;1.82
france:ARL_44BP;2.1;1.53;1.4;1.58;1.81
france:ARL_V39;2;1.59;1.4;1.52;1.82
france:Achilles_FR;2.1;1.6;1.4;1.52;1.83
france:B1;4.1;2.8;1.6;2.54;3.64
france:BDR_G1B;3.2;1.91;1.5;1.95;2.13
france:Bat-Chat-25CL;1.1;1.05;1.5;1.1;1.22
france:D1;4.3;4.62;2.7;3.8;6.52
france:D2;3.7;3.64;2.2;3;5.52
france:ELC_AMX;2.8;1.97;2.3;2.31;2.09
france:F113_PzV_FR;1.9;1.65;1.5;1.56;1.89
france:F114_Projet_4_1;1.1;1.03;1.4;1.09;1.22
france:F115_Lorraine_50t;1.2;1.02;1.4;1.13;1.16
france:F116_Bat_Chat_Bourrasque;1.3;1.28;1.5;1.31;1.34
france:F117_Alt_Proto_AMX_30;1.3;1.15;1.5;1.29;1.35
france:F11_Renault_G1R;3.2;2;1.5;1.96;2.12
france:F120_Project_Louis;1.1;1.04;1.4;1.12;1.14
france:F123_ERAC_105_Proto;1.3;1.22;1.5;1.3;1.35
france:F126_AMX_13_M24;1.5;1.58;1.4;1.32;1.62
france:F127_ELC_AMX_901_Proto;1.4;1.28;1.5;1.28;1.34
france:F140_Sentinelle;1.4;1.15;1.4;1.28;1.35
france:F19_Lorraine40t;1.3;1.17;1.5;1.3;1.34
france:F19_Lorraine40t_Com;1.3;1.17;1.5;1.29;1.34
france:F34_ARL_V39_BP;2.1;1.59;1.4;1.51;1.82
france:F68_AMX_Chasseur_de_char_46;1.3;1.17;1.5;1.25;1.33
france:F69_AMX_13D;1.4;1.16;1.5;1.28;1.34
france:F69_AMX_13_57GF;1.6;1.49;1.5;1.26;1.61
france:F71_AMX_30_prototype;1.1;1.06;1.4;1.12;1.19
france:F73_M4A1_Revalorise;1.3;1.23;1.5;1.27;1.34
france:F74_AMX_M4_1949;1.4;1.12;1.4;1.28;1.35
france:F75_Char_de_25t;1.1;1.02;1.5;1.11;1.22
france:F84_Somua_SM;1.4;1.12;1.4;1.14;1.14
france:F89_Canon_dassaut_105;1.6;1.15;1.4;1.26;1.35
france:FCM_36Pak40;4.2;2.95;1.9;3.18;5.52
france:FCM_50t;1.3;1.17;1.5;1.25;1.35
france:F_AMX-12BP;1.9;1.64;1.6;1.6;2.02
france:RenaultFT_AC;5.5;4.37;2.2;4.49;8.51
france:RenaultR35;5.5;5.28;3.3;6.76;7.8
france:RenaultUE57;4.2;2.79;1.9;2.68;5.95
france:S_35CA;3.2;2.07;1.5;1.96;2.11
france:Somua_Sau_40;3.7;2.75;1.8;2.54;3.62
germany:B-1bis_captured;4.1;2.96;1.6;2.55;3.89
germany:DW_II;3.7;2.52;1.6;2.52;3.53
germany:DickerMax;2.1;1.57;1.4;1.53;2.02
germany:E-25;1.8;1.46;1.4;1.24;1.61
germany:E-50;1.1;1.09;1.4;1.1;1.15
germany:E-75;1.2;1.02;1.4;1.17;1.14
germany:Ferdinand;1.4;1.16;1.4;1.25;1.35
germany:G100_Gtraktor_Krupp;4.9;3.31;1.9;3.49;5.75
germany:G103_RU_251;1.4;1.3;1.5;1.25;1.33
germany:G106_PzKpfwPanther_AusfF;1.3;1.21;1.5;1.26;1.35
germany:G109_Steyr_WT;1.8;1.37;1.4;1.27;1.6
germany:G110_Mauschen;1.2;1.02;1.4;1.18;1.12
germany:G112_JagdPanzer_105;1.2;1.05;1.4;1.04;1.18
germany:G113_SP_I_C;1.7;1.49;1.5;1.28;1.58
germany:G114_Rheinmetall_Scorpion;1.6;1.16;1.4;1.24;1.33
germany:G115_VK10001P;1.4;1.15;1.4;1.33;1.35
germany:G118_VK4503;1.7;1.3;1.4;1.27;1.61
germany:G119_Panzer58_SH;1.3;1.22;1.5;1.28;1.35
germany:G119_Pz58_Mutz;1.3;1.17;1.5;1.26;1.35
germany:G120_M41_90;1.4;1.29;1.5;1.26;1.34
germany:G126_HWK_12;1.1;1.05;1.5;1.08;1.29
germany:G136_Tiger_131;2.1;1.68;1.4;1.55;1.85
germany:G138_VK16801P;1.5;1.15;1.4;1.16;1.13
germany:G140_HWK_30;1.4;1.27;1.5;1.27;1.34
germany:G142_M48RPz;1.3;1.18;1.5;1.27;1.35
germany:G147_mPanzer_64;1.7;1.49;1.5;1.29;1.61
germany:G148_Elephant;1.4;1.16;1.4;1.21;1.35
germany:G154_Kpz_07_RH;1.3;1.15;1.5;1.26;1.35
germany:G162_Project_Kpz_07P_E;1.1;1.02;1.4;1.16;1.14
germany:G164_Kpz_Pr_68_P;1.3;1.22;1.5;1.3;1.34
germany:G165_Erich_Koncept;1.1;1.06;1.4;1.14;1.19
germany:G166_LKpz_70_K;1.1;1.02;1.5;1.11;1.32
germany:G167_Tiger_Maus;1.2;1.03;1.4;1.11;1.13
germany:G168_KJpz_T_III;1.2;1.05;1.4;1.05;1.29
germany:G190_VK_1602_Quby;1.4;1.36;1.5;1.25;1.34
germany:G20_Marder_II;4.2;3.01;1.9;3.18;5.54
germany:G85_Auf_Panther;1.5;1.49;1.5;1.25;1.61
germany:G_E-75RT;1.4;1.14;1.4;1.3;1.35
germany:G_E-75TS;1.2;1.03;1.4;1.13;1.13
germany:G_VK3502_BP;2.1;1.68;1.4;1.57;1.79
germany:H39_captured;4.3;5.58;2.7;3.99;6.04
germany:Hetzer;3.7;2.6;1.8;2.54;3.47
germany:Indien_Panzer;1.3;1.17;1.5;1.25;1.35
germany:JagdPanther;1.8;1.37;1.4;1.25;1.62
germany:JagdPantherII;1.6;1.16;1.4;1.23;1.35
germany:JagdPzIV;2.1;1.58;1.4;1.49;1.8
germany:JagdPzIV_AusfBP;2;1.58;1.4;1.49;1.8
germany:JagdTiger;1.1;1.05;1.4;1.04;1.12
germany:JagdTiger_SdKfz_185;1.4;1.17;1.4;1.21;1.35
germany:JagdTiger_SdKfz_185_Snowstorm;1.4;1.33;1.4;1.21;1.35
germany:Krupp-38D;2.6;2.22;1.7;1.95;2.12
germany:Lowe;1.4;1.11;1.4;1.29;1.35
germany:Nashorn;2;1.51;1.4;1.51;2.24
germany:Nashorn_BP;2;1.51;1.4;1.5;2.24
germany:Panther_II;1.3;1.21;1.5;1.26;1.35
germany:Panther_M10;1.5;1.54;1.4;1.26;1.61
germany:PanzerJager_I;5.5;4.33;2.2;4.49;8.8
germany:Pro_Ag_A;1.1;1.02;1.4;1.1;1.21
germany:Pz35t;4.4;4.9;2.7;3.94;6.05
germany:Pz38_NA;2.9;2.78;1.9;2.55;3.65
germany:Pz38t;3.6;3.88;2.1;3.05;5.58
germany:PzII;5.5;5.28;3.3;6.67;6.81
germany:PzIII;3.7;3.69;2.2;3.26;5.49
germany:PzIII_A;3.7;3.09;2.1;2.98;5.58
germany:PzIII_IV;2.7;2.22;1.7;1.95;2.12
germany:PzII_J;3.6;5.5;2.1;2.83;5.46
germany:PzII_Luchs;2.9;2.94;1.9;2.55;2.2
germany:PzIV;2.7;2.22;1.7;1.95;2.13
germany:PzIV_Hydro;2.5;2.22;1.7;1.95;2.11
germany:PzIV_schmalturm;1.9;1.65;1.5;1.53;1.87
germany:PzV;1.5;1.33;1.4;1.26;1.61
germany:PzVI;1.7;1.3;1.4;1.28;1.62
germany:PzVIB_Tiger_II;1.4;1.14;1.4;1.27;1.35
germany:PzVI_AusfBP;2.1;1.68;1.4;1.56;1.79
germany:PzVI_Tiger_P;1.7;1.3;1.4;1.28;1.62
germany:PzV_PzIV;2.6;2.22;1.7;1.95;2.12
germany:Pz_II_AusfG;3.8;2.9;2.1;2.85;5.55
germany:Pz_IV_AusfA;3.6;3.2;2.2;4.05;5.74
germany:Pz_IV_AusfBT;2.7;2.53;1.7;1.96;2.12
germany:Pz_IV_AusfD;3.1;2.43;1.9;2.54;3.09
germany:Pz_Sfl_IVc;3;1.61;1.5;1.98;2.08
germany:RhB_Waffentrager;1.6;1.26;1.4;1.28;1.33
germany:S01_Frankentank;1.7;1.33;1.4;1.37;1.62
germany:S35_captured;3.8;2.85;2.1;2.93;5.52
germany:StuGIII;3.2;1.93;1.5;1.97;2.12
germany:Sturer_Emil;1.8;1.31;1.4;1.28;1.61
germany:T-15;3.8;4.72;2.1;2.82;5.55
germany:T-25;2.5;2.13;1.7;1.95;2.11
germany:VK1602;2.8;2.9;2.3;1.96;2.11
germany:VK2801;1.9;1.53;1.6;1.58;1.95
germany:VK2801_BP;1.9;1.53;1.6;1.59;1.95
germany:VK3001H;3.2;1.75;1.5;1.96;2.13
germany:VK3001P;1.9;1.61;1.5;1.57;1.81
germany:VK3002DB;1.5;1.58;1.4;1.28;1.61
germany:VK3002DB_BP;1.9;1.65;1.5;1.56;1.87
germany:VK3002DB_V1;1.9;1.65;1.5;1.56;1.84
germany:VK3002M;1.9;1.65;1.5;1.57;1.91
germany:VK3601H;2.1;1.64;1.4;1.57;1.78
germany:VK4502A;1.3;1.17;1.4;1.29;1.35
germany:VK4502P;1.2;1.02;1.4;1.18;1.13
germany:Waffentrager_IV;1.2;1.06;1.4;1.12;1.19
germany:Waffentrager_IV_CL;1.2;1.05;1.4;1.06;1.19
japan:Chi_Ha;3.6;3.8;2.2;3.2;5.54
japan:Chi_He;3;2.72;1.9;2.55;3.24
japan:Chi_Ni;4.4;4.62;2.5;4.09;6.52
japan:Chi_Nu;2.6;2.02;1.7;1.94;2.12
japan:Chi_Nu_Kai;2.6;2.02;1.7;1.95;2.11
japan:Chi_Ri;1.5;1.58;1.4;1.25;1.61
japan:Chi_To;1.9;1.61;1.5;1.55;1.9
japan:Firefly_GuP;1.9;1.49;1.5;1.55;2.01
japan:G09_Hetzer_GuP;3.7;2.6;1.8;2.54;3.47
japan:Ha_Go;5.7;5.36;3.3;5.99;8.2
japan:IS-2_GuP;1.6;1.44;1.4;1.33;1.61
japan:J21_Ho_Ri;1.2;1.05;1.4;1.11;1.13
japan:J22_Ho_Ri_type96;1.5;1.16;1.4;1.25;1.35
japan:J23_Chi_To_SPG;1.8;1.37;1.4;1.25;1.62
japan:J24_Type_57;1.4;1.16;1.4;1.33;1.35
japan:J26_Type_68;1.2;1.02;1.4;1.13;1.13
japan:J27_Chi_Se;1.4;1.15;1.4;1.27;1.35
japan:J28_Ju_To;1.7;1.41;1.4;1.32;1.62
japan:J29_Ju_Nu;2.1;1.73;1.4;1.57;1.8
japan:J29_Ju_Nu_BP;2.1;1.73;1.4;1.57;1.8
japan:J30_Mitsu_108;3.2;2.37;1.5;1.97;2.13
japan:J31_Type_5_Ka_Ri;1.5;1.16;1.4;1.26;1.35
japan:J36_Type_63_HT;1.4;1.15;1.4;1.29;1.35
japan:J38_Type_95_Ji_Ro;1.5;1.16;1.4;1.22;1.35
japan:Ke_Ho;2.9;2.71;1.9;2.55;1
japan:Ke_Ni;3.8;4.07;2.1;2.67;5.71
japan:Ke_Ni_B;4.5;4.85;2.1;2.14;5.71
japan:PzVI_GuP;2.1;1.64;1.4;1.54;1.81
japan:Pz_IV_AusfH;2.6;2.22;1.7;1.95;2.12
japan:STA_1;1.3;1.15;1.5;1.28;1.34
japan:Type_61;1.1;1.02;1.4;1.1;1.24
other:AMX_dracula;1.5;1.58;1.4;1.3;1.61
other:Akawara;1.3;1.28;1.5;1.28;1.35
other:Oth06_Sega_Lupus;1.7;1.44;1.4;1.34;1.62
other:Oth08_WH_Vindicator;1.8;1.74;1.4;1.24;1.61
other:Oth09_WH_Predator;1.7;1.47;1.4;1.25;1.62
other:Oth11_T-55A;1.1;1.1;1.4;1.09;1.16
other:Oth12_Sega_Hafen;1.6;1.66;1.4;1.27;1.61
other:Oth14_MdM_AshKnight;1.7;1.47;1.4;1.27;1.62
other:Oth15_RustCrab;2.6;2.22;1.7;1.96;2.12
other:Oth16_KV_TG;1.6;1.76;1.4;1.27;1.61
other:Oth17_BDT-5A;2.6;2.22;1.7;1.95;2.12
other:Oth18_BDT-5B;1.9;1.85;1.5;1.55;1.94
other:Oth19_BDT-5C;1.5;1.66;1.4;1.25;1.61
other:Oth20_M4FL10;1.5;1.67;1.4;1.27;1.61
other:Oth22_Sega_Vulcan;1.7;1.41;1.4;1.33;1.62
other:Oth23_Werewolf;1.6;1.44;1.4;1.33;1.61
other:Oth24_StuH_42HW;3.2;2.6;1.5;1.97;2.13
other:Oth25_HWT20_FalconFire;1.7;1.47;1.4;1.25;1.62
other:Oth26_StrvM42HW;2.7;2.22;1.7;1.96;2.11
other:Oth27_M24_AMX;1.9;1.64;1.6;1.6;2.02
other:Oth28_Sturmfeur_HW;1.7;1.41;1.4;1.31;1.61
other:Oth29_A15HW;2.8;1.83;2.3;1.96;2.12
other:Oth30_T54MS;1.3;1.2;1.5;1.3;1.35
other:Oth31_T150BP;2.1;1.71;1.4;1.56;1.79
other:Oth33_Blizzard_BP;2;1.68;1.6;1.56;2.21
other:Oth34_PanzerMummy;1.7;1.39;1.4;1.3;1.61
other:Oth35_PanzerMejai;3.2;2.27;1.5;1.95;2.13
other:Oth36_Indien_Panzer_S1;1.5;1.56;1.4;1.28;1.61
other:Oth37_T26_E4_SuperPershing_S1;1.3;1.25;1.5;1.3;1.35
other:Oth38_50TP_Tyszkiewicza_S1;1.1;1.02;1.4;1.17;1.14
other:Oth39_T20_S2;1.5;1.62;1.4;1.28;1.61
other:Oth40_TVP_46_S2;1.3;1.15;1.5;1.28;1.35
other:Oth41_Centurion_Mk3_S2;1.1;1.05;1.4;1.11;1.18
other:Oth42_ForestWitch_HW23;1.7;1.42;1.4;1.32;1.61
other:Oth44_Charioteer_T;1.6;1.17;1.4;1.27;1.35
other:Oth45_Sandstriker;1.4;1.16;1.4;1.33;1.35
other:Oth46_Charioteer_S3;1.8;1.37;1.4;1.27;1.61
other:Oth47_AMX_13_90_S3;1.4;1.3;1.5;1.28;1.34
other:Oth48_EMIL_1951E1_S3;1.1;1.02;1.4;1.15;1.14
other:Oth49_TM_04_NT;1.5;1.63;1.4;1.34;1.61
other:Oth50_STT;1.6;1.55;1.5;1.28;1.61
other:Oth51_FD_24;1.3;1.18;1.5;1.28;1.35
other:Oth52_Toxique;1.3;1.32;1.5;1.31;1.35
other:Oth53_Rammer;1.6;1.46;1.4;1.31;1.61
other:Oth61_Pz_VM_Set;4.1;2.58;1.6;2.54;3.71
other:Oth62_Pz_VlD_Set;3.2;1.97;1.5;1.96;2.13
other:Oth63_Pz_VllF_Set;2.1;1.64;1.4;1.57;1.84
other:Oth64_Pz_VlllK_Set;1.6;1.44;1.4;1.31;1.61
other:Oth65_PzVIllM_Tigeom;2.1;1.67;1.4;1.59;1.86
other:Oth66_E100ANN_S4;1.7;1.47;1.4;1.33;1.62
other:Oth67_FV215b_183ANN_S4;1.5;1.16;1.4;1.24;1.35
other:Oth68_KV2ANN_S4;1.2;1.03;1.4;1.23;1.14
other:Oth69_Souleater;1.5;1.17;1.4;1.26;1.35
other:Oth70_Lightbringer;1.6;1.47;1.4;1.29;1.61
other:Oth71_Titan_Strife;1.3;1.16;1.4;1.28;1.35
other:Oth72_Hazard_I;1.5;1.57;1.4;1.3;1.61
other:Oth73_Nemini;1.4;1.17;1.4;1.31;1.35
other:Oth74_Argonaut;1.1;1.05;1.4;1.06;1.18
other:Oth75_Braniaq_DM5;1.1;1.06;1.4;1.14;1.18
other:Oth76_Destiny;1.2;1.02;1.4;1.17;1.15
other:Oth77_Prowler;1.4;1.16;1.4;1.3;1.35
other:Oth78_AC_17_P_Sterope;1.2;1.02;1.4;1.13;1.12
other:Oth80_AC_16_Celeno;1.2;1.02;1.4;1.13;1.12
other:Oth81_AC_15_Taygete;1.4;1.24;1.4;1.33;1.35
other:Oth82_AC_14_Pleione;1.4;1.15;1.4;1.29;1.35
other:Oth83_AC_13_Merope;1.7;1.4;1.4;1.32;1.61
other:Oth84_FVE_Defender;1.1;1.12;1.4;1.16;1.15
other:Oth85_Ghoul;1.3;1.36;1.5;1.28;1.35
other:Oth86_Survivalist;1.1;1.06;1.4;1.1;1.12
other:Oth92_JagdPantherII_Titan;1.4;1.17;1.4;1.24;1.35
other:Oth93_Convoyer;1.4;1.16;1.4;1.24;1.35
other:Sega_Edelweiss;1.5;1.62;1.4;1.28;1.61
other:Sega_Nameless;1.7;1.41;1.4;1.29;1.62
other:Strv_74A2;1.9;1.8;1.5;1.54;1.9
other:Van_Helsing;1.8;1.59;1.4;1.25;1.61
other:WZ-135G_FT;1.8;1.5;1.4;1.3;1.61
uk:GB03_Cruiser_Mk_I;4.4;4.9;2.7;3.96;9.12
uk:GB04_Valentine;2.9;1.85;1.9;2.53;3.37
uk:GB05_Vickers_Medium_Mk_II;6.5;3.84;2.2;3.8;12.37
uk:GB06_Vickers_Medium_Mk_III;4.9;3.9;1.9;2.88;5.74
uk:GB07_Matilda;4.1;2.83;1.6;2.56;3.64
uk:GB08_Churchill_I;3.3;1.8;1.5;1.96;2.13
uk:GB09_Churchill_VII;2.1;1.7;1.4;1.54;1.76
uk:GB09_Churchill_VII_BP;2.1;1.67;1.4;1.54;1.79
uk:GB101_FV1066_Senlac;1.4;1.16;1.5;1.24;1.33
uk:GB106_Cobra;1.1;1.05;1.4;1.14;1.16
uk:GB108_A46;1.5;1.56;1.5;1.25;1.6
uk:GB109_GSOR_1008;1.6;1.16;1.4;1.25;1.34
uk:GB109_GSOR_1008_Fearless;1.6;1.18;1.4;1.24;1.34
uk:GB10_Black_Prince;1.7;1.47;1.4;1.23;1.62
uk:GB110_FV4201_Chieftain_Prototype;1.1;1.02;1.4;1.13;1.15
uk:GB111_Charlemagne;1.4;1.13;1.4;1.31;1.35
uk:GB112_Caliban;1.4;1.16;1.4;1.45;1.35
uk:GB114_Vickers_MBT_Mk3;1.3;1.28;1.5;1.12;1.14
uk:GB116_Harry_Hopkins;2.5;1.97;2.3;1.96;2.12
uk:GB11_Caernarvon;1.3;1.13;1.4;1.23;1.35
uk:GB11_Caernarvon_D;1.4;1.16;1.4;1.3;1.35
uk:GB128_Nemesis;1.3;1.29;1.5;1.28;1.35
uk:GB12_Conqueror;1.1;1.02;1.4;1.1;1.13
uk:GB19_Sherman_Firefly;1.9;1.49;1.5;1.55;2.01
uk:GB20_Crusader;2.7;1.83;2.3;1.96;2.11
uk:GB21_A27BP;1.9;1.7;1.5;1.55;1.94
uk:GB21_Cromwell;1.9;1.7;1.5;1.55;1.94
uk:GB22_Comet;1.5;1.58;1.4;1.25;1.61
uk:GB23_Centurion;1.3;1.12;1.5;1.25;1.35
uk:GB24_Centurion_Mk3;1.1;1.02;1.4;1.14;1.15
uk:GB32_Tortoise;1.1;1.05;1.4;1.05;1.11
uk:GB35_Sentinel_AC_IV;1.9;1.7;1.5;1.56;1.94
uk:GB39_Universal_CarrierQF2;5.5;4.89;2.2;3.99;11.07
uk:GB40_Gun_Carrier_BP;2;1.56;1.4;1.52;1.77
uk:GB40_Gun_Carrier_Churchill;2.1;1.48;1.4;1.51;1.79
uk:GB41_Challenger;1.8;1.35;1.4;1.26;1.61
uk:GB42_Valentine_AT;4.3;2.68;1.9;2.43;5.48
uk:GB44_Archer_Custom;3.2;1.76;1.5;1.98;2.08
uk:GB51_Excelsior;3.2;2.27;1.5;1.97;2.13
uk:GB52_FV201_A45;1.7;1.47;1.4;1.24;1.61
uk:GB57_Alecto;3.7;2.93;1.8;2.54;1
uk:GB58_Cruiser_Mk_III;4.4;4.9;2.5;4.26;6.52
uk:GB59_Cruiser_Mk_IV;3.7;3.82;2.2;3.05;5.52
uk:GB60_Covenanter;3;3;1.9;2.55;2.68
uk:GB63_TOG_II;1.9;1.53;1.4;1.54;1.8
uk:GB63_TOG_II_BP;1.9;1.53;1.4;1.57;1.79
uk:GB68_Matilda_Black_Prince;2.5;1.96;1.7;1.98;2.12
uk:GB69_Cruiser_Mk_II;5.9;5.88;3.3;5.32;8.2
uk:GB71_AT_15A;1.7;1.58;1.4;1.22;1.62
uk:GB72_AT15;1.4;1.19;1.4;1.19;1.35
uk:GB73_AT2;3.2;2.45;1.5;1.97;2.13
uk:GB74_AT8;2;1.6;1.4;1.51;1.76
uk:GB75_AT7;1.7;1.33;1.4;1.23;1.62
uk:GB76_Mk_VIC;4.3;5.58;2.7;4.01;9.12
uk:GB80_Charioteer;1.6;1.16;1.4;1.27;1.34
uk:GB81_FV4004;1.2;1.05;1.4;1.11;1.2
uk:GB85_Cromwell_Berlin;1.9;1.7;1.5;1.55;1.94
uk:GB87_Chieftain_T95_turret;1.4;1.17;1.4;1.25;1.35
uk:GB93_Caernarvon_AX;1.3;1.13;1.4;1.25;1.35
uk:GB94_Centurion_Mk5-1_RAAC;1.3;1.12;1.5;1.28;1.35
uk:GB97_Chimera;1.3;1.15;1.5;1.32;1.35
uk:GB99_Turtle_Mk1;1.4;1.18;1.4;1.22;1.35
uk:GB_Centurion_MkDF;1.3;1.12;1.5;1.29;1.35
uk:GB_Churchill_MkVI;2.1;1.73;1.4;1.54;1.77
uk:GB_Churchill_MkVIII;2.1;1.64;1.4;1.56;1.79
uk:GB_FV301;1.4;1.07;1.5;1.25;1.35
uk:GB_Sherman_V;2.6;2.28;1.7;1.96;2.11
uk:GB_Vickers_Cruiser;1.1;1;1.5;1.11;1.2
uk:Sentinel_AC1;3;2.88;1.9;2.55;3.47
usa:A05_M4_105BD;2.7;2.4;1.7;1.96;2.12
usa:A100_T49;1.4;1.53;1.5;1.29;1.34
usa:A102_T28_concept;1.7;1.47;1.4;1.27;1.62
usa:A104_M4A3E8A;1.9;1.88;1.5;1.54;1.84
usa:A111_T25_Pilot;1.3;1.27;1.5;1.26;1.35
usa:A115_Chrysler_K;1.4;1.16;1.4;1.3;1.35
usa:A117_T26E5;1.4;1.15;1.4;1.27;1.35
usa:A121_T26E3;1.5;1.54;1.4;1.31;1.61
usa:A122_TS-5;1.4;1.15;1.4;1.23;1.35
usa:A124_T54E2;1.4;1.15;1.4;1.29;1.35
usa:A125_AEP_1;1.1;1.02;1.4;1.15;1.14
usa:A130_Super_Hellcat;1.7;1.51;1.4;1.24;1.6
usa:A132_T77;1.3;1.15;1.4;1.29;1.35
usa:A136_T42;1.5;1.54;1.4;1.29;1.61
usa:A140_ASTRON_REX_105;1.3;1.31;1.5;1.31;1.35
usa:A141_M-4-Y;1.4;1.16;1.4;1.27;1.35
usa:A142_Pawlack_Tank;2.1;1.68;1.4;1.58;1.85
usa:A146_TL_7_120;1.1;1.02;1.4;1.14;1.14
usa:A150_MBT_B;1.2;1.02;1.4;1.21;1.13
usa:A151_TS_60;1.3;1.14;1.4;1.06;1.15
usa:A175_ATAC;1.1;1.02;1.5;1.11;1.32
usa:A68_T28D;1.4;1.2;1.4;1.28;1.35
usa:A77_T6M;3;2.72;1.9;2.52;3.24
usa:A94_T37;1.9;1.73;1.6;1.55;2.21
usa:A99_T92E1;1.1;1.1;1.5;1.2;1.2
usa:M-3-Y;1.4;1.14;1.4;1.28;1.35
usa:M-3-Y-Midas;1.4;1.14;1.4;1.28;1.35
usa:M-5-Y;1.1;1.02;1.4;1.2;1.14
usa:M-7-Y;1.7;1.32;1.4;1.32;1.61
usa:M103;1.1;1.02;1.4;1.14;1.13
usa:M10_Wolverine;3.2;2.18;1.5;1.96;2.13
usa:M18E2BP;1.9;1.65;1.5;1.57;2.02
usa:M18_Hellcat;2.1;1.58;1.4;1.51;2.99
usa:M22_Locust;3.8;4.23;2.1;3;5.59
usa:M24_Chaffee;1.9;1.73;1.6;1.55;2.36
usa:M24_Chaffee_BP;1.9;1.73;1.6;1.55;2.36
usa:M2_lt;5.5;5.28;3.3;6.76;7.39
usa:M2_med;3.6;4.29;2.2;2.93;5.54
usa:M36_BP;2.1;1.58;1.4;1.53;1.85
usa:M36_Slagger;2.1;1.58;1.4;1.54;1.85
usa:M3_Grant;3;2.38;1.9;2.55;3.13
usa:M3_Stuart;4.4;4.29;2.7;4.23;5.89
usa:M41_Bulldog;1.6;1.49;1.5;1.25;1.61
usa:M46_CL;1.1;1.04;1.4;1.08;1.17
usa:M46_Patton;1.1;1.08;1.4;1.09;1.17
usa:M4A2E4;2.6;2.28;1.7;1.96;2.13
usa:M4A3E8_BP;1.9;1.88;1.5;1.55;1.84
usa:M4A3E8_Sherman;1.9;1.88;1.5;1.55;1.84
usa:M4_Sherman;2.7;1.98;1.7;1.95;2.12
usa:M5_Stuart;3.7;3.48;2.1;2.94;5.57
usa:M6;2.1;1.6;1.4;1.58;1.8
usa:M6A2E1;1.6;1.32;1.4;1.33;1.62
usa:M6A2E1_exp;1.4;1.07;1.4;1.33;1.35
usa:M6E2BP;2.1;1.6;1.4;1.57;1.77
usa:M6E2V2_BP;2.1;1.64;1.4;1.53;1.78
usa:M7_med;3.1;2.38;1.9;2.54;2.98
usa:M8A1;4;2.73;1.8;2.54;2.87
usa:Pershing;1.3;1.33;1.5;1.25;1.35
usa:Ram-II;2.5;2.22;1.7;1.97;2.12
usa:Sherman_Jumbo;1.9;1.88;1.5;1.56;1.8
usa:T14;3.2;2.35;1.5;1.97;2.13
usa:T18;5.5;4.48;2.2;4.43;5.65
usa:T1_E6;4.4;5;2.7;4.12;8.54
usa:T1_hvy;3.3;2;1.5;1.96;2.13
usa:T20;1.5;1.54;1.4;1.28;1.61
usa:T21;1.9;1.81;1.6;1.57;2.27
usa:T21_BP;1.9;1.81;1.6;1.56;2.24
usa:T23E3;1.5;1.54;1.4;1.25;1.61
usa:T25_2;1.7;1.4;1.4;1.23;1.61
usa:T25_AT;1.7;1.44;1.4;1.26;1.61
usa:T26_E4_SuperPershing;1.3;1.2;1.5;1.29;1.35
usa:T28;1.4;1.16;1.4;1.23;1.35
usa:T28_Prototype;1.4;1.16;1.4;1.24;1.35
usa:T29;1.7;1.32;1.4;1.31;1.61
usa:T2_lt;4.3;5.45;2.7;3.9;8.26
usa:T2_med;4.6;5;2.5;4.23;7.11
usa:T30;1.1;1.05;1.4;1.12;1.17
usa:T32;1.4;1.16;1.4;1.29;1.35
usa:T34_hvy;1.3;1.07;1.4;1.3;1.35
usa:T34_hvy_Freedom;1.4;1.07;1.4;1.31;1.35
usa:T40;4;2.38;1.8;2.54;3.13
usa:T49;3.2;2.18;1.5;1.97;2.08
usa:T49_Fearless;1.4;1.53;1.5;1.28;1.34
usa:T54E1;1.1;1.06;1.4;1.1;1.16
usa:T69;1.3;1.33;1.5;1.27;1.35
usa:T71;1.6;1.49;1.5;1.27;1.6
usa:T7_Combat_Car;4.4;5.58;2.7;4.26;8.54
usa:T82;4.1;13.5;1.9;2.78;5.78
usa:T95;1.2;1.05;1.4;1.11;1.12
usa:T95E2;1.3;1.33;1.5;1.25;1.35
ussr:A-20;3.1;2.72;1.9;2.54;3
ussr:A-32;3;2.85;1.9;2.52;3.43
ussr:AT-1;5.5;3.99;2.2;3.99;8.51
ussr:BT-2;4.3;4.71;2.7;4.18;6.52
ussr:BT-7;3.6;2.98;2.1;3.06;5.51
ussr:BT-SV;3.8;2.86;2.1;3.16;5.58
ussr:Churchill_LL;3.2;2.27;1.5;1.97;2.13
ussr:GAZ-74b;4;2.48;1.8;2.53;3.05
ussr:IS;1.6;1.44;1.4;1.32;1.62
ussr:IS-2Sh;1.4;1.15;1.4;1.31;1.35
ussr:IS-3;1.4;1.16;1.4;1.3;1.35
ussr:IS8;1.2;1.02;1.4;1.12;1.15
ussr:ISU-152;1.6;1.13;1.4;1.38;1.35
ussr:K-91;1.1;1.02;1.4;1.1;1.12
ussr:KV-13;1.5;1.67;1.4;1.26;1.61
ussr:KV-1s;2.3;1.6;1.4;1.6;1.82
ussr:KV-1s-BP;2.3;1.5;1.4;1.64;1.83
ussr:KV-220;3.2;2.27;1.5;1.96;2.13
ussr:KV-3;1.7;1.44;1.4;1.33;1.62
ussr:KV-5;1.5;1.15;1.4;1.29;1.35
ussr:KV1;3.2;2.11;1.5;1.95;2.13
ussr:KV2;2.4;2.25;1.4;1.52;1.85
ussr:KV4;1.4;1.13;1.4;1.3;1.35
ussr:LTP;3.8;2.88;2.1;3.16;5.56
ussr:M3_Stuart_LL;3.7;5.04;2.1;2.86;5.51
ussr:MS-1obr21;4.3;4.71;2.7;4.18;6.98
ussr:MT25;2;1.79;1.6;1.56;2.06
ussr:Matilda_II_LL;2.5;2.22;1.7;1.96;2.12
ussr:Object252;1.4;1.14;1.4;1.33;1.35
ussr:Object252_Fearless;1.4;1.41;1.4;1.33;1.35
ussr:Object_704;1.1;1.05;1.4;1.1;1.13
ussr:R105_BT_7A;3.8;6.27;2.1;3.86;5.64
ussr:R107_LTB;1.6;1.56;1.5;1.28;1.61
ussr:R109_T54S;1.4;1.34;1.5;1.29;1.34
ussr:R111_ISU130;1.5;1.16;1.4;1.23;1.35
ussr:R112_T54_45;1.3;1.37;1.5;1.31;1.35
ussr:R113_Object_730;1.4;1.15;1.4;1.31;1.35
ussr:R115_IS-3_auto;1.4;1.15;1.4;1.3;1.35
ussr:R116_ISU122C_Berlin;1.8;1.45;1.4;1.22;1.62
ussr:R127_T44_100_P;1.3;1.33;1.5;1.27;1.35
ussr:R131_LTG;1.6;1.61;1.5;1.28;1.61
ussr:R134_Object_252U;1.4;1.14;1.4;1.33;1.35
ussr:R140_M4A2_Loza;1.9;1.88;1.5;1.55;1.84
ussr:R146_STG;1.3;1.33;1.5;1.33;1.35
ussr:R154_T-34E-43;2.6;2.22;1.7;1.96;2.11
ussr:R158_LT_432;1.4;1.34;1.5;1.28;1.34
ussr:R159_SU_130PM;1.6;1.16;1.4;1.26;1.34
ussr:R160_object_244;2.1;1.71;1.4;1.55;1.82
ussr:R172_Object_752;1.1;1.02;1.4;1.13;1.13
ussr:R175_IS_2E;1.6;1.47;1.4;1.31;1.61
ussr:R180_Object_274A;1.3;1.31;1.5;1.3;1.35
ussr:R181_Object_452K;1.2;1.02;1.4;1.15;1.13
ussr:R187_Object_590;1.3;1.32;1.5;1.29;1.35
ussr:R192_K_2;1.4;1.12;1.4;1.33;1.35
ussr:R194_ST_62_var_2;1.3;1.31;1.5;1.28;1.35
ussr:R217_Object_Kust;1.5;1.16;1.4;1.27;1.35
ussr:R43_T-70_57;3.8;3.41;2.1;3.43;5.49
ussr:R63_ST_IBD;1.2;1.02;1.4;1.17;1.14
ussr:R71_IS_2B;1.6;1.44;1.4;1.33;1.61
ussr:R98_T44_85;1.5;1.64;1.4;1.26;1.61
ussr:R_Object_84;1.1;1.07;1.5;1.12;1.17
ussr:ST_I;1.2;1.02;1.4;1.16;1.13
ussr:SU-100;2.1;1.57;1.4;1.55;1.81
ussr:SU-101;1.5;1.15;1.4;1.26;1.35
ussr:SU-152;1.8;2;1.4;1.38;1.61
ussr:SU-76;4.2;3.6;1.9;3.18;5.56
ussr:SU-85;3.2;1.99;1.5;1.97;2.12
ussr:SU100M1;1.7;1.35;1.4;1.25;1.61
ussr:SU100Y;2.1;1.53;1.4;1.57;1.89
ussr:SU122_44;1.8;1.45;1.4;1.25;1.61
ussr:SU122_54;1.2;1.05;1.4;1.03;1.14
ussr:SU76I;4.1;3.31;1.9;3.18;5.47
ussr:SU_85I;3.1;2.3;1.5;1.98;2.12
ussr:T-127;3.8;2.76;2.1;3.16;5.52
ussr:T-26;5.5;5.07;3.3;6.16;8.2
ussr:T-28;3;2.85;1.9;2.54;3.09
ussr:T-34;2.7;2.01;1.7;1.96;2.12
ussr:T-34-85;1.9;1.71;1.5;1.58;1.87
ussr:T-34-85_Rudy;1.5;1.67;1.4;1.26;1.61
ussr:T-34-85_Victory;1.9;1.88;1.5;1.57;1.87
ussr:T-43;1.5;1.6;1.4;1.25;1.61
ussr:T-44;1.3;1.33;1.5;1.34;1.35
ussr:T-46;3.8;4.32;2.1;2.83;5.6
ussr:T-54;1.1;1.1;1.4;1.09;1.16
ussr:T150;2.1;1.55;1.4;1.59;1.82
ussr:T34_85BP;1.9;1.88;1.5;1.57;1.87
ussr:T34_85TUR;1.5;1.58;1.4;1.29;1.62
ussr:Tetrarch_LL;4.3;3.88;2.7;3.88;8.51
ussr:Valentine_LL;2.9;4.55;1.9;2.55;3.42
1 name maxHealthFactor piercingPowerFactor moduleHealthFactor damageFactor armorFactor
2 china:Ch01_Type59 1.3 1.33 1.5 1.29 1.35
3 china:Ch02_Type62 1.5 1.6 1.5 1.27 1.61
4 china:Ch03_WZ-111 1.4 1.17 1.4 1.3 1.35
5 china:Ch04_T34_1 1.5 1.54 1.4 1.3 1.61
6 china:Ch05_T34_2 1.3 1.33 1.5 1.29 1.34
7 china:Ch07_Vickers_MkE_Type_BT26 5.5 5.07 3.3 7.05 8.8
8 china:Ch08_Type97_Chi_Ha 3.7 3.71 2.2 3.13 5.56
9 china:Ch09_M5 2.9 2.91 1.9 2.54 3.47
10 china:Ch10_IS2 1.6 1.44 1.4 1.33 1.61
11 china:Ch11_110 1.4 1.17 1.4 1.29 1.35
12 china:Ch12_111_1_2_3 1.2 1.02 1.4 1.17 1.14
13 china:Ch14_T34_3 1.3 1.37 1.5 1.13 1.17
14 china:Ch15_59_16 1.9 1.77 1.6 1.57 2.24
15 china:Ch15_59_16_BP 1.9 1.7 1.6 1.56 2.24
16 china:Ch16_WZ_131 1.6 1.52 1.5 1.28 1.61
17 china:Ch17_WZ131_1_WZ132 1.4 1.22 1.5 1.29 1.34
18 china:Ch18_WZ-120 1.1 1.09 1.4 1.11 1.17
19 china:Ch20_Type58 1.9 1.88 1.5 1.57 1.86
20 china:Ch21_T34 2.7 2.1 1.7 1.97 2.12
21 china:Ch23_112 1.4 1.17 1.4 1.31 1.35
22 china:Ch24_Type64 1.9 1.86 1.6 1.57 2.35
23 china:Ch26_59_Patton 1.3 1.33 1.5 1.1 1.16
24 china:Ch28_WZ_132A 1.1 1.04 1.5 1.11 1.24
25 china:Ch34_WZ-131G_FT 2.1 1.57 1.4 1.51 1.73
26 china:Ch34_WZ-131G_FT_BP 2.1 1.57 1.4 1.51 2.02
27 china:Ch35_T-34-2G_FT 1.8 1.48 1.4 1.25 1.61
28 china:Ch36_WZ-111-1G_FT 1.6 1.16 1.4 1.25 1.35
29 china:Ch37_WZ-111G_FT 1.2 1.05 1.4 1.15 1.13
30 china:Ch39_WZ120_1G_FT 1.6 1.16 1.4 1.24 1.35
31 china:Ch40_WZ120G_FT 1.1 1.05 1.4 1.08 1.13
32 china:Ch42_M41D 1.7 1.49 1.5 1.24 1.61
33 china:Ch43_WZ-122_2 1.3 1.21 1.5 1.31 1.35
34 china:Ch45_WZ_114 1.2 1.02 1.4 1.19 1.13
35 china:Ch47_BZ_176 1.2 1.03 1.4 1.17 1.13
36 china:Ch49_BZ_68 1.2 1.02 1.4 1.2 1.16
37 china:Ch50_BZ_166 1.4 1.17 1.4 1.3 1.35
38 china:Ch51_BZ_58 1.6 1.46 1.4 1.29 1.62
39 china:Ch56_BZ_74_1 1.2 1.02 1.4 1.16 1.14
40 china:Ch59_BZ58_2 1.2 1.02 1.4 1.19 1.13
41 china:Ch60_Type59_spc 1.3 1.37 1.5 1.32 1.35
42 china:Ch_LT_vz38 4.3 4.29 2.7 3.99 4.88
43 china:Ch_WZ-112v2 1.4 1.17 1.4 1.31 1.35
44 china:Type_58BP 1.9 1.88 1.5 1.57 2.04
45 european:Cz02_T50 1.1 1.03 1.4 1.12 1.19
46 european:Cz05_T34-100 1.5 1.45 1.4 1.29 1.61
47 european:Cz07_TVP_46 1.3 1.12 1.5 1.29 1.35
48 european:Cz08_T-25 1.9 1.83 1.5 1.56 2.02
49 european:Cz08_T-25BP 1.9 1.83 1.5 1.55 2.02
50 european:Cz08_T-25BPS 1.9 1.83 1.5 1.57 2.02
51 european:Cz13_T-27 1.1 1.07 1.4 1.12 1.19
52 european:Cz14_Skoda_T-56 1.4 1.17 1.4 1.33 1.35
53 european:Cz15_Skoda_T-45 1.7 1.44 1.4 1.27 1.61
54 european:Cz16_TNH_T_vz51 1.2 1.02 1.4 1.17 1.15
55 european:Cz18_TNH_105_1000 1.4 1.16 1.4 1.28 1.35
56 european:Cz19_Vz_44_1 1.7 1.43 1.4 1.3 1.61
57 european:Cz20_ShPTK_TVP_100 1.5 1.16 1.4 1.24 1.34
58 european:Cz29_Skoda_P-JS 2.2 1.67 1.4 1.56 1.78
59 european:Cz29_Skoda_P-JS_BP 2.2 1.67 1.4 1.57 1.79
60 european:F01_Vickers_MkF 5.5 5.07 3.3 6.16 8.8
61 european:It09_P43_ter 1.5 1.54 1.4 1.3 1.61
62 european:It10_P43_BD 1.9 1.74 1.5 1.57 1.82
63 european:It10_P43_bis 1.9 1.74 1.5 1.57 1.82
64 european:It10_P43_bis_BP 1.9 1.7 1.5 1.58 1.82
65 european:It116_Carro_d_assalto_P88 1.6 1.33 1.4 1.28 1.61
66 european:It117_ProgettoCC55mod54 1.3 1.13 1.4 1.29 1.35
67 european:It119_ProgettoC50mod66 1.1 1.02 1.4 1.18 1.13
68 european:It12_Prototipo_Standard_B 1.1 1.04 1.4 1.14 1.17
69 european:It13_Progetto_M35_mod_46 1.3 1.33 1.5 1.27 1.35
70 european:It14_P44_Pantera 1.3 1.21 1.5 1.26 1.35
71 european:It15_Semovente_M41 3.2 1.98 1.5 1.97 2.13
72 european:It16_Bassoto 2 1.59 1.4 1.53 1.8
73 european:It17_Semovente_mod_56 1.7 1.4 1.4 1.28 1.62
74 european:It18_Semovente_mod_64 1.4 1.2 1.4 1.24 1.35
75 european:It19_Controcarro_1MK2_Ciclope 1.1 1.05 1.4 1.19 1.13
76 european:It22_Progetto_C45_mod_71 1.3 1.14 1.4 1.23 1.35
77 european:It30_CC_mod_64_Prem 1.4 1.17 1.4 1.25 1.35
78 european:Pl03_PzV_Pudel 1.9 1.57 1.5 1.56 1.92
79 european:Pl05_50TP_Tyszkiewicza 1.2 1.02 1.4 1.18 1.13
80 european:Pl06_10TP 4.3 4.29 2.7 4.28 7.66
81 european:Pl07_14TP 3.6 3.57 2.1 3.12 5.56
82 european:Pl08_50TP_prot 1.2 1.02 1.4 1.17 1.14
83 european:Pl10_40TP_Habicha 2.1 1.64 1.4 1.57 1.83
84 european:Pl119_CS_52_LIS 1.5 1.56 1.4 1.31 1.61
85 european:Pl11_45TP_Habicha 1.6 1.44 1.4 1.3 1.61
86 european:Pl13_53TP_Markowskiego 1.4 1.14 1.4 1.32 1.35
87 european:Pl17_DS_PZlnz 2.6 2.28 1.7 1.95 2.12
88 european:Pl18_BUGI 1.9 1.57 1.5 1.58 1.94
89 european:Pl20_CS_44 1.5 1.54 1.4 1.3 1.61
90 european:Pl22_CS_59 1.1 1.04 1.4 1.1 1.21
91 european:Pl23_CS_53 1.3 1.31 1.5 1.29 1.35
92 european:Pl24_56TP 1.4 1.15 1.4 1.29 1.35
93 european:Pl27_SDP_Wz_66T 1.5 1.17 1.4 1.27 1.35
94 european:Pl28_SDP_Wz_60 1.1 1.05 1.4 1.11 1.12
95 european:Pl30_SDP_Wz58T 1.4 1.16 1.4 1.26 1.35
96 european:Pl31_SDP_Wz_57D 1.7 1.43 1.4 1.28 1.62
97 european:Pl32_SDP_Wz_44 2 1.57 1.4 1.52 1.76
98 european:Pl33_SDP_Wz_40 3.2 2.13 1.5 1.96 2.13
99 european:S02_StrvM42 2.7 2.22 1.7 1.95 2.11
100 european:S04_Lago-I 3.1 2.69 1.9 2.54 2.32
101 european:S07_Strv74BP 1.9 1.67 1.5 1.55 1.9
102 european:S07_Strv_74 1.9 1.67 1.5 1.55 1.9
103 european:S13_Leo 1.5 1.58 1.4 1.3 1.61
104 european:S13_Leo_NY 1.5 1.58 1.4 1.3 1.61
105 european:S17_EMIL_1952E2 1.2 1.02 1.4 1.16 1.12
106 european:S18_EMIL_1951E1 1.3 1.16 1.4 1.29 1.35
107 european:S23_Strv_81 1.3 1.14 1.5 1.26 1.35
108 european:S25_EMIL_51 1.3 1.12 1.4 1.29 1.35
109 european:S26_Lancen_C 1.3 1.22 1.5 1.31 1.35
110 european:S32_Bofors_Tornvagn 1.1 1.02 1.4 1.17 1.12
111 france:AMX38 4.4 4.29 2.7 4.15 5.95
112 france:AMX40 3 2.88 1.9 2.52 3.53
113 france:AMX50_Foch 1.2 1.05 1.4 1.11 1.14
114 france:AMX_12t 1.9 1.64 1.6 1.6 2.02
115 france:AMX_13_75 1.7 1.61 1.5 1.25 1.61
116 france:AMX_13_90 1.4 1.37 1.5 1.29 1.34
117 france:AMX_50_100 1.4 1.12 1.4 1.26 1.35
118 france:AMX_50_120 1.2 1.02 1.4 1.12 1.17
119 france:AMX_AC_Mle1946 1.7 1.45 1.4 1.24 1.61
120 france:AMX_AC_Mle1948 1.5 1.16 1.4 1.24 1.35
121 france:AMX_M4_1945 1.6 1.47 1.4 1.28 1.61
122 france:ARL_44 2.1 1.57 1.4 1.53 1.82
123 france:ARL_44BP 2.1 1.53 1.4 1.58 1.81
124 france:ARL_V39 2 1.59 1.4 1.52 1.82
125 france:Achilles_FR 2.1 1.6 1.4 1.52 1.83
126 france:B1 4.1 2.8 1.6 2.54 3.64
127 france:BDR_G1B 3.2 1.91 1.5 1.95 2.13
128 france:Bat-Chat-25CL 1.1 1.05 1.5 1.1 1.22
129 france:D1 4.3 4.62 2.7 3.8 6.52
130 france:D2 3.7 3.64 2.2 3 5.52
131 france:ELC_AMX 2.8 1.97 2.3 2.31 2.09
132 france:F113_PzV_FR 1.9 1.65 1.5 1.56 1.89
133 france:F114_Projet_4_1 1.1 1.03 1.4 1.09 1.22
134 france:F115_Lorraine_50t 1.2 1.02 1.4 1.13 1.16
135 france:F116_Bat_Chat_Bourrasque 1.3 1.28 1.5 1.31 1.34
136 france:F117_Alt_Proto_AMX_30 1.3 1.15 1.5 1.29 1.35
137 france:F11_Renault_G1R 3.2 2 1.5 1.96 2.12
138 france:F120_Project_Louis 1.1 1.04 1.4 1.12 1.14
139 france:F123_ERAC_105_Proto 1.3 1.22 1.5 1.3 1.35
140 france:F126_AMX_13_M24 1.5 1.58 1.4 1.32 1.62
141 france:F127_ELC_AMX_901_Proto 1.4 1.28 1.5 1.28 1.34
142 france:F140_Sentinelle 1.4 1.15 1.4 1.28 1.35
143 france:F19_Lorraine40t 1.3 1.17 1.5 1.3 1.34
144 france:F19_Lorraine40t_Com 1.3 1.17 1.5 1.29 1.34
145 france:F34_ARL_V39_BP 2.1 1.59 1.4 1.51 1.82
146 france:F68_AMX_Chasseur_de_char_46 1.3 1.17 1.5 1.25 1.33
147 france:F69_AMX_13D 1.4 1.16 1.5 1.28 1.34
148 france:F69_AMX_13_57GF 1.6 1.49 1.5 1.26 1.61
149 france:F71_AMX_30_prototype 1.1 1.06 1.4 1.12 1.19
150 france:F73_M4A1_Revalorise 1.3 1.23 1.5 1.27 1.34
151 france:F74_AMX_M4_1949 1.4 1.12 1.4 1.28 1.35
152 france:F75_Char_de_25t 1.1 1.02 1.5 1.11 1.22
153 france:F84_Somua_SM 1.4 1.12 1.4 1.14 1.14
154 france:F89_Canon_dassaut_105 1.6 1.15 1.4 1.26 1.35
155 france:FCM_36Pak40 4.2 2.95 1.9 3.18 5.52
156 france:FCM_50t 1.3 1.17 1.5 1.25 1.35
157 france:F_AMX-12BP 1.9 1.64 1.6 1.6 2.02
158 france:RenaultFT_AC 5.5 4.37 2.2 4.49 8.51
159 france:RenaultR35 5.5 5.28 3.3 6.76 7.8
160 france:RenaultUE57 4.2 2.79 1.9 2.68 5.95
161 france:S_35CA 3.2 2.07 1.5 1.96 2.11
162 france:Somua_Sau_40 3.7 2.75 1.8 2.54 3.62
163 germany:B-1bis_captured 4.1 2.96 1.6 2.55 3.89
164 germany:DW_II 3.7 2.52 1.6 2.52 3.53
165 germany:DickerMax 2.1 1.57 1.4 1.53 2.02
166 germany:E-25 1.8 1.46 1.4 1.24 1.61
167 germany:E-50 1.1 1.09 1.4 1.1 1.15
168 germany:E-75 1.2 1.02 1.4 1.17 1.14
169 germany:Ferdinand 1.4 1.16 1.4 1.25 1.35
170 germany:G100_Gtraktor_Krupp 4.9 3.31 1.9 3.49 5.75
171 germany:G103_RU_251 1.4 1.3 1.5 1.25 1.33
172 germany:G106_PzKpfwPanther_AusfF 1.3 1.21 1.5 1.26 1.35
173 germany:G109_Steyr_WT 1.8 1.37 1.4 1.27 1.6
174 germany:G110_Mauschen 1.2 1.02 1.4 1.18 1.12
175 germany:G112_JagdPanzer_105 1.2 1.05 1.4 1.04 1.18
176 germany:G113_SP_I_C 1.7 1.49 1.5 1.28 1.58
177 germany:G114_Rheinmetall_Scorpion 1.6 1.16 1.4 1.24 1.33
178 germany:G115_VK10001P 1.4 1.15 1.4 1.33 1.35
179 germany:G118_VK4503 1.7 1.3 1.4 1.27 1.61
180 germany:G119_Panzer58_SH 1.3 1.22 1.5 1.28 1.35
181 germany:G119_Pz58_Mutz 1.3 1.17 1.5 1.26 1.35
182 germany:G120_M41_90 1.4 1.29 1.5 1.26 1.34
183 germany:G126_HWK_12 1.1 1.05 1.5 1.08 1.29
184 germany:G136_Tiger_131 2.1 1.68 1.4 1.55 1.85
185 germany:G138_VK16801P 1.5 1.15 1.4 1.16 1.13
186 germany:G140_HWK_30 1.4 1.27 1.5 1.27 1.34
187 germany:G142_M48RPz 1.3 1.18 1.5 1.27 1.35
188 germany:G147_mPanzer_64 1.7 1.49 1.5 1.29 1.61
189 germany:G148_Elephant 1.4 1.16 1.4 1.21 1.35
190 germany:G154_Kpz_07_RH 1.3 1.15 1.5 1.26 1.35
191 germany:G162_Project_Kpz_07P_E 1.1 1.02 1.4 1.16 1.14
192 germany:G164_Kpz_Pr_68_P 1.3 1.22 1.5 1.3 1.34
193 germany:G165_Erich_Koncept 1.1 1.06 1.4 1.14 1.19
194 germany:G166_LKpz_70_K 1.1 1.02 1.5 1.11 1.32
195 germany:G167_Tiger_Maus 1.2 1.03 1.4 1.11 1.13
196 germany:G168_KJpz_T_III 1.2 1.05 1.4 1.05 1.29
197 germany:G190_VK_1602_Quby 1.4 1.36 1.5 1.25 1.34
198 germany:G20_Marder_II 4.2 3.01 1.9 3.18 5.54
199 germany:G85_Auf_Panther 1.5 1.49 1.5 1.25 1.61
200 germany:G_E-75RT 1.4 1.14 1.4 1.3 1.35
201 germany:G_E-75TS 1.2 1.03 1.4 1.13 1.13
202 germany:G_VK3502_BP 2.1 1.68 1.4 1.57 1.79
203 germany:H39_captured 4.3 5.58 2.7 3.99 6.04
204 germany:Hetzer 3.7 2.6 1.8 2.54 3.47
205 germany:Indien_Panzer 1.3 1.17 1.5 1.25 1.35
206 germany:JagdPanther 1.8 1.37 1.4 1.25 1.62
207 germany:JagdPantherII 1.6 1.16 1.4 1.23 1.35
208 germany:JagdPzIV 2.1 1.58 1.4 1.49 1.8
209 germany:JagdPzIV_AusfBP 2 1.58 1.4 1.49 1.8
210 germany:JagdTiger 1.1 1.05 1.4 1.04 1.12
211 germany:JagdTiger_SdKfz_185 1.4 1.17 1.4 1.21 1.35
212 germany:JagdTiger_SdKfz_185_Snowstorm 1.4 1.33 1.4 1.21 1.35
213 germany:Krupp-38D 2.6 2.22 1.7 1.95 2.12
214 germany:Lowe 1.4 1.11 1.4 1.29 1.35
215 germany:Nashorn 2 1.51 1.4 1.51 2.24
216 germany:Nashorn_BP 2 1.51 1.4 1.5 2.24
217 germany:Panther_II 1.3 1.21 1.5 1.26 1.35
218 germany:Panther_M10 1.5 1.54 1.4 1.26 1.61
219 germany:PanzerJager_I 5.5 4.33 2.2 4.49 8.8
220 germany:Pro_Ag_A 1.1 1.02 1.4 1.1 1.21
221 germany:Pz35t 4.4 4.9 2.7 3.94 6.05
222 germany:Pz38_NA 2.9 2.78 1.9 2.55 3.65
223 germany:Pz38t 3.6 3.88 2.1 3.05 5.58
224 germany:PzII 5.5 5.28 3.3 6.67 6.81
225 germany:PzIII 3.7 3.69 2.2 3.26 5.49
226 germany:PzIII_A 3.7 3.09 2.1 2.98 5.58
227 germany:PzIII_IV 2.7 2.22 1.7 1.95 2.12
228 germany:PzII_J 3.6 5.5 2.1 2.83 5.46
229 germany:PzII_Luchs 2.9 2.94 1.9 2.55 2.2
230 germany:PzIV 2.7 2.22 1.7 1.95 2.13
231 germany:PzIV_Hydro 2.5 2.22 1.7 1.95 2.11
232 germany:PzIV_schmalturm 1.9 1.65 1.5 1.53 1.87
233 germany:PzV 1.5 1.33 1.4 1.26 1.61
234 germany:PzVI 1.7 1.3 1.4 1.28 1.62
235 germany:PzVIB_Tiger_II 1.4 1.14 1.4 1.27 1.35
236 germany:PzVI_AusfBP 2.1 1.68 1.4 1.56 1.79
237 germany:PzVI_Tiger_P 1.7 1.3 1.4 1.28 1.62
238 germany:PzV_PzIV 2.6 2.22 1.7 1.95 2.12
239 germany:Pz_II_AusfG 3.8 2.9 2.1 2.85 5.55
240 germany:Pz_IV_AusfA 3.6 3.2 2.2 4.05 5.74
241 germany:Pz_IV_AusfBT 2.7 2.53 1.7 1.96 2.12
242 germany:Pz_IV_AusfD 3.1 2.43 1.9 2.54 3.09
243 germany:Pz_Sfl_IVc 3 1.61 1.5 1.98 2.08
244 germany:RhB_Waffentrager 1.6 1.26 1.4 1.28 1.33
245 germany:S01_Frankentank 1.7 1.33 1.4 1.37 1.62
246 germany:S35_captured 3.8 2.85 2.1 2.93 5.52
247 germany:StuGIII 3.2 1.93 1.5 1.97 2.12
248 germany:Sturer_Emil 1.8 1.31 1.4 1.28 1.61
249 germany:T-15 3.8 4.72 2.1 2.82 5.55
250 germany:T-25 2.5 2.13 1.7 1.95 2.11
251 germany:VK1602 2.8 2.9 2.3 1.96 2.11
252 germany:VK2801 1.9 1.53 1.6 1.58 1.95
253 germany:VK2801_BP 1.9 1.53 1.6 1.59 1.95
254 germany:VK3001H 3.2 1.75 1.5 1.96 2.13
255 germany:VK3001P 1.9 1.61 1.5 1.57 1.81
256 germany:VK3002DB 1.5 1.58 1.4 1.28 1.61
257 germany:VK3002DB_BP 1.9 1.65 1.5 1.56 1.87
258 germany:VK3002DB_V1 1.9 1.65 1.5 1.56 1.84
259 germany:VK3002M 1.9 1.65 1.5 1.57 1.91
260 germany:VK3601H 2.1 1.64 1.4 1.57 1.78
261 germany:VK4502A 1.3 1.17 1.4 1.29 1.35
262 germany:VK4502P 1.2 1.02 1.4 1.18 1.13
263 germany:Waffentrager_IV 1.2 1.06 1.4 1.12 1.19
264 germany:Waffentrager_IV_CL 1.2 1.05 1.4 1.06 1.19
265 japan:Chi_Ha 3.6 3.8 2.2 3.2 5.54
266 japan:Chi_He 3 2.72 1.9 2.55 3.24
267 japan:Chi_Ni 4.4 4.62 2.5 4.09 6.52
268 japan:Chi_Nu 2.6 2.02 1.7 1.94 2.12
269 japan:Chi_Nu_Kai 2.6 2.02 1.7 1.95 2.11
270 japan:Chi_Ri 1.5 1.58 1.4 1.25 1.61
271 japan:Chi_To 1.9 1.61 1.5 1.55 1.9
272 japan:Firefly_GuP 1.9 1.49 1.5 1.55 2.01
273 japan:G09_Hetzer_GuP 3.7 2.6 1.8 2.54 3.47
274 japan:Ha_Go 5.7 5.36 3.3 5.99 8.2
275 japan:IS-2_GuP 1.6 1.44 1.4 1.33 1.61
276 japan:J21_Ho_Ri 1.2 1.05 1.4 1.11 1.13
277 japan:J22_Ho_Ri_type96 1.5 1.16 1.4 1.25 1.35
278 japan:J23_Chi_To_SPG 1.8 1.37 1.4 1.25 1.62
279 japan:J24_Type_57 1.4 1.16 1.4 1.33 1.35
280 japan:J26_Type_68 1.2 1.02 1.4 1.13 1.13
281 japan:J27_Chi_Se 1.4 1.15 1.4 1.27 1.35
282 japan:J28_Ju_To 1.7 1.41 1.4 1.32 1.62
283 japan:J29_Ju_Nu 2.1 1.73 1.4 1.57 1.8
284 japan:J29_Ju_Nu_BP 2.1 1.73 1.4 1.57 1.8
285 japan:J30_Mitsu_108 3.2 2.37 1.5 1.97 2.13
286 japan:J31_Type_5_Ka_Ri 1.5 1.16 1.4 1.26 1.35
287 japan:J36_Type_63_HT 1.4 1.15 1.4 1.29 1.35
288 japan:J38_Type_95_Ji_Ro 1.5 1.16 1.4 1.22 1.35
289 japan:Ke_Ho 2.9 2.71 1.9 2.55 1
290 japan:Ke_Ni 3.8 4.07 2.1 2.67 5.71
291 japan:Ke_Ni_B 4.5 4.85 2.1 2.14 5.71
292 japan:PzVI_GuP 2.1 1.64 1.4 1.54 1.81
293 japan:Pz_IV_AusfH 2.6 2.22 1.7 1.95 2.12
294 japan:STA_1 1.3 1.15 1.5 1.28 1.34
295 japan:Type_61 1.1 1.02 1.4 1.1 1.24
296 other:AMX_dracula 1.5 1.58 1.4 1.3 1.61
297 other:Akawara 1.3 1.28 1.5 1.28 1.35
298 other:Oth06_Sega_Lupus 1.7 1.44 1.4 1.34 1.62
299 other:Oth08_WH_Vindicator 1.8 1.74 1.4 1.24 1.61
300 other:Oth09_WH_Predator 1.7 1.47 1.4 1.25 1.62
301 other:Oth11_T-55A 1.1 1.1 1.4 1.09 1.16
302 other:Oth12_Sega_Hafen 1.6 1.66 1.4 1.27 1.61
303 other:Oth14_MdM_AshKnight 1.7 1.47 1.4 1.27 1.62
304 other:Oth15_RustCrab 2.6 2.22 1.7 1.96 2.12
305 other:Oth16_KV_TG 1.6 1.76 1.4 1.27 1.61
306 other:Oth17_BDT-5A 2.6 2.22 1.7 1.95 2.12
307 other:Oth18_BDT-5B 1.9 1.85 1.5 1.55 1.94
308 other:Oth19_BDT-5C 1.5 1.66 1.4 1.25 1.61
309 other:Oth20_M4FL10 1.5 1.67 1.4 1.27 1.61
310 other:Oth22_Sega_Vulcan 1.7 1.41 1.4 1.33 1.62
311 other:Oth23_Werewolf 1.6 1.44 1.4 1.33 1.61
312 other:Oth24_StuH_42HW 3.2 2.6 1.5 1.97 2.13
313 other:Oth25_HWT20_FalconFire 1.7 1.47 1.4 1.25 1.62
314 other:Oth26_StrvM42HW 2.7 2.22 1.7 1.96 2.11
315 other:Oth27_M24_AMX 1.9 1.64 1.6 1.6 2.02
316 other:Oth28_Sturmfeur_HW 1.7 1.41 1.4 1.31 1.61
317 other:Oth29_A15HW 2.8 1.83 2.3 1.96 2.12
318 other:Oth30_T54MS 1.3 1.2 1.5 1.3 1.35
319 other:Oth31_T150BP 2.1 1.71 1.4 1.56 1.79
320 other:Oth33_Blizzard_BP 2 1.68 1.6 1.56 2.21
321 other:Oth34_PanzerMummy 1.7 1.39 1.4 1.3 1.61
322 other:Oth35_PanzerMejai 3.2 2.27 1.5 1.95 2.13
323 other:Oth36_Indien_Panzer_S1 1.5 1.56 1.4 1.28 1.61
324 other:Oth37_T26_E4_SuperPershing_S1 1.3 1.25 1.5 1.3 1.35
325 other:Oth38_50TP_Tyszkiewicza_S1 1.1 1.02 1.4 1.17 1.14
326 other:Oth39_T20_S2 1.5 1.62 1.4 1.28 1.61
327 other:Oth40_TVP_46_S2 1.3 1.15 1.5 1.28 1.35
328 other:Oth41_Centurion_Mk3_S2 1.1 1.05 1.4 1.11 1.18
329 other:Oth42_ForestWitch_HW23 1.7 1.42 1.4 1.32 1.61
330 other:Oth44_Charioteer_T 1.6 1.17 1.4 1.27 1.35
331 other:Oth45_Sandstriker 1.4 1.16 1.4 1.33 1.35
332 other:Oth46_Charioteer_S3 1.8 1.37 1.4 1.27 1.61
333 other:Oth47_AMX_13_90_S3 1.4 1.3 1.5 1.28 1.34
334 other:Oth48_EMIL_1951E1_S3 1.1 1.02 1.4 1.15 1.14
335 other:Oth49_TM_04_NT 1.5 1.63 1.4 1.34 1.61
336 other:Oth50_STT 1.6 1.55 1.5 1.28 1.61
337 other:Oth51_FD_24 1.3 1.18 1.5 1.28 1.35
338 other:Oth52_Toxique 1.3 1.32 1.5 1.31 1.35
339 other:Oth53_Rammer 1.6 1.46 1.4 1.31 1.61
340 other:Oth61_Pz_VM_Set 4.1 2.58 1.6 2.54 3.71
341 other:Oth62_Pz_VlD_Set 3.2 1.97 1.5 1.96 2.13
342 other:Oth63_Pz_VllF_Set 2.1 1.64 1.4 1.57 1.84
343 other:Oth64_Pz_VlllK_Set 1.6 1.44 1.4 1.31 1.61
344 other:Oth65_PzVIllM_Tigeom 2.1 1.67 1.4 1.59 1.86
345 other:Oth66_E100ANN_S4 1.7 1.47 1.4 1.33 1.62
346 other:Oth67_FV215b_183ANN_S4 1.5 1.16 1.4 1.24 1.35
347 other:Oth68_KV2ANN_S4 1.2 1.03 1.4 1.23 1.14
348 other:Oth69_Souleater 1.5 1.17 1.4 1.26 1.35
349 other:Oth70_Lightbringer 1.6 1.47 1.4 1.29 1.61
350 other:Oth71_Titan_Strife 1.3 1.16 1.4 1.28 1.35
351 other:Oth72_Hazard_I 1.5 1.57 1.4 1.3 1.61
352 other:Oth73_Nemini 1.4 1.17 1.4 1.31 1.35
353 other:Oth74_Argonaut 1.1 1.05 1.4 1.06 1.18
354 other:Oth75_Braniaq_DM5 1.1 1.06 1.4 1.14 1.18
355 other:Oth76_Destiny 1.2 1.02 1.4 1.17 1.15
356 other:Oth77_Prowler 1.4 1.16 1.4 1.3 1.35
357 other:Oth78_AC_17_P_Sterope 1.2 1.02 1.4 1.13 1.12
358 other:Oth80_AC_16_Celeno 1.2 1.02 1.4 1.13 1.12
359 other:Oth81_AC_15_Taygete 1.4 1.24 1.4 1.33 1.35
360 other:Oth82_AC_14_Pleione 1.4 1.15 1.4 1.29 1.35
361 other:Oth83_AC_13_Merope 1.7 1.4 1.4 1.32 1.61
362 other:Oth84_FVE_Defender 1.1 1.12 1.4 1.16 1.15
363 other:Oth85_Ghoul 1.3 1.36 1.5 1.28 1.35
364 other:Oth86_Survivalist 1.1 1.06 1.4 1.1 1.12
365 other:Oth92_JagdPantherII_Titan 1.4 1.17 1.4 1.24 1.35
366 other:Oth93_Convoyer 1.4 1.16 1.4 1.24 1.35
367 other:Sega_Edelweiss 1.5 1.62 1.4 1.28 1.61
368 other:Sega_Nameless 1.7 1.41 1.4 1.29 1.62
369 other:Strv_74A2 1.9 1.8 1.5 1.54 1.9
370 other:Van_Helsing 1.8 1.59 1.4 1.25 1.61
371 other:WZ-135G_FT 1.8 1.5 1.4 1.3 1.61
372 uk:GB03_Cruiser_Mk_I 4.4 4.9 2.7 3.96 9.12
373 uk:GB04_Valentine 2.9 1.85 1.9 2.53 3.37
374 uk:GB05_Vickers_Medium_Mk_II 6.5 3.84 2.2 3.8 12.37
375 uk:GB06_Vickers_Medium_Mk_III 4.9 3.9 1.9 2.88 5.74
376 uk:GB07_Matilda 4.1 2.83 1.6 2.56 3.64
377 uk:GB08_Churchill_I 3.3 1.8 1.5 1.96 2.13
378 uk:GB09_Churchill_VII 2.1 1.7 1.4 1.54 1.76
379 uk:GB09_Churchill_VII_BP 2.1 1.67 1.4 1.54 1.79
380 uk:GB101_FV1066_Senlac 1.4 1.16 1.5 1.24 1.33
381 uk:GB106_Cobra 1.1 1.05 1.4 1.14 1.16
382 uk:GB108_A46 1.5 1.56 1.5 1.25 1.6
383 uk:GB109_GSOR_1008 1.6 1.16 1.4 1.25 1.34
384 uk:GB109_GSOR_1008_Fearless 1.6 1.18 1.4 1.24 1.34
385 uk:GB10_Black_Prince 1.7 1.47 1.4 1.23 1.62
386 uk:GB110_FV4201_Chieftain_Prototype 1.1 1.02 1.4 1.13 1.15
387 uk:GB111_Charlemagne 1.4 1.13 1.4 1.31 1.35
388 uk:GB112_Caliban 1.4 1.16 1.4 1.45 1.35
389 uk:GB114_Vickers_MBT_Mk3 1.3 1.28 1.5 1.12 1.14
390 uk:GB116_Harry_Hopkins 2.5 1.97 2.3 1.96 2.12
391 uk:GB11_Caernarvon 1.3 1.13 1.4 1.23 1.35
392 uk:GB11_Caernarvon_D 1.4 1.16 1.4 1.3 1.35
393 uk:GB128_Nemesis 1.3 1.29 1.5 1.28 1.35
394 uk:GB12_Conqueror 1.1 1.02 1.4 1.1 1.13
395 uk:GB19_Sherman_Firefly 1.9 1.49 1.5 1.55 2.01
396 uk:GB20_Crusader 2.7 1.83 2.3 1.96 2.11
397 uk:GB21_A27BP 1.9 1.7 1.5 1.55 1.94
398 uk:GB21_Cromwell 1.9 1.7 1.5 1.55 1.94
399 uk:GB22_Comet 1.5 1.58 1.4 1.25 1.61
400 uk:GB23_Centurion 1.3 1.12 1.5 1.25 1.35
401 uk:GB24_Centurion_Mk3 1.1 1.02 1.4 1.14 1.15
402 uk:GB32_Tortoise 1.1 1.05 1.4 1.05 1.11
403 uk:GB35_Sentinel_AC_IV 1.9 1.7 1.5 1.56 1.94
404 uk:GB39_Universal_CarrierQF2 5.5 4.89 2.2 3.99 11.07
405 uk:GB40_Gun_Carrier_BP 2 1.56 1.4 1.52 1.77
406 uk:GB40_Gun_Carrier_Churchill 2.1 1.48 1.4 1.51 1.79
407 uk:GB41_Challenger 1.8 1.35 1.4 1.26 1.61
408 uk:GB42_Valentine_AT 4.3 2.68 1.9 2.43 5.48
409 uk:GB44_Archer_Custom 3.2 1.76 1.5 1.98 2.08
410 uk:GB51_Excelsior 3.2 2.27 1.5 1.97 2.13
411 uk:GB52_FV201_A45 1.7 1.47 1.4 1.24 1.61
412 uk:GB57_Alecto 3.7 2.93 1.8 2.54 1
413 uk:GB58_Cruiser_Mk_III 4.4 4.9 2.5 4.26 6.52
414 uk:GB59_Cruiser_Mk_IV 3.7 3.82 2.2 3.05 5.52
415 uk:GB60_Covenanter 3 3 1.9 2.55 2.68
416 uk:GB63_TOG_II 1.9 1.53 1.4 1.54 1.8
417 uk:GB63_TOG_II_BP 1.9 1.53 1.4 1.57 1.79
418 uk:GB68_Matilda_Black_Prince 2.5 1.96 1.7 1.98 2.12
419 uk:GB69_Cruiser_Mk_II 5.9 5.88 3.3 5.32 8.2
420 uk:GB71_AT_15A 1.7 1.58 1.4 1.22 1.62
421 uk:GB72_AT15 1.4 1.19 1.4 1.19 1.35
422 uk:GB73_AT2 3.2 2.45 1.5 1.97 2.13
423 uk:GB74_AT8 2 1.6 1.4 1.51 1.76
424 uk:GB75_AT7 1.7 1.33 1.4 1.23 1.62
425 uk:GB76_Mk_VIC 4.3 5.58 2.7 4.01 9.12
426 uk:GB80_Charioteer 1.6 1.16 1.4 1.27 1.34
427 uk:GB81_FV4004 1.2 1.05 1.4 1.11 1.2
428 uk:GB85_Cromwell_Berlin 1.9 1.7 1.5 1.55 1.94
429 uk:GB87_Chieftain_T95_turret 1.4 1.17 1.4 1.25 1.35
430 uk:GB93_Caernarvon_AX 1.3 1.13 1.4 1.25 1.35
431 uk:GB94_Centurion_Mk5-1_RAAC 1.3 1.12 1.5 1.28 1.35
432 uk:GB97_Chimera 1.3 1.15 1.5 1.32 1.35
433 uk:GB99_Turtle_Mk1 1.4 1.18 1.4 1.22 1.35
434 uk:GB_Centurion_MkDF 1.3 1.12 1.5 1.29 1.35
435 uk:GB_Churchill_MkVI 2.1 1.73 1.4 1.54 1.77
436 uk:GB_Churchill_MkVIII 2.1 1.64 1.4 1.56 1.79
437 uk:GB_FV301 1.4 1.07 1.5 1.25 1.35
438 uk:GB_Sherman_V 2.6 2.28 1.7 1.96 2.11
439 uk:GB_Vickers_Cruiser 1.1 1 1.5 1.11 1.2
440 uk:Sentinel_AC1 3 2.88 1.9 2.55 3.47
441 usa:A05_M4_105BD 2.7 2.4 1.7 1.96 2.12
442 usa:A100_T49 1.4 1.53 1.5 1.29 1.34
443 usa:A102_T28_concept 1.7 1.47 1.4 1.27 1.62
444 usa:A104_M4A3E8A 1.9 1.88 1.5 1.54 1.84
445 usa:A111_T25_Pilot 1.3 1.27 1.5 1.26 1.35
446 usa:A115_Chrysler_K 1.4 1.16 1.4 1.3 1.35
447 usa:A117_T26E5 1.4 1.15 1.4 1.27 1.35
448 usa:A121_T26E3 1.5 1.54 1.4 1.31 1.61
449 usa:A122_TS-5 1.4 1.15 1.4 1.23 1.35
450 usa:A124_T54E2 1.4 1.15 1.4 1.29 1.35
451 usa:A125_AEP_1 1.1 1.02 1.4 1.15 1.14
452 usa:A130_Super_Hellcat 1.7 1.51 1.4 1.24 1.6
453 usa:A132_T77 1.3 1.15 1.4 1.29 1.35
454 usa:A136_T42 1.5 1.54 1.4 1.29 1.61
455 usa:A140_ASTRON_REX_105 1.3 1.31 1.5 1.31 1.35
456 usa:A141_M-4-Y 1.4 1.16 1.4 1.27 1.35
457 usa:A142_Pawlack_Tank 2.1 1.68 1.4 1.58 1.85
458 usa:A146_TL_7_120 1.1 1.02 1.4 1.14 1.14
459 usa:A150_MBT_B 1.2 1.02 1.4 1.21 1.13
460 usa:A151_TS_60 1.3 1.14 1.4 1.06 1.15
461 usa:A175_ATAC 1.1 1.02 1.5 1.11 1.32
462 usa:A68_T28D 1.4 1.2 1.4 1.28 1.35
463 usa:A77_T6M 3 2.72 1.9 2.52 3.24
464 usa:A94_T37 1.9 1.73 1.6 1.55 2.21
465 usa:A99_T92E1 1.1 1.1 1.5 1.2 1.2
466 usa:M-3-Y 1.4 1.14 1.4 1.28 1.35
467 usa:M-3-Y-Midas 1.4 1.14 1.4 1.28 1.35
468 usa:M-5-Y 1.1 1.02 1.4 1.2 1.14
469 usa:M-7-Y 1.7 1.32 1.4 1.32 1.61
470 usa:M103 1.1 1.02 1.4 1.14 1.13
471 usa:M10_Wolverine 3.2 2.18 1.5 1.96 2.13
472 usa:M18E2BP 1.9 1.65 1.5 1.57 2.02
473 usa:M18_Hellcat 2.1 1.58 1.4 1.51 2.99
474 usa:M22_Locust 3.8 4.23 2.1 3 5.59
475 usa:M24_Chaffee 1.9 1.73 1.6 1.55 2.36
476 usa:M24_Chaffee_BP 1.9 1.73 1.6 1.55 2.36
477 usa:M2_lt 5.5 5.28 3.3 6.76 7.39
478 usa:M2_med 3.6 4.29 2.2 2.93 5.54
479 usa:M36_BP 2.1 1.58 1.4 1.53 1.85
480 usa:M36_Slagger 2.1 1.58 1.4 1.54 1.85
481 usa:M3_Grant 3 2.38 1.9 2.55 3.13
482 usa:M3_Stuart 4.4 4.29 2.7 4.23 5.89
483 usa:M41_Bulldog 1.6 1.49 1.5 1.25 1.61
484 usa:M46_CL 1.1 1.04 1.4 1.08 1.17
485 usa:M46_Patton 1.1 1.08 1.4 1.09 1.17
486 usa:M4A2E4 2.6 2.28 1.7 1.96 2.13
487 usa:M4A3E8_BP 1.9 1.88 1.5 1.55 1.84
488 usa:M4A3E8_Sherman 1.9 1.88 1.5 1.55 1.84
489 usa:M4_Sherman 2.7 1.98 1.7 1.95 2.12
490 usa:M5_Stuart 3.7 3.48 2.1 2.94 5.57
491 usa:M6 2.1 1.6 1.4 1.58 1.8
492 usa:M6A2E1 1.6 1.32 1.4 1.33 1.62
493 usa:M6A2E1_exp 1.4 1.07 1.4 1.33 1.35
494 usa:M6E2BP 2.1 1.6 1.4 1.57 1.77
495 usa:M6E2V2_BP 2.1 1.64 1.4 1.53 1.78
496 usa:M7_med 3.1 2.38 1.9 2.54 2.98
497 usa:M8A1 4 2.73 1.8 2.54 2.87
498 usa:Pershing 1.3 1.33 1.5 1.25 1.35
499 usa:Ram-II 2.5 2.22 1.7 1.97 2.12
500 usa:Sherman_Jumbo 1.9 1.88 1.5 1.56 1.8
501 usa:T14 3.2 2.35 1.5 1.97 2.13
502 usa:T18 5.5 4.48 2.2 4.43 5.65
503 usa:T1_E6 4.4 5 2.7 4.12 8.54
504 usa:T1_hvy 3.3 2 1.5 1.96 2.13
505 usa:T20 1.5 1.54 1.4 1.28 1.61
506 usa:T21 1.9 1.81 1.6 1.57 2.27
507 usa:T21_BP 1.9 1.81 1.6 1.56 2.24
508 usa:T23E3 1.5 1.54 1.4 1.25 1.61
509 usa:T25_2 1.7 1.4 1.4 1.23 1.61
510 usa:T25_AT 1.7 1.44 1.4 1.26 1.61
511 usa:T26_E4_SuperPershing 1.3 1.2 1.5 1.29 1.35
512 usa:T28 1.4 1.16 1.4 1.23 1.35
513 usa:T28_Prototype 1.4 1.16 1.4 1.24 1.35
514 usa:T29 1.7 1.32 1.4 1.31 1.61
515 usa:T2_lt 4.3 5.45 2.7 3.9 8.26
516 usa:T2_med 4.6 5 2.5 4.23 7.11
517 usa:T30 1.1 1.05 1.4 1.12 1.17
518 usa:T32 1.4 1.16 1.4 1.29 1.35
519 usa:T34_hvy 1.3 1.07 1.4 1.3 1.35
520 usa:T34_hvy_Freedom 1.4 1.07 1.4 1.31 1.35
521 usa:T40 4 2.38 1.8 2.54 3.13
522 usa:T49 3.2 2.18 1.5 1.97 2.08
523 usa:T49_Fearless 1.4 1.53 1.5 1.28 1.34
524 usa:T54E1 1.1 1.06 1.4 1.1 1.16
525 usa:T69 1.3 1.33 1.5 1.27 1.35
526 usa:T71 1.6 1.49 1.5 1.27 1.6
527 usa:T7_Combat_Car 4.4 5.58 2.7 4.26 8.54
528 usa:T82 4.1 13.5 1.9 2.78 5.78
529 usa:T95 1.2 1.05 1.4 1.11 1.12
530 usa:T95E2 1.3 1.33 1.5 1.25 1.35
531 ussr:A-20 3.1 2.72 1.9 2.54 3
532 ussr:A-32 3 2.85 1.9 2.52 3.43
533 ussr:AT-1 5.5 3.99 2.2 3.99 8.51
534 ussr:BT-2 4.3 4.71 2.7 4.18 6.52
535 ussr:BT-7 3.6 2.98 2.1 3.06 5.51
536 ussr:BT-SV 3.8 2.86 2.1 3.16 5.58
537 ussr:Churchill_LL 3.2 2.27 1.5 1.97 2.13
538 ussr:GAZ-74b 4 2.48 1.8 2.53 3.05
539 ussr:IS 1.6 1.44 1.4 1.32 1.62
540 ussr:IS-2Sh 1.4 1.15 1.4 1.31 1.35
541 ussr:IS-3 1.4 1.16 1.4 1.3 1.35
542 ussr:IS8 1.2 1.02 1.4 1.12 1.15
543 ussr:ISU-152 1.6 1.13 1.4 1.38 1.35
544 ussr:K-91 1.1 1.02 1.4 1.1 1.12
545 ussr:KV-13 1.5 1.67 1.4 1.26 1.61
546 ussr:KV-1s 2.3 1.6 1.4 1.6 1.82
547 ussr:KV-1s-BP 2.3 1.5 1.4 1.64 1.83
548 ussr:KV-220 3.2 2.27 1.5 1.96 2.13
549 ussr:KV-3 1.7 1.44 1.4 1.33 1.62
550 ussr:KV-5 1.5 1.15 1.4 1.29 1.35
551 ussr:KV1 3.2 2.11 1.5 1.95 2.13
552 ussr:KV2 2.4 2.25 1.4 1.52 1.85
553 ussr:KV4 1.4 1.13 1.4 1.3 1.35
554 ussr:LTP 3.8 2.88 2.1 3.16 5.56
555 ussr:M3_Stuart_LL 3.7 5.04 2.1 2.86 5.51
556 ussr:MS-1obr21 4.3 4.71 2.7 4.18 6.98
557 ussr:MT25 2 1.79 1.6 1.56 2.06
558 ussr:Matilda_II_LL 2.5 2.22 1.7 1.96 2.12
559 ussr:Object252 1.4 1.14 1.4 1.33 1.35
560 ussr:Object252_Fearless 1.4 1.41 1.4 1.33 1.35
561 ussr:Object_704 1.1 1.05 1.4 1.1 1.13
562 ussr:R105_BT_7A 3.8 6.27 2.1 3.86 5.64
563 ussr:R107_LTB 1.6 1.56 1.5 1.28 1.61
564 ussr:R109_T54S 1.4 1.34 1.5 1.29 1.34
565 ussr:R111_ISU130 1.5 1.16 1.4 1.23 1.35
566 ussr:R112_T54_45 1.3 1.37 1.5 1.31 1.35
567 ussr:R113_Object_730 1.4 1.15 1.4 1.31 1.35
568 ussr:R115_IS-3_auto 1.4 1.15 1.4 1.3 1.35
569 ussr:R116_ISU122C_Berlin 1.8 1.45 1.4 1.22 1.62
570 ussr:R127_T44_100_P 1.3 1.33 1.5 1.27 1.35
571 ussr:R131_LTG 1.6 1.61 1.5 1.28 1.61
572 ussr:R134_Object_252U 1.4 1.14 1.4 1.33 1.35
573 ussr:R140_M4A2_Loza 1.9 1.88 1.5 1.55 1.84
574 ussr:R146_STG 1.3 1.33 1.5 1.33 1.35
575 ussr:R154_T-34E-43 2.6 2.22 1.7 1.96 2.11
576 ussr:R158_LT_432 1.4 1.34 1.5 1.28 1.34
577 ussr:R159_SU_130PM 1.6 1.16 1.4 1.26 1.34
578 ussr:R160_object_244 2.1 1.71 1.4 1.55 1.82
579 ussr:R172_Object_752 1.1 1.02 1.4 1.13 1.13
580 ussr:R175_IS_2E 1.6 1.47 1.4 1.31 1.61
581 ussr:R180_Object_274A 1.3 1.31 1.5 1.3 1.35
582 ussr:R181_Object_452K 1.2 1.02 1.4 1.15 1.13
583 ussr:R187_Object_590 1.3 1.32 1.5 1.29 1.35
584 ussr:R192_K_2 1.4 1.12 1.4 1.33 1.35
585 ussr:R194_ST_62_var_2 1.3 1.31 1.5 1.28 1.35
586 ussr:R217_Object_Kust 1.5 1.16 1.4 1.27 1.35
587 ussr:R43_T-70_57 3.8 3.41 2.1 3.43 5.49
588 ussr:R63_ST_IBD 1.2 1.02 1.4 1.17 1.14
589 ussr:R71_IS_2B 1.6 1.44 1.4 1.33 1.61
590 ussr:R98_T44_85 1.5 1.64 1.4 1.26 1.61
591 ussr:R_Object_84 1.1 1.07 1.5 1.12 1.17
592 ussr:ST_I 1.2 1.02 1.4 1.16 1.13
593 ussr:SU-100 2.1 1.57 1.4 1.55 1.81
594 ussr:SU-101 1.5 1.15 1.4 1.26 1.35
595 ussr:SU-152 1.8 2 1.4 1.38 1.61
596 ussr:SU-76 4.2 3.6 1.9 3.18 5.56
597 ussr:SU-85 3.2 1.99 1.5 1.97 2.12
598 ussr:SU100M1 1.7 1.35 1.4 1.25 1.61
599 ussr:SU100Y 2.1 1.53 1.4 1.57 1.89
600 ussr:SU122_44 1.8 1.45 1.4 1.25 1.61
601 ussr:SU122_54 1.2 1.05 1.4 1.03 1.14
602 ussr:SU76I 4.1 3.31 1.9 3.18 5.47
603 ussr:SU_85I 3.1 2.3 1.5 1.98 2.12
604 ussr:T-127 3.8 2.76 2.1 3.16 5.52
605 ussr:T-26 5.5 5.07 3.3 6.16 8.2
606 ussr:T-28 3 2.85 1.9 2.54 3.09
607 ussr:T-34 2.7 2.01 1.7 1.96 2.12
608 ussr:T-34-85 1.9 1.71 1.5 1.58 1.87
609 ussr:T-34-85_Rudy 1.5 1.67 1.4 1.26 1.61
610 ussr:T-34-85_Victory 1.9 1.88 1.5 1.57 1.87
611 ussr:T-43 1.5 1.6 1.4 1.25 1.61
612 ussr:T-44 1.3 1.33 1.5 1.34 1.35
613 ussr:T-46 3.8 4.32 2.1 2.83 5.6
614 ussr:T-54 1.1 1.1 1.4 1.09 1.16
615 ussr:T150 2.1 1.55 1.4 1.59 1.82
616 ussr:T34_85BP 1.9 1.88 1.5 1.57 1.87
617 ussr:T34_85TUR 1.5 1.58 1.4 1.29 1.62
618 ussr:Tetrarch_LL 4.3 3.88 2.7 3.88 8.51
619 ussr:Valentine_LL 2.9 4.55 1.9 2.55 3.42
@@ -11,6 +11,7 @@ import { Html } from "@react-three/drei";
import { useRef } from "react"; import { useRef } from "react";
import { radToDeg } from "three/src/math/MathUtils.js"; import { radToDeg } from "three/src/math/MathUtils.js";
import { resolveArmorIndex } from "../../../core/blitzkit/resolveArmorIndex"; import { resolveArmorIndex } from "../../../core/blitzkit/resolveArmorIndex";
import { LocaleProvider, useLocale } from "../../../hooks/useLocale";
import { App } from "../../../stores/app"; import { App } from "../../../stores/app";
import { Duel } from "../../../stores/duel"; import { Duel } from "../../../stores/duel";
import { Tankopedia } from "../../../stores/tankopedia"; import { Tankopedia } from "../../../stores/tankopedia";
@@ -21,6 +22,7 @@ export function ArmorPlateDisplay() {
const highlightArmor = Tankopedia.use((state) => state.highlightArmor); const highlightArmor = Tankopedia.use((state) => state.highlightArmor);
const developerMode = App.useDeferred((state) => state.developerMode, false); const developerMode = App.useDeferred((state) => state.developerMode, false);
const input = useRef<HTMLInputElement>(null); const input = useRef<HTMLInputElement>(null);
const { locale } = useLocale();
if (highlightArmor === undefined) return null; if (highlightArmor === undefined) return null;
@@ -30,154 +32,164 @@ export function ArmorPlateDisplay() {
center center
style={{ pointerEvents: highlightArmor.editingPlate ? "auto" : "none" }} style={{ pointerEvents: highlightArmor.editingPlate ? "auto" : "none" }}
> >
<Card <LocaleProvider locale={locale}>
variant="classic" <Card
mb="4" variant="classic"
style={{ mb="4"
whiteSpace: "nowrap", style={{
color: highlightArmor.color, whiteSpace: "nowrap",
transform: "translateY(-50%)", color: highlightArmor.color,
}} transform: "translateY(-50%)",
> }}
<Flex direction="column"> >
<Text weight="bold"> <Flex direction="column">
{layerTypeNames[highlightArmor.type]}{" "} <Text weight="bold">
{highlightArmor.thickness.toFixed(0)} {layerTypeNames[highlightArmor.type]}{" "}
<Text size="1" weight="regular"> {highlightArmor.thickness.toFixed(0)}
mm <Text size="1" weight="regular">
</Text> mm
</Text>
{highlightArmor.type !== ArmorType.External && (
<Text size="2" color="gray">
{highlightArmor.thicknessAngled.toFixed(0)}
<Text size="1">
mm {radToDeg(highlightArmor.angle).toFixed(0)}°
</Text> </Text>
</Text> </Text>
)}
{developerMode && ( {highlightArmor.type !== ArmorType.External && (
<> <Text size="2" color="gray">
<Text mt="2" color="gray" size="2"> {highlightArmor.thicknessAngled.toFixed(0)}
<b>DEV:</b> <Code>{highlightArmor.name}</Code> <Text size="1">
mm {radToDeg(highlightArmor.angle).toFixed(0)}°
</Text>
</Text> </Text>
</> )}
)}
{highlightArmor.editingPlate && ( {developerMode && (
<Flex gap="2" align="center"> <>
<TextField.Root <Text mt="2" color="gray" size="2">
variant="classic" <b>DEV:</b>{" "}
style={{ width: 96 }} <Code variant="soft" highContrast color="gray">
mt="2" {highlightArmor.name}
ref={input} </Code>
key={highlightArmor.name} </Text>
type="number" </>
autoFocus )}
onFocus={(event) => event.target.select()}
onKeyDown={(event) => {
if (!highlightArmor.editingPlate) return;
const index = resolveArmorIndex(highlightArmor.name); {highlightArmor.editingPlate && (
<Flex gap="2" align="center">
if (index === undefined) return; <TextField.Root
variant="classic"
const thickness = event.currentTarget.valueAsNumber; style={{ width: 96 }}
mt="2"
if (isNaN(thickness) || thickness < 0) return; ref={input}
key={highlightArmor.name}
if (event.key === "Enter") { type="number"
input.current?.blur(); autoFocus
onFocus={(event) => event.target.select()}
Tankopedia.mutate((draft) => { onKeyDown={(event) => {
draft.highlightArmor = undefined;
const { protagonist } = Duel.state;
const tank = draft.model;
const turret = tank.turrets[protagonist.turret.id];
const gun = turret.guns[protagonist.gun.id];
const track = tank.tracks[protagonist.track.id];
if (highlightArmor.name.startsWith("hull_")) {
tank.armor.thickness[index] = thickness;
} else if (highlightArmor.name.startsWith("turret_")) {
turret.armor.thickness[index] = thickness;
} else if (highlightArmor.name.startsWith("gun_")) {
if (highlightArmor.name.includes("_armor_")) {
gun.armor.thickness[index] = thickness;
} else {
gun.thickness = thickness;
}
} else if (highlightArmor.name.startsWith("chassis_")) {
track.thickness = thickness;
}
});
} else if (event.key === "Escape") {
input.current?.blur();
Tankopedia.mutate((draft) => {
draft.highlightArmor = undefined;
});
}
}}
onClick={(event) => {
event.stopPropagation();
event.preventDefault();
}}
defaultValue={highlightArmor.thickness.toFixed(0)}
>
<TextField.Slot side="right">mm</TextField.Slot>
</TextField.Root>
<IconButton
mt="2"
color="red"
onClick={() => {
Tankopedia.mutate((draft) => {
if (!highlightArmor.editingPlate) return; if (!highlightArmor.editingPlate) return;
const index = resolveArmorIndex(highlightArmor.name); const index = resolveArmorIndex(highlightArmor.name);
if (index === undefined) return; if (index === undefined) return;
draft.highlightArmor = undefined; const thickness = event.currentTarget.valueAsNumber;
const initialState = Tankopedia.initial;
const { protagonist } = Duel.state;
const tank = draft.model;
const initialTank = initialState.model;
const turret = tank.turrets[protagonist.turret.id];
const initialTurret =
initialTank.turrets[protagonist.turret.id];
const gun = turret.guns[protagonist.gun.id];
const initialGun = initialTurret.guns[protagonist.gun.id];
const track = tank.tracks[protagonist.track.id];
const initialTrack =
initialTank.tracks[protagonist.track.id];
if (highlightArmor.name.startsWith("hull_")) { if (isNaN(thickness) || thickness < 0) return;
tank.armor.thickness[index] =
initialTank.armor.thickness[index]; if (event.key === "Enter") {
} else if (highlightArmor.name.startsWith("turret_")) { input.current?.blur();
turret.armor.thickness[index] =
initialTurret.armor.thickness[index]; Tankopedia.mutate((draft) => {
} else if (highlightArmor.name.startsWith("gun_")) { draft.highlightArmor = undefined;
if (highlightArmor.name.includes("_armor_")) { const { protagonist } = Duel.state;
gun.armor.thickness[index] = const tank = draft.model;
initialGun.armor.thickness[index]; const turret = tank.turrets[protagonist.turret.id];
} else { const gun = turret.guns[protagonist.gun.id];
gun.thickness = initialGun.thickness; const track = tank.tracks[protagonist.track.id];
}
} else if (highlightArmor.name.startsWith("chassis_")) { if (highlightArmor.name.startsWith("hull_")) {
track.thickness = initialTrack.thickness; tank.armor!.thickness[index] = thickness;
} else if (
highlightArmor.name.startsWith("turret_")
) {
turret.armor!.thickness[index] = thickness;
} else if (highlightArmor.name.startsWith("gun_")) {
if (highlightArmor.name.includes("_armor_")) {
gun.armor!.thickness[index] = thickness;
} else {
gun.thickness = thickness;
}
} else if (
highlightArmor.name.startsWith("chassis_")
) {
track.thickness = thickness;
}
});
} else if (event.key === "Escape") {
input.current?.blur();
Tankopedia.mutate((draft) => {
draft.highlightArmor = undefined;
});
} }
}); }}
}} onClick={(event) => {
> event.stopPropagation();
<ReloadIcon /> event.preventDefault();
</IconButton> }}
</Flex> defaultValue={highlightArmor.thickness.toFixed(0)}
)} >
</Flex> <TextField.Slot side="right">mm</TextField.Slot>
</Card> </TextField.Root>
<IconButton
mt="2"
color="red"
onClick={() => {
Tankopedia.mutate((draft) => {
if (!highlightArmor.editingPlate) return;
const index = resolveArmorIndex(highlightArmor.name);
if (index === undefined) return;
draft.highlightArmor = undefined;
const initialState = Tankopedia.initial;
const { protagonist } = Duel.state;
const tank = draft.model;
const initialTank = initialState.model;
const turret = tank.turrets[protagonist.turret.id];
const initialTurret =
initialTank.turrets[protagonist.turret.id];
const gun = turret.guns[protagonist.gun.id];
const initialGun =
initialTurret.guns[protagonist.gun.id];
const track = tank.tracks[protagonist.track.id];
const initialTrack =
initialTank.tracks[protagonist.track.id];
if (highlightArmor.name.startsWith("hull_")) {
tank.armor!.thickness[index] =
initialTank.armor!.thickness[index];
} else if (highlightArmor.name.startsWith("turret_")) {
turret.armor!.thickness[index] =
initialTurret.armor!.thickness[index];
} else if (highlightArmor.name.startsWith("gun_")) {
if (highlightArmor.name.includes("_armor_")) {
gun.armor!.thickness[index] =
initialGun.armor!.thickness[index];
} else {
gun.thickness = initialGun.thickness;
}
} else if (highlightArmor.name.startsWith("chassis_")) {
track.thickness = initialTrack.thickness;
}
});
}}
>
<ReloadIcon />
</IconButton>
</Flex>
)}
</Flex>
</Card>
</LocaleProvider>
</Html> </Html>
</group> </group>
); );
@@ -24,11 +24,11 @@ export const PrimaryArmorScene = memo(() => {
const trackModelDefinition = tankModelDefinition.tracks[track.id]; const trackModelDefinition = tankModelDefinition.tracks[track.id];
const turretModelDefinition = tankModelDefinition.turrets[turret.id]; const turretModelDefinition = tankModelDefinition.turrets[turret.id];
const gunModelDefinition = turretModelDefinition.guns[gun.id]; const gunModelDefinition = turretModelDefinition.guns[gun.id];
const hullOrigin = correctZYTuple(trackModelDefinition.origin); const hullOrigin = correctZYTuple(trackModelDefinition.origin!);
const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin); const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin!);
const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin); const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin!);
const isDynamicArmorActive = Duel.use((state) => const isDynamicArmorActive = Duel.use((state) =>
state.protagonist.consumables.includes(73) state.protagonist.consumables.includes(73),
); );
useTankTransform(track, turret, turretContainer, gunContainer); useTankTransform(track, turret, turretContainer, gunContainer);
@@ -41,8 +41,8 @@ export const PrimaryArmorScene = memo(() => {
const isVisible = isHull; const isVisible = isHull;
const armorId = nameToArmorId(node.name); const armorId = nameToArmorId(node.name);
const { spaced, thickness } = resolveArmor( const { spaced, thickness } = resolveArmor(
tankModelDefinition.armor, tankModelDefinition.armor!,
armorId armorId,
); );
if ( if (
@@ -69,13 +69,13 @@ export const PrimaryArmorScene = memo(() => {
const isCurrentTurret = node.name.startsWith( const isCurrentTurret = node.name.startsWith(
`turret_${turretModelDefinition.model_id `turret_${turretModelDefinition.model_id
.toString() .toString()
.padStart(2, "0")}` .padStart(2, "0")}`,
); );
const isVisible = isCurrentTurret; const isVisible = isCurrentTurret;
const armorId = nameToArmorId(node.name); const armorId = nameToArmorId(node.name);
const { spaced, thickness } = resolveArmor( const { spaced, thickness } = resolveArmor(
turretModelDefinition.armor, turretModelDefinition.armor!,
armorId armorId,
); );
if ( if (
@@ -103,13 +103,13 @@ export const PrimaryArmorScene = memo(() => {
<group ref={gunContainer}> <group ref={gunContainer}>
{armorNodes.map((node) => { {armorNodes.map((node) => {
const isCurrentGun = node.name.startsWith( const isCurrentGun = node.name.startsWith(
`gun_${gunModelDefinition.model_id.toString().padStart(2, "0")}` `gun_${gunModelDefinition.model_id.toString().padStart(2, "0")}`,
); );
const isVisible = isCurrentGun; const isVisible = isCurrentGun;
const armorId = nameToArmorId(node.name); const armorId = nameToArmorId(node.name);
const { spaced, thickness } = resolveArmor( const { spaced, thickness } = resolveArmor(
gunModelDefinition.armor, gunModelDefinition.armor!,
armorId armorId,
); );
if ( if (
@@ -19,7 +19,7 @@ import {
import { degToRad } from "three/src/math/MathUtils.js"; import { degToRad } from "three/src/math/MathUtils.js";
import { hasEquipment } from "../../../../core/blitzkit/hasEquipment"; import { hasEquipment } from "../../../../core/blitzkit/hasEquipment";
import { jsxTree } from "../../../../core/blitzkit/jsxTree"; import { jsxTree } from "../../../../core/blitzkit/jsxTree";
import { Duel, type EquipmentMatrix } from "../../../../stores/duel"; import { Duel } from "../../../../stores/duel";
import { Tankopedia } from "../../../../stores/tankopedia"; import { Tankopedia } from "../../../../stores/tankopedia";
import { TankopediaPersistent } from "../../../../stores/tankopediaPersistent"; import { TankopediaPersistent } from "../../../../stores/tankopediaPersistent";
import { transitionEvent } from "../../../Tankopedia/HeroSection/components/TankSandbox/components/Lighting"; import { transitionEvent } from "../../../Tankopedia/HeroSection/components/TankSandbox/components/Lighting";
@@ -88,24 +88,18 @@ export function PrimaryArmorSceneComponent({
material.uniforms.caliber.value = shell.caliber; material.uniforms.caliber.value = shell.caliber;
material.uniforms.ricochet.value = degToRad( material.uniforms.ricochet.value = degToRad(
isExplosive(shell.type) ? 90 : shell.ricochet! isExplosive(shell.type) ? 90 : shell.ricochet!,
); );
material.uniforms.normalization.value = degToRad( material.uniforms.normalization.value = degToRad(
shell.normalization ?? 0 shell.normalization ?? 0,
); );
material.uniforms.isExplosive.value = isExplosive(shell.type); material.uniforms.isExplosive.value = isExplosive(shell.type);
material.uniforms.canSplash.value = canSplash(shell.type); material.uniforms.canSplash.value = canSplash(shell.type);
material.uniforms.damage.value = shell.armor_damage; material.uniforms.damage.value = shell.armor_damage;
material.uniforms.explosionRadius.value = shell.explosion_radius; material.uniforms.explosionRadius.value = shell.explosion_radius;
handleProtagonistEquipmentChange( handleProtagonistEquipmentChange(true);
Duel.state.protagonist.equipmentMatrix, handleAntagonistEquipmentChange(true);
true
);
handleAntagonistEquipmentChange(
Duel.state.antagonist.equipmentMatrix,
true
);
invalidate(); invalidate();
} }
@@ -122,38 +116,43 @@ export function PrimaryArmorSceneComponent({
function handleWireframeChange(wireframe: boolean) { function handleWireframeChange(wireframe: boolean) {
material.wireframe = wireframe; material.wireframe = wireframe;
} }
function handleProtagonistEquipmentChange( function handleProtagonistEquipmentChange(noInvalidate = false) {
equipment: EquipmentMatrix, const equipment = Duel.state.protagonist.equipmentMatrix;
noInvalidate = false
) {
const hasEnhancedArmor = hasEquipment( const hasEnhancedArmor = hasEquipment(
110, 110,
Duel.state.protagonist.tank.equipment_preset, Duel.state.protagonist.tank.equipment_preset,
equipment equipment,
); );
material.uniforms.thickness.value = hasEnhancedArmor const equalizer =
? thickness * 1.03 (Duel.state.equalize
: thickness; ? Duel.state.protagonist.tank.equalizer?.armor
: undefined) ?? 1;
material.uniforms.thickness.value =
thickness * (hasEnhancedArmor ? 1.03 : 1) * equalizer;
if (!noInvalidate) invalidate(); if (!noInvalidate) invalidate();
} }
function handleAntagonistEquipmentChange( function handleAntagonistEquipmentChange(noInvalidate = false) {
equipment: EquipmentMatrix, const equipment = Duel.state.antagonist.equipmentMatrix;
noInvalidate = false
) {
const tankopediaEphemeral = Tankopedia.state; const tankopediaEphemeral = Tankopedia.state;
const shell = const shell =
tankopediaEphemeral.customShell ?? Duel.state.antagonist.shell; tankopediaEphemeral.customShell ?? Duel.state.antagonist.shell;
const penetration = shell.penetration.near; const penetration = shell.penetration!.near;
const hasCalibratedShells = hasEquipment( const hasCalibratedShells = hasEquipment(
103, 103,
Duel.state.antagonist.tank.equipment_preset, Duel.state.antagonist.tank.equipment_preset,
equipment equipment,
); );
material.uniforms.penetration.value = material.uniforms.penetration.value =
penetration * penetration *
resolvePenetrationCoefficient(hasCalibratedShells, shell.type); resolvePenetrationCoefficient(
hasCalibratedShells,
Duel.state.equalize,
shell.type,
Duel.state.antagonist.tank.equalizer,
);
if (!noInvalidate) invalidate(); if (!noInvalidate) invalidate();
} }
@@ -165,12 +164,12 @@ export function PrimaryArmorSceneComponent({
handleShellChange(); handleShellChange();
handleGreenPenetrationChange(TankopediaPersistent.state.greenPenetration); handleGreenPenetrationChange(TankopediaPersistent.state.greenPenetration);
handleAdvancedHighlightingChange( handleAdvancedHighlightingChange(
TankopediaPersistent.state.advancedHighlighting TankopediaPersistent.state.advancedHighlighting,
); );
handleOpaqueChange(TankopediaPersistent.state.opaque); handleOpaqueChange(TankopediaPersistent.state.opaque);
handleWireframeChange(TankopediaPersistent.state.wireframe); handleWireframeChange(TankopediaPersistent.state.wireframe);
handleProtagonistEquipmentChange(Duel.state.protagonist.equipmentMatrix); handleProtagonistEquipmentChange();
handleAntagonistEquipmentChange(Duel.state.antagonist.equipmentMatrix); handleAntagonistEquipmentChange();
transitionEvent.on(handleTransitionEvent); transitionEvent.on(handleTransitionEvent);
@@ -179,20 +178,27 @@ export function PrimaryArmorSceneComponent({
Tankopedia.on((state) => state.customShell, handleShellChange), Tankopedia.on((state) => state.customShell, handleShellChange),
TankopediaPersistent.on( TankopediaPersistent.on(
(state) => state.greenPenetration, (state) => state.greenPenetration,
handleGreenPenetrationChange handleGreenPenetrationChange,
), ),
TankopediaPersistent.on( TankopediaPersistent.on(
(state) => state.advancedHighlighting, (state) => state.advancedHighlighting,
handleAdvancedHighlightingChange handleAdvancedHighlightingChange,
), ),
TankopediaPersistent.on((state) => state.opaque, handleOpaqueChange), TankopediaPersistent.on((state) => state.opaque, handleOpaqueChange),
Duel.on( Duel.on(
(state) => state.protagonist.equipmentMatrix, (state) => state.protagonist.equipmentMatrix,
(equipment) => handleProtagonistEquipmentChange(equipment) () => handleProtagonistEquipmentChange(),
), ),
Duel.on( Duel.on(
(state) => state.antagonist.equipmentMatrix, (state) => state.antagonist.equipmentMatrix,
(equipment) => handleAntagonistEquipmentChange(equipment) () => handleAntagonistEquipmentChange(),
),
Duel.on(
(state) => state.equalize,
() => {
handleProtagonistEquipmentChange();
handleAntagonistEquipmentChange();
},
), ),
() => transitionEvent.off(handleTransitionEvent), () => transitionEvent.off(handleTransitionEvent),
]; ];
@@ -204,7 +210,7 @@ export function PrimaryArmorSceneComponent({
useFrame(({ gl, camera }) => { useFrame(({ gl, camera }) => {
gl.getSize(material.uniforms.resolution.value).multiplyScalar( gl.getSize(material.uniforms.resolution.value).multiplyScalar(
gl.getPixelRatio() gl.getPixelRatio(),
); );
material.uniforms.spacedArmorBuffer.value = spacedArmorRenderTarget.texture; material.uniforms.spacedArmorBuffer.value = spacedArmorRenderTarget.texture;
material.uniforms.spacedArmorDepth.value = material.uniforms.spacedArmorDepth.value =
@@ -52,7 +52,7 @@ export function ShotDisplay() {
useEffect(() => { useEffect(() => {
if (shot?.splashRadius !== undefined) { if (shot?.splashRadius !== undefined) {
const outlineGeometry = new BufferGeometry().setFromPoints( const outlineGeometry = new BufferGeometry().setFromPoints(
new Path().absarc(0, 0, 1, 0, Math.PI * 2).getSpacedPoints(50) new Path().absarc(0, 0, 1, 0, Math.PI * 2).getSpacedPoints(50),
); );
const outlineMaterial = new LineDashedMaterial({ const outlineMaterial = new LineDashedMaterial({
color: "orange", color: "orange",
@@ -61,7 +61,7 @@ export function ShotDisplay() {
}); });
const outline = new Line( const outline = new Line(
outlineGeometry, outlineGeometry,
outlineMaterial outlineMaterial,
).computeLineDistances(); ).computeLineDistances();
splashRadiusWrapper.current?.add(outline); splashRadiusWrapper.current?.add(outline);
@@ -134,11 +134,11 @@ export function ShotDisplay() {
const outLength = const outLength =
shot.out && shot.out.layers.length > 0 shot.out && shot.out.layers.length > 0
? (shot.in.layers.at(-1) as ShotLayerNonExternal).point.distanceTo( ? (shot.in.layers.at(-1) as ShotLayerNonExternal).point.distanceTo(
(shot.out.layers.at(-1) as ShotLayerNonExternal).point (shot.out.layers.at(-1) as ShotLayerNonExternal).point,
) )
: SHOT_DISPLAY_LENGTH_INFINITY; : SHOT_DISPLAY_LENGTH_INFINITY;
const inLast = shot.in.layers.findLast( const inLast = shot.in.layers.findLast(
(layer) => layer.type !== null (layer) => layer.type !== null,
) as ShotLayerNonExternal; ) as ShotLayerNonExternal;
const outTitleColor = shot.out const outTitleColor = shot.out
? shotStatusColors[shot.out.status] ? shotStatusColors[shot.out.status]
@@ -154,7 +154,7 @@ export function ShotDisplay() {
position={inLast.point} position={inLast.point}
ref={splashRadiusWrapper} ref={splashRadiusWrapper}
rotation={new Euler().setFromQuaternion( rotation={new Euler().setFromQuaternion(
new Quaternion().setFromUnitVectors(K_HAT, shot.in.surfaceNormal) new Quaternion().setFromUnitVectors(K_HAT, shot.in.surfaceNormal),
)} )}
> >
<mesh> <mesh>
@@ -225,7 +225,7 @@ export function ShotDisplay() {
<group <group
position={inLast.point} position={inLast.point}
rotation={new Euler().setFromQuaternion( rotation={new Euler().setFromQuaternion(
new Quaternion().setFromUnitVectors(J_HAT, shot.in.surfaceNormal) new Quaternion().setFromUnitVectors(J_HAT, shot.in.surfaceNormal),
)} )}
> >
<mesh position={[0, inLength / 2, 0]} renderOrder={2}> <mesh position={[0, inLength / 2, 0]} renderOrder={2}>
@@ -253,8 +253,8 @@ export function ShotDisplay() {
rotation={new Euler().setFromQuaternion( rotation={new Euler().setFromQuaternion(
new Quaternion().setFromUnitVectors( new Quaternion().setFromUnitVectors(
J_HAT, J_HAT,
shot.out.surfaceNormal.clone() shot.out.surfaceNormal.clone(),
) ),
)} )}
> >
<mesh position={[0, outLength / 2, 0]} renderOrder={2}> <mesh position={[0, outLength / 2, 0]} renderOrder={2}>
@@ -37,15 +37,15 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
const trackModelDefinition = tankModelDefinition.tracks[track.id]; const trackModelDefinition = tankModelDefinition.tracks[track.id];
const turretModelDefinition = tankModelDefinition.turrets[turret.id]; const turretModelDefinition = tankModelDefinition.turrets[turret.id];
const gunModelDefinition = turretModelDefinition.guns[gun.id]; const gunModelDefinition = turretModelDefinition.guns[gun.id];
const hullOrigin = correctZYTuple(trackModelDefinition.origin); const hullOrigin = correctZYTuple(trackModelDefinition.origin!);
const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin); const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin!);
const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin); const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin!);
const maskOrigin = const maskOrigin =
gunModelDefinition.mask === undefined gunModelDefinition.mask === undefined
? undefined ? undefined
: gunModelDefinition.mask + hullOrigin.y + turretOrigin.y + gunOrigin.y; : gunModelDefinition.mask + hullOrigin.y + turretOrigin.y + gunOrigin.y;
const isDynamicArmorActive = Duel.use((state) => const isDynamicArmorActive = Duel.use((state) =>
state.protagonist.consumables.includes(73) state.protagonist.consumables.includes(73),
); );
useTankTransform(track, turret, turretContainer, gunContainer); useTankTransform(track, turret, turretContainer, gunContainer);
@@ -58,8 +58,8 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
const isVisible = isHull; const isVisible = isHull;
const armorId = nameToArmorId(node.name); const armorId = nameToArmorId(node.name);
const { spaced, thickness } = resolveArmor( const { spaced, thickness } = resolveArmor(
tankModelDefinition.armor, tankModelDefinition.armor!,
armorId armorId,
); );
if ( if (
@@ -104,13 +104,13 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
<group ref={turretContainer}> <group ref={turretContainer}>
{armorNodes.map((node) => { {armorNodes.map((node) => {
const isCurrentTurret = node.name.startsWith( const isCurrentTurret = node.name.startsWith(
`turret_${turretModelDefinition.model_id.toString().padStart(2, "0")}` `turret_${turretModelDefinition.model_id.toString().padStart(2, "0")}`,
); );
const isVisible = isCurrentTurret; const isVisible = isCurrentTurret;
const armorId = nameToArmorId(node.name); const armorId = nameToArmorId(node.name);
const { spaced, thickness } = resolveArmor( const { spaced, thickness } = resolveArmor(
turretModelDefinition.armor, turretModelDefinition.armor!,
armorId armorId,
); );
if ( if (
@@ -139,13 +139,13 @@ export const SpacedArmorScene = memo<SpacedArmorSceneProps>(({ scene }) => {
<group ref={gunContainer}> <group ref={gunContainer}>
{armorNodes.map((node) => { {armorNodes.map((node) => {
const isCurrentGun = node.name.startsWith( const isCurrentGun = node.name.startsWith(
`gun_${gunModelDefinition.model_id.toString().padStart(2, "0")}` `gun_${gunModelDefinition.model_id.toString().padStart(2, "0")}`,
); );
const isVisible = isCurrentGun; const isVisible = isCurrentGun;
const armorId = nameToArmorId(node.name); const armorId = nameToArmorId(node.name);
const { spaced, thickness } = resolveArmor( const { spaced, thickness } = resolveArmor(
gunModelDefinition.armor, gunModelDefinition.armor!,
armorId armorId,
); );
if ( if (
@@ -10,7 +10,7 @@ import {
import type { ArmorUserData, ExternalModuleVariant } from "../.."; import type { ArmorUserData, ExternalModuleVariant } from "../..";
import { hasEquipment } from "../../../../../../core/blitzkit/hasEquipment"; import { hasEquipment } from "../../../../../../core/blitzkit/hasEquipment";
import { jsxTree } from "../../../../../../core/blitzkit/jsxTree"; import { jsxTree } from "../../../../../../core/blitzkit/jsxTree";
import { Duel, type EquipmentMatrix } from "../../../../../../stores/duel"; import { Duel } from "../../../../../../stores/duel";
import { Tankopedia } from "../../../../../../stores/tankopedia"; import { Tankopedia } from "../../../../../../stores/tankopedia";
import { ArmorType } from "../../../SpacedArmorScene"; import { ArmorType } from "../../../SpacedArmorScene";
import fragmentShader from "./shaders/fragment.glsl?raw"; import fragmentShader from "./shaders/fragment.glsl?raw";
@@ -50,48 +50,71 @@ export function SpacedArmorSubExternal({
const tankopediaEphemeral = Tankopedia.state; const tankopediaEphemeral = Tankopedia.state;
const shell = const shell =
tankopediaEphemeral.customShell ?? Duel.state.antagonist.shell; tankopediaEphemeral.customShell ?? Duel.state.antagonist.shell;
material.uniforms.penetration.value = shell.penetration.near; material.uniforms.penetration.value = shell.penetration!.near;
} }
function handleProtagonistEquipmentChange(equipment: EquipmentMatrix) { function handleProtagonistEquipmentChange() {
const equipment = Duel.state.protagonist.equipmentMatrix;
const hasEnhancedArmor = hasEquipment( const hasEnhancedArmor = hasEquipment(
110, 110,
Duel.state.protagonist.tank.equipment_preset, Duel.state.protagonist.tank.equipment_preset,
equipment equipment,
); );
material.uniforms.thickness.value = hasEnhancedArmor const equalizer =
? thickness * 1.03 (Duel.state.equalize
: thickness; ? Duel.state.protagonist.tank.equalizer?.armor
: undefined) ?? 1;
material.uniforms.thickness.value =
thickness * (hasEnhancedArmor ? 1.03 : 1) * equalizer;
} }
function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) { function handleAntagonistEquipmentChange() {
const equipment = Duel.state.antagonist.equipmentMatrix;
const tankopediaEphemeral = Tankopedia.state; const tankopediaEphemeral = Tankopedia.state;
const shell = const shell =
tankopediaEphemeral.customShell ?? Duel.state.antagonist.shell; tankopediaEphemeral.customShell ?? Duel.state.antagonist.shell;
const penetration = shell.penetration.near; const penetration = shell.penetration!.near;
const hasCalibratedShells = hasEquipment( const hasCalibratedShells = hasEquipment(
103, 103,
Duel.state.antagonist.tank.equipment_preset, Duel.state.antagonist.tank.equipment_preset,
equipment equipment,
); );
const equalize =
(Duel.state.equalize
? Duel.state.antagonist.tank.equalizer?.penetration
: undefined) ?? 1;
material.uniforms.penetration.value = material.uniforms.penetration.value =
penetration * penetration *
resolvePenetrationCoefficient(hasCalibratedShells, shell.type); resolvePenetrationCoefficient(
hasCalibratedShells,
Duel.state.equalize,
shell.type,
Duel.state.antagonist.tank.equalizer,
) *
equalize;
} }
handleShellChange(); handleShellChange();
handleProtagonistEquipmentChange(Duel.state.protagonist.equipmentMatrix); handleProtagonistEquipmentChange();
handleAntagonistEquipmentChange(Duel.state.antagonist.equipmentMatrix); handleAntagonistEquipmentChange();
const unsubscribes = [ const unsubscribes = [
Duel.on((state) => state.antagonist.shell, handleShellChange), Duel.on((state) => state.antagonist.shell, handleShellChange),
Tankopedia.on((state) => state.customShell, handleShellChange), Tankopedia.on((state) => state.customShell, handleShellChange),
Duel.on( Duel.on(
(state) => state.protagonist.equipmentMatrix, (state) => state.protagonist.equipmentMatrix,
handleProtagonistEquipmentChange handleProtagonistEquipmentChange,
), ),
Duel.on( Duel.on(
(state) => state.antagonist.equipmentMatrix, (state) => state.antagonist.equipmentMatrix,
handleAntagonistEquipmentChange handleAntagonistEquipmentChange,
),
Duel.on(
(state) => state.equalize,
() => {
handleProtagonistEquipmentChange();
handleAntagonistEquipmentChange();
},
), ),
]; ];
@@ -10,7 +10,7 @@ import { degToRad } from "three/src/math/MathUtils.js";
import type { ArmorUserData } from "../.."; import type { ArmorUserData } from "../..";
import { hasEquipment } from "../../../../../../core/blitzkit/hasEquipment"; import { hasEquipment } from "../../../../../../core/blitzkit/hasEquipment";
import { jsxTree } from "../../../../../../core/blitzkit/jsxTree"; import { jsxTree } from "../../../../../../core/blitzkit/jsxTree";
import { Duel, type EquipmentMatrix } from "../../../../../../stores/duel"; import { Duel } from "../../../../../../stores/duel";
import { Tankopedia } from "../../../../../../stores/tankopedia"; import { Tankopedia } from "../../../../../../stores/tankopedia";
import { ArmorType } from "../../../SpacedArmorScene"; import { ArmorType } from "../../../SpacedArmorScene";
import fragmentShader from "./shaders/fragment.glsl?raw"; import fragmentShader from "./shaders/fragment.glsl?raw";
@@ -53,55 +53,78 @@ export function SpacedArmorSubSpaced({
const shell = const shell =
tankopediaEphemeral.customShell ?? Duel.state.antagonist.shell; tankopediaEphemeral.customShell ?? Duel.state.antagonist.shell;
material.uniforms.penetration.value = shell.penetration.near; material.uniforms.penetration.value = shell.penetration!.near;
material.uniforms.caliber.value = shell.caliber; material.uniforms.caliber.value = shell.caliber;
material.uniforms.ricochet.value = degToRad( material.uniforms.ricochet.value = degToRad(
isExplosive(shell.type) ? 90 : shell.ricochet! isExplosive(shell.type) ? 90 : shell.ricochet!,
); );
material.uniforms.normalization.value = degToRad( material.uniforms.normalization.value = degToRad(
shell.normalization ?? 0 shell.normalization ?? 0,
); );
} }
function handleProtagonistEquipmentChange(equipment: EquipmentMatrix) { function handleProtagonistEquipmentChange() {
const equipment = Duel.state.protagonist.equipmentMatrix;
const hasEnhancedArmor = hasEquipment( const hasEnhancedArmor = hasEquipment(
110, 110,
Duel.state.protagonist.tank.equipment_preset, Duel.state.protagonist.tank.equipment_preset,
equipment equipment,
); );
material.uniforms.thickness.value = hasEnhancedArmor const equalizer =
? thickness * 1.03 (Duel.state.equalize
: thickness; ? Duel.state.protagonist.tank.equalizer?.armor
: undefined) ?? 1;
material.uniforms.thickness.value =
thickness * (hasEnhancedArmor ? 1.03 : 1) * equalizer;
} }
function handleAntagonistEquipmentChange(equipment: EquipmentMatrix) { function handleAntagonistEquipmentChange() {
const equipment = Duel.state.antagonist.equipmentMatrix;
const tankopediaEphemeral = Tankopedia.state; const tankopediaEphemeral = Tankopedia.state;
const shell = const shell =
tankopediaEphemeral.customShell ?? Duel.state.antagonist.shell; tankopediaEphemeral.customShell ?? Duel.state.antagonist.shell;
const penetration = shell.penetration.near; const penetration = shell.penetration!.near;
const hasCalibratedShells = hasEquipment( const hasCalibratedShells = hasEquipment(
103, 103,
Duel.state.antagonist.tank.equipment_preset, Duel.state.antagonist.tank.equipment_preset,
equipment equipment,
); );
const equalize =
(Duel.state.equalize
? Duel.state.antagonist.tank.equalizer?.penetration
: undefined) ?? 1;
material.uniforms.penetration.value = material.uniforms.penetration.value =
penetration * penetration *
resolvePenetrationCoefficient(hasCalibratedShells, shell.type); resolvePenetrationCoefficient(
hasCalibratedShells,
Duel.state.equalize,
shell.type,
Duel.state.antagonist.tank.equalizer,
) *
equalize;
} }
handleShellChange(); handleShellChange();
handleProtagonistEquipmentChange(Duel.state.protagonist.equipmentMatrix); handleProtagonistEquipmentChange();
handleAntagonistEquipmentChange(Duel.state.antagonist.equipmentMatrix); handleAntagonistEquipmentChange();
const unsubscribes = [ const unsubscribes = [
Duel.on((state) => state.antagonist.shell, handleShellChange), Duel.on((state) => state.antagonist.shell, handleShellChange),
Tankopedia.on((state) => state.customShell, handleShellChange), Tankopedia.on((state) => state.customShell, handleShellChange),
Duel.on( Duel.on(
(state) => state.protagonist.equipmentMatrix, (state) => state.protagonist.equipmentMatrix,
handleProtagonistEquipmentChange handleProtagonistEquipmentChange,
), ),
Duel.on( Duel.on(
(state) => state.antagonist.equipmentMatrix, (state) => state.antagonist.equipmentMatrix,
handleAntagonistEquipmentChange handleAntagonistEquipmentChange,
),
Duel.on(
(state) => state.equalize,
() => {
handleProtagonistEquipmentChange();
handleAntagonistEquipmentChange();
},
), ),
]; ];
@@ -18,6 +18,7 @@ import {
import { degToRad } from "three/src/math/MathUtils.js"; import { degToRad } from "three/src/math/MathUtils.js";
import { hasEquipment } from "../../../../core/blitzkit/hasEquipment"; import { hasEquipment } from "../../../../core/blitzkit/hasEquipment";
import { jsxTree } from "../../../../core/blitzkit/jsxTree"; import { jsxTree } from "../../../../core/blitzkit/jsxTree";
import { defaultEqualizer } from "../../../../core/blitzkit/tankToDuelMember";
import { discardClippingPlane } from "../../../../core/three/discardClippingPlane"; import { discardClippingPlane } from "../../../../core/three/discardClippingPlane";
import { Duel } from "../../../../stores/duel"; import { Duel } from "../../../../stores/duel";
import { import {
@@ -83,16 +84,25 @@ export function SpacedArmorSceneComponent({
point: Vector3, point: Vector3,
intersections: Intersection[], intersections: Intersection[],
allowRicochet: boolean, allowRicochet: boolean,
remainingPenetrationInput?: number remainingPenetrationInput?: number,
) => { ) => {
const { customShell } = Tankopedia.state; const { customShell } = Tankopedia.state;
const shell = customShell ?? Duel.state.antagonist.shell; const shell = customShell ?? Duel.state.antagonist.shell;
const { equalize } = Duel.state;
const protagonistEqualizer =
(equalize ? Duel.state.protagonist.tank.equalizer : undefined) ??
defaultEqualizer;
const antagonistEqualizer =
(equalize ? Duel.state.antagonist.tank.equalizer : undefined) ??
defaultEqualizer;
const cameraNormal = camera.position.clone().sub(point).normalize(); const cameraNormal = camera.position.clone().sub(point).normalize();
const shot: Shot = { const shot: Shot = {
splashRadius: splashRadius:
shell.type === ShellType.HE ? shell.explosion_radius : undefined, shell.type === ShellType.SHELL_TYPE_HE
? shell.explosion_radius
: undefined,
damage: -1, damage: -1,
containsGaps: shell.type === ShellType.HEAT, containsGaps: shell.type === ShellType.SHELL_TYPE_HEAT,
in: { in: {
surfaceNormal: cameraNormal, surfaceNormal: cameraNormal,
layers: [], layers: [],
@@ -103,21 +113,27 @@ export function SpacedArmorSceneComponent({
const hasCalibratedShells = hasEquipment( const hasCalibratedShells = hasEquipment(
103, 103,
Duel.state.antagonist.tank.equipment_preset, Duel.state.antagonist.tank.equipment_preset,
Duel.state.antagonist.equipmentMatrix Duel.state.antagonist.equipmentMatrix,
); );
const hasEnhancedArmor = hasEquipment( const hasEnhancedArmor = hasEquipment(
110, 110,
Duel.state.protagonist.tank.equipment_preset, Duel.state.protagonist.tank.equipment_preset,
Duel.state.protagonist.equipmentMatrix Duel.state.protagonist.equipmentMatrix,
); );
const penetration = const penetration =
shell.penetration.near * shell.penetration!.near *
resolvePenetrationCoefficient(hasCalibratedShells, shell.type); resolvePenetrationCoefficient(
const thicknessCoefficient = hasEnhancedArmor ? 1.03 : 1; hasCalibratedShells,
equalize,
shell.type,
antagonistEqualizer,
);
const thicknessCoefficient =
(hasEnhancedArmor ? 1.03 : 1) * protagonistEqualizer.armor;
const filteredIntersections = intersections.filter( const filteredIntersections = intersections.filter(
(intersection) => (intersection) =>
"type" in intersection.object.userData && "type" in intersection.object.userData &&
!discardClippingPlane(intersection.object, intersection.point) !discardClippingPlane(intersection.object, intersection.point),
) as unknown as Intersection<Object3D & { userData: ArmorUserData }>[]; ) as unknown as Intersection<Object3D & { userData: ArmorUserData }>[];
const encounteredExternalModuleVariants: ExternalModuleVariant[] = []; const encounteredExternalModuleVariants: ExternalModuleVariant[] = [];
const noDuplicateIntersections: typeof filteredIntersections = []; const noDuplicateIntersections: typeof filteredIntersections = [];
@@ -126,12 +142,12 @@ export function SpacedArmorSceneComponent({
if (intersection.object.userData.type === ArmorType.External) { if (intersection.object.userData.type === ArmorType.External) {
if ( if (
!encounteredExternalModuleVariants.includes( !encounteredExternalModuleVariants.includes(
intersection.object.userData.variant intersection.object.userData.variant,
) )
) { ) {
noDuplicateIntersections.push(intersection); noDuplicateIntersections.push(intersection);
encounteredExternalModuleVariants.push( encounteredExternalModuleVariants.push(
intersection.object.userData.variant intersection.object.userData.variant,
); );
} }
} else { } else {
@@ -159,10 +175,10 @@ export function SpacedArmorSceneComponent({
const surfaceNormal = intersection const surfaceNormal = intersection
.normal!.clone() .normal!.clone()
.applyQuaternion( .applyQuaternion(
intersection.object.getWorldQuaternion(new Quaternion()) intersection.object.getWorldQuaternion(new Quaternion()),
); );
if (shell.type === ShellType.HEAT && index > 0) { if (shell.type === ShellType.SHELL_TYPE_HEAT && index > 0) {
const lastLayer = shot.in.layers.at(-1) as ShotLayerBase; const lastLayer = shot.in.layers.at(-1) as ShotLayerBase;
const distance = lastLayer.point.distanceTo(intersection.point); const distance = lastLayer.point.distanceTo(intersection.point);
remainingPenetration -= 0.5 * remainingPenetration * distance; remainingPenetration -= 0.5 * remainingPenetration * distance;
@@ -181,7 +197,7 @@ export function SpacedArmorSceneComponent({
const thickness = layer.thickness * thicknessCoefficient; const thickness = layer.thickness * thicknessCoefficient;
remainingPenetration -= thickness; remainingPenetration -= thickness;
const blocked = const blocked =
remainingPenetration <= 0 || shell.type === ShellType.HE; remainingPenetration <= 0 || shell.type === ShellType.SHELL_TYPE_HE;
shot.in.layers.push({ shot.in.layers.push({
type: ArmorType.External, type: ArmorType.External,
@@ -190,7 +206,7 @@ export function SpacedArmorSceneComponent({
point: intersection.point, point: intersection.point,
surfaceNormal, surfaceNormal,
status: status:
shell.type === ShellType.HE shell.type === ShellType.SHELL_TYPE_HE
? "blocked" ? "blocked"
: blocked : blocked
? "blocked" ? "blocked"
@@ -200,7 +216,7 @@ export function SpacedArmorSceneComponent({
} else { } else {
const thickness = layer.thickness * thicknessCoefficient; const thickness = layer.thickness * thicknessCoefficient;
const ricochet = degToRad( const ricochet = degToRad(
isExplosive(shell.type) ? 90 : shell.ricochet! isExplosive(shell.type) ? 90 : shell.ricochet!,
); );
const angle = surfaceNormal.angleTo(cameraNormal); const angle = surfaceNormal.angleTo(cameraNormal);
@@ -241,7 +257,8 @@ export function SpacedArmorSceneComponent({
point: intersection.point, point: intersection.point,
surfaceNormal: intersection.face!.normal, surfaceNormal: intersection.face!.normal,
status: status:
shell.type === ShellType.HE && layer.type !== ArmorType.Primary shell.type === ShellType.SHELL_TYPE_HE &&
layer.type !== ArmorType.Primary
? "blocked" ? "blocked"
: blocked : blocked
? "blocked" ? "blocked"
@@ -251,7 +268,10 @@ export function SpacedArmorSceneComponent({
} }
} }
if (shell.type !== ShellType.HE && remainingPenetration <= 0) { if (
shell.type !== ShellType.SHELL_TYPE_HE &&
remainingPenetration <= 0
) {
break; break;
} }
@@ -266,7 +286,7 @@ export function SpacedArmorSceneComponent({
if (!lastLayer || !firstLayer) { if (!lastLayer || !firstLayer) {
return null; return null;
} else { } else {
if (shell.type === ShellType.HE) { if (shell.type === ShellType.SHELL_TYPE_HE) {
const totalSpacedArmorThickness = shot.in.layers.reduce( const totalSpacedArmorThickness = shot.in.layers.reduce(
(acc, layer) => { (acc, layer) => {
if (layer.type === null || layer.type === ArmorType.Primary) { if (layer.type === null || layer.type === ArmorType.Primary) {
@@ -279,10 +299,10 @@ export function SpacedArmorSceneComponent({
return acc + layer.thicknessAngled; return acc + layer.thicknessAngled;
}, },
0 0,
); );
const distanceFromSpacedArmor = firstLayer.point.distanceTo( const distanceFromSpacedArmor = firstLayer.point.distanceTo(
lastLayer.point lastLayer.point,
); );
const finalDamage = Math.max( const finalDamage = Math.max(
0, 0,
@@ -291,7 +311,7 @@ export function SpacedArmorSceneComponent({
(1 - distanceFromSpacedArmor / shell.explosion_radius!) - (1 - distanceFromSpacedArmor / shell.explosion_radius!) -
1.1 * 1.1 *
(lastLayer.thicknessAngled + (lastLayer.thicknessAngled +
Math.min(penetration, totalSpacedArmorThickness)) Math.min(penetration, totalSpacedArmorThickness)),
); );
/** /**
@@ -328,20 +348,20 @@ export function SpacedArmorSceneComponent({
.sub( .sub(
lastLayer.surfaceNormal lastLayer.surfaceNormal
.clone() .clone()
.multiplyScalar(2 * shellNormal.dot(lastLayer.surfaceNormal)) .multiplyScalar(2 * shellNormal.dot(lastLayer.surfaceNormal)),
); );
caster.set(lastLayer.point, ricochetNormal); caster.set(lastLayer.point, ricochetNormal);
const ricochetIntersections = caster.intersectObjects( const ricochetIntersections = caster.intersectObjects(
props.scene.children, props.scene.children,
true true,
); );
const outShot = shoot( const outShot = shoot(
lastLayer.point, lastLayer.point,
ricochetIntersections, ricochetIntersections,
false, false,
remainingPenetration remainingPenetration,
); );
shot.in.status = "ricochet"; shot.in.status = "ricochet";
@@ -361,9 +381,11 @@ export function SpacedArmorSceneComponent({
} }
} }
shot.damage *= antagonistEqualizer.damage;
return shot; return shot;
}, },
[camera] [camera],
); );
return ( return (
@@ -43,9 +43,9 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
const trackModelDefinition = tankModelDefinition.tracks[track.id]; const trackModelDefinition = tankModelDefinition.tracks[track.id];
const turretModelDefinition = tankModelDefinition.turrets[turret.id]; const turretModelDefinition = tankModelDefinition.turrets[turret.id];
const gunModelDefinition = turretModelDefinition.guns[gun.id]; const gunModelDefinition = turretModelDefinition.guns[gun.id];
const hullOrigin = correctZYTuple(trackModelDefinition.origin); const hullOrigin = correctZYTuple(trackModelDefinition.origin!);
const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin); const turretOrigin = correctZYTuple(tankModelDefinition.turret_origin!);
const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin); const gunOrigin = correctZYTuple(turretModelDefinition.gun_origin!);
const canvas = useThree((state) => state.gl.domElement); const canvas = useThree((state) => state.gl.domElement);
const maskOrigin = const maskOrigin =
@@ -53,16 +53,16 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
? undefined ? undefined
: gunModelDefinition.mask + hullOrigin.y + turretOrigin.y + gunOrigin.y; : gunModelDefinition.mask + hullOrigin.y + turretOrigin.y + gunOrigin.y;
const showPrimaryArmor = TankopediaPersistent.use( const showPrimaryArmor = TankopediaPersistent.use(
(state) => state.showPrimaryArmor (state) => state.showPrimaryArmor,
); );
const showSpacedArmor = TankopediaPersistent.use( const showSpacedArmor = TankopediaPersistent.use(
(state) => state.showSpacedArmor (state) => state.showSpacedArmor,
); );
const showExternalModules = TankopediaPersistent.use( const showExternalModules = TankopediaPersistent.use(
(state) => state.showExternalModules (state) => state.showExternalModules,
); );
const isDynamicArmorActive = Duel.use((state) => const isDynamicArmorActive = Duel.use((state) =>
state.protagonist.consumables.includes(73) state.protagonist.consumables.includes(73),
); );
useTankTransform(track, turret, turretContainer, gunContainer); useTankTransform(track, turret, turretContainer, gunContainer);
@@ -75,8 +75,8 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
const isVisible = isHull; const isVisible = isHull;
const armorId = nameToArmorId(node.name); const armorId = nameToArmorId(node.name);
const { spaced, thickness } = resolveArmor( const { spaced, thickness } = resolveArmor(
tankModelDefinition.armor, tankModelDefinition.armor!,
armorId armorId,
); );
if ( if (
@@ -127,13 +127,13 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
<group ref={turretContainer}> <group ref={turretContainer}>
{armorNodes.map((node) => { {armorNodes.map((node) => {
const isCurrentTurret = node.name.startsWith( const isCurrentTurret = node.name.startsWith(
`turret_${turretModelDefinition.model_id.toString().padStart(2, "0")}` `turret_${turretModelDefinition.model_id.toString().padStart(2, "0")}`,
); );
const isVisible = isCurrentTurret; const isVisible = isCurrentTurret;
const armorId = nameToArmorId(node.name); const armorId = nameToArmorId(node.name);
const { spaced, thickness } = resolveArmor( const { spaced, thickness } = resolveArmor(
turretModelDefinition.armor, turretModelDefinition.armor!,
armorId armorId,
); );
if ( if (
@@ -168,12 +168,12 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
const hasImprovedVerticalStabilizer = hasEquipment( const hasImprovedVerticalStabilizer = hasEquipment(
122, 122,
Duel.state.protagonist.tank.equipment_preset, Duel.state.protagonist.tank.equipment_preset,
Duel.state.protagonist.equipmentMatrix Duel.state.protagonist.equipmentMatrix,
); );
const hasDownImprovedVerticalStabilizer = hasEquipment( const hasDownImprovedVerticalStabilizer = hasEquipment(
124, 124,
Duel.state.protagonist.tank.equipment_preset, Duel.state.protagonist.tank.equipment_preset,
Duel.state.protagonist.equipmentMatrix Duel.state.protagonist.equipmentMatrix,
); );
const boundingRect = canvas.getBoundingClientRect(); const boundingRect = canvas.getBoundingClientRect();
@@ -184,10 +184,10 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
modelTransformEvent.last!.pitch, modelTransformEvent.last!.pitch,
modelTransformEvent.last!.yaw + modelTransformEvent.last!.yaw +
delta.x * (Math.PI / boundingRect.width), delta.x * (Math.PI / boundingRect.width),
gunModelDefinition.pitch, gunModelDefinition.pitch!,
turretModelDefinition.yaw, turretModelDefinition.yaw,
hasImprovedVerticalStabilizer, hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer hasDownImprovedVerticalStabilizer,
); );
modelTransformEvent.dispatch({ pitch, yaw }); modelTransformEvent.dispatch({ pitch, yaw });
} }
@@ -216,13 +216,13 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
<group ref={gunContainer}> <group ref={gunContainer}>
{armorNodes.map((node) => { {armorNodes.map((node) => {
const isCurrentGun = node.name.startsWith( const isCurrentGun = node.name.startsWith(
`gun_${gunModelDefinition.model_id.toString().padStart(2, "0")}` `gun_${gunModelDefinition.model_id.toString().padStart(2, "0")}`,
); );
const isVisible = isCurrentGun; const isVisible = isCurrentGun;
const armorId = nameToArmorId(node.name); const armorId = nameToArmorId(node.name);
const { spaced, thickness } = resolveArmor( const { spaced, thickness } = resolveArmor(
gunModelDefinition.armor, gunModelDefinition.armor!,
armorId armorId,
); );
if ( if (
@@ -256,12 +256,12 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
const hasImprovedVerticalStabilizer = hasEquipment( const hasImprovedVerticalStabilizer = hasEquipment(
122, 122,
Duel.state.protagonist.tank.equipment_preset, Duel.state.protagonist.tank.equipment_preset,
Duel.state.protagonist.equipmentMatrix Duel.state.protagonist.equipmentMatrix,
); );
const hasDownImprovedVerticalStabilizer = hasEquipment( const hasDownImprovedVerticalStabilizer = hasEquipment(
124, 124,
Duel.state.protagonist.tank.equipment_preset, Duel.state.protagonist.tank.equipment_preset,
Duel.state.protagonist.equipmentMatrix Duel.state.protagonist.equipmentMatrix,
); );
const boundingRect = canvas.getBoundingClientRect(); const boundingRect = canvas.getBoundingClientRect();
delta.set(event.clientX, event.clientY).sub(position); delta.set(event.clientX, event.clientY).sub(position);
@@ -272,10 +272,10 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
delta.y * (Math.PI / boundingRect.height), delta.y * (Math.PI / boundingRect.height),
modelTransformEvent.last!.yaw + modelTransformEvent.last!.yaw +
delta.x * (Math.PI / boundingRect.width), delta.x * (Math.PI / boundingRect.width),
gunModelDefinition.pitch, gunModelDefinition.pitch!,
turretModelDefinition.yaw, turretModelDefinition.yaw,
hasImprovedVerticalStabilizer, hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer hasDownImprovedVerticalStabilizer,
); );
modelTransformEvent.dispatch({ pitch, yaw }); modelTransformEvent.dispatch({ pitch, yaw });
} }
@@ -332,12 +332,12 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
const hasImprovedVerticalStabilizer = hasEquipment( const hasImprovedVerticalStabilizer = hasEquipment(
122, 122,
Duel.state.protagonist.tank.equipment_preset, Duel.state.protagonist.tank.equipment_preset,
Duel.state.protagonist.equipmentMatrix Duel.state.protagonist.equipmentMatrix,
); );
const hasDownImprovedVerticalStabilizer = hasEquipment( const hasDownImprovedVerticalStabilizer = hasEquipment(
124, 124,
Duel.state.protagonist.tank.equipment_preset, Duel.state.protagonist.tank.equipment_preset,
Duel.state.protagonist.equipmentMatrix Duel.state.protagonist.equipmentMatrix,
); );
const boundingRect = canvas.getBoundingClientRect(); const boundingRect = canvas.getBoundingClientRect();
delta.set(event.clientX, event.clientY).sub(position); delta.set(event.clientX, event.clientY).sub(position);
@@ -348,10 +348,10 @@ export const StaticArmor = memo<ArmorSceneProps>(({ thicknessRange }) => {
delta.y * (Math.PI / boundingRect.height), delta.y * (Math.PI / boundingRect.height),
modelTransformEvent.last!.yaw + modelTransformEvent.last!.yaw +
delta.x * (Math.PI / boundingRect.width), delta.x * (Math.PI / boundingRect.width),
gunModelDefinition.pitch, gunModelDefinition.pitch!,
turretModelDefinition.yaw, turretModelDefinition.yaw,
hasImprovedVerticalStabilizer, hasImprovedVerticalStabilizer,
hasDownImprovedVerticalStabilizer hasDownImprovedVerticalStabilizer,
); );
modelTransformEvent.dispatch({ pitch, yaw }); modelTransformEvent.dispatch({ pitch, yaw });
} }
@@ -22,7 +22,10 @@ import {
modelTransformEvent, modelTransformEvent,
type ModelTransformEventData, type ModelTransformEventData,
} from "../../../core/blitzkit/modelTransform"; } from "../../../core/blitzkit/modelTransform";
import { defaultEqualizer } from "../../../core/blitzkit/tankToDuelMember";
import { discardClippingPlane } from "../../../core/three/discardClippingPlane"; import { discardClippingPlane } from "../../../core/three/discardClippingPlane";
import { useEquipment } from "../../../hooks/useEquipment";
import { Duel } from "../../../stores/duel";
import { Tankopedia } from "../../../stores/tankopedia"; import { Tankopedia } from "../../../stores/tankopedia";
import { transitionEvent } from "../../Tankopedia/HeroSection/components/TankSandbox/components/Lighting"; import { transitionEvent } from "../../Tankopedia/HeroSection/components/TankSandbox/components/Lighting";
import { ArmorType } from "./SpacedArmorScene"; import { ArmorType } from "./SpacedArmorScene";
@@ -64,46 +67,68 @@ const externalModuleColor = new Color(192 / 255, 192 / 255, 192 / 255);
export function StaticArmorSceneComponent({ export function StaticArmorSceneComponent({
name, name,
thickness, thickness: thicknessRaw,
thicknessRange, thicknessRange,
node, node,
onPointerDown, onPointerDown,
...props ...props
}: StaticArmorSceneComponentProps) { }: StaticArmorSceneComponentProps) {
const camera = useThree((state) => state.camera); const camera = useThree((state) => state.camera);
const hasEnhancedArmor = useEquipment(110);
const tank = Duel.use((state) => state.protagonist.tank);
const equalize = Duel.use((state) => state.equalize);
const equalizer = (equalize ? tank.equalizer : undefined) ?? defaultEqualizer;
const thicknessCoefficient = (hasEnhancedArmor ? 1.03 : 1) * equalizer.armor;
const thickness = thicknessRaw * thicknessCoefficient;
const x = thickness / thicknessRange.value; const x = thickness / thicknessRange.value;
const xClamped = clamp(x, 0, 1); const xClamped = clamp(x, 0, 1);
let color: Color; const color = useMemo(() => {
let opacity: number; switch (props.type) {
let depthWrite = true; case ArmorType.Primary: {
if (x > 1) {
switch (props.type) { return new Color(clamp(2 - x, 0.5, 1), 0, 0);
case ArmorType.Primary: } else {
if (x > 1) { return new Color(-((1 - x) ** 2) + 1, -(x ** 2) + 1, 0);
color = new Color(clamp(2 - x, 0.5, 1), 0, 0); }
} else {
color = new Color(-((1 - x) ** 2) + 1, -(x ** 2) + 1, 0);
} }
opacity = 1;
break;
case ArmorType.Spaced: case ArmorType.Spaced: {
color = new Color( return new Color(
clamp(1 - (7 / 8) * xClamped, 0, 1), clamp(1 - (7 / 8) * xClamped, 0, 1),
0, 0,
clamp(1 - (1 / 8) * xClamped, 0, 1), clamp(1 - (1 / 8) * xClamped, 0, 1),
); );
opacity = clamp(x + 1 / 2, 0, 1); }
break;
case ArmorType.External: case ArmorType.External: {
color = externalModuleColor; return externalModuleColor;
opacity = 1 / 8; }
depthWrite = false; }
break; }, [props.type, xClamped]);
} const opacity = useMemo(() => {
switch (props.type) {
case ArmorType.Primary:
return 1;
opacity = clamp(opacity, 0, 1); case ArmorType.Spaced:
return clamp(x + 1 / 2, 0, 1);
case ArmorType.External:
return 1 / 8;
}
}, [props.type, x]);
const depthWrite = useMemo(() => {
switch (props.type) {
case ArmorType.Primary:
return true;
case ArmorType.Spaced:
return true;
case ArmorType.External:
return false;
}
}, [props.type]);
const surfaceMaterial = useMemo( const surfaceMaterial = useMemo(
() => () =>
@@ -115,7 +140,7 @@ export function StaticArmorSceneComponent({
opacity: 0, opacity: 0,
userData: { opacity0: opacity }, userData: { opacity0: opacity },
}), }),
[thickness], [hasEnhancedArmor, equalize],
); );
const outlineMaterial = useMemo( const outlineMaterial = useMemo(
() => () =>
@@ -126,7 +151,7 @@ export function StaticArmorSceneComponent({
opacity: 0, opacity: 0,
transparent: true, transparent: true,
}), }),
[thickness], [hasEnhancedArmor, equalize],
); );
useEffect(() => { useEffect(() => {
@@ -229,7 +254,7 @@ export function StaticArmorSceneComponent({
return () => { return () => {
unsubscribes.forEach((unsubscribe) => unsubscribe()); unsubscribes.forEach((unsubscribe) => unsubscribe());
}; };
}, [thickness]); }, [hasEnhancedArmor, equalize]);
return ( return (
<> <>
@@ -1,16 +1,16 @@
import { TankClass } from '@blitzkit/core'; import { TankClass } from "@blitzkit/core";
import type { ComponentProps, ReactNode } from 'react'; import type { ComponentProps, ReactNode } from "react";
import { ClassHeavy } from './components/ClassHeavy'; import { ClassHeavy } from "./components/ClassHeavy";
import { ClassLight } from './components/ClassLight'; import { ClassLight } from "./components/ClassLight";
import { ClassMedium } from './components/ClassMedium'; import { ClassMedium } from "./components/ClassMedium";
import { ClassTankDestroyer } from './components/ClassTankDestroyer'; import { ClassTankDestroyer } from "./components/ClassTankDestroyer";
export const classIcons: Record< export const classIcons: Record<
TankClass, TankClass,
(props: ComponentProps<'svg'>) => ReactNode (props: ComponentProps<"svg">) => ReactNode
> = { > = {
[TankClass.TANK_DESTROYER]: ClassTankDestroyer, [TankClass.TANK_CLASS_TANK_DESTROYER]: ClassTankDestroyer,
[TankClass.HEAVY]: ClassHeavy, [TankClass.TANK_CLASS_HEAVY]: ClassHeavy,
[TankClass.LIGHT]: ClassLight, [TankClass.TANK_CLASS_LIGHT]: ClassLight,
[TankClass.MEDIUM]: ClassMedium, [TankClass.TANK_CLASS_MEDIUM]: ClassMedium,
}; };
@@ -15,7 +15,7 @@ interface BodyProps {
export function Body({ stats }: BodyProps) { export function Body({ stats }: BodyProps) {
const members = CompareEphemeral.use((state) => state.members); const members = CompareEphemeral.use((state) => state.members);
const hasNonRegularGun = members.some( const hasNonRegularGun = members.some(
({ gun }) => gun.gun_type!.$case !== "regular" ({ gun }) => gun.gun_type!.$case !== "regular",
); );
const { unwrap, strings } = useLocale(); const { unwrap, strings } = useLocale();
@@ -55,7 +55,7 @@ export function Body({ stats }: BodyProps) {
}} }}
> >
<img <img
alt={unwrap(thisShell.name)} alt={unwrap(thisShell.name!)}
width={16} width={16}
height={16} height={16}
src={asset(`icons/shells/${thisShell.icon}.webp`)} src={asset(`icons/shells/${thisShell.icon}.webp`)}
@@ -14,7 +14,7 @@ import { StickyRowHeaderCell } from "../StickyRowHeaderCell";
type CompareRowProps = { type CompareRowProps = {
display?: ( display?: (
member: Awaited<TankCharacteristics> member: Awaited<TankCharacteristics>,
) => number | string | undefined; ) => number | string | undefined;
deltaType?: CompareCellDirection; deltaType?: CompareCellDirection;
decimals?: number; decimals?: number;
@@ -46,7 +46,7 @@ export function CompareRow({
const values = stats.map((stat) => const values = stats.map((stat) =>
typeof props.value === "function" typeof props.value === "function"
? props.value(stat)! ? props.value(stat)!
: (stat[props.value] as number) : (stat[props.value] as number),
); );
const id = useRef(Math.random()); const id = useRef(Math.random());
const name = const name =
@@ -56,10 +56,10 @@ export function CompareRow({
return ( return (
<Table.Row> <Table.Row>
<StickyRowHeaderCell> <StickyRowHeaderCell left="-1.25rem">
<Flex <Flex
align="center" align="center"
style={{ whiteSpace: "nowrap" }} maxWidth={{ initial: "6rem", sm: "unset" }}
gap={indent ? { initial: "4", sm: "6" } : { initial: "1", sm: "2" }} gap={indent ? { initial: "4", sm: "6" } : { initial: "1", sm: "2" }}
> >
<IconButton <IconButton
@@ -111,8 +111,8 @@ export function CompareRow({
const resolvedDisplayValue = display const resolvedDisplayValue = display
? display(stats[index]) ? display(stats[index])
: decimals === undefined : decimals === undefined
? value ? value
: value?.toFixed(decimals); : value?.toFixed(decimals);
return ( return (
<CompareCell <CompareCell
@@ -145,8 +145,8 @@ export function CompareRow({
deltaNominalDisplay deltaNominalDisplay
? deltaNominalDisplay(delta) ? deltaNominalDisplay(delta)
: decimals === undefined : decimals === undefined
? delta ? delta
: delta.toFixed(decimals) : delta.toFixed(decimals)
}`} }`}
) )
</Text> </Text>
@@ -156,7 +156,7 @@ export function CompareRow({
<Text color="gray"> <Text color="gray">
( (
{`${deltaPercentage > 0 ? "+" : ""}${Math.round( {`${deltaPercentage > 0 ? "+" : ""}${Math.round(
deltaPercentage * 100 deltaPercentage * 100,
)}%`} )}%`}
) )
</Text> </Text>
@@ -1,7 +1,7 @@
import { Heading, Table } from '@radix-ui/themes'; import { Heading, Table } from "@radix-ui/themes";
import { times } from 'lodash-es'; import { times } from "lodash-es";
import { CompareEphemeral } from '../../stores/compareEphemeral'; import { CompareEphemeral } from "../../stores/compareEphemeral";
import { StickyColumnHeaderCell } from '../StickyColumnHeaderCell'; import { StickyColumnHeaderCell } from "../StickyColumnHeaderCell";
export function CompareSectionTitle({ children }: { children: string }) { export function CompareSectionTitle({ children }: { children: string }) {
const memberCount = CompareEphemeral.use((state) => state.members.length); const memberCount = CompareEphemeral.use((state) => state.members.length);
@@ -1,7 +1,15 @@
import { asset, availableProvisions, permanentSkills } from "@blitzkit/core"; import { asset, availableProvisions, permanentSkills } from "@blitzkit/core";
import { checkConsumableProvisionInclusivity } from "@blitzkit/core/src/blitzkit/checkConsumableProvisionInclusivity"; import { checkConsumableProvisionInclusivity } from "@blitzkit/core/src/blitzkit/checkConsumableProvisionInclusivity";
import { ComponentPlaceholderIcon } from "@radix-ui/react-icons"; import { ComponentPlaceholderIcon } from "@radix-ui/react-icons";
import { Button, Flex, IconButton, Popover, Table } from "@radix-ui/themes"; import {
Button,
Flex,
IconButton,
Popover,
Switch,
Table,
Text,
} from "@radix-ui/themes";
import { times } from "lodash-es"; import { times } from "lodash-es";
import { awaitableConsumableDefinitions } from "../../core/awaitables/consumableDefinitions"; import { awaitableConsumableDefinitions } from "../../core/awaitables/consumableDefinitions";
import { awaitableEquipmentDefinitions } from "../../core/awaitables/equipmentDefinitions"; import { awaitableEquipmentDefinitions } from "../../core/awaitables/equipmentDefinitions";
@@ -44,6 +52,7 @@ export function CompareTable({ stats }: CompareTableProps) {
const crewSkills = CompareEphemeral.use((state) => state.crewSkills); const crewSkills = CompareEphemeral.use((state) => state.crewSkills);
const members = CompareEphemeral.use((state) => state.members); const members = CompareEphemeral.use((state) => state.members);
const { unwrap, strings } = useLocale(); const { unwrap, strings } = useLocale();
const equalize = CompareEphemeral.use((state) => state.equalize);
return ( return (
<StickyTableRoot <StickyTableRoot
@@ -80,10 +89,12 @@ export function CompareTable({ stats }: CompareTableProps) {
<Table.Row> <Table.Row>
<Table.Cell style={{ height: "100%" }}> <Table.Cell style={{ height: "100%" }}>
<Flex <Flex
gap="2"
style={{ style={{
width: "100%", width: "100%",
height: "100%", height: "100%",
}} }}
direction="column"
align="center" align="center"
justify="center" justify="center"
> >
@@ -105,13 +116,13 @@ export function CompareTable({ stats }: CompareTableProps) {
crewSkills[skill] === 0 crewSkills[skill] === 0
? Var("gray-11") ? Var("gray-11")
: permanentSkills.includes(skill) : permanentSkills.includes(skill)
? Var("crimson-11") ? Var("crimson-11")
: Var("amber-11"), : Var("amber-11"),
}} }}
/> />
))} ))}
</Flex> </Flex>
) ),
)} )}
</Flex> </Flex>
</Button> </Button>
@@ -155,6 +166,21 @@ export function CompareTable({ stats }: CompareTableProps) {
</Flex> </Flex>
</Popover.Content> </Popover.Content>
</Popover.Root> </Popover.Root>
<Flex align="center" gap="1">
<Text>
{strings.website.tools.compare.table.skills.equalize}
</Text>
<Switch
size="1"
checked={equalize}
onCheckedChange={(checked) => {
CompareEphemeral.mutate((draft) => {
draft.equalize = checked;
});
}}
/>
</Flex>
</Flex> </Flex>
</Table.Cell> </Table.Cell>
@@ -173,19 +199,19 @@ export function CompareTable({ stats }: CompareTableProps) {
consumables, consumables,
camouflage, camouflage,
}, },
index index,
) => { ) => {
const equipmentPreset = const equipmentPreset =
equipmentDefinitions.presets[tank.equipment_preset]; equipmentDefinitions.presets[tank.equipment_preset];
const provisionsList = availableProvisions( const provisionsList = availableProvisions(
tank, tank,
gun, gun,
provisionDefinitions provisionDefinitions,
); );
const consumablesList = Object.values( const consumablesList = Object.values(
consumableDefinitions.consumables consumableDefinitions.consumables,
).filter((consumable) => ).filter((consumable) =>
checkConsumableProvisionInclusivity(consumable, tank, gun) checkConsumableProvisionInclusivity(consumable, tank, gun),
); );
return ( return (
@@ -294,11 +320,11 @@ export function CompareTable({ stats }: CompareTableProps) {
<img <img
alt={unwrap( alt={unwrap(
provisionDefinitions.provisions[provision] provisionDefinitions.provisions[provision]
.name .name!,
)} )}
key={provision} key={provision}
src={asset( src={asset(
`/icons/provisions/${provision}.webp` `/icons/provisions/${provision}.webp`,
)} )}
style={{ style={{
left: "50%", left: "50%",
@@ -335,7 +361,7 @@ export function CompareTable({ stats }: CompareTableProps) {
<Popover.Content> <Popover.Content>
<ProvisionsManager <ProvisionsManager
provisions={provisionsList.map( provisions={provisionsList.map(
(provision) => provision.id (provision) => provision.id,
)} )}
selected={provisions} selected={provisions}
disabled={tank.max_provisions === provisions.length} disabled={tank.max_provisions === provisions.length}
@@ -452,7 +478,7 @@ export function CompareTable({ stats }: CompareTableProps) {
CompareEphemeral.mutate((draft) => { CompareEphemeral.mutate((draft) => {
draft.members[index].equipmentMatrix = times( draft.members[index].equipmentMatrix = times(
3, 3,
() => times(3, () => 0) () => times(3, () => 0),
) as EquipmentMatrix; ) as EquipmentMatrix;
draft.sorting = undefined; draft.sorting = undefined;
}); });
@@ -501,11 +527,11 @@ export function CompareTable({ stats }: CompareTableProps) {
alt={unwrap( alt={unwrap(
consumableDefinitions.consumables[ consumableDefinitions.consumables[
consumable consumable
].name ].name!,
)} )}
key={consumable} key={consumable}
src={asset( src={asset(
`/icons/consumables/${consumable}.webp` `/icons/consumables/${consumable}.webp`,
)} )}
style={{ style={{
left: "50%", left: "50%",
@@ -610,7 +636,7 @@ export function CompareTable({ stats }: CompareTableProps) {
</Flex> </Flex>
</Table.Cell> </Table.Cell>
); );
} },
)} )}
</Table.Row> </Table.Row>
</Table.Body> </Table.Body>
@@ -55,7 +55,7 @@ export function TankCard({ index, tank }: TankCardProps) {
}; };
}); });
const closest = distances.reduce((a, b) => const closest = distances.reduce((a, b) =>
a.distance < b.distance ? a : b a.distance < b.distance ? a : b,
); );
dropIndex = closest.index; dropIndex = closest.index;
@@ -85,8 +85,8 @@ export function TankCard({ index, tank }: TankCardProps) {
CompareEphemeral.mutate((draft) => { CompareEphemeral.mutate((draft) => {
const fromIndex = index; const fromIndex = index;
const toIndex = dropIndex > index ? dropIndex - 1 : dropIndex; const toIndex = dropIndex > index ? dropIndex - 1 : dropIndex;
const member = draft.members[fromIndex]; const member = draft.members[fromIndex];
draft.members.splice(fromIndex, 1); draft.members.splice(fromIndex, 1);
draft.members.splice(toIndex, 0, member); draft.members.splice(toIndex, 0, member);
}); });
@@ -99,7 +99,7 @@ export function TankCard({ index, tank }: TankCardProps) {
return () => { return () => {
draggable.current?.removeEventListener("pointerdown", handlePointerDown); draggable.current?.removeEventListener("pointerdown", handlePointerDown);
}; };
}, []); }, [index]);
return ( return (
<StickyColumnHeaderCell width="0"> <StickyColumnHeaderCell width="0">
@@ -119,7 +119,7 @@ export function TankCard({ index, tank }: TankCardProps) {
}} }}
> >
<img <img
alt={unwrap(tank.name)} alt={unwrap(tank.name!)}
src={tankIcon(tank.id)} src={tankIcon(tank.id)}
width={64} width={64}
height={64} height={64}
@@ -137,11 +137,10 @@ export function TankCard({ index, tank }: TankCardProps) {
textOverflow: "ellipsis", textOverflow: "ellipsis",
}} }}
> >
{unwrap(tank.name)} {unwrap(tank.name!)}
</Text> </Text>
</Flex> </Flex>
</Flex> </Flex>
<InsertionMarker index={index + 1} /> <InsertionMarker index={index + 1} />
</StickyColumnHeaderCell> </StickyColumnHeaderCell>
); );
Loaded 100 of 164 files, more files were not shown because too many files have changed in this diff. Show more