Update BotEquipmentModGenerator.cs

Oopsies!
This commit is contained in:
Valens
2025-01-25 16:23:00 -05:00
parent 799a4c778e
commit 43a7e648b2
@@ -11,6 +11,8 @@ using Core.Services;
using Core.Utils;
using Core.Utils.Cloners;
using Core.Utils.Collections;
using Core.Models.Eft.Player;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace Core.Generators;
@@ -35,8 +37,7 @@ public class BotEquipmentModGenerator(
BotEquipmentModPoolService _botEquipmentModPoolService,
ConfigServer _configServer,
ICloner _cloner
)
{
) {
protected BotConfig _botConfig = _configServer.GetConfig<BotConfig>();
/// <summary>
@@ -50,28 +51,23 @@ public class BotEquipmentModGenerator(
/// <param name="shouldForceSpawn">should this mod be forced to spawn</param>
/// <returns>Item + compatible mods as an array</returns>
public List<Item> GenerateModsForEquipment(List<Item> equipment, string parentId, TemplateItem parentTemplate, GenerateEquipmentProperties settings,
EquipmentFilterDetails specificBlacklist, bool shouldForceSpawn = false)
{
EquipmentFilterDetails specificBlacklist, bool shouldForceSpawn = false) {
var forceSpawn = shouldForceSpawn;
// Get mod pool for the desired item
if (!settings.ModPool.TryGetValue(parentTemplate.Id, out var compatibleModsPool))
{
if (!settings.ModPool.TryGetValue(parentTemplate.Id, out var compatibleModsPool)) {
_logger.Warning($"bot: {settings.BotData.Role} lacks a mod slot pool for item: {parentTemplate.Id} {parentTemplate.Name}");
}
// Iterate over mod pool and choose mods to add to item
foreach (var (modSlotName, modPool) in compatibleModsPool ?? [])
{
foreach (var (modSlotName, modPool) in compatibleModsPool ?? []) {
// Get the templates slot object from db
var itemSlotTemplate = GetModItemSlotFromDb(modSlotName, parentTemplate);
if (itemSlotTemplate is null)
{
if (itemSlotTemplate is null) {
_logger.Error(
_localisationService.GetText(
"bot-mod_slot_missing_from_item",
new
{
new {
modSlot = modSlotName,
parentId = parentTemplate.Id,
parentName = parentTemplate.Name,
@@ -91,14 +87,12 @@ public class BotEquipmentModGenerator(
);
// Rolled to skip mod and it shouldn't be force-spawned
if (modSpawnResult == ModSpawn.SKIP && !forceSpawn)
{
if (modSpawnResult == ModSpawn.SKIP && !forceSpawn) {
continue;
}
// Ensure submods for nvgs all spawn together
if (modSlotName == "mod_nvg")
{
if (modSlotName == "mod_nvg") {
forceSpawn = true;
}
@@ -107,8 +101,7 @@ public class BotEquipmentModGenerator(
// Filter the pool of items in blacklist
var filteredModPool = FilterModsByBlacklist(modPoolToChooseFrom, specificBlacklist, modSlotName);
if (filteredModPool.Count > 0)
{
if (filteredModPool.Count > 0) {
// use filtered pool as it has items in it
modPoolToChooseFrom = filteredModPool;
}
@@ -117,16 +110,14 @@ public class BotEquipmentModGenerator(
if (
settings.BotEquipmentConfig.FilterPlatesByLevel.GetValueOrDefault(false) &&
_itemHelper.IsRemovablePlateSlot(modSlotName.ToLower())
)
{
) {
var plateSlotFilteringOutcome = FilterPlateModsForSlotByLevel(
settings,
modSlotName.ToLower(),
compatibleModsPool[modSlotName],
parentTemplate
);
switch (plateSlotFilteringOutcome.Result)
{
switch (plateSlotFilteringOutcome.Result) {
case Result.UNKNOWN_FAILURE or Result.NO_DEFAULT_FILTER:
_logger.Debug(
$"Plate slot: {modSlotName} selection for armor: {parentTemplate.Id} failed: {plateSlotFilteringOutcome.Result}, skipping"
@@ -148,27 +139,23 @@ public class BotEquipmentModGenerator(
string modTpl = null;
var found = false;
var exhaustableModPool = CreateExhaustableArray(modPoolToChooseFrom.ToList());
while (exhaustableModPool.HasValues())
{
while (exhaustableModPool.HasValues()) {
modTpl = exhaustableModPool.GetRandomValue();
if (modTpl is not null &&
!_botGeneratorHelper.IsItemIncompatibleWithCurrentItems(equipment, modTpl, modSlotName).Incompatible.GetValueOrDefault(false))
{
!_botGeneratorHelper.IsItemIncompatibleWithCurrentItems(equipment, modTpl, modSlotName).Incompatible.GetValueOrDefault(false)) {
found = true;
break;
}
}
// Compatible item not found but slot REQUIRES item, get random item from db
if (!found && itemSlotTemplate.Required.GetValueOrDefault(false))
{
if (!found && itemSlotTemplate.Required.GetValueOrDefault(false)) {
modTpl = GetRandomModTplFromItemDb(modTpl, itemSlotTemplate, modSlotName, equipment);
found = modTpl is not null;
}
// Compatible item not found + not required - skip
if (!(found || itemSlotTemplate.Required.GetValueOrDefault(false)))
{
if (!(found || itemSlotTemplate.Required.GetValueOrDefault(false))) {
continue;
}
@@ -182,8 +169,7 @@ public class BotEquipmentModGenerator(
parentTemplate,
settings.BotData.Role
)
)
{
) {
continue;
}
@@ -194,8 +180,7 @@ public class BotEquipmentModGenerator(
);
// Does item being added exist in mod pool - has its own mod pool
if (settings.ModPool.ContainsKey(modTpl))
{
if (settings.ModPool.ContainsKey(modTpl)) {
// Call self again with mod being added as item to add child mods to
GenerateModsForEquipment(
equipment,
@@ -220,11 +205,123 @@ public class BotEquipmentModGenerator(
/// <param name="armorItem">The armor items db template</param>
/// <returns>Array of plate tpls to choose from</returns>
public FilterPlateModsForSlotByLevelResult FilterPlateModsForSlotByLevel(GenerateEquipmentProperties settings, string modSlot,
HashSet<string> existingPlateTplPool, TemplateItem armorItem)
{
throw new NotImplementedException();
HashSet<string> existingPlateTplPool, TemplateItem armorItem) {
var result = new FilterPlateModsForSlotByLevelResult {
Result = Result.UNKNOWN_FAILURE,
PlateModTemplates = null,
};
// Not pmc or not a plate slot, return original mod pool array
if (!_itemHelper.IsRemovablePlateSlot(modSlot)) {
result.Result = Result.NOT_PLATE_HOLDING_SLOT;
result.PlateModTemplates = existingPlateTplPool;
return result;
}
// Get the front/back/side weights based on bots level
var plateSlotWeights = settings.BotEquipmentConfig?.ArmorPlateWeighting?.FirstOrDefault(
(armorWeight) =>
settings.BotData.Level >= armorWeight.LevelRange.Min &&
settings.BotData.Level <= armorWeight.LevelRange.Max
);
if (plateSlotWeights is null) {
// No weights, return original array of plate tpls
result.Result = Result.LACKS_PLATE_WEIGHTS;
result.PlateModTemplates = existingPlateTplPool;
return result;
}
// Get the specific plate slot weights (front/back/side)
var plateWeights = plateSlotWeights[modSlot];
if (plateWeights is null) {
// No weights, return original array of plate tpls
result.Result = Result.LACKS_PLATE_WEIGHTS;
result.PlateModTemplates = existingPlateTplPool;
return result;
}
// Choose a plate level based on weighting
var chosenArmorPlateLevel = _weightedRandomHelper.GetWeightedValue<string>(plateWeights);
// Convert the array of ids into database items
var platesFromDb = existingPlateTplPool.Select((plateTpl) => _itemHelper.GetItem(plateTpl)[1]);
// Filter plates to the chosen level based on its armorClass property
var platesOfDesiredLevel = platesFromDb.Filter((item) => item._props.armorClass == chosenArmorPlateLevel);
if (platesOfDesiredLevel.length > 0) {
// Plates found
result.Result = Result.SUCCESS;
result.PlateModTemplates = platesOfDesiredLevel.map((item) => item._id);
return result;
}
// no plates found that fit requirements, lets get creative
// Get lowest and highest plate classes available for this armor
const minMaxArmorPlateClass = this.getMinMaxArmorPlateClass(platesFromDb);
// 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();
// New chosen plate class is higher than max, then set to min and check if valid
if (Number(chosenArmorPlateLevel) > minMaxArmorPlateClass.max) {
chosenArmorPlateLevel = minMaxArmorPlateClass.min.toString();
}
findCompatiblePlateAttempts++;
platesOfDesiredLevel = platesFromDb.filter((item) => item._props.armorClass === chosenArmorPlateLevel);
// Valid plates found, exit
if (platesOfDesiredLevel.length > 0) {
break;
}
// No valid plate class found in 3 tries, attempt default plates
if (findCompatiblePlateAttempts >= maxAttempts) {
this.logger.debug(
`Plate filter too restrictive for armor: ${ armorItem._name} ${ armorItem._id}, unable to find plates of level: ${ chosenArmorPlateLevel}, using items default plate`,
);
const defaultPlate = this.getDefaultPlateTpl(armorItem, modSlot);
if (defaultPlate) {
// Return Default Plates cause couldn't get lowest level available from original selection
result.result = Result.SUCCESS;
result.plateModTpls = [defaultPlate];
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;
}
// Only return the items ids
result.result = Result.SUCCESS;
result.plateModTpls = platesOfDesiredLevel.map((item) => item._id);
return result;
/**
* Get the default plate an armor has in its db item
* @param armorItem Item to look up default plate