Ejemplo n.º 1
0
        // Remove the BuildWeaponDirectorSequence, to prevent duplicate ammo consumption
        static bool Prefix(MechMeleeSequence __instance)
        {
            Traverse BuildMeleeDirectorSequenceT = Traverse.Create(__instance).Method("BuildMeleeDirectorSequence");

            BuildMeleeDirectorSequenceT.GetValue();

            if (__instance.OwningMech.GameRep != null)
            {
                __instance.OwningMech.GameRep.ReturnToNeutralFacing(isParellelSequence: true, 0.5f, __instance.RootSequenceGUID, __instance.SequenceGUID, null);
            }
            if (__instance.OwningMech.GameRep != null)
            {
                __instance.OwningMech.GameRep.FadeThrottleAudio(0f, 50f, 1f);
            }
            SharedState.Combat.MessageCenter.AddSubscriber(MessageCenterMessageType.OnAttackComplete, __instance.OnMeleeComplete);
            SharedState.Combat.MessageCenter.AddSubscriber(MessageCenterMessageType.OnAttackSequenceFire, __instance.OnMeleeReady);

            // Reading meleeSequence as a property doesn't seem to work, because it always returns null. I'm unsure if this
            //   is a harmony bug... so read it directly, which seems to work.
            Traverse            meleeSequenceT = Traverse.Create(__instance).Field("meleeSequence");
            AttackStackSequence meleeSequence  = meleeSequenceT.GetValue <AttackStackSequence>();

            SharedState.Combat.MessageCenter.PublishMessage(new AddParallelSequenceToStackMessage(meleeSequence));

            return(false);
        }
Ejemplo n.º 2
0
            public static void Postfix(AttackStackSequence __instance, MessageCenterMessage message)
            {
                try
                {
                    Logger.Debug($"[AttackStackSequence_OnAttackBegin_POSTFIX] Focus on attacked target...");

                    CombatGameState               ___combatGameState         = (CombatGameState)AccessTools.Property(typeof(AttackStackSequence), "Combat").GetValue(__instance, null);
                    AttackSequenceBeginMessage    attackSequenceBeginMessage = message as AttackSequenceBeginMessage;
                    AttackDirector.AttackSequence attackSequence             = ___combatGameState.AttackDirector.GetAttackSequence(attackSequenceBeginMessage.sequenceId);

                    bool isChosenTargetFriendly = attackSequence.chosenTarget.team.GUID == ___combatGameState.LocalPlayerTeamGuid || ___combatGameState.HostilityMatrix.IsLocalPlayerFriendly(attackSequence.chosenTarget.team.GUID);
                    bool shouldFocus            = (CameraUnchained.Settings.FocusOnTargetedFriendly && isChosenTargetFriendly) || (CameraUnchained.Settings.FocusOnTargetedEnemy && !isChosenTargetFriendly);
                    Logger.Info($"[AttackStackSequence_OnAttackBegin_POSTFIX] isChosenTargetFriendly: {isChosenTargetFriendly}, shouldFocus: {shouldFocus}");

                    if (attackSequence == null || !shouldFocus)
                    {
                        return;
                    }

                    if (attackSequence.stackItemUID == __instance.SequenceGUID && !__instance.hasOwningSequence)
                    {
                        CameraControl.Instance.SetMovingToGroundPos(attackSequence.chosenTarget.CurrentPosition, 0.95f);
                    }
                }
                catch (Exception e)
                {
                    Logger.Error(e);
                }
            }
