public override string ToString() { return(string.Format("DPE: {0:N0} Dmg: {1:N0}*{2}", (DamageTickAverage * Duration / 2f) / EnergyCost, (DamageTickAverage * Duration / 2f), CatAbilityBuilder.BuildTooltip( "Damage Per Tick Hit", DamageTickRaw, "Damage Per Average Tick", DamageTickAverage, "Damage Per Energy", (DamageTickAverage * Duration / 2f) / EnergyCost ))); }
public override string ToString() { return(string.Format("DPE: {0:N1} Dmg: {1:N0}*{2}", DamageAverage / EnergyCost, DamageAverage, CatAbilityBuilder.BuildTooltip( "Damage Per Hit", DamageRaw, "Damage Per Landed Attack", DamageAverage, "Damage Per Energy", DamageAverage / EnergyCost ))); }
public override string ToString() { return(string.Format("DPS: {0:N0}*{1}", DPSRoarAverage + DPSFurySwipesAverage, CatAbilityBuilder.BuildTooltip( "Damage Per Hit", DamageRaw, "Damage Per Swing", DamageAverage, "Damage Per Hit w/ Savage Roar", DamageRoarRaw, "Damage Per Swing w/ Savage Roar", DamageRoarAverage, "Damage Per Fury Swipes Hit", DamageFurySwipesRaw, "Damage Per Fury Swipes Swing", DamageFurySwipesAverage ))); }
public override string ToString() { return(string.Format("DPE: {0:N0} Dmg: {1:N0}*{2}", DamageAverage / 10f, DamageAverage, CatAbilityBuilder.BuildTooltip( "Damage Per Hit", DamageRaw, "Damage Per Landed Attack", DamageAverage, "Damage Per Landed Attack Above 80%", DamageAbove80PercentAverage, "Damage Per Energy", DamageAverage / EnergyCost, "Damage Per Energy Above 80%", DamageAbove80PercentAverage / EnergyCost, "Damage Per Energy w/ Stampede", DamageAverage / 10f, "Damage Per Energy Above 80% w/ Stampede", DamageAbove80PercentAverage / 10f ))); }
public CatRotationCalculator(CatAbilityBuilder abilities, int fightDuration, MangleUsage mangleUsage) { Abilities = abilities; FightDuration = fightDuration; MangleUsage = mangleUsage; float chanceExtraCPPerHit = abilities.ShredStats.ComboPointsGenerated - 1f; float c = chanceExtraCPPerHit, h = (1f - chanceExtraCPPerHit); _chanceExtraCP[0] = c; _chanceExtraCP[1] = c * h; _chanceExtraCP[2] = c * c + c * h * h; _chanceExtraCP[3] = 2 * c * c * h + c * h * h * h; _chanceExtraCP[4] = c * c * c + 3 * c * c * h * h + c * h * h * h * h; }
public override CharacterCalculationsBase GetCharacterCalculations(Character character, Item additionalItem, bool referenceCalculation, bool significantChange, bool needsDisplayCalculations) { // First things first, we need to ensure that we aren't using bad data CharacterCalculationsCat calculatedStats = new CharacterCalculationsCat(); if (character == null) { return calculatedStats; } CalculationOptionsCat calcOpts = character.CalculationOptions as CalculationOptionsCat; if (calcOpts == null) { return calculatedStats; } BossOptions bossOpts = character.BossOptions; if (bossOpts == null) { return calculatedStats; } int targetLevel = bossOpts.Level; int characterLevel = character.Level; float lagVariance = (float)calcOpts.LagVariance / 1000f; StatsCat stats = GetCharacterStats(character, additionalItem) as StatsCat; int levelDifference = (targetLevel - characterLevel); calculatedStats.BasicStats = stats; calculatedStats.TargetLevel = targetLevel; bool maintainMangleBecauseNooneElseIs = !character.ActiveBuffsConflictingBuffContains("Bleed Damage"); int T11Count; character.SetBonusCount.TryGetValue("Stormrider's Battlegarb", out T11Count); MangleUsage mangleusage = maintainMangleBecauseNooneElseIs ? MangleUsage.MaintainMangle : MangleUsage.None; if (calcOpts.CustomUseMangle && T11Count >= 4) { mangleusage = MangleUsage.Maintain4T11; } // else if (calcOpts.CustomUseMangle) { mangleusage = MangleUsage.MangleDPS; } if (mangleusage != MangleUsage.None) { stats.BonusBleedDamageMultiplier = (1f + stats.BonusBleedDamageMultiplier) * (1f + (maintainMangleBecauseNooneElseIs ? 0.30f : 0f)) // if someone else is putting up mangle, don't try to add to it here as it won't stack - 1f; } #region Basic Chances and Constants float modArmor = 1f - StatConversion.GetArmorDamageReduction(character.Level, bossOpts.Armor, stats.TargetArmorReduction, stats.ArmorPenetration); float critMultiplier = 2f * (1f + stats.BonusCritDamageMultiplier); float hasteBonus = StatConversion.GetPhysicalHasteFromRating(stats.HasteRating, CharacterClass.Druid); float attackSpeed = (1f / (1f + hasteBonus)) / (1f + stats.PhysicalHaste); float energyPerSecond = 10f * (1f + hasteBonus); float hitBonus = stats.PhysicalHit + StatConversion.GetPhysicalHitFromRating(stats.HitRating, CharacterClass.Druid); float expertiseBonus = StatConversion.GetDodgeParryReducFromExpertise(StatConversion.GetExpertiseFromRating(stats.ExpertiseRating, CharacterClass.Druid) + stats.Expertise, CharacterClass.Druid); float masteryBonus = StatConversion.GetMasteryFromRating(stats.MasteryRating, CharacterClass.Druid) + 8f; stats.NonShredBleedDamageMultiplier = masteryBonus * 0.031f; float chanceDodge = Math.Max(0f, StatConversion.WHITE_DODGE_CHANCE_CAP[targetLevel - 85] - expertiseBonus); float chanceMiss = Math.Max(0f, StatConversion.WHITE_MISS_CHANCE_CAP[targetLevel - 85] - hitBonus); float chanceAvoided = chanceMiss + chanceDodge; //Crit Chances float chanceCritYellow = 0f; float chanceCritBite = 0f; float chanceCritRip = 0f; float chanceCritRake = 0f; float chanceGlance = 0f; float chanceCritWhite = 0f; for (int i = 0; i < stats.TemporaryCritRatingUptimes.Length; i++) { //Sum up the weighted chances for each crit value WeightedStat iStat = stats.TemporaryCritRatingUptimes[i]; //Yellow - 2 Roll, so total of X chance to avoid, total of 1 chance to crit and hit when not avoided float chanceCritYellowTemp = Math.Min(1f, StatConversion.GetCritFromRating(stats.CritRating + iStat.Value, CharacterClass.Druid) + StatConversion.GetCritFromAgility(stats.Agility, CharacterClass.Druid) + stats.PhysicalCrit + StatConversion.NPC_LEVEL_CRIT_MOD[targetLevel - 85]); //float chanceHitYellowTemp = 1f - chanceCritYellowTemp; //float cpPerCPG = chanceHitYellow + chanceCritYellow * (1f + stats.CPOnCrit); //Bite - Identical to Yellow, with higher crit chance float chanceCritBiteTemp = Math.Min(1f, chanceCritYellowTemp + character.DruidTalents.RendAndTear * 0.25f / 3f); //float chanceHitBiteTemp = 1f - chanceCritBiteTemp; //Bleeds - 1 Roll, no avoidance, total of 1 chance to crit and hit float chanceCritRipTemp = Math.Min(1f, chanceCritYellowTemp); float chanceCritRakeTemp = Math.Min(1f, chanceCritYellowTemp); //White float chanceGlanceTemp = StatConversion.WHITE_GLANCE_CHANCE_CAP[targetLevel - 85]; float chanceCritWhiteTemp = Math.Min(chanceCritYellowTemp, 1f - chanceGlanceTemp - chanceAvoided); //float chanceHitWhiteTemp = 1f - chanceCritWhiteTemp - chanceAvoided - chanceGlanceTemp; chanceCritYellow += iStat.Chance * chanceCritYellowTemp; chanceCritBite += iStat.Chance * chanceCritBiteTemp; chanceCritRip += iStat.Chance * chanceCritRipTemp; chanceCritRake += iStat.Chance * chanceCritRakeTemp; chanceGlance += iStat.Chance * chanceGlanceTemp; chanceCritWhite += iStat.Chance * chanceCritWhiteTemp; } #endregion #region Attack Damages /* float baseDamage = 55f + (stats.AttackPower / 14f) + stats.WeaponDamage; float meleeDamageRaw = (baseDamage) * (1f + stats.BonusPhysicalDamageMultiplier) * (1f + stats.BonusDamageMultiplier) * modArmor; float mangleDamageRaw = (baseDamage * 2f + 566f) * (1f + stats.BonusPhysicalDamageMultiplier) * (1f + stats.BonusDamageMultiplier) * (1f + stats.BonusMangleDamageMultiplier) * modArmor; float shredDamageRaw = (baseDamage * 2.25f + 666f + stats.BonusShredDamage) * (1f + stats.BonusPhysicalDamageMultiplier) * (1f + stats.BonusDamageMultiplier) * (1f + stats.BonusShredDamageMultiplier) * (1f + stats.BonusBleedDamageMultiplier) * modArmor; float rakeDamageRaw = (176f + stats.AttackPower * 0.01f) * (1f + stats.BonusPhysicalDamageMultiplier) * (1f + stats.BonusDamageMultiplier) * (1f + stats.BonusRakeDamageMultiplier) * (1f + stats.BonusBleedDamageMultiplier); float rakeDamageDot = (1074f + stats.AttackPower * 0.18f) * (1f + stats.BonusPhysicalDamageMultiplier) * (1f + stats.BonusDamageMultiplier) * (1f + stats.BonusRakeDamageMultiplier) * (1f + stats.BonusBleedDamageMultiplier) * ((9f + stats.BonusRakeDuration) / 9f); float ripDamageRaw = (3006f + stats.AttackPower * 0.3f + (stats.BonusRipDamagePerCPPerTick * 5f * 6f)) * (1f + stats.BonusPhysicalDamageMultiplier) * (1f + stats.BonusDamageMultiplier) * (1f + stats.BonusRipDamageMultiplier) * (1f + stats.BonusBleedDamageMultiplier); float biteBaseDamageRaw = 190f * (1f + stats.BonusPhysicalDamageMultiplier) * (1f + stats.BonusDamageMultiplier) * (1f + stats.BonusFerociousBiteDamageMultiplier) * modArmor; float biteCPDamageRaw = (290f + stats.AttackPower * 0.07f) * (1f + stats.BonusPhysicalDamageMultiplier) * (1f + stats.BonusDamageMultiplier) * (1f + stats.BonusFerociousBiteDamageMultiplier) * modArmor; float meleeDamageAverage = chanceGlance * meleeDamageRaw * glanceMultiplier + chanceCritWhite * meleeDamageRaw * critMultiplier + chanceHitWhite * meleeDamageRaw; float mangleDamageAverage = (1f - chanceCritYellow) * mangleDamageRaw + chanceCritYellow * mangleDamageRaw * critMultiplier; float shredDamageAverage = (1f - chanceCritYellow) * shredDamageRaw + chanceCritYellow * shredDamageRaw * critMultiplier; float rakeDamageAverage = ((1f - chanceCritYellow) * rakeDamageRaw + chanceCritYellow * rakeDamageRaw * critMultiplier) + ((1f - chanceCritRake) * rakeDamageDot + chanceCritRake * rakeDamageDot * critMultiplierBleed); float ripDamageAverage = ((1f - chanceCritRip) * ripDamageRaw + chanceCritRip * ripDamageRaw * critMultiplierBleed); float biteBaseDamageAverage = (1f - chanceCritBite) * biteBaseDamageRaw + chanceCritBite * biteBaseDamageRaw * critMultiplier; float biteCPDamageAverage = (1f - chanceCritBite) * biteCPDamageRaw + chanceCritBite * biteCPDamageRaw * critMultiplier; //if (needsDisplayCalculations) //{ // Console.WriteLine("White: {0:P} Avoided, {1:P} Glance, {2:P} Hit, {3:P} Crit - Swing: {4}", chanceAvoided, chanceGlance, chanceHitWhite, chanceCritWhite, meleeDamageAverage); // Console.WriteLine("Yellow: {0:P} Avoided, {1:P} NonAvoided, {2:P} Hit, {3:P} Crit - Swing: {4}", chanceAvoided, chanceNonAvoided, 1f - chanceCritYellow, chanceCritYellow, mangleDamageAverage); // Console.WriteLine("Bite: {0:P} Avoided, {1:P} NonAvoided, {2:P} Hit, {3:P} Crit - Swing: {4}", chanceAvoided, chanceNonAvoided, 1f - chanceCritBite, chanceCritBite, biteBaseDamageAverage); // Console.WriteLine("RipBleed: {0:P} Hit, {1:P} Crit - Swing: {2}", 1f - chanceCritRip, chanceCritRip, ripDamageAverage); // Console.WriteLine(); //} #endregion #region Energy Costs float mangleEnergyRaw = 45f - stats.MangleCatCostReduction; float shredEnergyRaw = 60f - stats.ShredCostReduction; float rakeEnergyRaw = 40f - stats.RakeCostReduction; float ripEnergyRaw = 30f - stats.RipCostReduction; float biteEnergyRaw = 35f; //Assuming no wasted energy float roarEnergyRaw = 25f; //[rawCost + ((1/chance_to_land) - 1) * rawCost/5] float cpgEnergyCostMultiplier = 1f + ((1f / chanceNonAvoided) - 1f) * 0.2f; float finisherEnergyCostMultiplier = 1f + ((1f / chanceNonAvoided) - 1f) * (1f - stats.FinisherEnergyOnAvoid); float mangleEnergyAverage = mangleEnergyRaw * cpgEnergyCostMultiplier; float shredEnergyAverage = shredEnergyRaw * cpgEnergyCostMultiplier; float rakeEnergyAverage = rakeEnergyRaw * cpgEnergyCostMultiplier; float ripEnergyAverage = ripEnergyRaw * finisherEnergyCostMultiplier; float biteEnergyAverage = biteEnergyRaw * finisherEnergyCostMultiplier; float roarEnergyAverage = roarEnergyRaw; #endregion #region Ability Stats CatAbilityStats meleeStats = new CatMeleeStats() { DamagePerHit = meleeDamageRaw, DamagePerSwing = meleeDamageAverage, }; CatAbilityStats mangleStats = new CatMangleStats() { DamagePerHit = mangleDamageRaw, DamagePerSwing = mangleDamageAverage, DurationUptime = mangleDurationUptime, DurationAverage = mangleDurationAverage, EnergyCost = mangleEnergyAverage, }; CatAbilityStats shredStats = new CatShredStats() { DamagePerHit = shredDamageRaw, DamagePerSwing = shredDamageAverage, EnergyCost = shredEnergyAverage, }; CatAbilityStats rakeStats = new CatRakeStats() { DamagePerHit = rakeDamageRaw + rakeDamageDot, DamagePerSwing = rakeDamageAverage, DurationUptime = rakeDurationUptime, DurationAverage = rakeDurationAverage, EnergyCost = rakeEnergyAverage, }; CatAbilityStats ripStats = new CatRipStats() { DamagePerHit = ripDamageRaw, DamagePerSwing = ripDamageAverage, DurationUptime = ripDurationUptime, DurationAverage = ripDurationAverage, EnergyCost = ripEnergyAverage, }; CatAbilityStats biteStats = new CatBiteStats() { DamagePerHit = biteBaseDamageRaw, DamagePerSwing = biteBaseDamageAverage, DamagePerHitPerCP = biteCPDamageRaw, DamagePerSwingPerCP = biteCPDamageAverage, EnergyCost = biteEnergyAverage, }; CatAbilityStats roarStats = new CatRoarStats() { DurationUptime = roarBonusDuration, DurationAverage = 9f + roarBonusDuration, EnergyCost = roarEnergyAverage, DurationPerCP = 5f, }; */ #endregion #region Rotations CatAbilityBuilder abilities = new CatAbilityBuilder(stats, character.DruidTalents, character.MainHand == null ? 0.75f : ((character.MainHand.MinDamage + character.MainHand.MaxDamage) / 2f) / character.MainHand.Speed, attackSpeed, modArmor, hasteBonus, critMultiplier, chanceAvoided, chanceCritWhite, chanceCritYellow, chanceCritYellow, chanceCritYellow, chanceCritYellow, chanceCritRake, chanceCritRip, chanceCritBite, chanceGlance); var rotationCalculator = new CatRotationCalculator(abilities, 750/*bossOpts.BerserkTimer*/, mangleusage); var optimalRotation = rotationCalculator.GetOptimalRotation(); //TODO: Check for 4T11, maintain it if so calculatedStats.Abilities = abilities; calculatedStats.HighestDPSRotation = optimalRotation; //CatRotationCalculator rotationCalculator = new CatRotationCalculator(stats, calcOpts.Duration, cpPerCPG, // maintainMangle, berserkDuration, attackSpeed, // true, // character.DruidTalents.GlyphOfShred, chanceAvoided, chanceCritYellow * stats.BonusCPOnCrit, // cpgEnergyCostMultiplier, stats.ClearcastOnBleedChance, meleeStats, mangleStats, shredStats, // rakeStats, ripStats, biteStats, roarStats); //CatRotationCalculator.CatRotationCalculation rotationCalculationDPS = new CatRotationCalculator.CatRotationCalculation(); //for (int roarCP = 1; roarCP < 6; roarCP++) // for (int biteCP = 0; biteCP < 6; biteCP++) // for (int useRake = 0; useRake < 2; useRake++) // for (int useShred = 0; useShred < 2; useShred++) // for (int useRip = 0; useRip < 2; useRip++) // { // CatRotationCalculator.CatRotationCalculation rotationCalculation = // rotationCalculator.GetRotationCalculations( // useRake == 1, useShred == 1, useRip == 1, biteCP, roarCP); // if (rotationCalculation.DPS > rotationCalculationDPS.DPS) // rotationCalculationDPS = rotationCalculation; // } //calculatedStats.HighestDPSRotation = rotationCalculationDPS; //calculatedStats.CustomRotation = rotationCalculator.GetRotationCalculations( // calcOpts.CustomUseRake, calcOpts.CustomUseShred, calcOpts.CustomUseRip, calcOpts.CustomCPFerociousBite, calcOpts.CustomCPSavageRoar); //if (character.DruidTalents.GlyphOfShred && rotationCalculationDPS.ShredCount > 0) //{ // ripStats.DurationUptime += 6f; // ripStats.DurationAverage += 6f; //} //ripStats.DamagePerHit *= ripStats.DurationUptime / 12f; //ripStats.DamagePerSwing *= ripStats.DurationUptime / 12f; #endregion calculatedStats.AvoidedAttacks = chanceAvoided * 100f; calculatedStats.DodgedAttacks = chanceDodge * 100f; calculatedStats.MissedAttacks = chanceMiss * 100f; calculatedStats.CritChance = chanceCritYellow * 100f; calculatedStats.AttackSpeed = attackSpeed; calculatedStats.ArmorMitigation = (1f - modArmor) * 100f; calculatedStats.Duration = bossOpts.BerserkTimer; float magicDPS = (stats.ShadowDamage + stats.ArcaneDamage + stats.NatureDamage + stats.FireDamage + stats.FrostDamage) * (1f + chanceCritYellow); float physicalDPS = stats.PhysicalDamage; float abomDPS = (stats.MoteOfAnger * abilities.MeleeStats.DamageAverage); calculatedStats.DPSPoints = calculatedStats.HighestDPSRotation.DPS + magicDPS + physicalDPS + abomDPS; calculatedStats.SurvivabilityPoints = stats.Health / 1000f; calculatedStats.OverallPoints = calculatedStats.DPSPoints + calculatedStats.SurvivabilityPoints; return calculatedStats; }