FixieWixie

This commit is contained in:
CWX
2025-01-25 21:25:03 +00:00
parent 667daff7cc
commit 8de6a8be94
@@ -37,7 +37,8 @@ public class BotEquipmentModGenerator(
BotEquipmentModPoolService _botEquipmentModPoolService,
ConfigServer _configServer,
ICloner _cloner
) {
)
{
protected BotConfig _botConfig = _configServer.GetConfig<BotConfig>();
/// <summary>
@@ -51,23 +52,28 @@ 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,
@@ -87,12 +93,14 @@ 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;
}
@@ -101,7 +109,8 @@ 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;
}
@@ -110,14 +119,16 @@ 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"
@@ -139,23 +150,27 @@ 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;
}
@@ -169,7 +184,8 @@ public class BotEquipmentModGenerator(
parentTemplate,
settings.BotData.Role
)
) {
)
{
continue;
}
@@ -180,7 +196,8 @@ 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,
@@ -205,14 +222,17 @@ 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) {
var result = new FilterPlateModsForSlotByLevelResult {
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)) {
if (!_itemHelper.IsRemovablePlateSlot(modSlot))
{
result.Result = Result.NOT_PLATE_HOLDING_SLOT;
result.PlateModTemplates = existingPlateTplPool;
@@ -224,8 +244,10 @@ public class BotEquipmentModGenerator(
(armorWeight) =>
settings.BotData.Level >= armorWeight.LevelRange.Min &&
settings.BotData.Level <= armorWeight.LevelRange.Max
);
if (plateSlotWeights is null) {
);
if (plateSlotWeights is null)
{
// No weights, return original array of plate tpls
result.Result = Result.LACKS_PLATE_WEIGHTS;
result.PlateModTemplates = existingPlateTplPool;
@@ -235,7 +257,8 @@ public class BotEquipmentModGenerator(
// Get the specific plate slot weights (front/back/side)
var plateWeights = plateSlotWeights[modSlot];
if (plateWeights is null) {
if (plateWeights is null)
{
// No weights, return original array of plate tpls
result.Result = Result.LACKS_PLATE_WEIGHTS;
result.PlateModTemplates = existingPlateTplPool;
@@ -251,7 +274,8 @@ public class BotEquipmentModGenerator(
// Filter plates to the chosen level based on its armorClass property
var platesOfDesiredLevel = platesFromDb.Filter((item) => item._props.armorClass == chosenArmorPlateLevel);
if (platesOfDesiredLevel.length > 0) {
if (platesOfDesiredLevel.length > 0)
{
// Plates found
result.Result = Result.SUCCESS;
result.PlateModTemplates = platesOfDesiredLevel.map((item) => item._id);
@@ -262,65 +286,73 @@ public class BotEquipmentModGenerator(
// no plates found that fit requirements, lets get creative
// Get lowest and highest plate classes available for this armor
const minMaxArmorPlateClass = this.getMinMaxArmorPlateClass(platesFromDb);
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++) {
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) {
if (Number(chosenArmorPlateLevel) > minMaxArmorPlateClass.max)
{
chosenArmorPlateLevel = minMaxArmorPlateClass.min.toString();
}
findCompatiblePlateAttempts++;
platesOfDesiredLevel = platesFromDb.filter((item) => item._props.armorClass === chosenArmorPlateLevel);
platesOfDesiredLevel = platesFromDb.filter((item) => item._props.armorClass == = chosenArmorPlateLevel);
// Valid plates found, exit
if (platesOfDesiredLevel.length > 0) {
if (platesOfDesiredLevel.length > 0)
{
break;
}
// No valid plate class found in 3 tries, attempt default plates
if (findCompatiblePlateAttempts >= maxAttempts) {
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`,
$"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];
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;
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;
}
}
// 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
@@ -457,7 +489,7 @@ public class BotEquipmentModGenerator(
}
if (!IsModValidForSlot(modToAdd, modsParentSlot, modSlot, request.ParentTemplate, request.BotData.Role)
)
)
{
continue;
}
@@ -608,7 +640,6 @@ public class BotEquipmentModGenerator(
request.ModPool[modToAddTemplate.Value.Id] = modFromService;
containsModInPool = true;
}
}
if (containsModInPool)
@@ -1033,10 +1064,13 @@ public class BotEquipmentModGenerator(
var weaponTpl = modSpawnRequest.Weapon[0].Template;
modSpawnRequest.RandomisationSettings.MinimumMagazineSize.TryGetValue(weaponTpl, out var minMagSizeFromSettings);
var minMagazineSize = minMagSizeFromSettings;
var desiredMagazineTpls = modPool.Where((magTpl) => {
var magazineDb = _itemHelper.GetItem(magTpl).Value;
return magazineDb.Properties is not null && magazineDb.Properties.Cartridges.FirstOrDefault().MaxCount >= minMagazineSize;
});
var desiredMagazineTpls = modPool.Where(
(magTpl) =>
{
var magazineDb = _itemHelper.GetItem(magTpl).Value;
return magazineDb.Properties is not null && magazineDb.Properties.Cartridges.FirstOrDefault().MaxCount >= minMagazineSize;
}
);
if (!desiredMagazineTpls.Any())
{
@@ -1467,7 +1501,8 @@ public class BotEquipmentModGenerator(
/// <param name="modTemplate">db object for modItem we get compatible mods from</param>
/// <param name="modPool">Pool of mods we are adding to</param>
/// <param name="botEquipBlacklist">A blacklist of items that cannot be picked</param>
public void AddCompatibleModsForProvidedMod(string desiredSlotName, TemplateItem modTemplate, Dictionary<string, Dictionary<string, HashSet<string>>> modPool,
public void AddCompatibleModsForProvidedMod(string desiredSlotName, TemplateItem modTemplate,
Dictionary<string, Dictionary<string, HashSet<string>>> modPool,
EquipmentFilterDetails botEquipBlacklist)
{
var desiredSlotObject = modTemplate.Properties.Slots?.FirstOrDefault((slot) => slot.Name.Contains(desiredSlotName));
@@ -1670,7 +1705,9 @@ public class BotEquipmentModGenerator(
var whitelistedSightTypes = botWeaponSightWhitelist[weaponDetails.Value.Parent];
if (whitelistedSightTypes is null)
{
_logger.Debug($"Unable to find whitelist for weapon type: {weaponDetails.Value.Parent} {weaponDetails.Value.Name}, skipping sight filtering");
_logger.Debug(
$"Unable to find whitelist for weapon type: {weaponDetails.Value.Parent} {weaponDetails.Value.Name}, skipping sight filtering"
);
return scopes;
}