BotGenerator/Generator/Helpers/Gear/GearChanceHelpers.cs
DrakiaXYZ 9ddbc91977 Drastically reduce memory footprint at the cost of removing threading
- Drastically improve performance, where non-threaded perf is now better than previous threaded perf
- Process all data in-line, as opposed to building huge lists of bot data that is then processed in groups later
- Memory footprint should now be relatively static no matter the amount of dumps
2024-11-03 23:29:50 -08:00

436 lines
17 KiB
C#

using Common.Models.Input;
using Common.Models.Output;
using Common.Models;
using Generator.Weighting;
namespace Generator.Helpers.Gear
{
public static class GearChanceHelpers
{
private static Dictionary<string, Dictionary<string, int>> weaponModCount = new Dictionary<string, Dictionary<string, int>>();
private static Dictionary<string, Dictionary<string, int>> weaponSlotCount = new Dictionary<string, Dictionary<string, int>>();
private static Dictionary<string, Dictionary<string, int>> equipmentModCount = new Dictionary<string, Dictionary<string, int>>();
private static Dictionary<string, Dictionary<string, int>> equipmentSlotCount = new Dictionary<string, Dictionary<string, int>>();
public static void AddModChances(Bot bot, Datum baseBot)
{
// TODO: Further split these counts by equipment slot? (ex. "FirstPrimaryWeapon", "Holster", etc.)
var validSlots = new List<string> { "FirstPrimaryWeapon", "SecondPrimaryWeapon", "Holster" };
if (!weaponModCount.TryGetValue(baseBot.Info.Settings.Role.ToLower(), out var modCounts))
{
modCounts = new Dictionary<string, int>();
weaponModCount.Add(baseBot.Info.Settings.Role.ToLower(), modCounts);
}
if (!weaponSlotCount.TryGetValue(baseBot.Info.Settings.Role.ToLower(), out var slotCounts))
{
slotCounts = new Dictionary<string, int>();
weaponSlotCount.Add(baseBot.Info.Settings.Role.ToLower(), slotCounts);
}
var validParents = new List<string>();
foreach (var inventoryItem in baseBot.Inventory.items)
{
if (validSlots.Contains(inventoryItem.slotId))
{
validParents.Add(inventoryItem._id);
}
else if (validParents.Contains(inventoryItem.parentId))
{
validParents.Add(inventoryItem._id);
}
else
{
continue;
}
var template = ItemTemplateHelper.GetTemplateById(inventoryItem._tpl);
var parentTemplate = ItemTemplateHelper.GetTemplateById(baseBot.Inventory.items.Single(i => i._id == inventoryItem.parentId)._tpl);
if (!(parentTemplate?._props?.Slots?.FirstOrDefault(slot => slot._name == inventoryItem.slotId)?._required ?? false))
{
if (modCounts.ContainsKey(inventoryItem.slotId.ToLower()))
{
modCounts[inventoryItem.slotId.ToLower()]++;
}
else
{
modCounts.Add(inventoryItem.slotId.ToLower(), 1);
}
}
if ((template?._props?.Slots?.Count ?? 0) < 1)
{
// Item has no slots, nothing to count here
continue;
}
foreach (var slot in template._props.Slots)
{
if (slot._required)
{
continue;
}
if (slot._name.StartsWith("camora"))
{
continue;
}
if (slotCounts.ContainsKey(slot._name.ToLower()))
{
slotCounts[slot._name.ToLower()]++;
}
else
{
slotCounts.Add(slot._name.ToLower(), 1);
}
}
}
}
public static void CalculateModChances(Bot bot)
{
if (!weaponModCount.TryGetValue(bot.botType.ToString(), out var modCounts))
{
modCounts = new Dictionary<string, int>();
weaponModCount.Add(bot.botType.ToString(), modCounts);
}
if (!weaponSlotCount.TryGetValue(bot.botType.ToString(), out var slotCounts))
{
slotCounts = new Dictionary<string, int>();
weaponSlotCount.Add(bot.botType.ToString(), slotCounts);
}
bot.chances.weaponMods = slotCounts.ToDictionary(
kvp => kvp.Key,
kvp => GetPercent(kvp.Value, modCounts.GetValueOrDefault(kvp.Key)));
}
public static void AddEquipmentModChances(Bot bot, Datum baseBot)
{
if (!equipmentModCount.TryGetValue(baseBot.Info.Settings.Role.ToLower(), out var modCounts))
{
modCounts = new Dictionary<string, int>();
equipmentModCount.Add(baseBot.Info.Settings.Role.ToLower(), modCounts);
}
if (!equipmentSlotCount.TryGetValue(baseBot.Info.Settings.Role.ToLower(), out var slotCounts))
{
slotCounts = new Dictionary<string, int>();
equipmentSlotCount.Add(baseBot.Info.Settings.Role.ToLower(), slotCounts);
}
// TODO: Further split these counts by equipment slot? (ex. "FirstPrimaryWeapon", "Holster", etc.)
var validSlots = new List<string> { "Headwear", "ArmorVest", "TacticalVest" };
var validParents = new List<string>();
foreach (var inventoryItem in baseBot.Inventory.items)
{
if (validSlots.Contains(inventoryItem.slotId))
{
validParents.Add(inventoryItem._id);
}
else if (validParents.Contains(inventoryItem.parentId))
{
validParents.Add(inventoryItem._id);
}
else
{
continue;
}
var template = ItemTemplateHelper.GetTemplateById(inventoryItem._tpl);
var parentTemplate = ItemTemplateHelper.GetTemplateById(baseBot.Inventory.items.Single(i => i._id == inventoryItem.parentId)._tpl);
if (!(parentTemplate?._props?.Slots?.FirstOrDefault(slot => slot._name == inventoryItem.slotId)?._required ?? false))
{
if (modCounts.ContainsKey(inventoryItem.slotId.ToLower()))
{
modCounts[inventoryItem.slotId.ToLower()]++;
}
else
{
modCounts.Add(inventoryItem.slotId.ToLower(), 1);
}
}
if ((template?._props?.Slots?.Count ?? 0) < 1)
{
// Item has no slots, nothing to count here
continue;
}
foreach (var slot in template._props.Slots)
{
if (slot._required)
{
continue;
}
if (slot._name.StartsWith("camora"))
{
continue;
}
if (slotCounts.ContainsKey(slot._name.ToLower()))
{
slotCounts[slot._name.ToLower()]++;
}
else
{
slotCounts.Add(slot._name.ToLower(), 1);
}
}
}
}
public static void CalculateEquipmentModChances(Bot bot)
{
if (!equipmentModCount.TryGetValue(bot.botType.ToString(), out var modCounts))
{
modCounts = new Dictionary<string, int>();
equipmentModCount.Add(bot.botType.ToString(), modCounts);
}
if (!equipmentSlotCount.TryGetValue(bot.botType.ToString(), out var slotCounts))
{
slotCounts = new Dictionary<string, int>();
equipmentSlotCount.Add(bot.botType.ToString(), slotCounts);
}
bot.chances.equipmentMods = slotCounts.ToDictionary(
kvp => kvp.Key,
kvp => GetPercent(kvp.Value, modCounts.GetValueOrDefault(kvp.Key)));
}
internal static void ApplyEquipmentChanceOverrides(Bot botToUpdate)
{
switch (botToUpdate.botType)
{
case BotType.bosstagilla:
botToUpdate.chances.equipment.FaceCover = 100;
break;
}
}
public static void ApplyModChanceOverrides(Bot botToUpdate)
{
switch (botToUpdate.botType)
{
case BotType.bosskojaniy:
botToUpdate.chances.weaponMods["mod_stock"] = 100;
botToUpdate.chances.weaponMods["mod_scope"] = 100;
break;
case BotType.bosstagilla:
botToUpdate.chances.weaponMods["mod_tactical"] = 100; // force ultima thermal camera
botToUpdate.chances.weaponMods["mod_stock"] = 100;
break;
case BotType.bossbully:
botToUpdate.chances.weaponMods["mod_stock"] = 100;
break;
case BotType.bosskilla:
botToUpdate.chances.weaponMods["mod_stock"] = 100;
botToUpdate.chances.weaponMods["mod_stock_001"] = 100;
break;
case BotType.bosssanitar:
botToUpdate.chances.weaponMods["mod_scope"] = 100;
break;
case BotType.pmcbot:
botToUpdate.chances.weaponMods["mod_stock"] = 100;
break;
case BotType.followerbully:
botToUpdate.chances.weaponMods["mod_stock"] = 100;
botToUpdate.chances.weaponMods["mod_stock_000"] = 100;
break;
case BotType.followergluharassault:
case BotType.followergluharscout:
case BotType.followergluharsecurity:
case BotType.followergluharsnipe:
botToUpdate.chances.weaponMods["mod_stock"] = 100;
break;
case BotType.followerkojaniy:
botToUpdate.chances.weaponMods["mod_stock"] = 100;
break;
case BotType.sectantpriest:
botToUpdate.chances.weaponMods["mod_stock"] = 100;
break;
case BotType.sectantwarrior:
botToUpdate.chances.weaponMods["mod_stock"] = 100;
break;
case BotType.marksman:
botToUpdate.chances.weaponMods["mod_scope"] = 100;
botToUpdate.chances.weaponMods["mod_stock"] = 100;
break;
case BotType.exusec:
botToUpdate.chances.weaponMods["mod_stock"] = 100;
botToUpdate.chances.weaponMods["mod_stock_000"] = 100;
botToUpdate.chances.weaponMods["mod_stock_001"] = 100;
break;
}
}
public static void AddGenerationChances(Bot bot, WeightingService weightingService)
{
var weightsData = weightingService.GetBotGenerationWeights(bot.botType);
bot.generation = new GenerationChances(
weightsData["specialItems"],
weightsData["healing"],
weightsData["drugs"],
weightsData["stims"],
weightsData["food"],
weightsData["drinks"],
weightsData["currency"],
weightsData["backpackLoot"],
weightsData["pocketLoot"],
weightsData["vestLoot"],
weightsData["magazines"],
weightsData["grenades"]);
}
public static void AddEquipmentChances(Bot bot, Datum baseBot)
{
bot.chances.equipment.Headwear += baseBot.Inventory.items.Count(x => x.slotId == "Headwear");
bot.chances.equipment.Earpiece += baseBot.Inventory.items.Count(x => x.slotId == "Earpiece");
bot.chances.equipment.FaceCover += baseBot.Inventory.items.Count(x => x.slotId == "FaceCover");
bot.chances.equipment.ArmorVest += baseBot.Inventory.items.Count(x => x.slotId == "ArmorVest");
bot.chances.equipment.Eyewear += baseBot.Inventory.items.Count(x => x.slotId == "Eyewear");
bot.chances.equipment.ArmBand += baseBot.Inventory.items.Count(x => x.slotId == "ArmBand");
bot.chances.equipment.TacticalVest += baseBot.Inventory.items.Count(x => x.slotId == "TacticalVest");
bot.chances.equipment.Backpack += baseBot.Inventory.items.Count(x => x.slotId == "Backpack");
bot.chances.equipment.FirstPrimaryWeapon += baseBot.Inventory.items.Count(x => x.slotId == "FirstPrimaryWeapon");
bot.chances.equipment.SecondPrimaryWeapon += baseBot.Inventory.items.Count(x => x.slotId == "SecondPrimaryWeapon");
bot.chances.equipment.Holster += baseBot.Inventory.items.Count(x => x.slotId == "Holster");
bot.chances.equipment.Scabbard += baseBot.Inventory.items.Count(x => x.slotId == "Scabbard");
bot.chances.equipment.Pockets += baseBot.Inventory.items.Count(x => x.slotId == "Pockets");
bot.chances.equipment.SecuredContainer += baseBot.Inventory.items.Count(x => x.slotId == "SecuredContainer");
}
public static void CalculateEquipmentChances(Bot bot)
{
if (bot.botCount == 0)
{
// No bots, don't do anything
return;
}
bot.chances.equipment = new EquipmentChances(
GetPercent(bot.botCount, bot.chances.equipment.Headwear),
GetPercent(bot.botCount, bot.chances.equipment.Earpiece),
GetPercent(bot.botCount, bot.chances.equipment.FaceCover),
GetPercent(bot.botCount, bot.chances.equipment.ArmorVest),
GetPercent(bot.botCount, bot.chances.equipment.Eyewear),
GetPercent(bot.botCount, bot.chances.equipment.ArmBand),
GetPercent(bot.botCount, bot.chances.equipment.TacticalVest),
GetPercent(bot.botCount, bot.chances.equipment.Backpack),
GetPercent(bot.botCount, bot.chances.equipment.FirstPrimaryWeapon),
GetPercent(bot.botCount, bot.chances.equipment.SecondPrimaryWeapon),
GetPercent(bot.botCount, bot.chances.equipment.Holster),
GetPercent(bot.botCount, bot.chances.equipment.Scabbard),
GetPercent(bot.botCount, bot.chances.equipment.Pockets),
GetPercent(bot.botCount, bot.chances.equipment.SecuredContainer));
}
private static int GetPercent(int total, int count)
{
var percentChance = (int)Math.Ceiling((double)(((200 * count) + 1) / (total * 2)));
return percentChance > 100 ? 100 : percentChance; // return 100 if value is > 100
}
private static MinMax GetMagazineCountByBotType(BotType botType)
{
int min;
int max;
switch (botType)
{
case BotType.bosskilla:
min = 3;
max = 3;
break;
default:
min = 2;
max = 4;
break;
}
return new MinMax(min, max);
}
private static MinMax GetLooseLootCountByBotType(BotType botType)
{
int min;
int max;
switch (botType)
{
case BotType.assault:
min= 0;
max= 6;
break;
case BotType.marksman:
min = 0;
max = 0;
break;
case BotType.exusec:
min = 2;
max = 4;
break;
case BotType.bossbully:
min = 3;
max= 7;
break;
case BotType.bossgluhar:
min = 2;
max = 9;
break;
case BotType.bosskilla:
min = 4;
max = 10;
break;
case BotType.bosskojaniy:
min = 0;
max = 7;
break;
case BotType.bosssanitar:
case BotType.followersanitar:
min = 2;
max = 5;
break;
default:
min = 1;
max = 4;
break;
}
return new MinMax(min, max);
}
private static MinMax GetMedicalItemCountByBotType(BotType botType)
{
int min;
int max;
switch (botType)
{
case BotType.bosssanitar:
case BotType.followersanitar:
min = 4;
max = 7;
break;
default:
min = 1;
max = 2;
break;
}
return new MinMax(min, max);
}
}
}