public static void Prefix(MechFallSequence __instance)
        {
            Mod.Log.Trace("MFS:OnComplete - entered.");
            int damagePointsTT = (int)Math.Ceiling(__instance.OwningMech.tonnage / 10f);

            Mod.Log.Debug($"Actor: {CombatantUtils.Label(__instance.OwningMech)} will suffer {damagePointsTT} TT damage points.");

            // Check for any pilot skill damage reduction
            float damageReduction = 1.0f - __instance.OwningMech.PilotCheckMod(Mod.Config.Piloting.DFAReductionMulti);
            float reducedDamage   = (float)Math.Max(0f, Math.Floor(damageReduction * damagePointsTT));

            Mod.Log.Debug($" Reducing TT fall damage from: {damagePointsTT} by {damageReduction:P1} to {reducedDamage}");

            List <int> locationDamage = new List <int>();

            while (damagePointsTT >= 5)
            {
                locationDamage.Add(5 * Mod.Config.Piloting.FallingDamagePerTenTons);
                damagePointsTT -= 5;
            }
            if (damagePointsTT > 0)
            {
                locationDamage.Add(damagePointsTT * Mod.Config.Piloting.FallingDamagePerTenTons);
            }

            Mod.Log.Debug($"Applying falling damage to actor: {CombatantUtils.Label(__instance.OwningMech)}");
            foreach (int damage in locationDamage)
            {
                ArmorLocation location = FallingDamageLocations[__instance.OwningMech.Combat.NetworkRandom.Int(0, FallingDamageLocations.Length)];
                Mod.Log.Debug($"  {damage} damage to location: {location}");
                __instance.OwningMech.DEBUG_DamageLocation(location, damage, __instance.OwningMech, DamageType.KnockdownSelf);
            }
        }
        // Create a falling sequence, publish a floatie with the error
        public static void AddFallingSequence(Mech mech, MultiSequence parentSequence, string floatieText)
        {
            MechFallSequence mechFallSequence = new MechFallSequence(mech, floatieText, new Vector2(0f, -1f));

            string        fallDebuffText   = new Text(Mod.Config.LocalizedFloaties[floatieText]).ToString();
            MultiSequence showInfoSequence = new ShowActorInfoSequence(mech, fallDebuffText, FloatieMessage.MessageNature.Debuff, false)
            {
                RootSequenceGUID = mechFallSequence.SequenceGUID
            };

            mechFallSequence.AddChildSequence(showInfoSequence, mechFallSequence.MessageIndex);

            mech.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(mechFallSequence));
        }
示例#3
0
        // Create a falling sequence, publish a floatie with the error
        public static void AddFallingSequence(Mech mech, MultiSequence parentSequence, string floatieText)
        {
            Mod.Log.Info?.Write($"Adding falling sequence for mech: {mech.DistinctId()}");

            MechFallSequence mechFallSequence = new MechFallSequence(mech, floatieText, new Vector2(0f, -1f));

            string        fallDebuffText   = new Text(Mod.LocalizedText.Floaties[floatieText]).ToString();
            MultiSequence showInfoSequence = new ShowActorInfoSequence(mech, fallDebuffText, FloatieMessage.MessageNature.Debuff, false)
            {
                RootSequenceGUID = mechFallSequence.SequenceGUID
            };

            mechFallSequence.AddChildSequence(showInfoSequence, mechFallSequence.MessageIndex);
            mech.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(mechFallSequence));
            Mod.Log.Info?.Write(" -- published fall sequence.");

            IStackSequence doneWithActorSequence = mech.DoneWithActor();

            mech.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(doneWithActorSequence));
            Mod.Log.Info?.Write(" -- published doneWithActor sequence.");
        }
