mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 02:37:05 +02:00
feat: consumable timings
This commit is contained in:
1 parent
8f098d435a
commit
809f807da8
11 files changed
+145
-46
No files matched your search
@@ -254,8 +254,7 @@ interface OptionalDeviceSlots {
|
||||
interface OptionalDeviceSlotRow {
|
||||
[key: string]: { device0: string; device1: string };
|
||||
}
|
||||
|
||||
export interface ConsumablesProvisionsCommon {
|
||||
export interface ConsumablesCommon {
|
||||
[key: string]: {
|
||||
id: number;
|
||||
userString: string;
|
||||
@@ -270,8 +269,28 @@ export interface ConsumablesProvisionsCommon {
|
||||
script: {
|
||||
'#text': string;
|
||||
automatic?: boolean;
|
||||
cooldown: number;
|
||||
duration?: number;
|
||||
cooldown?: number;
|
||||
shotEffect?: string;
|
||||
bonusValues?: { [key: string]: number };
|
||||
} & Record<string, string>;
|
||||
};
|
||||
}
|
||||
export interface ProvisionsCommon {
|
||||
[key: string]: {
|
||||
id: number;
|
||||
userString: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
category: string;
|
||||
tags: string;
|
||||
vehicleFilter?: {
|
||||
include: { vehicle: ConsumablesVehicleFilter; nations?: string };
|
||||
exclude?: { vehicle: ConsumablesVehicleFilter; nations?: string };
|
||||
};
|
||||
script: {
|
||||
'#text': string;
|
||||
automatic?: boolean;
|
||||
shotEffect?: string;
|
||||
bonusValues?: { [key: string]: number };
|
||||
} & Record<string, string>;
|
||||
@@ -337,10 +356,10 @@ export async function definitions(production: boolean) {
|
||||
const optionalDeviceSlots = await readXMLDVPL<{
|
||||
root: OptionalDeviceSlots;
|
||||
}>(`${DATA}/${POI.optionalDeviceSlots}.dvpl`);
|
||||
const consumables = await readXMLDVPL<{ root: ConsumablesProvisionsCommon }>(
|
||||
const consumables = await readXMLDVPL<{ root: ConsumablesCommon }>(
|
||||
`${DATA}/${POI.consumablesCommon}.dvpl`,
|
||||
);
|
||||
const provisions = await readXMLDVPL<{ root: ConsumablesProvisionsCommon }>(
|
||||
const provisions = await readXMLDVPL<{ root: ProvisionsCommon }>(
|
||||
`${DATA}/${POI.provisionsCommon}.dvpl`,
|
||||
);
|
||||
|
||||
@@ -793,6 +812,8 @@ export async function definitions(production: boolean) {
|
||||
Object.values(consumables.root).forEach((consumable) => {
|
||||
consumableDefinitions[consumable.id] = {
|
||||
id: consumable.id,
|
||||
cooldown: consumable.script.cooldown,
|
||||
duration: consumable.script.duration,
|
||||
name:
|
||||
strings[consumable.userString] ??
|
||||
missingStrings[consumable.userString] ??
|
||||
|
||||
@@ -3,6 +3,7 @@ import { use } from 'react';
|
||||
import { ConsumableButton } from '../../../../../../../components/ModuleButtons/ConsumableButton';
|
||||
import { checkConsumableProvisionInclusivity } from '../../../../../../../core/blitzkrieg/checkConsumableProvisionInclusivity';
|
||||
import { consumableDefinitions } from '../../../../../../../core/blitzkrieg/consumableDefinitions';
|
||||
import { useEquipment } from '../../../../../../../core/blitzkrieg/useEquipment';
|
||||
import { useDuel } from '../../../../../../../stores/duel';
|
||||
import {
|
||||
mutateTankopediaTemporary,
|
||||
@@ -22,6 +23,8 @@ export function Consumables() {
|
||||
protagonist.gun,
|
||||
),
|
||||
);
|
||||
const hasConsumableDeliverySystem = useEquipment(118);
|
||||
const hasHighEndConsumables = useEquipment(101);
|
||||
|
||||
return (
|
||||
<ConfigurationChildWrapper>
|
||||
@@ -33,6 +36,14 @@ export function Consumables() {
|
||||
|
||||
return (
|
||||
<ConsumableButton
|
||||
duration={
|
||||
typeof consumable.duration === 'number'
|
||||
? consumable.duration * (hasHighEndConsumables ? 0.7 : 1)
|
||||
: undefined
|
||||
}
|
||||
cooldown={
|
||||
consumable.cooldown * (hasConsumableDeliverySystem ? 0.85 : 1)
|
||||
}
|
||||
key={consumable.id}
|
||||
first={index === 0}
|
||||
last={index === consumablesList.length - 1}
|
||||
|
||||
+3
-7
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useDelta } from '../../../../../../../hooks/useDelta';
|
||||
import { Info, InfoProps } from './Info';
|
||||
|
||||
interface InfoWithDeltaProps extends InfoProps {
|
||||
@@ -6,14 +6,10 @@ interface InfoWithDeltaProps extends InfoProps {
|
||||
}
|
||||
|
||||
export function InfoWithDelta({ children, ...props }: InfoWithDeltaProps) {
|
||||
const lastValue = useRef(children);
|
||||
|
||||
useEffect(() => {
|
||||
lastValue.current = children;
|
||||
});
|
||||
const delta = useDelta(children);
|
||||
|
||||
return (
|
||||
<Info {...props} delta={children - lastValue.current}>
|
||||
<Info {...props} delta={delta}>
|
||||
{children}
|
||||
</Info>
|
||||
);
|
||||
|
||||
+15
-13
@@ -1,4 +1,4 @@
|
||||
import { Heading } from '@radix-ui/themes';
|
||||
import { Flex, Heading } from '@radix-ui/themes';
|
||||
import { CamouflageButton } from '../../../../../../../components/ModuleButtons/CamouflageButton';
|
||||
import {
|
||||
mutateTankopediaTemporary,
|
||||
@@ -6,23 +6,25 @@ import {
|
||||
} from '../../../../../../../stores/tankopedia';
|
||||
import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
||||
|
||||
export function Camouflage() {
|
||||
export function Miscellaneous() {
|
||||
const camouflage = useTankopediaTemporary((state) => state.camouflage);
|
||||
|
||||
return (
|
||||
<ConfigurationChildWrapper>
|
||||
<Heading size="4">Camouflage</Heading>
|
||||
<Heading size="4">Miscellaneous</Heading>
|
||||
|
||||
<CamouflageButton
|
||||
selected={camouflage}
|
||||
first
|
||||
last
|
||||
onClick={() => {
|
||||
mutateTankopediaTemporary((draft) => {
|
||||
draft.camouflage = !camouflage;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<Flex gap="2">
|
||||
<CamouflageButton
|
||||
selected={camouflage}
|
||||
first
|
||||
last
|
||||
onClick={() => {
|
||||
mutateTankopediaTemporary((draft) => {
|
||||
draft.camouflage = !camouflage;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</ConfigurationChildWrapper>
|
||||
);
|
||||
}
|
||||
@@ -8,10 +8,10 @@ import { mutateDuel, useDuel } from '../../../../stores/duel';
|
||||
import { mutateTankopediaTemporary } from '../../../../stores/tankopedia';
|
||||
import { AntagonistBar } from './components/AntagonistBar';
|
||||
import { Characteristics } from './components/Characteristics';
|
||||
import { Camouflage } from './components/Characteristics/components/Camouflage';
|
||||
import { Consumables } from './components/Characteristics/components/Consumables';
|
||||
import { Crew } from './components/Characteristics/components/Crew';
|
||||
import { Equipments } from './components/Characteristics/components/Equipments';
|
||||
import { Miscellaneous } from './components/Characteristics/components/Miscellaneous';
|
||||
import { Modules } from './components/Characteristics/components/Modules';
|
||||
import { Provisions } from './components/Characteristics/components/Provisions';
|
||||
import { TankSandbox } from './components/Model/TankSandbox';
|
||||
@@ -119,11 +119,11 @@ export default function Page({ params }: { params: { id: string } }) {
|
||||
|
||||
<Flex direction="column" gap="4">
|
||||
<Modules />
|
||||
<Equipments />
|
||||
<Crew />
|
||||
<Equipments />
|
||||
<Consumables />
|
||||
<Provisions />
|
||||
<Camouflage />
|
||||
<Miscellaneous />
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
|
||||
@@ -1,19 +1,62 @@
|
||||
import { ClockIcon, ReloadIcon } from '@radix-ui/react-icons';
|
||||
import { Flex, Text } from '@radix-ui/themes';
|
||||
import { asset } from '../../core/blitzkrieg/asset';
|
||||
import { useDelta } from '../../hooks/useDelta';
|
||||
import { GenericTankComponentButton } from './GenericTankComponentButton';
|
||||
import { TankComponentButtonProps } from './TankComponentButton';
|
||||
|
||||
interface ConsumableButtonProps extends TankComponentButtonProps {
|
||||
consumable: number;
|
||||
duration?: number;
|
||||
cooldown: number;
|
||||
}
|
||||
|
||||
export function ConsumableButton({
|
||||
consumable,
|
||||
cooldown,
|
||||
duration,
|
||||
...props
|
||||
}: ConsumableButtonProps) {
|
||||
const cooldownDelta = useDelta(cooldown);
|
||||
const durationDelta = useDelta(duration ?? 0);
|
||||
|
||||
return (
|
||||
<GenericTankComponentButton
|
||||
icon={asset(`icons/consumables/${consumable}.webp`)}
|
||||
{...props}
|
||||
/>
|
||||
>
|
||||
<Flex direction="column" style={{ transform: 'translateX(-10px)' }}>
|
||||
{duration && (
|
||||
<Text
|
||||
color={
|
||||
durationDelta < 0
|
||||
? 'green'
|
||||
: durationDelta > 0
|
||||
? 'tomato'
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<Flex align="center" gap="1" style={{ marginBottom: -4 }}>
|
||||
<ClockIcon />
|
||||
{Math.round(duration)}s
|
||||
</Flex>
|
||||
</Text>
|
||||
)}
|
||||
<Text
|
||||
color={
|
||||
cooldownDelta < 0
|
||||
? 'green'
|
||||
: cooldownDelta > 0
|
||||
? 'tomato'
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<Flex align="center" gap="1">
|
||||
<ReloadIcon />
|
||||
{Math.round(cooldown)}s
|
||||
</Flex>
|
||||
</Text>
|
||||
</Flex>
|
||||
</GenericTankComponentButton>
|
||||
);
|
||||
}
|
||||
@@ -12,22 +12,35 @@ interface GenericTankComponentButtonProps extends TankComponentButtonProps {
|
||||
export function GenericTankComponentButton({
|
||||
icon,
|
||||
iconStyles,
|
||||
children,
|
||||
...props
|
||||
}: GenericTankComponentButtonProps) {
|
||||
return (
|
||||
<TankComponentButton disabled={props.disabled} {...props}>
|
||||
<img
|
||||
draggable={false}
|
||||
src={icon}
|
||||
<TankComponentButton {...props}>
|
||||
<div
|
||||
style={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
position: 'absolute',
|
||||
opacity: props.disabled ? 0.5 : 1,
|
||||
transform: 'scale(0.75)',
|
||||
...iconStyles,
|
||||
width: 48,
|
||||
height: 40,
|
||||
position: 'relative',
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<img
|
||||
draggable={false}
|
||||
src={icon}
|
||||
style={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
position: 'absolute',
|
||||
opacity: props.disabled ? 0.5 : 1,
|
||||
transform: 'translate(-50%, -50%) scale(0.75)',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
...iconStyles,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{children}
|
||||
</TankComponentButton>
|
||||
);
|
||||
}
|
||||
@@ -18,19 +18,17 @@ export function TankComponentButton({
|
||||
last = false,
|
||||
rowChild,
|
||||
discriminator,
|
||||
disabled,
|
||||
children,
|
||||
...props
|
||||
}: TankComponentButtonProps) {
|
||||
return (
|
||||
<Button
|
||||
disabled={disabled}
|
||||
radius="medium"
|
||||
color={selected ? undefined : 'gray'}
|
||||
variant={selected ? 'surface' : 'soft'}
|
||||
style={{
|
||||
padding: 0,
|
||||
width: 48,
|
||||
minWidth: 48,
|
||||
height: 40,
|
||||
position: 'relative',
|
||||
borderTopLeftRadius: first ? undefined : 0,
|
||||
@@ -39,10 +37,8 @@ export function TankComponentButton({
|
||||
borderBottomRightRadius: last ? undefined : 0,
|
||||
margin: rowChild ? -0.5 : 'unset',
|
||||
}}
|
||||
{...(props as unknown as ComponentProps<typeof Button>)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
|
||||
{discriminator !== undefined && (
|
||||
<Text
|
||||
size="1"
|
||||
@@ -59,6 +55,8 @@ export function TankComponentButton({
|
||||
{discriminator}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -25,6 +25,8 @@ export type TankFilterDefinition =
|
||||
export interface ConsumableEntry {
|
||||
id: number;
|
||||
name: string;
|
||||
cooldown: number;
|
||||
duration?: number;
|
||||
include: TankFilterDefinition[];
|
||||
exclude?: TankFilterDefinition[];
|
||||
}
|
||||
|
||||
@@ -25,3 +25,5 @@ export interface EquipmentDefinitions {
|
||||
export const equipmentDefinitions = fetchCdonLz4<EquipmentDefinitions>(
|
||||
asset('definitions/equipment.cdon.lz4'),
|
||||
);
|
||||
|
||||
equipmentDefinitions.then(console.log);
|
||||
@@ -0,0 +1,11 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
export function useDelta(value: number) {
|
||||
const lastValue = useRef(value);
|
||||
|
||||
useEffect(() => {
|
||||
lastValue.current = value;
|
||||
});
|
||||
|
||||
return value - lastValue.current;
|
||||
}
|
||||
Reference in new issue
Block a user