Compare commits

..

No commits in common. "master" and "master" have entirely different histories.

18 changed files with 469098 additions and 577102 deletions

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -69,7 +69,7 @@ namespace Common.Models.Output
public Dictionary<string, int> TacticalVest { get; set; } public Dictionary<string, int> TacticalVest { get; set; }
public Dictionary<string, int> Pockets { get; set; } public Dictionary<string, int> Pockets { get; set; }
public Dictionary<string, int> Backpack { get; set; } public Dictionary<string, int> Backpack { get; set; }
public Dictionary<string, int> SecuredContainer { get; set; } public new Dictionary<string, int> SecuredContainer { get; set; }
public Dictionary<string, int> SpecialLoot { get; set; } public new Dictionary<string, int> SpecialLoot { get; set; }
} }
} }

View File

@ -1,5 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Common.Models.Output; namespace Common.Models.Output;
@ -19,7 +18,6 @@ public class Bot
public Bot(BotType botType) public Bot(BotType botType)
{ {
this.botType = botType; this.botType = botType;
botCount = 0;
appearance = new Appearance(); appearance = new Appearance();
experience = new Experience(); experience = new Experience();
health = new Health(); health = new Health();
@ -34,8 +32,6 @@ public class Bot
[JsonIgnore] [JsonIgnore]
public BotType botType { get; set; } public BotType botType { get; set; }
[JsonIgnore]
public int botCount { get; set; }
public Appearance appearance { get; set; } public Appearance appearance { get; set; }
public Experience experience { get; set; } public Experience experience { get; set; }
public Health health { get; set; } public Health health { get; set; }
@ -70,20 +66,20 @@ public class Experience
public Experience() public Experience()
{ {
level = new MinMax(0, 1); level = new MinMax(0, 1);
reward = new Dictionary<string, MinMax>(); reward = new MinMax(-1, -1);
standingForKill = new Dictionary<string, object>(); standingForKill = -0.02;
aggressorBonus = new Dictionary<string, object>(); ; aggressorBonus = 0.01;
useSimpleAnimator = false; useSimpleAnimator = false;
} }
public MinMax level { get; set; } public MinMax level { get; set; }
public Dictionary<string, MinMax> reward { get; set; } public MinMax reward { get; set; }
public Dictionary<string, object> standingForKill { get; set; } public object standingForKill { get; set; }
public new Dictionary<string, object> aggressorBonus { get; set; } public object aggressorBonus { get; set; }
public bool useSimpleAnimator { get; set; } public bool useSimpleAnimator { get; set; }
} }
public class BodyParts : IEquatable<BodyParts> public class BodyParts
{ {
public BodyParts() public BodyParts()
{ {
@ -96,17 +92,6 @@ public class BodyParts : IEquatable<BodyParts>
RightLeg = new MinMax(65, 65); RightLeg = new MinMax(65, 65);
} }
public bool Equals(BodyParts other)
{
return this.Head.Equals(other.Head) &&
this.Chest.Equals(other.Chest) &&
this.Stomach.Equals(other.Stomach) &&
this.LeftArm.Equals(other.LeftArm) &&
this.RightArm.Equals(other.RightArm) &&
this.LeftLeg.Equals(other.LeftLeg) &&
this.RightLeg.Equals(other.RightLeg);
}
public MinMax Head { get; set; } public MinMax Head { get; set; }
public MinMax Chest { get; set; } public MinMax Chest { get; set; }
public MinMax Stomach { get; set; } public MinMax Stomach { get; set; }
@ -271,7 +256,7 @@ public class ItemChances
public GenerationWeightData grenades { get; set; } public GenerationWeightData grenades { get; set; }
} }
public class MinMax : IEquatable<MinMax> public class MinMax
{ {
public MinMax(int min, int max) public MinMax(int min, int max)
{ {
@ -279,11 +264,6 @@ public class MinMax : IEquatable<MinMax>
this.max = max; this.max = max;
} }
public bool Equals(MinMax other)
{
return this.min == other.min && this.max == other.max;
}
public int min { get; set; } public int min { get; set; }
public int max { get; set; } public int max { get; set; }
} }

File diff suppressed because it is too large Load Diff

137
Common/Bot/BotParser.cs Normal file
View File

