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); } }
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); }
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); } }
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; }
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); } }