mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 12:09:51 +02:00
feat: consumables
This commit is contained in:
1 parent
62bfab0960
commit
417385f231
14 files changed
+171
-152
No files matched your search
Vendored
+1
@@ -43,6 +43,7 @@
|
|||||||
"Everytime",
|
"Everytime",
|
||||||
"FASTNAME",
|
"FASTNAME",
|
||||||
"FOURCC",
|
"FOURCC",
|
||||||
|
"fsformat",
|
||||||
"fullstats",
|
"fullstats",
|
||||||
"fuzzysort",
|
"fuzzysort",
|
||||||
"gamemode",
|
"gamemode",
|
||||||
|
|||||||
+51
-123
@@ -1,6 +1,6 @@
|
|||||||
# Cedilla Object Notation
|
# Cedilla Object Notation
|
||||||
|
|
||||||
A compact binary representation similar to JSON objects. This documentation uses some funky (yet concise) syntax I developed (don't worry, my C++ isn't that bad!).
|
This documentation uses an experimental binary notation syntax called "Buffer Up".
|
||||||
|
|
||||||
1. JSON compressed as CDON, written directly to disk (a direct 50% reduction).
|
1. JSON compressed as CDON, written directly to disk (a direct 50% reduction).
|
||||||
2. CDON further compressed with LZ4 (a clean 90% reduction).
|
2. CDON further compressed with LZ4 (a clean 90% reduction).
|
||||||
@@ -9,62 +9,43 @@ A compact binary representation similar to JSON objects. This documentation uses
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Primitives
|
FS stands for "Fast String".
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
struct String {
|
primary CedillaObjectNotation {
|
||||||
uint32 length;
|
utf8"CDON" magic;
|
||||||
utf8[length] value;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Reading
|
|
||||||
|
|
||||||
- Everything is little endian
|
|
||||||
- Magic is always `"CDON"`
|
|
||||||
- The index of fast strings must be read as mentioned by the fast string format
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
struct BlitzkriegObjectNotation {
|
|
||||||
ascii[4] magic;
|
|
||||||
Header header;
|
Header header;
|
||||||
Body body;
|
primary Body<header.fsFormat> body;
|
||||||
}
|
}
|
||||||
```
|
|
||||||
|
|
||||||
### Header
|
Header {
|
||||||
|
|
||||||
- Version is always `1`
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
struct Header {
|
|
||||||
uint16 version;
|
uint16 version;
|
||||||
FastStringFormat fastStringFormat;
|
FSFormat fsFormat;
|
||||||
StringTable stringTable;
|
StringTable<fsFormat> stringTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
// read as uint8
|
FSFormat enum<uint8> {
|
||||||
enum FastStringFormat {
|
|
||||||
Format8,
|
Format8,
|
||||||
Format16,
|
Format16,
|
||||||
Format32,
|
Format32,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct StringTable {
|
FSPrimitive<FSFormat fsFormat> match(fsFormat) {
|
||||||
(uint8 | uint16 | uint32) count;
|
Format8: uint8;
|
||||||
|
Format16: uint16;
|
||||||
|
Format32: uint32;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringTable<FSFormat fsFormat> {
|
||||||
|
FSPrimitive<fsFormat> count;
|
||||||
String[count] strings;
|
String[count] strings;
|
||||||
}
|
}
|
||||||
```
|
|
||||||
|
|
||||||
### Body
|
Body<FSFormat fsFormat> {
|
||||||
|
primary Value<fsFormat> object;
|
||||||
```cpp
|
|
||||||
struct Body {
|
|
||||||
Value object;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// read as uint8
|
ValueType enum<uint8> {
|
||||||
enum ValueType {
|
|
||||||
Null,
|
Null,
|
||||||
Boolean,
|
Boolean,
|
||||||
Uint8,
|
Uint8,
|
||||||
@@ -78,96 +59,43 @@ enum ValueType {
|
|||||||
Float32,
|
Float32,
|
||||||
Float64,
|
Float64,
|
||||||
String,
|
String,
|
||||||
FastString,
|
FS,
|
||||||
Array,
|
Array,
|
||||||
Object,
|
Object,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Value = `Value${ValueType}`
|
String {
|
||||||
|
uint32 length;
|
||||||
struct ValueNull {
|
primary utf8<length> value;
|
||||||
ValueType.Null type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ValueBoolean {
|
StringValue Value & { (ValueType.String | ValueType.FS) type }
|
||||||
ValueType.Boolean type;
|
|
||||||
uint8 value;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ValueUint8 {
|
Value<FSFormat fsFormat> {
|
||||||
ValueType.Uint8 type;
|
ValueType type;
|
||||||
uint8 value;
|
match<type> {
|
||||||
}
|
Boolean: { uint8 value; }
|
||||||
|
Uint8: { uint8 value; }
|
||||||
struct ValueUint16 {
|
Uint16: { uint16 value; }
|
||||||
ValueType.Uint16 type;
|
Uint32: { uint32 value; }
|
||||||
uint16 value;
|
Uint64: { uint64 value; }
|
||||||
}
|
Int8: { int8 value; }
|
||||||
|
Int16: { int16 value; }
|
||||||
struct ValueUint32 {
|
Int32: { int32 value; }
|
||||||
ValueType.Uint32 type;
|
Int64: { int64 value; }
|
||||||
uint32 value;
|
Float32: { float32 value; }
|
||||||
}
|
Float64: { float64 value; }
|
||||||
|
String: { String value; }
|
||||||
struct ValueUint64 {
|
FS: { FSPrimitive<fsFormat> index; }
|
||||||
ValueType.Uint64 type;
|
Array: {
|
||||||
uint64 value;
|
uint32 count;
|
||||||
}
|
Value[count] values;
|
||||||
|
}
|
||||||
struct ValueInt8 {
|
Object: {
|
||||||
ValueType.Int8 type;
|
uint32 count;
|
||||||
int8 value;
|
StringValue[count] keys;
|
||||||
}
|
Value[count] values;
|
||||||
|
}
|
||||||
struct ValueInt16 {
|
}
|
||||||
ValueType.Int16 type;
|
|
||||||
int16 value;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ValueInt32 {
|
|
||||||
ValueType.Int32 type;
|
|
||||||
int32 value;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ValueInt64 {
|
|
||||||
ValueType.Int64 type;
|
|
||||||
int64 value;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ValueFloat32 {
|
|
||||||
ValueType.Float32 type;
|
|
||||||
float32 value;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ValueFloat64 {
|
|
||||||
ValueType.Float64 type;
|
|
||||||
float64 value;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ValueString {
|
|
||||||
ValueType.String type;
|
|
||||||
String value;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ValueFastString {
|
|
||||||
ValueType.FastString type;
|
|
||||||
(uint8 | uint16 | uint32) index;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ValueArray {
|
|
||||||
ValueType.Array type;
|
|
||||||
uint32 count;
|
|
||||||
Value[count] values;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ValueObject {
|
|
||||||
ValueType.Object type;
|
|
||||||
uint32 count;
|
|
||||||
(ValueString | ValueFastString)[count] keys;
|
|
||||||
Value[count] values;
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Writing
|
|
||||||
|
|
||||||
It's pretty obvious how to write. Remember that a `FastString` should only be applied to the string table if the string shows up more than once in the body. Otherwise, there's no point and `String` should be used.
|
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { readdir } from 'fs/promises';
|
||||||
|
import sharp from 'sharp';
|
||||||
|
import { readDVPLFile } from '../../src/core/blitz/readDVPLFile';
|
||||||
|
import { readStringDVPL } from '../../src/core/blitz/readStringDVPL';
|
||||||
|
import { commitAssets } from '../../src/core/blitzkrieg/commitAssets';
|
||||||
|
import { FileChange } from '../../src/core/blitzkrieg/commitMultipleFiles';
|
||||||
|
import { DATA, POI } from './constants';
|
||||||
|
|
||||||
|
export async function boosterIcons(production: boolean) {
|
||||||
|
const boosterFiles = (await readdir(`${DATA}/${POI.boosterIcons}`)).filter(
|
||||||
|
(file) => !file.endsWith('@2x.txt.dvpl') && !file.startsWith('texture0'),
|
||||||
|
);
|
||||||
|
const image = sharp(
|
||||||
|
await readDVPLFile(`${DATA}/${POI.boosterIcons}/texture0.packed.webp.dvpl`),
|
||||||
|
);
|
||||||
|
const changes: FileChange[] = [];
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
boosterFiles.map(async (file) => {
|
||||||
|
const name = file.match(/booster_(.+).txt.dvpl/)![1];
|
||||||
|
const sizes = (
|
||||||
|
await readStringDVPL(`${DATA}/${POI.boosterIcons}/${file}`)
|
||||||
|
)
|
||||||
|
.split('\n')[4]
|
||||||
|
.split(' ')
|
||||||
|
.map(Number);
|
||||||
|
const content = (
|
||||||
|
await image
|
||||||
|
.clone()
|
||||||
|
.extract({
|
||||||
|
left: sizes[0],
|
||||||
|
top: sizes[1],
|
||||||
|
width: sizes[2],
|
||||||
|
height: sizes[3],
|
||||||
|
})
|
||||||
|
.toBuffer()
|
||||||
|
).toString('base64');
|
||||||
|
|
||||||
|
changes.push({
|
||||||
|
path: `icons/boosters/${name}.webp`,
|
||||||
|
content,
|
||||||
|
encoding: 'base64',
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await commitAssets('booster icons', changes, production);
|
||||||
|
}
|
||||||
@@ -27,4 +27,5 @@ export const POI = {
|
|||||||
'UI/Screens3/Lobby/Inventory/Equipment/EquipmentItemImage.style.yaml',
|
'UI/Screens3/Lobby/Inventory/Equipment/EquipmentItemImage.style.yaml',
|
||||||
provisionsCommon: 'XML/item_defs/vehicles/common/provisions/common.xml',
|
provisionsCommon: 'XML/item_defs/vehicles/common/provisions/common.xml',
|
||||||
defaultCamoIcon: 'Gfx/UI/Hangar/IconCamouflage.webp',
|
defaultCamoIcon: 'Gfx/UI/Hangar/IconCamouflage.webp',
|
||||||
|
boosterIcons: 'Gfx/Shared/boosters',
|
||||||
} as const;
|
} as const;
|
||||||
@@ -6,7 +6,7 @@ import { readYAMLDVPL } from '../../src/core/blitz/readYAMLDVPL';
|
|||||||
import { commitAssets } from '../../src/core/blitzkrieg/commitAssets';
|
import { commitAssets } from '../../src/core/blitzkrieg/commitAssets';
|
||||||
import { FileChange } from '../../src/core/blitzkrieg/commitMultipleFiles';
|
import { FileChange } from '../../src/core/blitzkrieg/commitMultipleFiles';
|
||||||
import { DATA, POI } from './constants';
|
import { DATA, POI } from './constants';
|
||||||
import { ConsumablesProvisionsCommon } from './definitions';
|
import { ConsumablesCommon, ProvisionsCommon } from './definitions';
|
||||||
|
|
||||||
interface Mappings {
|
interface Mappings {
|
||||||
Header: { version: number };
|
Header: { version: number };
|
||||||
@@ -35,10 +35,10 @@ export async function consumableProvisionIcons(production: boolean) {
|
|||||||
`${DATA}/${POI.equipmentItemImageMappings}.dvpl`,
|
`${DATA}/${POI.equipmentItemImageMappings}.dvpl`,
|
||||||
);
|
);
|
||||||
const consumablesCommon = await readXMLDVPL<{
|
const consumablesCommon = await readXMLDVPL<{
|
||||||
root: ConsumablesProvisionsCommon;
|
root: ConsumablesCommon;
|
||||||
}>(`${DATA}/${POI.consumablesCommon}.dvpl`);
|
}>(`${DATA}/${POI.consumablesCommon}.dvpl`);
|
||||||
const provisionsCommon = await readXMLDVPL<{
|
const provisionsCommon = await readXMLDVPL<{
|
||||||
root: ConsumablesProvisionsCommon;
|
root: ProvisionsCommon;
|
||||||
}>(`${DATA}/${POI.provisionsCommon}.dvpl`);
|
}>(`${DATA}/${POI.provisionsCommon}.dvpl`);
|
||||||
const styleSheets = Object.values(equipmentItemImageMappings.StyleSheets);
|
const styleSheets = Object.values(equipmentItemImageMappings.StyleSheets);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { config } from 'dotenv';
|
import { config } from 'dotenv';
|
||||||
import { argv } from 'process';
|
import { argv } from 'process';
|
||||||
|
import { boosterIcons } from './boosterIcons';
|
||||||
import { camouflageIcons } from './camouflageIcons';
|
import { camouflageIcons } from './camouflageIcons';
|
||||||
import { circleFlags } from './circleFlags';
|
import { circleFlags } from './circleFlags';
|
||||||
import { consumableProvisionIcons } from './consumableProvisionIcons';
|
import { consumableProvisionIcons } from './consumableProvisionIcons';
|
||||||
@@ -35,10 +36,9 @@ const methods = [
|
|||||||
equipmentIcons,
|
equipmentIcons,
|
||||||
consumableProvisionIcons,
|
consumableProvisionIcons,
|
||||||
camouflageIcons,
|
camouflageIcons,
|
||||||
|
boosterIcons,
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const method of methods) {
|
for (const method of methods) {
|
||||||
if (allTargets || targets?.includes(method.name)) {
|
if (allTargets || targets?.includes(method.name)) method(production);
|
||||||
await method(production);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,9 @@ export function Consumables() {
|
|||||||
protagonist.gun,
|
protagonist.gun,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
const cooldownBooster = useTankopediaTemporary(
|
||||||
|
(state) => state.cooldownBooster,
|
||||||
|
);
|
||||||
const hasConsumableDeliverySystem = useEquipment(118);
|
const hasConsumableDeliverySystem = useEquipment(118);
|
||||||
const hasHighEndConsumables = useEquipment(101);
|
const hasHighEndConsumables = useEquipment(101);
|
||||||
|
|
||||||
@@ -42,7 +45,9 @@ export function Consumables() {
|
|||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
cooldown={
|
cooldown={
|
||||||
consumable.cooldown * (hasConsumableDeliverySystem ? 0.85 : 1)
|
consumable.cooldown *
|
||||||
|
(hasConsumableDeliverySystem ? 0.85 : 1) *
|
||||||
|
(1 - cooldownBooster * 0.1)
|
||||||
}
|
}
|
||||||
key={consumable.id}
|
key={consumable.id}
|
||||||
first={index === 0}
|
first={index === 0}
|
||||||
|
|||||||
+37
-2
@@ -1,5 +1,6 @@
|
|||||||
import { Flex, Heading } from '@radix-ui/themes';
|
import { Flex, Heading } from '@radix-ui/themes';
|
||||||
import { CamouflageButton } from '../../../../../../../components/ModuleButtons/CamouflageButton';
|
import { GenericTankComponentButton } from '../../../../../../../components/ModuleButtons/GenericTankComponentButton';
|
||||||
|
import { asset } from '../../../../../../../core/blitzkrieg/asset';
|
||||||
import {
|
import {
|
||||||
mutateTankopediaTemporary,
|
mutateTankopediaTemporary,
|
||||||
useTankopediaTemporary,
|
useTankopediaTemporary,
|
||||||
@@ -8,14 +9,23 @@ import { ConfigurationChildWrapper } from './ConfigurationChildWrapper';
|
|||||||
|
|
||||||
export function Miscellaneous() {
|
export function Miscellaneous() {
|
||||||
const camouflage = useTankopediaTemporary((state) => state.camouflage);
|
const camouflage = useTankopediaTemporary((state) => state.camouflage);
|
||||||
|
const cooldownBooster = useTankopediaTemporary(
|
||||||
|
(state) => state.cooldownBooster,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConfigurationChildWrapper>
|
<ConfigurationChildWrapper>
|
||||||
<Heading size="4">Miscellaneous</Heading>
|
<Heading size="4">Miscellaneous</Heading>
|
||||||
|
|
||||||
<Flex gap="2">
|
<Flex gap="2">
|
||||||
<CamouflageButton
|
<GenericTankComponentButton
|
||||||
|
icon={asset('icons/camo.webp')}
|
||||||
selected={camouflage}
|
selected={camouflage}
|
||||||
|
iconStyles={{
|
||||||
|
top: '50%',
|
||||||
|
left: '50%',
|
||||||
|
transform: 'translate(calc(-50% + 4px), calc(-50% + 4px))',
|
||||||
|
}}
|
||||||
first
|
first
|
||||||
last
|
last
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -24,6 +34,31 @@ export function Miscellaneous() {
|
|||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<GenericTankComponentButton
|
||||||
|
icon={asset('icons/boosters/equipment.webp')}
|
||||||
|
selected={cooldownBooster > 0}
|
||||||
|
first
|
||||||
|
last
|
||||||
|
banner={
|
||||||
|
cooldownBooster === 0
|
||||||
|
? undefined
|
||||||
|
: cooldownBooster === 1
|
||||||
|
? '#afb0abc0'
|
||||||
|
: cooldownBooster === 2
|
||||||
|
? '#5f72cbc0'
|
||||||
|
: '#9b3cc0c0'
|
||||||
|
}
|
||||||
|
iconStyles={{
|
||||||
|
transform: 'translate(-50%, -50%) scale(0.8)',
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
mutateTankopediaTemporary((draft) => {
|
||||||
|
draft.cooldownBooster++;
|
||||||
|
if (draft.cooldownBooster === 4) draft.cooldownBooster = 0;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Flex>
|
</Flex>
|
||||||
</ConfigurationChildWrapper>
|
</ConfigurationChildWrapper>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -119,9 +119,9 @@ export default function Page({ params }: { params: { id: string } }) {
|
|||||||
|
|
||||||
<Flex direction="column" gap="4">
|
<Flex direction="column" gap="4">
|
||||||
<Modules />
|
<Modules />
|
||||||
<Crew />
|
|
||||||
<Equipments />
|
<Equipments />
|
||||||
<Consumables />
|
<Consumables />
|
||||||
|
<Crew />
|
||||||
<Provisions />
|
<Provisions />
|
||||||
<Miscellaneous />
|
<Miscellaneous />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
import { asset } from '../../core/blitzkrieg/asset';
|
|
||||||
import { GenericTankComponentButton } from './GenericTankComponentButton';
|
|
||||||
import { TankComponentButtonProps } from './TankComponentButton';
|
|
||||||
|
|
||||||
export function CamouflageButton(props: TankComponentButtonProps) {
|
|
||||||
return (
|
|
||||||
<GenericTankComponentButton
|
|
||||||
icon={asset('icons/camo.webp')}
|
|
||||||
iconStyles={{
|
|
||||||
top: '50%',
|
|
||||||
left: '50%',
|
|
||||||
transform: 'translate(calc(-50% + 4px), calc(-50% + 4px))',
|
|
||||||
}}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -28,6 +28,7 @@ export function GenericTankComponentButton({
|
|||||||
draggable={false}
|
draggable={false}
|
||||||
src={icon}
|
src={icon}
|
||||||
style={{
|
style={{
|
||||||
|
objectFit: 'contain',
|
||||||
width: 32,
|
width: 32,
|
||||||
height: 32,
|
height: 32,
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export interface TankComponentButtonProps
|
|||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
discriminator?: ReactNode;
|
discriminator?: ReactNode;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
banner?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TankComponentButton({
|
export function TankComponentButton({
|
||||||
@@ -19,6 +20,7 @@ export function TankComponentButton({
|
|||||||
rowChild,
|
rowChild,
|
||||||
discriminator,
|
discriminator,
|
||||||
children,
|
children,
|
||||||
|
banner,
|
||||||
...props
|
...props
|
||||||
}: TankComponentButtonProps) {
|
}: TankComponentButtonProps) {
|
||||||
return (
|
return (
|
||||||
@@ -36,9 +38,24 @@ export function TankComponentButton({
|
|||||||
borderBottomLeftRadius: first ? undefined : 0,
|
borderBottomLeftRadius: first ? undefined : 0,
|
||||||
borderBottomRightRadius: last ? undefined : 0,
|
borderBottomRightRadius: last ? undefined : 0,
|
||||||
margin: rowChild ? -0.5 : 'unset',
|
margin: rowChild ? -0.5 : 'unset',
|
||||||
|
overflow: 'hidden',
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
|
{banner !== undefined && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: 'absolute',
|
||||||
|
width: '200%',
|
||||||
|
height: 12,
|
||||||
|
backgroundColor: banner,
|
||||||
|
top: '50%',
|
||||||
|
left: '-50%',
|
||||||
|
transform: 'translateY(-50%) rotate(-22.5deg)',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{discriminator !== undefined && (
|
{discriminator !== undefined && (
|
||||||
<Text
|
<Text
|
||||||
size="1"
|
size="1"
|
||||||
|
|||||||
@@ -25,5 +25,3 @@ export interface EquipmentDefinitions {
|
|||||||
export const equipmentDefinitions = fetchCdonLz4<EquipmentDefinitions>(
|
export const equipmentDefinitions = fetchCdonLz4<EquipmentDefinitions>(
|
||||||
asset('definitions/equipment.cdon.lz4'),
|
asset('definitions/equipment.cdon.lz4'),
|
||||||
);
|
);
|
||||||
|
|
||||||
equipmentDefinitions.then(console.log);
|
|
||||||
@@ -69,6 +69,7 @@ interface TankopediaTemporary {
|
|||||||
consumables: number[];
|
consumables: number[];
|
||||||
provisions: number[];
|
provisions: number[];
|
||||||
camouflage: boolean;
|
camouflage: boolean;
|
||||||
|
cooldownBooster: number;
|
||||||
shot?: Shot;
|
shot?: Shot;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,6 +179,7 @@ export const useTankopediaTemporary = create<TankopediaTemporary>()(
|
|||||||
consumables: [],
|
consumables: [],
|
||||||
provisions: [],
|
provisions: [],
|
||||||
camouflage: false,
|
camouflage: false,
|
||||||
|
cooldownBooster: 0,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user