Beispiel #1
0
        // Calculate the initiative modifiers from all components based upon a MechDef. For whatever reason they
        //  reverse the modifier right out of the gate, such that these values are positives automatically
        public static int GetNormalizedComponentModifier(MechDef mechDef)
        {
            int unitInit = 0;

            if (mechDef.Inventory != null)
            {
                MechComponentRef[] inventory = mechDef.Inventory;
                foreach (MechComponentRef mechComponentRef in inventory)
                {
                    if (mechComponentRef.Def != null && mechComponentRef.Def.statusEffects != null)
                    {
                        EffectData[] statusEffects = mechComponentRef.Def.statusEffects;
                        foreach (EffectData effect in statusEffects)
                        {
                            if (MechStatisticsRules.GetInitiativeModifierFromEffectData(effect, true, null) == 0)
                            {
                                unitInit += MechStatisticsRules.GetInitiativeModifierFromEffectData(effect, false, null);
                            }
                        }
                    }
                }
            }

            Mod.Log.Debug($"Normalized BaseInit for mechDef:{mechDef.Name} is unitInit:{unitInit}");
            return(unitInit);
        }
        public static void ValidateAllMechDefTonnages(this MechLabPanel mechLabPanel, bool errorsOnly = true)
        {
            foreach (KeyValuePair <string, MechDef> mechDefs in mechLabPanel.Sim.DataManager.MechDefs)
            {
                string  id      = mechDefs.Key;
                MechDef mechDef = mechDefs.Value;
                float   num     = 0f;
                float   tonnage = mechDef.Chassis.Tonnage;

                MechStatisticsRules.CalculateTonnage(mechDef, ref num, ref tonnage);

                if ((double)Mathf.Abs(num - mechDef.Chassis.Tonnage) < 0.0001)
                {
                    if (!errorsOnly)
                    {
                        Logger.Debug("[MechLabPanelExtensions_ValidateAllMechDefTonnages] MechDef (" + mechDef.Name + "/" + id + "): Passed");
                    }
                }
                else if (num > mechDef.Chassis.Tonnage)
                {
                    float diff = num - mechDef.Chassis.Tonnage;
                    Logger.Debug("[MechLabPanelExtensions_ValidateAllMechDefTonnages] MechDef (" + mechDef.Name + "/" + id + "): OVERWEIGHT (+" + diff + " Tons)");
                }
                //else if (num <= mechDef.Chassis.Tonnage - 0.5f)
                else if (num < mechDef.Chassis.Tonnage)
                {
                    float diff = mechDef.Chassis.Tonnage - num;
                    Logger.Debug("[MechLabPanelExtensions_ValidateAllMechDefTonnages] MechDef (" + mechDef.Name + "/" + id + "): UNDERWEIGHT (-" + diff + " Tons)");
                }
            }
        }
        public void ModifyMech(MechDef mDef, SimGameState s, UpgradeList ulist, ref float _, List <string[]> changedAmmoTypes, MechDef fromData)
        {
            BTRandomMechComponentUpgrader_Init.Log.Log("correcting tonage 1: inventory");
            List <MechComponentRef> inv = mDef.Inventory.ToList();
            float tonnage = 0;
            float max     = 0;

            MechStatisticsRules.CalculateTonnage(mDef, ref tonnage, ref max);
            while (tonnage > mDef.Chassis.Tonnage)
            {
                int i = inv.FindIndex((x) => !x.IsFixed && ulist.CanRemove.Contains(x.ComponentDefID));
                if (i == -1)
                {
                    BTRandomMechComponentUpgrader_Init.Log.Log("no removable found");
                    break;
                }
                BTRandomMechComponentUpgrader_Init.Log.Log($"removed {inv[i].ComponentDefID} to reduce weight");
                tonnage -= inv[i].Def.Tonnage;
                inv.RemoveAt(i);
            }

            foreach (string id in ulist.CanRemove)
            {
                MechComponentDef d = s.GetComponentDefFromID(id);
                while (tonnage + d.Tonnage <= mDef.Chassis.Tonnage)
                {
                    ChassisLocations loc = ChassisLocations.None;
                    foreach (ChassisLocations l in RMCU_Helper.Locations)
                    {
                        if (mDef.GetFreeSlotsInLoc(inv, l, null) >= d.InventorySize && d.CanPutComponentIntoLoc(l))
                        {
                            loc = l;
                            break;
                        }
                    }
                    if (loc == ChassisLocations.None)
                    {
                        BTRandomMechComponentUpgrader_Init.Log.Log("no free location found!");
                        break;
                    }
                    MechComponentRef r = new MechComponentRef(d.Description.Id, null, d.ComponentType, loc, -1, ComponentDamageLevel.Functional, false);
                    r.SetComponentDef(d);
                    inv.Add(r);
                    tonnage += d.Tonnage;
                    BTRandomMechComponentUpgrader_Init.Log.Log($"added {r.ComponentDefID} to use free weight");
                }
            }
            mDef.SetInventory(inv.ToArray());
        }
        public static float GetMechArmorPointFactor(this MechDef m)
        {
            float t = 0;
            float _ = 0;

            MechStatisticsRules.CalculateTonnage(m, ref t, ref _);
            t -= m.Chassis.InitialTonnage;
            foreach (MechComponentRef i in m.Inventory)
            {
                t -= i.Def.Tonnage;
            }
            float arm = m.GetMechArmor();

            return(t / arm);
        }
Beispiel #5
0
        private static void AutoFixMechDef(MechDef mechDef, IEnumerable <MechDefAutoFixCategory> fixers)
        {
            if (Control.settings.AutoFixMechDefSkip.Contains(mechDef.Description.Id))
            {
                return;
            }

            mechDef.Refresh();

            float originalTotalTonnage = 0, maxValue = 0;

            MechStatisticsRules.CalculateTonnage(mechDef, ref originalTotalTonnage, ref maxValue);

            foreach (var fixer in fixers)
            {
                if (fixer.ShouldFix)
                {
                    fixer.AutoFixMechDef.AutoFixMechDef(mechDef, originalTotalTonnage);
                }
            }
        }
