This commit is contained in:
Alex
2025-01-15 15:06:58 +00:00
26 changed files with 1117 additions and 144 deletions
+254 -34
View File
@@ -145,7 +145,8 @@ public class BotInventoryGenerator
var questStashItemsId = _hashUtil.Generate();
var sortingTableId = _hashUtil.Generate();
return new BotBaseInventory{
return new BotBaseInventory
{
Items =
[
new() { Id = equipmentId, Template = ItemTpl.INVENTORY_DEFAULT },
@@ -181,7 +182,8 @@ public class BotInventoryGenerator
BotBaseInventory botInventory, int botLevel, string chosenGameVersion, GetRaidConfigurationRequestData raidConfig)
{
// These will be handled later
var excludedSlots = new List<EquipmentSlots>(){
var excludedSlots = new List<EquipmentSlots>()
{
EquipmentSlots.Pockets,
EquipmentSlots.FirstPrimaryWeapon,
EquipmentSlots.SecondPrimaryWeapon,
@@ -203,10 +205,11 @@ public class BotInventoryGenerator
_weatherHelper.IsNightTime(raidConfig.TimeVariant)
)
{
foreach (var equipmentSlotKvP in (randomistionDetails.NighttimeChanges.EquipmentModsModifiers)) {
foreach (var equipmentSlotKvP in (randomistionDetails.NighttimeChanges.EquipmentModsModifiers))
{
// Never let mod chance go outside of 0 - 100
randomistionDetails.EquipmentMods[equipmentSlotKvP.Key] = Math.Min(
Math.Max( randomistionDetails.EquipmentMods[equipmentSlotKvP.Key] + equipmentSlotKvP.Value, 0), 100);
Math.Max(randomistionDetails.EquipmentMods[equipmentSlotKvP.Key] + equipmentSlotKvP.Value, 0), 100);
}
}
@@ -218,14 +221,16 @@ public class BotInventoryGenerator
// Iterate over all equipment slots of bot, do it in specifc order to reduce conflicts
// e.g. ArmorVest should be generated after TactivalVest
// or FACE_COVER before HEADWEAR
foreach (var equipmentSlotKvP in templateInventory.Equipment) {
foreach (var equipmentSlotKvP in templateInventory.Equipment)
{
// Skip some slots as they need to be done in a specific order + with specific parameter values
// e.g. Weapons
if (excludedSlots.Contains(equipmentSlotKvP.Key)) {
if (excludedSlots.Contains(equipmentSlotKvP.Key))
{
continue;
}
GenerateEquipment( new GenerateEquipmentProperties
GenerateEquipment(new GenerateEquipmentProperties
{
RootEquipmentSlot = equipmentSlotKvP.Key,
RootEquipmentPool = equipmentSlotKvP.Value,
@@ -240,17 +245,17 @@ public class BotInventoryGenerator
}
// Generate below in specific order
GenerateEquipment( new GenerateEquipmentProperties
GenerateEquipment(new GenerateEquipmentProperties
{
RootEquipmentSlot = EquipmentSlots.Pockets,
// Unheard profiles have unique sized pockets, TODO - handle this somewhere else in a better way
RootEquipmentPool =
chosenGameVersion == GameEditions.UNHEARD
? new Dictionary<string, double>{ [ItemTpl.POCKETS_1X4_TUE] = 1 }
? new Dictionary<string, double> { [ItemTpl.POCKETS_1X4_TUE] = 1 }
: templateInventory.Equipment[EquipmentSlots.Pockets],
ModPool = templateInventory.Mods,
SpawnChances = wornItemChances,
BotData = new BotData{ Role = botRole, Level = botLevel, EquipmentRole = botEquipmentRole },
BotData = new BotData { Role = botRole, Level = botLevel, EquipmentRole = botEquipmentRole },
Inventory = botInventory,
BotEquipmentConfig = botEquipConfig,
RandomisationDetails = randomistionDetails,
@@ -258,7 +263,7 @@ public class BotInventoryGenerator
GeneratingPlayerLevel = pmcProfile.Info.Level,
});
GenerateEquipment( new GenerateEquipmentProperties
GenerateEquipment(new GenerateEquipmentProperties
{
RootEquipmentSlot = EquipmentSlots.FaceCover,
RootEquipmentPool = templateInventory.Equipment[EquipmentSlots.FaceCover],
@@ -271,7 +276,7 @@ public class BotInventoryGenerator
GeneratingPlayerLevel = pmcProfile.Info.Level,
});
GenerateEquipment( new GenerateEquipmentProperties
GenerateEquipment(new GenerateEquipmentProperties
{
RootEquipmentSlot = EquipmentSlots.Headwear,
RootEquipmentPool = templateInventory.Equipment[EquipmentSlots.Headwear],
@@ -297,7 +302,7 @@ public class BotInventoryGenerator
GeneratingPlayerLevel = pmcProfile.Info.Level,
});
var hasArmorVest = GenerateEquipment( new GenerateEquipmentProperties
var hasArmorVest = GenerateEquipment(new GenerateEquipmentProperties
{
RootEquipmentSlot = EquipmentSlots.ArmorVest,
RootEquipmentPool = templateInventory.Equipment[EquipmentSlots.ArmorVest],
@@ -311,23 +316,26 @@ public class BotInventoryGenerator
});
// Bot has no armor vest and flagged to be forced to wear armored rig in this event
if (botEquipConfig.ForceOnlyArmoredRigWhenNoArmor.GetValueOrDefault(false) && !hasArmorVest) {
if (botEquipConfig.ForceOnlyArmoredRigWhenNoArmor.GetValueOrDefault(false) && !hasArmorVest)
{
// Filter rigs down to only those with armor
FilterRigsToThoseWithProtection(templateInventory.Equipment, botRole);
}
// Optimisation - Remove armored rigs from pool
if (hasArmorVest) {
if (hasArmorVest)
{
// Filter rigs down to only those with armor
FilterRigsToThoseWithoutProtection(templateInventory.Equipment, botRole);
}
// Bot is flagged as always needing a vest
if (botEquipConfig.ForceRigWhenNoVest.GetValueOrDefault(false) && !hasArmorVest) {
if (botEquipConfig.ForceRigWhenNoVest.GetValueOrDefault(false) && !hasArmorVest)
{
wornItemChances.EquipmentChances["TacticalVest"] = 100;
}
GenerateEquipment( new GenerateEquipmentProperties
GenerateEquipment(new GenerateEquipmentProperties
{
RootEquipmentSlot = EquipmentSlots.Earpiece,
RootEquipmentPool = templateInventory.Equipment[EquipmentSlots.Earpiece],
@@ -348,12 +356,17 @@ public class BotInventoryGenerator
/// <param name="botRole">Role of bot vests are being filtered for</param>
public void FilterRigsToThoseWithProtection(Dictionary<EquipmentSlots, Dictionary<string, double>> templateEquipment, string botRole)
{
throw new NotImplementedException();
}
public void FilterRigsToThoseWithoutProtection(Dictionary<EquipmentSlots, Dictionary<string, double>> templateEquipment, string botRole, bool allowEmptyResult = true)
{
throw new NotImplementedException();
var tacVestsWithArmor = templateEquipment[EquipmentSlots.TacticalVest].Where(kvp => _itemHelper.ItemHasSlots(kvp.Key))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
if (tacVestsWithArmor.Count() == 0)
{
_logger.Debug($"Unable to filter to only armored rigs as bot: {botRole} has none in pool");
return;
}
templateEquipment[EquipmentSlots.TacticalVest] = tacVestsWithArmor;
}
/// <summary>
@@ -362,9 +375,20 @@ public class BotInventoryGenerator
/// <param name="templateEquipment">Equpiment to filter TacticalVest of</param>
/// <param name="botRole">Role of bot vests are being filtered for</param>
/// <param name="allowEmptyRequest">Should the function return all rigs when 0 unarmored are found</param>
public void FilterRigsTothoseWithoutProtection(Equipment templateEquipment, string botRole, bool allowEmptyRequest = false)
public void FilterRigsToThoseWithoutProtection(Dictionary<EquipmentSlots, Dictionary<string, double>> templateEquipment, string botRole,
bool allowEmptyResult = true)
{
throw new NotImplementedException();
var tacVestsWithoutArmor = templateEquipment[EquipmentSlots.TacticalVest].Where(kvp => !_itemHelper.ItemHasSlots(kvp.Key))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
if (!allowEmptyResult && tacVestsWithoutArmor.Count() == 0)
{
_logger.Debug($"Unable to filter to only unarmored rigs as bot: {botRole} has none in pool");
return;
}
templateEquipment[EquipmentSlots.TacticalVest] = tacVestsWithoutArmor;
}
/// <summary>
@@ -375,7 +399,129 @@ public class BotInventoryGenerator
public bool GenerateEquipment(GenerateEquipmentProperties settings)
{
_logger.Error("NOT IMPLEMENTED - GenerateEquipment");
return true;
List<string> slotsToCheck = [EquipmentSlots.Pockets.ToString(), EquipmentSlots.SecuredContainer.ToString()];
double? spawnChance = slotsToCheck.Contains(settings.RootEquipmentSlot.ToString())
? 100
: settings.SpawnChances.EquipmentChances[settings.RootEquipmentSlot.ToString()];
if (spawnChance is null)
{
_logger.Warning(_localisationService.GetText("bot-no_spawn_chance_defined_for_equipment_slot",
settings.RootEquipmentSlot));
return false;
}
// Roll dice on equipment item
var shouldSpawn = _randomUtil.GetChance100(spawnChance ?? 0);
if (shouldSpawn && settings.RootEquipmentPool.Count() == 0)
{
TemplateItem pickedItemDb = new TemplateItem();
var found = false;
// Limit attempts to find a compatible item as its expensive to check them all
var maxAttempts = Math.Round(settings.RootEquipmentPool.Count() * 0.75); // Roughly 75% of pool size
var attempts = 0;
while (!found)
{
if (settings.RootEquipmentPool.Count() == 0)
{
return false;
}
var chosenItemTpl = _weightedRandomHelper.GetWeightedValue<string>(settings.RootEquipmentPool);
var dbResult = _itemHelper.GetItem(chosenItemTpl);
if (!dbResult.Key)
{
_logger.Error(_localisationService.GetText("bot-missing_item_template", chosenItemTpl));
_logger.Debug($"EquipmentSlot-> {settings.RootEquipmentSlot}");
// Remove picked item
settings.RootEquipmentPool.Remove(chosenItemTpl);
attempts++;
continue;
}
// Is the chosen item compatible with other items equipped
var compatibilityResult = _botGeneratorHelper.IsItemIncompatibleWithCurrentItems(
settings.Inventory.Items,
chosenItemTpl,
settings.RootEquipmentSlot.ToString()
);
if (compatibilityResult.Incompatible ?? false)
{
// Tried x different items that failed, stop
if (attempts > maxAttempts)
{
return false;
}
// Remove picked item from pool
settings.RootEquipmentPool.Remove(chosenItemTpl);
// Increment times tried
attempts++;
}
else
{
// Success
found = true;
pickedItemDb = dbResult.Value;
}
}
// Create root item
var id = _hashUtil.Generate();
Item item = new()
{
Id = id,
Template = pickedItemDb.Id,
ParentId = settings.Inventory.Equipment,
SlotId = settings.RootEquipmentSlot.ToString(),
Upd = _botGeneratorHelper.GenerateExtraPropertiesForItem(pickedItemDb, settings.BotData.Role)
};
var botEquipBlacklist = _botEquipmentFilterService.GetBotEquipmentBlacklist(
settings.BotData.EquipmentRole,
(double)settings.GeneratingPlayerLevel
);
// Edge case: Filter the armor items mod pool if bot exists in config dict + config has armor slot
if ((_botConfig.Equipment[settings.BotData.EquipmentRole] is not null) &&
(settings.RandomisationDetails.RandomisedArmorSlots.Contains(settings.RootEquipmentSlot.ToString())))
{
// Filter out mods from relevant blacklist
settings.ModPool[pickedItemDb.Id] = GetFilteredDynamicModsForItem(
pickedItemDb.Id,
botEquipBlacklist.Equipment
);
}
// Does item have slots for sub-mods to be inserted into
if (pickedItemDb.Properties.Slots?.Count() > 0 && (settings.GenerateModsBlacklist.Contains(pickedItemDb.Id)))
{
var childItemsToAdd = _botEquipmentModGenerator.GenerateModsForEquipment(
[item],
id,
pickedItemDb,
settings,
botEquipBlacklist
);
settings.Inventory.Items.AddRange(childItemsToAdd);
}
else
{
// No slots, add root item only
settings.Inventory.Items.Add(item);
}
return true;
}
return false;
}
/// <summary>
@@ -386,7 +532,19 @@ public class BotInventoryGenerator
/// <returns>Filtered pool of mods</returns>
public Dictionary<string, List<string>> GetFilteredDynamicModsForItem(string itemTpl, Dictionary<string, List<string>> equipmentBlacklist)
{
throw new NotImplementedException();
var modPool = _botEquipmentModPoolService.GetModsForGearSlot(itemTpl);
foreach (var modSlot in modPool.Keys ?? Enumerable.Empty<string>())
{
var blacklistedMods = equipmentBlacklist[modSlot] ?? [];
var filteredMods = modPool[modSlot].Where((slotName) => !blacklistedMods.Contains(slotName));
if (filteredMods.Count() > 0)
{
modPool[modSlot] = filteredMods.ToList();
}
}
return modPool;
}
/// <summary>
@@ -401,10 +559,27 @@ public class BotInventoryGenerator
/// <param name="itemGenerationLimitsMinMax">Limits for items the bot can have</param>
/// <param name="botLevel">level of bot having weapon generated</param>
public void GenerateAndAddWeaponsToBot(BotTypeInventory templateInventory, Chances equipmentChances, string sessionId, BotBaseInventory botInventory,
string botRole,
bool isPmc, Generation itemGenerationLimitsMinMax, int botLevel)
string botRole, bool isPmc, Generation itemGenerationLimitsMinMax, int botLevel)
{
throw new NotImplementedException();
var weaponSlotsToFill = GetDesiredWeaponsForBot(equipmentChances);
foreach (var weaponSlot in weaponSlotsToFill)
{
// Add weapon to bot if true and bot json has something to put into the slot
if (weaponSlot.ShouldSpawn && templateInventory.Equipment[weaponSlot.Slot].Any())
{
AddWeaponAndMagazinesToInventory(
sessionId,
weaponSlot,
templateInventory,
botInventory,
equipmentChances,
botRole,
isPmc,
itemGenerationLimitsMinMax,
botLevel
);
}
}
}
/// <summary>
@@ -412,9 +587,30 @@ public class BotInventoryGenerator
/// </summary>
/// <param name="equipmentChances">Chances bot has certain equipment</param>
/// <returns>What slots bot should have weapons generated for</returns>
public object GetDesiredWeaponsForBot(Chances equipmentChances) // TODO: Type fuckery { slot: EquipmentSlots; shouldSpawn: boolean }[]
public List<DesiredWeapons> GetDesiredWeaponsForBot(Chances equipmentChances) // TODO: Type fuckery { slot: EquipmentSlots; shouldSpawn: boolean }[]
{
throw new NotImplementedException();
var shouldSpawnPrimary = _randomUtil.GetChance100(equipmentChances.EquipmentChances["FirstPrimaryWeapon"]);
return
[
new()
{
Slot = EquipmentSlots.FirstPrimaryWeapon, ShouldSpawn = shouldSpawnPrimary
},
new()
{
Slot = EquipmentSlots.SecondPrimaryWeapon,
ShouldSpawn = shouldSpawnPrimary
? _randomUtil.GetChance100(equipmentChances.EquipmentChances["SecondPrimaryWeapon"])
: false
},
new()
{
Slot = EquipmentSlots.Holster,
ShouldSpawn = shouldSpawnPrimary
? _randomUtil.GetChance100(equipmentChances.EquipmentChances["Holster"]) // Primary weapon = roll for chance at pistol
: true // No primary = force pistol
}
];
}
/// <summary>
@@ -429,10 +625,34 @@ public class BotInventoryGenerator
/// <param name="isPmc">Is the bot being generated as a pmc</param>
/// <param name="itemGenerationWeights"></param>
/// <param name="botLevel"></param>
public void AddWeaponAndMagazineToInventory(string sessionId, object weaponSlot, BotBaseInventory templateInventory, BotBaseInventory botInventory,
public void AddWeaponAndMagazinesToInventory(string sessionId, DesiredWeapons weaponSlot, BotTypeInventory templateInventory, BotBaseInventory botInventory,
Chances equipmentChances, string botRole,
bool isPmc, Generation itemGenerationWeights, int botLevel)
{
throw new NotImplementedException();
var generatedweapon = _botWeaponGenerator.GenerateRandomWeapon(
sessionId,
weaponSlot.Slot.ToString(),
templateInventory,
botInventory.Equipment,
equipmentChances.WeaponModsChances,
botRole,
isPmc,
botLevel
);
botInventory.Items.AddRange(generatedweapon.Weapon);
_botWeaponGenerator.AddExtraMagazinesToInventory(
generatedweapon,
itemGenerationWeights.Items.Magazines,
botInventory,
botRole);
}
}
public class DesiredWeapons
{
public EquipmentSlots Slot { get; set; }
public bool ShouldSpawn { get; set; }
}