@ -0,0 +1,137 @@
using System.Collections.Concurrent;
using Common.Models.Input;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Common.Models.Output;
namespace Common.Bots;
public static class BotParser
{
private static readonly JsonSerializerOptions serialiserOptions = new() { };
public static async Task<List<Datum>> ParseAsync(string dumpPath, HashSet<string> botTypes)
{
var stopwatch = Stopwatch.StartNew();
DiskHelpers.CreateDirIfDoesntExist(dumpPath);
var botFiles = Directory.GetFiles(dumpPath, "*.json", SearchOption.TopDirectoryOnly);
LoggingHelpers.LogToConsole($"{botFiles.Length} bot dump files found");
// key = bot type
// Store bots keyed against their ID so we never get duplicates
var parsedBotsDict = new ConcurrentDictionary<string, Datum>();
int totalDupeCount = 0;
var tasks = new List<Task>();
foreach (var filePath in botFiles)
{
tasks.Add(ProcessBotFile(botTypes, filePath, parsedBotsDict, totalDupeCount));
}
await Task.WhenAll(tasks.ToArray());
stopwatch.Stop();
LoggingHelpers.LogToConsole($"Cleaned and Parsed: {parsedBotsDict.Count} bots. {totalDupeCount} dupes were ignored. Took {LoggingHelpers.LogTimeTaken(stopwatch.Elapsed.TotalSeconds)} seconds");
return [.. parsedBotsDict.Values];
}
private static async Task<int> ProcessBotFile(
HashSet<string> botTypes,
string filePath,
ConcurrentDictionary<string, Datum> parsedBotsDict,
int totalDupeCount)
{
var splitFilePath = filePath.Split("\\");
int dupeCount = 0;
List<Datum> bots = [];
try
{
// Parse the bots inside the json file
using (var reader = new StreamReader(filePath))
{
var deSerialisedObject = JsonSerializer.Deserialize<Root>(reader.ReadToEnd(), serialiserOptions);
bots.AddRange(deSerialisedObject.data.Where(botData => botTypes.Contains(botData.Info.Settings.Role.ToLower())));
}
}
catch (Exception ex)
{
Console.WriteLine($"File parse fucked up: {filePath}");
throw;
}
if (bots == null || bots.Count == 0)
{
Console.WriteLine($"Skipping file: {splitFilePath.Last()}. no bots found, ");
return totalDupeCount;
}
//Console.WriteLine($"parsing: {bots.Count} bots in file {splitFilePath.Last()}");
foreach (var bot in bots)
{
// Bot fucks up something, never allow it in
if (bot._id == "6483938c53cc9087c70eae86")
{
Console.WriteLine("oh no");
continue;
}
// null out unnecessary data to save ram
bot.Stats = null;
bot.Encyclopedia = null;
bot.Hideout = null;
bot.TaskConditionCounters = null;
bot.Bonuses = null;
bot.InsuredItems = null;
// Add bot if not already added
if (!parsedBotsDict.TryAdd(bot._id, bot))
{
dupeCount++;
}
}
totalDupeCount += dupeCount;
//Console.WriteLine($"Parsed file: {filePath}");
return totalDupeCount;
}
private static IEnumerable<Datum> ParseJson(string json)
{
var deSerialisedObject = JsonSerializer.Deserialize<Root>(json, serialiserOptions);
return deSerialisedObject.data;
}
private static string PruneMalformedBsgJson(string json, string fileName)
{
// Bsg send json where an item has a location of 1 but it should be an object with x/y/z coords
var o = JObject.Parse(json);
var jItemsToReplace = o.SelectTokens("$.data[*].Inventory.items[?(@.location == 1)].location");
//var jItemsToReplace = o.SelectTokens("$.data[*].Inventory.items[?(@.location == 1 && @.slotId == 'cartridges')].location");
if (jItemsToReplace != null && jItemsToReplace.Any())
{
LoggingHelpers.LogToConsole($"file {fileName} has {jItemsToReplace.Count()} json issues, cleaning up.", ConsoleColor.Yellow);
foreach (var item in jItemsToReplace)
{
var obj = new { x = 1, y = 0, r = 0 };
item.Replace(JToken.FromObject(obj));
}
}
var returnString = o.ToString();
o = null;
jItemsToReplace = null;
return returnString;
}
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -46,7 +46,7 @@
}, },
"grenades": { "grenades": {
"weights": { "weights": {
"0": 13, "0": 8,
"1": 6, "1": 6,
"2": 2, "2": 2,
"3": 1, "3": 1,
@ -1339,375 +1339,5 @@
}, },
"whitelist": [] "whitelist": []
} }
},
"infectedpmc": {
"backpackLoot": {
"weights": {
"0": 1
},
"whitelist": []
},
"currency": {
"weights": {
"0": 1
},
"whitelist": []
},
"drinks": {
"weights": {
"0": 1
},
"whitelist": []
},
"drugs": {
"weights": {
"0": 1
},
"whitelist": []
},
"food": {
"weights": {
"0": 1
},
"whitelist": []
},
"grenades": {
"weights": {
"0": 1
},
"whitelist": []
},
"healing": {
"weights": {
"0": 1
},
"whitelist": []
},
"magazines": {
"weights": {
"0": 1
},
"whitelist": []
},
"pocketLoot": {
"weights": {
"0": 1
},
"whitelist": []
},
"specialItems": {
"weights": {
"0": 1
},
"whitelist": []
},
"stims": {
"weights": {
"0": 1
},
"whitelist": []
},
"vestLoot": {
"weights": {
"0": 1
},
"whitelist": []
}
},
"infectedassault": {
"backpackLoot": {
"weights": {
"0": 1
},
"whitelist": []
},
"currency": {
"weights": {
"0": 1
},
"whitelist": []
},
"drinks": {
"weights": {
"0": 1
},
"whitelist": []
},
"drugs": {
"weights": {
"0": 1
},
"whitelist": []
},
"food": {
"weights": {
"0": 1
},
"whitelist": []
},
"grenades": {
"weights": {
"0": 1
},
"whitelist": []
},
"healing": {
"weights": {
"0": 1
},
"whitelist": []
},
"magazines": {
"weights": {
"0": 1
},
"whitelist": []
},
"pocketLoot": {
"weights": {
"0": 1
},
"whitelist": []
},
"specialItems": {
"weights": {
"0": 1
},
"whitelist": []
},
"stims": {
"weights": {
"0": 1
},
"whitelist": []
},
"vestLoot": {
"weights": {
"0": 1
},
"whitelist": []
}
},
"infectedcivil": {
"backpackLoot": {
"weights": {
"0": 1
},
"whitelist": []
},
"currency": {
"weights": {
"0": 1
},
"whitelist": []
},
"drinks": {
"weights": {
"0": 1
},
"whitelist": []
},
"drugs": {
"weights": {
"0": 1
},
"whitelist": []
},
"food": {
"weights": {
"0": 1
},
"whitelist": []
},
"grenades": {
"weights": {
"0": 1
},
"whitelist": []
},
"healing": {
"weights": {
"0": 1
},
"whitelist": []
},
"magazines": {
"weights": {
"0": 1
},
"whitelist": []
},
"pocketLoot": {
"weights": {
"0": 1
},
"whitelist": []
},
"specialItems": {
"weights": {
"0": 1
},
"whitelist": []
},
"stims": {
"weights": {
"0": 1
},
"whitelist": []
},
"vestLoot": {
"weights": {
"0": 1
},
"whitelist": []
}
},
"infectedlaborant": {
"backpackLoot": {
"weights": {
"0": 1
},
"whitelist": []
},
"currency": {
"weights": {
"0": 1
},
"whitelist": []
},
"drinks": {
"weights": {
"0": 1
},
"whitelist": []
},
"drugs": {
"weights": {
"0": 1
},
"whitelist": []
},
"food": {
"weights": {
"0": 1
},
"whitelist": []
},
"grenades": {
"weights": {
"0": 1
},
"whitelist": []
},
"healing": {
"weights": {
"0": 1
},
"whitelist": []
},
"magazines": {
"weights": {
"0": 1
},
"whitelist": []
},
"pocketLoot": {
"weights": {
"0": 1
},
"whitelist": []
},
"specialItems": {
"weights": {
"0": 1
},
"whitelist": []
},
"stims": {
"weights": {
"0": 1
},
"whitelist": []
},
"vestLoot": {
"weights": {
"0": 1
},
"whitelist": []
}
},
"infectedtagilla": {
"backpackLoot": {
"weights": {
"0": 1
},
"whitelist": []
},
"currency": {
"weights": {
"0": 1
},
"whitelist": []
},
"drinks": {
"weights": {
"0": 1
},
"whitelist": []
},
"drugs": {
"weights": {
"0": 1
},
"whitelist": []
},
"food": {
"weights": {
"0": 1
},
"whitelist": []
},
"grenades": {
"weights": {
"0": 1
},
"whitelist": []
},
"healing": {
"weights": {
"0": 1
},
"whitelist": []
},
"magazines": {
"weights": {
"0": 1
},
"whitelist": []
},
"pocketLoot": {
"weights": {
"0": 1
},
"whitelist": []
},
"specialItems": {
"weights": {
"0": 1
},
"whitelist": []
},
"stims": {
"weights": {
"0": 1
},
"whitelist": []
},
"vestLoot": {
"weights": {
"0": 1
},
"whitelist": []
}
} }
} }

View File