Beispiel #6
0
        internal static void AddEngineIfPossible(MechDef mechDef)
        {
            if (!Control.settings.AutoFixMechDefEngine)
            {
                return;
            }

            //Control.mod.Logger.LogDebug("A Id=" + mechDef.Description.Id);

            mechDef.Refresh();

            //Control.mod.Logger.LogDebug("B DataManager=" + mechDef.DataManager);

            if (mechDef.Inventory.Any(x => x.GetEngineRef() != null))
            {
                return;
            }

            float currentValue = 0, maxValue = 0;

            MechStatisticsRules.CalculateTonnage(mechDef, ref currentValue, ref maxValue);

            var tonnage          = mechDef.Chassis.Tonnage;
            var maxEngineTonnage = tonnage - currentValue;
            var maxEngine        = (EngineDef)null;

            //Control.mod.Logger.LogDebug("C maxEngineTonnage=" + maxEngineTonnage);

            foreach (var keyvalue in mechDef.DataManager.HeatSinkDefs)
            {
                var heatSinkDef = keyvalue.Value;

                if (heatSinkDef.Tonnage > maxEngineTonnage)
                {
                    continue;
                }

                var engineDef = heatSinkDef.GetEngineDef();
                if (engineDef == null)
                {
                    continue;
                }

                if (engineDef.Type != EngineDef.EngineType.Std)
                {
                    continue;
                }

                if (maxEngine != null && maxEngine.Rating >= engineDef.Rating)
                {
                    continue;
                }

                maxEngine = engineDef;
            }

            //Control.mod.Logger.LogDebug("D maxEngine=" + maxEngine);

            if (maxEngine == null)
            {
                return;
            }

            var componentRefs = new List <MechComponentRef>(mechDef.Inventory);

            { // remove superfluous jump jets
                var maxJetCount = Control.calc.CalcJumpJetCount(maxEngine, tonnage);
                var jumpJetList = componentRefs.Where(x => x.ComponentDefType == ComponentType.JumpJet).ToList();
                for (var i = 0; i < jumpJetList.Count - maxJetCount; i++)
                {
                    componentRefs.Remove(jumpJetList[i]);
                }
            }

            { // add engine
                var componentRef = new MechComponentRef(maxEngine.Def.Description.Id, null, maxEngine.Def.ComponentType, ChassisLocations.CenterTorso);
                componentRefs.Add(componentRef);
            }

            mechDef.SetInventory(componentRefs.ToArray());
        }
 internal HardpointCounter(ChassisDef chassisDef, ChassisLocations location)
 {
     MechStatisticsRules.GetHardpointCountForLocation(chassisDef, location, ref numBallistic, ref numEnergy, ref numMissile, ref numSmall);
 }
