feat: provision filtering

This commit is contained in:
TrèsAbhi committed 2024-01-25 13:51:30 -06:00
1 parent 5bbe93bcec
commit 8f8c91e0b0
7 files changed
+123 -24

No files matched your search

+2 -1
View File
@@ -21,9 +21,10 @@ export const POI = {
optionalDevicesImage:
'Gfx/UI/InventoryIcons/Big/OptionalDevices/texture0.packed.webp',
consumablesCommon: 'XML/item_defs/vehicles/common/consumables/common.xml',
provisionsCommon: 'XML/item_defs/vehicles/common/provisions/common.xml',
consumableIcons: 'Gfx/UI/InventoryIcons/Big/Consumables',
provisionIcons: 'Gfx/UI/InventoryIcons/Big/Provisions',
equipmentItemImageMappings:
'UI/Screens3/Lobby/Inventory/Equipment/EquipmentItemImage.style.yaml',
provisionsCommon: 'XML/item_defs/vehicles/common/provisions/common.xml',
defaultCamoIcon: 'Gfx/UI/Hangar/IconCamouflage.webp',
} as const;
@@ -16,15 +16,20 @@ interface Mappings {
}[];
}
export async function consumableIcons(production: boolean) {
console.log('Building consumable icons...');
export async function consumableProvisionIcons(production: boolean) {
console.log('Building consumable and provision icons...');
const changes: FileChange[] = [];
const texture = sharp(
const consumablesTexture = sharp(
await readDVPLFile(
`${DATA}/${POI.consumableIcons}/texture0.packed.webp.dvpl`,
),
);
const provisionsTexture = sharp(
await readDVPLFile(
`${DATA}/${POI.provisionIcons}/texture0.packed.webp.dvpl`,
),
);
const equipmentItemImageMappings = await readYAMLDVPL<Mappings>(
`${DATA}/${POI.equipmentItemImageMappings}.dvpl`,
@@ -32,6 +37,9 @@ export async function consumableIcons(production: boolean) {
const consumablesCommon = await readXMLDVPL<{
root: ConsumablesProvisionsCommon;
}>(`${DATA}/${POI.consumablesCommon}.dvpl`);
const provisionsCommon = await readXMLDVPL<{
root: ConsumablesProvisionsCommon;
}>(`${DATA}/${POI.provisionsCommon}.dvpl`);
const styleSheets = Object.values(equipmentItemImageMappings.StyleSheets);
await Promise.all(
@@ -43,20 +51,21 @@ export async function consumableIcons(production: boolean) {
);
if (!styleSheet) {
throw new Error(
`No style sheet found for consumable ${consumable.icon}`,
console.warn(
`No style sheet found for consumable ${consumable.icon}; skipping...`,
);
return;
}
const configPath = styleSheet.properties['bg-sprite']
.replace('~res:/', '')
.replace('.psd', '.txt');
.replace('.psd', '')
.replace('.txt', '');
if (configPath.startsWith('Gfx/Shared')) {
const imagePath = `${configPath}.packed.webp.dvpl`;
const content = (await readDVPLFile(`${DATA}/${imagePath}`)).toString(
'base64',
);
const content = (
await readDVPLFile(`${DATA}/${configPath}.packed.webp.dvpl`)
).toString('base64');
changes.push({
path: `icons/consumables/${consumable.id}.webp`,
@@ -64,12 +73,12 @@ export async function consumableIcons(production: boolean) {
content,
});
} else {
const sizes = (await readStringDVPL(`${DATA}/${configPath}.dvpl`))
const sizes = (await readStringDVPL(`${DATA}/${configPath}.txt.dvpl`))
.split('\n')[4]
.split(' ')
.map(Number);
const content = (
await texture
await consumablesTexture
.clone()
.extract({
left: sizes[0],
@@ -89,5 +98,61 @@ export async function consumableIcons(production: boolean) {
}),
);
await commitAssets('consumable icons', changes, production);
await Promise.all(
Object.values(provisionsCommon.root).map(async (provision) => {
const styleSheet = styleSheets.find((styleSheet) =>
styleSheet.selectors.includes(
`.equipment_item.${provision.icon} * .equipment-img`,
),
);
if (!styleSheet) {
console.warn(
`No style sheet found for provision ${provision.icon}; skipping...`,
);
return;
}
const configPath = styleSheet.properties['bg-sprite']
.replace('~res:/', '')
.replace('.psd', '')
.replace('.txt', '');
if (configPath.startsWith('Gfx/Shared')) {
const content = (
await readDVPLFile(`${DATA}/${configPath}.packed.webp.dvpl`)
).toString('base64');
changes.push({
path: `icons/provisions/${provision.id}.webp`,
encoding: 'base64',
content,
});
} else {
const sizes = (await readStringDVPL(`${DATA}/${configPath}.txt.dvpl`))
.split('\n')[4]
.split(' ')
.map(Number);
const content = (
await provisionsTexture
.clone()
.extract({
left: sizes[0],
top: sizes[1],
width: sizes[2],
height: sizes[3],
})
.toBuffer()
).toString('base64');
changes.push({
path: `icons/provisions/${provision.id}.webp`,
encoding: 'base64',
content,
});
}
}),
);
await commitAssets('consumable and provision icons', changes, production);
}
+31 -3
View File
@@ -201,8 +201,8 @@ export interface ConsumablesProvisionsCommon {
category: string;
tags: string;
vehicleFilter?: {
include: { vehicle: ConsumablesFilter };
exclude?: { vehicle: ConsumablesFilter };
include: { vehicle: ConsumablesVehicleFilter; nations?: string };
exclude?: { vehicle: ConsumablesVehicleFilter; nations?: string };
};
script: {
'#text': string;
@@ -215,7 +215,7 @@ export interface ConsumablesProvisionsCommon {
};
}
type ConsumablesFilter =
type ConsumablesVehicleFilter =
| { minLevel: number; maxLevel: number }
| { name: string }
| { extendedTags: string };
@@ -669,6 +669,13 @@ export async function definitions(production: boolean) {
ids: includeRaw.name.split(' ').map((key) => tankStringIdMap[key]),
});
} else throw new SyntaxError('Unhandled include type');
if (consumable.vehicleFilter?.include.nations) {
consumableDefinitions[consumable.id].include.push({
type: 'nation',
nations: consumable.vehicleFilter.include.nations.split(' '),
});
}
}
if (excludeRaw) {
@@ -687,6 +694,13 @@ export async function definitions(production: boolean) {
) as TankFilterDefinitionCategory[],
});
} else throw new SyntaxError('Unhandled exclude type');
if (consumable.vehicleFilter?.exclude?.nations) {
consumableDefinitions[consumable.id].exclude!.push({
type: 'nation',
nations: consumable.vehicleFilter.exclude.nations.split(' '),
});
}
}
Object.entries({
@@ -736,6 +750,13 @@ export async function definitions(production: boolean) {
ids: includeRaw.name.split(' ').map((key) => tankStringIdMap[key]),
});
} else throw new SyntaxError('Unhandled include type');
if (provision.vehicleFilter?.include.nations) {
provisionDefinitions[provision.id].include.push({
type: 'nation',
nations: provision.vehicleFilter.include.nations.split(' '),
});
}
}
if (excludeRaw) {
@@ -754,6 +775,13 @@ export async function definitions(production: boolean) {
) as TankFilterDefinitionCategory[],
});
} else throw new SyntaxError('Unhandled exclude type');
if (provision.vehicleFilter?.exclude?.nations) {
provisionDefinitions[provision.id].exclude!.push({
type: 'nation',
nations: provision.vehicleFilter.exclude.nations.split(' '),
});
}
}
Object.entries({
+2 -2
View File
@@ -2,7 +2,7 @@ import { config } from 'dotenv';
import { argv } from 'process';
import { camouflageIcons } from './camouflageIcons';
import { circleFlags } from './circleFlags';
import { consumableIcons } from './consumableIcons';
import { consumableProvisionIcons } from './consumableProvisionIcons';
import { definitions } from './definitions';
import { equipmentIcons } from './equipmentIcons';
import { buildModuleIcons } from './moduleIcons';
@@ -33,7 +33,7 @@ const methods = [
buildShellIcons,
buildModuleIcons,
equipmentIcons,
consumableIcons,
consumableProvisionIcons,
camouflageIcons,
];
@@ -17,11 +17,6 @@ export function Configure() {
<Camouflage />
<Provisions />
</Flex>
{/*
<Flex gap="4" direction="column">
<Heading size="5">Provisions</Heading>
</Flex> */}
</Flex>
);
}
@@ -15,6 +15,9 @@ export function checkConsumableProvisionInclusivity(
case 'ids':
return rule.ids.includes(tank.id);
case 'nation':
return rule.nations.includes(tank.nation);
case 'category':
throw new SyntaxError('Category filtering found in include rule');
}
@@ -27,6 +30,9 @@ export function checkConsumableProvisionInclusivity(
case 'ids':
return rule.ids.includes(tank.id);
case 'nation':
return rule.nations.includes(tank.nation);
case 'category':
return rule.categories.some((category) => {
switch (category) {
@@ -15,6 +15,10 @@ export type TankFilterDefinition =
| {
type: 'category';
categories: TankFilterDefinitionCategory[];
}
| {
type: 'nation';
nations: string[];
};
export interface ConsumableEntry {