From 310762d66dbf5bce2a98609e81c0edd1ea52c538 Mon Sep 17 00:00:00 2001 From: Dev Date: Thu, 18 Jan 2024 23:25:08 +0000 Subject: [PATCH] Refactor airdrop loot generation to account for armor plate changes --- project/assets/configs/airdrop.json | 32 ++++++--- project/src/controllers/LocationController.ts | 3 +- project/src/generators/LootGenerator.ts | 67 ++++++++++++++----- .../src/models/spt/config/IAirdropConfig.ts | 4 +- .../src/models/spt/services/LootRequest.ts | 3 +- 5 files changed, 81 insertions(+), 28 deletions(-) diff --git a/project/assets/configs/airdrop.json b/project/assets/configs/airdrop.json index 076e251c..35ff5c59 100644 --- a/project/assets/configs/airdrop.json +++ b/project/assets/configs/airdrop.json @@ -30,9 +30,13 @@ }, "loot": { "mixed": { - "presetCount": { + "weaponPresetCount": { "min": 3, "max": 5 + }, + "armorPresetCount": { + "min": 1, + "max": 3 }, "itemCount": { "min": 12, @@ -155,9 +159,13 @@ "allowBossItems": false }, "weaponArmor": { - "presetCount": { + "weaponPresetCount": { "min": 6, "max": 8 + }, + "armorPresetCount": { + "min": 3, + "max": 5 }, "itemCount": { "min": 10, @@ -251,9 +259,13 @@ "allowBossItems": false }, "foodMedical": { - "presetCount": { - "min": 3, - "max": 5 + "weaponPresetCount": { + "min": 0, + "max": 0 + }, + "armorPresetCount": { + "min": 0, + "max": 0 }, "itemCount": { "min": 17, @@ -334,9 +346,13 @@ "allowBossItems": false }, "barter": { - "presetCount": { - "min": 3, - "max": 5 + "weaponPresetCount": { + "min": 0, + "max": 0 + }, + "armorPresetCount": { + "min": 0, + "max": 0 }, "itemCount": { "min": 16, diff --git a/project/src/controllers/LocationController.ts b/project/src/controllers/LocationController.ts index 3cf1077b..74dc16f9 100644 --- a/project/src/controllers/LocationController.ts +++ b/project/src/controllers/LocationController.ts @@ -208,7 +208,8 @@ export class LocationController } return { - presetCount: lootSettingsByType.presetCount, + weaponPresetCount: lootSettingsByType.weaponPresetCount, + armorPresetCount: lootSettingsByType.armorPresetCount, itemCount: lootSettingsByType.itemCount, weaponCrateCount: lootSettingsByType.weaponCrateCount, itemBlacklist: lootSettingsByType.itemBlacklist, diff --git a/project/src/generators/LootGenerator.ts b/project/src/generators/LootGenerator.ts index 2deaa84b..5973ceaf 100644 --- a/project/src/generators/LootGenerator.ts +++ b/project/src/generators/LootGenerator.ts @@ -105,14 +105,27 @@ export class LootGenerator } } - const globalDefaultPresets = Object.entries(tables.globals.ItemPresets).filter((x) => - x[1]._encyclopedia !== undefined - ); - const randomisedPresetCount = this.randomUtil.getInt(options.presetCount.min, options.presetCount.max); + const globalDefaultPresets = Object.values(this.presetHelper.getDefaultPresets()); + + // Filter default presets to just weapons + const weaponDefaultPresets = globalDefaultPresets.filter(preset => this.itemHelper.isOfBaseclass(preset._encyclopedia, BaseClasses.WEAPON)); + const randomisedWeaponPresetCount = this.randomUtil.getInt(options.weaponPresetCount.min, options.weaponPresetCount.max); const itemBlacklistArray = Array.from(itemBlacklist); - for (let index = 0; index < randomisedPresetCount; index++) + for (let index = 0; index < randomisedWeaponPresetCount; index++) { - if (!this.findAndAddRandomPresetToLoot(globalDefaultPresets, itemTypeCounts, itemBlacklistArray, result)) + if (!this.findAndAddRandomPresetToLoot(weaponDefaultPresets, itemTypeCounts, itemBlacklistArray, result)) + { + index--; + } + } + + // Filter default presets to just armors and then filter again by protection level + const armorDefaultPresets = globalDefaultPresets.filter(preset => this.itemHelper.armorItemCanHoldMods(preset._encyclopedia)); + const levelFilteredArmorPresets = armorDefaultPresets.filter(armor => this.armorIsDesiredProtectionLevel(armor, options)); + const randomisedArmorPresetCount = this.randomUtil.getInt(options.armorPresetCount.min, options.armorPresetCount.max); + for (let index = 0; index < randomisedArmorPresetCount; index++) + { + if (!this.findAndAddRandomPresetToLoot(levelFilteredArmorPresets, itemTypeCounts, itemBlacklistArray, result)) { index--; } @@ -121,6 +134,29 @@ export class LootGenerator return result; } + /** + * Filter armor items by their main plates protection level + * @param armor Armor preset + * @param options Loot request options + * @returns True item passes checks + */ + protected armorIsDesiredProtectionLevel(armor: IPreset, options: LootRequest): boolean + { + const frontPlate = armor._items.find(mod => mod?.slotId?.toLowerCase() === "front_plate"); + if (frontPlate) + { + const plateDb = this.itemHelper.getItem(frontPlate._tpl); + return options.armorLevelWhitelist.includes(Number.parseInt(plateDb[1]._props.armorClass as any)); + } + + const helmetTop = armor._items.find(mod => mod?.slotId?.toLowerCase() === "helmet_top"); + if (helmetTop) + { + const plateDb = this.itemHelper.getItem(helmetTop._tpl); + return options.armorLevelWhitelist.includes(Number.parseInt(plateDb[1]._props.armorClass as any)); + } + } + /** * Construct item limit record to hold max and current item count for each item type * @param limits limits as defined in config @@ -160,6 +196,12 @@ export class LootGenerator return false; } + // Skip armors as they need to come from presets + if (this.itemHelper.armorItemCanHoldMods(randomItem._id)) + { + return false; + } + const newLootItem: LootItem = { id: this.hashUtil.generate(), tpl: randomItem._id, @@ -167,15 +209,6 @@ export class LootGenerator stackCount: 1, }; - // Check if armor has level in allowed whitelist - if (randomItem._parent === BaseClasses.ARMOR || randomItem._parent === BaseClasses.VEST) - { - if (!options.armorLevelWhitelist.includes(Number(randomItem._props.armorClass))) - { - return false; - } - } - // Special case - handle items that need a stackcount > 1 if (randomItem._props.StackMaxSize > 1) { @@ -224,14 +257,14 @@ export class LootGenerator * @returns true if preset was valid and added to pool */ protected findAndAddRandomPresetToLoot( - globalDefaultPresets: [string, IPreset][], + globalDefaultPresets: IPreset[], itemTypeCounts: Record, itemBlacklist: string[], result: LootItem[], ): boolean { // Choose random preset and get details from item.json using encyclopedia value (encyclopedia === tplId) - const randomPreset = this.randomUtil.getArrayValue(globalDefaultPresets)[1]; + const randomPreset = this.randomUtil.getArrayValue(globalDefaultPresets); if (!randomPreset?._encyclopedia) { this.logger.debug(`Airdrop - preset with id: ${randomPreset._id} lacks encyclopedia property, skipping`); diff --git a/project/src/models/spt/config/IAirdropConfig.ts b/project/src/models/spt/config/IAirdropConfig.ts index b785aaee..1eac1aac 100644 --- a/project/src/models/spt/config/IAirdropConfig.ts +++ b/project/src/models/spt/config/IAirdropConfig.ts @@ -44,7 +44,9 @@ export interface AirdropChancePercent export interface AirdropLoot { /** Min/max of weapons inside crate */ - presetCount?: MinMax; + weaponPresetCount?: MinMax; + /** Min/max of armors (head/chest/rig) inside crate */ + armorPresetCount?: MinMax; /** Min/max of items inside crate */ itemCount: MinMax; /** Min/max of sealed weapon boxes inside crate */ diff --git a/project/src/models/spt/services/LootRequest.ts b/project/src/models/spt/services/LootRequest.ts index 442b9659..eb27ce26 100644 --- a/project/src/models/spt/services/LootRequest.ts +++ b/project/src/models/spt/services/LootRequest.ts @@ -2,7 +2,8 @@ import { MinMax } from "@spt-aki/models/common/MinMax"; export interface LootRequest { - presetCount: MinMax; + weaponPresetCount: MinMax; + armorPresetCount: MinMax; itemCount: MinMax; weaponCrateCount: MinMax; itemBlacklist: string[];