public BearAbilityBuilder(StatsBear stats, float weaponDPS, float attackSpeed, float armorDamageMultiplier, float chanceAvoided, float chanceResisted, float chanceCrit, float chanceCritSpell, float chanceGlance, float critMultiplier, float spellCritMultiplier) { Stats = stats; WeaponDPS = weaponDPS; AttackSpeed = attackSpeed; ArmorDamageMultiplier = armorDamageMultiplier; ChanceAvoided = chanceAvoided; ChanceResisted = chanceResisted; ChanceCrit = chanceCrit; ChanceCritSpell = chanceCritSpell; ChanceGlance = chanceGlance; CritMultiplier = critMultiplier; SpellCritMultiplier = spellCritMultiplier; BaseDamage = (WeaponDPS + AttackPower / 14f) * 2.5f; }
private static void AccumulateProcs(Character character, StatsBear statsTotal) { CalculationOptionsBear calcOpts = character.CalculationOptions as CalculationOptionsBear ?? new CalculationOptionsBear(); BossOptions bossOpts = character.BossOptions; if (bossOpts.Attacks.Count < 1) { bossOpts.Attacks.Add(BossHandler.ADefaultMeleeAttack); bossOpts.Attacks[0].IsTheDefaultMelee = true; bossOpts.DamagingTargs = true; } if (bossOpts.DefaultMeleeAttack == null) { bossOpts.Attacks.Add(BossHandler.ADefaultMeleeAttack); bossOpts.Attacks[bossOpts.Attacks.Count-1].IsTheDefaultMelee = true; bossOpts.DamagingTargs = true; } Attack bossAttack = bossOpts.DefaultMeleeAttack; int targetLevel = bossOpts.Level; float fightDuration = bossOpts.BerserkTimer; float hasteBonus = StatConversion.GetHasteFromRating(statsTotal.HasteRating, CharacterClass.Druid); float playerHastedAttackSpeed = (2.5f / (1f + hasteBonus)) / (1f + statsTotal.PhysicalHaste); float meleeHitInterval = 1f / (1f / playerHastedAttackSpeed + 1f / 1.5f); int levelDifference = (bossOpts.Level - character.Level); float hitBonus = StatConversion.GetPhysicalHitFromRating(statsTotal.HitRating) + statsTotal.PhysicalHit; float expertiseBonus = StatConversion.GetDodgeParryReducFromExpertise(StatConversion.GetExpertiseFromRating(statsTotal.ExpertiseRating, CharacterClass.Druid) + statsTotal.Expertise, CharacterClass.Druid); float chanceDodge = Math.Max(0f, StatConversion.WHITE_DODGE_CHANCE_CAP[levelDifference] - expertiseBonus); float chanceParry = Math.Max(0f, StatConversion.WHITE_PARRY_CHANCE_CAP[levelDifference] - expertiseBonus); float chanceMiss = Math.Max(0f, StatConversion.WHITE_MISS_CHANCE_CAP[levelDifference] - hitBonus); float chanceAvoided = chanceMiss + chanceDodge + chanceParry; float rawChanceCrit = StatConversion.GetPhysicalCritFromRating(statsTotal.CritRating, CharacterClass.Druid) + StatConversion.GetPhysicalCritFromAgility(statsTotal.Agility, CharacterClass.Druid) + statsTotal.PhysicalCrit + StatConversion.NPC_LEVEL_CRIT_MOD[levelDifference]; float chanceCrit = rawChanceCrit * (1f - chanceAvoided); float chanceHit = 1f - chanceAvoided; float levelDifferenceAvoidance = levelDifference * 0.002f; float baseAgi = character.Race == CharacterRace.NightElf ? 87 : 77; //float defSkill = (float)Math.Floor(StatConversion.GetDefenseFromRating(statsTotal.DefenseRating, CharacterClass.Druid)); float dodgeNonDR = statsTotal.Dodge - levelDifferenceAvoidance + StatConversion.GetDodgeFromAgility(baseAgi, CharacterClass.Druid); float missNonDR = statsTotal.Miss - levelDifferenceAvoidance; float dodgePreDR = StatConversion.GetDodgeFromAgility(statsTotal.Agility - baseAgi, CharacterClass.Druid) + StatConversion.GetDodgeFromRating(statsTotal.DodgeRating, CharacterClass.Druid) /*+ defSkill * StatConversion.DEFENSE_RATING_AVOIDANCE_MULTIPLIER / 100f*/; float missPreDR = 0f;// (defSkill * StatConversion.DEFENSE_RATING_AVOIDANCE_MULTIPLIER / 100f); float dodgePostDR = 0.01f / (1f / 116.890707f + 0.00972f / dodgePreDR); float missPostDR = 0.01f / (1f / 16f + 0.00972f / missPreDR); float dodgeTotal = dodgeNonDR + dodgePostDR; float missTotal = missNonDR + missPostDR; float TargAttackSpeed = bossAttack.AttackSpeed / (1f - statsTotal.BossAttackSpeedReductionMultiplier); Stats statsProcs = new Stats(); //float uptime; foreach (SpecialEffect effect in statsTotal.SpecialEffects()) { switch (effect.Trigger) { case Trigger.Use: effect.AccumulateAverageStats(statsProcs, 0f, 1f, 2.5f, fightDuration); break; case Trigger.MeleeHit: case Trigger.PhysicalHit: effect.AccumulateAverageStats(statsProcs, meleeHitInterval, chanceHit, 2.5f, fightDuration); break; case Trigger.MeleeAttack: case Trigger.PhysicalAttack: if (effect.Stats.MoteOfAnger > 0) { // When in effect stats, MoteOfAnger is % of melee hits // When in character stats, MoteOfAnger is average procs per second statsProcs.MoteOfAnger = effect.Stats.MoteOfAnger * effect.GetAverageProcsPerSecond(meleeHitInterval, 1f, 2.5f, fightDuration) / effect.MaxStack; } else { effect.AccumulateAverageStats(statsProcs, meleeHitInterval, 1f, 2.5f, fightDuration); } break; case Trigger.MeleeCrit: case Trigger.PhysicalCrit: effect.AccumulateAverageStats(statsProcs, meleeHitInterval, chanceCrit, 2.5f, fightDuration); break; case Trigger.DoTTick: effect.AccumulateAverageStats(statsProcs, 3f, 1f, 2.5f, fightDuration); break; case Trigger.DamageDone: effect.AccumulateAverageStats(statsProcs, meleeHitInterval / 2f, 1f, 2.5f, fightDuration); break; case Trigger.DamageOrHealingDone: effect.AccumulateAverageStats(statsProcs, meleeHitInterval / 2f, 1f, 2.5f, fightDuration); // also needs healing break; case Trigger.MangleBearHit: effect.AccumulateAverageStats(statsProcs, 6f - statsTotal.MangleCooldownReduction, chanceHit, 2.5f, fightDuration); break; case Trigger.MangleCatOrShredOrInfectedWoundsHit: effect.AccumulateAverageStats(statsProcs, 1f / (1f / (6f - statsTotal.MangleCooldownReduction) + //Mangles Per Second 1f / meleeHitInterval) //Mauls Per Second , chanceHit, 2.5f, fightDuration); break; case Trigger.SwipeBearOrLacerateHit: effect.AccumulateAverageStats(statsProcs, 2.25f, chanceHit, 2.5f, fightDuration); break; case Trigger.LacerateTick: effect.AccumulateAverageStats(statsProcs, 3f, 1f, 2.5f, fightDuration); break; case Trigger.DamageTakenPutsMeBelow35PercHealth: effect.AccumulateAverageStats(statsProcs, TargAttackSpeed * 0.8f, (1f - 0.8f * (dodgeTotal + missTotal)) * 0.35f, fightDuration); //Assume you get hit by other things, like dots, aoes, etc, making you get targeted with damage 35% more often than the boss, and half the hits you take are unavoidable. break; case Trigger.DamageTakenPutsMeBelow50PercHealth: effect.AccumulateAverageStats(statsProcs, TargAttackSpeed * 0.8f, (1f - 0.8f * (dodgeTotal + missTotal)) * 0.50f, fightDuration); //Assume you get hit by other things, like dots, aoes, etc, making you get targeted with damage 50% more often than the boss, and half the hits you take are unavoidable. break; case Trigger.DamageTaken: effect.AccumulateAverageStats(statsProcs, TargAttackSpeed * 0.8f, 1f - 0.8f * (dodgeTotal + missTotal), fightDuration); //Assume you get hit by other things, like dots, aoes, etc, making you get targeted with damage 25% more often than the boss, and half the hits you take are unavoidable. break; case Trigger.DamageTakenPhysical: effect.AccumulateAverageStats(statsProcs, TargAttackSpeed, 1f - (dodgeTotal + missTotal), fightDuration); //Assume you get hit by other things, like dots, aoes, etc, making you get targeted with damage 25% more often than the boss, and half the hits you take are unavoidable. break; case Trigger.Barkskin: effect.AccumulateAverageStats(statsProcs, 60f, 1f, 0f, fightDuration); break; case Trigger.Berserk: effect.AccumulateAverageStats(statsProcs, 180f, 1, 0f, fightDuration); break; } } statsProcs.Agility += statsProcs.HighestStat + statsProcs.Paragon; statsProcs.Stamina = (float)Math.Floor(statsProcs.Stamina * (1f + statsTotal.BonusStaminaMultiplier)); statsProcs.Strength = (float)Math.Floor(statsProcs.Strength * (1f + statsTotal.BonusStrengthMultiplier)); statsProcs.Agility = (float)Math.Floor(statsProcs.Agility * (1f + statsTotal.BonusAgilityMultiplier)); statsProcs.AttackPower += statsProcs.Strength + statsProcs.Agility * 2f; statsProcs.AttackPower = (float)Math.Floor(statsProcs.AttackPower * (1f + statsTotal.BonusAttackPowerMultiplier)); statsProcs.Health += (float)Math.Floor(statsProcs.Stamina * 14f) + (float)Math.Floor(statsProcs.BattlemasterHealthProc); statsProcs.Health *= (1f + statsProcs.BonusHealthMultiplier); statsProcs.Armor += /*2f * (float)Math.Floor(statsProcs.Agility)*/ + statsProcs.BonusArmor; // Armor no longer gets bonuses from Agi in Cata statsProcs.Armor = (float)Math.Floor(statsProcs.Armor * (1f + statsTotal.BonusArmorMultiplier)); if (statsProcs.HighestSecondaryStat > 0) { if (statsTotal.CritRating > statsTotal.HasteRating && statsTotal.CritRating > statsTotal.MasteryRating) { statsProcs.CritRating += statsProcs.HighestSecondaryStat; // this will be invalidated after this, but I'm at least putting it in for now } else if (statsTotal.HasteRating > statsTotal.CritRating && statsTotal.HasteRating > statsTotal.MasteryRating) { statsProcs.HasteRating += statsProcs.HighestSecondaryStat; } else if (statsTotal.MasteryRating > statsTotal.CritRating && statsTotal.MasteryRating > statsTotal.HasteRating) { statsProcs.MasteryRating += statsProcs.HighestSecondaryStat; } statsProcs.HighestSecondaryStat = 0; } statsTotal.Accumulate(statsProcs); }
/// <summary> /// Gets the total Stats of the Character /// </summary> /// <param name="character">The Character to get the total Stats of</param> /// <param name="additionalItem">An additional item to grant the Character the stats of (as if it were worn)</param> /// <returns>The total stats for the Character</returns> public override Stats GetCharacterStats(Character character, Item additionalItem) { CalculationOptionsBear calcOpts = character.CalculationOptions as CalculationOptionsBear ?? new CalculationOptionsBear(); DruidTalents talents = character.DruidTalents; bool hasCritBuff = false; foreach (Buff buff in character.ActiveBuffs) { if (buff.Group == "Critical Strike Chance") { hasCritBuff = true; break; } } StatsBear statsTotal = new StatsBear() { BonusAttackPowerMultiplier = 0.25f, BonusBleedDamageMultiplier = (character.ActiveBuffsContains("Mangle") || character.ActiveBuffsContains("Trauma") ? 0f : 0.3f), Dodge = 0.02f * talents.FeralSwiftness + 0.03f * talents.NaturalReaction, FurySwipesChance = 0.05f * talents.FurySwipes, BonusEnrageDamageMultiplier = 0.05f * talents.KingOfTheJungle, HasteOnFeralCharge = 0.15f * talents.Stampede, BaseArmorMultiplier = 2.2f * (1f + 0.10f * talents.ThickHide / 3f) * (1f + 0.26f * talents.ThickHide) - 1f, CritChanceReduction = 0.02f * talents.ThickHide, PhysicalCrit = (hasCritBuff ? 0f : 0.05f * talents.LeaderOfThePack) + (talents.Pulverize > 0 ? 0.09f: 0f), SpellCrit = (hasCritBuff ? 0f : 0.05f * talents.LeaderOfThePack), BonusPulverizeDuration = 4f * talents.EndlessCarnage, DamageTakenReductionMultiplier = 0.09f * talents.NaturalReaction, BonusMaulDamageMultiplier = 0.04f * talents.RendAndTear, BonusStaminaMultiplier = (1f + 0.02f * talents.HeartOfTheWild) * (Character.ValidateArmorSpecialization(character, ItemType.Leather) ? 1.05f : 1f) - 1f, BonusPhysicalDamageMultiplier = 0.04f * talents.MasterShapeshifter, BonusMangleDamageMultiplier = talents.GlyphOfMangle ? 0.1f : 0f, BonusLacerateCritChance = talents.GlyphOfLacerate ? 0.05f : 0f, BonusFaerieFireStacks = talents.FeralAggression, BerserkDuration = (talents.GlyphOfBerserk ? 10f : 0f), }; #region Set Bonuses int PvPCount; character.SetBonusCount.TryGetValue("Gladiator's Sanctuary", out PvPCount); if (PvPCount >= 2) { statsTotal.Agility += 70f; statsTotal.Resilience += 400f; } if (PvPCount >= 4) { // the 15% movement speed is only outdoors which most dungeons are not statsTotal.Agility += 90f; } int T11Count; character.SetBonusCount.TryGetValue("Stormrider's Battlegarb", out T11Count); if (T11Count >= 2) { //statsTotal.BonusDamageMultiplierRakeTick = (1f + statsTotal.BonusDamageMultiplierRakeTick) * (1f + 0.10f) - 1f; statsTotal.BonusDamageMultiplierLacerate = (1f + statsTotal.BonusDamageMultiplierLacerate) * (1f + 0.10f) - 1f; } if (T11Count >= 4) { /*statsBuffs.AddSpecialEffect(new SpecialEffect(Trigger.MangleCatHit, new Stats() { BonusAttackPowerMultiplier = 0.01f, }, 30, 0, 1f, 3));*/ statsTotal.BonusSurvivalInstinctsDurationMultiplier = 0.5f; } int T12Count; character.SetBonusCount.TryGetValue("Obsidian Arborweave Battlegarb", out T12Count); if (T12Count >= 2) { statsTotal.BonusMangleDamageMultiplier = (1f + statsTotal.BonusMangleDamageMultiplier) * (1f + 0.10f) - 1f; statsTotal.BonusMaulDamageMultiplier = (1f + statsTotal.BonusMaulDamageMultiplier) * (1f + 0.10f) - 1f; } if (T12Count >= 4) { statsTotal.AddSpecialEffect(SpecialEffect4T12); } int T13Count; character.SetBonusCount.TryGetValue("Deep Earth Battlegarb", out T13Count); if (T13Count >= 2) { statsTotal.Tier_13_2_piece = true; } if (T13Count >= 4) { statsTotal.Tier_13_4_piece = (10f + 25f)/2; } #endregion // Leader of the Pack self-heal statsTotal.AddSpecialEffect(LeaderOfThePackSpecialEffect); // Survival Instincts SpecialEffect SurvivalInstinctsSpecialEffect = new SpecialEffect(Trigger.Use, new Stats() { DamageTakenReductionMultiplier = 0.50f, }, 12f * (1f + statsTotal.BonusSurvivalInstinctsDurationMultiplier), 180f, 1f); statsTotal.AddSpecialEffect(SurvivalInstinctsSpecialEffect); // Barkskin SpecialEffect BarkskinSpecialEffect = new SpecialEffect(Trigger.Use, new Stats() { DamageTakenReductionMultiplier = 0.20f, CritChanceReduction = (talents.GlyphOfBarkskin ? 0.25f : 0f), }, 12f, 60f, 1f); statsTotal.AddSpecialEffect(BarkskinSpecialEffect); // Frenzied Regeneration SpecialEffect FrenziedRegenerationSpecialEffect = new SpecialEffect(Trigger.Use, new Stats() { BonusHealthMultiplier = 0.15f, HealthRestoreFromMaxHealth = (talents.GlyphOfFrenziedRegeneration ? 0f : (0.015f * (1f + statsTotal.Tier_13_4_piece))), HealingReceivedMultiplier = (talents.GlyphOfFrenziedRegeneration ? (0.30f * (1f + statsTotal.Tier_13_4_piece)) : 0f) }, 20f, 180f, 1f); statsTotal.AddSpecialEffect(FrenziedRegenerationSpecialEffect); // Berserk StatsBear tempBear = new StatsBear(); tempBear.AddSpecialEffect(new SpecialEffect(Trigger.LacerateTick, new StatsBear() { MangleCooldownReduction = 6f, MangleCostReduction = 1f }, float.PositiveInfinity, 0, 0.5f)); SpecialEffect BerserkSpecialEffect = new SpecialEffect(Trigger.Use, tempBear, 15f + statsTotal.BerserkDuration, 180f, 1f); statsTotal.AddSpecialEffect(BerserkSpecialEffect); // Enrage SpecialEffect EnrageSpecialEffect = new SpecialEffect(Trigger.Use, new StatsBear() { BonusDamageMultiplier = (0.05f * talents.KingOfTheJungle) }, 10f, 60f, 1f); statsTotal.AddSpecialEffect(EnrageSpecialEffect); statsTotal.Accumulate(BaseStats.GetBaseStats(character.Level, character.Class, character.Race, BaseStats.DruidForm.Bear)); statsTotal.Accumulate(GetItemStats(character, additionalItem)); statsTotal.Accumulate(GetBuffsStats(character, calcOpts)); statsTotal.Stamina = (float)Math.Floor(statsTotal.Stamina * (1f + statsTotal.BonusStaminaMultiplier)); statsTotal.Strength = (float)Math.Floor(statsTotal.Strength * (1f + statsTotal.BonusStrengthMultiplier)); statsTotal.Agility = (float)Math.Floor(statsTotal.Agility * (1f + statsTotal.BonusAgilityMultiplier)); statsTotal.AttackPower += (float)Math.Floor(statsTotal.Strength); statsTotal.AttackPower += (float)Math.Floor(statsTotal.Agility - 20f) * 2f + 20f; statsTotal.AttackPower = (float)Math.Floor(statsTotal.AttackPower * (1f + statsTotal.BonusAttackPowerMultiplier)); statsTotal.Health += ((statsTotal.Stamina - 20f) * 14f) + 20f; statsTotal.Health *= (1f + statsTotal.BonusHealthMultiplier); statsTotal.Armor *= 1f + statsTotal.BaseArmorMultiplier; statsTotal.Armor += statsTotal.BonusArmor; statsTotal.Armor = (float)Math.Floor(statsTotal.Armor * (1f + statsTotal.BonusArmorMultiplier)); statsTotal.NatureResistance += statsTotal.NatureResistanceBuff; statsTotal.FireResistance += statsTotal.FireResistanceBuff; statsTotal.FrostResistance += statsTotal.FrostResistanceBuff; statsTotal.ShadowResistance += statsTotal.ShadowResistanceBuff; statsTotal.ArcaneResistance += statsTotal.ArcaneResistanceBuff; AccumulateProcs(character, statsTotal); return statsTotal; }
/// <summary> /// Calculates the threat properties of the Character /// </summary> /// <param name="stats">The total Stats of the character</param> /// <param name="targetLevel">The level of the target</param> /// <param name="calculatedStats">The CharacterCalculationsBear object to fill with results</param> /// <param name="character">The Character to calculate the threat properties of</param> private void CalculateThreat(StatsBear stats, int targetLevel, CharacterCalculationsBear calculatedStats, Character character) { CalculationOptionsBear calcOpts = character.CalculationOptions as CalculationOptionsBear ?? new CalculationOptionsBear(); BossOptions bossOpts = character.BossOptions; if (bossOpts.Attacks.Count < 1) { bossOpts.Attacks.Add(BossHandler.ADefaultMeleeAttack); bossOpts.Attacks[0].IsTheDefaultMelee = true; bossOpts.DamagingTargs = true; } if (bossOpts.DefaultMeleeAttack == null) { bossOpts.Attacks.Add(BossHandler.ADefaultMeleeAttack); bossOpts.Attacks[bossOpts.Attacks.Count - 1].IsTheDefaultMelee = true; bossOpts.DamagingTargs = true; } Attack bossAttack = bossOpts.DefaultMeleeAttack; DruidTalents talents = character.DruidTalents; // Establish base multipliers and chances float modArmor = 1f - StatConversion.GetArmorDamageReduction(character.Level, bossOpts.Armor, stats.TargetArmorReduction, stats.ArmorPenetration); float critMultiplier = 2f * (1 + stats.BonusCritDamageMultiplier); float spellCritMultiplier = 2f * (1 + stats.BonusCritDamageMultiplier); float hasteBonus = StatConversion.GetPhysicalHasteFromRating(stats.HasteRating, CharacterClass.Druid); float attackSpeed = (2.5f) / (1f + hasteBonus); attackSpeed = attackSpeed / (1f + stats.PhysicalHaste); float hitBonus = StatConversion.GetPhysicalHitFromRating(stats.HitRating, CharacterClass.Druid) + stats.PhysicalHit; float expertiseBonus = StatConversion.GetDodgeParryReducFromExpertise(StatConversion.GetExpertiseFromRating(stats.ExpertiseRating, CharacterClass.Druid) + stats.Expertise, CharacterClass.Druid); float spellHitBonus = StatConversion.GetSpellHitFromRating(stats.HitRating, CharacterClass.Druid) + stats.SpellHit; int levelDifference = (targetLevel - character.Level); float chanceDodge = Math.Max(0f, StatConversion.WHITE_DODGE_CHANCE_CAP[levelDifference] - expertiseBonus); float chanceParry = Math.Max(0f, StatConversion.WHITE_PARRY_CHANCE_CAP[levelDifference] - expertiseBonus); float chanceMiss = Math.Max(0f, StatConversion.WHITE_MISS_CHANCE_CAP[levelDifference] - hitBonus); float chanceResist = Math.Max(0f, StatConversion.GetSpellMiss(-levelDifference, false) - spellHitBonus); float chanceGlance = StatConversion.WHITE_GLANCE_CHANCE_CAP[levelDifference]; //0.2335774f; //float glanceMultiplier = 0.7f; float chanceAvoided = chanceMiss + chanceDodge + chanceParry; float rawChanceCrit = StatConversion.GetPhysicalCritFromRating(stats.CritRating, CharacterClass.Druid) + StatConversion.GetPhysicalCritFromAgility(stats.Agility, CharacterClass.Druid) + stats.PhysicalCrit + StatConversion.NPC_LEVEL_CRIT_MOD[levelDifference]; float chanceCrit = rawChanceCrit * (1f - chanceAvoided); float rawChanceCritSpell = StatConversion.GetSpellCritFromRating(stats.CritRating, CharacterClass.Druid) + StatConversion.GetSpellCritFromIntellect(stats.Intellect, CharacterClass.Druid) + stats.SpellCrit + stats.SpellCritOnTarget + StatConversion.NPC_LEVEL_CRIT_MOD[levelDifference]; float chanceCritSpell = rawChanceCritSpell * (1f - chanceResist); calculatedStats.DodgedAttacks = chanceDodge; calculatedStats.ParriedAttacks = chanceParry; calculatedStats.MissedAttacks = chanceMiss; float movementdowntime = 3f / 5.5f / (1 + stats.MovementSpeed); // Movement Duration / Movement Frequency / (1 + Movement Speed) BearAbilityBuilder abilities = new BearAbilityBuilder(stats, character.MainHand == null ? 0.75f : ((character.MainHand.MinDamage + character.MainHand.MaxDamage) / 2f) / character.MainHand.Speed, attackSpeed, modArmor, chanceAvoided, chanceResist, chanceCrit, chanceCritSpell, chanceGlance, critMultiplier, spellCritMultiplier); var optimalRotations = BearRotationCalculator.GetOptimalRotations(abilities); calculatedStats.Abilities = abilities; calculatedStats.HighestDPSRotation = optimalRotations.Item1; float bonusdamage = stats.ArcaneDamage + stats.FireDamage + stats.FrostDamage + stats.NatureDamage + stats.ShadowDamage + stats.HolyDamage + stats.PhysicalDamage; calculatedStats.HighestDPSRotation.DPS += bonusdamage; calculatedStats.HighestDPSRotation.DPS *= 1 - movementdowntime; calculatedStats.HighestTPSRotation = optimalRotations.Item2; calculatedStats.HighestTPSRotation.TPS += bonusdamage * 5f; calculatedStats.HighestTPSRotation.TPS *= 1 - movementdowntime; calculatedStats.ThreatPoints = calculatedStats.HighestTPSRotation.TPS * calcOpts.ThreatScale / 10f; }