/* Submits a Mech Lab Work Order to the game's Mech Lab queue to actually be processed */ public static void SubmitWorkOrder(SimGameState simGame, WorkOrderEntry_MechLab newMechLabWorkOrder) { try { Logger.LogDebug("Begin SubmitWorkOrder(): "); // Now that all WO subentries are added, insert the base MechLab work order to the game's Mech Lab Work Order Queue as the highest priority (index 0) simGame.MechLabQueue.Insert(0, newMechLabWorkOrder); // Call this to properly Initialize the base Mech Lab WO and make it visible in the Mech Lab queue UI / timeline etc. simGame.InitializeMechLabEntry(newMechLabWorkOrder, newMechLabWorkOrder.GetCBillCost()); // Force an update of the Mech Lab queue, false is to tell it a day isn't passing. simGame.UpdateMechLabWorkQueue(false); // Simple cost debugging for the log foreach (WorkOrderEntry subEntries in newMechLabWorkOrder.SubEntries) { Logger.LogDebug(subEntries.Description + " Repair Tech Costs: " + subEntries.GetCost()); } Logger.LogInfo(simGame.GetMechByID(newMechLabWorkOrder.MechID).Name + " Repair Costs are - techCost: " + newMechLabWorkOrder.GetCost() + " | CBill Cost: " + newMechLabWorkOrder.GetCBillCost()); Logger.LogInfo("====================================================================="); // Deduct the total CBill costs of the WO from player inventory. For some reason this isn't done automatically via the HBS WO system. simGame.AddFunds(-newMechLabWorkOrder.GetCBillCost(), "ArmorRepair", true); } catch (Exception ex) { Logger.LogError(ex); } }
/* Submits a Mech Lab Work Order to our temporary queue, it will be held there until the player decides whether to let Yang run the repairs or not.*/ public static void SubmitTempWorkOrder(SimGameState simGame, WorkOrderEntry_MechLab newMechLabWorkOrder, MechDef mech) { try { Logger.LogDebug("Adding base MechLab WO to temporary queue."); Globals.tempMechLabQueue.Add(newMechLabWorkOrder); Logger.LogDebug("Number of work orders in temp queue: " + Globals.tempMechLabQueue.Count.ToString()); } catch (Exception ex) { Logger.LogError(ex); } }
private void ApplyCosts(WorkOrderEntry_MechLab workOrder, WorkOrderCosts.Costs costs, Dictionary <string, string> variables = null) { if (costs == null) { return; } var adapter = new WorkOrderEntry_MechLabAdapter(workOrder); if (!string.IsNullOrEmpty(costs.CBillCost)) { adapter.CBillCost = Convert.ToInt32(FormulaEvaluator.Shared.Evaluate(costs.CBillCost, variables)); } if (!string.IsNullOrEmpty(costs.TechCost)) { adapter.Cost = Convert.ToInt32(FormulaEvaluator.Shared.Evaluate(costs.TechCost, variables)); } }
public static void Postfix(SimGameState __instance, WorkOrderEntry entry) { if (!Core.Settings.RepairRearm) { return; } if (entry.Type == WorkOrderType.MechLabModifyArmor) { WorkOrderEntry_MechLab workOrderEntry_MechLab = entry as WorkOrderEntry_MechLab; MechDef mechBayID = __instance.GetMechByID(workOrderEntry_MechLab.MechID); Logger.LogDebug("CancelWorkOrder"); if (mechBayID.MechTags.Contains("XLRP_Armor_Repairing")) { mechBayID.MechTags.Remove("XLRP_Armor_Repairing"); } } }
public static void Postfix(SimGameState __instance, WorkOrderEntry entry) { if (!Core.Settings.RepairRearm) { return; } if (entry.Type == WorkOrderType.MechLabModifyArmor) { Logger.LogDebug("CompleteWorkOrder"); WorkOrderEntry_MechLab workOrderEntry_MechLab = entry as WorkOrderEntry_MechLab; MechDef mechBayID = __instance.GetMechByID(workOrderEntry_MechLab.MechLabParent.MechID); if (mechBayID.MechTags.Contains("XLRP_Armor_Repairing")) { mechBayID.MechTags.Remove("XLRP_Armor_Repairing"); mechBayID.MechTags.Remove("XLRP_R&R"); mechBayID.MechTags.Where(tag => tag.StartsWith("XLRPArmor")).Do(x => mechBayID.MechTags.Remove(x)); } } }
// Run after completion of contracts and queue up any orders in the temp queue into the game's Mech Lab queue public static void Postfix(SimGameState __instance) { try { // If there are any work orders in the temporary queue, prompt the player if (Globals.tempMechLabQueue.Count > 0) { Logger.LogDebug("Processing temp Mech Lab queue orders."); int cbills = 0; int techCost = 0; int mechRepairCount = 0; int skipMechCount = 0; string mechRepairCountDisplayed = String.Empty; string skipMechCountDisplayed = String.Empty; string skipMechMessage = String.Empty; string finalMessage = String.Empty; // If player has disabled auto repairing mechs with destroyed components, check for them and remove them from the temp queue before continuing if (!ArmorRepair.ModSettings.AutoRepairMechsWithDestroyedComponents) { for (int index = 0; index < Globals.tempMechLabQueue.Count; index++) { WorkOrderEntry_MechLab order = Globals.tempMechLabQueue[index]; Logger.LogDebug("Checking for destroyed components."); bool destroyedComponents = false; MechDef mech = __instance.GetMechByID(order.MechID); destroyedComponents = Helpers.CheckDestroyedComponents(mech); if (destroyedComponents) { // Remove this work order from the temp mech lab queue if the mech has destroyed components and move to next iteration Logger.LogDebug("Removing " + mech.Name + " order from temp queue due to destroyed components and mod settings."); Globals.tempMechLabQueue.Remove(order); destroyedComponents = false; skipMechCount++; index++; } } } Logger.LogDebug("Temp Queue has " + Globals.tempMechLabQueue.Count + " entries."); // Calculate summary of total repair costs from the temp work order queue for (int index = 0; index < Globals.tempMechLabQueue.Count; index++) { WorkOrderEntry_MechLab order = Globals.tempMechLabQueue[index]; MechDef mech = __instance.GetMechByID(order.MechID); Logger.LogDebug("Adding " + mech.Name + " to RepairCount."); cbills += order.GetCBillCost(); techCost += order.GetCost(); mechRepairCount++; } mechRepairCount = Mathf.Clamp(mechRepairCount, 0, 4); Logger.LogDebug("Temp Queue has " + Globals.tempMechLabQueue.Count + " work order entries."); // If Yang's Auto Repair prompt is enabled, build a message prompt dialog for the player if (ArmorRepair.ModSettings.EnableAutoRepairPrompt) { // Calculate a friendly techCost of the work order in days, based on number of current mechtechs in the player's game. if (techCost != 0 && __instance.MechTechSkill != 0) { techCost = Mathf.CeilToInt((float)techCost / (float)__instance.MechTechSkill); } else { techCost = 1; // Safety in case of weird div/0 } // Generate a quick friendly description of how many mechs were damaged in battle switch (mechRepairCount) { case 0: { Logger.LogDebug("mechRepairCount was 0."); break; } case 1: { mechRepairCountDisplayed = "one of our 'Mechs was"; break; } case 2: { mechRepairCountDisplayed = "a couple of the 'Mechs were"; break; } case 3: { mechRepairCountDisplayed = "three of our 'Mechs were"; break; } case 4: { mechRepairCountDisplayed = "our whole lance was"; break; } } // Generate a friendly description of how many mechs were damaged but had components destroyed switch (skipMechCount) { case 0: { Logger.LogDebug("skipMechCount was 0."); break; } case 1: { skipMechCountDisplayed = "one of the 'Mechs is damaged but has"; break; } case 2: { skipMechCountDisplayed = "two of the 'Mechs are damaged but have"; break; } case 3: { skipMechCountDisplayed = "three of the 'Mechs are damaged but have "; break; } case 4: { skipMechCountDisplayed = "the whole lance is damaged but has"; break; } } // Check if there are any mechs to process if (mechRepairCount > 0 || skipMechCount > 0) { Logger.LogDebug("mechRepairCount is " + mechRepairCount + " skipMechCount is " + skipMechCount); // Setup the notification for mechs with damaged components that we might want to skip if (skipMechCount > 0 && mechRepairCount == 0) { skipMechMessage = String.Format("{0} destroyed components. I'll leave the repairs for you to review.", skipMechCountDisplayed); } else { skipMechMessage = String.Format("{0} destroyed components, so I'll leave those repairs to you.", skipMechCountDisplayed); } Logger.LogDebug("Firing Yang's UI notification."); SimGameInterruptManager notificationQueue = __instance.GetInterruptQueue(); // If all of the mechs needing repairs have damaged components and should be skipped from auto-repair, change the message notification structure to make more sense (e.g. just have an OK button) if (skipMechCount > 0 && mechRepairCount == 0) { finalMessage = String.Format( "Boss, {0} \n\n", skipMechMessage ); // Queue Notification notificationQueue.QueuePauseNotification( "'Mech Repairs Needed", finalMessage, __instance.GetCrewPortrait(SimGameCrew.Crew_Yang), string.Empty, delegate { Logger.LogDebug("[PROMPT] All damaged mechs had destroyed components and won't be queued for repair."); }, "OK" ); } else { if (skipMechCount > 0) { finalMessage = String.Format( "Boss, {0} damaged. It'll cost <color=#DE6729>{1}{2:n0}</color> and {3} days for these repairs. Want my crew to get started?\n\nAlso, {4}\n\n", mechRepairCountDisplayed, '¢', cbills.ToString(), techCost.ToString(), skipMechMessage ); } else { finalMessage = String.Format( "Boss, {0} damaged on the last engagement. It'll cost <color=#DE6729>{1}{2:n0}</color> and {3} days for the repairs.\n\nWant my crew to get started?", mechRepairCountDisplayed, '¢', cbills.ToString(), techCost.ToString() ); } // Queue up Yang's notification notificationQueue.QueuePauseNotification( "'Mech Repairs Needed", finalMessage, __instance.GetCrewPortrait(SimGameCrew.Crew_Yang), string.Empty, delegate { Logger.LogDebug("[PROMPT] Moving work orders from temp queue to Mech Lab queue: " + Globals.tempMechLabQueue.Count + " work orders"); foreach (WorkOrderEntry_MechLab workOrder in Globals.tempMechLabQueue.ToList()) { Logger.LogInfo("[PROMPT] Moving work order from temp queue to Mech Lab queue: " + workOrder.Description + " - " + workOrder.GetCBillCost()); Helpers.SubmitWorkOrder(__instance, workOrder); Globals.tempMechLabQueue.Remove(workOrder); } }, "Yes", delegate { Logger.LogInfo("[PROMPT] Discarding work orders from temp queue: " + Globals.tempMechLabQueue.Count + " work orders"); foreach (WorkOrderEntry_MechLab workOrder in Globals.tempMechLabQueue.ToList()) { Logger.LogInfo("[PROMPT] Discarding work order from temp queue: " + workOrder.Description + " - " + workOrder.GetCBillCost()); Globals.tempMechLabQueue.Remove(workOrder); } }, "No" ); } } } else // If Auto Repair prompt is not enabled, just proceed with queuing the remaining temp queue work orders and don't notify the player { foreach (WorkOrderEntry_MechLab workOrder in Globals.tempMechLabQueue.ToList()) { Logger.LogInfo("[AUTO] Moving work order from temp queue to Mech Lab queue: " + workOrder.Description + " - " + workOrder.GetCBillCost()); Helpers.SubmitWorkOrder(__instance, workOrder); Globals.tempMechLabQueue.Remove(workOrder); } } } } catch (Exception ex) { Globals.tempMechLabQueue.Clear(); Logger.LogError(ex); } }
public static bool Prefix(MechBayPanel __instance, MechBayMechUnitElement mechElement) { Logger.LogDebug("We are repairing, yes?"); Logger.Log("We are repairing, yes?"); if (!Core.Settings.RepairRearm) { return(true); } var sim = UnityGameInstance.BattleTechGame.Simulation; MechDef mechDef = mechElement.MechDef; WorkOrderEntry_MechLab workOrderEntry_MechLab = __instance.Sim.GetWorkOrderEntryForMech(mechDef); bool flag = false; for (int i = 0; i < mechDef.Inventory.Length; i++) { MechComponentRef mechComponentRef = mechDef.Inventory[i]; if (mechComponentRef.DamageLevel != ComponentDamageLevel.Functional && mechComponentRef.DamageLevel != ComponentDamageLevel.Installing && !MechValidationRules.MechComponentUnderMaintenance(mechComponentRef, MechValidationLevel.MechLab, workOrderEntry_MechLab)) { flag = true; break; } } if (!mechDef.IsDamaged && !flag) { return(false); } List <ChassisLocations> list = new List <ChassisLocations>(); __instance.pendingWorkOrderNew = false; __instance.pendingWorkOrderEntriesToAdd.Clear(); if (workOrderEntry_MechLab == null) { workOrderEntry_MechLab = new WorkOrderEntry_MechLab(WorkOrderType.MechLabGeneric, "MechLab-BaseWorkOrder", Strings.T("Modify 'Mech - {0}", new object[] { mechDef.Description.Name }), mechDef.GUID, 0, Strings.T(__instance.Sim.Constants.Story.GeneralMechWorkOrderCompletedText, new object[] { mechDef.Description.Name })); workOrderEntry_MechLab.SetMechDef(mechDef); __instance.pendingWorkOrderNew = true; } __instance.pendingWorkOrder = workOrderEntry_MechLab; if (!MechValidationRules.MechStructureUnderMaintenance(ChassisLocations.Head, MechValidationLevel.MechLab, workOrderEntry_MechLab) && mechDef.Head.CurrentInternalStructure < mechDef.Chassis.Head.InternalStructure) { list.Add(ChassisLocations.Head); } if (!MechValidationRules.MechStructureUnderMaintenance(ChassisLocations.CenterTorso, MechValidationLevel.MechLab, workOrderEntry_MechLab) && mechDef.CenterTorso.CurrentInternalStructure < mechDef.Chassis.CenterTorso.InternalStructure) { list.Add(ChassisLocations.CenterTorso); } if (!MechValidationRules.MechStructureUnderMaintenance(ChassisLocations.LeftTorso, MechValidationLevel.MechLab, workOrderEntry_MechLab) && mechDef.LeftTorso.CurrentInternalStructure < mechDef.Chassis.LeftTorso.InternalStructure) { list.Add(ChassisLocations.LeftTorso); } if (!MechValidationRules.MechStructureUnderMaintenance(ChassisLocations.RightTorso, MechValidationLevel.MechLab, workOrderEntry_MechLab) && mechDef.RightTorso.CurrentInternalStructure < mechDef.Chassis.RightTorso.InternalStructure) { list.Add(ChassisLocations.RightTorso); } if (!MechValidationRules.MechStructureUnderMaintenance(ChassisLocations.LeftLeg, MechValidationLevel.MechLab, workOrderEntry_MechLab) && mechDef.LeftLeg.CurrentInternalStructure < mechDef.Chassis.LeftLeg.InternalStructure) { list.Add(ChassisLocations.LeftLeg); } if (!MechValidationRules.MechStructureUnderMaintenance(ChassisLocations.RightLeg, MechValidationLevel.MechLab, workOrderEntry_MechLab) && mechDef.RightLeg.CurrentInternalStructure < mechDef.Chassis.RightLeg.InternalStructure) { list.Add(ChassisLocations.RightLeg); } if (!MechValidationRules.MechStructureUnderMaintenance(ChassisLocations.LeftArm, MechValidationLevel.MechLab, workOrderEntry_MechLab) && mechDef.LeftArm.CurrentInternalStructure < mechDef.Chassis.LeftArm.InternalStructure) { list.Add(ChassisLocations.LeftArm); } if (!MechValidationRules.MechStructureUnderMaintenance(ChassisLocations.RightArm, MechValidationLevel.MechLab, workOrderEntry_MechLab) && mechDef.RightArm.CurrentInternalStructure < mechDef.Chassis.RightArm.InternalStructure) { list.Add(ChassisLocations.RightArm); } if (list.Count < 1 && !flag) { GenericPopupBuilder.Create("Repair Already Ordered", string.Format("A repair order has already been queued for " + "{0}", mechDef.Name)).AddFader(new UIColorRef?(LazySingletonBehavior <UIManager> .Instance.UILookAndColorConstants.PopupBackfill), 0f, true).Render(); __instance.OnRepairAllCancelled(); return(false); } int num = 0; int num2 = 0; for (int j = 0; j < list.Count; j++) { LocationDef chassisLocationDef = mechDef.GetChassisLocationDef(list[j]); LocationLoadoutDef locationLoadoutDef = mechDef.GetLocationLoadoutDef(list[j]); int structureCount = Mathf.RoundToInt(Mathf.Max(0f, chassisLocationDef.InternalStructure - locationLoadoutDef.CurrentInternalStructure)); WorkOrderEntry_RepairMechStructure workOrderEntry_RepairMechStructure = __instance.Sim.CreateMechRepairWorkOrder(mechDef.GUID, list[j], structureCount); __instance.pendingWorkOrderEntriesToAdd.Add(workOrderEntry_RepairMechStructure); num += workOrderEntry_RepairMechStructure.GetCost(); num2 += workOrderEntry_RepairMechStructure.GetCBillCost(); } StringBuilder stringBuilder = new StringBuilder(); int num3 = 0; for (int k = 0; k < mechDef.Inventory.Length; k++) { MechComponentRef mechComponentRef2 = mechDef.Inventory[k]; if (string.IsNullOrEmpty(mechComponentRef2.SimGameUID)) { mechComponentRef2.SetSimGameUID(__instance.Sim.GenerateSimGameUID()); } if (mechComponentRef2.DamageLevel == ComponentDamageLevel.Destroyed) { if (num3 < 1) { stringBuilder.Append("\n\nThe following components have been Destroyed. If you continue with the Repair, " + "replacement Components will NOT be installed. If you want to replace them with identical or " + "different Components, you must Refit the 'Mech.\n\n"); } if (num3 < 5) { stringBuilder.Append(mechComponentRef2.MountedLocation.ToString()); stringBuilder.Append(": "); stringBuilder.Append(mechComponentRef2.Def.Description.Name); stringBuilder.Append("\n"); } num3++; WorkOrderEntry_InstallComponent workOrderEntry_InstallComponent = sim.CreateComponentInstallWorkOrder(__instance.selectedMech.MechDef.GUID, mechComponentRef2, ChassisLocations.None, mechComponentRef2.MountedLocation); __instance.pendingWorkOrderEntriesToAdd.Insert(0, workOrderEntry_InstallComponent); num += workOrderEntry_InstallComponent.GetCost(); num2 += workOrderEntry_InstallComponent.GetCBillCost(); } else if (mechComponentRef2.DamageLevel != ComponentDamageLevel.Functional && mechComponentRef2.DamageLevel != ComponentDamageLevel.Installing) { WorkOrderEntry_RepairComponent workOrderEntry_RepairComponent = __instance.Sim.CreateComponentRepairWorkOrder(mechComponentRef2, true); __instance.pendingWorkOrderEntriesToAdd.Add(workOrderEntry_RepairComponent); num += workOrderEntry_RepairComponent.GetCost(); num2 += workOrderEntry_RepairComponent.GetCBillCost(); } } foreach (var foo in __instance.pendingWorkOrderEntriesToAdd) { Logger.LogDebug(foo.ID); } Logger.LogDebug("Armor Repair Section"); float armorLoss = 1; bool armorTag = false; foreach (var tag in mechDef.MechTags) { Logger.LogDebug(tag); if (tag.StartsWith("XLRPArmor")) { armorTag = true; string[] parsedString = tag.Split('_'); armorLoss = float.Parse(parsedString[1]); } Logger.LogDebug(armorLoss.ToString()); } if (armorTag) { if (!mechDef.MechTags.Contains("XLRP_Armor_Repairing")) { mechDef.MechTags.Add("XLRP_Armor_Repairing"); } int brokenArmor = (int)((1 - armorLoss) * mechDef.MechDefAssignedArmor); int frontArmor = (int)(mechDef.MechDefAssignedArmor - mechDef.CenterTorso.AssignedRearArmor - mechDef.LeftTorso.AssignedRearArmor - mechDef.RightTorso.AssignedRearArmor); int rearArmor = (int)(mechDef.CenterTorso.AssignedRearArmor + mechDef.LeftTorso.AssignedRearArmor + mechDef.RightTorso.AssignedRearArmor); Logger.LogDebug($"brokenAmor: {brokenArmor}, frontArmor: {frontArmor}, rearArmor: {rearArmor}"); WorkOrderEntry_ModifyMechArmor subEntry = sim.CreateMechArmorModifyWorkOrder(__instance.selectedMech.MechDef.GUID, ChassisLocations.All, brokenArmor, frontArmor, rearArmor); __instance.pendingWorkOrderEntriesToAdd.Add(subEntry); num += subEntry.GetCost(); num2 += subEntry.GetCBillCost(); } num = Mathf.Max(1, Mathf.CeilToInt((float)num / (float)__instance.Sim.MechTechSkill)); if (num3 > 5) { stringBuilder.Append(Strings.T("...\nAnd {0} additional destroyed components.\n", new object[] { num3 - 5 })); } string body = Strings.T("Repairing {0} will cost {1:n0} C-Bills and take {2} Days.{3}\n\nProceed?", new object[] { mechDef.Name, num2, num, stringBuilder.ToString() }); GenericPopupBuilder.Create("Repair 'Mech?", body).AddButton("Cancel", new Action(__instance.OnRepairAllCancelled), true, null). AddButton("Repair", new Action(__instance.OnRepairAllAccepted), true, null).AddFader(new UIColorRef?(LazySingletonBehavior <UIManager> .Instance.UILookAndColorConstants.PopupBackfill), 0f, true).Render(); return(false); }
internal static void Validate(List <MechDef> mechdefs) { foreach (var mechDef in mechdefs) { try { var dm = UnityGameInstance.BattleTechGame.DataManager; var work = new WorkOrderEntry_MechLab(WorkOrderType.MechLabGeneric, "test", "test", "test", 0); if (mechDef == null) { Control.LogError("NullMECHDEF!"); continue; } if (Control.Settings.IgnoreValidationTags != null && Control.Settings.IgnoreValidationTags.Length > 0) { bool skip = false; foreach (var tag in Control.Settings.IgnoreValidationTags) { if ((mechDef.Chassis.ChassisTags != null && mechDef.Chassis.ChassisTags.Contains(tag)) || (mechDef.MechTags != null && mechDef.MechTags.Contains(tag))) { Control.LogDebug(DType.AutofixValidate, $"{mechDef.Description.Id} Ignored by {tag}"); skip = true; break; } } if (skip) { continue; } } var error = MechValidationRules.ValidateMechDef(MechValidationLevel.Full, dm, mechDef, work); foreach (var component in mechDef.Inventory) { foreach (var validator in component.GetComponents <IMechValidate>()) { validator.ValidateMech(error, MechValidationLevel.MechLab, mechDef, component); } } bool bad_mech = false; foreach (var pair in error) { if (pair.Value.Count > 0) { if (!bad_mech) { bad_mech = true; Control.LogDebug(DType.AutofixValidate, $"{mechDef.Description.Id} has errors:"); } foreach (var text in pair.Value) { Control.LogDebug(DType.AutofixValidate, $"[{pair.Key}]:{text}"); } } } if (!bad_mech && !Control.Settings.DEBUG_ShowOnlyErrors) { Control.LogDebug(DType.AutofixValidate, $"{mechDef.Description.Id} no errors"); } } catch (Exception e) { Control.LogError($"{mechDef.Description.Id} throwed exception on validation", e); } } }
public static bool Prefix(SimGameState __instance, MechDef mech) { try { // Start of analysing a mech for armor repair Logger.LogInfo("Analysing Mech: " + mech.Name); Logger.LogInfo("============================================"); // Base generic MechLab WO for a mech that requires armor or structure repair - each individual locational subentry WO has to be added to this base WO later WorkOrderEntry_MechLab newMechLabWorkOrder = null; /* STRUCTURE DAMAGE CHECKS * ------------------------ * Check if the given mech needs any structure repaired and that EnableStructureRepair is true in the mod settings * */ if (ArmorRepair.ModSettings.EnableStructureRepair) { if (Helpers.CheckStructureDamage(mech)) { Logger.LogDebug("SimGameConstant: StructureRepairTechPoints: " + __instance.Constants.MechLab.StructureRepairTechPoints); Logger.LogDebug("SimGameConstant: StructureRepairCost: " + __instance.Constants.MechLab.StructureRepairCost); // Loop over the ChassisLocations for repair in their highest -> lowest priority order from the dictionary defined in Helpers for (int index = 0; index < Globals.repairPriorities.Count; index++) { // Set current looped ChassisLocation ChassisLocations thisLoc = Globals.repairPriorities.ElementAt(index).Value; // Get current mech's loadout definition from the looped chassis location LocationLoadoutDef thisLocLoadout = mech.GetLocationLoadoutDef(thisLoc); // Friendly name for this location string thisLocName = thisLoc.ToString(); Logger.LogDebug("Analysing location: " + thisLocName); // Check if a new base MechLab order needs to be created or not if (newMechLabWorkOrder == null) { // Create new base work order of the generic MechLab type if it doesn't already exist newMechLabWorkOrder = Helpers.CreateBaseMechLabOrder(__instance, mech); } float currentStructure = thisLocLoadout.CurrentInternalStructure; float definedStructure = mech.GetChassisLocationDef(thisLoc).InternalStructure; // Only create work orders for repairing structure if this location has taken damage in combat if (currentStructure != definedStructure) { // Work out difference of structure lost for each location - default to 0 int structureDifference = 0; structureDifference = (int)Mathf.Abs(currentStructure - definedStructure); Logger.LogInfo("Total structure difference for " + thisLocName + " is " + structureDifference); Logger.LogInfo("Creating MechRepair work order entry for " + thisLocName); Logger.LogDebug("Calling CreateMechRepairWorkOrder with params - GUID: " + mech.GUID.ToString() + " | Location: " + thisLocName + " | structureDifference: " + structureDifference ); WorkOrderEntry_RepairMechStructure newRepairWorkOrder = __instance.CreateMechRepairWorkOrder( mech.GUID, thisLocLoadout.Location, structureDifference ); Logger.LogDebug("Adding WO subentry to repair missing " + thisLocName + " structure."); newMechLabWorkOrder.AddSubEntry(newRepairWorkOrder); } else { Logger.LogDebug("Structure repair not required for: " + thisLocName); } } } } /* COMPONENT DAMAGE CHECKS * ----------------------- * Check if the given mech needs any critted components repaired * * NOTE: Not yet working. Repair components are added to work order but not actually repaired after WO completes. Noticed there is another queue involved on SGS.WorkOrderComponents we need to debug. * Currently throws "SimGameState [ERROR] ML_RepairComponent MechBay - RepairComponent - SGRef_490 had an invalid mechComponentID Ammo_AmmunitionBox_Generic_AC5, skipping" in SimGame logger on WO completion. * if (Helpers.CheckDamagedComponents(mech)) * { * for (int index = 0; index < mech.Inventory.Length; index++) * { * MechComponentRef mechComponentRef = mech.Inventory[index]; * * // Penalized = Critted Component * if (mechComponentRef.DamageLevel == ComponentDamageLevel.Penalized) * { * // Check if a new base MechLab order needs to be created or not * if (newMechLabWorkOrder == null) * { * // Create new base work order of the generic MechLab type if it doesn't already exist * newMechLabWorkOrder = Helpers.CreateBaseMechLabOrder(__instance, mech); * } * * // Create a new component repair work order for this component * Logger.LogInfo("Creating Component Repair work order entry for " + mechComponentRef.ComponentDefID); * WorkOrderEntry_RepairComponent newComponentRepairOrder = __instance.CreateComponentRepairWorkOrder(mechComponentRef, false); * * // Attach as a child to the base Mech Lab order. * Logger.LogDebug("Adding WO subentry to repair component " + mechComponentRef.ComponentDefID); * newMechLabWorkOrder.AddSubEntry(newComponentRepairOrder); * } * } * } */ /* ARMOR DAMAGE CHECKS * ------------------- * Check if the given mech needs any structure repaired * */ if (Helpers.CheckArmorDamage(mech)) { Logger.LogDebug("SimGameConstant: ArmorInstallTechPoints: " + __instance.Constants.MechLab.ArmorInstallTechPoints); Logger.LogDebug("SimGameConstant: ArmorInstallCost: " + __instance.Constants.MechLab.ArmorInstallCost); // Loop over the ChassisLocations for repair in their highest -> lowest priority order from the dictionary defined in Helpers for (int index = 0; index < Globals.repairPriorities.Count; index++) { // Set current ChassisLocation ChassisLocations thisLoc = Globals.repairPriorities.ElementAt(index).Value; // Get current mech's loadout from the looped chassis location LocationLoadoutDef thisLocLoadout = mech.GetLocationLoadoutDef(thisLoc); // Friendly name for this location string thisLocName = thisLoc.ToString(); Logger.LogDebug("Analysing location: " + thisLocName); // Check if a new base MechLab order needs to be created if (newMechLabWorkOrder == null) { // Create new base work order of the generic MechLab type if it doesn't already exist newMechLabWorkOrder = Helpers.CreateBaseMechLabOrder(__instance, mech); } // Work out difference of armor lost for each location - default to 0 int armorDifference = 0; // Consider rear armour in difference calculation if this is a RT, CT or LT if (thisLocLoadout == mech.CenterTorso || thisLocLoadout == mech.RightTorso || thisLocLoadout == mech.LeftTorso) { Logger.LogDebug("Location also has rear armor."); armorDifference = (int)Mathf.Abs(thisLocLoadout.CurrentArmor - thisLocLoadout.AssignedArmor) + (int)Mathf.Abs(thisLocLoadout.CurrentRearArmor - thisLocLoadout.AssignedRearArmor); } else { armorDifference = (int)Mathf.Abs(thisLocLoadout.CurrentArmor - thisLocLoadout.AssignedArmor); } // Only create work orders for repairing armor if this location has taken armor damage in combat if (armorDifference != 0) { Logger.LogInfo("Total armor difference for " + thisLocName + " is " + armorDifference); Logger.LogInfo("Creating ModifyMechArmor work order entry for " + thisLocName); Logger.LogDebug("Calling ModifyMechArmor WO with params - GUID: " + mech.GUID.ToString() + " | Location: " + thisLocName + " | armorDifference: " + armorDifference + " | AssignedArmor: " + thisLocLoadout.AssignedArmor + " | AssignedRearArmor: " + thisLocLoadout.AssignedRearArmor ); WorkOrderEntry_ModifyMechArmor newArmorWorkOrder = __instance.CreateMechArmorModifyWorkOrder( mech.GUID, thisLocLoadout.Location, armorDifference, (int)(thisLocLoadout.AssignedArmor), (int)(thisLocLoadout.AssignedRearArmor) ); /* IMPORTANT! * This has turned out to be required as CurrentArmor appears to be reset to AssignedArmor from somewhere unknown in the game after battle * So if we don't reset AssignedArmor now, player can cancel the work order to get a free armor reset anyway! * * NOTE: CeilToInt (or similar rounding) is vital to prevent fractions of armor from causing Mech tonnage / validation issues for the player */ Logger.LogDebug("Forcing assignment of Assigned Armor: " + thisLocLoadout.AssignedArmor + " To Current Armor (CeilToInt): " + Mathf.CeilToInt(thisLocLoadout.CurrentArmor)); thisLocLoadout.AssignedArmor = Mathf.CeilToInt(thisLocLoadout.CurrentArmor); thisLocLoadout.AssignedRearArmor = Mathf.CeilToInt(thisLocLoadout.CurrentRearArmor); Logger.LogInfo("Adding WO subentry to install missing " + thisLocName + " armor."); newMechLabWorkOrder.AddSubEntry(newArmorWorkOrder); } else { Logger.LogDebug("Armor repair not required for: " + thisLocName); } } } /* WORK ORDER SUBMISSION * --------------------- * Submit the complete work order for the mech, which will include any repair armor / structure subentries for each location * */ if (newMechLabWorkOrder != null) { if (newMechLabWorkOrder.SubEntryCount > 0) { // Submit work order to our temporary queue for internal processing Helpers.SubmitTempWorkOrder( __instance, newMechLabWorkOrder, mech ); } else { Logger.LogInfo(mech.Name + " did not require repairs."); } } // Lifted from original RestoreMechPostCombat method - resets any non-functional mech components back to functional foreach (MechComponentRef mechComponentRef in mech.Inventory) { if (mechComponentRef.DamageLevel == ComponentDamageLevel.NonFunctional) { Logger.LogDebug("Resetting non-functional mech component: " + mechComponentRef.ToString()); mechComponentRef.DamageLevel = ComponentDamageLevel.Functional; } } return(false); // Finally, prevent firing the original method } catch (Exception ex) { Logger.LogError(ex); return(true); // Allow original method to fire if there is an exception } }
// Run after completion of contracts and queue up any orders in the temp queue into the game's Mech Lab queue public static void Postfix(SimGameState __instance) { try { foreach (var mechDef in Core.CombatMechs) { if (mechDef.MechDefCurrentStructure < mechDef.MechDefMaxStructure) { continue; } bool componentDamage = false; for (int i = 0; i < mechDef.inventory.Length; i++) { if (mechDef.inventory[i].DamageLevel == ComponentDamageLevel.Destroyed || mechDef.inventory[i].DamageLevel == ComponentDamageLevel.NonFunctional) { componentDamage = true; } } if (componentDamage) { continue; } var tempWO = Helpers.CreateBaseMechLabOrder(__instance, mechDef); tempWO.AddSubEntry(Repair_ReArm.RepairArmorMechDef(mechDef)); Core.tempMechLabQueue.Add(tempWO); } // If there are any work orders in the temporary queue, prompt the player if (Core.tempMechLabQueue.Count > 0) { Logger.LogDebug("Processing temp Mech Lab queue orders."); int cbills = 0; int techCost = 0; int mechRepairCount = 0; int skipMechCount = 0; string mechRepairCountDisplayed = String.Empty; string skipMechCountDisplayed = String.Empty; string skipMechMessage = String.Empty; string finalMessage = String.Empty; //int Counter = Core.tempMechLabQueue.Count; //for (int index = Counter - 1; index < 0; index--) //{ // if (Counter == 0) // break; // WorkOrderEntry_MechLab order = Core.tempMechLabQueue[index]; // LogDebug("Checking for destroyed components."); // bool destroyedComponents = false; // MechDef mech = __instance.GetMechByID(order.MechID); // if (mech != null) // destroyedComponents = Helpers.CheckDestroyedComponents(mech); // else // destroyedComponents = true; // if (destroyedComponents) // { // // Remove this work order from the temp mech lab queue if the mech has destroyed components and move to next iteration // Logger.LogDebug("Removing " + mech.Name + " order from temp queue due to destroyed components and mod settings."); // Core.tempMechLabQueue.Remove(order); // destroyedComponents = false; // skipMechCount++; // } //} // Calculate summary of total repair costs from the temp work order queue for (int index = 0; index < Core.tempMechLabQueue.Count; index++) { if (Core.tempMechLabQueue.Count == 0) { break; } WorkOrderEntry_MechLab order = Core.tempMechLabQueue[index]; MechDef mech = __instance.GetMechByID(order.MechID); LogDebug("Adding " + mech.Name + " to RepairCount."); cbills += order.GetCBillCost(); techCost += order.GetCost(); mechRepairCount++; } mechRepairCount = Mathf.Clamp(mechRepairCount, 0, 6); // If Yang's Auto Repair prompt is enabled, build a message prompt dialog for the player if (true) { // Calculate a friendly techCost of the work order in days, based on number of current mechtechs in the player's game. if (techCost != 0 && __instance.MechTechSkill != 0) { techCost = Mathf.CeilToInt((float)techCost / (float)__instance.MechTechSkill); } else { techCost = 1; // Safety in case of weird div/0 } // Generate a quick friendly description of how many mechs were damaged in battle switch (mechRepairCount) { case 0: { Logger.LogDebug("mechRepairCount was 0."); break; } case 1: { mechRepairCountDisplayed = "one of our 'Mechs had only its armor"; break; } case 2: { mechRepairCountDisplayed = "a couple of the 'Mechs had only their armor"; break; } case 3: { mechRepairCountDisplayed = "three of our 'Mechs had only their armor"; break; } case 4: { mechRepairCountDisplayed = "an entire lance of ours had only their armor"; break; } case 5: { mechRepairCountDisplayed = "five of our 'Mechs had only their armor"; break; } case 6: { mechRepairCountDisplayed = "every 'Mech we dropped with had only their armor"; break; } } // Generate a friendly description of how many mechs were damaged but had components destroyed switch (skipMechCount) { case 0: { Logger.LogDebug("skipMechCount was 0."); break; } case 1: { skipMechCountDisplayed = "one of the 'Mechs is damaged but has"; break; } case 2: { skipMechCountDisplayed = "two of the 'Mechs are damaged but have"; break; } case 3: { skipMechCountDisplayed = "three of the 'Mechs are damaged but have "; break; } case 4: { skipMechCountDisplayed = "the whole lance is damaged but has"; break; } } // Check if there are any mechs to process if (mechRepairCount > 0 || skipMechCount > 0) { Logger.LogDebug("mechRepairCount is " + mechRepairCount + " skipMechCount is " + skipMechCount); // Setup the notification for mechs with damaged components that we might want to skip if (skipMechCount > 0 && mechRepairCount == 0) { skipMechMessage = String.Format("{0} destroyed components. I'll leave the repairs for you to review.", skipMechCountDisplayed); } else { skipMechMessage = String.Format("{0} destroyed components, so I'll leave those repairs to you.", skipMechCountDisplayed); } Logger.LogDebug("Firing Yang's UI notification."); SimGameInterruptManager notificationQueue = __instance.GetInterruptQueue(); // If all of the mechs needing repairs have damaged components and should be skipped from auto-repair, change the message notification structure to make more sense (e.g. just have an OK button) if (skipMechCount > 0 && mechRepairCount == 0) { finalMessage = String.Format( "Boss, {0} \n\n", skipMechMessage ); // Queue Notification notificationQueue.QueuePauseNotification( "'Mech Armor Repairs Needed", finalMessage, __instance.GetCrewPortrait(SimGameCrew.Crew_Yang), string.Empty, delegate { Logger.LogDebug("[PROMPT] All damaged mechs had destroyed components and won't be queued for repair."); }, "OK" ); } else { if (skipMechCount > 0) { finalMessage = String.Format( "Boss, {0} damaged. It'll cost <color=#DE6729>{1}{2:n0}</color> and {3} days for these repairs. Want my crew to get started?\n\nAlso, {4}\n\n", mechRepairCountDisplayed, '¢', cbills.ToString(), techCost.ToString(), skipMechMessage ); } else { finalMessage = String.Format( "Boss, {0} damaged on the last engagement. It'll cost <color=#DE6729>{1}{2:n0}</color> and {3} days for the repairs.\n\nWant my crew to get started?", mechRepairCountDisplayed, '¢', cbills.ToString(), techCost.ToString() ); } // Queue up Yang's notification notificationQueue.QueuePauseNotification( "'Mech Armor Repairs Needed", finalMessage, __instance.GetCrewPortrait(SimGameCrew.Crew_Yang), string.Empty, delegate { Logger.LogDebug("[PROMPT] Moving work orders from temp queue to Mech Lab queue: " + Core.tempMechLabQueue.Count + " work orders"); foreach (WorkOrderEntry_MechLab workOrder in Core.tempMechLabQueue.ToList()) { Helpers.SubmitWorkOrder(__instance, workOrder); Core.tempMechLabQueue.Remove(workOrder); } }, "Yes", delegate { foreach (WorkOrderEntry_MechLab workOrder in Core.tempMechLabQueue.ToList()) { Core.tempMechLabQueue.Remove(workOrder); } }, "No" ); } } } } } catch (Exception ex) { Core.tempMechLabQueue.Clear(); Error(ex); } }