feat: provisions

This commit is contained in:
TrèsAbhi committed 2024-01-25 13:10:20 -06:00
1 parent 561b59d235
commit 5bbe93bcec
14 files changed
+233 -53

No files matched your search

@@ -1,8 +1,8 @@
import { Flex, Heading, Text } 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 { checkConsumableProvisionInclusivity } from '../../../../../../../core/blitzkrieg/checkConsumableInclusivity';
import { consumableDefinitions } from '../../../../../../../core/blitzkrieg/consumableDefinitions';
import { useDuel } from '../../../../../../../stores/duel';
import {
mutateTankopediaTemporary,
@@ -15,7 +15,11 @@ export function Consumables() {
const consumables = useTankopediaTemporary((state) => state.consumables);
const consumablesList = Object.values(awaitedConsumableDefinitions).filter(
(consumable) =>
checkConsumableInclusivity(consumable, protagonist.tank, protagonist.gun),
checkConsumableProvisionInclusivity(
consumable,
protagonist.tank,
protagonist.gun,
),
);
return (
@@ -0,0 +1,63 @@
import { Flex, Heading, Text } from '@radix-ui/themes';
import { use } from 'react';
import { ModuleButton } from '../../../../../../../components/ModuleButton';
import { checkConsumableProvisionInclusivity } from '../../../../../../../core/blitzkrieg/checkConsumableInclusivity';
import { provisionDefinitions } from '../../../../../../../core/blitzkrieg/provisionDefinitions';
import { useDuel } from '../../../../../../../stores/duel';
import {
mutateTankopediaTemporary,
useTankopediaTemporary,
} from '../../../../../../../stores/tankopedia';
export function Provisions() {
const protagonist = useDuel((state) => state.protagonist!);
const awaitedProvisionDefinitions = use(provisionDefinitions);
const provisions = useTankopediaTemporary((state) => state.provisions);
const provisionsList = Object.values(awaitedProvisionDefinitions).filter(
(provision) =>
checkConsumableProvisionInclusivity(
provision,
protagonist.tank,
protagonist.gun,
),
);
return (
<Flex gap="2" direction="column">
<Heading size="4">
Provisions <Text color="gray">(max {protagonist.tank.provisions})</Text>
</Heading>
<Flex>
{provisionsList.map((provision, index) => {
const selected = provisions.includes(provision.id);
return (
<ModuleButton
first={index === 0}
last={index === provisionsList.length - 1}
rowChild
type="provision"
disabled={
protagonist.tank.provisions === provisions.length && !selected
}
provision={provision.id}
selected={selected}
onClick={() => {
mutateTankopediaTemporary((draft) => {
if (selected) {
draft.provisions = draft.provisions.filter(
(id) => id !== provision.id,
);
} else {
draft.provisions.push(provision.id);
}
});
}}
/>
);
})}
</Flex>
</Flex>
);
}
@@ -3,6 +3,7 @@ import { Camouflage } from './components/Camouflage';
import { Consumables } from './components/Consumables';
import { Equipments } from './components/Equipments';
import { Modules } from './components/Modules';
import { Provisions } from './components/Provisions';
export function Configure() {
return (
@@ -14,13 +15,10 @@ export function Configure() {
<Equipments />
<Consumables />
<Camouflage />
<Provisions />
</Flex>
{/*
<Flex gap="4" direction="column">
<Heading size="5">Camouflage</Heading>
</Flex>
<Flex gap="4" direction="column">
<Heading size="5">Provisions</Heading>
</Flex> */}
+7 -1
View File
@@ -25,6 +25,10 @@ type ModuleButtonProps = Omit<ComponentProps<typeof Button>, 'type'> & {
type: 'consumable';
consumable: number;
}
| {
type: 'provision';
provision: number;
}
| {
type: 'camouflage';
}
@@ -89,7 +93,9 @@ export function ModuleButton({
? `icons/consumables/${props.consumable}.webp`
: props.type === 'camouflage'
? `icons/camo.webp`
: `icons/equipment/${props.equipment}.webp`,
: props.type === 'provision'
? `icons/provisions/${props.provision}.webp`
: `icons/equipment/${props.equipment}.webp`,
)}
style={imageStyles}
/>
@@ -1,12 +1,13 @@
import { ConsumableEntry } from './consumablesDefinitions';
import { ConsumableEntry } from './consumableDefinitions';
import { ProvisionEntry } from './provisionDefinitions';
import { GunDefinition, TankDefinition } from './tankDefinitions';
export function checkConsumableInclusivity(
consumable: ConsumableEntry,
export function checkConsumableProvisionInclusivity(
consumableProvision: ConsumableEntry | ProvisionEntry,
tank: TankDefinition,
gun: GunDefinition,
) {
const included = consumable.include.every((rule) => {
const included = consumableProvision.include.every((rule) => {
switch (rule.type) {
case 'tier':
return rule.min <= tank.tier && tank.tier <= rule.max;
@@ -18,7 +19,7 @@ export function checkConsumableInclusivity(
throw new SyntaxError('Category filtering found in include rule');
}
});
const excluded = consumable.exclude?.some((rule) => {
const excluded = consumableProvision.exclude?.some((rule) => {
switch (rule.type) {
case 'tier':
return rule.min <= tank.tier && tank.tier <= rule.max;
@@ -1,8 +1,8 @@
import { asset } from './asset';
export type ConsumableDefinitionFilterCategory = 'clip';
export type TankFilterDefinitionCategory = 'clip';
type ConsumableDefinitionFilter =
export type TankFilterDefinition =
| {
type: 'tier';
min: number;
@@ -14,15 +14,15 @@ type ConsumableDefinitionFilter =
}
| {
type: 'category';
categories: ConsumableDefinitionFilterCategory[];
categories: TankFilterDefinitionCategory[];
};
export interface ConsumableEntry {
id: number;
name: string;
effects?: Record<string, number>;
include: ConsumableDefinitionFilter[];
exclude?: ConsumableDefinitionFilter[];
include: TankFilterDefinition[];
exclude?: TankFilterDefinition[];
}
export interface ConsumableDefinitions {
@@ -0,0 +1,21 @@
import { asset } from './asset';
import { TankFilterDefinition } from './consumableDefinitions';
export interface ProvisionEntry {
id: number;
name: string;
effects?: Record<string, number>;
include: TankFilterDefinition[];
exclude?: TankFilterDefinition[];
}
export interface ProvisionDefinitions {
[key: string]: ProvisionEntry;
}
export const provisionDefinitions = fetch(
asset('definitions/provisions.json'),
{
cache: 'no-cache',
},
).then(async (response) => response.json() as Promise<ProvisionDefinitions>);
+1
View File
@@ -20,6 +20,7 @@ export interface TankDefinition {
name_full?: string;
tree_type: TreeTypeString;
consumables: number;
provisions: number;
tier: Tier;
type: TankType;
testing?: boolean;
+2
View File
@@ -58,6 +58,7 @@ interface TankopediaTemporary {
equipmentMatrix: EquipmentMatrix;
mode: TankopediaMode;
consumables: number[];
provisions: number[];
camouflage: boolean;
}
@@ -107,6 +108,7 @@ export const useTankopediaTemporary = create<TankopediaTemporary>()(
],
mode: 'model',
consumables: [],
provisions: [],
camouflage: false,
})),
);