mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-25 11:39:51 +01:00
Compare commits
5
Commits
5ea7bbd6a1
...
17264c1adc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17264c1adc | ||
|
|
2627aba938 | ||
|
|
a2574efb9c | ||
|
|
d3ab4bac51 | ||
|
|
ad78e45f1b |
No files matched your search
+1
-1
Submodule packages/closed updated: 58840ed37f...9a99ff408a.
@@ -6,12 +6,15 @@ import type { AvatarList } from "@protos/avatar_list";
|
||||
import type { Avatars } from "@protos/avatars";
|
||||
import type { Background } from "@protos/background";
|
||||
import type { Backgrounds } from "@protos/backgrounds";
|
||||
import type { Consumables } from "@protos/consumables";
|
||||
import type { Equipment } from "@protos/equipment";
|
||||
import type { PopularTanks } from "@protos/popular_tanks";
|
||||
import type { Sets } from "@protos/sets";
|
||||
import type { Tank } from "@protos/tank";
|
||||
import type { TankList } from "@protos/tank_list";
|
||||
import type { TankUpgradePresets } from "@protos/tank_upgrade_presets";
|
||||
import type { Tanks } from "@protos/tanks";
|
||||
import type { TierPrices } from "@protos/tier_prices";
|
||||
import type { Tiers } from "@protos/tiers";
|
||||
|
||||
export abstract class AbstractAPI {
|
||||
@@ -27,6 +30,9 @@ export abstract class AbstractAPI {
|
||||
|
||||
abstract tiers(): Promise<Tiers>;
|
||||
abstract equipment(): Promise<Equipment>;
|
||||
abstract consumables(): Promise<Consumables>;
|
||||
abstract tierPrices(): Promise<TierPrices>;
|
||||
abstract sets(): Promise<Sets>;
|
||||
|
||||
abstract strings(locale: string): Promise<Strings>;
|
||||
abstract gameStrings(locale: string): Promise<Record<string, string>>;
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { fetchPB } from "@blitzkit/core";
|
||||
import type { Strings } from "@blitzkit/i18n";
|
||||
import { Avatars } from "@protos/avatars";
|
||||
import { Consumables } from "@protos/consumables";
|
||||
import { Equipment } from "@protos/equipment";
|
||||
import { PopularTanks } from "@protos/popular_tanks";
|
||||
import { Sets } from "@protos/sets";
|
||||
import { TankList } from "@protos/tank_list";
|
||||
import { TankUpgradePresets } from "@protos/tank_upgrade_presets";
|
||||
import { Tanks } from "@protos/tanks";
|
||||
import { TierPrices } from "@protos/tier_prices";
|
||||
import { Tiers } from "@protos/tiers";
|
||||
import { AbstractAPI, Cache } from "./abstract";
|
||||
|
||||
@@ -59,6 +62,21 @@ export class ClientAPI extends AbstractAPI {
|
||||
return fetchPB("/api/equipment.pb", Equipment);
|
||||
}
|
||||
|
||||
@Cache()
|
||||
consumables() {
|
||||
return fetchPB("/api/consumables.pb", Consumables);
|
||||
}
|
||||
|
||||
@Cache()
|
||||
tierPrices() {
|
||||
return fetchPB("/api/tier-prices.pb", TierPrices);
|
||||
}
|
||||
|
||||
@Cache()
|
||||
sets() {
|
||||
return fetchPB("/api/sets.pb", Sets);
|
||||
}
|
||||
|
||||
@Cache()
|
||||
async strings(locale: string) {
|
||||
const response = await fetch(`/api/strings/${locale}.json`);
|
||||
|
||||
@@ -5,12 +5,15 @@ import locales from "@blitzkit/i18n/locales.json";
|
||||
import type { Avatar, DeepPartial } from "@protos/avatar";
|
||||
import type { Background } from "@protos/background";
|
||||
import type { RemoteStorageComponent } from "@protos/blitz_static_remote_storage_component";
|
||||
import { Consumables } from "@protos/consumables";
|
||||
import { Equipment } from "@protos/equipment";
|
||||
import type { PopularTanks } from "@protos/popular_tanks";
|
||||
import { Sets } from "@protos/sets";
|
||||
import type { Tank } from "@protos/tank";
|
||||
import type { TankListEntry } from "@protos/tank_list";
|
||||
import { TankUpgradePresets } from "@protos/tank_upgrade_presets";
|
||||
import type { Tanks } from "@protos/tanks";
|
||||
import { TierPrices } from "@protos/tier_prices";
|
||||
import type { Tiers } from "@protos/tiers";
|
||||
import { existsSync } from "fs";
|
||||
import { mkdir, readFile, writeFile } from "fs/promises";
|
||||
@@ -262,6 +265,47 @@ export class ServerAPI extends AbstractAPI {
|
||||
return equipment;
|
||||
}
|
||||
|
||||
@Cache()
|
||||
async consumables() {
|
||||
const consumables = Consumables.create();
|
||||
|
||||
for (const item of this.metadata.group("ConsumableEntity")) {
|
||||
const purchase = item.BlitzStaticPurchase("purchasable");
|
||||
const consumable = item.Consumable();
|
||||
const compatibility = item.Compatibility();
|
||||
|
||||
consumables.consumables[item.id] = {
|
||||
purchase,
|
||||
consumable,
|
||||
compatibility,
|
||||
};
|
||||
}
|
||||
|
||||
return consumables;
|
||||
}
|
||||
|
||||
@Cache()
|
||||
async tierPrices() {
|
||||
const tierPrices = TierPrices.create();
|
||||
|
||||
for (const item of this.metadata.group("TierPricesEntity")) {
|
||||
tierPrices.prices[item.id] = item.TierPrices();
|
||||
}
|
||||
|
||||
return tierPrices;
|
||||
}
|
||||
|
||||
@Cache()
|
||||
async sets() {
|
||||
const sets = Sets.create();
|
||||
|
||||
for (const item of this.metadata.group("TankSetEntity")) {
|
||||
sets.sets[item.id] = item.TankSetCatalog();
|
||||
}
|
||||
|
||||
return sets;
|
||||
}
|
||||
|
||||
@Cache(true)
|
||||
async strings(locale: string) {
|
||||
const localized = (await globbedStrings[
|
||||
|
||||
@@ -19,9 +19,12 @@ import {
|
||||
maxModulesPerRow,
|
||||
originalLineName,
|
||||
} from "../../config/modules";
|
||||
import { useCompatibility } from "../../hooks/useCompatibility";
|
||||
import { useConsumables } from "../../hooks/useConsumables";
|
||||
import { useEquipment } from "../../hooks/useEquipment";
|
||||
import { useProtagonist } from "../../hooks/useProtagonist";
|
||||
import { useStrings } from "../../hooks/useStrings";
|
||||
import { useTierPrices } from "../../hooks/useTierPrices";
|
||||
import { useUpgradePreset } from "../../hooks/useUpgradePreset";
|
||||
import { Tankopedia } from "../../stores/tankopedia";
|
||||
import { Button } from "../Button";
|
||||
@@ -43,10 +46,25 @@ export function TankopediaLoadout() {
|
||||
<Section>
|
||||
{showModules && <Modules />}
|
||||
<Equipment />
|
||||
<Consumables />
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
function Consumables() {
|
||||
const consumables = useConsumables();
|
||||
const tierPrices = useTierPrices();
|
||||
const tank = useProtagonist();
|
||||
const isCompatible = useCompatibility(tank);
|
||||
|
||||
return Object.values(consumables.consumables).map(
|
||||
({ compatibility, consumable }) => {
|
||||
if (!isCompatible(compatibility!)) return null;
|
||||
return <span>{consumable!.name_key}</span>;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function Equipment() {
|
||||
const strings = useStrings();
|
||||
const tank = useProtagonist();
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { ErrorWrapper } from "../components/ErrorWrapper";
|
||||
|
||||
export function withErrorWrapper<Props extends object>(
|
||||
Component: (props: Props) => ReactNode,
|
||||
) {
|
||||
return function (props: Props) {
|
||||
return (
|
||||
<ErrorWrapper>
|
||||
<Component {...props} />
|
||||
</ErrorWrapper>
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import type { ReactNode } from "react";
|
||||
import { LocaleProvider } from "../hooks/useLocale";
|
||||
|
||||
interface WithLocaleProps {
|
||||
locale: string;
|
||||
}
|
||||
|
||||
export function withLocale<Props extends object>(
|
||||
Component: (props: Props) => ReactNode,
|
||||
) {
|
||||
return function ({ locale, ...props }: Props & WithLocaleProps) {
|
||||
return (
|
||||
<LocaleProvider locale={locale}>
|
||||
<Component {...(props as Props)} />
|
||||
</LocaleProvider>
|
||||
);
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import type { CompatibilityComponent } from "@protos/blitz_static_compatibility_component";
|
||||
import type { TankCompatibility_Include } from "@protos/blitz_static_tank_compatibility";
|
||||
import { TankTraits } from "@protos/blitz_static_tank_compatibility";
|
||||
import { TankAttributeChange_AttributeName } from "@protos/blitz_static_tank_upgrade_single_stage";
|
||||
import type { Sets } from "@protos/sets";
|
||||
import type { Tank } from "@protos/tank";
|
||||
import { useSets } from "./useSets";
|
||||
|
||||
export function useCompatibility(tank: Tank) {
|
||||
const sets = useSets();
|
||||
|
||||
return function (compatibility: CompatibilityComponent) {
|
||||
if (
|
||||
compatibility.tank_set_compatibility ||
|
||||
compatibility.game_type_compatibility ||
|
||||
compatibility.quest_compatibility ||
|
||||
compatibility.quest_slots_compatibility ||
|
||||
compatibility.tank_style_compatibility
|
||||
) {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
return (
|
||||
compatibility.tank_compatibility === undefined ||
|
||||
(tankCompatibilityApplies(
|
||||
compatibility.tank_compatibility.include,
|
||||
tank,
|
||||
sets,
|
||||
) &&
|
||||
!tankCompatibilityApplies(
|
||||
compatibility.tank_compatibility.exclude,
|
||||
tank,
|
||||
sets,
|
||||
))
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
function tankCompatibilityApplies(
|
||||
include: TankCompatibility_Include | undefined,
|
||||
tank: Tank,
|
||||
sets: Sets,
|
||||
) {
|
||||
if (include === undefined) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (include.is_special) {
|
||||
throw new Error("is_special Not implemented");
|
||||
}
|
||||
|
||||
return (
|
||||
include.nations.some((nation) => nation === tank.tank!.nation) ||
|
||||
include.tiers.some((tier) => tier === tank.tank!.tier) ||
|
||||
include.tier_catalog_ids.some(
|
||||
(tier) => tier === tank.tank!.tier_catalog_id,
|
||||
) ||
|
||||
include.classes.some((_class) => _class === tank.tank!.tank_class) ||
|
||||
include.catalogs_id.some((id) => id === tank.id) ||
|
||||
include.tank_traits.some((trait) => tankHasTraits(trait, tank)) ||
|
||||
include.types.some((type) => type === tank.tank!.tank_type) ||
|
||||
include.tank_sets_catalog_ids.some((id) =>
|
||||
Object.entries(sets.sets).some(
|
||||
([setId, set]) =>
|
||||
setId === id &&
|
||||
set.tank_set_rewards.some((reward) =>
|
||||
reward.tank_set_reward_on_level!.reward_list.some(
|
||||
(reward) => reward.tank_reward?.tank_catalog_id === tank.id,
|
||||
),
|
||||
),
|
||||
),
|
||||
) ||
|
||||
include.tech_tags.some((tag) => tank.tank!.tech_tags.includes(tag))
|
||||
);
|
||||
}
|
||||
|
||||
function tankHasTraits(traits: TankTraits, tank: Tank) {
|
||||
switch (traits) {
|
||||
case TankTraits.TANK_TRAITS_HAS_CLIP:
|
||||
// TODO: use current tank state to check if the current gun has a clip
|
||||
|
||||
return tank.tank!.upgrade_lines.some((line) =>
|
||||
line.stages.some((stage) =>
|
||||
stage.attributes.every((attribute) =>
|
||||
attribute.attribute_name !==
|
||||
TankAttributeChange_AttributeName.ATTRIBUTE_NAME_IS_PUMP &&
|
||||
attribute.attribute_name ===
|
||||
TankAttributeChange_AttributeName.ATTRIBUTE_NAME_CLIP_SIZE
|
||||
? attribute.value === 1
|
||||
: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown trait: ${traits}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { api } from "../api/dynamic";
|
||||
import { useAwait } from "./useAwait";
|
||||
|
||||
export function useConsumables() {
|
||||
const consumables = useAwait(() => api.consumables(), "consumables");
|
||||
return consumables;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { api } from "../api/dynamic";
|
||||
import { useAwait } from "./useAwait";
|
||||
|
||||
export function useSets() {
|
||||
const sets = useAwait(() => api.sets(), "sets");
|
||||
return sets;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { api } from "../api/dynamic";
|
||||
import { useAwait } from "./useAwait";
|
||||
|
||||
export function useTierPrices() {
|
||||
const tierPrices = useAwait(() => api.tierPrices(), "tier-prices");
|
||||
return tierPrices;
|
||||
}
|
||||
@@ -1,21 +1,9 @@
|
||||
import { AvatarControls } from "../../../components/AvatarControls";
|
||||
import { AvatarsList } from "../../../components/AvatarsList";
|
||||
import { Section } from "../../../components/Section";
|
||||
import { LocaleProvider } from "../../../hooks/useLocale";
|
||||
import { withLocale } from "../../../hocs/withLocale";
|
||||
|
||||
interface PageProps {
|
||||
locale: string;
|
||||
}
|
||||
|
||||
export function Page({ locale, ...props }: PageProps) {
|
||||
return (
|
||||
<LocaleProvider locale={locale}>
|
||||
<Content {...props} />
|
||||
</LocaleProvider>
|
||||
);
|
||||
}
|
||||
|
||||
function Content() {
|
||||
export const Page = withLocale(() => {
|
||||
return (
|
||||
<>
|
||||
<Section>
|
||||
@@ -27,4 +15,4 @@ function Content() {
|
||||
</Section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,45 +0,0 @@
|
||||
---
|
||||
import { Grade } from "@protos/blitz_static_standard_grades_enum";
|
||||
import { api } from "../../../api/dynamic";
|
||||
import BaseLayout from "../../../layouts/BaseLayout.astro";
|
||||
|
||||
export { getStaticPaths } from "../index.astro";
|
||||
|
||||
const { locale } = Astro.props;
|
||||
const { backgrounds } = await api.backgrounds();
|
||||
---
|
||||
|
||||
<BaseLayout {locale} accent="amber">
|
||||
{
|
||||
backgrounds.map((background) => {
|
||||
return (
|
||||
<>
|
||||
<h3>
|
||||
{background.stuff_ui!.display_name}{" "}
|
||||
{Grade[background.stuff_ui!.grade]}{" "}
|
||||
</h3>
|
||||
|
||||
<pre>{JSON.stringify(background.profile_background, null, 2)}</pre>
|
||||
|
||||
{background.stuff_ui!.description && (
|
||||
<p>{background.stuff_ui!.description}</p>
|
||||
)}
|
||||
{background.stuff_ui!.obtaining_methods && (
|
||||
<p>{background.stuff_ui!.obtaining_methods}</p>
|
||||
)}
|
||||
{background.stuff_ui!.features && (
|
||||
<p>{background.stuff_ui!.features}</p>
|
||||
)}
|
||||
|
||||
<img width="128" src={`/api/backgrounds/${background.name}.webp`} />
|
||||
|
||||
{background.sellable && (
|
||||
<pre>{JSON.stringify(background.sellable, null, 2)}</pre>
|
||||
)}
|
||||
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
})
|
||||
}
|
||||
</BaseLayout>
|
||||
@@ -1,15 +0,0 @@
|
||||
---
|
||||
import { Heading } from "../../../components/Heading";
|
||||
import { Section } from "../../../components/Section";
|
||||
import BaseLayout from "../../../layouts/BaseLayout.astro";
|
||||
|
||||
export { getStaticPaths } from "../index.astro";
|
||||
|
||||
const { locale } = Astro.props;
|
||||
---
|
||||
|
||||
<BaseLayout {locale} accent="amber">
|
||||
<Section>
|
||||
<Heading>Settings</Heading>
|
||||
</Section>
|
||||
</BaseLayout>
|
||||
@@ -1,379 +1,74 @@
|
||||
import { Fragment, useMemo } from "react";
|
||||
import { api } from "../../../../api/dynamic";
|
||||
import { ErrorWrapper } from "../../../../components/ErrorWrapper";
|
||||
import { TankopediaLoadout } from "../../../../components/TankopediaLoadout";
|
||||
import { withErrorWrapper } from "../../../../hocs/withErrorWrapper";
|
||||
import { withLocale } from "../../../../hocs/withLocale";
|
||||
import { useAwait } from "../../../../hooks/useAwait";
|
||||
import { useCharacteristicRenderer } from "../../../../hooks/useCharacteristicRenderer";
|
||||
import { useEquipment } from "../../../../hooks/useEquipment";
|
||||
import { LocaleProvider } from "../../../../hooks/useLocale";
|
||||
import { Tankopedia } from "../../../../stores/tankopedia";
|
||||
import { characteristicsOrder } from "../../../../tankopedia/characteristicsOrder";
|
||||
import { computeCharacteristics } from "../../../../tankopedia/computeCharacteristics";
|
||||
|
||||
interface PageProps {
|
||||
id: string;
|
||||
locale: string;
|
||||
}
|
||||
|
||||
export function Page(props: PageProps) {
|
||||
return (
|
||||
<ErrorWrapper>
|
||||
<Wrapper {...props} />
|
||||
</ErrorWrapper>
|
||||
);
|
||||
}
|
||||
export const Page = withErrorWrapper(
|
||||
withLocale<PageProps>(({ id }) => {
|
||||
const protagonistTank = useAwait(() => api.tank(id), `tank-${id}`);
|
||||
|
||||
function Wrapper({ id, locale }: PageProps) {
|
||||
const tank = useAwait(() => api.tank(id), `tank-${id}`);
|
||||
Tankopedia.useInitialization(protagonistTank);
|
||||
|
||||
Tankopedia.useInitialization(tank);
|
||||
const renderCharacteristic = useCharacteristicRenderer();
|
||||
|
||||
return (
|
||||
<LocaleProvider locale={locale}>
|
||||
<Content />
|
||||
</LocaleProvider>
|
||||
);
|
||||
}
|
||||
const protagonist = Tankopedia.use((state) => state.protagonist);
|
||||
const protagonistEquipment = useEquipment(protagonistTank.tank!);
|
||||
|
||||
function Content() {
|
||||
const renderCharacteristic = useCharacteristicRenderer();
|
||||
const { characteristics } = useMemo(
|
||||
() =>
|
||||
computeCharacteristics(
|
||||
protagonistTank,
|
||||
protagonistEquipment,
|
||||
protagonist,
|
||||
),
|
||||
[protagonist],
|
||||
);
|
||||
|
||||
const protagonist = Tankopedia.use((state) => state.protagonist);
|
||||
const protagonistTank = useAwait(
|
||||
() => api.tank(protagonist.id),
|
||||
`tank-${protagonist.id}`,
|
||||
);
|
||||
const protagonistEquipment = useEquipment(protagonistTank.tank!);
|
||||
return (
|
||||
<>
|
||||
<h1>{protagonist.id}</h1>
|
||||
|
||||
const { characteristics } = useMemo(
|
||||
() =>
|
||||
computeCharacteristics(
|
||||
protagonistTank,
|
||||
protagonistEquipment,
|
||||
protagonist,
|
||||
),
|
||||
[protagonist],
|
||||
);
|
||||
<br />
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>{protagonist.id}</h1>
|
||||
<TankopediaLoadout />
|
||||
|
||||
<br />
|
||||
<h2>characteristics</h2>
|
||||
|
||||
<TankopediaLoadout />
|
||||
{characteristicsOrder.map((group) => {
|
||||
return (
|
||||
<Fragment key={group.group}>
|
||||
<h3>{group.group}</h3>
|
||||
|
||||
{/* <h2>upgrades</h2>
|
||||
|
||||
{protagonistTank.tank!.upgrade_lines.map((line) => {
|
||||
return (
|
||||
<>
|
||||
<h3>
|
||||
{line.name} ({protagonist.upgrades[line.name]})
|
||||
</h3>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-start",
|
||||
gap: 8,
|
||||
}}
|
||||
>
|
||||
{line.stages.map((stage, index) => {
|
||||
if (stage.custom_picture) {
|
||||
throw new Error("Custom picture not supported");
|
||||
{group.order.map((item) => {
|
||||
if ("toy" in item) {
|
||||
return <span key={`toy-${item.toy}`}>toy: {item.toy}</span>;
|
||||
}
|
||||
|
||||
const value = characteristics[item.name];
|
||||
|
||||
if (value == null) return null;
|
||||
|
||||
return (
|
||||
<button
|
||||
key={stage.tech_name}
|
||||
onClick={() => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.protagonist.upgrades[line.name] = index;
|
||||
|
||||
for (const required of stage.required_upgrades) {
|
||||
for (const line of protagonistTank.tank!
|
||||
.upgrade_lines) {
|
||||
let i = 0;
|
||||
|
||||
for (const stage of line.stages) {
|
||||
if (stage.tech_name === required) {
|
||||
draft.protagonist.upgrades[line.name] =
|
||||
Math.max(
|
||||
draft.protagonist.upgrades[line.name],
|
||||
i,
|
||||
);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const line of protagonistTank.tank!
|
||||
.upgrade_lines) {
|
||||
let i = draft.protagonist.upgrades[line.name];
|
||||
|
||||
while (i > 0) {
|
||||
const currentStage = line.stages[i];
|
||||
const valid = currentStage.required_upgrades.every(
|
||||
(required) => {
|
||||
for (const line of protagonistTank.tank!
|
||||
.upgrade_lines) {
|
||||
const idx =
|
||||
draft.protagonist.upgrades[line.name];
|
||||
|
||||
if (
|
||||
line.stages[idx]?.tech_name === required
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
);
|
||||
|
||||
if (valid) break;
|
||||
|
||||
i--;
|
||||
}
|
||||
|
||||
draft.protagonist.upgrades[line.name] = i;
|
||||
}
|
||||
});
|
||||
}}
|
||||
style={{
|
||||
backgroundColor:
|
||||
protagonist.upgrades[line.name] === index
|
||||
? "green"
|
||||
: undefined,
|
||||
}}
|
||||
>
|
||||
{stage.tech_name}: {Grade[stage.grade]}
|
||||
{StageParameters_StageType[stage.stage_type]}
|
||||
{tankEntityGameStrings[stage.display_name]}
|
||||
</button>
|
||||
<span key={`characteristic-${item.name}`}>
|
||||
{item.name}: {renderCharacteristic(value, item)}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
})} */}
|
||||
|
||||
<h2>characteristics</h2>
|
||||
|
||||
{characteristicsOrder.map((group) => {
|
||||
return (
|
||||
<Fragment key={group.group}>
|
||||
<h3>{group.group}</h3>
|
||||
|
||||
{group.order.map((item) => {
|
||||
if ("toy" in item) {
|
||||
return <span key={`toy-${item.toy}`}>toy: {item.toy}</span>;
|
||||
}
|
||||
|
||||
const value = characteristics[item.name];
|
||||
|
||||
if (value == null) return null;
|
||||
|
||||
return (
|
||||
<span key={`characteristic-${item.name}`}>
|
||||
{item.name}: {renderCharacteristic(value, item)}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
|
||||
// const parameters = aggregateParameters(
|
||||
// protagonistTank.tank!,
|
||||
// protagonist.upgrades,
|
||||
// );
|
||||
|
||||
// if (parameters.modules_upgrades.length > 0) {
|
||||
// throw new Error("Modules upgrades display not implemented");
|
||||
// }
|
||||
|
||||
// return (
|
||||
// <>
|
||||
// <h1>{protagonist.id}</h1>
|
||||
|
||||
// <br />
|
||||
|
||||
// <h2>upgrades</h2>
|
||||
|
||||
// {protagonistTank.tank!.upgrade_lines.map((line) => {
|
||||
// return (
|
||||
// <>
|
||||
// <h3>
|
||||
// {line.name} ({protagonist.upgrades[line.name]})
|
||||
// </h3>
|
||||
|
||||
// <div
|
||||
// style={{
|
||||
// display: "flex",
|
||||
// flexDirection: "column",
|
||||
// alignItems: "flex-start",
|
||||
// gap: 8,
|
||||
// }}
|
||||
// >
|
||||
// {line.stages.map((stage, index) => {
|
||||
// if (stage.custom_picture) {
|
||||
// throw new Error("Custom picture not supported");
|
||||
// }
|
||||
|
||||
// return (
|
||||
// <button
|
||||
// key={stage.tech_name}
|
||||
// onClick={() => {
|
||||
// Tankopedia.mutate((draft) => {
|
||||
// draft.protagonist.upgrades[line.name] = index;
|
||||
|
||||
// for (const required of stage.required_upgrades) {
|
||||
// for (const line of protagonistTank.tank!
|
||||
// .upgrade_lines) {
|
||||
// let i = 0;
|
||||
|
||||
// for (const stage of line.stages) {
|
||||
// if (stage.tech_name === required) {
|
||||
// draft.protagonist.upgrades[line.name] =
|
||||
// Math.max(
|
||||
// draft.protagonist.upgrades[line.name],
|
||||
// i,
|
||||
// );
|
||||
// }
|
||||
|
||||
// i++;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// for (const line of protagonistTank.tank!
|
||||
// .upgrade_lines) {
|
||||
// let i = draft.protagonist.upgrades[line.name];
|
||||
|
||||
// while (i > 0) {
|
||||
// const currentStage = line.stages[i];
|
||||
// const valid = currentStage.required_upgrades.every(
|
||||
// (required) => {
|
||||
// for (const line of protagonistTank.tank!
|
||||
// .upgrade_lines) {
|
||||
// const idx =
|
||||
// draft.protagonist.upgrades[line.name];
|
||||
|
||||
// if (
|
||||
// line.stages[idx]?.tech_name === required
|
||||
// ) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
|
||||
// return false;
|
||||
// },
|
||||
// );
|
||||
|
||||
// if (valid) break;
|
||||
|
||||
// i--;
|
||||
// }
|
||||
|
||||
// draft.protagonist.upgrades[line.name] = i;
|
||||
// }
|
||||
// });
|
||||
// }}
|
||||
// style={{
|
||||
// backgroundColor:
|
||||
// protagonist.upgrades[line.name] === index
|
||||
// ? "green"
|
||||
// : undefined,
|
||||
// }}
|
||||
// >
|
||||
// {stage.tech_name}: {Grade[stage.grade]}
|
||||
// {StageParameters_StageType[stage.stage_type]}
|
||||
// {tankEntityGameStrings[stage.display_name]}
|
||||
// </button>
|
||||
// );
|
||||
// })}
|
||||
// </div>
|
||||
// </>
|
||||
// );
|
||||
// })}
|
||||
|
||||
// <h2>characteristics</h2>
|
||||
|
||||
// {}
|
||||
|
||||
// <h2>parameters</h2>
|
||||
|
||||
// <h3>attributes</h3>
|
||||
|
||||
// {parameters.attributes.map((attribute) => {
|
||||
// return (
|
||||
// <span>
|
||||
// {TankAttributeChange_AttributeName[attribute.attribute_name]}:{" "}
|
||||
// {attribute.value}
|
||||
// </span>
|
||||
// );
|
||||
// })}
|
||||
|
||||
// <h3>penetration groups</h3>
|
||||
|
||||
// {parameters.penetration_groups_upgrades.map((upgrade) => {
|
||||
// return (
|
||||
// <>
|
||||
// <h4>{PenetrationGroupUpgrade_TankPart[upgrade.tank_part]}</h4>
|
||||
|
||||
// <span>
|
||||
// primary armor ({upgrade.primary_armor.length}):{" "}
|
||||
// {upgrade.primary_armor}
|
||||
// </span>
|
||||
|
||||
// <br />
|
||||
|
||||
// {upgrade.penetration_groups.map((group) => {
|
||||
// return (
|
||||
// <span>
|
||||
// {group.group_name}: {group.armor} (
|
||||
// {PenetrationGroup_CommonData[group.common_data]})
|
||||
// </span>
|
||||
// );
|
||||
// })}
|
||||
// </>
|
||||
// );
|
||||
// })}
|
||||
|
||||
// <h2>modules ({parameters.modules_upgrades.length})</h2>
|
||||
|
||||
// {parameters.modules_upgrades.map((upgrade) => {
|
||||
// return (
|
||||
// <span>{ModuleUpgrade_AttributeName[upgrade.attribute_name]}</span>
|
||||
// );
|
||||
// })}
|
||||
|
||||
// <h2>shells</h2>
|
||||
|
||||
// {parameters.shells_upgrades.map((upgrade) => {
|
||||
// return (
|
||||
// <>
|
||||
// <h3>
|
||||
// {upgrade.shell_id}: {ShellUpgrade_ShellType[upgrade.shell_type]}
|
||||
// </h3>
|
||||
|
||||
// <pre>
|
||||
// silver_price: {JSON.stringify(upgrade.silver_price, null, 2)}
|
||||
// </pre>
|
||||
|
||||
// <pre>
|
||||
// tray_shells: {JSON.stringify(upgrade.tray_shells, null, 2)}
|
||||
// </pre>
|
||||
// </>
|
||||
// );
|
||||
// })}
|
||||
// </>
|
||||
// );
|
||||
}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}),
|
||||
);
|
||||
@@ -5,27 +5,15 @@ import { IncrementalLoader } from "../../../components/IncrementalLoader";
|
||||
import { TankCard } from "../../../components/TankCard";
|
||||
import { TankCards } from "../../../components/TankCards";
|
||||
import { TankSearch } from "../../../components/TankSearch";
|
||||
import { withLocale } from "../../../hocs/withLocale";
|
||||
import { useAwait } from "../../../hooks/useAwait";
|
||||
import { LocaleProvider } from "../../../hooks/useLocale";
|
||||
import { useSearchableTanks } from "../../../hooks/useSearchableTanks";
|
||||
import { useTierCompare } from "../../../hooks/useTierCompare";
|
||||
import { TankFilters } from "../../../stores/tankFilters";
|
||||
|
||||
const { go } = fuzzysort;
|
||||
|
||||
interface PageProps {
|
||||
locale: string;
|
||||
}
|
||||
|
||||
export function Page({ locale, ...props }: PageProps) {
|
||||
return (
|
||||
<LocaleProvider locale={locale}>
|
||||
<Content {...props} />
|
||||
</LocaleProvider>
|
||||
);
|
||||
}
|
||||
|
||||
function Content() {
|
||||
export const Page = withLocale(() => {
|
||||
const tanks = useAwait(() => api.tanks(), "tanks");
|
||||
|
||||
const compareTiers = useTierCompare();
|
||||
@@ -60,7 +48,7 @@ function Content() {
|
||||
<List tanks={tanksFiltered} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
interface ListProps {
|
||||
tanks: string[];
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Consumables } from "@protos/consumables";
|
||||
import { api } from "../../api/dynamic";
|
||||
|
||||
export { getStaticPaths } from "./_index";
|
||||
|
||||
export async function GET() {
|
||||
const consumables = await api.consumables();
|
||||
return new Response(Consumables.encode(consumables).finish());
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Sets } from "@protos/sets";
|
||||
import { api } from "../../api/dynamic";
|
||||
|
||||
export { getStaticPaths } from "./_index";
|
||||
|
||||
export async function GET() {
|
||||
const sets = await api.sets();
|
||||
return new Response(Sets.encode(sets).finish());
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { TierPrices } from "@protos/tier_prices";
|
||||
import { api } from "../../api/dynamic";
|
||||
|
||||
export { getStaticPaths } from "./_index";
|
||||
|
||||
export async function GET() {
|
||||
const tierPrices = await api.tierPrices();
|
||||
return new Response(TierPrices.encode(tierPrices).finish());
|
||||
}
|
||||
Reference in new issue
Block a user