feat: consumable buttons

This commit is contained in:
TrèsAbhi committed 2024-01-25 11:50:43 -06:00
1 parent a6c3ac36ef
commit ad1ad80026
7 files changed
+120 -26

No files matched your search

+1
View File
@@ -46,6 +46,7 @@
"glsl",
"GLTF",
"immer",
"Inclusivity",
"JOINTINDEX",
"JOINTWEIGHT",
"kranvagn",
+7 -2
View File
@@ -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');
}
@@ -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 (
<Flex gap="4" direction="column">
@@ -118,6 +122,35 @@ export function Configure() {
<Flex gap="2" direction="column">
<Heading size="4">Consumables</Heading>
{Object.values(awaitedConsumableDefinitions)
.filter((consumable) =>
checkConsumableInclusivity(
consumable,
protagonist.tank,
protagonist.gun,
),
)
.map((consumable) => (
<ModuleButton
first
last
type="consumable"
consumable={consumable.id}
selected={consumables.includes(consumable.id)}
onClick={() => {
mutateTankopediaTemporary((draft) => {
if (draft.consumables.includes(consumable.id)) {
draft.consumables = draft.consumables.filter(
(id) => id !== consumable.id,
);
} else {
draft.consumables.push(consumable.id);
}
});
}}
/>
))}
</Flex>
</Flex>
+25 -16
View File
@@ -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<ComponentProps<typeof Button>, 'type'> & {
@@ -21,6 +21,10 @@ type ModuleButtonProps = Omit<ComponentProps<typeof Button>, '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 (
<Button
disabled={disabled}
radius="medium"
color={selected ? undefined : 'gray'}
variant={selected ? 'surface' : 'soft'}
@@ -57,22 +77,11 @@ export function ModuleButton({
? `icons/modules/${props.module}.webp`
: props.type === 'shell'
? `icons/shells/${props.shell}.webp`
: `icons/equipment/${props.equipment}.webp`,
: props.type === 'consumable'
? `icons/consumables/${props.consumable}.webp`
: `icons/equipment/${props.equipment}.webp`,
)}
style={{
width: 32,
height: 32,
position: 'absolute',
...(props.type === 'module'
? {
top: '50%',
left: '50%',
transform: 'translate(calc(-50% + 2px), calc(-50% + 2px))',
}
: {
transform: 'scale(0.75)',
}),
}}
style={imageStyles}
/>
{discriminator !== undefined && (
<Text
@@ -0,0 +1,40 @@
import { ConsumableEntry } from './consumablesDefinitions';
import { GunDefinition, TankDefinition } from './tankDefinitions';
export function checkConsumableInclusivity(
consumable: ConsumableEntry,
tank: TankDefinition,
gun: GunDefinition,
) {
const included = consumable.include.every((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':
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;
}
+12 -8
View File
@@ -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<string, number>;
include: ConsumableDefinitionFilter[];
exclude?: ConsumableDefinitionFilter[];
}
export interface ConsumableDefinitions {
[key: string]: {
id: number;
name: string;
effects?: Record<string, number>;
include: ConsumableDefinitionFilter[];
exclude?: ConsumableDefinitionFilter[];
};
[key: string]: ConsumableEntry;
}
export const consumableDefinitions = fetch(
+2
View File
@@ -57,6 +57,7 @@ interface TankopediaTemporary {
};
equipmentMatrix: EquipmentMatrix;
mode: TankopediaMode;
consumables: number[];
}
export const useTankopediaPersistent = create<TankopediaPersistent>()(
@@ -104,6 +105,7 @@ export const useTankopediaTemporary = create<TankopediaTemporary>()(
[0, 1, 0],
],
mode: 'model',
consumables: [],
})),
);