Example #1
0
        // Evaluates whether a given mech needs any structure repaired
        public static bool CheckStructureDamage(MechDef mech)
        {
            // Default to not requesting any structure repair
            bool mechNeedsRepair = false;

            // Using the repair priority for loop here as it is faster and simpler than foreach'ing over ChassisLocations and filtering out ones that don't have armor
            for (int index = 0; index < Globals.repairPriorities.Count; index++)
            {
                // Set current ChassisLocation
                ChassisLocations thisLoc = Globals.repairPriorities.ElementAt(index).Value;
                // Get current mech location loadout from the looped chassis definitions
                LocationLoadoutDef thisLocLoadout = mech.GetLocationLoadoutDef(thisLoc);
                // Friendly name for this location
                string thisLocName = thisLoc.ToString();

                // Work out difference of armor lost for each location - default to 0
                int   structureDifference = 0;
                float currentStructure    = thisLocLoadout.CurrentInternalStructure;
                float definedStructure    = mech.GetChassisLocationDef(thisLoc).InternalStructure;
                structureDifference = (int)Mathf.Abs(currentStructure - definedStructure);

                // If any difference betwen the location's current and assigned armor is detected, flag this mech for armor repair
                if (structureDifference > 0)
                {
                    Logger.LogDebug(mech.Name + " requires structure repair based on damage to: " + thisLocName);
                    mechNeedsRepair = true;
                    break; // Stop evaluating other locations once a repair requirement is determined
                }
            }

            return(mechNeedsRepair);
        }
Example #2
0
        private HardpointDataDef._WeaponHardpointData GetWeaponData(ChassisLocations location)
        {
            var locationString = location.ToString().ToLower();
            var weaponsData    = chassisDef.HardpointDataDef.HardpointData.FirstOrDefault(x => x.location == locationString);

            return(weaponsData);
        }
Example #3
0
        private static void Postfix(ref SimGameState __instance, ref string mechSimGameUID, ref ChassisLocations location, ref int structureCount, ref WorkOrderEntry_RepairMechStructure __result)
        {
            try
            {
                float mechTonnageModifier = 1f;
                // Original method code, this is still needed to work out zero structure modifiers
                string id   = string.Format("MechLab - RepairMech - {0}", __instance.GenerateSimGameUID());
                bool   flag = false;
                float  num  = 1f;
                float  num2 = 1f;

                foreach (MechDef mechDef in __instance.ActiveMechs.Values)
                {
                    if (mechDef.GUID == mechSimGameUID)
                    {
                        // If ScaleStructureCostByTonnage is enabled, make the mech tonnage work as a percentage tech cost reduction (95 tons = 0.95 or "95%" of the cost, 50 tons = 0.05 or "50%" of the cost etc)
                        if (ArmorRepair.ModSettings.ScaleStructureCostByTonnage)
                        {
                            mechTonnageModifier = mechDef.Chassis.Tonnage * 0.01f;
                        }

                        if (mechDef.GetChassisLocationDef(location).InternalStructure == (float)structureCount)
                        {
                            flag = true;
                        }

                        break;
                    }
                }
                if (flag)
                {
                    num  = __instance.Constants.MechLab.ZeroStructureCBillModifier;
                    num2 = __instance.Constants.MechLab.ZeroStructureTechPointModifier;
                }

                int techCost  = Mathf.CeilToInt((__instance.Constants.MechLab.StructureRepairTechPoints * (float)structureCount * num2) * mechTonnageModifier);
                int cbillCost = Mathf.CeilToInt((float)((__instance.Constants.MechLab.StructureRepairCost * structureCount) * num) * mechTonnageModifier);

                Logger.LogDebug("Structure WO Subentry Costing:");
                Logger.LogDebug("***************************************");
                Logger.LogDebug("location: " + location.ToString());
                Logger.LogDebug("structureCount: " + structureCount);
                Logger.LogDebug("mechTonnageModifier: " + mechTonnageModifier);
                Logger.LogDebug("techCost: " + techCost);
                Logger.LogDebug("cBill cost: " + cbillCost);
                Logger.LogDebug("***************************************");

                __result = new WorkOrderEntry_RepairMechStructure(id, string.Format("Repair 'Mech - {0}", location.ToString()), mechSimGameUID, techCost, location, structureCount, cbillCost, string.Empty);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
            }
        }