Beispiel #8
0
        public void AutoFixMechDef(MechDef mechDef)
        {
            if (!AutoFixerFeature.settings.MechDefEngine)
            {
                return;
            }

            if (!AutoFixerFeature.settings.MechTagsAutoFixEnabled.Any(mechDef.MechTags.Contains))
            {
                return;
            }

            Control.mod.Logger.Log($"Auto fixing mechDef={mechDef.Description.Id} chassisDef={mechDef.Chassis.Description.Id}");

            MechDefBuilder builder;

            {
                var inventory = mechDef.Inventory.ToList();
                foreach (var componentRef in inventory)
                {
                    Control.mod.Logger.LogDebug($" {componentRef.ComponentDefID}{(componentRef.IsFixed?" (fixed)":"")} at {componentRef.MountedLocation}");
                }

                builder = new MechDefBuilder(mechDef.Chassis, inventory);
            }

            ArmorStructureRatioFeature.Shared.AutoFixMechDef(mechDef);

            var res = EngineSearcher.SearchInventory(builder.Inventory);

            var engineHeatSinkDef = mechDef.DataManager.HeatSinkDefs.Get(res.CoolingDef.HeatSinkDefId).GetComponent <EngineHeatSinkDef>();

            float CalcFreeTonnage()
            {
                float currentTotalTonnage = 0, maxValue = 0;

                MechStatisticsRules.CalculateTonnage(mechDef, ref currentTotalTonnage, ref maxValue);
                var freeTonnage = mechDef.Chassis.Tonnage - currentTotalTonnage;

                return(freeTonnage);
            }

            if (!EngineFeature.settings.AllowMixingHeatSinkTypes)
            {
                // remove incompatible heat sinks
                var incompatibleHeatSinks = builder.Inventory
                                            .Where(r => r.Def.Is <EngineHeatSinkDef>(out var hs) && hs.HSCategory != engineHeatSinkDef.HSCategory)
                                            .ToList();

                foreach (var incompatibleHeatSink in incompatibleHeatSinks)
                {
                    builder.Remove(incompatibleHeatSink);
                    builder.Add(engineHeatSinkDef.Def, ChassisLocations.Head, true);
                }
            }

            Engine engine = null;

            if (res.CoreDef != null)
            {
                engine = new Engine(res.CoolingDef, res.HeatBlockDef, res.CoreDef, res.Weights, new List <MechComponentRef>());

                // convert external heat sinks into internal ones
                // TODO only to make space if needed, drop the rest of the heat sinks

                {
                    var max     = engine.HeatSinkInternalAdditionalMaxCount;
                    var current = engine.EngineHeatBlockDef.HeatSinkCount;

                    var heatSinks = builder.Inventory
                                    .Where(r => r.Def.Is <EngineHeatSinkDef>(out var hs) && hs.HSCategory == engineHeatSinkDef.HSCategory)
                                    .ToList();

                    while (current < max && heatSinks.Count > 0)
                    {
                        var component = heatSinks[0];
                        heatSinks.RemoveAt(0);
                        builder.Remove(component);
                        current++;
                    }

                    if (current > 0)
                    {
                        var heatBlock = builder.Inventory.FirstOrDefault(r => r.Def.Is <EngineHeatBlockDef>());
                        if (heatBlock != null)
                        {
                            builder.Remove(heatBlock);
                        }

                        var heatBlockDefId = $"{AutoFixerFeature.settings.MechDefHeatBlockDef}_{current}";
                        var def            = mechDef.DataManager.HeatSinkDefs.Get(heatBlockDefId);
                        builder.Add(def, ChassisLocations.CenterTorso, true);
                    }
                }
            }
            else
            {
                var freeTonnage = CalcFreeTonnage();

                Control.mod.Logger.LogDebug($" find engine for freeTonnage={freeTonnage}");

                var jumpJets       = builder.Inventory.Where(x => x.ComponentDefType == ComponentType.JumpJet).ToList();
                var jumpJetTonnage = jumpJets.Select(x => x.Def.Tonnage).FirstOrDefault(); //0 if no jjs

                var externalHeatSinks = builder.Inventory
                                        .Where(r => r.Def.Is <EngineHeatSinkDef>(out var hs) && hs.HSCategory == engineHeatSinkDef.HSCategory)
                                        .ToList();
                var internalHeatSinksCount = res.HeatBlockDef.HeatSinkCount;

                var engineCandidates = new List <Engine>();

                var engineCoreDefs = mechDef.DataManager.HeatSinkDefs
                                     .Select(hs => hs.Value)
                                     .Select(hs => hs.GetComponent <EngineCoreDef>())
                                     .Where(c => c != null)
                                     .OrderByDescending(x => x.Rating);

                var removedExternalHeatSinksOverUse = false;

                foreach (var coreDef in engineCoreDefs)
                {
                    {
                        // remove superfluous jump jets
                        var maxJetCount = coreDef.GetMovement(mechDef.Chassis.Tonnage).JumpJetCount;
                        while (jumpJets.Count > maxJetCount)
                        {
                            var lastIndex = jumpJets.Count - 1;
                            var jumpJet   = jumpJets[lastIndex];
                            freeTonnage += jumpJet.Def.Tonnage;
                            builder.Remove(jumpJet);
                            jumpJets.Remove(jumpJet);

                            Control.mod.Logger.LogDebug("  Removed JumpJet");
                        }
                    }

                    {
                        var candidate = new Engine(res.CoolingDef, res.HeatBlockDef, coreDef, res.Weights, new List <MechComponentRef>());

                        Control.mod.Logger.LogDebug($"  candidate id={coreDef.Def.Description.Id} TotalTonnage={candidate.TotalTonnage}");

                        engineCandidates.Add(candidate);

                        var internalHeatSinksMax = candidate.HeatSinkInternalAdditionalMaxCount;

                        // convert external ones to internal ones
                        while (internalHeatSinksCount < internalHeatSinksMax && externalHeatSinks.Count > 0)
                        {
                            var component = externalHeatSinks[0];
                            externalHeatSinks.RemoveAt(0);
                            builder.Remove(component);
                            internalHeatSinksCount++;

                            Control.mod.Logger.LogDebug("  ~Converted external to internal");
                        }

                        // this only runs on the engine that takes the most heat sinks (since this is in a for loop with rating descending order)
                        // that way we only remove external heat sinks that couldn't be moved internally
                        while (!removedExternalHeatSinksOverUse && externalHeatSinks.Count > 0)
                        {
                            var component = externalHeatSinks[0];
                            externalHeatSinks.RemoveAt(0);
                            builder.Remove(component);
                            var newComponent = builder.Add(component.Def);
                            if (newComponent == null)
                            {
                                Control.mod.Logger.LogDebug("  Removed external heat sink that doesn't fit");
                                // might still need to remove some
                                continue;
                            }
                            // addition worked
                            externalHeatSinks.Add(newComponent);
                            break;
                        }
                        removedExternalHeatSinksOverUse = true;

                        // convert internal ones to external ones
                        while (internalHeatSinksCount > internalHeatSinksMax)
                        {
                            if (builder.Add(engineHeatSinkDef.Def) == null)
                            {
                                Control.mod.Logger.LogDebug("  ~Dropped external when converting from internal");
                                freeTonnage++;
                            }
                            else
                            {
                                Control.mod.Logger.LogDebug("  ~Converted internal to external");
                            }
                            internalHeatSinksCount--;
                        }

                        // remove candidates that make no sense anymore
                        // TODO not perfect and maybe too large for small mechs
                        engineCandidates = engineCandidates.Where(x => x.TotalTonnage <= freeTonnage + 6 * engineHeatSinkDef.Def.Tonnage + jumpJetTonnage).ToList();
                    }

                    // go through all candidates, larger first
                    engine = engineCandidates.FirstOrDefault(candidate => candidate.TotalTonnage <= freeTonnage);

                    if (engine != null)
                    {
                        break;
                    }
                }

                if (engine != null)
                {
                    Control.mod.Logger.LogDebug($" engine={engine.CoreDef} freeTonnage={freeTonnage}");
                    var dummyCore = builder.Inventory.FirstOrDefault(r => r.ComponentDefID == AutoFixerFeature.settings.MechDefCoreDummy);
                    builder.Remove(dummyCore);
                    builder.Add(engine.CoreDef.Def, ChassisLocations.CenterTorso, true);

                    // convert internal heat sinks back as external ones if the mech can fit it
                    while (internalHeatSinksCount > 0 && builder.Add(engineHeatSinkDef.Def) != null)
                    {
                        internalHeatSinksCount--;
                    }

                    if (internalHeatSinksCount > 0)
                    {
                        var heatBlock = builder.Inventory.FirstOrDefault(r => r.Def.Is <EngineHeatBlockDef>());
                        if (heatBlock != null)
                        {
                            builder.Remove(heatBlock);
                        }

                        var heatBlockDefId = $"{AutoFixerFeature.settings.MechDefHeatBlockDef}_{internalHeatSinksCount}";
                        var def            = mechDef.DataManager.HeatSinkDefs.Get(heatBlockDefId);
                        builder.Add(def, ChassisLocations.CenterTorso, true);
                    }
                }
            }

            if (engine == null)
            {
                return;
            }

            // add free heat sinks
            {
                var max = engine.HeatSinkExternalFreeMaxCount;
                for (var i = 0; i < max; i++)
                {
                    builder.Add(engineHeatSinkDef.Def, ChassisLocations.Head, true);
                }
            }

            // find any overused location
            if (builder.HasOveruseAtAnyLocation())
            {
                Control.mod.Logger.LogError($" Overuse found");
                // heatsinks, upgrades
                var itemsToBeReordered = builder.Inventory
                                         .Where(IsMovable)
                                         .OrderBy(c => MechDefBuilder.LocationCount(c.Def.AllowedLocations))
                                         .ThenByDescending(c => c.Def.InventorySize)
                                         .ToList();

                // remove all items that can be reordered: heatsinks, upgrades
                foreach (var item in itemsToBeReordered)
                {
                    builder.Remove(item);
                }

                // then add most restricting, and then largest items first (probably double head sinks)
                foreach (var item in itemsToBeReordered)
                {
                    if (builder.Add(item.Def) == null)
                    {
                        Control.mod.Logger.LogError($" Component {item.ComponentDefID} from {item.MountedLocation} can't be re-added");
                    }
                    else
                    {
                        Control.mod.Logger.LogDebug($"  Component {item.ComponentDefID} re-added");
                    }
                }
            }

            mechDef.SetInventory(builder.Inventory.OrderBy(element => element, new OrderComparer()).ToArray());

            {
                var freeTonnage = CalcFreeTonnage();
                if (freeTonnage > 0)
                {
                    // TODO add armor for each location with free tonnage left
                }
                else if (freeTonnage < 0)
                {
                    var removableItems = builder.Inventory
                                         .Where(IsRemovable)
                                         .OrderBy(c => c.Def.Tonnage)
                                         .ThenByDescending(c => c.Def.InventorySize)
                                         .ThenByDescending(c =>
                    {
                        switch (c.ComponentDefType)
                        {
                        case ComponentType.HeatSink:
                            return(2);

                        case ComponentType.JumpJet:
                            return(1);

                        default:
                            return(0);
                        }
                    })
                                         .ToList();

                    while (removableItems.Count > 0 && freeTonnage < 0)
                    {
                        var item = removableItems[0];
                        removableItems.RemoveAt(0);
                        freeTonnage += item.Def.Tonnage;
                        builder.Remove(item);
                    }
                }
            }

            mechDef.SetInventory(builder.Inventory.OrderBy(element => element, new OrderComparer()).ToArray());
        }
