Example #1
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);
            }
        }
Example #2
0
        public static bool Prefix(AttackDirector __instance, MessageCenterMessage message)
        {
            AttackCompleteMessage attackCompleteMessage = (AttackCompleteMessage)message;
            int sequenceId = attackCompleteMessage.sequenceId;

            AttackDirector.AttackSequence attackSequence = __instance.GetAttackSequence(sequenceId);
            if (attackSequence == null)
            {
                return(true);
            }
            CustomAmmoCategoriesLog.Log.LogWrite($"Try jamm weapon of " + attackSequence.attacker.DisplayName + "\n");
            JammingEnabler.jammQueue.Enqueue(attackSequence.attacker);
            return(true);
        }
Example #3
0
        public static void Postfix(MechDFASequence __instance, MessageCenterMessage message)
        {
            AttackCompleteMessage attackCompleteMessage = message as AttackCompleteMessage;

            Mod.Log.Info($"DFA success ratio: {attackCompleteMessage.attackSequence.RatioSuccessfulHits}");
            if (!attackCompleteMessage.attackSequence.attackCompletelyMissed)
            {
                Mod.Log.Debug($" DFA attack by {CombatantUtils.Label(__instance.OwningMech)} vs. {CombatantUtils.Label(__instance.DFATarget)} succeeded.");

                // Check for source falling
                float sourceSkillMulti = __instance.OwningMech.PilotCheckMod(Mod.Config.Melee.SkillMulti);
                bool  sourcePassed     = CheckHelper.DidCheckPassThreshold(Mod.Config.Melee.MadeDFAFallChance, __instance.OwningMech, sourceSkillMulti, ModConfig.FT_Melee_DFA);
                if (!sourcePassed)
                {
                    Mod.Log.Info($"Source actor: {CombatantUtils.Label(__instance.OwningMech)} failed pilot check from DFA, forcing fall.");
                    MechHelper.AddFallingSequence(__instance.OwningMech, __instance, ModConfig.FT_Melee_DFA);
                }

                // Check for target falling
                if (__instance.DFATarget is Mech targetMech)
                {
                    float targetSkillMulti = targetMech.PilotCheckMod(Mod.Config.Melee.SkillMulti);
                    bool  targetPassed     = CheckHelper.DidCheckPassThreshold(Mod.Config.Melee.HitByDFAFallChance, targetMech, targetSkillMulti, ModConfig.FT_Melee_DFA);
                    if (!targetPassed)
                    {
                        Mod.Log.Info($"Target actor: {CombatantUtils.Label(targetMech)} failed pilot check from DFA, forcing fall.");
                        MechHelper.AddFallingSequence(targetMech, __instance, ModConfig.FT_Melee_DFA);
                    }
                }
                else
                {
                    Mod.Log.Debug($"Target {CombatantUtils.Label(__instance.DFATarget)} is not a mech, cannot fall - skipping.");
                }
            }
            else
            {
                Mod.Log.Debug($" DFA attack by {CombatantUtils.Label(__instance.OwningMech)} vs. {CombatantUtils.Label(__instance.DFATarget)} failed.");

                // Force the source mech to fall
                Mod.Log.Info($"Source actor: {CombatantUtils.Label(__instance.OwningMech)} failed DFA attack, forcing fall.");
                MechHelper.AddFallingSequence(__instance.OwningMech, __instance, ModConfig.FT_Melee_DFA);
            }
        }