Example #4
0
        public override string ToString()
        {
            var sb = new StringBuilder("Allowed Locations for " + Tag);

            sb.Append("\n- Default: " + Default.ToString());
            if (UnitTypes != null && UnitTypes.Length > 0)
            {
                foreach (var unitType in UnitTypes)
                {
                    sb.Append("\n- " + unitType.UnitType + ": " + unitType.Location.ToString());
                }
            }
            return(sb.ToString());
        }
Example #5
0
        // Evaluates whether a given mech needs any armor repaired
        public static bool CheckArmorDamage(MechDef mech)
        {
            // Default to not requesting any armor repair
            bool mechNeedsArmor = false;

            // Using the repair priority for loop here as it is faster and simpler than foreach'ing over ChassisLocations and filtering out ones that don't have armor
            for (int index = 0; index < Globals.repairPriorities.Count; index++)
            {
                // Set current ChassisLocation
                ChassisLocations thisLoc = Globals.repairPriorities.ElementAt(index).Value;
                // Get current mech location loadout from the looped chassis definitions
                LocationLoadoutDef thisLocLoadout = mech.GetLocationLoadoutDef(thisLoc);
                // Friendly name for this location
                string thisLocName = thisLoc.ToString();

                // Work out difference of armor lost for each location - default to 0
                int armorDifference = 0;

                // Consider rear armour in difference calculation if this is a RT, CT or LT
                if (thisLocLoadout == mech.CenterTorso || thisLocLoadout == mech.RightTorso || thisLocLoadout == mech.LeftTorso)
                {
                    armorDifference = (int)Mathf.Abs(thisLocLoadout.CurrentArmor - thisLocLoadout.AssignedArmor) + (int)Mathf.Abs(thisLocLoadout.CurrentRearArmor - thisLocLoadout.AssignedRearArmor);
                }
                else
                {
                    armorDifference = (int)Mathf.Abs(thisLocLoadout.CurrentArmor - thisLocLoadout.AssignedArmor);
                }

                // If any difference betwen the location's current and assigned armor is detected, flag this mech for armor repair
                if (armorDifference > 0)
                {
                    Logger.LogDebug(mech.Name + " requires armor repair based on armor loss from: " + thisLocName);
                    mechNeedsArmor = true;
                    break; // Stop evaluating other locations once a repair requirement is determined on any location (no point checking further)
                }
            }

            if (!mechNeedsArmor)
            {
                Logger.LogInfo(mech.Name + " does not require armor repairs.");
            }

            return(mechNeedsArmor);
        }
