Further improvements to weapon mod generation
This commit is contained in:
parent
af33625a5c
commit
2f412641b2
@ -159,7 +159,7 @@ export class BotEquipmentModGenerator {
|
||||
// Choose random mod from pool and check its compatibility
|
||||
let modTpl: string | undefined;
|
||||
let found = false;
|
||||
const exhaustableModPool = new ExhaustableArray<string>(modPoolToChooseFrom, this.randomUtil, this.cloner);
|
||||
const exhaustableModPool = this.createExhaustableArray(modPoolToChooseFrom);
|
||||
while (exhaustableModPool.hasValues()) {
|
||||
modTpl = exhaustableModPool.getRandomValue();
|
||||
if (
|
||||
@ -788,7 +788,7 @@ export class BotEquipmentModGenerator {
|
||||
}
|
||||
|
||||
// Pick random mod that's compatible
|
||||
const chosenModResult = this.pickWeaponModTplForSlotFromPool(
|
||||
const chosenModResult = this.getCompatibleWeaponModTplForSlotFromPool(
|
||||
modPool,
|
||||
parentSlot,
|
||||
request.modSpawnResult,
|
||||
@ -803,7 +803,6 @@ export class BotEquipmentModGenerator {
|
||||
// Log if mod chosen was incompatible
|
||||
if (chosenModResult.incompatible && parentSlot._required) {
|
||||
this.logger.debug(chosenModResult.reason);
|
||||
// this.logger.debug(`Weapon: ${weapon.map(x => `${x._tpl} ${x.slotId ?? ""}`).join(",")}`)
|
||||
}
|
||||
|
||||
// Get random mod to attach from items db for required slots if none found above
|
||||
@ -831,7 +830,8 @@ export class BotEquipmentModGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Choose a weapon mod tpl for a given slot from a pool of choices
|
||||
* Checks chosen tpl is compatible with all existing weapon items
|
||||
* @param modPool Pool of mods that can be picked from
|
||||
* @param parentSlot Slot the picked mod will have as a parent
|
||||
* @param choiceTypeEnum How should chosen tpl be treated: DEFAULT_MOD/SPAWN/SKIP
|
||||
@ -839,16 +839,14 @@ export class BotEquipmentModGenerator {
|
||||
* @param modSlotName Name of slot picked mod will be placed into
|
||||
* @returns Chosen weapon details
|
||||
*/
|
||||
protected pickWeaponModTplForSlotFromPool(
|
||||
protected getCompatibleWeaponModTplForSlotFromPool(
|
||||
modPool: string[],
|
||||
parentSlot: Slot,
|
||||
choiceTypeEnum: ModSpawn,
|
||||
weapon: Item[],
|
||||
modSlotName: string,
|
||||
): IChooseRandomCompatibleModResult {
|
||||
let chosenTpl: string;
|
||||
|
||||
// Filter out incompatable mods from pool
|
||||
// Filter out incompatible mods from pool
|
||||
let preFilteredModPool = this.getFilteredModPool(modPool, weapon);
|
||||
if (preFilteredModPool.length === 0) {
|
||||
return {
|
||||
@ -864,17 +862,51 @@ export class BotEquipmentModGenerator {
|
||||
return { incompatible: true, found: false, reason: "No mods found in parents allowed list" };
|
||||
}
|
||||
|
||||
// Create pool to pick mod item from
|
||||
const exhaustableModPool = new ExhaustableArray(preFilteredModPool, this.randomUtil, this.cloner);
|
||||
let chosenModResult: IChooseRandomCompatibleModResult = { incompatible: true, found: false, reason: "unknown" };
|
||||
return this.getCompatibleModFromPool(preFilteredModPool, choiceTypeEnum, weapon);
|
||||
}
|
||||
|
||||
// How many times can a mod for the slot be blocked before we stop trying
|
||||
/**
|
||||
*
|
||||
* @param modPool Pool of item Tpls to choose from
|
||||
* @param modSpawnType How should the slot choice be handled - forced/normal etc
|
||||
* @param weapon Weapon mods at current time
|
||||
* @param modSlotName Name of mod slot being filled
|
||||
* @returns IChooseRandomCompatibleModResult
|
||||
*/
|
||||
protected getCompatibleModFromPool(
|
||||
modPool: string[],
|
||||
modSpawnType: ModSpawn,
|
||||
weapon: Item[],
|
||||
): IChooseRandomCompatibleModResult {
|
||||
// Create exhaustable pool to pick mod item from
|
||||
const exhaustableModPool = this.createExhaustableArray(modPool);
|
||||
|
||||
// Create default response if no compatible item is found below
|
||||
const chosenModResult: IChooseRandomCompatibleModResult = {
|
||||
incompatible: true,
|
||||
found: false,
|
||||
reason: "unknown",
|
||||
};
|
||||
|
||||
// Limit how many attempts to find a compatible mod can occur before giving up
|
||||
const maxBlockedAttempts = Math.round(modPool.length * 0.75); // 75% of pool size
|
||||
let blockedAttemptCount = 0;
|
||||
let chosenTpl: string;
|
||||
while (exhaustableModPool.hasValues()) {
|
||||
chosenTpl = exhaustableModPool.getRandomValue();
|
||||
if (choiceTypeEnum === ModSpawn.DEFAULT_MOD && modPool.length === 1) {
|
||||
// Default mod wanted and only one choice in pool
|
||||
const pickedItemDetails = this.itemHelper.getItem(chosenTpl);
|
||||
if (!pickedItemDetails[0]) {
|
||||
// Not valid item, try again
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!pickedItemDetails[1]._props) {
|
||||
// no props data, try again
|
||||
continue;
|
||||
}
|
||||
|
||||
// Success - Default wanted + only 1 item in pool
|
||||
if (modSpawnType === ModSpawn.DEFAULT_MOD && modPool.length === 1) {
|
||||
chosenModResult.found = true;
|
||||
chosenModResult.incompatible = false;
|
||||
chosenModResult.chosenTpl = chosenTpl;
|
||||
@ -882,39 +914,44 @@ export class BotEquipmentModGenerator {
|
||||
break;
|
||||
}
|
||||
|
||||
chosenModResult = this.botGeneratorHelper.isWeaponModIncompatibleWithCurrentMods(
|
||||
weapon,
|
||||
chosenTpl,
|
||||
modSlotName,
|
||||
// Check if existing weapon mods are incompatible with chosen item
|
||||
const existingItemBlockingChoice = weapon.find((item) =>
|
||||
pickedItemDetails[1]._props.ConflictingItems?.includes(item._tpl),
|
||||
);
|
||||
|
||||
if (chosenModResult.slotBlocked) {
|
||||
if (existingItemBlockingChoice) {
|
||||
// Give max of x attempts of picking a mod if blocked by another
|
||||
if (blockedAttemptCount > maxBlockedAttempts) {
|
||||
blockedAttemptCount = 0;
|
||||
blockedAttemptCount = 0; // reset
|
||||
break;
|
||||
}
|
||||
|
||||
blockedAttemptCount++;
|
||||
|
||||
// Try again
|
||||
// Not compatible - Try again
|
||||
continue;
|
||||
}
|
||||
|
||||
// Some mod combos will never work, make sure this isnt the case
|
||||
if (!(chosenModResult.incompatible || this.weaponModComboIsIncompatible(weapon, chosenTpl))) {
|
||||
// Success
|
||||
chosenModResult.found = true;
|
||||
chosenModResult.incompatible = false;
|
||||
chosenModResult.chosenTpl = chosenTpl;
|
||||
|
||||
// Edge case- Some mod combos will never work, make sure this isnt the case
|
||||
if (this.weaponModComboIsIncompatible(weapon, chosenTpl)) {
|
||||
chosenModResult.reason = `Chosen weapon mod: ${chosenTpl} can never be compatible with existing weapon mods`;
|
||||
break;
|
||||
}
|
||||
|
||||
// Success
|
||||
chosenModResult.found = true;
|
||||
chosenModResult.incompatible = false;
|
||||
chosenModResult.chosenTpl = chosenTpl;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return chosenModResult;
|
||||
}
|
||||
|
||||
protected createExhaustableArray<T>(itemsToAddToArray: T[]) {
|
||||
return new ExhaustableArray<T>(itemsToAddToArray, this.randomUtil, this.cloner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of mod tpls that are compatible with the current weapon
|
||||
* @param initialModPool
|
||||
@ -1093,7 +1130,7 @@ export class BotEquipmentModGenerator {
|
||||
const allowedItems = parentSlot._props.filters[0].Filter;
|
||||
|
||||
// Find mod item that fits slot from sorted mod array
|
||||
const exhaustableModPool = new ExhaustableArray(allowedItems, this.randomUtil, this.cloner);
|
||||
const exhaustableModPool = this.createExhaustableArray(allowedItems);
|
||||
let tmpModTpl = fallbackModTpl;
|
||||
while (exhaustableModPool.hasValues()) {
|
||||
tmpModTpl = exhaustableModPool.getRandomValue();
|
||||
@ -1293,10 +1330,10 @@ export class BotEquipmentModGenerator {
|
||||
let modSlot = "cartridges";
|
||||
const camoraFirstSlot = "camora_000";
|
||||
if (modSlot in itemModPool) {
|
||||
exhaustableModPool = new ExhaustableArray(itemModPool[modSlot], this.randomUtil, this.cloner);
|
||||
exhaustableModPool = this.createExhaustableArray(itemModPool[modSlot]);
|
||||
} else if (camoraFirstSlot in itemModPool) {
|
||||
modSlot = camoraFirstSlot;
|
||||
exhaustableModPool = new ExhaustableArray(this.mergeCamoraPools(itemModPool), this.randomUtil, this.cloner);
|
||||
exhaustableModPool = this.createExhaustableArray(this.mergeCamoraPools(itemModPool));
|
||||
} else {
|
||||
this.logger.error(this.localisationService.getText("bot-missing_cartridge_slot", cylinderMagTemplate._id));
|
||||
|
||||
|
@ -351,20 +351,21 @@ export class BotInventoryGenerator {
|
||||
continue;
|
||||
}
|
||||
|
||||
const compatabilityResult = this.botGeneratorHelper.isItemIncompatibleWithCurrentItems(
|
||||
const compatibilityResult = this.botGeneratorHelper.isItemIncompatibleWithCurrentItems(
|
||||
settings.inventory.items,
|
||||
chosenItemTpl,
|
||||
settings.rootEquipmentSlot,
|
||||
);
|
||||
if (compatabilityResult.incompatible) {
|
||||
if (compatibilityResult.incompatible) {
|
||||
// Tried x different items that failed, stop
|
||||
if (attempts > maxAttempts) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove picked item
|
||||
// Remove picked item from pool
|
||||
delete settings.rootEquipmentPool[chosenItemTpl];
|
||||
|
||||
// Increment times tried
|
||||
attempts++;
|
||||
} else {
|
||||
// Success
|
||||
|
@ -250,58 +250,6 @@ export class BotGeneratorHelper {
|
||||
return { Durability: currentDurability, MaxDurability: maxDurability };
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform validation checks on mod tpl against rest of weapon child items
|
||||
* @param weapon all items in weapon
|
||||
* @param tplToCheck Chosen tpl
|
||||
* @param modSlot Slot mod will be placed in
|
||||
* @returns IChooseRandomCompatibleModResult
|
||||
*/
|
||||
public isWeaponModIncompatibleWithCurrentMods(
|
||||
weapon: Item[],
|
||||
tplToCheck: string,
|
||||
modSlot: string,
|
||||
): IChooseRandomCompatibleModResult {
|
||||
const itemToEquipDb = this.itemHelper.getItem(tplToCheck);
|
||||
const itemToEquip = itemToEquipDb[1];
|
||||
|
||||
if (!itemToEquipDb[0]) {
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("bot-invalid_item_compatibility_check", {
|
||||
itemTpl: tplToCheck,
|
||||
slot: modSlot,
|
||||
}),
|
||||
);
|
||||
|
||||
return { incompatible: true, found: false, reason: `item: ${tplToCheck} does not exist in the database` };
|
||||
}
|
||||
|
||||
// No props property
|
||||
if (!itemToEquip._props) {
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("bot-compatibility_check_missing_props", {
|
||||
id: itemToEquip._id,
|
||||
name: itemToEquip._name,
|
||||
slot: modSlot,
|
||||
}),
|
||||
);
|
||||
|
||||
return { incompatible: true, found: false, reason: `item: ${tplToCheck} does not have a _props field` };
|
||||
}
|
||||
|
||||
// Check for existing weapon mods being incompatable with new item
|
||||
const blockingModItem = weapon.find((item) => itemToEquip._props.ConflictingItems?.includes(item._tpl));
|
||||
if (blockingModItem) {
|
||||
return {
|
||||
incompatible: true,
|
||||
found: false,
|
||||
reason: ` Cannot add: ${tplToCheck} to slot: ${modSlot}. Would block existing item: ${blockingModItem._tpl} in slot: ${blockingModItem.slotId}`,
|
||||
};
|
||||
}
|
||||
|
||||
return { incompatible: false, reason: "" };
|
||||
}
|
||||
|
||||
/**
|
||||
* Can item be added to another item without conflict
|
||||
* @param itemsEquipped Items to check compatibilities with
|
||||
|
@ -1505,21 +1505,32 @@ export class ItemHelper {
|
||||
}
|
||||
|
||||
const itemPool = slot._props.filters[0].Filter ?? [];
|
||||
const chosenTpl = this.getCompatibleTplFromArray(itemPool, incompatibleModTpls);
|
||||
if (!chosenTpl) {
|
||||
if (itemPool.length === 0) {
|
||||
this.logger.debug(
|
||||
`Unable to add mod to item: ${itemToAddTemplate._id} ${itemToAddTemplate._name} slot: ${slot._name} as no compatible tpl could be found in pool of ${itemPool.length}, skipping`,
|
||||
`Unable to choose a mod for slot: ${slot._name} on item: ${itemToAddTemplate._id} ${itemToAddTemplate._name}, parents' 'Filter' array is empty, skipping`,
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const chosenTpl = this.getCompatibleTplFromArray(itemPool, incompatibleModTpls);
|
||||
if (!chosenTpl) {
|
||||
this.logger.debug(
|
||||
`Unable to choose a mod for slot: ${slot._name} on item: ${itemToAddTemplate._id} ${itemToAddTemplate._name}, no compatible tpl found in pool of ${itemPool.length}, skipping`,
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create basic item structure ready to add to weapon array
|
||||
const modItemToAdd = {
|
||||
_id: this.hashUtil.generate(),
|
||||
_tpl: chosenTpl,
|
||||
parentId: result[0]._id,
|
||||
slotId: slot._name,
|
||||
};
|
||||
|
||||
// Add chosen item to weapon array
|
||||
result.push(modItemToAdd);
|
||||
|
||||
const modItemDbDetails = this.getItem(modItemToAdd._tpl)[1];
|
||||
|
Loading…
Reference in New Issue
Block a user