Example #4
0
        public static void Postfix(MechMeleeSequence __instance, MessageCenterMessage message)
        {
            AttackCompleteMessage attackCompleteMessage = message as AttackCompleteMessage;

            if (__instance.selectedMeleeType == MeleeAttackType.Kick || __instance.selectedMeleeType == MeleeAttackType.Stomp)
            {
                if (attackCompleteMessage.attackSequence.attackCompletelyMissed)
                {
                    Mod.Log.Debug($" Kick attack by {CombatantUtils.Label(__instance.OwningMech)} vs. {CombatantUtils.Label(__instance.MeleeTarget)} failed.");
                    // Check for source falling
                    float sourceMulti  = __instance.OwningMech.PilotCheckMod(Mod.Config.Melee.SkillMulti);
                    bool  sourcePassed = CheckHelper.DidCheckPassThreshold(Mod.Config.Melee.MissedKickFallChance, __instance.OwningMech, sourceMulti, ModConfig.FT_Melee_Kick);
                    if (!sourcePassed)
                    {
                        Mod.Log.Info($"Source actor: {CombatantUtils.Label(__instance.OwningMech)} failed pilot check from missed kick, forcing fall.");
                        MechHelper.AddFallingSequence(__instance.OwningMech, __instance, ModConfig.FT_Melee_Kick);
                    }
                }
                else
                {
                    Mod.Log.Debug($" Kick attack by {CombatantUtils.Label(__instance.OwningMech)} vs. {CombatantUtils.Label(__instance.MeleeTarget)} succeeded.");
                    // Check for target falling
                    if (__instance.MeleeTarget is Mech targetMech)
                    {
                        float targetMulti  = targetMech.PilotCheckMod(Mod.Config.Melee.SkillMulti);
                        bool  targetPassed = CheckHelper.DidCheckPassThreshold(Mod.Config.Melee.HitByKickFallChance, targetMech, targetMulti, ModConfig.FT_Melee_Kick);
                        if (!targetPassed)
                        {
                            Mod.Log.Info($"Target actor: {CombatantUtils.Label(targetMech)} failed pilot check from kick, forcing fall.");
                            MechHelper.AddFallingSequence(targetMech, __instance, ModConfig.FT_Melee_Kick);
                        }
                    }
                    else
                    {
                        Mod.Log.Debug($"Target actor: {CombatantUtils.Label(__instance.MeleeTarget)} is not a mech, cannot fall - skipping.");
                    }
                }
            }

            if (__instance.selectedMeleeType == MeleeAttackType.Charge || __instance.selectedMeleeType == MeleeAttackType.Tackle)
            {
                if (!attackCompleteMessage.attackSequence.attackCompletelyMissed)
                {
                    Mod.Log.Debug($" Charge attack by {CombatantUtils.Label(__instance.OwningMech)} vs. {CombatantUtils.Label(__instance.MeleeTarget)} succeeded.");

                    // Check for source falling
                    float sourceSkillMulti = __instance.OwningMech.PilotCheckMod(Mod.Config.Melee.SkillMulti);
                    bool  sourcePassed     = CheckHelper.DidCheckPassThreshold(Mod.Config.Melee.MadeChargeFallChance, __instance.OwningMech, sourceSkillMulti, ModConfig.FT_Melee_Charge);
                    if (!sourcePassed)
                    {
                        Mod.Log.Info($"Source actor: {CombatantUtils.Label(__instance.OwningMech)} failed pilot check from charge, forcing fall.");
                        MechHelper.AddFallingSequence(__instance.OwningMech, __instance, ModConfig.FT_Melee_Charge);
                    }

                    // Check for target falling
                    if (__instance.MeleeTarget is Mech targetMech)
                    {
                        float targetSkillMulti = targetMech.PilotCheckMod(Mod.Config.Melee.SkillMulti);
                        bool  targetPassed     = CheckHelper.DidCheckPassThreshold(Mod.Config.Melee.HitByChargeFallChance, targetMech, targetSkillMulti, ModConfig.FT_Melee_Charge);
                        if (!targetPassed)
                        {
                            Mod.Log.Info($"Target actor: {CombatantUtils.Label(targetMech)} failed pilot check from charge, forcing fall.");
                            MechHelper.AddFallingSequence(targetMech, __instance, ModConfig.FT_Melee_Charge);
                        }
                    }
                    else
                    {
                        Mod.Log.Debug($"Target actor: {CombatantUtils.Label(__instance.MeleeTarget)} is not a mech, cannot fall - skipping.");
                    }
                }
                else
                {
                    Mod.Log.Debug($" Charge attack by {CombatantUtils.Label(__instance.OwningMech)} vs. {CombatantUtils.Label(__instance.MeleeTarget)} failed.");
                }
            }
        }
        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;
        }