Example #6
0
        private static void Postfix(ref SimGameState __instance, ref string mechSimGameUID, ref ChassisLocations location, ref int armorDiff, ref int frontArmor, ref int rearArmor, ref WorkOrderEntry_ModifyMechArmor __result)
        {
            string id = string.Format("MechLab - ModifyArmor - {0}", __instance.GenerateSimGameUID());

            try
            {
                float mechTonnageModifier = 1f;
                int   techCost            = 0;
                int   cbillCost           = 0;

                foreach (MechDef mechDef in __instance.ActiveMechs.Values)
                {
                    if (mechDef.GUID == mechSimGameUID)
                    {
                        // If ScaleArmorCostByTonnage is enabled, make the mech tonnage work as a percentage tech cost reduction (95 tons = 0.95 or "95%" of the cost, 50 tons = 0.05 or "50%" of the cost etc)
                        if (ArmorRepair.ModSettings.ScaleArmorCostByTonnage)
                        {
                            mechTonnageModifier = mechDef.Chassis.Tonnage * 0.01f;
                        }
                        float locationTechCost  = ((armorDiff * mechTonnageModifier) * __instance.Constants.MechLab.ArmorInstallTechPoints);
                        float locationCbillCost = ((armorDiff * mechTonnageModifier) * __instance.Constants.MechLab.ArmorInstallCost);
                        techCost  = Mathf.CeilToInt(locationTechCost);
                        cbillCost = Mathf.CeilToInt(locationCbillCost);

                        Logger.LogDebug("Armor WO SubEntry Costing: ");
                        Logger.LogDebug("***************************************");
                        Logger.LogDebug("location: " + location.ToString());
                        Logger.LogDebug("armorDifference: " + armorDiff);
                        Logger.LogDebug("mechTonnage: " + mechDef.Chassis.Tonnage);
                        Logger.LogDebug("mechTonnageModifier: " + mechTonnageModifier);
                        Logger.LogDebug("techCost: " + techCost);
                        Logger.LogDebug("cbillCost: " + cbillCost);
                        Logger.LogDebug("***************************************");
                    }
                }

                __result = new WorkOrderEntry_ModifyMechArmor(id, string.Format("Modify Armor - {0}", location.ToString()), mechSimGameUID, techCost, location, frontArmor, rearArmor, cbillCost, string.Empty);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
            }
        }
        private static void Postfix(ref SimGameState __instance,
                                    ref string mechSimGameUID,
                                    ref ChassisLocations location,
                                    ref int armorDiff, ref int frontArmor, ref int rearArmor, ref WorkOrderEntry_ModifyMechArmor __result)
        {
            string id = string.Format("MechLab - ModifyArmor - {0}", __instance.GenerateSimGameUID());

            try
            {
                float mechTonnageModifier = 1f;
                int   techCost            = 0;
                int   cbillCost           = 0;


                foreach (MechDef mechDef in __instance.ActiveMechs.Values)
                {
                    if (mechDef.GUID == mechSimGameUID)
                    {
                        ArmorRepairFactor armor     = null;
                        MechComponentRef  armoritem = null;
                        foreach (var item in mechDef.Inventory)
                        {
                            if (item.IsCategory(ArmorRepair.ModSettings.ArmorCategory))
                            {
                                armor     = item.GetComponent <ArmorRepairFactor>();
                                armoritem = item;
                                break;
                            }
                        }

                        Logger.LogDebug("Armor WO SubEntry Costing: ");
                        Logger.LogDebug("***************************************");
                        Logger.LogDebug(" location: " + location.ToString());
                        Logger.LogDebug(" armorDifference: " + armorDiff);
                        Logger.LogDebug(" mechTonnage: " + mechDef.Chassis.Tonnage);
                        Logger.LogDebug(" mechTonnageModifier: " + mechTonnageModifier);
                        if (armor != null)
                        {
                            Logger.LogDebug($" ArmorRepair mods tp:{armor.ArmorTPCost:0.00} cb:{armor.ArmorCBCost:0.00}");
                        }

                        float atpcost = armor?.ArmorTPCost ?? 1;
                        float acbcost = armor?.ArmorCBCost ?? 1;


                        if (ArmorRepair.ModSettings.RepairCostByTag != null && ArmorRepair.ModSettings.RepairCostByTag.Length > 0)
                        {
                            foreach (var cost in ArmorRepair.ModSettings.RepairCostByTag)
                            {
                                if (mechDef.Chassis.ChassisTags.Contains(cost.Tag))
                                {
                                    Logger.LogDebug($" Chassis {cost.Tag} mods tp:{cost.ArmorTPCost:0.00} cb:{cost.ArmorCBCost:0.00}");
                                    atpcost *= cost.ArmorTPCost;
                                    acbcost *= cost.ArmorCBCost;
                                }

                                if (armoritem != null && armoritem.Def.ComponentTags.Contains(cost.Tag))
                                {
                                    Logger.LogDebug($" {armoritem.ComponentDefID} {cost.Tag} mods tp:{cost.ArmorTPCost:0.00} cb:{cost.ArmorCBCost:0.00}");
                                    atpcost *= cost.ArmorTPCost;
                                    acbcost *= cost.ArmorCBCost;
                                }
                            }
                        }


                        // If ScaleArmorCostByTonnage is enabled, make the mech tonnage work as a percentage tech cost reduction (95 tons = 0.95 or "95%" of the cost, 50 tons = 0.05 or "50%" of the cost etc)
                        if (ArmorRepair.ModSettings.ScaleArmorCostByTonnage)
                        {
                            mechTonnageModifier = mechDef.Chassis.Tonnage * 0.01f;
                        }

                        float locationTechCost  = ((armorDiff * mechTonnageModifier) * __instance.Constants.MechLab.ArmorInstallTechPoints) * atpcost;
                        float locationCbillCost = ((armorDiff * mechTonnageModifier) * __instance.Constants.MechLab.ArmorInstallCost) * acbcost;
                        techCost  = Mathf.CeilToInt(locationTechCost);
                        cbillCost = Mathf.CeilToInt(locationCbillCost);

                        Logger.LogDebug($" tpmod: {atpcost:0.000}");
                        Logger.LogDebug($" cbmod: {acbcost:0.000}");

                        Logger.LogDebug(" techCost: " + techCost);
                        Logger.LogDebug(" cbillCost: " + cbillCost);
                        Logger.LogDebug("***************************************");
                    }
                }

                __result = new WorkOrderEntry_ModifyMechArmor(id, string.Format("Modify Armor - {0}", location.ToString()), mechSimGameUID, techCost, location, frontArmor, rearArmor, cbillCost, string.Empty);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
            }
        }
