public override ComparisonCalculationBase[] GetCustomChartData(Character character, string chartName) { switch (chartName) { case "Combat Table": CharacterCalculationsRogue currentCalculationsRogue = GetCharacterCalculations(character) as CharacterCalculationsRogue; ComparisonCalculationsRogue calcMiss = new ComparisonCalculationsRogue(); ComparisonCalculationsRogue calcDodge = new ComparisonCalculationsRogue(); ComparisonCalculationsRogue calcParry = new ComparisonCalculationsRogue(); ComparisonCalculationsRogue calcBlock = new ComparisonCalculationsRogue(); ComparisonCalculationsRogue calcGlance = new ComparisonCalculationsRogue(); ComparisonCalculationsRogue calcCrit = new ComparisonCalculationsRogue(); ComparisonCalculationsRogue calcHit = new ComparisonCalculationsRogue(); if (currentCalculationsRogue != null) { calcMiss.Name = " Miss "; calcDodge.Name = " Dodge "; calcGlance.Name = " Glance "; calcCrit.Name = " Crit "; calcHit.Name = "Hit"; float crits = 5f; float glancing = 25f; float hits = 100f - (crits + glancing); calcMiss.OverallPoints = 0f; calcDodge.OverallPoints = 0f; calcParry.OverallPoints = 0f; calcBlock.OverallPoints = 0f; calcGlance.OverallPoints = 0f; calcCrit.OverallPoints = 0f; calcHit.OverallPoints = 0f; } return(new ComparisonCalculationBase[] { calcMiss, calcDodge, calcParry, calcGlance, calcBlock, calcCrit, calcHit }); default: return(new ComparisonCalculationBase[0]); } }
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 CharacterCalculationsRogue calc = new CharacterCalculationsRogue(); if (character == null) { return calc; } CalculationOptionsRogue calcOpts = character.CalculationOptions as CalculationOptionsRogue; if (calcOpts == null) { return calc; } // BossOptions bossOpts = character.BossOptions; RogueTalents talents = character.RogueTalents; PTRMode = calcOpts.PTRMode; #region Spec determination int spec; int assCounter = 0, combatCounter = 0, subtCounter = 0; for (int i = 0; i <= 18; i++) assCounter += int.Parse(talents.ToString()[i].ToString()); for (int i = 19; i <= 37; i++) combatCounter += int.Parse(talents.ToString()[i].ToString()); for (int i = 38; i <= 56; i++) subtCounter += int.Parse(talents.ToString()[i].ToString()); if (assCounter > combatCounter && assCounter > subtCounter) spec = 0; else if (combatCounter > subtCounter) spec = 1; else spec = 2; #endregion int targetLevel = bossOpts.Level; float targetArmor = bossOpts.Armor; bool targetPoisonable = calcOpts.TargetPoisonable; WeightedStat[] critRatingUptimes; Stats stats = GetCharacterStatsWithTemporaryEffects(character, additionalItem, out critRatingUptimes); calc.BasicStats = stats; calc.TargetLevel = targetLevel; calc.Spec = spec; Item mainHand = character.MainHand == null ? new Knuckles() : character.MainHand.Item; Item offHand = character.OffHand == null ? new Knuckles() : character.OffHand.Item; #region Basic Chances and Constants #region Constants from talents float dmgBonusOnGarrRuptTickChance = RV.Talents.VenomousWoundsProcChance * talents.VenomousWounds; float cPGCritDmgMult = RV.Talents.LethalityCritMult * talents.Lethality; float ambushBSCostReduc = RV.Talents.SlaughterFTShadowsBSAmbushCostReduc[talents.SlaughterFromTheShadows]; float ambushCPBonus = RV.Talents.InitiativeChance * talents.Initiative; float ambushCritBonus = RV.Talents.ImpAmbushCritBonus * talents.ImprovedAmbush; float ambushDmgMult = RV.Talents.ImpAmbushDmgMult * talents.ImprovedAmbush + RV.Talents.OpportunityDmgMult * talents.Opportunity; float bSDmgMult = RV.Talents.AggressionDmgMult[talents.Aggression] + RV.Talents.OpportunityDmgMult * talents.Opportunity + (spec == 2 ? RV.Mastery.SinisterCallingMult: 0f); float bSCritBonus = talents.PuncturingWounds * RV.Talents.PuncturingWoundsBSCritMult + (stats.Rogue_T11_2P > 0 ? RV.Set.T112CritBonus : 0f); float evisCritBonus = (talents.GlyphOfEviscerate ? RV.Glyph.EvisCritMult : 0f); float mutiCritBonus = talents.PuncturingWounds * RV.Talents.PuncturingWoundsMutiCritMult + (stats.Rogue_T11_2P > 0 ? RV.Set.T112CritBonus : 0f); float ruptDmgMult = (spec == 2 ? RV.Mastery.Executioner + RV.Mastery.ExecutionerPerMast * StatConversion.GetMasteryFromRating(stats.MasteryRating) : 0f); float ruptDurationBonus = talents.GlyphOfRupture ? RV.Glyph.RuptBonusDuration : 0; float snDDurationBonus = talents.GlyphOfSliceandDice ? RV.Glyph.SnDBonusDuration : 0; float exposeDurationBonus = talents.GlyphOfExposeArmor ? RV.Glyph.ExposeBonusDuration : 0; float snDDurationMult = RV.Talents.ImpSliceAndDice * talents.ImprovedSliceAndDice; float sStrikeCritBonus = (stats.Rogue_T11_2P > 0 ? RV.Set.T112CritBonus : 0f); float cPonCPGCritChance = RV.Talents.SealFateChance * talents.SealFate; float evisDmgMult = (1f + RV.Talents.AggressionDmgMult[talents.Aggression] + RV.Talents.CoupDeGraceMult[talents.CoupDeGrace]) * (1f + (spec == 2 ? RV.Mastery.Executioner + RV.Mastery.ExecutionerPerMast * StatConversion.GetMasteryFromRating(stats.MasteryRating) : 0f)) - 1f; float envenomDmgMult = (1f + RV.Talents.CoupDeGraceMult[talents.CoupDeGrace]) * (1f + (spec == 2 ? RV.Mastery.Executioner + RV.Mastery.ExecutionerPerMast * StatConversion.GetMasteryFromRating(stats.MasteryRating) : 0f)) - 1f; float hemoCostReduc = RV.Talents.SlaughterFTShadowsHemoCostReduc * talents.SlaughterFromTheShadows; float hemoDmgMult = (spec == 2 ? RV.Mastery.SinisterCallingMult : 0f); float meleeDmgMult = spec == 0 && mainHand.Type == ItemType.Dagger && offHand.Type == ItemType.Dagger ? RV.Mastery.AssassinsResolveMeleeDmgBonus : 0f; float meleeSpeedMult = (1f + RV.Talents.LightningReflexesSpeedMult * talents.LightningReflexes) * (1f + RV.AR.MeleeSpeedMult * RV.AR.Duration / RV.AR.CD * talents.AdrenalineRush) * (1f + RV.SnD.SpeedBonus) - 1f; float mutiCostReduc = talents.GlyphOfMutilate ? RV.Glyph.MutiCostReduc : 0; float mutiDmgMult = RV.Talents.OpportunityDmgMult * talents.Opportunity; float oHDmgMult = (1f + RV.OHDmgReduc) * (1f + (spec == 1 ? RV.Mastery.AmbidexterityDmgMult : 0f)) - 1f; float potentPoisonsMult = (spec == 0 ? RV.Mastery.PotentPoisonsDmgMult + RV.Mastery.PotentPoisonsDmgMultPerMast * StatConversion.GetMasteryFromRating(stats.MasteryRating) : 0f); float spellDmgMult = character.ActiveBuffs.Contains(Buff.GetBuffByName("Lightning Breath")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Fire Breath")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Master Poisoner")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Ebon Plaguebringer")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Earth and Moon")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Curse of the Elements")) ? 0f : (calcOpts.TargetPoisonable ? RV.Talents.MasterPoisonerSpellDmgMult * talents.MasterPoisoner : 0f); float natureDmgMult = (1f + spellDmgMult) * (1f + stats.BonusNatureDamageMultiplier) * (1f + stats.BonusDamageMultiplier) * (1f + potentPoisonsMult) - 1f; float poisonDmgMult = (1f + spellDmgMult) * (1f + stats.BonusNatureDamageMultiplier) * (1f + stats.BonusDamageMultiplier) * (1f + potentPoisonsMult + RV.Talents.VilePoisonsDmgMult[talents.VilePoisons]) - 1f; float sSCostReduc = RV.Talents.ImpSinisterStrikeCostReduc * talents.ImprovedSinisterStrike; float sSDmgMult = (1f + RV.Talents.ImpSinisterStrikeDmgMult * talents.ImprovedSinisterStrike) * (1f + RV.Talents.AggressionDmgMult[talents.Aggression] * (1f + stats.FangsoftheFatherMultiplier)) - 1f; #endregion float exposeArmor = character.ActiveBuffs.Contains(Buff.GetBuffByName("Corrosive Spit")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Tear Armor")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Sunder Armor")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Faerie Fire")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Expose Armor")) ? 0f : RV.Expose.ArmorReduc; float findWeakness = talents.FindWeakness * RV.Talents.FindWeaknessArmorIgnore * ((RV.SD.Duration + RV.Talents.FindWeaknessDuration) / RV.SD.CD + (RV.Talents.FindWeaknessDuration / (RV.Vanish.CD - RV.Talents.ElusivenessVanishCDReduc * talents.Elusiveness) + (talents.Preparation > 0 ? RV.Talents.FindWeaknessDuration / (RV.Talents.PreparationCD * talents.Preparation) : 0f))); float modArmor = 1f - StatConversion.GetArmorDamageReduction(character.Level, bossOpts.Armor, stats.TargetArmorReduction + exposeArmor, 0f) * (1f - findWeakness); float critMultiplier = RV.CritDmgMult * (1f + stats.BonusCritDamageMultiplier); float critMultiplierPoison = RV.CritDmgMultPoison * (1f + stats.BonusCritDamageMultiplier); float hasteBonus = (1f + StatConversion.GetPhysicalHasteFromRating(stats.HasteRating, CharacterClass.Rogue)) * (1f + stats.PhysicalHaste) - 1f; float speedModifier = 1f / (1f + hasteBonus) / (1f + meleeSpeedMult); float mainHandSpeed = mainHand == null ? 0f : mainHand._speed * speedModifier; float offHandSpeed = offHand == null ? 0f : offHand._speed * speedModifier; float mainHandSpeedNorm = mainHand.Type == ItemType.Dagger ? RV.WeapSpeedNormDagger : RV.WeapSpeedNorm; float offHandSpeedNorm = mainHand.Type == ItemType.Dagger ? RV.WeapSpeedNormDagger : RV.WeapSpeedNorm; float hitBonus = stats.PhysicalHit + StatConversion.GetPhysicalHitFromRating(stats.HitRating, CharacterClass.Rogue); float spellHitBonus = stats.SpellHit + StatConversion.GetSpellHitFromRating(stats.HitRating, CharacterClass.Rogue); float expertiseMHBonus = ((character.Race == CharacterRace.Human && (mainHand.Type == ItemType.OneHandSword || mainHand.Type == ItemType.OneHandMace)) ? RV.Racial.HumanExpBonus : 0) + ((character.Race == CharacterRace.Dwarf && (mainHand.Type == ItemType.OneHandMace)) ? RV.Racial.DwarfExpBonus : 0) + ((character.Race == CharacterRace.Orc && (mainHand.Type == ItemType.OneHandAxe || mainHand.Type == ItemType.FistWeapon)) ? RV.Racial.OrcExpBonus : 0); float expertiseOHBonus = ((character.Race == CharacterRace.Human && (offHand.Type == ItemType.OneHandSword || offHand.Type == ItemType.OneHandMace)) ? RV.Racial.HumanExpBonus : 0) + ((character.Race == CharacterRace.Dwarf && (offHand.Type == ItemType.OneHandMace)) ? RV.Racial.DwarfExpBonus : 0) + ((character.Race == CharacterRace.Orc && (offHand.Type == ItemType.OneHandAxe || offHand.Type == ItemType.FistWeapon)) ? RV.Racial.OrcExpBonus : 0); expertiseMHBonus = StatConversion.GetDodgeParryReducFromExpertise(StatConversion.GetExpertiseFromRating(stats.ExpertiseRating, CharacterClass.Rogue) + stats.Expertise + expertiseMHBonus, CharacterClass.Rogue); expertiseOHBonus = StatConversion.GetDodgeParryReducFromExpertise(StatConversion.GetExpertiseFromRating(stats.ExpertiseRating, CharacterClass.Rogue) + stats.Expertise + expertiseOHBonus, CharacterClass.Rogue); float chanceMHDodge = Math.Max(0f, StatConversion.WHITE_DODGE_CHANCE_CAP[targetLevel - character.Level] - expertiseMHBonus); float chanceOHDodge = Math.Max(0f, StatConversion.WHITE_DODGE_CHANCE_CAP[targetLevel - character.Level] - expertiseOHBonus); float chanceParry = 0f; //Math.Max(0f, StatConversion.WHITE_PARRY_CHANCE_CAP[targetLevel - character.Level] - expertiseBonus); float chanceWhiteMiss = Math.Max(0f, StatConversion.WHITE_MISS_CHANCE_CAP_DW[targetLevel - character.Level] - hitBonus); float chanceMiss = Math.Max(0f, StatConversion.YELLOW_MISS_CHANCE_CAP[targetLevel - character.Level] - hitBonus); float chancePoisonMiss = Math.Max(0f, StatConversion.GetSpellMiss(character.Level - targetLevel, false) - spellHitBonus); float glanceMultiplier = RV.GlanceMult; float chanceWhiteMHAvoided = chanceWhiteMiss + chanceMHDodge + chanceParry; float chanceWhiteOHAvoided = chanceWhiteMiss + chanceOHDodge + chanceParry; float chanceMHAvoided = chanceMiss + chanceMHDodge + chanceParry; float chanceOHAvoided = chanceMiss + chanceOHDodge + chanceParry; float chanceFinisherAvoided = chanceMiss + chanceMHDodge + chanceParry; float chancePoisonAvoided = chancePoisonMiss; float chanceWhiteMHNonAvoided = 1f - chanceWhiteMHAvoided; float chanceWhiteOHNonAvoided = 1f - chanceWhiteOHAvoided; float chanceMHNonAvoided = 1f - chanceMHAvoided; float chanceOHNonAvoided = 1f - chanceOHAvoided; float chancePoisonNonAvoided = 1f - chancePoisonAvoided; ////Crit Chances float chanceCritYellow = 0f; float chanceHitYellow = 0f; float chanceCritAmbush = 0f; float chanceHitAmbush = 0f; float chanceCritBackstab = 0f; float chanceHitBackstab = 0f; float cpPerBackstab = 0f; float chanceCritMuti = 0f; float chanceHitMuti = 0f; float cpPerMuti = 0f; float chanceCritSStrike = 0f; float chanceHitSStrike = 0f; float cpPerSStrike = 0f; float chanceCritHemo = 0f; float chanceHitHemo = 0f; float cpPerHemo = 0f; float chanceCritRStrike = 0f; float chanceHitRStrike = 0f; float cpPerRStrike = 0f; float chanceCritEvis = 0f; float chanceHitEvis = 0f; //float chanceCritBleed = 0f; float chanceGlance = StatConversion.WHITE_GLANCE_CHANCE_CAP[targetLevel - character.Level]; float chanceCritWhiteMainTotal = 0f; float chanceCritWhiteMain = 0f; float chanceHitWhiteMain = 0f; float chanceCritWhiteOffTotal = 0f; float chanceCritWhiteOff = 0f; float chanceHitWhiteOff = 0f; float chanceCritPoison = 0f; float chanceHitPoison = 0f; for (int i = 0; i < critRatingUptimes.Length; i++) { //Sum up the weighted chances for each crit value WeightedStat iStat = critRatingUptimes[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.Rogue) + StatConversion.GetCritFromAgility(stats.Agility - RV.BaseStatCalcReduc, CharacterClass.Rogue) + stats.PhysicalCrit + StatConversion.NPC_LEVEL_CRIT_MOD[targetLevel - character.Level] ); float chanceHitYellowTemp = 1f - chanceCritYellowTemp; float chanceCritPoisonTemp = Math.Min(1f, StatConversion.GetSpellCritFromRating(stats.CritRating + iStat.Value) + stats.SpellCrit + stats.SpellCritOnTarget + StatConversion.NPC_LEVEL_CRIT_MOD[targetLevel - character.Level]); float chanceHitPoisonTemp = 1f - chanceCritPoisonTemp; //Ambush - Identical to Yellow, but higher crit chance float chanceCritAmbushTmp = Math.Min(1f, chanceCritYellowTemp + ambushCritBonus); float chanceHitAmbushTmp = 1f - chanceCritAmbushTmp; float cpPerAmbushTmp = (chanceHitAmbushTmp + chanceCritAmbushTmp * (1f + cPonCPGCritChance)) * (1f + ambushCPBonus); //Backstab - Identical to Yellow, with higher crit chance float chanceCritBackstabTemp = Math.Min(1f, chanceCritYellowTemp + bSCritBonus); float chanceHitBackstabTemp = 1f - chanceCritBackstabTemp; float cpPerBackstabTemp = (chanceHitBackstabTemp + chanceCritBackstabTemp * (1f + cPonCPGCritChance)); //Mutilate - Identical to Yellow, with higher crit chance float chanceCritMutiTemp = Math.Min(1f, chanceCritYellowTemp + mutiCritBonus); float chanceHitMutiTemp = 1f - chanceCritMutiTemp; float cpPerMutiTemp = (1 + chanceHitMutiTemp * chanceHitMutiTemp + (1 - chanceHitMutiTemp * chanceHitMutiTemp) * (1f + cPonCPGCritChance)); //Sinister Strike - Identical to Yellow, with higher crit chance float chanceCritSStrikeTemp = Math.Min(1f, chanceCritYellowTemp + sStrikeCritBonus); float chanceHitSStrikeTemp = 1f - chanceCritSStrikeTemp; float cpPerSStrikeTemp = (chanceHitSStrikeTemp + chanceCritSStrikeTemp * (1f + cPonCPGCritChance)) * (1f + (talents.GlyphOfSinisterStrike ? RV.Glyph.SSCPBonusChance : 0f)); //Hemorrhage - Identical to Yellow, with higher crit chance float chanceCritHemoTemp = Math.Min(1f, chanceCritYellowTemp); float chanceHitHemoTemp = 1f - chanceCritHemoTemp; float cpPerHemoTemp = (chanceHitHemoTemp + chanceCritHemoTemp * (1f + cPonCPGCritChance)); //Revealing Strike - Identical to Yellow, with higher crit chance float chanceCritRStrikeTemp = Math.Min(1f, chanceCritYellowTemp); float chanceHitRStrikeTemp = 1f - chanceCritRStrikeTemp; float cpPerRStrikeTemp = chanceHitRStrikeTemp + chanceCritRStrikeTemp; //Eviscerate - Identical to Yellow, with higher crit chance float chanceCritEvisTemp = Math.Min(1f, chanceCritYellowTemp + evisCritBonus); float chanceHitEvisTemp = 1f - chanceCritEvisTemp; //White Mainhand float chanceCritWhiteMainTotalTemp = chanceCritYellowTemp; float chanceCritWhiteMainTemp = Math.Min(chanceCritYellowTemp, 1f - chanceGlance - chanceWhiteMHAvoided); float chanceHitWhiteMainTemp = 1f - chanceCritWhiteMainTemp - chanceWhiteMHAvoided - chanceGlance; // float chanceCritWhiteMainTemp = Math.Min(chanceCritYellowTemp, 1f - chanceGlance); // float chanceHitWhiteMainTemp = 1f - chanceCritWhiteMainTemp - chanceGlance; //White Offhand float chanceCritWhiteOffTotalTemp = chanceCritYellowTemp; float chanceCritWhiteOffTemp = Math.Min(chanceCritYellowTemp, 1f - chanceGlance - chanceWhiteOHAvoided); float chanceHitWhiteOffTemp = 1f - chanceCritWhiteOffTemp - chanceWhiteOHAvoided - chanceGlance; // float chanceCritWhiteOffTemp = Math.Min(chanceCritYellowTemp, 1f - chanceGlance); // float chanceHitWhiteOffTemp = 1f - chanceCritWhiteOffTemp - chanceGlance; chanceCritYellow += iStat.Chance * chanceCritYellowTemp; chanceHitYellow += iStat.Chance * chanceHitYellowTemp; chanceCritAmbush += iStat.Chance * chanceCritAmbushTmp; chanceHitAmbush += iStat.Chance * chanceHitAmbushTmp; chanceCritBackstab += iStat.Chance * chanceCritBackstabTemp; chanceHitBackstab += iStat.Chance * chanceHitBackstabTemp; cpPerBackstab += iStat.Chance * cpPerBackstabTemp; chanceCritMuti += iStat.Chance * chanceCritMutiTemp; chanceHitMuti += iStat.Chance * chanceHitMutiTemp; cpPerMuti += iStat.Chance * cpPerMutiTemp; chanceCritSStrike += iStat.Chance * chanceCritSStrikeTemp; chanceHitSStrike += iStat.Chance * chanceHitSStrikeTemp; cpPerSStrike += iStat.Chance * cpPerSStrikeTemp; chanceCritHemo += iStat.Chance * chanceCritHemoTemp; chanceHitHemo += iStat.Chance * chanceHitHemoTemp; cpPerHemo += iStat.Chance * cpPerHemoTemp; chanceCritRStrike += iStat.Chance * chanceCritRStrikeTemp; chanceHitRStrike += iStat.Chance * chanceHitRStrikeTemp; cpPerRStrike += iStat.Chance * cpPerRStrikeTemp; chanceCritEvis += iStat.Chance * chanceCritEvisTemp; chanceHitEvis += iStat.Chance * chanceHitEvisTemp; chanceCritWhiteMainTotal += iStat.Chance * chanceCritWhiteMainTotalTemp; chanceCritWhiteMain += iStat.Chance * chanceCritWhiteMainTemp; chanceHitWhiteMain += iStat.Chance * chanceHitWhiteMainTemp; chanceCritWhiteOffTotal += iStat.Chance * chanceCritWhiteOffTotalTemp; chanceCritWhiteOff += iStat.Chance * chanceCritWhiteOffTemp; chanceHitWhiteOff += iStat.Chance * chanceHitWhiteOffTemp; chanceCritPoison += iStat.Chance * chanceCritPoisonTemp; chanceHitPoison += iStat.Chance * chanceHitPoisonTemp; } calc.DodgedMHAttacks = chanceMHDodge * 100f; calc.DodgedOHAttacks = chanceOHDodge * 100f; calc.ParriedAttacks = chanceParry * 100f; calc.MissedWhiteAttacks = chanceWhiteMiss * 100f; calc.MissedAttacks = chanceMiss * 100f; calc.MissedPoisonAttacks = chancePoisonMiss * 100f; float timeToReapplyDebuffs = 1f / (1f - chanceMHAvoided) - 1f; float lagVariance = (float)calcOpts.Latency / 1000f; float ruptDurationUptime = RV.Rupt.BaseDuration + ruptDurationBonus; float ruptDurationAverage = ruptDurationUptime + timeToReapplyDebuffs + lagVariance; float snDBonusDuration = snDDurationBonus - lagVariance; float recupBonusDuration = -lagVariance; float eABonusDuration = exposeDurationBonus - lagVariance; #endregion #region Attack Damages float DPSfromAP = stats.AttackPower / RV.APperDPS; float baseDamage = (mainHand == null ? 0f : mainHand._speed) * DPSfromAP + stats.WeaponDamage + (mainHand.MinDamage + mainHand.MaxDamage) / 2f; float baseDamageNorm = mainHandSpeedNorm * DPSfromAP + stats.WeaponDamage + (mainHand.MinDamage + mainHand.MaxDamage) / 2f; float baseOffDamage = ((offHand == null ? 0f : offHand._speed) * DPSfromAP + stats.WeaponDamage + (offHand.MinDamage + offHand.MaxDamage) / 2f) * (1f + oHDmgMult); float baseOffDamageNorm = (offHandSpeedNorm * DPSfromAP + stats.WeaponDamage + (offHand.MinDamage + offHand.MaxDamage) / 2f) * (1f + oHDmgMult); float meleeBonus = (1f + stats.BonusPhysicalDamageMultiplier) * (1f + stats.BonusDamageMultiplier) * (1f + meleeDmgMult); float meleeDamageRaw = baseDamage * meleeBonus * modArmor; float meleeOffDamageRaw = baseOffDamage * meleeBonus * modArmor; float meleeDamageNormRaw = baseDamageNorm * meleeBonus * modArmor; float meleeOffDamageNormRaw = baseOffDamageNorm * meleeBonus * modArmor; float backstabDamageRaw = (baseDamageNorm * RV.BS.WeapDmgMult + RV.BS.BonusDmg) * meleeBonus * (1f + bSDmgMult) * modArmor; backstabDamageRaw *= (mainHand._type == ItemType.Dagger ? 1f : 0f); float hemoDamageRaw = (baseDamageNorm * RV.Hemo.WeapDmgMult * (mainHand._type == ItemType.Dagger ? RV.Hemo.DaggerDmgMult : 0f)) * meleeBonus * (1f + hemoDmgMult) * modArmor; hemoDamageRaw *= (talents.Hemorrhage > 0 ? 1f: 0f); float sStrikeDamageRaw = (baseDamageNorm + RV.SS.BonusDmg) * meleeBonus * (1f + sSDmgMult) * modArmor; float mutiDamageMainRaw = (baseDamageNorm * RV.Muti.WeapDmgMult + RV.Muti.BonusDmg) * meleeBonus * (1f + mutiDmgMult) * (1f + (targetPoisonable ? 0.2f : 0f)) * modArmor; mutiDamageMainRaw *= ((spec == 0 && mainHand._type == ItemType.Dagger && offHand._type == ItemType.Dagger) ? 1f : 0f); float mutiDamageOffRaw = (baseOffDamageNorm * RV.Muti.WeapDmgMult + RV.Muti.BonusDmg) * meleeBonus * (1f + mutiDmgMult) * (1f + (targetPoisonable ? 0.2f : 0f)) * modArmor; mutiDamageOffRaw *= ((spec == 0 && mainHand._type == ItemType.Dagger && offHand._type == ItemType.Dagger) ? 1f : 0f); float mutiDamageRaw = mutiDamageMainRaw + mutiDamageOffRaw; float rStrikeDamageRaw = (baseDamage * RV.RS.WeapDmgMult) * meleeBonus * (1f + stats.FangsoftheFatherMultiplier) * modArmor; rStrikeDamageRaw *= talents.RevealingStrike > 0 ? 1f : 0f; float ambushDmgRaw = (baseDamageNorm + RV.Ambush.BonusDmg) * RV.Ambush.WeapDmgMult * (mainHand._type == ItemType.Dagger ? RV.Ambush.DaggerDmgMult : 1f) * (1f + ambushDmgMult); float[] ruptDamageRaw = new float[] {0, (RV.Rupt.BaseDmg + 1 * RV.Rupt.TickBaseDmg + RV.Rupt.TickAPMult[1] * stats.AttackPower) * (3f + 1f + ruptDurationBonus / RV.Rupt.TickTime) * meleeBonus * (1f + stats.BonusBleedDamageMultiplier), (RV.Rupt.BaseDmg + 2 * RV.Rupt.TickBaseDmg + RV.Rupt.TickAPMult[2] * stats.AttackPower) * (3f + 2f + ruptDurationBonus / RV.Rupt.TickTime) * meleeBonus * (1f + stats.BonusBleedDamageMultiplier), (RV.Rupt.BaseDmg + 3 * RV.Rupt.TickBaseDmg + RV.Rupt.TickAPMult[3] * stats.AttackPower) * (3f + 3f + ruptDurationBonus / RV.Rupt.TickTime) * meleeBonus * (1f + stats.BonusBleedDamageMultiplier), (RV.Rupt.BaseDmg + 4 * RV.Rupt.TickBaseDmg + RV.Rupt.TickAPMult[4] * stats.AttackPower) * (3f + 4f + ruptDurationBonus / RV.Rupt.TickTime) * meleeBonus * (1f + stats.BonusBleedDamageMultiplier), (RV.Rupt.BaseDmg + 5 * RV.Rupt.TickBaseDmg + RV.Rupt.TickAPMult[5] * stats.AttackPower) * (3f + 5f + ruptDurationBonus / RV.Rupt.TickTime) * meleeBonus * (1f + stats.BonusBleedDamageMultiplier)}; float evisBaseDamageRaw = RV.Evis.BaseAvgDmg * meleeBonus * (1f + evisDmgMult) * modArmor; float evisCPDamageRaw = (RV.Evis.CPBaseDmg + RV.Evis.CPAPMult * stats.AttackPower) * meleeBonus * (1f + evisDmgMult) * modArmor; float envenomBaseDamageRaw = RV.Envenom.BaseDmg * (1f + natureDmgMult) * (1f + envenomDmgMult) * (1f + meleeDmgMult); float envenomCPDamageRaw = (RV.Envenom.CPBaseDmg + RV.Envenom.CPAPMult * stats.AttackPower) * (1f + natureDmgMult) * (1f + envenomDmgMult) * (1f + meleeDmgMult); float iPDamageRaw = (RV.IP.BaseAvgDmg + RV.IP.APMult * stats.AttackPower) * (1f + poisonDmgMult); float dPDamageRaw = (RV.DP.BaseDmg + RV.DP.APMult * stats.AttackPower) * (1f + poisonDmgMult) * RV.DP.TickTime / RV.DP.Duration; float wPDamageRaw = (RV.WP.BaseDmg + RV.WP.APMult * stats.AttackPower) * (1f + poisonDmgMult); float venomousWoundsRaw = dmgBonusOnGarrRuptTickChance * (RV.Talents.VenomousWoundsBonusDmg + RV.Talents.VenomousWoundsAPMult * stats.AttackPower) * (1f + natureDmgMult); float meleeDamageAverage = (chanceGlance * glanceMultiplier + chanceCritWhiteMain * critMultiplier + chanceHitWhiteMain) * meleeDamageRaw; float meleeOffDamageAverage = (chanceGlance * glanceMultiplier + chanceCritWhiteOff * critMultiplier + chanceHitWhiteOff) * meleeOffDamageRaw; float meleeDamageNormAverage = (chanceGlance * glanceMultiplier + chanceCritWhiteMain * critMultiplier + chanceHitWhiteMain) * meleeDamageNormRaw; float meleeOffDamageNormAverage = (chanceGlance * glanceMultiplier + chanceCritWhiteOff * critMultiplier + chanceHitWhiteOff) * meleeOffDamageNormRaw; float mainGaucheDmgAvg = (1f - chanceCritYellow) * meleeDamageRaw + chanceCritYellow * meleeDamageRaw * critMultiplier; float backstabDamageAverage = (1f - chanceCritBackstab) * backstabDamageRaw + chanceCritBackstab * backstabDamageRaw * (critMultiplier + cPGCritDmgMult); float hemoDamageAverage = (1f - chanceCritHemo) * hemoDamageRaw + chanceCritHemo * hemoDamageRaw * (critMultiplier + cPGCritDmgMult); float sStrikeDamageAverage = (1f - chanceCritSStrike) * sStrikeDamageRaw + chanceCritSStrike * sStrikeDamageRaw * (critMultiplier + cPGCritDmgMult); float mutiDamageAverage = chanceHitMuti * chanceHitMuti * mutiDamageRaw + chanceCritMuti * chanceHitMuti * (mutiDamageMainRaw * (critMultiplier + cPGCritDmgMult) + mutiDamageOffRaw) + chanceHitMuti * chanceCritMuti * (mutiDamageMainRaw + mutiDamageOffRaw * (critMultiplier + cPGCritDmgMult)) + chanceCritMuti * chanceCritMuti * (mutiDamageMainRaw + mutiDamageOffRaw) * (critMultiplier + cPGCritDmgMult); float rStrikeDamageAverage = (1f - chanceCritRStrike) * rStrikeDamageRaw + chanceCritRStrike * rStrikeDamageRaw * critMultiplier; float ambushDmgAvg = (1f - chanceCritAmbush) * ambushDmgRaw + chanceCritAmbush * ambushDmgRaw * critMultiplier; float[] ruptDamageAverage = new float[] {0, ((1f - chanceCritYellow) * ruptDamageRaw[1] + chanceCritYellow * ruptDamageRaw[1] * critMultiplier), ((1f - chanceCritYellow) * ruptDamageRaw[2] + chanceCritYellow * ruptDamageRaw[2] * critMultiplier), ((1f - chanceCritYellow) * ruptDamageRaw[3] + chanceCritYellow * ruptDamageRaw[3] * critMultiplier), ((1f - chanceCritYellow) * ruptDamageRaw[4] + chanceCritYellow * ruptDamageRaw[4] * critMultiplier), ((1f - chanceCritYellow) * ruptDamageRaw[5] + chanceCritYellow * ruptDamageRaw[5] * critMultiplier)}; float evisBaseDamageAverage = (1f - chanceCritEvis) * evisBaseDamageRaw + chanceCritEvis * evisBaseDamageRaw * critMultiplier; float evisCPDamageAverage = (1f - chanceCritEvis) * evisCPDamageRaw + chanceCritEvis * evisCPDamageRaw * critMultiplier; float envenomBaseDamageAverage = (1f - chanceCritYellow) * envenomBaseDamageRaw + chanceCritYellow * envenomBaseDamageRaw * critMultiplier; float envenomCPDamageAverage = (1f - chanceCritYellow) * envenomCPDamageRaw + chanceCritYellow * envenomCPDamageRaw * critMultiplier; float iPDamageAverage = (1f - chanceCritPoison) * iPDamageRaw + chanceCritPoison * iPDamageRaw * critMultiplierPoison; float dPDamageAverage = (1f - chanceCritPoison) * dPDamageRaw + chanceCritPoison * dPDamageRaw * critMultiplierPoison; float wPDamageAverage = (1f - chanceCritPoison) * wPDamageRaw + chanceCritPoison * wPDamageRaw * critMultiplierPoison; float venomousWoundsAverage = (1f - chanceCritPoison) * venomousWoundsRaw + chanceCritPoison * venomousWoundsRaw * critMultiplierPoison; #endregion #region Energy Costs float ambushEnergyRaw = RV.Ambush.Cost - ambushBSCostReduc; //float garrEnergyRaw = RV.Garrote.Cost; float backstabEnergyRaw = RV.BS.Cost - ambushBSCostReduc - (talents.GlyphOfBackstab ? chanceCritBackstab * RV.Glyph.BSEnergyOnCrit : 0f); float hemoEnergyRaw = RV.Hemo.Cost - hemoCostReduc; float sStrikeEnergyRaw = RV.SS.Cost - sSCostReduc; float mutiEnergyRaw = RV.Muti.Cost - mutiCostReduc; float rSEnergyRaw = RV.RS.Cost; float ruptEnergyRaw = RV.Rupt.Cost; float evisEnergyRaw = RV.Evis.Cost; float envenomEnergyRaw = RV.Envenom.Cost; float snDEnergyRaw = RV.SnD.Cost; float recupEnergyRaw = RV.Recup.Cost; float exposeEnergyRaw = RV.Expose.Cost; //[rawCost + ((1/chance_to_land) - 1) * rawCost/5] float energyCostMultiplier = 1f + ((1f / chanceMHNonAvoided) - 1f) * (1 - RV.EnergyReturnOnAvoid); float backstabEnergyAverage = backstabEnergyRaw * energyCostMultiplier; float hemoEnergyAverage = hemoEnergyRaw * energyCostMultiplier; float sStrikeEnergyAverage = sStrikeEnergyRaw * energyCostMultiplier; float mutiEnergyAverage = mutiEnergyRaw * energyCostMultiplier; float rSEnergyAverage = rSEnergyRaw * energyCostMultiplier; float ruptEnergyAverage = ruptEnergyRaw * energyCostMultiplier; float evisEnergyAverage = evisEnergyRaw * energyCostMultiplier; float envenomEnergyAverage = envenomEnergyRaw * energyCostMultiplier; float snDEnergyAverage = snDEnergyRaw * energyCostMultiplier; float recupEnergyAverage = recupEnergyRaw * energyCostMultiplier; float eAEnergyAverage = exposeEnergyRaw * energyCostMultiplier; #endregion #region Ability Stats RogueAbilityStats mainHandStats = new RogueMHStats() { DamagePerHit = meleeDamageRaw, DamagePerSwing = meleeDamageAverage, Weapon = mainHand, CritChance = chanceCritWhiteMain, }; RogueAbilityStats offHandStats = new RogueOHStats() { DamagePerHit = meleeOffDamageRaw, DamagePerSwing = meleeOffDamageAverage, Weapon = offHand, CritChance = chanceCritWhiteOff, }; RogueAbilityStats mainGaucheStats = new RogueMainGaucheStats() { DamagePerHit = meleeDamageNormRaw, DamagePerSwing = mainGaucheDmgAvg, Weapon = mainHand, CritChance = chanceCritYellow, }; RogueAbilityStats backstabStats = new RogueBackstabStats() { DamagePerHit = backstabDamageRaw, DamagePerSwing = backstabDamageAverage, EnergyCost = backstabEnergyAverage, CritChance = chanceCritBackstab, CPPerSwing = cpPerBackstab, }; RogueAbilityStats hemoStats = new RogueHemoStats() { DamagePerHit = hemoDamageRaw, DamagePerSwing = hemoDamageAverage, EnergyCost = hemoEnergyAverage, CritChance = chanceCritHemo, CPPerSwing = cpPerHemo, }; RogueAbilityStats sStrikeStats = new RogueSStrikeStats() { DamagePerHit = sStrikeDamageRaw, DamagePerSwing = sStrikeDamageAverage, EnergyCost = sStrikeEnergyAverage, CritChance = chanceCritSStrike, CPPerSwing = cpPerSStrike, }; RogueAbilityStats mutiStats = new RogueMutiStats() { DamagePerHit = mutiDamageRaw, DamagePerSwing = mutiDamageAverage, EnergyCost = mutiEnergyAverage, CritChance = chanceCritMuti, CPPerSwing = cpPerMuti, }; RogueAbilityStats rStrikeStats = new RogueRStrikeStats() { DamagePerHit = rStrikeDamageRaw, DamagePerSwing = rStrikeDamageAverage, EnergyCost = rSEnergyAverage, CritChance = chanceCritRStrike, CPPerSwing = cpPerRStrike, }; RogueAbilityStats ruptStats = new RogueRuptStats() { DamagePerHitArray = new float[] {0, ruptDamageRaw[1], ruptDamageRaw[2], ruptDamageRaw[3], ruptDamageRaw[4], ruptDamageRaw[5]}, DamagePerSwingArray = new float[] {0, ruptDamageAverage[1], ruptDamageAverage[2], ruptDamageAverage[3], ruptDamageAverage[4], ruptDamageAverage[5]}, DurationUptime = ruptDurationUptime, DurationAverage = ruptDurationAverage, DurationPerCP = RV.Rupt.DurationPerCP, EnergyCost = ruptEnergyAverage, }; RogueAbilityStats evisStats = new RogueEvisStats() { DamagePerHit = evisBaseDamageRaw, DamagePerSwing = evisBaseDamageAverage, DamagePerHitPerCP = evisCPDamageRaw, DamagePerSwingPerCP = evisCPDamageAverage, EnergyCost = evisEnergyAverage, CritChance = chanceCritEvis, DamagePerCrit = evisBaseDamageRaw * critMultiplier, DamagePerCritPerCP = evisCPDamageRaw * critMultiplier, }; RogueAbilityStats envenomStats = new RogueEnvenomStats() { DamagePerHit = envenomBaseDamageRaw, DamagePerSwing = envenomBaseDamageAverage, DamagePerHitPerCP = envenomCPDamageRaw, DamagePerSwingPerCP = envenomCPDamageAverage, EnergyCost = envenomEnergyAverage, CritChance = chanceCritYellow, DamagePerCrit = envenomBaseDamageRaw * critMultiplier, DamagePerCritPerCP = envenomCPDamageRaw * critMultiplier, }; RogueAbilityStats snDStats = new RogueSnDStats() { DurationUptime = snDBonusDuration * (1f + snDDurationMult), DurationAverage = (RV.SnD.BaseDuration + snDBonusDuration) * (1f + snDDurationMult), EnergyCost = snDEnergyAverage, DurationPerCP = RV.SnD.DurationPerCP, }; RogueAbilityStats recupStats = new RogueRecupStats() { DurationUptime = recupBonusDuration, DurationAverage = RV.Recup.BaseDuration + recupBonusDuration, EnergyCost = recupEnergyAverage, DurationPerCP = RV.Recup.DurationPerCP, }; RogueAbilityStats exposeStats = new RogueExposeStats() { DurationUptime = eABonusDuration, DurationAverage = RV.Expose.BaseDuration + eABonusDuration, EnergyCost = eAEnergyAverage, DurationPerCP = RV.Expose.DurationPerCP, }; RogueAbilityStats iPStats = new RogueIPStats() { DamagePerHit = iPDamageRaw, DamagePerSwing = iPDamageAverage, }; RogueAbilityStats dPStats = new RogueDPStats() { DamagePerHit = dPDamageRaw, DamagePerSwing = dPDamageAverage, }; RogueAbilityStats wPStats = new RogueWPStats() { DamagePerHit = wPDamageRaw, DamagePerSwing = wPDamageAverage, }; RogueAbilityStats venomousWoundsStats = new RogueVenomousWoundsStats() { DamagePerHit = venomousWoundsRaw, DamagePerSwing = venomousWoundsAverage, }; #endregion #region Rotations RogueRotationCalculator rotationCalculator; RogueRotationCalculator.RogueRotationCalculation rotationCalculationOptimal; // BonusPhysicalDamageMultiplier = character.ActiveBuffs.Contains(Buff.GetBuffByName("Ravage")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Acid Spit")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Brittle Bones")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Blood Frenzy")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Savage Combat")) ? 0f : RV.Talents.SavageCombatMult * talents.SavageCombat, #region Assassination if (spec == 0) { rotationCalculator = new RogueRotationCalculatorAss(character, stats, bossOpts, calcOpts, hasteBonus, mainHandSpeed, offHandSpeed, mainHandSpeedNorm, offHandSpeedNorm, chanceWhiteMHAvoided, chanceWhiteOHAvoided, chanceMHAvoided, chanceOHAvoided, chanceFinisherAvoided, chancePoisonAvoided, chanceCritYellow * cPonCPGCritChance, (1f - chanceHitMuti * chanceHitMuti) * cPonCPGCritChance, mainHandStats, offHandStats, backstabStats, mutiStats, ruptStats, envenomStats, snDStats, exposeStats, iPStats, dPStats, wPStats, venomousWoundsStats); rotationCalculationOptimal = new RogueRotationCalculatorAss.RogueRotationCalculation(); bool segmentedOptimize = talents.MurderousIntent > 0; int numberOfSegments = segmentedOptimize ? 2 : 1; float durationMultiplier = 1f; if (!calcOpts.ForceCustom) { while (numberOfSegments > 0) { if (segmentedOptimize && numberOfSegments == 2) durationMultiplier = 1 - RV.Talents.MurderousIntentThreshold; else if (segmentedOptimize && numberOfSegments == 1) durationMultiplier = RV.Talents.MurderousIntentThreshold; RogueRotationCalculator.RogueRotationCalculation rotationCalculationDPS = new RogueRotationCalculatorAss.RogueRotationCalculation(); for (int snDCP = 4; snDCP < 6; snDCP++) for (int finisherCP = 4; finisherCP < 6; finisherCP++) for (int CPG = 0; CPG < 2; CPG++) { if (CPG == 1 && (!calcOpts.EnableBS || backstabStats.DamagePerSwing == 0)) continue; for (int ruptCP = 3; ruptCP < 6; ruptCP++) { if (ruptCP > 3 && !calcOpts.EnableRupt) continue; for (int mHPoison = 1; mHPoison < 3; mHPoison++) { if (mainHand == null) break; if ((mHPoison == 1 && !calcOpts.EnableIP) || (mHPoison == 2 && !calcOpts.EnableDP)) continue; for (int oHPoison = 1; oHPoison < 3; oHPoison++) { if (offHand == null) break; if ((oHPoison == 1 && !calcOpts.EnableIP) || (oHPoison == 2 && !calcOpts.EnableDP)) continue; bool useTotT = false; RogueRotationCalculatorAss.RogueRotationCalculation rotationCalculation = rotationCalculator.GetRotationCalculations(durationMultiplier, CPG, 0, (ruptCP == 3 ? 0 : ruptCP), false, 0, finisherCP, snDCP, mHPoison, oHPoison, useTotT, (int)exposeArmor, PTRMode); if (rotationCalculation.DPS > rotationCalculationDPS.DPS) rotationCalculationDPS = rotationCalculation; } } } } if (numberOfSegments == 2) rotationCalculationOptimal = rotationCalculationDPS; else if (segmentedOptimize) rotationCalculationOptimal += rotationCalculationDPS; else rotationCalculationOptimal = rotationCalculationDPS; numberOfSegments--; } } numberOfSegments = segmentedOptimize ? 2 : 1; durationMultiplier = 1f; while (numberOfSegments > 0) { if (segmentedOptimize && numberOfSegments == 2) durationMultiplier = 1 - RV.Talents.MurderousIntentThreshold; else if (segmentedOptimize && numberOfSegments == 1) durationMultiplier = RV.Talents.MurderousIntentThreshold; RogueRotationCalculator.RogueRotationCalculation rotationCalculationDPS = new RogueRotationCalculatorAss.RogueRotationCalculation(); rotationCalculationDPS = rotationCalculator.GetRotationCalculations( durationMultiplier, calcOpts.CustomCPG, calcOpts.CustomRecupCP, calcOpts.CustomRuptCP, calcOpts.CustomUseRS, calcOpts.CustomFinisher, calcOpts.CustomCPFinisher, calcOpts.CustomCPSnD, calcOpts.CustomMHPoison, calcOpts.CustomOHPoison, calcOpts.CustomUseTotT, (int)exposeArmor, PTRMode); if (numberOfSegments == 2) calc.CustomRotation = rotationCalculationDPS; else if (segmentedOptimize) calc.CustomRotation += rotationCalculationDPS; else calc.CustomRotation = rotationCalculationDPS; numberOfSegments--; } } #endregion #region Combat else if (spec == 1) { rotationCalculator = new RogueRotationCalculatorCombat(character, stats, bossOpts, calcOpts, hasteBonus, mainHandSpeed, offHandSpeed, mainHandSpeedNorm, offHandSpeedNorm, chanceWhiteMHAvoided, chanceWhiteOHAvoided, chanceMHAvoided, chanceOHAvoided, chanceFinisherAvoided, chancePoisonAvoided, chanceCritYellow * cPonCPGCritChance, mainHandStats, offHandStats, mainGaucheStats, sStrikeStats, rStrikeStats, ruptStats, evisStats, snDStats, exposeStats, iPStats, dPStats, wPStats); rotationCalculationOptimal = new RogueRotationCalculatorCombat.RogueRotationCalculation(); if (!calcOpts.ForceCustom) { RogueRotationCalculator.RogueRotationCalculation rotationCalculationDPS = new RogueRotationCalculatorCombat.RogueRotationCalculation(); for (int snDCP = 4; snDCP < 6; snDCP++) for (int finisherCP = 4; finisherCP < 6; finisherCP++) for (int ruptCP = 3; ruptCP < 6; ruptCP++) for (int useRS = 0; useRS < 2; useRS++) { if (useRS == 1 && (!calcOpts.EnableRS || rStrikeStats.DamagePerSwing == 0)) continue; for (int mHPoison = 1; mHPoison < 4; mHPoison++) { if (!targetPoisonable || mainHand == null) break; if ((mHPoison == 1 && !calcOpts.EnableIP) || (mHPoison == 2 && !calcOpts.EnableDP) || (mHPoison == 3 && !calcOpts.EnableWP)) continue; for (int oHPoison = 1; oHPoison < 4; oHPoison++) { if (!targetPoisonable || offHand == null) break; if ((oHPoison == 1 && !calcOpts.EnableIP) || (oHPoison == 2 && !calcOpts.EnableDP) || (oHPoison == 3 && !calcOpts.EnableWP)) continue; bool useTotT = false; RogueRotationCalculator.RogueRotationCalculation rotationCalculation = rotationCalculator.GetRotationCalculations(0, 0, 0, (ruptCP == 3 ? 0 : ruptCP), useRS == 1, 0, finisherCP, snDCP, mHPoison, oHPoison, useTotT, (int)exposeArmor, PTRMode); if (rotationCalculation.DPS > rotationCalculationDPS.DPS) rotationCalculationDPS = rotationCalculation; } } } rotationCalculationOptimal = rotationCalculationDPS; } calc.CustomRotation = rotationCalculator.GetRotationCalculations(0, calcOpts.CustomCPG, calcOpts.CustomRecupCP, calcOpts.CustomRuptCP, calcOpts.CustomUseRS, calcOpts.CustomFinisher, calcOpts.CustomCPFinisher, calcOpts.CustomCPSnD, calcOpts.CustomMHPoison, calcOpts.CustomOHPoison, calcOpts.CustomUseTotT, (int)exposeArmor, PTRMode); } #endregion #region Subtlety else { rotationCalculator = new RogueRotationCalculatorSubt(character, stats, bossOpts, calcOpts, hasteBonus, mainHandSpeed, offHandSpeed, mainHandSpeedNorm, offHandSpeedNorm, chanceWhiteMHAvoided, chanceWhiteOHAvoided, chanceMHAvoided, chanceOHAvoided, chanceFinisherAvoided, chancePoisonAvoided, chanceCritYellow * cPonCPGCritChance, mainHandStats, offHandStats, backstabStats, hemoStats, ruptStats, evisStats, snDStats, recupStats, exposeStats, iPStats, dPStats, wPStats); rotationCalculationOptimal = new RogueRotationCalculatorSubt.RogueRotationCalculation(); bool useHemo = !(character.ActiveBuffs.Contains(Buff.GetBuffByName("Mangle")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Hemorrhage")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Blood Frenzy")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Gore")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Stampede")) || character.ActiveBuffs.Contains(Buff.GetBuffByName("Tendon Rip"))); if (!calcOpts.ForceCustom) { RogueRotationCalculator.RogueRotationCalculation rotationCalculationDPS = new RogueRotationCalculatorSubt.RogueRotationCalculation(); for (int snDCP = 4; snDCP < 6; snDCP++) for (int finisherCP = 4; finisherCP < 6; finisherCP++) for (int CPG = 0; CPG < 2; CPG++) { if ((CPG == 0 && (!calcOpts.EnableBS || backstabStats.DamagePerSwing == 0)) || (CPG == 1 && (!calcOpts.EnableHemo || hemoStats.DamagePerSwing == 0))) continue; for (int ruptCP = 3; ruptCP < 6; ruptCP++) { if (ruptCP > 3 && !calcOpts.EnableRupt) continue; for (int recupCP = 3; recupCP < 6; recupCP++) { if (recupCP > 3 && !calcOpts.EnableRecup) continue; for (int mHPoison = 1; mHPoison < 3; mHPoison++) { if (!targetPoisonable || mainHand == null) break; if ((mHPoison == 1 && !calcOpts.EnableIP) || (mHPoison == 2 && !calcOpts.EnableDP)) continue; for (int oHPoison = 1; oHPoison < 3; oHPoison++) { if (!targetPoisonable || offHand == null) break; if ((oHPoison == 1 && !calcOpts.EnableIP) || (oHPoison == 2 && !calcOpts.EnableDP)) continue; bool useTotT = false; RogueRotationCalculator.RogueRotationCalculation rotationCalculation = rotationCalculator.GetRotationCalculations(0, CPG, (recupCP == 3 ? 0 : recupCP), (ruptCP == 3 ? 0 : ruptCP), useHemo, 0, finisherCP, snDCP, mHPoison, oHPoison, useTotT, (int)exposeArmor, PTRMode); if (rotationCalculation.DPS > rotationCalculationDPS.DPS) rotationCalculationDPS = rotationCalculation; } } } } } rotationCalculationOptimal = rotationCalculationDPS; } calc.CustomRotation = rotationCalculator.GetRotationCalculations(0, calcOpts.CustomCPG, calcOpts.CustomRecupCP, calcOpts.CustomRuptCP, calcOpts.CustomUseRS, calcOpts.CustomFinisher, calcOpts.CustomCPFinisher, calcOpts.CustomCPSnD, calcOpts.CustomMHPoison, calcOpts.CustomOHPoison, calcOpts.CustomUseTotT, (int)exposeArmor, PTRMode); } #endregion calc.HighestDPSRotation = calcOpts.ForceCustom == false ? rotationCalculationOptimal : calc.CustomRotation; #endregion calc.AvoidedWhiteMHAttacks = chanceWhiteMHAvoided * 100f; calc.AvoidedWhiteOHAttacks = chanceWhiteOHAvoided * 100f; calc.AvoidedAttacks = chanceMHAvoided * 100f; calc.AvoidedFinisherAttacks = chanceFinisherAvoided * 100f; calc.AvoidedPoisonAttacks = chancePoisonAvoided * 100f; calc.DodgedMHAttacks = chanceMHDodge * 100f; calc.ParriedAttacks = chanceParry * 100f; calc.MissedAttacks = chanceMiss * 100f; calc.CritChanceYellow = chanceCritYellow * 100f; calc.CritChanceMHTotal = chanceCritWhiteMainTotal * 100f; calc.CritChanceMH = chanceCritWhiteMain * 100f; calc.CritChanceOHTotal = chanceCritWhiteOffTotal * 100f; calc.CritChanceOH = chanceCritWhiteOff * 100f; calc.MainHandSpeed = mainHandSpeed; calc.OffHandSpeed = offHandSpeed; calc.ArmorMitigation = (1f - modArmor) * 100f; calc.Duration = bossOpts.BerserkTimer; calc.MainHandStats = mainHandStats; calc.OffHandStats = offHandStats; calc.MainGaucheStats = mainGaucheStats; calc.BackstabStats = backstabStats; calc.HemoStats = hemoStats; calc.SStrikeStats = sStrikeStats; calc.MutiStats = mutiStats; calc.RStrikeStats = rStrikeStats; calc.RuptStats = ruptStats; calc.SnDStats = snDStats; calc.EvisStats = evisStats; calc.EnvenomStats = envenomStats; calc.IPStats = iPStats; calc.DPStats = dPStats; calc.WPStats = wPStats; calc.VenomousWoundsStats = venomousWoundsStats; float magicDPS = 0f; // (stats.ShadowDamage + stats.ArcaneDamage) * (1f + chanceCritYellow); calc.DPSPoints = calc.HighestDPSRotation.DPS + magicDPS; calc.SurvivabilityPoints = stats.Health / 100f; calc.OverallPoints = calc.DPSPoints + calc.SurvivabilityPoints; return calc; }
/// <summary> /// Calculate damage output /// </summary> /// <param name="character"></param> /// <param name="additionalItem"></param> /// <returns></returns> /// Much of this code is based on Aldriana's RogueCalc /// public override CharacterCalculationsBase GetCharacterCalculations(Character character, Item additionalItem) { float energyCPG, numCPG, sndLength, finisherCost, sndEnergy, sndHaste, cycleTime, energyRegen, ruthlessnessCP; float totalHaste; string cpg; float missChance, mhDodgeChance, ohDodgeChance, glanceChance, mhExpertise, ohExpertise, mhCrit, ohCrit, probMHHit, probOHHit; float mhHastedSpeed, ohHastedSpeed, avgMHDmg, avgOHDmg, totalArmor; float mhAttacks, ohAttacks, ohHits, ssHits, wfHits, avgWFDmg; float whiteDPS, finisherDPS, wfDPS, ssDPS, poisonDPS, cpgDPS; float mhWhite, ohWhite, damageReduction, bonusWhiteCritDmg; float avgCPGDmg, bonusCPGCrit, bonusCPGDmgMult, bonusCPGCritDmgMult, cpgCrit; float finisherDmg, evisMod, evisMin, evisMax; float probPoison; bool calcDeadly; CalculationOptionsRogue calcOpts = character.CalculationOptions as CalculationOptionsRogue; Stats stats = GetCharacterStats(character, additionalItem); CharacterCalculationsRogue calculatedStats = new CharacterCalculationsRogue(); calculatedStats.BasicStats = stats; whiteDPS = finisherDPS = wfDPS = ssDPS = poisonDPS = cpgDPS = 0f; missChance = 28f; missChance -= calcOpts.Precision + stats.Hit + stats.HitRating * RogueConversions.HitRatingToHit; if (missChance < 0f) { missChance = 0f; } mhExpertise = ohExpertise = calcOpts.WeaponExpertise * 5f + stats.Expertise + stats.ExpertiseRating * RogueConversions.ExpertiseRatingToExpertise; if (character.Race == Character.CharacterRace.Human) { if (character.MainHand != null && (character.MainHand.Type == Item.ItemType.OneHandSword || character.MainHand.Type == Item.ItemType.OneHandMace)) { mhExpertise += 5f; } if (character.OffHand != null && (character.OffHand.Type == Item.ItemType.OneHandSword || character.OffHand.Type == Item.ItemType.OneHandMace)) { ohExpertise += 5f; } } mhDodgeChance = 6.5f; mhDodgeChance -= .25f * mhExpertise; ohDodgeChance = 6.5f; ohDodgeChance -= .25f * ohExpertise; if (mhDodgeChance < 0f) { mhDodgeChance = 0f; } if (ohDodgeChance < 0f) { ohDodgeChance = 0f; } probMHHit = 1f - missChance / 100f - mhDodgeChance / 100f; probOHHit = 1f - missChance / 100f - ohDodgeChance / 100f; glanceChance = .25f; mhCrit = ohCrit = stats.Crit + stats.CritRating * RogueConversions.CritRatingToCrit; if (character.MainHand != null && character.MainHand.Type == Item.ItemType.Dagger) { mhCrit += calcOpts.DaggerSpecialization; } if (character.OffHand != null && character.OffHand.Type == Item.ItemType.Dagger) { ohCrit += calcOpts.DaggerSpecialization; } if (character.MainHand != null && character.MainHand.Type == Item.ItemType.FistWeapon) { mhCrit += calcOpts.FistSpecialization; } if (character.OffHand != null && character.OffHand.Type == Item.ItemType.FistWeapon) { ohCrit += calcOpts.FistSpecialization; } // if we have mutilate and we're using two daggers, assume we use it to generate CPs if (calcOpts.Mutilate > 0 && character.MainHand != null && character.MainHand.Type == Item.ItemType.Dagger && character.OffHand != null && character.OffHand.Type == Item.ItemType.Dagger) { cpg = "mutilate"; energyCPG = 60f; } // if we're main handing a dagger, assume we're using backstab it to generate CPs else if (character.MainHand != null && character.MainHand.Type == Item.ItemType.Dagger) { cpg = "backstab"; energyCPG = 60f; } // if we have hemo, assume we use it to generate CPs else if (calcOpts.Hemorrhage > 0) { cpg = "hemo"; energyCPG = 35f; } // otherwise use sinister strike else { cpg = "ss"; switch (calcOpts.ImprovedSinisterStrike) { case 2: energyCPG = 40f; break; case 1: energyCPG = 42f; break; default: energyCPG = 45f; break; } } // cycle stuff sndLength = 6f + 3f * calcOpts.DPSCycle['s']; sndLength += stats.BonusSnDDuration; sndLength *= 1f + 0.15f * calcOpts.ImprovedSliceandDice; ruthlessnessCP = .2f * calcOpts.Ruthlessness; numCPG = calcOpts.DPSCycle.TotalComboPoints - 2f * ruthlessnessCP; if (calcOpts.DPSCycle['r'] > 0) { finisherCost = 25f; } else if (calcOpts.DPSCycle['e'] > 0) { finisherCost = 35f; } else { finisherCost = 0f; } energyRegen = 10f; if (calcOpts.AdrenalineRush > 0) { energyRegen += .5f; } sndEnergy = (calcOpts.DPSCycle['s'] - ruthlessnessCP) * energyCPG + 25f; sndHaste = .3f; sndHaste *= (1f + stats.BonusSnDHaste); totalArmor = calcOpts.TargetArmor - stats.ArmorPenetration; damageReduction = 1f - (totalArmor / (totalArmor + 10557.5f)); #region White Damage whiteDPS = mhWhite = ohWhite = 0f; totalHaste = 1f; totalHaste *= (1f + sndHaste) * (1f + (stats.HasteRating * RogueConversions.HasteRatingToHaste) / 100); totalHaste *= (1f + .2f * 15f / 120f * calcOpts.BladeFlurry); bonusWhiteCritDmg = 1f + stats.BonusCritMultiplier; // MH mhAttacks = 0f; avgMHDmg = 0f; ohHits = 0f; if (character.MainHand != null) { avgMHDmg = (character.MainHand.MinDamage + character.MainHand.MaxDamage + stats.WeaponDamage * 2) / 2.0f; avgMHDmg += (stats.AttackPower / 14.0f) * character.MainHand.Speed; //mhHastedSpeed = character.MainHand.Speed / totalHaste; mhAttacks = totalHaste / character.MainHand.Speed; mhWhite = avgMHDmg * mhAttacks * probMHHit; mhWhite = (1f - mhCrit / 100f) * mhWhite + (mhCrit / 100f) * (mhWhite * (2f * bonusWhiteCritDmg)); mhWhite *= damageReduction; } // OH ohAttacks = 0f; if (character.OffHand != null) { avgOHDmg = (character.OffHand.MinDamage + character.OffHand.MaxDamage + stats.WeaponDamage * 2) / 2.0f; avgOHDmg += (stats.AttackPower / 14.0f) * character.OffHand.Speed; avgOHDmg *= (0.25f + calcOpts.DualWieldSpecialization * 0.1f); ohAttacks = totalHaste / character.OffHand.Speed; ohHits = ohAttacks * probOHHit; energyRegen += (.2f * 3f * calcOpts.CombatPotency) * ohHits; ohWhite = avgOHDmg * ohHits; ohWhite = (1f - ohCrit / 100f) * ohWhite + (ohCrit / 100f) * (ohWhite * (2f * bonusWhiteCritDmg)); ohWhite *= damageReduction; } cycleTime = (numCPG * energyCPG + 25f + finisherCost) / energyRegen; #region CPG Damage cpgDPS = 0f; if (character.MainHand != null) { avgCPGDmg = 0f; cpgCrit = 0f; bonusCPGCrit = 0f; bonusCPGDmgMult = 1f; bonusCPGCritDmgMult = 2f; if (cpg == "mutilate" && character.OffHand != null) { bonusCPGCrit += 5f * calcOpts.PuncturingWounds; bonusCPGCritDmgMult *= (1f + .06f * calcOpts.Lethality); bonusCPGDmgMult *= (1f + 0.04f * calcOpts.Opportunity); avgCPGDmg = (character.MainHand.MinDamage + character.MainHand.MaxDamage) / 2f + 121.5f; avgCPGDmg += stats.AttackPower / 14f * 1.7f; avgCPGDmg += (character.OffHand.MinDamage + character.OffHand.MaxDamage) / 2f + 121.5f; avgCPGDmg += stats.AttackPower / 14f * 1.7f; avgCPGDmg *= 1.5f; } else if (cpg == "backstab") { bonusCPGDmgMult *= (1f + .02f * calcOpts.Aggression); bonusCPGDmgMult *= (1f + .1f * calcOpts.SurpriseAttacks); bonusCPGDmgMult *= (1f + 0.04f * calcOpts.Opportunity); bonusCPGCrit += 10f * calcOpts.PuncturingWounds; bonusCPGCritDmgMult *= (1f + .06f * calcOpts.Lethality); avgCPGDmg = (character.MainHand.MinDamage + character.MainHand.MaxDamage + stats.WeaponDamage) / 2f; avgCPGDmg += stats.AttackPower / 14f * 1.7f; avgCPGDmg *= 1.5f; avgCPGDmg += 255f; } else if (cpg == "hemo") { bonusCPGDmgMult *= (1f + .1f * calcOpts.SurpriseAttacks); bonusCPGDmgMult *= (1f + stats.BonusCPGDamage); bonusCPGCritDmgMult *= (1f + .06f * calcOpts.Lethality); avgCPGDmg = (character.MainHand.MinDamage + character.MainHand.MaxDamage + stats.WeaponDamage) / 2f; avgCPGDmg += stats.AttackPower / 14f * 2.4f; avgCPGDmg *= 1.1f; } else { // sinister strike avgCPGDmg = (character.MainHand.MinDamage + character.MainHand.MaxDamage + stats.WeaponDamage) / 2f; avgCPGDmg += stats.AttackPower / 14f * 2.4f; avgCPGDmg += 98f; // TBC max rank bonusCPGDmgMult *= (1f + .02f * calcOpts.Aggression); bonusCPGDmgMult *= (1f + .1f * calcOpts.SurpriseAttacks); bonusCPGDmgMult *= (1f + stats.BonusCPGDamage); bonusCPGCritDmgMult *= (1f + .06f * calcOpts.Lethality); } avgCPGDmg *= bonusCPGDmgMult; cpgCrit = mhCrit + bonusCPGCrit; avgCPGDmg = (1f - cpgCrit / 100f) * avgCPGDmg + (cpgCrit / 100f) * (avgCPGDmg * bonusCPGCritDmgMult); cpgDPS = avgCPGDmg * numCPG / cycleTime; cpgDPS *= damageReduction; } #endregion #region Finisher Damage finisherDPS = 0f; if (character.MainHand != null) { if (calcOpts.DPSCycle['r'] > 0) { switch (calcOpts.DPSCycle['r']) { case 5: finisherDmg = 4f * (stats.AttackPower * .01f + 81f); break; case 4: finisherDmg = 5f * (stats.AttackPower * 0.02f + 92f); break; case 3: finisherDmg = 6f * (stats.AttackPower * 0.03f + 103f); break; case 2: finisherDmg = 7f * (stats.AttackPower * 0.03f + 114f); break; default: finisherDmg = 8f * (stats.AttackPower * 0.03f + 125f); break; } finisherDmg *= (1f + .1f * calcOpts.SerratedBlades) * (1f + stats.BonusBleedDamageMultiplier); finisherDmg *= (1f - missChance / 100f); if (calcOpts.SurpriseAttacks < 1) { finisherDmg *= (1f - mhDodgeChance / 100f); } finisherDPS = finisherDmg / cycleTime; } else if (calcOpts.DPSCycle['e'] > 0) { evisMod = stats.AttackPower * calcOpts.DPSCycle['e'] * .03f; evisMin = 245f + (calcOpts.DPSCycle['e'] - 1f) * 185f + evisMod; evisMax = 365f + (calcOpts.DPSCycle['e'] - 1f) * 185f + evisMod; finisherDmg = (evisMin + evisMax) / 2f; finisherDmg *= (1f + 0.05f * calcOpts.ImprovedEviscerate); finisherDmg *= (1f + 0.02f * calcOpts.Aggression); finisherDmg = finisherDmg * (1f - (mhCrit / 100f)) + (finisherDmg * 2f) * (mhCrit / 100f); finisherDmg *= (1f - (missChance / 100f)); if (calcOpts.SurpriseAttacks < 1) { finisherDmg *= (1f - (mhDodgeChance / 100f)); } finisherDmg *= damageReduction; finisherDPS = finisherDmg / cycleTime; } else { } } #endregion #region Sword Spec Damage ssDPS = 0f; ssHits = 0f; // main hand if (character.MainHand != null && character.MainHand.Type == Item.ItemType.OneHandSword) { ssHits += mhAttacks * 0.01f * calcOpts.SwordSpecialization * probMHHit; // CPG ssHits += (numCPG / cycleTime) * 0.01f * calcOpts.SwordSpecialization * probMHHit; // finishers ssHits += 1f / cycleTime * 0.01f * calcOpts.SwordSpecialization * probMHHit; } // offhand if (character.OffHand != null && character.OffHand.Type == Item.ItemType.OneHandSword) { ssHits += ohAttacks * 0.01f * calcOpts.SwordSpecialization * probOHHit; } ssDPS = (ssHits * avgMHDmg) * (1 - mhCrit / 100f) + (ssHits * avgMHDmg * 2f * bonusWhiteCritDmg) * (mhCrit / 100f); ssDPS *= damageReduction; #endregion #region WF Damage wfDPS = 0f; if (character.MainHand != null && stats.WindfuryAPBonus > 0) { wfHits = mhAttacks * probMHHit * .2f * probMHHit; wfHits += ssHits * .2f * probMHHit; avgWFDmg = (character.MainHand.MinDamage + character.MainHand.MaxDamage + stats.WeaponDamage * 2) / 2.0f; avgWFDmg += (stats.AttackPower + stats.WindfuryAPBonus) / 14f * character.MainHand.Speed; avgWFDmg = avgWFDmg * (1f - mhCrit / 100f) + avgMHDmg * 2f * (mhCrit / 100f); wfDPS = avgWFDmg * wfHits; wfDPS *= damageReduction; } #endregion whiteDPS = mhWhite + ohWhite; #endregion #region Poison DPS poisonDPS = 0f; probPoison = (.83f + .05f * calcOpts.MasterPoisoner) * (.2f + .02f * calcOpts.ImprovedPoisons); calcDeadly = true; if (character.MainHand != null && stats.WindfuryAPBonus == 0f) { // no WF, consider the main hand poison if (calcOpts.TempMainHandEnchant == "Deadly Poison" && calcDeadly) { poisonDPS += 180f * calcOpts.VilePoisons * .04f / 12f; calcDeadly = false; } else if (calcOpts.TempMainHandEnchant == "Instant Poison") { poisonDPS += ohHits * probPoison * 170f * (1f + calcOpts.VilePoisons * 0.04f); } } if (character.OffHand != null) { if (calcOpts.TempOffHandEnchant == "Deadly Poison" && calcDeadly) { poisonDPS += 180f * (1f + calcOpts.VilePoisons * .04f) / 12f; calcDeadly = false; } else if (calcOpts.TempOffHandEnchant == "Instant Poison") { poisonDPS += ohHits * probPoison * 170f * (1f + calcOpts.VilePoisons * 0.04f); } } #endregion calculatedStats.WhiteDPS = whiteDPS + ssDPS; calculatedStats.CPGDPS = cpgDPS; calculatedStats.FinisherDPS = finisherDPS; calculatedStats.WindfuryDPS = wfDPS; calculatedStats.SwordSpecDPS = ssDPS; calculatedStats.PoisonDPS = poisonDPS; calculatedStats.DPSPoints = whiteDPS + cpgDPS + finisherDPS + wfDPS + ssDPS + poisonDPS; calculatedStats.OverallPoints = calculatedStats.DPSPoints; return(calculatedStats); }