Compare commits

...
15 Commits
15 changed files with 258 additions and 188 deletions

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
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "blitzkit",
"version": "1.29.15",
"version": "1.29.16",
"license": "BlitzKit License",
"private": true,
"scripts": {
+36 -36
View File
@@ -1,7 +1,7 @@
{
"test": {
"default": "",
"long": ".",
"default": "test",
"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."
},
"common": {
@@ -17,9 +17,9 @@
"plus": "+{value}"
},
"gun_types": {
"auto_loader": "Auto loader",
"auto_loader": "Samo ładowacz",
"auto_reloader": "Automatyczne przeładowanie",
"regular": ""
"regular": "Regular"
},
"shells": {
"ap": "AP",
@@ -28,18 +28,18 @@
"he": "OB"
},
"player_count_period": {
"total": "Total",
"d_120": "Past 120 days",
"d_90": "Past 90 days",
"d_60": "Past 60 days",
"d_30": "Past month",
"total": "Łącznie",
"d_120": "Powyżej 120 dni",
"d_90": "Powyżej 90 dni",
"d_60": "Powyżej 60 dni",
"d_30": "W ciągu miesiąca",
"d_7": "W minionym tygodniu",
"d_1": "Yesterday"
"d_1": "Wczoraj"
},
"branches": {
"local": "dev",
"dev": "beta",
"opentest": "opentest",
"opentest": "otwarty test",
"preview": "podgląd"
},
"locales": {
@@ -80,45 +80,45 @@
},
"nations": {
"ussr": "ZSRR",
"germany": "Germany",
"usa": "U.S.A.",
"germany": "Niemcy",
"usa": "Stany Zjednoczone.",
"china": "Chiny",
"uk": "Wielka Brytania",
"france": "France",
"japan": "Japan",
"france": "Francja",
"japan": "Japonia",
"european": "Nacja Europejska",
"other": "Nacja Hybrydowa"
},
"nations_adjectives": {
"ussr": "ZSRR",
"germany": "German",
"germany": "Niemcy",
"usa": "Amerykański",
"china": "Chiński",
"uk": "Wielkiej Brytanii",
"france": "Francuski",
"japan": "Japoński",
"european": "European",
"other": "Hybrid"
"european": "Europa",
"other": "Hybryda"
},
"tank_class_short": {
"-1": "unrecognized",
"0": "Light",
"1": "Medium",
"2": "Heavy",
"0": "Lekki",
"1": "Średni",
"2": "Ciężki",
"3": "TD"
},
"tank_class_short_plural_lowercase": {
"0": "lights",
"1": "mediums",
"2": "heavies",
"0": "lekkie",
"1": "średnie",
"2": "ciężkie",
"3": "TDs"
},
"tank_class_medium": {
"-1": "unrecognized",
"0": "Lekki",
"1": "Medium",
"2": "Heavy",
"3": "Tank destroyer"
"1": "Średni",
"2": "Ciężki",
"3": "Niszczyciel czołgów"
},
"tree_type": {
"-1": "unrecognized",
@@ -210,7 +210,7 @@
"good": "Dobrze",
"very_good": "Bardzo dobrze",
"great": "Świetne",
"unicum": "Unikalny ",
"unicum": "Unikalny",
"super_unicum": "Bardzo unikalny"
}
},
@@ -236,7 +236,7 @@
"description": "Kariera"
},
"custom": {
"name": "Niestandardowy",
"name": "custom",
"description": "Zakres niestandardowy",
"options": {
"start": {
@@ -856,7 +856,7 @@
"hull": "Hull",
"turret": "Turret",
"hard": "Hard",
"medium": "Medium",
"medium": "Średni",
"soft": "Soft",
"effective": "Effective"
},
@@ -1066,19 +1066,19 @@
"speedBackwards": "Backwards",
"hardTerrainCoefficient": "Hard",
"hardTerrainCoefficientRaw": "Hard",
"mediumTerrainCoefficient": "Medium",
"mediumTerrainCoefficientRaw": "Medium",
"mediumTerrainCoefficient": "Średni",
"mediumTerrainCoefficientRaw": "Średni",
"softTerrainCoefficient": "Soft",
"softTerrainCoefficientRaw": "Soft",
"enginePower": "Engine power",
"powerToWeightRatioHardTerrain": "Hard",
"powerToWeightRatioMediumTerrain": "Medium",
"powerToWeightRatioMediumTerrain": "Średni",
"powerToWeightRatioSoftTerrain": "Soft",
"weight": "Weight",
"turretTraverseSpeed": "Turret traverse speed",
"gunTraverseSpeed": "Gun traverse speed",
"hullTraverseHardTerrain": "Hard",
"hullTraverseMediumTerrain": "Medium",
"hullTraverseMediumTerrain": "Średni",
"hullTraverseSoftTerrain": "Soft",
"health": "Health",
"fireChance": "Fire chance",
@@ -1181,7 +1181,7 @@
"table": {
"tanks": {
"header": "Tank",
"total": "Total"
"total": "Łącznie"
},
"stats": {
"winrate": "Śr. wygranych",
@@ -1239,7 +1239,7 @@
},
"table": {
"tank": "Tank",
"total": "Total",
"total": "Łącznie",
"remove": "Remove"
}
},
+43 -43
View File
@@ -86,8 +86,8 @@
"uk": "英国",
"france": "法国",
"japan": "日本",
"european": "欧洲国家",
"other": "混合型国家"
"european": "欧盟 (E系)",
"other": "X系"
},
"nations_adjectives": {
"ussr": "苏联",
@@ -97,34 +97,34 @@
"uk": "英国",
"france": "法国",
"japan": "日本",
"european": "欧洲国家",
"other": "混合型国家"
"european": "欧盟 (E系)",
"other": "X系"
},
"tank_class_short": {
"-1": "未识别",
"0": "轻型坦克",
"1": "中型坦克",
"2": "重型战车",
"3": "驱逐战车"
"3": "坦克歼击车"
},
"tank_class_short_plural_lowercase": {
"0": "轻型战车",
"1": "中型战车",
"2": "重型战车",
"3": "驱逐战车"
"0": "轻型坦克",
"1": "中型坦克",
"2": "重型坦克",
"3": "坦克歼击车"
},
"tank_class_medium": {
"-1": "未识别",
"0": "轻型战车",
"1": "中型战车",
"2": "重型战车",
"3": "驱逐战车"
"0": "轻型坦克",
"1": "中型坦克",
"2": "重型坦克",
"3": "坦克歼击车"
},
"tree_type": {
"-1": "未识别",
"0": "科技树",
"1": "加值战车",
"2": "收藏家战车"
"1": "金币车",
"2": "收藏车"
},
"regions": {
"normal": {
@@ -140,8 +140,8 @@
},
"leagues": {
"diamond": "钻石",
"platinum": "白金",
"gold": "黃金",
"platinum": "铂金",
"gold": "黄金",
"silver": "白银",
"bronze": "青铜"
},
@@ -210,7 +210,7 @@
"good": "好",
"very_good": "非常好",
"great": "很好",
"unicum": "極品",
"unicum": "极品",
"super_unicum": "超强势玩家"
}
},
@@ -256,7 +256,7 @@
"description": "公会名称或标签"
},
"tank": {
"name": "战车",
"name": "坦克",
"description": "要获取战绩的坦克"
},
"username": {
@@ -269,8 +269,8 @@
"description": "国别"
},
"class": {
"name": "战车车型",
"description": "战车车型"
"name": "坦克车型",
"description": "坦克车型"
},
"type": {
"name": "战车类型",
@@ -320,7 +320,7 @@
}
},
"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": {
"name": "回放",
"description": "显示重播结果",
@@ -427,8 +427,8 @@
"description": "玩家数据统计"
},
"tank": {
"name": "战车",
"description": "战车数据统计"
"name": "坦克",
"description": "坦克数据统计"
}
}
},
@@ -564,15 +564,15 @@
},
"today": {
"name": "今日",
"description": "今天玩过的战车和统计资料"
"description": "今天玩过的坦克和统计数据"
},
"stats": {
"name": "统计资料",
"description": "一段时间内的快速统计"
},
"owned_tanks": {
"name": "已拥有的战车",
"description": "显示该阶级中所有您已拥有的战车",
"name": "已拥有的坦克",
"description": "在一个等级中显示所有已拥有的坦克",
"body": {
"title": "# {name} 所拥有的 {tier} 阶坦克\n\n"
}
@@ -585,7 +585,7 @@
"view_channels": "查看频道:需要找到重新整理按钮使用的讯息",
"read_message_history": "读取消息历史: 需要找到消息以刷新",
"attach_files": "附加文件:需要将图像添加到基于图像的命令",
"external_emojis": "外部表情符号:需要添加国家旗帜和战车类别"
"external_emojis": "外部表情:需要添加国家旗帜和坦克等级表情"
}
},
"ping": {
@@ -602,7 +602,7 @@
},
"wotb": {
"name": "战车世界闪击战",
"description": "测试战车世界闪击战的ping值(延迟值)"
"description": "测试坦克世界闪击战的ping值(延迟值)"
}
}
},
@@ -673,7 +673,7 @@
"search_bar_hint": "搜寻战车……",
"recent": "最近查看的",
"select_all": "全选",
"count_singular": "1辆战车",
"count_singular": "1 辆坦克",
"count_plural": "{count} 辆坦克",
"sorting_by": "按 {name} 排序",
"clear": "清除",
@@ -681,7 +681,7 @@
"passive": "{name} (被动)",
"active": "{name} (主动)",
"no_tanks_found": {
"body": "找不到战车。",
"body": "未找到坦克。",
"search_again": "请尝试重新搜索。",
"clear_filters": "请尝试清除筛选。"
},
@@ -695,11 +695,11 @@
"survivability.volume": "体积",
"survivability.length": "长度",
"fire.dpm": "标准炮弹 每分钟伤害血量",
"fire.dpmPremium": "加值炮弹 每分钟伤害血量",
"fire.dpmPremium": "金币炮弹 每分钟伤害值",
"fire.reload": "装填时间",
"fire.caliber": "口径",
"fire.standardPenetration": "标准炮弹穿透力",
"fire.premiumPenetration": "加值炮弹穿透力",
"fire.premiumPenetration": "金币炮弹穿透力",
"fire.tertiaryPenetration": "第三种炮弹穿透力",
"fire.damage": "炮弹伤害值",
"fire.moduleDamage": "模组伤害值",
@@ -750,8 +750,8 @@
"dismiss": "取消"
},
"discord": {
"join_short": "加入超級酷的人群!",
"join_long": "加入我們的Discord社群,和超酷的人一起聊天!",
"join_short": "加入超级酷的人群!",
"join_long": "加入我们的Discord社群,和超酷的人一起聊天!",
"action": "Discord"
},
"hero": {
@@ -765,7 +765,7 @@
"title": "其他酷炫的项目"
},
"help_translate": "请帮忙将 {name} 翻译成 {language}!",
"seo_description": "战车世界闪击战 全方位工具"
"seo_description": "坦克世界闪击战 全方位工具"
},
"docs": {
"patches": {
@@ -800,7 +800,7 @@
"proceed": "继续操作"
},
"generate": "创建游玩列表",
"name": "遊玩列表",
"name": "游玩列表",
"description": "坦克游玩列表生成器",
"seo_description": "想不到在坦克世界闪击战玩什么? 使用BlitzKit 游玩列表来获得建议!",
"button": "生成",
@@ -961,7 +961,7 @@
"type": "战车类型",
"dev_id": "开发参数:ID",
"purchase": "购买价格",
"restoration": "恢复战车价格",
"restoration": "恢复坦克价格",
"sale": "出售价格",
"research": "研发所需经验"
},
@@ -1056,10 +1056,10 @@
"dispersionGunDamaged": "主炮损坏时",
"gunDepression": "俯角",
"gunElevation": "仰角",
"gunFrontalDepression": "车辆前部俯角",
"gunFrontalElevation": "车辆前部仰角",
"gunRearDepression": "车辆後部俯角",
"gunRearElevation": "车辆後部仰角",
"gunFrontalDepression": "车体前部俯角",
"gunFrontalElevation": "车体前部仰角",
"gunRearDepression": "车体后部俯角",
"gunRearElevation": "车体后部仰角",
"azimuthLeft": "左射界",
"azimuthRight": "右射界",
"speedForwards": "最高前进速度",
@@ -1259,7 +1259,7 @@
"description": "用于串流的嵌入",
"seo_description": "创建具有极高自订化的《坦克世界:闪击战》串流嵌入功能",
"coming_soon": "更多嵌入功能即将推出。",
"suggest": "建议与想法",
"suggest": "建议与想法。",
"preview": {
"title": "示例预览",
"upload": "上传测试背景"
@@ -170,6 +170,7 @@ export async function extractModel(vfs: AbstractVFS, path: string) {
break;
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.setRotation(component["tc.localRotation"]);
node.setScale(component["tc.localScale"]);
+10 -5
View File
@@ -5,6 +5,7 @@ import {
Video,
youtubers,
} from "@blitzkit/core";
import locales from "@blitzkit/i18n/locales.json";
import { google } from "googleapis";
import { cloneDeep, uniqBy } from "lodash-es";
import { AssetUploader } from "./core/github/assetUploader";
@@ -42,9 +43,11 @@ for (const tank of tanksSanitized) {
}
try {
const q = `World of Tanks Blitz ${tank.name.locales[locales.default]}`;
const results = await youtube.search.list({
part: ["snippet"],
q: `World of Tanks Blitz ${tank.name}`,
q,
});
if (!results.data.items || results.data.items.length === 0) continue;
@@ -54,9 +57,9 @@ for (const tank of tanksSanitized) {
(item) =>
item.snippet?.channelId &&
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] = {
@@ -66,11 +69,13 @@ for (const tank of tanksSanitized) {
({
id: item.id!.videoId!,
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));
} catch (error) {
@@ -14,7 +14,7 @@ import { StickyRowHeaderCell } from "../StickyRowHeaderCell";
type CompareRowProps = {
display?: (
member: Awaited<TankCharacteristics>
member: Awaited<TankCharacteristics>,
) => number | string | undefined;
deltaType?: CompareCellDirection;
decimals?: number;
@@ -46,7 +46,7 @@ export function CompareRow({
const values = stats.map((stat) =>
typeof props.value === "function"
? props.value(stat)!
: (stat[props.value] as number)
: (stat[props.value] as number),
);
const id = useRef(Math.random());
const name =
@@ -56,10 +56,10 @@ export function CompareRow({
return (
<Table.Row>
<StickyRowHeaderCell>
<StickyRowHeaderCell left="-1.25rem">
<Flex
align="center"
style={{ whiteSpace: "nowrap" }}
maxWidth={{ initial: "6rem", sm: "unset" }}
gap={indent ? { initial: "4", sm: "6" } : { initial: "1", sm: "2" }}
>
<IconButton
@@ -111,8 +111,8 @@ export function CompareRow({
const resolvedDisplayValue = display
? display(stats[index])
: decimals === undefined
? value
: value?.toFixed(decimals);
? value
: value?.toFixed(decimals);
return (
<CompareCell
@@ -145,8 +145,8 @@ export function CompareRow({
deltaNominalDisplay
? deltaNominalDisplay(delta)
: decimals === undefined
? delta
: delta.toFixed(decimals)
? delta
: delta.toFixed(decimals)
}`}
)
</Text>
@@ -156,7 +156,7 @@ export function CompareRow({
<Text color="gray">
(
{`${deltaPercentage > 0 ? "+" : ""}${Math.round(
deltaPercentage * 100
deltaPercentage * 100,
)}%`}
)
</Text>
@@ -1,7 +1,7 @@
import { Heading, Table } from '@radix-ui/themes';
import { times } from 'lodash-es';
import { CompareEphemeral } from '../../stores/compareEphemeral';
import { StickyColumnHeaderCell } from '../StickyColumnHeaderCell';
import { Heading, Table } from "@radix-ui/themes";
import { times } from "lodash-es";
import { CompareEphemeral } from "../../stores/compareEphemeral";
import { StickyColumnHeaderCell } from "../StickyColumnHeaderCell";
export function CompareSectionTitle({ children }: { children: string }) {
const memberCount = CompareEphemeral.use((state) => state.members.length);
@@ -105,13 +105,13 @@ export function CompareTable({ stats }: CompareTableProps) {
crewSkills[skill] === 0
? Var("gray-11")
: permanentSkills.includes(skill)
? Var("crimson-11")
: Var("amber-11"),
? Var("crimson-11")
: Var("amber-11"),
}}
/>
))}
</Flex>
)
),
)}
</Flex>
</Button>
@@ -173,19 +173,19 @@ export function CompareTable({ stats }: CompareTableProps) {
consumables,
camouflage,
},
index
index,
) => {
const equipmentPreset =
equipmentDefinitions.presets[tank.equipment_preset];
const provisionsList = availableProvisions(
tank,
gun,
provisionDefinitions
provisionDefinitions,
);
const consumablesList = Object.values(
consumableDefinitions.consumables
consumableDefinitions.consumables,
).filter((consumable) =>
checkConsumableProvisionInclusivity(consumable, tank, gun)
checkConsumableProvisionInclusivity(consumable, tank, gun),
);
return (
@@ -294,11 +294,11 @@ export function CompareTable({ stats }: CompareTableProps) {
<img
alt={unwrap(
provisionDefinitions.provisions[provision]
.name
.name,
)}
key={provision}
src={asset(
`/icons/provisions/${provision}.webp`
`/icons/provisions/${provision}.webp`,
)}
style={{
left: "50%",
@@ -335,7 +335,7 @@ export function CompareTable({ stats }: CompareTableProps) {
<Popover.Content>
<ProvisionsManager
provisions={provisionsList.map(
(provision) => provision.id
(provision) => provision.id,
)}
selected={provisions}
disabled={tank.max_provisions === provisions.length}
@@ -452,7 +452,7 @@ export function CompareTable({ stats }: CompareTableProps) {
CompareEphemeral.mutate((draft) => {
draft.members[index].equipmentMatrix = times(
3,
() => times(3, () => 0)
() => times(3, () => 0),
) as EquipmentMatrix;
draft.sorting = undefined;
});
@@ -501,11 +501,11 @@ export function CompareTable({ stats }: CompareTableProps) {
alt={unwrap(
consumableDefinitions.consumables[
consumable
].name
].name,
)}
key={consumable}
src={asset(
`/icons/consumables/${consumable}.webp`
`/icons/consumables/${consumable}.webp`,
)}
style={{
left: "50%",
@@ -610,7 +610,7 @@ export function CompareTable({ stats }: CompareTableProps) {
</Flex>
</Table.Cell>
);
}
},
)}
</Table.Row>
</Table.Body>
@@ -55,7 +55,7 @@ export function TankCard({ index, tank }: TankCardProps) {
};
});
const closest = distances.reduce((a, b) =>
a.distance < b.distance ? a : b
a.distance < b.distance ? a : b,
);
dropIndex = closest.index;
@@ -85,8 +85,8 @@ export function TankCard({ index, tank }: TankCardProps) {
CompareEphemeral.mutate((draft) => {
const fromIndex = index;
const toIndex = dropIndex > index ? dropIndex - 1 : dropIndex;
const member = draft.members[fromIndex];
draft.members.splice(fromIndex, 1);
draft.members.splice(toIndex, 0, member);
});
@@ -99,7 +99,7 @@ export function TankCard({ index, tank }: TankCardProps) {
return () => {
draggable.current?.removeEventListener("pointerdown", handlePointerDown);
};
}, []);
}, [index]);
return (
<StickyColumnHeaderCell width="0">
@@ -141,7 +141,6 @@ export function TankCard({ index, tank }: TankCardProps) {
</Text>
</Flex>
</Flex>
<InsertionMarker index={index + 1} />
</StickyColumnHeaderCell>
);
@@ -84,7 +84,7 @@ export function Model() {
useEffect(() => {
function handleModelTransform(
event: QuicklimeEvent<ModelTransformEventData>
event: QuicklimeEvent<ModelTransformEventData>,
) {
turretGroup.current.rotation.z = event.data.yaw;
gunGroup.current.rotation.x = event.data.pitch;
@@ -171,6 +171,49 @@ export function Model() {
turretModel.gun_origin.y,
]}
>
<group
position={[
-turretTankModel.turret_origin.x -
turretTrackModel.origin.x -
turretModel.gun_origin.x,
-turretTankModel.turret_origin.z -
turretTrackModel.origin.z -
turretModel.gun_origin.z,
-turretTankModel.turret_origin.y -
turretTrackModel.origin.y -
turretModel.gun_origin.y,
]}
>
{turretNodes.map((node) => {
const isCurrentMantlet =
node.name ===
`gun_${turretModel.guns[turret.turret.guns.at(-1)!.id].model_id.toString().padStart(2, "0")}_mask`;
// const isCurrentGun =
// node.name ===
// `gun_${gunModel.model_id.toString().padStart(2, "0")}`;
const isVisible = isCurrentMantlet;
if (!isVisible) return null;
return jsxTree(node, {
mesh(_, props, key) {
return (
<mesh
{...props}
key={key}
castShadow
receiveShadow
onPointerDown={(event) => {
enablePitchRotation.current = true;
handlePointerDown(event);
}}
/>
);
},
});
})}
</group>
<group
position={[
-gunTankModel.turret_origin.x -
@@ -185,13 +228,13 @@ export function Model() {
]}
>
{gunNodes.map((node) => {
const isCurrentMantlet =
node.name ===
`gun_${gunModel.model_id.toString().padStart(2, "0")}_mask`;
// const isCurrentMantlet =
// node.name ===
// `gun_${gunModel.model_id.toString().padStart(2, "0")}_mask`;
const isCurrentGun =
node.name ===
`gun_${gunModel.model_id.toString().padStart(2, "0")}`;
const isVisible = isCurrentGun || isCurrentMantlet;
const isVisible = isCurrentGun;
if (!isVisible) return null;
@@ -1,12 +1,16 @@
import { Table } from "@radix-ui/themes";
import type { RowHeaderCellProps } from "@radix-ui/themes/src/components/table.js";
export function StickyRowHeaderCell({ style, ...props }: RowHeaderCellProps) {
interface Props extends RowHeaderCellProps {
left?: string | number;
}
export function StickyRowHeaderCell({ style, left = 0, ...props }: Props) {
return (
<Table.RowHeaderCell
style={{
position: "sticky",
left: 0,
left,
backgroundColor: "var(--color-panel)",
...style,
}}
@@ -1,12 +1,12 @@
import { youtubers } from '@blitzkit/core';
import { Box, Flex, Heading, Link, Skeleton, Text } from '@radix-ui/themes';
import { awaitableReviews } from '../../core/awaitables/reviews';
import { Var } from '../../core/radix/var';
import { useLocale } from '../../hooks/useLocale';
import { Duel } from '../../stores/duel';
import type { MaybeSkeletonComponentProps } from '../../types/maybeSkeletonComponentProps';
import { PageWrapper } from '../PageWrapper';
import { VerifiedIcon } from '../VerifiedIcon';
import { youtubers } from "@blitzkit/core";
import { Box, Flex, Heading, Link, Skeleton, Text } from "@radix-ui/themes";
import { awaitableReviews } from "../../core/awaitables/reviews";
import { Var } from "../../core/radix/var";
import { useLocale } from "../../hooks/useLocale";
import { Duel } from "../../stores/duel";
import type { MaybeSkeletonComponentProps } from "../../types/maybeSkeletonComponentProps";
import { PageWrapper } from "../PageWrapper";
import { VerifiedIcon } from "../VerifiedIcon";
const reviews = await awaitableReviews;
@@ -47,22 +47,22 @@ export function VideoSection({ skeleton }: MaybeSkeletonComponentProps) {
<Flex
direction="column"
style={{
borderRadius: 'var(--radius-3)',
overflow: 'hidden',
borderRadius: "var(--radius-3)",
overflow: "hidden",
background: skeleton
? Var('color-panel-solid')
? Var("color-panel-solid")
: `url(${image})`,
backgroundPosition: '0 18px',
backgroundSize: 'cover',
backgroundPosition: "0 18px",
backgroundSize: "cover",
}}
>
<Box
style={{
background: skeleton ? 'transparent' : `url(${image})`,
backgroundSize: 'cover',
backgroundPosition: 'center',
background: skeleton ? "transparent" : `url(${image})`,
backgroundSize: "cover",
backgroundPosition: "center",
height: 128,
aspectRatio: '16 / 9',
aspectRatio: "16 / 9",
}}
/>
<Flex
@@ -70,8 +70,8 @@ export function VideoSection({ skeleton }: MaybeSkeletonComponentProps) {
width="100%"
justify="center"
style={{
backdropFilter: 'blur(4rem)',
WebkitBackdropFilter: 'blur(4rem)',
backdropFilter: "blur(4rem)",
WebkitBackdropFilter: "blur(4rem)",
}}
align="center"
gap="1"
@@ -8,7 +8,7 @@ const tanks = Object.values(tankDefinitions.tanks).filter((tank) => {
const turret = tank.turrets.at(-1)!;
const turretModel = tankModel.turrets[turret.id];
return tank.tier >= 10 && !turretModel.yaw;
return !turretModel.yaw;
});
export function curateMixer() {
@@ -76,7 +76,7 @@ export function tankCharacteristics(
tankModelDefinition: ModelDefinition;
equipmentDefinitions: EquipmentDefinitions;
provisionDefinitions: ProvisionDefinitions;
}
},
) {
const preset = equipmentDefinitions.presets[tank.equipment_preset];
const turretModelDefinition = tankModelDefinition.turrets[turret.id];
@@ -138,7 +138,7 @@ export function tankCharacteristics(
provisionDefinitions.provisions[provision].crew
? total + provisionDefinitions.provisions[provision].crew! / 100
: total,
0
0,
) + (hasImprovedVentilation ? 0.08 : 0);
const commanderMastery = 1 + provisionCrewBonus;
const loaderMastery =
@@ -158,13 +158,13 @@ export function tankCharacteristics(
coefficient(
[applyReactiveArmor && shell.type !== ShellType.HE, -0.27],
[applyDynamicArmor, -0.1],
[applySpallLiner && shell.type === ShellType.HE, -0.2]
[applySpallLiner && shell.type === ShellType.HE, -0.2],
);
const assaultDamageCoefficient =
gun.assault_ranges && gun.assault_ranges.types.includes(shell.type)
? gun.assault_ranges.ranges.find(
({ distance }) => distance >= assaultDistance
)?.factor ?? 0
? (gun.assault_ranges.ranges.find(
({ distance }) => distance >= assaultDistance,
)?.factor ?? 0)
: 1;
const moduleDamageCoefficient = coefficient([hasTungsten, 0.15]);
const reloadCoefficient =
@@ -178,16 +178,16 @@ export function tankCharacteristics(
const healthCoefficient = coefficient(
[hasSandbagArmor, 0.03],
[hasEnhancedSandbagArmor, 0.06],
[hasImprovedAssembly, 0.04]
[hasImprovedAssembly, 0.04],
);
const shellVelocityCoefficient = coefficient(
[hasSupercharger, 0.35],
[hasImprovedGunPowder, 0.3]
[hasImprovedGunPowder, 0.3],
);
const aimTimeCoefficient =
coefficient(
[hasEnhancedGunLayingDrive, -0.15],
[hasReticleCalibration, -0.3]
[hasReticleCalibration, -0.3],
) * coefficient([true, degressiveStat(gunnerMastery)]);
const dispersionStillCoefficient =
coefficient([hasRefinedGun, -0.15], [hasReticleCalibration, -0.3]) *
@@ -197,20 +197,20 @@ export function tankCharacteristics(
-0.12,
]);
const dispersionHullTraverseCoefficient = coefficient(
[hasVerticalStabilizer, -0.15],
[true, -crewSkills.smooth_turn / 100]
[hasVerticalStabilizer, -0.12],
[true, -crewSkills.smooth_turn / 100],
);
const dispersionAfterShotCoefficient = coefficient(
[hasVerticalStabilizer, -0.15],
[true, -crewSkills.soft_recoil / 100]
[hasVerticalStabilizer, -0.12],
[true, -crewSkills.soft_recoil / 100],
);
const dispersionMovingCoefficient = coefficient(
[hasVerticalStabilizer, -0.15],
[true, -crewSkills.smooth_driving / 100]
[hasVerticalStabilizer, -0.12],
[true, -crewSkills.smooth_driving / 100],
);
const dispersionTurretTraverseCoefficient = coefficient(
[hasVerticalStabilizer, -0.15],
[true, -crewSkills.smooth_turret * (2 / 100)]
[hasVerticalStabilizer, -0.12],
[true, -crewSkills.smooth_turret * (2 / 100)],
);
const enginePowerCoefficient = coefficient(
[
@@ -225,7 +225,7 @@ export function tankCharacteristics(
[hasStandardFuel, 0.03],
[hasImprovedFuel, 0.1],
[hasGearOil, 0.03],
[hasImprovedGearOil, 0.06]
[hasImprovedGearOil, 0.06],
);
const turretTraverseCoefficient =
coefficient([hasStandardFuel, 0.03], [hasImprovedFuel, 0.1]) *
@@ -234,7 +234,7 @@ export function tankCharacteristics(
coefficient(
[hasImprovedControl, 0.11],
[hasImprovedEnginePowerBoost, 0.01],
[true, crewSkills.virtuoso / 100]
[true, crewSkills.virtuoso / 100],
) * coefficient([true, progressiveStat(driverMastery)]);
const resistanceHardCoefficient =
@@ -253,22 +253,22 @@ export function tankCharacteristics(
tank.class === TankClass.HEAVY
? 0.06
: tank.class === TankClass.MEDIUM
? 0.08
: tank.class === TankClass.LIGHT
? 0.11
: 0.04,
? 0.08
: tank.class === TankClass.LIGHT
? 0.11
: 0.04,
]) * coefficient([true, progressiveStat(commanderMastery)]);
const fireChanceCoefficient = coefficient([hasProtectiveKit, -0.2]);
const speedForwardsSum = sum(
[hasImprovedEnginePowerBoost, 4],
[hasGearOil, 2],
[hasImprovedGearOil, 4]
[hasImprovedGearOil, 4],
);
const speedBackwardsSum = sum(
[hasImprovedEnginePowerBoost, 8],
[hasGearOil, 2],
[hasImprovedGearOil, 4]
[hasImprovedGearOil, 4],
);
const camouflageCoefficientMoving = sum(
[
@@ -278,8 +278,8 @@ export function tankCharacteristics(
tank.class === TankClass.TANK_DESTROYER
? 0.04
: tank.class === TankClass.HEAVY
? 0.03
: 0.02,
? 0.03
: 0.02,
]),
],
[
@@ -287,11 +287,11 @@ export function tankCharacteristics(
tank.class === TankClass.HEAVY
? 0.02
: tank.class === TankClass.MEDIUM
? 0.04
: tank.class === TankClass.LIGHT
? 0.07
: 0.1,
]
? 0.04
: tank.class === TankClass.LIGHT
? 0.07
: 0.1,
],
);
const camouflageCoefficientStill = sum(
[
@@ -302,10 +302,10 @@ export function tankCharacteristics(
tank.class === TankClass.TANK_DESTROYER
? 0.04
: tank.class === TankClass.HEAVY
? 0.03
: 0.02,
? 0.03
: 0.02,
],
[true, crewSkills.camouflage * (3 / 100)]
[true, crewSkills.camouflage * (3 / 100)],
),
],
[
@@ -313,18 +313,18 @@ export function tankCharacteristics(
tank.class === TankClass.HEAVY
? 0.04
: tank.class === TankClass.MEDIUM
? 0.08
: tank.class === TankClass.LIGHT
? 0.14
: 0.2,
]
? 0.08
: tank.class === TankClass.LIGHT
? 0.14
: 0.2,
],
);
const size = normalizeBoundingBox(
unionBoundingBox(
tankModelDefinition.bounding_box,
turretModelDefinition.bounding_box
)
turretModelDefinition.bounding_box,
),
);
const weightKg =
tank.weight + engine.weight + track.weight + turret.weight + gun.weight;
@@ -344,7 +344,7 @@ export function tankCharacteristics(
shell,
damageCoefficient,
reloadCoefficient,
intraClipCoefficient
intraClipCoefficient,
);
const intraClip =
gun.gun_type?.$case === "regular"
@@ -398,7 +398,7 @@ export function tankCharacteristics(
const shellReloads =
gun.gun_type!.$case === "auto_reloader"
? gun.gun_type!.value.shell_reloads.map(
(reload) => reload * reloadCoefficient
(reload) => reload * reloadCoefficient,
)
: undefined;
const shellReload =