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