public static void Prefix(MechDFASequence __instance, ref List <Weapon> ___requestedWeapons)
        {
            if (___requestedWeapons.Count < 1)
            {
                return;
            }

            AbstractActor actor       = __instance.owningActor;
            ICombatant    MeleeTarget = __instance.DFATarget;

            bool TargetIsDead            = MeleeTarget.IsDead;
            bool TargetIsFlaggedForDeath = MeleeTarget.IsFlaggedForDeath;

            Logger.Info("[MechMeleeSequence_OnMeleeComplete_PREFIX] TargetIsDead: " + TargetIsDead);
            Logger.Info("[MechMeleeSequence_OnMeleeComplete_PREFIX] TargetIsFlaggedForDeath: " + TargetIsFlaggedForDeath);

            if (TargetIsDead || TargetIsFlaggedForDeath)
            {
                ___requestedWeapons.Clear();
                actor.Combat.MessageCenter.PublishMessage(new FloatieMessage(actor.GUID, actor.GUID, "SUSPENDED SUPPORT WEAPONS", FloatieMessage.MessageNature.Neutral));
            }
            else
            {
                DontShootTheDead.BuildSupportWeaponSequence = true;
                Traverse BuildWeaponDirectorSequence = Traverse.Create(__instance).Method("BuildWeaponDirectorSequence");
                BuildWeaponDirectorSequence.GetValue();
            }
        }
Exemple #2
0
            public static void Prefix(MechDFASequence __instance, ref MessageCenterMessage message)
            {
                // Only remove entrenched if the attach actually did hit?

                /*
                 * AttackCompleteMessage attackCompleteMessage = (AttackCompleteMessage)message;
                 * if (attackCompleteMessage.attackSequence.attackCompletelyMissed)
                 * {
                 *  return;
                 * }
                 */

                // Get melee target
                ICombatant DFATarget = (ICombatant)AccessTools.Property(typeof(MechDFASequence), "DFATarget").GetValue(__instance, null);

                if (DFATarget is Mech TargetMech)
                {
                    // Remove Entrenched
                    if (TargetMech.IsEntrenched)
                    {
                        Logger.Debug("[MechDFASequence_OnMeleeComplete_PREFIX] Removing Entrenched from target");
                        TargetMech.IsEntrenched = false;
                        TargetMech.Combat.MessageCenter.PublishMessage(new FloatieMessage(TargetMech.GUID, TargetMech.GUID, "LOST: ENTRENCHED", FloatieMessage.MessageNature.Debuff));
                    }
                    else
                    {
                        Logger.Debug("[MechDFASequence_OnMeleeComplete_PREFIX] Target wasn't entrenched");
                    }
                }
            }
 static void Prefix(MechDFASequence __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");
     }
 }
        static void Prefix(MechDFASequence __instance)
        {
            // Reset melee state
            ModState.ForceDamageTable = DamageTable.NONE;

            Mod.MeleeLog.Debug?.Write("Regenerating melee support weapons hit locations...");
            Traverse BuildWeaponDirectorSequenceT = Traverse.Create(__instance).Method("BuildWeaponDirectorSequence");

            BuildWeaponDirectorSequenceT.GetValue();
            Mod.MeleeLog.Debug?.Write(" -- Done!");
        }
        public static bool Prefix(MechDFASequence __instance, ref List <Weapon> ___requestedWeapons)
        {
            Logger.Info("[MechDFASequence_BuildWeaponDirectorSequence_PREFIX] DontShootTheDead.BuildSupportWeaponSequence: " + DontShootTheDead.BuildSupportWeaponSequence);

            // Only build a weapon sequence if melee didn't already kill the target
            if (DontShootTheDead.BuildSupportWeaponSequence)
            {
                // Will still only build a weapon sequence if ___requestedWeapons.Count is > 0
                return(true);
            }
            else
            {
                return(false);
            }
        }
        static void Postfix(MechDFASequence __instance)
        {
            Mod.Log.Trace?.Write("MMS:CO - entered.");

            // Base method checks for target knockdown. Do the same for the attacker.
            if (!__instance.OwningMech.IsDead)
            {
                __instance.OwningMech.CheckForInstability();
                __instance.OwningMech.HandleKnockdown(__instance.RootSequenceGUID, __instance.owningActor.GUID, Vector2.one, null);
            }

            // Invalidate our melee state as we're done
            ModState.ForceDamageTable = DamageTable.NONE;

            ModState.InvalidateState(__instance.OwningMech);
        }
        //Init(Mech mech, ICombatant DFATarget, List<Weapon> requestedWeapons, Vector3 finalJumpDestination, Quaternion finalJumpRotation)
        static void Postfix(MechDFASequence __instance, Mech mech, ICombatant DFATarget,
                            List <Weapon> requestedWeapons, Vector3 finalJumpDestination)
        {
            try
            {
                // Find the selectedAttack we should use for this sequence
                MeleeAttack selectedAttack = ModState.GetSelectedAttack(mech);
                if (selectedAttack == null)
                {
                    Mod.MeleeLog.Warn?.Write($"Melee sequence {__instance.SequenceGUID} has no pre-selected attack state, will have to autoselected. Let Frost know as this should not happen!");
                    MeleeState meleeState = ModState.AddorUpdateMeleeState(mech, finalJumpDestination, DFATarget as AbstractActor);
                    if (meleeState == null)
                    {
                        Mod.Log.Error?.Write($"Could not build DFA state for selected melee attack - this should NEVER happen!");
                        return;
                    }
                    selectedAttack = meleeState.DFA;
                }

                if (selectedAttack == null || !selectedAttack.IsValid || !(selectedAttack is DFAAttack))
                {
                    Mod.Log.Error?.Write($"Could not select a valid attack for the selected sequence - this should NEVER happen!");
                    return;
                }

                // Check to see if we have an imaginary weapon to use; if not create it
                (Weapon meleeWeapon, Weapon dfaWeapon)weapons = ModState.GetFakedWeapons(mech);

                // Create the weapon + representation
                ModState.AddOrUpdateMeleeSequenceState(__instance.SequenceGUID, selectedAttack, weapons.dfaWeapon);

                // TODO: Filter selected weapons by melee
                StringBuilder sb = new StringBuilder();
                foreach (Weapon weapon in requestedWeapons)
                {
                    sb.Append(weapon.UIName);
                    sb.Append(",");
                }
                Mod.MeleeLog.Info?.Write($"  -- Initial requested weapons: {sb}");
            }
            catch (Exception e)
            {
                Mod.Log.Error?.Write(e, $"Failed to initialize DFA sequence {__instance.SequenceGUID}!");
            }
        }
