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(); } }
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}!"); } }
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); }