Beispiel #9
0
        private static DumperDataEntry FillMech(MechDef d, int parts, int maxparts, int storage, int active, int sma_parts)
        {
            DumperDataEntry r = new DumperDataEntry();

            r.DataTxt    = new string[9];
            r.DataTxt[0] = ((storage + active) > 0 ? "+" : "-") + d.Chassis.Tonnage + "t " + TryLoc(d.Chassis.Description.UIName) + " " + TryLoc(d.Chassis.VariantName);
            r.DataTxt[1] = TryLoc(d.Chassis.StockRole) + "";
            int bal = 0;
            int en  = 0;
            int mis = 0;
            int sup = 0;

            foreach (ChassisLocations c in AllChassisLocs)
            {
                MechStatisticsRules.GetHardpointCountForLocation(d, c, ref bal, ref en, ref mis, ref sup);
            }
            r.DataTxt[2] = bal + "/" + en + "/" + mis + "/" + sup;
            r.DataTxt[3] = (d.Chassis.Tonnage - d.Chassis.InitialTonnage) + "/" + d.Chassis.Heatsinks;
            float carmor = 0;
            float marmor = 0;

            foreach (ChassisLocations c in AllChassisLocs)
            {
                carmor += d.GetLocationLoadoutDef(c).AssignedArmor;
                marmor += d.GetChassisLocationDef(c).MaxArmor;
                if (d.GetChassisLocationDef(c).MaxRearArmor > 0)
                {
                    carmor += d.GetLocationLoadoutDef(c).AssignedRearArmor;
                    marmor += d.GetChassisLocationDef(c).MaxRearArmor;
                }
            }
            float div = UnityGameInstance.BattleTechGame.MechStatisticsConstants.ARMOR_PER_TENTH_TON * 10f;

            if (d.Chassis.ChassisTags.Contains("chassis_ferro"))
            {
                if (d.Chassis.ChassisTags.Contains("chassis_clan"))
                {
                    div = UnityGameInstance.BattleTechGame.MechStatisticsConstants.ARMOR_PER_TENTH_TON * 12f;
                }
                else
                {
                    div = UnityGameInstance.BattleTechGame.MechStatisticsConstants.ARMOR_PER_TENTH_TON * 11.2f;
                }
            }
            carmor       /= div;
            marmor       /= div;
            carmor        = Mathf.Round(carmor * 10) / 10;
            marmor        = Mathf.Round(marmor * 10) / 10;
            r.DataTxt[3] += "/" + carmor + "/" + marmor + "/" + (d.Chassis.Tonnage - d.Chassis.InitialTonnage - marmor);
            if (d.Chassis.MovementCapDef == null)
            {
                d.Chassis.RefreshMovementCaps();
                if (d.Chassis.MovementCapDef == null)
                {
                    r.DataTxt[4] = "??/" + d.Chassis.MaxJumpjets;
                }
                else
                {
                    r.DataTxt[4] = d.Chassis.MovementCapDef.MaxWalkDistance + "/" + d.Chassis.MaxJumpjets;
                }
            }
            else
            {
                r.DataTxt[4] = d.Chassis.MovementCapDef.MaxWalkDistance + "/" + d.Chassis.MaxJumpjets;
            }
            r.DataTxt[5] = d.Chassis.MeleeDamage + "/" + d.Chassis.MeleeInstability + "/" + d.Chassis.DFADamage + "/" + d.Chassis.DFAInstability;
            r.DataTxt[6] = active + "/" + storage + "/" + parts;
            if (sma_parts >= 0)
            {
                r.DataTxt[6] += "(" + sma_parts + ")";
            }
            r.DataTxt[6] += "/" + maxparts;
            r.DataTxt[7]  = d.Chassis.Description.Id + "/" + d.Description.Id;
            Dictionary <string, int> eq  = new Dictionary <string, int>();
            Dictionary <string, int> feq = new Dictionary <string, int>();

            foreach (MechComponentRef c in d.Inventory)
            {
                if (c.ComponentDefType == ComponentType.Weapon)
                {
                    WeaponDef wep = c.Def as WeaponDef;
                    if (wep != null && wep.WeaponCategoryValue.IsMelee || wep.WeaponSubType == WeaponSubType.AIImaginary || wep.WeaponEffectID.Contains("WeaponEffect-Artillery"))
                    {
                        continue;
                    }
                }
                string key = c.Def.Description.Id;
                if (c.IsFixed)
                {
                    if (feq.ContainsKey(key))
                    {
                        feq[key]++;
                    }
                    else
                    {
                        feq.Add(key, 1);
                    }
                }
                else
                {
                    if (eq.ContainsKey(key))
                    {
                        eq[key]++;
                    }
                    else
                    {
                        eq.Add(key, 1);
                    }
                }
            }
            string txteq  = "";
            string txtfeq = "";

            foreach (string key in eq.Keys.OrderBy((k) => k))
            {
                if (!string.IsNullOrEmpty(txteq))
                {
                    txteq += ",";
                }
                txteq += key + ":" + eq[key];
            }
            foreach (string key in feq.Keys.OrderBy((k) => k))
            {
                if (!string.IsNullOrEmpty(txtfeq))
                {
                    txtfeq += ",";
                }
                txtfeq += key + ":" + feq[key];
            }
            string txtext = "";

            foreach (string ex in ExtrasToNote)
            {
                if (d.Chassis.ChassisTags.Contains(ex))
                {
                    if (!string.IsNullOrEmpty(txtext))
                    {
                        txtext += ",";
                    }
                    txtext += ex;
                }
            }
            r.DataTxt[8] = txtext + "/" + txteq + "/" + txtfeq;
            r.Sort       = string.Format("{0,3}_{1}", new object[] { d.Chassis.Tonnage, d.Chassis.VariantName });
            r.DataCsv    = d.Chassis.Tonnage + ";" + d.Chassis.Description.UIName + ";" + d.Chassis.VariantName + ";" + d.Chassis.StockRole;
            r.DataCsv   += ";" + bal + ";" + en + ";" + mis + ";" + sup;
            r.DataCsv   += ";" + (d.Chassis.Tonnage - d.Chassis.InitialTonnage) + ";" + d.Chassis.Heatsinks + ";" + carmor + ";" + marmor + ";" + (d.Chassis.Tonnage - d.Chassis.InitialTonnage - marmor);
            r.DataCsv   += ";" + (d.Chassis.MovementCapDef == null ? -1f : d.Chassis.MovementCapDef.MaxWalkDistance) + ";" + d.Chassis.MaxJumpjets;
            r.DataCsv   += ";" + d.Chassis.MeleeDamage + ";" + d.Chassis.MeleeInstability + ";" + d.Chassis.DFADamage + ";" + d.Chassis.DFAInstability;
            r.DataCsv   += ";" + active + ";" + storage + ";" + parts;
            if (sma_parts >= 0)
            {
                r.DataCsv += "(" + sma_parts + ")";
            }
            r.DataCsv += ";" + maxparts;
            r.DataCsv += ";" + d.Chassis.Description.Id + ";" + d.Description.Id;
            r.DataCsv += ";" + txtext + ";" + txteq + ";" + txtfeq;
            return(r);
        }
