some consumable compatibility code

This commit is contained in:
tresabhi committed 2026-05-20 13:48:20 -05:00
1 parent a2574efb9c
commit 2627aba938
9 files changed
+154 -10

No files matched your search

+2
View File
@@ -9,6 +9,7 @@ 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";
@@ -31,6 +32,7 @@ export abstract class AbstractAPI {
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>>;
+6
View File
@@ -4,6 +4,7 @@ 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";
@@ -71,6 +72,11 @@ export class ClientAPI extends AbstractAPI {
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`);
+12
View File
@@ -8,6 +8,7 @@ import type { RemoteStorageComponent } from "@protos/blitz_static_remote_storage
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";
@@ -294,6 +295,17 @@ export class ServerAPI extends AbstractAPI {
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,6 +19,7 @@ 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";
@@ -26,7 +27,6 @@ import { useStrings } from "../../hooks/useStrings";
import { useTierPrices } from "../../hooks/useTierPrices";
import { useUpgradePreset } from "../../hooks/useUpgradePreset";
import { Tankopedia } from "../../stores/tankopedia";
import { isCompatible } from "../../tankopedia/isCompatible";
import { Button } from "../Button";
import { Heading } from "../Heading";
import { Link } from "../Link";
@@ -54,10 +54,12 @@ export function TankopediaLoadout() {
function Consumables() {
const consumables = useConsumables();
const tierPrices = useTierPrices();
const tank = useProtagonist();
const isCompatible = useCompatibility();
return Object.values(consumables.consumables).map(
({ compatibility, consumable }) => {
if (!isCompatible(compatibility!)) return null;
if (!isCompatible(compatibility!, tank)) return null;
return <span>{consumable!.name_key}</span>;
},
@@ -0,0 +1,113 @@
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(
compatibility: CompatibilityComponent,
tank: Tank,
) {
const sets = useSets();
return function (
{
tank_compatibility,
tank_set_compatibility,
game_type_compatibility,
quest_compatibility,
quest_slots_compatibility,
tank_style_compatibility,
}: CompatibilityComponent,
tank: Tank,
) {
if (
tank_set_compatibility ||
game_type_compatibility ||
quest_compatibility ||
quest_slots_compatibility ||
tank_style_compatibility
) {
throw new Error("Not implemented");
}
if (
tank_compatibility !== undefined &&
(!tankCompatibilityApplies(tank_compatibility.include!, tank, sets) ||
tankCompatibilityApplies(tank_compatibility.exclude!, tank, sets))
) {
return false;
}
return true;
};
}
function tankCompatibilityApplies(
{
nations,
tiers,
tier_catalog_ids,
classes,
catalogs_id,
tank_traits,
types,
tank_sets_catalog_ids,
is_special,
tech_tags,
...rest
}: TankCompatibility_Include,
tank: Tank,
sets: Sets,
) {
if (is_special) {
throw new Error("is_special Not implemented");
}
return (
nations.some((nation) => nation === tank.tank!.nation) ||
tiers.some((tier) => tier === tank.tank!.tier) ||
tier_catalog_ids.some((tier) => tier === tank.tank!.tier_catalog_id) ||
classes.some((_class) => _class === tank.tank!.tank_class) ||
catalogs_id.some((id) => id === tank.id) ||
tank_traits.some((trait) => tankHasTraits(trait, tank)) ||
types.some((type) => type === tank.tank!.tank_type) ||
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,
),
),
),
) ||
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}`);
}
}
+7
View File
@@ -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,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());
}
@@ -1,7 +0,0 @@
import type { CompatibilityComponent } from "@protos/blitz_static_compatibility_component";
export function isCompatible(compatibility: CompatibilityComponent) {
console.log(compatibility);
return true;
}