// Resolve turret hits - any hull breach kill the unit private static void ResolveTurretHullBreaches(Turret targetTurret) { bool needsQuip = false; // Check for immunity List <MechComponent> components = targetTurret.allComponents.Where(mc => mc.DamageLevel == ComponentDamageLevel.Functional).ToList(); bool hasImmunity = false; foreach (MechComponent mc in components) { if (mc.StatCollection.ContainsStatistic(ModStats.HullBreachImmunity)) { Mod.Log.Debug?.Write($" Component: {mc.UIName} grants hull breach immunity, skipping!"); hasImmunity = true; break; } } if (hasImmunity) { return; } foreach (BuildingLocation hitLocation in ModState.BreachHitsTurret.Keys) { // If no immunity, sum the breach check across all trials float passChance = 1f - ModState.BreachCheck; //float sequencePassChance = Mathf.Pow(passChance, ModState.BreachHitsTurret[hitLocation]); // TODO: Number of trials is way too rough, and can make breaches extremely common. Weakening to flat percentage chance. float sequencePassChance = Mathf.Pow(passChance, 1); float sequenceThreshold = 1f - sequencePassChance; Mod.Log.Debug?.Write($" For pass chance: {passChance} with n trials: {ModState.BreachHitsTurret[hitLocation]} has sequencePassChance: {sequencePassChance} => sequenceThreshold: {sequenceThreshold}"); // Check for failure bool passedCheck = CheckHelper.DidCheckPassThreshold(sequenceThreshold, targetTurret, 0f, Mod.LocalizedText.Floaties[ModText.FT_Hull_Breach]); Mod.Log.Debug?.Write($"Actor: {CombatantUtils.Label(targetTurret)} HULL BREACH check: {passedCheck} for location: {hitLocation}"); if (!passedCheck) { Mod.Log.Info?.Write($" Turret {CombatantUtils.Label(targetTurret)} suffers a hull breach in location: {hitLocation}"); string floatieText = new Text(Mod.LocalizedText.Floaties[ModText.FT_Hull_Breach]).ToString(); MultiSequence showInfoSequence = new ShowActorInfoSequence(targetTurret, floatieText, FloatieMessage.MessageNature.Debuff, false); targetTurret.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(showInfoSequence)); needsQuip = true; if (targetTurret.GetPilot() != null) { targetTurret.GetPilot().KillPilot(targetTurret.Combat.Constants, "", 0, DamageType.Unknown, null, null); } targetTurret.FlagForDeath("Dead from hull breach!", DeathMethod.Unknown, DamageType.Unknown, -1, -1, "", false); targetTurret.HandleDeath("0"); } } if (needsQuip) { QuipHelper.PublishQuip(targetTurret, Mod.LocalizedText.Quips.Breach); } }
private static void ResolveVehicleHullBreaches(Vehicle targetVehicle) { bool needsQuip = false; foreach (VehicleChassisLocations hitLocation in ModState.BreachHitsVehicle.Keys) { List <MechComponent> componentsInLocation = targetVehicle.allComponents.Where(mc => mc.mechComponentRef.DamageLevel == ComponentDamageLevel.Functional && mc.mechComponentRef.MountedLocation == (ChassisLocations)hitLocation).ToList(); // Check for immunity in this location bool hasImmunity = false; foreach (MechComponent mc in componentsInLocation) { if (mc.StatCollection.ContainsStatistic(ModStats.HullBreachImmunity)) { Mod.Log.Debug($" Component: {mc.UIName} grants hull breach immunity, skipping!"); hasImmunity = true; break; } } if (hasImmunity) { continue; } // If no immunity, sum the breach check across all trials float passChance = 1f - ModState.BreachCheck; float sequencePassChance = Mathf.Pow(passChance, ModState.BreachHitsVehicle[hitLocation]); float sequenceThreshold = 1f - sequencePassChance; Mod.Log.Debug($" For pass chance: {passChance} with n trials: {ModState.BreachHitsVehicle[hitLocation]} has sequencePassChance: {sequencePassChance} => sequenceThreshold: {sequenceThreshold}"); // Check for failure bool passedCheck = CheckHelper.DidCheckPassThreshold(sequenceThreshold, targetVehicle, 0f, Mod.Config.LocalizedFloaties[ModConfig.FT_Hull_Breach]); Mod.Log.Debug($"Actor: {CombatantUtils.Label(targetVehicle)} HULL BREACH check: {passedCheck} for location: {hitLocation}"); if (!passedCheck) { string floatieText = new Text(Mod.Config.LocalizedFloaties[ModConfig.FT_Hull_Breach]).ToString(); MultiSequence showInfoSequence = new ShowActorInfoSequence(targetVehicle, floatieText, FloatieMessage.MessageNature.Debuff, false); targetVehicle.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(showInfoSequence)); needsQuip = true; if (targetVehicle.GetPilot() != null) { targetVehicle.GetPilot().KillPilot(targetVehicle.Combat.Constants, "", 0, DamageType.Unknown, null, null); } targetVehicle.FlagForDeath("Dead from hull breach!", DeathMethod.Unknown, DamageType.Unknown, -1, -1, "", false); targetVehicle.HandleDeath("0"); break; } } if (needsQuip) { QuipHelper.PublishQuip(targetVehicle, Mod.Config.Qips.Breach); } }
static void Postfix(BehaviorNode __instance, ref BehaviorTreeResults __result) { Traverse unitT = Traverse.Create(__instance).Field("unit"); AbstractActor unit = unitT.GetValue <AbstractActor>(); if (unit is Mech mech) { float heatCheck = mech.HeatCheckMod(Mod.Config.SkillChecks.ModPerPointOfGuts); int futureHeat = mech.CurrentHeat - mech.AdjustedHeatsinkCapacity; // Check to see if we will shutdown bool passedStartupCheck = CheckHelper.DidCheckPassThreshold(Mod.Config.Heat.Shutdown, futureHeat, mech, heatCheck, ModText.FT_Check_Startup); Mod.Log.Info?.Write($"AI unit {CombatantUtils.Label(mech)} heatCheck: {heatCheck} vs. futureHeat: {futureHeat} " + $"(from currentHeat: {mech.CurrentHeat} - sinking: {mech.AdjustedHeatsinkCapacity}) => passed: {passedStartupCheck}"); if (!passedStartupCheck) { Mod.Log.Info?.Write($" -- shutdown check failed, forcing it to remain shutdown."); BehaviorTreeResults newResult = new BehaviorTreeResults(BehaviorNodeState.Failure); newResult.orderInfo = new OrderInfo(OrderType.Stand); __result = newResult; bool failedInjuryCheck = CheckHelper.ResolvePilotInjuryCheck(mech, futureHeat, -1, -1, heatCheck); if (failedInjuryCheck) { Mod.Log.Info?.Write(" -- unit did not pass injury check!"); } bool failedSystemFailureCheck = CheckHelper.ResolveSystemFailureCheck(mech, futureHeat, -1, heatCheck); if (failedSystemFailureCheck) { Mod.Log.Info?.Write(" -- unit did not pass system failure check!"); } bool failedAmmoCheck = CheckHelper.ResolveRegularAmmoCheck(mech, futureHeat, -1, heatCheck); if (failedAmmoCheck) { Mod.Log.Info?.Write(" -- unit did not pass ammo explosion check!"); } bool failedVolatileAmmoCheck = CheckHelper.ResolveVolatileAmmoCheck(mech, futureHeat, -1, heatCheck); if (failedVolatileAmmoCheck) { Mod.Log.Info?.Write(" -- unit did not pass volatile ammo explosion check!"); } QuipHelper.PublishQuip(mech, Mod.LocalizedText.Quips.Startup); } else { Mod.Log.Info?.Write($" -- shutdown check passed, starting up normally."); } } }
public static bool Prefix(MechStartupInvocation __instance, CombatGameState combatGameState) { Mech mech = combatGameState.FindActorByGUID(__instance.MechGUID) as Mech; if (mech == null) { return(true); } // Check to see if we should restart automatically float heatCheck = mech.HeatCheckMod(Mod.Config.Piloting.SkillMulti); int futureHeat = mech.CurrentHeat - mech.AdjustedHeatsinkCapacity; bool passedStartupCheck = CheckHelper.DidCheckPassThreshold(Mod.Config.Heat.Shutdown, futureHeat, mech, heatCheck, ModConfig.FT_Check_Startup); if (passedStartupCheck) { return(true); } // Do the normal startup process Mod.Log.Debug($"Mech: {CombatantUtils.Label(mech)} failed a startup roll, venting heat but remaining offline."); QuipHelper.PublishQuip(mech, Mod.Config.Qips.Startup); DoneWithActorSequence doneWithActorSequence = (DoneWithActorSequence)mech.GetDoneWithActorOrders(); MechHeatSequence mechHeatSequence = new MechHeatSequence(mech, true, true, "STARTUP"); doneWithActorSequence.AddChildSequence(mechHeatSequence, mechHeatSequence.MessageIndex); InvocationStackSequenceCreated message = new InvocationStackSequenceCreated(doneWithActorSequence, __instance); combatGameState.MessageCenter.PublishMessage(message); AddSequenceToStackMessage.Publish(combatGameState.MessageCenter, doneWithActorSequence); //mech.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(mechHeatSequence)); //mech.OnStartupComplete(mechHeatSequence.SequenceGUID); //mech.DoneWithActor(); return(false); }
// Resolve mech hits - mark components invalid, but kill the pilot on a head-hit private static void ResolveMechHullBreaches(Mech targetMech) { bool needsQuip = false; foreach (ChassisLocations hitLocation in ModState.BreachHitsMech.Keys) { List <MechComponent> componentsInLocation = targetMech.allComponents.Where(mc => mc.mechComponentRef.DamageLevel == ComponentDamageLevel.Functional && mc.mechComponentRef.MountedLocation == hitLocation).ToList(); // Check for immunity in this location bool hasImmunity = false; foreach (MechComponent mc in componentsInLocation) { if (mc.StatCollection.ContainsStatistic(ModStats.HullBreachImmunity)) { Mod.Log.Debug($" Component: {mc.UIName} grants hull breach immunity, skipping!"); hasImmunity = true; break; } } if (hasImmunity) { continue; } // If no immunity, sum the breach check across all trials float passChance = 1f - ModState.BreachCheck; float sequencePassChance = Mathf.Pow(passChance, ModState.BreachHitsMech[hitLocation]); float sequenceThreshold = 1f - sequencePassChance; Mod.Log.Debug($" For pass chance: {passChance} with n trials: {ModState.BreachHitsMech[hitLocation]} has sequencePassChance: {sequencePassChance} => sequenceThreshold: {sequenceThreshold}"); // Check for failure bool passedCheck = CheckHelper.DidCheckPassThreshold(sequenceThreshold, targetMech, 0f, Mod.Config.LocalizedFloaties[ModConfig.FT_Hull_Breach]); Mod.Log.Debug($"Actor: {CombatantUtils.Label(targetMech)} HULL BREACH check: {passedCheck} for location: {hitLocation}"); if (!passedCheck) { string floatieText = new Text(Mod.Config.LocalizedFloaties[ModConfig.FT_Hull_Breach]).ToString(); MultiSequence showInfoSequence = new ShowActorInfoSequence(targetMech, floatieText, FloatieMessage.MessageNature.Debuff, false); targetMech.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(showInfoSequence)); needsQuip = true; if (hitLocation <= ChassisLocations.RightTorso) { switch (hitLocation) { case ChassisLocations.Head: Mod.Log.Debug($" Head structure damage taken, killing pilot!"); targetMech.GetPilot().KillPilot(targetMech.Combat.Constants, "", 0, DamageType.Enemy, null, null); break; case ChassisLocations.CenterTorso: default: if (hitLocation == ChassisLocations.CenterTorso) { Mod.Log.Debug($" Center Torso hull breach!"); } // Walk the location and disable every component in it foreach (MechComponent mc in componentsInLocation) { Mod.Log.Debug($" Marking component: {mc.defId} of type: {mc.componentDef.Description.Name} nonfunctional"); mc.DamageComponent(default(WeaponHitInfo), ComponentDamageLevel.NonFunctional, true); } break; } } } } if (needsQuip) { QuipHelper.PublishQuip(targetMech, Mod.Config.Qips.Breach); } }
public static bool Prefix(MechStartupInvocation __instance, CombatGameState combatGameState) { Mech mech = combatGameState.FindActorByGUID(__instance.MechGUID) as Mech; if (mech == null) { return(true); } Mod.Log.Info?.Write($"Processing startup for Mech: {CombatantUtils.Label(mech)}"); // Check to see if we should restart automatically float heatCheck = mech.HeatCheckMod(Mod.Config.SkillChecks.ModPerPointOfGuts); int futureHeat = mech.CurrentHeat - mech.AdjustedHeatsinkCapacity; bool passedStartupCheck = CheckHelper.DidCheckPassThreshold(Mod.Config.Heat.Shutdown, futureHeat, mech, heatCheck, ModText.FT_Check_Startup); Mod.Log.Info?.Write($" -- futureHeat: {futureHeat} = current: {mech.CurrentHeat} - HSCapacity: {mech.AdjustedHeatsinkCapacity} vs. heatCheck: {heatCheck} => passedStartup: {passedStartupCheck}"); bool failedInjuryCheck = CheckHelper.ResolvePilotInjuryCheck(mech, futureHeat, -1, -1, heatCheck); if (failedInjuryCheck) { Mod.Log.Info?.Write(" -- unit did not pass injury check!"); } bool failedSystemFailureCheck = CheckHelper.ResolveSystemFailureCheck(mech, futureHeat, -1, heatCheck); if (failedSystemFailureCheck) { Mod.Log.Info?.Write(" -- unit did not pass system failure check!"); } bool failedAmmoCheck = CheckHelper.ResolveRegularAmmoCheck(mech, futureHeat, -1, heatCheck); if (failedAmmoCheck) { Mod.Log.Info?.Write(" -- unit did not pass ammo explosion check!"); } bool failedVolatileAmmoCheck = CheckHelper.ResolveVolatileAmmoCheck(mech, futureHeat, -1, heatCheck); if (failedVolatileAmmoCheck) { Mod.Log.Info?.Write(" -- unit did not pass volatile ammo explosion check!"); } if (passedStartupCheck) { Mod.Log.Debug?.Write($" -- passed startup roll, going through regular MechStartupSequence."); return(true); } Mod.Log.Info?.Write($" -- failed startup roll, venting heat but remaining offline."); DoneWithActorSequence doneWithActorSequence = (DoneWithActorSequence)mech.GetDoneWithActorOrders(); MechHeatSequence mechHeatSequence = new MechHeatSequence(OwningMech: mech, performHeatSinkStep: true, applyStartupHeatSinks: false, instigatorID: "STARTUP"); doneWithActorSequence.AddChildSequence(mechHeatSequence, mechHeatSequence.MessageIndex); QuipHelper.PublishQuip(mech, Mod.LocalizedText.Quips.Startup); InvocationStackSequenceCreated message = new InvocationStackSequenceCreated(doneWithActorSequence, __instance); combatGameState.MessageCenter.PublishMessage(message); AddSequenceToStackMessage.Publish(combatGameState.MessageCenter, doneWithActorSequence); Mod.Log.Debug?.Write($" -- sent sequence to messageCenter"); return(false); }
public static void Postfix(MechFallSequence __instance) { Mod.Log.Trace?.Write("MFS:OnAdded - entered."); QuipHelper.PublishQuip(__instance.OwningMech, Mod.LocalizedText.Quips.Knockdown); }