From a21ac7a4cdf4c4087010c57469eb183fd0cc7f4f Mon Sep 17 00:00:00 2001
From: Chomp <chomp@noreply.dev.sp-tarkov.com>
Date: Fri, 4 Feb 2022 12:08:49 +0000
Subject: [PATCH] Weighting system changes

---
 Common.Models/Output/Equipment.cs          |  56 +++++-----
 Common/Extensions/StringToolsExtensions.cs |   9 ++
 Generator/BaseBotGenerator.cs              |  29 -----
 Generator/Helpers/Gear/GearHelpers.cs      |  66 +++++++++---
 PMCGenerator/Program.cs                    | 117 +++++++++++++--------
 5 files changed, 164 insertions(+), 113 deletions(-)

diff --git a/Common.Models/Output/Equipment.cs b/Common.Models/Output/Equipment.cs
index d91a265..c96e9df 100644
--- a/Common.Models/Output/Equipment.cs
+++ b/Common.Models/Output/Equipment.cs
@@ -6,36 +6,36 @@ namespace Common.Models.Output
     {
         public Equipment()
         {
-            Headwear = new List<string>();
-            Earpiece = new List<string>();
-            FaceCover = new List<string>();
-            ArmorVest = new List<string>();
-            Eyewear = new List<string>();
-            ArmBand = new List<string>();
-            TacticalVest = new List<string>();
-            Backpack = new List<string>();
-            FirstPrimaryWeapon = new List<string>();
-            SecondPrimaryWeapon = new List<string>();
-            Holster = new List<string>();
-            Scabbard = new List<string>();
-            Pockets = new List<string>();
-            SecuredContainer = new List<string>();
+            Headwear = new Dictionary<string, int>();
+            Earpiece = new Dictionary<string, int>();
+            FaceCover = new Dictionary<string, int>();
+            ArmorVest = new Dictionary<string, int>();
+            Eyewear = new Dictionary<string, int>();
+            ArmBand = new Dictionary<string, int>();
+            TacticalVest = new Dictionary<string, int>();
+            Backpack = new Dictionary<string, int>();
+            FirstPrimaryWeapon = new Dictionary<string, int>();
+            SecondPrimaryWeapon = new Dictionary<string, int>();
+            Holster = new Dictionary<string, int>();
+            Scabbard = new Dictionary<string, int>();
+            Pockets = new Dictionary<string, int>();
+            SecuredContainer = new Dictionary<string, int>();
         }
 
-        public List<string> Headwear { get; set; }
-        public List<string> Earpiece { get; set; }
-        public List<string> FaceCover { get; set; }
-        public List<string> ArmorVest { get; set; }
-        public List<string> Eyewear { get; set; }
-        public List<string> ArmBand { get; set; }
-        public List<string> TacticalVest { get; set; }
-        public List<string> Backpack { get; set; }
-        public List<string> FirstPrimaryWeapon { get; set; }
-        public List<string> SecondPrimaryWeapon { get; set; }
-        public List<string> Holster { get; set; }
-        public List<string> Scabbard { get; set; }
-        public List<string> Pockets { get; set; }
-        public List<string> SecuredContainer { get; set; }
+        public Dictionary<string, int> Headwear { get; set; }
+        public Dictionary<string, int> Earpiece { get; set; }
+        public Dictionary<string, int> FaceCover { get; set; }
+        public Dictionary<string, int> ArmorVest { get; set; }
+        public Dictionary<string, int> Eyewear { get; set; }
+        public Dictionary<string, int> ArmBand { get; set; }
+        public Dictionary<string, int> TacticalVest { get; set; }
+        public Dictionary<string, int> Backpack { get; set; }
+        public Dictionary<string, int> FirstPrimaryWeapon { get; set; }
+        public Dictionary<string, int> SecondPrimaryWeapon { get; set; }
+        public Dictionary<string, int> Holster { get; set; }
+        public Dictionary<string, int> Scabbard { get; set; }
+        public Dictionary<string, int> Pockets { get; set; }
+        public Dictionary<string, int> SecuredContainer { get; set; }
     }
 
     public class Inventory