@ -4,87 +4,110 @@ using Common.Models.Input;
using Common.Models.Output; using Common.Models.Output;
using Generator.Helpers; using Generator.Helpers;
using Generator.Helpers.Gear; using Generator.Helpers.Gear;
using System.Diagnostics;
namespace Generator namespace Generator
{ {
public static class BaseBotGenerator public static class BaseBotGenerator
{ {
public static void UpdateBaseDetails(Bot botData, Datum rawBotData) //TODO: pass in bot types and use those to create the classes in rawBots list
public static IEnumerable<Bot> GenerateBaseDetails(IEnumerable<Datum> rawBots, string workingPath, IEnumerable<string> botTypes)
{ {
UpdateBodyPartHealth(botData, rawBotData); var stopwatch = Stopwatch.StartNew();
AddExperience(botData, rawBotData); LoggingHelpers.LogToConsole("Started processing bot base");
AddStandingForKill(botData, rawBotData);
AddAggressorBonus(botData, rawBotData);
AddSkills(botData, rawBotData);
botData.experience.useSimpleAnimator = rawBotData.Info.Settings.UseSimpleAnimator;
AddVisualAppearanceItems(botData, rawBotData); // Create a list of bot objects ready to be hydrated
AddName(botData, rawBotData); var baseBots = new List<Bot>();
AddVoice(botData, rawBotData); foreach (var botType in botTypes)
{
var typeToAdd = (BotType)Enum.Parse(typeof(BotType), botType);
baseBots.Add(new Bot(typeToAdd));
} }
private static void AddSkills(Bot botToUpdate, Datum rawBotData) // Iterate over each bot type we just made and put some data into them
foreach (var botToUpdate in baseBots)
{ {
var rawBotType = botToUpdate.botType.ToString();
var rawBotsOfSameType = rawBots.Where(x => string.Equals(x.Info.Settings.Role, rawBotType, StringComparison.OrdinalIgnoreCase))
.ToList();
var rawBotsOfSameTypeCount = rawBotsOfSameType.Count.ToString();
if (rawBotsOfSameType.Count == 0)
{
LoggingHelpers.LogToConsole($"no bots of type {rawBotType}, skipping", ConsoleColor.DarkRed);
continue;
}
LoggingHelpers.LogToConsole($"Found {rawBotsOfSameTypeCount} bots of type: {rawBotType}");
UpdateBodyPartHealth(botToUpdate, rawBotsOfSameType);
AddDifficulties(botToUpdate, workingPath);
AddExperience(botToUpdate, rawBotsOfSameType);
AddStandingForKill(botToUpdate, rawBotsOfSameType);
AddSkills(botToUpdate, rawBotsOfSameType);
botToUpdate.experience.useSimpleAnimator = rawBotsOfSameType.First().Info.Settings.UseSimpleAnimator;
foreach (var rawParsedBot in rawBotsOfSameType)
{
AddVisualAppearanceItems(botToUpdate, rawParsedBot);
AddName(botToUpdate, rawParsedBot);
AddVoice(botToUpdate, rawParsedBot);
}
}
stopwatch.Stop();
LoggingHelpers.LogToConsole($"Finished processing bot base. Took {LoggingHelpers.LogTimeTaken(stopwatch.Elapsed.TotalSeconds)} seconds");
return baseBots;
}
private static void AddSkills(Bot botToUpdate, IEnumerable<Datum> rawBotsOfSameType)
{
var firstBotOfDesiredType = rawBotsOfSameType.Last();
// Find the smallest and biggest value for each skill // Find the smallest and biggest value for each skill
foreach (var skill in rawBotData.Skills.Common) foreach (var skill in firstBotOfDesiredType.Skills.Common)
{ {
if (botToUpdate.skills.Common.TryGetValue(skill.Id, out var existingSkill)) var skills = new List<Common.Models.Input.Common>();
foreach (var bot in rawBotsOfSameType)
{ {
existingSkill.min = Math.Min(existingSkill.min, skill.Progress); skills.Add(bot.Skills.Common.Find(x => x.Id == skill.Id));
existingSkill.max = Math.Max(existingSkill.max, skill.Progress);
} }
else
var min = skills.Min(x => x?.Progress);
var max = skills.Max(x => x?.Progress);
if (min.HasValue && max.HasValue)
{ {
botToUpdate.skills.Common.Add(skill.Id, new MinMax(skill.Progress, skill.Progress)); botToUpdate.skills.Common.Add(skill.Id, new MinMax(min.Value, max.Value));
} }
} }
} }
private static void AddStandingForKill(Bot botToUpdate, Datum rawBotData) private static void AddStandingForKill(Bot botToUpdate, IEnumerable<Datum> rawBotsOfSameType)
{ {
botToUpdate.experience.standingForKill ??= new Dictionary<string, object>(); var firstBotOfDesiredType = rawBotsOfSameType.Last();
if (!botToUpdate.experience.standingForKill.ContainsKey(rawBotData.Info.Settings.BotDifficulty)) botToUpdate.experience.standingForKill = firstBotOfDesiredType.Info.Settings.StandingForKill;
{ botToUpdate.experience.aggressorBonus = firstBotOfDesiredType.Info.Settings.AggressorBonus;
botToUpdate.experience.standingForKill.Add(rawBotData.Info.Settings.BotDifficulty, rawBotData.Info.Settings.StandingForKill);
}
} }
private static void AddAggressorBonus(Bot botToUpdate, Datum rawBotData) private static void AddExperience(Bot botToUpdate, IEnumerable<Datum> rawBotsOfSameType)
{ {
botToUpdate.experience.aggressorBonus ??= new Dictionary<string, object>(); var firstBotOfDesiredType = rawBotsOfSameType.Last();
if (!botToUpdate.experience.aggressorBonus.ContainsKey(rawBotData.Info.Settings.BotDifficulty))
{
botToUpdate.experience.aggressorBonus.Add(rawBotData.Info.Settings.BotDifficulty, rawBotData.Info.Settings.AggressorBonus);
}
}
private static void AddExperience(Bot botToUpdate, Datum rawBotData)
{
botToUpdate.experience.reward ??= new();
botToUpdate.experience.reward.TryGetValue(rawBotData.Info.Settings.BotDifficulty, out var minMaxValues);
if (minMaxValues is null)
{
botToUpdate.experience.reward.Add(rawBotData.Info.Settings.BotDifficulty, new(rawBotData.Info.Settings.Experience, rawBotData.Info.Settings.Experience));
return;
}
minMaxValues.min = Math.Min(minMaxValues.min, rawBotData.Info.Settings.Experience);
minMaxValues.max = Math.Max(minMaxValues.max, rawBotData.Info.Settings.Experience);
botToUpdate.experience.reward.min = firstBotOfDesiredType.Info.Settings.Experience;
botToUpdate.experience.reward.max = firstBotOfDesiredType.Info.Settings.Experience;
} }
private static void AddVoice(Bot bot, Datum rawBot) private static void AddVoice(Bot bot, Datum rawBot)
{ {
GearHelpers.IncrementDictionaryValue(bot.appearance.voice, rawBot.Info.Voice); GearHelpers.IncrementDictionaryValue(bot.appearance.voice, rawBot.Info.Voice);
GearHelpers.ReduceWeightValues(bot.appearance.voice);
} }
public static void AddDifficulties(Bot bot) private static void AddDifficulties(Bot bot, string workingPath)
{ {
string workingPath = Directory.GetCurrentDirectory();
string botType = bot.botType.ToString(); string botType = bot.botType.ToString();
var botDifficultyFiles = Directory var botDifficultyFiles = Directory
.GetFiles($"{workingPath}//Assets", "*.txt", SearchOption.TopDirectoryOnly) .GetFiles($"{workingPath}//Assets", "*.txt", SearchOption.TopDirectoryOnly)
@ -94,34 +117,51 @@ namespace Generator
DifficultyHelper.AddDifficultySettings(bot, botDifficultyFiles); DifficultyHelper.AddDifficultySettings(bot, botDifficultyFiles);
} }
private static void UpdateBodyPartHealth(Bot botToUpdate, Datum rawBot) private static void UpdateBodyPartHealth(Bot botToUpdate, List<Datum> rawBots)
{
var uniqueHealthSetups = new Dictionary<int, Common.Models.Output.BodyParts>();
foreach (var bot in rawBots)
{
var healthTotal = bot.Health.BodyParts.GetHpMaxTotal();
var alreadyExists = uniqueHealthSetups.ContainsKey(healthTotal);
if (!alreadyExists)
{ {
var bodyPartHpToAdd = new Common.Models.Output.BodyParts() var bodyPartHpToAdd = new Common.Models.Output.BodyParts()
{ {
Head = new MinMax(rawBot.Health.BodyParts.Head.Health.Current, rawBot.Health.BodyParts.Head.Health.Maximum), Head = new MinMax(bot.Health.BodyParts.Head.Health.Current, bot.Health.BodyParts.Head.Health.Maximum),
Chest = new MinMax(rawBot.Health.BodyParts.Chest.Health.Current, rawBot.Health.BodyParts.Chest.Health.Maximum), Chest = new MinMax(bot.Health.BodyParts.Chest.Health.Current, bot.Health.BodyParts.Chest.Health.Maximum),
Stomach = new MinMax(rawBot.Health.BodyParts.Stomach.Health.Current, rawBot.Health.BodyParts.Stomach.Health.Maximum), Stomach = new MinMax(bot.Health.BodyParts.Stomach.Health.Current, bot.Health.BodyParts.Stomach.Health.Maximum),
LeftArm = new MinMax(rawBot.Health.BodyParts.LeftArm.Health.Current, rawBot.Health.BodyParts.LeftArm.Health.Maximum), LeftArm = new MinMax(bot.Health.BodyParts.LeftArm.Health.Current, bot.Health.BodyParts.LeftArm.Health.Maximum),
RightArm = new MinMax(rawBot.Health.BodyParts.RightArm.Health.Current, rawBot.Health.BodyParts.RightArm.Health.Maximum), RightArm = new MinMax(bot.Health.BodyParts.RightArm.Health.Current, bot.Health.BodyParts.RightArm.Health.Maximum),
LeftLeg = new MinMax(rawBot.Health.BodyParts.LeftLeg.Health.Current, rawBot.Health.BodyParts.LeftLeg.Health.Maximum),
RightLeg = new MinMax(rawBot.Health.BodyParts.RightLeg.Health.Current, rawBot.Health.BodyParts.RightLeg.Health.Maximum)
}; };
if (!botToUpdate.health.BodyParts.Contains(bodyPartHpToAdd)) bodyPartHpToAdd.LeftLeg.min = bot.Health.BodyParts.LeftLeg.Health.Current;
{ bodyPartHpToAdd.LeftLeg.max = bot.Health.BodyParts.LeftLeg.Health.Maximum;
botToUpdate.health.BodyParts.Add(bodyPartHpToAdd);
bodyPartHpToAdd.RightLeg.min = bot.Health.BodyParts.RightLeg.Health.Current;
bodyPartHpToAdd.RightLeg.max = bot.Health.BodyParts.RightLeg.Health.Maximum;
uniqueHealthSetups.Add(healthTotal, bodyPartHpToAdd);
} }
} }
botToUpdate.health.BodyParts = uniqueHealthSetups.Values.ToList();
}
private static void AddVisualAppearanceItems(Bot botToUpdate, Datum rawBot) private static void AddVisualAppearanceItems(Bot botToUpdate, Datum rawBot)
{ {
GearHelpers.IncrementDictionaryValue(botToUpdate.appearance.feet, rawBot.Customization.Feet); GearHelpers.IncrementDictionaryValue(botToUpdate.appearance.feet, rawBot.Customization.Feet);
GearHelpers.ReduceWeightValues(botToUpdate.appearance.feet);
GearHelpers.IncrementDictionaryValue(botToUpdate.appearance.body, rawBot.Customization.Body); GearHelpers.IncrementDictionaryValue(botToUpdate.appearance.body, rawBot.Customization.Body);
GearHelpers.ReduceWeightValues(botToUpdate.appearance.body);
GearHelpers.IncrementDictionaryValue(botToUpdate.appearance.head, rawBot.Customization.Head); GearHelpers.IncrementDictionaryValue(botToUpdate.appearance.head, rawBot.Customization.Head);
GearHelpers.ReduceWeightValues(botToUpdate.appearance.head);
GearHelpers.IncrementDictionaryValue(botToUpdate.appearance.hands, rawBot.Customization.Hands); GearHelpers.IncrementDictionaryValue(botToUpdate.appearance.hands, rawBot.Customization.Hands);
GearHelpers.ReduceWeightValues(botToUpdate.appearance.hands);
} }
private static void AddName(Bot botToUpdate, Datum rawBot) private static void AddName(Bot botToUpdate, Datum rawBot)

View File

@ -8,15 +8,51 @@ namespace Generator
{ {
public static class BotChancesGenerator public static class BotChancesGenerator
{ {
public static void AddChances(Bot botToUpdate, Datum rawBot) public static IEnumerable<Bot> AddChances(this IEnumerable<Bot> botsToUpdate, Dictionary<string, List<Datum>> rawBots)
{ {
var stopwatch = Stopwatch.StartNew();
LoggingHelpers.LogToConsole("Started processing bot gear");
// use lock for lock safety
var dictionaryLock = new object();
var weightHelper = new WeightingService(); var weightHelper = new WeightingService();
// multithread
var tasks = new List<Task>();
foreach (var botToUpdate in botsToUpdate)
{
tasks.Add(Task.Factory.StartNew(() =>
{
var botType = botToUpdate.botType.ToString().ToLower();
List<Datum> rawBotsOfSameType;
lock (dictionaryLock)
{
if (!rawBots.TryGetValue(botType, out rawBotsOfSameType))
{
Console.WriteLine($"(chances) Unable to find {botType} on rawBots data");
return;
}
}
if (rawBotsOfSameType.Count == 0)
{
return;
}
// TODO: Add check to make sure incoming bot list has gear // TODO: Add check to make sure incoming bot list has gear
GearChanceHelpers.AddEquipmentChances(botToUpdate, rawBot); GearChanceHelpers.CalculateEquipmentChances(botToUpdate, rawBotsOfSameType);
GearChanceHelpers.AddGenerationChances(botToUpdate, weightHelper); GearChanceHelpers.AddGenerationChances(botToUpdate, weightHelper);
GearChanceHelpers.AddModChances(botToUpdate, rawBot); GearChanceHelpers.CalculateModChances(botToUpdate, rawBotsOfSameType);
GearChanceHelpers.AddEquipmentModChances(botToUpdate, rawBot); GearChanceHelpers.CalculateEquipmentModChances(botToUpdate, rawBotsOfSameType);
GearChanceHelpers.ApplyModChanceOverrides(botToUpdate);
GearChanceHelpers.ApplyEquipmentChanceOverrides(botToUpdate);
}));
}
Task.WaitAll(tasks.ToArray());
stopwatch.Stop();
LoggingHelpers.LogToConsole($"Finished processing bot chances. Took {LoggingHelpers.LogTimeTaken(stopwatch.Elapsed.TotalSeconds)} seconds");
return botsToUpdate;
} }
} }
} }

View File

@ -7,12 +7,52 @@ namespace Generator
{ {
public static class BotGearGenerator public static class BotGearGenerator
{ {
public static void AddGear(Bot botToUpdate, Datum rawBotData) public static IEnumerable<Bot> AddGear(this IEnumerable<Bot> baseBots, Dictionary<string, List<Datum>> rawBots)
{ {
GearHelpers.AddEquippedGear(botToUpdate, rawBotData); var stopwatch = Stopwatch.StartNew();
GearHelpers.AddAmmo(botToUpdate, rawBotData); LoggingHelpers.LogToConsole("Started processing bot gear");
GearHelpers.AddEquippedMods(botToUpdate, rawBotData);
//GearHelpers.AddCartridges(botToUpdate, rawBotData); var dictionaryLock = new object();
var tasks = new List<Task>();
foreach (var botToUpdate in baseBots)
{
tasks.Add(Task.Factory.StartNew(() =>
{
var botType = botToUpdate.botType.ToString().ToLower();
List<Datum> rawBotsOfSameType;
lock (dictionaryLock)
{
if (!rawBots.TryGetValue(botType, out rawBotsOfSameType))
{
Console.WriteLine($"(gear) Unable to find {botType} on rawBots data");
return;
}
}
if (rawBotsOfSameType.Count == 0)
{
return;
}
foreach (var rawParsedBot in rawBotsOfSameType)
{
GearHelpers.AddEquippedGear(botToUpdate, rawParsedBot);
GearHelpers.AddAmmo(botToUpdate, rawParsedBot);
GearHelpers.AddEquippedMods(botToUpdate, rawParsedBot);
//GearHelpers.AddCartridges(botToUpdate, rawParsedBot);
}
GearHelpers.ReduceAmmoWeightValues(botToUpdate);
GearHelpers.ReduceEquipmentWeightValues(botToUpdate.inventory.equipment);
}));
}
Task.WaitAll(tasks.ToArray());
stopwatch.Stop();
LoggingHelpers.LogToConsole($"Finished processing bot gear. Took {LoggingHelpers.LogTimeTaken(stopwatch.Elapsed.TotalSeconds)} seconds");
return baseBots;
} }
} }
} }

View File

@ -1,7 +1,6 @@
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using Common.Extensions; using Common.Extensions;
using Common.Models;
using Common.Models.Input; using Common.Models.Input;
using Common.Models.Output; using Common.Models.Output;
using Generator.Helpers.Gear; using Generator.Helpers.Gear;
@ -10,20 +9,72 @@ namespace Generator
{ {
public static class BotLootGenerator public static class BotLootGenerator
{ {
internal static void AddLoot(Bot botToUpdate, Datum rawBotData) internal static IEnumerable<Bot> AddLoot(this IEnumerable<Bot> botsWithGear, Dictionary<string, List<Datum>> rawBots)
{ {
AddLootToContainers(botToUpdate, rawBotData); var stopwatch = Stopwatch.StartNew();
LoggingHelpers.LogToConsole("Started processing bot loot");
var dictionaryLock = new object();
var tasks = new List<Task>(50);
foreach (var botToUpdate in botsWithGear)
{
tasks.Add(Task.Factory.StartNew(() =>
{
var botType = botToUpdate.botType.ToString().ToLower();
List<Datum> rawBotsOfSameType;
lock (dictionaryLock)
{
if (!rawBots.TryGetValue(botType, out rawBotsOfSameType))
{
Console.WriteLine($"(loot) Unable to find {botType} on rawBots data");
return;
}
} }
private static void AddLootToContainers(Bot botToUpdate, Datum rawBot) if (rawBotsOfSameType.Count == 0)
{
return;
}
AddLootToContainers(botType, botToUpdate, rawBotsOfSameType);
GearHelpers.ReduceWeightValues(botToUpdate.inventory.equipment.Backpack);
GearHelpers.ReduceWeightValues(botToUpdate.inventory.equipment.Pockets);
GearHelpers.ReduceWeightValues(botToUpdate.inventory.equipment.TacticalVest);
GearHelpers.ReduceWeightValues(botToUpdate.inventory.equipment.SecuredContainer);
//foreach (var rawParsedBot in rawBotsOfSameType)
//{
// AddPocketLoot(botToUpdate, rawParsedBot);
//}
//AddTacticalVestLoot(botToUpdate, rawBotsOfSameType);
//AddBackpackLoot(botToUpdate, rawBotsOfSameType);
//AddSecureContainerLoot(botToUpdate, rawBotsOfSameType);
//AddSpecialLoot(botToUpdate);
}));
}
Task.WaitAll(tasks.ToArray());
stopwatch.Stop();
LoggingHelpers.LogToConsole($"Finished processing bot loot. Took: {LoggingHelpers.LogTimeTaken(stopwatch.Elapsed.TotalSeconds)} seconds");
return botsWithGear;
}
private static void AddLootToContainers(string botType, Bot botToUpdate, List<Datum> rawBotsOfSameType)
{
// Process each bot
foreach (var rawBot in rawBotsOfSameType)
{ {
// Filter out base inventory items and equipment mod items // Filter out base inventory items and equipment mod items
var rawBotItems = rawBot.Inventory.items.Where(item => item.location != null); var rawBotItems = rawBot.Inventory.items.Where(item => item.location != null).ToList();
var botBackpack = rawBot.Inventory.items.FirstOrDefault(item => item.slotId == "Backpack"); var botBackpack = rawBot.Inventory.items.FirstOrDefault(item => item.slotId == "Backpack");
if (botBackpack != null) if (botBackpack != null)
{ {
AddLootItemsToContainerDictionary(rawBotItems, botBackpack._id, botToUpdate.inventory.items.Backpack, "backpack", botToUpdate.botType); AddLootItemsToContainerDictionary(rawBotItems, botBackpack._id, botToUpdate.inventory.items.Backpack);
} }
var botPockets = rawBot.Inventory.items.FirstOrDefault(item => item.slotId == "Pockets"); var botPockets = rawBot.Inventory.items.FirstOrDefault(item => item.slotId == "Pockets");
@ -58,12 +109,7 @@ namespace Generator
} }
AddSpecialLoot(botToUpdate); AddSpecialLoot(botToUpdate);
}
// Cleanup of weights
GearHelpers.ReduceWeightValues(botToUpdate.inventory.items.Backpack);
GearHelpers.ReduceWeightValues(botToUpdate.inventory.items.Pockets);
GearHelpers.ReduceWeightValues(botToUpdate.inventory.items.TacticalVest);
GearHelpers.ReduceWeightValues(botToUpdate.inventory.items.SecuredContainer);
} }
/// <summary> /// <summary>
@ -73,17 +119,16 @@ namespace Generator
/// <param name="itemsToFilter">Bots inventory items</param> /// <param name="itemsToFilter">Bots inventory items</param>
/// <param name="containerId"></param> /// <param name="containerId"></param>
/// <param name="dictToAddTo"></param> /// <param name="dictToAddTo"></param>
private static void AddLootItemsToContainerDictionary(IEnumerable<Common.Models.Input.Item> itemsToFilter, string containerId, Dictionary<string, int> dictToAddTo, string container = "", BotType type = BotType.arenafighterevent) private static void AddLootItemsToContainerDictionary(List<Item> itemsToFilter, string containerId, Dictionary<string, int> dictToAddTo)
{ {
foreach (var itemToAdd in itemsToFilter) var lootItems = itemsToFilter.Where(item => item.parentId == containerId);
foreach (var itemToAdd in lootItems)
{ {
if (itemToAdd.parentId != containerId) continue;
if (!dictToAddTo.ContainsKey(itemToAdd._tpl)) if (!dictToAddTo.ContainsKey(itemToAdd._tpl))
{ {
dictToAddTo[itemToAdd._tpl] = 1; dictToAddTo[itemToAdd._tpl] = 1;
continue; return;
} }
dictToAddTo[itemToAdd._tpl]++; dictToAddTo[itemToAdd._tpl]++;

View File

@ -1,274 +0,0 @@
using System.Collections.Concurrent;
using Common.Models.Input;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Common.Models.Output;
using Common.Models;
using Generator;
using Generator.Helpers.Gear;
namespace Common.Bots;
public static class BotParser
{
private static readonly JsonSerializerOptions serialiserOptions = new() { };
public static List<Bot> Parse(string dumpPath, HashSet<string> botTypes)
{
var stopwatch = Stopwatch.StartNew();
// Build the list of base bot data
var baseBots = new HashSet<Bot>();
foreach (var botType in botTypes)
{
var typeToAdd = (BotType)Enum.Parse(typeof(BotType), botType);
baseBots.Add(new Bot(typeToAdd));
}
DiskHelpers.CreateDirIfDoesntExist(dumpPath);
var botFiles = Directory.GetFiles(dumpPath, "*.json", SearchOption.TopDirectoryOnly);
LoggingHelpers.LogToConsole($"{botFiles.Length.ToString()} bot dump files found");
// Store a list of parsed bots so we don't parse the same bot twice
int totalDupeCount = 0;
var parsedBotIds = new HashSet<string>();
int i = 0;
foreach (var filePath in botFiles)
{
i++;
if (i % 500 == 0) Console.WriteLine($"Processing file {i.ToString()}");
ProcessBotFileSync(baseBots, filePath, parsedBotIds, totalDupeCount);
}
// Handle things we can only do once all data has been processed, or only needs to run once per bot type
foreach (var bot in baseBots)
{
// Skip any bots we didn't handle
if (bot.botCount == 0) continue;
BaseBotGenerator.AddDifficulties(bot);
GearChanceHelpers.CalculateModChances(bot);
GearChanceHelpers.CalculateEquipmentModChances(bot);
GearChanceHelpers.CalculateEquipmentChances(bot);
GearChanceHelpers.ApplyModChanceOverrides(bot);
GearChanceHelpers.ApplyEquipmentChanceOverrides(bot);
GearHelpers.ReduceAmmoWeightValues(bot);
GearHelpers.ReduceEquipmentWeightValues(bot.inventory.equipment);
GearHelpers.ReduceWeightValues(bot.appearance.voice);
GearHelpers.ReduceWeightValues(bot.appearance.feet);
GearHelpers.ReduceWeightValues(bot.appearance.body);
GearHelpers.ReduceWeightValues(bot.appearance.head);
GearHelpers.ReduceWeightValues(bot.appearance.hands);
}
stopwatch.Stop();
LoggingHelpers.LogToConsole($"{totalDupeCount.ToString()} dupes were ignored. Took {LoggingHelpers.LogTimeTaken(stopwatch.Elapsed.TotalSeconds)} seconds");
return baseBots.ToList();
}
private static void ProcessBotFileSync(
HashSet<Bot> baseBots,
string filePath,
HashSet<string> parsedBotIds,
int totalDupeCount)
{
var splitFilePath = filePath.Split("\\");
int dupeCount = 0;
List<Datum> bots = new List<Datum>();
try
{
// Parse the bots inside the json file
using (var reader = new StreamReader(filePath))
{
var deSerialisedObject = JsonSerializer.Deserialize<Root>(reader.ReadToEnd(), serialiserOptions);
foreach (var botData in deSerialisedObject.data.ToList())
{
// Bot fucks up something, never allow it in
if (botData._id == "6483938c53cc9087c70eae86")
{
Console.WriteLine("oh no");
continue;
}
var role = botData.Info.Settings.Role;
var botType = Enum.Parse<BotType>(role, true);
Bot baseBot = null;
foreach (var bot in baseBots)
{
if (bot.botType == botType)
{
baseBot = bot;
break;
}
}
if (baseBot == null)
{
Console.WriteLine($"Skipping {botData._id} due to unknown role {botData.Info.Settings.Role}");
continue;
}
// Add bot if not already added
if (!parsedBotIds.Add(botData._id))
{
dupeCount++;
}
baseBot.botCount += 1;
BaseBotGenerator.UpdateBaseDetails(baseBot, botData);
BotGearGenerator.AddGear(baseBot, botData);
BotLootGenerator.AddLoot(baseBot, botData);
BotChancesGenerator.AddChances(baseBot, botData);
}
}
}
catch (Exception)
{
Console.WriteLine($"File parse fucked up: {filePath}");
throw;
}
totalDupeCount += dupeCount;
}
public static async Task<List<Datum>> ParseAsync(string dumpPath, HashSet<string> botTypes)
{
var stopwatch = Stopwatch.StartNew();
DiskHelpers.CreateDirIfDoesntExist(dumpPath);
var botFiles = Directory.GetFiles(dumpPath, "*.json", SearchOption.TopDirectoryOnly);
LoggingHelpers.LogToConsole($"{botFiles.Length.ToString()} bot dump files found");
// key = bot type
// Store bots keyed against their ID so we never get duplicates
var parsedBotsDict = new ConcurrentDictionary<string, Datum>();
int totalDupeCount = 0;
var tasks = new List<Task>();
foreach (var filePath in botFiles)
{
tasks.Add(ProcessBotFile(botTypes, filePath, parsedBotsDict, totalDupeCount));
}
await Task.WhenAll(tasks.ToArray());
stopwatch.Stop();
LoggingHelpers.LogToConsole($"Cleaned and Parsed: {parsedBotsDict.Count.ToString()} bots. {totalDupeCount.ToString()} dupes were ignored. Took {LoggingHelpers.LogTimeTaken(stopwatch.Elapsed.TotalSeconds)} seconds");
return parsedBotsDict.Values.ToList();
}
private static async Task<int> ProcessBotFile(
HashSet<string> botTypes,
string filePath,
ConcurrentDictionary<string, Datum> parsedBotsDict,
int totalDupeCount)
{
var splitFilePath = filePath.Split("\\");
int dupeCount = 0;
List<Datum> bots = new List<Datum>();
try
{
// Parse the bots inside the json file
using var reader = new StreamReader(filePath);
var deSerialisedObject = await JsonSerializer.DeserializeAsync<Root>(reader.BaseStream, serialiserOptions);
var botTypesLower = new HashSet<string>(botTypes, StringComparer.OrdinalIgnoreCase);
var filteredBots = new List<Datum>();
foreach (var botData in deSerialisedObject.data.ToList())
{
var roleLower = botData.Info.Settings.Role.ToLower();
if (botTypesLower.Contains(roleLower))
{
filteredBots.Add(botData);
}
}
bots.AddRange(filteredBots);
}
catch (Exception)
{
Console.WriteLine($"File parse fucked up: {filePath}");
throw;
}
if (bots == null || bots.Count == 0)
{
Console.WriteLine($"Skipping file: {splitFilePath.Last()}. no bots found, ");
return totalDupeCount;
}
//Console.WriteLine($"parsing: {bots.Count} bots in file {splitFilePath.Last()}");
foreach (var bot in bots)
{
// Bot fucks up something, never allow it in
if (bot._id == "6483938c53cc9087c70eae86")
{
Console.WriteLine("oh no");
continue;
}
// null out unnecessary data to save ram
bot.Stats = null;
bot.Encyclopedia = null;
bot.Hideout = null;
bot.TaskConditionCounters = null;
bot.Bonuses = null;
bot.InsuredItems = null;
// Add bot if not already added
if (!parsedBotsDict.TryAdd(bot._id, bot))
{
dupeCount++;
}
}
totalDupeCount += dupeCount;
//Console.WriteLine($"Parsed file: {filePath}");
return totalDupeCount;
}
private static IEnumerable<Datum> ParseJson(string json)
{
var deSerialisedObject = JsonSerializer.Deserialize<Root>(json, serialiserOptions);
return deSerialisedObject.data;
}
private static string PruneMalformedBsgJson(string json, string fileName)
{
// Bsg send json where an item has a location of 1 but it should be an object with x/y/z coords
var o = JObject.Parse(json);
var jItemsToReplace = o.SelectTokens("$.data[*].Inventory.items[?(@.location == 1)].location");
//var jItemsToReplace = o.SelectTokens("$.data[*].Inventory.items[?(@.location == 1 && @.slotId == 'cartridges')].location");
if (jItemsToReplace != null && jItemsToReplace.Any())
{
LoggingHelpers.LogToConsole($"file {fileName} has {jItemsToReplace.Count().ToString()} json issues, cleaning up.", ConsoleColor.Yellow);
var jItemsToReplaceList = jItemsToReplace.ToList();
foreach (var item in jItemsToReplaceList)
{
var obj = new { x = 1, y = 0, r = 0 };
item.Replace(JToken.FromObject(obj));
}
}
var returnString = o.ToString();
o = null;
jItemsToReplace = null;
return returnString;
}
}

View File

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<ServerGarbageCollection>true</ServerGarbageCollection> <ServerGarbageCollection>true</ServerGarbageCollection>
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection> <ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>

View File

@ -7,29 +7,16 @@ namespace Generator.Helpers.Gear
{ {
public static class GearChanceHelpers public static class GearChanceHelpers
{ {
private static Dictionary<string, Dictionary<string, int>> weaponModCount = new Dictionary<string, Dictionary<string, int>>(); public static void CalculateModChances(Bot bot, List<Datum> baseBots)
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.) // TODO: Further split these counts by equipment slot? (ex. "FirstPrimaryWeapon", "Holster", etc.)
var validSlots = new List<string> { "FirstPrimaryWeapon", "SecondPrimaryWeapon", "Holster" }; var validSlots = new List<string> { "FirstPrimaryWeapon", "SecondPrimaryWeapon", "Holster" };
if (!weaponModCount.TryGetValue(baseBot.Info.Settings.Role.ToLower(), out var modCounts)) var modCounts = new Dictionary<string, int>();
{ var slotCounts = new Dictionary<string, int>();
modCounts = new Dictionary<string, int>();
weaponModCount.Add(baseBot.Info.Settings.Role.ToLower(), modCounts);
}
if (!weaponSlotCount.TryGetValue(baseBot.Info.Settings.Role.ToLower(), out var slotCounts)) foreach (var baseBot in baseBots)
{ {
slotCounts = new Dictionary<string, int>();
weaponSlotCount.Add(baseBot.Info.Settings.Role.ToLower(), slotCounts);
}
var validParents = new List<string>(); var validParents = new List<string>();
foreach (var inventoryItem in baseBot.Inventory.items) foreach (var inventoryItem in baseBot.Inventory.items)
{ {
@ -91,42 +78,21 @@ namespace Generator.Helpers.Gear
} }
} }
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( bot.chances.weaponMods = slotCounts.ToDictionary(
kvp => kvp.Key, kvp => kvp.Key,
kvp => GetPercent(kvp.Value, modCounts.GetValueOrDefault(kvp.Key))); kvp => GetPercent(kvp.Value, modCounts.GetValueOrDefault(kvp.Key)));
} }
public static void AddEquipmentModChances(Bot bot, Datum baseBot) public static void CalculateEquipmentModChances(Bot bot, List<Datum> baseBots)
{ {
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.) // TODO: Further split these counts by equipment slot? (ex. "FirstPrimaryWeapon", "Holster", etc.)
var validSlots = new List<string> { "Headwear", "ArmorVest", "TacticalVest" }; var validSlots = new List<string> { "Headwear", "ArmorVest", "TacticalVest" };
var modCounts = new Dictionary<string, int>();
var slotCounts = new Dictionary<string, int>();
foreach (var baseBot in baseBots)
{
var validParents = new List<string>(); var validParents = new List<string>();
foreach (var inventoryItem in baseBot.Inventory.items) foreach (var inventoryItem in baseBot.Inventory.items)
@ -189,20 +155,6 @@ namespace Generator.Helpers.Gear
} }
} }
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( bot.chances.equipmentMods = slotCounts.ToDictionary(
kvp => kvp.Key, kvp => kvp.Key,
kvp => GetPercent(kvp.Value, modCounts.GetValueOrDefault(kvp.Key))); kvp => GetPercent(kvp.Value, modCounts.GetValueOrDefault(kvp.Key)));
@ -293,47 +245,47 @@ namespace Generator.Helpers.Gear
weightsData["grenades"]); weightsData["grenades"]);
} }
public static void AddEquipmentChances(Bot bot, Datum baseBot) public static void CalculateEquipmentChances(Bot bot, List<Datum> baseBots)
{ {
bot.chances.equipment.Headwear += baseBot.Inventory.items.Count(x => x.slotId == "Headwear"); // TODO: Convert to dynamic?
bot.chances.equipment.Earpiece += baseBot.Inventory.items.Count(x => x.slotId == "Earpiece"); var totalBotsCount = baseBots.Count;
bot.chances.equipment.FaceCover += baseBot.Inventory.items.Count(x => x.slotId == "FaceCover"); int headwearCount = 0, earCount = 0, faceCoverCount = 0, armorVestCount = 0, eyeWearCount = 0, armBandCount = 0,
bot.chances.equipment.ArmorVest += baseBot.Inventory.items.Count(x => x.slotId == "ArmorVest"); tacticalVestCount = 0, backpackCount = 0, firstPrimaryCount = 0, secondPrimaryCount = 0, holsterCount = 0,
bot.chances.equipment.Eyewear += baseBot.Inventory.items.Count(x => x.slotId == "Eyewear"); scabbardCount = 0, pocketsCount = 0, securedContainerCount = 0;
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) foreach (var baseBot in baseBots)
{ {
if (bot.botCount == 0) headwearCount += baseBot.Inventory.items.Count(x => x.slotId == "Headwear");
{ earCount += baseBot.Inventory.items.Count(x => x.slotId == "Earpiece");
// No bots, don't do anything faceCoverCount += baseBot.Inventory.items.Count(x => x.slotId == "FaceCover");
return; armorVestCount += baseBot.Inventory.items.Count(x => x.slotId == "ArmorVest");
eyeWearCount += baseBot.Inventory.items.Count(x => x.slotId == "Eyewear");
armBandCount += baseBot.Inventory.items.Count(x => x.slotId == "ArmBand");
tacticalVestCount += baseBot.Inventory.items.Count(x => x.slotId == "TacticalVest");
backpackCount += baseBot.Inventory.items.Count(x => x.slotId == "Backpack");
firstPrimaryCount += baseBot.Inventory.items.Count(x => x.slotId == "FirstPrimaryWeapon");
secondPrimaryCount += baseBot.Inventory.items.Count(x => x.slotId == "SecondPrimaryWeapon");
holsterCount += baseBot.Inventory.items.Count(x => x.slotId == "Holster");
scabbardCount += baseBot.Inventory.items.Count(x => x.slotId == "Scabbard");
pocketsCount += baseBot.Inventory.items.Count(x => x.slotId == "Pockets");
securedContainerCount += baseBot.Inventory.items.Count(x => x.slotId == "SecuredContainer");
} }
bot.chances.equipment = new EquipmentChances( bot.chances.equipment = new EquipmentChances(
GetPercent(bot.botCount, bot.chances.equipment.Headwear), GetPercent(totalBotsCount, headwearCount),
GetPercent(bot.botCount, bot.chances.equipment.Earpiece), GetPercent(totalBotsCount, earCount),
GetPercent(bot.botCount, bot.chances.equipment.FaceCover), GetPercent(totalBotsCount, faceCoverCount),
GetPercent(bot.botCount, bot.chances.equipment.ArmorVest), GetPercent(totalBotsCount, armorVestCount),
GetPercent(bot.botCount, bot.chances.equipment.Eyewear), GetPercent(totalBotsCount, eyeWearCount),
GetPercent(bot.botCount, bot.chances.equipment.ArmBand), GetPercent(totalBotsCount, armBandCount),
GetPercent(bot.botCount, bot.chances.equipment.TacticalVest), GetPercent(totalBotsCount, tacticalVestCount),
GetPercent(bot.botCount, bot.chances.equipment.Backpack), GetPercent(totalBotsCount, backpackCount),
GetPercent(bot.botCount, bot.chances.equipment.FirstPrimaryWeapon), GetPercent(totalBotsCount, firstPrimaryCount),
GetPercent(bot.botCount, bot.chances.equipment.SecondPrimaryWeapon), GetPercent(totalBotsCount, secondPrimaryCount),
GetPercent(bot.botCount, bot.chances.equipment.Holster), GetPercent(totalBotsCount, holsterCount),
GetPercent(bot.botCount, bot.chances.equipment.Scabbard), GetPercent(totalBotsCount, scabbardCount),
GetPercent(bot.botCount, bot.chances.equipment.Pockets), GetPercent(totalBotsCount, pocketsCount),
GetPercent(bot.botCount, bot.chances.equipment.SecuredContainer)); GetPercent(totalBotsCount, securedContainerCount));
} }
private static int GetPercent(int total, int count) private static int GetPercent(int total, int count)

View File

@ -65,6 +65,7 @@ namespace Generator.Helpers.Gear
internal static void AddAmmo(Bot botToUpdate, Datum bot) internal static void AddAmmo(Bot botToUpdate, Datum bot)
{ {
//var weightService = new WeightingService();
foreach (var ammo in bot.Inventory.items.Where( foreach (var ammo in bot.Inventory.items.Where(
x => x.slotId != null x => x.slotId != null
&& (x.slotId == "patron_in_weapon" && (x.slotId == "patron_in_weapon"
@ -116,6 +117,7 @@ namespace Generator.Helpers.Gear
public static void AddEquippedGear(Bot botToUpdate, Datum bot) public static void AddEquippedGear(Bot botToUpdate, Datum bot)
{ {
// add equipped gear // add equipped gear
var weightService = new WeightingService();
foreach (var inventoryItem in bot.Inventory.items.Where(x=>x.slotId != null)) foreach (var inventoryItem in bot.Inventory.items.Where(x=>x.slotId != null))
{ {
switch (inventoryItem.slotId?.ToLower()) switch (inventoryItem.slotId?.ToLower())
@ -296,13 +298,7 @@ namespace Generator.Helpers.Gear
foreach (var cartridge in botToUpdate.inventory.Ammo.Keys) foreach (var cartridge in botToUpdate.inventory.Ammo.Keys)
{ {
var cartridgeWithWeights = botToUpdate.inventory.Ammo[cartridge]; var cartridgeWithWeights = botToUpdate.inventory.Ammo[cartridge];
foreach (var cartridgeKvP in cartridgeWithWeights)
{
cartridgeWithWeights[cartridgeKvP.Key] = ReduceValueAccuracy(cartridgeKvP.Value);
}
var weights = cartridgeWithWeights.Values.Select(x => x).ToList(); var weights = cartridgeWithWeights.Values.Select(x => x).ToList();
var commonAmmoDivisor = CommonDivisor(weights); var commonAmmoDivisor = CommonDivisor(weights);
foreach (var cartridgeWeightKvP in cartridgeWithWeights) foreach (var cartridgeWeightKvP in cartridgeWithWeights)
@ -347,11 +343,6 @@ namespace Generator.Helpers.Gear
return; return;
} }
foreach (var itemWeightKvp in equipmentDict)
{
equipmentDict[itemWeightKvp.Key] = ReduceValueAccuracy(itemWeightKvp.Value, 4);
}
var weights = equipmentDict.Values.Select(x => x).ToList(); var weights = equipmentDict.Values.Select(x => x).ToList();
var commonAmmoDivisor = CommonDivisor(weights); var commonAmmoDivisor = CommonDivisor(weights);
@ -366,13 +357,5 @@ namespace Generator.Helpers.Gear
equipmentDict[itemTplWithWeight.Key] /= commonAmmoDivisor; equipmentDict[itemTplWithWeight.Key] /= commonAmmoDivisor;
} }
} }
private static int ReduceValueAccuracy(long x, int digits = 3)
{
int i = (int)Math.Log10(x);
i = Math.Max(0, i - (digits - 1));
i = (int)Math.Pow(10, i);
return (int)(x / i * i);
}
} }
} }

View File

@ -1,7 +1,5 @@
using Common.Models.Input; using Common.Models.Input;
using Common.Models.Output;
using Generator.Helpers; using Generator.Helpers;
using System.Diagnostics;
namespace Generator; namespace Generator;
@ -9,9 +7,6 @@ internal static class Program
{ {
internal static async Task Main(string[] args) internal static async Task Main(string[] args)
{ {
var stopwatch = Stopwatch.StartNew();
LoggingHelpers.LogToConsole("Started processing bots");
// Create list of bots we want to process // Create list of bots we want to process
string[] botTypes = { string[] botTypes = {
"assault", "assault",
@ -65,31 +60,44 @@ internal static class Program
"spiritwinter", "spiritwinter",
"skier", "skier",
"peacemaker", "peacemaker"
"infectedassault",
"infectedpmc",
"infectedcivil",
"infectedlaborant",
"infectedtagilla",
}; };
// Read raw bot dumps and turn into c# objects // Read raw bot dumps and turn into c# objects
var workingPath = Directory.GetCurrentDirectory(); var workingPath = Directory.GetCurrentDirectory();
var dumpPath = $"{workingPath}//dumps"; var dumpPath = $"{workingPath}//dumps";
List<Bot> bots = BotParser.Parse(dumpPath, botTypes.ToHashSet()); var parsedBots = await BotParser.ParseAsync(dumpPath, botTypes.ToHashSet());
if (bots.Count == 0) // Put in dictionary for better use later on
var rawBotsCache = new Dictionary<string, List<Datum>>(45);
foreach (var rawBot in parsedBots)
{
if (rawBotsCache.TryGetValue(rawBot.Info.Settings.Role.ToLower(), out var botList))
{
botList.Add(rawBot);
continue;
}
// Doesnt exist, add key and bot
rawBotsCache.Add(rawBot.Info.Settings.Role.ToLower(), new List<Datum> { rawBot });
}
if (parsedBots.Count == 0)
{ {
LoggingHelpers.LogToConsole("No bots found, unable to continue"); LoggingHelpers.LogToConsole("No bots found, unable to continue");
LoggingHelpers.LogToConsole("Check your dumps are in 'Generator\\bin\\Debug\\net6.0\\dumps' and start with 'resp.' NOT 'req.'"); LoggingHelpers.LogToConsole("Check your dumps are in 'Generator\\bin\\Debug\\net6.0\\dumps' and start with 'resp.' NOT 'req.'");
return; return;
} }
var jsonWriter = new JsonWriter(workingPath, "output"); // Generate the base bot class with basic details (health/body part hp etc) and then append everything else
jsonWriter.WriteJson(bots); var bots = BaseBotGenerator.GenerateBaseDetails(parsedBots, workingPath, botTypes)
.AddGear(rawBotsCache) // Add weapons/armor
.AddLoot(rawBotsCache)
.AddChances(rawBotsCache); // Add mod/equipment chances
stopwatch.Stop(); // Output bot to json file
LoggingHelpers.LogToConsole($"Finished processing bots. Took {LoggingHelpers.LogTimeTaken(stopwatch.Elapsed.TotalSeconds)} seconds"); var jsonWriter = new JsonWriter(workingPath, "output");
jsonWriter.WriteJson(bots.ToList());
} }
} }

View File

@ -14,14 +14,11 @@ namespace Generator.Weighting
public class WeightingService public class WeightingService
{ {
private static Dictionary<BotType, Weightings> _weights = null; private readonly Dictionary<BotType, Weightings> _weights;
private static Dictionary<string, Dictionary<string, GenerationWeightData>> _generationWeights = null; private readonly Dictionary<string, Dictionary<string, GenerationWeightData>> _generationWeights;
public WeightingService() public WeightingService()
{ {
// Cache the loaded data
if (_weights != null && _generationWeights != null) return;
var assetsPath = $"{Directory.GetCurrentDirectory()}\\Assets"; var assetsPath = $"{Directory.GetCurrentDirectory()}\\Assets";
var weightsFilePath = $"{assetsPath}\\weights.json"; var weightsFilePath = $"{assetsPath}\\weights.json";
if (!File.Exists(weightsFilePath)) if (!File.Exists(weightsFilePath))

View File

@ -2,11 +2,11 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>