Example #8
0
 internal static string GetStringFromLocation(ChassisLocations location)
 {
     return(location.ToString().ToLower());
 }
Example #9
0
        internal bool ValidateMech(
            out string error,
            MechDef mechDef,
            ChassisLocations dropLocation,
            MechComponentRef droppedComponent = null,
            Dictionary <MechValidationType, List <Text> > errors = null)
        {
            error = null;

            var tagsOnMech = new HashSet <string>();

            // chassis
            {
                var chassis = mechDef.Chassis;
                // tags
                if (chassis.ChassisTags != null)
                {
                    tagsOnMech.UnionWith(chassis.ChassisTags);
                }

                // id
                var identifier = chassis.Description.Id;
                tagsOnMech.Add(identifier);
            }

            void ProcessComponent(MechComponentRef item, HashSet <string> tagsForComponent, string location)
            {
                // tags
                if (item.Def.ComponentTags != null)
                {
                    tagsForComponent.UnionWith(item.Def.ComponentTags.Select(i => i.Replace("{location}", location)));
                }

                // id
                var identifier = item.ComponentDefID;

                tagsForComponent.Add(identifier);

                // category for component
                foreach (var component in item.GetComponents <Category>())
                {
                    tagsForComponent.Add(component.CategoryID);
                    if (!string.IsNullOrEmpty(component.Tag))
                    {
                        tagsForComponent.Add(component.Tag);
                    }
                }
            }

            // components
            foreach (var def in mechDef.Inventory)
            {
                ProcessComponent(def, tagsOnMech, def.MountedLocation.ToString());
            }

            HashSet <string> tagsForDropped = null;

            if (droppedComponent != null)
            {
                tagsForDropped = new HashSet <string>();
                ProcessComponent(droppedComponent, tagsForDropped, dropLocation.ToString());
                tagsOnMech.UnionWith(tagsForDropped); // used for incompatible check
            }

            var checkRequiresForTags = tagsOnMech;

            if (tagsForDropped != null)
            {
                checkRequiresForTags = tagsForDropped;

                if (!Control.Settings.TagRestrictionDropValidateRequiredTags)
                {
                    checkRequiresForTags = new HashSet <string>();
                }
            }

            foreach (var tag in checkRequiresForTags)
            {
                var requiredAnyTags          = RequiredAnyTags(tag);
                var hasMetAnyRequiredAnyTags = true; // no required any tags = ok
                foreach (var requiredAnyTag in requiredAnyTags)
                {
                    hasMetAnyRequiredAnyTags = false; // at least one required any tag check = nok
                    if (tagsOnMech.Contains(requiredAnyTag))
                    {
                        hasMetAnyRequiredAnyTags = true; // at least on required any tag found = ok
                        break;
                    }
                }

                if (hasMetAnyRequiredAnyTags)
                {
                    continue;
                }

                var tagName = NameForTag(tag);
                error = $"{tagName} requirements are not met";

                if (errors == null)
                {
                    return(false);
                }

                errors[MechValidationType.InvalidInventorySlots].Add(new Text(error));
            }


            foreach (var tag in checkRequiresForTags)
            {
                var requiredTags = RequiredTags(tag);
                foreach (var requiredTag in requiredTags)
                {
                    if (tagsOnMech.Contains(requiredTag))
                    {
                        continue;
                    }

                    var tagName         = NameForTag(tag);
                    var requiredTagName = NameForTag(requiredTag);
                    error = $"{tagName} requires {requiredTagName}";

                    if (errors == null)
                    {
                        return(false);
                    }

                    errors[MechValidationType.InvalidInventorySlots].Add(new Text(error));
                }
            }


            var checkIncompatiblesForTags = tagsOnMech;

            if (tagsForDropped != null)
            {
                if (!Control.Settings.TagRestrictionDropValidateIncompatibleTags)
                {
                    checkIncompatiblesForTags = new HashSet <string>();
                }
            }

            foreach (var tag in checkIncompatiblesForTags)
            {
                var tagsPool = tagsOnMech;
                // if dropping we either want only to check either:
                // - the "dropped tags incompatibles" with mech+items
                // - each "mech+items incompatibles" with dropped tags
                if (tagsForDropped != null && !tagsForDropped.Contains(tag))
                {
                    tagsPool = tagsForDropped;
                }

                var incompatibleTags = IncompatibleTags(tag);
                foreach (var incompatibleTag in incompatibleTags)
                {
                    if (!tagsPool.Contains(incompatibleTag))
                    {
                        continue;
                    }

                    var tagName             = NameForTag(tag);
                    var incompatibleTagName = NameForTag(incompatibleTag);
                    error = $"{tagName} can't be used with {incompatibleTagName}";

                    if (errors == null)
                    {
                        return(false);
                    }

                    errors[MechValidationType.InvalidInventorySlots].Add(new Text(error));
                }
            }

            return(error == null);
        }