diff --git a/Common/Extensions/StringToolsExtensions.cs b/Common/Extensions/StringToolsExtensions.cs
index feefc34..4b6becc 100644
--- a/Common/Extensions/StringToolsExtensions.cs
+++ b/Common/Extensions/StringToolsExtensions.cs
@@ -13,6 +13,15 @@ namespace Common.Extensions
                 self.Add(item);
         }
 
+        /// <summary>
+        /// Add a string to a list only if it doesnt already exist
+        /// </summary>
+        public static void AddUnique(this IDictionary<string, int> self, string itemkey, int weight)
+        {
+            if (!self.ContainsKey(itemkey))
+                self.Add(itemkey, weight);
+        }
+
         public static void AddUniqueRange(this IList<string> self, IList<string> itemsToAdd)
         {
             foreach (var item in itemsToAdd)
diff --git a/Generator/BaseBotGenerator.cs b/Generator/BaseBotGenerator.cs
index cabf7a4..eb16d37 100644
--- a/Generator/BaseBotGenerator.cs
+++ b/Generator/BaseBotGenerator.cs
@@ -140,35 +140,6 @@ namespace Generator
             }
 
             botToUpdate.health.BodyParts = uniqueHealthSetups.Values.ToList();
-
-            //var firstBotOfDesiredType = rawBots.FirstOrDefault();
-            //if (firstBotOfDesiredType == null)
-            //{
-            //    string botType = botToUpdate.botType.ToString();
-            //    LoggingHelpers.LogToConsole($"bot type of: {botType} not found, unable to update body part health.");
-            //    return;
-            //}
-
-            //botToUpdate.health.BodyParts.Head.min = firstBotOfDesiredType.Health.BodyParts.Head.Health.Current;
-            //botToUpdate.health.BodyParts.Head.max = firstBotOfDesiredType.Health.BodyParts.Head.Health.Maximum;
-
-            //botToUpdate.health.BodyParts.Chest.min = firstBotOfDesiredType.Health.BodyParts.Chest.Health.Current;
-            //botToUpdate.health.BodyParts.Chest.max = firstBotOfDesiredType.Health.BodyParts.Chest.Health.Maximum;
-
-            //botToUpdate.health.BodyParts.Stomach.min = firstBotOfDesiredType.Health.BodyParts.Stomach.Health.Current;
-            //botToUpdate.health.BodyParts.Stomach.max = firstBotOfDesiredType.Health.BodyParts.Stomach.Health.Maximum;
-
-            //botToUpdate.health.BodyParts.LeftArm.min = firstBotOfDesiredType.Health.BodyParts.LeftArm.Health.Current;
-            //botToUpdate.health.BodyParts.LeftArm.max = firstBotOfDesiredType.Health.BodyParts.LeftArm.Health.Maximum;
-
-            //botToUpdate.health.BodyParts.RightArm.min = firstBotOfDesiredType.Health.BodyParts.RightArm.Health.Current;
-            //botToUpdate.health.BodyParts.RightArm.max = firstBotOfDesiredType.Health.BodyParts.RightArm.Health.Maximum;
-
-            //botToUpdate.health.BodyParts.LeftLeg.min = firstBotOfDesiredType.Health.BodyParts.LeftLeg.Health.Current;
-            //botToUpdate.health.BodyParts.LeftLeg.max = firstBotOfDesiredType.Health.BodyParts.LeftLeg.Health.Maximum;
-
-            //botToUpdate.health.BodyParts.RightLeg.min = firstBotOfDesiredType.Health.BodyParts.RightLeg.Health.Current;
-            //botToUpdate.health.BodyParts.RightLeg.max = firstBotOfDesiredType.Health.BodyParts.RightLeg.Health.Maximum;
         }
 
         private static void AddVisualAppearanceItems(Bot botToUpdate, Datum rawBot)
diff --git a/Generator/Helpers/Gear/GearHelpers.cs b/Generator/Helpers/Gear/GearHelpers.cs
index f76cc6f..b70f906 100644
--- a/Generator/Helpers/Gear/GearHelpers.cs
+++ b/Generator/Helpers/Gear/GearHelpers.cs
@@ -13,9 +13,29 @@ namespace Generator.Helpers.Gear
             var modItemsInRawBot = new List<Item>();
             var itemsWithModsInRawBot = new List<Item>();
 