Ejemplo n.º 3
0
        public static void Prefix(AttackStackSequence __instance, MessageCenterMessage message)
        {
            AttackCompleteMessage attackCompleteMessage = message as AttackCompleteMessage;
            bool ShouldPanic  = false;
            bool IsEarlyPanic = false;
            Mech mech         = null;

            if (attackCompleteMessage == null || attackCompleteMessage.stackItemUID != __instance.SequenceGUID)
            {
                return;
            }


            if (__instance.directorSequences[0].target is Mech)
            {
                mech        = __instance.directorSequences[0].target as Mech;
                ShouldPanic = RollHelpers.ShouldPanic(mech, attackCompleteMessage.attackSequence);
            }

            if (mech == null || mech.GUID == null || attackCompleteMessage == null)
            {
                return;
            }

            Holder.SerializeActiveJson();

            if (PanicHelpers.IsPanicking(mech, ref IsEarlyPanic) && BasicPanic.RollForEjectionResult(mech, attackCompleteMessage.attackSequence, IsEarlyPanic))
            {
                mech.EjectPilot(mech.GUID, attackCompleteMessage.stackItemUID, DeathMethod.PilotEjection, false);
            }
        }
Ejemplo n.º 4
0
 public static void Prefix(AttackStackSequence __instance)
 {
     if (__instance.owningActor.SkillTactics < 6 && __instance.isMoraleAttack)
     {
         __instance.isMoraleAttack = false;
         WasLowTacticsMoraleAttack = true;
     }
 }
Ejemplo n.º 5
0
 public static void Postfix(AttackStackSequence __instance)
 {
     if (WasLowTacticsMoraleAttack)
     {
         __instance.isMoraleAttack = true;
         __instance.owningActor.team.ModifyMorale(__instance.owningActor.OffensivePushCost * -1);
         WasLowTacticsMoraleAttack = false;
     }
 }
        public static void Prefix(AttackStackSequence __instance)
        {
            IsMoraleAttack = __instance.isMoraleAttack;
            Mod.Log.Debug($"AttackStackSequence:OnAdded:prefix - Recording IsMoraleAttack: {IsMoraleAttack}");

            AbstractActor   attacker  = __instance.owningActor;
            ActorInitiative actorInit = ActorInitiativeHolder.GetOrCreate(attacker);

            MoraleAttackMod = actorInit.calledShotMod;
        }
Ejemplo n.º 7
0
        public static void Prefix(AttackStackSequence __instance)
        {
            if (__instance.directorSequences == null || __instance.directorSequences.Count == 0)
            {
                return;
            }

            var target = __instance.directorSequences[0].chosenTarget;

            armorBeforeAttack     = target.SummaryArmorCurrent;
            structureBeforeAttack = target.SummaryStructureCurrent;
        }
Ejemplo n.º 8
0
        public static void Prefix(AttackStackSequence __instance, MessageCenterMessage message)
        {
            var attackCompleteMessage = message as AttackCompleteMessage;

            if (attackCompleteMessage == null || attackCompleteMessage.stackItemUID != __instance.SequenceGUID)
            {
                return;
            }

            if ((__instance.directorSequences[0].target is Mech mech) && PunchinOut.RollForEjectionResult(mech, attackCompleteMessage.attackSequence))
            {
                mech.EjectPilot(mech.GUID, attackCompleteMessage.stackItemUID, DeathMethod.PilotEjection, false);
            }
        }
Ejemplo n.º 9
0
            public static void Prefix(AttackStackSequence __instance)
            {
                var target = __instance.directorSequences[0].chosenTarget;

                mechArmorBeforeAttack     = target.SummaryArmorCurrent;
                mechStructureBeforeAttack = target.SummaryStructureCurrent;

                // get defender's current heat
                if (__instance.directorSequences[0].chosenTarget is Mech defender)
                {
                    LogDebug($"Defender pre-attack heat {defender.CurrentHeat}");
                    mechHeatBeforeAttack = defender.CurrentHeat;
                }
            }
Ejemplo n.º 10
0
        public static bool SkipProcessingAttack(AttackStackSequence __instance, MessageCenterMessage message)
        {
            var attackCompleteMessage = message as AttackCompleteMessage;

            if (attackCompleteMessage == null || attackCompleteMessage.stackItemUID != __instance.SequenceGUID)
            {
                return(true);
            }

            // can't do stuff with vehicles and buildings
            if (!(__instance.directorSequences[0].chosenTarget is Mech))
            {
                return(true);
            }

            return(__instance.directorSequences[0].chosenTarget?.GUID == null);
        }
