diff --git a/.vscode/settings.json b/.vscode/settings.json
index 8de5aaa43..c5334eed5 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -46,6 +46,7 @@
"glsl",
"GLTF",
"immer",
+ "Inclusivity",
"JOINTINDEX",
"JOINTWEIGHT",
"kranvagn",
diff --git a/scripts/buildAssets/definitions.ts b/scripts/buildAssets/definitions.ts
index f02e7070b..eae2b8548 100644
--- a/scripts/buildAssets/definitions.ts
+++ b/scripts/buildAssets/definitions.ts
@@ -7,7 +7,10 @@ import { readYAML } from '../../src/core/blitz/readYAML';
import { readYAMLDVPL } from '../../src/core/blitz/readYAMLDVPL';
import { toUniqueId } from '../../src/core/blitz/toUniqueId';
import { commitAssets } from '../../src/core/blitzkrieg/commitAssets';
-import { ConsumableDefinitions } from '../../src/core/blitzkrieg/consumablesDefinitions';
+import {
+ ConsumableDefinitionFilterCategory,
+ ConsumableDefinitions,
+} from '../../src/core/blitzkrieg/consumablesDefinitions';
import {
EquipmentDefinitions,
EquipmentPreset,
@@ -666,7 +669,9 @@ export async function buildDefinitions(production: boolean) {
} else if ('extendedTags' in excludeRaw) {
consumableDefinitions[consumable.id].exclude!.push({
type: 'category',
- categories: excludeRaw.extendedTags.split(' '),
+ categories: excludeRaw.extendedTags.split(
+ ' ',
+ ) as ConsumableDefinitionFilterCategory[],
});
} else throw new SyntaxError('Unhandled exclude type');
}
diff --git a/src/app/tools/tankopedia/[id]/components/Configure.tsx b/src/app/tools/tankopedia/[id]/components/Configure.tsx
index 1ae2712bb..48d1a6f3f 100644
--- a/src/app/tools/tankopedia/[id]/components/Configure.tsx
+++ b/src/app/tools/tankopedia/[id]/components/Configure.tsx
@@ -1,6 +1,8 @@
import { Flex, Heading } from '@radix-ui/themes';
import { use } from 'react';
import { ModuleButton } from '../../../../../components/ModuleButton';
+import { checkConsumableInclusivity } from '../../../../../core/blitzkrieg/checkConsumableInclusivity';
+import { consumableDefinitions } from '../../../../../core/blitzkrieg/consumablesDefinitions';
import { equipmentDefinitions } from '../../../../../core/blitzkrieg/equipmentDefinitions';
import { TIER_ROMAN_NUMERALS } from '../../../../../core/blitzkrieg/tankDefinitions';
import { mutateDuel, useDuel } from '../../../../../stores/duel';
@@ -12,11 +14,13 @@ import {
export function Configure() {
const protagonist = useDuel((state) => state.protagonist!);
const awaitedEquipmentDefinitions = use(equipmentDefinitions);
+ const awaitedConsumableDefinitions = use(consumableDefinitions);
const equipmentRows =
awaitedEquipmentDefinitions.presets[protagonist.tank.equipment];
const equipmentMatrix = useTankopediaTemporary(
(state) => state.equipmentMatrix,
);
+ const consumables = useTankopediaTemporary((state) => state.consumables);
return (
@@ -118,6 +122,35 @@ export function Configure() {
Consumables
+
+ {Object.values(awaitedConsumableDefinitions)
+ .filter((consumable) =>
+ checkConsumableInclusivity(
+ consumable,
+ protagonist.tank,
+ protagonist.gun,
+ ),
+ )
+ .map((consumable) => (
+ {
+ mutateTankopediaTemporary((draft) => {
+ if (draft.consumables.includes(consumable.id)) {
+ draft.consumables = draft.consumables.filter(
+ (id) => id !== consumable.id,
+ );
+ } else {
+ draft.consumables.push(consumable.id);
+ }
+ });
+ }}
+ />
+ ))}
diff --git a/src/components/ModuleButton.tsx b/src/components/ModuleButton.tsx
index 024289bd2..a0f90c8c3 100644
--- a/src/components/ModuleButton.tsx
+++ b/src/components/ModuleButton.tsx
@@ -1,5 +1,5 @@
import { Button, Text } from '@radix-ui/themes';
-import { ComponentProps } from 'react';
+import { CSSProperties, ComponentProps } from 'react';
import { asset } from '../core/blitzkrieg/asset';
type ModuleButtonProps = Omit, 'type'> & {
@@ -21,6 +21,10 @@ type ModuleButtonProps = Omit, 'type'> & {
type: 'equipment';
equipment: number;
}
+ | {
+ type: 'consumable';
+ consumable: number;
+ }
);
export function ModuleButton({
@@ -30,10 +34,26 @@ export function ModuleButton({
last = false,
rowChild,
discriminator,
+ disabled,
...props
}: ModuleButtonProps) {
+ const imageStyles: CSSProperties = {
+ width: 32,
+ height: 32,
+ position: 'absolute',
+ opacity: disabled ? 0.5 : 1,
+ transform: 'scale(0.75)',
+ };
+
+ if (props.type === 'module') {
+ imageStyles.top = '50%';
+ imageStyles.left = '50%';
+ imageStyles.transform = 'translate(calc(-50% + 2px), calc(-50% + 2px))';
+ }
+
return (
{discriminator !== undefined && (
{
+ switch (rule.type) {
+ case 'tier':
+ return rule.min <= tank.tier && tank.tier <= rule.max;
+
+ case 'ids':
+ return rule.ids.includes(tank.id);
+
+ case 'category':
+ throw new SyntaxError('Category filtering found in include rule');
+ }
+ });
+ const excluded = consumable.exclude?.some((rule) => {
+ switch (rule.type) {
+ case 'tier':
+ return rule.min <= tank.tier && tank.tier <= rule.max;
+
+ case 'ids':
+ return rule.ids.includes(tank.id);
+
+ case 'category':
+ return rule.categories.some((category) => {
+ switch (category) {
+ case 'clip':
+ return gun.type !== 'regular';
+ }
+ });
+ }
+ });
+
+ return included && !excluded;
+}
diff --git a/src/core/blitzkrieg/consumablesDefinitions.ts b/src/core/blitzkrieg/consumablesDefinitions.ts
index ca2200c96..fb5d8b7d4 100644
--- a/src/core/blitzkrieg/consumablesDefinitions.ts
+++ b/src/core/blitzkrieg/consumablesDefinitions.ts
@@ -1,5 +1,7 @@
import { asset } from './asset';
+export type ConsumableDefinitionFilterCategory = 'clip';
+
type ConsumableDefinitionFilter =
| {
type: 'tier';
@@ -12,17 +14,19 @@ type ConsumableDefinitionFilter =
}
| {
type: 'category';
- categories: string[];
+ categories: ConsumableDefinitionFilterCategory[];
};
+export interface ConsumableEntry {
+ id: number;
+ name: string;
+ effects?: Record;
+ include: ConsumableDefinitionFilter[];
+ exclude?: ConsumableDefinitionFilter[];
+}
+
export interface ConsumableDefinitions {
- [key: string]: {
- id: number;
- name: string;
- effects?: Record;
- include: ConsumableDefinitionFilter[];
- exclude?: ConsumableDefinitionFilter[];
- };
+ [key: string]: ConsumableEntry;
}
export const consumableDefinitions = fetch(
diff --git a/src/stores/tankopedia.ts b/src/stores/tankopedia.ts
index 53be5d307..ef56760d5 100644
--- a/src/stores/tankopedia.ts
+++ b/src/stores/tankopedia.ts
@@ -57,6 +57,7 @@ interface TankopediaTemporary {
};
equipmentMatrix: EquipmentMatrix;
mode: TankopediaMode;
+ consumables: number[];
}
export const useTankopediaPersistent = create()(
@@ -104,6 +105,7 @@ export const useTankopediaTemporary = create()(
[0, 1, 0],
],
mode: 'model',
+ consumables: [],
})),
);