private bool ValidateAttack(Mech attacker, AbstractActor target, HashSet <MeleeAttackType> validAnimations) { ActorMeleeCondition meleeCondition = ModState.GetMeleeCondition(attacker); if (!meleeCondition.CanUsePhysicalAttack()) { Mod.MeleeLog.Info?.Write($"Attacker cannot make a physical attack, skipping."); return(false); } // If no punch - we're not a valid attack. if (!validAnimations.Contains(MeleeAttackType.Punch)) { Mod.MeleeLog.Info?.Write("Animations do not include a punch, cannot use physical weapon."); return(false); } if (target.UnaffectedPathing()) { Mod.MeleeLog.Info?.Write($"Target is unaffected by pathing, likely a VTOL or LAM in flight. Cannot melee it!"); return(false); } // If distance > walkSpeed, disable kick/physical weapon/punch if (!state.HasWalkAttackNodes) { Mod.MeleeLog.Info?.Write($"No walking nodes found for melee attack!"); return(false); } Mod.MeleeLog.Info?.Write("PHYSICAL WEAPON ATTACK validated"); return(true); }
private bool ValidateAttack(Mech attacker, AbstractActor target) { ActorMeleeCondition meleeCondition = ModState.GetMeleeCondition(attacker); if (!meleeCondition.CanDFA()) { Mod.MeleeLog.Info?.Write($"Attacker cannot DFA, skipping."); return(false); } if (!attacker.CanDFATargetFromPosition(target, attacker.CurrentPosition)) { Mod.MeleeLog.Info?.Write($"Attacker unable to DFA target from their position."); return(false); } if (target.UnaffectedPathing()) { Mod.MeleeLog.Info?.Write($"Target is unaffected by pathing, likely a VTOL or LAM in flight. Cannot melee it!"); return(false); } // No damage check - by rules, you can DFA? Mod.MeleeLog.Info?.Write("DFA ATTACK validated"); return(true); }
public static float PhysicalWeaponInstability(this Mech mech) { ActorMeleeCondition attackerCondition = ModState.GetMeleeCondition(mech); if (!attackerCondition.CanUsePhysicalAttack()) { return(0); } // 0 is a signal that there's no divisor float tonnageMulti = mech.StatCollection.ContainsStatistic(ModStats.PhysicalWeaponTargetInstability) && mech.StatCollection.GetValue <float>(ModStats.PhysicalWeaponTargetInstability) > 0 ? mech.StatCollection.GetValue <float>(ModStats.PhysicalWeaponTargetInstability) : Mod.Config.Melee.PhysicalWeapon.DefaultInstabilityPerAttackerTon; float raw = (float)Math.Ceiling(tonnageMulti * mech.tonnage); Mod.MeleeLog.Debug?.Write($"PHYSICAL WEAPON instability => tonnageMulti: {tonnageMulti} x attacker tonnage: {mech.tonnage} = raw: {raw}"); // Modifiers float mod = mech.StatCollection.ContainsStatistic(ModStats.PhysicalWeaponTargetInstabilityMod) ? mech.StatCollection.GetValue <int>(ModStats.PhysicalWeaponTargetInstabilityMod) : 0f; float multi = mech.StatCollection.ContainsStatistic(ModStats.PhysicalWeaponTargetInstabilityMulti) ? mech.StatCollection.GetValue <float>(ModStats.PhysicalWeaponTargetInstabilityMulti) : 1f; // Roll up final damage float final = (float)Math.Ceiling((raw + mod) * multi); Mod.MeleeLog.Debug?.Write($" - Target instability => final: {final} = (raw: {raw} + mod: {mod}) x multi: {multi}"); return(final); }
private bool ValidateAttack(Mech attacker, AbstractActor target, HashSet <MeleeAttackType> validAnimations) { ActorMeleeCondition meleeCondition = ModState.GetMeleeCondition(attacker); if (!meleeCondition.CanKick()) { Mod.MeleeLog.Info?.Write($"Attacker cannot kick, skipping."); return(false); } // If neither kick (mech) or stomp (vehicle) - we're not a valid attack. if (!validAnimations.Contains(MeleeAttackType.Kick) && !validAnimations.Contains(MeleeAttackType.Stomp)) { Mod.MeleeLog.Info?.Write("Animations do not include a kick or stomp, cannot kick."); return(false); } if (target.UnaffectedPathing()) { Mod.MeleeLog.Info?.Write($"Target is unaffected by pathing, likely a VTOL or LAM in flight. Cannot melee it!"); return(false); } // If distance > walkSpeed, disable kick/physical weapon/punch if (!state.HasWalkAttackNodes) { Mod.MeleeLog.Info?.Write($"No walking nodes found for melee attack!"); return(false); } Mod.MeleeLog.Info?.Write("KICK ATTACK validated"); return(true); }
public static float PunchInstability(this Mech mech) { ActorMeleeCondition attackerCondition = ModState.GetMeleeCondition(mech); if (!attackerCondition.CanPunch()) { return(0); } // 0 is a signal that there's no divisor float tonnageMulti = mech.StatCollection.ContainsStatistic(ModStats.PunchTargetInstability) && mech.StatCollection.GetValue <float>(ModStats.PunchTargetInstability) > 0 ? mech.StatCollection.GetValue <float>(ModStats.PunchTargetInstability) : Mod.Config.Melee.Punch.TargetInstabilityPerAttackerTon; float raw = (float)Math.Ceiling(tonnageMulti * mech.tonnage); Mod.MeleeLog.Debug?.Write($"PUNCH instability => tonnageMulti: {tonnageMulti} x attacker tonnage: {mech.tonnage} = raw: {raw}"); // Modifiers float mod = mech.StatCollection.ContainsStatistic(ModStats.PunchTargetInstabilityMod) ? mech.StatCollection.GetValue <int>(ModStats.PunchTargetInstabilityMod) : 0f; float multi = mech.StatCollection.ContainsStatistic(ModStats.PunchTargetInstabilityMulti) ? mech.StatCollection.GetValue <float>(ModStats.PunchTargetInstabilityMulti) : 1f; // Leg actuator damage float leftReductionMulti = 1f; int damagedLeftCount = 2 - attackerCondition.LeftArmActuatorsCount; for (int i = 0; i < damagedLeftCount; i++) { leftReductionMulti *= Mod.Config.Melee.Punch.ArmActuatorDamageReduction; } Mod.MeleeLog.Debug?.Write($" - Left actuator damage multi is: {leftReductionMulti}"); float rightReductionMulti = 1f; int damagedRightCount = 2 - attackerCondition.RightArmActuatorsCount; for (int i = 0; i < damagedRightCount; i++) { rightReductionMulti *= Mod.Config.Melee.Punch.ArmActuatorDamageReduction; } Mod.MeleeLog.Debug?.Write($" - Right actuator damage multi is: {rightReductionMulti}"); float actuatorMulti = leftReductionMulti >= rightReductionMulti ? leftReductionMulti : rightReductionMulti; Mod.MeleeLog.Debug?.Write($" - Using actuator damage multi of: {actuatorMulti}"); // Roll up instability float final = (float)Math.Ceiling((raw + mod) * multi * actuatorMulti); Mod.MeleeLog.Info?.Write($" - Target instability => final: {final} = (raw: {raw} + mod: {mod}) x " + $"multi: {multi} x actuatorMulti: {actuatorMulti}"); return(final); }
public static float PunchDamage(this Mech mech) { ActorMeleeCondition attackerCondition = ModState.GetMeleeCondition(mech); if (!attackerCondition.CanPunch()) { return(0); } float tonnageMulti = mech.StatCollection.ContainsStatistic(ModStats.PunchTargetDamage) && mech.StatCollection.GetValue <float>(ModStats.PunchTargetDamage) > 0 ? mech.StatCollection.GetValue <float>(ModStats.PunchTargetDamage) : Mod.Config.Melee.Punch.TargetDamagePerAttackerTon; float raw = (float)Math.Ceiling(tonnageMulti * mech.tonnage); Mod.MeleeLog.Debug?.Write($"PUNCH damage => tonnageMulti: {tonnageMulti} x attacker tonnage: {mech.tonnage} = raw: {raw}"); // Modifiers float mod = mech.StatCollection.ContainsStatistic(ModStats.PunchTargetDamageMod) ? mech.StatCollection.GetValue <int>(ModStats.PunchTargetDamageMod) : 0f; float multi = mech.StatCollection.ContainsStatistic(ModStats.PunchTargetDamageMulti) ? mech.StatCollection.GetValue <float>(ModStats.PunchTargetDamageMulti) : 1f; // Actuator damage float leftReductionMulti = 1f; int damagedLeftActuators = 2 - attackerCondition.LeftArmActuatorsCount; for (int i = 0; i < damagedLeftActuators; i++) { leftReductionMulti *= Mod.Config.Melee.Punch.ArmActuatorDamageReduction; } Mod.MeleeLog.Debug?.Write($" - Left arm actuator damage is: {leftReductionMulti}"); float rightReductionMulti = 1f; int damagedRightActuators = 2 - attackerCondition.RightArmActuatorsCount; for (int i = 0; i < damagedRightActuators; i++) { rightReductionMulti *= Mod.Config.Melee.Punch.ArmActuatorDamageReduction; } Mod.MeleeLog.Debug?.Write($" - Right arm actuator damage is: {rightReductionMulti}"); float reductionMulti = leftReductionMulti >= rightReductionMulti ? leftReductionMulti : rightReductionMulti; Mod.MeleeLog.Debug?.Write($" - Using arm actuator damage reduction of: {reductionMulti}"); // Roll up final damage float final = (float)Math.Ceiling((raw + mod) * multi * reductionMulti); Mod.MeleeLog.Debug?.Write($" - Target damage per strike => final: {final} = (raw: {raw} + mod: {mod}) x " + $"multi: {multi} x reductionMulti: {reductionMulti}"); return(final); }
public static float KickInstability(this Mech mech) { ActorMeleeCondition attackerCondition = ModState.GetMeleeCondition(mech); if (!attackerCondition.CanKick()) { return(0); } float raw = (float)Math.Ceiling(Mod.Config.Melee.Kick.TargetInstabilityPerAttackerTon * mech.tonnage); Mod.MeleeLog.Debug?.Write($"KICK baseStability: {Mod.Config.Melee.Punch.TargetInstabilityPerAttackerTon} x " + $"attacker tonnage: {mech.tonnage} = {raw}"); // Modifiers float mod = mech.StatCollection.ContainsStatistic(ModStats.KickTargetInstabilityMod) ? mech.StatCollection.GetValue <int>(ModStats.KickTargetInstabilityMod) : 0f; float multi = mech.StatCollection.ContainsStatistic(ModStats.KickTargetInstabilityMulti) ? mech.StatCollection.GetValue <float>(ModStats.KickTargetInstabilityMulti) : 1f; // Leg actuator damage float leftReductionMulti = 1f; int damagedLeftCount = 2 - attackerCondition.LeftLegActuatorsCount; for (int i = 0; i < damagedLeftCount; i++) { leftReductionMulti *= Mod.Config.Melee.Kick.LegActuatorDamageReduction; } Mod.MeleeLog.Debug?.Write($" - Left actuator damage multi is: {leftReductionMulti}"); float rightReductionMulti = 1f; int damagedRight = 2 - attackerCondition.RightLegActuatorsCount; for (int i = 0; i < damagedRight; i++) { rightReductionMulti *= Mod.Config.Melee.Kick.LegActuatorDamageReduction; } Mod.MeleeLog.Debug?.Write($" - Right actuator damage multi is: {rightReductionMulti}"); float actuatorMulti = leftReductionMulti >= rightReductionMulti ? leftReductionMulti : rightReductionMulti; Mod.MeleeLog.Debug?.Write($" - Using actuator damage multi of: {actuatorMulti}"); // Roll up instability float final = (float)Math.Ceiling((raw + mod) * multi * actuatorMulti); Mod.MeleeLog.Debug?.Write($" - Target instability => final: {final} = (raw: {raw} + mod: {mod}) x " + $"multi: {multi} x actuatorMulti: {actuatorMulti}"); return(final); }
private void CalculateModifiers(Mech attacker, AbstractActor target) { // -2 to hit base this.AttackModifiers.Add(ModText.LT_Label_Easy_to_Kick, Mod.Config.Melee.Kick.BaseAttackBonus); // If target is prone, -2 modifier if (target.IsProne) { this.AttackModifiers.Add(ModText.LT_Label_Target_Prone, Mod.Config.Melee.ProneTargetAttackModifier); } // Actuator damage; +1 for foot actuator, +2 to hit for each upper/lower actuator hit ActorMeleeCondition attackerCondition = ModState.GetMeleeCondition(attacker); int leftLegMalus = (2 - attackerCondition.LeftLegActuatorsCount) * Mod.Config.Melee.Kick.LegActuatorDamageMalus; if (!attackerCondition.LeftFootIsFunctional) { leftLegMalus += Mod.Config.Melee.Kick.FootActuatorDamageMalus; } int rightLegMalus = (2 - attackerCondition.RightLegActuatorsCount) * Mod.Config.Melee.Kick.LegActuatorDamageMalus; if (!attackerCondition.RightFootIsFunctional) { rightLegMalus += Mod.Config.Melee.Kick.FootActuatorDamageMalus; } int bestLegMalus = leftLegMalus <= rightLegMalus ? leftLegMalus : rightLegMalus; if (bestLegMalus != 0) { this.AttackModifiers.Add(ModText.LT_Label_Actuator_Damage, bestLegMalus); } // Check for attack modifier statistic if (attacker.StatCollection.ContainsStatistic(ModStats.KickAttackMod) && attacker.StatCollection.GetValue <int>(ModStats.KickAttackMod) != 0) { this.AttackModifiers.Add(ModText.LT_Label_Kick_Attack_Mod, attacker.StatCollection.GetValue <int>(ModStats.KickAttackMod)); } }
private bool ValidateAttack(Mech attacker, AbstractActor target, HashSet <MeleeAttackType> validAnimations) { ActorMeleeCondition meleeCondition = ModState.GetMeleeCondition(attacker); if (!meleeCondition.CanCharge()) { Mod.MeleeLog.Info?.Write($"Attacker cannot charge, skipping."); return(false); } // If neither tackle (mech) or stomp (vehicle) - we're not a valid attack. if (!validAnimations.Contains(MeleeAttackType.Tackle) && !validAnimations.Contains(MeleeAttackType.Stomp)) { Mod.MeleeLog.Info?.Write("Animations do not include a tackle or stomp, attacker cannot charge!"); return(false); } // Charges cannot target prone units if (target.IsProne) { Mod.MeleeLog.Info?.Write($"Attacker unable to charge prone target"); return(false); } if (target.UnaffectedPathing()) { Mod.MeleeLog.Info?.Write($"Target is unaffected by pathing, likely a VTOL or LAM in flight. Cannot melee it!"); return(false); } // If distance > walkSpeed, disable kick/physical weapon/punch if (!state.HasSprintAttackNodes) { Mod.MeleeLog.Info?.Write($"No sprinting nodes found for melee attack!"); return(false); } Mod.MeleeLog.Info?.Write("CHARGE ATTACK validated"); return(true); }
private void CalculateModifiers(Mech attacker, AbstractActor target) { // If target is prone, -2 modifier if (target.IsProne) { this.AttackModifiers.Add(ModText.LT_Label_Target_Prone, Mod.Config.Melee.ProneTargetAttackModifier); } // +2 to hit for each upper/lower actuator hit ActorMeleeCondition attackerCondition = ModState.GetMeleeCondition(attacker); int leftArmMalus = (2 - attackerCondition.LeftArmActuatorsCount) * Mod.Config.Melee.Punch.ArmActuatorDamageMalus; int rightArmMalus = (2 - attackerCondition.RightArmActuatorsCount) * Mod.Config.Melee.Punch.ArmActuatorDamageMalus; int bestMalus = leftArmMalus <= rightArmMalus ? leftArmMalus : rightArmMalus; // If the ignore actuators stat is set, set the malus to 0 regardless of damage Statistic ignoreActuatorsStat = attacker.StatCollection.GetStatistic(ModStats.PhysicalWeaponIgnoreActuators); if (ignoreActuatorsStat != null && ignoreActuatorsStat.Value <bool>()) { bestMalus = 0; } // Add actuator damage if it exists if (bestMalus != 0) { this.AttackModifiers.Add(ModText.LT_Label_Actuator_Damage, bestMalus); } // Check for attack modifier statistic Statistic phyWeapAttackMod = attacker.StatCollection.GetStatistic(ModStats.PhysicalWeaponAttackMod); if (phyWeapAttackMod != null && phyWeapAttackMod.Value <int>() != 0) { this.AttackModifiers.Add(ModText.LT_Label_Physical_Weapon_Attack_Mod, attacker.StatCollection.GetValue <int>(ModStats.PhysicalWeaponAttackMod)); } }
public static bool CanMakePhysicalWeaponAttack(this Mech mech) { ActorMeleeCondition attackerCondition = ModState.GetMeleeCondition(mech); return(attackerCondition.CanUsePhysicalAttack()); }