Beispiel #10
0
        public void AutoFixMechDef(MechDef mechDef, float originalTotalTonnage)
        {
            if (mechDef.Inventory.Any(c => c.Def.GetComponent <EngineCoreDef>() != null))
            {
                return;
            }

            var inventory           = new List <MechComponentRef>(mechDef.Inventory);
            var standardHeatSinkDef = mechDef.DataManager.GetDefaultEngineHeatSinkDef();
            var engineHeatSinkDef   = inventory
                                      .Select(r => r.Def.GetComponent <EngineHeatSink>())
                                      .FirstOrDefault(d => d != null && d != standardHeatSinkDef) ?? standardHeatSinkDef;

            if (!Control.settings.AllowMixingHeatSinkTypes)
            {
                // remove incompatible heat sinks
                inventory.RemoveAll(r => r.Def.Is <EngineHeatSink>(out var engineHeatSink) && engineHeatSink.HSCategory != engineHeatSinkDef.HSCategory);
            }

            float freeTonnage;
            {
                float currentTotalTonnage = 0, maxValue = 0;
                MechStatisticsRules.CalculateTonnage(mechDef, ref currentTotalTonnage, ref maxValue);

                var originalInitialTonnage = ChassisHandler.GetOriginalTonnage(mechDef.Chassis);
                if (originalInitialTonnage.HasValue) // either use the freed up tonnage from the initial tonnage fix
                {
                    freeTonnage  = originalInitialTonnage.Value - mechDef.Chassis.InitialTonnage;
                    freeTonnage -= currentTotalTonnage - originalTotalTonnage;
                }

                else // or use up available total tonnage
                {
                    freeTonnage = mechDef.Chassis.Tonnage - currentTotalTonnage;
                }
            }

            //Control.mod.Logger.LogDebug("C maxEngineTonnage=" + maxEngineTonnage);
            var standardWeights   = new Weights(); // use default gyro and weights
            var stanardEngineType = mechDef.DataManager.HeatSinkDefs.Get(Control.settings.AutoFixMechDefEngineTypeDef);

            var engineCoreDefs = mechDef.DataManager.HeatSinkDefs
                                 .Select(hs => hs.Value)
                                 .Select(hs => hs.GetComponent <EngineCoreDef>())
                                 .Where(c => c != null)
                                 .OrderByDescending(x => x.Rating);

            var maxEngine = engineCoreDefs
                            .Select(coreDef => new EngineCoreRef(engineHeatSinkDef, coreDef))
                            .Select(coreRef => new Engine(coreRef, standardWeights, Enumerable.Empty <MechComponentRef>()))
                            .FirstOrDefault(engine => !(engine.TotalTonnage > freeTonnage));

            if (maxEngine == null)
            {
                return;
            }

            // Control.mod.Logger.LogDebug("D maxEngine=" + maxEngine.CoreDef);

            {
                // remove superfluous jump jets
                var maxJetCount = maxEngine.CoreDef.GetMovement(mechDef.Chassis.Tonnage).JumpJetCount;
                var jumpJetList = inventory.Where(x => x.ComponentDefType == ComponentType.JumpJet).ToList();
                for (var i = 0; i < jumpJetList.Count - maxJetCount; i++)
                {
                    inventory.Remove(jumpJetList[i]);
                }
            }

            var builder = new MechDefBuilder(mechDef.Chassis, inventory);

            // add engine
            builder.Add(
                maxEngine.CoreDef.Def,
                ChassisLocations.CenterTorso,
                engineHeatSinkDef != standardHeatSinkDef ? "/ihstype=" + engineHeatSinkDef.Def.Description.Id : null
                );

            // add standard shielding
            builder.Add(stanardEngineType, ChassisLocations.CenterTorso);

            // add free heatsinks
            {
                var count = 0;
                while (count < maxEngine.CoreDef.MaxFreeExternalHeatSinks)
                {
                    if (builder.Add(engineHeatSinkDef.Def))
                    {
                        count++;
                    }
                    else
                    {
                        break;
                    }
                }
            }

            mechDef.SetInventory(inventory.ToArray());
        }
