// 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);
            }
        }
        // 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));
        }
示例#4
0
        static void Prefix(MechMeleeSequence __instance)
        {
            (MeleeAttack meleeAttack, Weapon fakeWeapon)seqState = ModState.GetMeleeSequenceState(__instance.SequenceGUID);
            if (seqState.meleeAttack != null && seqState.meleeAttack.AttackerInstability != 0)
            {
                Mod.MeleeLog.Info?.Write($" -- Adding {seqState.meleeAttack.AttackerInstability} absolute instability to attacker.");
                __instance.OwningMech.AddAbsoluteInstability(seqState.meleeAttack.AttackerInstability, StabilityChangeSource.Attack, "-1");
            }

            // Publish a floatie warning of the swarm attack on the target
            if (ModState.ForceDamageTable == DamageTable.SWARM)
            {
                string        swarmAttackText  = new Text(Mod.LocalizedText.Floaties[ModText.FT_Swarm_Attack]).ToString();
                MultiSequence showInfoSequence = new ShowActorInfoSequence(__instance.MeleeTarget, swarmAttackText, FloatieMessage.MessageNature.Debuff, false)
                {
                    RootSequenceGUID = __instance.SequenceGUID
                };
                SharedState.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(showInfoSequence));
                Mod.Log.Info?.Write(" -- published fall sequence.");
            }
        }
示例#5
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.");
        }
        // 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);
            }
        }
示例#7
0
        static bool Prefix(Pilot __instance, DamageType damageType, ref bool ___needsInjury, ref InjuryReason ___injuryReason)
        {
            Mod.Log.Trace?.Write("P:SNI - entered");

            // DEBUG Line here: Someone is emitting an injuryReason of 101. Try to identify them by emitting a stack trace when this happens.
            if ((int)___injuryReason > 6)
            {
                Mod.Log.Warn?.Write($"PainTolerance intercepted injuryReason with value of: {(int)___injuryReason} and desc: {___injuryReason}");
                Mod.Log.Info?.Write($"  -- injured actor was: {__instance.ParentActor.DistinctId()}");
                System.Diagnostics.StackTrace t = new System.Diagnostics.StackTrace();
                Mod.Log.Info?.Write($" -- PainTolerance:InjurePilot intercepted call stack:");
                Mod.Log.Info?.Write($" --\n\n{t}");
                Mod.Log.Info?.Write($" -- Skipping pain tolerance check!");
                return(true);
            }

            if (__instance.ParentActor == null)
            {
                return(true);
            }

            Mod.Log.Info?.Write($"Checking pilot: {__instance.ParentActor.DistinctId()} to resist injury of type: {___injuryReason}");

            // Compute the resist penalty for each damage type that we support
            if (damageType == DamageType.HeadShot)
            {
                Mod.Log.Info?.Write($"  Actor suffered a headshot, injury resist was set to: {ModState.InjuryResistPenalty}");
            }
            else if (damageType == DamageType.AmmoExplosion)
            {
                Mod.Log.Info?.Write($"  Actor suffered an ammo explosion, injury resist was set to: {ModState.InjuryResistPenalty}");
            }
            else if (damageType == DamageType.Knockdown || damageType == DamageType.KnockdownSelf)
            {
                ModState.InjuryResistPenalty = Mod.Config.Combat.PainTolerance.KnockdownResistPenalty;
                Mod.Log.Info?.Write($"  Actor was knocked down, setting injury resist to: {ModState.InjuryResistPenalty}");
            }
            else if (damageType == DamageType.SideTorso)
            {
                ModState.InjuryResistPenalty = Mod.Config.Combat.PainTolerance.SideLocationDestroyedResistPenalty;
                Mod.Log.Info?.Write($"  Actor torso/side destroyed, setting injury resist to: {ModState.InjuryResistPenalty}");
            }
            else if (damageType == DamageType.Overheat || damageType == DamageType.OverheatSelf ||
                     "OVERHEATED".Equals(__instance.InjuryReasonDescription, StringComparison.InvariantCultureIgnoreCase))
            {
                // comparison string must match label in https://github.com/BattletechModders/MechEngineer/blob/master/source/Features/ShutdownInjuryProtection/Patches/Pilot_InjuryReasonDescription_Patch.cs
                Mod.Log.Debug?.Write($"  Actor damage from overheating or ME heatDamage injury, computing overheat ratio.");
                float overheatRatio   = PainHelper.CalculateOverheatRatio(__instance.ParentActor as Mech);
                int   overheatPenalty = (int)Math.Floor(overheatRatio * Mod.Config.Combat.PainTolerance.OverheatResistPenaltyPerHeatPercentile);
                Mod.Log.Debug?.Write($"  overheatPenalty:{overheatPenalty} = " +
                                     $"Floor( overheatRatio:{overheatRatio} * penaltyPerOverheatDamage{Mod.Config.Combat.PainTolerance.OverheatResistPenaltyPerHeatPercentile} )");

                ModState.InjuryResistPenalty = overheatPenalty;
                Mod.Log.Info?.Write($"  Actor overheated, setting injury resist to: {ModState.InjuryResistPenalty}");
            }

            // Check head injury
            if (__instance.ParentActor.ImmuneToHeadInjuries())
            {
                Mod.Log.Info?.Write($"Ignoring head injury on actor: {__instance.ParentActor.DistinctId()} due to stat");
                return(false);
            }

            // Need default resistance?
            if (ModState.InjuryResistPenalty != -1)
            {
                bool success = PainHelper.MakeResistCheck(__instance);
                if (success)
                {
                    Mod.Log.Info?.Write($"Ignoring {___injuryReason} injury on pilot.");

                    // Publish a floatie
                    string         localText     = new Text(Mod.LocalizedText.Floaties[ModText.FT_InjuryResist], new object[] { }).ToString();
                    IStackSequence stackSequence = new ShowActorInfoSequence(__instance.ParentActor, localText, FloatieMessage.MessageNature.PilotInjury, useCamera: false);
                    SharedState.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(stackSequence));

                    return(false);
                }
                else
                {
                    Mod.Log.Info?.Write($"Pilot will suffer injury type: {___injuryReason}.");
                }

                ModState.InjuryResistPenalty = -1;
            }

            return(true);
        }
示例#8
0
    internal static void ShowInjuryMessage(this Pilot pilot, string message)
    {
        var sequence = new ShowActorInfoSequence(pilot.ParentActor, message, FloatieMessage.MessageNature.PilotInjury, true);

        pilot.ParentActor.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(sequence));
    }