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
|
// Choose random mod from pool and check its compatibility
|
||||||
let modTpl: string | undefined;
|
let modTpl: string | undefined;
|
||||||
let found = false;
|
let found = false;
|
||||||
const exhaustableModPool = new ExhaustableArray<string>(modPoolToChooseFrom, this.randomUtil, this.cloner);
|
const exhaustableModPool = this.createExhaustableArray(modPoolToChooseFrom);
|
||||||
while (exhaustableModPool.hasValues()) {
|
while (exhaustableModPool.hasValues()) {
|
||||||
modTpl = exhaustableModPool.getRandomValue();
|
modTpl = exhaustableModPool.getRandomValue();
|
||||||
if (
|
if (
|
||||||
@ -788,7 +788,7 @@ export class BotEquipmentModGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pick random mod that's compatible
|
// Pick random mod that's compatible
|
||||||
const chosenModResult = this.pickWeaponModTplForSlotFromPool(
|
const chosenModResult = this.getCompatibleWeaponModTplForSlotFromPool(
|
||||||
modPool,
|
modPool,
|
||||||
parentSlot,
|
parentSlot,
|
||||||
request.modSpawnResult,
|
request.modSpawnResult,
|
||||||
@ -803,7 +803,6 @@ export class BotEquipmentModGenerator {
|
|||||||
// Log if mod chosen was incompatible
|
// Log if mod chosen was incompatible
|
||||||
if (chosenModResult.incompatible && parentSlot._required) {
|
if (chosenModResult.incompatible && parentSlot._required) {
|
||||||
this.logger.debug(chosenModResult.reason);
|
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
|
// 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 modPool Pool of mods that can be picked from
|
||||||
* @param parentSlot Slot the picked mod will have as a parent
|
* @param parentSlot Slot the picked mod will have as a parent
|
||||||
* @param choiceTypeEnum How should chosen tpl be treated: DEFAULT_MOD/SPAWN/SKIP
|
* @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
|
* @param modSlotName Name of slot picked mod will be placed into
|
||||||
* @returns Chosen weapon details
|
* @returns Chosen weapon details
|
||||||
*/
|
*/
|
||||||
protected pickWeaponModTplForSlotFromPool(
|
protected getCompatibleWeaponModTplForSlotFromPool(
|
||||||
modPool: string[],
|
modPool: string[],
|
||||||
parentSlot: Slot,
|
parentSlot: Slot,
|
||||||
choiceTypeEnum: ModSpawn,
|
choiceTypeEnum: ModSpawn,
|
||||||
weapon: Item[],
|
weapon: Item[],
|
||||||
modSlotName: string,
|
modSlotName: string,
|
||||||
): IChooseRandomCompatibleModResult {
|
): IChooseRandomCompatibleModResult {
|
||||||
let chosenTpl: string;
|
// Filter out incompatible mods from pool
|
||||||
|
|
||||||
// Filter out incompatable mods from pool
|
|
||||||
let preFilteredModPool = this.getFilteredModPool(modPool, weapon);
|
let preFilteredModPool = this.getFilteredModPool(modPool, weapon);
|
||||||
if (preFilteredModPool.length === 0) {
|
if (preFilteredModPool.length === 0) {
|
||||||
return {
|
return {
|
||||||
@ -864,17 +862,51 @@ export class BotEquipmentModGenerator {
|
|||||||
return { incompatible: true, found: false, reason: "No mods found in parents allowed list" };
|
return { incompatible: true, found: false, reason: "No mods found in parents allowed list" };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create pool to pick mod item from
|
return this.getCompatibleModFromPool(preFilteredModPool, choiceTypeEnum, weapon);
|
||||||
const exhaustableModPool = new ExhaustableArray(preFilteredModPool, this.randomUtil, this.cloner);
|
}
|
||||||
let chosenModResult: IChooseRandomCompatibleModResult = { incompatible: true, found: false, reason: "unknown" };
|
|
||||||
|
|
||||||
// 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
|
const maxBlockedAttempts = Math.round(modPool.length * 0.75); // 75% of pool size
|
||||||
let blockedAttemptCount = 0;
|
let blockedAttemptCount = 0;
|
||||||
|
let chosenTpl: string;
|
||||||
while (exhaustableModPool.hasValues()) {
|
while (exhaustableModPool.hasValues()) {
|
||||||
chosenTpl = exhaustableModPool.getRandomValue();
|
chosenTpl = exhaustableModPool.getRandomValue();
|
||||||
if (choiceTypeEnum === ModSpawn.DEFAULT_MOD && modPool.length === 1) {
|
const pickedItemDetails = this.itemHelper.getItem(chosenTpl);
|
||||||
// Default mod wanted and only one choice in pool
|
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.found = true;
|
||||||
chosenModResult.incompatible = false;
|
chosenModResult.incompatible = false;
|
||||||
chosenModResult.chosenTpl = chosenTpl;
|
chosenModResult.chosenTpl = chosenTpl;
|
||||||
@ -882,39 +914,44 @@ export class BotEquipmentModGenerator {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
chosenModResult = this.botGeneratorHelper.isWeaponModIncompatibleWithCurrentMods(
|
// Check if existing weapon mods are incompatible with chosen item
|
||||||
weapon,
|
const existingItemBlockingChoice = weapon.find((item) =>
|
||||||
chosenTpl,
|
pickedItemDetails[1]._props.ConflictingItems?.includes(item._tpl),
|
||||||
modSlotName,
|
|
||||||
);
|
);
|
||||||
|
if (existingItemBlockingChoice) {
|
||||||
if (chosenModResult.slotBlocked) {
|
|
||||||
// Give max of x attempts of picking a mod if blocked by another
|
// Give max of x attempts of picking a mod if blocked by another
|
||||||
if (blockedAttemptCount > maxBlockedAttempts) {
|
if (blockedAttemptCount > maxBlockedAttempts) {
|
||||||
blockedAttemptCount = 0;
|
blockedAttemptCount = 0; // reset
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
blockedAttemptCount++;
|
blockedAttemptCount++;
|
||||||
|
|
||||||
// Try again
|
// Not compatible - Try again
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some mod combos will never work, make sure this isnt the case
|
// Edge case- Some mod combos will never work, make sure this isnt the case
|
||||||
if (!(chosenModResult.incompatible || this.weaponModComboIsIncompatible(weapon, chosenTpl))) {
|
if (this.weaponModComboIsIncompatible(weapon, chosenTpl)) {
|
||||||
// Success
|
chosenModResult.reason = `Chosen weapon mod: ${chosenTpl} can never be compatible with existing weapon mods`;
|
||||||
chosenModResult.found = true;
|
|
||||||
chosenModResult.incompatible = false;
|
|
||||||
chosenModResult.chosenTpl = chosenTpl;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Success
|
||||||
|
chosenModResult.found = true;
|
||||||
|
chosenModResult.incompatible = false;
|
||||||
|
chosenModResult.chosenTpl = chosenTpl;
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return chosenModResult;
|
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
|
* Get a list of mod tpls that are compatible with the current weapon
|
||||||
* @param initialModPool
|
* @param initialModPool
|
||||||
@ -1093,7 +1130,7 @@ export class BotEquipmentModGenerator {
|
|||||||
const allowedItems = parentSlot._props.filters[0].Filter;
|
const allowedItems = parentSlot._props.filters[0].Filter;
|
||||||
|
|
||||||
// Find mod item that fits slot from sorted mod array
|
// 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;
|
let tmpModTpl = fallbackModTpl;
|
||||||
while (exhaustableModPool.hasValues()) {
|
while (exhaustableModPool.hasValues()) {
|
||||||
tmpModTpl = exhaustableModPool.getRandomValue();
|
tmpModTpl = exhaustableModPool.getRandomValue();
|
||||||
@ -1293,10 +1330,10 @@ export class BotEquipmentModGenerator {
|
|||||||
let modSlot = "cartridges";
|
let modSlot = "cartridges";
|
||||||
const camoraFirstSlot = "camora_000";
|
const camoraFirstSlot = "camora_000";
|
||||||
if (modSlot in itemModPool) {
|
if (modSlot in itemModPool) {
|
||||||
exhaustableModPool = new ExhaustableArray(itemModPool[modSlot], this.randomUtil, this.cloner);
|
exhaustableModPool = this.createExhaustableArray(itemModPool[modSlot]);
|
||||||
} else if (camoraFirstSlot in itemModPool) {
|
} else if (camoraFirstSlot in itemModPool) {
|
||||||
modSlot = camoraFirstSlot;
|
modSlot = camoraFirstSlot;
|
||||||
exhaustableModPool = new ExhaustableArray(this.mergeCamoraPools(itemModPool), this.randomUtil, this.cloner);
|
exhaustableModPool = this.createExhaustableArray(this.mergeCamoraPools(itemModPool));
|
||||||
} else {
|
} else {
|
||||||
this.logger.error(this.localisationService.getText("bot-missing_cartridge_slot", cylinderMagTemplate._id));
|
this.logger.error(this.localisationService.getText("bot-missing_cartridge_slot", cylinderMagTemplate._id));
|
||||||
|
|
||||||
|
@ -351,20 +351,21 @@ export class BotInventoryGenerator {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const compatabilityResult = this.botGeneratorHelper.isItemIncompatibleWithCurrentItems(
|
const compatibilityResult = this.botGeneratorHelper.isItemIncompatibleWithCurrentItems(
|
||||||
settings.inventory.items,
|
settings.inventory.items,
|
||||||
chosenItemTpl,
|
chosenItemTpl,
|
||||||
settings.rootEquipmentSlot,
|
settings.rootEquipmentSlot,
|
||||||
);
|
);
|
||||||
if (compatabilityResult.incompatible) {
|
if (compatibilityResult.incompatible) {
|
||||||
// Tried x different items that failed, stop
|
// Tried x different items that failed, stop
|
||||||
if (attempts > maxAttempts) {
|
if (attempts > maxAttempts) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove picked item
|
// Remove picked item from pool
|
||||||
delete settings.rootEquipmentPool[chosenItemTpl];
|
delete settings.rootEquipmentPool[chosenItemTpl];
|
||||||
|
|
||||||
|
// Increment times tried
|
||||||
attempts++;
|
attempts++;
|
||||||
} else {
|
} else {
|
||||||
// Success
|
// Success
|
||||||
|
@ -250,58 +250,6 @@ export class BotGeneratorHelper {
|
|||||||
return { Durability: currentDurability, MaxDurability: maxDurability };
|
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
|
* Can item be added to another item without conflict
|
||||||
* @param itemsEquipped Items to check compatibilities with
|
* @param itemsEquipped Items to check compatibilities with
|
||||||
|
@ -1505,21 +1505,32 @@ export class ItemHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const itemPool = slot._props.filters[0].Filter ?? [];
|
const itemPool = slot._props.filters[0].Filter ?? [];
|
||||||
const chosenTpl = this.getCompatibleTplFromArray(itemPool, incompatibleModTpls);
|
if (itemPool.length === 0) {
|
||||||
if (!chosenTpl) {
|
|
||||||
this.logger.debug(
|
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;
|
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 = {
|
const modItemToAdd = {
|
||||||
_id: this.hashUtil.generate(),
|
_id: this.hashUtil.generate(),
|
||||||
_tpl: chosenTpl,
|
_tpl: chosenTpl,
|
||||||
parentId: result[0]._id,
|
parentId: result[0]._id,
|
||||||
slotId: slot._name,
|
slotId: slot._name,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add chosen item to weapon array
|
||||||
result.push(modItemToAdd);
|
result.push(modItemToAdd);
|
||||||
|
|
||||||
const modItemDbDetails = this.getItem(modItemToAdd._tpl)[1];
|
const modItemDbDetails = this.getItem(modItemToAdd._tpl)[1];
|
||||||
|
Loading…
Reference in New Issue
Block a user