Ejemplo n.º 11
0
        public static void Prefix(AttackStackSequence __instance)
        {
            if (__instance.directorSequences == null || __instance.directorSequences.Count == 0)
            {
                return;
            }

            var target = __instance.directorSequences[0].chosenTarget;

            armorBeforeAttack     = target.SummaryArmorCurrent;
            structureBeforeAttack = target.SummaryStructureCurrent;

            // get defender's current heat
            if (__instance.directorSequences[0].chosenTarget is Mech defender)
            {
                mechHeatBeforeAttack = defender.CurrentHeat;
            }
        }
        private void ResolveNextAttack()
        {
            this.timeSinceLastAttack += Time.deltaTime;
            if (this.timeSinceLastAttack > this.timeBetweenAttacks)
            {
                if (this.AttackingActors.Count > 0)
                {
                    AbstractActor actor = this.AttackingActors[0];
                    this.AttackingActors.RemoveAt(0);
                    Mod.Log.Info?.Write($"Ambush attack from actor: {actor.DistinctId()}");

                    // Find the closest target
                    ICombatant closestTarget = AllTargets[0];
                    foreach (ICombatant target in AllTargets)
                    {
                        if ((target.CurrentPosition - actor.CurrentPosition).magnitude <
                            (closestTarget.CurrentPosition - actor.CurrentPosition).magnitude)
                        {
                            closestTarget = target;
                        }
                    }

                    float currentRange = (closestTarget.CurrentPosition - actor.CurrentPosition).magnitude;
                    Mod.Log.Info?.Write($" -- ambush attack targets closest actor: {closestTarget.DistinctId()} at distance: {currentRange}");

                    List <Weapon> selectedWeapons = new List <Weapon>();
                    foreach (Weapon weapon in actor.Weapons)
                    {
                        if (weapon.CanFire && weapon.MinRange < currentRange)
                        {
                            selectedWeapons.Add(weapon);
                            Mod.Log.Info?.Write($" -- ambush weapon: {weapon.UIName}");
                        }
                    }

                    AttackStackSequence attackSequence = new AttackStackSequence(actor, closestTarget, actor.CurrentPosition, actor.CurrentRotation,
                                                                                 selectedWeapons);
                    ModState.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(attackSequence));
                }
                this.timeSinceLastAttack = 0f;
            }
        }