Beispiel #11
0
        public void AutoFixMechDef(MechDef mechDef, float originalTotalTonnage)
        {
            if (mechDef.Inventory.Any(c => c.Def.GetComponent <EngineCoreDef>() != null))
            {
                return;
            }

            float freeTonnage;
            {
                float currentTotalTonnage = 0, maxValue = 0;
                MechStatisticsRules.CalculateTonnage(mechDef, ref currentTotalTonnage, ref maxValue);

                var originalInitialTonnage = ChassisHandler.GetOriginalTonnage(mechDef.Chassis);
                if (originalInitialTonnage.HasValue) // either use the freed up tonnage from the initial tonnage fix
                {
                    freeTonnage  = originalInitialTonnage.Value - mechDef.Chassis.InitialTonnage;
                    freeTonnage -= currentTotalTonnage - originalTotalTonnage;
                }

                else // or use up available total tonnage
                {
                    freeTonnage = mechDef.Chassis.Tonnage - currentTotalTonnage;
                }
            }

            var maxEngine = (Engine)null;

            //Control.mod.Logger.LogDebug("C maxEngineTonnage=" + maxEngineTonnage);

            var standardEngineType  = mechDef.DataManager.HeatSinkDefs.Get(Control.settings.AutoFixMechDefEngineTypeDef).GetComponent <EngineType>();
            var standardHeatSinkDef = mechDef.DataManager.GetDefaultEngineHeatSinkDef();

            var engineHeatSinkdef = mechDef.Inventory
                                    .Select(r => r.Def.GetComponent <EngineHeatSink>())
                                    .FirstOrDefault(d => d != null && d != standardHeatSinkDef) ?? standardHeatSinkDef;

            foreach (var keyvalue in mechDef.DataManager.HeatSinkDefs)
            {
                var heatSinkDef = keyvalue.Value;

                var coreDef = heatSinkDef.GetComponent <EngineCoreDef>();
                if (coreDef == null)
                {
                    continue;
                }

                var coreRef = new EngineCoreRef(engineHeatSinkdef, coreDef);
                var engine  = new Engine(coreRef, standardEngineType, Enumerable.Empty <MechComponentRef>());
                if (engine.TotalTonnage > freeTonnage)
                {
                    continue;
                }

                if (maxEngine != null && maxEngine.CoreDef.Rating >= coreDef.Rating)
                {
                    continue;
                }

                maxEngine = engine;
            }

            if (maxEngine == null)
            {
                return;
            }

            // Control.mod.Logger.LogDebug("D maxEngine=" + maxEngine.CoreDef);

            var componentRefs = new List <MechComponentRef>(mechDef.Inventory);

            {
                // remove superfluous jump jets
                var maxJetCount = maxEngine.CoreDef.GetMovement(mechDef.Chassis.Tonnage).JumpJetCount;
                var jumpJetList = componentRefs.Where(x => x.ComponentDefType == ComponentType.JumpJet).ToList();
                for (var i = 0; i < jumpJetList.Count - maxJetCount; i++)
                {
                    componentRefs.Remove(jumpJetList[i]);
                }
            }

            var builder = new MechDefBuilder(mechDef.Chassis, componentRefs);

            // add engine
            builder.Add(
                maxEngine.CoreDef.Def,
                ChassisLocations.CenterTorso,
                engineHeatSinkdef != standardHeatSinkDef ? "/ihstype=" + engineHeatSinkdef.Def.Description.Id : null
                );

            // add standard shielding
            builder.Add(standardEngineType.Def, ChassisLocations.CenterTorso);

            // add free heatsinks
            {
                var count = 0;
                while (count < maxEngine.CoreDef.MaxFreeExternalHeatSinks)
                {
                    if (builder.Add(engineHeatSinkdef.Def))
                    {
                        count++;
                    }
                    else
                    {
                        break;
                    }
                }
            }

            mechDef.SetInventory(componentRefs.ToArray());
        }
        // return false so original function does not get called.
        public static bool Prefix(LanceLoadoutSlot __instance, GameObject ___initiativeObj, TextMeshProUGUI ___initiativeText,
                                  UIColorRefTracker ___initiativeColor, HBSTooltip ___initiativeTooltip, LanceConfiguratorPanel ___LC)
        {
            if (___initiativeObj == null || ___initiativeText == null || ___initiativeColor == null)
            {
                return(false);
            }
            if (__instance.SelectedMech == null || __instance.SelectedMech.MechDef == null || __instance.SelectedMech.MechDef.Chassis == null)
            {
                ___initiativeObj.SetActive(false);
                return(false);
            }
            if (__instance.SelectedPilot == null || __instance.SelectedPilot.Pilot == null || __instance.SelectedPilot.Pilot.pilotDef == null)
            {
                ___initiativeObj.SetActive(false);
                return(false);
            }
            ___initiativeObj.SetActive(true);

            int num  = 1;            // default to assault phase
            int num2 = 0;            // default to no modification by effects

            float f_walkSpeed = __instance.SelectedMech.MechDef.Chassis.MovementCapDef.MaxWalkDistance;

            if (f_walkSpeed >= TheEngineInitiative.Settings.MechPhaseSpecialMinSpeed)
            {
                num = 5;                  //special phase
            }
            else if (f_walkSpeed >= TheEngineInitiative.Settings.MechPhaseLightMinSpeed)
            {
                num = 4;                  //light phase
            }
            else if (f_walkSpeed >= TheEngineInitiative.Settings.MechPhaseMediumMinSpeed)
            {
                num = 3;                  //medium phase
            }
            else if (f_walkSpeed >= TheEngineInitiative.Settings.MechPhaseHeavyMinSpeed)
            {
                num = 2;                  //heavy phase
            }
            // check if pilot mods initiative
            if (__instance.SelectedPilot.Pilot.pilotDef.AbilityDefs != null)
            {
                foreach (AbilityDef abilityDef in __instance.SelectedPilot.Pilot.pilotDef.AbilityDefs)
                {
                    foreach (EffectData effect in abilityDef.EffectData)
                    {
                        if (MechStatisticsRules.GetInitiativeModifierFromEffectData(effect, true, null) == 0)
                        {
                            num2 += MechStatisticsRules.GetInitiativeModifierFromEffectData(effect, false, null);
                        }
                    }
                }
            }
            // check if any of the mech's inventory changes initiative.
            if (__instance.SelectedMech.MechDef.Inventory != null)
            {
                foreach (MechComponentRef mechComponentRef in __instance.SelectedMech.MechDef.Inventory)
                {
                    if (mechComponentRef.Def != null && mechComponentRef.Def.statusEffects != null)
                    {
                        foreach (EffectData effect2 in mechComponentRef.Def.statusEffects)
                        {
                            if (MechStatisticsRules.GetInitiativeModifierFromEffectData(effect2, true, null) == 0)
                            {
                                num2 += MechStatisticsRules.GetInitiativeModifierFromEffectData(effect2, false, null);
                            }
                        }
                    }
                }
            }
            // is there a lance bonus?
            int num3 = 0;

            if (___LC != null)
            {
                num3 = ___LC.lanceInitiativeModifier;
            }
            num2 += num3;

            // make sure initiative is within the valid range.
            int num4 = Mathf.Clamp(num + num2, 1, 5);

            //set our text.
            ___initiativeText.SetText($"{num4}");
            if (___initiativeTooltip != null)
            {
                // build the tooltip.  Going to use the mech's name and tell where its speed puts it, initiative-wise.
                string tooltipTitle = $"{__instance.SelectedMech.MechDef.Name}";
                string tooltipText  = "A max walking speed of " + $"{f_walkSpeed}" + "m per turn means this mech moves in initiative phase " + $"{num}" + ".";

                // if there are effects, tell the player what they've changed initiative to.
                if (num2 != 0)
                {
                    tooltipText += " Effects have modified this to phase " + $"{num4}" + ".";
                }
                BaseDescriptionDef initiativeData = new BaseDescriptionDef("MB_MIW_MECH_TT", tooltipTitle, tooltipText, null);
                ___initiativeTooltip.enabled = true;
                ___initiativeTooltip.SetDefaultStateData(TooltipUtilities.GetStateDataFromObject(initiativeData));
            }
            // if we've been bumped up, make it gold, if bumped down, make it red, else white.
            ___initiativeColor.SetUIColor((num2 > 0) ? UIColor.Gold : ((num2 < 0) ? UIColor.Red : UIColor.White));

            return(false);
        }