Example #10
0
        public static bool Prefix(SimGameState __instance, MechDef mech)
        {
            try
            {
                // Start of analysing a mech for armor repair
                Logger.LogInfo("Analysing Mech: " + mech.Name);
                Logger.LogInfo("============================================");

                // Base generic MechLab WO for a mech that requires armor or structure repair - each individual locational subentry WO has to be added to this base WO later
                WorkOrderEntry_MechLab newMechLabWorkOrder = null;

                /* STRUCTURE DAMAGE CHECKS
                 * ------------------------
                 * Check if the given mech needs any structure repaired and that EnableStructureRepair is true in the mod settings
                 *
                 */
                if (ArmorRepair.ModSettings.EnableStructureRepair)
                {
                    if (Helpers.CheckStructureDamage(mech))
                    {
                        Logger.LogDebug("SimGameConstant: StructureRepairTechPoints: " + __instance.Constants.MechLab.StructureRepairTechPoints);
                        Logger.LogDebug("SimGameConstant: StructureRepairCost: " + __instance.Constants.MechLab.StructureRepairCost);

                        // Loop over the ChassisLocations for repair in their highest -> lowest priority order from the dictionary defined in Helpers
                        for (int index = 0; index < Globals.repairPriorities.Count; index++)
                        {
                            // Set current looped ChassisLocation
                            ChassisLocations thisLoc = Globals.repairPriorities.ElementAt(index).Value;
                            // Get current mech's loadout definition from the looped chassis location
                            LocationLoadoutDef thisLocLoadout = mech.GetLocationLoadoutDef(thisLoc);
                            // Friendly name for this location
                            string thisLocName = thisLoc.ToString();

                            Logger.LogDebug("Analysing location: " + thisLocName);

                            // Check if a new base MechLab order needs to be created or not
                            if (newMechLabWorkOrder == null)
                            {
                                // Create new base work order of the generic MechLab type if it doesn't already exist
                                newMechLabWorkOrder = Helpers.CreateBaseMechLabOrder(__instance, mech);
                            }

                            float currentStructure = thisLocLoadout.CurrentInternalStructure;
                            float definedStructure = mech.GetChassisLocationDef(thisLoc).InternalStructure;

                            // Only create work orders for repairing structure if this location has taken damage in combat
                            if (currentStructure != definedStructure)
                            {
                                // Work out difference of structure lost for each location - default to 0
                                int structureDifference = 0;
                                structureDifference = (int)Mathf.Abs(currentStructure - definedStructure);

                                Logger.LogInfo("Total structure difference for " + thisLocName + " is " + structureDifference);

                                Logger.LogInfo("Creating MechRepair work order entry for " + thisLocName);
                                Logger.LogDebug("Calling CreateMechRepairWorkOrder with params - GUID: " +
                                                mech.GUID.ToString() +
                                                " | Location: " + thisLocName +
                                                " | structureDifference: " + structureDifference
                                                );

                                WorkOrderEntry_RepairMechStructure newRepairWorkOrder = __instance.CreateMechRepairWorkOrder(
                                    mech.GUID,
                                    thisLocLoadout.Location,
                                    structureDifference
                                    );

                                Logger.LogDebug("Adding WO subentry to repair missing " + thisLocName + " structure.");
                                newMechLabWorkOrder.AddSubEntry(newRepairWorkOrder);
                            }
                            else
                            {
                                Logger.LogDebug("Structure repair not required for: " + thisLocName);
                            }
                        }
                    }
                }

                /* COMPONENT DAMAGE CHECKS
                 * -----------------------
                 * Check if the given mech needs any critted components repaired
                 *
                 * NOTE: Not yet working. Repair components are added to work order but not actually repaired after WO completes. Noticed there is another queue involved on SGS.WorkOrderComponents we need to debug.
                 * Currently throws "SimGameState [ERROR] ML_RepairComponent MechBay - RepairComponent - SGRef_490 had an invalid mechComponentID Ammo_AmmunitionBox_Generic_AC5, skipping" in SimGame logger on WO completion.
                 * if (Helpers.CheckDamagedComponents(mech))
                 * {
                 *  for (int index = 0; index < mech.Inventory.Length; index++)
                 *  {
                 *      MechComponentRef mechComponentRef = mech.Inventory[index];
                 *
                 *      // Penalized = Critted Component
                 *      if (mechComponentRef.DamageLevel == ComponentDamageLevel.Penalized)
                 *      {
                 *          // Check if a new base MechLab order needs to be created or not
                 *          if (newMechLabWorkOrder == null)
                 *          {
                 *              // Create new base work order of the generic MechLab type if it doesn't already exist
                 *              newMechLabWorkOrder = Helpers.CreateBaseMechLabOrder(__instance, mech);
                 *          }
                 *
                 *          // Create a new component repair work order for this component
                 *          Logger.LogInfo("Creating Component Repair work order entry for " + mechComponentRef.ComponentDefID);
                 *          WorkOrderEntry_RepairComponent newComponentRepairOrder = __instance.CreateComponentRepairWorkOrder(mechComponentRef, false);
                 *
                 *          // Attach as a child to the base Mech Lab order.
                 *          Logger.LogDebug("Adding WO subentry to repair component " + mechComponentRef.ComponentDefID);
                 *          newMechLabWorkOrder.AddSubEntry(newComponentRepairOrder);
                 *      }
                 *  }
                 * }
                 */


                /* ARMOR DAMAGE CHECKS
                 * -------------------
                 * Check if the given mech needs any structure repaired
                 *
                 */
                if (Helpers.CheckArmorDamage(mech))
                {
                    Logger.LogDebug("SimGameConstant: ArmorInstallTechPoints: " + __instance.Constants.MechLab.ArmorInstallTechPoints);
                    Logger.LogDebug("SimGameConstant: ArmorInstallCost: " + __instance.Constants.MechLab.ArmorInstallCost);

                    // Loop over the ChassisLocations for repair in their highest -> lowest priority order from the dictionary defined in Helpers
                    for (int index = 0; index < Globals.repairPriorities.Count; index++)
                    {
                        // Set current ChassisLocation
                        ChassisLocations thisLoc = Globals.repairPriorities.ElementAt(index).Value;
                        // Get current mech's loadout from the looped chassis location
                        LocationLoadoutDef thisLocLoadout = mech.GetLocationLoadoutDef(thisLoc);
                        // Friendly name for this location
                        string thisLocName = thisLoc.ToString();

                        Logger.LogDebug("Analysing location: " + thisLocName);

                        // Check if a new base MechLab order needs to be created
                        if (newMechLabWorkOrder == null)
                        {
                            // Create new base work order of the generic MechLab type if it doesn't already exist
                            newMechLabWorkOrder = Helpers.CreateBaseMechLabOrder(__instance, mech);
                        }

                        // Work out difference of armor lost for each location - default to 0
                        int armorDifference = 0;

                        // Consider rear armour in difference calculation if this is a RT, CT or LT
                        if (thisLocLoadout == mech.CenterTorso || thisLocLoadout == mech.RightTorso || thisLocLoadout == mech.LeftTorso)
                        {
                            Logger.LogDebug("Location also has rear armor.");
                            armorDifference = (int)Mathf.Abs(thisLocLoadout.CurrentArmor - thisLocLoadout.AssignedArmor) + (int)Mathf.Abs(thisLocLoadout.CurrentRearArmor - thisLocLoadout.AssignedRearArmor);
                        }
                        else
                        {
                            armorDifference = (int)Mathf.Abs(thisLocLoadout.CurrentArmor - thisLocLoadout.AssignedArmor);
                        }
                        // Only create work orders for repairing armor if this location has taken armor damage in combat
                        if (armorDifference != 0)
                        {
                            Logger.LogInfo("Total armor difference for " + thisLocName + " is " + armorDifference);
                            Logger.LogInfo("Creating ModifyMechArmor work order entry for " + thisLocName);
                            Logger.LogDebug("Calling ModifyMechArmor WO with params - GUID: " +
                                            mech.GUID.ToString() +
                                            " | Location: " + thisLocName +
                                            " | armorDifference: " + armorDifference +
                                            " | AssignedArmor: " + thisLocLoadout.AssignedArmor +
                                            " | AssignedRearArmor: " + thisLocLoadout.AssignedRearArmor
                                            );
                            WorkOrderEntry_ModifyMechArmor newArmorWorkOrder = __instance.CreateMechArmorModifyWorkOrder(
                                mech.GUID,
                                thisLocLoadout.Location,
                                armorDifference,
                                (int)(thisLocLoadout.AssignedArmor),
                                (int)(thisLocLoadout.AssignedRearArmor)
                                );

                            /* IMPORTANT!
                             * This has turned out to be required as CurrentArmor appears to be reset to AssignedArmor from somewhere unknown in the game after battle
                             * So if we don't reset AssignedArmor now, player can cancel the work order to get a free armor reset anyway!
                             *
                             * NOTE: CeilToInt (or similar rounding) is vital to prevent fractions of armor from causing Mech tonnage / validation issues for the player
                             */
                            Logger.LogDebug("Forcing assignment of Assigned Armor: " + thisLocLoadout.AssignedArmor + " To Current Armor (CeilToInt): " + Mathf.CeilToInt(thisLocLoadout.CurrentArmor));
                            thisLocLoadout.AssignedArmor     = Mathf.CeilToInt(thisLocLoadout.CurrentArmor);
                            thisLocLoadout.AssignedRearArmor = Mathf.CeilToInt(thisLocLoadout.CurrentRearArmor);

                            Logger.LogInfo("Adding WO subentry to install missing " + thisLocName + " armor.");
                            newMechLabWorkOrder.AddSubEntry(newArmorWorkOrder);
                        }
                        else
                        {
                            Logger.LogDebug("Armor repair not required for: " + thisLocName);
                        }
                    }
                }


                /* WORK ORDER SUBMISSION
                 * ---------------------
                 * Submit the complete work order for the mech, which will include any repair armor / structure subentries for each location
                 *
                 */
                if (newMechLabWorkOrder != null)
                {
                    if (newMechLabWorkOrder.SubEntryCount > 0)
                    {
                        // Submit work order to our temporary queue for internal processing
                        Helpers.SubmitTempWorkOrder(
                            __instance,
                            newMechLabWorkOrder,
                            mech
                            );
                    }
                    else
                    {
                        Logger.LogInfo(mech.Name + " did not require repairs.");
                    }
                }

                // Lifted from original RestoreMechPostCombat method - resets any non-functional mech components back to functional
                foreach (MechComponentRef mechComponentRef in mech.Inventory)
                {
                    if (mechComponentRef.DamageLevel == ComponentDamageLevel.NonFunctional)
                    {
                        Logger.LogDebug("Resetting non-functional mech component: " + mechComponentRef.ToString());
                        mechComponentRef.DamageLevel = ComponentDamageLevel.Functional;
                    }
                }

                return(false); // Finally, prevent firing the original method
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                return(true); // Allow original method to fire if there is an exception
            }
        }
        private static void Postfix(ref SimGameState __instance, ref string mechSimGameUID, ref ChassisLocations location, ref int structureCount, ref WorkOrderEntry_RepairMechStructure __result)
        {
            try
            {
                float mechTonnageModifier = 1f;
                // Original method code, this is still needed to work out zero structure modifiers
                string id          = string.Format("MechLab - RepairMech - {0}", __instance.GenerateSimGameUID());
                bool   is_repaired = false;
                float  cbmod       = 1f;
                float  tpmod       = 1f;

                foreach (MechDef mechDef in __instance.ActiveMechs.Values)
                {
                    if (mechDef.GUID == mechSimGameUID)
                    {
                        if (mechDef.GetChassisLocationDef(location).InternalStructure == (float)structureCount)
                        {
                            is_repaired = true;
                            break;
                        }
                        Logger.LogDebug("Structure WO Subentry Costing:");
                        Logger.LogDebug("***************************************");
                        Logger.LogDebug(" location: " + location.ToString());
                        Logger.LogDebug(" structureCount: " + structureCount);
                        Logger.LogDebug(" mechTonnageModifier: " + mechTonnageModifier);

                        // If ScaleStructureCostByTonnage is enabled, make the mech tonnage work as a percentage tech cost reduction (95 tons = 0.95 or "95%" of the cost, 50 tons = 0.05 or "50%" of the cost etc)
                        if (ArmorRepair.ModSettings.ScaleStructureCostByTonnage)
                        {
                            mechTonnageModifier = mechDef.Chassis.Tonnage * 0.01f;
                        }

                        StructureRepairFactor str        = null;
                        MechComponentRef      structitem = null;
                        foreach (var item in mechDef.Inventory)
                        {
                            if (item.IsCategory(ArmorRepair.ModSettings.StructureCategory))
                            {
                                str        = item.GetComponent <StructureRepairFactor>();
                                structitem = item;
                                break;
                            }
                        }

                        if (str != null)
                        {
                            Logger.LogDebug($" StructRepair mods tp:{str.StructureTPCost:0.00} cb:{str.StructureCBCost:0.00}");
                        }

                        tpmod *= str?.StructureTPCost ?? 1;
                        cbmod *= str?.StructureTPCost ?? 1;


                        if (ArmorRepair.ModSettings.RepairCostByTag != null && ArmorRepair.ModSettings.RepairCostByTag.Length > 0)
                        {
                            foreach (var cost in ArmorRepair.ModSettings.RepairCostByTag)
                            {
                                if (mechDef.Chassis.ChassisTags.Contains(cost.Tag))
                                {
                                    Logger.LogDebug($" Chassis {cost.Tag} mods tp:{cost.StructureTPCost:0.00} cb:{cost.StructureCBCost:0.00}");
                                    tpmod *= cost.StructureTPCost;
                                    cbmod *= cost.StructureCBCost;
                                }

                                if (structitem != null && structitem.Def.ComponentTags.Contains(cost.Tag))
                                {
                                    Logger.LogDebug($" {structitem.ComponentDefID} {cost.Tag} mods tp:{cost.StructureTPCost:0.00} cb:{cost.StructureCBCost:0.00}");

                                    tpmod *= cost.StructureTPCost;
                                    cbmod *= cost.StructureCBCost;
                                }
                            }
                        }
                        break;
                    }
                }
                if (is_repaired)
                {
                    cbmod = __instance.Constants.MechLab.ZeroStructureCBillModifier;
                    tpmod = __instance.Constants.MechLab.ZeroStructureTechPointModifier;
                }



                int techCost  = Mathf.CeilToInt((__instance.Constants.MechLab.StructureRepairTechPoints * (float)structureCount * tpmod) * mechTonnageModifier);
                int cbillCost = Mathf.CeilToInt((float)((__instance.Constants.MechLab.StructureRepairCost * structureCount) * cbmod) * mechTonnageModifier);

                Logger.LogDebug($" tpmod: {tpmod:0.000}");
                Logger.LogDebug($" cbmod: {cbmod:0.000}");
                Logger.LogDebug(" techCost: " + techCost);
                Logger.LogDebug(" cBill cost: " + cbillCost);
                Logger.LogDebug("***************************************");

                __result = new WorkOrderEntry_RepairMechStructure(id, string.Format("Repair 'Mech - {0}", location.ToString()), mechSimGameUID, techCost, location, structureCount, cbillCost, string.Empty);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
            }
        }