Ejemplo n.º 13
0
        public static void Prefix(AttackStackSequence __instance, MessageCenterMessage message)
        {
            var stopwatch = new Stopwatch();

            stopwatch.Start();

            if (ShouldSkipProcessing(__instance, message))
            {
                return;
            }

            if (!(message is AttackCompleteMessage attackCompleteMessage))
            {
                return;
            }

            var director = __instance.directorSequences;

            if (director == null)
            {
                return;
            }

            LogReport(new string('═', 46));
            LogReport($"{director[0].attacker.DisplayName} attacks {director[0].chosenTarget.DisplayName}");

            // get the attacker in case they have mech quirks
            AbstractActor defender = null;

            switch (director[0]?.chosenTarget)
            {
            case Vehicle _:
                defender = (Vehicle)director[0]?.chosenTarget;
                break;

            case Mech _:
                defender = (Mech)director[0]?.chosenTarget;
                break;
            }

            // a building?
            if (defender == null)
            {
                LogDebug("Not a mech or vehicle");
                return;
            }

            var attacker = director[0].attacker;
            var index    = GetActorIndex(defender);

            if (!ShouldPanic(defender, attackCompleteMessage.attackSequence))
            {
                return;
            }

            // automatically eject a klutzy pilot on knockdown with an additional roll failing on 13
            if (defender.IsFlaggedForKnockdown)
            {
                var defendingMech = (Mech)defender;
                if (defendingMech.pilot.pilotDef.PilotTags.Contains("pilot_klutz"))
                {
                    if (Random.Range(1, 100) == 13)
                    {
                        defender.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage
                                                                         (new ShowActorInfoSequence(defender, "WOOPS!", FloatieMessage.MessageNature.Debuff, false)));
                        LogReport("Very klutzy!");
                        return;
                    }
                }
            }

            // store saving throw
            // check it against panic
            // check it again ejection
            var savingThrow = SavingThrows.GetSavingThrow(defender, attacker);

            Mech_AddExternalHeat_Patch.heatDamage = 0;
            // panic saving throw
            if (SavingThrows.SavedVsPanic(defender, savingThrow))
            {
                return;
            }

            // stop if pilot isn't Panicked
            if (TrackedActors[index].PanicStatus != PanicStatus.Panicked)
            {
                return;
            }

            // eject saving throw
            if (SavingThrows.SavedVsEject(defender, savingThrow))
            {
                return;
            }

            // ejecting
            // random phrase
            if (modSettings.EnableEjectPhrases &&
                defender is Mech &&
                Random.Range(1, 100) <= modSettings.EjectPhraseChance)
            {
                var ejectMessage = ejectPhraseList[Random.Range(1, ejectPhraseList.Count)];
                defender.Combat.MessageCenter.PublishMessage(
                    new AddSequenceToStackMessage(
                        new ShowActorInfoSequence(defender, $"{ejectMessage}", FloatieMessage.MessageNature.Debuff, true)));
            }

            // remove effects, to prevent exceptions that occur for unknown reasons
            var combat           = UnityGameInstance.BattleTechGame.Combat;
            var effectsTargeting = combat.EffectManager.GetAllEffectsTargeting(defender);

            foreach (var effect in effectsTargeting)
            {
                // some effects removal throw, so silently drop them
                try
                {
                    defender.CancelEffect(effect);
                }
                catch
                {
                    // ignored
                }
            }

            if (modSettings.VehiclesCanPanic &&
                defender is Vehicle)
            {
                // make the regular Pilot Ejected floatie not appear, for this ejection
                var original = AccessTools.Method(typeof(BattleTech.VehicleRepresentation), "PlayDeathFloatie");
                var prefix   = AccessTools.Method(typeof(VehicleRepresentation), nameof(VehicleRepresentation.PrefixDeathFloatie));
                harmony.Patch(original, new HarmonyMethod(prefix));
                defender.EjectPilot(defender.GUID, attackCompleteMessage.stackItemUID, DeathMethod.PilotEjection, true);
                harmony.Unpatch(original, HarmonyPatchType.Prefix);
                defender.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(
                                                                 new ShowActorInfoSequence(defender, "Crew destroys vehicle!", FloatieMessage.MessageNature.Inspiration, true)));
            }
            else
            {
                defender.EjectPilot(defender.GUID, attackCompleteMessage.stackItemUID, DeathMethod.PilotEjection, false);
            }

            LogReport($"Ejected.  Runtime {stopwatch.ElapsedMilliSeconds}ms");

            if (!modSettings.CountAsKills)
            {
                return;
            }

            try
            {
                // this seems pretty convoluted
                var attackerPilot = combat.AllMechs.Where(mech => mech.pilot.Team.IsLocalPlayer)
                                    .Where(x => x.PilotableActorDef == attacker.PilotableActorDef).Select(y => y.pilot).FirstOrDefault();

                var statCollection = attackerPilot?.StatCollection;
                if (statCollection == null)
                {
                    return;
                }

                if (defender is Mech)
                {
                    // add UI icons.. and pilot history?   ... MechsKilled already incremented??
                    statCollection.Set("MechsKilled", attackerPilot.MechsKilled + 1);
                    var value = statCollection.GetStatistic("MechsEjected")?.Value <int?>();
                    if (statCollection.GetStatistic("MechsEjected") == null)
                    {
                        statCollection.AddStatistic("MechsEjected", 1);
                    }
                    else
                    {
                        statCollection.Set("MechsEjected", value + 1);
                    }

                    // add achievement kill (more complicated)
                    var combatProcessors = Traverse.Create(UnityGameInstance.BattleTechGame.Achievements).Field("combatProcessors").GetValue <AchievementProcessor[]>();
                    var combatProcessor  = combatProcessors.FirstOrDefault(x => x.GetType() == AccessTools.TypeByName("BattleTech.Achievements.CombatProcessor"));

                    // field is of type Dictionary<string, CombatProcessor.MechCombatStats>
                    var playerMechStats = Traverse.Create(combatProcessor).Field("playerMechStats").GetValue <IDictionary>();
                    if (playerMechStats != null)
                    {
                        foreach (DictionaryEntry kvp in playerMechStats)
                        {
                            if ((string)kvp.Key == attackerPilot.GUID)
                            {
                                Traverse.Create(kvp.Value).Method("IncrementKillCount").GetValue();
                            }
                        }
                    }
                }
                else if (modSettings.VehiclesCanPanic &&
                         defender is Vehicle)
                {
                    var value = statCollection.GetStatistic("VehiclesEjected")?.Value <int?>();
                    if (statCollection.GetStatistic("VehiclesEjected") == null)
                    {
                        statCollection.AddStatistic("VehiclesEjected", 1);
                    }
                    else
                    {
                        statCollection.Set("VehiclesEjected", value + 1);
                    }
                }
            }
            catch (Exception ex)
            {
                LogDebug(ex);
            }
        }
        static void Prefix(MechDFASequence __instance, MessageCenterMessage message, AttackStackSequence ___meleeSequence)
        {
            Mod.Log.Trace?.Write("MMS:OMC entered.");

            AttackCompleteMessage attackCompleteMessage = message as AttackCompleteMessage;

            Mod.MeleeLog.Info?.Write($"== Resolving cluster damage, instability, and unsteady on DFA attacker: {CombatantUtils.Label(__instance.OwningMech)} and " +
                                     $"target: {CombatantUtils.Label(__instance.DFATarget)}.");
            (MeleeAttack meleeAttack, Weapon fakeWeapon)seqState = ModState.GetMeleeSequenceState(__instance.SequenceGUID);

            if (attackCompleteMessage.stackItemUID == ___meleeSequence.SequenceGUID && seqState.meleeAttack != null)
            {
                // Check to see if the target was hit
                bool targetWasHit = false;
                foreach (AttackDirector.AttackSequence attackSequence in ___meleeSequence.directorSequences)
                {
                    if (!attackSequence.attackCompletelyMissed)
                    {
                        targetWasHit = true;
                        Mod.MeleeLog.Info?.Write($" -- AttackSequence: {attackSequence.stackItemUID} hit the target.");
                    }
                    else
                    {
                        Mod.MeleeLog.Info?.Write($" -- AttackSequence: {attackSequence.stackItemUID} missed the target.");
                    }
                }

                if (__instance.OwningMech.isHasStability() && !__instance.OwningMech.IsOrWillBeProne)
                {
                    // Attacker stability and unsteady - always applies as we're always a mech
                    if ((targetWasHit && seqState.meleeAttack.UnsteadyAttackerOnHit) ||
                        (!targetWasHit && seqState.meleeAttack.UnsteadyAttackerOnMiss))
                    {
                        Mod.MeleeLog.Info?.Write(" -- Forcing attacker to become unsteady from attack!");
                        __instance.OwningMech.DumpEvasion();
                    }
                }

                // Attacker cluster damage
                if (targetWasHit && !__instance.OwningMech.IsDead)
                {
                    // Make sure we use the attackers's damage table
                    ModState.ForceDamageTable = seqState.meleeAttack.AttackerTable;
                    if (seqState.meleeAttack.AttackerDamageClusters.Length > 0)
                    {
                        try
                        {
                            Mod.MeleeLog.Info?.Write($" -- Applying {seqState.meleeAttack.AttackerDamageClusters.Sum()} damage to attacker as {seqState.meleeAttack.AttackerDamageClusters.Length} clusters.");
                            AttackHelper.CreateImaginaryAttack(__instance.OwningMech, seqState.fakeWeapon, __instance.OwningMech, __instance.SequenceGUID,
                                                               seqState.meleeAttack.AttackerDamageClusters, DamageType.Melee, MeleeAttackType.Kick);
                        }
                        catch (Exception e)
                        {
                            Mod.Log.Error?.Write(e, "FAILED TO APPLY DFA DAMAGE TO ATTACKER");
                        }
                    }
                }

                if (targetWasHit)
                {
                    // Target mech stability and unsteady
                    if (__instance.DFATarget is Mech targetMech && targetMech.isHasStability() && !targetMech.IsProne)
                    {
                        if (seqState.meleeAttack.TargetInstability != 0)
                        {
                            Mod.MeleeLog.Info?.Write($" -- Adding {seqState.meleeAttack.TargetInstability} absolute instability to target.");
                            targetMech.AddAbsoluteInstability(seqState.meleeAttack.TargetInstability, StabilityChangeSource.Attack, "-1");
                        }

                        if (seqState.meleeAttack.OnTargetMechHitForceUnsteady)
                        {
                            Mod.MeleeLog.Info?.Write(" -- Forcing target to become unsteady from attack!");
                            targetMech.DumpEvasion();
                        }
                    }

                    // Target vehicle evasion damage
                    if (__instance.DFATarget is Vehicle || __instance.DFATarget.FakeVehicle() || __instance.DFATarget.NavalUnit())
                    {
                        AbstractActor targetActor = __instance.DFATarget as AbstractActor;
                        if (seqState.meleeAttack.OnTargetVehicleHitEvasionPipsRemoved != 0 && targetActor.EvasivePipsCurrent > 0)
                        {
                            Mod.MeleeLog.Info?.Write($" -- Removing {seqState.meleeAttack.OnTargetVehicleHitEvasionPipsRemoved} from target vehicle.");
                            int modifiedPips = targetActor.EvasivePipsCurrent - seqState.meleeAttack.OnTargetVehicleHitEvasionPipsRemoved;
                            if (modifiedPips < 0)
                            {
                                modifiedPips = 0;
                            }

                            targetActor.EvasivePipsCurrent = modifiedPips;
                            SharedState.Combat.MessageCenter.PublishMessage(new EvasiveChangedMessage(targetActor.GUID, targetActor.EvasivePipsCurrent));
                        }
                    }

                    // Target cluster damage - first attack was applied through melee weapon
                    if (seqState.meleeAttack.TargetDamageClusters.Length > 1 && !__instance.DFATarget.IsDead)
                    {
                        try
                        {
                            // Make sure we use the attackers's damage table
                            ModState.ForceDamageTable = seqState.meleeAttack.TargetTable;

                            // The target already got hit by the first cluster as the weapon damage. Only add the additional hits
                            float[] clusterDamage = seqState.meleeAttack.TargetDamageClusters.SubArray(1, seqState.meleeAttack.TargetDamageClusters.Length);
                            Mod.MeleeLog.Info?.Write($" -- Applying {clusterDamage.Sum()} damage to target as {clusterDamage.Length} clusters.");
                            AttackHelper.CreateImaginaryAttack(__instance.OwningMech, seqState.fakeWeapon, __instance.DFATarget, __instance.SequenceGUID, clusterDamage,
                                                               DamageType.Melee, MeleeAttackType.DFA);
                        }
                        catch (Exception e)
                        {
                            Mod.Log.Error?.Write(e, "FAILED TO APPLY DFA DAMAGE TO TARGET");
                        }
                    }
                }

                Mod.MeleeLog.Info?.Write($"== Done.");
            }

            // Restore the attacker's DFA damage
            __instance.OwningMech.StatCollection.Set <float>(ModStats.HBS_DFA_Self_Damage, ModState.OriginalDFASelfDamage);
            __instance.OwningMech.StatCollection.Set <bool>(ModStats.HBS_DFA_Causes_Self_Unsteady, true);

            // Reset melee state
            ModState.ForceDamageTable      = DamageTable.NONE;
            ModState.OriginalDFASelfDamage = 0f;
        }
 public static void Postfix(AttackStackSequence __instance)
 {
     IsMoraleAttack  = false;
     MoraleAttackMod = 0;
     Mod.Log.Debug($"AttackStackSequence:OnAdded:prefix - Resetting IsMoraleAttack");
 }