Beispiel #13
0
        public void ModifyMech(MechDef mDef, SimGameState s, UpgradeList ulist, ref float _, List <string[]> changedAmmoTypes, MechDef fromData)
        {
            float tonnage = 0;
            float max     = 0;

            MechStatisticsRules.CalculateTonnage(mDef, ref tonnage, ref max);
            float armorfact = mDef.GetMechArmorPointFactor();

            BTRandomMechComponentUpgrader_Init.Log.Log($"correcting tonnage 2: armor (each armor point costs {armorfact} t)");
            while (tonnage + armorfact <= mDef.Chassis.Tonnage)
            {
                bool assOne = false;
                foreach (ChassisLocations c in RMCU_Helper.Locations)
                {
                    if (tonnage + armorfact >= mDef.Chassis.Tonnage)
                    {
                        break;
                    }
                    LocationLoadoutDef l = mDef.GetLocationLoadoutDef(c);
                    if (l.AssignedArmor >= mDef.GetChassisLocationDef(c).MaxArmor)
                    {
                        continue;
                    }
                    l.AssignedArmor += 1;
                    l.CurrentArmor  += 1;
                    tonnage         += armorfact;
                    BTRandomMechComponentUpgrader_Init.Log.Log($"increased {c} armor to {l.AssignedArmor}");
                    assOne = true;
                }
                foreach (ChassisLocations c in RMCU_Helper.RearArmoredLocs)
                {
                    if (tonnage + armorfact >= mDef.Chassis.Tonnage)
                    {
                        break;
                    }
                    LocationLoadoutDef l = mDef.GetLocationLoadoutDef(c);
                    if (l.AssignedRearArmor >= mDef.GetChassisLocationDef(c).MaxRearArmor)
                    {
                        continue;
                    }
                    l.AssignedRearArmor += 1;
                    l.CurrentRearArmor  += 1;
                    tonnage             += armorfact;
                    BTRandomMechComponentUpgrader_Init.Log.Log($"increased {c} rear armor to {l.AssignedRearArmor}");
                    assOne = true;
                }
                if (!assOne)
                {
                    BTRandomMechComponentUpgrader_Init.Log.Log("no free armor location found!");
                    break;
                }
            }
            while (tonnage > mDef.Chassis.Tonnage)
            {
                bool assOne = false;
                foreach (ChassisLocations c in RMCU_Helper.Locations)
                {
                    if (tonnage <= mDef.Chassis.Tonnage)
                    {
                        break;
                    }
                    LocationLoadoutDef l = mDef.GetLocationLoadoutDef(c);
                    if (l.AssignedArmor <= 1)
                    {
                        continue;
                    }
                    l.AssignedArmor -= 1;
                    l.CurrentArmor  -= 1;
                    tonnage         -= armorfact;
                    BTRandomMechComponentUpgrader_Init.Log.Log($"decreased {c} armor to {l.AssignedArmor}");
                    assOne = true;
                }
                foreach (ChassisLocations c in RMCU_Helper.RearArmoredLocs)
                {
                    if (tonnage <= mDef.Chassis.Tonnage)
                    {
                        break;
                    }
                    LocationLoadoutDef l = mDef.GetLocationLoadoutDef(c);
                    if (l.AssignedRearArmor <= 1)
                    {
                        continue;
                    }
                    l.AssignedRearArmor -= 1;
                    l.CurrentRearArmor  -= 1;
                    tonnage             -= armorfact;
                    BTRandomMechComponentUpgrader_Init.Log.Log($"decreased {c} rear armor to {l.AssignedRearArmor}");
                    assOne = true;
                }
                if (!assOne)
                {
                    BTRandomMechComponentUpgrader_Init.Log.Log("no free armor location found!");
                    break;
                }
            }

            BTRandomMechComponentUpgrader_Init.Log.Log($"final weight: {tonnage}/{mDef.Chassis.Tonnage}");
        }
