0
0
mirror of https://github.com/sp-tarkov/server.git synced 2025-02-13 07:10:44 -05:00

Small refactor of armor plate selection code

This commit is contained in:
Chomp 2025-01-16 10:35:00 +00:00
parent 6cbd650330
commit 91f12715fb
2 changed files with 81 additions and 52 deletions

View File

@ -237,14 +237,14 @@ export class BotEquipmentModGenerator {
* Filter a bots plate pool based on its current level * Filter a bots plate pool based on its current level
* @param settings Bot equipment generation settings * @param settings Bot equipment generation settings
* @param modSlot Armor slot being filtered * @param modSlot Armor slot being filtered
* @param existingPlateTplPool Plates tpls to choose from * @param compatiblePlateTplPool Compatible plate tpl pool to choose from
* @param armorItem The armor items db template * @param armorItem The armor items db template
* @returns Array of plate tpls to choose from * @returns Array of plate tpls to choose from
*/ */
protected filterPlateModsForSlotByLevel( protected filterPlateModsForSlotByLevel(
settings: IGenerateEquipmentProperties, settings: IGenerateEquipmentProperties,
modSlot: string, modSlot: string,
existingPlateTplPool: string[], compatiblePlateTplPool: string[],
armorItem: ITemplateItem, armorItem: ITemplateItem,
): IFilterPlateModsForSlotByLevelResult { ): IFilterPlateModsForSlotByLevelResult {
const result: IFilterPlateModsForSlotByLevelResult = { const result: IFilterPlateModsForSlotByLevelResult = {
@ -255,7 +255,7 @@ export class BotEquipmentModGenerator {
// Not pmc or not a plate slot, return original mod pool array // Not pmc or not a plate slot, return original mod pool array
if (!this.itemHelper.isRemovablePlateSlot(modSlot)) { if (!this.itemHelper.isRemovablePlateSlot(modSlot)) {
result.result = Result.NOT_PLATE_HOLDING_SLOT; result.result = Result.NOT_PLATE_HOLDING_SLOT;
result.plateModTpls = existingPlateTplPool; result.plateModTpls = compatiblePlateTplPool;
return result; return result;
} }
@ -269,7 +269,7 @@ export class BotEquipmentModGenerator {
if (!plateSlotWeights) { if (!plateSlotWeights) {
// No weights, return original array of plate tpls // No weights, return original array of plate tpls
result.result = Result.LACKS_PLATE_WEIGHTS; result.result = Result.LACKS_PLATE_WEIGHTS;
result.plateModTpls = existingPlateTplPool; result.plateModTpls = compatiblePlateTplPool;
return result; return result;
} }
@ -279,7 +279,7 @@ export class BotEquipmentModGenerator {
if (!plateWeights) { if (!plateWeights) {
// No weights, return original array of plate tpls // No weights, return original array of plate tpls
result.result = Result.LACKS_PLATE_WEIGHTS; result.result = Result.LACKS_PLATE_WEIGHTS;
result.plateModTpls = existingPlateTplPool; result.plateModTpls = compatiblePlateTplPool;
return result; return result;
} }
@ -288,70 +288,73 @@ export class BotEquipmentModGenerator {
let chosenArmorPlateLevel = this.weightedRandomHelper.getWeightedValue<string>(plateWeights); let chosenArmorPlateLevel = this.weightedRandomHelper.getWeightedValue<string>(plateWeights);
// Convert the array of ids into database items // Convert the array of ids into database items
const platesFromDb = existingPlateTplPool.map((plateTpl) => this.itemHelper.getItem(plateTpl)[1]); const platesFromDb = compatiblePlateTplPool.map((plateTpl) => this.itemHelper.getItem(plateTpl)[1]);
// Filter plates to the chosen level based on its armorClass property // Filter plates to the chosen level based on its armorClass property
let platesOfDesiredLevel = platesFromDb.filter((item) => item._props.armorClass === chosenArmorPlateLevel); let platesOfDesiredLevel = platesFromDb.filter((item) => item._props.armorClass === chosenArmorPlateLevel);
if (platesOfDesiredLevel.length === 0) { if (platesOfDesiredLevel.length > 0) {
// Get lowest and highest plate classes available for this armor // Plates found
const minMaxArmorPlateClass = this.getMinMaxArmorPlateClass(platesFromDb); result.result = Result.SUCCESS;
result.plateModTpls = platesOfDesiredLevel.map((item) => item._id);
let fitPlateIntoArmorAttempts = 0; return result;
const maxTries = 3; }
// Attempt to increase plate class level to attempt getting minimum plate level useable based on original selection // no plates found that fit requirements, lets get creative
for (let i = 0; i < maxTries; i++) {
chosenArmorPlateLevel = (Number.parseInt(chosenArmorPlateLevel) + 1).toString();
// If new chosen plate class is higher than max, then set to min and check if valid // Get lowest and highest plate classes available for this armor
if (Number(chosenArmorPlateLevel) > minMaxArmorPlateClass.max) { const minMaxArmorPlateClass = this.getMinMaxArmorPlateClass(platesFromDb);
chosenArmorPlateLevel = minMaxArmorPlateClass.min.toString();
}
platesOfDesiredLevel = platesFromDb.filter((item) => item._props.armorClass === chosenArmorPlateLevel); // Increment plate class level in attempt to get useable plate
let findCompatiblePlateAttempts = 0;
const maxAttempts = 3;
for (let i = 0; i < maxAttempts; i++) {
chosenArmorPlateLevel = (Number.parseInt(chosenArmorPlateLevel) + 1).toString();
fitPlateIntoArmorAttempts++; // New chosen plate class is higher than max, then set to min and check if valid
// Break loop if valid plate class is found if (Number(chosenArmorPlateLevel) > minMaxArmorPlateClass.max) {
if (platesOfDesiredLevel.length > 0) { chosenArmorPlateLevel = minMaxArmorPlateClass.min.toString();
break; }
}
// If no valid plate class is found in 3 tries then attempt default plates findCompatiblePlateAttempts++;
if (fitPlateIntoArmorAttempts >= maxTries) {
this.logger.debug(
`Plate filter was too restrictive for armor: ${armorItem._name} ${armorItem._id}, unable to find plates of level: ${chosenArmorPlateLevel}. Using mod items default plate`,
);
const relatedItemDbModSlot = armorItem._props.Slots.find(
(slot) => slot._name.toLowerCase() === modSlot,
);
const defaultPlate = relatedItemDbModSlot._props.filters[0].Plate;
if (!defaultPlate) {
// No relevant plate found after filtering AND no default plate
// Last attempt, get default preset and see if it has a plate default platesOfDesiredLevel = platesFromDb.filter((item) => item._props.armorClass === chosenArmorPlateLevel);
const defaultPreset = this.presetHelper.getDefaultPreset(armorItem._id); // Valid plates found, exit
if (defaultPreset) { if (platesOfDesiredLevel.length > 0) {
const relatedPresetSlot = defaultPreset._items.find( break;
(item) => item.slotId?.toLowerCase() === modSlot, }
);
if (relatedPresetSlot) {
result.result = Result.SUCCESS;
result.plateModTpls = [relatedPresetSlot._tpl];
return result; // No valid plate class found in 3 tries, attempt default plates
} if (findCompatiblePlateAttempts >= maxAttempts) {
} this.logger.debug(
// Return Default Preset cause didn't have default plates `Plate filter too restrictive for armor: ${armorItem._name} ${armorItem._id}, unable to find plates of level: ${chosenArmorPlateLevel}, using items default plate`,
result.result = Result.NO_DEFAULT_FILTER; );
return result; const defaultPlate = this.getDefaultPlateTpl(armorItem, modSlot);
} if (defaultPlate) {
// Return Default Plates cause couldn't get lowest level available from original selection // Return Default Plates cause couldn't get lowest level available from original selection
result.result = Result.SUCCESS; result.result = Result.SUCCESS;
result.plateModTpls = [defaultPlate]; result.plateModTpls = [defaultPlate];
return result; return result;
} }
// No plate found after filtering AND no default plate
// Last attempt, get default preset and see if it has a plate default
const defaultPresetPlateSlot = this.getDefaultPresetArmorSlot(armorItem._id, modSlot);
if (defaultPresetPlateSlot) {
// Found a plate, exit
const plateItem = this.itemHelper.getItem(defaultPresetPlateSlot._tpl);
platesOfDesiredLevel = [plateItem[1]];
break;
}
// Everything failed, no default plate or no default preset armor plate
result.result = Result.NO_DEFAULT_FILTER;
return result;
} }
} }
@ -362,6 +365,32 @@ export class BotEquipmentModGenerator {
return result; return result;
} }
/**
* Get the default plate an armor has in its db item
* @param armorItem Item to look up default plate
* @param modSlot front/back
* @returns Tpl of plate
*/
protected getDefaultPlateTpl(armorItem: ITemplateItem, modSlot: string): string | undefined {
const relatedItemDbModSlot = armorItem._props.Slots?.find(
(slot: { _name: string }) => slot._name.toLowerCase() === modSlot,
);
return relatedItemDbModSlot?._props.filters[0].Plate;
}
/**
* Get the matching armor slot from the default preset matching passed in armor tpl
* @param presetItemId Id of preset
* @param modSlot front/back
* @returns Armor IItem
*/
protected getDefaultPresetArmorSlot(armorItemTpl: string, modSlot: string): IItem | undefined {
const defaultPreset = this.presetHelper.getDefaultPreset(armorItemTpl);
return defaultPreset?._items.find((item) => item.slotId?.toLowerCase() === modSlot);
}
/** /**
* Gets the minimum and maximum plate class levels from an array of plates * Gets the minimum and maximum plate class levels from an array of plates
* @param platePool Pool of plates to sort by armorClass to get min and max * @param platePool Pool of plates to sort by armorClass to get min and max

View File

@ -1,6 +1,6 @@
export interface IFilterPlateModsForSlotByLevelResult { export interface IFilterPlateModsForSlotByLevelResult {
result: Result; result: Result;
plateModTpls: string[]; plateModTpls?: string[];
} }
export enum Result { export enum Result {