public static bool ResolveVolatileAmmoCheck(Mech mech, int heatToCheck, int rootSequenceGUID, float heatCheck) { bool failedVolatileAmmoCheck = false; AmmunitionBox mostDamagingVolatile = HeatHelper.FindMostDamagingAmmoBox(mech, true); if (mostDamagingVolatile != null) { failedVolatileAmmoCheck = !CheckHelper.DidCheckPassThreshold(Mod.Config.Heat.Explosion, heatToCheck, mech, heatCheck, ModText.FT_Check_Explosion); Mod.Log.Debug?.Write($" failedVolatileAmmoCheck: {failedVolatileAmmoCheck}"); if (failedVolatileAmmoCheck) { Mod.Log.Info?.Write($"-- Volatile Ammo Explosion check failed on {CombatantUtils.Label(mech)}, forcing volatile ammo explosion"); if (mostDamagingVolatile != null) { Mod.Log.Info?.Write($" Exploding inferno ammo: {mostDamagingVolatile.UIName}"); WeaponHitInfo fakeHit = new WeaponHitInfo(rootSequenceGUID, -1, -1, -1, string.Empty, string.Empty, -1, null, null, null, null, null, null, null, new AttackDirection[] { AttackDirection.None }, null, null, null); mostDamagingVolatile.DamageComponent(fakeHit, ComponentDamageLevel.Destroyed, true); } else { Mod.Log.Debug?.Write(" Unit has no Volatile ammo boxes, skipping."); } } } return(failedVolatileAmmoCheck); }
public static bool ResolvePilotInjuryCheck(Mech mech, int heatToCheck, int rootSequenceGUID, int sequenceGUID, float heatCheck) { bool failedInjuryCheck = !CheckHelper.DidCheckPassThreshold(Mod.Config.Heat.PilotInjury, heatToCheck, mech, heatCheck, ModText.FT_Check_Injury); Mod.Log.Debug?.Write($" failedInjuryCheck: {failedInjuryCheck}"); if (failedInjuryCheck) { Mod.Log.Info?.Write($"-- Pilot Heat Injury check failed for {CombatantUtils.Label(mech)}, forcing injury from heat"); mech.pilot.InjurePilot(sequenceGUID.ToString(), rootSequenceGUID, 1, DamageType.OverheatSelf, null, mech); if (!mech.pilot.IsIncapacitated) { AudioEventManager.SetPilotVOSwitch <AudioSwitch_dialog_dark_light>(AudioSwitch_dialog_dark_light.dark, mech); AudioEventManager.PlayPilotVO(VOEvents.Pilot_TakeDamage, mech, null, null, true); if (mech.team.LocalPlayerControlsTeam) { AudioEventManager.PlayAudioEvent("audioeventdef_musictriggers_combat", "friendly_warrior_injured", null, null); } } else { mech.FlagForDeath("Pilot Killed", DeathMethod.PilotKilled, DamageType.OverheatSelf, 1, sequenceGUID, "0", false); string localText = new Text(Mod.LocalizedText.Floaties[ModText.FT_Death_By_Overheat]).ToString(); mech.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage( new ShowActorInfoSequence(mech, new Text(localText, Array.Empty <object>()), FloatieMessage.MessageNature.PilotInjury, true)) ); mech.HandleDeath("0"); } } return(failedInjuryCheck); }
public static bool ResolveSystemFailureCheck(Mech mech, int heatToCheck, int rootSequenceGUID, float heatCheck) { bool failedSystemFailureCheck = !CheckHelper.DidCheckPassThreshold(Mod.Config.Heat.SystemFailures, heatToCheck, mech, heatCheck, ModText.FT_Check_System_Failure); Mod.Log.Debug?.Write($" failedSystemFailureCheck: {failedSystemFailureCheck}"); if (failedSystemFailureCheck) { Mod.Log.Info?.Write($"-- System Failure check failed, forcing system damage on unit: {CombatantUtils.Label(mech)}"); List <MechComponent> functionalComponents = new List <MechComponent>(); foreach (MechComponent mc in mech.allComponents) { bool canTarget = mc.IsFunctional; if (mc.mechComponentRef.Is <Flags>(out Flags flagsCC)) { if (flagsCC.IsSet(ModStats.ME_IgnoreDamage)) { canTarget = false; Mod.Log.Trace?.Write($" Component: {mc.Name} / {mc.UIName} is marked ignores_damage."); } } if (canTarget) { functionalComponents.Add(mc); } } MechComponent componentToDamage = functionalComponents.GetRandomElement(); Mod.Log.Info?.Write($" Destroying component: {componentToDamage.UIName} from heat damage."); WeaponHitInfo fakeHit = new WeaponHitInfo(rootSequenceGUID, -1, -1, -1, string.Empty, string.Empty, -1, null, null, null, null, null, null, null, new AttackDirection[] { AttackDirection.None }, null, null, null); componentToDamage.DamageComponent(fakeHit, ComponentDamageLevel.Destroyed, true); } return(failedSystemFailureCheck); }
private static void Prefix(ActorMovementSequence __instance) { Mod.Log.Trace("AMS:OC entered"); // Interleaved - check for visibility to any enemies if (!__instance.owningActor.Combat.TurnDirector.IsInterleaved) { if (__instance.owningActor.Combat.LocalPlayerTeam.GetDetectedEnemyUnits().Count > 0) { Mod.Log.Info("AMS:OC TD is not interleaved but enemies are detected - disabling autobrace. "); __instance.owningActor.AutoBrace = false; } else { Mod.Log.Info("AMS:OC TD is not interleaved and no enemies - autobracing "); __instance.owningActor.AutoBrace = true; } } // Movement - check for damage after a sprint, and if so force a piloting check if (__instance.OwningMech != null && __instance.isSprinting && __instance.OwningMech.ActuatorDamageMalus() != 0) { Mod.Log.Debug($"Actor: {CombatantUtils.Label(__instance.OwningMech)} has actuator damage, forcing piloting check."); float sourceSkillMulti = __instance.OwningMech.PilotCheckMod(Mod.Config.Move.SkillMulti); bool sourcePassed = CheckHelper.DidCheckPassThreshold(Mod.Config.Move.FallAfterRunChance, __instance.OwningMech, sourceSkillMulti, ModConfig.FT_Fall_After_Run); if (!sourcePassed) { Mod.Log.Info($"Source actor: {CombatantUtils.Label(__instance.OwningMech)} failed pilot check after sprinting with actuator damage, forcing fall."); MechHelper.AddFallingSequence(__instance.OwningMech, __instance, ModConfig.FT_Fall_After_Run); } } }
private static void Prefix(MechJumpSequence __instance) { Mod.ActivationLog.Debug?.Write($"MJS:OC entered for actor: {CombatantUtils.Label(__instance.OwningMech)}"); // Check for visibility to any enemies if (!__instance.owningActor.Combat.TurnDirector.IsInterleaved) { Mod.ActivationLog.Info?.Write("MJS:OC is not interleaved and no enemies - autobracing "); __instance.owningActor.AutoBrace = true; } Mod.Log.Trace?.Write($"JUMP -- ABILITY_CONSUMES_FIRING: {__instance.AbilityConsumesFiring} / CONSUMES_FIRING: {__instance.ConsumesFiring}"); if (__instance.OwningMech == null) { return; // Nothing more to do } // Movement - check for damage after a jump, and if so force a piloting check if (__instance.OwningMech.ActuatorDamageMalus() != 0 || Mod.Config.Developer.ForceFallAfterJump) { Mod.Log.Info?.Write($"Actor: {CombatantUtils.Label(__instance.OwningMech)} has actuator damage, forcing piloting check."); float sourceSkillMulti = __instance.OwningMech.PilotCheckMod(Mod.Config.SkillChecks.ModPerPointOfPiloting); float damagePenalty = __instance.OwningMech.ActuatorDamageMalus() * Mod.Config.SkillChecks.ModPerPointOfPiloting; float checkMod = sourceSkillMulti + damagePenalty; Mod.Log.Debug?.Write($" moveSkillMulti:{sourceSkillMulti} - damagePenalty: {damagePenalty} = checkMod: {checkMod}"); bool sourcePassed = Mod.Config.Developer.ForceFallAfterJump ? false : CheckHelper.DidCheckPassThreshold(Mod.Config.Move.FallAfterJumpChance, __instance.OwningMech, checkMod, ModText.FT_Fall_After_Jump); if (!sourcePassed) { Mod.Log.Info?.Write($"Source actor: {CombatantUtils.Label(__instance.OwningMech)} failed pilot check after jumping with actuator damage, forcing fall."); MechHelper.AddFallingSequence(__instance.OwningMech, __instance, ModText.FT_Fall_After_Jump); } } }
private static void Prefix(ActorMovementSequence __instance) { Mod.ActivationLog.Info?.Write($"AMS:OC:PRE entered for actor: {CombatantUtils.Label(__instance?.OwningActor)}"); // Interleaved - check for visibility to any enemies if (!__instance.owningActor.Combat.TurnDirector.IsInterleaved) { __instance.owningActor.AutoBrace = true; } // Movement - check for damage after a sprint, and if so force a piloting check if (__instance.OwningMech != null && __instance.isSprinting && __instance.OwningMech.ActuatorDamageMalus() != 0) { Mod.Log.Info?.Write($"Actor: {CombatantUtils.Label(__instance.OwningMech)} has actuator damage, forcing piloting check."); float sourceSkillMulti = __instance.OwningMech.PilotCheckMod(Mod.Config.SkillChecks.ModPerPointOfPiloting); float damagePenalty = __instance.OwningMech.ActuatorDamageMalus() * Mod.Config.SkillChecks.ModPerPointOfPiloting; float checkMod = sourceSkillMulti + damagePenalty; Mod.Log.Debug?.Write($" moveSkillMulti:{sourceSkillMulti} - damagePenalty: {damagePenalty} = checkMod: {checkMod}"); bool sourcePassed = CheckHelper.DidCheckPassThreshold(Mod.Config.Move.FallAfterRunChance, __instance.OwningMech, checkMod, ModText.FT_Fall_After_Run); if (!sourcePassed) { Mod.Log.Info?.Write($"Source actor: {CombatantUtils.Label(__instance.OwningMech)} failed pilot check after sprinting with actuator damage, forcing fall."); MechHelper.AddFallingSequence(__instance.OwningMech, __instance, ModText.FT_Fall_After_Run); } } }
public static void PilotCheckOnInstabilityDamage(Mech target, float stabilityDamage) { // Do nothing in these cases if (target.IsDead || target.IsOrWillBeProne || !target.IsUnsteady) { Mod.Log.Debug($"Target: {CombatantUtils.Label(target)} is dead, will be prone, or has no stability damage. Skipping."); return; } float pilotCheck = target.PilotCheckMod(Mod.Config.Piloting.SkillMulti); bool didCheckPass = CheckHelper.DidCheckPassThreshold(Mod.Config.Piloting.StabilityCheck, target, pilotCheck, ModConfig.FT_Check_Fall); if (!didCheckPass) { Mod.Log.Debug($"Actor: {CombatantUtils.Label(target)} failed a knockdown check due to instability, starting fall sequence."); target.FlagForKnockdown(); string fallDebuffText = new Text(Mod.Config.LocalizedFloaties[ModConfig.FT_Check_Fall]).ToString(); target.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage( new ShowActorInfoSequence(target, fallDebuffText, FloatieMessage.MessageNature.Debuff, true) )); } }
public static bool Prefix(MechHeatSequence __instance, HeatSequenceState newState) { if (newState != HeatSequenceState.Finished) { return(true); } Traverse stateT = Traverse.Create(__instance).Field("state"); HeatSequenceState currentState = (HeatSequenceState)stateT.GetValue <int>(); if (currentState == newState) { return(true); } Mod.Log.Info($"MHS - executing updated logic for state: {newState} on actor:{__instance.OwningMech.DisplayName}_{__instance.OwningMech.GetPilot().Name}."); stateT.SetValue((int)newState); Traverse timeInCurrentStateT = Traverse.Create(__instance).Field("timeInCurrentState"); timeInCurrentStateT.SetValue(0f); /* Finished Can be invoked from a rising state (heat being added): * Attack Sequence, artillery sequence, actor burning effect, (heatSinkStep=false, applyStartupHeatSinks=false) * On End of turn sequence - (heatSinkStep=true, applyStartupHeatSinks=false) * On Mech Startup sequence - (heatSinkStep=true, applyStartupHeatSinks=true) */ if (!__instance.PerformHeatSinkStep) { Mod.Log.Debug($"Reconciling heat for actor: {CombatantUtils.Label(__instance.OwningMech)}"); Mod.Log.Debug($" Before - currentHeat: {__instance.OwningMech.CurrentHeat} tempHeat: {__instance.OwningMech.TempHeat} " + $"isPastMaxHeat: {__instance.OwningMech.IsPastMaxHeat} hasAppliedHeatSinks: {__instance.OwningMech.HasAppliedHeatSinks}"); // Checks for heat damage, clamps heat to max and min __instance.OwningMech.ReconcileHeat(__instance.RootSequenceGUID, __instance.InstigatorID); Mod.Log.Debug($" After - currentHeat: {__instance.OwningMech.CurrentHeat} tempHeat: {__instance.OwningMech.TempHeat} " + $"isPastMaxHeat: {__instance.OwningMech.IsPastMaxHeat} hasAppliedHeatSinks: {__instance.OwningMech.HasAppliedHeatSinks}"); } //if (__instance.OwningMech.IsPastMaxHeat && !__instance.OwningMech.IsShutDown) { // __instance.OwningMech.GenerateOverheatedSequence(__instance); // return; //} if (__instance.PerformHeatSinkStep && !__instance.ApplyStartupHeatSinks) { // We are at the end of the turn - force an overheat Mod.Log.Info("AT END OF TURN - CHECKING EFFECTS"); MultiSequence sequence = new MultiSequence(__instance.OwningMech.Combat); // Possible sequences // Shutdown // Fall from shutdown // Ammo Explosion // System damage // Pilot injury // Pilot death float heatCheck = __instance.OwningMech.HeatCheckMod(Mod.Config.Piloting.SkillMulti); float pilotCheck = __instance.OwningMech.PilotCheckMod(Mod.Config.Piloting.SkillMulti); Mod.Log.Debug($" Actor: {CombatantUtils.Label(__instance.OwningMech)} has gutsMulti: {heatCheck} pilotingMulti: {pilotCheck}"); // Resolve Pilot Injury bool failedInjuryCheck = !CheckHelper.DidCheckPassThreshold(Mod.Config.Heat.PilotInjury, __instance.OwningMech.CurrentHeat, __instance.OwningMech, heatCheck, ModConfig.FT_Check_Injury); Mod.Log.Debug($" failedInjuryCheck: {failedInjuryCheck}"); if (failedInjuryCheck) { Mod.Log.Debug("-- Pilot Injury check failed, forcing injury from heat"); __instance.OwningMech.pilot.InjurePilot(__instance.SequenceGUID.ToString(), __instance.RootSequenceGUID, 1, DamageType.OverheatSelf, null, __instance.OwningMech); if (!__instance.OwningMech.pilot.IsIncapacitated) { AudioEventManager.SetPilotVOSwitch <AudioSwitch_dialog_dark_light>(AudioSwitch_dialog_dark_light.dark, __instance.OwningMech); AudioEventManager.PlayPilotVO(VOEvents.Pilot_TakeDamage, __instance.OwningMech, null, null, true); if (__instance.OwningMech.team.LocalPlayerControlsTeam) { AudioEventManager.PlayAudioEvent("audioeventdef_musictriggers_combat", "friendly_warrior_injured", null, null); } } } // Resolve System Damage bool failedSystemFailureCheck = !CheckHelper.DidCheckPassThreshold(Mod.Config.Heat.SystemFailures, __instance.OwningMech.CurrentHeat, __instance.OwningMech, heatCheck, ModConfig.FT_Check_System_Failure); Mod.Log.Debug($" failedSystemFailureCheck: {failedSystemFailureCheck}"); if (failedSystemFailureCheck) { Mod.Log.Debug("-- System Failure check failed, forcing system damage"); List <MechComponent> functionalComponents = __instance.OwningMech.allComponents.Where(mc => mc.IsFunctional).ToList(); MechComponent componentToDamage = functionalComponents.GetRandomElement(); Mod.Log.Debug($" Destroying component: {componentToDamage.UIName} from heat damage."); WeaponHitInfo fakeHit = new WeaponHitInfo(__instance.RootSequenceGUID, -1, -1, -1, string.Empty, string.Empty, -1, null, null, null, null, null, null, null, new AttackDirection[] { AttackDirection.None }, null, null, null); componentToDamage.DamageComponent(fakeHit, ComponentDamageLevel.Destroyed, true); } // Resolve Ammo Explosion - regular ammo bool failedAmmoCheck = false; AmmunitionBox mostDamaging = HeatHelper.FindMostDamagingAmmoBox(__instance.OwningMech, false); if (mostDamaging != null) { failedAmmoCheck = !CheckHelper.DidCheckPassThreshold(Mod.Config.Heat.Explosion, __instance.OwningMech.CurrentHeat, __instance.OwningMech, heatCheck, ModConfig.FT_Check_Explosion); Mod.Log.Debug($" failedAmmoCheck: {failedAmmoCheck}"); if (failedAmmoCheck) { Mod.Log.Debug("-- Ammo Explosion check failed, forcing ammo explosion"); if (mostDamaging != null) { Mod.Log.Debug($" Exploding ammo: {mostDamaging.UIName}"); WeaponHitInfo fakeHit = new WeaponHitInfo(__instance.RootSequenceGUID, -1, -1, -1, string.Empty, string.Empty, -1, null, null, null, null, null, null, null, new AttackDirection[] { AttackDirection.None }, null, null, null); mostDamaging.DamageComponent(fakeHit, ComponentDamageLevel.Destroyed, true); } else { Mod.Log.Debug(" Unit has no ammo boxes, skipping."); } } } // Resolve Ammo Explosion - inferno ammo bool failedVolatileAmmoCheck = false; AmmunitionBox mostDamagingVolatile = HeatHelper.FindMostDamagingAmmoBox(__instance.OwningMech, true); if (mostDamagingVolatile != null) { failedVolatileAmmoCheck = !CheckHelper.DidCheckPassThreshold(Mod.Config.Heat.Explosion, __instance.OwningMech.CurrentHeat, __instance.OwningMech, heatCheck, ModConfig.FT_Check_Explosion); Mod.Log.Debug($" failedVolatileAmmoCheck: {failedVolatileAmmoCheck}"); if (failedVolatileAmmoCheck) { Mod.Log.Debug("-- Volatile Ammo Explosion check failed, forcing volatile ammo explosion"); if (mostDamaging != null) { Mod.Log.Debug($" Exploding inferno ammo: {mostDamagingVolatile.UIName}"); WeaponHitInfo fakeHit = new WeaponHitInfo(__instance.RootSequenceGUID, -1, -1, -1, string.Empty, string.Empty, -1, null, null, null, null, null, null, null, new AttackDirection[] { AttackDirection.None }, null, null, null); mostDamagingVolatile.DamageComponent(fakeHit, ComponentDamageLevel.Destroyed, true); } else { Mod.Log.Debug(" Unit has no inferno ammo boxes, skipping."); } } } bool failedShutdownCheck = false; if (!__instance.OwningMech.IsShutDown) { // Resolve Shutdown + Fall failedShutdownCheck = !CheckHelper.DidCheckPassThreshold(Mod.Config.Heat.Shutdown, __instance.OwningMech.CurrentHeat, __instance.OwningMech, heatCheck, ModConfig.FT_Check_Shutdown); Mod.Log.Debug($" failedShutdownCheck: {failedShutdownCheck}"); if (failedShutdownCheck) { Mod.Log.Debug("-- Shutdown check failed, forcing unit to shutdown"); string debuffText = new Text(Mod.Config.LocalizedFloaties[ModConfig.FT_Shutdown_Failed_Overide]).ToString(); sequence.AddChildSequence(new ShowActorInfoSequence(__instance.OwningMech, debuffText, FloatieMessage.MessageNature.Debuff, true), sequence.ChildSequenceCount - 1); MechEmergencyShutdownSequence mechShutdownSequence = new MechEmergencyShutdownSequence(__instance.OwningMech) { RootSequenceGUID = __instance.SequenceGUID }; sequence.AddChildSequence(mechShutdownSequence, sequence.ChildSequenceCount - 1); if (__instance.OwningMech.IsOrWillBeProne) { bool failedFallingCheck = !CheckHelper.DidCheckPassThreshold(Mod.Config.Heat.ShutdownFallThreshold, __instance.OwningMech, pilotCheck, ModConfig.FT_Check_Fall); Mod.Log.Debug($" failedFallingCheck: {failedFallingCheck}"); if (failedFallingCheck) { Mod.Log.Info("Pilot check from shutdown failed! Forcing a fall!"); string fallDebuffText = new Text(Mod.Config.LocalizedFloaties[ModConfig.FT_Shutdown_Fall]).ToString(); sequence.AddChildSequence(new ShowActorInfoSequence(__instance.OwningMech, fallDebuffText, FloatieMessage.MessageNature.Debuff, true), sequence.ChildSequenceCount - 1); MechFallSequence mfs = new MechFallSequence(__instance.OwningMech, "Overheat", new Vector2(0f, -1f)) { RootSequenceGUID = __instance.SequenceGUID }; sequence.AddChildSequence(mfs, sequence.ChildSequenceCount - 1); } else { Mod.Log.Info($"Pilot check to avoid falling passed."); } } else { Mod.Log.Debug("Unit is already prone, skipping."); } } } else { Mod.Log.Debug("Unit is already shutdown, skipping."); } if (failedInjuryCheck || failedSystemFailureCheck || failedAmmoCheck || failedShutdownCheck) { __instance.OwningMech.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(sequence)); } return(false); } if (__instance.OwningMech.GameRep != null) { if (__instance.OwningMech.team.LocalPlayerControlsTeam) { if (__instance.OwningMech.CurrentHeat > __instance.OwningMech.OverheatLevel) { string text = string.Format("MechHeatSequence_{0}_{1}", __instance.RootSequenceGUID, __instance.SequenceGUID); AudioEventManager.CreateVOQueue(text, -1f, null, null); AudioEventManager.QueueVOEvent(text, VOEvents.Mech_Overheat_Warning, __instance.OwningMech); AudioEventManager.StartVOQueue(1f); } if ((float)__instance.OwningMech.CurrentHeat > (float)__instance.OwningMech.MaxHeat - (float)(__instance.OwningMech.MaxHeat - __instance.OwningMech.OverheatLevel) * 0.333f) { WwiseManager.PostEvent <AudioEventList_ui>(AudioEventList_ui.ui_overheat_alarm_3, WwiseManager.GlobalAudioObject, null, null); } else if ((float)__instance.OwningMech.CurrentHeat > (float)__instance.OwningMech.MaxHeat - (float)(__instance.OwningMech.MaxHeat - __instance.OwningMech.OverheatLevel) * 0.666f) { WwiseManager.PostEvent <AudioEventList_ui>(AudioEventList_ui.ui_overheat_alarm_2, WwiseManager.GlobalAudioObject, null, null); } else if (__instance.OwningMech.CurrentHeat > __instance.OwningMech.OverheatLevel) { WwiseManager.PostEvent <AudioEventList_ui>(AudioEventList_ui.ui_overheat_alarm_1, WwiseManager.GlobalAudioObject, null, null); } } if (__instance.OwningMech.CurrentHeat > Mod.Config.Heat.ShowLowOverheatAnim) { __instance.OwningMech.GameRep.StopManualPersistentVFX(__instance.OwningMech.Combat.Constants.VFXNames.heat_midHeat_persistent); __instance.OwningMech.GameRep.PlayVFX(8, __instance.OwningMech.Combat.Constants.VFXNames.heat_highHeat_persistent, true, Vector3.zero, false, -1f); return(false); } if ((float)__instance.OwningMech.CurrentHeat > Mod.Config.Heat.ShowExtremeOverheatAnim) { __instance.OwningMech.GameRep.StopManualPersistentVFX(__instance.OwningMech.Combat.Constants.VFXNames.heat_highHeat_persistent); __instance.OwningMech.GameRep.PlayVFX(8, __instance.OwningMech.Combat.Constants.VFXNames.heat_midHeat_persistent, true, Vector3.zero, false, -1f); return(false); } __instance.OwningMech.GameRep.StopManualPersistentVFX(__instance.OwningMech.Combat.Constants.VFXNames.heat_highHeat_persistent); __instance.OwningMech.GameRep.StopManualPersistentVFX(__instance.OwningMech.Combat.Constants.VFXNames.heat_midHeat_persistent); } return(false); }