Beispiel #14
0
        public void AutoFixMechDef(MechDef mechDef)
        {
            if (!AutoFixerFeature.settings.MechDefEngine)
            {
                return;
            }

            //DumpAllAsTable();
            if (mechDef.Inventory.Any(c => c.Def.GetComponent <EngineCoreDef>() != null))
            {
                return;
            }

            Control.mod.Logger.Log($"Auto fixing mechDef={mechDef.Description.Id} chassisDef={mechDef.Chassis.Description.Id}");

            ArmorStructureRatioFeature.Shared.AutoFixMechDef(mechDef);

            var builder             = new MechDefBuilder(mechDef.Chassis, mechDef.Inventory.ToList());
            var standardHeatSinkDef = mechDef.DataManager.GetDefaultEngineHeatSinkDef();
            var engineHeatSinkDef   = builder.Inventory
                                      .Select(r => r.Def.GetComponent <CoolingDef>())
                                      .Where(d => d != null)
                                      .Select(d => mechDef.DataManager.HeatSinkDefs.Get(d.HeatSinkDefId))
                                      .Where(d => d != null)
                                      .Select(d => d.GetComponent <EngineHeatSinkDef>())
                                      .FirstOrDefault() ?? standardHeatSinkDef;

            float freeTonnage;
            {
                float currentTotalTonnage = 0, maxValue = 0;
                MechStatisticsRules.CalculateTonnage(mechDef, ref currentTotalTonnage, ref maxValue);
                var maxFreeTonnage = mechDef.Chassis.Tonnage - currentTotalTonnage;

                var initialTonnage         = mechDef.Chassis.InitialTonnage;
                var originalInitialTonnage = ChassisHandler.GetOriginalInitialTonnage(mechDef.Chassis) ?? initialTonnage;
                var initialTonnageGain     = Mathf.Max(0, originalInitialTonnage - initialTonnage);
                if (AutoFixerFeature.settings.MechDefAutoFixAgainstMaxFreeTonnage.Contains(mechDef.Description.Id))
                {
                    freeTonnage = maxFreeTonnage;
                }
                else
                {
                    var freeTonnageThreshold = AutoFixerFeature.settings.MechDefAutoFixInitialTonnageDiffThreshold;
                    freeTonnage = Mathf.Min(maxFreeTonnage, initialTonnageGain + freeTonnageThreshold);
                }

                Control.mod.Logger.LogDebug($"freeTonnage={freeTonnage}" +
                                            $" currentTotalTonnage={currentTotalTonnage}" +
                                            $" maxFreeTonnage={maxFreeTonnage}" +
                                            $" initialTonnageGain={initialTonnageGain}" +
                                            $" initialGainSmaller={initialTonnageGain < maxFreeTonnage}");
            }

            //Control.mod.Logger.LogDebug("C maxEngineTonnage=" + maxEngineTonnage);
            var standardWeights   = new Weights(); // use default gyro and weights
            var standardHeatBlock = mechDef.DataManager.HeatSinkDefs.Get(AutoFixerFeature.settings.MechDefHeatBlockDef).GetComponent <EngineHeatBlockDef>();
            var standardCooling   = mechDef.DataManager.HeatSinkDefs.Get(AutoFixerFeature.settings.MechDefCoolingDef).GetComponent <CoolingDef>();

            var engineCoreDefs = mechDef.DataManager.HeatSinkDefs
                                 .Select(hs => hs.Value)
                                 .Select(hs => hs.GetComponent <EngineCoreDef>())
                                 .Where(c => c != null)
                                 .OrderByDescending(x => x.Rating);

            Engine maxEngine = null;

            {
                //var heatSinks = builder.Inventory.Where(x => x.ComponentDefType == ComponentType.HeatSink && x.Def.Is<EngineHeatSinkDef>()).ToList();
                var jumpJetList = builder.Inventory.Where(x => x.ComponentDefType == ComponentType.JumpJet).ToList();
                var engines     = new LinkedList <Engine>();

                foreach (var coreDef in engineCoreDefs)
                {
                    {
                        var engine = new Engine(standardCooling, standardHeatBlock, coreDef, standardWeights, new List <MechComponentRef>());
                        engines.AddFirst(engine);
                    }

                    {
                        // remove superfluous jump jets
                        var maxJetCount = coreDef.GetMovement(mechDef.Chassis.Tonnage).JumpJetCount;
                        //Control.mod.Logger.LogDebug($"before Inventory.Count={builder.Inventory.Count} jumpJetList.Count={jumpJetList.Count} maxJetCount={maxJetCount}");
                        while (jumpJetList.Count > maxJetCount)
                        {
                            var lastIndex = jumpJetList.Count - 1;
                            var jumpJet   = jumpJetList[lastIndex];
                            freeTonnage += jumpJet.Def.Tonnage;
                            builder.Remove(jumpJet);
                            jumpJetList.Remove(jumpJet);
                        }
                        //Control.mod.Logger.LogDebug($"after Inventory.Count={builder.Inventory.Count} jumpJetList.Count={jumpJetList.Count} maxJetCount={maxJetCount}");
                    }

                    foreach (var engine in engines)
                    {
//                        Control.mod.Logger.LogDebug($"D engine={engine.CoreDef} engine.TotalTonnage={engine.TotalTonnage} freeTonnage={freeTonnage}");

                        if (engine.TotalTonnage <= freeTonnage)
                        {
                            maxEngine = engine;
                        }
                        else
                        {
                            break;
                        }
                    }
                    if (maxEngine != null)
                    {
                        break;
                    }
                }
            }

            if (maxEngine == null)
            {
                return;
            }

            Control.mod.Logger.LogDebug($" maxEngine={maxEngine.CoreDef} freeTonnage={freeTonnage}");
            {
                var dummyCore = builder.Inventory.FirstOrDefault(r => r.ComponentDefID == AutoFixerFeature.settings.MechDefCoreDummy);
                if (dummyCore != null)
                {
                    builder.Remove(dummyCore);
                }
            }

            // add engine
            builder.Add(maxEngine.CoreDef.Def, ChassisLocations.CenterTorso, true);

            if (!EngineFeature.settings.AllowMixingHeatSinkTypes)
            {
                // remove incompatible heat sinks
                var incompatibleHeatSinks = builder.Inventory
                                            .Where(r => r.Def.Is <EngineHeatSinkDef>(out var hs) && hs.HSCategory != engineHeatSinkDef.HSCategory)
                                            .ToList();
                foreach (var incompatibleHeatSink in incompatibleHeatSinks)
                {
                    builder.Remove(incompatibleHeatSink);
                }

                //Control.mod.Logger.LogDebug($"Inventory.Count={builder.Inventory.Count} incompatibleHeatSinks.Count={incompatibleHeatSinks.Count}");
                // add same amount of compatible heat sinks
                foreach (var unused in incompatibleHeatSinks)
                {
                    builder.Add(engineHeatSinkDef.Def);
                }

                //Control.mod.Logger.LogDebug($"Inventory.Count={builder.Inventory.Count}");
            }

            // add free heatsinks
            {
                //var maxFree = maxEngine.CoreDef.ExternalHeatSinksFreeMaxCount;
                //var current = maxEngine.ExternalHeatSinkCount;
                var maxFree = maxEngine.HeatSinkExternalFreeMaxCount;
                var current = 0; //we assume exiting heatsinks on the mech are additional and not free
                for (var i = current; i < maxFree; i++)
                {
                    if (!builder.Add(engineHeatSinkDef.Def))
                    {
                        break;
                    }
                }
                //Control.mod.Logger.LogDebug($"Inventory.Count={builder.Inventory.Count} maxFree={maxFree}");
            }

            // find any overused location
            if (builder.HasOveruseAtAnyLocation())
            {
                // heatsinks, upgrades
                var itemsToBeReordered = builder.Inventory
                                         .Where(IsReorderable)
                                         .OrderBy(c => MechDefBuilder.LocationCount(c.Def.AllowedLocations))
                                         .ThenByDescending(c => c.Def.InventorySize)
                                         .ThenByDescending(c =>
                {
                    switch (c.ComponentDefType)
                    {
                    case ComponentType.Upgrade:
                        return(2);

                    case ComponentType.AmmunitionBox:
                        return(1);

                    default:
                        return(0);
                    }
                })
                                         .ToList();

                // remove all items that can be reordered: heatsinks, upgrades
                foreach (var item in itemsToBeReordered)
                {
                    builder.Remove(item);
                }

                // then add most restricting, and then largest items first (probably double head sinks)
                foreach (var item in itemsToBeReordered)
                {
                    // couldn't add everything
                    if (!builder.Add(item.Def))
                    {
                        return;
                    }
                }
            }

            mechDef.SetInventory(builder.Inventory.OrderBy(element => element, new OrderComparer()).ToArray());

            //{
            //    float currentTotalTonnage = 0, maxValue = 0;
            //    MechStatisticsRules.CalculateTonnage(mechDef, ref currentTotalTonnage, ref maxValue);
            //    Control.mod.Logger.LogDebug($" end currentTotalTonnage={currentTotalTonnage} mechDef.Chassis.Tonnage={mechDef.Chassis.Tonnage}");
            //}
        }