Ejemplo n.º 16
0
            public static void Prefix(AttackStackSequence __instance, MessageCenterMessage message)
            {
                try
                {
                    if (!(message is AttackCompleteMessage attackCompleteMessage) || attackCompleteMessage.stackItemUID != __instance.SequenceGUID)
                    {
                        return;
                    }

                    //@ToDo: Find "Stray Shot" targets and call rolls too?
                    List <string> allEffectedTargetIds = __instance.directorSequences[0].allAffectedTargetIds;
                    foreach (string id in allEffectedTargetIds)
                    {
                        ICombatant combatant = __instance.directorSequences[0].Director.Combat.FindCombatantByGUID(id);
                        Logger.Debug($"[AttackStackSequence_OnAttack_Complete_PREFIX] ------> AFFECTED TARGET: {combatant.DisplayName}");

                        if ((combatant is Mech mech) && Attack.TryPenetrateStressResistance(mech, attackCompleteMessage.attackSequence, out int stressLevel, out float ejectionChance))
                        {
                            if (Actor.RollForEjection(mech, stressLevel, ejectionChance))
                            {
                                mech.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(new ShowActorInfoSequence(mech, "PANICKED!", FloatieMessage.MessageNature.PilotInjury, true)));
                                mech.EjectPilot(mech.GUID, attackCompleteMessage.stackItemUID, DeathMethod.PilotEjection, false);

                                // Ejections as a direct result of an attack should count as kills
                                if (__instance.directorSequences[0].attacker is Mech attackingMech && attackingMech.GetPilot() != null)
                                {
                                    Pilot attackingPilot = attackingMech.GetPilot();
                                    attackingPilot.LogMechKillInflicted(-1, attackingPilot.GUID);
                                }
                            }
                            else
                            {
                                string floatieMessage = Miscellaneous.GetStressLevelString(stressLevel);
                                mech.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(new ShowActorInfoSequence(mech, floatieMessage, FloatieMessage.MessageNature.PilotInjury, true)));
                            }
                        }
                    }



                    /* Only rolling on chosen target, ignoring stray shots...
                     * if ((__instance.directorSequences[0].chosenTarget is Mech mech) && Attack.TryPenetrateStressResistance(mech, attackCompleteMessage.attackSequence, out int stressLevel, out float ejectionChance))
                     * {
                     *  if (Actor.RollForEjection(mech, stressLevel, ejectionChance))
                     *  {
                     *      mech.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(new ShowActorInfoSequence(mech, "PANICKED!", FloatieMessage.MessageNature.PilotInjury, true)));
                     *      mech.EjectPilot(mech.GUID, attackCompleteMessage.stackItemUID, DeathMethod.PilotEjection, false);
                     *
                     *      // Ejections as a direct result of an attack should count as kills
                     *      if (__instance.directorSequences[0].attacker is Mech attackingMech && attackingMech.GetPilot() != null)
                     *      {
                     *          Pilot attackingPilot = attackingMech.GetPilot();
                     *          attackingPilot.LogMechKillInflicted(-1, attackingPilot.GUID);
                     *      }
                     *  }
                     *  else
                     *  {
                     *      string floatieMessage = Miscellaneous.GetStressLevelString(stressLevel);
                     *      mech.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(new ShowActorInfoSequence(mech, floatieMessage, FloatieMessage.MessageNature.PilotInjury, true)));
                     *  }
                     * }
                     */
                }
                catch (Exception e)
                {
                    Logger.Error(e);
                }
            }