示例#4
0
        public static void Prefix(MechFallSequence __instance)
        {
            Mod.Log.Trace?.Write("MFS:OnComplete - entered.");
            int damagePointsTT = (int)Math.Ceiling(__instance.OwningMech.tonnage / 10f);

            Mod.Log.Debug?.Write($"Actor: {CombatantUtils.Label(__instance.OwningMech)} will suffer {damagePointsTT} TT damage points.");

            // Check for any pilot skill damage reduction
            float damageReduction = 1.0f - __instance.OwningMech.PilotCheckMod(Mod.Config.Piloting.DFAReductionMulti);
            float reducedDamage   = (float)Math.Max(0f, Math.Floor(damageReduction * damagePointsTT));

            Mod.Log.Debug?.Write($" Reducing TT fall damage from: {damagePointsTT} by {damageReduction:P1} to {reducedDamage}");

            List <float> locationDamage = new List <float>();

            while (damagePointsTT >= 5)
            {
                locationDamage.Add(5 * Mod.Config.Piloting.FallingDamagePerTenTons);
                damagePointsTT -= 5;
            }
            if (damagePointsTT > 0)
            {
                locationDamage.Add(damagePointsTT * Mod.Config.Piloting.FallingDamagePerTenTons);
            }

            Mod.Log.Info?.Write($"FALLING DAMAGE: TT damage: {damagePointsTT} => {damagePointsTT * Mod.Config.Piloting.FallingDamagePerTenTons} falling damage to actor: {CombatantUtils.Label(__instance.OwningMech)}");

            try
            {
                (Weapon melee, Weapon dfa)fakeWeapons = ModState.GetFakedWeapons(__instance.OwningMech);
                AttackHelper.CreateImaginaryAttack(__instance.OwningMech, fakeWeapons.melee, __instance.OwningMech, __instance.SequenceGUID, locationDamage.ToArray(), DamageType.KnockdownSelf, MeleeAttackType.NotSet);
            }
            catch (Exception e)
            {
                Mod.Log.Error?.Write(e, "FAILED TO APPLY FALL DAMAGE");
            }
        }
        static void ApplyFallingDamage(MechFallSequence sequence, int oldState, int newState)
        {
            if (newState != FinishedState)
            {
                return;
            }
            var mech = sequence.OwningMech;

            if (mech.IsFlaggedForDeath || mech.IsDead)
            {
                return;                                        // TODO: maybe even the dead should take damage?
            }
            var locationTakingDamage = possibleLocations[UnityEngine.Random.Range(0, possibleLocations.Length)];

            Logger.Debug($"falling happened!\nlocation taking damage: {locationTakingDamage}");
            var rawFallingDamage   = Core.ModSettings.FallingAmountDamagePerTon * mech.tonnage;
            var fallingDamageValue = rawFallingDamage;

            if (Core.ModSettings.PilotingSkillFallingDamageMitigation)
            {
                var mitigation        = Calculator.PilotingMitigation(mech);
                var mitigationPercent = Mathf.RoundToInt(mitigation * 100);
                fallingDamageValue = rawFallingDamage - (mitigation * rawFallingDamage);
                Logger.Debug($"falling damage numbers\n" +
                             $"pilotSkill: {mech.SkillPiloting}\n" +
                             $"mitigation: {mitigation}\n" +
                             $"rawFallingDamage: {rawFallingDamage}\n" +
                             $"mitigationPercent: {mitigationPercent}\n" +
                             $"fallingDamageValue: {fallingDamageValue}");
                mech.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(new ShowActorInfoSequence(mech, $"Pilot Check: Avoided {mitigationPercent}% Falling Damage!", FloatieMessage.MessageNature.Neutral, true)));
            }
            mech.DEBUG_DamageLocation(locationTakingDamage, fallingDamageValue, mech);
            if (AttackDirector.damageLogger.IsLogEnabled)
            {
                AttackDirector.damageLogger.Log($"@@@@@@@@ {mech.DisplayName} takes {fallingDamageValue} damage to its {Mech.GetLongArmorLocation(locationTakingDamage)} from falling!");
            }
        }
            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);
            }