Example #6
0
            public static void Prefix(MechDFASequence __instance, MessageCenterMessage message)
            {
                try
                {
                    AttackCompleteMessage attackCompleteMessage = (AttackCompleteMessage)message;
                    if (attackCompleteMessage.attackSequence.attackCompletelyMissed)
                    {
                        Logger.Debug("[MechDFASequence_OnMeleeComplete_PREFIX] Attack did no damage! Aborting...");
                        return;
                    }

                    Pilot pilot = __instance.owningActor.GetPilot();
                    if (pilot.IsJuggernaut())
                    {
                        ICombatant DFATarget = (ICombatant)AccessTools.Property(typeof(MechDFASequence), "DFATarget").GetValue(__instance, null);

                        if (DFATarget.IsDead || DFATarget.IsFlaggedForDeath)
                        {
                            return;
                        }

                        // IMPORTANT! At this point any stab dmg is already applied to the target, normalized by entrenched or terrain...
                        if (DFATarget is Mech TargetMech)
                        {
                            Logger.Debug("[MechDFASequence_OnMeleeComplete_PREFIX] DFATarget.IsUnsteady: " + TargetMech.IsUnsteady);
                            Logger.Debug("[MechDFASequence_OnMeleeComplete_PREFIX] DFATarget.MaxStability: " + TargetMech.MaxStability);
                            Logger.Debug("[MechDFASequence_OnMeleeComplete_PREFIX] DFATarget.CurrentStability: " + TargetMech.CurrentStability);

                            /*
                             * // Additional stability damage depending on distance jumped?
                             * float additionalStabilityDamage = Utilities.GetAdditionalStabilityDamageFromJumpDistance(__instance.OwningMech, TargetMech, false);
                             *
                             * // Using the attacker from the sequence is more reliable than __instance.OwningMech?
                             * //Mech AttackingMech = attackCompleteMessage.attackSequence.attacker as Mech;
                             * //float additionalStabilityDamage = Utilities.GetAdditionalStabilityDamageFromJumpDistance(AttackingMech, TargetMech, false);
                             *
                             * Logger.Debug("[MechDFASequence_OnMeleeComplete_PREFIX] Apply additional stability damage from distance jumped: " + additionalStabilityDamage);
                             *
                             * TargetMech.AddAbsoluteInstability(additionalStabilityDamage, StabilityChangeSource.NotSet, __instance.owningActor.GUID);
                             * Logger.Debug("[MechDFASequence_OnMeleeComplete_PREFIX] DFATarget.CurrentStability: " + TargetMech.CurrentStability);
                             */

                            if (TargetMech.CurrentStability >= TargetMech.MaxStability)
                            {
                                Logger.Debug("[MechDFASequence_OnMeleeComplete_PREFIX] Mech should be knocked down regardless of being unsteady before...");
                                TargetMech.FlagForKnockdown();

                                if (!TargetMech.IsUnsteady)
                                {
                                    // Push message out
                                    TargetMech.Combat.MessageCenter.PublishMessage(new FloatieMessage(TargetMech.GUID, TargetMech.GUID, "OFF BALANCE", FloatieMessage.MessageNature.Debuff));
                                }
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    Logger.Error(e);
                }
            }
            static void Postfix(MechMeleeSequence __instance, MessageCenterMessage message)
            {
                try
                {
                    AttackCompleteMessage attackCompleteMessage = (AttackCompleteMessage)message;
                    if (attackCompleteMessage.attackSequence.attackCompletelyMissed)
                    {
                        Logger.Debug("[MechMeleeSequence_OnMeleeComplete_POSTFIX] Missed! Aborting...");
                        return;
                    }

                    Pilot pilot = __instance.owningActor.GetPilot();
                    if (pilot.IsJuggernaut())
                    {
                        // Get melee target
                        ICombatant MeleeTarget = (ICombatant)AccessTools.Property(typeof(MechMeleeSequence), "MeleeTarget").GetValue(__instance, null);

                        // Reapplying "MeleeHitPushBackPhases" here as it doesn't seem to work anymore when only defined in AbilityDef
                        (MeleeTarget as AbstractActor).ForceUnitOnePhaseDown(__instance.owningActor.GUID, __instance.SequenceGUID, false);

                        Logger.Debug("[MechMeleeSequence_OnMeleeComplete_POSTFIX] Fields.JuggernautCharges: " + Fields.JuggernautCharges);

                        if (Fields.JuggernautCharges)
                        {
                            // Charge and tackle causes slight instability

                            //float stabilityDamageSelf = __instance.OwningMech.GetMinStability(0, -1);
                            //__instance.OwningMech.AddAbsoluteInstability(stabilityDamageSelf, StabilityChangeSource.NotSet, __instance.owningActor.GUID);

                            float resultingStability = __instance.OwningMech.GetMinStability(__instance.OwningMech.CurrentStability, -1);
                            __instance.OwningMech.StatCollection.Set <float>("Stability", resultingStability);
                            __instance.OwningMech.NeedsInstabilityCheck = true;

                            if (MeleeTarget.IsDead || MeleeTarget.IsFlaggedForDeath)
                            {
                                return;
                            }

                            if (MeleeTarget is Mech TargetMech)
                            {
                                // Remove Entrenched from target when charging
                                if (TargetMech.IsEntrenched)
                                {
                                    Logger.Debug("[MechMeleeSequence_OnMeleeComplete_POSTFIX] Removing Entrenched from target");
                                    TargetMech.IsEntrenched = false;
                                    TargetMech.Combat.MessageCenter.PublishMessage(new FloatieMessage(TargetMech.GUID, TargetMech.GUID, "LOST: ENTRENCHED", FloatieMessage.MessageNature.Debuff));
                                }

                                /*
                                 * // IMPORTANT! At this point any stab dmg is already applied to the target, normalized by entrenched or terrain...
                                 * // Additional stability damage depending on distance?
                                 * float additionalStabilityDamage = Utilities.GetAdditionalStabilityDamageFromSprintDistance(__instance.OwningMech, TargetMech, false);
                                 *
                                 * // Using the attacker from the sequence is more reliable than __instance.OwningMech?
                                 * //Mech AttackingMech = attackCompleteMessage.attackSequence.attacker as Mech;
                                 * //float additionalStabilityDamage = Utilities.GetAdditionalStabilityDamageFromSprintDistance(AttackingMech, TargetMech, false);
                                 *
                                 * Logger.Debug("[MechMeleeSequence_OnMeleeComplete_POSTFIX] Apply additional stability damage from distance sprinted: " + additionalStabilityDamage);
                                 *
                                 * TargetMech.AddAbsoluteInstability(additionalStabilityDamage, StabilityChangeSource.NotSet, __instance.owningActor.GUID);
                                 * Logger.Debug("[MechMeleeSequence_OnMeleeComplete_POSTFIX] MeleeTarget.CurrentStability: " + TargetMech.CurrentStability);
                                 */
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    Logger.Error(e);
                }
            }