Exemple #8
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);
            }
        }
        static void Postfix(MechDFASequence __instance, List <Weapon> ___requestedWeapons)
        {
            // TODO: If this happens before the above... need to grab the selected melee type from state
            Mod.MeleeLog.Info?.Write($"Setting current melee type to: {MeleeAttackType.DFA} and weapon to: {__instance.OwningMech.DFAWeapon.UIName}");

            (MeleeAttack meleeAttack, Weapon fakeWeapon)seqState = ModState.GetMeleeSequenceState(__instance.SequenceGUID);
            if (seqState.meleeAttack != null)
            {
                // Modify the owning mech DFA melee weapon to do the 'first' hit
                float targetDamage = seqState.meleeAttack.TargetDamageClusters?.Length > 0 ?
                                     seqState.meleeAttack.TargetDamageClusters[0] : 0;
                __instance.OwningMech.DFAWeapon.StatCollection.Set <float>(ModStats.HBS_Weapon_DamagePerShot, targetDamage);
                __instance.OwningMech.DFAWeapon.StatCollection.Set <float>(ModStats.HBS_Weapon_Instability, 0);
                Mod.MeleeLog.Info?.Write($"For {CombatantUtils.Label(__instance.OwningMech)} set DFA weapon damage: {targetDamage}  and instability: {seqState.meleeAttack.TargetInstability}");

                // Cache the attacker's original DFASelfDamage value and set it to zero, so we can apply our own damage
                ModState.OriginalDFASelfDamage = __instance.OwningMech.StatCollection.GetValue <float>(ModStats.HBS_DFA_Self_Damage);
                __instance.OwningMech.StatCollection.Set <float>(ModStats.HBS_DFA_Self_Damage, 0f);
                __instance.OwningMech.StatCollection.Set <bool>(ModStats.HBS_DFA_Causes_Self_Unsteady, false);

                // Make sure we use the target's damage table
                ModState.ForceDamageTable = seqState.meleeAttack.TargetTable;

                // Filter any weapons from requested weapons. This works because BuildMeleeDirectorSequence is called immediately before BuildWeaponDirectorSequence
                if (Mod.Config.Melee.FilterCanUseInMeleeWeaponsByAttack)
                {
                    Mod.MeleeLog.Debug?.Write($"Filtering DFA weapons by attack type: {seqState.meleeAttack.Label}");
                    List <Weapon> allowedWeapons = new List <Weapon>();
                    foreach (Weapon weapon in ___requestedWeapons)
                    {
                        if (seqState.meleeAttack.IsRangedWeaponAllowed(weapon))
                        {
                            Mod.MeleeLog.Debug?.Write($" -- Weapon: {weapon.UIName} is allowed by melee type.");
                            allowedWeapons.Add(weapon);
                        }
                    }
                    ___requestedWeapons.Clear();
                    ___requestedWeapons.AddRange(allowedWeapons);
                }
            }
        }
        static void Prefix(MechDFASequence __instance)
        {
            if (__instance == null || __instance.OwningMech == null || __instance.OwningMech.GetPilot() == null)
            {
                return; // Nothing to do
            }
            List <Ability> passives = __instance.OwningMech.GetPilot().PassiveAbilities;

            if (passives.Count > 0)
            {
                foreach (Ability ability in passives)
                {
                    if (ability.Def.Description.Id.Equals(Mod.Config.Abilities.JuggernautId, StringComparison.InvariantCultureIgnoreCase))
                    {
                        Mod.Log.Info?.Write("Pilot has Juggernaut, bracing after DFA attack.");
                        __instance.OwningMech.BracedLastRound = true;
                        __instance.OwningMech.ApplyInstabilityReduction(StabilityChangeSource.Bracing);
                    }
                }
            }

            return;
        }
        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 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);
                }
            }
 public static void Prefix(MechDFASequence __instance)
 {
     // Reset flag for weapon sequence
     DontShootTheDead.BuildSupportWeaponSequence = false;
 }
 public static void Prefix(MechDFASequence __instance, ref float timeout)
 {
     // Was a hardcoded 10f
     timeout = 6f;
     Logger.Debug("[MechDFASequence_DelayFireWeapons_PREFIX] timeout: " + timeout);
 }