示例#7
0
 public static void Postfix(MechFallSequence __instance)
 {
     Mod.Log.Trace?.Write("MFS:OnAdded - entered.");
     QuipHelper.PublishQuip(__instance.OwningMech, Mod.LocalizedText.Quips.Knockdown);
 }
        static void Prefix(Mech __instance)
        {
            Mod.HeatLog.Info?.Write($"AT END OF TURN: ACTOR: {__instance.DistinctId()} has currentHeat: {__instance.CurrentHeat}" +
                                    $" tempHeat: {__instance.TempHeat}  maxHeat: {__instance.MaxHeat}  heatsinkCapacity: {__instance.AdjustedHeatsinkCapacity}");

            // Invalidate any melee state the actor may have set
            ModState.InvalidateState(__instance);

            // Make the checks for ammo explosions, etc
            float heatCheck  = __instance.HeatCheckMod(Mod.Config.SkillChecks.ModPerPointOfGuts);
            float pilotCheck = __instance.PilotCheckMod(Mod.Config.SkillChecks.ModPerPointOfPiloting);

            Mod.HeatLog.Debug?.Write($" Actor: {__instance.DistinctId()} has heatCheckMod: {heatCheck}  pilotingCheckMod: {pilotCheck}");

            MultiSequence sequence                 = new MultiSequence(__instance.Combat);
            bool          failedInjuryCheck        = CheckHelper.ResolvePilotInjuryCheck(__instance, __instance.CurrentHeat, sequence.RootSequenceGUID, sequence.SequenceGUID, heatCheck);
            bool          failedSystemFailureCheck = CheckHelper.ResolveSystemFailureCheck(__instance, __instance.CurrentHeat, sequence.SequenceGUID, heatCheck);
            bool          failedAmmoCheck          = CheckHelper.ResolveRegularAmmoCheck(__instance, __instance.CurrentHeat, sequence.SequenceGUID, heatCheck);
            bool          failedVolatileAmmoCheck  = CheckHelper.ResolveVolatileAmmoCheck(__instance, __instance.CurrentHeat, sequence.SequenceGUID, heatCheck);

            Mod.HeatLog.Info?.Write($"  failedInjuryCheck: {failedInjuryCheck}  failedSystemFailureCheck: {failedSystemFailureCheck}  " +
                                    $"failedAmmoCheck: {failedAmmoCheck}  failedVolatileAmmoCheck: {failedVolatileAmmoCheck}");

            bool failedShutdownCheck = false;

            if (!__instance.IsShutDown)
            {
                // Resolve Shutdown + Fall
                failedShutdownCheck = !CheckHelper.DidCheckPassThreshold(Mod.Config.Heat.Shutdown, __instance.CurrentHeat, __instance, heatCheck, ModText.FT_Check_Shutdown);
                Mod.HeatLog.Info?.Write($"  failedShutdownCheck: {failedShutdownCheck}");
                if (failedShutdownCheck)
                {
                    Mod.HeatLog.Info?.Write($"-- Shutdown check failed for unit {CombatantUtils.Label(__instance)}, forcing unit to shutdown");

                    string debuffText = new Text(Mod.LocalizedText.Floaties[ModText.FT_Shutdown_Failed_Overide]).ToString();
                    sequence.AddChildSequence(new ShowActorInfoSequence(__instance, debuffText,
                                                                        FloatieMessage.MessageNature.Debuff, true), sequence.ChildSequenceCount - 1);

                    MechEmergencyShutdownSequence mechShutdownSequence = new MechEmergencyShutdownSequence(__instance);
                    //{
                    //    RootSequenceGUID = __instance.SequenceGUID
                    //};
                    sequence.AddChildSequence(mechShutdownSequence, sequence.ChildSequenceCount - 1);

                    if (__instance.IsOrWillBeProne)
                    {
                        bool failedFallingCheck = !CheckHelper.DidCheckPassThreshold(Mod.Config.Heat.ShutdownFallThreshold, __instance, pilotCheck, ModText.FT_Check_Fall);
                        Mod.HeatLog.Debug?.Write($"  failedFallingCheck: {failedFallingCheck}");
                        if (failedFallingCheck)
                        {
                            Mod.HeatLog.Info?.Write("   Pilot check from shutdown failed! Forcing a fall!");
                            string fallDebuffText = new Text(Mod.LocalizedText.Floaties[ModText.FT_Shutdown_Fall]).ToString();
                            sequence.AddChildSequence(new ShowActorInfoSequence(__instance, fallDebuffText,
                                                                                FloatieMessage.MessageNature.Debuff, true), sequence.ChildSequenceCount - 1);

                            MechFallSequence mfs = new MechFallSequence(__instance, "Overheat", new Vector2(0f, -1f));
                            //{
                            //    RootSequenceGUID = __instance.SequenceGUID
                            //};
                            sequence.AddChildSequence(mfs, sequence.ChildSequenceCount - 1);
                        }
                        else
                        {
                            Mod.HeatLog.Info?.Write($"Pilot check to avoid falling passed. Applying unstead to unit.");
                            __instance.ApplyUnsteady();
                        }
                    }
                    else
                    {
                        Mod.HeatLog.Debug?.Write("Unit is already prone, skipping.");
                    }
                }
            }
            else
            {
                Mod.HeatLog.Debug?.Write("Unit is already shutdown, skipping.");
            }

            if (failedInjuryCheck || failedSystemFailureCheck || failedAmmoCheck || failedVolatileAmmoCheck || failedShutdownCheck)
            {
                __instance.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(sequence));
            }
        }