// 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); }
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); } } }
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); }
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()); }
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); }
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()); }
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); }
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}"); }
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}"); //} }