Ejemplo n.º 17
0
            public static void Prefix(AttackStackSequence __instance, MessageCenterMessage message)
            {
                var stopwatch = new Stopwatch();

                stopwatch.Start();

                if (SkipProcessingAttack(__instance, message))
                {
                    return;
                }

                if (!(message is AttackCompleteMessage attackCompleteMessage))
                {
                    return;
                }

                var director = __instance.directorSequences;

                if (director == null)
                {
                    return;
                }

                LogDebug(new string('#', 46));
                LogDebug($"{director[0].attacker.DisplayName} attacks {director[0].chosenTarget.DisplayName}");

                // get the attacker in case they have mech quirks
                var  defender = (Mech)director[0]?.chosenTarget;
                Mech attacker = null;

                if (director[0].attacker is Mech)
                {
                    attacker = (Mech)director[0].attacker;
                }

                var index = GetPilotIndex(defender);

                if (!ShouldPanic(defender, attackCompleteMessage.attackSequence))
                {
                    return;
                }

                // automatically eject a klutzy pilot on an additional roll yielding 13
                if (defender.IsFlaggedForKnockdown && defender.pilot.pilotDef.PilotTags.Contains("pilot_klutz"))
                {
                    if (Random.Range(1, 100) == 13)
                    {
                        defender.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage
                                                                         (new ShowActorInfoSequence(defender, "WOOPS!", FloatieMessage.MessageNature.Debuff, false)));
                        LogDebug("Very klutzy!");
                        return;
                    }
                }

                // store saving throw
                // check it against panic
                // check it again ejection
                var savingThrow = GetSavingThrow(defender, attacker);

                heatDamage = 0;
                // panic saving throw
                if (SavedVsPanic(defender, savingThrow))
                {
                    return;
                }

                // stop if pilot isn't Panicked
                if (trackedPilots[index].panicStatus != PanicStatus.Panicked)
                {
                    return;
                }

                // eject saving throw
                if (SavedVsEject(defender, savingThrow))
                {
                    return;
                }

                // ejecting
                // random phrase
                try
                {
                    if (modSettings.EnableEjectPhrases &&
                        Random.Range(1, 100) <= modSettings.EjectPhraseChance)
                    {
                        var ejectMessage = ejectPhraseList[Random.Range(1, ejectPhraseList.Count)];
                        defender.Combat.MessageCenter.PublishMessage(
                            new AddSequenceToStackMessage(
                                new ShowActorInfoSequence(defender, $"{ejectMessage}", FloatieMessage.MessageNature.Debuff, true)));
                    }
                }
                catch (Exception e)
                {
                    LogError(e);
                }

                // remove effects, to prevent exceptions that occur for unknown reasons
                try
                {
                    var           combat           = UnityGameInstance.BattleTechGame.Combat;
                    List <Effect> effectsTargeting = combat.EffectManager.GetAllEffectsTargeting(defender);
                    foreach (var effect in effectsTargeting)
                    {
                        // some effects removal throw, so silently drop them
                        try
                        {
                            defender.CancelEffect(effect);
                        }
                        // ReSharper disable once EmptyGeneralCatchClause
                        catch
                        {
                        }
                    }
                }
                catch (Exception e)
                {
                    LogError(e);
                }

                defender.EjectPilot(defender.GUID, attackCompleteMessage.stackItemUID, DeathMethod.PilotEjection, false);
                LogDebug($"Ejected.  Runtime {stopwatch.ElapsedMilliSeconds}ms");
            }