+
+                foreach (var inv in rawParsedBot.Inventory.items.Where(x => x.slotId == "mod_magazine"))
+                {
+                var count = rawParsedBot.Inventory.items.Where(x => x.slotId == "mod_magazine").Count();
+                    if (inv._tpl == "60dc519adf4c47305f6d410d")
+                    {
+                        var y = 1;
+                    }
+                }
+
             modItemsInRawBot = rawParsedBot.Inventory.items
                 .Where(x => x.slotId != null && (x.slotId.StartsWith("mod_") || x.slotId.StartsWith("patron_in_weapon"))).ToList();
 
+            var x = new List<Item>();
+            foreach (var item in rawParsedBot.Inventory.items.Where(x=>x.slotId == "mod_magazine"))
+            {
+                if (item._tpl == "60dc519adf4c47305f6d410d")
+                { 
+                    var wow = 1; 
+                }
+                x.Add(item);
+            }
+
             // get items with Mods by iterating over mod items and getting the parent item
             itemsWithModsInRawBot.AddRange(modItemsInRawBot
                 .Select(modItem => rawParsedBot.Inventory.items
@@ -27,6 +47,17 @@ namespace Generator.Helpers.Gear
                 var modsToAdd = modItemsInRawBot.Where(x => x.parentId == itemToAdd._id).ToList();
 
                 AddItemToDictionary(itemToAdd, modsToAdd, itemsWithModsDictionary);
+
+                // check if these mods have sub-mods and add those
+                foreach (var modAdded in modsToAdd.Where(x=>x.slotId == "mod_magazine"))
+                {
+                    // look for items where parentId is this mods id
+                    var subItems = rawParsedBot.Inventory.items.Where(x => x.parentId == modAdded._id && x.slotId != "cartridges").ToList();
+                    if (subItems.Count > 0)
+                    {
+                        AddItemToDictionary(itemToAdd, subItems, itemsWithModsDictionary);
+                    }
+                }
             }
 
             botToUpdate.inventory.mods = itemsWithModsDictionary;
@@ -35,51 +66,51 @@ namespace Generator.Helpers.Gear
         public static void AddEquippedGear(Bot botToUpdate, Datum bot)
         {
             // add equipped gear
-            foreach (var inventoryItem in bot.Inventory.items)
+            foreach (var inventoryItem in bot.Inventory.items.Where(x=>x.slotId != null))
             {
                 switch (inventoryItem.slotId?.ToLower())
                 {
                     case "headwear":
-                        botToUpdate.inventory.equipment.Headwear.AddUnique(inventoryItem._tpl);
+                        botToUpdate.inventory.equipment.Headwear.AddUnique(inventoryItem._tpl, GetItemWeight(inventoryItem._tpl));
                         break;
                     case "earpiece":
-                        botToUpdate.inventory.equipment.Earpiece.AddUnique(inventoryItem._tpl);
+                        botToUpdate.inventory.equipment.Earpiece.AddUnique(inventoryItem._tpl, GetItemWeight(inventoryItem._tpl));
                         break;
                     case "facecover":
-                        botToUpdate.inventory.equipment.FaceCover.AddUnique(inventoryItem._tpl);
+                        botToUpdate.inventory.equipment.FaceCover.AddUnique(inventoryItem._tpl, GetItemWeight(inventoryItem._tpl));
                         break;
                     case "armorvest":
-                        botToUpdate.inventory.equipment.ArmorVest.AddUnique(inventoryItem._tpl);
+                        botToUpdate.inventory.equipment.ArmorVest.AddUnique(inventoryItem._tpl, GetItemWeight(inventoryItem._tpl));
                         break;
                     case "eyewear":
-                        botToUpdate.inventory.equipment.Eyewear.AddUnique(inventoryItem._tpl);
+                        botToUpdate.inventory.equipment.Eyewear.AddUnique(inventoryItem._tpl, GetItemWeight(inventoryItem._tpl));
                         break;
                     case "armband":
-                        botToUpdate.inventory.equipment.ArmBand.AddUnique(inventoryItem._tpl);
+                        botToUpdate.inventory.equipment.ArmBand.AddUnique(inventoryItem._tpl, GetItemWeight(inventoryItem._tpl));
                         break;
                     case "tacticalvest":
-                        botToUpdate.inventory.equipment.TacticalVest.AddUnique(inventoryItem._tpl);
+                        botToUpdate.inventory.equipment.TacticalVest.AddUnique(inventoryItem._tpl, GetItemWeight(inventoryItem._tpl));
                         break;
                     case "backpack":
-                        botToUpdate.inventory.equipment.Backpack.AddUnique(inventoryItem._tpl);
+                        botToUpdate.inventory.equipment.Backpack.AddUnique(inventoryItem._tpl, GetItemWeight(inventoryItem._tpl));
                         break;
                     case "firstprimaryweapon":
-                        botToUpdate.inventory.equipment.FirstPrimaryWeapon.AddUnique(inventoryItem._tpl);
+                        botToUpdate.inventory.equipment.FirstPrimaryWeapon.AddUnique(inventoryItem._tpl, GetItemWeight(inventoryItem._tpl));
                         break;
                     case "secondprimaryweapon":
-                        botToUpdate.inventory.equipment.SecondPrimaryWeapon.AddUnique(inventoryItem._tpl);
+                        botToUpdate.inventory.equipment.SecondPrimaryWeapon.AddUnique(inventoryItem._tpl, GetItemWeight(inventoryItem._tpl));
                         break;
                     case "holster":
-                        botToUpdate.inventory.equipment.Holster.AddUnique(inventoryItem._tpl);
+                        botToUpdate.inventory.equipment.Holster.AddUnique(inventoryItem._tpl, GetItemWeight(inventoryItem._tpl));
                         break;
                     case "scabbard":
-                        botToUpdate.inventory.equipment.Scabbard.AddUnique(inventoryItem._tpl);
+                        botToUpdate.inventory.equipment.Scabbard.AddUnique(inventoryItem._tpl, GetItemWeight(inventoryItem._tpl));
                         break;
                     case "pockets":
-                        botToUpdate.inventory.equipment.Pockets.AddUnique(inventoryItem._tpl);
+                        botToUpdate.inventory.equipment.Pockets.AddUnique(inventoryItem._tpl, GetItemWeight(inventoryItem._tpl));
                         break;
                     case "securedcontainer":
-                        botToUpdate.inventory.equipment.SecuredContainer.AddUnique(inventoryItem._tpl);
+                        botToUpdate.inventory.equipment.SecuredContainer.AddUnique(inventoryItem._tpl, GetItemWeight(inventoryItem._tpl));
                         break;
                     default:
                         break;
@@ -87,6 +118,11 @@ namespace Generator.Helpers.Gear
             }
         }
 
+        private static int GetItemWeight(string tpl)
+        {
+            // TODO: implement
+            return 1;
+        }
 
         public static void AddCartridges(Bot botToUpdate, Datum rawParsedBot)
         {
diff --git a/PMCGenerator/Program.cs b/PMCGenerator/Program.cs
index 48646d5..b4e2a49 100644
--- a/PMCGenerator/Program.cs
+++ b/PMCGenerator/Program.cs
@@ -10,54 +10,95 @@ using Generator.Helpers;
 
 namespace PMCGenerator
 {
-    
+
     class Program
     {
         static void Main(string[] args)
         {
-            var itemLibrary = GetItemLibrary();
-
-            var parsedPresets = GetPresets();
+            List<Presets> parsedPresets = GetPresets();
 
             // Create flat lists of weapons + list of mods
             var flatPrimaryWeaponsList = GetWeaponsFromRawFile(parsedPresets);
             var flatSecondaryWeaponsList = GetSecondaryWeaponsFromRawFile(parsedPresets);
-
             var flatAllWeaponsList = CombinePrimaryAndSecondaryWeapons(flatPrimaryWeaponsList, flatSecondaryWeaponsList);
 
+            var output = new
+            {
+                FirstPrimaryWeapon = AddWeaponsToOutput(flatPrimaryWeaponsList),
+                Holster = AddWeaponsToOutput(flatSecondaryWeaponsList),
+                mods = AddModsToOutput(flatAllWeaponsList, parsedPresets, flatPrimaryWeaponsList)
+            };
+
+            // Create output dir
+            var outputPath = CreateOutputFolder();
+
+            // Turn into json
+            var outputJson = JsonConvert.SerializeObject(output, Formatting.Indented);
+
+            CreateJsonFile(outputPath, outputJson);
+        }
+
+        private static Dictionary<string, int> AddWeaponsToOutput(List<WeaponDetails> flatPrimaryWeaponsList)
+        {
+            var results = new Dictionary<string, int>();
+
+            var distinctPrimaryWeaponIds = flatPrimaryWeaponsList.Select(x => x.TemplateId).Distinct();
+            foreach (var primaryWeapon in distinctPrimaryWeaponIds.Select(id => new KeyValuePair<string, int>(id, GetWeaponWeighting(id))))
+            {
+                results.Add(primaryWeapon.Key, primaryWeapon.Value);
+            }
+
+            return results;
+        }
+
+        private static int GetWeaponWeighting(string id)
+        {
+            // TODO get weighting data from styrr
+            return 1;
+        }
+
+        private static Dictionary<string, Dictionary<string, List<string>>> AddModsToOutput(
+    List<WeaponDetails> flatAllWeaponsList,
+    List<Presets> parsedPresets,
+    List<WeaponDetails> flatPrimaryWeaponsList)
+        {
+            var result = new Dictionary<string, Dictionary<string, List<string>>>();
+            var itemLibrary = GetItemLibrary();
             var flatModList = GetModsFromRawFile(parsedPresets);
 
-            // Add weapon mods to output
-            var output = new {
-                FirstPrimaryWeapon = new List<string>(),
-                Holster = new List<string>(),
-                mods = new Dictionary<string, Dictionary<string, List<string>>>() };
-
-            output.FirstPrimaryWeapon.AddRange(flatPrimaryWeaponsList.Select(x => x.TemplateId).Distinct());
-            output.Holster.AddRange(flatSecondaryWeaponsList.Select(x => x.TemplateId).Distinct());
-
-            // Loop over each gun
+            // Time to generate mods for weapons
             foreach (var weapon in flatAllWeaponsList)
             {
-                // add weapon if its not already here
-                if (!output.mods.ContainsKey(weapon.TemplateId))
+                // add weapon id if its not already here
+                if (!result.ContainsKey(weapon.TemplateId))
                 {
                     // Add weapon to dictionary
-                    output.mods.Add(weapon.TemplateId, new Dictionary<string, List<string>>());
+                    result.Add(weapon.TemplateId, new Dictionary<string, List<string>>());
                 }
 
-                // Get top level mods types for this gun
+                // Get top level mod types for this gun
                 var uniqueModSlots = flatModList.Where(x => x.ParentId == weapon.Id).Select(x => x.SlotId).Distinct().ToList();
 
                 var chamberedBulletModItemName = "patron_in_weapon";
-                if (weapon.TemplateId != "60db29ce99594040e04c4a27") // shotgun revolver
+                if (weapon.TemplateId != "60db29ce99594040e04c4a27" && weapon.TemplateId != "5580223e4bdc2d1c128b457f") // not shotgun revolver or double barrel
                 {
                     uniqueModSlots.AddUnique(chamberedBulletModItemName);
                 }
 
+                if (weapon.TemplateId == "60db29ce99594040e04c4a27") // shotgun revolver
+                {
+                    // live file has: mod_barrel, mod_stock, mod_handguard, mod_magazine
+                }
+
+                if (weapon.TemplateId == "5580223e4bdc2d1c128b457f") // double barrel
+                {
+                    uniqueModSlots.AddUnique("patron_in_weapon_000");
+                    uniqueModSlots.AddUnique("patron_in_weapon_001");
+                }
+
                 foreach (var modSlotId in uniqueModSlots)
                 {
-                    Dictionary<string, List<string>> weaponModsToModify = output.mods[weapon.TemplateId];
+                    Dictionary<string, List<string>> weaponModsToModify = result[weapon.TemplateId];
 
                     if (!weaponModsToModify.ContainsKey(modSlotId))
                     {
@@ -67,7 +108,7 @@ namespace PMCGenerator
 
                 // Add compatible bullets to weapons gun chamber
                 var compatibleBullets = GetCompatibileBullets(itemLibrary, weapon);
-                var modItemToAddBulletsTo = output.mods[weapon.TemplateId].FirstOrDefault(x => x.Key == chamberedBulletModItemName);
+                var modItemToAddBulletsTo = result[weapon.TemplateId].FirstOrDefault(x => x.Key == chamberedBulletModItemName);
                 if (modItemToAddBulletsTo.Key != null) // some guns dont have a mod you add bullets to (e.g. revolvers)
                 {
                     modItemToAddBulletsTo.Value.AddUniqueRange(compatibleBullets);
@@ -75,7 +116,7 @@ namespace PMCGenerator
 
                 // Add compatabible mods to weapon
                 var modsForWeapon = flatModList.Where(x => x.ParentId == weapon.Id).ToList();
-                Dictionary<string, List<string>> weaponMods = output.mods[weapon.TemplateId];
+                Dictionary<string, List<string>> weaponMods = result[weapon.TemplateId];
                 foreach (var mod in modsForWeapon)
                 {
                     weaponMods[mod.SlotId].AddUnique(mod.TemplateId);
@@ -83,29 +124,29 @@ namespace PMCGenerator
                     if (mod.SlotId == "mod_magazine")
                     {
                         // add special mod item for magazine that gives info on what cartridges can be used
-                        AddCartridgeItemToModListWithCompatibileCartridges(output.mods, compatibleBullets, mod);
+                        AddCartridgeItemToModListWithCompatibileCartridges(result, compatibleBullets, mod);
                     }
                 }
             }
 
             // Get mods where parent is not weapon and add to output
-            foreach (var mod in flatModList.Where(x => x.ParentId != null 
+            foreach (var mod in flatModList.Where(x => x.ParentId != null
                         && !flatPrimaryWeaponsList.Any(y => y.Id == x.ParentId)).ToList())
             {
                 // No parent tempalte id found, create and add mods details
-                if (!output.mods.ContainsKey(mod.ParentTemplateId))
+                if (!result.ContainsKey(mod.ParentTemplateId))
                 {
-                    var templateIdsList = new List<string>{mod.TemplateId};
-                    var subtype = new Dictionary<string, List<string>>{{ mod.SlotId, templateIdsList } };
-                    output.mods.Add(mod.ParentTemplateId, subtype);
+                    var templateIdsList = new List<string> { mod.TemplateId };
+                    var subtype = new Dictionary<string, List<string>> { { mod.SlotId, templateIdsList } };
+                    result.Add(mod.ParentTemplateId, subtype);
                 }
 
                 //Add subtype to item
-                var subtypeToAddTo = output.mods[mod.ParentTemplateId];
+                var subtypeToAddTo = result[mod.ParentTemplateId];
                 // No subtype, add it
                 if (!subtypeToAddTo.ContainsKey(mod.SlotId))
                 {
-                    var valueToAdd = new List<string>(){ mod.TemplateId };
+                    var valueToAdd = new List<string>() { mod.TemplateId };
                     subtypeToAddTo.Add(mod.SlotId, valueToAdd);
                 }
 
@@ -113,13 +154,7 @@ namespace PMCGenerator
                 subtypeToAddTo[mod.SlotId].AddUnique(mod.TemplateId);
             }
 
-            // Create output dir
-            var outputPath = CreateOutputFolder();
-
-            // Turn into json
-            var outputJson = JsonConvert.SerializeObject(output, Formatting.Indented);
-
-            CreateJsonFile(outputPath, outputJson);
+            return result;
         }
 
         private static List<WeaponDetails> CombinePrimaryAndSecondaryWeapons(List<WeaponDetails> flatPrimaryWeaponsList, List<WeaponDetails> flatSecondaryWeaponsList)
@@ -227,7 +262,7 @@ namespace PMCGenerator
 
         private static List<ModDetails> GetModsFromRawFile(List<Presets> parsedPresets)
         {
-            List <ModDetails> result = new List<ModDetails>();
+            List<ModDetails> result = new List<ModDetails>();
             foreach (var file in parsedPresets)
             {
                 foreach (var item in file.weaponbuilds)
@@ -251,7 +286,7 @@ namespace PMCGenerator
                     }
                 }
             }
-            
+
 
             return result;
         }
@@ -291,7 +326,7 @@ namespace PMCGenerator
                     result.Add(new WeaponDetails(item.Key, weapon.items[0]._id, weapon.items[0]._tpl));
                 }
             }
-            
+
             return result;
         }